perl plugin: #undef macros before poisoning them.
[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 # undef sprintf
45 # pragma GCC poison sprintf
46 #endif
47
48 #include <XSUB.h>
49
50 /* Some versions of Perl define their own version of DEBUG... :-/ */
51 #ifdef DEBUG
52 # undef DEBUG
53 #endif /* DEBUG */
54
55 /* ... while we want the definition found in plugin.h. */
56 #include "plugin.h"
57 #include "common.h"
58
59 #include "filter_chain.h"
60
61 #include <pthread.h>
62
63 #if !defined(USE_ITHREADS)
64 # error "Perl does not support ithreads!"
65 #endif /* !defined(USE_ITHREADS) */
66
67 /* clear the Perl sub's stack frame
68  * (this should only be used inside an XSUB) */
69 #define CLEAR_STACK_FRAME PL_stack_sp = PL_stack_base + *PL_markstack_ptr
70
71 #define PLUGIN_INIT     0
72 #define PLUGIN_READ     1
73 #define PLUGIN_WRITE    2
74 #define PLUGIN_SHUTDOWN 3
75 #define PLUGIN_LOG      4
76 #define PLUGIN_NOTIF    5
77 #define PLUGIN_FLUSH    6
78
79 #define PLUGIN_TYPES    7
80
81 #define PLUGIN_CONFIG   254
82 #define PLUGIN_DATASET  255
83
84 #define FC_MATCH  0
85 #define FC_TARGET 1
86
87 #define FC_TYPES  2
88
89 #define FC_CB_CREATE  0
90 #define FC_CB_DESTROY 1
91 #define FC_CB_EXEC    2
92
93 #define FC_CB_TYPES   3
94
95 #define log_debug(...) DEBUG ("perl: " __VA_ARGS__)
96 #define log_info(...) INFO ("perl: " __VA_ARGS__)
97 #define log_warn(...) WARNING ("perl: " __VA_ARGS__)
98 #define log_err(...) ERROR ("perl: " __VA_ARGS__)
99
100 /* this is defined in DynaLoader.a */
101 void boot_DynaLoader (PerlInterpreter *, CV *);
102
103 static XS (Collectd_plugin_register_ds);
104 static XS (Collectd_plugin_unregister_ds);
105 static XS (Collectd_plugin_dispatch_values);
106 static XS (Collectd_plugin_get_interval);
107 static XS (Collectd__plugin_write);
108 static XS (Collectd__plugin_flush);
109 static XS (Collectd_plugin_dispatch_notification);
110 static XS (Collectd_plugin_log);
111 static XS (Collectd__fc_register);
112 static XS (Collectd_call_by_name);
113
114 /*
115  * private data types
116  */
117
118 typedef struct c_ithread_s {
119         /* the thread's Perl interpreter */
120         PerlInterpreter *interp;
121
122         /* double linked list of threads */
123         struct c_ithread_s *prev;
124         struct c_ithread_s *next;
125 } c_ithread_t;
126
127 typedef struct {
128         c_ithread_t *head;
129         c_ithread_t *tail;
130
131 #if COLLECT_DEBUG
132         /* some usage stats */
133         int number_of_threads;
134 #endif /* COLLECT_DEBUG */
135
136         pthread_mutex_t mutex;
137 } c_ithread_list_t;
138
139 /* name / user_data for Perl matches / targets */
140 typedef struct {
141         char *name;
142         SV   *user_data;
143 } pfc_user_data_t;
144
145 #define PFC_USER_DATA_FREE(data) \
146         do { \
147                 sfree ((data)->name); \
148                 if (NULL != (data)->user_data) \
149                         sv_free ((data)->user_data); \
150                 sfree (data); \
151         } while (0)
152
153 /*
154  * Public variable
155  */
156 extern char **environ;
157
158 /*
159  * private variables
160  */
161
162 /* if perl_threads != NULL perl_threads->head must
163  * point to the "base" thread */
164 static c_ithread_list_t *perl_threads = NULL;
165
166 /* the key used to store each pthread's ithread */
167 static pthread_key_t perl_thr_key;
168
169 static int    perl_argc = 0;
170 static char **perl_argv = NULL;
171
172 static char base_name[DATA_MAX_NAME_LEN] = "";
173
174 static struct {
175         char name[64];
176         XS ((*f));
177 } api[] =
178 {
179         { "Collectd::plugin_register_data_set",   Collectd_plugin_register_ds },
180         { "Collectd::plugin_unregister_data_set", Collectd_plugin_unregister_ds },
181         { "Collectd::plugin_dispatch_values",     Collectd_plugin_dispatch_values },
182         { "Collectd::plugin_get_interval",        Collectd_plugin_get_interval },
183         { "Collectd::_plugin_write",              Collectd__plugin_write },
184         { "Collectd::_plugin_flush",              Collectd__plugin_flush },
185         { "Collectd::plugin_dispatch_notification",
186                 Collectd_plugin_dispatch_notification },
187         { "Collectd::plugin_log",                 Collectd_plugin_log },
188         { "Collectd::_fc_register",               Collectd__fc_register },
189         { "Collectd::call_by_name",               Collectd_call_by_name },
190         { "", NULL }
191 };
192
193 struct {
194         char name[64];
195         int  value;
196 } constants[] =
197 {
198         { "Collectd::TYPE_INIT",          PLUGIN_INIT },
199         { "Collectd::TYPE_READ",          PLUGIN_READ },
200         { "Collectd::TYPE_WRITE",         PLUGIN_WRITE },
201         { "Collectd::TYPE_SHUTDOWN",      PLUGIN_SHUTDOWN },
202         { "Collectd::TYPE_LOG",           PLUGIN_LOG },
203         { "Collectd::TYPE_NOTIF",         PLUGIN_NOTIF },
204         { "Collectd::TYPE_FLUSH",         PLUGIN_FLUSH },
205         { "Collectd::TYPE_CONFIG",        PLUGIN_CONFIG },
206         { "Collectd::TYPE_DATASET",       PLUGIN_DATASET },
207         { "Collectd::DS_TYPE_COUNTER",    DS_TYPE_COUNTER },
208         { "Collectd::DS_TYPE_GAUGE",      DS_TYPE_GAUGE },
209         { "Collectd::DS_TYPE_DERIVE",     DS_TYPE_DERIVE },
210         { "Collectd::DS_TYPE_ABSOLUTE",   DS_TYPE_ABSOLUTE },
211         { "Collectd::LOG_ERR",            LOG_ERR },
212         { "Collectd::LOG_WARNING",        LOG_WARNING },
213         { "Collectd::LOG_NOTICE",         LOG_NOTICE },
214         { "Collectd::LOG_INFO",           LOG_INFO },
215         { "Collectd::LOG_DEBUG",          LOG_DEBUG },
216         { "Collectd::FC_MATCH",           FC_MATCH },
217         { "Collectd::FC_TARGET",          FC_TARGET },
218         { "Collectd::FC_CB_CREATE",       FC_CB_CREATE },
219         { "Collectd::FC_CB_DESTROY",      FC_CB_DESTROY },
220         { "Collectd::FC_CB_EXEC",         FC_CB_EXEC },
221         { "Collectd::FC_MATCH_NO_MATCH",  FC_MATCH_NO_MATCH },
222         { "Collectd::FC_MATCH_MATCHES",   FC_MATCH_MATCHES },
223         { "Collectd::FC_TARGET_CONTINUE", FC_TARGET_CONTINUE },
224         { "Collectd::FC_TARGET_STOP",     FC_TARGET_STOP },
225         { "Collectd::FC_TARGET_RETURN",   FC_TARGET_RETURN },
226         { "Collectd::NOTIF_FAILURE",      NOTIF_FAILURE },
227         { "Collectd::NOTIF_WARNING",      NOTIF_WARNING },
228         { "Collectd::NOTIF_OKAY",         NOTIF_OKAY },
229         { "", 0 }
230 };
231
232 struct {
233         char  name[64];
234         char *var;
235 } g_strings[] =
236 {
237         { "Collectd::hostname_g", hostname_g },
238         { "", NULL }
239 };
240
241 /*
242  * Helper functions for data type conversion.
243  */
244
245 /*
246  * data source:
247  * [
248  *   {
249  *     name => $ds_name,
250  *     type => $ds_type,
251  *     min  => $ds_min,
252  *     max  => $ds_max
253  *   },
254  *   ...
255  * ]
256  */
257 static int hv2data_source (pTHX_ HV *hash, data_source_t *ds)
258 {
259         SV **tmp = NULL;
260
261         if ((NULL == hash) || (NULL == ds))
262                 return -1;
263
264         if (NULL != (tmp = hv_fetch (hash, "name", 4, 0))) {
265                 sstrncpy (ds->name, SvPV_nolen (*tmp), sizeof (ds->name));
266         }
267         else {
268                 log_err ("hv2data_source: No DS name given.");
269                 return -1;
270         }
271
272         if (NULL != (tmp = hv_fetch (hash, "type", 4, 0))) {
273                 ds->type = SvIV (*tmp);
274
275                 if ((DS_TYPE_COUNTER != ds->type)
276                                 && (DS_TYPE_GAUGE != ds->type)
277                                 && (DS_TYPE_DERIVE != ds->type)
278                                 && (DS_TYPE_ABSOLUTE != ds->type)) {
279                         log_err ("hv2data_source: Invalid DS type.");
280                         return -1;
281                 }
282         }
283         else {
284                 ds->type = DS_TYPE_COUNTER;
285         }
286
287         if (NULL != (tmp = hv_fetch (hash, "min", 3, 0)))
288                 ds->min = SvNV (*tmp);
289         else
290                 ds->min = NAN;
291
292         if (NULL != (tmp = hv_fetch (hash, "max", 3, 0)))
293                 ds->max = SvNV (*tmp);
294         else
295                 ds->max = NAN;
296         return 0;
297 } /* static int hv2data_source (HV *, data_source_t *) */
298
299 static int av2value (pTHX_ char *name, AV *array, value_t *value, int len)
300 {
301         const data_set_t *ds;
302
303         int i = 0;
304
305         if ((NULL == name) || (NULL == array) || (NULL == value))
306                 return -1;
307
308         if (av_len (array) < len - 1)
309                 len = av_len (array) + 1;
310
311         if (0 >= len)
312                 return -1;
313
314         ds = plugin_get_ds (name);
315         if (NULL == ds) {
316                 log_err ("av2value: Unknown dataset \"%s\"", name);
317                 return -1;
318         }
319
320         if (ds->ds_num < len) {
321                 log_warn ("av2value: Value length exceeds data set length.");
322                 len = ds->ds_num;
323         }
324
325         for (i = 0; i < len; ++i) {
326                 SV **tmp = av_fetch (array, i, 0);
327
328                 if (NULL != tmp) {
329                         if (DS_TYPE_COUNTER == ds->ds[i].type)
330                                 value[i].counter = SvIV (*tmp);
331                         else if (DS_TYPE_GAUGE == ds->ds[i].type)
332                                 value[i].gauge = SvNV (*tmp);
333                         else if (DS_TYPE_DERIVE == ds->ds[i].type)
334                                 value[i].derive = SvIV (*tmp);
335                         else if (DS_TYPE_ABSOLUTE == ds->ds[i].type)
336                                 value[i].absolute = SvIV (*tmp);
337                 }
338                 else {
339                         return -1;
340                 }
341         }
342         return len;
343 } /* static int av2value (char *, AV *, value_t *, int) */
344
345 /*
346  * value list:
347  * {
348  *   values => [ @values ],
349  *   time   => $time,
350  *   host   => $host,
351  *   plugin => $plugin,
352  *   plugin_instance => $pinstance,
353  *   type_instance   => $tinstance,
354  * }
355  */
356 static int hv2value_list (pTHX_ HV *hash, value_list_t *vl)
357 {
358         SV **tmp;
359
360         if ((NULL == hash) || (NULL == vl))
361                 return -1;
362
363         if (NULL == (tmp = hv_fetch (hash, "type", 4, 0))) {
364                 log_err ("hv2value_list: No type given.");
365                 return -1;
366         }
367
368         sstrncpy (vl->type, SvPV_nolen (*tmp), sizeof (vl->type));
369
370         if ((NULL == (tmp = hv_fetch (hash, "values", 6, 0)))
371                         || (! (SvROK (*tmp) && (SVt_PVAV == SvTYPE (SvRV (*tmp)))))) {
372                 log_err ("hv2value_list: No valid values given.");
373                 return -1;
374         }
375
376         {
377                 AV  *array = (AV *)SvRV (*tmp);
378                 int len    = av_len (array) + 1;
379
380                 if (len <= 0)
381                         return -1;
382
383                 vl->values     = (value_t *)smalloc (len * sizeof (value_t));
384                 vl->values_len = av2value (aTHX_ vl->type, (AV *)SvRV (*tmp),
385                                 vl->values, len);
386
387                 if (-1 == vl->values_len) {
388                         sfree (vl->values);
389                         return -1;
390                 }
391         }
392
393         if (NULL != (tmp = hv_fetch (hash, "time", 4, 0)))
394         {
395                 double t = SvNV (*tmp);
396                 vl->time = DOUBLE_TO_CDTIME_T (t);
397         }
398
399         if (NULL != (tmp = hv_fetch (hash, "interval", 8, 0)))
400         {
401                 double t = SvNV (*tmp);
402                 vl->interval = DOUBLE_TO_CDTIME_T (t);
403         }
404
405         if (NULL != (tmp = hv_fetch (hash, "host", 4, 0)))
406                 sstrncpy (vl->host, SvPV_nolen (*tmp), sizeof (vl->host));
407         else
408                 sstrncpy (vl->host, hostname_g, sizeof (vl->host));
409
410         if (NULL != (tmp = hv_fetch (hash, "plugin", 6, 0)))
411                 sstrncpy (vl->plugin, SvPV_nolen (*tmp), sizeof (vl->plugin));
412
413         if (NULL != (tmp = hv_fetch (hash, "plugin_instance", 15, 0)))
414                 sstrncpy (vl->plugin_instance, SvPV_nolen (*tmp),
415                                 sizeof (vl->plugin_instance));
416
417         if (NULL != (tmp = hv_fetch (hash, "type_instance", 13, 0)))
418                 sstrncpy (vl->type_instance, SvPV_nolen (*tmp),
419                                 sizeof (vl->type_instance));
420         return 0;
421 } /* static int hv2value_list (pTHX_ HV *, value_list_t *) */
422
423 static int av2data_set (pTHX_ AV *array, char *name, data_set_t *ds)
424 {
425         int len, i;
426
427         if ((NULL == array) || (NULL == name) || (NULL == ds))
428                 return -1;
429
430         len = av_len (array);
431
432         if (-1 == len) {
433                 log_err ("av2data_set: Invalid data set.");
434                 return -1;
435         }
436
437         ds->ds = (data_source_t *)smalloc ((len + 1) * sizeof (data_source_t));
438         ds->ds_num = len + 1;
439
440         for (i = 0; i <= len; ++i) {
441                 SV **elem = av_fetch (array, i, 0);
442
443                 if (NULL == elem) {
444                         log_err ("av2data_set: Failed to fetch data source %i.", i);
445                         return -1;
446                 }
447
448                 if (! (SvROK (*elem) && (SVt_PVHV == SvTYPE (SvRV (*elem))))) {
449                         log_err ("av2data_set: Invalid data source.");
450                         return -1;
451                 }
452
453                 if (-1 == hv2data_source (aTHX_ (HV *)SvRV (*elem), &ds->ds[i]))
454                         return -1;
455
456                 log_debug ("av2data_set: "
457                                 "DS.name = \"%s\", DS.type = %i, DS.min = %f, DS.max = %f",
458                                 ds->ds[i].name, ds->ds[i].type, ds->ds[i].min, ds->ds[i].max);
459         }
460
461         sstrncpy (ds->type, name, sizeof (ds->type));
462         return 0;
463 } /* static int av2data_set (pTHX_ AV *, data_set_t *) */
464
465 /*
466  * notification:
467  * {
468  *   severity => $severity,
469  *   time     => $time,
470  *   message  => $msg,
471  *   host     => $host,
472  *   plugin   => $plugin,
473  *   type     => $type,
474  *   plugin_instance => $instance,
475  *   type_instance   => $type_instance,
476  *   meta     => [ { name => <name>, value => <value> }, ... ]
477  * }
478  */
479 static int av2notification_meta (pTHX_ AV *array, notification_meta_t **meta)
480 {
481         notification_meta_t **m = meta;
482
483         int len = av_len (array);
484         int i;
485
486         for (i = 0; i <= len; ++i) {
487                 SV **tmp = av_fetch (array, i, 0);
488                 HV  *hash;
489
490                 if (NULL == tmp)
491                         return -1;
492
493                 if (! (SvROK (*tmp) && (SVt_PVHV == SvTYPE (SvRV (*tmp))))) {
494                         log_warn ("av2notification_meta: Skipping invalid "
495                                         "meta information.");
496                         continue;
497                 }
498
499                 hash = (HV *)SvRV (*tmp);
500
501                 *m = (notification_meta_t *)smalloc (sizeof (**m));
502
503                 if (NULL == (tmp = hv_fetch (hash, "name", 4, 0))) {
504                         log_warn ("av2notification_meta: Skipping invalid "
505                                         "meta information.");
506                         free (*m);
507                         continue;
508                 }
509                 sstrncpy ((*m)->name, SvPV_nolen (*tmp), sizeof ((*m)->name));
510
511                 if (NULL == (tmp = hv_fetch (hash, "value", 5, 0))) {
512                         log_warn ("av2notification_meta: Skipping invalid "
513                                         "meta information.");
514                         free ((*m)->name);
515                         free (*m);
516                         continue;
517                 }
518
519                 if (SvNOK (*tmp)) {
520                         (*m)->nm_value.nm_double = SvNVX (*tmp);
521                         (*m)->type = NM_TYPE_DOUBLE;
522                 }
523                 else if (SvUOK (*tmp)) {
524                         (*m)->nm_value.nm_unsigned_int = SvUVX (*tmp);
525                         (*m)->type = NM_TYPE_UNSIGNED_INT;
526                 }
527                 else if (SvIOK (*tmp)) {
528                         (*m)->nm_value.nm_signed_int = SvIVX (*tmp);
529                         (*m)->type = NM_TYPE_SIGNED_INT;
530                 }
531                 else {
532                         (*m)->nm_value.nm_string = sstrdup (SvPV_nolen (*tmp));
533                         (*m)->type = NM_TYPE_STRING;
534                 }
535
536                 (*m)->next = NULL;
537                 m = &((*m)->next);
538         }
539         return 0;
540 } /* static int av2notification_meta (AV *, notification_meta_t *) */
541
542 static int hv2notification (pTHX_ HV *hash, notification_t *n)
543 {
544         SV **tmp = NULL;
545
546         if ((NULL == hash) || (NULL == n))
547                 return -1;
548
549         if (NULL != (tmp = hv_fetch (hash, "severity", 8, 0)))
550                 n->severity = SvIV (*tmp);
551         else
552                 n->severity = NOTIF_FAILURE;
553
554         if (NULL != (tmp = hv_fetch (hash, "time", 4, 0)))
555         {
556                 double t = SvNV (*tmp);
557                 n->time = DOUBLE_TO_CDTIME_T (t);
558         }
559         else
560                 n->time = cdtime ();
561
562         if (NULL != (tmp = hv_fetch (hash, "message", 7, 0)))
563                 sstrncpy (n->message, SvPV_nolen (*tmp), sizeof (n->message));
564
565         if (NULL != (tmp = hv_fetch (hash, "host", 4, 0)))
566                 sstrncpy (n->host, SvPV_nolen (*tmp), sizeof (n->host));
567         else
568                 sstrncpy (n->host, hostname_g, sizeof (n->host));
569
570         if (NULL != (tmp = hv_fetch (hash, "plugin", 6, 0)))
571                 sstrncpy (n->plugin, SvPV_nolen (*tmp), sizeof (n->plugin));
572
573         if (NULL != (tmp = hv_fetch (hash, "plugin_instance", 15, 0)))
574                 sstrncpy (n->plugin_instance, SvPV_nolen (*tmp),
575                                 sizeof (n->plugin_instance));
576
577         if (NULL != (tmp = hv_fetch (hash, "type", 4, 0)))
578                 sstrncpy (n->type, SvPV_nolen (*tmp), sizeof (n->type));
579
580         if (NULL != (tmp = hv_fetch (hash, "type_instance", 13, 0)))
581                 sstrncpy (n->type_instance, SvPV_nolen (*tmp),
582                                 sizeof (n->type_instance));
583
584         n->meta = NULL;
585         while (NULL != (tmp = hv_fetch (hash, "meta", 4, 0))) {
586                 if (! (SvROK (*tmp) && (SVt_PVAV == SvTYPE (SvRV (*tmp))))) {
587                         log_warn ("hv2notification: Ignoring invalid meta information.");
588                         break;
589                 }
590
591                 if (0 != av2notification_meta (aTHX_ (AV *)SvRV (*tmp), &n->meta)) {
592                         plugin_notification_meta_free (n->meta);
593                         n->meta = NULL;
594                         return -1;
595                 }
596                 break;
597         }
598         return 0;
599 } /* static int hv2notification (pTHX_ HV *, notification_t *) */
600
601 static int data_set2av (pTHX_ data_set_t *ds, AV *array)
602 {
603         int i = 0;
604
605         if ((NULL == ds) || (NULL == array))
606                 return -1;
607
608         av_extend (array, ds->ds_num);
609
610         for (i = 0; i < ds->ds_num; ++i) {
611                 HV *source = newHV ();
612
613                 if (NULL == hv_store (source, "name", 4,
614                                 newSVpv (ds->ds[i].name, 0), 0))
615                         return -1;
616
617                 if (NULL == hv_store (source, "type", 4, newSViv (ds->ds[i].type), 0))
618                         return -1;
619
620                 if (! isnan (ds->ds[i].min))
621                         if (NULL == hv_store (source, "min", 3,
622                                         newSVnv (ds->ds[i].min), 0))
623                                 return -1;
624
625                 if (! isnan (ds->ds[i].max))
626                         if (NULL == hv_store (source, "max", 3,
627                                         newSVnv (ds->ds[i].max), 0))
628                                 return -1;
629
630                 if (NULL == av_store (array, i, newRV_noinc ((SV *)source)))
631                         return -1;
632         }
633         return 0;
634 } /* static int data_set2av (data_set_t *, AV *) */
635
636 static int value_list2hv (pTHX_ value_list_t *vl, data_set_t *ds, HV *hash)
637 {
638         AV *values = NULL;
639
640         int i   = 0;
641         int len = 0;
642
643         if ((NULL == vl) || (NULL == ds) || (NULL == hash))
644                 return -1;
645
646         len = vl->values_len;
647
648         if (ds->ds_num < len) {
649                 log_warn ("value2av: Value length exceeds data set length.");
650                 len = ds->ds_num;
651         }
652
653         values = newAV ();
654         av_extend (values, len - 1);
655
656         for (i = 0; i < len; ++i) {
657                 SV *val = NULL;
658
659                 if (DS_TYPE_COUNTER == ds->ds[i].type)
660                         val = newSViv (vl->values[i].counter);
661                 else if (DS_TYPE_GAUGE == ds->ds[i].type)
662                         val = newSVnv (vl->values[i].gauge);
663                 else if (DS_TYPE_DERIVE == ds->ds[i].type)
664                         val = newSViv (vl->values[i].derive);
665                 else if (DS_TYPE_ABSOLUTE == ds->ds[i].type)
666                         val = newSViv (vl->values[i].absolute);
667
668                 if (NULL == av_store (values, i, val)) {
669                         av_undef (values);
670                         return -1;
671                 }
672         }
673
674         if (NULL == hv_store (hash, "values", 6, newRV_noinc ((SV *)values), 0))
675                 return -1;
676
677         if (0 != vl->time)
678         {
679                 double t = CDTIME_T_TO_DOUBLE (vl->time);
680                 if (NULL == hv_store (hash, "time", 4, newSVnv (t), 0))
681                         return -1;
682         }
683
684         {
685                 double t = CDTIME_T_TO_DOUBLE (vl->interval);
686                 if (NULL == hv_store (hash, "interval", 8, newSVnv (t), 0))
687                         return -1;
688         }
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 /*
1666  * Collectd::plugin_get_interval ().
1667  */
1668 static XS (Collectd_plugin_get_interval)
1669 {
1670         dXSARGS;
1671
1672         /* make sure we don't get any unused variable warnings for 'items';
1673          * don't abort, though */
1674         if (items)
1675                 log_err ("Usage: Collectd::plugin_get_interval()");
1676
1677         XSRETURN_NV ((NV) CDTIME_T_TO_DOUBLE (plugin_get_interval ()));
1678 } /* static XS (Collectd_plugin_get_interval) */
1679
1680 /* Collectd::plugin_write (plugin, ds, vl).
1681  *
1682  * plugin:
1683  *   name of the plugin to call, may be 'undef'
1684  *
1685  * ds:
1686  *   data-set that describes the submitted values, may be 'undef'
1687  *
1688  * vl:
1689  *   value-list to be written
1690  */
1691 static XS (Collectd__plugin_write)
1692 {
1693         char *plugin;
1694         SV   *ds, *vl;
1695         AV   *ds_array;
1696
1697         int ret;
1698
1699         dXSARGS;
1700
1701         if (3 != items) {
1702                 log_err ("Usage: Collectd::plugin_write(plugin, ds, vl)");
1703                 XSRETURN_EMPTY;
1704         }
1705
1706         log_debug ("Collectd::plugin_write: plugin=\"%s\", ds=\"%s\", vl=\"%s\"",
1707                         SvPV_nolen (ST (0)), SvOK (ST (1)) ? SvPV_nolen (ST (1)) : "",
1708                         SvPV_nolen (ST (2)));
1709
1710         if (! SvOK (ST (0)))
1711                 plugin = NULL;
1712         else
1713                 plugin = SvPV_nolen (ST (0));
1714
1715         ds = ST (1);
1716         if (SvROK (ds) && (SVt_PVAV == SvTYPE (SvRV (ds))))
1717                 ds_array = (AV *)SvRV (ds);
1718         else if (! SvOK (ds))
1719                 ds_array = NULL;
1720         else {
1721                 log_err ("Collectd::plugin_write: Invalid data-set.");
1722                 XSRETURN_EMPTY;
1723         }
1724
1725         vl = ST (2);
1726         if (! (SvROK (vl) && (SVt_PVHV == SvTYPE (SvRV (vl))))) {
1727                 log_err ("Collectd::plugin_write: Invalid value-list.");
1728                 XSRETURN_EMPTY;
1729         }
1730
1731         ret = pplugin_write (aTHX_ plugin, ds_array, (HV *)SvRV (vl));
1732
1733         if (0 == ret)
1734                 XSRETURN_YES;
1735         else
1736                 XSRETURN_EMPTY;
1737 } /* static XS (Collectd__plugin_write) */
1738
1739 /*
1740  * Collectd::_plugin_flush (plugin, timeout, identifier).
1741  *
1742  * plugin:
1743  *   name of the plugin to flush
1744  *
1745  * timeout:
1746  *   timeout to use when flushing the data
1747  *
1748  * identifier:
1749  *   data-set identifier to flush
1750  */
1751 static XS (Collectd__plugin_flush)
1752 {
1753         char *plugin  = NULL;
1754         int   timeout = -1;
1755         char *id      = NULL;
1756
1757         dXSARGS;
1758
1759         if (3 != items) {
1760                 log_err ("Usage: Collectd::_plugin_flush(plugin, timeout, id)");
1761                 XSRETURN_EMPTY;
1762         }
1763
1764         if (SvOK (ST (0)))
1765                 plugin = SvPV_nolen (ST (0));
1766
1767         if (SvOK (ST (1)))
1768                 timeout = (int)SvIV (ST (1));
1769
1770         if (SvOK (ST (2)))
1771                 id = SvPV_nolen (ST (2));
1772
1773         log_debug ("Collectd::_plugin_flush: plugin = \"%s\", timeout = %i, "
1774                         "id = \"%s\"", plugin, timeout, id);
1775
1776         if (0 == plugin_flush (plugin, timeout, id))
1777                 XSRETURN_YES;
1778         else
1779                 XSRETURN_EMPTY;
1780 } /* static XS (Collectd__plugin_flush) */
1781
1782 /*
1783  * Collectd::plugin_dispatch_notification (notif).
1784  *
1785  * notif:
1786  *   notification to dispatch
1787  */
1788 static XS (Collectd_plugin_dispatch_notification)
1789 {
1790         SV *notif = NULL;
1791
1792         int ret = 0;
1793
1794         dXSARGS;
1795
1796         if (1 != items) {
1797                 log_err ("Usage: Collectd::plugin_dispatch_notification(notif)");
1798                 XSRETURN_EMPTY;
1799         }
1800
1801         log_debug ("Collectd::plugin_dispatch_notification: notif = \"%s\"",
1802                         SvPV_nolen (ST (0)));
1803
1804         notif = ST (0);
1805
1806         if (! (SvROK (notif) && (SVt_PVHV == SvTYPE (SvRV (notif))))) {
1807                 log_err ("Collectd::plugin_dispatch_notification: Invalid notif.");
1808                 XSRETURN_EMPTY;
1809         }
1810
1811         ret = pplugin_dispatch_notification (aTHX_ (HV *)SvRV (notif));
1812
1813         if (0 == ret)
1814                 XSRETURN_YES;
1815         else
1816                 XSRETURN_EMPTY;
1817 } /* static XS (Collectd_plugin_dispatch_notification) */
1818
1819 /*
1820  * Collectd::plugin_log (level, message).
1821  *
1822  * level:
1823  *   log level (LOG_DEBUG, ... LOG_ERR)
1824  *
1825  * message:
1826  *   log message
1827  */
1828 static XS (Collectd_plugin_log)
1829 {
1830         dXSARGS;
1831
1832         if (2 != items) {
1833                 log_err ("Usage: Collectd::plugin_log(level, message)");
1834                 XSRETURN_EMPTY;
1835         }
1836
1837         plugin_log (SvIV (ST (0)), "%s", SvPV_nolen (ST (1)));
1838         XSRETURN_YES;
1839 } /* static XS (Collectd_plugin_log) */
1840
1841 /*
1842  * Collectd::_fc_register (type, name)
1843  *
1844  * type:
1845  *   match | target
1846  *
1847  * name:
1848  *   name of the match
1849  */
1850 static XS (Collectd__fc_register)
1851 {
1852         int   type;
1853         char *name;
1854
1855         int ret = 0;
1856
1857         dXSARGS;
1858
1859         if (2 != items) {
1860                 log_err ("Usage: Collectd::_fc_register(type, name)");
1861                 XSRETURN_EMPTY;
1862         }
1863
1864         type = SvIV (ST (0));
1865         name = SvPV_nolen (ST (1));
1866
1867         if (FC_MATCH == type)
1868                 ret = fc_register_match (name, pmatch);
1869         else if (FC_TARGET == type)
1870                 ret = fc_register_target (name, ptarget);
1871
1872         if (0 == ret)
1873                 XSRETURN_YES;
1874         else
1875                 XSRETURN_EMPTY;
1876 } /* static XS (Collectd_fc_register) */
1877
1878 /*
1879  * Collectd::call_by_name (...).
1880  *
1881  * Call a Perl sub identified by its name passed through $Collectd::cb_name.
1882  */
1883 static XS (Collectd_call_by_name)
1884 {
1885         SV   *tmp  = NULL;
1886         char *name = NULL;
1887
1888         if (NULL == (tmp = get_sv ("Collectd::cb_name", 0))) {
1889                 sv_setpv (get_sv ("@", 1), "cb_name has not been set");
1890                 CLEAR_STACK_FRAME;
1891                 return;
1892         }
1893
1894         name = SvPV_nolen (tmp);
1895
1896         if (NULL == get_cv (name, 0)) {
1897                 sv_setpvf (get_sv ("@", 1), "unknown callback \"%s\"", name);
1898                 CLEAR_STACK_FRAME;
1899                 return;
1900         }
1901
1902         /* simply pass on the subroutine call without touching the stack,
1903          * thus leaving any arguments and return values in place */
1904         call_pv (name, 0);
1905 } /* static XS (Collectd_call_by_name) */
1906
1907 /*
1908  * Interface to collectd.
1909  */
1910
1911 static int perl_init (void)
1912 {
1913         dTHX;
1914
1915         if (NULL == perl_threads)
1916                 return 0;
1917
1918         if (NULL == aTHX) {
1919                 c_ithread_t *t = NULL;
1920
1921                 pthread_mutex_lock (&perl_threads->mutex);
1922                 t = c_ithread_create (perl_threads->head->interp);
1923                 pthread_mutex_unlock (&perl_threads->mutex);
1924
1925                 aTHX = t->interp;
1926         }
1927
1928         log_debug ("perl_init: c_ithread: interp = %p (active threads: %i)",
1929                         aTHX, perl_threads->number_of_threads);
1930         return pplugin_call_all (aTHX_ PLUGIN_INIT);
1931 } /* static int perl_init (void) */
1932
1933 static int perl_read (void)
1934 {
1935         dTHX;
1936
1937         if (NULL == perl_threads)
1938                 return 0;
1939
1940         if (NULL == aTHX) {
1941                 c_ithread_t *t = NULL;
1942
1943                 pthread_mutex_lock (&perl_threads->mutex);
1944                 t = c_ithread_create (perl_threads->head->interp);
1945                 pthread_mutex_unlock (&perl_threads->mutex);
1946
1947                 aTHX = t->interp;
1948         }
1949
1950         /* Assert that we're not running as the base thread. Otherwise, we might
1951          * run into concurrency issues with c_ithread_create(). See
1952          * https://github.com/collectd/collectd/issues/9 for details. */
1953         assert (aTHX != perl_threads->head->interp);
1954
1955         log_debug ("perl_read: c_ithread: interp = %p (active threads: %i)",
1956                         aTHX, perl_threads->number_of_threads);
1957         return pplugin_call_all (aTHX_ PLUGIN_READ);
1958 } /* static int perl_read (void) */
1959
1960 static int perl_write (const data_set_t *ds, const value_list_t *vl,
1961                 user_data_t __attribute__((unused)) *user_data)
1962 {
1963         int status;
1964         dTHX;
1965
1966         if (NULL == perl_threads)
1967                 return 0;
1968
1969         if (NULL == aTHX) {
1970                 c_ithread_t *t = NULL;
1971
1972                 pthread_mutex_lock (&perl_threads->mutex);
1973                 t = c_ithread_create (perl_threads->head->interp);
1974                 pthread_mutex_unlock (&perl_threads->mutex);
1975
1976                 aTHX = t->interp;
1977         }
1978
1979         /* Lock the base thread if this is not called from one of the read threads
1980          * to avoid race conditions with c_ithread_create(). See
1981          * https://github.com/collectd/collectd/issues/9 for details. */
1982         if (aTHX == perl_threads->head->interp)
1983                 pthread_mutex_lock (&perl_threads->mutex);
1984
1985         log_debug ("perl_write: c_ithread: interp = %p (active threads: %i)",
1986                         aTHX, perl_threads->number_of_threads);
1987         status = pplugin_call_all (aTHX_ PLUGIN_WRITE, ds, vl);
1988
1989         if (aTHX == perl_threads->head->interp)
1990                 pthread_mutex_unlock (&perl_threads->mutex);
1991
1992         return status;
1993 } /* static int perl_write (const data_set_t *, const value_list_t *) */
1994
1995 static void perl_log (int level, const char *msg,
1996                 user_data_t __attribute__((unused)) *user_data)
1997 {
1998         dTHX;
1999
2000         if (NULL == perl_threads)
2001                 return;
2002
2003         if (NULL == aTHX) {
2004                 c_ithread_t *t = NULL;
2005
2006                 pthread_mutex_lock (&perl_threads->mutex);
2007                 t = c_ithread_create (perl_threads->head->interp);
2008                 pthread_mutex_unlock (&perl_threads->mutex);
2009
2010                 aTHX = t->interp;
2011         }
2012
2013         /* Lock the base thread if this is not called from one of the read threads
2014          * to avoid race conditions with c_ithread_create(). See
2015          * https://github.com/collectd/collectd/issues/9 for details. */
2016         if (aTHX == perl_threads->head->interp)
2017                 pthread_mutex_lock (&perl_threads->mutex);
2018
2019         pplugin_call_all (aTHX_ PLUGIN_LOG, level, msg);
2020
2021         if (aTHX == perl_threads->head->interp)
2022                 pthread_mutex_unlock (&perl_threads->mutex);
2023
2024         return;
2025 } /* static void perl_log (int, const char *) */
2026
2027 static int perl_notify (const notification_t *notif,
2028                 user_data_t __attribute__((unused)) *user_data)
2029 {
2030         dTHX;
2031
2032         if (NULL == perl_threads)
2033                 return 0;
2034
2035         if (NULL == aTHX) {
2036                 c_ithread_t *t = NULL;
2037
2038                 pthread_mutex_lock (&perl_threads->mutex);
2039                 t = c_ithread_create (perl_threads->head->interp);
2040                 pthread_mutex_unlock (&perl_threads->mutex);
2041
2042                 aTHX = t->interp;
2043         }
2044         return pplugin_call_all (aTHX_ PLUGIN_NOTIF, notif);
2045 } /* static int perl_notify (const notification_t *) */
2046
2047 static int perl_flush (cdtime_t timeout, const char *identifier,
2048                 user_data_t __attribute__((unused)) *user_data)
2049 {
2050         dTHX;
2051
2052         if (NULL == perl_threads)
2053                 return 0;
2054
2055         if (NULL == aTHX) {
2056                 c_ithread_t *t = NULL;
2057
2058                 pthread_mutex_lock (&perl_threads->mutex);
2059                 t = c_ithread_create (perl_threads->head->interp);
2060                 pthread_mutex_unlock (&perl_threads->mutex);
2061
2062                 aTHX = t->interp;
2063         }
2064         return pplugin_call_all (aTHX_ PLUGIN_FLUSH, timeout, identifier);
2065 } /* static int perl_flush (const int) */
2066
2067 static int perl_shutdown (void)
2068 {
2069         c_ithread_t *t = NULL;
2070
2071         int ret = 0;
2072
2073         dTHX;
2074
2075         plugin_unregister_complex_config ("perl");
2076
2077         if (NULL == perl_threads)
2078                 return 0;
2079
2080         if (NULL == aTHX) {
2081                 c_ithread_t *t = NULL;
2082
2083                 pthread_mutex_lock (&perl_threads->mutex);
2084                 t = c_ithread_create (perl_threads->head->interp);
2085                 pthread_mutex_unlock (&perl_threads->mutex);
2086
2087                 aTHX = t->interp;
2088         }
2089
2090         log_debug ("perl_shutdown: c_ithread: interp = %p (active threads: %i)",
2091                         aTHX, perl_threads->number_of_threads);
2092
2093         plugin_unregister_log ("perl");
2094         plugin_unregister_notification ("perl");
2095         plugin_unregister_init ("perl");
2096         plugin_unregister_read ("perl");
2097         plugin_unregister_write ("perl");
2098         plugin_unregister_flush ("perl");
2099
2100         ret = pplugin_call_all (aTHX_ PLUGIN_SHUTDOWN);
2101
2102         pthread_mutex_lock (&perl_threads->mutex);
2103         t = perl_threads->tail;
2104
2105         while (NULL != t) {
2106                 c_ithread_t *thr = t;
2107
2108                 /* the pointer has to be advanced before destroying
2109                  * the thread as this will free the memory */
2110                 t = t->prev;
2111
2112                 c_ithread_destroy (thr);
2113         }
2114
2115         pthread_mutex_unlock (&perl_threads->mutex);
2116         pthread_mutex_destroy (&perl_threads->mutex);
2117
2118         sfree (perl_threads);
2119
2120         pthread_key_delete (perl_thr_key);
2121
2122         PERL_SYS_TERM ();
2123
2124         plugin_unregister_shutdown ("perl");
2125         return ret;
2126 } /* static void perl_shutdown (void) */
2127
2128 /*
2129  * Access functions for global variables.
2130  *
2131  * These functions implement the "magic" used to access
2132  * the global variables from Perl.
2133  */
2134
2135 static int g_pv_get (pTHX_ SV *var, MAGIC *mg)
2136 {
2137         char *pv = mg->mg_ptr;
2138         sv_setpv (var, pv);
2139         return 0;
2140 } /* static int g_pv_get (pTHX_ SV *, MAGIC *) */
2141
2142 static int g_pv_set (pTHX_ SV *var, MAGIC *mg)
2143 {
2144         char *pv = mg->mg_ptr;
2145         sstrncpy (pv, SvPV_nolen (var), DATA_MAX_NAME_LEN);
2146         return 0;
2147 } /* static int g_pv_set (pTHX_ SV *, MAGIC *) */
2148
2149 static int g_interval_get (pTHX_ SV *var, MAGIC *mg)
2150 {
2151         log_warn ("Accessing $interval_g is deprecated (and might not "
2152                         "give the desired results) - plugin_get_interval() should "
2153                         "be used instead.");
2154         sv_setnv (var, CDTIME_T_TO_DOUBLE (interval_g));
2155         return 0;
2156 } /* static int g_interval_get (pTHX_ SV *, MAGIC *) */
2157
2158 static int g_interval_set (pTHX_ SV *var, MAGIC *mg)
2159 {
2160         double nv = (double)SvNV (var);
2161         log_warn ("Accessing $interval_g is deprecated (and might not "
2162                         "give the desired results) - plugin_get_interval() should "
2163                         "be used instead.");
2164         interval_g = DOUBLE_TO_CDTIME_T (nv);
2165         return 0;
2166 } /* static int g_interval_set (pTHX_ SV *, MAGIC *) */
2167
2168 static MGVTBL g_pv_vtbl = {
2169         g_pv_get, g_pv_set, NULL, NULL, NULL, NULL, NULL
2170 #if HAVE_PERL_STRUCT_MGVTBL_SVT_LOCAL
2171                 , NULL
2172 #endif
2173 };
2174 static MGVTBL g_interval_vtbl = {
2175         g_interval_get, g_interval_set, NULL, NULL, NULL, NULL, NULL
2176 #if HAVE_PERL_STRUCT_MGVTBL_SVT_LOCAL
2177                 , NULL
2178 #endif
2179 };
2180
2181 /* bootstrap the Collectd module */
2182 static void xs_init (pTHX)
2183 {
2184         HV   *stash = NULL;
2185         SV   *tmp   = NULL;
2186         char *file  = __FILE__;
2187
2188         int i = 0;
2189
2190         dXSUB_SYS;
2191
2192         /* enable usage of Perl modules using shared libraries */
2193         newXS ("DynaLoader::boot_DynaLoader", boot_DynaLoader, file);
2194
2195         /* register API */
2196         for (i = 0; NULL != api[i].f; ++i)
2197                 newXS (api[i].name, api[i].f, file);
2198
2199         stash = gv_stashpv ("Collectd", 1);
2200
2201         /* export "constants" */
2202         for (i = 0; '\0' != constants[i].name[0]; ++i)
2203                 newCONSTSUB (stash, constants[i].name, newSViv (constants[i].value));
2204
2205         /* export global variables
2206          * by adding "magic" to the SV's representing the globale variables
2207          * perl is able to automagically call the get/set function when
2208          * accessing any such variable (this is basically the same as using
2209          * tie() in Perl) */
2210         /* global strings */
2211         for (i = 0; '\0' != g_strings[i].name[0]; ++i) {
2212                 tmp = get_sv (g_strings[i].name, 1);
2213                 sv_magicext (tmp, NULL, PERL_MAGIC_ext, &g_pv_vtbl,
2214                                 g_strings[i].var, 0);
2215         }
2216
2217         tmp = get_sv ("Collectd::interval_g", /* create = */ 1);
2218         sv_magicext (tmp, NULL, /* how = */ PERL_MAGIC_ext,
2219                         /* vtbl = */ &g_interval_vtbl,
2220                         /* name = */ NULL, /* namelen = */ 0);
2221
2222         return;
2223 } /* static void xs_init (pTHX) */
2224
2225 /* Initialize the global Perl interpreter. */
2226 static int init_pi (int argc, char **argv)
2227 {
2228         dTHXa (NULL);
2229
2230         if (NULL != perl_threads)
2231                 return 0;
2232
2233         log_info ("Initializing Perl interpreter...");
2234 #if COLLECT_DEBUG
2235         {
2236                 int i = 0;
2237
2238                 for (i = 0; i < argc; ++i)
2239                         log_debug ("argv[%i] = \"%s\"", i, argv[i]);
2240         }
2241 #endif /* COLLECT_DEBUG */
2242
2243         if (0 != pthread_key_create (&perl_thr_key, c_ithread_destructor)) {
2244                 log_err ("init_pi: pthread_key_create failed");
2245
2246                 /* this must not happen - cowardly giving up if it does */
2247                 return -1;
2248         }
2249
2250 #ifdef __FreeBSD__
2251         /* On FreeBSD, PERL_SYS_INIT3 expands to some expression which
2252          * triggers a "value computed is not used" warning by gcc. */
2253         (void)
2254 #endif
2255         PERL_SYS_INIT3 (&argc, &argv, &environ);
2256
2257         perl_threads = (c_ithread_list_t *)smalloc (sizeof (c_ithread_list_t));
2258         memset (perl_threads, 0, sizeof (c_ithread_list_t));
2259
2260         pthread_mutex_init (&perl_threads->mutex, NULL);
2261         /* locking the mutex should not be necessary at this point
2262          * but let's just do it for the sake of completeness */
2263         pthread_mutex_lock (&perl_threads->mutex);
2264
2265         perl_threads->head = c_ithread_create (NULL);
2266         perl_threads->tail = perl_threads->head;
2267
2268         if (NULL == (perl_threads->head->interp = perl_alloc ())) {
2269                 log_err ("init_pi: Not enough memory.");
2270                 exit (3);
2271         }
2272
2273         aTHX = perl_threads->head->interp;
2274         pthread_mutex_unlock (&perl_threads->mutex);
2275
2276         perl_construct (aTHX);
2277
2278         PL_exit_flags |= PERL_EXIT_DESTRUCT_END;
2279
2280         if (0 != perl_parse (aTHX_ xs_init, argc, argv, NULL)) {
2281                 SV *err = get_sv ("@", 1);
2282                 log_err ("init_pi: Unable to bootstrap Collectd: %s",
2283                                 SvPV_nolen (err));
2284
2285                 perl_destruct (perl_threads->head->interp);
2286                 perl_free (perl_threads->head->interp);
2287                 sfree (perl_threads);
2288
2289                 pthread_key_delete (perl_thr_key);
2290                 return -1;
2291         }
2292
2293         /* Set $0 to "collectd" because perl_parse() has to set it to "-e". */
2294         sv_setpv (get_sv ("0", 0), "collectd");
2295
2296         perl_run (aTHX);
2297
2298         plugin_register_log ("perl", perl_log, /* user_data = */ NULL);
2299         plugin_register_notification ("perl", perl_notify,
2300                         /* user_data = */ NULL);
2301         plugin_register_init ("perl", perl_init);
2302
2303         plugin_register_read ("perl", perl_read);
2304
2305         plugin_register_write ("perl", perl_write, /* user_data = */ NULL);
2306         plugin_register_flush ("perl", perl_flush, /* user_data = */ NULL);
2307         plugin_register_shutdown ("perl", perl_shutdown);
2308         return 0;
2309 } /* static int init_pi (const char **, const int) */
2310
2311 /*
2312  * LoadPlugin "<Plugin>"
2313  */
2314 static int perl_config_loadplugin (pTHX_ oconfig_item_t *ci)
2315 {
2316         char module_name[DATA_MAX_NAME_LEN];
2317
2318         char *value = NULL;
2319
2320         if ((0 != ci->children_num) || (1 != ci->values_num)
2321                         || (OCONFIG_TYPE_STRING != ci->values[0].type)) {
2322                 log_err ("LoadPlugin expects a single string argument.");
2323                 return 1;
2324         }
2325
2326         value = ci->values[0].value.string;
2327
2328         if (NULL == get_module_name (module_name, sizeof (module_name), value)) {
2329                 log_err ("Invalid module name %s", value);
2330                 return (1);
2331         }
2332
2333         if (0 != init_pi (perl_argc, perl_argv))
2334                 return -1;
2335
2336         assert (NULL != perl_threads);
2337         assert (NULL != perl_threads->head);
2338
2339         aTHX = perl_threads->head->interp;
2340
2341         log_debug ("perl_config: loading perl plugin \"%s\"", value);
2342         load_module (PERL_LOADMOD_NOIMPORT,
2343                         newSVpv (module_name, strlen (module_name)), Nullsv);
2344         return 0;
2345 } /* static int perl_config_loadplugin (oconfig_item_it *) */
2346
2347 /*
2348  * BaseName "<Name>"
2349  */
2350 static int perl_config_basename (pTHX_ oconfig_item_t *ci)
2351 {
2352         char *value = NULL;
2353
2354         if ((0 != ci->children_num) || (1 != ci->values_num)
2355                         || (OCONFIG_TYPE_STRING != ci->values[0].type)) {
2356                 log_err ("BaseName expects a single string argument.");
2357                 return 1;
2358         }
2359
2360         value = ci->values[0].value.string;
2361
2362         log_debug ("perl_config: Setting plugin basename to \"%s\"", value);
2363         sstrncpy (base_name, value, sizeof (base_name));
2364         return 0;
2365 } /* static int perl_config_basename (oconfig_item_it *) */
2366
2367 /*
2368  * EnableDebugger "<Package>"|""
2369  */
2370 static int perl_config_enabledebugger (pTHX_ oconfig_item_t *ci)
2371 {
2372         char *value = NULL;
2373
2374         if ((0 != ci->children_num) || (1 != ci->values_num)
2375                         || (OCONFIG_TYPE_STRING != ci->values[0].type)) {
2376                 log_err ("EnableDebugger expects a single string argument.");
2377                 return 1;
2378         }
2379
2380         if (NULL != perl_threads) {
2381                 log_warn ("EnableDebugger has no effects if used after LoadPlugin.");
2382                 return 1;
2383         }
2384
2385         value = ci->values[0].value.string;
2386
2387         perl_argv = (char **)realloc (perl_argv,
2388                         (++perl_argc + 1) * sizeof (char *));
2389
2390         if (NULL == perl_argv) {
2391                 log_err ("perl_config: Not enough memory.");
2392                 exit (3);
2393         }
2394
2395         if ('\0' == value[0]) {
2396                 perl_argv[perl_argc - 1] = "-d";
2397         }
2398         else {
2399                 perl_argv[perl_argc - 1] = (char *)smalloc (strlen (value) + 4);
2400                 sstrncpy (perl_argv[perl_argc - 1], "-d:", 4);
2401                 sstrncpy (perl_argv[perl_argc - 1] + 3, value, strlen (value) + 1);
2402         }
2403
2404         perl_argv[perl_argc] = NULL;
2405         return 0;
2406 } /* static int perl_config_enabledebugger (oconfig_item_it *) */
2407
2408 /*
2409  * IncludeDir "<Dir>"
2410  */
2411 static int perl_config_includedir (pTHX_ oconfig_item_t *ci)
2412 {
2413         char *value = NULL;
2414
2415         if ((0 != ci->children_num) || (1 != ci->values_num)
2416                         || (OCONFIG_TYPE_STRING != ci->values[0].type)) {
2417                 log_err ("IncludeDir expects a single string argument.");
2418                 return 1;
2419         }
2420
2421         value = ci->values[0].value.string;
2422
2423         if (NULL == aTHX) {
2424                 perl_argv = (char **)realloc (perl_argv,
2425                                 (++perl_argc + 1) * sizeof (char *));
2426
2427                 if (NULL == perl_argv) {
2428                         log_err ("perl_config: Not enough memory.");
2429                         exit (3);
2430                 }
2431
2432                 perl_argv[perl_argc - 1] = (char *)smalloc (strlen (value) + 3);
2433                 sstrncpy(perl_argv[perl_argc - 1], "-I", 3);
2434                 sstrncpy(perl_argv[perl_argc - 1] + 2, value, strlen (value) + 1);
2435
2436                 perl_argv[perl_argc] = NULL;
2437         }
2438         else {
2439                 /* prepend the directory to @INC */
2440                 av_unshift (GvAVn (PL_incgv), 1);
2441                 av_store (GvAVn (PL_incgv), 0, newSVpv (value, strlen (value)));
2442         }
2443         return 0;
2444 } /* static int perl_config_includedir (oconfig_item_it *) */
2445
2446 /*
2447  * <Plugin> block
2448  */
2449 static int perl_config_plugin (pTHX_ oconfig_item_t *ci)
2450 {
2451         int retvals = 0;
2452         int ret     = 0;
2453
2454         char *plugin;
2455         HV   *config;
2456
2457         dSP;
2458
2459         if ((1 != ci->values_num) || (OCONFIG_TYPE_STRING != ci->values[0].type)) {
2460                 log_err ("LoadPlugin expects a single string argument.");
2461                 return 1;
2462         }
2463
2464         plugin = ci->values[0].value.string;
2465         config = newHV ();
2466
2467         if (0 != oconfig_item2hv (aTHX_ ci, config)) {
2468                 hv_clear (config);
2469                 hv_undef (config);
2470
2471                 log_err ("Unable to convert configuration to a Perl hash value.");
2472                 config = (HV *)&PL_sv_undef;
2473         }
2474
2475         ENTER;
2476         SAVETMPS;
2477
2478         PUSHMARK (SP);
2479
2480         XPUSHs (sv_2mortal (newSVpv (plugin, 0)));
2481         XPUSHs (sv_2mortal (newRV_noinc ((SV *)config)));
2482
2483         PUTBACK;
2484
2485         retvals = call_pv ("Collectd::_plugin_dispatch_config", G_SCALAR);
2486
2487         SPAGAIN;
2488         if (0 < retvals) {
2489                 SV *tmp = POPs;
2490                 if (! SvTRUE (tmp))
2491                         ret = 1;
2492         }
2493         else
2494                 ret = 1;
2495
2496         PUTBACK;
2497         FREETMPS;
2498         LEAVE;
2499         return ret;
2500 } /* static int perl_config_plugin (oconfig_item_it *) */
2501
2502 static int perl_config (oconfig_item_t *ci)
2503 {
2504         int status = 0;
2505         int i = 0;
2506
2507         dTHXa (NULL);
2508
2509         for (i = 0; i < ci->children_num; ++i) {
2510                 oconfig_item_t *c = ci->children + i;
2511                 int current_status = 0;
2512
2513                 if (NULL != perl_threads)
2514                         aTHX = PERL_GET_CONTEXT;
2515
2516                 if (0 == strcasecmp (c->key, "LoadPlugin"))
2517                         current_status = perl_config_loadplugin (aTHX_ c);
2518                 else if (0 == strcasecmp (c->key, "BaseName"))
2519                         current_status = perl_config_basename (aTHX_ c);
2520                 else if (0 == strcasecmp (c->key, "EnableDebugger"))
2521                         current_status = perl_config_enabledebugger (aTHX_ c);
2522                 else if (0 == strcasecmp (c->key, "IncludeDir"))
2523                         current_status = perl_config_includedir (aTHX_ c);
2524                 else if (0 == strcasecmp (c->key, "Plugin"))
2525                         current_status = perl_config_plugin (aTHX_ c);
2526                 else
2527                 {
2528                         log_warn ("Ignoring unknown config key \"%s\".", c->key);
2529                         current_status = 0;
2530                 }
2531
2532                 /* fatal error - it's up to perl_config_* to clean up */
2533                 if (0 > current_status) {
2534                         log_err ("Configuration failed with a fatal error - "
2535                                         "plugin disabled!");
2536                         return current_status;
2537                 }
2538
2539                 status += current_status;
2540         }
2541         return status;
2542 } /* static int perl_config (oconfig_item_t *) */
2543
2544 void module_register (void)
2545 {
2546         perl_argc = 4;
2547         perl_argv = (char **)smalloc ((perl_argc + 1) * sizeof (char *));
2548
2549         /* default options for the Perl interpreter */
2550         perl_argv[0] = "";
2551         perl_argv[1] = "-MCollectd";
2552         perl_argv[2] = "-e";
2553         perl_argv[3] = "1";
2554         perl_argv[4] = NULL;
2555
2556         plugin_register_complex_config ("perl", perl_config);
2557         return;
2558 } /* void module_register (void) */
2559
2560 /* vim: set sw=4 ts=4 tw=78 noexpandtab : */
2561