Merge branch 'libvirt-interfacenumber'
[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  **/
21
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"
28
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>
34
35 static const char *config_keys[] = {
36     "Connection",
37
38     "RefreshInterval",
39
40     "Domain",
41     "BlockDevice",
42     "InterfaceDevice",
43     "IgnoreSelected",
44
45     "HostnameFormat",
46     "InterfaceFormat",
47
48     NULL
49 };
50 #define NR_CONFIG_KEYS ((sizeof config_keys / sizeof config_keys[0]) - 1)
51
52 /* Connection. */
53 static virConnectPtr conn = 0;
54 static char *conn_string = NULL;
55 static c_complain_t conn_complain = C_COMPLAIN_INIT_STATIC;
56
57 /* Seconds between list refreshes, 0 disables completely. */
58 static int interval = 60;
59
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;
66
67 static int ignore_device_match (ignorelist_t *,
68                                 const char *domname, const char *devpath);
69
70 /* Actual list of domains found on last refresh. */
71 static virDomainPtr *domains = NULL;
72 static int nr_domains = 0;
73
74 static void free_domains (void);
75 static int add_domain (virDomainPtr dom);
76
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 };
82
83 static struct block_device *block_devices = NULL;
84 static int nr_block_devices = 0;
85
86 static void free_block_devices (void);
87 static int add_block_device (virDomainPtr dom, const char *path);
88
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     char *number;               /* interface device number */
95 };
96
97 static struct interface_device *interface_devices = NULL;
98 static int nr_interface_devices = 0;
99
100 static void free_interface_devices (void);
101 static int add_interface_device (virDomainPtr dom, const char *path, const char *address, unsigned int number);
102
103 /* HostnameFormat. */
104 #define HF_MAX_FIELDS 3
105
106 enum hf_field {
107     hf_none = 0,
108     hf_hostname,
109     hf_name,
110     hf_uuid
111 };
112
113 static enum hf_field hostname_format[HF_MAX_FIELDS] =
114     { hf_name };
115
116 /* InterfaceFormat. */
117 enum if_field {
118     if_address,
119     if_name,
120     if_number
121 };
122
123 static enum if_field interface_format = if_name;
124
125 /* Time that we last refreshed. */
126 static time_t last_refresh = (time_t) 0;
127
128 static int refresh_lists (void);
129
130 /* ERROR(...) macro for virterrors. */
131 #define VIRT_ERROR(conn,s) do {                 \
132         virErrorPtr err;                        \
133         err = (conn) ? virConnGetLastError ((conn)) : virGetLastError (); \
134         if (err) ERROR ("%s: %s", (s), err->message);                   \
135     } while(0)
136
137 static void
138 init_value_list (value_list_t *vl, virDomainPtr dom)
139 {
140     int i, n;
141     const char *name;
142     char uuid[VIR_UUID_STRING_BUFLEN];
143
144     sstrncpy (vl->plugin, "libvirt", sizeof (vl->plugin));
145
146     vl->host[0] = '\0';
147
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;
152
153         n = DATA_MAX_NAME_LEN - strlen (vl->host) - 2;
154
155         if (i > 0 && n >= 1) {
156             strncat (vl->host, ":", 1);
157             n--;
158         }
159
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     }
176
177     vl->host[sizeof (vl->host) - 1] = '\0';
178 } /* void init_value_list */
179
180 static void
181 cpu_submit (unsigned long long cpu_time,
182             virDomainPtr dom, const char *type)
183 {
184     value_t values[1];
185     value_list_t vl = VALUE_LIST_INIT;
186
187     init_value_list (&vl, dom);
188
189     values[0].derive = cpu_time;
190
191     vl.values = values;
192     vl.values_len = 1;
193
194     sstrncpy (vl.type, type, sizeof (vl.type));
195
196     plugin_dispatch_values (&vl);
197 }
198
199 static void
200 vcpu_submit (derive_t cpu_time,
201              virDomainPtr dom, int vcpu_nr, const char *type)
202 {
203     value_t values[1];
204     value_list_t vl = VALUE_LIST_INIT;
205
206     init_value_list (&vl, dom);
207
208     values[0].derive = cpu_time;
209     vl.values = values;
210     vl.values_len = 1;
211
212     sstrncpy (vl.type, type, sizeof (vl.type));
213     ssnprintf (vl.type_instance, sizeof (vl.type_instance), "%d", vcpu_nr);
214
215     plugin_dispatch_values (&vl);
216 }
217
218 static void
219 submit_derive2 (const char *type, derive_t v0, derive_t v1,
220              virDomainPtr dom, const char *devname)
221 {
222     value_t values[2];
223     value_list_t vl = VALUE_LIST_INIT;
224
225     init_value_list (&vl, dom);
226
227     values[0].derive = v0;
228     values[1].derive = v1;
229     vl.values = values;
230     vl.values_len = 2;
231
232     sstrncpy (vl.type, type, sizeof (vl.type));
233     sstrncpy (vl.type_instance, devname, sizeof (vl.type_instance));
234
235     plugin_dispatch_values (&vl);
236 } /* void submit_derive2 */
237
238 static int
239 lv_init (void)
240 {
241     if (virInitialize () != 0)
242         return -1;
243
244         return 0;
245 }
246
247 static int
248 lv_config (const char *key, const char *value)
249 {
250     if (virInitialize () != 0)
251         return 1;
252
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);
259
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     }
270
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     }
277
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     }
290
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     }
306
307     if (strcasecmp (key, "HostnameFormat") == 0) {
308         char *value_copy;
309         char *fields[HF_MAX_FIELDS];
310         int i, n;
311
312         value_copy = strdup (value);
313         if (value_copy == NULL) {
314             ERROR ("libvirt plugin: strdup failed.");
315             return -1;
316         }
317
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         }
324
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);
339
340         for (i = n; i < HF_MAX_FIELDS; ++i)
341             hostname_format[i] = hf_none;
342
343         return 0;
344     }
345
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 if (strcasecmp (value, "number") == 0)
352             interface_format = if_number;
353         else {
354             ERROR ("unknown InterfaceFormat: %s", value);
355             return -1;
356         }
357         return 0;
358     }
359
360     /* Unrecognised option. */
361     return -1;
362 }
363
364 static int
365 lv_read (void)
366 {
367     time_t t;
368     int i;
369
370     if (conn == NULL) {
371         /* `conn_string == NULL' is acceptable. */
372         conn = virConnectOpenReadOnly (conn_string);
373         if (conn == NULL) {
374             c_complain (LOG_ERR, &conn_complain,
375                     "libvirt plugin: Unable to connect: "
376                     "virConnectOpenReadOnly failed.");
377             return -1;
378         }
379     }
380     c_release (LOG_NOTICE, &conn_complain,
381             "libvirt plugin: Connection established.");
382
383     time (&t);
384
385     /* Need to refresh domain or device lists? */
386     if ((last_refresh == (time_t) 0) ||
387             ((interval > 0) && ((last_refresh + interval) <= t))) {
388         if (refresh_lists () != 0) {
389             if (conn != NULL)
390                 virConnectClose (conn);
391             conn = NULL;
392             return -1;
393         }
394         last_refresh = t;
395     }
396
397 #if 0
398     for (i = 0; i < nr_domains; ++i)
399         fprintf (stderr, "domain %s\n", virDomainGetName (domains[i]));
400     for (i = 0; i < nr_block_devices; ++i)
401         fprintf  (stderr, "block device %d %s:%s\n",
402                   i, virDomainGetName (block_devices[i].dom),
403                   block_devices[i].path);
404     for (i = 0; i < nr_interface_devices; ++i)
405         fprintf (stderr, "interface device %d %s:%s\n",
406                  i, virDomainGetName (interface_devices[i].dom),
407                  interface_devices[i].path);
408 #endif
409
410     /* Get CPU usage, VCPU usage for each domain. */
411     for (i = 0; i < nr_domains; ++i) {
412         virDomainInfo info;
413         virVcpuInfoPtr vinfo = NULL;
414         int status;
415         int j;
416
417         status = virDomainGetInfo (domains[i], &info);
418         if (status != 0)
419         {
420             ERROR ("libvirt plugin: virDomainGetInfo failed with status %i.",
421                     status);
422             continue;
423         }
424
425         cpu_submit (info.cpuTime, domains[i], "virt_cpu_total");
426
427         vinfo = malloc (info.nrVirtCpu * sizeof (vinfo[0]));
428         if (vinfo == NULL) {
429             ERROR ("libvirt plugin: malloc failed.");
430             continue;
431         }
432
433         status = virDomainGetVcpus (domains[i], vinfo, info.nrVirtCpu,
434                 /* cpu map = */ NULL, /* cpu map length = */ 0);
435         if (status < 0)
436         {
437             ERROR ("libvirt plugin: virDomainGetVcpus failed with status %i.",
438                     status);
439             free (vinfo);
440             continue;
441         }
442
443         for (j = 0; j < info.nrVirtCpu; ++j)
444             vcpu_submit (vinfo[j].cpuTime,
445                     domains[i], vinfo[j].number, "virt_vcpu");
446
447         sfree (vinfo);
448     }
449
450     /* Get block device stats for each domain. */
451     for (i = 0; i < nr_block_devices; ++i) {
452         struct _virDomainBlockStats stats;
453
454         if (virDomainBlockStats (block_devices[i].dom, block_devices[i].path,
455                     &stats, sizeof stats) != 0)
456             continue;
457
458         if ((stats.rd_req != -1) && (stats.wr_req != -1))
459             submit_derive2 ("disk_ops",
460                     (derive_t) stats.rd_req, (derive_t) stats.wr_req,
461                     block_devices[i].dom, block_devices[i].path);
462
463         if ((stats.rd_bytes != -1) && (stats.wr_bytes != -1))
464             submit_derive2 ("disk_octets",
465                     (derive_t) stats.rd_bytes, (derive_t) stats.wr_bytes,
466                     block_devices[i].dom, block_devices[i].path);
467     } /* for (nr_block_devices) */
468
469     /* Get interface stats for each domain. */
470     for (i = 0; i < nr_interface_devices; ++i) {
471         struct _virDomainInterfaceStats stats;
472         char *display_name = NULL;
473
474
475         switch (interface_format) {
476             case if_address:
477                 display_name = interface_devices[i].address;
478                 break;
479             case if_number:
480                 display_name = interface_devices[i].number;
481                 break;
482             case if_name:
483             default:
484                 display_name = interface_devices[i].path;
485         }
486
487         if (virDomainInterfaceStats (interface_devices[i].dom,
488                     interface_devices[i].path,
489                     &stats, sizeof stats) != 0)
490             continue;
491
492         if ((stats.rx_bytes != -1) && (stats.tx_bytes != -1))
493             submit_derive2 ("if_octets",
494                     (derive_t) stats.rx_bytes, (derive_t) stats.tx_bytes,
495                     interface_devices[i].dom, display_name);
496
497         if ((stats.rx_packets != -1) && (stats.tx_packets != -1))
498             submit_derive2 ("if_packets",
499                     (derive_t) stats.rx_packets, (derive_t) stats.tx_packets,
500                     interface_devices[i].dom, display_name);
501
502         if ((stats.rx_errs != -1) && (stats.tx_errs != -1))
503             submit_derive2 ("if_errors",
504                     (derive_t) stats.rx_errs, (derive_t) stats.tx_errs,
505                     interface_devices[i].dom, display_name);
506
507         if ((stats.rx_drop != -1) && (stats.tx_drop != -1))
508             submit_derive2 ("if_dropped",
509                     (derive_t) stats.rx_drop, (derive_t) stats.tx_drop,
510                     interface_devices[i].dom, display_name);
511     } /* for (nr_interface_devices) */
512
513     return 0;
514 }
515
516 static int
517 refresh_lists (void)
518 {
519     int n;
520
521     n = virConnectNumOfDomains (conn);
522     if (n < 0) {
523         VIRT_ERROR (conn, "reading number of domains");
524         return -1;
525     }
526
527     if (n > 0) {
528         int i;
529         int *domids;
530
531         /* Get list of domains. */
532         domids = malloc (sizeof (int) * n);
533         if (domids == 0) {
534             ERROR ("libvirt plugin: malloc failed.");
535             return -1;
536         }
537
538         n = virConnectListDomains (conn, domids, n);
539         if (n < 0) {
540             VIRT_ERROR (conn, "reading list of domains");
541             sfree (domids);
542             return -1;
543         }
544
545         free_block_devices ();
546         free_interface_devices ();
547         free_domains ();
548
549         /* Fetch each domain and add it to the list, unless ignore. */
550         for (i = 0; i < n; ++i) {
551             virDomainPtr dom = NULL;
552             const char *name;
553             char *xml = NULL;
554             xmlDocPtr xml_doc = NULL;
555             xmlXPathContextPtr xpath_ctx = NULL;
556             xmlXPathObjectPtr xpath_obj = NULL;
557             int j;
558
559             dom = virDomainLookupByID (conn, domids[i]);
560             if (dom == NULL) {
561                 VIRT_ERROR (conn, "virDomainLookupByID");
562                 /* Could be that the domain went away -- ignore it anyway. */
563                 continue;
564             }
565
566             name = virDomainGetName (dom);
567             if (name == NULL) {
568                 VIRT_ERROR (conn, "virDomainGetName");
569                 goto cont;
570             }
571
572             if (il_domains && ignorelist_match (il_domains, name) != 0)
573                 goto cont;
574
575             if (add_domain (dom) < 0) {
576                 ERROR ("libvirt plugin: malloc failed.");
577                 goto cont;
578             }
579
580             /* Get a list of devices for this domain. */
581             xml = virDomainGetXMLDesc (dom, 0);
582             if (!xml) {
583                 VIRT_ERROR (conn, "virDomainGetXMLDesc");
584                 goto cont;
585             }
586
587             /* Yuck, XML.  Parse out the devices. */
588             xml_doc = xmlReadDoc ((xmlChar *) xml, NULL, NULL, XML_PARSE_NONET);
589             if (xml_doc == NULL) {
590                 VIRT_ERROR (conn, "xmlReadDoc");
591                 goto cont;
592             }
593
594             xpath_ctx = xmlXPathNewContext (xml_doc);
595
596             /* Block devices. */
597             xpath_obj = xmlXPathEval
598                 ((xmlChar *) "/domain/devices/disk/target[@dev]",
599                  xpath_ctx);
600             if (xpath_obj == NULL || xpath_obj->type != XPATH_NODESET ||
601                 xpath_obj->nodesetval == NULL)
602                 goto cont;
603
604             for (j = 0; j < xpath_obj->nodesetval->nodeNr; ++j) {
605                 xmlNodePtr node;
606                 char *path = NULL;
607
608                 node = xpath_obj->nodesetval->nodeTab[j];
609                 if (!node) continue;
610                 path = (char *) xmlGetProp (node, (xmlChar *) "dev");
611                 if (!path) continue;
612
613                 if (il_block_devices &&
614                     ignore_device_match (il_block_devices, name, path) != 0)
615                     goto cont2;
616
617                 add_block_device (dom, path);
618             cont2:
619                 if (path) xmlFree (path);
620             }
621             xmlXPathFreeObject (xpath_obj);
622
623             /* Network interfaces. */
624             xpath_obj = xmlXPathEval
625                 ((xmlChar *) "/domain/devices/interface[target[@dev]]",
626                  xpath_ctx);
627             if (xpath_obj == NULL || xpath_obj->type != XPATH_NODESET ||
628                 xpath_obj->nodesetval == NULL)
629                 goto cont;
630
631             xmlNodeSetPtr xml_interfaces = xpath_obj->nodesetval;
632
633             for (j = 0; j < xml_interfaces->nodeNr; ++j) {
634                 char *path = NULL;
635                 char *address = NULL;
636                 xmlNodePtr xml_interface;
637
638                 xml_interface = xml_interfaces->nodeTab[j];
639                 if (!xml_interface) continue;
640                 xmlNodePtr child = NULL;
641
642                 for (child = xml_interface->children; child; child = child->next) {
643                     if (child->type != XML_ELEMENT_NODE) continue;
644
645                     if (xmlStrEqual(child->name, (const xmlChar *) "target")) {
646                         path = (char *) xmlGetProp (child, (const xmlChar *) "dev");
647                         if (!path) continue;
648                     } else if (xmlStrEqual(child->name, (const xmlChar *) "mac")) {
649                         address = (char *) xmlGetProp (child, (const xmlChar *) "address");
650                         if (!address) continue;
651                     }
652                 }
653
654                 if (il_interface_devices &&
655                     (ignore_device_match (il_interface_devices, name, path) != 0 ||
656                      ignore_device_match (il_interface_devices, name, address) != 0))
657                     goto cont3;
658
659                 add_interface_device (dom, path, address, j+1);
660                 cont3:
661                     if (path) xmlFree (path);
662                     if (address) xmlFree (address);
663             }
664
665         cont:
666             if (xpath_obj) xmlXPathFreeObject (xpath_obj);
667             if (xpath_ctx) xmlXPathFreeContext (xpath_ctx);
668             if (xml_doc) xmlFreeDoc (xml_doc);
669             sfree (xml);
670         }
671
672         sfree (domids);
673     }
674
675     return 0;
676 }
677
678 static void
679 free_domains ()
680 {
681     int i;
682
683     if (domains) {
684         for (i = 0; i < nr_domains; ++i)
685             virDomainFree (domains[i]);
686         sfree (domains);
687     }
688     domains = NULL;
689     nr_domains = 0;
690 }
691
692 static int
693 add_domain (virDomainPtr dom)
694 {
695     virDomainPtr *new_ptr;
696     int new_size = sizeof (domains[0]) * (nr_domains+1);
697
698     if (domains)
699         new_ptr = realloc (domains, new_size);
700     else
701         new_ptr = malloc (new_size);
702
703     if (new_ptr == NULL)
704         return -1;
705
706     domains = new_ptr;
707     domains[nr_domains] = dom;
708     return nr_domains++;
709 }
710
711 static void
712 free_block_devices ()
713 {
714     int i;
715
716     if (block_devices) {
717         for (i = 0; i < nr_block_devices; ++i)
718             sfree (block_devices[i].path);
719         sfree (block_devices);
720     }
721     block_devices = NULL;
722     nr_block_devices = 0;
723 }
724
725 static int
726 add_block_device (virDomainPtr dom, const char *path)
727 {
728     struct block_device *new_ptr;
729     int new_size = sizeof (block_devices[0]) * (nr_block_devices+1);
730     char *path_copy;
731
732     path_copy = strdup (path);
733     if (!path_copy)
734         return -1;
735
736     if (block_devices)
737         new_ptr = realloc (block_devices, new_size);
738     else
739         new_ptr = malloc (new_size);
740
741     if (new_ptr == NULL) {
742         sfree (path_copy);
743         return -1;
744     }
745     block_devices = new_ptr;
746     block_devices[nr_block_devices].dom = dom;
747     block_devices[nr_block_devices].path = path_copy;
748     return nr_block_devices++;
749 }
750
751 static void
752 free_interface_devices ()
753 {
754     int i;
755
756     if (interface_devices) {
757         for (i = 0; i < nr_interface_devices; ++i) {
758             sfree (interface_devices[i].path);
759             sfree (interface_devices[i].address);
760             sfree (interface_devices[i].number);
761         }
762         sfree (interface_devices);
763     }
764     interface_devices = NULL;
765     nr_interface_devices = 0;
766 }
767
768 static int
769 add_interface_device (virDomainPtr dom, const char *path, const char *address, unsigned int number)
770 {
771     struct interface_device *new_ptr;
772     int new_size = sizeof (interface_devices[0]) * (nr_interface_devices+1);
773     char *path_copy, *address_copy, number_string[15];
774
775     path_copy = strdup (path);
776     if (!path_copy) return -1;
777
778     address_copy = strdup (address);
779     if (!address_copy) {
780         sfree(path_copy);
781         return -1;
782     }
783
784     snprintf(number_string, sizeof (number_string), "interface-%u", number);
785
786     if (interface_devices)
787         new_ptr = realloc (interface_devices, new_size);
788     else
789         new_ptr = malloc (new_size);
790
791     if (new_ptr == NULL) {
792         sfree (path_copy);
793         sfree (address_copy);
794         return -1;
795     }
796     interface_devices = new_ptr;
797     interface_devices[nr_interface_devices].dom = dom;
798     interface_devices[nr_interface_devices].path = path_copy;
799     interface_devices[nr_interface_devices].address = address_copy;
800     interface_devices[nr_interface_devices].number = strdup(number_string);
801     return nr_interface_devices++;
802 }
803
804 static int
805 ignore_device_match (ignorelist_t *il, const char *domname, const char *devpath)
806 {
807     char *name;
808     int n, r;
809
810     n = sizeof (char) * (strlen (domname) + strlen (devpath) + 2);
811     name = malloc (n);
812     if (name == NULL) {
813         ERROR ("libvirt plugin: malloc failed.");
814         return 0;
815     }
816     ssnprintf (name, n, "%s:%s", domname, devpath);
817     r = ignorelist_match (il, name);
818     sfree (name);
819     return r;
820 }
821
822 static int
823 lv_shutdown (void)
824 {
825     free_block_devices ();
826     free_interface_devices ();
827     free_domains ();
828
829     if (conn != NULL)
830         virConnectClose (conn);
831     conn = NULL;
832
833     ignorelist_free (il_domains);
834     il_domains = NULL;
835     ignorelist_free (il_block_devices);
836     il_block_devices = NULL;
837     ignorelist_free (il_interface_devices);
838     il_interface_devices = NULL;
839
840     return 0;
841 }
842
843 void
844 module_register (void)
845 {
846     plugin_register_config ("libvirt",
847     lv_config,
848     config_keys, NR_CONFIG_KEYS);
849     plugin_register_init ("libvirt", lv_init);
850     plugin_register_read ("libvirt", lv_read);
851     plugin_register_shutdown ("libvirt", lv_shutdown);
852 }
853
854 /*
855  * vim: shiftwidth=4 tabstop=8 softtabstop=4 expandtab fdm=marker
856  */