1 | /* |
---|
2 | * dhcpcd - DHCP client daemon |
---|
3 | * Copyright (c) 2006-2014 Roy Marples <roy@marples.name> |
---|
4 | * All rights reserved |
---|
5 | |
---|
6 | * Redistribution and use in source and binary forms, with or without |
---|
7 | * modification, are permitted provided that the following conditions |
---|
8 | * are met: |
---|
9 | * 1. Redistributions of source code must retain the above copyright |
---|
10 | * notice, this list of conditions and the following disclaimer. |
---|
11 | * 2. Redistributions in binary form must reproduce the above copyright |
---|
12 | * notice, this list of conditions and the following disclaimer in the |
---|
13 | * documentation and/or other materials provided with the distribution. |
---|
14 | * |
---|
15 | * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND |
---|
16 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
---|
17 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE |
---|
18 | * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE |
---|
19 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL |
---|
20 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS |
---|
21 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) |
---|
22 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT |
---|
23 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY |
---|
24 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF |
---|
25 | * SUCH DAMAGE. |
---|
26 | */ |
---|
27 | |
---|
28 | #include <sys/stat.h> |
---|
29 | #include <sys/uio.h> |
---|
30 | #include <sys/wait.h> |
---|
31 | |
---|
32 | #include <netinet/in.h> |
---|
33 | #include <arpa/inet.h> |
---|
34 | |
---|
35 | #include <ctype.h> |
---|
36 | #include <errno.h> |
---|
37 | #include <signal.h> |
---|
38 | /* We can't include spawn.h here because it may not exist. |
---|
39 | * config.h will pull it in, or our compat one. */ |
---|
40 | #include <stdlib.h> |
---|
41 | #include <string.h> |
---|
42 | #include <syslog.h> |
---|
43 | #include <unistd.h> |
---|
44 | |
---|
45 | #include "config.h" |
---|
46 | #include "common.h" |
---|
47 | #include "dhcp.h" |
---|
48 | #include "dhcp6.h" |
---|
49 | #include "if-options.h" |
---|
50 | #include "if-pref.h" |
---|
51 | #include "ipv6nd.h" |
---|
52 | #include "net.h" |
---|
53 | #include "script.h" |
---|
54 | |
---|
55 | #define DEFAULT_PATH "PATH=/usr/bin:/usr/sbin:/bin:/sbin" |
---|
56 | |
---|
57 | static const char * const if_params[] = { |
---|
58 | "interface", |
---|
59 | "reason", |
---|
60 | "pid", |
---|
61 | "ifmetric", |
---|
62 | "ifwireless", |
---|
63 | "ifflags", |
---|
64 | "ssid", |
---|
65 | "profile", |
---|
66 | "interface_order", |
---|
67 | NULL |
---|
68 | }; |
---|
69 | |
---|
70 | void |
---|
71 | if_printoptions(void) |
---|
72 | { |
---|
73 | const char * const *p; |
---|
74 | |
---|
75 | for (p = if_params; *p; p++) |
---|
76 | printf(" - %s\n", *p); |
---|
77 | } |
---|
78 | |
---|
79 | static int |
---|
80 | exec_script(char *const *argv, char *const *env) |
---|
81 | { |
---|
82 | pid_t pid; |
---|
83 | posix_spawnattr_t attr; |
---|
84 | short flags; |
---|
85 | sigset_t defsigs; |
---|
86 | int i; |
---|
87 | |
---|
88 | /* posix_spawn is a safe way of executing another image |
---|
89 | * and changing signals back to how they should be. */ |
---|
90 | if (posix_spawnattr_init(&attr) == -1) |
---|
91 | return -1; |
---|
92 | flags = POSIX_SPAWN_SETSIGMASK | POSIX_SPAWN_SETSIGDEF; |
---|
93 | posix_spawnattr_setflags(&attr, flags); |
---|
94 | sigemptyset(&defsigs); |
---|
95 | for (i = 0; i < handle_sigs[i]; i++) |
---|
96 | sigaddset(&defsigs, handle_sigs[i]); |
---|
97 | posix_spawnattr_setsigdefault(&attr, &defsigs); |
---|
98 | posix_spawnattr_setsigmask(&attr, &dhcpcd_sigset); |
---|
99 | errno = 0; |
---|
100 | i = posix_spawn(&pid, argv[0], NULL, &attr, argv, env); |
---|
101 | if (i) { |
---|
102 | errno = i; |
---|
103 | return -1; |
---|
104 | } |
---|
105 | return pid; |
---|
106 | } |
---|
107 | |
---|
108 | #ifdef INET |
---|
109 | static char * |
---|
110 | make_var(const char *prefix, const char *var) |
---|
111 | { |
---|
112 | size_t len; |
---|
113 | char *v; |
---|
114 | |
---|
115 | len = strlen(prefix) + strlen(var) + 2; |
---|
116 | v = malloc(len); |
---|
117 | if (v == NULL) { |
---|
118 | syslog(LOG_ERR, "%s: %m", __func__); |
---|
119 | return NULL; |
---|
120 | } |
---|
121 | snprintf(v, len, "%s_%s", prefix, var); |
---|
122 | return v; |
---|
123 | } |
---|
124 | |
---|
125 | |
---|
126 | static int |
---|
127 | append_config(char ***env, ssize_t *len, |
---|
128 | const char *prefix, const char *const *config) |
---|
129 | { |
---|
130 | ssize_t i, j, e1; |
---|
131 | char **ne, *eq, **nep, *p; |
---|
132 | |
---|
133 | if (config == NULL) |
---|
134 | return 0; |
---|
135 | |
---|
136 | ne = *env; |
---|
137 | for (i = 0; config[i] != NULL; i++) { |
---|
138 | eq = strchr(config[i], '='); |
---|
139 | e1 = eq - config[i] + 1; |
---|
140 | for (j = 0; j < *len; j++) { |
---|
141 | if (strncmp(ne[j] + strlen(prefix) + 1, |
---|
142 | config[i], e1) == 0) |
---|
143 | { |
---|
144 | free(ne[j]); |
---|
145 | ne[j] = make_var(prefix, config[i]); |
---|
146 | if (ne[j] == NULL) |
---|
147 | return -1; |
---|
148 | break; |
---|
149 | } |
---|
150 | } |
---|
151 | if (j == *len) { |
---|
152 | j++; |
---|
153 | p = make_var(prefix, config[i]); |
---|
154 | if (p == NULL) |
---|
155 | return -1; |
---|
156 | nep = realloc(ne, sizeof(char *) * (j + 1)); |
---|
157 | if (nep == NULL) { |
---|
158 | syslog(LOG_ERR, "%s: %m", __func__); |
---|
159 | return -1; |
---|
160 | } |
---|
161 | ne = nep; |
---|
162 | ne[j - 1] = p; |
---|
163 | *len = j; |
---|
164 | } |
---|
165 | } |
---|
166 | *env = ne; |
---|
167 | return 0; |
---|
168 | } |
---|
169 | #endif |
---|
170 | |
---|
171 | static size_t |
---|
172 | arraytostr(const char *const *argv, char **s) |
---|
173 | { |
---|
174 | const char *const *ap; |
---|
175 | char *p; |
---|
176 | size_t len, l; |
---|
177 | |
---|
178 | len = 0; |
---|
179 | ap = argv; |
---|
180 | while (*ap) |
---|
181 | len += strlen(*ap++) + 1; |
---|
182 | *s = p = malloc(len); |
---|
183 | if (p == NULL) |
---|
184 | return -1; |
---|
185 | ap = argv; |
---|
186 | while (*ap) { |
---|
187 | l = strlen(*ap) + 1; |
---|
188 | memcpy(p, *ap, l); |
---|
189 | p += l; |
---|
190 | ap++; |
---|
191 | } |
---|
192 | return len; |
---|
193 | } |
---|
194 | |
---|
195 | static ssize_t |
---|
196 | make_env(const struct interface *ifp, const char *reason, char ***argv) |
---|
197 | { |
---|
198 | char **env, **nenv, *p; |
---|
199 | ssize_t e, elen, l; |
---|
200 | const struct if_options *ifo = ifp->options; |
---|
201 | const struct interface *ifp2; |
---|
202 | int dhcp, dhcp6, ra; |
---|
203 | const struct dhcp_state *state; |
---|
204 | #ifdef INET6 |
---|
205 | const struct dhcp6_state *d6_state; |
---|
206 | #endif |
---|
207 | |
---|
208 | dhcp = dhcp6 = ra = 0; |
---|
209 | state = D_STATE(ifp); |
---|
210 | #ifdef INET6 |
---|
211 | d6_state = D6_CSTATE(ifp); |
---|
212 | #endif |
---|
213 | if (strcmp(reason, "TEST") == 0) { |
---|
214 | #ifdef INET6 |
---|
215 | if (d6_state && d6_state->new) |
---|
216 | dhcp6 = 1; |
---|
217 | else if (ipv6nd_has_ra(ifp)) |
---|
218 | ra = 1; |
---|
219 | else |
---|
220 | #endif |
---|
221 | dhcp = 1; |
---|
222 | } else if (reason[strlen(reason) - 1] == '6') |
---|
223 | dhcp6 = 1; |
---|
224 | else if (strcmp(reason, "ROUTERADVERT") == 0) |
---|
225 | ra = 1; |
---|
226 | else |
---|
227 | dhcp = 1; |
---|
228 | |
---|
229 | /* When dumping the lease, we only want to report interface and |
---|
230 | reason - the other interface variables are meaningless */ |
---|
231 | if (options & DHCPCD_DUMPLEASE) |
---|
232 | elen = 2; |
---|
233 | else |
---|
234 | elen = 10; |
---|
235 | |
---|
236 | #define EMALLOC(i, l) if ((env[(i)] = malloc(l)) == NULL) goto eexit; |
---|
237 | /* Make our env */ |
---|
238 | env = calloc(1, sizeof(char *) * (elen + 1)); |
---|
239 | if (env == NULL) |
---|
240 | goto eexit; |
---|
241 | e = strlen("interface") + strlen(ifp->name) + 2; |
---|
242 | EMALLOC(0, e); |
---|
243 | snprintf(env[0], e, "interface=%s", ifp->name); |
---|
244 | e = strlen("reason") + strlen(reason) + 2; |
---|
245 | EMALLOC(1, e); |
---|
246 | snprintf(env[1], e, "reason=%s", reason); |
---|
247 | if (options & DHCPCD_DUMPLEASE) |
---|
248 | goto dumplease; |
---|
249 | e = 20; |
---|
250 | EMALLOC(2, e); |
---|
251 | snprintf(env[2], e, "pid=%d", getpid()); |
---|
252 | EMALLOC(3, e); |
---|
253 | snprintf(env[3], e, "ifmetric=%d", ifp->metric); |
---|
254 | EMALLOC(4, e); |
---|
255 | snprintf(env[4], e, "ifwireless=%d", ifp->wireless); |
---|
256 | EMALLOC(5, e); |
---|
257 | snprintf(env[5], e, "ifflags=%u", ifp->flags); |
---|
258 | EMALLOC(6, e); |
---|
259 | snprintf(env[6], e, "ifmtu=%d", get_mtu(ifp->name)); |
---|
260 | l = e = strlen("interface_order="); |
---|
261 | TAILQ_FOREACH(ifp2, ifaces, next) { |
---|
262 | e += strlen(ifp2->name) + 1; |
---|
263 | } |
---|
264 | EMALLOC(7, e); |
---|
265 | p = env[7]; |
---|
266 | strlcpy(p, "interface_order=", e); |
---|
267 | e -= l; |
---|
268 | p += l; |
---|
269 | TAILQ_FOREACH(ifp2, ifaces, next) { |
---|
270 | l = strlcpy(p, ifp2->name, e); |
---|
271 | p += l; |
---|
272 | e -= l; |
---|
273 | *p++ = ' '; |
---|
274 | e--; |
---|
275 | } |
---|
276 | *--p = '\0'; |
---|
277 | if (strcmp(reason, "TEST") == 0) { |
---|
278 | env[8] = strdup("if_up=false"); |
---|
279 | env[9] = strdup("if_down=false"); |
---|
280 | } else if ((dhcp && state && state->new) |
---|
281 | #ifdef INET6 |
---|
282 | || (dhcp6 && d6_state && d6_state->new) |
---|
283 | || (ra && ipv6nd_has_ra(ifp)) |
---|
284 | #endif |
---|
285 | ) |
---|
286 | { |
---|
287 | env[8] = strdup("if_up=true"); |
---|
288 | env[9] = strdup("if_down=false"); |
---|
289 | } else { |
---|
290 | env[8] = strdup("if_up=false"); |
---|
291 | env[9] = strdup("if_down=true"); |
---|
292 | } |
---|
293 | if (env[8] == NULL || env[9] == NULL) |
---|
294 | goto eexit; |
---|
295 | if (*ifp->profile) { |
---|
296 | e = strlen("profile=") + strlen(ifp->profile) + 2; |
---|
297 | EMALLOC(elen, e); |
---|
298 | snprintf(env[elen++], e, "profile=%s", ifp->profile); |
---|
299 | } |
---|
300 | if (ifp->wireless) { |
---|
301 | e = strlen("new_ssid=") + strlen(ifp->ssid) + 2; |
---|
302 | if (strcmp(reason, "CARRIER") == 0) { |
---|
303 | nenv = realloc(env, sizeof(char *) * (elen + 2)); |
---|
304 | if (nenv == NULL) |
---|
305 | goto eexit; |
---|
306 | env = nenv; |
---|
307 | EMALLOC(elen, e); |
---|
308 | snprintf(env[elen++], e, "new_ssid=%s", ifp->ssid); |
---|
309 | } |
---|
310 | else if (strcmp(reason, "NOCARRIER") == 0) { |
---|
311 | nenv = realloc(env, sizeof(char *) * (elen + 2)); |
---|
312 | if (nenv == NULL) |
---|
313 | goto eexit; |
---|
314 | env = nenv; |
---|
315 | EMALLOC(elen, e); |
---|
316 | snprintf(env[elen++], e, "old_ssid=%s", ifp->ssid); |
---|
317 | } |
---|
318 | } |
---|
319 | #ifdef INET |
---|
320 | if (dhcp && state && state->old) { |
---|
321 | e = dhcp_env(NULL, NULL, state->old, ifp); |
---|
322 | if (e > 0) { |
---|
323 | nenv = realloc(env, sizeof(char *) * (elen + e + 1)); |
---|
324 | if (nenv == NULL) |
---|
325 | goto eexit; |
---|
326 | env = nenv; |
---|
327 | l = dhcp_env(env + elen, "old", state->old, ifp); |
---|
328 | if (l == -1) |
---|
329 | goto eexit; |
---|
330 | elen += l; |
---|
331 | } |
---|
332 | if (append_config(&env, &elen, "old", |
---|
333 | (const char *const *)ifo->config) == -1) |
---|
334 | goto eexit; |
---|
335 | } |
---|
336 | #endif |
---|
337 | #ifdef INET6 |
---|
338 | if (dhcp6 && d6_state && d6_state->old) { |
---|
339 | e = dhcp6_env(NULL, NULL, ifp, |
---|
340 | d6_state->old, d6_state->old_len); |
---|
341 | if (e > 0) { |
---|
342 | nenv = realloc(env, sizeof(char *) * (elen + e + 1)); |
---|
343 | if (nenv == NULL) |
---|
344 | goto eexit; |
---|
345 | env = nenv; |
---|
346 | l = dhcp6_env(env + elen, "old", ifp, |
---|
347 | d6_state->old, d6_state->old_len); |
---|
348 | if (l == -1) |
---|
349 | goto eexit; |
---|
350 | elen += l; |
---|
351 | } |
---|
352 | } |
---|
353 | #endif |
---|
354 | |
---|
355 | dumplease: |
---|
356 | #ifdef INET |
---|
357 | if (dhcp && state && state->new) { |
---|
358 | e = dhcp_env(NULL, NULL, state->new, ifp); |
---|
359 | if (e > 0) { |
---|
360 | nenv = realloc(env, sizeof(char *) * (elen + e + 1)); |
---|
361 | if (nenv == NULL) |
---|
362 | goto eexit; |
---|
363 | env = nenv; |
---|
364 | l = dhcp_env(env + elen, "new", |
---|
365 | state->new, ifp); |
---|
366 | if (l == -1) |
---|
367 | goto eexit; |
---|
368 | elen += l; |
---|
369 | } |
---|
370 | if (append_config(&env, &elen, "new", |
---|
371 | (const char *const *)ifo->config) == -1) |
---|
372 | goto eexit; |
---|
373 | } |
---|
374 | #endif |
---|
375 | #ifdef INET6 |
---|
376 | if (dhcp6 && d6_state && d6_state->new) { |
---|
377 | e = dhcp6_env(NULL, NULL, ifp, |
---|
378 | d6_state->new, d6_state->new_len); |
---|
379 | if (e > 0) { |
---|
380 | nenv = realloc(env, sizeof(char *) * (elen + e + 1)); |
---|
381 | if (nenv == NULL) |
---|
382 | goto eexit; |
---|
383 | env = nenv; |
---|
384 | l = dhcp6_env(env + elen, "new", ifp, |
---|
385 | d6_state->new, d6_state->new_len); |
---|
386 | if (l == -1) |
---|
387 | goto eexit; |
---|
388 | elen += l; |
---|
389 | } |
---|
390 | } |
---|
391 | if (ra) { |
---|
392 | e = ipv6nd_env(NULL, NULL, ifp); |
---|
393 | if (e > 0) { |
---|
394 | nenv = realloc(env, sizeof(char *) * (elen + e + 1)); |
---|
395 | if (nenv == NULL) |
---|
396 | goto eexit; |
---|
397 | env = nenv; |
---|
398 | l = ipv6nd_env(env + elen, NULL, ifp); |
---|
399 | if (l == -1) |
---|
400 | goto eexit; |
---|
401 | elen += l; |
---|
402 | } |
---|
403 | } |
---|
404 | #endif |
---|
405 | |
---|
406 | /* Add our base environment */ |
---|
407 | if (ifo->environ) { |
---|
408 | e = 0; |
---|
409 | while (ifo->environ[e++]) |
---|
410 | ; |
---|
411 | nenv = realloc(env, sizeof(char *) * (elen + e + 1)); |
---|
412 | if (nenv == NULL) |
---|
413 | goto eexit; |
---|
414 | env = nenv; |
---|
415 | e = 0; |
---|
416 | while (ifo->environ[e]) { |
---|
417 | env[elen + e] = strdup(ifo->environ[e]); |
---|
418 | if (env[elen + e] == NULL) |
---|
419 | goto eexit; |
---|
420 | e++; |
---|
421 | } |
---|
422 | elen += e; |
---|
423 | } |
---|
424 | env[elen] = NULL; |
---|
425 | |
---|
426 | *argv = env; |
---|
427 | return elen; |
---|
428 | |
---|
429 | eexit: |
---|
430 | syslog(LOG_ERR, "%s: %m", __func__); |
---|
431 | nenv = env; |
---|
432 | while (*nenv) |
---|
433 | free(*nenv++); |
---|
434 | free(env); |
---|
435 | return -1; |
---|
436 | } |
---|
437 | |
---|
438 | static int |
---|
439 | send_interface1(int fd, const struct interface *iface, const char *reason) |
---|
440 | { |
---|
441 | char **env, **ep, *s; |
---|
442 | ssize_t elen; |
---|
443 | struct iovec iov[2]; |
---|
444 | int retval; |
---|
445 | |
---|
446 | if (make_env(iface, reason, &env) == -1) |
---|
447 | return -1; |
---|
448 | elen = arraytostr((const char *const *)env, &s); |
---|
449 | if (elen == -1) |
---|
450 | return -1; |
---|
451 | iov[0].iov_base = &elen; |
---|
452 | iov[0].iov_len = sizeof(ssize_t); |
---|
453 | iov[1].iov_base = s; |
---|
454 | iov[1].iov_len = elen; |
---|
455 | retval = writev(fd, iov, 2); |
---|
456 | ep = env; |
---|
457 | while (*ep) |
---|
458 | free(*ep++); |
---|
459 | free(env); |
---|
460 | free(s); |
---|
461 | return retval; |
---|
462 | } |
---|
463 | |
---|
464 | int |
---|
465 | send_interface(int fd, const struct interface *iface) |
---|
466 | { |
---|
467 | const char *reason; |
---|
468 | int retval = 0; |
---|
469 | int onestate = 0; |
---|
470 | #ifdef INET |
---|
471 | const struct dhcp_state *state = D_CSTATE(iface); |
---|
472 | |
---|
473 | if (state) { |
---|
474 | onestate = 1; |
---|
475 | if (send_interface1(fd, iface, state->reason) == -1) |
---|
476 | retval = -1; |
---|
477 | } |
---|
478 | #endif |
---|
479 | |
---|
480 | #ifdef INET6 |
---|
481 | if (ipv6nd_has_ra(iface)) { |
---|
482 | onestate = 1; |
---|
483 | if (send_interface1(fd, iface, "ROUTERADVERT") == -1) |
---|
484 | retval = -1; |
---|
485 | } |
---|
486 | if (D6_STATE_RUNNING(iface)) { |
---|
487 | onestate = 1; |
---|
488 | if (send_interface1(fd, iface, "INFORM6") == -1) |
---|
489 | retval = -1; |
---|
490 | } |
---|
491 | #endif |
---|
492 | |
---|
493 | if (!onestate) { |
---|
494 | switch (iface->carrier) { |
---|
495 | case LINK_UP: |
---|
496 | reason = "CARRIER"; |
---|
497 | break; |
---|
498 | case LINK_DOWN: |
---|
499 | reason = "NOCARRIER"; |
---|
500 | break; |
---|
501 | default: |
---|
502 | reason = "UNKNOWN"; |
---|
503 | break; |
---|
504 | } |
---|
505 | if (send_interface1(fd, iface, reason) == -1) |
---|
506 | retval = -1; |
---|
507 | } |
---|
508 | return retval; |
---|
509 | } |
---|
510 | |
---|
511 | int |
---|
512 | script_runreason(const struct interface *ifp, const char *reason) |
---|
513 | { |
---|
514 | char *const argv[2] = { UNCONST(ifp->options->script), NULL }; |
---|
515 | char **env = NULL, **ep; |
---|
516 | char *path, *bigenv; |
---|
517 | ssize_t e, elen = 0; |
---|
518 | pid_t pid; |
---|
519 | int status = 0; |
---|
520 | const struct fd_list *fd; |
---|
521 | struct iovec iov[2]; |
---|
522 | |
---|
523 | if (ifp->options->script == NULL || |
---|
524 | ifp->options->script[0] == '\0' || |
---|
525 | strcmp(ifp->options->script, "/dev/null") == 0) |
---|
526 | return 0; |
---|
527 | |
---|
528 | syslog(LOG_DEBUG, "%s: executing `%s' %s", |
---|
529 | ifp->name, argv[0], reason); |
---|
530 | |
---|
531 | /* Make our env */ |
---|
532 | elen = make_env(ifp, reason, &env); |
---|
533 | ep = realloc(env, sizeof(char *) * (elen + 2)); |
---|
534 | if (ep == NULL) { |
---|
535 | elen = -1; |
---|
536 | goto out; |
---|
537 | } |
---|
538 | env = ep; |
---|
539 | /* Add path to it */ |
---|
540 | path = getenv("PATH"); |
---|
541 | if (path) { |
---|
542 | e = strlen("PATH") + strlen(path) + 2; |
---|
543 | env[elen] = malloc(e); |
---|
544 | if (env[elen] == NULL) { |
---|
545 | elen = -1; |
---|
546 | goto out; |
---|
547 | } |
---|
548 | snprintf(env[elen], e, "PATH=%s", path); |
---|
549 | } else { |
---|
550 | env[elen] = strdup(DEFAULT_PATH); |
---|
551 | if (env[elen] == NULL) { |
---|
552 | elen = -1; |
---|
553 | goto out; |
---|
554 | } |
---|
555 | } |
---|
556 | env[++elen] = NULL; |
---|
557 | |
---|
558 | pid = exec_script(argv, env); |
---|
559 | if (pid == -1) |
---|
560 | syslog(LOG_ERR, "%s: %s: %m", __func__, argv[0]); |
---|
561 | else if (pid != 0) { |
---|
562 | /* Wait for the script to finish */ |
---|
563 | while (waitpid(pid, &status, 0) == -1) { |
---|
564 | if (errno != EINTR) { |
---|
565 | syslog(LOG_ERR, "waitpid: %m"); |
---|
566 | status = 0; |
---|
567 | break; |
---|
568 | } |
---|
569 | } |
---|
570 | if (WIFEXITED(status)) { |
---|
571 | if (WEXITSTATUS(status)) |
---|
572 | syslog(LOG_ERR, |
---|
573 | "%s: %s: WEXITSTATUS %d", |
---|
574 | __func__, argv[0], WEXITSTATUS(status)); |
---|
575 | } else if (WIFSIGNALED(status)) |
---|
576 | syslog(LOG_ERR, "%s: %s: %s", |
---|
577 | __func__, argv[0], strsignal(WTERMSIG(status))); |
---|
578 | } |
---|
579 | |
---|
580 | /* Send to our listeners */ |
---|
581 | bigenv = NULL; |
---|
582 | for (fd = control_fds; fd != NULL; fd = fd->next) { |
---|
583 | if (fd->listener) { |
---|
584 | if (bigenv == NULL) { |
---|
585 | elen = arraytostr((const char *const *)env, |
---|
586 | &bigenv); |
---|
587 | iov[0].iov_base = &elen; |
---|
588 | iov[0].iov_len = sizeof(ssize_t); |
---|
589 | iov[1].iov_base = bigenv; |
---|
590 | iov[1].iov_len = elen; |
---|
591 | } |
---|
592 | if (writev(fd->fd, iov, 2) == -1) |
---|
593 | syslog(LOG_ERR, "%s: writev: %m", __func__); |
---|
594 | } |
---|
595 | } |
---|
596 | free(bigenv); |
---|
597 | |
---|
598 | out: |
---|
599 | /* Cleanup */ |
---|
600 | ep = env; |
---|
601 | while (*ep) |
---|
602 | free(*ep++); |
---|
603 | free(env); |
---|
604 | if (elen == -1) |
---|
605 | return -1; |
---|
606 | return WEXITSTATUS(status); |
---|
607 | } |
---|