Applied the suggested changes.
[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 memory_submit (gauge_t memory, virDomainPtr dom)
182 {
183     value_t values[1];
184     value_list_t vl = VALUE_LIST_INIT;
185
186     init_value_list (&vl, dom);
187
188     values[0].gauge = memory;
189
190     vl.values = values;
191     vl.values_len = 1;
192
193     sstrncpy (vl.type, "memory", sizeof (vl.type));
194
195     plugin_dispatch_values (&vl);
196 }
197
198 static void
199 cpu_submit (unsigned long long cpu_time,
200             virDomainPtr dom, const char *type)
201 {
202     value_t values[1];
203     value_list_t vl = VALUE_LIST_INIT;
204
205     init_value_list (&vl, dom);
206
207     values[0].derive = cpu_time;
208
209     vl.values = values;
210     vl.values_len = 1;
211
212     sstrncpy (vl.type, type, sizeof (vl.type));
213
214     plugin_dispatch_values (&vl);
215 }
216
217 static void
218 vcpu_submit (derive_t cpu_time,
219              virDomainPtr dom, int vcpu_nr, const char *type)
220 {
221     value_t values[1];
222     value_list_t vl = VALUE_LIST_INIT;
223
224     init_value_list (&vl, dom);
225
226     values[0].derive = cpu_time;
227     vl.values = values;
228     vl.values_len = 1;
229
230     sstrncpy (vl.type, type, sizeof (vl.type));
231     ssnprintf (vl.type_instance, sizeof (vl.type_instance), "%d", vcpu_nr);
232
233     plugin_dispatch_values (&vl);
234 }
235
236 static void
237 submit_derive2 (const char *type, derive_t v0, derive_t v1,
238              virDomainPtr dom, const char *devname)
239 {
240     value_t values[2];
241     value_list_t vl = VALUE_LIST_INIT;
242
243     init_value_list (&vl, dom);
244
245     values[0].derive = v0;
246     values[1].derive = v1;
247     vl.values = values;
248     vl.values_len = 2;
249
250     sstrncpy (vl.type, type, sizeof (vl.type));
251     sstrncpy (vl.type_instance, devname, sizeof (vl.type_instance));
252
253     plugin_dispatch_values (&vl);
254 } /* void submit_derive2 */
255
256 static int
257 lv_init (void)
258 {
259     if (virInitialize () != 0)
260         return -1;
261
262         return 0;
263 }
264
265 static int
266 lv_config (const char *key, const char *value)
267 {
268     if (virInitialize () != 0)
269         return 1;
270
271     if (il_domains == NULL)
272         il_domains = ignorelist_create (1);
273     if (il_block_devices == NULL)
274         il_block_devices = ignorelist_create (1);
275     if (il_interface_devices == NULL)
276         il_interface_devices = ignorelist_create (1);
277
278     if (strcasecmp (key, "Connection") == 0) {
279         char *tmp = strdup (value);
280         if (tmp == NULL) {
281             ERROR ("libvirt plugin: Connection strdup failed.");
282             return 1;
283         }
284         sfree (conn_string);
285         conn_string = tmp;
286         return 0;
287     }
288
289     if (strcasecmp (key, "RefreshInterval") == 0) {
290         char *eptr = NULL;
291         interval = strtol (value, &eptr, 10);
292         if (eptr == NULL || *eptr != '\0') return 1;
293         return 0;
294     }
295
296     if (strcasecmp (key, "Domain") == 0) {
297         if (ignorelist_add (il_domains, value)) return 1;
298         return 0;
299     }
300     if (strcasecmp (key, "BlockDevice") == 0) {
301         if (ignorelist_add (il_block_devices, value)) return 1;
302         return 0;
303     }
304     if (strcasecmp (key, "InterfaceDevice") == 0) {
305         if (ignorelist_add (il_interface_devices, value)) return 1;
306         return 0;
307     }
308
309     if (strcasecmp (key, "IgnoreSelected") == 0) {
310         if (IS_TRUE (value))
311         {
312             ignorelist_set_invert (il_domains, 0);
313             ignorelist_set_invert (il_block_devices, 0);
314             ignorelist_set_invert (il_interface_devices, 0);
315         }
316         else
317         {
318             ignorelist_set_invert (il_domains, 1);
319             ignorelist_set_invert (il_block_devices, 1);
320             ignorelist_set_invert (il_interface_devices, 1);
321         }
322         return 0;
323     }
324
325     if (strcasecmp (key, "HostnameFormat") == 0) {
326         char *value_copy;
327         char *fields[HF_MAX_FIELDS];
328         int i, n;
329
330         value_copy = strdup (value);
331         if (value_copy == NULL) {
332             ERROR ("libvirt plugin: strdup failed.");
333             return -1;
334         }
335
336         n = strsplit (value_copy, fields, HF_MAX_FIELDS);
337         if (n < 1) {
338             sfree (value_copy);
339             ERROR ("HostnameFormat: no fields");
340             return -1;
341         }
342
343         for (i = 0; i < n; ++i) {
344             if (strcasecmp (fields[i], "hostname") == 0)
345                 hostname_format[i] = hf_hostname;
346             else if (strcasecmp (fields[i], "name") == 0)
347                 hostname_format[i] = hf_name;
348             else if (strcasecmp (fields[i], "uuid") == 0)
349                 hostname_format[i] = hf_uuid;
350             else {
351                 sfree (value_copy);
352                 ERROR ("unknown HostnameFormat field: %s", fields[i]);
353                 return -1;
354             }
355         }
356         sfree (value_copy);
357
358         for (i = n; i < HF_MAX_FIELDS; ++i)
359             hostname_format[i] = hf_none;
360
361         return 0;
362     }
363
364     if (strcasecmp (key, "InterfaceFormat") == 0) {
365         if (strcasecmp (value, "name") == 0)
366             interface_format = if_name;
367         else if (strcasecmp (value, "address") == 0)
368             interface_format = if_address;
369         else if (strcasecmp (value, "number") == 0)
370             interface_format = if_number;
371         else {
372             ERROR ("unknown InterfaceFormat: %s", value);
373             return -1;
374         }
375         return 0;
376     }
377
378     /* Unrecognised option. */
379     return -1;
380 }
381
382 static int
383 lv_read (void)
384 {
385     time_t t;
386     int i;
387
388     if (conn == NULL) {
389         /* `conn_string == NULL' is acceptable. */
390         conn = virConnectOpenReadOnly (conn_string);
391         if (conn == NULL) {
392             c_complain (LOG_ERR, &conn_complain,
393                     "libvirt plugin: Unable to connect: "
394                     "virConnectOpenReadOnly failed.");
395             return -1;
396         }
397     }
398     c_release (LOG_NOTICE, &conn_complain,
399             "libvirt plugin: Connection established.");
400
401     time (&t);
402
403     /* Need to refresh domain or device lists? */
404     if ((last_refresh == (time_t) 0) ||
405             ((interval > 0) && ((last_refresh + interval) <= t))) {
406         if (refresh_lists () != 0) {
407             if (conn != NULL)
408                 virConnectClose (conn);
409             conn = NULL;
410             return -1;
411         }
412         last_refresh = t;
413     }
414
415 #if 0
416     for (i = 0; i < nr_domains; ++i)
417         fprintf (stderr, "domain %s\n", virDomainGetName (domains[i]));
418     for (i = 0; i < nr_block_devices; ++i)
419         fprintf  (stderr, "block device %d %s:%s\n",
420                   i, virDomainGetName (block_devices[i].dom),
421                   block_devices[i].path);
422     for (i = 0; i < nr_interface_devices; ++i)
423         fprintf (stderr, "interface device %d %s:%s\n",
424                  i, virDomainGetName (interface_devices[i].dom),
425                  interface_devices[i].path);
426 #endif
427
428     /* Get CPU usage, memory, VCPU usage for each domain. */
429     for (i = 0; i < nr_domains; ++i) {
430         virDomainInfo info;
431         virVcpuInfoPtr vinfo = NULL;
432         int status;
433         int j;
434
435         status = virDomainGetInfo (domains[i], &info);
436         if (status != 0)
437         {
438             ERROR ("libvirt plugin: virDomainGetInfo failed with status %i.",
439                     status);
440             continue;
441         }
442
443         cpu_submit (info.cpuTime, domains[i], "virt_cpu_total");
444         memory_submit ((gauge_t) info.memory, domains[i]);
445
446         vinfo = malloc (info.nrVirtCpu * sizeof (vinfo[0]));
447         if (vinfo == NULL) {
448             ERROR ("libvirt plugin: malloc failed.");
449             continue;
450         }
451
452         status = virDomainGetVcpus (domains[i], vinfo, info.nrVirtCpu,
453                 /* cpu map = */ NULL, /* cpu map length = */ 0);
454         if (status < 0)
455         {
456             ERROR ("libvirt plugin: virDomainGetVcpus failed with status %i.",
457                     status);
458             free (vinfo);
459             continue;
460         }
461
462         for (j = 0; j < info.nrVirtCpu; ++j)
463             vcpu_submit (vinfo[j].cpuTime,
464                     domains[i], vinfo[j].number, "virt_vcpu");
465
466         sfree (vinfo);
467     }
468
469     /* Get block device stats for each domain. */
470     for (i = 0; i < nr_block_devices; ++i) {
471         struct _virDomainBlockStats stats;
472
473         if (virDomainBlockStats (block_devices[i].dom, block_devices[i].path,
474                     &stats, sizeof stats) != 0)
475             continue;
476
477         if ((stats.rd_req != -1) && (stats.wr_req != -1))
478             submit_derive2 ("disk_ops",
479                     (derive_t) stats.rd_req, (derive_t) stats.wr_req,
480                     block_devices[i].dom, block_devices[i].path);
481
482         if ((stats.rd_bytes != -1) && (stats.wr_bytes != -1))
483             submit_derive2 ("disk_octets",
484                     (derive_t) stats.rd_bytes, (derive_t) stats.wr_bytes,
485                     block_devices[i].dom, block_devices[i].path);
486     } /* for (nr_block_devices) */
487
488     /* Get interface stats for each domain. */
489     for (i = 0; i < nr_interface_devices; ++i) {
490         struct _virDomainInterfaceStats stats;
491         char *display_name = NULL;
492
493
494         switch (interface_format) {
495             case if_address:
496                 display_name = interface_devices[i].address;
497                 break;
498             case if_number:
499                 display_name = interface_devices[i].number;
500                 break;
501             case if_name:
502             default:
503                 display_name = interface_devices[i].path;
504         }
505
506         if (virDomainInterfaceStats (interface_devices[i].dom,
507                     interface_devices[i].path,
508                     &stats, sizeof stats) != 0)
509             continue;
510
511         if ((stats.rx_bytes != -1) && (stats.tx_bytes != -1))
512             submit_derive2 ("if_octets",
513                     (derive_t) stats.rx_bytes, (derive_t) stats.tx_bytes,
514                     interface_devices[i].dom, display_name);
515
516         if ((stats.rx_packets != -1) && (stats.tx_packets != -1))
517             submit_derive2 ("if_packets",
518                     (derive_t) stats.rx_packets, (derive_t) stats.tx_packets,
519                     interface_devices[i].dom, display_name);
520
521         if ((stats.rx_errs != -1) && (stats.tx_errs != -1))
522             submit_derive2 ("if_errors",
523                     (derive_t) stats.rx_errs, (derive_t) stats.tx_errs,
524                     interface_devices[i].dom, display_name);
525
526         if ((stats.rx_drop != -1) && (stats.tx_drop != -1))
527             submit_derive2 ("if_dropped",
528                     (derive_t) stats.rx_drop, (derive_t) stats.tx_drop,
529                     interface_devices[i].dom, display_name);
530     } /* for (nr_interface_devices) */
531
532     return 0;
533 }
534
535 static int
536 refresh_lists (void)
537 {
538     int n;
539
540     n = virConnectNumOfDomains (conn);
541     if (n < 0) {
542         VIRT_ERROR (conn, "reading number of domains");
543         return -1;
544     }
545
546     if (n > 0) {
547         int i;
548         int *domids;
549
550         /* Get list of domains. */
551         domids = malloc (sizeof (int) * n);
552         if (domids == 0) {
553             ERROR ("libvirt plugin: malloc failed.");
554             return -1;
555         }
556
557         n = virConnectListDomains (conn, domids, n);
558         if (n < 0) {
559             VIRT_ERROR (conn, "reading list of domains");
560             sfree (domids);
561             return -1;
562         }
563
564         free_block_devices ();
565         free_interface_devices ();
566         free_domains ();
567
568         /* Fetch each domain and add it to the list, unless ignore. */
569         for (i = 0; i < n; ++i) {
570             virDomainPtr dom = NULL;
571             const char *name;
572             char *xml = NULL;
573             xmlDocPtr xml_doc = NULL;
574             xmlXPathContextPtr xpath_ctx = NULL;
575             xmlXPathObjectPtr xpath_obj = NULL;
576             int j;
577
578             dom = virDomainLookupByID (conn, domids[i]);
579             if (dom == NULL) {
580                 VIRT_ERROR (conn, "virDomainLookupByID");
581                 /* Could be that the domain went away -- ignore it anyway. */
582                 continue;
583             }
584
585             name = virDomainGetName (dom);
586             if (name == NULL) {
587                 VIRT_ERROR (conn, "virDomainGetName");
588                 goto cont;
589             }
590
591             if (il_domains && ignorelist_match (il_domains, name) != 0)
592                 goto cont;
593
594             if (add_domain (dom) < 0) {
595                 ERROR ("libvirt plugin: malloc failed.");
596                 goto cont;
597             }
598
599             /* Get a list of devices for this domain. */
600             xml = virDomainGetXMLDesc (dom, 0);
601             if (!xml) {
602                 VIRT_ERROR (conn, "virDomainGetXMLDesc");
603                 goto cont;
604             }
605
606             /* Yuck, XML.  Parse out the devices. */
607             xml_doc = xmlReadDoc ((xmlChar *) xml, NULL, NULL, XML_PARSE_NONET);
608             if (xml_doc == NULL) {
609                 VIRT_ERROR (conn, "xmlReadDoc");
610                 goto cont;
611             }
612
613             xpath_ctx = xmlXPathNewContext (xml_doc);
614
615             /* Block devices. */
616             xpath_obj = xmlXPathEval
617                 ((xmlChar *) "/domain/devices/disk/target[@dev]",
618                  xpath_ctx);
619             if (xpath_obj == NULL || xpath_obj->type != XPATH_NODESET ||
620                 xpath_obj->nodesetval == NULL)
621                 goto cont;
622
623             for (j = 0; j < xpath_obj->nodesetval->nodeNr; ++j) {
624                 xmlNodePtr node;
625                 char *path = NULL;
626
627                 node = xpath_obj->nodesetval->nodeTab[j];
628                 if (!node) continue;
629                 path = (char *) xmlGetProp (node, (xmlChar *) "dev");
630                 if (!path) continue;
631
632                 if (il_block_devices &&
633                     ignore_device_match (il_block_devices, name, path) != 0)
634                     goto cont2;
635
636                 add_block_device (dom, path);
637             cont2:
638                 if (path) xmlFree (path);
639             }
640             xmlXPathFreeObject (xpath_obj);
641
642             /* Network interfaces. */
643             xpath_obj = xmlXPathEval
644                 ((xmlChar *) "/domain/devices/interface[target[@dev]]",
645                  xpath_ctx);
646             if (xpath_obj == NULL || xpath_obj->type != XPATH_NODESET ||
647                 xpath_obj->nodesetval == NULL)
648                 goto cont;
649
650             xmlNodeSetPtr xml_interfaces = xpath_obj->nodesetval;
651
652             for (j = 0; j < xml_interfaces->nodeNr; ++j) {
653                 char *path = NULL;
654                 char *address = NULL;
655                 xmlNodePtr xml_interface;
656
657                 xml_interface = xml_interfaces->nodeTab[j];
658                 if (!xml_interface) continue;
659                 xmlNodePtr child = NULL;
660
661                 for (child = xml_interface->children; child; child = child->next) {
662                     if (child->type != XML_ELEMENT_NODE) continue;
663
664                     if (xmlStrEqual(child->name, (const xmlChar *) "target")) {
665                         path = (char *) xmlGetProp (child, (const xmlChar *) "dev");
666                         if (!path) continue;
667                     } else if (xmlStrEqual(child->name, (const xmlChar *) "mac")) {
668                         address = (char *) xmlGetProp (child, (const xmlChar *) "address");
669                         if (!address) continue;
670                     }
671                 }
672
673                 if (il_interface_devices &&
674                     (ignore_device_match (il_interface_devices, name, path) != 0 ||
675                      ignore_device_match (il_interface_devices, name, address) != 0))
676                     goto cont3;
677
678                 add_interface_device (dom, path, address, j+1);
679                 cont3:
680                     if (path) xmlFree (path);
681                     if (address) xmlFree (address);
682             }
683
684         cont:
685             if (xpath_obj) xmlXPathFreeObject (xpath_obj);
686             if (xpath_ctx) xmlXPathFreeContext (xpath_ctx);
687             if (xml_doc) xmlFreeDoc (xml_doc);
688             sfree (xml);
689         }
690
691         sfree (domids);
692     }
693
694     return 0;
695 }
696
697 static void
698 free_domains ()
699 {
700     int i;
701
702     if (domains) {
703         for (i = 0; i < nr_domains; ++i)
704             virDomainFree (domains[i]);
705         sfree (domains);
706     }
707     domains = NULL;
708     nr_domains = 0;
709 }
710
711 static int
712 add_domain (virDomainPtr dom)
713 {
714     virDomainPtr *new_ptr;
715     int new_size = sizeof (domains[0]) * (nr_domains+1);
716
717     if (domains)
718         new_ptr = realloc (domains, new_size);
719     else
720         new_ptr = malloc (new_size);
721
722     if (new_ptr == NULL)
723         return -1;
724
725     domains = new_ptr;
726     domains[nr_domains] = dom;
727     return nr_domains++;
728 }
729
730 static void
731 free_block_devices ()
732 {
733     int i;
734
735     if (block_devices) {
736         for (i = 0; i < nr_block_devices; ++i)
737             sfree (block_devices[i].path);
738         sfree (block_devices);
739     }
740     block_devices = NULL;
741     nr_block_devices = 0;
742 }
743
744 static int
745 add_block_device (virDomainPtr dom, const char *path)
746 {
747     struct block_device *new_ptr;
748     int new_size = sizeof (block_devices[0]) * (nr_block_devices+1);
749     char *path_copy;
750
751     path_copy = strdup (path);
752     if (!path_copy)
753         return -1;
754
755     if (block_devices)
756         new_ptr = realloc (block_devices, new_size);
757     else
758         new_ptr = malloc (new_size);
759
760     if (new_ptr == NULL) {
761         sfree (path_copy);
762         return -1;
763     }
764     block_devices = new_ptr;
765     block_devices[nr_block_devices].dom = dom;
766     block_devices[nr_block_devices].path = path_copy;
767     return nr_block_devices++;
768 }
769
770 static void
771 free_interface_devices ()
772 {
773     int i;
774
775     if (interface_devices) {
776         for (i = 0; i < nr_interface_devices; ++i) {
777             sfree (interface_devices[i].path);
778             sfree (interface_devices[i].address);
779             sfree (interface_devices[i].number);
780         }
781         sfree (interface_devices);
782     }
783     interface_devices = NULL;
784     nr_interface_devices = 0;
785 }
786
787 static int
788 add_interface_device (virDomainPtr dom, const char *path, const char *address, unsigned int number)
789 {
790     struct interface_device *new_ptr;
791     int new_size = sizeof (interface_devices[0]) * (nr_interface_devices+1);
792     char *path_copy, *address_copy, number_string[15];
793
794     path_copy = strdup (path);
795     if (!path_copy) return -1;
796
797     address_copy = strdup (address);
798     if (!address_copy) {
799         sfree(path_copy);
800         return -1;
801     }
802
803     snprintf(number_string, sizeof (number_string), "interface-%u", number);
804
805     if (interface_devices)
806         new_ptr = realloc (interface_devices, new_size);
807     else
808         new_ptr = malloc (new_size);
809
810     if (new_ptr == NULL) {
811         sfree (path_copy);
812         sfree (address_copy);
813         return -1;
814     }
815     interface_devices = new_ptr;
816     interface_devices[nr_interface_devices].dom = dom;
817     interface_devices[nr_interface_devices].path = path_copy;
818     interface_devices[nr_interface_devices].address = address_copy;
819     interface_devices[nr_interface_devices].number = strdup(number_string);
820     return nr_interface_devices++;
821 }
822
823 static int
824 ignore_device_match (ignorelist_t *il, const char *domname, const char *devpath)
825 {
826     char *name;
827     int n, r;
828
829     n = sizeof (char) * (strlen (domname) + strlen (devpath) + 2);
830     name = malloc (n);
831     if (name == NULL) {
832         ERROR ("libvirt plugin: malloc failed.");
833         return 0;
834     }
835     ssnprintf (name, n, "%s:%s", domname, devpath);
836     r = ignorelist_match (il, name);
837     sfree (name);
838     return r;
839 }
840
841 static int
842 lv_shutdown (void)
843 {
844     free_block_devices ();
845     free_interface_devices ();
846     free_domains ();
847
848     if (conn != NULL)
849         virConnectClose (conn);
850     conn = NULL;
851
852     ignorelist_free (il_domains);
853     il_domains = NULL;
854     ignorelist_free (il_block_devices);
855     il_block_devices = NULL;
856     ignorelist_free (il_interface_devices);
857     il_interface_devices = NULL;
858
859     return 0;
860 }
861
862 void
863 module_register (void)
864 {
865     plugin_register_config ("libvirt",
866     lv_config,
867     config_keys, NR_CONFIG_KEYS);
868     plugin_register_init ("libvirt", lv_init);
869     plugin_register_read ("libvirt", lv_read);
870     plugin_register_shutdown ("libvirt", lv_shutdown);
871 }
872
873 /*
874  * vim: shiftwidth=4 tabstop=8 softtabstop=4 expandtab fdm=marker
875  */