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