Code

Merge branch 'ff/netlib'
[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     sstrncpy (vl->plugin, "libvirt", sizeof (vl->plugin));
144     vl->host[0] = '\0';
146     /* Construct the hostname field according to HostnameFormat. */
147     for (i = 0; i < HF_MAX_FIELDS; ++i) {
148         if (hostname_format[i] == hf_none)
149             continue;
151         n = DATA_MAX_NAME_LEN - strlen (vl->host) - 2;
153         if (i > 0 && n >= 1) {
154             strncat (vl->host, ":", 1);
155             n--;
156         }
158         switch (hostname_format[i]) {
159         case hf_none: break;
160         case hf_hostname:
161             strncat (vl->host, hostname_g, n);
162             break;
163         case hf_name:
164             name = virDomainGetName (dom);
165             if (name)
166                 strncat (vl->host, name, n);
167             break;
168         case hf_uuid:
169             if (virDomainGetUUIDString (dom, uuid) == 0)
170                 strncat (vl->host, uuid, n);
171             break;
172         }
173     }
175     vl->host[sizeof (vl->host) - 1] = '\0';
176 } /* void init_value_list */
178 static void
179 cpu_submit (unsigned long long cpu_time,
180             virDomainPtr dom, const char *type)
182     value_t values[1];
183     value_list_t vl = VALUE_LIST_INIT;
185     init_value_list (&vl, dom);
187     values[0].derive = cpu_time;
189     vl.values = values;
190     vl.values_len = 1;
192     sstrncpy (vl.type, type, sizeof (vl.type));
194     plugin_dispatch_values (&vl);
197 static void
198 vcpu_submit (derive_t cpu_time,
199              virDomainPtr dom, int vcpu_nr, const char *type)
201     value_t values[1];
202     value_list_t vl = VALUE_LIST_INIT;
204     init_value_list (&vl, dom);
206     values[0].derive = cpu_time;
207     vl.values = values;
208     vl.values_len = 1;
210     sstrncpy (vl.type, type, sizeof (vl.type));
211     ssnprintf (vl.type_instance, sizeof (vl.type_instance), "%d", vcpu_nr);
213     plugin_dispatch_values (&vl);
216 static void
217 submit_derive2 (const char *type, derive_t v0, derive_t v1,
218              virDomainPtr dom, const char *devname)
220     value_t values[2];
221     value_list_t vl = VALUE_LIST_INIT;
223     init_value_list (&vl, dom);
225     values[0].derive = v0;
226     values[1].derive = v1;
227     vl.values = values;
228     vl.values_len = 2;
230     sstrncpy (vl.type, type, sizeof (vl.type));
231     sstrncpy (vl.type_instance, devname, sizeof (vl.type_instance));
233     plugin_dispatch_values (&vl);
234 } /* void submit_derive2 */
236 static int
237 lv_init (void)
239     if (virInitialize () != 0)
240         return -1;
242         return 0;
245 static int
246 lv_config (const char *key, const char *value)
248     if (virInitialize () != 0)
249         return 1;
251     if (il_domains == NULL)
252         il_domains = ignorelist_create (1);
253     if (il_block_devices == NULL)
254         il_block_devices = ignorelist_create (1);
255     if (il_interface_devices == NULL)
256         il_interface_devices = ignorelist_create (1);
258     if (strcasecmp (key, "Connection") == 0) {
259         char *tmp = strdup (value);
260         if (tmp == NULL) {
261             ERROR ("libvirt plugin: Connection strdup failed.");
262             return 1;
263         }
264         sfree (conn_string);
265         conn_string = tmp;
266         return 0;
267     }
269     if (strcasecmp (key, "RefreshInterval") == 0) {
270         char *eptr = NULL;
271         interval = strtol (value, &eptr, 10);
272         if (eptr == NULL || *eptr != '\0') return 1;
273         return 0;
274     }
276     if (strcasecmp (key, "Domain") == 0) {
277         if (ignorelist_add (il_domains, value)) return 1;
278         return 0;
279     }
280     if (strcasecmp (key, "BlockDevice") == 0) {
281         if (ignorelist_add (il_block_devices, value)) return 1;
282         return 0;
283     }
284     if (strcasecmp (key, "InterfaceDevice") == 0) {
285         if (ignorelist_add (il_interface_devices, value)) return 1;
286         return 0;
287     }
289     if (strcasecmp (key, "IgnoreSelected") == 0) {
290         if (IS_TRUE (value))
291         {
292             ignorelist_set_invert (il_domains, 0);
293             ignorelist_set_invert (il_block_devices, 0);
294             ignorelist_set_invert (il_interface_devices, 0);
295         }
296         else
297         {
298             ignorelist_set_invert (il_domains, 1);
299             ignorelist_set_invert (il_block_devices, 1);
300             ignorelist_set_invert (il_interface_devices, 1);
301         }
302         return 0;
303     }
305     if (strcasecmp (key, "HostnameFormat") == 0) {
306         char *value_copy;
307         char *fields[HF_MAX_FIELDS];
308         int i, n;
310         value_copy = strdup (value);
311         if (value_copy == NULL) {
312             ERROR ("libvirt plugin: strdup failed.");
313             return -1;
314         }
316         n = strsplit (value_copy, fields, HF_MAX_FIELDS);
317         if (n < 1) {
318             sfree (value_copy);
319             ERROR ("HostnameFormat: no fields");
320             return -1;
321         }
323         for (i = 0; i < n; ++i) {
324             if (strcasecmp (fields[i], "hostname") == 0)
325                 hostname_format[i] = hf_hostname;
326             else if (strcasecmp (fields[i], "name") == 0)
327                 hostname_format[i] = hf_name;
328             else if (strcasecmp (fields[i], "uuid") == 0)
329                 hostname_format[i] = hf_uuid;
330             else {
331                 sfree (value_copy);
332                 ERROR ("unknown HostnameFormat field: %s", fields[i]);
333                 return -1;
334             }
335         }
336         sfree (value_copy);
338         for (i = n; i < HF_MAX_FIELDS; ++i)
339             hostname_format[i] = hf_none;
341         return 0;
342     }
344     if (strcasecmp (key, "InterfaceFormat") == 0) {
345         if (strcasecmp (value, "name") == 0)
346             interface_format = if_name;
347         else if (strcasecmp (value, "address") == 0)
348             interface_format = if_address;
349         else {
350             ERROR ("unknown InterfaceFormat: %s", value);
351             return -1;
352         }
353         return 0;
354     }
356     /* Unrecognised option. */
357     return -1;
360 static int
361 lv_read (void)
363     time_t t;
364     int i;
366     if (conn == NULL) {
367         /* `conn_string == NULL' is acceptable. */
368         conn = virConnectOpenReadOnly (conn_string);
369         if (conn == NULL) {
370             c_complain (LOG_ERR, &conn_complain,
371                     "libvirt plugin: Unable to connect: "
372                     "virConnectOpenReadOnly failed.");
373             return -1;
374         }
375     }
376     c_release (LOG_NOTICE, &conn_complain,
377             "libvirt plugin: Connection established.");
379     time (&t);
381     /* Need to refresh domain or device lists? */
382     if ((last_refresh == (time_t) 0) ||
383             ((interval > 0) && ((last_refresh + interval) <= t))) {
384         if (refresh_lists () != 0) {
385             if (conn != NULL)
386                 virConnectClose (conn);
387             conn = NULL;
388             return -1;
389         }
390         last_refresh = t;
391     }
393 #if 0
394     for (i = 0; i < nr_domains; ++i)
395         fprintf (stderr, "domain %s\n", virDomainGetName (domains[i]));
396     for (i = 0; i < nr_block_devices; ++i)
397         fprintf  (stderr, "block device %d %s:%s\n",
398                   i, virDomainGetName (block_devices[i].dom),
399                   block_devices[i].path);
400     for (i = 0; i < nr_interface_devices; ++i)
401         fprintf (stderr, "interface device %d %s:%s\n",
402                  i, virDomainGetName (interface_devices[i].dom),
403                  interface_devices[i].path);
404 #endif
406     /* Get CPU usage, VCPU usage for each domain. */
407     for (i = 0; i < nr_domains; ++i) {
408         virDomainInfo info;
409         virVcpuInfoPtr vinfo = NULL;
410         int status;
411         int j;
413         status = virDomainGetInfo (domains[i], &info);
414         if (status != 0)
415         {
416             ERROR ("libvirt plugin: virDomainGetInfo failed with status %i.",
417                     status);
418             continue;
419         }
421         cpu_submit (info.cpuTime, domains[i], "virt_cpu_total");
423         vinfo = malloc (info.nrVirtCpu * sizeof (vinfo[0]));
424         if (vinfo == NULL) {
425             ERROR ("libvirt plugin: malloc failed.");
426             continue;
427         }
429         status = virDomainGetVcpus (domains[i], vinfo, info.nrVirtCpu,
430                 /* cpu map = */ NULL, /* cpu map length = */ 0);
431         if (status < 0)
432         {
433             ERROR ("libvirt plugin: virDomainGetVcpus failed with status %i.",
434                     status);
435             free (vinfo);
436             continue;
437         }
439         for (j = 0; j < info.nrVirtCpu; ++j)
440             vcpu_submit (vinfo[j].cpuTime,
441                     domains[i], vinfo[j].number, "virt_vcpu");
443         sfree (vinfo);
444     }
446     /* Get block device stats for each domain. */
447     for (i = 0; i < nr_block_devices; ++i) {
448         struct _virDomainBlockStats stats;
450         if (virDomainBlockStats (block_devices[i].dom, block_devices[i].path,
451                     &stats, sizeof stats) != 0)
452             continue;
454         if ((stats.rd_req != -1) && (stats.wr_req != -1))
455             submit_derive2 ("disk_ops",
456                     (derive_t) stats.rd_req, (derive_t) stats.wr_req,
457                     block_devices[i].dom, block_devices[i].path);
459         if ((stats.rd_bytes != -1) && (stats.wr_bytes != -1))
460             submit_derive2 ("disk_octets",
461                     (derive_t) stats.rd_bytes, (derive_t) stats.wr_bytes,
462                     block_devices[i].dom, block_devices[i].path);
463     } /* for (nr_block_devices) */
465     /* Get interface stats for each domain. */
466     for (i = 0; i < nr_interface_devices; ++i) {
467         struct _virDomainInterfaceStats stats;
468         char *display_name = interface_devices[i].path;
470         if (interface_format == if_address)
471             display_name = interface_devices[i].address;
473         if (virDomainInterfaceStats (interface_devices[i].dom,
474                     interface_devices[i].path,
475                     &stats, sizeof stats) != 0)
476             continue;
478         if ((stats.rx_bytes != -1) && (stats.tx_bytes != -1))
479             submit_derive2 ("if_octets",
480                     (derive_t) stats.rx_bytes, (derive_t) stats.tx_bytes,
481                     interface_devices[i].dom, display_name);
483         if ((stats.rx_packets != -1) && (stats.tx_packets != -1))
484             submit_derive2 ("if_packets",
485                     (derive_t) stats.rx_packets, (derive_t) stats.tx_packets,
486                     interface_devices[i].dom, display_name);
488         if ((stats.rx_errs != -1) && (stats.tx_errs != -1))
489             submit_derive2 ("if_errors",
490                     (derive_t) stats.rx_errs, (derive_t) stats.tx_errs,
491                     interface_devices[i].dom, display_name);
493         if ((stats.rx_drop != -1) && (stats.tx_drop != -1))
494             submit_derive2 ("if_dropped",
495                     (derive_t) stats.rx_drop, (derive_t) stats.tx_drop,
496                     interface_devices[i].dom, display_name);
497     } /* for (nr_interface_devices) */
499     return 0;
502 static int
503 refresh_lists (void)
505     int n;
507     n = virConnectNumOfDomains (conn);
508     if (n < 0) {
509         VIRT_ERROR (conn, "reading number of domains");
510         return -1;
511     }
513     if (n > 0) {
514         int i;
515         int *domids;
517         /* Get list of domains. */
518         domids = malloc (sizeof (int) * n);
519         if (domids == 0) {
520             ERROR ("libvirt plugin: malloc failed.");
521             return -1;
522         }
524         n = virConnectListDomains (conn, domids, n);
525         if (n < 0) {
526             VIRT_ERROR (conn, "reading list of domains");
527             sfree (domids);
528             return -1;
529         }
531         free_block_devices ();
532         free_interface_devices ();
533         free_domains ();
535         /* Fetch each domain and add it to the list, unless ignore. */
536         for (i = 0; i < n; ++i) {
537             virDomainPtr dom = NULL;
538             const char *name;
539             char *xml = NULL;
540             xmlDocPtr xml_doc = NULL;
541             xmlXPathContextPtr xpath_ctx = NULL;
542             xmlXPathObjectPtr xpath_obj = NULL;
543             int j;
545             dom = virDomainLookupByID (conn, domids[i]);
546             if (dom == NULL) {
547                 VIRT_ERROR (conn, "virDomainLookupByID");
548                 /* Could be that the domain went away -- ignore it anyway. */
549                 continue;
550             }
552             name = virDomainGetName (dom);
553             if (name == NULL) {
554                 VIRT_ERROR (conn, "virDomainGetName");
555                 goto cont;
556             }
558             if (il_domains && ignorelist_match (il_domains, name) != 0)
559                 goto cont;
561             if (add_domain (dom) < 0) {
562                 ERROR ("libvirt plugin: malloc failed.");
563                 goto cont;
564             }
566             /* Get a list of devices for this domain. */
567             xml = virDomainGetXMLDesc (dom, 0);
568             if (!xml) {
569                 VIRT_ERROR (conn, "virDomainGetXMLDesc");
570                 goto cont;
571             }
573             /* Yuck, XML.  Parse out the devices. */
574             xml_doc = xmlReadDoc ((xmlChar *) xml, NULL, NULL, XML_PARSE_NONET);
575             if (xml_doc == NULL) {
576                 VIRT_ERROR (conn, "xmlReadDoc");
577                 goto cont;
578             }
580             xpath_ctx = xmlXPathNewContext (xml_doc);
582             /* Block devices. */
583             xpath_obj = xmlXPathEval
584                 ((xmlChar *) "/domain/devices/disk/target[@dev]",
585                  xpath_ctx);
586             if (xpath_obj == NULL || xpath_obj->type != XPATH_NODESET ||
587                 xpath_obj->nodesetval == NULL)
588                 goto cont;
590             for (j = 0; j < xpath_obj->nodesetval->nodeNr; ++j) {
591                 xmlNodePtr node;
592                 char *path = NULL;
594                 node = xpath_obj->nodesetval->nodeTab[j];
595                 if (!node) continue;
596                 path = (char *) xmlGetProp (node, (xmlChar *) "dev");
597                 if (!path) continue;
599                 if (il_block_devices &&
600                     ignore_device_match (il_block_devices, name, path) != 0)
601                     goto cont2;
603                 add_block_device (dom, path);
604             cont2:
605                 if (path) xmlFree (path);
606             }
607             xmlXPathFreeObject (xpath_obj);
609             /* Network interfaces. */
610             xpath_obj = xmlXPathEval
611                 ((xmlChar *) "/domain/devices/interface[target[@dev]]",
612                  xpath_ctx);
613             if (xpath_obj == NULL || xpath_obj->type != XPATH_NODESET ||
614                 xpath_obj->nodesetval == NULL)
615                 goto cont;
617             xmlNodeSetPtr xml_interfaces = xpath_obj->nodesetval;
619             for (j = 0; j < xml_interfaces->nodeNr; ++j) {
620                 char *path = NULL;
621                 char *address = NULL;
622                 xmlNodePtr xml_interface;
624                 xml_interface = xml_interfaces->nodeTab[j];
625                 if (!xml_interface) continue;
626                 xmlNodePtr child = NULL;
628                 for (child = xml_interface->children; child; child = child->next) {
629                     if (child->type != XML_ELEMENT_NODE) continue;
631                     if (xmlStrEqual(child->name, (const xmlChar *) "target")) {
632                         path = (char *) xmlGetProp (child, (const xmlChar *) "dev");
633                         if (!path) continue;
634                     } else if (xmlStrEqual(child->name, (const xmlChar *) "mac")) {
635                         address = (char *) xmlGetProp (child, (const xmlChar *) "address");
636                         if (!address) continue;
637                     }
638                 }
640                 if (il_interface_devices &&
641                     (ignore_device_match (il_interface_devices, name, path) != 0 ||
642                      ignore_device_match (il_interface_devices, name, address) != 0))
643                     goto cont3;
645                 add_interface_device (dom, path, address);
646                 cont3:
647                     if (path) xmlFree (path);
648                     if (address) xmlFree (address);
649             }
651         cont:
652             if (xpath_obj) xmlXPathFreeObject (xpath_obj);
653             if (xpath_ctx) xmlXPathFreeContext (xpath_ctx);
654             if (xml_doc) xmlFreeDoc (xml_doc);
655             sfree (xml);
656         }
658         sfree (domids);
659     }
661     return 0;
664 static void
665 free_domains ()
667     int i;
669     if (domains) {
670         for (i = 0; i < nr_domains; ++i)
671             virDomainFree (domains[i]);
672         sfree (domains);
673     }
674     domains = NULL;
675     nr_domains = 0;
678 static int
679 add_domain (virDomainPtr dom)
681     virDomainPtr *new_ptr;
682     int new_size = sizeof (domains[0]) * (nr_domains+1);
684     if (domains)
685         new_ptr = realloc (domains, new_size);
686     else
687         new_ptr = malloc (new_size);
689     if (new_ptr == NULL)
690         return -1;
692     domains = new_ptr;
693     domains[nr_domains] = dom;
694     return nr_domains++;
697 static void
698 free_block_devices ()
700     int i;
702     if (block_devices) {
703         for (i = 0; i < nr_block_devices; ++i)
704             sfree (block_devices[i].path);
705         sfree (block_devices);
706     }
707     block_devices = NULL;
708     nr_block_devices = 0;
711 static int
712 add_block_device (virDomainPtr dom, const char *path)
714     struct block_device *new_ptr;
715     int new_size = sizeof (block_devices[0]) * (nr_block_devices+1);
716     char *path_copy;
718     path_copy = strdup (path);
719     if (!path_copy)
720         return -1;
722     if (block_devices)
723         new_ptr = realloc (block_devices, new_size);
724     else
725         new_ptr = malloc (new_size);
727     if (new_ptr == NULL) {
728         sfree (path_copy);
729         return -1;
730     }
731     block_devices = new_ptr;
732     block_devices[nr_block_devices].dom = dom;
733     block_devices[nr_block_devices].path = path_copy;
734     return nr_block_devices++;
737 static void
738 free_interface_devices ()
740     int i;
742     if (interface_devices) {
743         for (i = 0; i < nr_interface_devices; ++i) {
744             sfree (interface_devices[i].path);
745             sfree (interface_devices[i].address);
746         }
747         sfree (interface_devices);
748     }
749     interface_devices = NULL;
750     nr_interface_devices = 0;
753 static int
754 add_interface_device (virDomainPtr dom, const char *path, const char *address)
756     struct interface_device *new_ptr;
757     int new_size = sizeof (interface_devices[0]) * (nr_interface_devices+1);
758     char *path_copy, *address_copy;
760     path_copy = strdup (path);
761     if (!path_copy) return -1;
763     address_copy = strdup (address);
764     if (!address_copy) return -1;
766     if (interface_devices)
767         new_ptr = realloc (interface_devices, new_size);
768     else
769         new_ptr = malloc (new_size);
771     if (new_ptr == NULL) {
772         sfree (path_copy);
773         sfree (address_copy);
774         return -1;
775     }
776     interface_devices = new_ptr;
777     interface_devices[nr_interface_devices].dom = dom;
778     interface_devices[nr_interface_devices].path = path_copy;
779     interface_devices[nr_interface_devices].address = address_copy;
780     return nr_interface_devices++;
783 static int
784 ignore_device_match (ignorelist_t *il, const char *domname, const char *devpath)
786     char *name;
787     int n, r;
789     n = sizeof (char) * (strlen (domname) + strlen (devpath) + 2);
790     name = malloc (n);
791     if (name == NULL) {
792         ERROR ("libvirt plugin: malloc failed.");
793         return 0;
794     }
795     ssnprintf (name, n, "%s:%s", domname, devpath);
796     r = ignorelist_match (il, name);
797     sfree (name);
798     return r;
801 static int
802 lv_shutdown (void)
804     free_block_devices ();
805     free_interface_devices ();
806     free_domains ();
808     if (conn != NULL)
809         virConnectClose (conn);
810     conn = NULL;
812     ignorelist_free (il_domains);
813     il_domains = NULL;
814     ignorelist_free (il_block_devices);
815     il_block_devices = NULL;
816     ignorelist_free (il_interface_devices);
817     il_interface_devices = NULL;
819     return 0;
822 void
823 module_register (void)
825     plugin_register_config ("libvirt",
826             lv_config,
827             config_keys, NR_CONFIG_KEYS);
828     plugin_register_init ("libvirt", lv_init);
829     plugin_register_read ("libvirt", lv_read);
830     plugin_register_shutdown ("libvirt", lv_shutdown);
833 /*
834  * vim: shiftwidth=4 tabstop=8 softtabstop=4 expandtab fdm=marker
835  */