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