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