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