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