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