perl plugin, Collectd.pm: Added support to flush Perl plugins.
[collectd.git] / src / perl.c
1 /**
2  * collectd - src/perl.c
3  * Copyright (C) 2007, 2008  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 #include "collectd.h"
31
32 #include "configfile.h"
33
34 #include <EXTERN.h>
35 #include <perl.h>
36
37 #include <XSUB.h>
38
39 /* Some versions of Perl define their own version of DEBUG... :-/ */
40 #ifdef DEBUG
41 # undef DEBUG
42 #endif /* DEBUG */
43
44 /* ... while we want the definition found in plugin.h. */
45 #include "plugin.h"
46 #include "common.h"
47
48 #include <pthread.h>
49
50 #if !defined(USE_ITHREADS)
51 # error "Perl does not support ithreads!"
52 #endif /* !defined(USE_ITHREADS) */
53
54 /* clear the Perl sub's stack frame
55  * (this should only be used inside an XSUB) */
56 #define CLEAR_STACK_FRAME PL_stack_sp = PL_stack_base + *PL_markstack_ptr
57
58 #define PLUGIN_INIT     0
59 #define PLUGIN_READ     1
60 #define PLUGIN_WRITE    2
61 #define PLUGIN_SHUTDOWN 3
62 #define PLUGIN_LOG      4
63 #define PLUGIN_NOTIF    5
64 #define PLUGIN_FLUSH    6
65
66 #define PLUGIN_TYPES    7
67
68 #define PLUGIN_DATASET  255
69
70 #define log_debug(...) DEBUG ("perl: " __VA_ARGS__)
71 #define log_info(...) INFO ("perl: " __VA_ARGS__)
72 #define log_warn(...) WARNING ("perl: " __VA_ARGS__)
73 #define log_err(...) ERROR ("perl: " __VA_ARGS__)
74
75 /* this is defined in DynaLoader.a */
76 void boot_DynaLoader (PerlInterpreter *, CV *);
77
78 static XS (Collectd_plugin_register_ds);
79 static XS (Collectd_plugin_unregister_ds);
80 static XS (Collectd_plugin_dispatch_values);
81 static XS (Collectd_plugin_dispatch_notification);
82 static XS (Collectd_plugin_log);
83 static XS (Collectd_call_by_name);
84
85 /*
86  * private data types
87  */
88
89 typedef struct c_ithread_s {
90         /* the thread's Perl interpreter */
91         PerlInterpreter *interp;
92
93         /* double linked list of threads */
94         struct c_ithread_s *prev;
95         struct c_ithread_s *next;
96 } c_ithread_t;
97
98 typedef struct {
99         c_ithread_t *head;
100         c_ithread_t *tail;
101
102 #if COLLECT_DEBUG
103         /* some usage stats */
104         int number_of_threads;
105 #endif /* COLLECT_DEBUG */
106
107         pthread_mutex_t mutex;
108 } c_ithread_list_t;
109
110 /*
111  * private variables
112  */
113
114 /* if perl_threads != NULL perl_threads->head must
115  * point to the "base" thread */
116 static c_ithread_list_t *perl_threads = NULL;
117
118 /* the key used to store each pthread's ithread */
119 static pthread_key_t perl_thr_key;
120
121 static int    perl_argc = 0;
122 static char **perl_argv = NULL;
123
124 static char base_name[DATA_MAX_NAME_LEN] = "";
125
126 static struct {
127         char name[64];
128         XS ((*f));
129 } api[] =
130 {
131         { "Collectd::plugin_register_data_set",   Collectd_plugin_register_ds },
132         { "Collectd::plugin_unregister_data_set", Collectd_plugin_unregister_ds },
133         { "Collectd::plugin_dispatch_values",     Collectd_plugin_dispatch_values },
134         { "Collectd::plugin_dispatch_notification",
135                 Collectd_plugin_dispatch_notification },
136         { "Collectd::plugin_log",                 Collectd_plugin_log },
137         { "Collectd::call_by_name",               Collectd_call_by_name },
138         { "", NULL }
139 };
140
141 struct {
142         char name[64];
143         int  value;
144 } constants[] =
145 {
146         { "Collectd::TYPE_INIT",       PLUGIN_INIT },
147         { "Collectd::TYPE_READ",       PLUGIN_READ },
148         { "Collectd::TYPE_WRITE",      PLUGIN_WRITE },
149         { "Collectd::TYPE_SHUTDOWN",   PLUGIN_SHUTDOWN },
150         { "Collectd::TYPE_LOG",        PLUGIN_LOG },
151         { "Collectd::TYPE_NOTIF",      PLUGIN_NOTIF },
152         { "Collectd::TYPE_FLUSH",      PLUGIN_FLUSH },
153         { "Collectd::TYPE_DATASET",    PLUGIN_DATASET },
154         { "Collectd::DS_TYPE_COUNTER", DS_TYPE_COUNTER },
155         { "Collectd::DS_TYPE_GAUGE",   DS_TYPE_GAUGE },
156         { "Collectd::LOG_ERR",         LOG_ERR },
157         { "Collectd::LOG_WARNING",     LOG_WARNING },
158         { "Collectd::LOG_NOTICE",      LOG_NOTICE },
159         { "Collectd::LOG_INFO",        LOG_INFO },
160         { "Collectd::LOG_DEBUG",       LOG_DEBUG },
161         { "Collectd::NOTIF_FAILURE",   NOTIF_FAILURE },
162         { "Collectd::NOTIF_WARNING",   NOTIF_WARNING },
163         { "Collectd::NOTIF_OKAY",      NOTIF_OKAY },
164         { "", 0 }
165 };
166
167 struct {
168         char  name[64];
169         char *var;
170 } g_strings[] =
171 {
172         { "Collectd::hostname_g", hostname_g },
173         { "", NULL }
174 };
175
176 struct {
177         char  name[64];
178         int  *var;
179 } g_integers[] =
180 {
181         { "Collectd::interval_g", &interval_g },
182         { "", NULL }
183 };
184
185 /*
186  * Helper functions for data type conversion.
187  */
188
189 /*
190  * data source:
191  * [
192  *   {
193  *     name => $ds_name,
194  *     type => $ds_type,
195  *     min  => $ds_min,
196  *     max  => $ds_max
197  *   },
198  *   ...
199  * ]
200  */
201 static int hv2data_source (pTHX_ HV *hash, data_source_t *ds)
202 {
203         SV **tmp = NULL;
204
205         if ((NULL == hash) || (NULL == ds))
206                 return -1;
207
208         if (NULL != (tmp = hv_fetch (hash, "name", 4, 0))) {
209                 strncpy (ds->name, SvPV_nolen (*tmp), DATA_MAX_NAME_LEN);
210                 ds->name[DATA_MAX_NAME_LEN - 1] = '\0';
211         }
212         else {
213                 log_err ("hv2data_source: No DS name given.");
214                 return -1;
215         }
216
217         if (NULL != (tmp = hv_fetch (hash, "type", 4, 0))) {
218                 ds->type = SvIV (*tmp);
219
220                 if ((DS_TYPE_COUNTER != ds->type) && (DS_TYPE_GAUGE != ds->type)) {
221                         log_err ("hv2data_source: Invalid DS type.");
222                         return -1;
223                 }
224         }
225         else {
226                 ds->type = DS_TYPE_COUNTER;
227         }
228
229         if (NULL != (tmp = hv_fetch (hash, "min", 3, 0)))
230                 ds->min = SvNV (*tmp);
231         else
232                 ds->min = NAN;
233
234         if (NULL != (tmp = hv_fetch (hash, "max", 3, 0)))
235                 ds->max = SvNV (*tmp);
236         else
237                 ds->max = NAN;
238         return 0;
239 } /* static data_source_t *hv2data_source (HV *) */
240
241 static int av2value (pTHX_ char *name, AV *array, value_t *value, int len)
242 {
243         const data_set_t *ds;
244
245         int i = 0;
246
247         if ((NULL == name) || (NULL == array) || (NULL == value))
248                 return -1;
249
250         if (av_len (array) < len - 1)
251                 len = av_len (array) + 1;
252
253         if (0 >= len)
254                 return -1;
255
256         ds = plugin_get_ds (name);
257         if (NULL == ds) {
258                 log_err ("av2value: Unknown dataset \"%s\"", name);
259                 return -1;
260         }
261
262         if (ds->ds_num < len) {
263                 log_warn ("av2value: Value length exceeds data set length.");
264                 len = ds->ds_num;
265         }
266
267         for (i = 0; i < len; ++i) {
268                 SV **tmp = av_fetch (array, i, 0);
269
270                 if (NULL != tmp) {
271                         if (DS_TYPE_COUNTER == ds->ds[i].type)
272                                 value[i].counter = SvIV (*tmp);
273                         else
274                                 value[i].gauge = SvNV (*tmp);
275                 }
276                 else {
277                         return -1;
278                 }
279         }
280         return len;
281 } /* static int av2value (char *, AV *, value_t *, int) */
282
283 static int data_set2av (pTHX_ data_set_t *ds, AV *array)
284 {
285         int i = 0;
286
287         if ((NULL == ds) || (NULL == array))
288                 return -1;
289
290         av_extend (array, ds->ds_num);
291
292         for (i = 0; i < ds->ds_num; ++i) {
293                 HV *source = newHV ();
294
295                 if (NULL == hv_store (source, "name", 4,
296                                 newSVpv (ds->ds[i].name, 0), 0))
297                         return -1;
298
299                 if (NULL == hv_store (source, "type", 4, newSViv (ds->ds[i].type), 0))
300                         return -1;
301
302                 if (! isnan (ds->ds[i].min))
303                         if (NULL == hv_store (source, "min", 3,
304                                         newSVnv (ds->ds[i].min), 0))
305                                 return -1;
306
307                 if (! isnan (ds->ds[i].max))
308                         if (NULL == hv_store (source, "max", 3,
309                                         newSVnv (ds->ds[i].max), 0))
310                                 return -1;
311
312                 if (NULL == av_store (array, i, newRV_noinc ((SV *)source)))
313                         return -1;
314         }
315         return 0;
316 } /* static int data_set2av (data_set_t *, AV *) */
317
318 static int value_list2hv (pTHX_ value_list_t *vl, data_set_t *ds, HV *hash)
319 {
320         AV *values = NULL;
321
322         int i   = 0;
323         int len = 0;
324
325         if ((NULL == vl) || (NULL == ds) || (NULL == hash))
326                 return -1;
327
328         len = vl->values_len;
329
330         if (ds->ds_num < len) {
331                 log_warn ("value2av: Value length exceeds data set length.");
332                 len = ds->ds_num;
333         }
334
335         values = newAV ();
336         av_extend (values, len - 1);
337
338         for (i = 0; i < len; ++i) {
339                 SV *val = NULL;
340
341                 if (DS_TYPE_COUNTER == ds->ds[i].type)
342                         val = newSViv (vl->values[i].counter);
343                 else
344                         val = newSVnv (vl->values[i].gauge);
345
346                 if (NULL == av_store (values, i, val)) {
347                         av_undef (values);
348                         return -1;
349                 }
350         }
351
352         if (NULL == hv_store (hash, "values", 6, newRV_noinc ((SV *)values), 0))
353                 return -1;
354
355         if (0 != vl->time)
356                 if (NULL == hv_store (hash, "time", 4, newSViv (vl->time), 0))
357                         return -1;
358
359         if ('\0' != vl->host[0])
360                 if (NULL == hv_store (hash, "host", 4, newSVpv (vl->host, 0), 0))
361                         return -1;
362
363         if ('\0' != vl->plugin[0])
364                 if (NULL == hv_store (hash, "plugin", 6, newSVpv (vl->plugin, 0), 0))
365                         return -1;
366
367         if ('\0' != vl->plugin_instance[0])
368                 if (NULL == hv_store (hash, "plugin_instance", 15,
369                                 newSVpv (vl->plugin_instance, 0), 0))
370                         return -1;
371
372         if ('\0' != vl->type_instance[0])
373                 if (NULL == hv_store (hash, "type_instance", 13,
374                                 newSVpv (vl->type_instance, 0), 0))
375                         return -1;
376         return 0;
377 } /* static int value2av (value_list_t *, data_set_t *, HV *) */
378
379 static int notification2hv (pTHX_ notification_t *n, HV *hash)
380 {
381         if (NULL == hv_store (hash, "severity", 8, newSViv (n->severity), 0))
382                 return -1;
383
384         if (0 != n->time)
385                 if (NULL == hv_store (hash, "time", 4, newSViv (n->time), 0))
386                         return -1;
387
388         if ('\0' != *n->message)
389                 if (NULL == hv_store (hash, "message", 7, newSVpv (n->message, 0), 0))
390                         return -1;
391
392         if ('\0' != *n->host)
393                 if (NULL == hv_store (hash, "host", 4, newSVpv (n->host, 0), 0))
394                         return -1;
395
396         if ('\0' != *n->plugin)
397                 if (NULL == hv_store (hash, "plugin", 6, newSVpv (n->plugin, 0), 0))
398                         return -1;
399
400         if ('\0' != *n->plugin_instance)
401                 if (NULL == hv_store (hash, "plugin_instance", 15,
402                                 newSVpv (n->plugin_instance, 0), 0))
403                         return -1;
404
405         if ('\0' != *n->type)
406                 if (NULL == hv_store (hash, "type", 4, newSVpv (n->type, 0), 0))
407                         return -1;
408
409         if ('\0' != *n->type_instance)
410                 if (NULL == hv_store (hash, "type_instance", 13,
411                                 newSVpv (n->type_instance, 0), 0))
412                         return -1;
413         return 0;
414 } /* static int notification2hv (notification_t *, HV *) */
415
416 /*
417  * Internal functions.
418  */
419
420 static char *get_module_name (char *buf, size_t buf_len, const char *module) {
421         int status = 0;
422         if (base_name[0] == '\0')
423                 status = snprintf (buf, buf_len, "%s", module);
424         else
425                 status = snprintf (buf, buf_len, "%s::%s", base_name, module);
426         if ((status < 0) || ((unsigned int)status >= buf_len))
427                 return (NULL);
428         buf[buf_len - 1] = '\0';
429         return (buf);
430 } /* char *get_module_name */
431
432 /*
433  * Add a plugin's data set definition.
434  */
435 static int pplugin_register_data_set (pTHX_ char *name, AV *dataset)
436 {
437         int len = -1;
438         int ret = 0;
439         int i   = 0;
440
441         data_source_t *ds  = NULL;
442         data_set_t    *set = NULL;
443
444         if ((NULL == name) || (NULL == dataset))
445                 return -1;
446
447         len = av_len (dataset);
448
449         if (-1 == len)
450                 return -1;
451
452         ds  = (data_source_t *)smalloc ((len + 1) * sizeof (data_source_t));
453         set = (data_set_t *)smalloc (sizeof (data_set_t));
454
455         for (i = 0; i <= len; ++i) {
456                 SV **elem = av_fetch (dataset, i, 0);
457
458                 if (NULL == elem)
459                         return -1;
460
461                 if (! (SvROK (*elem) && (SVt_PVHV == SvTYPE (SvRV (*elem))))) {
462                         log_err ("pplugin_register_data_set: Invalid data source.");
463                         return -1;
464                 }
465
466                 if (-1 == hv2data_source (aTHX_ (HV *)SvRV (*elem), &ds[i]))
467                         return -1;
468
469                 log_debug ("pplugin_register_data_set: "
470                                 "DS.name = \"%s\", DS.type = %i, DS.min = %f, DS.max = %f",
471                                 ds[i].name, ds[i].type, ds[i].min, ds[i].max);
472         }
473
474         strncpy (set->type, name, DATA_MAX_NAME_LEN);
475         set->type[DATA_MAX_NAME_LEN - 1] = '\0';
476
477         set->ds_num = len + 1;
478         set->ds = ds;
479
480         ret = plugin_register_data_set (set);
481
482         free (ds);
483         free (set);
484         return ret;
485 } /* static int pplugin_register_data_set (char *, SV *) */
486
487 /*
488  * Remove a plugin's data set definition.
489  */
490 static int pplugin_unregister_data_set (char *name)
491 {
492         if (NULL == name)
493                 return 0;
494         return plugin_unregister_data_set (name);
495 } /* static int pplugin_unregister_data_set (char *) */
496
497 /*
498  * Submit the values to the write functions.
499  *
500  * value list:
501  * {
502  *   values => [ @values ],
503  *   time   => $time,
504  *   host   => $host,
505  *   plugin => $plugin,
506  *   plugin_instance => $pinstance,
507  *   type_instance   => $tinstance,
508  * }
509  */
510 static int pplugin_dispatch_values (pTHX_ char *name, HV *values)
511 {
512         value_list_t list = VALUE_LIST_INIT;
513         value_t      *val = NULL;
514
515         SV **tmp = NULL;
516
517         int ret = 0;
518
519         if ((NULL == name) || (NULL == values))
520                 return -1;
521
522         if ((NULL == (tmp = hv_fetch (values, "values", 6, 0)))
523                         || (! (SvROK (*tmp) && (SVt_PVAV == SvTYPE (SvRV (*tmp)))))) {
524                 log_err ("pplugin_dispatch_values: No valid values given.");
525                 return -1;
526         }
527
528         {
529                 AV  *array = (AV *)SvRV (*tmp);
530                 int len    = av_len (array) + 1;
531
532                 if (len <= 0)
533                         return -1;
534
535                 val = (value_t *)smalloc (len * sizeof (value_t));
536
537                 list.values_len = av2value (aTHX_ name, (AV *)SvRV (*tmp), val, len);
538                 list.values = val;
539
540                 if (-1 == list.values_len) {
541                         sfree (val);
542                         return -1;
543                 }
544         }
545
546         if (NULL != (tmp = hv_fetch (values, "time", 4, 0))) {
547                 list.time = (time_t)SvIV (*tmp);
548         }
549         else {
550                 list.time = time (NULL);
551         }
552
553         if (NULL != (tmp = hv_fetch (values, "host", 4, 0))) {
554                 strncpy (list.host, SvPV_nolen (*tmp), DATA_MAX_NAME_LEN);
555                 list.host[DATA_MAX_NAME_LEN - 1] = '\0';
556         }
557         else {
558                 strcpy (list.host, hostname_g);
559         }
560
561         if (NULL != (tmp = hv_fetch (values, "plugin", 6, 0))) {
562                 strncpy (list.plugin, SvPV_nolen (*tmp), DATA_MAX_NAME_LEN);
563                 list.plugin[DATA_MAX_NAME_LEN - 1] = '\0';
564         }
565
566         if (NULL != (tmp = hv_fetch (values, "plugin_instance", 15, 0))) {
567                 strncpy (list.plugin_instance, SvPV_nolen (*tmp), DATA_MAX_NAME_LEN);
568                 list.plugin_instance[DATA_MAX_NAME_LEN - 1] = '\0';
569         }
570
571         if (NULL != (tmp = hv_fetch (values, "type_instance", 13, 0))) {
572                 strncpy (list.type_instance, SvPV_nolen (*tmp), DATA_MAX_NAME_LEN);
573                 list.type_instance[DATA_MAX_NAME_LEN - 1] = '\0';
574         }
575
576         ret = plugin_dispatch_values (name, &list);
577
578         sfree (val);
579         return ret;
580 } /* static int pplugin_dispatch_values (char *, HV *) */
581
582 /*
583  * Dispatch a notification.
584  *
585  * notification:
586  * {
587  *   severity => $severity,
588  *   time     => $time,
589  *   message  => $msg,
590  *   host     => $host,
591  *   plugin   => $plugin,
592  *   type     => $type,
593  *   plugin_instance => $instance,
594  *   type_instance   => $type_instance
595  * }
596  */
597 static int pplugin_dispatch_notification (pTHX_ HV *notif)
598 {
599         notification_t n;
600
601         SV **tmp = NULL;
602
603         if (NULL == notif)
604                 return -1;
605
606         memset (&n, 0, sizeof (n));
607
608         if (NULL != (tmp = hv_fetch (notif, "severity", 8, 0)))
609                 n.severity = SvIV (*tmp);
610         else
611                 n.severity = NOTIF_FAILURE;
612
613         if (NULL != (tmp = hv_fetch (notif, "time", 4, 0)))
614                 n.time = (time_t)SvIV (*tmp);
615         else
616                 n.time = time (NULL);
617
618         if (NULL != (tmp = hv_fetch (notif, "message", 7, 0)))
619                 strncpy (n.message, SvPV_nolen (*tmp), sizeof (n.message));
620         n.message[sizeof (n.message) - 1] = '\0';
621
622         if (NULL != (tmp = hv_fetch (notif, "host", 4, 0)))
623                 strncpy (n.host, SvPV_nolen (*tmp), sizeof (n.host));
624         else
625                 strncpy (n.host, hostname_g, sizeof (n.host));
626         n.host[sizeof (n.host) - 1] = '\0';
627
628         if (NULL != (tmp = hv_fetch (notif, "plugin", 6, 0)))
629                 strncpy (n.plugin, SvPV_nolen (*tmp), sizeof (n.plugin));
630         n.plugin[sizeof (n.plugin) - 1] = '\0';
631
632         if (NULL != (tmp = hv_fetch (notif, "plugin_instance", 15, 0)))
633                 strncpy (n.plugin_instance, SvPV_nolen (*tmp),
634                                 sizeof (n.plugin_instance));
635         n.plugin_instance[sizeof (n.plugin_instance) - 1] = '\0';
636
637         if (NULL != (tmp = hv_fetch (notif, "type", 4, 0)))
638                 strncpy (n.type, SvPV_nolen (*tmp), sizeof (n.type));
639         n.type[sizeof (n.type) - 1] = '\0';
640
641         if (NULL != (tmp = hv_fetch (notif, "type_instance", 13, 0)))
642                 strncpy (n.type_instance, SvPV_nolen (*tmp), sizeof (n.type_instance));
643         n.type_instance[sizeof (n.type_instance) - 1] = '\0';
644         return plugin_dispatch_notification (&n);
645 } /* static int pplugin_dispatch_notification (HV *) */
646
647 /*
648  * Call all working functions of the given type.
649  */
650 static int pplugin_call_all (pTHX_ int type, ...)
651 {
652         int retvals = 0;
653
654         va_list ap;
655         int ret = 0;
656
657         dSP;
658
659         if ((type < 0) || (type >= PLUGIN_TYPES))
660                 return -1;
661
662         va_start (ap, type);
663
664         ENTER;
665         SAVETMPS;
666
667         PUSHMARK (SP);
668
669         XPUSHs (sv_2mortal (newSViv ((IV)type)));
670
671         if (PLUGIN_WRITE == type) {
672                 /*
673                  * $_[0] = $plugin_type;
674                  *
675                  * $_[1] =
676                  * [
677                  *   {
678                  *     name => $ds_name,
679                  *     type => $ds_type,
680                  *     min  => $ds_min,
681                  *     max  => $ds_max
682                  *   },
683                  *   ...
684                  * ];
685                  *
686                  * $_[2] =
687                  * {
688                  *   values => [ $v1, ... ],
689                  *   time   => $time,
690                  *   host   => $hostname,
691                  *   plugin => $plugin,
692                  *   plugin_instance => $instance,
693                  *   type_instance   => $type_instance
694                  * };
695                  */
696                 data_set_t   *ds;
697                 value_list_t *vl;
698
699                 AV *pds = newAV ();
700                 HV *pvl = newHV ();
701
702                 ds = va_arg (ap, data_set_t *);
703                 vl = va_arg (ap, value_list_t *);
704
705                 if (-1 == data_set2av (aTHX_ ds, pds)) {
706                         av_clear (pds);
707                         av_undef (pds);
708                         pds = Nullav;
709                         ret = -1;
710                 }
711
712                 if (-1 == value_list2hv (aTHX_ vl, ds, pvl)) {
713                         hv_clear (pvl);
714                         hv_undef (pvl);
715                         pvl = Nullhv;
716                         ret = -1;
717                 }
718
719                 XPUSHs (sv_2mortal (newSVpv (ds->type, 0)));
720                 XPUSHs (sv_2mortal (newRV_noinc ((SV *)pds)));
721                 XPUSHs (sv_2mortal (newRV_noinc ((SV *)pvl)));
722         }
723         else if (PLUGIN_LOG == type) {
724                 /*
725                  * $_[0] = $level;
726                  *
727                  * $_[1] = $message;
728                  */
729                 XPUSHs (sv_2mortal (newSViv (va_arg (ap, int))));
730                 XPUSHs (sv_2mortal (newSVpv (va_arg (ap, char *), 0)));
731         }
732         else if (PLUGIN_NOTIF == type) {
733                 /*
734                  * $_[0] =
735                  * {
736                  *   severity => $severity,
737                  *   time     => $time,
738                  *   message  => $msg,
739                  *   host     => $host,
740                  *   plugin   => $plugin,
741                  *   type     => $type,
742                  *   plugin_instance => $instance,
743                  *   type_instance   => $type_instance
744                  * };
745                  */
746                 notification_t *n;
747                 HV *notif = newHV ();
748
749                 n = va_arg (ap, notification_t *);
750
751                 if (-1 == notification2hv (aTHX_ n, notif)) {
752                         hv_clear (notif);
753                         hv_undef (notif);
754                         notif = Nullhv;
755                         ret = -1;
756                 }
757
758                 XPUSHs (sv_2mortal (newRV_noinc ((SV *)notif)));
759         }
760         else if (PLUGIN_FLUSH == type) {
761                 /*
762                  * $_[0] = $timeout;
763                  */
764                 XPUSHs (sv_2mortal (newSViv (va_arg (ap, int))));
765         }
766
767         PUTBACK;
768
769         retvals = call_pv ("Collectd::plugin_call_all", G_SCALAR);
770
771         SPAGAIN;
772         if (0 < retvals) {
773                 SV *tmp = POPs;
774                 if (! SvTRUE (tmp))
775                         ret = -1;
776         }
777
778         PUTBACK;
779         FREETMPS;
780         LEAVE;
781
782         va_end (ap);
783         return ret;
784 } /* static int pplugin_call_all (int, ...) */
785
786 /*
787  * Exported Perl API.
788  */
789
790 /*
791  * Collectd::plugin_register_data_set (type, dataset).
792  *
793  * type:
794  *   type of the dataset
795  *
796  * dataset:
797  *   dataset to be registered
798  */
799 static XS (Collectd_plugin_register_ds)
800 {
801         SV  *data = NULL;
802         int ret   = 0;
803
804         dXSARGS;
805
806         if (2 != items) {
807                 log_err ("Usage: Collectd::plugin_register_data_set(type, dataset)");
808                 XSRETURN_EMPTY;
809         }
810
811         log_debug ("Collectd::plugin_register_data_set: "
812                         "type = \"%s\", dataset = \"%s\"",
813                         SvPV_nolen (ST (0)), SvPV_nolen (ST (1)));
814
815         data = ST (1);
816
817         if (SvROK (data) && (SVt_PVAV == SvTYPE (SvRV (data)))) {
818                 ret = pplugin_register_data_set (aTHX_ SvPV_nolen (ST (0)),
819                                 (AV *)SvRV (data));
820         }
821         else {
822                 log_err ("Collectd::plugin_register_data_set: Invalid data.");
823                 XSRETURN_EMPTY;
824         }
825
826         if (0 == ret)
827                 XSRETURN_YES;
828         else
829                 XSRETURN_EMPTY;
830 } /* static XS (Collectd_plugin_register_ds) */
831
832 /*
833  * Collectd::plugin_unregister_data_set (type).
834  *
835  * type:
836  *   type of the dataset
837  */
838 static XS (Collectd_plugin_unregister_ds)
839 {
840         dXSARGS;
841
842         if (1 != items) {
843                 log_err ("Usage: Collectd::plugin_unregister_data_set(type)");
844                 XSRETURN_EMPTY;
845         }
846
847         log_debug ("Collectd::plugin_unregister_data_set: type = \"%s\"",
848                         SvPV_nolen (ST (0)));
849
850         if (0 == pplugin_unregister_data_set (SvPV_nolen (ST (0))))
851                 XSRETURN_YES;
852         else
853                 XSRETURN_EMPTY;
854 } /* static XS (Collectd_plugin_register_ds) */
855
856 /*
857  * Collectd::plugin_dispatch_values (name, values).
858  *
859  * name:
860  *   name of the plugin
861  *
862  * values:
863  *   value list to submit
864  */
865 static XS (Collectd_plugin_dispatch_values)
866 {
867         SV *values = NULL;
868
869         int ret = 0;
870
871         dXSARGS;
872
873         if (2 != items) {
874                 log_err ("Usage: Collectd::plugin_dispatch_values(name, values)");
875                 XSRETURN_EMPTY;
876         }
877
878         log_debug ("Collectd::plugin_dispatch_values: "
879                         "name = \"%s\", values=\"%s\"",
880                         SvPV_nolen (ST (0)), SvPV_nolen (ST (1)));
881
882         values = ST (1);
883
884         if (! (SvROK (values) && (SVt_PVHV == SvTYPE (SvRV (values))))) {
885                 log_err ("Collectd::plugin_dispatch_values: Invalid values.");
886                 XSRETURN_EMPTY;
887         }
888
889         if ((NULL == ST (0)) || (NULL == values))
890                 XSRETURN_EMPTY;
891
892         ret = pplugin_dispatch_values (aTHX_ SvPV_nolen (ST (0)),
893                         (HV *)SvRV (values));
894
895         if (0 == ret)
896                 XSRETURN_YES;
897         else
898                 XSRETURN_EMPTY;
899 } /* static XS (Collectd_plugin_dispatch_values) */
900
901 /*
902  * Collectd::plugin_dispatch_notification (notif).
903  *
904  * notif:
905  *   notification to dispatch
906  */
907 static XS (Collectd_plugin_dispatch_notification)
908 {
909         SV *notif = NULL;
910
911         int ret = 0;
912
913         dXSARGS;
914
915         if (1 != items) {
916                 log_err ("Usage: Collectd::plugin_dispatch_notification(notif)");
917                 XSRETURN_EMPTY;
918         }
919
920         log_debug ("Collectd::plugin_dispatch_notification: notif = \"%s\"",
921                         SvPV_nolen (ST (0)));
922
923         notif = ST (0);
924
925         if (! (SvROK (notif) && (SVt_PVHV == SvTYPE (SvRV (notif))))) {
926                 log_err ("Collectd::plugin_dispatch_notification: Invalid notif.");
927                 XSRETURN_EMPTY;
928         }
929
930         ret = pplugin_dispatch_notification (aTHX_ (HV *)SvRV (notif));
931
932         if (0 == ret)
933                 XSRETURN_YES;
934         else
935                 XSRETURN_EMPTY;
936 } /* static XS (Collectd_plugin_dispatch_notification) */
937
938 /*
939  * Collectd::plugin_log (level, message).
940  *
941  * level:
942  *   log level (LOG_DEBUG, ... LOG_ERR)
943  *
944  * message:
945  *   log message
946  */
947 static XS (Collectd_plugin_log)
948 {
949         dXSARGS;
950
951         if (2 != items) {
952                 log_err ("Usage: Collectd::plugin_log(level, message)");
953                 XSRETURN_EMPTY;
954         }
955
956         plugin_log (SvIV (ST (0)), SvPV_nolen (ST (1)));
957         XSRETURN_YES;
958 } /* static XS (Collectd_plugin_log) */
959
960 /*
961  * Collectd::call_by_name (...).
962  *
963  * Call a Perl sub identified by its name passed through $Collectd::cb_name.
964  */
965 static XS (Collectd_call_by_name)
966 {
967         SV   *tmp  = NULL;
968         char *name = NULL;
969
970         if (NULL == (tmp = get_sv ("Collectd::cb_name", 0))) {
971                 sv_setpv (get_sv ("@", 1), "cb_name has not been set");
972                 CLEAR_STACK_FRAME;
973                 return;
974         }
975
976         name = SvPV_nolen (tmp);
977
978         if (NULL == get_cv (name, 0)) {
979                 sv_setpvf (get_sv ("@", 1), "unknown callback \"%s\"", name);
980                 CLEAR_STACK_FRAME;
981                 return;
982         }
983
984         /* simply pass on the subroutine call without touching the stack,
985          * thus leaving any arguments and return values in place */
986         call_pv (name, 0);
987 } /* static XS (Collectd_call_by_name) */
988
989 /*
990  * collectd's perl interpreter based thread implementation.
991  *
992  * This has been inspired by Perl's ithreads introduced in version 5.6.0.
993  */
994
995 /* must be called with perl_threads->mutex locked */
996 static void c_ithread_destroy (c_ithread_t *ithread)
997 {
998         dTHXa (ithread->interp);
999
1000         assert (NULL != perl_threads);
1001
1002         PERL_SET_CONTEXT (aTHX);
1003         log_debug ("Shutting down Perl interpreter %p...", aTHX);
1004
1005 #if COLLECT_DEBUG
1006         sv_report_used ();
1007
1008         --perl_threads->number_of_threads;
1009 #endif /* COLLECT_DEBUG */
1010
1011         perl_destruct (aTHX);
1012         perl_free (aTHX);
1013
1014         if (NULL == ithread->prev)
1015                 perl_threads->head = ithread->next;
1016         else
1017                 ithread->prev->next = ithread->next;
1018
1019         if (NULL == ithread->next)
1020                 perl_threads->tail = ithread->prev;
1021         else
1022                 ithread->next->prev = ithread->prev;
1023
1024         sfree (ithread);
1025         return;
1026 } /* static void c_ithread_destroy (c_ithread_t *) */
1027
1028 static void c_ithread_destructor (void *arg)
1029 {
1030         c_ithread_t *ithread = (c_ithread_t *)arg;
1031         c_ithread_t *t = NULL;
1032
1033         if (NULL == perl_threads)
1034                 return;
1035
1036         pthread_mutex_lock (&perl_threads->mutex);
1037
1038         for (t = perl_threads->head; NULL != t; t = t->next)
1039                 if (t == ithread)
1040                         break;
1041
1042         /* the ithread no longer exists */
1043         if (NULL == t)
1044                 return;
1045
1046         c_ithread_destroy (ithread);
1047
1048         pthread_mutex_unlock (&perl_threads->mutex);
1049         return;
1050 } /* static void c_ithread_destructor (void *) */
1051
1052 /* must be called with perl_threads->mutex locked */
1053 static c_ithread_t *c_ithread_create (PerlInterpreter *base)
1054 {
1055         c_ithread_t *t = NULL;
1056         dTHXa (NULL);
1057
1058         assert (NULL != perl_threads);
1059
1060         t = (c_ithread_t *)smalloc (sizeof (c_ithread_t));
1061         memset (t, 0, sizeof (c_ithread_t));
1062
1063         t->interp = (NULL == base)
1064                 ? NULL
1065                 : perl_clone (base, CLONEf_KEEP_PTR_TABLE);
1066
1067         aTHX = t->interp;
1068
1069         if (NULL != base) {
1070                 av_clear (PL_endav);
1071                 av_undef (PL_endav);
1072                 PL_endav = Nullav;
1073         }
1074
1075 #if COLLECT_DEBUG
1076         ++perl_threads->number_of_threads;
1077 #endif /* COLLECT_DEBUG */
1078
1079         t->next = NULL;
1080
1081         if (NULL == perl_threads->tail) {
1082                 perl_threads->head = t;
1083                 t->prev = NULL;
1084         }
1085         else {
1086                 perl_threads->tail->next = t;
1087                 t->prev = perl_threads->tail;
1088         }
1089
1090         perl_threads->tail = t;
1091
1092         pthread_setspecific (perl_thr_key, (const void *)t);
1093         return t;
1094 } /* static c_ithread_t *c_ithread_create (PerlInterpreter *) */
1095
1096 /*
1097  * Interface to collectd.
1098  */
1099
1100 static int perl_init (void)
1101 {
1102         dTHX;
1103
1104         if (NULL == perl_threads)
1105                 return 0;
1106
1107         if (NULL == aTHX) {
1108                 c_ithread_t *t = NULL;
1109
1110                 pthread_mutex_lock (&perl_threads->mutex);
1111                 t = c_ithread_create (perl_threads->head->interp);
1112                 pthread_mutex_unlock (&perl_threads->mutex);
1113
1114                 aTHX = t->interp;
1115         }
1116
1117         log_debug ("perl_init: c_ithread: interp = %p (active threads: %i)",
1118                         aTHX, perl_threads->number_of_threads);
1119         return pplugin_call_all (aTHX_ PLUGIN_INIT);
1120 } /* static int perl_init (void) */
1121
1122 static int perl_read (void)
1123 {
1124         dTHX;
1125
1126         if (NULL == perl_threads)
1127                 return 0;
1128
1129         if (NULL == aTHX) {
1130                 c_ithread_t *t = NULL;
1131
1132                 pthread_mutex_lock (&perl_threads->mutex);
1133                 t = c_ithread_create (perl_threads->head->interp);
1134                 pthread_mutex_unlock (&perl_threads->mutex);
1135
1136                 aTHX = t->interp;
1137         }
1138
1139         log_debug ("perl_read: c_ithread: interp = %p (active threads: %i)",
1140                         aTHX, perl_threads->number_of_threads);
1141         return pplugin_call_all (aTHX_ PLUGIN_READ);
1142 } /* static int perl_read (void) */
1143
1144 static int perl_write (const data_set_t *ds, const value_list_t *vl)
1145 {
1146         dTHX;
1147
1148         if (NULL == perl_threads)
1149                 return 0;
1150
1151         if (NULL == aTHX) {
1152                 c_ithread_t *t = NULL;
1153
1154                 pthread_mutex_lock (&perl_threads->mutex);
1155                 t = c_ithread_create (perl_threads->head->interp);
1156                 pthread_mutex_unlock (&perl_threads->mutex);
1157
1158                 aTHX = t->interp;
1159         }
1160
1161         log_debug ("perl_write: c_ithread: interp = %p (active threads: %i)",
1162                         aTHX, perl_threads->number_of_threads);
1163         return pplugin_call_all (aTHX_ PLUGIN_WRITE, ds, vl);
1164 } /* static int perl_write (const data_set_t *, const value_list_t *) */
1165
1166 static void perl_log (int level, const char *msg)
1167 {
1168         dTHX;
1169
1170         if (NULL == perl_threads)
1171                 return;
1172
1173         if (NULL == aTHX) {
1174                 c_ithread_t *t = NULL;
1175
1176                 pthread_mutex_lock (&perl_threads->mutex);
1177                 t = c_ithread_create (perl_threads->head->interp);
1178                 pthread_mutex_unlock (&perl_threads->mutex);
1179
1180                 aTHX = t->interp;
1181         }
1182
1183         pplugin_call_all (aTHX_ PLUGIN_LOG, level, msg);
1184         return;
1185 } /* static void perl_log (int, const char *) */
1186
1187 static int perl_notify (const notification_t *notif)
1188 {
1189         dTHX;
1190
1191         if (NULL == perl_threads)
1192                 return 0;
1193
1194         if (NULL == aTHX) {
1195                 c_ithread_t *t = NULL;
1196
1197                 pthread_mutex_lock (&perl_threads->mutex);
1198                 t = c_ithread_create (perl_threads->head->interp);
1199                 pthread_mutex_unlock (&perl_threads->mutex);
1200
1201                 aTHX = t->interp;
1202         }
1203         return pplugin_call_all (aTHX_ PLUGIN_NOTIF, notif);
1204 } /* static int perl_notify (const notification_t *) */
1205
1206 static int perl_flush (const int timeout)
1207 {
1208         dTHX;
1209
1210         if (NULL == perl_threads)
1211                 return 0;
1212
1213         if (NULL == aTHX) {
1214                 c_ithread_t *t = NULL;
1215
1216                 pthread_mutex_lock (&perl_threads->mutex);
1217                 t = c_ithread_create (perl_threads->head->interp);
1218                 pthread_mutex_unlock (&perl_threads->mutex);
1219
1220                 aTHX = t->interp;
1221         }
1222         return pplugin_call_all (aTHX_ PLUGIN_FLUSH, timeout);
1223 } /* static int perl_flush (const int) */
1224
1225 static int perl_shutdown (void)
1226 {
1227         c_ithread_t *t = NULL;
1228
1229         int ret = 0;
1230
1231         dTHX;
1232
1233         plugin_unregister_complex_config ("perl");
1234
1235         if (NULL == perl_threads)
1236                 return 0;
1237
1238         if (NULL == aTHX) {
1239                 c_ithread_t *t = NULL;
1240
1241                 pthread_mutex_lock (&perl_threads->mutex);
1242                 t = c_ithread_create (perl_threads->head->interp);
1243                 pthread_mutex_unlock (&perl_threads->mutex);
1244
1245                 aTHX = t->interp;
1246         }
1247
1248         log_debug ("perl_shutdown: c_ithread: interp = %p (active threads: %i)",
1249                         aTHX, perl_threads->number_of_threads);
1250
1251         plugin_unregister_log ("perl");
1252         plugin_unregister_notification ("perl");
1253         plugin_unregister_init ("perl");
1254         plugin_unregister_read ("perl");
1255         plugin_unregister_write ("perl");
1256         plugin_unregister_flush ("perl");
1257
1258         ret = pplugin_call_all (aTHX_ PLUGIN_SHUTDOWN);
1259
1260         pthread_mutex_lock (&perl_threads->mutex);
1261         t = perl_threads->tail;
1262
1263         while (NULL != t) {
1264                 c_ithread_t *thr = t;
1265
1266                 /* the pointer has to be advanced before destroying
1267                  * the thread as this will free the memory */
1268                 t = t->prev;
1269
1270                 c_ithread_destroy (thr);
1271         }
1272
1273         pthread_mutex_unlock (&perl_threads->mutex);
1274         pthread_mutex_destroy (&perl_threads->mutex);
1275
1276         sfree (perl_threads);
1277
1278         pthread_key_delete (perl_thr_key);
1279
1280         PERL_SYS_TERM ();
1281
1282         plugin_unregister_shutdown ("perl");
1283         return ret;
1284 } /* static void perl_shutdown (void) */
1285
1286 /*
1287  * Access functions for global variables.
1288  *
1289  * These functions implement the "magic" used to access
1290  * the global variables from Perl.
1291  */
1292
1293 static int g_pv_get (pTHX_ SV *var, MAGIC *mg)
1294 {
1295         char *pv = mg->mg_ptr;
1296         sv_setpv (var, pv);
1297         return 0;
1298 } /* static int g_pv_get (pTHX_ SV *, MAGIC *) */
1299
1300 static int g_pv_set (pTHX_ SV *var, MAGIC *mg)
1301 {
1302         char *pv = mg->mg_ptr;
1303         strncpy (pv, SvPV_nolen (var), DATA_MAX_NAME_LEN);
1304         pv[DATA_MAX_NAME_LEN - 1] = '\0';
1305         return 0;
1306 } /* static int g_pv_set (pTHX_ SV *, MAGIC *) */
1307
1308 static int g_iv_get (pTHX_ SV *var, MAGIC *mg)
1309 {
1310         int *iv = (int *)mg->mg_ptr;
1311         sv_setiv (var, *iv);
1312         return 0;
1313 } /* static int g_iv_get (pTHX_ SV *, MAGIC *) */
1314
1315 static int g_iv_set (pTHX_ SV *var, MAGIC *mg)
1316 {
1317         int *iv = (int *)mg->mg_ptr;
1318         *iv = (int)SvIV (var);
1319         return 0;
1320 } /* static int g_iv_set (pTHX_ SV *, MAGIC *) */
1321
1322 static MGVTBL g_pv_vtbl = { g_pv_get, g_pv_set, NULL, NULL, NULL, NULL, NULL };
1323 static MGVTBL g_iv_vtbl = { g_iv_get, g_iv_set, NULL, NULL, NULL, NULL, NULL };
1324
1325 /* bootstrap the Collectd module */
1326 static void xs_init (pTHX)
1327 {
1328         HV   *stash = NULL;
1329         SV   *tmp   = NULL;
1330         char *file  = __FILE__;
1331
1332         int i = 0;
1333
1334         dXSUB_SYS;
1335
1336         /* enable usage of Perl modules using shared libraries */
1337         newXS ("DynaLoader::boot_DynaLoader", boot_DynaLoader, file);
1338
1339         /* register API */
1340         for (i = 0; NULL != api[i].f; ++i)
1341                 newXS (api[i].name, api[i].f, file);
1342
1343         stash = gv_stashpv ("Collectd", 1);
1344
1345         /* export "constants" */
1346         for (i = 0; '\0' != constants[i].name[0]; ++i)
1347                 newCONSTSUB (stash, constants[i].name, newSViv (constants[i].value));
1348
1349         /* export global variables
1350          * by adding "magic" to the SV's representing the globale variables
1351          * perl is able to automagically call the get/set function when
1352          * accessing any such variable (this is basically the same as using
1353          * tie() in Perl) */
1354         /* global strings */
1355         for (i = 0; '\0' != g_strings[i].name[0]; ++i) {
1356                 tmp = get_sv (g_strings[i].name, 1);
1357                 sv_magicext (tmp, NULL, PERL_MAGIC_ext, &g_pv_vtbl,
1358                                 g_strings[i].var, 0);
1359         }
1360
1361         /* global integers */
1362         for (i = 0; '\0' != g_integers[i].name[0]; ++i) {
1363                 tmp = get_sv (g_integers[i].name, 1);
1364                 sv_magicext (tmp, NULL, PERL_MAGIC_ext, &g_iv_vtbl,
1365                                 (char *)g_integers[i].var, 0);
1366         }
1367         return;
1368 } /* static void xs_init (pTHX) */
1369
1370 /* Initialize the global Perl interpreter. */
1371 static int init_pi (int argc, char **argv)
1372 {
1373         dTHXa (NULL);
1374
1375         if (NULL != perl_threads)
1376                 return 0;
1377
1378         log_info ("Initializing Perl interpreter...");
1379 #if COLLECT_DEBUG
1380         {
1381                 int i = 0;
1382
1383                 for (i = 0; i < argc; ++i)
1384                         log_debug ("argv[%i] = \"%s\"", i, argv[i]);
1385         }
1386 #endif /* COLLECT_DEBUG */
1387
1388         if (0 != pthread_key_create (&perl_thr_key, c_ithread_destructor)) {
1389                 log_err ("init_pi: pthread_key_create failed");
1390
1391                 /* this must not happen - cowardly giving up if it does */
1392                 exit (1);
1393         }
1394
1395         PERL_SYS_INIT3 (&argc, &argv, &environ);
1396
1397         perl_threads = (c_ithread_list_t *)smalloc (sizeof (c_ithread_list_t));
1398         memset (perl_threads, 0, sizeof (c_ithread_list_t));
1399
1400         pthread_mutex_init (&perl_threads->mutex, NULL);
1401         /* locking the mutex should not be necessary at this point
1402          * but let's just do it for the sake of completeness */
1403         pthread_mutex_lock (&perl_threads->mutex);
1404
1405         perl_threads->head = c_ithread_create (NULL);
1406         perl_threads->tail = perl_threads->head;
1407
1408         if (NULL == (perl_threads->head->interp = perl_alloc ())) {
1409                 log_err ("init_pi: Not enough memory.");
1410                 exit (3);
1411         }
1412
1413         aTHX = perl_threads->head->interp;
1414         pthread_mutex_unlock (&perl_threads->mutex);
1415
1416         perl_construct (aTHX);
1417
1418         PL_exit_flags |= PERL_EXIT_DESTRUCT_END;
1419
1420         if (0 != perl_parse (aTHX_ xs_init, argc, argv, NULL)) {
1421                 log_err ("init_pi: Unable to bootstrap Collectd.");
1422                 exit (1);
1423         }
1424
1425         /* Set $0 to "collectd" because perl_parse() has to set it to "-e". */
1426         sv_setpv (get_sv ("0", 0), "collectd");
1427
1428         perl_run (aTHX);
1429
1430         plugin_register_log ("perl", perl_log);
1431         plugin_register_notification ("perl", perl_notify);
1432         plugin_register_init ("perl", perl_init);
1433
1434         plugin_register_read ("perl", perl_read);
1435
1436         plugin_register_write ("perl", perl_write);
1437         plugin_register_flush ("perl", perl_flush);
1438         plugin_register_shutdown ("perl", perl_shutdown);
1439         return 0;
1440 } /* static int init_pi (const char **, const int) */
1441
1442 /*
1443  * LoadPlugin "<Plugin>"
1444  */
1445 static int perl_config_loadplugin (pTHX_ oconfig_item_t *ci)
1446 {
1447         char module_name[DATA_MAX_NAME_LEN];
1448
1449         char *value = NULL;
1450
1451         if ((0 != ci->children_num) || (1 != ci->values_num)
1452                         || (OCONFIG_TYPE_STRING != ci->values[0].type)) {
1453                 log_err ("LoadPlugin expects a single string argument.");
1454                 return 1;
1455         }
1456
1457         value = ci->values[0].value.string;
1458
1459         if (NULL == get_module_name (module_name, sizeof (module_name), value)) {
1460                 log_err ("Invalid module name %s", value);
1461                 return (1);
1462         }
1463
1464         init_pi (perl_argc, perl_argv);
1465         assert (NULL != perl_threads);
1466         assert (NULL != perl_threads->head);
1467
1468         aTHX = perl_threads->head->interp;
1469
1470         log_debug ("perl_config: loading perl plugin \"%s\"", value);
1471         load_module (PERL_LOADMOD_NOIMPORT,
1472                         newSVpv (module_name, strlen (module_name)), Nullsv);
1473         return 0;
1474 } /* static int perl_config_loadplugin (oconfig_item_it *) */
1475
1476 /*
1477  * BaseName "<Name>"
1478  */
1479 static int perl_config_basename (pTHX_ oconfig_item_t *ci)
1480 {
1481         char *value = NULL;
1482
1483         if ((0 != ci->children_num) || (1 != ci->values_num)
1484                         || (OCONFIG_TYPE_STRING != ci->values[0].type)) {
1485                 log_err ("BaseName expects a single string argument.");
1486                 return 1;
1487         }
1488
1489         value = ci->values[0].value.string;
1490
1491         log_debug ("perl_config: Setting plugin basename to \"%s\"", value);
1492         strncpy (base_name, value, sizeof (base_name));
1493         base_name[sizeof (base_name) - 1] = '\0';
1494         return 0;
1495 } /* static int perl_config_basename (oconfig_item_it *) */
1496
1497 /*
1498  * EnableDebugger "<Package>"|""
1499  */
1500 static int perl_config_enabledebugger (pTHX_ oconfig_item_t *ci)
1501 {
1502         char *value = NULL;
1503
1504         if ((0 != ci->children_num) || (1 != ci->values_num)
1505                         || (OCONFIG_TYPE_STRING != ci->values[0].type)) {
1506                 log_err ("EnableDebugger expects a single string argument.");
1507                 return 1;
1508         }
1509
1510         if (NULL != perl_threads) {
1511                 log_warn ("EnableDebugger has no effects if used after LoadPlugin.");
1512                 return 1;
1513         }
1514
1515         value = ci->values[0].value.string;
1516
1517         perl_argv = (char **)realloc (perl_argv,
1518                         (++perl_argc + 1) * sizeof (char *));
1519
1520         if (NULL == perl_argv) {
1521                 log_err ("perl_config: Not enough memory.");
1522                 exit (3);
1523         }
1524
1525         if ('\0' == value[0]) {
1526                 perl_argv[perl_argc - 1] = "-d";
1527         }
1528         else {
1529                 perl_argv[perl_argc - 1] = (char *)smalloc (strlen (value) + 4);
1530                 sstrncpy (perl_argv[perl_argc - 1], "-d:", 4);
1531                 sstrncpy (perl_argv[perl_argc - 1] + 3, value, strlen (value) + 1);
1532         }
1533
1534         perl_argv[perl_argc] = NULL;
1535         return 0;
1536 } /* static int perl_config_enabledebugger (oconfig_item_it *) */
1537
1538 /*
1539  * IncludeDir "<Dir>"
1540  */
1541 static int perl_config_includedir (pTHX_ oconfig_item_t *ci)
1542 {
1543         char *value = NULL;
1544
1545         if ((0 != ci->children_num) || (1 != ci->values_num)
1546                         || (OCONFIG_TYPE_STRING != ci->values[0].type)) {
1547                 log_err ("IncludeDir expects a single string argument.");
1548                 return 1;
1549         }
1550
1551         value = ci->values[0].value.string;
1552
1553         if (NULL == aTHX) {
1554                 perl_argv = (char **)realloc (perl_argv,
1555                                 (++perl_argc + 1) * sizeof (char *));
1556
1557                 if (NULL == perl_argv) {
1558                         log_err ("perl_config: Not enough memory.");
1559                         exit (3);
1560                 }
1561
1562                 perl_argv[perl_argc - 1] = (char *)smalloc (strlen (value) + 3);
1563                 sstrncpy(perl_argv[perl_argc - 1], "-I", 3);
1564                 sstrncpy(perl_argv[perl_argc - 1] + 2, value, strlen (value) + 1);
1565
1566                 perl_argv[perl_argc] = NULL;
1567         }
1568         else {
1569                 /* prepend the directory to @INC */
1570                 av_unshift (GvAVn (PL_incgv), 1);
1571                 av_store (GvAVn (PL_incgv), 0, newSVpv (value, strlen (value)));
1572         }
1573         return 0;
1574 } /* static int perl_config_includedir (oconfig_item_it *) */
1575
1576 static int perl_config (oconfig_item_t *ci)
1577 {
1578         int i = 0;
1579
1580         dTHX;
1581
1582         /* dTHX does not get any valid values in case Perl
1583          * has not been initialized */
1584         if (NULL == perl_threads)
1585                 aTHX = NULL;
1586
1587         for (i = 0; i < ci->children_num; ++i) {
1588                 oconfig_item_t *c = ci->children + i;
1589
1590                 if (0 == strcasecmp (c->key, "LoadPlugin"))
1591                         perl_config_loadplugin (aTHX_ c);
1592                 else if (0 == strcasecmp (c->key, "BaseName"))
1593                         perl_config_basename (aTHX_ c);
1594                 else if (0 == strcasecmp (c->key, "EnableDebugger"))
1595                         perl_config_enabledebugger (aTHX_ c);
1596                 else if (0 == strcasecmp (c->key, "IncludeDir"))
1597                         perl_config_includedir (aTHX_ c);
1598                 else
1599                         log_warn ("Ignoring unknown config key \"%s\".", c->key);
1600         }
1601         return 0;
1602 } /* static int perl_config (oconfig_item_t *) */
1603
1604 void module_register (void)
1605 {
1606         perl_argc = 4;
1607         perl_argv = (char **)smalloc ((perl_argc + 1) * sizeof (char *));
1608
1609         /* default options for the Perl interpreter */
1610         perl_argv[0] = "";
1611         perl_argv[1] = "-MCollectd";
1612         perl_argv[2] = "-e";
1613         perl_argv[3] = "1";
1614         perl_argv[4] = NULL;
1615
1616         plugin_register_complex_config ("perl", perl_config);
1617         return;
1618 } /* void module_register (void) */
1619
1620 /* vim: set sw=4 ts=4 tw=78 noexpandtab : */
1621