write your own plugins in Perl and return arbitrary values using this
API. See collectd-perl(5).
- This plugin is still considered to be experimental and subject to change
- between minor releases.
-
- ping
Network latency: Time to reach the default gateway or another given
host.
use strict;
use warnings;
+use Collectd qw( :all );
+
# data set definition:
# see section "DATA TYPES" in collectd-perl(5) for details
+# (take a look at the types.db file for a large list of predefined data-sets)
my $dataset =
[
{
name => 'my_ds',
- type => Collectd::DS_TYPE_GAUGE,
+ type => DS_TYPE_GAUGE,
min => 0,
max => 65535,
},
];
# This code is executed after loading the plugin to register it with collectd.
-Collectd::plugin_register (Collectd::TYPE_LOG, 'myplugin', \&my_log);
-Collectd::plugin_register (Collectd::TYPE_DATASET, 'myplugin', $dataset);
-Collectd::plugin_register (Collectd::TYPE_INIT, 'myplugin', \&my_init);
-Collectd::plugin_register (Collectd::TYPE_READ, 'myplugin', \&my_read);
-Collectd::plugin_register (Collectd::TYPE_WRITE, 'myplugin', \&my_write);
-Collectd::plugin_register (Collectd::TYPE_SHUTDOWN, 'myplugin', \&my_shutdown);
+plugin_register (TYPE_LOG, 'myplugin', 'my_log');
+plugin_register (TYPE_DATASET, 'myplugin', $dataset);
+plugin_register (TYPE_INIT, 'myplugin', 'my_init');
+plugin_register (TYPE_READ, 'myplugin', 'my_read');
+plugin_register (TYPE_WRITE, 'myplugin', 'my_write');
+plugin_register (TYPE_SHUTDOWN, 'myplugin', 'my_shutdown');
# For each of the functions below see collectd-perl(5) for details about
# arguments and the like.
# dispatch the values to collectd which passes them on to all registered
# write functions - the first argument is used to lookup the data set
# definition
- Collectd::plugin_dispatch_values ('myplugin', $vl);
+ plugin_dispatch_values ('myplugin', $vl);
# A false return value indicates an error and the plugin will be skipped
# for an increasing amount of time.
my $vl = shift;
if (scalar (@$ds) != scalar (@{$vl->{'values'}})) {
- Collectd::plugin_log (Collectd::LOG_WARNING,
+ plugin_log (LOG_WARNING,
"DS number does not match values length");
return;
}
endif
AM_CPPFLAGS += -DPLUGINDIR='"${pkglibdir}"'
-sbin_PROGRAMS = collectd
+sbin_PROGRAMS = collectd collectdmon
bin_PROGRAMS = collectd-nagios
collectd_SOURCES = collectd.c collectd.h \
collectd_LDFLAGS += -loconfig
endif
+collectdmon_SOURCES = collectdmon.c
+collectdmon_CPPFLAGS = $(AM_CPPFLAGS)
+
collectd_nagios_SOURCES = collectd-nagios.c
collectd_nagios_LDFLAGS =
if BUILD_WITH_LIBSOCKET
dist_man_MANS = collectd.1 collectd-nagios.1 collectd.conf.5 \
collectd-email.5 collectd-exec.5 collectd-perl.5 \
- collectd-snmp.5 collectd-unixsock.5
+ collectd-snmp.5 collectd-unixsock.5 collectdmon.1
#collectd_1_SOURCES = collectd.pod
EXTRA_DIST += collectd-email.pod collectd-exec.pod collectd-nagios.pod \
collectd-perl.pod collectd-snmp.pod collectd-unixsock.pod \
- collectd.conf.pod collectd.pod
+ collectd.conf.pod collectd.pod collectdmon.pod
.pod.1:
pod2man --release=$(VERSION) --center=$(PACKAGE) $< >.pod2man.tmp 2>/dev/null && mv -f .pod2man.tmp $@ || true
Perl-script every time you want to read a value with the C<exec plugin> (see
L<collectd-exec(5)>) and provides a lot more functionality, too.
-Please note that this is still considered to be experimental and subject to
-change between minor releases.
-
=head1 CONFIGURATION
=over 4
See the section "DATA TYPES" above for a complete documentation of the data
types used by the read and write functions.
-=head1 BUGS
+=head1 CAVEATS
+
+=over 4
+
+=item
+
+collectd is heavily multi-threaded. Each collectd thread accessing the perl
+plugin will be mapped to a Perl interpreter thread (see L<threads(3perl)>).
+Any such thread will be created and destroyed transparently and on-the-fly.
+
+Hence, any plugin has to be thread-safe if it provides several entry points
+from collectd (i.E<nbsp>e. if it registers more than one callback). Please
+note that no data is shared between threads by default. You have to use the
+B<threads::shared> module to do so.
-This plugin does not yet work correctly if collectd uses multiple threads.
-Perl does not allow multiple threads to access a single interpreter at the
-same time. As a temporary workaround you should use a single read thread only
-(see collectd's B<ReadThread> configuration option).
+=item
+
+Each function name registered with collectd has to be available before the
+first thread has been created (i.E<nbsp>e. basically at compile time). This
+basically means that hacks (yes, I really consider this to be a hack) like
+C<*foo = \&bar; plugin_register (TYPE_READ, "plugin", "foo");> most likely
+will not work. This is due to the fact that the symbol table is not shared
+across different threads.
+
+=item
+
+Each plugin is usually only loaded once and kept in memory for performance
+reasons. Therefore, END blocks are only executed once when collectd shuts
+down. You should not rely on END blocks anyway - use B<shutdown functions>
+instead.
+
+=back
=head1 SEE ALSO
L<collectd.conf(5)>,
L<collectd-exec(5)>,
L<perl(1)>,
+L<threads(3perl)>,
+L<threads::shared(3perl)>,
L<perldebug(1)>
=head1 AUTHOR
#<Plugin ntpd>
# Host "localhost"
# Port 123
+# ReverseLookups false
#</Plugin>
#<Plugin nut>
UDP-Port to connect to. Defaults to B<123>.
+=item B<ReverseLookups> B<true>|B<false>
+
+Sets wether or not to perform reverse lookups on peers. Since the name or
+IP-address may be used in a filename it is recommended to disable reverse
+lookups. The default is to do reverse lookups to preserve backwards
+compatibility, though.
+
=back
=head2 Plugin C<nut>
--- /dev/null
+/**
+ * collectd - src/collectdmon.c
+ * Copyright (C) 2007 Sebastian Harl
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * 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
+ *
+ * Author:
+ * Sebastian Harl <sh at tokkee.org>
+ **/
+
+#include "config.h"
+
+#include <assert.h>
+
+#include <errno.h>
+
+#include <fcntl.h>
+
+#include <signal.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+
+#include <string.h>
+
+#include <syslog.h>
+
+#include <sys/resource.h>
+#include <sys/time.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/wait.h>
+
+#include <time.h>
+
+#include <unistd.h>
+
+#ifndef COLLECTDMON_PIDFILE
+# define COLLECTDMON_PIDFILE LOCALSTATEDIR"/run/collectdmon.pid"
+#endif /* ! COLLECTDMON_PIDFILE */
+
+#ifndef WCOREDUMP
+# define WCOREDUMP(s) 0
+#endif /* ! WCOREDUMP */
+
+static int loop = 0;
+static int restart = 0;
+
+static char *pidfile = NULL;
+static pid_t collectd_pid = 0;
+
+static void exit_usage (char *name)
+{
+ printf ("Usage: %s <options> [-- <collectd options>]\n"
+
+ "\nAvailable options:\n"
+ " -h Display this help and exit.\n"
+ " -c <path> Path to the collectd binary.\n"
+ " -P <file> PID-file.\n"
+
+ "\nFor <collectd options> see collectd.conf(5).\n"
+
+ "\n"PACKAGE" "VERSION", http://collectd.org/\n"
+ "by Florian octo Forster <octo@verplant.org>\n"
+ "for contributions see `AUTHORS'\n", name);
+ exit (0);
+} /* exit_usage */
+
+static int pidfile_create (void)
+{
+ FILE *file = NULL;
+
+ if (NULL == pidfile)
+ pidfile = COLLECTDMON_PIDFILE;
+
+ if (NULL == (file = fopen (pidfile, "w"))) {
+ syslog (LOG_ERR, "Error: couldn't open PID-file (%s) for writing: %s",
+ pidfile, strerror (errno));
+ return -1;
+ }
+
+ fprintf (file, "%i\n", (int)getpid ());
+ fclose (file);
+ return 0;
+} /* pidfile_create */
+
+static int pidfile_delete (void)
+{
+ assert (NULL != pidfile);
+
+ if (0 != unlink (pidfile)) {
+ syslog (LOG_ERR, "Error: couldn't delete PID-file (%s): %s",
+ pidfile, strerror (errno));
+ return -1;
+ }
+ return 0;
+} /* pidfile_remove */
+
+static int daemonize (void)
+{
+ struct rlimit rl;
+
+ pid_t pid = 0;
+ int i = 0;
+
+ if (0 != chdir ("/")) {
+ fprintf (stderr, "Error: chdir() failed: %s\n", strerror (errno));
+ return -1;
+ }
+
+ if (0 != getrlimit (RLIMIT_NOFILE, &rl)) {
+ fprintf (stderr, "Error: getrlimit() failed: %s\n", strerror (errno));
+ return -1;
+ }
+
+ if (0 > (pid = fork ())) {
+ fprintf (stderr, "Error: fork() failed: %s\n", strerror (errno));
+ return -1;
+ }
+ else if (pid != 0) {
+ exit (0);
+ }
+
+ if (0 != pidfile_create ())
+ return -1;
+
+ setsid ();
+
+ if (RLIM_INFINITY == rl.rlim_max)
+ rl.rlim_max = 1024;
+
+ for (i = 0; i < rl.rlim_max; ++i)
+ close (i);
+
+ errno = 0;
+ if (open ("/dev/null", O_RDWR) != 0) {
+ syslog (LOG_ERR, "Error: couldn't connect STDIN to /dev/null: %s",
+ strerror (errno));
+ return -1;
+ }
+
+ errno = 0;
+ if (dup (0) != 1) {
+ syslog (LOG_ERR, "Error: couldn't connect STDOUT to /dev/null: %s",
+ strerror (errno));
+ return -1;
+ }
+
+ errno = 0;
+ if (dup (0) != 2) {
+ syslog (LOG_ERR, "Error: couldn't connect STDERR to /dev/null: %s",
+ strerror (errno));
+ return -1;
+ }
+ return 0;
+} /* daemonize */
+
+static int collectd_start (int argc, char **argv)
+{
+ pid_t pid = 0;
+
+ if (0 > (pid = fork ())) {
+ syslog (LOG_ERR, "Error: fork() failed: %s", strerror (errno));
+ return -1;
+ }
+ else if (pid != 0) {
+ collectd_pid = pid;
+ return 0;
+ }
+
+ execvp (argv[0], argv);
+ syslog (LOG_ERR, "Error: execvp(%s) failed: %s",
+ argv[0], strerror (errno));
+ exit (-1);
+} /* collectd_start */
+
+static int collectd_stop (void)
+{
+ if (0 == collectd_pid)
+ return 0;
+
+ if (0 != kill (collectd_pid, SIGTERM)) {
+ syslog (LOG_ERR, "Error: kill() failed: %s", strerror (errno));
+ return -1;
+ }
+ return 0;
+} /* collectd_stop */
+
+static void sig_int_term_handler (int signo)
+{
+ ++loop;
+ return;
+} /* sig_int_term_handler */
+
+static void sig_hup_handler (int signo)
+{
+ ++restart;
+ return;
+} /* sig_hup_handler */
+
+static void log_status (int status)
+{
+ if (WIFEXITED (status)) {
+ if (0 == WEXITSTATUS (status))
+ syslog (LOG_INFO, "Info: collectd terminated with exit status %i",
+ WEXITSTATUS (status));
+ else
+ syslog (LOG_WARNING,
+ "Warning: collectd terminated with exit status %i",
+ WEXITSTATUS (status));
+ }
+ else if (WIFSIGNALED (status)) {
+ syslog (LOG_WARNING, "Warning: collectd was terminated by signal %i%s",
+ WTERMSIG (status), WCOREDUMP (status) ? " (core dumped)" : "");
+ }
+ return;
+} /* log_status */
+
+static void check_respawn (void)
+{
+ time_t t = time (NULL);
+
+ static time_t timestamp = 0;
+ static int counter = 0;
+
+ if ((t - 120) < timestamp)
+ ++counter;
+ else {
+ timestamp = t;
+ counter = 0;
+ }
+
+ if (10 < counter) {
+ unsigned int time_left = 300;
+
+ syslog (LOG_ERR, "Error: collectd is respawning too fast - "
+ "disabled for %i seconds", time_left);
+
+ while ((0 < (time_left = sleep (time_left))) && (0 == loop));
+ }
+ return;
+} /* check_respawn */
+
+int main (int argc, char **argv)
+{
+ int collectd_argc = 0;
+ char *collectd = NULL;
+ char **collectd_argv = NULL;
+
+ struct sigaction sa;
+
+ int i = 0;
+
+ /* parse command line options */
+ while (42) {
+ int c = getopt (argc, argv, "hc:P:");
+
+ if (-1 == c)
+ break;
+
+ switch (c) {
+ case 'c':
+ collectd = optarg;
+ break;
+ case 'P':
+ pidfile = optarg;
+ break;
+ case 'h':
+ default:
+ exit_usage (argv[0]);
+ }
+ }
+
+ for (i = optind; i < argc; ++i)
+ if (0 == strcmp (argv[i], "-f"))
+ break;
+
+ /* i < argc => -f already present */
+ collectd_argc = 1 + argc - optind + ((i < argc) ? 0 : 1);
+ collectd_argv = (char **)calloc (collectd_argc + 1, sizeof (char *));
+
+ if (NULL == collectd_argv) {
+ fprintf (stderr, "Out of memory.");
+ return 3;
+ }
+
+ collectd_argv[0] = (NULL == collectd) ? "collectd" : collectd;
+
+ if (i == argc)
+ collectd_argv[collectd_argc - 1] = "-f";
+
+ for (i = optind; i < argc; ++i)
+ collectd_argv[i - optind + 1] = argv[i];
+
+ collectd_argv[collectd_argc] = NULL;
+
+ openlog ("collectdmon", LOG_CONS | LOG_PID, LOG_DAEMON);
+
+ if (-1 == daemonize ())
+ return 1;
+
+ sa.sa_handler = sig_int_term_handler;
+ sa.sa_flags = 0;
+ sigemptyset (&sa.sa_mask);
+
+ if (0 != sigaction (SIGINT, &sa, NULL)) {
+ syslog (LOG_ERR, "Error: sigaction() failed: %s", strerror (errno));
+ return 1;
+ }
+
+ if (0 != sigaction (SIGTERM, &sa, NULL)) {
+ syslog (LOG_ERR, "Error: sigaction() failed: %s", strerror (errno));
+ return 1;
+ }
+
+ sa.sa_handler = sig_hup_handler;
+
+ if (0 != sigaction (SIGHUP, &sa, NULL)) {
+ syslog (LOG_ERR, "Error: sigaction() failed: %s", strerror (errno));
+ return 1;
+ }
+
+ sigaddset (&sa.sa_mask, SIGCHLD);
+ if (0 != sigprocmask (SIG_BLOCK, &sa.sa_mask, NULL)) {
+ syslog (LOG_ERR, "Error: sigprocmask() failed: %s", strerror (errno));
+ return 1;
+ }
+
+ while (0 == loop) {
+ int status = 0;
+
+ if (0 != collectd_start (collectd_argc, collectd_argv)) {
+ syslog (LOG_ERR, "Error: failed to start collectd.");
+ break;
+ }
+
+ assert (0 < collectd_pid);
+ while ((collectd_pid != waitpid (collectd_pid, &status, 0))
+ && (EINTR == errno))
+ if ((0 != loop) || (0 != restart))
+ collectd_stop ();
+
+ collectd_pid = 0;
+
+ log_status (status);
+ check_respawn ();
+
+ if (0 != restart) {
+ syslog (LOG_INFO, "Info: restarting collectd");
+ restart = 0;
+ }
+ else if (0 == loop)
+ syslog (LOG_WARNING, "Warning: restarting collectd");
+ }
+
+ syslog (LOG_INFO, "Info: shutting down collectdmon");
+
+ pidfile_delete ();
+ closelog ();
+
+ free (collectd_argv);
+ return 0;
+} /* main */
+
+/* vim: set sw=4 ts=4 tw=78 noexpandtab : */
+
--- /dev/null
+=head1 NAME
+
+collectdmon - Monitoring daemon for collectd
+
+=head1 SYNOPSIS
+
+collectdmon I<[options]> [-- I<collectd options>]
+
+=head1 DESCRIPTION
+
+collectdmon is a small "wrapper" daemon which starts and monitors the collectd
+daemon. If collectd terminates it will automatically be restarted, unless
+collectdmon was told to shut it down.
+
+=head1 OPTIONS
+
+collectdmon supports the following options:
+
+=over 4
+
+=item B<-c> I<E<lt>pathE<gt>>
+
+Specify the pathname of the collectd binary. You may either specify an
+absolute path or simply the name of the binary in which case the B<PATH>
+variable will be searched for it. The default is "B<collectd>".
+
+=item B<-P> I<E<lt>pid-fileE<gt>>
+
+Specify the pid file. The default is "I</var/run/collectdmon.pid>".
+
+=item B<-h>
+
+Output usage information and exit.
+
+=item I<collectd options>
+
+Specify options that are passed on to collectd. If it is not already included,
+B<-f> will be added to these options. See L<collectd(1)>.
+
+=back
+
+=head1 SEE ALSO
+
+L<collectd(1)>,
+L<collectd.conf(5)>,
+L<http://collectd.org/>
+
+=head1 AUTHOR
+
+collectd has been written by Florian Forster E<lt>octo at verplant.orgE<gt>
+and many contributors (see `AUTHORS').
+
+collectdmon has been written by Sebastian Harl E<lt>sh@tokkee.orgE<gt>.
+
+=cut
{
"Host",
"Port",
- NULL
+ "ReverseLookups"
};
-static int config_keys_num = 2;
+static int config_keys_num = STATIC_ARRAY_SIZE (config_keys);
+
+static int do_reverse_lookups = 1;
# define NTPD_DEFAULT_HOST "localhost"
# define NTPD_DEFAULT_PORT "123"
"CHRONOLOG", "DUMBCLOCK", "ULINK_M320", "PCF", /* 32-35 */
"WWV_AUDIO", "GPS_FG", "HOPF_S", "HOPF_P", /* 36-39 */
"JJY", "TT_IRIG", "GPS_ZYFER", "GPS_RIPENCC", /* 40-43 */
- "NEOCLK4X", NULL /* 44 */
+ "NEOCLK4X" /* 44 */
};
-static int refclock_names_num = 45;
+static int refclock_names_num = STATIC_ARRAY_SIZE (refclock_names);
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
* End of the copied stuff.. *
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
strncpy (ntpd_port, value, sizeof (ntpd_port));
ntpd_port[sizeof (ntpd_port) - 1] = '\0';
}
+ else if (strcasecmp (key, "ReverseLookups") == 0)
+ {
+ if ((strcasecmp (value, "True") == 0)
+ || (strcasecmp (value, "Yes") == 0)
+ || (strcasecmp (value, "On") == 0))
+ do_reverse_lookups = 1;
+ else
+ do_reverse_lookups = 0;
+ }
else
{
return (-1);
ptr = ps + i;
refclock_id = 0;
- /*
- if (((ntohl (ptr->dstadr) & 0xFFFFFF00) == 0x7F000000) || (ptr->dstadr == 0))
- continue;
- */
-
/* Convert the `long floating point' offset value to double */
M_LFPTOD (ntohl (ptr->offset_int), ntohl (ptr->offset_frc), offset);
- if (ptr->v6_flag)
- {
- struct sockaddr_in6 sa;
-
- memset (&sa, 0, sizeof (sa));
- sa.sin6_family = AF_INET6;
- sa.sin6_port = htons (123);
- memcpy (&sa.sin6_addr, &ptr->srcadr6, sizeof (struct in6_addr));
-
- status = getnameinfo ((const struct sockaddr *) &sa,
- sizeof (sa),
- peername, sizeof (peername),
- NULL, 0, 0 /* no flags */);
- if (status != 0)
- {
- char errbuf[1024];
- ERROR ("ntpd plugin: getnameinfo failed: %s",
- (status == EAI_SYSTEM)
- ? sstrerror (errno, errbuf, sizeof (errbuf))
- : gai_strerror (status));
- continue;
- }
- }
- else if ((ntohl (ptr->srcadr) & REFCLOCK_MASK) == REFCLOCK_ADDR)
+ /* Special IP addresses for hardware clocks and stuff.. */
+ if (!ptr->v6_flag
+ && ((ntohl (ptr->srcadr) & REFCLOCK_MASK)
+ == REFCLOCK_ADDR))
{
struct in_addr addr_obj;
char *addr_str;
strncpy (peername, addr_str, sizeof (peername));
}
}
- else /* IPv4 */
+ else /* Normal network host. */
{
- struct in_addr addr_obj;
- struct hostent *addr_he;
- char *addr_str;
+ struct sockaddr_storage sa;
+ socklen_t sa_len;
+ int flags = 0;
- memset ((void *) &addr_obj, '\0', sizeof (addr_obj));
- addr_obj.s_addr = ptr->srcadr;
- addr_str = inet_ntoa (addr_obj);
+ memset (&sa, '\0', sizeof (sa));
- addr_he = gethostbyaddr ((const void *) &addr_obj,
- sizeof (addr_obj), AF_INET);
- if (addr_he != NULL)
+ if (ptr->v6_flag)
{
- strncpy (peername, addr_he->h_name, sizeof (peername));
+ struct sockaddr_in6 *sa_ptr;
+ sa_ptr = (struct sockaddr_in6 *) &sa;
+
+ sa_ptr->sin6_family = AF_INET6;
+ sa_ptr->sin6_port = htons (123);
+ memcpy (&sa_ptr->sin6_addr, &ptr->srcadr6,
+ sizeof (struct in6_addr));
+ sa_len = sizeof (struct sockaddr_in6);
}
else
{
- strncpy (peername, addr_str, sizeof (peername));
+ struct sockaddr_in *sa_ptr;
+ sa_ptr = (struct sockaddr_in *) &sa;
+
+ sa_ptr->sin_family = AF_INET;
+ sa_ptr->sin_port = htons (123);
+ memcpy (&sa_ptr->sin_addr, &ptr->srcadr,
+ sizeof (struct in_addr));
+ sa_len = sizeof (struct sockaddr_in);
+ }
+
+ if (do_reverse_lookups == 0)
+ flags |= NI_NUMERICHOST;
+
+ status = getnameinfo ((const struct sockaddr *) &sa,
+ sa_len,
+ peername, sizeof (peername),
+ NULL, 0, /* No port name */
+ flags);
+ if (status != 0)
+ {
+ char errbuf[1024];
+ ERROR ("ntpd plugin: getnameinfo failed: %s",
+ (status == EAI_SYSTEM)
+ ? sstrerror (errno, errbuf, sizeof (errbuf))
+ : gai_strerror (status));
+ continue;
}
}
static int pplugin_register_data_set (pTHX_ char *name, AV *dataset)
{
int len = -1;
+ int ret = 0;
int i = 0;
data_source_t *ds = NULL;
set->ds_num = len + 1;
set->ds = ds;
- return plugin_register_data_set (set);
+
+ ret = plugin_register_data_set (set);
+
+ free (ds);
+ free (set);
+ return ret;
} /* static int pplugin_register_data_set (char *, SV *) */
/*