fix issue 2696 to dynamically allocate grname buffer in increments of _SC_GETGR_R_SIZ...
[collectd.git] / src / exec.c
index 60bd961..2c99ce2 100644 (file)
@@ -39,6 +39,7 @@
 #include <signal.h>
 #include <sys/types.h>
 
+#include <stdlib.h>
 #ifdef HAVE_SYS_CAPABILITY_H
 #include <sys/capability.h>
 #endif
@@ -78,9 +79,14 @@ 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;
+static program_list_t *pl_head;
 static pthread_mutex_t pl_lock = PTHREAD_MUTEX_INITIALIZER;
 
 /*
@@ -108,26 +114,26 @@ static int exec_config_exec(oconfig_item_t *ci) /* {{{ */
 
   if (ci->children_num != 0) {
     WARNING("exec plugin: The config option `%s' may not be a block.", ci->key);
-    return (-1);
+    return -1;
   }
   if (ci->values_num < 2) {
     WARNING("exec plugin: The config option `%s' needs at least two "
             "arguments.",
             ci->key);
-    return (-1);
+    return -1;
   }
   if ((ci->values[0].type != OCONFIG_TYPE_STRING) ||
       (ci->values[1].type != OCONFIG_TYPE_STRING)) {
     WARNING("exec plugin: The first two arguments to the `%s' option must "
             "be string arguments.",
             ci->key);
-    return (-1);
+    return -1;
   }
 
   pl = calloc(1, sizeof(*pl));
   if (pl == NULL) {
     ERROR("exec plugin: calloc failed.");
-    return (-1);
+    return -1;
   }
 
   if (strcasecmp("NotificationExec", ci->key) == 0)
@@ -139,7 +145,7 @@ static int exec_config_exec(oconfig_item_t *ci) /* {{{ */
   if (pl->user == NULL) {
     ERROR("exec plugin: strdup failed.");
     sfree(pl);
-    return (-1);
+    return -1;
   }
 
   pl->group = strchr(pl->user, ':');
@@ -153,7 +159,7 @@ static int exec_config_exec(oconfig_item_t *ci) /* {{{ */
     ERROR("exec plugin: strdup failed.");
     sfree(pl->user);
     sfree(pl);
-    return (-1);
+    return -1;
   }
 
   pl->argv = calloc(ci->values_num, sizeof(*pl->argv));
@@ -162,7 +168,7 @@ static int exec_config_exec(oconfig_item_t *ci) /* {{{ */
     sfree(pl->exec);
     sfree(pl->user);
     sfree(pl);
-    return (-1);
+    return -1;
   }
 
   {
@@ -179,7 +185,7 @@ static int exec_config_exec(oconfig_item_t *ci) /* {{{ */
     sfree(pl->exec);
     sfree(pl->user);
     sfree(pl);
-    return (-1);
+    return -1;
   }
 
   for (i = 1; i < (ci->values_num - 1); i++) {
@@ -187,8 +193,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));
@@ -213,7 +218,7 @@ static int exec_config_exec(oconfig_item_t *ci) /* {{{ */
     sfree(pl->exec);
     sfree(pl->user);
     sfree(pl);
-    return (-1);
+    return -1;
   }
 
   for (i = 0; pl->argv[i] != NULL; i++) {
@@ -223,7 +228,7 @@ static int exec_config_exec(oconfig_item_t *ci) /* {{{ */
   pl->next = pl_head;
   pl_head = pl;
 
-  return (0);
+  return 0;
 } /* int exec_config_exec }}} */
 
 static int exec_config(oconfig_item_t *ci) /* {{{ */
@@ -238,7 +243,7 @@ static int exec_config(oconfig_item_t *ci) /* {{{ */
     }
   } /* for (i) */
 
-  return (0);
+  return 0;
 } /* int exec_config }}} */
 
 static void set_environment(void) /* {{{ */
@@ -246,18 +251,18 @@ static void set_environment(void) /* {{{ */
   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()));
+  snprintf(buffer, sizeof(buffer), "COLLECTD_INTERVAL=%.3f",
+           CDTIME_T_TO_DOUBLE(plugin_get_interval()));
   putenv(buffer);
 
-  ssnprintf(buffer, sizeof(buffer), "COLLECTD_HOSTNAME=%s", hostname_g);
+  snprintf(buffer, sizeof(buffer), "COLLECTD_HOSTNAME=%s", hostname_g);
   putenv(buffer);
 #endif
 } /* }}} void set_environment */
