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