Code

virt plugin: Rename the "libvirt" plugin to "virt".
authorFlorian Forster <octo@collectd.org>
Tue, 16 Sep 2014 09:17:55 +0000 (11:17 +0200)
committerFlorian Forster <octo@collectd.org>
Tue, 16 Sep 2014 09:17:55 +0000 (11:17 +0200)
"libvirt" confused libtool and caused problems when re-building from source.

configure.ac
src/Makefile.am
src/collectd.conf.in
src/collectd.conf.pod
src/configfile.c
src/libvirt.c [deleted file]
src/virt.c [new file with mode: 0644]

index bee5f72adfb89edf02b86c33dd783d4af68e6040..bcd8b6021b677ce0d14ff7efafe7f31476a2f532 100644 (file)
@@ -4972,7 +4972,6 @@ plugin_interface="no"
 plugin_ipmi="no"
 plugin_ipvs="no"
 plugin_irq="no"
-plugin_libvirt="no"
 plugin_load="no"
 plugin_log_logstash="no"
 plugin_memory="no"
@@ -4988,8 +4987,9 @@ plugin_tape="no"
 plugin_tcpconns="no"
 plugin_ted="no"
 plugin_thermal="no"
-plugin_users="no"
 plugin_uptime="no"
+plugin_users="no"
+plugin_virt="no"
 plugin_vmem="no"
 plugin_vserver="no"
 plugin_wireless="no"
@@ -5214,11 +5214,6 @@ then
        plugin_interface="yes"
 fi
 
-if test "x$with_libxml2" = "xyes" && test "x$with_libvirt" = "xyes"
-then
-       plugin_libvirt="yes"
-fi
-
 if test "x$have_getloadavg" = "xyes"
 then
        plugin_load="yes"
@@ -5283,6 +5278,12 @@ then
        plugin_users="yes"
 fi
 
+if test "x$with_libxml2" = "xyes" && test "x$with_libvirt" = "xyes"
+then
+       plugin_virt="yes"
+fi
+
+
 m4_divert_once([HELP_ENABLE], [
 collectd plugins:])
 
@@ -5341,7 +5342,6 @@ AC_PLUGIN([iptables],    [$with_libiptc],      [IPTables rule counters])
 AC_PLUGIN([ipvs],        [$plugin_ipvs],       [IPVS connection statistics])
 AC_PLUGIN([irq],         [$plugin_irq],        [IRQ statistics])
 AC_PLUGIN([java],        [$with_java],         [Embed the Java Virtual Machine])
-AC_PLUGIN([libvirt],     [$plugin_libvirt],    [Virtual machine statistics])
 AC_PLUGIN([load],        [$plugin_load],       [System load])
 AC_PLUGIN([logfile],     [yes],                [File logging plugin])
 AC_PLUGIN([log_logstash], [$plugin_log_logstash], [Logstash json_event compatible logging])
@@ -5417,6 +5417,7 @@ AC_PLUGIN([uptime],      [$plugin_uptime],     [Uptime statistics])
 AC_PLUGIN([users],       [$plugin_users],      [User statistics])
 AC_PLUGIN([uuid],        [yes],                [UUID as hostname plugin])
 AC_PLUGIN([varnish],     [$with_libvarnish],   [Varnish cache statistics])
+AC_PLUGIN([virt],        [$plugin_virt],       [Virtual machine statistics])
 AC_PLUGIN([vmem],        [$plugin_vmem],       [Virtual memory statistics])
 AC_PLUGIN([vserver],     [$plugin_vserver],    [Linux VServer statistics])
 AC_PLUGIN([wireless],    [$plugin_wireless],   [Wireless statistics])
@@ -5706,7 +5707,6 @@ Configuration:
     ipvs  . . . . . . . . $enable_ipvs
     irq . . . . . . . . . $enable_irq
     java  . . . . . . . . $enable_java
-    libvirt . . . . . . . $enable_libvirt
     load  . . . . . . . . $enable_load
     logfile . . . . . . . $enable_logfile
     lpar  . . . . . . . . $enable_lpar
@@ -5781,6 +5781,7 @@ Configuration:
     users . . . . . . . . $enable_users
     uuid  . . . . . . . . $enable_uuid
     varnish . . . . . . . $enable_varnish
+    virt  . . . . . . . . $enable_virt
     vmem  . . . . . . . . $enable_vmem
     vserver . . . . . . . $enable_vserver
     wireless  . . . . . . $enable_wireless
index b8623653118416a5790f9b1fd1096d4747e85f5d..340c79289f3f52b9e9e943d5d80aa8b7ed912111 100644 (file)
@@ -566,17 +566,6 @@ collectd_LDADD += "-dlopen" java.la
 collectd_DEPENDENCIES += java.la
 endif
 
-if BUILD_PLUGIN_LIBVIRT
-pkglib_LTLIBRARIES += libvirt.la
-libvirt_la_SOURCES = libvirt.c
-libvirt_la_CFLAGS = $(AM_CFLAGS) \
-               $(BUILD_WITH_LIBVIRT_CFLAGS) $(BUILD_WITH_LIBXML2_CFLAGS)
-libvirt_la_LIBADD = $(BUILD_WITH_LIBVIRT_LIBS) $(BUILD_WITH_LIBXML2_LIBS)
-libvirt_la_LDFLAGS = -module -avoid-version
-collectd_LDADD += "-dlopen" libvirt.la
-collectd_DEPENDENCIES += libvirt.la
-endif
-
 if BUILD_PLUGIN_LOAD
 pkglib_LTLIBRARIES += load.la
 load_la_SOURCES = load.c
@@ -1375,6 +1364,17 @@ collectd_LDADD += "-dlopen" varnish.la
 collectd_DEPENDENCIES += varnish.la
 endif
 
+if BUILD_PLUGIN_VIRT
+pkglib_LTLIBRARIES += virt.la
+virt_la_SOURCES = virt.c
+virt_la_CFLAGS = $(AM_CFLAGS) \
+               $(BUILD_WITH_LIBVIRT_CFLAGS) $(BUILD_WITH_LIBXML2_CFLAGS)
+virt_la_LIBADD = $(BUILD_WITH_LIBVIRT_LIBS) $(BUILD_WITH_LIBXML2_LIBS)
+virt_la_LDFLAGS = -module -avoid-version
+collectd_LDADD += "-dlopen" virt.la
+collectd_DEPENDENCIES += virt.la
+endif
+
 if BUILD_PLUGIN_VMEM
 pkglib_LTLIBRARIES += vmem.la
 vmem_la_SOURCES = vmem.c
index 91c040a3ca67a144ba573c4faaf2d5ef5f926c23..8e1bae7154c79830d9b1ed14f6a23f4d467c2114 100644 (file)
 #@BUILD_PLUGIN_IPVS_TRUE@LoadPlugin ipvs
 #@BUILD_PLUGIN_IRQ_TRUE@LoadPlugin irq
 #@BUILD_PLUGIN_JAVA_TRUE@LoadPlugin java
-#@BUILD_PLUGIN_LIBVIRT_TRUE@LoadPlugin libvirt
 @BUILD_PLUGIN_LOAD_TRUE@@BUILD_PLUGIN_LOAD_TRUE@LoadPlugin load
 #@BUILD_PLUGIN_LPAR_TRUE@LoadPlugin lpar
 #@BUILD_PLUGIN_LVM_TRUE@LoadPlugin lvm
 #@BUILD_PLUGIN_UUID_TRUE@LoadPlugin uuid
 #@BUILD_PLUGIN_VARNISH_TRUE@LoadPlugin varnish
 #@BUILD_PLUGIN_MIC_TRUE@LoadPlugin mic
+#@BUILD_PLUGIN_VIRT_TRUE@LoadPlugin virt
 #@BUILD_PLUGIN_VMEM_TRUE@LoadPlugin vmem
 #@BUILD_PLUGIN_VSERVER_TRUE@LoadPlugin vserver
 #@BUILD_PLUGIN_WIRELESS_TRUE@LoadPlugin wireless
 #      </Plugin>
 #</Plugin>
 
-#<Plugin libvirt>
-#      Connection "xen:///"
-#      RefreshInterval 60
-#      Domain "name"
-#      BlockDevice "name:device"
-#      InterfaceDevice "name:device"
-#      IgnoreSelected false
-#      HostnameFormat name
-#      InterfaceFormat name
-#      PluginInstanceFormat name
-#</Plugin>
-
 #<Plugin load>
 #        ReportRelative true
 #</Plugin>
 #   </Instance>
 #</Plugin>
 
+#<Plugin virt>
+#      Connection "xen:///"
+#      RefreshInterval 60
+#      Domain "name"
+#      BlockDevice "name:device"
+#      InterfaceDevice "name:device"
+#      IgnoreSelected false
+#      HostnameFormat name
+#      InterfaceFormat name
+#      PluginInstanceFormat name
+#</Plugin>
+
 #<Plugin vmem>
 #      Verbose false
 #</Plugin>
