xref: /dflybsd-src/contrib/dhcpcd/src/script.c (revision b9ccd228a26f629f198e7700ad032193d3644a3b)
18d36e1dfSRoy Marples /* SPDX-License-Identifier: BSD-2-Clause */
27827cba2SAaron LI /*
37827cba2SAaron LI  * dhcpcd - DHCP client daemon
48d36e1dfSRoy 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 
368d36e1dfSRoy Marples #include <assert.h>
377827cba2SAaron LI #include <ctype.h>
387827cba2SAaron LI #include <errno.h>
397827cba2SAaron LI #include <signal.h>
407827cba2SAaron LI #include <spawn.h>
418d36e1dfSRoy 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 
628d36e1dfSRoy 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
89*b9ccd228SRoy Marples script_exec(const struct dhcpcd_ctx *ctx, char *const *argv, char *const *env)
907827cba2SAaron LI {
91*b9ccd228SRoy Marples 	pid_t pid = 0;
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
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 
176*b9ccd228SRoy Marples static char **
177*b9ccd228SRoy Marples script_buftoenv(struct dhcpcd_ctx *ctx, char *buf, size_t len)
178*b9ccd228SRoy Marples {
179*b9ccd228SRoy Marples 	char **env, **envp, *bufp, *endp;
180*b9ccd228SRoy Marples 	size_t nenv;
181*b9ccd228SRoy Marples 
182*b9ccd228SRoy Marples 	/* Count the terminated env strings.
183*b9ccd228SRoy Marples 	 * Assert that the terminations are correct. */
184*b9ccd228SRoy Marples 	nenv = 0;
185*b9ccd228SRoy Marples 	endp = buf + len;
186*b9ccd228SRoy Marples 	for (bufp = buf; bufp < endp; bufp++) {
187*b9ccd228SRoy Marples 		if (*bufp == '\0') {
188*b9ccd228SRoy Marples #ifndef NDEBUG
189*b9ccd228SRoy Marples 			if (bufp + 1 < endp)
190*b9ccd228SRoy Marples 				assert(*(bufp + 1) != '\0');
191*b9ccd228SRoy Marples #endif
192*b9ccd228SRoy Marples 			nenv++;
193*b9ccd228SRoy Marples 		}
194*b9ccd228SRoy Marples 	}
195*b9ccd228SRoy Marples 	assert(*(bufp - 1) == '\0');
196*b9ccd228SRoy Marples 
197*b9ccd228SRoy Marples 	if (ctx->script_envlen < nenv) {
198*b9ccd228SRoy Marples 		env = reallocarray(ctx->script_env, nenv + 1, sizeof(*env));
199*b9ccd228SRoy Marples 		if (env == NULL)
200*b9ccd228SRoy Marples 			return NULL;
201*b9ccd228SRoy Marples 		ctx->script_env = env;
202*b9ccd228SRoy Marples 		ctx->script_envlen = nenv;
203*b9ccd228SRoy Marples 	}
204*b9ccd228SRoy Marples 
205*b9ccd228SRoy Marples 	bufp = buf;
206*b9ccd228SRoy Marples 	envp = ctx->script_env;
207*b9ccd228SRoy Marples 	*envp++ = bufp++;
208*b9ccd228SRoy Marples 	endp--; /* Avoid setting the last \0 to an invalid pointer */
209*b9ccd228SRoy Marples 	for (; bufp < endp; bufp++) {
210*b9ccd228SRoy Marples 		if (*bufp == '\0')
211*b9ccd228SRoy Marples 			*envp++ = bufp + 1;
212*b9ccd228SRoy Marples 	}
213*b9ccd228SRoy Marples 	*envp = NULL;
214*b9ccd228SRoy Marples 
215*b9ccd228SRoy Marples 	return ctx->script_env;
216*b9ccd228SRoy Marples }
217*b9ccd228SRoy Marples 
2188d36e1dfSRoy Marples static long
2198d36e1dfSRoy Marples make_env(const struct interface *ifp, const char *reason)
2208d36e1dfSRoy Marples {
2218d36e1dfSRoy Marples 	struct dhcpcd_ctx *ctx = ifp->ctx;
2228d36e1dfSRoy Marples 	FILE *fp;
2238d36e1dfSRoy Marples 	long buf_pos, i;
224*b9ccd228SRoy Marples 	char *path;
2258d36e1dfSRoy Marples 	int protocol = PROTO_LINK;
2267827cba2SAaron LI 	const struct if_options *ifo = ifp->options;
2277827cba2SAaron LI 	const struct interface *ifp2;
2287827cba2SAaron LI 	int af;
2297827cba2SAaron LI #ifdef INET
2307827cba2SAaron LI 	const struct dhcp_state *state;
2317827cba2SAaron LI #ifdef IPV4LL
2327827cba2SAaron LI 	const struct ipv4ll_state *istate;
2337827cba2SAaron LI #endif
2347827cba2SAaron LI #endif
2358d36e1dfSRoy Marples #ifdef DHCP6
2367827cba2SAaron LI 	const struct dhcp6_state *d6_state;
2377827cba2SAaron LI #endif
2387827cba2SAaron LI 
2398d36e1dfSRoy Marples #ifdef HAVE_OPEN_MEMSTREAM
2408d36e1dfSRoy Marples 	if (ctx->script_fp == NULL) {
2418d36e1dfSRoy Marples 		fp = open_memstream(&ctx->script_buf, &ctx->script_buflen);
2428d36e1dfSRoy Marples 		if (fp == NULL)
2438d36e1dfSRoy Marples 			goto eexit;
2448d36e1dfSRoy Marples 		ctx->script_fp = fp;
2458d36e1dfSRoy Marples 	} else {
2468d36e1dfSRoy Marples 		fp = ctx->script_fp;
2478d36e1dfSRoy Marples 		rewind(fp);
2488d36e1dfSRoy Marples 	}
2498d36e1dfSRoy Marples #else
2508d36e1dfSRoy Marples 	char tmpfile[] = "/tmp/dhcpcd-script-env-XXXXXX";
2518d36e1dfSRoy Marples 	int tmpfd;
2528d36e1dfSRoy Marples 
2538d36e1dfSRoy Marples 	fp = NULL;
2548d36e1dfSRoy Marples 	tmpfd = mkstemp(tmpfile);
2558d36e1dfSRoy Marples 	if (tmpfd == -1)
2568d36e1dfSRoy Marples 		goto eexit;
2578d36e1dfSRoy Marples 	unlink(tmpfile);
2588d36e1dfSRoy Marples 	fp = fdopen(tmpfd, "w+");
2598d36e1dfSRoy Marples 	if (fp == NULL) {
2608d36e1dfSRoy Marples 		close(tmpfd);
2618d36e1dfSRoy Marples 		goto eexit;
2628d36e1dfSRoy Marples 	}
2638d36e1dfSRoy Marples #endif
2648d36e1dfSRoy Marples 
2657827cba2SAaron LI #ifdef INET
2667827cba2SAaron LI 	state = D_STATE(ifp);
2677827cba2SAaron LI #ifdef IPV4LL
2687827cba2SAaron LI 	istate = IPV4LL_CSTATE(ifp);
2697827cba2SAaron LI #endif
2707827cba2SAaron LI #endif
2718d36e1dfSRoy Marples #ifdef DHCP6
2727827cba2SAaron LI 	d6_state = D6_CSTATE(ifp);
2737827cba2SAaron LI #endif
2747827cba2SAaron LI 	if (strcmp(reason, "TEST") == 0) {
2757827cba2SAaron LI 		if (1 == 2) {}
2767827cba2SAaron LI #ifdef INET6
2778d36e1dfSRoy Marples #ifdef DHCP6
2787827cba2SAaron LI 		else if (d6_state && d6_state->new)
2797827cba2SAaron LI 			protocol = PROTO_DHCP6;
2808d36e1dfSRoy Marples #endif
2817827cba2SAaron LI 		else if (ipv6nd_hasra(ifp))
2827827cba2SAaron LI 			protocol = PROTO_RA;
2837827cba2SAaron LI #endif
2847827cba2SAaron LI #ifdef INET
2857827cba2SAaron LI #ifdef IPV4LL
2867827cba2SAaron LI 		else if (istate && istate->addr != NULL)
2877827cba2SAaron LI 			protocol = PROTO_IPV4LL;
2887827cba2SAaron LI #endif
2897827cba2SAaron LI 		else
2907827cba2SAaron LI 			protocol = PROTO_DHCP;
2917827cba2SAaron LI #endif
2927827cba2SAaron LI 	}
2937827cba2SAaron LI #ifdef INET6
2947827cba2SAaron LI 	else if (strcmp(reason, "STATIC6") == 0)
2957827cba2SAaron LI 		protocol = PROTO_STATIC6;
2968d36e1dfSRoy Marples #ifdef DHCP6
2977827cba2SAaron LI 	else if (reason[strlen(reason) - 1] == '6')
2987827cba2SAaron LI 		protocol = PROTO_DHCP6;
2998d36e1dfSRoy Marples #endif
3007827cba2SAaron LI 	else if (strcmp(reason, "ROUTERADVERT") == 0)
3017827cba2SAaron LI 		protocol = PROTO_RA;
3027827cba2SAaron LI #endif
3037827cba2SAaron LI 	else if (strcmp(reason, "PREINIT") == 0 ||
3047827cba2SAaron LI 	    strcmp(reason, "CARRIER") == 0 ||
3057827cba2SAaron LI 	    strcmp(reason, "NOCARRIER") == 0 ||
3067827cba2SAaron LI 	    strcmp(reason, "UNKNOWN") == 0 ||
3077827cba2SAaron LI 	    strcmp(reason, "DEPARTED") == 0 ||
3087827cba2SAaron LI 	    strcmp(reason, "STOPPED") == 0)
3097827cba2SAaron LI 		protocol = PROTO_LINK;
3107827cba2SAaron LI #ifdef INET
3117827cba2SAaron LI #ifdef IPV4LL
3127827cba2SAaron LI 	else if (strcmp(reason, "IPV4LL") == 0)
3137827cba2SAaron LI 		protocol = PROTO_IPV4LL;
3147827cba2SAaron LI #endif
3157827cba2SAaron LI 	else
3167827cba2SAaron LI 		protocol = PROTO_DHCP;
3177827cba2SAaron LI #endif
3187827cba2SAaron LI 
3198d36e1dfSRoy Marples 	/* Needed for scripts */
3208d36e1dfSRoy Marples 	path = getenv("PATH");
3218d36e1dfSRoy Marples 	if (efprintf(fp, "PATH=%s", path == NULL ? DEFAULT_PATH:path) == -1)
3227827cba2SAaron LI 		goto eexit;
3238d36e1dfSRoy Marples 
3248d36e1dfSRoy Marples 	if (efprintf(fp, "interface=%s", ifp->name) == -1)
3258d36e1dfSRoy Marples 		goto eexit;
3268d36e1dfSRoy Marples 	if (efprintf(fp, "reason=%s", reason) == -1)
3278d36e1dfSRoy Marples 		goto eexit;
3287827cba2SAaron LI 	if (ifp->ctx->options & DHCPCD_DUMPLEASE)
3297827cba2SAaron LI 		goto dumplease;
3308d36e1dfSRoy Marples 	if (efprintf(fp, "pid=%d", getpid()) == -1)
3318d36e1dfSRoy Marples 		goto eexit;
3328d36e1dfSRoy Marples 	if (efprintf(fp, "ifcarrier=%s",
3337827cba2SAaron LI 	    ifp->carrier == LINK_UNKNOWN ? "unknown" :
3348d36e1dfSRoy Marples 	    ifp->carrier == LINK_UP ? "up" : "down") == -1)
3358d36e1dfSRoy Marples 		goto eexit;
3368d36e1dfSRoy Marples 	if (efprintf(fp, "ifmetric=%d", ifp->metric) == -1)
3378d36e1dfSRoy Marples 		goto eexit;
3388d36e1dfSRoy Marples 	if (efprintf(fp, "ifwireless=%d", ifp->wireless) == -1)
3398d36e1dfSRoy Marples 		goto eexit;
3408d36e1dfSRoy Marples 	if (efprintf(fp, "ifflags=%u", ifp->flags) == -1)
3418d36e1dfSRoy Marples 		goto eexit;
3428d36e1dfSRoy Marples 	if (efprintf(fp, "ifmtu=%d", if_getmtu(ifp)) == -1)
3438d36e1dfSRoy Marples 		goto eexit;
3448d36e1dfSRoy Marples 
3458d36e1dfSRoy Marples 	if (fprintf(fp, "interface_order=") == -1)
3468d36e1dfSRoy Marples 		goto eexit;
3477827cba2SAaron LI 	TAILQ_FOREACH(ifp2, ifp->ctx->ifaces, next) {
3488d36e1dfSRoy Marples 		if (ifp2 != TAILQ_FIRST(ifp->ctx->ifaces)) {
3498d36e1dfSRoy Marples 			if (fputc(' ', fp) == EOF)
3508d36e1dfSRoy Marples 				return -1;
3517827cba2SAaron LI 		}
3528d36e1dfSRoy Marples 		if (fprintf(fp, "%s", ifp2->name) == -1)
3538d36e1dfSRoy Marples 			return -1;
3547827cba2SAaron LI 	}
3558d36e1dfSRoy Marples 	if (fputc('\0', fp) == EOF)
3568d36e1dfSRoy Marples 		return -1;
3578d36e1dfSRoy Marples 
3587827cba2SAaron LI 	if (strcmp(reason, "STOPPED") == 0) {
3598d36e1dfSRoy Marples 		if (efprintf(fp, "if_up=false") == -1)
3608d36e1dfSRoy Marples 			goto eexit;
3618d36e1dfSRoy Marples 		if (efprintf(fp, "if_down=%s",
3628d36e1dfSRoy Marples 		    ifo->options & DHCPCD_RELEASE ? "true" : "false") == -1)
3638d36e1dfSRoy Marples 			goto eexit;
3647827cba2SAaron LI 	} else if (strcmp(reason, "TEST") == 0 ||
3657827cba2SAaron LI 	    strcmp(reason, "PREINIT") == 0 ||
3667827cba2SAaron LI 	    strcmp(reason, "CARRIER") == 0 ||
3677827cba2SAaron LI 	    strcmp(reason, "UNKNOWN") == 0)
3687827cba2SAaron LI 	{
3698d36e1dfSRoy Marples 		if (efprintf(fp, "if_up=false") == -1)
3708d36e1dfSRoy Marples 			goto eexit;
3718d36e1dfSRoy Marples 		if (efprintf(fp, "if_down=false") == -1)
3728d36e1dfSRoy Marples 			goto eexit;
3737827cba2SAaron LI 	} else if (1 == 2 /* appease ifdefs */
3747827cba2SAaron LI #ifdef INET
3757827cba2SAaron LI 	    || (protocol == PROTO_DHCP && state && state->new)
3767827cba2SAaron LI #ifdef IPV4LL
3777827cba2SAaron LI 	    || (protocol == PROTO_IPV4LL && IPV4LL_STATE_RUNNING(ifp))
3787827cba2SAaron LI #endif
3797827cba2SAaron LI #endif
3807827cba2SAaron LI #ifdef INET6
3817827cba2SAaron LI 	    || (protocol == PROTO_STATIC6 && IPV6_STATE_RUNNING(ifp))
3828d36e1dfSRoy Marples #ifdef DHCP6
3837827cba2SAaron LI 	    || (protocol == PROTO_DHCP6 && d6_state && d6_state->new)
3848d36e1dfSRoy Marples #endif
3857827cba2SAaron LI 	    || (protocol == PROTO_RA && ipv6nd_hasra(ifp))
3867827cba2SAaron LI #endif
3877827cba2SAaron LI 	    )
3887827cba2SAaron LI 	{
3898d36e1dfSRoy Marples 		if (efprintf(fp, "if_up=true") == -1)
3908d36e1dfSRoy Marples 			goto eexit;
3918d36e1dfSRoy Marples 		if (efprintf(fp, "if_down=false") == -1)
3928d36e1dfSRoy Marples 			goto eexit;
3937827cba2SAaron LI 	} else {
3948d36e1dfSRoy Marples 		if (efprintf(fp, "if_up=false") == -1)
3958d36e1dfSRoy Marples 			goto eexit;
3968d36e1dfSRoy Marples 		if (efprintf(fp, "if_down=true") == -1)
3978d36e1dfSRoy Marples 			goto eexit;
3987827cba2SAaron LI 	}
3997827cba2SAaron LI 	if (protocols[protocol] != NULL) {
4008d36e1dfSRoy Marples 		if (efprintf(fp, "protocol=%s", protocols[protocol]) == -1)
4017827cba2SAaron LI 			goto eexit;
4027827cba2SAaron LI 	}
4037827cba2SAaron LI 	if ((af = dhcpcd_ifafwaiting(ifp)) != AF_MAX) {
4048d36e1dfSRoy Marples 		if (efprintf(fp, "if_afwaiting=%d", af) == -1)
4058d36e1dfSRoy Marples 			goto eexit;
4067827cba2SAaron LI 	}
4077827cba2SAaron LI 	if ((af = dhcpcd_afwaiting(ifp->ctx)) != AF_MAX) {
4087827cba2SAaron LI 		TAILQ_FOREACH(ifp2, ifp->ctx->ifaces, next) {
4097827cba2SAaron LI 			if ((af = dhcpcd_ifafwaiting(ifp2)) != AF_MAX)
4107827cba2SAaron LI 				break;
4117827cba2SAaron LI 		}
4127827cba2SAaron LI 	}
4137827cba2SAaron LI 	if (af != AF_MAX) {
4148d36e1dfSRoy Marples 		if (efprintf(fp, "af_waiting=%d", af) == -1)
4158d36e1dfSRoy Marples 			goto eexit;
4167827cba2SAaron LI 	}
4177827cba2SAaron LI 	if (ifo->options & DHCPCD_DEBUG) {
4188d36e1dfSRoy Marples 		if (efprintf(fp, "syslog_debug=true") == -1)
4198d36e1dfSRoy Marples 			goto eexit;
4207827cba2SAaron LI 	}
421*b9ccd228SRoy Marples 	if (*ifp->profile != '\0') {
4228d36e1dfSRoy Marples 		if (efprintf(fp, "profile=%s", ifp->profile) == -1)
4238d36e1dfSRoy Marples 			goto eexit;
4247827cba2SAaron LI 	}
4257827cba2SAaron LI 	if (ifp->wireless) {
4268d36e1dfSRoy Marples 		char pssid[IF_SSIDLEN * 4];
4277827cba2SAaron LI 
4288d36e1dfSRoy Marples 		if (print_string(pssid, sizeof(pssid), OT_ESCSTRING,
4298d36e1dfSRoy Marples 		    ifp->ssid, ifp->ssid_len) != -1)
4308d36e1dfSRoy Marples 		{
4318d36e1dfSRoy Marples 			if (efprintf(fp, "ifssid=%s", pssid) == -1)
4328d36e1dfSRoy Marples 				goto eexit;
4337827cba2SAaron LI 		}
4347827cba2SAaron LI 	}
4357827cba2SAaron LI #ifdef INET
4367827cba2SAaron LI 	if (protocol == PROTO_DHCP && state && state->old) {
4378d36e1dfSRoy Marples 		if (dhcp_env(fp, "old", ifp,
4388d36e1dfSRoy Marples 		    state->old, state->old_len) == -1)
4397827cba2SAaron LI 			goto eexit;
4408d36e1dfSRoy Marples 		if (append_config(fp, "old",
4417827cba2SAaron LI 		    (const char *const *)ifo->config) == -1)
4427827cba2SAaron LI 			goto eexit;
4437827cba2SAaron LI 	}
4447827cba2SAaron LI #endif
4458d36e1dfSRoy Marples #ifdef DHCP6
4467827cba2SAaron LI 	if (protocol == PROTO_DHCP6 && d6_state && d6_state->old) {
4478d36e1dfSRoy Marples 		if (dhcp6_env(fp, "old", ifp,
4488d36e1dfSRoy Marples 		    d6_state->old, d6_state->old_len) == -1)
4497827cba2SAaron LI 			goto eexit;
4507827cba2SAaron LI 	}
4517827cba2SAaron LI #endif
4527827cba2SAaron LI 
4537827cba2SAaron LI dumplease:
4547827cba2SAaron LI #ifdef INET
4557827cba2SAaron LI #ifdef IPV4LL
4567827cba2SAaron LI 	if (protocol == PROTO_IPV4LL) {
4578d36e1dfSRoy Marples 		if (ipv4ll_env(fp, istate->down ? "old" : "new", ifp) == -1)
4587827cba2SAaron LI 			goto eexit;
4597827cba2SAaron LI 	}
4607827cba2SAaron LI #endif
4617827cba2SAaron LI 	if (protocol == PROTO_DHCP && state && state->new) {
4628d36e1dfSRoy Marples 		if (dhcp_env(fp, "new", ifp,
4638d36e1dfSRoy Marples 		    state->new, state->new_len) == -1)
4647827cba2SAaron LI 			goto eexit;
4658d36e1dfSRoy Marples 		if (append_config(fp, "new",
4667827cba2SAaron LI 		    (const char *const *)ifo->config) == -1)
4677827cba2SAaron LI 			goto eexit;
4687827cba2SAaron LI 	}
4697827cba2SAaron LI #endif
4707827cba2SAaron LI #ifdef INET6
4717827cba2SAaron LI 	if (protocol == PROTO_STATIC6) {
4728d36e1dfSRoy Marples 		if (ipv6_env(fp, "new", ifp) == -1)
4737827cba2SAaron LI 			goto eexit;
4747827cba2SAaron LI 	}
4758d36e1dfSRoy Marples #ifdef DHCP6
4767827cba2SAaron LI 	if (protocol == PROTO_DHCP6 && D6_STATE_RUNNING(ifp)) {
4778d36e1dfSRoy Marples 		if (dhcp6_env(fp, "new", ifp,
4788d36e1dfSRoy Marples 		    d6_state->new, d6_state->new_len) == -1)
4797827cba2SAaron LI 			goto eexit;
4807827cba2SAaron LI 	}
4818d36e1dfSRoy Marples #endif
4827827cba2SAaron LI 	if (protocol == PROTO_RA) {
4838d36e1dfSRoy Marples 		if (ipv6nd_env(fp, ifp) == -1)
4847827cba2SAaron LI 			goto eexit;
4857827cba2SAaron LI 	}
4867827cba2SAaron LI #endif
4877827cba2SAaron LI 
4887827cba2SAaron LI 	/* Add our base environment */
4897827cba2SAaron LI 	if (ifo->environ) {
4908d36e1dfSRoy Marples 		for (i = 0; ifo->environ[i] != NULL; i++)
4918d36e1dfSRoy Marples 			if (efprintf(fp, "%s", ifo->environ[i]) == -1)
4927827cba2SAaron LI 				goto eexit;
4937827cba2SAaron LI 	}
4947827cba2SAaron LI 
4958d36e1dfSRoy Marples 	/* Convert buffer to argv */
4968d36e1dfSRoy Marples 	fflush(fp);
4978d36e1dfSRoy Marples 
4988d36e1dfSRoy Marples 	buf_pos = ftell(fp);
4998d36e1dfSRoy Marples 	if (buf_pos == -1) {
5008d36e1dfSRoy Marples 		logerr(__func__);
5018d36e1dfSRoy Marples 		goto eexit;
5028d36e1dfSRoy Marples 	}
5038d36e1dfSRoy Marples 
5048d36e1dfSRoy Marples #ifndef HAVE_OPEN_MEMSTREAM
5058d36e1dfSRoy Marples 	size_t buf_len = (size_t)buf_pos;
5068d36e1dfSRoy Marples 	if (ctx->script_buflen < buf_len) {
5078d36e1dfSRoy Marples 		char *buf = realloc(ctx->script_buf, buf_len);
5088d36e1dfSRoy Marples 		if (buf == NULL)
5098d36e1dfSRoy Marples 			goto eexit;
5108d36e1dfSRoy Marples 		ctx->script_buf = buf;
5118d36e1dfSRoy Marples 		ctx->script_buflen = buf_len;
5128d36e1dfSRoy Marples 	}
5138d36e1dfSRoy Marples 	rewind(fp);
5148d36e1dfSRoy Marples 	if (fread(ctx->script_buf, sizeof(char), buf_len, fp) != buf_len)
5158d36e1dfSRoy Marples 		goto eexit;
5168d36e1dfSRoy Marples 	fclose(fp);
5178d36e1dfSRoy Marples 	fp = NULL;
5188d36e1dfSRoy Marples #endif
5198d36e1dfSRoy Marples 
520*b9ccd228SRoy Marples 	if (script_buftoenv(ctx, ctx->script_buf, (size_t)buf_pos) == NULL)
5218d36e1dfSRoy Marples 		goto eexit;
5228d36e1dfSRoy Marples 
5238d36e1dfSRoy Marples 	return buf_pos - 1;
5247827cba2SAaron LI 
5257827cba2SAaron LI eexit:
5267827cba2SAaron LI 	logerr(__func__);
5278d36e1dfSRoy Marples #ifndef HAVE_OPEN_MEMSTREAM
5288d36e1dfSRoy Marples 	if (fp != NULL)
5298d36e1dfSRoy Marples 		fclose(fp);
5308d36e1dfSRoy Marples #endif
5317827cba2SAaron LI 	return -1;
5327827cba2SAaron LI }
5337827cba2SAaron LI 
5347827cba2SAaron LI static int
5358d36e1dfSRoy Marples send_interface1(struct fd_list *fd, const struct interface *ifp,
5367827cba2SAaron LI     const char *reason)
5377827cba2SAaron LI {
5388d36e1dfSRoy Marples 	struct dhcpcd_ctx *ctx = ifp->ctx;
5398d36e1dfSRoy Marples 	long len;
5407827cba2SAaron LI 
5418d36e1dfSRoy Marples 	len = make_env(ifp, reason);
5428d36e1dfSRoy Marples 	if (len == -1)
5437827cba2SAaron LI 		return -1;
5448d36e1dfSRoy Marples 	return control_queue(fd, ctx->script_buf,  (size_t)len, 1);
5457827cba2SAaron LI }
5467827cba2SAaron LI 
5477827cba2SAaron LI int
5487827cba2SAaron LI send_interface(struct fd_list *fd, const struct interface *ifp)
5497827cba2SAaron LI {
5507827cba2SAaron LI 	const char *reason;
5517827cba2SAaron LI 	int retval = 0;
5527827cba2SAaron LI #ifdef INET
5537827cba2SAaron LI 	const struct dhcp_state *d;
5547827cba2SAaron LI #endif
5558d36e1dfSRoy Marples #ifdef DHCP6
5567827cba2SAaron LI 	const struct dhcp6_state *d6;
5577827cba2SAaron LI #endif
5587827cba2SAaron LI 
5597827cba2SAaron LI 	switch (ifp->carrier) {
5607827cba2SAaron LI 	case LINK_UP:
5617827cba2SAaron LI 		reason = "CARRIER";
5627827cba2SAaron LI 		break;
5637827cba2SAaron LI 	case LINK_DOWN:
5648d36e1dfSRoy Marples 	case LINK_DOWN_IFFUP:
5657827cba2SAaron LI 		reason = "NOCARRIER";
5667827cba2SAaron LI 		break;
5677827cba2SAaron LI 	default:
5687827cba2SAaron LI 		reason = "UNKNOWN";
5697827cba2SAaron LI 		break;
5707827cba2SAaron LI 	}
5717827cba2SAaron LI 	if (send_interface1(fd, ifp, reason) == -1)
5727827cba2SAaron LI 		retval = -1;
5737827cba2SAaron LI #ifdef INET
5747827cba2SAaron LI 	if (D_STATE_RUNNING(ifp)) {
5757827cba2SAaron LI 		d = D_CSTATE(ifp);
5767827cba2SAaron LI 		if (send_interface1(fd, ifp, d->reason) == -1)
5777827cba2SAaron LI 			retval = -1;
5787827cba2SAaron LI 	}
5797827cba2SAaron LI #ifdef IPV4LL
5807827cba2SAaron LI 	if (IPV4LL_STATE_RUNNING(ifp)) {
5817827cba2SAaron LI 		if (send_interface1(fd, ifp, "IPV4LL") == -1)
5827827cba2SAaron LI 			retval = -1;
5837827cba2SAaron LI 	}
5847827cba2SAaron LI #endif
5857827cba2SAaron LI #endif
5867827cba2SAaron LI 
5877827cba2SAaron LI #ifdef INET6
5887827cba2SAaron LI 	if (IPV6_STATE_RUNNING(ifp)) {
5897827cba2SAaron LI 		if (send_interface1(fd, ifp, "STATIC6") == -1)
5907827cba2SAaron LI 			retval = -1;
5917827cba2SAaron LI 	}
5927827cba2SAaron LI 	if (RS_STATE_RUNNING(ifp)) {
5937827cba2SAaron LI 		if (send_interface1(fd, ifp, "ROUTERADVERT") == -1)
5947827cba2SAaron LI 			retval = -1;
5957827cba2SAaron LI 	}
5968d36e1dfSRoy Marples #ifdef DHCP6
5977827cba2SAaron LI 	if (D6_STATE_RUNNING(ifp)) {
5987827cba2SAaron LI 		d6 = D6_CSTATE(ifp);
5997827cba2SAaron LI 		if (send_interface1(fd, ifp, d6->reason) == -1)
6007827cba2SAaron LI 			retval = -1;
6017827cba2SAaron LI 	}
6027827cba2SAaron LI #endif
6038d36e1dfSRoy Marples #endif
6047827cba2SAaron LI 
6057827cba2SAaron LI 	return retval;
6067827cba2SAaron LI }
6077827cba2SAaron LI 
6087827cba2SAaron LI int
6097827cba2SAaron LI script_runreason(const struct interface *ifp, const char *reason)
6107827cba2SAaron LI {
6118d36e1dfSRoy Marples 	struct dhcpcd_ctx *ctx = ifp->ctx;
6127827cba2SAaron LI 	char *argv[2];
6137827cba2SAaron LI 	pid_t pid;
6147827cba2SAaron LI 	int status = 0;
6157827cba2SAaron LI 	struct fd_list *fd;
6167827cba2SAaron LI 
6178d36e1dfSRoy Marples 	if (ifp->options->script == NULL &&
6187827cba2SAaron LI 	    TAILQ_FIRST(&ifp->ctx->control_fds) == NULL)
6197827cba2SAaron LI 		return 0;
6207827cba2SAaron LI 
6217827cba2SAaron LI 	/* Make our env */
6228d36e1dfSRoy Marples 	if (make_env(ifp, reason) == -1) {
6237827cba2SAaron LI 		logerr(__func__);
6247827cba2SAaron LI 		return -1;
6257827cba2SAaron LI 	}
6267827cba2SAaron LI 
6278d36e1dfSRoy Marples 	if (ifp->options->script == NULL)
6287827cba2SAaron LI 		goto send_listeners;
6297827cba2SAaron LI 
6308d36e1dfSRoy Marples 	argv[0] = ifp->options->script;
6317827cba2SAaron LI 	argv[1] = NULL;
6327827cba2SAaron LI 	logdebugx("%s: executing `%s' %s", ifp->name, argv[0], reason);
6337827cba2SAaron LI 
634*b9ccd228SRoy Marples 	pid = script_exec(ctx, argv, ctx->script_env);
6357827cba2SAaron LI 	if (pid == -1)
6367827cba2SAaron LI 		logerr("%s: %s", __func__, argv[0]);
6377827cba2SAaron LI 	else if (pid != 0) {
6387827cba2SAaron LI 		/* Wait for the script to finish */
6397827cba2SAaron LI 		while (waitpid(pid, &status, 0) == -1) {
6407827cba2SAaron LI 			if (errno != EINTR) {
6417827cba2SAaron LI 				logerr("%s: waitpid", __func__);
6427827cba2SAaron LI 				status = 0;
6437827cba2SAaron LI 				break;
6447827cba2SAaron LI 			}
6457827cba2SAaron LI 		}
6467827cba2SAaron LI 		if (WIFEXITED(status)) {
6477827cba2SAaron LI 			if (WEXITSTATUS(status))
6487827cba2SAaron LI 				logerrx("%s: %s: WEXITSTATUS %d",
6497827cba2SAaron LI 				    __func__, argv[0], WEXITSTATUS(status));
6507827cba2SAaron LI 		} else if (WIFSIGNALED(status))
6517827cba2SAaron LI 			logerrx("%s: %s: %s",
6527827cba2SAaron LI 			    __func__, argv[0], strsignal(WTERMSIG(status)));
6537827cba2SAaron LI 	}
6547827cba2SAaron LI 
6557827cba2SAaron LI send_listeners:
6567827cba2SAaron LI 	/* Send to our listeners */
6577827cba2SAaron LI 	status = 0;
6588d36e1dfSRoy Marples 	TAILQ_FOREACH(fd, &ctx->control_fds, next) {
6597827cba2SAaron LI 		if (!(fd->flags & FD_LISTEN))
6607827cba2SAaron LI 			continue;
6618d36e1dfSRoy Marples 		if (control_queue(fd, ctx->script_buf, ctx->script_buflen,
6628d36e1dfSRoy Marples 		    true) == -1)
6637827cba2SAaron LI 			logerr("%s: control_queue", __func__);
6647827cba2SAaron LI 		else
6657827cba2SAaron LI 			status = 1;
6667827cba2SAaron LI 	}
6677827cba2SAaron LI 
6687827cba2SAaron LI 	return WEXITSTATUS(status);
6697827cba2SAaron LI }
670