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; 345cc34ba0cSRoy Marples if (protocols[protocol] != NULL) { 346cc34ba0cSRoy Marples if (efprintf(fp, "protocol=%s", 347cc34ba0cSRoy Marples protocols[protocol]) == -1) 348cc34ba0cSRoy Marples goto eexit; 349d4fb1e02SRoy Marples } 350cc34ba0cSRoy Marples } 351cc34ba0cSRoy Marples if (ifp->ctx->options & DHCPCD_DUMPLEASE && protocol != PROTO_LINK) 3527827cba2SAaron LI goto dumplease; 353*b2927f2bSRoy Marples if (efprintf(fp, "if_configured=%s", 354*b2927f2bSRoy Marples ifo->options & DHCPCD_CONFIGURE ? "true" : "false") == -1) 355*b2927f2bSRoy Marples goto eexit; 3568d36e1dfSRoy Marples if (efprintf(fp, "ifcarrier=%s", 3577827cba2SAaron LI ifp->carrier == LINK_UNKNOWN ? "unknown" : 3588d36e1dfSRoy Marples ifp->carrier == LINK_UP ? "up" : "down") == -1) 3598d36e1dfSRoy Marples goto eexit; 3608d36e1dfSRoy Marples if (efprintf(fp, "ifmetric=%d", ifp->metric) == -1) 3618d36e1dfSRoy Marples goto eexit; 3628d36e1dfSRoy Marples if (efprintf(fp, "ifwireless=%d", ifp->wireless) == -1) 3638d36e1dfSRoy Marples goto eexit; 3648d36e1dfSRoy Marples if (efprintf(fp, "ifflags=%u", ifp->flags) == -1) 3658d36e1dfSRoy Marples goto eexit; 3668d36e1dfSRoy Marples if (efprintf(fp, "ifmtu=%d", if_getmtu(ifp)) == -1) 3678d36e1dfSRoy Marples goto eexit; 368cc34ba0cSRoy Marples if (ifp->wireless) { 369cc34ba0cSRoy Marples char pssid[IF_SSIDLEN * 4]; 370cc34ba0cSRoy Marples 371cc34ba0cSRoy Marples if (print_string(pssid, sizeof(pssid), OT_ESCSTRING, 372cc34ba0cSRoy Marples ifp->ssid, ifp->ssid_len) != -1) 373cc34ba0cSRoy Marples { 374cc34ba0cSRoy Marples if (efprintf(fp, "ifssid=%s", pssid) == -1) 375cc34ba0cSRoy Marples goto eexit; 376cc34ba0cSRoy Marples } 377cc34ba0cSRoy Marples } 378cc34ba0cSRoy Marples if (*ifp->profile != '\0') { 379cc34ba0cSRoy Marples if (efprintf(fp, "profile=%s", ifp->profile) == -1) 380cc34ba0cSRoy Marples goto eexit; 381cc34ba0cSRoy Marples } 382cc34ba0cSRoy Marples if (ifp->ctx->options & DHCPCD_DUMPLEASE) 383cc34ba0cSRoy Marples goto dumplease; 3848d36e1dfSRoy Marples 3858d36e1dfSRoy Marples if (fprintf(fp, "interface_order=") == -1) 3868d36e1dfSRoy Marples goto eexit; 3877827cba2SAaron LI TAILQ_FOREACH(ifp2, ifp->ctx->ifaces, next) { 3888d36e1dfSRoy Marples if (ifp2 != TAILQ_FIRST(ifp->ctx->ifaces)) { 3898d36e1dfSRoy Marples if (fputc(' ', fp) == EOF) 3908d36e1dfSRoy Marples return -1; 3917827cba2SAaron LI } 3928d36e1dfSRoy Marples if (fprintf(fp, "%s", ifp2->name) == -1) 3938d36e1dfSRoy Marples return -1; 3947827cba2SAaron LI } 3958d36e1dfSRoy Marples if (fputc('\0', fp) == EOF) 3968d36e1dfSRoy Marples return -1; 3978d36e1dfSRoy Marples 3987827cba2SAaron LI if (strcmp(reason, "STOPPED") == 0) { 3998d36e1dfSRoy Marples if (efprintf(fp, "if_up=false") == -1) 4008d36e1dfSRoy Marples goto eexit; 4018d36e1dfSRoy Marples if (efprintf(fp, "if_down=%s", 4028d36e1dfSRoy Marples ifo->options & DHCPCD_RELEASE ? "true" : "false") == -1) 4038d36e1dfSRoy Marples goto eexit; 4047827cba2SAaron LI } else if (strcmp(reason, "TEST") == 0 || 4057827cba2SAaron LI strcmp(reason, "PREINIT") == 0 || 4067827cba2SAaron LI strcmp(reason, "CARRIER") == 0 || 4077827cba2SAaron LI strcmp(reason, "UNKNOWN") == 0) 4087827cba2SAaron LI { 4098d36e1dfSRoy Marples if (efprintf(fp, "if_up=false") == -1) 4108d36e1dfSRoy Marples goto eexit; 4118d36e1dfSRoy Marples if (efprintf(fp, "if_down=false") == -1) 4128d36e1dfSRoy Marples goto eexit; 4137827cba2SAaron LI } else if (1 == 2 /* appease ifdefs */ 4147827cba2SAaron LI #ifdef INET 4157827cba2SAaron LI || (protocol == PROTO_DHCP && state && state->new) 4167827cba2SAaron LI #ifdef IPV4LL 4177827cba2SAaron LI || (protocol == PROTO_IPV4LL && IPV4LL_STATE_RUNNING(ifp)) 4187827cba2SAaron LI #endif 4197827cba2SAaron LI #endif 4207827cba2SAaron LI #ifdef INET6 4217827cba2SAaron LI || (protocol == PROTO_STATIC6 && IPV6_STATE_RUNNING(ifp)) 4228d36e1dfSRoy Marples #ifdef DHCP6 4237827cba2SAaron LI || (protocol == PROTO_DHCP6 && d6_state && d6_state->new) 4248d36e1dfSRoy Marples #endif 4257827cba2SAaron LI || (protocol == PROTO_RA && ipv6nd_hasra(ifp)) 4267827cba2SAaron LI #endif 4277827cba2SAaron LI ) 4287827cba2SAaron LI { 4298d36e1dfSRoy Marples if (efprintf(fp, "if_up=true") == -1) 4308d36e1dfSRoy Marples goto eexit; 4318d36e1dfSRoy Marples if (efprintf(fp, "if_down=false") == -1) 4328d36e1dfSRoy Marples goto eexit; 4337827cba2SAaron LI } else { 4348d36e1dfSRoy Marples if (efprintf(fp, "if_up=false") == -1) 4358d36e1dfSRoy Marples goto eexit; 4368d36e1dfSRoy Marples if (efprintf(fp, "if_down=true") == -1) 4378d36e1dfSRoy Marples goto eexit; 4387827cba2SAaron LI } 4397827cba2SAaron LI if ((af = dhcpcd_ifafwaiting(ifp)) != AF_MAX) { 4408d36e1dfSRoy Marples if (efprintf(fp, "if_afwaiting=%d", af) == -1) 4418d36e1dfSRoy Marples goto eexit; 4427827cba2SAaron LI } 4437827cba2SAaron LI if ((af = dhcpcd_afwaiting(ifp->ctx)) != AF_MAX) { 4447827cba2SAaron LI TAILQ_FOREACH(ifp2, ifp->ctx->ifaces, next) { 4457827cba2SAaron LI if ((af = dhcpcd_ifafwaiting(ifp2)) != AF_MAX) 4467827cba2SAaron LI break; 4477827cba2SAaron LI } 4487827cba2SAaron LI } 4497827cba2SAaron LI if (af != AF_MAX) { 4508d36e1dfSRoy Marples if (efprintf(fp, "af_waiting=%d", af) == -1) 4518d36e1dfSRoy Marples goto eexit; 4527827cba2SAaron LI } 4537827cba2SAaron LI if (ifo->options & DHCPCD_DEBUG) { 4548d36e1dfSRoy Marples if (efprintf(fp, "syslog_debug=true") == -1) 4558d36e1dfSRoy Marples goto eexit; 4567827cba2SAaron LI } 4577827cba2SAaron LI #ifdef INET 4587827cba2SAaron LI if (protocol == PROTO_DHCP && state && state->old) { 4598d36e1dfSRoy Marples if (dhcp_env(fp, "old", ifp, 4608d36e1dfSRoy Marples state->old, state->old_len) == -1) 4617827cba2SAaron LI goto eexit; 4628d36e1dfSRoy Marples if (append_config(fp, "old", 4637827cba2SAaron LI (const char *const *)ifo->config) == -1) 4647827cba2SAaron LI goto eexit; 4657827cba2SAaron LI } 4667827cba2SAaron LI #endif 4678d36e1dfSRoy Marples #ifdef DHCP6 4687827cba2SAaron LI if (protocol == PROTO_DHCP6 && d6_state && d6_state->old) { 4698d36e1dfSRoy Marples if (dhcp6_env(fp, "old", ifp, 4708d36e1dfSRoy Marples d6_state->old, d6_state->old_len) == -1) 4717827cba2SAaron LI goto eexit; 4727827cba2SAaron LI } 4737827cba2SAaron LI #endif 4747827cba2SAaron LI 4757827cba2SAaron LI dumplease: 4767827cba2SAaron LI #ifdef INET 4777827cba2SAaron LI #ifdef IPV4LL 4786e63cc1fSRoy Marples if (protocol == PROTO_IPV4LL && istate) { 4798d36e1dfSRoy Marples if (ipv4ll_env(fp, istate->down ? "old" : "new", ifp) == -1) 4807827cba2SAaron LI goto eexit; 4817827cba2SAaron LI } 4827827cba2SAaron LI #endif 4837827cba2SAaron LI if (protocol == PROTO_DHCP && state && state->new) { 4848d36e1dfSRoy Marples if (dhcp_env(fp, "new", ifp, 4858d36e1dfSRoy Marples state->new, state->new_len) == -1) 4867827cba2SAaron LI goto eexit; 4878d36e1dfSRoy Marples if (append_config(fp, "new", 4887827cba2SAaron LI (const char *const *)ifo->config) == -1) 4897827cba2SAaron LI goto eexit; 4907827cba2SAaron LI } 4917827cba2SAaron LI #endif 4927827cba2SAaron LI #ifdef INET6 4937827cba2SAaron LI if (protocol == PROTO_STATIC6) { 4948d36e1dfSRoy Marples if (ipv6_env(fp, "new", ifp) == -1) 4957827cba2SAaron LI goto eexit; 4967827cba2SAaron LI } 4978d36e1dfSRoy Marples #ifdef DHCP6 4987827cba2SAaron LI if (protocol == PROTO_DHCP6 && D6_STATE_RUNNING(ifp)) { 4998d36e1dfSRoy Marples if (dhcp6_env(fp, "new", ifp, 5008d36e1dfSRoy Marples d6_state->new, d6_state->new_len) == -1) 5017827cba2SAaron LI goto eexit; 5027827cba2SAaron LI } 5038d36e1dfSRoy Marples #endif 5047827cba2SAaron LI if (protocol == PROTO_RA) { 5058d36e1dfSRoy Marples if (ipv6nd_env(fp, ifp) == -1) 5067827cba2SAaron LI goto eexit; 5077827cba2SAaron LI } 5087827cba2SAaron LI #endif 5097827cba2SAaron LI 5107827cba2SAaron LI /* Add our base environment */ 5117827cba2SAaron LI if (ifo->environ) { 5128d36e1dfSRoy Marples for (i = 0; ifo->environ[i] != NULL; i++) 5138d36e1dfSRoy Marples if (efprintf(fp, "%s", ifo->environ[i]) == -1) 5147827cba2SAaron LI goto eexit; 5157827cba2SAaron LI } 5167827cba2SAaron LI 5178d36e1dfSRoy Marples /* Convert buffer to argv */ 5188d36e1dfSRoy Marples fflush(fp); 5198d36e1dfSRoy Marples 5208d36e1dfSRoy Marples buf_pos = ftell(fp); 5218d36e1dfSRoy Marples if (buf_pos == -1) { 5228d36e1dfSRoy Marples logerr(__func__); 5238d36e1dfSRoy Marples goto eexit; 5248d36e1dfSRoy Marples } 5258d36e1dfSRoy Marples 5268d36e1dfSRoy Marples #ifndef HAVE_OPEN_MEMSTREAM 5278d36e1dfSRoy Marples size_t buf_len = (size_t)buf_pos; 5288d36e1dfSRoy Marples if (ctx->script_buflen < buf_len) { 5298d36e1dfSRoy Marples char *buf = realloc(ctx->script_buf, buf_len); 5308d36e1dfSRoy Marples if (buf == NULL) 5318d36e1dfSRoy Marples goto eexit; 5328d36e1dfSRoy Marples ctx->script_buf = buf; 5338d36e1dfSRoy Marples ctx->script_buflen = buf_len; 5348d36e1dfSRoy Marples } 5358d36e1dfSRoy Marples rewind(fp); 5368d36e1dfSRoy Marples if (fread(ctx->script_buf, sizeof(char), buf_len, fp) != buf_len) 5378d36e1dfSRoy Marples goto eexit; 5388d36e1dfSRoy Marples fclose(fp); 5398d36e1dfSRoy Marples fp = NULL; 5408d36e1dfSRoy Marples #endif 5418d36e1dfSRoy Marples 542d4fb1e02SRoy Marples if (is_stdin) 543d4fb1e02SRoy Marples return buf_pos; 544d4fb1e02SRoy Marples 545b9ccd228SRoy Marples if (script_buftoenv(ctx, ctx->script_buf, (size_t)buf_pos) == NULL) 5468d36e1dfSRoy Marples goto eexit; 5478d36e1dfSRoy Marples 5486e63cc1fSRoy Marples return buf_pos; 5497827cba2SAaron LI 5507827cba2SAaron LI eexit: 5517827cba2SAaron LI logerr(__func__); 5528d36e1dfSRoy Marples #ifndef HAVE_OPEN_MEMSTREAM 5538d36e1dfSRoy Marples if (fp != NULL) 5548d36e1dfSRoy Marples fclose(fp); 5558d36e1dfSRoy Marples #endif 5567827cba2SAaron LI return -1; 5577827cba2SAaron LI } 5587827cba2SAaron LI 5597827cba2SAaron LI static int 5608d36e1dfSRoy Marples send_interface1(struct fd_list *fd, const struct interface *ifp, 5617827cba2SAaron LI const char *reason) 5627827cba2SAaron LI { 5638d36e1dfSRoy Marples struct dhcpcd_ctx *ctx = ifp->ctx; 5648d36e1dfSRoy Marples long len; 5657827cba2SAaron LI 5666e63cc1fSRoy Marples len = make_env(ifp->ctx, ifp, reason); 5678d36e1dfSRoy Marples if (len == -1) 5687827cba2SAaron LI return -1; 5697f8103cdSRoy Marples return control_queue(fd, ctx->script_buf, (size_t)len); 5707827cba2SAaron LI } 5717827cba2SAaron LI 5727827cba2SAaron LI int 5736e63cc1fSRoy Marples send_interface(struct fd_list *fd, const struct interface *ifp, int af) 5747827cba2SAaron LI { 5757827cba2SAaron LI int retval = 0; 5767827cba2SAaron LI #ifdef INET 5777827cba2SAaron LI const struct dhcp_state *d; 5787827cba2SAaron LI #endif 5798d36e1dfSRoy Marples #ifdef DHCP6 5807827cba2SAaron LI const struct dhcp6_state *d6; 5817827cba2SAaron LI #endif 5827827cba2SAaron LI 5836e63cc1fSRoy Marples #ifndef AF_LINK 5846e63cc1fSRoy Marples #define AF_LINK AF_PACKET 5856e63cc1fSRoy Marples #endif 5866e63cc1fSRoy Marples 5876e63cc1fSRoy Marples if (af == AF_UNSPEC || af == AF_LINK) { 5886e63cc1fSRoy Marples const char *reason; 5896e63cc1fSRoy Marples 5907827cba2SAaron LI switch (ifp->carrier) { 5917827cba2SAaron LI case LINK_UP: 5927827cba2SAaron LI reason = "CARRIER"; 5937827cba2SAaron LI break; 5947827cba2SAaron LI case LINK_DOWN: 5957827cba2SAaron LI reason = "NOCARRIER"; 5967827cba2SAaron LI break; 5977827cba2SAaron LI default: 5987827cba2SAaron LI reason = "UNKNOWN"; 5997827cba2SAaron LI break; 6007827cba2SAaron LI } 6016e63cc1fSRoy Marples if (fd != NULL) { 6027827cba2SAaron LI if (send_interface1(fd, ifp, reason) == -1) 6037827cba2SAaron LI retval = -1; 6046e63cc1fSRoy Marples } else 6056e63cc1fSRoy Marples retval++; 6066e63cc1fSRoy Marples } 6076e63cc1fSRoy Marples 6087827cba2SAaron LI #ifdef INET 6096e63cc1fSRoy Marples if (af == AF_UNSPEC || af == AF_INET) { 6107827cba2SAaron LI if (D_STATE_RUNNING(ifp)) { 6117827cba2SAaron LI d = D_CSTATE(ifp); 6126e63cc1fSRoy Marples if (fd != NULL) { 6137827cba2SAaron LI if (send_interface1(fd, ifp, d->reason) == -1) 6147827cba2SAaron LI retval = -1; 6156e63cc1fSRoy Marples } else 6166e63cc1fSRoy Marples retval++; 6177827cba2SAaron LI } 6187827cba2SAaron LI #ifdef IPV4LL 6197827cba2SAaron LI if (IPV4LL_STATE_RUNNING(ifp)) { 6206e63cc1fSRoy Marples if (fd != NULL) { 6217827cba2SAaron LI if (send_interface1(fd, ifp, "IPV4LL") == -1) 6227827cba2SAaron LI retval = -1; 6236e63cc1fSRoy Marples } else 6246e63cc1fSRoy Marples retval++; 6257827cba2SAaron LI } 6267827cba2SAaron LI #endif 6276e63cc1fSRoy Marples } 6287827cba2SAaron LI #endif 6297827cba2SAaron LI 6307827cba2SAaron LI #ifdef INET6 6316e63cc1fSRoy Marples if (af == AF_UNSPEC || af == AF_INET6) { 6327827cba2SAaron LI if (IPV6_STATE_RUNNING(ifp)) { 6336e63cc1fSRoy Marples if (fd != NULL) { 6347827cba2SAaron LI if (send_interface1(fd, ifp, "STATIC6") == -1) 6357827cba2SAaron LI retval = -1; 6366e63cc1fSRoy Marples } else 6376e63cc1fSRoy Marples retval++; 6387827cba2SAaron LI } 6397827cba2SAaron LI if (RS_STATE_RUNNING(ifp)) { 6406e63cc1fSRoy Marples if (fd != NULL) { 6416e63cc1fSRoy Marples if (send_interface1(fd, ifp, 6426e63cc1fSRoy Marples "ROUTERADVERT") == -1) 6437827cba2SAaron LI retval = -1; 6446e63cc1fSRoy Marples } else 6456e63cc1fSRoy Marples retval++; 6467827cba2SAaron LI } 6478d36e1dfSRoy Marples #ifdef DHCP6 6487827cba2SAaron LI if (D6_STATE_RUNNING(ifp)) { 6497827cba2SAaron LI d6 = D6_CSTATE(ifp); 6506e63cc1fSRoy Marples if (fd != NULL) { 6517827cba2SAaron LI if (send_interface1(fd, ifp, d6->reason) == -1) 6527827cba2SAaron LI retval = -1; 6536e63cc1fSRoy Marples } else 6546e63cc1fSRoy Marples retval++; 6557827cba2SAaron LI } 6567827cba2SAaron LI #endif 6576e63cc1fSRoy Marples } 6588d36e1dfSRoy Marples #endif 6597827cba2SAaron LI 6607827cba2SAaron LI return retval; 6617827cba2SAaron LI } 6627827cba2SAaron LI 6636e63cc1fSRoy Marples static int 6646e63cc1fSRoy Marples script_run(struct dhcpcd_ctx *ctx, char **argv) 6657827cba2SAaron LI { 6667827cba2SAaron LI pid_t pid; 6677827cba2SAaron LI int status = 0; 6687827cba2SAaron LI 669280986e4SRoy Marples pid = script_exec(argv, ctx->script_env); 6707827cba2SAaron LI if (pid == -1) 6717827cba2SAaron LI logerr("%s: %s", __func__, argv[0]); 6727827cba2SAaron LI else if (pid != 0) { 6737827cba2SAaron LI /* Wait for the script to finish */ 6747827cba2SAaron LI while (waitpid(pid, &status, 0) == -1) { 6757827cba2SAaron LI if (errno != EINTR) { 6767827cba2SAaron LI logerr("%s: waitpid", __func__); 6777827cba2SAaron LI status = 0; 6787827cba2SAaron LI break; 6797827cba2SAaron LI } 6807827cba2SAaron LI } 6817827cba2SAaron LI if (WIFEXITED(status)) { 6827827cba2SAaron LI if (WEXITSTATUS(status)) 6837827cba2SAaron LI logerrx("%s: %s: WEXITSTATUS %d", 6847827cba2SAaron LI __func__, argv[0], WEXITSTATUS(status)); 6857827cba2SAaron LI } else if (WIFSIGNALED(status)) 6867827cba2SAaron LI logerrx("%s: %s: %s", 6877827cba2SAaron LI __func__, argv[0], strsignal(WTERMSIG(status))); 6887827cba2SAaron LI } 6897827cba2SAaron LI 6906e63cc1fSRoy Marples return WEXITSTATUS(status); 6916e63cc1fSRoy Marples } 6926e63cc1fSRoy Marples 6936e63cc1fSRoy Marples int 694d4fb1e02SRoy Marples script_dump(const char *env, size_t len) 695d4fb1e02SRoy Marples { 696d4fb1e02SRoy Marples const char *ep = env + len; 697d4fb1e02SRoy Marples 698d4fb1e02SRoy Marples if (len == 0) 699d4fb1e02SRoy Marples return 0; 700d4fb1e02SRoy Marples 701d4fb1e02SRoy Marples if (*(ep - 1) != '\0') { 702d4fb1e02SRoy Marples errno = EINVAL; 703d4fb1e02SRoy Marples return -1; 704d4fb1e02SRoy Marples } 705d4fb1e02SRoy Marples 706d4fb1e02SRoy Marples for (; env < ep; env += strlen(env) + 1) { 707d4fb1e02SRoy Marples if (strncmp(env, "new_", 4) == 0) 708d4fb1e02SRoy Marples env += 4; 709d4fb1e02SRoy Marples printf("%s\n", env); 710d4fb1e02SRoy Marples } 711d4fb1e02SRoy Marples return 0; 712d4fb1e02SRoy Marples } 713d4fb1e02SRoy Marples 714d4fb1e02SRoy Marples int 7156e63cc1fSRoy Marples script_runreason(const struct interface *ifp, const char *reason) 7166e63cc1fSRoy Marples { 7176e63cc1fSRoy Marples struct dhcpcd_ctx *ctx = ifp->ctx; 7186e63cc1fSRoy Marples char *argv[2]; 7196e63cc1fSRoy Marples int status = 0; 7206e63cc1fSRoy Marples struct fd_list *fd; 721d4fb1e02SRoy Marples long buflen; 7226e63cc1fSRoy Marples 723d4fb1e02SRoy Marples if (ctx->script == NULL && 7246e63cc1fSRoy Marples TAILQ_FIRST(&ifp->ctx->control_fds) == NULL) 7256e63cc1fSRoy Marples return 0; 7266e63cc1fSRoy Marples 7276e63cc1fSRoy Marples /* Make our env */ 728d4fb1e02SRoy Marples if ((buflen = make_env(ifp->ctx, ifp, reason)) == -1) { 7296e63cc1fSRoy Marples logerr(__func__); 7306e63cc1fSRoy Marples return -1; 7316e63cc1fSRoy Marples } 7326e63cc1fSRoy Marples 733d4fb1e02SRoy Marples if (strncmp(reason, "DUMP", 4) == 0) 734d4fb1e02SRoy Marples return script_dump(ctx->script_buf, (size_t)buflen); 735d4fb1e02SRoy Marples 736d4fb1e02SRoy Marples if (ctx->script == NULL) 7376e63cc1fSRoy Marples goto send_listeners; 7386e63cc1fSRoy Marples 739d4fb1e02SRoy Marples argv[0] = ctx->script; 7406e63cc1fSRoy Marples argv[1] = NULL; 741a0d9933aSRoy Marples logdebugx("%s: executing: %s %s", ifp->name, argv[0], reason); 7426e63cc1fSRoy Marples 7436e63cc1fSRoy Marples #ifdef PRIVSEP 7446e63cc1fSRoy Marples if (ctx->options & DHCPCD_PRIVSEP) { 745d4fb1e02SRoy Marples if (ps_root_script(ctx, 7466e63cc1fSRoy Marples ctx->script_buf, ctx->script_buflen) == -1) 7476e63cc1fSRoy Marples logerr(__func__); 7486e63cc1fSRoy Marples goto send_listeners; 7496e63cc1fSRoy Marples } 7506e63cc1fSRoy Marples #endif 7516e63cc1fSRoy Marples 752d4fb1e02SRoy Marples script_run(ctx, argv); 7536e63cc1fSRoy Marples 7547827cba2SAaron LI send_listeners: 7557827cba2SAaron LI /* Send to our listeners */ 7567827cba2SAaron LI status = 0; 7578d36e1dfSRoy Marples TAILQ_FOREACH(fd, &ctx->control_fds, next) { 7587827cba2SAaron LI if (!(fd->flags & FD_LISTEN)) 7597827cba2SAaron LI continue; 7607f8103cdSRoy Marples if (control_queue(fd, ctx->script_buf, ctx->script_buflen)== -1) 7617827cba2SAaron LI logerr("%s: control_queue", __func__); 7627827cba2SAaron LI else 7637827cba2SAaron LI status = 1; 7647827cba2SAaron LI } 7657827cba2SAaron LI 7666e63cc1fSRoy Marples return status; 7677827cba2SAaron LI } 768