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