exec plugin: Implemented a first version.
authorFlorian Forster <octo@leeloo.lan.home.verplant.org>
Sat, 17 Feb 2007 11:21:26 +0000 (12:21 +0100)
committerFlorian Forster <octo@leeloo.lan.home.verplant.org>
Sat, 17 Feb 2007 11:21:26 +0000 (12:21 +0100)
configure.in
src/Makefile.am
src/common.h
src/exec.c [new file with mode: 0644]

index be43e7e..0a70410 100644 (file)
@@ -1068,6 +1068,7 @@ AC_COLLECTD([df],        [disable], [module], [df statistics])
 AC_COLLECTD([dns],       [disable], [module], [dns statistics])
 AC_COLLECTD([email],     [disable], [module], [email statistics])
 AC_COLLECTD([entropy],   [disable], [module], [entropy statistics])
+AC_COLLECTD([exec],      [disable], [module], [exec of external programs])
 AC_COLLECTD([hddtemp],   [disable], [module], [hdd temperature statistics])
 AC_COLLECTD([load],      [disable], [module], [system load statistics])
 AC_COLLECTD([mbmon],     [disable], [module], [motherboard monitor statistics])
@@ -1125,6 +1126,7 @@ Configuration:
     dns . . . . . . . . $enable_dns
     email . . . . . . . $enable_email
     entropy . . . . . . $enable_entropy
+    exec  . . . . . . . $enable_exec
     hddtemp . . . . . . $enable_hddtemp
     load  . . . . . . . $enable_load
     mbmon . . . . . . . $enable_mbmon
index f4c5464..2fd0ccb 100644 (file)
@@ -204,6 +204,17 @@ collectd_LDADD += "-dlopen" entropy.la
 collectd_DEPENDENCIES += entropy.la
 endif
 
+if BUILD_MODULE_EXEC
+pkglib_LTLIBRARIES += exec.la
+exec_la_SOURCES = exec.c
+exec_la_LDFLAGS = -module -avoid-version
+if BUILD_WITH_LIBPTHREAD
+exec_la_LDFLAGS += -lpthread
+endif
+collectd_LDADD += "-dlopen" exec.la
+collectd_DEPENDENCIES += exec.la
+endif
+
 #if BUILD_MODULE_QUOTA
 #pkglib_LTLIBRARIES += quota.la
 #quota_la_SOURCES = quota_plugin.c quota_plugin.h
index ce1ffe5..9164cc2 100644 (file)
@@ -31,6 +31,8 @@
        } \
        (ptr) = NULL
 
+#define STATIC_ARRAY_SIZE(a) (sizeof (a) / sizeof (*(a)))
+
 void sstrncpy(char *d, const char *s, int len);
 char *sstrdup(const char *s);
 void *smalloc(size_t size);
diff --git a/src/exec.c b/src/exec.c
new file mode 100644 (file)
index 0000000..836c881
--- /dev/null
@@ -0,0 +1,353 @@
+/**
+ * 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
+ */