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