+2009-03-18, Version 4.6.2
+ * collectd: Some Solaris utility code has been improved.
+ * filter subsystem: Allow `Chains' without default targets.
+ * liboping: A patch to comply with strict aliasing rules has been
+ added.
+ * timediff match: Fix a typo: The match was registered with a wrong
+ name which prevented this match to be used as documented. Thanks to
+ Bruno Prémont for finding this problem.
+ * bind plugin: Fix collection of the cached RR sets. The number of RR
+ sets currently in the cache was collected as a counter value, which
+ is nonsense. Thanks to Bruno Prémont for implementing this.
+ * dns plugin: Don't pass NULL to `pcap_open_live': Some systems,
+ primarily BSDs, don't take it well and crash.
+ * oracle plugin: Portability to 64 bit systems has been improved.
+ * postgresql plugin: The default configuration has been improved.
+ * rrdtool plugin: Fix a possible race condition: If the network plugin
+ is brought and dispatches a value before the rrdtool plugin is
+ initialized, the daemon may crash.
+
2009-02-22, Version 4.6.1
* collectd: Many documentation fixes.
* Collectd::Unixsock: Error handling has been improved.
*/
public class PluginData {
- protected long _time;
+ protected long _time = 0;
protected String _host;
protected String _plugin;
protected String _pluginInstance = "";
private List<Number> _values = new ArrayList<Number>();
private DataSet _ds;
- private long _interval;
+ private long _interval = 0;
public ValueList() {
--- /dev/null
+/*
+ * collectd/java - org/collectd/java/JMXMemory.java
+ * Copyright (C) 2009 Florian octo Forster
+ *
+ * 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:
+ * Florian octo Forster <octo at verplant.org>
+ */
+
+package org.collectd.java;
+
+import java.util.List;
+import java.util.Date;
+
+import java.lang.management.ManagementFactory;
+import java.lang.management.MemoryUsage;
+import java.lang.management.MemoryMXBean;
+
+import javax.management.MBeanServerConnection;
+import javax.management.remote.JMXConnector;
+import javax.management.remote.JMXConnectorFactory;
+import javax.management.remote.JMXServiceURL;
+
+import org.collectd.api.Collectd;
+import org.collectd.api.DataSet;
+import org.collectd.api.ValueList;
+import org.collectd.api.Notification;
+import org.collectd.api.OConfigItem;
+
+import org.collectd.api.CollectdConfigInterface;
+import org.collectd.api.CollectdInitInterface;
+import org.collectd.api.CollectdReadInterface;
+import org.collectd.api.CollectdShutdownInterface;
+
+import org.collectd.api.OConfigValue;
+import org.collectd.api.OConfigItem;
+
+public class JMXMemory implements CollectdConfigInterface, /* {{{ */
+ CollectdInitInterface,
+ CollectdReadInterface,
+ CollectdShutdownInterface
+{
+ private String _jmx_service_url = null;
+ private MemoryMXBean _mbean = null;
+
+ public JMXMemory ()
+ {
+ Collectd.registerConfig ("JMXMemory", this);
+ Collectd.registerInit ("JMXMemory", this);
+ Collectd.registerRead ("JMXMemory", this);
+ Collectd.registerShutdown ("JMXMemory", this);
+ }
+
+ private void submit (String plugin_instance, MemoryUsage usage) /* {{{ */
+ {
+ ValueList vl;
+
+ long mem_init;
+ long mem_used;
+ long mem_committed;
+ long mem_max;
+
+ mem_init = usage.getInit ();
+ mem_used = usage.getUsed ();
+ mem_committed = usage.getCommitted ();
+ mem_max = usage.getMax ();
+
+ Collectd.logDebug ("JMXMemory plugin: plugin_instance = " + plugin_instance + "; "
+ + "mem_init = " + mem_init + "; "
+ + "mem_used = " + mem_used + "; "
+ + "mem_committed = " + mem_committed + "; "
+ + "mem_max = " + mem_max + ";");
+
+ vl = new ValueList ();
+
+ vl.setHost ("localhost");
+ vl.setPlugin ("JMXMemory");
+ vl.setPluginInstance (plugin_instance);
+ vl.setType ("memory");
+
+ if (mem_init >= 0)
+ {
+ vl.addValue (mem_init);
+ vl.setTypeInstance ("init");
+ Collectd.dispatchValues (vl);
+ vl.clearValues ();
+ }
+
+ if (mem_used >= 0)
+ {
+ vl.addValue (mem_used);
+ vl.setTypeInstance ("used");
+ Collectd.dispatchValues (vl);
+ vl.clearValues ();
+ }
+
+ if (mem_committed >= 0)
+ {
+ vl.addValue (mem_committed);
+ vl.setTypeInstance ("committed");
+ Collectd.dispatchValues (vl);
+ vl.clearValues ();
+ }
+
+ if (mem_max >= 0)
+ {
+ vl.addValue (mem_max);
+ vl.setTypeInstance ("max");
+ Collectd.dispatchValues (vl);
+ vl.clearValues ();
+ }
+ } /* }}} void submit */
+
+ private int configServiceURL (OConfigItem ci) /* {{{ */
+ {
+ List<OConfigValue> values;
+ OConfigValue cv;
+
+ values = ci.getValues ();
+ if (values.size () != 1)
+ {
+ Collectd.logError ("JMXMemory plugin: The JMXServiceURL option needs "
+ + "exactly one string argument.");
+ return (-1);
+ }
+
+ cv = values.get (0);
+ if (cv.getType () != OConfigValue.OCONFIG_TYPE_STRING)
+ {
+ Collectd.logError ("JMXMemory plugin: The JMXServiceURL option needs "
+ + "exactly one string argument.");
+ return (-1);
+ }
+
+ _jmx_service_url = cv.getString ();
+ return (0);
+ } /* }}} int configServiceURL */
+
+ public int config (OConfigItem ci) /* {{{ */
+ {
+ List<OConfigItem> children;
+ int i;
+
+ Collectd.logDebug ("JMXMemory plugin: config: ci = " + ci + ";");
+
+ children = ci.getChildren ();
+ for (i = 0; i < children.size (); i++)
+ {
+ OConfigItem child;
+ String key;
+
+ child = children.get (i);
+ key = child.getKey ();
+ if (key.equalsIgnoreCase ("JMXServiceURL"))
+ {
+ configServiceURL (child);
+ }
+ else
+ {
+ Collectd.logError ("JMXMemory plugin: Unknown config option: " + key);
+ }
+ }
+
+ return (0);
+ } /* }}} int config */
+
+ public int init () /* {{{ */
+ {
+ JMXServiceURL service_url;
+ JMXConnector connector;
+ MBeanServerConnection connection;
+
+ if (_jmx_service_url == null)
+ {
+ Collectd.logError ("JMXMemory: _jmx_service_url == null");
+ return (-1);
+ }
+
+ try
+ {
+ service_url = new JMXServiceURL (_jmx_service_url);
+ connector = JMXConnectorFactory.connect (service_url);
+ connection = connector.getMBeanServerConnection ();
+ _mbean = ManagementFactory.newPlatformMXBeanProxy (connection,
+ ManagementFactory.MEMORY_MXBEAN_NAME,
+ MemoryMXBean.class);
+ }
+ catch (Exception e)
+ {
+ Collectd.logError ("JMXMemory: Creating MBean failed: " + e);
+ return (-1);
+ }
+
+ return (0);
+ } /* }}} int init */
+
+ public int read () /* {{{ */
+ {
+ if (_mbean == null)
+ {
+ Collectd.logError ("JMXMemory: _mbean == null");
+ return (-1);
+ }
+
+ submit ("heap", _mbean.getHeapMemoryUsage ());
+ submit ("non_heap", _mbean.getNonHeapMemoryUsage ());
+
+ return (0);
+ } /* }}} int read */
+
+ public int shutdown () /* {{{ */
+ {
+ System.out.print ("org.collectd.java.JMXMemory.Shutdown ();\n");
+ _jmx_service_url = null;
+ _mbean = null;
+ return (0);
+ } /* }}} int shutdown */
+} /* }}} class JMXMemory */
+
+/* vim: set sw=2 sts=2 et fdm=marker : */
AC_FUNC_STRERROR_R
AC_CACHE_CHECK([for strtok_r],
- [have_strtok_r_default],
+ [c_cv_have_strtok_r_default],
AC_LINK_IFELSE(
AC_LANG_PROGRAM(
[[[[
printf ("token = %s;\n", token);
}
]]]]),
- [have_strtok_r_default="yes"],
- [have_strtok_r_default="no"]
+ [c_cv_have_strtok_r_default="yes"],
+ [c_cv_have_strtok_r_default="no"]
)
)
-if test "x$have_strtok_r_default" = "xno"
+if test "x$c_cv_have_strtok_r_default" = "xno"
then
SAVE_CFLAGS="$CFLAGS"
CFLAGS="$CFLAGS -D_REENTRANT=1"
AC_CACHE_CHECK([if strtok_r needs _REENTRANT],
- [have_strtok_r_reentrant],
+ [c_cv_have_strtok_r_reentrant],
AC_LINK_IFELSE(
AC_LANG_PROGRAM(
[[[[
printf ("token = %s;\n", token);
}
]]]]),
- [have_strtok_r_reentrant="yes"],
+ [c_cv_have_strtok_r_reentrant="yes"],
[AC_MSG_FAILURE([strtok_r isn't available. Please file a bugreport!])]
)
)
[nan_type="none"])
if test "x$nan_type" = "xnone"; then
AC_CACHE_CHECK([whether NAN is defined by default],
- [have_nan_default],
+ [c_cv_have_nan_default],
AC_COMPILE_IFELSE(
AC_LANG_PROGRAM(
[[
else
return 1;
]]),
- [have_nan_default="yes"],
- [have_nan_default="no"]
+ [c_cv_have_nan_default="yes"],
+ [c_cv_have_nan_default="no"]
)
)
- if test "x$have_nan_default" = "xyes"
+ if test "x$c_cv_have_nan_default" = "xyes"
then
nan_type="default"
fi
fi
if test "x$nan_type" = "xnone"; then
AC_CACHE_CHECK([whether NAN is defined by __USE_ISOC99],
- [have_nan_isoc],
+ [c_cv_have_nan_isoc],
AC_COMPILE_IFELSE(
AC_LANG_PROGRAM(
[[
else
return 1;
]]),
- [have_nan_isoc="yes"],
- [have_nan_isoc="no"]
+ [c_cv_have_nan_isoc="yes"],
+ [c_cv_have_nan_isoc="no"]
)
)
- if test "x$have_nan_isoc" = "xyes"
+ if test "x$c_cv_have_nan_isoc" = "xyes"
then
nan_type="isoc99"
fi
fi
if test "x$nan_type" = "xnone"; then
AC_CACHE_CHECK([whether NAN can be defined by 0/0],
- [have_nan_zero],
+ [c_cv_have_nan_zero],
AC_RUN_IFELSE(
AC_LANG_PROGRAM(
[[
else
return 1;
]]),
- [have_nan_zero="yes"],
- [have_nan_zero="no"]
+ [c_cv_have_nan_zero="yes"],
+ [c_cv_have_nan_zero="no"]
)
)
- if test "x$have_nan_zero" = "xyes"
+ if test "x$c_cv_have_nan_zero" = "xyes"
then
nan_type="zero"
fi
if test "x$fp_layout_type" = "xunknown"; then
AC_CACHE_CHECK([if doubles are stored in x86 representation],
- [fp_layout_need_nothing],
+ [c_cv_fp_layout_need_nothing],
AC_RUN_IFELSE(
AC_LANG_PROGRAM(
[[[[
else
return (1);
]]]]),
- [fp_layout_need_nothing="yes"],
- [fp_layout_need_nothing="no"]
+ [c_cv_fp_layout_need_nothing="yes"],
+ [c_cv_fp_layout_need_nothing="no"]
)
)
- if test "x$fp_layout_need_nothing" = "xyes"; then
+ if test "x$c_cv_fp_layout_need_nothing" = "xyes"; then
fp_layout_type="nothing"
fi
fi
if test "x$fp_layout_type" = "xunknown"; then
AC_CACHE_CHECK([if endianflip converts to x86 representation],
- [fp_layout_need_endianflip],
+ [c_cv_fp_layout_need_endianflip],
AC_RUN_IFELSE(
AC_LANG_PROGRAM(
[[[[
else
return (1);
]]]]),
- [fp_layout_need_endianflip="yes"],
- [fp_layout_need_endianflip="no"]
+ [c_cv_fp_layout_need_endianflip="yes"],
+ [c_cv_fp_layout_need_endianflip="no"]
)
)
- if test "x$fp_layout_need_endianflip" = "xyes"; then
+ if test "x$c_cv_fp_layout_need_endianflip" = "xyes"; then
fp_layout_type="endianflip"
fi
fi
if test "x$fp_layout_type" = "xunknown"; then
AC_CACHE_CHECK([if intswap converts to x86 representation],
- [fp_layout_need_intswap],
+ [c_cv_fp_layout_need_intswap],
AC_RUN_IFELSE(
AC_LANG_PROGRAM(
[[[[
else
return (1);
]]]]),
- [fp_layout_need_intswap="yes"],
- [fp_layout_need_intswap="no"]
+ [c_cv_fp_layout_need_intswap="yes"],
+ [c_cv_fp_layout_need_intswap="no"]
)
)
- if test "x$fp_layout_need_intswap" = "xyes"; then
+ if test "x$c_cv_fp_layout_need_intswap" = "xyes"; then
fp_layout_type="intswap"
fi
fi
if test "x$have_getmntent" = "xc"; then
AC_CACHE_CHECK([whether getmntent takes one argument],
- [have_one_getmntent],
+ [c_cv_have_one_getmntent],
AC_COMPILE_IFELSE(
AC_LANG_PROGRAM([[AC_INCLUDES_DEFAULT
#include <mntent.h>
me = getmntent (fh);
]]
),
- [have_one_getmntent="yes"],
- [have_one_getmntent="no"]
+ [c_cv_have_one_getmntent="yes"],
+ [c_cv_have_one_getmntent="no"]
)
)
AC_CACHE_CHECK([whether getmntent takes two arguments],
- [have_two_getmntent],
+ [c_cv_have_two_getmntent],
AC_COMPILE_IFELSE(
AC_LANG_PROGRAM([[AC_INCLUDES_DEFAULT
#include <sys/mnttab.h>
status = getmntent (fh, &mt);
]]
),
- [have_two_getmntent="yes"],
- [have_two_getmntent="no"]
+ [c_cv_have_two_getmntent="yes"],
+ [c_cv_have_two_getmntent="no"]
)
)
fi
# Check for different versions of `getmntent' here..
if test "x$have_getmntent" = "xc"; then
- if test "x$have_one_getmntent" = "xyes"; then
+ if test "x$c_cv_have_one_getmntent" = "xyes"; then
AC_DEFINE(HAVE_ONE_GETMNTENT, 1,
[Define if the function getmntent exists and takes one argument.])
fi
- if test "x$have_two_getmntent" = "xyes"; then
+ if test "x$c_cv_have_two_getmntent" = "xyes"; then
AC_DEFINE(HAVE_TWO_GETMNTENT, 1,
[Define if the function getmntent exists and takes two arguments.])
fi
LDFLAGS="$LDFLAGS $PERL_LDFLAGS"
AC_CACHE_CHECK([for libperl],
- [have_libperl],
+ [c_cv_have_libperl],
AC_LINK_IFELSE(
AC_LANG_PROGRAM(
[[
newSVpv ("Collectd::Plugin::FooBar", 24),
Nullsv);
]]),
- [have_libperl="yes"],
- [have_libperl="no"]
+ [c_cv_have_libperl="yes"],
+ [c_cv_have_libperl="no"]
)
)
- if test "x$have_libperl" = "xyes"
+ if test "x$c_cv_have_libperl" = "xyes"
then
AC_DEFINE(HAVE_LIBPERL, 1, [Define if libperl is present and usable.])
AC_SUBST(PERL_CFLAGS)
LDFLAGS=$SAVE_LDFLAGS
else if test -z "$perl_interpreter"; then
with_libperl="no (no perl interpreter found)"
- have_libperl="no"
+ c_cv_have_libperl="no"
fi; fi
AM_CONDITIONAL(BUILD_WITH_LIBPERL, test "x$with_libperl" = "xyes")
LDFLAGS="$LDFLAGS $PERL_LDFLAGS"
AC_CACHE_CHECK([if perl supports ithreads],
- [have_perl_ithreads],
+ [c_cv_have_perl_ithreads],
AC_LINK_IFELSE(
AC_LANG_PROGRAM(
[[
#endif /* !defined(USE_ITHREADS) */
]],
[[ ]]),
- [have_perl_ithreads="yes"],
- [have_perl_ithreads="no"]
+ [c_cv_have_perl_ithreads="yes"],
+ [c_cv_have_perl_ithreads="no"]
)
)
- if test "x$have_perl_ithreads" = "xyes"
+ if test "x$c_cv_have_perl_ithreads" = "xyes"
then
AC_DEFINE(HAVE_PERL_ITHREADS, 1, [Define if Perl supports ithreads.])
fi
LDFLAGS=$SAVE_LDFLAGS
fi
-have_broken_perl_load_module="no"
+c_cv_have_broken_perl_load_module="no"
if test "x$with_libperl" = "xyes"
then
SAVE_CFLAGS=$CFLAGS
LDFLAGS="$LDFLAGS $PERL_LDFLAGS"
AC_CACHE_CHECK([for broken Perl_load_module()],
- [have_broken_perl_load_module],
+ [c_cv_have_broken_perl_load_module],
AC_LINK_IFELSE(
AC_LANG_PROGRAM(
[[
newSVpv ("Collectd::Plugin::FooBar", 24),
Nullsv);
]]),
- [have_broken_perl_load_module="no"],
- [have_broken_perl_load_module="yes"]
+ [c_cv_have_broken_perl_load_module="no"],
+ [c_cv_have_broken_perl_load_module="yes"]
)
)
LDFLAGS=$SAVE_LDFLAGS
fi
AM_CONDITIONAL(HAVE_BROKEN_PERL_LOAD_MODULE,
- test "x$have_broken_perl_load_module" = "xyes")
+ test "x$c_cv_have_broken_perl_load_module" = "xyes")
if test "x$with_libperl" = "xyes"
then
plugin_ted="no"
plugin_thermal="no"
plugin_users="no"
+plugin_uptime="no"
plugin_vmem="no"
plugin_vserver="no"
plugin_wireless="no"
plugin_swap="yes"
plugin_tcpconns="yes"
plugin_thermal="yes"
+ plugin_uptime="yes"
plugin_vmem="yes"
plugin_vserver="yes"
plugin_wireless="yes"
fi
# Solaris
+if test "x$with_kstat" = "xyes"
+then
+ plugin_uptime="yes"
+fi
+
if test "x$with_devinfo$with_kstat" = "xyesyes"
then
plugin_cpu="yes"
then
plugin_cpu="yes"
plugin_swap="yes"
+ plugin_uptime="yes"
fi
if test "x$have_sysctlbyname" = "xyes"
then
plugin_load="yes"
fi
-if test "x$have_libperl$have_perl_ithreads" = "xyesyes"
+if test "x$c_cv_have_libperl$c_cv_have_perl_ithreads" = "xyesyes"
then
plugin_perl="yes"
fi
AC_PLUGIN([ted], [$plugin_ted], [Read The Energy Detective values])
AC_PLUGIN([thermal], [$plugin_thermal], [Linux ACPI thermal zone statistics])
AC_PLUGIN([unixsock], [yes], [Unixsock communication plugin])
+AC_PLUGIN([uptime], [$plugin_uptime], [Uptime statistics])
AC_PLUGIN([users], [$plugin_users], [User statistics])
AC_PLUGIN([uuid], [yes], [UUID as hostname plugin])
AC_PLUGIN([vmem], [$plugin_vmem], [Virtual memory statistics])
ted . . . . . . . . . $enable_ted
thermal . . . . . . . $enable_thermal
unixsock . . . . . . $enable_unixsock
+ uptime . . . . . . . $enable_uptime
users . . . . . . . . $enable_users
uuid . . . . . . . . $enable_uuid
vmem . . . . . . . . $enable_vmem
--- /dev/null
+#! /usr/bin/env python
+# -*- coding: utf-8 -*-
+# vim: fileencoding=utf-8
+#
+# Copyright © 2009 Adrian Perez <aperez@igalia.com>
+#
+# Distributed under terms of the GPLv2 license.
+
+"""
+Collectd network protocol implementation.
+"""
+
+import socket
+import struct
+
+try:
+ from cStringIO import StringIO
+except ImportError:
+ from StringIO import StringIO
+
+from datetime import datetime
+from copy import deepcopy
+
+
+DEFAULT_PORT = 25826
+"""Default port"""
+
+DEFAULT_IPv4_GROUP = "239.192.74.66"
+"""Default IPv4 multicast group"""
+
+DEFAULT_IPv6_GROUP = "ff18::efc0:4a42"
+"""Default IPv6 multicast group"""
+
+
+
+# Message kinds
+TYPE_HOST = 0x0000
+TYPE_TIME = 0x0001
+TYPE_PLUGIN = 0x0002
+TYPE_PLUGIN_INSTANCE = 0x0003
+TYPE_TYPE = 0x0004
+TYPE_TYPE_INSTANCE = 0x0005
+TYPE_VALUES = 0x0006
+TYPE_INTERVAL = 0x0007
+
+# For notifications
+TYPE_MESSAGE = 0x0100
+TYPE_SEVERITY = 0x0101
+
+# DS kinds
+DS_TYPE_COUNTER = 0
+DS_TYPE_GAUGE = 1
+
+
+header = struct.Struct("!2H")
+number = struct.Struct("!Q")
+short = struct.Struct("!H")
+double = struct.Struct("<d")
+
+
+def decode_network_values(ptype, plen, buf):
+ """Decodes a list of DS values in collectd network format
+ """
+ nvalues = short.unpack_from(buf, header.size)[0]
+ off = header.size + short.size + nvalues
+ valskip = double.size
+
+ # Check whether our expected packet size is the reported one
+ assert ((valskip + 1) * nvalues + short.size + header.size) == plen
+ assert double.size == number.size
+
+ result = []
+ for dstype in map(ord, buf[header.size+short.size:off]):
+ if dstype == DS_TYPE_COUNTER:
+ result.append((dstype, number.unpack_from(buf, off)[0]))
+ off += valskip
+ elif dstype == DS_TYPE_GAUGE:
+ result.append((dstype, double.unpack_from(buf, off)[0]))
+ off += valskip
+ else:
+ raise ValueError("DS type %i unsupported" % dstype)
+
+ return result
+
+
+def decode_network_number(ptype, plen, buf):
+ """Decodes a number (64-bit unsigned) in collectd network format.
+ """
+ return number.unpack_from(buf, header.size)[0]
+
+
+def decode_network_string(msgtype, plen, buf):
+ """Decodes a floating point number (64-bit) in collectd network format.
+ """
+ return buf[header.size:plen-1]
+
+
+# Mapping of message types to decoding functions.
+_decoders = {
+ TYPE_VALUES : decode_network_values,
+ TYPE_TIME : decode_network_number,
+ TYPE_INTERVAL : decode_network_number,
+ TYPE_HOST : decode_network_string,
+ TYPE_PLUGIN : decode_network_string,
+ TYPE_PLUGIN_INSTANCE: decode_network_string,
+ TYPE_TYPE : decode_network_string,
+ TYPE_TYPE_INSTANCE : decode_network_string,
+ TYPE_MESSAGE : decode_network_string,
+ TYPE_SEVERITY : decode_network_number,
+}
+
+
+def decode_network_packet(buf):
+ """Decodes a network packet in collectd format.
+ """
+ off = 0
+ blen = len(buf)
+ while off < blen:
+ ptype, plen = header.unpack_from(buf, off)
+
+ if plen > blen - off:
+ raise ValueError("Packet longer than amount of data in buffer")
+
+ if ptype not in _decoders:
+ raise ValueError("Message type %i not recognized" % ptype)
+
+ yield ptype, _decoders[ptype](ptype, plen, buf[off:])
+ off += plen
+
+
+
+
+
+class Data(object):
+ time = 0
+ host = None
+ plugin = None
+ plugininstance = None
+ type = None
+ typeinstance = None
+
+ def __init__(self, **kw):
+ [setattr(self, k, v) for k, v in kw.iteritems()]
+
+ @property
+ def datetime(self):
+ return datetime.fromtimestamp(self.time)
+
+ @property
+ def source(self):
+ buf = StringIO()
+ if self.host:
+ buf.write(self.host)
+ if self.plugin:
+ buf.write("/")
+ buf.write(self.plugin)
+ if self.plugininstance:
+ buf.write("/")
+ buf.write(self.plugininstance)
+ if self.type:
+ buf.write("/")
+ buf.write(self.type)
+ if self.typeinstance:
+ buf.write("/")
+ buf.write(self.typeinstance)
+ return buf.getvalue()
+
+ def __str__(self):
+ return "[%i] %s" % (self.time, self.source)
+
+
+
+class Notification(Data):
+ FAILURE = 1
+ WARNING = 2
+ OKAY = 4
+
+ SEVERITY = {
+ FAILURE: "FAILURE",
+ WARNING: "WARNING",
+ OKAY : "OKAY",
+ }
+
+ __severity = 0
+ message = ""
+
+ def __set_severity(self, value):
+ if value in (self.FAILURE, self.WARNING, self.OKAY):
+ self.__severity = value
+
+ severity = property(lambda self: self.__severity, __set_severity)
+
+ @property
+ def severitystring(self):
+ return self.SEVERITY.get(self.severity, "UNKNOWN")
+
+ def __str__(self):
+ return "%s [%s] %s" % (
+ super(Notification, self).__str__(),
+ self.severitystring,
+ self.message)
+
+
+
+class Values(Data, list):
+ def __str__(self):
+ return "%s %s" % (Data.__str__(self), list.__str__(self))
+
+
+
+def interpret_opcodes(iterable):
+ vl = Values()
+ nt = Notification()
+
+ for kind, data in iterable:
+ if kind == TYPE_TIME:
+ vl.time = nt.time = data
+ elif kind == TYPE_INTERVAL:
+ vl.interval = data
+ elif kind == TYPE_HOST:
+ vl.host = nt.host = data
+ elif kind == TYPE_PLUGIN:
+ vl.plugin = nt.plugin = data
+ elif kind == TYPE_PLUGIN_INSTANCE:
+ vl.plugininstance = nt.plugininstance = data
+ elif kind == TYPE_TYPE:
+ vl.type = nt.type = data
+ elif kind == TYPE_TYPE_INSTANCE:
+ vl.typeinstance = nt.typeinstance = data
+ elif kind == TYPE_SEVERITY:
+ nt.severity = data
+ elif kind == TYPE_MESSAGE:
+ nt.message = data
+ yield deepcopy(nt)
+ elif kind == TYPE_VALUES:
+ vl[:] = data
+ yield deepcopy(vl)
+
+
+
+class Reader(object):
+ """Network reader for collectd data.
+
+ Listens on the network in a given address, which can be a multicast
+ group address, and handles reading data when it arrives.
+ """
+ addr = None
+ host = None
+ port = DEFAULT_PORT
+
+ BUFFER_SIZE = 1024
+
+
+ def __init__(self, host=None, port=DEFAULT_PORT, multicast=False):
+ if host is None:
+ multicast = True
+ host = DEFAULT_IPv4_GROUP
+
+ self.host, self.port = host, port
+ self.ipv6 = ":" in self.host
+
+ family, socktype, proto, canonname, sockaddr = socket.getaddrinfo(
+ None if multicast else self.host, self.port,
+ socket.AF_INET6 if self.ipv6 else socket.AF_UNSPEC,
+ socket.SOCK_DGRAM, 0, socket.AI_PASSIVE)[0]
+
+ self._sock = socket.socket(family, socktype, proto)
+ self._sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
+ self._sock.bind(sockaddr)
+
+ if multicast:
+ if hasattr(socket, "SO_REUSEPORT"):
+ self._sock.setsockopt(
+ socket.SOL_SOCKET,
+ socket.SO_REUSEPORT, 1)
+
+ val = None
+ if family == socket.AF_INET:
+ assert "." in self.host
+ val = struct.pack("4sl",
+ socket.inet_aton(self.host), socket.INADDR_ANY)
+ elif family == socket.AF_INET6:
+ raise NotImplementedError("IPv6 support not ready yet")
+ else:
+ raise ValueError("Unsupported network address family")
+
+ self._sock.setsockopt(
+ socket.IPPROTO_IPV6 if self.ipv6 else socket.IPPROTO_IP,
+ socket.IP_ADD_MEMBERSHIP, val)
+ self._sock.setsockopt(
+ socket.IPPROTO_IPV6 if self.ipv6 else socket.IPPROTO_IP,
+ socket.IP_MULTICAST_LOOP, 0)
+
+
+ def receive(self):
+ """Receives a single raw collect network packet.
+ """
+ return self._sock.recv(self.BUFFER_SIZE)
+
+
+ def decode(self, buf=None):
+ """Decodes a given buffer or the next received packet.
+ """
+ if buf is None:
+ buf = self.receive()
+ return decode_network_packet(buf)
+
+
+ def interpret(self, iterable=None):
+ """Interprets a sequence
+ """
+ if iterable is None:
+ iterable = self.decode()
+ if isinstance(iterable, basestring):
+ iterable = self.decode(iterable)
+ return interpret_opcodes(iterable)
+
+
--- /dev/null
+#-*- coding: ISO-8859-1 -*-
+# collect.py: the python collectd-unixsock module.
+#
+# Requires collectd to be configured with the unixsock plugin, like so:
+#
+# LoadPlugin unixsock
+# <Plugin unixsock>
+# SocketFile "/var/run/collectd-unixsock"
+# SocketPerms "0775"
+# </Plugin>
+#
+# Copyright (C) 2008 Clay Loveless <clay@killersoft.com>
+#
+# This software is provided 'as-is', without any express or implied
+# warranty. In no event will the author be held liable for any damages
+# arising from the use of this software.
+#
+# Permission is granted to anyone to use this software for any purpose,
+# including commercial applications, and to alter it and redistribute it
+# freely, subject to the following restrictions:
+#
+# 1. The origin of this software must not be misrepresented; you must not
+# claim that you wrote the original software. If you use this software
+# in a product, an acknowledgment in the product documentation would be
+# appreciated but is not required.
+# 2. Altered source versions must be plainly marked as such, and must not be
+# misrepresented as being the original software.
+# 3. This notice may not be removed or altered from any source distribution.
+
+import socket, string
+
+class Collect(object):
+
+ def __init__(self, path='/var/run/collectd-unixsock'):
+ self._sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
+ self._path = path
+ self._sock.connect(self._path)
+
+ def list(self):
+ numvalues = self._cmd('LISTVAL')
+ lines = []
+ if numvalues:
+ lines = self._readlines(numvalues)
+ return lines
+
+ def get(self, val, flush=True):
+ numvalues = self._cmd('GETVAL "' + val + '"')
+ lines = []
+ if numvalues:
+ lines = self._readlines(numvalues)
+ if flush:
+ self._cmd('FLUSH identifier="' + val + '"')
+ return lines
+
+ def _cmd(self, c):
+ self._sock.send(c + "\n")
+ stat = string.split(self._readline())
+ status = int(stat[0])
+ if status:
+ return status
+ return False
+
+ '''
+ _readline and _readlines methods borrowed from the _fileobject class
+ in sockets.py, tweaked a little bit for use in the collectd context.
+ '''
+ def _readline(self):
+ data = ''
+ buf = []
+ recv = self._sock.recv
+ while data != "\n":
+ data = recv(1)
+ if not data:
+ break
+ if data != "\n":
+ buf.append(data)
+ return ''.join(buf)
+
+ def _readlines(self, sizehint=0):
+ total = 0
+ list = []
+ while True:
+ line = self._readline()
+ if not line:
+ break
+ list.append(line)
+ total = len(list)
+ if sizehint and total >= sizehint:
+ break
+ return list
+
+ def __del__(self):
+ self._sock.close()
+
+
+
+if __name__ == '__main__':
+
+ '''
+ Example usage:
+ Collect values from socket and dump to STDOUT.
+ '''
+
+ c = Collect('/var/run/collectd-unixsock')
+ list = c.list()
+
+ for val in list:
+ stamp, key = string.split(val)
+ glines = c.get(key)
+ print stamp + ' ' + key + ' ' + ', '.join(glines)
+
utils_avltree.c utils_avltree.h \
utils_cache.c utils_cache.h \
utils_complain.c utils_complain.h \
+ utils_heap.c utils_heap.h \
utils_ignorelist.c utils_ignorelist.h \
utils_llist.c utils_llist.h \
utils_parse_option.c utils_parse_option.h \
collectdmon_CPPFLAGS = $(AM_CPPFLAGS)
collectd_nagios_SOURCES = collectd-nagios.c
-collectd_nagios_LDFLAGS =
+collectd_nagios_LDADD =
if BUILD_WITH_LIBSOCKET
-collectd_nagios_LDFLAGS += -lsocket
+collectd_nagios_LDADD += -lsocket
endif
-collectd_nagios_LDADD = libcollectdclient/libcollectdclient.la
+collectd_nagios_LDADD += libcollectdclient/libcollectdclient.la
collectd_nagios_DEPENDENCIES = libcollectdclient/libcollectdclient.la
pkglib_LTLIBRARIES =
pkglib_LTLIBRARIES += apcups.la
apcups_la_SOURCES = apcups.c
apcups_la_LDFLAGS = -module -avoid-version
+apcups_la_LIBADD =
if BUILD_WITH_LIBSOCKET
-apcups_la_LDFLAGS += -lsocket
+apcups_la_LIBADD += -lsocket
endif
collectd_LDADD += "-dlopen" apcups.la
collectd_DEPENDENCIES += apcups.la
if BUILD_PLUGIN_APPLE_SENSORS
pkglib_LTLIBRARIES += apple_sensors.la
apple_sensors_la_SOURCES = apple_sensors.c
-apple_sensors_la_LDFLAGS = -module -avoid-version -lIOKit
+apple_sensors_la_LDFLAGS = -module -avoid-version
+apple_sensors_la_LIBADD = -lIOKit
collectd_LDADD += "-dlopen" apple_sensors.la
collectd_DEPENDENCIES += apple_sensors.la
endif
pkglib_LTLIBRARIES += battery.la
battery_la_SOURCES = battery.c
battery_la_LDFLAGS = -module -avoid-version
+battery_la_LIBADD =
if BUILD_WITH_LIBIOKIT
-battery_la_LDFLAGS += -lIOKit
+battery_la_LIBADD += -lIOKit
endif
collectd_LDADD += "-dlopen" battery.la
collectd_DEPENDENCIES += battery.la
cpu_la_LDFLAGS = -module -avoid-version
cpu_la_LIBADD =
if BUILD_WITH_LIBKSTAT
-cpu_la_LDFLAGS += -lkstat
+cpu_la_LIBADD += -lkstat
endif
if BUILD_WITH_LIBDEVINFO
-cpu_la_LDFLAGS += -ldevinfo
+cpu_la_LIBADD += -ldevinfo
endif
if BUILD_WITH_LIBSTATGRAB
cpu_la_CFLAGS += $(BUILD_WITH_LIBSTATGRAB_CFLAGS)
disk_la_LDFLAGS = -module -avoid-version
disk_la_LIBADD =
if BUILD_WITH_LIBKSTAT
-disk_la_LDFLAGS += -lkstat
+disk_la_LIBADD += -lkstat
endif
if BUILD_WITH_LIBDEVINFO
-disk_la_LDFLAGS += -ldevinfo
+disk_la_LIBADD += -ldevinfo
endif
if BUILD_WITH_LIBIOKIT
-disk_la_LDFLAGS += -lIOKit
+disk_la_LIBADD += -lIOKit
endif
if BUILD_WITH_LIBSTATGRAB
disk_la_CFLAGS += $(BUILD_WITH_LIBSTATGRAB_CFLAGS)
if BUILD_PLUGIN_DNS
pkglib_LTLIBRARIES += dns.la
dns_la_SOURCES = dns.c utils_dns.c utils_dns.h
-dns_la_LDFLAGS = -module -avoid-version -lpcap -lpthread
+dns_la_LDFLAGS = -module -avoid-version
+dns_la_LIBADD = -lpcap -lpthread
collectd_LDADD += "-dlopen" dns.la
collectd_DEPENDENCIES += dns.la
endif
pkglib_LTLIBRARIES += email.la
email_la_SOURCES = email.c
email_la_LDFLAGS = -module -avoid-version
-if BUILD_WITH_LIBPTHREAD
-email_la_LDFLAGS += -lpthread
-endif
+email_la_LIBADD = -lpthread
collectd_LDADD += "-dlopen" email.la
collectd_DEPENDENCIES += email.la
endif
utils_cmd_putnotif.c utils_cmd_putnotif.h \
utils_cmd_putval.c utils_cmd_putval.h
exec_la_LDFLAGS = -module -avoid-version
-if BUILD_WITH_LIBPTHREAD
-exec_la_LDFLAGS += -lpthread
-endif
+exec_la_LIBADD = -lpthread
collectd_LDADD += "-dlopen" exec.la
collectd_DEPENDENCIES += exec.la
endif
pkglib_LTLIBRARIES += hddtemp.la
hddtemp_la_SOURCES = hddtemp.c
hddtemp_la_LDFLAGS = -module -avoid-version
+hddtemp_la_LIBADD =
if BUILD_WITH_LIBSOCKET
-hddtemp_la_LDFLAGS += -lsocket
+hddtemp_la_LIBADD += -lsocket
endif
collectd_LDADD += "-dlopen" hddtemp.la
collectd_DEPENDENCIES += hddtemp.la
interface_la_LIBADD += $(BUILD_WITH_LIBSTATGRAB_LDFLAGS)
else
if BUILD_WITH_LIBKSTAT
-interface_la_LDFLAGS += -lkstat
+interface_la_LIBADD += -lkstat
endif
if BUILD_WITH_LIBDEVINFO
-interface_la_LDFLAGS += -ldevinfo
+interface_la_LIBADD += -ldevinfo
endif # BUILD_WITH_LIBDEVINFO
endif # !BUILD_WITH_LIBSTATGRAB
endif # BUILD_PLUGIN_INTERFACE
iptables_la_LIBADD = libiptc/libiptc.la
iptables_la_DEPENDENCIES = libiptc/libiptc.la
else
-iptables_la_LDFLAGS += -liptc
+iptables_la_LIBADD = -liptc
endif
collectd_LDADD += "-dlopen" iptables.la
collectd_DEPENDENCIES += iptables.la
pkglib_LTLIBRARIES += mbmon.la
mbmon_la_SOURCES = mbmon.c
mbmon_la_LDFLAGS = -module -avoid-version
+mbmon_la_LIBADD =
if BUILD_WITH_LIBSOCKET
-mbmon_la_LDFLAGS += -lsocket
+mbmon_la_LIBADD += -lsocket
endif
collectd_LDADD += "-dlopen" mbmon.la
collectd_DEPENDENCIES += mbmon.la
memcached_la_LDFLAGS = -module -avoid-version
memcached_la_LIBADD =
if BUILD_WITH_LIBSOCKET
-memcached_la_LDFLAGS += -lsocket
+memcached_la_LIBADD += -lsocket
endif
collectd_LDADD += "-dlopen" memcached.la
collectd_DEPENDENCIES += memcached.la
pkglib_LTLIBRARIES += network.la
network_la_SOURCES = network.c network.h
network_la_LDFLAGS = -module -avoid-version
+network_la_LIBADD = -lpthread
if BUILD_WITH_LIBSOCKET
-network_la_LDFLAGS += -lsocket
-endif
-if BUILD_WITH_LIBPTHREAD
-network_la_LDFLAGS += -lpthread
+network_la_LIBADD += -lsocket
endif
collectd_LDADD += "-dlopen" network.la
collectd_DEPENDENCIES += network.la
pkglib_LTLIBRARIES += notify_desktop.la
notify_desktop_la_SOURCES = notify_desktop.c
notify_desktop_la_CFLAGS = $(AM_CFLAGS) $(LIBNOTIFY_CFLAGS)
-notify_desktop_la_LDFLAGS = -module -avoid-version $(LIBNOTIFY_LIBS)
+notify_desktop_la_LDFLAGS = -module -avoid-version
+notify_desktop_la_LIBADD = $(LIBNOTIFY_LIBS)
collectd_LDADD += "-dlopen" notify_desktop.la
collectd_DEPENDENCIES += notify_desktop.la
endif
if BUILD_PLUGIN_NOTIFY_EMAIL
pkglib_LTLIBRARIES += notify_email.la
notify_email_la_SOURCES = notify_email.c
-notify_email_la_LDFLAGS = -L/usr/local/lib -lesmtp -lssl -lcrypto -pthread -module -avoid-version
+notify_email_la_LDFLAGS = -module -avoid-version
+notify_email_la_LIBADD = -lesmtp -lssl -lcrypto -lpthread -ldl
collectd_LDADD += "-dlopen" notify_email.la
collectd_DEPENDENCIES += notify_email.la
endif
pkglib_LTLIBRARIES += ntpd.la
ntpd_la_SOURCES = ntpd.c
ntpd_la_LDFLAGS = -module -avoid-version
+ntpd_la_LIBADD =
if BUILD_WITH_LIBSOCKET
-ntpd_la_LDFLAGS += -lsocket
+ntpd_la_LIBADD += -lsocket
endif
collectd_LDADD += "-dlopen" ntpd.la
collectd_DEPENDENCIES += ntpd.la
pkglib_LTLIBRARIES += nut.la
nut_la_SOURCES = nut.c
nut_la_CFLAGS = $(AM_CFLAGS) $(BUILD_WITH_LIBUPSCLIENT_CFLAGS)
-nut_la_LDFLAGS = -module -avoid-version -lpthread $(BUILD_WITH_LIBUPSCLIENT_LIBS)
+nut_la_LDFLAGS = -module -avoid-version
+nut_la_LIBADD = -lpthread $(BUILD_WITH_LIBUPSCLIENT_LIBS)
collectd_LDADD += "-dlopen" nut.la
collectd_DEPENDENCIES += nut.la
endif
ping_la_SOURCES = ping.c
ping_la_LDFLAGS = -module -avoid-version
if BUILD_WITH_OWN_LIBOPING
-ping_la_LIBADD = liboping/liboping.la
+ping_la_LIBADD = liboping/liboping.la
ping_la_DEPENDENCIES = liboping/liboping.la
else
-ping_la_LDFLAGS += -loping
+ping_la_LIBADD = -loping
endif
collectd_LDADD += "-dlopen" ping.la
collectd_DEPENDENCIES += ping.la
utils_db_query.c utils_db_query.h
postgresql_la_CPPFLAGS = $(AM_CPPFLAGS) $(BUILD_WITH_LIBPQ_CPPFLAGS)
postgresql_la_LDFLAGS = -module -avoid-version \
- $(BUILD_WITH_LIBPQ_LDFLAGS) -lpq
+ $(BUILD_WITH_LIBPQ_LDFLAGS)
+postgresql_la_LIBADD = -lpq
collectd_LDADD += "-dlopen" postgresql.la
collectd_DEPENDENCIES += postgresql.la
endif
pkglib_LTLIBRARIES += sensors.la
sensors_la_SOURCES = sensors.c
sensors_la_CFLAGS = $(AM_CFLAGS) $(BUILD_WITH_LIBSENSORS_CFLAGS)
-sensors_la_LDFLAGS = -module -avoid-version $(BUILD_WITH_LIBSENSORS_LDFLAGS)
-sensors_la_LIBADD = -lsensors
+sensors_la_LDFLAGS = -module -avoid-version
+sensors_la_LIBADD = $(BUILD_WITH_LIBSENSORS_LDFLAGS)
collectd_LDADD += "-dlopen" sensors.la
collectd_DEPENDENCIES += sensors.la
endif
if BUILD_PLUGIN_TAPE
pkglib_LTLIBRARIES += tape.la
tape_la_SOURCES = tape.c
-tape_la_LDFLAGS = -module -avoid-version -lkstat -ldevinfo
+tape_la_LDFLAGS = -module -avoid-version
+tape_la_LIBADD = -lkstat -ldevinfo
collectd_LDADD += "-dlopen" tape.la
collectd_DEPENDENCIES += tape.la
endif
utils_cmd_listval.h utils_cmd_listval.c \
utils_cmd_putval.h utils_cmd_putval.c \
utils_cmd_putnotif.h utils_cmd_putnotif.c
-unixsock_la_LDFLAGS = -module -avoid-version -lpthread
+unixsock_la_LDFLAGS = -module -avoid-version
+unixsock_la_LIBADD = -lpthread
collectd_LDADD += "-dlopen" unixsock.la
collectd_DEPENDENCIES += unixsock.la
endif
+if BUILD_PLUGIN_UPTIME
+pkglib_LTLIBRARIES += uptime.la
+uptime_la_SOURCES = uptime.c
+uptime_la_CFLAGS = $(AM_CFLAGS)
+uptime_la_LDFLAGS = -module -avoid-version
+uptime_la_LIBADD =
+if BUILD_WITH_LIBKSTAT
+uptime_la_LIBADD += -lkstat
+endif
+collectd_LDADD += "-dlopen" uptime.la
+collectd_DEPENDENCIES += uptime.la
+endif
+
if BUILD_PLUGIN_USERS
pkglib_LTLIBRARIES += users.la
users_la_SOURCES = users.c
pkglib_LTLIBRARIES += xmms.la
xmms_la_SOURCES = xmms.c
xmms_la_CFLAGS = $(AM_CFLAGS) $(BUILD_WITH_LIBXMMS_CFLAGS)
-xmms_la_LDFLAGS = -module -avoid-version $(BUILD_WITH_LIBXMMS_LIBS)
+xmms_la_LDFLAGS = -module -avoid-version
+xmms_la_LIBADD = $(BUILD_WITH_LIBXMMS_LIBS)
collectd_LDADD += "-dlopen" xmms.la
collectd_DEPENDENCIES += xmms.la
endif
=item B<Interval> I<Seconds>
-Collect data from this host every I<Seconds> seconds. This value needs to be a
-multiple of the global B<Interval> setting and, if it is not, will be rounded
-B<down> to one and a warning is logged in this case. So if your global
-B<Interval> is set to I<10> and you configure I<25> here, it's rounded down to
-I<20>. By default the global B<Interval> setting will be used.
-
-This option is meant for devices with not much CPU power, e.E<nbsp>g. network
-equipment such as switches, embedded devices, rack monitoring systems and so
-on. Since the B<Step> of generated RRD files depends on this setting it's
-wise to select a reasonable value once and never change it.
+Collect data from this host every I<Seconds> seconds. This option is meant for
+devices with not much CPU power, e.E<nbsp>g. network equipment such as
+switches, embedded devices, rack monitoring systems and so on. Since the
+B<Step> of generated RRD files depends on this setting it's wise to select a
+reasonable value once and never change it.
=back
#@BUILD_PLUGIN_TEAMSPEAK2_TRUE@LoadPlugin teamspeak2
#@BUILD_PLUGIN_THERMAL_TRUE@LoadPlugin thermal
#@BUILD_PLUGIN_UNIXSOCK_TRUE@LoadPlugin unixsock
+#@BUILD_PLUGIN_UPTIME_TRUE@LoadPlugin uptime
#@BUILD_PLUGIN_USERS_TRUE@LoadPlugin users
#@BUILD_PLUGIN_UUID_TRUE@LoadPlugin uuid
#@BUILD_PLUGIN_VMEM_TRUE@LoadPlugin vmem
#</Plugin>
#<Plugin mysql>
-# Host "database.serv.er"
-# User "db_user"
-# Password "secret"
-# Database "db_name"
+# <Database db_name>
+# Host "database.serv.er"
+# User "db_user"
+# Password "secret"
+# Database "db_name"
+# MasterStats true
+# </Database>
+#
+# <Database db_name2>
+# Host "localhost"
+# Socket "/var/run/mysql/mysqld.sock"
+# SlaveStats true
+# SlaveNotifications true
+# </Database>
#</Plugin>
#<Plugin netlink>
# Load required matches:
#@BUILD_PLUGIN_MATCH_REGEX_TRUE@LoadPlugin match_regex
#@BUILD_PLUGIN_MATCH_VALUE_TRUE@LoadPlugin match_value
+#@BUILD_PLUGIN_MATCH_TIMEDIFF_TRUE@LoadPlugin match_timediff
# Load required targets:
#@BUILD_PLUGIN_TARGET_NOTIFICATION_TRUE@LoadPlugin target_notification
=head2 Plugin C<mysql>
-The C<mysql plugin> requires B<mysqlclient> to be installed. It connects to the
-database when started and keeps the connection up as long as possible. When the
-connection is interrupted for whatever reason it will try to re-connect. The
-plugin will complaint loudly in case anything goes wrong.
+The C<mysql plugin> requires B<mysqlclient> to be installed. It connects to
+one or more databases when started and keeps the connection up as long as
+possible. When the connection is interrupted for whatever reason it will try
+to re-connect. The plugin will complaint loudly in case anything goes wrong.
+
+This plugin issues the MySQL C<SHOW STATUS> / C<SHOW GLOBAL STATUS> command
+and collects information about MySQL network traffic, executed statements,
+requests, the query cache and threads by evaluating the
+C<Bytes_{received,sent}>, C<Com_*>, C<Handler_*>, C<Qcache_*> and C<Threads_*>
+return values. Please refer to the B<MySQL reference manual>, I<5.1.6. Server
+Status Variables> for an explanation of these values.
+
+Optionally, master and slave statistics may be collected in a MySQL
+replication setup. In that case, information about the synchronization state
+of the nodes are collected by evaluating the C<Position> return value of the
+C<SHOW MASTER STATUS> command and the C<Seconds_Behind_Master>,
+C<Read_Master_Log_Pos> and C<Exec_Master_Log_Pos> return values of the
+C<SHOW SLAVE STATUS> command. See the B<MySQL reference manual>,
+I<12.5.5.21 SHOW MASTER STATUS Syntax> and
+I<12.5.5.31 SHOW SLAVE STATUS Syntax> for details.
-This plugin issues the MySQL C<SHOW STATUS> command and collects information
-about MySQL network traffic, executed statements, requests, the query cache
-and threads by evaluating the C<Bytes_{received,sent}>, C<Com_*>,
-C<Handler_*>, C<Qcache_*> and C<Threads_*> return values. Please refer to the
-B<MySQL reference manual>, I<5.1.6. Server Status Variables> for an
-explanation of these values.
+Synopsis:
+
+ <Plugin mysql>
+ <Database foo>
+ Host "hostname"
+ User "username"
+ Password "password"
+ Port "3306"
+ MasterStats true
+ </Database>
+
+ <Database bar>
+ Host "localhost"
+ Socket "/var/run/mysql/mysqld.sock"
+ SlaveStats true
+ SlaveNotifications true
+ </Database>
+ </Plugin>
-Use the following options to configure the plugin:
+A B<Database> block defines one connection to a MySQL database. It accepts a
+single argument which specifies the name of the database. None of the other
+options are required. MySQL will use default values as documented in the
+section "mysql_real_connect()" in the B<MySQL reference manual>.
=over 4
Otherwise, use the B<Port> option above. See the documentation for the
C<mysql_real_connect> function for details.
+=item B<MasterStats> I<true|false>
+
+=item B<SlaveStats> I<true|false>
+
+Enable the collection of master / slave statistics in a replication setup.
+
+=item B<SlaveNotifications> I<true|false>
+
+If enabled, the plugin sends a notification if the replication slave I/O and /
+or SQL threads are not running.
+
=back
=head2 Plugin C<netlink>
B<Sensor> is inverted: All selected interfaces are ignored and all other
interfaces are collected.
+=item B<Interval> I<Seconds>
+
+Sets the interval in which all sensors should be read. If not specified, the
+global B<Interval> setting is used.
+
=back
B<EXPERIMENTAL!> The C<onewire> plugin is experimental, because it doesn't yet
(tv).tv_usec = (tv).tv_usec % 1000000; \
} while (0)
+/* make sure tv_sec stores less than a second */
+#define NORMALIZE_TIMESPEC(tv) \
+ do { \
+ (tv).tv_sec += (tv).tv_nsec / 1000000000; \
+ (tv).tv_nsec = (tv).tv_nsec % 1000000000; \
+ } while (0)
+
int check_create_dir (const char *file_orig);
#ifdef HAVE_LIBKSTAT
free (key);
free (value);
- DEBUG ("return (%i)", ret);
+ DEBUG ("cf_dispatch: return (%i)", ret);
return (ret);
} /* int cf_dispatch */
cf_default_typesdb = 0;
- if (ci->values_num < 1)
+ if (ci->values_num < 1) {
+ ERROR ("configfile: `TypesDB' needs at least one argument.");
return (-1);
+ }
for (i = 0; i < ci->values_num; ++i)
{
- if (OCONFIG_TYPE_STRING != ci->values[i].type)
+ if (OCONFIG_TYPE_STRING != ci->values[i].type) {
+ WARNING ("configfile: TypesDB: Skipping %i. argument which "
+ "is not a string.", i + 1);
continue;
+ }
read_types_list (ci->values[i].value.string);
}
break;
} /* for (ci->children) */
- /* Additional sanity checking. */
- while (status == 0)
- {
- if (chain->targets == NULL)
- {
- WARNING ("Filter subsystem: Chain %s: No default target has been "
- "specified. Please make sure that there is a <Target> block within "
- "the <Chain> block!", chain->name);
- status = -1;
- break;
- }
-
- break;
- } /* while (status == 0) */
-
if (status != 0)
{
fc_free_chains (chain);
return (NULL);
} /* }}} jobject ctoj_value_to_number */
-/* Convert a data_source_t to a org.collectd.api.DataSource */
+/* Convert a data_source_t to a org/collectd/api/DataSource */
static jobject ctoj_data_source (JNIEnv *jvm_env, /* {{{ */
const data_source_t *dsrc)
{
/* Look up the DataSource class */
c_datasource = (*jvm_env)->FindClass (jvm_env,
- "org.collectd.api.DataSource");
+ "org/collectd/api/DataSource");
if (c_datasource == NULL)
{
ERROR ("java plugin: ctoj_data_source: "
- "FindClass (org.collectd.api.DataSource) failed.");
+ "FindClass (org/collectd/api/DataSource) failed.");
return (NULL);
}
return (o_datasource);
} /* }}} jobject ctoj_data_source */
-/* Convert a oconfig_value_t to a org.collectd.api.OConfigValue */
+/* Convert a oconfig_value_t to a org/collectd/api/OConfigValue */
static jobject ctoj_oconfig_value (JNIEnv *jvm_env, /* {{{ */
oconfig_value_t ocvalue)
{
o_argument = NULL;
c_ocvalue = (*jvm_env)->FindClass (jvm_env,
- "org.collectd.api.OConfigValue");
+ "org/collectd/api/OConfigValue");
if (c_ocvalue == NULL)
{
ERROR ("java plugin: ctoj_oconfig_value: "
- "FindClass (org.collectd.api.OConfigValue) failed.");
+ "FindClass (org/collectd/api/OConfigValue) failed.");
return (NULL);
}
return (o_ocvalue);
} /* }}} jobject ctoj_oconfig_value */
-/* Convert a oconfig_item_t to a org.collectd.api.OConfigItem */
+/* Convert a oconfig_item_t to a org/collectd/api/OConfigItem */
static jobject ctoj_oconfig_item (JNIEnv *jvm_env, /* {{{ */
const oconfig_item_t *ci)
{
jobject o_ocitem;
int i;
- c_ocitem = (*jvm_env)->FindClass (jvm_env, "org.collectd.api.OConfigItem");
+ c_ocitem = (*jvm_env)->FindClass (jvm_env, "org/collectd/api/OConfigItem");
if (c_ocitem == NULL)
{
ERROR ("java plugin: ctoj_oconfig_item: "
- "FindClass (org.collectd.api.OConfigItem) failed.");
+ "FindClass (org/collectd/api/OConfigItem) failed.");
return (NULL);
}
return (NULL);
}
- (*jvm_env)->CallVoidMethod (jvm_env, o_ocitem, m_addvalue, o_child);
+ (*jvm_env)->CallVoidMethod (jvm_env, o_ocitem, m_addchild, o_child);
(*jvm_env)->DeleteLocalRef (jvm_env, o_child);
} /* }}} for (i = 0; i < ci->children_num; i++) */
return (o_ocitem);
} /* }}} jobject ctoj_oconfig_item */
-/* Convert a data_set_t to a org.collectd.api.DataSet */
+/* Convert a data_set_t to a org/collectd/api/DataSet */
static jobject ctoj_data_set (JNIEnv *jvm_env, const data_set_t *ds) /* {{{ */
{
jclass c_dataset;
jobject o_dataset;
int i;
- /* Look up the org.collectd.api.DataSet class */
- c_dataset = (*jvm_env)->FindClass (jvm_env, "org.collectd.api.DataSet");
+ /* Look up the org/collectd/api/DataSet class */
+ c_dataset = (*jvm_env)->FindClass (jvm_env, "org/collectd/api/DataSet");
if (c_dataset == NULL)
{
ERROR ("java plugin: ctoj_data_set: Looking up the "
- "org.collectd.api.DataSet class failed.");
+ "org/collectd/api/DataSet class failed.");
return (NULL);
}
/* Search for the `void addDataSource (DataSource)' method. */
m_add = (*jvm_env)->GetMethodID (jvm_env,
- c_dataset, "addDataSource", "(Lorg.collectd.api.DataSource;)V");
+ c_dataset, "addDataSource", "(Lorg/collectd/api/DataSource;)V");
if (m_add == NULL)
{
ERROR ("java plugin: ctoj_data_set: Looking up the "
/* Look for the `void setDataSource (List<DataSource> ds)' method. */
m_setdataset = (*jvm_env)->GetMethodID (jvm_env, c_valuelist,
- "setDataSet", "(Lorg.collectd.api.DataSet;)V");
+ "setDataSet", "(Lorg/collectd/api/DataSet;)V");
if (m_setdataset == NULL)
{
ERROR ("java plugin: ctoj_value_list_add_data_set: "
return (0);
} /* }}} int ctoj_value_list_add_data_set */
-/* Convert a value_list_t (and data_set_t) to a org.collectd.api.ValueList */
+/* Convert a value_list_t (and data_set_t) to a org/collectd/api/ValueList */
static jobject ctoj_value_list (JNIEnv *jvm_env, /* {{{ */
const data_set_t *ds, const value_list_t *vl)
{
/* First, create a new ValueList instance..
* Look up the class.. */
c_valuelist = (*jvm_env)->FindClass (jvm_env,
- "org.collectd.api.ValueList");
+ "org/collectd/api/ValueList");
if (c_valuelist == NULL)
{
ERROR ("java plugin: ctoj_value_list: "
- "FindClass (org.collectd.api.ValueList) failed.");
+ "FindClass (org/collectd/api/ValueList) failed.");
return (NULL);
}
return (o_valuelist);
} /* }}} jobject ctoj_value_list */
-/* Convert a notification_t to a org.collectd.api.Notification */
+/* Convert a notification_t to a org/collectd/api/Notification */
static jobject ctoj_notification (JNIEnv *jvm_env, /* {{{ */
const notification_t *n)
{
/* First, create a new Notification instance..
* Look up the class.. */
c_notification = (*jvm_env)->FindClass (jvm_env,
- "org.collectd.api.Notification");
+ "org/collectd/api/Notification");
if (c_notification == NULL)
{
ERROR ("java plugin: ctoj_notification: "
- "FindClass (org.collectd.api.Notification) failed.");
+ "FindClass (org/collectd/api/Notification) failed.");
return (NULL);
}
return (0);
} /* }}} int jtoc_values_array */
-/* Convert a org.collectd.api.ValueList to a value_list_t. */
+/* Convert a org/collectd/api/ValueList to a value_list_t. */
static int jtoc_value_list (JNIEnv *jvm_env, value_list_t *vl, /* {{{ */
jobject object_ptr)
{
return (0);
} /* }}} int jtoc_value_list */
-/* Convert a org.collectd.api.Notification to a notification_t. */
+/* Convert a org/collectd/api/Notification to a notification_t. */
static int jtoc_notification (JNIEnv *jvm_env, notification_t *n, /* {{{ */
jobject object_ptr)
{
ud.data = (void *) cbi;
ud.free_func = cjni_callback_info_destroy;
- plugin_register_complex_read (cbi->name, cjni_read, &ud);
+ plugin_register_complex_read (cbi->name, cjni_read,
+ /* interval = */ NULL, &ud);
(*jvm_env)->DeleteLocalRef (jvm_env, o_read);
(*jvm_env)->ReleaseStringUTFChars (jvm_env, o_name, c_name);
- cbi->class = (*jvm_env)->GetObjectClass (jvm_env, o_callback);
+ cbi->object = (*jvm_env)->NewGlobalRef (jvm_env, o_callback);
+ if (cbi->object == NULL)
+ {
+ ERROR ("java plugin: cjni_callback_info_create: NewGlobalRef failed.");
+ free (cbi);
+ return (NULL);
+ }
+
+ cbi->class = (*jvm_env)->GetObjectClass (jvm_env, cbi->object);
if (cbi->class == NULL)
{
ERROR ("java plugin: cjni_callback_info_create: GetObjectClass failed.");
return (NULL);
}
- cbi->object = o_callback;
-
cbi->method = (*jvm_env)->GetMethodID (jvm_env, cbi->class,
method_name, method_signature);
if (cbi->method == NULL)
return (NULL);
}
- (*jvm_env)->NewGlobalRef (jvm_env, o_callback);
-
return (cbi);
} /* }}} cjni_callback_info_t cjni_callback_info_create */
jclass api_class_ptr;
int status;
- api_class_ptr = (*jvm_env)->FindClass (jvm_env, "org.collectd.api.Collectd");
+ api_class_ptr = (*jvm_env)->FindClass (jvm_env, "org/collectd/api/Collectd");
if (api_class_ptr == NULL)
{
- ERROR ("cjni_init_native: Cannot find API class `org.collectd.api.Collectd'.");
+ ERROR ("cjni_init_native: Cannot find API class `org/collectd/api/Collectd'.");
return (-1);
}
i, jvm_argv[i]);
vm_args.options[i].optionString = jvm_argv[i];
}
- /*
- vm_args.options[0].optionString = "-verbose:jni";
- vm_args.options[1].optionString = "-Djava.class.path=/home/octo/collectd/bindings/java";
- */
status = JNI_CreateJavaVM (&jvm, (void **) &jvm_env, (void **) &vm_args);
if (status != 0)
JNIEnv *jvm_env;
java_plugin_class_t *class;
jmethodID constructor_id;
+ jobject tmp_object;
if ((ci->values_num != 1) || (ci->values[0].type != OCONFIG_TYPE_STRING))
{
return (-1);
}
- class->object = (*jvm_env)->NewObject (jvm_env, class->class,
+ tmp_object = (*jvm_env)->NewObject (jvm_env, class->class,
constructor_id);
+ if (tmp_object != NULL)
+ class->object = (*jvm_env)->NewGlobalRef (jvm_env, tmp_object);
+ else
+ class->object = NULL;
if (class->object == NULL)
{
ERROR ("java plugin: cjni_config_load_plugin: "
return (-1);
}
- (*jvm_env)->NewGlobalRef (jvm_env, class->object);
cjni_thread_detach ();
java_classes_list_len++;
int status;
size_t i;
+ jclass class;
+ jmethodID method;
+
if ((ci->values_num != 1) || (ci->values[0].type != OCONFIG_TYPE_STRING))
{
WARNING ("java plugin: `Plugin' blocks "
return (-1);
}
+ class = (*jvm_env)->GetObjectClass (jvm_env, cbi->object);
+ method = (*jvm_env)->GetMethodID (jvm_env, class,
+ "config", "(Lorg/collectd/api/OConfigItem;)I");
+
status = (*jvm_env)->CallIntMethod (jvm_env,
- cbi->object, cbi->method, o_ocitem);
+ cbi->object, method, o_ocitem);
(*jvm_env)->DeleteLocalRef (jvm_env, o_ocitem);
cjni_thread_detach ();
cjni_callback_info_t *cbi_factory;
const char *name;
jobject o_ci;
+ jobject o_tmp;
int type;
size_t i;
}
/* Then call the factory method so it creates a new object for us. */
- cbi_ret->object = (*jvm_env)->CallObjectMethod (jvm_env,
+ o_tmp = (*jvm_env)->CallObjectMethod (jvm_env,
cbi_factory->object, cbi_factory->method, o_ci);
- if (cbi_ret->object == NULL)
+ if (o_tmp == NULL)
{
ERROR ("java plugin: cjni_match_target_create: CallObjectMethod failed.");
BAIL_OUT (-1);
}
+ cbi_ret->object = (*jvm_env)->NewGlobalRef (jvm_env, o_tmp);
+ if (o_tmp == NULL)
+ {
+ ERROR ("java plugin: cjni_match_target_create: NewGlobalRef failed.");
+ BAIL_OUT (-1);
+ }
+
/* This is the class of the match. It is possibly different from the class of
* the match-factory! */
cbi_ret->class = (*jvm_env)->GetObjectClass (jvm_env, cbi_ret->object);
BAIL_OUT (-1);
}
- /* We have everything we hoped for. Now we add a new global reference so this
- * match isn't freed immediately after we return.. */
- (*jvm_env)->NewGlobalRef (jvm_env, cbi_ret->object);
-
/* Return the newly created match via the user_data pointer. */
*user_data = (void *) cbi_ret;
{
pinghost_t *ph;
- struct sockaddr_storage sockaddr;
- socklen_t sockaddr_len;
-
struct addrinfo ai_hints;
struct addrinfo *ai_list, *ai_ptr;
int ai_return;
{
ph->fd = -1;
- sockaddr_len = sizeof (sockaddr);
- memset (&sockaddr, '\0', sockaddr_len);
-
if (ai_ptr->ai_family == AF_INET)
{
- struct sockaddr_in *si;
-
- si = (struct sockaddr_in *) &sockaddr;
- si->sin_family = AF_INET;
- si->sin_port = htons (ph->ident);
- si->sin_addr.s_addr = htonl (INADDR_ANY);
-
ai_ptr->ai_socktype = SOCK_RAW;
ai_ptr->ai_protocol = IPPROTO_ICMP;
}
else if (ai_ptr->ai_family == AF_INET6)
{
- struct sockaddr_in6 *si;
-
- si = (struct sockaddr_in6 *) &sockaddr;
- si->sin6_family = AF_INET6;
- si->sin6_port = htons (ph->ident);
- si->sin6_addr = in6addr_any;
-
ai_ptr->ai_socktype = SOCK_RAW;
ai_ptr->ai_protocol = IPPROTO_ICMPV6;
}
mproc.create = mt_create;
mproc.destroy = mt_destroy;
mproc.match = mt_match;
- fc_register_match ("value", mproc);
+ fc_register_match ("timediff", mproc);
} /* module_register */
/* vim: set sw=2 sts=2 tw=78 et fdm=marker : */
/**
* collectd - src/mysql.c
- * Copyright (C) 2006,2007 Florian octo Forster
+ * Copyright (C) 2006-2009 Florian octo Forster
+ * Copyright (C) 2009 Doug MacEachern
+ * Copyright (C) 2009 Sebastian tokkee 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
*
* Authors:
* Florian octo Forster <octo at verplant.org>
+ * Mirko Buffoni <briareos at eswat.org>
+ * Doug MacEachern <dougm at hyperic.com>
+ * Sebastian tokkee Harl <sh at tokkee.org>
**/
#include "collectd.h"
/* TODO: Understand `Select_*' and possibly do that stuff as well.. */
-static const char *config_keys[] =
+struct mysql_database_s /* {{{ */
{
- "Host",
- "User",
- "Password",
- "Database",
- "Port",
- "Socket",
- NULL
+ /* instance == NULL => legacy mode */
+ char *instance;
+ char *host;
+ char *user;
+ char *pass;
+ char *database;
+ char *socket;
+ int port;
+
+ int master_stats;
+ int slave_stats;
+
+ int slave_notif;
+ int slave_io_running;
+ int slave_sql_running;
+
+ MYSQL *con;
+ int state;
};
-static int config_keys_num = 6;
+typedef struct mysql_database_s mysql_database_t; /* }}} */
-static char *host = "localhost";
-static char *user;
-static char *pass;
-static char *db = NULL;
-static char *socket = NULL;
-static int port = 0;
+static int mysql_read (user_data_t *ud);
-static MYSQL *getconnection (void)
+static void mysql_database_free (void *arg) /* {{{ */
{
- static MYSQL *con;
- static int state;
+ mysql_database_t *db;
- static int wait_for = 0;
- static int wait_increase = 60;
+ DEBUG ("mysql plugin: mysql_database_free (arg = %p);", arg);
- if (state != 0)
+ db = (mysql_database_t *) arg;
+
+ if (db == NULL)
+ return;
+
+ if (db->con != NULL)
+ mysql_close (db->con);
+
+ sfree (db->host);
+ sfree (db->user);
+ sfree (db->pass);
+ sfree (db->socket);
+ sfree (db->instance);
+ sfree (db->database);
+ sfree (db);
+} /* }}} void mysql_database_free */
+
+/* Configuration handling functions {{{
+ *
+ * <Plugin mysql>
+ * <Database "plugin_instance1">
+ * Host "localhost"
+ * Port 22000
+ * ...
+ * </Database>
+ * </Plugin>
+ */
+
+static int mysql_config_set_string (char **ret_string, /* {{{ */
+ oconfig_item_t *ci)
+{
+ char *string;
+
+ if ((ci->values_num != 1)
+ || (ci->values[0].type != OCONFIG_TYPE_STRING))
{
- int err;
- if ((err = mysql_ping (con)) != 0)
+ WARNING ("mysql plugin: The `%s' config option "
+ "needs exactly one string argument.", ci->key);
+ return (-1);
+ }
+
+ string = strdup (ci->values[0].value.string);
+ if (string == NULL)
+ {
+ ERROR ("mysql plugin: strdup failed.");
+ return (-1);
+ }
+
+ if (*ret_string != NULL)
+ free (*ret_string);
+ *ret_string = string;
+
+ return (0);
+} /* }}} int mysql_config_set_string */
+
+static int mysql_config_set_int (int *ret_int, /* {{{ */
+ oconfig_item_t *ci)
+{
+ if ((ci->values_num != 1)
+ || (ci->values[0].type != OCONFIG_TYPE_NUMBER))
+ {
+ WARNING ("mysql plugin: The `%s' config option "
+ "needs exactly one string argument.", ci->key);
+ return (-1);
+ }
+
+ *ret_int = ci->values[0].value.number;
+
+ return (0);
+} /* }}} int mysql_config_set_int */
+
+static int mysql_config_set_boolean (int *ret_boolean, /* {{{ */
+ oconfig_item_t *ci)
+{
+ int status = 0;
+
+ if (ci->values_num != 1)
+ status = -1;
+
+ if (status == 0)
+ {
+ if (ci->values[0].type == OCONFIG_TYPE_BOOLEAN)
+ *ret_boolean = ci->values[0].value.boolean;
+ else if (ci->values[0].type == OCONFIG_TYPE_STRING)
{
- WARNING ("mysql_ping failed: %s", mysql_error (con));
- state = 0;
+ if (IS_TRUE (ci->values[0].value.string))
+ *ret_boolean = 1;
+ else if (IS_FALSE (ci->values[0].value.string))
+ *ret_boolean = 0;
+ else
+ status = -1;
}
else
+ status = -1;
+ }
+
+ if (status != 0)
+ {
+ WARNING ("mysql plugin: The `%s' config option "
+ "needs exactly one boolean argument.", ci->key);
+ return (-1);
+ }
+ return (0);
+} /* }}} mysql_config_set_boolean */
+
+static int mysql_config (oconfig_item_t *ci) /* {{{ */
+{
+ mysql_database_t *db;
+ int plugin_block;
+ int status = 0;
+ int i;
+
+ if ((ci->values_num != 1)
+ || (ci->values[0].type != OCONFIG_TYPE_STRING))
+ {
+ WARNING ("mysql plugin: The `Database' block "
+ "needs exactly one string argument.");
+ return (-1);
+ }
+
+ db = (mysql_database_t *) malloc (sizeof (*db));
+ if (db == NULL)
+ {
+ ERROR ("mysql plugin: malloc failed.");
+ return (-1);
+ }
+ memset (db, 0, sizeof (*db));
+
+ /* initialize all the pointers */
+ db->host = NULL;
+ db->user = NULL;
+ db->pass = NULL;
+ db->database = NULL;
+ db->socket = NULL;
+ db->con = NULL;
+
+ /* trigger a notification, if it's not running */
+ db->slave_io_running = 1;
+ db->slave_sql_running = 1;
+
+ plugin_block = 1;
+ if (strcasecmp ("Plugin", ci->key) == 0)
+ {
+ db->instance = NULL;
+ }
+ else if (strcasecmp ("Database", ci->key) == 0)
+ {
+ plugin_block = 0;
+ status = mysql_config_set_string (&db->instance, ci);
+ if (status != 0)
{
- state = 1;
- return (con);
+ sfree (db);
+ return (status);
}
+ assert (db->instance != NULL);
+ }
+ else
+ {
+ ERROR ("mysql plugin: mysql_config: "
+ "Invalid key: %s", ci->key);
+ return (-1);
}
- if (wait_for > 0)
+ /* Fill the `mysql_database_t' structure.. */
+ for (i = 0; i < ci->children_num; i++)
{
- wait_for -= interval_g;
- return (NULL);
+ oconfig_item_t *child = ci->children + i;
+
+ if (strcasecmp ("Host", child->key) == 0)
+ status = mysql_config_set_string (&db->host, child);
+ else if (strcasecmp ("User", child->key) == 0)
+ status = mysql_config_set_string (&db->user, child);
+ else if (strcasecmp ("Password", child->key) == 0)
+ status = mysql_config_set_string (&db->pass, child);
+ else if (strcasecmp ("Port", child->key) == 0)
+ status = mysql_config_set_int (&db->port, child);
+ else if (strcasecmp ("Socket", child->key) == 0)
+ status = mysql_config_set_string (&db->socket, child);
+ /* Check if we're currently handling the `Plugin' block. If so,
+ * handle `Database' _blocks_, too. */
+ else if ((plugin_block != 0)
+ && (strcasecmp ("Database", child->key) == 0)
+ && (child->children != NULL))
+ {
+ /* If `plugin_block > 1', there has been at least one
+ * `Database' block */
+ plugin_block++;
+ status = mysql_config (child);
+ }
+ /* Now handle ordinary `Database' options (without children) */
+ else if ((strcasecmp ("Database", child->key) == 0)
+ && (child->children == NULL))
+ status = mysql_config_set_string (&db->database, child);
+ else if (strcasecmp ("MasterStats", child->key) == 0)
+ status = mysql_config_set_boolean (&db->master_stats, child);
+ else if (strcasecmp ("SlaveStats", child->key) == 0)
+ status = mysql_config_set_boolean (&db->slave_stats, child);
+ else if (strcasecmp ("SlaveNotifications", child->key) == 0)
+ status = mysql_config_set_boolean (&db->slave_notif, child);
+ else
+ {
+ WARNING ("mysql plugin: Option `%s' not allowed here.", child->key);
+ status = -1;
+ }
+
+ if (status != 0)
+ break;
}
- wait_for = wait_increase;
- wait_increase *= 2;
- if (wait_increase > 86400)
- wait_increase = 86400;
+ /* Check if there were any `Database' blocks. */
+ if (plugin_block > 1)
+ {
+ /* There were connection blocks. Don't use any legacy stuff. */
+ if ((db->host != NULL)
+ || (db->user != NULL)
+ || (db->pass != NULL)
+ || (db->database != NULL)
+ || (db->socket != NULL)
+ || (db->port != 0))
+ {
+ WARNING ("mysql plugin: At least one <Database> "
+ "block has been found. The legacy "
+ "configuration will be ignored.");
+ }
+ mysql_database_free (db);
+ return (0);
+ }
+ else if (plugin_block != 0)
+ {
+ WARNING ("mysql plugin: You're using the legacy "
+ "configuration options. Please consider "
+ "updating your configuration!");
+ }
+
+ /* Check that all necessary options have been given. */
+ while (status == 0)
+ {
+ /* Zero is allowed and automatically handled by
+ * `mysql_real_connect'. */
+ if ((db->port < 0) || (db->port > 65535))
+ {
+ ERROR ("mysql plugin: Database %s: Port number out "
+ "of range: %i",
+ (db->instance != NULL)
+ ? db->instance
+ : "<legacy>",
+ db->port);
+ status = -1;
+ }
+ break;
+ } /* while (status == 0) */
+
+ /* If all went well, register this database for reading */
+ if (status == 0)
+ {
+ user_data_t ud;
+ char cb_name[DATA_MAX_NAME_LEN];
- if ((con = mysql_init (con)) == NULL)
+ DEBUG ("mysql plugin: Registering new read callback: %s",
+ (db->database != NULL) ? db->database : "<default>");
+
+ memset (&ud, 0, sizeof (ud));
+ ud.data = (void *) db;
+ ud.free_func = mysql_database_free;
+
+ if (db->database != NULL)
+ ssnprintf (cb_name, sizeof (cb_name), "mysql-%s",
+ db->database);
+ else
+ sstrncpy (cb_name, "mysql", sizeof (cb_name));
+
+ plugin_register_complex_read (cb_name, mysql_read,
+ /* interval = */ NULL, &ud);
+ }
+ else
+ {
+ mysql_database_free (db);
+ return (-1);
+ }
+
+ return (0);
+} /* }}} int mysql_config */
+
+/* }}} End of configuration handling functions */
+
+static MYSQL *getconnection (mysql_database_t *db)
+{
+ if (db->state != 0)
{
- ERROR ("mysql_init failed: %s", mysql_error (con));
- state = 0;
+ int err;
+ if ((err = mysql_ping (db->con)) != 0)
+ {
+ WARNING ("mysql_ping failed: %s", mysql_error (db->con));
+ db->state = 0;
+ }
+ else
+ {
+ db->state = 1;
+ return (db->con);
+ }
+ }
+
+ if ((db->con = mysql_init (db->con)) == NULL)
+ {
+ ERROR ("mysql_init failed: %s", mysql_error (db->con));
+ db->state = 0;
return (NULL);
}
- if (mysql_real_connect (con, host, user, pass, db, port, socket, 0) == NULL)
+ if (mysql_real_connect (db->con, db->host, db->user, db->pass,
+ db->database, db->port, db->socket, 0) == NULL)
{
- ERROR ("mysql_real_connect failed: %s", mysql_error (con));
- state = 0;
+ ERROR ("mysql plugin: Failed to connect to database %s "
+ "at server %s: %s",
+ (db->database != NULL) ? db->database : "<none>",
+ (db->host != NULL) ? db->host : "localhost",
+ mysql_error (db->con));
+ db->state = 0;
return (NULL);
}
else
{
- state = 1;
- wait_for = 0;
- wait_increase = 60;
- return (con);
+ INFO ("mysql plugin: Sucessfully connected to database %s "
+ "at server %s (server version: %s, protocol version: %d)",
+ (db->database != NULL) ? db->database : "<none>",
+ mysql_get_host_info (db->con),
+ mysql_get_server_info (db->con),
+ mysql_get_proto_info (db->con));
+ db->state = 1;
+ return (db->con);
}
-} /* static MYSQL *getconnection (void) */
+} /* static MYSQL *getconnection (mysql_database_t *db) */
-static int config (const char *key, const char *value)
+static void set_host (mysql_database_t *db, char *buf, size_t buflen)
{
- if (strcasecmp (key, "host") == 0)
- return ((host = strdup (value)) == NULL ? 1 : 0);
- else if (strcasecmp (key, "user") == 0)
- return ((user = strdup (value)) == NULL ? 1 : 0);
- else if (strcasecmp (key, "password") == 0)
- return ((pass = strdup (value)) == NULL ? 1 : 0);
- else if (strcasecmp (key, "database") == 0)
- return ((db = strdup (value)) == NULL ? 1 : 0);
- else if (strcasecmp (key, "socket") == 0)
- return ((socket = strdup (value)) == NULL ? 1 : 0);
- else if (strcasecmp (key, "port") == 0)
+ /* XXX legacy mode - use hostname_g */
+ if (db->instance == NULL)
+ sstrncpy (buf, hostname_g, buflen);
+ else
{
- char *endptr = NULL;
- int temp;
-
- errno = 0;
- temp = strtol (value, &endptr, 0);
- if ((errno != 0) || (value == endptr))
- {
- ERROR ("mysql plugin: Invalid \"Port\" argument: %s",
- value);
- port = 0;
- return (1);
- }
- else if ((temp < 0) || (temp >= 65535))
- {
- ERROR ("mysql plugin: Port number out of range: %i",
- temp);
- port = 0;
- return (1);
- }
-
- port = temp;
- return (0);
+ if ((db->host == NULL)
+ || (strcmp ("", db->host) == 0)
+ || (strcmp ("localhost", db->host) == 0))
+ sstrncpy (buf, hostname_g, buflen);
+ else
+ sstrncpy (buf, db->host, buflen);
}
+}
+
+static void set_plugin_instance (mysql_database_t *db,
+ char *buf, size_t buflen)
+{
+ /* XXX legacy mode - no plugin_instance */
+ if (db->instance == NULL)
+ sstrncpy (buf, "", buflen);
else
- return (-1);
-} /* int config */
+ sstrncpy (buf, db->instance, buflen);
+}
-static void counter_submit (const char *type, const char *type_instance,
- counter_t value)
+static void submit (const char *type, const char *type_instance,
+ value_t *values, size_t values_len, mysql_database_t *db)
{
- value_t values[1];
value_list_t vl = VALUE_LIST_INIT;
- values[0].counter = value;
+ vl.values = values;
+ vl.values_len = values_len;
+
+ set_host (db, vl.host, sizeof (vl.host));
- vl.values = values;
- vl.values_len = 1;
- sstrncpy (vl.host, hostname_g, sizeof (vl.host));
sstrncpy (vl.plugin, "mysql", sizeof (vl.plugin));
+ set_plugin_instance (db, vl.plugin_instance, sizeof (vl.plugin_instance));
+
sstrncpy (vl.type, type, sizeof (vl.type));
- sstrncpy (vl.type_instance, type_instance, sizeof (vl.type_instance));
+ if (type_instance != NULL)
+ sstrncpy (vl.type_instance, type_instance, sizeof (vl.type_instance));
plugin_dispatch_values (&vl);
+} /* submit */
+
+static void counter_submit (const char *type, const char *type_instance,
+ counter_t value, mysql_database_t *db)
+{
+ value_t values[1];
+
+ values[0].counter = value;
+ submit (type, type_instance, values, STATIC_ARRAY_SIZE (values), db);
} /* void counter_submit */
+static void gauge_submit (const char *type, const char *type_instance,
+ gauge_t value, mysql_database_t *db)
+{
+ value_t values[1];
+
+ values[0].gauge = value;
+ submit (type, type_instance, values, STATIC_ARRAY_SIZE (values), db);
+} /* void gauge_submit */
+
static void qcache_submit (counter_t hits, counter_t inserts,
counter_t not_cached, counter_t lowmem_prunes,
- gauge_t queries_in_cache)
+ gauge_t queries_in_cache, mysql_database_t *db)
{
value_t values[5];
- value_list_t vl = VALUE_LIST_INIT;
values[0].counter = hits;
values[1].counter = inserts;
values[3].counter = lowmem_prunes;
values[4].gauge = queries_in_cache;
- vl.values = values;
- vl.values_len = 5;
- sstrncpy (vl.host, hostname_g, sizeof (vl.host));
- sstrncpy (vl.plugin, "mysql", sizeof (vl.plugin));
- sstrncpy (vl.type, "mysql_qcache", sizeof (vl.type));
-
- plugin_dispatch_values (&vl);
+ submit ("mysql_qcache", NULL, values, STATIC_ARRAY_SIZE (values), db);
} /* void qcache_submit */
static void threads_submit (gauge_t running, gauge_t connected, gauge_t cached,
- counter_t created)
+ counter_t created, mysql_database_t *db)
{
value_t values[4];
- value_list_t vl = VALUE_LIST_INIT;
values[0].gauge = running;
values[1].gauge = connected;
values[2].gauge = cached;
values[3].counter = created;
- vl.values = values;
- vl.values_len = 4;
- sstrncpy (vl.host, hostname_g, sizeof (vl.host));
- sstrncpy (vl.plugin, "mysql", sizeof (vl.plugin));
- sstrncpy (vl.type, "mysql_threads", sizeof (vl.type));
-
- plugin_dispatch_values (&vl);
+ submit ("mysql_threads", NULL, values, STATIC_ARRAY_SIZE (values), db);
} /* void threads_submit */
-static void traffic_submit (counter_t rx, counter_t tx)
+static void traffic_submit (counter_t rx, counter_t tx, mysql_database_t *db)
{
value_t values[2];
- value_list_t vl = VALUE_LIST_INIT;
values[0].counter = rx;
values[1].counter = tx;
- vl.values = values;
- vl.values_len = 2;
- sstrncpy (vl.host, hostname_g, sizeof (vl.host));
- sstrncpy (vl.plugin, "mysql", sizeof (vl.plugin));
- sstrncpy (vl.type, "mysql_octets", sizeof (vl.type));
-
- plugin_dispatch_values (&vl);
+ submit ("mysql_octets", NULL, values, STATIC_ARRAY_SIZE (values), db);
} /* void traffic_submit */
-static int mysql_read (void)
+static MYSQL_RES *exec_query (MYSQL *con, const char *query)
+{
+ MYSQL_RES *res;
+
+ int query_len = strlen (query);
+
+ if (mysql_real_query (con, query, query_len))
+ {
+ ERROR ("mysql plugin: Failed to execute query: %s",
+ mysql_error (con));
+ INFO ("mysql plugin: SQL query was: %s", query);
+ return (NULL);
+ }
+
+ res = mysql_store_result (con);
+ if (res == NULL)
+ {
+ ERROR ("mysql plugin: Failed to store query result: %s",
+ mysql_error (con));
+ INFO ("mysql plugin: SQL query was: %s", query);
+ return (NULL);
+ }
+
+ return (res);
+} /* exec_query */
+
+static int mysql_read_master_stats (mysql_database_t *db, MYSQL *con)
{
+ MYSQL_RES *res;
+ MYSQL_ROW row;
+
+ char *query;
+ int field_num;
+ unsigned long long position;
+
+ query = "SHOW MASTER STATUS";
+
+ res = exec_query (con, query);
+ if (res == NULL)
+ return (-1);
+
+ row = mysql_fetch_row (res);
+ if (row == NULL)
+ {
+ ERROR ("mysql plugin: Failed to get master statistics: "
+ "`%s' did not return any rows.", query);
+ return (-1);
+ }
+
+ field_num = mysql_num_fields (res);
+ if (field_num < 2)
+ {
+ ERROR ("mysql plugin: Failed to get master statistics: "
+ "`%s' returned less than two columns.", query);
+ return (-1);
+ }
+
+ position = atoll (row[1]);
+ counter_submit ("mysql_log_position", "master-bin", position, db);
+
+ row = mysql_fetch_row (res);
+ if (row != NULL)
+ WARNING ("mysql plugin: `%s' returned more than one row - "
+ "ignoring further results.", query);
+
+ mysql_free_result (res);
+
+ return (0);
+} /* mysql_read_master_stats */
+
+static int mysql_read_slave_stats (mysql_database_t *db, MYSQL *con)
+{
+ MYSQL_RES *res;
+ MYSQL_ROW row;
+
+ char *query;
+ int field_num;
+
+ /* WTF? libmysqlclient does not seem to provide any means to
+ * translate a column name to a column index ... :-/ */
+ const int READ_MASTER_LOG_POS_IDX = 6;
+ const int SLAVE_IO_RUNNING_IDX = 10;
+ const int SLAVE_SQL_RUNNING_IDX = 11;
+ const int EXEC_MASTER_LOG_POS_IDX = 21;
+ const int SECONDS_BEHIND_MASTER_IDX = 32;
+
+ query = "SHOW SLAVE STATUS";
+
+ res = exec_query (con, query);
+ if (res == NULL)
+ return (-1);
+
+ row = mysql_fetch_row (res);
+ if (row == NULL)
+ {
+ ERROR ("mysql plugin: Failed to get slave statistics: "
+ "`%s' did not return any rows.", query);
+ return (-1);
+ }
+
+ field_num = mysql_num_fields (res);
+ if (field_num < 33)
+ {
+ ERROR ("mysql plugin: Failed to get slave statistics: "
+ "`%s' returned less than 33 columns.", query);
+ return (-1);
+ }
+
+ if (db->slave_stats)
+ {
+ unsigned long long counter;
+ double gauge;
+
+ counter = atoll (row[READ_MASTER_LOG_POS_IDX]);
+ counter_submit ("mysql_log_position", "slave-read", counter, db);
+
+ counter = atoll (row[EXEC_MASTER_LOG_POS_IDX]);
+ counter_submit ("mysql_log_position", "slave-exec", counter, db);
+
+ if (row[SECONDS_BEHIND_MASTER_IDX] != NULL)
+ {
+ gauge = atof (row[SECONDS_BEHIND_MASTER_IDX]);
+ gauge_submit ("time_offset", NULL, gauge, db);
+ }
+ }
+
+ if (db->slave_notif)
+ {
+ notification_t n = { 0, time (NULL), "", "",
+ "mysql", "", "time_offset", "", NULL };
+
+ char *io, *sql;
+
+ io = row[SLAVE_IO_RUNNING_IDX];
+ sql = row[SLAVE_SQL_RUNNING_IDX];
+
+ set_host (db, n.host, sizeof (n.host));
+ set_plugin_instance (db,
+ n.plugin_instance, sizeof (n.plugin_instance));
+
+ if (((io == NULL) || (strcasecmp (io, "yes") != 0))
+ && (db->slave_io_running))
+ {
+ n.severity = NOTIF_WARNING;
+ ssnprintf (n.message, sizeof (n.message),
+ "slave I/O thread not started or not connected to master");
+ plugin_dispatch_notification (&n);
+ db->slave_io_running = 0;
+ }
+ else if (((io != NULL) && (strcasecmp (io, "yes") == 0))
+ && (! db->slave_io_running))
+ {
+ n.severity = NOTIF_OKAY;
+ ssnprintf (n.message, sizeof (n.message),
+ "slave I/O thread started and connected to master");
+ plugin_dispatch_notification (&n);
+ db->slave_io_running = 1;
+ }
+
+ if (((sql == NULL) || (strcasecmp (sql, "yes") != 0))
+ && (db->slave_sql_running))
+ {
+ n.severity = NOTIF_WARNING;
+ ssnprintf (n.message, sizeof (n.message),
+ "slave SQL thread not started");
+ plugin_dispatch_notification (&n);
+ db->slave_sql_running = 0;
+ }
+ else if (((sql != NULL) && (strcasecmp (sql, "yes") == 0))
+ && (! db->slave_sql_running))
+ {
+ n.severity = NOTIF_OKAY;
+ ssnprintf (n.message, sizeof (n.message),
+ "slave SQL thread started");
+ plugin_dispatch_notification (&n);
+ db->slave_sql_running = 0;
+ }
+ }
+
+ row = mysql_fetch_row (res);
+ if (row != NULL)
+ WARNING ("mysql plugin: `%s' returned more than one row - "
+ "ignoring further results.", query);
+
+ mysql_free_result (res);
+
+ return (0);
+} /* mysql_read_slave_stats */
+
+static int mysql_read (user_data_t *ud)
+{
+ mysql_database_t *db;
MYSQL *con;
MYSQL_RES *res;
MYSQL_ROW row;
char *query;
- int query_len;
int field_num;
unsigned long long qcache_hits = 0ULL;
unsigned long long traffic_incoming = 0ULL;
unsigned long long traffic_outgoing = 0ULL;
+ if ((ud == NULL) || (ud->data == NULL))
+ {
+ ERROR ("mysql plugin: mysql_database_read: Invalid user data.");
+ return (-1);
+ }
+
+ db = (mysql_database_t *) ud->data;
+
/* An error message will have been printed in this case */
- if ((con = getconnection ()) == NULL)
+ if ((con = getconnection (db)) == NULL)
return (-1);
query = "SHOW STATUS";
if (mysql_get_server_version (con) >= 50002)
query = "SHOW GLOBAL STATUS";
- query_len = strlen (query);
-
- if (mysql_real_query (con, query, query_len))
- {
- ERROR ("mysql_real_query failed: %s\n",
- mysql_error (con));
- return (-1);
- }
-
- if ((res = mysql_store_result (con)) == NULL)
- {
- ERROR ("mysql_store_result failed: %s\n",
- mysql_error (con));
+ res = exec_query (con, query);
+ if (res == NULL)
return (-1);
- }
field_num = mysql_num_fields (res);
while ((row = mysql_fetch_row (res)))
/* Ignore `prepared statements' */
if (strncmp (key, "Com_stmt_", 9) != 0)
- counter_submit ("mysql_commands", key + 4, val);
+ counter_submit ("mysql_commands", key + 4, val, db);
}
else if (strncmp (key, "Handler_", 8) == 0)
{
if (val == 0ULL)
continue;
- counter_submit ("mysql_handler", key + 8, val);
+ counter_submit ("mysql_handler", key + 8, val, db);
}
else if (strncmp (key, "Qcache_", 7) == 0)
{
|| (qcache_not_cached != 0ULL)
|| (qcache_lowmem_prunes != 0ULL))
qcache_submit (qcache_hits, qcache_inserts, qcache_not_cached,
- qcache_lowmem_prunes, qcache_queries_in_cache);
+ qcache_lowmem_prunes, qcache_queries_in_cache, db);
if (threads_created != 0ULL)
threads_submit (threads_running, threads_connected,
- threads_cached, threads_created);
+ threads_cached, threads_created, db);
+
+ traffic_submit (traffic_incoming, traffic_outgoing, db);
- traffic_submit (traffic_incoming, traffic_outgoing);
+ if (db->master_stats)
+ mysql_read_master_stats (db, con);
- /* mysql_close (con); */
+ if ((db->slave_stats) || (db->slave_notif))
+ mysql_read_slave_stats (db, con);
return (0);
} /* int mysql_read */
void module_register (void)
{
- plugin_register_config ("mysql", config, config_keys, config_keys_num);
- plugin_register_read ("mysql", mysql_read);
+ plugin_register_complex_config ("mysql", mysql_config);
} /* void module_register */
static int ow_family_features_num = STATIC_ARRAY_SIZE (ow_family_features);
static char *device_g = NULL;
+static int ow_interval = 0;
static const char *config_keys[] =
{
"Device",
"IgnoreSelected",
"Sensor",
+ "Interval"
};
static int config_keys_num = STATIC_ARRAY_SIZE (config_keys);
sfree (device_g);
device_g = temp;
}
- else
+ else if (strcasecmp ("Interval", key) == 0)
{
- return (-1);
+ int tmp;
+ tmp = atoi (value);
+ if (tmp > 0)
+ ow_interval = tmp;
+ else
+ ERROR ("onewire plugin: Invalid `Interval' setting: %s", value);
}
-
- return (0);
-}
-
-static int cow_init (void)
-{
- int status;
-
- if (device_g == NULL)
+ else
{
- ERROR ("onewire plugin: cow_init: No device configured.");
return (-1);
}
- status = (int) OW_init (device_g);
- if (status != 0)
- {
- ERROR ("onewire plugin: OW_init(%s) failed: %i.", device_g, status);
- return (1);
- }
-
return (0);
-} /* int cow_init */
+}
static int cow_read_values (const char *path, const char *name,
const ow_family_features_t *family_info)
return (0);
} /* int cow_read_bus */
-static int cow_read (void)
+static int cow_read (user_data_t *ud __attribute__((unused)))
{
return (cow_read_bus ("/"));
} /* int cow_read */
return (0);
} /* int cow_shutdown */
+static int cow_init (void)
+{
+ int status;
+ struct timespec cb_interval;
+
+ if (device_g == NULL)
+ {
+ ERROR ("onewire plugin: cow_init: No device configured.");
+ return (-1);
+ }
+
+ status = (int) OW_init (device_g);
+ if (status != 0)
+ {
+ ERROR ("onewire plugin: OW_init(%s) failed: %i.", device_g, status);
+ return (1);
+ }
+
+ memset (&cb_interval, 0, sizeof (cb_interval));
+ if (ow_interval > 0)
+ cb_interval.tv_sec = (time_t) ow_interval;
+
+ plugin_register_complex_read ("onewire", cow_read,
+ &cb_interval, /* user data = */ NULL);
+ plugin_register_shutdown ("onewire", cow_shutdown);
+
+ return (0);
+} /* int cow_init */
+
void module_register (void)
{
plugin_register_init ("onewire", cow_init);
- plugin_register_read ("onewire", cow_read);
- plugin_register_shutdown ("onewire", cow_shutdown);
plugin_register_config ("onewire", cow_load_config,
config_keys, config_keys_num);
}
#include "configfile.h"
#include "utils_avltree.h"
#include "utils_llist.h"
+#include "utils_heap.h"
#include "utils_cache.h"
#include "utils_threshold.h"
#include "filter_chain.h"
#define rf_callback rf_super.cf_callback
#define rf_udata rf_super.cf_udata
callback_func_t rf_super;
+ char rf_name[DATA_MAX_NAME_LEN];
int rf_type;
- int rf_wait_time;
- int rf_wait_left;
- enum { DONE = 0, TODO = 1, ACTIVE = 2 } rf_needs_read;
+ struct timespec rf_interval;
+ struct timespec rf_effective_interval;
+ struct timespec rf_next_read;
};
typedef struct read_func_s read_func_t;
* Private variables
*/
static llist_t *list_init;
-static llist_t *list_read;
static llist_t *list_write;
static llist_t *list_flush;
static llist_t *list_shutdown;
static char *plugindir = NULL;
+static c_heap_t *read_heap = NULL;
static int read_loop = 1;
static pthread_mutex_t read_lock = PTHREAD_MUTEX_INITIALIZER;
static pthread_cond_t read_cond = PTHREAD_COND_INITIALIZER;
*list = NULL;
} /* }}} void destroy_all_callbacks */
+static void destroy_read_heap (void) /* {{{ */
+{
+ if (read_heap == NULL)
+ return;
+
+ while (42)
+ {
+ callback_func_t *cf;
+
+ cf = c_head_get_root (read_heap);
+ if (cf == NULL)
+ break;
+
+ destroy_callback (cf);
+ }
+
+ c_heap_destroy (read_heap);
+ read_heap = NULL;
+} /* }}} void destroy_read_heap */
+
static int register_callback (llist_t **list, /* {{{ */
const char *name, callback_func_t *cf)
{
static void *plugin_read_thread (void __attribute__((unused)) *args)
{
- llentry_t *le;
- read_func_t *rf;
- int status;
- int done;
-
- pthread_mutex_lock (&read_lock);
-
while (read_loop != 0)
{
- le = llist_head (list_read);
- done = 0;
+ read_func_t *rf;
+ struct timeval now;
+ int status;
- while ((read_loop != 0) && (le != NULL))
+ /* Get the read function that needs to be read next. */
+ rf = c_head_get_root (read_heap);
+ if (rf == NULL)
{
- rf = (read_func_t *) le->value;
+ struct timespec abstime;
- if (rf->rf_needs_read != TODO)
- {
- le = le->next;
- continue;
- }
+ gettimeofday (&now, /* timezone = */ NULL);
- /* We will do this read function */
- rf->rf_needs_read = ACTIVE;
+ abstime.tv_sec = now.tv_sec + interval_g;
+ abstime.tv_nsec = 1000 * now.tv_usec;
- DEBUG ("[thread #%5lu] plugin: plugin_read_thread: Handling %s",
- (unsigned long int) pthread_self (), le->key);
+ pthread_mutex_lock (&read_lock);
+ pthread_cond_timedwait (&read_cond, &read_lock,
+ &abstime);
pthread_mutex_unlock (&read_lock);
+ continue;
+ }
- if (rf->rf_type == RF_SIMPLE)
- {
- int (*callback) (void);
+ if ((rf->rf_interval.tv_sec == 0) && (rf->rf_interval.tv_nsec == 0))
+ {
+ gettimeofday (&now, /* timezone = */ NULL);
- callback = rf->rf_callback;
- status = (*callback) ();
- }
- else
- {
- plugin_read_cb callback;
+ rf->rf_interval.tv_sec = interval_g;
+ rf->rf_interval.tv_nsec = 0;
- callback = rf->rf_callback;
- status = (*callback) (&rf->rf_udata);
- }
+ rf->rf_effective_interval = rf->rf_interval;
- done++;
+ rf->rf_next_read.tv_sec = now.tv_sec;
+ rf->rf_next_read.tv_nsec = 1000 * now.tv_usec;
+ }
- if (status != 0)
- {
- if (rf->rf_wait_time < interval_g)
- rf->rf_wait_time = interval_g;
- rf->rf_wait_left = rf->rf_wait_time;
- rf->rf_wait_time = rf->rf_wait_time * 2;
- if (rf->rf_wait_time > 86400)
- rf->rf_wait_time = 86400;
-
- NOTICE ("read-function of plugin `%s' "
- "failed. Will suspend it for %i "
- "seconds.", le->key, rf->rf_wait_left);
- }
- else
+ /* sleep until this entry is due,
+ * using pthread_cond_timedwait */
+ pthread_mutex_lock (&read_lock);
+ pthread_cond_timedwait (&read_cond, &read_lock,
+ &rf->rf_next_read);
+ pthread_mutex_unlock (&read_lock);
+
+ /* Check if we're supposed to stop.. This may have interrupted
+ * the sleep, too. */
+ if (read_loop == 0)
+ {
+ /* Insert `rf' again, so it can be free'd correctly */
+ c_heap_insert (read_heap, rf);
+ break;
+ }
+
+ DEBUG ("plugin_read_thread: Handling `%s'.", rf->rf_name);
+
+ if (rf->rf_type == RF_SIMPLE)
+ {
+ int (*callback) (void);
+
+ callback = rf->rf_callback;
+ status = (*callback) ();
+ }
+ else
+ {
+ plugin_read_cb callback;
+
+ callback = rf->rf_callback;
+ status = (*callback) (&rf->rf_udata);
+ }
+
+ /* If the function signals failure, we will increase the
+ * intervals in which it will be called. */
+ if (status != 0)
+ {
+ rf->rf_effective_interval.tv_sec *= 2;
+ rf->rf_effective_interval.tv_nsec *= 2;
+ NORMALIZE_TIMESPEC (rf->rf_effective_interval);
+
+ if (rf->rf_effective_interval.tv_sec >= 86400)
{
- rf->rf_wait_left = 0;
- rf->rf_wait_time = interval_g;
+ rf->rf_effective_interval.tv_sec = 86400;
+ rf->rf_effective_interval.tv_nsec = 0;
}
- pthread_mutex_lock (&read_lock);
-
- rf->rf_needs_read = DONE;
- le = le->next;
- } /* while (le != NULL) */
+ NOTICE ("read-function of plugin `%s' failed. "
+ "Will suspend it for %i seconds.",
+ rf->rf_name,
+ (int) rf->rf_effective_interval.tv_sec);
+ }
+ else
+ {
+ /* Success: Restore the interval, if it was changed. */
+ rf->rf_effective_interval = rf->rf_interval;
+ }
- if ((read_loop != 0) && (done == 0))
+ /* update the ``next read due'' field */
+ gettimeofday (&now, /* timezone = */ NULL);
+
+ DEBUG ("plugin_read_thread: Effective interval of the "
+ "%s plugin is %i.%09i.",
+ rf->rf_name,
+ (int) rf->rf_effective_interval.tv_sec,
+ (int) rf->rf_effective_interval.tv_nsec);
+
+ /* Calculate the next (absolute) time at which this function
+ * should be called. */
+ rf->rf_next_read.tv_sec = rf->rf_next_read.tv_sec
+ + rf->rf_effective_interval.tv_sec;
+ rf->rf_next_read.tv_nsec = rf->rf_next_read.tv_nsec
+ + rf->rf_effective_interval.tv_nsec;
+ NORMALIZE_TIMESPEC (rf->rf_next_read);
+
+ /* Check, if `rf_next_read' is in the past. */
+ if ((rf->rf_next_read.tv_sec < now.tv_sec)
+ || ((rf->rf_next_read.tv_sec == now.tv_sec)
+ && (rf->rf_next_read.tv_nsec < (1000 * now.tv_usec))))
{
- DEBUG ("[thread #%5lu] plugin: plugin_read_thread: Waiting on read_cond.",
- (unsigned long int) pthread_self ());
- pthread_cond_wait (&read_cond, &read_lock);
+ /* `rf_next_read' is in the past. Insert `now'
+ * so this value doesn't trail off into the
+ * past too much. */
+ rf->rf_next_read.tv_sec = now.tv_sec;
+ rf->rf_next_read.tv_nsec = 1000 * now.tv_usec;
}
- } /* while (read_loop) */
- pthread_mutex_unlock (&read_lock);
+ DEBUG ("plugin_read_thread: Next read of the %s plugin at %i.%09i.",
+ rf->rf_name,
+ (int) rf->rf_next_read.tv_sec,
+ (int) rf->rf_next_read.tv_nsec);
+
+ /* Re-insert this read function into the heap again. */
+ c_heap_insert (read_heap, rf);
+ } /* while (read_loop) */
pthread_exit (NULL);
return ((void *) 0);
/* user_data = */ NULL));
} /* plugin_register_init */
+static int plugin_compare_read_func (const void *arg0, const void *arg1)
+{
+ const read_func_t *rf0;
+ const read_func_t *rf1;
+
+ rf0 = arg0;
+ rf1 = arg1;
+
+ if (rf0->rf_next_read.tv_sec < rf1->rf_next_read.tv_sec)
+ return (-1);
+ else if (rf0->rf_next_read.tv_sec > rf1->rf_next_read.tv_sec)
+ return (1);
+ else if (rf0->rf_next_read.tv_nsec < rf1->rf_next_read.tv_nsec)
+ return (-1);
+ else if (rf0->rf_next_read.tv_nsec > rf1->rf_next_read.tv_nsec)
+ return (1);
+ else
+ return (0);
+} /* int plugin_compare_read_func */
+
int plugin_register_read (const char *name,
int (*callback) (void))
{
read_func_t *rf;
+ if (read_heap == NULL)
+ {
+ read_heap = c_heap_create (plugin_compare_read_func);
+ if (read_heap == NULL)
+ {
+ ERROR ("plugin_register_read: "
+ "c_heap_create failed.");
+ return (-1);
+ }
+ }
+
rf = (read_func_t *) malloc (sizeof (read_func_t));
if (rf == NULL)
{
rf->rf_callback = (void *) callback;
rf->rf_udata.data = NULL;
rf->rf_udata.free_func = NULL;
- rf->rf_wait_time = interval_g;
- rf->rf_wait_left = 0;
+ sstrncpy (rf->rf_name, name, sizeof (rf->rf_name));
rf->rf_type = RF_SIMPLE;
- rf->rf_needs_read = DONE;
+ rf->rf_interval.tv_sec = 0;
+ rf->rf_interval.tv_nsec = 0;
+ rf->rf_effective_interval = rf->rf_interval;
- return (register_callback (&list_read, name, (callback_func_t *) rf));
+ return (c_heap_insert (read_heap, rf));
} /* int plugin_register_read */
int plugin_register_complex_read (const char *name,
- plugin_read_cb callback, user_data_t *user_data)
+ plugin_read_cb callback,
+ const struct timespec *interval,
+ user_data_t *user_data)
{
read_func_t *rf;
+ if (read_heap == NULL)
+ {
+ read_heap = c_heap_create (plugin_compare_read_func);
+ if (read_heap == NULL)
+ {
+ ERROR ("plugin_register_read: c_heap_create failed.");
+ return (-1);
+ }
+ }
+
rf = (read_func_t *) malloc (sizeof (read_func_t));
if (rf == NULL)
{
memset (rf, 0, sizeof (read_func_t));
rf->rf_callback = (void *) callback;
- rf->rf_wait_time = interval_g;
- rf->rf_wait_left = 0;
+ sstrncpy (rf->rf_name, name, sizeof (rf->rf_name));
rf->rf_type = RF_COMPLEX;
- rf->rf_needs_read = DONE;
+ if (interval != NULL)
+ {
+ rf->rf_interval = *interval;
+ }
+ rf->rf_effective_interval = rf->rf_interval;
/* Set user data */
if (user_data == NULL)
rf->rf_udata = *user_data;
}
- return (register_callback (&list_read, name, (callback_func_t *) rf));
+ return (c_heap_insert (read_heap, rf));
} /* int plugin_register_complex_read */
int plugin_register_write (const char *name,
int plugin_unregister_read (const char *name)
{
- return (plugin_unregister (list_read, name));
+ /* TODO: Implement removal of a specific key from the heap. */
+ assert (0);
+ return (-1);
}
int plugin_unregister_write (const char *name)
post_cache_chain = fc_chain_get_by_name (chain_name);
- if ((list_init == NULL) && (list_read == NULL))
+ if ((list_init == NULL) && (read_heap == NULL))
return;
/* Calling all init callbacks before checking if read callbacks
}
/* Start read-threads */
- if (list_read != NULL)
+ if (read_heap != NULL)
{
const char *rt;
int num;
}
} /* void plugin_init_all */
+/* TODO: Rename this function. */
void plugin_read_all (void)
{
- llentry_t *le;
- read_func_t *rf;
-
uc_check_timeout ();
- if (list_read == NULL)
- return;
-
- pthread_mutex_lock (&read_lock);
-
- le = llist_head (list_read);
- while (le != NULL)
- {
- rf = (read_func_t *) le->value;
-
- if (rf->rf_needs_read != DONE)
- {
- le = le->next;
- continue;
- }
-
- if (rf->rf_wait_left > 0)
- rf->rf_wait_left -= interval_g;
-
- if (rf->rf_wait_left <= 0)
- {
- rf->rf_needs_read = TODO;
- }
-
- le = le->next;
- }
-
- DEBUG ("plugin: plugin_read_all: Signalling `read_cond'");
- pthread_cond_broadcast (&read_cond);
- pthread_mutex_unlock (&read_lock);
+ return;
} /* void plugin_read_all */
/* Read function called when the `-T' command line argument is given. */
int plugin_read_all_once (void)
{
- llentry_t *le;
- read_func_t *rf;
int status;
int return_status = 0;
- if (list_read == NULL)
+ if (read_heap == NULL)
{
NOTICE ("No read-functions are registered.");
return (0);
}
- for (le = llist_head (list_read);
- le != NULL;
- le = le->next)
+ while (42)
{
- rf = (read_func_t *) le->value;
+ read_func_t *rf;
+
+ rf = c_head_get_root (read_heap);
+ if (rf == NULL)
+ break;
if (rf->rf_type == RF_SIMPLE)
{
if (status != 0)
{
NOTICE ("read-function of plugin `%s' failed.",
- le->key);
+ rf->rf_name);
return_status = -1;
}
+
+ destroy_callback ((void *) rf);
}
return (return_status);
stop_read_threads ();
destroy_all_callbacks (&list_init);
- destroy_all_callbacks (&list_read);
+ destroy_read_heap ();
plugin_flush (/* plugin = */ NULL, /* timeout = */ -1,
/* identifier = */ NULL);
if (vl->time == 0)
vl->time = time (NULL);
+ if (vl->interval <= 0)
+ vl->interval = interval_g;
+
DEBUG ("plugin_dispatch_values: time = %u; interval = %i; "
"host = %s; "
"plugin = %s; plugin_instance = %s; "
int plugin_register_read (const char *name,
int (*callback) (void));
int plugin_register_complex_read (const char *name,
- plugin_read_cb callback, user_data_t *user_data);
+ plugin_read_cb callback,
+ const struct timespec *interval,
+ user_data_t *user_data);
int plugin_register_write (const char *name,
plugin_write_cb callback, user_data_t *user_data);
int plugin_register_flush (const char *name,
void *sess_handle;
c_complain_t complaint;
uint32_t interval;
- time_t next_update;
data_definition_t **data_list;
int data_list_len;
- enum /******************************************************/
- { /* This host.. */
- STATE_IDLE, /* - just sits there until `next_update < interval_g' */
- STATE_WAIT, /* - waits to be queried. */
- STATE_BUSY /* - is currently being queried. */
- } state; /******************************************************/
- struct host_definition_s *next;
};
typedef struct host_definition_s host_definition_t;
/*
* Private variables
*/
-static int do_shutdown = 0;
-
-pthread_t *threads = NULL;
-int threads_num = 0;
-
static data_definition_t *data_head = NULL;
-static host_definition_t *host_head = NULL;
-static pthread_mutex_t host_lock = PTHREAD_MUTEX_INITIALIZER;
-static pthread_cond_t host_cond = PTHREAD_COND_INITIALIZER;
+/*
+ * Prototypes
+ */
+static int csnmp_read_host (user_data_t *ud);
/*
* Private functions
*/
+static void csnmp_host_close_session (host_definition_t *host) /* {{{ */
+{
+ if (host->sess_handle == NULL)
+ return;
+
+ snmp_sess_close (host->sess_handle);
+ host->sess_handle = NULL;
+} /* }}} void csnmp_host_close_session */
+
+static void csnmp_host_definition_destroy (void *arg) /* {{{ */
+{
+ host_definition_t *hd;
+
+ hd = arg;
+
+ if (hd == NULL)
+ return;
+
+ if (hd->name != NULL)
+ {
+ DEBUG ("snmp plugin: Destroying host definition for host `%s'.",
+ hd->name);
+ }
+
+ csnmp_host_close_session (hd);
+
+ sfree (hd->name);
+ sfree (hd->address);
+ sfree (hd->community);
+ sfree (hd->data_list);
+
+ sfree (hd);
+} /* }}} void csnmp_host_definition_destroy */
+
/* Many functions to handle the configuration. {{{ */
/* First there are many functions which do configuration stuff. It's a big
* bloated and messy, I'm afraid. */
int status = 0;
int i;
+ /* Registration stuff. */
+ char cb_name[DATA_MAX_NAME_LEN];
+ user_data_t cb_data;
+ struct timespec cb_interval;
+
if ((ci->values_num != 1) || (ci->values[0].type != OCONFIG_TYPE_STRING))
{
WARNING ("snmp plugin: `Host' needs exactly one string argument.");
hd->sess_handle = NULL;
hd->interval = 0;
- hd->next_update = 0;
- hd->state = STATE_IDLE;
for (i = 0; i < ci->children_num; i++)
{
if (status != 0)
{
- sfree (hd->name);
- sfree (hd);
+ csnmp_host_definition_destroy (hd);
return (-1);
}
DEBUG ("snmp plugin: hd = { name = %s, address = %s, community = %s, version = %i }",
hd->name, hd->address, hd->community, hd->version);
- if (host_head == NULL)
- host_head = hd;
- else
+ ssnprintf (cb_name, sizeof (cb_name), "snmp-%s", hd->name);
+
+ memset (&cb_data, 0, sizeof (cb_data));
+ cb_data.data = hd;
+ cb_data.free_func = csnmp_host_definition_destroy;
+
+ memset (&cb_interval, 0, sizeof (cb_interval));
+ if (hd->interval != 0)
+ cb_interval.tv_sec = (time_t) hd->interval;
+
+ status = plugin_register_complex_read (cb_name, csnmp_read_host,
+ /* interval = */ &cb_interval, /* user_data = */ &cb_data);
+ if (status != 0)
{
- host_definition_t *last;
- last = host_head;
- while (last->next != NULL)
- last = last->next;
- last->next = hd;
+ ERROR ("snmp plugin: Registering complex read function failed.");
+ csnmp_host_definition_destroy (hd);
+ return (-1);
}
return (0);
/* }}} End of the config stuff. Now the interesting part begins */
-static void csnmp_host_close_session (host_definition_t *host)
-{
- if (host->sess_handle == NULL)
- return;
-
- snmp_sess_close (host->sess_handle);
- host->sess_handle = NULL;
-} /* void csnmp_host_close_session */
-
static void csnmp_host_open_session (host_definition_t *host)
{
struct snmp_session sess;
return (0);
} /* int csnmp_read_value */
-static int csnmp_read_host (host_definition_t *host)
+static int csnmp_read_host (user_data_t *ud)
{
- int i;
+ host_definition_t *host;
time_t time_start;
time_t time_end;
+ int status;
+ int success;
+ int i;
+
+ host = ud->data;
+
+ if (host->interval == 0)
+ host->interval = interval_g;
time_start = time (NULL);
DEBUG ("snmp plugin: csnmp_read_host (%s) started at %u;", host->name,
if (host->sess_handle == NULL)
return (-1);
+ success = 0;
for (i = 0; i < host->data_list_len; i++)
{
data_definition_t *data = host->data_list[i];
if (data->is_table)
- csnmp_read_table (host, data);
+ status = csnmp_read_table (host, data);
else
- csnmp_read_value (host, data);
+ status = csnmp_read_value (host, data);
+
+ if (status == 0)
+ success++;
}
time_end = time (NULL);
(unsigned int) time_end);
if ((uint32_t) (time_end - time_start) > host->interval)
{
- WARNING ("snmp plugin: Host `%s' should be queried every %i seconds, "
- "but reading all values takes %u seconds.",
+ WARNING ("snmp plugin: Host `%s' should be queried every %"PRIu32
+ " seconds, but reading all values takes %u seconds.",
host->name, host->interval, (unsigned int) (time_end - time_start));
}
+ if (success == 0)
+ return (-1);
+
return (0);
} /* int csnmp_read_host */
-static void *csnmp_read_thread (void __attribute__((unused)) *data)
-{
- host_definition_t *host;
-
- pthread_mutex_lock (&host_lock);
- while (do_shutdown == 0)
- {
- pthread_cond_wait (&host_cond, &host_lock);
-
- for (host = host_head; host != NULL; host = host->next)
- {
- if (do_shutdown != 0)
- break;
- if (host->state != STATE_WAIT)
- continue;
-
- host->state = STATE_BUSY;
- pthread_mutex_unlock (&host_lock);
- csnmp_read_host (host);
- pthread_mutex_lock (&host_lock);
- host->state = STATE_IDLE;
- } /* for (host) */
- } /* while (do_shutdown == 0) */
- pthread_mutex_unlock (&host_lock);
-
- pthread_exit ((void *) 0);
- return ((void *) 0);
-} /* void *csnmp_read_thread */
-
static int csnmp_init (void)
{
- host_definition_t *host;
- int i;
-
- if (host_head == NULL)
- {
- NOTICE ("snmp plugin: No host has been defined.");
- return (-1);
- }
-
call_snmp_init_once ();
- threads_num = 0;
- for (host = host_head; host != NULL; host = host->next)
- {
- threads_num++;
- /* We need to initialize `interval' here, because `interval_g' isn't
- * initialized during `configure'. */
- host->next_update = time (NULL);
- if (host->interval == 0)
- {
- host->interval = interval_g;
- }
- else if (host->interval < (uint32_t) interval_g)
- {
- host->interval = interval_g;
- WARNING ("snmp plugin: Data for host `%s' will be collected every %i seconds.",
- host->name, host->interval);
- }
-
- csnmp_host_open_session (host);
- } /* for (host) */
-
- /* Now start the reading threads */
- if (threads_num > 3)
- {
- threads_num = 3 + ((threads_num - 3) / 10);
- if (threads_num > 10)
- threads_num = 10;
- }
-
- threads = (pthread_t *) malloc (threads_num * sizeof (pthread_t));
- if (threads == NULL)
- {
- ERROR ("snmp plugin: malloc failed.");
- return (-1);
- }
- memset (threads, '\0', threads_num * sizeof (pthread_t));
-
- for (i = 0; i < threads_num; i++)
- pthread_create (threads + i, NULL, csnmp_read_thread, (void *) 0);
-
return (0);
} /* int csnmp_init */
-static int csnmp_read (void)
-{
- host_definition_t *host;
- time_t now;
-
- if (host_head == NULL)
- {
- INFO ("snmp plugin: No hosts configured.");
- return (-1);
- }
-
- now = time (NULL);
-
- pthread_mutex_lock (&host_lock);
- for (host = host_head; host != NULL; host = host->next)
- {
- if (host->state != STATE_IDLE)
- continue;
-
- /* Skip this host if the next or a later iteration will be sufficient. */
- if (host->next_update >= (now + interval_g))
- continue;
-
- host->state = STATE_WAIT;
- host->next_update = now + host->interval;
- } /* for (host) */
-
- pthread_cond_broadcast (&host_cond);
- pthread_mutex_unlock (&host_lock);
-
- return (0);
-} /* int csnmp_read */
-
static int csnmp_shutdown (void)
{
- host_definition_t *host_this;
- host_definition_t *host_next;
-
data_definition_t *data_this;
data_definition_t *data_next;
- int i;
-
- pthread_mutex_lock (&host_lock);
- do_shutdown = 1;
- pthread_cond_broadcast (&host_cond);
- pthread_mutex_unlock (&host_lock);
-
- for (i = 0; i < threads_num; i++)
- pthread_join (threads[i], NULL);
-
- /* Now that all the threads have exited, let's free all the global variables.
- * This isn't really neccessary, I guess, but I think it's good stile to do
- * so anyway. */
- host_this = host_head;
- host_head = NULL;
- while (host_this != NULL)
- {
- host_next = host_this->next;
-
- csnmp_host_close_session (host_this);
-
- sfree (host_this->name);
- sfree (host_this->address);
- sfree (host_this->community);
- sfree (host_this->data_list);
- sfree (host_this);
-
- host_this = host_next;
- }
+ /* When we get here, the read threads have been stopped and all the
+ * `host_definition_t' will be freed. */
+ DEBUG ("snmp plugin: Destroying all data definitions.");
data_this = data_head;
data_head = NULL;
{
plugin_register_complex_config ("snmp", csnmp_config);
plugin_register_init ("snmp", csnmp_init);
- plugin_register_read ("snmp", csnmp_read);
plugin_register_shutdown ("snmp", csnmp_shutdown);
} /* void module_register */
multimeter value:GAUGE:U:U
mysql_commands value:COUNTER:0:U
mysql_handler value:COUNTER:0:U
+mysql_log_position value:COUNTER:0:4294967295
mysql_octets rx:COUNTER:0:4294967295, tx:COUNTER:0:4294967295
mysql_qcache hits:COUNTER:0:U, inserts:COUNTER:0:U, not_cached:COUNTER:0:U, lowmem_prunes:COUNTER:0:U, queries_in_cache:GAUGE:0:U
mysql_threads running:GAUGE:0:U, connected:GAUGE:0:U, cached:GAUGE:0:U, created:COUNTER:0:U
time_dispersion seconds:GAUGE:-1000000:1000000
timeleft timeleft:GAUGE:0:3600
time_offset seconds:GAUGE:-1000000:1000000
+uptime value:GAUGE:0:4294967295
users users:GAUGE:0:65535
virt_cpu_total ns:COUNTER:0:256000000000
virt_vcpu ns:COUNTER:0:1000000000
if (fh == NULL)
{
char errbuf[1024];
- ERROR ("open (%s) failed: %s",
+ fprintf (stderr, "Failed to open types database `%s': %s.\n",
+ file, sstrerror (errno, errbuf, sizeof (errbuf)));
+ ERROR ("Failed to open types database `%s': %s",
file, sstrerror (errno, errbuf, sizeof (errbuf)));
return (-1);
}
--- /dev/null
+/**
+ * collectd - src/uptime.c
+ * Copyright (C) 2009 Marco Chiappero
+ *
+ * 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:
+ * Marco Chiappero <marco at absence.it>
+ **/
+
+#include "collectd.h"
+#include "common.h"
+#include "plugin.h"
+
+#if KERNEL_LINUX
+# define UPTIME_FILE "/proc/uptime"
+/* No need for includes, using /proc filesystem, Linux only. */
+/* #endif KERNEL_LINUX */
+
+#elif HAVE_LIBKSTAT
+/* Using kstats chain to retrieve the boot time, this applies to:
+ * - Solaris / OpenSolaris
+ */
+/* #endif HAVE_LIBKSTAT */
+
+#elif HAVE_SYS_SYSCTL_H
+# include <sys/sysctl.h>
+/* Using sysctl interface to retrieve the boot time, this applies to:
+ * - *BSD
+ * - Darwin / OS X
+ */
+/* #endif HAVE_SYS_SYSCTL_H */
+
+#else
+# error "No applicable input method."
+#endif
+
+/*
+ * Global variables
+ */
+#if KERNEL_LINUX
+/* global variables not needed */
+/* #endif KERNEL_LINUX */
+
+#elif HAVE_LIBKSTAT
+static time_t boottime;
+extern kstat_ctl_t *kc;
+/* #endif HAVE_LIBKSTAT */
+
+#elif HAVE_SYS_SYSCTL_H
+static time_t boottime;
+#endif
+
+static void uptime_submit (gauge_t uptime)
+{
+ value_t values[1];
+ value_list_t vl = VALUE_LIST_INIT;
+
+ values[0].gauge = uptime;
+
+ vl.values = values;
+ vl.values_len = 1;
+
+ sstrncpy (vl.host, hostname_g, sizeof (vl.host));
+ sstrncpy (vl.plugin, "uptime", sizeof (vl.plugin));
+ sstrncpy (vl.type, "uptime", sizeof (vl.type));
+
+ plugin_dispatch_values (&vl);
+}
+
+#if !defined(KERNEL_LINUX) || !KERNEL_LINUX
+static int uptime_init (void)
+{
+/* NOTE
+
+ On unix systems other than Linux there is no /proc filesystem which
+ calculates the uptime every time we call a read for the /proc/uptime
+ file, the only information available is the boot time (in unix time,
+ since epoch). Hence there is no need to read, every time the
+ plugin_read is called, a value that won't change: this is a right
+ task for the uptime_init function. However, since uptime_init is run
+ only once, if the function fails in retrieving the boot time, the
+ plugin is unregistered and there is no chance to try again later.
+ Nevertheless, this is very unlikely to happen.
+ */
+
+# if HAVE_LIBKSTAT
+ kstat_t *ksp;
+ kstat_named_t *knp;
+
+ ksp = NULL;
+ knp = NULL;
+
+ /* kstats chain already opened by update_kstat (using *kc), let's verify everything went fine. */
+ if (kc == NULL)
+ {
+ ERROR ("uptime plugin: kstat chain control structure not available.");
+ return (-1);
+ }
+
+ ksp = kstat_lookup (kc, "unix", 0, "system_misc");
+ if (ksp == NULL)
+ {
+ ERROR ("uptime plugin: Cannot find unix:0:system_misc kstat.");
+ return (-1);
+ }
+
+ if (kstat_read (kc, ksp, NULL) < 0)
+ {
+ ERROR ("uptime plugin: kstat_read failed.");
+ return (-1);
+ }
+
+ knp = (kstat_named_t *) kstat_data_lookup (ksp, "boot_time");
+ if (knp == NULL)
+ {
+ ERROR ("uptime plugin: kstat_data_lookup (boot_time) failed.");
+ return (-1);
+ }
+
+ boottime = (time_t) knp->value.ui32;
+/* #endif HAVE_LIBKSTAT */
+
+# elif HAVE_SYS_SYSCTL_H
+ struct timeval boottv;
+ size_t boottv_len;
+ int status;
+
+ int mib[2];
+
+ mib[0] = CTL_KERN;
+ mib[1] = KERN_BOOTTIME;
+
+ memset (&boottv, 0, sizeof (boottv));
+ boottv_len = sizeof (boottv);
+
+ status = sysctl (mib, STATIC_ARRAY_SIZE (mib), &boottv, &boottv_len,
+ /* new_value = */ NULL, /* new_length = */ 0);
+ if (status != 0)
+ {
+ char errbuf[1024];
+ ERROR ("uptime plugin: No value read from sysctl interface: %s",
+ sstrerror (errno, errbuf, sizeof (errbuf)));
+ return (-1);
+ }
+
+ boottime = boottv.tv_sec;
+ if (boottime == 0)
+ {
+ ERROR ("uptime plugin: sysctl(3) returned success, "
+ "but `boottime' is zero!");
+ return (-1);
+ }
+#endif /* HAVE_SYS_SYSCTL_H */
+
+ return (0);
+
+}
+#endif /* !KERNEL_LINUX */
+
+static int uptime_read (void)
+{
+ gauge_t uptime;
+
+#if KERNEL_LINUX
+ FILE *fh;
+
+ fh = fopen (UPTIME_FILE, "r");
+
+ if (fh == NULL)
+ {
+ char errbuf[1024];
+ ERROR ("uptime plugin: Cannot open "UPTIME_FILE": %s",
+ sstrerror (errno, errbuf, sizeof (errbuf)));
+ return (-1);
+ }
+
+ if ( fscanf (fh, "%lf", &uptime) < 1 )
+ {
+ WARNING ("uptime plugin: No value read from "UPTIME_FILE);
+ fclose (fh);
+ return (-1);
+ }
+
+ fclose (fh);
+/* #endif KERNEL_LINUX */
+
+#elif HAVE_LIBKSTAT || HAVE_SYS_SYSCTL_H
+ time_t elapsed;
+
+ elapsed = time (NULL) - boottime;
+
+ uptime = (gauge_t) elapsed;
+#endif /* HAVE_LIBKSTAT || HAVE_SYS_SYSCTL_H */
+
+ uptime_submit (uptime);
+
+ return (0);
+}
+
+void module_register (void)
+{
+#if !defined(KERNEL_LINUX) || !KERNEL_LINUX
+ plugin_register_init ("uptime", uptime_init);
+#endif
+ plugin_register_read ("uptime", uptime_read);
+} /* void module_register */
if (FORMAT_VL (name, sizeof (name), vl, ds) != 0)
{
- ERROR ("uc_insert: FORMAT_VL failed.");
+ ERROR ("utils_cache: uc_get_rate: FORMAT_VL failed.");
return (NULL);
}
/**
* collectd - src/utils_cms_putval.c
- * Copyright (C) 2007,2008 Florian octo Forster
+ * Copyright (C) 2007-2009 Florian octo Forster
*
* 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
vl->values[i].gauge = NAN;
else if (0 != parse_value (ptr, &vl->values[i], ds->ds[i]))
{
- print_to_socket (fh, "-1 Failed to parse value `%s'.", ptr);
+ print_to_socket (fh, "-1 Failed to parse value `%s'.\n", ptr);
return (-1);
}
--- /dev/null
+/**
+ * collectd - src/utils_heap.c
+ * Copyright (C) 2009 Florian octo Forster
+ *
+ * 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; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ * 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:
+ * Florian octo Forster <octo at verplant.org>
+ **/
+
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <assert.h>
+#include <pthread.h>
+
+#include "utils_heap.h"
+
+struct c_heap_s
+{
+ pthread_mutex_t lock;
+ int (*compare) (const void *, const void *);
+
+ void **list;
+ size_t list_len; /* # entries used */
+ size_t list_size; /* # entries allocated */
+};
+
+enum reheap_direction
+{
+ DIR_UP,
+ DIR_DOWN
+};
+
+static void reheap (c_heap_t *h, size_t root, enum reheap_direction dir)
+{
+ size_t left;
+ size_t right;
+ size_t min;
+ int status;
+
+ /* Calculate the positions of the children */
+ left = (2 * root) + 1;
+ if (left >= h->list_len)
+ left = 0;
+
+ right = (2 * root) + 2;
+ if (right >= h->list_len)
+ right = 0;
+
+ /* Check which one of the children is smaller. */
+ if ((left == 0) && (right == 0))
+ return;
+ else if (left == 0)
+ min = right;
+ else if (right == 0)
+ min = left;
+ else
+ {
+ status = h->compare (h->list[left], h->list[right]);
+ if (status > 0)
+ min = right;
+ else
+ min = left;
+ }
+
+ status = h->compare (h->list[root], h->list[min]);
+ if (status <= 0)
+ {
+ /* We didn't need to change anything, so the rest of the tree should be
+ * okay now. */
+ return;
+ }
+ else /* if (status > 0) */
+ {
+ void *tmp;
+
+ tmp = h->list[root];
+ h->list[root] = h->list[min];
+ h->list[min] = tmp;
+ }
+
+ if ((dir == DIR_UP) && (root == 0))
+ return;
+
+ if (dir == DIR_UP)
+ reheap (h, root / 2, dir);
+ else if (dir == DIR_DOWN)
+ reheap (h, min, dir);
+} /* void reheap */
+
+c_heap_t *c_heap_create (int (*compare) (const void *, const void *))
+{
+ c_heap_t *h;
+
+ if (compare == NULL)
+ return (NULL);
+
+ h = malloc (sizeof (*h));
+ if (h == NULL)
+ return (NULL);
+
+ memset (h, 0, sizeof (*h));
+ pthread_mutex_init (&h->lock, /* attr = */ NULL);
+ h->compare = compare;
+
+ h->list = NULL;
+ h->list_len = 0;
+ h->list_size = 0;
+
+ return (h);
+} /* c_heap_t *c_heap_create */
+
+void c_heap_destroy (c_heap_t *h)
+{
+ if (h == NULL)
+ return;
+
+ h->list_len = 0;
+ h->list_size = 0;
+ free (h->list);
+ h->list = NULL;
+
+ pthread_mutex_destroy (&h->lock);
+
+ free (h);
+} /* void c_heap_destroy */
+
+int c_heap_insert (c_heap_t *h, void *ptr)
+{
+ if ((h == NULL) || (ptr == NULL))
+ return (-EINVAL);
+
+ pthread_mutex_lock (&h->lock);
+
+ assert (h->list_len <= h->list_size);
+ if (h->list_len == h->list_size)
+ {
+ void **tmp;
+
+ tmp = realloc (h->list, (h->list_size + 16) * sizeof (*h->list));
+ if (tmp == NULL)
+ {
+ pthread_mutex_unlock (&h->lock);
+ return (-ENOMEM);
+ }
+
+ h->list = tmp;
+ h->list_size += 16;
+ }
+
+ /* Insert the new node as a leaf. */
+ h->list[h->list_len] = ptr;
+ h->list_len++;
+
+ /* Reorganize the heap from bottom up. */
+ reheap (h, /* parent of this node = */ (h->list_len - 1) / 2, DIR_UP);
+
+ pthread_mutex_unlock (&h->lock);
+ return (0);
+} /* int c_heap_insert */
+
+void *c_head_get_root (c_heap_t *h)
+{
+ void *ret = NULL;
+
+ if (h == NULL)
+ return (NULL);
+
+ pthread_mutex_lock (&h->lock);
+
+ if (h->list_len == 0)
+ {
+ pthread_mutex_unlock (&h->lock);
+ return (NULL);
+ }
+ else if (h->list_len == 1)
+ {
+ ret = h->list[0];
+ h->list[0] = NULL;
+ h->list_len = 0;
+ }
+ else /* if (h->list_len > 1) */
+ {
+ ret = h->list[0];
+ h->list[0] = h->list[h->list_len - 1];
+ h->list[h->list_len - 1] = NULL;
+ h->list_len--;
+
+ reheap (h, /* root = */ 0, DIR_DOWN);
+ }
+
+ /* free some memory */
+ if ((h->list_len + 32) < h->list_size)
+ {
+ void **tmp;
+
+ tmp = realloc (h->list, (h->list_len + 16) * sizeof (*h->list));
+ if (tmp != NULL)
+ {
+ h->list = tmp;
+ h->list_size = h->list_len + 16;
+ }
+ }
+
+ pthread_mutex_unlock (&h->lock);
+
+ return (ret);
+} /* void *c_head_get_root */
+
+/* vim: set sw=2 sts=2 et fdm=marker : */
--- /dev/null
+/**
+ * collectd - src/utils_heap.h
+ * Copyright (C) 2009 Florian octo Forster
+ *
+ * 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; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ * 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:
+ * Florian octo Forster <octo at verplant.org>
+ **/
+
+#ifndef UTILS_HEAP_H
+#define UTILS_HEAP_H 1
+
+struct c_heap_s;
+typedef struct c_heap_s c_heap_t;
+
+/*
+ * NAME
+ * c_heap_create
+ *
+ * DESCRIPTION
+ * Allocates a new heap.
+ *
+ * PARAMETERS
+ * `compare' The function-pointer `compare' is used to compare two keys. It
+ * has to return less than zero if it's first argument is smaller
+ * then the second argument, more than zero if the first argument
+ * is bigger than the second argument and zero if they are equal.
+ * If your keys are char-pointers, you can use the `strcmp'
+ * function from the libc here.
+ *
+ * RETURN VALUE
+ * A c_heap_t-pointer upon success or NULL upon failure.
+ */
+c_heap_t *c_heap_create (int (*compare) (const void *, const void *));
+
+/*
+ * NAME
+ * c_heap_destroy
+ *
+ * DESCRIPTION
+ * Deallocates a heap. Stored value- and key-pointer are lost, but of course
+ * not freed.
+ */
+void c_heap_destroy (c_heap_t *h);
+
+/*
+ * NAME
+ * c_heap_insert
+ *
+ * DESCRIPTION
+ * Stores the key-value-pair in the heap pointed to by `h'.
+ *
+ * PARAMETERS
+ * `h' Heap to store the data in.
+ * `ptr' Value to be stored. This is typically a pointer to a data
+ * structure. The data structure is of course *not* copied and may
+ * not be free'd before the pointer has been removed from the heap
+ * again.
+ *
+ * RETURN VALUE
+ * Zero upon success, non-zero otherwise. It's less than zero if an error
+ * occurred or greater than zero if the key is already stored in the tree.
+ */
+int c_heap_insert (c_heap_t *h, void *ptr);
+
+/*
+ * NAME
+ * c_head_get_root
+ *
+ * DESCRIPTION
+ * Removes the value at the root of the heap and returns both, key and value.
+ *
+ * PARAMETERS
+ * `h' Heap to remove key-value-pair from.
+ *
+ * RETURN VALUE
+ * The pointer passed to `c_heap_insert' or NULL if there are no more
+ * elements in the heap (or an error occurred).
+ */
+void *c_head_get_root (c_heap_t *h);
+
+#endif /* UTILS_HEAP_H */
+/* vim: set sw=2 sts=2 et : */
#!/bin/sh
-DEFAULT_VERSION="4.6.1.git"
+DEFAULT_VERSION="4.6.2.git"
VERSION="$( git describe 2> /dev/null | sed -e 's/^collectd-//' )"