src/virt_test.c: Fix memory leaks detected by valgrind
[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  *   Przemyslaw Szczerbik <przemyslawx.szczerbik@intel.com>
21  **/
22
23 #include "collectd.h"
24
25 #include "common.h"
26 #include "plugin.h"
27 #include "utils_complain.h"
28 #include "utils_ignorelist.h"
29
30 #include <libgen.h> /* for basename(3) */
31 #include <libvirt/libvirt.h>
32 #include <libvirt/virterror.h>
33 #include <libxml/parser.h>
34 #include <libxml/tree.h>
35 #include <libxml/xpath.h>
36 #include <libxml/xpathInternals.h>
37
38 /* Plugin name */
39 #define PLUGIN_NAME "virt"
40
41 /* Secure strcat macro assuring null termination. Parameter (n) is the size of
42    buffer (d), allowing this macro to be safe for static and dynamic buffers */
43 #define SSTRNCAT(d, s, n)                                                      \
44   do {                                                                         \
45     size_t _l = strlen(d);                                                     \
46     sstrncpy((d) + _l, (s), (n)-_l);                                           \
47   } while (0)
48
49 #ifdef LIBVIR_CHECK_VERSION
50
51 #if LIBVIR_CHECK_VERSION(0, 9, 2)
52 #define HAVE_DOM_REASON 1
53 #endif
54
55 #if LIBVIR_CHECK_VERSION(0, 9, 5)
56 #define HAVE_BLOCK_STATS_FLAGS 1
57 #define HAVE_DOM_REASON_PAUSED_SHUTTING_DOWN 1
58 #endif
59
60 #if LIBVIR_CHECK_VERSION(0, 9, 10)
61 #define HAVE_DISK_ERR 1
62 #endif
63
64 #if LIBVIR_CHECK_VERSION(0, 9, 11)
65 #define HAVE_CPU_STATS 1
66 #define HAVE_DOM_STATE_PMSUSPENDED 1
67 #define HAVE_DOM_REASON_RUNNING_WAKEUP 1
68 #endif
69
70 /*
71   virConnectListAllDomains() appeared in 0.10.2
72   Note that LIBVIR_CHECK_VERSION appeared a year later, so
73   in some systems which actually have virConnectListAllDomains()
74   we can't detect this.
75  */
76 #if LIBVIR_CHECK_VERSION(0, 10, 2)
77 #define HAVE_LIST_ALL_DOMAINS 1
78 #endif
79
80 #if LIBVIR_CHECK_VERSION(1, 0, 1)
81 #define HAVE_DOM_REASON_PAUSED_SNAPSHOT 1
82 #endif
83
84 #if LIBVIR_CHECK_VERSION(1, 1, 1)
85 #define HAVE_DOM_REASON_PAUSED_CRASHED 1
86 #endif
87
88 #if LIBVIR_CHECK_VERSION(1, 2, 9)
89 #define HAVE_JOB_STATS 1
90 #endif
91
92 #if LIBVIR_CHECK_VERSION(1, 2, 10)
93 #define HAVE_DOM_REASON_CRASHED 1
94 #endif
95
96 #if LIBVIR_CHECK_VERSION(1, 2, 11)
97 #define HAVE_FS_INFO 1
98 #endif
99
100 #if LIBVIR_CHECK_VERSION(1, 2, 15)
101 #define HAVE_DOM_REASON_PAUSED_STARTING_UP 1
102 #endif
103
104 #if LIBVIR_CHECK_VERSION(1, 3, 3)
105 #define HAVE_PERF_STATS 1
106 #define HAVE_DOM_REASON_POSTCOPY 1
107 #endif
108
109 #endif /* LIBVIR_CHECK_VERSION */
110
111 static const char *config_keys[] = {"Connection",
112
113                                     "RefreshInterval",
114
115                                     "Domain",
116                                     "BlockDevice",
117                                     "BlockDeviceFormat",
118                                     "BlockDeviceFormatBasename",
119                                     "InterfaceDevice",
120                                     "IgnoreSelected",
121
122                                     "HostnameFormat",
123                                     "InterfaceFormat",
124
125                                     "PluginInstanceFormat",
126
127                                     "Instances",
128                                     "ExtraStats",
129                                     "PersistentNotification",
130                                     NULL};
131
132 /* PersistentNotification is false by default */
133 static _Bool persistent_notification = 0;
134
135 /* libvirt event loop */
136 static pthread_t event_loop_tid;
137
138 static int domain_event_cb_id;
139
140 const char *domain_states[] = {
141         [VIR_DOMAIN_NOSTATE] = "no state",
142         [VIR_DOMAIN_RUNNING] = "the domain is running",
143         [VIR_DOMAIN_BLOCKED] = "the domain is blocked on resource",
144         [VIR_DOMAIN_PAUSED] = "the domain is paused by user",
145         [VIR_DOMAIN_SHUTDOWN] = "the domain is being shut down",
146         [VIR_DOMAIN_SHUTOFF] = "the domain is shut off",
147         [VIR_DOMAIN_CRASHED] = "the domain is crashed",
148 #ifdef HAVE_DOM_STATE_PMSUSPENDED
149         [VIR_DOMAIN_PMSUSPENDED] =
150             "the domain is suspended by guest power management",
151 #endif
152 };
153
154 static int map_domain_event_to_state(int event) {
155   int ret;
156   switch (event) {
157   case VIR_DOMAIN_EVENT_STARTED:
158     ret = VIR_DOMAIN_RUNNING;
159     break;
160   case VIR_DOMAIN_EVENT_SUSPENDED:
161     ret = VIR_DOMAIN_PAUSED;
162     break;
163   case VIR_DOMAIN_EVENT_RESUMED:
164     ret = VIR_DOMAIN_RUNNING;
165     break;
166   case VIR_DOMAIN_EVENT_STOPPED:
167     ret = VIR_DOMAIN_SHUTOFF;
168     break;
169   case VIR_DOMAIN_EVENT_SHUTDOWN:
170     ret = VIR_DOMAIN_SHUTDOWN;
171     break;
172 #ifdef HAVE_DOM_STATE_PMSUSPENDED
173   case VIR_DOMAIN_EVENT_PMSUSPENDED:
174     ret = VIR_DOMAIN_PMSUSPENDED;
175     break;
176 #endif
177 #ifdef HAVE_DOM_REASON_CRASHED
178   case VIR_DOMAIN_EVENT_CRASHED:
179     ret = VIR_DOMAIN_CRASHED;
180     break;
181 #endif
182   default:
183     ret = VIR_DOMAIN_NOSTATE;
184   }
185   return ret;
186 }
187
188 #ifdef HAVE_DOM_REASON
189 static int map_domain_event_detail_to_reason(int event, int detail) {
190   int ret;
191   switch (event) {
192   case VIR_DOMAIN_EVENT_STARTED:
193     switch (detail) {
194     case VIR_DOMAIN_EVENT_STARTED_BOOTED: /* Normal startup from boot */
195       ret = VIR_DOMAIN_RUNNING_BOOTED;
196       break;
197     case VIR_DOMAIN_EVENT_STARTED_MIGRATED: /* Incoming migration from another
198                                                host */
199       ret = VIR_DOMAIN_RUNNING_MIGRATED;
200       break;
201     case VIR_DOMAIN_EVENT_STARTED_RESTORED: /* Restored from a state file */
202       ret = VIR_DOMAIN_RUNNING_RESTORED;
203       break;
204     case VIR_DOMAIN_EVENT_STARTED_FROM_SNAPSHOT: /* Restored from snapshot */
205       ret = VIR_DOMAIN_RUNNING_FROM_SNAPSHOT;
206       break;
207 #ifdef HAVE_DOM_REASON_RUNNING_WAKEUP
208     case VIR_DOMAIN_EVENT_STARTED_WAKEUP: /* Started due to wakeup event */
209       ret = VIR_DOMAIN_RUNNING_WAKEUP;
210       break;
211 #endif
212     default:
213       ret = VIR_DOMAIN_RUNNING_UNKNOWN;
214     }
215     break;
216   case VIR_DOMAIN_EVENT_SUSPENDED:
217     switch (detail) {
218     case VIR_DOMAIN_EVENT_SUSPENDED_PAUSED: /* Normal suspend due to admin
219                                                pause */
220       ret = VIR_DOMAIN_PAUSED_USER;
221       break;
222     case VIR_DOMAIN_EVENT_SUSPENDED_MIGRATED: /* Suspended for offline
223                                                  migration */
224       ret = VIR_DOMAIN_PAUSED_MIGRATION;
225       break;
226     case VIR_DOMAIN_EVENT_SUSPENDED_IOERROR: /* Suspended due to a disk I/O
227                                                 error */
228       ret = VIR_DOMAIN_PAUSED_IOERROR;
229       break;
230     case VIR_DOMAIN_EVENT_SUSPENDED_WATCHDOG: /* Suspended due to a watchdog
231                                                  firing */
232       ret = VIR_DOMAIN_PAUSED_WATCHDOG;
233       break;
234     case VIR_DOMAIN_EVENT_SUSPENDED_RESTORED: /* Restored from paused state
235                                                  file */
236       ret = VIR_DOMAIN_PAUSED_UNKNOWN;
237       break;
238     case VIR_DOMAIN_EVENT_SUSPENDED_FROM_SNAPSHOT: /* Restored from paused
239                                                       snapshot */
240       ret = VIR_DOMAIN_PAUSED_FROM_SNAPSHOT;
241       break;
242     case VIR_DOMAIN_EVENT_SUSPENDED_API_ERROR: /* Suspended after failure during
243                                                   libvirt API call */
244       ret = VIR_DOMAIN_PAUSED_UNKNOWN;
245       break;
246 #ifdef HAVE_DOM_REASON_POSTCOPY
247     case VIR_DOMAIN_EVENT_SUSPENDED_POSTCOPY: /* Suspended for post-copy
248                                                  migration */
249       ret = VIR_DOMAIN_PAUSED_POSTCOPY;
250       break;
251     case VIR_DOMAIN_EVENT_SUSPENDED_POSTCOPY_FAILED: /* Suspended after failed
252                                                         post-copy */
253       ret = VIR_DOMAIN_PAUSED_POSTCOPY_FAILED;
254       break;
255 #endif
256     default:
257       ret = VIR_DOMAIN_PAUSED_UNKNOWN;
258     }
259     break;
260   case VIR_DOMAIN_EVENT_RESUMED:
261     switch (detail) {
262     case VIR_DOMAIN_EVENT_RESUMED_UNPAUSED: /* Normal resume due to admin
263                                                unpause */
264       ret = VIR_DOMAIN_RUNNING_UNPAUSED;
265       break;
266     case VIR_DOMAIN_EVENT_RESUMED_MIGRATED: /* Resumed for completion of
267                                                migration */
268       ret = VIR_DOMAIN_RUNNING_MIGRATED;
269       break;
270     case VIR_DOMAIN_EVENT_RESUMED_FROM_SNAPSHOT: /* Resumed from snapshot */
271       ret = VIR_DOMAIN_RUNNING_FROM_SNAPSHOT;
272       break;
273 #ifdef HAVE_DOM_REASON_POSTCOPY
274     case VIR_DOMAIN_EVENT_RESUMED_POSTCOPY: /* Resumed, but migration is still
275                                                running in post-copy mode */
276       ret = VIR_DOMAIN_RUNNING_POSTCOPY;
277       break;
278 #endif
279     default:
280       ret = VIR_DOMAIN_RUNNING_UNKNOWN;
281     }
282     break;
283   case VIR_DOMAIN_EVENT_STOPPED:
284     switch (detail) {
285     case VIR_DOMAIN_EVENT_STOPPED_SHUTDOWN: /* Normal shutdown */
286       ret = VIR_DOMAIN_SHUTOFF_SHUTDOWN;
287       break;
288     case VIR_DOMAIN_EVENT_STOPPED_DESTROYED: /* Forced poweroff from host */
289       ret = VIR_DOMAIN_SHUTOFF_DESTROYED;
290       break;
291     case VIR_DOMAIN_EVENT_STOPPED_CRASHED: /* Guest crashed */
292       ret = VIR_DOMAIN_SHUTOFF_CRASHED;
293       break;
294     case VIR_DOMAIN_EVENT_STOPPED_MIGRATED: /* Migrated off to another host */
295       ret = VIR_DOMAIN_SHUTOFF_MIGRATED;
296       break;
297     case VIR_DOMAIN_EVENT_STOPPED_SAVED: /* Saved to a state file */
298       ret = VIR_DOMAIN_SHUTOFF_SAVED;
299       break;
300     case VIR_DOMAIN_EVENT_STOPPED_FAILED: /* Host emulator/mgmt failed */
301       ret = VIR_DOMAIN_SHUTOFF_FAILED;
302       break;
303     case VIR_DOMAIN_EVENT_STOPPED_FROM_SNAPSHOT: /* Offline snapshot loaded */
304       ret = VIR_DOMAIN_SHUTOFF_FROM_SNAPSHOT;
305       break;
306     default:
307       ret = VIR_DOMAIN_SHUTOFF_UNKNOWN;
308     }
309     break;
310   case VIR_DOMAIN_EVENT_SHUTDOWN:
311     switch (detail) {
312     case VIR_DOMAIN_EVENT_SHUTDOWN_FINISHED: /* Guest finished shutdown
313                                                 sequence */
314       ret = VIR_DOMAIN_SHUTDOWN_USER;
315       break;
316     default:
317       ret = VIR_DOMAIN_SHUTDOWN_UNKNOWN;
318     }
319     break;
320 #ifdef HAVE_DOM_STATE_PMSUSPENDED
321   case VIR_DOMAIN_EVENT_PMSUSPENDED:
322     switch (detail) {
323     case VIR_DOMAIN_EVENT_PMSUSPENDED_MEMORY: /* Guest was PM suspended to
324                                                  memory */
325       ret = VIR_DOMAIN_PMSUSPENDED_UNKNOWN;
326       break;
327     case VIR_DOMAIN_EVENT_PMSUSPENDED_DISK: /* Guest was PM suspended to disk */
328       ret = VIR_DOMAIN_PMSUSPENDED_DISK_UNKNOWN;
329       break;
330     default:
331       ret = VIR_DOMAIN_PMSUSPENDED_UNKNOWN;
332     }
333     break;
334 #endif
335   case VIR_DOMAIN_EVENT_CRASHED:
336     switch (detail) {
337     case VIR_DOMAIN_EVENT_CRASHED_PANICKED: /* Guest was panicked */
338       ret = VIR_DOMAIN_CRASHED_PANICKED;
339       break;
340     default:
341       ret = VIR_DOMAIN_CRASHED_UNKNOWN;
342     }
343     break;
344   default:
345     ret = VIR_DOMAIN_NOSTATE_UNKNOWN;
346   }
347   return ret;
348 }
349
350 #define DOMAIN_STATE_REASON_MAX_SIZE 20
351 const char *domain_reasons[][DOMAIN_STATE_REASON_MAX_SIZE] = {
352         [VIR_DOMAIN_NOSTATE][VIR_DOMAIN_NOSTATE_UNKNOWN] =
353             "the reason is unknown",
354
355         [VIR_DOMAIN_RUNNING][VIR_DOMAIN_RUNNING_UNKNOWN] =
356             "the reason is unknown",
357         [VIR_DOMAIN_RUNNING][VIR_DOMAIN_RUNNING_BOOTED] =
358             "normal startup from boot",
359         [VIR_DOMAIN_RUNNING][VIR_DOMAIN_RUNNING_MIGRATED] =
360             "migrated from another host",
361         [VIR_DOMAIN_RUNNING][VIR_DOMAIN_RUNNING_RESTORED] =
362             "restored from a state file",
363         [VIR_DOMAIN_RUNNING][VIR_DOMAIN_RUNNING_FROM_SNAPSHOT] =
364             "restored from snapshot",
365         [VIR_DOMAIN_RUNNING][VIR_DOMAIN_RUNNING_UNPAUSED] =
366             "returned from paused state",
367         [VIR_DOMAIN_RUNNING][VIR_DOMAIN_RUNNING_MIGRATION_CANCELED] =
368             "returned from migration",
369         [VIR_DOMAIN_RUNNING][VIR_DOMAIN_RUNNING_SAVE_CANCELED] =
370             "returned from failed save process",
371 #ifdef HAVE_DOM_REASON_RUNNING_WAKEUP
372         [VIR_DOMAIN_RUNNING][VIR_DOMAIN_RUNNING_WAKEUP] =
373             "returned from pmsuspended due to wakeup event",
374 #endif
375 #ifdef HAVE_DOM_REASON_CRASHED
376         [VIR_DOMAIN_RUNNING][VIR_DOMAIN_RUNNING_CRASHED] =
377             "resumed from crashed",
378 #endif
379 #ifdef HAVE_DOM_REASON_POSTCOPY
380         [VIR_DOMAIN_RUNNING][VIR_DOMAIN_RUNNING_POSTCOPY] =
381             "running in post-copy migration mode",
382 #endif
383         [VIR_DOMAIN_BLOCKED][VIR_DOMAIN_BLOCKED_UNKNOWN] =
384             "the reason is unknown",
385
386         [VIR_DOMAIN_PAUSED][VIR_DOMAIN_PAUSED_UNKNOWN] =
387             "the reason is unknown",
388         [VIR_DOMAIN_PAUSED][VIR_DOMAIN_PAUSED_USER] = "paused on user request",
389         [VIR_DOMAIN_PAUSED][VIR_DOMAIN_PAUSED_MIGRATION] =
390             "paused for offline migration",
391         [VIR_DOMAIN_PAUSED][VIR_DOMAIN_PAUSED_SAVE] = "paused for save",
392         [VIR_DOMAIN_PAUSED][VIR_DOMAIN_PAUSED_DUMP] =
393             "paused for offline core dump",
394         [VIR_DOMAIN_PAUSED][VIR_DOMAIN_PAUSED_IOERROR] =
395             "paused due to a disk I/O error",
396         [VIR_DOMAIN_PAUSED][VIR_DOMAIN_PAUSED_WATCHDOG] =
397             "paused due to a watchdog event",
398         [VIR_DOMAIN_PAUSED][VIR_DOMAIN_PAUSED_FROM_SNAPSHOT] =
399             "paused after restoring from snapshot",
400 #ifdef HAVE_DOM_REASON_PAUSED_SHUTTING_DOWN
401         [VIR_DOMAIN_PAUSED][VIR_DOMAIN_PAUSED_SHUTTING_DOWN] =
402             "paused during shutdown process",
403 #endif
404 #ifdef HAVE_DOM_REASON_PAUSED_SNAPSHOT
405         [VIR_DOMAIN_PAUSED][VIR_DOMAIN_PAUSED_SNAPSHOT] =
406             "paused while creating a snapshot",
407 #endif
408 #ifdef HAVE_DOM_REASON_PAUSED_CRASHED
409         [VIR_DOMAIN_PAUSED][VIR_DOMAIN_PAUSED_CRASHED] =
410             "paused due to a guest crash",
411 #endif
412 #ifdef HAVE_DOM_REASON_PAUSED_STARTING_UP
413         [VIR_DOMAIN_PAUSED][VIR_DOMAIN_PAUSED_STARTING_UP] =
414             "the domain is being started",
415 #endif
416 #ifdef HAVE_DOM_REASON_POSTCOPY
417         [VIR_DOMAIN_PAUSED][VIR_DOMAIN_PAUSED_POSTCOPY] =
418             "paused for post-copy migration",
419         [VIR_DOMAIN_PAUSED][VIR_DOMAIN_PAUSED_POSTCOPY_FAILED] =
420             "paused after failed post-copy",
421 #endif
422         [VIR_DOMAIN_SHUTDOWN][VIR_DOMAIN_SHUTDOWN_UNKNOWN] =
423             "the reason is unknown",
424         [VIR_DOMAIN_SHUTDOWN][VIR_DOMAIN_SHUTDOWN_USER] =
425             "shutting down on user request",
426
427         [VIR_DOMAIN_SHUTOFF][VIR_DOMAIN_SHUTOFF_UNKNOWN] =
428             "the reason is unknown",
429         [VIR_DOMAIN_SHUTOFF][VIR_DOMAIN_SHUTOFF_SHUTDOWN] = "normal shutdown",
430         [VIR_DOMAIN_SHUTOFF][VIR_DOMAIN_SHUTOFF_DESTROYED] = "forced poweroff",
431         [VIR_DOMAIN_SHUTOFF][VIR_DOMAIN_SHUTOFF_CRASHED] = "domain crashed",
432         [VIR_DOMAIN_SHUTOFF][VIR_DOMAIN_SHUTOFF_MIGRATED] =
433             "migrated to another host",
434         [VIR_DOMAIN_SHUTOFF][VIR_DOMAIN_SHUTOFF_SAVED] = "saved to a file",
435         [VIR_DOMAIN_SHUTOFF][VIR_DOMAIN_SHUTOFF_FAILED] =
436             "domain failed to start",
437         [VIR_DOMAIN_SHUTOFF][VIR_DOMAIN_SHUTOFF_FROM_SNAPSHOT] =
438             "restored from a snapshot which was taken while domain was shutoff",
439
440         [VIR_DOMAIN_CRASHED][VIR_DOMAIN_CRASHED_UNKNOWN] =
441             "the reason is unknown",
442 #ifdef VIR_DOMAIN_CRASHED_PANICKED
443         [VIR_DOMAIN_CRASHED][VIR_DOMAIN_CRASHED_PANICKED] = "domain panicked",
444 #endif
445
446 #ifdef HAVE_DOM_STATE_PMSUSPENDED
447         [VIR_DOMAIN_PMSUSPENDED][VIR_DOMAIN_PMSUSPENDED_UNKNOWN] =
448             "the reason is unknown",
449 #endif
450 };
451 #endif /* HAVE_DOM_REASON */
452
453 #define NR_CONFIG_KEYS ((sizeof config_keys / sizeof config_keys[0]) - 1)
454 #define NANOSEC_IN_SEC 1e9
455
456 #define GET_STATS(_f, _name, ...)                                              \
457   do {                                                                         \
458     status = _f(__VA_ARGS__);                                                  \
459     if (status != 0)                                                           \
460       ERROR(PLUGIN_NAME ": Failed to get " _name);                             \
461   } while (0)
462
463 /* Connection. */
464 static virConnectPtr conn = 0;
465 static char *conn_string = NULL;
466 static c_complain_t conn_complain = C_COMPLAIN_INIT_STATIC;
467
468 /* Node information required for %CPU */
469 static virNodeInfo nodeinfo;
470
471 /* Seconds between list refreshes, 0 disables completely. */
472 static int interval = 60;
473
474 /* List of domains, if specified. */
475 static ignorelist_t *il_domains = NULL;
476 /* List of block devices, if specified. */
477 static ignorelist_t *il_block_devices = NULL;
478 /* List of network interface devices, if specified. */
479 static ignorelist_t *il_interface_devices = NULL;
480
481 static int ignore_device_match(ignorelist_t *, const char *domname,
482                                const char *devpath);
483
484 /* Actual list of block devices found on last refresh. */
485 struct block_device {
486   virDomainPtr dom; /* domain */
487   char *path;       /* name of block device */
488 };
489
490 /* Actual list of network interfaces found on last refresh. */
491 struct interface_device {
492   virDomainPtr dom; /* domain */
493   char *path;       /* name of interface device */
494   char *address;    /* mac address of interface device */
495   char *number;     /* interface device number */
496 };
497
498 typedef struct domain_s {
499   virDomainPtr ptr;
500   virDomainInfo info;
501   _Bool active;
502 } domain_t;
503
504 struct lv_read_state {
505   /* Actual list of domains found on last refresh. */
506   domain_t *domains;
507   int nr_domains;
508
509   struct block_device *block_devices;
510   int nr_block_devices;
511
512   struct interface_device *interface_devices;
513   int nr_interface_devices;
514 };
515
516 static void free_domains(struct lv_read_state *state);
517 static int add_domain(struct lv_read_state *state, virDomainPtr dom,
518                       _Bool active);
519
520 static void free_block_devices(struct lv_read_state *state);
521 static int add_block_device(struct lv_read_state *state, virDomainPtr dom,
522                             const char *path);
523
524 static void free_interface_devices(struct lv_read_state *state);
525 static int add_interface_device(struct lv_read_state *state, virDomainPtr dom,
526                                 const char *path, const char *address,
527                                 unsigned int number);
528
529 #define METADATA_VM_PARTITION_URI "http://ovirt.org/ovirtmap/tag/1.0"
530 #define METADATA_VM_PARTITION_ELEMENT "tag"
531 #define METADATA_VM_PARTITION_PREFIX "ovirtmap"
532
533 #define BUFFER_MAX_LEN 256
534 #define PARTITION_TAG_MAX_LEN 32
535
536 struct lv_read_instance {
537   struct lv_read_state read_state;
538   char tag[PARTITION_TAG_MAX_LEN];
539   size_t id;
540 };
541
542 struct lv_user_data {
543   struct lv_read_instance inst;
544   user_data_t ud;
545 };
546
547 #define NR_INSTANCES_DEFAULT 1
548 #define NR_INSTANCES_MAX 128
549 static int nr_instances = NR_INSTANCES_DEFAULT;
550 static struct lv_user_data lv_read_user_data[NR_INSTANCES_MAX];
551
552 /* HostnameFormat. */
553 #define HF_MAX_FIELDS 3
554
555 enum hf_field { hf_none = 0, hf_hostname, hf_name, hf_uuid };
556
557 static enum hf_field hostname_format[HF_MAX_FIELDS] = {hf_name};
558
559 /* PluginInstanceFormat */
560 #define PLGINST_MAX_FIELDS 2
561
562 enum plginst_field { plginst_none = 0, plginst_name, plginst_uuid };
563
564 static enum plginst_field plugin_instance_format[PLGINST_MAX_FIELDS] = {
565     plginst_none};
566
567 /* BlockDeviceFormat */
568 enum bd_field { target, source };
569
570 /* InterfaceFormat. */
571 enum if_field { if_address, if_name, if_number };
572
573 /* ExtraStats */
574 #define EX_STATS_MAX_FIELDS 15
575 enum ex_stats {
576   ex_stats_none = 0,
577   ex_stats_disk = 1 << 0,
578   ex_stats_pcpu = 1 << 1,
579   ex_stats_cpu_util = 1 << 2,
580   ex_stats_domain_state = 1 << 3,
581 #ifdef HAVE_PERF_STATS
582   ex_stats_perf = 1 << 4,
583 #endif
584   ex_stats_vcpupin = 1 << 5,
585 #ifdef HAVE_DISK_ERR
586   ex_stats_disk_err = 1 << 6,
587 #endif
588 #ifdef HAVE_FS_INFO
589   ex_stats_fs_info = 1 << 7,
590 #endif
591 #ifdef HAVE_JOB_STATS
592   ex_stats_job_stats_completed = 1 << 8,
593   ex_stats_job_stats_background = 1 << 9,
594 #endif
595 };
596
597 static unsigned int extra_stats = ex_stats_none;
598
599 struct ex_stats_item {
600   const char *name;
601   enum ex_stats flag;
602 };
603 static const struct ex_stats_item ex_stats_table[] = {
604     {"disk", ex_stats_disk},
605     {"pcpu", ex_stats_pcpu},
606     {"cpu_util", ex_stats_cpu_util},
607     {"domain_state", ex_stats_domain_state},
608 #ifdef HAVE_PERF_STATS
609     {"perf", ex_stats_perf},
610 #endif
611     {"vcpupin", ex_stats_vcpupin},
612 #ifdef HAVE_DISK_ERR
613     {"disk_err", ex_stats_disk_err},
614 #endif
615 #ifdef HAVE_FS_INFO
616     {"fs_info", ex_stats_fs_info},
617 #endif
618 #ifdef HAVE_JOB_STATS
619     {"job_stats_completed", ex_stats_job_stats_completed},
620     {"job_stats_background", ex_stats_job_stats_background},
621 #endif
622     {NULL, ex_stats_none},
623 };
624
625 /* BlockDeviceFormatBasename */
626 _Bool blockdevice_format_basename = 0;
627 static enum bd_field blockdevice_format = target;
628 static enum if_field interface_format = if_name;
629
630 /* Time that we last refreshed. */
631 static time_t last_refresh = (time_t)0;
632
633 static int refresh_lists(struct lv_read_instance *inst);
634
635 struct lv_info {
636   virDomainInfo di;
637   unsigned long long total_user_cpu_time;
638   unsigned long long total_syst_cpu_time;
639 };
640
641 struct lv_block_info {
642   virDomainBlockStatsStruct bi;
643
644   long long rd_total_times;
645   long long wr_total_times;
646
647   long long fl_req;
648   long long fl_total_times;
649 };
650
651 static void init_block_info(struct lv_block_info *binfo) {
652   if (binfo == NULL)
653     return;
654
655   binfo->bi.rd_req = -1;
656   binfo->bi.wr_req = -1;
657   binfo->bi.rd_bytes = -1;
658   binfo->bi.wr_bytes = -1;
659
660   binfo->rd_total_times = -1;
661   binfo->wr_total_times = -1;
662   binfo->fl_req = -1;
663   binfo->fl_total_times = -1;
664 }
665
666 #ifdef HAVE_BLOCK_STATS_FLAGS
667
668 #define GET_BLOCK_INFO_VALUE(NAME, FIELD)                                      \
669   if (!strcmp(param[i].field, NAME)) {                                         \
670     binfo->FIELD = param[i].value.l;                                           \
671     continue;                                                                  \
672   }
673
674 static int get_block_info(struct lv_block_info *binfo,
675                           virTypedParameterPtr param, int nparams) {
676   if (binfo == NULL || param == NULL)
677     return -1;
678
679   for (int i = 0; i < nparams; ++i) {
680     /* ignore type. Everything must be LLONG anyway. */
681     GET_BLOCK_INFO_VALUE("rd_operations", bi.rd_req);
682     GET_BLOCK_INFO_VALUE("wr_operations", bi.wr_req);
683     GET_BLOCK_INFO_VALUE("rd_bytes", bi.rd_bytes);
684     GET_BLOCK_INFO_VALUE("wr_bytes", bi.wr_bytes);
685     GET_BLOCK_INFO_VALUE("rd_total_times", rd_total_times);
686     GET_BLOCK_INFO_VALUE("wr_total_times", wr_total_times);
687     GET_BLOCK_INFO_VALUE("flush_operations", fl_req);
688     GET_BLOCK_INFO_VALUE("flush_total_times", fl_total_times);
689   }
690
691   return 0;
692 }
693
694 #undef GET_BLOCK_INFO_VALUE
695
696 #endif /* HAVE_BLOCK_STATS_FLAGS */
697
698 /* ERROR(...) macro for virterrors. */
699 #define VIRT_ERROR(conn, s)                                                    \
700   do {                                                                         \
701     virErrorPtr err;                                                           \
702     err = (conn) ? virConnGetLastError((conn)) : virGetLastError();            \
703     if (err)                                                                   \
704       ERROR("%s: %s", (s), err->message);                                      \
705   } while (0)
706
707 static void init_lv_info(struct lv_info *info) {
708   if (info != NULL)
709     memset(info, 0, sizeof(*info));
710 }
711
712 static int lv_domain_info(virDomainPtr dom, struct lv_info *info) {
713 #ifdef HAVE_CPU_STATS
714   virTypedParameterPtr param = NULL;
715   int nparams = 0;
716 #endif /* HAVE_CPU_STATS */
717   int ret = virDomainGetInfo(dom, &(info->di));
718   if (ret != 0) {
719     return ret;
720   }
721
722 #ifdef HAVE_CPU_STATS
723   nparams = virDomainGetCPUStats(dom, NULL, 0, -1, 1, 0);
724   if (nparams < 0) {
725     VIRT_ERROR(conn, "getting the CPU params count");
726     return -1;
727   }
728
729   param = calloc(nparams, sizeof(virTypedParameter));
730   if (param == NULL) {
731     ERROR("virt plugin: alloc(%i) for cpu parameters failed.", nparams);
732     return -1;
733   }
734
735   ret = virDomainGetCPUStats(dom, param, nparams, -1, 1, 0); // total stats.
736   if (ret < 0) {
737     virTypedParamsClear(param, nparams);
738     sfree(param);
739     VIRT_ERROR(conn, "getting the disk params values");
740     return -1;
741   }
742
743   for (int i = 0; i < nparams; ++i) {
744     if (!strcmp(param[i].field, "user_time"))
745       info->total_user_cpu_time = param[i].value.ul;
746     else if (!strcmp(param[i].field, "system_time"))
747       info->total_syst_cpu_time = param[i].value.ul;
748   }
749
750   virTypedParamsClear(param, nparams);
751   sfree(param);
752 #endif /* HAVE_CPU_STATS */
753
754   return 0;
755 }
756
757 static void init_value_list(value_list_t *vl, virDomainPtr dom) {
758   const char *name;
759   char uuid[VIR_UUID_STRING_BUFLEN];
760
761   sstrncpy(vl->plugin, PLUGIN_NAME, sizeof(vl->plugin));
762
763   vl->host[0] = '\0';
764
765   /* Construct the hostname field according to HostnameFormat. */
766   for (int i = 0; i < HF_MAX_FIELDS; ++i) {
767     if (hostname_format[i] == hf_none)
768       continue;
769
770     if (i > 0)
771       SSTRNCAT(vl->host, ":", sizeof(vl->host));
772
773     switch (hostname_format[i]) {
774     case hf_none:
775       break;
776     case hf_hostname:
777       SSTRNCAT(vl->host, hostname_g, sizeof(vl->host));
778       break;
779     case hf_name:
780       name = virDomainGetName(dom);
781       if (name)
782         SSTRNCAT(vl->host, name, sizeof(vl->host));
783       break;
784     case hf_uuid:
785       if (virDomainGetUUIDString(dom, uuid) == 0)
786         SSTRNCAT(vl->host, uuid, sizeof(vl->host));
787       break;
788     }
789   }
790
791   /* Construct the plugin instance field according to PluginInstanceFormat. */
792   for (int i = 0; i < PLGINST_MAX_FIELDS; ++i) {
793     if (plugin_instance_format[i] == plginst_none)
794       continue;
795
796     if (i > 0)
797       SSTRNCAT(vl->plugin_instance, ":", sizeof(vl->plugin_instance));
798
799     switch (plugin_instance_format[i]) {
800     case plginst_none:
801       break;
802     case plginst_name:
803       name = virDomainGetName(dom);
804       if (name)
805         SSTRNCAT(vl->plugin_instance, name, sizeof(vl->plugin_instance));
806       break;
807     case plginst_uuid:
808       if (virDomainGetUUIDString(dom, uuid) == 0)
809         SSTRNCAT(vl->plugin_instance, uuid, sizeof(vl->plugin_instance));
810       break;
811     }
812   }
813
814 } /* void init_value_list */
815
816 static int init_notif(notification_t *notif, const virDomainPtr domain,
817                       int severity, const char *msg, const char *type,
818                       const char *type_instance) {
819   value_list_t vl = VALUE_LIST_INIT;
820
821   if (!notif) {
822     ERROR(PLUGIN_NAME ": init_notif: NULL pointer");
823     return -1;
824   }
825
826   init_value_list(&vl, domain);
827   notification_init(notif, severity, msg, vl.host, vl.plugin,
828                     vl.plugin_instance, type, type_instance);
829   notif->time = cdtime();
830   return 0;
831 }
832
833 static void submit_notif(const virDomainPtr domain, int severity,
834                          const char *msg, const char *type,
835                          const char *type_instance) {
836   notification_t notif;
837
838   init_notif(&notif, domain, severity, msg, type, type_instance);
839   plugin_dispatch_notification(&notif);
840   if (notif.meta)
841     plugin_notification_meta_free(notif.meta);
842 }
843
844 static void submit(virDomainPtr dom, char const *type,
845                    char const *type_instance, value_t *values,
846                    size_t values_len) {
847   value_list_t vl = VALUE_LIST_INIT;
848   init_value_list(&vl, dom);
849
850   vl.values = values;
851   vl.values_len = values_len;
852
853   sstrncpy(vl.type, type, sizeof(vl.type));
854   if (type_instance != NULL)
855     sstrncpy(vl.type_instance, type_instance, sizeof(vl.type_instance));
856
857   plugin_dispatch_values(&vl);
858 }
859
860 static void memory_submit(virDomainPtr dom, gauge_t value) {
861   submit(dom, "memory", "total", &(value_t){.gauge = value}, 1);
862 }
863
864 static void memory_stats_submit(gauge_t value, virDomainPtr dom,
865                                 int tag_index) {
866   static const char *tags[] = {"swap_in",        "swap_out", "major_fault",
867                                "minor_fault",    "unused",   "available",
868                                "actual_balloon", "rss",      "usable",
869                                "last_update"};
870
871   if ((tag_index < 0) || (tag_index >= (int)STATIC_ARRAY_SIZE(tags))) {
872     ERROR("virt plugin: Array index out of bounds: tag_index = %d", tag_index);
873     return;
874   }
875
876   submit(dom, "memory", tags[tag_index], &(value_t){.gauge = value}, 1);
877 }
878
879 static void submit_derive2(const char *type, derive_t v0, derive_t v1,
880                            virDomainPtr dom, const char *devname) {
881   value_t values[] = {
882       {.derive = v0}, {.derive = v1},
883   };
884
885   submit(dom, type, devname, values, STATIC_ARRAY_SIZE(values));
886 } /* void submit_derive2 */
887
888 static void pcpu_submit(virDomainPtr dom, struct lv_info *info) {
889 #ifdef HAVE_CPU_STATS
890   if (extra_stats & ex_stats_pcpu)
891     submit_derive2("ps_cputime", info->total_user_cpu_time,
892                    info->total_syst_cpu_time, dom, NULL);
893 #endif /* HAVE_CPU_STATS */
894 }
895
896 static double cpu_ns_to_percent(unsigned int node_cpus,
897                                 unsigned long long cpu_time_old,
898                                 unsigned long long cpu_time_new) {
899   double percent = 0.0;
900   unsigned long long cpu_time_diff = 0;
901   double time_diff_sec = CDTIME_T_TO_DOUBLE(plugin_get_interval());
902
903   if (node_cpus != 0 && time_diff_sec != 0 && cpu_time_old != 0) {
904     cpu_time_diff = cpu_time_new - cpu_time_old;
905     percent = ((double)(100 * cpu_time_diff)) /
906               (time_diff_sec * node_cpus * NANOSEC_IN_SEC);
907   }
908
909   DEBUG(PLUGIN_NAME ": node_cpus=%u cpu_time_old=%" PRIu64
910                     " cpu_time_new=%" PRIu64 "cpu_time_diff=%" PRIu64
911                     " time_diff_sec=%f percent=%f",
912         node_cpus, (uint64_t)cpu_time_old, (uint64_t)cpu_time_new,
913         (uint64_t)cpu_time_diff, time_diff_sec, percent);
914
915   return percent;
916 }
917
918 static void cpu_submit(const domain_t *dom, unsigned long long cpuTime_new) {
919
920   if (!dom)
921     return;
922
923   if (extra_stats & ex_stats_cpu_util) {
924     /* Computing %CPU requires 2 samples of cpuTime */
925     if (dom->info.cpuTime != 0 && cpuTime_new != 0) {
926
927       submit(dom->ptr, "percent", "virt_cpu_total",
928              &(value_t){.gauge = cpu_ns_to_percent(
929                             nodeinfo.cpus, dom->info.cpuTime, cpuTime_new)},
930              1);
931     }
932   }
933
934   submit(dom->ptr, "virt_cpu_total", NULL, &(value_t){.derive = cpuTime_new},
935          1);
936 }
937
938 static void vcpu_submit(derive_t value, virDomainPtr dom, int vcpu_nr,
939                         const char *type) {
940   char type_instance[DATA_MAX_NAME_LEN];
941
942   snprintf(type_instance, sizeof(type_instance), "%d", vcpu_nr);
943   submit(dom, type, type_instance, &(value_t){.derive = value}, 1);
944 }
945
946 static void disk_submit(struct lv_block_info *binfo, virDomainPtr dom,
947                         const char *dev) {
948   char *dev_copy = strdup(dev);
949   const char *type_instance = dev_copy;
950
951   if (!dev_copy)
952     return;
953
954   if (blockdevice_format_basename && blockdevice_format == source)
955     type_instance = basename(dev_copy);
956
957   if (!type_instance) {
958     sfree(dev_copy);
959     return;
960   }
961
962   char flush_type_instance[DATA_MAX_NAME_LEN];
963   snprintf(flush_type_instance, sizeof(flush_type_instance), "flush-%s",
964            type_instance);
965
966   if ((binfo->bi.rd_req != -1) && (binfo->bi.wr_req != -1))
967     submit_derive2("disk_ops", (derive_t)binfo->bi.rd_req,
968                    (derive_t)binfo->bi.wr_req, dom, type_instance);
969
970   if ((binfo->bi.rd_bytes != -1) && (binfo->bi.wr_bytes != -1))
971     submit_derive2("disk_octets", (derive_t)binfo->bi.rd_bytes,
972                    (derive_t)binfo->bi.wr_bytes, dom, type_instance);
973
974   if (extra_stats & ex_stats_disk) {
975     if ((binfo->rd_total_times != -1) && (binfo->wr_total_times != -1))
976       submit_derive2("disk_time", (derive_t)binfo->rd_total_times,
977                      (derive_t)binfo->wr_total_times, dom, type_instance);
978
979     if (binfo->fl_req != -1)
980       submit(dom, "total_requests", flush_type_instance,
981              &(value_t){.derive = (derive_t)binfo->fl_req}, 1);
982     if (binfo->fl_total_times != -1) {
983       derive_t value = binfo->fl_total_times / 1000; // ns -> ms
984       submit(dom, "total_time_in_ms", flush_type_instance,
985              &(value_t){.derive = value}, 1);
986     }
987   }
988
989   sfree(dev_copy);
990 }
991
992 static unsigned int parse_ex_stats_flags(char **exstats, int numexstats) {
993   unsigned int ex_stats_flags = ex_stats_none;
994   for (int i = 0; i < numexstats; i++) {
995     for (int j = 0; ex_stats_table[j].name != NULL; j++) {
996       if (strcasecmp(exstats[i], ex_stats_table[j].name) == 0) {
997         DEBUG(PLUGIN_NAME " plugin: enabling extra stats for '%s'",
998               ex_stats_table[j].name);
999         ex_stats_flags |= ex_stats_table[j].flag;
1000         break;
1001       }
1002
1003       if (ex_stats_table[j + 1].name == NULL) {
1004         ERROR(PLUGIN_NAME ": Unmatched ExtraStats option: %s", exstats[i]);
1005       }
1006     }
1007   }
1008   return ex_stats_flags;
1009 }
1010
1011 static void domain_state_submit_notif(virDomainPtr dom, int state, int reason) {
1012   if ((state < 0) || (state >= STATIC_ARRAY_SIZE(domain_states))) {
1013     ERROR(PLUGIN_NAME ": Array index out of bounds: state=%d", state);
1014     return;
1015   }
1016
1017   char msg[DATA_MAX_NAME_LEN];
1018   const char *state_str = domain_states[state];
1019 #ifdef HAVE_DOM_REASON
1020   if ((reason < 0) || (reason >= STATIC_ARRAY_SIZE(domain_reasons[0]))) {
1021     ERROR(PLUGIN_NAME ": Array index out of bounds: reason=%d", reason);
1022     return;
1023   }
1024
1025   const char *reason_str = domain_reasons[state][reason];
1026   /* Array size for domain reasons is fixed, but different domain states can
1027    * have different number of reasons. We need to check if reason was
1028    * successfully parsed */
1029   if (!reason_str) {
1030     ERROR(PLUGIN_NAME ": Invalid reason (%d) for domain state: %s", reason,
1031           state_str);
1032     return;
1033   }
1034 #else
1035   const char *reason_str = "N/A";
1036 #endif
1037
1038   snprintf(msg, sizeof(msg), "Domain state: %s. Reason: %s", state_str,
1039            reason_str);
1040
1041   int severity;
1042   switch (state) {
1043   case VIR_DOMAIN_NOSTATE:
1044   case VIR_DOMAIN_RUNNING:
1045   case VIR_DOMAIN_SHUTDOWN:
1046   case VIR_DOMAIN_SHUTOFF:
1047     severity = NOTIF_OKAY;
1048     break;
1049   case VIR_DOMAIN_BLOCKED:
1050   case VIR_DOMAIN_PAUSED:
1051 #ifdef DOM_STATE_PMSUSPENDED
1052   case VIR_DOMAIN_PMSUSPENDED:
1053 #endif
1054     severity = NOTIF_WARNING;
1055     break;
1056   case VIR_DOMAIN_CRASHED:
1057     severity = NOTIF_FAILURE;
1058     break;
1059   default:
1060     ERROR(PLUGIN_NAME ": Unrecognized domain state (%d)", state);
1061     return;
1062   }
1063   submit_notif(dom, severity, msg, "domain_state", NULL);
1064 }
1065
1066 static int lv_config(const char *key, const char *value) {
1067   if (virInitialize() != 0)
1068     return 1;
1069
1070   if (il_domains == NULL)
1071     il_domains = ignorelist_create(1);
1072   if (il_block_devices == NULL)
1073     il_block_devices = ignorelist_create(1);
1074   if (il_interface_devices == NULL)
1075     il_interface_devices = ignorelist_create(1);
1076
1077   if (strcasecmp(key, "Connection") == 0) {
1078     char *tmp = strdup(value);
1079     if (tmp == NULL) {
1080       ERROR(PLUGIN_NAME " plugin: Connection strdup failed.");
1081       return 1;
1082     }
1083     sfree(conn_string);
1084     conn_string = tmp;
1085     return 0;
1086   }
1087
1088   if (strcasecmp(key, "RefreshInterval") == 0) {
1089     char *eptr = NULL;
1090     interval = strtol(value, &eptr, 10);
1091     if (eptr == NULL || *eptr != '\0')
1092       return 1;
1093     return 0;
1094   }
1095
1096   if (strcasecmp(key, "Domain") == 0) {
1097     if (ignorelist_add(il_domains, value))
1098       return 1;
1099     return 0;
1100   }
1101   if (strcasecmp(key, "BlockDevice") == 0) {
1102     if (ignorelist_add(il_block_devices, value))
1103       return 1;
1104     return 0;
1105   }
1106
1107   if (strcasecmp(key, "BlockDeviceFormat") == 0) {
1108     if (strcasecmp(value, "target") == 0)
1109       blockdevice_format = target;
1110     else if (strcasecmp(value, "source") == 0)
1111       blockdevice_format = source;
1112     else {
1113       ERROR(PLUGIN_NAME " plugin: unknown BlockDeviceFormat: %s", value);
1114       return -1;
1115     }
1116     return 0;
1117   }
1118   if (strcasecmp(key, "BlockDeviceFormatBasename") == 0) {
1119     blockdevice_format_basename = IS_TRUE(value);
1120     return 0;
1121   }
1122   if (strcasecmp(key, "InterfaceDevice") == 0) {
1123     if (ignorelist_add(il_interface_devices, value))
1124       return 1;
1125     return 0;
1126   }
1127
1128   if (strcasecmp(key, "IgnoreSelected") == 0) {
1129     if (IS_TRUE(value)) {
1130       ignorelist_set_invert(il_domains, 0);
1131       ignorelist_set_invert(il_block_devices, 0);
1132       ignorelist_set_invert(il_interface_devices, 0);
1133     } else {
1134       ignorelist_set_invert(il_domains, 1);
1135       ignorelist_set_invert(il_block_devices, 1);
1136       ignorelist_set_invert(il_interface_devices, 1);
1137     }
1138     return 0;
1139   }
1140
1141   if (strcasecmp(key, "HostnameFormat") == 0) {
1142     char *value_copy;
1143     char *fields[HF_MAX_FIELDS];
1144     int n;
1145
1146     value_copy = strdup(value);
1147     if (value_copy == NULL) {
1148       ERROR(PLUGIN_NAME " plugin: strdup failed.");
1149       return -1;
1150     }
1151
1152     n = strsplit(value_copy, fields, HF_MAX_FIELDS);
1153     if (n < 1) {
1154       sfree(value_copy);
1155       ERROR(PLUGIN_NAME " plugin: HostnameFormat: no fields");
1156       return -1;
1157     }
1158
1159     for (int i = 0; i < n; ++i) {
1160       if (strcasecmp(fields[i], "hostname") == 0)
1161         hostname_format[i] = hf_hostname;
1162       else if (strcasecmp(fields[i], "name") == 0)
1163         hostname_format[i] = hf_name;
1164       else if (strcasecmp(fields[i], "uuid") == 0)
1165         hostname_format[i] = hf_uuid;
1166       else {
1167         ERROR(PLUGIN_NAME " plugin: unknown HostnameFormat field: %s",
1168               fields[i]);
1169         sfree(value_copy);
1170         return -1;
1171       }
1172     }
1173     sfree(value_copy);
1174
1175     for (int i = n; i < HF_MAX_FIELDS; ++i)
1176       hostname_format[i] = hf_none;
1177
1178     return 0;
1179   }
1180
1181   if (strcasecmp(key, "PluginInstanceFormat") == 0) {
1182     char *value_copy;
1183     char *fields[PLGINST_MAX_FIELDS];
1184     int n;
1185
1186     value_copy = strdup(value);
1187     if (value_copy == NULL) {
1188       ERROR(PLUGIN_NAME " plugin: strdup failed.");
1189       return -1;
1190     }
1191
1192     n = strsplit(value_copy, fields, PLGINST_MAX_FIELDS);
1193     if (n < 1) {
1194       sfree(value_copy);
1195       ERROR(PLUGIN_NAME " plugin: PluginInstanceFormat: no fields");
1196       return -1;
1197     }
1198
1199     for (int i = 0; i < n; ++i) {
1200       if (strcasecmp(fields[i], "none") == 0) {
1201         plugin_instance_format[i] = plginst_none;
1202         break;
1203       } else if (strcasecmp(fields[i], "name") == 0)
1204         plugin_instance_format[i] = plginst_name;
1205       else if (strcasecmp(fields[i], "uuid") == 0)
1206         plugin_instance_format[i] = plginst_uuid;
1207       else {
1208         ERROR(PLUGIN_NAME " plugin: unknown PluginInstanceFormat field: %s",
1209               fields[i]);
1210         sfree(value_copy);
1211         return -1;
1212       }
1213     }
1214     sfree(value_copy);
1215
1216     for (int i = n; i < PLGINST_MAX_FIELDS; ++i)
1217       plugin_instance_format[i] = plginst_none;
1218
1219     return 0;
1220   }
1221
1222   if (strcasecmp(key, "InterfaceFormat") == 0) {
1223     if (strcasecmp(value, "name") == 0)
1224       interface_format = if_name;
1225     else if (strcasecmp(value, "address") == 0)
1226       interface_format = if_address;
1227     else if (strcasecmp(value, "number") == 0)
1228       interface_format = if_number;
1229     else {
1230       ERROR(PLUGIN_NAME " plugin: unknown InterfaceFormat: %s", value);
1231       return -1;
1232     }
1233     return 0;
1234   }
1235
1236   if (strcasecmp(key, "Instances") == 0) {
1237     char *eptr = NULL;
1238     double val = strtod(value, &eptr);
1239
1240     if (*eptr != '\0') {
1241       ERROR(PLUGIN_NAME " plugin: Invalid value for Instances = '%s'", value);
1242       return 1;
1243     }
1244     if (val <= 0) {
1245       ERROR(PLUGIN_NAME " plugin: Instances <= 0 makes no sense.");
1246       return 1;
1247     }
1248     if (val > NR_INSTANCES_MAX) {
1249       ERROR(PLUGIN_NAME " plugin: Instances=%f > NR_INSTANCES_MAX=%i"
1250                         " use a lower setting or recompile the plugin.",
1251             val, NR_INSTANCES_MAX);
1252       return 1;
1253     }
1254
1255     nr_instances = (int)val;
1256     DEBUG(PLUGIN_NAME " plugin: configured %i instances", nr_instances);
1257     return 0;
1258   }
1259
1260   if (strcasecmp(key, "ExtraStats") == 0) {
1261     char *localvalue = strdup(value);
1262     if (localvalue != NULL) {
1263       char *exstats[EX_STATS_MAX_FIELDS];
1264       int numexstats =
1265           strsplit(localvalue, exstats, STATIC_ARRAY_SIZE(exstats));
1266       extra_stats = parse_ex_stats_flags(exstats, numexstats);
1267       sfree(localvalue);
1268
1269 #ifdef HAVE_JOB_STATS
1270       if ((extra_stats & ex_stats_job_stats_completed) &&
1271           (extra_stats & ex_stats_job_stats_background)) {
1272         ERROR(PLUGIN_NAME " plugin: Invalid job stats configuration. Only one "
1273                           "type of job statistics can be collected at the same "
1274                           "time");
1275         return 1;
1276       }
1277 #endif
1278     }
1279   }
1280
1281   if (strcasecmp(key, "PersistentNotification") == 0) {
1282     persistent_notification = IS_TRUE(value);
1283     return 0;
1284   }
1285
1286   /* Unrecognised option. */
1287   return -1;
1288 }
1289
1290 static int lv_connect(void) {
1291   if (conn == NULL) {
1292 /* `conn_string == NULL' is acceptable */
1293 #ifdef HAVE_FS_INFO
1294     /* virDomainGetFSInfo requires full read-write access connection */
1295     if (extra_stats & ex_stats_fs_info)
1296       conn = virConnectOpen(conn_string);
1297     else
1298 #endif
1299       conn = virConnectOpenReadOnly(conn_string);
1300     if (conn == NULL) {
1301       c_complain(LOG_ERR, &conn_complain,
1302                  PLUGIN_NAME " plugin: Unable to connect: "
1303                              "virConnectOpen failed.");
1304       return -1;
1305     }
1306     int status = virNodeGetInfo(conn, &nodeinfo);
1307     if (status != 0) {
1308       ERROR(PLUGIN_NAME ": virNodeGetInfo failed");
1309       return -1;
1310     }
1311   }
1312   c_release(LOG_NOTICE, &conn_complain,
1313             PLUGIN_NAME " plugin: Connection established.");
1314   return 0;
1315 }
1316
1317 static void lv_disconnect(void) {
1318   if (conn != NULL)
1319     virConnectClose(conn);
1320   conn = NULL;
1321   WARNING(PLUGIN_NAME " plugin: closed connection to libvirt");
1322 }
1323
1324 static int lv_domain_block_info(virDomainPtr dom, const char *path,
1325                                 struct lv_block_info *binfo) {
1326 #ifdef HAVE_BLOCK_STATS_FLAGS
1327   int nparams = 0;
1328   if (virDomainBlockStatsFlags(dom, path, NULL, &nparams, 0) < 0 ||
1329       nparams <= 0) {
1330     VIRT_ERROR(conn, "getting the disk params count");
1331     return -1;
1332   }
1333
1334   virTypedParameterPtr params = calloc((size_t)nparams, sizeof(*params));
1335   if (params == NULL) {
1336     ERROR("virt plugin: alloc(%i) for block=%s parameters failed.", nparams,
1337           path);
1338     return -1;
1339   }
1340
1341   int rc = -1;
1342   if (virDomainBlockStatsFlags(dom, path, params, &nparams, 0) < 0) {
1343     VIRT_ERROR(conn, "getting the disk params values");
1344   } else {
1345     rc = get_block_info(binfo, params, nparams);
1346   }
1347
1348   virTypedParamsClear(params, nparams);
1349   sfree(params);
1350   return rc;
1351 #else
1352   return virDomainBlockStats(dom, path, &(binfo->bi), sizeof(binfo->bi));
1353 #endif /* HAVE_BLOCK_STATS_FLAGS */
1354 }
1355
1356 #ifdef HAVE_PERF_STATS
1357 static void perf_submit(virDomainStatsRecordPtr stats) {
1358   for (int i = 0; i < stats->nparams; ++i) {
1359     /* Replace '.' with '_' in event field to match other metrics' naming
1360      * convention */
1361     char *c = strchr(stats->params[i].field, '.');
1362     if (c)
1363       *c = '_';
1364     submit(stats->dom, "perf", stats->params[i].field,
1365            &(value_t){.derive = stats->params[i].value.ul}, 1);
1366   }
1367 }
1368
1369 static int get_perf_events(virDomainPtr domain) {
1370   virDomainStatsRecordPtr *stats = NULL;
1371   /* virDomainListGetStats requires a NULL terminated list of domains */
1372   virDomainPtr domain_array[] = {domain, NULL};
1373
1374   int status =
1375       virDomainListGetStats(domain_array, VIR_DOMAIN_STATS_PERF, &stats, 0);
1376   if (status == -1) {
1377     ERROR("virt plugin: virDomainListGetStats failed with status %i.", status);
1378     return status;
1379   }
1380
1381   for (int i = 0; i < status; ++i)
1382     perf_submit(stats[i]);
1383
1384   virDomainStatsRecordListFree(stats);
1385   return 0;
1386 }
1387 #endif /* HAVE_PERF_STATS */
1388
1389 static void vcpu_pin_submit(virDomainPtr dom, int max_cpus, int vcpu,
1390                             unsigned char *cpu_maps, int cpu_map_len) {
1391   for (int cpu = 0; cpu < max_cpus; ++cpu) {
1392     char type_instance[DATA_MAX_NAME_LEN];
1393     _Bool is_set = VIR_CPU_USABLE(cpu_maps, cpu_map_len, vcpu, cpu) ? 1 : 0;
1394
1395     snprintf(type_instance, sizeof(type_instance), "vcpu_%d-cpu_%d", vcpu, cpu);
1396     submit(dom, "cpu_affinity", type_instance, &(value_t){.gauge = is_set}, 1);
1397   }
1398 }
1399
1400 static int get_vcpu_stats(virDomainPtr domain, unsigned short nr_virt_cpu) {
1401   int max_cpus = VIR_NODEINFO_MAXCPUS(nodeinfo);
1402   int cpu_map_len = VIR_CPU_MAPLEN(max_cpus);
1403
1404   virVcpuInfoPtr vinfo = calloc(nr_virt_cpu, sizeof(vinfo[0]));
1405   if (vinfo == NULL) {
1406     ERROR(PLUGIN_NAME " plugin: malloc failed.");
1407     return -1;
1408   }
1409
1410   unsigned char *cpumaps = calloc(nr_virt_cpu, cpu_map_len);
1411   if (cpumaps == NULL) {
1412     ERROR(PLUGIN_NAME " plugin: malloc failed.");
1413     sfree(vinfo);
1414     return -1;
1415   }
1416
1417   int status =
1418       virDomainGetVcpus(domain, vinfo, nr_virt_cpu, cpumaps, cpu_map_len);
1419   if (status < 0) {
1420     ERROR(PLUGIN_NAME " plugin: virDomainGetVcpus failed with status %i.",
1421           status);
1422     sfree(cpumaps);
1423     sfree(vinfo);
1424     return status;
1425   }
1426
1427   for (int i = 0; i < nr_virt_cpu; ++i) {
1428     vcpu_submit(vinfo[i].cpuTime, domain, vinfo[i].number, "virt_vcpu");
1429     if (extra_stats & ex_stats_vcpupin)
1430       vcpu_pin_submit(domain, max_cpus, i, cpumaps, cpu_map_len);
1431   }
1432
1433   sfree(cpumaps);
1434   sfree(vinfo);
1435   return 0;
1436 }
1437
1438 #ifdef HAVE_DOM_REASON
1439
1440 static void domain_state_submit(virDomainPtr dom, int state, int reason) {
1441   value_t values[] = {
1442       {.gauge = (gauge_t)state}, {.gauge = (gauge_t)reason},
1443   };
1444
1445   submit(dom, "domain_state", NULL, values, STATIC_ARRAY_SIZE(values));
1446 }
1447
1448 static int get_domain_state(virDomainPtr domain) {
1449   int domain_state = 0;
1450   int domain_reason = 0;
1451
1452   int status = virDomainGetState(domain, &domain_state, &domain_reason, 0);
1453   if (status != 0) {
1454     ERROR(PLUGIN_NAME " plugin: virDomainGetState failed with status %i.",
1455           status);
1456     return status;
1457   }
1458
1459   domain_state_submit(domain, domain_state, domain_reason);
1460
1461   return status;
1462 }
1463
1464 #ifdef HAVE_LIST_ALL_DOMAINS
1465 static int get_domain_state_notify(virDomainPtr domain) {
1466   int domain_state = 0;
1467   int domain_reason = 0;
1468
1469   int status = virDomainGetState(domain, &domain_state, &domain_reason, 0);
1470   if (status != 0) {
1471     ERROR(PLUGIN_NAME " plugin: virDomainGetState failed with status %i.",
1472           status);
1473     return status;
1474   }
1475
1476   if (persistent_notification)
1477     domain_state_submit_notif(domain, domain_state, domain_reason);
1478
1479   return status;
1480 }
1481 #endif /* HAVE_LIST_ALL_DOMAINS */
1482 #endif /* HAVE_DOM_REASON */
1483
1484 static int get_memory_stats(virDomainPtr domain) {
1485   virDomainMemoryStatPtr minfo =
1486       calloc(VIR_DOMAIN_MEMORY_STAT_NR, sizeof(virDomainMemoryStatStruct));
1487   if (minfo == NULL) {
1488     ERROR("virt plugin: malloc failed.");
1489     return -1;
1490   }
1491
1492   int mem_stats =
1493       virDomainMemoryStats(domain, minfo, VIR_DOMAIN_MEMORY_STAT_NR, 0);
1494   if (mem_stats < 0) {
1495     ERROR("virt plugin: virDomainMemoryStats failed with mem_stats %i.",
1496           mem_stats);
1497     sfree(minfo);
1498     return mem_stats;
1499   }
1500
1501   for (int i = 0; i < mem_stats; i++)
1502     memory_stats_submit((gauge_t)minfo[i].val * 1024, domain, minfo[i].tag);
1503
1504   sfree(minfo);
1505   return 0;
1506 }
1507
1508 #ifdef HAVE_DISK_ERR
1509 static void disk_err_submit(virDomainPtr domain,
1510                             virDomainDiskErrorPtr disk_err) {
1511   submit(domain, "disk_error", disk_err->disk,
1512          &(value_t){.gauge = disk_err->error}, 1);
1513 }
1514
1515 static int get_disk_err(virDomainPtr domain) {
1516   /* Get preferred size of disk errors array */
1517   int disk_err_count = virDomainGetDiskErrors(domain, NULL, 0, 0);
1518   if (disk_err_count == -1) {
1519     ERROR(PLUGIN_NAME
1520           " plugin: failed to get preferred size of disk errors array");
1521     return -1;
1522   }
1523
1524   DEBUG(PLUGIN_NAME
1525         " plugin: preferred size of disk errors array: %d for domain %s",
1526         disk_err_count, virDomainGetName(domain));
1527   virDomainDiskError disk_err[disk_err_count];
1528
1529   disk_err_count = virDomainGetDiskErrors(domain, disk_err, disk_err_count, 0);
1530   if (disk_err_count == -1) {
1531     ERROR(PLUGIN_NAME " plugin: virDomainGetDiskErrors failed with status %d",
1532           disk_err_count);
1533     return -1;
1534   }
1535
1536   DEBUG(PLUGIN_NAME " plugin: detected %d disk errors in domain %s",
1537         disk_err_count, virDomainGetName(domain));
1538
1539   for (int i = 0; i < disk_err_count; ++i) {
1540     disk_err_submit(domain, &disk_err[i]);
1541     sfree(disk_err[i].disk);
1542   }
1543
1544   return 0;
1545 }
1546 #endif /* HAVE_DISK_ERR */
1547
1548 static int get_block_stats(struct block_device *block_dev) {
1549
1550   if (!block_dev) {
1551     ERROR(PLUGIN_NAME " plugin: get_block_stats NULL pointer");
1552     return -1;
1553   }
1554
1555   struct lv_block_info binfo;
1556   init_block_info(&binfo);
1557
1558   if (lv_domain_block_info(block_dev->dom, block_dev->path, &binfo) < 0) {
1559     ERROR(PLUGIN_NAME " plugin: lv_domain_block_info failed");
1560     return -1;
1561   }
1562
1563   disk_submit(&binfo, block_dev->dom, block_dev->path);
1564   return 0;
1565 }
1566
1567 #ifdef HAVE_FS_INFO
1568
1569 #define NM_ADD_ITEM(_fun, _name, _val)                                         \
1570   do {                                                                         \
1571     ret = _fun(&notif, _name, _val);                                           \
1572     if (ret != 0) {                                                            \
1573       ERROR(PLUGIN_NAME " plugin: failed to add notification metadata");       \
1574       goto cleanup;                                                            \
1575     }                                                                          \
1576   } while (0)
1577
1578 #define NM_ADD_STR_ITEMS(_items, _size)                                        \
1579   do {                                                                         \
1580     for (int _i = 0; _i < _size; ++_i) {                                       \
1581       DEBUG(PLUGIN_NAME                                                        \
1582             " plugin: Adding notification metadata name=%s value=%s",          \
1583             _items[_i].name, _items[_i].value);                                \
1584       NM_ADD_ITEM(plugin_notification_meta_add_string, _items[_i].name,        \
1585                   _items[_i].value);                                           \
1586     }                                                                          \
1587   } while (0)
1588
1589 static int fs_info_notify(virDomainPtr domain, virDomainFSInfoPtr fs_info) {
1590   notification_t notif;
1591   int ret = 0;
1592
1593   /* Local struct, just for the purpose of this function. */
1594   typedef struct nm_str_item_s {
1595     const char *name;
1596     const char *value;
1597   } nm_str_item_t;
1598
1599   nm_str_item_t fs_dev_alias[fs_info->ndevAlias];
1600   nm_str_item_t fs_str_items[] = {
1601       {.name = "mountpoint", .value = fs_info->mountpoint},
1602       {.name = "name", .value = fs_info->name},
1603       {.name = "fstype", .value = fs_info->fstype}};
1604
1605   for (int i = 0; i < fs_info->ndevAlias; ++i) {
1606     fs_dev_alias[i].name = "devAlias";
1607     fs_dev_alias[i].value = fs_info->devAlias[i];
1608   }
1609
1610   init_notif(&notif, domain, NOTIF_OKAY, "File system information",
1611              "file_system", NULL);
1612   NM_ADD_STR_ITEMS(fs_str_items, STATIC_ARRAY_SIZE(fs_str_items));
1613   NM_ADD_ITEM(plugin_notification_meta_add_unsigned_int, "ndevAlias",
1614               fs_info->ndevAlias);
1615   NM_ADD_STR_ITEMS(fs_dev_alias, fs_info->ndevAlias);
1616
1617   plugin_dispatch_notification(&notif);
1618
1619 cleanup:
1620   if (notif.meta)
1621     plugin_notification_meta_free(notif.meta);
1622   return ret;
1623 }
1624
1625 #undef RETURN_ON_ERR
1626 #undef NM_ADD_STR_ITEMS
1627
1628 static int get_fs_info(virDomainPtr domain) {
1629   virDomainFSInfoPtr *fs_info = NULL;
1630   int ret = 0;
1631
1632   int mount_points_cnt = virDomainGetFSInfo(domain, &fs_info, 0);
1633   if (mount_points_cnt == -1) {
1634     ERROR(PLUGIN_NAME " plugin: virDomainGetFSInfo failed: %d",
1635           mount_points_cnt);
1636     return mount_points_cnt;
1637   }
1638
1639   for (int i = 0; i < mount_points_cnt; ++i) {
1640     if (fs_info_notify(domain, fs_info[i]) != 0) {
1641       ERROR(PLUGIN_NAME " plugin: failed to send file system notification "
1642                         "for mount point %s",
1643             fs_info[i]->mountpoint);
1644       ret = -1;
1645     }
1646     virDomainFSInfoFree(fs_info[i]);
1647   }
1648
1649   sfree(fs_info);
1650   return ret;
1651 }
1652
1653 #endif /* HAVE_FS_INFO */
1654
1655 #ifdef HAVE_JOB_STATS
1656 static void job_stats_submit(virDomainPtr domain, virTypedParameterPtr param) {
1657   value_t vl = {0};
1658
1659   if (param->type == VIR_TYPED_PARAM_INT)
1660     vl.derive = param->value.i;
1661   else if (param->type == VIR_TYPED_PARAM_UINT)
1662     vl.derive = param->value.ui;
1663   else if (param->type == VIR_TYPED_PARAM_LLONG)
1664     vl.derive = param->value.l;
1665   else if (param->type == VIR_TYPED_PARAM_ULLONG)
1666     vl.derive = param->value.ul;
1667   else if (param->type == VIR_TYPED_PARAM_DOUBLE)
1668     vl.derive = param->value.d;
1669   else if (param->type == VIR_TYPED_PARAM_BOOLEAN)
1670     vl.derive = param->value.b;
1671   else if (param->type == VIR_TYPED_PARAM_STRING) {
1672     submit_notif(domain, NOTIF_OKAY, param->value.s, "job_stats", param->field);
1673     return;
1674   } else {
1675     ERROR(PLUGIN_NAME " plugin: unrecognized virTypedParameterType");
1676     return;
1677   }
1678
1679   submit(domain, "job_stats", param->field, &vl, 1);
1680 }
1681
1682 static int get_job_stats(virDomainPtr domain) {
1683   int ret = 0;
1684   int job_type = 0;
1685   int nparams = 0;
1686   virTypedParameterPtr params = NULL;
1687   int flags = (extra_stats & ex_stats_job_stats_completed)
1688                   ? VIR_DOMAIN_JOB_STATS_COMPLETED
1689                   : 0;
1690
1691   ret = virDomainGetJobStats(domain, &job_type, &params, &nparams, flags);
1692   if (ret != 0) {
1693     ERROR(PLUGIN_NAME " plugin: virDomainGetJobStats failed: %d", ret);
1694     return ret;
1695   }
1696
1697   DEBUG(PLUGIN_NAME " plugin: job_type=%d nparams=%d", job_type, nparams);
1698
1699   for (int i = 0; i < nparams; ++i) {
1700     DEBUG(PLUGIN_NAME " plugin: param[%d] field=%s type=%d", i, params[i].field,
1701           params[i].type);
1702     job_stats_submit(domain, &params[i]);
1703   }
1704
1705   virTypedParamsFree(params, nparams);
1706   return ret;
1707 }
1708 #endif /* HAVE_JOB_STATS */
1709
1710 static int get_domain_metrics(domain_t *domain) {
1711   struct lv_info info;
1712
1713   if (!domain || !domain->ptr) {
1714     ERROR(PLUGIN_NAME ": get_domain_metrics: NULL pointer");
1715     return -1;
1716   }
1717
1718   init_lv_info(&info);
1719   int status = lv_domain_info(domain->ptr, &info);
1720   if (status != 0) {
1721     ERROR(PLUGIN_NAME " plugin: virDomainGetInfo failed with status %i.",
1722           status);
1723     return -1;
1724   }
1725
1726   if (extra_stats & ex_stats_domain_state) {
1727 #ifdef HAVE_DOM_REASON
1728     /* At this point we already know domain's state from virDomainGetInfo call,
1729      * however it doesn't provide a reason for entering particular state.
1730      * We need to get it from virDomainGetState.
1731      */
1732     GET_STATS(get_domain_state, "domain reason", domain->ptr);
1733 #endif
1734   }
1735
1736   /* Gather remaining stats only for running domains */
1737   if (info.di.state != VIR_DOMAIN_RUNNING)
1738     return 0;
1739
1740   pcpu_submit(domain->ptr, &info);
1741   cpu_submit(domain, info.di.cpuTime);
1742
1743   memory_submit(domain->ptr, (gauge_t)info.di.memory * 1024);
1744
1745   GET_STATS(get_vcpu_stats, "vcpu stats", domain->ptr, info.di.nrVirtCpu);
1746   GET_STATS(get_memory_stats, "memory stats", domain->ptr);
1747
1748 #ifdef HAVE_PERF_STATS
1749   if (extra_stats & ex_stats_perf)
1750     GET_STATS(get_perf_events, "performance monitoring events", domain->ptr);
1751 #endif
1752
1753 #ifdef HAVE_FS_INFO
1754   if (extra_stats & ex_stats_fs_info)
1755     GET_STATS(get_fs_info, "file system info", domain->ptr);
1756 #endif
1757
1758 #ifdef HAVE_DISK_ERR
1759   if (extra_stats & ex_stats_disk_err)
1760     GET_STATS(get_disk_err, "disk errors", domain->ptr);
1761 #endif
1762
1763 #ifdef HAVE_JOB_STATS
1764   if (extra_stats &
1765       (ex_stats_job_stats_completed | ex_stats_job_stats_background))
1766     GET_STATS(get_job_stats, "job stats", domain->ptr);
1767 #endif
1768
1769   /* Update cached virDomainInfo. It has to be done after cpu_submit */
1770   memcpy(&domain->info, &info.di, sizeof(domain->info));
1771
1772   return 0;
1773 }
1774
1775 static int get_if_dev_stats(struct interface_device *if_dev) {
1776   virDomainInterfaceStatsStruct stats = {0};
1777   char *display_name = NULL;
1778
1779   if (!if_dev) {
1780     ERROR(PLUGIN_NAME " plugin: get_if_dev_stats: NULL pointer");
1781     return -1;
1782   }
1783
1784   switch (interface_format) {
1785   case if_address:
1786     display_name = if_dev->address;
1787     break;
1788   case if_number:
1789     display_name = if_dev->number;
1790     break;
1791   case if_name:
1792   default:
1793     display_name = if_dev->path;
1794   }
1795
1796   if (virDomainInterfaceStats(if_dev->dom, if_dev->path, &stats,
1797                               sizeof(stats)) != 0) {
1798     ERROR(PLUGIN_NAME " plugin: virDomainInterfaceStats failed");
1799     return -1;
1800   }
1801
1802   if ((stats.rx_bytes != -1) && (stats.tx_bytes != -1))
1803     submit_derive2("if_octets", (derive_t)stats.rx_bytes,
1804                    (derive_t)stats.tx_bytes, if_dev->dom, display_name);
1805
1806   if ((stats.rx_packets != -1) && (stats.tx_packets != -1))
1807     submit_derive2("if_packets", (derive_t)stats.rx_packets,
1808                    (derive_t)stats.tx_packets, if_dev->dom, display_name);
1809
1810   if ((stats.rx_errs != -1) && (stats.tx_errs != -1))
1811     submit_derive2("if_errors", (derive_t)stats.rx_errs,
1812                    (derive_t)stats.tx_errs, if_dev->dom, display_name);
1813
1814   if ((stats.rx_drop != -1) && (stats.tx_drop != -1))
1815     submit_derive2("if_dropped", (derive_t)stats.rx_drop,
1816                    (derive_t)stats.tx_drop, if_dev->dom, display_name);
1817   return 0;
1818 }
1819
1820 static int domain_lifecycle_event_cb(__attribute__((unused)) virConnectPtr conn,
1821                                      virDomainPtr dom, int event, int detail,
1822                                      __attribute__((unused)) void *opaque) {
1823   int domain_state = map_domain_event_to_state(event);
1824   int domain_reason = 0; /* 0 means UNKNOWN reason for any state */
1825 #ifdef HAVE_DOM_REASON
1826   domain_reason = map_domain_event_detail_to_reason(event, detail);
1827 #endif
1828   domain_state_submit_notif(dom, domain_state, domain_reason);
1829
1830   return 0;
1831 }
1832
1833 static int register_event_impl(void) {
1834   if (virEventRegisterDefaultImpl() < 0) {
1835     virErrorPtr err = virGetLastError();
1836     ERROR(PLUGIN_NAME
1837           " plugin: error while event implementation registering: %s",
1838           err && err->message ? err->message : "Unknown error");
1839     return -1;
1840   }
1841
1842   return 0;
1843 }
1844
1845 /* worker function running default event implementation */
1846 static void *event_loop_worker(__attribute__((unused)) void *arg) {
1847   while (1) {
1848     if (virEventRunDefaultImpl() < 0) {
1849       virErrorPtr err = virGetLastError();
1850       ERROR(PLUGIN_NAME " plugin: failed to run event loop: %s\n",
1851             err && err->message ? err->message : "Unknown error");
1852     }
1853   }
1854
1855   return NULL;
1856 }
1857
1858 /* register domain event callback and start event loop thread */
1859 static int start_event_loop(void) {
1860   domain_event_cb_id = virConnectDomainEventRegisterAny(
1861       conn, NULL, VIR_DOMAIN_EVENT_ID_LIFECYCLE,
1862       VIR_DOMAIN_EVENT_CALLBACK(domain_lifecycle_event_cb), NULL, NULL);
1863   if (domain_event_cb_id == -1) {
1864     ERROR(PLUGIN_NAME " plugin: error while callback registering");
1865     return -1;
1866   }
1867
1868   if (pthread_create(&event_loop_tid, NULL, event_loop_worker, NULL)) {
1869     ERROR(PLUGIN_NAME " plugin: failed event loop thread creation");
1870     virConnectDomainEventDeregisterAny(conn, domain_event_cb_id);
1871     return -1;
1872   }
1873
1874   return 0;
1875 }
1876
1877 /* stop event loop thread and deregister callback */
1878 static void stop_event_loop(void) {
1879   if (pthread_cancel(event_loop_tid) != 0)
1880     ERROR(PLUGIN_NAME " plugin: cancelling thread %lu failed", event_loop_tid);
1881
1882   if (pthread_join(event_loop_tid, NULL) != 0)
1883     ERROR(PLUGIN_NAME " plugin: stopping thread %lu failed", event_loop_tid);
1884
1885   if (conn != NULL && domain_event_cb_id != -1)
1886     virConnectDomainEventDeregisterAny(conn, domain_event_cb_id);
1887 }
1888
1889 static int persistent_domains_state_notification(void) {
1890   int status = 0;
1891   int n;
1892 #ifdef HAVE_LIST_ALL_DOMAINS
1893   virDomainPtr *domains = NULL;
1894   n = virConnectListAllDomains(conn, &domains,
1895                                VIR_CONNECT_LIST_DOMAINS_PERSISTENT);
1896   if (n < 0) {
1897     VIRT_ERROR(conn, "reading list of persistent domains");
1898     status = -1;
1899   } else {
1900     DEBUG(PLUGIN_NAME " plugin: getting state of %i persistent domains", n);
1901     /* Fetch each persistent domain's state and notify it */
1902     int n_notified = n;
1903     for (int i = 0; i < n; ++i) {
1904       status = get_domain_state_notify(domains[i]);
1905       if (status != 0) {
1906         n_notified--;
1907         ERROR(PLUGIN_NAME " plugin: could not notify state of domain %s",
1908               virDomainGetName(domains[i]));
1909       }
1910       virDomainFree(domains[i]);
1911     }
1912
1913     sfree(domains);
1914     DEBUG(PLUGIN_NAME " plugin: notified state of %i persistent domains",
1915           n_notified);
1916   }
1917 #else
1918   n = virConnectNumOfDomains(conn);
1919   if (n > 0) {
1920     int *domids;
1921     /* Get list of domains. */
1922     domids = malloc(sizeof(*domids) * n);
1923     if (domids == NULL) {
1924       ERROR(PLUGIN_NAME " plugin: malloc failed.");
1925       return -1;
1926     }
1927     n = virConnectListDomains(conn, domids, n);
1928     if (n < 0) {
1929       VIRT_ERROR(conn, "reading list of domains");
1930       sfree(domids);
1931       return -1;
1932     }
1933     /* Fetch info of each active domain and notify it */
1934     for (int i = 0; i < n; ++i) {
1935       virDomainInfo info;
1936       virDomainPtr dom = NULL;
1937       dom = virDomainLookupByID(conn, domids[i]);
1938       if (dom == NULL) {
1939         VIRT_ERROR(conn, "virDomainLookupByID");
1940         /* Could be that the domain went away -- ignore it anyway. */
1941         continue;
1942       }
1943       status = virDomainGetInfo(dom, &info);
1944       if (status != 0) {
1945         ERROR(PLUGIN_NAME " plugin: virDomainGetInfo failed with status %i.",
1946               status);
1947         continue;
1948       }
1949       /* virDomainGetState is not available. Submit 0, which corresponds to
1950        * unknown reason. */
1951       domain_state_submit_notif(dom, info.state, 0);
1952     }
1953     sfree(domids);
1954   }
1955 #endif
1956
1957   return status;
1958 }
1959
1960 static int lv_read(user_data_t *ud) {
1961   time_t t;
1962   struct lv_read_instance *inst = NULL;
1963   struct lv_read_state *state = NULL;
1964
1965   if (ud->data == NULL) {
1966     ERROR(PLUGIN_NAME " plugin: NULL userdata");
1967     return -1;
1968   }
1969
1970   inst = ud->data;
1971   state = &inst->read_state;
1972
1973   _Bool reconnect = conn == NULL ? 1 : 0;
1974   /* event implementation must be registered before connection is opened */
1975   if (inst->id == 0) {
1976     if (!persistent_notification && reconnect)
1977       if (register_event_impl() != 0)
1978         return -1;
1979
1980     if (lv_connect() < 0)
1981       return -1;
1982
1983     if (!persistent_notification && reconnect && conn != NULL)
1984       if (start_event_loop() != 0)
1985         return -1;
1986   }
1987
1988   time(&t);
1989
1990   /* Need to refresh domain or device lists? */
1991   if ((last_refresh == (time_t)0) ||
1992       ((interval > 0) && ((last_refresh + interval) <= t))) {
1993     if (inst->id == 0 && persistent_notification) {
1994       int status = persistent_domains_state_notification();
1995       if (status != 0)
1996         DEBUG(PLUGIN_NAME " plugin: persistent_domains_state_notifications "
1997                           "returned with status %i",
1998               status);
1999     }
2000     if (refresh_lists(inst) != 0) {
2001       if (inst->id == 0) {
2002         if (!persistent_notification)
2003           stop_event_loop();
2004         lv_disconnect();
2005       }
2006       return -1;
2007     }
2008     last_refresh = t;
2009   }
2010
2011 #if COLLECT_DEBUG
2012   for (int i = 0; i < state->nr_domains; ++i)
2013     DEBUG(PLUGIN_NAME " plugin: domain %s",
2014           virDomainGetName(state->domains[i].ptr));
2015   for (int i = 0; i < state->nr_block_devices; ++i)
2016     DEBUG(PLUGIN_NAME " plugin: block device %d %s:%s", i,
2017           virDomainGetName(state->block_devices[i].dom),
2018           state->block_devices[i].path);
2019   for (int i = 0; i < state->nr_interface_devices; ++i)
2020     DEBUG(PLUGIN_NAME " plugin: interface device %d %s:%s", i,
2021           virDomainGetName(state->interface_devices[i].dom),
2022           state->interface_devices[i].path);
2023 #endif
2024
2025   /* Get domains' metrics */
2026   for (int i = 0; i < state->nr_domains; ++i) {
2027     domain_t *dom = &state->domains[i];
2028     int status = 0;
2029     if (dom->active)
2030       status = get_domain_metrics(dom);
2031 #ifdef HAVE_DOM_REASON
2032     else
2033       status = get_domain_state(dom->ptr);
2034 #endif
2035
2036     if (status != 0)
2037       ERROR(PLUGIN_NAME " failed to get metrics for domain=%s",
2038             virDomainGetName(dom->ptr));
2039   }
2040
2041   /* Get block device stats for each domain. */
2042   for (int i = 0; i < state->nr_block_devices; ++i) {
2043     int status = get_block_stats(&state->block_devices[i]);
2044     if (status != 0)
2045       ERROR(PLUGIN_NAME
2046             " failed to get stats for block device (%s) in domain %s",
2047             state->block_devices[i].path,
2048             virDomainGetName(state->domains[i].ptr));
2049   }
2050
2051   /* Get interface stats for each domain. */
2052   for (int i = 0; i < state->nr_interface_devices; ++i) {
2053     int status = get_if_dev_stats(&state->interface_devices[i]);
2054     if (status != 0)
2055       ERROR(PLUGIN_NAME
2056             " failed to get interface stats for device (%s) in domain %s",
2057             state->interface_devices[i].path,
2058             virDomainGetName(state->interface_devices[i].dom));
2059   }
2060
2061   return 0;
2062 }
2063
2064 static int lv_init_instance(size_t i, plugin_read_cb callback) {
2065   struct lv_user_data *lv_ud = &(lv_read_user_data[i]);
2066   struct lv_read_instance *inst = &(lv_ud->inst);
2067
2068   memset(lv_ud, 0, sizeof(*lv_ud));
2069
2070   snprintf(inst->tag, sizeof(inst->tag), "%s-%" PRIsz, PLUGIN_NAME, i);
2071   inst->id = i;
2072
2073   user_data_t *ud = &(lv_ud->ud);
2074   ud->data = inst;
2075   ud->free_func = NULL;
2076
2077   INFO(PLUGIN_NAME " plugin: reader %s initialized", inst->tag);
2078
2079   return plugin_register_complex_read(NULL, inst->tag, callback, 0, ud);
2080 }
2081
2082 static void lv_clean_read_state(struct lv_read_state *state) {
2083   free_block_devices(state);
2084   free_interface_devices(state);
2085   free_domains(state);
2086 }
2087
2088 static void lv_fini_instance(size_t i) {
2089   struct lv_read_instance *inst = &(lv_read_user_data[i].inst);
2090   struct lv_read_state *state = &(inst->read_state);
2091
2092   lv_clean_read_state(state);
2093
2094   INFO(PLUGIN_NAME " plugin: reader %s finalized", inst->tag);
2095 }
2096
2097 static int lv_init(void) {
2098   if (virInitialize() != 0)
2099     return -1;
2100
2101   /* event implementation must be registered before connection is opened */
2102   if (!persistent_notification)
2103     if (register_event_impl() != 0)
2104       return -1;
2105
2106   if (lv_connect() != 0)
2107     return -1;
2108
2109   DEBUG(PLUGIN_NAME " plugin: starting event loop");
2110
2111   if (!persistent_notification)
2112     if (start_event_loop() != 0)
2113       return -1;
2114
2115   DEBUG(PLUGIN_NAME " plugin: starting %i instances", nr_instances);
2116
2117   for (int i = 0; i < nr_instances; ++i)
2118     if (lv_init_instance(i, lv_read) != 0)
2119       return -1;
2120
2121   return 0;
2122 }
2123
2124 /*
2125  * returns 0 on success and <0 on error
2126  */
2127 static int lv_domain_get_tag(xmlXPathContextPtr xpath_ctx, const char *dom_name,
2128                              char *dom_tag) {
2129   char xpath_str[BUFFER_MAX_LEN] = {'\0'};
2130   xmlXPathObjectPtr xpath_obj = NULL;
2131   xmlNodePtr xml_node = NULL;
2132   int ret = -1;
2133   int err;
2134
2135   err = xmlXPathRegisterNs(xpath_ctx,
2136                            (const xmlChar *)METADATA_VM_PARTITION_PREFIX,
2137                            (const xmlChar *)METADATA_VM_PARTITION_URI);
2138   if (err) {
2139     ERROR(PLUGIN_NAME " plugin: xmlXpathRegisterNs(%s, %s) failed on domain %s",
2140           METADATA_VM_PARTITION_PREFIX, METADATA_VM_PARTITION_URI, dom_name);
2141     goto done;
2142   }
2143
2144   snprintf(xpath_str, sizeof(xpath_str), "/domain/metadata/%s:%s/text()",
2145            METADATA_VM_PARTITION_PREFIX, METADATA_VM_PARTITION_ELEMENT);
2146   xpath_obj = xmlXPathEvalExpression((xmlChar *)xpath_str, xpath_ctx);
2147   if (xpath_obj == NULL) {
2148     ERROR(PLUGIN_NAME " plugin: xmlXPathEval(%s) failed on domain %s",
2149           xpath_str, dom_name);
2150     goto done;
2151   }
2152
2153   if (xpath_obj->type != XPATH_NODESET) {
2154     ERROR(PLUGIN_NAME " plugin: xmlXPathEval(%s) unexpected return type %d "
2155                       "(wanted %d) on domain %s",
2156           xpath_str, xpath_obj->type, XPATH_NODESET, dom_name);
2157     goto done;
2158   }
2159
2160   /*
2161    * from now on there is no real error, it's ok if a domain
2162    * doesn't have the metadata partition tag.
2163    */
2164   ret = 0;
2165   if (xpath_obj->nodesetval == NULL || xpath_obj->nodesetval->nodeNr != 1) {
2166     DEBUG(PLUGIN_NAME " plugin: xmlXPathEval(%s) return nodeset size=%i "
2167                       "expected=1 on domain %s",
2168           xpath_str,
2169           (xpath_obj->nodesetval == NULL) ? 0 : xpath_obj->nodesetval->nodeNr,
2170           dom_name);
2171   } else {
2172     xml_node = xpath_obj->nodesetval->nodeTab[0];
2173     sstrncpy(dom_tag, (const char *)xml_node->content, PARTITION_TAG_MAX_LEN);
2174   }
2175
2176 done:
2177   /* deregister to clean up */
2178   err = xmlXPathRegisterNs(xpath_ctx,
2179                            (const xmlChar *)METADATA_VM_PARTITION_PREFIX, NULL);
2180   if (err) {
2181     /* we can't really recover here */
2182     ERROR(PLUGIN_NAME
2183           " plugin: deregistration of namespace %s failed for domain %s",
2184           METADATA_VM_PARTITION_PREFIX, dom_name);
2185   }
2186   if (xpath_obj)
2187     xmlXPathFreeObject(xpath_obj);
2188
2189   return ret;
2190 }
2191
2192 static int is_known_tag(const char *dom_tag) {
2193   for (int i = 0; i < nr_instances; ++i)
2194     if (!strcmp(dom_tag, lv_read_user_data[i].inst.tag))
2195       return 1;
2196   return 0;
2197 }
2198
2199 static int lv_instance_include_domain(struct lv_read_instance *inst,
2200                                       const char *dom_name,
2201                                       const char *dom_tag) {
2202   if ((dom_tag[0] != '\0') && (strcmp(dom_tag, inst->tag) == 0))
2203     return 1;
2204
2205   /* instance#0 will always be there, so it is in charge of extra duties */
2206   if (inst->id == 0) {
2207     if (dom_tag[0] == '\0' || !is_known_tag(dom_tag)) {
2208       DEBUG(PLUGIN_NAME " plugin#%s: refreshing domain %s "
2209                         "with unknown tag '%s'",
2210             inst->tag, dom_name, dom_tag);
2211       return 1;
2212     }
2213   }
2214
2215   return 0;
2216 }
2217
2218 static int refresh_lists(struct lv_read_instance *inst) {
2219   struct lv_read_state *state = &inst->read_state;
2220   int n;
2221
2222 #ifndef HAVE_LIST_ALL_DOMAINS
2223   n = virConnectNumOfDomains(conn);
2224   if (n < 0) {
2225     VIRT_ERROR(conn, "reading number of domains");
2226     return -1;
2227   }
2228 #endif
2229
2230   lv_clean_read_state(state);
2231
2232 #ifndef HAVE_LIST_ALL_DOMAINS
2233   if (n == 0)
2234     goto end;
2235 #endif
2236
2237 #ifdef HAVE_LIST_ALL_DOMAINS
2238   virDomainPtr *domains, *domains_inactive;
2239   int m = virConnectListAllDomains(conn, &domains_inactive,
2240                                    VIR_CONNECT_LIST_DOMAINS_INACTIVE);
2241   n = virConnectListAllDomains(conn, &domains, VIR_CONNECT_LIST_DOMAINS_ACTIVE);
2242 #else
2243   int *domids;
2244
2245   /* Get list of domains. */
2246   domids = malloc(sizeof(*domids) * n);
2247   if (domids == NULL) {
2248     ERROR(PLUGIN_NAME " plugin: malloc failed.");
2249     return -1;
2250   }
2251
2252   n = virConnectListDomains(conn, domids, n);
2253 #endif
2254
2255   if (n < 0) {
2256     VIRT_ERROR(conn, "reading list of domains");
2257 #ifndef HAVE_LIST_ALL_DOMAINS
2258     sfree(domids);
2259 #else
2260     for (int i = 0; i < m; ++i)
2261       virDomainFree(domains_inactive[i]);
2262     sfree(domains_inactive);
2263 #endif
2264     return -1;
2265   }
2266
2267 #ifdef HAVE_LIST_ALL_DOMAINS
2268   for (int i = 0; i < m; ++i)
2269     if (add_domain(state, domains_inactive[i], 0) < 0) {
2270       ERROR(PLUGIN_NAME " plugin: malloc failed.");
2271       continue;
2272     }
2273 #endif
2274
2275   /* Fetch each domain and add it to the list, unless ignore. */
2276   for (int i = 0; i < n; ++i) {
2277     const char *name;
2278     char *xml = NULL;
2279     xmlDocPtr xml_doc = NULL;
2280     xmlXPathContextPtr xpath_ctx = NULL;
2281     xmlXPathObjectPtr xpath_obj = NULL;
2282     char tag[PARTITION_TAG_MAX_LEN] = {'\0'};
2283     virDomainInfo info;
2284     int status;
2285
2286 #ifdef HAVE_LIST_ALL_DOMAINS
2287     virDomainPtr dom = domains[i];
2288 #else
2289     virDomainPtr dom = NULL;
2290     dom = virDomainLookupByID(conn, domids[i]);
2291     if (dom == NULL) {
2292       VIRT_ERROR(conn, "virDomainLookupByID");
2293       /* Could be that the domain went away -- ignore it anyway. */
2294       continue;
2295     }
2296 #endif
2297
2298     name = virDomainGetName(dom);
2299     if (name == NULL) {
2300       VIRT_ERROR(conn, "virDomainGetName");
2301       goto cont;
2302     }
2303
2304     status = virDomainGetInfo(dom, &info);
2305     if (status != 0) {
2306       ERROR(PLUGIN_NAME " plugin: virDomainGetInfo failed with status %i.",
2307             status);
2308       continue;
2309     }
2310
2311     if (info.state != VIR_DOMAIN_RUNNING) {
2312       DEBUG(PLUGIN_NAME " plugin: skipping inactive domain %s", name);
2313       continue;
2314     }
2315
2316     if (il_domains && ignorelist_match(il_domains, name) != 0)
2317       goto cont;
2318
2319     /* Get a list of devices for this domain. */
2320     xml = virDomainGetXMLDesc(dom, 0);
2321     if (!xml) {
2322       VIRT_ERROR(conn, "virDomainGetXMLDesc");
2323       goto cont;
2324     }
2325
2326     /* Yuck, XML.  Parse out the devices. */
2327     xml_doc = xmlReadDoc((xmlChar *)xml, NULL, NULL, XML_PARSE_NONET);
2328     if (xml_doc == NULL) {
2329       VIRT_ERROR(conn, "xmlReadDoc");
2330       goto cont;
2331     }
2332
2333     xpath_ctx = xmlXPathNewContext(xml_doc);
2334
2335     if (lv_domain_get_tag(xpath_ctx, name, tag) < 0) {
2336       ERROR(PLUGIN_NAME " plugin: lv_domain_get_tag failed.");
2337       goto cont;
2338     }
2339
2340     if (!lv_instance_include_domain(inst, name, tag))
2341       goto cont;
2342
2343     if (add_domain(state, dom, 1) < 0) {
2344       ERROR(PLUGIN_NAME " plugin: malloc failed.");
2345       goto cont;
2346     }
2347
2348     /* Block devices. */
2349     const char *bd_xmlpath = "/domain/devices/disk/target[@dev]";
2350     if (blockdevice_format == source)
2351       bd_xmlpath = "/domain/devices/disk/source[@dev]";
2352     xpath_obj = xmlXPathEval((const xmlChar *)bd_xmlpath, xpath_ctx);
2353
2354     if (xpath_obj == NULL || xpath_obj->type != XPATH_NODESET ||
2355         xpath_obj->nodesetval == NULL)
2356       goto cont;
2357
2358     for (int j = 0; j < xpath_obj->nodesetval->nodeNr; ++j) {
2359       xmlNodePtr node;
2360       char *path = NULL;
2361
2362       node = xpath_obj->nodesetval->nodeTab[j];
2363       if (!node)
2364         continue;
2365       path = (char *)xmlGetProp(node, (xmlChar *)"dev");
2366       if (!path)
2367         continue;
2368
2369       if (il_block_devices &&
2370           ignore_device_match(il_block_devices, name, path) != 0)
2371         goto cont2;
2372
2373       add_block_device(state, dom, path);
2374     cont2:
2375       if (path)
2376         xmlFree(path);
2377     }
2378     xmlXPathFreeObject(xpath_obj);
2379
2380     /* Network interfaces. */
2381     xpath_obj = xmlXPathEval(
2382         (xmlChar *)"/domain/devices/interface[target[@dev]]", xpath_ctx);
2383     if (xpath_obj == NULL || xpath_obj->type != XPATH_NODESET ||
2384         xpath_obj->nodesetval == NULL)
2385       goto cont;
2386
2387     xmlNodeSetPtr xml_interfaces = xpath_obj->nodesetval;
2388
2389     for (int j = 0; j < xml_interfaces->nodeNr; ++j) {
2390       char *path = NULL;
2391       char *address = NULL;
2392       xmlNodePtr xml_interface;
2393
2394       xml_interface = xml_interfaces->nodeTab[j];
2395       if (!xml_interface)
2396         continue;
2397
2398       for (xmlNodePtr child = xml_interface->children; child;
2399            child = child->next) {
2400         if (child->type != XML_ELEMENT_NODE)
2401           continue;
2402
2403         if (xmlStrEqual(child->name, (const xmlChar *)"target")) {
2404           path = (char *)xmlGetProp(child, (const xmlChar *)"dev");
2405           if (!path)
2406             continue;
2407         } else if (xmlStrEqual(child->name, (const xmlChar *)"mac")) {
2408           address = (char *)xmlGetProp(child, (const xmlChar *)"address");
2409           if (!address)
2410             continue;
2411         }
2412       }
2413
2414       if (il_interface_devices &&
2415           (ignore_device_match(il_interface_devices, name, path) != 0 ||
2416            ignore_device_match(il_interface_devices, name, address) != 0))
2417         goto cont3;
2418
2419       add_interface_device(state, dom, path, address, j + 1);
2420     cont3:
2421       if (path)
2422         xmlFree(path);
2423       if (address)
2424         xmlFree(address);
2425     }
2426
2427   cont:
2428     if (xpath_obj)
2429       xmlXPathFreeObject(xpath_obj);
2430     if (xpath_ctx)
2431       xmlXPathFreeContext(xpath_ctx);
2432     if (xml_doc)
2433       xmlFreeDoc(xml_doc);
2434     sfree(xml);
2435   }
2436
2437 #ifdef HAVE_LIST_ALL_DOMAINS
2438   for (int i = 0; i < n; ++i)
2439     virDomainFree(domains[i]);
2440   sfree(domains);
2441   for (int i = 0; i < m; ++i)
2442     virDomainFree(domains_inactive[i]);
2443   sfree(domains_inactive);
2444 #else
2445   sfree(domids);
2446
2447 end:
2448 #endif
2449
2450   DEBUG(PLUGIN_NAME " plugin#%s: refreshing"
2451                     " domains=%i block_devices=%i iface_devices=%i",
2452         inst->tag, state->nr_domains, state->nr_block_devices,
2453         state->nr_interface_devices);
2454
2455   return 0;
2456 }
2457
2458 static void free_domains(struct lv_read_state *state) {
2459   if (state->domains) {
2460     for (int i = 0; i < state->nr_domains; ++i)
2461       virDomainFree(state->domains[i].ptr);
2462     sfree(state->domains);
2463   }
2464   state->domains = NULL;
2465   state->nr_domains = 0;
2466 }
2467
2468 static int add_domain(struct lv_read_state *state, virDomainPtr dom,
2469                       _Bool active) {
2470   domain_t *new_ptr;
2471   int new_size = sizeof(state->domains[0]) * (state->nr_domains + 1);
2472
2473   if (state->domains)
2474     new_ptr = realloc(state->domains, new_size);
2475   else
2476     new_ptr = malloc(new_size);
2477
2478   if (new_ptr == NULL)
2479     return -1;
2480
2481   state->domains = new_ptr;
2482   state->domains[state->nr_domains].ptr = dom;
2483   state->domains[state->nr_domains].active = active;
2484   memset(&state->domains[state->nr_domains].info, 0,
2485          sizeof(state->domains[state->nr_domains].info));
2486
2487   return state->nr_domains++;
2488 }
2489
2490 static void free_block_devices(struct lv_read_state *state) {
2491   if (state->block_devices) {
2492     for (int i = 0; i < state->nr_block_devices; ++i)
2493       sfree(state->block_devices[i].path);
2494     sfree(state->block_devices);
2495   }
2496   state->block_devices = NULL;
2497   state->nr_block_devices = 0;
2498 }
2499
2500 static int add_block_device(struct lv_read_state *state, virDomainPtr dom,
2501                             const char *path) {
2502   struct block_device *new_ptr;
2503   int new_size =
2504       sizeof(state->block_devices[0]) * (state->nr_block_devices + 1);
2505   char *path_copy;
2506
2507   path_copy = strdup(path);
2508   if (!path_copy)
2509     return -1;
2510
2511   if (state->block_devices)
2512     new_ptr = realloc(state->block_devices, new_size);
2513   else
2514     new_ptr = malloc(new_size);
2515
2516   if (new_ptr == NULL) {
2517     sfree(path_copy);
2518     return -1;
2519   }
2520   state->block_devices = new_ptr;
2521   state->block_devices[state->nr_block_devices].dom = dom;
2522   state->block_devices[state->nr_block_devices].path = path_copy;
2523   return state->nr_block_devices++;
2524 }
2525
2526 static void free_interface_devices(struct lv_read_state *state) {
2527   if (state->interface_devices) {
2528     for (int i = 0; i < state->nr_interface_devices; ++i) {
2529       sfree(state->interface_devices[i].path);
2530       sfree(state->interface_devices[i].address);
2531       sfree(state->interface_devices[i].number);
2532     }
2533     sfree(state->interface_devices);
2534   }
2535   state->interface_devices = NULL;
2536   state->nr_interface_devices = 0;
2537 }
2538
2539 static int add_interface_device(struct lv_read_state *state, virDomainPtr dom,
2540                                 const char *path, const char *address,
2541                                 unsigned int number) {
2542   struct interface_device *new_ptr;
2543   int new_size =
2544       sizeof(state->interface_devices[0]) * (state->nr_interface_devices + 1);
2545   char *path_copy, *address_copy, number_string[15];
2546
2547   if ((path == NULL) || (address == NULL))
2548     return EINVAL;
2549
2550   path_copy = strdup(path);
2551   if (!path_copy)
2552     return -1;
2553
2554   address_copy = strdup(address);
2555   if (!address_copy) {
2556     sfree(path_copy);
2557     return -1;
2558   }
2559
2560   snprintf(number_string, sizeof(number_string), "interface-%u", number);
2561
2562   if (state->interface_devices)
2563     new_ptr = realloc(state->interface_devices, new_size);
2564   else
2565     new_ptr = malloc(new_size);
2566
2567   if (new_ptr == NULL) {
2568     sfree(path_copy);
2569     sfree(address_copy);
2570     return -1;
2571   }
2572   state->interface_devices = new_ptr;
2573   state->interface_devices[state->nr_interface_devices].dom = dom;
2574   state->interface_devices[state->nr_interface_devices].path = path_copy;
2575   state->interface_devices[state->nr_interface_devices].address = address_copy;
2576   state->interface_devices[state->nr_interface_devices].number =
2577       strdup(number_string);
2578   return state->nr_interface_devices++;
2579 }
2580
2581 static int ignore_device_match(ignorelist_t *il, const char *domname,
2582                                const char *devpath) {
2583   char *name;
2584   int n, r;
2585
2586   if ((domname == NULL) || (devpath == NULL))
2587     return 0;
2588
2589   n = strlen(domname) + strlen(devpath) + 2;
2590   name = malloc(n);
2591   if (name == NULL) {
2592     ERROR(PLUGIN_NAME " plugin: malloc failed.");
2593     return 0;
2594   }
2595   snprintf(name, n, "%s:%s", domname, devpath);
2596   r = ignorelist_match(il, name);
2597   sfree(name);
2598   return r;
2599 }
2600
2601 static int lv_shutdown(void) {
2602   for (int i = 0; i < nr_instances; ++i) {
2603     lv_fini_instance(i);
2604   }
2605
2606   DEBUG(PLUGIN_NAME " plugin: stopping event loop");
2607
2608   if (!persistent_notification)
2609     stop_event_loop();
2610
2611   lv_disconnect();
2612
2613   ignorelist_free(il_domains);
2614   il_domains = NULL;
2615   ignorelist_free(il_block_devices);
2616   il_block_devices = NULL;
2617   ignorelist_free(il_interface_devices);
2618   il_interface_devices = NULL;
2619
2620   return 0;
2621 }
2622
2623 void module_register(void) {
2624   plugin_register_config(PLUGIN_NAME, lv_config, config_keys, NR_CONFIG_KEYS);
2625   plugin_register_init(PLUGIN_NAME, lv_init);
2626   plugin_register_shutdown(PLUGIN_NAME, lv_shutdown);
2627 }