xref: /dflybsd-src/contrib/dhcpcd/src/script.c (revision 8d36e1df80df39bec701fcb39b0b7982e1b94e77)
1*8d36e1dfSRoy Marples /* SPDX-License-Identifier: BSD-2-Clause */
27827cba2SAaron LI /*
37827cba2SAaron LI  * dhcpcd - DHCP client daemon
4*8d36e1dfSRoy Marples  * Copyright (c) 2006-2019 Roy Marples <roy@marples.name>
57827cba2SAaron LI  * All rights reserved
67827cba2SAaron LI 
77827cba2SAaron LI  * Redistribution and use in source and binary forms, with or without
87827cba2SAaron LI  * modification, are permitted provided that the following conditions
97827cba2SAaron LI  * are met:
107827cba2SAaron LI  * 1. Redistributions of source code must retain the above copyright
117827cba2SAaron LI  *    notice, this list of conditions and the following disclaimer.
127827cba2SAaron LI  * 2. Redistributions in binary form must reproduce the above copyright
137827cba2SAaron LI  *    notice, this list of conditions and the following disclaimer in the
147827cba2SAaron LI  *    documentation and/or other materials provided with the distribution.
157827cba2SAaron LI  *
167827cba2SAaron LI  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
177827cba2SAaron LI  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
187827cba2SAaron LI  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
197827cba2SAaron LI  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
207827cba2SAaron LI  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
217827cba2SAaron LI  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
227827cba2SAaron LI  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
237827cba2SAaron LI  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
247827cba2SAaron LI  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
257827cba2SAaron LI  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
267827cba2SAaron LI  * SUCH DAMAGE.
277827cba2SAaron LI  */
287827cba2SAaron LI 
297827cba2SAaron LI #include <sys/stat.h>
307827cba2SAaron LI #include <sys/uio.h>
317827cba2SAaron LI #include <sys/wait.h>
327827cba2SAaron LI 
337827cba2SAaron LI #include <netinet/in.h>
347827cba2SAaron LI #include <arpa/inet.h>
357827cba2SAaron LI 
36*8d36e1dfSRoy Marples #include <assert.h>
377827cba2SAaron LI #include <ctype.h>
387827cba2SAaron LI #include <errno.h>
397827cba2SAaron LI #include <signal.h>
407827cba2SAaron LI #include <spawn.h>
41*8d36e1dfSRoy Marples #include <stdarg.h>
427827cba2SAaron LI #include <stdlib.h>
437827cba2SAaron LI #include <string.h>
447827cba2SAaron LI #include <unistd.h>
457827cba2SAaron LI 
467827cba2SAaron LI #include "config.h"
477827cba2SAaron LI #include "common.h"
487827cba2SAaron LI #include "dhcp.h"
497827cba2SAaron LI #include "dhcp6.h"
507827cba2SAaron LI #include "if.h"
517827cba2SAaron LI #include "if-options.h"
527827cba2SAaron LI #include "ipv4ll.h"
537827cba2SAaron LI #include "ipv6nd.h"
547827cba2SAaron LI #include "logerr.h"
557827cba2SAaron LI #include "script.h"
567827cba2SAaron LI 
577827cba2SAaron LI /* Allow the OS to define another script env var name */
587827cba2SAaron LI #ifndef RC_SVCNAME
597827cba2SAaron LI #define RC_SVCNAME "RC_SVCNAME"
607827cba2SAaron LI #endif
617827cba2SAaron LI 
62*8d36e1dfSRoy Marples #define DEFAULT_PATH	"/usr/bin:/usr/sbin:/bin:/sbin"
637827cba2SAaron LI 
647827cba2SAaron LI static const char * const if_params[] = {
657827cba2SAaron LI 	"interface",
667827cba2SAaron LI 	"protocol",
677827cba2SAaron LI 	"reason",
687827cba2SAaron LI 	"pid",
697827cba2SAaron LI 	"ifcarrier",
707827cba2SAaron LI 	"ifmetric",
717827cba2SAaron LI 	"ifwireless",
727827cba2SAaron LI 	"ifflags",
737827cba2SAaron LI 	"ssid",
747827cba2SAaron LI 	"profile",
757827cba2SAaron LI 	"interface_order",
767827cba2SAaron LI 	NULL
777827cba2SAaron LI };
787827cba2SAaron LI 
797827cba2SAaron LI void
807827cba2SAaron LI if_printoptions(void)
817827cba2SAaron LI {
827827cba2SAaron LI 	const char * const *p;
837827cba2SAaron LI 
847827cba2SAaron LI 	for (p = if_params; *p; p++)
857827cba2SAaron LI 		printf(" -  %s\n", *p);
867827cba2SAaron LI }
877827cba2SAaron LI 
887827cba2SAaron LI static int
897827cba2SAaron LI exec_script(const struct dhcpcd_ctx *ctx, char *const *argv, char *const *env)
907827cba2SAaron LI {
917827cba2SAaron LI 	pid_t pid;
927827cba2SAaron LI 	posix_spawnattr_t attr;
937827cba2SAaron LI 	int r;
947827cba2SAaron LI #ifdef USE_SIGNALS
957827cba2SAaron LI 	size_t i;
967827cba2SAaron LI 	short flags;
977827cba2SAaron LI 	sigset_t defsigs;
987827cba2SAaron LI #else
997827cba2SAaron LI 	UNUSED(ctx);
1007827cba2SAaron LI #endif
1017827cba2SAaron LI 
1027827cba2SAaron LI 	/* posix_spawn is a safe way of executing another image
1037827cba2SAaron LI 	 * and changing signals back to how they should be. */
1047827cba2SAaron LI 	if (posix_spawnattr_init(&attr) == -1)
1057827cba2SAaron LI 		return -1;
1067827cba2SAaron LI #ifdef USE_SIGNALS
1077827cba2SAaron LI 	flags = POSIX_SPAWN_SETSIGMASK | POSIX_SPAWN_SETSIGDEF;
1087827cba2SAaron LI 	posix_spawnattr_setflags(&attr, flags);
1097827cba2SAaron LI 	sigemptyset(&defsigs);
1107827cba2SAaron LI 	for (i = 0; i < dhcpcd_signals_len; i++)
1117827cba2SAaron LI 		sigaddset(&defsigs, dhcpcd_signals[i]);
1127827cba2SAaron LI 	posix_spawnattr_setsigdefault(&attr, &defsigs);
1137827cba2SAaron LI 	posix_spawnattr_setsigmask(&attr, &ctx->sigset);
1147827cba2SAaron LI #endif
1157827cba2SAaron LI 	errno = 0;
1167827cba2SAaron LI 	r = posix_spawn(&pid, argv[0], NULL, &attr, argv, env);
1177827cba2SAaron LI 	posix_spawnattr_destroy(&attr);
1187827cba2SAaron LI 	if (r) {
1197827cba2SAaron LI 		errno = r;
1207827cba2SAaron LI 		return -1;
1217827cba2SAaron LI 	}
1227827cba2SAaron LI 	return pid;
1237827cba2SAaron LI }
1247827cba2SAaron LI 
1257827cba2SAaron LI #ifdef INET
1267827cba2SAaron LI static int
127*8d36e1dfSRoy Marples append_config(FILE *fp, const char *prefix, const char *const *config)
1287827cba2SAaron LI {
129*8d36e1dfSRoy Marples 	size_t i;
1307827cba2SAaron LI 
1317827cba2SAaron LI 	if (config == NULL)
1327827cba2SAaron LI 		return 0;
1337827cba2SAaron LI 
134*8d36e1dfSRoy Marples 	/* Do we need to replace existing config rather than append? */
1357827cba2SAaron LI 	for (i = 0; config[i] != NULL; i++) {
136*8d36e1dfSRoy Marples 		if (efprintf(fp, "%s_%s", prefix, config[i]) == -1)
1377827cba2SAaron LI 			return -1;
1387827cba2SAaron LI 	}
139*8d36e1dfSRoy Marples 	return 1;
1407827cba2SAaron LI }
1417827cba2SAaron LI 
142*8d36e1dfSRoy Marples #endif
143*8d36e1dfSRoy Marples 
1447827cba2SAaron LI #define	PROTO_LINK	0
1457827cba2SAaron LI #define	PROTO_DHCP	1
1467827cba2SAaron LI #define	PROTO_IPV4LL	2
1477827cba2SAaron LI #define	PROTO_RA	3
1487827cba2SAaron LI #define	PROTO_DHCP6	4
1497827cba2SAaron LI #define	PROTO_STATIC6	5
1507827cba2SAaron LI static const char *protocols[] = {
1517827cba2SAaron LI 	"link",
1527827cba2SAaron LI 	"dhcp",
1537827cba2SAaron LI 	"ipv4ll",
1547827cba2SAaron LI 	"ra",
1557827cba2SAaron LI 	"dhcp6",
1567827cba2SAaron LI 	"static6"
1577827cba2SAaron LI };
1587827cba2SAaron LI 
159*8d36e1dfSRoy Marples int
160*8d36e1dfSRoy Marples efprintf(FILE *fp, const char *fmt, ...)
1617827cba2SAaron LI {
162*8d36e1dfSRoy Marples 	va_list args;
163*8d36e1dfSRoy Marples 	int r;
164*8d36e1dfSRoy Marples 
165*8d36e1dfSRoy Marples 	va_start(args, fmt);
166*8d36e1dfSRoy Marples 	r = vfprintf(fp, fmt, args);
167*8d36e1dfSRoy Marples 	va_end(args);
168*8d36e1dfSRoy Marples 	if (r == -1)
169*8d36e1dfSRoy Marples 		return -1;
170*8d36e1dfSRoy Marples 	/* Write a trailing NULL so we can easily create env strings. */
171*8d36e1dfSRoy Marples 	if (fputc('\0', fp) == EOF)
172*8d36e1dfSRoy Marples 		return -1;
173*8d36e1dfSRoy Marples 	return r;
174*8d36e1dfSRoy Marples }
175*8d36e1dfSRoy Marples 
176*8d36e1dfSRoy Marples static long
177*8d36e1dfSRoy Marples make_env(const struct interface *ifp, const char *reason)
178*8d36e1dfSRoy Marples {
179*8d36e1dfSRoy Marples 	struct dhcpcd_ctx *ctx = ifp->ctx;
180*8d36e1dfSRoy Marples 	FILE *fp;
181*8d36e1dfSRoy Marples 	char **env, **envp, *bufp, *endp, *path;
182*8d36e1dfSRoy Marples 	size_t nenv;
183*8d36e1dfSRoy Marples 	long buf_pos, i;
184*8d36e1dfSRoy Marples 	int protocol = PROTO_LINK;
1857827cba2SAaron LI 	const struct if_options *ifo = ifp->options;
1867827cba2SAaron LI 	const struct interface *ifp2;
1877827cba2SAaron LI 	int af;
1887827cba2SAaron LI #ifdef INET
1897827cba2SAaron LI 	const struct dhcp_state *state;
1907827cba2SAaron LI #ifdef IPV4LL
1917827cba2SAaron LI 	const struct ipv4ll_state *istate;
1927827cba2SAaron LI #endif
1937827cba2SAaron LI #endif
194*8d36e1dfSRoy Marples #ifdef DHCP6
1957827cba2SAaron LI 	const struct dhcp6_state *d6_state;
1967827cba2SAaron LI #endif
1977827cba2SAaron LI 
198*8d36e1dfSRoy Marples #ifdef HAVE_OPEN_MEMSTREAM
199*8d36e1dfSRoy Marples 	if (ctx->script_fp == NULL) {
200*8d36e1dfSRoy Marples 		fp = open_memstream(&ctx->script_buf, &ctx->script_buflen);
201*8d36e1dfSRoy Marples 		if (fp == NULL)
202*8d36e1dfSRoy Marples 			goto eexit;
203*8d36e1dfSRoy Marples 		ctx->script_fp = fp;
204*8d36e1dfSRoy Marples 	} else {
205*8d36e1dfSRoy Marples 		fp = ctx->script_fp;
206*8d36e1dfSRoy Marples 		rewind(fp);
207*8d36e1dfSRoy Marples 	}
208*8d36e1dfSRoy Marples #else
209*8d36e1dfSRoy Marples 	char tmpfile[] = "/tmp/dhcpcd-script-env-XXXXXX";
210*8d36e1dfSRoy Marples 	int tmpfd;
211*8d36e1dfSRoy Marples 
212*8d36e1dfSRoy Marples 	fp = NULL;
213*8d36e1dfSRoy Marples 	tmpfd = mkstemp(tmpfile);
214*8d36e1dfSRoy Marples 	if (tmpfd == -1)
215*8d36e1dfSRoy Marples 		goto eexit;
216*8d36e1dfSRoy Marples 	unlink(tmpfile);
217*8d36e1dfSRoy Marples 	fp = fdopen(tmpfd, "w+");
218*8d36e1dfSRoy Marples 	if (fp == NULL) {
219*8d36e1dfSRoy Marples 		close(tmpfd);
220*8d36e1dfSRoy Marples 		goto eexit;
221*8d36e1dfSRoy Marples 	}
222*8d36e1dfSRoy Marples #endif
223*8d36e1dfSRoy Marples 
2247827cba2SAaron LI #ifdef INET
2257827cba2SAaron LI 	state = D_STATE(ifp);
2267827cba2SAaron LI #ifdef IPV4LL
2277827cba2SAaron LI 	istate = IPV4LL_CSTATE(ifp);
2287827cba2SAaron LI #endif
2297827cba2SAaron LI #endif
230*8d36e1dfSRoy Marples #ifdef DHCP6
2317827cba2SAaron LI 	d6_state = D6_CSTATE(ifp);
2327827cba2SAaron LI #endif
2337827cba2SAaron LI 	if (strcmp(reason, "TEST") == 0) {
2347827cba2SAaron LI 		if (1 == 2) {}
2357827cba2SAaron LI #ifdef INET6
236*8d36e1dfSRoy Marples #ifdef DHCP6
2377827cba2SAaron LI 		else if (d6_state && d6_state->new)
2387827cba2SAaron LI 			protocol = PROTO_DHCP6;
239*8d36e1dfSRoy Marples #endif
2407827cba2SAaron LI 		else if (ipv6nd_hasra(ifp))
2417827cba2SAaron LI 			protocol = PROTO_RA;
2427827cba2SAaron LI #endif
2437827cba2SAaron LI #ifdef INET
2447827cba2SAaron LI #ifdef IPV4LL
2457827cba2SAaron LI 		else if (istate && istate->addr != NULL)
2467827cba2SAaron LI 			protocol = PROTO_IPV4LL;
2477827cba2SAaron LI #endif
2487827cba2SAaron LI 		else
2497827cba2SAaron LI 			protocol = PROTO_DHCP;
2507827cba2SAaron LI #endif
2517827cba2SAaron LI 	}
2527827cba2SAaron LI #ifdef INET6
2537827cba2SAaron LI 	else if (strcmp(reason, "STATIC6") == 0)
2547827cba2SAaron LI 		protocol = PROTO_STATIC6;
255*8d36e1dfSRoy Marples #ifdef DHCP6
2567827cba2SAaron LI 	else if (reason[strlen(reason) - 1] == '6')
2577827cba2SAaron LI 		protocol = PROTO_DHCP6;
258*8d36e1dfSRoy Marples #endif
2597827cba2SAaron LI 	else if (strcmp(reason, "ROUTERADVERT") == 0)
2607827cba2SAaron LI 		protocol = PROTO_RA;
2617827cba2SAaron LI #endif
2627827cba2SAaron LI 	else if (strcmp(reason, "PREINIT") == 0 ||
2637827cba2SAaron LI 	    strcmp(reason, "CARRIER") == 0 ||
2647827cba2SAaron LI 	    strcmp(reason, "NOCARRIER") == 0 ||
2657827cba2SAaron LI 	    strcmp(reason, "UNKNOWN") == 0 ||
2667827cba2SAaron LI 	    strcmp(reason, "DEPARTED") == 0 ||
2677827cba2SAaron LI 	    strcmp(reason, "STOPPED") == 0)
2687827cba2SAaron LI 		protocol = PROTO_LINK;
2697827cba2SAaron LI #ifdef INET
2707827cba2SAaron LI #ifdef IPV4LL
2717827cba2SAaron LI 	else if (strcmp(reason, "IPV4LL") == 0)
2727827cba2SAaron LI 		protocol = PROTO_IPV4LL;
2737827cba2SAaron LI #endif
2747827cba2SAaron LI 	else
2757827cba2SAaron LI 		protocol = PROTO_DHCP;
2767827cba2SAaron LI #endif
2777827cba2SAaron LI 
278*8d36e1dfSRoy Marples 	/* Needed for scripts */
279*8d36e1dfSRoy Marples 	path = getenv("PATH");
280*8d36e1dfSRoy Marples 	if (efprintf(fp, "PATH=%s", path == NULL ? DEFAULT_PATH:path) == -1)
2817827cba2SAaron LI 		goto eexit;
282*8d36e1dfSRoy Marples 
283*8d36e1dfSRoy Marples 	if (efprintf(fp, "interface=%s", ifp->name) == -1)
284*8d36e1dfSRoy Marples 		goto eexit;
285*8d36e1dfSRoy Marples 	if (efprintf(fp, "reason=%s", reason) == -1)
286*8d36e1dfSRoy Marples 		goto eexit;
2877827cba2SAaron LI 	if (ifp->ctx->options & DHCPCD_DUMPLEASE)
2887827cba2SAaron LI 		goto dumplease;
289*8d36e1dfSRoy Marples 	if (efprintf(fp, "pid=%d", getpid()) == -1)
290*8d36e1dfSRoy Marples 		goto eexit;
291*8d36e1dfSRoy Marples 	if (efprintf(fp, "ifcarrier=%s",
2927827cba2SAaron LI 	    ifp->carrier == LINK_UNKNOWN ? "unknown" :
293*8d36e1dfSRoy Marples 	    ifp->carrier == LINK_UP ? "up" : "down") == -1)
294*8d36e1dfSRoy Marples 		goto eexit;
295*8d36e1dfSRoy Marples 	if (efprintf(fp, "ifmetric=%d", ifp->metric) == -1)
296*8d36e1dfSRoy Marples 		goto eexit;
297*8d36e1dfSRoy Marples 	if (efprintf(fp, "ifwireless=%d", ifp->wireless) == -1)
298*8d36e1dfSRoy Marples 		goto eexit;
299*8d36e1dfSRoy Marples 	if (efprintf(fp, "ifflags=%u", ifp->flags) == -1)
300*8d36e1dfSRoy Marples 		goto eexit;
301*8d36e1dfSRoy Marples 	if (efprintf(fp, "ifmtu=%d", if_getmtu(ifp)) == -1)
302*8d36e1dfSRoy Marples 		goto eexit;
303*8d36e1dfSRoy Marples 
304*8d36e1dfSRoy Marples 	if (fprintf(fp, "interface_order=") == -1)
305*8d36e1dfSRoy Marples 		goto eexit;
3067827cba2SAaron LI 	TAILQ_FOREACH(ifp2, ifp->ctx->ifaces, next) {
307*8d36e1dfSRoy Marples 		if (ifp2 != TAILQ_FIRST(ifp->ctx->ifaces)) {
308*8d36e1dfSRoy Marples 			if (fputc(' ', fp) == EOF)
309*8d36e1dfSRoy Marples 				return -1;
3107827cba2SAaron LI 		}
311*8d36e1dfSRoy Marples 		if (fprintf(fp, "%s", ifp2->name) == -1)
312*8d36e1dfSRoy Marples 			return -1;
3137827cba2SAaron LI 	}
314*8d36e1dfSRoy Marples 	if (fputc('\0', fp) == EOF)
315*8d36e1dfSRoy Marples 		return -1;
316*8d36e1dfSRoy Marples 
3177827cba2SAaron LI 	if (strcmp(reason, "STOPPED") == 0) {
318*8d36e1dfSRoy Marples 		if (efprintf(fp, "if_up=false") == -1)
319*8d36e1dfSRoy Marples 			goto eexit;
320*8d36e1dfSRoy Marples 		if (efprintf(fp, "if_down=%s",
321*8d36e1dfSRoy Marples 		    ifo->options & DHCPCD_RELEASE ? "true" : "false") == -1)
322*8d36e1dfSRoy Marples 			goto eexit;
3237827cba2SAaron LI 	} else if (strcmp(reason, "TEST") == 0 ||
3247827cba2SAaron LI 	    strcmp(reason, "PREINIT") == 0 ||
3257827cba2SAaron LI 	    strcmp(reason, "CARRIER") == 0 ||
3267827cba2SAaron LI 	    strcmp(reason, "UNKNOWN") == 0)
3277827cba2SAaron LI 	{
328*8d36e1dfSRoy Marples 		if (efprintf(fp, "if_up=false") == -1)
329*8d36e1dfSRoy Marples 			goto eexit;
330*8d36e1dfSRoy Marples 		if (efprintf(fp, "if_down=false") == -1)
331*8d36e1dfSRoy Marples 			goto eexit;
3327827cba2SAaron LI 	} else if (1 == 2 /* appease ifdefs */
3337827cba2SAaron LI #ifdef INET
3347827cba2SAaron LI 	    || (protocol == PROTO_DHCP && state && state->new)
3357827cba2SAaron LI #ifdef IPV4LL
3367827cba2SAaron LI 	    || (protocol == PROTO_IPV4LL && IPV4LL_STATE_RUNNING(ifp))
3377827cba2SAaron LI #endif
3387827cba2SAaron LI #endif
3397827cba2SAaron LI #ifdef INET6
3407827cba2SAaron LI 	    || (protocol == PROTO_STATIC6 && IPV6_STATE_RUNNING(ifp))
341*8d36e1dfSRoy Marples #ifdef DHCP6
3427827cba2SAaron LI 	    || (protocol == PROTO_DHCP6 && d6_state && d6_state->new)
343*8d36e1dfSRoy Marples #endif
3447827cba2SAaron LI 	    || (protocol == PROTO_RA && ipv6nd_hasra(ifp))
3457827cba2SAaron LI #endif
3467827cba2SAaron LI 	    )
3477827cba2SAaron LI 	{
348*8d36e1dfSRoy Marples 		if (efprintf(fp, "if_up=true") == -1)
349*8d36e1dfSRoy Marples 			goto eexit;
350*8d36e1dfSRoy Marples 		if (efprintf(fp, "if_down=false") == -1)
351*8d36e1dfSRoy Marples 			goto eexit;
3527827cba2SAaron LI 	} else {
353*8d36e1dfSRoy Marples 		if (efprintf(fp, "if_up=false") == -1)
354*8d36e1dfSRoy Marples 			goto eexit;
355*8d36e1dfSRoy Marples 		if (efprintf(fp, "if_down=true") == -1)
356*8d36e1dfSRoy Marples 			goto eexit;
3577827cba2SAaron LI 	}
3587827cba2SAaron LI 	if (protocols[protocol] != NULL) {
359*8d36e1dfSRoy Marples 		if (efprintf(fp, "protocol=%s", protocols[protocol]) == -1)
3607827cba2SAaron LI 			goto eexit;
3617827cba2SAaron LI 	}
3627827cba2SAaron LI 	if ((af = dhcpcd_ifafwaiting(ifp)) != AF_MAX) {
363*8d36e1dfSRoy Marples 		if (efprintf(fp, "if_afwaiting=%d", af) == -1)
364*8d36e1dfSRoy Marples 			goto eexit;
3657827cba2SAaron LI 	}
3667827cba2SAaron LI 	if ((af = dhcpcd_afwaiting(ifp->ctx)) != AF_MAX) {
3677827cba2SAaron LI 		TAILQ_FOREACH(ifp2, ifp->ctx->ifaces, next) {
3687827cba2SAaron LI 			if ((af = dhcpcd_ifafwaiting(ifp2)) != AF_MAX)
3697827cba2SAaron LI 				break;
3707827cba2SAaron LI 		}
3717827cba2SAaron LI 	}
3727827cba2SAaron LI 	if (af != AF_MAX) {
373*8d36e1dfSRoy Marples 		if (efprintf(fp, "af_waiting=%d", af) == -1)
374*8d36e1dfSRoy Marples 			goto eexit;
3757827cba2SAaron LI 	}
3767827cba2SAaron LI 	if (ifo->options & DHCPCD_DEBUG) {
377*8d36e1dfSRoy Marples 		if (efprintf(fp, "syslog_debug=true") == -1)
378*8d36e1dfSRoy Marples 			goto eexit;
3797827cba2SAaron LI 	}
3807827cba2SAaron LI 	if (*ifp->profile) {
381*8d36e1dfSRoy Marples 		if (efprintf(fp, "profile=%s", ifp->profile) == -1)
382*8d36e1dfSRoy Marples 			goto eexit;
3837827cba2SAaron LI 	}
3847827cba2SAaron LI 	if (ifp->wireless) {
385*8d36e1dfSRoy Marples 		char pssid[IF_SSIDLEN * 4];
3867827cba2SAaron LI 
387*8d36e1dfSRoy Marples 		if (print_string(pssid, sizeof(pssid), OT_ESCSTRING,
388*8d36e1dfSRoy Marples 		    ifp->ssid, ifp->ssid_len) != -1)
389*8d36e1dfSRoy Marples 		{
390*8d36e1dfSRoy Marples 			if (efprintf(fp, "ifssid=%s", pssid) == -1)
391*8d36e1dfSRoy Marples 				goto eexit;
3927827cba2SAaron LI 		}
3937827cba2SAaron LI 	}
3947827cba2SAaron LI #ifdef INET
3957827cba2SAaron LI 	if (protocol == PROTO_DHCP && state && state->old) {
396*8d36e1dfSRoy Marples 		if (dhcp_env(fp, "old", ifp,
397*8d36e1dfSRoy Marples 		    state->old, state->old_len) == -1)
3987827cba2SAaron LI 			goto eexit;
399*8d36e1dfSRoy Marples 		if (append_config(fp, "old",
4007827cba2SAaron LI 		    (const char *const *)ifo->config) == -1)
4017827cba2SAaron LI 			goto eexit;
4027827cba2SAaron LI 	}
4037827cba2SAaron LI #endif
404*8d36e1dfSRoy Marples #ifdef DHCP6
4057827cba2SAaron LI 	if (protocol == PROTO_DHCP6 && d6_state && d6_state->old) {
406*8d36e1dfSRoy Marples 		if (dhcp6_env(fp, "old", ifp,
407*8d36e1dfSRoy Marples 		    d6_state->old, d6_state->old_len) == -1)
4087827cba2SAaron LI 			goto eexit;
4097827cba2SAaron LI 	}
4107827cba2SAaron LI #endif
4117827cba2SAaron LI 
4127827cba2SAaron LI dumplease:
4137827cba2SAaron LI #ifdef INET
4147827cba2SAaron LI #ifdef IPV4LL
4157827cba2SAaron LI 	if (protocol == PROTO_IPV4LL) {
416*8d36e1dfSRoy Marples 		if (ipv4ll_env(fp, istate->down ? "old" : "new", ifp) == -1)
4177827cba2SAaron LI 			goto eexit;
4187827cba2SAaron LI 	}
4197827cba2SAaron LI #endif
4207827cba2SAaron LI 	if (protocol == PROTO_DHCP && state && state->new) {
421*8d36e1dfSRoy Marples 		if (dhcp_env(fp, "new", ifp,
422*8d36e1dfSRoy Marples 		    state->new, state->new_len) == -1)
4237827cba2SAaron LI 			goto eexit;
424*8d36e1dfSRoy Marples 		if (append_config(fp, "new",
4257827cba2SAaron LI 		    (const char *const *)ifo->config) == -1)
4267827cba2SAaron LI 			goto eexit;
4277827cba2SAaron LI 	}
4287827cba2SAaron LI #endif
4297827cba2SAaron LI #ifdef INET6
4307827cba2SAaron LI 	if (protocol == PROTO_STATIC6) {
431*8d36e1dfSRoy Marples 		if (ipv6_env(fp, "new", ifp) == -1)
4327827cba2SAaron LI 			goto eexit;
4337827cba2SAaron LI 	}
434*8d36e1dfSRoy Marples #ifdef DHCP6
4357827cba2SAaron LI 	if (protocol == PROTO_DHCP6 && D6_STATE_RUNNING(ifp)) {
436*8d36e1dfSRoy Marples 		if (dhcp6_env(fp, "new", ifp,
437*8d36e1dfSRoy Marples 		    d6_state->new, d6_state->new_len) == -1)
4387827cba2SAaron LI 			goto eexit;
4397827cba2SAaron LI 	}
440*8d36e1dfSRoy Marples #endif
4417827cba2SAaron LI 	if (protocol == PROTO_RA) {
442*8d36e1dfSRoy Marples 		if (ipv6nd_env(fp, ifp) == -1)
4437827cba2SAaron LI 			goto eexit;
4447827cba2SAaron LI 	}
4457827cba2SAaron LI #endif
4467827cba2SAaron LI 
4477827cba2SAaron LI 	/* Add our base environment */
4487827cba2SAaron LI 	if (ifo->environ) {
449*8d36e1dfSRoy Marples 		for (i = 0; ifo->environ[i] != NULL; i++)
450*8d36e1dfSRoy Marples 			if (efprintf(fp, "%s", ifo->environ[i]) == -1)
4517827cba2SAaron LI 				goto eexit;
4527827cba2SAaron LI 	}
4537827cba2SAaron LI 
454*8d36e1dfSRoy Marples 	/* Convert buffer to argv */
455*8d36e1dfSRoy Marples 	fflush(fp);
456*8d36e1dfSRoy Marples 
457*8d36e1dfSRoy Marples 	buf_pos = ftell(fp);
458*8d36e1dfSRoy Marples 	if (buf_pos == -1) {
459*8d36e1dfSRoy Marples 		logerr(__func__);
460*8d36e1dfSRoy Marples 		goto eexit;
461*8d36e1dfSRoy Marples 	}
462*8d36e1dfSRoy Marples 
463*8d36e1dfSRoy Marples #ifndef HAVE_OPEN_MEMSTREAM
464*8d36e1dfSRoy Marples 	size_t buf_len = (size_t)buf_pos;
465*8d36e1dfSRoy Marples 	if (ctx->script_buflen < buf_len) {
466*8d36e1dfSRoy Marples 		char *buf = realloc(ctx->script_buf, buf_len);
467*8d36e1dfSRoy Marples 		if (buf == NULL)
468*8d36e1dfSRoy Marples 			goto eexit;
469*8d36e1dfSRoy Marples 		ctx->script_buf = buf;
470*8d36e1dfSRoy Marples 		ctx->script_buflen = buf_len;
471*8d36e1dfSRoy Marples 	}
472*8d36e1dfSRoy Marples 	rewind(fp);
473*8d36e1dfSRoy Marples 	if (fread(ctx->script_buf, sizeof(char), buf_len, fp) != buf_len)
474*8d36e1dfSRoy Marples 		goto eexit;
475*8d36e1dfSRoy Marples 	fclose(fp);
476*8d36e1dfSRoy Marples 	fp = NULL;
477*8d36e1dfSRoy Marples #endif
478*8d36e1dfSRoy Marples 
479*8d36e1dfSRoy Marples 	/* Count the terminated env strings.
480*8d36e1dfSRoy Marples 	 * Assert that the terminations are correct. */
481*8d36e1dfSRoy Marples 	nenv = 0;
482*8d36e1dfSRoy Marples 	endp = ctx->script_buf + buf_pos;
483*8d36e1dfSRoy Marples 	for (bufp = ctx->script_buf; bufp < endp; bufp++) {
484*8d36e1dfSRoy Marples 		if (*bufp == '\0') {
485*8d36e1dfSRoy Marples #ifndef NDEBUG
486*8d36e1dfSRoy Marples 			if (bufp + 1 < endp)
487*8d36e1dfSRoy Marples 				assert(*(bufp + 1) != '\0');
488*8d36e1dfSRoy Marples #endif
489*8d36e1dfSRoy Marples 			nenv++;
490*8d36e1dfSRoy Marples 		}
491*8d36e1dfSRoy Marples 	}
492*8d36e1dfSRoy Marples 	assert(*(bufp - 1) == '\0');
493*8d36e1dfSRoy Marples 
494*8d36e1dfSRoy Marples 	if (ctx->script_envlen < nenv) {
495*8d36e1dfSRoy Marples 		env = reallocarray(ctx->script_env, nenv + 1, sizeof(*env));
496*8d36e1dfSRoy Marples 		if (env == NULL)
497*8d36e1dfSRoy Marples 			goto eexit;
498*8d36e1dfSRoy Marples 		ctx->script_env = env;
499*8d36e1dfSRoy Marples 		ctx->script_envlen = nenv;
500*8d36e1dfSRoy Marples 	}
501*8d36e1dfSRoy Marples 
502*8d36e1dfSRoy Marples 	bufp = ctx->script_buf;
503*8d36e1dfSRoy Marples 	envp = ctx->script_env;
504*8d36e1dfSRoy Marples 	*envp++ = bufp++;
505*8d36e1dfSRoy Marples 	endp--; /* Avoid setting the last \0 to an invalid pointer */
506*8d36e1dfSRoy Marples 	for (; bufp < endp; bufp++) {
507*8d36e1dfSRoy Marples 		if (*bufp == '\0')
508*8d36e1dfSRoy Marples 			*envp++ = bufp + 1;
509*8d36e1dfSRoy Marples 	}
510*8d36e1dfSRoy Marples 	*envp = NULL;
511*8d36e1dfSRoy Marples 
512*8d36e1dfSRoy Marples 	return buf_pos - 1;
5137827cba2SAaron LI 
5147827cba2SAaron LI eexit:
5157827cba2SAaron LI 	logerr(__func__);
516*8d36e1dfSRoy Marples #ifndef HAVE_OPEN_MEMSTREAM
517*8d36e1dfSRoy Marples 	if (fp != NULL)
518*8d36e1dfSRoy Marples 		fclose(fp);
519*8d36e1dfSRoy Marples #endif
5207827cba2SAaron LI 	return -1;
5217827cba2SAaron LI }
5227827cba2SAaron LI 
5237827cba2SAaron LI static int
524*8d36e1dfSRoy Marples send_interface1(struct fd_list *fd, const struct interface *ifp,
5257827cba2SAaron LI     const char *reason)
5267827cba2SAaron LI {
527*8d36e1dfSRoy Marples 	struct dhcpcd_ctx *ctx = ifp->ctx;
528*8d36e1dfSRoy Marples 	long len;
5297827cba2SAaron LI 
530*8d36e1dfSRoy Marples 	len = make_env(ifp, reason);
531*8d36e1dfSRoy Marples 	if (len == -1)
5327827cba2SAaron LI 		return -1;
533*8d36e1dfSRoy Marples 	return control_queue(fd, ctx->script_buf,  (size_t)len, 1);
5347827cba2SAaron LI }
5357827cba2SAaron LI 
5367827cba2SAaron LI int
5377827cba2SAaron LI send_interface(struct fd_list *fd, const struct interface *ifp)
5387827cba2SAaron LI {
5397827cba2SAaron LI 	const char *reason;
5407827cba2SAaron LI 	int retval = 0;
5417827cba2SAaron LI #ifdef INET
5427827cba2SAaron LI 	const struct dhcp_state *d;
5437827cba2SAaron LI #endif
544*8d36e1dfSRoy Marples #ifdef DHCP6
5457827cba2SAaron LI 	const struct dhcp6_state *d6;
5467827cba2SAaron LI #endif
5477827cba2SAaron LI 
5487827cba2SAaron LI 	switch (ifp->carrier) {
5497827cba2SAaron LI 	case LINK_UP:
5507827cba2SAaron LI 		reason = "CARRIER";
5517827cba2SAaron LI 		break;
5527827cba2SAaron LI 	case LINK_DOWN:
553*8d36e1dfSRoy Marples 	case LINK_DOWN_IFFUP:
5547827cba2SAaron LI 		reason = "NOCARRIER";
5557827cba2SAaron LI 		break;
5567827cba2SAaron LI 	default:
5577827cba2SAaron LI 		reason = "UNKNOWN";
5587827cba2SAaron LI 		break;
5597827cba2SAaron LI 	}
5607827cba2SAaron LI 	if (send_interface1(fd, ifp, reason) == -1)
5617827cba2SAaron LI 		retval = -1;
5627827cba2SAaron LI #ifdef INET
5637827cba2SAaron LI 	if (D_STATE_RUNNING(ifp)) {
5647827cba2SAaron LI 		d = D_CSTATE(ifp);
5657827cba2SAaron LI 		if (send_interface1(fd, ifp, d->reason) == -1)
5667827cba2SAaron LI 			retval = -1;
5677827cba2SAaron LI 	}
5687827cba2SAaron LI #ifdef IPV4LL
5697827cba2SAaron LI 	if (IPV4LL_STATE_RUNNING(ifp)) {
5707827cba2SAaron LI 		if (send_interface1(fd, ifp, "IPV4LL") == -1)
5717827cba2SAaron LI 			retval = -1;
5727827cba2SAaron LI 	}
5737827cba2SAaron LI #endif
5747827cba2SAaron LI #endif
5757827cba2SAaron LI 
5767827cba2SAaron LI #ifdef INET6
5777827cba2SAaron LI 	if (IPV6_STATE_RUNNING(ifp)) {
5787827cba2SAaron LI 		if (send_interface1(fd, ifp, "STATIC6") == -1)
5797827cba2SAaron LI 			retval = -1;
5807827cba2SAaron LI 	}
5817827cba2SAaron LI 	if (RS_STATE_RUNNING(ifp)) {
5827827cba2SAaron LI 		if (send_interface1(fd, ifp, "ROUTERADVERT") == -1)
5837827cba2SAaron LI 			retval = -1;
5847827cba2SAaron LI 	}
585*8d36e1dfSRoy Marples #ifdef DHCP6
5867827cba2SAaron LI 	if (D6_STATE_RUNNING(ifp)) {
5877827cba2SAaron LI 		d6 = D6_CSTATE(ifp);
5887827cba2SAaron LI 		if (send_interface1(fd, ifp, d6->reason) == -1)
5897827cba2SAaron LI 			retval = -1;
5907827cba2SAaron LI 	}
5917827cba2SAaron LI #endif
592*8d36e1dfSRoy Marples #endif
5937827cba2SAaron LI 
5947827cba2SAaron LI 	return retval;
5957827cba2SAaron LI }
5967827cba2SAaron LI 
5977827cba2SAaron LI int
5987827cba2SAaron LI script_runreason(const struct interface *ifp, const char *reason)
5997827cba2SAaron LI {
600*8d36e1dfSRoy Marples 	struct dhcpcd_ctx *ctx = ifp->ctx;
6017827cba2SAaron LI 	char *argv[2];
6027827cba2SAaron LI 	pid_t pid;
6037827cba2SAaron LI 	int status = 0;
6047827cba2SAaron LI 	struct fd_list *fd;
6057827cba2SAaron LI 
606*8d36e1dfSRoy Marples 	if (ifp->options->script == NULL &&
6077827cba2SAaron LI 	    TAILQ_FIRST(&ifp->ctx->control_fds) == NULL)
6087827cba2SAaron LI 		return 0;
6097827cba2SAaron LI 
6107827cba2SAaron LI 	/* Make our env */
611*8d36e1dfSRoy Marples 	if (make_env(ifp, reason) == -1) {
6127827cba2SAaron LI 		logerr(__func__);
6137827cba2SAaron LI 		return -1;
6147827cba2SAaron LI 	}
6157827cba2SAaron LI 
616*8d36e1dfSRoy Marples 	if (ifp->options->script == NULL)
6177827cba2SAaron LI 		goto send_listeners;
6187827cba2SAaron LI 
619*8d36e1dfSRoy Marples 	argv[0] = ifp->options->script;
6207827cba2SAaron LI 	argv[1] = NULL;
6217827cba2SAaron LI 	logdebugx("%s: executing `%s' %s", ifp->name, argv[0], reason);
6227827cba2SAaron LI 
623*8d36e1dfSRoy Marples 	pid = exec_script(ctx, argv, ctx->script_env);
6247827cba2SAaron LI 	if (pid == -1)
6257827cba2SAaron LI 		logerr("%s: %s", __func__, argv[0]);
6267827cba2SAaron LI 	else if (pid != 0) {
6277827cba2SAaron LI 		/* Wait for the script to finish */
6287827cba2SAaron LI 		while (waitpid(pid, &status, 0) == -1) {
6297827cba2SAaron LI 			if (errno != EINTR) {
6307827cba2SAaron LI 				logerr("%s: waitpid", __func__);
6317827cba2SAaron LI 				status = 0;
6327827cba2SAaron LI 				break;
6337827cba2SAaron LI 			}
6347827cba2SAaron LI 		}
6357827cba2SAaron LI 		if (WIFEXITED(status)) {
6367827cba2SAaron LI 			if (WEXITSTATUS(status))
6377827cba2SAaron LI 				logerrx("%s: %s: WEXITSTATUS %d",
6387827cba2SAaron LI 				    __func__, argv[0], WEXITSTATUS(status));
6397827cba2SAaron LI 		} else if (WIFSIGNALED(status))
6407827cba2SAaron LI 			logerrx("%s: %s: %s",
6417827cba2SAaron LI 			    __func__, argv[0], strsignal(WTERMSIG(status)));
6427827cba2SAaron LI 	}
6437827cba2SAaron LI 
6447827cba2SAaron LI send_listeners:
6457827cba2SAaron LI 	/* Send to our listeners */
6467827cba2SAaron LI 	status = 0;
647*8d36e1dfSRoy Marples 	TAILQ_FOREACH(fd, &ctx->control_fds, next) {
6487827cba2SAaron LI 		if (!(fd->flags & FD_LISTEN))
6497827cba2SAaron LI 			continue;
650*8d36e1dfSRoy Marples 		if (control_queue(fd, ctx->script_buf, ctx->script_buflen,
651*8d36e1dfSRoy Marples 		    true) == -1)
6527827cba2SAaron LI 			logerr("%s: control_queue", __func__);
6537827cba2SAaron LI 		else
6547827cba2SAaron LI 			status = 1;
6557827cba2SAaron LI 	}
6567827cba2SAaron LI 
6577827cba2SAaron LI 	return WEXITSTATUS(status);
6587827cba2SAaron LI }
659