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