17c478bd9Sstevel@tonic-gate /* 27c478bd9Sstevel@tonic-gate * CDDL HEADER START 37c478bd9Sstevel@tonic-gate * 47c478bd9Sstevel@tonic-gate * The contents of this file are subject to the terms of the 576cf44abSjeanm * Common Development and Distribution License (the "License"). 676cf44abSjeanm * You may not use this file except in compliance with the License. 77c478bd9Sstevel@tonic-gate * 87c478bd9Sstevel@tonic-gate * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 97c478bd9Sstevel@tonic-gate * or http://www.opensolaris.org/os/licensing. 107c478bd9Sstevel@tonic-gate * See the License for the specific language governing permissions 117c478bd9Sstevel@tonic-gate * and limitations under the License. 127c478bd9Sstevel@tonic-gate * 137c478bd9Sstevel@tonic-gate * When distributing Covered Code, include this CDDL HEADER in each 147c478bd9Sstevel@tonic-gate * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 157c478bd9Sstevel@tonic-gate * If applicable, add the following below this CDDL HEADER, with the 167c478bd9Sstevel@tonic-gate * fields enclosed by brackets "[]" replaced with your own identifying 177c478bd9Sstevel@tonic-gate * information: Portions Copyright [yyyy] [name of copyright owner] 187c478bd9Sstevel@tonic-gate * 197c478bd9Sstevel@tonic-gate * CDDL HEADER END 207c478bd9Sstevel@tonic-gate */ 217c478bd9Sstevel@tonic-gate /* 229444c26fSTom Whitten * Copyright 2010 Sun Microsystems, Inc. All rights reserved. 237c478bd9Sstevel@tonic-gate * Use is subject to license terms. 2433f5ff17SMilan Jurik * Copyright 2012 Milan Jurik. All rights reserved. 257c478bd9Sstevel@tonic-gate */ 267c478bd9Sstevel@tonic-gate 277c478bd9Sstevel@tonic-gate /* 28*0d421f66SBryan Cantrill * Copyright (c) 2013, Joyent, Inc. All rights reserved. 29*0d421f66SBryan Cantrill */ 30*0d421f66SBryan Cantrill 31*0d421f66SBryan Cantrill /* 327c478bd9Sstevel@tonic-gate * fork.c - safe forking for svc.startd 337c478bd9Sstevel@tonic-gate * 347c478bd9Sstevel@tonic-gate * fork_configd() and fork_sulogin() are related, special cases that handle the 357c478bd9Sstevel@tonic-gate * spawning of specific client processes for svc.startd. 367c478bd9Sstevel@tonic-gate */ 377c478bd9Sstevel@tonic-gate 387c478bd9Sstevel@tonic-gate #include <sys/contract/process.h> 397c478bd9Sstevel@tonic-gate #include <sys/corectl.h> 407c478bd9Sstevel@tonic-gate #include <sys/ctfs.h> 417c478bd9Sstevel@tonic-gate #include <sys/stat.h> 427c478bd9Sstevel@tonic-gate #include <sys/types.h> 437c478bd9Sstevel@tonic-gate #include <sys/uio.h> 447c478bd9Sstevel@tonic-gate #include <sys/wait.h> 457c478bd9Sstevel@tonic-gate #include <assert.h> 467c478bd9Sstevel@tonic-gate #include <errno.h> 477c478bd9Sstevel@tonic-gate #include <fcntl.h> 487c478bd9Sstevel@tonic-gate #include <libcontract.h> 497c478bd9Sstevel@tonic-gate #include <libcontract_priv.h> 507b209c2cSacruz #include <libscf_priv.h> 517c478bd9Sstevel@tonic-gate #include <limits.h> 524d53c7adSDan Price #include <poll.h> 537c478bd9Sstevel@tonic-gate #include <port.h> 547c478bd9Sstevel@tonic-gate #include <signal.h> 557c478bd9Sstevel@tonic-gate #include <stdarg.h> 567c478bd9Sstevel@tonic-gate #include <stdio.h> 577c478bd9Sstevel@tonic-gate #include <stdlib.h> 587c478bd9Sstevel@tonic-gate #include <string.h> 597c478bd9Sstevel@tonic-gate #include <unistd.h> 6076cf44abSjeanm #include <utmpx.h> 614d53c7adSDan Price #include <spawn.h> 627c478bd9Sstevel@tonic-gate 639444c26fSTom Whitten #include "manifest_hash.h" 647c478bd9Sstevel@tonic-gate #include "configd_exit.h" 657c478bd9Sstevel@tonic-gate #include "protocol.h" 667c478bd9Sstevel@tonic-gate #include "startd.h" 677c478bd9Sstevel@tonic-gate 6876cf44abSjeanm static struct utmpx *utmpp; /* pointer for getutxent() */ 6976cf44abSjeanm 707c478bd9Sstevel@tonic-gate pid_t 717c478bd9Sstevel@tonic-gate startd_fork1(int *forkerr) 727c478bd9Sstevel@tonic-gate { 737c478bd9Sstevel@tonic-gate pid_t p; 747c478bd9Sstevel@tonic-gate 757c478bd9Sstevel@tonic-gate /* 767c478bd9Sstevel@tonic-gate * prefork stack 777c478bd9Sstevel@tonic-gate */ 787c478bd9Sstevel@tonic-gate wait_prefork(); 79*0d421f66SBryan Cantrill utmpx_prefork(); 807c478bd9Sstevel@tonic-gate 817c478bd9Sstevel@tonic-gate p = fork1(); 827c478bd9Sstevel@tonic-gate 837c478bd9Sstevel@tonic-gate if (p == -1 && forkerr != NULL) 847c478bd9Sstevel@tonic-gate *forkerr = errno; 857c478bd9Sstevel@tonic-gate 867c478bd9Sstevel@tonic-gate /* 877c478bd9Sstevel@tonic-gate * postfork stack 887c478bd9Sstevel@tonic-gate */ 89*0d421f66SBryan Cantrill utmpx_postfork(); 907c478bd9Sstevel@tonic-gate wait_postfork(p); 917c478bd9Sstevel@tonic-gate 927c478bd9Sstevel@tonic-gate return (p); 937c478bd9Sstevel@tonic-gate } 947c478bd9Sstevel@tonic-gate 957c478bd9Sstevel@tonic-gate /* 967c478bd9Sstevel@tonic-gate * void fork_mount(char *, char *) 977c478bd9Sstevel@tonic-gate * Run mount(1M) with the given options and mount point. (mount(1M) has much 987c478bd9Sstevel@tonic-gate * hidden knowledge; it's much less correct to reimplement that logic here to 997c478bd9Sstevel@tonic-gate * save a fork(2)/exec(2) invocation.) 1007c478bd9Sstevel@tonic-gate */ 1017c478bd9Sstevel@tonic-gate int 1027c478bd9Sstevel@tonic-gate fork_mount(char *path, char *opts) 1037c478bd9Sstevel@tonic-gate { 1047c478bd9Sstevel@tonic-gate pid_t pid; 1057c478bd9Sstevel@tonic-gate uint_t tries = 0; 1067c478bd9Sstevel@tonic-gate int status; 1077c478bd9Sstevel@tonic-gate 1087c478bd9Sstevel@tonic-gate for (pid = fork1(); pid == -1; pid = fork1()) { 1097c478bd9Sstevel@tonic-gate if (++tries > MAX_MOUNT_RETRIES) 1107c478bd9Sstevel@tonic-gate return (-1); 1117c478bd9Sstevel@tonic-gate 1127c478bd9Sstevel@tonic-gate (void) sleep(tries); 1137c478bd9Sstevel@tonic-gate } 1147c478bd9Sstevel@tonic-gate 1157c478bd9Sstevel@tonic-gate if (pid != 0) { 1167c478bd9Sstevel@tonic-gate (void) waitpid(pid, &status, 0); 1177c478bd9Sstevel@tonic-gate 1187c478bd9Sstevel@tonic-gate /* 1197c478bd9Sstevel@tonic-gate * If our mount(1M) invocation exited by peculiar means, or with 1207c478bd9Sstevel@tonic-gate * a non-zero status, our mount likelihood is low. 1217c478bd9Sstevel@tonic-gate */ 1227c478bd9Sstevel@tonic-gate if (!WIFEXITED(status) || 1237c478bd9Sstevel@tonic-gate WEXITSTATUS(status) != 0) 1247c478bd9Sstevel@tonic-gate return (-1); 1257c478bd9Sstevel@tonic-gate 1267c478bd9Sstevel@tonic-gate return (0); 1277c478bd9Sstevel@tonic-gate } 1287c478bd9Sstevel@tonic-gate 1297c478bd9Sstevel@tonic-gate (void) execl("/sbin/mount", "mount", "-o", opts, path, NULL); 1307c478bd9Sstevel@tonic-gate 1317c478bd9Sstevel@tonic-gate return (-1); 1327c478bd9Sstevel@tonic-gate } 1337c478bd9Sstevel@tonic-gate 1347c478bd9Sstevel@tonic-gate /* 1357c478bd9Sstevel@tonic-gate * pid_t fork_common(...) 1369444c26fSTom Whitten * Common routine used by fork_sulogin, fork_emi, and fork_configd to 1379444c26fSTom Whitten * fork a process in a contract with the provided terms. Invokes 1387c478bd9Sstevel@tonic-gate * fork_sulogin (with its no-fork argument set) on errors. 1397c478bd9Sstevel@tonic-gate */ 1407c478bd9Sstevel@tonic-gate static pid_t 1417b209c2cSacruz fork_common(const char *name, const char *svc_fmri, int retries, ctid_t *ctidp, 1427c478bd9Sstevel@tonic-gate uint_t inf, uint_t crit, uint_t fatal, uint_t param, uint64_t cookie) 1437c478bd9Sstevel@tonic-gate { 1447c478bd9Sstevel@tonic-gate uint_t tries = 0; 1457c478bd9Sstevel@tonic-gate int ctfd, err; 1467c478bd9Sstevel@tonic-gate pid_t pid; 1477c478bd9Sstevel@tonic-gate 1487c478bd9Sstevel@tonic-gate /* 1497c478bd9Sstevel@tonic-gate * Establish process contract terms. 1507c478bd9Sstevel@tonic-gate */ 1517c478bd9Sstevel@tonic-gate if ((ctfd = open64(CTFS_ROOT "/process/template", O_RDWR)) == -1) { 1527c478bd9Sstevel@tonic-gate fork_sulogin(B_TRUE, "Could not open process contract template " 1537c478bd9Sstevel@tonic-gate "for %s: %s\n", name, strerror(errno)); 1547c478bd9Sstevel@tonic-gate /* NOTREACHED */ 1557c478bd9Sstevel@tonic-gate } 1567c478bd9Sstevel@tonic-gate 1577c478bd9Sstevel@tonic-gate err = ct_tmpl_set_critical(ctfd, crit); 1587c478bd9Sstevel@tonic-gate err |= ct_pr_tmpl_set_fatal(ctfd, fatal); 1597c478bd9Sstevel@tonic-gate err |= ct_tmpl_set_informative(ctfd, inf); 1607c478bd9Sstevel@tonic-gate err |= ct_pr_tmpl_set_param(ctfd, param); 1617c478bd9Sstevel@tonic-gate err |= ct_tmpl_set_cookie(ctfd, cookie); 1627b209c2cSacruz err |= ct_pr_tmpl_set_svc_fmri(ctfd, svc_fmri); 1637b209c2cSacruz err |= ct_pr_tmpl_set_svc_aux(ctfd, name); 1647c478bd9Sstevel@tonic-gate if (err) { 1657c478bd9Sstevel@tonic-gate (void) close(ctfd); 1667c478bd9Sstevel@tonic-gate fork_sulogin(B_TRUE, "Could not set %s process contract " 1677c478bd9Sstevel@tonic-gate "terms\n", name); 1687c478bd9Sstevel@tonic-gate /* NOTREACHED */ 1697c478bd9Sstevel@tonic-gate } 1707c478bd9Sstevel@tonic-gate 1717c478bd9Sstevel@tonic-gate if (err = ct_tmpl_activate(ctfd)) { 1727c478bd9Sstevel@tonic-gate (void) close(ctfd); 1737c478bd9Sstevel@tonic-gate fork_sulogin(B_TRUE, "Could not activate %s process contract " 1747c478bd9Sstevel@tonic-gate "template: %s\n", name, strerror(err)); 1757c478bd9Sstevel@tonic-gate /* NOTREACHED */ 1767c478bd9Sstevel@tonic-gate } 1777c478bd9Sstevel@tonic-gate 178*0d421f66SBryan Cantrill utmpx_prefork(); 179*0d421f66SBryan Cantrill 1807c478bd9Sstevel@tonic-gate /* 1817c478bd9Sstevel@tonic-gate * Attempt to fork "retries" times. 1827c478bd9Sstevel@tonic-gate */ 1837c478bd9Sstevel@tonic-gate for (pid = fork1(); pid == -1; pid = fork1()) { 1847c478bd9Sstevel@tonic-gate if (++tries > retries) { 1857c478bd9Sstevel@tonic-gate /* 1867c478bd9Sstevel@tonic-gate * When we exit the sulogin session, init(1M) 1877c478bd9Sstevel@tonic-gate * will restart svc.startd(1M). 1887c478bd9Sstevel@tonic-gate */ 1897c478bd9Sstevel@tonic-gate err = errno; 1907c478bd9Sstevel@tonic-gate (void) ct_tmpl_clear(ctfd); 1917c478bd9Sstevel@tonic-gate (void) close(ctfd); 192*0d421f66SBryan Cantrill utmpx_postfork(); 1937c478bd9Sstevel@tonic-gate fork_sulogin(B_TRUE, "Could not fork to start %s: %s\n", 1947c478bd9Sstevel@tonic-gate name, strerror(err)); 1957c478bd9Sstevel@tonic-gate /* NOTREACHED */ 1967c478bd9Sstevel@tonic-gate } 1977c478bd9Sstevel@tonic-gate (void) sleep(tries); 1987c478bd9Sstevel@tonic-gate } 1997c478bd9Sstevel@tonic-gate 200*0d421f66SBryan Cantrill utmpx_postfork(); 201*0d421f66SBryan Cantrill 2027c478bd9Sstevel@tonic-gate /* 2037c478bd9Sstevel@tonic-gate * Clean up, return pid and ctid. 2047c478bd9Sstevel@tonic-gate */ 2057c478bd9Sstevel@tonic-gate if (pid != 0 && (errno = contract_latest(ctidp)) != 0) 2067c478bd9Sstevel@tonic-gate uu_die("Could not get new contract id for %s\n", name); 2077c478bd9Sstevel@tonic-gate (void) ct_tmpl_clear(ctfd); 2087c478bd9Sstevel@tonic-gate (void) close(ctfd); 2097c478bd9Sstevel@tonic-gate 2107c478bd9Sstevel@tonic-gate return (pid); 2117c478bd9Sstevel@tonic-gate } 2127c478bd9Sstevel@tonic-gate 2137c478bd9Sstevel@tonic-gate /* 2147c478bd9Sstevel@tonic-gate * void fork_sulogin(boolean_t, const char *, ...) 2157c478bd9Sstevel@tonic-gate * When we are invoked with the -s flag from boot (or run into an unfixable 2167c478bd9Sstevel@tonic-gate * situation), we run a private copy of sulogin. When the sulogin session 2177c478bd9Sstevel@tonic-gate * is ended, we continue. This is the last fallback action for system 2187c478bd9Sstevel@tonic-gate * maintenance. 2197c478bd9Sstevel@tonic-gate * 2207c478bd9Sstevel@tonic-gate * If immediate is true, fork_sulogin() executes sulogin(1M) directly, without 2217c478bd9Sstevel@tonic-gate * forking. 2227c478bd9Sstevel@tonic-gate * 2237c478bd9Sstevel@tonic-gate * Because fork_sulogin() is needed potentially before we daemonize, we leave 2247c478bd9Sstevel@tonic-gate * it outside the wait_register() framework. 2257c478bd9Sstevel@tonic-gate */ 2267c478bd9Sstevel@tonic-gate /*PRINTFLIKE2*/ 2277c478bd9Sstevel@tonic-gate void 2287c478bd9Sstevel@tonic-gate fork_sulogin(boolean_t immediate, const char *format, ...) 2297c478bd9Sstevel@tonic-gate { 2307c478bd9Sstevel@tonic-gate va_list args; 2312f1b831aSacruz int fd_console; 2327c478bd9Sstevel@tonic-gate 2337c478bd9Sstevel@tonic-gate (void) printf("Requesting System Maintenance Mode\n"); 2347c478bd9Sstevel@tonic-gate 2357c478bd9Sstevel@tonic-gate if (!booting_to_single_user) 2367c478bd9Sstevel@tonic-gate (void) printf("(See /lib/svc/share/README for more " 2377c478bd9Sstevel@tonic-gate "information.)\n"); 2387c478bd9Sstevel@tonic-gate 2397c478bd9Sstevel@tonic-gate va_start(args, format); 2407c478bd9Sstevel@tonic-gate (void) vprintf(format, args); 2417c478bd9Sstevel@tonic-gate va_end(args); 2427c478bd9Sstevel@tonic-gate 2437c478bd9Sstevel@tonic-gate if (!immediate) { 2447c478bd9Sstevel@tonic-gate ctid_t ctid; 2457c478bd9Sstevel@tonic-gate pid_t pid; 2467c478bd9Sstevel@tonic-gate 2477b209c2cSacruz pid = fork_common("sulogin", SVC_SULOGIN_FMRI, 2487b209c2cSacruz MAX_SULOGIN_RETRIES, &ctid, CT_PR_EV_HWERR, 0, 2497b209c2cSacruz CT_PR_EV_HWERR, CT_PR_PGRPONLY, SULOGIN_COOKIE); 2507c478bd9Sstevel@tonic-gate 2517c478bd9Sstevel@tonic-gate if (pid != 0) { 2527c478bd9Sstevel@tonic-gate (void) waitpid(pid, NULL, 0); 2537c478bd9Sstevel@tonic-gate contract_abandon(ctid); 2547c478bd9Sstevel@tonic-gate return; 2557c478bd9Sstevel@tonic-gate } 2567c478bd9Sstevel@tonic-gate /* close all inherited fds */ 2577c478bd9Sstevel@tonic-gate closefrom(0); 2587c478bd9Sstevel@tonic-gate } else { 2597c478bd9Sstevel@tonic-gate (void) printf("Directly executing sulogin.\n"); 2607c478bd9Sstevel@tonic-gate /* 2617c478bd9Sstevel@tonic-gate * Can't call closefrom() in this MT section 2627c478bd9Sstevel@tonic-gate * so safely close a minimum set of fds. 2637c478bd9Sstevel@tonic-gate */ 2642f1b831aSacruz (void) close(STDIN_FILENO); 2652f1b831aSacruz (void) close(STDOUT_FILENO); 2662f1b831aSacruz (void) close(STDERR_FILENO); 2677c478bd9Sstevel@tonic-gate } 2687c478bd9Sstevel@tonic-gate 2697c478bd9Sstevel@tonic-gate (void) setpgrp(); 2707c478bd9Sstevel@tonic-gate 2717c478bd9Sstevel@tonic-gate /* open the console for sulogin */ 2727c478bd9Sstevel@tonic-gate if ((fd_console = open("/dev/console", O_RDWR)) >= 0) { 2737c478bd9Sstevel@tonic-gate if (fd_console != STDIN_FILENO) 2747c478bd9Sstevel@tonic-gate while (dup2(fd_console, STDIN_FILENO) < 0 && 2757c478bd9Sstevel@tonic-gate errno == EINTR) 2767c478bd9Sstevel@tonic-gate ; 2777c478bd9Sstevel@tonic-gate if (fd_console != STDOUT_FILENO) 2787c478bd9Sstevel@tonic-gate while (dup2(fd_console, STDOUT_FILENO) < 0 && 2797c478bd9Sstevel@tonic-gate errno == EINTR) 2807c478bd9Sstevel@tonic-gate ; 2817c478bd9Sstevel@tonic-gate if (fd_console != STDERR_FILENO) 2827c478bd9Sstevel@tonic-gate while (dup2(fd_console, STDERR_FILENO) < 0 && 2837c478bd9Sstevel@tonic-gate errno == EINTR) 2847c478bd9Sstevel@tonic-gate ; 2852f1b831aSacruz if (fd_console > STDERR_FILENO) 2867c478bd9Sstevel@tonic-gate (void) close(fd_console); 2877c478bd9Sstevel@tonic-gate } 2887c478bd9Sstevel@tonic-gate 28976cf44abSjeanm setutxent(); 29076cf44abSjeanm while ((utmpp = getutxent()) != NULL) { 29176cf44abSjeanm if (strcmp(utmpp->ut_user, "LOGIN") != 0) { 29276cf44abSjeanm if (strcmp(utmpp->ut_line, "console") == 0) { 29376cf44abSjeanm (void) kill(utmpp->ut_pid, 9); 29476cf44abSjeanm break; 29576cf44abSjeanm } 29676cf44abSjeanm } 29776cf44abSjeanm } 29876cf44abSjeanm 2997c478bd9Sstevel@tonic-gate (void) execl("/sbin/sulogin", "sulogin", NULL); 3007c478bd9Sstevel@tonic-gate 3017c478bd9Sstevel@tonic-gate uu_warn("Could not exec() sulogin"); 3027c478bd9Sstevel@tonic-gate 3037c478bd9Sstevel@tonic-gate exit(1); 3047c478bd9Sstevel@tonic-gate } 3057c478bd9Sstevel@tonic-gate 3067c478bd9Sstevel@tonic-gate #define CONFIGD_PATH "/lib/svc/bin/svc.configd" 3077c478bd9Sstevel@tonic-gate 3087c478bd9Sstevel@tonic-gate /* 3097c478bd9Sstevel@tonic-gate * void fork_configd(int status) 3107c478bd9Sstevel@tonic-gate * We are interested in exit events (since the parent's exiting means configd 3117c478bd9Sstevel@tonic-gate * is ready to run and since the child's exiting indicates an error case) and 3127c478bd9Sstevel@tonic-gate * in empty events. This means we have a unique template for initiating 3137c478bd9Sstevel@tonic-gate * configd. 3147c478bd9Sstevel@tonic-gate */ 3157c478bd9Sstevel@tonic-gate void 3167c478bd9Sstevel@tonic-gate fork_configd(int exitstatus) 3177c478bd9Sstevel@tonic-gate { 3187c478bd9Sstevel@tonic-gate pid_t pid; 3197c478bd9Sstevel@tonic-gate ctid_t ctid = -1; 3207c478bd9Sstevel@tonic-gate int err; 3217c478bd9Sstevel@tonic-gate char path[PATH_MAX]; 3227c478bd9Sstevel@tonic-gate 3234362b902SSean Wilcox /* 3244362b902SSean Wilcox * Checking the existatus for the potential failure of the 3254362b902SSean Wilcox * daemonized svc.configd. If this is not the first time 3264362b902SSean Wilcox * through, but a call from the svc.configd monitoring thread 3274362b902SSean Wilcox * after a failure this is the status that is expected. Other 3284362b902SSean Wilcox * failures are exposed during initialization or are fixed 3294362b902SSean Wilcox * by a restart (e.g door closings). 3304362b902SSean Wilcox * 3314362b902SSean Wilcox * If this is on-disk database corruption it will also be 3324362b902SSean Wilcox * caught by a restart but could be cleared before the restart. 3334362b902SSean Wilcox * 3344362b902SSean Wilcox * Or this could be internal database corruption due to a 3354362b902SSean Wilcox * rogue service that needs to be cleared before restart. 3364362b902SSean Wilcox */ 3374362b902SSean Wilcox if (WEXITSTATUS(exitstatus) == CONFIGD_EXIT_DATABASE_BAD) { 3384362b902SSean Wilcox fork_sulogin(B_FALSE, "svc.configd exited with database " 3394362b902SSean Wilcox "corrupt error after initialization of the repository\n"); 3404362b902SSean Wilcox } 3414362b902SSean Wilcox 3427c478bd9Sstevel@tonic-gate retry: 3437c478bd9Sstevel@tonic-gate log_framework(LOG_DEBUG, "fork_configd trying to start svc.configd\n"); 3447c478bd9Sstevel@tonic-gate 3457c478bd9Sstevel@tonic-gate /* 3467c478bd9Sstevel@tonic-gate * If we're retrying, we will have an old contract lying around 3477c478bd9Sstevel@tonic-gate * from the failure. Since we're going to be creating a new 3487c478bd9Sstevel@tonic-gate * contract shortly, we abandon the old one now. 3497c478bd9Sstevel@tonic-gate */ 3507c478bd9Sstevel@tonic-gate if (ctid != -1) 3517c478bd9Sstevel@tonic-gate contract_abandon(ctid); 3527c478bd9Sstevel@tonic-gate ctid = -1; 3537c478bd9Sstevel@tonic-gate 3547b209c2cSacruz pid = fork_common("svc.configd", SCF_SERVICE_CONFIGD, 3557b209c2cSacruz MAX_CONFIGD_RETRIES, &ctid, 0, CT_PR_EV_EXIT, 0, 3567b209c2cSacruz CT_PR_INHERIT | CT_PR_REGENT, CONFIGD_COOKIE); 3577c478bd9Sstevel@tonic-gate 3587c478bd9Sstevel@tonic-gate if (pid != 0) { 3597c478bd9Sstevel@tonic-gate int exitstatus; 3607c478bd9Sstevel@tonic-gate 3617c478bd9Sstevel@tonic-gate st->st_configd_pid = pid; 3627c478bd9Sstevel@tonic-gate 3637c478bd9Sstevel@tonic-gate if (waitpid(pid, &exitstatus, 0) == -1) { 3647c478bd9Sstevel@tonic-gate fork_sulogin(B_FALSE, "waitpid on svc.configd " 3657c478bd9Sstevel@tonic-gate "failed: %s\n", strerror(errno)); 3667c478bd9Sstevel@tonic-gate } else if (WIFEXITED(exitstatus)) { 3677c478bd9Sstevel@tonic-gate char *errstr; 3687c478bd9Sstevel@tonic-gate 3697c478bd9Sstevel@tonic-gate /* 3707c478bd9Sstevel@tonic-gate * Examine exitstatus. This will eventually get more 3717c478bd9Sstevel@tonic-gate * complicated, as we will want to teach startd how to 3727c478bd9Sstevel@tonic-gate * invoke configd with alternate repositories, etc. 3737c478bd9Sstevel@tonic-gate * 3747c478bd9Sstevel@tonic-gate * Note that exec(2) failure results in an exit status 3757c478bd9Sstevel@tonic-gate * of 1, resulting in the default clause below. 3767c478bd9Sstevel@tonic-gate */ 3777c478bd9Sstevel@tonic-gate 3787c478bd9Sstevel@tonic-gate /* 3797c478bd9Sstevel@tonic-gate * Assign readable strings to cases we don't handle, or 3807c478bd9Sstevel@tonic-gate * have error outcomes that cannot be eliminated. 3817c478bd9Sstevel@tonic-gate */ 3827c478bd9Sstevel@tonic-gate switch (WEXITSTATUS(exitstatus)) { 3837c478bd9Sstevel@tonic-gate case CONFIGD_EXIT_BAD_ARGS: 3847c478bd9Sstevel@tonic-gate errstr = "bad arguments"; 3857c478bd9Sstevel@tonic-gate break; 3867c478bd9Sstevel@tonic-gate 3877c478bd9Sstevel@tonic-gate case CONFIGD_EXIT_DATABASE_BAD: 3887c478bd9Sstevel@tonic-gate errstr = "database corrupt"; 3897c478bd9Sstevel@tonic-gate break; 3907c478bd9Sstevel@tonic-gate 3917c478bd9Sstevel@tonic-gate case CONFIGD_EXIT_DATABASE_LOCKED: 3927c478bd9Sstevel@tonic-gate errstr = "database locked"; 3937c478bd9Sstevel@tonic-gate break; 3947c478bd9Sstevel@tonic-gate case CONFIGD_EXIT_INIT_FAILED: 3957c478bd9Sstevel@tonic-gate errstr = "initialization failure"; 3967c478bd9Sstevel@tonic-gate break; 3977c478bd9Sstevel@tonic-gate case CONFIGD_EXIT_DOOR_INIT_FAILED: 3987c478bd9Sstevel@tonic-gate errstr = "door initialization failure"; 3997c478bd9Sstevel@tonic-gate break; 4007c478bd9Sstevel@tonic-gate case CONFIGD_EXIT_DATABASE_INIT_FAILED: 4017c478bd9Sstevel@tonic-gate errstr = "database initialization failure"; 4027c478bd9Sstevel@tonic-gate break; 4037c478bd9Sstevel@tonic-gate case CONFIGD_EXIT_NO_THREADS: 4047c478bd9Sstevel@tonic-gate errstr = "no threads available"; 4057c478bd9Sstevel@tonic-gate break; 4067c478bd9Sstevel@tonic-gate case CONFIGD_EXIT_LOST_MAIN_DOOR: 4077c478bd9Sstevel@tonic-gate errstr = "lost door server attachment"; 4087c478bd9Sstevel@tonic-gate break; 4097c478bd9Sstevel@tonic-gate case 1: 4107c478bd9Sstevel@tonic-gate errstr = "execution failure"; 4117c478bd9Sstevel@tonic-gate break; 4127c478bd9Sstevel@tonic-gate default: 4137c478bd9Sstevel@tonic-gate errstr = "unknown error"; 4147c478bd9Sstevel@tonic-gate break; 4157c478bd9Sstevel@tonic-gate } 4167c478bd9Sstevel@tonic-gate 4177c478bd9Sstevel@tonic-gate /* 4187c478bd9Sstevel@tonic-gate * Remedial actions for various configd failures. 4197c478bd9Sstevel@tonic-gate */ 4207c478bd9Sstevel@tonic-gate switch (WEXITSTATUS(exitstatus)) { 4217c478bd9Sstevel@tonic-gate case CONFIGD_EXIT_OKAY: 4227c478bd9Sstevel@tonic-gate break; 4237c478bd9Sstevel@tonic-gate 4247c478bd9Sstevel@tonic-gate case CONFIGD_EXIT_DATABASE_LOCKED: 4257c478bd9Sstevel@tonic-gate /* attempt remount of / read-write */ 4267c478bd9Sstevel@tonic-gate if (fs_is_read_only("/", NULL) == 1) { 4277c478bd9Sstevel@tonic-gate if (fs_remount("/") == -1) 4287c478bd9Sstevel@tonic-gate fork_sulogin(B_FALSE, 4297c478bd9Sstevel@tonic-gate "remount of root " 4307c478bd9Sstevel@tonic-gate "filesystem failed\n"); 4317c478bd9Sstevel@tonic-gate 4327c478bd9Sstevel@tonic-gate goto retry; 4337c478bd9Sstevel@tonic-gate } 4347c478bd9Sstevel@tonic-gate break; 4357c478bd9Sstevel@tonic-gate 4367c478bd9Sstevel@tonic-gate default: 4377c478bd9Sstevel@tonic-gate fork_sulogin(B_FALSE, "svc.configd exited " 4387c478bd9Sstevel@tonic-gate "with status %d (%s)\n", 4397c478bd9Sstevel@tonic-gate WEXITSTATUS(exitstatus), errstr); 4407c478bd9Sstevel@tonic-gate goto retry; 4417c478bd9Sstevel@tonic-gate } 4427c478bd9Sstevel@tonic-gate } else if (WIFSIGNALED(exitstatus)) { 4437c478bd9Sstevel@tonic-gate char signame[SIG2STR_MAX]; 4447c478bd9Sstevel@tonic-gate 4457c478bd9Sstevel@tonic-gate if (sig2str(WTERMSIG(exitstatus), signame)) 4467c478bd9Sstevel@tonic-gate (void) snprintf(signame, SIG2STR_MAX, 4477c478bd9Sstevel@tonic-gate "signum %d", WTERMSIG(exitstatus)); 4487c478bd9Sstevel@tonic-gate 4497c478bd9Sstevel@tonic-gate fork_sulogin(B_FALSE, "svc.configd signalled:" 4507c478bd9Sstevel@tonic-gate " %s\n", signame); 4517c478bd9Sstevel@tonic-gate 4527c478bd9Sstevel@tonic-gate goto retry; 4537c478bd9Sstevel@tonic-gate } else { 4547c478bd9Sstevel@tonic-gate fork_sulogin(B_FALSE, "svc.configd non-exit " 4557c478bd9Sstevel@tonic-gate "condition: 0x%x\n", exitstatus); 4567c478bd9Sstevel@tonic-gate 4577c478bd9Sstevel@tonic-gate goto retry; 4587c478bd9Sstevel@tonic-gate } 4597c478bd9Sstevel@tonic-gate 4607c478bd9Sstevel@tonic-gate /* 4617c478bd9Sstevel@tonic-gate * Announce that we have a valid svc.configd status. 4627c478bd9Sstevel@tonic-gate */ 4637c478bd9Sstevel@tonic-gate MUTEX_LOCK(&st->st_configd_live_lock); 4647c478bd9Sstevel@tonic-gate st->st_configd_lives = 1; 4657c478bd9Sstevel@tonic-gate err = pthread_cond_broadcast(&st->st_configd_live_cv); 4667c478bd9Sstevel@tonic-gate assert(err == 0); 4677c478bd9Sstevel@tonic-gate MUTEX_UNLOCK(&st->st_configd_live_lock); 4687c478bd9Sstevel@tonic-gate 4697c478bd9Sstevel@tonic-gate log_framework(LOG_DEBUG, "fork_configd broadcasts configd is " 4707c478bd9Sstevel@tonic-gate "live\n"); 4717c478bd9Sstevel@tonic-gate return; 4727c478bd9Sstevel@tonic-gate } 4737c478bd9Sstevel@tonic-gate 4747c478bd9Sstevel@tonic-gate /* 4757c478bd9Sstevel@tonic-gate * Set our per-process core file path to leave core files in 4767c478bd9Sstevel@tonic-gate * /etc/svc/volatile directory, named after the PID to aid in debugging. 4777c478bd9Sstevel@tonic-gate */ 4787c478bd9Sstevel@tonic-gate (void) snprintf(path, sizeof (path), 4797c478bd9Sstevel@tonic-gate "/etc/svc/volatile/core.configd.%%p"); 4807c478bd9Sstevel@tonic-gate 4817c478bd9Sstevel@tonic-gate (void) core_set_process_path(path, strlen(path) + 1, getpid()); 4827c478bd9Sstevel@tonic-gate 4837c478bd9Sstevel@tonic-gate log_framework(LOG_DEBUG, "executing svc.configd\n"); 4847c478bd9Sstevel@tonic-gate 4857c478bd9Sstevel@tonic-gate (void) execl(CONFIGD_PATH, CONFIGD_PATH, NULL); 4867c478bd9Sstevel@tonic-gate 4877c478bd9Sstevel@tonic-gate /* 4887c478bd9Sstevel@tonic-gate * Status code is used above to identify configd exec failure. 4897c478bd9Sstevel@tonic-gate */ 4907c478bd9Sstevel@tonic-gate exit(1); 4917c478bd9Sstevel@tonic-gate } 4927c478bd9Sstevel@tonic-gate 4937c478bd9Sstevel@tonic-gate void * 4947c478bd9Sstevel@tonic-gate fork_configd_thread(void *vctid) 4957c478bd9Sstevel@tonic-gate { 4967c478bd9Sstevel@tonic-gate int fd, err; 4977c478bd9Sstevel@tonic-gate ctid_t configd_ctid = (ctid_t)vctid; 4987c478bd9Sstevel@tonic-gate 4997c478bd9Sstevel@tonic-gate if (configd_ctid == -1) { 5007c478bd9Sstevel@tonic-gate log_framework(LOG_DEBUG, 5017c478bd9Sstevel@tonic-gate "fork_configd_thread starting svc.configd\n"); 5027c478bd9Sstevel@tonic-gate fork_configd(0); 5037c478bd9Sstevel@tonic-gate } else { 5047c478bd9Sstevel@tonic-gate /* 5057c478bd9Sstevel@tonic-gate * configd_ctid is known: we broadcast and continue. 5067c478bd9Sstevel@tonic-gate * test contract for appropriate state by verifying that 5077c478bd9Sstevel@tonic-gate * there is one or more processes within it? 5087c478bd9Sstevel@tonic-gate */ 5097c478bd9Sstevel@tonic-gate log_framework(LOG_DEBUG, 5107c478bd9Sstevel@tonic-gate "fork_configd_thread accepting svc.configd with CTID %ld\n", 5117c478bd9Sstevel@tonic-gate configd_ctid); 5127c478bd9Sstevel@tonic-gate MUTEX_LOCK(&st->st_configd_live_lock); 5137c478bd9Sstevel@tonic-gate st->st_configd_lives = 1; 5147c478bd9Sstevel@tonic-gate (void) pthread_cond_broadcast(&st->st_configd_live_cv); 5157c478bd9Sstevel@tonic-gate MUTEX_UNLOCK(&st->st_configd_live_lock); 5167c478bd9Sstevel@tonic-gate } 5177c478bd9Sstevel@tonic-gate 5187c478bd9Sstevel@tonic-gate fd = open64(CTFS_ROOT "/process/pbundle", O_RDONLY); 5197c478bd9Sstevel@tonic-gate if (fd == -1) 5207c478bd9Sstevel@tonic-gate uu_die("process bundle open failed"); 5217c478bd9Sstevel@tonic-gate 5227c478bd9Sstevel@tonic-gate /* 5237c478bd9Sstevel@tonic-gate * Make sure we get all events (including those generated by configd 5247c478bd9Sstevel@tonic-gate * before this thread was started). 5257c478bd9Sstevel@tonic-gate */ 5267c478bd9Sstevel@tonic-gate err = ct_event_reset(fd); 5277c478bd9Sstevel@tonic-gate assert(err == 0); 5287c478bd9Sstevel@tonic-gate 5297c478bd9Sstevel@tonic-gate for (;;) { 5307c478bd9Sstevel@tonic-gate int efd, sfd; 5317c478bd9Sstevel@tonic-gate ct_evthdl_t ev; 5327c478bd9Sstevel@tonic-gate uint32_t type; 5337c478bd9Sstevel@tonic-gate ctevid_t evid; 5347c478bd9Sstevel@tonic-gate ct_stathdl_t status; 5357c478bd9Sstevel@tonic-gate ctid_t ctid; 5367c478bd9Sstevel@tonic-gate uint64_t cookie; 5377c478bd9Sstevel@tonic-gate pid_t pid; 5387c478bd9Sstevel@tonic-gate 5397c478bd9Sstevel@tonic-gate if (err = ct_event_read_critical(fd, &ev)) { 5407c478bd9Sstevel@tonic-gate assert(err != EINVAL && err != EAGAIN); 5417c478bd9Sstevel@tonic-gate log_error(LOG_WARNING, 5427c478bd9Sstevel@tonic-gate "Error reading next contract event: %s", 5437c478bd9Sstevel@tonic-gate strerror(err)); 5447c478bd9Sstevel@tonic-gate continue; 5457c478bd9Sstevel@tonic-gate } 5467c478bd9Sstevel@tonic-gate 5477c478bd9Sstevel@tonic-gate evid = ct_event_get_evid(ev); 5487c478bd9Sstevel@tonic-gate ctid = ct_event_get_ctid(ev); 5497c478bd9Sstevel@tonic-gate type = ct_event_get_type(ev); 5507c478bd9Sstevel@tonic-gate 5517c478bd9Sstevel@tonic-gate /* Fetch cookie. */ 5527c478bd9Sstevel@tonic-gate sfd = contract_open(ctid, "process", "status", O_RDONLY); 5537c478bd9Sstevel@tonic-gate if (sfd < 0) { 5547c478bd9Sstevel@tonic-gate ct_event_free(ev); 5557c478bd9Sstevel@tonic-gate continue; 5567c478bd9Sstevel@tonic-gate } 5577c478bd9Sstevel@tonic-gate 5587c478bd9Sstevel@tonic-gate if (err = ct_status_read(sfd, CTD_COMMON, &status)) { 5597c478bd9Sstevel@tonic-gate log_framework(LOG_WARNING, "Could not get status for " 5607c478bd9Sstevel@tonic-gate "contract %ld: %s\n", ctid, strerror(err)); 5617c478bd9Sstevel@tonic-gate 5627c478bd9Sstevel@tonic-gate ct_event_free(ev); 5637c478bd9Sstevel@tonic-gate startd_close(sfd); 5647c478bd9Sstevel@tonic-gate continue; 5657c478bd9Sstevel@tonic-gate } 5667c478bd9Sstevel@tonic-gate 5677c478bd9Sstevel@tonic-gate cookie = ct_status_get_cookie(status); 5687c478bd9Sstevel@tonic-gate 5697c478bd9Sstevel@tonic-gate ct_status_free(status); 5707c478bd9Sstevel@tonic-gate 5717c478bd9Sstevel@tonic-gate startd_close(sfd); 5727c478bd9Sstevel@tonic-gate 5737c478bd9Sstevel@tonic-gate /* 5747c478bd9Sstevel@tonic-gate * Don't process events from contracts we aren't interested in. 5757c478bd9Sstevel@tonic-gate */ 5767c478bd9Sstevel@tonic-gate if (cookie != CONFIGD_COOKIE) { 5777c478bd9Sstevel@tonic-gate ct_event_free(ev); 5787c478bd9Sstevel@tonic-gate continue; 5797c478bd9Sstevel@tonic-gate } 5807c478bd9Sstevel@tonic-gate 5817c478bd9Sstevel@tonic-gate if (type == CT_PR_EV_EXIT) { 5827c478bd9Sstevel@tonic-gate int exitstatus; 5837c478bd9Sstevel@tonic-gate 5847c478bd9Sstevel@tonic-gate (void) ct_pr_event_get_pid(ev, &pid); 5857c478bd9Sstevel@tonic-gate (void) ct_pr_event_get_exitstatus(ev, 5867c478bd9Sstevel@tonic-gate &exitstatus); 5877c478bd9Sstevel@tonic-gate 5887c478bd9Sstevel@tonic-gate if (st->st_configd_pid != pid) { 5897c478bd9Sstevel@tonic-gate /* 5907c478bd9Sstevel@tonic-gate * This is the child exiting, so we 5917c478bd9Sstevel@tonic-gate * abandon the contract and restart 5927c478bd9Sstevel@tonic-gate * configd. 5937c478bd9Sstevel@tonic-gate */ 5947c478bd9Sstevel@tonic-gate contract_abandon(ctid); 5957c478bd9Sstevel@tonic-gate fork_configd(exitstatus); 5967c478bd9Sstevel@tonic-gate } 5977c478bd9Sstevel@tonic-gate } 5987c478bd9Sstevel@tonic-gate 5997c478bd9Sstevel@tonic-gate efd = contract_open(ctid, "process", "ctl", O_WRONLY); 6007c478bd9Sstevel@tonic-gate if (efd != -1) { 6017c478bd9Sstevel@tonic-gate (void) ct_ctl_ack(efd, evid); 6027c478bd9Sstevel@tonic-gate startd_close(efd); 6037c478bd9Sstevel@tonic-gate } 6047c478bd9Sstevel@tonic-gate 6057c478bd9Sstevel@tonic-gate ct_event_free(ev); 6067c478bd9Sstevel@tonic-gate 6077c478bd9Sstevel@tonic-gate } 6087c478bd9Sstevel@tonic-gate 6097c478bd9Sstevel@tonic-gate /*NOTREACHED*/ 6107c478bd9Sstevel@tonic-gate return (NULL); 6117c478bd9Sstevel@tonic-gate } 6127c478bd9Sstevel@tonic-gate 6137c478bd9Sstevel@tonic-gate void 6147c478bd9Sstevel@tonic-gate fork_rc_script(char rl, const char *arg, boolean_t wait) 6157c478bd9Sstevel@tonic-gate { 6167c478bd9Sstevel@tonic-gate pid_t pid; 6177c478bd9Sstevel@tonic-gate int tmpl, err, stat; 6187c478bd9Sstevel@tonic-gate char path[20] = "/sbin/rc.", log[20] = "rc..log", timebuf[20]; 6197c478bd9Sstevel@tonic-gate time_t now; 6207c478bd9Sstevel@tonic-gate struct tm ltime; 6217c478bd9Sstevel@tonic-gate size_t sz; 6227c478bd9Sstevel@tonic-gate char *pathenv; 6237c478bd9Sstevel@tonic-gate char **nenv; 6247c478bd9Sstevel@tonic-gate 6257c478bd9Sstevel@tonic-gate path[8] = rl; 6267c478bd9Sstevel@tonic-gate 6277c478bd9Sstevel@tonic-gate tmpl = open64(CTFS_ROOT "/process/template", O_RDWR); 6287c478bd9Sstevel@tonic-gate if (tmpl >= 0) { 6297c478bd9Sstevel@tonic-gate err = ct_tmpl_set_critical(tmpl, 0); 6307c478bd9Sstevel@tonic-gate assert(err == 0); 6317c478bd9Sstevel@tonic-gate 6327c478bd9Sstevel@tonic-gate err = ct_tmpl_set_informative(tmpl, 0); 6337c478bd9Sstevel@tonic-gate assert(err == 0); 6347c478bd9Sstevel@tonic-gate 6357c478bd9Sstevel@tonic-gate err = ct_pr_tmpl_set_fatal(tmpl, 0); 6367c478bd9Sstevel@tonic-gate assert(err == 0); 6377c478bd9Sstevel@tonic-gate 6387c478bd9Sstevel@tonic-gate err = ct_tmpl_activate(tmpl); 6397c478bd9Sstevel@tonic-gate assert(err == 0); 6407c478bd9Sstevel@tonic-gate 6417c478bd9Sstevel@tonic-gate err = close(tmpl); 6427c478bd9Sstevel@tonic-gate assert(err == 0); 6437c478bd9Sstevel@tonic-gate } else { 6447c478bd9Sstevel@tonic-gate uu_warn("Could not create contract template for %s.\n", path); 6457c478bd9Sstevel@tonic-gate } 6467c478bd9Sstevel@tonic-gate 6477c478bd9Sstevel@tonic-gate pid = startd_fork1(NULL); 6487c478bd9Sstevel@tonic-gate if (pid < 0) { 6497c478bd9Sstevel@tonic-gate return; 6507c478bd9Sstevel@tonic-gate } else if (pid != 0) { 6517c478bd9Sstevel@tonic-gate /* parent */ 6527c478bd9Sstevel@tonic-gate if (wait) { 6537c478bd9Sstevel@tonic-gate do 6547c478bd9Sstevel@tonic-gate err = waitpid(pid, &stat, 0); 65576cf44abSjeanm while (err != 0 && errno == EINTR) 65676cf44abSjeanm ; 6577c478bd9Sstevel@tonic-gate 6587c478bd9Sstevel@tonic-gate if (!WIFEXITED(stat)) { 6597c478bd9Sstevel@tonic-gate log_framework(LOG_INFO, 6607c478bd9Sstevel@tonic-gate "%s terminated with waitpid() status %d.\n", 6617c478bd9Sstevel@tonic-gate path, stat); 6627c478bd9Sstevel@tonic-gate } else if (WEXITSTATUS(stat) != 0) { 6637c478bd9Sstevel@tonic-gate log_framework(LOG_INFO, 6647c478bd9Sstevel@tonic-gate "%s failed with status %d.\n", path, 6657c478bd9Sstevel@tonic-gate WEXITSTATUS(stat)); 6667c478bd9Sstevel@tonic-gate } 6677c478bd9Sstevel@tonic-gate } 6687c478bd9Sstevel@tonic-gate 6697c478bd9Sstevel@tonic-gate return; 6707c478bd9Sstevel@tonic-gate } 6717c478bd9Sstevel@tonic-gate 6727c478bd9Sstevel@tonic-gate /* child */ 6737c478bd9Sstevel@tonic-gate 6747c478bd9Sstevel@tonic-gate log[2] = rl; 6757c478bd9Sstevel@tonic-gate 6767c478bd9Sstevel@tonic-gate setlog(log); 6777c478bd9Sstevel@tonic-gate 6787c478bd9Sstevel@tonic-gate now = time(NULL); 6797c478bd9Sstevel@tonic-gate sz = strftime(timebuf, sizeof (timebuf), "%b %e %T", 6807c478bd9Sstevel@tonic-gate localtime_r(&now, <ime)); 6817c478bd9Sstevel@tonic-gate assert(sz != 0); 6827c478bd9Sstevel@tonic-gate 6837c478bd9Sstevel@tonic-gate (void) fprintf(stderr, "%s Executing %s %s\n", timebuf, path, arg); 6847c478bd9Sstevel@tonic-gate 6857c478bd9Sstevel@tonic-gate if (rl == 'S') 6867c478bd9Sstevel@tonic-gate pathenv = "PATH=/sbin:/usr/sbin:/usr/bin"; 6877c478bd9Sstevel@tonic-gate else 6887c478bd9Sstevel@tonic-gate pathenv = "PATH=/usr/sbin:/usr/bin"; 6897c478bd9Sstevel@tonic-gate 6907c478bd9Sstevel@tonic-gate nenv = set_smf_env(NULL, 0, pathenv, NULL, NULL); 6917c478bd9Sstevel@tonic-gate 6927c478bd9Sstevel@tonic-gate (void) execle(path, path, arg, 0, nenv); 6937c478bd9Sstevel@tonic-gate 6947c478bd9Sstevel@tonic-gate perror("exec"); 6957c478bd9Sstevel@tonic-gate exit(0); 6967c478bd9Sstevel@tonic-gate } 6974d53c7adSDan Price 6989444c26fSTom Whitten #define SVCCFG_PATH "/usr/sbin/svccfg" 6999444c26fSTom Whitten #define EMI_MFST "/lib/svc/manifest/system/early-manifest-import.xml" 7009444c26fSTom Whitten #define EMI_PATH "/lib/svc/method/manifest-import" 7019444c26fSTom Whitten 7029444c26fSTom Whitten /* 7039444c26fSTom Whitten * Set Early Manifest Import service's state and log file. 7049444c26fSTom Whitten */ 7059444c26fSTom Whitten static int 7069444c26fSTom Whitten emi_set_state(restarter_instance_state_t state, boolean_t setlog) 7079444c26fSTom Whitten { 7089444c26fSTom Whitten int r, ret = 1; 7099444c26fSTom Whitten instance_data_t idata; 7109444c26fSTom Whitten scf_handle_t *hndl = NULL; 7119444c26fSTom Whitten scf_instance_t *inst = NULL; 7129444c26fSTom Whitten 7139444c26fSTom Whitten retry: 7149444c26fSTom Whitten if (hndl == NULL) 7159444c26fSTom Whitten hndl = libscf_handle_create_bound(SCF_VERSION); 7169444c26fSTom Whitten 7179444c26fSTom Whitten if (hndl == NULL) { 7189444c26fSTom Whitten /* 7199444c26fSTom Whitten * In the case that we can't bind to the repository 7209444c26fSTom Whitten * (which should have been started), we need to allow 7219444c26fSTom Whitten * the user into maintenance mode to determine what's 7229444c26fSTom Whitten * failed. 7239444c26fSTom Whitten */ 7249444c26fSTom Whitten fork_sulogin(B_FALSE, "Unable to bind a new repository" 7259444c26fSTom Whitten " handle: %s\n", scf_strerror(scf_error())); 7269444c26fSTom Whitten goto retry; 7279444c26fSTom Whitten } 7289444c26fSTom Whitten 7299444c26fSTom Whitten if (inst == NULL) 7309444c26fSTom Whitten inst = safe_scf_instance_create(hndl); 7319444c26fSTom Whitten 7329444c26fSTom Whitten if (scf_handle_decode_fmri(hndl, SCF_INSTANCE_EMI, NULL, NULL, 7339444c26fSTom Whitten inst, NULL, NULL, SCF_DECODE_FMRI_EXACT) == -1) { 7349444c26fSTom Whitten switch (scf_error()) { 7359444c26fSTom Whitten case SCF_ERROR_NOT_FOUND: 7369444c26fSTom Whitten goto out; 7379444c26fSTom Whitten 7389444c26fSTom Whitten case SCF_ERROR_CONNECTION_BROKEN: 7399444c26fSTom Whitten case SCF_ERROR_NOT_BOUND: 7409444c26fSTom Whitten libscf_handle_rebind(hndl); 7419444c26fSTom Whitten goto retry; 7429444c26fSTom Whitten 7439444c26fSTom Whitten default: 7449444c26fSTom Whitten fork_sulogin(B_FALSE, "Couldn't fetch %s service: " 7459444c26fSTom Whitten "%s\n", SCF_INSTANCE_EMI, 7469444c26fSTom Whitten scf_strerror(scf_error())); 7479444c26fSTom Whitten goto retry; 7489444c26fSTom Whitten } 7499444c26fSTom Whitten } 7509444c26fSTom Whitten 7519444c26fSTom Whitten if (setlog) { 7529444c26fSTom Whitten (void) libscf_note_method_log(inst, st->st_log_prefix, EMI_LOG); 7539444c26fSTom Whitten log_framework(LOG_DEBUG, 7549444c26fSTom Whitten "Set logfile property for %s\n", SCF_INSTANCE_EMI); 7559444c26fSTom Whitten } 7569444c26fSTom Whitten 7579444c26fSTom Whitten idata.i_fmri = SCF_INSTANCE_EMI; 7589444c26fSTom Whitten idata.i_state = RESTARTER_STATE_NONE; 7599444c26fSTom Whitten idata.i_next_state = RESTARTER_STATE_NONE; 7609444c26fSTom Whitten switch (r = _restarter_commit_states(hndl, &idata, state, 7619444c26fSTom Whitten RESTARTER_STATE_NONE, NULL)) { 7629444c26fSTom Whitten case 0: 7639444c26fSTom Whitten break; 7649444c26fSTom Whitten 7659444c26fSTom Whitten case ECONNABORTED: 7669444c26fSTom Whitten libscf_handle_rebind(hndl); 7679444c26fSTom Whitten goto retry; 7689444c26fSTom Whitten 7699444c26fSTom Whitten case ENOMEM: 7709444c26fSTom Whitten case ENOENT: 7719444c26fSTom Whitten case EPERM: 7729444c26fSTom Whitten case EACCES: 7739444c26fSTom Whitten case EROFS: 7749444c26fSTom Whitten fork_sulogin(B_FALSE, "Could not set state of " 7759444c26fSTom Whitten "%s: %s\n", SCF_INSTANCE_EMI, strerror(r)); 7769444c26fSTom Whitten goto retry; 7779444c26fSTom Whitten 7789444c26fSTom Whitten case EINVAL: 7799444c26fSTom Whitten default: 7809444c26fSTom Whitten bad_error("_restarter_commit_states", r); 7819444c26fSTom Whitten } 7829444c26fSTom Whitten ret = 0; 7839444c26fSTom Whitten 7849444c26fSTom Whitten out: 7859444c26fSTom Whitten scf_instance_destroy(inst); 7869444c26fSTom Whitten scf_handle_destroy(hndl); 7879444c26fSTom Whitten return (ret); 7889444c26fSTom Whitten } 7899444c26fSTom Whitten 7909444c26fSTom Whitten /* 7919444c26fSTom Whitten * It is possible that the early-manifest-import service is disabled. This 7929444c26fSTom Whitten * would not be the normal case for Solaris, but it may happen on dedicated 7939444c26fSTom Whitten * systems. So this function checks the state of the general/enabled 7949444c26fSTom Whitten * property for Early Manifest Import. 7959444c26fSTom Whitten * 7969444c26fSTom Whitten * It is also possible that the early-manifest-import service does not yet 7979444c26fSTom Whitten * have a repository representation when this function runs. This happens 7989444c26fSTom Whitten * if non-Early Manifest Import system is upgraded to an Early Manifest 7999444c26fSTom Whitten * Import based system. Thus, the non-existence of general/enabled is not 8009444c26fSTom Whitten * an error. 8019444c26fSTom Whitten * 8029444c26fSTom Whitten * Returns 1 if Early Manifest Import is disabled and 0 otherwise. 8039444c26fSTom Whitten */ 8049444c26fSTom Whitten static int 8059444c26fSTom Whitten emi_is_disabled() 8069444c26fSTom Whitten { 8079444c26fSTom Whitten int disabled = 0; 8089444c26fSTom Whitten int disconnected = 1; 8099444c26fSTom Whitten int enabled; 8109444c26fSTom Whitten scf_handle_t *hndl = NULL; 8119444c26fSTom Whitten scf_instance_t *inst = NULL; 8129444c26fSTom Whitten uchar_t stored_hash[MHASH_SIZE]; 8139444c26fSTom Whitten char *pname; 8149444c26fSTom Whitten int hashash, r; 8159444c26fSTom Whitten 8169444c26fSTom Whitten while (hndl == NULL) { 8179444c26fSTom Whitten hndl = libscf_handle_create_bound(SCF_VERSION); 8189444c26fSTom Whitten 8199444c26fSTom Whitten if (hndl == NULL) { 8209444c26fSTom Whitten /* 8219444c26fSTom Whitten * In the case that we can't bind to the repository 8229444c26fSTom Whitten * (which should have been started), we need to 8239444c26fSTom Whitten * allow the user into maintenance mode to 8249444c26fSTom Whitten * determine what's failed. 8259444c26fSTom Whitten */ 8269444c26fSTom Whitten fork_sulogin(B_FALSE, "Unable to bind a new repository " 8279444c26fSTom Whitten "handle: %s\n", scf_strerror(scf_error())); 8289444c26fSTom Whitten } 8299444c26fSTom Whitten } 8309444c26fSTom Whitten 8319444c26fSTom Whitten while (disconnected) { 8329444c26fSTom Whitten r = libscf_fmri_get_instance(hndl, SCF_INSTANCE_EMI, &inst); 8339444c26fSTom Whitten if (r != 0) { 8349444c26fSTom Whitten switch (r) { 8359444c26fSTom Whitten case ECONNABORTED: 8369444c26fSTom Whitten libscf_handle_rebind(hndl); 8379444c26fSTom Whitten continue; 8389444c26fSTom Whitten 8399444c26fSTom Whitten case ENOENT: 8409444c26fSTom Whitten /* 8419444c26fSTom Whitten * Early Manifest Import service is not in 8429444c26fSTom Whitten * the repository. Check the manifest file 8439444c26fSTom Whitten * and service's hash in smf/manifest to 8449444c26fSTom Whitten * figure out whether Early Manifest Import 8459444c26fSTom Whitten * service was deleted. If Early Manifest Import 8469444c26fSTom Whitten * service was deleted, treat that as a disable 8479444c26fSTom Whitten * and don't run early import. 8489444c26fSTom Whitten */ 8499444c26fSTom Whitten 8509444c26fSTom Whitten if (access(EMI_MFST, F_OK)) { 8519444c26fSTom Whitten /* 8529444c26fSTom Whitten * Manifest isn't found, so service is 8539444c26fSTom Whitten * properly removed. 8549444c26fSTom Whitten */ 8559444c26fSTom Whitten disabled = 1; 8569444c26fSTom Whitten } else { 8579444c26fSTom Whitten /* 8589444c26fSTom Whitten * If manifest exists and we have the 8599444c26fSTom Whitten * hash, the service was improperly 8609444c26fSTom Whitten * deleted, generate a warning and treat 8619444c26fSTom Whitten * this as a disable. 8629444c26fSTom Whitten */ 8639444c26fSTom Whitten 8649444c26fSTom Whitten if ((pname = mhash_filename_to_propname( 8659444c26fSTom Whitten EMI_MFST, B_TRUE)) == NULL) { 8669444c26fSTom Whitten /* 8679444c26fSTom Whitten * Treat failure to get propname 8689444c26fSTom Whitten * as a disable. 8699444c26fSTom Whitten */ 8709444c26fSTom Whitten disabled = 1; 8719444c26fSTom Whitten uu_warn("Failed to get propname" 8729444c26fSTom Whitten " for %s.\n", 8739444c26fSTom Whitten SCF_INSTANCE_EMI); 8749444c26fSTom Whitten } else { 8759444c26fSTom Whitten hashash = mhash_retrieve_entry( 8769444c26fSTom Whitten hndl, pname, 8779444c26fSTom Whitten stored_hash, 8789444c26fSTom Whitten NULL) == 0; 8799444c26fSTom Whitten uu_free(pname); 8809444c26fSTom Whitten 8819444c26fSTom Whitten if (hashash) { 8829444c26fSTom Whitten disabled = 1; 8839444c26fSTom Whitten uu_warn("%s service is " 8849444c26fSTom Whitten "deleted \n", 8859444c26fSTom Whitten SCF_INSTANCE_EMI); 8869444c26fSTom Whitten } 8879444c26fSTom Whitten } 8889444c26fSTom Whitten 8899444c26fSTom Whitten } 8909444c26fSTom Whitten 8919444c26fSTom Whitten disconnected = 0; 8929444c26fSTom Whitten continue; 8939444c26fSTom Whitten 8949444c26fSTom Whitten default: 8959444c26fSTom Whitten bad_error("libscf_fmri_get_instance", 8969444c26fSTom Whitten scf_error()); 8979444c26fSTom Whitten } 8989444c26fSTom Whitten } 8999444c26fSTom Whitten r = libscf_get_basic_instance_data(hndl, inst, SCF_INSTANCE_EMI, 9009444c26fSTom Whitten &enabled, NULL, NULL); 9019444c26fSTom Whitten if (r == 0) { 9029444c26fSTom Whitten /* 9039444c26fSTom Whitten * enabled can be returned as -1, which indicates 9049444c26fSTom Whitten * that the enabled property was not found. To us 9059444c26fSTom Whitten * that means that the service was not disabled. 9069444c26fSTom Whitten */ 9079444c26fSTom Whitten if (enabled == 0) 9089444c26fSTom Whitten disabled = 1; 9099444c26fSTom Whitten } else { 9109444c26fSTom Whitten switch (r) { 9119444c26fSTom Whitten case ECONNABORTED: 9129444c26fSTom Whitten libscf_handle_rebind(hndl); 9139444c26fSTom Whitten continue; 9149444c26fSTom Whitten 9159444c26fSTom Whitten case ECANCELED: 9169444c26fSTom Whitten case ENOENT: 9179444c26fSTom Whitten break; 9189444c26fSTom Whitten default: 9199444c26fSTom Whitten bad_error("libscf_get_basic_instance_data", r); 9209444c26fSTom Whitten } 9219444c26fSTom Whitten } 9229444c26fSTom Whitten disconnected = 0; 9239444c26fSTom Whitten } 9249444c26fSTom Whitten 9259444c26fSTom Whitten out: 9269444c26fSTom Whitten if (inst != NULL) 9279444c26fSTom Whitten scf_instance_destroy(inst); 9289444c26fSTom Whitten scf_handle_destroy(hndl); 9299444c26fSTom Whitten return (disabled); 9309444c26fSTom Whitten } 9319444c26fSTom Whitten 9329444c26fSTom Whitten void 9339444c26fSTom Whitten fork_emi() 9349444c26fSTom Whitten { 9359444c26fSTom Whitten pid_t pid; 9369444c26fSTom Whitten ctid_t ctid = -1; 9379444c26fSTom Whitten char **envp, **np; 9389444c26fSTom Whitten char *emipath; 9399444c26fSTom Whitten char corepath[PATH_MAX]; 9409444c26fSTom Whitten char *svc_state; 9419444c26fSTom Whitten int setemilog; 9429444c26fSTom Whitten int sz; 9439444c26fSTom Whitten 9449444c26fSTom Whitten if (emi_is_disabled()) { 9459444c26fSTom Whitten log_framework(LOG_NOTICE, "%s is disabled and will " 9469444c26fSTom Whitten "not be run.\n", SCF_INSTANCE_EMI); 9479444c26fSTom Whitten return; 9489444c26fSTom Whitten } 9499444c26fSTom Whitten 9509444c26fSTom Whitten /* 9519444c26fSTom Whitten * Early Manifest Import should run only once, at boot. If svc.startd 9529444c26fSTom Whitten * is some how restarted, Early Manifest Import should not run again. 9539444c26fSTom Whitten * Use the Early Manifest Import service's state to figure out whether 9549444c26fSTom Whitten * Early Manifest Import has successfully completed earlier and bail 9559444c26fSTom Whitten * out if it did. 9569444c26fSTom Whitten */ 9579444c26fSTom Whitten if (svc_state = smf_get_state(SCF_INSTANCE_EMI)) { 9589444c26fSTom Whitten if (strcmp(svc_state, SCF_STATE_STRING_ONLINE) == 0) { 9599444c26fSTom Whitten free(svc_state); 9609444c26fSTom Whitten return; 9619444c26fSTom Whitten } 9629444c26fSTom Whitten free(svc_state); 9639444c26fSTom Whitten } 9649444c26fSTom Whitten 9659444c26fSTom Whitten /* 9669444c26fSTom Whitten * Attempt to set Early Manifest Import service's state and log file. 9679444c26fSTom Whitten * If emi_set_state fails, set log file again in the next call to 9689444c26fSTom Whitten * emi_set_state. 9699444c26fSTom Whitten */ 9709444c26fSTom Whitten setemilog = emi_set_state(RESTARTER_STATE_OFFLINE, B_TRUE); 9719444c26fSTom Whitten 9729444c26fSTom Whitten /* Don't go further if /usr isn't available */ 9739444c26fSTom Whitten if (access(SVCCFG_PATH, F_OK)) { 9749444c26fSTom Whitten log_framework(LOG_NOTICE, "Early Manifest Import is not " 9759444c26fSTom Whitten "supported on systems with a separate /usr filesystem.\n"); 9769444c26fSTom Whitten return; 9779444c26fSTom Whitten } 9789444c26fSTom Whitten 9799444c26fSTom Whitten fork_retry: 9809444c26fSTom Whitten log_framework(LOG_DEBUG, "Starting Early Manifest Import\n"); 9819444c26fSTom Whitten 9829444c26fSTom Whitten /* 9839444c26fSTom Whitten * If we're retrying, we will have an old contract lying around 9849444c26fSTom Whitten * from the failure. Since we're going to be creating a new 9859444c26fSTom Whitten * contract shortly, we abandon the old one now. 9869444c26fSTom Whitten */ 9879444c26fSTom Whitten if (ctid != -1) 9889444c26fSTom Whitten contract_abandon(ctid); 9899444c26fSTom Whitten ctid = -1; 9909444c26fSTom Whitten 9919444c26fSTom Whitten pid = fork_common(SCF_INSTANCE_EMI, SCF_INSTANCE_EMI, 9929444c26fSTom Whitten MAX_EMI_RETRIES, &ctid, 0, 0, 0, 0, EMI_COOKIE); 9939444c26fSTom Whitten 9949444c26fSTom Whitten if (pid != 0) { 9959444c26fSTom Whitten int exitstatus; 9969444c26fSTom Whitten 9979444c26fSTom Whitten if (waitpid(pid, &exitstatus, 0) == -1) { 9989444c26fSTom Whitten fork_sulogin(B_FALSE, "waitpid on %s failed: " 9999444c26fSTom Whitten "%s\n", SCF_INSTANCE_EMI, strerror(errno)); 10009444c26fSTom Whitten } else if (WIFEXITED(exitstatus)) { 10019444c26fSTom Whitten if (WEXITSTATUS(exitstatus)) { 10029444c26fSTom Whitten fork_sulogin(B_FALSE, "%s exited with status " 10039444c26fSTom Whitten "%d \n", SCF_INSTANCE_EMI, 10049444c26fSTom Whitten WEXITSTATUS(exitstatus)); 10059444c26fSTom Whitten goto fork_retry; 10069444c26fSTom Whitten } 10079444c26fSTom Whitten } else if (WIFSIGNALED(exitstatus)) { 10089444c26fSTom Whitten char signame[SIG2STR_MAX]; 10099444c26fSTom Whitten 10109444c26fSTom Whitten if (sig2str(WTERMSIG(exitstatus), signame)) 10119444c26fSTom Whitten (void) snprintf(signame, SIG2STR_MAX, 10129444c26fSTom Whitten "signum %d", WTERMSIG(exitstatus)); 10139444c26fSTom Whitten 10149444c26fSTom Whitten fork_sulogin(B_FALSE, "%s signalled: %s\n", 10159444c26fSTom Whitten SCF_INSTANCE_EMI, signame); 10169444c26fSTom Whitten goto fork_retry; 10179444c26fSTom Whitten } else { 10189444c26fSTom Whitten fork_sulogin(B_FALSE, "%s non-exit condition: 0x%x\n", 10199444c26fSTom Whitten SCF_INSTANCE_EMI, exitstatus); 10209444c26fSTom Whitten goto fork_retry; 10219444c26fSTom Whitten } 10229444c26fSTom Whitten 10239444c26fSTom Whitten log_framework(LOG_DEBUG, "%s completed successfully\n", 10249444c26fSTom Whitten SCF_INSTANCE_EMI); 10259444c26fSTom Whitten 10269444c26fSTom Whitten /* 10279444c26fSTom Whitten * Once Early Manifest Import completed, the Early Manifest 10289444c26fSTom Whitten * Import service must have been imported so set log file and 10299444c26fSTom Whitten * state properties. Since this information is required for 10309444c26fSTom Whitten * late manifest import and common admin operations, failing to 10319444c26fSTom Whitten * set these properties should result in su login so admin can 10329444c26fSTom Whitten * correct the problem. 10339444c26fSTom Whitten */ 10349444c26fSTom Whitten (void) emi_set_state(RESTARTER_STATE_ONLINE, 10359444c26fSTom Whitten setemilog ? B_TRUE : B_FALSE); 10369444c26fSTom Whitten 10379444c26fSTom Whitten return; 10389444c26fSTom Whitten } 10399444c26fSTom Whitten 10409444c26fSTom Whitten /* child */ 10419444c26fSTom Whitten 10429444c26fSTom Whitten /* 10439444c26fSTom Whitten * Set our per-process core file path to leave core files in 10449444c26fSTom Whitten * /etc/svc/volatile directory, named after the PID to aid in debugging. 10459444c26fSTom Whitten */ 10469444c26fSTom Whitten (void) snprintf(corepath, sizeof (corepath), 10479444c26fSTom Whitten "/etc/svc/volatile/core.emi.%%p"); 10489444c26fSTom Whitten (void) core_set_process_path(corepath, strlen(corepath) + 1, getpid()); 10499444c26fSTom Whitten 10509444c26fSTom Whitten /* 10519444c26fSTom Whitten * Similar to running legacy services, we need to manually set 10529444c26fSTom Whitten * log files here and environment variables. 10539444c26fSTom Whitten */ 10549444c26fSTom Whitten setlog(EMI_LOG); 10559444c26fSTom Whitten 10569444c26fSTom Whitten envp = startd_zalloc(sizeof (char *) * 3); 10579444c26fSTom Whitten np = envp; 10589444c26fSTom Whitten 10599444c26fSTom Whitten sz = sizeof ("SMF_FMRI=") + strlen(SCF_INSTANCE_EMI); 10609444c26fSTom Whitten *np = startd_zalloc(sz); 10619444c26fSTom Whitten (void) strlcpy(*np, "SMF_FMRI=", sz); 10629444c26fSTom Whitten (void) strncat(*np, SCF_INSTANCE_EMI, sz); 10639444c26fSTom Whitten np++; 10649444c26fSTom Whitten 10659444c26fSTom Whitten emipath = getenv("PATH"); 10669444c26fSTom Whitten if (emipath == NULL) 10679444c26fSTom Whitten emipath = strdup("/usr/sbin:/usr/bin"); 10689444c26fSTom Whitten 10699444c26fSTom Whitten sz = sizeof ("PATH=") + strlen(emipath); 10709444c26fSTom Whitten *np = startd_zalloc(sz); 10719444c26fSTom Whitten (void) strlcpy(*np, "PATH=", sz); 10729444c26fSTom Whitten (void) strncat(*np, emipath, sz); 10739444c26fSTom Whitten 10749444c26fSTom Whitten log_framework(LOG_DEBUG, "executing Early Manifest Import\n"); 10759444c26fSTom Whitten (void) execle(EMI_PATH, EMI_PATH, NULL, envp); 10769444c26fSTom Whitten 10779444c26fSTom Whitten /* 10789444c26fSTom Whitten * Status code is used above to identify Early Manifest Import 10799444c26fSTom Whitten * exec failure. 10809444c26fSTom Whitten */ 10819444c26fSTom Whitten exit(1); 10829444c26fSTom Whitten } 10839444c26fSTom Whitten 10844d53c7adSDan Price extern char **environ; 10854d53c7adSDan Price 10864d53c7adSDan Price /* 10874d53c7adSDan Price * A local variation on system(3c) which accepts a timeout argument. This 10884d53c7adSDan Price * allows us to better ensure that the system will actually shut down. 10894d53c7adSDan Price * 10904d53c7adSDan Price * gracetime specifies an amount of time in seconds which the routine must wait 10914d53c7adSDan Price * after the command exits, to allow for asynchronous effects (like sent 10924d53c7adSDan Price * signals) to take effect. This can be zero. 10934d53c7adSDan Price */ 10944d53c7adSDan Price void 10954d53c7adSDan Price fork_with_timeout(const char *cmd, uint_t gracetime, uint_t timeout) 10964d53c7adSDan Price { 10974d53c7adSDan Price int err = 0; 10984d53c7adSDan Price pid_t pid; 10994d53c7adSDan Price char *argv[4]; 11004d53c7adSDan Price posix_spawnattr_t attr; 11014d53c7adSDan Price posix_spawn_file_actions_t factions; 11024d53c7adSDan Price 11034d53c7adSDan Price sigset_t mask, savemask; 11044d53c7adSDan Price uint_t msec_timeout; 11054d53c7adSDan Price uint_t msec_spent = 0; 11064d53c7adSDan Price uint_t msec_gracetime; 11074d53c7adSDan Price int status; 11084d53c7adSDan Price 11094d53c7adSDan Price msec_timeout = timeout * 1000; 11104d53c7adSDan Price msec_gracetime = gracetime * 1000; 11114d53c7adSDan Price 11124d53c7adSDan Price /* 11134d53c7adSDan Price * See also system(3c) in libc. This is very similar, except 11144d53c7adSDan Price * that we avoid some unneeded complexity. 11154d53c7adSDan Price */ 11164d53c7adSDan Price err = posix_spawnattr_init(&attr); 11174d53c7adSDan Price if (err == 0) 11184d53c7adSDan Price err = posix_spawnattr_setflags(&attr, 11194d53c7adSDan Price POSIX_SPAWN_SETSIGMASK | POSIX_SPAWN_SETSIGDEF | 11204d53c7adSDan Price POSIX_SPAWN_NOSIGCHLD_NP | POSIX_SPAWN_WAITPID_NP | 11214d53c7adSDan Price POSIX_SPAWN_NOEXECERR_NP); 11224d53c7adSDan Price 11234d53c7adSDan Price /* 11244d53c7adSDan Price * We choose to close fd's above 2, a deviation from system. 11254d53c7adSDan Price */ 11264d53c7adSDan Price if (err == 0) 11274d53c7adSDan Price err = posix_spawn_file_actions_init(&factions); 11284d53c7adSDan Price if (err == 0) 11294d53c7adSDan Price err = posix_spawn_file_actions_addclosefrom_np(&factions, 11304d53c7adSDan Price STDERR_FILENO + 1); 11314d53c7adSDan Price 11324d53c7adSDan Price (void) sigemptyset(&mask); 11334d53c7adSDan Price (void) sigaddset(&mask, SIGCHLD); 11344d53c7adSDan Price (void) thr_sigsetmask(SIG_BLOCK, &mask, &savemask); 11354d53c7adSDan Price 11364d53c7adSDan Price argv[0] = "/bin/sh"; 11374d53c7adSDan Price argv[1] = "-c"; 11384d53c7adSDan Price argv[2] = (char *)cmd; 11394d53c7adSDan Price argv[3] = NULL; 11404d53c7adSDan Price 11414d53c7adSDan Price if (err == 0) 11424d53c7adSDan Price err = posix_spawn(&pid, "/bin/sh", &factions, &attr, 11434d53c7adSDan Price (char *const *)argv, (char *const *)environ); 11444d53c7adSDan Price 11454d53c7adSDan Price (void) posix_spawnattr_destroy(&attr); 11464d53c7adSDan Price (void) posix_spawn_file_actions_destroy(&factions); 11474d53c7adSDan Price 11484d53c7adSDan Price if (err) { 11494d53c7adSDan Price uu_warn("Failed to spawn %s: %s\n", cmd, strerror(err)); 11504d53c7adSDan Price } else { 11514d53c7adSDan Price for (;;) { 11524d53c7adSDan Price int w; 11534d53c7adSDan Price w = waitpid(pid, &status, WNOHANG); 11544d53c7adSDan Price if (w == -1 && errno != EINTR) 11554d53c7adSDan Price break; 11564d53c7adSDan Price if (w > 0) { 11574d53c7adSDan Price /* 11584d53c7adSDan Price * Command succeeded, so give it gracetime 11594d53c7adSDan Price * seconds for it to have an effect. 11604d53c7adSDan Price */ 11614d53c7adSDan Price if (status == 0 && msec_gracetime != 0) 11624d53c7adSDan Price (void) poll(NULL, 0, msec_gracetime); 11634d53c7adSDan Price break; 11644d53c7adSDan Price } 11654d53c7adSDan Price 11664d53c7adSDan Price (void) poll(NULL, 0, 100); 11674d53c7adSDan Price msec_spent += 100; 11684d53c7adSDan Price /* 11694d53c7adSDan Price * If we timed out, kill off the process, then try to 11704d53c7adSDan Price * wait for it-- it's possible that we could accumulate 11714d53c7adSDan Price * a zombie here since we don't allow waitpid to hang, 11724d53c7adSDan Price * but it's better to let that happen and continue to 11734d53c7adSDan Price * make progress. 11744d53c7adSDan Price */ 11754d53c7adSDan Price if (msec_spent >= msec_timeout) { 11764d53c7adSDan Price uu_warn("'%s' timed out after %d " 11774d53c7adSDan Price "seconds. Killing.\n", cmd, 11784d53c7adSDan Price timeout); 11794d53c7adSDan Price (void) kill(pid, SIGTERM); 11804d53c7adSDan Price (void) poll(NULL, 0, 100); 11814d53c7adSDan Price (void) kill(pid, SIGKILL); 11824d53c7adSDan Price (void) poll(NULL, 0, 100); 11834d53c7adSDan Price (void) waitpid(pid, &status, WNOHANG); 11844d53c7adSDan Price break; 11854d53c7adSDan Price } 11864d53c7adSDan Price } 11874d53c7adSDan Price } 11884d53c7adSDan Price (void) thr_sigsetmask(SIG_BLOCK, &savemask, NULL); 11894d53c7adSDan Price } 1190