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