a07ed5c8d1591a4bb203bf0f815f5580c981ed5f
[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 #include <limits.h> /* PATH_MAX */
8
9 #include "graph_list.h"
10 #include "common.h"
11 #include "utils_params.h"
12
13 #include <fcgiapp.h>
14 #include <fcgi_stdio.h>
15
16 /*
17  * Defines
18  */
19 #define UPDATE_INTERVAL 10
20
21 #define ANY_TOKEN "/any/"
22 #define ALL_TOKEN "/all/"
23
24 #if 0
25 # define GL_DEBUG(...) do {             \
26   printf ("Content-Type: text/plain\n\n"); \
27   printf (__VA_ARGS__);                    \
28 } while (0)
29 #else
30 # define GL_DEBUG(...) /**/
31 #endif
32
33 /*
34  * Data types
35  */
36 struct graph_ident_s /* {{{ */
37 {
38   char *host;
39   char *plugin;
40   char *plugin_instance;
41   char *type;
42   char *type_instance;
43 }; /* }}} struct graph_ident_s */
44
45 struct graph_instance_s /* {{{ */
46 {
47   graph_ident_t select;
48
49   graph_ident_t *files;
50   size_t files_num;
51
52   graph_instance_t *next;
53 }; /* }}} struct graph_instance_s */
54
55 struct graph_config_s /* {{{ */
56 {
57   graph_ident_t select;
58
59   char *title;
60   char *vertical_label;
61
62   graph_instance_t *instances;
63
64   graph_config_t *next;
65 }; /* }}} struct graph_config_s */
66
67 /*
68  * Global variables
69  */
70 static graph_config_t *graph_config_head = NULL;
71
72 static graph_ident_t *graph_list = NULL;
73 static size_t graph_list_length = 0;
74 static time_t gl_last_update = 0;
75
76 /*
77  * Private functions
78  */
79 /* FIXME: These "print_*" functions are used for debugging. They should be
80  * removed at some point. */
81 static int print_files (const graph_instance_t *inst) /* {{{ */
82 {
83   size_t i;
84
85   for (i = 0; i < inst->files_num; i++)
86   {
87     graph_ident_t *file = inst->files + i;
88
89     printf ("    File \"%s/%s-%s/%s-%s\"\n",
90         file->host,
91         file->plugin, file->plugin_instance,
92         file->type, file->type_instance);
93   }
94
95   return (0);
96 } /* }}} int print_instances */
97
98 static int print_instances (const graph_config_t *cfg) /* {{{ */
99 {
100   graph_instance_t *inst;
101
102   for (inst = cfg->instances; inst != NULL; inst = inst->next)
103   {
104     printf ("  Instance \"%s/%s-%s/%s-%s\"\n",
105         inst->select.host,
106         inst->select.plugin, inst->select.plugin_instance,
107         inst->select.type, inst->select.type_instance);
108
109     print_files (inst);
110   }
111
112   return (0);
113 } /* }}} int print_instances */
114
115 static int print_graphs (void) /* {{{ */
116 {
117   graph_config_t *cfg;
118
119   for (cfg = graph_config_head; cfg != NULL; cfg = cfg->next)
120   {
121     printf ("Graph \"%s/%s-%s/%s-%s\"\n",
122         cfg->select.host,
123         cfg->select.plugin, cfg->select.plugin_instance,
124         cfg->select.type, cfg->select.type_instance);
125
126     print_instances (cfg);
127   }
128
129   return (0);
130 } /* }}} int print_graphs */
131
132 /* "Safe" version of strcmp(3): Either or both pointers may be NULL. */
133 static int strcmp_s (const char *s1, const char *s2) /* {{{ */
134 {
135   if ((s1 == NULL) && (s2 == NULL))
136     return (0);
137   else if (s1 == NULL)
138     return (1);
139   else if (s2 == NULL)
140     return (-1);
141   assert ((s1 != NULL) && (s2 != NULL));
142
143   return (strcmp (s1, s2));
144 } /* }}} int strcmp_s */
145
146 static _Bool part_matches (const char *sel, const char *val) /* {{{ */
147 {
148   if ((sel == NULL) && (val == NULL))
149     return (1);
150
151   if (sel == NULL) /* && (val != NULL) */
152     return (0);
153
154   if ((strcasecmp (ALL_TOKEN, sel) == 0)
155       || (strcasecmp (ANY_TOKEN, sel) == 0))
156     return (1);
157
158   if (val == NULL) /* && (sel != NULL) */
159     return (0);
160
161   if (strcmp (sel, val) == 0)
162     return (1);
163
164   return (0);
165 } /* }}} _Bool part_matches */
166
167 static _Bool file_matches (const graph_ident_t *sel, /* {{{ */
168     const graph_ident_t *val)
169 {
170   if ((sel == NULL) && (val == NULL))
171     return (0);
172   else if (sel == NULL)
173     return (-1);
174   else if (val == NULL)
175     return (1);
176
177   if (!part_matches (sel->host, val->host))
178     return (0);
179
180   if (!part_matches (sel->plugin, val->plugin))
181     return (0);
182
183   if (!part_matches (sel->plugin_instance, val->plugin_instance))
184     return (0);
185
186   if (!part_matches (sel->type, val->type))
187     return (0);
188
189   if (!part_matches (sel->type_instance, val->type_instance))
190     return (0);
191
192   return (1);
193 } /* }}} _Bool ident_compare */
194
195 /* Free all the pointers contained in the graph_ident_t but don't free the
196  * graph_ident_t* itself. */
197 static void ident_destroy (graph_ident_t *ident) /* {{{ */
198 {
199   if (ident == NULL)
200     return;
201
202   free (ident->host);
203   ident->host = NULL;
204   free (ident->plugin);
205   ident->plugin = NULL;
206   free (ident->plugin_instance);
207   ident->plugin_instance = NULL;
208   free (ident->type);
209   ident->type = NULL;
210   free (ident->type_instance);
211   ident->type_instance = NULL;
212 } /* }}} void ident_destroy */
213
214 static void instance_destroy (graph_instance_t *inst) /* {{{ */
215 {
216   graph_instance_t *next;
217   size_t i;
218
219   if (inst == NULL)
220     return;
221
222   next = inst->next;
223
224   ident_destroy (&inst->select);
225
226   for (i = 0; i < inst->files_num; i++)
227     ident_destroy (inst->files + i);
228   free (inst->files);
229
230   free (inst);
231
232   instance_destroy (next);
233 } /* }}} void instance_destroy */
234
235 static void graph_destroy (graph_config_t *cfg) /* {{{ */
236 {
237   graph_config_t *next;
238
239   if (cfg == NULL)
240     return;
241
242   next = cfg->next;
243
244   ident_destroy (&cfg->select);
245
246   free (cfg->title);
247   free (cfg->vertical_label);
248
249   instance_destroy (cfg->instances);
250
251   graph_destroy (next);
252 } /* }}} void graph_destroy */
253
254 /*
255  * Copy part of an identifier. If the "template" value is ANY_TOKEN, "value" is
256  * copied and returned. This function is used when creating graph_instance_t
257  * from graph_config_t.
258  */
259 static char *identifier_copy_part (const char *template, const char *value) /* {{{ */
260 {
261   if (template == NULL)
262   {
263     return (NULL);
264   }
265   else if (strcasecmp (ANY_TOKEN, template) == 0)
266   {
267     /* ANY in the graph selection => concrete value in the instance. */
268     if (value == NULL)
269       return (NULL);
270     else
271       return (strdup (value));
272   }
273   else if (strcasecmp (ALL_TOKEN, template) == 0)
274   {
275     /* ALL in the graph selection => ALL in the instance. */
276     return (strdup (ALL_TOKEN));
277   }
278   else
279   {
280     assert (value != NULL);
281     assert (strcmp (template, value) == 0);
282     return (strdup (template));
283   }
284 } /* }}} int identifier_copy_part */
285
286 static graph_instance_t *instance_create (graph_config_t *cfg, /* {{{ */
287     const graph_ident_t *file)
288 {
289   graph_instance_t *i;
290
291   if ((cfg == NULL) || (file == NULL))
292     return (NULL);
293
294   i = malloc (sizeof (*i));
295   if (i == NULL)
296     return (NULL);
297   memset (i, 0, sizeof (*i));
298
299   i->select.host = identifier_copy_part (cfg->select.host, file->host);
300   i->select.plugin = identifier_copy_part (cfg->select.plugin, file->plugin);
301   i->select.plugin_instance = identifier_copy_part (cfg->select.plugin_instance,
302       file->plugin_instance);
303   i->select.type = identifier_copy_part (cfg->select.type, file->type);
304   i->select.type_instance = identifier_copy_part (cfg->select.type_instance,
305       file->type_instance);
306
307   i->files = NULL;
308   i->files_num = 0;
309
310   i->next = NULL;
311
312   if (cfg->instances == NULL)
313     cfg->instances = i;
314   else
315   {
316     graph_instance_t *last;
317
318     last = cfg->instances;
319     while (last->next != NULL)
320       last = last->next;
321
322     last->next = i;
323   }
324
325   return (i);
326 } /* }}} graph_instance_t *instance_create */
327
328 static int instance_add_file (graph_instance_t *inst, /* {{{ */
329     const graph_ident_t *file)
330 {
331   graph_ident_t *tmp;
332
333   tmp = realloc (inst->files, sizeof (*inst->files) * (inst->files_num + 1));
334   if (tmp == NULL)
335     return (ENOMEM);
336   inst->files = tmp;
337   tmp = inst->files + inst->files_num;
338
339   memset (tmp, 0, sizeof (*tmp));
340   tmp->host = strdup (file->host);
341   tmp->plugin = strdup (file->plugin);
342   if (file->plugin_instance != NULL)
343     tmp->plugin_instance = strdup (file->plugin_instance);
344   else
345     tmp->plugin_instance = NULL;
346   tmp->type = strdup (file->type);
347   if (file->type_instance != NULL)
348     tmp->type_instance = strdup (file->type_instance);
349   else
350     tmp->type_instance = NULL;
351
352   inst->files_num++;
353
354   return (0);
355 } /* }}} int instance_add_file */
356
357 static graph_instance_t *graph_find_instance (graph_config_t *cfg, /* {{{ */
358     const graph_ident_t *file)
359 {
360   graph_instance_t *i;
361
362   if ((cfg == NULL) || (file == NULL))
363     return (NULL);
364
365   for (i = cfg->instances; i != NULL; i = i->next)
366     if (file_matches (&i->select, file))
367       return (i);
368
369   return (NULL);
370 } /* }}} graph_instance_t *graph_find_instance */
371
372 static int graph_add_file (graph_config_t *cfg, const graph_ident_t *file) /* {{{ */
373 {
374   graph_instance_t *inst;
375
376   inst = graph_find_instance (cfg, file);
377   if (inst == NULL)
378   {
379     inst = instance_create (cfg, file);
380     if (inst == NULL)
381       return (ENOMEM);
382   }
383
384   return (instance_add_file (inst, file));
385 } /* }}} int graph_add_file */
386
387 static int graph_append (graph_config_t *cfg) /* {{{ */
388 {
389   graph_config_t *last;
390
391   if (cfg == NULL)
392     return (EINVAL);
393
394   if (graph_config_head == NULL)
395   {
396     graph_config_head = cfg;
397     return (0);
398   }
399
400   last = graph_config_head;
401   while (last->next != NULL)
402     last = last->next;
403
404   last->next = cfg;
405
406   return (0);
407 } /* }}} int graph_append */
408
409 static int graph_create_from_file (const graph_ident_t *file) /* {{{ */
410 {
411   graph_config_t *cfg;
412
413   cfg = malloc (sizeof (*cfg));
414   if (cfg == NULL)
415     return (ENOMEM);
416   memset (cfg, 0, sizeof (*cfg));
417
418   cfg->select.host = strdup (file->host);
419   cfg->select.plugin = strdup (file->plugin);
420   cfg->select.plugin_instance = strdup (file->plugin_instance);
421   cfg->select.type = strdup (file->type);
422   cfg->select.type_instance = strdup (file->type_instance);
423
424   cfg->title = NULL;
425   cfg->vertical_label = NULL;
426   cfg->instances = NULL;
427   cfg->next = NULL;
428
429   graph_append (cfg);
430
431   return (graph_add_file (cfg, file));
432 } /* }}} int graph_create_from_file */
433
434 static int register_file (const graph_ident_t *file) /* {{{ */
435 {
436   graph_config_t *cfg;
437   int num_graphs = 0;
438
439   for (cfg = graph_config_head; cfg != NULL; cfg = cfg->next)
440   {
441     int status;
442
443     if (!file_matches (&cfg->select, file))
444       continue;
445
446     status = graph_add_file (cfg, file);
447     if (status != 0)
448     {
449       /* report error */;
450     }
451     else
452     {
453       num_graphs++;
454     }
455   }
456
457   if (num_graphs == 0)
458     graph_create_from_file (file);
459
460   return (0);
461 } /* }}} int register_file */
462
463 static int FIXME_graph_create_from_file (const graph_ident_t *file, /* {{{ */
464     const char *title)
465 {
466   graph_config_t *cfg;
467
468   cfg = malloc (sizeof (*cfg));
469   if (cfg == NULL)
470     return (ENOMEM);
471   memset (cfg, 0, sizeof (*cfg));
472
473   cfg->select.host = strdup (file->host);
474   cfg->select.plugin = strdup (file->plugin);
475   cfg->select.plugin_instance = strdup (file->plugin_instance);
476   cfg->select.type = strdup (file->type);
477   cfg->select.type_instance = strdup (file->type_instance);
478
479   cfg->title = NULL;
480   if (title != NULL)
481     cfg->title = strdup (title);
482   cfg->vertical_label = NULL;
483   cfg->instances = NULL;
484   cfg->next = NULL;
485
486   graph_append (cfg);
487
488   return (0);
489 } /* }}} int FIXME_graph_create_from_file */
490
491 /* FIXME: Actually read the config file here. */
492 static int read_graph_config (void) /* {{{ */
493 {
494   if (graph_config_head != NULL)
495     return (0);
496
497   graph_ident_t ident;
498
499
500   ident.host = ANY_TOKEN;
501   ident.plugin = "cpu";
502   ident.plugin_instance = ANY_TOKEN;
503   ident.type = "cpu";
504   ident.type_instance = ALL_TOKEN;
505   FIXME_graph_create_from_file (&ident, "CPU {instance} usage");
506
507   ident.plugin = "memory";
508   ident.plugin_instance = "";
509   ident.type = "memory";
510   FIXME_graph_create_from_file (&ident, "Memory usage");
511
512   ident.plugin = "swap";
513   ident.plugin_instance = "";
514   ident.type = "swap";
515   FIXME_graph_create_from_file (&ident, "Swap");
516
517   ident.plugin = ANY_TOKEN;
518   ident.plugin_instance = ANY_TOKEN;
519   ident.type = "ps_state";
520   FIXME_graph_create_from_file (&ident, "Processes");
521
522   ident.host = ALL_TOKEN;
523   ident.plugin = "cpu";
524   ident.plugin_instance = ALL_TOKEN;
525   ident.type = "cpu";
526   ident.type_instance = "idle";
527   FIXME_graph_create_from_file (&ident, "CPU idle overview");
528
529   return (0);
530 } /* }}} int read_graph_config */
531
532 static int gl_compare (const void *p0, const void *p1) /* {{{ */
533 {
534   const graph_ident_t *gl0 = p0;
535   const graph_ident_t *gl1 = p1;
536   int status;
537
538   status = strcmp (gl0->host, gl1->host);
539   if (status != 0)
540     return (status);
541
542   status = strcmp (gl0->plugin, gl1->plugin);
543   if (status != 0)
544     return (status);
545
546   status = strcmp_s (gl0->plugin_instance, gl1->plugin_instance);
547   if (status != 0)
548     return (status);
549
550   status = strcmp (gl0->type, gl1->type);
551   if (status != 0)
552     return (status);
553
554   return (strcmp_s (gl0->type_instance, gl1->type_instance));
555 } /* }}} int gl_compare */
556
557 static void gl_clear_entry (graph_ident_t *gl) /* {{{ */
558 {
559   if (gl == NULL)
560     return;
561
562   free (gl->host);
563   free (gl->plugin);
564   free (gl->plugin_instance);
565   free (gl->type);
566   free (gl->type_instance);
567
568   gl->host = NULL;
569   gl->plugin = NULL;
570   gl->plugin_instance = NULL;
571   gl->type = NULL;
572   gl->type_instance = NULL;
573 } /* }}} void gl_clear_entry */
574
575 static void gl_clear (void) /* {{{ */
576 {
577   size_t i;
578   graph_config_t *cfg;
579
580   cfg = graph_config_head;
581   graph_config_head = NULL;
582   graph_destroy (cfg);
583
584   for (i = 0; i < graph_list_length; i++)
585     gl_clear_entry (graph_list + i);
586
587   free (graph_list);
588   graph_list = NULL;
589   graph_list_length = 0;
590   gl_last_update = 0;
591 } /* }}} void gl_clear */
592
593 static int gl_add_copy (graph_ident_t *gl) /* {{{ */
594 {
595   graph_ident_t *ptr;
596   int status;
597
598   if (gl == NULL)
599     return (EINVAL);
600
601   ptr = realloc (graph_list, sizeof (*graph_list) * (graph_list_length + 1));
602   if (ptr == NULL)
603     return (ENOMEM);
604   graph_list = ptr;
605
606   ptr = graph_list + graph_list_length;
607   memset (ptr, 0, sizeof (*ptr));
608   ptr->host = NULL;
609   ptr->plugin = NULL;
610   ptr->plugin_instance = NULL;
611   ptr->type = NULL;
612   ptr->type_instance = NULL;
613
614 #define DUP_OR_BREAK(member) do {                    \
615   ptr->member = NULL;                                \
616   if (gl->member != NULL)                            \
617   {                                                  \
618     ptr->member = strdup (gl->member);               \
619     if (ptr->member == NULL)                         \
620       break;                                         \
621   }                                                  \
622 } while (0)
623
624   status = ENOMEM;
625   do
626   {
627     DUP_OR_BREAK(host);
628     DUP_OR_BREAK(plugin);
629     DUP_OR_BREAK(plugin_instance);
630     DUP_OR_BREAK(type);
631     DUP_OR_BREAK(type_instance);
632
633     status = 0;
634   } while (0);
635
636 #undef DUP_OR_BREAK
637
638   if (status != 0)
639   {
640     free (ptr->host);
641     free (ptr->plugin);
642     free (ptr->plugin_instance);
643     free (ptr->type);
644     free (ptr->type_instance);
645     return (status);
646   }
647
648   graph_list_length++;
649   return (0);
650 } /* }}} int gl_add_copy */
651
652 static int callback_type (const char *type, void *user_data) /* {{{ */
653 {
654   graph_ident_t *gl;
655   int status;
656
657   if ((type == NULL) || (user_data == NULL))
658     return (EINVAL);
659
660   gl = user_data;
661   if ((gl->type != NULL) || (gl->type_instance != NULL))
662     return (EINVAL);
663
664   gl->type = strdup (type);
665   if (gl->type == NULL)
666     return (ENOMEM);
667
668   gl->type_instance = strchr (gl->type, '-');
669   if (gl->type_instance != NULL)
670   {
671     *gl->type_instance = 0;
672     gl->type_instance++;
673   }
674   else
675   {
676     gl->type_instance = gl->type + strlen (gl->type);
677   }
678
679   register_file (gl);
680
681   status = gl_add_copy (gl);
682
683   free (gl->type);
684   gl->type = NULL;
685   gl->type_instance = NULL;
686
687   return (status);
688 } /* }}} int callback_type */
689
690 static int callback_plugin (const char *plugin, void *user_data) /* {{{ */
691 {
692   graph_ident_t *gl;
693   int status;
694
695   if ((plugin == NULL) || (user_data == NULL))
696     return (EINVAL);
697
698   gl = user_data;
699   if ((gl->plugin != NULL) || (gl->plugin_instance != NULL))
700     return (EINVAL);
701
702   gl->plugin = strdup (plugin);
703   if (gl->plugin == NULL)
704     return (ENOMEM);
705
706   gl->plugin_instance = strchr (gl->plugin, '-');
707   if (gl->plugin_instance != NULL)
708   {
709     *gl->plugin_instance = 0;
710     gl->plugin_instance++;
711   }
712   else
713   {
714     gl->plugin_instance = gl->plugin + strlen (gl->plugin);
715   }
716
717   status = foreach_type (gl->host, plugin, callback_type, gl);
718
719   free (gl->plugin);
720   gl->plugin = NULL;
721   gl->plugin_instance = NULL;
722
723   return (status);
724 } /* }}} int callback_plugin */
725
726 static int callback_host (const char *host, void *user_data) /* {{{ */
727 {
728   graph_ident_t *gl;
729   int status;
730
731   if ((host == NULL) || (user_data == NULL))
732     return (EINVAL);
733
734   gl = user_data;
735   if (gl->host != NULL)
736     return (EINVAL);
737
738   gl->host = strdup (host);
739   if (gl->host == NULL)
740     return (ENOMEM);
741
742   status =  foreach_plugin (host, callback_plugin, gl);
743
744   free (gl->host);
745   gl->host = NULL;
746
747   return (status);
748 } /* }}} int callback_host */
749
750 static const char *get_part_from_param (const char *prim_key, /* {{{ */
751     const char *sec_key)
752 {
753   const char *val;
754
755   val = param (prim_key);
756   if (val != NULL)
757     return (val);
758   
759   return (param (sec_key));
760 } /* }}} const char *get_part_from_param */
761
762 int gl_ident_get_rrdargs (graph_config_t *cfg, /* {{{ */
763     graph_instance_t *inst,
764     graph_ident_t *ident,
765     str_array_t *args)
766 {
767   char *file;
768   char **dses = NULL;
769   size_t dses_num = 0;
770   int status;
771   size_t i;
772
773   if ((cfg == NULL) || (inst == NULL) || (ident == NULL) || (args == NULL))
774     return (EINVAL);
775
776   file = ident_to_file (ident);
777   if (file == NULL)
778   {
779     GL_DEBUG ("gl_ident_get_rrdargs: ident_to_file returned NULL.\n");
780     return (-1);
781   }
782
783   GL_DEBUG ("gl_ident_get_rrdargs: file = %s;\n", file);
784
785   status = ds_list_from_rrd_file (file, &dses_num, &dses);
786   if (status != 0)
787   {
788     free (file);
789     return (status);
790   }
791
792   for (i = 0; i < dses_num; i++)
793   {
794     int id;
795
796     GL_DEBUG ("gl_ident_get_rrdargs: ds[%lu] = %s;\n", (unsigned long) i, dses[i]);
797
798     id = array_argc (args);
799     array_append_format (args, "DEF:avg%i=%s:%s:AVERAGE", id, file, dses[i]);
800     array_append_format (args, "LINE1:avg%i#ff0000:%s\\l", id, file);
801
802     free (dses[i]);
803   }
804   free (dses);
805
806   free (file);
807
808   return (0);
809 } /* }}} int gl_ident_get_rrdargs */
810
811 /*
812  * Global functions
813  */
814 char *ident_to_file (const graph_ident_t *ident) /* {{{ */
815 {
816   char buffer[PATH_MAX];
817
818   buffer[0] = 0;
819
820   strlcat (buffer, DATA_DIR, sizeof (buffer));
821   strlcat (buffer, "/", sizeof (buffer));
822
823   strlcat (buffer, ident->host, sizeof (buffer));
824   strlcat (buffer, "/", sizeof (buffer));
825   strlcat (buffer, ident->plugin, sizeof (buffer));
826   if (ident->plugin_instance[0] != 0)
827   {
828     strlcat (buffer, "-", sizeof (buffer));
829     strlcat (buffer, ident->plugin_instance, sizeof (buffer));
830   }
831   strlcat (buffer, "/", sizeof (buffer));
832   strlcat (buffer, ident->type, sizeof (buffer));
833   if (ident->type_instance[0] != 0)
834   {
835     strlcat (buffer, "-", sizeof (buffer));
836     strlcat (buffer, ident->type_instance, sizeof (buffer));
837   }
838
839   strlcat (buffer, ".rrd", sizeof (buffer));
840
841   return (strdup (buffer));
842 } /* }}} char *ident_to_file */
843
844 int gl_instance_get_params (graph_config_t *cfg, graph_instance_t *inst, /* {{{ */
845     char *buffer, size_t buffer_size)
846 {
847   if ((inst == NULL) || (buffer == NULL) || (buffer_size < 1))
848     return (EINVAL);
849
850   buffer[0] = 0;
851
852 #define COPY_FIELD(field) do {                                  \
853   if (strcmp (cfg->select.field, inst->select.field) == 0)      \
854   {                                                             \
855     strlcat (buffer, #field, buffer_size);                      \
856     strlcat (buffer, "=", buffer_size);                         \
857     strlcat (buffer, cfg->select.field, buffer_size);           \
858   }                                                             \
859   else                                                          \
860   {                                                             \
861     strlcat (buffer, "graph_", buffer_size);                    \
862     strlcat (buffer, #field, buffer_size);                      \
863     strlcat (buffer, "=", buffer_size);                         \
864     strlcat (buffer, cfg->select.field, buffer_size);           \
865     strlcat (buffer, ";", buffer_size);                         \
866     strlcat (buffer, "inst_", buffer_size);                     \
867     strlcat (buffer, #field, buffer_size);                      \
868     strlcat (buffer, "=", buffer_size);                         \
869     strlcat (buffer, inst->select.field, buffer_size);          \
870   }                                                             \
871 } while (0)
872
873   COPY_FIELD(host);
874   strlcat (buffer, ";", buffer_size);
875   COPY_FIELD(plugin);
876   strlcat (buffer, ";", buffer_size);
877   COPY_FIELD(plugin_instance);
878   strlcat (buffer, ";", buffer_size);
879   COPY_FIELD(type);
880   strlcat (buffer, ";", buffer_size);
881   COPY_FIELD(type_instance);
882
883 #undef COPY_FIELD
884
885   return (0);
886 } /* }}} int gl_instance_get_params */
887
888 graph_instance_t *inst_get_selected (graph_config_t *cfg) /* {{{ */
889 {
890   const char *host = get_part_from_param ("inst_host", "host");
891   const char *plugin = get_part_from_param ("inst_plugin", "plugin");
892   const char *plugin_instance = get_part_from_param ("inst_plugin_instance", "plugin_instance");
893   const char *type = get_part_from_param ("inst_type", "type");
894   const char *type_instance = get_part_from_param ("inst_type_instance", "type_instance");
895   graph_instance_t *inst;
896
897   if (cfg == NULL)
898     cfg = graph_get_selected ();
899
900   if (cfg == NULL)
901   {
902     GL_DEBUG ("inst_get_selected: cfg == NULL;\n");
903     return (NULL);
904   }
905
906   if ((host == NULL)
907       || (plugin == NULL) || (plugin_instance == NULL)
908       || (type == NULL) || (type_instance == NULL))
909   {
910     GL_DEBUG ("inst_get_selected: A parameter is NULL.\n");
911     return (NULL);
912   }
913
914   for (inst = cfg->instances; inst != NULL; inst = inst->next)
915   {
916     if ((strcmp (inst->select.host, host) != 0)
917         || (strcmp (inst->select.plugin, plugin) != 0)
918         || (strcmp (inst->select.plugin_instance, plugin_instance) != 0)
919         || (strcmp (inst->select.type, type) != 0)
920         || (strcmp (inst->select.type_instance, type_instance) != 0))
921       continue;
922
923     return (inst);
924   }
925
926   GL_DEBUG ("inst_get_selected: No match found.\n");
927   return (NULL);
928 } /* }}} graph_instance_t *inst_get_selected */
929
930 int gl_graph_get_all (gl_cfg_callback callback, /* {{{ */
931     void *user_data)
932 {
933   graph_config_t *cfg;
934
935   if (callback == NULL)
936     return (EINVAL);
937
938   gl_update ();
939
940   for (cfg = graph_config_head; cfg != NULL; cfg = cfg->next)
941   {
942     int status;
943
944     status = (*callback) (cfg, user_data);
945     if (status != 0)
946       return (status);
947   }
948
949   return (0);
950 } /* }}} int gl_graph_get_all */
951
952 graph_config_t *graph_get_selected (void) /* {{{ */
953 {
954   const char *host = get_part_from_param ("graph_host", "host");
955   const char *plugin = get_part_from_param ("graph_plugin", "plugin");
956   const char *plugin_instance = get_part_from_param ("graph_plugin_instance", "plugin_instance");
957   const char *type = get_part_from_param ("graph_type", "type");
958   const char *type_instance = get_part_from_param ("graph_type_instance", "type_instance");
959   graph_config_t *cfg;
960
961   if ((host == NULL)
962       || (plugin == NULL) || (plugin_instance == NULL)
963       || (type == NULL) || (type_instance == NULL))
964   {
965     printf ("Content-Type: text/plain\n\n");
966     printf ("graph_get_selected(): A param is NULL.\n");
967     return (NULL);
968   }
969
970   gl_update ();
971
972   for (cfg = graph_config_head; cfg != NULL; cfg = cfg->next)
973   {
974     if ((strcmp (cfg->select.host, host) != 0)
975         || (strcmp (cfg->select.plugin, plugin) != 0)
976         || (strcmp (cfg->select.plugin_instance, plugin_instance) != 0)
977         || (strcmp (cfg->select.type, type) != 0)
978         || (strcmp (cfg->select.type_instance, type_instance) != 0))
979       continue;
980
981     return (cfg);
982   }
983
984   printf ("Content-Type: text/plain\n\n");
985   printf ("graph_get_selected(): No matching graph found.\n");
986
987   return (NULL);
988 } /* }}} graph_config_t *graph_get_selected */
989
990 int gl_graph_instance_get_all (graph_config_t *cfg, /* {{{ */
991     gl_inst_callback callback, void *user_data)
992 {
993   graph_instance_t *inst;
994
995   if ((cfg == NULL) || (callback == NULL))
996     return (EINVAL);
997
998   for (inst = cfg->instances; inst != NULL; inst = inst->next)
999   {
1000     int status;
1001
1002     status = (*callback) (cfg, inst, user_data);
1003     if (status != 0)
1004       return (status);
1005   }
1006
1007   return (0);
1008 } /* }}} int gl_graph_instance_get_all */
1009
1010 int gl_graph_get_title (graph_config_t *cfg, /* {{{ */
1011     char *buffer, size_t buffer_size)
1012 {
1013   if ((cfg == NULL) || (buffer == NULL) || (buffer_size < 1))
1014     return (EINVAL);
1015
1016   buffer[0] = 0;
1017   strlcat (buffer, cfg->select.host, buffer_size);
1018   strlcat (buffer, "/", buffer_size);
1019   strlcat (buffer, cfg->select.plugin, buffer_size);
1020   if (cfg->select.plugin_instance[0] != 0)
1021   {
1022     strlcat (buffer, "-", buffer_size);
1023     strlcat (buffer, cfg->select.plugin_instance, buffer_size);
1024   }
1025   strlcat (buffer, "/", buffer_size);
1026   strlcat (buffer, cfg->select.type, buffer_size);
1027   if (cfg->select.type_instance[0] != 0)
1028   {
1029     strlcat (buffer, "-", buffer_size);
1030     strlcat (buffer, cfg->select.type_instance, buffer_size);
1031   }
1032
1033   return (0);
1034 } /* }}} int gl_graph_get_title */
1035
1036 int gl_instance_get_all (gl_inst_callback callback, /* {{{ */
1037     void *user_data)
1038 {
1039   graph_config_t *cfg;
1040
1041   gl_update ();
1042
1043   for (cfg = graph_config_head; cfg != NULL; cfg = cfg->next)
1044   {
1045     graph_instance_t *inst;
1046
1047     for (inst = cfg->instances; inst != NULL; inst = inst->next)
1048     {
1049       int status;
1050
1051       status = (*callback) (cfg, inst, user_data);
1052       if (status != 0)
1053         return (status);
1054     }
1055   }
1056
1057   return (0);
1058 } /* }}} int gl_instance_get_all */
1059
1060 int gl_instance_get_rrdargs (graph_config_t *cfg, /* {{{ */
1061     graph_instance_t *inst,
1062     str_array_t *args)
1063 {
1064   size_t i;
1065
1066   if ((cfg == NULL) || (inst == NULL) || (args == NULL))
1067     return (EINVAL);
1068
1069   if (cfg->title != NULL)
1070   {
1071     array_append (args, "-t");
1072     array_append (args, cfg->title);
1073   }
1074
1075   for (i = 0; i < inst->files_num; i++)
1076   {
1077     int status;
1078
1079     status = gl_ident_get_rrdargs (cfg, inst, inst->files + i, args);
1080     if (status != 0)
1081       return (status);
1082   }
1083
1084   return (0);
1085 } /* }}} int gl_instance_get_rrdargs */
1086
1087 /* DELETEME */
1088
1089 int gl_update (void) /* {{{ */
1090 {
1091   time_t now;
1092   graph_ident_t gl;
1093   int status;
1094
1095   /*
1096   printf ("Content-Type: text/plain\n\n");
1097   */
1098
1099   now = time (NULL);
1100
1101   if ((gl_last_update + UPDATE_INTERVAL) >= now)
1102     return (0);
1103
1104   gl_clear ();
1105
1106   read_graph_config ();
1107
1108   memset (&gl, 0, sizeof (gl));
1109   gl.host = NULL;
1110   gl.plugin = NULL;
1111   gl.plugin_instance = NULL;
1112   gl.type = NULL;
1113   gl.type_instance = NULL;
1114
1115   status = foreach_host (callback_host, &gl);
1116
1117   /* print_graphs (); */
1118
1119   if (graph_list_length > 1)
1120     qsort (graph_list, graph_list_length, sizeof (*graph_list), gl_compare);
1121
1122   return (status);
1123 } /* }}} int gl_update */
1124
1125 int gl_foreach (gl_callback callback, void *user_data) /* {{{ */
1126 {
1127   size_t i;
1128
1129   for (i = 0; i < graph_list_length; i++)
1130   {
1131     int status;
1132
1133     status = (*callback) ((void *) (graph_list + i), user_data);
1134     if (status != 0)
1135       return (status);
1136   }
1137
1138   return (0);
1139 } /* }}} int gl_foreach */
1140
1141 /* vim: set sw=2 sts=2 et fdm=marker : */