X-Git-Url: https://git.octo.it/?a=blobdiff_plain;f=src%2Fexec.c;h=88026b662a0be208daa58b24c418357a1c63399a;hb=a94f533;hp=44f03e3387a9236260a6388eb3efbd62596dfac7;hpb=4cf9eb921f5994d4515613efa8739ce445cfd9a1;p=collectd.git diff --git a/src/exec.c b/src/exec.c index 44f03e33..88026b66 100644 --- a/src/exec.c +++ b/src/exec.c @@ -35,7 +35,6 @@ #define PL_NORMAL 0x01 #define PL_NOTIF_ACTION 0x02 -#define PL_NAGIOS_PLUGIN 0x04 #define PL_RUNNING 0x10 @@ -128,11 +127,6 @@ static int exec_config_exec (oconfig_item_t *ci) /* {{{ */ } memset (pl, '\0', sizeof (program_list_t)); -#if 0 - if (strcasecmp ("NagiosExec", ci->key) == 0) - pl->flags |= PL_NAGIOS_PLUGIN; - else -#endif if (strcasecmp ("NotificationExec", ci->key) == 0) pl->flags |= PL_NOTIF_ACTION; else @@ -176,10 +170,9 @@ static int exec_config_exec (oconfig_item_t *ci) /* {{{ */ { char *tmp = strrchr (ci->values[1].value.string, '/'); if (tmp == NULL) - strncpy (buffer, ci->values[1].value.string, sizeof (buffer)); + sstrncpy (buffer, ci->values[1].value.string, sizeof (buffer)); else - strncpy (buffer, tmp + 1, sizeof (buffer)); - buffer[sizeof (buffer) - 1] = '\0'; + sstrncpy (buffer, tmp + 1, sizeof (buffer)); } pl->argv[0] = strdup (buffer); if (pl->argv[0] == NULL) @@ -202,17 +195,16 @@ static int exec_config_exec (oconfig_item_t *ci) /* {{{ */ { if (ci->values[i + 1].type == OCONFIG_TYPE_NUMBER) { - snprintf (buffer, sizeof (buffer), "%lf", + ssnprintf (buffer, sizeof (buffer), "%lf", ci->values[i + 1].value.number); } else { if (ci->values[i + 1].value.boolean) - strncpy (buffer, "true", sizeof (buffer)); + sstrncpy (buffer, "true", sizeof (buffer)); else - strncpy (buffer, "false", sizeof (buffer)); + sstrncpy (buffer, "false", sizeof (buffer)); } - buffer[sizeof (buffer) - 1] = '\0'; pl->argv[i] = strdup (buffer); } @@ -256,9 +248,6 @@ static int exec_config (oconfig_item_t *ci) /* {{{ */ { oconfig_item_t *child = ci->children + i; if ((strcasecmp ("Exec", child->key) == 0) -#if 0 - || (strcasecmp ("NagiosExec", child->key) == 0) -#endif || (strcasecmp ("NotificationExec", child->key) == 0)) exec_config_exec (child); else @@ -334,6 +323,25 @@ static void exec_child (program_list_t *pl) /* {{{ */ } } /* if (pl->group == NULL) */ +#if HAVE_SETGROUPS + if (getuid () == 0) + { + gid_t glist[2]; + size_t glist_len; + + glist[0] = gid; + glist_len = 1; + + if ((gid != egid) && (egid != -1)) + { + glist[1] = egid; + glist_len = 2; + } + + setgroups (glist_len, glist); + } +#endif /* HAVE_SETGROUPS */ + status = setgid (gid); if (status != 0) { @@ -369,14 +377,17 @@ static void exec_child (program_list_t *pl) /* {{{ */ } /* void exec_child }}} */ /* - * Creates two pipes (one for reading, ong for writing), 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 STDERR of the child. Then is calls `exec_child'. + * 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 + * of the child. Then is calls `exec_child'. */ -static int fork_child (program_list_t *pl, int *fd_in, int *fd_out) /* {{{ */ +static int fork_child (program_list_t *pl, int *fd_in, int *fd_out, int *fd_err) /* {{{ */ { int fd_pipe_in[2]; int fd_pipe_out[2]; + int fd_pipe_err[2]; + char errbuf[1024]; int status; int pid; @@ -386,7 +397,6 @@ static int fork_child (program_list_t *pl, int *fd_in, int *fd_out) /* {{{ */ status = pipe (fd_pipe_in); if (status != 0) { - char errbuf[1024]; ERROR ("exec plugin: pipe failed: %s", sstrerror (errno, errbuf, sizeof (errbuf))); return (-1); @@ -395,7 +405,14 @@ static int fork_child (program_list_t *pl, int *fd_in, int *fd_out) /* {{{ */ status = pipe (fd_pipe_out); if (status != 0) { - char errbuf[1024]; + ERROR ("exec plugin: pipe failed: %s", + sstrerror (errno, errbuf, sizeof (errbuf))); + return (-1); + } + + status = pipe (fd_pipe_err); + if (status != 0) + { ERROR ("exec plugin: pipe failed: %s", sstrerror (errno, errbuf, sizeof (errbuf))); return (-1); @@ -404,50 +421,45 @@ static int fork_child (program_list_t *pl, int *fd_in, int *fd_out) /* {{{ */ pid = fork (); if (pid < 0) { - char errbuf[1024]; ERROR ("exec plugin: fork failed: %s", sstrerror (errno, errbuf, sizeof (errbuf))); return (-1); } else if (pid == 0) { - close (fd_pipe_in[1]); - close (fd_pipe_out[0]); + int fd_num; + int fd; - /* If the `out' pipe has the filedescriptor STDIN we have to be careful - * with the `dup's below. So, if this is the case we have to handle the - * `out' pipe first. */ - if (fd_pipe_out[1] == STDIN_FILENO) + /* Close all file descriptors but the pipe end we need. */ + fd_num = getdtablesize (); + for (fd = 0; fd < fd_num; fd++) { - int new_fileno = (fd_pipe_in[0] == STDOUT_FILENO) - ? STDERR_FILENO : STDOUT_FILENO; - dup2 (fd_pipe_out[1], new_fileno); - close (fd_pipe_out[1]); - fd_pipe_out[1] = new_fileno; + if ((fd == fd_pipe_in[0]) + || (fd == fd_pipe_out[1]) + || (fd == fd_pipe_err[1])) + continue; + close (fd); } - /* Now `fd_pipe_out[1]' is either `STDOUT' or `STDERR', but definitely not - * `STDIN_FILENO'. */ /* Connect the `in' pipe to STDIN */ if (fd_pipe_in[0] != STDIN_FILENO) { dup2 (fd_pipe_in[0], STDIN_FILENO); close (fd_pipe_in[0]); - fd_pipe_in[0] = STDIN_FILENO; } - /* Now connect the `out' pipe to STDOUT and STDERR */ + /* Now connect the `out' pipe to STDOUT */ if (fd_pipe_out[1] != STDOUT_FILENO) + { dup2 (fd_pipe_out[1], STDOUT_FILENO); - if (fd_pipe_out[1] != STDERR_FILENO) - dup2 (fd_pipe_out[1], STDERR_FILENO); + close (fd_pipe_out[1]); + } - /* If the pipe has some FD that's something completely different, close it - * now. */ - if ((fd_pipe_out[1] != STDOUT_FILENO) && (fd_pipe_out[1] != STDERR_FILENO)) + /* Now connect the `out' pipe to STDOUT */ + if (fd_pipe_err[1] != STDERR_FILENO) { - close (fd_pipe_out[1]); - fd_pipe_out[1] = STDOUT_FILENO; + dup2 (fd_pipe_err[1], STDERR_FILENO); + close (fd_pipe_err[1]); } exec_child (pl); @@ -456,6 +468,7 @@ static int fork_child (program_list_t *pl, int *fd_in, int *fd_out) /* {{{ */ close (fd_pipe_in[0]); close (fd_pipe_out[1]); + close (fd_pipe_err[1]); if (fd_in != NULL) *fd_in = fd_pipe_in[1]; @@ -467,110 +480,159 @@ static int fork_child (program_list_t *pl, int *fd_in, int *fd_out) /* {{{ */ else close (fd_pipe_out[0]); + if (fd_err != NULL) + *fd_err = fd_pipe_err[0]; + else + close (fd_pipe_err[0]); + return (pid); } /* int fork_child }}} */ static int parse_line (char *buffer) /* {{{ */ { - char *fields[256]; - int fields_num; - - fields[0] = "PUTVAL"; - fields_num = strsplit (buffer, fields + 1, STATIC_ARRAY_SIZE(fields) - 1); - - if (strcasecmp (fields[1], "putval") == 0) - return (handle_putval (stdout, fields + 1, fields_num)); + if (strncasecmp ("PUTVAL", buffer, strlen ("PUTVAL")) == 0) + return (handle_putval (stdout, buffer)); +#if !COLLECT_DEBUG +#error "TODO: PUTNOTIF" else if (strcasecmp (fields[1], "putnotif") == 0) return (handle_putnotif (stdout, fields + 1, fields_num)); - - /* compatibility code */ - return (handle_putval (stdout, fields, fields_num + 1)); +#endif + 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)); + } } /* int parse_line }}} */ static void *exec_read_one (void *arg) /* {{{ */ { program_list_t *pl = (program_list_t *) arg; - int fd; - FILE *fh; - char buffer[1024]; + int fd, fd_err, highest_fd; + fd_set fdset, copy; int status; + char buffer[1200]; /* if not completely read */ + char buffer_err[1024]; + char *pbuffer = buffer; + char *pbuffer_err = buffer_err; - status = fork_child (pl, NULL, &fd); + status = fork_child (pl, NULL, &fd, &fd_err); if (status < 0) pthread_exit ((void *) 1); pl->pid = status; assert (pl->pid != 0); - fh = fdopen (fd, "r"); - if (fh == NULL) - { - char errbuf[1024]; - ERROR ("exec plugin: fdopen (%i) failed: %s", fd, - sstrerror (errno, errbuf, sizeof (errbuf))); - kill (pl->pid, SIGTERM); - pl->pid = 0; - close (fd); - pthread_exit ((void *) 1); - } + FD_ZERO( &fdset ); + FD_SET(fd, &fdset); + FD_SET(fd_err, &fdset); - buffer[0] = '\0'; - while (fgets (buffer, sizeof (buffer), fh) != NULL) + /* Determine the highest file descriptor */ + highest_fd = (fd > fd_err) ? fd : fd_err; + + /* We use a copy of fdset, as select modifies it */ + copy = fdset; + + while (select(highest_fd + 1, ©, NULL, NULL, NULL ) > 0) { int len; - len = strlen (buffer); + if (FD_ISSET(fd, ©)) + { + char *pnl; - /* Remove newline from end. */ - while ((len > 0) && ((buffer[len - 1] == '\n') - || (buffer[len - 1] == '\r'))) - buffer[--len] = '\0'; + len = read(fd, pbuffer, sizeof(buffer) - 1 - (pbuffer - buffer)); - DEBUG ("exec plugin: exec_read_one: buffer = %s", buffer); + if (len < 0) + { + if (errno == EAGAIN || errno == EINTR) continue; + break; + } + else if (len == 0) break; /* We've reached EOF */ - if (pl->flags & PL_NAGIOS_PLUGIN) - break; + pbuffer[len] = '\0'; - parse_line (buffer); - } /* while (fgets) */ + len += pbuffer - buffer; + pbuffer = buffer; - fclose (fh); + while ((pnl = strchr(pbuffer, '\n'))) + { + *pnl = '\0'; + if (*(pnl-1) == '\r' ) *(pnl-1) = '\0'; - if (waitpid (pl->pid, &status, 0) > 0) - pl->status = status; + parse_line (pbuffer); - DEBUG ("exec plugin: Child %i exited with status %i.", - (int) pl->pid, pl->status); + pbuffer = ++pnl; + } + /* not completely read ? */ + if (pbuffer - buffer < len) + { + len -= pbuffer - buffer; + memmove(buffer, pbuffer, len); + pbuffer = buffer + len; + } + else + pbuffer = buffer; + } + else if (FD_ISSET(fd_err, ©)) + { + char *pnl; - if (pl->flags & PL_NAGIOS_PLUGIN) - { - notification_t n; + len = read(fd_err, pbuffer_err, sizeof(buffer_err) - 1 - (pbuffer_err - buffer_err)); - memset (&n, '\0', sizeof (n)); - - n.severity = NOTIF_FAILURE; - if (pl->status == 0) - n.severity = NOTIF_OKAY; - else if (pl->status == 1) - n.severity = NOTIF_WARNING; + if (len < 0) + { + if (errno == EAGAIN || errno == EINTR) continue; + break; + } + else if (len == 0) break; /* We've reached EOF */ - strncpy (n.message, buffer, sizeof (n.message)); - n.message[sizeof (n.message) - 1] = '\0'; + pbuffer_err[len] = '\0'; - n.time = time (NULL); + len += pbuffer_err - buffer_err; + pbuffer_err = buffer_err; - strncpy (n.host, hostname_g, sizeof (n.host)); - n.host[sizeof (n.host) - 1] = '\0'; + while ((pnl = strchr(pbuffer_err, '\n'))) + { + *pnl = '\0'; + if (*(pnl-1) == '\r' ) *(pnl-1) = '\0'; - plugin_dispatch_notification (&n); + ERROR ("exec plugin: exec_read_one: error = %s", pbuffer_err); + + pbuffer_err = ++pnl; + } + /* not completely read ? */ + if (pbuffer_err - buffer_err < len) + { + len -= pbuffer_err - buffer_err; + memmove(buffer_err, pbuffer_err, len); + pbuffer_err = buffer_err + len; + } + else + pbuffer_err = buffer_err; + } + /* reset copy */ + copy = fdset; } + if (waitpid (pl->pid, &status, 0) > 0) + pl->status = status; + + DEBUG ("exec plugin: Child %i exited with status %i.", + (int) pl->pid, pl->status); + pl->pid = 0; pthread_mutex_lock (&pl_lock); pl->flags &= ~PL_RUNNING; pthread_mutex_unlock (&pl_lock); + close (fd); + close (fd_err); + pthread_exit ((void *) 0); return (NULL); } /* void *exec_read_one }}} */ @@ -578,14 +640,15 @@ static void *exec_read_one (void *arg) /* {{{ */ static void *exec_notification_one (void *arg) /* {{{ */ { program_list_t *pl = ((program_list_and_notification_t *) arg)->pl; - const notification_t *n = &((program_list_and_notification_t *) arg)->n; + notification_t *n = &((program_list_and_notification_t *) arg)->n; + notification_meta_t *meta; int fd; FILE *fh; int pid; int status; const char *severity; - pid = fork_child (pl, &fd, NULL); + pid = fork_child (pl, &fd, NULL, NULL); if (pid < 0) { sfree (arg); pthread_exit ((void *) 1); @@ -627,6 +690,21 @@ static void *exec_notification_one (void *arg) /* {{{ */ if (strlen (n->type_instance) > 0) fprintf (fh, "TypeInstance: %s\n", n->type_instance); + for (meta = n->meta; meta != NULL; meta = meta->next) + { + if (meta->type == NM_TYPE_STRING) + fprintf (fh, "%s: %s\n", meta->name, meta->value_string); + else if (meta->type == NM_TYPE_SIGNED_INT) + fprintf (fh, "%s: %"PRIi64"\n", meta->name, meta->value_signed_int); + else if (meta->type == NM_TYPE_UNSIGNED_INT) + fprintf (fh, "%s: %"PRIu64"\n", meta->name, meta->value_unsigned_int); + else if (meta->type == NM_TYPE_DOUBLE) + fprintf (fh, "%s: %e\n", meta->name, meta->value_double); + else if (meta->type == NM_TYPE_BOOLEAN) + fprintf (fh, "%s: %s\n", meta->name, + meta->value_boolean ? "true" : "false"); + } + fprintf (fh, "\n%s\n", n->message); fflush (fh); @@ -637,6 +715,7 @@ static void *exec_notification_one (void *arg) /* {{{ */ DEBUG ("exec plugin: Child %i exited with status %i.", pid, status); + plugin_notification_meta_free (n); sfree (arg); pthread_exit ((void *) 0); return (NULL); @@ -662,8 +741,8 @@ static int exec_read (void) /* {{{ */ pthread_t t; pthread_attr_t attr; - /* Only execute `normal' and `nagios' style executables here. */ - if ((pl->flags & (PL_NAGIOS_PLUGIN | PL_NORMAL)) == 0) + /* Only execute `normal' style executables here. */ + if ((pl->flags & PL_NORMAL) == 0) continue; pthread_mutex_lock (&pl_lock); @@ -713,6 +792,11 @@ static int exec_notification (const notification_t *n) pln->pl = pl; memcpy (&pln->n, n, sizeof (notification_t)); + /* Set the `meta' member to NULL, otherwise `plugin_notification_meta_copy' + * will run into an endless loop. */ + pln->n.meta = NULL; + plugin_notification_meta_copy (&pln->n, n); + pthread_attr_init (&attr); pthread_attr_setdetachstate (&attr, PTHREAD_CREATE_DETACHED); pthread_create (&t, &attr, exec_notification_one, (void *) pln);