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