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