graph_list: Add basic configuration infrastructure.
[collection4.git] / graph_list.c
1 #include <stdlib.h>
2 #include <stdio.h>
3 #include <string.h>
4 #include <time.h>
5 #include <errno.h>
6 #include <assert.h>
7
8 #include <fcgiapp.h>
9 #include <fcgi_stdio.h>
10
11 #include "graph_list.h"
12 #include "common.h"
13
14 /*
15  * Defines
16  */
17 #define UPDATE_INTERVAL 10
18
19 #define ANY_TOKEN "/any/"
20 #define ALL_TOKEN "/all/"
21
22 /*
23  * Data types
24  */
25 struct graph_ident_s /* {{{ */
26 {
27   char *host;
28   char *plugin;
29   char *plugin_instance;
30   char *type;
31   char *type_instance;
32 }; /* }}} struct graph_ident_s */
33
34 struct graph_instance_s;
35 typedef struct graph_instance_s graph_instance_t;
36 struct graph_instance_s
37 {
38   graph_ident_t select;
39
40   graph_ident_t *files;
41   size_t files_num;
42
43   graph_instance_t *next;
44 };
45
46 struct graph_config_s;
47 typedef struct graph_config_s graph_config_t;
48 struct graph_config_s
49 {
50   graph_ident_t select;
51
52   char *title;
53   char *vertical_label;
54
55   graph_instance_t *instances;
56
57   graph_config_t *next;
58 };
59
60 /*
61  * Global variables
62  */
63 static graph_config_t *graph_config_head = NULL;
64
65 static graph_ident_t *graph_list = NULL;
66 static size_t graph_list_length = 0;
67 static time_t gl_last_update = 0;
68
69 /*
70  * Private functions
71  */
72 static int print_files (const graph_instance_t *inst) /* {{{ */
73 {
74   size_t i;
75
76   for (i = 0; i < inst->files_num; i++)
77   {
78     graph_ident_t *file = inst->files + i;
79
80     printf ("    File \"%s/%s-%s/%s-%s\"\n",
81         file->host,
82         file->plugin, file->plugin_instance,
83         file->type, file->type_instance);
84   }
85
86   return (0);
87 } /* }}} int print_instances */
88
89 static int print_instances (const graph_config_t *cfg) /* {{{ */
90 {
91   graph_instance_t *inst;
92
93   for (inst = cfg->instances; inst != NULL; inst = inst->next)
94   {
95     printf ("  Instance \"%s/%s-%s/%s-%s\"\n",
96         inst->select.host,
97         inst->select.plugin, inst->select.plugin_instance,
98         inst->select.type, inst->select.type_instance);
99
100     print_files (inst);
101   }
102
103   return (0);
104 } /* }}} int print_instances */
105
106 static int print_graphs (void) /* {{{ */
107 {
108   graph_config_t *cfg;
109
110   for (cfg = graph_config_head; cfg != NULL; cfg = cfg->next)
111   {
112     printf ("Graph \"%s/%s-%s/%s-%s\"\n",
113         cfg->select.host,
114         cfg->select.plugin, cfg->select.plugin_instance,
115         cfg->select.type, cfg->select.type_instance);
116
117     print_instances (cfg);
118   }
119
120   return (0);
121 } /* }}} int print_graphs */
122
123
124 /* "Safe" version of strcmp(3): Either or both pointers may be NULL. */
125 static int strcmp_s (const char *s1, const char *s2) /* {{{ */
126 {
127   if ((s1 == NULL) && (s2 == NULL))
128     return (0);
129   else if (s1 == NULL)
130     return (1);
131   else if (s2 == NULL)
132     return (-1);
133   assert ((s1 != NULL) && (s2 != NULL));
134
135   return (strcmp (s1, s2));
136 } /* }}} int strcmp_s */
137
138 static _Bool part_matches (const char *sel, const char *val) /* {{{ */
139 {
140   if ((sel == NULL) && (val == NULL))
141     return (1);
142
143   if (sel == NULL) /* && (val != NULL) */
144     return (0);
145
146   if ((strcasecmp (ALL_TOKEN, sel) == 0)
147       || (strcasecmp (ANY_TOKEN, sel) == 0))
148     return (1);
149
150   if (val == NULL) /* && (sel != NULL) */
151     return (0);
152
153   if (strcmp (sel, val) == 0)
154     return (1);
155
156   return (0);
157 } /* }}} _Bool part_matches */
158
159 static _Bool file_matches (const graph_ident_t *sel, /* {{{ */
160     const graph_ident_t *val)
161 {
162   if ((sel == NULL) && (val == NULL))
163     return (0);
164   else if (sel == NULL)
165     return (-1);
166   else if (val == NULL)
167     return (1);
168
169   if (!part_matches (sel->host, val->host))
170     return (0);
171
172   if (!part_matches (sel->plugin, val->plugin))
173     return (0);
174
175   if (!part_matches (sel->plugin_instance, val->plugin_instance))
176     return (0);
177
178   if (!part_matches (sel->type, val->type))
179     return (0);
180
181   if (!part_matches (sel->type_instance, val->type_instance))
182     return (0);
183
184   return (1);
185 } /* }}} _Bool ident_compare */
186
187 /*
188  * Copy part of an identifier. If the "template" value is ANY_TOKEN, "value" is
189  * copied and returned. This function is used when creating graph_instance_t
190  * from graph_config_t.
191  */
192 static char *identifier_copy_part (const char *template, const char *value) /* {{{ */
193 {
194   if (template == NULL)
195   {
196     return (NULL);
197   }
198   else if (strcasecmp (ANY_TOKEN, template) == 0)
199   {
200     /* ANY in the graph selection => concrete value in the instance. */
201     if (value == NULL)
202       return (NULL);
203     else
204       return (strdup (value));
205   }
206   else if (strcasecmp (ALL_TOKEN, template) == 0)
207   {
208     /* ALL in the graph selection => ALL in the instance. */
209     return (strdup (ALL_TOKEN));
210   }
211   else
212   {
213     assert (value != NULL);
214     assert (strcmp (template, value) == 0);
215     return (strdup (template));
216   }
217 } /* }}} int identifier_copy_part */
218
219 static graph_instance_t *instance_create (graph_config_t *cfg, /* {{{ */
220     const graph_ident_t *file)
221 {
222   graph_instance_t *i;
223
224   if ((cfg == NULL) || (file == NULL))
225     return (NULL);
226
227   i = malloc (sizeof (*i));
228   if (i == NULL)
229     return (NULL);
230   memset (i, 0, sizeof (*i));
231
232   i->select.host = identifier_copy_part (cfg->select.host, file->host);
233   i->select.plugin = identifier_copy_part (cfg->select.plugin, file->plugin);
234   i->select.plugin_instance = identifier_copy_part (cfg->select.plugin_instance,
235       file->plugin_instance);
236   i->select.type = identifier_copy_part (cfg->select.type, file->type);
237   i->select.type_instance = identifier_copy_part (cfg->select.type_instance,
238       file->type_instance);
239
240   i->files = NULL;
241   i->files_num = 0;
242
243   i->next = NULL;
244
245   if (cfg->instances == NULL)
246     cfg->instances = i;
247   else
248   {
249     graph_instance_t *last;
250
251     last = cfg->instances;
252     while (last->next != NULL)
253       last = last->next;
254
255     last->next = i;
256   }
257
258   return (i);
259 } /* }}} graph_instance_t *instance_create */
260
261 static int instance_add_file (graph_instance_t *inst, /* {{{ */
262     const graph_ident_t *file)
263 {
264   graph_ident_t *tmp;
265
266   tmp = realloc (inst->files, sizeof (*inst->files) * (inst->files_num + 1));
267   if (tmp == NULL)
268     return (ENOMEM);
269   inst->files = tmp;
270   tmp = inst->files + inst->files_num;
271
272   memset (tmp, 0, sizeof (*tmp));
273   tmp->host = strdup (file->host);
274   tmp->plugin = strdup (file->plugin);
275   if (file->plugin_instance != NULL)
276     tmp->plugin_instance = strdup (file->plugin_instance);
277   else
278     tmp->plugin_instance = NULL;
279   tmp->type = strdup (file->type);
280   if (file->type_instance != NULL)
281     tmp->type_instance = strdup (file->type_instance);
282   else
283     tmp->type_instance = NULL;
284
285   inst->files_num++;
286
287   return (0);
288 } /* }}} int instance_add_file */
289
290 static graph_instance_t *graph_find_instance (graph_config_t *cfg, /* {{{ */
291     const graph_ident_t *file)
292 {
293   graph_instance_t *i;
294
295   if ((cfg == NULL) || (file == NULL))
296     return (NULL);
297
298   for (i = cfg->instances; i != NULL; i = i->next)
299     if (file_matches (&i->select, file))
300       return (i);
301
302   return (NULL);
303 } /* }}} graph_instance_t *graph_find_instance */
304
305 static int graph_add_file (graph_config_t *cfg, const graph_ident_t *file) /* {{{ */
306 {
307   graph_instance_t *inst;
308
309   inst = graph_find_instance (cfg, file);
310   if (inst == NULL)
311   {
312     inst = instance_create (cfg, file);
313     if (inst == NULL)
314       return (ENOMEM);
315   }
316
317   return (instance_add_file (inst, file));
318 } /* }}} int graph_add_file */
319
320 static int graph_append (graph_config_t *cfg) /* {{{ */
321 {
322   graph_config_t *last;
323
324   if (cfg == NULL)
325     return (EINVAL);
326
327   if (graph_config_head == NULL)
328   {
329     graph_config_head = cfg;
330     return (0);
331   }
332
333   last = graph_config_head;
334   while (last->next != NULL)
335     last = last->next;
336
337   last->next = cfg;
338
339   return (0);
340 } /* }}} int graph_append */
341
342 static int graph_create_from_file (const graph_ident_t *file) /* {{{ */
343 {
344   graph_config_t *cfg;
345
346   cfg = malloc (sizeof (*cfg));
347   if (cfg == NULL)
348     return (ENOMEM);
349   memset (cfg, 0, sizeof (*cfg));
350
351   cfg->select.host = strdup (file->host);
352   cfg->select.plugin = strdup (file->plugin);
353   cfg->select.plugin_instance = strdup (file->plugin_instance);
354   cfg->select.type = strdup (file->type);
355   cfg->select.type_instance = strdup (file->type_instance);
356
357   cfg->title = NULL;
358   cfg->vertical_label = NULL;
359   cfg->instances = NULL;
360   cfg->next = NULL;
361
362   graph_append (cfg);
363
364   return (graph_add_file (cfg, file));
365 } /* }}} int graph_create_from_file */
366
367 static int register_file (const graph_ident_t *file) /* {{{ */
368 {
369   graph_config_t *cfg;
370   int num_graphs = 0;
371
372   for (cfg = graph_config_head; cfg != NULL; cfg = cfg->next)
373   {
374     int status;
375
376     if (!file_matches (&cfg->select, file))
377       continue;
378
379     status = graph_add_file (cfg, file);
380     if (status != 0)
381     {
382       /* report error */;
383     }
384     else
385     {
386       num_graphs++;
387     }
388   }
389
390   if (num_graphs == 0)
391     graph_create_from_file (file);
392
393   return (0);
394 } /* }}} int register_file */
395
396 static int FIXME_graph_create_from_file (const graph_ident_t *file) /* {{{ */
397 {
398   graph_config_t *cfg;
399
400   cfg = malloc (sizeof (*cfg));
401   if (cfg == NULL)
402     return (ENOMEM);
403   memset (cfg, 0, sizeof (*cfg));
404
405   cfg->select.host = strdup (file->host);
406   cfg->select.plugin = strdup (file->plugin);
407   cfg->select.plugin_instance = strdup (file->plugin_instance);
408   cfg->select.type = strdup (file->type);
409   cfg->select.type_instance = strdup (file->type_instance);
410
411   cfg->title = NULL;
412   cfg->vertical_label = NULL;
413   cfg->instances = NULL;
414   cfg->next = NULL;
415
416   graph_append (cfg);
417
418   return (0);
419 } /* }}} int FIXME_graph_create_from_file */
420
421 static int read_graph_config (void) /* {{{ */
422 {
423   if (graph_config_head != NULL)
424     return (0);
425
426   graph_ident_t ident;
427
428
429   ident.host = ANY_TOKEN;
430   ident.plugin = "cpu";
431   ident.plugin_instance = ANY_TOKEN;
432   ident.type = "cpu";
433   ident.type_instance = ALL_TOKEN;
434   FIXME_graph_create_from_file (&ident);
435
436   ident.plugin = "memory";
437   ident.plugin_instance = "";
438   ident.type = "memory";
439   FIXME_graph_create_from_file (&ident);
440
441   ident.plugin = "swap";
442   ident.plugin_instance = "";
443   ident.type = "swap";
444   FIXME_graph_create_from_file (&ident);
445
446   ident.plugin = ANY_TOKEN;
447   ident.plugin_instance = ANY_TOKEN;
448   ident.type = "ps_state";
449   FIXME_graph_create_from_file (&ident);
450
451   ident.host = ALL_TOKEN;
452   ident.plugin = "cpu";
453   ident.plugin_instance = ALL_TOKEN;
454   ident.type = "cpu";
455   ident.type_instance = "idle";
456   FIXME_graph_create_from_file (&ident);
457
458   return (0);
459 } /* }}} int read_graph_config */
460
461
462
463 static int gl_compare (const void *p0, const void *p1) /* {{{ */
464 {
465   const graph_ident_t *gl0 = p0;
466   const graph_ident_t *gl1 = p1;
467   int status;
468
469   status = strcmp (gl0->host, gl1->host);
470   if (status != 0)
471     return (status);
472
473   status = strcmp (gl0->plugin, gl1->plugin);
474   if (status != 0)
475     return (status);
476
477   status = strcmp_s (gl0->plugin_instance, gl1->plugin_instance);
478   if (status != 0)
479     return (status);
480
481   status = strcmp (gl0->type, gl1->type);
482   if (status != 0)
483     return (status);
484
485   return (strcmp_s (gl0->type_instance, gl1->type_instance));
486 } /* }}} int gl_compare */
487
488 static void gl_clear_entry (graph_ident_t *gl) /* {{{ */
489 {
490   if (gl == NULL)
491     return;
492
493   free (gl->host);
494   free (gl->plugin);
495   free (gl->plugin_instance);
496   free (gl->type);
497   free (gl->type_instance);
498
499   gl->host = NULL;
500   gl->plugin = NULL;
501   gl->plugin_instance = NULL;
502   gl->type = NULL;
503   gl->type_instance = NULL;
504 } /* }}} void gl_clear_entry */
505
506 static void gl_clear (void) /* {{{ */
507 {
508   size_t i;
509
510   for (i = 0; i < graph_list_length; i++)
511     gl_clear_entry (graph_list + i);
512
513   free (graph_list);
514   graph_list = NULL;
515   graph_list_length = 0;
516   gl_last_update = 0;
517 } /* }}} void gl_clear */
518
519 static int gl_add_copy (graph_ident_t *gl) /* {{{ */
520 {
521   graph_ident_t *ptr;
522   int status;
523
524   if (gl == NULL)
525     return (EINVAL);
526
527   ptr = realloc (graph_list, sizeof (*graph_list) * (graph_list_length + 1));
528   if (ptr == NULL)
529     return (ENOMEM);
530   graph_list = ptr;
531
532   ptr = graph_list + graph_list_length;
533   memset (ptr, 0, sizeof (*ptr));
534   ptr->host = NULL;
535   ptr->plugin = NULL;
536   ptr->plugin_instance = NULL;
537   ptr->type = NULL;
538   ptr->type_instance = NULL;
539
540 #define DUP_OR_BREAK(member) do {                    \
541   ptr->member = NULL;                                \
542   if (gl->member != NULL)                            \
543   {                                                  \
544     ptr->member = strdup (gl->member);               \
545     if (ptr->member == NULL)                         \
546       break;                                         \
547   }                                                  \
548 } while (0)
549
550   status = ENOMEM;
551   do
552   {
553     DUP_OR_BREAK(host);
554     DUP_OR_BREAK(plugin);
555     DUP_OR_BREAK(plugin_instance);
556     DUP_OR_BREAK(type);
557     DUP_OR_BREAK(type_instance);
558
559     status = 0;
560   } while (0);
561
562 #undef DUP_OR_BREAK
563
564   if (status != 0)
565   {
566     free (ptr->host);
567     free (ptr->plugin);
568     free (ptr->plugin_instance);
569     free (ptr->type);
570     free (ptr->type_instance);
571     return (status);
572   }
573
574   graph_list_length++;
575   return (0);
576 } /* }}} int gl_add_copy */
577
578 static int callback_type (const char *type, void *user_data) /* {{{ */
579 {
580   graph_ident_t *gl;
581   int status;
582
583   if ((type == NULL) || (user_data == NULL))
584     return (EINVAL);
585
586   gl = user_data;
587   if ((gl->type != NULL) || (gl->type_instance != NULL))
588     return (EINVAL);
589
590   gl->type = strdup (type);
591   if (gl->type == NULL)
592     return (ENOMEM);
593
594   gl->type_instance = strchr (gl->type, '-');
595   if (gl->type_instance != NULL)
596   {
597     *gl->type_instance = 0;
598     gl->type_instance++;
599   }
600   else
601   {
602     gl->type_instance = gl->type + strlen (gl->type);
603   }
604
605   register_file (gl);
606
607   status = gl_add_copy (gl);
608
609   free (gl->type);
610   gl->type = NULL;
611   gl->type_instance = NULL;
612
613   return (status);
614 } /* }}} int callback_type */
615
616 static int callback_plugin (const char *plugin, void *user_data) /* {{{ */
617 {
618   graph_ident_t *gl;
619   int status;
620
621   if ((plugin == NULL) || (user_data == NULL))
622     return (EINVAL);
623
624   gl = user_data;
625   if ((gl->plugin != NULL) || (gl->plugin_instance != NULL))
626     return (EINVAL);
627
628   gl->plugin = strdup (plugin);
629   if (gl->plugin == NULL)
630     return (ENOMEM);
631
632   gl->plugin_instance = strchr (gl->plugin, '-');
633   if (gl->plugin_instance != NULL)
634   {
635     *gl->plugin_instance = 0;
636     gl->plugin_instance++;
637   }
638   else
639   {
640     gl->plugin_instance = gl->plugin + strlen (gl->plugin);
641   }
642
643   status = foreach_type (gl->host, plugin, callback_type, gl);
644
645   free (gl->plugin);
646   gl->plugin = NULL;
647   gl->plugin_instance = NULL;
648
649   return (status);
650 } /* }}} int callback_plugin */
651
652 static int callback_host (const char *host, void *user_data) /* {{{ */
653 {
654   graph_ident_t *gl;
655   int status;
656
657   if ((host == NULL) || (user_data == NULL))
658     return (EINVAL);
659
660   gl = user_data;
661   if (gl->host != NULL)
662     return (EINVAL);
663
664   gl->host = strdup (host);
665   if (gl->host == NULL)
666     return (ENOMEM);
667
668   status =  foreach_plugin (host, callback_plugin, gl);
669
670   free (gl->host);
671   gl->host = NULL;
672
673   return (status);
674 } /* }}} int callback_host */
675
676 /*
677  * Global functions
678  */
679 int gl_update (void) /* {{{ */
680 {
681   time_t now;
682   graph_ident_t gl;
683   int status;
684
685   printf ("Content-Type: text/plain\n\n");
686
687   read_graph_config ();
688
689   now = time (NULL);
690
691   if ((gl_last_update + UPDATE_INTERVAL) >= now)
692     return (0);
693
694   gl_clear ();
695
696   memset (&gl, 0, sizeof (gl));
697   gl.host = NULL;
698   gl.plugin = NULL;
699   gl.plugin_instance = NULL;
700   gl.type = NULL;
701   gl.type_instance = NULL;
702
703   status = foreach_host (callback_host, &gl);
704
705   print_graphs ();
706
707   if (graph_list_length > 1)
708     qsort (graph_list, graph_list_length, sizeof (*graph_list), gl_compare);
709
710   return (status);
711 } /* }}} int gl_update */
712
713 int gl_foreach (gl_callback callback, void *user_data) /* {{{ */
714 {
715   size_t i;
716
717   for (i = 0; i < graph_list_length; i++)
718   {
719     int status;
720
721     status = (*callback) ((void *) (graph_list + i), user_data);
722     if (status != 0)
723       return (status);
724   }
725
726   return (0);
727 } /* }}} int gl_foreach */
728
729 /* vim: set sw=2 sts=2 et fdm=marker : */