a4de6730843c3d70225165d079b760757b477339
[collectd.git] / src / exec.c
1 /**
2  * collectd - src/exec.c
3  * Copyright (C) 2007-2010  Florian octo Forster
4  * Copyright (C) 2007-2009  Sebastian Harl
5  * Copyright (C) 2008       Peter Holik
6  *
7  * This program is free software; you can redistribute it and/or modify it
8  * under the terms of the GNU General Public License as published by the
9  * Free Software Foundation; only version 2 of the License is applicable.
10  *
11  * This program is distributed in the hope that it will be useful, but
12  * WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License along
17  * with this program; if not, write to the Free Software Foundation, Inc.,
18  * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA
19  *
20  * Authors:
21  *   Florian octo Forster <octo at collectd.org>
22  *   Sebastian Harl <sh at tokkee.org>
23  *   Peter Holik <peter at holik.at>
24  **/
25
26 #define _DEFAULT_SOURCE
27 #define _BSD_SOURCE /* For setgroups */
28
29 #include "collectd.h"
30
31 #include "common.h"
32 #include "plugin.h"
33
34 #include "utils_cmd_putnotif.h"
35 #include "utils_cmd_putval.h"
36
37 #include <grp.h>
38 #include <pwd.h>
39 #include <signal.h>
40 #include <sys/types.h>
41
42 #ifdef HAVE_SYS_CAPABILITY_H
43 #include <sys/capability.h>
44 #endif
45
46 #define PL_NORMAL 0x01
47 #define PL_NOTIF_ACTION 0x02
48
49 #define PL_RUNNING 0x10
50
51 /*
52  * Private data types
53  */
54 /*
55  * Access to this structure is serialized using the `pl_lock' lock and the
56  * `PL_RUNNING' flag. The execution of notifications is *not* serialized, so
57  * all functions used to handle notifications MUST NOT write to this structure.
58  * The `pid' and `status' fields are thus unused if the `PL_NOTIF_ACTION' flag
59  * is set.
60  * The `PL_RUNNING' flag is set in `exec_read' and unset in `exec_read_one'.
61  */
62 struct program_list_s;
63 typedef struct program_list_s program_list_t;
64 struct program_list_s {
65   char *user;
66   char *group;
67   char *exec;
68   char **argv;
69   int pid;
70   int status;
71   int flags;
72   program_list_t *next;
73 };
74
75 typedef struct program_list_and_notification_s {
76   program_list_t *pl;
77   notification_t n;
78 } program_list_and_notification_t;
79
80 /*
81  * constants
82  */
83 const long int MAX_GRBUF_SIZE = 65536;
84
85 /*
86  * Private variables
87  */
88 static program_list_t *pl_head = NULL;
89 static pthread_mutex_t pl_lock = PTHREAD_MUTEX_INITIALIZER;
90
91 /*
92  * Functions
93  */
94 static void sigchld_handler(int __attribute__((unused)) signal) /* {{{ */
95 {
96   pid_t pid;
97   int status;
98   while ((pid = waitpid(-1, &status, WNOHANG)) > 0) {
99     program_list_t *pl;
100     for (pl = pl_head; pl != NULL; pl = pl->next)
101       if (pl->pid == pid)
102         break;
103     if (pl != NULL)
104       pl->status = status;
105   } /* while (waitpid) */
106 } /* void sigchld_handler }}} */
107
108 static int exec_config_exec(oconfig_item_t *ci) /* {{{ */
109 {
110   program_list_t *pl;
111   char buffer[128];
112   int i;
113
114   if (ci->children_num != 0) {
115     WARNING("exec plugin: The config option `%s' may not be a block.", ci->key);
116     return -1;
117   }
118   if (ci->values_num < 2) {
119     WARNING("exec plugin: The config option `%s' needs at least two "
120             "arguments.",
121             ci->key);
122     return -1;
123   }
124   if ((ci->values[0].type != OCONFIG_TYPE_STRING) ||
125       (ci->values[1].type != OCONFIG_TYPE_STRING)) {
126     WARNING("exec plugin: The first two arguments to the `%s' option must "
127             "be string arguments.",
128             ci->key);
129     return -1;
130   }
131
132   pl = calloc(1, sizeof(*pl));
133   if (pl == NULL) {
134     ERROR("exec plugin: calloc failed.");
135     return -1;
136   }
137
138   if (strcasecmp("NotificationExec", ci->key) == 0)
139     pl->flags |= PL_NOTIF_ACTION;
140   else
141     pl->flags |= PL_NORMAL;
142
143   pl->user = strdup(ci->values[0].value.string);
144   if (pl->user == NULL) {
145     ERROR("exec plugin: strdup failed.");
146     sfree(pl);
147     return -1;
148   }
149
150   pl->group = strchr(pl->user, ':');
151   if (pl->group != NULL) {
152     *pl->group = '\0';
153     pl->group++;
154   }
155
156   pl->exec = strdup(ci->values[1].value.string);
157   if (pl->exec == NULL) {
158     ERROR("exec plugin: strdup failed.");
159     sfree(pl->user);
160     sfree(pl);
161     return -1;
162   }
163
164   pl->argv = calloc(ci->values_num, sizeof(*pl->argv));
165   if (pl->argv == NULL) {
166     ERROR("exec plugin: calloc failed.");
167     sfree(pl->exec);
168     sfree(pl->user);
169     sfree(pl);
170     return -1;
171   }
172
173   {
174     char *tmp = strrchr(ci->values[1].value.string, '/');
175     if (tmp == NULL)
176       sstrncpy(buffer, ci->values[1].value.string, sizeof(buffer));
177     else
178       sstrncpy(buffer, tmp + 1, sizeof(buffer));
179   }
180   pl->argv[0] = strdup(buffer);
181   if (pl->argv[0] == NULL) {
182     ERROR("exec plugin: strdup failed.");
183     sfree(pl->argv);
184     sfree(pl->exec);
185     sfree(pl->user);
186     sfree(pl);
187     return -1;
188   }
189
190   for (i = 1; i < (ci->values_num - 1); i++) {
191     if (ci->values[i + 1].type == OCONFIG_TYPE_STRING) {
192       pl->argv[i] = strdup(ci->values[i + 1].value.string);
193     } else {
194       if (ci->values[i + 1].type == OCONFIG_TYPE_NUMBER) {
195         snprintf(buffer, sizeof(buffer), "%lf", ci->values[i + 1].value.number);
196       } else {
197         if (ci->values[i + 1].value.boolean)
198           sstrncpy(buffer, "true", sizeof(buffer));
199         else
200           sstrncpy(buffer, "false", sizeof(buffer));
201       }
202
203       pl->argv[i] = strdup(buffer);
204     }
205
206     if (pl->argv[i] == NULL) {
207       ERROR("exec plugin: strdup failed.");
208       break;
209     }
210   } /* for (i) */
211
212   if (i < (ci->values_num - 1)) {
213     while ((--i) >= 0) {
214       sfree(pl->argv[i]);
215     }
216     sfree(pl->argv);
217     sfree(pl->exec);
218     sfree(pl->user);
219     sfree(pl);
220     return -1;
221   }
222
223   for (i = 0; pl->argv[i] != NULL; i++) {
224     DEBUG("exec plugin: argv[%i] = %s", i, pl->argv[i]);
225   }
226
227   pl->next = pl_head;
228   pl_head = pl;
229
230   return 0;
231 } /* int exec_config_exec }}} */
232
233 static int exec_config(oconfig_item_t *ci) /* {{{ */
234 {
235   for (int i = 0; i < ci->children_num; i++) {
236     oconfig_item_t *child = ci->children + i;
237     if ((strcasecmp("Exec", child->key) == 0) ||
238         (strcasecmp("NotificationExec", child->key) == 0))
239       exec_config_exec(child);
240     else {
241       WARNING("exec plugin: Unknown config option `%s'.", child->key);
242     }
243   } /* for (i) */
244
245   return 0;
246 } /* int exec_config }}} */
247
248 #if !defined(HAVE_SETENV)
249 static char env_interval[64];
250 // max hostname len is 255, so this should be enough
251 static char env_hostname[300];
252 #endif
253
254 static void set_environment(void) /* {{{ */
255 {
256 #ifdef HAVE_SETENV
257   char buffer[1024];
258
259   snprintf(buffer, sizeof(buffer), "%.3f",
260            CDTIME_T_TO_DOUBLE(plugin_get_interval()));
261   setenv("COLLECTD_INTERVAL", buffer, /* overwrite = */ 1);
262
263   sstrncpy(buffer, hostname_g, sizeof(buffer));
264   setenv("COLLECTD_HOSTNAME", buffer, /* overwrite = */ 1);
265 #else
266   snprintf(env_interval, sizeof(env_interval), "COLLECTD_INTERVAL=%.3f",
267            CDTIME_T_TO_DOUBLE(plugin_get_interval()));
268   putenv(env_interval);
269
270   snprintf(env_hostname, sizeof(env_hostname), "COLLECTD_HOSTNAME=%s",
271            hostname_g);
272   putenv(env_hostname);
273 #endif
274 } /* }}} void set_environment */
275
276 __attribute__((noreturn)) static void exec_child(program_list_t *pl, int uid,
277                                                  int gid, int egid) /* {{{ */
278 {
279   int status;
280   char errbuf[1024];
281
282 #if HAVE_SETGROUPS
283   if (getuid() == 0) {
284     gid_t glist[2];
285     size_t glist_len;
286
287     glist[0] = gid;
288     glist_len = 1;
289
290     if ((gid != egid) && (egid != -1)) {
291       glist[1] = egid;
292       glist_len = 2;
293     }
294
295     setgroups(glist_len, glist);
296   }
297 #endif /* HAVE_SETGROUPS */
298
299   status = setgid(gid);
300   if (status != 0) {
301     ERROR("exec plugin: setgid (%i) failed: %s", gid,
302           sstrerror(errno, errbuf, sizeof(errbuf)));
303     exit(-1);
304   }
305
306   if (egid != -1) {
307     status = setegid(egid);
308     if (status != 0) {
309       ERROR("exec plugin: setegid (%i) failed: %s", egid,
310             sstrerror(errno, errbuf, sizeof(errbuf)));
311       exit(-1);
312     }
313   }
314
315   status = setuid(uid);
316   if (status != 0) {
317     ERROR("exec plugin: setuid (%i) failed: %s", uid,
318           sstrerror(errno, errbuf, sizeof(errbuf)));
319     exit(-1);
320   }
321
322   execvp(pl->exec, pl->argv);
323
324   ERROR("exec plugin: Failed to execute ``%s'': %s", pl->exec,
325         sstrerror(errno, errbuf, sizeof(errbuf)));
326   exit(-1);
327 } /* void exec_child }}} */
328
329 static void reset_signal_mask(void) /* {{{ */
330 {
331   sigset_t ss;
332
333   sigemptyset(&ss);
334   sigprocmask(SIG_SETMASK, &ss, /* old mask = */ NULL);
335 } /* }}} void reset_signal_mask */
336
337 static int create_pipe(int fd_pipe[2]) /* {{{ */
338 {
339   char errbuf[1024];
340   int status;
341
342   status = pipe(fd_pipe);
343   if (status != 0) {
344     ERROR("exec plugin: pipe failed: %s",
345           sstrerror(errno, errbuf, sizeof(errbuf)));
346     return -1;
347   }
348
349   return 0;
350 } /* }}} int create_pipe */
351
352 static void close_pipe(int fd_pipe[2]) /* {{{ */
353 {
354   if (fd_pipe[0] != -1)
355     close(fd_pipe[0]);
356
357   if (fd_pipe[1] != -1)
358     close(fd_pipe[1]);
359 } /* }}} void close_pipe */
360
361 /*
362  * Get effective group ID from group name.
363  * Input arguments:
364  *       pl  :program list struct with group name
365  *       gid :group id to fallback in case egid cannot be determined.
366  * Returns:
367  *       egid effective group id if successfull,
368  *            -1 if group is not defined/not found.
369  *            -2 for any buffer allocation error.
370  */
371 static int getegr_id(program_list_t *pl, int gid) /* {{{ */
372 {
373   if (pl->group == NULL) {
374     return -1;
375   }
376   if (strcmp(pl->group, "") == 0) {
377     return gid;
378   }
379   struct group *gr_ptr = NULL;
380   struct group gr;
381
382   long int grbuf_size = sysconf(_SC_GETGR_R_SIZE_MAX);
383   if (grbuf_size <= 0)
384     grbuf_size = sysconf(_SC_PAGESIZE);
385   if (grbuf_size <= 0)
386     grbuf_size = 4096;
387
388   char *temp = NULL;
389   char *grbuf = NULL;
390
391   do {
392     temp = realloc(grbuf, grbuf_size);
393     if (temp == NULL) {
394       ERROR("exec plugin: getegr_id for %s: realloc buffer[%ld] failed ",
395             pl->group, grbuf_size);
396       sfree(grbuf);
397       return -2;
398     }
399     grbuf = temp;
400     if (getgrnam_r(pl->group, &gr, grbuf, grbuf_size, &gr_ptr) == 0) {
401       sfree(grbuf);
402       if (gr_ptr == NULL) {
403         ERROR("exec plugin: No such group: `%s'", pl->group);
404         return -1;
405       }
406       return gr.gr_gid;
407     } else if (errno == ERANGE) {
408       grbuf_size += grbuf_size; // increment buffer size and try again
409     } else {
410       char errbuf[1024];
411       ERROR("exec plugin: getegr_id failed %s",
412             sstrerror(errno, errbuf, sizeof(errbuf)));
413       sfree(grbuf);
414       return -2;
415     }
416   } while (grbuf_size <= MAX_GRBUF_SIZE);
417   ERROR("exec plugin: getegr_id Max grbuf size reached  for %s", pl->group);
418   sfree(grbuf);
419   return -2;
420 } /* }}} */
421
422 /*
423  * Creates three pipes (one for reading, one for writing and one for errors),
424  * forks a child, sets up the pipes so that fd_in is connected to STDIN of
425  * the child and fd_out is connected to STDOUT and fd_err is connected to STDERR
426  * of the child. Then is calls `exec_child'.
427  */
428 static int fork_child(program_list_t *pl, int *fd_in, int *fd_out,
429                       int *fd_err) /* {{{ */
430 {
431   int fd_pipe_in[2] = {-1, -1};
432   int fd_pipe_out[2] = {-1, -1};
433   int fd_pipe_err[2] = {-1, -1};
434   char errbuf[1024];
435   int status;
436   int pid;
437
438   int uid;
439   int gid;
440   int egid;
441
442   struct passwd *sp_ptr;
443   struct passwd sp;
444
445   if (pl->pid != 0)
446     return -1;
447
448   long int nambuf_size = sysconf(_SC_GETPW_R_SIZE_MAX);
449   if (nambuf_size <= 0)
450     nambuf_size = sysconf(_SC_PAGESIZE);
451   if (nambuf_size <= 0)
452     nambuf_size = 4096;
453   char nambuf[nambuf_size];
454
455   if ((create_pipe(fd_pipe_in) == -1) || (create_pipe(fd_pipe_out) == -1) ||
456       (create_pipe(fd_pipe_err) == -1))
457     goto failed;
458
459   sp_ptr = NULL;
460   status = getpwnam_r(pl->user, &sp, nambuf, sizeof(nambuf), &sp_ptr);
461   if (status != 0) {
462     ERROR("exec plugin: Failed to get user information for user ``%s'': %s",
463           pl->user, sstrerror(status, errbuf, sizeof(errbuf)));
464     goto failed;
465   }
466
467   if (sp_ptr == NULL) {
468     ERROR("exec plugin: No such user: `%s'", pl->user);
469     goto failed;
470   }
471
472   uid = sp.pw_uid;
473   gid = sp.pw_gid;
474   if (uid == 0) {
475     ERROR("exec plugin: Cowardly refusing to exec program as root.");
476     goto failed;
477   }
478
479   egid = getegr_id(pl, gid);
480   if (egid == -2) {
481     goto failed;
482   }
483
484   pid = fork();
485   if (pid < 0) {
486     ERROR("exec plugin: fork failed: %s",
487           sstrerror(errno, errbuf, sizeof(errbuf)));
488     goto failed;
489   } else if (pid == 0) {
490     int fd_num;
491
492     /* Close all file descriptors but the pipe end we need. */
493     fd_num = getdtablesize();
494     for (int fd = 0; fd < fd_num; fd++) {
495       if ((fd == fd_pipe_in[0]) || (fd == fd_pipe_out[1]) ||
496           (fd == fd_pipe_err[1]))
497         continue;
498       close(fd);
499     }
500
501     /* Connect the `in' pipe to STDIN */
502     if (fd_pipe_in[0] != STDIN_FILENO) {
503       dup2(fd_pipe_in[0], STDIN_FILENO);
504       close(fd_pipe_in[0]);
505     }
506
507     /* Now connect the `out' pipe to STDOUT */
508     if (fd_pipe_out[1] != STDOUT_FILENO) {
509       dup2(fd_pipe_out[1], STDOUT_FILENO);
510       close(fd_pipe_out[1]);
511     }
512
513     /* Now connect the `err' pipe to STDERR */
514     if (fd_pipe_err[1] != STDERR_FILENO) {
515       dup2(fd_pipe_err[1], STDERR_FILENO);
516       close(fd_pipe_err[1]);
517     }
518
519     set_environment();
520
521     /* Unblock all signals */
522     reset_signal_mask();
523
524     exec_child(pl, uid, gid, egid);
525     /* does not return */
526   }
527
528   close(fd_pipe_in[0]);
529   close(fd_pipe_out[1]);
530   close(fd_pipe_err[1]);
531
532   if (fd_in != NULL)
533     *fd_in = fd_pipe_in[1];
534   else
535     close(fd_pipe_in[1]);
536
537   if (fd_out != NULL)
538     *fd_out = fd_pipe_out[0];
539   else
540     close(fd_pipe_out[0]);
541
542   if (fd_err != NULL)
543     *fd_err = fd_pipe_err[0];
544   else
545     close(fd_pipe_err[0]);
546
547   return pid;
548
549 failed:
550   close_pipe(fd_pipe_in);
551   close_pipe(fd_pipe_out);
552   close_pipe(fd_pipe_err);
553
554   return -1;
555 } /* int fork_child }}} */
556
557 static int parse_line(char *buffer) /* {{{ */
558 {
559   if (strncasecmp("PUTVAL", buffer, strlen("PUTVAL")) == 0)
560     return cmd_handle_putval(stdout, buffer);
561   else if (strncasecmp("PUTNOTIF", buffer, strlen("PUTNOTIF")) == 0)
562     return handle_putnotif(stdout, buffer);
563   else {
564     ERROR("exec plugin: Unable to parse command, ignoring line: \"%s\"",
565           buffer);
566     return -1;
567   }
568 } /* int parse_line }}} */
569
570 static void *exec_read_one(void *arg) /* {{{ */
571 {
572   program_list_t *pl = (program_list_t *)arg;
573   int fd, fd_err, highest_fd;
574   fd_set fdset, copy;
575   int status;
576   char buffer[1200]; /* if not completely read */
577   char buffer_err[1024];
578   char *pbuffer = buffer;
579   char *pbuffer_err = buffer_err;
580
581   status = fork_child(pl, NULL, &fd, &fd_err);
582   if (status < 0) {
583     /* Reset the "running" flag */
584     pthread_mutex_lock(&pl_lock);
585     pl->flags &= ~PL_RUNNING;
586     pthread_mutex_unlock(&pl_lock);
587     pthread_exit((void *)1);
588   }
589   pl->pid = status;
590
591   assert(pl->pid != 0);
592
593   FD_ZERO(&fdset);
594   FD_SET(fd, &fdset);
595   FD_SET(fd_err, &fdset);
596
597   /* Determine the highest file descriptor */
598   highest_fd = (fd > fd_err) ? fd : fd_err;
599
600   /* We use a copy of fdset, as select modifies it */
601   copy = fdset;
602
603   while (1) {
604     int len;
605
606     status = select(highest_fd + 1, &copy, NULL, NULL, NULL);
607     if (status < 0) {
608       if (errno == EINTR)
609         continue;
610       break;
611     }
612
613     if (FD_ISSET(fd, &copy)) {
614       char *pnl;
615
616       len = read(fd, pbuffer, sizeof(buffer) - 1 - (pbuffer - buffer));
617
618       if (len < 0) {
619         if (errno == EAGAIN || errno == EINTR)
620           continue;
621         break;
622       } else if (len == 0)
623         break; /* We've reached EOF */
624
625       pbuffer[len] = '\0';
626
627       len += pbuffer - buffer;
628       pbuffer = buffer;
629
630       while ((pnl = strchr(pbuffer, '\n'))) {
631         *pnl = '\0';
632         if (*(pnl - 1) == '\r')
633           *(pnl - 1) = '\0';
634
635         parse_line(pbuffer);
636
637         pbuffer = ++pnl;
638       }
639       /* not completely read ? */
640       if (pbuffer - buffer < len) {
641         len -= pbuffer - buffer;
642         memmove(buffer, pbuffer, len);
643         pbuffer = buffer + len;
644       } else
645         pbuffer = buffer;
646     } else if (FD_ISSET(fd_err, &copy)) {
647       char *pnl;
648
649       len = read(fd_err, pbuffer_err,
650                  sizeof(buffer_err) - 1 - (pbuffer_err - buffer_err));
651
652       if (len < 0) {
653         if (errno == EAGAIN || errno == EINTR)
654           continue;
655         break;
656       } else if (len == 0) {
657         /* We've reached EOF */
658         NOTICE("exec plugin: Program `%s' has closed STDERR.", pl->exec);
659
660         /* Remove file descriptor form select() set. */
661         FD_CLR(fd_err, &fdset);
662         copy = fdset;
663         highest_fd = fd;
664
665         /* Clean up file descriptor */
666         close(fd_err);
667         fd_err = -1;
668         continue;
669       }
670
671       pbuffer_err[len] = '\0';
672
673       len += pbuffer_err - buffer_err;
674       pbuffer_err = buffer_err;
675
676       while ((pnl = strchr(pbuffer_err, '\n'))) {
677         *pnl = '\0';
678         if (*(pnl - 1) == '\r')
679           *(pnl - 1) = '\0';
680
681         ERROR("exec plugin: exec_read_one: error = %s", pbuffer_err);
682
683         pbuffer_err = ++pnl;
684       }
685       /* not completely read ? */
686       if (pbuffer_err - buffer_err < len) {
687         len -= pbuffer_err - buffer_err;
688         memmove(buffer_err, pbuffer_err, len);
689         pbuffer_err = buffer_err + len;
690       } else
691         pbuffer_err = buffer_err;
692     }
693     /* reset copy */
694     copy = fdset;
695   }
696
697   DEBUG("exec plugin: exec_read_one: Waiting for `%s' to exit.", pl->exec);
698   if (waitpid(pl->pid, &status, 0) > 0)
699     pl->status = status;
700
701   DEBUG("exec plugin: Child %i exited with status %i.", (int)pl->pid,
702         pl->status);
703
704   pl->pid = 0;
705
706   pthread_mutex_lock(&pl_lock);
707   pl->flags &= ~PL_RUNNING;
708   pthread_mutex_unlock(&pl_lock);
709
710   close(fd);
711   if (fd_err >= 0)
712     close(fd_err);
713
714   pthread_exit((void *)0);
715   return NULL;
716 } /* void *exec_read_one }}} */
717
718 static void *exec_notification_one(void *arg) /* {{{ */
719 {
720   program_list_t *pl = ((program_list_and_notification_t *)arg)->pl;
721   notification_t *n = &((program_list_and_notification_t *)arg)->n;
722   int fd;
723   FILE *fh;
724   int pid;
725   int status;
726   const char *severity;
727
728   pid = fork_child(pl, &fd, NULL, NULL);
729   if (pid < 0) {
730     sfree(arg);
731     pthread_exit((void *)1);
732   }
733
734   fh = fdopen(fd, "w");
735   if (fh == NULL) {
736     char errbuf[1024];
737     ERROR("exec plugin: fdopen (%i) failed: %s", fd,
738           sstrerror(errno, errbuf, sizeof(errbuf)));
739     kill(pid, SIGTERM);
740     close(fd);
741     sfree(arg);
742     pthread_exit((void *)1);
743   }
744
745   severity = "FAILURE";
746   if (n->severity == NOTIF_WARNING)
747     severity = "WARNING";
748   else if (n->severity == NOTIF_OKAY)
749     severity = "OKAY";
750
751   fprintf(fh, "Severity: %s\n"
752               "Time: %.3f\n",
753           severity, CDTIME_T_TO_DOUBLE(n->time));
754
755   /* Print the optional fields */
756   if (strlen(n->host) > 0)
757     fprintf(fh, "Host: %s\n", n->host);
758   if (strlen(n->plugin) > 0)
759     fprintf(fh, "Plugin: %s\n", n->plugin);
760   if (strlen(n->plugin_instance) > 0)
761     fprintf(fh, "PluginInstance: %s\n", n->plugin_instance);
762   if (strlen(n->type) > 0)
763     fprintf(fh, "Type: %s\n", n->type);
764   if (strlen(n->type_instance) > 0)
765     fprintf(fh, "TypeInstance: %s\n", n->type_instance);
766
767   for (notification_meta_t *meta = n->meta; meta != NULL; meta = meta->next) {
768     if (meta->type == NM_TYPE_STRING)
769       fprintf(fh, "%s: %s\n", meta->name, meta->nm_value.nm_string);
770     else if (meta->type == NM_TYPE_SIGNED_INT)
771       fprintf(fh, "%s: %" PRIi64 "\n", meta->name,
772               meta->nm_value.nm_signed_int);
773     else if (meta->type == NM_TYPE_UNSIGNED_INT)
774       fprintf(fh, "%s: %" PRIu64 "\n", meta->name,
775               meta->nm_value.nm_unsigned_int);
776     else if (meta->type == NM_TYPE_DOUBLE)
777       fprintf(fh, "%s: %e\n", meta->name, meta->nm_value.nm_double);
778     else if (meta->type == NM_TYPE_BOOLEAN)
779       fprintf(fh, "%s: %s\n", meta->name,
780               meta->nm_value.nm_boolean ? "true" : "false");
781   }
782
783   fprintf(fh, "\n%s\n", n->message);
784
785   fflush(fh);
786   fclose(fh);
787
788   waitpid(pid, &status, 0);
789
790   DEBUG("exec plugin: Child %i exited with status %i.", pid, status);
791
792   if (n->meta != NULL)
793     plugin_notification_meta_free(n->meta);
794   n->meta = NULL;
795   sfree(arg);
796   pthread_exit((void *)0);
797   return NULL;
798 } /* void *exec_notification_one }}} */
799
800 static int exec_init(void) /* {{{ */
801 {
802   struct sigaction sa = {.sa_handler = sigchld_handler};
803
804   sigaction(SIGCHLD, &sa, NULL);
805
806 #if defined(HAVE_SYS_CAPABILITY_H) && defined(CAP_SETUID) && defined(CAP_SETGID)
807   if ((check_capability(CAP_SETUID) != 0) ||
808       (check_capability(CAP_SETGID) != 0)) {
809     if (getuid() == 0)
810       WARNING(
811           "exec plugin: Running collectd as root, but the CAP_SETUID "
812           "or CAP_SETGID capabilities are missing. The plugin's read function "
813           "will probably fail. Is your init system dropping capabilities?");
814     else
815       WARNING(
816           "exec plugin: collectd doesn't have the CAP_SETUID or "
817           "CAP_SETGID capabilities. If you don't want to run collectd as root, "
818           "try running \"setcap 'cap_setuid=ep cap_setgid=ep'\" on the "
819           "collectd binary.");
820   }
821 #endif
822
823   return 0;
824 } /* int exec_init }}} */
825
826 static int exec_read(void) /* {{{ */
827 {
828   for (program_list_t *pl = pl_head; pl != NULL; pl = pl->next) {
829     pthread_t t;
830     pthread_attr_t attr;
831
832     /* Only execute `normal' style executables here. */
833     if ((pl->flags & PL_NORMAL) == 0)
834       continue;
835
836     pthread_mutex_lock(&pl_lock);
837     /* Skip if a child is already running. */
838     if ((pl->flags & PL_RUNNING) != 0) {
839       pthread_mutex_unlock(&pl_lock);
840       continue;
841     }
842     pl->flags |= PL_RUNNING;
843     pthread_mutex_unlock(&pl_lock);
844
845     pthread_attr_init(&attr);
846     pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
847     int status =
848         plugin_thread_create(&t, &attr, exec_read_one, (void *)pl, "exec read");
849     if (status != 0) {
850       ERROR("exec plugin: plugin_thread_create failed.");
851     }
852     pthread_attr_destroy(&attr);
853   } /* for (pl) */
854
855   return 0;
856 } /* int exec_read }}} */
857
858 static int exec_notification(const notification_t *n, /* {{{ */
859                              user_data_t __attribute__((unused)) * user_data) {
860   program_list_and_notification_t *pln;
861
862   for (program_list_t *pl = pl_head; pl != NULL; pl = pl->next) {
863     pthread_t t;
864     pthread_attr_t attr;
865
866     /* Only execute `notification' style executables here. */
867     if ((pl->flags & PL_NOTIF_ACTION) == 0)
868       continue;
869
870     /* Skip if a child is already running. */
871     if (pl->pid != 0)
872       continue;
873
874     pln = malloc(sizeof(*pln));
875     if (pln == NULL) {
876       ERROR("exec plugin: malloc failed.");
877       continue;
878     }
879
880     pln->pl = pl;
881     memcpy(&pln->n, n, sizeof(notification_t));
882
883     /* Set the `meta' member to NULL, otherwise `plugin_notification_meta_copy'
884      * will run into an endless loop. */
885     pln->n.meta = NULL;
886     plugin_notification_meta_copy(&pln->n, n);
887
888     pthread_attr_init(&attr);
889     pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
890     int status = plugin_thread_create(&t, &attr, exec_notification_one,
891                                       (void *)pln, "exec notify");
892     if (status != 0) {
893       ERROR("exec plugin: plugin_thread_create failed.");
894     }
895     pthread_attr_destroy(&attr);
896   } /* for (pl) */
897
898   return 0;
899 } /* }}} int exec_notification */
900
901 static int exec_shutdown(void) /* {{{ */
902 {
903   program_list_t *pl;
904   program_list_t *next;
905
906   pl = pl_head;
907   while (pl != NULL) {
908     next = pl->next;
909
910     if (pl->pid > 0) {
911       kill(pl->pid, SIGTERM);
912       INFO("exec plugin: Sent SIGTERM to %hu", (unsigned short int)pl->pid);
913     }
914
915     sfree(pl->user);
916     sfree(pl);
917
918     pl = next;
919   } /* while (pl) */
920   pl_head = NULL;
921
922   return 0;
923 } /* int exec_shutdown }}} */
924
925 void module_register(void) {
926   plugin_register_complex_config("exec", exec_config);
927   plugin_register_init("exec", exec_init);
928   plugin_register_read("exec", exec_read);
929   plugin_register_notification("exec", exec_notification,
930                                /* user_data = */ NULL);
931   plugin_register_shutdown("exec", exec_shutdown);
932 } /* void module_register */