From: Florian Forster Date: Wed, 31 Jan 2007 12:48:21 +0000 (+0100) Subject: Merge branch 'pull/collectd-4' X-Git-Tag: collectd-4.0.0~211 X-Git-Url: https://git.octo.it/?a=commitdiff_plain;h=a3fe18a43186fd295346d023f1ef174566f5708f;hp=b18ade442090e2041ce75279c5859b5bd74b1791;p=collectd.git Merge branch 'pull/collectd-4' --- diff --git a/configure.in b/configure.in index 0b0aa890..23f0e59e 100644 --- a/configure.in +++ b/configure.in @@ -1005,6 +1005,7 @@ AC_COLLECTD([serial], [disable], [module], [serial statistics]) AC_COLLECTD([swap], [disable], [module], [swap statistics]) AC_COLLECTD([tape], [disable], [module], [tape statistics]) AC_COLLECTD([traffic], [disable], [module], [system traffic statistics]) +AC_COLLECTD([unixsock], [disable], [module], [UNIX socket plugin]) AC_COLLECTD([users], [disable], [module], [user count statistics]) AC_COLLECTD([vserver], [disable], [module], [vserver statistics]) AC_COLLECTD([wireless], [disable], [module], [wireless link statistics]) @@ -1061,6 +1062,7 @@ Configuration: swap . . . . . . . $enable_swap tape . . . . . . . $enable_tape traffic . . . . . . $enable_traffic + unixsock . . . . . $enable_unixsock users . . . . . . . $enable_users vserver . . . . . . $enable_vserver wireless . . . . . $enable_wireless diff --git a/src/Makefile.am b/src/Makefile.am index 87dcec07..1efdb1ab 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -8,6 +8,7 @@ AM_CFLAGS = -Wall -Werror endif sbin_PROGRAMS = collectd +bin_PROGRAMS = collectd-nagios collectd_SOURCES = collectd.c collectd.h \ utils_debug.c utils_debug.h \ @@ -18,6 +19,7 @@ collectd_SOURCES = collectd.c collectd.h \ plugin.c plugin.h \ configfile.c configfile.h collectd_CPPFLAGS = $(LTDLINCL) +collectd_CPPFLAGS += -DPREFIX='"${prefix}"' collectd_CPPFLAGS += -DCONFIGFILE='"${sysconfdir}/${PACKAGE_NAME}.conf"' collectd_CPPFLAGS += -DPKGLOCALSTATEDIR='"${localstatedir}/lib/${PACKAGE_NAME}"' if BUILD_FEATURE_DAEMON @@ -60,6 +62,8 @@ endif collectd_LDADD = $(LIBLTDL) libconfig/libconfig.la "-dlopen" self collectd_DEPENDENCIES = $(LIBLTDL) libconfig/libconfig.la +collectd_nagios_SOURCES = collectd-nagios.c + pkglib_LTLIBRARIES = if BUILD_MODULE_APACHE @@ -430,6 +434,17 @@ traffic_la_LDFLAGS += -lstatgrab endif endif +if BUILD_MODULE_UNIXSOCK +pkglib_LTLIBRARIES += unixsock.la +unixsock_la_SOURCES = unixsock.c +unixsock_la_LDFLAGS = -module -avoid-version +if BUILD_WITH_LIBPTHREAD +unixsock_la_LDFLAGS += -lpthread +endif +collectd_LDADD += "-dlopen" unixsock.la +collectd_DEPENDENCIES += unixsock.la +endif + if BUILD_MODULE_USERS pkglib_LTLIBRARIES += users.la users_la_SOURCES = users.c diff --git a/src/battery.c b/src/battery.c index 2141a58c..64ff994a 100644 --- a/src/battery.c +++ b/src/battery.c @@ -1,11 +1,10 @@ /** * collectd - src/battery.c - * Copyright (C) 2006 Florian octo Forster + * Copyright (C) 2006,2007 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. + * 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 @@ -25,9 +24,6 @@ #include "plugin.h" #include "utils_debug.h" -#define MODULE_NAME "battery" -#define BUFSIZE 512 - #if HAVE_MACH_MACH_TYPES_H # include #endif @@ -61,31 +57,37 @@ #define INVALID_VALUE 47841.29 -static char *battery_current_file = "battery-%s/current.rrd"; -static char *battery_voltage_file = "battery-%s/voltage.rrd"; -static char *battery_charge_file = "battery-%s/charge.rrd"; +static data_source_t data_source_charge[1] = +{ + {"charge", DS_TYPE_GAUGE, 0, NAN} +}; + +static data_set_t charge_ds = +{ + "charge", 1, data_source_charge +}; + +static data_source_t data_source_current[1] = +{ + {"current", DS_TYPE_GAUGE, NAN, NAN} +}; -static char *ds_def_current[] = +static data_set_t current_ds = { - "DS:current:GAUGE:"COLLECTD_HEARTBEAT":U:U", - NULL + "current", 1, data_source_current }; -static int ds_num_current = 1; -static char *ds_def_voltage[] = +static data_source_t data_source_voltage[1] = { - "DS:voltage:GAUGE:"COLLECTD_HEARTBEAT":U:U", - NULL + {"voltage", DS_TYPE_GAUGE, NAN, NAN} }; -static int ds_num_voltage = 1; -static char *ds_def_charge[] = +static data_set_t voltage_ds = { - "DS:charge:GAUGE:"COLLECTD_HEARTBEAT":0:U", - NULL + "voltage", 1, data_source_voltage }; -static int ds_num_charge = 1; +#if BATTERY_HAVE_READ #if HAVE_IOKIT_IOKITLIB_H || HAVE_IOKIT_PS_IOPOWERSOURCES_H /* No global variables */ /* #endif HAVE_IOKIT_IOKITLIB_H || HAVE_IOKIT_PS_IOPOWERSOURCES_H */ @@ -95,7 +97,7 @@ static int battery_pmu_num = 0; static char *battery_pmu_file = "/proc/pmu/battery_%i"; #endif /* KERNEL_LINUX */ -static void battery_init (void) +static int battery_init (void) { #if HAVE_IOKIT_IOKITLIB_H || HAVE_IOKIT_PS_IOPOWERSOURCES_H /* No init neccessary */ @@ -103,13 +105,13 @@ static void battery_init (void) #elif KERNEL_LINUX int len; - char filename[BUFSIZE]; + char filename[128]; for (battery_pmu_num = 0; ; battery_pmu_num++) { - len = snprintf (filename, BUFSIZE, battery_pmu_file, battery_pmu_num); + len = snprintf (filename, sizeof (filename), battery_pmu_file, battery_pmu_num); - if ((len >= BUFSIZE) || (len < 0)) + if ((len >= sizeof (filename)) || (len < 0)) break; if (access (filename, R_OK)) @@ -117,90 +119,25 @@ static void battery_init (void) } #endif /* KERNEL_LINUX */ - return; -} - -static void battery_current_write (char *host, char *inst, char *val) -{ - char filename[BUFSIZE]; - int len; - - len = snprintf (filename, BUFSIZE, battery_current_file, inst); - if ((len >= BUFSIZE) || (len < 0)) - return; - - rrd_update_file (host, filename, val, - ds_def_current, ds_num_current); -} - -static void battery_voltage_write (char *host, char *inst, char *val) -{ - char filename[BUFSIZE]; - int len; - - len = snprintf (filename, BUFSIZE, battery_voltage_file, inst); - if ((len >= BUFSIZE) || (len < 0)) - return; - - rrd_update_file (host, filename, val, - ds_def_voltage, ds_num_voltage); + return (0); } -static void battery_charge_write (char *host, char *inst, char *val) +static void battery_submit (const char *plugin_instance, const char *type, double value) { - char filename[BUFSIZE]; - int len; - - len = snprintf (filename, BUFSIZE, battery_charge_file, inst); - if ((len >= BUFSIZE) || (len < 0)) - return; + value_t values[1]; + value_list_t vl = VALUE_LIST_INIT; - rrd_update_file (host, filename, val, - ds_def_charge, ds_num_charge); -} + values[0].gauge = value; -#if BATTERY_HAVE_READ -static void battery_submit (char *inst, double current, double voltage, double charge) -{ - int len; - char buffer[BUFSIZE]; + vl.values = values; + vl.values_len = 1; + vl.time = time (NULL); + strcpy (vl.host, hostname); + strcpy (vl.plugin, "battery"); + strcpy (vl.plugin_instance, plugin_instance); - if (current != INVALID_VALUE) - { - len = snprintf (buffer, BUFSIZE, "N:%.3f", current); - - if ((len > 0) && (len < BUFSIZE)) - plugin_submit ("battery_current", inst, buffer); - } - else - { - plugin_submit ("battery_current", inst, "N:U"); - } - - if (voltage != INVALID_VALUE) - { - len = snprintf (buffer, BUFSIZE, "N:%.3f", voltage); - - if ((len > 0) && (len < BUFSIZE)) - plugin_submit ("battery_voltage", inst, buffer); - } - else - { - plugin_submit ("battery_voltage", inst, "N:U"); - } - - if (charge != INVALID_VALUE) - { - len = snprintf (buffer, BUFSIZE, "N:%.3f", charge); - - if ((len > 0) && (len < BUFSIZE)) - plugin_submit ("battery_charge", inst, buffer); - } - else - { - plugin_submit ("battery_charge", inst, "N:U"); - } -} + plugin_dispatch_values (type, &vl); +} /* void battery_submit */ #if HAVE_IOKIT_PS_IOPOWERSOURCES_H || HAVE_IOKIT_IOKITLIB_H double dict_get_double (CFDictionaryRef dict, char *key_string) @@ -409,7 +346,7 @@ static void get_via_generic_iokit (double *ret_charge, } #endif /* HAVE_IOKIT_IOKITLIB_H */ -static void battery_read (void) +static int battery_read (void) { #if HAVE_IOKIT_IOKITLIB_H || HAVE_IOKIT_PS_IOPOWERSOURCES_H double charge = INVALID_VALUE; /* Current charge in Ah */ @@ -429,16 +366,18 @@ static void battery_read (void) if ((charge_rel != INVALID_VALUE) && (charge_abs != INVALID_VALUE)) charge = charge_abs * charge_rel / 100.0; - if ((charge != INVALID_VALUE) - || (current != INVALID_VALUE) - || (voltage != INVALID_VALUE)) - battery_submit ("0", current, voltage, charge); + if (charge != INVALID_VALUE) + battery_submit ("0", "charge", charge); + if (current != INVALID_VALUE) + battery_submit ("0", "current", current); + if (voltage != INVALID_VALUE) + battery_submit ("0", "voltage", voltage); /* #endif HAVE_IOKIT_IOKITLIB_H || HAVE_IOKIT_PS_IOPOWERSOURCES_H */ #elif KERNEL_LINUX FILE *fh; - char buffer[BUFSIZE]; - char filename[BUFSIZE]; + char buffer[1024]; + char filename[256]; char *fields[8]; int numfields; @@ -448,24 +387,24 @@ static void battery_read (void) for (i = 0; i < battery_pmu_num; i++) { - char batnum_str[BUFSIZE]; + char batnum_str[256]; double current = INVALID_VALUE; double voltage = INVALID_VALUE; double charge = INVALID_VALUE; double *valptr = NULL; - len = snprintf (filename, BUFSIZE, battery_pmu_file, i); - if ((len >= BUFSIZE) || (len < 0)) + len = snprintf (filename, sizeof (filename), battery_pmu_file, i); + if ((len >= sizeof (filename)) || (len < 0)) continue; - len = snprintf (batnum_str, BUFSIZE, "%i", i); - if ((len >= BUFSIZE) || (len < 0)) + len = snprintf (batnum_str, sizeof (batnum_str), "%i", i); + if ((len >= sizeof (batnum_str)) || (len < 0)) continue; if ((fh = fopen (filename, "r")) == NULL) continue; - while (fgets (buffer, BUFSIZE, fh) != NULL) + while (fgets (buffer, sizeof (buffer), fh) != NULL) { numfields = strsplit (buffer, fields, 8); @@ -495,13 +434,15 @@ static void battery_read (void) } } - if ((current != INVALID_VALUE) - || (voltage != INVALID_VALUE) - || (charge != INVALID_VALUE)) - battery_submit (batnum_str, current, voltage, charge); - fclose (fh); fh = NULL; + + if (charge != INVALID_VALUE) + battery_submit ("0", "charge", charge); + if (current != INVALID_VALUE) + battery_submit ("0", "current", current); + if (voltage != INVALID_VALUE) + battery_submit ("0", "voltage", voltage); } if (access ("/proc/acpi/battery", R_OK | X_OK) == 0) @@ -518,7 +459,7 @@ static void battery_read (void) if ((dh = opendir ("/proc/acpi/battery")) == NULL) { syslog (LOG_ERR, "Cannot open `/proc/acpi/battery': %s", strerror (errno)); - return; + return (-1); } while ((ent = readdir (dh)) != NULL) @@ -526,8 +467,10 @@ static void battery_read (void) if (ent->d_name[0] == '.') continue; - len = snprintf (filename, BUFSIZE, "/proc/acpi/battery/%s/state", ent->d_name); - if ((len >= BUFSIZE) || (len < 0)) + len = snprintf (filename, sizeof (filename), + "/proc/acpi/battery/%s/state", + ent->d_name); + if ((len >= sizeof (filename)) || (len < 0)) continue; if ((fh = fopen (filename, "r")) == NULL) @@ -545,7 +488,7 @@ static void battery_read (void) * [11:00] <@tokkee> remaining capacity: 4136 mAh * [11:00] <@tokkee> present voltage: 12428 mV */ - while (fgets (buffer, BUFSIZE, fh) != NULL) + while (fgets (buffer, sizeof (buffer), fh) != NULL) { numfields = strsplit (buffer, fields, 8); @@ -585,34 +528,37 @@ static void battery_read (void) if ((fields[2] == endptr) || (errno != 0)) *valptr = INVALID_VALUE; } - } + } /* while (fgets (buffer, sizeof (buffer), fh) != NULL) */ + + fclose (fh); if ((current != INVALID_VALUE) && (charging == 0)) current *= -1; - if ((current != INVALID_VALUE) - || (voltage != INVALID_VALUE) - || (charge != INVALID_VALUE)) - battery_submit (ent->d_name, current, voltage, charge); - - fclose (fh); + if (charge != INVALID_VALUE) + battery_submit ("0", "charge", charge); + if (current != INVALID_VALUE) + battery_submit ("0", "current", current); + if (voltage != INVALID_VALUE) + battery_submit ("0", "voltage", voltage); } closedir (dh); } #endif /* KERNEL_LINUX */ + + return (0); } -#else -# define battery_read NULL #endif /* BATTERY_HAVE_READ */ void module_register (void) { - plugin_register (MODULE_NAME, battery_init, battery_read, NULL); - plugin_register ("battery_current", NULL, NULL, battery_current_write); - plugin_register ("battery_voltage", NULL, NULL, battery_voltage_write); - plugin_register ("battery_charge", NULL, NULL, battery_charge_write); -} + plugin_register_data_set (&charge_ds); + plugin_register_data_set (¤t_ds); + plugin_register_data_set (&voltage_ds); -#undef BUFSIZE -#undef MODULE_NAME +#if BATTERY_HAVE_READ + plugin_register_init ("battery", battery_init); + plugin_register_read ("battery", battery_read); +#endif /* BATTERY_HAVE_READ */ +} diff --git a/src/collectd-nagios.c b/src/collectd-nagios.c new file mode 100644 index 00000000..160412c5 --- /dev/null +++ b/src/collectd-nagios.c @@ -0,0 +1,492 @@ +#include +#include +#include +#include +#include +#include + +#include +#include + +/* + * This weird macro cascade forces the glibc to define `NAN'. I don't know + * another way to solve this, so more intelligent solutions are welcome. -octo + */ +#ifndef __USE_ISOC99 +# define DISABLE__USE_ISOC99 1 +# define __USE_ISOC99 1 +#endif +#include +#ifdef DISABLE__USE_ISOC99 +# undef DISABLE__USE_ISOC99 +# undef __USE_ISOC99 +#endif + +#define RET_OKAY 0 +#define RET_WARNING 1 +#define RET_CRITICAL 2 +#define RET_UNKNOWN 3 + +#define CON_NONE 0 +#define CON_AVERAGE 1 +#define CON_SUM 2 + +struct range_s +{ + double min; + double max; + int invert; +}; +typedef struct range_s range_t; + +extern char *optarg; +extern int optind, opterr, optopt; + +static char *socket_file_g = NULL; +static char *value_string_g = NULL; +static char *hostname_g = NULL; + +static range_t range_critical_g; +static range_t range_warning_g; +static int consolitation_g = CON_NONE; + +static char **match_ds_g = NULL; +static int match_ds_num_g = 0; + +static int ignore_ds (const char *name) +{ + int i; + + if (match_ds_g == NULL) + return (0); + + for (i = 0; i < match_ds_num_g; i++) + if (strcasecmp (match_ds_g[i], name) == 0) + return (0); + + return (1); +} /* int ignore_ds */ + +static void parse_range (char *string, range_t *range) +{ + char *min_ptr; + char *max_ptr; + + if (*string == '@') + { + range->invert = 1; + string++; + } + + max_ptr = strchr (string, ':'); + if (max_ptr == NULL) + { + min_ptr = NULL; + max_ptr = string; + } + else + { + min_ptr = string; + *max_ptr = '\0'; + max_ptr++; + } + + assert (max_ptr != NULL); + + /* `10' == `0:10' */ + if (min_ptr == NULL) + range->min = 0.0; + /* :10 == ~:10 == -inf:10 */ + else if ((*min_ptr == '\0') || (*min_ptr == '~')) + range->min = NAN; + else + range->min = atof (min_ptr); + + if ((*max_ptr == '\0') || (*max_ptr == '~')) + range->max = NAN; + else + range->max = atof (max_ptr); +} /* void parse_range */ + +int match_range (range_t *range, double value) +{ + int ret = 0; + + if ((range->min != NAN) && (range->min > value)) + ret = 1; + if ((range->max != NAN) && (range->max < value)) + ret = 1; + + return (((ret - range->invert) == 0) ? 0 : 1); +} + +static int get_values (int *ret_values_num, double **ret_values, + char ***ret_values_names) +{ + struct sockaddr_un sa; + int status; + int fd; + FILE *fh; + char buffer[4096]; + + int values_num; + double *values; + char **values_names; + + int i; + + fd = socket (PF_UNIX, SOCK_STREAM, 0); + if (fd < 0) + { + fprintf (stderr, "socket failed: %s\n", + strerror (errno)); + return (-1); + } + + memset (&sa, '\0', sizeof (sa)); + sa.sun_family = AF_UNIX; + strncpy (sa.sun_path, socket_file_g, + sizeof (sa.sun_path) - 1); + + status = connect (fd, (struct sockaddr *) &sa, sizeof (sa)); + if (status != 0) + { + fprintf (stderr, "connect failed: %s\n", + strerror (errno)); + return (-1); + } + + fh = fdopen (fd, "r+"); + if (fh == NULL) + { + fprintf (stderr, "fdopen failed: %s\n", + strerror (errno)); + close (fd); + return (-1); + } + + fprintf (fh, "GETVAL %s/%s\n", hostname_g, value_string_g); + fflush (fh); + + if (fgets (buffer, sizeof (buffer), fh) == NULL) + { + fprintf (stderr, "fgets failed: %s\n", + strerror (errno)); + close (fd); + return (-1); + } + close (fd); fd = -1; + + values_num = atoi (buffer); + if (values_num < 1) + return (-1); + + values = (double *) malloc (values_num * sizeof (double)); + if (values == NULL) + { + fprintf (stderr, "malloc failed: %s\n", + strerror (errno)); + return (-1); + } + + values_names = (char **) malloc (values_num * sizeof (char *)); + if (values_names == NULL) + { + fprintf (stderr, "malloc failed: %s\n", + strerror (errno)); + free (values); + return (-1); + } + + { + char *ptr = strchr (buffer, ' ') + 1; + char *key; + char *value; + + i = 0; + while ((key = strtok (ptr, " \t")) != NULL) + { + ptr = NULL; + value = strchr (key, '='); + if (value == NULL) + continue; + *value = '\0'; value++; + + if (ignore_ds (key) != 0) + continue; + + values_names[i] = strdup (key); + values[i] = atof (value); + + i++; + if (i >= values_num) + break; + } + values_num = i; + } + + *ret_values_num = values_num; + *ret_values = values; + *ret_values_names = values_names; + + return (0); +} /* int get_values */ + +static void usage (const char *name) +{ + fprintf (stderr, "Usage: %s <-s socket> <-n value_spec> <-H hostname> [options]\n" + "\n" + "Valid options are:\n" + " -s Path to collectd's UNIX-socket.\n" + " -n Value specification to get from collectd.\n" + " Format: `plugin-instance/type-instance'\n" + " -d Select the DS to examine. May be repeated to examine multiple\n" + " DSes. By default all DSes are used.\n" + " -g Method to use to consolidate several DSes.\n" + " Valid arguments are `none', `average' and `sum'\n" + " -H Hostname to query the values for.\n" + " -c Critical range\n" + " -w Warning range\n" + "\n" + "Consolidation functions:\n" + " none: Apply the warning- and critical-ranges to each data-source\n" + " individually.\n" + " average: Calculate the average of all matching DSes and apply the\n" + " warning- and critical-ranges to the calculated average.\n" + " sum: Apply the ranges to the sum of all DSes.\n" + "\n", name); + exit (1); +} /* void usage */ + +int do_check_con_none (int values_num, double *values, char **values_names) +{ + int i; + + int num_critical = 0; + int num_warning = 0; + int num_okay = 0; + + for (i = 0; i < values_num; i++) + { + if (values[i] == NAN) + num_warning++; + else if (match_range (&range_critical_g, values[i]) != 0) + num_critical++; + else if (match_range (&range_warning_g, values[i]) != 0) + num_warning++; + else + num_okay++; + } + + if ((num_critical != 0) || (values_num == 0)) + { + printf ("CRITICAL: %i critical, %i warning, %i okay\n", + num_critical, num_warning, num_okay); + return (RET_CRITICAL); + } + else if (num_warning != 0) + { + printf ("WARNING: %i warning, %i okay\n", + num_warning, num_okay); + return (RET_WARNING); + } + else + { + printf ("OKAY: %i okay\n", num_okay); + return (RET_OKAY); + } + + return (RET_UNKNOWN); +} /* int do_check_con_none */ + +int do_check_con_average (int values_num, double *values, char **values_names) +{ + int i; + double total; + int total_num; + + total = 0.0; + total_num = 0; + for (i = 0; i < values_num; i++) + { + if (values[i] != NAN) + { + total += values[i]; + total_num++; + } + } + + if (total_num == 0) + { + printf ("WARNING: No defined values found\n"); + return (RET_WARNING); + } + + if (match_range (&range_critical_g, total / total_num) != 0) + { + printf ("CRITICAL: Average = %lf\n", + (double) (total / total_num)); + return (RET_CRITICAL); + } + else if (match_range (&range_warning_g, total / total_num) != 0) + { + printf ("WARNING: Average = %lf\n", + (double) (total / total_num)); + return (RET_WARNING); + } + else + { + printf ("OKAY: Average = %lf\n", + (double) (total / total_num)); + return (RET_OKAY); + } + + return (RET_UNKNOWN); +} /* int do_check_con_average */ + +int do_check_con_sum (int values_num, double *values, char **values_names) +{ + int i; + double total; + int total_num; + + total = 0.0; + total_num = 0; + for (i = 0; i < values_num; i++) + { + if (values[i] != NAN) + { + total += values[i]; + total_num++; + } + } + + if (total_num == 0) + { + printf ("WARNING: No defined values found\n"); + return (RET_WARNING); + } + + if (match_range (&range_critical_g, total) != 0) + { + printf ("CRITICAL: Sum = %lf\n", total); + return (RET_CRITICAL); + } + else if (match_range (&range_warning_g, total) != 0) + { + printf ("WARNING: Sum = %lf\n", total); + return (RET_WARNING); + } + else + { + printf ("OKAY: Sum = %lf\n", total); + return (RET_OKAY); + } + + return (RET_UNKNOWN); +} /* int do_check_con_sum */ + +int do_check (void) +{ + double *values; + char **values_names; + int values_num; + + if (get_values (&values_num, &values, &values_names) != 0) + { + fputs ("ERROR: Cannot get values from daemon\n", stdout); + return (RET_CRITICAL); + } + + if (consolitation_g == CON_NONE) + return (do_check_con_none (values_num, values, values_names)); + else if (consolitation_g == CON_AVERAGE) + return (do_check_con_average (values_num, values, values_names)); + else if (consolitation_g == CON_SUM) + return (do_check_con_sum (values_num, values, values_names)); + + free (values); + free (values_names); + + return (RET_UNKNOWN); +} + +int main (int argc, char **argv) +{ + range_critical_g.min = NAN; + range_critical_g.max = NAN; + range_critical_g.invert = 0; + + range_warning_g.min = NAN; + range_warning_g.max = NAN; + range_warning_g.invert = 0; + + while (42) + { + int c; + + c = getopt (argc, argv, "w:c:s:n:H:g:d:h"); + if (c < 0) + break; + + switch (c) + { + case 'c': + parse_range (optarg, &range_critical_g); + break; + case 'w': + parse_range (optarg, &range_warning_g); + break; + case 's': + socket_file_g = optarg; + break; + case 'n': + value_string_g = optarg; + break; + case 'H': + hostname_g = optarg; + break; + case 'g': + if (strcasecmp (optarg, "none") == 0) + consolitation_g = CON_NONE; + else if (strcasecmp (optarg, "average") == 0) + consolitation_g = CON_AVERAGE; + else if (strcasecmp (optarg, "sum") == 0) + consolitation_g = CON_SUM; + else + usage (argv[0]); + break; + case 'd': + { + char **tmp; + tmp = (char **) realloc (match_ds_g, + (match_ds_num_g + 1) + * sizeof (char *)); + if (tmp == NULL) + { + fprintf (stderr, "realloc failed: %s\n", + strerror (errno)); + return (RET_UNKNOWN); + } + match_ds_g = tmp; + match_ds_g[match_ds_num_g] = strdup (optarg); + if (match_ds_g[match_ds_num_g] == NULL) + { + fprintf (stderr, "strdup failed: %s\n", + strerror (errno)); + return (RET_UNKNOWN); + } + match_ds_num_g++; + break; + } + default: + usage (argv[0]); + } /* switch (c) */ + } + + if ((socket_file_g == NULL) || (value_string_g == NULL) + || (hostname_g == NULL)) + usage (argv[0]); + + return (do_check ()); +} /* int main */ diff --git a/src/cpu.c b/src/cpu.c index 06515593..437f7587 100644 --- a/src/cpu.c +++ b/src/cpu.c @@ -75,18 +75,14 @@ # define CPU_HAVE_READ 0 #endif -static data_source_t dsrc[5] = +static data_source_t dsrc[1] = { - {"user", DS_TYPE_COUNTER, 0, 4294967295.0}, - {"nice", DS_TYPE_COUNTER, 0, 4294967295.0}, - {"syst", DS_TYPE_COUNTER, 0, 4294967295.0}, - {"idle", DS_TYPE_COUNTER, 0, 4294967295.0}, - {"wait", DS_TYPE_COUNTER, 0, 4294967295.0} + {"value", DS_TYPE_COUNTER, 0, 4294967295.0} }; static data_set_t ds = { - "cpu", 5, dsrc + "cpu", 1, dsrc }; #if CPU_HAVE_READ @@ -177,28 +173,22 @@ static int init (void) return (0); } /* int init */ -static void submit (int cpu_num, unsigned long long user, - unsigned long long nice, unsigned long long syst, - unsigned long long idle, unsigned long long wait) +static void submit (int cpu_num, const char *type_instance, counter_t value) { - value_t values[5]; + value_t values[1]; value_list_t vl = VALUE_LIST_INIT; - values[0].counter = user; - values[1].counter = nice; - values[2].counter = syst; - values[3].counter = idle; - values[4].counter = wait; + values[0].counter = value; vl.values = values; - vl.values_len = 2; + vl.values_len = 1; vl.time = time (NULL); strcpy (vl.host, hostname); strcpy (vl.plugin, "cpu"); - strcpy (vl.plugin_instance, ""); - snprintf (vl.type_instance, sizeof (vl.type_instance), + snprintf (vl.plugin_instance, sizeof (vl.type_instance), "%i", cpu_num); - vl.type_instance[DATA_MAX_NAME_LEN - 1] = '\0'; + vl.plugin_instance[DATA_MAX_NAME_LEN - 1] = '\0'; + strcpy (vl.type_instance, type_instance); plugin_dispatch_values ("cpu", &vl); } @@ -241,11 +231,10 @@ static int cpu_read (void) continue; } - submit (cpu, cpu_info.cpu_ticks[CPU_STATE_USER], - cpu_info.cpu_ticks[CPU_STATE_NICE], - cpu_info.cpu_ticks[CPU_STATE_SYSTEM], - cpu_info.cpu_ticks[CPU_STATE_IDLE], - 0ULL); + submit (cpu, "user", (counter_t) cpu_info.cpu_ticks[CPU_STATE_USER]); + submit (cpu, "nice", (counter_t) cpu_info.cpu_ticks[CPU_STATE_USER]); + submit (cpu, "system", (counter_t) cpu_info.cpu_ticks[CPU_STATE_USER]); + submit (cpu, "idle", (counter_t) cpu_info.cpu_ticks[CPU_STATE_USER]); #endif /* PROCESSOR_CPU_LOAD_INFO */ #if PROCESSOR_TEMPERATURE /* @@ -296,8 +285,8 @@ static int cpu_read (void) #elif defined(KERNEL_LINUX) int cpu; - unsigned long long user, nice, syst, idle; - unsigned long long wait, intr, sitr; /* sitr == soft interrupt */ + counter_t user, nice, syst, idle; + counter_t wait, intr, sitr; /* sitr == soft interrupt */ FILE *fh; char buf[1024]; @@ -334,22 +323,21 @@ static int cpu_read (void) syst = atoll (fields[3]); idle = atoll (fields[4]); + submit (cpu, "user", user); + submit (cpu, "nice", nice); + submit (cpu, "system", syst); + submit (cpu, "idle", idle); + if (numfields >= 8) { wait = atoll (fields[5]); intr = atoll (fields[6]); sitr = atoll (fields[7]); - /* I doubt anyone cares about the time spent in - * interrupt handlers.. */ - syst += intr + sitr; - } - else - { - wait = 0LL; + submit (cpu, "wait", wait); + submit (cpu, "interrupt", intr); + submit (cpu, "softirq", sitr); } - - submit (cpu, user, nice, syst, idle, wait); } fclose (fh); @@ -357,7 +345,7 @@ static int cpu_read (void) #elif defined(HAVE_LIBKSTAT) int cpu; - unsigned long long user, syst, idle, wait; + counter_t user, syst, idle, wait; static cpu_stat_t cs; if (kc == NULL) @@ -368,13 +356,15 @@ static int cpu_read (void) if (kstat_read (kc, ksp[cpu], &cs) == -1) continue; /* error message? */ - idle = (unsigned long long) cs.cpu_sysinfo.cpu[CPU_IDLE]; - user = (unsigned long long) cs.cpu_sysinfo.cpu[CPU_USER]; - syst = (unsigned long long) cs.cpu_sysinfo.cpu[CPU_KERNEL]; - wait = (unsigned long long) cs.cpu_sysinfo.cpu[CPU_WAIT]; + idle = (counter_t) cs.cpu_sysinfo.cpu[CPU_IDLE]; + user = (counter_t) cs.cpu_sysinfo.cpu[CPU_USER]; + syst = (counter_t) cs.cpu_sysinfo.cpu[CPU_KERNEL]; + wait = (counter_t) cs.cpu_sysinfo.cpu[CPU_WAIT]; - submit (ksp[cpu]->ks_instance, - user, 0LL, syst, idle, wait); + submit (ksp[cpu]->ks_instance, "user", user); + submit (ksp[cpu]->ks_instance, "system", syst); + submit (ksp[cpu]->ks_instance, "idle", idle); + submit (ksp[cpu]->ks_instance, "wait", wait); } /* #endif defined(HAVE_LIBKSTAT) */ @@ -399,8 +389,10 @@ static int cpu_read (void) cpuinfo[CP_SYS] += cpuinfo[CP_INTR]; - /* FIXME: Instance is always `0' */ - submit (0, cpuinfo[CP_USER], cpuinfo[CP_NICE], cpuinfo[CP_SYS], cpuinfo[CP_IDLE], 0LL); + submit (0, "user", cpuinfo[CP_USER]); + submit (0, "nice", cpuinfo[CP_NICE]); + submit (0, "system", cpuinfo[CP_SYS]); + submit (0, "idle", cpuinfo[CP_IDLE]); #endif return (0); diff --git a/src/cpufreq.c b/src/cpufreq.c index ba0149ad..e53e4952 100644 --- a/src/cpufreq.c +++ b/src/cpufreq.c @@ -1,6 +1,6 @@ /** * collectd - src/cpufreq.c - * Copyright (C) 2005,2006 Peter Holik + * Copyright (C) 2005-2007 Peter Holik * * 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 @@ -32,99 +32,100 @@ # define CPUFREQ_HAVE_READ 0 #endif -static char *cpufreq_file = "cpufreq-%s.rrd"; +static data_source_t data_source[1] = +{ + {"value", DS_TYPE_GAUGE, 0, NAN} +}; -static char *ds_def[] = +static data_set_t data_set = { - "DS:value:GAUGE:"COLLECTD_HEARTBEAT":0:U", - NULL + "cpufreq", 1, data_source }; -static int ds_num = 1; +#if CPUFREQ_HAVE_READ #ifdef KERNEL_LINUX static int num_cpu = 0; #endif -#define BUFSIZE 256 - -static void cpufreq_init (void) +static int cpufreq_init (void) { #ifdef KERNEL_LINUX int status; - char filename[BUFSIZE]; + char filename[256]; num_cpu = 0; while (1) { - status = snprintf (filename, BUFSIZE, "/sys/devices/system/cpu/cpu%d/cpufreq/scaling_cur_freq", num_cpu); - if (status < 1 || status >= BUFSIZE) + status = snprintf (filename, sizeof (filename), + "/sys/devices/system/cpu/cpu%d/cpufreq/" + "scaling_cur_freq", num_cpu); + if (status < 1 || status >= sizeof (filename)) break; - if (access(filename, R_OK)) + if (access (filename, R_OK)) break; num_cpu++; } - syslog (LOG_INFO, MODULE_NAME" found %d cpu(s)", num_cpu); + syslog (LOG_INFO, "cpufreq plugin: Found %d CPU%s", num_cpu, + (num_cpu == 1) ? "" : "s"); + + if (num_cpu == 0) + plugin_unregister_read ("cpufreq"); #endif /* defined(KERNEL_LINUX) */ - return; -} + return (0); +} /* int cpufreq_init */ -static void cpufreq_write (char *host, char *inst, char *val) +static void cpufreq_submit (int cpu_num, double value) { - int status; - char file[BUFSIZE]; - - status = snprintf (file, BUFSIZE, cpufreq_file, inst); - if (status < 1 || status >= BUFSIZE) - return; - - rrd_update_file (host, file, val, ds_def, ds_num); -} + value_t values[1]; + value_list_t vl = VALUE_LIST_INIT; -#if CPUFREQ_HAVE_READ -static void cpufreq_submit (int cpu_num, unsigned long long val) -{ - char buf[BUFSIZE]; - char cpu[16]; + values[0].gauge = value; - if (snprintf (buf, BUFSIZE, "%u:%llu", (unsigned int) curtime, val) >= BUFSIZE) - return; - snprintf (cpu, 16, "%i", cpu_num); + vl.values = values; + vl.values_len = 1; + vl.time = time (NULL); + strcpy (vl.host, hostname); + strcpy (vl.plugin, "cpufreq"); + snprintf (vl.type_instance, sizeof (vl.type_instance), + "%i", cpu_num); - plugin_submit (MODULE_NAME, cpu, buf); + plugin_dispatch_values ("cpufreq", &vl); } -static void cpufreq_read (void) +static int cpufreq_read (void) { #ifdef KERNEL_LINUX int status; unsigned long long val; int i = 0; FILE *fp; - char filename[BUFSIZE]; + char filename[256]; char buffer[16]; for (i = 0; i < num_cpu; i++) { - status = snprintf (filename, BUFSIZE, "/sys/devices/system/cpu/cpu%d/cpufreq/scaling_cur_freq", i); - if (status < 1 || status >= BUFSIZE) - return; + status = snprintf (filename, sizeof (filename), + "/sys/devices/system/cpu/cpu%d/cpufreq/" + "scaling_cur_freq", i); + if (status < 1 || status >= sizeof (filename)) + return (-1); if ((fp = fopen (filename, "r")) == NULL) { syslog (LOG_WARNING, "cpufreq: fopen: %s", strerror (errno)); - return; + return (-1); } if (fgets (buffer, 16, fp) == NULL) { syslog (LOG_WARNING, "cpufreq: fgets: %s", strerror (errno)); fclose (fp); - return; + return (-1); } if (fclose (fp)) @@ -137,16 +138,17 @@ static void cpufreq_read (void) } #endif /* defined(KERNEL_LINUX) */ - return; -} -#else -#define cpufreq_read NULL -#endif + return (0); +} /* int cpufreq_read */ +#endif /* CPUFREQ_HAVE_READ */ #undef BUFSIZE void module_register (void) { - plugin_register (MODULE_NAME, cpufreq_init, cpufreq_read, cpufreq_write); -} + plugin_register_data_set (&data_set); -#undef MODULE_NAME +#if CPUFREQ_HAVE_READ + plugin_register_init ("cpufreq", cpufreq_init); + plugin_register_read ("cpufreq", cpufreq_read); +#endif /* CPUFREQ_HAVE_READ */ +} diff --git a/src/df.c b/src/df.c index d327164a..dd5e1382 100644 --- a/src/df.c +++ b/src/df.c @@ -1,11 +1,10 @@ /** * collectd - src/df.c - * Copyright (C) 2005,2006 Florian octo Forster + * Copyright (C) 2005-2007 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. + * 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 @@ -27,8 +26,6 @@ #include "utils_mount.h" #include "utils_ignorelist.h" -#define MODULE_NAME "df" - #if HAVE_STATFS || HAVE_STATVFS # define DF_HAVE_READ 1 #else @@ -49,17 +46,20 @@ # define BLOCKSIZE(s) (s).f_bsize #endif -static char *filename_template = "df-%s.rrd"; +/* 2^50 - 1 == 1125899906842623 = 1 Petabyte */ +static data_source_t dsrc[2] = +{ + {"free", DS_TYPE_GAUGE, 0, 1125899906842623.0}, + {"used", DS_TYPE_GAUGE, 0, 1125899906842623.0} +}; -static char *ds_def[] = +static data_set_t ds = { - "DS:used:GAUGE:"COLLECTD_HEARTBEAT":0:U", - "DS:free:GAUGE:"COLLECTD_HEARTBEAT":0:U", - NULL + "df", 2, dsrc }; -static int ds_num = 2; -static char *config_keys[] = +#if DF_HAVE_READ +static const char *config_keys[] = { "Device", "MountPoint", @@ -73,9 +73,7 @@ static ignorelist_t *il_device = NULL; static ignorelist_t *il_mountpoint = NULL; static ignorelist_t *il_fstype = NULL; -#define BUFSIZE 512 - -static void df_init (void) +static int df_init (void) { if (il_device == NULL) il_device = ignorelist_create (1); @@ -84,10 +82,10 @@ static void df_init (void) if (il_fstype == NULL) il_fstype = ignorelist_create (1); - return; + return (0); } -static int df_config (char *key, char *value) +static int df_config (const char *key, const char *value) { df_init (); @@ -131,35 +129,28 @@ static int df_config (char *key, char *value) return (-1); } -static void df_write (char *host, char *inst, char *val) -{ - char file[BUFSIZE]; - int status; - - status = snprintf (file, BUFSIZE, filename_template, inst); - if (status < 1) - return; - else if (status >= BUFSIZE) - return; - - rrd_update_file (host, file, val, ds_def, ds_num); -} - -#if DF_HAVE_READ static void df_submit (char *df_name, - unsigned long long df_used, - unsigned long long df_free) + gauge_t df_used, + gauge_t df_free) { - char buf[BUFSIZE]; + value_t values[2]; + value_list_t vl = VALUE_LIST_INIT; - if (snprintf (buf, BUFSIZE, "%u:%llu:%llu", (unsigned int) curtime, - df_used, df_free) >= BUFSIZE) - return; + values[0].gauge = df_used; + values[1].gauge = df_free; - plugin_submit (MODULE_NAME, df_name, buf); -} + vl.values = values; + vl.values_len = 2; + vl.time = time (NULL); + strcpy (vl.host, hostname); + strcpy (vl.plugin, "df"); + strcpy (vl.plugin_instance, ""); + strncpy (vl.type_instance, df_name, sizeof (vl.type_instance)); -static void df_read (void) + plugin_dispatch_values ("df", &vl); +} /* void df_submit */ + +static int df_read (void) { #if HAVE_STATVFS struct statvfs statbuf; @@ -171,13 +162,13 @@ static void df_read (void) cu_mount_t *mnt_ptr; unsigned long long blocksize; - unsigned long long df_free; - unsigned long long df_used; - char mnt_name[BUFSIZE]; + gauge_t df_free; + gauge_t df_used; + char mnt_name[256]; mnt_list = NULL; if (cu_mount_getlist (&mnt_list) == NULL) - return; + return (-1); for (mnt_ptr = mnt_list; mnt_ptr != NULL; mnt_ptr = mnt_ptr->next) { @@ -196,13 +187,13 @@ static void df_read (void) if (strcmp (mnt_ptr->dir, "/") == 0) { - strncpy (mnt_name, "root", BUFSIZE); + strncpy (mnt_name, "root", sizeof (mnt_name)); } else { int i, len; - strncpy (mnt_name, mnt_ptr->dir + 1, BUFSIZE); + strncpy (mnt_name, mnt_ptr->dir + 1, sizeof (mnt_name)); len = strlen (mnt_name); for (i = 0; i < len; i++) @@ -224,16 +215,18 @@ static void df_read (void) } cu_mount_freelist (mnt_list); -} /* static void df_read (void) */ -#else -# define df_read NULL + + return (0); +} /* int df_read */ #endif /* DF_HAVE_READ */ void module_register (void) { - plugin_register (MODULE_NAME, df_init, df_read, df_write); - cf_register (MODULE_NAME, df_config, config_keys, config_keys_num); -} + plugin_register_data_set (&ds); -#undef BUFSIZE -#undef MODULE_NAME +#if DF_HAVE_READ + plugin_register_config ("df", df_config, config_keys, config_keys_num); + plugin_register_init ("df", df_init); + plugin_register_read ("df", df_read); +#endif +} /* void module_register */ diff --git a/src/disk.c b/src/disk.c index c809fdb6..3cb86e7e 100644 --- a/src/disk.c +++ b/src/disk.c @@ -1,11 +1,10 @@ /** * collectd - src/disk.c - * Copyright (C) 2005,2006 Florian octo Forster + * Copyright (C) 2005-2007 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. + * 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 @@ -25,8 +24,6 @@ #include "plugin.h" #include "utils_debug.h" -#define MODULE_NAME "disk" - #if HAVE_MACH_MACH_TYPES_H # include #endif @@ -61,34 +58,53 @@ # define DISK_HAVE_READ 0 #endif -static char *disk_filename_template = "disk-%s.rrd"; -static char *part_filename_template = "partition-%s.rrd"; +/* 2^34 = 17179869184 = ~17.2GByte/s */ +static data_source_t octets_dsrc[2] = +{ + {"read", DS_TYPE_COUNTER, 0, 17179869183.0}, + {"write", DS_TYPE_COUNTER, 0, 17179869183.0} +}; + +static data_set_t octets_ds = +{ + "disk_octets", 2, octets_dsrc +}; + +static data_source_t operations_dsrc[2] = +{ + {"read", DS_TYPE_COUNTER, 0, 4294967295.0}, + {"write", DS_TYPE_COUNTER, 0, 4294967295.0} +}; -/* 104857600 == 100 MB */ -static char *disk_ds_def[] = +static data_set_t operations_ds = { - "DS:rcount:COUNTER:"COLLECTD_HEARTBEAT":0:U", - "DS:rmerged:COUNTER:"COLLECTD_HEARTBEAT":0:U", - "DS:rbytes:COUNTER:"COLLECTD_HEARTBEAT":0:104857600", - "DS:rtime:COUNTER:"COLLECTD_HEARTBEAT":0:U", - "DS:wcount:COUNTER:"COLLECTD_HEARTBEAT":0:U", - "DS:wmerged:COUNTER:"COLLECTD_HEARTBEAT":0:U", - "DS:wbytes:COUNTER:"COLLECTD_HEARTBEAT":0:104857600", - "DS:wtime:COUNTER:"COLLECTD_HEARTBEAT":0:U", - NULL + "disk_ops", 2, operations_dsrc }; -static int disk_ds_num = 8; -static char *part_ds_def[] = +static data_source_t merged_dsrc[2] = { - "DS:rcount:COUNTER:"COLLECTD_HEARTBEAT":0:U", - "DS:rbytes:COUNTER:"COLLECTD_HEARTBEAT":0:104857600", - "DS:wcount:COUNTER:"COLLECTD_HEARTBEAT":0:U", - "DS:wbytes:COUNTER:"COLLECTD_HEARTBEAT":0:104857600", - NULL + {"read", DS_TYPE_COUNTER, 0, 4294967295.0}, + {"write", DS_TYPE_COUNTER, 0, 4294967295.0} }; -static int part_ds_num = 4; +static data_set_t merged_ds = +{ + "disk_merged", 2, merged_dsrc +}; + +/* max is 1000000us per second. */ +static data_source_t time_dsrc[2] = +{ + {"read", DS_TYPE_COUNTER, 0, 1000000.0}, + {"write", DS_TYPE_COUNTER, 0, 1000000.0} +}; + +static data_set_t time_ds = +{ + "disk_time", 2, time_dsrc +}; + +#if DISK_HAVE_READ #if HAVE_IOKIT_IOKITLIB_H static mach_port_t io_master_port = MACH_PORT_NULL; /* #endif HAVE_IOKIT_IOKITLIB_H */ @@ -101,11 +117,11 @@ typedef struct diskstats /* This overflows in roughly 1361 year */ unsigned int poll_count; - unsigned long long read_sectors; - unsigned long long write_sectors; + counter_t read_sectors; + counter_t write_sectors; - unsigned long long read_bytes; - unsigned long long write_bytes; + counter_t read_bytes; + counter_t write_bytes; struct diskstats *next; } diskstats_t; @@ -121,7 +137,7 @@ static kstat_t *ksp[MAX_NUMDISK]; static int numdisk = 0; #endif /* HAVE_LIBKSTAT */ -static void disk_init (void) +static int disk_init (void) { #if HAVE_IOKIT_IOKITLIB_H kern_return_t status; @@ -139,7 +155,7 @@ static void disk_init (void) syslog (LOG_ERR, "IOMasterPort failed: %s", mach_error_string (status)); io_master_port = MACH_PORT_NULL; - return; + return (-1); } /* #endif HAVE_IOKIT_IOKITLIB_H */ @@ -163,7 +179,7 @@ static void disk_init (void) numdisk = 0; if (kc == NULL) - return; + return (-1); for (numdisk = 0, ksp_chain = kc->kc_chain; (numdisk < MAX_NUMDISK) && (ksp_chain != NULL); @@ -178,83 +194,29 @@ static void disk_init (void) } #endif /* HAVE_LIBKSTAT */ - return; -} + return (0); +} /* int disk_init */ -static void disk_write (char *host, char *inst, char *val) +static void disk_submit (const char *plugin_instance, + const char *type, + counter_t read, counter_t write) { - char file[512]; - int status; + value_t values[2]; + value_list_t vl = VALUE_LIST_INIT; - status = snprintf (file, 512, disk_filename_template, inst); - if (status < 1) - return; - else if (status >= 512) - return; + values[0].counter = read; + values[1].counter = write; - rrd_update_file (host, file, val, disk_ds_def, disk_ds_num); -} + vl.values = values; + vl.values_len = 2; + vl.time = time (NULL); + strcpy (vl.host, hostname); + strcpy (vl.plugin, "disk"); + strncpy (vl.plugin_instance, plugin_instance, + sizeof (vl.plugin_instance)); -static void partition_write (char *host, char *inst, char *val) -{ - char file[512]; - int status; - - status = snprintf (file, 512, part_filename_template, inst); - if (status < 1) - return; - else if (status >= 512) - return; - - rrd_update_file (host, file, val, part_ds_def, part_ds_num); -} - -#if DISK_HAVE_READ -#define BUFSIZE 512 -static void disk_submit (char *disk_name, - unsigned long long read_count, - unsigned long long read_merged, - unsigned long long read_bytes, - unsigned long long read_time, - unsigned long long write_count, - unsigned long long write_merged, - unsigned long long write_bytes, - unsigned long long write_time) -{ - char buf[BUFSIZE]; - - if (snprintf (buf, BUFSIZE, "%u:%llu:%llu:%llu:%llu:%llu:%llu:%llu:%llu", - (unsigned int) curtime, - read_count, read_merged, read_bytes, read_time, - write_count, write_merged, write_bytes, - write_time) >= BUFSIZE) - return; - - DBG ("disk_name = %s; buf = %s;", - disk_name, buf); - - plugin_submit (MODULE_NAME, disk_name, buf); -} - -#if KERNEL_LINUX || HAVE_LIBKSTAT -static void partition_submit (char *part_name, - unsigned long long read_count, - unsigned long long read_bytes, - unsigned long long write_count, - unsigned long long write_bytes) -{ - char buf[BUFSIZE]; - - if (snprintf (buf, BUFSIZE, "%u:%llu:%llu:%llu:%llu", - (unsigned int) curtime, - read_count, read_bytes, write_count, - write_bytes) >= BUFSIZE) - return; - - plugin_submit ("partition", part_name, buf); -} -#endif /* KERNEL_LINUX || HAVE_LIBKSTAT */ -#undef BUFSIZE + plugin_dispatch_values (type, &vl); +} /* void disk_submit */ #if HAVE_IOKIT_IOKITLIB_H static signed long long dict_get_value (CFDictionaryRef dict, const char *key) @@ -293,7 +255,7 @@ static signed long long dict_get_value (CFDictionaryRef dict, const char *key) } #endif /* HAVE_IOKIT_IOKITLIB_H */ -static void disk_read (void) +static int disk_read (void) { #if HAVE_IOKIT_IOKITLIB_H io_registry_entry_t disk; @@ -324,7 +286,7 @@ static void disk_read (void) { plugin_complain (LOG_ERR, &complain_obj, "disk plugin: " "IOServiceGetMatchingServices failed."); - return; + return (-1); } else if (complain_obj.interval != 0) { @@ -395,6 +357,7 @@ static void disk_read (void) continue; } + /* kIOBSDNameKey */ disk_major = (int) dict_get_value (child_dict, kIOBSDMajorKey); disk_minor = (int) dict_get_value (child_dict, @@ -409,6 +372,11 @@ static void disk_read (void) kIOBlockStorageDriverStatisticsWritesKey); write_byt = dict_get_value (stats_dict, kIOBlockStorageDriverStatisticsBytesWrittenKey); + /* This property describes the number of nanoseconds spent + * performing writes since the block storage driver was + * instantiated. It is one of the statistic entries listed + * under the top-level kIOBlockStorageDriverStatisticsKey + * property table. It has an OSNumber value. */ write_tme = dict_get_value (stats_dict, kIOBlockStorageDriverStatisticsTotalWriteTimeKey); @@ -423,15 +391,14 @@ static void disk_read (void) } DBG ("disk_name = %s", disk_name); - if ((read_ops != -1LL) - || (read_byt != -1LL) - || (read_tme != -1LL) - || (write_ops != -1LL) - || (write_byt != -1LL) - || (write_tme != -1LL)) - disk_submit (disk_name, - read_ops, 0ULL, read_byt, read_tme, - write_ops, 0ULL, write_byt, write_tme); + if ((read_byt != -1LL) || (write_byt != -1LL)) + disk_submit (disk_name, "disk_octets", read_byt, write_byt); + if ((read_ops != -1LL) || (write_ops != -1LL)) + disk_submit (disk_name, "disk_ops", read_ops, write_ops); + if ((read_tme != -1LL) || (write_tme != -1LL)) + disk_submit (disk_name, "disk_time", + read_tme / 1000, + write_tme / 1000); CFRelease (child_dict); IOObjectRelease (disk_child); @@ -444,7 +411,6 @@ static void disk_read (void) #elif KERNEL_LINUX FILE *fh; char buffer[1024]; - char disk_name[128]; char *fields[32]; int numfields; @@ -453,17 +419,17 @@ static void disk_read (void) int major = 0; int minor = 0; - unsigned long long read_sectors = 0ULL; - unsigned long long write_sectors = 0ULL; - - unsigned long long read_count = 0ULL; - unsigned long long read_merged = 0ULL; - unsigned long long read_bytes = 0ULL; - unsigned long long read_time = 0ULL; - unsigned long long write_count = 0ULL; - unsigned long long write_merged = 0ULL; - unsigned long long write_bytes = 0ULL; - unsigned long long write_time = 0ULL; + counter_t read_sectors = 0; + counter_t write_sectors = 0; + + counter_t read_count = 0; + counter_t read_merged = 0; + counter_t read_bytes = 0; + counter_t read_time = 0; + counter_t write_count = 0; + counter_t write_merged = 0; + counter_t write_bytes = 0; + counter_t write_time = 0; int is_disk = 0; diskstats_t *ds, *pre_ds; @@ -474,18 +440,23 @@ static void disk_read (void) { if ((fh = fopen ("/proc/partitions", "r")) == NULL) { - plugin_complain (LOG_ERR, &complain_obj, "disk plugin: Failed to open /proc/{diskstats,partitions}."); - return; + plugin_complain (LOG_ERR, &complain_obj, + "disk plugin: Failed to open /proc/" + "{diskstats,partitions}."); + return (-1); } /* Kernel is 2.4.* */ fieldshift = 1; } - plugin_relief (LOG_NOTICE, &complain_obj, "disk plugin: Succeeded to open /proc/{diskstats,partitions}."); + plugin_relief (LOG_NOTICE, &complain_obj, "disk plugin: " + "Succeeded to open /proc/{diskstats,partitions}."); - while (fgets (buffer, 1024, fh) != NULL) + while (fgets (buffer, sizeof (buffer), fh) != NULL) { + char *disk_name; + numfields = strsplit (buffer, fields, 32); if ((numfields != (14 + fieldshift)) && (numfields != 7)) @@ -494,9 +465,7 @@ static void disk_read (void) major = atoll (fields[0]); minor = atoll (fields[1]); - if (snprintf (disk_name, 128, "%i-%i", major, minor) < 1) - continue; - disk_name[127] = '\0'; + disk_name = fields[2]; for (ds = disklist, pre_ds = disklist; ds != NULL; pre_ds = ds, ds = ds->next) if (strcmp (disk_name, ds->name) == 0) @@ -582,12 +551,21 @@ static void disk_read (void) continue; } + if ((read_bytes != -1LL) || (write_bytes != -1LL)) + disk_submit (disk_name, "disk_octets", read_bytes, write_bytes); + if ((read_count != -1LL) || (write_count != -1LL)) + disk_submit (disk_name, "disk_ops", read_count, write_count); if (is_disk) - disk_submit (disk_name, read_count, read_merged, read_bytes, read_time, - write_count, write_merged, write_bytes, write_time); - else - partition_submit (disk_name, read_count, read_bytes, write_count, write_bytes); - } + { + if ((read_merged != -1LL) || (write_merged != -1LL)) + disk_submit (disk_name, "disk_merged", + read_merged, write_merged); + if ((read_time != -1LL) || (write_time != -1LL)) + disk_submit (disk_name, "disk_time", + read_time * 1000, + write_time * 1000); + } + } /* while (fgets (buffer, sizeof (buffer), fh) != NULL) */ fclose (fh); /* #endif defined(KERNEL_LINUX) */ @@ -597,7 +575,7 @@ static void disk_read (void) int i; if (kc == NULL) - return; + return (-1); for (i = 0; i < numdisk; i++) { @@ -605,24 +583,33 @@ static void disk_read (void) continue; if (strncmp (ksp[i]->ks_class, "disk", 4) == 0) - disk_submit (ksp[i]->ks_name, - kio.reads, 0LL, kio.nread, kio.rtime, - kio.writes, 0LL, kio.nwritten, kio.wtime); + { + disk_submit (ksp[i]->ks_name, "disk_octets", kio.reads, kio.writes); + disk_submit (ksp[i]->ks_name, "disk_ops", kio.nreads, kio.nwrites); + /* FIXME: Convert this to microseconds if necessary */ + disk_submit (ksp[i]->ks_name, "disk_time", kio.rtime, kio.wtime); + } else if (strncmp (ksp[i]->ks_class, "partition", 9) == 0) - partition_submit (ksp[i]->ks_name, - kio.reads, kio.nread, - kio.writes,kio.nwritten); + { + disk_submit (ksp[i]->ks_name, "disk_octets", kio.reads, kio.writes); + disk_submit (ksp[i]->ks_name, "disk_ops", kio.nreads, kio.nwrites); + } } #endif /* defined(HAVE_LIBKSTAT) */ -} /* static void disk_read (void) */ -#else -# define disk_read NULL + + return (0); +} /* int disk_read */ #endif /* DISK_HAVE_READ */ void module_register (void) { - plugin_register ("partition", NULL, NULL, partition_write); - plugin_register (MODULE_NAME, disk_init, disk_read, disk_write); -} + plugin_register_data_set (&octets_ds); + plugin_register_data_set (&operations_ds); + plugin_register_data_set (&merged_ds); + plugin_register_data_set (&time_ds); -#undef MODULE_NAME +#if DISK_HAVE_READ + plugin_register_init ("disk", disk_init); + plugin_register_read ("disk", disk_read); +#endif /* DISK_HAVE_READ */ +} diff --git a/src/plugin.c b/src/plugin.c index b40d62a1..2daeea9f 100644 --- a/src/plugin.c +++ b/src/plugin.c @@ -29,6 +29,17 @@ #include "utils_debug.h" /* + * Private structures + */ +struct read_func_s +{ + int wait_time; + int wait_left; + int (*callback) (void); +}; +typedef struct read_func_s read_func_t; + +/* * Private variables */ static llist_t *list_init; @@ -77,6 +88,21 @@ static int register_callback (llist_t **list, const char *name, void *callback) return (0); } /* int register_callback */ +static int plugin_unregister (llist_t *list, const char *name) +{ + llentry_t *e; + + e = llist_search (list, name); + + if (e == NULL) + return (-1); + + llist_remove (list, e); + llentry_destroy (e); + + return (0); +} /* int plugin_unregister */ + /* * (Try to) load the shared object `file'. Won't complain if it isn't a shared * object, but it will bitch about a shared object not having a @@ -215,7 +241,22 @@ int plugin_register_init (const char *name, int plugin_register_read (const char *name, int (*callback) (void)) { - return (register_callback (&list_read, name, (void *) callback)); + read_func_t *rf; + + rf = (read_func_t *) malloc (sizeof (read_func_t)); + if (rf == NULL) + { + syslog (LOG_ERR, "plugin_register_read: malloc failed: %s", + strerror (errno)); + return (-1); + } + + memset (rf, '\0', sizeof (read_func_t)); + rf->wait_time = atoi (COLLECTD_STEP); + rf->wait_left = 0; + rf->callback = callback; + + return (register_callback (&list_read, name, (void *) rf)); } /* int plugin_register_read */ int plugin_register_write (const char *name, @@ -235,6 +276,43 @@ int plugin_register_data_set (const data_set_t *ds) return (register_callback (&list_data_set, ds->type, (void *) ds)); } /* int plugin_register_data_set */ +int plugin_unregister_init (const char *name) +{ + return (plugin_unregister (list_init, name)); +} + +int plugin_unregister_read (const char *name) +{ + return (plugin_unregister (list_read, name)); + llentry_t *e; + + e = llist_search (list_read, name); + + if (e == NULL) + return (-1); + + llist_remove (list_read, e); + free (e->value); + llentry_destroy (e); + + return (0); +} + +int plugin_unregister_write (const char *name) +{ + return (plugin_unregister (list_write, name)); +} + +int plugin_unregister_shutdown (const char *name) +{ + return (plugin_unregister (list_shutdown, name)); +} + +int plugin_unregister_data_set (const char *name) +{ + return (plugin_unregister (list_data_set, name)); +} + void plugin_init_all (void) { int (*callback) (void); @@ -257,20 +335,49 @@ void plugin_init_all (void) void plugin_read_all (const int *loop) { - int (*callback) (void); - llentry_t *le; + llentry_t *le; + read_func_t *rf; + int status; + int step; if (list_read == NULL) return; + step = atoi (COLLECTD_STEP); + le = llist_head (list_read); while ((*loop == 0) && (le != NULL)) { - callback = le->value; - (*callback) (); + rf = (read_func_t *) le->value; + + if (rf->wait_left > 0) + rf->wait_left -= step; + if (rf->wait_left > 0) + { + le = le->next; + continue; + } + + status = rf->callback (); + if (status != 0) + { + rf->wait_left = rf->wait_time; + rf->wait_time = rf->wait_time * 2; + if (rf->wait_time > 86400) + rf->wait_time = 86400; + + syslog (LOG_NOTICE, "read-function of plugin `%s' " + "failed. Will syspend it for %i " + "seconds.", le->key, rf->wait_left); + } + else + { + rf->wait_left = 0; + rf->wait_time = step; + } le = le->next; - } + } /* while ((*loop == 0) && (le != NULL)) */ } /* void plugin_read_all */ void plugin_shutdown_all (void) diff --git a/src/plugin.h b/src/plugin.h index 107078e7..b0bdbee1 100644 --- a/src/plugin.h +++ b/src/plugin.h @@ -138,6 +138,12 @@ int plugin_register_shutdown (char *name, int (*callback) (void)); int plugin_register_data_set (const data_set_t *ds); +int plugin_unregister_init (const char *name); +int plugin_unregister_read (const char *name); +int plugin_unregister_write (const char *name); +int plugin_unregister_shutdown (const char *name); +int plugin_unregister_data_set (const char *name); + /* * NAME * plugin_dispatch_values diff --git a/src/unixsock.c b/src/unixsock.c new file mode 100644 index 00000000..de1fa4fd --- /dev/null +++ b/src/unixsock.c @@ -0,0 +1,687 @@ +/** + * collectd - src/unixsock.c + * Copyright (C) 2007 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 + * + * Author: + * Florian octo Forster + **/ + +#include "collectd.h" +#include "common.h" +#include "plugin.h" +#include "configfile.h" +#include "utils_debug.h" + +/* Folks without pthread will need to disable this plugin. */ +#include + +#include +#include +#include + +#include + +#ifndef UNIX_PATH_MAX +# define UNIX_PATH_MAX sizeof (((struct sockaddr_un *)0)->sun_path) +#endif + +#define US_DEFAULT_PATH PREFIX"/var/run/"PACKAGE_NAME"-unixsock" + +/* + * Private data structures + */ +/* linked list of cached values */ +typedef struct value_cache_s +{ + char name[4*DATA_MAX_NAME_LEN]; + int values_num; + gauge_t *gauge; + counter_t *counter; + const data_set_t *ds; + time_t time; + struct value_cache_s *next; +} value_cache_t; + +/* + * Private variables + */ +/* valid configuration file keys */ +static const char *config_keys[] = +{ + "SocketFile", + "SocketGroup", + "SocketPerms", + NULL +}; +static int config_keys_num = 3; + +/* socket configuration */ +static int sock_fd = -1; +static char *sock_file = NULL; +static char *sock_group = NULL; +static int sock_perms = S_IRWXU | S_IRWXG; + +static pthread_t listen_thread = -1; + +/* Linked list and auxilliary variables for saving values */ +static value_cache_t *cache_head = NULL; +static pthread_mutex_t cache_lock = PTHREAD_MUTEX_INITIALIZER; +static unsigned int cache_oldest = UINT_MAX; + +/* + * Functions + */ +static value_cache_t *cache_search (const char *name) +{ + value_cache_t *vc; + + for (vc = cache_head; vc != NULL; vc = vc->next) + { + if (strcmp (vc->name, name) == 0) + break; + } /* for vc = cache_head .. NULL */ + + return (vc); +} /* value_cache_t *cache_search */ + +static int cache_alloc_name (char *ret, int ret_len, + const char *hostname, + const char *plugin, const char *plugin_instance, + const char *type, const char *type_instance) +{ + int status; + + assert (plugin != NULL); + assert (type != NULL); + + if ((plugin_instance == NULL) || (strlen (plugin_instance) == 0)) + { + if ((type_instance == NULL) || (strlen (type_instance) == 0)) + status = snprintf (ret, ret_len, "%s/%s/%s", + hostname, plugin, type); + else + status = snprintf (ret, ret_len, "%s/%s/%s-%s", + hostname, plugin, type, type_instance); + } + else + { + if ((type_instance == NULL) || (strlen (type_instance) == 0)) + status = snprintf (ret, ret_len, "%s/%s-%s/%s", + hostname, plugin, plugin_instance, type); + else + status = snprintf (ret, ret_len, "%s/%s-%s/%s-%s", + hostname, plugin, plugin_instance, type, type_instance); + } + + if ((status < 1) || (status >= ret_len)) + return (-1); + return (0); +} /* int cache_alloc_name */ + +static int cache_insert (const data_set_t *ds, const value_list_t *vl) +{ + /* We're called from `cache_update' so we don't need to lock the mutex */ + value_cache_t *vc; + int i; + + DBG ("ds->ds_num = %i; vl->values_len = %i;", + ds->ds_num, vl->values_len); + assert (ds->ds_num == vl->values_len); + + vc = (value_cache_t *) malloc (sizeof (value_cache_t)); + if (vc == NULL) + { + pthread_mutex_unlock (&cache_lock); + syslog (LOG_ERR, "unixsock plugin: malloc failed: %s", + strerror (errno)); + return (-1); + } + + vc->gauge = (gauge_t *) malloc (sizeof (gauge_t) * vl->values_len); + if (vc->gauge == NULL) + { + pthread_mutex_unlock (&cache_lock); + syslog (LOG_ERR, "unixsock plugin: malloc failed: %s", + strerror (errno)); + free (vc); + return (-1); + } + + vc->counter = (counter_t *) malloc (sizeof (counter_t) * vl->values_len); + if (vc->counter == NULL) + { + pthread_mutex_unlock (&cache_lock); + syslog (LOG_ERR, "unixsock plugin: malloc failed: %s", + strerror (errno)); + free (vc->gauge); + free (vc); + return (-1); + } + + if (cache_alloc_name (vc->name, sizeof (vc->name), + vl->host, vl->plugin, vl->plugin_instance, + ds->type, vl->type_instance) != 0) + { + pthread_mutex_unlock (&cache_lock); + syslog (LOG_ERR, "unixsock plugin: cache_alloc_name failed."); + free (vc->counter); + free (vc->gauge); + free (vc); + return (-1); + } + + for (i = 0; i < ds->ds_num; i++) + { + if (ds->ds[i].type == DS_TYPE_COUNTER) + { + vc->gauge[i] = 0.0; + vc->counter[i] = vl->values[i].counter; + } + else if (ds->ds[i].type == DS_TYPE_GAUGE) + { + vc->gauge[i] = vl->values[i].gauge; + vc->counter[i] = 0; + } + else + { + vc->gauge[i] = 0.0; + vc->counter[i] = 0; + } + } + vc->values_num = ds->ds_num; + vc->ds = ds; + + vc->next = cache_head; + cache_head = vc; + + vc->time = vl->time; + if (vc->time < cache_oldest) + cache_oldest = vc->time; + + pthread_mutex_unlock (&cache_lock); + return (0); +} /* int cache_insert */ + +static int cache_update (const data_set_t *ds, const value_list_t *vl) +{ + char name[4*DATA_MAX_NAME_LEN];; + value_cache_t *vc; + int i; + + if (cache_alloc_name (name, sizeof (name), + vl->host, + vl->plugin, vl->plugin_instance, + ds->type, vl->type_instance) != 0) + return (-1); + + pthread_mutex_lock (&cache_lock); + + vc = cache_search (name); + + if (vc == NULL) + return (cache_insert (ds, vl)); + + assert (vc->values_num == ds->ds_num); + assert (vc->values_num == vl->values_len); + + /* + * Update the values. This is possibly a lot more that you'd expect + * because we honor min and max values and handle counter overflows here. + */ + for (i = 0; i < ds->ds_num; i++) + { + if (ds->ds[i].type == DS_TYPE_COUNTER) + { + if (vl->values[i].counter < vc->counter[i]) + { + if (vl->values[i].counter <= 4294967295U) + { + vc->gauge[i] = ((4294967295U - vl->values[i].counter) + + vc->counter[i]) / (vl->time - vc->time); + } + else + { + vc->gauge[i] = ((18446744073709551615ULL - vl->values[i].counter) + + vc->counter[i]) / (vl->time - vc->time); + } + } + else + { + vc->gauge[i] = (vl->values[i].counter - vc->counter[i]) + / (vl->time - vc->time); + } + + vc->counter[i] = vl->values[i].counter; + } + else if (ds->ds[i].type == DS_TYPE_GAUGE) + { + vc->gauge[i] = vl->values[i].gauge; + vc->counter[i] = 0; + } + else + { + vc->gauge[i] = NAN; + vc->counter[i] = 0; + } + + if ((vc->gauge[i] == NAN) + || ((ds->ds[i].min != NAN) && (vc->gauge[i] < ds->ds[i].min)) + || ((ds->ds[i].max != NAN) && (vc->gauge[i] > ds->ds[i].max))) + vc->gauge[i] = NAN; + } /* for i = 0 .. ds->ds_num */ + + vc->ds = ds; + vc->time = vl->time; + + if (vc->time < cache_oldest) + cache_oldest = vc->time; + + pthread_mutex_unlock (&cache_lock); + return (0); +} /* int cache_update */ + +static void cache_flush (int max_age) +{ + value_cache_t *this; + value_cache_t *prev; + time_t now; + + pthread_mutex_lock (&cache_lock); + + now = time (NULL); + + if ((now - cache_oldest) <= max_age) + { + pthread_mutex_unlock (&cache_lock); + return; + } + + cache_oldest = now; + + prev = NULL; + this = cache_head; + + while (this != NULL) + { + if ((now - this->time) <= max_age) + { + if (this->time < cache_oldest) + cache_oldest = this->time; + + prev = this; + this = this->next; + continue; + } + + if (prev == NULL) + cache_head = this->next; + else + prev->next = this->next; + + free (this->gauge); + free (this->counter); + free (this); + + if (prev == NULL) + this = cache_head; + else + this = prev->next; + } /* while (this != NULL) */ + + pthread_mutex_unlock (&cache_lock); +} /* int cache_flush */ + +static int us_open_socket (void) +{ + struct sockaddr_un sa; + int status; + + sock_fd = socket (PF_UNIX, SOCK_STREAM, 0); + if (sock_fd < 0) + { + syslog (LOG_ERR, "unixsock plugin: socket failed: %s", + strerror (errno)); + return (-1); + } + + memset (&sa, '\0', sizeof (sa)); + sa.sun_family = AF_UNIX; + strncpy (sa.sun_path, (sock_file != NULL) ? sock_file : US_DEFAULT_PATH, + sizeof (sa.sun_path) - 1); + /* unlink (sa.sun_path); */ + + status = bind (sock_fd, (struct sockaddr *) &sa, sizeof (sa)); + if (status != 0) + { + DBG ("bind failed: %s; sa.sun_path = %s", + strerror (errno), sa.sun_path); + syslog (LOG_ERR, "unixsock plugin: bind failed: %s", + strerror (errno)); + close (sock_fd); + sock_fd = -1; + return (-1); + } + + status = listen (sock_fd, 8); + if (status != 0) + { + syslog (LOG_ERR, "unixsock plugin: listen failed: %s", + strerror (errno)); + close (sock_fd); + sock_fd = -1; + return (-1); + } + + do + { + struct group *g; + + errno = 0; + g = getgrnam ((sock_group != NULL) ? sock_group : COLLECTD_GRP_NAME); + + if (errno != 0) + { + syslog (LOG_WARNING, "unixsock plugin: getgrnam (%s) failed: %s", + (sock_group != NULL) ? sock_group : COLLECTD_GRP_NAME, + strerror (errno)); + break; + } + + if (g == NULL) + break; + + if (chown ((sock_file != NULL) ? sock_file : US_DEFAULT_PATH, + (uid_t) -1, g->gr_gid) != 0) + { + syslog (LOG_WARNING, "unixsock plugin: chown (%s, -1, %i) failed: %s", + (sock_file != NULL) ? sock_file : US_DEFAULT_PATH, + (int) g->gr_gid, + strerror (errno)); + } + } while (0); + + return (0); +} /* int us_open_socket */ + +static int us_handle_getval (FILE *fh, char **fields, int fields_num) +{ + char *hostname = fields[1]; + char *plugin; + char *plugin_instance; + char *type; + char *type_instance; + char name[4*DATA_MAX_NAME_LEN]; + value_cache_t *vc; + int status; + int i; + + if (fields_num != 2) + return (-1); + + plugin = strchr (hostname, '/'); + if (plugin == NULL) + return (-1); + *plugin = '\0'; plugin++; + + type = strchr (plugin, '/'); + if (type == NULL) + return (-1); + *type = '\0'; type++; + + plugin_instance = strchr (plugin, '-'); + if (plugin_instance != NULL) + { + *plugin_instance = '\0'; + plugin_instance++; + } + + type_instance = strchr (type, '-'); + if (type_instance != NULL) + { + *type_instance = '\0'; + type_instance++; + } + + status = cache_alloc_name (name, sizeof (name), + hostname, plugin, plugin_instance, type, type_instance); + if (status != 0) + return (-1); + + pthread_mutex_lock (&cache_lock); + + DBG ("vc = cache_search (%s)", name); + vc = cache_search (name); + + if (vc == NULL) + { + DBG ("Did not find cache entry."); + fprintf (fh, "-1 No such value"); + } + else + { + DBG ("Found cache entry."); + fprintf (fh, "%i", vc->values_num); + for (i = 0; i < vc->values_num; i++) + { + fprintf (fh, " %s=", vc->ds->ds[i].name); + if (vc->gauge[i] == NAN) + fprintf (fh, "NaN"); + else + fprintf (fh, "%12e", vc->gauge[i]); + } + } + + /* Free the mutex as soon as possible and definitely before flushing */ + pthread_mutex_unlock (&cache_lock); + + fprintf (fh, "\n"); + fflush (fh); + + return (0); +} /* int us_handle_getval */ + +static void *us_handle_client (void *arg) +{ + int fd; + FILE *fh; + char buffer[1024]; + char *fields[128]; + int fields_num; + + fd = *((int *) arg); + free (arg); + arg = NULL; + + DBG ("Reading from fd #%i", fd); + + fh = fdopen (fd, "r+"); + if (fh == NULL) + { + syslog (LOG_ERR, "unixsock plugin: fdopen failed: %s", + strerror (errno)); + close (fd); + pthread_exit ((void *) 1); + } + + while (fgets (buffer, sizeof (buffer), fh) != NULL) + { + int len; + + len = strlen (buffer); + while ((len > 0) + && ((buffer[len - 1] == '\n') || (buffer[len - 1] == '\r'))) + buffer[--len] = '\0'; + + if (len == 0) + continue; + + DBG ("fgets -> buffer = %s; len = %i;", buffer, len); + + fields_num = strsplit (buffer, fields, + sizeof (fields) / sizeof (fields[0])); + + if (fields_num < 1) + { + close (fd); + break; + } + + if (strcasecmp (fields[0], "getval") == 0) + { + us_handle_getval (fh, fields, fields_num); + } + else + { + fprintf (fh, "Unknown command: %s\n", fields[0]); + fflush (fh); + } + } /* while (fgets) */ + + DBG ("Exiting.."); + close (fd); + + pthread_exit ((void *) 0); +} /* void *us_handle_client */ + +static void *us_server_thread (void *arg) +{ + int status; + int *remote_fd; + pthread_t th; + pthread_attr_t th_attr; + + if (us_open_socket () != 0) + pthread_exit ((void *) 1); + + while (42) + { + DBG ("Calling accept.."); + status = accept (sock_fd, NULL, NULL); + if (status < 0) + { + if (errno == EINTR) + continue; + + syslog (LOG_ERR, "unixsock plugin: accept failed: %s", + strerror (errno)); + close (sock_fd); + sock_fd = -1; + pthread_exit ((void *) 1); + } + + remote_fd = (int *) malloc (sizeof (int)); + if (remote_fd == NULL) + { + syslog (LOG_WARNING, "unixsock plugin: malloc failed: %s", + strerror (errno)); + close (status); + continue; + } + *remote_fd = status; + + DBG ("Spawning child to handle connection on fd #%i", *remote_fd); + + pthread_attr_init (&th_attr); + pthread_attr_setdetachstate (&th_attr, PTHREAD_CREATE_DETACHED); + + status = pthread_create (&th, &th_attr, us_handle_client, (void *) remote_fd); + if (status != 0) + { + syslog (LOG_WARNING, "unixsock plugin: pthread_create failed: %s", + strerror (status)); + close (*remote_fd); + free (remote_fd); + continue; + } + } /* while (42) */ + + return ((void *) 0); +} /* void *us_server_thread */ + +static int us_config (const char *key, const char *val) +{ + if (strcasecmp (key, "SocketFile") == 0) + { + sfree (sock_file); + sock_file = strdup (val); + } + else if (strcasecmp (key, "SocketGroup") == 0) + { + sfree (sock_group); + sock_group = strdup (val); + } + else if (strcasecmp (key, "SocketPerms") == 0) + { + sock_perms = (int) strtol (val, NULL, 8); + } + else + { + return (-1); + } + + return (0); +} /* int us_config */ + +static int us_init (void) +{ + int status; + + status = pthread_create (&listen_thread, NULL, us_server_thread, NULL); + if (status != 0) + { + syslog (LOG_ERR, "unixsock plugin: pthread_create failed: %s", + strerror (status)); + return (-1); + } + + return (0); +} /* int us_init */ + +static int us_shutdown (void) +{ + void *ret; + + if (listen_thread >= 0) + { + pthread_kill (listen_thread, SIGTERM); + pthread_join (listen_thread, &ret); + } + + plugin_unregister_init ("unixsock"); + plugin_unregister_write ("unixsock"); + plugin_unregister_shutdown ("unixsock"); + + return (0); +} /* int us_shutdown */ + +static int us_write (const data_set_t *ds, const value_list_t *vl) +{ + cache_update (ds, vl); + cache_flush (2 * atoi (COLLECTD_STEP)); + + return (0); +} + +void module_register (void) +{ + plugin_register_config ("unixsock", us_config, + config_keys, config_keys_num); + plugin_register_init ("unixsock", us_init); + plugin_register_write ("unixsock", us_write); + plugin_register_shutdown ("unixsock", us_shutdown); +} /* void module_register (void) */ + +/* vim: set sw=4 ts=4 sts=4 tw=78 : */