Code

Treewide: remove vim modelines from C code files
[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  **/
27 #include "collectd.h"
29 #include "common.h"
30 #include "meta_data.h"
31 #include "plugin.h"
33 #define MD_MAX_NONSTRING_CHARS 128
35 /*
36  * Data types
37  */
38 union meta_value_u {
39   char *mv_string;
40   int64_t mv_signed_int;
41   uint64_t mv_unsigned_int;
42   double mv_double;
43   _Bool mv_boolean;
44 };
45 typedef union meta_value_u meta_value_t;
47 struct meta_entry_s;
48 typedef struct meta_entry_s meta_entry_t;
49 struct meta_entry_s {
50   char *key;
51   meta_value_t value;
52   int type;
53   meta_entry_t *next;
54 };
56 struct meta_data_s {
57   meta_entry_t *head;
58   pthread_mutex_t lock;
59 };
61 /*
62  * Private functions
63  */
64 static char *md_strdup(const char *orig) /* {{{ */
65 {
66   size_t sz;
67   char *dest;
69   if (orig == NULL)
70     return (NULL);
72   sz = strlen(orig) + 1;
73   dest = malloc(sz);
74   if (dest == NULL)
75     return (NULL);
77   memcpy(dest, orig, sz);
79   return (dest);
80 } /* }}} char *md_strdup */
82 static meta_entry_t *md_entry_alloc(const char *key) /* {{{ */
83 {
84   meta_entry_t *e;
86   e = calloc(1, sizeof(*e));
87   if (e == NULL) {
88     ERROR("md_entry_alloc: calloc failed.");
89     return (NULL);
90   }
92   e->key = md_strdup(key);
93   if (e->key == NULL) {
94     free(e);
95     ERROR("md_entry_alloc: md_strdup failed.");
96     return (NULL);
97   }
99   e->type = 0;
100   e->next = NULL;
102   return (e);
103 } /* }}} meta_entry_t *md_entry_alloc */
105 /* XXX: The lock on md must be held while calling this function! */
106 static meta_entry_t *md_entry_clone_contents(const meta_entry_t *orig) /* {{{ */
108   meta_entry_t *copy;
110   /* WARNINGS :
111    *  - we do not check that orig != NULL here. You should have done it before.
112    *  - we do not set copy->next. DO NOT FORGET TO SET copy->next IN YOUR
113    * FUNCTION
114    */
116   copy = md_entry_alloc(orig->key);
117   if (copy == NULL)
118     return (NULL);
119   copy->type = orig->type;
120   if (copy->type == MD_TYPE_STRING)
121     copy->value.mv_string = strdup(orig->value.mv_string);
122   else
123     copy->value = orig->value;
125   return (copy);
126 } /* }}} meta_entry_t *md_entry_clone_contents */
128 static meta_entry_t *md_entry_clone(const meta_entry_t *orig) /* {{{ */
130   meta_entry_t *copy;
132   if (orig == NULL)
133     return (NULL);
135   copy = md_entry_clone_contents(orig);
137   copy->next = md_entry_clone(orig->next);
138   return (copy);
139 } /* }}} meta_entry_t *md_entry_clone */
141 static void md_entry_free(meta_entry_t *e) /* {{{ */
143   if (e == NULL)
144     return;
146   free(e->key);
148   if (e->type == MD_TYPE_STRING)
149     free(e->value.mv_string);
151   if (e->next != NULL)
152     md_entry_free(e->next);
154   free(e);
155 } /* }}} void md_entry_free */
157 static int md_entry_insert(meta_data_t *md, meta_entry_t *e) /* {{{ */
159   meta_entry_t *this;
160   meta_entry_t *prev;
162   if ((md == NULL) || (e == NULL))
163     return (-EINVAL);
165   pthread_mutex_lock(&md->lock);
167   prev = NULL;
168   this = md->head;
169   while (this != NULL) {
170     if (strcasecmp(e->key, this->key) == 0)
171       break;
173     prev = this;
174     this = this->next;
175   }
177   if (this == NULL) {
178     /* This key does not exist yet. */
179     if (md->head == NULL)
180       md->head = e;
181     else {
182       assert(prev != NULL);
183       prev->next = e;
184     }
186     e->next = NULL;
187   } else /* (this != NULL) */
188   {
189     if (prev == NULL)
190       md->head = e;
191     else
192       prev->next = e;
194     e->next = this->next;
195   }
197   pthread_mutex_unlock(&md->lock);
199   if (this != NULL) {
200     this->next = NULL;
201     md_entry_free(this);
202   }
204   return (0);
205 } /* }}} int md_entry_insert */
207 /* XXX: The lock on md must be held while calling this function! */
208 static int md_entry_insert_clone(meta_data_t *md, meta_entry_t *orig) /* {{{ */
210   meta_entry_t *e;
211   meta_entry_t *this;
212   meta_entry_t *prev;
214   /* WARNINGS :
215    *  - we do not check that md and e != NULL here. You should have done it
216    * before.
217    *  - we do not use the lock. You should have set it before.
218    */
220   e = md_entry_clone_contents(orig);
222   prev = NULL;
223   this = md->head;
224   while (this != NULL) {
225     if (strcasecmp(e->key, this->key) == 0)
226       break;
228     prev = this;
229     this = this->next;
230   }
232   if (this == NULL) {
233     /* This key does not exist yet. */
234     if (md->head == NULL)
235       md->head = e;
236     else {
237       assert(prev != NULL);
238       prev->next = e;
239     }
241     e->next = NULL;
242   } else /* (this != NULL) */
243   {
244     if (prev == NULL)
245       md->head = e;
246     else
247       prev->next = e;
249     e->next = this->next;
250   }
252   if (this != NULL) {
253     this->next = NULL;
254     md_entry_free(this);
255   }
257   return (0);
258 } /* }}} int md_entry_insert_clone */
260 /* XXX: The lock on md must be held while calling this function! */
261 static meta_entry_t *md_entry_lookup(meta_data_t *md, /* {{{ */
262                                      const char *key) {
263   meta_entry_t *e;
265   if ((md == NULL) || (key == NULL))
266     return (NULL);
268   for (e = md->head; e != NULL; e = e->next)
269     if (strcasecmp(key, e->key) == 0)
270       break;
272   return (e);
273 } /* }}} meta_entry_t *md_entry_lookup */
275 /*
276  * Each value_list_t*, as it is going through the system, is handled by exactly
277  * one thread. Plugins which pass a value_list_t* to another thread, e.g. the
278  * rrdtool plugin, must create a copy first. The meta data within a
279  * value_list_t* is not thread safe and doesn't need to be.
280  *
281  * The meta data associated with cache entries are a different story. There, we
282  * need to ensure exclusive locking to prevent leaks and other funky business.
283  * This is ensured by the uc_meta_data_get_*() functions.
284  */
286 /*
287  * Public functions
288  */
289 meta_data_t *meta_data_create(void) /* {{{ */
291   meta_data_t *md;
293   md = calloc(1, sizeof(*md));
294   if (md == NULL) {
295     ERROR("meta_data_create: calloc failed.");
296     return (NULL);
297   }
299   pthread_mutex_init(&md->lock, /* attr = */ NULL);
301   return (md);
302 } /* }}} meta_data_t *meta_data_create */
304 meta_data_t *meta_data_clone(meta_data_t *orig) /* {{{ */
306   meta_data_t *copy;
308   if (orig == NULL)
309     return (NULL);
311   copy = meta_data_create();
312   if (copy == NULL)
313     return (NULL);
315   pthread_mutex_lock(&orig->lock);
316   copy->head = md_entry_clone(orig->head);
317   pthread_mutex_unlock(&orig->lock);
319   return (copy);
320 } /* }}} meta_data_t *meta_data_clone */
322 int meta_data_clone_merge(meta_data_t **dest, meta_data_t *orig) /* {{{ */
324   if (orig == NULL)
325     return (0);
327   if (*dest == NULL) {
328     *dest = meta_data_clone(orig);
329     return (0);
330   }
332   pthread_mutex_lock(&orig->lock);
333   for (meta_entry_t *e = orig->head; e != NULL; e = e->next) {
334     md_entry_insert_clone((*dest), e);
335   }
336   pthread_mutex_unlock(&orig->lock);
338   return (0);
339 } /* }}} int meta_data_clone_merge */
341 void meta_data_destroy(meta_data_t *md) /* {{{ */
343   if (md == NULL)
344     return;
346   md_entry_free(md->head);
347   pthread_mutex_destroy(&md->lock);
348   free(md);
349 } /* }}} void meta_data_destroy */
351 int meta_data_exists(meta_data_t *md, const char *key) /* {{{ */
353   if ((md == NULL) || (key == NULL))
354     return (-EINVAL);
356   pthread_mutex_lock(&md->lock);
358   for (meta_entry_t *e = md->head; e != NULL; e = e->next) {
359     if (strcasecmp(key, e->key) == 0) {
360       pthread_mutex_unlock(&md->lock);
361       return (1);
362     }
363   }
365   pthread_mutex_unlock(&md->lock);
366   return (0);
367 } /* }}} int meta_data_exists */
369 int meta_data_type(meta_data_t *md, const char *key) /* {{{ */
371   if ((md == NULL) || (key == NULL))
372     return -EINVAL;
374   pthread_mutex_lock(&md->lock);
376   for (meta_entry_t *e = md->head; e != NULL; e = e->next) {
377     if (strcasecmp(key, e->key) == 0) {
378       pthread_mutex_unlock(&md->lock);
379       return e->type;
380     }
381   }
383   pthread_mutex_unlock(&md->lock);
384   return 0;
385 } /* }}} int meta_data_type */
387 int meta_data_toc(meta_data_t *md, char ***toc) /* {{{ */
389   int i = 0, count = 0;
391   if ((md == NULL) || (toc == NULL))
392     return -EINVAL;
394   pthread_mutex_lock(&md->lock);
396   for (meta_entry_t *e = md->head; e != NULL; e = e->next)
397     ++count;
399   if (count == 0) {
400     pthread_mutex_unlock(&md->lock);
401     return (count);
402   }
404   *toc = calloc(count, sizeof(**toc));
405   for (meta_entry_t *e = md->head; e != NULL; e = e->next)
406     (*toc)[i++] = strdup(e->key);
408   pthread_mutex_unlock(&md->lock);
409   return count;
410 } /* }}} int meta_data_toc */
412 int meta_data_delete(meta_data_t *md, const char *key) /* {{{ */
414   meta_entry_t *this;
415   meta_entry_t *prev;
417   if ((md == NULL) || (key == NULL))
418     return (-EINVAL);
420   pthread_mutex_lock(&md->lock);
422   prev = NULL;
423   this = md->head;
424   while (this != NULL) {
425     if (strcasecmp(key, this->key) == 0)
426       break;
428     prev = this;
429     this = this->next;
430   }
432   if (this == NULL) {
433     pthread_mutex_unlock(&md->lock);
434     return (-ENOENT);
435   }
437   if (prev == NULL)
438     md->head = this->next;
439   else
440     prev->next = this->next;
442   pthread_mutex_unlock(&md->lock);
444   this->next = NULL;
445   md_entry_free(this);
447   return (0);
448 } /* }}} int meta_data_delete */
450 /*
451  * Add functions
452  */
453 int meta_data_add_string(meta_data_t *md, /* {{{ */
454                          const char *key, const char *value) {
455   meta_entry_t *e;
457   if ((md == NULL) || (key == NULL) || (value == NULL))
458     return (-EINVAL);
460   e = md_entry_alloc(key);
461   if (e == NULL)
462     return (-ENOMEM);
464   e->value.mv_string = md_strdup(value);
465   if (e->value.mv_string == NULL) {
466     ERROR("meta_data_add_string: md_strdup failed.");
467     md_entry_free(e);
468     return (-ENOMEM);
469   }
470   e->type = MD_TYPE_STRING;
472   return (md_entry_insert(md, e));
473 } /* }}} int meta_data_add_string */
475 int meta_data_add_signed_int(meta_data_t *md, /* {{{ */
476                              const char *key, int64_t value) {
477   meta_entry_t *e;
479   if ((md == NULL) || (key == NULL))
480     return (-EINVAL);
482   e = md_entry_alloc(key);
483   if (e == NULL)
484     return (-ENOMEM);
486   e->value.mv_signed_int = value;
487   e->type = MD_TYPE_SIGNED_INT;
489   return (md_entry_insert(md, e));
490 } /* }}} int meta_data_add_signed_int */
492 int meta_data_add_unsigned_int(meta_data_t *md, /* {{{ */
493                                const char *key, uint64_t value) {
494   meta_entry_t *e;
496   if ((md == NULL) || (key == NULL))
497     return (-EINVAL);
499   e = md_entry_alloc(key);
500   if (e == NULL)
501     return (-ENOMEM);
503   e->value.mv_unsigned_int = value;
504   e->type = MD_TYPE_UNSIGNED_INT;
506   return (md_entry_insert(md, e));
507 } /* }}} int meta_data_add_unsigned_int */
509 int meta_data_add_double(meta_data_t *md, /* {{{ */
510                          const char *key, double value) {
511   meta_entry_t *e;
513   if ((md == NULL) || (key == NULL))
514     return (-EINVAL);
516   e = md_entry_alloc(key);
517   if (e == NULL)
518     return (-ENOMEM);
520   e->value.mv_double = value;
521   e->type = MD_TYPE_DOUBLE;
523   return (md_entry_insert(md, e));
524 } /* }}} int meta_data_add_double */
526 int meta_data_add_boolean(meta_data_t *md, /* {{{ */
527                           const char *key, _Bool value) {
528   meta_entry_t *e;
530   if ((md == NULL) || (key == NULL))
531     return (-EINVAL);
533   e = md_entry_alloc(key);
534   if (e == NULL)
535     return (-ENOMEM);
537   e->value.mv_boolean = value;
538   e->type = MD_TYPE_BOOLEAN;
540   return (md_entry_insert(md, e));
541 } /* }}} int meta_data_add_boolean */
543 /*
544  * Get functions
545  */
546 int meta_data_get_string(meta_data_t *md, /* {{{ */
547                          const char *key, char **value) {
548   meta_entry_t *e;
549   char *temp;
551   if ((md == NULL) || (key == NULL) || (value == NULL))
552     return (-EINVAL);
554   pthread_mutex_lock(&md->lock);
556   e = md_entry_lookup(md, key);
557   if (e == NULL) {
558     pthread_mutex_unlock(&md->lock);
559     return (-ENOENT);
560   }
562   if (e->type != MD_TYPE_STRING) {
563     ERROR("meta_data_get_string: Type mismatch for key `%s'", e->key);
564     pthread_mutex_unlock(&md->lock);
565     return (-ENOENT);
566   }
568   temp = md_strdup(e->value.mv_string);
569   if (temp == NULL) {
570     pthread_mutex_unlock(&md->lock);
571     ERROR("meta_data_get_string: md_strdup failed.");
572     return (-ENOMEM);
573   }
575   pthread_mutex_unlock(&md->lock);
577   *value = temp;
579   return (0);
580 } /* }}} int meta_data_get_string */
582 int meta_data_get_signed_int(meta_data_t *md, /* {{{ */
583                              const char *key, int64_t *value) {
584   meta_entry_t *e;
586   if ((md == NULL) || (key == NULL) || (value == NULL))
587     return (-EINVAL);
589   pthread_mutex_lock(&md->lock);
591   e = md_entry_lookup(md, key);
592   if (e == NULL) {
593     pthread_mutex_unlock(&md->lock);
594     return (-ENOENT);
595   }
597   if (e->type != MD_TYPE_SIGNED_INT) {
598     ERROR("meta_data_get_signed_int: Type mismatch for key `%s'", e->key);
599     pthread_mutex_unlock(&md->lock);
600     return (-ENOENT);
601   }
603   *value = e->value.mv_signed_int;
605   pthread_mutex_unlock(&md->lock);
606   return (0);
607 } /* }}} int meta_data_get_signed_int */
609 int meta_data_get_unsigned_int(meta_data_t *md, /* {{{ */
610                                const char *key, uint64_t *value) {
611   meta_entry_t *e;
613   if ((md == NULL) || (key == NULL) || (value == NULL))
614     return (-EINVAL);
616   pthread_mutex_lock(&md->lock);
618   e = md_entry_lookup(md, key);
619   if (e == NULL) {
620     pthread_mutex_unlock(&md->lock);
621     return (-ENOENT);
622   }
624   if (e->type != MD_TYPE_UNSIGNED_INT) {
625     ERROR("meta_data_get_unsigned_int: Type mismatch for key `%s'", e->key);
626     pthread_mutex_unlock(&md->lock);
627     return (-ENOENT);
628   }
630   *value = e->value.mv_unsigned_int;
632   pthread_mutex_unlock(&md->lock);
633   return (0);
634 } /* }}} int meta_data_get_unsigned_int */
636 int meta_data_get_double(meta_data_t *md, /* {{{ */
637                          const char *key, double *value) {
638   meta_entry_t *e;
640   if ((md == NULL) || (key == NULL) || (value == NULL))
641     return (-EINVAL);
643   pthread_mutex_lock(&md->lock);
645   e = md_entry_lookup(md, key);
646   if (e == NULL) {
647     pthread_mutex_unlock(&md->lock);
648     return (-ENOENT);
649   }
651   if (e->type != MD_TYPE_DOUBLE) {
652     ERROR("meta_data_get_double: Type mismatch for key `%s'", e->key);
653     pthread_mutex_unlock(&md->lock);
654     return (-ENOENT);
655   }
657   *value = e->value.mv_double;
659   pthread_mutex_unlock(&md->lock);
660   return (0);
661 } /* }}} int meta_data_get_double */
663 int meta_data_get_boolean(meta_data_t *md, /* {{{ */
664                           const char *key, _Bool *value) {
665   meta_entry_t *e;
667   if ((md == NULL) || (key == NULL) || (value == NULL))
668     return (-EINVAL);
670   pthread_mutex_lock(&md->lock);
672   e = md_entry_lookup(md, key);
673   if (e == NULL) {
674     pthread_mutex_unlock(&md->lock);
675     return (-ENOENT);
676   }
678   if (e->type != MD_TYPE_BOOLEAN) {
679     ERROR("meta_data_get_boolean: Type mismatch for key `%s'", e->key);
680     pthread_mutex_unlock(&md->lock);
681     return (-ENOENT);
682   }
684   *value = e->value.mv_boolean;
686   pthread_mutex_unlock(&md->lock);
687   return (0);
688 } /* }}} int meta_data_get_boolean */
690 int meta_data_as_string(meta_data_t *md, /* {{{ */
691                         const char *key, char **value) {
692   meta_entry_t *e;
693   const char *actual;
694   char buffer[MD_MAX_NONSTRING_CHARS]; /* For non-string types. */
695   char *temp;
696   int type;
698   if ((md == NULL) || (key == NULL) || (value == NULL))
699     return (-EINVAL);
701   pthread_mutex_lock(&md->lock);
703   e = md_entry_lookup(md, key);
704   if (e == NULL) {
705     pthread_mutex_unlock(&md->lock);
706     return (-ENOENT);
707   }
709   type = e->type;
711   switch (type) {
712   case MD_TYPE_STRING:
713     actual = e->value.mv_string;
714     break;
715   case MD_TYPE_SIGNED_INT:
716     ssnprintf(buffer, sizeof(buffer), "%" PRIi64, e->value.mv_signed_int);
717     actual = buffer;
718     break;
719   case MD_TYPE_UNSIGNED_INT:
720     ssnprintf(buffer, sizeof(buffer), "%" PRIu64, e->value.mv_unsigned_int);
721     actual = buffer;
722     break;
723   case MD_TYPE_DOUBLE:
724     ssnprintf(buffer, sizeof(buffer), GAUGE_FORMAT, e->value.mv_double);
725     actual = buffer;
726     break;
727   case MD_TYPE_BOOLEAN:
728     actual = e->value.mv_boolean ? "true" : "false";
729     break;
730   default:
731     pthread_mutex_unlock(&md->lock);
732     ERROR("meta_data_as_string: unknown type %d for key `%s'", type, key);
733     return (-ENOENT);
734   }
736   pthread_mutex_unlock(&md->lock);
738   temp = md_strdup(actual);
739   if (temp == NULL) {
740     pthread_mutex_unlock(&md->lock);
741     ERROR("meta_data_as_string: md_strdup failed for key `%s'.", key);
742     return (-ENOMEM);
743   }
745   *value = temp;
747   return (0);
748 } /* }}} int meta_data_as_string */