Merge branch 'collectd-5.7' into collectd-5.8
[collectd.git] / src / exec.c
index 80515d9..a38026e 100644 (file)
@@ -78,6 +78,11 @@ typedef struct program_list_and_notification_s {
 } program_list_and_notification_t;
 
 /*
+ * constants
+ */
+const long int MAX_GRBUF_SIZE = 65536;
+
+/*
  * Private variables
  */
 static program_list_t *pl_head = NULL;
@@ -187,8 +192,7 @@ static int exec_config_exec(oconfig_item_t *ci) /* {{{ */
       pl->argv[i] = strdup(ci->values[i + 1].value.string);
     } else {
       if (ci->values[i + 1].type == OCONFIG_TYPE_NUMBER) {
-        ssnprintf(buffer, sizeof(buffer), "%lf",
-                  ci->values[i + 1].value.number);
+        snprintf(buffer, sizeof(buffer), "%lf", ci->values[i + 1].value.number);
       } else {
         if (ci->values[i + 1].value.boolean)
           sstrncpy(buffer, "true", sizeof(buffer));
@@ -241,27 +245,47 @@ static int exec_config(oconfig_item_t *ci) /* {{{ */
   return 0;
 } /* int exec_config }}} */
 
+#if !defined(HAVE_SETENV)
+static char env_interval[64];
+// max hostname len is 255, so this should be enough
+static char env_hostname[300];
+#endif
+
 static void set_environment(void) /* {{{ */
 {
+#ifdef HAVE_SETENV
   char buffer[1024];
 
-#ifdef HAVE_SETENV
-  ssnprintf(buffer, sizeof(buffer), "%.3f",
-            CDTIME_T_TO_DOUBLE(plugin_get_interval()));
+  snprintf(buffer, sizeof(buffer), "%.3f",
+           CDTIME_T_TO_DOUBLE(plugin_get_interval()));
   setenv("COLLECTD_INTERVAL", buffer, /* overwrite = */ 1);
 
   sstrncpy(buffer, hostname_g, sizeof(buffer));
   setenv("COLLECTD_HOSTNAME", buffer, /* overwrite = */ 1);
 #else
-  ssnprintf(buffer, sizeof(buffer), "COLLECTD_INTERVAL=%.3f",
-            CDTIME_T_TO_DOUBLE(plugin_get_interval()));
-  putenv(buffer);
+  snprintf(env_interval, sizeof(env_interval), "COLLECTD_INTERVAL=%.3f",
+           CDTIME_T_TO_DOUBLE(plugin_get_interval()));
+  putenv(env_interval);
 
-  ssnprintf(buffer, sizeof(buffer), "COLLECTD_HOSTNAME=%s", hostname_g);
-  putenv(buffer);
+  snprintf(env_hostname, sizeof(env_hostname), "COLLECTD_HOSTNAME=%s",
+           hostname_g);
+  putenv(env_hostname);
 #endif
 } /* }}} void set_environment */
 
+static void unset_environment(void) /* {{{ */
+{
+#ifdef HAVE_SETENV
+  unsetenv("COLLECTD_INTERVAL");
+  unsetenv("COLLECTD_HOSTNAME");
+#else
+  snprintf(env_interval, sizeof(env_interval), "COLLECTD_INTERVAL");
+  putenv(env_interval);
+  snprintf(env_hostname, sizeof(env_hostname), "COLLECTD_HOSTNAME");
+  putenv(env_hostname);
+#endif
+} /* }}} void unset_environment */
+
 __attribute__((noreturn)) static void exec_child(program_list_t *pl, int uid,
                                                  int gid, int egid) /* {{{ */
 {
@@ -348,6 +372,67 @@ static void close_pipe(int fd_pipe[2]) /* {{{ */
 } /* }}} void close_pipe */
 
 /*
+ * Get effective group ID from group name.
+ * Input arguments:
+ *       pl  :program list struct with group name
+ *       gid :group id to fallback in case egid cannot be determined.
+ * Returns:
+ *       egid effective group id if successfull,
+ *            -1 if group is not defined/not found.
+ *            -2 for any buffer allocation error.
+ */
+static int getegr_id(program_list_t *pl, int gid) /* {{{ */
+{
+  if (pl->group == NULL) {
+    return -1;
+  }
+  if (strcmp(pl->group, "") == 0) {
+    return gid;
+  }
+  struct group *gr_ptr = NULL;
+  struct group gr;
+
+  long int grbuf_size = sysconf(_SC_GETGR_R_SIZE_MAX);
+  if (grbuf_size <= 0)
+    grbuf_size = sysconf(_SC_PAGESIZE);
+  if (grbuf_size <= 0)
+    grbuf_size = 4096;
+
+  char *temp = NULL;
+  char *grbuf = NULL;
+
+  do {
+    temp = realloc(grbuf, grbuf_size);
+    if (temp == NULL) {
+      ERROR("exec plugin: getegr_id for %s: realloc buffer[%ld] failed ",
+            pl->group, grbuf_size);
+      sfree(grbuf);
+      return -2;
+    }
+    grbuf = temp;
+    if (getgrnam_r(pl->group, &gr, grbuf, grbuf_size, &gr_ptr) == 0) {
+      sfree(grbuf);
+      if (gr_ptr == NULL) {
+        ERROR("exec plugin: No such group: `%s'", pl->group);
+        return -1;
+      }
+      return gr.gr_gid;
+    } else if (errno == ERANGE) {
+      grbuf_size += grbuf_size; // increment buffer size and try again
+    } else {
+      char errbuf[1024];
+      ERROR("exec plugin: getegr_id failed %s",
+            sstrerror(errno, errbuf, sizeof(errbuf)));
+      sfree(grbuf);
+      return -2;
+    }
+  } while (grbuf_size <= MAX_GRBUF_SIZE);
+  ERROR("exec plugin: getegr_id Max grbuf size reached  for %s", pl->group);
+  sfree(grbuf);
+  return -2;
+} /* }}} */
+
+/*
  * Creates three pipes (one for reading, one for writing and one for errors),
  * forks a child, sets up the pipes so that fd_in is connected to STDIN of
  * the child and fd_out is connected to STDOUT and fd_err is connected to STDERR
@@ -369,11 +454,17 @@ static int fork_child(program_list_t *pl, int *fd_in, int *fd_out,
 
   struct passwd *sp_ptr;
   struct passwd sp;
-  char nambuf[4096];
 
   if (pl->pid != 0)
     return -1;
 
+  long int nambuf_size = sysconf(_SC_GETPW_R_SIZE_MAX);
+  if (nambuf_size <= 0)
+    nambuf_size = sysconf(_SC_PAGESIZE);
+  if (nambuf_size <= 0)
+    nambuf_size = 4096;
+  char nambuf[nambuf_size];
+
   if ((create_pipe(fd_pipe_in) == -1) || (create_pipe(fd_pipe_out) == -1) ||
       (create_pipe(fd_pipe_err) == -1))
     goto failed;
@@ -398,31 +489,12 @@ static int fork_child(program_list_t *pl, int *fd_in, int *fd_out,
     goto failed;
   }
 
-  /* The group configured in the configfile is set as effective group, because
-   * this way the forked process can (re-)gain the user's primary group. */
-  egid = -1;
-  if (NULL != pl->group) {
-    if ('\0' != *pl->group) {
-      struct group *gr_ptr = NULL;
-      struct group gr;
-
-      status = getgrnam_r(pl->group, &gr, nambuf, sizeof(nambuf), &gr_ptr);
-      if (0 != status) {
-        ERROR("exec plugin: Failed to get group information "
-              "for group ``%s'': %s",
-              pl->group, sstrerror(status, errbuf, sizeof(errbuf)));
-        goto failed;
-      }
-      if (NULL == gr_ptr) {
-        ERROR("exec plugin: No such group: `%s'", pl->group);
-        goto failed;
-      }
+  egid = getegr_id(pl, gid);
+  if (egid == -2) {
+    goto failed;
+  }
 
-      egid = gr.gr_gid;
-    } else {
-      egid = gid;
-    }
-  } /* if (pl->group == NULL) */
+  set_environment();
 
   pid = fork();
   if (pid < 0) {
@@ -459,8 +531,6 @@ static int fork_child(program_list_t *pl, int *fd_in, int *fd_out,
       close(fd_pipe_err[1]);
     }
 
-    set_environment();
-
     /* Unblock all signals */
     reset_signal_mask();
 
@@ -468,6 +538,8 @@ static int fork_child(program_list_t *pl, int *fd_in, int *fd_out,
     /* does not return */
   }
 
+  unset_environment();
+
   close(fd_pipe_in[0]);
   close(fd_pipe_out[1]);
   close(fd_pipe_err[1]);
@@ -490,6 +562,8 @@ static int fork_child(program_list_t *pl, int *fd_in, int *fd_out,
   return pid;
 
 failed:
+  unset_environment();
+
   close_pipe(fd_pipe_in);
   close_pipe(fd_pipe_out);
   close_pipe(fd_pipe_err);
@@ -787,7 +861,11 @@ static int exec_read(void) /* {{{ */
 
     pthread_attr_init(&attr);
     pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
-    plugin_thread_create(&t, &attr, exec_read_one, (void *)pl, "exec read");
+    int status =
+        plugin_thread_create(&t, &attr, exec_read_one, (void *)pl, "exec read");
+    if (status != 0) {
+      ERROR("exec plugin: plugin_thread_create failed.");
+    }
     pthread_attr_destroy(&attr);
   } /* for (pl) */
 
@@ -826,8 +904,11 @@ static int exec_notification(const notification_t *n, /* {{{ */
 
     pthread_attr_init(&attr);
     pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
-    plugin_thread_create(&t, &attr, exec_notification_one, (void *)pln,
-                         "exec notify");
+    int status = plugin_thread_create(&t, &attr, exec_notification_one,
+                                      (void *)pln, "exec notify");
+    if (status != 0) {
+      ERROR("exec plugin: plugin_thread_create failed.");
+    }
     pthread_attr_destroy(&attr);
   } /* for (pl) */