Fixed memory leaks
[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             sfree (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         sfree (vinfo);
491
492         minfo = malloc (VIR_DOMAIN_MEMORY_STAT_NR * sizeof (virDomainMemoryStatStruct));
493         if (minfo == NULL) {
494             ERROR ("libvirt plugin: malloc failed.");
495             continue;
496         }
497
498         status =  virDomainMemoryStats (domains[i], minfo, VIR_DOMAIN_MEMORY_STAT_NR, 0);
499
500         if (status < 0) {
501             ERROR ("libvirt plugin: virDomainMemoryStats failed with status %i.",
502                     status);
503             sfree (minfo);
504             continue;
505         }
506
507         for (j = 0; j < status; j++) {
508             memory_stats_submit ((gauge_t) minfo[j].val, domains[i], minfo[j].tag);
509         }
510
511         sfree (minfo);
512     }
513
514
515     /* Get block device stats for each domain. */
516     for (i = 0; i < nr_block_devices; ++i) {
517         struct _virDomainBlockStats stats;
518
519         if (virDomainBlockStats (block_devices[i].dom, block_devices[i].path,
520                     &stats, sizeof stats) != 0)
521             continue;
522
523         if ((stats.rd_req != -1) && (stats.wr_req != -1))
524             submit_derive2 ("disk_ops",
525                     (derive_t) stats.rd_req, (derive_t) stats.wr_req,
526                     block_devices[i].dom, block_devices[i].path);
527
528         if ((stats.rd_bytes != -1) && (stats.wr_bytes != -1))
529             submit_derive2 ("disk_octets",
530                     (derive_t) stats.rd_bytes, (derive_t) stats.wr_bytes,
531                     block_devices[i].dom, block_devices[i].path);
532     } /* for (nr_block_devices) */
533
534     /* Get interface stats for each domain. */
535     for (i = 0; i < nr_interface_devices; ++i) {
536         struct _virDomainInterfaceStats stats;
537         char *display_name = NULL;
538
539
540         switch (interface_format) {
541             case if_address:
542                 display_name = interface_devices[i].address;
543                 break;
544             case if_number:
545                 display_name = interface_devices[i].number;
546                 break;
547             case if_name:
548             default:
549                 display_name = interface_devices[i].path;
550         }
551
552         if (virDomainInterfaceStats (interface_devices[i].dom,
553                     interface_devices[i].path,
554                     &stats, sizeof stats) != 0)
555             continue;
556
557         if ((stats.rx_bytes != -1) && (stats.tx_bytes != -1))
558             submit_derive2 ("if_octets",
559                     (derive_t) stats.rx_bytes, (derive_t) stats.tx_bytes,
560                     interface_devices[i].dom, display_name);
561
562         if ((stats.rx_packets != -1) && (stats.tx_packets != -1))
563             submit_derive2 ("if_packets",
564                     (derive_t) stats.rx_packets, (derive_t) stats.tx_packets,
565                     interface_devices[i].dom, display_name);
566
567         if ((stats.rx_errs != -1) && (stats.tx_errs != -1))
568             submit_derive2 ("if_errors",
569                     (derive_t) stats.rx_errs, (derive_t) stats.tx_errs,
570                     interface_devices[i].dom, display_name);
571
572         if ((stats.rx_drop != -1) && (stats.tx_drop != -1))
573             submit_derive2 ("if_dropped",
574                     (derive_t) stats.rx_drop, (derive_t) stats.tx_drop,
575                     interface_devices[i].dom, display_name);
576     } /* for (nr_interface_devices) */
577
578     return 0;
579 }
580
581 static int
582 refresh_lists (void)
583 {
584     int n;
585
586     n = virConnectNumOfDomains (conn);
587     if (n < 0) {
588         VIRT_ERROR (conn, "reading number of domains");
589         return -1;
590     }
591
592     if (n > 0) {
593         int i;
594         int *domids;
595
596         /* Get list of domains. */
597         domids = malloc (sizeof (int) * n);
598         if (domids == 0) {
599             ERROR ("libvirt plugin: malloc failed.");
600             return -1;
601         }
602
603         n = virConnectListDomains (conn, domids, n);
604         if (n < 0) {
605             VIRT_ERROR (conn, "reading list of domains");
606             sfree (domids);
607             return -1;
608         }
609
610         free_block_devices ();
611         free_interface_devices ();
612         free_domains ();
613
614         /* Fetch each domain and add it to the list, unless ignore. */
615         for (i = 0; i < n; ++i) {
616             virDomainPtr dom = NULL;
617             const char *name;
618             char *xml = NULL;
619             xmlDocPtr xml_doc = NULL;
620             xmlXPathContextPtr xpath_ctx = NULL;
621             xmlXPathObjectPtr xpath_obj = NULL;
622             int j;
623
624             dom = virDomainLookupByID (conn, domids[i]);
625             if (dom == NULL) {
626                 VIRT_ERROR (conn, "virDomainLookupByID");
627                 /* Could be that the domain went away -- ignore it anyway. */
628                 continue;
629             }
630
631             name = virDomainGetName (dom);
632             if (name == NULL) {
633                 VIRT_ERROR (conn, "virDomainGetName");
634                 goto cont;
635             }
636
637             if (il_domains && ignorelist_match (il_domains, name) != 0)
638                 goto cont;
639
640             if (add_domain (dom) < 0) {
641                 ERROR ("libvirt plugin: malloc failed.");
642                 goto cont;
643             }
644
645             /* Get a list of devices for this domain. */
646             xml = virDomainGetXMLDesc (dom, 0);
647             if (!xml) {
648                 VIRT_ERROR (conn, "virDomainGetXMLDesc");
649                 goto cont;
650             }
651
652             /* Yuck, XML.  Parse out the devices. */
653             xml_doc = xmlReadDoc ((xmlChar *) xml, NULL, NULL, XML_PARSE_NONET);
654             if (xml_doc == NULL) {
655                 VIRT_ERROR (conn, "xmlReadDoc");
656                 goto cont;
657             }
658
659             xpath_ctx = xmlXPathNewContext (xml_doc);
660
661             /* Block devices. */
662             xpath_obj = xmlXPathEval
663                 ((xmlChar *) "/domain/devices/disk/target[@dev]",
664                  xpath_ctx);
665             if (xpath_obj == NULL || xpath_obj->type != XPATH_NODESET ||
666                 xpath_obj->nodesetval == NULL)
667                 goto cont;
668
669             for (j = 0; j < xpath_obj->nodesetval->nodeNr; ++j) {
670                 xmlNodePtr node;
671                 char *path = NULL;
672
673                 node = xpath_obj->nodesetval->nodeTab[j];
674                 if (!node) continue;
675                 path = (char *) xmlGetProp (node, (xmlChar *) "dev");
676                 if (!path) continue;
677
678                 if (il_block_devices &&
679                     ignore_device_match (il_block_devices, name, path) != 0)
680                     goto cont2;
681
682                 add_block_device (dom, path);
683             cont2:
684                 if (path) xmlFree (path);
685             }
686             xmlXPathFreeObject (xpath_obj);
687
688             /* Network interfaces. */
689             xpath_obj = xmlXPathEval
690                 ((xmlChar *) "/domain/devices/interface[target[@dev]]",
691                  xpath_ctx);
692             if (xpath_obj == NULL || xpath_obj->type != XPATH_NODESET ||
693                 xpath_obj->nodesetval == NULL)
694                 goto cont;
695
696             xmlNodeSetPtr xml_interfaces = xpath_obj->nodesetval;
697
698             for (j = 0; j < xml_interfaces->nodeNr; ++j) {
699                 char *path = NULL;
700                 char *address = NULL;
701                 xmlNodePtr xml_interface;
702
703                 xml_interface = xml_interfaces->nodeTab[j];
704                 if (!xml_interface) continue;
705                 xmlNodePtr child = NULL;
706
707                 for (child = xml_interface->children; child; child = child->next) {
708                     if (child->type != XML_ELEMENT_NODE) continue;
709
710                     if (xmlStrEqual(child->name, (const xmlChar *) "target")) {
711                         path = (char *) xmlGetProp (child, (const xmlChar *) "dev");
712                         if (!path) continue;
713                     } else if (xmlStrEqual(child->name, (const xmlChar *) "mac")) {
714                         address = (char *) xmlGetProp (child, (const xmlChar *) "address");
715                         if (!address) continue;
716                     }
717                 }
718
719                 if (il_interface_devices &&
720                     (ignore_device_match (il_interface_devices, name, path) != 0 ||
721                      ignore_device_match (il_interface_devices, name, address) != 0))
722                     goto cont3;
723
724                 add_interface_device (dom, path, address, j+1);
725                 cont3:
726                     if (path) xmlFree (path);
727                     if (address) xmlFree (address);
728             }
729
730         cont:
731             if (xpath_obj) xmlXPathFreeObject (xpath_obj);
732             if (xpath_ctx) xmlXPathFreeContext (xpath_ctx);
733             if (xml_doc) xmlFreeDoc (xml_doc);
734             sfree (xml);
735         }
736
737         sfree (domids);
738     }
739
740     return 0;
741 }
742
743 static void
744 free_domains ()
745 {
746     int i;
747
748     if (domains) {
749         for (i = 0; i < nr_domains; ++i)
750             virDomainFree (domains[i]);
751         sfree (domains);
752     }
753     domains = NULL;
754     nr_domains = 0;
755 }
756
757 static int
758 add_domain (virDomainPtr dom)
759 {
760     virDomainPtr *new_ptr;
761     int new_size = sizeof (domains[0]) * (nr_domains+1);
762
763     if (domains)
764         new_ptr = realloc (domains, new_size);
765     else
766         new_ptr = malloc (new_size);
767
768     if (new_ptr == NULL)
769         return -1;
770
771     domains = new_ptr;
772     domains[nr_domains] = dom;
773     return nr_domains++;
774 }
775
776 static void
777 free_block_devices ()
778 {
779     int i;
780
781     if (block_devices) {
782         for (i = 0; i < nr_block_devices; ++i)
783             sfree (block_devices[i].path);
784         sfree (block_devices);
785     }
786     block_devices = NULL;
787     nr_block_devices = 0;
788 }
789
790 static int
791 add_block_device (virDomainPtr dom, const char *path)
792 {
793     struct block_device *new_ptr;
794     int new_size = sizeof (block_devices[0]) * (nr_block_devices+1);
795     char *path_copy;
796
797     path_copy = strdup (path);
798     if (!path_copy)
799         return -1;
800
801     if (block_devices)
802         new_ptr = realloc (block_devices, new_size);
803     else
804         new_ptr = malloc (new_size);
805
806     if (new_ptr == NULL) {
807         sfree (path_copy);
808         return -1;
809     }
810     block_devices = new_ptr;
811     block_devices[nr_block_devices].dom = dom;
812     block_devices[nr_block_devices].path = path_copy;
813     return nr_block_devices++;
814 }
815
816 static void
817 free_interface_devices ()
818 {
819     int i;
820
821     if (interface_devices) {
822         for (i = 0; i < nr_interface_devices; ++i) {
823             sfree (interface_devices[i].path);
824             sfree (interface_devices[i].address);
825             sfree (interface_devices[i].number);
826         }
827         sfree (interface_devices);
828     }
829     interface_devices = NULL;
830     nr_interface_devices = 0;
831 }
832
833 static int
834 add_interface_device (virDomainPtr dom, const char *path, const char *address, unsigned int number)
835 {
836     struct interface_device *new_ptr;
837     int new_size = sizeof (interface_devices[0]) * (nr_interface_devices+1);
838     char *path_copy, *address_copy, number_string[15];
839
840     path_copy = strdup (path);
841     if (!path_copy) return -1;
842
843     address_copy = strdup (address);
844     if (!address_copy) {
845         sfree(path_copy);
846         return -1;
847     }
848
849     snprintf(number_string, sizeof (number_string), "interface-%u", number);
850
851     if (interface_devices)
852         new_ptr = realloc (interface_devices, new_size);
853     else
854         new_ptr = malloc (new_size);
855
856     if (new_ptr == NULL) {
857         sfree (path_copy);
858         sfree (address_copy);
859         return -1;
860     }
861     interface_devices = new_ptr;
862     interface_devices[nr_interface_devices].dom = dom;
863     interface_devices[nr_interface_devices].path = path_copy;
864     interface_devices[nr_interface_devices].address = address_copy;
865     interface_devices[nr_interface_devices].number = strdup(number_string);
866     return nr_interface_devices++;
867 }
868
869 static int
870 ignore_device_match (ignorelist_t *il, const char *domname, const char *devpath)
871 {
872     char *name;
873     int n, r;
874
875     n = sizeof (char) * (strlen (domname) + strlen (devpath) + 2);
876     name = malloc (n);
877     if (name == NULL) {
878         ERROR ("libvirt plugin: malloc failed.");
879         return 0;
880     }
881     ssnprintf (name, n, "%s:%s", domname, devpath);
882     r = ignorelist_match (il, name);
883     sfree (name);
884     return r;
885 }
886
887 static int
888 lv_shutdown (void)
889 {
890     free_block_devices ();
891     free_interface_devices ();
892     free_domains ();
893
894     if (conn != NULL)
895         virConnectClose (conn);
896     conn = NULL;
897
898     ignorelist_free (il_domains);
899     il_domains = NULL;
900     ignorelist_free (il_block_devices);
901     il_block_devices = NULL;
902     ignorelist_free (il_interface_devices);
903     il_interface_devices = NULL;
904
905     return 0;
906 }
907
908 void
909 module_register (void)
910 {
911     plugin_register_config ("libvirt",
912     lv_config,
913     config_keys, NR_CONFIG_KEYS);
914     plugin_register_init ("libvirt", lv_init);
915     plugin_register_read ("libvirt", lv_read);
916     plugin_register_shutdown ("libvirt", lv_shutdown);
917 }
918
919 /*
920  * vim: shiftwidth=4 tabstop=8 softtabstop=4 expandtab fdm=marker
921  */