1d4fb1e02SRoy Marples /* SPDX-License-Identifier: BSD-2-Clause */
27827cba2SAaron LI /*
37827cba2SAaron LI * dhcpcd - DHCP client daemon
480aa9461SRoy Marples * Copyright (c) 2006-2023 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
770aaf6155SRoy Marples static const char * true_str = "true";
780aaf6155SRoy Marples static const char * false_str = "false";
790aaf6155SRoy Marples
807827cba2SAaron LI void
if_printoptions(void)817827cba2SAaron LI if_printoptions(void)
827827cba2SAaron LI {
837827cba2SAaron LI const char * const *p;
847827cba2SAaron LI
857827cba2SAaron LI for (p = if_params; *p; p++)
867827cba2SAaron LI printf(" - %s\n", *p);
877827cba2SAaron LI }
887827cba2SAaron LI
896e63cc1fSRoy Marples pid_t
script_exec(char * const * argv,char * const * env)90280986e4SRoy Marples script_exec(char *const *argv, char *const *env)
917827cba2SAaron LI {
92b9ccd228SRoy Marples pid_t pid = 0;
937827cba2SAaron LI posix_spawnattr_t attr;
947827cba2SAaron LI int r;
957827cba2SAaron LI #ifdef USE_SIGNALS
967827cba2SAaron LI size_t i;
977827cba2SAaron LI short flags;
987827cba2SAaron LI sigset_t defsigs;
997827cba2SAaron LI #else
1007827cba2SAaron LI UNUSED(ctx);
1017827cba2SAaron LI #endif
1027827cba2SAaron LI
1037827cba2SAaron LI /* posix_spawn is a safe way of executing another image
1047827cba2SAaron LI * and changing signals back to how they should be. */
1057827cba2SAaron LI if (posix_spawnattr_init(&attr) == -1)
1067827cba2SAaron LI return -1;
1077827cba2SAaron LI #ifdef USE_SIGNALS
1087827cba2SAaron LI flags = POSIX_SPAWN_SETSIGMASK | POSIX_SPAWN_SETSIGDEF;
1097827cba2SAaron LI posix_spawnattr_setflags(&attr, flags);
1107827cba2SAaron LI sigemptyset(&defsigs);
111280986e4SRoy Marples posix_spawnattr_setsigmask(&attr, &defsigs);
1127827cba2SAaron LI for (i = 0; i < dhcpcd_signals_len; i++)
1137827cba2SAaron LI sigaddset(&defsigs, dhcpcd_signals[i]);
114d4fb1e02SRoy Marples for (i = 0; i < dhcpcd_signals_ignore_len; i++)
115d4fb1e02SRoy Marples sigaddset(&defsigs, dhcpcd_signals_ignore[i]);
1167827cba2SAaron LI posix_spawnattr_setsigdefault(&attr, &defsigs);
1177827cba2SAaron LI #endif
1187827cba2SAaron LI errno = 0;
1197827cba2SAaron LI r = posix_spawn(&pid, argv[0], NULL, &attr, argv, env);
1207827cba2SAaron LI posix_spawnattr_destroy(&attr);
1217827cba2SAaron LI if (r) {
1227827cba2SAaron LI errno = r;
1237827cba2SAaron LI return -1;
1247827cba2SAaron LI }
1257827cba2SAaron LI return pid;
1267827cba2SAaron LI }
1277827cba2SAaron LI
1287827cba2SAaron LI #ifdef INET
1297827cba2SAaron LI static int
append_config(FILE * fp,const char * prefix,const char * const * config)1308d36e1dfSRoy Marples append_config(FILE *fp, const char *prefix, const char *const *config)
1317827cba2SAaron LI {
1328d36e1dfSRoy Marples size_t i;
1337827cba2SAaron LI
1347827cba2SAaron LI if (config == NULL)
1357827cba2SAaron LI return 0;
1367827cba2SAaron LI
1378d36e1dfSRoy Marples /* Do we need to replace existing config rather than append? */
1387827cba2SAaron LI for (i = 0; config[i] != NULL; i++) {
1398d36e1dfSRoy Marples if (efprintf(fp, "%s_%s", prefix, config[i]) == -1)
1407827cba2SAaron LI return -1;
1417827cba2SAaron LI }
1428d36e1dfSRoy Marples return 1;
1437827cba2SAaron LI }
1447827cba2SAaron LI
1458d36e1dfSRoy Marples #endif
1468d36e1dfSRoy Marples
1477827cba2SAaron LI #define PROTO_LINK 0
1487827cba2SAaron LI #define PROTO_DHCP 1
1497827cba2SAaron LI #define PROTO_IPV4LL 2
1507827cba2SAaron LI #define PROTO_RA 3
1517827cba2SAaron LI #define PROTO_DHCP6 4
1527827cba2SAaron LI #define PROTO_STATIC6 5
1537827cba2SAaron LI static const char *protocols[] = {
1547827cba2SAaron LI "link",
1557827cba2SAaron LI "dhcp",
1567827cba2SAaron LI "ipv4ll",
1577827cba2SAaron LI "ra",
1587827cba2SAaron LI "dhcp6",
1597827cba2SAaron LI "static6"
1607827cba2SAaron LI };
1617827cba2SAaron LI
1628d36e1dfSRoy Marples int
efprintf(FILE * fp,const char * fmt,...)1638d36e1dfSRoy Marples efprintf(FILE *fp, const char *fmt, ...)
1647827cba2SAaron LI {
1658d36e1dfSRoy Marples va_list args;
1668d36e1dfSRoy Marples int r;
1678d36e1dfSRoy Marples
1688d36e1dfSRoy Marples va_start(args, fmt);
1698d36e1dfSRoy Marples r = vfprintf(fp, fmt, args);
1708d36e1dfSRoy Marples va_end(args);
1718d36e1dfSRoy Marples if (r == -1)
1728d36e1dfSRoy Marples return -1;
1738d36e1dfSRoy Marples /* Write a trailing NULL so we can easily create env strings. */
1748d36e1dfSRoy Marples if (fputc('\0', fp) == EOF)
1758d36e1dfSRoy Marples return -1;
1768d36e1dfSRoy Marples return r;
1778d36e1dfSRoy Marples }
1788d36e1dfSRoy Marples
1796e63cc1fSRoy Marples char **
script_buftoenv(struct dhcpcd_ctx * ctx,char * buf,size_t len)180b9ccd228SRoy Marples script_buftoenv(struct dhcpcd_ctx *ctx, char *buf, size_t len)
181b9ccd228SRoy Marples {
182b9ccd228SRoy Marples char **env, **envp, *bufp, *endp;
183b9ccd228SRoy Marples size_t nenv;
184b9ccd228SRoy Marples
185b9ccd228SRoy Marples /* Count the terminated env strings.
186b9ccd228SRoy Marples * Assert that the terminations are correct. */
187b9ccd228SRoy Marples nenv = 0;
188b9ccd228SRoy Marples endp = buf + len;
189b9ccd228SRoy Marples for (bufp = buf; bufp < endp; bufp++) {
190b9ccd228SRoy Marples if (*bufp == '\0') {
191b9ccd228SRoy Marples #ifndef NDEBUG
192b9ccd228SRoy Marples if (bufp + 1 < endp)
193b9ccd228SRoy Marples assert(*(bufp + 1) != '\0');
194b9ccd228SRoy Marples #endif
195b9ccd228SRoy Marples nenv++;
196b9ccd228SRoy Marples }
197b9ccd228SRoy Marples }
198b9ccd228SRoy Marples assert(*(bufp - 1) == '\0');
199d4fb1e02SRoy Marples if (nenv == 0)
200d4fb1e02SRoy Marples return NULL;
201b9ccd228SRoy Marples
202b9ccd228SRoy Marples if (ctx->script_envlen < nenv) {
203b9ccd228SRoy Marples env = reallocarray(ctx->script_env, nenv + 1, sizeof(*env));
204b9ccd228SRoy Marples if (env == NULL)
205b9ccd228SRoy Marples return NULL;
206b9ccd228SRoy Marples ctx->script_env = env;
207b9ccd228SRoy Marples ctx->script_envlen = nenv;
208b9ccd228SRoy Marples }
209b9ccd228SRoy Marples
210b9ccd228SRoy Marples bufp = buf;
211b9ccd228SRoy Marples envp = ctx->script_env;
212b9ccd228SRoy Marples *envp++ = bufp++;
213b9ccd228SRoy Marples endp--; /* Avoid setting the last \0 to an invalid pointer */
214b9ccd228SRoy Marples for (; bufp < endp; bufp++) {
215b9ccd228SRoy Marples if (*bufp == '\0')
216b9ccd228SRoy Marples *envp++ = bufp + 1;
217b9ccd228SRoy Marples }
218b9ccd228SRoy Marples *envp = NULL;
219b9ccd228SRoy Marples
220b9ccd228SRoy Marples return ctx->script_env;
221b9ccd228SRoy Marples }
222b9ccd228SRoy Marples
2238d36e1dfSRoy Marples static long
make_env(struct dhcpcd_ctx * ctx,const struct interface * ifp,const char * reason)2246e63cc1fSRoy Marples make_env(struct dhcpcd_ctx *ctx, const struct interface *ifp,
2256e63cc1fSRoy Marples const char *reason)
2268d36e1dfSRoy Marples {
2278d36e1dfSRoy Marples FILE *fp;
2288d36e1dfSRoy Marples long buf_pos, i;
229b9ccd228SRoy Marples char *path;
2308d36e1dfSRoy Marples int protocol = PROTO_LINK;
2316e63cc1fSRoy Marples const struct if_options *ifo;
2327827cba2SAaron LI const struct interface *ifp2;
2337827cba2SAaron LI int af;
2340aaf6155SRoy Marples bool is_stdin = ifp->name[0] == '\0';
2350aaf6155SRoy Marples const char *if_up, *if_down;
2360aaf6155SRoy Marples rb_tree_t ifaces;
2370aaf6155SRoy Marples struct rt *rt;
2387827cba2SAaron LI #ifdef INET
2397827cba2SAaron LI const struct dhcp_state *state;
2407827cba2SAaron LI #ifdef IPV4LL
2417827cba2SAaron LI const struct ipv4ll_state *istate;
2427827cba2SAaron LI #endif
2437827cba2SAaron LI #endif
2448d36e1dfSRoy Marples #ifdef DHCP6
2457827cba2SAaron LI const struct dhcp6_state *d6_state;
2467827cba2SAaron LI #endif
2477827cba2SAaron LI
2488d36e1dfSRoy Marples #ifdef HAVE_OPEN_MEMSTREAM
2498d36e1dfSRoy Marples if (ctx->script_fp == NULL) {
2508d36e1dfSRoy Marples fp = open_memstream(&ctx->script_buf, &ctx->script_buflen);
2518d36e1dfSRoy Marples if (fp == NULL)
2528d36e1dfSRoy Marples goto eexit;
2538d36e1dfSRoy Marples ctx->script_fp = fp;
2548d36e1dfSRoy Marples } else {
2558d36e1dfSRoy Marples fp = ctx->script_fp;
2568d36e1dfSRoy Marples rewind(fp);
2578d36e1dfSRoy Marples }
2588d36e1dfSRoy Marples #else
2598d36e1dfSRoy Marples char tmpfile[] = "/tmp/dhcpcd-script-env-XXXXXX";
2608d36e1dfSRoy Marples int tmpfd;
2618d36e1dfSRoy Marples
2628d36e1dfSRoy Marples fp = NULL;
2638d36e1dfSRoy Marples tmpfd = mkstemp(tmpfile);
264d4fb1e02SRoy Marples if (tmpfd == -1) {
265d4fb1e02SRoy Marples logerr("%s: mkstemp", __func__);
266d4fb1e02SRoy Marples return -1;
267d4fb1e02SRoy Marples }
2688d36e1dfSRoy Marples unlink(tmpfile);
2698d36e1dfSRoy Marples fp = fdopen(tmpfd, "w+");
2708d36e1dfSRoy Marples if (fp == NULL) {
2718d36e1dfSRoy Marples close(tmpfd);
2728d36e1dfSRoy Marples goto eexit;
2738d36e1dfSRoy Marples }
2748d36e1dfSRoy Marples #endif
2758d36e1dfSRoy Marples
276d4fb1e02SRoy Marples if (!(ifp->ctx->options & DHCPCD_DUMPLEASE)) {
2776e63cc1fSRoy Marples /* Needed for scripts */
2786e63cc1fSRoy Marples path = getenv("PATH");
279d4fb1e02SRoy Marples if (efprintf(fp, "PATH=%s",
280d4fb1e02SRoy Marples path == NULL ? DEFAULT_PATH : path) == -1)
2816e63cc1fSRoy Marples goto eexit;
2826e63cc1fSRoy Marples if (efprintf(fp, "pid=%d", getpid()) == -1)
2836e63cc1fSRoy Marples goto eexit;
284d4fb1e02SRoy Marples }
2850aaf6155SRoy Marples
286d4fb1e02SRoy Marples if (!is_stdin) {
287d4fb1e02SRoy Marples if (efprintf(fp, "reason=%s", reason) == -1)
2886e63cc1fSRoy Marples goto eexit;
2896e63cc1fSRoy Marples }
2906e63cc1fSRoy Marples
2916e63cc1fSRoy Marples ifo = ifp->options;
2927827cba2SAaron LI #ifdef INET
2937827cba2SAaron LI state = D_STATE(ifp);
2947827cba2SAaron LI #ifdef IPV4LL
2957827cba2SAaron LI istate = IPV4LL_CSTATE(ifp);
2967827cba2SAaron LI #endif
2977827cba2SAaron LI #endif
2988d36e1dfSRoy Marples #ifdef DHCP6
2997827cba2SAaron LI d6_state = D6_CSTATE(ifp);
3007827cba2SAaron LI #endif
3017827cba2SAaron LI if (strcmp(reason, "TEST") == 0) {
3026e63cc1fSRoy Marples if (1 == 2) {
3036e63cc1fSRoy Marples /* This space left intentionally blank
3046e63cc1fSRoy Marples * as all the below statements are optional. */
3056e63cc1fSRoy Marples }
3067827cba2SAaron LI #ifdef INET6
3078d36e1dfSRoy Marples #ifdef DHCP6
3087827cba2SAaron LI else if (d6_state && d6_state->new)
3097827cba2SAaron LI protocol = PROTO_DHCP6;
3108d36e1dfSRoy Marples #endif
3117827cba2SAaron LI else if (ipv6nd_hasra(ifp))
3127827cba2SAaron LI protocol = PROTO_RA;
3137827cba2SAaron LI #endif
3147827cba2SAaron LI #ifdef INET
3157827cba2SAaron LI #ifdef IPV4LL
3167827cba2SAaron LI else if (istate && istate->addr != NULL)
3177827cba2SAaron LI protocol = PROTO_IPV4LL;
3187827cba2SAaron LI #endif
3197827cba2SAaron LI else
3207827cba2SAaron LI protocol = PROTO_DHCP;
3217827cba2SAaron LI #endif
3227827cba2SAaron LI }
3237827cba2SAaron LI #ifdef INET6
3247827cba2SAaron LI else if (strcmp(reason, "STATIC6") == 0)
3257827cba2SAaron LI protocol = PROTO_STATIC6;
3268d36e1dfSRoy Marples #ifdef DHCP6
3277827cba2SAaron LI else if (reason[strlen(reason) - 1] == '6')
3287827cba2SAaron LI protocol = PROTO_DHCP6;
3298d36e1dfSRoy Marples #endif
3307827cba2SAaron LI else if (strcmp(reason, "ROUTERADVERT") == 0)
3317827cba2SAaron LI protocol = PROTO_RA;
3327827cba2SAaron LI #endif
3337827cba2SAaron LI else if (strcmp(reason, "PREINIT") == 0 ||
3347827cba2SAaron LI strcmp(reason, "CARRIER") == 0 ||
3357827cba2SAaron LI strcmp(reason, "NOCARRIER") == 0 ||
3360aaf6155SRoy Marples strcmp(reason, "NOCARRIER_ROAMING") == 0 ||
3377827cba2SAaron LI strcmp(reason, "UNKNOWN") == 0 ||
3387827cba2SAaron LI strcmp(reason, "DEPARTED") == 0 ||
3397827cba2SAaron LI strcmp(reason, "STOPPED") == 0)
3407827cba2SAaron LI protocol = PROTO_LINK;
3417827cba2SAaron LI #ifdef INET
3427827cba2SAaron LI #ifdef IPV4LL
3437827cba2SAaron LI else if (strcmp(reason, "IPV4LL") == 0)
3447827cba2SAaron LI protocol = PROTO_IPV4LL;
3457827cba2SAaron LI #endif
3467827cba2SAaron LI else
3477827cba2SAaron LI protocol = PROTO_DHCP;
3487827cba2SAaron LI #endif
3497827cba2SAaron LI
350d4fb1e02SRoy Marples if (!is_stdin) {
3518d36e1dfSRoy Marples if (efprintf(fp, "interface=%s", ifp->name) == -1)
3528d36e1dfSRoy Marples goto eexit;
353cc34ba0cSRoy Marples if (protocols[protocol] != NULL) {
354cc34ba0cSRoy Marples if (efprintf(fp, "protocol=%s",
355cc34ba0cSRoy Marples protocols[protocol]) == -1)
356cc34ba0cSRoy Marples goto eexit;
357d4fb1e02SRoy Marples }
358cc34ba0cSRoy Marples }
359cc34ba0cSRoy Marples if (ifp->ctx->options & DHCPCD_DUMPLEASE && protocol != PROTO_LINK)
3607827cba2SAaron LI goto dumplease;
361b2927f2bSRoy Marples if (efprintf(fp, "if_configured=%s",
362b2927f2bSRoy Marples ifo->options & DHCPCD_CONFIGURE ? "true" : "false") == -1)
363b2927f2bSRoy Marples goto eexit;
3648d36e1dfSRoy Marples if (efprintf(fp, "ifcarrier=%s",
3657827cba2SAaron LI ifp->carrier == LINK_UNKNOWN ? "unknown" :
3668d36e1dfSRoy Marples ifp->carrier == LINK_UP ? "up" : "down") == -1)
3678d36e1dfSRoy Marples goto eexit;
3688d36e1dfSRoy Marples if (efprintf(fp, "ifmetric=%d", ifp->metric) == -1)
3698d36e1dfSRoy Marples goto eexit;
3708d36e1dfSRoy Marples if (efprintf(fp, "ifwireless=%d", ifp->wireless) == -1)
3718d36e1dfSRoy Marples goto eexit;
3728d36e1dfSRoy Marples if (efprintf(fp, "ifflags=%u", ifp->flags) == -1)
3738d36e1dfSRoy Marples goto eexit;
3748d36e1dfSRoy Marples if (efprintf(fp, "ifmtu=%d", if_getmtu(ifp)) == -1)
3758d36e1dfSRoy Marples goto eexit;
376cc34ba0cSRoy Marples if (ifp->wireless) {
377cc34ba0cSRoy Marples char pssid[IF_SSIDLEN * 4];
378cc34ba0cSRoy Marples
379cc34ba0cSRoy Marples if (print_string(pssid, sizeof(pssid), OT_ESCSTRING,
380cc34ba0cSRoy Marples ifp->ssid, ifp->ssid_len) != -1)
381cc34ba0cSRoy Marples {
382cc34ba0cSRoy Marples if (efprintf(fp, "ifssid=%s", pssid) == -1)
383cc34ba0cSRoy Marples goto eexit;
384cc34ba0cSRoy Marples }
385cc34ba0cSRoy Marples }
386cc34ba0cSRoy Marples if (*ifp->profile != '\0') {
387cc34ba0cSRoy Marples if (efprintf(fp, "profile=%s", ifp->profile) == -1)
388cc34ba0cSRoy Marples goto eexit;
389cc34ba0cSRoy Marples }
390cc34ba0cSRoy Marples if (ifp->ctx->options & DHCPCD_DUMPLEASE)
391cc34ba0cSRoy Marples goto dumplease;
3928d36e1dfSRoy Marples
3930a68f8d2SRoy Marples ifp->ctx->rt_order = 0;
3940aaf6155SRoy Marples rb_tree_init(&ifaces, &rt_compare_proto_ops);
3950aaf6155SRoy Marples TAILQ_FOREACH(ifp2, ifp->ctx->ifaces, next) {
3960aaf6155SRoy Marples if (!ifp2->active)
3970aaf6155SRoy Marples continue;
3980aaf6155SRoy Marples rt = rt_new(UNCONST(ifp2));
3990aaf6155SRoy Marples if (rt == NULL)
4000aaf6155SRoy Marples goto eexit;
4010a68f8d2SRoy Marples if (rt_proto_add(&ifaces, rt) != rt)
4020aaf6155SRoy Marples goto eexit;
4030aaf6155SRoy Marples }
4048d36e1dfSRoy Marples if (fprintf(fp, "interface_order=") == -1)
4058d36e1dfSRoy Marples goto eexit;
4060aaf6155SRoy Marples RB_TREE_FOREACH(rt, &ifaces) {
4070aaf6155SRoy Marples if (rt != RB_TREE_MIN(&ifaces) &&
4080aaf6155SRoy Marples fprintf(fp, "%s", " ") == -1)
4090aaf6155SRoy Marples goto eexit;
4100aaf6155SRoy Marples if (fprintf(fp, "%s", rt->rt_ifp->name) == -1)
4110aaf6155SRoy Marples goto eexit;
4127827cba2SAaron LI }
4130aaf6155SRoy Marples rt_headclear(&ifaces, AF_UNSPEC);
4148d36e1dfSRoy Marples if (fputc('\0', fp) == EOF)
4150aaf6155SRoy Marples goto eexit;
4168d36e1dfSRoy Marples
4177827cba2SAaron LI if (strcmp(reason, "STOPPED") == 0) {
4180aaf6155SRoy Marples if_up = false_str;
4190aaf6155SRoy Marples if_down = ifo->options & DHCPCD_RELEASE ? true_str : false_str;
4207827cba2SAaron LI } else if (strcmp(reason, "TEST") == 0 ||
4217827cba2SAaron LI strcmp(reason, "PREINIT") == 0 ||
4227827cba2SAaron LI strcmp(reason, "CARRIER") == 0 ||
4237827cba2SAaron LI strcmp(reason, "UNKNOWN") == 0)
4247827cba2SAaron LI {
4250aaf6155SRoy Marples if_up = false_str;
4260aaf6155SRoy Marples if_down = false_str;
4270aaf6155SRoy Marples } else if (strcmp(reason, "NOCARRIER") == 0) {
4280aaf6155SRoy Marples if_up = false_str;
4290aaf6155SRoy Marples if_down = true_str;
4300aaf6155SRoy Marples } else if (strcmp(reason, "NOCARRIER_ROAMING") == 0) {
4310aaf6155SRoy Marples if_up = true_str;
4320aaf6155SRoy Marples if_down = false_str;
4337827cba2SAaron LI } else if (1 == 2 /* appease ifdefs */
4347827cba2SAaron LI #ifdef INET
4357827cba2SAaron LI || (protocol == PROTO_DHCP && state && state->new)
4367827cba2SAaron LI #ifdef IPV4LL
4377827cba2SAaron LI || (protocol == PROTO_IPV4LL && IPV4LL_STATE_RUNNING(ifp))
4387827cba2SAaron LI #endif
4397827cba2SAaron LI #endif
4407827cba2SAaron LI #ifdef INET6
4417827cba2SAaron LI || (protocol == PROTO_STATIC6 && IPV6_STATE_RUNNING(ifp))
4428d36e1dfSRoy Marples #ifdef DHCP6
4437827cba2SAaron LI || (protocol == PROTO_DHCP6 && d6_state && d6_state->new)
4448d36e1dfSRoy Marples #endif
4457827cba2SAaron LI || (protocol == PROTO_RA && ipv6nd_hasra(ifp))
4467827cba2SAaron LI #endif
4477827cba2SAaron LI )
4487827cba2SAaron LI {
4490aaf6155SRoy Marples if_up = true_str;
4500aaf6155SRoy Marples if_down = false_str;
4517827cba2SAaron LI } else {
4520aaf6155SRoy Marples if_up = false_str;
4530aaf6155SRoy Marples if_down = true_str;
4547827cba2SAaron LI }
4550aaf6155SRoy Marples if (efprintf(fp, "if_up=%s", if_up) == -1)
4560aaf6155SRoy Marples goto eexit;
4570aaf6155SRoy Marples if (efprintf(fp, "if_down=%s", if_down) == -1)
4580aaf6155SRoy Marples goto eexit;
4590aaf6155SRoy Marples
4607827cba2SAaron LI if ((af = dhcpcd_ifafwaiting(ifp)) != AF_MAX) {
4618d36e1dfSRoy Marples if (efprintf(fp, "if_afwaiting=%d", af) == -1)
4628d36e1dfSRoy Marples goto eexit;
4637827cba2SAaron LI }
4647827cba2SAaron LI if ((af = dhcpcd_afwaiting(ifp->ctx)) != AF_MAX) {
4657827cba2SAaron LI TAILQ_FOREACH(ifp2, ifp->ctx->ifaces, next) {
4667827cba2SAaron LI if ((af = dhcpcd_ifafwaiting(ifp2)) != AF_MAX)
4677827cba2SAaron LI break;
4687827cba2SAaron LI }
4697827cba2SAaron LI }
4707827cba2SAaron LI if (af != AF_MAX) {
4718d36e1dfSRoy Marples if (efprintf(fp, "af_waiting=%d", af) == -1)
4728d36e1dfSRoy Marples goto eexit;
4737827cba2SAaron LI }
4747827cba2SAaron LI if (ifo->options & DHCPCD_DEBUG) {
4758d36e1dfSRoy Marples if (efprintf(fp, "syslog_debug=true") == -1)
4768d36e1dfSRoy Marples goto eexit;
4777827cba2SAaron LI }
4787827cba2SAaron LI #ifdef INET
4797827cba2SAaron LI if (protocol == PROTO_DHCP && state && state->old) {
4808d36e1dfSRoy Marples if (dhcp_env(fp, "old", ifp,
4818d36e1dfSRoy Marples state->old, state->old_len) == -1)
4827827cba2SAaron LI goto eexit;
4838d36e1dfSRoy Marples if (append_config(fp, "old",
4847827cba2SAaron LI (const char *const *)ifo->config) == -1)
4857827cba2SAaron LI goto eexit;
4867827cba2SAaron LI }
4877827cba2SAaron LI #endif
4888d36e1dfSRoy Marples #ifdef DHCP6
4897827cba2SAaron LI if (protocol == PROTO_DHCP6 && d6_state && d6_state->old) {
4908d36e1dfSRoy Marples if (dhcp6_env(fp, "old", ifp,
4918d36e1dfSRoy Marples d6_state->old, d6_state->old_len) == -1)
4927827cba2SAaron LI goto eexit;
4937827cba2SAaron LI }
4947827cba2SAaron LI #endif
4957827cba2SAaron LI
4967827cba2SAaron LI dumplease:
4977827cba2SAaron LI #ifdef INET
4987827cba2SAaron LI #ifdef IPV4LL
4996e63cc1fSRoy Marples if (protocol == PROTO_IPV4LL && istate) {
5008d36e1dfSRoy Marples if (ipv4ll_env(fp, istate->down ? "old" : "new", ifp) == -1)
5017827cba2SAaron LI goto eexit;
5027827cba2SAaron LI }
5037827cba2SAaron LI #endif
5047827cba2SAaron LI if (protocol == PROTO_DHCP && state && state->new) {
5058d36e1dfSRoy Marples if (dhcp_env(fp, "new", ifp,
5068d36e1dfSRoy Marples state->new, state->new_len) == -1)
5077827cba2SAaron LI goto eexit;
5088d36e1dfSRoy Marples if (append_config(fp, "new",
5097827cba2SAaron LI (const char *const *)ifo->config) == -1)
5107827cba2SAaron LI goto eexit;
5117827cba2SAaron LI }
5127827cba2SAaron LI #endif
5137827cba2SAaron LI #ifdef INET6
5147827cba2SAaron LI if (protocol == PROTO_STATIC6) {
5158d36e1dfSRoy Marples if (ipv6_env(fp, "new", ifp) == -1)
5167827cba2SAaron LI goto eexit;
5177827cba2SAaron LI }
5188d36e1dfSRoy Marples #ifdef DHCP6
5197827cba2SAaron LI if (protocol == PROTO_DHCP6 && D6_STATE_RUNNING(ifp)) {
5208d36e1dfSRoy Marples if (dhcp6_env(fp, "new", ifp,
5218d36e1dfSRoy Marples d6_state->new, d6_state->new_len) == -1)
5227827cba2SAaron LI goto eexit;
5237827cba2SAaron LI }
5248d36e1dfSRoy Marples #endif
5257827cba2SAaron LI if (protocol == PROTO_RA) {
5268d36e1dfSRoy Marples if (ipv6nd_env(fp, ifp) == -1)
5277827cba2SAaron LI goto eexit;
5287827cba2SAaron LI }
5297827cba2SAaron LI #endif
5307827cba2SAaron LI
5317827cba2SAaron LI /* Add our base environment */
5327827cba2SAaron LI if (ifo->environ) {
5338d36e1dfSRoy Marples for (i = 0; ifo->environ[i] != NULL; i++)
5348d36e1dfSRoy Marples if (efprintf(fp, "%s", ifo->environ[i]) == -1)
5357827cba2SAaron LI goto eexit;
5367827cba2SAaron LI }
5377827cba2SAaron LI
5388d36e1dfSRoy Marples /* Convert buffer to argv */
5398d36e1dfSRoy Marples fflush(fp);
5408d36e1dfSRoy Marples
5418d36e1dfSRoy Marples buf_pos = ftell(fp);
5428d36e1dfSRoy Marples if (buf_pos == -1) {
5438d36e1dfSRoy Marples logerr(__func__);
5448d36e1dfSRoy Marples goto eexit;
5458d36e1dfSRoy Marples }
5468d36e1dfSRoy Marples
5478d36e1dfSRoy Marples #ifndef HAVE_OPEN_MEMSTREAM
5488d36e1dfSRoy Marples size_t buf_len = (size_t)buf_pos;
5498d36e1dfSRoy Marples if (ctx->script_buflen < buf_len) {
5508d36e1dfSRoy Marples char *buf = realloc(ctx->script_buf, buf_len);
5518d36e1dfSRoy Marples if (buf == NULL)
5528d36e1dfSRoy Marples goto eexit;
5538d36e1dfSRoy Marples ctx->script_buf = buf;
5548d36e1dfSRoy Marples ctx->script_buflen = buf_len;
5558d36e1dfSRoy Marples }
5568d36e1dfSRoy Marples rewind(fp);
5578d36e1dfSRoy Marples if (fread(ctx->script_buf, sizeof(char), buf_len, fp) != buf_len)
5588d36e1dfSRoy Marples goto eexit;
5598d36e1dfSRoy Marples fclose(fp);
5608d36e1dfSRoy Marples fp = NULL;
5618d36e1dfSRoy Marples #endif
5628d36e1dfSRoy Marples
563d4fb1e02SRoy Marples if (is_stdin)
564d4fb1e02SRoy Marples return buf_pos;
565d4fb1e02SRoy Marples
566b9ccd228SRoy Marples if (script_buftoenv(ctx, ctx->script_buf, (size_t)buf_pos) == NULL)
5678d36e1dfSRoy Marples goto eexit;
5688d36e1dfSRoy Marples
5696e63cc1fSRoy Marples return buf_pos;
5707827cba2SAaron LI
5717827cba2SAaron LI eexit:
5727827cba2SAaron LI logerr(__func__);
5738d36e1dfSRoy Marples #ifndef HAVE_OPEN_MEMSTREAM
5748d36e1dfSRoy Marples if (fp != NULL)
5758d36e1dfSRoy Marples fclose(fp);
5768d36e1dfSRoy Marples #endif
5777827cba2SAaron LI return -1;
5787827cba2SAaron LI }
5797827cba2SAaron LI
5807827cba2SAaron LI static int
send_interface1(struct fd_list * fd,const struct interface * ifp,const char * reason)5818d36e1dfSRoy Marples send_interface1(struct fd_list *fd, const struct interface *ifp,
5827827cba2SAaron LI const char *reason)
5837827cba2SAaron LI {
5848d36e1dfSRoy Marples struct dhcpcd_ctx *ctx = ifp->ctx;
5858d36e1dfSRoy Marples long len;
5867827cba2SAaron LI
5876e63cc1fSRoy Marples len = make_env(ifp->ctx, ifp, reason);
5888d36e1dfSRoy Marples if (len == -1)
5897827cba2SAaron LI return -1;
5907f8103cdSRoy Marples return control_queue(fd, ctx->script_buf, (size_t)len);
5917827cba2SAaron LI }
5927827cba2SAaron LI
5937827cba2SAaron LI int
send_interface(struct fd_list * fd,const struct interface * ifp,int af)5946e63cc1fSRoy Marples send_interface(struct fd_list *fd, const struct interface *ifp, int af)
5957827cba2SAaron LI {
5967827cba2SAaron LI int retval = 0;
5977827cba2SAaron LI #ifdef INET
5987827cba2SAaron LI const struct dhcp_state *d;
5997827cba2SAaron LI #endif
6008d36e1dfSRoy Marples #ifdef DHCP6
6017827cba2SAaron LI const struct dhcp6_state *d6;
6027827cba2SAaron LI #endif
6037827cba2SAaron LI
6046e63cc1fSRoy Marples #ifndef AF_LINK
6056e63cc1fSRoy Marples #define AF_LINK AF_PACKET
6066e63cc1fSRoy Marples #endif
6076e63cc1fSRoy Marples
6086e63cc1fSRoy Marples if (af == AF_UNSPEC || af == AF_LINK) {
6096e63cc1fSRoy Marples const char *reason;
6106e63cc1fSRoy Marples
6117827cba2SAaron LI switch (ifp->carrier) {
6127827cba2SAaron LI case LINK_UP:
6137827cba2SAaron LI reason = "CARRIER";
6147827cba2SAaron LI break;
6157827cba2SAaron LI case LINK_DOWN:
6167827cba2SAaron LI reason = "NOCARRIER";
6177827cba2SAaron LI break;
6187827cba2SAaron LI default:
6197827cba2SAaron LI reason = "UNKNOWN";
6207827cba2SAaron LI break;
6217827cba2SAaron LI }
6226e63cc1fSRoy Marples if (fd != NULL) {
6237827cba2SAaron LI if (send_interface1(fd, ifp, reason) == -1)
6247827cba2SAaron LI retval = -1;
6256e63cc1fSRoy Marples } else
6266e63cc1fSRoy Marples retval++;
6276e63cc1fSRoy Marples }
6286e63cc1fSRoy Marples
6297827cba2SAaron LI #ifdef INET
6306e63cc1fSRoy Marples if (af == AF_UNSPEC || af == AF_INET) {
6317827cba2SAaron LI if (D_STATE_RUNNING(ifp)) {
6327827cba2SAaron LI d = D_CSTATE(ifp);
6336e63cc1fSRoy Marples if (fd != NULL) {
6347827cba2SAaron LI if (send_interface1(fd, ifp, d->reason) == -1)
6357827cba2SAaron LI retval = -1;
6366e63cc1fSRoy Marples } else
6376e63cc1fSRoy Marples retval++;
6387827cba2SAaron LI }
6397827cba2SAaron LI #ifdef IPV4LL
6407827cba2SAaron LI if (IPV4LL_STATE_RUNNING(ifp)) {
6416e63cc1fSRoy Marples if (fd != NULL) {
6427827cba2SAaron LI if (send_interface1(fd, ifp, "IPV4LL") == -1)
6437827cba2SAaron LI retval = -1;
6446e63cc1fSRoy Marples } else
6456e63cc1fSRoy Marples retval++;
6467827cba2SAaron LI }
6477827cba2SAaron LI #endif
6486e63cc1fSRoy Marples }
6497827cba2SAaron LI #endif
6507827cba2SAaron LI
6517827cba2SAaron LI #ifdef INET6
6526e63cc1fSRoy Marples if (af == AF_UNSPEC || af == AF_INET6) {
6537827cba2SAaron LI if (IPV6_STATE_RUNNING(ifp)) {
6546e63cc1fSRoy Marples if (fd != NULL) {
6557827cba2SAaron LI if (send_interface1(fd, ifp, "STATIC6") == -1)
6567827cba2SAaron LI retval = -1;
6576e63cc1fSRoy Marples } else
6586e63cc1fSRoy Marples retval++;
6597827cba2SAaron LI }
6607827cba2SAaron LI if (RS_STATE_RUNNING(ifp)) {
6616e63cc1fSRoy Marples if (fd != NULL) {
6626e63cc1fSRoy Marples if (send_interface1(fd, ifp,
6636e63cc1fSRoy Marples "ROUTERADVERT") == -1)
6647827cba2SAaron LI retval = -1;
6656e63cc1fSRoy Marples } else
6666e63cc1fSRoy Marples retval++;
6677827cba2SAaron LI }
6688d36e1dfSRoy Marples #ifdef DHCP6
6697827cba2SAaron LI if (D6_STATE_RUNNING(ifp)) {
6707827cba2SAaron LI d6 = D6_CSTATE(ifp);
6716e63cc1fSRoy Marples if (fd != NULL) {
6727827cba2SAaron LI if (send_interface1(fd, ifp, d6->reason) == -1)
6737827cba2SAaron LI retval = -1;
6746e63cc1fSRoy Marples } else
6756e63cc1fSRoy Marples retval++;
6767827cba2SAaron LI }
6777827cba2SAaron LI #endif
6786e63cc1fSRoy Marples }
6798d36e1dfSRoy Marples #endif
6807827cba2SAaron LI
6817827cba2SAaron LI return retval;
6827827cba2SAaron LI }
6837827cba2SAaron LI
6846e63cc1fSRoy Marples static int
script_status(const char * script,int status)685*a85d0907SRoy Marples script_status(const char *script, int status)
686*a85d0907SRoy Marples {
687*a85d0907SRoy Marples
688*a85d0907SRoy Marples if (WIFEXITED(status)) {
689*a85d0907SRoy Marples if (WEXITSTATUS(status))
690*a85d0907SRoy Marples logerrx("%s: %s: WEXITSTATUS %d",
691*a85d0907SRoy Marples __func__, script, WEXITSTATUS(status));
692*a85d0907SRoy Marples } else if (WIFSIGNALED(status))
693*a85d0907SRoy Marples logerrx("%s: %s: %s",
694*a85d0907SRoy Marples __func__, script, strsignal(WTERMSIG(status)));
695*a85d0907SRoy Marples
696*a85d0907SRoy Marples return WEXITSTATUS(status);
697*a85d0907SRoy Marples }
698*a85d0907SRoy Marples
699*a85d0907SRoy Marples static int
script_run(struct dhcpcd_ctx * ctx,char ** argv)7006e63cc1fSRoy Marples script_run(struct dhcpcd_ctx *ctx, char **argv)
7017827cba2SAaron LI {
7027827cba2SAaron LI pid_t pid;
703*a85d0907SRoy Marples int status;
7047827cba2SAaron LI
705280986e4SRoy Marples pid = script_exec(argv, ctx->script_env);
706*a85d0907SRoy Marples if (pid == -1) {
7077827cba2SAaron LI logerr("%s: %s", __func__, argv[0]);
708*a85d0907SRoy Marples return -1;
709*a85d0907SRoy Marples } else if (pid == 0)
710*a85d0907SRoy Marples return 0;
711*a85d0907SRoy Marples
7127827cba2SAaron LI /* Wait for the script to finish */
7137827cba2SAaron LI while (waitpid(pid, &status, 0) == -1) {
7147827cba2SAaron LI if (errno != EINTR) {
7157827cba2SAaron LI logerr("%s: waitpid", __func__);
7167827cba2SAaron LI status = 0;
7177827cba2SAaron LI break;
7187827cba2SAaron LI }
7197827cba2SAaron LI }
720*a85d0907SRoy Marples return script_status(argv[0], status);
7216e63cc1fSRoy Marples }
7226e63cc1fSRoy Marples
7236e63cc1fSRoy Marples int
script_dump(const char * env,size_t len)724d4fb1e02SRoy Marples script_dump(const char *env, size_t len)
725d4fb1e02SRoy Marples {
726d4fb1e02SRoy Marples const char *ep = env + len;
727d4fb1e02SRoy Marples
728d4fb1e02SRoy Marples if (len == 0)
729d4fb1e02SRoy Marples return 0;
730d4fb1e02SRoy Marples
731d4fb1e02SRoy Marples if (*(ep - 1) != '\0') {
732d4fb1e02SRoy Marples errno = EINVAL;
733d4fb1e02SRoy Marples return -1;
734d4fb1e02SRoy Marples }
735d4fb1e02SRoy Marples
736d4fb1e02SRoy Marples for (; env < ep; env += strlen(env) + 1) {
737d4fb1e02SRoy Marples if (strncmp(env, "new_", 4) == 0)
738d4fb1e02SRoy Marples env += 4;
739d4fb1e02SRoy Marples printf("%s\n", env);
740d4fb1e02SRoy Marples }
741d4fb1e02SRoy Marples return 0;
742d4fb1e02SRoy Marples }
743d4fb1e02SRoy Marples
744d4fb1e02SRoy Marples int
script_runreason(const struct interface * ifp,const char * reason)7456e63cc1fSRoy Marples script_runreason(const struct interface *ifp, const char *reason)
7466e63cc1fSRoy Marples {
7476e63cc1fSRoy Marples struct dhcpcd_ctx *ctx = ifp->ctx;
7486e63cc1fSRoy Marples char *argv[2];
7496e63cc1fSRoy Marples int status = 0;
7506e63cc1fSRoy Marples struct fd_list *fd;
751d4fb1e02SRoy Marples long buflen;
7526e63cc1fSRoy Marples
753d4fb1e02SRoy Marples if (ctx->script == NULL &&
7546e63cc1fSRoy Marples TAILQ_FIRST(&ifp->ctx->control_fds) == NULL)
7556e63cc1fSRoy Marples return 0;
7566e63cc1fSRoy Marples
7576e63cc1fSRoy Marples /* Make our env */
758d4fb1e02SRoy Marples if ((buflen = make_env(ifp->ctx, ifp, reason)) == -1) {
7596e63cc1fSRoy Marples logerr(__func__);
7606e63cc1fSRoy Marples return -1;
7616e63cc1fSRoy Marples }
7626e63cc1fSRoy Marples
763d4fb1e02SRoy Marples if (strncmp(reason, "DUMP", 4) == 0)
764d4fb1e02SRoy Marples return script_dump(ctx->script_buf, (size_t)buflen);
765d4fb1e02SRoy Marples
766d4fb1e02SRoy Marples if (ctx->script == NULL)
7676e63cc1fSRoy Marples goto send_listeners;
7686e63cc1fSRoy Marples
769d4fb1e02SRoy Marples argv[0] = ctx->script;
7706e63cc1fSRoy Marples argv[1] = NULL;
771a0d9933aSRoy Marples logdebugx("%s: executing: %s %s", ifp->name, argv[0], reason);
7726e63cc1fSRoy Marples
7736e63cc1fSRoy Marples #ifdef PRIVSEP
774*a85d0907SRoy Marples if (IN_PRIVSEP(ctx)) {
775*a85d0907SRoy Marples ssize_t err;
776*a85d0907SRoy Marples
777*a85d0907SRoy Marples err = ps_root_script(ctx, ctx->script_buf, (size_t)buflen);
778*a85d0907SRoy Marples if (err == -1)
7796e63cc1fSRoy Marples logerr(__func__);
780*a85d0907SRoy Marples else
781*a85d0907SRoy Marples script_status(ctx->script, (int)err);
7826e63cc1fSRoy Marples goto send_listeners;
7836e63cc1fSRoy Marples }
7846e63cc1fSRoy Marples #endif
7856e63cc1fSRoy Marples
786d4fb1e02SRoy Marples script_run(ctx, argv);
7876e63cc1fSRoy Marples
7887827cba2SAaron LI send_listeners:
7897827cba2SAaron LI /* Send to our listeners */
7907827cba2SAaron LI status = 0;
7918d36e1dfSRoy Marples TAILQ_FOREACH(fd, &ctx->control_fds, next) {
7927827cba2SAaron LI if (!(fd->flags & FD_LISTEN))
7937827cba2SAaron LI continue;
7947f8103cdSRoy Marples if (control_queue(fd, ctx->script_buf, ctx->script_buflen)== -1)
7957827cba2SAaron LI logerr("%s: control_queue", __func__);
7967827cba2SAaron LI else
7977827cba2SAaron LI status = 1;
7987827cba2SAaron LI }
7997827cba2SAaron LI
8006e63cc1fSRoy Marples return status;
8017827cba2SAaron LI }
802