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