fa0196b3470a85b01fc309fde54973945c17245e
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>
29 #include <pthread.h>
31 /*
32 * Private data types
33 */
34 struct program_list_s;
35 typedef struct program_list_s program_list_t;
36 struct program_list_s
37 {
38 char *user;
39 char *exec;
40 int pid;
41 program_list_t *next;
42 };
44 /*
45 * Private variables
46 */
47 static data_source_t dsrc_counter[1] =
48 {
49 {"value", DS_TYPE_COUNTER, NAN, NAN}
50 };
52 static data_set_t ds_counter =
53 {
54 "counter", STATIC_ARRAY_SIZE (dsrc_counter), dsrc_counter
55 };
57 static data_source_t dsrc_gauge[1] =
58 {
59 {"value", DS_TYPE_GAUGE, NAN, NAN}
60 };
62 static data_set_t ds_gauge =
63 {
64 "gauge", STATIC_ARRAY_SIZE (dsrc_gauge), dsrc_gauge
65 };
67 static const char *config_keys[] =
68 {
69 "Exec"
70 };
71 static int config_keys_num = STATIC_ARRAY_SIZE (config_keys);
73 static program_list_t *pl_head = NULL;
75 /*
76 * Functions
77 */
78 static int exec_config (const char *key, const char *value)
79 {
80 if (strcasecmp ("Exec", key) == 0)
81 {
82 program_list_t *pl;
83 pl = (program_list_t *) malloc (sizeof (program_list_t));
84 if (pl == NULL)
85 return (1);
86 memset (pl, '\0', sizeof (program_list_t));
88 pl->user = strdup (value);
89 if (pl->user == NULL)
90 {
91 sfree (pl);
92 return (1);
93 }
95 pl->exec = strchr (pl->user, ' ');
96 if (pl->exec == NULL)
97 {
98 sfree (pl->user);
99 sfree (pl);
100 return (1);
101 }
102 while (*pl->exec == ' ')
103 {
104 *pl->exec = '\0';
105 pl->exec++;
106 }
108 if (*pl->exec == '\0')
109 {
110 sfree (pl->user);
111 sfree (pl);
112 return (1);
113 }
115 pl->next = pl_head;
116 pl_head = pl;
117 }
118 else
119 {
120 return (-1);
121 }
123 return (0);
124 } /* int exec_config */
126 static void submit_counter (const char *type_instance, counter_t value)
127 {
128 value_t values[1];
129 value_list_t vl = VALUE_LIST_INIT;
131 DEBUG ("type_instance = %s; value = %llu;", type_instance, value);
133 values[0].counter = value;
135 vl.values = values;
136 vl.values_len = 1;
137 vl.time = time (NULL);
138 strcpy (vl.host, hostname_g);
139 strcpy (vl.plugin, "exec");
140 strcpy (vl.plugin_instance, "");
141 strncpy (vl.type_instance, type_instance, sizeof (vl.type_instance));
143 plugin_dispatch_values ("counter", &vl);
144 } /* void submit_counter */
146 static void submit_gauge (const char *type_instance, gauge_t value)
147 {
148 value_t values[1];
149 value_list_t vl = VALUE_LIST_INIT;
151 DEBUG ("type_instance = %s; value = %lf;", type_instance, value);
153 values[0].gauge = value;
155 vl.values = values;
156 vl.values_len = 1;
157 vl.time = time (NULL);
158 strcpy (vl.host, hostname_g);
159 strcpy (vl.plugin, "exec");
160 strcpy (vl.plugin_instance, "");
161 strncpy (vl.type_instance, type_instance, sizeof (vl.type_instance));
163 plugin_dispatch_values ("gauge", &vl);
164 } /* void submit_counter */
166 static void exec_child (program_list_t *pl)
167 {
168 int status;
169 int uid;
170 char *arg0;
172 struct passwd *sp_ptr;
173 struct passwd sp;
174 char pwnambuf[2048];
176 sp_ptr = NULL;
177 status = getpwnam_r (pl->user, &sp, pwnambuf, sizeof (pwnambuf), &sp_ptr);
178 if (status != 0)
179 {
180 ERROR ("exec plugin: getpwnam_r failed: %s", strerror (status));
181 exit (-1);
182 }
183 if (sp_ptr == NULL)
184 {
185 ERROR ("exec plugin: No such user: `%s'", pl->user);
186 exit (-1);
187 }
189 uid = sp.pw_uid;
190 if (uid == 0)
191 {
192 ERROR ("exec plugin: Cowardly refusing to exec program as root.");
193 exit (-1);
194 }
196 status = setuid (uid);
197 if (status != 0)
198 {
199 ERROR ("exec plugin: setuid failed: %s", strerror (errno));
200 exit (-1);
201 }
203 arg0 = strrchr (pl->exec, '/');
204 if (arg0 != NULL)
205 arg0++;
206 if ((arg0 == NULL) || (*arg0 == '\0'))
207 arg0 = pl->exec;
209 status = execlp (pl->exec, arg0, (char *) 0);
211 ERROR ("exec plugin: exec failed: %s", strerror (errno));
212 exit (-1);
213 } /* void exec_child */
215 static int fork_child (program_list_t *pl)
216 {
217 int fd_pipe[2];
218 int status;
220 if (pl->pid != 0)
221 return (-1);
223 status = pipe (fd_pipe);
224 if (status != 0)
225 {
226 ERROR ("exec plugin: pipe failed: %s", strerror (errno));
227 return (-1);
228 }
230 pl->pid = fork ();
231 if (pl->pid < 0)
232 {
233 ERROR ("exec plugin: fork failed: %s", strerror (errno));
234 return (-1);
235 }
236 else if (pl->pid == 0)
237 {
238 close (fd_pipe[0]);
240 /* Connect the pipe to STDOUT and STDERR */
241 if (fd_pipe[1] != STDOUT_FILENO)
242 dup2 (fd_pipe[1], STDOUT_FILENO);
243 if (fd_pipe[1] != STDERR_FILENO)
244 dup2 (fd_pipe[1], STDERR_FILENO);
245 if ((fd_pipe[1] != STDOUT_FILENO) && (fd_pipe[1] != STDERR_FILENO))
246 close (fd_pipe[1]);
248 exec_child (pl);
249 /* does not return */
250 }
252 close (fd_pipe[1]);
253 return (fd_pipe[0]);
254 } /* int fork_child */
256 static void *exec_read_one (void *arg)
257 {
258 program_list_t *pl = (program_list_t *) arg;
259 int fd;
260 FILE *fh;
261 char buffer[1024];
263 fd = fork_child (pl);
264 if (fd < 0)
265 pthread_exit ((void *) 1);
267 assert (pl->pid != 0);
269 fh = fdopen (fd, "r");
270 if (fh == NULL)
271 {
272 ERROR ("exec plugin: fdopen (%i) failed: %s", fd,
273 strerror (errno));
274 kill (pl->pid, SIGTERM);
275 close (fd);
276 pthread_exit ((void *) 1);
277 }
279 while (fgets (buffer, sizeof (buffer), fh) != NULL)
280 {
281 int len;
282 char *type;
283 char *type_instance;
284 char *value;
286 DEBUG ("buffer = %s", buffer);
288 len = strlen (buffer);
289 if (len < 5)
290 continue;
292 if (buffer[0] == '#')
293 continue;
295 type = buffer;
297 type_instance = strchr (type, ',');
298 if (type_instance == NULL)
299 continue;
300 *type_instance = '\0';
301 type_instance++;
303 if ((strcasecmp ("counter", type) != 0)
304 && (strcasecmp ("gauge", type) != 0))
305 {
306 WARNING ("exec plugin: Received invalid type: %s", type);
307 continue;
308 }
310 value = strchr (type_instance, ',');
311 if (value == NULL)
312 continue;
313 *value = '\0';
314 value++;
316 DEBUG ("value = %s", value);
318 if (strcasecmp ("counter", type) == 0)
319 submit_counter (type_instance, atoll (value));
320 else
321 submit_gauge (type_instance, atof (value));
322 } /* while (fgets) */
324 fclose (fh);
325 pl->pid = 0;
327 pthread_exit ((void *) 0);
328 } /* void *exec_read_one */
330 static int exec_read (void)
331 {
332 program_list_t *pl;
334 for (pl = pl_head; pl != NULL; pl = pl->next)
335 {
336 pthread_t t;
337 pthread_attr_t attr;
339 if (pl->pid != 0)
340 continue;
342 pthread_attr_init (&attr);
343 pthread_attr_setdetachstate (&attr, PTHREAD_CREATE_DETACHED);
344 pthread_create (&t, &attr, exec_read_one, (void *) pl);
345 } /* for (pl) */
347 return (0);
348 } /* int exec_read */
350 void module_register (void)
351 {
352 plugin_register_data_set (&ds_counter);
353 plugin_register_data_set (&ds_gauge);
354 plugin_register_config ("exec", exec_config, config_keys, config_keys_num);
355 plugin_register_read ("exec", exec_read);
356 } /* void module_register */
358 /*
359 * vim:shiftwidth=2:softtabstop=2:tabstop=8
360 */