1*0Sstevel@tonic-gate /* 2*0Sstevel@tonic-gate * CDDL HEADER START 3*0Sstevel@tonic-gate * 4*0Sstevel@tonic-gate * The contents of this file are subject to the terms of the 5*0Sstevel@tonic-gate * Common Development and Distribution License, Version 1.0 only 6*0Sstevel@tonic-gate * (the "License"). You may not use this file except in compliance 7*0Sstevel@tonic-gate * with the License. 8*0Sstevel@tonic-gate * 9*0Sstevel@tonic-gate * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 10*0Sstevel@tonic-gate * or http://www.opensolaris.org/os/licensing. 11*0Sstevel@tonic-gate * See the License for the specific language governing permissions 12*0Sstevel@tonic-gate * and limitations under the License. 13*0Sstevel@tonic-gate * 14*0Sstevel@tonic-gate * When distributing Covered Code, include this CDDL HEADER in each 15*0Sstevel@tonic-gate * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 16*0Sstevel@tonic-gate * If applicable, add the following below this CDDL HEADER, with the 17*0Sstevel@tonic-gate * fields enclosed by brackets "[]" replaced with your own identifying 18*0Sstevel@tonic-gate * information: Portions Copyright [yyyy] [name of copyright owner] 19*0Sstevel@tonic-gate * 20*0Sstevel@tonic-gate * CDDL HEADER END 21*0Sstevel@tonic-gate */ 22*0Sstevel@tonic-gate /* 23*0Sstevel@tonic-gate * Copyright 2005 Sun Microsystems, Inc. All rights reserved. 24*0Sstevel@tonic-gate * Use is subject to license terms. 25*0Sstevel@tonic-gate */ 26*0Sstevel@tonic-gate 27*0Sstevel@tonic-gate #pragma ident "%Z%%M% %I% %E% SMI" 28*0Sstevel@tonic-gate 29*0Sstevel@tonic-gate /* 30*0Sstevel@tonic-gate * fork.c - safe forking for svc.startd 31*0Sstevel@tonic-gate * 32*0Sstevel@tonic-gate * fork_configd() and fork_sulogin() are related, special cases that handle the 33*0Sstevel@tonic-gate * spawning of specific client processes for svc.startd. 34*0Sstevel@tonic-gate */ 35*0Sstevel@tonic-gate 36*0Sstevel@tonic-gate #include <sys/contract/process.h> 37*0Sstevel@tonic-gate #include <sys/corectl.h> 38*0Sstevel@tonic-gate #include <sys/ctfs.h> 39*0Sstevel@tonic-gate #include <sys/stat.h> 40*0Sstevel@tonic-gate #include <sys/types.h> 41*0Sstevel@tonic-gate #include <sys/uio.h> 42*0Sstevel@tonic-gate #include <sys/wait.h> 43*0Sstevel@tonic-gate #include <assert.h> 44*0Sstevel@tonic-gate #include <errno.h> 45*0Sstevel@tonic-gate #include <fcntl.h> 46*0Sstevel@tonic-gate #include <libcontract.h> 47*0Sstevel@tonic-gate #include <libcontract_priv.h> 48*0Sstevel@tonic-gate #include <limits.h> 49*0Sstevel@tonic-gate #include <port.h> 50*0Sstevel@tonic-gate #include <signal.h> 51*0Sstevel@tonic-gate #include <stdarg.h> 52*0Sstevel@tonic-gate #include <stdio.h> 53*0Sstevel@tonic-gate #include <stdlib.h> 54*0Sstevel@tonic-gate #include <string.h> 55*0Sstevel@tonic-gate #include <unistd.h> 56*0Sstevel@tonic-gate 57*0Sstevel@tonic-gate #include "configd_exit.h" 58*0Sstevel@tonic-gate #include "protocol.h" 59*0Sstevel@tonic-gate #include "startd.h" 60*0Sstevel@tonic-gate 61*0Sstevel@tonic-gate pid_t 62*0Sstevel@tonic-gate startd_fork1(int *forkerr) 63*0Sstevel@tonic-gate { 64*0Sstevel@tonic-gate pid_t p; 65*0Sstevel@tonic-gate 66*0Sstevel@tonic-gate /* 67*0Sstevel@tonic-gate * prefork stack 68*0Sstevel@tonic-gate */ 69*0Sstevel@tonic-gate wait_prefork(); 70*0Sstevel@tonic-gate 71*0Sstevel@tonic-gate p = fork1(); 72*0Sstevel@tonic-gate 73*0Sstevel@tonic-gate if (p == -1 && forkerr != NULL) 74*0Sstevel@tonic-gate *forkerr = errno; 75*0Sstevel@tonic-gate 76*0Sstevel@tonic-gate /* 77*0Sstevel@tonic-gate * postfork stack 78*0Sstevel@tonic-gate */ 79*0Sstevel@tonic-gate wait_postfork(p); 80*0Sstevel@tonic-gate 81*0Sstevel@tonic-gate return (p); 82*0Sstevel@tonic-gate } 83*0Sstevel@tonic-gate 84*0Sstevel@tonic-gate /* 85*0Sstevel@tonic-gate * void fork_mount(char *, char *) 86*0Sstevel@tonic-gate * Run mount(1M) with the given options and mount point. (mount(1M) has much 87*0Sstevel@tonic-gate * hidden knowledge; it's much less correct to reimplement that logic here to 88*0Sstevel@tonic-gate * save a fork(2)/exec(2) invocation.) 89*0Sstevel@tonic-gate */ 90*0Sstevel@tonic-gate int 91*0Sstevel@tonic-gate fork_mount(char *path, char *opts) 92*0Sstevel@tonic-gate { 93*0Sstevel@tonic-gate pid_t pid; 94*0Sstevel@tonic-gate uint_t tries = 0; 95*0Sstevel@tonic-gate int status; 96*0Sstevel@tonic-gate 97*0Sstevel@tonic-gate for (pid = fork1(); pid == -1; pid = fork1()) { 98*0Sstevel@tonic-gate if (++tries > MAX_MOUNT_RETRIES) 99*0Sstevel@tonic-gate return (-1); 100*0Sstevel@tonic-gate 101*0Sstevel@tonic-gate (void) sleep(tries); 102*0Sstevel@tonic-gate } 103*0Sstevel@tonic-gate 104*0Sstevel@tonic-gate if (pid != 0) { 105*0Sstevel@tonic-gate (void) waitpid(pid, &status, 0); 106*0Sstevel@tonic-gate 107*0Sstevel@tonic-gate /* 108*0Sstevel@tonic-gate * If our mount(1M) invocation exited by peculiar means, or with 109*0Sstevel@tonic-gate * a non-zero status, our mount likelihood is low. 110*0Sstevel@tonic-gate */ 111*0Sstevel@tonic-gate if (!WIFEXITED(status) || 112*0Sstevel@tonic-gate WEXITSTATUS(status) != 0) 113*0Sstevel@tonic-gate return (-1); 114*0Sstevel@tonic-gate 115*0Sstevel@tonic-gate return (0); 116*0Sstevel@tonic-gate } 117*0Sstevel@tonic-gate 118*0Sstevel@tonic-gate (void) execl("/sbin/mount", "mount", "-o", opts, path, NULL); 119*0Sstevel@tonic-gate 120*0Sstevel@tonic-gate return (-1); 121*0Sstevel@tonic-gate } 122*0Sstevel@tonic-gate 123*0Sstevel@tonic-gate /* 124*0Sstevel@tonic-gate * pid_t fork_common(...) 125*0Sstevel@tonic-gate * Common routine used by fork_sulogin and fork_configd to fork a 126*0Sstevel@tonic-gate * process in a contract with the provided terms. Invokes 127*0Sstevel@tonic-gate * fork_sulogin (with its no-fork argument set) on errors. 128*0Sstevel@tonic-gate */ 129*0Sstevel@tonic-gate static pid_t 130*0Sstevel@tonic-gate fork_common(const char *name, int retries, ctid_t *ctidp, 131*0Sstevel@tonic-gate uint_t inf, uint_t crit, uint_t fatal, uint_t param, uint64_t cookie) 132*0Sstevel@tonic-gate { 133*0Sstevel@tonic-gate uint_t tries = 0; 134*0Sstevel@tonic-gate int ctfd, err; 135*0Sstevel@tonic-gate pid_t pid; 136*0Sstevel@tonic-gate 137*0Sstevel@tonic-gate /* 138*0Sstevel@tonic-gate * Establish process contract terms. 139*0Sstevel@tonic-gate */ 140*0Sstevel@tonic-gate if ((ctfd = open64(CTFS_ROOT "/process/template", O_RDWR)) == -1) { 141*0Sstevel@tonic-gate fork_sulogin(B_TRUE, "Could not open process contract template " 142*0Sstevel@tonic-gate "for %s: %s\n", name, strerror(errno)); 143*0Sstevel@tonic-gate /* NOTREACHED */ 144*0Sstevel@tonic-gate } 145*0Sstevel@tonic-gate 146*0Sstevel@tonic-gate err = ct_tmpl_set_critical(ctfd, crit); 147*0Sstevel@tonic-gate err |= ct_pr_tmpl_set_fatal(ctfd, fatal); 148*0Sstevel@tonic-gate err |= ct_tmpl_set_informative(ctfd, inf); 149*0Sstevel@tonic-gate err |= ct_pr_tmpl_set_param(ctfd, param); 150*0Sstevel@tonic-gate err |= ct_tmpl_set_cookie(ctfd, cookie); 151*0Sstevel@tonic-gate if (err) { 152*0Sstevel@tonic-gate (void) close(ctfd); 153*0Sstevel@tonic-gate fork_sulogin(B_TRUE, "Could not set %s process contract " 154*0Sstevel@tonic-gate "terms\n", name); 155*0Sstevel@tonic-gate /* NOTREACHED */ 156*0Sstevel@tonic-gate } 157*0Sstevel@tonic-gate 158*0Sstevel@tonic-gate if (err = ct_tmpl_activate(ctfd)) { 159*0Sstevel@tonic-gate (void) close(ctfd); 160*0Sstevel@tonic-gate fork_sulogin(B_TRUE, "Could not activate %s process contract " 161*0Sstevel@tonic-gate "template: %s\n", name, strerror(err)); 162*0Sstevel@tonic-gate /* NOTREACHED */ 163*0Sstevel@tonic-gate } 164*0Sstevel@tonic-gate 165*0Sstevel@tonic-gate /* 166*0Sstevel@tonic-gate * Attempt to fork "retries" times. 167*0Sstevel@tonic-gate */ 168*0Sstevel@tonic-gate for (pid = fork1(); pid == -1; pid = fork1()) { 169*0Sstevel@tonic-gate if (++tries > retries) { 170*0Sstevel@tonic-gate /* 171*0Sstevel@tonic-gate * When we exit the sulogin session, init(1M) 172*0Sstevel@tonic-gate * will restart svc.startd(1M). 173*0Sstevel@tonic-gate */ 174*0Sstevel@tonic-gate err = errno; 175*0Sstevel@tonic-gate (void) ct_tmpl_clear(ctfd); 176*0Sstevel@tonic-gate (void) close(ctfd); 177*0Sstevel@tonic-gate fork_sulogin(B_TRUE, "Could not fork to start %s: %s\n", 178*0Sstevel@tonic-gate name, strerror(err)); 179*0Sstevel@tonic-gate /* NOTREACHED */ 180*0Sstevel@tonic-gate } 181*0Sstevel@tonic-gate (void) sleep(tries); 182*0Sstevel@tonic-gate } 183*0Sstevel@tonic-gate 184*0Sstevel@tonic-gate /* 185*0Sstevel@tonic-gate * Clean up, return pid and ctid. 186*0Sstevel@tonic-gate */ 187*0Sstevel@tonic-gate if (pid != 0 && (errno = contract_latest(ctidp)) != 0) 188*0Sstevel@tonic-gate uu_die("Could not get new contract id for %s\n", name); 189*0Sstevel@tonic-gate (void) ct_tmpl_clear(ctfd); 190*0Sstevel@tonic-gate (void) close(ctfd); 191*0Sstevel@tonic-gate 192*0Sstevel@tonic-gate return (pid); 193*0Sstevel@tonic-gate } 194*0Sstevel@tonic-gate 195*0Sstevel@tonic-gate /* 196*0Sstevel@tonic-gate * void fork_sulogin(boolean_t, const char *, ...) 197*0Sstevel@tonic-gate * When we are invoked with the -s flag from boot (or run into an unfixable 198*0Sstevel@tonic-gate * situation), we run a private copy of sulogin. When the sulogin session 199*0Sstevel@tonic-gate * is ended, we continue. This is the last fallback action for system 200*0Sstevel@tonic-gate * maintenance. 201*0Sstevel@tonic-gate * 202*0Sstevel@tonic-gate * If immediate is true, fork_sulogin() executes sulogin(1M) directly, without 203*0Sstevel@tonic-gate * forking. 204*0Sstevel@tonic-gate * 205*0Sstevel@tonic-gate * Because fork_sulogin() is needed potentially before we daemonize, we leave 206*0Sstevel@tonic-gate * it outside the wait_register() framework. 207*0Sstevel@tonic-gate */ 208*0Sstevel@tonic-gate /*PRINTFLIKE2*/ 209*0Sstevel@tonic-gate void 210*0Sstevel@tonic-gate fork_sulogin(boolean_t immediate, const char *format, ...) 211*0Sstevel@tonic-gate { 212*0Sstevel@tonic-gate va_list args; 213*0Sstevel@tonic-gate int i, fd_console; 214*0Sstevel@tonic-gate 215*0Sstevel@tonic-gate (void) printf("Requesting System Maintenance Mode\n"); 216*0Sstevel@tonic-gate 217*0Sstevel@tonic-gate if (!booting_to_single_user) 218*0Sstevel@tonic-gate (void) printf("(See /lib/svc/share/README for more " 219*0Sstevel@tonic-gate "information.)\n"); 220*0Sstevel@tonic-gate 221*0Sstevel@tonic-gate va_start(args, format); 222*0Sstevel@tonic-gate (void) vprintf(format, args); 223*0Sstevel@tonic-gate va_end(args); 224*0Sstevel@tonic-gate 225*0Sstevel@tonic-gate if (!immediate) { 226*0Sstevel@tonic-gate ctid_t ctid; 227*0Sstevel@tonic-gate pid_t pid; 228*0Sstevel@tonic-gate 229*0Sstevel@tonic-gate pid = fork_common("sulogin", MAX_SULOGIN_RETRIES, &ctid, 230*0Sstevel@tonic-gate CT_PR_EV_HWERR, 0, CT_PR_EV_HWERR, CT_PR_PGRPONLY, 231*0Sstevel@tonic-gate SULOGIN_COOKIE); 232*0Sstevel@tonic-gate 233*0Sstevel@tonic-gate if (pid != 0) { 234*0Sstevel@tonic-gate (void) waitpid(pid, NULL, 0); 235*0Sstevel@tonic-gate contract_abandon(ctid); 236*0Sstevel@tonic-gate return; 237*0Sstevel@tonic-gate } 238*0Sstevel@tonic-gate /* close all inherited fds */ 239*0Sstevel@tonic-gate closefrom(0); 240*0Sstevel@tonic-gate } else { 241*0Sstevel@tonic-gate (void) printf("Directly executing sulogin.\n"); 242*0Sstevel@tonic-gate /* 243*0Sstevel@tonic-gate * Can't call closefrom() in this MT section 244*0Sstevel@tonic-gate * so safely close a minimum set of fds. 245*0Sstevel@tonic-gate */ 246*0Sstevel@tonic-gate for (i = 0; i < 3; i++) 247*0Sstevel@tonic-gate (void) close(i); 248*0Sstevel@tonic-gate } 249*0Sstevel@tonic-gate 250*0Sstevel@tonic-gate (void) setpgrp(); 251*0Sstevel@tonic-gate 252*0Sstevel@tonic-gate /* open the console for sulogin */ 253*0Sstevel@tonic-gate if ((fd_console = open("/dev/console", O_RDWR)) >= 0) { 254*0Sstevel@tonic-gate if (fd_console != STDIN_FILENO) 255*0Sstevel@tonic-gate while (dup2(fd_console, STDIN_FILENO) < 0 && 256*0Sstevel@tonic-gate errno == EINTR) 257*0Sstevel@tonic-gate ; 258*0Sstevel@tonic-gate if (fd_console != STDOUT_FILENO) 259*0Sstevel@tonic-gate while (dup2(fd_console, STDOUT_FILENO) < 0 && 260*0Sstevel@tonic-gate errno == EINTR) 261*0Sstevel@tonic-gate ; 262*0Sstevel@tonic-gate if (fd_console != STDERR_FILENO) 263*0Sstevel@tonic-gate while (dup2(fd_console, STDERR_FILENO) < 0 && 264*0Sstevel@tonic-gate errno == EINTR) 265*0Sstevel@tonic-gate ; 266*0Sstevel@tonic-gate if (fd_console > 2) 267*0Sstevel@tonic-gate (void) close(fd_console); 268*0Sstevel@tonic-gate } 269*0Sstevel@tonic-gate 270*0Sstevel@tonic-gate (void) execl("/sbin/sulogin", "sulogin", NULL); 271*0Sstevel@tonic-gate 272*0Sstevel@tonic-gate uu_warn("Could not exec() sulogin"); 273*0Sstevel@tonic-gate 274*0Sstevel@tonic-gate exit(1); 275*0Sstevel@tonic-gate } 276*0Sstevel@tonic-gate 277*0Sstevel@tonic-gate #define CONFIGD_PATH "/lib/svc/bin/svc.configd" 278*0Sstevel@tonic-gate 279*0Sstevel@tonic-gate /* 280*0Sstevel@tonic-gate * void fork_configd(int status) 281*0Sstevel@tonic-gate * We are interested in exit events (since the parent's exiting means configd 282*0Sstevel@tonic-gate * is ready to run and since the child's exiting indicates an error case) and 283*0Sstevel@tonic-gate * in empty events. This means we have a unique template for initiating 284*0Sstevel@tonic-gate * configd. 285*0Sstevel@tonic-gate */ 286*0Sstevel@tonic-gate /*ARGSUSED*/ 287*0Sstevel@tonic-gate void 288*0Sstevel@tonic-gate fork_configd(int exitstatus) 289*0Sstevel@tonic-gate { 290*0Sstevel@tonic-gate pid_t pid; 291*0Sstevel@tonic-gate ctid_t ctid = -1; 292*0Sstevel@tonic-gate int err; 293*0Sstevel@tonic-gate char path[PATH_MAX]; 294*0Sstevel@tonic-gate 295*0Sstevel@tonic-gate retry: 296*0Sstevel@tonic-gate log_framework(LOG_DEBUG, "fork_configd trying to start svc.configd\n"); 297*0Sstevel@tonic-gate 298*0Sstevel@tonic-gate /* 299*0Sstevel@tonic-gate * If we're retrying, we will have an old contract lying around 300*0Sstevel@tonic-gate * from the failure. Since we're going to be creating a new 301*0Sstevel@tonic-gate * contract shortly, we abandon the old one now. 302*0Sstevel@tonic-gate */ 303*0Sstevel@tonic-gate if (ctid != -1) 304*0Sstevel@tonic-gate contract_abandon(ctid); 305*0Sstevel@tonic-gate ctid = -1; 306*0Sstevel@tonic-gate 307*0Sstevel@tonic-gate pid = fork_common("svc.configd", MAX_CONFIGD_RETRIES, &ctid, 308*0Sstevel@tonic-gate 0, CT_PR_EV_EXIT, 0, CT_PR_INHERIT | CT_PR_REGENT, CONFIGD_COOKIE); 309*0Sstevel@tonic-gate 310*0Sstevel@tonic-gate if (pid != 0) { 311*0Sstevel@tonic-gate int exitstatus; 312*0Sstevel@tonic-gate 313*0Sstevel@tonic-gate st->st_configd_pid = pid; 314*0Sstevel@tonic-gate 315*0Sstevel@tonic-gate if (waitpid(pid, &exitstatus, 0) == -1) { 316*0Sstevel@tonic-gate fork_sulogin(B_FALSE, "waitpid on svc.configd " 317*0Sstevel@tonic-gate "failed: %s\n", strerror(errno)); 318*0Sstevel@tonic-gate } else if (WIFEXITED(exitstatus)) { 319*0Sstevel@tonic-gate char *errstr; 320*0Sstevel@tonic-gate 321*0Sstevel@tonic-gate /* 322*0Sstevel@tonic-gate * Examine exitstatus. This will eventually get more 323*0Sstevel@tonic-gate * complicated, as we will want to teach startd how to 324*0Sstevel@tonic-gate * invoke configd with alternate repositories, etc. 325*0Sstevel@tonic-gate * 326*0Sstevel@tonic-gate * Note that exec(2) failure results in an exit status 327*0Sstevel@tonic-gate * of 1, resulting in the default clause below. 328*0Sstevel@tonic-gate */ 329*0Sstevel@tonic-gate 330*0Sstevel@tonic-gate /* 331*0Sstevel@tonic-gate * Assign readable strings to cases we don't handle, or 332*0Sstevel@tonic-gate * have error outcomes that cannot be eliminated. 333*0Sstevel@tonic-gate */ 334*0Sstevel@tonic-gate switch (WEXITSTATUS(exitstatus)) { 335*0Sstevel@tonic-gate case CONFIGD_EXIT_BAD_ARGS: 336*0Sstevel@tonic-gate errstr = "bad arguments"; 337*0Sstevel@tonic-gate break; 338*0Sstevel@tonic-gate 339*0Sstevel@tonic-gate case CONFIGD_EXIT_DATABASE_BAD: 340*0Sstevel@tonic-gate errstr = "database corrupt"; 341*0Sstevel@tonic-gate break; 342*0Sstevel@tonic-gate 343*0Sstevel@tonic-gate case CONFIGD_EXIT_DATABASE_LOCKED: 344*0Sstevel@tonic-gate errstr = "database locked"; 345*0Sstevel@tonic-gate break; 346*0Sstevel@tonic-gate case CONFIGD_EXIT_INIT_FAILED: 347*0Sstevel@tonic-gate errstr = "initialization failure"; 348*0Sstevel@tonic-gate break; 349*0Sstevel@tonic-gate case CONFIGD_EXIT_DOOR_INIT_FAILED: 350*0Sstevel@tonic-gate errstr = "door initialization failure"; 351*0Sstevel@tonic-gate break; 352*0Sstevel@tonic-gate case CONFIGD_EXIT_DATABASE_INIT_FAILED: 353*0Sstevel@tonic-gate errstr = "database initialization failure"; 354*0Sstevel@tonic-gate break; 355*0Sstevel@tonic-gate case CONFIGD_EXIT_NO_THREADS: 356*0Sstevel@tonic-gate errstr = "no threads available"; 357*0Sstevel@tonic-gate break; 358*0Sstevel@tonic-gate case CONFIGD_EXIT_LOST_MAIN_DOOR: 359*0Sstevel@tonic-gate errstr = "lost door server attachment"; 360*0Sstevel@tonic-gate break; 361*0Sstevel@tonic-gate case 1: 362*0Sstevel@tonic-gate errstr = "execution failure"; 363*0Sstevel@tonic-gate break; 364*0Sstevel@tonic-gate default: 365*0Sstevel@tonic-gate errstr = "unknown error"; 366*0Sstevel@tonic-gate break; 367*0Sstevel@tonic-gate } 368*0Sstevel@tonic-gate 369*0Sstevel@tonic-gate /* 370*0Sstevel@tonic-gate * Remedial actions for various configd failures. 371*0Sstevel@tonic-gate */ 372*0Sstevel@tonic-gate switch (WEXITSTATUS(exitstatus)) { 373*0Sstevel@tonic-gate case CONFIGD_EXIT_OKAY: 374*0Sstevel@tonic-gate break; 375*0Sstevel@tonic-gate 376*0Sstevel@tonic-gate case CONFIGD_EXIT_DATABASE_LOCKED: 377*0Sstevel@tonic-gate /* attempt remount of / read-write */ 378*0Sstevel@tonic-gate if (fs_is_read_only("/", NULL) == 1) { 379*0Sstevel@tonic-gate if (fs_remount("/") == -1) 380*0Sstevel@tonic-gate fork_sulogin(B_FALSE, 381*0Sstevel@tonic-gate "remount of root " 382*0Sstevel@tonic-gate "filesystem failed\n"); 383*0Sstevel@tonic-gate 384*0Sstevel@tonic-gate goto retry; 385*0Sstevel@tonic-gate } 386*0Sstevel@tonic-gate break; 387*0Sstevel@tonic-gate 388*0Sstevel@tonic-gate default: 389*0Sstevel@tonic-gate fork_sulogin(B_FALSE, "svc.configd exited " 390*0Sstevel@tonic-gate "with status %d (%s)\n", 391*0Sstevel@tonic-gate WEXITSTATUS(exitstatus), errstr); 392*0Sstevel@tonic-gate goto retry; 393*0Sstevel@tonic-gate } 394*0Sstevel@tonic-gate } else if (WIFSIGNALED(exitstatus)) { 395*0Sstevel@tonic-gate char signame[SIG2STR_MAX]; 396*0Sstevel@tonic-gate 397*0Sstevel@tonic-gate if (sig2str(WTERMSIG(exitstatus), signame)) 398*0Sstevel@tonic-gate (void) snprintf(signame, SIG2STR_MAX, 399*0Sstevel@tonic-gate "signum %d", WTERMSIG(exitstatus)); 400*0Sstevel@tonic-gate 401*0Sstevel@tonic-gate fork_sulogin(B_FALSE, "svc.configd signalled:" 402*0Sstevel@tonic-gate " %s\n", signame); 403*0Sstevel@tonic-gate 404*0Sstevel@tonic-gate goto retry; 405*0Sstevel@tonic-gate } else { 406*0Sstevel@tonic-gate fork_sulogin(B_FALSE, "svc.configd non-exit " 407*0Sstevel@tonic-gate "condition: 0x%x\n", exitstatus); 408*0Sstevel@tonic-gate 409*0Sstevel@tonic-gate goto retry; 410*0Sstevel@tonic-gate } 411*0Sstevel@tonic-gate 412*0Sstevel@tonic-gate /* 413*0Sstevel@tonic-gate * Announce that we have a valid svc.configd status. 414*0Sstevel@tonic-gate */ 415*0Sstevel@tonic-gate MUTEX_LOCK(&st->st_configd_live_lock); 416*0Sstevel@tonic-gate st->st_configd_lives = 1; 417*0Sstevel@tonic-gate err = pthread_cond_broadcast(&st->st_configd_live_cv); 418*0Sstevel@tonic-gate assert(err == 0); 419*0Sstevel@tonic-gate MUTEX_UNLOCK(&st->st_configd_live_lock); 420*0Sstevel@tonic-gate 421*0Sstevel@tonic-gate log_framework(LOG_DEBUG, "fork_configd broadcasts configd is " 422*0Sstevel@tonic-gate "live\n"); 423*0Sstevel@tonic-gate return; 424*0Sstevel@tonic-gate } 425*0Sstevel@tonic-gate 426*0Sstevel@tonic-gate /* 427*0Sstevel@tonic-gate * Set our per-process core file path to leave core files in 428*0Sstevel@tonic-gate * /etc/svc/volatile directory, named after the PID to aid in debugging. 429*0Sstevel@tonic-gate */ 430*0Sstevel@tonic-gate (void) snprintf(path, sizeof (path), 431*0Sstevel@tonic-gate "/etc/svc/volatile/core.configd.%%p"); 432*0Sstevel@tonic-gate 433*0Sstevel@tonic-gate (void) core_set_process_path(path, strlen(path) + 1, getpid()); 434*0Sstevel@tonic-gate 435*0Sstevel@tonic-gate log_framework(LOG_DEBUG, "executing svc.configd\n"); 436*0Sstevel@tonic-gate 437*0Sstevel@tonic-gate (void) execl(CONFIGD_PATH, CONFIGD_PATH, NULL); 438*0Sstevel@tonic-gate 439*0Sstevel@tonic-gate /* 440*0Sstevel@tonic-gate * Status code is used above to identify configd exec failure. 441*0Sstevel@tonic-gate */ 442*0Sstevel@tonic-gate exit(1); 443*0Sstevel@tonic-gate } 444*0Sstevel@tonic-gate 445*0Sstevel@tonic-gate void * 446*0Sstevel@tonic-gate fork_configd_thread(void *vctid) 447*0Sstevel@tonic-gate { 448*0Sstevel@tonic-gate int fd, err; 449*0Sstevel@tonic-gate ctid_t configd_ctid = (ctid_t)vctid; 450*0Sstevel@tonic-gate 451*0Sstevel@tonic-gate if (configd_ctid == -1) { 452*0Sstevel@tonic-gate log_framework(LOG_DEBUG, 453*0Sstevel@tonic-gate "fork_configd_thread starting svc.configd\n"); 454*0Sstevel@tonic-gate fork_configd(0); 455*0Sstevel@tonic-gate } else { 456*0Sstevel@tonic-gate /* 457*0Sstevel@tonic-gate * configd_ctid is known: we broadcast and continue. 458*0Sstevel@tonic-gate * test contract for appropriate state by verifying that 459*0Sstevel@tonic-gate * there is one or more processes within it? 460*0Sstevel@tonic-gate */ 461*0Sstevel@tonic-gate log_framework(LOG_DEBUG, 462*0Sstevel@tonic-gate "fork_configd_thread accepting svc.configd with CTID %ld\n", 463*0Sstevel@tonic-gate configd_ctid); 464*0Sstevel@tonic-gate MUTEX_LOCK(&st->st_configd_live_lock); 465*0Sstevel@tonic-gate st->st_configd_lives = 1; 466*0Sstevel@tonic-gate (void) pthread_cond_broadcast(&st->st_configd_live_cv); 467*0Sstevel@tonic-gate MUTEX_UNLOCK(&st->st_configd_live_lock); 468*0Sstevel@tonic-gate } 469*0Sstevel@tonic-gate 470*0Sstevel@tonic-gate fd = open64(CTFS_ROOT "/process/pbundle", O_RDONLY); 471*0Sstevel@tonic-gate if (fd == -1) 472*0Sstevel@tonic-gate uu_die("process bundle open failed"); 473*0Sstevel@tonic-gate 474*0Sstevel@tonic-gate /* 475*0Sstevel@tonic-gate * Make sure we get all events (including those generated by configd 476*0Sstevel@tonic-gate * before this thread was started). 477*0Sstevel@tonic-gate */ 478*0Sstevel@tonic-gate err = ct_event_reset(fd); 479*0Sstevel@tonic-gate assert(err == 0); 480*0Sstevel@tonic-gate 481*0Sstevel@tonic-gate for (;;) { 482*0Sstevel@tonic-gate int efd, sfd; 483*0Sstevel@tonic-gate ct_evthdl_t ev; 484*0Sstevel@tonic-gate uint32_t type; 485*0Sstevel@tonic-gate ctevid_t evid; 486*0Sstevel@tonic-gate ct_stathdl_t status; 487*0Sstevel@tonic-gate ctid_t ctid; 488*0Sstevel@tonic-gate uint64_t cookie; 489*0Sstevel@tonic-gate pid_t pid; 490*0Sstevel@tonic-gate 491*0Sstevel@tonic-gate if (err = ct_event_read_critical(fd, &ev)) { 492*0Sstevel@tonic-gate assert(err != EINVAL && err != EAGAIN); 493*0Sstevel@tonic-gate log_error(LOG_WARNING, 494*0Sstevel@tonic-gate "Error reading next contract event: %s", 495*0Sstevel@tonic-gate strerror(err)); 496*0Sstevel@tonic-gate continue; 497*0Sstevel@tonic-gate } 498*0Sstevel@tonic-gate 499*0Sstevel@tonic-gate evid = ct_event_get_evid(ev); 500*0Sstevel@tonic-gate ctid = ct_event_get_ctid(ev); 501*0Sstevel@tonic-gate type = ct_event_get_type(ev); 502*0Sstevel@tonic-gate 503*0Sstevel@tonic-gate /* Fetch cookie. */ 504*0Sstevel@tonic-gate sfd = contract_open(ctid, "process", "status", O_RDONLY); 505*0Sstevel@tonic-gate if (sfd < 0) { 506*0Sstevel@tonic-gate ct_event_free(ev); 507*0Sstevel@tonic-gate continue; 508*0Sstevel@tonic-gate } 509*0Sstevel@tonic-gate 510*0Sstevel@tonic-gate if (err = ct_status_read(sfd, CTD_COMMON, &status)) { 511*0Sstevel@tonic-gate log_framework(LOG_WARNING, "Could not get status for " 512*0Sstevel@tonic-gate "contract %ld: %s\n", ctid, strerror(err)); 513*0Sstevel@tonic-gate 514*0Sstevel@tonic-gate ct_event_free(ev); 515*0Sstevel@tonic-gate startd_close(sfd); 516*0Sstevel@tonic-gate continue; 517*0Sstevel@tonic-gate } 518*0Sstevel@tonic-gate 519*0Sstevel@tonic-gate cookie = ct_status_get_cookie(status); 520*0Sstevel@tonic-gate 521*0Sstevel@tonic-gate ct_status_free(status); 522*0Sstevel@tonic-gate 523*0Sstevel@tonic-gate startd_close(sfd); 524*0Sstevel@tonic-gate 525*0Sstevel@tonic-gate /* 526*0Sstevel@tonic-gate * Don't process events from contracts we aren't interested in. 527*0Sstevel@tonic-gate */ 528*0Sstevel@tonic-gate if (cookie != CONFIGD_COOKIE) { 529*0Sstevel@tonic-gate ct_event_free(ev); 530*0Sstevel@tonic-gate continue; 531*0Sstevel@tonic-gate } 532*0Sstevel@tonic-gate 533*0Sstevel@tonic-gate if (type == CT_PR_EV_EXIT) { 534*0Sstevel@tonic-gate int exitstatus; 535*0Sstevel@tonic-gate 536*0Sstevel@tonic-gate (void) ct_pr_event_get_pid(ev, &pid); 537*0Sstevel@tonic-gate (void) ct_pr_event_get_exitstatus(ev, 538*0Sstevel@tonic-gate &exitstatus); 539*0Sstevel@tonic-gate 540*0Sstevel@tonic-gate if (st->st_configd_pid != pid) { 541*0Sstevel@tonic-gate /* 542*0Sstevel@tonic-gate * This is the child exiting, so we 543*0Sstevel@tonic-gate * abandon the contract and restart 544*0Sstevel@tonic-gate * configd. 545*0Sstevel@tonic-gate */ 546*0Sstevel@tonic-gate contract_abandon(ctid); 547*0Sstevel@tonic-gate fork_configd(exitstatus); 548*0Sstevel@tonic-gate } 549*0Sstevel@tonic-gate } 550*0Sstevel@tonic-gate 551*0Sstevel@tonic-gate efd = contract_open(ctid, "process", "ctl", O_WRONLY); 552*0Sstevel@tonic-gate if (efd != -1) { 553*0Sstevel@tonic-gate (void) ct_ctl_ack(efd, evid); 554*0Sstevel@tonic-gate startd_close(efd); 555*0Sstevel@tonic-gate } 556*0Sstevel@tonic-gate 557*0Sstevel@tonic-gate ct_event_free(ev); 558*0Sstevel@tonic-gate 559*0Sstevel@tonic-gate } 560*0Sstevel@tonic-gate 561*0Sstevel@tonic-gate /*NOTREACHED*/ 562*0Sstevel@tonic-gate return (NULL); 563*0Sstevel@tonic-gate } 564*0Sstevel@tonic-gate 565*0Sstevel@tonic-gate void 566*0Sstevel@tonic-gate fork_rc_script(char rl, const char *arg, boolean_t wait) 567*0Sstevel@tonic-gate { 568*0Sstevel@tonic-gate pid_t pid; 569*0Sstevel@tonic-gate int tmpl, err, stat; 570*0Sstevel@tonic-gate char path[20] = "/sbin/rc.", log[20] = "rc..log", timebuf[20]; 571*0Sstevel@tonic-gate time_t now; 572*0Sstevel@tonic-gate struct tm ltime; 573*0Sstevel@tonic-gate size_t sz; 574*0Sstevel@tonic-gate char *pathenv; 575*0Sstevel@tonic-gate char **nenv; 576*0Sstevel@tonic-gate 577*0Sstevel@tonic-gate path[8] = rl; 578*0Sstevel@tonic-gate 579*0Sstevel@tonic-gate tmpl = open64(CTFS_ROOT "/process/template", O_RDWR); 580*0Sstevel@tonic-gate if (tmpl >= 0) { 581*0Sstevel@tonic-gate err = ct_tmpl_set_critical(tmpl, 0); 582*0Sstevel@tonic-gate assert(err == 0); 583*0Sstevel@tonic-gate 584*0Sstevel@tonic-gate err = ct_tmpl_set_informative(tmpl, 0); 585*0Sstevel@tonic-gate assert(err == 0); 586*0Sstevel@tonic-gate 587*0Sstevel@tonic-gate err = ct_pr_tmpl_set_fatal(tmpl, 0); 588*0Sstevel@tonic-gate assert(err == 0); 589*0Sstevel@tonic-gate 590*0Sstevel@tonic-gate err = ct_tmpl_activate(tmpl); 591*0Sstevel@tonic-gate assert(err == 0); 592*0Sstevel@tonic-gate 593*0Sstevel@tonic-gate err = close(tmpl); 594*0Sstevel@tonic-gate assert(err == 0); 595*0Sstevel@tonic-gate } else { 596*0Sstevel@tonic-gate uu_warn("Could not create contract template for %s.\n", path); 597*0Sstevel@tonic-gate } 598*0Sstevel@tonic-gate 599*0Sstevel@tonic-gate pid = startd_fork1(NULL); 600*0Sstevel@tonic-gate if (pid < 0) { 601*0Sstevel@tonic-gate return; 602*0Sstevel@tonic-gate } else if (pid != 0) { 603*0Sstevel@tonic-gate /* parent */ 604*0Sstevel@tonic-gate if (wait) { 605*0Sstevel@tonic-gate do 606*0Sstevel@tonic-gate err = waitpid(pid, &stat, 0); 607*0Sstevel@tonic-gate while (err != 0 && errno == EINTR); 608*0Sstevel@tonic-gate 609*0Sstevel@tonic-gate if (!WIFEXITED(stat)) { 610*0Sstevel@tonic-gate log_framework(LOG_INFO, 611*0Sstevel@tonic-gate "%s terminated with waitpid() status %d.\n", 612*0Sstevel@tonic-gate path, stat); 613*0Sstevel@tonic-gate } else if (WEXITSTATUS(stat) != 0) { 614*0Sstevel@tonic-gate log_framework(LOG_INFO, 615*0Sstevel@tonic-gate "%s failed with status %d.\n", path, 616*0Sstevel@tonic-gate WEXITSTATUS(stat)); 617*0Sstevel@tonic-gate } 618*0Sstevel@tonic-gate } 619*0Sstevel@tonic-gate 620*0Sstevel@tonic-gate return; 621*0Sstevel@tonic-gate } 622*0Sstevel@tonic-gate 623*0Sstevel@tonic-gate /* child */ 624*0Sstevel@tonic-gate 625*0Sstevel@tonic-gate log[2] = rl; 626*0Sstevel@tonic-gate 627*0Sstevel@tonic-gate setlog(log); 628*0Sstevel@tonic-gate 629*0Sstevel@tonic-gate now = time(NULL); 630*0Sstevel@tonic-gate sz = strftime(timebuf, sizeof (timebuf), "%b %e %T", 631*0Sstevel@tonic-gate localtime_r(&now, <ime)); 632*0Sstevel@tonic-gate assert(sz != 0); 633*0Sstevel@tonic-gate 634*0Sstevel@tonic-gate (void) fprintf(stderr, "%s Executing %s %s\n", timebuf, path, arg); 635*0Sstevel@tonic-gate 636*0Sstevel@tonic-gate if (rl == 'S') 637*0Sstevel@tonic-gate pathenv = "PATH=/sbin:/usr/sbin:/usr/bin"; 638*0Sstevel@tonic-gate else 639*0Sstevel@tonic-gate pathenv = "PATH=/usr/sbin:/usr/bin"; 640*0Sstevel@tonic-gate 641*0Sstevel@tonic-gate nenv = set_smf_env(NULL, 0, pathenv, NULL, NULL); 642*0Sstevel@tonic-gate 643*0Sstevel@tonic-gate (void) execle(path, path, arg, 0, nenv); 644*0Sstevel@tonic-gate 645*0Sstevel@tonic-gate perror("exec"); 646*0Sstevel@tonic-gate exit(0); 647*0Sstevel@tonic-gate } 648