plugin_register
plugin_unregister
plugin_dispatch_values
+ plugin_get_interval
plugin_write
plugin_flush
plugin_flush_one
my $type = shift;
my %plugins;
+ my $interval;
our $cb_name = undef;
%plugins = %{$plugins[$type]};
}
+ $interval = plugin_get_interval ();
+
foreach my $plugin (keys %plugins) {
my $p = $plugins{$plugin};
my $status = 0;
if ($p->{'wait_left'} > 0) {
- $p->{'wait_left'} -= $interval_g;
+ $p->{'wait_left'} -= $interval;
}
next if ($p->{'wait_left'} > 0);
if ($status) {
$p->{'wait_left'} = 0;
- $p->{'wait_time'} = $interval_g;
+ $p->{'wait_time'} = $interval;
}
elsif (TYPE_READ == $type) {
- if ($p->{'wait_time'} < $interval_g) {
- $p->{'wait_time'} = $interval_g;
+ if ($p->{'wait_time'} < $interval) {
+ $p->{'wait_time'} = $interval;
}
$p->{'wait_left'} = $p->{'wait_time'};
}
%p = (
- wait_time => $interval_g,
+ wait_time => plugin_get_interval (),
wait_left => 0,
cb_name => $data,
);
sub openvz_read
{
- my %v = (time => time(), interval => $interval_g);
+ my %v = (time => time(), interval => plugin_get_interval());
my (@veids, $veid, $name, $key, $val, $i, @lines, @parts, @counters);
@veids = map { s/ //g; $_; } split(/\n/, `$vzlist -Ho veid`);
camqp_config_t *conf = user_data;
int status;
+ cdtime_t interval = plugin_get_interval ();
+
while (subscriber_threads_running)
{
amqp_frame_t frame;
struct timespec ts_interval;
ERROR ("amqp plugin: camqp_connect failed. "
"Will sleep for %.3f seconds.",
- CDTIME_T_TO_DOUBLE (interval_g));
- CDTIME_T_TO_TIMESPEC (interval_g, &ts_interval);
+ CDTIME_T_TO_DOUBLE (interval));
+ CDTIME_T_TO_TIMESPEC (interval, &ts_interval);
nanosleep (&ts_interval, /* remaining = */ NULL);
continue;
}
struct timespec ts_interval;
ERROR ("amqp plugin: amqp_simple_wait_frame failed. "
"Will sleep for %.3f seconds.",
- CDTIME_T_TO_DOUBLE (interval_g));
+ CDTIME_T_TO_DOUBLE (interval));
camqp_close_connection (conf);
- CDTIME_T_TO_TIMESPEC (interval_g, &ts_interval);
+ CDTIME_T_TO_TIMESPEC (interval, &ts_interval);
nanosleep (&ts_interval, /* remaining = */ NULL);
continue;
}
tmp = subscriber_threads + subscriber_threads_num;
memset (tmp, 0, sizeof (*tmp));
- status = pthread_create (tmp, /* attr = */ NULL,
+ status = plugin_thread_create (tmp, /* attr = */ NULL,
camqp_subscribe_thread, conf);
if (status != 0)
{
{
values => [123, 0.5],
time => time (),
- interval => $interval_g,
+ interval => plugin_get_interval (),
host => $hostname_g,
plugin => 'myplugin',
type => 'myplugin',
Wrappers around B<plugin_log>, using B<LOG_ERR>, B<LOG_WARNING>,
B<LOG_NOTICE>, B<LOG_INFO> and B<LOG_DEBUG> respectively as I<log-level>.
+=item B<plugin_get_interval> ()
+
+Returns the interval of the current plugin as a floating point number in
+seconds. This value depends on the interval configured within the
+C<LoadPlugin perl> block or the global interval (see L<collectd.conf(5)> for
+details).
+
=back
The following function provides the filter chain C-interface to Perl-modules.
This variable keeps the interval in seconds in which the read functions are
queried (see the B<Interval> configuration option).
+B<Note:> This variable should no longer be used in favor of
+C<plugin_get_interval()> (see above). This function takes any plugin-specific
+interval settings into account (see the C<Interval> option of C<LoadPlugin> in
+L<collectd.conf(5)> for details).
+
=back
Any changes to these variables will be globally visible in collectd.
static int init_global_variables (void)
{
- const char *str;
-
- str = global_option_get ("Interval");
- if (str == NULL)
- {
- interval_g = TIME_T_TO_CDTIME_T (10);
- }
- else
- {
- double tmp;
-
- tmp = atof (str);
- if (tmp <= 0.0)
- {
- fprintf (stderr, "Cannot set the interval to a "
- "correct value.\n"
- "Please check your settings.\n");
- return (-1);
- }
+ char const *str;
- interval_g = DOUBLE_TO_CDTIME_T (tmp);
- }
+ interval_g = cf_get_default_interval ();
+ assert (interval_g > 0);
DEBUG ("interval_g = %.3f;", CDTIME_T_TO_DOUBLE (interval_g));
str = global_option_get ("Timeout");
static int do_loop (void)
{
+ cdtime_t interval = cf_get_default_interval ();
cdtime_t wait_until;
- wait_until = cdtime () + interval_g;
+ wait_until = cdtime () + interval;
while (loop == 0)
{
WARNING ("Not sleeping because the next interval is "
"%.3f seconds in the past!",
CDTIME_T_TO_DOUBLE (now - wait_until));
- wait_until = now + interval_g;
+ wait_until = now + interval;
continue;
}
CDTIME_T_TO_TIMESPEC (wait_until - now, &ts_wait);
- wait_until = wait_until + interval_g;
+ wait_until = wait_until + interval;
while ((loop == 0) && (nanosleep (&ts_wait, &ts_wait) != 0))
{
if (optind < argc)
exit_usage (1);
+ plugin_init_ctx ();
+
/*
* Read options from the config file, the environment and the command
* line (in that order, with later options overwriting previous ones in
either C<perl> or C<python>, the default is changed to enabled in order to keep
the average user from ever having to deal with this low level linking stuff.
+=item B<Interval> I<Seconds>
+
+Sets a plugin-specific interval for collecting metrics. This overrides the
+global B<Interval> setting. If a plugin provides own support for specifying an
+interval, that setting will take precedence.
+
=back
=item B<Include> I<Path>
# define COLLECTD_GRP_NAME "collectd"
#endif
+#ifndef COLLECTD_DEFAULT_INTERVAL
+# define COLLECTD_DEFAULT_INTERVAL 10.0
+#endif
+
#define STATIC_ARRAY_LEN(array) (sizeof (array) / sizeof ((array)[0]))
/* Remove GNU specific __attribute__ settings when using another compiler */
int (*callback) (const char *, const char *);
const char **keys;
int keys_num;
+ plugin_ctx_t ctx;
struct cf_callback *next;
} cf_callback_t;
{
char *type;
int (*callback) (oconfig_item_t *);
+ plugin_ctx_t ctx;
struct cf_complex_callback_s *next;
} cf_complex_callback_t;
{"PIDFile", NULL, PIDFILE},
{"Hostname", NULL, NULL},
{"FQDNLookup", NULL, "true"},
- {"Interval", NULL, "10"},
+ {"Interval", NULL, NULL},
{"ReadThreads", NULL, "5"},
{"Timeout", NULL, "2"},
{"PreCacheChain", NULL, "PreCache"},
const char *orig_value)
{
cf_callback_t *cf_cb;
+ plugin_ctx_t old_ctx;
char *key;
char *value;
int ret;
ret = -1;
+ old_ctx = plugin_set_ctx (cf_cb->ctx);
+
for (i = 0; i < cf_cb->keys_num; i++)
{
if ((cf_cb->keys[i] != NULL)
}
}
+ plugin_set_ctx (old_ctx);
+
if (i >= cf_cb->keys_num)
WARNING ("Plugin `%s' did not register for value `%s'.", type, key);
int i;
const char *name;
unsigned int flags = 0;
+ plugin_ctx_t ctx;
+ plugin_ctx_t old_ctx;
+ int ret_val;
+
assert (strcasecmp (ci->key, "LoadPlugin") == 0);
if (ci->values_num != 1)
name = ci->values[0].value.string;
+ /* default to the global interval set before loading this plugin */
+ memset (&ctx, 0, sizeof (ctx));
+ ctx.interval = cf_get_default_interval ();
+
/*
* XXX: Magic at work:
*
for (i = 0; i < ci->children_num; ++i) {
if (strcasecmp("Globals", ci->children[i].key) == 0)
cf_util_get_flag (ci->children + i, &flags, PLUGIN_FLAGS_GLOBAL);
+ else if (strcasecmp ("Interval", ci->children[i].key) == 0) {
+ double interval = 0.0;
+
+ if (cf_util_get_double (ci->children + i, &interval) != 0) {
+ /* cf_util_get_double will log an error */
+ continue;
+ }
+
+ ctx.interval = DOUBLE_TO_CDTIME_T (interval);
+ }
else {
WARNING("Ignoring unknown LoadPlugin option \"%s\" "
"for plugin \"%s\"",
}
}
- return (plugin_load (name, (uint32_t) flags));
+ old_ctx = plugin_set_ctx (ctx);
+ ret_val = plugin_load (name, (uint32_t) flags);
+ /* reset to the "global" context */
+ plugin_set_ctx (old_ctx);
+
+ return (ret_val);
} /* int dispatch_value_loadplugin */
static int dispatch_value_plugin (const char *plugin, oconfig_item_t *ci)
/* Check for a complex callback first */
for (cb = complex_callback_head; cb != NULL; cb = cb->next)
+ {
if (strcasecmp (name, cb->type) == 0)
- return (cb->callback (ci));
+ {
+ plugin_ctx_t old_ctx;
+ int ret_val;
+
+ old_ctx = plugin_set_ctx (cb->ctx);
+ ret_val = (cb->callback (ci));
+ plugin_set_ctx (old_ctx);
+ return (ret_val);
+ }
+ }
/* Hm, no complex plugin found. Dispatch the values one by one */
for (i = 0; i < ci->children_num; i++)
: cf_global_options[i].def);
} /* char *global_option_get */
+cdtime_t cf_get_default_interval (void)
+{
+ char const *str = global_option_get ("Interval");
+ double interval_double = COLLECTD_DEFAULT_INTERVAL;
+
+ if (str != NULL)
+ {
+ char *endptr = NULL;
+ double tmp = strtod (str, &endptr);
+
+ if ((endptr == NULL) || (endptr == str) || (*endptr != 0))
+ ERROR ("cf_get_default_interval: Unable to parse string \"%s\" "
+ "as number.", str);
+ else if (tmp <= 0.0)
+ ERROR ("cf_get_default_interval: Interval must be a positive number. "
+ "The current number is %g.", tmp);
+ else
+ interval_double = tmp;
+ }
+
+ return (DOUBLE_TO_CDTIME_T (interval_double));
+} /* }}} cdtime_t cf_get_default_interval */
+
void cf_unregister (const char *type)
{
cf_callback_t *this, *prev;
cf_cb->callback = callback;
cf_cb->keys = keys;
cf_cb->keys_num = keys_num;
+ cf_cb->ctx = plugin_get_ctx ();
cf_cb->next = first_callback;
first_callback = cf_cb;
new->callback = callback;
new->next = NULL;
+ new->ctx = plugin_get_ctx ();
+
if (complex_callback_head == NULL)
{
complex_callback_head = new;
return (0);
} /* }}} int cf_util_get_int */
+int cf_util_get_double (const oconfig_item_t *ci, double *ret_value) /* {{{ */
+{
+ if ((ci == NULL) || (ret_value == NULL))
+ return (EINVAL);
+
+ if ((ci->values_num != 1) || (ci->values[0].type != OCONFIG_TYPE_NUMBER))
+ {
+ ERROR ("cf_util_get_double: The %s option requires "
+ "exactly one numeric argument.", ci->key);
+ return (-1);
+ }
+
+ *ret_value = ci->values[0].value.number;
+
+ return (0);
+} /* }}} int cf_util_get_double */
+
int cf_util_get_boolean (const oconfig_item_t *ci, _Bool *ret_bool) /* {{{ */
{
if ((ci == NULL) || (ret_bool == NULL))
int global_option_set (const char *option, const char *value);
const char *global_option_get (const char *option);
+cdtime_t cf_get_default_interval (void);
+
/* Assures the config option is a string, duplicates it and returns the copy in
* "ret_string". If necessary "*ret_string" is freed first. Returns zero upon
* success. */
/* Assures the config option is a number and returns it as an int. */
int cf_util_get_int (const oconfig_item_t *ci, int *ret_value);
+/* Assures the config option is a number and returns it as a double. */
+int cf_util_get_double (const oconfig_item_t *ci, double *ret_value);
+
/* Assures the config option is a boolean and assignes it to `ret_bool'.
* Otherwise, `ret_bool' is not changed and non-zero is returned. */
int cf_util_get_boolean (const oconfig_item_t *ci, _Bool *ret_bool);
DEBUG ("host_processors returned %i %s", (int) cpu_list_len, cpu_list_len == 1 ? "processor" : "processors");
INFO ("cpu plugin: Found %i processor%s.", (int) cpu_list_len, cpu_list_len == 1 ? "" : "s");
- cpu_temp_retry_max = 86400 / CDTIME_T_TO_TIME_T (interval_g);
+ cpu_temp_retry_max = 86400 / CDTIME_T_TO_TIME_T (plugin_get_interval ());
/* #endif PROCESSOR_CPU_LOAD_INFO */
#elif defined(HAVE_LIBKSTAT)
#if KERNEL_LINUX
static counter_t disk_calc_time_incr (counter_t delta_time, counter_t delta_ops)
{
+ double interval = CDTIME_T_TO_DOUBLE (plugin_get_interval ());
double avg_time = ((double) delta_time) / ((double) delta_ops);
- double avg_time_incr = CDTIME_T_TO_DOUBLE (interval_g) * avg_time;
+ double avg_time_incr = interval * avg_time;
return ((counter_t) (avg_time_incr + .5));
}
pcap_obj = pcap_open_live ((pcap_device != NULL) ? pcap_device : "any",
PCAP_SNAPLEN,
0 /* Not promiscuous */,
- (int) CDTIME_T_TO_MS (interval_g / 2),
+ (int) CDTIME_T_TO_MS (plugin_get_interval () / 2),
pcap_error);
if (pcap_obj == NULL)
{
if (listen_thread_init != 0)
return (-1);
- status = pthread_create (&listen_thread, NULL, dns_child_loop,
+ status = plugin_thread_create (&listen_thread, NULL, dns_child_loop,
(void *) 0);
if (status != 0)
{
collectors[i] = (collector_t *)smalloc (sizeof (collector_t));
collectors[i]->socket = NULL;
- if (0 != (err = pthread_create (&collectors[i]->thread, &ptattr,
- collect, collectors[i]))) {
+ if (0 != (err = plugin_thread_create (&collectors[i]->thread,
+ &ptattr, collect, collectors[i]))) {
char errbuf[1024];
log_err ("pthread_create() failed: %s",
sstrerror (errno, errbuf, sizeof (errbuf)));
{
int err = 0;
- if (0 != (err = pthread_create (&connector, NULL,
+ if (0 != (err = plugin_thread_create (&connector, NULL,
open_connection, NULL))) {
char errbuf[1024];
disabled = 1;
char buffer[1024];
#ifdef HAVE_SETENV
- ssnprintf (buffer, sizeof (buffer), "%.3f", CDTIME_T_TO_DOUBLE (interval_g));
+ ssnprintf (buffer, sizeof (buffer), "%.3f",
+ CDTIME_T_TO_DOUBLE (plugin_get_interval ()));
setenv ("COLLECTD_INTERVAL", buffer, /* overwrite = */ 1);
ssnprintf (buffer, sizeof (buffer), "%s", hostname_g);
setenv ("COLLECTD_HOSTNAME", buffer, /* overwrite = */ 1);
#else
ssnprintf (buffer, sizeof (buffer), "COLLECTD_INTERVAL=%.3f",
- CDTIME_T_TO_DOUBLE (interval_g));
+ CDTIME_T_TO_DOUBLE (plugin_get_interval ()));
putenv (buffer);
ssnprintf (buffer, sizeof (buffer), "COLLECTD_HOSTNAME=%s", hostname_g);
pthread_attr_init (&attr);
pthread_attr_setdetachstate (&attr, PTHREAD_CREATE_DETACHED);
- pthread_create (&t, &attr, exec_read_one, (void *) pl);
+ plugin_thread_create (&t, &attr, exec_read_one, (void *) pl);
pthread_attr_destroy (&attr);
} /* for (pl) */
pthread_attr_init (&attr);
pthread_attr_setdetachstate (&attr, PTHREAD_CREATE_DETACHED);
- pthread_create (&t, &attr, exec_notification_one, (void *) pln);
+ plugin_thread_create (&t, &attr, exec_notification_one, (void *) pln);
pthread_attr_destroy (&attr);
} /* for (pl) */
mc_receive_thread_loop = 1;
- status = pthread_create (&mc_receive_thread_id, /* attr = */ NULL,
+ status = plugin_thread_create (&mc_receive_thread_id, /* attr = */ NULL,
mc_receive_thread, /* args = */ NULL);
if (status != 0)
{
int status;
/* Don't send `ADD' notifications during startup (~ 1 minute) */
- time_t iv = CDTIME_T_TO_TIME_T (interval_g);
+ time_t iv = CDTIME_T_TO_TIME_T (plugin_get_interval ());
c_ipmi_init_in_progress = 1 + (60 / iv);
c_ipmi_active = 1;
- status = pthread_create (&thread_id, /* attr = */ NULL, thread_main,
+ status = plugin_thread_create (&thread_id, /* attr = */ NULL, thread_main,
/* user data = */ NULL);
if (status != 0)
{
const char *name;
char uuid[VIR_UUID_STRING_BUFLEN];
- vl->interval = interval_g;
-
sstrncpy (vl->plugin, "libvirt", sizeof (vl->plugin));
vl->host[0] = '\0';
return (EINVAL);
if (host->interval <= 0)
- host->interval = interval_g;
+ host->interval = plugin_get_interval ();
if (slave->instance[0] == 0)
ssnprintf (slave->instance, sizeof (slave->instance), "slave_%i",
vl.values = values;
vl.values_len = 2;
vl.time = 0;
- vl.interval = interval_g;
sstrncpy (vl.host, hostname_g, sizeof (vl.host));
sstrncpy (vl.plugin, "network", sizeof (vl.plugin));
if (dispatch_thread_running == 0)
{
int status;
- status = pthread_create (&dispatch_thread_id,
+ status = plugin_thread_create (&dispatch_thread_id,
NULL /* no attributes */,
dispatch_thread,
NULL /* no argument */);
if (receive_thread_running == 0)
{
int status;
- status = pthread_create (&receive_thread_id,
+ status = plugin_thread_create (&receive_thread_id,
NULL /* no attributes */,
receive_thread,
NULL /* no argument */);
static XS (Collectd_plugin_register_ds);
static XS (Collectd_plugin_unregister_ds);
static XS (Collectd_plugin_dispatch_values);
+static XS (Collectd_plugin_get_interval);
static XS (Collectd__plugin_write);
static XS (Collectd__plugin_flush);
static XS (Collectd_plugin_dispatch_notification);
{ "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_get_interval", Collectd_plugin_get_interval },
{ "Collectd::_plugin_write", Collectd__plugin_write },
{ "Collectd::_plugin_flush", Collectd__plugin_flush },
{ "Collectd::plugin_dispatch_notification",
XSRETURN_EMPTY;
} /* static XS (Collectd_plugin_dispatch_values) */
+/*
+ * Collectd::plugin_get_interval ().
+ */
+static XS (Collectd_plugin_get_interval)
+{
+ dXSARGS;
+
+ /* make sure we don't get any unused variable warnings for 'items';
+ * don't abort, though */
+ if (items)
+ log_err ("Usage: Collectd::plugin_get_interval()");
+
+ XSRETURN_NV ((NV) CDTIME_T_TO_DOUBLE (plugin_get_interval ()));
+} /* static XS (Collectd_plugin_get_interval) */
+
/* Collectd::plugin_write (plugin, ds, vl).
*
* plugin:
static int g_interval_get (pTHX_ SV *var, MAGIC *mg)
{
- cdtime_t *interval = (cdtime_t *)mg->mg_ptr;
- double nv;
-
- nv = CDTIME_T_TO_DOUBLE (*interval);
-
- sv_setnv (var, nv);
+ log_warn ("Accessing $interval_g is deprecated (and might not "
+ "give the desired results) - plugin_get_interval() should "
+ "be used instead.");
+ sv_setnv (var, CDTIME_T_TO_DOUBLE (interval_g));
return 0;
} /* static int g_interval_get (pTHX_ SV *, MAGIC *) */
static int g_interval_set (pTHX_ SV *var, MAGIC *mg)
{
- cdtime_t *interval = (cdtime_t *)mg->mg_ptr;
- double nv;
-
- nv = (double)SvNV (var);
-
- *interval = DOUBLE_TO_CDTIME_T (nv);
+ double nv = (double)SvNV (var);
+ log_warn ("Accessing $interval_g is deprecated (and might not "
+ "give the desired results) - plugin_get_interval() should "
+ "be used instead.");
+ interval_g = DOUBLE_TO_CDTIME_T (nv);
return 0;
} /* static int g_interval_set (pTHX_ SV *, MAGIC *) */
tmp = get_sv ("Collectd::interval_g", /* create = */ 1);
sv_magicext (tmp, NULL, /* how = */ PERL_MAGIC_ext,
/* vtbl = */ &g_interval_vtbl,
- /* name = */ (char *) &interval_g, /* namelen = */ 0);
+ /* name = */ NULL, /* namelen = */ 0);
return;
} /* static void xs_init (pTHX) */
if (collector_thread_running)
return (0);
- status = pthread_create (&collector_thread_id,
+ status = plugin_thread_create (&collector_thread_id,
/* attrs = */ NULL,
collector_thread,
/* args = */ NULL);
ping_thread_loop = 1;
ping_thread_error = 0;
- status = pthread_create (&ping_thread_id, /* attr = */ NULL,
+ status = plugin_thread_create (&ping_thread_id, /* attr = */ NULL,
ping_thread, /* arg = */ (void *) 0);
if (status != 0)
{
{
void *cf_callback;
user_data_t cf_udata;
+ plugin_ctx_t cf_ctx;
};
typedef struct callback_func_s callback_func_t;
* The `rf_super' member MUST be the first one in this structure! */
#define rf_callback rf_super.cf_callback
#define rf_udata rf_super.cf_udata
+#define rf_ctx rf_super.cf_ctx
callback_func_t rf_super;
char rf_group[DATA_MAX_NAME_LEN];
char rf_name[DATA_MAX_NAME_LEN];
static pthread_t *read_threads = NULL;
static int read_threads_num = 0;
+static pthread_key_t plugin_ctx_key;
+static _Bool plugin_ctx_key_initialized = 0;
+
/*
* Static functions
*/
cf->cf_udata = *ud;
}
+ cf->cf_ctx = plugin_get_ctx ();
+
return (register_callback (list, name, cf));
} /* }}} int create_register_callback */
dlh = lt_dlopenadvise(file, advise);
lt_dladvise_destroy(&advise);
} else {
- dlh = lt_dlopen (file);
+ dlh = lt_dlopen (file);
}
#else /* if LIBTOOL_VERSION == 1 */
if (flags & PLUGIN_FLAGS_GLOBAL)
while (read_loop != 0)
{
read_func_t *rf;
+ plugin_ctx_t old_ctx;
cdtime_t now;
int status;
int rf_type;
int rc;
- /* Get the read function that needs to be read next. */
+ /* Get the read function that needs to be read next.
+ * We don't need to hold "read_lock" for the heap, but we need
+ * to call c_heap_get_root() and pthread_cond_wait() in the
+ * same protected block. */
+ pthread_mutex_lock (&read_lock);
rf = c_heap_get_root (read_heap);
if (rf == NULL)
{
- struct timespec abstime;
-
- now = cdtime ();
-
- CDTIME_T_TO_TIMESPEC (now + interval_g, &abstime);
-
- pthread_mutex_lock (&read_lock);
- pthread_cond_timedwait (&read_cond, &read_lock,
- &abstime);
- pthread_mutex_unlock (&read_lock);
+ pthread_cond_wait (&read_cond, &read_lock);
+ pthread_mutex_unlock (&read_lock);
continue;
}
+ pthread_mutex_unlock (&read_lock);
if ((rf->rf_interval.tv_sec == 0) && (rf->rf_interval.tv_nsec == 0))
{
+ /* this should not happen, because the interval is set
+ * for each plugin when loading it
+ * XXX: issue a warning? */
now = cdtime ();
- CDTIME_T_TO_TIMESPEC (interval_g, &rf->rf_interval);
+ CDTIME_T_TO_TIMESPEC (plugin_get_interval (), &rf->rf_interval);
rf->rf_effective_interval = rf->rf_interval;
DEBUG ("plugin_read_thread: Handling `%s'.", rf->rf_name);
+ old_ctx = plugin_set_ctx (rf->rf_ctx);
+
if (rf_type == RF_SIMPLE)
{
int (*callback) (void);
status = (*callback) (&rf->rf_udata);
}
+ plugin_set_ctx (old_ctx);
+
/* If the function signals failure, we will increase the
* intervals in which it will be called. */
if (status != 0)
/* This does not fail. */
llist_append (read_list, le);
+ /* Wake up all the read threads. */
+ pthread_cond_broadcast (&read_cond);
pthread_mutex_unlock (&read_lock);
return (0);
} /* int plugin_insert_read */
+static int read_cb_wrapper (user_data_t *ud)
+{
+ int (*callback) (void);
+
+ if (ud == NULL)
+ return -1;
+
+ callback = ud->data;
+ return callback();
+} /* int read_cb_wrapper */
+
int plugin_register_read (const char *name,
int (*callback) (void))
{
read_func_t *rf;
+ plugin_ctx_t ctx = plugin_get_ctx ();
int status;
+ if (ctx.interval != 0) {
+ /* If ctx.interval is not zero (== use the plugin or global
+ * interval), we need to use the "complex" read callback,
+ * because only that allows to specify a different interval.
+ * Wrap the callback using read_cb_wrapper(). */
+ struct timespec interval;
+ user_data_t user_data;
+
+ user_data.data = callback;
+ user_data.free_func = NULL;
+
+ CDTIME_T_TO_TIMESPEC (ctx.interval, &interval);
+ return plugin_register_complex_read (/* group = */ NULL,
+ name, read_cb_wrapper, &interval, &user_data);
+ }
+
+ DEBUG ("plugin_register_read: default_interval = %.3f",
+ CDTIME_T_TO_DOUBLE(plugin_get_interval ()));
+
rf = malloc (sizeof (*rf));
if (rf == NULL)
{
rf->rf_callback = (void *) callback;
rf->rf_udata.data = NULL;
rf->rf_udata.free_func = NULL;
+ rf->rf_ctx = ctx;
rf->rf_group[0] = '\0';
sstrncpy (rf->rf_name, name, sizeof (rf->rf_name));
rf->rf_type = RF_SIMPLE;
user_data_t *user_data)
{
read_func_t *rf;
+ plugin_ctx_t ctx = plugin_get_ctx ();
int status;
rf = malloc (sizeof (*rf));
{
rf->rf_interval = *interval;
}
+ else if (ctx.interval != 0)
+ {
+ CDTIME_T_TO_TIMESPEC (ctx.interval, &rf->rf_interval);
+ }
rf->rf_effective_interval = rf->rf_interval;
+ DEBUG ("plugin_register_read: interval = %i.%09i",
+ (int) rf->rf_interval.tv_sec,
+ (int) rf->rf_interval.tv_nsec);
+
/* Set user data */
if (user_data == NULL)
{
rf->rf_udata = *user_data;
}
+ rf->rf_ctx = ctx;
+
status = plugin_insert_read (rf);
if (status != 0)
sfree (rf);
{
callback_func_t *cf;
plugin_init_cb callback;
+ plugin_ctx_t old_ctx;
cf = le->value;
+ old_ctx = plugin_set_ctx (cf->cf_ctx);
callback = cf->cf_callback;
status = (*callback) ();
+ plugin_set_ctx (old_ctx);
if (status != 0)
{
while (42)
{
read_func_t *rf;
+ plugin_ctx_t old_ctx;
rf = c_heap_get_root (read_heap);
if (rf == NULL)
break;
+ old_ctx = plugin_set_ctx (rf->rf_ctx);
+
if (rf->rf_type == RF_SIMPLE)
{
int (*callback) (void);
status = (*callback) (&rf->rf_udata);
}
+ plugin_set_ctx (old_ctx);
+
if (status != 0)
{
NOTICE ("read-function of plugin `%s' failed.",
callback_func_t *cf = le->value;
plugin_write_cb callback;
+ /* do not switch plugin context; rather keep the context (interval)
+ * information of the calling read plugin */
+
DEBUG ("plugin: plugin_write: Writing values via %s.", le->key);
callback = cf->cf_callback;
status = (*callback) (ds, vl, &cf->cf_udata);
cf = le->value;
+ /* do not switch plugin context; rather keep the context (interval)
+ * information of the calling read plugin */
+
DEBUG ("plugin: plugin_write: Writing values via %s.", le->key);
callback = cf->cf_callback;
status = (*callback) (ds, vl, &cf->cf_udata);
{
callback_func_t *cf;
plugin_flush_cb callback;
+ plugin_ctx_t old_ctx;
if ((plugin != NULL)
&& (strcmp (plugin, le->key) != 0))
}
cf = le->value;
+ old_ctx = plugin_set_ctx (cf->cf_ctx);
callback = cf->cf_callback;
(*callback) (timeout, identifier, &cf->cf_udata);
+ plugin_set_ctx (old_ctx);
+
le = le->next;
}
return (0);
{
callback_func_t *cf;
plugin_shutdown_cb callback;
+ plugin_ctx_t old_ctx;
cf = le->value;
+ old_ctx = plugin_set_ctx (cf->cf_ctx);
callback = cf->cf_callback;
/* Advance the pointer before calling the callback allows
le = le->next;
(*callback) ();
+
+ plugin_set_ctx (old_ctx);
}
/* Write plugins which use the `user_data' pointer usually need the
{
callback_func_t *cf;
plugin_missing_cb callback;
+ plugin_ctx_t old_ctx;
int status;
cf = le->value;
+ old_ctx = plugin_set_ctx (cf->cf_ctx);
callback = cf->cf_callback;
status = (*callback) (vl, &cf->cf_udata);
+ plugin_set_ctx (old_ctx);
if (status != 0)
{
if (status < 0)
vl->time = cdtime ();
if (vl->interval <= 0)
- vl->interval = interval_g;
+ {
+ plugin_ctx_t ctx = plugin_get_ctx ();
+
+ if (ctx.interval != 0)
+ vl->interval = ctx.interval;
+ else
+ {
+ char name[6 * DATA_MAX_NAME_LEN];
+ FORMAT_VL (name, sizeof (name), vl);
+ ERROR ("plugin_dispatch_values: Unable to determine "
+ "interval from context for "
+ "value list \"%s\". "
+ "This indicates a broken plugin. "
+ "Please report this problem to the "
+ "collectd mailing list or at "
+ "<http://collectd.org/bugs/>.", name);
+ vl->interval = interval_g;
+ }
+ }
DEBUG ("plugin_dispatch_values: time = %.3f; interval = %.3f; "
"host = %s; "
plugin_notification_cb callback;
int status;
+ /* do not switch plugin context; rather keep the context
+ * (interval) information of the calling plugin */
+
cf = le->value;
callback = cf->cf_callback;
status = (*callback) (notif, &cf->cf_udata);
cf = le->value;
callback = cf->cf_callback;
+ /* do not switch plugin context; rather keep the context
+ * (interval) information of the calling plugin */
+
(*callback) (level, msg, &cf->cf_udata);
le = le->next;
return (0);
} /* int plugin_notification_meta_free */
+static void plugin_ctx_destructor (void *ctx)
+{
+ sfree (ctx);
+} /* void plugin_ctx_destructor */
+
+static plugin_ctx_t ctx_init = { /* interval = */ 0 };
+
+static plugin_ctx_t *plugin_ctx_create (void)
+{
+ plugin_ctx_t *ctx;
+
+ ctx = malloc (sizeof (*ctx));
+ if (ctx == NULL) {
+ char errbuf[1024];
+ ERROR ("Failed to allocate plugin context: %s",
+ sstrerror (errno, errbuf, sizeof (errbuf)));
+ return NULL;
+ }
+
+ *ctx = ctx_init;
+ assert (plugin_ctx_key_initialized);
+ pthread_setspecific (plugin_ctx_key, ctx);
+ DEBUG("Created new plugin context.");
+ return (ctx);
+} /* int plugin_ctx_create */
+
+void plugin_init_ctx (void)
+{
+ pthread_key_create (&plugin_ctx_key, plugin_ctx_destructor);
+ plugin_ctx_key_initialized = 1;
+} /* void plugin_init_ctx */
+
+plugin_ctx_t plugin_get_ctx (void)
+{
+ plugin_ctx_t *ctx;
+
+ assert (plugin_ctx_key_initialized);
+ ctx = pthread_getspecific (plugin_ctx_key);
+
+ if (ctx == NULL) {
+ ctx = plugin_ctx_create ();
+ /* this must no happen -- exit() instead? */
+ if (ctx == NULL)
+ return ctx_init;
+ }
+
+ return (*ctx);
+} /* plugin_ctx_t plugin_get_ctx */
+
+plugin_ctx_t plugin_set_ctx (plugin_ctx_t ctx)
+{
+ plugin_ctx_t *c;
+ plugin_ctx_t old;
+
+ assert (plugin_ctx_key_initialized);
+ c = pthread_getspecific (plugin_ctx_key);
+
+ if (c == NULL) {
+ c = plugin_ctx_create ();
+ /* this must no happen -- exit() instead? */
+ if (c == NULL)
+ return ctx_init;
+ }
+
+ old = *c;
+ *c = ctx;
+
+ return (old);
+} /* void plugin_set_ctx */
+
+cdtime_t plugin_get_interval (void)
+{
+ cdtime_t interval;
+
+ interval = plugin_get_ctx().interval;
+ if (interval > 0)
+ return interval;
+
+ return cf_get_default_interval ();
+} /* cdtime_t plugin_get_interval */
+
+typedef struct {
+ plugin_ctx_t ctx;
+ void *(*start_routine) (void *);
+ void *arg;
+} plugin_thread_t;
+
+static void *plugin_thread_start (void *arg)
+{
+ plugin_thread_t *plugin_thread = arg;
+
+ void *(*start_routine) (void *) = plugin_thread->start_routine;
+ void *plugin_arg = plugin_thread->arg;
+
+ plugin_set_ctx (plugin_thread->ctx);
+
+ free (plugin_thread);
+
+ return start_routine (plugin_arg);
+} /* void *plugin_thread_start */
+
+int plugin_thread_create (pthread_t *thread, const pthread_attr_t *attr,
+ void *(*start_routine) (void *), void *arg)
+{
+ plugin_thread_t *plugin_thread;
+
+ plugin_thread = malloc (sizeof (*plugin_thread));
+ if (plugin_thread == NULL)
+ return -1;
+
+ plugin_thread->ctx = plugin_get_ctx ();
+ plugin_thread->start_routine = start_routine;
+ plugin_thread->arg = arg;
+
+ return pthread_create (thread, attr,
+ plugin_thread_start, plugin_thread);
+} /* int plugin_thread_create */
+
/* vim: set sw=8 ts=8 noet fdm=marker : */
#define NOTIF_WARNING 2
#define NOTIF_OKAY 4
+#define plugin_interval (plugin_get_ctx().interval)
+
/*
* Public data types
*/
};
typedef struct value_list_s value_list_t;
-#define VALUE_LIST_INIT { NULL, 0, 0, interval_g, "localhost", "", "", "", "", NULL }
+#define VALUE_LIST_INIT { NULL, 0, 0, plugin_get_interval (), \
+ "localhost", "", "", "", "", NULL }
#define VALUE_LIST_STATIC { NULL, 0, 0, 0, "localhost", "", "", "", "", NULL }
struct data_source_s
};
typedef struct user_data_s user_data_t;
+struct plugin_ctx_s
+{
+ cdtime_t interval;
+};
+typedef struct plugin_ctx_s plugin_ctx_t;
+
/*
* Callback types
*/
int plugin_notification_meta_free (notification_meta_t *n);
+/*
+ * Plugin context management.
+ */
+
+void plugin_init_ctx (void);
+
+plugin_ctx_t plugin_get_ctx (void);
+plugin_ctx_t plugin_set_ctx (plugin_ctx_t ctx);
+
+/*
+ * NAME
+ * plugin_get_interval
+ *
+ * DESCRIPTION
+ * This function returns the current value of the plugin's interval. The
+ * return value will be strictly greater than zero in all cases. If
+ * everything else fails, it will fall back to 10 seconds.
+ */
+cdtime_t plugin_get_interval (void);
+
+/*
+ * Context-aware thread management.
+ */
+
+int plugin_thread_create (pthread_t *thread, const pthread_attr_t *attr,
+ void *(*start_routine) (void *), void *arg);
+
#endif /* PLUGIN_H */
case C_PSQL_PARAM_INTERVAL:
ssnprintf (interval, sizeof (interval), "%.3f",
(db->interval > 0)
- ? CDTIME_T_TO_DOUBLE (db->interval) : interval_g);
+ ? CDTIME_T_TO_DOUBLE (db->interval)
+ : plugin_get_interval ());
params[i] = interval;
break;
case C_PSQL_PARAM_INSTANCE:
break;
}
- cdt_timeout = interval_g * 3 / 4;
+ cdt_timeout = plugin_get_interval () * 3 / 4;
if (cdt_timeout < TIME_T_TO_CDTIME_T (2))
cdt_timeout = TIME_T_TO_CDTIME_T (2);
pthread_sigmask(SIG_BLOCK, &sigset, NULL);
state = PyEval_SaveThread();
if (do_interactive) {
- if (pthread_create(&thread, NULL, cpy_interactive, NULL)) {
+ if (plugin_thread_create(&thread, NULL, cpy_interactive, NULL)) {
ERROR("python: Error creating thread for interactive interpreter.");
}
}
if (rrdcreate_config.heartbeat <= 0)
rrdcreate_config.heartbeat = 2 * rrdcreate_config.stepsize;
- if ((rrdcreate_config.heartbeat > 0)
- && (rrdcreate_config.heartbeat < CDTIME_T_TO_TIME_T (interval_g)))
- WARNING ("rrdtool plugin: Your `heartbeat' is "
- "smaller than your `interval'. This will "
- "likely cause problems.");
- else if ((rrdcreate_config.stepsize > 0)
- && (rrdcreate_config.stepsize < CDTIME_T_TO_TIME_T (interval_g)))
- WARNING ("rrdtool plugin: Your `stepsize' is "
- "smaller than your `interval'. This will "
- "create needlessly big RRD-files.");
-
/* Set the cache up */
pthread_mutex_lock (&cache_lock);
pthread_mutex_unlock (&cache_lock);
- status = pthread_create (&queue_thread, /* attr = */ NULL,
+ status = plugin_thread_create (&queue_thread, /* attr = */ NULL,
rrd_queue_thread, /* args = */ NULL);
if (status != 0)
{
host = ud->data;
if (host->interval == 0)
- host->interval = interval_g;
+ host->interval = plugin_get_interval ();
time_start = cdtime ();
DEBUG ("Spawning child to handle connection on fd #%i", *remote_fd);
- status = pthread_create (&th, &th_attr, us_handle_client, (void *) remote_fd);
+ status = plugin_thread_create (&th, &th_attr,
+ us_handle_client, (void *) remote_fd);
if (status != 0)
{
char errbuf[1024];
loop = 1;
- status = pthread_create (&listen_thread, NULL, us_server_thread, NULL);
+ status = plugin_thread_create (&listen_thread, NULL,
+ us_server_thread, NULL);
if (status != 0)
{
char errbuf[1024];
buffer_ident,
(vl->interval > 0)
? CDTIME_T_TO_DOUBLE (vl->interval)
- : CDTIME_T_TO_DOUBLE (interval_g),
+ : CDTIME_T_TO_DOUBLE (plugin_get_interval ()),
buffer_values);
return (0);
c->last = now;
- if (c->interval < interval_g)
- c->interval = interval_g;
+ if (c->interval < plugin_get_interval ())
+ c->interval = plugin_get_interval ();
else
c->interval *= 2;