cdb87e82bffa7fed7a197d587a8b8d4a9cdca21c
[rrdtool.git] / src / rrd_tool.c
1 /*****************************************************************************
2  * RRDtool 1.2.99907080300  Copyright by Tobi Oetiker, 1997-2007
3  *****************************************************************************
4  * rrd_tool.c  Startup wrapper
5  *****************************************************************************/
6
7 #if defined(_WIN32) && !defined(__CYGWIN__) && !defined(__CYGWIN32__) && !defined(HAVE_CONFIG_H)
8 #include "../win32/config.h"
9 #else
10 #ifdef HAVE_CONFIG_H
11 #include "../rrd_config.h"
12 #endif
13 #endif
14
15 #include "rrd_tool.h"
16 #include "rrd_xport.h"
17 #include "rrd_i18n.h"
18
19 #ifdef HAVE_LOCALE_H
20 #include <locale.h>
21 #endif
22
23 void      PrintUsage(
24     char *cmd);
25 int       CountArgs(
26     char *aLine);
27 int       CreateArgs(
28     char *,
29     char *,
30     int,
31     char **);
32 int       HandleInputLine(
33     int,
34     char **,
35     FILE *);
36 int       RemoteMode = 0;
37 int       ChangeRoot = 0;
38
39 #define TRUE            1
40 #define FALSE           0
41 #define MAX_LENGTH      10000
42
43
44 void PrintUsage(
45     char *cmd)
46 {
47
48     char      help_main[] =
49         N_("RRDtool %s"
50         "  Copyright 1997-2007 by Tobias Oetiker <tobi@oetiker.ch>\n"
51         "               Compiled %s %s\n\n"
52         "Usage: rrdtool [options] command command_options\n\n");
53
54     char      help_list[] =
55         N_("Valid commands: create, update, updatev, graph, dump, restore,\n"
56         "\t\tlast, lastupdate, first, info, fetch, tune,\n"
57         "\t\tresize, xport\n\n");
58
59     char      help_listremote[] =
60         N_("Valid remote commands: quit, ls, cd, mkdir, pwd\n\n");
61
62
63     char      help_create[] =
64         N_("* create - create a new RRD\n\n"
65         "\trrdtool create filename [--start|-b start time]\n"
66         "\t\t[--step|-s step]\n"
67         "\t\t[DS:ds-name:DST:dst arguments]\n"
68         "\t\t[RRA:CF:cf arguments]\n\n");
69
70     char      help_dump[] =
71         N_("* dump - dump an RRD to XML\n\n"
72         "\trrdtool dump filename.rrd >filename.xml\n\n");
73
74     char      help_info[] =
75         N_("* info - returns the configuration and status of the RRD\n\n"
76         "\trrdtool info filename.rrd\n\n");
77
78     char      help_restore[] =
79         N_("* restore - restore an RRD file from its XML form\n\n"
80         "\trrdtool restore [--range-check|-r] [--force-overwrite|-f] filename.xml filename.rrd\n\n");
81
82     char      help_last[] =
83         N_("* last - show last update time for RRD\n\n"
84         "\trrdtool last filename.rrd\n\n");
85
86     char      help_lastupdate[] =
87         N_("* lastupdate - returns the most recent datum stored for\n"
88         "  each DS in an RRD\n\n" "\trrdtool lastupdate filename.rrd\n\n");
89
90     char      help_first[] =
91         N_("* first - show first update time for RRA within an RRD\n\n"
92         "\trrdtool first filename.rrd [--rraindex number]\n\n");
93
94     char      help_update[] =
95         N_("* update - update an RRD\n\n"
96         "\trrdtool update filename\n"
97         "\t\t--template|-t ds-name:ds-name:...\n"
98         "\t\ttime|N:value[:value...]\n\n"
99         "\t\tat-time@value[:value...]\n\n"
100         "\t\t[ time:value[:value...] ..]\n\n");
101
102     char      help_updatev[] =
103         N_("* updatev - a verbose version of update\n"
104         "\treturns information about values, RRAs, and datasources updated\n\n"
105         "\trrdtool updatev filename\n"
106         "\t\t--template|-t ds-name:ds-name:...\n"
107         "\t\ttime|N:value[:value...]\n\n"
108         "\t\tat-time@value[:value...]\n\n"
109         "\t\t[ time:value[:value...] ..]\n\n");
110
111     char      help_fetch[] =
112         N_("* fetch - fetch data out of an RRD\n\n"
113         "\trrdtool fetch filename.rrd CF\n"
114         "\t\t[-r|--resolution resolution]\n"
115         "\t\t[-s|--start start] [-e|--end end]\n\n");
116
117 /* break up very large strings (help_graph, help_tune) for ISO C89 compliance*/
118
119     char      help_graph1[] =
120         N_("* graph - generate a graph from one or several RRD\n\n"
121         "\trrdtool graph filename [-s|--start seconds] [-e|--end seconds]\n"
122         "\t\t[-x|--x-grid x-axis grid and label]\n"
123         "\t\t[-Y|--alt-y-grid]\n"
124         "\t\t[-y|--y-grid y-axis grid and label]\n"
125         "\t\t[-v|--vertical-label string] [-w|--width pixels]\n"
126         "\t\t[-h|--height pixels] [-o|--logarithmic]\n"
127         "\t\t[-u|--upper-limit value] [-z|--lazy]\n"
128         "\t\t[-l|--lower-limit value] [-r|--rigid]\n"
129         "\t\t[-g|--no-legend]\n"
130         "\t\t[-F|--force-rules-legend]\n" "\t\t[-j|--only-graph]\n");
131     char      help_graph2[] =
132         "\t\t[-n|--font FONTTAG:size:font]\n"
133         "\t\t[-m|--zoom factor]\n"
134         "\t\t[-A|--alt-autoscale]\n"
135         "\t\t[-M|--alt-autoscale-max]\n"
136         "\t\t[-R|--font-render-mode {normal,light,mono}]\n"
137         "\t\t[-B|--font-smoothing-threshold size]\n"
138         "\t\t[-E|--slope-mode]\n"
139         "\t\t[-N|--no-gridfit]\n"
140         "\t\t[-X|--units-exponent value]\n"
141         "\t\t[-L|--units-length value]\n"
142         "\t\t[-S|--step seconds]\n"
143         "\t\t[-f|--imginfo printfstr]\n"
144         "\t\t[-a|--imgformat PNG]\n"
145         "\t\t[-c|--color COLORTAG#rrggbb[aa]] [-t|--title string]\n"
146         "\t\t[-W|--watermark string]\n" "\t\t[DEF:vname=rrd:ds-name:CF]\n";
147     char      help_graph3[] =
148         "\t\t[CDEF:vname=rpn-expression]\n"
149         "\t\t[VDEF:vdefname=rpn-expression]\n"
150         "\t\t[PRINT:vdefname:format]\n"
151         "\t\t[GPRINT:vdefname:format]\n"
152         "\t\t[COMMENT:text]\n"
153         "\t\t[SHIFT:vname:offset]\n"
154         "\t\t[TICK:vname#rrggbb[aa][:[fraction][:legend]]]\n"
155         "\t\t[HRULE:value#rrggbb[aa][:legend]]\n"
156         "\t\t[VRULE:value#rrggbb[aa][:legend]]\n"
157         "\t\t[LINE[width]:vname[#rrggbb[aa][:[legend][:STACK]]]]\n"
158         "\t\t[AREA:vname[#rrggbb[aa][:[legend][:STACK]]]]\n"
159         "\t\t[PRINT:vname:CF:format] (deprecated)\n"
160         "\t\t[GPRINT:vname:CF:format] (deprecated)\n"
161         "\t\t[STACK:vname[#rrggbb[aa][:legend]]] (deprecated)\n\n";
162
163     char      help_tune1[] =
164         N_(" * tune -  Modify some basic properties of an RRD\n\n"
165         "\trrdtool tune filename\n"
166         "\t\t[--heartbeat|-h ds-name:heartbeat]\n"
167         "\t\t[--data-source-type|-d ds-name:DST]\n"
168         "\t\t[--data-source-rename|-r old-name:new-name]\n"
169         "\t\t[--minimum|-i ds-name:min] [--maximum|-a ds-name:max]\n"
170         "\t\t[--deltapos scale-value] [--deltaneg scale-value]\n"
171         "\t\t[--failure-threshold integer]\n"
172         "\t\t[--window-length integer]\n"
173         "\t\t[--alpha adaptation-parameter]\n");
174     char      help_tune2[] =
175         N_(" * tune -  Modify some basic properties of an RRD\n\n"
176         "\t\t[--beta adaptation-parameter]\n"
177         "\t\t[--gamma adaptation-parameter]\n"
178         "\t\t[--gamma-deviation adaptation-parameter]\n"
179         "\t\t[--aberrant-reset ds-name]\n\n");
180
181     char      help_resize[] =
182         N_(" * resize - alter the length of one of the RRAs in an RRD\n\n"
183         "\trrdtool resize filename rranum GROW|SHRINK rows\n\n");
184
185     char      help_xport[] =
186         N_("* xport - generate XML dump from one or several RRD\n\n"
187         "\trrdtool xport [-s|--start seconds] [-e|--end seconds]\n"
188         "\t\t[-m|--maxrows rows]\n"
189         "\t\t[--step seconds]\n"
190         "\t\t[--enumds]\n"
191         "\t\t[DEF:vname=rrd:ds-name:CF]\n"
192         "\t\t[CDEF:vname=rpn-expression]\n" "\t\t[XPORT:vname:legend]\n\n");
193
194     char      help_quit[] =
195         N_(" * quit - closing a session in remote mode\n\n" "\trrdtool quit\n\n");
196
197     char      help_ls[] =
198         N_(" * ls - lists all *.rrd files in current directory\n\n"
199         "\trrdtool ls\n\n");
200
201     char      help_cd[] =
202         N_(" * cd - changes the current directory\n\n"
203         "\trrdtool cd new directory\n\n");
204
205     char      help_mkdir[] =
206         N_(" * mkdir - creates a new directory\n\n"
207         "\trrdtool mkdir newdirectoryname\n\n");
208
209     char      help_pwd[] =
210         N_(" * pwd - returns the current working directory\n\n"
211         "\trrdtool pwd\n\n");
212
213     char      help_lic[] =
214         N_("RRDtool is distributed under the Terms of the GNU General\n"
215         "Public License Version 2. (www.gnu.org/copyleft/gpl.html)\n\n"
216         "For more information read the RRD manpages\n\n");
217
218     enum { C_NONE, C_CREATE, C_DUMP, C_INFO, C_RESTORE, C_LAST,
219         C_LASTUPDATE, C_FIRST, C_UPDATE, C_FETCH, C_GRAPH, C_TUNE,
220         C_RESIZE, C_XPORT, C_QUIT, C_LS, C_CD, C_MKDIR, C_PWD,
221         C_UPDATEV
222     };
223
224     int       help_cmd = C_NONE;
225
226     if (cmd) {
227         if (!strcmp(cmd, "create"))
228             help_cmd = C_CREATE;
229         else if (!strcmp(cmd, "dump"))
230             help_cmd = C_DUMP;
231         else if (!strcmp(cmd, "info"))
232             help_cmd = C_INFO;
233         else if (!strcmp(cmd, "restore"))
234             help_cmd = C_RESTORE;
235         else if (!strcmp(cmd, "last"))
236             help_cmd = C_LAST;
237         else if (!strcmp(cmd, "lastupdate"))
238             help_cmd = C_LASTUPDATE;
239         else if (!strcmp(cmd, "first"))
240             help_cmd = C_FIRST;
241         else if (!strcmp(cmd, "update"))
242             help_cmd = C_UPDATE;
243         else if (!strcmp(cmd, "updatev"))
244             help_cmd = C_UPDATEV;
245         else if (!strcmp(cmd, "fetch"))
246             help_cmd = C_FETCH;
247         else if (!strcmp(cmd, "graph"))
248             help_cmd = C_GRAPH;
249         else if (!strcmp(cmd, "tune"))
250             help_cmd = C_TUNE;
251         else if (!strcmp(cmd, "resize"))
252             help_cmd = C_RESIZE;
253         else if (!strcmp(cmd, "xport"))
254             help_cmd = C_XPORT;
255         else if (!strcmp(cmd, "quit"))
256             help_cmd = C_QUIT;
257         else if (!strcmp(cmd, "ls"))
258             help_cmd = C_LS;
259         else if (!strcmp(cmd, "cd"))
260             help_cmd = C_CD;
261         else if (!strcmp(cmd, "mkdir"))
262             help_cmd = C_MKDIR;
263         else if (!strcmp(cmd, "pwd"))
264             help_cmd = C_PWD;
265     }
266     fprintf (stdout, _(help_main), PACKAGE_VERSION, __DATE__, __TIME__);
267     fflush (stdout);
268     switch (help_cmd) {
269     case C_NONE:
270         fputs(_(help_list), stdout);
271         if (RemoteMode) {
272             fputs(_(help_listremote), stdout);
273         }
274         break;
275     case C_CREATE:
276         fputs(_(help_create), stdout);
277         break;
278     case C_DUMP:
279         fputs(_(help_dump), stdout);
280         break;
281     case C_INFO:
282         fputs(_(help_info), stdout);
283         break;
284     case C_RESTORE:
285         fputs(_(help_restore), stdout);
286         break;
287     case C_LAST:
288         fputs(_(help_last), stdout);
289         break;
290     case C_LASTUPDATE:
291         fputs(_(help_lastupdate), stdout);
292         break;
293     case C_FIRST:
294         fputs(_(help_first), stdout);
295         break;
296     case C_UPDATE:
297         fputs(_(help_update), stdout);
298         break;
299     case C_UPDATEV:
300         fputs(_(help_updatev), stdout);
301         break;
302     case C_FETCH:
303         fputs(_(help_fetch), stdout);
304         break;
305     case C_GRAPH:
306         fputs(_(help_graph1), stdout);
307         fputs(help_graph2, stdout);
308         fputs(help_graph3, stdout);
309         break;
310     case C_TUNE:
311         fputs(_(help_tune1), stdout);
312         fputs(_(help_tune2), stdout);
313         break;
314     case C_RESIZE:
315         fputs(_(help_resize), stdout);
316         break;
317     case C_XPORT:
318         fputs(_(help_xport), stdout);
319         break;
320     case C_QUIT:
321         fputs(_(help_quit), stdout);
322         break;
323     case C_LS:
324         fputs(_(help_ls), stdout);
325         break;
326     case C_CD:
327         fputs(_(help_cd), stdout);
328         break;
329     case C_MKDIR:
330         fputs(_(help_mkdir), stdout);
331         break;
332     case C_PWD:
333         fputs(_(help_pwd), stdout);
334         break;
335     }
336     fputs(_(help_lic), stdout);
337 }
338
339 static char *fgetslong(
340     char **aLinePtr,
341     FILE * stream)
342 {
343     char     *linebuf;
344     size_t    bufsize = MAX_LENGTH;
345     int       eolpos = 0;
346
347     if (feof(stream))
348         return *aLinePtr = 0;
349     if (!(linebuf = malloc(bufsize))) {
350         perror("fgetslong: malloc");
351         exit(1);
352     }
353     linebuf[0] = '\0';
354     while (fgets(linebuf + eolpos, MAX_LENGTH, stream)) {
355         eolpos += strlen(linebuf + eolpos);
356         if (linebuf[eolpos - 1] == '\n')
357             return *aLinePtr = linebuf;
358         bufsize += MAX_LENGTH;
359         if (!(linebuf = realloc(linebuf, bufsize))) {
360             perror("fgetslong: realloc");
361             exit(1);
362         }
363     }
364     return *aLinePtr = linebuf[0] ? linebuf : 0;
365 }
366
367 int main(
368     int argc,
369     char *argv[])
370 {
371     char    **myargv;
372     char     *aLine;
373     char     *firstdir = "";
374
375 #ifdef MUST_DISABLE_SIGFPE
376     signal(SIGFPE, SIG_IGN);
377 #endif
378 #ifdef MUST_DISABLE_FPMASK
379     fpsetmask(0);
380 #endif
381 #ifdef HAVE_LOCALE_H
382     setlocale (LC_ALL, "");
383 #endif
384 #ifdef HAVE_LIBINTL_H
385     bindtextdomain (GETTEXT_PACKAGE, LOCALEDIR);
386     bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8");
387     textdomain (GETTEXT_PACKAGE);
388 #endif
389     if (argc == 1) {
390         PrintUsage("");
391         return 0;
392     }
393
394     if (((argc == 2) || (argc == 3)) && !strcmp("-", argv[1])) {
395 #if HAVE_GETRUSAGE
396         struct rusage myusage;
397         struct timeval starttime;
398         struct timeval currenttime;
399
400         gettimeofday(&starttime, NULL);
401 #endif
402         RemoteMode = 1;
403         if ((argc == 3) && strcmp("", argv[2])) {
404
405             if (
406 #ifdef HAVE_GETUID
407                    getuid()
408 #else
409                    1
410 #endif
411                    == 0) {
412
413 #ifdef HAVE_CHROOT
414                 chroot(argv[2]);
415                 if (errno != 0) {
416                     fprintf(stderr,
417                             "ERROR: can't change root to '%s' errno=%d\n",
418                             argv[2], errno);
419                     exit(errno);
420                 }
421                 ChangeRoot = 1;
422                 firstdir = "/";
423 #else
424                 fprintf(stderr,
425                         "ERROR: change root is not supported by your OS "
426                         "or at least by this copy of rrdtool\n");
427                 exit(1);
428 #endif
429             } else {
430                 firstdir = argv[2];
431             }
432         }
433         if (strcmp(firstdir, "")) {
434             chdir(firstdir);
435             if (errno != 0) {
436                 fprintf(stderr, "ERROR: %s\n", rrd_strerror(errno));
437                 exit(errno);
438             }
439         }
440
441         while (fgetslong(&aLine, stdin)) {
442             if ((argc = CountArgs(aLine)) == 0) {
443                 printf("ERROR: not enough arguments\n");
444             }
445             if ((myargv = (char **) malloc((argc + 1) *
446                                            sizeof(char *))) == NULL) {
447                 perror("malloc");
448                 exit(1);
449             }
450             if ((argc = CreateArgs(argv[0], aLine, argc, myargv)) < 0) {
451                 printf("ERROR: creating arguments\n");
452             } else {
453                 int       ret = HandleInputLine(argc, myargv, stdout);
454
455                 free(myargv);
456                 if (ret == 0) {
457 #if HAVE_GETRUSAGE
458                     getrusage(RUSAGE_SELF, &myusage);
459                     gettimeofday(&currenttime, NULL);
460                     printf("OK u:%1.2f s:%1.2f r:%1.2f\n",
461                            (double) myusage.ru_utime.tv_sec +
462                            (double) myusage.ru_utime.tv_usec / 1000000.0,
463                            (double) myusage.ru_stime.tv_sec +
464                            (double) myusage.ru_stime.tv_usec / 1000000.0,
465                            (double) (currenttime.tv_sec - starttime.tv_sec)
466                            + (double) (currenttime.tv_usec -
467                                        starttime.tv_usec)
468                            / 1000000.0);
469 #else
470                     printf("OK\n");
471
472 #endif
473                 }
474             }
475             fflush(stdout); /* this is important for pipes to work */
476             free(aLine);
477         }
478     } else if (argc == 2) {
479         PrintUsage(argv[1]);
480         exit(0);
481     } else if (argc == 3 && !strcmp(argv[1], "help")) {
482         PrintUsage(argv[2]);
483         exit(0);
484     } else {
485         exit(HandleInputLine(argc, argv, stderr));
486     }
487     return 0;
488 }
489
490 /* HandleInputLine is NOT thread safe - due to readdir issues,
491    resolving them portably is not really simple. */
492 int HandleInputLine(
493     int argc,
494     char **argv,
495     FILE * out)
496 {
497 #if defined(HAVE_OPENDIR) && defined (HAVE_READDIR)
498     DIR      *curdir;   /* to read current dir with ls */
499     struct dirent *dent;
500 #endif
501 #if defined(HAVE_SYS_STAT_H)
502     struct stat st;
503 #endif
504     char     *cwd;      /* To hold current working dir on call to pwd */
505
506     /* Reset errno to 0 before we start.
507      */
508     errno = 0;
509
510     if (RemoteMode) {
511         if (argc > 1 && strcmp("quit", argv[1]) == 0) {
512             if (argc > 2) {
513                 printf("ERROR: invalid parameter count for quit\n");
514                 return (1);
515             }
516             exit(0);
517         }
518 #if defined(HAVE_OPENDIR) && defined(HAVE_READDIR) && defined(HAVE_CHDIR)
519         if (argc > 1 && strcmp("cd", argv[1]) == 0) {
520             if (argc > 3) {
521                 printf("ERROR: invalid parameter count for cd\n");
522                 return (1);
523             }
524 #if ! defined(HAVE_CHROOT) || ! defined(HAVE_GETUID)
525             if (getuid() == 0 && !ChangeRoot) {
526                 printf
527                     ("ERROR: chdir security problem - rrdtool is running as "
528                      "root but not chroot!\n");
529                 return (1);
530             }
531 #endif
532             chdir(argv[2]);
533             if (errno != 0) {
534                 printf("ERROR: %s\n", rrd_strerror(errno));
535                 return (1);
536             }
537             return (0);
538         }
539         if (argc > 1 && strcmp("pwd", argv[1]) == 0) {
540             if (argc > 2) {
541                 printf("ERROR: invalid parameter count for pwd\n");
542                 return (1);
543             }
544             cwd = getcwd(NULL, MAXPATH);
545             if (cwd == NULL) {
546                 printf("ERROR: %s\n", rrd_strerror(errno));
547                 return (1);
548             }
549             printf("%s\n", cwd);
550             free(cwd);
551             return (0);
552         }
553         if (argc > 1 && strcmp("mkdir", argv[1]) == 0) {
554             if (argc > 3) {
555                 printf("ERROR: invalid parameter count for mkdir\n");
556                 return (1);
557             }
558 #if ! defined(HAVE_CHROOT) || ! defined(HAVE_GETUID)
559             if (getuid() == 0 && !ChangeRoot) {
560                 printf
561                     ("ERROR: mkdir security problem - rrdtool is running as "
562                      "root but not chroot!\n");
563                 return (1);
564             }
565 #endif
566             mkdir(argv[2], 0777);
567             if (errno != 0) {
568                 printf("ERROR: %s\n", rrd_strerror(errno));
569                 return (1);
570             }
571             return (0);
572         }
573         if (argc > 1 && strcmp("ls", argv[1]) == 0) {
574             if (argc > 2) {
575                 printf("ERROR: invalid parameter count for ls\n");
576                 return (1);
577             }
578             if ((curdir = opendir(".")) != NULL) {
579                 while ((dent = readdir(curdir)) != NULL) {
580                     if (!stat(dent->d_name, &st)) {
581                         if (S_ISDIR(st.st_mode)) {
582                             printf("d %s\n", dent->d_name);
583                         }
584                         if (strlen(dent->d_name) > 4 && S_ISREG(st.st_mode)) {
585                             if (!strcmp
586                                 (dent->d_name + NAMLEN(dent) - 4, ".rrd")
587                                 || !strcmp(dent->d_name + NAMLEN(dent) - 4,
588                                            ".RRD")) {
589                                 printf("- %s\n", dent->d_name);
590                             }
591                         }
592                     }
593                 }
594                 closedir(curdir);
595             } else {
596                 printf("ERROR: %s\n", rrd_strerror(errno));
597                 return (errno);
598             }
599             return (0);
600         }
601 #endif                          /* opendir and readdir */
602
603     }
604     if (argc < 3
605         || strcmp("help", argv[1]) == 0
606         || strcmp("--help", argv[1]) == 0
607         || strcmp("-help", argv[1]) == 0
608         || strcmp("-?", argv[1]) == 0 || strcmp("-h", argv[1]) == 0) {
609         PrintUsage("");
610         return 0;
611     }
612
613     if (strcmp("create", argv[1]) == 0)
614         rrd_create(argc - 1, &argv[1]);
615     else if (strcmp("dump", argv[1]) == 0)
616         rrd_dump(argc - 1, &argv[1]);
617     else if (strcmp("info", argv[1]) == 0 || strcmp("updatev", argv[1]) == 0) {
618         info_t   *data, *save;
619
620         if (strcmp("info", argv[1]) == 0)
621             data = rrd_info(argc - 1, &argv[1]);
622         else
623             data = rrd_update_v(argc - 1, &argv[1]);
624         while (data) {
625             save = data;
626             printf("%s = ", data->key);
627             free(data->key);
628
629             switch (data->type) {
630             case RD_I_VAL:
631                 if (isnan(data->value.u_val))
632                     printf("NaN");
633                 else
634                     printf("%0.10e", data->value.u_val);
635                 break;
636             case RD_I_CNT:
637                 printf("%lu", data->value.u_cnt);
638                 break;
639             case RD_I_INT:
640                 printf("%d", data->value.u_int);
641                 break;
642             case RD_I_STR:
643                 printf("\"%s\"", data->value.u_str);
644                 free(data->value.u_str);
645                 break;
646             }
647             data = data->next;
648             free(save);
649             printf("\n");
650         }
651         free(data);
652     }
653
654     else if (strcmp("--version", argv[1]) == 0 ||
655              strcmp("version", argv[1]) == 0 ||
656              strcmp("v", argv[1]) == 0 ||
657              strcmp("-v", argv[1]) == 0 || strcmp("-version", argv[1]) == 0)
658         printf("RRDtool " PACKAGE_VERSION
659                "  Copyright by Tobi Oetiker, 1997-2005 (%f)\n",
660                rrd_version());
661     else if (strcmp("restore", argv[1]) == 0)
662         rrd_restore(argc - 1, &argv[1]);
663     else if (strcmp("resize", argv[1]) == 0)
664         rrd_resize(argc - 1, &argv[1]);
665     else if (strcmp("last", argv[1]) == 0)
666         printf("%ld\n", rrd_last(argc - 1, &argv[1]));
667     else if (strcmp("lastupdate", argv[1]) == 0) {
668         time_t    last_update;
669         char    **ds_namv;
670         char    **last_ds;
671         unsigned long ds_cnt, i;
672
673         if (rrd_lastupdate(argc - 1, &argv[1], &last_update,
674                            &ds_cnt, &ds_namv, &last_ds) == 0) {
675             for (i = 0; i < ds_cnt; i++)
676                 printf(" %s", ds_namv[i]);
677             printf("\n\n");
678             printf("%10lu:", last_update);
679             for (i = 0; i < ds_cnt; i++) {
680                 printf(" %s", last_ds[i]);
681                 free(last_ds[i]);
682                 free(ds_namv[i]);
683             }
684             printf("\n");
685             free(last_ds);
686             free(ds_namv);
687         }
688     } else if (strcmp("first", argv[1]) == 0)
689         printf("%ld\n", rrd_first(argc - 1, &argv[1]));
690     else if (strcmp("update", argv[1]) == 0)
691         rrd_update(argc - 1, &argv[1]);
692     else if (strcmp("fetch", argv[1]) == 0) {
693         time_t    start, end, ti;
694         unsigned long step, ds_cnt, i, ii;
695         rrd_value_t *data, *datai;
696         char    **ds_namv;
697
698         if (rrd_fetch
699             (argc - 1, &argv[1], &start, &end, &step, &ds_cnt, &ds_namv,
700              &data) != -1) {
701             datai = data;
702             printf("           ");
703             for (i = 0; i < ds_cnt; i++)
704                 printf("%20s", ds_namv[i]);
705             printf("\n\n");
706             for (ti = start + step; ti <= end; ti += step) {
707                 printf("%10lu:", ti);
708                 for (ii = 0; ii < ds_cnt; ii++)
709                     printf(" %0.10e", *(datai++));
710                 printf("\n");
711             }
712             for (i = 0; i < ds_cnt; i++)
713                 free(ds_namv[i]);
714             free(ds_namv);
715             free(data);
716         }
717     } else if (strcmp("xport", argv[1]) == 0) {
718         int       xxsize;
719         unsigned long int j = 0;
720         time_t    start, end, ti;
721         unsigned long step, col_cnt, row_cnt;
722         rrd_value_t *data, *ptr;
723         char    **legend_v;
724         int       enumds = 0;
725         int       i;
726         size_t    vtag_s = strlen(COL_DATA_TAG) + 10;
727         char     *vtag = malloc(vtag_s);
728
729         for (i = 2; i < argc; i++) {
730             if (strcmp("--enumds", argv[i]) == 0)
731                 enumds = 1;
732         }
733
734         if (rrd_xport
735             (argc - 1, &argv[1], &xxsize, &start, &end, &step, &col_cnt,
736              &legend_v, &data) != -1) {
737             row_cnt = (end - start) / step;
738             ptr = data;
739             printf("<?xml version=\"1.0\" encoding=\"%s\"?>\n\n",
740                    XML_ENCODING);
741             printf("<%s>\n", ROOT_TAG);
742             printf("  <%s>\n", META_TAG);
743             printf("    <%s>%lu</%s>\n", META_START_TAG, start + step,
744                    META_START_TAG);
745             printf("    <%s>%lu</%s>\n", META_STEP_TAG, step, META_STEP_TAG);
746             printf("    <%s>%lu</%s>\n", META_END_TAG, end, META_END_TAG);
747             printf("    <%s>%lu</%s>\n", META_ROWS_TAG, row_cnt,
748                    META_ROWS_TAG);
749             printf("    <%s>%lu</%s>\n", META_COLS_TAG, col_cnt,
750                    META_COLS_TAG);
751             printf("    <%s>\n", LEGEND_TAG);
752             for (j = 0; j < col_cnt; j++) {
753                 char     *entry = NULL;
754
755                 entry = legend_v[j];
756                 printf("      <%s>%s</%s>\n", LEGEND_ENTRY_TAG, entry,
757                        LEGEND_ENTRY_TAG);
758                 free(entry);
759             }
760             free(legend_v);
761             printf("    </%s>\n", LEGEND_TAG);
762             printf("  </%s>\n", META_TAG);
763             printf("  <%s>\n", DATA_TAG);
764             for (ti = start + step; ti <= end; ti += step) {
765                 printf("    <%s>", DATA_ROW_TAG);
766                 printf("<%s>%lu</%s>", COL_TIME_TAG, ti, COL_TIME_TAG);
767                 for (j = 0; j < col_cnt; j++) {
768                     rrd_value_t newval = DNAN;
769
770                     if (enumds == 1)
771                         snprintf(vtag, vtag_s, "%s%lu", COL_DATA_TAG, j);
772                     else
773                         snprintf(vtag, vtag_s, "%s", COL_DATA_TAG);
774
775                     newval = *ptr;
776                     if (isnan(newval)) {
777                         printf("<%s>NaN</%s>", vtag, vtag);
778                     } else {
779                         printf("<%s>%0.10e</%s>", vtag, newval, vtag);
780                     };
781                     ptr++;
782                 }
783                 printf("</%s>\n", DATA_ROW_TAG);
784             }
785             free(data);
786             printf("  </%s>\n", DATA_TAG);
787             printf("</%s>\n", ROOT_TAG);
788         }
789         free(vtag);
790     } else if (strcmp("graph", argv[1]) == 0) {
791         char    **calcpr;
792
793 #ifdef notused /*XXX*/
794         const char *imgfile = argv[2];  /* rrd_graph changes argv pointer */
795 #endif
796         int       xsize, ysize;
797         double    ymin, ymax;
798         int       i;
799         int       tostdout = (strcmp(argv[2], "-") == 0);
800         int       imginfo = 0;
801
802         for (i = 2; i < argc; i++) {
803             if (strcmp(argv[i], "--imginfo") == 0
804                 || strcmp(argv[i], "-f") == 0) {
805                 imginfo = 1;
806                 break;
807             }
808         }
809         if (rrd_graph
810             (argc - 1, &argv[1], &calcpr, &xsize, &ysize, NULL, &ymin,
811              &ymax) != -1) {
812             if (!tostdout && !imginfo)
813                 printf("%dx%d\n", xsize, ysize);
814             if (calcpr) {
815                 for (i = 0; calcpr[i]; i++) {
816                     if (!tostdout)
817                         printf("%s\n", calcpr[i]);
818                     free(calcpr[i]);
819                 }
820                 free(calcpr);
821             }
822         }
823
824     } else if (strcmp("tune", argv[1]) == 0)
825         rrd_tune(argc - 1, &argv[1]);
826     else {
827         rrd_set_error("unknown function '%s'", argv[1]);
828     }
829     if (rrd_test_error()) {
830         fprintf(out, "ERROR: %s\n", rrd_get_error());
831         rrd_clear_error();
832         return 1;
833     }
834     return (0);
835 }
836
837 int CountArgs(
838     char *aLine)
839 {
840     int       i = 0;
841     int       aCount = 0;
842     int       inarg = 0;
843
844     while (aLine[i] == ' ')
845         i++;
846     while (aLine[i] != 0) {
847         if ((aLine[i] == ' ') && inarg) {
848             inarg = 0;
849         }
850         if ((aLine[i] != ' ') && !inarg) {
851             inarg = 1;
852             aCount++;
853         }
854         i++;
855     }
856     return aCount;
857 }
858
859 /*
860  * CreateArgs - take a string (aLine) and tokenize
861  */
862 int CreateArgs(
863     char *pName,
864     char *aLine,
865     int argc,
866     char **argv)
867 {
868     char     *getP, *putP;
869     char    **pargv = argv;
870     char      Quote = 0;
871     int       inArg = 0;
872     int       len;
873
874     len = strlen(aLine);
875     /* remove trailing space and newlines */
876     while (len && aLine[len] <= ' ') {
877         aLine[len] = 0;
878         len--;
879     }
880     /* sikp leading blanks */
881     while (*aLine && *aLine <= ' ')
882         aLine++;
883
884     pargv[0] = pName;
885     argc = 1;
886     getP = aLine;
887     putP = aLine;
888     while (*getP) {
889         switch (*getP) {
890         case ' ':
891             if (Quote) {
892                 *(putP++) = *getP;
893             } else if (inArg) {
894                 *(putP++) = 0;
895                 inArg = 0;
896             }
897             break;
898         case '"':
899         case '\'':
900             if (Quote != 0) {
901                 if (Quote == *getP)
902                     Quote = 0;
903                 else {
904                     *(putP++) = *getP;
905                 }
906             } else {
907                 if (!inArg) {
908                     pargv[argc++] = putP;
909                     inArg = 1;
910                 }
911                 Quote = *getP;
912             }
913             break;
914         default:
915             if (!inArg) {
916                 pargv[argc++] = putP;
917                 inArg = 1;
918             }
919             *(putP++) = *getP;
920             break;
921         }
922         getP++;
923     }
924
925     *putP = '\0';
926
927     if (Quote)
928         return -1;
929     else
930         return argc;
931 }