Code

Merge branch 'collectd-5.0'
[collectd.git] / src / libvirt.c
1 /**
2  * collectd - src/libvirt.c
3  * Copyright (C) 2006-2008  Red Hat Inc.
4  *
5  * This program is free software; you can redistribute it and/or modify it
6  * under the terms of the GNU General Public License as published by the
7  * Free Software Foundation; only version 2 of the license is applicable.
8  *
9  * This program is distributed in the hope that it will be useful, but
10  * WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License along
15  * with this program; if not, write to the Free Software Foundation, Inc.,
16  * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA
17  *
18  * Authors:
19  *   Richard W.M. Jones <rjones@redhat.com>
20  **/
22 #include "collectd.h"
23 #include "common.h"
24 #include "plugin.h"
25 #include "configfile.h"
26 #include "utils_ignorelist.h"
27 #include "utils_complain.h"
29 #include <libvirt/libvirt.h>
30 #include <libvirt/virterror.h>
31 #include <libxml/parser.h>
32 #include <libxml/tree.h>
33 #include <libxml/xpath.h>
35 static const char *config_keys[] = {
36     "Connection",
38     "RefreshInterval",
40     "Domain",
41     "BlockDevice",
42     "InterfaceDevice",
43     "IgnoreSelected",
45     "HostnameFormat",
46     "InterfaceFormat",
48     NULL
49 };
50 #define NR_CONFIG_KEYS ((sizeof config_keys / sizeof config_keys[0]) - 1)
52 /* Connection. */
53 static virConnectPtr conn = 0;
54 static char *conn_string = NULL;
55 static c_complain_t conn_complain = C_COMPLAIN_INIT_STATIC;
57 /* Seconds between list refreshes, 0 disables completely. */
58 static int interval = 60;
60 /* List of domains, if specified. */
61 static ignorelist_t *il_domains = NULL;
62 /* List of block devices, if specified. */
63 static ignorelist_t *il_block_devices = NULL;
64 /* List of network interface devices, if specified. */
65 static ignorelist_t *il_interface_devices = NULL;
67 static int ignore_device_match (ignorelist_t *,
68                                 const char *domname, const char *devpath);
70 /* Actual list of domains found on last refresh. */
71 static virDomainPtr *domains = NULL;
72 static int nr_domains = 0;
74 static void free_domains (void);
75 static int add_domain (virDomainPtr dom);
77 /* Actual list of block devices found on last refresh. */
78 struct block_device {
79     virDomainPtr dom;           /* domain */
80     char *path;                 /* name of block device */
81 };
83 static struct block_device *block_devices = NULL;
84 static int nr_block_devices = 0;
86 static void free_block_devices (void);
87 static int add_block_device (virDomainPtr dom, const char *path);
89 /* Actual list of network interfaces found on last refresh. */
90 struct interface_device {
91     virDomainPtr dom;           /* domain */
92     char *path;                 /* name of interface device */
93     char *address;              /* mac address of interface device */
94 };
96 static struct interface_device *interface_devices = NULL;
97 static int nr_interface_devices = 0;
99 static void free_interface_devices (void);
100 static int add_interface_device (virDomainPtr dom, const char *path, const char *address);
102 /* HostnameFormat. */
103 #define HF_MAX_FIELDS 3
105 enum hf_field {
106     hf_none = 0,
107     hf_hostname,
108     hf_name,
109     hf_uuid
110 };
112 static enum hf_field hostname_format[HF_MAX_FIELDS] =
113     { hf_name };
115 /* InterfaceFormat. */
116 enum if_field {
117     if_address,
118     if_name
119 };
121 static enum if_field interface_format = if_name;
123 /* Time that we last refreshed. */
124 static time_t last_refresh = (time_t) 0;
126 static int refresh_lists (void);
128 /* ERROR(...) macro for virterrors. */
129 #define VIRT_ERROR(conn,s) do {                 \
130         virErrorPtr err;                        \
131         err = (conn) ? virConnGetLastError ((conn)) : virGetLastError (); \
132         if (err) ERROR ("%s: %s", (s), err->message);                   \
133     } while(0)
135 static void
136 init_value_list (value_list_t *vl, virDomainPtr dom)
138     int i, n;
139     const char *name;
140     char uuid[VIR_UUID_STRING_BUFLEN];
142     vl->interval = interval_g;
144     sstrncpy (vl->plugin, "libvirt", sizeof (vl->plugin));
146     vl->host[0] = '\0';
148     /* Construct the hostname field according to HostnameFormat. */
149     for (i = 0; i < HF_MAX_FIELDS; ++i) {
150         if (hostname_format[i] == hf_none)
151             continue;
153         n = DATA_MAX_NAME_LEN - strlen (vl->host) - 2;
155         if (i > 0 && n >= 1) {
156             strncat (vl->host, ":", 1);
157             n--;
158         }
160         switch (hostname_format[i]) {
161         case hf_none: break;
162         case hf_hostname:
163             strncat (vl->host, hostname_g, n);
164             break;
165         case hf_name:
166             name = virDomainGetName (dom);
167             if (name)
168                 strncat (vl->host, name, n);
169             break;
170         case hf_uuid:
171             if (virDomainGetUUIDString (dom, uuid) == 0)
172                 strncat (vl->host, uuid, n);
173             break;
174         }
175     }
177     vl->host[sizeof (vl->host) - 1] = '\0';
178 } /* void init_value_list */
180 static void
181 cpu_submit (unsigned long long cpu_time,
182             virDomainPtr dom, const char *type)
184     value_t values[1];
185     value_list_t vl = VALUE_LIST_INIT;
187     init_value_list (&vl, dom);
189     values[0].derive = cpu_time;
191     vl.values = values;
192     vl.values_len = 1;
194     sstrncpy (vl.type, type, sizeof (vl.type));
196     plugin_dispatch_values (&vl);
199 static void
200 vcpu_submit (derive_t cpu_time,
201              virDomainPtr dom, int vcpu_nr, const char *type)
203     value_t values[1];
204     value_list_t vl = VALUE_LIST_INIT;
206     init_value_list (&vl, dom);
208     values[0].derive = cpu_time;
209     vl.values = values;
210     vl.values_len = 1;
212     sstrncpy (vl.type, type, sizeof (vl.type));
213     ssnprintf (vl.type_instance, sizeof (vl.type_instance), "%d", vcpu_nr);
215     plugin_dispatch_values (&vl);
218 static void
219 submit_derive2 (const char *type, derive_t v0, derive_t v1,
220              virDomainPtr dom, const char *devname)
222     value_t values[2];
223     value_list_t vl = VALUE_LIST_INIT;
225     init_value_list (&vl, dom);
227     values[0].derive = v0;
228     values[1].derive = v1;
229     vl.values = values;
230     vl.values_len = 2;
232     sstrncpy (vl.type, type, sizeof (vl.type));
233     sstrncpy (vl.type_instance, devname, sizeof (vl.type_instance));
235     plugin_dispatch_values (&vl);
236 } /* void submit_derive2 */
238 static int
239 lv_init (void)
241     if (virInitialize () != 0)
242         return -1;
244         return 0;
247 static int
248 lv_config (const char *key, const char *value)
250     if (virInitialize () != 0)
251         return 1;
253     if (il_domains == NULL)
254         il_domains = ignorelist_create (1);
255     if (il_block_devices == NULL)
256         il_block_devices = ignorelist_create (1);
257     if (il_interface_devices == NULL)
258         il_interface_devices = ignorelist_create (1);
260     if (strcasecmp (key, "Connection") == 0) {
261         char *tmp = strdup (value);
262         if (tmp == NULL) {
263             ERROR ("libvirt plugin: Connection strdup failed.");
264             return 1;
265         }
266         sfree (conn_string);
267         conn_string = tmp;
268         return 0;
269     }
271     if (strcasecmp (key, "RefreshInterval") == 0) {
272         char *eptr = NULL;
273         interval = strtol (value, &eptr, 10);
274         if (eptr == NULL || *eptr != '\0') return 1;
275         return 0;
276     }
278     if (strcasecmp (key, "Domain") == 0) {
279         if (ignorelist_add (il_domains, value)) return 1;
280         return 0;
281     }
282     if (strcasecmp (key, "BlockDevice") == 0) {
283         if (ignorelist_add (il_block_devices, value)) return 1;
284         return 0;
285     }
286     if (strcasecmp (key, "InterfaceDevice") == 0) {
287         if (ignorelist_add (il_interface_devices, value)) return 1;
288         return 0;
289     }
291     if (strcasecmp (key, "IgnoreSelected") == 0) {
292         if (IS_TRUE (value))
293         {
294             ignorelist_set_invert (il_domains, 0);
295             ignorelist_set_invert (il_block_devices, 0);
296             ignorelist_set_invert (il_interface_devices, 0);
297         }
298         else
299         {
300             ignorelist_set_invert (il_domains, 1);
301             ignorelist_set_invert (il_block_devices, 1);
302             ignorelist_set_invert (il_interface_devices, 1);
303         }
304         return 0;
305     }
307     if (strcasecmp (key, "HostnameFormat") == 0) {
308         char *value_copy;
309         char *fields[HF_MAX_FIELDS];
310         int i, n;
312         value_copy = strdup (value);
313         if (value_copy == NULL) {
314             ERROR ("libvirt plugin: strdup failed.");
315             return -1;
316         }
318         n = strsplit (value_copy, fields, HF_MAX_FIELDS);
319         if (n < 1) {
320             sfree (value_copy);
321             ERROR ("HostnameFormat: no fields");
322             return -1;
323         }
325         for (i = 0; i < n; ++i) {
326             if (strcasecmp (fields[i], "hostname") == 0)
327                 hostname_format[i] = hf_hostname;
328             else if (strcasecmp (fields[i], "name") == 0)
329                 hostname_format[i] = hf_name;
330             else if (strcasecmp (fields[i], "uuid") == 0)
331                 hostname_format[i] = hf_uuid;
332             else {
333                 sfree (value_copy);
334                 ERROR ("unknown HostnameFormat field: %s", fields[i]);
335                 return -1;
336             }
337         }
338         sfree (value_copy);
340         for (i = n; i < HF_MAX_FIELDS; ++i)
341             hostname_format[i] = hf_none;
343         return 0;
344     }
346     if (strcasecmp (key, "InterfaceFormat") == 0) {
347         if (strcasecmp (value, "name") == 0)
348             interface_format = if_name;
349         else if (strcasecmp (value, "address") == 0)
350             interface_format = if_address;
351         else {
352             ERROR ("unknown InterfaceFormat: %s", value);
353             return -1;
354         }
355         return 0;
356     }
358     /* Unrecognised option. */
359     return -1;
362 static int
363 lv_read (void)
365     time_t t;
366     int i;
368     if (conn == NULL) {
369         /* `conn_string == NULL' is acceptable. */
370         conn = virConnectOpenReadOnly (conn_string);
371         if (conn == NULL) {
372             c_complain (LOG_ERR, &conn_complain,
373                     "libvirt plugin: Unable to connect: "
374                     "virConnectOpenReadOnly failed.");
375             return -1;
376         }
377     }
378     c_release (LOG_NOTICE, &conn_complain,
379             "libvirt plugin: Connection established.");
381     time (&t);
383     /* Need to refresh domain or device lists? */
384     if ((last_refresh == (time_t) 0) ||
385             ((interval > 0) && ((last_refresh + interval) <= t))) {
386         if (refresh_lists () != 0) {
387             if (conn != NULL)
388                 virConnectClose (conn);
389             conn = NULL;
390             return -1;
391         }
392         last_refresh = t;
393     }
395 #if 0
396     for (i = 0; i < nr_domains; ++i)
397         fprintf (stderr, "domain %s\n", virDomainGetName (domains[i]));
398     for (i = 0; i < nr_block_devices; ++i)
399         fprintf  (stderr, "block device %d %s:%s\n",
400                   i, virDomainGetName (block_devices[i].dom),
401                   block_devices[i].path);
402     for (i = 0; i < nr_interface_devices; ++i)
403         fprintf (stderr, "interface device %d %s:%s\n",
404                  i, virDomainGetName (interface_devices[i].dom),
405                  interface_devices[i].path);
406 #endif
408     /* Get CPU usage, VCPU usage for each domain. */
409     for (i = 0; i < nr_domains; ++i) {
410         virDomainInfo info;
411         virVcpuInfoPtr vinfo = NULL;
412         int status;
413         int j;
415         status = virDomainGetInfo (domains[i], &info);
416         if (status != 0)
417         {
418             ERROR ("libvirt plugin: virDomainGetInfo failed with status %i.",
419                     status);
420             continue;
421         }
423         cpu_submit (info.cpuTime, domains[i], "virt_cpu_total");
425         vinfo = malloc (info.nrVirtCpu * sizeof (vinfo[0]));
426         if (vinfo == NULL) {
427             ERROR ("libvirt plugin: malloc failed.");
428             continue;
429         }
431         status = virDomainGetVcpus (domains[i], vinfo, info.nrVirtCpu,
432                 /* cpu map = */ NULL, /* cpu map length = */ 0);
433         if (status < 0)
434         {
435             ERROR ("libvirt plugin: virDomainGetVcpus failed with status %i.",
436                     status);
437             free (vinfo);
438             continue;
439         }
441         for (j = 0; j < info.nrVirtCpu; ++j)
442             vcpu_submit (vinfo[j].cpuTime,
443                     domains[i], vinfo[j].number, "virt_vcpu");
445         sfree (vinfo);
446     }
448     /* Get block device stats for each domain. */
449     for (i = 0; i < nr_block_devices; ++i) {
450         struct _virDomainBlockStats stats;
452         if (virDomainBlockStats (block_devices[i].dom, block_devices[i].path,
453                     &stats, sizeof stats) != 0)
454             continue;
456         if ((stats.rd_req != -1) && (stats.wr_req != -1))
457             submit_derive2 ("disk_ops",
458                     (derive_t) stats.rd_req, (derive_t) stats.wr_req,
459                     block_devices[i].dom, block_devices[i].path);
461         if ((stats.rd_bytes != -1) && (stats.wr_bytes != -1))
462             submit_derive2 ("disk_octets",
463                     (derive_t) stats.rd_bytes, (derive_t) stats.wr_bytes,
464                     block_devices[i].dom, block_devices[i].path);
465     } /* for (nr_block_devices) */
467     /* Get interface stats for each domain. */
468     for (i = 0; i < nr_interface_devices; ++i) {
469         struct _virDomainInterfaceStats stats;
470         char *display_name = interface_devices[i].path;
472         if (interface_format == if_address)
473             display_name = interface_devices[i].address;
475         if (virDomainInterfaceStats (interface_devices[i].dom,
476                     interface_devices[i].path,
477                     &stats, sizeof stats) != 0)
478             continue;
480         if ((stats.rx_bytes != -1) && (stats.tx_bytes != -1))
481             submit_derive2 ("if_octets",
482                     (derive_t) stats.rx_bytes, (derive_t) stats.tx_bytes,
483                     interface_devices[i].dom, display_name);
485         if ((stats.rx_packets != -1) && (stats.tx_packets != -1))
486             submit_derive2 ("if_packets",
487                     (derive_t) stats.rx_packets, (derive_t) stats.tx_packets,
488                     interface_devices[i].dom, display_name);
490         if ((stats.rx_errs != -1) && (stats.tx_errs != -1))
491             submit_derive2 ("if_errors",
492                     (derive_t) stats.rx_errs, (derive_t) stats.tx_errs,
493                     interface_devices[i].dom, display_name);
495         if ((stats.rx_drop != -1) && (stats.tx_drop != -1))
496             submit_derive2 ("if_dropped",
497                     (derive_t) stats.rx_drop, (derive_t) stats.tx_drop,
498                     interface_devices[i].dom, display_name);
499     } /* for (nr_interface_devices) */
501     return 0;
504 static int
505 refresh_lists (void)
507     int n;
509     n = virConnectNumOfDomains (conn);
510     if (n < 0) {
511         VIRT_ERROR (conn, "reading number of domains");
512         return -1;
513     }
515     if (n > 0) {
516         int i;
517         int *domids;
519         /* Get list of domains. */
520         domids = malloc (sizeof (int) * n);
521         if (domids == 0) {
522             ERROR ("libvirt plugin: malloc failed.");
523             return -1;
524         }
526         n = virConnectListDomains (conn, domids, n);
527         if (n < 0) {
528             VIRT_ERROR (conn, "reading list of domains");
529             sfree (domids);
530             return -1;
531         }
533         free_block_devices ();
534         free_interface_devices ();
535         free_domains ();
537         /* Fetch each domain and add it to the list, unless ignore. */
538         for (i = 0; i < n; ++i) {
539             virDomainPtr dom = NULL;
540             const char *name;
541             char *xml = NULL;
542             xmlDocPtr xml_doc = NULL;
543             xmlXPathContextPtr xpath_ctx = NULL;
544             xmlXPathObjectPtr xpath_obj = NULL;
545             int j;
547             dom = virDomainLookupByID (conn, domids[i]);
548             if (dom == NULL) {
549                 VIRT_ERROR (conn, "virDomainLookupByID");
550                 /* Could be that the domain went away -- ignore it anyway. */
551                 continue;
552             }
554             name = virDomainGetName (dom);
555             if (name == NULL) {
556                 VIRT_ERROR (conn, "virDomainGetName");
557                 goto cont;
558             }
560             if (il_domains && ignorelist_match (il_domains, name) != 0)
561                 goto cont;
563             if (add_domain (dom) < 0) {
564                 ERROR ("libvirt plugin: malloc failed.");
565                 goto cont;
566             }
568             /* Get a list of devices for this domain. */
569             xml = virDomainGetXMLDesc (dom, 0);
570             if (!xml) {
571                 VIRT_ERROR (conn, "virDomainGetXMLDesc");
572                 goto cont;
573             }
575             /* Yuck, XML.  Parse out the devices. */
576             xml_doc = xmlReadDoc ((xmlChar *) xml, NULL, NULL, XML_PARSE_NONET);
577             if (xml_doc == NULL) {
578                 VIRT_ERROR (conn, "xmlReadDoc");
579                 goto cont;
580             }
582             xpath_ctx = xmlXPathNewContext (xml_doc);
584             /* Block devices. */
585             xpath_obj = xmlXPathEval
586                 ((xmlChar *) "/domain/devices/disk/target[@dev]",
587                  xpath_ctx);
588             if (xpath_obj == NULL || xpath_obj->type != XPATH_NODESET ||
589                 xpath_obj->nodesetval == NULL)
590                 goto cont;
592             for (j = 0; j < xpath_obj->nodesetval->nodeNr; ++j) {
593                 xmlNodePtr node;
594                 char *path = NULL;
596                 node = xpath_obj->nodesetval->nodeTab[j];
597                 if (!node) continue;
598                 path = (char *) xmlGetProp (node, (xmlChar *) "dev");
599                 if (!path) continue;
601                 if (il_block_devices &&
602                     ignore_device_match (il_block_devices, name, path) != 0)
603                     goto cont2;
605                 add_block_device (dom, path);
606             cont2:
607                 if (path) xmlFree (path);
608             }
609             xmlXPathFreeObject (xpath_obj);
611             /* Network interfaces. */
612             xpath_obj = xmlXPathEval
613                 ((xmlChar *) "/domain/devices/interface[target[@dev]]",
614                  xpath_ctx);
615             if (xpath_obj == NULL || xpath_obj->type != XPATH_NODESET ||
616                 xpath_obj->nodesetval == NULL)
617                 goto cont;
619             xmlNodeSetPtr xml_interfaces = xpath_obj->nodesetval;
621             for (j = 0; j < xml_interfaces->nodeNr; ++j) {
622                 char *path = NULL;
623                 char *address = NULL;
624                 xmlNodePtr xml_interface;
626                 xml_interface = xml_interfaces->nodeTab[j];
627                 if (!xml_interface) continue;
628                 xmlNodePtr child = NULL;
630                 for (child = xml_interface->children; child; child = child->next) {
631                     if (child->type != XML_ELEMENT_NODE) continue;
633                     if (xmlStrEqual(child->name, (const xmlChar *) "target")) {
634                         path = (char *) xmlGetProp (child, (const xmlChar *) "dev");
635                         if (!path) continue;
636                     } else if (xmlStrEqual(child->name, (const xmlChar *) "mac")) {
637                         address = (char *) xmlGetProp (child, (const xmlChar *) "address");
638                         if (!address) continue;
639                     }
640                 }
642                 if (il_interface_devices &&
643                     (ignore_device_match (il_interface_devices, name, path) != 0 ||
644                      ignore_device_match (il_interface_devices, name, address) != 0))
645                     goto cont3;
647                 add_interface_device (dom, path, address);
648                 cont3:
649                     if (path) xmlFree (path);
650                     if (address) xmlFree (address);
651             }
653         cont:
654             if (xpath_obj) xmlXPathFreeObject (xpath_obj);
655             if (xpath_ctx) xmlXPathFreeContext (xpath_ctx);
656             if (xml_doc) xmlFreeDoc (xml_doc);
657             sfree (xml);
658         }
660         sfree (domids);
661     }
663     return 0;
666 static void
667 free_domains ()
669     int i;
671     if (domains) {
672         for (i = 0; i < nr_domains; ++i)
673             virDomainFree (domains[i]);
674         sfree (domains);
675     }
676     domains = NULL;
677     nr_domains = 0;
680 static int
681 add_domain (virDomainPtr dom)
683     virDomainPtr *new_ptr;
684     int new_size = sizeof (domains[0]) * (nr_domains+1);
686     if (domains)
687         new_ptr = realloc (domains, new_size);
688     else
689         new_ptr = malloc (new_size);
691     if (new_ptr == NULL)
692         return -1;
694     domains = new_ptr;
695     domains[nr_domains] = dom;
696     return nr_domains++;
699 static void
700 free_block_devices ()
702     int i;
704     if (block_devices) {
705         for (i = 0; i < nr_block_devices; ++i)
706             sfree (block_devices[i].path);
707         sfree (block_devices);
708     }
709     block_devices = NULL;
710     nr_block_devices = 0;
713 static int
714 add_block_device (virDomainPtr dom, const char *path)
716     struct block_device *new_ptr;
717     int new_size = sizeof (block_devices[0]) * (nr_block_devices+1);
718     char *path_copy;
720     path_copy = strdup (path);
721     if (!path_copy)
722         return -1;
724     if (block_devices)
725         new_ptr = realloc (block_devices, new_size);
726     else
727         new_ptr = malloc (new_size);
729     if (new_ptr == NULL) {
730         sfree (path_copy);
731         return -1;
732     }
733     block_devices = new_ptr;
734     block_devices[nr_block_devices].dom = dom;
735     block_devices[nr_block_devices].path = path_copy;
736     return nr_block_devices++;
739 static void
740 free_interface_devices ()
742     int i;
744     if (interface_devices) {
745         for (i = 0; i < nr_interface_devices; ++i) {
746             sfree (interface_devices[i].path);
747             sfree (interface_devices[i].address);
748         }
749         sfree (interface_devices);
750     }
751     interface_devices = NULL;
752     nr_interface_devices = 0;
755 static int
756 add_interface_device (virDomainPtr dom, const char *path, const char *address)
758     struct interface_device *new_ptr;
759     int new_size = sizeof (interface_devices[0]) * (nr_interface_devices+1);
760     char *path_copy, *address_copy;
762     path_copy = strdup (path);
763     if (!path_copy) return -1;
765     address_copy = strdup (address);
766     if (!address_copy) return -1;
768     if (interface_devices)
769         new_ptr = realloc (interface_devices, new_size);
770     else
771         new_ptr = malloc (new_size);
773     if (new_ptr == NULL) {
774         sfree (path_copy);
775         sfree (address_copy);
776         return -1;
777     }
778     interface_devices = new_ptr;
779     interface_devices[nr_interface_devices].dom = dom;
780     interface_devices[nr_interface_devices].path = path_copy;
781     interface_devices[nr_interface_devices].address = address_copy;
782     return nr_interface_devices++;
785 static int
786 ignore_device_match (ignorelist_t *il, const char *domname, const char *devpath)
788     char *name;
789     int n, r;
791     n = sizeof (char) * (strlen (domname) + strlen (devpath) + 2);
792     name = malloc (n);
793     if (name == NULL) {
794         ERROR ("libvirt plugin: malloc failed.");
795         return 0;
796     }
797     ssnprintf (name, n, "%s:%s", domname, devpath);
798     r = ignorelist_match (il, name);
799     sfree (name);
800     return r;
803 static int
804 lv_shutdown (void)
806     free_block_devices ();
807     free_interface_devices ();
808     free_domains ();
810     if (conn != NULL)
811         virConnectClose (conn);
812     conn = NULL;
814     ignorelist_free (il_domains);
815     il_domains = NULL;
816     ignorelist_free (il_block_devices);
817     il_block_devices = NULL;
818     ignorelist_free (il_interface_devices);
819     il_interface_devices = NULL;
821     return 0;
824 void
825 module_register (void)
827     plugin_register_config ("libvirt",
828             lv_config,
829             config_keys, NR_CONFIG_KEYS);
830     plugin_register_init ("libvirt", lv_init);
831     plugin_register_read ("libvirt", lv_read);
832     plugin_register_shutdown ("libvirt", lv_shutdown);
835 /*
836  * vim: shiftwidth=4 tabstop=8 softtabstop=4 expandtab fdm=marker
837  */