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