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);
550 printf("ERROR: %s\n",rrd_strerror(errno));
555 #endif /* opendir and readdir */
559 || strcmp("help", argv[1]) == 0
560 || strcmp("--help", argv[1]) == 0
561 || strcmp("-help", argv[1]) == 0
562 || strcmp("-?", argv[1]) == 0
563 || strcmp("-h", argv[1]) == 0 ) {
568 if (strcmp("create", argv[1]) == 0)
569 rrd_create(argc-1, &argv[1]);
570 else if (strcmp("dump", argv[1]) == 0)
571 rrd_dump(argc-1, &argv[1]);
572 else if (strcmp("info", argv[1]) == 0
573 || strcmp("updatev", argv[1]) == 0){
575 if (strcmp("info",argv[1]) == 0)
576 data=rrd_info(argc-1, &argv[1]);
578 data=rrd_update_v(argc-1, &argv[1]);
581 printf ("%s = ", data->key);
584 switch (data->type) {
586 if (isnan (data->value.u_val))
589 printf ("%0.10e", data->value.u_val);
592 printf ("%lu", data->value.u_cnt);
595 printf ("%d", data->value.u_int);
598 printf ("\"%s\"", data->value.u_str);
599 free(data->value.u_str);
609 else if (strcmp("--version", argv[1]) == 0 ||
610 strcmp("version", argv[1]) == 0 ||
611 strcmp("v", argv[1]) == 0 ||
612 strcmp("-v", argv[1]) == 0 ||
613 strcmp("-version", argv[1]) == 0 )
614 printf("RRDtool " PACKAGE_VERSION " Copyright by Tobi Oetiker, 1997-2005 (%f)\n",
616 else if (strcmp("restore", argv[1]) == 0)
617 rrd_restore(argc-1, &argv[1]);
618 else if (strcmp("resize", argv[1]) == 0)
619 rrd_resize(argc-1, &argv[1]);
620 else if (strcmp("last", argv[1]) == 0)
621 printf("%ld\n",rrd_last(argc-1, &argv[1]));
622 else if (strcmp("first", argv[1]) == 0)
623 printf("%ld\n",rrd_first(argc-1, &argv[1]));
624 else if (strcmp("update", argv[1]) == 0)
625 rrd_update(argc-1, &argv[1]);
626 else if (strcmp("fetch", argv[1]) == 0) {
627 time_t start,end, ti;
628 unsigned long step, ds_cnt,i,ii;
629 rrd_value_t *data,*datai;
631 if (rrd_fetch(argc-1, &argv[1],&start,&end,&step,&ds_cnt,&ds_namv,&data) != -1) {
634 for (i = 0; i<ds_cnt;i++)
635 printf("%20s",ds_namv[i]);
637 for (ti = start+step; ti <= end; ti += step){
638 printf("%10lu:", ti);
639 for (ii = 0; ii < ds_cnt; ii++)
640 printf(" %0.10e", *(datai++));
643 for (i=0;i<ds_cnt;i++)
648 } else if (strcmp("xport", argv[1]) == 0) {
650 unsigned long int j = 0;
651 time_t start,end, ti;
652 unsigned long step, col_cnt,row_cnt;
653 rrd_value_t *data,*ptr;
655 if(rrd_xport(argc-1, &argv[1], &xxsize,&start,&end,&step,&col_cnt,&legend_v,&data) != -1) {
656 row_cnt = (end-start)/step;
658 printf("<?xml version=\"1.0\" encoding=\"%s\"?>\n\n", XML_ENCODING);
659 printf("<%s>\n", ROOT_TAG);
660 printf(" <%s>\n", META_TAG);
661 printf(" <%s>%lu</%s>\n", META_START_TAG, start+step, META_START_TAG);
662 printf(" <%s>%lu</%s>\n", META_STEP_TAG, step, META_STEP_TAG);
663 printf(" <%s>%lu</%s>\n", META_END_TAG, end, META_END_TAG);
664 printf(" <%s>%lu</%s>\n", META_ROWS_TAG, row_cnt, META_ROWS_TAG);
665 printf(" <%s>%lu</%s>\n", META_COLS_TAG, col_cnt, META_COLS_TAG);
666 printf(" <%s>\n", LEGEND_TAG);
667 for (j = 0; j < col_cnt; j++) {
670 printf(" <%s>%s</%s>\n", LEGEND_ENTRY_TAG, entry, LEGEND_ENTRY_TAG);
674 printf(" </%s>\n", LEGEND_TAG);
675 printf(" </%s>\n", META_TAG);
676 printf(" <%s>\n", DATA_TAG);
677 for (ti = start+step; ti <= end; ti += step) {
678 printf (" <%s>", DATA_ROW_TAG);
679 printf ("<%s>%lu</%s>", COL_TIME_TAG, ti, COL_TIME_TAG);
680 for (j = 0; j < col_cnt; j++) {
681 rrd_value_t newval = DNAN;
684 printf("<%s>NaN</%s>", COL_DATA_TAG, COL_DATA_TAG);
686 printf("<%s>%0.10e</%s>", COL_DATA_TAG, newval, COL_DATA_TAG);
690 printf("</%s>\n", DATA_ROW_TAG);
693 printf(" </%s>\n", DATA_TAG);
694 printf("</%s>\n", ROOT_TAG);
697 else if (strcmp("graph", argv[1]) == 0) {
699 #ifdef notused /*XXX*/
700 const char *imgfile = argv[2]; /* rrd_graph changes argv pointer */
705 int tostdout = (strcmp(argv[2],"-") == 0);
707 for (i=2;i<argc;i++){
708 if (strcmp(argv[i],"--imginfo") == 0 || strcmp(argv[i],"-f") == 0){
713 if( rrd_graph(argc-1, &argv[1], &calcpr, &xsize, &ysize, NULL, &ymin, &ymax) != -1 ) {
714 if (!tostdout && !imginfo)
715 printf ("%dx%d\n",xsize,ysize);
717 for(i=0;calcpr[i];i++){
719 printf("%s\n",calcpr[i]);
726 } else if (strcmp("tune", argv[1]) == 0)
727 rrd_tune(argc-1, &argv[1]);
729 rrd_set_error("unknown function '%s'",argv[1]);
731 if (rrd_test_error()) {
732 fprintf(out, "ERROR: %s\n",rrd_get_error());
739 int CountArgs(char *aLine)
744 while (aLine[i] == ' ') i++;
745 while (aLine[i] != 0){
746 if((aLine[i]== ' ') && inarg){
749 if((aLine[i]!= ' ') && ! inarg){
759 * CreateArgs - take a string (aLine) and tokenize
761 int CreateArgs(char *pName, char *aLine, int argc, char **argv)
770 /* remove trailing space and newlines */
771 while (len && aLine[len] <= ' ') {
772 aLine[len] = 0 ; len--;
774 /* sikp leading blanks */
775 while (*aLine && *aLine <= ' ') aLine++;
802 pargv[argc++] = putP;
810 pargv[argc++] = putP;