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