/**
* collectd - src/exec.c
- * Copyright (C) 2007-2009 Florian octo Forster
+ * Copyright (C) 2007-2010 Florian octo Forster
* Copyright (C) 2007-2009 Sebastian Harl
* Copyright (C) 2008 Peter Holik
*
program_list_t *pl;
for (pl = pl_head; pl != NULL; pl = pl->next)
if (pl->pid == pid)
- break;
+ break;
if (pl != NULL)
pl->status = status;
} /* while (waitpid) */
if (ci->children_num != 0)
{
WARNING ("exec plugin: The config option `%s' may not be a block.",
- ci->key);
+ ci->key);
return (-1);
}
if (ci->values_num < 2)
{
WARNING ("exec plugin: The config option `%s' needs at least two "
- "arguments.", ci->key);
+ "arguments.", ci->key);
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);
+ "be string arguments.", ci->key);
return (-1);
}
{
if (ci->values[i + 1].type == OCONFIG_TYPE_NUMBER)
{
- ssnprintf (buffer, sizeof (buffer), "%lf",
- ci->values[i + 1].value.number);
+ ssnprintf (buffer, sizeof (buffer), "%lf",
+ ci->values[i + 1].value.number);
}
else
{
- if (ci->values[i + 1].value.boolean)
- sstrncpy (buffer, "true", sizeof (buffer));
- else
- sstrncpy (buffer, "false", sizeof (buffer));
+ if (ci->values[i + 1].value.boolean)
+ sstrncpy (buffer, "true", sizeof (buffer));
+ else
+ sstrncpy (buffer, "false", sizeof (buffer));
}
pl->argv[i] = strdup (buffer);
{
oconfig_item_t *child = ci->children + i;
if ((strcasecmp ("Exec", child->key) == 0)
- || (strcasecmp ("NotificationExec", child->key) == 0))
+ || (strcasecmp ("NotificationExec", child->key) == 0))
exec_config_exec (child);
else
{
return (0);
} /* int exec_config }}} */
-static void exec_child (program_list_t *pl) /* {{{ */
+static void set_environment (void) /* {{{ */
+{
+ char buffer[1024];
+
+#ifdef HAVE_SETENV
+ ssnprintf (buffer, sizeof (buffer), "%.3f",
+ CDTIME_T_TO_DOUBLE (plugin_get_interval ()));
+ setenv ("COLLECTD_INTERVAL", buffer, /* overwrite = */ 1);
+
+ ssnprintf (buffer, sizeof (buffer), "%s", hostname_g);
+ setenv ("COLLECTD_HOSTNAME", buffer, /* overwrite = */ 1);
+#else
+ ssnprintf (buffer, sizeof (buffer), "COLLECTD_INTERVAL=%.3f",
+ CDTIME_T_TO_DOUBLE (plugin_get_interval ()));
+ putenv (buffer);
+
+ ssnprintf (buffer, sizeof (buffer), "COLLECTD_HOSTNAME=%s", hostname_g);
+ putenv (buffer);
+#endif
+} /* }}} void set_environment */
+
+__attribute__((noreturn))
+static void exec_child (program_list_t *pl, int uid, int gid, int egid) /* {{{ */
{
int status;
- int uid;
- int gid;
- int egid;
-
- struct passwd *sp_ptr;
- struct passwd sp;
- char nambuf[2048];
char errbuf[1024];
- sp_ptr = NULL;
- status = getpwnam_r (pl->user, &sp, nambuf, sizeof (nambuf), &sp_ptr);
- if (status != 0)
- {
- ERROR ("exec plugin: getpwnam_r failed: %s",
- sstrerror (errno, errbuf, sizeof (errbuf)));
- exit (-1);
- }
- if (sp_ptr == NULL)
- {
- ERROR ("exec plugin: No such user: `%s'", pl->user);
- exit (-1);
- }
-
- uid = sp.pw_uid;
- gid = sp.pw_gid;
- if (uid == 0)
- {
- ERROR ("exec plugin: Cowardly refusing to exec program as root.");
- exit (-1);
- }
-
- /* 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: getgrnam_r failed: %s",
- sstrerror (errno, errbuf, sizeof (errbuf)));
- exit (-1);
- }
- if (NULL == gr_ptr)
- {
- ERROR ("exec plugin: No such group: `%s'", pl->group);
- exit (-1);
- }
-
- egid = gr.gr_gid;
- }
- else
- {
- egid = gid;
- }
- } /* if (pl->group == NULL) */
-
#if HAVE_SETGROUPS
if (getuid () == 0)
{
if (status != 0)
{
ERROR ("exec plugin: setgid (%i) failed: %s",
- gid, sstrerror (errno, errbuf, sizeof (errbuf)));
+ gid, sstrerror (errno, errbuf, sizeof (errbuf)));
exit (-1);
}
if (status != 0)
{
ERROR ("exec plugin: setegid (%i) failed: %s",
- egid, sstrerror (errno, errbuf, sizeof (errbuf)));
+ egid, sstrerror (errno, errbuf, sizeof (errbuf)));
exit (-1);
}
}
if (status != 0)
{
ERROR ("exec plugin: setuid (%i) failed: %s",
- uid, sstrerror (errno, errbuf, sizeof (errbuf)));
+ uid, sstrerror (errno, errbuf, sizeof (errbuf)));
exit (-1);
}
status = execvp (pl->exec, pl->argv);
- ERROR ("exec plugin: exec failed: %s",
- sstrerror (errno, errbuf, sizeof (errbuf)));
+ ERROR ("exec plugin: Failed to execute ``%s'': %s",
+ pl->exec, sstrerror (errno, errbuf, sizeof (errbuf)));
exit (-1);
} /* void exec_child }}} */
+static void reset_signal_mask (void) /* {{{ */
+{
+ sigset_t ss;
+
+ memset (&ss, 0, sizeof (ss));
+ sigemptyset (&ss);
+ sigprocmask (SIG_SETMASK, &ss, /* old mask = */ NULL);
+} /* }}} void reset_signal_mask */
+
/*
* 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
int status;
int pid;
+ int uid;
+ int gid;
+ int egid;
+
+ struct passwd *sp_ptr;
+ struct passwd sp;
+ char nambuf[2048];
+
if (pl->pid != 0)
return (-1);
if (status != 0)
{
ERROR ("exec plugin: pipe failed: %s",
- sstrerror (errno, errbuf, sizeof (errbuf)));
+ sstrerror (errno, errbuf, sizeof (errbuf)));
return (-1);
}
if (status != 0)
{
ERROR ("exec plugin: pipe failed: %s",
- sstrerror (errno, errbuf, sizeof (errbuf)));
+ sstrerror (errno, errbuf, sizeof (errbuf)));
return (-1);
}
if (status != 0)
{
ERROR ("exec plugin: pipe failed: %s",
- sstrerror (errno, errbuf, sizeof (errbuf)));
+ sstrerror (errno, errbuf, sizeof (errbuf)));
+ return (-1);
+ }
+
+ sp_ptr = NULL;
+ 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 (errno, errbuf, sizeof (errbuf)));
+ return (-1);
+ }
+ if (sp_ptr == NULL)
+ {
+ ERROR ("exec plugin: No such user: `%s'", pl->user);
return (-1);
}
+ uid = sp.pw_uid;
+ gid = sp.pw_gid;
+ if (uid == 0)
+ {
+ ERROR ("exec plugin: Cowardly refusing to exec program as root.");
+ return (-1);
+ }
+
+ /* 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 (errno, errbuf, sizeof (errbuf)));
+ return (-1);
+ }
+ if (NULL == gr_ptr)
+ {
+ ERROR ("exec plugin: No such group: `%s'", pl->group);
+ return (-1);
+ }
+
+ egid = gr.gr_gid;
+ }
+ else
+ {
+ egid = gid;
+ }
+ } /* if (pl->group == NULL) */
+
pid = fork ();
if (pid < 0)
{
ERROR ("exec plugin: fork failed: %s",
- sstrerror (errno, errbuf, sizeof (errbuf)));
+ sstrerror (errno, errbuf, sizeof (errbuf)));
return (-1);
}
else if (pid == 0)
for (fd = 0; fd < fd_num; fd++)
{
if ((fd == fd_pipe_in[0])
- || (fd == fd_pipe_out[1])
- || (fd == fd_pipe_err[1]))
- continue;
+ || (fd == fd_pipe_out[1])
+ || (fd == fd_pipe_err[1]))
+ continue;
close (fd);
}
close (fd_pipe_out[1]);
}
- /* Now connect the `out' pipe to STDOUT */
+ /* Now connect the `err' pipe to STDERR */
if (fd_pipe_err[1] != STDERR_FILENO)
{
dup2 (fd_pipe_err[1], STDERR_FILENO);
close (fd_pipe_err[1]);
}
- exec_child (pl);
+ set_environment ();
+
+ /* Unblock all signals */
+ reset_signal_mask ();
+
+ exec_child (pl, uid, gid, egid);
/* does not return */
}
return (handle_putnotif (stdout, buffer));
else
{
- /* For backwards compatibility */
- char tmp[1220];
- /* Let's annoy the user a bit.. */
- INFO ("exec plugin: Prepending `PUTVAL' to this line: %s", buffer);
- ssnprintf (tmp, sizeof (tmp), "PUTVAL %s", buffer);
- return (handle_putval (stdout, tmp));
+ ERROR ("exec plugin: Unable to parse command, ignoring line: \"%s\"",
+ buffer);
+ return (-1);
}
} /* int parse_line }}} */
status = fork_child (pl, NULL, &fd, &fd_err);
if (status < 0)
+ {
+ /* Reset the "running" flag */
+ pthread_mutex_lock (&pl_lock);
+ pl->flags &= ~PL_RUNNING;
+ pthread_mutex_unlock (&pl_lock);
pthread_exit ((void *) 1);
+ }
pl->pid = status;
assert (pl->pid != 0);
/* We use a copy of fdset, as select modifies it */
copy = fdset;
- while (select(highest_fd + 1, ©, NULL, NULL, NULL ) > 0)
+ while (1)
{
int len;
+ status = select (highest_fd + 1, ©, NULL, NULL, NULL);
+ if (status < 0)
+ {
+ if (errno == EINTR)
+ continue;
+ break;
+ }
+
if (FD_ISSET(fd, ©))
{
char *pnl;
if (len < 0)
{
- if (errno == EAGAIN || errno == EINTR) continue;
+ if (errno == EAGAIN || errno == EINTR)
+ continue;
break;
}
else if (len == 0)
{
- /* We've reached EOF */
- NOTICE ("exec plugin: Program `%s' has closed STDERR.",
- pl->exec);
- close (fd_err);
- FD_CLR (fd_err, &fdset);
- highest_fd = fd;
- fd_err = -1;
- continue;
+ /* We've reached EOF */
+ NOTICE ("exec plugin: Program `%s' has closed STDERR.", pl->exec);
+
+ /* Remove file descriptor form select() set. */
+ FD_CLR (fd_err, &fdset);
+ copy = fdset;
+ highest_fd = fd;
+
+ /* Clean up file descriptor */
+ close (fd_err);
+ fd_err = -1;
+ continue;
}
pbuffer_err[len] = '\0';
{
char errbuf[1024];
ERROR ("exec plugin: fdopen (%i) failed: %s", fd,
- sstrerror (errno, errbuf, sizeof (errbuf)));
+ sstrerror (errno, errbuf, sizeof (errbuf)));
kill (pl->pid, SIGTERM);
pl->pid = 0;
close (fd);
fprintf (fh,
"Severity: %s\n"
- "Time: %u\n",
- severity, (unsigned int) n->time);
+ "Time: %.3f\n",
+ severity, CDTIME_T_TO_DOUBLE (n->time));
/* Print the optional fields */
if (strlen (n->host) > 0)
fprintf (fh, "%s: %e\n", meta->name, meta->nm_value.nm_double);
else if (meta->type == NM_TYPE_BOOLEAN)
fprintf (fh, "%s: %s\n", meta->name,
- meta->nm_value.nm_boolean ? "true" : "false");
+ meta->nm_value.nm_boolean ? "true" : "false");
}
fprintf (fh, "\n%s\n", n->message);
pthread_attr_init (&attr);
pthread_attr_setdetachstate (&attr, PTHREAD_CREATE_DETACHED);
- pthread_create (&t, &attr, exec_read_one, (void *) pl);
+ plugin_thread_create (&t, &attr, exec_read_one, (void *) pl);
+ pthread_attr_destroy (&attr);
} /* for (pl) */
return (0);
} /* int exec_read }}} */
-static int exec_notification (const notification_t *n,
+static int exec_notification (const notification_t *n, /* {{{ */
user_data_t __attribute__((unused)) *user_data)
{
program_list_t *pl;
continue;
pln = (program_list_and_notification_t *) malloc (sizeof
- (program_list_and_notification_t));
+ (program_list_and_notification_t));
if (pln == NULL)
{
ERROR ("exec plugin: malloc failed.");
pthread_attr_init (&attr);
pthread_attr_setdetachstate (&attr, PTHREAD_CREATE_DETACHED);
- pthread_create (&t, &attr, exec_notification_one, (void *) pln);
+ plugin_thread_create (&t, &attr, exec_notification_one, (void *) pln);
+ pthread_attr_destroy (&attr);
} /* for (pl) */
return (0);