xref: /minix/external/bsd/dhcpcd/dist/script.c (revision bb9622b5)
1 #include <sys/cdefs.h>
2  __RCSID("$NetBSD: script.c,v 1.23 2015/09/04 12:25:01 roy Exp $");
3 
4 /*
5  * dhcpcd - DHCP client daemon
6  * Copyright (c) 2006-2015 Roy Marples <roy@marples.name>
7  * All rights reserved
8 
9  * Redistribution and use in source and binary forms, with or without
10  * modification, are permitted provided that the following conditions
11  * are met:
12  * 1. Redistributions of source code must retain the above copyright
13  *    notice, this list of conditions and the following disclaimer.
14  * 2. Redistributions in binary form must reproduce the above copyright
15  *    notice, this list of conditions and the following disclaimer in the
16  *    documentation and/or other materials provided with the distribution.
17  *
18  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
19  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
22  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
23  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
24  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
25  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
26  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
27  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
28  * SUCH DAMAGE.
29  */
30 
31 #include <sys/stat.h>
32 #include <sys/uio.h>
33 #include <sys/wait.h>
34 
35 #include <netinet/in.h>
36 #include <arpa/inet.h>
37 
38 #include <ctype.h>
39 #include <errno.h>
40 #include <signal.h>
41 /* We can't include spawn.h here because it may not exist.
42  * config.h will pull it in, or our compat one. */
43 #include <stdlib.h>
44 #include <string.h>
45 #include <unistd.h>
46 
47 #include "config.h"
48 #include "common.h"
49 #include "dhcp.h"
50 #include "dhcp6.h"
51 #include "if.h"
52 #include "if-options.h"
53 #include "ipv4ll.h"
54 #include "ipv6nd.h"
55 #include "script.h"
56 
57 #ifdef HAVE_SPAWN_H
58 #include <spawn.h>
59 #else
60 #include "compat/posix_spawn.h"
61 #endif
62 
63 /* Allow the OS to define another script env var name */
64 #ifndef RC_SVCNAME
65 #define RC_SVCNAME "RC_SVCNAME"
66 #endif
67 
68 #define DEFAULT_PATH	"PATH=/usr/bin:/usr/sbin:/bin:/sbin"
69 
70 static const char * const if_params[] = {
71 	"interface",
72 	"reason",
73 	"pid",
74 	"ifcarrier",
75 	"ifmetric",
76 	"ifwireless",
77 	"ifflags",
78 	"ssid",
79 	"profile",
80 	"interface_order",
81 	NULL
82 };
83 
84 void
85 if_printoptions(void)
86 {
87 	const char * const *p;
88 
89 	for (p = if_params; *p; p++)
90 		printf(" -  %s\n", *p);
91 }
92 
93 static int
94 exec_script(const struct dhcpcd_ctx *ctx, char *const *argv, char *const *env)
95 {
96 	pid_t pid;
97 	posix_spawnattr_t attr;
98 	int r;
99 #ifdef USE_SIGNALS
100 	size_t i;
101 	short flags;
102 	sigset_t defsigs;
103 #else
104 	UNUSED(ctx);
105 #endif
106 
107 	/* posix_spawn is a safe way of executing another image
108 	 * and changing signals back to how they should be. */
109 	if (posix_spawnattr_init(&attr) == -1)
110 		return -1;
111 #ifdef USE_SIGNALS
112 	flags = POSIX_SPAWN_SETSIGMASK | POSIX_SPAWN_SETSIGDEF;
113 	posix_spawnattr_setflags(&attr, flags);
114 	sigemptyset(&defsigs);
115 	for (i = 0; i < dhcpcd_signals_len; i++)
116 		sigaddset(&defsigs, dhcpcd_signals[i]);
117 	posix_spawnattr_setsigdefault(&attr, &defsigs);
118 	posix_spawnattr_setsigmask(&attr, &ctx->sigset);
119 #endif
120 	errno = 0;
121 	r = posix_spawn(&pid, argv[0], NULL, &attr, argv, env);
122 	if (r) {
123 		errno = r;
124 		return -1;
125 	}
126 	return pid;
127 }
128 
129 #ifdef INET
130 static char *
131 make_var(struct dhcpcd_ctx *ctx, const char *prefix, const char *var)
132 {
133 	size_t len;
134 	char *v;
135 
136 	len = strlen(prefix) + strlen(var) + 2;
137 	if ((v = malloc(len)) == NULL) {
138 		logger(ctx, LOG_ERR, "%s: %m", __func__);
139 		return NULL;
140 	}
141 	snprintf(v, len, "%s_%s", prefix, var);
142 	return v;
143 }
144 
145 
146 static int
147 append_config(struct dhcpcd_ctx *ctx, char ***env, size_t *len,
148     const char *prefix, const char *const *config)
149 {
150 	size_t i, j, e1;
151 	char **ne, *eq, **nep, *p;
152 	int ret;
153 
154 	if (config == NULL)
155 		return 0;
156 
157 	ne = *env;
158 	ret = 0;
159 	for (i = 0; config[i] != NULL; i++) {
160 		eq = strchr(config[i], '=');
161 		e1 = (size_t)(eq - config[i] + 1);
162 		for (j = 0; j < *len; j++) {
163 			if (strncmp(ne[j], prefix, strlen(prefix)) == 0 &&
164 			    ne[j][strlen(prefix)] == '_' &&
165 			    strncmp(ne[j] + strlen(prefix) + 1,
166 			    config[i], e1) == 0)
167 			{
168 				p = make_var(ctx, prefix, config[i]);
169 				if (p == NULL) {
170 					ret = -1;
171 					break;
172 				}
173 				free(ne[j]);
174 				ne[j] = p;
175 				break;
176 			}
177 		}
178 		if (j == *len) {
179 			j++;
180 			p = make_var(ctx, prefix, config[i]);
181 			if (p == NULL) {
182 				ret = -1;
183 				break;
184 			}
185 			nep = realloc(ne, sizeof(char *) * (j + 1));
186 			if (nep == NULL) {
187 				logger(ctx, LOG_ERR, "%s: %m", __func__);
188 				free(p);
189 				ret = -1;
190 				break;
191 			}
192 			ne = nep;
193 			ne[j - 1] = p;
194 			*len = j;
195 		}
196 	}
197 	*env = ne;
198 	return ret;
199 }
200 #endif
201 
202 static ssize_t
203 arraytostr(const char *const *argv, char **s)
204 {
205 	const char *const *ap;
206 	char *p;
207 	size_t len, l;
208 
209 	if (*argv == NULL)
210 		return 0;
211 	len = 0;
212 	ap = argv;
213 	while (*ap)
214 		len += strlen(*ap++) + 1;
215 	*s = p = malloc(len);
216 	if (p == NULL)
217 		return -1;
218 	ap = argv;
219 	while (*ap) {
220 		l = strlen(*ap) + 1;
221 		memcpy(p, *ap, l);
222 		p += l;
223 		ap++;
224 	}
225 	return (ssize_t)len;
226 }
227 
228 static ssize_t
229 make_env(const struct interface *ifp, const char *reason, char ***argv)
230 {
231 	char **env, **nenv, *p;
232 	size_t e, elen, l;
233 #if defined(INET) || defined(INET6)
234 	ssize_t n;
235 #endif
236 	const struct if_options *ifo = ifp->options;
237 	const struct interface *ifp2;
238 	int af;
239 #ifdef INET
240 	int dhcp, ipv4ll;
241 	const struct dhcp_state *state;
242 	const struct ipv4ll_state *istate;
243 #endif
244 #ifdef INET6
245 	const struct dhcp6_state *d6_state;
246 	int dhcp6, ra;
247 #endif
248 
249 #ifdef INET
250 	dhcp = ipv4ll = 0;
251 	state = D_STATE(ifp);
252 	istate = IPV4LL_CSTATE(ifp);
253 #endif
254 #ifdef INET6
255 	dhcp6 = ra = 0;
256 	d6_state = D6_CSTATE(ifp);
257 #endif
258 	if (strcmp(reason, "TEST") == 0) {
259 		if (1 == 2) {}
260 #ifdef INET6
261 		else if (d6_state && d6_state->new)
262 			dhcp6 = 1;
263 		else if (ipv6nd_hasra(ifp))
264 			ra = 1;
265 #endif
266 #ifdef INET
267 		else if (state->added)
268 			dhcp = 1;
269 		else
270 			ipv4ll = 1;
271 #endif
272 	}
273 #ifdef INET6
274 	else if (reason[strlen(reason) - 1] == '6')
275 		dhcp6 = 1;
276 	else if (strcmp(reason, "ROUTERADVERT") == 0)
277 		ra = 1;
278 #endif
279 	else if (strcmp(reason, "PREINIT") == 0 ||
280 	    strcmp(reason, "CARRIER") == 0 ||
281 	    strcmp(reason, "NOCARRIER") == 0 ||
282 	    strcmp(reason, "UNKNOWN") == 0 ||
283 	    strcmp(reason, "DEPARTED") == 0 ||
284 	    strcmp(reason, "STOPPED") == 0)
285 	{
286 		/* This space left intentionally blank */
287 	}
288 #ifdef INET
289 	else if (strcmp(reason, "IPV4LL") == 0)
290 		ipv4ll = 1;
291 	else
292 		dhcp = 1;
293 #endif
294 
295 	/* When dumping the lease, we only want to report interface and
296 	   reason - the other interface variables are meaningless */
297 	if (ifp->ctx->options & DHCPCD_DUMPLEASE)
298 		elen = 2;
299 	else
300 		elen = 11;
301 
302 #define EMALLOC(i, l) if ((env[(i)] = malloc((l))) == NULL) goto eexit;
303 	/* Make our env + space for profile, wireless and debug */
304 	env = calloc(1, sizeof(char *) * (elen + 4 + 1));
305 	if (env == NULL)
306 		goto eexit;
307 	e = strlen("interface") + strlen(ifp->name) + 2;
308 	EMALLOC(0, e);
309 	snprintf(env[0], e, "interface=%s", ifp->name);
310 	e = strlen("reason") + strlen(reason) + 2;
311 	EMALLOC(1, e);
312 	snprintf(env[1], e, "reason=%s", reason);
313 	if (ifp->ctx->options & DHCPCD_DUMPLEASE)
314 		goto dumplease;
315 	e = 20;
316 	EMALLOC(2, e);
317 	snprintf(env[2], e, "pid=%d", getpid());
318 	EMALLOC(3, e);
319 	snprintf(env[3], e, "ifcarrier=%s",
320 	    ifp->carrier == LINK_UNKNOWN ? "unknown" :
321 	    ifp->carrier == LINK_UP ? "up" : "down");
322 	EMALLOC(4, e);
323 	snprintf(env[4], e, "ifmetric=%d", ifp->metric);
324 	EMALLOC(5, e);
325 	snprintf(env[5], e, "ifwireless=%d", ifp->wireless);
326 	EMALLOC(6, e);
327 	snprintf(env[6], e, "ifflags=%u", ifp->flags);
328 	EMALLOC(7, e);
329 	snprintf(env[7], e, "ifmtu=%d", if_getmtu(ifp));
330 	l = e = strlen("interface_order=");
331 	TAILQ_FOREACH(ifp2, ifp->ctx->ifaces, next) {
332 		e += strlen(ifp2->name) + 1;
333 	}
334 	EMALLOC(8, e);
335 	p = env[8];
336 	strlcpy(p, "interface_order=", e);
337 	e -= l;
338 	p += l;
339 	TAILQ_FOREACH(ifp2, ifp->ctx->ifaces, next) {
340 		l = strlcpy(p, ifp2->name, e);
341 		p += l;
342 		e -= l;
343 		*p++ = ' ';
344 		e--;
345 	}
346 	*--p = '\0';
347 	if (strcmp(reason, "STOPPED") == 0) {
348 		env[9] = strdup("if_up=false");
349 		if (ifo->options & DHCPCD_RELEASE)
350 			env[10] = strdup("if_down=true");
351 		else
352 			env[10] = strdup("if_down=false");
353 	} else if (strcmp(reason, "TEST") == 0 ||
354 	    strcmp(reason, "PREINIT") == 0 ||
355 	    strcmp(reason, "CARRIER") == 0 ||
356 	    strcmp(reason, "UNKNOWN") == 0)
357 	{
358 		env[9] = strdup("if_up=false");
359 		env[10] = strdup("if_down=false");
360 	} else if (1 == 2 /* appease ifdefs */
361 #ifdef INET
362 	    || (dhcp && state && state->new)
363 	    || (ipv4ll && IPV4LL_STATE_RUNNING(ifp))
364 #endif
365 #ifdef INET6
366 	    || (dhcp6 && d6_state && d6_state->new)
367 	    || (ra && ipv6nd_hasra(ifp))
368 #endif
369 	    )
370 	{
371 		env[9] = strdup("if_up=true");
372 		env[10] = strdup("if_down=false");
373 	} else {
374 		env[9] = strdup("if_up=false");
375 		env[10] = strdup("if_down=true");
376 	}
377 	if (env[9] == NULL || env[10] == NULL)
378 		goto eexit;
379 	if ((af = dhcpcd_ifafwaiting(ifp)) != AF_MAX) {
380 		e = 20;
381 		EMALLOC(elen, e);
382 		snprintf(env[elen++], e, "if_afwaiting=%d", af);
383 	}
384 	if ((af = dhcpcd_afwaiting(ifp->ctx)) != AF_MAX) {
385 		TAILQ_FOREACH(ifp2, ifp->ctx->ifaces, next) {
386 			if ((af = dhcpcd_ifafwaiting(ifp2)) != AF_MAX)
387 				break;
388 		}
389 	}
390 	if (af != AF_MAX) {
391 		e = 20;
392 		EMALLOC(elen, e);
393 		snprintf(env[elen++], e, "af_waiting=%d", af);
394 	}
395 	if (ifo->options & DHCPCD_DEBUG) {
396 		e = strlen("syslog_debug=true") + 1;
397 		EMALLOC(elen, e);
398 		snprintf(env[elen++], e, "syslog_debug=true");
399 	}
400 	if (*ifp->profile) {
401 		e = strlen("profile=") + strlen(ifp->profile) + 1;
402 		EMALLOC(elen, e);
403 		snprintf(env[elen++], e, "profile=%s", ifp->profile);
404 	}
405 	if (ifp->wireless) {
406 		static const char *pfx = "ifssid=";
407 		size_t pfx_len;
408 		ssize_t psl;
409 
410 		pfx_len = strlen(pfx);
411 		psl = print_string(NULL, 0, ESCSTRING,
412 		    (const uint8_t *)ifp->ssid, ifp->ssid_len);
413 		if (psl != -1) {
414 			EMALLOC(elen, pfx_len + (size_t)psl + 1);
415 			memcpy(env[elen], pfx, pfx_len);
416 			print_string(env[elen] + pfx_len, (size_t)psl + 1,
417 			    ESCSTRING,
418 			    (const uint8_t *)ifp->ssid, ifp->ssid_len);
419 			elen++;
420 		}
421 	}
422 #ifdef INET
423 	if (dhcp && state && state->old) {
424 		n = dhcp_env(NULL, NULL, state->old, ifp);
425 		if (n == -1)
426 			goto eexit;
427 		if (n > 0) {
428 			nenv = realloc(env, sizeof(char *) *
429 			    (elen + (size_t)n + 1));
430 			if (nenv == NULL)
431 				goto eexit;
432 			env = nenv;
433 			n = dhcp_env(env + elen, "old", state->old, ifp);
434 			if (n == -1)
435 				goto eexit;
436 			elen += (size_t)n;
437 		}
438 		if (append_config(ifp->ctx, &env, &elen, "old",
439 		    (const char *const *)ifo->config) == -1)
440 			goto eexit;
441 	}
442 #endif
443 #ifdef INET6
444 	if (dhcp6 && d6_state && d6_state->old) {
445 		n = dhcp6_env(NULL, NULL, ifp,
446 		    d6_state->old, d6_state->old_len);
447 		if (n > 0) {
448 			nenv = realloc(env, sizeof(char *) *
449 			    (elen + (size_t)n + 1));
450 			if (nenv == NULL)
451 				goto eexit;
452 			env = nenv;
453 			n = dhcp6_env(env + elen, "old", ifp,
454 			    d6_state->old, d6_state->old_len);
455 			if (n == -1)
456 				goto eexit;
457 			elen += (size_t)n;
458 		}
459 	}
460 #endif
461 
462 dumplease:
463 #ifdef INET
464 	if (ipv4ll) {
465 		n = ipv4ll_env(NULL, NULL, ifp);
466 		if (n > 0) {
467 			nenv = realloc(env, sizeof(char *) *
468 			    (elen + (size_t)n + 1));
469 			if (nenv == NULL)
470 				goto eexit;
471 			env = nenv;
472 			if ((n = ipv4ll_env(env + elen,
473 			    istate->down ? "old" : "new", ifp)) == -1)
474 				goto eexit;
475 			elen += (size_t)n;
476 		}
477 	}
478 	if (dhcp && state && state->new) {
479 		n = dhcp_env(NULL, NULL, state->new, ifp);
480 		if (n > 0) {
481 			nenv = realloc(env, sizeof(char *) *
482 			    (elen + (size_t)n + 1));
483 			if (nenv == NULL)
484 				goto eexit;
485 			env = nenv;
486 			n = dhcp_env(env + elen, "new",
487 			    state->new, ifp);
488 			if (n == -1)
489 				goto eexit;
490 			elen += (size_t)n;
491 		}
492 		if (append_config(ifp->ctx, &env, &elen, "new",
493 		    (const char *const *)ifo->config) == -1)
494 			goto eexit;
495 	}
496 #endif
497 #ifdef INET6
498 	if (dhcp6 && D6_STATE_RUNNING(ifp)) {
499 		n = dhcp6_env(NULL, NULL, ifp,
500 		    d6_state->new, d6_state->new_len);
501 		if (n > 0) {
502 			nenv = realloc(env, sizeof(char *) *
503 			    (elen + (size_t)n + 1));
504 			if (nenv == NULL)
505 				goto eexit;
506 			env = nenv;
507 			n = dhcp6_env(env + elen, "new", ifp,
508 			    d6_state->new, d6_state->new_len);
509 			if (n == -1)
510 				goto eexit;
511 			elen += (size_t)n;
512 		}
513 	}
514 	if (ra) {
515 		n = ipv6nd_env(NULL, NULL, ifp);
516 		if (n > 0) {
517 			nenv = realloc(env, sizeof(char *) *
518 			    (elen + (size_t)n + 1));
519 			if (nenv == NULL)
520 				goto eexit;
521 			env = nenv;
522 			n = ipv6nd_env(env + elen, NULL, ifp);
523 			if (n == -1)
524 				goto eexit;
525 			elen += (size_t)n;
526 		}
527 	}
528 #endif
529 
530 	/* Add our base environment */
531 	if (ifo->environ) {
532 		e = 0;
533 		while (ifo->environ[e++])
534 			;
535 		nenv = realloc(env, sizeof(char *) * (elen + e + 1));
536 		if (nenv == NULL)
537 			goto eexit;
538 		env = nenv;
539 		e = 0;
540 		while (ifo->environ[e]) {
541 			env[elen + e] = strdup(ifo->environ[e]);
542 			if (env[elen + e] == NULL)
543 				goto eexit;
544 			e++;
545 		}
546 		elen += e;
547 	}
548 	env[elen] = NULL;
549 
550 	*argv = env;
551 	return (ssize_t)elen;
552 
553 eexit:
554 	logger(ifp->ctx, LOG_ERR, "%s: %m", __func__);
555 	if (env) {
556 		nenv = env;
557 		while (*nenv)
558 			free(*nenv++);
559 		free(env);
560 	}
561 	return -1;
562 }
563 
564 static int
565 send_interface1(struct fd_list *fd, const struct interface *iface,
566     const char *reason)
567 {
568 	char **env, **ep, *s;
569 	size_t elen;
570 	int retval;
571 
572 	if (make_env(iface, reason, &env) == -1)
573 		return -1;
574 	s = NULL;
575 	elen = (size_t)arraytostr((const char *const *)env, &s);
576 	if ((ssize_t)elen == -1) {
577 		free(s);
578 		return -1;
579 	}
580 	retval = control_queue(fd, s, elen, 1);
581 	ep = env;
582 	while (*ep)
583 		free(*ep++);
584 	free(env);
585 	return retval;
586 }
587 
588 int
589 send_interface(struct fd_list *fd, const struct interface *ifp)
590 {
591 	const char *reason;
592 	int retval = 0;
593 #ifdef INET
594 	const struct dhcp_state *d;
595 #endif
596 #ifdef INET6
597 	const struct dhcp6_state *d6;
598 #endif
599 
600 	switch (ifp->carrier) {
601 	case LINK_UP:
602 		reason = "CARRIER";
603 		break;
604 	case LINK_DOWN:
605 		reason = "NOCARRIER";
606 		break;
607 	default:
608 		reason = "UNKNOWN";
609 		break;
610 	}
611 	if (send_interface1(fd, ifp, reason) == -1)
612 		retval = -1;
613 #ifdef INET
614 	if (D_STATE_RUNNING(ifp)) {
615 		d = D_CSTATE(ifp);
616 		if (send_interface1(fd, ifp, d->reason) == -1)
617 			retval = -1;
618 	}
619 	if (IPV4LL_STATE_RUNNING(ifp)) {
620 		if (send_interface1(fd, ifp, "IPV4LL") == -1)
621 			retval = -1;
622 	}
623 #endif
624 
625 #ifdef INET6
626 	if (RS_STATE_RUNNING(ifp)) {
627 		if (send_interface1(fd, ifp, "ROUTERADVERT") == -1)
628 			retval = -1;
629 	}
630 	if (D6_STATE_RUNNING(ifp)) {
631 		d6 = D6_CSTATE(ifp);
632 		if (send_interface1(fd, ifp, d6->reason) == -1)
633 			retval = -1;
634 	}
635 #endif
636 
637 	return retval;
638 }
639 
640 int
641 script_runreason(const struct interface *ifp, const char *reason)
642 {
643 	char *argv[2];
644 	char **env = NULL, **ep;
645 	char *svcname, *path, *bigenv;
646 	size_t e, elen = 0;
647 	pid_t pid;
648 	int status = 0;
649 	struct fd_list *fd;
650 
651 	if (ifp->options->script &&
652 	    (ifp->options->script[0] == '\0' ||
653 	    strcmp(ifp->options->script, "/dev/null") == 0) &&
654 	    TAILQ_FIRST(&ifp->ctx->control_fds) == NULL)
655 		return 0;
656 
657 	/* Make our env */
658 	elen = (size_t)make_env(ifp, reason, &env);
659 	if (elen == (size_t)-1) {
660 		logger(ifp->ctx, LOG_ERR, "%s: make_env: %m", ifp->name);
661 		return -1;
662 	}
663 
664 	if (ifp->options->script &&
665 	    (ifp->options->script[0] == '\0' ||
666 	    strcmp(ifp->options->script, "/dev/null") == 0))
667 	    	goto send_listeners;
668 
669 	argv[0] = ifp->options->script ? ifp->options->script : UNCONST(SCRIPT);
670 	argv[1] = NULL;
671 	logger(ifp->ctx, LOG_DEBUG, "%s: executing `%s' %s",
672 	    ifp->name, argv[0], reason);
673 
674 	/* Resize for PATH and RC_SVCNAME */
675 	svcname = getenv(RC_SVCNAME);
676 	ep = realloc(env, sizeof(char *) * (elen + 2 + (svcname ? 1 : 0)));
677 	if (ep == NULL) {
678 		elen = 0;
679 		goto out;
680 	}
681 	env = ep;
682 	/* Add path to it */
683 	path = getenv("PATH");
684 	if (path) {
685 		e = strlen("PATH") + strlen(path) + 2;
686 		env[elen] = malloc(e);
687 		if (env[elen] == NULL) {
688 			elen = 0;
689 			goto out;
690 		}
691 		snprintf(env[elen], e, "PATH=%s", path);
692 	} else {
693 		env[elen] = strdup(DEFAULT_PATH);
694 		if (env[elen] == NULL) {
695 			elen = 0;
696 			goto out;
697 		}
698 	}
699 	if (svcname) {
700 		e = strlen(RC_SVCNAME) + strlen(svcname) + 2;
701 		env[++elen] = malloc(e);
702 		if (env[elen] == NULL) {
703 			elen = 0;
704 			goto out;
705 		}
706 		snprintf(env[elen], e, "%s=%s", RC_SVCNAME, svcname);
707 	}
708 	env[++elen] = NULL;
709 
710 	pid = exec_script(ifp->ctx, argv, env);
711 	if (pid == -1)
712 		logger(ifp->ctx, LOG_ERR, "%s: %s: %m", __func__, argv[0]);
713 	else if (pid != 0) {
714 		/* Wait for the script to finish */
715 		while (waitpid(pid, &status, 0) == -1) {
716 			if (errno != EINTR) {
717 				logger(ifp->ctx, LOG_ERR, "waitpid: %m");
718 				status = 0;
719 				break;
720 			}
721 		}
722 		if (WIFEXITED(status)) {
723 			if (WEXITSTATUS(status))
724 				logger(ifp->ctx, LOG_ERR,
725 				    "%s: %s: WEXITSTATUS %d",
726 				    __func__, argv[0], WEXITSTATUS(status));
727 		} else if (WIFSIGNALED(status))
728 			logger(ifp->ctx, LOG_ERR, "%s: %s: %s",
729 			    __func__, argv[0], strsignal(WTERMSIG(status)));
730 	}
731 
732 send_listeners:
733 	/* Send to our listeners */
734 	bigenv = NULL;
735 	status = 0;
736 	TAILQ_FOREACH(fd, &ifp->ctx->control_fds, next) {
737 		if (!(fd->flags & FD_LISTEN))
738 			continue;
739 		if (bigenv == NULL) {
740 			elen = (size_t)arraytostr((const char *const *)env,
741 			    &bigenv);
742 			if ((ssize_t)elen == -1) {
743 				logger(ifp->ctx, LOG_ERR, "%s: arraytostr: %m",
744 				    ifp->name);
745 				    break;
746 			}
747 		}
748 		if (control_queue(fd, bigenv, elen, 1) == -1)
749 			logger(ifp->ctx, LOG_ERR,
750 			    "%s: control_queue: %m", __func__);
751 		else
752 			status = 1;
753 	}
754 	if (!status)
755 		free(bigenv);
756 
757 out:
758 	/* Cleanup */
759 	ep = env;
760 	while (*ep)
761 		free(*ep++);
762 	free(env);
763 	if (elen == 0)
764 		return -1;
765 	return WEXITSTATUS(status);
766 }
767