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