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