116c34f363d535b2d644ee73add242804be3465d
[collectd.git] / src / perl.c
1 /**
2  * collectd - src/perl.c
3  * Copyright (C) 2007-2009  Sebastian Harl
4  *
5  * This program is free software; you can redistribute it and/or modify it
6  * under the terms of the GNU General Public License as published by the
7  * Free Software Foundation; only version 2 of the License is applicable.
8  *
9  * This program is distributed in the hope that it will be useful, but
10  * WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License along
15  * with this program; if not, write to the Free Software Foundation, Inc.,
16  * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA
17  *
18  * Author:
19  *   Sebastian Harl <sh at tokkee.org>
20  **/
21
22 /*
23  * This plugin embeds a Perl interpreter into collectd and provides an
24  * interface for collectd plugins written in perl.
25  */
26
27 /* do not automatically get the thread specific perl interpreter */
28 #define PERL_NO_GET_CONTEXT
29
30 #define DONT_POISON_SPRINTF_YET 1
31 #include "collectd.h"
32 #undef DONT_POISON_SPRINTF_YET
33
34 #include "configfile.h"
35
36 #if HAVE_STDBOOL_H
37 # include <stdbool.h>
38 #endif
39
40 #include <EXTERN.h>
41 #include <perl.h>
42
43 #if defined(COLLECT_DEBUG) && COLLECT_DEBUG && defined(__GNUC__) && __GNUC__
44 # pragma GCC poison sprintf
45 #endif
46
47 #include <XSUB.h>
48
49 /* Some versions of Perl define their own version of DEBUG... :-/ */
50 #ifdef DEBUG
51 # undef DEBUG
52 #endif /* DEBUG */
53
54 /* ... while we want the definition found in plugin.h. */
55 #include "plugin.h"
56 #include "common.h"
57
58 #include "filter_chain.h"
59
60 #include <pthread.h>
61
62 #if !defined(USE_ITHREADS)
63 # error "Perl does not support ithreads!"
64 #endif /* !defined(USE_ITHREADS) */
65
66 /* clear the Perl sub's stack frame
67  * (this should only be used inside an XSUB) */
68 #define CLEAR_STACK_FRAME PL_stack_sp = PL_stack_base + *PL_markstack_ptr
69
70 #define PLUGIN_INIT     0
71 #define PLUGIN_READ     1
72 #define PLUGIN_WRITE    2
73 #define PLUGIN_SHUTDOWN 3
74 #define PLUGIN_LOG      4
75 #define PLUGIN_NOTIF    5
76 #define PLUGIN_FLUSH    6
77
78 #define PLUGIN_TYPES    7
79
80 #define PLUGIN_CONFIG   254
81 #define PLUGIN_DATASET  255
82
83 #define FC_MATCH  0
84 #define FC_TARGET 1
85
86 #define FC_TYPES  2
87
88 #define FC_CB_CREATE  0
89 #define FC_CB_DESTROY 1
90 #define FC_CB_EXEC    2
91
92 #define FC_CB_TYPES   3
93
94 #define log_debug(...) DEBUG ("perl: " __VA_ARGS__)
95 #define log_info(...) INFO ("perl: " __VA_ARGS__)
96 #define log_warn(...) WARNING ("perl: " __VA_ARGS__)
97 #define log_err(...) ERROR ("perl: " __VA_ARGS__)
98
99 /* this is defined in DynaLoader.a */
100 void boot_DynaLoader (PerlInterpreter *, CV *);
101
102 static XS (Collectd_plugin_register_ds);
103 static XS (Collectd_plugin_unregister_ds);
104 static XS (Collectd_plugin_dispatch_values);
105 static XS (Collectd__plugin_write);
106 static XS (Collectd__plugin_flush);
107 static XS (Collectd_plugin_dispatch_notification);
108 static XS (Collectd_plugin_log);
109 static XS (Collectd__fc_register);
110 static XS (Collectd_call_by_name);
111
112 /*
113  * private data types
114  */
115
116 typedef struct c_ithread_s {
117         /* the thread's Perl interpreter */
118         PerlInterpreter *interp;
119
120         /* double linked list of threads */
121         struct c_ithread_s *prev;
122         struct c_ithread_s *next;
123 } c_ithread_t;
124
125 typedef struct {
126         c_ithread_t *head;
127         c_ithread_t *tail;
128
129 #if COLLECT_DEBUG
130         /* some usage stats */
131         int number_of_threads;
132 #endif /* COLLECT_DEBUG */
133
134         pthread_mutex_t mutex;
135 } c_ithread_list_t;
136
137 /* name / user_data for Perl matches / targets */
138 typedef struct {
139         char *name;
140         SV   *user_data;
141 } pfc_user_data_t;
142
143 #define PFC_USER_DATA_FREE(data) \
144         do { \
145                 sfree ((data)->name); \
146                 if (NULL != (data)->user_data) \
147                         sv_free ((data)->user_data); \
148                 sfree (data); \
149         } while (0)
150
151 /*
152  * Public variable
153  */
154 extern char **environ;
155
156 /*
157  * private variables
158  */
159
160 /* if perl_threads != NULL perl_threads->head must
161  * point to the "base" thread */
162 static c_ithread_list_t *perl_threads = NULL;
163
164 /* the key used to store each pthread's ithread */
165 static pthread_key_t perl_thr_key;
166
167 static int    perl_argc = 0;
168 static char **perl_argv = NULL;
169
170 static char base_name[DATA_MAX_NAME_LEN] = "";
171
172 static struct {
173         char name[64];
174         XS ((*f));
175 } api[] =
176 {
177         { "Collectd::plugin_register_data_set",   Collectd_plugin_register_ds },
178         { "Collectd::plugin_unregister_data_set", Collectd_plugin_unregister_ds },
179         { "Collectd::plugin_dispatch_values",     Collectd_plugin_dispatch_values },
180         { "Collectd::_plugin_write",              Collectd__plugin_write },
181         { "Collectd::_plugin_flush",              Collectd__plugin_flush },
182         { "Collectd::plugin_dispatch_notification",
183                 Collectd_plugin_dispatch_notification },
184         { "Collectd::plugin_log",                 Collectd_plugin_log },
185         { "Collectd::_fc_register",               Collectd__fc_register },
186         { "Collectd::call_by_name",               Collectd_call_by_name },
187         { "", NULL }
188 };
189
190 struct {
191         char name[64];
192         int  value;
193 } constants[] =
194 {
195         { "Collectd::TYPE_INIT",          PLUGIN_INIT },
196         { "Collectd::TYPE_READ",          PLUGIN_READ },
197         { "Collectd::TYPE_WRITE",         PLUGIN_WRITE },
198         { "Collectd::TYPE_SHUTDOWN",      PLUGIN_SHUTDOWN },
199         { "Collectd::TYPE_LOG",           PLUGIN_LOG },
200         { "Collectd::TYPE_NOTIF",         PLUGIN_NOTIF },
201         { "Collectd::TYPE_FLUSH",         PLUGIN_FLUSH },
202         { "Collectd::TYPE_CONFIG",        PLUGIN_CONFIG },
203         { "Collectd::TYPE_DATASET",       PLUGIN_DATASET },
204         { "Collectd::DS_TYPE_COUNTER",    DS_TYPE_COUNTER },
205         { "Collectd::DS_TYPE_GAUGE",      DS_TYPE_GAUGE },
206         { "Collectd::DS_TYPE_DERIVE",     DS_TYPE_DERIVE },
207         { "Collectd::DS_TYPE_ABSOLUTE",   DS_TYPE_ABSOLUTE },
208         { "Collectd::LOG_ERR",            LOG_ERR },
209         { "Collectd::LOG_WARNING",        LOG_WARNING },
210         { "Collectd::LOG_NOTICE",         LOG_NOTICE },
211         { "Collectd::LOG_INFO",           LOG_INFO },
212         { "Collectd::LOG_DEBUG",          LOG_DEBUG },
213         { "Collectd::FC_MATCH",           FC_MATCH },
214         { "Collectd::FC_TARGET",          FC_TARGET },
215         { "Collectd::FC_CB_CREATE",       FC_CB_CREATE },
216         { "Collectd::FC_CB_DESTROY",      FC_CB_DESTROY },
217         { "Collectd::FC_CB_EXEC",         FC_CB_EXEC },
218         { "Collectd::FC_MATCH_NO_MATCH",  FC_MATCH_NO_MATCH },
219         { "Collectd::FC_MATCH_MATCHES",   FC_MATCH_MATCHES },
220         { "Collectd::FC_TARGET_CONTINUE", FC_TARGET_CONTINUE },
221         { "Collectd::FC_TARGET_STOP",     FC_TARGET_STOP },
222         { "Collectd::FC_TARGET_RETURN",   FC_TARGET_RETURN },
223         { "Collectd::NOTIF_FAILURE",      NOTIF_FAILURE },
224         { "Collectd::NOTIF_WARNING",      NOTIF_WARNING },
225         { "Collectd::NOTIF_OKAY",         NOTIF_OKAY },
226         { "", 0 }
227 };
228
229 struct {
230         char  name[64];
231         char *var;
232 } g_strings[] =
233 {
234         { "Collectd::hostname_g", hostname_g },
235         { "", NULL }
236 };
237
238 struct {
239         char  name[64];
240         int  *var;
241 } g_integers[] =
242 {
243         { "Collectd::interval_g", &interval_g },
244         { "", NULL }
245 };
246
247 /*
248  * Helper functions for data type conversion.
249  */
250
251 /*
252  * data source:
253  * [
254  *   {
255  *     name => $ds_name,
256  *     type => $ds_type,
257  *     min  => $ds_min,
258  *     max  => $ds_max
259  *   },
260  *   ...
261  * ]
262  */
263 static int hv2data_source (pTHX_ HV *hash, data_source_t *ds)
264 {
265         SV **tmp = NULL;
266
267         if ((NULL == hash) || (NULL == ds))
268                 return -1;
269
270         if (NULL != (tmp = hv_fetch (hash, "name", 4, 0))) {
271                 sstrncpy (ds->name, SvPV_nolen (*tmp), sizeof (ds->name));
272         }
273         else {
274                 log_err ("hv2data_source: No DS name given.");
275                 return -1;
276         }
277
278         if (NULL != (tmp = hv_fetch (hash, "type", 4, 0))) {
279                 ds->type = SvIV (*tmp);
280
281                 if ((DS_TYPE_COUNTER != ds->type)
282                                 && (DS_TYPE_GAUGE != ds->type)
283                                 && (DS_TYPE_DERIVE != ds->type)
284                                 && (DS_TYPE_ABSOLUTE != ds->type)) {
285                         log_err ("hv2data_source: Invalid DS type.");
286                         return -1;
287                 }
288         }
289         else {
290                 ds->type = DS_TYPE_COUNTER;
291         }
292
293         if (NULL != (tmp = hv_fetch (hash, "min", 3, 0)))
294                 ds->min = SvNV (*tmp);
295         else
296                 ds->min = NAN;
297
298         if (NULL != (tmp = hv_fetch (hash, "max", 3, 0)))
299                 ds->max = SvNV (*tmp);
300         else
301                 ds->max = NAN;
302         return 0;
303 } /* static int hv2data_source (HV *, data_source_t *) */
304
305 static int av2value (pTHX_ char *name, AV *array, value_t *value, int len)
306 {
307         const data_set_t *ds;
308
309         int i = 0;
310
311         if ((NULL == name) || (NULL == array) || (NULL == value))
312                 return -1;
313
314         if (av_len (array) < len - 1)
315                 len = av_len (array) + 1;
316
317         if (0 >= len)
318                 return -1;
319
320         ds = plugin_get_ds (name);
321         if (NULL == ds) {
322                 log_err ("av2value: Unknown dataset \"%s\"", name);
323                 return -1;
324         }
325
326         if (ds->ds_num < len) {
327                 log_warn ("av2value: Value length exceeds data set length.");
328                 len = ds->ds_num;
329         }
330
331         for (i = 0; i < len; ++i) {
332                 SV **tmp = av_fetch (array, i, 0);
333
334                 if (NULL != tmp) {
335                         if (DS_TYPE_COUNTER == ds->ds[i].type)
336                                 value[i].counter = SvIV (*tmp);
337                         else if (DS_TYPE_GAUGE == ds->ds[i].type)
338                                 value[i].gauge = SvNV (*tmp);
339                         else if (DS_TYPE_DERIVE == ds->ds[i].type)
340                                 value[i].derive = SvIV (*tmp);
341                         else if (DS_TYPE_ABSOLUTE == ds->ds[i].type)
342                                 value[i].absolute = SvIV (*tmp);
343                 }
344                 else {
345                         return -1;
346                 }
347         }
348         return len;
349 } /* static int av2value (char *, AV *, value_t *, int) */
350
351 /*
352  * value list:
353  * {
354  *   values => [ @values ],
355  *   time   => $time,
356  *   host   => $host,
357  *   plugin => $plugin,
358  *   plugin_instance => $pinstance,
359  *   type_instance   => $tinstance,
360  * }
361  */
362 static int hv2value_list (pTHX_ HV *hash, value_list_t *vl)
363 {
364         SV **tmp;
365
366         if ((NULL == hash) || (NULL == vl))
367                 return -1;
368
369         if (NULL == (tmp = hv_fetch (hash, "type", 4, 0))) {
370                 log_err ("hv2value_list: No type given.");
371                 return -1;
372         }
373
374         sstrncpy (vl->type, SvPV_nolen (*tmp), sizeof (vl->type));
375
376         if ((NULL == (tmp = hv_fetch (hash, "values", 6, 0)))
377                         || (! (SvROK (*tmp) && (SVt_PVAV == SvTYPE (SvRV (*tmp)))))) {
378                 log_err ("hv2value_list: No valid values given.");
379                 return -1;
380         }
381
382         {
383                 AV  *array = (AV *)SvRV (*tmp);
384                 int len    = av_len (array) + 1;
385
386                 if (len <= 0)
387                         return -1;
388
389                 vl->values     = (value_t *)smalloc (len * sizeof (value_t));
390                 vl->values_len = av2value (aTHX_ vl->type, (AV *)SvRV (*tmp),
391                                 vl->values, len);
392
393                 if (-1 == vl->values_len) {
394                         sfree (vl->values);
395                         return -1;
396                 }
397         }
398
399         if (NULL != (tmp = hv_fetch (hash, "time", 4, 0)))
400         {
401                 double t = SvNV (*tmp);
402                 vl->time = DOUBLE_TO_CDTIME_T (t);
403         }
404
405         if (NULL != (tmp = hv_fetch (hash, "interval", 8, 0)))
406                 vl->interval = SvIV (*tmp);
407
408         if (NULL != (tmp = hv_fetch (hash, "host", 4, 0)))
409                 sstrncpy (vl->host, SvPV_nolen (*tmp), sizeof (vl->host));
410         else
411                 sstrncpy (vl->host, hostname_g, sizeof (vl->host));
412
413         if (NULL != (tmp = hv_fetch (hash, "plugin", 6, 0)))
414                 sstrncpy (vl->plugin, SvPV_nolen (*tmp), sizeof (vl->plugin));
415
416         if (NULL != (tmp = hv_fetch (hash, "plugin_instance", 15, 0)))
417                 sstrncpy (vl->plugin_instance, SvPV_nolen (*tmp),
418                                 sizeof (vl->plugin_instance));
419
420         if (NULL != (tmp = hv_fetch (hash, "type_instance", 13, 0)))
421                 sstrncpy (vl->type_instance, SvPV_nolen (*tmp),
422                                 sizeof (vl->type_instance));
423         return 0;
424 } /* static int hv2value_list (pTHX_ HV *, value_list_t *) */
425
426 static int av2data_set (pTHX_ AV *array, char *name, data_set_t *ds)
427 {
428         int len, i;
429
430         if ((NULL == array) || (NULL == name) || (NULL == ds))
431                 return -1;
432
433         len = av_len (array);
434
435         if (-1 == len) {
436                 log_err ("av2data_set: Invalid data set.");
437                 return -1;
438         }
439
440         ds->ds = (data_source_t *)smalloc ((len + 1) * sizeof (data_source_t));
441         ds->ds_num = len + 1;
442
443         for (i = 0; i <= len; ++i) {
444                 SV **elem = av_fetch (array, i, 0);
445
446                 if (NULL == elem) {
447                         log_err ("av2data_set: Failed to fetch data source %i.", i);
448                         return -1;
449                 }
450
451                 if (! (SvROK (*elem) && (SVt_PVHV == SvTYPE (SvRV (*elem))))) {
452                         log_err ("av2data_set: Invalid data source.");
453                         return -1;
454                 }
455
456                 if (-1 == hv2data_source (aTHX_ (HV *)SvRV (*elem), &ds->ds[i]))
457                         return -1;
458
459                 log_debug ("av2data_set: "
460                                 "DS.name = \"%s\", DS.type = %i, DS.min = %f, DS.max = %f",
461                                 ds->ds[i].name, ds->ds[i].type, ds->ds[i].min, ds->ds[i].max);
462         }
463
464         sstrncpy (ds->type, name, sizeof (ds->type));
465         return 0;
466 } /* static int av2data_set (pTHX_ AV *, data_set_t *) */
467
468 /*
469  * notification:
470  * {
471  *   severity => $severity,
472  *   time     => $time,
473  *   message  => $msg,
474  *   host     => $host,
475  *   plugin   => $plugin,
476  *   type     => $type,
477  *   plugin_instance => $instance,
478  *   type_instance   => $type_instance,
479  *   meta     => [ { name => <name>, value => <value> }, ... ]
480  * }
481  */
482 static int av2notification_meta (pTHX_ AV *array, notification_meta_t **meta)
483 {
484         notification_meta_t **m = meta;
485
486         int len = av_len (array);
487         int i;
488
489         for (i = 0; i <= len; ++i) {
490                 SV **tmp = av_fetch (array, i, 0);
491                 HV  *hash;
492
493                 if (NULL == tmp)
494                         return -1;
495
496                 if (! (SvROK (*tmp) && (SVt_PVHV == SvTYPE (SvRV (*tmp))))) {
497                         log_warn ("av2notification_meta: Skipping invalid "
498                                         "meta information.");
499                         continue;
500                 }
501
502                 hash = (HV *)SvRV (*tmp);
503
504                 *m = (notification_meta_t *)smalloc (sizeof (**m));
505
506                 if (NULL == (tmp = hv_fetch (hash, "name", 4, 0))) {
507                         log_warn ("av2notification_meta: Skipping invalid "
508                                         "meta information.");
509                         free (*m);
510                         continue;
511                 }
512                 sstrncpy ((*m)->name, SvPV_nolen (*tmp), sizeof ((*m)->name));
513
514                 if (NULL == (tmp = hv_fetch (hash, "value", 5, 0))) {
515                         log_warn ("av2notification_meta: Skipping invalid "
516                                         "meta information.");
517                         free ((*m)->name);
518                         free (*m);
519                         continue;
520                 }
521
522                 if (SvNOK (*tmp)) {
523                         (*m)->nm_value.nm_double = SvNVX (*tmp);
524                         (*m)->type = NM_TYPE_DOUBLE;
525                 }
526                 else if (SvUOK (*tmp)) {
527                         (*m)->nm_value.nm_unsigned_int = SvUVX (*tmp);
528                         (*m)->type = NM_TYPE_UNSIGNED_INT;
529                 }
530                 else if (SvIOK (*tmp)) {
531                         (*m)->nm_value.nm_signed_int = SvIVX (*tmp);
532                         (*m)->type = NM_TYPE_SIGNED_INT;
533                 }
534                 else {
535                         (*m)->nm_value.nm_string = sstrdup (SvPV_nolen (*tmp));
536                         (*m)->type = NM_TYPE_STRING;
537                 }
538
539                 (*m)->next = NULL;
540                 m = &((*m)->next);
541         }
542         return 0;
543 } /* static int av2notification_meta (AV *, notification_meta_t *) */
544
545 static int hv2notification (pTHX_ HV *hash, notification_t *n)
546 {
547         SV **tmp = NULL;
548
549         if ((NULL == hash) || (NULL == n))
550                 return -1;
551
552         if (NULL != (tmp = hv_fetch (hash, "severity", 8, 0)))
553                 n->severity = SvIV (*tmp);
554         else
555                 n->severity = NOTIF_FAILURE;
556
557         if (NULL != (tmp = hv_fetch (hash, "time", 4, 0)))
558         {
559                 double t = SvNV (*tmp);
560                 n->time = DOUBLE_TO_CDTIME_T (t);
561         }
562         else
563                 n->time = cdtime ();
564
565         if (NULL != (tmp = hv_fetch (hash, "message", 7, 0)))
566                 sstrncpy (n->message, SvPV_nolen (*tmp), sizeof (n->message));
567
568         if (NULL != (tmp = hv_fetch (hash, "host", 4, 0)))
569                 sstrncpy (n->host, SvPV_nolen (*tmp), sizeof (n->host));
570         else
571                 sstrncpy (n->host, hostname_g, sizeof (n->host));
572
573         if (NULL != (tmp = hv_fetch (hash, "plugin", 6, 0)))
574                 sstrncpy (n->plugin, SvPV_nolen (*tmp), sizeof (n->plugin));
575
576         if (NULL != (tmp = hv_fetch (hash, "plugin_instance", 15, 0)))
577                 sstrncpy (n->plugin_instance, SvPV_nolen (*tmp),
578                                 sizeof (n->plugin_instance));
579
580         if (NULL != (tmp = hv_fetch (hash, "type", 4, 0)))
581                 sstrncpy (n->type, SvPV_nolen (*tmp), sizeof (n->type));
582
583         if (NULL != (tmp = hv_fetch (hash, "type_instance", 13, 0)))
584                 sstrncpy (n->type_instance, SvPV_nolen (*tmp),
585                                 sizeof (n->type_instance));
586
587         n->meta = NULL;
588         while (NULL != (tmp = hv_fetch (hash, "meta", 4, 0))) {
589                 if (! (SvROK (*tmp) && (SVt_PVAV == SvTYPE (SvRV (*tmp))))) {
590                         log_warn ("hv2notification: Ignoring invalid meta information.");
591                         break;
592                 }
593
594                 if (0 != av2notification_meta (aTHX_ (AV *)SvRV (*tmp), &n->meta)) {
595                         plugin_notification_meta_free (n->meta);
596                         n->meta = NULL;
597                         return -1;
598                 }
599                 break;
600         }
601         return 0;
602 } /* static int hv2notification (pTHX_ HV *, notification_t *) */
603
604 static int data_set2av (pTHX_ data_set_t *ds, AV *array)
605 {
606         int i = 0;
607
608         if ((NULL == ds) || (NULL == array))
609                 return -1;
610
611         av_extend (array, ds->ds_num);
612
613         for (i = 0; i < ds->ds_num; ++i) {
614                 HV *source = newHV ();
615
616                 if (NULL == hv_store (source, "name", 4,
617                                 newSVpv (ds->ds[i].name, 0), 0))
618                         return -1;
619
620                 if (NULL == hv_store (source, "type", 4, newSViv (ds->ds[i].type), 0))
621                         return -1;
622
623                 if (! isnan (ds->ds[i].min))
624                         if (NULL == hv_store (source, "min", 3,
625                                         newSVnv (ds->ds[i].min), 0))
626                                 return -1;
627
628                 if (! isnan (ds->ds[i].max))
629                         if (NULL == hv_store (source, "max", 3,
630                                         newSVnv (ds->ds[i].max), 0))
631                                 return -1;
632
633                 if (NULL == av_store (array, i, newRV_noinc ((SV *)source)))
634                         return -1;
635         }
636         return 0;
637 } /* static int data_set2av (data_set_t *, AV *) */
638
639 static int value_list2hv (pTHX_ value_list_t *vl, data_set_t *ds, HV *hash)
640 {
641         AV *values = NULL;
642
643         int i   = 0;
644         int len = 0;
645
646         if ((NULL == vl) || (NULL == ds) || (NULL == hash))
647                 return -1;
648
649         len = vl->values_len;
650
651         if (ds->ds_num < len) {
652                 log_warn ("value2av: Value length exceeds data set length.");
653                 len = ds->ds_num;
654         }
655
656         values = newAV ();
657         av_extend (values, len - 1);
658
659         for (i = 0; i < len; ++i) {
660                 SV *val = NULL;
661
662                 if (DS_TYPE_COUNTER == ds->ds[i].type)
663                         val = newSViv (vl->values[i].counter);
664                 else if (DS_TYPE_GAUGE == ds->ds[i].type)
665                         val = newSVnv (vl->values[i].gauge);
666                 else if (DS_TYPE_DERIVE == ds->ds[i].type)
667                         val = newSViv (vl->values[i].derive);
668                 else if (DS_TYPE_ABSOLUTE == ds->ds[i].type)
669                         val = newSViv (vl->values[i].absolute);
670
671                 if (NULL == av_store (values, i, val)) {
672                         av_undef (values);
673                         return -1;
674                 }
675         }
676
677         if (NULL == hv_store (hash, "values", 6, newRV_noinc ((SV *)values), 0))
678                 return -1;
679
680         if (0 != vl->time)
681         {
682                 double t = CDTIME_T_TO_DOUBLE (vl->time);
683                 if (NULL == hv_store (hash, "time", 4, newSVnv (t), 0))
684                         return -1;
685         }
686
687         if (NULL == hv_store (hash, "interval", 8, newSViv (vl->interval), 0))
688                 return -1;
689
690         if ('\0' != vl->host[0])
691                 if (NULL == hv_store (hash, "host", 4, newSVpv (vl->host, 0), 0))
692                         return -1;
693
694         if ('\0' != vl->plugin[0])
695                 if (NULL == hv_store (hash, "plugin", 6, newSVpv (vl->plugin, 0), 0))
696                         return -1;
697
698         if ('\0' != vl->plugin_instance[0])
699                 if (NULL == hv_store (hash, "plugin_instance", 15,
700                                 newSVpv (vl->plugin_instance, 0), 0))
701                         return -1;
702
703         if ('\0' != vl->type[0])
704                 if (NULL == hv_store (hash, "type", 4, newSVpv (vl->type, 0), 0))
705                         return -1;
706
707         if ('\0' != vl->type_instance[0])
708                 if (NULL == hv_store (hash, "type_instance", 13,
709                                 newSVpv (vl->type_instance, 0), 0))
710                         return -1;
711         return 0;
712 } /* static int value2av (value_list_t *, data_set_t *, HV *) */
713
714 static int notification_meta2av (pTHX_ notification_meta_t *meta, AV *array)
715 {
716         int meta_num = 0;
717         int i;
718
719         while (meta) {
720                 ++meta_num;
721                 meta = meta->next;
722         }
723
724         av_extend (array, meta_num);
725
726         for (i = 0; NULL != meta; meta = meta->next, ++i) {
727                 HV *m = newHV ();
728                 SV *value;
729
730                 if (NULL == hv_store (m, "name", 4, newSVpv (meta->name, 0), 0))
731                         return -1;
732
733                 if (NM_TYPE_STRING == meta->type)
734                         value = newSVpv (meta->nm_value.nm_string, 0);
735                 else if (NM_TYPE_SIGNED_INT == meta->type)
736                         value = newSViv (meta->nm_value.nm_signed_int);
737                 else if (NM_TYPE_UNSIGNED_INT == meta->type)
738                         value = newSVuv (meta->nm_value.nm_unsigned_int);
739                 else if (NM_TYPE_DOUBLE == meta->type)
740                         value = newSVnv (meta->nm_value.nm_double);
741                 else if (NM_TYPE_BOOLEAN == meta->type)
742                         value = meta->nm_value.nm_boolean ? &PL_sv_yes : &PL_sv_no;
743                 else
744                         return -1;
745
746                 if (NULL == hv_store (m, "value", 5, value, 0)) {
747                         sv_free (value);
748                         return -1;
749                 }
750
751                 if (NULL == av_store (array, i, newRV_noinc ((SV *)m))) {
752                         hv_clear (m);
753                         hv_undef (m);
754                         return -1;
755                 }
756         }
757         return 0;
758 } /* static int notification_meta2av (notification_meta_t *, AV *) */
759
760 static int notification2hv (pTHX_ notification_t *n, HV *hash)
761 {
762         if (NULL == hv_store (hash, "severity", 8, newSViv (n->severity), 0))
763                 return -1;
764
765         if (0 != n->time)
766         {
767                 double t = CDTIME_T_TO_DOUBLE (n->time);
768                 if (NULL == hv_store (hash, "time", 4, newSVnv (t), 0))
769                         return -1;
770         }
771
772         if ('\0' != *n->message)
773                 if (NULL == hv_store (hash, "message", 7, newSVpv (n->message, 0), 0))
774                         return -1;
775
776         if ('\0' != *n->host)
777                 if (NULL == hv_store (hash, "host", 4, newSVpv (n->host, 0), 0))
778                         return -1;
779
780         if ('\0' != *n->plugin)
781                 if (NULL == hv_store (hash, "plugin", 6, newSVpv (n->plugin, 0), 0))
782                         return -1;
783
784         if ('\0' != *n->plugin_instance)
785                 if (NULL == hv_store (hash, "plugin_instance", 15,
786                                 newSVpv (n->plugin_instance, 0), 0))
787                         return -1;
788
789         if ('\0' != *n->type)
790                 if (NULL == hv_store (hash, "type", 4, newSVpv (n->type, 0), 0))
791                         return -1;
792
793         if ('\0' != *n->type_instance)
794                 if (NULL == hv_store (hash, "type_instance", 13,
795                                 newSVpv (n->type_instance, 0), 0))
796                         return -1;
797
798         if (NULL != n->meta) {
799                 AV *meta = newAV ();
800                 if ((0 != notification_meta2av (aTHX_ n->meta, meta))
801                                 || (NULL == hv_store (hash, "meta", 4,
802                                                 newRV_noinc ((SV *)meta), 0))) {
803                         av_clear (meta);
804                         av_undef (meta);
805                         return -1;
806                 }
807         }
808         return 0;
809 } /* static int notification2hv (notification_t *, HV *) */
810
811 static int oconfig_item2hv (pTHX_ oconfig_item_t *ci, HV *hash)
812 {
813         int i;
814
815         AV *values;
816         AV *children;
817
818         if (NULL == hv_store (hash, "key", 3, newSVpv (ci->key, 0), 0))
819                 return -1;
820
821         values = newAV ();
822         if (0 < ci->values_num)
823                 av_extend (values, ci->values_num);
824
825         if (NULL == hv_store (hash, "values", 6, newRV_noinc ((SV *)values), 0)) {
826                 av_clear (values);
827                 av_undef (values);
828                 return -1;
829         }
830
831         for (i = 0; i < ci->values_num; ++i) {
832                 SV *value;
833
834                 switch (ci->values[i].type) {
835                         case OCONFIG_TYPE_STRING:
836                                 value = newSVpv (ci->values[i].value.string, 0);
837                                 break;
838                         case OCONFIG_TYPE_NUMBER:
839                                 value = newSVnv ((NV)ci->values[i].value.number);
840                                 break;
841                         case OCONFIG_TYPE_BOOLEAN:
842                                 value = ci->values[i].value.boolean ? &PL_sv_yes : &PL_sv_no;
843                                 break;
844                         default:
845                                 log_err ("oconfig_item2hv: Invalid value type %i.",
846                                                 ci->values[i].type);
847                                 value = &PL_sv_undef;
848                 }
849
850                 if (NULL == av_store (values, i, value)) {
851                         sv_free (value);
852                         return -1;
853                 }
854         }
855
856         /* ignoring 'parent' member which is uninteresting in this case */
857
858         children = newAV ();
859         if (0 < ci->children_num)
860                 av_extend (children, ci->children_num);
861
862         if (NULL == hv_store (hash, "children", 8, newRV_noinc ((SV *)children), 0)) {
863                 av_clear (children);
864                 av_undef (children);
865                 return -1;
866         }
867
868         for (i = 0; i < ci->children_num; ++i) {
869                 HV *child = newHV ();
870
871                 if (0 != oconfig_item2hv (aTHX_ ci->children + i, child)) {
872                         hv_clear (child);
873                         hv_undef (child);
874                         return -1;
875                 }
876
877                 if (NULL == av_store (children, i, newRV_noinc ((SV *)child))) {
878                         hv_clear (child);
879                         hv_undef (child);
880                         return -1;
881                 }
882         }
883         return 0;
884 } /* static int oconfig_item2hv (pTHX_ oconfig_item_t *, HV *) */
885
886 /*
887  * Internal functions.
888  */
889
890 static char *get_module_name (char *buf, size_t buf_len, const char *module) {
891         int status = 0;
892         if (base_name[0] == '\0')
893                 status = ssnprintf (buf, buf_len, "%s", module);
894         else
895                 status = ssnprintf (buf, buf_len, "%s::%s", base_name, module);
896         if ((status < 0) || ((unsigned int)status >= buf_len))
897                 return (NULL);
898         return (buf);
899 } /* char *get_module_name */
900
901 /*
902  * Add a plugin's data set definition.
903  */
904 static int pplugin_register_data_set (pTHX_ char *name, AV *dataset)
905 {
906         int ret = 0;
907
908         data_set_t ds;
909
910         if ((NULL == name) || (NULL == dataset))
911                 return -1;
912
913         if (0 != av2data_set (aTHX_ dataset, name, &ds))
914                 return -1;
915
916         ret = plugin_register_data_set (&ds);
917
918         free (ds.ds);
919         return ret;
920 } /* static int pplugin_register_data_set (char *, SV *) */
921
922 /*
923  * Remove a plugin's data set definition.
924  */
925 static int pplugin_unregister_data_set (char *name)
926 {
927         if (NULL == name)
928                 return 0;
929         return plugin_unregister_data_set (name);
930 } /* static int pplugin_unregister_data_set (char *) */
931
932 /*
933  * Submit the values to the write functions.
934  */
935 static int pplugin_dispatch_values (pTHX_ HV *values)
936 {
937         value_list_t vl = VALUE_LIST_INIT;
938
939         int ret = 0;
940
941         if (NULL == values)
942                 return -1;
943
944         if (0 != hv2value_list (aTHX_ values, &vl))
945                 return -1;
946
947         ret = plugin_dispatch_values (&vl);
948
949         sfree (vl.values);
950         return ret;
951 } /* static int pplugin_dispatch_values (char *, HV *) */
952
953 /*
954  * Submit the values to a single write function.
955  */
956 static int pplugin_write (pTHX_ const char *plugin, AV *data_set, HV *values)
957 {
958         data_set_t   ds;
959         value_list_t vl = VALUE_LIST_INIT;
960
961         int ret;
962
963         if (NULL == values)
964                 return -1;
965
966         if (0 != hv2value_list (aTHX_ values, &vl))
967                 return -1;
968
969         if ((NULL != data_set)
970                         && (0 != av2data_set (aTHX_ data_set, vl.type, &ds)))
971                 return -1;
972
973         ret = plugin_write (plugin, NULL == data_set ? NULL : &ds, &vl);
974         if (0 != ret)
975                 log_warn ("Dispatching value to plugin \"%s\" failed with status %i.",
976                                 NULL == plugin ? "<any>" : plugin, ret);
977
978         if (NULL != data_set)
979                 sfree (ds.ds);
980         sfree (vl.values);
981         return ret;
982 } /* static int pplugin_write (const char *plugin, HV *, HV *) */
983
984 /*
985  * Dispatch a notification.
986  */
987 static int pplugin_dispatch_notification (pTHX_ HV *notif)
988 {
989         notification_t n;
990
991         int ret;
992
993         if (NULL == notif)
994                 return -1;
995
996         memset (&n, 0, sizeof (n));
997
998         if (0 != hv2notification (aTHX_ notif, &n))
999                 return -1;
1000
1001         ret = plugin_dispatch_notification (&n);
1002         plugin_notification_meta_free (n.meta);
1003         return ret;
1004 } /* static int pplugin_dispatch_notification (HV *) */
1005
1006 /*
1007  * Call all working functions of the given type.
1008  */
1009 static int pplugin_call_all (pTHX_ int type, ...)
1010 {
1011         int retvals = 0;
1012
1013         va_list ap;
1014         int ret = 0;
1015
1016         dSP;
1017
1018         if ((type < 0) || (type >= PLUGIN_TYPES))
1019                 return -1;
1020
1021         va_start (ap, type);
1022
1023         ENTER;
1024         SAVETMPS;
1025
1026         PUSHMARK (SP);
1027
1028         XPUSHs (sv_2mortal (newSViv ((IV)type)));
1029
1030         if (PLUGIN_WRITE == type) {
1031                 /*
1032                  * $_[0] = $plugin_type;
1033                  *
1034                  * $_[1] =
1035                  * [
1036                  *   {
1037                  *     name => $ds_name,
1038                  *     type => $ds_type,
1039                  *     min  => $ds_min,
1040                  *     max  => $ds_max
1041                  *   },
1042                  *   ...
1043                  * ];
1044                  *
1045                  * $_[2] =
1046                  * {
1047                  *   values => [ $v1, ... ],
1048                  *   time   => $time,
1049                  *   host   => $hostname,
1050                  *   plugin => $plugin,
1051                  *   type   => $type,
1052                  *   plugin_instance => $instance,
1053                  *   type_instance   => $type_instance
1054                  * };
1055                  */
1056                 data_set_t   *ds;
1057                 value_list_t *vl;
1058
1059                 AV *pds = newAV ();
1060                 HV *pvl = newHV ();
1061
1062                 ds = va_arg (ap, data_set_t *);
1063                 vl = va_arg (ap, value_list_t *);
1064
1065                 if (-1 == data_set2av (aTHX_ ds, pds)) {
1066                         av_clear (pds);
1067                         av_undef (pds);
1068                         pds = (AV *)&PL_sv_undef;
1069                         ret = -1;
1070                 }
1071
1072                 if (-1 == value_list2hv (aTHX_ vl, ds, pvl)) {
1073                         hv_clear (pvl);
1074                         hv_undef (pvl);
1075                         pvl = (HV *)&PL_sv_undef;
1076                         ret = -1;
1077                 }
1078
1079                 XPUSHs (sv_2mortal (newSVpv (ds->type, 0)));
1080                 XPUSHs (sv_2mortal (newRV_noinc ((SV *)pds)));
1081                 XPUSHs (sv_2mortal (newRV_noinc ((SV *)pvl)));
1082         }
1083         else if (PLUGIN_LOG == type) {
1084                 /*
1085                  * $_[0] = $level;
1086                  *
1087                  * $_[1] = $message;
1088                  */
1089                 XPUSHs (sv_2mortal (newSViv (va_arg (ap, int))));
1090                 XPUSHs (sv_2mortal (newSVpv (va_arg (ap, char *), 0)));
1091         }
1092         else if (PLUGIN_NOTIF == type) {
1093                 /*
1094                  * $_[0] =
1095                  * {
1096                  *   severity => $severity,
1097                  *   time     => $time,
1098                  *   message  => $msg,
1099                  *   host     => $host,
1100                  *   plugin   => $plugin,
1101                  *   type     => $type,
1102                  *   plugin_instance => $instance,
1103                  *   type_instance   => $type_instance
1104                  * };
1105                  */
1106                 notification_t *n;
1107                 HV *notif = newHV ();
1108
1109                 n = va_arg (ap, notification_t *);
1110
1111                 if (-1 == notification2hv (aTHX_ n, notif)) {
1112                         hv_clear (notif);
1113                         hv_undef (notif);
1114                         notif = (HV *)&PL_sv_undef;
1115                         ret = -1;
1116                 }
1117
1118                 XPUSHs (sv_2mortal (newRV_noinc ((SV *)notif)));
1119         }
1120         else if (PLUGIN_FLUSH == type) {
1121                 cdtime_t timeout;
1122
1123                 /*
1124                  * $_[0] = $timeout;
1125                  * $_[1] = $identifier;
1126                  */
1127                 timeout = va_arg (ap, cdtime_t);
1128
1129                 XPUSHs (sv_2mortal (newSVnv (CDTIME_T_TO_DOUBLE (timeout))));
1130                 XPUSHs (sv_2mortal (newSVpv (va_arg (ap, char *), 0)));
1131         }
1132
1133         PUTBACK;
1134
1135         retvals = call_pv ("Collectd::plugin_call_all", G_SCALAR);
1136
1137         SPAGAIN;
1138         if (0 < retvals) {
1139                 SV *tmp = POPs;
1140                 if (! SvTRUE (tmp))
1141                         ret = -1;
1142         }
1143
1144         PUTBACK;
1145         FREETMPS;
1146         LEAVE;
1147
1148         va_end (ap);
1149         return ret;
1150 } /* static int pplugin_call_all (int, ...) */
1151
1152 /*
1153  * collectd's perl interpreter based thread implementation.
1154  *
1155  * This has been inspired by Perl's ithreads introduced in version 5.6.0.
1156  */
1157
1158 /* must be called with perl_threads->mutex locked */
1159 static void c_ithread_destroy (c_ithread_t *ithread)
1160 {
1161         dTHXa (ithread->interp);
1162
1163         assert (NULL != perl_threads);
1164
1165         PERL_SET_CONTEXT (aTHX);
1166         log_debug ("Shutting down Perl interpreter %p...", aTHX);
1167
1168 #if COLLECT_DEBUG
1169         sv_report_used ();
1170
1171         --perl_threads->number_of_threads;
1172 #endif /* COLLECT_DEBUG */
1173
1174         perl_destruct (aTHX);
1175         perl_free (aTHX);
1176
1177         if (NULL == ithread->prev)
1178                 perl_threads->head = ithread->next;
1179         else
1180                 ithread->prev->next = ithread->next;
1181
1182         if (NULL == ithread->next)
1183                 perl_threads->tail = ithread->prev;
1184         else
1185                 ithread->next->prev = ithread->prev;
1186
1187         sfree (ithread);
1188         return;
1189 } /* static void c_ithread_destroy (c_ithread_t *) */
1190
1191 static void c_ithread_destructor (void *arg)
1192 {
1193         c_ithread_t *ithread = (c_ithread_t *)arg;
1194         c_ithread_t *t = NULL;
1195
1196         if (NULL == perl_threads)
1197                 return;
1198
1199         pthread_mutex_lock (&perl_threads->mutex);
1200
1201         for (t = perl_threads->head; NULL != t; t = t->next)
1202                 if (t == ithread)
1203                         break;
1204
1205         /* the ithread no longer exists */
1206         if (NULL == t)
1207                 return;
1208
1209         c_ithread_destroy (ithread);
1210
1211         pthread_mutex_unlock (&perl_threads->mutex);
1212         return;
1213 } /* static void c_ithread_destructor (void *) */
1214
1215 /* must be called with perl_threads->mutex locked */
1216 static c_ithread_t *c_ithread_create (PerlInterpreter *base)
1217 {
1218         c_ithread_t *t = NULL;
1219         dTHXa (NULL);
1220
1221         assert (NULL != perl_threads);
1222
1223         t = (c_ithread_t *)smalloc (sizeof (c_ithread_t));
1224         memset (t, 0, sizeof (c_ithread_t));
1225
1226         t->interp = (NULL == base)
1227                 ? NULL
1228                 : perl_clone (base, CLONEf_KEEP_PTR_TABLE);
1229
1230         aTHX = t->interp;
1231
1232         if ((NULL != base) && (NULL != PL_endav)) {
1233                 av_clear (PL_endav);
1234                 av_undef (PL_endav);
1235                 PL_endav = Nullav;
1236         }
1237
1238 #if COLLECT_DEBUG
1239         ++perl_threads->number_of_threads;
1240 #endif /* COLLECT_DEBUG */
1241
1242         t->next = NULL;
1243
1244         if (NULL == perl_threads->tail) {
1245                 perl_threads->head = t;
1246                 t->prev = NULL;
1247         }
1248         else {
1249                 perl_threads->tail->next = t;
1250                 t->prev = perl_threads->tail;
1251         }
1252
1253         perl_threads->tail = t;
1254
1255         pthread_setspecific (perl_thr_key, (const void *)t);
1256         return t;
1257 } /* static c_ithread_t *c_ithread_create (PerlInterpreter *) */
1258
1259 /*
1260  * Filter chains implementation.
1261  */
1262
1263 static int fc_call (pTHX_ int type, int cb_type, pfc_user_data_t *data, ...)
1264 {
1265         int retvals = 0;
1266
1267         va_list ap;
1268         int ret = 0;
1269
1270         notification_meta_t **meta  = NULL;
1271         AV                   *pmeta = NULL;
1272
1273         dSP;
1274
1275         if ((type < 0) || (type >= FC_TYPES))
1276                 return -1;
1277
1278         if ((cb_type < 0) || (cb_type >= FC_CB_TYPES))
1279                 return -1;
1280
1281         va_start (ap, data);
1282
1283         ENTER;
1284         SAVETMPS;
1285
1286         PUSHMARK (SP);
1287
1288         XPUSHs (sv_2mortal (newSViv ((IV)type)));
1289         XPUSHs (sv_2mortal (newSVpv (data->name, 0)));
1290         XPUSHs (sv_2mortal (newSViv ((IV)cb_type)));
1291
1292         if (FC_CB_CREATE == cb_type) {
1293                 /*
1294                  * $_[0] = $ci;
1295                  * $_[1] = $user_data;
1296                  */
1297                 oconfig_item_t *ci;
1298                 HV *config = newHV ();
1299
1300                 ci = va_arg (ap, oconfig_item_t *);
1301
1302                 if (0 != oconfig_item2hv (aTHX_ ci, config)) {
1303                         hv_clear (config);
1304                         hv_undef (config);
1305                         config = (HV *)&PL_sv_undef;
1306                         ret = -1;
1307                 }
1308
1309                 XPUSHs (sv_2mortal (newRV_noinc ((SV *)config)));
1310         }
1311         else if (FC_CB_DESTROY == cb_type) {
1312                 /*
1313                  * $_[1] = $user_data;
1314                  */
1315
1316                 /* nothing to be done - the user data pointer
1317                  * is pushed onto the stack later */
1318         }
1319         else if (FC_CB_EXEC == cb_type) {
1320                 /*
1321                  * $_[0] = $ds;
1322                  * $_[1] = $vl;
1323                  * $_[2] = $meta;
1324                  * $_[3] = $user_data;
1325                  */
1326                 data_set_t   *ds;
1327                 value_list_t *vl;
1328
1329                 AV *pds = newAV ();
1330                 HV *pvl = newHV ();
1331
1332                 ds   = va_arg (ap, data_set_t *);
1333                 vl   = va_arg (ap, value_list_t *);
1334                 meta = va_arg (ap, notification_meta_t **);
1335
1336                 if (0 != data_set2av (aTHX_ ds, pds)) {
1337                         av_clear (pds);
1338                         av_undef (pds);
1339                         pds = (AV *)&PL_sv_undef;
1340                         ret = -1;
1341                 }
1342
1343                 if (0 != value_list2hv (aTHX_ vl, ds, pvl)) {
1344                         hv_clear (pvl);
1345                         hv_undef (pvl);
1346                         pvl = (HV *)&PL_sv_undef;
1347                         ret = -1;
1348                 }
1349
1350                 if (NULL != meta) {
1351                         pmeta = newAV ();
1352
1353                         if (0 != notification_meta2av (aTHX_ *meta, pmeta)) {
1354                                 av_clear (pmeta);
1355                                 av_undef (pmeta);
1356                                 pmeta = (AV *)&PL_sv_undef;
1357                                 ret = -1;
1358                         }
1359                 }
1360                 else {
1361                         pmeta = (AV *)&PL_sv_undef;
1362                 }
1363
1364                 XPUSHs (sv_2mortal (newRV_noinc ((SV *)pds)));
1365                 XPUSHs (sv_2mortal (newRV_noinc ((SV *)pvl)));
1366                 XPUSHs (sv_2mortal (newRV_noinc ((SV *)pmeta)));
1367         }
1368
1369         XPUSHs (sv_2mortal (newRV_inc (data->user_data)));
1370
1371         PUTBACK;
1372
1373         retvals = call_pv ("Collectd::fc_call", G_SCALAR);
1374
1375         if ((FC_CB_EXEC == cb_type) && (meta != NULL)) {
1376                 assert (pmeta != NULL);
1377
1378                 plugin_notification_meta_free (*meta);
1379                 av2notification_meta (aTHX_ pmeta, meta);
1380         }
1381
1382         SPAGAIN;
1383         if (0 < retvals) {
1384                 SV *tmp = POPs;
1385
1386                 /* the exec callbacks return a status, while
1387                  * the others return a boolean value */
1388                 if (FC_CB_EXEC == cb_type)
1389                         ret = SvIV (tmp);
1390                 else if (! SvTRUE (tmp))
1391                         ret = -1;
1392         }
1393
1394         PUTBACK;
1395         FREETMPS;
1396         LEAVE;
1397
1398         va_end (ap);
1399         return ret;
1400 } /* static int fc_call (int, int, pfc_user_data_t *, ...) */
1401
1402 static int fc_create (int type, const oconfig_item_t *ci, void **user_data)
1403 {
1404         pfc_user_data_t *data;
1405
1406         int ret = 0;
1407
1408         dTHX;
1409
1410         if (NULL == perl_threads)
1411                 return 0;
1412
1413         if (NULL == aTHX) {
1414                 c_ithread_t *t = NULL;
1415
1416                 pthread_mutex_lock (&perl_threads->mutex);
1417                 t = c_ithread_create (perl_threads->head->interp);
1418                 pthread_mutex_unlock (&perl_threads->mutex);
1419
1420                 aTHX = t->interp;
1421         }
1422
1423         log_debug ("fc_create: c_ithread: interp = %p (active threads: %i)",
1424                         aTHX, perl_threads->number_of_threads);
1425
1426         if ((1 != ci->values_num)
1427                         || (OCONFIG_TYPE_STRING != ci->values[0].type)) {
1428                 log_warn ("A \"%s\" block expects a single string argument.",
1429                                 (FC_MATCH == type) ? "Match" : "Target");
1430                 return -1;
1431         }
1432
1433         data = (pfc_user_data_t *)smalloc (sizeof (*data));
1434         data->name      = sstrdup (ci->values[0].value.string);
1435         data->user_data = newSV (0);
1436
1437         ret = fc_call (aTHX_ type, FC_CB_CREATE, data, ci);
1438
1439         if (0 != ret)
1440                 PFC_USER_DATA_FREE (data);
1441         else
1442                 *user_data = data;
1443         return ret;
1444 } /* static int fc_create (int, const oconfig_item_t *, void **) */
1445
1446 static int fc_destroy (int type, void **user_data)
1447 {
1448         pfc_user_data_t *data = *(pfc_user_data_t **)user_data;
1449
1450         int ret = 0;
1451
1452         dTHX;
1453
1454         if ((NULL == perl_threads) || (NULL == data))
1455                 return 0;
1456
1457         if (NULL == aTHX) {
1458                 c_ithread_t *t = NULL;
1459
1460                 pthread_mutex_lock (&perl_threads->mutex);
1461                 t = c_ithread_create (perl_threads->head->interp);
1462                 pthread_mutex_unlock (&perl_threads->mutex);
1463
1464                 aTHX = t->interp;
1465         }
1466
1467         log_debug ("fc_destroy: c_ithread: interp = %p (active threads: %i)",
1468                         aTHX, perl_threads->number_of_threads);
1469
1470         ret = fc_call (aTHX_ type, FC_CB_DESTROY, data);
1471
1472         PFC_USER_DATA_FREE (data);
1473         *user_data = NULL;
1474         return ret;
1475 } /* static int fc_destroy (int, void **) */
1476
1477 static int fc_exec (int type, const data_set_t *ds, const value_list_t *vl,
1478                 notification_meta_t **meta, void **user_data)
1479 {
1480         pfc_user_data_t *data = *(pfc_user_data_t **)user_data;
1481
1482         dTHX;
1483
1484         if (NULL == perl_threads)
1485                 return 0;
1486
1487         assert (NULL != data);
1488
1489         if (NULL == aTHX) {
1490                 c_ithread_t *t = NULL;
1491
1492                 pthread_mutex_lock (&perl_threads->mutex);
1493                 t = c_ithread_create (perl_threads->head->interp);
1494                 pthread_mutex_unlock (&perl_threads->mutex);
1495
1496                 aTHX = t->interp;
1497         }
1498
1499         log_debug ("fc_exec: c_ithread: interp = %p (active threads: %i)",
1500                         aTHX, perl_threads->number_of_threads);
1501
1502         return fc_call (aTHX_ type, FC_CB_EXEC, data, ds, vl, meta);
1503 } /* static int fc_exec (int, const data_set_t *, const value_list_t *,
1504                 notification_meta_t **, void **) */
1505
1506 static int pmatch_create (const oconfig_item_t *ci, void **user_data)
1507 {
1508         return fc_create (FC_MATCH, ci, user_data);
1509 } /* static int pmatch_create (const oconfig_item_t *, void **) */
1510
1511 static int pmatch_destroy (void **user_data)
1512 {
1513         return fc_destroy (FC_MATCH, user_data);
1514 } /* static int pmatch_destroy (void **) */
1515
1516 static int pmatch_match (const data_set_t *ds, const value_list_t *vl,
1517                 notification_meta_t **meta, void **user_data)
1518 {
1519         return fc_exec (FC_MATCH, ds, vl, meta, user_data);
1520 } /* static int pmatch_match (const data_set_t *, const value_list_t *,
1521                 notification_meta_t **, void **) */
1522
1523 static match_proc_t pmatch = {
1524         pmatch_create, pmatch_destroy, pmatch_match
1525 };
1526
1527 static int ptarget_create (const oconfig_item_t *ci, void **user_data)
1528 {
1529         return fc_create (FC_TARGET, ci, user_data);
1530 } /* static int ptarget_create (const oconfig_item_t *, void **) */
1531
1532 static int ptarget_destroy (void **user_data)
1533 {
1534         return fc_destroy (FC_TARGET, user_data);
1535 } /* static int ptarget_destroy (void **) */
1536
1537 static int ptarget_invoke (const data_set_t *ds, value_list_t *vl,
1538                 notification_meta_t **meta, void **user_data)
1539 {
1540         return fc_exec (FC_TARGET, ds, vl, meta, user_data);
1541 } /* static int ptarget_invoke (const data_set_t *, value_list_t *,
1542                 notification_meta_t **, void **) */
1543
1544 static target_proc_t ptarget = {
1545         ptarget_create, ptarget_destroy, ptarget_invoke
1546 };
1547
1548 /*
1549  * Exported Perl API.
1550  */
1551
1552 /*
1553  * Collectd::plugin_register_data_set (type, dataset).
1554  *
1555  * type:
1556  *   type of the dataset
1557  *
1558  * dataset:
1559  *   dataset to be registered
1560  */
1561 static XS (Collectd_plugin_register_ds)
1562 {
1563         SV  *data = NULL;
1564         int ret   = 0;
1565
1566         dXSARGS;
1567
1568         log_warn ("Using plugin_register() to register new data-sets is "
1569                         "deprecated - add new entries to a custom types.db instead.");
1570
1571         if (2 != items) {
1572                 log_err ("Usage: Collectd::plugin_register_data_set(type, dataset)");
1573                 XSRETURN_EMPTY;
1574         }
1575
1576         log_debug ("Collectd::plugin_register_data_set: "
1577                         "type = \"%s\", dataset = \"%s\"",
1578                         SvPV_nolen (ST (0)), SvPV_nolen (ST (1)));
1579
1580         data = ST (1);
1581
1582         if (SvROK (data) && (SVt_PVAV == SvTYPE (SvRV (data)))) {
1583                 ret = pplugin_register_data_set (aTHX_ SvPV_nolen (ST (0)),
1584                                 (AV *)SvRV (data));
1585         }
1586         else {
1587                 log_err ("Collectd::plugin_register_data_set: Invalid data.");
1588                 XSRETURN_EMPTY;
1589         }
1590
1591         if (0 == ret)
1592                 XSRETURN_YES;
1593         else
1594                 XSRETURN_EMPTY;
1595 } /* static XS (Collectd_plugin_register_ds) */
1596
1597 /*
1598  * Collectd::plugin_unregister_data_set (type).
1599  *
1600  * type:
1601  *   type of the dataset
1602  */
1603 static XS (Collectd_plugin_unregister_ds)
1604 {
1605         dXSARGS;
1606
1607         if (1 != items) {
1608                 log_err ("Usage: Collectd::plugin_unregister_data_set(type)");
1609                 XSRETURN_EMPTY;
1610         }
1611
1612         log_debug ("Collectd::plugin_unregister_data_set: type = \"%s\"",
1613                         SvPV_nolen (ST (0)));
1614
1615         if (0 == pplugin_unregister_data_set (SvPV_nolen (ST (0))))
1616                 XSRETURN_YES;
1617         else
1618                 XSRETURN_EMPTY;
1619 } /* static XS (Collectd_plugin_register_ds) */
1620
1621 /*
1622  * Collectd::plugin_dispatch_values (name, values).
1623  *
1624  * name:
1625  *   name of the plugin
1626  *
1627  * values:
1628  *   value list to submit
1629  */
1630 static XS (Collectd_plugin_dispatch_values)
1631 {
1632         SV *values     = NULL;
1633
1634         int ret = 0;
1635
1636         dXSARGS;
1637
1638         if (1 != items) {
1639                 log_err ("Usage: Collectd::plugin_dispatch_values(values)");
1640                 XSRETURN_EMPTY;
1641         }
1642
1643         log_debug ("Collectd::plugin_dispatch_values: values=\"%s\"",
1644                         SvPV_nolen (ST (/* stack index = */ 0)));
1645
1646         values = ST (/* stack index = */ 0);
1647
1648         /* Make sure the argument is a hash reference. */
1649         if (! (SvROK (values) && (SVt_PVHV == SvTYPE (SvRV (values))))) {
1650                 log_err ("Collectd::plugin_dispatch_values: Invalid values.");
1651                 XSRETURN_EMPTY;
1652         }
1653
1654         if (NULL == values)
1655                 XSRETURN_EMPTY;
1656
1657         ret = pplugin_dispatch_values (aTHX_ (HV *)SvRV (values));
1658
1659         if (0 == ret)
1660                 XSRETURN_YES;
1661         else
1662                 XSRETURN_EMPTY;
1663 } /* static XS (Collectd_plugin_dispatch_values) */
1664
1665 /* Collectd::plugin_write (plugin, ds, vl).
1666  *
1667  * plugin:
1668  *   name of the plugin to call, may be 'undef'
1669  *
1670  * ds:
1671  *   data-set that describes the submitted values, may be 'undef'
1672  *
1673  * vl:
1674  *   value-list to be written
1675  */
1676 static XS (Collectd__plugin_write)
1677 {
1678         char *plugin;
1679         SV   *ds, *vl;
1680         AV   *ds_array;
1681
1682         int ret;
1683
1684         dXSARGS;
1685
1686         if (3 != items) {
1687                 log_err ("Usage: Collectd::plugin_write(plugin, ds, vl)");
1688                 XSRETURN_EMPTY;
1689         }
1690
1691         log_debug ("Collectd::plugin_write: plugin=\"%s\", ds=\"%s\", vl=\"%s\"",
1692                         SvPV_nolen (ST (0)), SvOK (ST (1)) ? SvPV_nolen (ST (1)) : "",
1693                         SvPV_nolen (ST (2)));
1694
1695         if (! SvOK (ST (0)))
1696                 plugin = NULL;
1697         else
1698                 plugin = SvPV_nolen (ST (0));
1699
1700         ds = ST (1);
1701         if (SvROK (ds) && (SVt_PVAV == SvTYPE (SvRV (ds))))
1702                 ds_array = (AV *)SvRV (ds);
1703         else if (! SvOK (ds))
1704                 ds_array = NULL;
1705         else {
1706                 log_err ("Collectd::plugin_write: Invalid data-set.");
1707                 XSRETURN_EMPTY;
1708         }
1709
1710         vl = ST (2);
1711         if (! (SvROK (vl) && (SVt_PVHV == SvTYPE (SvRV (vl))))) {
1712                 log_err ("Collectd::plugin_write: Invalid value-list.");
1713                 XSRETURN_EMPTY;
1714         }
1715
1716         ret = pplugin_write (aTHX_ plugin, ds_array, (HV *)SvRV (vl));
1717
1718         if (0 == ret)
1719                 XSRETURN_YES;
1720         else
1721                 XSRETURN_EMPTY;
1722 } /* static XS (Collectd__plugin_write) */
1723
1724 /*
1725  * Collectd::_plugin_flush (plugin, timeout, identifier).
1726  *
1727  * plugin:
1728  *   name of the plugin to flush
1729  *
1730  * timeout:
1731  *   timeout to use when flushing the data
1732  *
1733  * identifier:
1734  *   data-set identifier to flush
1735  */
1736 static XS (Collectd__plugin_flush)
1737 {
1738         char *plugin  = NULL;
1739         int   timeout = -1;
1740         char *id      = NULL;
1741
1742         dXSARGS;
1743
1744         if (3 != items) {
1745                 log_err ("Usage: Collectd::_plugin_flush(plugin, timeout, id)");
1746                 XSRETURN_EMPTY;
1747         }
1748
1749         if (SvOK (ST (0)))
1750                 plugin = SvPV_nolen (ST (0));
1751
1752         if (SvOK (ST (1)))
1753                 timeout = (int)SvIV (ST (1));
1754
1755         if (SvOK (ST (2)))
1756                 id = SvPV_nolen (ST (2));
1757
1758         log_debug ("Collectd::_plugin_flush: plugin = \"%s\", timeout = %i, "
1759                         "id = \"%s\"", plugin, timeout, id);
1760
1761         if (0 == plugin_flush (plugin, timeout, id))
1762                 XSRETURN_YES;
1763         else
1764                 XSRETURN_EMPTY;
1765 } /* static XS (Collectd__plugin_flush) */
1766
1767 /*
1768  * Collectd::plugin_dispatch_notification (notif).
1769  *
1770  * notif:
1771  *   notification to dispatch
1772  */
1773 static XS (Collectd_plugin_dispatch_notification)
1774 {
1775         SV *notif = NULL;
1776
1777         int ret = 0;
1778
1779         dXSARGS;
1780
1781         if (1 != items) {
1782                 log_err ("Usage: Collectd::plugin_dispatch_notification(notif)");
1783                 XSRETURN_EMPTY;
1784         }
1785
1786         log_debug ("Collectd::plugin_dispatch_notification: notif = \"%s\"",
1787                         SvPV_nolen (ST (0)));
1788
1789         notif = ST (0);
1790
1791         if (! (SvROK (notif) && (SVt_PVHV == SvTYPE (SvRV (notif))))) {
1792                 log_err ("Collectd::plugin_dispatch_notification: Invalid notif.");
1793                 XSRETURN_EMPTY;
1794         }
1795
1796         ret = pplugin_dispatch_notification (aTHX_ (HV *)SvRV (notif));
1797
1798         if (0 == ret)
1799                 XSRETURN_YES;
1800         else
1801                 XSRETURN_EMPTY;
1802 } /* static XS (Collectd_plugin_dispatch_notification) */
1803
1804 /*
1805  * Collectd::plugin_log (level, message).
1806  *
1807  * level:
1808  *   log level (LOG_DEBUG, ... LOG_ERR)
1809  *
1810  * message:
1811  *   log message
1812  */
1813 static XS (Collectd_plugin_log)
1814 {
1815         dXSARGS;
1816
1817         if (2 != items) {
1818                 log_err ("Usage: Collectd::plugin_log(level, message)");
1819                 XSRETURN_EMPTY;
1820         }
1821
1822         plugin_log (SvIV (ST (0)), "%s", SvPV_nolen (ST (1)));
1823         XSRETURN_YES;
1824 } /* static XS (Collectd_plugin_log) */
1825
1826 /*
1827  * Collectd::_fc_register (type, name)
1828  *
1829  * type:
1830  *   match | target
1831  *
1832  * name:
1833  *   name of the match
1834  */
1835 static XS (Collectd__fc_register)
1836 {
1837         int   type;
1838         char *name;
1839
1840         int ret = 0;
1841
1842         dXSARGS;
1843
1844         if (2 != items) {
1845                 log_err ("Usage: Collectd::_fc_register(type, name)");
1846                 XSRETURN_EMPTY;
1847         }
1848
1849         type = SvIV (ST (0));
1850         name = SvPV_nolen (ST (1));
1851
1852         if (FC_MATCH == type)
1853                 ret = fc_register_match (name, pmatch);
1854         else if (FC_TARGET == type)
1855                 ret = fc_register_target (name, ptarget);
1856
1857         if (0 == ret)
1858                 XSRETURN_YES;
1859         else
1860                 XSRETURN_EMPTY;
1861 } /* static XS (Collectd_fc_register) */
1862
1863 /*
1864  * Collectd::call_by_name (...).
1865  *
1866  * Call a Perl sub identified by its name passed through $Collectd::cb_name.
1867  */
1868 static XS (Collectd_call_by_name)
1869 {
1870         SV   *tmp  = NULL;
1871         char *name = NULL;
1872
1873         if (NULL == (tmp = get_sv ("Collectd::cb_name", 0))) {
1874                 sv_setpv (get_sv ("@", 1), "cb_name has not been set");
1875                 CLEAR_STACK_FRAME;
1876                 return;
1877         }
1878
1879         name = SvPV_nolen (tmp);
1880
1881         if (NULL == get_cv (name, 0)) {
1882                 sv_setpvf (get_sv ("@", 1), "unknown callback \"%s\"", name);
1883                 CLEAR_STACK_FRAME;
1884                 return;
1885         }
1886
1887         /* simply pass on the subroutine call without touching the stack,
1888          * thus leaving any arguments and return values in place */
1889         call_pv (name, 0);
1890 } /* static XS (Collectd_call_by_name) */
1891
1892 /*
1893  * Interface to collectd.
1894  */
1895
1896 static int perl_init (void)
1897 {
1898         dTHX;
1899
1900         if (NULL == perl_threads)
1901                 return 0;
1902
1903         if (NULL == aTHX) {
1904                 c_ithread_t *t = NULL;
1905
1906                 pthread_mutex_lock (&perl_threads->mutex);
1907                 t = c_ithread_create (perl_threads->head->interp);
1908                 pthread_mutex_unlock (&perl_threads->mutex);
1909
1910                 aTHX = t->interp;
1911         }
1912
1913         log_debug ("perl_init: c_ithread: interp = %p (active threads: %i)",
1914                         aTHX, perl_threads->number_of_threads);
1915         return pplugin_call_all (aTHX_ PLUGIN_INIT);
1916 } /* static int perl_init (void) */
1917
1918 static int perl_read (void)
1919 {
1920         dTHX;
1921
1922         if (NULL == perl_threads)
1923                 return 0;
1924
1925         if (NULL == aTHX) {
1926                 c_ithread_t *t = NULL;
1927
1928                 pthread_mutex_lock (&perl_threads->mutex);
1929                 t = c_ithread_create (perl_threads->head->interp);
1930                 pthread_mutex_unlock (&perl_threads->mutex);
1931
1932                 aTHX = t->interp;
1933         }
1934
1935         log_debug ("perl_read: c_ithread: interp = %p (active threads: %i)",
1936                         aTHX, perl_threads->number_of_threads);
1937         return pplugin_call_all (aTHX_ PLUGIN_READ);
1938 } /* static int perl_read (void) */
1939
1940 static int perl_write (const data_set_t *ds, const value_list_t *vl,
1941                 user_data_t __attribute__((unused)) *user_data)
1942 {
1943         dTHX;
1944
1945         if (NULL == perl_threads)
1946                 return 0;
1947
1948         if (NULL == aTHX) {
1949                 c_ithread_t *t = NULL;
1950
1951                 pthread_mutex_lock (&perl_threads->mutex);
1952                 t = c_ithread_create (perl_threads->head->interp);
1953                 pthread_mutex_unlock (&perl_threads->mutex);
1954
1955                 aTHX = t->interp;
1956         }
1957
1958         log_debug ("perl_write: c_ithread: interp = %p (active threads: %i)",
1959                         aTHX, perl_threads->number_of_threads);
1960         return pplugin_call_all (aTHX_ PLUGIN_WRITE, ds, vl);
1961 } /* static int perl_write (const data_set_t *, const value_list_t *) */
1962
1963 static void perl_log (int level, const char *msg,
1964                 user_data_t __attribute__((unused)) *user_data)
1965 {
1966         dTHX;
1967
1968         if (NULL == perl_threads)
1969                 return;
1970
1971         if (NULL == aTHX) {
1972                 c_ithread_t *t = NULL;
1973
1974                 pthread_mutex_lock (&perl_threads->mutex);
1975                 t = c_ithread_create (perl_threads->head->interp);
1976                 pthread_mutex_unlock (&perl_threads->mutex);
1977
1978                 aTHX = t->interp;
1979         }
1980
1981         pplugin_call_all (aTHX_ PLUGIN_LOG, level, msg);
1982         return;
1983 } /* static void perl_log (int, const char *) */
1984
1985 static int perl_notify (const notification_t *notif,
1986                 user_data_t __attribute__((unused)) *user_data)
1987 {
1988         dTHX;
1989
1990         if (NULL == perl_threads)
1991                 return 0;
1992
1993         if (NULL == aTHX) {
1994                 c_ithread_t *t = NULL;
1995
1996                 pthread_mutex_lock (&perl_threads->mutex);
1997                 t = c_ithread_create (perl_threads->head->interp);
1998                 pthread_mutex_unlock (&perl_threads->mutex);
1999
2000                 aTHX = t->interp;
2001         }
2002         return pplugin_call_all (aTHX_ PLUGIN_NOTIF, notif);
2003 } /* static int perl_notify (const notification_t *) */
2004
2005 static int perl_flush (cdtime_t timeout, const char *identifier,
2006                 user_data_t __attribute__((unused)) *user_data)
2007 {
2008         dTHX;
2009
2010         if (NULL == perl_threads)
2011                 return 0;
2012
2013         if (NULL == aTHX) {
2014                 c_ithread_t *t = NULL;
2015
2016                 pthread_mutex_lock (&perl_threads->mutex);
2017                 t = c_ithread_create (perl_threads->head->interp);
2018                 pthread_mutex_unlock (&perl_threads->mutex);
2019
2020                 aTHX = t->interp;
2021         }
2022         return pplugin_call_all (aTHX_ PLUGIN_FLUSH, timeout, identifier);
2023 } /* static int perl_flush (const int) */
2024
2025 static int perl_shutdown (void)
2026 {
2027         c_ithread_t *t = NULL;
2028
2029         int ret = 0;
2030
2031         dTHX;
2032
2033         plugin_unregister_complex_config ("perl");
2034
2035         if (NULL == perl_threads)
2036                 return 0;
2037
2038         if (NULL == aTHX) {
2039                 c_ithread_t *t = NULL;
2040
2041                 pthread_mutex_lock (&perl_threads->mutex);
2042                 t = c_ithread_create (perl_threads->head->interp);
2043                 pthread_mutex_unlock (&perl_threads->mutex);
2044
2045                 aTHX = t->interp;
2046         }
2047
2048         log_debug ("perl_shutdown: c_ithread: interp = %p (active threads: %i)",
2049                         aTHX, perl_threads->number_of_threads);
2050
2051         plugin_unregister_log ("perl");
2052         plugin_unregister_notification ("perl");
2053         plugin_unregister_init ("perl");
2054         plugin_unregister_read ("perl");
2055         plugin_unregister_write ("perl");
2056         plugin_unregister_flush ("perl");
2057
2058         ret = pplugin_call_all (aTHX_ PLUGIN_SHUTDOWN);
2059
2060         pthread_mutex_lock (&perl_threads->mutex);
2061         t = perl_threads->tail;
2062
2063         while (NULL != t) {
2064                 c_ithread_t *thr = t;
2065
2066                 /* the pointer has to be advanced before destroying
2067                  * the thread as this will free the memory */
2068                 t = t->prev;
2069
2070                 c_ithread_destroy (thr);
2071         }
2072
2073         pthread_mutex_unlock (&perl_threads->mutex);
2074         pthread_mutex_destroy (&perl_threads->mutex);
2075
2076         sfree (perl_threads);
2077
2078         pthread_key_delete (perl_thr_key);
2079
2080         PERL_SYS_TERM ();
2081
2082         plugin_unregister_shutdown ("perl");
2083         return ret;
2084 } /* static void perl_shutdown (void) */
2085
2086 /*
2087  * Access functions for global variables.
2088  *
2089  * These functions implement the "magic" used to access
2090  * the global variables from Perl.
2091  */
2092
2093 static int g_pv_get (pTHX_ SV *var, MAGIC *mg)
2094 {
2095         char *pv = mg->mg_ptr;
2096         sv_setpv (var, pv);
2097         return 0;
2098 } /* static int g_pv_get (pTHX_ SV *, MAGIC *) */
2099
2100 static int g_pv_set (pTHX_ SV *var, MAGIC *mg)
2101 {
2102         char *pv = mg->mg_ptr;
2103         sstrncpy (pv, SvPV_nolen (var), DATA_MAX_NAME_LEN);
2104         return 0;
2105 } /* static int g_pv_set (pTHX_ SV *, MAGIC *) */
2106
2107 static int g_iv_get (pTHX_ SV *var, MAGIC *mg)
2108 {
2109         int *iv = (int *)mg->mg_ptr;
2110         sv_setiv (var, *iv);
2111         return 0;
2112 } /* static int g_iv_get (pTHX_ SV *, MAGIC *) */
2113
2114 static int g_iv_set (pTHX_ SV *var, MAGIC *mg)
2115 {
2116         int *iv = (int *)mg->mg_ptr;
2117         *iv = (int)SvIV (var);
2118         return 0;
2119 } /* static int g_iv_set (pTHX_ SV *, MAGIC *) */
2120
2121 static MGVTBL g_pv_vtbl = {
2122         g_pv_get, g_pv_set, NULL, NULL, NULL, NULL, NULL
2123 #if HAVE_PERL_STRUCT_MGVTBL_SVT_LOCAL
2124                 , NULL
2125 #endif
2126 };
2127 static MGVTBL g_iv_vtbl = {
2128         g_iv_get, g_iv_set, NULL, NULL, NULL, NULL, NULL
2129 #if HAVE_PERL_STRUCT_MGVTBL_SVT_LOCAL
2130                 , NULL
2131 #endif
2132 };
2133
2134 /* bootstrap the Collectd module */
2135 static void xs_init (pTHX)
2136 {
2137         HV   *stash = NULL;
2138         SV   *tmp   = NULL;
2139         char *file  = __FILE__;
2140
2141         int i = 0;
2142
2143         dXSUB_SYS;
2144
2145         /* enable usage of Perl modules using shared libraries */
2146         newXS ("DynaLoader::boot_DynaLoader", boot_DynaLoader, file);
2147
2148         /* register API */
2149         for (i = 0; NULL != api[i].f; ++i)
2150                 newXS (api[i].name, api[i].f, file);
2151
2152         stash = gv_stashpv ("Collectd", 1);
2153
2154         /* export "constants" */
2155         for (i = 0; '\0' != constants[i].name[0]; ++i)
2156                 newCONSTSUB (stash, constants[i].name, newSViv (constants[i].value));
2157
2158         /* export global variables
2159          * by adding "magic" to the SV's representing the globale variables
2160          * perl is able to automagically call the get/set function when
2161          * accessing any such variable (this is basically the same as using
2162          * tie() in Perl) */
2163         /* global strings */
2164         for (i = 0; '\0' != g_strings[i].name[0]; ++i) {
2165                 tmp = get_sv (g_strings[i].name, 1);
2166                 sv_magicext (tmp, NULL, PERL_MAGIC_ext, &g_pv_vtbl,
2167                                 g_strings[i].var, 0);
2168         }
2169
2170         /* global integers */
2171         for (i = 0; '\0' != g_integers[i].name[0]; ++i) {
2172                 tmp = get_sv (g_integers[i].name, 1);
2173                 sv_magicext (tmp, NULL, PERL_MAGIC_ext, &g_iv_vtbl,
2174                                 (char *)g_integers[i].var, 0);
2175         }
2176         return;
2177 } /* static void xs_init (pTHX) */
2178
2179 /* Initialize the global Perl interpreter. */
2180 static int init_pi (int argc, char **argv)
2181 {
2182         dTHXa (NULL);
2183
2184         if (NULL != perl_threads)
2185                 return 0;
2186
2187         log_info ("Initializing Perl interpreter...");
2188 #if COLLECT_DEBUG
2189         {
2190                 int i = 0;
2191
2192                 for (i = 0; i < argc; ++i)
2193                         log_debug ("argv[%i] = \"%s\"", i, argv[i]);
2194         }
2195 #endif /* COLLECT_DEBUG */
2196
2197         if (0 != pthread_key_create (&perl_thr_key, c_ithread_destructor)) {
2198                 log_err ("init_pi: pthread_key_create failed");
2199
2200                 /* this must not happen - cowardly giving up if it does */
2201                 return -1;
2202         }
2203
2204 #ifdef __FreeBSD__
2205         /* On FreeBSD, PERL_SYS_INIT3 expands to some expression which
2206          * triggers a "value computed is not used" warning by gcc. */
2207         (void)
2208 #endif
2209         PERL_SYS_INIT3 (&argc, &argv, &environ);
2210
2211         perl_threads = (c_ithread_list_t *)smalloc (sizeof (c_ithread_list_t));
2212         memset (perl_threads, 0, sizeof (c_ithread_list_t));
2213
2214         pthread_mutex_init (&perl_threads->mutex, NULL);
2215         /* locking the mutex should not be necessary at this point
2216          * but let's just do it for the sake of completeness */
2217         pthread_mutex_lock (&perl_threads->mutex);
2218
2219         perl_threads->head = c_ithread_create (NULL);
2220         perl_threads->tail = perl_threads->head;
2221
2222         if (NULL == (perl_threads->head->interp = perl_alloc ())) {
2223                 log_err ("init_pi: Not enough memory.");
2224                 exit (3);
2225         }
2226
2227         aTHX = perl_threads->head->interp;
2228         pthread_mutex_unlock (&perl_threads->mutex);
2229
2230         perl_construct (aTHX);
2231
2232         PL_exit_flags |= PERL_EXIT_DESTRUCT_END;
2233
2234         if (0 != perl_parse (aTHX_ xs_init, argc, argv, NULL)) {
2235                 SV *err = get_sv ("@", 1);
2236                 log_err ("init_pi: Unable to bootstrap Collectd: %s",
2237                                 SvPV_nolen (err));
2238
2239                 perl_destruct (perl_threads->head->interp);
2240                 perl_free (perl_threads->head->interp);
2241                 sfree (perl_threads);
2242
2243                 pthread_key_delete (perl_thr_key);
2244                 return -1;
2245         }
2246
2247         /* Set $0 to "collectd" because perl_parse() has to set it to "-e". */
2248         sv_setpv (get_sv ("0", 0), "collectd");
2249
2250         perl_run (aTHX);
2251
2252         plugin_register_log ("perl", perl_log, /* user_data = */ NULL);
2253         plugin_register_notification ("perl", perl_notify,
2254                         /* user_data = */ NULL);
2255         plugin_register_init ("perl", perl_init);
2256
2257         plugin_register_read ("perl", perl_read);
2258
2259         plugin_register_write ("perl", perl_write, /* user_data = */ NULL);
2260         plugin_register_flush ("perl", perl_flush, /* user_data = */ NULL);
2261         plugin_register_shutdown ("perl", perl_shutdown);
2262         return 0;
2263 } /* static int init_pi (const char **, const int) */
2264
2265 /*
2266  * LoadPlugin "<Plugin>"
2267  */
2268 static int perl_config_loadplugin (pTHX_ oconfig_item_t *ci)
2269 {
2270         char module_name[DATA_MAX_NAME_LEN];
2271
2272         char *value = NULL;
2273
2274         if ((0 != ci->children_num) || (1 != ci->values_num)
2275                         || (OCONFIG_TYPE_STRING != ci->values[0].type)) {
2276                 log_err ("LoadPlugin expects a single string argument.");
2277                 return 1;
2278         }
2279
2280         value = ci->values[0].value.string;
2281
2282         if (NULL == get_module_name (module_name, sizeof (module_name), value)) {
2283                 log_err ("Invalid module name %s", value);
2284                 return (1);
2285         }
2286
2287         if (0 != init_pi (perl_argc, perl_argv))
2288                 return -1;
2289
2290         assert (NULL != perl_threads);
2291         assert (NULL != perl_threads->head);
2292
2293         aTHX = perl_threads->head->interp;
2294
2295         log_debug ("perl_config: loading perl plugin \"%s\"", value);
2296         load_module (PERL_LOADMOD_NOIMPORT,
2297                         newSVpv (module_name, strlen (module_name)), Nullsv);
2298         return 0;
2299 } /* static int perl_config_loadplugin (oconfig_item_it *) */
2300
2301 /*
2302  * BaseName "<Name>"
2303  */
2304 static int perl_config_basename (pTHX_ oconfig_item_t *ci)
2305 {
2306         char *value = NULL;
2307
2308         if ((0 != ci->children_num) || (1 != ci->values_num)
2309                         || (OCONFIG_TYPE_STRING != ci->values[0].type)) {
2310                 log_err ("BaseName expects a single string argument.");
2311                 return 1;
2312         }
2313
2314         value = ci->values[0].value.string;
2315
2316         log_debug ("perl_config: Setting plugin basename to \"%s\"", value);
2317         sstrncpy (base_name, value, sizeof (base_name));
2318         return 0;
2319 } /* static int perl_config_basename (oconfig_item_it *) */
2320
2321 /*
2322  * EnableDebugger "<Package>"|""
2323  */
2324 static int perl_config_enabledebugger (pTHX_ oconfig_item_t *ci)
2325 {
2326         char *value = NULL;
2327
2328         if ((0 != ci->children_num) || (1 != ci->values_num)
2329                         || (OCONFIG_TYPE_STRING != ci->values[0].type)) {
2330                 log_err ("EnableDebugger expects a single string argument.");
2331                 return 1;
2332         }
2333
2334         if (NULL != perl_threads) {
2335                 log_warn ("EnableDebugger has no effects if used after LoadPlugin.");
2336                 return 1;
2337         }
2338
2339         value = ci->values[0].value.string;
2340
2341         perl_argv = (char **)realloc (perl_argv,
2342                         (++perl_argc + 1) * sizeof (char *));
2343
2344         if (NULL == perl_argv) {
2345                 log_err ("perl_config: Not enough memory.");
2346                 exit (3);
2347         }
2348
2349         if ('\0' == value[0]) {
2350                 perl_argv[perl_argc - 1] = "-d";
2351         }
2352         else {
2353                 perl_argv[perl_argc - 1] = (char *)smalloc (strlen (value) + 4);
2354                 sstrncpy (perl_argv[perl_argc - 1], "-d:", 4);
2355                 sstrncpy (perl_argv[perl_argc - 1] + 3, value, strlen (value) + 1);
2356         }
2357
2358         perl_argv[perl_argc] = NULL;
2359         return 0;
2360 } /* static int perl_config_enabledebugger (oconfig_item_it *) */
2361
2362 /*
2363  * IncludeDir "<Dir>"
2364  */
2365 static int perl_config_includedir (pTHX_ oconfig_item_t *ci)
2366 {
2367         char *value = NULL;
2368
2369         if ((0 != ci->children_num) || (1 != ci->values_num)
2370                         || (OCONFIG_TYPE_STRING != ci->values[0].type)) {
2371                 log_err ("IncludeDir expects a single string argument.");
2372                 return 1;
2373         }
2374
2375         value = ci->values[0].value.string;
2376
2377         if (NULL == aTHX) {
2378                 perl_argv = (char **)realloc (perl_argv,
2379                                 (++perl_argc + 1) * sizeof (char *));
2380
2381                 if (NULL == perl_argv) {
2382                         log_err ("perl_config: Not enough memory.");
2383                         exit (3);
2384                 }
2385
2386                 perl_argv[perl_argc - 1] = (char *)smalloc (strlen (value) + 3);
2387                 sstrncpy(perl_argv[perl_argc - 1], "-I", 3);
2388                 sstrncpy(perl_argv[perl_argc - 1] + 2, value, strlen (value) + 1);
2389
2390                 perl_argv[perl_argc] = NULL;
2391         }
2392         else {
2393                 /* prepend the directory to @INC */
2394                 av_unshift (GvAVn (PL_incgv), 1);
2395                 av_store (GvAVn (PL_incgv), 0, newSVpv (value, strlen (value)));
2396         }
2397         return 0;
2398 } /* static int perl_config_includedir (oconfig_item_it *) */
2399
2400 /*
2401  * <Plugin> block
2402  */
2403 static int perl_config_plugin (pTHX_ oconfig_item_t *ci)
2404 {
2405         int retvals = 0;
2406         int ret     = 0;
2407
2408         char *plugin;
2409         HV   *config;
2410
2411         dSP;
2412
2413         if ((1 != ci->values_num) || (OCONFIG_TYPE_STRING != ci->values[0].type)) {
2414                 log_err ("LoadPlugin expects a single string argument.");
2415                 return 1;
2416         }
2417
2418         plugin = ci->values[0].value.string;
2419         config = newHV ();
2420
2421         if (0 != oconfig_item2hv (aTHX_ ci, config)) {
2422                 hv_clear (config);
2423                 hv_undef (config);
2424
2425                 log_err ("Unable to convert configuration to a Perl hash value.");
2426                 config = (HV *)&PL_sv_undef;
2427         }
2428
2429         ENTER;
2430         SAVETMPS;
2431
2432         PUSHMARK (SP);
2433
2434         XPUSHs (sv_2mortal (newSVpv (plugin, 0)));
2435         XPUSHs (sv_2mortal (newRV_noinc ((SV *)config)));
2436
2437         PUTBACK;
2438
2439         retvals = call_pv ("Collectd::_plugin_dispatch_config", G_SCALAR);
2440
2441         SPAGAIN;
2442         if (0 < retvals) {
2443                 SV *tmp = POPs;
2444                 if (! SvTRUE (tmp))
2445                         ret = 1;
2446         }
2447         else
2448                 ret = 1;
2449
2450         PUTBACK;
2451         FREETMPS;
2452         LEAVE;
2453         return ret;
2454 } /* static int perl_config_plugin (oconfig_item_it *) */
2455
2456 static int perl_config (oconfig_item_t *ci)
2457 {
2458         int status = 0;
2459         int i = 0;
2460
2461         dTHXa (NULL);
2462
2463         for (i = 0; i < ci->children_num; ++i) {
2464                 oconfig_item_t *c = ci->children + i;
2465                 int current_status = 0;
2466
2467                 if (NULL != perl_threads)
2468                         aTHX = PERL_GET_CONTEXT;
2469
2470                 if (0 == strcasecmp (c->key, "LoadPlugin"))
2471                         current_status = perl_config_loadplugin (aTHX_ c);
2472                 else if (0 == strcasecmp (c->key, "BaseName"))
2473                         current_status = perl_config_basename (aTHX_ c);
2474                 else if (0 == strcasecmp (c->key, "EnableDebugger"))
2475                         current_status = perl_config_enabledebugger (aTHX_ c);
2476                 else if (0 == strcasecmp (c->key, "IncludeDir"))
2477                         current_status = perl_config_includedir (aTHX_ c);
2478                 else if (0 == strcasecmp (c->key, "Plugin"))
2479                         current_status = perl_config_plugin (aTHX_ c);
2480                 else
2481                 {
2482                         log_warn ("Ignoring unknown config key \"%s\".", c->key);
2483                         current_status = 0;
2484                 }
2485
2486                 /* fatal error - it's up to perl_config_* to clean up */
2487                 if (0 > current_status) {
2488                         log_err ("Configuration failed with a fatal error - "
2489                                         "plugin disabled!");
2490                         return current_status;
2491                 }
2492
2493                 status += current_status;
2494         }
2495         return status;
2496 } /* static int perl_config (oconfig_item_t *) */
2497
2498 void module_register (void)
2499 {
2500         perl_argc = 4;
2501         perl_argv = (char **)smalloc ((perl_argc + 1) * sizeof (char *));
2502
2503         /* default options for the Perl interpreter */
2504         perl_argv[0] = "";
2505         perl_argv[1] = "-MCollectd";
2506         perl_argv[2] = "-e";
2507         perl_argv[3] = "1";
2508         perl_argv[4] = NULL;
2509
2510         plugin_register_complex_config ("perl", perl_config);
2511         return;
2512 } /* void module_register (void) */
2513
2514 /* vim: set sw=4 ts=4 tw=78 noexpandtab : */
2515