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