xref: /dflybsd-src/contrib/dhcpcd/src/script.c (revision 7f8103cd6117aa6e0a3477a357c138ad32da2540)
1d4fb1e02SRoy Marples /* SPDX-License-Identifier: BSD-2-Clause */
27827cba2SAaron LI /*
37827cba2SAaron LI  * dhcpcd - DHCP client daemon
46e63cc1fSRoy Marples  * Copyright (c) 2006-2020 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 
368d36e1dfSRoy Marples #include <assert.h>
377827cba2SAaron LI #include <ctype.h>
387827cba2SAaron LI #include <errno.h>
396e63cc1fSRoy Marples #include <pwd.h>
407827cba2SAaron LI #include <signal.h>
417827cba2SAaron LI #include <spawn.h>
428d36e1dfSRoy Marples #include <stdarg.h>
437827cba2SAaron LI #include <stdlib.h>
447827cba2SAaron LI #include <string.h>
457827cba2SAaron LI #include <unistd.h>
467827cba2SAaron LI 
477827cba2SAaron LI #include "config.h"
487827cba2SAaron LI #include "common.h"
497827cba2SAaron LI #include "dhcp.h"
507827cba2SAaron LI #include "dhcp6.h"
516e63cc1fSRoy Marples #include "eloop.h"
527827cba2SAaron LI #include "if.h"
537827cba2SAaron LI #include "if-options.h"
547827cba2SAaron LI #include "ipv4ll.h"
557827cba2SAaron LI #include "ipv6nd.h"
567827cba2SAaron LI #include "logerr.h"
576e63cc1fSRoy Marples #include "privsep.h"
587827cba2SAaron LI #include "script.h"
597827cba2SAaron LI 
608d36e1dfSRoy Marples #define DEFAULT_PATH	"/usr/bin:/usr/sbin:/bin:/sbin"
617827cba2SAaron LI 
627827cba2SAaron LI static const char * const if_params[] = {
637827cba2SAaron LI 	"interface",
647827cba2SAaron LI 	"protocol",
657827cba2SAaron LI 	"reason",
667827cba2SAaron LI 	"pid",
677827cba2SAaron LI 	"ifcarrier",
687827cba2SAaron LI 	"ifmetric",
697827cba2SAaron LI 	"ifwireless",
707827cba2SAaron LI 	"ifflags",
717827cba2SAaron LI 	"ssid",
727827cba2SAaron LI 	"profile",
737827cba2SAaron LI 	"interface_order",
747827cba2SAaron LI 	NULL
757827cba2SAaron LI };
767827cba2SAaron LI 
777827cba2SAaron LI void
787827cba2SAaron LI if_printoptions(void)
797827cba2SAaron LI {
807827cba2SAaron LI 	const char * const *p;
817827cba2SAaron LI 
827827cba2SAaron LI 	for (p = if_params; *p; p++)
837827cba2SAaron LI 		printf(" -  %s\n", *p);
847827cba2SAaron LI }
857827cba2SAaron LI 
866e63cc1fSRoy Marples pid_t
87280986e4SRoy Marples script_exec(char *const *argv, char *const *env)
887827cba2SAaron LI {
89b9ccd228SRoy Marples 	pid_t pid = 0;
907827cba2SAaron LI 	posix_spawnattr_t attr;
917827cba2SAaron LI 	int r;
927827cba2SAaron LI #ifdef USE_SIGNALS
937827cba2SAaron LI 	size_t i;
947827cba2SAaron LI 	short flags;
957827cba2SAaron LI 	sigset_t defsigs;
967827cba2SAaron LI #else
977827cba2SAaron LI 	UNUSED(ctx);
987827cba2SAaron LI #endif
997827cba2SAaron LI 
1007827cba2SAaron LI 	/* posix_spawn is a safe way of executing another image
1017827cba2SAaron LI 	 * and changing signals back to how they should be. */
1027827cba2SAaron LI 	if (posix_spawnattr_init(&attr) == -1)
1037827cba2SAaron LI 		return -1;
1047827cba2SAaron LI #ifdef USE_SIGNALS
1057827cba2SAaron LI 	flags = POSIX_SPAWN_SETSIGMASK | POSIX_SPAWN_SETSIGDEF;
1067827cba2SAaron LI 	posix_spawnattr_setflags(&attr, flags);
1077827cba2SAaron LI 	sigemptyset(&defsigs);
108280986e4SRoy Marples 	posix_spawnattr_setsigmask(&attr, &defsigs);
1097827cba2SAaron LI 	for (i = 0; i < dhcpcd_signals_len; i++)
1107827cba2SAaron LI 		sigaddset(&defsigs, dhcpcd_signals[i]);
111d4fb1e02SRoy Marples 	for (i = 0; i < dhcpcd_signals_ignore_len; i++)
112d4fb1e02SRoy Marples 		sigaddset(&defsigs, dhcpcd_signals_ignore[i]);
1137827cba2SAaron LI 	posix_spawnattr_setsigdefault(&attr, &defsigs);
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
1278d36e1dfSRoy Marples append_config(FILE *fp, const char *prefix, const char *const *config)
1287827cba2SAaron LI {
1298d36e1dfSRoy Marples 	size_t i;
1307827cba2SAaron LI 
1317827cba2SAaron LI 	if (config == NULL)
1327827cba2SAaron LI 		return 0;
1337827cba2SAaron LI 
1348d36e1dfSRoy Marples 	/* Do we need to replace existing config rather than append? */
1357827cba2SAaron LI 	for (i = 0; config[i] != NULL; i++) {
1368d36e1dfSRoy Marples 		if (efprintf(fp, "%s_%s", prefix, config[i]) == -1)
1377827cba2SAaron LI 			return -1;
1387827cba2SAaron LI 	}
1398d36e1dfSRoy Marples 	return 1;
1407827cba2SAaron LI }
1417827cba2SAaron LI 
1428d36e1dfSRoy Marples #endif
1438d36e1dfSRoy 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 
1598d36e1dfSRoy Marples int
1608d36e1dfSRoy Marples efprintf(FILE *fp, const char *fmt, ...)
1617827cba2SAaron LI {
1628d36e1dfSRoy Marples 	va_list args;
1638d36e1dfSRoy Marples 	int r;
1648d36e1dfSRoy Marples 
1658d36e1dfSRoy Marples 	va_start(args, fmt);
1668d36e1dfSRoy Marples 	r = vfprintf(fp, fmt, args);
1678d36e1dfSRoy Marples 	va_end(args);
1688d36e1dfSRoy Marples 	if (r == -1)
1698d36e1dfSRoy Marples 		return -1;
1708d36e1dfSRoy Marples 	/* Write a trailing NULL so we can easily create env strings. */
1718d36e1dfSRoy Marples 	if (fputc('\0', fp) == EOF)
1728d36e1dfSRoy Marples 		return -1;
1738d36e1dfSRoy Marples 	return r;
1748d36e1dfSRoy Marples }
1758d36e1dfSRoy Marples 
1766e63cc1fSRoy Marples char **
177b9ccd228SRoy Marples script_buftoenv(struct dhcpcd_ctx *ctx, char *buf, size_t len)
178b9ccd228SRoy Marples {
179b9ccd228SRoy Marples 	char **env, **envp, *bufp, *endp;
180b9ccd228SRoy Marples 	size_t nenv;
181b9ccd228SRoy Marples 
182b9ccd228SRoy Marples 	/* Count the terminated env strings.
183b9ccd228SRoy Marples 	 * Assert that the terminations are correct. */
184b9ccd228SRoy Marples 	nenv = 0;
185b9ccd228SRoy Marples 	endp = buf + len;
186b9ccd228SRoy Marples 	for (bufp = buf; bufp < endp; bufp++) {
187b9ccd228SRoy Marples 		if (*bufp == '\0') {
188b9ccd228SRoy Marples #ifndef NDEBUG
189b9ccd228SRoy Marples 			if (bufp + 1 < endp)
190b9ccd228SRoy Marples 				assert(*(bufp + 1) != '\0');
191b9ccd228SRoy Marples #endif
192b9ccd228SRoy Marples 			nenv++;
193b9ccd228SRoy Marples 		}
194b9ccd228SRoy Marples 	}
195b9ccd228SRoy Marples 	assert(*(bufp - 1) == '\0');
196d4fb1e02SRoy Marples 	if (nenv == 0)
197d4fb1e02SRoy Marples 		return NULL;
198b9ccd228SRoy Marples 
199b9ccd228SRoy Marples 	if (ctx->script_envlen < nenv) {
200b9ccd228SRoy Marples 		env = reallocarray(ctx->script_env, nenv + 1, sizeof(*env));
201b9ccd228SRoy Marples 		if (env == NULL)
202b9ccd228SRoy Marples 			return NULL;
203b9ccd228SRoy Marples 		ctx->script_env = env;
204b9ccd228SRoy Marples 		ctx->script_envlen = nenv;
205b9ccd228SRoy Marples 	}
206b9ccd228SRoy Marples 
207b9ccd228SRoy Marples 	bufp = buf;
208b9ccd228SRoy Marples 	envp = ctx->script_env;
209b9ccd228SRoy Marples 	*envp++ = bufp++;
210b9ccd228SRoy Marples 	endp--; /* Avoid setting the last \0 to an invalid pointer */
211b9ccd228SRoy Marples 	for (; bufp < endp; bufp++) {
212b9ccd228SRoy Marples 		if (*bufp == '\0')
213b9ccd228SRoy Marples 			*envp++ = bufp + 1;
214b9ccd228SRoy Marples 	}
215b9ccd228SRoy Marples 	*envp = NULL;
216b9ccd228SRoy Marples 
217b9ccd228SRoy Marples 	return ctx->script_env;
218b9ccd228SRoy Marples }
219b9ccd228SRoy Marples 
2208d36e1dfSRoy Marples static long
2216e63cc1fSRoy Marples make_env(struct dhcpcd_ctx *ctx, const struct interface *ifp,
2226e63cc1fSRoy Marples     const char *reason)
2238d36e1dfSRoy Marples {
2248d36e1dfSRoy Marples 	FILE *fp;
2258d36e1dfSRoy Marples 	long buf_pos, i;
226b9ccd228SRoy Marples 	char *path;
2278d36e1dfSRoy Marples 	int protocol = PROTO_LINK;
2286e63cc1fSRoy Marples 	const struct if_options *ifo;
2297827cba2SAaron LI 	const struct interface *ifp2;
2307827cba2SAaron LI 	int af;
2317827cba2SAaron LI #ifdef INET
2327827cba2SAaron LI 	const struct dhcp_state *state;
2337827cba2SAaron LI #ifdef IPV4LL
2347827cba2SAaron LI 	const struct ipv4ll_state *istate;
2357827cba2SAaron LI #endif
2367827cba2SAaron LI #endif
2378d36e1dfSRoy Marples #ifdef DHCP6
2387827cba2SAaron LI 	const struct dhcp6_state *d6_state;
2397827cba2SAaron LI #endif
240d4fb1e02SRoy Marples 	bool is_stdin = ifp->name[0] == '\0';
2417827cba2SAaron LI 
2428d36e1dfSRoy Marples #ifdef HAVE_OPEN_MEMSTREAM
2438d36e1dfSRoy Marples 	if (ctx->script_fp == NULL) {
2448d36e1dfSRoy Marples 		fp = open_memstream(&ctx->script_buf, &ctx->script_buflen);
2458d36e1dfSRoy Marples 		if (fp == NULL)
2468d36e1dfSRoy Marples 			goto eexit;
2478d36e1dfSRoy Marples 		ctx->script_fp = fp;
2488d36e1dfSRoy Marples 	} else {
2498d36e1dfSRoy Marples 		fp = ctx->script_fp;
2508d36e1dfSRoy Marples 		rewind(fp);
2518d36e1dfSRoy Marples 	}
2528d36e1dfSRoy Marples #else
2538d36e1dfSRoy Marples 	char tmpfile[] = "/tmp/dhcpcd-script-env-XXXXXX";
2548d36e1dfSRoy Marples 	int tmpfd;
2558d36e1dfSRoy Marples 
2568d36e1dfSRoy Marples 	fp = NULL;
2578d36e1dfSRoy Marples 	tmpfd = mkstemp(tmpfile);
258d4fb1e02SRoy Marples 	if (tmpfd == -1) {
259d4fb1e02SRoy Marples 		logerr("%s: mkstemp", __func__);
260d4fb1e02SRoy Marples 		return -1;
261d4fb1e02SRoy Marples 	}
2628d36e1dfSRoy Marples 	unlink(tmpfile);
2638d36e1dfSRoy Marples 	fp = fdopen(tmpfd, "w+");
2648d36e1dfSRoy Marples 	if (fp == NULL) {
2658d36e1dfSRoy Marples 		close(tmpfd);
2668d36e1dfSRoy Marples 		goto eexit;
2678d36e1dfSRoy Marples 	}
2688d36e1dfSRoy Marples #endif
2698d36e1dfSRoy Marples 
270d4fb1e02SRoy Marples 	if (!(ifp->ctx->options & DHCPCD_DUMPLEASE)) {
2716e63cc1fSRoy Marples 		/* Needed for scripts */
2726e63cc1fSRoy Marples 		path = getenv("PATH");
273d4fb1e02SRoy Marples 		if (efprintf(fp, "PATH=%s",
274d4fb1e02SRoy Marples 		    path == NULL ? DEFAULT_PATH : path) == -1)
2756e63cc1fSRoy Marples 			goto eexit;
2766e63cc1fSRoy Marples 		if (efprintf(fp, "pid=%d", getpid()) == -1)
2776e63cc1fSRoy Marples 			goto eexit;
278d4fb1e02SRoy Marples 	}
279d4fb1e02SRoy Marples 	if (!is_stdin) {
280d4fb1e02SRoy Marples 		if (efprintf(fp, "reason=%s", reason) == -1)
2816e63cc1fSRoy Marples 			goto eexit;
2826e63cc1fSRoy Marples 	}
2836e63cc1fSRoy Marples 
2846e63cc1fSRoy Marples 	ifo = ifp->options;
2857827cba2SAaron LI #ifdef INET
2867827cba2SAaron LI 	state = D_STATE(ifp);
2877827cba2SAaron LI #ifdef IPV4LL
2887827cba2SAaron LI 	istate = IPV4LL_CSTATE(ifp);
2897827cba2SAaron LI #endif
2907827cba2SAaron LI #endif
2918d36e1dfSRoy Marples #ifdef DHCP6
2927827cba2SAaron LI 	d6_state = D6_CSTATE(ifp);
2937827cba2SAaron LI #endif
2947827cba2SAaron LI 	if (strcmp(reason, "TEST") == 0) {
2956e63cc1fSRoy Marples 		if (1 == 2) {
2966e63cc1fSRoy Marples 			/* This space left intentionally blank
2976e63cc1fSRoy Marples 			 * as all the below statements are optional. */
2986e63cc1fSRoy Marples 		}
2997827cba2SAaron LI #ifdef INET6
3008d36e1dfSRoy Marples #ifdef DHCP6
3017827cba2SAaron LI 		else if (d6_state && d6_state->new)
3027827cba2SAaron LI 			protocol = PROTO_DHCP6;
3038d36e1dfSRoy Marples #endif
3047827cba2SAaron LI 		else if (ipv6nd_hasra(ifp))
3057827cba2SAaron LI 			protocol = PROTO_RA;
3067827cba2SAaron LI #endif
3077827cba2SAaron LI #ifdef INET
3087827cba2SAaron LI #ifdef IPV4LL
3097827cba2SAaron LI 		else if (istate && istate->addr != NULL)
3107827cba2SAaron LI 			protocol = PROTO_IPV4LL;
3117827cba2SAaron LI #endif
3127827cba2SAaron LI 		else
3137827cba2SAaron LI 			protocol = PROTO_DHCP;
3147827cba2SAaron LI #endif
3157827cba2SAaron LI 	}
3167827cba2SAaron LI #ifdef INET6
3177827cba2SAaron LI 	else if (strcmp(reason, "STATIC6") == 0)
3187827cba2SAaron LI 		protocol = PROTO_STATIC6;
3198d36e1dfSRoy Marples #ifdef DHCP6
3207827cba2SAaron LI 	else if (reason[strlen(reason) - 1] == '6')
3217827cba2SAaron LI 		protocol = PROTO_DHCP6;
3228d36e1dfSRoy Marples #endif
3237827cba2SAaron LI 	else if (strcmp(reason, "ROUTERADVERT") == 0)
3247827cba2SAaron LI 		protocol = PROTO_RA;
3257827cba2SAaron LI #endif
3267827cba2SAaron LI 	else if (strcmp(reason, "PREINIT") == 0 ||
3277827cba2SAaron LI 	    strcmp(reason, "CARRIER") == 0 ||
3287827cba2SAaron LI 	    strcmp(reason, "NOCARRIER") == 0 ||
3297827cba2SAaron LI 	    strcmp(reason, "UNKNOWN") == 0 ||
3307827cba2SAaron LI 	    strcmp(reason, "DEPARTED") == 0 ||
3317827cba2SAaron LI 	    strcmp(reason, "STOPPED") == 0)
3327827cba2SAaron LI 		protocol = PROTO_LINK;
3337827cba2SAaron LI #ifdef INET
3347827cba2SAaron LI #ifdef IPV4LL
3357827cba2SAaron LI 	else if (strcmp(reason, "IPV4LL") == 0)
3367827cba2SAaron LI 		protocol = PROTO_IPV4LL;
3377827cba2SAaron LI #endif
3387827cba2SAaron LI 	else
3397827cba2SAaron LI 		protocol = PROTO_DHCP;
3407827cba2SAaron LI #endif
3417827cba2SAaron LI 
342d4fb1e02SRoy Marples 	if (!is_stdin) {
3438d36e1dfSRoy Marples 		if (efprintf(fp, "interface=%s", ifp->name) == -1)
3448d36e1dfSRoy Marples 			goto eexit;
345d4fb1e02SRoy Marples 	}
3467827cba2SAaron LI 	if (ifp->ctx->options & DHCPCD_DUMPLEASE)
3477827cba2SAaron LI 		goto dumplease;
3488d36e1dfSRoy Marples 	if (efprintf(fp, "ifcarrier=%s",
3497827cba2SAaron LI 	    ifp->carrier == LINK_UNKNOWN ? "unknown" :
3508d36e1dfSRoy Marples 	    ifp->carrier == LINK_UP ? "up" : "down") == -1)
3518d36e1dfSRoy Marples 		goto eexit;
3528d36e1dfSRoy Marples 	if (efprintf(fp, "ifmetric=%d", ifp->metric) == -1)
3538d36e1dfSRoy Marples 		goto eexit;
3548d36e1dfSRoy Marples 	if (efprintf(fp, "ifwireless=%d", ifp->wireless) == -1)
3558d36e1dfSRoy Marples 		goto eexit;
3568d36e1dfSRoy Marples 	if (efprintf(fp, "ifflags=%u", ifp->flags) == -1)
3578d36e1dfSRoy Marples 		goto eexit;
3588d36e1dfSRoy Marples 	if (efprintf(fp, "ifmtu=%d", if_getmtu(ifp)) == -1)
3598d36e1dfSRoy Marples 		goto eexit;
3608d36e1dfSRoy Marples 
3618d36e1dfSRoy Marples 	if (fprintf(fp, "interface_order=") == -1)
3628d36e1dfSRoy Marples 		goto eexit;
3637827cba2SAaron LI 	TAILQ_FOREACH(ifp2, ifp->ctx->ifaces, next) {
3648d36e1dfSRoy Marples 		if (ifp2 != TAILQ_FIRST(ifp->ctx->ifaces)) {
3658d36e1dfSRoy Marples 			if (fputc(' ', fp) == EOF)
3668d36e1dfSRoy Marples 				return -1;
3677827cba2SAaron LI 		}
3688d36e1dfSRoy Marples 		if (fprintf(fp, "%s", ifp2->name) == -1)
3698d36e1dfSRoy Marples 			return -1;
3707827cba2SAaron LI 	}
3718d36e1dfSRoy Marples 	if (fputc('\0', fp) == EOF)
3728d36e1dfSRoy Marples 		return -1;
3738d36e1dfSRoy Marples 
3747827cba2SAaron LI 	if (strcmp(reason, "STOPPED") == 0) {
3758d36e1dfSRoy Marples 		if (efprintf(fp, "if_up=false") == -1)
3768d36e1dfSRoy Marples 			goto eexit;
3778d36e1dfSRoy Marples 		if (efprintf(fp, "if_down=%s",
3788d36e1dfSRoy Marples 		    ifo->options & DHCPCD_RELEASE ? "true" : "false") == -1)
3798d36e1dfSRoy Marples 			goto eexit;
3807827cba2SAaron LI 	} else if (strcmp(reason, "TEST") == 0 ||
3817827cba2SAaron LI 	    strcmp(reason, "PREINIT") == 0 ||
3827827cba2SAaron LI 	    strcmp(reason, "CARRIER") == 0 ||
3837827cba2SAaron LI 	    strcmp(reason, "UNKNOWN") == 0)
3847827cba2SAaron LI 	{
3858d36e1dfSRoy Marples 		if (efprintf(fp, "if_up=false") == -1)
3868d36e1dfSRoy Marples 			goto eexit;
3878d36e1dfSRoy Marples 		if (efprintf(fp, "if_down=false") == -1)
3888d36e1dfSRoy Marples 			goto eexit;
3897827cba2SAaron LI 	} else if (1 == 2 /* appease ifdefs */
3907827cba2SAaron LI #ifdef INET
3917827cba2SAaron LI 	    || (protocol == PROTO_DHCP && state && state->new)
3927827cba2SAaron LI #ifdef IPV4LL
3937827cba2SAaron LI 	    || (protocol == PROTO_IPV4LL && IPV4LL_STATE_RUNNING(ifp))
3947827cba2SAaron LI #endif
3957827cba2SAaron LI #endif
3967827cba2SAaron LI #ifdef INET6
3977827cba2SAaron LI 	    || (protocol == PROTO_STATIC6 && IPV6_STATE_RUNNING(ifp))
3988d36e1dfSRoy Marples #ifdef DHCP6
3997827cba2SAaron LI 	    || (protocol == PROTO_DHCP6 && d6_state && d6_state->new)
4008d36e1dfSRoy Marples #endif
4017827cba2SAaron LI 	    || (protocol == PROTO_RA && ipv6nd_hasra(ifp))
4027827cba2SAaron LI #endif
4037827cba2SAaron LI 	    )
4047827cba2SAaron LI 	{
4058d36e1dfSRoy Marples 		if (efprintf(fp, "if_up=true") == -1)
4068d36e1dfSRoy Marples 			goto eexit;
4078d36e1dfSRoy Marples 		if (efprintf(fp, "if_down=false") == -1)
4088d36e1dfSRoy Marples 			goto eexit;
4097827cba2SAaron LI 	} else {
4108d36e1dfSRoy Marples 		if (efprintf(fp, "if_up=false") == -1)
4118d36e1dfSRoy Marples 			goto eexit;
4128d36e1dfSRoy Marples 		if (efprintf(fp, "if_down=true") == -1)
4138d36e1dfSRoy Marples 			goto eexit;
4147827cba2SAaron LI 	}
4157827cba2SAaron LI 	if (protocols[protocol] != NULL) {
4168d36e1dfSRoy Marples 		if (efprintf(fp, "protocol=%s", protocols[protocol]) == -1)
4177827cba2SAaron LI 			goto eexit;
4187827cba2SAaron LI 	}
4197827cba2SAaron LI 	if ((af = dhcpcd_ifafwaiting(ifp)) != AF_MAX) {
4208d36e1dfSRoy Marples 		if (efprintf(fp, "if_afwaiting=%d", af) == -1)
4218d36e1dfSRoy Marples 			goto eexit;
4227827cba2SAaron LI 	}
4237827cba2SAaron LI 	if ((af = dhcpcd_afwaiting(ifp->ctx)) != AF_MAX) {
4247827cba2SAaron LI 		TAILQ_FOREACH(ifp2, ifp->ctx->ifaces, next) {
4257827cba2SAaron LI 			if ((af = dhcpcd_ifafwaiting(ifp2)) != AF_MAX)
4267827cba2SAaron LI 				break;
4277827cba2SAaron LI 		}
4287827cba2SAaron LI 	}
4297827cba2SAaron LI 	if (af != AF_MAX) {
4308d36e1dfSRoy Marples 		if (efprintf(fp, "af_waiting=%d", af) == -1)
4318d36e1dfSRoy Marples 			goto eexit;
4327827cba2SAaron LI 	}
4337827cba2SAaron LI 	if (ifo->options & DHCPCD_DEBUG) {
4348d36e1dfSRoy Marples 		if (efprintf(fp, "syslog_debug=true") == -1)
4358d36e1dfSRoy Marples 			goto eexit;
4367827cba2SAaron LI 	}
437b9ccd228SRoy Marples 	if (*ifp->profile != '\0') {
4388d36e1dfSRoy Marples 		if (efprintf(fp, "profile=%s", ifp->profile) == -1)
4398d36e1dfSRoy Marples 			goto eexit;
4407827cba2SAaron LI 	}
4417827cba2SAaron LI 	if (ifp->wireless) {
4428d36e1dfSRoy Marples 		char pssid[IF_SSIDLEN * 4];
4437827cba2SAaron LI 
4448d36e1dfSRoy Marples 		if (print_string(pssid, sizeof(pssid), OT_ESCSTRING,
4458d36e1dfSRoy Marples 		    ifp->ssid, ifp->ssid_len) != -1)
4468d36e1dfSRoy Marples 		{
4478d36e1dfSRoy Marples 			if (efprintf(fp, "ifssid=%s", pssid) == -1)
4488d36e1dfSRoy Marples 				goto eexit;
4497827cba2SAaron LI 		}
4507827cba2SAaron LI 	}
4517827cba2SAaron LI #ifdef INET
4527827cba2SAaron LI 	if (protocol == PROTO_DHCP && state && state->old) {
4538d36e1dfSRoy Marples 		if (dhcp_env(fp, "old", ifp,
4548d36e1dfSRoy Marples 		    state->old, state->old_len) == -1)
4557827cba2SAaron LI 			goto eexit;
4568d36e1dfSRoy Marples 		if (append_config(fp, "old",
4577827cba2SAaron LI 		    (const char *const *)ifo->config) == -1)
4587827cba2SAaron LI 			goto eexit;
4597827cba2SAaron LI 	}
4607827cba2SAaron LI #endif
4618d36e1dfSRoy Marples #ifdef DHCP6
4627827cba2SAaron LI 	if (protocol == PROTO_DHCP6 && d6_state && d6_state->old) {
4638d36e1dfSRoy Marples 		if (dhcp6_env(fp, "old", ifp,
4648d36e1dfSRoy Marples 		    d6_state->old, d6_state->old_len) == -1)
4657827cba2SAaron LI 			goto eexit;
4667827cba2SAaron LI 	}
4677827cba2SAaron LI #endif
4687827cba2SAaron LI 
4697827cba2SAaron LI dumplease:
4707827cba2SAaron LI #ifdef INET
4717827cba2SAaron LI #ifdef IPV4LL
4726e63cc1fSRoy Marples 	if (protocol == PROTO_IPV4LL && istate) {
4738d36e1dfSRoy Marples 		if (ipv4ll_env(fp, istate->down ? "old" : "new", ifp) == -1)
4747827cba2SAaron LI 			goto eexit;
4757827cba2SAaron LI 	}
4767827cba2SAaron LI #endif
4777827cba2SAaron LI 	if (protocol == PROTO_DHCP && state && state->new) {
4788d36e1dfSRoy Marples 		if (dhcp_env(fp, "new", ifp,
4798d36e1dfSRoy Marples 		    state->new, state->new_len) == -1)
4807827cba2SAaron LI 			goto eexit;
4818d36e1dfSRoy Marples 		if (append_config(fp, "new",
4827827cba2SAaron LI 		    (const char *const *)ifo->config) == -1)
4837827cba2SAaron LI 			goto eexit;
4847827cba2SAaron LI 	}
4857827cba2SAaron LI #endif
4867827cba2SAaron LI #ifdef INET6
4877827cba2SAaron LI 	if (protocol == PROTO_STATIC6) {
4888d36e1dfSRoy Marples 		if (ipv6_env(fp, "new", ifp) == -1)
4897827cba2SAaron LI 			goto eexit;
4907827cba2SAaron LI 	}
4918d36e1dfSRoy Marples #ifdef DHCP6
4927827cba2SAaron LI 	if (protocol == PROTO_DHCP6 && D6_STATE_RUNNING(ifp)) {
4938d36e1dfSRoy Marples 		if (dhcp6_env(fp, "new", ifp,
4948d36e1dfSRoy Marples 		    d6_state->new, d6_state->new_len) == -1)
4957827cba2SAaron LI 			goto eexit;
4967827cba2SAaron LI 	}
4978d36e1dfSRoy Marples #endif
4987827cba2SAaron LI 	if (protocol == PROTO_RA) {
4998d36e1dfSRoy Marples 		if (ipv6nd_env(fp, ifp) == -1)
5007827cba2SAaron LI 			goto eexit;
5017827cba2SAaron LI 	}
5027827cba2SAaron LI #endif
5037827cba2SAaron LI 
5047827cba2SAaron LI 	/* Add our base environment */
5057827cba2SAaron LI 	if (ifo->environ) {
5068d36e1dfSRoy Marples 		for (i = 0; ifo->environ[i] != NULL; i++)
5078d36e1dfSRoy Marples 			if (efprintf(fp, "%s", ifo->environ[i]) == -1)
5087827cba2SAaron LI 				goto eexit;
5097827cba2SAaron LI 	}
5107827cba2SAaron LI 
5118d36e1dfSRoy Marples 	/* Convert buffer to argv */
5128d36e1dfSRoy Marples 	fflush(fp);
5138d36e1dfSRoy Marples 
5148d36e1dfSRoy Marples 	buf_pos = ftell(fp);
5158d36e1dfSRoy Marples 	if (buf_pos == -1) {
5168d36e1dfSRoy Marples 		logerr(__func__);
5178d36e1dfSRoy Marples 		goto eexit;
5188d36e1dfSRoy Marples 	}
5198d36e1dfSRoy Marples 
5208d36e1dfSRoy Marples #ifndef HAVE_OPEN_MEMSTREAM
5218d36e1dfSRoy Marples 	size_t buf_len = (size_t)buf_pos;
5228d36e1dfSRoy Marples 	if (ctx->script_buflen < buf_len) {
5238d36e1dfSRoy Marples 		char *buf = realloc(ctx->script_buf, buf_len);
5248d36e1dfSRoy Marples 		if (buf == NULL)
5258d36e1dfSRoy Marples 			goto eexit;
5268d36e1dfSRoy Marples 		ctx->script_buf = buf;
5278d36e1dfSRoy Marples 		ctx->script_buflen = buf_len;
5288d36e1dfSRoy Marples 	}
5298d36e1dfSRoy Marples 	rewind(fp);
5308d36e1dfSRoy Marples 	if (fread(ctx->script_buf, sizeof(char), buf_len, fp) != buf_len)
5318d36e1dfSRoy Marples 		goto eexit;
5328d36e1dfSRoy Marples 	fclose(fp);
5338d36e1dfSRoy Marples 	fp = NULL;
5348d36e1dfSRoy Marples #endif
5358d36e1dfSRoy Marples 
536d4fb1e02SRoy Marples 	if (is_stdin)
537d4fb1e02SRoy Marples 		return buf_pos;
538d4fb1e02SRoy Marples 
539b9ccd228SRoy Marples 	if (script_buftoenv(ctx, ctx->script_buf, (size_t)buf_pos) == NULL)
5408d36e1dfSRoy Marples 		goto eexit;
5418d36e1dfSRoy Marples 
5426e63cc1fSRoy Marples 	return buf_pos;
5437827cba2SAaron LI 
5447827cba2SAaron LI eexit:
5457827cba2SAaron LI 	logerr(__func__);
5468d36e1dfSRoy Marples #ifndef HAVE_OPEN_MEMSTREAM
5478d36e1dfSRoy Marples 	if (fp != NULL)
5488d36e1dfSRoy Marples 		fclose(fp);
5498d36e1dfSRoy Marples #endif
5507827cba2SAaron LI 	return -1;
5517827cba2SAaron LI }
5527827cba2SAaron LI 
5537827cba2SAaron LI static int
5548d36e1dfSRoy Marples send_interface1(struct fd_list *fd, const struct interface *ifp,
5557827cba2SAaron LI     const char *reason)
5567827cba2SAaron LI {
5578d36e1dfSRoy Marples 	struct dhcpcd_ctx *ctx = ifp->ctx;
5588d36e1dfSRoy Marples 	long len;
5597827cba2SAaron LI 
5606e63cc1fSRoy Marples 	len = make_env(ifp->ctx, ifp, reason);
5618d36e1dfSRoy Marples 	if (len == -1)
5627827cba2SAaron LI 		return -1;
563*7f8103cdSRoy Marples 	return control_queue(fd, ctx->script_buf, (size_t)len);
5647827cba2SAaron LI }
5657827cba2SAaron LI 
5667827cba2SAaron LI int
5676e63cc1fSRoy Marples send_interface(struct fd_list *fd, const struct interface *ifp, int af)
5687827cba2SAaron LI {
5697827cba2SAaron LI 	int retval = 0;
5707827cba2SAaron LI #ifdef INET
5717827cba2SAaron LI 	const struct dhcp_state *d;
5727827cba2SAaron LI #endif
5738d36e1dfSRoy Marples #ifdef DHCP6
5747827cba2SAaron LI 	const struct dhcp6_state *d6;
5757827cba2SAaron LI #endif
5767827cba2SAaron LI 
5776e63cc1fSRoy Marples #ifndef AF_LINK
5786e63cc1fSRoy Marples #define	AF_LINK	AF_PACKET
5796e63cc1fSRoy Marples #endif
5806e63cc1fSRoy Marples 
5816e63cc1fSRoy Marples 	if (af == AF_UNSPEC || af == AF_LINK) {
5826e63cc1fSRoy Marples 		const char *reason;
5836e63cc1fSRoy Marples 
5847827cba2SAaron LI 		switch (ifp->carrier) {
5857827cba2SAaron LI 		case LINK_UP:
5867827cba2SAaron LI 			reason = "CARRIER";
5877827cba2SAaron LI 			break;
5887827cba2SAaron LI 		case LINK_DOWN:
5898d36e1dfSRoy Marples 		case LINK_DOWN_IFFUP:
5907827cba2SAaron LI 			reason = "NOCARRIER";
5917827cba2SAaron LI 			break;
5927827cba2SAaron LI 		default:
5937827cba2SAaron LI 			reason = "UNKNOWN";
5947827cba2SAaron LI 			break;
5957827cba2SAaron LI 		}
5966e63cc1fSRoy Marples 		if (fd != NULL) {
5977827cba2SAaron LI 			if (send_interface1(fd, ifp, reason) == -1)
5987827cba2SAaron LI 				retval = -1;
5996e63cc1fSRoy Marples 		} else
6006e63cc1fSRoy Marples 			retval++;
6016e63cc1fSRoy Marples 	}
6026e63cc1fSRoy Marples 
6037827cba2SAaron LI #ifdef INET
6046e63cc1fSRoy Marples 	if (af == AF_UNSPEC || af == AF_INET) {
6057827cba2SAaron LI 		if (D_STATE_RUNNING(ifp)) {
6067827cba2SAaron LI 			d = D_CSTATE(ifp);
6076e63cc1fSRoy Marples 			if (fd != NULL) {
6087827cba2SAaron LI 				if (send_interface1(fd, ifp, d->reason) == -1)
6097827cba2SAaron LI 					retval = -1;
6106e63cc1fSRoy Marples 			} else
6116e63cc1fSRoy Marples 				retval++;
6127827cba2SAaron LI 		}
6137827cba2SAaron LI #ifdef IPV4LL
6147827cba2SAaron LI 		if (IPV4LL_STATE_RUNNING(ifp)) {
6156e63cc1fSRoy Marples 			if (fd != NULL) {
6167827cba2SAaron LI 				if (send_interface1(fd, ifp, "IPV4LL") == -1)
6177827cba2SAaron LI 					retval = -1;
6186e63cc1fSRoy Marples 			} else
6196e63cc1fSRoy Marples 				retval++;
6207827cba2SAaron LI 		}
6217827cba2SAaron LI #endif
6226e63cc1fSRoy Marples 	}
6237827cba2SAaron LI #endif
6247827cba2SAaron LI 
6257827cba2SAaron LI #ifdef INET6
6266e63cc1fSRoy Marples 	if (af == AF_UNSPEC || af == AF_INET6) {
6277827cba2SAaron LI 		if (IPV6_STATE_RUNNING(ifp)) {
6286e63cc1fSRoy Marples 			if (fd != NULL) {
6297827cba2SAaron LI 				if (send_interface1(fd, ifp, "STATIC6") == -1)
6307827cba2SAaron LI 					retval = -1;
6316e63cc1fSRoy Marples 			} else
6326e63cc1fSRoy Marples 				retval++;
6337827cba2SAaron LI 		}
6347827cba2SAaron LI 		if (RS_STATE_RUNNING(ifp)) {
6356e63cc1fSRoy Marples 			if (fd != NULL) {
6366e63cc1fSRoy Marples 				if (send_interface1(fd, ifp,
6376e63cc1fSRoy Marples 				    "ROUTERADVERT") == -1)
6387827cba2SAaron LI 					retval = -1;
6396e63cc1fSRoy Marples 			} else
6406e63cc1fSRoy Marples 				retval++;
6417827cba2SAaron LI 		}
6428d36e1dfSRoy Marples #ifdef DHCP6
6437827cba2SAaron LI 		if (D6_STATE_RUNNING(ifp)) {
6447827cba2SAaron LI 			d6 = D6_CSTATE(ifp);
6456e63cc1fSRoy Marples 			if (fd != NULL) {
6467827cba2SAaron LI 				if (send_interface1(fd, ifp, d6->reason) == -1)
6477827cba2SAaron LI 					retval = -1;
6486e63cc1fSRoy Marples 			} else
6496e63cc1fSRoy Marples 				retval++;
6507827cba2SAaron LI 		}
6517827cba2SAaron LI #endif
6526e63cc1fSRoy Marples 	}
6538d36e1dfSRoy Marples #endif
6547827cba2SAaron LI 
6557827cba2SAaron LI 	return retval;
6567827cba2SAaron LI }
6577827cba2SAaron LI 
6586e63cc1fSRoy Marples static int
6596e63cc1fSRoy Marples script_run(struct dhcpcd_ctx *ctx, char **argv)
6607827cba2SAaron LI {
6617827cba2SAaron LI 	pid_t pid;
6627827cba2SAaron LI 	int status = 0;
6637827cba2SAaron LI 
664280986e4SRoy Marples 	pid = script_exec(argv, ctx->script_env);
6657827cba2SAaron LI 	if (pid == -1)
6667827cba2SAaron LI 		logerr("%s: %s", __func__, argv[0]);
6677827cba2SAaron LI 	else if (pid != 0) {
6687827cba2SAaron LI 		/* Wait for the script to finish */
6697827cba2SAaron LI 		while (waitpid(pid, &status, 0) == -1) {
6707827cba2SAaron LI 			if (errno != EINTR) {
6717827cba2SAaron LI 				logerr("%s: waitpid", __func__);
6727827cba2SAaron LI 				status = 0;
6737827cba2SAaron LI 				break;
6747827cba2SAaron LI 			}
6757827cba2SAaron LI 		}
6767827cba2SAaron LI 		if (WIFEXITED(status)) {
6777827cba2SAaron LI 			if (WEXITSTATUS(status))
6787827cba2SAaron LI 				logerrx("%s: %s: WEXITSTATUS %d",
6797827cba2SAaron LI 				    __func__, argv[0], WEXITSTATUS(status));
6807827cba2SAaron LI 		} else if (WIFSIGNALED(status))
6817827cba2SAaron LI 			logerrx("%s: %s: %s",
6827827cba2SAaron LI 			    __func__, argv[0], strsignal(WTERMSIG(status)));
6837827cba2SAaron LI 	}
6847827cba2SAaron LI 
6856e63cc1fSRoy Marples 	return WEXITSTATUS(status);
6866e63cc1fSRoy Marples }
6876e63cc1fSRoy Marples 
6886e63cc1fSRoy Marples int
689d4fb1e02SRoy Marples script_dump(const char *env, size_t len)
690d4fb1e02SRoy Marples {
691d4fb1e02SRoy Marples 	const char *ep = env + len;
692d4fb1e02SRoy Marples 
693d4fb1e02SRoy Marples 	if (len == 0)
694d4fb1e02SRoy Marples 		return 0;
695d4fb1e02SRoy Marples 
696d4fb1e02SRoy Marples 	if (*(ep - 1) != '\0') {
697d4fb1e02SRoy Marples 		errno = EINVAL;
698d4fb1e02SRoy Marples 		return -1;
699d4fb1e02SRoy Marples 	}
700d4fb1e02SRoy Marples 
701d4fb1e02SRoy Marples 	for (; env < ep; env += strlen(env) + 1) {
702d4fb1e02SRoy Marples 		if (strncmp(env, "new_", 4) == 0)
703d4fb1e02SRoy Marples 			env += 4;
704d4fb1e02SRoy Marples 		printf("%s\n", env);
705d4fb1e02SRoy Marples 	}
706d4fb1e02SRoy Marples 	return 0;
707d4fb1e02SRoy Marples }
708d4fb1e02SRoy Marples 
709d4fb1e02SRoy Marples int
7106e63cc1fSRoy Marples script_runreason(const struct interface *ifp, const char *reason)
7116e63cc1fSRoy Marples {
7126e63cc1fSRoy Marples 	struct dhcpcd_ctx *ctx = ifp->ctx;
7136e63cc1fSRoy Marples 	char *argv[2];
7146e63cc1fSRoy Marples 	int status = 0;
7156e63cc1fSRoy Marples 	struct fd_list *fd;
716d4fb1e02SRoy Marples 	long buflen;
7176e63cc1fSRoy Marples 
718d4fb1e02SRoy Marples 	if (ctx->script == NULL &&
7196e63cc1fSRoy Marples 	    TAILQ_FIRST(&ifp->ctx->control_fds) == NULL)
7206e63cc1fSRoy Marples 		return 0;
7216e63cc1fSRoy Marples 
7226e63cc1fSRoy Marples 	/* Make our env */
723d4fb1e02SRoy Marples 	if ((buflen = make_env(ifp->ctx, ifp, reason)) == -1) {
7246e63cc1fSRoy Marples 		logerr(__func__);
7256e63cc1fSRoy Marples 		return -1;
7266e63cc1fSRoy Marples 	}
7276e63cc1fSRoy Marples 
728d4fb1e02SRoy Marples 	if (strncmp(reason, "DUMP", 4) == 0)
729d4fb1e02SRoy Marples 		return script_dump(ctx->script_buf, (size_t)buflen);
730d4fb1e02SRoy Marples 
731d4fb1e02SRoy Marples 	if (ctx->script == NULL)
7326e63cc1fSRoy Marples 		goto send_listeners;
7336e63cc1fSRoy Marples 
734d4fb1e02SRoy Marples 	argv[0] = ctx->script;
7356e63cc1fSRoy Marples 	argv[1] = NULL;
7366e63cc1fSRoy Marples 	logdebugx("%s: executing `%s' %s", ifp->name, argv[0], reason);
7376e63cc1fSRoy Marples 
7386e63cc1fSRoy Marples #ifdef PRIVSEP
7396e63cc1fSRoy Marples 	if (ctx->options & DHCPCD_PRIVSEP) {
740d4fb1e02SRoy Marples 		if (ps_root_script(ctx,
7416e63cc1fSRoy Marples 		    ctx->script_buf, ctx->script_buflen) == -1)
7426e63cc1fSRoy Marples 			logerr(__func__);
7436e63cc1fSRoy Marples 		goto send_listeners;
7446e63cc1fSRoy Marples 	}
7456e63cc1fSRoy Marples #endif
7466e63cc1fSRoy Marples 
747d4fb1e02SRoy Marples 	script_run(ctx, argv);
7486e63cc1fSRoy Marples 
7497827cba2SAaron LI send_listeners:
7507827cba2SAaron LI 	/* Send to our listeners */
7517827cba2SAaron LI 	status = 0;
7528d36e1dfSRoy Marples 	TAILQ_FOREACH(fd, &ctx->control_fds, next) {
7537827cba2SAaron LI 		if (!(fd->flags & FD_LISTEN))
7547827cba2SAaron LI 			continue;
755*7f8103cdSRoy Marples 		if (control_queue(fd, ctx->script_buf, ctx->script_buflen)== -1)
7567827cba2SAaron LI 			logerr("%s: control_queue", __func__);
7577827cba2SAaron LI 		else
7587827cba2SAaron LI 			status = 1;
7597827cba2SAaron LI 	}
7607827cba2SAaron LI 
7616e63cc1fSRoy Marples 	return status;
7627827cba2SAaron LI }
763