/**
* collectd - src/perl.c
- * Copyright (C) 2007 Sebastian Harl
+ * Copyright (C) 2007, 2008 Sebastian Harl
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
/* do not automatically get the thread specific perl interpreter */
#define PERL_NO_GET_CONTEXT
+#define DONT_POISON_SPRINTF_YET 1
#include "collectd.h"
+#undef DONT_POISON_SPRINTF_YET
#include "configfile.h"
#include <EXTERN.h>
#include <perl.h>
+#if __GNUC__
+# pragma GCC poison sprintf
+#endif
+
#include <XSUB.h>
/* Some versions of Perl define their own version of DEBUG... :-/ */
#define PLUGIN_WRITE 2
#define PLUGIN_SHUTDOWN 3
#define PLUGIN_LOG 4
+#define PLUGIN_NOTIF 5
+#define PLUGIN_FLUSH 6
-#define PLUGIN_TYPES 5
+#define PLUGIN_TYPES 7
#define PLUGIN_DATASET 255
static XS (Collectd_plugin_register_ds);
static XS (Collectd_plugin_unregister_ds);
static XS (Collectd_plugin_dispatch_values);
+static XS (Collectd_plugin_flush_one);
+static XS (Collectd_plugin_flush_all);
+static XS (Collectd_plugin_dispatch_notification);
static XS (Collectd_plugin_log);
static XS (Collectd_call_by_name);
{ "Collectd::plugin_register_data_set", Collectd_plugin_register_ds },
{ "Collectd::plugin_unregister_data_set", Collectd_plugin_unregister_ds },
{ "Collectd::plugin_dispatch_values", Collectd_plugin_dispatch_values },
+ { "Collectd::plugin_flush_one", Collectd_plugin_flush_one },
+ { "Collectd::plugin_flush_all", Collectd_plugin_flush_all },
+ { "Collectd::plugin_dispatch_notification",
+ Collectd_plugin_dispatch_notification },
{ "Collectd::plugin_log", Collectd_plugin_log },
{ "Collectd::call_by_name", Collectd_call_by_name },
{ "", NULL }
{ "Collectd::TYPE_WRITE", PLUGIN_WRITE },
{ "Collectd::TYPE_SHUTDOWN", PLUGIN_SHUTDOWN },
{ "Collectd::TYPE_LOG", PLUGIN_LOG },
+ { "Collectd::TYPE_NOTIF", PLUGIN_NOTIF },
+ { "Collectd::TYPE_FLUSH", PLUGIN_FLUSH },
{ "Collectd::TYPE_DATASET", PLUGIN_DATASET },
{ "Collectd::DS_TYPE_COUNTER", DS_TYPE_COUNTER },
{ "Collectd::DS_TYPE_GAUGE", DS_TYPE_GAUGE },
{ "Collectd::LOG_NOTICE", LOG_NOTICE },
{ "Collectd::LOG_INFO", LOG_INFO },
{ "Collectd::LOG_DEBUG", LOG_DEBUG },
+ { "Collectd::NOTIF_FAILURE", NOTIF_FAILURE },
+ { "Collectd::NOTIF_WARNING", NOTIF_WARNING },
+ { "Collectd::NOTIF_OKAY", NOTIF_OKAY },
{ "", 0 }
};
return -1;
if (NULL != (tmp = hv_fetch (hash, "name", 4, 0))) {
- strncpy (ds->name, SvPV_nolen (*tmp), DATA_MAX_NAME_LEN);
- ds->name[DATA_MAX_NAME_LEN - 1] = '\0';
+ sstrncpy (ds->name, SvPV_nolen (*tmp), sizeof (ds->name));
}
else {
log_err ("hv2data_source: No DS name given.");
newSVpv (vl->plugin_instance, 0), 0))
return -1;
+ if ('\0' != vl->type[0])
+ if (NULL == hv_store (hash, "type", 4, newSVpv (vl->type, 0), 0))
+ return -1;
+
if ('\0' != vl->type_instance[0])
if (NULL == hv_store (hash, "type_instance", 13,
newSVpv (vl->type_instance, 0), 0))
return 0;
} /* static int value2av (value_list_t *, data_set_t *, HV *) */
+static int notification2hv (pTHX_ notification_t *n, HV *hash)
+{
+ if (NULL == hv_store (hash, "severity", 8, newSViv (n->severity), 0))
+ return -1;
+
+ if (0 != n->time)
+ if (NULL == hv_store (hash, "time", 4, newSViv (n->time), 0))
+ return -1;
+
+ if ('\0' != *n->message)
+ if (NULL == hv_store (hash, "message", 7, newSVpv (n->message, 0), 0))
+ return -1;
+
+ if ('\0' != *n->host)
+ if (NULL == hv_store (hash, "host", 4, newSVpv (n->host, 0), 0))
+ return -1;
+
+ if ('\0' != *n->plugin)
+ if (NULL == hv_store (hash, "plugin", 6, newSVpv (n->plugin, 0), 0))
+ return -1;
+
+ if ('\0' != *n->plugin_instance)
+ if (NULL == hv_store (hash, "plugin_instance", 15,
+ newSVpv (n->plugin_instance, 0), 0))
+ return -1;
+
+ if ('\0' != *n->type)
+ if (NULL == hv_store (hash, "type", 4, newSVpv (n->type, 0), 0))
+ return -1;
+
+ if ('\0' != *n->type_instance)
+ if (NULL == hv_store (hash, "type_instance", 13,
+ newSVpv (n->type_instance, 0), 0))
+ return -1;
+ return 0;
+} /* static int notification2hv (notification_t *, HV *) */
+
/*
* Internal functions.
*/
static char *get_module_name (char *buf, size_t buf_len, const char *module) {
int status = 0;
if (base_name[0] == '\0')
- status = snprintf (buf, buf_len, "%s", module);
+ status = ssnprintf (buf, buf_len, "%s", module);
else
- status = snprintf (buf, buf_len, "%s::%s", base_name, module);
+ status = ssnprintf (buf, buf_len, "%s::%s", base_name, module);
if ((status < 0) || ((unsigned int)status >= buf_len))
return (NULL);
- buf[buf_len - 1] = '\0';
return (buf);
} /* char *get_module_name */
ds[i].name, ds[i].type, ds[i].min, ds[i].max);
}
- strncpy (set->type, name, DATA_MAX_NAME_LEN);
- set->type[DATA_MAX_NAME_LEN - 1] = '\0';
+ sstrncpy (set->type, name, sizeof (set->type));
set->ds_num = len + 1;
set->ds = ds;
* type_instance => $tinstance,
* }
*/
-static int pplugin_dispatch_values (pTHX_ char *name, HV *values)
+static int pplugin_dispatch_values (pTHX_ HV *values)
{
value_list_t list = VALUE_LIST_INIT;
value_t *val = NULL;
int ret = 0;
- if ((NULL == name) || (NULL == values))
+ if (NULL == values)
return -1;
+ if (NULL == (tmp = hv_fetch (values, "type", 4, 0))) {
+ log_err ("pplugin_dispatch_values: No type given.");
+ return -1;
+ }
+
+ sstrncpy (list.type, SvPV_nolen (*tmp), sizeof (list.type));
+
if ((NULL == (tmp = hv_fetch (values, "values", 6, 0)))
|| (! (SvROK (*tmp) && (SVt_PVAV == SvTYPE (SvRV (*tmp)))))) {
log_err ("pplugin_dispatch_values: No valid values given.");
val = (value_t *)smalloc (len * sizeof (value_t));
- list.values_len = av2value (aTHX_ name, (AV *)SvRV (*tmp), val, len);
+ list.values_len = av2value (aTHX_ list.type, (AV *)SvRV (*tmp),
+ val, len);
list.values = val;
if (-1 == list.values_len) {
}
if (NULL != (tmp = hv_fetch (values, "host", 4, 0))) {
- strncpy (list.host, SvPV_nolen (*tmp), DATA_MAX_NAME_LEN);
- list.host[DATA_MAX_NAME_LEN - 1] = '\0';
+ sstrncpy (list.host, SvPV_nolen (*tmp), sizeof (list.host));
}
else {
- strcpy (list.host, hostname_g);
+ sstrncpy (list.host, hostname_g, sizeof (list.host));
}
- if (NULL != (tmp = hv_fetch (values, "plugin", 6, 0))) {
- strncpy (list.plugin, SvPV_nolen (*tmp), DATA_MAX_NAME_LEN);
- list.plugin[DATA_MAX_NAME_LEN - 1] = '\0';
- }
+ if (NULL != (tmp = hv_fetch (values, "plugin", 6, 0)))
+ sstrncpy (list.plugin, SvPV_nolen (*tmp), sizeof (list.plugin));
- if (NULL != (tmp = hv_fetch (values,
- "plugin_instance", 15, 0))) {
- strncpy (list.plugin_instance, SvPV_nolen (*tmp), DATA_MAX_NAME_LEN);
- list.plugin_instance[DATA_MAX_NAME_LEN - 1] = '\0';
- }
+ if (NULL != (tmp = hv_fetch (values, "plugin_instance", 15, 0)))
+ sstrncpy (list.plugin_instance, SvPV_nolen (*tmp),
+ sizeof (list.plugin_instance));
- if (NULL != (tmp = hv_fetch (values, "type_instance", 13, 0))) {
- strncpy (list.type_instance, SvPV_nolen (*tmp), DATA_MAX_NAME_LEN);
- list.type_instance[DATA_MAX_NAME_LEN - 1] = '\0';
- }
+ if (NULL != (tmp = hv_fetch (values, "type_instance", 13, 0)))
+ sstrncpy (list.type_instance, SvPV_nolen (*tmp),
+ sizeof (list.type_instance));
- ret = plugin_dispatch_values (name, &list);
+ ret = plugin_dispatch_values (&list);
sfree (val);
return ret;
} /* static int pplugin_dispatch_values (char *, HV *) */
/*
+ * Dispatch a notification.
+ *
+ * notification:
+ * {
+ * severity => $severity,
+ * time => $time,
+ * message => $msg,
+ * host => $host,
+ * plugin => $plugin,
+ * type => $type,
+ * plugin_instance => $instance,
+ * type_instance => $type_instance
+ * }
+ */
+static int pplugin_dispatch_notification (pTHX_ HV *notif)
+{
+ notification_t n;
+
+ SV **tmp = NULL;
+
+ if (NULL == notif)
+ return -1;
+
+ memset (&n, 0, sizeof (n));
+
+ if (NULL != (tmp = hv_fetch (notif, "severity", 8, 0)))
+ n.severity = SvIV (*tmp);
+ else
+ n.severity = NOTIF_FAILURE;
+
+ if (NULL != (tmp = hv_fetch (notif, "time", 4, 0)))
+ n.time = (time_t)SvIV (*tmp);
+ else
+ n.time = time (NULL);
+
+ if (NULL != (tmp = hv_fetch (notif, "message", 7, 0)))
+ sstrncpy (n.message, SvPV_nolen (*tmp), sizeof (n.message));
+
+ if (NULL != (tmp = hv_fetch (notif, "host", 4, 0)))
+ sstrncpy (n.host, SvPV_nolen (*tmp), sizeof (n.host));
+ else
+ sstrncpy (n.host, hostname_g, sizeof (n.host));
+
+ if (NULL != (tmp = hv_fetch (notif, "plugin", 6, 0)))
+ sstrncpy (n.plugin, SvPV_nolen (*tmp), sizeof (n.plugin));
+
+ if (NULL != (tmp = hv_fetch (notif, "plugin_instance", 15, 0)))
+ sstrncpy (n.plugin_instance, SvPV_nolen (*tmp),
+ sizeof (n.plugin_instance));
+
+ if (NULL != (tmp = hv_fetch (notif, "type", 4, 0)))
+ sstrncpy (n.type, SvPV_nolen (*tmp), sizeof (n.type));
+
+ if (NULL != (tmp = hv_fetch (notif, "type_instance", 13, 0)))
+ sstrncpy (n.type_instance, SvPV_nolen (*tmp), sizeof (n.type_instance));
+ return plugin_dispatch_notification (&n);
+} /* static int pplugin_dispatch_notification (HV *) */
+
+/*
* Call all working functions of the given type.
*/
static int pplugin_call_all (pTHX_ int type, ...)
* time => $time,
* host => $hostname,
* plugin => $plugin,
+ * type => $type,
* plugin_instance => $instance,
* type_instance => $type_instance
* };
ds = va_arg (ap, data_set_t *);
vl = va_arg (ap, value_list_t *);
- if (-1 == data_set2av (aTHX_ ds, pds))
- return -1;
+ if (-1 == data_set2av (aTHX_ ds, pds)) {
+ av_clear (pds);
+ av_undef (pds);
+ pds = Nullav;
+ ret = -1;
+ }
- if (-1 == value_list2hv (aTHX_ vl, ds, pvl))
- return -1;
+ if (-1 == value_list2hv (aTHX_ vl, ds, pvl)) {
+ hv_clear (pvl);
+ hv_undef (pvl);
+ pvl = Nullhv;
+ ret = -1;
+ }
XPUSHs (sv_2mortal (newSVpv (ds->type, 0)));
XPUSHs (sv_2mortal (newRV_noinc ((SV *)pds)));
XPUSHs (sv_2mortal (newSViv (va_arg (ap, int))));
XPUSHs (sv_2mortal (newSVpv (va_arg (ap, char *), 0)));
}
+ else if (PLUGIN_NOTIF == type) {
+ /*
+ * $_[0] =
+ * {
+ * severity => $severity,
+ * time => $time,
+ * message => $msg,
+ * host => $host,
+ * plugin => $plugin,
+ * type => $type,
+ * plugin_instance => $instance,
+ * type_instance => $type_instance
+ * };
+ */
+ notification_t *n;
+ HV *notif = newHV ();
+
+ n = va_arg (ap, notification_t *);
+
+ if (-1 == notification2hv (aTHX_ n, notif)) {
+ hv_clear (notif);
+ hv_undef (notif);
+ notif = Nullhv;
+ ret = -1;
+ }
+
+ XPUSHs (sv_2mortal (newRV_noinc ((SV *)notif)));
+ }
+ else if (PLUGIN_FLUSH == type) {
+ /*
+ * $_[0] = $timeout;
+ */
+ XPUSHs (sv_2mortal (newSViv (va_arg (ap, int))));
+ }
PUTBACK;
log_debug ("Collectd::plugin_unregister_data_set: type = \"%s\"",
SvPV_nolen (ST (0)));
- if (0 == pplugin_unregister_data_set (SvPV_nolen (ST (1))))
+ if (0 == pplugin_unregister_data_set (SvPV_nolen (ST (0))))
XSRETURN_YES;
else
XSRETURN_EMPTY;
*/
static XS (Collectd_plugin_dispatch_values)
{
- SV *values = NULL;
+ SV *values = NULL;
+ int values_idx = 0;
int ret = 0;
dXSARGS;
- if (2 != items) {
- log_err ("Usage: Collectd::plugin_dispatch_values(name, values)");
+ if (2 == items) {
+ log_warn ("Collectd::plugin_dispatch_values with two arguments "
+ "is deprecated - pass the type through values->{type}.");
+ values_idx = 1;
+ }
+ else if (1 != items) {
+ log_err ("Usage: Collectd::plugin_dispatch_values(values)");
XSRETURN_EMPTY;
}
- log_debug ("Collectd::plugin_dispatch_values: "
- "name = \"%s\", values=\"%s\"",
- SvPV_nolen (ST (0)), SvPV_nolen (ST (1)));
+ log_debug ("Collectd::plugin_dispatch_values: values=\"%s\"",
+ SvPV_nolen (ST (values_idx)));
- values = ST (1);
+ values = ST (values_idx);
if (! (SvROK (values) && (SVt_PVHV == SvTYPE (SvRV (values))))) {
log_err ("Collectd::plugin_dispatch_values: Invalid values.");
XSRETURN_EMPTY;
}
- if ((NULL == ST (0)) || (NULL == values))
+ if (((2 == items) && (NULL == ST (0))) || (NULL == values))
XSRETURN_EMPTY;
- ret = pplugin_dispatch_values (aTHX_ SvPV_nolen (ST (0)),
- (HV *)SvRV (values));
+ if ((2 == items) && (NULL == hv_store ((HV *)SvRV (values), "type", 4,
+ newSVsv (ST (0)), 0))) {
+ log_err ("Collectd::plugin_dispatch_values: Could not store type.");
+ XSRETURN_EMPTY;
+ }
+
+ ret = pplugin_dispatch_values (aTHX_ (HV *)SvRV (values));
if (0 == ret)
XSRETURN_YES;
} /* static XS (Collectd_plugin_dispatch_values) */
/*
+ * Collectd::plugin_flush_one (timeout, name).
+ *
+ * timeout:
+ * timeout to use when flushing the data
+ *
+ * name:
+ * name of the plugin to flush
+ */
+static XS (Collectd_plugin_flush_one)
+{
+ dXSARGS;
+
+ if (2 != items) {
+ log_err ("Usage: Collectd::plugin_flush_one(timeout, name)");
+ XSRETURN_EMPTY;
+ }
+
+ log_debug ("Collectd::plugin_flush_one: timeout = %i, name = \"%s\"",
+ (int)SvIV (ST (0)), SvPV_nolen (ST (1)));
+
+ if (0 == plugin_flush_one ((int)SvIV (ST (0)), SvPV_nolen (ST (1))))
+ XSRETURN_YES;
+ else
+ XSRETURN_EMPTY;
+} /* static XS (Collectd_plugin_flush_one) */
+
+/*
+ * Collectd::plugin_flush_all (timeout).
+ *
+ * timeout:
+ * timeout to use when flushing the data
+ */
+static XS (Collectd_plugin_flush_all)
+{
+ dXSARGS;
+
+ if (1 != items) {
+ log_err ("Usage: Collectd::plugin_flush_all(timeout)");
+ XSRETURN_EMPTY;
+ }
+
+ log_debug ("Collectd::plugin_flush_all: timeout = %i", (int)SvIV (ST (0)));
+
+ plugin_flush_all ((int)SvIV (ST (0)));
+ XSRETURN_YES;
+} /* static XS (Collectd_plugin_flush_all) */
+
+/*
+ * Collectd::plugin_dispatch_notification (notif).
+ *
+ * notif:
+ * notification to dispatch
+ */
+static XS (Collectd_plugin_dispatch_notification)
+{
+ SV *notif = NULL;
+
+ int ret = 0;
+
+ dXSARGS;
+
+ if (1 != items) {
+ log_err ("Usage: Collectd::plugin_dispatch_notification(notif)");
+ XSRETURN_EMPTY;
+ }
+
+ log_debug ("Collectd::plugin_dispatch_notification: notif = \"%s\"",
+ SvPV_nolen (ST (0)));
+
+ notif = ST (0);
+
+ if (! (SvROK (notif) && (SVt_PVHV == SvTYPE (SvRV (notif))))) {
+ log_err ("Collectd::plugin_dispatch_notification: Invalid notif.");
+ XSRETURN_EMPTY;
+ }
+
+ ret = pplugin_dispatch_notification (aTHX_ (HV *)SvRV (notif));
+
+ if (0 == ret)
+ XSRETURN_YES;
+ else
+ XSRETURN_EMPTY;
+} /* static XS (Collectd_plugin_dispatch_notification) */
+
+/*
* Collectd::plugin_log (level, message).
*
* level:
aTHX = t->interp;
- if (NULL != base) {
+ if ((NULL != base) && (NULL != PL_endav)) {
av_clear (PL_endav);
av_undef (PL_endav);
PL_endav = Nullav;
return;
} /* static void perl_log (int, const char *) */
+static int perl_notify (const notification_t *notif)
+{
+ dTHX;
+
+ if (NULL == perl_threads)
+ return 0;
+
+ if (NULL == aTHX) {
+ c_ithread_t *t = NULL;
+
+ pthread_mutex_lock (&perl_threads->mutex);
+ t = c_ithread_create (perl_threads->head->interp);
+ pthread_mutex_unlock (&perl_threads->mutex);
+
+ aTHX = t->interp;
+ }
+ return pplugin_call_all (aTHX_ PLUGIN_NOTIF, notif);
+} /* static int perl_notify (const notification_t *) */
+
+/* TODO: Implement flushing of single identifiers. */
+static int perl_flush (int timeout, const char *identifier)
+{
+ dTHX;
+
+ if (NULL == perl_threads)
+ return 0;
+
+ if (NULL == aTHX) {
+ c_ithread_t *t = NULL;
+
+ pthread_mutex_lock (&perl_threads->mutex);
+ t = c_ithread_create (perl_threads->head->interp);
+ pthread_mutex_unlock (&perl_threads->mutex);
+
+ aTHX = t->interp;
+ }
+ return pplugin_call_all (aTHX_ PLUGIN_FLUSH, timeout);
+} /* static int perl_flush (const int) */
+
static int perl_shutdown (void)
{
c_ithread_t *t = NULL;
aTHX, perl_threads->number_of_threads);
plugin_unregister_log ("perl");
+ plugin_unregister_notification ("perl");
plugin_unregister_init ("perl");
plugin_unregister_read ("perl");
plugin_unregister_write ("perl");
+ plugin_unregister_flush ("perl");
ret = pplugin_call_all (aTHX_ PLUGIN_SHUTDOWN);
static int g_pv_set (pTHX_ SV *var, MAGIC *mg)
{
char *pv = mg->mg_ptr;
- strncpy (pv, SvPV_nolen (var), DATA_MAX_NAME_LEN);
- pv[DATA_MAX_NAME_LEN - 1] = '\0';
+ sstrncpy (pv, SvPV_nolen (var), DATA_MAX_NAME_LEN);
return 0;
} /* static int g_pv_set (pTHX_ SV *, MAGIC *) */
log_err ("init_pi: pthread_key_create failed");
/* this must not happen - cowardly giving up if it does */
- exit (1);
+ return -1;
}
+#ifdef __FreeBSD__
+ /* On FreeBSD, PERL_SYS_INIT3 expands to some expression which
+ * triggers a "value computed is not used" warning by gcc. */
+ (void)
+#endif
PERL_SYS_INIT3 (&argc, &argv, &environ);
perl_threads = (c_ithread_list_t *)smalloc (sizeof (c_ithread_list_t));
if (0 != perl_parse (aTHX_ xs_init, argc, argv, NULL)) {
log_err ("init_pi: Unable to bootstrap Collectd.");
- exit (1);
+
+ perl_destruct (perl_threads->head->interp);
+ perl_free (perl_threads->head->interp);
+ sfree (perl_threads);
+
+ pthread_key_delete (perl_thr_key);
+ return -1;
}
/* Set $0 to "collectd" because perl_parse() has to set it to "-e". */
perl_run (aTHX);
plugin_register_log ("perl", perl_log);
+ plugin_register_notification ("perl", perl_notify);
plugin_register_init ("perl", perl_init);
plugin_register_read ("perl", perl_read);
plugin_register_write ("perl", perl_write);
+ plugin_register_flush ("perl", perl_flush);
plugin_register_shutdown ("perl", perl_shutdown);
return 0;
} /* static int init_pi (const char **, const int) */
return (1);
}
- init_pi (perl_argc, perl_argv);
+ if (0 != init_pi (perl_argc, perl_argv))
+ return -1;
+
assert (NULL != perl_threads);
assert (NULL != perl_threads->head);
value = ci->values[0].value.string;
log_debug ("perl_config: Setting plugin basename to \"%s\"", value);
- strncpy (base_name, value, sizeof (base_name));
- base_name[sizeof (base_name) - 1] = '\0';
+ sstrncpy (base_name, value, sizeof (base_name));
return 0;
} /* static int perl_config_basename (oconfig_item_it *) */
return 1;
}
+ if (NULL != perl_threads) {
+ log_warn ("EnableDebugger has no effects if used after LoadPlugin.");
+ return 1;
+ }
+
value = ci->values[0].value.string;
perl_argv = (char **)realloc (perl_argv,