index 8ecc92d647ff7a38c5ea9310560d29b05ef9bfb4..939cfdaaa342dbfeaa5b18dddca98509bccb4667 100644 (file)
@@ -860,7 +860,7 @@ and are checked by default depends on the distribution you use.
 
 This plugin reads absolute air pressure using digital barometer sensor MPL115A2
 or MPL3115 from Freescale (sensor attached to any I2C bus available in
-the computer, for HW details see 
+the computer, for HW details see
 I<http://www.freescale.com/webapp/sps/site/prod_summary.jsp?code=MPL115A> or
 I<http://www.freescale.com/webapp/sps/site/prod_summary.jsp?code=MPL3115A2>).
 The sensor type - one fo these two - is detected automatically by the plugin
@@ -973,7 +973,7 @@ Temperature sensor which should be used as a reference when normalizing the pres
 When specified more sensors a minumum is found and uses each time.
 The temperature reading directly from this pressure sensor/plugin
 is typically not suitable as the pressure sensor
-will be probably inside while we want outside temperature. 
+will be probably inside while we want outside temperature.
 The collectd reference name is something like
 <hostname>/<plugin_name>-<plugin_instance>/<type>-<type_instance>
 (<type_instance> is usually omitted when there is just single value type).
@@ -1786,7 +1786,7 @@ There must be at least one B<ValuesFrom> option inside each B<Result> block.
 =item B<MetadataFrom> [I<column0> I<column1> ...]
 
 Names the columns whose content is used as metadata for the data sets
-that are dispatched to the daemon. 
+that are dispatched to the daemon.
 
 The actual data type in the columns is not that important. The plugin will
 automatically cast the values to the right type if it know how to do that. So
@@ -2430,101 +2430,6 @@ independent from the I<JavaClass> argument passed to B<LoadPlugin>.
 
 =back
 
-=head2 Plugin C<libvirt>
-
-This plugin allows CPU, disk and network load to be collected for virtualized
-guests on the machine. This means that these characteristics can be collected
-for guest systems without installing any software on them - collectd only runs
-on the hosting system. The statistics are collected through libvirt
-(L<http://libvirt.org/>).
-
-Only I<Connection> is required.
-
-=over 4
-
-=item B<Connection> I<uri>
-
-Connect to the hypervisor given by I<uri>. For example if using Xen use:
-
- Connection "xen:///"
-
-Details which URIs allowed are given at L<http://libvirt.org/uri.html>.
-
-=item B<RefreshInterval> I<seconds>
-
-Refresh the list of domains and devices every I<seconds>. The default is 60
-seconds. Setting this to be the same or smaller than the I<Interval> will cause
-the list of domains and devices to be refreshed on every iteration.
-
-Refreshing the devices in particular is quite a costly operation, so if your
-virtualization setup is static you might consider increasing this. If this
-option is set to 0, refreshing is disabled completely.
-
-=item B<Domain> I<name>
-
-=item B<BlockDevice> I<name:dev>
-
-=item B<InterfaceDevice> I<name:dev>
-
-=item B<IgnoreSelected> I<true>|I<false>
-
-Select which domains and devices are collected.
-
-If I<IgnoreSelected> is not given or I<false> then only the listed domains and
-disk/network devices are collected.
-
-If I<IgnoreSelected> is I<true> then the test is reversed and the listed
-domains and disk/network devices are ignored, while the rest are collected.
-
-The domain name and device names may use a regular expression, if the name is
-surrounded by I</.../> and collectd was compiled with support for regexps.
-
-The default is to collect statistics for all domains and all their devices.
-
-Example:
-
- BlockDevice "/:hdb/"
- IgnoreSelected "true"
-
-Ignore all I<hdb> devices on any domain, but other block devices (eg. I<hda>)
-will be collected.
-
-=item B<HostnameFormat> B<name|uuid|hostname|...>
-
-When the libvirt plugin logs data, it sets the hostname of the collected data
-according to this setting. The default is to use the guest name as provided by
-the hypervisor, which is equal to setting B<name>.
-
-B<uuid> means use the guest's UUID. This is useful if you want to track the
-same guest across migrations.
-
-B<hostname> means to use the global B<Hostname> setting, which is probably not
-useful on its own because all guests will appear to have the same name.
-
-You can also specify combinations of these fields. For example B<name uuid>
-means to concatenate the guest name and UUID (with a literal colon character
-between, thus I<"foo:1234-1234-1234-1234">).
-
-=item B<InterfaceFormat> B<name>|B<address>
-
-When the libvirt plugin logs interface data, it sets the name of the collected
-data according to this setting. The default is to use the path as provided by
-the hypervisor (the "dev" property of the target node), which is equal to
-setting B<name>.
-
-B<address> means use the interface's mac address. This is useful since the
-interface path might change between reboots of a guest or across migrations.
-
-=item B<PluginInstanceFormat> B<name|uuid>
-
-When the libvirt plugin logs data, it sets the plugin_instance of the collected 
-data according to this setting. The default is to use the guest name as provided 
-by the hypervisor, which is equal to setting B<name>.
-
-B<uuid> means use the guest's UUID.
-
-=back
-
 =head2 Plugin C<load>
 
 The I<Load plugin> collects the system load. These numbers give a rough overview
@@ -6504,6 +6409,101 @@ Collect statistics about worker threads. False by default.
 
 =back
 
+=head2 Plugin C<virt>
+
+This plugin allows CPU, disk and network load to be collected for virtualized
+guests on the machine. This means that these metrics can be collected for guest
+systems without installing any software on them - I<collectd> only runs on the
+host system. The statistics are collected through libvirt
+(L<http://libvirt.org/>).
+
+Only I<Connection> is required.
+
+=over 4
+
+=item B<Connection> I<uri>
+
+Connect to the hypervisor given by I<uri>. For example if using Xen use:
+
+ Connection "xen:///"
+
+Details which URIs allowed are given at L<http://libvirt.org/uri.html>.
+
+=item B<RefreshInterval> I<seconds>
+
+Refresh the list of domains and devices every I<seconds>. The default is 60
+seconds. Setting this to be the same or smaller than the I<Interval> will cause
+the list of domains and devices to be refreshed on every iteration.
+
+Refreshing the devices in particular is quite a costly operation, so if your
+virtualization setup is static you might consider increasing this. If this
+option is set to 0, refreshing is disabled completely.
+
+=item B<Domain> I<name>
+
+=item B<BlockDevice> I<name:dev>
+
+=item B<InterfaceDevice> I<name:dev>
+
+=item B<IgnoreSelected> B<true>|B<false>
+
+Select which domains and devices are collected.
+
+If I<IgnoreSelected> is not given or B<false> then only the listed domains and
+disk/network devices are collected.
+
+If I<IgnoreSelected> is B<true> then the test is reversed and the listed
+domains and disk/network devices are ignored, while the rest are collected.
+
+The domain name and device names may use a regular expression, if the name is
+surrounded by I</.../> and collectd was compiled with support for regexps.
+
+The default is to collect statistics for all domains and all their devices.
+
+Example:
+
+ BlockDevice "/:hdb/"
+ IgnoreSelected "true"
+
+Ignore all I<hdb> devices on any domain, but other block devices (eg. I<hda>)
+will be collected.
+
+=item B<HostnameFormat> B<name|uuid|hostname|...>
+
+When the libvirt plugin logs data, it sets the hostname of the collected data
+according to this setting. The default is to use the guest name as provided by
+the hypervisor, which is equal to setting B<name>.
+
+B<uuid> means use the guest's UUID. This is useful if you want to track the
+same guest across migrations.
+
+B<hostname> means to use the global B<Hostname> setting, which is probably not
+useful on its own because all guests will appear to have the same name.
+
+You can also specify combinations of these fields. For example B<name uuid>
+means to concatenate the guest name and UUID (with a literal colon character
+between, thus I<"foo:1234-1234-1234-1234">).
+
+=item B<InterfaceFormat> B<name>|B<address>
+
+When the libvirt plugin logs interface data, it sets the name of the collected
+data according to this setting. The default is to use the path as provided by
+the hypervisor (the "dev" property of the target node), which is equal to
+setting B<name>.
+
+B<address> means use the interface's mac address. This is useful since the
+interface path might change between reboots of a guest or across migrations.
+
+=item B<PluginInstanceFormat> B<name|uuid>
+
+When the libvirt plugin logs data, it sets the plugin_instance of the collected
+data according to this setting. The default is to use the guest name as provided
+by the hypervisor, which is equal to setting B<name>.
+
+B<uuid> means use the guest's UUID.
+
+=back
+
 =head2 Plugin C<vmem>
 
 The C<vmem> plugin collects information about the usage of virtual memory.
index fbb3cd3362f55a5e98b7e071074eb7893d8d6399..02fd96f6fc2a09c2dd32dca7ca7a65c1c059f068 100644 (file)
@@ -281,6 +281,8 @@ static int dispatch_loadplugin (const oconfig_item_t *ci)
                return (-1);
 
        name = ci->values[0].value.string;
+       if (strcmp ("libvirt", name) == 0)
+               name = "virt";
 
        /* default to the global interval set before loading this plugin */
        memset (&ctx, 0, sizeof (ctx));
@@ -383,6 +385,15 @@ static int dispatch_block_plugin (oconfig_item_t *ci)
                return (-1);
 
        name = ci->values[0].value.string;
+       if (strcmp ("libvirt", name) == 0)
+       {
+               /* TODO(octo): Remove this legacy. */
+               WARNING ("The \"libvirt\" plugin has been renamed to \"virt\" to avoid problems with the build system. "
+                               "Your configuration is still using the old name. "
+                               "Please change it to use \"virt\" as soon as possible. "
+                               "This compatibility code will go away eventually.");
+               name = "virt";
+       }
 
        if (IS_TRUE (global_option_get ("AutoLoadPlugin")))
        {
diff --git a/src/libvirt.c b/src/libvirt.c
deleted file mode 100644 (file)
index b0c694a..0000000
+++ /dev/null
@@ -1,1004 +0,0 @@
-/**
- * collectd - src/libvirt.c
- * Copyright (C) 2006-2008  Red Hat Inc.
- *
- * 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:
- *   Richard W.M. Jones <rjones@redhat.com>
- **/
-
-#include "collectd.h"
-#include "common.h"
-#include "plugin.h"
-#include "configfile.h"
-#include "utils_ignorelist.h"
-#include "utils_complain.h"
-
-#include <libvirt/libvirt.h>
-#include <libvirt/virterror.h>
-#include <libxml/parser.h>
-#include <libxml/tree.h>
-#include <libxml/xpath.h>
-
-/* Plugin name */
-#define PLUGIN_NAME "libvirt"
-
-static const char *config_keys[] = {
-    "Connection",
-
-    "RefreshInterval",
-
-    "Domain",
-    "BlockDevice",
-    "InterfaceDevice",
-    "IgnoreSelected",
-
-    "HostnameFormat",
-    "InterfaceFormat",
-
-    "PluginInstanceFormat",
-
-    NULL
-};
-#define NR_CONFIG_KEYS ((sizeof config_keys / sizeof config_keys[0]) - 1)
-
-/* Connection. */
-static virConnectPtr conn = 0;
-static char *conn_string = NULL;
-static c_complain_t conn_complain = C_COMPLAIN_INIT_STATIC;
-
-/* Seconds between list refreshes, 0 disables completely. */
-static int interval = 60;
-
-/* List of domains, if specified. */
-static ignorelist_t *il_domains = NULL;
-/* List of block devices, if specified. */
-static ignorelist_t *il_block_devices = NULL;
-/* List of network interface devices, if specified. */
-static ignorelist_t *il_interface_devices = NULL;
-
-static int ignore_device_match (ignorelist_t *,
-                                const char *domname, const char *devpath);
-
-/* Actual list of domains found on last refresh. */
-static virDomainPtr *domains = NULL;
-static int nr_domains = 0;
-
-static void free_domains (void);
-static int add_domain (virDomainPtr dom);
-
-/* Actual list of block devices found on last refresh. */
-struct block_device {
-    virDomainPtr dom;           /* domain */
-    char *path;                 /* name of block device */
-};
-
-static struct block_device *block_devices = NULL;
-static int nr_block_devices = 0;
-
-static void free_block_devices (void);
-static int add_block_device (virDomainPtr dom, const char *path);
-
-/* Actual list of network interfaces found on last refresh. */
-struct interface_device {
-    virDomainPtr dom;           /* domain */
-    char *path;                 /* name of interface device */
-    char *address;              /* mac address of interface device */
-    char *number;               /* interface device number */
-};
-
-static struct interface_device *interface_devices = NULL;
-static int nr_interface_devices = 0;
-
-static void free_interface_devices (void);
-static int add_interface_device (virDomainPtr dom, const char *path, const char *address, unsigned int number);
-
-/* HostnameFormat. */
-#define HF_MAX_FIELDS 3
-
-enum hf_field {
-    hf_none = 0,
-    hf_hostname,
-    hf_name,
-    hf_uuid
-};
-
-static enum hf_field hostname_format[HF_MAX_FIELDS] =
-    { hf_name };
-
-/* PluginInstanceFormat */
-#define PLGINST_MAX_FIELDS 2
-
-enum plginst_field {
-    plginst_none = 0,
-    plginst_name,
-    plginst_uuid
-};
-
-static enum plginst_field plugin_instance_format[PLGINST_MAX_FIELDS] =
-    { plginst_name };
-
-/* InterfaceFormat. */
-enum if_field {
-    if_address,
-    if_name,
-    if_number
-};
-
-static enum if_field interface_format = if_name;
-
-/* Time that we last refreshed. */
-static time_t last_refresh = (time_t) 0;
-
-static int refresh_lists (void);
-
-/* ERROR(...) macro for virterrors. */
-#define VIRT_ERROR(conn,s) do {                 \
-        virErrorPtr err;                        \
-        err = (conn) ? virConnGetLastError ((conn)) : virGetLastError (); \
-        if (err) ERROR ("%s: %s", (s), err->message);                   \
-    } while(0)
-
-static void
-init_value_list (value_list_t *vl, virDomainPtr dom)
-{
-    int i, n;
-    const char *name;
-    char uuid[VIR_UUID_STRING_BUFLEN];
-
-    sstrncpy (vl->plugin, PLUGIN_NAME, sizeof (vl->plugin));
-
-    vl->host[0] = '\0';
-
-    /* Construct the hostname field according to HostnameFormat. */
-    for (i = 0; i < HF_MAX_FIELDS; ++i) {
-        if (hostname_format[i] == hf_none)
-            continue;
-
-        n = DATA_MAX_NAME_LEN - strlen (vl->host) - 2;
-
-        if (i > 0 && n >= 1) {
-            strncat (vl->host, ":", 1);
-            n--;
-        }
-
-        switch (hostname_format[i]) {
-        case hf_none: break;
-        case hf_hostname:
-            strncat (vl->host, hostname_g, n);
-            break;
-        case hf_name:
-            name = virDomainGetName (dom);
-            if (name)
-                strncat (vl->host, name, n);
-            break;
-        case hf_uuid:
-            if (virDomainGetUUIDString (dom, uuid) == 0)
-                strncat (vl->host, uuid, n);
-            break;
-        }
-    }
-
-    vl->host[sizeof (vl->host) - 1] = '\0';
-
-    /* Construct the plugin instance field according to PluginInstanceFormat. */
-    for (i = 0; i < PLGINST_MAX_FIELDS; ++i) {
-        if (plugin_instance_format[i] == plginst_none)
-            continue;
-
-        n = sizeof(vl->plugin_instance) - strlen (vl->plugin_instance) - 2;
-
-        if (i > 0 && n >= 1) {
-            strncat (vl->plugin_instance, ":", 1);
-            n--;
-        }
-
-        switch (plugin_instance_format[i]) {
-        case plginst_none: break;
-        case plginst_name:
-            name = virDomainGetName (dom);
-            if (name)
-                strncat (vl->plugin_instance, name, n);
-            break;
-        case plginst_uuid:
-            if (virDomainGetUUIDString (dom, uuid) == 0)
-                strncat (vl->plugin_instance, uuid, n);
-            break;
-        }
-    }
-
-    vl->plugin_instance[sizeof (vl->plugin_instance) - 1] = '\0';
-
-} /* void init_value_list */
-
-static void
-memory_submit (gauge_t memory, virDomainPtr dom)
-{
-    value_t values[1];
-    value_list_t vl = VALUE_LIST_INIT;
-
-    init_value_list (&vl, dom);
-
-    values[0].gauge = memory;
-
-    vl.values = values;
-    vl.values_len = 1;
-
-    sstrncpy (vl.type, "memory", sizeof (vl.type));
-    sstrncpy (vl.type_instance, "total", sizeof (vl.type_instance));
-
-    plugin_dispatch_values (&vl);
-}
-
-static void
-memory_stats_submit (gauge_t memory, virDomainPtr dom, int tag_index)
-{
-    static const char *tags[] = { "swap_in", "swap_out", "major_fault", "minor_fault",
-                                    "unused", "available", "actual_balloon", "rss"};
-
-    value_t values[1];
-    value_list_t vl = VALUE_LIST_INIT;
-
-    init_value_list (&vl, dom);
-
-    values[0].gauge = memory;
-
-    vl.values = values;
-    vl.values_len = 1;
-
-    sstrncpy (vl.type, "memory", sizeof (vl.type));
-    sstrncpy (vl.type_instance, tags[tag_index], sizeof (vl.type_instance));
-
-    plugin_dispatch_values (&vl);
-}
-
-static void
-cpu_submit (unsigned long long cpu_time,
-            virDomainPtr dom, const char *type)
-{
-    value_t values[1];
-    value_list_t vl = VALUE_LIST_INIT;
-
-    init_value_list (&vl, dom);
-
-    values[0].derive = cpu_time;
-
-    vl.values = values;
-    vl.values_len = 1;
-
-    sstrncpy (vl.type, type, sizeof (vl.type));
-
-    plugin_dispatch_values (&vl);
-}
-
-static void
-vcpu_submit (derive_t cpu_time,
-             virDomainPtr dom, int vcpu_nr, const char *type)
-{
-    value_t values[1];
-    value_list_t vl = VALUE_LIST_INIT;
-
-    init_value_list (&vl, dom);
-
-    values[0].derive = cpu_time;
-    vl.values = values;
-    vl.values_len = 1;
-
-    sstrncpy (vl.type, type, sizeof (vl.type));
-    ssnprintf (vl.type_instance, sizeof (vl.type_instance), "%d", vcpu_nr);
-
-    plugin_dispatch_values (&vl);
-}
-
-static void
-submit_derive2 (const char *type, derive_t v0, derive_t v1,
-             virDomainPtr dom, const char *devname)
-{
-    value_t values[2];
-    value_list_t vl = VALUE_LIST_INIT;
-
-    init_value_list (&vl, dom);
-
-    values[0].derive = v0;
-    values[1].derive = v1;
-    vl.values = values;
-    vl.values_len = 2;
-
-    sstrncpy (vl.type, type, sizeof (vl.type));
-    sstrncpy (vl.type_instance, devname, sizeof (vl.type_instance));
-
-    plugin_dispatch_values (&vl);
-} /* void submit_derive2 */
-
-static int
-lv_init (void)
-{
-    if (virInitialize () != 0)
-        return -1;
-
-       return 0;
-}
-
-static int
-lv_config (const char *key, const char *value)
-{
-    if (virInitialize () != 0)
-        return 1;
-
-    if (il_domains == NULL)
-        il_domains = ignorelist_create (1);
-    if (il_block_devices == NULL)
-        il_block_devices = ignorelist_create (1);
-    if (il_interface_devices == NULL)
-        il_interface_devices = ignorelist_create (1);
-
-    if (strcasecmp (key, "Connection") == 0) {
-        char *tmp = strdup (value);
-        if (tmp == NULL) {
-            ERROR (PLUGIN_NAME " plugin: Connection strdup failed.");
-            return 1;
-        }
-        sfree (conn_string);
-        conn_string = tmp;
-        return 0;
-    }
-
-    if (strcasecmp (key, "RefreshInterval") == 0) {
-        char *eptr = NULL;
-        interval = strtol (value, &eptr, 10);
-        if (eptr == NULL || *eptr != '\0') return 1;
-        return 0;
-    }
-
-    if (strcasecmp (key, "Domain") == 0) {
-        if (ignorelist_add (il_domains, value)) return 1;
-        return 0;
-    }
-    if (strcasecmp (key, "BlockDevice") == 0) {
-        if (ignorelist_add (il_block_devices, value)) return 1;
-        return 0;
-    }
-    if (strcasecmp (key, "InterfaceDevice") == 0) {
-        if (ignorelist_add (il_interface_devices, value)) return 1;
-        return 0;
-    }
-
-    if (strcasecmp (key, "IgnoreSelected") == 0) {
-        if (IS_TRUE (value))
-        {
-            ignorelist_set_invert (il_domains, 0);
-            ignorelist_set_invert (il_block_devices, 0);
-            ignorelist_set_invert (il_interface_devices, 0);
-        }
-        else
-        {
-            ignorelist_set_invert (il_domains, 1);
-            ignorelist_set_invert (il_block_devices, 1);
-            ignorelist_set_invert (il_interface_devices, 1);
-        }
-        return 0;
-    }
-
-    if (strcasecmp (key, "HostnameFormat") == 0) {
-        char *value_copy;
-        char *fields[HF_MAX_FIELDS];
-        int i, n;
-
-        value_copy = strdup (value);
-        if (value_copy == NULL) {
-            ERROR (PLUGIN_NAME " plugin: strdup failed.");
-            return -1;
-        }
-
-        n = strsplit (value_copy, fields, HF_MAX_FIELDS);
-        if (n < 1) {
-            sfree (value_copy);
-            ERROR (PLUGIN_NAME " plugin: HostnameFormat: no fields");
-            return -1;
-        }
-
-        for (i = 0; i < n; ++i) {
-            if (strcasecmp (fields[i], "hostname") == 0)
-                hostname_format[i] = hf_hostname;
-            else if (strcasecmp (fields[i], "name") == 0)
-                hostname_format[i] = hf_name;
-            else if (strcasecmp (fields[i], "uuid") == 0)
-                hostname_format[i] = hf_uuid;
-            else {
-                sfree (value_copy);
-                ERROR (PLUGIN_NAME " plugin: unknown HostnameFormat field: %s", fields[i]);
-                return -1;
-            }
-        }
-        sfree (value_copy);
-
-        for (i = n; i < HF_MAX_FIELDS; ++i)
-            hostname_format[i] = hf_none;
-
-        return 0;
-    }
-
-    if (strcasecmp (key, "PluginInstanceFormat") == 0) {
-        char *value_copy;
-        char *fields[PLGINST_MAX_FIELDS];
-        int i, n;
-
-        value_copy = strdup (value);
-        if (value_copy == NULL) {
-            ERROR (PLUGIN_NAME " plugin: strdup failed.");
-            return -1;
-        }
-
-        n = strsplit (value_copy, fields, PLGINST_MAX_FIELDS);
-        if (n < 1) {
-            sfree (value_copy);
-            ERROR (PLUGIN_NAME " plugin: PluginInstanceFormat: no fields");
-            return -1;
-        }
-
-        for (i = 0; i < n; ++i) {
-            if (strcasecmp (fields[i], "name") == 0)
-                plugin_instance_format[i] = plginst_name;
-            else if (strcasecmp (fields[i], "uuid") == 0)
-                plugin_instance_format[i] = plginst_uuid;
-            else {
-                sfree (value_copy);
-                ERROR (PLUGIN_NAME " plugin: unknown HostnameFormat field: %s", fields[i]);
-                return -1;
-            }
-        }
-        sfree (value_copy);
-
-        for (i = n; i < PLGINST_MAX_FIELDS; ++i)
-            plugin_instance_format[i] = plginst_none;
-
-        return 0;
-    }
-
-    if (strcasecmp (key, "InterfaceFormat") == 0) {
-        if (strcasecmp (value, "name") == 0)
-            interface_format = if_name;
-        else if (strcasecmp (value, "address") == 0)
-            interface_format = if_address;
-        else if (strcasecmp (value, "number") == 0)
-            interface_format = if_number;
-        else {
-            ERROR (PLUGIN_NAME " plugin: unknown InterfaceFormat: %s", value);
-            return -1;
-        }
-        return 0;
-    }
-
-    /* Unrecognised option. */
-    return -1;
-}
-
-static int
-lv_read (void)
-{
-    time_t t;
-    int i;
-
-    if (conn == NULL) {
-        /* `conn_string == NULL' is acceptable. */
-        conn = virConnectOpenReadOnly (conn_string);
-        if (conn == NULL) {
-            c_complain (LOG_ERR, &conn_complain,
-                    PLUGIN_NAME " plugin: Unable to connect: "
-                    "virConnectOpenReadOnly failed.");
-            return -1;
-        }
-    }
-    c_release (LOG_NOTICE, &conn_complain,
-            PLUGIN_NAME " plugin: Connection established.");
-
-    time (&t);
-
-    /* Need to refresh domain or device lists? */
-    if ((last_refresh == (time_t) 0) ||
-            ((interval > 0) && ((last_refresh + interval) <= t))) {
-        if (refresh_lists () != 0) {
-            if (conn != NULL)
-                virConnectClose (conn);
-            conn = NULL;
-            return -1;
-        }
-        last_refresh = t;
-    }
-
-#if 0
-    for (i = 0; i < nr_domains; ++i)
-        fprintf (stderr, "domain %s\n", virDomainGetName (domains[i]));
-    for (i = 0; i < nr_block_devices; ++i)
-        fprintf  (stderr, "block device %d %s:%s\n",
-                  i, virDomainGetName (block_devices[i].dom),
-                  block_devices[i].path);
-    for (i = 0; i < nr_interface_devices; ++i)
-        fprintf (stderr, "interface device %d %s:%s\n",
-                 i, virDomainGetName (interface_devices[i].dom),
-                 interface_devices[i].path);
-#endif
-
-    /* Get CPU usage, memory, VCPU usage for each domain. */
-    for (i = 0; i < nr_domains; ++i) {
-        virDomainInfo info;
-        virVcpuInfoPtr vinfo = NULL;
-        virDomainMemoryStatPtr minfo = NULL;
-        int status;
-        int j;
-
-        status = virDomainGetInfo (domains[i], &info);
-        if (status != 0)
-        {
-            ERROR (PLUGIN_NAME " plugin: virDomainGetInfo failed with status %i.",
-                    status);
-            continue;
-        }
-
-        cpu_submit (info.cpuTime, domains[i], "virt_cpu_total");
-        memory_submit ((gauge_t) info.memory * 1024, domains[i]);
-
-        vinfo = malloc (info.nrVirtCpu * sizeof (vinfo[0]));
-        if (vinfo == NULL) {
-            ERROR (PLUGIN_NAME " plugin: malloc failed.");
-            continue;
-        }
-
-        status = virDomainGetVcpus (domains[i], vinfo, info.nrVirtCpu,
-                /* cpu map = */ NULL, /* cpu map length = */ 0);
-        if (status < 0)
-        {
-            ERROR (PLUGIN_NAME " plugin: virDomainGetVcpus failed with status %i.",
-                    status);
-            sfree (vinfo);
-            continue;
-        }
-
-        for (j = 0; j < info.nrVirtCpu; ++j)
-            vcpu_submit (vinfo[j].cpuTime,
-                    domains[i], vinfo[j].number, "virt_vcpu");
-
-        sfree (vinfo);
-
-        minfo = malloc (VIR_DOMAIN_MEMORY_STAT_NR * sizeof (virDomainMemoryStatStruct));
-        if (minfo == NULL) {
-            ERROR ("libvirt plugin: malloc failed.");
-            continue;
-        }
-
-        status =  virDomainMemoryStats (domains[i], minfo, VIR_DOMAIN_MEMORY_STAT_NR, 0);
-
-        if (status < 0) {
-            ERROR ("libvirt plugin: virDomainMemoryStats failed with status %i.",
-                    status);
-            sfree (minfo);
-            continue;
-        }
-
-        for (j = 0; j < status; j++) {
-            memory_stats_submit ((gauge_t) minfo[j].val, domains[i], minfo[j].tag);
-        }
-
-        sfree (minfo);
-    }
-
-
-    /* Get block device stats for each domain. */
-    for (i = 0; i < nr_block_devices; ++i) {
-        struct _virDomainBlockStats stats;
-
-        if (virDomainBlockStats (block_devices[i].dom, block_devices[i].path,
-                    &stats, sizeof stats) != 0)
-            continue;
-
-        if ((stats.rd_req != -1) && (stats.wr_req != -1))
-            submit_derive2 ("disk_ops",
-                    (derive_t) stats.rd_req, (derive_t) stats.wr_req,
-                    block_devices[i].dom, block_devices[i].path);
-
-        if ((stats.rd_bytes != -1) && (stats.wr_bytes != -1))
-            submit_derive2 ("disk_octets",
-                    (derive_t) stats.rd_bytes, (derive_t) stats.wr_bytes,
-                    block_devices[i].dom, block_devices[i].path);
-    } /* for (nr_block_devices) */
-
-    /* Get interface stats for each domain. */
-    for (i = 0; i < nr_interface_devices; ++i) {
-        struct _virDomainInterfaceStats stats;
-        char *display_name = NULL;
-
-
-        switch (interface_format) {
-            case if_address:
-                display_name = interface_devices[i].address;
-                break;
-            case if_number:
-                display_name = interface_devices[i].number;
-                break;
-            case if_name:
-            default:
-                display_name = interface_devices[i].path;
-        }
-
-        if (virDomainInterfaceStats (interface_devices[i].dom,
-                    interface_devices[i].path,
-                    &stats, sizeof stats) != 0)
-            continue;
-
-       if ((stats.rx_bytes != -1) && (stats.tx_bytes != -1))
-           submit_derive2 ("if_octets",
-                   (derive_t) stats.rx_bytes, (derive_t) stats.tx_bytes,
-                   interface_devices[i].dom, display_name);
-
-       if ((stats.rx_packets != -1) && (stats.tx_packets != -1))
-           submit_derive2 ("if_packets",
-                   (derive_t) stats.rx_packets, (derive_t) stats.tx_packets,
-                   interface_devices[i].dom, display_name);
-
-       if ((stats.rx_errs != -1) && (stats.tx_errs != -1))
-           submit_derive2 ("if_errors",
-                   (derive_t) stats.rx_errs, (derive_t) stats.tx_errs,
-                   interface_devices[i].dom, display_name);
-
-       if ((stats.rx_drop != -1) && (stats.tx_drop != -1))
-           submit_derive2 ("if_dropped",
-                   (derive_t) stats.rx_drop, (derive_t) stats.tx_drop,
-                   interface_devices[i].dom, display_name);
-    } /* for (nr_interface_devices) */
-
-    return 0;
-}
-
-static int
-refresh_lists (void)
-{
-    int n;
-
-    n = virConnectNumOfDomains (conn);
-    if (n < 0) {
-        VIRT_ERROR (conn, "reading number of domains");
-        return -1;
-    }
-
-    if (n > 0) {
-        int i;
-        int *domids;
-
-        /* Get list of domains. */
-        domids = malloc (sizeof (int) * n);
-        if (domids == 0) {
-            ERROR (PLUGIN_NAME " plugin: malloc failed.");
-            return -1;
-        }
-
-        n = virConnectListDomains (conn, domids, n);
-        if (n < 0) {
-            VIRT_ERROR (conn, "reading list of domains");
-            sfree (domids);
-            return -1;
-        }
-
-        free_block_devices ();
-        free_interface_devices ();
-        free_domains ();
-
-        /* Fetch each domain and add it to the list, unless ignore. */
-        for (i = 0; i < n; ++i) {
-            virDomainPtr dom = NULL;
-            const char *name;
-            char *xml = NULL;
-            xmlDocPtr xml_doc = NULL;
-            xmlXPathContextPtr xpath_ctx = NULL;
-            xmlXPathObjectPtr xpath_obj = NULL;
-            int j;
-
-            dom = virDomainLookupByID (conn, domids[i]);
-            if (dom == NULL) {
-                VIRT_ERROR (conn, "virDomainLookupByID");
-                /* Could be that the domain went away -- ignore it anyway. */
-                continue;
-            }
-
-            name = virDomainGetName (dom);
-            if (name == NULL) {
-                VIRT_ERROR (conn, "virDomainGetName");
-                goto cont;
-            }
-
-            if (il_domains && ignorelist_match (il_domains, name) != 0)
-                goto cont;
-
-            if (add_domain (dom) < 0) {
-                ERROR (PLUGIN_NAME " plugin: malloc failed.");
-                goto cont;
-            }
-
-            /* Get a list of devices for this domain. */
-            xml = virDomainGetXMLDesc (dom, 0);
-            if (!xml) {
-                VIRT_ERROR (conn, "virDomainGetXMLDesc");
-                goto cont;
-            }
-
-            /* Yuck, XML.  Parse out the devices. */
-            xml_doc = xmlReadDoc ((xmlChar *) xml, NULL, NULL, XML_PARSE_NONET);
-            if (xml_doc == NULL) {
-                VIRT_ERROR (conn, "xmlReadDoc");
-                goto cont;
-            }
-
-            xpath_ctx = xmlXPathNewContext (xml_doc);
-
-            /* Block devices. */
-            xpath_obj = xmlXPathEval
-                ((xmlChar *) "/domain/devices/disk/target[@dev]",
-                 xpath_ctx);
-            if (xpath_obj == NULL || xpath_obj->type != XPATH_NODESET ||
-                xpath_obj->nodesetval == NULL)
-                goto cont;
-
-            for (j = 0; j < xpath_obj->nodesetval->nodeNr; ++j) {
-                xmlNodePtr node;
-                char *path = NULL;
-
-                node = xpath_obj->nodesetval->nodeTab[j];
-                if (!node) continue;
-                path = (char *) xmlGetProp (node, (xmlChar *) "dev");
-                if (!path) continue;
-
-                if (il_block_devices &&
-                    ignore_device_match (il_block_devices, name, path) != 0)
-                    goto cont2;
-
-                add_block_device (dom, path);
-            cont2:
-                if (path) xmlFree (path);
-            }
-            xmlXPathFreeObject (xpath_obj);
-
-            /* Network interfaces. */
-            xpath_obj = xmlXPathEval
-                ((xmlChar *) "/domain/devices/interface[target[@dev]]",
-                 xpath_ctx);
-            if (xpath_obj == NULL || xpath_obj->type != XPATH_NODESET ||
-                xpath_obj->nodesetval == NULL)
-                goto cont;
-
-            xmlNodeSetPtr xml_interfaces = xpath_obj->nodesetval;
-
-            for (j = 0; j < xml_interfaces->nodeNr; ++j) {
-                char *path = NULL;
-                char *address = NULL;
-                xmlNodePtr xml_interface;
-
-                xml_interface = xml_interfaces->nodeTab[j];
-                if (!xml_interface) continue;
-                xmlNodePtr child = NULL;
-
-                for (child = xml_interface->children; child; child = child->next) {
-                    if (child->type != XML_ELEMENT_NODE) continue;
-
-                    if (xmlStrEqual(child->name, (const xmlChar *) "target")) {
-                        path = (char *) xmlGetProp (child, (const xmlChar *) "dev");
-                        if (!path) continue;
-                    } else if (xmlStrEqual(child->name, (const xmlChar *) "mac")) {
-                        address = (char *) xmlGetProp (child, (const xmlChar *) "address");
-                        if (!address) continue;
-                    }
-                }
-
-                if (il_interface_devices &&
-                    (ignore_device_match (il_interface_devices, name, path) != 0 ||
-                     ignore_device_match (il_interface_devices, name, address) != 0))
-                    goto cont3;
-
-                add_interface_device (dom, path, address, j+1);
-                cont3:
-                    if (path) xmlFree (path);
-                    if (address) xmlFree (address);
-            }
-
-        cont:
-            if (xpath_obj) xmlXPathFreeObject (xpath_obj);
-            if (xpath_ctx) xmlXPathFreeContext (xpath_ctx);
-            if (xml_doc) xmlFreeDoc (xml_doc);
-            sfree (xml);
-        }
-
-        sfree (domids);
-    }
-
-    return 0;
-}
-
-static void
-free_domains ()
-{
-    int i;
-
-    if (domains) {
-        for (i = 0; i < nr_domains; ++i)
-            virDomainFree (domains[i]);
-        sfree (domains);
-    }
-    domains = NULL;
-    nr_domains = 0;
-}
-
-static int
-add_domain (virDomainPtr dom)
-{
-    virDomainPtr *new_ptr;
-    int new_size = sizeof (domains[0]) * (nr_domains+1);
-
-    if (domains)
-        new_ptr = realloc (domains, new_size);
-    else
-        new_ptr = malloc (new_size);
-
-    if (new_ptr == NULL)
-        return -1;
-
-    domains = new_ptr;
-    domains[nr_domains] = dom;
-    return nr_domains++;
-}
-
-static void
-free_block_devices ()
-{
-    int i;
-
-    if (block_devices) {
-        for (i = 0; i < nr_block_devices; ++i)
-            sfree (block_devices[i].path);
-        sfree (block_devices);
-    }
-    block_devices = NULL;
-    nr_block_devices = 0;
-}
-
-static int
-add_block_device (virDomainPtr dom, const char *path)
-{
-    struct block_device *new_ptr;
-    int new_size = sizeof (block_devices[0]) * (nr_block_devices+1);
-    char *path_copy;
-
-    path_copy = strdup (path);
-    if (!path_copy)
-        return -1;
-
-    if (block_devices)
-        new_ptr = realloc (block_devices, new_size);
-    else
-        new_ptr = malloc (new_size);
-
-    if (new_ptr == NULL) {
-        sfree (path_copy);
-        return -1;
-    }
-    block_devices = new_ptr;
-    block_devices[nr_block_devices].dom = dom;
-    block_devices[nr_block_devices].path = path_copy;
-    return nr_block_devices++;
-}
-
-static void
-free_interface_devices ()
-{
-    int i;
-
-    if (interface_devices) {
-        for (i = 0; i < nr_interface_devices; ++i) {
-            sfree (interface_devices[i].path);
-            sfree (interface_devices[i].address);
-            sfree (interface_devices[i].number);
-        }
-        sfree (interface_devices);
-    }
-    interface_devices = NULL;
-    nr_interface_devices = 0;
-}
-
-static int
-add_interface_device (virDomainPtr dom, const char *path, const char *address, unsigned int number)
-{
-    struct interface_device *new_ptr;
-    int new_size = sizeof (interface_devices[0]) * (nr_interface_devices+1);
-    char *path_copy, *address_copy, number_string[15];
-
-    path_copy = strdup (path);
-    if (!path_copy) return -1;
-
-    address_copy = strdup (address);
-    if (!address_copy) {
-        sfree(path_copy);
-        return -1;
-    }
-
-    snprintf(number_string, sizeof (number_string), "interface-%u", number);
-
-    if (interface_devices)
-        new_ptr = realloc (interface_devices, new_size);
-    else
-        new_ptr = malloc (new_size);
-
-    if (new_ptr == NULL) {
-        sfree (path_copy);
-        sfree (address_copy);
-        return -1;
-    }
-    interface_devices = new_ptr;
-    interface_devices[nr_interface_devices].dom = dom;
-    interface_devices[nr_interface_devices].path = path_copy;
-    interface_devices[nr_interface_devices].address = address_copy;
-    interface_devices[nr_interface_devices].number = strdup(number_string);
-    return nr_interface_devices++;
-}
-
-static int
-ignore_device_match (ignorelist_t *il, const char *domname, const char *devpath)
-{
-    char *name;
-    int n, r;
-
-    n = sizeof (char) * (strlen (domname) + strlen (devpath) + 2);
-    name = malloc (n);
-    if (name == NULL) {
-        ERROR (PLUGIN_NAME " plugin: malloc failed.");
-        return 0;
-    }
-    ssnprintf (name, n, "%s:%s", domname, devpath);
-    r = ignorelist_match (il, name);
-    sfree (name);
-    return r;
-}
-
-static int
-lv_shutdown (void)
-{
-    free_block_devices ();
-    free_interface_devices ();
-    free_domains ();
-
-    if (conn != NULL)
-        virConnectClose (conn);
-    conn = NULL;
-
-    ignorelist_free (il_domains);
-    il_domains = NULL;
-    ignorelist_free (il_block_devices);
-    il_block_devices = NULL;
-    ignorelist_free (il_interface_devices);
-    il_interface_devices = NULL;
-
-    return 0;
-}
-
-void
-module_register (void)
-{
-    plugin_register_config (PLUGIN_NAME,
-    lv_config,
-    config_keys, NR_CONFIG_KEYS);
-    plugin_register_init (PLUGIN_NAME, lv_init);
-    plugin_register_read (PLUGIN_NAME, lv_read);
-    plugin_register_shutdown (PLUGIN_NAME, lv_shutdown);
-}
-
-/*
- * vim: shiftwidth=4 tabstop=8 softtabstop=4 expandtab fdm=marker
- */
diff --git a/src/virt.c b/src/virt.c
new file mode 100644 (file)
index 0000000..6118c0f
--- /dev/null
@@ -0,0 +1,1004 @@
+/**
+ * collectd - src/virt.c
+ * Copyright (C) 2006-2008  Red Hat Inc.
+ *
+ * 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:
+ *   Richard W.M. Jones <rjones@redhat.com>
+ **/
+
+#include "collectd.h"
+#include "common.h"
+#include "plugin.h"
+#include "configfile.h"
+#include "utils_ignorelist.h"
+#include "utils_complain.h"
+
+#include <libvirt/libvirt.h>
+#include <libvirt/virterror.h>
+#include <libxml/parser.h>
+#include <libxml/tree.h>
+#include <libxml/xpath.h>
+
+/* Plugin name */
+#define PLUGIN_NAME "virt"
+
+static const char *config_keys[] = {
+    "Connection",
+
+    "RefreshInterval",
+
+    "Domain",
+    "BlockDevice",
+    "InterfaceDevice",
+    "IgnoreSelected",
+
+    "HostnameFormat",
+    "InterfaceFormat",
+
+    "PluginInstanceFormat",
+
+    NULL
+};
+#define NR_CONFIG_KEYS ((sizeof config_keys / sizeof config_keys[0]) - 1)
+
+/* Connection. */
+static virConnectPtr conn = 0;
+static char *conn_string = NULL;
+static c_complain_t conn_complain = C_COMPLAIN_INIT_STATIC;
+
+/* Seconds between list refreshes, 0 disables completely. */
+static int interval = 60;
+
+/* List of domains, if specified. */
+static ignorelist_t *il_domains = NULL;
+/* List of block devices, if specified. */
+static ignorelist_t *il_block_devices = NULL;
+/* List of network interface devices, if specified. */
+static ignorelist_t *il_interface_devices = NULL;
+
+static int ignore_device_match (ignorelist_t *,
+                                const char *domname, const char *devpath);
+
+/* Actual list of domains found on last refresh. */
+static virDomainPtr *domains = NULL;
+static int nr_domains = 0;
+
+static void free_domains (void);
+static int add_domain (virDomainPtr dom);
+
+/* Actual list of block devices found on last refresh. */
+struct block_device {
+    virDomainPtr dom;           /* domain */
+    char *path;                 /* name of block device */
+};
+
+static struct block_device *block_devices = NULL;
+static int nr_block_devices = 0;
+
+static void free_block_devices (void);
+static int add_block_device (virDomainPtr dom, const char *path);
+
+/* Actual list of network interfaces found on last refresh. */
+struct interface_device {
+    virDomainPtr dom;           /* domain */
+    char *path;                 /* name of interface device */
+    char *address;              /* mac address of interface device */
+    char *number;               /* interface device number */
+};
+
+static struct interface_device *interface_devices = NULL;
+static int nr_interface_devices = 0;
+
+static void free_interface_devices (void);
+static int add_interface_device (virDomainPtr dom, const char *path, const char *address, unsigned int number);
+
+/* HostnameFormat. */
+#define HF_MAX_FIELDS 3
+
+enum hf_field {
+    hf_none = 0,
+    hf_hostname,
+    hf_name,
+    hf_uuid
+};
+
+static enum hf_field hostname_format[HF_MAX_FIELDS] =
+    { hf_name };
+
+/* PluginInstanceFormat */
+#define PLGINST_MAX_FIELDS 2
+
+enum plginst_field {
+    plginst_none = 0,
+    plginst_name,
+    plginst_uuid
+};
+
+static enum plginst_field plugin_instance_format[PLGINST_MAX_FIELDS] =
+    { plginst_name };
+
+/* InterfaceFormat. */
+enum if_field {
+    if_address,
+    if_name,
+    if_number
+};
+
+static enum if_field interface_format = if_name;
+
+/* Time that we last refreshed. */
+static time_t last_refresh = (time_t) 0;
+
+static int refresh_lists (void);
+
+/* ERROR(...) macro for virterrors. */
+#define VIRT_ERROR(conn,s) do {                 \
+        virErrorPtr err;                        \
+        err = (conn) ? virConnGetLastError ((conn)) : virGetLastError (); \
+        if (err) ERROR ("%s: %s", (s), err->message);                   \
+    } while(0)
+
+static void
+init_value_list (value_list_t *vl, virDomainPtr dom)
+{
+    int i, n;
+    const char *name;
+    char uuid[VIR_UUID_STRING_BUFLEN];
+
+    sstrncpy (vl->plugin, PLUGIN_NAME, sizeof (vl->plugin));
+
+    vl->host[0] = '\0';
+
+    /* Construct the hostname field according to HostnameFormat. */
+    for (i = 0; i < HF_MAX_FIELDS; ++i) {
+        if (hostname_format[i] == hf_none)
+            continue;
+
+        n = DATA_MAX_NAME_LEN - strlen (vl->host) - 2;
+
+        if (i > 0 && n >= 1) {
+            strncat (vl->host, ":", 1);
+            n--;
+        }
+
+        switch (hostname_format[i]) {
+        case hf_none: break;
+        case hf_hostname:
+            strncat (vl->host, hostname_g, n);
+            break;
+        case hf_name:
+            name = virDomainGetName (dom);
+            if (name)
+                strncat (vl->host, name, n);
+            break;
+        case hf_uuid:
+            if (virDomainGetUUIDString (dom, uuid) == 0)
+                strncat (vl->host, uuid, n);
+            break;
+        }
+    }
+
+    vl->host[sizeof (vl->host) - 1] = '\0';
+
+    /* Construct the plugin instance field according to PluginInstanceFormat. */
+    for (i = 0; i < PLGINST_MAX_FIELDS; ++i) {
+        if (plugin_instance_format[i] == plginst_none)
+            continue;
+
+        n = sizeof(vl->plugin_instance) - strlen (vl->plugin_instance) - 2;
+
+        if (i > 0 && n >= 1) {
+            strncat (vl->plugin_instance, ":", 1);
+            n--;
+        }
+
+        switch (plugin_instance_format[i]) {
+        case plginst_none: break;
+        case plginst_name:
+            name = virDomainGetName (dom);
+            if (name)
+                strncat (vl->plugin_instance, name, n);
+            break;
+        case plginst_uuid:
+            if (virDomainGetUUIDString (dom, uuid) == 0)
+                strncat (vl->plugin_instance, uuid, n);
+            break;
+        }
+    }
+
+    vl->plugin_instance[sizeof (vl->plugin_instance) - 1] = '\0';
+
+} /* void init_value_list */
+
+static void
+memory_submit (gauge_t memory, virDomainPtr dom)
+{
+    value_t values[1];
+    value_list_t vl = VALUE_LIST_INIT;
+
+    init_value_list (&vl, dom);
+
+    values[0].gauge = memory;
+
+    vl.values = values;
+    vl.values_len = 1;
+
+    sstrncpy (vl.type, "memory", sizeof (vl.type));
+    sstrncpy (vl.type_instance, "total", sizeof (vl.type_instance));
+
+    plugin_dispatch_values (&vl);
+}
+
+static void
+memory_stats_submit (gauge_t memory, virDomainPtr dom, int tag_index)
+{
+    static const char *tags[] = { "swap_in", "swap_out", "major_fault", "minor_fault",
+                                    "unused", "available", "actual_balloon", "rss"};
+
+    value_t values[1];
+    value_list_t vl = VALUE_LIST_INIT;
+
+    init_value_list (&vl, dom);
+
+    values[0].gauge = memory;
+
+    vl.values = values;
+    vl.values_len = 1;
+
+    sstrncpy (vl.type, "memory", sizeof (vl.type));
+    sstrncpy (vl.type_instance, tags[tag_index], sizeof (vl.type_instance));
+
+    plugin_dispatch_values (&vl);
+}
+
+static void
+cpu_submit (unsigned long long cpu_time,
+            virDomainPtr dom, const char *type)
+{
+    value_t values[1];
+    value_list_t vl = VALUE_LIST_INIT;
+
+    init_value_list (&vl, dom);
+
+    values[0].derive = cpu_time;
+
+    vl.values = values;
+    vl.values_len = 1;
+
+    sstrncpy (vl.type, type, sizeof (vl.type));
+
+    plugin_dispatch_values (&vl);
+}
+
+static void
+vcpu_submit (derive_t cpu_time,
+             virDomainPtr dom, int vcpu_nr, const char *type)
+{
+    value_t values[1];
+    value_list_t vl = VALUE_LIST_INIT;
+
+    init_value_list (&vl, dom);
+
+    values[0].derive = cpu_time;
+    vl.values = values;
+    vl.values_len = 1;
+
+    sstrncpy (vl.type, type, sizeof (vl.type));
+    ssnprintf (vl.type_instance, sizeof (vl.type_instance), "%d", vcpu_nr);
+
+    plugin_dispatch_values (&vl);
+}
+
+static void
+submit_derive2 (const char *type, derive_t v0, derive_t v1,
+             virDomainPtr dom, const char *devname)
+{
+    value_t values[2];
+    value_list_t vl = VALUE_LIST_INIT;
+
+    init_value_list (&vl, dom);
+
+    values[0].derive = v0;
+    values[1].derive = v1;
+    vl.values = values;
+    vl.values_len = 2;
+
+    sstrncpy (vl.type, type, sizeof (vl.type));
+    sstrncpy (vl.type_instance, devname, sizeof (vl.type_instance));
+
+    plugin_dispatch_values (&vl);
+} /* void submit_derive2 */
+
+static int
+lv_init (void)
+{
+    if (virInitialize () != 0)
+        return -1;
+
+       return 0;
+}
+
+static int
+lv_config (const char *key, const char *value)
+{
+    if (virInitialize () != 0)
+        return 1;
+
+    if (il_domains == NULL)
+        il_domains = ignorelist_create (1);
+    if (il_block_devices == NULL)
+        il_block_devices = ignorelist_create (1);
+    if (il_interface_devices == NULL)
+        il_interface_devices = ignorelist_create (1);
+
+    if (strcasecmp (key, "Connection") == 0) {
+        char *tmp = strdup (value);
+        if (tmp == NULL) {
+            ERROR (PLUGIN_NAME " plugin: Connection strdup failed.");
+            return 1;
+        }
+        sfree (conn_string);
+        conn_string = tmp;
+        return 0;
+    }
+
+    if (strcasecmp (key, "RefreshInterval") == 0) {
+        char *eptr = NULL;
+        interval = strtol (value, &eptr, 10);
+        if (eptr == NULL || *eptr != '\0') return 1;
+        return 0;
+    }
+
+    if (strcasecmp (key, "Domain") == 0) {
+        if (ignorelist_add (il_domains, value)) return 1;
+        return 0;
+    }
+    if (strcasecmp (key, "BlockDevice") == 0) {
+        if (ignorelist_add (il_block_devices, value)) return 1;
+        return 0;
+    }
+    if (strcasecmp (key, "InterfaceDevice") == 0) {
+        if (ignorelist_add (il_interface_devices, value)) return 1;
+        return 0;
+    }
+
+    if (strcasecmp (key, "IgnoreSelected") == 0) {
+        if (IS_TRUE (value))
+        {
+            ignorelist_set_invert (il_domains, 0);
+            ignorelist_set_invert (il_block_devices, 0);
+            ignorelist_set_invert (il_interface_devices, 0);
+        }
+        else
+        {
+            ignorelist_set_invert (il_domains, 1);
+            ignorelist_set_invert (il_block_devices, 1);
+            ignorelist_set_invert (il_interface_devices, 1);
+        }
+        return 0;
+    }
+
+    if (strcasecmp (key, "HostnameFormat") == 0) {
+        char *value_copy;
+        char *fields[HF_MAX_FIELDS];
+        int i, n;
+
+        value_copy = strdup (value);
+        if (value_copy == NULL) {
+            ERROR (PLUGIN_NAME " plugin: strdup failed.");
+            return -1;
+        }
+
+        n = strsplit (value_copy, fields, HF_MAX_FIELDS);
+        if (n < 1) {
+            sfree (value_copy);
+            ERROR (PLUGIN_NAME " plugin: HostnameFormat: no fields");
+            return -1;
+        }
+
+        for (i = 0; i < n; ++i) {
+            if (strcasecmp (fields[i], "hostname") == 0)
+                hostname_format[i] = hf_hostname;
+            else if (strcasecmp (fields[i], "name") == 0)
+                hostname_format[i] = hf_name;
+            else if (strcasecmp (fields[i], "uuid") == 0)
+                hostname_format[i] = hf_uuid;
+            else {
+                sfree (value_copy);
+                ERROR (PLUGIN_NAME " plugin: unknown HostnameFormat field: %s", fields[i]);
+                return -1;
+            }
+        }
+        sfree (value_copy);
+
+        for (i = n; i < HF_MAX_FIELDS; ++i)
+            hostname_format[i] = hf_none;
+
+        return 0;
+    }
+
+    if (strcasecmp (key, "PluginInstanceFormat") == 0) {
+        char *value_copy;
+        char *fields[PLGINST_MAX_FIELDS];
+        int i, n;
+
+        value_copy = strdup (value);
+        if (value_copy == NULL) {
+            ERROR (PLUGIN_NAME " plugin: strdup failed.");
+            return -1;
+        }
+
+        n = strsplit (value_copy, fields, PLGINST_MAX_FIELDS);
+        if (n < 1) {
+            sfree (value_copy);
+            ERROR (PLUGIN_NAME " plugin: PluginInstanceFormat: no fields");
+            return -1;
+        }
+
+        for (i = 0; i < n; ++i) {
+            if (strcasecmp (fields[i], "name") == 0)
+                plugin_instance_format[i] = plginst_name;
+            else if (strcasecmp (fields[i], "uuid") == 0)
+                plugin_instance_format[i] = plginst_uuid;
+            else {
+                sfree (value_copy);
+                ERROR (PLUGIN_NAME " plugin: unknown HostnameFormat field: %s", fields[i]);
+                return -1;
+            }
+        }
+        sfree (value_copy);
+
+        for (i = n; i < PLGINST_MAX_FIELDS; ++i)
+            plugin_instance_format[i] = plginst_none;
+
+        return 0;
+    }
+
+    if (strcasecmp (key, "InterfaceFormat") == 0) {
+        if (strcasecmp (value, "name") == 0)
+            interface_format = if_name;
+        else if (strcasecmp (value, "address") == 0)
+            interface_format = if_address;
+        else if (strcasecmp (value, "number") == 0)
+            interface_format = if_number;
+        else {
+            ERROR (PLUGIN_NAME " plugin: unknown InterfaceFormat: %s", value);
+            return -1;
+        }
+        return 0;
+    }
+
+    /* Unrecognised option. */
+    return -1;
+}
+
+static int
+lv_read (void)
+{
+    time_t t;
+    int i;
+
+    if (conn == NULL) {
+        /* `conn_string == NULL' is acceptable. */
+        conn = virConnectOpenReadOnly (conn_string);
+        if (conn == NULL) {
+            c_complain (LOG_ERR, &conn_complain,
+                    PLUGIN_NAME " plugin: Unable to connect: "
+                    "virConnectOpenReadOnly failed.");
+            return -1;
+        }
+    }
+    c_release (LOG_NOTICE, &conn_complain,
+            PLUGIN_NAME " plugin: Connection established.");
+
+    time (&t);
+
+    /* Need to refresh domain or device lists? */
+    if ((last_refresh == (time_t) 0) ||
+            ((interval > 0) && ((last_refresh + interval) <= t))) {
+        if (refresh_lists () != 0) {
+            if (conn != NULL)
+                virConnectClose (conn);
+            conn = NULL;
+            return -1;
+        }
+        last_refresh = t;
+    }
+
+#if 0
+    for (i = 0; i < nr_domains; ++i)
+        fprintf (stderr, "domain %s\n", virDomainGetName (domains[i]));
+    for (i = 0; i < nr_block_devices; ++i)
+        fprintf  (stderr, "block device %d %s:%s\n",
+                  i, virDomainGetName (block_devices[i].dom),
+                  block_devices[i].path);
+    for (i = 0; i < nr_interface_devices; ++i)
+        fprintf (stderr, "interface device %d %s:%s\n",
+                 i, virDomainGetName (interface_devices[i].dom),
+                 interface_devices[i].path);
+#endif
+
+    /* Get CPU usage, memory, VCPU usage for each domain. */
+    for (i = 0; i < nr_domains; ++i) {
+        virDomainInfo info;
+        virVcpuInfoPtr vinfo = NULL;
+        virDomainMemoryStatPtr minfo = NULL;
+        int status;
+        int j;
+
+        status = virDomainGetInfo (domains[i], &info);
+        if (status != 0)
+        {
+            ERROR (PLUGIN_NAME " plugin: virDomainGetInfo failed with status %i.",
+                    status);
+            continue;
+        }
+
+        cpu_submit (info.cpuTime, domains[i], "virt_cpu_total");
+        memory_submit ((gauge_t) info.memory * 1024, domains[i]);
+
+        vinfo = malloc (info.nrVirtCpu * sizeof (vinfo[0]));
+        if (vinfo == NULL) {
+            ERROR (PLUGIN_NAME " plugin: malloc failed.");
+            continue;
+        }
+
+        status = virDomainGetVcpus (domains[i], vinfo, info.nrVirtCpu,
+                /* cpu map = */ NULL, /* cpu map length = */ 0);
+        if (status < 0)
+        {
+            ERROR (PLUGIN_NAME " plugin: virDomainGetVcpus failed with status %i.",
+                    status);
+            sfree (vinfo);
+            continue;
+        }
+
+        for (j = 0; j < info.nrVirtCpu; ++j)
+            vcpu_submit (vinfo[j].cpuTime,
+                    domains[i], vinfo[j].number, "virt_vcpu");
+
+        sfree (vinfo);
+
+        minfo = malloc (VIR_DOMAIN_MEMORY_STAT_NR * sizeof (virDomainMemoryStatStruct));
+        if (minfo == NULL) {
+            ERROR ("virt plugin: malloc failed.");
+            continue;
+        }
+
+        status =  virDomainMemoryStats (domains[i], minfo, VIR_DOMAIN_MEMORY_STAT_NR, 0);
+
+        if (status < 0) {
+            ERROR ("virt plugin: virDomainMemoryStats failed with status %i.",
+                    status);
+            sfree (minfo);
+            continue;
+        }
+
+        for (j = 0; j < status; j++) {
+            memory_stats_submit ((gauge_t) minfo[j].val, domains[i], minfo[j].tag);
+        }
+
+        sfree (minfo);
+    }
+
+
+    /* Get block device stats for each domain. */
+    for (i = 0; i < nr_block_devices; ++i) {
+        struct _virDomainBlockStats stats;
+
+        if (virDomainBlockStats (block_devices[i].dom, block_devices[i].path,
+                    &stats, sizeof stats) != 0)
+            continue;
+
+        if ((stats.rd_req != -1) && (stats.wr_req != -1))
+            submit_derive2 ("disk_ops",
+                    (derive_t) stats.rd_req, (derive_t) stats.wr_req,
+                    block_devices[i].dom, block_devices[i].path);
+
+        if ((stats.rd_bytes != -1) && (stats.wr_bytes != -1))
+            submit_derive2 ("disk_octets",
+                    (derive_t) stats.rd_bytes, (derive_t) stats.wr_bytes,
+                    block_devices[i].dom, block_devices[i].path);
+    } /* for (nr_block_devices) */
+
+    /* Get interface stats for each domain. */
+    for (i = 0; i < nr_interface_devices; ++i) {
+        struct _virDomainInterfaceStats stats;
+        char *display_name = NULL;
+
+
+        switch (interface_format) {
+            case if_address:
+                display_name = interface_devices[i].address;
+                break;
+            case if_number:
+                display_name = interface_devices[i].number;
+                break;
+            case if_name:
+            default:
+                display_name = interface_devices[i].path;
+        }
+
+        if (virDomainInterfaceStats (interface_devices[i].dom,
+                    interface_devices[i].path,
+                    &stats, sizeof stats) != 0)
+            continue;
+
+       if ((stats.rx_bytes != -1) && (stats.tx_bytes != -1))
+           submit_derive2 ("if_octets",
+                   (derive_t) stats.rx_bytes, (derive_t) stats.tx_bytes,
+                   interface_devices[i].dom, display_name);
+
+       if ((stats.rx_packets != -1) && (stats.tx_packets != -1))
+           submit_derive2 ("if_packets",
+                   (derive_t) stats.rx_packets, (derive_t) stats.tx_packets,
+                   interface_devices[i].dom, display_name);
+
+       if ((stats.rx_errs != -1) && (stats.tx_errs != -1))
+           submit_derive2 ("if_errors",
+                   (derive_t) stats.rx_errs, (derive_t) stats.tx_errs,
+                   interface_devices[i].dom, display_name);
+
+       if ((stats.rx_drop != -1) && (stats.tx_drop != -1))
+           submit_derive2 ("if_dropped",
+                   (derive_t) stats.rx_drop, (derive_t) stats.tx_drop,
+                   interface_devices[i].dom, display_name);
+    } /* for (nr_interface_devices) */
+
+    return 0;
+}
+
+static int
+refresh_lists (void)
+{
+    int n;
+
+    n = virConnectNumOfDomains (conn);
+    if (n < 0) {
+        VIRT_ERROR (conn, "reading number of domains");
+        return -1;
+    }
+
+    if (n > 0) {
+        int i;
+        int *domids;
+
+        /* Get list of domains. */
+        domids = malloc (sizeof (int) * n);
+        if (domids == 0) {
+            ERROR (PLUGIN_NAME " plugin: malloc failed.");
+            return -1;
+        }
+
+        n = virConnectListDomains (conn, domids, n);
+        if (n < 0) {
+            VIRT_ERROR (conn, "reading list of domains");
+            sfree (domids);
+            return -1;
+        }
+
+        free_block_devices ();
+        free_interface_devices ();
+        free_domains ();
+
+        /* Fetch each domain and add it to the list, unless ignore. */
+        for (i = 0; i < n; ++i) {
+            virDomainPtr dom = NULL;
+            const char *name;
+            char *xml = NULL;
+            xmlDocPtr xml_doc = NULL;
+            xmlXPathContextPtr xpath_ctx = NULL;
+            xmlXPathObjectPtr xpath_obj = NULL;
+            int j;
+
+            dom = virDomainLookupByID (conn, domids[i]);
+            if (dom == NULL) {
+                VIRT_ERROR (conn, "virDomainLookupByID");
+                /* Could be that the domain went away -- ignore it anyway. */
+                continue;
+            }
+
+            name = virDomainGetName (dom);
+            if (name == NULL) {
+                VIRT_ERROR (conn, "virDomainGetName");
+                goto cont;
+            }
+
+            if (il_domains && ignorelist_match (il_domains, name) != 0)
+                goto cont;
+
+            if (add_domain (dom) < 0) {
+                ERROR (PLUGIN_NAME " plugin: malloc failed.");
+                goto cont;
+            }
+
+            /* Get a list of devices for this domain. */
+            xml = virDomainGetXMLDesc (dom, 0);
+            if (!xml) {
+                VIRT_ERROR (conn, "virDomainGetXMLDesc");
+                goto cont;
+            }
+
+            /* Yuck, XML.  Parse out the devices. */
+            xml_doc = xmlReadDoc ((xmlChar *) xml, NULL, NULL, XML_PARSE_NONET);
+            if (xml_doc == NULL) {
+                VIRT_ERROR (conn, "xmlReadDoc");
+                goto cont;
+            }
+
+            xpath_ctx = xmlXPathNewContext (xml_doc);
+
+            /* Block devices. */
+            xpath_obj = xmlXPathEval
+                ((xmlChar *) "/domain/devices/disk/target[@dev]",
+                 xpath_ctx);
+            if (xpath_obj == NULL || xpath_obj->type != XPATH_NODESET ||
+                xpath_obj->nodesetval == NULL)
+                goto cont;
+
+            for (j = 0; j < xpath_obj->nodesetval->nodeNr; ++j) {
+                xmlNodePtr node;
+                char *path = NULL;
+
+                node = xpath_obj->nodesetval->nodeTab[j];
+                if (!node) continue;
+                path = (char *) xmlGetProp (node, (xmlChar *) "dev");
+                if (!path) continue;
+
+                if (il_block_devices &&
+                    ignore_device_match (il_block_devices, name, path) != 0)
+                    goto cont2;
+
+                add_block_device (dom, path);
+            cont2:
+                if (path) xmlFree (path);
+            }
+            xmlXPathFreeObject (xpath_obj);
+
+            /* Network interfaces. */
+            xpath_obj = xmlXPathEval
+                ((xmlChar *) "/domain/devices/interface[target[@dev]]",
+                 xpath_ctx);
+            if (xpath_obj == NULL || xpath_obj->type != XPATH_NODESET ||
+                xpath_obj->nodesetval == NULL)
+                goto cont;
+
+            xmlNodeSetPtr xml_interfaces = xpath_obj->nodesetval;
+
+            for (j = 0; j < xml_interfaces->nodeNr; ++j) {
+                char *path = NULL;
+                char *address = NULL;
+                xmlNodePtr xml_interface;
+
+                xml_interface = xml_interfaces->nodeTab[j];
+                if (!xml_interface) continue;
+                xmlNodePtr child = NULL;
+
+                for (child = xml_interface->children; child; child = child->next) {
+                    if (child->type != XML_ELEMENT_NODE) continue;
+
+                    if (xmlStrEqual(child->name, (const xmlChar *) "target")) {
+                        path = (char *) xmlGetProp (child, (const xmlChar *) "dev");
+                        if (!path) continue;
+                    } else if (xmlStrEqual(child->name, (const xmlChar *) "mac")) {
+                        address = (char *) xmlGetProp (child, (const xmlChar *) "address");
+                        if (!address) continue;
+                    }
+                }
+
+                if (il_interface_devices &&
+                    (ignore_device_match (il_interface_devices, name, path) != 0 ||
+                     ignore_device_match (il_interface_devices, name, address) != 0))
+                    goto cont3;
+
+                add_interface_device (dom, path, address, j+1);
+                cont3:
+                    if (path) xmlFree (path);
+                    if (address) xmlFree (address);
+            }
+
+        cont:
+            if (xpath_obj) xmlXPathFreeObject (xpath_obj);
+            if (xpath_ctx) xmlXPathFreeContext (xpath_ctx);
+            if (xml_doc) xmlFreeDoc (xml_doc);
+            sfree (xml);
+        }
+
+        sfree (domids);
+    }
+
+    return 0;
+}
+
+static void
+free_domains ()
+{
+    int i;
+
+    if (domains) {
+        for (i = 0; i < nr_domains; ++i)
+            virDomainFree (domains[i]);
+        sfree (domains);
+    }
+    domains = NULL;
+    nr_domains = 0;
+}
+
+static int
+add_domain (virDomainPtr dom)
+{
+    virDomainPtr *new_ptr;
+    int new_size = sizeof (domains[0]) * (nr_domains+1);
+
+    if (domains)
+        new_ptr = realloc (domains, new_size);
+    else
+        new_ptr = malloc (new_size);
+
+    if (new_ptr == NULL)
+        return -1;
+
+    domains = new_ptr;
+    domains[nr_domains] = dom;
+    return nr_domains++;
+}
+
+static void
+free_block_devices ()
+{
+    int i;
+
+    if (block_devices) {
+        for (i = 0; i < nr_block_devices; ++i)
+            sfree (block_devices[i].path);
+        sfree (block_devices);
+    }
+    block_devices = NULL;
+    nr_block_devices = 0;
+}
+
+static int
+add_block_device (virDomainPtr dom, const char *path)
+{
+    struct block_device *new_ptr;
+    int new_size = sizeof (block_devices[0]) * (nr_block_devices+1);
+    char *path_copy;
+
+    path_copy = strdup (path);
+    if (!path_copy)
+        return -1;
+
+    if (block_devices)
+        new_ptr = realloc (block_devices, new_size);
+    else
+        new_ptr = malloc (new_size);
+
+    if (new_ptr == NULL) {
+        sfree (path_copy);
+        return -1;
+    }
+    block_devices = new_ptr;
+    block_devices[nr_block_devices].dom = dom;
+    block_devices[nr_block_devices].path = path_copy;
+    return nr_block_devices++;
+}
+
+static void
+free_interface_devices ()
+{
+    int i;
+
+    if (interface_devices) {
+        for (i = 0; i < nr_interface_devices; ++i) {
+            sfree (interface_devices[i].path);
+            sfree (interface_devices[i].address);
+            sfree (interface_devices[i].number);
+        }
+        sfree (interface_devices);
+    }
+    interface_devices = NULL;
+    nr_interface_devices = 0;
+}
+
+static int
+add_interface_device (virDomainPtr dom, const char *path, const char *address, unsigned int number)
+{
+    struct interface_device *new_ptr;
+    int new_size = sizeof (interface_devices[0]) * (nr_interface_devices+1);
+    char *path_copy, *address_copy, number_string[15];
+
+    path_copy = strdup (path);
+    if (!path_copy) return -1;
+
+    address_copy = strdup (address);
+    if (!address_copy) {
+        sfree(path_copy);
+        return -1;
+    }
+
+    snprintf(number_string, sizeof (number_string), "interface-%u", number);
+
+    if (interface_devices)
+        new_ptr = realloc (interface_devices, new_size);
+    else
+        new_ptr = malloc (new_size);
+
+    if (new_ptr == NULL) {
+        sfree (path_copy);
+        sfree (address_copy);
+        return -1;
+    }
+    interface_devices = new_ptr;
+    interface_devices[nr_interface_devices].dom = dom;
+    interface_devices[nr_interface_devices].path = path_copy;
+    interface_devices[nr_interface_devices].address = address_copy;
+    interface_devices[nr_interface_devices].number = strdup(number_string);
+    return nr_interface_devices++;
+}
+
+static int
+ignore_device_match (ignorelist_t *il, const char *domname, const char *devpath)
+{
+    char *name;
+    int n, r;
+
+    n = sizeof (char) * (strlen (domname) + strlen (devpath) + 2);
+    name = malloc (n);
+    if (name == NULL) {
+        ERROR (PLUGIN_NAME " plugin: malloc failed.");
+        return 0;
+    }
+    ssnprintf (name, n, "%s:%s", domname, devpath);
+    r = ignorelist_match (il, name);
+    sfree (name);
+    return r;
+}
+
+static int
+lv_shutdown (void)
+{
+    free_block_devices ();
+    free_interface_devices ();
+    free_domains ();
+
+    if (conn != NULL)
+        virConnectClose (conn);
+    conn = NULL;
+
+    ignorelist_free (il_domains);
+    il_domains = NULL;
+    ignorelist_free (il_block_devices);
+    il_block_devices = NULL;
+    ignorelist_free (il_interface_devices);
+    il_interface_devices = NULL;
+
+    return 0;
+}
+
+void
+module_register (void)
+{
+    plugin_register_config (PLUGIN_NAME,
+    lv_config,
+    config_keys, NR_CONFIG_KEYS);
+    plugin_register_init (PLUGIN_NAME, lv_init);
+    plugin_register_read (PLUGIN_NAME, lv_read);
+    plugin_register_shutdown (PLUGIN_NAME, lv_shutdown);
+}
+
+/*
+ * vim: shiftwidth=4 tabstop=8 softtabstop=4 expandtab fdm=marker
+ */