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