Merge pull request #2808 from elfiesmelfie/fix_virt_shadow_warning
[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;
472 static char *conn_string;
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;
483 /* List of block devices, if specified. */
484 static ignorelist_t *il_block_devices;
485 /* List of network interface devices, if specified. */
486 static ignorelist_t *il_interface_devices;
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;
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) || ((size_t)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) ||
1028       ((size_t)reason >= STATIC_ARRAY_SIZE(domain_reasons[0]))) {
1029     ERROR(PLUGIN_NAME ": Array index out of bounds: reason=%d", reason);
1030     return;
1031   }
1032
1033   const char *reason_str = domain_reasons[state][reason];
1034   /* Array size for domain reasons is fixed, but different domain states can
1035    * have different number of reasons. We need to check if reason was
1036    * successfully parsed */
1037   if (!reason_str) {
1038     ERROR(PLUGIN_NAME ": Invalid reason (%d) for domain state: %s", reason,
1039           state_str);
1040     return;
1041   }
1042 #else
1043   const char *reason_str = "N/A";
1044 #endif
1045
1046   snprintf(msg, sizeof(msg), "Domain state: %s. Reason: %s", state_str,
1047            reason_str);
1048
1049   int severity;
1050   switch (state) {
1051   case VIR_DOMAIN_NOSTATE:
1052   case VIR_DOMAIN_RUNNING:
1053   case VIR_DOMAIN_SHUTDOWN:
1054   case VIR_DOMAIN_SHUTOFF:
1055     severity = NOTIF_OKAY;
1056     break;
1057   case VIR_DOMAIN_BLOCKED:
1058   case VIR_DOMAIN_PAUSED:
1059 #ifdef DOM_STATE_PMSUSPENDED
1060   case VIR_DOMAIN_PMSUSPENDED:
1061 #endif
1062     severity = NOTIF_WARNING;
1063     break;
1064   case VIR_DOMAIN_CRASHED:
1065     severity = NOTIF_FAILURE;
1066     break;
1067   default:
1068     ERROR(PLUGIN_NAME ": Unrecognized domain state (%d)", state);
1069     return;
1070   }
1071   submit_notif(dom, severity, msg, "domain_state", NULL);
1072 }
1073
1074 static int lv_config(const char *key, const char *value) {
1075   if (virInitialize() != 0)
1076     return 1;
1077
1078   if (il_domains == NULL)
1079     il_domains = ignorelist_create(1);
1080   if (il_block_devices == NULL)
1081     il_block_devices = ignorelist_create(1);
1082   if (il_interface_devices == NULL)
1083     il_interface_devices = ignorelist_create(1);
1084
1085   if (strcasecmp(key, "Connection") == 0) {
1086     char *tmp = strdup(value);
1087     if (tmp == NULL) {
1088       ERROR(PLUGIN_NAME " plugin: Connection strdup failed.");
1089       return 1;
1090     }
1091     sfree(conn_string);
1092     conn_string = tmp;
1093     return 0;
1094   }
1095
1096   if (strcasecmp(key, "RefreshInterval") == 0) {
1097     char *eptr = NULL;
1098     interval = strtol(value, &eptr, 10);
1099     if (eptr == NULL || *eptr != '\0')
1100       return 1;
1101     return 0;
1102   }
1103
1104   if (strcasecmp(key, "Domain") == 0) {
1105     if (ignorelist_add(il_domains, value))
1106       return 1;
1107     return 0;
1108   }
1109   if (strcasecmp(key, "BlockDevice") == 0) {
1110     if (ignorelist_add(il_block_devices, value))
1111       return 1;
1112     return 0;
1113   }
1114
1115   if (strcasecmp(key, "BlockDeviceFormat") == 0) {
1116     if (strcasecmp(value, "target") == 0)
1117       blockdevice_format = target;
1118     else if (strcasecmp(value, "source") == 0)
1119       blockdevice_format = source;
1120     else {
1121       ERROR(PLUGIN_NAME " plugin: unknown BlockDeviceFormat: %s", value);
1122       return -1;
1123     }
1124     return 0;
1125   }
1126   if (strcasecmp(key, "BlockDeviceFormatBasename") == 0) {
1127     blockdevice_format_basename = IS_TRUE(value) ? true : false;
1128     return 0;
1129   }
1130   if (strcasecmp(key, "InterfaceDevice") == 0) {
1131     if (ignorelist_add(il_interface_devices, value))
1132       return 1;
1133     return 0;
1134   }
1135
1136   if (strcasecmp(key, "IgnoreSelected") == 0) {
1137     if (IS_TRUE(value)) {
1138       ignorelist_set_invert(il_domains, 0);
1139       ignorelist_set_invert(il_block_devices, 0);
1140       ignorelist_set_invert(il_interface_devices, 0);
1141     } else {
1142       ignorelist_set_invert(il_domains, 1);
1143       ignorelist_set_invert(il_block_devices, 1);
1144       ignorelist_set_invert(il_interface_devices, 1);
1145     }
1146     return 0;
1147   }
1148
1149   if (strcasecmp(key, "HostnameFormat") == 0) {
1150     char *value_copy;
1151     char *fields[HF_MAX_FIELDS];
1152     int n;
1153
1154     value_copy = strdup(value);
1155     if (value_copy == NULL) {
1156       ERROR(PLUGIN_NAME " plugin: strdup failed.");
1157       return -1;
1158     }
1159
1160     n = strsplit(value_copy, fields, HF_MAX_FIELDS);
1161     if (n < 1) {
1162       sfree(value_copy);
1163       ERROR(PLUGIN_NAME " plugin: HostnameFormat: no fields");
1164       return -1;
1165     }
1166
1167     for (int i = 0; i < n; ++i) {
1168       if (strcasecmp(fields[i], "hostname") == 0)
1169         hostname_format[i] = hf_hostname;
1170       else if (strcasecmp(fields[i], "name") == 0)
1171         hostname_format[i] = hf_name;
1172       else if (strcasecmp(fields[i], "uuid") == 0)
1173         hostname_format[i] = hf_uuid;
1174       else {
1175         ERROR(PLUGIN_NAME " plugin: unknown HostnameFormat field: %s",
1176               fields[i]);
1177         sfree(value_copy);
1178         return -1;
1179       }
1180     }
1181     sfree(value_copy);
1182
1183     for (int i = n; i < HF_MAX_FIELDS; ++i)
1184       hostname_format[i] = hf_none;
1185
1186     return 0;
1187   }
1188
1189   if (strcasecmp(key, "PluginInstanceFormat") == 0) {
1190     char *value_copy;
1191     char *fields[PLGINST_MAX_FIELDS];
1192     int n;
1193
1194     value_copy = strdup(value);
1195     if (value_copy == NULL) {
1196       ERROR(PLUGIN_NAME " plugin: strdup failed.");
1197       return -1;
1198     }
1199
1200     n = strsplit(value_copy, fields, PLGINST_MAX_FIELDS);
1201     if (n < 1) {
1202       sfree(value_copy);
1203       ERROR(PLUGIN_NAME " plugin: PluginInstanceFormat: no fields");
1204       return -1;
1205     }
1206
1207     for (int i = 0; i < n; ++i) {
1208       if (strcasecmp(fields[i], "none") == 0) {
1209         plugin_instance_format[i] = plginst_none;
1210         break;
1211       } else if (strcasecmp(fields[i], "name") == 0)
1212         plugin_instance_format[i] = plginst_name;
1213       else if (strcasecmp(fields[i], "uuid") == 0)
1214         plugin_instance_format[i] = plginst_uuid;
1215       else {
1216         ERROR(PLUGIN_NAME " plugin: unknown PluginInstanceFormat field: %s",
1217               fields[i]);
1218         sfree(value_copy);
1219         return -1;
1220       }
1221     }
1222     sfree(value_copy);
1223
1224     for (int i = n; i < PLGINST_MAX_FIELDS; ++i)
1225       plugin_instance_format[i] = plginst_none;
1226
1227     return 0;
1228   }
1229
1230   if (strcasecmp(key, "InterfaceFormat") == 0) {
1231     if (strcasecmp(value, "name") == 0)
1232       interface_format = if_name;
1233     else if (strcasecmp(value, "address") == 0)
1234       interface_format = if_address;
1235     else if (strcasecmp(value, "number") == 0)
1236       interface_format = if_number;
1237     else {
1238       ERROR(PLUGIN_NAME " plugin: unknown InterfaceFormat: %s", value);
1239       return -1;
1240     }
1241     return 0;
1242   }
1243
1244   if (strcasecmp(key, "Instances") == 0) {
1245     char *eptr = NULL;
1246     double val = strtod(value, &eptr);
1247
1248     if (*eptr != '\0') {
1249       ERROR(PLUGIN_NAME " plugin: Invalid value for Instances = '%s'", value);
1250       return 1;
1251     }
1252     if (val <= 0) {
1253       ERROR(PLUGIN_NAME " plugin: Instances <= 0 makes no sense.");
1254       return 1;
1255     }
1256     if (val > NR_INSTANCES_MAX) {
1257       ERROR(PLUGIN_NAME " plugin: Instances=%f > NR_INSTANCES_MAX=%i"
1258                         " use a lower setting or recompile the plugin.",
1259             val, NR_INSTANCES_MAX);
1260       return 1;
1261     }
1262
1263     nr_instances = (int)val;
1264     DEBUG(PLUGIN_NAME " plugin: configured %i instances", nr_instances);
1265     return 0;
1266   }
1267
1268   if (strcasecmp(key, "ExtraStats") == 0) {
1269     char *localvalue = strdup(value);
1270     if (localvalue != NULL) {
1271       char *exstats[EX_STATS_MAX_FIELDS];
1272       int numexstats =
1273           strsplit(localvalue, exstats, STATIC_ARRAY_SIZE(exstats));
1274       extra_stats = parse_ex_stats_flags(exstats, numexstats);
1275       sfree(localvalue);
1276
1277 #ifdef HAVE_JOB_STATS
1278       if ((extra_stats & ex_stats_job_stats_completed) &&
1279           (extra_stats & ex_stats_job_stats_background)) {
1280         ERROR(PLUGIN_NAME " plugin: Invalid job stats configuration. Only one "
1281                           "type of job statistics can be collected at the same "
1282                           "time");
1283         return 1;
1284       }
1285 #endif
1286     }
1287   }
1288
1289   if (strcasecmp(key, "PersistentNotification") == 0) {
1290     persistent_notification = IS_TRUE(value);
1291     return 0;
1292   }
1293
1294   /* Unrecognised option. */
1295   return -1;
1296 }
1297
1298 static int lv_connect(void) {
1299   if (conn == NULL) {
1300 /* `conn_string == NULL' is acceptable */
1301 #ifdef HAVE_FS_INFO
1302     /* virDomainGetFSInfo requires full read-write access connection */
1303     if (extra_stats & ex_stats_fs_info)
1304       conn = virConnectOpen(conn_string);
1305     else
1306 #endif
1307       conn = virConnectOpenReadOnly(conn_string);
1308     if (conn == NULL) {
1309       c_complain(LOG_ERR, &conn_complain,
1310                  PLUGIN_NAME " plugin: Unable to connect: "
1311                              "virConnectOpen failed.");
1312       return -1;
1313     }
1314     int status = virNodeGetInfo(conn, &nodeinfo);
1315     if (status != 0) {
1316       ERROR(PLUGIN_NAME ": virNodeGetInfo failed");
1317       return -1;
1318     }
1319   }
1320   c_release(LOG_NOTICE, &conn_complain,
1321             PLUGIN_NAME " plugin: Connection established.");
1322   return 0;
1323 }
1324
1325 static void lv_disconnect(void) {
1326   if (conn != NULL)
1327     virConnectClose(conn);
1328   conn = NULL;
1329   WARNING(PLUGIN_NAME " plugin: closed connection to libvirt");
1330 }
1331
1332 static int lv_domain_block_info(virDomainPtr dom, const char *path,
1333                                 struct lv_block_info *binfo) {
1334 #ifdef HAVE_BLOCK_STATS_FLAGS
1335   int nparams = 0;
1336   if (virDomainBlockStatsFlags(dom, path, NULL, &nparams, 0) < 0 ||
1337       nparams <= 0) {
1338     VIRT_ERROR(conn, "getting the disk params count");
1339     return -1;
1340   }
1341
1342   virTypedParameterPtr params = calloc((size_t)nparams, sizeof(*params));
1343   if (params == NULL) {
1344     ERROR("virt plugin: alloc(%i) for block=%s parameters failed.", nparams,
1345           path);
1346     return -1;
1347   }
1348
1349   int rc = -1;
1350   if (virDomainBlockStatsFlags(dom, path, params, &nparams, 0) < 0) {
1351     VIRT_ERROR(conn, "getting the disk params values");
1352   } else {
1353     rc = get_block_info(binfo, params, nparams);
1354   }
1355
1356   virTypedParamsClear(params, nparams);
1357   sfree(params);
1358   return rc;
1359 #else
1360   return virDomainBlockStats(dom, path, &(binfo->bi), sizeof(binfo->bi));
1361 #endif /* HAVE_BLOCK_STATS_FLAGS */
1362 }
1363
1364 #ifdef HAVE_PERF_STATS
1365 static void perf_submit(virDomainStatsRecordPtr stats) {
1366   for (int i = 0; i < stats->nparams; ++i) {
1367     /* Replace '.' with '_' in event field to match other metrics' naming
1368      * convention */
1369     char *c = strchr(stats->params[i].field, '.');
1370     if (c)
1371       *c = '_';
1372     submit(stats->dom, "perf", stats->params[i].field,
1373            &(value_t){.derive = stats->params[i].value.ul}, 1);
1374   }
1375 }
1376
1377 static int get_perf_events(virDomainPtr domain) {
1378   virDomainStatsRecordPtr *stats = NULL;
1379   /* virDomainListGetStats requires a NULL terminated list of domains */
1380   virDomainPtr domain_array[] = {domain, NULL};
1381
1382   int status =
1383       virDomainListGetStats(domain_array, VIR_DOMAIN_STATS_PERF, &stats, 0);
1384   if (status == -1) {
1385     ERROR("virt plugin: virDomainListGetStats failed with status %i.", status);
1386     return status;
1387   }
1388
1389   for (int i = 0; i < status; ++i)
1390     perf_submit(stats[i]);
1391
1392   virDomainStatsRecordListFree(stats);
1393   return 0;
1394 }
1395 #endif /* HAVE_PERF_STATS */
1396
1397 static void vcpu_pin_submit(virDomainPtr dom, int max_cpus, int vcpu,
1398                             unsigned char *cpu_maps, int cpu_map_len) {
1399   for (int cpu = 0; cpu < max_cpus; ++cpu) {
1400     char type_instance[DATA_MAX_NAME_LEN];
1401     bool is_set = VIR_CPU_USABLE(cpu_maps, cpu_map_len, vcpu, cpu);
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 (size_t _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 (size_t 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 con_,
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   pthread_mutex_lock(&thread_data->active_mutex);
1903   thread_data->is_active = false;
1904   pthread_mutex_unlock(&thread_data->active_mutex);
1905
1906   return 0;
1907 }
1908
1909 /* register domain event callback and start event loop thread */
1910 static int start_event_loop(virt_notif_thread_t *thread_data) {
1911   assert(thread_data != NULL);
1912   thread_data->domain_event_cb_id = virConnectDomainEventRegisterAny(
1913       conn, NULL, VIR_DOMAIN_EVENT_ID_LIFECYCLE,
1914       VIR_DOMAIN_EVENT_CALLBACK(domain_lifecycle_event_cb), NULL, NULL);
1915   if (thread_data->domain_event_cb_id == -1) {
1916     ERROR(PLUGIN_NAME " plugin: error while callback registering");
1917     return -1;
1918   }
1919
1920   virt_notif_thread_set_active(thread_data, 1);
1921   if (pthread_create(&thread_data->event_loop_tid, NULL, event_loop_worker,
1922                      thread_data)) {
1923     ERROR(PLUGIN_NAME " plugin: failed event loop thread creation");
1924     virConnectDomainEventDeregisterAny(conn, thread_data->domain_event_cb_id);
1925     return -1;
1926   }
1927
1928   return 0;
1929 }
1930
1931 /* stop event loop thread and deregister callback */
1932 static void stop_event_loop(virt_notif_thread_t *thread_data) {
1933   /* stopping loop and de-registering event handler*/
1934   virt_notif_thread_set_active(thread_data, 0);
1935   if (conn != NULL && thread_data->domain_event_cb_id != -1)
1936     virConnectDomainEventDeregisterAny(conn, thread_data->domain_event_cb_id);
1937
1938   if (pthread_join(notif_thread.event_loop_tid, NULL) != 0)
1939     ERROR(PLUGIN_NAME " plugin: stopping notification thread failed");
1940 }
1941
1942 static int persistent_domains_state_notification(void) {
1943   int status = 0;
1944   int n;
1945 #ifdef HAVE_LIST_ALL_DOMAINS
1946   virDomainPtr *domains = NULL;
1947   n = virConnectListAllDomains(conn, &domains,
1948                                VIR_CONNECT_LIST_DOMAINS_PERSISTENT);
1949   if (n < 0) {
1950     VIRT_ERROR(conn, "reading list of persistent domains");
1951     status = -1;
1952   } else {
1953     DEBUG(PLUGIN_NAME " plugin: getting state of %i persistent domains", n);
1954     /* Fetch each persistent domain's state and notify it */
1955     int n_notified = n;
1956     for (int i = 0; i < n; ++i) {
1957       status = get_domain_state_notify(domains[i]);
1958       if (status != 0) {
1959         n_notified--;
1960         ERROR(PLUGIN_NAME " plugin: could not notify state of domain %s",
1961               virDomainGetName(domains[i]));
1962       }
1963       virDomainFree(domains[i]);
1964     }
1965
1966     sfree(domains);
1967     DEBUG(PLUGIN_NAME " plugin: notified state of %i persistent domains",
1968           n_notified);
1969   }
1970 #else
1971   n = virConnectNumOfDomains(conn);
1972   if (n > 0) {
1973     int *domids;
1974     /* Get list of domains. */
1975     domids = calloc(n, sizeof(*domids));
1976     if (domids == NULL) {
1977       ERROR(PLUGIN_NAME " plugin: calloc failed.");
1978       return -1;
1979     }
1980     n = virConnectListDomains(conn, domids, n);
1981     if (n < 0) {
1982       VIRT_ERROR(conn, "reading list of domains");
1983       sfree(domids);
1984       return -1;
1985     }
1986     /* Fetch info of each active domain and notify it */
1987     for (int i = 0; i < n; ++i) {
1988       virDomainInfo info;
1989       virDomainPtr dom = NULL;
1990       dom = virDomainLookupByID(conn, domids[i]);
1991       if (dom == NULL) {
1992         VIRT_ERROR(conn, "virDomainLookupByID");
1993         /* Could be that the domain went away -- ignore it anyway. */
1994         continue;
1995       }
1996       status = virDomainGetInfo(dom, &info);
1997       if (status == 0)
1998         /* virDomainGetState is not available. Submit 0, which corresponds to
1999          * unknown reason. */
2000         domain_state_submit_notif(dom, info.state, 0);
2001       else
2002         ERROR(PLUGIN_NAME " plugin: virDomainGetInfo failed with status %i.",
2003               status);
2004
2005       virDomainFree(dom);
2006     }
2007     sfree(domids);
2008   }
2009 #endif
2010
2011   return status;
2012 }
2013
2014 static int lv_read(user_data_t *ud) {
2015   time_t t;
2016   struct lv_read_instance *inst = NULL;
2017   struct lv_read_state *state = NULL;
2018
2019   if (ud->data == NULL) {
2020     ERROR(PLUGIN_NAME " plugin: NULL userdata");
2021     return -1;
2022   }
2023
2024   inst = ud->data;
2025   state = &inst->read_state;
2026
2027   bool reconnect = conn == NULL ? true : false;
2028   /* event implementation must be registered before connection is opened */
2029   if (inst->id == 0) {
2030     if (!persistent_notification && reconnect)
2031       if (register_event_impl() != 0)
2032         return -1;
2033
2034     if (lv_connect() < 0)
2035       return -1;
2036
2037     if (!persistent_notification && reconnect && conn != NULL)
2038       if (start_event_loop(&notif_thread) != 0)
2039         return -1;
2040   }
2041
2042   time(&t);
2043
2044   /* Need to refresh domain or device lists? */
2045   if ((last_refresh == (time_t)0) ||
2046       ((interval > 0) && ((last_refresh + interval) <= t))) {
2047     if (refresh_lists(inst) != 0) {
2048       if (inst->id == 0) {
2049         if (!persistent_notification)
2050           stop_event_loop(&notif_thread);
2051         lv_disconnect();
2052       }
2053       return -1;
2054     }
2055     last_refresh = t;
2056   }
2057
2058   /* persistent domains state notifications are handled by instance 0 */
2059   if (inst->id == 0 && persistent_notification) {
2060     int status = persistent_domains_state_notification();
2061     if (status != 0)
2062       DEBUG(PLUGIN_NAME " plugin: persistent_domains_state_notifications "
2063                         "returned with status %i",
2064             status);
2065   }
2066
2067 #if COLLECT_DEBUG
2068   for (int i = 0; i < state->nr_domains; ++i)
2069     DEBUG(PLUGIN_NAME " plugin: domain %s",
2070           virDomainGetName(state->domains[i].ptr));
2071   for (int i = 0; i < state->nr_block_devices; ++i)
2072     DEBUG(PLUGIN_NAME " plugin: block device %d %s:%s", i,
2073           virDomainGetName(state->block_devices[i].dom),
2074           state->block_devices[i].path);
2075   for (int i = 0; i < state->nr_interface_devices; ++i)
2076     DEBUG(PLUGIN_NAME " plugin: interface device %d %s:%s", i,
2077           virDomainGetName(state->interface_devices[i].dom),
2078           state->interface_devices[i].path);
2079 #endif
2080
2081   /* Get domains' metrics */
2082   for (int i = 0; i < state->nr_domains; ++i) {
2083     domain_t *dom = &state->domains[i];
2084     int status = 0;
2085     if (dom->active)
2086       status = get_domain_metrics(dom);
2087 #ifdef HAVE_DOM_REASON
2088     else
2089       status = get_domain_state(dom->ptr);
2090 #endif
2091
2092     if (status != 0)
2093       ERROR(PLUGIN_NAME " failed to get metrics for domain=%s",
2094             virDomainGetName(dom->ptr));
2095   }
2096
2097   /* Get block device stats for each domain. */
2098   for (int i = 0; i < state->nr_block_devices; ++i) {
2099     int status = get_block_stats(&state->block_devices[i]);
2100     if (status != 0)
2101       ERROR(PLUGIN_NAME
2102             " failed to get stats for block device (%s) in domain %s",
2103             state->block_devices[i].path,
2104             virDomainGetName(state->domains[i].ptr));
2105   }
2106
2107   /* Get interface stats for each domain. */
2108   for (int i = 0; i < state->nr_interface_devices; ++i) {
2109     int status = get_if_dev_stats(&state->interface_devices[i]);
2110     if (status != 0)
2111       ERROR(PLUGIN_NAME
2112             " failed to get interface stats for device (%s) in domain %s",
2113             state->interface_devices[i].path,
2114             virDomainGetName(state->interface_devices[i].dom));
2115   }
2116
2117   return 0;
2118 }
2119
2120 static int lv_init_instance(size_t i, plugin_read_cb callback) {
2121   struct lv_user_data *lv_ud = &(lv_read_user_data[i]);
2122   struct lv_read_instance *inst = &(lv_ud->inst);
2123
2124   memset(lv_ud, 0, sizeof(*lv_ud));
2125
2126   snprintf(inst->tag, sizeof(inst->tag), "%s-%" PRIsz, PLUGIN_NAME, i);
2127   inst->id = i;
2128
2129   user_data_t *ud = &(lv_ud->ud);
2130   ud->data = inst;
2131   ud->free_func = NULL;
2132
2133   INFO(PLUGIN_NAME " plugin: reader %s initialized", inst->tag);
2134
2135   return plugin_register_complex_read(NULL, inst->tag, callback, 0, ud);
2136 }
2137
2138 static void lv_clean_read_state(struct lv_read_state *state) {
2139   free_block_devices(state);
2140   free_interface_devices(state);
2141   free_domains(state);
2142 }
2143
2144 static void lv_fini_instance(size_t i) {
2145   struct lv_read_instance *inst = &(lv_read_user_data[i].inst);
2146   struct lv_read_state *state = &(inst->read_state);
2147
2148   lv_clean_read_state(state);
2149
2150   INFO(PLUGIN_NAME " plugin: reader %s finalized", inst->tag);
2151 }
2152
2153 static int lv_init(void) {
2154   if (virInitialize() != 0)
2155     return -1;
2156
2157   /* event implementation must be registered before connection is opened */
2158   if (!persistent_notification)
2159     if (register_event_impl() != 0)
2160       return -1;
2161
2162   if (lv_connect() != 0)
2163     return -1;
2164
2165   DEBUG(PLUGIN_NAME " plugin: starting event loop");
2166
2167   if (!persistent_notification) {
2168     virt_notif_thread_init(&notif_thread);
2169     if (start_event_loop(&notif_thread) != 0)
2170       return -1;
2171   }
2172
2173   DEBUG(PLUGIN_NAME " plugin: starting %i instances", nr_instances);
2174
2175   for (int i = 0; i < nr_instances; ++i)
2176     if (lv_init_instance(i, lv_read) != 0)
2177       return -1;
2178
2179   return 0;
2180 }
2181
2182 /*
2183  * returns 0 on success and <0 on error
2184  */
2185 static int lv_domain_get_tag(xmlXPathContextPtr xpath_ctx, const char *dom_name,
2186                              char *dom_tag) {
2187   char xpath_str[BUFFER_MAX_LEN] = {'\0'};
2188   xmlXPathObjectPtr xpath_obj = NULL;
2189   xmlNodePtr xml_node = NULL;
2190   int ret = -1;
2191   int err;
2192
2193   err = xmlXPathRegisterNs(xpath_ctx,
2194                            (const xmlChar *)METADATA_VM_PARTITION_PREFIX,
2195                            (const xmlChar *)METADATA_VM_PARTITION_URI);
2196   if (err) {
2197     ERROR(PLUGIN_NAME " plugin: xmlXpathRegisterNs(%s, %s) failed on domain %s",
2198           METADATA_VM_PARTITION_PREFIX, METADATA_VM_PARTITION_URI, dom_name);
2199     goto done;
2200   }
2201
2202   snprintf(xpath_str, sizeof(xpath_str), "/domain/metadata/%s:%s/text()",
2203            METADATA_VM_PARTITION_PREFIX, METADATA_VM_PARTITION_ELEMENT);
2204   xpath_obj = xmlXPathEvalExpression((xmlChar *)xpath_str, xpath_ctx);
2205   if (xpath_obj == NULL) {
2206     ERROR(PLUGIN_NAME " plugin: xmlXPathEval(%s) failed on domain %s",
2207           xpath_str, dom_name);
2208     goto done;
2209   }
2210
2211   if (xpath_obj->type != XPATH_NODESET) {
2212     ERROR(PLUGIN_NAME " plugin: xmlXPathEval(%s) unexpected return type %d "
2213                       "(wanted %d) on domain %s",
2214           xpath_str, xpath_obj->type, XPATH_NODESET, dom_name);
2215     goto done;
2216   }
2217
2218   /*
2219    * from now on there is no real error, it's ok if a domain
2220    * doesn't have the metadata partition tag.
2221    */
2222   ret = 0;
2223   if (xpath_obj->nodesetval == NULL || xpath_obj->nodesetval->nodeNr != 1) {
2224     DEBUG(PLUGIN_NAME " plugin: xmlXPathEval(%s) return nodeset size=%i "
2225                       "expected=1 on domain %s",
2226           xpath_str,
2227           (xpath_obj->nodesetval == NULL) ? 0 : xpath_obj->nodesetval->nodeNr,
2228           dom_name);
2229   } else {
2230     xml_node = xpath_obj->nodesetval->nodeTab[0];
2231     sstrncpy(dom_tag, (const char *)xml_node->content, PARTITION_TAG_MAX_LEN);
2232   }
2233
2234 done:
2235   /* deregister to clean up */
2236   err = xmlXPathRegisterNs(xpath_ctx,
2237                            (const xmlChar *)METADATA_VM_PARTITION_PREFIX, NULL);
2238   if (err) {
2239     /* we can't really recover here */
2240     ERROR(PLUGIN_NAME
2241           " plugin: deregistration of namespace %s failed for domain %s",
2242           METADATA_VM_PARTITION_PREFIX, dom_name);
2243   }
2244   if (xpath_obj)
2245     xmlXPathFreeObject(xpath_obj);
2246
2247   return ret;
2248 }
2249
2250 static int is_known_tag(const char *dom_tag) {
2251   for (int i = 0; i < nr_instances; ++i)
2252     if (!strcmp(dom_tag, lv_read_user_data[i].inst.tag))
2253       return 1;
2254   return 0;
2255 }
2256
2257 static int lv_instance_include_domain(struct lv_read_instance *inst,
2258                                       const char *dom_name,
2259                                       const char *dom_tag) {
2260   if ((dom_tag[0] != '\0') && (strcmp(dom_tag, inst->tag) == 0))
2261     return 1;
2262
2263   /* instance#0 will always be there, so it is in charge of extra duties */
2264   if (inst->id == 0) {
2265     if (dom_tag[0] == '\0' || !is_known_tag(dom_tag)) {
2266       DEBUG(PLUGIN_NAME " plugin#%s: refreshing domain %s "
2267                         "with unknown tag '%s'",
2268             inst->tag, dom_name, dom_tag);
2269       return 1;
2270     }
2271   }
2272
2273   return 0;
2274 }
2275
2276 static int refresh_lists(struct lv_read_instance *inst) {
2277   struct lv_read_state *state = &inst->read_state;
2278   int n;
2279
2280 #ifndef HAVE_LIST_ALL_DOMAINS
2281   n = virConnectNumOfDomains(conn);
2282   if (n < 0) {
2283     VIRT_ERROR(conn, "reading number of domains");
2284     return -1;
2285   }
2286 #endif
2287
2288   lv_clean_read_state(state);
2289
2290 #ifndef HAVE_LIST_ALL_DOMAINS
2291   if (n == 0)
2292     goto end;
2293 #endif
2294
2295 #ifdef HAVE_LIST_ALL_DOMAINS
2296   virDomainPtr *domains, *domains_inactive;
2297   int m = virConnectListAllDomains(conn, &domains_inactive,
2298                                    VIR_CONNECT_LIST_DOMAINS_INACTIVE);
2299   n = virConnectListAllDomains(conn, &domains, VIR_CONNECT_LIST_DOMAINS_ACTIVE);
2300 #else
2301   int *domids;
2302
2303   /* Get list of domains. */
2304   domids = calloc(n, sizeof(*domids));
2305   if (domids == NULL) {
2306     ERROR(PLUGIN_NAME " plugin: calloc failed.");
2307     return -1;
2308   }
2309
2310   n = virConnectListDomains(conn, domids, n);
2311 #endif
2312
2313   if (n < 0) {
2314     VIRT_ERROR(conn, "reading list of domains");
2315 #ifndef HAVE_LIST_ALL_DOMAINS
2316     sfree(domids);
2317 #else
2318     for (int i = 0; i < m; ++i)
2319       virDomainFree(domains_inactive[i]);
2320     sfree(domains_inactive);
2321 #endif
2322     return -1;
2323   }
2324
2325 #ifdef HAVE_LIST_ALL_DOMAINS
2326   for (int i = 0; i < m; ++i)
2327     if (add_domain(state, domains_inactive[i], 0) < 0) {
2328       ERROR(PLUGIN_NAME " plugin: malloc failed.");
2329       virDomainFree(domains_inactive[i]);
2330       domains_inactive[i] = NULL;
2331       continue;
2332     }
2333 #endif
2334
2335   /* Fetch each domain and add it to the list, unless ignore. */
2336   for (int i = 0; i < n; ++i) {
2337     const char *name;
2338     char *xml = NULL;
2339     xmlDocPtr xml_doc = NULL;
2340     xmlXPathContextPtr xpath_ctx = NULL;
2341     xmlXPathObjectPtr xpath_obj = NULL;
2342     char tag[PARTITION_TAG_MAX_LEN] = {'\0'};
2343     virDomainInfo info;
2344     int status;
2345
2346 #ifdef HAVE_LIST_ALL_DOMAINS
2347     virDomainPtr dom = domains[i];
2348 #else
2349     virDomainPtr dom = NULL;
2350     dom = virDomainLookupByID(conn, domids[i]);
2351     if (dom == NULL) {
2352       VIRT_ERROR(conn, "virDomainLookupByID");
2353       /* Could be that the domain went away -- ignore it anyway. */
2354       continue;
2355     }
2356 #endif
2357
2358     if (add_domain(state, dom, 1) < 0) {
2359       /*
2360        * When domain is already tracked, then there is
2361        * no problem with memory handling (will be freed
2362        * with the rest of domains cached data)
2363        * But in case of error like this (error occurred
2364        * before adding domain to track) we have to take
2365        * care it ourselves and call virDomainFree
2366        */
2367       ERROR(PLUGIN_NAME " plugin: malloc failed.");
2368       virDomainFree(dom);
2369       goto cont;
2370     }
2371
2372     name = virDomainGetName(dom);
2373     if (name == NULL) {
2374       VIRT_ERROR(conn, "virDomainGetName");
2375       goto cont;
2376     }
2377
2378     status = virDomainGetInfo(dom, &info);
2379     if (status != 0) {
2380       ERROR(PLUGIN_NAME " plugin: virDomainGetInfo failed with status %i.",
2381             status);
2382       continue;
2383     }
2384
2385     if (info.state != VIR_DOMAIN_RUNNING) {
2386       DEBUG(PLUGIN_NAME " plugin: skipping inactive domain %s", name);
2387       continue;
2388     }
2389
2390     if (il_domains && ignorelist_match(il_domains, name) != 0)
2391       goto cont;
2392
2393     /* Get a list of devices for this domain. */
2394     xml = virDomainGetXMLDesc(dom, 0);
2395     if (!xml) {
2396       VIRT_ERROR(conn, "virDomainGetXMLDesc");
2397       goto cont;
2398     }
2399
2400     /* Yuck, XML.  Parse out the devices. */
2401     xml_doc = xmlReadDoc((xmlChar *)xml, NULL, NULL, XML_PARSE_NONET);
2402     if (xml_doc == NULL) {
2403       VIRT_ERROR(conn, "xmlReadDoc");
2404       goto cont;
2405     }
2406
2407     xpath_ctx = xmlXPathNewContext(xml_doc);
2408
2409     if (lv_domain_get_tag(xpath_ctx, name, tag) < 0) {
2410       ERROR(PLUGIN_NAME " plugin: lv_domain_get_tag failed.");
2411       goto cont;
2412     }
2413
2414     if (!lv_instance_include_domain(inst, name, tag))
2415       goto cont;
2416
2417     /* Block devices. */
2418     const char *bd_xmlpath = "/domain/devices/disk/target[@dev]";
2419     if (blockdevice_format == source)
2420       bd_xmlpath = "/domain/devices/disk/source[@dev]";
2421     xpath_obj = xmlXPathEval((const xmlChar *)bd_xmlpath, xpath_ctx);
2422
2423     if (xpath_obj == NULL || xpath_obj->type != XPATH_NODESET ||
2424         xpath_obj->nodesetval == NULL)
2425       goto cont;
2426
2427     for (int j = 0; j < xpath_obj->nodesetval->nodeNr; ++j) {
2428       xmlNodePtr node;
2429       char *path = NULL;
2430
2431       node = xpath_obj->nodesetval->nodeTab[j];
2432       if (!node)
2433         continue;
2434       path = (char *)xmlGetProp(node, (xmlChar *)"dev");
2435       if (!path)
2436         continue;
2437
2438       if (il_block_devices &&
2439           ignore_device_match(il_block_devices, name, path) != 0)
2440         goto cont2;
2441
2442       add_block_device(state, dom, path);
2443     cont2:
2444       if (path)
2445         xmlFree(path);
2446     }
2447     xmlXPathFreeObject(xpath_obj);
2448
2449     /* Network interfaces. */
2450     xpath_obj = xmlXPathEval(
2451         (xmlChar *)"/domain/devices/interface[target[@dev]]", xpath_ctx);
2452     if (xpath_obj == NULL || xpath_obj->type != XPATH_NODESET ||
2453         xpath_obj->nodesetval == NULL)
2454       goto cont;
2455
2456     xmlNodeSetPtr xml_interfaces = xpath_obj->nodesetval;
2457
2458     for (int j = 0; j < xml_interfaces->nodeNr; ++j) {
2459       char *path = NULL;
2460       char *address = NULL;
2461       xmlNodePtr xml_interface;
2462
2463       xml_interface = xml_interfaces->nodeTab[j];
2464       if (!xml_interface)
2465         continue;
2466
2467       for (xmlNodePtr child = xml_interface->children; child;
2468            child = child->next) {
2469         if (child->type != XML_ELEMENT_NODE)
2470           continue;
2471
2472         if (xmlStrEqual(child->name, (const xmlChar *)"target")) {
2473           path = (char *)xmlGetProp(child, (const xmlChar *)"dev");
2474           if (!path)
2475             continue;
2476         } else if (xmlStrEqual(child->name, (const xmlChar *)"mac")) {
2477           address = (char *)xmlGetProp(child, (const xmlChar *)"address");
2478           if (!address)
2479             continue;
2480         }
2481       }
2482
2483       if (il_interface_devices &&
2484           (ignore_device_match(il_interface_devices, name, path) != 0 ||
2485            ignore_device_match(il_interface_devices, name, address) != 0))
2486         goto cont3;
2487
2488       add_interface_device(state, dom, path, address, j + 1);
2489     cont3:
2490       if (path)
2491         xmlFree(path);
2492       if (address)
2493         xmlFree(address);
2494     }
2495
2496   cont:
2497     if (xpath_obj)
2498       xmlXPathFreeObject(xpath_obj);
2499     if (xpath_ctx)
2500       xmlXPathFreeContext(xpath_ctx);
2501     if (xml_doc)
2502       xmlFreeDoc(xml_doc);
2503     sfree(xml);
2504   }
2505
2506 #ifdef HAVE_LIST_ALL_DOMAINS
2507   /* NOTE: domains_active and domains_inactive data will be cleared during
2508      refresh of all domains (inside lv_clean_read_state function) so we need
2509      to free here only allocated arrays */
2510   sfree(domains);
2511   sfree(domains_inactive);
2512 #else
2513   sfree(domids);
2514
2515 end:
2516 #endif
2517
2518   DEBUG(PLUGIN_NAME " plugin#%s: refreshing"
2519                     " domains=%i block_devices=%i iface_devices=%i",
2520         inst->tag, state->nr_domains, state->nr_block_devices,
2521         state->nr_interface_devices);
2522
2523   return 0;
2524 }
2525
2526 static void free_domains(struct lv_read_state *state) {
2527   if (state->domains) {
2528     for (int i = 0; i < state->nr_domains; ++i)
2529       virDomainFree(state->domains[i].ptr);
2530     sfree(state->domains);
2531   }
2532   state->domains = NULL;
2533   state->nr_domains = 0;
2534 }
2535
2536 static int add_domain(struct lv_read_state *state, virDomainPtr dom,
2537                       bool active) {
2538   domain_t *new_ptr;
2539   int new_size = sizeof(state->domains[0]) * (state->nr_domains + 1);
2540
2541   if (state->domains)
2542     new_ptr = realloc(state->domains, new_size);
2543   else
2544     new_ptr = malloc(new_size);
2545
2546   if (new_ptr == NULL)
2547     return -1;
2548
2549   state->domains = new_ptr;
2550   state->domains[state->nr_domains].ptr = dom;
2551   state->domains[state->nr_domains].active = active;
2552   memset(&state->domains[state->nr_domains].info, 0,
2553          sizeof(state->domains[state->nr_domains].info));
2554
2555   return state->nr_domains++;
2556 }
2557
2558 static void free_block_devices(struct lv_read_state *state) {
2559   if (state->block_devices) {
2560     for (int i = 0; i < state->nr_block_devices; ++i)
2561       sfree(state->block_devices[i].path);
2562     sfree(state->block_devices);
2563   }
2564   state->block_devices = NULL;
2565   state->nr_block_devices = 0;
2566 }
2567
2568 static int add_block_device(struct lv_read_state *state, virDomainPtr dom,
2569                             const char *path) {
2570   struct block_device *new_ptr;
2571   int new_size =
2572       sizeof(state->block_devices[0]) * (state->nr_block_devices + 1);
2573   char *path_copy;
2574
2575   path_copy = strdup(path);
2576   if (!path_copy)
2577     return -1;
2578
2579   if (state->block_devices)
2580     new_ptr = realloc(state->block_devices, new_size);
2581   else
2582     new_ptr = malloc(new_size);
2583
2584   if (new_ptr == NULL) {
2585     sfree(path_copy);
2586     return -1;
2587   }
2588   state->block_devices = new_ptr;
2589   state->block_devices[state->nr_block_devices].dom = dom;
2590   state->block_devices[state->nr_block_devices].path = path_copy;
2591   return state->nr_block_devices++;
2592 }
2593
2594 static void free_interface_devices(struct lv_read_state *state) {
2595   if (state->interface_devices) {
2596     for (int i = 0; i < state->nr_interface_devices; ++i) {
2597       sfree(state->interface_devices[i].path);
2598       sfree(state->interface_devices[i].address);
2599       sfree(state->interface_devices[i].number);
2600     }
2601     sfree(state->interface_devices);
2602   }
2603   state->interface_devices = NULL;
2604   state->nr_interface_devices = 0;
2605 }
2606
2607 static int add_interface_device(struct lv_read_state *state, virDomainPtr dom,
2608                                 const char *path, const char *address,
2609                                 unsigned int number) {
2610   struct interface_device *new_ptr;
2611   int new_size =
2612       sizeof(state->interface_devices[0]) * (state->nr_interface_devices + 1);
2613   char *path_copy, *address_copy, number_string[21];
2614
2615   if ((path == NULL) || (address == NULL))
2616     return EINVAL;
2617
2618   path_copy = strdup(path);
2619   if (!path_copy)
2620     return -1;
2621
2622   address_copy = strdup(address);
2623   if (!address_copy) {
2624     sfree(path_copy);
2625     return -1;
2626   }
2627
2628   snprintf(number_string, sizeof(number_string), "interface-%u", number);
2629
2630   if (state->interface_devices)
2631     new_ptr = realloc(state->interface_devices, new_size);
2632   else
2633     new_ptr = malloc(new_size);
2634
2635   if (new_ptr == NULL) {
2636     sfree(path_copy);
2637     sfree(address_copy);
2638     return -1;
2639   }
2640   state->interface_devices = new_ptr;
2641   state->interface_devices[state->nr_interface_devices].dom = dom;
2642   state->interface_devices[state->nr_interface_devices].path = path_copy;
2643   state->interface_devices[state->nr_interface_devices].address = address_copy;
2644   state->interface_devices[state->nr_interface_devices].number =
2645       strdup(number_string);
2646   return state->nr_interface_devices++;
2647 }
2648
2649 static int ignore_device_match(ignorelist_t *il, const char *domname,
2650                                const char *devpath) {
2651   char *name;
2652   int r;
2653
2654   if ((domname == NULL) || (devpath == NULL))
2655     return 0;
2656
2657   size_t n = strlen(domname) + strlen(devpath) + 2;
2658   name = malloc(n);
2659   if (name == NULL) {
2660     ERROR(PLUGIN_NAME " plugin: malloc failed.");
2661     return 0;
2662   }
2663   snprintf(name, n, "%s:%s", domname, devpath);
2664   r = ignorelist_match(il, name);
2665   sfree(name);
2666   return r;
2667 }
2668
2669 static int lv_shutdown(void) {
2670   for (int i = 0; i < nr_instances; ++i) {
2671     lv_fini_instance(i);
2672   }
2673
2674   DEBUG(PLUGIN_NAME " plugin: stopping event loop");
2675
2676   if (!persistent_notification)
2677     stop_event_loop(&notif_thread);
2678
2679   lv_disconnect();
2680
2681   ignorelist_free(il_domains);
2682   il_domains = NULL;
2683   ignorelist_free(il_block_devices);
2684   il_block_devices = NULL;
2685   ignorelist_free(il_interface_devices);
2686   il_interface_devices = NULL;
2687
2688   return 0;
2689 }
2690
2691 void module_register(void) {
2692   plugin_register_config(PLUGIN_NAME, lv_config, config_keys, NR_CONFIG_KEYS);
2693   plugin_register_init(PLUGIN_NAME, lv_init);
2694   plugin_register_shutdown(PLUGIN_NAME, lv_shutdown);
2695 }