Merge branch 'more-varnish-stats'
authorFlorian Forster <octo@collectd.org>
Sat, 25 May 2013 18:20:48 +0000 (20:20 +0200)
committerFlorian Forster <octo@collectd.org>
Sat, 25 May 2013 18:20:48 +0000 (20:20 +0200)
15 files changed:
AUTHORS
README
configure.in
src/Makefile.am
src/cgroups.c [new file with mode: 0644]
src/collectd.conf.in
src/collectd.conf.pod
src/common.c
src/common.h
src/configfile.c
src/curl_xml.c
src/mic.c [new file with mode: 0644]
src/plugin.c
src/plugin.h
src/thermal.c

diff --git a/AUTHORS b/AUTHORS
index e04e116..9df0f7a 100644 (file)
--- a/AUTHORS
+++ b/AUTHORS
@@ -76,6 +76,9 @@ Doug MacEachern <dougm at hyperic.com>
 Edward “Koko” Konetzko <konetzed at quixoticagony.com>
  - fscache plugin.
 
+Evan Felix <evan.felix at pnnl.gov>
+ - mic plugin.
+
 Fabian Linzberger <e at lefant.net>
  - Percentage aggregation for `collectd-nagios'.
 
@@ -129,6 +132,7 @@ Michael Hanselmann <public at hansmi.ch>
 
 Michael Stapelberg <michael+git at stapelberg.de>
  - OpenBSD port of the tcpconns plugin.
+ - cgroups plugin.
 
 Michał Mirosław <mirq-linux at rere.qmqm.pl>
  - thermal plugin.
diff --git a/README b/README
index 1c23aaa..04e87ab 100644 (file)
--- a/README
+++ b/README
@@ -37,6 +37,9 @@ Features
       Name server and resolver statistics from the `statistics-channel'
       interface of BIND 9.5, 9,6 and later.
 
+    - cgroups
+      CPU accounting information for process groups under Linux.
+
     - conntrack
       Number of nf_conntrack entries.
 
@@ -162,6 +165,10 @@ Features
       Memory utilization: Memory occupied by running processes, page cache,
       buffer cache and free.
 
+    - mic
+      Collects CPU usage, memory usage, temperatures and power consumption from
+      Intel Many Integrated Core (MIC) CPUs.
+
     - modbus
       Reads values from Modbus/TCP enabled devices. Supports reading values
       from multiple "slaves" so gateway devices can be used.
index 144f289..ba52389 100644 (file)
@@ -4204,6 +4204,63 @@ fi
 AM_CONDITIONAL(BUILD_WITH_LIBYAJL, test "x$with_libyajl" = "xyes")
 # }}}
 
+# --with-mic {{{
+with_mic_cflags="-I/opt/intel/mic/sysmgmt/sdk/include"
+with_mic_ldpath="-L/opt/intel/mic/sysmgmt/sdk/lib/Linux"
+with_mic_libs=""
+AC_ARG_WITH(mic,[AS_HELP_STRING([--with-mic@<:@=PREFIX@:>@], [Path to Intel MIC Access API.])],
+[
+       if test "x$withval" = "xno"
+       then
+               with_mic="no"
+       else if test "x$withval" = "xyes"
+       then
+               with_mic="yes"
+       else if test -d "$with_mic/lib"
+       then
+               AC_MSG_NOTICE([Not checking for Intel Mic: Manually configured])
+               with_mic_cflags="-I$withval/include"
+               with_mic_ldpath="-L$withval/lib/Linux"
+               with_mic_libs="-lMicAccessSDK -lscif -lpthread"
+               with_mic="yes"
+       fi; fi; fi
+],
+[with_mic="yes"])
+if test "x$with_mic" = "xyes"
+then
+       SAVE_CPPFLAGS="$CPPFLAGS"
+       CPPFLAGS="$CPPFLAGS $with_mic_cflags"
+       AC_CHECK_HEADERS(MicAccessApi.h,[],[with_mic="no (MicAccessApi not found)"])
+       CPPFLAGS="$SAVE_CPPFLAGS"
+fi
+if test "x$with_mic" = "xyes"
+then
+       SAVE_CPPFLAGS="$CPPFLAGS"
+       SAVE_LDFLAGS="$LDFLAGS"
+
+       CPPFLAGS="$CPPFLAGS $with_mic_cflags"
+       LDFLAGS="$LDFLAGS $with_mic_ldpath"
+
+       AC_CHECK_LIB(MicAccessSDK, MicInitAPI,
+                       [with_mic_ldpath="$with_mic_ldpath"
+                       with_mic_libs="-lMicAccessSDK -lscif -lpthread"],
+                       [with_mic="no (symbol MicInitAPI not found)"],[-lscif -lpthread])
+
+       CPPFLAGS="$SAVE_CPPFLAGS"
+       LDFLAGS="$SAVE_LDFLAGS"
+fi
+
+if test "x$with_mic" = "xyes"
+then
+       BUILD_WITH_MIC_CPPFLAGS="$with_mic_cflags"
+       BUILD_WITH_MIC_LIBPATH="$with_mic_ldpath"
+       BUILD_WITH_MIC_LDADD="$with_mic_libs"
+       AC_SUBST(BUILD_WITH_MIC_CPPFLAGS)
+       AC_SUBST(BUILD_WITH_MIC_LIBPATH)
+       AC_SUBST(BUILD_WITH_MIC_LDADD)
+fi
+#}}}
+
 # --with-libvarnish {{{
 with_libvarnish_cppflags=""
 with_libvarnish_cflags=""
@@ -4630,6 +4687,7 @@ dependency_error="no"
 plugin_ascent="no"
 plugin_battery="no"
 plugin_bind="no"
+plugin_cgroups="no"
 plugin_conntrack="no"
 plugin_contextswitch="no"
 plugin_cpu="no"
@@ -4673,6 +4731,7 @@ then
        plugin_battery="yes"
        plugin_conntrack="yes"
        plugin_contextswitch="yes"
+       plugin_cgroups="yes"
        plugin_cpu="yes"
        plugin_cpufreq="yes"
        plugin_disk="yes"
@@ -4956,6 +5015,7 @@ AC_PLUGIN([csv],         [yes],                [CSV output plugin])
 AC_PLUGIN([curl],        [$with_libcurl],      [CURL generic web statistics])
 AC_PLUGIN([curl_json],   [$plugin_curl_json],    [CouchDB statistics])
 AC_PLUGIN([curl_xml],   [$plugin_curl_xml],    [CURL generic xml statistics])
+AC_PLUGIN([cgroups],     [$plugin_cgroups],    [CGroups CPU usage accounting])
 AC_PLUGIN([dbi],         [$with_libdbi],       [General database statistics])
 AC_PLUGIN([df],          [$plugin_df],         [Filesystem usage statistics])
 AC_PLUGIN([disk],        [$plugin_disk],       [Disk usage statistics])
