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 <libvirt/libvirt.h>
30 #include <libvirt/virterror.h>
31 #include <libxml/parser.h>
32 #include <libxml/tree.h>
33 #include <libxml/xpath.h>
35 /* Plugin name */
36 #define PLUGIN_NAME "virt"
38 static const char *config_keys[] = {"Connection",
40 "RefreshInterval",
42 "Domain",
43 "BlockDevice",
44 "InterfaceDevice",
45 "IgnoreSelected",
47 "HostnameFormat",
48 "InterfaceFormat",
50 "PluginInstanceFormat",
52 NULL};
53 #define NR_CONFIG_KEYS ((sizeof config_keys / sizeof config_keys[0]) - 1)
55 /* Connection. */
56 static virConnectPtr conn = 0;
57 static char *conn_string = NULL;
58 static c_complain_t conn_complain = C_COMPLAIN_INIT_STATIC;
60 /* Seconds between list refreshes, 0 disables completely. */
61 static int interval = 60;
63 /* List of domains, if specified. */
64 static ignorelist_t *il_domains = NULL;
65 /* List of block devices, if specified. */
66 static ignorelist_t *il_block_devices = NULL;
67 /* List of network interface devices, if specified. */
68 static ignorelist_t *il_interface_devices = NULL;
70 static int ignore_device_match(ignorelist_t *, const char *domname,
71 const char *devpath);
73 /* Actual list of domains found on last refresh. */
74 static virDomainPtr *domains = NULL;
75 static int nr_domains = 0;
77 static void free_domains(void);
78 static int add_domain(virDomainPtr dom);
80 /* Actual list of block devices found on last refresh. */
81 struct block_device {
82 virDomainPtr dom; /* domain */
83 char *path; /* name of block device */
84 };
86 static struct block_device *block_devices = NULL;
87 static int nr_block_devices = 0;
89 static void free_block_devices(void);
90 static int add_block_device(virDomainPtr dom, const char *path);
92 /* Actual list of network interfaces found on last refresh. */
93 struct interface_device {
94 virDomainPtr dom; /* domain */
95 char *path; /* name of interface device */
96 char *address; /* mac address of interface device */
97 char *number; /* interface device number */
98 };
100 static struct interface_device *interface_devices = NULL;
101 static int nr_interface_devices = 0;
103 static void free_interface_devices(void);
104 static int add_interface_device(virDomainPtr dom, const char *path,
105 const char *address, unsigned int number);
107 /* HostnameFormat. */
108 #define HF_MAX_FIELDS 3
110 enum hf_field { hf_none = 0, hf_hostname, hf_name, hf_uuid };
112 static enum hf_field hostname_format[HF_MAX_FIELDS] = {hf_name};
114 /* PluginInstanceFormat */
115 #define PLGINST_MAX_FIELDS 2
117 enum plginst_field { plginst_none = 0, plginst_name, plginst_uuid };
119 static enum plginst_field plugin_instance_format[PLGINST_MAX_FIELDS] = {
120 plginst_none};
122 /* InterfaceFormat. */
123 enum if_field { if_address, if_name, if_number };
125 static enum if_field interface_format = if_name;
127 /* Time that we last refreshed. */
128 static time_t last_refresh = (time_t)0;
130 static int refresh_lists(void);
132 /* ERROR(...) macro for virterrors. */
133 #define VIRT_ERROR(conn, s) \
134 do { \
135 virErrorPtr err; \
136 err = (conn) ? virConnGetLastError((conn)) : virGetLastError(); \
137 if (err) \
138 ERROR("%s: %s", (s), err->message); \
139 } while (0)
141 static void init_value_list(value_list_t *vl, virDomainPtr dom) {
142 int n;
143 const char *name;
144 char uuid[VIR_UUID_STRING_BUFLEN];
146 sstrncpy(vl->plugin, PLUGIN_NAME, sizeof(vl->plugin));
148 vl->host[0] = '\0';
150 /* Construct the hostname field according to HostnameFormat. */
151 for (int i = 0; i < HF_MAX_FIELDS; ++i) {
152 if (hostname_format[i] == hf_none)
153 continue;
155 n = DATA_MAX_NAME_LEN - strlen(vl->host) - 2;
157 if (i > 0 && n >= 1) {
158 strncat(vl->host, ":", 1);
159 n--;
160 }
162 switch (hostname_format[i]) {
163 case hf_none:
164 break;
165 case hf_hostname:
166 strncat(vl->host, hostname_g, n);
167 break;
168 case hf_name:
169 name = virDomainGetName(dom);
170 if (name)
171 strncat(vl->host, name, n);
172 break;
173 case hf_uuid:
174 if (virDomainGetUUIDString(dom, uuid) == 0)
175 strncat(vl->host, uuid, n);
176 break;
177 }
178 }
180 vl->host[sizeof(vl->host) - 1] = '\0';
182 /* Construct the plugin instance field according to PluginInstanceFormat. */
183 for (int i = 0; i < PLGINST_MAX_FIELDS; ++i) {
184 if (plugin_instance_format[i] == plginst_none)
185 continue;
187 n = sizeof(vl->plugin_instance) - strlen(vl->plugin_instance) - 2;
189 if (i > 0 && n >= 1) {
190 strncat(vl->plugin_instance, ":", 1);
191 n--;
192 }
194 switch (plugin_instance_format[i]) {
195 case plginst_none:
196 break;
197 case plginst_name:
198 name = virDomainGetName(dom);
199 if (name)
200 strncat(vl->plugin_instance, name, n);
201 break;
202 case plginst_uuid:
203 if (virDomainGetUUIDString(dom, uuid) == 0)
204 strncat(vl->plugin_instance, uuid, n);
205 break;
206 }
207 }
209 vl->plugin_instance[sizeof(vl->plugin_instance) - 1] = '\0';
211 } /* void init_value_list */
213 static void memory_submit(gauge_t memory, virDomainPtr dom) {
214 value_t values[1];
215 value_list_t vl = VALUE_LIST_INIT;
217 init_value_list(&vl, dom);
219 values[0].gauge = memory;
221 vl.values = values;
222 vl.values_len = 1;
224 sstrncpy(vl.type, "memory", sizeof(vl.type));
225 sstrncpy(vl.type_instance, "total", sizeof(vl.type_instance));
227 plugin_dispatch_values(&vl);
228 }
230 static void memory_stats_submit(gauge_t memory, virDomainPtr dom,
231 int tag_index) {
232 static const char *tags[] = {"swap_in", "swap_out", "major_fault",
233 "minor_fault", "unused", "available",
234 "actual_balloon", "rss"};
236 value_t values[1];
237 value_list_t vl = VALUE_LIST_INIT;
239 init_value_list(&vl, dom);
241 values[0].gauge = memory;
243 vl.values = values;
244 vl.values_len = 1;
246 sstrncpy(vl.type, "memory", sizeof(vl.type));
247 sstrncpy(vl.type_instance, tags[tag_index], sizeof(vl.type_instance));
249 plugin_dispatch_values(&vl);
250 }
252 static void cpu_submit(unsigned long long cpu_time, virDomainPtr dom,
253 const char *type) {
254 value_t values[1];
255 value_list_t vl = VALUE_LIST_INIT;
257 init_value_list(&vl, dom);
259 values[0].derive = cpu_time;
261 vl.values = values;
262 vl.values_len = 1;
264 sstrncpy(vl.type, type, sizeof(vl.type));
266 plugin_dispatch_values(&vl);
267 }
269 static void vcpu_submit(derive_t cpu_time, virDomainPtr dom, int vcpu_nr,
270 const char *type) {
271 value_t values[1];
272 value_list_t vl = VALUE_LIST_INIT;
274 init_value_list(&vl, dom);
276 values[0].derive = cpu_time;
277 vl.values = values;
278 vl.values_len = 1;
280 sstrncpy(vl.type, type, sizeof(vl.type));
281 ssnprintf(vl.type_instance, sizeof(vl.type_instance), "%d", vcpu_nr);
283 plugin_dispatch_values(&vl);
284 }
286 static void submit_derive2(const char *type, derive_t v0, derive_t v1,
287 virDomainPtr dom, const char *devname) {
288 value_t values[2];
289 value_list_t vl = VALUE_LIST_INIT;
291 init_value_list(&vl, dom);
293 values[0].derive = v0;
294 values[1].derive = v1;
295 vl.values = values;
296 vl.values_len = 2;
298 sstrncpy(vl.type, type, sizeof(vl.type));
299 sstrncpy(vl.type_instance, devname, sizeof(vl.type_instance));
301 plugin_dispatch_values(&vl);
302 } /* void submit_derive2 */
304 static int lv_init(void) {
305 if (virInitialize() != 0)
306 return -1;
307 else
308 return 0;
309 }
311 static int lv_config(const char *key, const char *value) {
312 if (virInitialize() != 0)
313 return 1;
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);
322 if (strcasecmp(key, "Connection") == 0) {
323 char *tmp = strdup(value);
324 if (tmp == NULL) {
325 ERROR(PLUGIN_NAME " plugin: Connection strdup failed.");
326 return 1;
327 }
328 sfree(conn_string);
329 conn_string = tmp;
330 return 0;
331 }
333 if (strcasecmp(key, "RefreshInterval") == 0) {
334 char *eptr = NULL;
335 interval = strtol(value, &eptr, 10);
336 if (eptr == NULL || *eptr != '\0')
337 return 1;
338 return 0;
339 }
341 if (strcasecmp(key, "Domain") == 0) {
342 if (ignorelist_add(il_domains, value))
343 return 1;
344 return 0;
345 }
346 if (strcasecmp(key, "BlockDevice") == 0) {
347 if (ignorelist_add(il_block_devices, value))
348 return 1;
349 return 0;
350 }
351 if (strcasecmp(key, "InterfaceDevice") == 0) {
352 if (ignorelist_add(il_interface_devices, value))
353 return 1;
354 return 0;
355 }
357 if (strcasecmp(key, "IgnoreSelected") == 0) {
358 if (IS_TRUE(value)) {
359 ignorelist_set_invert(il_domains, 0);
360 ignorelist_set_invert(il_block_devices, 0);
361 ignorelist_set_invert(il_interface_devices, 0);
362 } else {
363 ignorelist_set_invert(il_domains, 1);
364 ignorelist_set_invert(il_block_devices, 1);
365 ignorelist_set_invert(il_interface_devices, 1);
366 }
367 return 0;
368 }
370 if (strcasecmp(key, "HostnameFormat") == 0) {
371 char *value_copy;
372 char *fields[HF_MAX_FIELDS];
373 int n;
375 value_copy = strdup(value);
376 if (value_copy == NULL) {
377 ERROR(PLUGIN_NAME " plugin: strdup failed.");
378 return -1;
379 }
381 n = strsplit(value_copy, fields, HF_MAX_FIELDS);
382 if (n < 1) {
383 sfree(value_copy);
384 ERROR(PLUGIN_NAME " plugin: HostnameFormat: no fields");
385 return -1;
386 }
388 for (int i = 0; i < n; ++i) {
389 if (strcasecmp(fields[i], "hostname") == 0)
390 hostname_format[i] = hf_hostname;
391 else if (strcasecmp(fields[i], "name") == 0)
392 hostname_format[i] = hf_name;
393 else if (strcasecmp(fields[i], "uuid") == 0)
394 hostname_format[i] = hf_uuid;
395 else {
396 ERROR(PLUGIN_NAME " plugin: unknown HostnameFormat field: %s",
397 fields[i]);
398 sfree(value_copy);
399 return -1;
400 }
401 }
402 sfree(value_copy);
404 for (int i = n; i < HF_MAX_FIELDS; ++i)
405 hostname_format[i] = hf_none;
407 return 0;
408 }
410 if (strcasecmp(key, "PluginInstanceFormat") == 0) {
411 char *value_copy;
412 char *fields[PLGINST_MAX_FIELDS];
413 int n;
415 value_copy = strdup(value);
416 if (value_copy == NULL) {
417 ERROR(PLUGIN_NAME " plugin: strdup failed.");
418 return -1;
419 }
421 n = strsplit(value_copy, fields, PLGINST_MAX_FIELDS);
422 if (n < 1) {
423 sfree(value_copy);
424 ERROR(PLUGIN_NAME " plugin: PluginInstanceFormat: no fields");
425 return -1;
426 }
428 for (int i = 0; i < n; ++i) {
429 if (strcasecmp(fields[i], "none") == 0) {
430 plugin_instance_format[i] = plginst_none;
431 break;
432 } else if (strcasecmp(fields[i], "name") == 0)
433 plugin_instance_format[i] = plginst_name;
434 else if (strcasecmp(fields[i], "uuid") == 0)
435 plugin_instance_format[i] = plginst_uuid;
436 else {
437 ERROR(PLUGIN_NAME " plugin: unknown PluginInstanceFormat field: %s",
438 fields[i]);
439 sfree(value_copy);
440 return -1;
441 }
442 }
443 sfree(value_copy);
445 for (int i = n; i < PLGINST_MAX_FIELDS; ++i)
446 plugin_instance_format[i] = plginst_none;
448 return 0;
449 }
451 if (strcasecmp(key, "InterfaceFormat") == 0) {
452 if (strcasecmp(value, "name") == 0)
453 interface_format = if_name;
454 else if (strcasecmp(value, "address") == 0)
455 interface_format = if_address;
456 else if (strcasecmp(value, "number") == 0)
457 interface_format = if_number;
458 else {
459 ERROR(PLUGIN_NAME " plugin: unknown InterfaceFormat: %s", value);
460 return -1;
461 }
462 return 0;
463 }
465 /* Unrecognised option. */
466 return -1;
467 }
469 static int lv_read(void) {
470 time_t t;
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() != 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 < nr_domains; ++i) {
514 virDomainInfo info;
515 virVcpuInfoPtr vinfo = NULL;
516 virDomainMemoryStatPtr minfo = NULL;
517 int status;
519 status = virDomainGetInfo(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, domains[i], "virt_cpu_total");
532 memory_submit((gauge_t)info.memory * 1024, 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(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, domains[i], vinfo[j].number, "virt_vcpu");
552 sfree(vinfo);
554 minfo =
555 malloc(VIR_DOMAIN_MEMORY_STAT_NR * sizeof(virDomainMemoryStatStruct));
556 if (minfo == NULL) {
557 ERROR("virt plugin: malloc failed.");
558 continue;
559 }
561 status =
562 virDomainMemoryStats(domains[i], minfo, VIR_DOMAIN_MEMORY_STAT_NR, 0);
564 if (status < 0) {
565 ERROR("virt plugin: virDomainMemoryStats failed with status %i.", status);
566 sfree(minfo);
567 continue;
568 }
570 for (int j = 0; j < status; j++) {
571 memory_stats_submit((gauge_t)minfo[j].val * 1024, domains[i],
572 minfo[j].tag);
573 }
575 sfree(minfo);
576 }
578 /* Get block device stats for each domain. */
579 for (int i = 0; i < nr_block_devices; ++i) {
580 struct _virDomainBlockStats stats;
582 if (virDomainBlockStats(block_devices[i].dom, block_devices[i].path, &stats,
583 sizeof stats) != 0)
584 continue;
586 if ((stats.rd_req != -1) && (stats.wr_req != -1))
587 submit_derive2("disk_ops", (derive_t)stats.rd_req, (derive_t)stats.wr_req,
588 block_devices[i].dom, block_devices[i].path);
590 if ((stats.rd_bytes != -1) && (stats.wr_bytes != -1))
591 submit_derive2("disk_octets", (derive_t)stats.rd_bytes,
592 (derive_t)stats.wr_bytes, block_devices[i].dom,
593 block_devices[i].path);
594 } /* for (nr_block_devices) */
596 /* Get interface stats for each domain. */
597 for (int i = 0; i < nr_interface_devices; ++i) {
598 struct _virDomainInterfaceStats stats;
599 char *display_name = NULL;
601 switch (interface_format) {
602 case if_address:
603 display_name = interface_devices[i].address;
604 break;
605 case if_number:
606 display_name = interface_devices[i].number;
607 break;
608 case if_name:
609 default:
610 display_name = interface_devices[i].path;
611 }
613 if (virDomainInterfaceStats(interface_devices[i].dom,
614 interface_devices[i].path, &stats,
615 sizeof stats) != 0)
616 continue;
618 if ((stats.rx_bytes != -1) && (stats.tx_bytes != -1))
619 submit_derive2("if_octets", (derive_t)stats.rx_bytes,
620 (derive_t)stats.tx_bytes, interface_devices[i].dom,
621 display_name);
623 if ((stats.rx_packets != -1) && (stats.tx_packets != -1))
624 submit_derive2("if_packets", (derive_t)stats.rx_packets,
625 (derive_t)stats.tx_packets, interface_devices[i].dom,
626 display_name);
628 if ((stats.rx_errs != -1) && (stats.tx_errs != -1))
629 submit_derive2("if_errors", (derive_t)stats.rx_errs,
630 (derive_t)stats.tx_errs, interface_devices[i].dom,
631 display_name);
633 if ((stats.rx_drop != -1) && (stats.tx_drop != -1))
634 submit_derive2("if_dropped", (derive_t)stats.rx_drop,
635 (derive_t)stats.tx_drop, interface_devices[i].dom,
636 display_name);
637 } /* for (nr_interface_devices) */
639 return 0;
640 }
642 static int refresh_lists(void) {
643 int n;
645 n = virConnectNumOfDomains(conn);
646 if (n < 0) {
647 VIRT_ERROR(conn, "reading number of domains");
648 return -1;
649 }
651 if (n > 0) {
652 int *domids;
654 /* Get list of domains. */
655 domids = malloc(sizeof(*domids) * n);
656 if (domids == NULL) {
657 ERROR(PLUGIN_NAME " plugin: malloc failed.");
658 return -1;
659 }
661 n = virConnectListDomains(conn, domids, n);
662 if (n < 0) {
663 VIRT_ERROR(conn, "reading list of domains");
664 sfree(domids);
665 return -1;
666 }
668 free_block_devices();
669 free_interface_devices();
670 free_domains();
672 /* Fetch each domain and add it to the list, unless ignore. */
673 for (int i = 0; i < n; ++i) {
674 virDomainPtr dom = NULL;
675 const char *name;
676 char *xml = NULL;
677 xmlDocPtr xml_doc = NULL;
678 xmlXPathContextPtr xpath_ctx = NULL;
679 xmlXPathObjectPtr xpath_obj = NULL;
681 dom = virDomainLookupByID(conn, domids[i]);
682 if (dom == NULL) {
683 VIRT_ERROR(conn, "virDomainLookupByID");
684 /* Could be that the domain went away -- ignore it anyway. */
685 continue;
686 }
688 name = virDomainGetName(dom);
689 if (name == NULL) {
690 VIRT_ERROR(conn, "virDomainGetName");
691 goto cont;
692 }
694 if (il_domains && ignorelist_match(il_domains, name) != 0)
695 goto cont;
697 if (add_domain(dom) < 0) {
698 ERROR(PLUGIN_NAME " plugin: malloc failed.");
699 goto cont;
700 }
702 /* Get a list of devices for this domain. */
703 xml = virDomainGetXMLDesc(dom, 0);
704 if (!xml) {
705 VIRT_ERROR(conn, "virDomainGetXMLDesc");
706 goto cont;
707 }
709 /* Yuck, XML. Parse out the devices. */
710 xml_doc = xmlReadDoc((xmlChar *)xml, NULL, NULL, XML_PARSE_NONET);
711 if (xml_doc == NULL) {
712 VIRT_ERROR(conn, "xmlReadDoc");
713 goto cont;
714 }
716 xpath_ctx = xmlXPathNewContext(xml_doc);
718 /* Block devices. */
719 xpath_obj = xmlXPathEval((xmlChar *)"/domain/devices/disk/target[@dev]",
720 xpath_ctx);
721 if (xpath_obj == NULL || xpath_obj->type != XPATH_NODESET ||
722 xpath_obj->nodesetval == NULL)
723 goto cont;
725 for (int j = 0; j < xpath_obj->nodesetval->nodeNr; ++j) {
726 xmlNodePtr node;
727 char *path = NULL;
729 node = xpath_obj->nodesetval->nodeTab[j];
730 if (!node)
731 continue;
732 path = (char *)xmlGetProp(node, (xmlChar *)"dev");
733 if (!path)
734 continue;
736 if (il_block_devices &&
737 ignore_device_match(il_block_devices, name, path) != 0)
738 goto cont2;
740 add_block_device(dom, path);
741 cont2:
742 if (path)
743 xmlFree(path);
744 }
745 xmlXPathFreeObject(xpath_obj);
747 /* Network interfaces. */
748 xpath_obj = xmlXPathEval(
749 (xmlChar *)"/domain/devices/interface[target[@dev]]", xpath_ctx);
750 if (xpath_obj == NULL || xpath_obj->type != XPATH_NODESET ||
751 xpath_obj->nodesetval == NULL)
752 goto cont;
754 xmlNodeSetPtr xml_interfaces = xpath_obj->nodesetval;
756 for (int j = 0; j < xml_interfaces->nodeNr; ++j) {
757 char *path = NULL;
758 char *address = NULL;
759 xmlNodePtr xml_interface;
761 xml_interface = xml_interfaces->nodeTab[j];
762 if (!xml_interface)
763 continue;
765 for (xmlNodePtr child = xml_interface->children; child;
766 child = child->next) {
767 if (child->type != XML_ELEMENT_NODE)
768 continue;
770 if (xmlStrEqual(child->name, (const xmlChar *)"target")) {
771 path = (char *)xmlGetProp(child, (const xmlChar *)"dev");
772 if (!path)
773 continue;
774 } else if (xmlStrEqual(child->name, (const xmlChar *)"mac")) {
775 address = (char *)xmlGetProp(child, (const xmlChar *)"address");
776 if (!address)
777 continue;
778 }
779 }
781 if (il_interface_devices &&
782 (ignore_device_match(il_interface_devices, name, path) != 0 ||
783 ignore_device_match(il_interface_devices, name, address) != 0))
784 goto cont3;
786 add_interface_device(dom, path, address, j + 1);
787 cont3:
788 if (path)
789 xmlFree(path);
790 if (address)
791 xmlFree(address);
792 }
794 cont:
795 if (xpath_obj)
796 xmlXPathFreeObject(xpath_obj);
797 if (xpath_ctx)
798 xmlXPathFreeContext(xpath_ctx);
799 if (xml_doc)
800 xmlFreeDoc(xml_doc);
801 sfree(xml);
802 }
804 sfree(domids);
805 }
807 return 0;
808 }
810 static void free_domains(void) {
811 if (domains) {
812 for (int i = 0; i < nr_domains; ++i)
813 virDomainFree(domains[i]);
814 sfree(domains);
815 }
816 domains = NULL;
817 nr_domains = 0;
818 }
820 static int add_domain(virDomainPtr dom) {
821 virDomainPtr *new_ptr;
822 int new_size = sizeof(domains[0]) * (nr_domains + 1);
824 if (domains)
825 new_ptr = realloc(domains, new_size);
826 else
827 new_ptr = malloc(new_size);
829 if (new_ptr == NULL)
830 return -1;
832 domains = new_ptr;
833 domains[nr_domains] = dom;
834 return nr_domains++;
835 }
837 static void free_block_devices(void) {
838 if (block_devices) {
839 for (int i = 0; i < nr_block_devices; ++i)
840 sfree(block_devices[i].path);
841 sfree(block_devices);
842 }
843 block_devices = NULL;
844 nr_block_devices = 0;
845 }
847 static int add_block_device(virDomainPtr dom, const char *path) {
848 struct block_device *new_ptr;
849 int new_size = sizeof(block_devices[0]) * (nr_block_devices + 1);
850 char *path_copy;
852 path_copy = strdup(path);
853 if (!path_copy)
854 return -1;
856 if (block_devices)
857 new_ptr = realloc(block_devices, new_size);
858 else
859 new_ptr = malloc(new_size);
861 if (new_ptr == NULL) {
862 sfree(path_copy);
863 return -1;
864 }
865 block_devices = new_ptr;
866 block_devices[nr_block_devices].dom = dom;
867 block_devices[nr_block_devices].path = path_copy;
868 return nr_block_devices++;
869 }
871 static void free_interface_devices(void) {
872 if (interface_devices) {
873 for (int i = 0; i < nr_interface_devices; ++i) {
874 sfree(interface_devices[i].path);
875 sfree(interface_devices[i].address);
876 sfree(interface_devices[i].number);
877 }
878 sfree(interface_devices);
879 }
880 interface_devices = NULL;
881 nr_interface_devices = 0;
882 }
884 static int add_interface_device(virDomainPtr dom, const char *path,
885 const char *address, unsigned int number) {
886 struct interface_device *new_ptr;
887 int new_size = sizeof(interface_devices[0]) * (nr_interface_devices + 1);
888 char *path_copy, *address_copy, number_string[15];
890 if ((path == NULL) || (address == NULL))
891 return EINVAL;
893 path_copy = strdup(path);
894 if (!path_copy)
895 return -1;
897 address_copy = strdup(address);
898 if (!address_copy) {
899 sfree(path_copy);
900 return -1;
901 }
903 snprintf(number_string, sizeof(number_string), "interface-%u", number);
905 if (interface_devices)
906 new_ptr = realloc(interface_devices, new_size);
907 else
908 new_ptr = malloc(new_size);
910 if (new_ptr == NULL) {
911 sfree(path_copy);
912 sfree(address_copy);
913 return -1;
914 }
915 interface_devices = new_ptr;
916 interface_devices[nr_interface_devices].dom = dom;
917 interface_devices[nr_interface_devices].path = path_copy;
918 interface_devices[nr_interface_devices].address = address_copy;
919 interface_devices[nr_interface_devices].number = strdup(number_string);
920 return nr_interface_devices++;
921 }
923 static int ignore_device_match(ignorelist_t *il, const char *domname,
924 const char *devpath) {
925 char *name;
926 int n, r;
928 if ((domname == NULL) || (devpath == NULL))
929 return 0;
931 n = sizeof(char) * (strlen(domname) + strlen(devpath) + 2);
932 name = malloc(n);
933 if (name == NULL) {
934 ERROR(PLUGIN_NAME " plugin: malloc failed.");
935 return 0;
936 }
937 ssnprintf(name, n, "%s:%s", domname, devpath);
938 r = ignorelist_match(il, name);
939 sfree(name);
940 return r;
941 }
943 static int lv_shutdown(void) {
944 free_block_devices();
945 free_interface_devices();
946 free_domains();
948 if (conn != NULL)
949 virConnectClose(conn);
950 conn = NULL;
952 ignorelist_free(il_domains);
953 il_domains = NULL;
954 ignorelist_free(il_block_devices);
955 il_block_devices = NULL;
956 ignorelist_free(il_interface_devices);
957 il_interface_devices = NULL;
959 return 0;
960 }
962 void module_register(void) {
963 plugin_register_config(PLUGIN_NAME, lv_config, config_keys, NR_CONFIG_KEYS);
964 plugin_register_init(PLUGIN_NAME, lv_init);
965 plugin_register_read(PLUGIN_NAME, lv_read);
966 plugin_register_shutdown(PLUGIN_NAME, lv_shutdown);
967 }
969 /*
970 * vim: shiftwidth=4 tabstop=8 softtabstop=4 expandtab fdm=marker
971 */