Merge branch 'collectd-5.8'
[collectd.git] / src / virt.c
index 1b70b22..c52a8a7 100644 (file)
 #include <libxml/tree.h>
 #include <libxml/xpath.h>
 #include <libxml/xpathInternals.h>
+#include <stdbool.h>
 
 /* Plugin name */
 #define PLUGIN_NAME "virt"
 
+/* Secure strcat macro assuring null termination. Parameter (n) is the size of
+   buffer (d), allowing this macro to be safe for static and dynamic buffers */
+#define SSTRNCAT(d, s, n)                                                      \
+  do {                                                                         \
+    size_t _l = strlen(d);                                                     \
+    sstrncpy((d) + _l, (s), (n)-_l);                                           \
+  } while (0)
+
 #ifdef LIBVIR_CHECK_VERSION
 
 #if LIBVIR_CHECK_VERSION(0, 9, 2)
   in some systems which actually have virConnectListAllDomains()
   we can't detect this.
  */
-#ifdef LIBVIR_CHECK_VERSION
 #if LIBVIR_CHECK_VERSION(0, 10, 2)
 #define HAVE_LIST_ALL_DOMAINS 1
 #endif
-#endif
 
 #if LIBVIR_CHECK_VERSION(1, 0, 1)
 #define HAVE_DOM_REASON_PAUSED_SNAPSHOT 1
 
 #endif /* LIBVIR_CHECK_VERSION */
 
