virt: Replace malloc with calloc for array allocations
[collectd.git] / src / virt.c
index 9ed797b..4dc8645 100644 (file)
 /* 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
@@ -163,18 +169,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) {
@@ -193,9 +204,11 @@ 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;
     }
@@ -230,6 +243,7 @@ static int map_domain_event_detail_to_reason(int event, int detail) {
                                                   libvirt API call */
       ret = VIR_DOMAIN_PAUSED_UNKNOWN;
       break;
+#ifdef HAVE_DOM_REASON_POSTCOPY
     case VIR_DOMAIN_EVENT_SUSPENDED_POSTCOPY: /* Suspended for post-copy
                                                  migration */
       ret = VIR_DOMAIN_PAUSED_POSTCOPY;
@@ -238,6 +252,7 @@ static int map_domain_event_detail_to_reason(int event, int detail) {
                                                         post-copy */
       ret = VIR_DOMAIN_PAUSED_POSTCOPY_FAILED;
       break;
+#endif
     default:
       ret = VIR_DOMAIN_PAUSED_UNKNOWN;
     }
@@ -255,10 +270,12 @@ static int map_domain_event_detail_to_reason(int event, int detail) {
     case VIR_DOMAIN_EVENT_RESUMED_FROM_SNAPSHOT: /* Resumed from snapshot */
       ret = VIR_DOMAIN_RUNNING_FROM_SNAPSHOT;
       break;
+#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;
     }
@@ -300,6 +317,7 @@ static int map_domain_event_detail_to_reason(int event, int detail) {
       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
@@ -313,6 +331,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 */
@@ -328,7 +347,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] =
@@ -362,7 +380,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",
 
@@ -402,7 +419,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] =
@@ -739,7 +755,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];
 
@@ -752,44 +767,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:
@@ -797,17 +802,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,
@@ -1818,7 +1821,10 @@ static int domain_lifecycle_event_cb(__attribute__((unused)) virConnectPtr conn,
                                      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;
@@ -1884,9 +1890,9 @@ 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;
@@ -1901,6 +1907,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);
@@ -1912,9 +1919,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);
@@ -1983,13 +1990,6 @@ 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)
@@ -2001,6 +2001,15 @@ static int lv_read(user_data_t *ud) {
     last_refresh = t;
   }
 
+  /* 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",
@@ -2018,11 +2027,13 @@ static int lv_read(user_data_t *ud) {
   /* 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",
@@ -2234,9 +2245,9 @@ static int refresh_lists(struct lv_read_instance *inst) {
   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;
   }
 
@@ -2248,6 +2259,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;
@@ -2424,7 +2437,11 @@ static int refresh_lists(struct lv_read_instance *inst) {
   }
 
 #ifdef HAVE_LIST_ALL_DOMAINS
+  for (int i = 0; i < n; ++i)
+    virDomainFree(domains[i]);
   sfree(domains);
+  for (int i = 0; i < m; ++i)
+    virDomainFree(domains_inactive[i]);
   sfree(domains_inactive);
 #else
   sfree(domids);