--- /dev/null
+/**
+ * collectd - src/exec.c
+ * Copyright (C) 2007  Florian octo Forster
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; only version 2 of the License is applicable.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA
+ *
+ * Authors:
+ *   Florian octo Forster <octo at verplant.org>
+ **/
+
+#include "collectd.h"
+#include "common.h"
+#include "plugin.h"
+#include "utils_debug.h"
+
+#include <sys/types.h>
+#include <pwd.h>
+
+#include <pthread.h>
+
+/*
+ * Private data types
+ */
+struct program_list_s;
+typedef struct program_list_s program_list_t;
+struct program_list_s
+{
+  char           *user;
+  char           *exec;
+  int             pid;
+  program_list_t *next;
+};
+
+/*
+ * Private variables
+ */
+static data_source_t dsrc_counter[1] =
+{
+  {"value", DS_TYPE_COUNTER, NAN, NAN}
+};
+
+static data_set_t ds_counter =
+{
+  "counter", STATIC_ARRAY_SIZE (dsrc_counter), dsrc_counter
+};
+
+static data_source_t dsrc_gauge[1] =
+{
+  {"value", DS_TYPE_GAUGE, NAN, NAN}
+};
+
+static data_set_t ds_gauge =
+{
+  "gauge", STATIC_ARRAY_SIZE (dsrc_gauge), dsrc_gauge
+};
+
+static const char *config_keys[] =
+{
+  "Exec"
+};
+static int config_keys_num = STATIC_ARRAY_SIZE (config_keys);
+
+static program_list_t *pl_head = NULL;
+
+/*
+ * Functions
+ */
+static int exec_config (const char *key, const char *value)
+{
+  if (strcasecmp ("Exec", key) == 0)
+  {
+    program_list_t *pl;
+    pl = (program_list_t *) malloc (sizeof (program_list_t));
+    if (pl == NULL)
+      return (1);
+    memset (pl, '\0', sizeof (program_list_t));
+
+    pl->user = strdup (value);
+    if (pl->user == NULL)
+    {
+      sfree (pl);
+      return (1);
+    }
+
+    pl->exec = strchr (pl->user, ' ');
+    if (pl->exec == NULL)
+    {
+      sfree (pl->user);
+      sfree (pl);
+      return (1);
+    }
+    while (*pl->exec == ' ')
+    {
+      *pl->exec = '\0';
+      pl->exec++;
+    }
+
+    if (*pl->exec == '\0')
+    {
+      sfree (pl->user);
+      sfree (pl);
+      return (1);
+    }
+
+    pl->next = pl_head;
+    pl_head = pl;
+  }
+  else
+  {
+    return (-1);
+  }
+
+  return (0);
+} /* int exec_config */
+
+static void submit_counter (const char *type_instance, counter_t value)
+{
+  value_t values[1];
+  value_list_t vl = VALUE_LIST_INIT;
+
+  DBG ("type_instance = %s; value = %llu;", type_instance, value);
+
+  values[0].counter = value;
+
+  vl.values = values;
+  vl.values_len = 1;
+  vl.time = time (NULL);
+  strcpy (vl.host, hostname);
+  strcpy (vl.plugin, "exec");
+  strcpy (vl.plugin_instance, "");
+  strncpy (vl.type_instance, type_instance, sizeof (vl.type_instance));
+
+  plugin_dispatch_values ("counter", &vl);
+} /* void submit_counter */
+
+static void submit_gauge (const char *type_instance, gauge_t value)
+{
+  value_t values[1];
+  value_list_t vl = VALUE_LIST_INIT;
+
+  DBG ("type_instance = %s; value = %lf;", type_instance, value);
+
+  values[0].gauge = value;
+
+  vl.values = values;
+  vl.values_len = 1;
+  vl.time = time (NULL);
+  strcpy (vl.host, hostname);
+  strcpy (vl.plugin, "exec");
+  strcpy (vl.plugin_instance, "");
+  strncpy (vl.type_instance, type_instance, sizeof (vl.type_instance));
+
+  plugin_dispatch_values ("gauge", &vl);
+} /* void submit_counter */
+
+static void exec_child (program_list_t *pl)
+{
+  struct passwd *sp;
+  int status;
+  int uid;
+  char *arg0;
+
+  /* FIXME: Not thread safe! */
+  sp = getpwnam (pl->user);
+  if (sp == NULL)
+  {
+    syslog (LOG_ERR, "exec plugin: getpwnam failed: %s", strerror (errno));
+    exit (-1);
+  }
+
+  uid = sp->pw_uid;
+  if (uid == 0)
+  {
+    syslog (LOG_ERR, "exec plugin: Cowardly refusing to exec program as root.");
+    exit (-1);
+  }
+
+  status = setuid (uid);
+  if (status != 0)
+  {
+    syslog (LOG_ERR, "exec plugin: setuid failed: %s", strerror (errno));
+    exit (-1);
+  }
+
+  arg0 = strrchr (pl->exec, '/');
+  if (arg0 != NULL)
+    arg0++;
+  if ((arg0 == NULL) || (*arg0 == '\0'))
+    arg0 = pl->exec;
+
+  status = execlp (pl->exec, arg0, (char *) 0);
+
+  syslog (LOG_ERR, "exec plugin: exec failed: %s", strerror (errno));
+  exit (-1);
+} /* void exec_child */
+
+static int fork_child (program_list_t *pl)
+{
+  int fd_pipe[2];
+  int status;
+
+  if (pl->pid != 0)
+    return (-1);
+
+  status = pipe (fd_pipe);
+  if (status != 0)
+  {
+    syslog (LOG_ERR, "exec plugin: pipe failed: %s", strerror (errno));
+    return (-1);
+  }
+
+  pl->pid = fork ();
+  if (pl->pid < 0)
+  {
+    syslog (LOG_ERR, "exec plugin: fork failed: %s", strerror (errno));
+    return (-1);
+  }
+  else if (pl->pid == 0)
+  {
+    close (fd_pipe[0]);
+
+    /* Connect the pipe to STDOUT and STDERR */
+    if (fd_pipe[1] != STDOUT_FILENO)
+      dup2 (fd_pipe[1], STDOUT_FILENO);
+    if (fd_pipe[1] != STDERR_FILENO)
+      dup2 (fd_pipe[1], STDERR_FILENO);
+    if ((fd_pipe[1] != STDOUT_FILENO) && (fd_pipe[1] != STDERR_FILENO))
+      close (fd_pipe[1]);
+
+    exec_child (pl);
+    /* does not return */
+  }
+
+  close (fd_pipe[1]);
+  return (fd_pipe[0]);
+} /* int fork_child */
+
+static void *exec_read_one (void *arg)
+{
+  program_list_t *pl = (program_list_t *) arg;
+  int fd;
+  FILE *fh;
+  char buffer[1024];
+
+  fd = fork_child (pl);
+  if (fd < 0)
+    pthread_exit ((void *) 1);
+
+  assert (pl->pid != 0);
+
+  fh = fdopen (fd, "r");
+  if (fh == NULL)
+  {
+    syslog (LOG_ERR, "exec plugin: fdopen (%i) failed: %s", fd,
+       strerror (errno));
+    kill (pl->pid, SIGTERM);
+    close (fd);
+    pthread_exit ((void *) 1);
+  }
+
+  while (fgets (buffer, sizeof (buffer), fh) != NULL)
+  {
+    int len;
+    char *type;
+    char *type_instance;
+    char *value;
+
+    DBG ("buffer = %s", buffer);
+
+    len = strlen (buffer);
+    if (len < 5)
+      continue;
+
+    if (buffer[0] == '#')
+      continue;
+
+    type = buffer;
+
+    type_instance = strchr (type, ',');
+    if (type_instance == NULL)
+      continue;
+    *type_instance = '\0';
+    type_instance++;
+
+    if ((strcasecmp ("counter", type) != 0)
+       && (strcasecmp ("gauge", type) != 0))
+    {
+      syslog (LOG_WARNING, "exec plugin: Received invalid type: %s", type);
+      continue;
+    }
+
+    value = strchr (type_instance, ',');
+    if (value == NULL)
+      continue;
+    *value = '\0';
+    value++;
+
+    DBG ("value = %s", value);
+
+    if (strcasecmp ("counter", type) == 0)
+      submit_counter (type_instance, atoll (value));
+    else
+      submit_gauge (type_instance, atof (value));
+  } /* while (fgets) */
+
+  fclose (fh);
+  pl->pid = 0;
+
+  pthread_exit ((void *) 0);
+} /* void *exec_read_one */
+
+static int exec_read (void)
+{
+  program_list_t *pl;
+
+  for (pl = pl_head; pl != NULL; pl = pl->next)
+  {
+    pthread_t t;
+    pthread_attr_t attr;
+
+    if (pl->pid != 0)
+      continue;
+
+    pthread_attr_init (&attr);
+    pthread_attr_setdetachstate (&attr, PTHREAD_CREATE_DETACHED);
+    pthread_create (&t, &attr, exec_read_one, (void *) pl);
+  } /* for (pl) */
+
+  return (0);
+} /* int exec_read */
+
+void module_register (void)
+{
+  plugin_register_data_set (&ds_counter);
+  plugin_register_data_set (&ds_gauge);
+  plugin_register_config ("exec", exec_config, config_keys, config_keys_num);
+  plugin_register_read ("exec", exec_read);
+} /* void module_register */
+
+/*
+ * vim:shiftwidth=2:softtabstop=2:tabstop=8
+ */