src/action_graph.c: Send an "Expires" header.
[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     {
165       printf ("Last-Modified: %s\n", time_buffer);
166       printf ("X-Last-Modified: %s\n", time_buffer);
167       fprintf (stderr, "Last-Modified: %s (%li)\n", time_buffer, (long) data->mtime);
168     }
169   }
170
171   /* Print Expires header. */
172   if (data->end >= data->now)
173   {
174     /* The end of the timespan can be seen. */
175     long secs_per_pixel;
176
177     /* FIXME: Handle graphs with width != 400. */
178     secs_per_pixel = (data->end - data->begin) / 400;
179
180     expires = (time_t) (data->now + secs_per_pixel);
181   }
182   else /* if (data->end < data->now) */
183   {
184     expires = (time_t) (data->now + 86400);
185   }
186   status = time_to_rfc1123 (expires, time_buffer, sizeof (time_buffer));
187   if (status == 0)
188     printf ("Expires: %s\n", time_buffer);
189
190   printf ("\n");
191
192   fwrite (img->value.u_blo.ptr, img->value.u_blo.size,
193       /* nmemb = */ 1, stdout);
194
195   return (0);
196 } /* }}} int output_graph */
197
198 #define OUTPUT_ERROR(...) do {             \
199   printf ("Content-Type: text/plain\n\n"); \
200   printf (__VA_ARGS__);                    \
201   return (0);                              \
202 } while (0)
203
204 int action_graph (void) /* {{{ */
205 {
206   graph_data_t data;
207   graph_config_t *cfg;
208   graph_instance_t *inst;
209   int status;
210
211   cfg = gl_graph_get_selected ();
212   if (cfg == NULL)
213     OUTPUT_ERROR ("gl_graph_get_selected () failed.\n");
214
215   inst = inst_get_selected (cfg);
216   if (inst == NULL)
217     OUTPUT_ERROR ("inst_get_selected (%p) failed.\n", (void *) cfg);
218
219   data.args = array_create ();
220   if (data.args == NULL)
221     return (ENOMEM);
222
223   array_append (data.args, "graph");
224   array_append (data.args, "-");
225   array_append (data.args, "--imgformat");
226   array_append (data.args, "PNG");
227
228   get_time_args (&data);
229
230   status = inst_get_rrdargs (cfg, inst, data.args);
231   if (status != 0)
232   {
233     array_destroy (data.args);
234     OUTPUT_ERROR ("inst_get_rrdargs failed with status %i.\n", status);
235   }
236
237   rrd_clear_error ();
238   data.info = rrd_graph_v (array_argc (data.args), array_argv (data.args));
239   if ((data.info == NULL) || rrd_test_error ())
240   {
241     printf ("Content-Type: text/plain\n\n");
242     printf ("rrd_graph_v failed: %s\n", rrd_get_error ());
243     emulate_graph (array_argc (data.args), array_argv (data.args));
244   }
245   else
246   {
247     int status;
248
249     data.mtime = inst_get_mtime (inst);
250
251     status = output_graph (&data);
252     if (status != 0)
253     {
254       rrd_info_t *ptr;
255
256       printf ("Content-Type: text/plain\n\n");
257       printf ("output_graph failed. Maybe the \"image\" info was not found?\n\n");
258
259       for (ptr = data.info; ptr != NULL; ptr = ptr->next)
260       {
261         ag_info_print (ptr);
262       }
263     }
264   }
265
266   if (data.info != NULL)
267     rrd_info_free (data.info);
268
269   array_destroy (data.args);
270   data.args = NULL;
271
272   return (0);
273 } /* }}} int action_graph */
274
275 /* vim: set sw=2 sts=2 et fdm=marker : */