Hi,
I have written a simple sysctl plugin.
Initially I wanted to create a general sysctl plugin, but I ended up writing a
simple plugin that reads integer values from sysctl. The plugin relies on the
sysctlnametomib function (that is very similar to the sysctlnametomib) and
should work on BSD like systems (Mac OS X?). I have tested the plugin only on
FreeBSD.
For example one can get temperature statistics with
<Plugin sysctl>
Instance "coretemp"
<Entry "dev.cpu.0.temperature">
Type "fbsd_temp" # Real value = (fbsd_temp - 2732)/10.0
Instance "cpu-0"
</Entry>
<Entry "dev.cpu.1.temperature">
Type "fbsd_temp"
Instance "cpu-1"
</Entry>
</Plugin>
or ZFS arc statistics (like the ZFS ARC plugin)
<Plugin sysctl>
Instance "zfs-arc"
<Entry "kstat.zfs.misc.arcstats.size">
Instance "arc_size"
Type "gauge"
</Entry>
<Entry "kstat.zfs.misc.arcstats.l2_size">
Instance "l2_size"
Type "gauge"
</Entry>
... etc ...
</Plugin>
This plugin has proven useful for me and I wanted to share it. However it
currently supports only integer sysctl values. A patch in the attachment is
against collectd version 5.0.0.
I'm quite new in open source contribution, so feel free to comment my code.
Toni Ylenius
--- configure.in
+++ configure.in
@@ -608,6 +608,7 @@ AM_CONDITIONAL(BUILD_WITH_LIBPOSIX4, test "x$clock_gettime_needs_posix4" = "xyes
AC_CHECK_FUNCS(sysctl, [have_sysctl="yes"], [have_sysctl="no"])
AC_CHECK_FUNCS(sysctlbyname, [have_sysctlbyname="yes"], [have_sysctlbyname="no"])
+AC_CHECK_FUNCS(sysctlnametomib, [have_sysctlnametomib="yes"], [have_sysctlnametomib="no"])
AC_CHECK_FUNCS(host_statistics, [have_host_statistics="yes"], [have_host_statistics="no"])
AC_CHECK_FUNCS(processor_info, [have_processor_info="yes"], [have_processor_info="no"])
AC_CHECK_FUNCS(thread_info, [have_thread_info="yes"], [have_thread_info="no"])
@@ -4342,6 +4343,7 @@ plugin_processes="no"
plugin_protocols="no"
plugin_serial="no"
plugin_swap="no"
+plugin_sysctl="no"
plugin_tape="no"
plugin_tcpconns="no"
plugin_ted="no"
@@ -4487,6 +4489,10 @@ then
plugin_memory="yes"
plugin_tcpconns="yes"
fi
+if test "x$have_sysctl" = "xyes" && test "x$have_sysctlnametomib" = "xyes"
+then
+ plugin_sysctl="yes"
+fi
# Df plugin: Check if we know how to determine mount points first.
#if test "x$have_listmntent" = "xyes"; then
@@ -4689,6 +4695,7 @@ AC_PLUGIN([sensors], [$with_libsensors], [lm_sensors statistics])
AC_PLUGIN([serial], [$plugin_serial], [serial port traffic])
AC_PLUGIN([snmp], [$with_libnetsnmp], [SNMP querying plugin])
AC_PLUGIN([swap], [$plugin_swap], [Swap usage statistics])
+AC_PLUGIN([sysctl], [$plugin_sysctl], [Query sysctl for statistics])
AC_PLUGIN([syslog], [$have_syslog], [Syslog logging plugin])
AC_PLUGIN([table], [yes], [Parsing of tabular data])
AC_PLUGIN([tail], [yes], [Parsing of logfiles])
@@ -5015,6 +5022,7 @@ Configuration:
serial . . . . . . . $enable_serial
snmp . . . . . . . . $enable_snmp
swap . . . . . . . . $enable_swap
+ sysctl . . . . . . . $enable_sysctl
syslog . . . . . . . $enable_syslog
table . . . . . . . . $enable_table
tail . . . . . . . . $enable_tail
--- src/Makefile.am
+++ src/Makefile.am
@@ -1021,6 +1021,14 @@ endif
endif
+if BUILD_PLUGIN_SYSCTL
+pkglib_LTLIBRARIES += sysctl.la
+sysctl_la_SOURCES = sysctl.c
+sysctl_la_LDFLAGS = -module -avoid-version
+collectd_LDADD += "-dlopen" sysctl.la
+collectd_DEPENDENCIES += sysctl.la
+endif
+
if BUILD_PLUGIN_SYSLOG
pkglib_LTLIBRARIES += syslog.la
syslog_la_SOURCES = syslog.c
--- /dev/null
+++ src/sysctl.c
@@ -0,0 +1,420 @@
+/**
+ * collectd - src/sysctl.c
+ * Copyright (C) 2011 Toni Ylenius
+ *
+ * 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
+ * Free Software Foundation; only version 2 of the License is applicable.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * Authors:
+ * Toni Ylenius <toni.ylenius at iki.fi>
+ **/
+
+#include "collectd.h"
+#include "common.h"
+#include "plugin.h"
+
+#ifdef HAVE_SYS_SYSCTL_H
+# include <sys/sysctl.h>
+#endif
+
+#if HAVE_SYSCTL && HAVE_SYSCTLNAMETOMIB
+/* no global variables */
+/* #endif HAVE_SYSCTL && HAVE_SYSCTLNAMETOMIB */
+
+#else
+# error "Plugin needs sysctl() and sysctlnametomib()."
+#endif
+
+/*
+ * <Plugin sysctl>
+ * Instance "Sysctls-number-1"
+ * <Entry "hw.acpi.thermal.tz0.temperature">
+ * Type "gauge"
+ * Instance "CPU-temperature"
+ * </Entry>
+ * </Plugin>
+ *
+ * Sysctl block:
+ *
+ * Instance Name
+ * Name of the plugin instance. This must be defined.
+ *
+ * Entry block:
+ *
+ * Type type
+ * The values will be saved in this type (see types.db). Types are in
+ * lowercase. This option must be defined.
+ *
+ * Instance Name
+ * Optional instance name for an entry. If this is not defined entry's name
+ * will be used instead.
+ *
+ * The plugin retrieves values using sysctl calls. This works basically on
+ * BSD:s, but sysctl calls are not so useful on Linux after all. The plugin
+ * uses sysctlnametomib() to retrieve the numerical MIB an uses it to retrieve
+ * the values. This will speed up sysctl calls a lot according to FreeBSD
+ * sysctl(3).
+ *
+ * Currently plugin is quite simple. It reads entries' eight first bytes to 64
+ * bit integers and converts values to collectd values. The plugin does not
+ * support tabular or string entries.
+ */
+
+struct sysctl_entry_s
+{
+ char *plugin_instance;
+ char *name;
+ char *instance;
+ char *type_string;
+ int type; /* data_set's data_source type */
+ int mib[CTL_MAXNAME];
+ size_t mib_length;
+};
+typedef struct sysctl_entry_s sysctl_entry_t;
+
+
+/* Globals */
+static sysctl_entry_t **sysctl_entries = NULL;
+static size_t entries_num = 0;
+
+/* ---------- configuration ---------- */
+static int sysctl_config_set_string (char **target_string, char *input)
+{
+ char *ptr = NULL;
+ char *copy = NULL;
+
+ /* There should be no / characters in instance name, so they are not allowed
+ * in any string
+ */
+ for (ptr = input; *ptr != 0; ptr++)
+ {
+ if (*ptr == '/')
+ {
+ WARNING ("Sysctl plugin: There should be no `/' characters"
+ " in string.");
+ return (1);
+ }
+ }
+
+ /* Try to copy */
+ copy = strdup (input);
+ if (copy == NULL)
+ {
+ ERROR ("Sysctl plugin: strdup failed.");
+ return (1);
+ }
+
+ sfree (*target_string);
+ *target_string = copy;
+
+ return (0);
+} /* static int sysctl_config_set_string */
+
+static int sysctl_config_set_option (const char *option, char **target_string,
+ oconfig_item_t *ci)
+{
+ if ((ci->values_num != 1)
+ || (ci->values[0].type != OCONFIG_TYPE_STRING))
+ {
+ WARNING ("Sysctl plugin: The `%s' config option needs exactly "
+ "one string argument.", option);
+ return (1);
+ }
+
+ return (sysctl_config_set_string (target_string,
+ ci->values[0].value.string));
+} /* static int sysctl_config_set_option */
+
+static int sysctl_config_add_entry (oconfig_item_t *ci, char *plugin_instance) {
+ sysctl_entry_t *entry = NULL;
+ int status = 0;
+ int i;
+
+ if ((ci->values_num != 1) || (ci->values[0].type != OCONFIG_TYPE_STRING))
+ {
+ WARNING ("Sysctl plugin: `Entry' needs exactly one string argument.");
+ return (1);
+ }
+
+ if (plugin_instance == NULL)
+ {
+ ERROR ("Sysctl plugin: `Instance' needs to be defined before"
+ " `Entry'.");
+ return (1);
+ }
+
+ /* Initialize and clean memory for an entry */
+ entry = (sysctl_entry_t *) malloc (sizeof (*entry));
+ if (entry == NULL)
+ {
+ ERROR ("Sysctl plugin: malloc failed.");
+ return (1);
+ }
+ memset (entry, 0, sizeof (*entry)); /* TODO is this necessary */
+
+ /* Initialize entry's values */
+ entry->plugin_instance = NULL;
+ entry->name = NULL;
+ entry->instance = NULL;
+ entry->type_string = NULL;
+ entry->type = 0;
+ /* TODO How to initialize entry->mib */
+ entry->mib_length = 0;
+
+ /* Set */
+ status = sysctl_config_set_string (&entry->name,
+ ci->values[0].value.string);
+ status += sysctl_config_set_string (&entry->plugin_instance,
+ plugin_instance);
+
+ for (i = 0; (status == 0) && (i < ci->children_num); i++)
+ {
+ oconfig_item_t *option = ci->children + i;
+
+ if (strcasecmp ("Instance", option->key) == 0)
+ status = sysctl_config_set_option ("Instance", &entry->instance,
+ option);
+ else if (strcasecmp ("Type", option->key) == 0)
+ status = sysctl_config_set_option ("Type", &entry->type_string,
+ option);
+ else
+ {
+ WARNING ("Sysctl plugin: Option `%s' not allowed here.",
+ option->key);
+ status = 1;
+ }
+ } /* for */
+
+
+ if (status == 0)
+ {
+ if (entry->type_string == NULL)
+ {
+ WARNING ("Sysctl plugin: Option `Type' should be defined"
+ " in `Entry' block.");
+ status = 1;
+ }
+ else if (entry->instance == NULL)
+ {
+ status = sysctl_config_set_string (&entry->instance, entry->name);
+ }
+ }
+
+ if (status == 0)
+ {
+ sysctl_entry_t **temp_array;
+
+ temp_array = (sysctl_entry_t**) realloc (sysctl_entries,
+ sizeof (*sysctl_entries) * (entries_num + 1));
+ if (temp_array == NULL)
+ {
+ ERROR ("Sysctl plugin: realloc failed.");
+ status = 1;
+ }
+ else
+ {
+ sysctl_entries = temp_array;
+ sysctl_entries[entries_num] = entry;
+ entries_num++;
+ }
+ }
+
+ if (status != 0)
+ {
+ WARNING ("Sysctl plugin: There was configuration errors in `Entry'"
+ " `%s'.", entry->name);
+ sfree (entry->plugin_instance);
+ sfree (entry->name);
+ sfree (entry->instance);
+ sfree (entry->type_string);
+ sfree (entry);
+ return (1);
+ }
+
+ return (0);
+} /* static int sysctl_config_add_entry */
+
+static int sysctl_config (oconfig_item_t *ci)
+{
+ char *plugin_instance = NULL;
+ int status = 0;
+ int i;
+
+ /* Read all the entries even if some of them contain errors. In addition
+ * this structure allows one to define plugin_instance many times and the
+ * new definition will override the old one.
+ */
+ for (i = 0; i < ci->children_num; i++)
+ {
+ oconfig_item_t *child = ci->children + i;
+
+ if (strcasecmp ("Instance", child->key) == 0)
+ status += sysctl_config_set_option ("Instance", &plugin_instance,
+ child);
+ else if (strcasecmp ("Entry", child->key) == 0)
+ status += sysctl_config_add_entry (child, plugin_instance);
+ else
+ {
+ WARNING ("Sysctl plugin: Ignoring unknown config option `%s'.",
+ child->key);
+ }
+ }
+
+ return (status);
+} /* static int sysctl_config */
+
+/* ---------- initialization ---------- */
+static int sysctl_init(void)
+{
+ /* Check if types are defined and resolve MIB values for sysctls variable
+ * names. Prefetched MIBs will speed up sysctl queries according to FreeBSD
+ * sysctl(3)
+ */
+ size_t i;
+
+ for (i = 0; i < entries_num; i++)
+ {
+ const data_set_t *data_set = NULL;
+
+ /* If any of checks fail mib_length won't be set and read function does
+ * not read the entry in question.
+ */
+ data_set = plugin_get_ds (sysctl_entries[i]->type_string);
+ if (data_set == NULL)
+ {
+ WARNING ("Sysctl plugin: Type (dataset) `%s' is not defined.",
+ sysctl_entries[i]->type_string);
+ }
+ else if (data_set->ds_num != 1)
+ {
+ WARNING ("Sysctl plugin: Type (dataset) `%s' needs more than one"
+ " value and the plugin offers only one.",
+ sysctl_entries[i]->type_string);
+ }
+ else
+ {
+ int status = 0;
+ int mib[CTL_MAXNAME] = { 0 };
+ size_t mib_len = sizeof (mib);
+
+ /* Save the type and fetch mib */
+ sysctl_entries[i]->type = data_set->ds[0].type;
+
+ status = sysctlnametomib (sysctl_entries[i]->name,
+ mib, &mib_len);
+ if (status != 0)
+ {
+ WARNING ("Sysctl plugin: sysctlnametomib (%s) failed",
+ sysctl_entries[i]->name);
+ }
+ else
+ {
+ int j;
+
+ for (j = 0; j < CTL_MAXNAME; j++)
+ sysctl_entries[i]->mib[j] = mib[j];
+
+ sysctl_entries[i]->mib_length = mib_len;
+ }
+ }
+ }
+
+ return(0);
+} /* static int sysctl_init */
+
+/* ---------- read ---------- */
+static void sysctl_submit (sysctl_entry_t const *entry, int64_t input)
+{
+ value_t values[1];
+ value_list_t vl = VALUE_LIST_INIT;
+
+ if (entry->type == DS_TYPE_ABSOLUTE)
+ values[0].absolute = (absolute_t) input;
+ else if (entry->type == DS_TYPE_COUNTER)
+ values[0].counter = (counter_t) input;
+ else if (entry->type == DS_TYPE_DERIVE)
+ values[0].derive = (derive_t) input;
+ else if (entry->type == DS_TYPE_GAUGE)
+ values[0].gauge = (gauge_t) input;
+
+ /* Set the structure */
+ vl.values = values;
+ vl.values_len = 1;
+ sstrncpy (vl.host, hostname_g, sizeof (vl.host));
+ sstrncpy (vl.plugin, "sysctl", sizeof (vl.plugin));
+ sstrncpy (vl.plugin_instance, entry->plugin_instance,
+ sizeof (vl.plugin_instance));
+ sstrncpy (vl.type, entry->type_string, sizeof (vl.type));
+ sstrncpy (vl.type_instance, entry->instance, sizeof (vl.type_instance));
+
+ plugin_dispatch_values (&vl);
+} /* static void sysctl_submit */
+
+static int sysctl_read (void)
+{
+ size_t i;
+
+ for (i = 0; i < entries_num; i++)
+ {
+ /* Query sysctl only if entry's initialization succeeded */
+ if (sysctl_entries[i]->mib_length != 0)
+ {
+ /* Read to 64 bit integer */
+ int64_t value = 0;
+ size_t value_len = sizeof (value);
+ int status = 0;
+
+ status = sysctl (sysctl_entries[i]->mib,
+ sysctl_entries[i]->mib_length,
+ &value, &value_len,
+ /* new pointer = */ NULL, /* new length = */ 0);
+ if (status != 0)
+ {
+ ERROR ("Sysctl plugin: sysctl (%s) failed",
+ sysctl_entries[i]->name);
+ return(1);
+ }
+
+ sysctl_submit(sysctl_entries[i],value);
+ }
+ }
+
+ return(0);
+} /* static int sysctl_read */
+
+/* ---------- shutdown ---------- */
+static int sysctl_clean (void)
+{
+ size_t i;
+
+ for (i = 0; i < entries_num; i++)
+ {
+ sfree (sysctl_entries[i]->plugin_instance);
+ sfree (sysctl_entries[i]->name);
+ sfree (sysctl_entries[i]->instance);
+ sfree (sysctl_entries[i]->type_string);
+ sfree (sysctl_entries[i]);
+ sysctl_entries[i] = NULL;
+ }
+
+ return(0);
+} /* static int sysctl_clean */
+
+/* ---------- module_register ---------- */
+void module_register (void)
+{
+ plugin_register_complex_config ("sysctl", sysctl_config);
+ plugin_register_init ("sysctl", sysctl_init);
+ plugin_register_read ("sysctl", sysctl_read);
+ plugin_register_shutdown ("sysctl", sysctl_clean);
+} /* void module_register */
_______________________________________________
collectd mailing list
[email protected]
http://mailman.verplant.org/listinfo/collectd