script.c revision 1.1.1.7
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
61static 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
76void
77if_printoptions(void)
78{
79	const char * const *p;
80
81	for (p = if_params; *p; p++)
82		printf(" -  %s\n", *p);
83}
84
85static int
86exec_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
123static char *
124make_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
139static int
140append_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
195static ssize_t
196arraytostr(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
227static const char *protocols[] = {
228	"link",
229	"dhcp",
230	"ipv4ll",
231	"ra",
232	"dhcp6",
233	"static6"
234};
235
236static ssize_t
237make_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
491dumplease:
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
600eexit:
601	logerr(__func__);
602	if (env) {
603		nenv = env;
604		while (*nenv)
605			free(*nenv++);
606		free(env);
607	}
608	return -1;
609}
610
611static int
612send_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
635int
636send_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
696int
697script_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
782send_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
805out:
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