Merge branch 'collectd-5.6'
[collectd.git] / src / daemon / meta_data.c
1 /**
2  * collectd - src/meta_data.c
3  * Copyright (C) 2008-2011  Florian octo Forster
4  *
5  * Permission is hereby granted, free of charge, to any person obtaining a
6  * copy of this software and associated documentation files (the "Software"),
7  * to deal in the Software without restriction, including without limitation
8  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
9  * and/or sell copies of the Software, and to permit persons to whom the
10  * Software is furnished to do so, subject to the following conditions:
11  *
12  * The above copyright notice and this permission notice shall be included in
13  * all copies or substantial portions of the Software.
14  *
15  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
20  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
21  * DEALINGS IN THE SOFTWARE.
22  *
23  * Authors:
24  *   Florian octo Forster <octo at collectd.org>
25  **/
26
27 #include "collectd.h"
28
29 #include "common.h"
30 #include "plugin.h"
31 #include "meta_data.h"
32
33 #define MD_MAX_NONSTRING_CHARS 128
34
35 /*
36  * Data types
37  */
38 union meta_value_u
39 {
40   char    *mv_string;
41   int64_t  mv_signed_int;
42   uint64_t mv_unsigned_int;
43   double   mv_double;
44   _Bool    mv_boolean;
45 };
46 typedef union meta_value_u meta_value_t;
47
48 struct meta_entry_s;
49 typedef struct meta_entry_s meta_entry_t;
50 struct meta_entry_s
51 {
52   char         *key;
53   meta_value_t  value;
54   int           type;
55   meta_entry_t *next;
56 };
57
58 struct meta_data_s
59 {
60   meta_entry_t   *head;
61   pthread_mutex_t lock;
62 };
63
64 /*
65  * Private functions
66  */
67 static char *md_strdup (const char *orig) /* {{{ */
68 {
69   size_t sz;
70   char *dest;
71
72   if (orig == NULL)
73     return (NULL);
74
75   sz = strlen (orig) + 1;
76   dest = malloc (sz);
77   if (dest == NULL)
78     return (NULL);
79
80   memcpy (dest, orig, sz);
81
82   return (dest);
83 } /* }}} char *md_strdup */
84
85 static meta_entry_t *md_entry_alloc (const char *key) /* {{{ */
86 {
87   meta_entry_t *e;
88
89   e = calloc (1, sizeof (*e));
90   if (e == NULL)
91   {
92     ERROR ("md_entry_alloc: calloc failed.");
93     return (NULL);
94   }
95
96   e->key = md_strdup (key);
97   if (e->key == NULL)
98   {
99     free (e);
100     ERROR ("md_entry_alloc: md_strdup failed.");
101     return (NULL);
102   }
103
104   e->type = 0;
105   e->next = NULL;
106
107   return (e);
108 } /* }}} meta_entry_t *md_entry_alloc */
109
110 /* XXX: The lock on md must be held while calling this function! */
111 static meta_entry_t *md_entry_clone_contents (const meta_entry_t *orig) /* {{{ */
112 {
113   meta_entry_t *copy;
114
115   /* WARNINGS :
116    *  - we do not check that orig != NULL here. You should have done it before.
117    *  - we do not set copy->next. DO NOT FORGET TO SET copy->next IN YOUR FUNCTION
118    */
119
120   copy = md_entry_alloc (orig->key);
121   if (copy == NULL)
122     return (NULL);
123   copy->type = orig->type;
124   if (copy->type == MD_TYPE_STRING)
125     copy->value.mv_string = strdup (orig->value.mv_string);
126   else
127     copy->value = orig->value;
128
129   return (copy);
130 } /* }}} meta_entry_t *md_entry_clone_contents */
131
132 static meta_entry_t *md_entry_clone (const meta_entry_t *orig) /* {{{ */
133 {
134   meta_entry_t *copy;
135
136   if (orig == NULL)
137     return (NULL);
138
139   copy = md_entry_clone_contents(orig);
140
141   copy->next = md_entry_clone (orig->next);
142   return (copy);
143 } /* }}} meta_entry_t *md_entry_clone */
144
145 static void md_entry_free (meta_entry_t *e) /* {{{ */
146 {
147   if (e == NULL)
148     return;
149
150   free (e->key);
151
152   if (e->type == MD_TYPE_STRING)
153     free (e->value.mv_string);
154
155   if (e->next != NULL)
156     md_entry_free (e->next);
157
158   free (e);
159 } /* }}} void md_entry_free */
160
161 static int md_entry_insert (meta_data_t *md, meta_entry_t *e) /* {{{ */
162 {
163   meta_entry_t *this;
164   meta_entry_t *prev;
165
166   if ((md == NULL) || (e == NULL))
167     return (-EINVAL);
168
169   pthread_mutex_lock (&md->lock);
170
171   prev = NULL;
172   this = md->head;
173   while (this != NULL)
174   {
175     if (strcasecmp (e->key, this->key) == 0)
176       break;
177
178     prev = this;
179     this = this->next;
180   }
181
182   if (this == NULL)
183   {
184     /* This key does not exist yet. */
185     if (md->head == NULL)
186       md->head = e;
187     else
188     {
189       assert (prev != NULL);
190       prev->next = e;
191     }
192
193     e->next = NULL;
194   }
195   else /* (this != NULL) */
196   {
197     if (prev == NULL)
198       md->head = e;
199     else
200       prev->next = e;
201
202     e->next = this->next;
203   }
204
205   pthread_mutex_unlock (&md->lock);
206
207   if (this != NULL)
208   {
209     this->next = NULL;
210     md_entry_free (this);
211   }
212
213   return (0);
214 } /* }}} int md_entry_insert */
215
216 /* XXX: The lock on md must be held while calling this function! */
217 static int md_entry_insert_clone (meta_data_t *md, meta_entry_t *orig) /* {{{ */
218 {
219   meta_entry_t *e;
220   meta_entry_t *this;
221   meta_entry_t *prev;
222
223   /* WARNINGS :
224    *  - we do not check that md and e != NULL here. You should have done it before.
225    *  - we do not use the lock. You should have set it before.
226    */
227
228   e = md_entry_clone_contents(orig);
229
230   prev = NULL;
231   this = md->head;
232   while (this != NULL)
233   {
234     if (strcasecmp (e->key, this->key) == 0)
235       break;
236
237     prev = this;
238     this = this->next;
239   }
240
241   if (this == NULL)
242   {
243     /* This key does not exist yet. */
244     if (md->head == NULL)
245       md->head = e;
246     else
247     {
248       assert (prev != NULL);
249       prev->next = e;
250     }
251
252     e->next = NULL;
253   }
254   else /* (this != NULL) */
255   {
256     if (prev == NULL)
257       md->head = e;
258     else
259       prev->next = e;
260
261     e->next = this->next;
262   }
263
264   if (this != NULL)
265   {
266     this->next = NULL;
267     md_entry_free (this);
268   }
269
270   return (0);
271 } /* }}} int md_entry_insert_clone */
272
273 /* XXX: The lock on md must be held while calling this function! */
274 static meta_entry_t *md_entry_lookup (meta_data_t *md, /* {{{ */
275     const char *key)
276 {
277   meta_entry_t *e;
278
279   if ((md == NULL) || (key == NULL))
280     return (NULL);
281
282   for (e = md->head; e != NULL; e = e->next)
283     if (strcasecmp (key, e->key) == 0)
284       break;
285
286   return (e);
287 } /* }}} meta_entry_t *md_entry_lookup */
288
289 /*
290  * Each value_list_t*, as it is going through the system, is handled by exactly
291  * one thread. Plugins which pass a value_list_t* to another thread, e.g. the
292  * rrdtool plugin, must create a copy first. The meta data within a
293  * value_list_t* is not thread safe and doesn't need to be.
294  *
295  * The meta data associated with cache entries are a different story. There, we
296  * need to ensure exclusive locking to prevent leaks and other funky business.
297  * This is ensured by the uc_meta_data_get_*() functions.
298  */
299
300 /*
301  * Public functions
302  */
303 meta_data_t *meta_data_create (void) /* {{{ */
304 {
305   meta_data_t *md;
306
307   md = calloc (1, sizeof (*md));
308   if (md == NULL)
309   {
310     ERROR ("meta_data_create: calloc failed.");
311     return (NULL);
312   }
313
314   pthread_mutex_init (&md->lock, /* attr = */ NULL);
315
316   return (md);
317 } /* }}} meta_data_t *meta_data_create */
318
319 meta_data_t *meta_data_clone (meta_data_t *orig) /* {{{ */
320 {
321   meta_data_t *copy;
322
323   if (orig == NULL)
324     return (NULL);
325
326   copy = meta_data_create ();
327   if (copy == NULL)
328     return (NULL);
329
330   pthread_mutex_lock (&orig->lock);
331   copy->head = md_entry_clone (orig->head);
332   pthread_mutex_unlock (&orig->lock);
333
334   return (copy);
335 } /* }}} meta_data_t *meta_data_clone */
336
337 int meta_data_clone_merge (meta_data_t **dest, meta_data_t *orig) /* {{{ */
338 {
339   if (orig == NULL)
340     return (0);
341
342   if (*dest == NULL) {
343     *dest = meta_data_clone(orig);
344     return(0);
345   }
346
347   pthread_mutex_lock (&orig->lock);
348   for (meta_entry_t *e=orig->head; e != NULL; e = e->next)
349   {
350     md_entry_insert_clone((*dest), e);
351   }
352   pthread_mutex_unlock (&orig->lock);
353
354   return (0);
355 } /* }}} int meta_data_clone_merge */
356
357 void meta_data_destroy (meta_data_t *md) /* {{{ */
358 {
359   if (md == NULL)
360     return;
361
362   md_entry_free (md->head);
363   pthread_mutex_destroy (&md->lock);
364   free (md);
365 } /* }}} void meta_data_destroy */
366
367 int meta_data_exists (meta_data_t *md, const char *key) /* {{{ */
368 {
369   if ((md == NULL) || (key == NULL))
370     return (-EINVAL);
371
372   pthread_mutex_lock (&md->lock);
373
374   for (meta_entry_t *e = md->head; e != NULL; e = e->next)
375   {
376     if (strcasecmp (key, e->key) == 0)
377     {
378       pthread_mutex_unlock (&md->lock);
379       return (1);
380     }
381   }
382
383   pthread_mutex_unlock (&md->lock);
384   return (0);
385 } /* }}} int meta_data_exists */
386
387 int meta_data_type (meta_data_t *md, const char *key) /* {{{ */
388 {
389   if ((md == NULL) || (key == NULL))
390     return -EINVAL;
391
392   pthread_mutex_lock (&md->lock);
393
394   for (meta_entry_t *e = md->head; e != NULL; e = e->next)
395   {
396     if (strcasecmp (key, e->key) == 0)
397     {
398       pthread_mutex_unlock (&md->lock);
399       return e->type;
400     }
401   }
402
403   pthread_mutex_unlock (&md->lock);
404   return 0;
405 } /* }}} int meta_data_type */
406
407 int meta_data_toc (meta_data_t *md, char ***toc) /* {{{ */
408 {
409   int i = 0, count = 0;
410
411   if ((md == NULL) || (toc == NULL))
412     return -EINVAL;
413
414   pthread_mutex_lock (&md->lock);
415
416   for (meta_entry_t *e = md->head; e != NULL; e = e->next)
417     ++count;
418
419   if (count == 0)
420   {
421     pthread_mutex_unlock (&md->lock);
422     return (count);
423   }
424
425   *toc = calloc(count, sizeof(**toc));
426   for (meta_entry_t *e = md->head; e != NULL; e = e->next)
427     (*toc)[i++] = strdup(e->key);
428
429   pthread_mutex_unlock (&md->lock);
430   return count;
431 } /* }}} int meta_data_toc */
432
433 int meta_data_delete (meta_data_t *md, const char *key) /* {{{ */
434 {
435   meta_entry_t *this;
436   meta_entry_t *prev;
437
438   if ((md == NULL) || (key == NULL))
439     return (-EINVAL);
440
441   pthread_mutex_lock (&md->lock);
442
443   prev = NULL;
444   this = md->head;
445   while (this != NULL)
446   {
447     if (strcasecmp (key, this->key) == 0)
448       break;
449
450     prev = this;
451     this = this->next;
452   }
453
454   if (this == NULL)
455   {
456     pthread_mutex_unlock (&md->lock);
457     return (-ENOENT);
458   }
459
460   if (prev == NULL)
461     md->head = this->next;
462   else
463     prev->next = this->next;
464
465   pthread_mutex_unlock (&md->lock);
466
467   this->next = NULL;
468   md_entry_free (this);
469
470   return (0);
471 } /* }}} int meta_data_delete */
472
473 /*
474  * Add functions
475  */
476 int meta_data_add_string (meta_data_t *md, /* {{{ */
477     const char *key, const char *value)
478 {
479   meta_entry_t *e;
480
481   if ((md == NULL) || (key == NULL) || (value == NULL))
482     return (-EINVAL);
483
484   e = md_entry_alloc (key);
485   if (e == NULL)
486     return (-ENOMEM);
487
488   e->value.mv_string = md_strdup (value);
489   if (e->value.mv_string == NULL)
490   {
491     ERROR ("meta_data_add_string: md_strdup failed.");
492     md_entry_free (e);
493     return (-ENOMEM);
494   }
495   e->type = MD_TYPE_STRING;
496
497   return (md_entry_insert (md, e));
498 } /* }}} int meta_data_add_string */
499
500 int meta_data_add_signed_int (meta_data_t *md, /* {{{ */
501     const char *key, int64_t value)
502 {
503   meta_entry_t *e;
504
505   if ((md == NULL) || (key == NULL))
506     return (-EINVAL);
507
508   e = md_entry_alloc (key);
509   if (e == NULL)
510     return (-ENOMEM);
511
512   e->value.mv_signed_int = value;
513   e->type = MD_TYPE_SIGNED_INT;
514
515   return (md_entry_insert (md, e));
516 } /* }}} int meta_data_add_signed_int */
517
518 int meta_data_add_unsigned_int (meta_data_t *md, /* {{{ */
519     const char *key, uint64_t value)
520 {
521   meta_entry_t *e;
522
523   if ((md == NULL) || (key == NULL))
524     return (-EINVAL);
525
526   e = md_entry_alloc (key);
527   if (e == NULL)
528     return (-ENOMEM);
529
530   e->value.mv_unsigned_int = value;
531   e->type = MD_TYPE_UNSIGNED_INT;
532
533   return (md_entry_insert (md, e));
534 } /* }}} int meta_data_add_unsigned_int */
535
536 int meta_data_add_double (meta_data_t *md, /* {{{ */
537     const char *key, double value)
538 {
539   meta_entry_t *e;
540
541   if ((md == NULL) || (key == NULL))
542     return (-EINVAL);
543
544   e = md_entry_alloc (key);
545   if (e == NULL)
546     return (-ENOMEM);
547
548   e->value.mv_double = value;
549   e->type = MD_TYPE_DOUBLE;
550
551   return (md_entry_insert (md, e));
552 } /* }}} int meta_data_add_double */
553
554 int meta_data_add_boolean (meta_data_t *md, /* {{{ */
555     const char *key, _Bool value)
556 {
557   meta_entry_t *e;
558
559   if ((md == NULL) || (key == NULL))
560     return (-EINVAL);
561
562   e = md_entry_alloc (key);
563   if (e == NULL)
564     return (-ENOMEM);
565
566   e->value.mv_boolean = value;
567   e->type = MD_TYPE_BOOLEAN;
568
569   return (md_entry_insert (md, e));
570 } /* }}} int meta_data_add_boolean */
571
572 /*
573  * Get functions
574  */
575 int meta_data_get_string (meta_data_t *md, /* {{{ */
576     const char *key, char **value)
577 {
578   meta_entry_t *e;
579   char *temp;
580
581   if ((md == NULL) || (key == NULL) || (value == NULL))
582     return (-EINVAL);
583
584   pthread_mutex_lock (&md->lock);
585
586   e = md_entry_lookup (md, key);
587   if (e == NULL)
588   {
589     pthread_mutex_unlock (&md->lock);
590     return (-ENOENT);
591   }
592
593   if (e->type != MD_TYPE_STRING)
594   {
595     ERROR ("meta_data_get_string: Type mismatch for key `%s'", e->key);
596     pthread_mutex_unlock (&md->lock);
597     return (-ENOENT);
598   }
599
600   temp = md_strdup (e->value.mv_string);
601   if (temp == NULL)
602   {
603     pthread_mutex_unlock (&md->lock);
604     ERROR ("meta_data_get_string: md_strdup failed.");
605     return (-ENOMEM);
606   }
607
608   pthread_mutex_unlock (&md->lock);
609
610   *value = temp;
611
612   return (0);
613 } /* }}} int meta_data_get_string */
614
615 int meta_data_get_signed_int (meta_data_t *md, /* {{{ */
616     const char *key, int64_t *value)
617 {
618   meta_entry_t *e;
619
620   if ((md == NULL) || (key == NULL) || (value == NULL))
621     return (-EINVAL);
622
623   pthread_mutex_lock (&md->lock);
624
625   e = md_entry_lookup (md, key);
626   if (e == NULL)
627   {
628     pthread_mutex_unlock (&md->lock);
629     return (-ENOENT);
630   }
631
632   if (e->type != MD_TYPE_SIGNED_INT)
633   {
634     ERROR ("meta_data_get_signed_int: Type mismatch for key `%s'", e->key);
635     pthread_mutex_unlock (&md->lock);
636     return (-ENOENT);
637   }
638
639   *value = e->value.mv_signed_int;
640
641   pthread_mutex_unlock (&md->lock);
642   return (0);
643 } /* }}} int meta_data_get_signed_int */
644
645 int meta_data_get_unsigned_int (meta_data_t *md, /* {{{ */
646     const char *key, uint64_t *value)
647 {
648   meta_entry_t *e;
649
650   if ((md == NULL) || (key == NULL) || (value == NULL))
651     return (-EINVAL);
652
653   pthread_mutex_lock (&md->lock);
654
655   e = md_entry_lookup (md, key);
656   if (e == NULL)
657   {
658     pthread_mutex_unlock (&md->lock);
659     return (-ENOENT);
660   }
661
662   if (e->type != MD_TYPE_UNSIGNED_INT)
663   {
664     ERROR ("meta_data_get_unsigned_int: Type mismatch for key `%s'", e->key);
665     pthread_mutex_unlock (&md->lock);
666     return (-ENOENT);
667   }
668
669   *value = e->value.mv_unsigned_int;
670
671   pthread_mutex_unlock (&md->lock);
672   return (0);
673 } /* }}} int meta_data_get_unsigned_int */
674
675 int meta_data_get_double (meta_data_t *md, /* {{{ */
676     const char *key, double *value)
677 {
678   meta_entry_t *e;
679
680   if ((md == NULL) || (key == NULL) || (value == NULL))
681     return (-EINVAL);
682
683   pthread_mutex_lock (&md->lock);
684
685   e = md_entry_lookup (md, key);
686   if (e == NULL)
687   {
688     pthread_mutex_unlock (&md->lock);
689     return (-ENOENT);
690   }
691
692   if (e->type != MD_TYPE_DOUBLE)
693   {
694     ERROR ("meta_data_get_double: Type mismatch for key `%s'", e->key);
695     pthread_mutex_unlock (&md->lock);
696     return (-ENOENT);
697   }
698
699   *value = e->value.mv_double;
700
701   pthread_mutex_unlock (&md->lock);
702   return (0);
703 } /* }}} int meta_data_get_double */
704
705 int meta_data_get_boolean (meta_data_t *md, /* {{{ */
706     const char *key, _Bool *value)
707 {
708   meta_entry_t *e;
709
710   if ((md == NULL) || (key == NULL) || (value == NULL))
711     return (-EINVAL);
712
713   pthread_mutex_lock (&md->lock);
714
715   e = md_entry_lookup (md, key);
716   if (e == NULL)
717   {
718     pthread_mutex_unlock (&md->lock);
719     return (-ENOENT);
720   }
721
722   if (e->type != MD_TYPE_BOOLEAN)
723   {
724     ERROR ("meta_data_get_boolean: Type mismatch for key `%s'", e->key);
725     pthread_mutex_unlock (&md->lock);
726     return (-ENOENT);
727   }
728
729   *value = e->value.mv_boolean;
730
731   pthread_mutex_unlock (&md->lock);
732   return (0);
733 } /* }}} int meta_data_get_boolean */
734
735 int meta_data_as_string (meta_data_t *md, /* {{{ */
736     const char *key, char **value)
737 {
738   meta_entry_t *e;
739   char *actual;
740   char buffer[MD_MAX_NONSTRING_CHARS];  /* For non-string types. */
741   char *temp;
742   int type;
743
744   if ((md == NULL) || (key == NULL) || (value == NULL))
745     return (-EINVAL);
746
747   pthread_mutex_lock (&md->lock);
748
749   e = md_entry_lookup (md, key);
750   if (e == NULL)
751   {
752     pthread_mutex_unlock (&md->lock);
753     return (-ENOENT);
754   }
755
756   type = e->type;
757
758   switch (type)
759   {
760     case MD_TYPE_STRING:
761       actual = e->value.mv_string;
762       break;
763     case MD_TYPE_SIGNED_INT:
764       ssnprintf (buffer, sizeof (buffer), "%"PRIi64, e->value.mv_signed_int);
765       actual = buffer;
766       break;
767     case MD_TYPE_UNSIGNED_INT:
768       ssnprintf (buffer, sizeof (buffer), "%"PRIu64, e->value.mv_unsigned_int);
769       actual = buffer;
770       break;
771     case MD_TYPE_DOUBLE:
772       ssnprintf (buffer, sizeof (buffer), GAUGE_FORMAT, e->value.mv_double);
773       actual = buffer;
774       break;
775     case MD_TYPE_BOOLEAN:
776       actual = e->value.mv_boolean ? "true" : "false";
777       break;
778     default:
779       pthread_mutex_unlock (&md->lock);
780       ERROR ("meta_data_as_string: unknown type %d for key `%s'", type, key);
781       return (-ENOENT);
782   }
783
784   pthread_mutex_unlock (&md->lock);
785
786   temp = md_strdup (actual);
787   if (temp == NULL)
788   {
789     pthread_mutex_unlock (&md->lock);
790     ERROR ("meta_data_as_string: md_strdup failed for key `%s'.", key);
791     return (-ENOMEM);
792   }
793
794   *value = temp;
795
796   return (0);
797 } /* }}} int meta_data_as_string */
798
799 /* vim: set sw=2 sts=2 et fdm=marker : */