Various modules: Use the new "rrd_args_t" structure.
[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.h"
16 #include "graph_instance.h"
17 #include "graph_list.h"
18 #include "utils_cgi.h"
19 #include "utils_array.h"
20
21 #include <fcgiapp.h>
22 #include <fcgi_stdio.h>
23
24 struct graph_data_s
25 {
26   rrd_args_t *args;
27   rrd_info_t *info;
28   time_t mtime;
29   time_t expires;
30   long now;
31   long begin;
32   long end;
33 };
34 typedef struct graph_data_s graph_data_t;
35
36 static int get_time_args (graph_data_t *data) /* {{{ */
37 {
38   const char *begin_str;
39   const char *end_str;
40   long now;
41   long begin;
42   long end;
43   char *endptr;
44   long tmp;
45
46   begin_str = param ("begin");
47   end_str = param ("end");
48
49   now = (long) time (NULL);
50   data->now = now;
51   data->begin = now - 86400;
52   data->end = now;
53
54   if (begin_str != NULL)
55   {
56     endptr = NULL;
57     errno = 0;
58     tmp = strtol (begin_str, &endptr, /* base = */ 0);
59     if ((endptr == begin_str) || (errno != 0))
60       return (-1);
61     if (tmp <= 0)
62       begin = now + tmp;
63     else
64       begin = tmp;
65   }
66   else /* if (begin_str == NULL) */
67   {
68     begin = now - 86400;
69   }
70
71   if (end_str != NULL)
72   {
73     endptr = NULL;
74     errno = 0;
75     tmp = strtol (end_str, &endptr, /* base = */ 0);
76     if ((endptr == end_str) || (errno != 0))
77       return (-1);
78     end = tmp;
79     if (tmp <= 0)
80       end = now + tmp;
81     else
82       end = tmp;
83   }
84   else /* if (end_str == NULL) */
85   {
86     end = now;
87   }
88
89   if (begin == end)
90     return (-1);
91
92   if (begin > end)
93   {
94     tmp = begin;
95     begin = end;
96     end = tmp;
97   }
98
99   data->begin = begin;
100   data->end = end;
101
102   array_append (data->args->options, "-s");
103   array_append_format (data->args->options, "%li", begin);
104   array_append (data->args->options, "-e");
105   array_append_format (data->args->options, "%li", end);
106
107   return (0);
108 } /* }}} int get_time_args */
109
110 static void emulate_graph (int argc, char **argv) /* {{{ */
111 {
112   int i;
113
114   printf ("rrdtool \\\n");
115   for (i = 0; i < argc; i++)
116   {
117     if (i < (argc - 1))
118       printf ("  \"%s\" \\\n", argv[i]);
119     else
120       printf ("  \"%s\"\n", argv[i]);
121   }
122 } /* }}} void emulate_graph */
123
124 static int ag_info_print (rrd_info_t *info) /* {{{ */
125 {
126   if (info->type == RD_I_VAL)
127     printf ("[info] %s = %g;\n", info->key, info->value.u_val);
128   else if (info->type == RD_I_CNT)
129     printf ("[info] %s = %lu;\n", info->key, info->value.u_cnt);
130   else if (info->type == RD_I_STR)
131     printf ("[info] %s = %s;\n", info->key, info->value.u_str);
132   else if (info->type == RD_I_INT)
133     printf ("[info] %s = %i;\n", info->key, info->value.u_int);
134   else if (info->type == RD_I_BLO)
135     printf ("[info] %s = [blob, %lu bytes];\n", info->key, info->value.u_blo.size);
136   else
137     printf ("[info] %s = [unknown type %#x];\n", info->key, info->type);
138
139   return (0);
140 } /* }}} int ag_info_print */
141
142 static int output_graph (graph_data_t *data) /* {{{ */
143 {
144   rrd_info_t *img;
145   char time_buffer[256];
146   time_t expires;
147   int status;
148
149   for (img = data->info; img != NULL; img = img->next)
150     if ((strcmp ("image", img->key) == 0)
151         && (img->type == RD_I_BLO))
152       break;
153
154   if (img == NULL)
155     return (ENOENT);
156
157   printf ("Content-Type: image/png\n"
158       "Content-Length: %lu\n",
159       img->value.u_blo.size);
160   if (data->mtime > 0)
161   {
162     int status;
163     
164     status = time_to_rfc1123 (data->mtime, time_buffer, sizeof (time_buffer));
165     if (status == 0)
166       printf ("Last-Modified: %s\n", time_buffer);
167   }
168
169   /* Print Expires header. */
170   if (data->end >= data->now)
171   {
172     /* The end of the timespan can be seen. */
173     long secs_per_pixel;
174
175     /* FIXME: Handle graphs with width != 400. */
176     secs_per_pixel = (data->end - data->begin) / 400;
177
178     expires = (time_t) (data->now + secs_per_pixel);
179   }
180   else /* if (data->end < data->now) */
181   {
182     expires = (time_t) (data->now + 86400);
183   }
184   status = time_to_rfc1123 (expires, time_buffer, sizeof (time_buffer));
185   if (status == 0)
186     printf ("Expires: %s\n", time_buffer);
187
188   printf ("\n");
189
190   fwrite (img->value.u_blo.ptr, img->value.u_blo.size,
191       /* nmemb = */ 1, stdout);
192
193   return (0);
194 } /* }}} int output_graph */
195
196 #define OUTPUT_ERROR(...) do {             \
197   printf ("Content-Type: text/plain\n\n"); \
198   printf (__VA_ARGS__);                    \
199   return (0);                              \
200 } while (0)
201
202 int action_graph (void) /* {{{ */
203 {
204   graph_data_t data;
205   graph_config_t *cfg;
206   graph_instance_t *inst;
207   int status;
208
209   int argc;
210   char **argv;
211
212   cfg = gl_graph_get_selected ();
213   if (cfg == NULL)
214     OUTPUT_ERROR ("gl_graph_get_selected () failed.\n");
215
216   inst = inst_get_selected (cfg);
217   if (inst == NULL)
218     OUTPUT_ERROR ("inst_get_selected (%p) failed.\n", (void *) cfg);
219
220   data.args = ra_create ();
221   if (data.args == NULL)
222     return (ENOMEM);
223
224   array_append (data.args->options, "graph");
225   array_append (data.args->options, "-");
226   array_append (data.args->options, "--imgformat");
227   array_append (data.args->options, "PNG");
228
229   get_time_args (&data);
230
231   status = inst_get_rrdargs (cfg, inst, data.args);
232   if (status != 0)
233   {
234     ra_destroy (data.args);
235     OUTPUT_ERROR ("inst_get_rrdargs failed with status %i.\n", status);
236   }
237
238   argc = ra_argc (data.args);
239   argv = ra_argv (data.args);
240   if ((argc < 0) || (argv == NULL))
241   {
242     ra_destroy (data.args);
243     return (-1);
244   }
245
246   rrd_clear_error ();
247   data.info = rrd_graph_v (argc, argv);
248   if ((data.info == NULL) || rrd_test_error ())
249   {
250     printf ("Content-Type: text/plain\n\n");
251     printf ("rrd_graph_v failed: %s\n", rrd_get_error ());
252     emulate_graph (argc, argv);
253   }
254   else
255   {
256     int status;
257
258     data.mtime = inst_get_mtime (inst);
259
260     status = output_graph (&data);
261     if (status != 0)
262     {
263       rrd_info_t *ptr;
264
265       printf ("Content-Type: text/plain\n\n");
266       printf ("output_graph failed. Maybe the \"image\" info was not found?\n\n");
267
268       for (ptr = data.info; ptr != NULL; ptr = ptr->next)
269       {
270         ag_info_print (ptr);
271       }
272     }
273   }
274
275   if (data.info != NULL)
276     rrd_info_free (data.info);
277
278   ra_argv_free (argv);
279   ra_destroy (data.args);
280   data.args = NULL;
281
282   return (0);
283 } /* }}} int action_graph */
284
285 /* vim: set sw=2 sts=2 et fdm=marker : */