@@ -4990,6 +5050,7 @@ AC_PLUGIN([md],          [$have_linux_raid_md_u_h], [md (Linux software RAID) de
 AC_PLUGIN([memcachec],   [$with_libmemcached], [memcachec statistics])
 AC_PLUGIN([memcached],   [yes],                [memcached statistics])
 AC_PLUGIN([memory],      [$plugin_memory],     [Memory usage])
+AC_PLUGIN([mic],         [$with_mic],          [Intel Many Integrated Core stats])
 AC_PLUGIN([modbus],      [$with_libmodbus],    [Modbus plugin])
 AC_PLUGIN([multimeter],  [$plugin_multimeter], [Read multimeter values])
 AC_PLUGIN([mysql],       [$with_libmysql],     [MySQL statistics])
@@ -5225,6 +5286,7 @@ cat <<EOF;
 
 Configuration:
   Libraries:
+    intel mic . . . . . . $with_mic
     libcurl . . . . . . . $with_libcurl
     libdbi  . . . . . . . $with_libdbi
     libcredis . . . . . . $with_libcredis
@@ -5286,6 +5348,7 @@ Configuration:
     bind  . . . . . . . . $enable_bind
     conntrack . . . . . . $enable_conntrack
     contextswitch . . . . $enable_contextswitch
+    cgroups . . . . . . . $enable_cgroups
     cpu . . . . . . . . . $enable_cpu
     cpufreq . . . . . . . $enable_cpufreq
     csv . . . . . . . . . $enable_csv
index e969888..d67deca 100644 (file)
@@ -230,6 +230,14 @@ collectd_LDADD += "-dlopen" bind.la
 collectd_DEPENDENCIES += bind.la
 endif
 
+if BUILD_PLUGIN_CGROUPS
+pkglib_LTLIBRARIES += cgroups.la
+cgroups_la_SOURCES = cgroups.c utils_mount.c utils_mount.h
+cgroups_la_LDFLAGS = -module -avoid-version
+collectd_LDADD += "-dlopen" cgroups.la
+collectd_DEPENDENCIES += cgroups.la
+endif
+
 if BUILD_PLUGIN_CONNTRACK
 pkglib_LTLIBRARIES += conntrack.la
 conntrack_la_SOURCES = conntrack.c
@@ -1280,6 +1288,16 @@ collectd_LDADD += "-dlopen" uuid.la
 collectd_DEPENDENCIES += uuid.la
 endif
 
+if BUILD_PLUGIN_MIC
+pkglib_LTLIBRARIES += mic.la
+mic_la_SOURCES = mic.c
+mic_la_LDFLAGS = -module -avoid-version $(BUILD_WITH_MIC_LIBPATH)
+mic_la_CFLAGS = $(AM_CFLAGS) $(BUILD_WITH_MIC_CPPFLAGS)
+mic_la_LIBADD = $(BUILD_WITH_MIC_LDADD)
+collectd_LDADD += "-dlopen" mic.la
+collectd_DEPENDENCIES += mic.la
+endif
+
 if BUILD_PLUGIN_VARNISH
 pkglib_LTLIBRARIES += varnish.la
 varnish_la_SOURCES = varnish.c
diff --git a/src/cgroups.c b/src/cgroups.c
new file mode 100644 (file)
index 0000000..ffb1740
--- /dev/null
@@ -0,0 +1,251 @@
+/**
+ * collectd - src/cgroups.c
+ * Copyright (C) 2011  Michael Stapelberg
+ * Copyright (C) 2013  Florian 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:
+ *   Michael Stapelberg <michael at stapelberg.de>
+ *   Florian Forster <octo at collectd.org>
+ **/
+
+#include "collectd.h"
+#include "common.h"
+#include "plugin.h"
+#include "configfile.h"
+#include "utils_mount.h"
+#include "utils_ignorelist.h"
+
+static char const *config_keys[] =
+{
+       "CGroup",
+       "IgnoreSelected"
+};
+static int config_keys_num = STATIC_ARRAY_SIZE (config_keys);
+
+static ignorelist_t *il_cgroup = NULL;
+
+__attribute__ ((nonnull(1)))
+__attribute__ ((nonnull(2)))
+static void cgroups_submit_one (char const *plugin_instance,
+               char const *type_instance, value_t value)
+{
+       value_list_t vl = VALUE_LIST_INIT;
+
+       vl.values = &value;
+       vl.values_len = 1;
+       sstrncpy (vl.host, hostname_g, sizeof (vl.host));
+       sstrncpy (vl.plugin, "cgroups", sizeof (vl.plugin));
+       sstrncpy (vl.plugin_instance, plugin_instance,
+                       sizeof (vl.plugin_instance));
+       sstrncpy (vl.type, "cpu", sizeof (vl.type));
+       sstrncpy (vl.type_instance, type_instance,
+                       sizeof (vl.type_instance));
+
+       plugin_dispatch_values (&vl);
+} /* void cgroups_submit_one */
+
+/*
+ * This callback reads the user/system CPU time for each cgroup.
+ */
+static int read_cpuacct_procs (const char *dirname, char const *cgroup_name,
+    void *user_data)
+{
+       char abs_path[PATH_MAX];
+       struct stat statbuf;
+       char buf[1024];
+       int status;
+
+       FILE *fh;
+
+       if (ignorelist_match (il_cgroup, cgroup_name))
+               return (0);
+
+       ssnprintf (abs_path, sizeof (abs_path), "%s/%s", dirname, cgroup_name);
+
+       status = lstat (abs_path, &statbuf);
+       if (status != 0)
+       {
+               ERROR ("cgroups plugin: stat (\"%s\") failed.",
+                               abs_path);
+               return (-1);
+       }
+
+       /* We are only interested in directories, so skip everything else. */
+       if (!S_ISDIR (statbuf.st_mode))
+               return (0);
+
+       ssnprintf (abs_path, sizeof (abs_path), "%s/%s/cpuacct.stat",
+                       dirname, cgroup_name);
+       fh = fopen (abs_path, "r");
+       if (fh == NULL)
+       {
+               char errbuf[1024];
+               ERROR ("cgroups pluign: fopen (\"%s\") failed: %s",
+                               abs_path,
+                               sstrerror (errno, errbuf, sizeof (errbuf)));
+               return (-1);
+       }
+
+       while (fgets (buf, sizeof (buf), fh) != NULL)
+       {
+               char *fields[8];
+               int numfields = 0;
+               char *key;
+               size_t key_len;
+               value_t value;
+
+               /* Expected format:
+                *
+                *   user: 12345
+                *   system: 23456
+                */
+               strstripnewline (buf);
+               numfields = strsplit (buf, fields, STATIC_ARRAY_SIZE (fields));
+               if (numfields != 2)
+                       continue;
+
+               key = fields[0];
+               key_len = strlen (key);
+               if (key_len < 2)
+                       continue;
+
+               /* Strip colon off the first column */
+               if (key[key_len - 1] != ':')
+                       continue;
+               key[key_len - 1] = 0;
+
+               status = parse_value (fields[1], &value, DS_TYPE_DERIVE);
+               if (status != 0)
+                       continue;
+
+               cgroups_submit_one (cgroup_name, key, value);
+       }
+
+       fclose (fh);
+       return (0);
+} /* int read_cpuacct_procs */
+
+/*
+ * Gets called for every file/folder in /sys/fs/cgroup/cpu,cpuacct (or
+ * whereever cpuacct is mounted on the system). Calls walk_directory with the
+ * read_cpuacct_procs callback on every folder it finds, such as "system".
+ */
+static int read_cpuacct_root (const char *dirname, const char *filename,
+               void *user_data)
+{
+       char abs_path[PATH_MAX];
+       struct stat statbuf;
+       int status;
+
+       ssnprintf (abs_path, sizeof (abs_path), "%s/%s", dirname, filename);
+
+       status = lstat (abs_path, &statbuf);
+       if (status != 0)
+       {
+               ERROR ("cgroups plugin: stat (%s) failed.", abs_path);
+               return (-1);
+       }
+
+       if (S_ISDIR (statbuf.st_mode))
+       {
+               status = walk_directory (abs_path, read_cpuacct_procs,
+                               /* user_data = */ NULL,
+                               /* include_hidden = */ 0);
+               return (status);
+       }
+
+       return (0);
+}
+
+static int cgroups_init (void)
+{
+       if (il_cgroup == NULL)
+               il_cgroup = ignorelist_create (1);
+
+       return (0);
+}
+
+static int cgroups_config (const char *key, const char *value)
+{
+       cgroups_init ();
+
+       if (strcasecmp (key, "CGroup") == 0)
+       {
+               if (ignorelist_add (il_cgroup, value))
+                       return (1);
+               return (0);
+       }
+       else if (strcasecmp (key, "IgnoreSelected") == 0)
+       {
+               if (IS_TRUE (value))
+                       ignorelist_set_invert (il_cgroup, 0);
+               else
+                       ignorelist_set_invert (il_cgroup, 1);
+               return (0);
+       }
+
+       return (-1);
+}
+
+static int cgroups_read (void)
+{
+       cu_mount_t *mnt_list;
+       cu_mount_t *mnt_ptr;
+       _Bool cgroup_found = 0;
+
+       mnt_list = NULL;
+       if (cu_mount_getlist (&mnt_list) == NULL)
+       {
+               ERROR ("cgroups plugin: cu_mount_getlist failed.");
+               return (-1);
+       }
+
+       for (mnt_ptr = mnt_list; mnt_ptr != NULL; mnt_ptr = mnt_ptr->next)
+       {
+               /* Find the cgroup mountpoint which contains the cpuacct
+                * controller. */
+               if (strcmp(mnt_ptr->type, "cgroup") != 0 ||
+                       !cu_mount_getoptionvalue(mnt_ptr->options, "cpuacct"))
+                       continue;
+
+               walk_directory (mnt_ptr->dir, read_cpuacct_root,
+                               /* user_data = */ NULL,
+                               /* include_hidden = */ 0);
+               cgroup_found = 1;
+               /* It doesn't make sense to check other cpuacct mount-points
+                * (if any), they contain the same data. */
+               break;
+       }
+
+       cu_mount_freelist (mnt_list);
+
+       if (!cgroup_found)
+       {
+               WARNING ("cgroups plugin: Unable to find cgroup "
+                               "mount-point with the \"cpuacct\" option.");
+               return (-1);
+       }
+
+       return (0);
+} /* int cgroup_read */
+
+void module_register (void)
+{
+       plugin_register_config ("cgroups", cgroups_config,
+                       config_keys, config_keys_num);
+       plugin_register_init ("cgroups", cgroups_init);
+       plugin_register_read ("cgroups", cgroups_read);
+} /* void module_register */
index 467b4ce..4a88e66 100644 (file)
 #TypesDB     "@prefix@/share/@PACKAGE_NAME@/types.db"
 
 #----------------------------------------------------------------------------#
+# When enabled, plugins are loaded automatically with the default options    #
+# when an appropriate <Plugin ...> block is encountered.                     #
+# Disabled by default.                                                       #
+#----------------------------------------------------------------------------#
+#AutoLoadPlugin false
+
+#----------------------------------------------------------------------------#
 # Interval at which to query values. This may be overwritten on a per-plugin #
 # base by using the 'Interval' option of the LoadPlugin block:               #
 #   <LoadPlugin foo>                                                         #
@@ -72,6 +79,7 @@
 #@BUILD_PLUGIN_BIND_TRUE@LoadPlugin bind
 #@BUILD_PLUGIN_CONNTRACK_TRUE@LoadPlugin conntrack
 #@BUILD_PLUGIN_CONTEXTSWITCH_TRUE@LoadPlugin contextswitch
+#@BUILD_PLUGIN_CGROUPS_TRUE@LoadPlugin cgroups
 @BUILD_PLUGIN_CPU_TRUE@@BUILD_PLUGIN_CPU_TRUE@LoadPlugin cpu
 #@BUILD_PLUGIN_CPUFREQ_TRUE@LoadPlugin cpufreq
 @LOAD_PLUGIN_CSV@LoadPlugin csv
 #@BUILD_PLUGIN_USERS_TRUE@LoadPlugin users
 #@BUILD_PLUGIN_UUID_TRUE@LoadPlugin uuid
 #@BUILD_PLUGIN_VARNISH_TRUE@LoadPlugin varnish
+#@BUILD_PLUGIN_MIC_TRUE@LoadPlugin mic
 #@BUILD_PLUGIN_VMEM_TRUE@LoadPlugin vmem
 #@BUILD_PLUGIN_VSERVER_TRUE@LoadPlugin vserver
 #@BUILD_PLUGIN_WIRELESS_TRUE@LoadPlugin wireless
 #      UUIDFile "/etc/uuid"
 #</Plugin>
 
+#<Plugin mic>
+#   ShowCPU true
+#   ShowCPUCores true
+#   ShowMemory true
+#   ShowTemperatures true
+##  Temperature Sensors can be ignored/shown by repeated #Temperature lines, and
+##  then inverted with a IgnoreSelectedTemperature.
+##  Known Temperature sensors: die, devmem, fin, fout, vccp, vddg, vddq
+#   Temperature vddg
+#   IgnoreSelectedTemperature true
+#   ShowPower true
+##  Power Sensors can be ignored/shown by repeated #Power lines, and
+##  then inverted with a IgnoreSelectedTemperature.
+##  Known Temperature sensors: total0, total1, inst, imax, pci3, c2x3, c2x4, vccp, vddg, vddq
+#   Power total1
+#   IgnoreSelectedPower true
+#</Plugin>
+
 #<Plugin varnish>
 #   This tag support an argument if you want to
 #   monitor the local instance just use </Instance>
index 4d15fd2..432efb8 100644 (file)
@@ -68,17 +68,33 @@ directory for the daemon.
 
 =item B<LoadPlugin> I<Plugin>
 
-Loads the plugin I<Plugin>. There must be at least one such line or B<collectd>
-will be mostly useless.
+Loads the plugin I<Plugin>. This is required to load plugins, unless the
+B<AutoLoadPlugin> option is enabled (see below). Without any loaded plugins,
+I<collectd> will be mostly useless.
 
-Starting with collectd 4.9, this may also be a block in which further options
-affecting the behavior of B<LoadPlugin> may be specified. The following
-options are allowed inside a B<LoadPlugin> block:
+Only the first B<LoadPlugin> statement or block for a given plugin name has any
+effect. This is useful when you want to split up the configuration into smaller
+files and want each file to be "self contained", i.e. it contains a B<Plugin>
+block I<and> then appropriate B<LoadPlugin> statement. The downside is that if
+you have multiple conflicting B<LoadPlugin> blocks, e.g. when they specify
+different intervals, only one of them (the first one encountered) will take
+effect and all others will be silently ignored.
 
-  <LoadPlugin perl>
-    Globals true
-    Interval 10
-  </LoadPlugin>
+B<LoadPlugin> may either be a simple configuration I<statement> or a I<block>
+with additional options, affecting the behavior of B<LoadPlugin>. A simple
+statement looks like this:
+
+ LoadPlugin "cpu"
+
+Options inside a B<LoadPlugin> block can override default settings and
+influence the way plugins are loaded, e.g.:
+
+ <LoadPlugin perl>
+   Globals true
+   Interval 60
+ </LoadPlugin>
+
+The following options are valid inside B<LoadPlugin> blocks:
 
 =over 4
 
@@ -109,6 +125,19 @@ interval, that setting will take precedence.
 
 =back
 
+=item B<AutoLoadPlugin> B<false>|B<true>
+
+When set to B<false> (the default), each plugin needs to be loaded explicitly,
+using the B<LoadPlugin> statement documented above. If a
+B<E<lt>PluginE<nbsp>...E<gt>> block is encountered and no configuration
+handling callback for this plugin has been registered, a warning is logged and
+the block is ignored.
+
+When set to B<true>, explicit B<LoadPlugin> statements are not required. Each
+B<E<lt>PluginE<nbsp>...E<gt>> block acts as if it was immediately preceded by a
+B<LoadPlugin> statement. B<LoadPlugin> statements are still required for
+plugins that don't provide any configuration, e.g. the I<Load plugin>.
+
 =item B<Include> I<Path> [I<pattern>]
 
 If I<Path> points to a file, includes that file. If I<Path> points to a
@@ -852,6 +881,29 @@ By default no detailed zone information is collected.
 
 =back
 
+=head2 Plugin C<cgroups>
+
+This plugin collects the CPU user/system time for each I<cgroup> by reading the
+F<cpuacct.stat> files in the first cpuacct-mountpoint (typically
+F</sys/fs/cgroup/cpu.cpuacct> on machines using systemd).
+
+=over 4
+
+=item B<CGroup> I<Directory>
+
+Select I<cgroup> based on the name. Whether only matching I<cgroups> are
+collected or if they are ignored is controlled by the B<IgnoreSelected> option;
+see below.
+
+=item B<IgnoreSelected> B<true>|B<false>
+
+Invert the selection: If set to true, all cgroups I<except> the ones that
+match any one of the criteria are collected. By default only selected
+cgroups are collected if a selection is made. If no selection is configured
+at all, B<all> cgroups are selected.
+
+=back
+
 =head2 Plugin C<cpufreq>
 
 This plugin doesn't have any options. It reads
@@ -2207,7 +2259,7 @@ interpreted. For a description of match blocks, please see L<"Plugin tail">.
 
 =head2 Plugin C<memcached>
 
-The C<memcached plugin> connects to a memcached server and queries statistics
+The B<memcached plugin> connects to a memcached server and queries statistics
 about cache utilization, memory and bandwidth used.
 L<http://www.danga.com/memcached/>
 
@@ -2239,6 +2291,166 @@ setting is given, the B<Host> and B<Port> settings are ignored.
 
 =back
 
+=head2 Plugin C<mic>
+
+The B<mic plugin> gathers CPU statistics, memory usage and temperatures from
+Intel's Many Integrated Core (MIC) systems.
+
+B<Synopsis:>
+
+ <Plugin mic>
+   ShowCPU true
+   ShowCPUCores true
+   ShowMemory true
+   
+   ShowTemperatures true
+   Temperature vddg
+   Temperature vddq
+   IgnoreSelectedTemperature true
+
+   ShowPower true
+   Power total0
+   Power total1
+   IgnoreSelectedPower true   
+ </Plugin>
+
+The following options are valid inside the B<PluginE<nbsp>mic> block:
+
+=over 4
+
+=item B<ShowCPU> B<true>|B<false>
+
+If enabled (the default) a sum of the CPU usage accross all cores is reported.
+
+=item B<ShowCPUCores> B<true>|B<false>
+
+If enabled (the default) per-core CPU usage is reported.
+
+=item B<ShowMemory> B<true>|B<false>
+
+If enabled (the default) the physical memory usage of the MIC system is
+reported.
+
+=item B<ShowTemperatures> B<true>|B<false>
+
+If enabled (the default) various temperatures of the MIC system are reported.
+
+=item B<Temperature> I<Name>
+
+This option controls which temperatures are being reported. Whether matching
+temperatures are being ignored or I<only> matching temperatures are reported
+depends on the B<IgnoreSelectedTemperature> setting below. By default I<all>
+temperatures are reported.
+
+=item B<IgnoreSelectedTemperature> B<false>|B<true>
+
+Controls the behavior of the B<Temperature> setting above. If set to B<false>
+(the default) only temperatures matching a B<Temperature> option are reported
+or, if no B<Temperature> option is specified, all temperatures are reported. If
+set to B<true>, matching temperatures are I<ignored> and all other temperatures
+are reported.
+
+Known temperature names are:
+
+=over 4
+
+=item die
+
+Die of the CPU
+
+=item devmem
+
+Device Memory
+
+=item fin
+
+Fan In
+
+=item fout
+
+Fan Out 
+
+=item vccp
+
+Voltage ccp
+
+=item vddg
+
+Voltage ddg
+
+=item vddq
+
+Voltage ddq
+
+=back
+
+=item B<ShowPower> B<true>|B<false>
+
+If enabled (the default) various temperatures of the MIC system are reported.
+
+=item B<Power> I<Name>
+
+This option controls which power readings are being reported. Whether matching
+power readings are being ignored or I<only> matching power readings are reported
+depends on the B<IgnoreSelectedPower> setting below. By default I<all>
+power readings are reported.
+
+=item B<IgnoreSelectedPower> B<false>|B<true>
+
+Controls the behavior of the B<Power> setting above. If set to B<false>
+(the default) only power readings matching a B<Power> option are reported
+or, if no B<Power> option is specified, all power readings are reported. If
+set to B<true>, matching power readings are I<ignored> and all other power readings
+are reported.
+
+Known power names are:
+
+=over 4
+
+=item total0
+
+Total power utilization averaged over Time Window 0 (uWatts). 
+
+=item total1
+
+Total power utilization averaged over Time Window 0 (uWatts). 
+
+=item inst
+
+Instantaneous power (uWatts).
+
+=item imax
+
+Max instantaneous power (uWatts). 
+
+=item pcie
+
+PCI-E connector power (uWatts). 
+
+=item c2x3
+
+2x3 connector power (uWatts). 
+
+=item c2x4
+
+2x4 connector power (uWatts). 
+
+=item vccp
+
+Core rail (uVolts). 
+
+=item vddg
+
+Uncore rail (uVolts). 
+
+=item vddq
+
+Memory subsystem rail (uVolts). 
+
+=back
+
+=back
+
 =head2 Plugin C<modbus>
 
 The B<modbus plugin> connects to a Modbus "slave" via Modbus/TCP and reads
@@ -2246,7 +2458,7 @@ register values. It supports reading single registers (unsigned 16E<nbsp>bit
 values), large integer values (unsigned 32E<nbsp>bit values) and floating point
 values (two registers interpreted as IEEE floats in big endian notation).
 
-Synopsis:
+B<Synopsis:>
 
  <Data "voltage-input-1">
    RegisterBase 0
index d617832..d963efa 100644 (file)
@@ -86,6 +86,47 @@ int ssnprintf (char *dest, size_t n, const char *format, ...)
        return (ret);
 } /* int ssnprintf */
 
+char *ssnprintf_alloc (char const *format, ...) /* {{{ */
+{
+       char static_buffer[1024] = "";
+       char *alloc_buffer;
+       size_t alloc_buffer_size;
+       int status;
+       va_list ap;
+
+       /* Try printing into the static buffer. In many cases it will be
+        * sufficiently large and we can simply return a strdup() of this
+        * buffer. */
+       va_start (ap, format);
+       status = vsnprintf (static_buffer, sizeof (static_buffer), format, ap);
+       va_end (ap);
+       if (status < 0)
+               return (NULL);
+
+       /* "status" does not include the null byte. */
+       alloc_buffer_size = (size_t) (status + 1);
+       if (alloc_buffer_size <= sizeof (static_buffer))
+               return (strdup (static_buffer));
+
+       /* Allocate a buffer large enough to hold the string. */
+       alloc_buffer = malloc (alloc_buffer_size);
+       if (alloc_buffer == NULL)
+               return (NULL);
+       memset (alloc_buffer, 0, alloc_buffer_size);
+
+       /* Print again into this new buffer. */
+       va_start (ap, format);
+       status = vsnprintf (alloc_buffer, alloc_buffer_size, format, ap);
+       va_end (ap);
+       if (status < 0)
+       {
+               sfree (alloc_buffer);
+               return (NULL);
+       }
+
+       return (alloc_buffer);
+} /* }}} char *ssnprintf_alloc */
+
 char *sstrdup (const char *s)
 {
        char *r;
@@ -1213,18 +1254,25 @@ int walk_directory (const char *dir, dirwalk_callback_f callback,
        return (0);
 }
 
-int read_file_contents (const char *filename, char *buf, int bufsize)
+ssize_t read_file_contents (const char *filename, char *buf, size_t bufsize)
 {
        FILE *fh;
-       int n;
+       ssize_t ret;
 
-       if ((fh = fopen (filename, "r")) == NULL)
-               return -1;
+       fh = fopen (filename, "r");
+       if (fh == NULL)
+               return (-1);
 
-       n = fread(buf, 1, bufsize, fh);
-       fclose(fh);
+       ret = (ssize_t) fread (buf, 1, bufsize, fh);
+       if ((ret == 0) && (ferror (fh) != 0))
+       {
+               ERROR ("read_file_contents: Reading file \"%s\" failed.",
+                               filename);
+               ret = -1;
+       }
 
-       return n;
+       fclose(fh);
+       return (ret);
 }
 
 counter_t counter_diff (counter_t old_value, counter_t new_value)
index ae8e311..317be8d 100644 (file)
@@ -56,7 +56,13 @@ struct rate_to_value_state_s
 typedef struct rate_to_value_state_s rate_to_value_state_t;
 
 char *sstrncpy (char *dest, const char *src, size_t n);
+
+__attribute__ ((format(printf,3,4)))
 int ssnprintf (char *dest, size_t n, const char *format, ...);
+
+__attribute__ ((format(printf,1,2)))
+char *ssnprintf_alloc (char const *format, ...);
+
 char *sstrdup(const char *s);
 void *smalloc(size_t size);
 char *sstrerror (int errnum, char *buf, size_t buflen);
@@ -303,7 +309,7 @@ typedef int (*dirwalk_callback_f)(const char *dirname, const char *filename,
 int walk_directory (const char *dir, dirwalk_callback_f callback,
                void *user_data, int hidden);
 /* Returns the number of bytes read or negative on error. */
-int read_file_contents (const char *filename, char *buf, int bufsize);
+ssize_t read_file_contents (char const *filename, char *buf, size_t bufsize);
 
 counter_t counter_diff (counter_t old_value, counter_t new_value);
 
index ac5e8ed..876ee23 100644 (file)
@@ -110,6 +110,7 @@ static cf_global_option_t cf_global_options[] =
        {"ReadThreads", NULL, "5"},
        {"WriteThreads", NULL, "5"},
        {"Timeout",     NULL, "2"},
+       {"AutoLoadPlugin", NULL, "false"},
        {"PreCacheChain",  NULL, "PreCache"},
        {"PostCacheChain", NULL, "PostCache"}
 };
@@ -277,21 +278,6 @@ static int dispatch_loadplugin (const oconfig_item_t *ci)
        memset (&ctx, 0, sizeof (ctx));
        ctx.interval = cf_get_default_interval ();
 
-       /*
-        * XXX: Magic at work:
-        *
-        * Some of the language bindings, for example the Python and Perl
-        * plugins, need to be able to export symbols to the scripts they run.
-        * For this to happen, the "Globals" flag needs to be set.
-        * Unfortunately, this technical detail is hard to explain to the
-        * average user and she shouldn't have to worry about this, ideally.
-        * So in order to save everyone's sanity use a different default for a
-        * handful of special plugins. --octo
-        */
-       if ((strcasecmp ("Perl", name) == 0)
-                       || (strcasecmp ("Python", name) == 0))
-               flags |= PLUGIN_FLAGS_GLOBAL;
-
        for (i = 0; i < ci->children_num; ++i) {
                if (strcasecmp("Globals", ci->children[i].key) == 0)
                        cf_util_get_flag (ci->children + i, &flags, PLUGIN_FLAGS_GLOBAL);
@@ -394,6 +380,19 @@ static int dispatch_block_plugin (oconfig_item_t *ci)
 
        name = ci->values[0].value.string;
 
+       if (IS_TRUE (global_option_get ("AutoLoadPlugin")))
+       {
+               int status;
+
+               status = plugin_load (name, /* flags = */ 0);
+               if (status != 0)
+               {
+                       ERROR ("Automatically loading plugin \"%s\" failed "
+                                       "with status %i.", name, status);
+                       return (status);
+               }
+       }
+
        /* Check for a complex callback first */
        for (cb = complex_callback_head; cb != NULL; cb = cb->next)
        {
index 0b41304..5adaf06 100644 (file)
@@ -966,7 +966,7 @@ static int cx_config_add_url (oconfig_item_t *ci) /* {{{ */
   if (status == 0)
   {
     user_data_t ud;
-    char cb_name[DATA_MAX_NAME_LEN];
+    char *cb_name;
 
     if (db->instance == NULL)
       db->instance = strdup("default");
@@ -978,11 +978,10 @@ static int cx_config_add_url (oconfig_item_t *ci) /* {{{ */
     ud.data = (void *) db;
     ud.free_func = cx_free;
 
-    ssnprintf (cb_name, sizeof (cb_name), "curl_xml-%s-%s",
-               db->instance, db->url);
-
-    plugin_register_complex_read (/* group = */ NULL, cb_name, cx_read,
+    cb_name = ssnprintf_alloc ("curl_xml-%s-%s", db->instance, db->url);
+    plugin_register_complex_read (/* group = */ "curl_xml", cb_name, cx_read,
                                   /* interval = */ NULL, &ud);
+    sfree (cb_name);
   }
   else
   {
diff --git a/src/mic.c b/src/mic.c
new file mode 100644 (file)
index 0000000..570da51
--- /dev/null
+++ b/src/mic.c
@@ -0,0 +1,417 @@
+/**
+ * collectd - src/mic.c
+ * Copyright (C) 2013 Battelle Memorial Institute
+ *
+ * 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:
+ *   Evan Felix <evan.felix at pnnl.gov>
+ **/
+
+#include "collectd.h"
+#include "plugin.h"
+#include "common.h"
+#include "utils_ignorelist.h"
+
+#include <MicAccessTypes.h>
+#include <MicAccessErrorTypes.h>
+#include <MicAccessApi.h>
+#include <MicThermalAPI.h>
+#include <MicPowerManagerAPI.h>
+
+#define MAX_MICS 32
+#define MAX_CORES 256
+
+static MicDeviceOnSystem mics[MAX_MICS];
+static U32 num_mics = 0;
+static HANDLE mic_handle = NULL;
+
+static int const therm_ids[] = {
+       eMicThermalDie, eMicThermalDevMem, eMicThermalFin, eMicThermalFout,
+       eMicThermalVccp, eMicThermalVddg, eMicThermalVddq };
+static char const * const therm_names[] = {
+       "die", "devmem", "fin", "fout",
+       "vccp", "vddg", "vddq" };
+
+static const char *config_keys[] =
+{
+       "ShowCPU",
+       "ShowCPUCores",
+       "ShowMemory",
+       "ShowTemperatures",
+       "Temperature",
+       "IgnoreSelectedTemperature",
+       "ShowPower",
+       "Power",
+       "IgnoreSelectedPower"
+};
+static int config_keys_num = STATIC_ARRAY_SIZE (config_keys);
+
+static _Bool show_cpu = 1;
+static _Bool show_cpu_cores = 1;
+static _Bool show_memory = 1;
+static _Bool show_temps = 1;
+static ignorelist_t *temp_ignore = NULL;
+static _Bool show_power = 1;
+static ignorelist_t *power_ignore = NULL;
+
+static int mic_init (void)
+{
+       U32 ret;
+       U32 mic_count;
+
+       if (mic_handle)
+               return (0);
+
+       mic_count = (U32) STATIC_ARRAY_SIZE(mics);
+       ret = MicInitAPI(&mic_handle,  eTARGET_SCIF_DRIVER, mics, &mic_count);
+       if (ret != MIC_ACCESS_API_SUCCESS) {
+               ERROR("mic plugin: Problem initializing MicAccessAPI: %s",
+                               MicGetErrorString(ret));
+       }
+       DEBUG("mic plugin: found: %"PRIu32" MIC(s)",mic_count);
+
+       if (mic_count<0 || mic_count>=MAX_MICS) {
+               ERROR("mic plugin: No Intel MICs in system");
+               return (1);
+       }
+       else {
+               num_mics = mic_count;
+               return (0);
+       }
+}
+
+static int mic_config (const char *key, const char *value) {
+       if (temp_ignore == NULL)
+               temp_ignore = ignorelist_create(1);
+       if (power_ignore == NULL)
+               power_ignore = ignorelist_create(1);
+       if (temp_ignore == NULL || power_ignore == NULL)
+               return (1);
+
+       if (strcasecmp("ShowCPU",key) == 0)
+       {
+               show_cpu = IS_TRUE(value);
+       }
+       else if (strcasecmp("ShowCPUCores",key) == 0)
+       {
+               show_cpu_cores = IS_TRUE(value);
+       }
+       else if (strcasecmp("ShowTemperatures",key) == 0)
+       {
+               show_temps = IS_TRUE(value);
+       }
+       else if (strcasecmp("ShowMemory",key) == 0)
+       {
+               show_memory = IS_TRUE(value);
+       }
+       else if (strcasecmp("ShowPower",key) == 0)
+       {
+               show_power = IS_TRUE(value);
+       }
+       else if (strcasecmp("Temperature",key) == 0)
+       {
+               ignorelist_add(temp_ignore,value);
+       }
+       else if (strcasecmp("IgnoreSelectedTemperature",key) == 0)
+       {
+               int invert = 1;
+               if (IS_TRUE(value))
+                       invert = 0;
+               ignorelist_set_invert(temp_ignore,invert);
+       }
+       else if (strcasecmp("Power",key) == 0)
+       {
+               ignorelist_add(power_ignore,value);
+       }
+       else if (strcasecmp("IgnoreSelectedPower",key) == 0)
+       {
+               int invert = 1;
+               if (IS_TRUE(value))
+                       invert = 0;
+               ignorelist_set_invert(power_ignore,invert);
+       }
+       else
+       {
+               return (-1);
+       }
+       return (0);
+}
+
+static void mic_submit_memory_use(int micnumber, const char *type_instance, U32 val)
+{
+       value_t values[1];
+       value_list_t vl = VALUE_LIST_INIT;
+
+       /* MicAccessAPI reports KB's of memory, adjust for this */
+       DEBUG("mic plugin: Memory Value Report; %u %lf",val,((gauge_t)val)*1024.0);
+       values[0].gauge = ((gauge_t)val)*1024.0;
+
+       vl.values=values;
+       vl.values_len=1;
+
+       strncpy (vl.host, hostname_g, sizeof (vl.host));
+       strncpy (vl.plugin, "mic", sizeof (vl.plugin));
+       ssnprintf (vl.plugin_instance, sizeof (vl.plugin_instance), "%i", micnumber);
+       strncpy (vl.type, "memory", sizeof (vl.type));
+       strncpy (vl.type_instance, type_instance, sizeof (vl.type_instance));
+
+       plugin_dispatch_values (&vl);
+}
+
+/* Gather memory Utilization */
+static int mic_read_memory(int mic)
+{
+       U32 ret;
+       U32 mem_total,mem_free,mem_bufs;
+
+       ret = MicGetMemoryUtilization(mic_handle,&mem_total,&mem_free,&mem_bufs);
+       if (ret != MIC_ACCESS_API_SUCCESS) {
+               ERROR("mic plugin: Problem getting Memory Utilization: %s",
+                               MicGetErrorString(ret));
+               return (1);
+       }
+       mic_submit_memory_use(mic,"free",mem_free);
+       mic_submit_memory_use(mic,"used",mem_total-mem_free-mem_bufs);
+       mic_submit_memory_use(mic,"buffered",mem_bufs);
+       DEBUG("mic plugin: Memory Read: %u %u %u",mem_total,mem_free,mem_bufs);
+       return (0);
+}
+
+static void mic_submit_temp(int micnumber, const char *type, gauge_t val)
+{
+       value_t values[1];
+       value_list_t vl = VALUE_LIST_INIT;
+
+       values[0].gauge = val;
+
+       vl.values=values;
+       vl.values_len=1;
+
+       strncpy (vl.host, hostname_g, sizeof (vl.host));
+       strncpy (vl.plugin, "mic", sizeof (vl.plugin));
+       ssnprintf (vl.plugin_instance, sizeof (vl.plugin_instance),
+                       "%i", micnumber);
+       strncpy (vl.type, "temperature", sizeof (vl.type));
+       strncpy (vl.type_instance, type, sizeof (vl.type_instance));
+
+       plugin_dispatch_values (&vl);
+}
+
+/* Gather Temperature Information */
+static int mic_read_temps(int mic)
+{
+       size_t num_therms = STATIC_ARRAY_SIZE(therm_ids);
+       size_t j;
+
+       for (j = 0; j < num_therms; j++) {
+               U32 status;
+               U32 temp_buffer;
+               U32 buffer_size = (U32)sizeof(temp_buffer);
+               char const *name = therm_names[j];
+
+               if (ignorelist_match(temp_ignore, name) != 0)
+                       continue;
+
+               status = MicGetTemperature(mic_handle, therm_ids[j],
+                               &temp_buffer, &buffer_size);
+               if (status != MIC_ACCESS_API_SUCCESS) {
+                       ERROR("mic plugin: Error reading temperature \"%s\": "
+                                       "%s", name, MicGetErrorString(status));
+                       return (1);
+               }
+               mic_submit_temp(mic, name, temp_buffer);
+       }
+       return (0);
+}
+
+static void mic_submit_cpu(int micnumber, const char *type_instance,
+               int core, derive_t val)
+{
+       value_t values[1];
+       value_list_t vl = VALUE_LIST_INIT;
+
+       values[0].derive = val;
+
+       vl.values=values;
+       vl.values_len=1;
+
+       strncpy (vl.host, hostname_g, sizeof (vl.host));
+       strncpy (vl.plugin, "mic", sizeof (vl.plugin));
+       if (core < 0) /* global aggregation */
+               ssnprintf (vl.plugin_instance, sizeof (vl.plugin_instance),
+                               "%i", micnumber);
+       else /* per-core statistics */
+               ssnprintf (vl.plugin_instance, sizeof (vl.plugin_instance),
+                               "%i-cpu-%i", micnumber, core);
+       strncpy (vl.type, "cpu", sizeof (vl.type));
+       strncpy (vl.type_instance, type_instance, sizeof (vl.type_instance));
+
+       plugin_dispatch_values (&vl);
+}
+
+/*Gather CPU Utilization Information */
+static int mic_read_cpu(int mic)
+{
+       MicCoreUtil core_util;
+       MicCoreJiff core_jiffs[MAX_CORES];
+       U32 core_jiffs_size;
+       U32 status;
+
+       core_jiffs_size = MAX_CORES * sizeof(MicCoreJiff);
+       status = MicGetCoreUtilization(mic_handle, &core_util,
+                       core_jiffs, &core_jiffs_size);
+       if (status != MIC_ACCESS_API_SUCCESS) {
+               ERROR("mic plugin: Problem getting CPU utilization: %s",
+                               MicGetErrorString(status));
+               return(-1);
+       }
+
+       if (show_cpu) {
+               mic_submit_cpu(mic, "user", -1, core_util.sum.user);
+               mic_submit_cpu(mic, "sys", -1, core_util.sum.sys);
+               mic_submit_cpu(mic, "nice", -1, core_util.sum.nice);
+               mic_submit_cpu(mic, "idle", -1, core_util.sum.idle);
+       }
+
+       if (show_cpu_cores) {
+               int j;
+               for (j = 0; j < core_util.core; j++) {
+                       mic_submit_cpu(mic, "user", j, core_jiffs[j].user);
+                       mic_submit_cpu(mic, "sys", j, core_jiffs[j].sys);
+                       mic_submit_cpu(mic, "nice", j, core_jiffs[j].nice);
+                       mic_submit_cpu(mic, "idle", j, core_jiffs[j].idle);
+               }
+       }
+       return (0);
+}
+
+static void mic_submit_power(int micnumber, const char *type, const char *type_instance, gauge_t val)
+{
+       value_t values[1];
+       value_list_t vl = VALUE_LIST_INIT;
+
+       values[0].gauge = val;
+
+       vl.values=values;
+       vl.values_len=1;
+
+       strncpy (vl.host, hostname_g, sizeof (vl.host));
+       strncpy (vl.plugin, "mic", sizeof (vl.plugin));
+       ssnprintf (vl.plugin_instance, sizeof (vl.plugin_instance), "%i", micnumber);
+       strncpy (vl.type, type, sizeof (vl.type));
+       strncpy (vl.type_instance, type_instance, sizeof (vl.type_instance));
+
+       plugin_dispatch_values (&vl);
+}
+
+/* Gather Power Information */
+static int mic_read_power(int mic)
+{
+       U32 ret;
+       MicPwrUsage power_use;
+
+       ret = MicGetPowerUsage(mic_handle,&power_use);
+       if (ret != MIC_ACCESS_API_SUCCESS) {
+               ERROR("mic plugin: Problem getting Power Usage: %s",
+                       MicGetErrorString(ret));
+               return (1);
+       }
+
+       /* power is in uWatts, current in mA, voltage in uVolts..   convert to
+        * base unit */
+       #define SUB_POWER(name) do { if (ignorelist_match(power_ignore,#name)==0) \
+               mic_submit_power(mic,"power",#name,(gauge_t)power_use.name.prr*0.000001); \
+       } while(0)
+       #define SUB_VOLTS(name) do { if (ignorelist_match(power_ignore,#name)==0) {\
+               mic_submit_power(mic,"power",#name,(gauge_t)(power_use.name.pwr*0.000001)); \
+               mic_submit_power(mic,"current",#name,(gauge_t)(power_use.name.cur*0.001)); \
+               mic_submit_power(mic,"voltage",#name,(gauge_t)(power_use.name.volt*0.000001)); \
+       }} while(0)
+
+       SUB_POWER(total0);
+       SUB_POWER(total1);
+       SUB_POWER(inst);
+       SUB_POWER(imax);
+       SUB_POWER(pcie);
+       SUB_POWER(c2x3);
+       SUB_POWER(c2x4);
+       SUB_VOLTS(vccp);
+       SUB_VOLTS(vddg);
+       SUB_VOLTS(vddq);
+
+       return (0);
+}
+
+static int mic_read (void)
+{
+       int i;
+       U32 ret;
+       int error;
+
+       error=0;
+       for (i=0;i<num_mics;i++) {
+               ret = MicInitAdapter(&mic_handle,&mics[i]);
+               if (ret != MIC_ACCESS_API_SUCCESS) {
+                       ERROR("mic plugin: Problem initializing MicAdapter: %s",
+                                       MicGetErrorString(ret));
+                       error=1;
+               }
+
+               if (error == 0 && show_memory)
+                       error = mic_read_memory(i);
+
+               if (error == 0 && show_temps)
+                       error = mic_read_temps(i);
+
+               if (error == 0 && (show_cpu || show_cpu_cores))
+                       error = mic_read_cpu(i);
+
+               if (error == 0 && (show_power))
+                       error = mic_read_power(i);
+
+               ret = MicCloseAdapter(mic_handle);
+               if (ret != MIC_ACCESS_API_SUCCESS) {
+                       ERROR("mic plugin: Problem closing MicAdapter: %s",
+                                       MicGetErrorString(ret));
+                       error=2;
+                       break;
+               }
+       }
+       if (num_mics==0)
+               error=3;
+       return error;
+}
+
+
+static int mic_shutdown (void)
+{
+       if (mic_handle)
+               MicCloseAPI(&mic_handle);
+       mic_handle = NULL;
+
+       return (0);
+}
+
+void module_register (void)
+{
+       plugin_register_init ("mic", mic_init);
+       plugin_register_shutdown ("mic", mic_shutdown);
+       plugin_register_read ("mic", mic_read);
+       plugin_register_config ("mic",mic_config, config_keys, config_keys_num);
+} /* void module_register */
+
+/*
+ * vim: set shiftwidth=8 softtabstop=8 noet textwidth=78 :
+ */
index 4c6a032..894b0e5 100644 (file)
@@ -61,7 +61,7 @@ struct read_func_s
 #define rf_ctx rf_super.cf_ctx
        callback_func_t rf_super;
        char rf_group[DATA_MAX_NAME_LEN];
-       char rf_name[DATA_MAX_NAME_LEN];
+       char *rf_name;
        int rf_type;
        cdtime_t rf_interval;
        cdtime_t rf_effective_interval;
@@ -81,6 +81,8 @@ struct write_queue_s
 /*
  * Private variables
  */
+static c_avl_tree_t *plugins_loaded = NULL;
+
 static llist_t *list_init;
 static llist_t *list_write;
 static llist_t *list_flush;
@@ -437,6 +439,7 @@ static void *plugin_read_thread (void __attribute__((unused)) *args)
                {
                        DEBUG ("plugin_read_thread: Destroying the `%s' "
                                        "callback.", rf->rf_name);
+                       sfree (rf->rf_name);
                        destroy_callback ((callback_func_t *) rf);
                        rf = NULL;
                        continue;
@@ -830,8 +833,52 @@ void plugin_set_dir (const char *dir)
        }
 }
 
+static _Bool plugin_is_loaded (char const *name)
+{
+       int status;
+
+       if (plugins_loaded == NULL)
+               plugins_loaded = c_avl_create ((void *) strcasecmp);
+       assert (plugins_loaded != NULL);
+
+       status = c_avl_get (plugins_loaded, name, /* ret_value = */ NULL);
+       return (status == 0);
+}
+
+static int plugin_mark_loaded (char const *name)
+{
+       char *name_copy;
+       int status;
+
+       name_copy = strdup (name);
+       if (name_copy == NULL)
+               return (ENOMEM);
+
+       status = c_avl_insert (plugins_loaded,
+                       /* key = */ name_copy, /* value = */ NULL);
+       return (status);
+}
+
+static void plugin_free_loaded ()
+{
+       void *key;
+       void *value;
+
+       if (plugins_loaded == NULL)
+               return;
+
+       while (c_avl_pick (plugins_loaded, &key, &value) == 0)
+       {
+               sfree (key);
+               assert (value == NULL);
+       }
+
+       c_avl_destroy (plugins_loaded);
+       plugins_loaded = NULL;
+}
+
 #define BUFSIZE 512
-int plugin_load (const char *type, uint32_t flags)
+int plugin_load (char const *plugin_name, uint32_t flags)
 {
        DIR  *dh;
        const char *dir;
@@ -843,15 +890,38 @@ int plugin_load (const char *type, uint32_t flags)
        struct dirent *de;
        int status;
 
+       if (plugin_name == NULL)
+               return (EINVAL);
+
+       /* Check if plugin is already loaded and don't do anything in this
+        * case. */
+       if (plugin_is_loaded (plugin_name))
+               return (0);
+
        dir = plugin_get_dir ();
        ret = 1;
 
+       /*
+        * XXX: Magic at work:
+        *
+        * Some of the language bindings, for example the Python and Perl
+        * plugins, need to be able to export symbols to the scripts they run.
+        * For this to happen, the "Globals" flag needs to be set.
+        * Unfortunately, this technical detail is hard to explain to the
+        * average user and she shouldn't have to worry about this, ideally.
+        * So in order to save everyone's sanity use a different default for a
+        * handful of special plugins. --octo
+        */
+       if ((strcasecmp ("perl", plugin_name) == 0)
+                       || (strcasecmp ("python", plugin_name) == 0))
+               flags |= PLUGIN_FLAGS_GLOBAL;
+
        /* `cpu' should not match `cpufreq'. To solve this we add `.so' to the
         * type when matching the filename */
-       status = ssnprintf (typename, sizeof (typename), "%s.so", type);
+       status = ssnprintf (typename, sizeof (typename), "%s.so", plugin_name);
        if ((status < 0) || ((size_t) status >= sizeof (typename)))
        {
-               WARNING ("plugin_load: Filename too long: \"%s.so\"", type);
+               WARNING ("plugin_load: Filename too long: \"%s.so\"", plugin_name);
                return (-1);
        }
        typename_len = strlen (typename);
@@ -898,13 +968,14 @@ int plugin_load (const char *type, uint32_t flags)
                if (status == 0)
                {
                        /* success */
+                       plugin_mark_loaded (plugin_name);
                        ret = 0;
                        break;
                }
                else
                {
                        ERROR ("plugin_load: Load plugin \"%s\" failed with "
-                                       "status %i.", type, status);
+                                       "status %i.", plugin_name, status);
                }
        }
 
@@ -912,7 +983,7 @@ int plugin_load (const char *type, uint32_t flags)
 
        if (filename[0] == 0)
                ERROR ("plugin_load: Could not find plugin \"%s\" in %s",
-                               type, dir);
+                               plugin_name, dir);
 
        return (ret);
 }
@@ -1048,7 +1119,7 @@ int plugin_register_read (const char *name,
        rf->rf_udata.free_func = NULL;
        rf->rf_ctx = plugin_get_ctx ();
        rf->rf_group[0] = '\0';
-       sstrncpy (rf->rf_name, name, sizeof (rf->rf_name));
+       rf->rf_name = strdup (name);
        rf->rf_type = RF_SIMPLE;
        rf->rf_interval = plugin_get_interval ();
 
@@ -1080,7 +1151,7 @@ int plugin_register_complex_read (const char *group, const char *name,
                sstrncpy (rf->rf_group, group, sizeof (rf->rf_group));
        else
                rf->rf_group[0] = '\0';
-       sstrncpy (rf->rf_name, name, sizeof (rf->rf_name));
+       rf->rf_name = strdup (name);
        rf->rf_type = RF_COMPLEX;
        if (interval != NULL)
                rf->rf_interval = TIMESPEC_TO_CDTIME_T (interval);
@@ -1657,6 +1728,8 @@ void plugin_shutdown_all (void)
        destroy_all_callbacks (&list_notification);
        destroy_all_callbacks (&list_shutdown);
        destroy_all_callbacks (&list_log);
+
+       plugin_free_loaded ();
 } /* void plugin_shutdown_all */
 
 int plugin_dispatch_missing (const value_list_t *vl) /* {{{ */
index 635ff30..8f0c6d8 100644 (file)
@@ -223,7 +223,8 @@ void plugin_set_dir (const char *dir);
  *  and a value below zero if an error occurs.
  *
  * NOTES
- *  No attempt is made to re-load an already loaded module.
+ *  Re-loading an already loaded module is detected and zero is returned in
+ *  this case.
  */
 int plugin_load (const char *name, uint32_t flags);
 
index 603f85b..27c92bc 100644 (file)
@@ -80,12 +80,12 @@ static int thermal_sysfs_device_read (const char __attribute__((unused)) *dir,
        if (device_list && ignorelist_match (device_list, name))
                return -1;
 
-       len = snprintf (filename, sizeof (filename),
+       len = ssnprintf (filename, sizeof (filename),
                        "%s/%s/temp", dirname_sysfs, name);
        if ((len < 0) || ((size_t) len >= sizeof (filename)))
                return -1;
 
-       len = read_file_contents (filename, data, sizeof(data));
+       len = (ssize_t) read_file_contents (filename, data, sizeof(data));
        if (len > 1 && data[--len] == '\n') {
                char *endptr = NULL;
                double temp;
@@ -100,12 +100,12 @@ static int thermal_sysfs_device_read (const char __attribute__((unused)) *dir,
                }
        }
 
-       len = snprintf (filename, sizeof (filename),
+       len = ssnprintf (filename, sizeof (filename),
                        "%s/%s/cur_state", dirname_sysfs, name);
        if ((len < 0) || ((size_t) len >= sizeof (filename)))
                return -1;
 
-       len = read_file_contents (filename, data, sizeof(data));
+       len = (ssize_t) read_file_contents (filename, data, sizeof(data));
        if (len > 1 && data[--len] == '\n') {
                char *endptr = NULL;
                double state;
@@ -139,12 +139,12 @@ static int thermal_procfs_device_read (const char __attribute__((unused)) *dir,
         * temperature:             55 C
         */
        
-       len = snprintf (filename, sizeof (filename),
+       len = ssnprintf (filename, sizeof (filename),
                        "%s/%s/temperature", dirname_procfs, name);
        if ((len < 0) || ((size_t) len >= sizeof (filename)))
                return -1;
 
-       len = read_file_contents (filename, data, sizeof(data));
+       len = (ssize_t) read_file_contents (filename, data, sizeof(data));
        if ((len > 0) && ((size_t) len > sizeof(str_temp))
                        && (data[--len] == '\n')
                        && (! strncmp(data, str_temp, sizeof(str_temp)-1))) {