+/* structure used for aggregating notification-thread data*/
+typedef struct virt_notif_thread_s {
+  pthread_t event_loop_tid;
+  int domain_event_cb_id;
+  pthread_mutex_t active_mutex; /* protects 'is_active' member access*/
+  bool is_active;
+} virt_notif_thread_t;
+
 static const char *config_keys[] = {"Connection",
 
                                     "RefreshInterval",
@@ -124,12 +139,10 @@ static const char *config_keys[] = {"Connection",
                                     NULL};
 
 /* PersistentNotification is false by default */
-static _Bool persistent_notification = 0;
+static bool persistent_notification = false;
 
-/* libvirt event loop */
-static pthread_t event_loop_tid;
-
-static int domain_event_cb_id;
+/* Thread used for handling libvirt notifications events */
+static virt_notif_thread_t notif_thread;
 
 const char *domain_states[] = {
         [VIR_DOMAIN_NOSTATE] = "no state",
@@ -163,18 +176,23 @@ static int map_domain_event_to_state(int event) {
   case VIR_DOMAIN_EVENT_SHUTDOWN:
     ret = VIR_DOMAIN_SHUTDOWN;
     break;
+#ifdef HAVE_DOM_STATE_PMSUSPENDED
   case VIR_DOMAIN_EVENT_PMSUSPENDED:
     ret = VIR_DOMAIN_PMSUSPENDED;
     break;
+#endif
+#ifdef HAVE_DOM_REASON_CRASHED
   case VIR_DOMAIN_EVENT_CRASHED:
     ret = VIR_DOMAIN_CRASHED;
     break;
+#endif
   default:
     ret = VIR_DOMAIN_NOSTATE;
   }
   return ret;
 }
 
+#ifdef HAVE_DOM_REASON
 static int map_domain_event_detail_to_reason(int event, int detail) {
   int ret;
   switch (event) {
@@ -183,7 +201,8 @@ static int map_domain_event_detail_to_reason(int event, int detail) {
     case VIR_DOMAIN_EVENT_STARTED_BOOTED: /* Normal startup from boot */
       ret = VIR_DOMAIN_RUNNING_BOOTED;
       break;
-    case VIR_DOMAIN_EVENT_STARTED_MIGRATED: /* Incoming migration from another host */
+    case VIR_DOMAIN_EVENT_STARTED_MIGRATED: /* Incoming migration from another
+                                               host */
       ret = VIR_DOMAIN_RUNNING_MIGRATED;
       break;
     case VIR_DOMAIN_EVENT_STARTED_RESTORED: /* Restored from a state file */
@@ -192,60 +211,78 @@ static int map_domain_event_detail_to_reason(int event, int detail) {
     case VIR_DOMAIN_EVENT_STARTED_FROM_SNAPSHOT: /* Restored from snapshot */
       ret = VIR_DOMAIN_RUNNING_FROM_SNAPSHOT;
       break;
+#ifdef HAVE_DOM_REASON_RUNNING_WAKEUP
     case VIR_DOMAIN_EVENT_STARTED_WAKEUP: /* Started due to wakeup event */
       ret = VIR_DOMAIN_RUNNING_WAKEUP;
       break;
+#endif
     default:
       ret = VIR_DOMAIN_RUNNING_UNKNOWN;
     }
     break;
   case VIR_DOMAIN_EVENT_SUSPENDED:
     switch (detail) {
-    case VIR_DOMAIN_EVENT_SUSPENDED_PAUSED: /* Normal suspend due to admin pause */
+    case VIR_DOMAIN_EVENT_SUSPENDED_PAUSED: /* Normal suspend due to admin
+                                               pause */
       ret = VIR_DOMAIN_PAUSED_USER;
       break;
-    case VIR_DOMAIN_EVENT_SUSPENDED_MIGRATED: /* Suspended for offline migration */
+    case VIR_DOMAIN_EVENT_SUSPENDED_MIGRATED: /* Suspended for offline
+                                                 migration */
       ret = VIR_DOMAIN_PAUSED_MIGRATION;
       break;
-    case VIR_DOMAIN_EVENT_SUSPENDED_IOERROR: /* Suspended due to a disk I/O error */
+    case VIR_DOMAIN_EVENT_SUSPENDED_IOERROR: /* Suspended due to a disk I/O
+                                                error */
       ret = VIR_DOMAIN_PAUSED_IOERROR;
       break;
-    case VIR_DOMAIN_EVENT_SUSPENDED_WATCHDOG: /* Suspended due to a watchdog firing */
+    case VIR_DOMAIN_EVENT_SUSPENDED_WATCHDOG: /* Suspended due to a watchdog
+                                                 firing */
       ret = VIR_DOMAIN_PAUSED_WATCHDOG;
       break;
-    case VIR_DOMAIN_EVENT_SUSPENDED_RESTORED: /* Restored from paused state file */
+    case VIR_DOMAIN_EVENT_SUSPENDED_RESTORED: /* Restored from paused state
+                                                 file */
       ret = VIR_DOMAIN_PAUSED_UNKNOWN;
       break;
-    case VIR_DOMAIN_EVENT_SUSPENDED_FROM_SNAPSHOT: /* Restored from paused snapshot */
+    case VIR_DOMAIN_EVENT_SUSPENDED_FROM_SNAPSHOT: /* Restored from paused
+                                                      snapshot */
       ret = VIR_DOMAIN_PAUSED_FROM_SNAPSHOT;
       break;
-    case VIR_DOMAIN_EVENT_SUSPENDED_API_ERROR: /* Suspended after failure during libvirt API call */
+    case VIR_DOMAIN_EVENT_SUSPENDED_API_ERROR: /* Suspended after failure during
+                                                  libvirt API call */
       ret = VIR_DOMAIN_PAUSED_UNKNOWN;
       break;
-    case VIR_DOMAIN_EVENT_SUSPENDED_POSTCOPY: /* Suspended for post-copy migration */
+#ifdef HAVE_DOM_REASON_POSTCOPY
+    case VIR_DOMAIN_EVENT_SUSPENDED_POSTCOPY: /* Suspended for post-copy
+                                                 migration */
       ret = VIR_DOMAIN_PAUSED_POSTCOPY;
       break;
-    case VIR_DOMAIN_EVENT_SUSPENDED_POSTCOPY_FAILED: /* Suspended after failed post-copy */
+    case VIR_DOMAIN_EVENT_SUSPENDED_POSTCOPY_FAILED: /* Suspended after failed
+                                                        post-copy */
       ret = VIR_DOMAIN_PAUSED_POSTCOPY_FAILED;
       break;
+#endif
     default:
       ret = VIR_DOMAIN_PAUSED_UNKNOWN;
     }
     break;
   case VIR_DOMAIN_EVENT_RESUMED:
     switch (detail) {
-    case VIR_DOMAIN_EVENT_RESUMED_UNPAUSED: /* Normal resume due to admin unpause */
+    case VIR_DOMAIN_EVENT_RESUMED_UNPAUSED: /* Normal resume due to admin
+                                               unpause */
       ret = VIR_DOMAIN_RUNNING_UNPAUSED;
       break;
-    case VIR_DOMAIN_EVENT_RESUMED_MIGRATED: /* Resumed for completion of migration */
+    case VIR_DOMAIN_EVENT_RESUMED_MIGRATED: /* Resumed for completion of
+                                               migration */
       ret = VIR_DOMAIN_RUNNING_MIGRATED;
       break;
     case VIR_DOMAIN_EVENT_RESUMED_FROM_SNAPSHOT: /* Resumed from snapshot */
       ret = VIR_DOMAIN_RUNNING_FROM_SNAPSHOT;
       break;
-    case VIR_DOMAIN_EVENT_RESUMED_POSTCOPY: /* Resumed, but migration is still running in post-copy mode */
+#ifdef HAVE_DOM_REASON_POSTCOPY
+    case VIR_DOMAIN_EVENT_RESUMED_POSTCOPY: /* Resumed, but migration is still
+                                               running in post-copy mode */
       ret = VIR_DOMAIN_RUNNING_POSTCOPY;
       break;
+#endif
     default:
       ret = VIR_DOMAIN_RUNNING_UNKNOWN;
     }
@@ -279,16 +316,19 @@ static int map_domain_event_detail_to_reason(int event, int detail) {
     break;
   case VIR_DOMAIN_EVENT_SHUTDOWN:
     switch (detail) {
-    case VIR_DOMAIN_EVENT_SHUTDOWN_FINISHED: /* Guest finished shutdown sequence */
+    case VIR_DOMAIN_EVENT_SHUTDOWN_FINISHED: /* Guest finished shutdown
+                                                sequence */
       ret = VIR_DOMAIN_SHUTDOWN_USER;
       break;
     default:
       ret = VIR_DOMAIN_SHUTDOWN_UNKNOWN;
     }
     break;
+#ifdef HAVE_DOM_STATE_PMSUSPENDED
   case VIR_DOMAIN_EVENT_PMSUSPENDED:
     switch (detail) {
-    case VIR_DOMAIN_EVENT_PMSUSPENDED_MEMORY: /* Guest was PM suspended to memory */
+    case VIR_DOMAIN_EVENT_PMSUSPENDED_MEMORY: /* Guest was PM suspended to
+                                                 memory */
       ret = VIR_DOMAIN_PMSUSPENDED_UNKNOWN;
       break;
     case VIR_DOMAIN_EVENT_PMSUSPENDED_DISK: /* Guest was PM suspended to disk */
@@ -298,6 +338,7 @@ static int map_domain_event_detail_to_reason(int event, int detail) {
       ret = VIR_DOMAIN_PMSUSPENDED_UNKNOWN;
     }
     break;
+#endif
   case VIR_DOMAIN_EVENT_CRASHED:
     switch (detail) {
     case VIR_DOMAIN_EVENT_CRASHED_PANICKED: /* Guest was panicked */
@@ -313,7 +354,6 @@ static int map_domain_event_detail_to_reason(int event, int detail) {
   return ret;
 }
 
-#ifdef HAVE_DOM_REASON
 #define DOMAIN_STATE_REASON_MAX_SIZE 20
 const char *domain_reasons[][DOMAIN_STATE_REASON_MAX_SIZE] = {
         [VIR_DOMAIN_NOSTATE][VIR_DOMAIN_NOSTATE_UNKNOWN] =
@@ -347,7 +387,6 @@ const char *domain_reasons[][DOMAIN_STATE_REASON_MAX_SIZE] = {
         [VIR_DOMAIN_RUNNING][VIR_DOMAIN_RUNNING_POSTCOPY] =
             "running in post-copy migration mode",
 #endif
-
         [VIR_DOMAIN_BLOCKED][VIR_DOMAIN_BLOCKED_UNKNOWN] =
             "the reason is unknown",
 
@@ -387,7 +426,6 @@ const char *domain_reasons[][DOMAIN_STATE_REASON_MAX_SIZE] = {
         [VIR_DOMAIN_PAUSED][VIR_DOMAIN_PAUSED_POSTCOPY_FAILED] =
             "paused after failed post-copy",
 #endif
-
         [VIR_DOMAIN_SHUTDOWN][VIR_DOMAIN_SHUTDOWN_UNKNOWN] =
             "the reason is unknown",
         [VIR_DOMAIN_SHUTDOWN][VIR_DOMAIN_SHUTDOWN_USER] =
@@ -430,8 +468,8 @@ const char *domain_reasons[][DOMAIN_STATE_REASON_MAX_SIZE] = {
   } while (0)
 
 /* Connection. */
-static virConnectPtr conn = 0;
-static char *conn_string = NULL;
+static virConnectPtr conn;
+static char *conn_string;
 static c_complain_t conn_complain = C_COMPLAIN_INIT_STATIC;
 
 /* Node information required for %CPU */
@@ -441,11 +479,11 @@ static virNodeInfo nodeinfo;
 static int interval = 60;
 
 /* List of domains, if specified. */
-static ignorelist_t *il_domains = NULL;
+static ignorelist_t *il_domains;
 /* List of block devices, if specified. */
-static ignorelist_t *il_block_devices = NULL;
+static ignorelist_t *il_block_devices;
 /* List of network interface devices, if specified. */
-static ignorelist_t *il_interface_devices = NULL;
+static ignorelist_t *il_interface_devices;
 
 static int ignore_device_match(ignorelist_t *, const char *domname,
                                const char *devpath);
@@ -467,7 +505,7 @@ struct interface_device {
 typedef struct domain_s {
   virDomainPtr ptr;
   virDomainInfo info;
-  _Bool active;
+  bool active;
 } domain_t;
 
 struct lv_read_state {
@@ -484,7 +522,7 @@ struct lv_read_state {
 
 static void free_domains(struct lv_read_state *state);
 static int add_domain(struct lv_read_state *state, virDomainPtr dom,
-                      _Bool active);
+                      bool active);
 
 static void free_block_devices(struct lv_read_state *state);
 static int add_block_device(struct lv_read_state *state, virDomainPtr dom,
@@ -592,7 +630,7 @@ static const struct ex_stats_item ex_stats_table[] = {
 };
 
 /* BlockDeviceFormatBasename */
-_Bool blockdevice_format_basename = 0;
+static bool blockdevice_format_basename;
 static enum bd_field blockdevice_format = target;
 static enum if_field interface_format = if_name;
 
@@ -724,7 +762,6 @@ static int lv_domain_info(virDomainPtr dom, struct lv_info *info) {
 }
 
 static void init_value_list(value_list_t *vl, virDomainPtr dom) {
-  int n;
   const char *name;
   char uuid[VIR_UUID_STRING_BUFLEN];
 
@@ -737,44 +774,34 @@ static void init_value_list(value_list_t *vl, virDomainPtr dom) {
     if (hostname_format[i] == hf_none)
       continue;
 
-    n = DATA_MAX_NAME_LEN - strlen(vl->host) - 2;
-
-    if (i > 0 && n >= 1) {
-      strncat(vl->host, ":", 1);
-      n--;
-    }
+    if (i > 0)
+      SSTRNCAT(vl->host, ":", sizeof(vl->host));
 
     switch (hostname_format[i]) {
     case hf_none:
       break;
     case hf_hostname:
-      strncat(vl->host, hostname_g, n);
+      SSTRNCAT(vl->host, hostname_g, sizeof(vl->host));
       break;
     case hf_name:
       name = virDomainGetName(dom);
       if (name)
-        strncat(vl->host, name, n);
+        SSTRNCAT(vl->host, name, sizeof(vl->host));
       break;
     case hf_uuid:
       if (virDomainGetUUIDString(dom, uuid) == 0)
-        strncat(vl->host, uuid, n);
+        SSTRNCAT(vl->host, uuid, sizeof(vl->host));
       break;
     }
   }
 
-  vl->host[sizeof(vl->host) - 1] = '\0';
-
   /* Construct the plugin instance field according to PluginInstanceFormat. */
   for (int i = 0; i < PLGINST_MAX_FIELDS; ++i) {
     if (plugin_instance_format[i] == plginst_none)
       continue;
 
-    n = sizeof(vl->plugin_instance) - strlen(vl->plugin_instance) - 2;
-
-    if (i > 0 && n >= 1) {
-      strncat(vl->plugin_instance, ":", 1);
-      n--;
-    }
+    if (i > 0)
+      SSTRNCAT(vl->plugin_instance, ":", sizeof(vl->plugin_instance));
 
     switch (plugin_instance_format[i]) {
     case plginst_none:
@@ -782,17 +809,15 @@ static void init_value_list(value_list_t *vl, virDomainPtr dom) {
     case plginst_name:
       name = virDomainGetName(dom);
       if (name)
-        strncat(vl->plugin_instance, name, n);
+        SSTRNCAT(vl->plugin_instance, name, sizeof(vl->plugin_instance));
       break;
     case plginst_uuid:
       if (virDomainGetUUIDString(dom, uuid) == 0)
-        strncat(vl->plugin_instance, uuid, n);
+        SSTRNCAT(vl->plugin_instance, uuid, sizeof(vl->plugin_instance));
       break;
     }
   }
 
-  vl->plugin_instance[sizeof(vl->plugin_instance) - 1] = '\0';
-
 } /* void init_value_list */
 
 static int init_notif(notification_t *notif, const virDomainPtr domain,
@@ -991,7 +1016,7 @@ static unsigned int parse_ex_stats_flags(char **exstats, int numexstats) {
 }
 
 static void domain_state_submit_notif(virDomainPtr dom, int state, int reason) {
-  if ((state < 0) || (state >= STATIC_ARRAY_SIZE(domain_states))) {
+  if ((state < 0) || ((size_t)state >= STATIC_ARRAY_SIZE(domain_states))) {
     ERROR(PLUGIN_NAME ": Array index out of bounds: state=%d", state);
     return;
   }
@@ -999,7 +1024,8 @@ static void domain_state_submit_notif(virDomainPtr dom, int state, int reason) {
   char msg[DATA_MAX_NAME_LEN];
   const char *state_str = domain_states[state];
 #ifdef HAVE_DOM_REASON
-  if ((reason < 0) || (reason >= STATIC_ARRAY_SIZE(domain_reasons[0]))) {
+  if ((reason < 0) ||
+      ((size_t)reason >= STATIC_ARRAY_SIZE(domain_reasons[0]))) {
     ERROR(PLUGIN_NAME ": Array index out of bounds: reason=%d", reason);
     return;
   }
@@ -1098,7 +1124,7 @@ static int lv_config(const char *key, const char *value) {
     return 0;
   }
   if (strcasecmp(key, "BlockDeviceFormatBasename") == 0) {
-    blockdevice_format_basename = IS_TRUE(value);
+    blockdevice_format_basename = IS_TRUE(value) ? true : false;
     return 0;
   }
   if (strcasecmp(key, "InterfaceDevice") == 0) {
@@ -1372,7 +1398,7 @@ static void vcpu_pin_submit(virDomainPtr dom, int max_cpus, int vcpu,
                             unsigned char *cpu_maps, int cpu_map_len) {
   for (int cpu = 0; cpu < max_cpus; ++cpu) {
     char type_instance[DATA_MAX_NAME_LEN];
-    _Bool is_set = VIR_CPU_USABLE(cpu_maps, cpu_map_len, vcpu, cpu) ? 1 : 0;
+    bool is_set = VIR_CPU_USABLE(cpu_maps, cpu_map_len, vcpu, cpu);
 
     snprintf(type_instance, sizeof(type_instance), "vcpu_%d-cpu_%d", vcpu, cpu);
     submit(dom, "cpu_affinity", type_instance, &(value_t){.gauge = is_set}, 1);
@@ -1559,7 +1585,7 @@ static int get_block_stats(struct block_device *block_dev) {
 
 #define NM_ADD_STR_ITEMS(_items, _size)                                        \
   do {                                                                         \
-    for (int _i = 0; _i < _size; ++_i) {                                       \
+    for (size_t _i = 0; _i < _size; ++_i) {                                    \
       DEBUG(PLUGIN_NAME                                                        \
             " plugin: Adding notification metadata name=%s value=%s",          \
             _items[_i].name, _items[_i].value);                                \
@@ -1584,7 +1610,7 @@ static int fs_info_notify(virDomainPtr domain, virDomainFSInfoPtr fs_info) {
       {.name = "name", .value = fs_info->name},
       {.name = "fstype", .value = fs_info->fstype}};
 
-  for (int i = 0; i < fs_info->ndevAlias; ++i) {
+  for (size_t i = 0; i < fs_info->ndevAlias; ++i) {
     fs_dev_alias[i].name = "devAlias";
     fs_dev_alias[i].value = fs_info->devAlias[i];
   }
@@ -1799,11 +1825,14 @@ static int get_if_dev_stats(struct interface_device *if_dev) {
   return 0;
 }
 
-static int domain_lifecycle_event_cb(__attribute__((unused)) virConnectPtr conn,
+static int domain_lifecycle_event_cb(__attribute__((unused)) virConnectPtr con_,
                                      virDomainPtr dom, int event, int detail,
                                      __attribute__((unused)) void *opaque) {
   int domain_state = map_domain_event_to_state(event);
-  int domain_reason = map_domain_event_detail_to_reason(event, detail);
+  int domain_reason = 0; /* 0 means UNKNOWN reason for any state */
+#ifdef HAVE_DOM_REASON
+  domain_reason = map_domain_event_detail_to_reason(event, detail);
+#endif
   domain_state_submit_notif(dom, domain_state, domain_reason);
 
   return 0;
@@ -1821,9 +1850,30 @@ static int register_event_impl(void) {
   return 0;
 }
 
+static void virt_notif_thread_set_active(virt_notif_thread_t *thread_data,
+                                         const bool active) {
+  assert(thread_data != NULL);
+  pthread_mutex_lock(&thread_data->active_mutex);
+  thread_data->is_active = active;
+  pthread_mutex_unlock(&thread_data->active_mutex);
+}
+
+static bool virt_notif_thread_is_active(virt_notif_thread_t *thread_data) {
+  bool active = false;
+
+  assert(thread_data != NULL);
+  pthread_mutex_lock(&thread_data->active_mutex);
+  active = thread_data->is_active;
+  pthread_mutex_unlock(&thread_data->active_mutex);
+
+  return active;
+}
+
 /* worker function running default event implementation */
-static void *event_loop_worker(__attribute__((unused)) void *arg) {
-  while (1) {
+static void *event_loop_worker(void *arg) {
+  virt_notif_thread_t *thread_data = (virt_notif_thread_t *)arg;
+
+  while (virt_notif_thread_is_active(thread_data)) {
     if (virEventRunDefaultImpl() < 0) {
       virErrorPtr err = virGetLastError();
       ERROR(PLUGIN_NAME " plugin: failed to run event loop: %s\n",
@@ -1834,19 +1884,44 @@ static void *event_loop_worker(__attribute__((unused)) void *arg) {
   return NULL;
 }
 
+static int virt_notif_thread_init(virt_notif_thread_t *thread_data) {
+  int ret;
+
+  assert(thread_data != NULL);
+  ret = pthread_mutex_init(&thread_data->active_mutex, NULL);
+  if (ret != 0) {
+    ERROR(PLUGIN_NAME ": Failed to initialize mutex, err %u", ret);
+    return ret;
+  }
+
+  /**
+   * '0' and positive integers are meaningful ID's, therefore setting
+   * domain_event_cb_id to '-1'
+   */
+  thread_data->domain_event_cb_id = -1;
+  pthread_mutex_lock(&thread_data->active_mutex);
+  thread_data->is_active = false;
+  pthread_mutex_unlock(&thread_data->active_mutex);
+
+  return 0;
+}
+
 /* register domain event callback and start event loop thread */
-static int start_event_loop(void) {
-  domain_event_cb_id = virConnectDomainEventRegisterAny(
+static int start_event_loop(virt_notif_thread_t *thread_data) {
+  assert(thread_data != NULL);
+  thread_data->domain_event_cb_id = virConnectDomainEventRegisterAny(
       conn, NULL, VIR_DOMAIN_EVENT_ID_LIFECYCLE,
       VIR_DOMAIN_EVENT_CALLBACK(domain_lifecycle_event_cb), NULL, NULL);
-  if (domain_event_cb_id == -1) {
+  if (thread_data->domain_event_cb_id == -1) {
     ERROR(PLUGIN_NAME " plugin: error while callback registering");
     return -1;
   }
 
-  if (pthread_create(&event_loop_tid, NULL, event_loop_worker, NULL)) {
+  virt_notif_thread_set_active(thread_data, 1);
+  if (pthread_create(&thread_data->event_loop_tid, NULL, event_loop_worker,
+                     thread_data)) {
     ERROR(PLUGIN_NAME " plugin: failed event loop thread creation");
-    virConnectDomainEventDeregisterAny(conn, domain_event_cb_id);
+    virConnectDomainEventDeregisterAny(conn, thread_data->domain_event_cb_id);
     return -1;
   }
 
@@ -1854,31 +1929,27 @@ static int start_event_loop(void) {
 }
 
 /* stop event loop thread and deregister callback */
-static void stop_event_loop(void) {
-  if (pthread_cancel(event_loop_tid) != 0)
-    ERROR(PLUGIN_NAME " plugin: cancelling thread %lu failed",
-          event_loop_tid);
-
-  if (pthread_join(event_loop_tid, NULL) != 0)
-    ERROR(PLUGIN_NAME " plugin: stopping thread %lu failed",
-          event_loop_tid);
-
-  if (conn != NULL && domain_event_cb_id != -1)
-    virConnectDomainEventDeregisterAny(conn, domain_event_cb_id);
+static void stop_event_loop(virt_notif_thread_t *thread_data) {
+  /* stopping loop and de-registering event handler*/
+  virt_notif_thread_set_active(thread_data, 0);
+  if (conn != NULL && thread_data->domain_event_cb_id != -1)
+    virConnectDomainEventDeregisterAny(conn, thread_data->domain_event_cb_id);
+
+  if (pthread_join(notif_thread.event_loop_tid, NULL) != 0)
+    ERROR(PLUGIN_NAME " plugin: stopping notification thread failed");
 }
 
 static int persistent_domains_state_notification(void) {
   int status = 0;
   int n;
 #ifdef HAVE_LIST_ALL_DOMAINS
-  virDomainPtr *domains;
+  virDomainPtr *domains = NULL;
   n = virConnectListAllDomains(conn, &domains,
-                               VIR_CONNECT_GET_ALL_DOMAINS_STATS_PERSISTENT);
+                               VIR_CONNECT_LIST_DOMAINS_PERSISTENT);
   if (n < 0) {
     VIRT_ERROR(conn, "reading list of persistent domains");
     status = -1;
-  }
-  else {
+  } else {
     DEBUG(PLUGIN_NAME " plugin: getting state of %i persistent domains", n);
     /* Fetch each persistent domain's state and notify it */
     int n_notified = n;
@@ -1889,6 +1960,7 @@ static int persistent_domains_state_notification(void) {
         ERROR(PLUGIN_NAME " plugin: could not notify state of domain %s",
               virDomainGetName(domains[i]));
       }
+      virDomainFree(domains[i]);
     }
 
     sfree(domains);
@@ -1900,9 +1972,9 @@ static int persistent_domains_state_notification(void) {
   if (n > 0) {
     int *domids;
     /* Get list of domains. */
-    domids = malloc(sizeof(*domids) * n);
+    domids = calloc(n, sizeof(*domids));
     if (domids == NULL) {
-      ERROR(PLUGIN_NAME " plugin: malloc failed.");
+      ERROR(PLUGIN_NAME " plugin: calloc failed.");
       return -1;
     }
     n = virConnectListDomains(conn, domids, n);
@@ -1922,14 +1994,15 @@ static int persistent_domains_state_notification(void) {
         continue;
       }
       status = virDomainGetInfo(dom, &info);
-      if (status != 0) {
+      if (status == 0)
+        /* virDomainGetState is not available. Submit 0, which corresponds to
+         * unknown reason. */
+        domain_state_submit_notif(dom, info.state, 0);
+      else
         ERROR(PLUGIN_NAME " plugin: virDomainGetInfo failed with status %i.",
               status);
-        continue;
-      }
-      /* virDomainGetState is not available. Submit 0, which corresponds to
-       * unknown reason. */
-      domain_state_submit_notif(domain->ptr, info.di.state, 0);
+
+      virDomainFree(dom);
     }
     sfree(domids);
   }
@@ -1951,7 +2024,7 @@ static int lv_read(user_data_t *ud) {
   inst = ud->data;
   state = &inst->read_state;
 
-  _Bool reconnect = conn == NULL ? 1 : 0;
+  bool reconnect = conn == NULL ? true : false;
   /* event implementation must be registered before connection is opened */
   if (inst->id == 0) {
     if (!persistent_notification && reconnect)
@@ -1962,7 +2035,7 @@ static int lv_read(user_data_t *ud) {
       return -1;
 
     if (!persistent_notification && reconnect && conn != NULL)
-      if (start_event_loop() != 0)
+      if (start_event_loop(&notif_thread) != 0)
         return -1;
   }
 
@@ -1971,16 +2044,10 @@ static int lv_read(user_data_t *ud) {
   /* Need to refresh domain or device lists? */
   if ((last_refresh == (time_t)0) ||
       ((interval > 0) && ((last_refresh + interval) <= t))) {
-    if (inst->id == 0 && persistent_notification) {
-      int status = persistent_domains_state_notification();
-      if (status != 0)
-        DEBUG(PLUGIN_NAME " plugin: persistent_domains_state_notifications " \
-              "returned with status %i", status);
-    }
     if (refresh_lists(inst) != 0) {
       if (inst->id == 0) {
         if (!persistent_notification)
-          stop_event_loop();
+          stop_event_loop(&notif_thread);
         lv_disconnect();
       }
       return -1;
@@ -1988,28 +2055,39 @@ static int lv_read(user_data_t *ud) {
     last_refresh = t;
   }
 
-  #if COLLECT_DEBUG
-    for (int i = 0; i < state->nr_domains; ++i)
-        DEBUG(PLUGIN_NAME " plugin: domain %s",
-              virDomainGetName(state->domains[i].ptr));
-    for (int i = 0; i < state->nr_block_devices; ++i)
-        DEBUG(PLUGIN_NAME " plugin: block device %d %s:%s",
-              i, virDomainGetName(state->block_devices[i].dom),
-              state->block_devices[i].path);
-    for (int i = 0; i < state->nr_interface_devices; ++i)
-        DEBUG(PLUGIN_NAME " plugin: interface device %d %s:%s",
-              i, virDomainGetName(state->interface_devices[i].dom),
-              state->interface_devices[i].path);
-  #endif
+  /* persistent domains state notifications are handled by instance 0 */
+  if (inst->id == 0 && persistent_notification) {
+    int status = persistent_domains_state_notification();
+    if (status != 0)
+      DEBUG(PLUGIN_NAME " plugin: persistent_domains_state_notifications "
+                        "returned with status %i",
+            status);
+  }
+
+#if COLLECT_DEBUG
+  for (int i = 0; i < state->nr_domains; ++i)
+    DEBUG(PLUGIN_NAME " plugin: domain %s",
+          virDomainGetName(state->domains[i].ptr));
+  for (int i = 0; i < state->nr_block_devices; ++i)
+    DEBUG(PLUGIN_NAME " plugin: block device %d %s:%s", i,
+          virDomainGetName(state->block_devices[i].dom),
+          state->block_devices[i].path);
+  for (int i = 0; i < state->nr_interface_devices; ++i)
+    DEBUG(PLUGIN_NAME " plugin: interface device %d %s:%s", i,
+          virDomainGetName(state->interface_devices[i].dom),
+          state->interface_devices[i].path);
+#endif
 
   /* Get domains' metrics */
   for (int i = 0; i < state->nr_domains; ++i) {
     domain_t *dom = &state->domains[i];
-    int status;
+    int status = 0;
     if (dom->active)
       status = get_domain_metrics(dom);
+#ifdef HAVE_DOM_REASON
     else
       status = get_domain_state(dom->ptr);
+#endif
 
     if (status != 0)
       ERROR(PLUGIN_NAME " failed to get metrics for domain=%s",
@@ -2023,7 +2101,7 @@ static int lv_read(user_data_t *ud) {
       ERROR(PLUGIN_NAME
             " failed to get stats for block device (%s) in domain %s",
             state->block_devices[i].path,
-            virDomainGetName(state->domains[i].ptr));
+            virDomainGetName(state->block_devices[i].dom));
   }
 
   /* Get interface stats for each domain. */
@@ -2086,9 +2164,11 @@ static int lv_init(void) {
 
   DEBUG(PLUGIN_NAME " plugin: starting event loop");
 
-  if (!persistent_notification)
-    if (start_event_loop() != 0)
+  if (!persistent_notification) {
+    virt_notif_thread_init(&notif_thread);
+    if (start_event_loop(&notif_thread) != 0)
       return -1;
+  }
 
   DEBUG(PLUGIN_NAME " plugin: starting %i instances", nr_instances);
 
@@ -2216,15 +2296,14 @@ static int refresh_lists(struct lv_read_instance *inst) {
   virDomainPtr *domains, *domains_inactive;
   int m = virConnectListAllDomains(conn, &domains_inactive,
                                    VIR_CONNECT_LIST_DOMAINS_INACTIVE);
-  n = virConnectListAllDomains(conn, &domains,
-                               VIR_CONNECT_LIST_DOMAINS_ACTIVE);
+  n = virConnectListAllDomains(conn, &domains, VIR_CONNECT_LIST_DOMAINS_ACTIVE);
 #else
   int *domids;
 
   /* Get list of domains. */
-  domids = malloc(sizeof(*domids) * n);
+  domids = calloc(n, sizeof(*domids));
   if (domids == NULL) {
-    ERROR(PLUGIN_NAME " plugin: malloc failed.");
+    ERROR(PLUGIN_NAME " plugin: calloc failed.");
     return -1;
   }
 
@@ -2236,6 +2315,8 @@ static int refresh_lists(struct lv_read_instance *inst) {
 #ifndef HAVE_LIST_ALL_DOMAINS
     sfree(domids);
 #else
+    for (int i = 0; i < m; ++i)
+      virDomainFree(domains_inactive[i]);
     sfree(domains_inactive);
 #endif
     return -1;
@@ -2245,6 +2326,8 @@ static int refresh_lists(struct lv_read_instance *inst) {
   for (int i = 0; i < m; ++i)
     if (add_domain(state, domains_inactive[i], 0) < 0) {
       ERROR(PLUGIN_NAME " plugin: malloc failed.");
+      virDomainFree(domains_inactive[i]);
+      domains_inactive[i] = NULL;
       continue;
     }
 #endif
@@ -2272,6 +2355,20 @@ static int refresh_lists(struct lv_read_instance *inst) {
     }
 #endif
 
+    if (add_domain(state, dom, 1) < 0) {
+      /*
+       * When domain is already tracked, then there is
+       * no problem with memory handling (will be freed
+       * with the rest of domains cached data)
+       * But in case of error like this (error occurred
+       * before adding domain to track) we have to take
+       * care it ourselves and call virDomainFree
+       */
+      ERROR(PLUGIN_NAME " plugin: malloc failed.");
+      virDomainFree(dom);
+      goto cont;
+    }
+
     name = virDomainGetName(dom);
     if (name == NULL) {
       VIRT_ERROR(conn, "virDomainGetName");
@@ -2317,11 +2414,6 @@ static int refresh_lists(struct lv_read_instance *inst) {
     if (!lv_instance_include_domain(inst, name, tag))
       goto cont;
 
-    if (add_domain(state, dom, 1) < 0) {
-      ERROR(PLUGIN_NAME " plugin: malloc failed.");
-      goto cont;
-    }
-
     /* Block devices. */
     const char *bd_xmlpath = "/domain/devices/disk/target[@dev]";
     if (blockdevice_format == source)
@@ -2412,6 +2504,9 @@ static int refresh_lists(struct lv_read_instance *inst) {
   }
 
 #ifdef HAVE_LIST_ALL_DOMAINS
+  /* NOTE: domains_active and domains_inactive data will be cleared during
+     refresh of all domains (inside lv_clean_read_state function) so we need
+     to free here only allocated arrays */
   sfree(domains);
   sfree(domains_inactive);
 #else
@@ -2439,7 +2534,7 @@ static void free_domains(struct lv_read_state *state) {
 }
 
 static int add_domain(struct lv_read_state *state, virDomainPtr dom,
-                      _Bool active) {
+                      bool active) {
   domain_t *new_ptr;
   int new_size = sizeof(state->domains[0]) * (state->nr_domains + 1);
 
@@ -2515,7 +2610,7 @@ static int add_interface_device(struct lv_read_state *state, virDomainPtr dom,
   struct interface_device *new_ptr;
   int new_size =
       sizeof(state->interface_devices[0]) * (state->nr_interface_devices + 1);
-  char *path_copy, *address_copy, number_string[15];
+  char *path_copy, *address_copy, number_string[21];
 
   if ((path == NULL) || (address == NULL))
     return EINVAL;
@@ -2554,12 +2649,12 @@ static int add_interface_device(struct lv_read_state *state, virDomainPtr dom,
 static int ignore_device_match(ignorelist_t *il, const char *domname,
                                const char *devpath) {
   char *name;
-  int n, r;
+  int r;
 
   if ((domname == NULL) || (devpath == NULL))
     return 0;
 
-  n = strlen(domname) + strlen(devpath) + 2;
+  size_t n = strlen(domname) + strlen(devpath) + 2;
   name = malloc(n);
   if (name == NULL) {
     ERROR(PLUGIN_NAME " plugin: malloc failed.");
@@ -2579,7 +2674,7 @@ static int lv_shutdown(void) {
   DEBUG(PLUGIN_NAME " plugin: stopping event loop");
 
   if (!persistent_notification)
-    stop_event_loop();
+    stop_event_loop(&notif_thread);
 
   lv_disconnect();