@@ -266,7 +271,6 @@ __attribute__((noreturn)) static void exec_child(program_list_t *pl, int uid,
                                                  int gid, int egid) /* {{{ */
 {
   int status;
-  char errbuf[1024];
 
 #if HAVE_SETGROUPS
   if (getuid() == 0) {
@@ -287,31 +291,27 @@ __attribute__((noreturn)) static void exec_child(program_list_t *pl, int uid,
 
   status = setgid(gid);
   if (status != 0) {
-    ERROR("exec plugin: setgid (%i) failed: %s", gid,
-          sstrerror(errno, errbuf, sizeof(errbuf)));
+    ERROR("exec plugin: setgid (%i) failed: %s", gid, STRERRNO);
     exit(-1);
   }
 
   if (egid != -1) {
     status = setegid(egid);
     if (status != 0) {
-      ERROR("exec plugin: setegid (%i) failed: %s", egid,
-            sstrerror(errno, errbuf, sizeof(errbuf)));
+      ERROR("exec plugin: setegid (%i) failed: %s", egid, STRERRNO);
       exit(-1);
     }
   }
 
   status = setuid(uid);
   if (status != 0) {
-    ERROR("exec plugin: setuid (%i) failed: %s", uid,
-          sstrerror(errno, errbuf, sizeof(errbuf)));
+    ERROR("exec plugin: setuid (%i) failed: %s", uid, STRERRNO);
     exit(-1);
   }
 
   execvp(pl->exec, pl->argv);
 
-  ERROR("exec plugin: Failed to execute ``%s'': %s", pl->exec,
-        sstrerror(errno, errbuf, sizeof(errbuf)));
+  ERROR("exec plugin: Failed to execute ``%s'': %s", pl->exec, STRERRNO);
   exit(-1);
 } /* void exec_child }}} */
 
@@ -325,14 +325,12 @@ static void reset_signal_mask(void) /* {{{ */
 
 static int create_pipe(int fd_pipe[2]) /* {{{ */
 {
-  char errbuf[1024];
   int status;
 
   status = pipe(fd_pipe);
   if (status != 0) {
-    ERROR("exec plugin: pipe failed: %s",
-          sstrerror(errno, errbuf, sizeof(errbuf)));
-    return (-1);
+    ERROR("exec plugin: pipe failed: %s", STRERRNO);
+    return -1;
   }
 
   return 0;
@@ -359,7 +357,6 @@ static int fork_child(program_list_t *pl, int *fd_in, int *fd_out,
   int fd_pipe_in[2] = {-1, -1};
   int fd_pipe_out[2] = {-1, -1};
   int fd_pipe_err[2] = {-1, -1};
-  char errbuf[1024];
   int status;
   int pid;
 
@@ -371,7 +368,7 @@ static int fork_child(program_list_t *pl, int *fd_in, int *fd_out,
   struct passwd sp;
 
   if (pl->pid != 0)
-    return (-1);
+    return -1;
 
   long int nambuf_size = sysconf(_SC_GETPW_R_SIZE_MAX);
   if (nambuf_size <= 0)
@@ -388,7 +385,7 @@ static int fork_child(program_list_t *pl, int *fd_in, int *fd_out,
   status = getpwnam_r(pl->user, &sp, nambuf, sizeof(nambuf), &sp_ptr);
   if (status != 0) {
     ERROR("exec plugin: Failed to get user information for user ``%s'': %s",
-          pl->user, sstrerror(status, errbuf, sizeof(errbuf)));
+          pl->user, STRERROR(status));
     goto failed;
   }
 
@@ -407,8 +404,8 @@ static int fork_child(program_list_t *pl, int *fd_in, int *fd_out,
   /* 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) {
+  if (pl->group != NULL) {
+    if (*pl->group != '\0') {
       struct group *gr_ptr = NULL;
       struct group gr;
 
@@ -417,21 +414,65 @@ static int fork_child(program_list_t *pl, int *fd_in, int *fd_out,
         grbuf_size = sysconf(_SC_PAGESIZE);
       if (grbuf_size <= 0)
         grbuf_size = 4096;
-      char grbuf[grbuf_size];
 
-      status = getgrnam_r(pl->group, &gr, grbuf, sizeof(grbuf), &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;
+      long int size;
+      size = grbuf_size;
+      char *temp = NULL;
+      char *grbuf = NULL;
+      int getgr_failed = 0;
+      grbuf = malloc(size);
+      if (grbuf == NULL) {
+        ERROR("exec plugin: get group information for '%s' failed: buffer "
+              "malloc() failed",
+              pl->group);
+        getgr_failed = 1;
+        goto gr_finally;
+      }
+      int status;
+      while ((status = getgrnam_r(pl->group, &gr, grbuf, size, &gr_ptr)) != 0) {
+        switch (errno) {
+        case ERANGE:
+          if ((size + grbuf_size) < size ||
+              (size + grbuf_size) > MAX_GRBUF_SIZE) {
+            ERROR("exec plugin: get group information for '%s' max buffer "
+                  "limit (%ld) reached \n",
+                  pl->group, MAX_GRBUF_SIZE);
+            getgr_failed = 1;
+            goto gr_finally;
+          }
+          /* grow the buffer by 'grbuf_size' each time getgrnamr fails */
+          size += grbuf_size;
+          temp = realloc(grbuf, size);
+          if (temp == NULL) {
+            ERROR("exec plugin: get group information for '%s' realloc() "
+                  "buffer to (%ld) failed ",
+                  pl->group, size);
+            getgr_failed = 1;
+            goto gr_finally;
+          }
+          grbuf = temp;
+          break;
+        default:
+          ERROR("exec plugin: default errno: get group information for '%s' "
+                "failed : %s",
+                pl->group, sstrerror(status, errbuf, sizeof(errbuf)));
+          getgr_failed = 1;
+          goto gr_finally;
+        }
       }
-      if (NULL == gr_ptr) {
+      if (gr_ptr == NULL) {
         ERROR("exec plugin: No such group: `%s'", pl->group);
-        goto failed;
+        getgr_failed = 1;
+        goto gr_finally;
       }
-
       egid = gr.gr_gid;
+    gr_finally:
+      free(grbuf);
+      DEBUG("exec plugin: release grbuf memory ");
+      grbuf = NULL;
+      if (getgr_failed > 0) {
+        goto failed;
+      }
     } else {
       egid = gid;
     }
@@ -439,8 +480,7 @@ static int fork_child(program_list_t *pl, int *fd_in, int *fd_out,
 
   pid = fork();
   if (pid < 0) {
-    ERROR("exec plugin: fork failed: %s",
-          sstrerror(errno, errbuf, sizeof(errbuf)));
+    ERROR("exec plugin: fork failed: %s", STRERRNO);
     goto failed;
   } else if (pid == 0) {
     int fd_num;
@@ -500,26 +540,26 @@ static int fork_child(program_list_t *pl, int *fd_in, int *fd_out,
   else
     close(fd_pipe_err[0]);
 
-  return (pid);
+  return pid;
 
 failed:
   close_pipe(fd_pipe_in);
   close_pipe(fd_pipe_out);
   close_pipe(fd_pipe_err);
 
-  return (-1);
+  return -1;
 } /* int fork_child }}} */
 
 static int parse_line(char *buffer) /* {{{ */
 {
   if (strncasecmp("PUTVAL", buffer, strlen("PUTVAL")) == 0)
-    return (cmd_handle_putval(stdout, buffer));
+    return cmd_handle_putval(stdout, buffer);
   else if (strncasecmp("PUTNOTIF", buffer, strlen("PUTNOTIF")) == 0)
-    return (handle_putnotif(stdout, buffer));
+    return handle_putnotif(stdout, buffer);
   else {
     ERROR("exec plugin: Unable to parse command, ignoring line: \"%s\"",
           buffer);
-    return (-1);
+    return -1;
   }
 } /* int parse_line }}} */
 
@@ -668,7 +708,7 @@ static void *exec_read_one(void *arg) /* {{{ */
     close(fd_err);
 
   pthread_exit((void *)0);
-  return (NULL);
+  return NULL;
 } /* void *exec_read_one }}} */
 
 static void *exec_notification_one(void *arg) /* {{{ */
@@ -689,9 +729,7 @@ static void *exec_notification_one(void *arg) /* {{{ */
 
   fh = fdopen(fd, "w");
   if (fh == NULL) {
-    char errbuf[1024];
-    ERROR("exec plugin: fdopen (%i) failed: %s", fd,
-          sstrerror(errno, errbuf, sizeof(errbuf)));
+    ERROR("exec plugin: fdopen (%i) failed: %s", fd, STRERRNO);
     kill(pid, SIGTERM);
     close(fd);
     sfree(arg);
@@ -750,7 +788,7 @@ static void *exec_notification_one(void *arg) /* {{{ */
   n->meta = NULL;
   sfree(arg);
   pthread_exit((void *)0);
-  return (NULL);
+  return NULL;
 } /* void *exec_notification_one }}} */
 
 static int exec_init(void) /* {{{ */
@@ -776,7 +814,7 @@ static int exec_init(void) /* {{{ */
   }
 #endif
 
-  return (0);
+  return 0;
 } /* int exec_init }}} */
 
 static int exec_read(void) /* {{{ */
@@ -808,7 +846,7 @@ static int exec_read(void) /* {{{ */
     pthread_attr_destroy(&attr);
   } /* for (pl) */
 
-  return (0);
+  return 0;
 } /* int exec_read }}} */
 
 static int exec_notification(const notification_t *n, /* {{{ */
@@ -851,7 +889,7 @@ static int exec_notification(const notification_t *n, /* {{{ */
     pthread_attr_destroy(&attr);
   } /* for (pl) */
 
-  return (0);
+  return 0;
 } /* }}} int exec_notification */
 
 static int exec_shutdown(void) /* {{{ */
@@ -875,7 +913,7 @@ static int exec_shutdown(void) /* {{{ */
   } /* while (pl) */
   pl_head = NULL;
 
-  return (0);
+  return 0;
 } /* int exec_shutdown }}} */
 
 void module_register(void) {
@@ -886,7 +924,3 @@ void module_register(void) {
                                /* user_data = */ NULL);
   plugin_register_shutdown("exec", exec_shutdown);
 } /* void module_register */
-
-/*
- * vim:shiftwidth=2:softtabstop=2:tabstop=8:fdm=marker
- */