1 /* SPDX-License-Identifier: BSD-2-Clause */ 2 /* 3 * dhcpcd - DHCP client daemon 4 * Copyright (c) 2006-2025 Roy Marples <roy (at) marples.name> 5 * All rights reserved 6 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions 9 * are met: 10 * 1. Redistributions of source code must retain the above copyright 11 * notice, this list of conditions and the following disclaimer. 12 * 2. Redistributions in binary form must reproduce the above copyright 13 * notice, this list of conditions and the following disclaimer in the 14 * documentation and/or other materials provided with the distribution. 15 * 16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 17 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 19 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 20 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 21 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 22 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 23 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 24 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 25 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 26 * SUCH DAMAGE. 27 */ 28 29 #include <sys/stat.h> 30 #include <sys/uio.h> 31 #include <sys/wait.h> 32 33 #include <netinet/in.h> 34 #include <arpa/inet.h> 35 36 #include <assert.h> 37 #include <ctype.h> 38 #include <errno.h> 39 #include <pwd.h> 40 #include <signal.h> 41 #include <spawn.h> 42 #include <stdarg.h> 43 #include <stdlib.h> 44 #include <string.h> 45 #include <unistd.h> 46 47 #include "config.h" 48 #include "common.h" 49 #include "dhcp.h" 50 #include "dhcp6.h" 51 #include "eloop.h" 52 #include "if.h" 53 #include "if-options.h" 54 #include "ipv4ll.h" 55 #include "ipv6nd.h" 56 #include "logerr.h" 57 #include "privsep.h" 58 #include "script.h" 59 60 #define DEFAULT_PATH "/usr/bin:/usr/sbin:/bin:/sbin" 61 62 static const char * const if_params[] = { 63 "interface", 64 "protocol", 65 "reason", 66 "pid", 67 "ifcarrier", 68 "ifmetric", 69 "ifwireless", 70 "ifflags", 71 "ssid", 72 "profile", 73 "interface_order", 74 NULL 75 }; 76 77 static const char * true_str = "true"; 78 static const char * false_str = "false"; 79 80 void 81 if_printoptions(void) 82 { 83 const char * const *p; 84 85 for (p = if_params; *p; p++) 86 printf(" - %s\n", *p); 87 } 88 89 pid_t 90 script_exec(char *const *argv, char *const *env) 91 { 92 pid_t pid = 0; 93 posix_spawnattr_t attr; 94 int r; 95 #ifdef USE_SIGNALS 96 size_t i; 97 short flags; 98 sigset_t defsigs; 99 #else 100 UNUSED(ctx); 101 #endif 102 103 /* posix_spawn is a safe way of executing another image 104 * and changing signals back to how they should be. */ 105 if (posix_spawnattr_init(&attr) == -1) 106 return -1; 107 #ifdef USE_SIGNALS 108 flags = POSIX_SPAWN_SETSIGMASK | POSIX_SPAWN_SETSIGDEF; 109 posix_spawnattr_setflags(&attr, flags); 110 sigemptyset(&defsigs); 111 posix_spawnattr_setsigmask(&attr, &defsigs); 112 for (i = 0; i < dhcpcd_signals_len; i++) 113 sigaddset(&defsigs, dhcpcd_signals[i]); 114 for (i = 0; i < dhcpcd_signals_ignore_len; i++) 115 sigaddset(&defsigs, dhcpcd_signals_ignore[i]); 116 posix_spawnattr_setsigdefault(&attr, &defsigs); 117 #endif 118 errno = 0; 119 r = posix_spawn(&pid, argv[0], NULL, &attr, argv, env); 120 posix_spawnattr_destroy(&attr); 121 if (r) { 122 errno = r; 123 return -1; 124 } 125 return pid; 126 } 127 128 #ifdef INET 129 static int 130 append_config(FILE *fp, const char *prefix, const char *const *config) 131 { 132 size_t i; 133 134 if (config == NULL) 135 return 0; 136 137 /* Do we need to replace existing config rather than append? */ 138 for (i = 0; config[i] != NULL; i++) { 139 if (efprintf(fp, "%s_%s", prefix, config[i]) == -1) 140 return -1; 141 } 142 return 1; 143 } 144 145 #endif 146 147 #define PROTO_LINK 0 148 #define PROTO_DHCP 1 149 #define PROTO_IPV4LL 2 150 #define PROTO_RA 3 151 #define PROTO_DHCP6 4 152 #define PROTO_STATIC6 5 153 static const char *protocols[] = { 154 "link", 155 "dhcp", 156 "ipv4ll", 157 "ra", 158 "dhcp6", 159 "static6" 160 }; 161 162 int 163 efprintf(FILE *fp, const char *fmt, ...) 164 { 165 va_list args; 166 int r; 167 168 va_start(args, fmt); 169 r = vfprintf(fp, fmt, args); 170 va_end(args); 171 if (r == -1) 172 return -1; 173 /* Write a trailing NULL so we can easily create env strings. */ 174 if (fputc('\0', fp) == EOF) 175 return -1; 176 return r; 177 } 178 179 char ** 180 script_buftoenv(struct dhcpcd_ctx *ctx, char *buf, size_t len) 181 { 182 char **env, **envp, *bufp, *endp; 183 size_t nenv; 184 185 /* Count the terminated env strings. 186 * Assert that the terminations are correct. */ 187 nenv = 0; 188 endp = buf + len; 189 for (bufp = buf; bufp < endp; bufp++) { 190 if (*bufp == '\0') { 191 #ifndef NDEBUG 192 if (bufp + 1 < endp) 193 assert(*(bufp + 1) != '\0'); 194 #endif 195 nenv++; 196 } 197 } 198 assert(*(bufp - 1) == '\0'); 199 if (nenv == 0) 200 return NULL; 201 202 if (ctx->script_envlen < nenv) { 203 env = reallocarray(ctx->script_env, nenv + 1, sizeof(*env)); 204 if (env == NULL) 205 return NULL; 206 ctx->script_env = env; 207 ctx->script_envlen = nenv; 208 } 209 210 bufp = buf; 211 envp = ctx->script_env; 212 *envp++ = bufp++; 213 endp--; /* Avoid setting the last \0 to an invalid pointer */ 214 for (; bufp < endp; bufp++) { 215 if (*bufp == '\0') 216 *envp++ = bufp + 1; 217 } 218 *envp = NULL; 219 220 return ctx->script_env; 221 } 222 223 static long 224 make_env(struct dhcpcd_ctx *ctx, const struct interface *ifp, 225 const char *reason) 226 { 227 FILE *fp; 228 long buf_pos, i; 229 char *path; 230 int protocol = PROTO_LINK; 231 const struct if_options *ifo; 232 const struct interface *ifp2; 233 int af; 234 bool is_stdin = ifp->name[0] == '\0'; 235 const char *if_up, *if_down; 236 rb_tree_t ifaces; 237 struct rt *rt; 238 #ifdef INET 239 const struct dhcp_state *state; 240 #ifdef IPV4LL 241 const struct ipv4ll_state *istate; 242 #endif 243 #endif 244 #ifdef DHCP6 245 const struct dhcp6_state *d6_state; 246 #endif 247 248 #ifdef HAVE_OPEN_MEMSTREAM 249 if (ctx->script_fp == NULL) { 250 fp = open_memstream(&ctx->script_buf, &ctx->script_buflen); 251 if (fp == NULL) 252 goto eexit; 253 ctx->script_fp = fp; 254 } else { 255 fp = ctx->script_fp; 256 rewind(fp); 257 } 258 #else 259 char tmpfile[] = "/tmp/dhcpcd-script-env-XXXXXX"; 260 int tmpfd; 261 262 fp = NULL; 263 tmpfd = mkstemp(tmpfile); 264 if (tmpfd == -1) { 265 logerr("%s: mkstemp", __func__); 266 return -1; 267 } 268 unlink(tmpfile); 269 fp = fdopen(tmpfd, "w+"); 270 if (fp == NULL) { 271 close(tmpfd); 272 goto eexit; 273 } 274 #endif 275 276 if (!(ifp->ctx->options & DHCPCD_DUMPLEASE)) { 277 /* Needed for scripts */ 278 path = getenv("PATH"); 279 if (efprintf(fp, "PATH=%s", 280 path == NULL ? DEFAULT_PATH : path) == -1) 281 goto eexit; 282 if (efprintf(fp, "pid=%d", (int)getpid()) == -1) 283 goto eexit; 284 } 285 286 if (!is_stdin) { 287 if (efprintf(fp, "reason=%s", reason) == -1) 288 goto eexit; 289 } 290 291 ifo = ifp->options; 292 #ifdef INET 293 state = D_STATE(ifp); 294 #ifdef IPV4LL 295 istate = IPV4LL_CSTATE(ifp); 296 #endif 297 #endif 298 #ifdef DHCP6 299 d6_state = D6_CSTATE(ifp); 300 #endif 301 if (strcmp(reason, "TEST") == 0) { 302 if (1 == 2) { 303 /* This space left intentionally blank 304 * as all the below statements are optional. */ 305 } 306 #ifdef INET6 307 #ifdef DHCP6 308 else if (d6_state && d6_state->new) 309 protocol = PROTO_DHCP6; 310 #endif 311 else if (ipv6nd_hasra(ifp)) 312 protocol = PROTO_RA; 313 #endif 314 #ifdef INET 315 #ifdef IPV4LL 316 else if (istate && istate->addr != NULL) 317 protocol = PROTO_IPV4LL; 318 #endif 319 else 320 protocol = PROTO_DHCP; 321 #endif 322 } 323 #ifdef INET6 324 else if (strcmp(reason, "STATIC6") == 0) 325 protocol = PROTO_STATIC6; 326 #ifdef DHCP6 327 else if (reason[strlen(reason) - 1] == '6') 328 protocol = PROTO_DHCP6; 329 #endif 330 else if (strcmp(reason, "ROUTERADVERT") == 0) 331 protocol = PROTO_RA; 332 #endif 333 else if (strcmp(reason, "PREINIT") == 0 || 334 strcmp(reason, "CARRIER") == 0 || 335 strcmp(reason, "NOCARRIER") == 0 || 336 strcmp(reason, "NOCARRIER_ROAMING") == 0 || 337 strcmp(reason, "UNKNOWN") == 0 || 338 strcmp(reason, "DEPARTED") == 0 || 339 strcmp(reason, "STOPPED") == 0) 340 protocol = PROTO_LINK; 341 #ifdef INET 342 #ifdef IPV4LL 343 else if (strcmp(reason, "IPV4LL") == 0) 344 protocol = PROTO_IPV4LL; 345 #endif 346 else 347 protocol = PROTO_DHCP; 348 #endif 349 350 if (!is_stdin) { 351 if (efprintf(fp, "interface=%s", ifp->name) == -1) 352 goto eexit; 353 if (protocols[protocol] != NULL) { 354 if (efprintf(fp, "protocol=%s", 355 protocols[protocol]) == -1) 356 goto eexit; 357 } 358 } 359 if (ifp->ctx->options & DHCPCD_DUMPLEASE && protocol != PROTO_LINK) 360 goto dumplease; 361 if (efprintf(fp, "if_configured=%s", 362 ifo->options & DHCPCD_CONFIGURE ? "true" : "false") == -1) 363 goto eexit; 364 if (efprintf(fp, "ifcarrier=%s", 365 ifp->carrier == LINK_UNKNOWN ? "unknown" : 366 ifp->carrier == LINK_UP ? "up" : "down") == -1) 367 goto eexit; 368 if (efprintf(fp, "ifmetric=%d", ifp->metric) == -1) 369 goto eexit; 370 if (efprintf(fp, "ifwireless=%d", ifp->wireless) == -1) 371 goto eexit; 372 if (efprintf(fp, "ifflags=%u", ifp->flags) == -1) 373 goto eexit; 374 if (efprintf(fp, "ifmtu=%d", if_getmtu(ifp)) == -1) 375 goto eexit; 376 if (ifp->wireless) { 377 char pssid[IF_SSIDLEN * 4]; 378 379 if (print_string(pssid, sizeof(pssid), OT_ESCSTRING, 380 ifp->ssid, ifp->ssid_len) != -1) 381 { 382 if (efprintf(fp, "ifssid=%s", pssid) == -1) 383 goto eexit; 384 } 385 } 386 if (*ifp->profile != '\0') { 387 if (efprintf(fp, "profile=%s", ifp->profile) == -1) 388 goto eexit; 389 } 390 if (ifp->ctx->options & DHCPCD_DUMPLEASE) 391 goto dumplease; 392 393 ifp->ctx->rt_order = 0; 394 rb_tree_init(&ifaces, &rt_compare_proto_ops); 395 TAILQ_FOREACH(ifp2, ifp->ctx->ifaces, next) { 396 if (!ifp2->active) 397 continue; 398 rt = rt_new(UNCONST(ifp2)); 399 if (rt == NULL) 400 goto eexit; 401 if (rt_proto_add(&ifaces, rt) != rt) 402 goto eexit; 403 } 404 if (fprintf(fp, "interface_order=") == -1) 405 goto eexit; 406 RB_TREE_FOREACH(rt, &ifaces) { 407 if (rt != RB_TREE_MIN(&ifaces) && 408 fprintf(fp, "%s", " ") == -1) 409 goto eexit; 410 if (fprintf(fp, "%s", rt->rt_ifp->name) == -1) 411 goto eexit; 412 } 413 rt_headclear(&ifaces, AF_UNSPEC); 414 if (fputc('\0', fp) == EOF) 415 goto eexit; 416 417 if (strcmp(reason, "STOPPED") == 0) { 418 if_up = false_str; 419 if_down = ifo->options & DHCPCD_RELEASE ? true_str : false_str; 420 } else if (strcmp(reason, "TEST") == 0 || 421 strcmp(reason, "PREINIT") == 0 || 422 strcmp(reason, "CARRIER") == 0 || 423 strcmp(reason, "STOP") == 0 || 424 strcmp(reason, "UNKNOWN") == 0) 425 { 426 if_up = false_str; 427 if_down = false_str; 428 } else if (strcmp(reason, "NOCARRIER") == 0) { 429 if_up = false_str; 430 if_down = true_str; 431 } else if (strcmp(reason, "NOCARRIER_ROAMING") == 0) { 432 if_up = true_str; 433 if_down = false_str; 434 } else if (1 == 2 /* appease ifdefs */ 435 #ifdef INET 436 || (protocol == PROTO_DHCP && state && state->new) 437 #ifdef IPV4LL 438 || (protocol == PROTO_IPV4LL && IPV4LL_STATE_RUNNING(ifp)) 439 #endif 440 #endif 441 #ifdef INET6 442 || (protocol == PROTO_STATIC6 && IPV6_STATE_RUNNING(ifp)) 443 #ifdef DHCP6 444 || (protocol == PROTO_DHCP6 && d6_state && d6_state->new) 445 #endif 446 || (protocol == PROTO_RA && ipv6nd_hasra(ifp)) 447 #endif 448 ) 449 { 450 if_up = true_str; 451 if_down = false_str; 452 } else { 453 if_up = false_str; 454 if_down = true_str; 455 } 456 if (efprintf(fp, "if_up=%s", if_up) == -1) 457 goto eexit; 458 if (efprintf(fp, "if_down=%s", if_down) == -1) 459 goto eexit; 460 461 if ((af = dhcpcd_ifafwaiting(ifp)) != AF_MAX) { 462 if (efprintf(fp, "if_afwaiting=%d", af) == -1) 463 goto eexit; 464 } 465 if ((af = dhcpcd_afwaiting(ifp->ctx)) != AF_MAX) { 466 TAILQ_FOREACH(ifp2, ifp->ctx->ifaces, next) { 467 if ((af = dhcpcd_ifafwaiting(ifp2)) != AF_MAX) 468 break; 469 } 470 } 471 if (af != AF_MAX) { 472 if (efprintf(fp, "af_waiting=%d", af) == -1) 473 goto eexit; 474 } 475 if (loggetopts() & LOGERR_DEBUG) { 476 if (efprintf(fp, "syslog_debug=true") == -1) 477 goto eexit; 478 } 479 #ifdef INET 480 if (protocol == PROTO_DHCP && state && state->old) { 481 if (dhcp_env(fp, "old", ifp, 482 state->old, state->old_len) == -1) 483 goto eexit; 484 if (append_config(fp, "old", 485 (const char *const *)ifo->config) == -1) 486 goto eexit; 487 } 488 #endif 489 #ifdef DHCP6 490 if (protocol == PROTO_DHCP6 && d6_state && d6_state->old) { 491 if (dhcp6_env(fp, "old", ifp, 492 d6_state->old, d6_state->old_len) == -1) 493 goto eexit; 494 } 495 #endif 496 497 dumplease: 498 #ifdef INET 499 #ifdef IPV4LL 500 if (protocol == PROTO_IPV4LL && istate) { 501 if (ipv4ll_env(fp, istate->down ? "old" : "new", ifp) == -1) 502 goto eexit; 503 } 504 #endif 505 if (protocol == PROTO_DHCP && state && state->new) { 506 if (dhcp_env(fp, "new", ifp, 507 state->new, state->new_len) == -1) 508 goto eexit; 509 if (append_config(fp, "new", 510 (const char *const *)ifo->config) == -1) 511 goto eexit; 512 } 513 #endif 514 #ifdef INET6 515 if (protocol == PROTO_STATIC6) { 516 if (ipv6_env(fp, "new", ifp) == -1) 517 goto eexit; 518 } 519 #ifdef DHCP6 520 if (protocol == PROTO_DHCP6 && D6_STATE_RUNNING(ifp)) { 521 if (dhcp6_env(fp, "new", ifp, 522 d6_state->new, d6_state->new_len) == -1) 523 goto eexit; 524 } 525 #endif 526 if (protocol == PROTO_RA) { 527 if (ipv6nd_env(fp, ifp) == -1) 528 goto eexit; 529 } 530 #endif 531 532 /* Add our base environment */ 533 if (ifo->environ) { 534 for (i = 0; ifo->environ[i] != NULL; i++) 535 if (efprintf(fp, "%s", ifo->environ[i]) == -1) 536 goto eexit; 537 } 538 539 /* Convert buffer to argv */ 540 fflush(fp); 541 542 buf_pos = ftell(fp); 543 if (buf_pos == -1) { 544 logerr(__func__); 545 goto eexit; 546 } 547 548 #ifndef HAVE_OPEN_MEMSTREAM 549 size_t buf_len = (size_t)buf_pos; 550 if (ctx->script_buflen < buf_len) { 551 char *buf = realloc(ctx->script_buf, buf_len); 552 if (buf == NULL) 553 goto eexit; 554 ctx->script_buf = buf; 555 ctx->script_buflen = buf_len; 556 } 557 rewind(fp); 558 if (fread(ctx->script_buf, sizeof(char), buf_len, fp) != buf_len) 559 goto eexit; 560 fclose(fp); 561 fp = NULL; 562 #endif 563 564 if (is_stdin) 565 return buf_pos; 566 567 if (script_buftoenv(ctx, ctx->script_buf, (size_t)buf_pos) == NULL) 568 goto eexit; 569 570 return buf_pos; 571 572 eexit: 573 logerr(__func__); 574 #ifndef HAVE_OPEN_MEMSTREAM 575 if (fp != NULL) 576 fclose(fp); 577 #endif 578 return -1; 579 } 580 581 static int 582 send_interface1(struct fd_list *fd, const struct interface *ifp, 583 const char *reason) 584 { 585 struct dhcpcd_ctx *ctx = ifp->ctx; 586 long len; 587 588 len = make_env(ifp->ctx, ifp, reason); 589 if (len == -1) 590 return -1; 591 return control_queue(fd, ctx->script_buf, (size_t)len); 592 } 593 594 int 595 send_interface(struct fd_list *fd, const struct interface *ifp, int af) 596 { 597 int retval = 0; 598 #ifdef INET 599 const struct dhcp_state *d; 600 #endif 601 #ifdef DHCP6 602 const struct dhcp6_state *d6; 603 #endif 604 605 #ifndef AF_LINK 606 #define AF_LINK AF_PACKET 607 #endif 608 609 if (af == AF_UNSPEC || af == AF_LINK) { 610 const char *reason; 611 612 switch (ifp->carrier) { 613 case LINK_UP: 614 reason = "CARRIER"; 615 break; 616 case LINK_DOWN: 617 reason = "NOCARRIER"; 618 break; 619 default: 620 reason = "UNKNOWN"; 621 break; 622 } 623 if (fd != NULL) { 624 if (send_interface1(fd, ifp, reason) == -1) 625 retval = -1; 626 } else 627 retval++; 628 } 629 630 #ifdef INET 631 if (af == AF_UNSPEC || af == AF_INET) { 632 if (D_STATE_RUNNING(ifp)) { 633 d = D_CSTATE(ifp); 634 if (fd != NULL) { 635 if (send_interface1(fd, ifp, d->reason) == -1) 636 retval = -1; 637 } else 638 retval++; 639 } 640 #ifdef IPV4LL 641 if (IPV4LL_STATE_RUNNING(ifp)) { 642 if (fd != NULL) { 643 if (send_interface1(fd, ifp, "IPV4LL") == -1) 644 retval = -1; 645 } else 646 retval++; 647 } 648 #endif 649 } 650 #endif 651 652 #ifdef INET6 653 if (af == AF_UNSPEC || af == AF_INET6) { 654 if (IPV6_STATE_RUNNING(ifp)) { 655 if (fd != NULL) { 656 if (send_interface1(fd, ifp, "STATIC6") == -1) 657 retval = -1; 658 } else 659 retval++; 660 } 661 if (RS_STATE_RUNNING(ifp)) { 662 if (fd != NULL) { 663 if (send_interface1(fd, ifp, 664 "ROUTERADVERT") == -1) 665 retval = -1; 666 } else 667 retval++; 668 } 669 #ifdef DHCP6 670 if (D6_STATE_RUNNING(ifp)) { 671 d6 = D6_CSTATE(ifp); 672 if (fd != NULL) { 673 if (send_interface1(fd, ifp, d6->reason) == -1) 674 retval = -1; 675 } else 676 retval++; 677 } 678 #endif 679 } 680 #endif 681 682 return retval; 683 } 684 685 static int 686 script_status(const char *script, int status) 687 { 688 689 if (WIFEXITED(status)) { 690 if (WEXITSTATUS(status)) 691 logerrx("%s: %s: WEXITSTATUS %d", 692 __func__, script, WEXITSTATUS(status)); 693 } else if (WIFSIGNALED(status)) 694 logerrx("%s: %s: %s", 695 __func__, script, strsignal(WTERMSIG(status))); 696 697 return WEXITSTATUS(status); 698 } 699 700 static int 701 script_run(struct dhcpcd_ctx *ctx, char **argv) 702 { 703 pid_t pid; 704 int status; 705 706 pid = script_exec(argv, ctx->script_env); 707 if (pid == -1) { 708 logerr("%s: %s", __func__, argv[0]); 709 return -1; 710 } else if (pid == 0) 711 return 0; 712 713 /* Wait for the script to finish */ 714 while (waitpid(pid, &status, 0) == -1) { 715 if (errno != EINTR) { 716 logerr("%s: waitpid", __func__); 717 status = 0; 718 break; 719 } 720 } 721 return script_status(argv[0], status); 722 } 723 724 int 725 script_dump(const char *env, size_t len) 726 { 727 const char *ep = env + len; 728 729 if (len == 0) 730 return 0; 731 732 if (*(ep - 1) != '\0') { 733 errno = EINVAL; 734 return -1; 735 } 736 737 for (; env < ep; env += strlen(env) + 1) { 738 if (strncmp(env, "new_", 4) == 0) 739 env += 4; 740 printf("%s\n", env); 741 } 742 fflush(stdout); 743 return 0; 744 } 745 746 int 747 script_runreason(const struct interface *ifp, const char *reason) 748 { 749 struct dhcpcd_ctx *ctx = ifp->ctx; 750 char *argv[2]; 751 int status = 0; 752 struct fd_list *fd; 753 long buflen; 754 755 if (ctx->script == NULL && 756 TAILQ_FIRST(&ifp->ctx->control_fds) == NULL) 757 return 0; 758 759 /* Make our env */ 760 if ((buflen = make_env(ifp->ctx, ifp, reason)) == -1) { 761 logerr(__func__); 762 return -1; 763 } 764 765 if (strncmp(reason, "DUMP", 4) == 0) 766 return script_dump(ctx->script_buf, (size_t)buflen); 767 768 if (ctx->script == NULL) 769 goto send_listeners; 770 771 argv[0] = ctx->script; 772 argv[1] = NULL; 773 logdebugx("%s: executing: %s %s", ifp->name, argv[0], reason); 774 775 #ifdef PRIVSEP 776 if (IN_PRIVSEP(ctx)) { 777 ssize_t err; 778 779 err = ps_root_script(ctx, ctx->script_buf, (size_t)buflen); 780 if (err == -1) 781 logerr(__func__); 782 else 783 script_status(ctx->script, (int)err); 784 goto send_listeners; 785 } 786 #endif 787 788 script_run(ctx, argv); 789 790 send_listeners: 791 /* Send to our listeners */ 792 status = 0; 793 TAILQ_FOREACH(fd, &ctx->control_fds, next) { 794 if (!(fd->flags & FD_LISTEN)) 795 continue; 796 if (control_queue(fd, ctx->script_buf, ctx->script_buflen)== -1) 797 logerr("%s: control_queue", __func__); 798 else 799 status = 1; 800 } 801 802 return status; 803 } 804