virt: Fix memory handling for domains data
[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 = calloc(n, sizeof(*domids));
1923     if (domids == NULL) {
1924       ERROR(PLUGIN_NAME " plugin: calloc 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         /* virDomainGetState is not available. Submit 0, which corresponds to
1946          * unknown reason. */
1947         domain_state_submit_notif(dom, info.state, 0);
1948       else
1949         ERROR(PLUGIN_NAME " plugin: virDomainGetInfo failed with status %i.",
1950               status);
1951
1952       virDomainFree(dom);
1953     }
1954     sfree(domids);
1955   }
1956 #endif
1957
1958   return status;
1959 }
1960
1961 static int lv_read(user_data_t *ud) {
1962   time_t t;
1963   struct lv_read_instance *inst = NULL;
1964   struct lv_read_state *state = NULL;
1965
1966   if (ud->data == NULL) {
1967     ERROR(PLUGIN_NAME " plugin: NULL userdata");
1968     return -1;
1969   }
1970
1971   inst = ud->data;
1972   state = &inst->read_state;
1973
1974   _Bool reconnect = conn == NULL ? 1 : 0;
1975   /* event implementation must be registered before connection is opened */
1976   if (inst->id == 0) {
1977     if (!persistent_notification && reconnect)
1978       if (register_event_impl() != 0)
1979         return -1;
1980
1981     if (lv_connect() < 0)
1982       return -1;
1983
1984     if (!persistent_notification && reconnect && conn != NULL)
1985       if (start_event_loop() != 0)
1986         return -1;
1987   }
1988
1989   time(&t);
1990
1991   /* Need to refresh domain or device lists? */
1992   if ((last_refresh == (time_t)0) ||
1993       ((interval > 0) && ((last_refresh + interval) <= t))) {
1994     if (refresh_lists(inst) != 0) {
1995       if (inst->id == 0) {
1996         if (!persistent_notification)
1997           stop_event_loop();
1998         lv_disconnect();
1999       }
2000       return -1;
2001     }
2002     last_refresh = t;
2003   }
2004
2005   /* persistent domains state notifications are handled by instance 0 */
2006   if (inst->id == 0 && persistent_notification) {
2007     int status = persistent_domains_state_notification();
2008     if (status != 0)
2009       DEBUG(PLUGIN_NAME " plugin: persistent_domains_state_notifications "
2010                         "returned with status %i",
2011             status);
2012   }
2013
2014 #if COLLECT_DEBUG
2015   for (int i = 0; i < state->nr_domains; ++i)
2016     DEBUG(PLUGIN_NAME " plugin: domain %s",
2017           virDomainGetName(state->domains[i].ptr));
2018   for (int i = 0; i < state->nr_block_devices; ++i)
2019     DEBUG(PLUGIN_NAME " plugin: block device %d %s:%s", i,
2020           virDomainGetName(state->block_devices[i].dom),
2021           state->block_devices[i].path);
2022   for (int i = 0; i < state->nr_interface_devices; ++i)
2023     DEBUG(PLUGIN_NAME " plugin: interface device %d %s:%s", i,
2024           virDomainGetName(state->interface_devices[i].dom),
2025           state->interface_devices[i].path);
2026 #endif
2027
2028   /* Get domains' metrics */
2029   for (int i = 0; i < state->nr_domains; ++i) {
2030     domain_t *dom = &state->domains[i];
2031     int status = 0;
2032     if (dom->active)
2033       status = get_domain_metrics(dom);
2034 #ifdef HAVE_DOM_REASON
2035     else
2036       status = get_domain_state(dom->ptr);
2037 #endif
2038
2039     if (status != 0)
2040       ERROR(PLUGIN_NAME " failed to get metrics for domain=%s",
2041             virDomainGetName(dom->ptr));
2042   }
2043
2044   /* Get block device stats for each domain. */
2045   for (int i = 0; i < state->nr_block_devices; ++i) {
2046     int status = get_block_stats(&state->block_devices[i]);
2047     if (status != 0)
2048       ERROR(PLUGIN_NAME
2049             " failed to get stats for block device (%s) in domain %s",
2050             state->block_devices[i].path,
2051             virDomainGetName(state->domains[i].ptr));
2052   }
2053
2054   /* Get interface stats for each domain. */
2055   for (int i = 0; i < state->nr_interface_devices; ++i) {
2056     int status = get_if_dev_stats(&state->interface_devices[i]);
2057     if (status != 0)
2058       ERROR(PLUGIN_NAME
2059             " failed to get interface stats for device (%s) in domain %s",
2060             state->interface_devices[i].path,
2061             virDomainGetName(state->interface_devices[i].dom));
2062   }
2063
2064   return 0;
2065 }
2066
2067 static int lv_init_instance(size_t i, plugin_read_cb callback) {
2068   struct lv_user_data *lv_ud = &(lv_read_user_data[i]);
2069   struct lv_read_instance *inst = &(lv_ud->inst);
2070
2071   memset(lv_ud, 0, sizeof(*lv_ud));
2072
2073   snprintf(inst->tag, sizeof(inst->tag), "%s-%" PRIsz, PLUGIN_NAME, i);
2074   inst->id = i;
2075
2076   user_data_t *ud = &(lv_ud->ud);
2077   ud->data = inst;
2078   ud->free_func = NULL;
2079
2080   INFO(PLUGIN_NAME " plugin: reader %s initialized", inst->tag);
2081
2082   return plugin_register_complex_read(NULL, inst->tag, callback, 0, ud);
2083 }
2084
2085 static void lv_clean_read_state(struct lv_read_state *state) {
2086   free_block_devices(state);
2087   free_interface_devices(state);
2088   free_domains(state);
2089 }
2090
2091 static void lv_fini_instance(size_t i) {
2092   struct lv_read_instance *inst = &(lv_read_user_data[i].inst);
2093   struct lv_read_state *state = &(inst->read_state);
2094
2095   lv_clean_read_state(state);
2096
2097   INFO(PLUGIN_NAME " plugin: reader %s finalized", inst->tag);
2098 }
2099
2100 static int lv_init(void) {
2101   if (virInitialize() != 0)
2102     return -1;
2103
2104   /* event implementation must be registered before connection is opened */
2105   if (!persistent_notification)
2106     if (register_event_impl() != 0)
2107       return -1;
2108
2109   if (lv_connect() != 0)
2110     return -1;
2111
2112   DEBUG(PLUGIN_NAME " plugin: starting event loop");
2113
2114   if (!persistent_notification)
2115     if (start_event_loop() != 0)
2116       return -1;
2117
2118   DEBUG(PLUGIN_NAME " plugin: starting %i instances", nr_instances);
2119
2120   for (int i = 0; i < nr_instances; ++i)
2121     if (lv_init_instance(i, lv_read) != 0)
2122       return -1;
2123
2124   return 0;
2125 }
2126
2127 /*
2128  * returns 0 on success and <0 on error
2129  */
2130 static int lv_domain_get_tag(xmlXPathContextPtr xpath_ctx, const char *dom_name,
2131                              char *dom_tag) {
2132   char xpath_str[BUFFER_MAX_LEN] = {'\0'};
2133   xmlXPathObjectPtr xpath_obj = NULL;
2134   xmlNodePtr xml_node = NULL;
2135   int ret = -1;
2136   int err;
2137
2138   err = xmlXPathRegisterNs(xpath_ctx,
2139                            (const xmlChar *)METADATA_VM_PARTITION_PREFIX,
2140                            (const xmlChar *)METADATA_VM_PARTITION_URI);
2141   if (err) {
2142     ERROR(PLUGIN_NAME " plugin: xmlXpathRegisterNs(%s, %s) failed on domain %s",
2143           METADATA_VM_PARTITION_PREFIX, METADATA_VM_PARTITION_URI, dom_name);
2144     goto done;
2145   }
2146
2147   snprintf(xpath_str, sizeof(xpath_str), "/domain/metadata/%s:%s/text()",
2148            METADATA_VM_PARTITION_PREFIX, METADATA_VM_PARTITION_ELEMENT);
2149   xpath_obj = xmlXPathEvalExpression((xmlChar *)xpath_str, xpath_ctx);
2150   if (xpath_obj == NULL) {
2151     ERROR(PLUGIN_NAME " plugin: xmlXPathEval(%s) failed on domain %s",
2152           xpath_str, dom_name);
2153     goto done;
2154   }
2155
2156   if (xpath_obj->type != XPATH_NODESET) {
2157     ERROR(PLUGIN_NAME " plugin: xmlXPathEval(%s) unexpected return type %d "
2158                       "(wanted %d) on domain %s",
2159           xpath_str, xpath_obj->type, XPATH_NODESET, dom_name);
2160     goto done;
2161   }
2162
2163   /*
2164    * from now on there is no real error, it's ok if a domain
2165    * doesn't have the metadata partition tag.
2166    */
2167   ret = 0;
2168   if (xpath_obj->nodesetval == NULL || xpath_obj->nodesetval->nodeNr != 1) {
2169     DEBUG(PLUGIN_NAME " plugin: xmlXPathEval(%s) return nodeset size=%i "
2170                       "expected=1 on domain %s",
2171           xpath_str,
2172           (xpath_obj->nodesetval == NULL) ? 0 : xpath_obj->nodesetval->nodeNr,
2173           dom_name);
2174   } else {
2175     xml_node = xpath_obj->nodesetval->nodeTab[0];
2176     sstrncpy(dom_tag, (const char *)xml_node->content, PARTITION_TAG_MAX_LEN);
2177   }
2178
2179 done:
2180   /* deregister to clean up */
2181   err = xmlXPathRegisterNs(xpath_ctx,
2182                            (const xmlChar *)METADATA_VM_PARTITION_PREFIX, NULL);
2183   if (err) {
2184     /* we can't really recover here */
2185     ERROR(PLUGIN_NAME
2186           " plugin: deregistration of namespace %s failed for domain %s",
2187           METADATA_VM_PARTITION_PREFIX, dom_name);
2188   }
2189   if (xpath_obj)
2190     xmlXPathFreeObject(xpath_obj);
2191
2192   return ret;
2193 }
2194
2195 static int is_known_tag(const char *dom_tag) {
2196   for (int i = 0; i < nr_instances; ++i)
2197     if (!strcmp(dom_tag, lv_read_user_data[i].inst.tag))
2198       return 1;
2199   return 0;
2200 }
2201
2202 static int lv_instance_include_domain(struct lv_read_instance *inst,
2203                                       const char *dom_name,
2204                                       const char *dom_tag) {
2205   if ((dom_tag[0] != '\0') && (strcmp(dom_tag, inst->tag) == 0))
2206     return 1;
2207
2208   /* instance#0 will always be there, so it is in charge of extra duties */
2209   if (inst->id == 0) {
2210     if (dom_tag[0] == '\0' || !is_known_tag(dom_tag)) {
2211       DEBUG(PLUGIN_NAME " plugin#%s: refreshing domain %s "
2212                         "with unknown tag '%s'",
2213             inst->tag, dom_name, dom_tag);
2214       return 1;
2215     }
2216   }
2217
2218   return 0;
2219 }
2220
2221 static int refresh_lists(struct lv_read_instance *inst) {
2222   struct lv_read_state *state = &inst->read_state;
2223   int n;
2224
2225 #ifndef HAVE_LIST_ALL_DOMAINS
2226   n = virConnectNumOfDomains(conn);
2227   if (n < 0) {
2228     VIRT_ERROR(conn, "reading number of domains");
2229     return -1;
2230   }
2231 #endif
2232
2233   lv_clean_read_state(state);
2234
2235 #ifndef HAVE_LIST_ALL_DOMAINS
2236   if (n == 0)
2237     goto end;
2238 #endif
2239
2240 #ifdef HAVE_LIST_ALL_DOMAINS
2241   virDomainPtr *domains, *domains_inactive;
2242   int m = virConnectListAllDomains(conn, &domains_inactive,
2243                                    VIR_CONNECT_LIST_DOMAINS_INACTIVE);
2244   n = virConnectListAllDomains(conn, &domains, VIR_CONNECT_LIST_DOMAINS_ACTIVE);
2245 #else
2246   int *domids;
2247
2248   /* Get list of domains. */
2249   domids = calloc(n, sizeof(*domids));
2250   if (domids == NULL) {
2251     ERROR(PLUGIN_NAME " plugin: calloc failed.");
2252     return -1;
2253   }
2254
2255   n = virConnectListDomains(conn, domids, n);
2256 #endif
2257
2258   if (n < 0) {
2259     VIRT_ERROR(conn, "reading list of domains");
2260 #ifndef HAVE_LIST_ALL_DOMAINS
2261     sfree(domids);
2262 #else
2263     for (int i = 0; i < m; ++i)
2264       virDomainFree(domains_inactive[i]);
2265     sfree(domains_inactive);
2266 #endif
2267     return -1;
2268   }
2269
2270 #ifdef HAVE_LIST_ALL_DOMAINS
2271   for (int i = 0; i < m; ++i)
2272     if (add_domain(state, domains_inactive[i], 0) < 0) {
2273       ERROR(PLUGIN_NAME " plugin: malloc failed.");
2274       virDomainFree(domains_inactive[i]);
2275       domains_inactive[i] = NULL;
2276       continue;
2277     }
2278 #endif
2279
2280   /* Fetch each domain and add it to the list, unless ignore. */
2281   for (int i = 0; i < n; ++i) {
2282     const char *name;
2283     char *xml = NULL;
2284     xmlDocPtr xml_doc = NULL;
2285     xmlXPathContextPtr xpath_ctx = NULL;
2286     xmlXPathObjectPtr xpath_obj = NULL;
2287     char tag[PARTITION_TAG_MAX_LEN] = {'\0'};
2288     virDomainInfo info;
2289     int status;
2290
2291 #ifdef HAVE_LIST_ALL_DOMAINS
2292     virDomainPtr dom = domains[i];
2293 #else
2294     virDomainPtr dom = NULL;
2295     dom = virDomainLookupByID(conn, domids[i]);
2296     if (dom == NULL) {
2297       VIRT_ERROR(conn, "virDomainLookupByID");
2298       /* Could be that the domain went away -- ignore it anyway. */
2299       continue;
2300     }
2301 #endif
2302
2303     if (add_domain(state, dom, 1) < 0) {
2304       /*
2305        * When domain is already tracked, then there is
2306        * no problem with memory handling (will be freed
2307        * with the rest of domains cached data)
2308        * But in case of error like this (error occurred
2309        * before adding domain to track) we have to take
2310        * care it ourselves and call virDomainFree
2311        */
2312       ERROR(PLUGIN_NAME " plugin: malloc failed.");
2313       virDomainFree(dom);
2314       goto cont;
2315     }
2316
2317     name = virDomainGetName(dom);
2318     if (name == NULL) {
2319       VIRT_ERROR(conn, "virDomainGetName");
2320       goto cont;
2321     }
2322
2323     status = virDomainGetInfo(dom, &info);
2324     if (status != 0) {
2325       ERROR(PLUGIN_NAME " plugin: virDomainGetInfo failed with status %i.",
2326             status);
2327       continue;
2328     }
2329
2330     if (info.state != VIR_DOMAIN_RUNNING) {
2331       DEBUG(PLUGIN_NAME " plugin: skipping inactive domain %s", name);
2332       continue;
2333     }
2334
2335     if (il_domains && ignorelist_match(il_domains, name) != 0)
2336       goto cont;
2337
2338     /* Get a list of devices for this domain. */
2339     xml = virDomainGetXMLDesc(dom, 0);
2340     if (!xml) {
2341       VIRT_ERROR(conn, "virDomainGetXMLDesc");
2342       goto cont;
2343     }
2344
2345     /* Yuck, XML.  Parse out the devices. */
2346     xml_doc = xmlReadDoc((xmlChar *)xml, NULL, NULL, XML_PARSE_NONET);
2347     if (xml_doc == NULL) {
2348       VIRT_ERROR(conn, "xmlReadDoc");
2349       goto cont;
2350     }
2351
2352     xpath_ctx = xmlXPathNewContext(xml_doc);
2353
2354     if (lv_domain_get_tag(xpath_ctx, name, tag) < 0) {
2355       ERROR(PLUGIN_NAME " plugin: lv_domain_get_tag failed.");
2356       goto cont;
2357     }
2358
2359     if (!lv_instance_include_domain(inst, name, tag))
2360       goto cont;
2361
2362     /* Block devices. */
2363     const char *bd_xmlpath = "/domain/devices/disk/target[@dev]";
2364     if (blockdevice_format == source)
2365       bd_xmlpath = "/domain/devices/disk/source[@dev]";
2366     xpath_obj = xmlXPathEval((const xmlChar *)bd_xmlpath, xpath_ctx);
2367
2368     if (xpath_obj == NULL || xpath_obj->type != XPATH_NODESET ||
2369         xpath_obj->nodesetval == NULL)
2370       goto cont;
2371
2372     for (int j = 0; j < xpath_obj->nodesetval->nodeNr; ++j) {
2373       xmlNodePtr node;
2374       char *path = NULL;
2375
2376       node = xpath_obj->nodesetval->nodeTab[j];
2377       if (!node)
2378         continue;
2379       path = (char *)xmlGetProp(node, (xmlChar *)"dev");
2380       if (!path)
2381         continue;
2382
2383       if (il_block_devices &&
2384           ignore_device_match(il_block_devices, name, path) != 0)
2385         goto cont2;
2386
2387       add_block_device(state, dom, path);
2388     cont2:
2389       if (path)
2390         xmlFree(path);
2391     }
2392     xmlXPathFreeObject(xpath_obj);
2393
2394     /* Network interfaces. */
2395     xpath_obj = xmlXPathEval(
2396         (xmlChar *)"/domain/devices/interface[target[@dev]]", xpath_ctx);
2397     if (xpath_obj == NULL || xpath_obj->type != XPATH_NODESET ||
2398         xpath_obj->nodesetval == NULL)
2399       goto cont;
2400
2401     xmlNodeSetPtr xml_interfaces = xpath_obj->nodesetval;
2402
2403     for (int j = 0; j < xml_interfaces->nodeNr; ++j) {
2404       char *path = NULL;
2405       char *address = NULL;
2406       xmlNodePtr xml_interface;
2407
2408       xml_interface = xml_interfaces->nodeTab[j];
2409       if (!xml_interface)
2410         continue;
2411
2412       for (xmlNodePtr child = xml_interface->children; child;
2413            child = child->next) {
2414         if (child->type != XML_ELEMENT_NODE)
2415           continue;
2416
2417         if (xmlStrEqual(child->name, (const xmlChar *)"target")) {
2418           path = (char *)xmlGetProp(child, (const xmlChar *)"dev");
2419           if (!path)
2420             continue;
2421         } else if (xmlStrEqual(child->name, (const xmlChar *)"mac")) {
2422           address = (char *)xmlGetProp(child, (const xmlChar *)"address");
2423           if (!address)
2424             continue;
2425         }
2426       }
2427
2428       if (il_interface_devices &&
2429           (ignore_device_match(il_interface_devices, name, path) != 0 ||
2430            ignore_device_match(il_interface_devices, name, address) != 0))
2431         goto cont3;
2432
2433       add_interface_device(state, dom, path, address, j + 1);
2434     cont3:
2435       if (path)
2436         xmlFree(path);
2437       if (address)
2438         xmlFree(address);
2439     }
2440
2441   cont:
2442     if (xpath_obj)
2443       xmlXPathFreeObject(xpath_obj);
2444     if (xpath_ctx)
2445       xmlXPathFreeContext(xpath_ctx);
2446     if (xml_doc)
2447       xmlFreeDoc(xml_doc);
2448     sfree(xml);
2449   }
2450
2451 #ifdef HAVE_LIST_ALL_DOMAINS
2452   /* NOTE: domains_active and domains_inactive data will be cleared during
2453      refresh of all domains (inside lv_clean_read_state function) so we need
2454      to free here only allocated arrays */
2455   sfree(domains);
2456   sfree(domains_inactive);
2457 #else
2458   sfree(domids);
2459
2460 end:
2461 #endif
2462
2463   DEBUG(PLUGIN_NAME " plugin#%s: refreshing"
2464                     " domains=%i block_devices=%i iface_devices=%i",
2465         inst->tag, state->nr_domains, state->nr_block_devices,
2466         state->nr_interface_devices);
2467
2468   return 0;
2469 }
2470
2471 static void free_domains(struct lv_read_state *state) {
2472   if (state->domains) {
2473     for (int i = 0; i < state->nr_domains; ++i)
2474       virDomainFree(state->domains[i].ptr);
2475     sfree(state->domains);
2476   }
2477   state->domains = NULL;
2478   state->nr_domains = 0;
2479 }
2480
2481 static int add_domain(struct lv_read_state *state, virDomainPtr dom,
2482                       _Bool active) {
2483   domain_t *new_ptr;
2484   int new_size = sizeof(state->domains[0]) * (state->nr_domains + 1);
2485
2486   if (state->domains)
2487     new_ptr = realloc(state->domains, new_size);
2488   else
2489     new_ptr = malloc(new_size);
2490
2491   if (new_ptr == NULL)
2492     return -1;
2493
2494   state->domains = new_ptr;
2495   state->domains[state->nr_domains].ptr = dom;
2496   state->domains[state->nr_domains].active = active;
2497   memset(&state->domains[state->nr_domains].info, 0,
2498          sizeof(state->domains[state->nr_domains].info));
2499
2500   return state->nr_domains++;
2501 }
2502
2503 static void free_block_devices(struct lv_read_state *state) {
2504   if (state->block_devices) {
2505     for (int i = 0; i < state->nr_block_devices; ++i)
2506       sfree(state->block_devices[i].path);
2507     sfree(state->block_devices);
2508   }
2509   state->block_devices = NULL;
2510   state->nr_block_devices = 0;
2511 }
2512
2513 static int add_block_device(struct lv_read_state *state, virDomainPtr dom,
2514                             const char *path) {
2515   struct block_device *new_ptr;
2516   int new_size =
2517       sizeof(state->block_devices[0]) * (state->nr_block_devices + 1);
2518   char *path_copy;
2519
2520   path_copy = strdup(path);
2521   if (!path_copy)
2522     return -1;
2523
2524   if (state->block_devices)
2525     new_ptr = realloc(state->block_devices, new_size);
2526   else
2527     new_ptr = malloc(new_size);
2528
2529   if (new_ptr == NULL) {
2530     sfree(path_copy);
2531     return -1;
2532   }
2533   state->block_devices = new_ptr;
2534   state->block_devices[state->nr_block_devices].dom = dom;
2535   state->block_devices[state->nr_block_devices].path = path_copy;
2536   return state->nr_block_devices++;
2537 }
2538
2539 static void free_interface_devices(struct lv_read_state *state) {
2540   if (state->interface_devices) {
2541     for (int i = 0; i < state->nr_interface_devices; ++i) {
2542       sfree(state->interface_devices[i].path);
2543       sfree(state->interface_devices[i].address);
2544       sfree(state->interface_devices[i].number);
2545     }
2546     sfree(state->interface_devices);
2547   }
2548   state->interface_devices = NULL;
2549   state->nr_interface_devices = 0;
2550 }
2551
2552 static int add_interface_device(struct lv_read_state *state, virDomainPtr dom,
2553                                 const char *path, const char *address,
2554                                 unsigned int number) {
2555   struct interface_device *new_ptr;
2556   int new_size =
2557       sizeof(state->interface_devices[0]) * (state->nr_interface_devices + 1);
2558   char *path_copy, *address_copy, number_string[15];
2559
2560   if ((path == NULL) || (address == NULL))
2561     return EINVAL;
2562
2563   path_copy = strdup(path);
2564   if (!path_copy)
2565     return -1;
2566
2567   address_copy = strdup(address);
2568   if (!address_copy) {
2569     sfree(path_copy);
2570     return -1;
2571   }
2572
2573   snprintf(number_string, sizeof(number_string), "interface-%u", number);
2574
2575   if (state->interface_devices)
2576     new_ptr = realloc(state->interface_devices, new_size);
2577   else
2578     new_ptr = malloc(new_size);
2579
2580   if (new_ptr == NULL) {
2581     sfree(path_copy);
2582     sfree(address_copy);
2583     return -1;
2584   }
2585   state->interface_devices = new_ptr;
2586   state->interface_devices[state->nr_interface_devices].dom = dom;
2587   state->interface_devices[state->nr_interface_devices].path = path_copy;
2588   state->interface_devices[state->nr_interface_devices].address = address_copy;
2589   state->interface_devices[state->nr_interface_devices].number =
2590       strdup(number_string);
2591   return state->nr_interface_devices++;
2592 }
2593
2594 static int ignore_device_match(ignorelist_t *il, const char *domname,
2595                                const char *devpath) {
2596   char *name;
2597   int n, r;
2598
2599   if ((domname == NULL) || (devpath == NULL))
2600     return 0;
2601
2602   n = strlen(domname) + strlen(devpath) + 2;
2603   name = malloc(n);
2604   if (name == NULL) {
2605     ERROR(PLUGIN_NAME " plugin: malloc failed.");
2606     return 0;
2607   }
2608   snprintf(name, n, "%s:%s", domname, devpath);
2609   r = ignorelist_match(il, name);
2610   sfree(name);
2611   return r;
2612 }
2613
2614 static int lv_shutdown(void) {
2615   for (int i = 0; i < nr_instances; ++i) {
2616     lv_fini_instance(i);
2617   }
2618
2619   DEBUG(PLUGIN_NAME " plugin: stopping event loop");
2620
2621   if (!persistent_notification)
2622     stop_event_loop();
2623
2624   lv_disconnect();
2625
2626   ignorelist_free(il_domains);
2627   il_domains = NULL;
2628   ignorelist_free(il_block_devices);
2629   il_block_devices = NULL;
2630   ignorelist_free(il_interface_devices);
2631   il_interface_devices = NULL;
2632
2633   return 0;
2634 }
2635
2636 void module_register(void) {
2637   plugin_register_config(PLUGIN_NAME, lv_config, config_keys, NR_CONFIG_KEYS);
2638   plugin_register_init(PLUGIN_NAME, lv_init);
2639   plugin_register_shutdown(PLUGIN_NAME, lv_shutdown);
2640 }