c0beabfe51ca0ffd8f1b1c9f4810d0bf2e61573c
[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 #include "collectd.h"
28
29 #include "configfile.h"
30
31 #include <EXTERN.h>
32 #include <perl.h>
33
34 #include <XSUB.h>
35
36 /* Some versions of Perl define their own version of DEBUG... :-/ */
37 #ifdef DEBUG
38 # undef DEBUG
39 #endif /* DEBUG */
40
41 /* ... while we want the definition found in plugin.h. */
42 #include "plugin.h"
43 #include "common.h"
44
45 #define PLUGIN_INIT     0
46 #define PLUGIN_READ     1
47 #define PLUGIN_WRITE    2
48 #define PLUGIN_SHUTDOWN 3
49 #define PLUGIN_LOG      4
50
51 #define PLUGIN_TYPES    5
52
53 #define PLUGIN_DATASET  255
54
55 #define log_debug(...) DEBUG ("perl: " __VA_ARGS__)
56 #define log_info(...) INFO ("perl: " __VA_ARGS__)
57 #define log_warn(...) WARNING ("perl: " __VA_ARGS__)
58 #define log_err(...) ERROR ("perl: " __VA_ARGS__)
59
60
61 /* this is defined in DynaLoader.a */
62 void boot_DynaLoader (PerlInterpreter *, CV *);
63
64 static XS (Collectd_plugin_register_ds);
65 static XS (Collectd_plugin_unregister_ds);
66 static XS (Collectd_plugin_dispatch_values);
67 static XS (Collectd_plugin_log);
68
69
70 /*
71  * private data types
72  */
73
74 typedef struct {
75         int len;
76         int *values;
77 } ds_types_t;
78
79
80 /*
81  * private variables
82  */
83
84 static PerlInterpreter *perl = NULL;
85
86 static int  perl_argc   = 0;
87 static char **perl_argv = NULL;
88
89 static char base_name[DATA_MAX_NAME_LEN] = "";
90
91 static HV   *data_sets;
92
93 static struct {
94         char name[64];
95         XS ((*f));
96 } api[] =
97 {
98         { "Collectd::plugin_register_data_set",   Collectd_plugin_register_ds },
99         { "Collectd::plugin_unregister_data_set", Collectd_plugin_unregister_ds },
100         { "Collectd::plugin_dispatch_values",     Collectd_plugin_dispatch_values },
101         { "Collectd::plugin_log",                 Collectd_plugin_log },
102         { "", NULL }
103 };
104
105 struct {
106         char name[64];
107         int  value;
108 } constants[] =
109 {
110         { "Collectd::TYPE_INIT",       PLUGIN_INIT },
111         { "Collectd::TYPE_READ",       PLUGIN_READ },
112         { "Collectd::TYPE_WRITE",      PLUGIN_WRITE },
113         { "Collectd::TYPE_SHUTDOWN",   PLUGIN_SHUTDOWN },
114         { "Collectd::TYPE_LOG",        PLUGIN_LOG },
115         { "Collectd::TYPE_DATASET",    PLUGIN_DATASET },
116         { "Collectd::DS_TYPE_COUNTER", DS_TYPE_COUNTER },
117         { "Collectd::DS_TYPE_GAUGE",   DS_TYPE_GAUGE },
118         { "Collectd::LOG_ERR",         LOG_ERR },
119         { "Collectd::LOG_WARNING",     LOG_WARNING },
120         { "Collectd::LOG_NOTICE",      LOG_NOTICE },
121         { "Collectd::LOG_INFO",        LOG_INFO },
122         { "Collectd::LOG_DEBUG",       LOG_DEBUG },
123         { "", 0 }
124 };
125
126
127 /*
128  * Helper functions for data type conversion.
129  */
130
131 /*
132  * data source:
133  * [
134  *   {
135  *     name => $ds_name,
136  *     type => $ds_type,
137  *     min  => $ds_min,
138  *     max  => $ds_max
139  *   },
140  *   ...
141  * ]
142  */
143 static int hv2data_source (HV *hash, data_source_t *ds)
144 {
145         SV **tmp = NULL;
146
147         if ((NULL == hash) || (NULL == ds))
148                 return -1;
149
150         if (NULL != (tmp = Perl_hv_fetch (perl, hash, "name", 4, 0))) {
151                 strncpy (ds->name, SvPV_nolen (*tmp), DATA_MAX_NAME_LEN);
152                 ds->name[DATA_MAX_NAME_LEN - 1] = '\0';
153         }
154         else {
155                 log_err ("hv2data_source: No DS name given.");
156                 return -1;
157         }
158
159         if (NULL != (tmp = Perl_hv_fetch (perl, hash, "type", 4, 0))) {
160                 ds->type = SvIV (*tmp);
161
162                 if ((DS_TYPE_COUNTER != ds->type) && (DS_TYPE_GAUGE != ds->type)) {
163                         log_err ("hv2data_source: Invalid DS type.");
164                         return -1;
165                 }
166         }
167         else {
168                 ds->type = DS_TYPE_COUNTER;
169         }
170
171         if (NULL != (tmp = Perl_hv_fetch (perl, hash, "min", 3, 0)))
172                 ds->min = SvNV (*tmp);
173         else
174                 ds->min = NAN;
175
176         if (NULL != (tmp = Perl_hv_fetch (perl, hash, "max", 3, 0)))
177                 ds->max = SvNV (*tmp);
178         else
179                 ds->max = NAN;
180         return 0;
181 } /* static data_source_t *hv2data_source (HV *) */
182
183 static int av2value (char *name, AV *array, value_t *value, int len)
184 {
185         SV **tmp = NULL;
186
187         ds_types_t *ds = NULL;
188
189         int i = 0;
190
191         if ((NULL == name) || (NULL == array) || (NULL == value))
192                 return -1;
193
194         if (Perl_av_len (perl, array) < len - 1)
195                 len = Perl_av_len (perl, array) + 1;
196
197         if (0 >= len)
198                 return -1;
199
200         tmp = Perl_hv_fetch (perl, data_sets, name, strlen (name), 0);
201         if (NULL == tmp) {
202                 log_err ("av2value: No dataset for \"%s\".", name);
203                 return -1;
204         }
205         ds = (ds_types_t *)SvIV ((SV *)SvRV (*tmp));
206
207         if (ds->len < len) {
208                 log_warn ("av2value: Value length exceeds data set length.");
209                 len = ds->len;
210         }
211
212         for (i = 0; i < len; ++i) {
213                 SV **tmp = Perl_av_fetch (perl, array, i, 0);
214
215                 if (NULL != tmp) {
216                         if (DS_TYPE_COUNTER == ds->values[i])
217                                 value[i].counter = SvIV (*tmp);
218                         else
219                                 value[i].gauge = SvNV (*tmp);
220                 }
221                 else {
222                         return -1;
223                 }
224         }
225         return len;
226 } /* static int av2value (char *, AV *, value_t *, int) */
227
228 static int data_set2av (data_set_t *ds, AV *array)
229 {
230         int i = 0;
231
232         if ((NULL == ds) || (NULL == array))
233                 return -1;
234
235         Perl_av_extend (perl, array, ds->ds_num);
236
237         for (i = 0; i < ds->ds_num; ++i) {
238                 HV *source = Perl_newHV (perl);
239
240                 if (NULL == Perl_hv_store (perl, source, "name", 4,
241                                 Perl_newSVpv (perl, ds->ds[i].name, 0), 0))
242                         return -1;
243
244                 if (NULL == Perl_hv_store (perl, source, "type", 4,
245                                 Perl_newSViv (perl, ds->ds[i].type), 0))
246                         return -1;
247
248                 if (! isnan (ds->ds[i].min))
249                         if (NULL == Perl_hv_store (perl, source, "min", 3,
250                                         Perl_newSVnv (perl, ds->ds[i].min), 0))
251                                 return -1;
252
253                 if (! isnan (ds->ds[i].max))
254                         if (NULL == Perl_hv_store (perl, source, "max", 3,
255                                         Perl_newSVnv (perl, ds->ds[i].max), 0))
256                                 return -1;
257
258                 if (NULL == Perl_av_store (perl, array, i,
259                                 Perl_newRV_noinc (perl, (SV *)source)))
260                         return -1;
261         }
262         return 0;
263 } /* static int data_set2av (data_set_t *, AV *) */
264
265 static int value_list2hv (value_list_t *vl, data_set_t *ds, HV *hash)
266 {
267         AV *values = NULL;
268
269         int i   = 0;
270         int len = 0;
271
272         if ((NULL == vl) || (NULL == ds) || (NULL == hash))
273                 return -1;
274
275         len = vl->values_len;
276
277         if (ds->ds_num < len) {
278                 log_warn ("value2av: Value length exceeds data set length.");
279                 len = ds->ds_num;
280         }
281
282         values = Perl_newAV (perl);
283         Perl_av_extend (perl, values, len - 1);
284
285         for (i = 0; i < len; ++i) {
286                 SV *val = NULL;
287
288                 if (DS_TYPE_COUNTER == ds->ds[i].type)
289                         val = Perl_newSViv (perl, vl->values[i].counter);
290                 else
291                         val = Perl_newSVnv (perl, vl->values[i].gauge);
292
293                 if (NULL == Perl_av_store (perl, values, i, val)) {
294                         Perl_av_undef (perl, values);
295                         return -1;
296                 }
297         }
298
299         if (NULL == Perl_hv_store (perl, hash, "values", 6,
300                         Perl_newRV_noinc (perl, (SV *)values), 0))
301                 return -1;
302
303         if (0 != vl->time)
304                 if (NULL == Perl_hv_store (perl, hash, "time", 4,
305                                 Perl_newSViv (perl, vl->time), 0))
306                         return -1;
307
308         if ('\0' != vl->host[0])
309                 if (NULL == Perl_hv_store (perl, hash, "host", 4,
310                                 Perl_newSVpv (perl, vl->host, 0), 0))
311                         return -1;
312
313         if ('\0' != vl->plugin[0])
314                 if (NULL == Perl_hv_store (perl, hash, "plugin", 6,
315                                 Perl_newSVpv (perl, vl->plugin, 0), 0))
316                         return -1;
317
318         if ('\0' != vl->plugin_instance[0])
319                 if (NULL == Perl_hv_store (perl, hash, "plugin_instance", 15,
320                                 Perl_newSVpv (perl, vl->plugin_instance, 0), 0))
321                         return -1;
322
323         if ('\0' != vl->type_instance[0])
324                 if (NULL == Perl_hv_store (perl, hash, "type_instance", 13,
325                                 Perl_newSVpv (perl, vl->type_instance, 0), 0))
326                         return -1;
327         return 0;
328 } /* static int value2av (value_list_t *, data_set_t *, HV *) */
329
330
331 /*
332  * Internal functions.
333  */
334
335 static char *get_module_name (char *buf, size_t buf_len, const char *module) {
336         int status = 0;
337         if (base_name[0] == '\0')
338                 status = snprintf (buf, buf_len, "%s", module);
339         else
340                 status = snprintf (buf, buf_len, "%s::%s", base_name, module);
341         if ((status < 0) || (status >= buf_len))
342                 return (NULL);
343         buf[buf_len - 1] = '\0';
344         return (buf);
345 } /* char *get_module_name */
346
347 /*
348  * Add a plugin's data set definition.
349  */
350 static int pplugin_register_data_set (char *name, AV *dataset)
351 {
352         int len = -1;
353         int i   = 0;
354
355         data_source_t *ds  = NULL;
356         data_set_t    *set = NULL;
357
358         ds_types_t *types = NULL;
359
360         if ((NULL == name) || (NULL == dataset))
361                 return -1;
362
363         len = Perl_av_len (perl, dataset);
364
365         if (-1 == len)
366                 return -1;
367
368         ds  = (data_source_t *)smalloc ((len + 1) * sizeof (data_source_t));
369         set = (data_set_t *)smalloc (sizeof (data_set_t));
370
371         types = (ds_types_t *)smalloc (sizeof (ds_types_t));
372         types->len = len + 1;
373         types->values = (int *)smalloc ((types->len) * sizeof (int));
374
375         for (i = 0; i <= len; ++i) {
376                 SV **elem = Perl_av_fetch (perl, dataset, i, 0);
377
378                 if (NULL == elem)
379                         return -1;
380
381                 if (! (SvROK (*elem) && (SVt_PVHV == SvTYPE (SvRV (*elem))))) {
382                         log_err ("pplugin_register_data_set: Invalid data source.");
383                         return -1;
384                 }
385
386                 if (-1 == hv2data_source ((HV *)SvRV (*elem), &ds[i]))
387                         return -1;
388
389                 types->values[i] = ds[i].type;
390                 log_debug ("pplugin_register_data_set: "
391                                 "DS.name = \"%s\", DS.type = %i, DS.min = %f, DS.max = %f",
392                                 ds[i].name, ds[i].type, ds[i].min, ds[i].max);
393         }
394
395         if (NULL == Perl_hv_store (perl, data_sets, name, strlen (name),
396                         Perl_sv_setref_pv (perl, Perl_newSV (perl, 0), 0, types), 0))
397                 return -1;
398
399         strncpy (set->type, name, DATA_MAX_NAME_LEN);
400         set->type[DATA_MAX_NAME_LEN - 1] = '\0';
401
402         set->ds_num = len + 1;
403         set->ds = ds;
404         return plugin_register_data_set (set);
405 } /* static int pplugin_register_data_set (char *, SV *) */
406
407 /*
408  * Remove a plugin's data set definition.
409  */
410 static int pplugin_unregister_data_set (char *name)
411 {
412         SV *tmp = NULL;
413
414         if (NULL == name)
415                 return 0;
416
417         /* freeing the allocated memory of the element itself (ds_types_t *)
418          * causes a segfault during perl_destruct () thus I assume perl somehow
419          * takes care of this... */
420
421         tmp = Perl_hv_delete (perl, data_sets, name, strlen (name), 0);
422         if (NULL != tmp) {
423                 ds_types_t *ds = (ds_types_t *)SvIV ((SV *)SvRV (tmp));
424                 sfree (ds->values);
425         }
426         return plugin_unregister_data_set (name);
427 } /* static int pplugin_unregister_data_set (char *) */
428
429 /*
430  * Submit the values to the write functions.
431  *
432  * value list:
433  * {
434  *   values => [ @values ],
435  *   time   => $time,
436  *   host   => $host,
437  *   plugin => $plugin,
438  *   plugin_instance => $pinstance,
439  *   type_instance   => $tinstance,
440  * }
441  */
442 static int pplugin_dispatch_values (char *name, HV *values)
443 {
444         value_list_t list = VALUE_LIST_INIT;
445         value_t      *val = NULL;
446
447         SV **tmp = NULL;
448
449         int ret = 0;
450
451         if ((NULL == name) || (NULL == values))
452                 return -1;
453
454         if ((NULL == (tmp = Perl_hv_fetch (perl, values, "values", 6, 0)))
455                         || (! (SvROK (*tmp) && (SVt_PVAV == SvTYPE (SvRV (*tmp)))))) {
456                 log_err ("pplugin_dispatch_values: No valid values given.");
457                 return -1;
458         }
459
460         {
461                 AV  *array = (AV *)SvRV (*tmp);
462                 int len    = Perl_av_len (perl, array) + 1;
463
464                 val = (value_t *)smalloc (len * sizeof (value_t));
465
466                 list.values_len = av2value (name, (AV *)SvRV (*tmp), val, len);
467                 list.values = val;
468
469                 if (-1 == list.values_len) {
470                         sfree (val);
471                         return -1;
472                 }
473         }
474
475         if (NULL != (tmp = Perl_hv_fetch (perl, values, "time", 4, 0))) {
476                 list.time = (time_t)SvIV (*tmp);
477         }
478         else {
479                 list.time = time (NULL);
480         }
481
482         if (NULL != (tmp = Perl_hv_fetch (perl, values, "host", 4, 0))) {
483                 strncpy (list.host, SvPV_nolen (*tmp), DATA_MAX_NAME_LEN);
484                 list.host[DATA_MAX_NAME_LEN - 1] = '\0';
485         }
486         else {
487                 strcpy (list.host, hostname_g);
488         }
489
490         if (NULL != (tmp = Perl_hv_fetch (perl, values, "plugin", 6, 0))) {
491                 strncpy (list.plugin, SvPV_nolen (*tmp), DATA_MAX_NAME_LEN);
492                 list.plugin[DATA_MAX_NAME_LEN - 1] = '\0';
493         }
494
495         if (NULL != (tmp = Perl_hv_fetch (perl, values,
496                         "plugin_instance", 15, 0))) {
497                 strncpy (list.plugin_instance, SvPV_nolen (*tmp), DATA_MAX_NAME_LEN);
498                 list.plugin_instance[DATA_MAX_NAME_LEN - 1] = '\0';
499         }
500
501         if (NULL != (tmp = Perl_hv_fetch (perl, values, "type_instance", 13, 0))) {
502                 strncpy (list.type_instance, SvPV_nolen (*tmp), DATA_MAX_NAME_LEN);
503                 list.type_instance[DATA_MAX_NAME_LEN - 1] = '\0';
504         }
505
506         ret = plugin_dispatch_values (name, &list);
507
508         sfree (val);
509         return ret;
510 } /* static int pplugin_dispatch_values (char *, HV *) */
511
512 /*
513  * Call all working functions of the given type.
514  */
515 static int pplugin_call_all (int type, ...)
516 {
517         int retvals = 0;
518
519         va_list ap;
520         int ret = 0;
521
522         dSP;
523
524         if ((type < 0) || (type >= PLUGIN_TYPES))
525                 return -1;
526
527         va_start (ap, type);
528
529         ENTER;
530         SAVETMPS;
531
532         PUSHMARK (SP);
533
534         XPUSHs (sv_2mortal (Perl_newSViv (perl, (IV)type)));
535
536         if (PLUGIN_WRITE == type) {
537                 /*
538                  * $_[0] = $plugin_type;
539                  *
540                  * $_[1] =
541                  * [
542                  *   {
543                  *     name => $ds_name,
544                  *     type => $ds_type,
545                  *     min  => $ds_min,
546                  *     max  => $ds_max
547                  *   },
548                  *   ...
549                  * ];
550                  *
551                  * $_[2] =
552                  * {
553                  *   values => [ $v1, ... ],
554                  *   time   => $time,
555                  *   host   => $hostname,
556                  *   plugin => $plugin,
557                  *   plugin_instance => $instance,
558                  *   type_instance   => $type_instance
559                  * };
560                  */
561                 data_set_t   *ds;
562                 value_list_t *vl;
563
564                 AV *pds = Perl_newAV (perl);
565                 HV *pvl = Perl_newHV (perl);
566
567                 ds = va_arg (ap, data_set_t *);
568                 vl = va_arg (ap, value_list_t *);
569
570                 if (-1 == data_set2av (ds, pds))
571                         return -1;
572
573                 if (-1 == value_list2hv (vl, ds, pvl))
574                         return -1;
575
576                 XPUSHs (sv_2mortal (Perl_newSVpv (perl, ds->type, 0)));
577                 XPUSHs (sv_2mortal (Perl_newRV_noinc (perl, (SV *)pds)));
578                 XPUSHs (sv_2mortal (Perl_newRV_noinc (perl, (SV *)pvl)));
579         }
580         else if (PLUGIN_LOG == type) {
581                 /*
582                  * $_[0] = $level;
583                  *
584                  * $_[1] = $message;
585                  */
586                 XPUSHs (sv_2mortal (Perl_newSViv (perl, va_arg (ap, int))));
587                 XPUSHs (sv_2mortal (Perl_newSVpv (perl, va_arg (ap, char *), 0)));
588         }
589
590         PUTBACK;
591
592         retvals = Perl_call_pv (perl, "Collectd::plugin_call_all", G_SCALAR);
593
594         SPAGAIN;
595         if (0 < retvals) {
596                 SV *tmp = POPs;
597                 if (! SvTRUE (tmp))
598                         ret = -1;
599         }
600
601         PUTBACK;
602         FREETMPS;
603         LEAVE;
604
605         va_end (ap);
606         return ret;
607 } /* static int pplugin_call_all (int, ...) */
608
609
610 /*
611  * Exported Perl API.
612  */
613
614 /*
615  * Collectd::plugin_register_data_set (type, dataset).
616  *
617  * type:
618  *   type of the dataset
619  *
620  * dataset:
621  *   dataset to be registered
622  */
623 static XS (Collectd_plugin_register_ds)
624 {
625         SV  *data = NULL;
626         int ret   = 0;
627
628         dXSARGS;
629
630         if (2 != items) {
631                 log_err ("Usage: Collectd::plugin_register_data_set(type, dataset)");
632                 XSRETURN_EMPTY;
633         }
634
635         log_debug ("Collectd::plugin_register_data_set: "
636                         "type = \"%s\", dataset = \"%s\"",
637                         SvPV_nolen (ST (0)), SvPV_nolen (ST (1)));
638
639         data = ST (1);
640
641         if (SvROK (data) && (SVt_PVAV == SvTYPE (SvRV (data)))) {
642                 ret = pplugin_register_data_set (SvPV_nolen (ST (0)),
643                                 (AV *)SvRV (data));
644         }
645         else {
646                 log_err ("Collectd::plugin_register_data_set: Invalid data.");
647                 XSRETURN_EMPTY;
648         }
649
650         if (0 == ret)
651                 XSRETURN_YES;
652         else
653                 XSRETURN_EMPTY;
654 } /* static XS (Collectd_plugin_register_ds) */
655
656 /*
657  * Collectd::plugin_unregister_data_set (type).
658  *
659  * type:
660  *   type of the dataset
661  */
662 static XS (Collectd_plugin_unregister_ds)
663 {
664         dXSARGS;
665
666         if (1 != items) {
667                 log_err ("Usage: Collectd::plugin_unregister_data_set(type)");
668                 XSRETURN_EMPTY;
669         }
670
671         log_debug ("Collectd::plugin_unregister_data_set: type = \"%s\"",
672                         SvPV_nolen (ST (0)));
673
674         if (0 == pplugin_unregister_data_set (SvPV_nolen (ST (1))))
675                 XSRETURN_YES;
676         else
677                 XSRETURN_EMPTY;
678 } /* static XS (Collectd_plugin_register_ds) */
679
680 /*
681  * Collectd::plugin_dispatch_values (name, values).
682  *
683  * name:
684  *   name of the plugin
685  *
686  * values:
687  *   value list to submit
688  */
689 static XS (Collectd_plugin_dispatch_values)
690 {
691         SV *values = NULL;
692
693         int ret = 0;
694
695         dXSARGS;
696
697         if (2 != items) {
698                 log_err ("Usage: Collectd::plugin_dispatch_values(name, values)");
699                 XSRETURN_EMPTY;
700         }
701
702         log_debug ("Collectd::plugin_dispatch_values: "
703                         "name = \"%s\", values=\"%s\"",
704                         SvPV_nolen (ST (0)), SvPV_nolen (ST (1)));
705
706         values = ST (1);
707
708         if (! (SvROK (values) && (SVt_PVHV == SvTYPE (SvRV (values))))) {
709                 log_err ("Collectd::plugin_dispatch_values: Invalid values.");
710                 XSRETURN_EMPTY;
711         }
712
713         if ((NULL == ST (0)) || (NULL == values))
714                 XSRETURN_EMPTY;
715
716         ret = pplugin_dispatch_values (SvPV_nolen (ST (0)), (HV *)SvRV (values));
717
718         if (0 == ret)
719                 XSRETURN_YES;
720         else
721                 XSRETURN_EMPTY;
722 } /* static XS (Collectd_plugin_dispatch_values) */
723
724 /*
725  * Collectd::plugin_log (level, message).
726  *
727  * level:
728  *   log level (LOG_DEBUG, ... LOG_ERR)
729  *
730  * message:
731  *   log message
732  */
733 static XS (Collectd_plugin_log)
734 {
735         dXSARGS;
736
737         if (2 != items) {
738                 log_err ("Usage: Collectd::plugin_log(level, message)");
739                 XSRETURN_EMPTY;
740         }
741
742         plugin_log (SvIV (ST (0)), SvPV_nolen (ST (1)));
743         XSRETURN_YES;
744 } /* static XS (Collectd_plugin_log) */
745
746
747 /*
748  * Interface to collectd.
749  */
750
751 static int perl_init (void)
752 {
753         if (NULL == perl)
754                 return 0;
755
756         PERL_SET_CONTEXT (perl);
757         return pplugin_call_all (PLUGIN_INIT);
758 } /* static int perl_init (void) */
759
760 static int perl_read (void)
761 {
762         if (NULL == perl)
763                 return 0;
764
765         PERL_SET_CONTEXT (perl);
766         return pplugin_call_all (PLUGIN_READ);
767 } /* static int perl_read (void) */
768
769 static int perl_write (const data_set_t *ds, const value_list_t *vl)
770 {
771         if (NULL == perl)
772                 return 0;
773
774         PERL_SET_CONTEXT (perl);
775         return pplugin_call_all (PLUGIN_WRITE, ds, vl);
776 } /* static int perl_write (const data_set_t *, const value_list_t *) */
777
778 static void perl_log (int level, const char *msg)
779 {
780         if (NULL == perl)
781                 return;
782
783         PERL_SET_CONTEXT (perl);
784         pplugin_call_all (PLUGIN_LOG, level, msg);
785         return;
786 } /* static void perl_log (int, const char *) */
787
788 static int perl_shutdown (void)
789 {
790         int ret = 0;
791
792         plugin_unregister_complex_config ("perl");
793
794         if (NULL == perl)
795                 return 0;
796
797         plugin_unregister_log ("perl");
798         plugin_unregister_init ("perl");
799         plugin_unregister_read ("perl");
800         plugin_unregister_write ("perl");
801
802         PERL_SET_CONTEXT (perl);
803         ret = pplugin_call_all (PLUGIN_SHUTDOWN);
804
805         if (0 < Perl_hv_iterinit (perl, data_sets)) {
806                 char *k = NULL;
807                 I32  l  = 0;
808
809                 while (NULL != Perl_hv_iternextsv (perl, data_sets, &k, &l)) {
810                         pplugin_unregister_data_set (k);
811                 }
812         }
813
814         Perl_hv_undef (perl, data_sets);
815
816 #if COLLECT_DEBUG
817         Perl_sv_report_used (perl);
818 #endif /* COLLECT_DEBUG */
819
820         perl_destruct (perl);
821         perl_free (perl);
822         perl = NULL;
823
824         PERL_SYS_TERM ();
825
826         plugin_unregister_shutdown ("perl");
827         return ret;
828 } /* static void perl_shutdown (void) */
829
830 /* bootstrap the Collectd module */
831 static void xs_init (pTHX)
832 {
833         HV   *stash = NULL;
834         char *file  = __FILE__;
835
836         int i = 0;
837
838         dXSUB_SYS;
839
840         /* enable usage of Perl modules using shared libraries */
841         Perl_newXS (perl, "DynaLoader::boot_DynaLoader", boot_DynaLoader, file);
842
843         /* register API */
844         for (i = 0; NULL != api[i].f; ++i)
845                 Perl_newXS (perl, api[i].name, api[i].f, file);
846
847         stash = Perl_gv_stashpv (perl, "Collectd", 1);
848
849         /* export "constants" */
850         for (i = 0; '\0' != constants[i].name[0]; ++i)
851                 Perl_newCONSTSUB (perl, stash, constants[i].name,
852                                 Perl_newSViv (perl, constants[i].value));
853         return;
854 } /* static void xs_init (pTHX) */
855
856 /* Initialize the global Perl interpreter. */
857 static int init_pi (int argc, char **argv)
858 {
859         if (NULL != perl)
860                 return 0;
861
862         log_info ("Initializing Perl interpreter...");
863 #if COLLECT_DEBUG
864         {
865                 int i = 0;
866
867                 for (i = 0; i < argc; ++i)
868                         log_debug ("argv[%i] = \"%s\"", i, argv[i]);
869         }
870 #endif /* COLLECT_DEBUG */
871
872         PERL_SYS_INIT3 (&argc, &argv, &environ);
873
874         if (NULL == (perl = perl_alloc ())) {
875                 log_err ("module_register: Not enough memory.");
876                 exit (3);
877         }
878         perl_construct (perl);
879
880         PL_exit_flags |= PERL_EXIT_DESTRUCT_END;
881
882         if (0 != perl_parse (perl, xs_init, argc, argv, NULL)) {
883                 log_err ("module_register: Unable to bootstrap Collectd.");
884                 exit (1);
885         }
886         perl_run (perl);
887
888         data_sets = Perl_newHV (perl);
889
890         plugin_register_log ("perl", perl_log);
891         plugin_register_init ("perl", perl_init);
892
893         plugin_register_read ("perl", perl_read);
894
895         plugin_register_write ("perl", perl_write);
896         plugin_register_shutdown ("perl", perl_shutdown);
897         return 0;
898 } /* static int init_pi (const char **, const int) */
899
900 /*
901  * LoadPlugin "<Plugin>"
902  */
903 static int perl_config_loadplugin (oconfig_item_t *ci)
904 {
905         char module_name[DATA_MAX_NAME_LEN];
906
907         char *value = NULL;
908
909         if ((0 != ci->children_num) || (1 != ci->values_num)
910                         || (OCONFIG_TYPE_STRING != ci->values[0].type)) {
911         }
912
913         value = ci->values[0].value.string;
914
915         if (NULL == get_module_name (module_name, sizeof (module_name), value)) {
916                 log_err ("Invalid module name %s", value);
917                 return (1);
918         }
919
920         init_pi (perl_argc, perl_argv);
921
922         log_debug ("perl_config: loading perl plugin \"%s\"", value);
923         Perl_load_module (perl, PERL_LOADMOD_NOIMPORT,
924                         Perl_newSVpv (perl, module_name, strlen (module_name)),
925                         Nullsv);
926         return 0;
927 } /* static int perl_config_loadplugin (oconfig_item_it *) */
928
929 /*
930  * BaseName "<Name>"
931  */
932 static int perl_config_basename (oconfig_item_t *ci)
933 {
934         char *value = NULL;
935
936         if ((0 != ci->children_num) || (1 != ci->values_num)
937                         || (OCONFIG_TYPE_STRING != ci->values[0].type)) {
938         }
939
940         value = ci->values[0].value.string;
941
942         log_debug ("perl_config: Setting plugin basename to \"%s\"", value);
943         strncpy (base_name, value, sizeof (base_name));
944         base_name[sizeof (base_name) - 1] = '\0';
945         return 0;
946 } /* static int perl_config_basename (oconfig_item_it *) */
947
948 /*
949  * EnableDebugger "<Package>"|""
950  */
951 static int perl_config_enabledebugger (oconfig_item_t *ci)
952 {
953         char *value = NULL;
954
955         if ((0 != ci->children_num) || (1 != ci->values_num)
956                         || (OCONFIG_TYPE_STRING != ci->values[0].type)) {
957         }
958
959         value = ci->values[0].value.string;
960
961         perl_argv = (char **)realloc (perl_argv,
962                         (++perl_argc + 1) * sizeof (char *));
963
964         if (NULL == perl_argv) {
965                 log_err ("perl_config: Not enough memory.");
966                 exit (3);
967         }
968
969         if ('\0' == value[0]) {
970                 perl_argv[perl_argc - 1] = "-d";
971         }
972         else {
973                 perl_argv[perl_argc - 1] = (char *)smalloc (strlen (value) + 4);
974                 sstrncpy (perl_argv[perl_argc - 1], "-d:", 4);
975                 sstrncpy (perl_argv[perl_argc - 1] + 3, value, strlen (value) + 1);
976         }
977
978         perl_argv[perl_argc] = NULL;
979         return 0;
980 } /* static int perl_config_enabledebugger (oconfig_item_it *) */
981
982 /*
983  * IncludeDir "<Dir>"
984  */
985 static int perl_config_includedir (oconfig_item_t *ci)
986 {
987         char *value = NULL;
988
989         if ((0 != ci->children_num) || (1 != ci->values_num)
990                         || (OCONFIG_TYPE_STRING != ci->values[0].type)) {
991         }
992
993         value = ci->values[0].value.string;
994
995         if (NULL == perl) {
996                 perl_argv = (char **)realloc (perl_argv,
997                                 (++perl_argc + 1) * sizeof (char *));
998
999                 if (NULL == perl_argv) {
1000                         log_err ("perl_config: Not enough memory.");
1001                         exit (3);
1002                 }
1003
1004                 perl_argv[perl_argc - 1] = (char *)smalloc (strlen (value) + 3);
1005                 sstrncpy(perl_argv[perl_argc - 1], "-I", 3);
1006                 sstrncpy(perl_argv[perl_argc - 1] + 2, value, strlen (value) + 1);
1007
1008                 perl_argv[perl_argc] = NULL;
1009         }
1010         else {
1011                 /* prepend the directory to @INC */
1012                 Perl_av_unshift (perl, GvAVn (PL_incgv), 1);
1013                 Perl_av_store (perl, GvAVn (PL_incgv),
1014                                 0, Perl_newSVpv (perl, value, strlen (value)));
1015         }
1016         return 0;
1017 } /* static int perl_config_includedir (oconfig_item_it *) */
1018
1019 static int perl_config (oconfig_item_t *ci)
1020 {
1021         int i = 0;
1022
1023         for (i = 0; i < ci->children_num; ++i) {
1024                 oconfig_item_t *c = ci->children + i;
1025
1026                 if (0 == strcasecmp (c->key, "LoadPlugin"))
1027                         perl_config_loadplugin (c);
1028                 else if (0 == strcasecmp (c->key, "BaseName"))
1029                         perl_config_basename (c);
1030                 else if (0 == strcasecmp (c->key, "EnableDebugger"))
1031                         perl_config_enabledebugger (c);
1032                 else if (0 == strcasecmp (c->key, "IncludeDir"))
1033                         perl_config_includedir (c);
1034                 else
1035                         log_warn ("Ignoring unknown config key \"%s\".", c->key);
1036         }
1037         return 0;
1038 } /* static int perl_config (oconfig_item_t *) */
1039
1040 void module_register (void)
1041 {
1042         perl_argc = 4;
1043         perl_argv = (char **)smalloc ((perl_argc + 1) * sizeof (char *));
1044
1045         /* default options for the Perl interpreter */
1046         perl_argv[0] = "";
1047         perl_argv[1] = "-MCollectd";
1048         perl_argv[2] = "-e";
1049         perl_argv[3] = "1";
1050         perl_argv[4] = NULL;
1051
1052         plugin_register_complex_config ("perl", perl_config);
1053         return;
1054 } /* void module_register (void) */
1055
1056 /* vim: set sw=4 ts=4 tw=78 noexpandtab : */
1057