action "graph": Implement handling of arbitrary begin/end times.
[collection4.git] / src / action_graph.c
1 #include <stdlib.h>
2 #include <stdio.h>
3 #include <string.h>
4 #include <errno.h>
5 #include <stdint.h>
6 #include <inttypes.h>
7 #include <dirent.h> /* for PATH_MAX */
8 #include <assert.h>
9 #include <math.h>
10
11 #include <rrd.h>
12
13 #include "common.h"
14 #include "action_graph.h"
15 #include "graph_list.h"
16 #include "utils_params.h"
17 #include "utils_array.h"
18
19 #include <fcgiapp.h>
20 #include <fcgi_stdio.h>
21
22 static int get_time_args (str_array_t *args) /* {{{ */
23 {
24   const char *begin_str;
25   const char *end_str;
26   long now;
27   long begin;
28   long end;
29   char *endptr;
30   long tmp;
31
32   begin_str = param ("begin");
33   end_str = param ("end");
34
35   now = (long) time (NULL);
36
37   if (begin_str != NULL)
38   {
39     endptr = NULL;
40     errno = 0;
41     tmp = strtol (begin_str, &endptr, /* base = */ 0);
42     if ((endptr == begin_str) || (errno != 0))
43       return (-1);
44     if (tmp <= 0)
45       begin = now + tmp;
46     else
47       begin = tmp;
48   }
49   else /* if (begin_str == NULL) */
50   {
51     begin = now - 86400;
52   }
53
54   if (end_str != NULL)
55   {
56     endptr = NULL;
57     errno = 0;
58     tmp = strtol (end_str, &endptr, /* base = */ 0);
59     if ((endptr == end_str) || (errno != 0))
60       return (-1);
61     end = tmp;
62     if (tmp <= 0)
63       end = now + tmp;
64     else
65       end = tmp;
66   }
67   else /* if (end_str == NULL) */
68   {
69     end = now;
70   }
71
72   if (begin == end)
73     return (-1);
74
75   if (begin > end)
76   {
77     tmp = begin;
78     begin = end;
79     end = tmp;
80   }
81
82   array_append (args, "-s");
83   array_append_format (args, "%li", begin);
84   array_append (args, "-e");
85   array_append_format (args, "%li", end);
86
87   return (0);
88 } /* }}} int get_time_args */
89
90 static void emulate_graph (int argc, char **argv) /* {{{ */
91 {
92   int i;
93
94   printf ("rrdtool \\\n");
95   for (i = 0; i < argc; i++)
96   {
97     if (i < (argc - 1))
98       printf ("  \"%s\" \\\n", argv[i]);
99     else
100       printf ("  \"%s\"\n", argv[i]);
101   }
102 } /* }}} void emulate_graph */
103
104 static int ag_info_print (rrd_info_t *info) /* {{{ */
105 {
106   if (info->type == RD_I_VAL)
107     printf ("[info] %s = %g;\n", info->key, info->value.u_val);
108   else if (info->type == RD_I_CNT)
109     printf ("[info] %s = %lu;\n", info->key, info->value.u_cnt);
110   else if (info->type == RD_I_STR)
111     printf ("[info] %s = %s;\n", info->key, info->value.u_str);
112   else if (info->type == RD_I_INT)
113     printf ("[info] %s = %i;\n", info->key, info->value.u_int);
114   else if (info->type == RD_I_BLO)
115     printf ("[info] %s = [blob, %lu bytes];\n", info->key, info->value.u_blo.size);
116   else
117     printf ("[info] %s = [unknown type %#x];\n", info->key, info->type);
118
119   return (0);
120 } /* }}} int ag_info_print */
121
122 static int output_graph (rrd_info_t *info) /* {{{ */
123 {
124   rrd_info_t *img;
125
126   for (img = info; img != NULL; img = img->next)
127     if ((strcmp ("image", img->key) == 0)
128         && (img->type == RD_I_BLO))
129       break;
130
131   if (img == NULL)
132     return (ENOENT);
133
134   printf ("Content-Type: image/png\n"
135       "Content-Length: %lu\n"
136       "\n",
137       img->value.u_blo.size);
138   fwrite (img->value.u_blo.ptr, img->value.u_blo.size,
139       /* nmemb = */ 1, stdout);
140
141   return (0);
142 } /* }}} int output_graph */
143
144 #define OUTPUT_ERROR(...) do {             \
145   printf ("Content-Type: text/plain\n\n"); \
146   printf (__VA_ARGS__);                    \
147   return (0);                              \
148 } while (0)
149
150 int action_graph (void) /* {{{ */
151 {
152   str_array_t *args;
153   graph_config_t *cfg;
154   graph_instance_t *inst;
155   rrd_info_t *info;
156   int status;
157
158   cfg = gl_graph_get_selected ();
159   if (cfg == NULL)
160     OUTPUT_ERROR ("gl_graph_get_selected () failed.\n");
161
162   inst = inst_get_selected (cfg);
163   if (inst == NULL)
164     OUTPUT_ERROR ("inst_get_selected (%p) failed.\n", (void *) cfg);
165
166   args = array_create ();
167   if (args == NULL)
168     return (ENOMEM);
169
170   array_append (args, "graph");
171   array_append (args, "-");
172   array_append (args, "--imgformat");
173   array_append (args, "PNG");
174
175   get_time_args (args);
176
177   status = inst_get_rrdargs (cfg, inst, args);
178   if (status != 0)
179   {
180     array_destroy (args);
181     OUTPUT_ERROR ("inst_get_rrdargs failed with status %i.\n", status);
182   }
183
184   rrd_clear_error ();
185   info = rrd_graph_v (array_argc (args), array_argv (args));
186   if ((info == NULL) || rrd_test_error ())
187   {
188     printf ("Content-Type: text/plain\n\n");
189     printf ("rrd_graph_v failed: %s\n", rrd_get_error ());
190     emulate_graph (array_argc (args), array_argv (args));
191   }
192   else
193   {
194     int status;
195
196     status = output_graph (info);
197     if (status != 0)
198     {
199       rrd_info_t *ptr;
200
201       printf ("Content-Type: text/plain\n\n");
202       printf ("output_graph failed. Maybe the \"image\" info was not found?\n\n");
203
204       for (ptr = info; ptr != NULL; ptr = ptr->next)
205       {
206         ag_info_print (ptr);
207       }
208     }
209   }
210
211   if (info != NULL)
212     rrd_info_free (info);
213
214   array_destroy (args);
215   args = NULL;
216
217   return (0);
218 } /* }}} int action_graph */
219
220 /* vim: set sw=2 sts=2 et fdm=marker : */