xref: /dflybsd-src/contrib/dhcpcd/src/script.c (revision 6e63cc1f9bd88f540fc1d20551f8a802f663f025)
1*6e63cc1fSRoy Marples /* stSPDX-License-Identifier: BSD-2-Clause */
27827cba2SAaron LI /*
37827cba2SAaron LI  * dhcpcd - DHCP client daemon
4*6e63cc1fSRoy 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>
39*6e63cc1fSRoy 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"
51*6e63cc1fSRoy 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"
57*6e63cc1fSRoy 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 
86*6e63cc1fSRoy Marples pid_t
87b9ccd228SRoy Marples script_exec(const struct dhcpcd_ctx *ctx, 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);
1087827cba2SAaron LI 	for (i = 0; i < dhcpcd_signals_len; i++)
1097827cba2SAaron LI 		sigaddset(&defsigs, dhcpcd_signals[i]);
1107827cba2SAaron LI 	posix_spawnattr_setsigdefault(&attr, &defsigs);
1117827cba2SAaron LI 	posix_spawnattr_setsigmask(&attr, &ctx->sigset);
1127827cba2SAaron LI #endif
1137827cba2SAaron LI 	errno = 0;
1147827cba2SAaron LI 	r = posix_spawn(&pid, argv[0], NULL, &attr, argv, env);
1157827cba2SAaron LI 	posix_spawnattr_destroy(&attr);
1167827cba2SAaron LI 	if (r) {
1177827cba2SAaron LI 		errno = r;
1187827cba2SAaron LI 		return -1;
1197827cba2SAaron LI 	}
1207827cba2SAaron LI 	return pid;
1217827cba2SAaron LI }
1227827cba2SAaron LI 
1237827cba2SAaron LI #ifdef INET
1247827cba2SAaron LI static int
1258d36e1dfSRoy Marples append_config(FILE *fp, const char *prefix, const char *const *config)
1267827cba2SAaron LI {
1278d36e1dfSRoy Marples 	size_t i;
1287827cba2SAaron LI 
1297827cba2SAaron LI 	if (config == NULL)
1307827cba2SAaron LI 		return 0;
1317827cba2SAaron LI 
1328d36e1dfSRoy Marples 	/* Do we need to replace existing config rather than append? */
1337827cba2SAaron LI 	for (i = 0; config[i] != NULL; i++) {
1348d36e1dfSRoy Marples 		if (efprintf(fp, "%s_%s", prefix, config[i]) == -1)
1357827cba2SAaron LI 			return -1;
1367827cba2SAaron LI 	}
1378d36e1dfSRoy Marples 	return 1;
1387827cba2SAaron LI }
1397827cba2SAaron LI 
1408d36e1dfSRoy Marples #endif
1418d36e1dfSRoy Marples 
1427827cba2SAaron LI #define	PROTO_LINK	0
1437827cba2SAaron LI #define	PROTO_DHCP	1
1447827cba2SAaron LI #define	PROTO_IPV4LL	2
1457827cba2SAaron LI #define	PROTO_RA	3
1467827cba2SAaron LI #define	PROTO_DHCP6	4
1477827cba2SAaron LI #define	PROTO_STATIC6	5
1487827cba2SAaron LI static const char *protocols[] = {
1497827cba2SAaron LI 	"link",
1507827cba2SAaron LI 	"dhcp",
1517827cba2SAaron LI 	"ipv4ll",
1527827cba2SAaron LI 	"ra",
1537827cba2SAaron LI 	"dhcp6",
1547827cba2SAaron LI 	"static6"
1557827cba2SAaron LI };
1567827cba2SAaron LI 
1578d36e1dfSRoy Marples int
1588d36e1dfSRoy Marples efprintf(FILE *fp, const char *fmt, ...)
1597827cba2SAaron LI {
1608d36e1dfSRoy Marples 	va_list args;
1618d36e1dfSRoy Marples 	int r;
1628d36e1dfSRoy Marples 
1638d36e1dfSRoy Marples 	va_start(args, fmt);
1648d36e1dfSRoy Marples 	r = vfprintf(fp, fmt, args);
1658d36e1dfSRoy Marples 	va_end(args);
1668d36e1dfSRoy Marples 	if (r == -1)
1678d36e1dfSRoy Marples 		return -1;
1688d36e1dfSRoy Marples 	/* Write a trailing NULL so we can easily create env strings. */
1698d36e1dfSRoy Marples 	if (fputc('\0', fp) == EOF)
1708d36e1dfSRoy Marples 		return -1;
1718d36e1dfSRoy Marples 	return r;
1728d36e1dfSRoy Marples }
1738d36e1dfSRoy Marples 
174*6e63cc1fSRoy Marples char **
175b9ccd228SRoy Marples script_buftoenv(struct dhcpcd_ctx *ctx, char *buf, size_t len)
176b9ccd228SRoy Marples {
177b9ccd228SRoy Marples 	char **env, **envp, *bufp, *endp;
178b9ccd228SRoy Marples 	size_t nenv;
179b9ccd228SRoy Marples 
180b9ccd228SRoy Marples 	/* Count the terminated env strings.
181b9ccd228SRoy Marples 	 * Assert that the terminations are correct. */
182b9ccd228SRoy Marples 	nenv = 0;
183b9ccd228SRoy Marples 	endp = buf + len;
184b9ccd228SRoy Marples 	for (bufp = buf; bufp < endp; bufp++) {
185b9ccd228SRoy Marples 		if (*bufp == '\0') {
186b9ccd228SRoy Marples #ifndef NDEBUG
187b9ccd228SRoy Marples 			if (bufp + 1 < endp)
188b9ccd228SRoy Marples 				assert(*(bufp + 1) != '\0');
189b9ccd228SRoy Marples #endif
190b9ccd228SRoy Marples 			nenv++;
191b9ccd228SRoy Marples 		}
192b9ccd228SRoy Marples 	}
193b9ccd228SRoy Marples 	assert(*(bufp - 1) == '\0');
194b9ccd228SRoy Marples 
195b9ccd228SRoy Marples 	if (ctx->script_envlen < nenv) {
196b9ccd228SRoy Marples 		env = reallocarray(ctx->script_env, nenv + 1, sizeof(*env));
197b9ccd228SRoy Marples 		if (env == NULL)
198b9ccd228SRoy Marples 			return NULL;
199b9ccd228SRoy Marples 		ctx->script_env = env;
200b9ccd228SRoy Marples 		ctx->script_envlen = nenv;
201b9ccd228SRoy Marples 	}
202b9ccd228SRoy Marples 
203b9ccd228SRoy Marples 	bufp = buf;
204b9ccd228SRoy Marples 	envp = ctx->script_env;
205b9ccd228SRoy Marples 	*envp++ = bufp++;
206b9ccd228SRoy Marples 	endp--; /* Avoid setting the last \0 to an invalid pointer */
207b9ccd228SRoy Marples 	for (; bufp < endp; bufp++) {
208b9ccd228SRoy Marples 		if (*bufp == '\0')
209b9ccd228SRoy Marples 			*envp++ = bufp + 1;
210b9ccd228SRoy Marples 	}
211b9ccd228SRoy Marples 	*envp = NULL;
212b9ccd228SRoy Marples 
213b9ccd228SRoy Marples 	return ctx->script_env;
214b9ccd228SRoy Marples }
215b9ccd228SRoy Marples 
2168d36e1dfSRoy Marples static long
217*6e63cc1fSRoy Marples make_env(struct dhcpcd_ctx *ctx, const struct interface *ifp,
218*6e63cc1fSRoy Marples     const char *reason)
2198d36e1dfSRoy Marples {
2208d36e1dfSRoy Marples 	FILE *fp;
2218d36e1dfSRoy Marples 	long buf_pos, i;
222b9ccd228SRoy Marples 	char *path;
2238d36e1dfSRoy Marples 	int protocol = PROTO_LINK;
224*6e63cc1fSRoy Marples 	const struct if_options *ifo;
2257827cba2SAaron LI 	const struct interface *ifp2;
2267827cba2SAaron LI 	int af;
2277827cba2SAaron LI #ifdef INET
2287827cba2SAaron LI 	const struct dhcp_state *state;
2297827cba2SAaron LI #ifdef IPV4LL
2307827cba2SAaron LI 	const struct ipv4ll_state *istate;
2317827cba2SAaron LI #endif
2327827cba2SAaron LI #endif
2338d36e1dfSRoy Marples #ifdef DHCP6
2347827cba2SAaron LI 	const struct dhcp6_state *d6_state;
2357827cba2SAaron LI #endif
2367827cba2SAaron LI 
2378d36e1dfSRoy Marples #ifdef HAVE_OPEN_MEMSTREAM
2388d36e1dfSRoy Marples 	if (ctx->script_fp == NULL) {
2398d36e1dfSRoy Marples 		fp = open_memstream(&ctx->script_buf, &ctx->script_buflen);
2408d36e1dfSRoy Marples 		if (fp == NULL)
2418d36e1dfSRoy Marples 			goto eexit;
2428d36e1dfSRoy Marples 		ctx->script_fp = fp;
2438d36e1dfSRoy Marples 	} else {
2448d36e1dfSRoy Marples 		fp = ctx->script_fp;
2458d36e1dfSRoy Marples 		rewind(fp);
2468d36e1dfSRoy Marples 	}
2478d36e1dfSRoy Marples #else
2488d36e1dfSRoy Marples 	char tmpfile[] = "/tmp/dhcpcd-script-env-XXXXXX";
2498d36e1dfSRoy Marples 	int tmpfd;
2508d36e1dfSRoy Marples 
2518d36e1dfSRoy Marples 	fp = NULL;
2528d36e1dfSRoy Marples 	tmpfd = mkstemp(tmpfile);
2538d36e1dfSRoy Marples 	if (tmpfd == -1)
2548d36e1dfSRoy Marples 		goto eexit;
2558d36e1dfSRoy Marples 	unlink(tmpfile);
2568d36e1dfSRoy Marples 	fp = fdopen(tmpfd, "w+");
2578d36e1dfSRoy Marples 	if (fp == NULL) {
2588d36e1dfSRoy Marples 		close(tmpfd);
2598d36e1dfSRoy Marples 		goto eexit;
2608d36e1dfSRoy Marples 	}
2618d36e1dfSRoy Marples #endif
2628d36e1dfSRoy Marples 
263*6e63cc1fSRoy Marples 	/* Needed for scripts */
264*6e63cc1fSRoy Marples 	path = getenv("PATH");
265*6e63cc1fSRoy Marples 	if (efprintf(fp, "PATH=%s", path == NULL ? DEFAULT_PATH:path) == -1)
266*6e63cc1fSRoy Marples 		goto eexit;
267*6e63cc1fSRoy Marples 	if (efprintf(fp, "reason=%s", reason) == -1)
268*6e63cc1fSRoy Marples 		goto eexit;
269*6e63cc1fSRoy Marples 	if (efprintf(fp, "pid=%d", getpid()) == -1)
270*6e63cc1fSRoy Marples 		goto eexit;
271*6e63cc1fSRoy Marples 
272*6e63cc1fSRoy Marples #ifdef PRIVSEP
273*6e63cc1fSRoy Marples 	if (ctx->options & DHCPCD_PRIVSEP && ctx->ps_user != NULL) {
274*6e63cc1fSRoy Marples 		if (efprintf(fp, "chroot=%s", ctx->ps_user->pw_dir) == -1)
275*6e63cc1fSRoy Marples 			goto eexit;
276*6e63cc1fSRoy Marples 	}
277*6e63cc1fSRoy Marples 	if (strcmp(reason, "CHROOT") == 0)
278*6e63cc1fSRoy Marples 		goto make;
279*6e63cc1fSRoy Marples #endif
280*6e63cc1fSRoy Marples 
281*6e63cc1fSRoy Marples 	ifo = ifp->options;
2827827cba2SAaron LI #ifdef INET
2837827cba2SAaron LI 	state = D_STATE(ifp);
2847827cba2SAaron LI #ifdef IPV4LL
2857827cba2SAaron LI 	istate = IPV4LL_CSTATE(ifp);
2867827cba2SAaron LI #endif
2877827cba2SAaron LI #endif
2888d36e1dfSRoy Marples #ifdef DHCP6
2897827cba2SAaron LI 	d6_state = D6_CSTATE(ifp);
2907827cba2SAaron LI #endif
2917827cba2SAaron LI 	if (strcmp(reason, "TEST") == 0) {
292*6e63cc1fSRoy Marples 		if (1 == 2) {
293*6e63cc1fSRoy Marples 			/* This space left intentionally blank
294*6e63cc1fSRoy Marples 			 * as all the below statements are optional. */
295*6e63cc1fSRoy Marples 		}
2967827cba2SAaron LI #ifdef INET6
2978d36e1dfSRoy Marples #ifdef DHCP6
2987827cba2SAaron LI 		else if (d6_state && d6_state->new)
2997827cba2SAaron LI 			protocol = PROTO_DHCP6;
3008d36e1dfSRoy Marples #endif
3017827cba2SAaron LI 		else if (ipv6nd_hasra(ifp))
3027827cba2SAaron LI 			protocol = PROTO_RA;
3037827cba2SAaron LI #endif
3047827cba2SAaron LI #ifdef INET
3057827cba2SAaron LI #ifdef IPV4LL
3067827cba2SAaron LI 		else if (istate && istate->addr != NULL)
3077827cba2SAaron LI 			protocol = PROTO_IPV4LL;
3087827cba2SAaron LI #endif
3097827cba2SAaron LI 		else
3107827cba2SAaron LI 			protocol = PROTO_DHCP;
3117827cba2SAaron LI #endif
3127827cba2SAaron LI 	}
3137827cba2SAaron LI #ifdef INET6
3147827cba2SAaron LI 	else if (strcmp(reason, "STATIC6") == 0)
3157827cba2SAaron LI 		protocol = PROTO_STATIC6;
3168d36e1dfSRoy Marples #ifdef DHCP6
3177827cba2SAaron LI 	else if (reason[strlen(reason) - 1] == '6')
3187827cba2SAaron LI 		protocol = PROTO_DHCP6;
3198d36e1dfSRoy Marples #endif
3207827cba2SAaron LI 	else if (strcmp(reason, "ROUTERADVERT") == 0)
3217827cba2SAaron LI 		protocol = PROTO_RA;
3227827cba2SAaron LI #endif
3237827cba2SAaron LI 	else if (strcmp(reason, "PREINIT") == 0 ||
3247827cba2SAaron LI 	    strcmp(reason, "CARRIER") == 0 ||
3257827cba2SAaron LI 	    strcmp(reason, "NOCARRIER") == 0 ||
3267827cba2SAaron LI 	    strcmp(reason, "UNKNOWN") == 0 ||
3277827cba2SAaron LI 	    strcmp(reason, "DEPARTED") == 0 ||
3287827cba2SAaron LI 	    strcmp(reason, "STOPPED") == 0)
3297827cba2SAaron LI 		protocol = PROTO_LINK;
3307827cba2SAaron LI #ifdef INET
3317827cba2SAaron LI #ifdef IPV4LL
3327827cba2SAaron LI 	else if (strcmp(reason, "IPV4LL") == 0)
3337827cba2SAaron LI 		protocol = PROTO_IPV4LL;
3347827cba2SAaron LI #endif
3357827cba2SAaron LI 	else
3367827cba2SAaron LI 		protocol = PROTO_DHCP;
3377827cba2SAaron LI #endif
3387827cba2SAaron LI 
3398d36e1dfSRoy Marples 
3408d36e1dfSRoy Marples 	if (efprintf(fp, "interface=%s", ifp->name) == -1)
3418d36e1dfSRoy Marples 		goto eexit;
3427827cba2SAaron LI 	if (ifp->ctx->options & DHCPCD_DUMPLEASE)
3437827cba2SAaron LI 		goto dumplease;
3448d36e1dfSRoy Marples 	if (efprintf(fp, "ifcarrier=%s",
3457827cba2SAaron LI 	    ifp->carrier == LINK_UNKNOWN ? "unknown" :
3468d36e1dfSRoy Marples 	    ifp->carrier == LINK_UP ? "up" : "down") == -1)
3478d36e1dfSRoy Marples 		goto eexit;
3488d36e1dfSRoy Marples 	if (efprintf(fp, "ifmetric=%d", ifp->metric) == -1)
3498d36e1dfSRoy Marples 		goto eexit;
3508d36e1dfSRoy Marples 	if (efprintf(fp, "ifwireless=%d", ifp->wireless) == -1)
3518d36e1dfSRoy Marples 		goto eexit;
3528d36e1dfSRoy Marples 	if (efprintf(fp, "ifflags=%u", ifp->flags) == -1)
3538d36e1dfSRoy Marples 		goto eexit;
3548d36e1dfSRoy Marples 	if (efprintf(fp, "ifmtu=%d", if_getmtu(ifp)) == -1)
3558d36e1dfSRoy Marples 		goto eexit;
3568d36e1dfSRoy Marples 
3578d36e1dfSRoy Marples 	if (fprintf(fp, "interface_order=") == -1)
3588d36e1dfSRoy Marples 		goto eexit;
3597827cba2SAaron LI 	TAILQ_FOREACH(ifp2, ifp->ctx->ifaces, next) {
3608d36e1dfSRoy Marples 		if (ifp2 != TAILQ_FIRST(ifp->ctx->ifaces)) {
3618d36e1dfSRoy Marples 			if (fputc(' ', fp) == EOF)
3628d36e1dfSRoy Marples 				return -1;
3637827cba2SAaron LI 		}
3648d36e1dfSRoy Marples 		if (fprintf(fp, "%s", ifp2->name) == -1)
3658d36e1dfSRoy Marples 			return -1;
3667827cba2SAaron LI 	}
3678d36e1dfSRoy Marples 	if (fputc('\0', fp) == EOF)
3688d36e1dfSRoy Marples 		return -1;
3698d36e1dfSRoy Marples 
3707827cba2SAaron LI 	if (strcmp(reason, "STOPPED") == 0) {
3718d36e1dfSRoy Marples 		if (efprintf(fp, "if_up=false") == -1)
3728d36e1dfSRoy Marples 			goto eexit;
3738d36e1dfSRoy Marples 		if (efprintf(fp, "if_down=%s",
3748d36e1dfSRoy Marples 		    ifo->options & DHCPCD_RELEASE ? "true" : "false") == -1)
3758d36e1dfSRoy Marples 			goto eexit;
3767827cba2SAaron LI 	} else if (strcmp(reason, "TEST") == 0 ||
3777827cba2SAaron LI 	    strcmp(reason, "PREINIT") == 0 ||
3787827cba2SAaron LI 	    strcmp(reason, "CARRIER") == 0 ||
3797827cba2SAaron LI 	    strcmp(reason, "UNKNOWN") == 0)
3807827cba2SAaron LI 	{
3818d36e1dfSRoy Marples 		if (efprintf(fp, "if_up=false") == -1)
3828d36e1dfSRoy Marples 			goto eexit;
3838d36e1dfSRoy Marples 		if (efprintf(fp, "if_down=false") == -1)
3848d36e1dfSRoy Marples 			goto eexit;
3857827cba2SAaron LI 	} else if (1 == 2 /* appease ifdefs */
3867827cba2SAaron LI #ifdef INET
3877827cba2SAaron LI 	    || (protocol == PROTO_DHCP && state && state->new)
3887827cba2SAaron LI #ifdef IPV4LL
3897827cba2SAaron LI 	    || (protocol == PROTO_IPV4LL && IPV4LL_STATE_RUNNING(ifp))
3907827cba2SAaron LI #endif
3917827cba2SAaron LI #endif
3927827cba2SAaron LI #ifdef INET6
3937827cba2SAaron LI 	    || (protocol == PROTO_STATIC6 && IPV6_STATE_RUNNING(ifp))
3948d36e1dfSRoy Marples #ifdef DHCP6
3957827cba2SAaron LI 	    || (protocol == PROTO_DHCP6 && d6_state && d6_state->new)
3968d36e1dfSRoy Marples #endif
3977827cba2SAaron LI 	    || (protocol == PROTO_RA && ipv6nd_hasra(ifp))
3987827cba2SAaron LI #endif
3997827cba2SAaron LI 	    )
4007827cba2SAaron LI 	{
4018d36e1dfSRoy Marples 		if (efprintf(fp, "if_up=true") == -1)
4028d36e1dfSRoy Marples 			goto eexit;
4038d36e1dfSRoy Marples 		if (efprintf(fp, "if_down=false") == -1)
4048d36e1dfSRoy Marples 			goto eexit;
4057827cba2SAaron LI 	} else {
4068d36e1dfSRoy Marples 		if (efprintf(fp, "if_up=false") == -1)
4078d36e1dfSRoy Marples 			goto eexit;
4088d36e1dfSRoy Marples 		if (efprintf(fp, "if_down=true") == -1)
4098d36e1dfSRoy Marples 			goto eexit;
4107827cba2SAaron LI 	}
4117827cba2SAaron LI 	if (protocols[protocol] != NULL) {
4128d36e1dfSRoy Marples 		if (efprintf(fp, "protocol=%s", protocols[protocol]) == -1)
4137827cba2SAaron LI 			goto eexit;
4147827cba2SAaron LI 	}
4157827cba2SAaron LI 	if ((af = dhcpcd_ifafwaiting(ifp)) != AF_MAX) {
4168d36e1dfSRoy Marples 		if (efprintf(fp, "if_afwaiting=%d", af) == -1)
4178d36e1dfSRoy Marples 			goto eexit;
4187827cba2SAaron LI 	}
4197827cba2SAaron LI 	if ((af = dhcpcd_afwaiting(ifp->ctx)) != AF_MAX) {
4207827cba2SAaron LI 		TAILQ_FOREACH(ifp2, ifp->ctx->ifaces, next) {
4217827cba2SAaron LI 			if ((af = dhcpcd_ifafwaiting(ifp2)) != AF_MAX)
4227827cba2SAaron LI 				break;
4237827cba2SAaron LI 		}
4247827cba2SAaron LI 	}
4257827cba2SAaron LI 	if (af != AF_MAX) {
4268d36e1dfSRoy Marples 		if (efprintf(fp, "af_waiting=%d", af) == -1)
4278d36e1dfSRoy Marples 			goto eexit;
4287827cba2SAaron LI 	}
4297827cba2SAaron LI 	if (ifo->options & DHCPCD_DEBUG) {
4308d36e1dfSRoy Marples 		if (efprintf(fp, "syslog_debug=true") == -1)
4318d36e1dfSRoy Marples 			goto eexit;
4327827cba2SAaron LI 	}
433b9ccd228SRoy Marples 	if (*ifp->profile != '\0') {
4348d36e1dfSRoy Marples 		if (efprintf(fp, "profile=%s", ifp->profile) == -1)
4358d36e1dfSRoy Marples 			goto eexit;
4367827cba2SAaron LI 	}
4377827cba2SAaron LI 	if (ifp->wireless) {
4388d36e1dfSRoy Marples 		char pssid[IF_SSIDLEN * 4];
4397827cba2SAaron LI 
4408d36e1dfSRoy Marples 		if (print_string(pssid, sizeof(pssid), OT_ESCSTRING,
4418d36e1dfSRoy Marples 		    ifp->ssid, ifp->ssid_len) != -1)
4428d36e1dfSRoy Marples 		{
4438d36e1dfSRoy Marples 			if (efprintf(fp, "ifssid=%s", pssid) == -1)
4448d36e1dfSRoy Marples 				goto eexit;
4457827cba2SAaron LI 		}
4467827cba2SAaron LI 	}
4477827cba2SAaron LI #ifdef INET
4487827cba2SAaron LI 	if (protocol == PROTO_DHCP && state && state->old) {
4498d36e1dfSRoy Marples 		if (dhcp_env(fp, "old", ifp,
4508d36e1dfSRoy Marples 		    state->old, state->old_len) == -1)
4517827cba2SAaron LI 			goto eexit;
4528d36e1dfSRoy Marples 		if (append_config(fp, "old",
4537827cba2SAaron LI 		    (const char *const *)ifo->config) == -1)
4547827cba2SAaron LI 			goto eexit;
4557827cba2SAaron LI 	}
4567827cba2SAaron LI #endif
4578d36e1dfSRoy Marples #ifdef DHCP6
4587827cba2SAaron LI 	if (protocol == PROTO_DHCP6 && d6_state && d6_state->old) {
4598d36e1dfSRoy Marples 		if (dhcp6_env(fp, "old", ifp,
4608d36e1dfSRoy Marples 		    d6_state->old, d6_state->old_len) == -1)
4617827cba2SAaron LI 			goto eexit;
4627827cba2SAaron LI 	}
4637827cba2SAaron LI #endif
4647827cba2SAaron LI 
4657827cba2SAaron LI dumplease:
4667827cba2SAaron LI #ifdef INET
4677827cba2SAaron LI #ifdef IPV4LL
468*6e63cc1fSRoy Marples 	if (protocol == PROTO_IPV4LL && istate) {
4698d36e1dfSRoy Marples 		if (ipv4ll_env(fp, istate->down ? "old" : "new", ifp) == -1)
4707827cba2SAaron LI 			goto eexit;
4717827cba2SAaron LI 	}
4727827cba2SAaron LI #endif
4737827cba2SAaron LI 	if (protocol == PROTO_DHCP && state && state->new) {
4748d36e1dfSRoy Marples 		if (dhcp_env(fp, "new", ifp,
4758d36e1dfSRoy Marples 		    state->new, state->new_len) == -1)
4767827cba2SAaron LI 			goto eexit;
4778d36e1dfSRoy Marples 		if (append_config(fp, "new",
4787827cba2SAaron LI 		    (const char *const *)ifo->config) == -1)
4797827cba2SAaron LI 			goto eexit;
4807827cba2SAaron LI 	}
4817827cba2SAaron LI #endif
4827827cba2SAaron LI #ifdef INET6
4837827cba2SAaron LI 	if (protocol == PROTO_STATIC6) {
4848d36e1dfSRoy Marples 		if (ipv6_env(fp, "new", ifp) == -1)
4857827cba2SAaron LI 			goto eexit;
4867827cba2SAaron LI 	}
4878d36e1dfSRoy Marples #ifdef DHCP6
4887827cba2SAaron LI 	if (protocol == PROTO_DHCP6 && D6_STATE_RUNNING(ifp)) {
4898d36e1dfSRoy Marples 		if (dhcp6_env(fp, "new", ifp,
4908d36e1dfSRoy Marples 		    d6_state->new, d6_state->new_len) == -1)
4917827cba2SAaron LI 			goto eexit;
4927827cba2SAaron LI 	}
4938d36e1dfSRoy Marples #endif
4947827cba2SAaron LI 	if (protocol == PROTO_RA) {
4958d36e1dfSRoy Marples 		if (ipv6nd_env(fp, ifp) == -1)
4967827cba2SAaron LI 			goto eexit;
4977827cba2SAaron LI 	}
4987827cba2SAaron LI #endif
4997827cba2SAaron LI 
5007827cba2SAaron LI 	/* Add our base environment */
5017827cba2SAaron LI 	if (ifo->environ) {
5028d36e1dfSRoy Marples 		for (i = 0; ifo->environ[i] != NULL; i++)
5038d36e1dfSRoy Marples 			if (efprintf(fp, "%s", ifo->environ[i]) == -1)
5047827cba2SAaron LI 				goto eexit;
5057827cba2SAaron LI 	}
5067827cba2SAaron LI 
507*6e63cc1fSRoy Marples #ifdef PRIVSEP
508*6e63cc1fSRoy Marples make:
509*6e63cc1fSRoy Marples #endif
5108d36e1dfSRoy Marples 	/* Convert buffer to argv */
5118d36e1dfSRoy Marples 	fflush(fp);
5128d36e1dfSRoy Marples 
5138d36e1dfSRoy Marples 	buf_pos = ftell(fp);
5148d36e1dfSRoy Marples 	if (buf_pos == -1) {
5158d36e1dfSRoy Marples 		logerr(__func__);
5168d36e1dfSRoy Marples 		goto eexit;
5178d36e1dfSRoy Marples 	}
5188d36e1dfSRoy Marples 
5198d36e1dfSRoy Marples #ifndef HAVE_OPEN_MEMSTREAM
5208d36e1dfSRoy Marples 	size_t buf_len = (size_t)buf_pos;
5218d36e1dfSRoy Marples 	if (ctx->script_buflen < buf_len) {
5228d36e1dfSRoy Marples 		char *buf = realloc(ctx->script_buf, buf_len);
5238d36e1dfSRoy Marples 		if (buf == NULL)
5248d36e1dfSRoy Marples 			goto eexit;
5258d36e1dfSRoy Marples 		ctx->script_buf = buf;
5268d36e1dfSRoy Marples 		ctx->script_buflen = buf_len;
5278d36e1dfSRoy Marples 	}
5288d36e1dfSRoy Marples 	rewind(fp);
5298d36e1dfSRoy Marples 	if (fread(ctx->script_buf, sizeof(char), buf_len, fp) != buf_len)
5308d36e1dfSRoy Marples 		goto eexit;
5318d36e1dfSRoy Marples 	fclose(fp);
5328d36e1dfSRoy Marples 	fp = NULL;
5338d36e1dfSRoy Marples #endif
5348d36e1dfSRoy Marples 
535b9ccd228SRoy Marples 	if (script_buftoenv(ctx, ctx->script_buf, (size_t)buf_pos) == NULL)
5368d36e1dfSRoy Marples 		goto eexit;
5378d36e1dfSRoy Marples 
538*6e63cc1fSRoy Marples 	return buf_pos;
5397827cba2SAaron LI 
5407827cba2SAaron LI eexit:
5417827cba2SAaron LI 	logerr(__func__);
5428d36e1dfSRoy Marples #ifndef HAVE_OPEN_MEMSTREAM
5438d36e1dfSRoy Marples 	if (fp != NULL)
5448d36e1dfSRoy Marples 		fclose(fp);
5458d36e1dfSRoy Marples #endif
5467827cba2SAaron LI 	return -1;
5477827cba2SAaron LI }
5487827cba2SAaron LI 
5497827cba2SAaron LI static int
5508d36e1dfSRoy Marples send_interface1(struct fd_list *fd, const struct interface *ifp,
5517827cba2SAaron LI     const char *reason)
5527827cba2SAaron LI {
5538d36e1dfSRoy Marples 	struct dhcpcd_ctx *ctx = ifp->ctx;
5548d36e1dfSRoy Marples 	long len;
5557827cba2SAaron LI 
556*6e63cc1fSRoy Marples 	len = make_env(ifp->ctx, ifp, reason);
5578d36e1dfSRoy Marples 	if (len == -1)
5587827cba2SAaron LI 		return -1;
5598d36e1dfSRoy Marples 	return control_queue(fd, ctx->script_buf, (size_t)len, 1);
5607827cba2SAaron LI }
5617827cba2SAaron LI 
5627827cba2SAaron LI int
563*6e63cc1fSRoy Marples send_interface(struct fd_list *fd, const struct interface *ifp, int af)
5647827cba2SAaron LI {
5657827cba2SAaron LI 	int retval = 0;
5667827cba2SAaron LI #ifdef INET
5677827cba2SAaron LI 	const struct dhcp_state *d;
5687827cba2SAaron LI #endif
5698d36e1dfSRoy Marples #ifdef DHCP6
5707827cba2SAaron LI 	const struct dhcp6_state *d6;
5717827cba2SAaron LI #endif
5727827cba2SAaron LI 
573*6e63cc1fSRoy Marples #ifndef AF_LINK
574*6e63cc1fSRoy Marples #define	AF_LINK	AF_PACKET
575*6e63cc1fSRoy Marples #endif
576*6e63cc1fSRoy Marples 
577*6e63cc1fSRoy Marples 	if (af == AF_UNSPEC || af == AF_LINK) {
578*6e63cc1fSRoy Marples 		const char *reason;
579*6e63cc1fSRoy Marples 
5807827cba2SAaron LI 		switch (ifp->carrier) {
5817827cba2SAaron LI 		case LINK_UP:
5827827cba2SAaron LI 			reason = "CARRIER";
5837827cba2SAaron LI 			break;
5847827cba2SAaron LI 		case LINK_DOWN:
5858d36e1dfSRoy Marples 		case LINK_DOWN_IFFUP:
5867827cba2SAaron LI 			reason = "NOCARRIER";
5877827cba2SAaron LI 			break;
5887827cba2SAaron LI 		default:
5897827cba2SAaron LI 			reason = "UNKNOWN";
5907827cba2SAaron LI 			break;
5917827cba2SAaron LI 		}
592*6e63cc1fSRoy Marples 		if (fd != NULL) {
5937827cba2SAaron LI 			if (send_interface1(fd, ifp, reason) == -1)
5947827cba2SAaron LI 				retval = -1;
595*6e63cc1fSRoy Marples 		} else
596*6e63cc1fSRoy Marples 			retval++;
597*6e63cc1fSRoy Marples 	}
598*6e63cc1fSRoy Marples 
5997827cba2SAaron LI #ifdef INET
600*6e63cc1fSRoy Marples 	if (af == AF_UNSPEC || af == AF_INET) {
6017827cba2SAaron LI 		if (D_STATE_RUNNING(ifp)) {
6027827cba2SAaron LI 			d = D_CSTATE(ifp);
603*6e63cc1fSRoy Marples 			if (fd != NULL) {
6047827cba2SAaron LI 				if (send_interface1(fd, ifp, d->reason) == -1)
6057827cba2SAaron LI 					retval = -1;
606*6e63cc1fSRoy Marples 			} else
607*6e63cc1fSRoy Marples 				retval++;
6087827cba2SAaron LI 		}
6097827cba2SAaron LI #ifdef IPV4LL
6107827cba2SAaron LI 		if (IPV4LL_STATE_RUNNING(ifp)) {
611*6e63cc1fSRoy Marples 			if (fd != NULL) {
6127827cba2SAaron LI 				if (send_interface1(fd, ifp, "IPV4LL") == -1)
6137827cba2SAaron LI 					retval = -1;
614*6e63cc1fSRoy Marples 			} else
615*6e63cc1fSRoy Marples 				retval++;
6167827cba2SAaron LI 		}
6177827cba2SAaron LI #endif
618*6e63cc1fSRoy Marples 	}
6197827cba2SAaron LI #endif
6207827cba2SAaron LI 
6217827cba2SAaron LI #ifdef INET6
622*6e63cc1fSRoy Marples 	if (af == AF_UNSPEC || af == AF_INET6) {
6237827cba2SAaron LI 		if (IPV6_STATE_RUNNING(ifp)) {
624*6e63cc1fSRoy Marples 			if (fd != NULL) {
6257827cba2SAaron LI 				if (send_interface1(fd, ifp, "STATIC6") == -1)
6267827cba2SAaron LI 					retval = -1;
627*6e63cc1fSRoy Marples 			} else
628*6e63cc1fSRoy Marples 				retval++;
6297827cba2SAaron LI 		}
6307827cba2SAaron LI 		if (RS_STATE_RUNNING(ifp)) {
631*6e63cc1fSRoy Marples 			if (fd != NULL) {
632*6e63cc1fSRoy Marples 				if (send_interface1(fd, ifp,
633*6e63cc1fSRoy Marples 				    "ROUTERADVERT") == -1)
6347827cba2SAaron LI 					retval = -1;
635*6e63cc1fSRoy Marples 			} else
636*6e63cc1fSRoy Marples 				retval++;
6377827cba2SAaron LI 		}
6388d36e1dfSRoy Marples #ifdef DHCP6
6397827cba2SAaron LI 		if (D6_STATE_RUNNING(ifp)) {
6407827cba2SAaron LI 			d6 = D6_CSTATE(ifp);
641*6e63cc1fSRoy Marples 			if (fd != NULL) {
6427827cba2SAaron LI 				if (send_interface1(fd, ifp, d6->reason) == -1)
6437827cba2SAaron LI 					retval = -1;
644*6e63cc1fSRoy Marples 			} else
645*6e63cc1fSRoy Marples 				retval++;
6467827cba2SAaron LI 		}
6477827cba2SAaron LI #endif
648*6e63cc1fSRoy Marples 	}
6498d36e1dfSRoy Marples #endif
6507827cba2SAaron LI 
6517827cba2SAaron LI 	return retval;
6527827cba2SAaron LI }
6537827cba2SAaron LI 
654*6e63cc1fSRoy Marples static int
655*6e63cc1fSRoy Marples script_run(struct dhcpcd_ctx *ctx, char **argv)
6567827cba2SAaron LI {
6577827cba2SAaron LI 	pid_t pid;
6587827cba2SAaron LI 	int status = 0;
6597827cba2SAaron LI 
660b9ccd228SRoy Marples 	pid = script_exec(ctx, argv, ctx->script_env);
6617827cba2SAaron LI 	if (pid == -1)
6627827cba2SAaron LI 		logerr("%s: %s", __func__, argv[0]);
6637827cba2SAaron LI 	else if (pid != 0) {
6647827cba2SAaron LI 		/* Wait for the script to finish */
6657827cba2SAaron LI 		while (waitpid(pid, &status, 0) == -1) {
6667827cba2SAaron LI 			if (errno != EINTR) {
6677827cba2SAaron LI 				logerr("%s: waitpid", __func__);
6687827cba2SAaron LI 				status = 0;
6697827cba2SAaron LI 				break;
6707827cba2SAaron LI 			}
6717827cba2SAaron LI 		}
6727827cba2SAaron LI 		if (WIFEXITED(status)) {
6737827cba2SAaron LI 			if (WEXITSTATUS(status))
6747827cba2SAaron LI 				logerrx("%s: %s: WEXITSTATUS %d",
6757827cba2SAaron LI 				    __func__, argv[0], WEXITSTATUS(status));
6767827cba2SAaron LI 		} else if (WIFSIGNALED(status))
6777827cba2SAaron LI 			logerrx("%s: %s: %s",
6787827cba2SAaron LI 			    __func__, argv[0], strsignal(WTERMSIG(status)));
6797827cba2SAaron LI 	}
6807827cba2SAaron LI 
681*6e63cc1fSRoy Marples 	return WEXITSTATUS(status);
682*6e63cc1fSRoy Marples }
683*6e63cc1fSRoy Marples 
684*6e63cc1fSRoy Marples int
685*6e63cc1fSRoy Marples script_runreason(const struct interface *ifp, const char *reason)
686*6e63cc1fSRoy Marples {
687*6e63cc1fSRoy Marples 	struct dhcpcd_ctx *ctx = ifp->ctx;
688*6e63cc1fSRoy Marples 	char *argv[2];
689*6e63cc1fSRoy Marples 	int status = 0;
690*6e63cc1fSRoy Marples 	struct fd_list *fd;
691*6e63cc1fSRoy Marples 
692*6e63cc1fSRoy Marples 	if (ifp->options->script == NULL &&
693*6e63cc1fSRoy Marples 	    TAILQ_FIRST(&ifp->ctx->control_fds) == NULL)
694*6e63cc1fSRoy Marples 		return 0;
695*6e63cc1fSRoy Marples 
696*6e63cc1fSRoy Marples 	/* Make our env */
697*6e63cc1fSRoy Marples 	if (make_env(ifp->ctx, ifp, reason) == -1) {
698*6e63cc1fSRoy Marples 		logerr(__func__);
699*6e63cc1fSRoy Marples 		return -1;
700*6e63cc1fSRoy Marples 	}
701*6e63cc1fSRoy Marples 
702*6e63cc1fSRoy Marples 	if (ifp->options->script == NULL)
703*6e63cc1fSRoy Marples 		goto send_listeners;
704*6e63cc1fSRoy Marples 
705*6e63cc1fSRoy Marples 	argv[0] = ifp->options->script;
706*6e63cc1fSRoy Marples 	argv[1] = NULL;
707*6e63cc1fSRoy Marples 	logdebugx("%s: executing `%s' %s", ifp->name, argv[0], reason);
708*6e63cc1fSRoy Marples 
709*6e63cc1fSRoy Marples #ifdef PRIVSEP
710*6e63cc1fSRoy Marples 	if (ctx->options & DHCPCD_PRIVSEP) {
711*6e63cc1fSRoy Marples 		if (ps_root_script(ifp,
712*6e63cc1fSRoy Marples 		    ctx->script_buf, ctx->script_buflen) == -1)
713*6e63cc1fSRoy Marples 			logerr(__func__);
714*6e63cc1fSRoy Marples 		goto send_listeners;
715*6e63cc1fSRoy Marples 	}
716*6e63cc1fSRoy Marples #endif
717*6e63cc1fSRoy Marples 
718*6e63cc1fSRoy Marples 	status = script_run(ctx, argv);
719*6e63cc1fSRoy Marples 
7207827cba2SAaron LI send_listeners:
7217827cba2SAaron LI 	/* Send to our listeners */
7227827cba2SAaron LI 	status = 0;
7238d36e1dfSRoy Marples 	TAILQ_FOREACH(fd, &ctx->control_fds, next) {
7247827cba2SAaron LI 		if (!(fd->flags & FD_LISTEN))
7257827cba2SAaron LI 			continue;
7268d36e1dfSRoy Marples 		if (control_queue(fd, ctx->script_buf, ctx->script_buflen,
7278d36e1dfSRoy Marples 		    true) == -1)
7287827cba2SAaron LI 			logerr("%s: control_queue", __func__);
7297827cba2SAaron LI 		else
7307827cba2SAaron LI 			status = 1;
7317827cba2SAaron LI 	}
7327827cba2SAaron LI 
733*6e63cc1fSRoy Marples 	return status;
7347827cba2SAaron LI }
735*6e63cc1fSRoy Marples 
736*6e63cc1fSRoy Marples #ifdef PRIVSEP
737*6e63cc1fSRoy Marples int
738*6e63cc1fSRoy Marples script_runchroot(struct dhcpcd_ctx *ctx, char *script)
739*6e63cc1fSRoy Marples {
740*6e63cc1fSRoy Marples 	char *argv[2];
741*6e63cc1fSRoy Marples 
742*6e63cc1fSRoy Marples 	/* Make our env */
743*6e63cc1fSRoy Marples 	if (make_env(ctx, NULL, "CHROOT") == -1) {
744*6e63cc1fSRoy Marples 		logerr(__func__);
745*6e63cc1fSRoy Marples 		return -1;
746*6e63cc1fSRoy Marples 	}
747*6e63cc1fSRoy Marples 
748*6e63cc1fSRoy Marples 	argv[0] = script;
749*6e63cc1fSRoy Marples 	argv[1] = NULL;
750*6e63cc1fSRoy Marples 	logdebugx("executing `%s' %s", argv[0], "CHROOT");
751*6e63cc1fSRoy Marples 
752*6e63cc1fSRoy Marples 	return script_run(ctx, argv);
753*6e63cc1fSRoy Marples }
754*6e63cc1fSRoy Marples #endif
755