exec and unixsock plugin: Move the shared `PUTVAL' code into src/utils_cmd_putval...
[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 <signal.h>
30
31 #include <pthread.h>
32
33 /*
34  * Private data types
35  */
36 struct program_list_s;
37 typedef struct program_list_s program_list_t;
38 struct program_list_s
39 {
40   char           *user;
41   char           *exec;
42   int             pid;
43   program_list_t *next;
44 };
45
46 /*
47  * Private variables
48  */
49 static const char *config_keys[] =
50 {
51   "Exec"
52 };
53 static int config_keys_num = STATIC_ARRAY_SIZE (config_keys);
54
55 static program_list_t *pl_head = NULL;
56
57 /*
58  * Functions
59  */
60 static int exec_config (const char *key, const char *value)
61 {
62   if (strcasecmp ("Exec", key) == 0)
63   {
64     program_list_t *pl;
65     pl = (program_list_t *) malloc (sizeof (program_list_t));
66     if (pl == NULL)
67       return (1);
68     memset (pl, '\0', sizeof (program_list_t));
69
70     pl->user = strdup (value);
71     if (pl->user == NULL)
72     {
73       sfree (pl);
74       return (1);
75     }
76
77     pl->exec = strchr (pl->user, ' ');
78     if (pl->exec == NULL)
79     {
80       sfree (pl->user);
81       sfree (pl);
82       return (1);
83     }
84     while (*pl->exec == ' ')
85     {
86       *pl->exec = '\0';
87       pl->exec++;
88     }
89
90     if (*pl->exec == '\0')
91     {
92       sfree (pl->user);
93       sfree (pl);
94       return (1);
95     }
96
97     pl->next = pl_head;
98     pl_head = pl;
99   }
100   else
101   {
102     return (-1);
103   }
104
105   return (0);
106 } /* int exec_config */
107
108 static void exec_child (program_list_t *pl)
109 {
110   int status;
111   int uid;
112   char *arg0;
113
114   struct passwd *sp_ptr;
115   struct passwd sp;
116   char pwnambuf[2048];
117   char errbuf[1024];
118
119   sp_ptr = NULL;
120   status = getpwnam_r (pl->user, &sp, pwnambuf, sizeof (pwnambuf), &sp_ptr);
121   if (status != 0)
122   {
123     ERROR ("exec plugin: getpwnam_r failed: %s",
124         sstrerror (errno, errbuf, sizeof (errbuf)));
125     exit (-1);
126   }
127   if (sp_ptr == NULL)
128   {
129     ERROR ("exec plugin: No such user: `%s'", pl->user);
130     exit (-1);
131   }
132
133   uid = sp.pw_uid;
134   if (uid == 0)
135   {
136     ERROR ("exec plugin: Cowardly refusing to exec program as root.");
137     exit (-1);
138   }
139
140   status = setuid (uid);
141   if (status != 0)
142   {
143     ERROR ("exec plugin: setuid failed: %s",
144         sstrerror (errno, errbuf, sizeof (errbuf)));
145     exit (-1);
146   }
147
148   arg0 = strrchr (pl->exec, '/');
149   if (arg0 != NULL)
150     arg0++;
151   if ((arg0 == NULL) || (*arg0 == '\0'))
152     arg0 = pl->exec;
153
154   status = execlp (pl->exec, arg0, (char *) 0);
155
156   ERROR ("exec plugin: exec failed: %s",
157       sstrerror (errno, errbuf, sizeof (errbuf)));
158   exit (-1);
159 } /* void exec_child */
160
161 static int fork_child (program_list_t *pl)
162 {
163   int fd_pipe[2];
164   int status;
165
166   if (pl->pid != 0)
167     return (-1);
168
169   status = pipe (fd_pipe);
170   if (status != 0)
171   {
172     char errbuf[1024];
173     ERROR ("exec plugin: pipe failed: %s",
174         sstrerror (errno, errbuf, sizeof (errbuf)));
175     return (-1);
176   }
177
178   pl->pid = fork ();
179   if (pl->pid < 0)
180   {
181     char errbuf[1024];
182     ERROR ("exec plugin: fork failed: %s",
183         sstrerror (errno, errbuf, sizeof (errbuf)));
184     return (-1);
185   }
186   else if (pl->pid == 0)
187   {
188     close (fd_pipe[0]);
189
190     /* Connect the pipe to STDOUT and STDERR */
191     if (fd_pipe[1] != STDOUT_FILENO)
192       dup2 (fd_pipe[1], STDOUT_FILENO);
193     if (fd_pipe[1] != STDERR_FILENO)
194       dup2 (fd_pipe[1], STDERR_FILENO);
195     if ((fd_pipe[1] != STDOUT_FILENO) && (fd_pipe[1] != STDERR_FILENO))
196       close (fd_pipe[1]);
197
198     exec_child (pl);
199     /* does not return */
200   }
201
202   close (fd_pipe[1]);
203   return (fd_pipe[0]);
204 } /* int fork_child */
205
206 static int parse_line (char *buffer)
207 {
208   char *fields[256];
209   int fields_num;
210
211   fields[0] = "PUTVAL";
212   fields_num = strsplit (buffer, &fields[1], STATIC_ARRAY_SIZE(fields) - 1);
213
214   handle_putval (stdout, fields, fields_num + 1);
215 } /* int parse_line */
216
217 static void *exec_read_one (void *arg)
218 {
219   program_list_t *pl = (program_list_t *) arg;
220   int fd;
221   FILE *fh;
222   char buffer[1024];
223
224   fd = fork_child (pl);
225   if (fd < 0)
226     pthread_exit ((void *) 1);
227
228   assert (pl->pid != 0);
229
230   fh = fdopen (fd, "r");
231   if (fh == NULL)
232   {
233     char errbuf[1024];
234     ERROR ("exec plugin: fdopen (%i) failed: %s", fd,
235         sstrerror (errno, errbuf, sizeof (errbuf)));
236     kill (pl->pid, SIGTERM);
237     close (fd);
238     pthread_exit ((void *) 1);
239   }
240
241   while (fgets (buffer, sizeof (buffer), fh) != NULL)
242   {
243     int len;
244
245     len = strlen (buffer);
246
247     /* Remove newline from end. */
248     while ((len > 0) && ((buffer[len - 1] == '\n')
249           || (buffer[len - 1] == '\r')))
250       buffer[--len] = '\0';
251
252     DEBUG ("exec plugin: exec_read_one: buffer = %s", buffer);
253
254     parse_line (buffer);
255   } /* while (fgets) */
256
257   fclose (fh);
258   pl->pid = 0;
259
260   pthread_exit ((void *) 0);
261   return (NULL);
262 } /* void *exec_read_one */
263
264 static int exec_read (void)
265 {
266   program_list_t *pl;
267
268   for (pl = pl_head; pl != NULL; pl = pl->next)
269   {
270     pthread_t t;
271     pthread_attr_t attr;
272
273     if (pl->pid != 0)
274       continue;
275
276     pthread_attr_init (&attr);
277     pthread_attr_setdetachstate (&attr, PTHREAD_CREATE_DETACHED);
278     pthread_create (&t, &attr, exec_read_one, (void *) pl);
279   } /* for (pl) */
280
281   return (0);
282 } /* int exec_read */
283
284 static int exec_shutdown (void)
285 {
286   program_list_t *pl;
287   program_list_t *next;
288
289   pl = pl_head;
290   while (pl != NULL)
291   {
292     next = pl->next;
293
294     if (pl->pid > 0)
295     {
296       kill (pl->pid, SIGTERM);
297       INFO ("exec plugin: Sent SIGTERM to %hu", (unsigned short int) pl->pid);
298     }
299
300     sfree (pl->user);
301     sfree (pl);
302
303     pl = next;
304   } /* while (pl) */
305   pl_head = NULL;
306
307   return (0);
308 } /* int exec_shutdown */
309
310 void module_register (void)
311 {
312   plugin_register_config ("exec", exec_config, config_keys, config_keys_num);
313   plugin_register_read ("exec", exec_read);
314   plugin_register_shutdown ("exec", exec_shutdown);
315 } /* void module_register */
316
317 /*
318  * vim:shiftwidth=2:softtabstop=2:tabstop=8
319  */