1 /*****************************************************************************
2 * RRDtool 1.2.14 Copyright by Tobi Oetiker, 1997-2006
3 *****************************************************************************
4 * rrd_tool.c Startup wrapper
5 *****************************************************************************/
10 void PrintUsage(char *cmd);
11 int CountArgs(char *aLine);
12 int CreateArgs(char *, char *, int, char **);
13 int HandleInputLine(int, char **, FILE*);
18 #define MAX_LENGTH 10000
21 void PrintUsage(char *cmd)
25 "RRDtool " PACKAGE_VERSION " Copyright 1997-2006 by Tobias Oetiker <tobi@oetiker.ch>\n"
26 " Compiled " __DATE__ " " __TIME__ "\n\n"
27 "Usage: rrdtool [options] command command_options\n\n";
30 "Valid commands: create, update, updatev, graph, dump, restore,\n"
31 "\t\tlast, first, info, fetch, tune, resize, xport\n\n";
33 char help_listremote[] =
34 "Valid remote commands: quit, ls, cd, mkdir, pwd\n\n";
38 "* create - create a new RRD\n\n"
39 "\trrdtool create filename [--start|-b start time]\n"
40 "\t\t[--step|-s step]\n"
41 "\t\t[DS:ds-name:DST:dst arguments]\n"
42 "\t\t[RRA:CF:cf arguments]\n\n";
45 "* dump - dump an RRD to XML\n\n"
46 "\trrdtool dump filename.rrd >filename.xml\n\n";
49 "* info - returns the configuration and status of the RRD\n\n"
50 "\trrdtool info filename.rrd\n\n";
53 "* restore - restore an RRD file from its XML form\n\n"
54 "\trrdtool restore [--range-check|-r] [--force-overwrite|-f] filename.xml filename.rrd\n\n";
57 "* last - show last update time for RRD\n\n"
58 "\trrdtool last filename.rrd\n\n";
61 "* first - show first update time for RRA within an RRD\n\n"
62 "\trrdtool first filename.rrd [--rraindex number]\n\n";
65 "* update - update an RRD\n\n"
66 "\trrdtool update filename\n"
67 "\t\t--template|-t ds-name:ds-name:...\n"
68 "\t\ttime|N:value[:value...]\n\n"
69 "\t\tat-time@value[:value...]\n\n"
70 "\t\t[ time:value[:value...] ..]\n\n";
73 "* updatev - a verbose version of update\n"
74 "\treturns information about values, RRAs, and datasources updated\n\n"
75 "\trrdtool updatev filename\n"
76 "\t\t--template|-t ds-name:ds-name:...\n"
77 "\t\ttime|N:value[:value...]\n\n"
78 "\t\tat-time@value[:value...]\n\n"
79 "\t\t[ time:value[:value...] ..]\n\n";
82 "* fetch - fetch data out of an RRD\n\n"
83 "\trrdtool fetch filename.rrd CF\n"
84 "\t\t[-r|--resolution resolution]\n"
85 "\t\t[-s|--start start] [-e|--end end]\n\n";
87 /* break up very large strings (help_graph, help_tune) for ISO C89 compliance*/
90 "* graph - generate a graph from one or several RRD\n\n"
91 "\trrdtool graph filename [-s|--start seconds] [-e|--end seconds]\n"
92 "\t\t[-x|--x-grid x-axis grid and label]\n"
93 "\t\t[-Y|--alt-y-grid]\n"
94 "\t\t[-y|--y-grid y-axis grid and label]\n"
95 "\t\t[-v|--vertical-label string] [-w|--width pixels]\n"
96 "\t\t[-h|--height pixels] [-o|--logarithmic]\n"
97 "\t\t[-u|--upper-limit value] [-z|--lazy]\n"
98 "\t\t[-l|--lower-limit value] [-r|--rigid]\n"
99 "\t\t[-g|--no-legend]\n"
100 "\t\t[-F|--force-rules-legend]\n"
101 "\t\t[-j|--only-graph]\n";
103 "\t\t[-n|--font FONTTAG:size:font]\n"
104 "\t\t[-m|--zoom factor]\n"
105 "\t\t[-A|--alt-autoscale]\n"
106 "\t\t[-M|--alt-autoscale-max]\n"
107 "\t\t[-R|--font-render-mode {normal,light,mono}]\n"
108 "\t\t[-B|--font-smoothing-threshold size]\n"
109 "\t\t[-E|--slope-mode]\n"
110 "\t\t[-N|--no-gridfit]\n"
111 "\t\t[-X|--units-exponent value]\n"
112 "\t\t[-L|--units-length value]\n"
113 "\t\t[-S|--step seconds]\n"
114 "\t\t[-f|--imginfo printfstr]\n"
115 "\t\t[-a|--imgformat PNG]\n"
116 "\t\t[-c|--color COLORTAG#rrggbb[aa]] [-t|--title string]\n"
117 "\t\t[-W|--watermark string]\n"
118 "\t\t[DEF:vname=rrd:ds-name:CF]\n";
120 "\t\t[CDEF:vname=rpn-expression]\n"
121 "\t\t[VDEF:vdefname=rpn-expression]\n"
122 "\t\t[PRINT:vdefname:format]\n"
123 "\t\t[GPRINT:vdefname:format]\n"
124 "\t\t[COMMENT:text]\n"
125 "\t\t[SHIFT:vname:offset]\n"
126 "\t\t[TICK:vname#rrggbb[aa][:[fraction][:legend]]]\n"
127 "\t\t[HRULE:value#rrggbb[aa][:legend]]\n"
128 "\t\t[VRULE:value#rrggbb[aa][:legend]]\n"
129 "\t\t[LINE[width]:vname[#rrggbb[aa][:[legend][:STACK]]]]\n"
130 "\t\t[AREA:vname[#rrggbb[aa][:[legend][:STACK]]]]\n"
131 "\t\t[PRINT:vname:CF:format] (deprecated)\n"
132 "\t\t[GPRINT:vname:CF:format] (deprecated)\n"
133 "\t\t[STACK:vname[#rrggbb[aa][:legend]]] (deprecated)\n\n";
136 " * tune - Modify some basic properties of an RRD\n\n"
137 "\trrdtool tune filename\n"
138 "\t\t[--heartbeat|-h ds-name:heartbeat]\n"
139 "\t\t[--data-source-type|-d ds-name:DST]\n"
140 "\t\t[--data-source-rename|-r old-name:new-name]\n"
141 "\t\t[--minimum|-i ds-name:min] [--maximum|-a ds-name:max]\n"
142 "\t\t[--deltapos scale-value] [--deltaneg scale-value]\n"
143 "\t\t[--failure-threshold integer]\n"
144 "\t\t[--window-length integer]\n"
145 "\t\t[--alpha adaptation-parameter]\n";
147 " * tune - Modify some basic properties of an RRD\n\n"
148 "\t\t[--beta adaptation-parameter]\n"
149 "\t\t[--gamma adaptation-parameter]\n"
150 "\t\t[--gamma-deviation adaptation-parameter]\n"
151 "\t\t[--aberrant-reset ds-name]\n\n";
154 " * resize - alter the length of one of the RRAs in an RRD\n\n"
155 "\trrdtool resize filename rranum GROW|SHRINK rows\n\n";
158 "* xport - generate XML dump from one or several RRD\n\n"
159 "\trrdtool xport [-s|--start seconds] [-e|--end seconds]\n"
160 "\t\t[-m|--maxrows rows]\n"
161 "\t\t[--step seconds]\n"
162 "\t\t[DEF:vname=rrd:ds-name:CF]\n"
163 "\t\t[CDEF:vname=rpn-expression]\n"
164 "\t\t[XPORT:vname:legend]\n\n";
167 " * quit - closing a session in remote mode\n\n"
168 "\trrdtool quit\n\n";
171 " * ls - lists all *.rrd files in current directory\n\n"
175 " * cd - changes the current directory\n\n"
176 "\trrdtool cd new directory\n\n";
179 " * mkdir - creates a new directory\n\n"
180 "\trrdtool mkdir newdirectoryname\n\n";
183 " * pwd - returns the current working directory\n\n"
187 "RRDtool is distributed under the Terms of the GNU General\n"
188 "Public License Version 2. (www.gnu.org/copyleft/gpl.html)\n\n"
190 "For more information read the RRD manpages\n\n";
192 enum { C_NONE, C_CREATE, C_DUMP, C_INFO, C_RESTORE, C_LAST, C_FIRST,
193 C_UPDATE, C_FETCH, C_GRAPH, C_TUNE, C_RESIZE, C_XPORT,
194 C_QUIT, C_LS, C_CD, C_MKDIR, C_PWD, C_UPDATEV };
196 int help_cmd = C_NONE;
200 if (!strcmp(cmd,"create"))
202 else if (!strcmp(cmd,"dump"))
204 else if (!strcmp(cmd,"info"))
206 else if (!strcmp(cmd,"restore"))
207 help_cmd = C_RESTORE;
208 else if (!strcmp(cmd,"last"))
210 else if (!strcmp(cmd,"first"))
212 else if (!strcmp(cmd,"update"))
214 else if (!strcmp(cmd,"updatev"))
215 help_cmd = C_UPDATEV;
216 else if (!strcmp(cmd,"fetch"))
218 else if (!strcmp(cmd,"graph"))
220 else if (!strcmp(cmd,"tune"))
222 else if (!strcmp(cmd,"resize"))
224 else if (!strcmp(cmd,"xport"))
226 else if (!strcmp(cmd,"quit"))
228 else if (!strcmp(cmd,"ls"))
230 else if (!strcmp(cmd,"cd"))
232 else if (!strcmp(cmd,"mkdir"))
234 else if (!strcmp(cmd,"pwd"))
237 fputs(help_main, stdout);
241 fputs(help_list, stdout);
243 fputs(help_listremote, stdout);
247 fputs(help_create, stdout);
250 fputs(help_dump, stdout);
253 fputs(help_info, stdout);
256 fputs(help_restore, stdout);
259 fputs(help_last, stdout);
262 fputs(help_first, stdout);
265 fputs(help_update, stdout);
268 fputs(help_updatev, stdout);
271 fputs(help_fetch, stdout);
274 fputs(help_graph1, stdout);
275 fputs(help_graph2, stdout);
276 fputs(help_graph3, stdout);
279 fputs(help_tune1, stdout);
280 fputs(help_tune2, stdout);
283 fputs(help_resize, stdout);
286 fputs(help_xport, stdout);
289 fputs(help_quit, stdout);
292 fputs(help_ls, stdout);
295 fputs(help_cd, stdout);
298 fputs(help_mkdir, stdout);
301 fputs(help_pwd, stdout);
304 fputs(help_lic, stdout);
307 static char *fgetslong(char **aLinePtr, FILE *stream)
310 size_t bufsize = MAX_LENGTH;
313 if (feof(stream)) return *aLinePtr = 0;
314 if (!(linebuf = malloc(bufsize))) {
315 perror("fgetslong: malloc");
319 while (fgets(linebuf + eolpos, MAX_LENGTH, stream)) {
320 eolpos += strlen(linebuf + eolpos);
321 if (linebuf[eolpos - 1] == '\n') return *aLinePtr = linebuf;
322 bufsize += MAX_LENGTH;
323 if (!(linebuf = realloc(linebuf, bufsize))) {
324 perror("fgetslong: realloc");
328 return *aLinePtr = linebuf[0] ? linebuf : 0;
331 int main(int argc, char *argv[])
336 #ifdef MUST_DISABLE_SIGFPE
337 signal(SIGFPE,SIG_IGN);
339 #ifdef MUST_DISABLE_FPMASK
348 if (((argc == 2)||(argc == 3)) && !strcmp("-",argv[1]))
351 struct rusage myusage;
352 struct timeval starttime;
353 struct timeval currenttime;
356 tz.tz_minuteswest =0;
358 gettimeofday(&starttime,&tz);
361 if ((argc == 3) && strcmp("",argv[2])){
374 fprintf(stderr,"ERROR: can't change root to '%s' errno=%d\n",
381 fprintf(stderr,"ERROR: change root is not supported by your OS "
382 "or at least by this copy of rrdtool\n");
389 if (strcmp(firstdir,"")){
392 fprintf(stderr,"ERROR: %s\n",rrd_strerror(errno));
397 while (fgetslong(&aLine, stdin)){
398 if ((argc = CountArgs(aLine)) == 0) {
399 printf("ERROR: not enough arguments\n");
401 if ((myargv = (char **) malloc((argc+1) *
402 sizeof(char *))) == NULL) {
406 if ((argc=CreateArgs(argv[0], aLine, argc, myargv)) < 0) {
407 printf("ERROR: creating arguments\n");
409 int ret = HandleInputLine(argc, myargv, stdout);
415 getrusage(RUSAGE_SELF,&myusage);
416 gettimeofday(¤ttime,&tz);
417 printf("OK u:%1.2f s:%1.2f r:%1.2f\n",
418 (double)myusage.ru_utime.tv_sec+
419 (double)myusage.ru_utime.tv_usec/1000000.0,
420 (double)myusage.ru_stime.tv_sec+
421 (double)myusage.ru_stime.tv_usec/1000000.0,
422 (double)(currenttime.tv_sec-starttime.tv_sec)
423 +(double)(currenttime.tv_usec-starttime.tv_usec)
431 fflush(stdout); /* this is important for pipes to work */
440 else if (argc == 3 && !strcmp(argv[1],"help"))
446 exit(HandleInputLine(argc, argv, stderr));
451 /* HandleInputLine is NOT thread safe - due to readdir issues,
452 resolving them portably is not really simple. */
453 int HandleInputLine(int argc, char **argv, FILE* out)
455 #if defined(HAVE_OPENDIR) && defined (HAVE_READDIR)
456 DIR *curdir; /* to read current dir with ls */
459 #if defined(HAVE_SYS_STAT_H)
462 char* cwd; /* To hold current working dir on call to pwd */
464 /* Reset errno to 0 before we start.
469 if (argc>1 && strcmp("quit", argv[1]) == 0){
471 printf("ERROR: invalid parameter count for quit\n");
476 #if defined(HAVE_OPENDIR) && defined(HAVE_READDIR) && defined(HAVE_CHDIR)
477 if (argc>1 && strcmp("cd", argv[1]) == 0){
479 printf("ERROR: invalid parameter count for cd\n");
482 #if ! defined(HAVE_CHROOT) || ! defined(HAVE_GETUID)
483 if (getuid()==0 && ! ChangeRoot){
484 printf("ERROR: chdir security problem - rrdtool is running as "
485 "root but not chroot!\n");
491 printf("ERROR: %s\n",rrd_strerror(errno));
496 if (argc>1 && strcmp("pwd", argv[1]) == 0){
498 printf("ERROR: invalid parameter count for pwd\n");
501 cwd = getcwd(NULL, MAXPATH);
503 printf("ERROR: %s\n",rrd_strerror(errno));
510 if (argc>1 && strcmp("mkdir", argv[1]) == 0){
512 printf("ERROR: invalid parameter count for mkdir\n");
515 #if ! defined(HAVE_CHROOT) || ! defined(HAVE_GETUID)
516 if (getuid()==0 && ! ChangeRoot){
517 printf("ERROR: mkdir security problem - rrdtool is running as "
518 "root but not chroot!\n");
524 printf("ERROR: %s\n",rrd_strerror(errno));
529 if (argc>1 && strcmp("ls", argv[1]) == 0){
531 printf("ERROR: invalid parameter count for ls\n");
534 if ((curdir=opendir("."))!=NULL){
535 while((dent=readdir(curdir))!=NULL){
536 if (!stat(dent->d_name,&st)){
537 if (S_ISDIR(st.st_mode)){
538 printf("d %s\n",dent->d_name);
540 if (strlen(dent->d_name)>4 && S_ISREG(st.st_mode)){
541 if (!strcmp(dent->d_name+NAMLEN(dent)-4,".rrd") ||
542 !strcmp(dent->d_name+NAMLEN(dent)-4,".RRD")){
543 printf("- %s\n",dent->d_name);
551 printf("ERROR: %s\n",rrd_strerror(errno));
556 #endif /* opendir and readdir */
560 || strcmp("help", argv[1]) == 0
561 || strcmp("--help", argv[1]) == 0
562 || strcmp("-help", argv[1]) == 0
563 || strcmp("-?", argv[1]) == 0
564 || strcmp("-h", argv[1]) == 0 ) {
569 if (strcmp("create", argv[1]) == 0)
570 rrd_create(argc-1, &argv[1]);
571 else if (strcmp("dump", argv[1]) == 0)
572 rrd_dump(argc-1, &argv[1]);
573 else if (strcmp("info", argv[1]) == 0
574 || strcmp("updatev", argv[1]) == 0){
576 if (strcmp("info",argv[1]) == 0)
577 data=rrd_info(argc-1, &argv[1]);
579 data=rrd_update_v(argc-1, &argv[1]);
582 printf ("%s = ", data->key);
585 switch (data->type) {
587 if (isnan (data->value.u_val))
590 printf ("%0.10e", data->value.u_val);
593 printf ("%lu", data->value.u_cnt);
596 printf ("%d", data->value.u_int);
599 printf ("\"%s\"", data->value.u_str);
600 free(data->value.u_str);
610 else if (strcmp("--version", argv[1]) == 0 ||
611 strcmp("version", argv[1]) == 0 ||
612 strcmp("v", argv[1]) == 0 ||
613 strcmp("-v", argv[1]) == 0 ||
614 strcmp("-version", argv[1]) == 0 )
615 printf("RRDtool " PACKAGE_VERSION " Copyright by Tobi Oetiker, 1997-2005 (%f)\n",
617 else if (strcmp("restore", argv[1]) == 0)
618 rrd_restore(argc-1, &argv[1]);
619 else if (strcmp("resize", argv[1]) == 0)
620 rrd_resize(argc-1, &argv[1]);
621 else if (strcmp("last", argv[1]) == 0)
622 printf("%ld\n",rrd_last(argc-1, &argv[1]));
623 else if (strcmp("first", argv[1]) == 0)
624 printf("%ld\n",rrd_first(argc-1, &argv[1]));
625 else if (strcmp("update", argv[1]) == 0)
626 rrd_update(argc-1, &argv[1]);
627 else if (strcmp("fetch", argv[1]) == 0) {
628 time_t start,end, ti;
629 unsigned long step, ds_cnt,i,ii;
630 rrd_value_t *data,*datai;
632 if (rrd_fetch(argc-1, &argv[1],&start,&end,&step,&ds_cnt,&ds_namv,&data) != -1) {
635 for (i = 0; i<ds_cnt;i++)
636 printf("%20s",ds_namv[i]);
638 for (ti = start+step; ti <= end; ti += step){
639 printf("%10lu:", ti);
640 for (ii = 0; ii < ds_cnt; ii++)
641 printf(" %0.10e", *(datai++));
644 for (i=0;i<ds_cnt;i++)
649 } else if (strcmp("xport", argv[1]) == 0) {
651 unsigned long int j = 0;
652 time_t start,end, ti;
653 unsigned long step, col_cnt,row_cnt;
654 rrd_value_t *data,*ptr;
656 if(rrd_xport(argc-1, &argv[1], &xxsize,&start,&end,&step,&col_cnt,&legend_v,&data) != -1) {
657 row_cnt = (end-start)/step;
659 printf("<?xml version=\"1.0\" encoding=\"%s\"?>\n\n", XML_ENCODING);
660 printf("<%s>\n", ROOT_TAG);
661 printf(" <%s>\n", META_TAG);
662 printf(" <%s>%lu</%s>\n", META_START_TAG, start+step, META_START_TAG);
663 printf(" <%s>%lu</%s>\n", META_STEP_TAG, step, META_STEP_TAG);
664 printf(" <%s>%lu</%s>\n", META_END_TAG, end, META_END_TAG);
665 printf(" <%s>%lu</%s>\n", META_ROWS_TAG, row_cnt, META_ROWS_TAG);
666 printf(" <%s>%lu</%s>\n", META_COLS_TAG, col_cnt, META_COLS_TAG);
667 printf(" <%s>\n", LEGEND_TAG);
668 for (j = 0; j < col_cnt; j++) {
671 printf(" <%s>%s</%s>\n", LEGEND_ENTRY_TAG, entry, LEGEND_ENTRY_TAG);
675 printf(" </%s>\n", LEGEND_TAG);
676 printf(" </%s>\n", META_TAG);
677 printf(" <%s>\n", DATA_TAG);
678 for (ti = start+step; ti <= end; ti += step) {
679 printf (" <%s>", DATA_ROW_TAG);
680 printf ("<%s>%lu</%s>", COL_TIME_TAG, ti, COL_TIME_TAG);
681 for (j = 0; j < col_cnt; j++) {
682 rrd_value_t newval = DNAN;
685 printf("<%s>NaN</%s>", COL_DATA_TAG, COL_DATA_TAG);
687 printf("<%s>%0.10e</%s>", COL_DATA_TAG, newval, COL_DATA_TAG);
691 printf("</%s>\n", DATA_ROW_TAG);
694 printf(" </%s>\n", DATA_TAG);
695 printf("</%s>\n", ROOT_TAG);
698 else if (strcmp("graph", argv[1]) == 0) {
700 #ifdef notused /*XXX*/
701 const char *imgfile = argv[2]; /* rrd_graph changes argv pointer */
706 int tostdout = (strcmp(argv[2],"-") == 0);
708 for (i=2;i<argc;i++){
709 if (strcmp(argv[i],"--imginfo") == 0 || strcmp(argv[i],"-f") == 0){
714 if( rrd_graph(argc-1, &argv[1], &calcpr, &xsize, &ysize, NULL, &ymin, &ymax) != -1 ) {
715 if (!tostdout && !imginfo)
716 printf ("%dx%d\n",xsize,ysize);
718 for(i=0;calcpr[i];i++){
720 printf("%s\n",calcpr[i]);
727 } else if (strcmp("tune", argv[1]) == 0)
728 rrd_tune(argc-1, &argv[1]);
730 rrd_set_error("unknown function '%s'",argv[1]);
732 if (rrd_test_error()) {
733 fprintf(out, "ERROR: %s\n",rrd_get_error());
740 int CountArgs(char *aLine)
745 while (aLine[i] == ' ') i++;
746 while (aLine[i] != 0){
747 if((aLine[i]== ' ') && inarg){
750 if((aLine[i]!= ' ') && ! inarg){
760 * CreateArgs - take a string (aLine) and tokenize
762 int CreateArgs(char *pName, char *aLine, int argc, char **argv)
771 /* remove trailing space and newlines */
772 while (len && aLine[len] <= ' ') {
773 aLine[len] = 0 ; len--;
775 /* sikp leading blanks */
776 while (*aLine && *aLine <= ' ') aLine++;
803 pargv[argc++] = putP;
811 pargv[argc++] = putP;