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