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 **/
22 #include "collectd.h"
23 #include "common.h"
24 #include "plugin.h"
26 #include <sys/types.h>
27 #include <pwd.h>
28 #include <signal.h>
30 #include <pthread.h>
32 /*
33 * Private data types
34 */
35 struct program_list_s;
36 typedef struct program_list_s program_list_t;
37 struct program_list_s
38 {
39 char *user;
40 char *exec;
41 int pid;
42 program_list_t *next;
43 };
45 /*
46 * Private variables
47 */
48 static const char *config_keys[] =
49 {
50 "Exec"
51 };
52 static int config_keys_num = STATIC_ARRAY_SIZE (config_keys);
54 static program_list_t *pl_head = NULL;
56 /*
57 * Functions
58 */
59 static int exec_config (const char *key, const char *value)
60 {
61 if (strcasecmp ("Exec", key) == 0)
62 {
63 program_list_t *pl;
64 pl = (program_list_t *) malloc (sizeof (program_list_t));
65 if (pl == NULL)
66 return (1);
67 memset (pl, '\0', sizeof (program_list_t));
69 pl->user = strdup (value);
70 if (pl->user == NULL)
71 {
72 sfree (pl);
73 return (1);
74 }
76 pl->exec = strchr (pl->user, ' ');
77 if (pl->exec == NULL)
78 {
79 sfree (pl->user);
80 sfree (pl);
81 return (1);
82 }
83 while (*pl->exec == ' ')
84 {
85 *pl->exec = '\0';
86 pl->exec++;
87 }
89 if (*pl->exec == '\0')
90 {
91 sfree (pl->user);
92 sfree (pl);
93 return (1);
94 }
96 pl->next = pl_head;
97 pl_head = pl;
98 }
99 else
100 {
101 return (-1);
102 }
104 return (0);
105 } /* int exec_config */
107 static void submit_counter (const char *type_instance, counter_t value)
108 {
109 value_t values[1];
110 value_list_t vl = VALUE_LIST_INIT;
112 DEBUG ("type_instance = %s; value = %llu;", type_instance, value);
114 values[0].counter = value;
116 vl.values = values;
117 vl.values_len = 1;
118 vl.time = time (NULL);
119 strcpy (vl.host, hostname_g);
120 strcpy (vl.plugin, "exec");
121 strcpy (vl.plugin_instance, "");
122 strncpy (vl.type_instance, type_instance, sizeof (vl.type_instance));
124 plugin_dispatch_values ("counter", &vl);
125 } /* void submit_counter */
127 static void submit_gauge (const char *type_instance, gauge_t value)
128 {
129 value_t values[1];
130 value_list_t vl = VALUE_LIST_INIT;
132 DEBUG ("type_instance = %s; value = %lf;", type_instance, value);
134 values[0].gauge = value;
136 vl.values = values;
137 vl.values_len = 1;
138 vl.time = time (NULL);
139 strcpy (vl.host, hostname_g);
140 strcpy (vl.plugin, "exec");
141 strcpy (vl.plugin_instance, "");
142 strncpy (vl.type_instance, type_instance, sizeof (vl.type_instance));
144 plugin_dispatch_values ("gauge", &vl);
145 } /* void submit_counter */
147 static void exec_child (program_list_t *pl)
148 {
149 int status;
150 int uid;
151 char *arg0;
153 struct passwd *sp_ptr;
154 struct passwd sp;
155 char pwnambuf[2048];
156 char errbuf[1024];
158 sp_ptr = NULL;
159 status = getpwnam_r (pl->user, &sp, pwnambuf, sizeof (pwnambuf), &sp_ptr);
160 if (status != 0)
161 {
162 ERROR ("exec plugin: getpwnam_r failed: %s",
163 sstrerror (errno, errbuf, sizeof (errbuf)));
164 exit (-1);
165 }
166 if (sp_ptr == NULL)
167 {
168 ERROR ("exec plugin: No such user: `%s'", pl->user);
169 exit (-1);
170 }
172 uid = sp.pw_uid;
173 if (uid == 0)
174 {
175 ERROR ("exec plugin: Cowardly refusing to exec program as root.");
176 exit (-1);
177 }
179 status = setuid (uid);
180 if (status != 0)
181 {
182 ERROR ("exec plugin: setuid failed: %s",
183 sstrerror (errno, errbuf, sizeof (errbuf)));
184 exit (-1);
185 }
187 arg0 = strrchr (pl->exec, '/');
188 if (arg0 != NULL)
189 arg0++;
190 if ((arg0 == NULL) || (*arg0 == '\0'))
191 arg0 = pl->exec;
193 status = execlp (pl->exec, arg0, (char *) 0);
195 ERROR ("exec plugin: exec failed: %s",
196 sstrerror (errno, errbuf, sizeof (errbuf)));
197 exit (-1);
198 } /* void exec_child */
200 static int fork_child (program_list_t *pl)
201 {
202 int fd_pipe[2];
203 int status;
205 if (pl->pid != 0)
206 return (-1);
208 status = pipe (fd_pipe);
209 if (status != 0)
210 {
211 char errbuf[1024];
212 ERROR ("exec plugin: pipe failed: %s",
213 sstrerror (errno, errbuf, sizeof (errbuf)));
214 return (-1);
215 }
217 pl->pid = fork ();
218 if (pl->pid < 0)
219 {
220 char errbuf[1024];
221 ERROR ("exec plugin: fork failed: %s",
222 sstrerror (errno, errbuf, sizeof (errbuf)));
223 return (-1);
224 }
225 else if (pl->pid == 0)
226 {
227 close (fd_pipe[0]);
229 /* Connect the pipe to STDOUT and STDERR */
230 if (fd_pipe[1] != STDOUT_FILENO)
231 dup2 (fd_pipe[1], STDOUT_FILENO);
232 if (fd_pipe[1] != STDERR_FILENO)
233 dup2 (fd_pipe[1], STDERR_FILENO);
234 if ((fd_pipe[1] != STDOUT_FILENO) && (fd_pipe[1] != STDERR_FILENO))
235 close (fd_pipe[1]);
237 exec_child (pl);
238 /* does not return */
239 }
241 close (fd_pipe[1]);
242 return (fd_pipe[0]);
243 } /* int fork_child */
245 static void *exec_read_one (void *arg)
246 {
247 program_list_t *pl = (program_list_t *) arg;
248 int fd;
249 FILE *fh;
250 char buffer[1024];
252 fd = fork_child (pl);
253 if (fd < 0)
254 pthread_exit ((void *) 1);
256 assert (pl->pid != 0);
258 fh = fdopen (fd, "r");
259 if (fh == NULL)
260 {
261 char errbuf[1024];
262 ERROR ("exec plugin: fdopen (%i) failed: %s", fd,
263 sstrerror (errno, errbuf, sizeof (errbuf)));
264 kill (pl->pid, SIGTERM);
265 close (fd);
266 pthread_exit ((void *) 1);
267 }
269 while (fgets (buffer, sizeof (buffer), fh) != NULL)
270 {
271 int len;
272 char *type;
273 char *type_instance;
274 char *value;
276 DEBUG ("buffer = %s", buffer);
278 len = strlen (buffer);
279 if (len < 5)
280 continue;
282 if (buffer[0] == '#')
283 continue;
285 type = buffer;
287 type_instance = strchr (type, ',');
288 if (type_instance == NULL)
289 continue;
290 *type_instance = '\0';
291 type_instance++;
293 if ((strcasecmp ("counter", type) != 0)
294 && (strcasecmp ("gauge", type) != 0))
295 {
296 WARNING ("exec plugin: Received invalid type: %s", type);
297 continue;
298 }
300 value = strchr (type_instance, ',');
301 if (value == NULL)
302 continue;
303 *value = '\0';
304 value++;
306 DEBUG ("value = %s", value);
308 if (strcasecmp ("counter", type) == 0)
309 submit_counter (type_instance, atoll (value));
310 else
311 submit_gauge (type_instance, atof (value));
312 } /* while (fgets) */
314 fclose (fh);
315 pl->pid = 0;
317 pthread_exit ((void *) 0);
318 return (NULL);
319 } /* void *exec_read_one */
321 static int exec_read (void)
322 {
323 program_list_t *pl;
325 for (pl = pl_head; pl != NULL; pl = pl->next)
326 {
327 pthread_t t;
328 pthread_attr_t attr;
330 if (pl->pid != 0)
331 continue;
333 pthread_attr_init (&attr);
334 pthread_attr_setdetachstate (&attr, PTHREAD_CREATE_DETACHED);
335 pthread_create (&t, &attr, exec_read_one, (void *) pl);
336 } /* for (pl) */
338 return (0);
339 } /* int exec_read */
341 static int exec_shutdown (void)
342 {
343 program_list_t *pl;
344 program_list_t *next;
346 pl = pl_head;
347 while (pl != NULL)
348 {
349 next = pl->next;
351 if (pl->pid > 0)
352 {
353 kill (pl->pid, SIGTERM);
354 INFO ("exec plugin: Sent SIGTERM to %hu", (unsigned short int) pl->pid);
355 }
357 sfree (pl->user);
358 sfree (pl);
360 pl = next;
361 } /* while (pl) */
362 pl_head = NULL;
364 return (0);
365 } /* int exec_shutdown */
367 void module_register (void)
368 {
369 plugin_register_config ("exec", exec_config, config_keys, config_keys_num);
370 plugin_register_read ("exec", exec_read);
371 plugin_register_shutdown ("exec", exec_shutdown);
372 } /* void module_register */
374 /*
375 * vim:shiftwidth=2:softtabstop=2:tabstop=8
376 */