1*8d36e1dfSRoy Marples /* SPDX-License-Identifier: BSD-2-Clause */ 27827cba2SAaron LI /* 37827cba2SAaron LI * dhcpcd - DHCP client daemon 4*8d36e1dfSRoy Marples * Copyright (c) 2006-2019 Roy Marples <roy@marples.name> 57827cba2SAaron LI * All rights reserved 67827cba2SAaron LI 77827cba2SAaron LI * Redistribution and use in source and binary forms, with or without 87827cba2SAaron LI * modification, are permitted provided that the following conditions 97827cba2SAaron LI * are met: 107827cba2SAaron LI * 1. Redistributions of source code must retain the above copyright 117827cba2SAaron LI * notice, this list of conditions and the following disclaimer. 127827cba2SAaron LI * 2. Redistributions in binary form must reproduce the above copyright 137827cba2SAaron LI * notice, this list of conditions and the following disclaimer in the 147827cba2SAaron LI * documentation and/or other materials provided with the distribution. 157827cba2SAaron LI * 167827cba2SAaron LI * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 177827cba2SAaron LI * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 187827cba2SAaron LI * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 197827cba2SAaron LI * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 207827cba2SAaron LI * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 217827cba2SAaron LI * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 227827cba2SAaron LI * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 237827cba2SAaron LI * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 247827cba2SAaron LI * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 257827cba2SAaron LI * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 267827cba2SAaron LI * SUCH DAMAGE. 277827cba2SAaron LI */ 287827cba2SAaron LI 297827cba2SAaron LI #include <sys/stat.h> 307827cba2SAaron LI #include <sys/uio.h> 317827cba2SAaron LI #include <sys/wait.h> 327827cba2SAaron LI 337827cba2SAaron LI #include <netinet/in.h> 347827cba2SAaron LI #include <arpa/inet.h> 357827cba2SAaron LI 36*8d36e1dfSRoy Marples #include <assert.h> 377827cba2SAaron LI #include <ctype.h> 387827cba2SAaron LI #include <errno.h> 397827cba2SAaron LI #include <signal.h> 407827cba2SAaron LI #include <spawn.h> 41*8d36e1dfSRoy Marples #include <stdarg.h> 427827cba2SAaron LI #include <stdlib.h> 437827cba2SAaron LI #include <string.h> 447827cba2SAaron LI #include <unistd.h> 457827cba2SAaron LI 467827cba2SAaron LI #include "config.h" 477827cba2SAaron LI #include "common.h" 487827cba2SAaron LI #include "dhcp.h" 497827cba2SAaron LI #include "dhcp6.h" 507827cba2SAaron LI #include "if.h" 517827cba2SAaron LI #include "if-options.h" 527827cba2SAaron LI #include "ipv4ll.h" 537827cba2SAaron LI #include "ipv6nd.h" 547827cba2SAaron LI #include "logerr.h" 557827cba2SAaron LI #include "script.h" 567827cba2SAaron LI 577827cba2SAaron LI /* Allow the OS to define another script env var name */ 587827cba2SAaron LI #ifndef RC_SVCNAME 597827cba2SAaron LI #define RC_SVCNAME "RC_SVCNAME" 607827cba2SAaron LI #endif 617827cba2SAaron LI 62*8d36e1dfSRoy Marples #define DEFAULT_PATH "/usr/bin:/usr/sbin:/bin:/sbin" 637827cba2SAaron LI 647827cba2SAaron LI static const char * const if_params[] = { 657827cba2SAaron LI "interface", 667827cba2SAaron LI "protocol", 677827cba2SAaron LI "reason", 687827cba2SAaron LI "pid", 697827cba2SAaron LI "ifcarrier", 707827cba2SAaron LI "ifmetric", 717827cba2SAaron LI "ifwireless", 727827cba2SAaron LI "ifflags", 737827cba2SAaron LI "ssid", 747827cba2SAaron LI "profile", 757827cba2SAaron LI "interface_order", 767827cba2SAaron LI NULL 777827cba2SAaron LI }; 787827cba2SAaron LI 797827cba2SAaron LI void 807827cba2SAaron LI if_printoptions(void) 817827cba2SAaron LI { 827827cba2SAaron LI const char * const *p; 837827cba2SAaron LI 847827cba2SAaron LI for (p = if_params; *p; p++) 857827cba2SAaron LI printf(" - %s\n", *p); 867827cba2SAaron LI } 877827cba2SAaron LI 887827cba2SAaron LI static int 897827cba2SAaron LI exec_script(const struct dhcpcd_ctx *ctx, char *const *argv, char *const *env) 907827cba2SAaron LI { 917827cba2SAaron LI pid_t pid; 927827cba2SAaron LI posix_spawnattr_t attr; 937827cba2SAaron LI int r; 947827cba2SAaron LI #ifdef USE_SIGNALS 957827cba2SAaron LI size_t i; 967827cba2SAaron LI short flags; 977827cba2SAaron LI sigset_t defsigs; 987827cba2SAaron LI #else 997827cba2SAaron LI UNUSED(ctx); 1007827cba2SAaron LI #endif 1017827cba2SAaron LI 1027827cba2SAaron LI /* posix_spawn is a safe way of executing another image 1037827cba2SAaron LI * and changing signals back to how they should be. */ 1047827cba2SAaron LI if (posix_spawnattr_init(&attr) == -1) 1057827cba2SAaron LI return -1; 1067827cba2SAaron LI #ifdef USE_SIGNALS 1077827cba2SAaron LI flags = POSIX_SPAWN_SETSIGMASK | POSIX_SPAWN_SETSIGDEF; 1087827cba2SAaron LI posix_spawnattr_setflags(&attr, flags); 1097827cba2SAaron LI sigemptyset(&defsigs); 1107827cba2SAaron LI for (i = 0; i < dhcpcd_signals_len; i++) 1117827cba2SAaron LI sigaddset(&defsigs, dhcpcd_signals[i]); 1127827cba2SAaron LI posix_spawnattr_setsigdefault(&attr, &defsigs); 1137827cba2SAaron LI posix_spawnattr_setsigmask(&attr, &ctx->sigset); 1147827cba2SAaron LI #endif 1157827cba2SAaron LI errno = 0; 1167827cba2SAaron LI r = posix_spawn(&pid, argv[0], NULL, &attr, argv, env); 1177827cba2SAaron LI posix_spawnattr_destroy(&attr); 1187827cba2SAaron LI if (r) { 1197827cba2SAaron LI errno = r; 1207827cba2SAaron LI return -1; 1217827cba2SAaron LI } 1227827cba2SAaron LI return pid; 1237827cba2SAaron LI } 1247827cba2SAaron LI 1257827cba2SAaron LI #ifdef INET 1267827cba2SAaron LI static int 127*8d36e1dfSRoy Marples append_config(FILE *fp, const char *prefix, const char *const *config) 1287827cba2SAaron LI { 129*8d36e1dfSRoy Marples size_t i; 1307827cba2SAaron LI 1317827cba2SAaron LI if (config == NULL) 1327827cba2SAaron LI return 0; 1337827cba2SAaron LI 134*8d36e1dfSRoy Marples /* Do we need to replace existing config rather than append? */ 1357827cba2SAaron LI for (i = 0; config[i] != NULL; i++) { 136*8d36e1dfSRoy Marples if (efprintf(fp, "%s_%s", prefix, config[i]) == -1) 1377827cba2SAaron LI return -1; 1387827cba2SAaron LI } 139*8d36e1dfSRoy Marples return 1; 1407827cba2SAaron LI } 1417827cba2SAaron LI 142*8d36e1dfSRoy Marples #endif 143*8d36e1dfSRoy 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 159*8d36e1dfSRoy Marples int 160*8d36e1dfSRoy Marples efprintf(FILE *fp, const char *fmt, ...) 1617827cba2SAaron LI { 162*8d36e1dfSRoy Marples va_list args; 163*8d36e1dfSRoy Marples int r; 164*8d36e1dfSRoy Marples 165*8d36e1dfSRoy Marples va_start(args, fmt); 166*8d36e1dfSRoy Marples r = vfprintf(fp, fmt, args); 167*8d36e1dfSRoy Marples va_end(args); 168*8d36e1dfSRoy Marples if (r == -1) 169*8d36e1dfSRoy Marples return -1; 170*8d36e1dfSRoy Marples /* Write a trailing NULL so we can easily create env strings. */ 171*8d36e1dfSRoy Marples if (fputc('\0', fp) == EOF) 172*8d36e1dfSRoy Marples return -1; 173*8d36e1dfSRoy Marples return r; 174*8d36e1dfSRoy Marples } 175*8d36e1dfSRoy Marples 176*8d36e1dfSRoy Marples static long 177*8d36e1dfSRoy Marples make_env(const struct interface *ifp, const char *reason) 178*8d36e1dfSRoy Marples { 179*8d36e1dfSRoy Marples struct dhcpcd_ctx *ctx = ifp->ctx; 180*8d36e1dfSRoy Marples FILE *fp; 181*8d36e1dfSRoy Marples char **env, **envp, *bufp, *endp, *path; 182*8d36e1dfSRoy Marples size_t nenv; 183*8d36e1dfSRoy Marples long buf_pos, i; 184*8d36e1dfSRoy Marples int protocol = PROTO_LINK; 1857827cba2SAaron LI const struct if_options *ifo = ifp->options; 1867827cba2SAaron LI const struct interface *ifp2; 1877827cba2SAaron LI int af; 1887827cba2SAaron LI #ifdef INET 1897827cba2SAaron LI const struct dhcp_state *state; 1907827cba2SAaron LI #ifdef IPV4LL 1917827cba2SAaron LI const struct ipv4ll_state *istate; 1927827cba2SAaron LI #endif 1937827cba2SAaron LI #endif 194*8d36e1dfSRoy Marples #ifdef DHCP6 1957827cba2SAaron LI const struct dhcp6_state *d6_state; 1967827cba2SAaron LI #endif 1977827cba2SAaron LI 198*8d36e1dfSRoy Marples #ifdef HAVE_OPEN_MEMSTREAM 199*8d36e1dfSRoy Marples if (ctx->script_fp == NULL) { 200*8d36e1dfSRoy Marples fp = open_memstream(&ctx->script_buf, &ctx->script_buflen); 201*8d36e1dfSRoy Marples if (fp == NULL) 202*8d36e1dfSRoy Marples goto eexit; 203*8d36e1dfSRoy Marples ctx->script_fp = fp; 204*8d36e1dfSRoy Marples } else { 205*8d36e1dfSRoy Marples fp = ctx->script_fp; 206*8d36e1dfSRoy Marples rewind(fp); 207*8d36e1dfSRoy Marples } 208*8d36e1dfSRoy Marples #else 209*8d36e1dfSRoy Marples char tmpfile[] = "/tmp/dhcpcd-script-env-XXXXXX"; 210*8d36e1dfSRoy Marples int tmpfd; 211*8d36e1dfSRoy Marples 212*8d36e1dfSRoy Marples fp = NULL; 213*8d36e1dfSRoy Marples tmpfd = mkstemp(tmpfile); 214*8d36e1dfSRoy Marples if (tmpfd == -1) 215*8d36e1dfSRoy Marples goto eexit; 216*8d36e1dfSRoy Marples unlink(tmpfile); 217*8d36e1dfSRoy Marples fp = fdopen(tmpfd, "w+"); 218*8d36e1dfSRoy Marples if (fp == NULL) { 219*8d36e1dfSRoy Marples close(tmpfd); 220*8d36e1dfSRoy Marples goto eexit; 221*8d36e1dfSRoy Marples } 222*8d36e1dfSRoy Marples #endif 223*8d36e1dfSRoy Marples 2247827cba2SAaron LI #ifdef INET 2257827cba2SAaron LI state = D_STATE(ifp); 2267827cba2SAaron LI #ifdef IPV4LL 2277827cba2SAaron LI istate = IPV4LL_CSTATE(ifp); 2287827cba2SAaron LI #endif 2297827cba2SAaron LI #endif 230*8d36e1dfSRoy Marples #ifdef DHCP6 2317827cba2SAaron LI d6_state = D6_CSTATE(ifp); 2327827cba2SAaron LI #endif 2337827cba2SAaron LI if (strcmp(reason, "TEST") == 0) { 2347827cba2SAaron LI if (1 == 2) {} 2357827cba2SAaron LI #ifdef INET6 236*8d36e1dfSRoy Marples #ifdef DHCP6 2377827cba2SAaron LI else if (d6_state && d6_state->new) 2387827cba2SAaron LI protocol = PROTO_DHCP6; 239*8d36e1dfSRoy Marples #endif 2407827cba2SAaron LI else if (ipv6nd_hasra(ifp)) 2417827cba2SAaron LI protocol = PROTO_RA; 2427827cba2SAaron LI #endif 2437827cba2SAaron LI #ifdef INET 2447827cba2SAaron LI #ifdef IPV4LL 2457827cba2SAaron LI else if (istate && istate->addr != NULL) 2467827cba2SAaron LI protocol = PROTO_IPV4LL; 2477827cba2SAaron LI #endif 2487827cba2SAaron LI else 2497827cba2SAaron LI protocol = PROTO_DHCP; 2507827cba2SAaron LI #endif 2517827cba2SAaron LI } 2527827cba2SAaron LI #ifdef INET6 2537827cba2SAaron LI else if (strcmp(reason, "STATIC6") == 0) 2547827cba2SAaron LI protocol = PROTO_STATIC6; 255*8d36e1dfSRoy Marples #ifdef DHCP6 2567827cba2SAaron LI else if (reason[strlen(reason) - 1] == '6') 2577827cba2SAaron LI protocol = PROTO_DHCP6; 258*8d36e1dfSRoy Marples #endif 2597827cba2SAaron LI else if (strcmp(reason, "ROUTERADVERT") == 0) 2607827cba2SAaron LI protocol = PROTO_RA; 2617827cba2SAaron LI #endif 2627827cba2SAaron LI else if (strcmp(reason, "PREINIT") == 0 || 2637827cba2SAaron LI strcmp(reason, "CARRIER") == 0 || 2647827cba2SAaron LI strcmp(reason, "NOCARRIER") == 0 || 2657827cba2SAaron LI strcmp(reason, "UNKNOWN") == 0 || 2667827cba2SAaron LI strcmp(reason, "DEPARTED") == 0 || 2677827cba2SAaron LI strcmp(reason, "STOPPED") == 0) 2687827cba2SAaron LI protocol = PROTO_LINK; 2697827cba2SAaron LI #ifdef INET 2707827cba2SAaron LI #ifdef IPV4LL 2717827cba2SAaron LI else if (strcmp(reason, "IPV4LL") == 0) 2727827cba2SAaron LI protocol = PROTO_IPV4LL; 2737827cba2SAaron LI #endif 2747827cba2SAaron LI else 2757827cba2SAaron LI protocol = PROTO_DHCP; 2767827cba2SAaron LI #endif 2777827cba2SAaron LI 278*8d36e1dfSRoy Marples /* Needed for scripts */ 279*8d36e1dfSRoy Marples path = getenv("PATH"); 280*8d36e1dfSRoy Marples if (efprintf(fp, "PATH=%s", path == NULL ? DEFAULT_PATH:path) == -1) 2817827cba2SAaron LI goto eexit; 282*8d36e1dfSRoy Marples 283*8d36e1dfSRoy Marples if (efprintf(fp, "interface=%s", ifp->name) == -1) 284*8d36e1dfSRoy Marples goto eexit; 285*8d36e1dfSRoy Marples if (efprintf(fp, "reason=%s", reason) == -1) 286*8d36e1dfSRoy Marples goto eexit; 2877827cba2SAaron LI if (ifp->ctx->options & DHCPCD_DUMPLEASE) 2887827cba2SAaron LI goto dumplease; 289*8d36e1dfSRoy Marples if (efprintf(fp, "pid=%d", getpid()) == -1) 290*8d36e1dfSRoy Marples goto eexit; 291*8d36e1dfSRoy Marples if (efprintf(fp, "ifcarrier=%s", 2927827cba2SAaron LI ifp->carrier == LINK_UNKNOWN ? "unknown" : 293*8d36e1dfSRoy Marples ifp->carrier == LINK_UP ? "up" : "down") == -1) 294*8d36e1dfSRoy Marples goto eexit; 295*8d36e1dfSRoy Marples if (efprintf(fp, "ifmetric=%d", ifp->metric) == -1) 296*8d36e1dfSRoy Marples goto eexit; 297*8d36e1dfSRoy Marples if (efprintf(fp, "ifwireless=%d", ifp->wireless) == -1) 298*8d36e1dfSRoy Marples goto eexit; 299*8d36e1dfSRoy Marples if (efprintf(fp, "ifflags=%u", ifp->flags) == -1) 300*8d36e1dfSRoy Marples goto eexit; 301*8d36e1dfSRoy Marples if (efprintf(fp, "ifmtu=%d", if_getmtu(ifp)) == -1) 302*8d36e1dfSRoy Marples goto eexit; 303*8d36e1dfSRoy Marples 304*8d36e1dfSRoy Marples if (fprintf(fp, "interface_order=") == -1) 305*8d36e1dfSRoy Marples goto eexit; 3067827cba2SAaron LI TAILQ_FOREACH(ifp2, ifp->ctx->ifaces, next) { 307*8d36e1dfSRoy Marples if (ifp2 != TAILQ_FIRST(ifp->ctx->ifaces)) { 308*8d36e1dfSRoy Marples if (fputc(' ', fp) == EOF) 309*8d36e1dfSRoy Marples return -1; 3107827cba2SAaron LI } 311*8d36e1dfSRoy Marples if (fprintf(fp, "%s", ifp2->name) == -1) 312*8d36e1dfSRoy Marples return -1; 3137827cba2SAaron LI } 314*8d36e1dfSRoy Marples if (fputc('\0', fp) == EOF) 315*8d36e1dfSRoy Marples return -1; 316*8d36e1dfSRoy Marples 3177827cba2SAaron LI if (strcmp(reason, "STOPPED") == 0) { 318*8d36e1dfSRoy Marples if (efprintf(fp, "if_up=false") == -1) 319*8d36e1dfSRoy Marples goto eexit; 320*8d36e1dfSRoy Marples if (efprintf(fp, "if_down=%s", 321*8d36e1dfSRoy Marples ifo->options & DHCPCD_RELEASE ? "true" : "false") == -1) 322*8d36e1dfSRoy Marples goto eexit; 3237827cba2SAaron LI } else if (strcmp(reason, "TEST") == 0 || 3247827cba2SAaron LI strcmp(reason, "PREINIT") == 0 || 3257827cba2SAaron LI strcmp(reason, "CARRIER") == 0 || 3267827cba2SAaron LI strcmp(reason, "UNKNOWN") == 0) 3277827cba2SAaron LI { 328*8d36e1dfSRoy Marples if (efprintf(fp, "if_up=false") == -1) 329*8d36e1dfSRoy Marples goto eexit; 330*8d36e1dfSRoy Marples if (efprintf(fp, "if_down=false") == -1) 331*8d36e1dfSRoy Marples goto eexit; 3327827cba2SAaron LI } else if (1 == 2 /* appease ifdefs */ 3337827cba2SAaron LI #ifdef INET 3347827cba2SAaron LI || (protocol == PROTO_DHCP && state && state->new) 3357827cba2SAaron LI #ifdef IPV4LL 3367827cba2SAaron LI || (protocol == PROTO_IPV4LL && IPV4LL_STATE_RUNNING(ifp)) 3377827cba2SAaron LI #endif 3387827cba2SAaron LI #endif 3397827cba2SAaron LI #ifdef INET6 3407827cba2SAaron LI || (protocol == PROTO_STATIC6 && IPV6_STATE_RUNNING(ifp)) 341*8d36e1dfSRoy Marples #ifdef DHCP6 3427827cba2SAaron LI || (protocol == PROTO_DHCP6 && d6_state && d6_state->new) 343*8d36e1dfSRoy Marples #endif 3447827cba2SAaron LI || (protocol == PROTO_RA && ipv6nd_hasra(ifp)) 3457827cba2SAaron LI #endif 3467827cba2SAaron LI ) 3477827cba2SAaron LI { 348*8d36e1dfSRoy Marples if (efprintf(fp, "if_up=true") == -1) 349*8d36e1dfSRoy Marples goto eexit; 350*8d36e1dfSRoy Marples if (efprintf(fp, "if_down=false") == -1) 351*8d36e1dfSRoy Marples goto eexit; 3527827cba2SAaron LI } else { 353*8d36e1dfSRoy Marples if (efprintf(fp, "if_up=false") == -1) 354*8d36e1dfSRoy Marples goto eexit; 355*8d36e1dfSRoy Marples if (efprintf(fp, "if_down=true") == -1) 356*8d36e1dfSRoy Marples goto eexit; 3577827cba2SAaron LI } 3587827cba2SAaron LI if (protocols[protocol] != NULL) { 359*8d36e1dfSRoy Marples if (efprintf(fp, "protocol=%s", protocols[protocol]) == -1) 3607827cba2SAaron LI goto eexit; 3617827cba2SAaron LI } 3627827cba2SAaron LI if ((af = dhcpcd_ifafwaiting(ifp)) != AF_MAX) { 363*8d36e1dfSRoy Marples if (efprintf(fp, "if_afwaiting=%d", af) == -1) 364*8d36e1dfSRoy Marples goto eexit; 3657827cba2SAaron LI } 3667827cba2SAaron LI if ((af = dhcpcd_afwaiting(ifp->ctx)) != AF_MAX) { 3677827cba2SAaron LI TAILQ_FOREACH(ifp2, ifp->ctx->ifaces, next) { 3687827cba2SAaron LI if ((af = dhcpcd_ifafwaiting(ifp2)) != AF_MAX) 3697827cba2SAaron LI break; 3707827cba2SAaron LI } 3717827cba2SAaron LI } 3727827cba2SAaron LI if (af != AF_MAX) { 373*8d36e1dfSRoy Marples if (efprintf(fp, "af_waiting=%d", af) == -1) 374*8d36e1dfSRoy Marples goto eexit; 3757827cba2SAaron LI } 3767827cba2SAaron LI if (ifo->options & DHCPCD_DEBUG) { 377*8d36e1dfSRoy Marples if (efprintf(fp, "syslog_debug=true") == -1) 378*8d36e1dfSRoy Marples goto eexit; 3797827cba2SAaron LI } 3807827cba2SAaron LI if (*ifp->profile) { 381*8d36e1dfSRoy Marples if (efprintf(fp, "profile=%s", ifp->profile) == -1) 382*8d36e1dfSRoy Marples goto eexit; 3837827cba2SAaron LI } 3847827cba2SAaron LI if (ifp->wireless) { 385*8d36e1dfSRoy Marples char pssid[IF_SSIDLEN * 4]; 3867827cba2SAaron LI 387*8d36e1dfSRoy Marples if (print_string(pssid, sizeof(pssid), OT_ESCSTRING, 388*8d36e1dfSRoy Marples ifp->ssid, ifp->ssid_len) != -1) 389*8d36e1dfSRoy Marples { 390*8d36e1dfSRoy Marples if (efprintf(fp, "ifssid=%s", pssid) == -1) 391*8d36e1dfSRoy Marples goto eexit; 3927827cba2SAaron LI } 3937827cba2SAaron LI } 3947827cba2SAaron LI #ifdef INET 3957827cba2SAaron LI if (protocol == PROTO_DHCP && state && state->old) { 396*8d36e1dfSRoy Marples if (dhcp_env(fp, "old", ifp, 397*8d36e1dfSRoy Marples state->old, state->old_len) == -1) 3987827cba2SAaron LI goto eexit; 399*8d36e1dfSRoy Marples if (append_config(fp, "old", 4007827cba2SAaron LI (const char *const *)ifo->config) == -1) 4017827cba2SAaron LI goto eexit; 4027827cba2SAaron LI } 4037827cba2SAaron LI #endif 404*8d36e1dfSRoy Marples #ifdef DHCP6 4057827cba2SAaron LI if (protocol == PROTO_DHCP6 && d6_state && d6_state->old) { 406*8d36e1dfSRoy Marples if (dhcp6_env(fp, "old", ifp, 407*8d36e1dfSRoy Marples d6_state->old, d6_state->old_len) == -1) 4087827cba2SAaron LI goto eexit; 4097827cba2SAaron LI } 4107827cba2SAaron LI #endif 4117827cba2SAaron LI 4127827cba2SAaron LI dumplease: 4137827cba2SAaron LI #ifdef INET 4147827cba2SAaron LI #ifdef IPV4LL 4157827cba2SAaron LI if (protocol == PROTO_IPV4LL) { 416*8d36e1dfSRoy Marples if (ipv4ll_env(fp, istate->down ? "old" : "new", ifp) == -1) 4177827cba2SAaron LI goto eexit; 4187827cba2SAaron LI } 4197827cba2SAaron LI #endif 4207827cba2SAaron LI if (protocol == PROTO_DHCP && state && state->new) { 421*8d36e1dfSRoy Marples if (dhcp_env(fp, "new", ifp, 422*8d36e1dfSRoy Marples state->new, state->new_len) == -1) 4237827cba2SAaron LI goto eexit; 424*8d36e1dfSRoy Marples if (append_config(fp, "new", 4257827cba2SAaron LI (const char *const *)ifo->config) == -1) 4267827cba2SAaron LI goto eexit; 4277827cba2SAaron LI } 4287827cba2SAaron LI #endif 4297827cba2SAaron LI #ifdef INET6 4307827cba2SAaron LI if (protocol == PROTO_STATIC6) { 431*8d36e1dfSRoy Marples if (ipv6_env(fp, "new", ifp) == -1) 4327827cba2SAaron LI goto eexit; 4337827cba2SAaron LI } 434*8d36e1dfSRoy Marples #ifdef DHCP6 4357827cba2SAaron LI if (protocol == PROTO_DHCP6 && D6_STATE_RUNNING(ifp)) { 436*8d36e1dfSRoy Marples if (dhcp6_env(fp, "new", ifp, 437*8d36e1dfSRoy Marples d6_state->new, d6_state->new_len) == -1) 4387827cba2SAaron LI goto eexit; 4397827cba2SAaron LI } 440*8d36e1dfSRoy Marples #endif 4417827cba2SAaron LI if (protocol == PROTO_RA) { 442*8d36e1dfSRoy Marples if (ipv6nd_env(fp, ifp) == -1) 4437827cba2SAaron LI goto eexit; 4447827cba2SAaron LI } 4457827cba2SAaron LI #endif 4467827cba2SAaron LI 4477827cba2SAaron LI /* Add our base environment */ 4487827cba2SAaron LI if (ifo->environ) { 449*8d36e1dfSRoy Marples for (i = 0; ifo->environ[i] != NULL; i++) 450*8d36e1dfSRoy Marples if (efprintf(fp, "%s", ifo->environ[i]) == -1) 4517827cba2SAaron LI goto eexit; 4527827cba2SAaron LI } 4537827cba2SAaron LI 454*8d36e1dfSRoy Marples /* Convert buffer to argv */ 455*8d36e1dfSRoy Marples fflush(fp); 456*8d36e1dfSRoy Marples 457*8d36e1dfSRoy Marples buf_pos = ftell(fp); 458*8d36e1dfSRoy Marples if (buf_pos == -1) { 459*8d36e1dfSRoy Marples logerr(__func__); 460*8d36e1dfSRoy Marples goto eexit; 461*8d36e1dfSRoy Marples } 462*8d36e1dfSRoy Marples 463*8d36e1dfSRoy Marples #ifndef HAVE_OPEN_MEMSTREAM 464*8d36e1dfSRoy Marples size_t buf_len = (size_t)buf_pos; 465*8d36e1dfSRoy Marples if (ctx->script_buflen < buf_len) { 466*8d36e1dfSRoy Marples char *buf = realloc(ctx->script_buf, buf_len); 467*8d36e1dfSRoy Marples if (buf == NULL) 468*8d36e1dfSRoy Marples goto eexit; 469*8d36e1dfSRoy Marples ctx->script_buf = buf; 470*8d36e1dfSRoy Marples ctx->script_buflen = buf_len; 471*8d36e1dfSRoy Marples } 472*8d36e1dfSRoy Marples rewind(fp); 473*8d36e1dfSRoy Marples if (fread(ctx->script_buf, sizeof(char), buf_len, fp) != buf_len) 474*8d36e1dfSRoy Marples goto eexit; 475*8d36e1dfSRoy Marples fclose(fp); 476*8d36e1dfSRoy Marples fp = NULL; 477*8d36e1dfSRoy Marples #endif 478*8d36e1dfSRoy Marples 479*8d36e1dfSRoy Marples /* Count the terminated env strings. 480*8d36e1dfSRoy Marples * Assert that the terminations are correct. */ 481*8d36e1dfSRoy Marples nenv = 0; 482*8d36e1dfSRoy Marples endp = ctx->script_buf + buf_pos; 483*8d36e1dfSRoy Marples for (bufp = ctx->script_buf; bufp < endp; bufp++) { 484*8d36e1dfSRoy Marples if (*bufp == '\0') { 485*8d36e1dfSRoy Marples #ifndef NDEBUG 486*8d36e1dfSRoy Marples if (bufp + 1 < endp) 487*8d36e1dfSRoy Marples assert(*(bufp + 1) != '\0'); 488*8d36e1dfSRoy Marples #endif 489*8d36e1dfSRoy Marples nenv++; 490*8d36e1dfSRoy Marples } 491*8d36e1dfSRoy Marples } 492*8d36e1dfSRoy Marples assert(*(bufp - 1) == '\0'); 493*8d36e1dfSRoy Marples 494*8d36e1dfSRoy Marples if (ctx->script_envlen < nenv) { 495*8d36e1dfSRoy Marples env = reallocarray(ctx->script_env, nenv + 1, sizeof(*env)); 496*8d36e1dfSRoy Marples if (env == NULL) 497*8d36e1dfSRoy Marples goto eexit; 498*8d36e1dfSRoy Marples ctx->script_env = env; 499*8d36e1dfSRoy Marples ctx->script_envlen = nenv; 500*8d36e1dfSRoy Marples } 501*8d36e1dfSRoy Marples 502*8d36e1dfSRoy Marples bufp = ctx->script_buf; 503*8d36e1dfSRoy Marples envp = ctx->script_env; 504*8d36e1dfSRoy Marples *envp++ = bufp++; 505*8d36e1dfSRoy Marples endp--; /* Avoid setting the last \0 to an invalid pointer */ 506*8d36e1dfSRoy Marples for (; bufp < endp; bufp++) { 507*8d36e1dfSRoy Marples if (*bufp == '\0') 508*8d36e1dfSRoy Marples *envp++ = bufp + 1; 509*8d36e1dfSRoy Marples } 510*8d36e1dfSRoy Marples *envp = NULL; 511*8d36e1dfSRoy Marples 512*8d36e1dfSRoy Marples return buf_pos - 1; 5137827cba2SAaron LI 5147827cba2SAaron LI eexit: 5157827cba2SAaron LI logerr(__func__); 516*8d36e1dfSRoy Marples #ifndef HAVE_OPEN_MEMSTREAM 517*8d36e1dfSRoy Marples if (fp != NULL) 518*8d36e1dfSRoy Marples fclose(fp); 519*8d36e1dfSRoy Marples #endif 5207827cba2SAaron LI return -1; 5217827cba2SAaron LI } 5227827cba2SAaron LI 5237827cba2SAaron LI static int 524*8d36e1dfSRoy Marples send_interface1(struct fd_list *fd, const struct interface *ifp, 5257827cba2SAaron LI const char *reason) 5267827cba2SAaron LI { 527*8d36e1dfSRoy Marples struct dhcpcd_ctx *ctx = ifp->ctx; 528*8d36e1dfSRoy Marples long len; 5297827cba2SAaron LI 530*8d36e1dfSRoy Marples len = make_env(ifp, reason); 531*8d36e1dfSRoy Marples if (len == -1) 5327827cba2SAaron LI return -1; 533*8d36e1dfSRoy Marples return control_queue(fd, ctx->script_buf, (size_t)len, 1); 5347827cba2SAaron LI } 5357827cba2SAaron LI 5367827cba2SAaron LI int 5377827cba2SAaron LI send_interface(struct fd_list *fd, const struct interface *ifp) 5387827cba2SAaron LI { 5397827cba2SAaron LI const char *reason; 5407827cba2SAaron LI int retval = 0; 5417827cba2SAaron LI #ifdef INET 5427827cba2SAaron LI const struct dhcp_state *d; 5437827cba2SAaron LI #endif 544*8d36e1dfSRoy Marples #ifdef DHCP6 5457827cba2SAaron LI const struct dhcp6_state *d6; 5467827cba2SAaron LI #endif 5477827cba2SAaron LI 5487827cba2SAaron LI switch (ifp->carrier) { 5497827cba2SAaron LI case LINK_UP: 5507827cba2SAaron LI reason = "CARRIER"; 5517827cba2SAaron LI break; 5527827cba2SAaron LI case LINK_DOWN: 553*8d36e1dfSRoy Marples case LINK_DOWN_IFFUP: 5547827cba2SAaron LI reason = "NOCARRIER"; 5557827cba2SAaron LI break; 5567827cba2SAaron LI default: 5577827cba2SAaron LI reason = "UNKNOWN"; 5587827cba2SAaron LI break; 5597827cba2SAaron LI } 5607827cba2SAaron LI if (send_interface1(fd, ifp, reason) == -1) 5617827cba2SAaron LI retval = -1; 5627827cba2SAaron LI #ifdef INET 5637827cba2SAaron LI if (D_STATE_RUNNING(ifp)) { 5647827cba2SAaron LI d = D_CSTATE(ifp); 5657827cba2SAaron LI if (send_interface1(fd, ifp, d->reason) == -1) 5667827cba2SAaron LI retval = -1; 5677827cba2SAaron LI } 5687827cba2SAaron LI #ifdef IPV4LL 5697827cba2SAaron LI if (IPV4LL_STATE_RUNNING(ifp)) { 5707827cba2SAaron LI if (send_interface1(fd, ifp, "IPV4LL") == -1) 5717827cba2SAaron LI retval = -1; 5727827cba2SAaron LI } 5737827cba2SAaron LI #endif 5747827cba2SAaron LI #endif 5757827cba2SAaron LI 5767827cba2SAaron LI #ifdef INET6 5777827cba2SAaron LI if (IPV6_STATE_RUNNING(ifp)) { 5787827cba2SAaron LI if (send_interface1(fd, ifp, "STATIC6") == -1) 5797827cba2SAaron LI retval = -1; 5807827cba2SAaron LI } 5817827cba2SAaron LI if (RS_STATE_RUNNING(ifp)) { 5827827cba2SAaron LI if (send_interface1(fd, ifp, "ROUTERADVERT") == -1) 5837827cba2SAaron LI retval = -1; 5847827cba2SAaron LI } 585*8d36e1dfSRoy Marples #ifdef DHCP6 5867827cba2SAaron LI if (D6_STATE_RUNNING(ifp)) { 5877827cba2SAaron LI d6 = D6_CSTATE(ifp); 5887827cba2SAaron LI if (send_interface1(fd, ifp, d6->reason) == -1) 5897827cba2SAaron LI retval = -1; 5907827cba2SAaron LI } 5917827cba2SAaron LI #endif 592*8d36e1dfSRoy Marples #endif 5937827cba2SAaron LI 5947827cba2SAaron LI return retval; 5957827cba2SAaron LI } 5967827cba2SAaron LI 5977827cba2SAaron LI int 5987827cba2SAaron LI script_runreason(const struct interface *ifp, const char *reason) 5997827cba2SAaron LI { 600*8d36e1dfSRoy Marples struct dhcpcd_ctx *ctx = ifp->ctx; 6017827cba2SAaron LI char *argv[2]; 6027827cba2SAaron LI pid_t pid; 6037827cba2SAaron LI int status = 0; 6047827cba2SAaron LI struct fd_list *fd; 6057827cba2SAaron LI 606*8d36e1dfSRoy Marples if (ifp->options->script == NULL && 6077827cba2SAaron LI TAILQ_FIRST(&ifp->ctx->control_fds) == NULL) 6087827cba2SAaron LI return 0; 6097827cba2SAaron LI 6107827cba2SAaron LI /* Make our env */ 611*8d36e1dfSRoy Marples if (make_env(ifp, reason) == -1) { 6127827cba2SAaron LI logerr(__func__); 6137827cba2SAaron LI return -1; 6147827cba2SAaron LI } 6157827cba2SAaron LI 616*8d36e1dfSRoy Marples if (ifp->options->script == NULL) 6177827cba2SAaron LI goto send_listeners; 6187827cba2SAaron LI 619*8d36e1dfSRoy Marples argv[0] = ifp->options->script; 6207827cba2SAaron LI argv[1] = NULL; 6217827cba2SAaron LI logdebugx("%s: executing `%s' %s", ifp->name, argv[0], reason); 6227827cba2SAaron LI 623*8d36e1dfSRoy Marples pid = exec_script(ctx, argv, ctx->script_env); 6247827cba2SAaron LI if (pid == -1) 6257827cba2SAaron LI logerr("%s: %s", __func__, argv[0]); 6267827cba2SAaron LI else if (pid != 0) { 6277827cba2SAaron LI /* Wait for the script to finish */ 6287827cba2SAaron LI while (waitpid(pid, &status, 0) == -1) { 6297827cba2SAaron LI if (errno != EINTR) { 6307827cba2SAaron LI logerr("%s: waitpid", __func__); 6317827cba2SAaron LI status = 0; 6327827cba2SAaron LI break; 6337827cba2SAaron LI } 6347827cba2SAaron LI } 6357827cba2SAaron LI if (WIFEXITED(status)) { 6367827cba2SAaron LI if (WEXITSTATUS(status)) 6377827cba2SAaron LI logerrx("%s: %s: WEXITSTATUS %d", 6387827cba2SAaron LI __func__, argv[0], WEXITSTATUS(status)); 6397827cba2SAaron LI } else if (WIFSIGNALED(status)) 6407827cba2SAaron LI logerrx("%s: %s: %s", 6417827cba2SAaron LI __func__, argv[0], strsignal(WTERMSIG(status))); 6427827cba2SAaron LI } 6437827cba2SAaron LI 6447827cba2SAaron LI send_listeners: 6457827cba2SAaron LI /* Send to our listeners */ 6467827cba2SAaron LI status = 0; 647*8d36e1dfSRoy Marples TAILQ_FOREACH(fd, &ctx->control_fds, next) { 6487827cba2SAaron LI if (!(fd->flags & FD_LISTEN)) 6497827cba2SAaron LI continue; 650*8d36e1dfSRoy Marples if (control_queue(fd, ctx->script_buf, ctx->script_buflen, 651*8d36e1dfSRoy Marples true) == -1) 6527827cba2SAaron LI logerr("%s: control_queue", __func__); 6537827cba2SAaron LI else 6547827cba2SAaron LI status = 1; 6557827cba2SAaron LI } 6567827cba2SAaron LI 6577827cba2SAaron LI return WEXITSTATUS(status); 6587827cba2SAaron LI } 659