Code

virt plugin: Factor the read state into a struct
[collectd.git] / src / virt.c
1 /**
2  * collectd - src/virt.c
3  * Copyright (C) 2006-2008  Red Hat Inc.
4  *
5  * This program is free software; you can redistribute it and/or modify it
6  * under the terms of the GNU General Public License as published by the
7  * Free Software Foundation; only version 2 of the license is applicable.
8  *
9  * This program is distributed in the hope that it will be useful, but
10  * WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License along
15  * with this program; if not, write to the Free Software Foundation, Inc.,
16  * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA
17  *
18  * Authors:
19  *   Richard W.M. Jones <rjones@redhat.com>
20  **/
22 #include "collectd.h"
24 #include "common.h"
25 #include "plugin.h"
26 #include "utils_complain.h"
27 #include "utils_ignorelist.h"
29 #include <libgen.h> /* for basename(3) */
30 #include <libvirt/libvirt.h>
31 #include <libvirt/virterror.h>
32 #include <libxml/parser.h>
33 #include <libxml/tree.h>
34 #include <libxml/xpath.h>
36 /* Plugin name */
37 #define PLUGIN_NAME "virt"
39 static const char *config_keys[] = {"Connection",
41                                     "RefreshInterval",
43                                     "Domain",
44                                     "BlockDevice",
45                                     "BlockDeviceFormat",
46                                     "BlockDeviceFormatBasename",
47                                     "InterfaceDevice",
48                                     "IgnoreSelected",
50                                     "HostnameFormat",
51                                     "InterfaceFormat",
53                                     "PluginInstanceFormat",
55                                     NULL};
56 #define NR_CONFIG_KEYS ((sizeof config_keys / sizeof config_keys[0]) - 1)
58 /* Connection. */
59 static virConnectPtr conn = 0;
60 static char *conn_string = NULL;
61 static c_complain_t conn_complain = C_COMPLAIN_INIT_STATIC;
63 /* Seconds between list refreshes, 0 disables completely. */
64 static int interval = 60;
66 /* List of domains, if specified. */
67 static ignorelist_t *il_domains = NULL;
68 /* List of block devices, if specified. */
69 static ignorelist_t *il_block_devices = NULL;
70 /* List of network interface devices, if specified. */
71 static ignorelist_t *il_interface_devices = NULL;
73 static int ignore_device_match(ignorelist_t *, const char *domname,
74                                const char *devpath);
76 /* Actual list of block devices found on last refresh. */
77 struct block_device {
78   virDomainPtr dom; /* domain */
79   char *path;       /* name of block device */
80 };
82 /* Actual list of network interfaces found on last refresh. */
83 struct interface_device {
84   virDomainPtr dom; /* domain */
85   char *path;       /* name of interface device */
86   char *address;    /* mac address of interface device */
87   char *number;     /* interface device number */
88 };
90 struct lv_read_state {
91   /* Actual list of domains found on last refresh. */
92   virDomainPtr *domains;
93   int nr_domains;
95   struct block_device *block_devices;
96   int nr_block_devices;
98   struct interface_device *interface_devices;
99   int nr_interface_devices;
100 };
102 static struct lv_read_state read_state = {
103     NULL, 0, NULL, 0, NULL, 0,
104 };
106 static void free_domains(struct lv_read_state *state);
107 static int add_domain(struct lv_read_state *state, virDomainPtr dom);
109 static void free_block_devices(struct lv_read_state *state);
110 static int add_block_device(struct lv_read_state *state, virDomainPtr dom,
111                             const char *path);
113 static void free_interface_devices(struct lv_read_state *state);
114 static int add_interface_device(struct lv_read_state *state, virDomainPtr dom,
115                                 const char *path, const char *address,
116                                 unsigned int number);
118 /* HostnameFormat. */
119 #define HF_MAX_FIELDS 3
121 enum hf_field { hf_none = 0, hf_hostname, hf_name, hf_uuid };
123 static enum hf_field hostname_format[HF_MAX_FIELDS] = {hf_name};
125 /* PluginInstanceFormat */
126 #define PLGINST_MAX_FIELDS 2
128 enum plginst_field { plginst_none = 0, plginst_name, plginst_uuid };
130 static enum plginst_field plugin_instance_format[PLGINST_MAX_FIELDS] = {
131     plginst_none};
133 /* BlockDeviceFormat */
134 enum bd_field { target, source };
136 /* InterfaceFormat. */
137 enum if_field { if_address, if_name, if_number };
139 /* BlockDeviceFormatBasename */
140 _Bool blockdevice_format_basename = 0;
141 static enum bd_field blockdevice_format = target;
142 static enum if_field interface_format = if_name;
144 /* Time that we last refreshed. */
145 static time_t last_refresh = (time_t)0;
147 static int refresh_lists(struct lv_read_state *state);
149 /* ERROR(...) macro for virterrors. */
150 #define VIRT_ERROR(conn, s)                                                    \
151   do {                                                                         \
152     virErrorPtr err;                                                           \
153     err = (conn) ? virConnGetLastError((conn)) : virGetLastError();            \
154     if (err)                                                                   \
155       ERROR("%s: %s", (s), err->message);                                      \
156   } while (0)
158 static void init_value_list(value_list_t *vl, virDomainPtr dom) {
159   int n;
160   const char *name;
161   char uuid[VIR_UUID_STRING_BUFLEN];
163   sstrncpy(vl->plugin, PLUGIN_NAME, sizeof(vl->plugin));
165   vl->host[0] = '\0';
167   /* Construct the hostname field according to HostnameFormat. */
168   for (int i = 0; i < HF_MAX_FIELDS; ++i) {
169     if (hostname_format[i] == hf_none)
170       continue;
172     n = DATA_MAX_NAME_LEN - strlen(vl->host) - 2;
174     if (i > 0 && n >= 1) {
175       strncat(vl->host, ":", 1);
176       n--;
177     }
179     switch (hostname_format[i]) {
180     case hf_none:
181       break;
182     case hf_hostname:
183       strncat(vl->host, hostname_g, n);
184       break;
185     case hf_name:
186       name = virDomainGetName(dom);
187       if (name)
188         strncat(vl->host, name, n);
189       break;
190     case hf_uuid:
191       if (virDomainGetUUIDString(dom, uuid) == 0)
192         strncat(vl->host, uuid, n);
193       break;
194     }
195   }
197   vl->host[sizeof(vl->host) - 1] = '\0';
199   /* Construct the plugin instance field according to PluginInstanceFormat. */
200   for (int i = 0; i < PLGINST_MAX_FIELDS; ++i) {
201     if (plugin_instance_format[i] == plginst_none)
202       continue;
204     n = sizeof(vl->plugin_instance) - strlen(vl->plugin_instance) - 2;
206     if (i > 0 && n >= 1) {
207       strncat(vl->plugin_instance, ":", 1);
208       n--;
209     }
211     switch (plugin_instance_format[i]) {
212     case plginst_none:
213       break;
214     case plginst_name:
215       name = virDomainGetName(dom);
216       if (name)
217         strncat(vl->plugin_instance, name, n);
218       break;
219     case plginst_uuid:
220       if (virDomainGetUUIDString(dom, uuid) == 0)
221         strncat(vl->plugin_instance, uuid, n);
222       break;
223     }
224   }
226   vl->plugin_instance[sizeof(vl->plugin_instance) - 1] = '\0';
228 } /* void init_value_list */
230 static void submit(virDomainPtr dom, char const *type,
231                    char const *type_instance, value_t *values,
232                    size_t values_len) {
233   value_list_t vl = VALUE_LIST_INIT;
234   init_value_list(&vl, dom);
236   vl.values = values;
237   vl.values_len = values_len;
239   sstrncpy(vl.type, type, sizeof(vl.type));
240   if (type_instance != NULL)
241     sstrncpy(vl.type_instance, type_instance, sizeof(vl.type_instance));
243   plugin_dispatch_values(&vl);
246 static void memory_submit(gauge_t value, virDomainPtr dom) {
247   submit(dom, "memory", "total", &(value_t){.gauge = value}, 1);
250 static void memory_stats_submit(gauge_t value, virDomainPtr dom,
251                                 int tag_index) {
252   static const char *tags[] = {"swap_in",        "swap_out", "major_fault",
253                                "minor_fault",    "unused",   "available",
254                                "actual_balloon", "rss"};
256   if ((tag_index < 0) || (tag_index >= STATIC_ARRAY_SIZE(tags))) {
257     ERROR("virt plugin: Array index out of bounds: tag_index = %d", tag_index);
258     return;
259   }
261   submit(dom, "memory", tags[tag_index], &(value_t){.gauge = value}, 1);
264 static void cpu_submit(unsigned long long value, virDomainPtr dom,
265                        const char *type) {
266   submit(dom, type, NULL, &(value_t){.derive = (derive_t)value}, 1);
269 static void vcpu_submit(derive_t value, virDomainPtr dom, int vcpu_nr,
270                         const char *type) {
271   char type_instance[DATA_MAX_NAME_LEN];
273   ssnprintf(type_instance, sizeof(type_instance), "%d", vcpu_nr);
275   submit(dom, type, type_instance, &(value_t){.derive = value}, 1);
278 static void submit_derive2(const char *type, derive_t v0, derive_t v1,
279                            virDomainPtr dom, const char *devname) {
280   value_t values[] = {
281       {.derive = v0}, {.derive = v1},
282   };
284   submit(dom, type, devname, values, STATIC_ARRAY_SIZE(values));
285 } /* void submit_derive2 */
287 static int lv_init(void) {
288   if (virInitialize() != 0)
289     return -1;
290   else
291     return 0;
294 static int lv_config(const char *key, const char *value) {
295   if (virInitialize() != 0)
296     return 1;
298   if (il_domains == NULL)
299     il_domains = ignorelist_create(1);
300   if (il_block_devices == NULL)
301     il_block_devices = ignorelist_create(1);
302   if (il_interface_devices == NULL)
303     il_interface_devices = ignorelist_create(1);
305   if (strcasecmp(key, "Connection") == 0) {
306     char *tmp = strdup(value);
307     if (tmp == NULL) {
308       ERROR(PLUGIN_NAME " plugin: Connection strdup failed.");
309       return 1;
310     }
311     sfree(conn_string);
312     conn_string = tmp;
313     return 0;
314   }
316   if (strcasecmp(key, "RefreshInterval") == 0) {
317     char *eptr = NULL;
318     interval = strtol(value, &eptr, 10);
319     if (eptr == NULL || *eptr != '\0')
320       return 1;
321     return 0;
322   }
324   if (strcasecmp(key, "Domain") == 0) {
325     if (ignorelist_add(il_domains, value))
326       return 1;
327     return 0;
328   }
329   if (strcasecmp(key, "BlockDevice") == 0) {
330     if (ignorelist_add(il_block_devices, value))
331       return 1;
332     return 0;
333   }
335   if (strcasecmp(key, "BlockDeviceFormat") == 0) {
336     if (strcasecmp(value, "target") == 0)
337       blockdevice_format = target;
338     else if (strcasecmp(value, "source") == 0)
339       blockdevice_format = source;
340     else {
341       ERROR(PLUGIN_NAME " plugin: unknown BlockDeviceFormat: %s", value);
342       return -1;
343     }
344     return 0;
345   }
346   if (strcasecmp(key, "BlockDeviceFormatBasename") == 0) {
347     blockdevice_format_basename = IS_TRUE(value);
348     return 0;
349   }
350   if (strcasecmp(key, "InterfaceDevice") == 0) {
351     if (ignorelist_add(il_interface_devices, value))
352       return 1;
353     return 0;
354   }
356   if (strcasecmp(key, "IgnoreSelected") == 0) {
357     if (IS_TRUE(value)) {
358       ignorelist_set_invert(il_domains, 0);
359       ignorelist_set_invert(il_block_devices, 0);
360       ignorelist_set_invert(il_interface_devices, 0);
361     } else {
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   }
369   if (strcasecmp(key, "HostnameFormat") == 0) {
370     char *value_copy;
371     char *fields[HF_MAX_FIELDS];
372     int n;
374     value_copy = strdup(value);
375     if (value_copy == NULL) {
376       ERROR(PLUGIN_NAME " plugin: strdup failed.");
377       return -1;
378     }
380     n = strsplit(value_copy, fields, HF_MAX_FIELDS);
381     if (n < 1) {
382       sfree(value_copy);
383       ERROR(PLUGIN_NAME " plugin: HostnameFormat: no fields");
384       return -1;
385     }
387     for (int 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         ERROR(PLUGIN_NAME " plugin: unknown HostnameFormat field: %s",
396               fields[i]);
397         sfree(value_copy);
398         return -1;
399       }
400     }
401     sfree(value_copy);
403     for (int i = n; i < HF_MAX_FIELDS; ++i)
404       hostname_format[i] = hf_none;
406     return 0;
407   }
409   if (strcasecmp(key, "PluginInstanceFormat") == 0) {
410     char *value_copy;
411     char *fields[PLGINST_MAX_FIELDS];
412     int n;
414     value_copy = strdup(value);
415     if (value_copy == NULL) {
416       ERROR(PLUGIN_NAME " plugin: strdup failed.");
417       return -1;
418     }
420     n = strsplit(value_copy, fields, PLGINST_MAX_FIELDS);
421     if (n < 1) {
422       sfree(value_copy);
423       ERROR(PLUGIN_NAME " plugin: PluginInstanceFormat: no fields");
424       return -1;
425     }
427     for (int i = 0; i < n; ++i) {
428       if (strcasecmp(fields[i], "none") == 0) {
429         plugin_instance_format[i] = plginst_none;
430         break;
431       } else if (strcasecmp(fields[i], "name") == 0)
432         plugin_instance_format[i] = plginst_name;
433       else if (strcasecmp(fields[i], "uuid") == 0)
434         plugin_instance_format[i] = plginst_uuid;
435       else {
436         ERROR(PLUGIN_NAME " plugin: unknown PluginInstanceFormat field: %s",
437               fields[i]);
438         sfree(value_copy);
439         return -1;
440       }
441     }
442     sfree(value_copy);
444     for (int i = n; i < PLGINST_MAX_FIELDS; ++i)
445       plugin_instance_format[i] = plginst_none;
447     return 0;
448   }
450   if (strcasecmp(key, "InterfaceFormat") == 0) {
451     if (strcasecmp(value, "name") == 0)
452       interface_format = if_name;
453     else if (strcasecmp(value, "address") == 0)
454       interface_format = if_address;
455     else if (strcasecmp(value, "number") == 0)
456       interface_format = if_number;
457     else {
458       ERROR(PLUGIN_NAME " plugin: unknown InterfaceFormat: %s", value);
459       return -1;
460     }
461     return 0;
462   }
464   /* Unrecognised option. */
465   return -1;
468 static int lv_read(void) {
469   time_t t;
470   struct lv_read_state *state = &read_state;
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.");
485   time(&t);
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(state) != 0) {
491       if (conn != NULL)
492         virConnectClose(conn);
493       conn = NULL;
494       return -1;
495     }
496     last_refresh = t;
497   }
499 #if 0
500     for (int i = 0; i < nr_domains; ++i)
501         fprintf (stderr, "domain %s\n", virDomainGetName (domains[i]));
502     for (int 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 (int 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
512   /* Get CPU usage, memory, VCPU usage for each domain. */
513   for (int i = 0; i < state->nr_domains; ++i) {
514     virDomainInfo info;
515     virVcpuInfoPtr vinfo = NULL;
516     virDomainMemoryStatPtr minfo = NULL;
517     int status;
519     status = virDomainGetInfo(state->domains[i], &info);
520     if (status != 0) {
521       ERROR(PLUGIN_NAME " plugin: virDomainGetInfo failed with status %i.",
522             status);
523       continue;
524     }
526     if (info.state != VIR_DOMAIN_RUNNING) {
527       /* only gather stats for running domains */
528       continue;
529     }
531     cpu_submit(info.cpuTime, state->domains[i], "virt_cpu_total");
532     memory_submit((gauge_t)info.memory * 1024, state->domains[i]);
534     vinfo = malloc(info.nrVirtCpu * sizeof(vinfo[0]));
535     if (vinfo == NULL) {
536       ERROR(PLUGIN_NAME " plugin: malloc failed.");
537       continue;
538     }
540     status = virDomainGetVcpus(state->domains[i], vinfo, info.nrVirtCpu,
541                                /* cpu map = */ NULL, /* cpu map length = */ 0);
542     if (status < 0) {
543       ERROR(PLUGIN_NAME " plugin: virDomainGetVcpus failed with status %i.",
544             status);
545       sfree(vinfo);
546       continue;
547     }
549     for (int j = 0; j < info.nrVirtCpu; ++j)
550       vcpu_submit(vinfo[j].cpuTime, state->domains[i], vinfo[j].number,
551                   "virt_vcpu");
553     sfree(vinfo);
555     minfo =
556         malloc(VIR_DOMAIN_MEMORY_STAT_NR * sizeof(virDomainMemoryStatStruct));
557     if (minfo == NULL) {
558       ERROR("virt plugin: malloc failed.");
559       continue;
560     }
562     status = virDomainMemoryStats(state->domains[i], minfo,
563                                   VIR_DOMAIN_MEMORY_STAT_NR, 0);
565     if (status < 0) {
566       ERROR("virt plugin: virDomainMemoryStats failed with status %i.", status);
567       sfree(minfo);
568       continue;
569     }
571     for (int j = 0; j < status; j++) {
572       memory_stats_submit((gauge_t)minfo[j].val * 1024, state->domains[i],
573                           minfo[j].tag);
574     }
576     sfree(minfo);
577   }
579   /* Get block device stats for each domain. */
580   for (int i = 0; i < state->nr_block_devices; ++i) {
581     struct _virDomainBlockStats stats;
583     if (virDomainBlockStats(state->block_devices[i].dom,
584                             state->block_devices[i].path, &stats,
585                             sizeof stats) != 0)
586       continue;
588     char *type_instance = NULL;
589     if (blockdevice_format_basename && blockdevice_format == source)
590       type_instance = strdup(basename(state->block_devices[i].path));
591     else
592       type_instance = strdup(state->block_devices[i].path);
594     if ((stats.rd_req != -1) && (stats.wr_req != -1))
595       submit_derive2("disk_ops", (derive_t)stats.rd_req, (derive_t)stats.wr_req,
596                      state->block_devices[i].dom, type_instance);
598     if ((stats.rd_bytes != -1) && (stats.wr_bytes != -1))
599       submit_derive2("disk_octets", (derive_t)stats.rd_bytes,
600                      (derive_t)stats.wr_bytes, state->block_devices[i].dom,
601                      type_instance);
603     sfree(type_instance);
604   } /* for (nr_block_devices) */
606   /* Get interface stats for each domain. */
607   for (int i = 0; i < state->nr_interface_devices; ++i) {
608     struct _virDomainInterfaceStats stats;
609     char *display_name = NULL;
611     switch (interface_format) {
612     case if_address:
613       display_name = state->interface_devices[i].address;
614       break;
615     case if_number:
616       display_name = state->interface_devices[i].number;
617       break;
618     case if_name:
619     default:
620       display_name = state->interface_devices[i].path;
621     }
623     if (virDomainInterfaceStats(state->interface_devices[i].dom,
624                                 state->interface_devices[i].path, &stats,
625                                 sizeof stats) != 0)
626       continue;
628     if ((stats.rx_bytes != -1) && (stats.tx_bytes != -1))
629       submit_derive2("if_octets", (derive_t)stats.rx_bytes,
630                      (derive_t)stats.tx_bytes, state->interface_devices[i].dom,
631                      display_name);
633     if ((stats.rx_packets != -1) && (stats.tx_packets != -1))
634       submit_derive2("if_packets", (derive_t)stats.rx_packets,
635                      (derive_t)stats.tx_packets,
636                      state->interface_devices[i].dom, display_name);
638     if ((stats.rx_errs != -1) && (stats.tx_errs != -1))
639       submit_derive2("if_errors", (derive_t)stats.rx_errs,
640                      (derive_t)stats.tx_errs, state->interface_devices[i].dom,
641                      display_name);
643     if ((stats.rx_drop != -1) && (stats.tx_drop != -1))
644       submit_derive2("if_dropped", (derive_t)stats.rx_drop,
645                      (derive_t)stats.tx_drop, state->interface_devices[i].dom,
646                      display_name);
647   } /* for (nr_interface_devices) */
649   return 0;
652 static int refresh_lists(struct lv_read_state *state) {
653   int n;
655   n = virConnectNumOfDomains(conn);
656   if (n < 0) {
657     VIRT_ERROR(conn, "reading number of domains");
658     return -1;
659   }
661   if (n > 0) {
662     int *domids;
664     /* Get list of domains. */
665     domids = malloc(sizeof(*domids) * n);
666     if (domids == NULL) {
667       ERROR(PLUGIN_NAME " plugin: malloc failed.");
668       return -1;
669     }
671     n = virConnectListDomains(conn, domids, n);
672     if (n < 0) {
673       VIRT_ERROR(conn, "reading list of domains");
674       sfree(domids);
675       return -1;
676     }
678     free_block_devices(state);
679     free_interface_devices(state);
680     free_domains(state);
682     /* Fetch each domain and add it to the list, unless ignore. */
683     for (int i = 0; i < n; ++i) {
684       virDomainPtr dom = NULL;
685       const char *name;
686       char *xml = NULL;
687       xmlDocPtr xml_doc = NULL;
688       xmlXPathContextPtr xpath_ctx = NULL;
689       xmlXPathObjectPtr xpath_obj = NULL;
691       dom = virDomainLookupByID(conn, domids[i]);
692       if (dom == NULL) {
693         VIRT_ERROR(conn, "virDomainLookupByID");
694         /* Could be that the domain went away -- ignore it anyway. */
695         continue;
696       }
698       name = virDomainGetName(dom);
699       if (name == NULL) {
700         VIRT_ERROR(conn, "virDomainGetName");
701         goto cont;
702       }
704       if (il_domains && ignorelist_match(il_domains, name) != 0)
705         goto cont;
707       if (add_domain(state, dom) < 0) {
708         ERROR(PLUGIN_NAME " plugin: malloc failed.");
709         goto cont;
710       }
712       /* Get a list of devices for this domain. */
713       xml = virDomainGetXMLDesc(dom, 0);
714       if (!xml) {
715         VIRT_ERROR(conn, "virDomainGetXMLDesc");
716         goto cont;
717       }
719       /* Yuck, XML.  Parse out the devices. */
720       xml_doc = xmlReadDoc((xmlChar *)xml, NULL, NULL, XML_PARSE_NONET);
721       if (xml_doc == NULL) {
722         VIRT_ERROR(conn, "xmlReadDoc");
723         goto cont;
724       }
726       xpath_ctx = xmlXPathNewContext(xml_doc);
728       /* Block devices. */
729       char *bd_xmlpath = "/domain/devices/disk/target[@dev]";
730       if (blockdevice_format == source)
731         bd_xmlpath = "/domain/devices/disk/source[@dev]";
732       xpath_obj = xmlXPathEval((xmlChar *)bd_xmlpath, xpath_ctx);
734       if (xpath_obj == NULL || xpath_obj->type != XPATH_NODESET ||
735           xpath_obj->nodesetval == NULL)
736         goto cont;
738       for (int j = 0; j < xpath_obj->nodesetval->nodeNr; ++j) {
739         xmlNodePtr node;
740         char *path = NULL;
742         node = xpath_obj->nodesetval->nodeTab[j];
743         if (!node)
744           continue;
745         path = (char *)xmlGetProp(node, (xmlChar *)"dev");
746         if (!path)
747           continue;
749         if (il_block_devices &&
750             ignore_device_match(il_block_devices, name, path) != 0)
751           goto cont2;
753         add_block_device(state, dom, path);
754       cont2:
755         if (path)
756           xmlFree(path);
757       }
758       xmlXPathFreeObject(xpath_obj);
760       /* Network interfaces. */
761       xpath_obj = xmlXPathEval(
762           (xmlChar *)"/domain/devices/interface[target[@dev]]", xpath_ctx);
763       if (xpath_obj == NULL || xpath_obj->type != XPATH_NODESET ||
764           xpath_obj->nodesetval == NULL)
765         goto cont;
767       xmlNodeSetPtr xml_interfaces = xpath_obj->nodesetval;
769       for (int j = 0; j < xml_interfaces->nodeNr; ++j) {
770         char *path = NULL;
771         char *address = NULL;
772         xmlNodePtr xml_interface;
774         xml_interface = xml_interfaces->nodeTab[j];
775         if (!xml_interface)
776           continue;
778         for (xmlNodePtr child = xml_interface->children; child;
779              child = child->next) {
780           if (child->type != XML_ELEMENT_NODE)
781             continue;
783           if (xmlStrEqual(child->name, (const xmlChar *)"target")) {
784             path = (char *)xmlGetProp(child, (const xmlChar *)"dev");
785             if (!path)
786               continue;
787           } else if (xmlStrEqual(child->name, (const xmlChar *)"mac")) {
788             address = (char *)xmlGetProp(child, (const xmlChar *)"address");
789             if (!address)
790               continue;
791           }
792         }
794         if (il_interface_devices &&
795             (ignore_device_match(il_interface_devices, name, path) != 0 ||
796              ignore_device_match(il_interface_devices, name, address) != 0))
797           goto cont3;
799         add_interface_device(state, dom, path, address, j + 1);
800       cont3:
801         if (path)
802           xmlFree(path);
803         if (address)
804           xmlFree(address);
805       }
807     cont:
808       if (xpath_obj)
809         xmlXPathFreeObject(xpath_obj);
810       if (xpath_ctx)
811         xmlXPathFreeContext(xpath_ctx);
812       if (xml_doc)
813         xmlFreeDoc(xml_doc);
814       sfree(xml);
815     }
817     sfree(domids);
818   }
820   return 0;
823 static void free_domains(struct lv_read_state *state) {
824   if (state->domains) {
825     for (int i = 0; i < state->nr_domains; ++i)
826       virDomainFree(state->domains[i]);
827     sfree(state->domains);
828   }
829   state->domains = NULL;
830   state->nr_domains = 0;
833 static int add_domain(struct lv_read_state *state, virDomainPtr dom) {
834   virDomainPtr *new_ptr;
835   int new_size = sizeof(state->domains[0]) * (state->nr_domains + 1);
837   if (state->domains)
838     new_ptr = realloc(state->domains, new_size);
839   else
840     new_ptr = malloc(new_size);
842   if (new_ptr == NULL)
843     return -1;
845   state->domains = new_ptr;
846   state->domains[state->nr_domains] = dom;
847   return state->nr_domains++;
850 static void free_block_devices(struct lv_read_state *state) {
851   if (state->block_devices) {
852     for (int i = 0; i < state->nr_block_devices; ++i)
853       sfree(state->block_devices[i].path);
854     sfree(state->block_devices);
855   }
856   state->block_devices = NULL;
857   state->nr_block_devices = 0;
860 static int add_block_device(struct lv_read_state *state, virDomainPtr dom,
861                             const char *path) {
862   struct block_device *new_ptr;
863   int new_size =
864       sizeof(state->block_devices[0]) * (state->nr_block_devices + 1);
865   char *path_copy;
867   path_copy = strdup(path);
868   if (!path_copy)
869     return -1;
871   if (state->block_devices)
872     new_ptr = realloc(state->block_devices, new_size);
873   else
874     new_ptr = malloc(new_size);
876   if (new_ptr == NULL) {
877     sfree(path_copy);
878     return -1;
879   }
880   state->block_devices = new_ptr;
881   state->block_devices[state->nr_block_devices].dom = dom;
882   state->block_devices[state->nr_block_devices].path = path_copy;
883   return state->nr_block_devices++;
886 static void free_interface_devices(struct lv_read_state *state) {
887   if (state->interface_devices) {
888     for (int i = 0; i < state->nr_interface_devices; ++i) {
889       sfree(state->interface_devices[i].path);
890       sfree(state->interface_devices[i].address);
891       sfree(state->interface_devices[i].number);
892     }
893     sfree(state->interface_devices);
894   }
895   state->interface_devices = NULL;
896   state->nr_interface_devices = 0;
899 static int add_interface_device(struct lv_read_state *state, virDomainPtr dom,
900                                 const char *path, const char *address,
901                                 unsigned int number) {
902   struct interface_device *new_ptr;
903   int new_size =
904       sizeof(state->interface_devices[0]) * (state->nr_interface_devices + 1);
905   char *path_copy, *address_copy, number_string[15];
907   if ((path == NULL) || (address == NULL))
908     return EINVAL;
910   path_copy = strdup(path);
911   if (!path_copy)
912     return -1;
914   address_copy = strdup(address);
915   if (!address_copy) {
916     sfree(path_copy);
917     return -1;
918   }
920   snprintf(number_string, sizeof(number_string), "interface-%u", number);
922   if (state->interface_devices)
923     new_ptr = realloc(state->interface_devices, new_size);
924   else
925     new_ptr = malloc(new_size);
927   if (new_ptr == NULL) {
928     sfree(path_copy);
929     sfree(address_copy);
930     return -1;
931   }
932   state->interface_devices = new_ptr;
933   state->interface_devices[state->nr_interface_devices].dom = dom;
934   state->interface_devices[state->nr_interface_devices].path = path_copy;
935   state->interface_devices[state->nr_interface_devices].address = address_copy;
936   state->interface_devices[state->nr_interface_devices].number =
937       strdup(number_string);
938   return state->nr_interface_devices++;
941 static int ignore_device_match(ignorelist_t *il, const char *domname,
942                                const char *devpath) {
943   char *name;
944   int n, r;
946   if ((domname == NULL) || (devpath == NULL))
947     return 0;
949   n = sizeof(char) * (strlen(domname) + strlen(devpath) + 2);
950   name = malloc(n);
951   if (name == NULL) {
952     ERROR(PLUGIN_NAME " plugin: malloc failed.");
953     return 0;
954   }
955   ssnprintf(name, n, "%s:%s", domname, devpath);
956   r = ignorelist_match(il, name);
957   sfree(name);
958   return r;
961 static int lv_shutdown(void) {
962   struct lv_read_state *state = &read_state;
963   free_block_devices(state);
964   free_interface_devices(state);
965   free_domains(state);
967   if (conn != NULL)
968     virConnectClose(conn);
969   conn = NULL;
971   ignorelist_free(il_domains);
972   il_domains = NULL;
973   ignorelist_free(il_block_devices);
974   il_block_devices = NULL;
975   ignorelist_free(il_interface_devices);
976   il_interface_devices = NULL;
978   return 0;
981 void module_register(void) {
982   plugin_register_config(PLUGIN_NAME, lv_config, config_keys, NR_CONFIG_KEYS);
983   plugin_register_init(PLUGIN_NAME, lv_init);
984   plugin_register_read(PLUGIN_NAME, lv_read);
985   plugin_register_shutdown(PLUGIN_NAME, lv_shutdown);
988 /*
989  * vim: shiftwidth=4 tabstop=8 softtabstop=4 expandtab fdm=marker
990  */