rrdtool plugin: Sort the list of user defined `RRATimespan's.
[collectd.git] / src / exec.c
1 /**
2  * collectd - src/exec.c
3  * Copyright (C) 2007  Florian octo Forster
4  *
5  * This program is free software; you can redistribute it and/or modify it
6  * under the terms of the GNU General Public License as published by the
7  * Free Software Foundation; only version 2 of the License is applicable.
8  *
9  * This program is distributed in the hope that it will be useful, but
10  * WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License along
15  * with this program; if not, write to the Free Software Foundation, Inc.,
16  * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA
17  *
18  * Authors:
19  *   Florian octo Forster <octo at verplant.org>
20  **/
21
22 #include "collectd.h"
23 #include "common.h"
24 #include "plugin.h"
25 #include "utils_cmd_putval.h"
26
27 #include <sys/types.h>
28 #include <pwd.h>
29 #include <grp.h>
30 #include <signal.h>
31
32 #include <pthread.h>
33
34 /*
35  * Private data types
36  */
37 struct program_list_s;
38 typedef struct program_list_s program_list_t;
39 struct program_list_s
40 {
41   char           *user;
42   char           *group;
43   char           *exec;
44   int             pid;
45   program_list_t *next;
46 };
47
48 /*
49  * Private variables
50  */
51 static const char *config_keys[] =
52 {
53   "Exec"
54 };
55 static int config_keys_num = STATIC_ARRAY_SIZE (config_keys);
56
57 static program_list_t *pl_head = NULL;
58
59 /*
60  * Functions
61  */
62 static int exec_config (const char *key, const char *value)
63 {
64   if (strcasecmp ("Exec", key) == 0)
65   {
66     program_list_t *pl;
67     pl = (program_list_t *) malloc (sizeof (program_list_t));
68     if (pl == NULL)
69       return (1);
70     memset (pl, '\0', sizeof (program_list_t));
71
72     pl->user = strdup (value);
73     if (pl->user == NULL)
74     {
75       sfree (pl);
76       return (1);
77     }
78
79     pl->exec = strchr (pl->user, ' ');
80     if (pl->exec == NULL)
81     {
82       sfree (pl->user);
83       sfree (pl);
84       return (1);
85     }
86     while (*pl->exec == ' ')
87     {
88       *pl->exec = '\0';
89       pl->exec++;
90     }
91
92     if (*pl->exec == '\0')
93     {
94       sfree (pl->user);
95       sfree (pl);
96       return (1);
97     }
98
99     pl->next = pl_head;
100     pl_head = pl;
101
102     pl->group = strchr (pl->user, ':');
103     if (NULL != pl->group) {
104       *pl->group = '\0';
105       pl->group++;
106     }
107   }
108   else
109   {
110     return (-1);
111   }
112
113   return (0);
114 } /* int exec_config */
115
116 static void exec_child (program_list_t *pl)
117 {
118   int status;
119   int uid;
120   int gid;
121   int egid;
122   char *arg0;
123
124   struct passwd *sp_ptr;
125   struct passwd sp;
126   char nambuf[2048];
127   char errbuf[1024];
128
129   sp_ptr = NULL;
130   status = getpwnam_r (pl->user, &sp, nambuf, sizeof (nambuf), &sp_ptr);
131   if (status != 0)
132   {
133     ERROR ("exec plugin: getpwnam_r failed: %s",
134         sstrerror (errno, errbuf, sizeof (errbuf)));
135     exit (-1);
136   }
137   if (sp_ptr == NULL)
138   {
139     ERROR ("exec plugin: No such user: `%s'", pl->user);
140     exit (-1);
141   }
142
143   uid = sp.pw_uid;
144   gid = sp.pw_gid;
145   if (uid == 0)
146   {
147     ERROR ("exec plugin: Cowardly refusing to exec program as root.");
148     exit (-1);
149   }
150
151   /* The group configured in the configfile is set as effective group, because
152    * this way the forked process can (re-)gain the user's primary group. */
153   egid = -1;
154   if (NULL != pl->group)
155   {
156     if ('\0' != *pl->group) {
157       struct group *gr_ptr = NULL;
158       struct group gr;
159
160       status = getgrnam_r (pl->group, &gr, nambuf, sizeof (nambuf), &gr_ptr);
161       if (0 != status)
162       {
163         ERROR ("exec plugin: getgrnam_r failed: %s",
164             sstrerror (errno, errbuf, sizeof (errbuf)));
165         exit (-1);
166       }
167       if (NULL == gr_ptr)
168       {
169         ERROR ("exec plugin: No such group: `%s'", pl->group);
170         exit (-1);
171       }
172
173       egid = gr.gr_gid;
174     }
175     else
176     {
177       egid = gid;
178     }
179   } /* if (pl->group == NULL) */
180
181   status = setgid (gid);
182   if (status != 0)
183   {
184     ERROR ("exec plugin: setgid (%i) failed: %s",
185         gid, sstrerror (errno, errbuf, sizeof (errbuf)));
186     exit (-1);
187   }
188
189   if (egid != -1)
190   {
191     status = setegid (egid);
192     if (status != 0)
193     {
194       ERROR ("exec plugin: setegid (%i) failed: %s",
195           egid, sstrerror (errno, errbuf, sizeof (errbuf)));
196       exit (-1);
197     }
198   }
199
200   status = setuid (uid);
201   if (status != 0)
202   {
203     ERROR ("exec plugin: setuid (%i) failed: %s",
204         uid, sstrerror (errno, errbuf, sizeof (errbuf)));
205     exit (-1);
206   }
207
208   arg0 = strrchr (pl->exec, '/');
209   if (arg0 != NULL)
210     arg0++;
211   if ((arg0 == NULL) || (*arg0 == '\0'))
212     arg0 = pl->exec;
213
214   status = execlp (pl->exec, arg0, (char *) 0);
215
216   ERROR ("exec plugin: exec failed: %s",
217       sstrerror (errno, errbuf, sizeof (errbuf)));
218   exit (-1);
219 } /* void exec_child */
220
221 static int fork_child (program_list_t *pl)
222 {
223   int fd_pipe[2];
224   int status;
225
226   if (pl->pid != 0)
227     return (-1);
228
229   status = pipe (fd_pipe);
230   if (status != 0)
231   {
232     char errbuf[1024];
233     ERROR ("exec plugin: pipe failed: %s",
234         sstrerror (errno, errbuf, sizeof (errbuf)));
235     return (-1);
236   }
237
238   pl->pid = fork ();
239   if (pl->pid < 0)
240   {
241     char errbuf[1024];
242     ERROR ("exec plugin: fork failed: %s",
243         sstrerror (errno, errbuf, sizeof (errbuf)));
244     return (-1);
245   }
246   else if (pl->pid == 0)
247   {
248     close (fd_pipe[0]);
249
250     /* Connect the pipe to STDOUT and STDERR */
251     if (fd_pipe[1] != STDOUT_FILENO)
252       dup2 (fd_pipe[1], STDOUT_FILENO);
253     if (fd_pipe[1] != STDERR_FILENO)
254       dup2 (fd_pipe[1], STDERR_FILENO);
255     if ((fd_pipe[1] != STDOUT_FILENO) && (fd_pipe[1] != STDERR_FILENO))
256       close (fd_pipe[1]);
257
258     exec_child (pl);
259     /* does not return */
260   }
261
262   close (fd_pipe[1]);
263   return (fd_pipe[0]);
264 } /* int fork_child */
265
266 static int parse_line (char *buffer)
267 {
268   char *fields[256];
269   int fields_num;
270
271   fields[0] = "PUTVAL";
272   fields_num = strsplit (buffer, &fields[1], STATIC_ARRAY_SIZE(fields) - 1);
273
274   handle_putval (stdout, fields, fields_num + 1);
275   return (0);
276 } /* int parse_line */
277
278 static void *exec_read_one (void *arg)
279 {
280   program_list_t *pl = (program_list_t *) arg;
281   int fd;
282   FILE *fh;
283   char buffer[1024];
284
285   fd = fork_child (pl);
286   if (fd < 0)
287     pthread_exit ((void *) 1);
288
289   assert (pl->pid != 0);
290
291   fh = fdopen (fd, "r");
292   if (fh == NULL)
293   {
294     char errbuf[1024];
295     ERROR ("exec plugin: fdopen (%i) failed: %s", fd,
296         sstrerror (errno, errbuf, sizeof (errbuf)));
297     kill (pl->pid, SIGTERM);
298     pl->pid = 0;
299     close (fd);
300     pthread_exit ((void *) 1);
301   }
302
303   while (fgets (buffer, sizeof (buffer), fh) != NULL)
304   {
305     int len;
306
307     len = strlen (buffer);
308
309     /* Remove newline from end. */
310     while ((len > 0) && ((buffer[len - 1] == '\n')
311           || (buffer[len - 1] == '\r')))
312       buffer[--len] = '\0';
313
314     DEBUG ("exec plugin: exec_read_one: buffer = %s", buffer);
315
316     parse_line (buffer);
317   } /* while (fgets) */
318
319   fclose (fh);
320   pl->pid = 0;
321
322   pthread_exit ((void *) 0);
323   return (NULL);
324 } /* void *exec_read_one */
325
326 static int exec_read (void)
327 {
328   program_list_t *pl;
329
330   for (pl = pl_head; pl != NULL; pl = pl->next)
331   {
332     pthread_t t;
333     pthread_attr_t attr;
334
335     if (pl->pid != 0)
336       continue;
337
338     pthread_attr_init (&attr);
339     pthread_attr_setdetachstate (&attr, PTHREAD_CREATE_DETACHED);
340     pthread_create (&t, &attr, exec_read_one, (void *) pl);
341   } /* for (pl) */
342
343   return (0);
344 } /* int exec_read */
345
346 static int exec_shutdown (void)
347 {
348   program_list_t *pl;
349   program_list_t *next;
350
351   pl = pl_head;
352   while (pl != NULL)
353   {
354     next = pl->next;
355
356     if (pl->pid > 0)
357     {
358       kill (pl->pid, SIGTERM);
359       INFO ("exec plugin: Sent SIGTERM to %hu", (unsigned short int) pl->pid);
360     }
361
362     sfree (pl->user);
363     sfree (pl);
364
365     pl = next;
366   } /* while (pl) */
367   pl_head = NULL;
368
369   return (0);
370 } /* int exec_shutdown */
371
372 void module_register (void)
373 {
374   plugin_register_config ("exec", exec_config, config_keys, config_keys_num);
375   plugin_register_read ("exec", exec_read);
376   plugin_register_shutdown ("exec", exec_shutdown);
377 } /* void module_register */
378
379 /*
380  * vim:shiftwidth=2:softtabstop=2:tabstop=8
381  */