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 /* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */ 28*0Sstevel@tonic-gate /* All Rights Reserved */ 29*0Sstevel@tonic-gate 30*0Sstevel@tonic-gate /* 31*0Sstevel@tonic-gate * University Copyright- Copyright (c) 1982, 1986, 1988 32*0Sstevel@tonic-gate * The Regents of the University of California 33*0Sstevel@tonic-gate * All Rights Reserved 34*0Sstevel@tonic-gate * 35*0Sstevel@tonic-gate * University Acknowledgment- Portions of this document are derived from 36*0Sstevel@tonic-gate * software developed by the University of California, Berkeley, and its 37*0Sstevel@tonic-gate * contributors. 38*0Sstevel@tonic-gate */ 39*0Sstevel@tonic-gate 40*0Sstevel@tonic-gate #pragma ident "%Z%%M% %I% %E% SMI" 41*0Sstevel@tonic-gate 42*0Sstevel@tonic-gate /* 43*0Sstevel@tonic-gate * init(1M) is the general process spawning program. Its primary job is to 44*0Sstevel@tonic-gate * start and restart svc.startd for smf(5). For backwards-compatibility it also 45*0Sstevel@tonic-gate * spawns and respawns processes according to /etc/inittab and the current 46*0Sstevel@tonic-gate * run-level. It reads /etc/default/inittab for general configuration. 47*0Sstevel@tonic-gate * 48*0Sstevel@tonic-gate * To change run-levels the system administrator runs init from the command 49*0Sstevel@tonic-gate * line with a level name. init signals svc.startd via libscf and directs the 50*0Sstevel@tonic-gate * zone's init (pid 1 in the global zone) what to do by sending it a signal; 51*0Sstevel@tonic-gate * these signal numbers are commonly refered to in the code as 'states'. Valid 52*0Sstevel@tonic-gate * run-levels are [sS0123456]. Additionally, init can be given directives 53*0Sstevel@tonic-gate * [qQabc], which indicate actions to be taken pertaining to /etc/inittab. 54*0Sstevel@tonic-gate * 55*0Sstevel@tonic-gate * When init processes inittab entries, it finds processes that are to be 56*0Sstevel@tonic-gate * spawned at various run-levels. inittab contains the set of the levels for 57*0Sstevel@tonic-gate * which each inittab entry is valid. 58*0Sstevel@tonic-gate * 59*0Sstevel@tonic-gate * State File and Restartability 60*0Sstevel@tonic-gate * Premature exit by init(1M) is handled as a special case by the kernel: 61*0Sstevel@tonic-gate * init(1M) will be immediately re-executed, retaining its original PID. (PID 62*0Sstevel@tonic-gate * 1 in the global zone.) To track the processes it has previously spawned, 63*0Sstevel@tonic-gate * as well as other mutable state, init(1M) regularly updates a state file 64*0Sstevel@tonic-gate * such that its subsequent invocations have knowledge of its various 65*0Sstevel@tonic-gate * dependent processes and duties. 66*0Sstevel@tonic-gate * 67*0Sstevel@tonic-gate * Process Contracts 68*0Sstevel@tonic-gate * We start svc.startd(1M) in a contract and transfer inherited contracts when 69*0Sstevel@tonic-gate * restarting it. Everything else is started using the legacy contract 70*0Sstevel@tonic-gate * template, and the created contracts are abandoned when they become empty. 71*0Sstevel@tonic-gate * 72*0Sstevel@tonic-gate * utmpx Entry Handling 73*0Sstevel@tonic-gate * Because init(1M) no longer governs the startup process, its knowledge of 74*0Sstevel@tonic-gate * when utmpx becomes writable is indirect. However, spawned processes 75*0Sstevel@tonic-gate * expect to be constructed with valid utmpx entries. As a result, attempts 76*0Sstevel@tonic-gate * to write normal entries will be retried until successful. 77*0Sstevel@tonic-gate * 78*0Sstevel@tonic-gate * Maintenance Mode 79*0Sstevel@tonic-gate * In certain failure scenarios, init(1M) will enter a maintenance mode, in 80*0Sstevel@tonic-gate * which it invokes sulogin(1M) to allow the operator an opportunity to 81*0Sstevel@tonic-gate * repair the system. Normally, this operation is performed as a 82*0Sstevel@tonic-gate * fork(2)-exec(2)-waitpid(3C) sequence with the parent waiting for repair or 83*0Sstevel@tonic-gate * diagnosis to be completed. In the cases that fork(2) requests themselves 84*0Sstevel@tonic-gate * fail, init(1M) will directly execute sulogin(1M), and allow the kernel to 85*0Sstevel@tonic-gate * restart init(1M) on exit from the operator session. 86*0Sstevel@tonic-gate * 87*0Sstevel@tonic-gate * One scenario where init(1M) enters its maintenance mode is when 88*0Sstevel@tonic-gate * svc.startd(1M) begins to fail rapidly, defined as when the average time 89*0Sstevel@tonic-gate * between recent failures drops below a given threshold. 90*0Sstevel@tonic-gate */ 91*0Sstevel@tonic-gate 92*0Sstevel@tonic-gate #include <sys/contract/process.h> 93*0Sstevel@tonic-gate #include <sys/ctfs.h> 94*0Sstevel@tonic-gate #include <sys/stat.h> 95*0Sstevel@tonic-gate #include <sys/statvfs.h> 96*0Sstevel@tonic-gate #include <sys/stropts.h> 97*0Sstevel@tonic-gate #include <sys/systeminfo.h> 98*0Sstevel@tonic-gate #include <sys/time.h> 99*0Sstevel@tonic-gate #include <sys/termios.h> 100*0Sstevel@tonic-gate #include <sys/tty.h> 101*0Sstevel@tonic-gate #include <sys/types.h> 102*0Sstevel@tonic-gate #include <sys/utsname.h> 103*0Sstevel@tonic-gate 104*0Sstevel@tonic-gate #include <bsm/adt_event.h> 105*0Sstevel@tonic-gate #include <bsm/libbsm.h> 106*0Sstevel@tonic-gate #include <security/pam_appl.h> 107*0Sstevel@tonic-gate 108*0Sstevel@tonic-gate #include <assert.h> 109*0Sstevel@tonic-gate #include <ctype.h> 110*0Sstevel@tonic-gate #include <dirent.h> 111*0Sstevel@tonic-gate #include <errno.h> 112*0Sstevel@tonic-gate #include <fcntl.h> 113*0Sstevel@tonic-gate #include <libcontract.h> 114*0Sstevel@tonic-gate #include <libcontract_priv.h> 115*0Sstevel@tonic-gate #include <libintl.h> 116*0Sstevel@tonic-gate #include <libscf.h> 117*0Sstevel@tonic-gate #include <libscf_priv.h> 118*0Sstevel@tonic-gate #include <poll.h> 119*0Sstevel@tonic-gate #include <procfs.h> 120*0Sstevel@tonic-gate #include <signal.h> 121*0Sstevel@tonic-gate #include <stdarg.h> 122*0Sstevel@tonic-gate #include <stdio.h> 123*0Sstevel@tonic-gate #include <stdio_ext.h> 124*0Sstevel@tonic-gate #include <stdlib.h> 125*0Sstevel@tonic-gate #include <string.h> 126*0Sstevel@tonic-gate #include <strings.h> 127*0Sstevel@tonic-gate #include <syslog.h> 128*0Sstevel@tonic-gate #include <time.h> 129*0Sstevel@tonic-gate #include <ulimit.h> 130*0Sstevel@tonic-gate #include <unistd.h> 131*0Sstevel@tonic-gate #include <utmpx.h> 132*0Sstevel@tonic-gate #include <wait.h> 133*0Sstevel@tonic-gate #include <zone.h> 134*0Sstevel@tonic-gate #include <ucontext.h> 135*0Sstevel@tonic-gate 136*0Sstevel@tonic-gate #undef sleep 137*0Sstevel@tonic-gate 138*0Sstevel@tonic-gate #define fioctl(p, sptr, cmd) ioctl(fileno(p), sptr, cmd) 139*0Sstevel@tonic-gate #define min(a, b) (((a) < (b)) ? (a) : (b)) 140*0Sstevel@tonic-gate 141*0Sstevel@tonic-gate #define TRUE 1 142*0Sstevel@tonic-gate #define FALSE 0 143*0Sstevel@tonic-gate #define FAILURE -1 144*0Sstevel@tonic-gate 145*0Sstevel@tonic-gate #define UT_LINE_SZ 32 /* Size of a utmpx ut_line field */ 146*0Sstevel@tonic-gate 147*0Sstevel@tonic-gate /* 148*0Sstevel@tonic-gate * SLEEPTIME The number of seconds "init" sleeps between wakeups if 149*0Sstevel@tonic-gate * nothing else requires this "init" wakeup. 150*0Sstevel@tonic-gate */ 151*0Sstevel@tonic-gate #define SLEEPTIME (5 * 60) 152*0Sstevel@tonic-gate 153*0Sstevel@tonic-gate /* 154*0Sstevel@tonic-gate * MAXCMDL The maximum length of a command string in inittab. 155*0Sstevel@tonic-gate */ 156*0Sstevel@tonic-gate #define MAXCMDL 512 157*0Sstevel@tonic-gate 158*0Sstevel@tonic-gate /* 159*0Sstevel@tonic-gate * EXEC The length of the prefix string added to all comamnds 160*0Sstevel@tonic-gate * found in inittab. 161*0Sstevel@tonic-gate */ 162*0Sstevel@tonic-gate #define EXEC (sizeof ("exec ") - 1) 163*0Sstevel@tonic-gate 164*0Sstevel@tonic-gate /* 165*0Sstevel@tonic-gate * TWARN The amount of time between warning signal, SIGTERM, 166*0Sstevel@tonic-gate * and the fatal kill signal, SIGKILL. 167*0Sstevel@tonic-gate */ 168*0Sstevel@tonic-gate #define TWARN 5 169*0Sstevel@tonic-gate 170*0Sstevel@tonic-gate #define id_eq(x, y) ((x[0] == y[0] && x[1] == y[1] && x[2] == y[2] &&\ 171*0Sstevel@tonic-gate x[3] == y[3]) ? TRUE : FALSE) 172*0Sstevel@tonic-gate 173*0Sstevel@tonic-gate /* 174*0Sstevel@tonic-gate * The kernel's default umask is 022 these days; since some processes inherit 175*0Sstevel@tonic-gate * their umask from init, init will set it from CMASK in /etc/default/init. 176*0Sstevel@tonic-gate * init gets the default umask from the kernel, it sets it to 022 whenever 177*0Sstevel@tonic-gate * it wants to create a file and reverts to CMASK afterwards. 178*0Sstevel@tonic-gate */ 179*0Sstevel@tonic-gate 180*0Sstevel@tonic-gate static int cmask; 181*0Sstevel@tonic-gate 182*0Sstevel@tonic-gate /* 183*0Sstevel@tonic-gate * The following definitions, concluding with the 'lvls' array, provide a 184*0Sstevel@tonic-gate * common mapping between level-name (like 'S'), signal number (state), 185*0Sstevel@tonic-gate * run-level mask, and specific properties associated with a run-level. 186*0Sstevel@tonic-gate * This array should be accessed using the routines lvlname_to_state(), 187*0Sstevel@tonic-gate * lvlname_to_mask(), state_to_mask(), and state_to_flags(). 188*0Sstevel@tonic-gate */ 189*0Sstevel@tonic-gate 190*0Sstevel@tonic-gate /* 191*0Sstevel@tonic-gate * Correspondence of signals to init actions. 192*0Sstevel@tonic-gate */ 193*0Sstevel@tonic-gate #define LVLQ SIGHUP 194*0Sstevel@tonic-gate #define LVL0 SIGINT 195*0Sstevel@tonic-gate #define LVL1 SIGQUIT 196*0Sstevel@tonic-gate #define LVL2 SIGILL 197*0Sstevel@tonic-gate #define LVL3 SIGTRAP 198*0Sstevel@tonic-gate #define LVL4 SIGIOT 199*0Sstevel@tonic-gate #define LVL5 SIGEMT 200*0Sstevel@tonic-gate #define LVL6 SIGFPE 201*0Sstevel@tonic-gate #define SINGLE_USER SIGBUS 202*0Sstevel@tonic-gate #define LVLa SIGSEGV 203*0Sstevel@tonic-gate #define LVLb SIGSYS 204*0Sstevel@tonic-gate #define LVLc SIGPIPE 205*0Sstevel@tonic-gate 206*0Sstevel@tonic-gate /* 207*0Sstevel@tonic-gate * Bit Mask for each level. Used to determine legal levels. 208*0Sstevel@tonic-gate */ 209*0Sstevel@tonic-gate #define MASK0 0x0001 210*0Sstevel@tonic-gate #define MASK1 0x0002 211*0Sstevel@tonic-gate #define MASK2 0x0004 212*0Sstevel@tonic-gate #define MASK3 0x0008 213*0Sstevel@tonic-gate #define MASK4 0x0010 214*0Sstevel@tonic-gate #define MASK5 0x0020 215*0Sstevel@tonic-gate #define MASK6 0x0040 216*0Sstevel@tonic-gate #define MASKSU 0x0080 217*0Sstevel@tonic-gate #define MASKa 0x0100 218*0Sstevel@tonic-gate #define MASKb 0x0200 219*0Sstevel@tonic-gate #define MASKc 0x0400 220*0Sstevel@tonic-gate 221*0Sstevel@tonic-gate #define MASK_NUMERIC (MASK0 | MASK1 | MASK2 | MASK3 | MASK4 | MASK5 | MASK6) 222*0Sstevel@tonic-gate #define MASK_abc (MASKa | MASKb | MASKc) 223*0Sstevel@tonic-gate 224*0Sstevel@tonic-gate /* 225*0Sstevel@tonic-gate * Flags to indicate properties of various states. 226*0Sstevel@tonic-gate */ 227*0Sstevel@tonic-gate #define LSEL_RUNLEVEL 0x0001 /* runlevels you can transition to */ 228*0Sstevel@tonic-gate #define LSEL_NOAUDIT 0x0002 /* levels with auditing disabled */ 229*0Sstevel@tonic-gate 230*0Sstevel@tonic-gate typedef struct lvl { 231*0Sstevel@tonic-gate int lvl_state; 232*0Sstevel@tonic-gate int lvl_mask; 233*0Sstevel@tonic-gate char lvl_name; 234*0Sstevel@tonic-gate int lvl_flags; 235*0Sstevel@tonic-gate } lvl_t; 236*0Sstevel@tonic-gate 237*0Sstevel@tonic-gate static lvl_t lvls[] = { 238*0Sstevel@tonic-gate { LVLQ, 0, 'Q', 0 }, 239*0Sstevel@tonic-gate { LVLQ, 0, 'q', 0 }, 240*0Sstevel@tonic-gate { LVL0, MASK0, '0', LSEL_RUNLEVEL | LSEL_NOAUDIT }, 241*0Sstevel@tonic-gate { LVL1, MASK1, '1', LSEL_RUNLEVEL | LSEL_NOAUDIT }, 242*0Sstevel@tonic-gate { LVL2, MASK2, '2', LSEL_RUNLEVEL }, 243*0Sstevel@tonic-gate { LVL3, MASK3, '3', LSEL_RUNLEVEL }, 244*0Sstevel@tonic-gate { LVL4, MASK4, '4', LSEL_RUNLEVEL }, 245*0Sstevel@tonic-gate { LVL5, MASK5, '5', LSEL_RUNLEVEL | LSEL_NOAUDIT }, 246*0Sstevel@tonic-gate { LVL6, MASK6, '6', LSEL_RUNLEVEL | LSEL_NOAUDIT }, 247*0Sstevel@tonic-gate { SINGLE_USER, MASKSU, 'S', LSEL_RUNLEVEL | LSEL_NOAUDIT }, 248*0Sstevel@tonic-gate { SINGLE_USER, MASKSU, 's', LSEL_RUNLEVEL | LSEL_NOAUDIT }, 249*0Sstevel@tonic-gate { LVLa, MASKa, 'a', 0 }, 250*0Sstevel@tonic-gate { LVLb, MASKb, 'b', 0 }, 251*0Sstevel@tonic-gate { LVLc, MASKc, 'c', 0 } 252*0Sstevel@tonic-gate }; 253*0Sstevel@tonic-gate 254*0Sstevel@tonic-gate #define LVL_NELEMS (sizeof (lvls) / sizeof (lvl_t)) 255*0Sstevel@tonic-gate 256*0Sstevel@tonic-gate /* 257*0Sstevel@tonic-gate * Legal action field values. 258*0Sstevel@tonic-gate */ 259*0Sstevel@tonic-gate #define OFF 0 /* Kill process if on, else ignore */ 260*0Sstevel@tonic-gate #define RESPAWN 1 /* Continuously restart process when it dies */ 261*0Sstevel@tonic-gate #define ONDEMAND RESPAWN /* Respawn for a, b, c type processes */ 262*0Sstevel@tonic-gate #define ONCE 2 /* Start process, do not respawn when dead */ 263*0Sstevel@tonic-gate #define WAIT 3 /* Perform once and wait to complete */ 264*0Sstevel@tonic-gate #define BOOT 4 /* Start at boot time only */ 265*0Sstevel@tonic-gate #define BOOTWAIT 5 /* Start at boot time and wait to complete */ 266*0Sstevel@tonic-gate #define POWERFAIL 6 /* Start on powerfail */ 267*0Sstevel@tonic-gate #define POWERWAIT 7 /* Start and wait for complete on powerfail */ 268*0Sstevel@tonic-gate #define INITDEFAULT 8 /* Default level "init" should start at */ 269*0Sstevel@tonic-gate #define SYSINIT 9 /* Actions performed before init speaks */ 270*0Sstevel@tonic-gate 271*0Sstevel@tonic-gate #define M_OFF 0001 272*0Sstevel@tonic-gate #define M_RESPAWN 0002 273*0Sstevel@tonic-gate #define M_ONDEMAND M_RESPAWN 274*0Sstevel@tonic-gate #define M_ONCE 0004 275*0Sstevel@tonic-gate #define M_WAIT 0010 276*0Sstevel@tonic-gate #define M_BOOT 0020 277*0Sstevel@tonic-gate #define M_BOOTWAIT 0040 278*0Sstevel@tonic-gate #define M_PF 0100 279*0Sstevel@tonic-gate #define M_PWAIT 0200 280*0Sstevel@tonic-gate #define M_INITDEFAULT 0400 281*0Sstevel@tonic-gate #define M_SYSINIT 01000 282*0Sstevel@tonic-gate 283*0Sstevel@tonic-gate /* States for the inittab parser in getcmd(). */ 284*0Sstevel@tonic-gate #define ID 1 285*0Sstevel@tonic-gate #define LEVELS 2 286*0Sstevel@tonic-gate #define ACTION 3 287*0Sstevel@tonic-gate #define COMMAND 4 288*0Sstevel@tonic-gate #define COMMENT 5 289*0Sstevel@tonic-gate 290*0Sstevel@tonic-gate /* 291*0Sstevel@tonic-gate * Init can be in any of three main states, "normal" mode where it is 292*0Sstevel@tonic-gate * processing entries for the lines file in a normal fashion, "boot" mode, 293*0Sstevel@tonic-gate * where it is only interested in the boot actions, and "powerfail" mode, 294*0Sstevel@tonic-gate * where it is only interested in powerfail related actions. The following 295*0Sstevel@tonic-gate * masks declare the legal actions for each mode. 296*0Sstevel@tonic-gate */ 297*0Sstevel@tonic-gate #define NORMAL_MODES (M_OFF | M_RESPAWN | M_ONCE | M_WAIT) 298*0Sstevel@tonic-gate #define BOOT_MODES (M_BOOT | M_BOOTWAIT) 299*0Sstevel@tonic-gate #define PF_MODES (M_PF | M_PWAIT) 300*0Sstevel@tonic-gate 301*0Sstevel@tonic-gate struct PROC_TABLE { 302*0Sstevel@tonic-gate char p_id[4]; /* Four letter unique id of process */ 303*0Sstevel@tonic-gate pid_t p_pid; /* Process id */ 304*0Sstevel@tonic-gate short p_count; /* How many respawns of this command in */ 305*0Sstevel@tonic-gate /* the current series */ 306*0Sstevel@tonic-gate long p_time; /* Start time for a series of respawns */ 307*0Sstevel@tonic-gate short p_flags; 308*0Sstevel@tonic-gate short p_exit; /* Exit status of a process which died */ 309*0Sstevel@tonic-gate }; 310*0Sstevel@tonic-gate 311*0Sstevel@tonic-gate /* 312*0Sstevel@tonic-gate * Flags for the "p_flags" word of a PROC_TABLE entry: 313*0Sstevel@tonic-gate * 314*0Sstevel@tonic-gate * OCCUPIED This slot in init's proc table is in use. 315*0Sstevel@tonic-gate * 316*0Sstevel@tonic-gate * LIVING Process is alive. 317*0Sstevel@tonic-gate * 318*0Sstevel@tonic-gate * NOCLEANUP efork() is not allowed to cleanup this entry even 319*0Sstevel@tonic-gate * if process is dead. 320*0Sstevel@tonic-gate * 321*0Sstevel@tonic-gate * NAMED This process has a name, i.e. came from inittab. 322*0Sstevel@tonic-gate * 323*0Sstevel@tonic-gate * DEMANDREQUEST Process started by a "telinit [abc]" command. Processes 324*0Sstevel@tonic-gate * formed this way are respawnable and immune to level 325*0Sstevel@tonic-gate * changes as long as their entry exists in inittab. 326*0Sstevel@tonic-gate * 327*0Sstevel@tonic-gate * TOUCHED Flag used by remv() to determine whether it has looked 328*0Sstevel@tonic-gate * at an entry while checking for processes to be killed. 329*0Sstevel@tonic-gate * 330*0Sstevel@tonic-gate * WARNED Flag used by remv() to mark processes that have been 331*0Sstevel@tonic-gate * sent the SIGTERM signal. If they don't die in 5 332*0Sstevel@tonic-gate * seconds, they are sent the SIGKILL signal. 333*0Sstevel@tonic-gate * 334*0Sstevel@tonic-gate * KILLED Flag used by remv() to mark procs that have been sent 335*0Sstevel@tonic-gate * the SIGTERM and SIGKILL signals. 336*0Sstevel@tonic-gate * 337*0Sstevel@tonic-gate * PF_MASK Bitwise or of legal flags, for sanity checking. 338*0Sstevel@tonic-gate */ 339*0Sstevel@tonic-gate #define OCCUPIED 01 340*0Sstevel@tonic-gate #define LIVING 02 341*0Sstevel@tonic-gate #define NOCLEANUP 04 342*0Sstevel@tonic-gate #define NAMED 010 343*0Sstevel@tonic-gate #define DEMANDREQUEST 020 344*0Sstevel@tonic-gate #define TOUCHED 040 345*0Sstevel@tonic-gate #define WARNED 0100 346*0Sstevel@tonic-gate #define KILLED 0200 347*0Sstevel@tonic-gate #define PF_MASK 0377 348*0Sstevel@tonic-gate 349*0Sstevel@tonic-gate /* 350*0Sstevel@tonic-gate * Respawn limits for processes that are to be respawned: 351*0Sstevel@tonic-gate * 352*0Sstevel@tonic-gate * SPAWN_INTERVAL The number of seconds over which "init" will try to 353*0Sstevel@tonic-gate * respawn a process SPAWN_LIMIT times before it gets mad. 354*0Sstevel@tonic-gate * 355*0Sstevel@tonic-gate * SPAWN_LIMIT The number of respawns "init" will attempt in 356*0Sstevel@tonic-gate * SPAWN_INTERVAL seconds before it generates an 357*0Sstevel@tonic-gate * error message and inhibits further tries for 358*0Sstevel@tonic-gate * INHIBIT seconds. 359*0Sstevel@tonic-gate * 360*0Sstevel@tonic-gate * INHIBIT The number of seconds "init" ignores an entry it had 361*0Sstevel@tonic-gate * trouble spawning unless a "telinit Q" is received. 362*0Sstevel@tonic-gate */ 363*0Sstevel@tonic-gate 364*0Sstevel@tonic-gate #define SPAWN_INTERVAL (2*60) 365*0Sstevel@tonic-gate #define SPAWN_LIMIT 10 366*0Sstevel@tonic-gate #define INHIBIT (5*60) 367*0Sstevel@tonic-gate 368*0Sstevel@tonic-gate /* 369*0Sstevel@tonic-gate * The maximum number of decimal digits for an id_t. (ceil(log10 (max_id))) 370*0Sstevel@tonic-gate */ 371*0Sstevel@tonic-gate #define ID_MAX_STR_LEN 10 372*0Sstevel@tonic-gate 373*0Sstevel@tonic-gate #define NULLPROC ((struct PROC_TABLE *)(0)) 374*0Sstevel@tonic-gate #define NO_ROOM ((struct PROC_TABLE *)(FAILURE)) 375*0Sstevel@tonic-gate 376*0Sstevel@tonic-gate struct CMD_LINE { 377*0Sstevel@tonic-gate char c_id[4]; /* Four letter unique id of process to be */ 378*0Sstevel@tonic-gate /* affected by action */ 379*0Sstevel@tonic-gate short c_levels; /* Mask of legal levels for process */ 380*0Sstevel@tonic-gate short c_action; /* Mask for type of action required */ 381*0Sstevel@tonic-gate char *c_command; /* Pointer to init command */ 382*0Sstevel@tonic-gate }; 383*0Sstevel@tonic-gate 384*0Sstevel@tonic-gate struct pidrec { 385*0Sstevel@tonic-gate int pd_type; /* Command type */ 386*0Sstevel@tonic-gate pid_t pd_pid; /* pid to add or remove */ 387*0Sstevel@tonic-gate }; 388*0Sstevel@tonic-gate 389*0Sstevel@tonic-gate /* 390*0Sstevel@tonic-gate * pd_type's 391*0Sstevel@tonic-gate */ 392*0Sstevel@tonic-gate #define ADDPID 1 393*0Sstevel@tonic-gate #define REMPID 2 394*0Sstevel@tonic-gate 395*0Sstevel@tonic-gate static struct pidlist { 396*0Sstevel@tonic-gate pid_t pl_pid; /* pid to watch for */ 397*0Sstevel@tonic-gate int pl_dflag; /* Flag indicating SIGCLD from this pid */ 398*0Sstevel@tonic-gate short pl_exit; /* Exit status of proc */ 399*0Sstevel@tonic-gate struct pidlist *pl_next; /* Next in list */ 400*0Sstevel@tonic-gate } *Plhead, *Plfree; 401*0Sstevel@tonic-gate 402*0Sstevel@tonic-gate /* 403*0Sstevel@tonic-gate * The following structure contains a set of modes for /dev/syscon 404*0Sstevel@tonic-gate * and should match the default contents of /etc/ioctl.syscon. 405*0Sstevel@tonic-gate */ 406*0Sstevel@tonic-gate static struct termios dflt_termios = { 407*0Sstevel@tonic-gate BRKINT|ICRNL|IXON|IMAXBEL, /* iflag */ 408*0Sstevel@tonic-gate OPOST|ONLCR|TAB3, /* oflag */ 409*0Sstevel@tonic-gate CS8|CREAD|B9600, /* cflag */ 410*0Sstevel@tonic-gate ISIG|ICANON|ECHO|ECHOE|ECHOK|ECHOCTL|ECHOKE|IEXTEN, /* lflag */ 411*0Sstevel@tonic-gate CINTR, CQUIT, CERASE, CKILL, CEOF, 0, 0, 0, 412*0Sstevel@tonic-gate 0, 0, 0, 0, 0, 0, 0, 0, 413*0Sstevel@tonic-gate 0, 0, 0 414*0Sstevel@tonic-gate }; 415*0Sstevel@tonic-gate 416*0Sstevel@tonic-gate static struct termios stored_syscon_termios; 417*0Sstevel@tonic-gate static int write_ioctl = 0; /* Rewrite /etc/ioctl.syscon */ 418*0Sstevel@tonic-gate 419*0Sstevel@tonic-gate static union WAKEUP { 420*0Sstevel@tonic-gate struct WAKEFLAGS { 421*0Sstevel@tonic-gate unsigned w_usersignal : 1; /* User sent signal to "init" */ 422*0Sstevel@tonic-gate unsigned w_childdeath : 1; /* An "init" child died */ 423*0Sstevel@tonic-gate unsigned w_powerhit : 1; /* OS experienced powerfail */ 424*0Sstevel@tonic-gate } w_flags; 425*0Sstevel@tonic-gate int w_mask; 426*0Sstevel@tonic-gate } wakeup; 427*0Sstevel@tonic-gate 428*0Sstevel@tonic-gate 429*0Sstevel@tonic-gate struct init_state { 430*0Sstevel@tonic-gate int ist_runlevel; 431*0Sstevel@tonic-gate int ist_num_proc; 432*0Sstevel@tonic-gate int ist_utmpx_ok; 433*0Sstevel@tonic-gate struct PROC_TABLE ist_proc_table[1]; 434*0Sstevel@tonic-gate }; 435*0Sstevel@tonic-gate 436*0Sstevel@tonic-gate #define cur_state (g_state->ist_runlevel) 437*0Sstevel@tonic-gate #define num_proc (g_state->ist_num_proc) 438*0Sstevel@tonic-gate #define proc_table (g_state->ist_proc_table) 439*0Sstevel@tonic-gate #define utmpx_ok (g_state->ist_utmpx_ok) 440*0Sstevel@tonic-gate 441*0Sstevel@tonic-gate /* Contract cookies. */ 442*0Sstevel@tonic-gate #define ORDINARY_COOKIE 0 443*0Sstevel@tonic-gate #define STARTD_COOKIE 1 444*0Sstevel@tonic-gate 445*0Sstevel@tonic-gate 446*0Sstevel@tonic-gate #ifndef NDEBUG 447*0Sstevel@tonic-gate #define bad_error(func, err) { \ 448*0Sstevel@tonic-gate (void) fprintf(stderr, "%s:%d: %s() failed with unexpected " \ 449*0Sstevel@tonic-gate "error %d. Aborting.\n", __FILE__, __LINE__, (func), (err)); \ 450*0Sstevel@tonic-gate abort(); \ 451*0Sstevel@tonic-gate } 452*0Sstevel@tonic-gate #else 453*0Sstevel@tonic-gate #define bad_error(func, err) abort() 454*0Sstevel@tonic-gate #endif 455*0Sstevel@tonic-gate 456*0Sstevel@tonic-gate 457*0Sstevel@tonic-gate /* 458*0Sstevel@tonic-gate * Useful file and device names. 459*0Sstevel@tonic-gate */ 460*0Sstevel@tonic-gate static char *CONSOLE = "/dev/console"; /* Real system console */ 461*0Sstevel@tonic-gate static char *INITPIPE_DIR = "/etc"; 462*0Sstevel@tonic-gate static char *INITPIPE = "/etc/initpipe"; 463*0Sstevel@tonic-gate 464*0Sstevel@tonic-gate #define INIT_STATE_DIR "/etc/svc/volatile" 465*0Sstevel@tonic-gate static const char * const init_state_file = INIT_STATE_DIR "/init.state"; 466*0Sstevel@tonic-gate static const char * const init_next_state_file = 467*0Sstevel@tonic-gate INIT_STATE_DIR "/init-next.state"; 468*0Sstevel@tonic-gate 469*0Sstevel@tonic-gate static const int init_num_proc = 20; /* Initial size of process table. */ 470*0Sstevel@tonic-gate 471*0Sstevel@tonic-gate static char *UTMPX = UTMPX_FILE; /* Snapshot record file */ 472*0Sstevel@tonic-gate static char *WTMPX = WTMPX_FILE; /* Long term record file */ 473*0Sstevel@tonic-gate static char *INITTAB = "/etc/inittab"; /* Script file for "init" */ 474*0Sstevel@tonic-gate static char *SYSTTY = "/dev/systty"; /* System Console */ 475*0Sstevel@tonic-gate static char *SYSCON = "/dev/syscon"; /* Virtual System console */ 476*0Sstevel@tonic-gate static char *IOCTLSYSCON = "/etc/ioctl.syscon"; /* Last syscon modes */ 477*0Sstevel@tonic-gate static char *ENVFILE = "/etc/default/init"; /* Default env. */ 478*0Sstevel@tonic-gate static char *SU = "/etc/sulogin"; /* Super-user program for single user */ 479*0Sstevel@tonic-gate static char *SH = "/sbin/sh"; /* Standard shell */ 480*0Sstevel@tonic-gate 481*0Sstevel@tonic-gate /* 482*0Sstevel@tonic-gate * Default Path. /sbin is included in path only during sysinit phase 483*0Sstevel@tonic-gate */ 484*0Sstevel@tonic-gate #define DEF_PATH "PATH=/usr/sbin:/usr/bin" 485*0Sstevel@tonic-gate #define INIT_PATH "PATH=/sbin:/usr/sbin:/usr/bin" 486*0Sstevel@tonic-gate 487*0Sstevel@tonic-gate static int prior_state; 488*0Sstevel@tonic-gate static int prev_state; /* State "init" was in last time it woke */ 489*0Sstevel@tonic-gate static int new_state; /* State user wants "init" to go to. */ 490*0Sstevel@tonic-gate static int op_modes = BOOT_MODES; /* Current state of "init" */ 491*0Sstevel@tonic-gate static int Gchild = 0; /* Flag to indicate "godchild" died, set in */ 492*0Sstevel@tonic-gate /* childeath() and cleared in cleanaux() */ 493*0Sstevel@tonic-gate static int Pfd = -1; /* fd to receive pids thru */ 494*0Sstevel@tonic-gate static unsigned int spawncnt, pausecnt; 495*0Sstevel@tonic-gate static int rsflag; /* Set if a respawn has taken place */ 496*0Sstevel@tonic-gate static volatile int time_up; /* Flag set to TRUE by the alarm interrupt */ 497*0Sstevel@tonic-gate /* routine each time an alarm interrupt */ 498*0Sstevel@tonic-gate /* takes place. */ 499*0Sstevel@tonic-gate static int sflg = 0; /* Set if we were booted -s to single user */ 500*0Sstevel@tonic-gate static int rflg = 0; /* Set if booted -r, reconfigure devices */ 501*0Sstevel@tonic-gate static int bflg = 0; /* Set if booted -b, don't run rc scripts */ 502*0Sstevel@tonic-gate static pid_t init_pid; /* PID of "one true" init for current zone */ 503*0Sstevel@tonic-gate 504*0Sstevel@tonic-gate static struct init_state *g_state = NULL; 505*0Sstevel@tonic-gate static size_t g_state_sz; 506*0Sstevel@tonic-gate static int booting = 1; /* Set while we're booting. */ 507*0Sstevel@tonic-gate 508*0Sstevel@tonic-gate /* 509*0Sstevel@tonic-gate * Array for default global environment. 510*0Sstevel@tonic-gate */ 511*0Sstevel@tonic-gate #define MAXENVENT 24 /* Max number of default env variables + 1 */ 512*0Sstevel@tonic-gate /* init can use three itself, so this leaves */ 513*0Sstevel@tonic-gate /* 20 for the administrator in ENVFILE. */ 514*0Sstevel@tonic-gate static char *glob_envp[MAXENVENT]; /* Array of environment strings */ 515*0Sstevel@tonic-gate static int glob_envn; /* Number of environment strings */ 516*0Sstevel@tonic-gate 517*0Sstevel@tonic-gate 518*0Sstevel@tonic-gate static struct pollfd poll_fds[1]; 519*0Sstevel@tonic-gate static int poll_nfds = 0; /* poll_fds is uninitialized */ 520*0Sstevel@tonic-gate 521*0Sstevel@tonic-gate static int legacy_tmpl = -1; /* fd for legacy contract template */ 522*0Sstevel@tonic-gate static int startd_tmpl = -1; /* fd for svc.startd's template */ 523*0Sstevel@tonic-gate 524*0Sstevel@tonic-gate static char startd_cline[256] = ""; /* svc.startd's command line */ 525*0Sstevel@tonic-gate static int do_restart_startd = 1; /* Whether to restart svc.startd. */ 526*0Sstevel@tonic-gate static char *smf_options = NULL; /* Options to give to startd. */ 527*0Sstevel@tonic-gate static int smf_debug = 0; /* Messages for debugging smf(5) */ 528*0Sstevel@tonic-gate static time_t init_boot_time; /* Substitute for kernel boot time. */ 529*0Sstevel@tonic-gate 530*0Sstevel@tonic-gate #define NSTARTD_FAILURE_TIMES 3 /* trigger after 3 failures */ 531*0Sstevel@tonic-gate #define STARTD_FAILURE_RATE_NS 5000000000LL /* 1 failure/5 seconds */ 532*0Sstevel@tonic-gate 533*0Sstevel@tonic-gate static hrtime_t startd_failure_time[NSTARTD_FAILURE_TIMES]; 534*0Sstevel@tonic-gate static uint_t startd_failure_index; 535*0Sstevel@tonic-gate 536*0Sstevel@tonic-gate 537*0Sstevel@tonic-gate static char *prog_name(char *); 538*0Sstevel@tonic-gate static int state_to_mask(int); 539*0Sstevel@tonic-gate static int lvlname_to_mask(char, int *); 540*0Sstevel@tonic-gate static void lscf_set_runlevel(char); 541*0Sstevel@tonic-gate static int state_to_flags(int); 542*0Sstevel@tonic-gate static char state_to_name(int); 543*0Sstevel@tonic-gate static int lvlname_to_state(char); 544*0Sstevel@tonic-gate static int getcmd(struct CMD_LINE *, char *); 545*0Sstevel@tonic-gate static int realcon(); 546*0Sstevel@tonic-gate static int spawn_processes(); 547*0Sstevel@tonic-gate static int get_ioctl_syscon(); 548*0Sstevel@tonic-gate static int account(short, struct PROC_TABLE *, char *); 549*0Sstevel@tonic-gate static void alarmclk(); 550*0Sstevel@tonic-gate static void childeath(int); 551*0Sstevel@tonic-gate static void cleanaux(); 552*0Sstevel@tonic-gate static void clearent(pid_t, short); 553*0Sstevel@tonic-gate static void console(boolean_t, char *, ...); 554*0Sstevel@tonic-gate static void init_signals(void); 555*0Sstevel@tonic-gate static void setup_pipe(); 556*0Sstevel@tonic-gate static void killproc(pid_t); 557*0Sstevel@tonic-gate static void init_env(); 558*0Sstevel@tonic-gate static void boot_init(); 559*0Sstevel@tonic-gate static void powerfail(); 560*0Sstevel@tonic-gate static void remv(); 561*0Sstevel@tonic-gate static void write_ioctl_syscon(); 562*0Sstevel@tonic-gate static void spawn(struct PROC_TABLE *, struct CMD_LINE *); 563*0Sstevel@tonic-gate static void setimer(int); 564*0Sstevel@tonic-gate static void siglvl(int, siginfo_t *, ucontext_t *); 565*0Sstevel@tonic-gate static void sigpoll(int); 566*0Sstevel@tonic-gate static void enter_maintenance(void); 567*0Sstevel@tonic-gate static void timer(int); 568*0Sstevel@tonic-gate static void userinit(int, char **); 569*0Sstevel@tonic-gate static void notify_pam_dead(struct utmpx *); 570*0Sstevel@tonic-gate static long waitproc(struct PROC_TABLE *); 571*0Sstevel@tonic-gate static struct PROC_TABLE *efork(int, struct PROC_TABLE *, int); 572*0Sstevel@tonic-gate static struct PROC_TABLE *findpslot(struct CMD_LINE *); 573*0Sstevel@tonic-gate static void increase_proc_table_size(); 574*0Sstevel@tonic-gate static void st_init(); 575*0Sstevel@tonic-gate static void st_write(); 576*0Sstevel@tonic-gate static void contracts_init(); 577*0Sstevel@tonic-gate static void contract_event(struct pollfd *); 578*0Sstevel@tonic-gate static int startd_run(const char *, int, ctid_t); 579*0Sstevel@tonic-gate static void startd_record_failure(); 580*0Sstevel@tonic-gate static int startd_failure_rate_critical(); 581*0Sstevel@tonic-gate static char *audit_boot_msg(); 582*0Sstevel@tonic-gate static int audit_put_record(int, int, char *); 583*0Sstevel@tonic-gate static void update_boot_archive(int new_state); 584*0Sstevel@tonic-gate 585*0Sstevel@tonic-gate int 586*0Sstevel@tonic-gate main(int argc, char *argv[]) 587*0Sstevel@tonic-gate { 588*0Sstevel@tonic-gate int chg_lvl_flag = FALSE, print_banner = FALSE; 589*0Sstevel@tonic-gate int may_need_audit = 1; 590*0Sstevel@tonic-gate int c; 591*0Sstevel@tonic-gate char *msg; 592*0Sstevel@tonic-gate 593*0Sstevel@tonic-gate /* Get a timestamp for use as boot time, if needed. */ 594*0Sstevel@tonic-gate (void) time(&init_boot_time); 595*0Sstevel@tonic-gate 596*0Sstevel@tonic-gate /* Get the default umask */ 597*0Sstevel@tonic-gate cmask = umask(022); 598*0Sstevel@tonic-gate (void) umask(cmask); 599*0Sstevel@tonic-gate 600*0Sstevel@tonic-gate /* Parse the arguments to init. Check for single user */ 601*0Sstevel@tonic-gate opterr = 0; 602*0Sstevel@tonic-gate while ((c = getopt(argc, argv, "brsm:")) != EOF) { 603*0Sstevel@tonic-gate switch (c) { 604*0Sstevel@tonic-gate case 'b': 605*0Sstevel@tonic-gate rflg = 0; 606*0Sstevel@tonic-gate bflg = 1; 607*0Sstevel@tonic-gate if (!sflg) 608*0Sstevel@tonic-gate sflg++; 609*0Sstevel@tonic-gate break; 610*0Sstevel@tonic-gate case 'r': 611*0Sstevel@tonic-gate bflg = 0; 612*0Sstevel@tonic-gate rflg++; 613*0Sstevel@tonic-gate break; 614*0Sstevel@tonic-gate case 's': 615*0Sstevel@tonic-gate if (!bflg) 616*0Sstevel@tonic-gate sflg++; 617*0Sstevel@tonic-gate break; 618*0Sstevel@tonic-gate case 'm': 619*0Sstevel@tonic-gate smf_options = optarg; 620*0Sstevel@tonic-gate smf_debug = (strstr(smf_options, "debug") != NULL); 621*0Sstevel@tonic-gate break; 622*0Sstevel@tonic-gate } 623*0Sstevel@tonic-gate } 624*0Sstevel@tonic-gate 625*0Sstevel@tonic-gate /* 626*0Sstevel@tonic-gate * Determine if we are the main init, or a user invoked init, whose job 627*0Sstevel@tonic-gate * it is to inform init to change levels or perform some other action. 628*0Sstevel@tonic-gate */ 629*0Sstevel@tonic-gate if (zone_getattr(getzoneid(), ZONE_ATTR_INITPID, &init_pid, 630*0Sstevel@tonic-gate sizeof (init_pid)) != sizeof (init_pid)) { 631*0Sstevel@tonic-gate (void) fprintf(stderr, "could not get pid for init\n"); 632*0Sstevel@tonic-gate return (1); 633*0Sstevel@tonic-gate } 634*0Sstevel@tonic-gate 635*0Sstevel@tonic-gate /* 636*0Sstevel@tonic-gate * If this PID is not the same as the "true" init for the zone, then we 637*0Sstevel@tonic-gate * must be in 'user' mode. 638*0Sstevel@tonic-gate */ 639*0Sstevel@tonic-gate if (getpid() != init_pid) { 640*0Sstevel@tonic-gate userinit(argc, argv); 641*0Sstevel@tonic-gate } 642*0Sstevel@tonic-gate 643*0Sstevel@tonic-gate if (getzoneid() != GLOBAL_ZONEID) { 644*0Sstevel@tonic-gate print_banner = TRUE; 645*0Sstevel@tonic-gate } 646*0Sstevel@tonic-gate 647*0Sstevel@tonic-gate /* 648*0Sstevel@tonic-gate * Initialize state (and set "booting"). 649*0Sstevel@tonic-gate */ 650*0Sstevel@tonic-gate st_init(); 651*0Sstevel@tonic-gate 652*0Sstevel@tonic-gate if (booting && print_banner) { 653*0Sstevel@tonic-gate struct utsname un; 654*0Sstevel@tonic-gate char buf[BUFSIZ], *isa; 655*0Sstevel@tonic-gate long ret; 656*0Sstevel@tonic-gate int bits = 32; 657*0Sstevel@tonic-gate 658*0Sstevel@tonic-gate /* 659*0Sstevel@tonic-gate * We want to print the boot banner as soon as 660*0Sstevel@tonic-gate * possible. In the global zone, the kernel does it, 661*0Sstevel@tonic-gate * but we do not have that luxury in non-global zones, 662*0Sstevel@tonic-gate * so we will print it here. 663*0Sstevel@tonic-gate */ 664*0Sstevel@tonic-gate (void) uname(&un); 665*0Sstevel@tonic-gate ret = sysinfo(SI_ISALIST, buf, sizeof (buf)); 666*0Sstevel@tonic-gate if (ret != -1L && ret <= sizeof (buf)) { 667*0Sstevel@tonic-gate for (isa = strtok(buf, " "); isa; 668*0Sstevel@tonic-gate isa = strtok(NULL, " ")) { 669*0Sstevel@tonic-gate if (strcmp(isa, "sparcv9") == 0 || 670*0Sstevel@tonic-gate strcmp(isa, "amd64") == 0) { 671*0Sstevel@tonic-gate bits = 64; 672*0Sstevel@tonic-gate break; 673*0Sstevel@tonic-gate } 674*0Sstevel@tonic-gate } 675*0Sstevel@tonic-gate } 676*0Sstevel@tonic-gate 677*0Sstevel@tonic-gate console(B_FALSE, 678*0Sstevel@tonic-gate "\n\n%s Release %s Version %s %d-bit\r\n", 679*0Sstevel@tonic-gate un.sysname, un.release, un.version, bits); 680*0Sstevel@tonic-gate console(B_FALSE, 681*0Sstevel@tonic-gate "Copyright 1983-2005 Sun Microsystems, Inc. " 682*0Sstevel@tonic-gate " All rights reserved.\r\n"); 683*0Sstevel@tonic-gate console(B_FALSE, 684*0Sstevel@tonic-gate "Use is subject to license terms.\r\n"); 685*0Sstevel@tonic-gate } 686*0Sstevel@tonic-gate 687*0Sstevel@tonic-gate /* 688*0Sstevel@tonic-gate * Get the ioctl settings for /dev/syscon from /etc/ioctl.syscon 689*0Sstevel@tonic-gate * so that it can be brought up in the state it was in when the 690*0Sstevel@tonic-gate * system went down; or set to defaults if ioctl.syscon isn't 691*0Sstevel@tonic-gate * valid. 692*0Sstevel@tonic-gate * 693*0Sstevel@tonic-gate * This needs to be done even if we're restarting so reset_modes() 694*0Sstevel@tonic-gate * will work in case we need to go down to single user mode. 695*0Sstevel@tonic-gate */ 696*0Sstevel@tonic-gate write_ioctl = get_ioctl_syscon(); 697*0Sstevel@tonic-gate 698*0Sstevel@tonic-gate /* 699*0Sstevel@tonic-gate * Set up all signals to be caught or ignored as appropriate. 700*0Sstevel@tonic-gate */ 701*0Sstevel@tonic-gate init_signals(); 702*0Sstevel@tonic-gate 703*0Sstevel@tonic-gate /* Load glob_envp from ENVFILE. */ 704*0Sstevel@tonic-gate init_env(); 705*0Sstevel@tonic-gate 706*0Sstevel@tonic-gate contracts_init(); 707*0Sstevel@tonic-gate 708*0Sstevel@tonic-gate if (!booting) { 709*0Sstevel@tonic-gate /* cur_state should have been read in. */ 710*0Sstevel@tonic-gate 711*0Sstevel@tonic-gate op_modes = NORMAL_MODES; 712*0Sstevel@tonic-gate 713*0Sstevel@tonic-gate /* Rewrite the ioctl file if it was bad. */ 714*0Sstevel@tonic-gate if (write_ioctl) 715*0Sstevel@tonic-gate write_ioctl_syscon(); 716*0Sstevel@tonic-gate } else { 717*0Sstevel@tonic-gate /* 718*0Sstevel@tonic-gate * It's fine to boot up with state as zero, because 719*0Sstevel@tonic-gate * startd will later tell us the real state. 720*0Sstevel@tonic-gate */ 721*0Sstevel@tonic-gate cur_state = 0; 722*0Sstevel@tonic-gate op_modes = BOOT_MODES; 723*0Sstevel@tonic-gate 724*0Sstevel@tonic-gate boot_init(); 725*0Sstevel@tonic-gate } 726*0Sstevel@tonic-gate 727*0Sstevel@tonic-gate prev_state = prior_state = cur_state; 728*0Sstevel@tonic-gate 729*0Sstevel@tonic-gate /* 730*0Sstevel@tonic-gate * Here is the beginning of the main process loop. 731*0Sstevel@tonic-gate */ 732*0Sstevel@tonic-gate for (;;) { 733*0Sstevel@tonic-gate if (Pfd < 0) 734*0Sstevel@tonic-gate setup_pipe(); 735*0Sstevel@tonic-gate 736*0Sstevel@tonic-gate /* 737*0Sstevel@tonic-gate * Clean up any accounting records for dead "godchildren". 738*0Sstevel@tonic-gate */ 739*0Sstevel@tonic-gate if (Gchild) 740*0Sstevel@tonic-gate cleanaux(); 741*0Sstevel@tonic-gate 742*0Sstevel@tonic-gate /* 743*0Sstevel@tonic-gate * If in "normal" mode, check all living processes and initiate 744*0Sstevel@tonic-gate * kill sequence on those that should not be there anymore. 745*0Sstevel@tonic-gate */ 746*0Sstevel@tonic-gate if (op_modes == NORMAL_MODES && cur_state != LVLa && 747*0Sstevel@tonic-gate cur_state != LVLb && cur_state != LVLc) 748*0Sstevel@tonic-gate remv(); 749*0Sstevel@tonic-gate 750*0Sstevel@tonic-gate /* 751*0Sstevel@tonic-gate * If a change in run levels is the reason we awoke, now do 752*0Sstevel@tonic-gate * the accounting to report the change in the utmp file. 753*0Sstevel@tonic-gate * Also report the change on the system console. 754*0Sstevel@tonic-gate */ 755*0Sstevel@tonic-gate if (chg_lvl_flag) { 756*0Sstevel@tonic-gate chg_lvl_flag = FALSE; 757*0Sstevel@tonic-gate 758*0Sstevel@tonic-gate if (state_to_flags(cur_state) & LSEL_RUNLEVEL) { 759*0Sstevel@tonic-gate char rl = state_to_name(cur_state); 760*0Sstevel@tonic-gate 761*0Sstevel@tonic-gate if (rl != -1) 762*0Sstevel@tonic-gate lscf_set_runlevel(rl); 763*0Sstevel@tonic-gate } 764*0Sstevel@tonic-gate 765*0Sstevel@tonic-gate may_need_audit = 1; 766*0Sstevel@tonic-gate } 767*0Sstevel@tonic-gate 768*0Sstevel@tonic-gate /* 769*0Sstevel@tonic-gate * Scan the inittab file and spawn and respawn processes that 770*0Sstevel@tonic-gate * should be alive in the current state. If inittab does not 771*0Sstevel@tonic-gate * exist default to single user mode. 772*0Sstevel@tonic-gate */ 773*0Sstevel@tonic-gate if (spawn_processes() == FAILURE) { 774*0Sstevel@tonic-gate prior_state = prev_state; 775*0Sstevel@tonic-gate cur_state = SINGLE_USER; 776*0Sstevel@tonic-gate } 777*0Sstevel@tonic-gate 778*0Sstevel@tonic-gate /* If any respawns occurred, take note. */ 779*0Sstevel@tonic-gate if (rsflag) { 780*0Sstevel@tonic-gate rsflag = 0; 781*0Sstevel@tonic-gate spawncnt++; 782*0Sstevel@tonic-gate } 783*0Sstevel@tonic-gate 784*0Sstevel@tonic-gate /* 785*0Sstevel@tonic-gate * If a powerfail signal was received during the last 786*0Sstevel@tonic-gate * sequence, set mode to powerfail. When spawn_processes() is 787*0Sstevel@tonic-gate * entered the first thing it does is to check "powerhit". If 788*0Sstevel@tonic-gate * it is in PF_MODES then it clears "powerhit" and does 789*0Sstevel@tonic-gate * a powerfail sequence. If it is not in PF_MODES, then it 790*0Sstevel@tonic-gate * puts itself in PF_MODES and then clears "powerhit". Should 791*0Sstevel@tonic-gate * "powerhit" get set again while spawn_processes() is working 792*0Sstevel@tonic-gate * on a powerfail sequence, the following code will see that 793*0Sstevel@tonic-gate * spawn_processes() tries to execute the powerfail sequence 794*0Sstevel@tonic-gate * again. This guarantees that the powerfail sequence will be 795*0Sstevel@tonic-gate * successfully completed before further processing takes 796*0Sstevel@tonic-gate * place. 797*0Sstevel@tonic-gate */ 798*0Sstevel@tonic-gate if (wakeup.w_flags.w_powerhit) { 799*0Sstevel@tonic-gate op_modes = PF_MODES; 800*0Sstevel@tonic-gate /* 801*0Sstevel@tonic-gate * Make sure that cur_state != prev_state so that 802*0Sstevel@tonic-gate * ONCE and WAIT types work. 803*0Sstevel@tonic-gate */ 804*0Sstevel@tonic-gate prev_state = 0; 805*0Sstevel@tonic-gate } else if (op_modes != NORMAL_MODES) { 806*0Sstevel@tonic-gate /* 807*0Sstevel@tonic-gate * If spawn_processes() was not just called while in 808*0Sstevel@tonic-gate * normal mode, we set the mode to normal and it will 809*0Sstevel@tonic-gate * be called again to check normal modes. If we have 810*0Sstevel@tonic-gate * just finished a powerfail sequence with prev_state 811*0Sstevel@tonic-gate * equal to zero, we set prev_state equal to cur_state 812*0Sstevel@tonic-gate * before the next pass through. 813*0Sstevel@tonic-gate */ 814*0Sstevel@tonic-gate if (op_modes == PF_MODES) 815*0Sstevel@tonic-gate prev_state = cur_state; 816*0Sstevel@tonic-gate op_modes = NORMAL_MODES; 817*0Sstevel@tonic-gate } else if (cur_state == LVLa || cur_state == LVLb || 818*0Sstevel@tonic-gate cur_state == LVLc) { 819*0Sstevel@tonic-gate /* 820*0Sstevel@tonic-gate * If it was a change of levels that awakened us and the 821*0Sstevel@tonic-gate * new level is one of the demand levels then reset 822*0Sstevel@tonic-gate * cur_state to the previous state and do another scan 823*0Sstevel@tonic-gate * to take care of the usual respawn actions. 824*0Sstevel@tonic-gate */ 825*0Sstevel@tonic-gate cur_state = prior_state; 826*0Sstevel@tonic-gate prior_state = prev_state; 827*0Sstevel@tonic-gate prev_state = cur_state; 828*0Sstevel@tonic-gate } else { 829*0Sstevel@tonic-gate prev_state = cur_state; 830*0Sstevel@tonic-gate 831*0Sstevel@tonic-gate if (wakeup.w_mask == 0) { 832*0Sstevel@tonic-gate int ret; 833*0Sstevel@tonic-gate 834*0Sstevel@tonic-gate if (may_need_audit && (cur_state == LVL3)) { 835*0Sstevel@tonic-gate msg = audit_boot_msg(); 836*0Sstevel@tonic-gate 837*0Sstevel@tonic-gate may_need_audit = 0; 838*0Sstevel@tonic-gate (void) audit_put_record(ADT_SUCCESS, 839*0Sstevel@tonic-gate ADT_SUCCESS, msg); 840*0Sstevel@tonic-gate free(msg); 841*0Sstevel@tonic-gate } 842*0Sstevel@tonic-gate 843*0Sstevel@tonic-gate /* 844*0Sstevel@tonic-gate * "init" is finished with all actions for 845*0Sstevel@tonic-gate * the current wakeup. 846*0Sstevel@tonic-gate */ 847*0Sstevel@tonic-gate ret = poll(poll_fds, poll_nfds, 848*0Sstevel@tonic-gate SLEEPTIME * MILLISEC); 849*0Sstevel@tonic-gate pausecnt++; 850*0Sstevel@tonic-gate if (ret > 0) 851*0Sstevel@tonic-gate contract_event(&poll_fds[0]); 852*0Sstevel@tonic-gate else if (ret < 0 && errno != EINTR) 853*0Sstevel@tonic-gate console(B_TRUE, "poll() error: %s\n", 854*0Sstevel@tonic-gate strerror(errno)); 855*0Sstevel@tonic-gate } 856*0Sstevel@tonic-gate 857*0Sstevel@tonic-gate if (wakeup.w_flags.w_usersignal) { 858*0Sstevel@tonic-gate /* 859*0Sstevel@tonic-gate * Install the new level. This could be a real 860*0Sstevel@tonic-gate * change in levels or a telinit [Q|a|b|c] or 861*0Sstevel@tonic-gate * just a telinit to the same level at which 862*0Sstevel@tonic-gate * we are running. 863*0Sstevel@tonic-gate */ 864*0Sstevel@tonic-gate if (new_state != cur_state) { 865*0Sstevel@tonic-gate if (new_state == LVLa || 866*0Sstevel@tonic-gate new_state == LVLb || 867*0Sstevel@tonic-gate new_state == LVLc) { 868*0Sstevel@tonic-gate prev_state = prior_state; 869*0Sstevel@tonic-gate prior_state = cur_state; 870*0Sstevel@tonic-gate cur_state = new_state; 871*0Sstevel@tonic-gate } else { 872*0Sstevel@tonic-gate prev_state = cur_state; 873*0Sstevel@tonic-gate if (cur_state >= 0) 874*0Sstevel@tonic-gate prior_state = cur_state; 875*0Sstevel@tonic-gate cur_state = new_state; 876*0Sstevel@tonic-gate chg_lvl_flag = TRUE; 877*0Sstevel@tonic-gate } 878*0Sstevel@tonic-gate } 879*0Sstevel@tonic-gate 880*0Sstevel@tonic-gate new_state = 0; 881*0Sstevel@tonic-gate } 882*0Sstevel@tonic-gate 883*0Sstevel@tonic-gate if (wakeup.w_flags.w_powerhit) 884*0Sstevel@tonic-gate op_modes = PF_MODES; 885*0Sstevel@tonic-gate 886*0Sstevel@tonic-gate /* 887*0Sstevel@tonic-gate * Clear all wakeup reasons. 888*0Sstevel@tonic-gate */ 889*0Sstevel@tonic-gate wakeup.w_mask = 0; 890*0Sstevel@tonic-gate } 891*0Sstevel@tonic-gate } 892*0Sstevel@tonic-gate 893*0Sstevel@tonic-gate /*NOTREACHED*/ 894*0Sstevel@tonic-gate } 895*0Sstevel@tonic-gate 896*0Sstevel@tonic-gate static void 897*0Sstevel@tonic-gate update_boot_archive(int new_state) 898*0Sstevel@tonic-gate { 899*0Sstevel@tonic-gate if (new_state != LVL0 && new_state != LVL5 && new_state != LVL6) 900*0Sstevel@tonic-gate return; 901*0Sstevel@tonic-gate 902*0Sstevel@tonic-gate if (getzoneid() != GLOBAL_ZONEID) 903*0Sstevel@tonic-gate return; 904*0Sstevel@tonic-gate 905*0Sstevel@tonic-gate (void) system("/sbin/bootadm -a update_all"); 906*0Sstevel@tonic-gate } 907*0Sstevel@tonic-gate 908*0Sstevel@tonic-gate /* 909*0Sstevel@tonic-gate * void enter_maintenance() 910*0Sstevel@tonic-gate * A simple invocation of sulogin(1M), with no baggage, in the case that we 911*0Sstevel@tonic-gate * are unable to activate svc.startd(1M). We fork; the child runs sulogin; 912*0Sstevel@tonic-gate * we wait for it to exit. 913*0Sstevel@tonic-gate */ 914*0Sstevel@tonic-gate static void 915*0Sstevel@tonic-gate enter_maintenance() 916*0Sstevel@tonic-gate { 917*0Sstevel@tonic-gate struct PROC_TABLE *su_process; 918*0Sstevel@tonic-gate 919*0Sstevel@tonic-gate console(B_FALSE, "Requesting maintenance mode\n" 920*0Sstevel@tonic-gate "(See /lib/svc/share/README for additional information.)\n"); 921*0Sstevel@tonic-gate (void) sigset(SIGCLD, SIG_DFL); 922*0Sstevel@tonic-gate while ((su_process = efork(M_OFF, NULLPROC, NOCLEANUP)) == NO_ROOM) 923*0Sstevel@tonic-gate (void) pause(); 924*0Sstevel@tonic-gate (void) sigset(SIGCLD, childeath); 925*0Sstevel@tonic-gate if (su_process == NULLPROC) { 926*0Sstevel@tonic-gate int fd; 927*0Sstevel@tonic-gate 928*0Sstevel@tonic-gate (void) fclose(stdin); 929*0Sstevel@tonic-gate (void) fclose(stdout); 930*0Sstevel@tonic-gate (void) fclose(stderr); 931*0Sstevel@tonic-gate closefrom(0); 932*0Sstevel@tonic-gate 933*0Sstevel@tonic-gate fd = open(SYSCON, O_RDWR | O_NOCTTY); 934*0Sstevel@tonic-gate if (fd >= 0) { 935*0Sstevel@tonic-gate (void) dup2(fd, 1); 936*0Sstevel@tonic-gate (void) dup2(fd, 2); 937*0Sstevel@tonic-gate } else { 938*0Sstevel@tonic-gate /* 939*0Sstevel@tonic-gate * Need to issue an error message somewhere. 940*0Sstevel@tonic-gate */ 941*0Sstevel@tonic-gate syslog(LOG_CRIT, "init[%d]: cannot open %s; %s\n", 942*0Sstevel@tonic-gate getpid(), SYSCON, strerror(errno)); 943*0Sstevel@tonic-gate } 944*0Sstevel@tonic-gate 945*0Sstevel@tonic-gate /* 946*0Sstevel@tonic-gate * Execute the "su" program. 947*0Sstevel@tonic-gate */ 948*0Sstevel@tonic-gate (void) execle(SU, SU, "-", (char *)0, glob_envp); 949*0Sstevel@tonic-gate console(B_TRUE, "execle of %s failed: %s\n", SU, 950*0Sstevel@tonic-gate strerror(errno)); 951*0Sstevel@tonic-gate timer(5); 952*0Sstevel@tonic-gate exit(1); 953*0Sstevel@tonic-gate } 954*0Sstevel@tonic-gate 955*0Sstevel@tonic-gate /* 956*0Sstevel@tonic-gate * If we are the parent, wait around for the child to die 957*0Sstevel@tonic-gate * or for "init" to be signaled to change levels. 958*0Sstevel@tonic-gate */ 959*0Sstevel@tonic-gate while (waitproc(su_process) == FAILURE) { 960*0Sstevel@tonic-gate /* 961*0Sstevel@tonic-gate * All other reasons for waking are ignored when in 962*0Sstevel@tonic-gate * single-user mode. The only child we are interested 963*0Sstevel@tonic-gate * in is being waited for explicitly by waitproc(). 964*0Sstevel@tonic-gate */ 965*0Sstevel@tonic-gate wakeup.w_mask = 0; 966*0Sstevel@tonic-gate } 967*0Sstevel@tonic-gate } 968*0Sstevel@tonic-gate 969*0Sstevel@tonic-gate /* 970*0Sstevel@tonic-gate * remv() scans through "proc_table" and performs cleanup. If 971*0Sstevel@tonic-gate * there is a process in the table, which shouldn't be here at 972*0Sstevel@tonic-gate * the current run level, then remv() kills the process. 973*0Sstevel@tonic-gate */ 974*0Sstevel@tonic-gate static void 975*0Sstevel@tonic-gate remv() 976*0Sstevel@tonic-gate { 977*0Sstevel@tonic-gate struct PROC_TABLE *process; 978*0Sstevel@tonic-gate struct CMD_LINE cmd; 979*0Sstevel@tonic-gate char cmd_string[MAXCMDL]; 980*0Sstevel@tonic-gate int change_level; 981*0Sstevel@tonic-gate 982*0Sstevel@tonic-gate change_level = (cur_state != prev_state ? TRUE : FALSE); 983*0Sstevel@tonic-gate 984*0Sstevel@tonic-gate /* 985*0Sstevel@tonic-gate * Clear the TOUCHED flag on all entries so that when we have 986*0Sstevel@tonic-gate * finished scanning inittab, we will be able to tell if we 987*0Sstevel@tonic-gate * have any processes for which there is no entry in inittab. 988*0Sstevel@tonic-gate */ 989*0Sstevel@tonic-gate for (process = proc_table; 990*0Sstevel@tonic-gate (process < proc_table + num_proc); process++) { 991*0Sstevel@tonic-gate process->p_flags &= ~TOUCHED; 992*0Sstevel@tonic-gate } 993*0Sstevel@tonic-gate 994*0Sstevel@tonic-gate /* 995*0Sstevel@tonic-gate * Scan all inittab entries. 996*0Sstevel@tonic-gate */ 997*0Sstevel@tonic-gate while (getcmd(&cmd, &cmd_string[0]) == TRUE) { 998*0Sstevel@tonic-gate /* Scan for process which goes with this entry in inittab. */ 999*0Sstevel@tonic-gate for (process = proc_table; 1000*0Sstevel@tonic-gate (process < proc_table + num_proc); process++) { 1001*0Sstevel@tonic-gate if ((process->p_flags & OCCUPIED) == 0 || 1002*0Sstevel@tonic-gate !id_eq(process->p_id, cmd.c_id)) 1003*0Sstevel@tonic-gate continue; 1004*0Sstevel@tonic-gate 1005*0Sstevel@tonic-gate /* 1006*0Sstevel@tonic-gate * This slot contains the process we are looking for. 1007*0Sstevel@tonic-gate */ 1008*0Sstevel@tonic-gate 1009*0Sstevel@tonic-gate /* 1010*0Sstevel@tonic-gate * Is the cur_state SINGLE_USER or is this process 1011*0Sstevel@tonic-gate * marked as "off" or was this proc started by some 1012*0Sstevel@tonic-gate * mechanism other than LVL{a|b|c} and the current level 1013*0Sstevel@tonic-gate * does not support this process? 1014*0Sstevel@tonic-gate */ 1015*0Sstevel@tonic-gate if (cur_state == SINGLE_USER || 1016*0Sstevel@tonic-gate cmd.c_action == M_OFF || 1017*0Sstevel@tonic-gate ((cmd.c_levels & state_to_mask(cur_state)) == 0 && 1018*0Sstevel@tonic-gate (process->p_flags & DEMANDREQUEST) == 0)) { 1019*0Sstevel@tonic-gate if (process->p_flags & LIVING) { 1020*0Sstevel@tonic-gate /* 1021*0Sstevel@tonic-gate * Touch this entry so we know we have 1022*0Sstevel@tonic-gate * treated it. Note that procs which 1023*0Sstevel@tonic-gate * are already dead at this point and 1024*0Sstevel@tonic-gate * should not be restarted are left 1025*0Sstevel@tonic-gate * untouched. This causes their slot to 1026*0Sstevel@tonic-gate * be freed later after dead accounting 1027*0Sstevel@tonic-gate * is done. 1028*0Sstevel@tonic-gate */ 1029*0Sstevel@tonic-gate process->p_flags |= TOUCHED; 1030*0Sstevel@tonic-gate 1031*0Sstevel@tonic-gate if ((process->p_flags & KILLED) == 0) { 1032*0Sstevel@tonic-gate if (change_level) { 1033*0Sstevel@tonic-gate process->p_flags 1034*0Sstevel@tonic-gate |= WARNED; 1035*0Sstevel@tonic-gate (void) kill( 1036*0Sstevel@tonic-gate process->p_pid, 1037*0Sstevel@tonic-gate SIGTERM); 1038*0Sstevel@tonic-gate } else { 1039*0Sstevel@tonic-gate /* 1040*0Sstevel@tonic-gate * Fork a killing proc 1041*0Sstevel@tonic-gate * so "init" can 1042*0Sstevel@tonic-gate * continue without 1043*0Sstevel@tonic-gate * having to pause for 1044*0Sstevel@tonic-gate * TWARN seconds. 1045*0Sstevel@tonic-gate */ 1046*0Sstevel@tonic-gate killproc( 1047*0Sstevel@tonic-gate process->p_pid); 1048*0Sstevel@tonic-gate } 1049*0Sstevel@tonic-gate process->p_flags |= KILLED; 1050*0Sstevel@tonic-gate } 1051*0Sstevel@tonic-gate } 1052*0Sstevel@tonic-gate } else { 1053*0Sstevel@tonic-gate /* 1054*0Sstevel@tonic-gate * Process can exist at current level. If it is 1055*0Sstevel@tonic-gate * still alive or a DEMANDREQUEST we touch it so 1056*0Sstevel@tonic-gate * it will be left alone. Otherwise we leave it 1057*0Sstevel@tonic-gate * untouched so it will be accounted for and 1058*0Sstevel@tonic-gate * cleaned up later in remv(). Dead 1059*0Sstevel@tonic-gate * DEMANDREQUESTs will be accounted but not 1060*0Sstevel@tonic-gate * freed. 1061*0Sstevel@tonic-gate */ 1062*0Sstevel@tonic-gate if (process->p_flags & 1063*0Sstevel@tonic-gate (LIVING|NOCLEANUP|DEMANDREQUEST)) 1064*0Sstevel@tonic-gate process->p_flags |= TOUCHED; 1065*0Sstevel@tonic-gate } 1066*0Sstevel@tonic-gate 1067*0Sstevel@tonic-gate break; 1068*0Sstevel@tonic-gate } 1069*0Sstevel@tonic-gate } 1070*0Sstevel@tonic-gate 1071*0Sstevel@tonic-gate st_write(); 1072*0Sstevel@tonic-gate 1073*0Sstevel@tonic-gate /* 1074*0Sstevel@tonic-gate * If this was a change of levels call, scan through the 1075*0Sstevel@tonic-gate * process table for processes that were warned to die. If any 1076*0Sstevel@tonic-gate * are found that haven't left yet, sleep for TWARN seconds and 1077*0Sstevel@tonic-gate * then send final terminations to any that haven't died yet. 1078*0Sstevel@tonic-gate */ 1079*0Sstevel@tonic-gate if (change_level) { 1080*0Sstevel@tonic-gate 1081*0Sstevel@tonic-gate /* 1082*0Sstevel@tonic-gate * Set the alarm for TWARN seconds on the assumption 1083*0Sstevel@tonic-gate * that there will be some that need to be waited for. 1084*0Sstevel@tonic-gate * This won't harm anything except we are guaranteed to 1085*0Sstevel@tonic-gate * wakeup in TWARN seconds whether we need to or not. 1086*0Sstevel@tonic-gate */ 1087*0Sstevel@tonic-gate setimer(TWARN); 1088*0Sstevel@tonic-gate 1089*0Sstevel@tonic-gate /* 1090*0Sstevel@tonic-gate * Scan for processes which should be dying. We hope they 1091*0Sstevel@tonic-gate * will die without having to be sent a SIGKILL signal. 1092*0Sstevel@tonic-gate */ 1093*0Sstevel@tonic-gate for (process = proc_table; 1094*0Sstevel@tonic-gate (process < proc_table + num_proc); process++) { 1095*0Sstevel@tonic-gate /* 1096*0Sstevel@tonic-gate * If this process should die, hasn't yet, and the 1097*0Sstevel@tonic-gate * TWARN time hasn't expired yet, wait for process 1098*0Sstevel@tonic-gate * to die or for timer to expire. 1099*0Sstevel@tonic-gate */ 1100*0Sstevel@tonic-gate while (time_up == FALSE && 1101*0Sstevel@tonic-gate (process->p_flags & (WARNED|LIVING|OCCUPIED)) == 1102*0Sstevel@tonic-gate (WARNED|LIVING|OCCUPIED)) 1103*0Sstevel@tonic-gate (void) pause(); 1104*0Sstevel@tonic-gate 1105*0Sstevel@tonic-gate if (time_up == TRUE) 1106*0Sstevel@tonic-gate break; 1107*0Sstevel@tonic-gate } 1108*0Sstevel@tonic-gate 1109*0Sstevel@tonic-gate /* 1110*0Sstevel@tonic-gate * If we reached the end of the table without the timer 1111*0Sstevel@tonic-gate * expiring, then there are no procs which will have to be 1112*0Sstevel@tonic-gate * sent the SIGKILL signal. If the timer has expired, then 1113*0Sstevel@tonic-gate * it is necessary to scan the table again and send signals 1114*0Sstevel@tonic-gate * to all processes which aren't going away nicely. 1115*0Sstevel@tonic-gate */ 1116*0Sstevel@tonic-gate if (time_up == TRUE) { 1117*0Sstevel@tonic-gate for (process = proc_table; 1118*0Sstevel@tonic-gate (process < proc_table + num_proc); process++) { 1119*0Sstevel@tonic-gate if ((process->p_flags & 1120*0Sstevel@tonic-gate (WARNED|LIVING|OCCUPIED)) == 1121*0Sstevel@tonic-gate (WARNED|LIVING|OCCUPIED)) 1122*0Sstevel@tonic-gate (void) kill(process->p_pid, SIGKILL); 1123*0Sstevel@tonic-gate } 1124*0Sstevel@tonic-gate } 1125*0Sstevel@tonic-gate setimer(0); 1126*0Sstevel@tonic-gate } 1127*0Sstevel@tonic-gate 1128*0Sstevel@tonic-gate /* 1129*0Sstevel@tonic-gate * Rescan the proc_table for two kinds of entry, those marked LIVING, 1130*0Sstevel@tonic-gate * NAMED, which don't have an entry in inittab (haven't been TOUCHED 1131*0Sstevel@tonic-gate * by the above scanning), and haven't been sent kill signals, and 1132*0Sstevel@tonic-gate * those entries marked not LIVING, NAMED. The former procs are killed. 1133*0Sstevel@tonic-gate * The latter have DEAD_PROCESS accounting done and the slot cleared. 1134*0Sstevel@tonic-gate */ 1135*0Sstevel@tonic-gate for (process = proc_table; 1136*0Sstevel@tonic-gate (process < proc_table + num_proc); process++) { 1137*0Sstevel@tonic-gate if ((process->p_flags & (LIVING|NAMED|TOUCHED|KILLED|OCCUPIED)) 1138*0Sstevel@tonic-gate == (LIVING|NAMED|OCCUPIED)) { 1139*0Sstevel@tonic-gate killproc(process->p_pid); 1140*0Sstevel@tonic-gate process->p_flags |= KILLED; 1141*0Sstevel@tonic-gate } else if ((process->p_flags & (LIVING|NAMED|OCCUPIED)) == 1142*0Sstevel@tonic-gate (NAMED|OCCUPIED)) { 1143*0Sstevel@tonic-gate (void) account(DEAD_PROCESS, process, NULL); 1144*0Sstevel@tonic-gate /* 1145*0Sstevel@tonic-gate * If this named proc hasn't been TOUCHED, then free the 1146*0Sstevel@tonic-gate * space. It has either died of it's own accord, but 1147*0Sstevel@tonic-gate * isn't respawnable or it was killed because it 1148*0Sstevel@tonic-gate * shouldn't exist at this level. 1149*0Sstevel@tonic-gate */ 1150*0Sstevel@tonic-gate if ((process->p_flags & TOUCHED) == 0) 1151*0Sstevel@tonic-gate process->p_flags = 0; 1152*0Sstevel@tonic-gate } 1153*0Sstevel@tonic-gate } 1154*0Sstevel@tonic-gate 1155*0Sstevel@tonic-gate st_write(); 1156*0Sstevel@tonic-gate } 1157*0Sstevel@tonic-gate 1158*0Sstevel@tonic-gate /* 1159*0Sstevel@tonic-gate * Extract the svc.startd command line and whether to restart it from its 1160*0Sstevel@tonic-gate * inittab entry. 1161*0Sstevel@tonic-gate */ 1162*0Sstevel@tonic-gate /*ARGSUSED*/ 1163*0Sstevel@tonic-gate static void 1164*0Sstevel@tonic-gate process_startd_line(struct CMD_LINE *cmd, char *cmd_string) 1165*0Sstevel@tonic-gate { 1166*0Sstevel@tonic-gate size_t sz; 1167*0Sstevel@tonic-gate 1168*0Sstevel@tonic-gate /* Save the command line. */ 1169*0Sstevel@tonic-gate if (sflg || rflg) { 1170*0Sstevel@tonic-gate /* Also append -r or -s. */ 1171*0Sstevel@tonic-gate (void) strlcpy(startd_cline, cmd_string, sizeof (startd_cline)); 1172*0Sstevel@tonic-gate (void) strlcat(startd_cline, " -", sizeof (startd_cline)); 1173*0Sstevel@tonic-gate if (sflg) 1174*0Sstevel@tonic-gate sz = strlcat(startd_cline, "s", sizeof (startd_cline)); 1175*0Sstevel@tonic-gate if (rflg) 1176*0Sstevel@tonic-gate sz = strlcat(startd_cline, "r", sizeof (startd_cline)); 1177*0Sstevel@tonic-gate } else { 1178*0Sstevel@tonic-gate sz = strlcpy(startd_cline, cmd_string, sizeof (startd_cline)); 1179*0Sstevel@tonic-gate } 1180*0Sstevel@tonic-gate 1181*0Sstevel@tonic-gate if (sz >= sizeof (startd_cline)) { 1182*0Sstevel@tonic-gate console(B_TRUE, 1183*0Sstevel@tonic-gate "svc.startd command line too long. Ignoring.\n"); 1184*0Sstevel@tonic-gate startd_cline[0] = '\0'; 1185*0Sstevel@tonic-gate return; 1186*0Sstevel@tonic-gate } 1187*0Sstevel@tonic-gate } 1188*0Sstevel@tonic-gate 1189*0Sstevel@tonic-gate /* 1190*0Sstevel@tonic-gate * spawn_processes() scans inittab for entries which should be run at this 1191*0Sstevel@tonic-gate * mode. Processes which should be running but are not, are started. 1192*0Sstevel@tonic-gate */ 1193*0Sstevel@tonic-gate static int 1194*0Sstevel@tonic-gate spawn_processes() 1195*0Sstevel@tonic-gate { 1196*0Sstevel@tonic-gate struct PROC_TABLE *pp; 1197*0Sstevel@tonic-gate struct CMD_LINE cmd; 1198*0Sstevel@tonic-gate char cmd_string[MAXCMDL]; 1199*0Sstevel@tonic-gate short lvl_mask; 1200*0Sstevel@tonic-gate int status; 1201*0Sstevel@tonic-gate 1202*0Sstevel@tonic-gate /* 1203*0Sstevel@tonic-gate * First check the "powerhit" flag. If it is set, make sure the modes 1204*0Sstevel@tonic-gate * are PF_MODES and clear the "powerhit" flag. Avoid the possible race 1205*0Sstevel@tonic-gate * on the "powerhit" flag by disallowing a new powerfail interrupt 1206*0Sstevel@tonic-gate * between the test of the powerhit flag and the clearing of it. 1207*0Sstevel@tonic-gate */ 1208*0Sstevel@tonic-gate if (wakeup.w_flags.w_powerhit) { 1209*0Sstevel@tonic-gate wakeup.w_flags.w_powerhit = 0; 1210*0Sstevel@tonic-gate op_modes = PF_MODES; 1211*0Sstevel@tonic-gate } 1212*0Sstevel@tonic-gate lvl_mask = state_to_mask(cur_state); 1213*0Sstevel@tonic-gate 1214*0Sstevel@tonic-gate /* 1215*0Sstevel@tonic-gate * Scan through all the entries in inittab. 1216*0Sstevel@tonic-gate */ 1217*0Sstevel@tonic-gate while ((status = getcmd(&cmd, &cmd_string[0])) == TRUE) { 1218*0Sstevel@tonic-gate if (id_eq(cmd.c_id, "smf")) { 1219*0Sstevel@tonic-gate process_startd_line(&cmd, cmd_string); 1220*0Sstevel@tonic-gate continue; 1221*0Sstevel@tonic-gate } 1222*0Sstevel@tonic-gate 1223*0Sstevel@tonic-gate retry_for_proc_slot: 1224*0Sstevel@tonic-gate 1225*0Sstevel@tonic-gate /* 1226*0Sstevel@tonic-gate * Find out if there is a process slot for this entry already. 1227*0Sstevel@tonic-gate */ 1228*0Sstevel@tonic-gate if ((pp = findpslot(&cmd)) == NULLPROC) { 1229*0Sstevel@tonic-gate /* 1230*0Sstevel@tonic-gate * we've run out of proc table entries 1231*0Sstevel@tonic-gate * increase proc_table. 1232*0Sstevel@tonic-gate */ 1233*0Sstevel@tonic-gate increase_proc_table_size(); 1234*0Sstevel@tonic-gate 1235*0Sstevel@tonic-gate /* 1236*0Sstevel@tonic-gate * Retry now as we have an empty proc slot. 1237*0Sstevel@tonic-gate * In case increase_proc_table_size() fails, 1238*0Sstevel@tonic-gate * we will keep retrying. 1239*0Sstevel@tonic-gate */ 1240*0Sstevel@tonic-gate goto retry_for_proc_slot; 1241*0Sstevel@tonic-gate } 1242*0Sstevel@tonic-gate 1243*0Sstevel@tonic-gate /* 1244*0Sstevel@tonic-gate * If there is an entry, and it is marked as DEMANDREQUEST, 1245*0Sstevel@tonic-gate * one of the levels a, b, or c is in its levels mask, and 1246*0Sstevel@tonic-gate * the action field is ONDEMAND and ONDEMAND is a permissable 1247*0Sstevel@tonic-gate * mode, and the process is dead, then respawn it. 1248*0Sstevel@tonic-gate */ 1249*0Sstevel@tonic-gate if (((pp->p_flags & (LIVING|DEMANDREQUEST)) == DEMANDREQUEST) && 1250*0Sstevel@tonic-gate (cmd.c_levels & MASK_abc) && 1251*0Sstevel@tonic-gate (cmd.c_action & op_modes) == M_ONDEMAND) { 1252*0Sstevel@tonic-gate spawn(pp, &cmd); 1253*0Sstevel@tonic-gate continue; 1254*0Sstevel@tonic-gate } 1255*0Sstevel@tonic-gate 1256*0Sstevel@tonic-gate /* 1257*0Sstevel@tonic-gate * If the action is not an action we are interested in, 1258*0Sstevel@tonic-gate * skip the entry. 1259*0Sstevel@tonic-gate */ 1260*0Sstevel@tonic-gate if ((cmd.c_action & op_modes) == 0 || pp->p_flags & LIVING || 1261*0Sstevel@tonic-gate (cmd.c_levels & lvl_mask) == 0) 1262*0Sstevel@tonic-gate continue; 1263*0Sstevel@tonic-gate 1264*0Sstevel@tonic-gate /* 1265*0Sstevel@tonic-gate * If the modes are the normal modes (ONCE, WAIT, RESPAWN, OFF, 1266*0Sstevel@tonic-gate * ONDEMAND) and the action field is either OFF or the action 1267*0Sstevel@tonic-gate * field is ONCE or WAIT and the current level is the same as 1268*0Sstevel@tonic-gate * the last level, then skip this entry. ONCE and WAIT only 1269*0Sstevel@tonic-gate * get run when the level changes. 1270*0Sstevel@tonic-gate */ 1271*0Sstevel@tonic-gate if (op_modes == NORMAL_MODES && 1272*0Sstevel@tonic-gate (cmd.c_action == M_OFF || 1273*0Sstevel@tonic-gate (cmd.c_action & (M_ONCE|M_WAIT)) && 1274*0Sstevel@tonic-gate cur_state == prev_state)) 1275*0Sstevel@tonic-gate continue; 1276*0Sstevel@tonic-gate 1277*0Sstevel@tonic-gate /* 1278*0Sstevel@tonic-gate * At this point we are interested in performing the action for 1279*0Sstevel@tonic-gate * this entry. Actions fall into two categories, spinning off 1280*0Sstevel@tonic-gate * a process and not waiting, and spinning off a process and 1281*0Sstevel@tonic-gate * waiting for it to die. If the action is ONCE, RESPAWN, 1282*0Sstevel@tonic-gate * ONDEMAND, POWERFAIL, or BOOT we don't wait for the process 1283*0Sstevel@tonic-gate * to die, for all other actions we do wait. 1284*0Sstevel@tonic-gate */ 1285*0Sstevel@tonic-gate if (cmd.c_action & (M_ONCE | M_RESPAWN | M_PF | M_BOOT)) { 1286*0Sstevel@tonic-gate spawn(pp, &cmd); 1287*0Sstevel@tonic-gate 1288*0Sstevel@tonic-gate } else { 1289*0Sstevel@tonic-gate spawn(pp, &cmd); 1290*0Sstevel@tonic-gate while (waitproc(pp) == FAILURE); 1291*0Sstevel@tonic-gate (void) account(DEAD_PROCESS, pp, NULL); 1292*0Sstevel@tonic-gate pp->p_flags = 0; 1293*0Sstevel@tonic-gate } 1294*0Sstevel@tonic-gate } 1295*0Sstevel@tonic-gate return (status); 1296*0Sstevel@tonic-gate } 1297*0Sstevel@tonic-gate 1298*0Sstevel@tonic-gate /* 1299*0Sstevel@tonic-gate * spawn() spawns a shell, inserts the information about the process 1300*0Sstevel@tonic-gate * process into the proc_table, and does the startup accounting. 1301*0Sstevel@tonic-gate */ 1302*0Sstevel@tonic-gate static void 1303*0Sstevel@tonic-gate spawn(struct PROC_TABLE *process, struct CMD_LINE *cmd) 1304*0Sstevel@tonic-gate { 1305*0Sstevel@tonic-gate int i; 1306*0Sstevel@tonic-gate int modes, maxfiles; 1307*0Sstevel@tonic-gate time_t now; 1308*0Sstevel@tonic-gate struct PROC_TABLE tmproc, *oprocess; 1309*0Sstevel@tonic-gate 1310*0Sstevel@tonic-gate /* 1311*0Sstevel@tonic-gate * The modes to be sent to efork() are 0 unless we are 1312*0Sstevel@tonic-gate * spawning a LVLa, LVLb, or LVLc entry or we will be 1313*0Sstevel@tonic-gate * waiting for the death of the child before continuing. 1314*0Sstevel@tonic-gate */ 1315*0Sstevel@tonic-gate modes = NAMED; 1316*0Sstevel@tonic-gate if (process->p_flags & DEMANDREQUEST || cur_state == LVLa || 1317*0Sstevel@tonic-gate cur_state == LVLb || cur_state == LVLc) 1318*0Sstevel@tonic-gate modes |= DEMANDREQUEST; 1319*0Sstevel@tonic-gate if ((cmd->c_action & (M_SYSINIT | M_WAIT | M_BOOTWAIT | M_PWAIT)) != 0) 1320*0Sstevel@tonic-gate modes |= NOCLEANUP; 1321*0Sstevel@tonic-gate 1322*0Sstevel@tonic-gate /* 1323*0Sstevel@tonic-gate * If this is a respawnable process, check the threshold 1324*0Sstevel@tonic-gate * information to avoid excessive respawns. 1325*0Sstevel@tonic-gate */ 1326*0Sstevel@tonic-gate if (cmd->c_action & M_RESPAWN) { 1327*0Sstevel@tonic-gate /* 1328*0Sstevel@tonic-gate * Add NOCLEANUP to all respawnable commands so that the 1329*0Sstevel@tonic-gate * information about the frequency of respawns isn't lost. 1330*0Sstevel@tonic-gate */ 1331*0Sstevel@tonic-gate modes |= NOCLEANUP; 1332*0Sstevel@tonic-gate (void) time(&now); 1333*0Sstevel@tonic-gate 1334*0Sstevel@tonic-gate /* 1335*0Sstevel@tonic-gate * If no time is assigned, then this is the first time 1336*0Sstevel@tonic-gate * this command is being processed in this series. Assign 1337*0Sstevel@tonic-gate * the current time. 1338*0Sstevel@tonic-gate */ 1339*0Sstevel@tonic-gate if (process->p_time == 0L) 1340*0Sstevel@tonic-gate process->p_time = now; 1341*0Sstevel@tonic-gate 1342*0Sstevel@tonic-gate if (process->p_count++ == SPAWN_LIMIT) { 1343*0Sstevel@tonic-gate 1344*0Sstevel@tonic-gate if ((now - process->p_time) < SPAWN_INTERVAL) { 1345*0Sstevel@tonic-gate /* 1346*0Sstevel@tonic-gate * Process is respawning too rapidly. Print 1347*0Sstevel@tonic-gate * message and refuse to respawn it for now. 1348*0Sstevel@tonic-gate */ 1349*0Sstevel@tonic-gate console(B_TRUE, "Command is respawning too " 1350*0Sstevel@tonic-gate "rapidly. Check for possible errors.\n" 1351*0Sstevel@tonic-gate "id:%4s \"%s\"\n", 1352*0Sstevel@tonic-gate &cmd->c_id[0], &cmd->c_command[EXEC]); 1353*0Sstevel@tonic-gate return; 1354*0Sstevel@tonic-gate } 1355*0Sstevel@tonic-gate process->p_time = now; 1356*0Sstevel@tonic-gate process->p_count = 0; 1357*0Sstevel@tonic-gate 1358*0Sstevel@tonic-gate } else if (process->p_count > SPAWN_LIMIT) { 1359*0Sstevel@tonic-gate /* 1360*0Sstevel@tonic-gate * If process has been respawning too rapidly and 1361*0Sstevel@tonic-gate * the inhibit time limit hasn't expired yet, we 1362*0Sstevel@tonic-gate * refuse to respawn. 1363*0Sstevel@tonic-gate */ 1364*0Sstevel@tonic-gate if (now - process->p_time < SPAWN_INTERVAL + INHIBIT) 1365*0Sstevel@tonic-gate return; 1366*0Sstevel@tonic-gate process->p_time = now; 1367*0Sstevel@tonic-gate process->p_count = 0; 1368*0Sstevel@tonic-gate } 1369*0Sstevel@tonic-gate rsflag = TRUE; 1370*0Sstevel@tonic-gate } 1371*0Sstevel@tonic-gate 1372*0Sstevel@tonic-gate /* 1373*0Sstevel@tonic-gate * Spawn a child process to execute this command. 1374*0Sstevel@tonic-gate */ 1375*0Sstevel@tonic-gate (void) sigset(SIGCLD, SIG_DFL); 1376*0Sstevel@tonic-gate oprocess = process; 1377*0Sstevel@tonic-gate while ((process = efork(cmd->c_action, oprocess, modes)) == NO_ROOM) 1378*0Sstevel@tonic-gate (void) pause(); 1379*0Sstevel@tonic-gate 1380*0Sstevel@tonic-gate if (process == NULLPROC) { 1381*0Sstevel@tonic-gate 1382*0Sstevel@tonic-gate /* 1383*0Sstevel@tonic-gate * We are the child. We must make sure we get a different 1384*0Sstevel@tonic-gate * file pointer for our references to utmpx. Otherwise our 1385*0Sstevel@tonic-gate * seeks and reads will compete with those of the parent. 1386*0Sstevel@tonic-gate */ 1387*0Sstevel@tonic-gate endutxent(); 1388*0Sstevel@tonic-gate 1389*0Sstevel@tonic-gate /* 1390*0Sstevel@tonic-gate * Perform the accounting for the beginning of a process. 1391*0Sstevel@tonic-gate * Note that all processes are initially "INIT_PROCESS"es. 1392*0Sstevel@tonic-gate */ 1393*0Sstevel@tonic-gate tmproc.p_id[0] = cmd->c_id[0]; 1394*0Sstevel@tonic-gate tmproc.p_id[1] = cmd->c_id[1]; 1395*0Sstevel@tonic-gate tmproc.p_id[2] = cmd->c_id[2]; 1396*0Sstevel@tonic-gate tmproc.p_id[3] = cmd->c_id[3]; 1397*0Sstevel@tonic-gate tmproc.p_pid = getpid(); 1398*0Sstevel@tonic-gate tmproc.p_exit = 0; 1399*0Sstevel@tonic-gate (void) account(INIT_PROCESS, &tmproc, 1400*0Sstevel@tonic-gate prog_name(&cmd->c_command[EXEC])); 1401*0Sstevel@tonic-gate maxfiles = ulimit(UL_GDESLIM, 0); 1402*0Sstevel@tonic-gate for (i = 0; i < maxfiles; i++) 1403*0Sstevel@tonic-gate (void) fcntl(i, F_SETFD, FD_CLOEXEC); 1404*0Sstevel@tonic-gate 1405*0Sstevel@tonic-gate /* 1406*0Sstevel@tonic-gate * Now exec a shell with the -c option and the command 1407*0Sstevel@tonic-gate * from inittab. 1408*0Sstevel@tonic-gate */ 1409*0Sstevel@tonic-gate (void) execle(SH, "INITSH", "-c", cmd->c_command, (char *)0, 1410*0Sstevel@tonic-gate glob_envp); 1411*0Sstevel@tonic-gate console(B_TRUE, "Command\n\"%s\"\n failed to execute. errno " 1412*0Sstevel@tonic-gate "= %d (exec of shell failed)\n", cmd->c_command, errno); 1413*0Sstevel@tonic-gate 1414*0Sstevel@tonic-gate /* 1415*0Sstevel@tonic-gate * Don't come back so quickly that "init" doesn't have a 1416*0Sstevel@tonic-gate * chance to finish putting this child in "proc_table". 1417*0Sstevel@tonic-gate */ 1418*0Sstevel@tonic-gate timer(20); 1419*0Sstevel@tonic-gate exit(1); 1420*0Sstevel@tonic-gate 1421*0Sstevel@tonic-gate } 1422*0Sstevel@tonic-gate 1423*0Sstevel@tonic-gate /* 1424*0Sstevel@tonic-gate * We are the parent. Insert the necessary 1425*0Sstevel@tonic-gate * information in the proc_table. 1426*0Sstevel@tonic-gate */ 1427*0Sstevel@tonic-gate process->p_id[0] = cmd->c_id[0]; 1428*0Sstevel@tonic-gate process->p_id[1] = cmd->c_id[1]; 1429*0Sstevel@tonic-gate process->p_id[2] = cmd->c_id[2]; 1430*0Sstevel@tonic-gate process->p_id[3] = cmd->c_id[3]; 1431*0Sstevel@tonic-gate 1432*0Sstevel@tonic-gate st_write(); 1433*0Sstevel@tonic-gate 1434*0Sstevel@tonic-gate (void) sigset(SIGCLD, childeath); 1435*0Sstevel@tonic-gate } 1436*0Sstevel@tonic-gate 1437*0Sstevel@tonic-gate /* 1438*0Sstevel@tonic-gate * findpslot() finds the old slot in the process table for the 1439*0Sstevel@tonic-gate * command with the same id, or it finds an empty slot. 1440*0Sstevel@tonic-gate */ 1441*0Sstevel@tonic-gate static struct PROC_TABLE * 1442*0Sstevel@tonic-gate findpslot(struct CMD_LINE *cmd) 1443*0Sstevel@tonic-gate { 1444*0Sstevel@tonic-gate struct PROC_TABLE *process; 1445*0Sstevel@tonic-gate struct PROC_TABLE *empty = NULLPROC; 1446*0Sstevel@tonic-gate 1447*0Sstevel@tonic-gate for (process = proc_table; 1448*0Sstevel@tonic-gate (process < proc_table + num_proc); process++) { 1449*0Sstevel@tonic-gate if (process->p_flags & OCCUPIED && 1450*0Sstevel@tonic-gate id_eq(process->p_id, cmd->c_id)) 1451*0Sstevel@tonic-gate break; 1452*0Sstevel@tonic-gate 1453*0Sstevel@tonic-gate /* 1454*0Sstevel@tonic-gate * If the entry is totally empty and "empty" is still 0, 1455*0Sstevel@tonic-gate * remember where this hole is and make sure the slot is 1456*0Sstevel@tonic-gate * zeroed out. 1457*0Sstevel@tonic-gate */ 1458*0Sstevel@tonic-gate if (empty == NULLPROC && (process->p_flags & OCCUPIED) == 0) { 1459*0Sstevel@tonic-gate empty = process; 1460*0Sstevel@tonic-gate process->p_id[0] = '\0'; 1461*0Sstevel@tonic-gate process->p_id[1] = '\0'; 1462*0Sstevel@tonic-gate process->p_id[2] = '\0'; 1463*0Sstevel@tonic-gate process->p_id[3] = '\0'; 1464*0Sstevel@tonic-gate process->p_pid = 0; 1465*0Sstevel@tonic-gate process->p_time = 0L; 1466*0Sstevel@tonic-gate process->p_count = 0; 1467*0Sstevel@tonic-gate process->p_flags = 0; 1468*0Sstevel@tonic-gate process->p_exit = 0; 1469*0Sstevel@tonic-gate } 1470*0Sstevel@tonic-gate } 1471*0Sstevel@tonic-gate 1472*0Sstevel@tonic-gate /* 1473*0Sstevel@tonic-gate * If there is no entry for this slot, then there should be an 1474*0Sstevel@tonic-gate * empty slot. If there is no empty slot, then we've run out 1475*0Sstevel@tonic-gate * of proc_table space. If the latter is true, empty will be 1476*0Sstevel@tonic-gate * NULL and the caller will have to complain. 1477*0Sstevel@tonic-gate */ 1478*0Sstevel@tonic-gate if (process == (proc_table + num_proc)) 1479*0Sstevel@tonic-gate process = empty; 1480*0Sstevel@tonic-gate 1481*0Sstevel@tonic-gate return (process); 1482*0Sstevel@tonic-gate } 1483*0Sstevel@tonic-gate 1484*0Sstevel@tonic-gate /* 1485*0Sstevel@tonic-gate * getcmd() parses lines from inittab. Each time it finds a command line 1486*0Sstevel@tonic-gate * it will return TRUE as well as fill the passed CMD_LINE structure and 1487*0Sstevel@tonic-gate * the shell command string. When the end of inittab is reached, FALSE 1488*0Sstevel@tonic-gate * is returned inittab is automatically opened if it is not currently open 1489*0Sstevel@tonic-gate * and is closed when the end of the file is reached. 1490*0Sstevel@tonic-gate */ 1491*0Sstevel@tonic-gate static FILE *fp_inittab = NULL; 1492*0Sstevel@tonic-gate 1493*0Sstevel@tonic-gate static int 1494*0Sstevel@tonic-gate getcmd(struct CMD_LINE *cmd, char *shcmd) 1495*0Sstevel@tonic-gate { 1496*0Sstevel@tonic-gate char *ptr; 1497*0Sstevel@tonic-gate int c, lastc, state; 1498*0Sstevel@tonic-gate char *ptr1; 1499*0Sstevel@tonic-gate int answer, i, proceed; 1500*0Sstevel@tonic-gate struct stat sbuf; 1501*0Sstevel@tonic-gate static char *actions[] = { 1502*0Sstevel@tonic-gate "off", "respawn", "ondemand", "once", "wait", "boot", 1503*0Sstevel@tonic-gate "bootwait", "powerfail", "powerwait", "initdefault", 1504*0Sstevel@tonic-gate "sysinit", 1505*0Sstevel@tonic-gate }; 1506*0Sstevel@tonic-gate static short act_masks[] = { 1507*0Sstevel@tonic-gate M_OFF, M_RESPAWN, M_ONDEMAND, M_ONCE, M_WAIT, M_BOOT, 1508*0Sstevel@tonic-gate M_BOOTWAIT, M_PF, M_PWAIT, M_INITDEFAULT, M_SYSINIT, 1509*0Sstevel@tonic-gate }; 1510*0Sstevel@tonic-gate /* 1511*0Sstevel@tonic-gate * Only these actions will be allowed for entries which 1512*0Sstevel@tonic-gate * are specified for single-user mode. 1513*0Sstevel@tonic-gate */ 1514*0Sstevel@tonic-gate short su_acts = M_INITDEFAULT | M_PF | M_PWAIT | M_WAIT; 1515*0Sstevel@tonic-gate 1516*0Sstevel@tonic-gate if (fp_inittab == NULL) { 1517*0Sstevel@tonic-gate /* 1518*0Sstevel@tonic-gate * Before attempting to open inittab we stat it to make 1519*0Sstevel@tonic-gate * sure it currently exists and is not empty. We try 1520*0Sstevel@tonic-gate * several times because someone may have temporarily 1521*0Sstevel@tonic-gate * unlinked or truncated the file. 1522*0Sstevel@tonic-gate */ 1523*0Sstevel@tonic-gate for (i = 0; i < 3; i++) { 1524*0Sstevel@tonic-gate if (stat(INITTAB, &sbuf) == -1) { 1525*0Sstevel@tonic-gate if (i == 2) { 1526*0Sstevel@tonic-gate console(B_TRUE, 1527*0Sstevel@tonic-gate "Cannot stat %s, errno: %d\n", 1528*0Sstevel@tonic-gate INITTAB, errno); 1529*0Sstevel@tonic-gate return (FAILURE); 1530*0Sstevel@tonic-gate } else { 1531*0Sstevel@tonic-gate timer(3); 1532*0Sstevel@tonic-gate } 1533*0Sstevel@tonic-gate } else if (sbuf.st_size < 10) { 1534*0Sstevel@tonic-gate if (i == 2) { 1535*0Sstevel@tonic-gate console(B_TRUE, 1536*0Sstevel@tonic-gate "%s truncated or corrupted\n", 1537*0Sstevel@tonic-gate INITTAB); 1538*0Sstevel@tonic-gate return (FAILURE); 1539*0Sstevel@tonic-gate } else { 1540*0Sstevel@tonic-gate timer(3); 1541*0Sstevel@tonic-gate } 1542*0Sstevel@tonic-gate } else { 1543*0Sstevel@tonic-gate break; 1544*0Sstevel@tonic-gate } 1545*0Sstevel@tonic-gate } 1546*0Sstevel@tonic-gate 1547*0Sstevel@tonic-gate /* 1548*0Sstevel@tonic-gate * If unable to open inittab, print error message and 1549*0Sstevel@tonic-gate * return FAILURE to caller. 1550*0Sstevel@tonic-gate */ 1551*0Sstevel@tonic-gate if ((fp_inittab = fopen(INITTAB, "r")) == NULL) { 1552*0Sstevel@tonic-gate console(B_TRUE, "Cannot open %s errno: %d\n", INITTAB, 1553*0Sstevel@tonic-gate errno); 1554*0Sstevel@tonic-gate return (FAILURE); 1555*0Sstevel@tonic-gate } 1556*0Sstevel@tonic-gate } 1557*0Sstevel@tonic-gate 1558*0Sstevel@tonic-gate /* 1559*0Sstevel@tonic-gate * Keep getting commands from inittab until you find a 1560*0Sstevel@tonic-gate * good one or run out of file. 1561*0Sstevel@tonic-gate */ 1562*0Sstevel@tonic-gate for (answer = FALSE; answer == FALSE; ) { 1563*0Sstevel@tonic-gate /* 1564*0Sstevel@tonic-gate * Zero out the cmd itself before trying next line. 1565*0Sstevel@tonic-gate */ 1566*0Sstevel@tonic-gate bzero(cmd, sizeof (struct CMD_LINE)); 1567*0Sstevel@tonic-gate 1568*0Sstevel@tonic-gate /* 1569*0Sstevel@tonic-gate * Read in lines of inittab, parsing at colons, until a line is 1570*0Sstevel@tonic-gate * read in which doesn't end with a backslash. Do not start if 1571*0Sstevel@tonic-gate * the first character read is an EOF. Note that this means 1572*0Sstevel@tonic-gate * that lines which don't end in a newline are still processed, 1573*0Sstevel@tonic-gate * since the "for" will terminate normally once started, 1574*0Sstevel@tonic-gate * regardless of whether line terminates with a newline or EOF. 1575*0Sstevel@tonic-gate */ 1576*0Sstevel@tonic-gate state = FAILURE; 1577*0Sstevel@tonic-gate if ((c = fgetc(fp_inittab)) == EOF) { 1578*0Sstevel@tonic-gate answer = FALSE; 1579*0Sstevel@tonic-gate (void) fclose(fp_inittab); 1580*0Sstevel@tonic-gate fp_inittab = NULL; 1581*0Sstevel@tonic-gate break; 1582*0Sstevel@tonic-gate } 1583*0Sstevel@tonic-gate 1584*0Sstevel@tonic-gate for (proceed = TRUE, ptr = shcmd, state = ID, lastc = '\0'; 1585*0Sstevel@tonic-gate proceed && c != EOF; 1586*0Sstevel@tonic-gate lastc = c, c = fgetc(fp_inittab)) { 1587*0Sstevel@tonic-gate /* If we're not in the FAILURE state and haven't */ 1588*0Sstevel@tonic-gate /* yet reached the shell command field, process */ 1589*0Sstevel@tonic-gate /* the line, otherwise just look for a real end */ 1590*0Sstevel@tonic-gate /* of line. */ 1591*0Sstevel@tonic-gate if (state != FAILURE && state != COMMAND) { 1592*0Sstevel@tonic-gate /* 1593*0Sstevel@tonic-gate * Squeeze out spaces and tabs. 1594*0Sstevel@tonic-gate */ 1595*0Sstevel@tonic-gate if (c == ' ' || c == '\t') 1596*0Sstevel@tonic-gate continue; 1597*0Sstevel@tonic-gate 1598*0Sstevel@tonic-gate /* 1599*0Sstevel@tonic-gate * Ignore characters in a comment, except for the \n. 1600*0Sstevel@tonic-gate */ 1601*0Sstevel@tonic-gate if (state == COMMENT) { 1602*0Sstevel@tonic-gate if (c == '\n') { 1603*0Sstevel@tonic-gate lastc = ' '; 1604*0Sstevel@tonic-gate break; 1605*0Sstevel@tonic-gate } else { 1606*0Sstevel@tonic-gate continue; 1607*0Sstevel@tonic-gate } 1608*0Sstevel@tonic-gate } 1609*0Sstevel@tonic-gate 1610*0Sstevel@tonic-gate /* 1611*0Sstevel@tonic-gate * Detect comments (lines whose first non-whitespace 1612*0Sstevel@tonic-gate * character is '#') by checking that we're at the 1613*0Sstevel@tonic-gate * beginning of a line, have seen a '#', and haven't 1614*0Sstevel@tonic-gate * yet accumulated any characters. 1615*0Sstevel@tonic-gate */ 1616*0Sstevel@tonic-gate if (state == ID && c == '#' && ptr == shcmd) { 1617*0Sstevel@tonic-gate state = COMMENT; 1618*0Sstevel@tonic-gate continue; 1619*0Sstevel@tonic-gate } 1620*0Sstevel@tonic-gate 1621*0Sstevel@tonic-gate /* 1622*0Sstevel@tonic-gate * If the character is a ':', then check the 1623*0Sstevel@tonic-gate * previous field for correctness and advance 1624*0Sstevel@tonic-gate * to the next field. 1625*0Sstevel@tonic-gate */ 1626*0Sstevel@tonic-gate if (c == ':') { 1627*0Sstevel@tonic-gate switch (state) { 1628*0Sstevel@tonic-gate 1629*0Sstevel@tonic-gate case ID : 1630*0Sstevel@tonic-gate /* 1631*0Sstevel@tonic-gate * Check to see that there are only 1632*0Sstevel@tonic-gate * 1 to 4 characters for the id. 1633*0Sstevel@tonic-gate */ 1634*0Sstevel@tonic-gate if ((i = ptr - shcmd) < 1 || i > 4) { 1635*0Sstevel@tonic-gate state = FAILURE; 1636*0Sstevel@tonic-gate } else { 1637*0Sstevel@tonic-gate bcopy(shcmd, &cmd->c_id[0], i); 1638*0Sstevel@tonic-gate ptr = shcmd; 1639*0Sstevel@tonic-gate state = LEVELS; 1640*0Sstevel@tonic-gate } 1641*0Sstevel@tonic-gate break; 1642*0Sstevel@tonic-gate 1643*0Sstevel@tonic-gate case LEVELS : 1644*0Sstevel@tonic-gate /* 1645*0Sstevel@tonic-gate * Build a mask for all the levels for 1646*0Sstevel@tonic-gate * which this command will be legal. 1647*0Sstevel@tonic-gate */ 1648*0Sstevel@tonic-gate for (cmd->c_levels = 0, ptr1 = shcmd; 1649*0Sstevel@tonic-gate ptr1 < ptr; ptr1++) { 1650*0Sstevel@tonic-gate int mask; 1651*0Sstevel@tonic-gate if (lvlname_to_mask(*ptr1, 1652*0Sstevel@tonic-gate &mask) == -1) { 1653*0Sstevel@tonic-gate state = FAILURE; 1654*0Sstevel@tonic-gate break; 1655*0Sstevel@tonic-gate } 1656*0Sstevel@tonic-gate cmd->c_levels |= mask; 1657*0Sstevel@tonic-gate } 1658*0Sstevel@tonic-gate if (state != FAILURE) { 1659*0Sstevel@tonic-gate state = ACTION; 1660*0Sstevel@tonic-gate ptr = shcmd; /* Reset the buffer */ 1661*0Sstevel@tonic-gate } 1662*0Sstevel@tonic-gate break; 1663*0Sstevel@tonic-gate 1664*0Sstevel@tonic-gate case ACTION : 1665*0Sstevel@tonic-gate /* 1666*0Sstevel@tonic-gate * Null terminate the string in shcmd buffer and 1667*0Sstevel@tonic-gate * then try to match against legal actions. If 1668*0Sstevel@tonic-gate * the field is of length 0, then the default of 1669*0Sstevel@tonic-gate * "RESPAWN" is used if the id is numeric, 1670*0Sstevel@tonic-gate * otherwise the default is "OFF". 1671*0Sstevel@tonic-gate */ 1672*0Sstevel@tonic-gate if (ptr == shcmd) { 1673*0Sstevel@tonic-gate if (isdigit(cmd->c_id[0]) && 1674*0Sstevel@tonic-gate (cmd->c_id[1] == '\0' || 1675*0Sstevel@tonic-gate isdigit(cmd->c_id[1])) && 1676*0Sstevel@tonic-gate (cmd->c_id[2] == '\0' || 1677*0Sstevel@tonic-gate isdigit(cmd->c_id[2])) && 1678*0Sstevel@tonic-gate (cmd->c_id[3] == '\0' || 1679*0Sstevel@tonic-gate isdigit(cmd->c_id[3]))) 1680*0Sstevel@tonic-gate cmd->c_action = M_RESPAWN; 1681*0Sstevel@tonic-gate else 1682*0Sstevel@tonic-gate cmd->c_action = M_OFF; 1683*0Sstevel@tonic-gate } else { 1684*0Sstevel@tonic-gate for (cmd->c_action = 0, i = 0, *ptr = '\0'; 1685*0Sstevel@tonic-gate i < sizeof (actions)/sizeof (char *); 1686*0Sstevel@tonic-gate i++) { 1687*0Sstevel@tonic-gate if (strcmp(shcmd, actions[i]) == 0) { 1688*0Sstevel@tonic-gate if ((cmd->c_levels & MASKSU) && 1689*0Sstevel@tonic-gate !(act_masks[i] & su_acts)) 1690*0Sstevel@tonic-gate cmd->c_action = 0; 1691*0Sstevel@tonic-gate else 1692*0Sstevel@tonic-gate cmd->c_action = act_masks[i]; 1693*0Sstevel@tonic-gate break; 1694*0Sstevel@tonic-gate } 1695*0Sstevel@tonic-gate } 1696*0Sstevel@tonic-gate } 1697*0Sstevel@tonic-gate 1698*0Sstevel@tonic-gate /* 1699*0Sstevel@tonic-gate * If the action didn't match any legal action, 1700*0Sstevel@tonic-gate * set state to FAILURE. 1701*0Sstevel@tonic-gate */ 1702*0Sstevel@tonic-gate if (cmd->c_action == 0) { 1703*0Sstevel@tonic-gate state = FAILURE; 1704*0Sstevel@tonic-gate } else { 1705*0Sstevel@tonic-gate state = COMMAND; 1706*0Sstevel@tonic-gate (void) strcpy(shcmd, "exec "); 1707*0Sstevel@tonic-gate } 1708*0Sstevel@tonic-gate ptr = shcmd + EXEC; 1709*0Sstevel@tonic-gate break; 1710*0Sstevel@tonic-gate } 1711*0Sstevel@tonic-gate continue; 1712*0Sstevel@tonic-gate } 1713*0Sstevel@tonic-gate } 1714*0Sstevel@tonic-gate 1715*0Sstevel@tonic-gate /* If the character is a '\n', then this is the end of a */ 1716*0Sstevel@tonic-gate /* line. If the '\n' wasn't preceded by a backslash, */ 1717*0Sstevel@tonic-gate /* it is also the end of an inittab command. If it was */ 1718*0Sstevel@tonic-gate /* preceded by a backslash then the next line is a */ 1719*0Sstevel@tonic-gate /* continuation. Note that the continuation '\n' falls */ 1720*0Sstevel@tonic-gate /* through and is treated like other characters and is */ 1721*0Sstevel@tonic-gate /* stored in the shell command line. */ 1722*0Sstevel@tonic-gate if (c == '\n' && lastc != '\\') { 1723*0Sstevel@tonic-gate proceed = FALSE; 1724*0Sstevel@tonic-gate *ptr = '\0'; 1725*0Sstevel@tonic-gate break; 1726*0Sstevel@tonic-gate } 1727*0Sstevel@tonic-gate 1728*0Sstevel@tonic-gate /* For all other characters just stuff them into the */ 1729*0Sstevel@tonic-gate /* command as long as there aren't too many of them. */ 1730*0Sstevel@tonic-gate /* Make sure there is room for a terminating '\0' also. */ 1731*0Sstevel@tonic-gate if (ptr >= shcmd + MAXCMDL - 1) 1732*0Sstevel@tonic-gate state = FAILURE; 1733*0Sstevel@tonic-gate else 1734*0Sstevel@tonic-gate *ptr++ = (char)c; 1735*0Sstevel@tonic-gate 1736*0Sstevel@tonic-gate /* If the character we just stored was a quoted */ 1737*0Sstevel@tonic-gate /* backslash, then change "c" to '\0', so that this */ 1738*0Sstevel@tonic-gate /* backslash will not cause a subsequent '\n' to appear */ 1739*0Sstevel@tonic-gate /* quoted. In otherwords '\' '\' '\n' is the real end */ 1740*0Sstevel@tonic-gate /* of a command, while '\' '\n' is a continuation. */ 1741*0Sstevel@tonic-gate if (c == '\\' && lastc == '\\') 1742*0Sstevel@tonic-gate c = '\0'; 1743*0Sstevel@tonic-gate } 1744*0Sstevel@tonic-gate 1745*0Sstevel@tonic-gate /* 1746*0Sstevel@tonic-gate * Make sure all the fields are properly specified 1747*0Sstevel@tonic-gate * for a good command line. 1748*0Sstevel@tonic-gate */ 1749*0Sstevel@tonic-gate if (state == COMMAND) { 1750*0Sstevel@tonic-gate answer = TRUE; 1751*0Sstevel@tonic-gate cmd->c_command = shcmd; 1752*0Sstevel@tonic-gate 1753*0Sstevel@tonic-gate /* 1754*0Sstevel@tonic-gate * If no default level was supplied, insert 1755*0Sstevel@tonic-gate * all numerical levels. 1756*0Sstevel@tonic-gate */ 1757*0Sstevel@tonic-gate if (cmd->c_levels == 0) 1758*0Sstevel@tonic-gate cmd->c_levels = MASK_NUMERIC; 1759*0Sstevel@tonic-gate 1760*0Sstevel@tonic-gate /* 1761*0Sstevel@tonic-gate * If no action has been supplied, declare this 1762*0Sstevel@tonic-gate * entry to be OFF. 1763*0Sstevel@tonic-gate */ 1764*0Sstevel@tonic-gate if (cmd->c_action == 0) 1765*0Sstevel@tonic-gate cmd->c_action = M_OFF; 1766*0Sstevel@tonic-gate 1767*0Sstevel@tonic-gate /* 1768*0Sstevel@tonic-gate * If no shell command has been supplied, make sure 1769*0Sstevel@tonic-gate * there is a null string in the command field. 1770*0Sstevel@tonic-gate */ 1771*0Sstevel@tonic-gate if (ptr == shcmd + EXEC) 1772*0Sstevel@tonic-gate *shcmd = '\0'; 1773*0Sstevel@tonic-gate } else 1774*0Sstevel@tonic-gate answer = FALSE; 1775*0Sstevel@tonic-gate 1776*0Sstevel@tonic-gate /* 1777*0Sstevel@tonic-gate * If we have reached the end of inittab, then close it 1778*0Sstevel@tonic-gate * and quit trying to find a good command line. 1779*0Sstevel@tonic-gate */ 1780*0Sstevel@tonic-gate if (c == EOF) { 1781*0Sstevel@tonic-gate (void) fclose(fp_inittab); 1782*0Sstevel@tonic-gate fp_inittab = NULL; 1783*0Sstevel@tonic-gate break; 1784*0Sstevel@tonic-gate } 1785*0Sstevel@tonic-gate } 1786*0Sstevel@tonic-gate return (answer); 1787*0Sstevel@tonic-gate } 1788*0Sstevel@tonic-gate 1789*0Sstevel@tonic-gate /* 1790*0Sstevel@tonic-gate * lvlname_to_state(): convert the character name of a state to its level 1791*0Sstevel@tonic-gate * (its corresponding signal number). 1792*0Sstevel@tonic-gate */ 1793*0Sstevel@tonic-gate static int 1794*0Sstevel@tonic-gate lvlname_to_state(char name) 1795*0Sstevel@tonic-gate { 1796*0Sstevel@tonic-gate int i; 1797*0Sstevel@tonic-gate for (i = 0; i < LVL_NELEMS; i++) { 1798*0Sstevel@tonic-gate if (lvls[i].lvl_name == name) 1799*0Sstevel@tonic-gate return (lvls[i].lvl_state); 1800*0Sstevel@tonic-gate } 1801*0Sstevel@tonic-gate return (-1); 1802*0Sstevel@tonic-gate } 1803*0Sstevel@tonic-gate 1804*0Sstevel@tonic-gate /* 1805*0Sstevel@tonic-gate * state_to_name(): convert the level to the character name. 1806*0Sstevel@tonic-gate */ 1807*0Sstevel@tonic-gate static char 1808*0Sstevel@tonic-gate state_to_name(int state) 1809*0Sstevel@tonic-gate { 1810*0Sstevel@tonic-gate int i; 1811*0Sstevel@tonic-gate for (i = 0; i < LVL_NELEMS; i++) { 1812*0Sstevel@tonic-gate if (lvls[i].lvl_state == state) 1813*0Sstevel@tonic-gate return (lvls[i].lvl_name); 1814*0Sstevel@tonic-gate } 1815*0Sstevel@tonic-gate return (-1); 1816*0Sstevel@tonic-gate } 1817*0Sstevel@tonic-gate 1818*0Sstevel@tonic-gate /* 1819*0Sstevel@tonic-gate * state_to_mask(): return the mask corresponding to a signal number 1820*0Sstevel@tonic-gate */ 1821*0Sstevel@tonic-gate static int 1822*0Sstevel@tonic-gate state_to_mask(int state) 1823*0Sstevel@tonic-gate { 1824*0Sstevel@tonic-gate int i; 1825*0Sstevel@tonic-gate for (i = 0; i < LVL_NELEMS; i++) { 1826*0Sstevel@tonic-gate if (lvls[i].lvl_state == state) 1827*0Sstevel@tonic-gate return (lvls[i].lvl_mask); 1828*0Sstevel@tonic-gate } 1829*0Sstevel@tonic-gate return (0); /* return 0, since that represents an empty mask */ 1830*0Sstevel@tonic-gate } 1831*0Sstevel@tonic-gate 1832*0Sstevel@tonic-gate /* 1833*0Sstevel@tonic-gate * lvlname_to_mask(): return the mask corresponding to a levels character name 1834*0Sstevel@tonic-gate */ 1835*0Sstevel@tonic-gate static int 1836*0Sstevel@tonic-gate lvlname_to_mask(char name, int *mask) 1837*0Sstevel@tonic-gate { 1838*0Sstevel@tonic-gate int i; 1839*0Sstevel@tonic-gate for (i = 0; i < LVL_NELEMS; i++) { 1840*0Sstevel@tonic-gate if (lvls[i].lvl_name == name) { 1841*0Sstevel@tonic-gate *mask = lvls[i].lvl_mask; 1842*0Sstevel@tonic-gate return (0); 1843*0Sstevel@tonic-gate } 1844*0Sstevel@tonic-gate } 1845*0Sstevel@tonic-gate return (-1); 1846*0Sstevel@tonic-gate } 1847*0Sstevel@tonic-gate 1848*0Sstevel@tonic-gate /* 1849*0Sstevel@tonic-gate * state_to_flags(): return the flags corresponding to a runlevel. These 1850*0Sstevel@tonic-gate * indicate properties of that runlevel. 1851*0Sstevel@tonic-gate */ 1852*0Sstevel@tonic-gate static int 1853*0Sstevel@tonic-gate state_to_flags(int state) 1854*0Sstevel@tonic-gate { 1855*0Sstevel@tonic-gate int i; 1856*0Sstevel@tonic-gate for (i = 0; i < LVL_NELEMS; i++) { 1857*0Sstevel@tonic-gate if (lvls[i].lvl_state == state) 1858*0Sstevel@tonic-gate return (lvls[i].lvl_flags); 1859*0Sstevel@tonic-gate } 1860*0Sstevel@tonic-gate return (0); 1861*0Sstevel@tonic-gate } 1862*0Sstevel@tonic-gate 1863*0Sstevel@tonic-gate /* 1864*0Sstevel@tonic-gate * killproc() creates a child which kills the process specified by pid. 1865*0Sstevel@tonic-gate */ 1866*0Sstevel@tonic-gate void 1867*0Sstevel@tonic-gate killproc(pid_t pid) 1868*0Sstevel@tonic-gate { 1869*0Sstevel@tonic-gate struct PROC_TABLE *process; 1870*0Sstevel@tonic-gate 1871*0Sstevel@tonic-gate (void) sigset(SIGCLD, SIG_DFL); 1872*0Sstevel@tonic-gate while ((process = efork(M_OFF, NULLPROC, 0)) == NO_ROOM) 1873*0Sstevel@tonic-gate (void) pause(); 1874*0Sstevel@tonic-gate (void) sigset(SIGCLD, childeath); 1875*0Sstevel@tonic-gate 1876*0Sstevel@tonic-gate if (process == NULLPROC) { 1877*0Sstevel@tonic-gate /* 1878*0Sstevel@tonic-gate * efork() sets all signal handlers to the default, so reset 1879*0Sstevel@tonic-gate * the ALRM handler to make timer() work as expected. 1880*0Sstevel@tonic-gate */ 1881*0Sstevel@tonic-gate (void) sigset(SIGALRM, alarmclk); 1882*0Sstevel@tonic-gate 1883*0Sstevel@tonic-gate /* 1884*0Sstevel@tonic-gate * We are the child. Try to terminate the process nicely 1885*0Sstevel@tonic-gate * first using SIGTERM and if it refuses to die in TWARN 1886*0Sstevel@tonic-gate * seconds kill it with SIGKILL. 1887*0Sstevel@tonic-gate */ 1888*0Sstevel@tonic-gate (void) kill(pid, SIGTERM); 1889*0Sstevel@tonic-gate (void) timer(TWARN); 1890*0Sstevel@tonic-gate (void) kill(pid, SIGKILL); 1891*0Sstevel@tonic-gate (void) exit(0); 1892*0Sstevel@tonic-gate } 1893*0Sstevel@tonic-gate } 1894*0Sstevel@tonic-gate 1895*0Sstevel@tonic-gate /* 1896*0Sstevel@tonic-gate * Set up the default environment for all procs to be forked from init. 1897*0Sstevel@tonic-gate * Read the values from the /etc/default/init file, except for PATH. If 1898*0Sstevel@tonic-gate * there's not enough room in the environment array, the environment 1899*0Sstevel@tonic-gate * lines that don't fit are silently discarded. 1900*0Sstevel@tonic-gate */ 1901*0Sstevel@tonic-gate void 1902*0Sstevel@tonic-gate init_env() 1903*0Sstevel@tonic-gate { 1904*0Sstevel@tonic-gate char line[MAXCMDL]; 1905*0Sstevel@tonic-gate FILE *fp; 1906*0Sstevel@tonic-gate int inquotes, length, wslength; 1907*0Sstevel@tonic-gate char *tokp, *cp1, *cp2; 1908*0Sstevel@tonic-gate 1909*0Sstevel@tonic-gate glob_envp[0] = malloc((unsigned)(strlen(DEF_PATH)+2)); 1910*0Sstevel@tonic-gate (void) strcpy(glob_envp[0], DEF_PATH); 1911*0Sstevel@tonic-gate glob_envn = 1; 1912*0Sstevel@tonic-gate 1913*0Sstevel@tonic-gate if (rflg) { 1914*0Sstevel@tonic-gate glob_envp[1] = 1915*0Sstevel@tonic-gate malloc((unsigned)(strlen("_DVFS_RECONFIG=YES")+2)); 1916*0Sstevel@tonic-gate (void) strcpy(glob_envp[1], "_DVFS_RECONFIG=YES"); 1917*0Sstevel@tonic-gate ++glob_envn; 1918*0Sstevel@tonic-gate } else if (bflg == 1) { 1919*0Sstevel@tonic-gate glob_envp[1] = 1920*0Sstevel@tonic-gate malloc((unsigned)(strlen("RB_NOBOOTRC=YES")+2)); 1921*0Sstevel@tonic-gate (void) strcpy(glob_envp[1], "RB_NOBOOTRC=YES"); 1922*0Sstevel@tonic-gate ++glob_envn; 1923*0Sstevel@tonic-gate } 1924*0Sstevel@tonic-gate 1925*0Sstevel@tonic-gate if ((fp = fopen(ENVFILE, "r")) == NULL) { 1926*0Sstevel@tonic-gate console(B_TRUE, 1927*0Sstevel@tonic-gate "Cannot open %s. Environment not initialized.\n", 1928*0Sstevel@tonic-gate ENVFILE); 1929*0Sstevel@tonic-gate } else { 1930*0Sstevel@tonic-gate while (fgets(line, MAXCMDL - 1, fp) != NULL && 1931*0Sstevel@tonic-gate glob_envn < MAXENVENT - 2) { 1932*0Sstevel@tonic-gate /* 1933*0Sstevel@tonic-gate * Toss newline 1934*0Sstevel@tonic-gate */ 1935*0Sstevel@tonic-gate length = strlen(line); 1936*0Sstevel@tonic-gate if (line[length - 1] == '\n') 1937*0Sstevel@tonic-gate line[length - 1] = '\0'; 1938*0Sstevel@tonic-gate 1939*0Sstevel@tonic-gate /* 1940*0Sstevel@tonic-gate * Ignore blank or comment lines. 1941*0Sstevel@tonic-gate */ 1942*0Sstevel@tonic-gate if (line[0] == '#' || line[0] == '\0' || 1943*0Sstevel@tonic-gate (wslength = strspn(line, " \t\n")) == 1944*0Sstevel@tonic-gate strlen(line) || 1945*0Sstevel@tonic-gate strchr(line, '#') == line + wslength) 1946*0Sstevel@tonic-gate continue; 1947*0Sstevel@tonic-gate 1948*0Sstevel@tonic-gate /* 1949*0Sstevel@tonic-gate * First make a pass through the line and change 1950*0Sstevel@tonic-gate * any non-quoted semi-colons to blanks so they 1951*0Sstevel@tonic-gate * will be treated as token separators below. 1952*0Sstevel@tonic-gate */ 1953*0Sstevel@tonic-gate inquotes = 0; 1954*0Sstevel@tonic-gate for (cp1 = line; *cp1 != '\0'; cp1++) { 1955*0Sstevel@tonic-gate if (*cp1 == '"') { 1956*0Sstevel@tonic-gate if (inquotes == 0) 1957*0Sstevel@tonic-gate inquotes = 1; 1958*0Sstevel@tonic-gate else 1959*0Sstevel@tonic-gate inquotes = 0; 1960*0Sstevel@tonic-gate } else if (*cp1 == ';') { 1961*0Sstevel@tonic-gate if (inquotes == 0) 1962*0Sstevel@tonic-gate *cp1 = ' '; 1963*0Sstevel@tonic-gate } 1964*0Sstevel@tonic-gate } 1965*0Sstevel@tonic-gate 1966*0Sstevel@tonic-gate /* 1967*0Sstevel@tonic-gate * Tokens within the line are separated by blanks 1968*0Sstevel@tonic-gate * and tabs. For each token in the line which 1969*0Sstevel@tonic-gate * contains a '=' we strip out any quotes and then 1970*0Sstevel@tonic-gate * stick the token in the environment array. 1971*0Sstevel@tonic-gate */ 1972*0Sstevel@tonic-gate if ((tokp = strtok(line, " \t")) == NULL) 1973*0Sstevel@tonic-gate continue; 1974*0Sstevel@tonic-gate do { 1975*0Sstevel@tonic-gate if (strchr(tokp, '=') == NULL) 1976*0Sstevel@tonic-gate continue; 1977*0Sstevel@tonic-gate length = strlen(tokp); 1978*0Sstevel@tonic-gate while ((cp1 = strpbrk(tokp, "\"\'")) != NULL) { 1979*0Sstevel@tonic-gate for (cp2 = cp1; 1980*0Sstevel@tonic-gate cp2 < &tokp[length]; cp2++) 1981*0Sstevel@tonic-gate *cp2 = *(cp2 + 1); 1982*0Sstevel@tonic-gate length--; 1983*0Sstevel@tonic-gate } 1984*0Sstevel@tonic-gate 1985*0Sstevel@tonic-gate if (strncmp(tokp, "CMASK=", 1986*0Sstevel@tonic-gate sizeof ("CMASK=") - 1) == 0) { 1987*0Sstevel@tonic-gate long t; 1988*0Sstevel@tonic-gate 1989*0Sstevel@tonic-gate /* We know there's an = */ 1990*0Sstevel@tonic-gate t = strtol(strchr(tokp, '=') + 1, NULL, 1991*0Sstevel@tonic-gate 8); 1992*0Sstevel@tonic-gate 1993*0Sstevel@tonic-gate /* Sanity */ 1994*0Sstevel@tonic-gate if (t <= 077 && t >= 0) 1995*0Sstevel@tonic-gate cmask = (int)t; 1996*0Sstevel@tonic-gate (void) umask(cmask); 1997*0Sstevel@tonic-gate continue; 1998*0Sstevel@tonic-gate } 1999*0Sstevel@tonic-gate glob_envp[glob_envn] = 2000*0Sstevel@tonic-gate malloc((unsigned)(length + 1)); 2001*0Sstevel@tonic-gate (void) strcpy(glob_envp[glob_envn], tokp); 2002*0Sstevel@tonic-gate if (++glob_envn >= MAXENVENT - 1) 2003*0Sstevel@tonic-gate break; 2004*0Sstevel@tonic-gate } while ((tokp = strtok(NULL, " \t")) != NULL); 2005*0Sstevel@tonic-gate } 2006*0Sstevel@tonic-gate 2007*0Sstevel@tonic-gate /* 2008*0Sstevel@tonic-gate * Append a null pointer to the environment array 2009*0Sstevel@tonic-gate * to mark its end. 2010*0Sstevel@tonic-gate */ 2011*0Sstevel@tonic-gate glob_envp[glob_envn] = NULL; 2012*0Sstevel@tonic-gate (void) fclose(fp); 2013*0Sstevel@tonic-gate } 2014*0Sstevel@tonic-gate } 2015*0Sstevel@tonic-gate 2016*0Sstevel@tonic-gate /* 2017*0Sstevel@tonic-gate * boot_init(): Do initialization things that should be done at boot. 2018*0Sstevel@tonic-gate */ 2019*0Sstevel@tonic-gate void 2020*0Sstevel@tonic-gate boot_init() 2021*0Sstevel@tonic-gate { 2022*0Sstevel@tonic-gate int i; 2023*0Sstevel@tonic-gate struct PROC_TABLE *process, *oprocess; 2024*0Sstevel@tonic-gate struct CMD_LINE cmd; 2025*0Sstevel@tonic-gate char line[MAXCMDL]; 2026*0Sstevel@tonic-gate char *old_path; 2027*0Sstevel@tonic-gate int maxfiles; 2028*0Sstevel@tonic-gate 2029*0Sstevel@tonic-gate /* Use INIT_PATH for sysinit cmds */ 2030*0Sstevel@tonic-gate old_path = glob_envp[0]; 2031*0Sstevel@tonic-gate glob_envp[0] = malloc((unsigned)(strlen(INIT_PATH)+2)); 2032*0Sstevel@tonic-gate (void) strcpy(glob_envp[0], INIT_PATH); 2033*0Sstevel@tonic-gate 2034*0Sstevel@tonic-gate /* 2035*0Sstevel@tonic-gate * Scan inittab(4) and process the special svc.startd entry, initdefault 2036*0Sstevel@tonic-gate * and sysinit entries. 2037*0Sstevel@tonic-gate */ 2038*0Sstevel@tonic-gate while (getcmd(&cmd, &line[0]) == TRUE) { 2039*0Sstevel@tonic-gate if (startd_tmpl >= 0 && id_eq(cmd.c_id, "smf")) 2040*0Sstevel@tonic-gate process_startd_line(&cmd, line); 2041*0Sstevel@tonic-gate else if (cmd.c_action == M_INITDEFAULT) { 2042*0Sstevel@tonic-gate /* 2043*0Sstevel@tonic-gate * initdefault is no longer meaningful, as the SMF 2044*0Sstevel@tonic-gate * milestone controls what (legacy) run level we 2045*0Sstevel@tonic-gate * boot to. 2046*0Sstevel@tonic-gate */ 2047*0Sstevel@tonic-gate console(B_TRUE, 2048*0Sstevel@tonic-gate "Ignoring legacy \"initdefault\" entry.\n"); 2049*0Sstevel@tonic-gate } else if (cmd.c_action == M_SYSINIT) { 2050*0Sstevel@tonic-gate /* 2051*0Sstevel@tonic-gate * Execute the "sysinit" entry and wait for it to 2052*0Sstevel@tonic-gate * complete. No bookkeeping is performed on these 2053*0Sstevel@tonic-gate * entries because we avoid writing to the file system 2054*0Sstevel@tonic-gate * until after there has been an chance to check it. 2055*0Sstevel@tonic-gate */ 2056*0Sstevel@tonic-gate if (process = findpslot(&cmd)) { 2057*0Sstevel@tonic-gate (void) sigset(SIGCLD, SIG_DFL); 2058*0Sstevel@tonic-gate 2059*0Sstevel@tonic-gate for (oprocess = process; 2060*0Sstevel@tonic-gate (process = efork(M_OFF, oprocess, 2061*0Sstevel@tonic-gate (NAMED|NOCLEANUP))) == NO_ROOM; 2062*0Sstevel@tonic-gate /* CSTYLED */) 2063*0Sstevel@tonic-gate ; 2064*0Sstevel@tonic-gate (void) sigset(SIGCLD, childeath); 2065*0Sstevel@tonic-gate 2066*0Sstevel@tonic-gate if (process == NULLPROC) { 2067*0Sstevel@tonic-gate maxfiles = ulimit(UL_GDESLIM, 0); 2068*0Sstevel@tonic-gate 2069*0Sstevel@tonic-gate for (i = 0; i < maxfiles; i++) 2070*0Sstevel@tonic-gate (void) fcntl(i, F_SETFD, 2071*0Sstevel@tonic-gate FD_CLOEXEC); 2072*0Sstevel@tonic-gate (void) execle(SH, "INITSH", "-c", 2073*0Sstevel@tonic-gate cmd.c_command, 2074*0Sstevel@tonic-gate (char *)0, glob_envp); 2075*0Sstevel@tonic-gate console(B_TRUE, 2076*0Sstevel@tonic-gate "Command\n\"%s\"\n failed to execute. errno = %d (exec of shell failed)\n", 2077*0Sstevel@tonic-gate cmd.c_command, errno); 2078*0Sstevel@tonic-gate exit(1); 2079*0Sstevel@tonic-gate } else while (waitproc(process) == FAILURE); 2080*0Sstevel@tonic-gate process->p_flags = 0; 2081*0Sstevel@tonic-gate st_write(); 2082*0Sstevel@tonic-gate } 2083*0Sstevel@tonic-gate } 2084*0Sstevel@tonic-gate } 2085*0Sstevel@tonic-gate 2086*0Sstevel@tonic-gate /* Restore the path. */ 2087*0Sstevel@tonic-gate free(glob_envp[0]); 2088*0Sstevel@tonic-gate glob_envp[0] = old_path; 2089*0Sstevel@tonic-gate 2090*0Sstevel@tonic-gate /* 2091*0Sstevel@tonic-gate * This will enable st_write() to complain about init_state_file. 2092*0Sstevel@tonic-gate */ 2093*0Sstevel@tonic-gate booting = 0; 2094*0Sstevel@tonic-gate 2095*0Sstevel@tonic-gate /* 2096*0Sstevel@tonic-gate * If the /etc/ioctl.syscon didn't exist or had invalid contents write 2097*0Sstevel@tonic-gate * out a correct version. 2098*0Sstevel@tonic-gate */ 2099*0Sstevel@tonic-gate if (write_ioctl) 2100*0Sstevel@tonic-gate write_ioctl_syscon(); 2101*0Sstevel@tonic-gate 2102*0Sstevel@tonic-gate /* 2103*0Sstevel@tonic-gate * Start svc.startd(1M), which does most of the work. 2104*0Sstevel@tonic-gate */ 2105*0Sstevel@tonic-gate if (startd_cline[0] != '\0' && startd_tmpl >= 0) { 2106*0Sstevel@tonic-gate /* Start svc.startd. */ 2107*0Sstevel@tonic-gate if (startd_run(startd_cline, startd_tmpl, 0) == -1) 2108*0Sstevel@tonic-gate cur_state = SINGLE_USER; 2109*0Sstevel@tonic-gate } else { 2110*0Sstevel@tonic-gate console(B_TRUE, "Absent svc.startd entry or bad " 2111*0Sstevel@tonic-gate "contract template. Not starting svc.startd.\n"); 2112*0Sstevel@tonic-gate enter_maintenance(); 2113*0Sstevel@tonic-gate } 2114*0Sstevel@tonic-gate } 2115*0Sstevel@tonic-gate 2116*0Sstevel@tonic-gate /* 2117*0Sstevel@tonic-gate * init_signals(): Initialize all signals to either be caught or ignored. 2118*0Sstevel@tonic-gate */ 2119*0Sstevel@tonic-gate void 2120*0Sstevel@tonic-gate init_signals(void) 2121*0Sstevel@tonic-gate { 2122*0Sstevel@tonic-gate struct sigaction act; 2123*0Sstevel@tonic-gate int i; 2124*0Sstevel@tonic-gate 2125*0Sstevel@tonic-gate /* 2126*0Sstevel@tonic-gate * Start by ignoring all signals, then selectively re-enable some. 2127*0Sstevel@tonic-gate * The SIG_IGN disposition will only affect asynchronous signals: 2128*0Sstevel@tonic-gate * any signal that we trigger synchronously that doesn't end up 2129*0Sstevel@tonic-gate * being handled by siglvl() will be forcibly delivered by the kernel. 2130*0Sstevel@tonic-gate */ 2131*0Sstevel@tonic-gate for (i = SIGHUP; i <= SIGRTMAX; i++) 2132*0Sstevel@tonic-gate (void) sigset(i, SIG_IGN); 2133*0Sstevel@tonic-gate 2134*0Sstevel@tonic-gate /* 2135*0Sstevel@tonic-gate * Handle all level-changing signals using siglvl() and set sa_mask so 2136*0Sstevel@tonic-gate * that all level-changing signals are blocked while in siglvl(). 2137*0Sstevel@tonic-gate */ 2138*0Sstevel@tonic-gate act.sa_handler = siglvl; 2139*0Sstevel@tonic-gate act.sa_flags = SA_SIGINFO; 2140*0Sstevel@tonic-gate (void) sigemptyset(&act.sa_mask); 2141*0Sstevel@tonic-gate 2142*0Sstevel@tonic-gate (void) sigaddset(&act.sa_mask, LVLQ); 2143*0Sstevel@tonic-gate (void) sigaddset(&act.sa_mask, LVL0); 2144*0Sstevel@tonic-gate (void) sigaddset(&act.sa_mask, LVL1); 2145*0Sstevel@tonic-gate (void) sigaddset(&act.sa_mask, LVL2); 2146*0Sstevel@tonic-gate (void) sigaddset(&act.sa_mask, LVL3); 2147*0Sstevel@tonic-gate (void) sigaddset(&act.sa_mask, LVL4); 2148*0Sstevel@tonic-gate (void) sigaddset(&act.sa_mask, LVL5); 2149*0Sstevel@tonic-gate (void) sigaddset(&act.sa_mask, LVL6); 2150*0Sstevel@tonic-gate (void) sigaddset(&act.sa_mask, SINGLE_USER); 2151*0Sstevel@tonic-gate (void) sigaddset(&act.sa_mask, LVLa); 2152*0Sstevel@tonic-gate (void) sigaddset(&act.sa_mask, LVLb); 2153*0Sstevel@tonic-gate (void) sigaddset(&act.sa_mask, LVLc); 2154*0Sstevel@tonic-gate 2155*0Sstevel@tonic-gate (void) sigaction(LVLQ, &act, NULL); 2156*0Sstevel@tonic-gate (void) sigaction(LVL0, &act, NULL); 2157*0Sstevel@tonic-gate (void) sigaction(LVL1, &act, NULL); 2158*0Sstevel@tonic-gate (void) sigaction(LVL2, &act, NULL); 2159*0Sstevel@tonic-gate (void) sigaction(LVL3, &act, NULL); 2160*0Sstevel@tonic-gate (void) sigaction(LVL4, &act, NULL); 2161*0Sstevel@tonic-gate (void) sigaction(LVL5, &act, NULL); 2162*0Sstevel@tonic-gate (void) sigaction(LVL6, &act, NULL); 2163*0Sstevel@tonic-gate (void) sigaction(SINGLE_USER, &act, NULL); 2164*0Sstevel@tonic-gate (void) sigaction(LVLa, &act, NULL); 2165*0Sstevel@tonic-gate (void) sigaction(LVLb, &act, NULL); 2166*0Sstevel@tonic-gate (void) sigaction(LVLc, &act, NULL); 2167*0Sstevel@tonic-gate 2168*0Sstevel@tonic-gate (void) sigset(SIGALRM, alarmclk); 2169*0Sstevel@tonic-gate alarmclk(); 2170*0Sstevel@tonic-gate 2171*0Sstevel@tonic-gate (void) sigset(SIGCLD, childeath); 2172*0Sstevel@tonic-gate (void) sigset(SIGPWR, powerfail); 2173*0Sstevel@tonic-gate } 2174*0Sstevel@tonic-gate 2175*0Sstevel@tonic-gate /* 2176*0Sstevel@tonic-gate * Set up pipe for "godchildren". If the file exists and is a pipe just open 2177*0Sstevel@tonic-gate * it. Else, if the file system is r/w create it. Otherwise, defer its 2178*0Sstevel@tonic-gate * creation and open until after the sysinit functions have had a chance to 2179*0Sstevel@tonic-gate * make the root read/write. 2180*0Sstevel@tonic-gate */ 2181*0Sstevel@tonic-gate void 2182*0Sstevel@tonic-gate setup_pipe() 2183*0Sstevel@tonic-gate { 2184*0Sstevel@tonic-gate struct stat stat_buf; 2185*0Sstevel@tonic-gate struct statvfs statvfs_buf; 2186*0Sstevel@tonic-gate 2187*0Sstevel@tonic-gate if ((stat(INITPIPE, &stat_buf) == 0) && 2188*0Sstevel@tonic-gate ((stat_buf.st_mode & (S_IFMT|S_IRUSR)) == (S_IFIFO|S_IRUSR))) 2189*0Sstevel@tonic-gate Pfd = open(INITPIPE, O_RDWR | O_NDELAY); 2190*0Sstevel@tonic-gate else 2191*0Sstevel@tonic-gate if ((statvfs(INITPIPE_DIR, &statvfs_buf) == 0) && 2192*0Sstevel@tonic-gate ((statvfs_buf.f_flag & ST_RDONLY) == 0)) { 2193*0Sstevel@tonic-gate (void) unlink(INITPIPE); 2194*0Sstevel@tonic-gate (void) mknod(INITPIPE, S_IFIFO | 0600, 0); 2195*0Sstevel@tonic-gate Pfd = open(INITPIPE, O_RDWR | O_NDELAY); 2196*0Sstevel@tonic-gate } 2197*0Sstevel@tonic-gate 2198*0Sstevel@tonic-gate if (Pfd >= 0) { 2199*0Sstevel@tonic-gate (void) ioctl(Pfd, I_SETSIG, S_INPUT); 2200*0Sstevel@tonic-gate /* 2201*0Sstevel@tonic-gate * Read pipe in message discard mode. 2202*0Sstevel@tonic-gate */ 2203*0Sstevel@tonic-gate (void) ioctl(Pfd, I_SRDOPT, RMSGD); 2204*0Sstevel@tonic-gate (void) sigset(SIGPOLL, sigpoll); 2205*0Sstevel@tonic-gate } 2206*0Sstevel@tonic-gate } 2207*0Sstevel@tonic-gate 2208*0Sstevel@tonic-gate /* 2209*0Sstevel@tonic-gate * siglvl - handle an asynchronous signal from init(1M) telling us that we 2210*0Sstevel@tonic-gate * should change the current run level. We set new_state accordingly. 2211*0Sstevel@tonic-gate */ 2212*0Sstevel@tonic-gate void 2213*0Sstevel@tonic-gate siglvl(int sig, siginfo_t *sip, ucontext_t *ucp) 2214*0Sstevel@tonic-gate { 2215*0Sstevel@tonic-gate struct PROC_TABLE *process; 2216*0Sstevel@tonic-gate struct sigaction act; 2217*0Sstevel@tonic-gate 2218*0Sstevel@tonic-gate /* 2219*0Sstevel@tonic-gate * If the signal was from the kernel (rather than init(1M)) then init 2220*0Sstevel@tonic-gate * itself tripped the signal. That is, we might have a bug and tripped 2221*0Sstevel@tonic-gate * a real SIGSEGV instead of receiving it as an alias for SIGLVLa. In 2222*0Sstevel@tonic-gate * such a case we reset the disposition to SIG_DFL, block all signals 2223*0Sstevel@tonic-gate * in uc_mask but the current one, and return to the interrupted ucp 2224*0Sstevel@tonic-gate * to effect an appropriate death. The kernel will then restart us. 2225*0Sstevel@tonic-gate * 2226*0Sstevel@tonic-gate * The one exception to SI_FROMKERNEL() is SIGFPE (a.k.a. LVL6), which 2227*0Sstevel@tonic-gate * the kernel can send us when it wants to effect an orderly reboot. 2228*0Sstevel@tonic-gate * For this case we must also verify si_code is zero, rather than a 2229*0Sstevel@tonic-gate * code such as FPE_INTDIV which a bug might have triggered. 2230*0Sstevel@tonic-gate */ 2231*0Sstevel@tonic-gate if (sip != NULL && SI_FROMKERNEL(sip) && 2232*0Sstevel@tonic-gate (sig != SIGFPE || sip->si_code == 0)) { 2233*0Sstevel@tonic-gate 2234*0Sstevel@tonic-gate (void) sigemptyset(&act.sa_mask); 2235*0Sstevel@tonic-gate act.sa_handler = SIG_DFL; 2236*0Sstevel@tonic-gate act.sa_flags = 0; 2237*0Sstevel@tonic-gate (void) sigaction(sig, &act, NULL); 2238*0Sstevel@tonic-gate 2239*0Sstevel@tonic-gate (void) sigfillset(&ucp->uc_sigmask); 2240*0Sstevel@tonic-gate (void) sigdelset(&ucp->uc_sigmask, sig); 2241*0Sstevel@tonic-gate ucp->uc_flags |= UC_SIGMASK; 2242*0Sstevel@tonic-gate 2243*0Sstevel@tonic-gate (void) setcontext(ucp); 2244*0Sstevel@tonic-gate } 2245*0Sstevel@tonic-gate 2246*0Sstevel@tonic-gate /* 2247*0Sstevel@tonic-gate * If the signal received is a LVLQ signal, do not really 2248*0Sstevel@tonic-gate * change levels, just restate the current level. If the 2249*0Sstevel@tonic-gate * signal is not a LVLQ, set the new level to the signal 2250*0Sstevel@tonic-gate * received. 2251*0Sstevel@tonic-gate */ 2252*0Sstevel@tonic-gate if (sig == LVLQ) 2253*0Sstevel@tonic-gate new_state = cur_state; 2254*0Sstevel@tonic-gate else 2255*0Sstevel@tonic-gate new_state = sig; 2256*0Sstevel@tonic-gate 2257*0Sstevel@tonic-gate /* 2258*0Sstevel@tonic-gate * Clear all times and repeat counts in the process table 2259*0Sstevel@tonic-gate * since either the level is changing or the user has editted 2260*0Sstevel@tonic-gate * the inittab file and wants us to look at it again. 2261*0Sstevel@tonic-gate * If the user has fixed a typo, we don't want residual timing 2262*0Sstevel@tonic-gate * data preventing the fixed command line from executing. 2263*0Sstevel@tonic-gate */ 2264*0Sstevel@tonic-gate for (process = proc_table; 2265*0Sstevel@tonic-gate (process < proc_table + num_proc); process++) { 2266*0Sstevel@tonic-gate process->p_time = 0L; 2267*0Sstevel@tonic-gate process->p_count = 0; 2268*0Sstevel@tonic-gate } 2269*0Sstevel@tonic-gate 2270*0Sstevel@tonic-gate /* 2271*0Sstevel@tonic-gate * Set the flag to indicate that a "user signal" was received. 2272*0Sstevel@tonic-gate */ 2273*0Sstevel@tonic-gate wakeup.w_flags.w_usersignal = 1; 2274*0Sstevel@tonic-gate } 2275*0Sstevel@tonic-gate 2276*0Sstevel@tonic-gate 2277*0Sstevel@tonic-gate /* 2278*0Sstevel@tonic-gate * alarmclk 2279*0Sstevel@tonic-gate */ 2280*0Sstevel@tonic-gate static void 2281*0Sstevel@tonic-gate alarmclk() 2282*0Sstevel@tonic-gate { 2283*0Sstevel@tonic-gate time_up = TRUE; 2284*0Sstevel@tonic-gate } 2285*0Sstevel@tonic-gate 2286*0Sstevel@tonic-gate /* 2287*0Sstevel@tonic-gate * childeath_single(): 2288*0Sstevel@tonic-gate * 2289*0Sstevel@tonic-gate * This used to be the SIGCLD handler and it was set with signal() 2290*0Sstevel@tonic-gate * (as opposed to sigset()). When a child exited we'd come to the 2291*0Sstevel@tonic-gate * handler, wait for the child, and reenable the handler with 2292*0Sstevel@tonic-gate * signal() just before returning. The implementation of signal() 2293*0Sstevel@tonic-gate * checks with waitid() for waitable children and sends a SIGCLD 2294*0Sstevel@tonic-gate * if there are some. If children are exiting faster than the 2295*0Sstevel@tonic-gate * handler can run we keep sending signals and the handler never 2296*0Sstevel@tonic-gate * gets to return and eventually the stack runs out and init dies. 2297*0Sstevel@tonic-gate * To prevent that we set the handler with sigset() so the handler 2298*0Sstevel@tonic-gate * doesn't need to be reset, and in childeath() (see below) we 2299*0Sstevel@tonic-gate * call childeath_single() as long as there are children to be 2300*0Sstevel@tonic-gate * waited for. If a child exits while init is in the handler a 2301*0Sstevel@tonic-gate * SIGCLD will be pending and delivered on return from the handler. 2302*0Sstevel@tonic-gate * If the child was already waited for the handler will have nothing 2303*0Sstevel@tonic-gate * to do and return, otherwise the child will be waited for. 2304*0Sstevel@tonic-gate */ 2305*0Sstevel@tonic-gate static void 2306*0Sstevel@tonic-gate childeath_single() 2307*0Sstevel@tonic-gate { 2308*0Sstevel@tonic-gate struct PROC_TABLE *process; 2309*0Sstevel@tonic-gate struct pidlist *pp; 2310*0Sstevel@tonic-gate pid_t pid; 2311*0Sstevel@tonic-gate int status; 2312*0Sstevel@tonic-gate 2313*0Sstevel@tonic-gate /* 2314*0Sstevel@tonic-gate * Perform wait to get the process id of the child that died and 2315*0Sstevel@tonic-gate * then scan the process table to see if we are interested in 2316*0Sstevel@tonic-gate * this process. NOTE: if a super-user sends the SIGCLD signal 2317*0Sstevel@tonic-gate * to init, the following wait will not immediately return and 2318*0Sstevel@tonic-gate * init will be inoperative until one of its child really does die. 2319*0Sstevel@tonic-gate */ 2320*0Sstevel@tonic-gate pid = wait(&status); 2321*0Sstevel@tonic-gate 2322*0Sstevel@tonic-gate for (process = proc_table; 2323*0Sstevel@tonic-gate (process < proc_table + num_proc); process++) { 2324*0Sstevel@tonic-gate if ((process->p_flags & (LIVING|OCCUPIED)) == 2325*0Sstevel@tonic-gate (LIVING|OCCUPIED) && process->p_pid == pid) { 2326*0Sstevel@tonic-gate 2327*0Sstevel@tonic-gate /* 2328*0Sstevel@tonic-gate * Mark this process as having died and store the exit 2329*0Sstevel@tonic-gate * status. Also set the wakeup flag for a dead child 2330*0Sstevel@tonic-gate * and break out of the loop. 2331*0Sstevel@tonic-gate */ 2332*0Sstevel@tonic-gate process->p_flags &= ~LIVING; 2333*0Sstevel@tonic-gate process->p_exit = (short)status; 2334*0Sstevel@tonic-gate wakeup.w_flags.w_childdeath = 1; 2335*0Sstevel@tonic-gate 2336*0Sstevel@tonic-gate return; 2337*0Sstevel@tonic-gate } 2338*0Sstevel@tonic-gate } 2339*0Sstevel@tonic-gate 2340*0Sstevel@tonic-gate /* 2341*0Sstevel@tonic-gate * No process was found above, look through auxiliary list. 2342*0Sstevel@tonic-gate */ 2343*0Sstevel@tonic-gate (void) sighold(SIGPOLL); 2344*0Sstevel@tonic-gate pp = Plhead; 2345*0Sstevel@tonic-gate while (pp) { 2346*0Sstevel@tonic-gate if (pid > pp->pl_pid) { 2347*0Sstevel@tonic-gate /* 2348*0Sstevel@tonic-gate * Keep on looking. 2349*0Sstevel@tonic-gate */ 2350*0Sstevel@tonic-gate pp = pp->pl_next; 2351*0Sstevel@tonic-gate continue; 2352*0Sstevel@tonic-gate } else if (pid < pp->pl_pid) { 2353*0Sstevel@tonic-gate /* 2354*0Sstevel@tonic-gate * Not in the list. 2355*0Sstevel@tonic-gate */ 2356*0Sstevel@tonic-gate break; 2357*0Sstevel@tonic-gate } else { 2358*0Sstevel@tonic-gate /* 2359*0Sstevel@tonic-gate * This is a dead "godchild". 2360*0Sstevel@tonic-gate */ 2361*0Sstevel@tonic-gate pp->pl_dflag = 1; 2362*0Sstevel@tonic-gate pp->pl_exit = (short)status; 2363*0Sstevel@tonic-gate wakeup.w_flags.w_childdeath = 1; 2364*0Sstevel@tonic-gate Gchild = 1; /* Notice to call cleanaux(). */ 2365*0Sstevel@tonic-gate break; 2366*0Sstevel@tonic-gate } 2367*0Sstevel@tonic-gate } 2368*0Sstevel@tonic-gate 2369*0Sstevel@tonic-gate (void) sigrelse(SIGPOLL); 2370*0Sstevel@tonic-gate } 2371*0Sstevel@tonic-gate 2372*0Sstevel@tonic-gate /* ARGSUSED */ 2373*0Sstevel@tonic-gate static void 2374*0Sstevel@tonic-gate childeath(int signo) 2375*0Sstevel@tonic-gate { 2376*0Sstevel@tonic-gate siginfo_t info; 2377*0Sstevel@tonic-gate 2378*0Sstevel@tonic-gate while ((waitid(P_ALL, (id_t)0, &info, WEXITED|WNOHANG|WNOWAIT) == 0) && 2379*0Sstevel@tonic-gate info.si_pid != 0) 2380*0Sstevel@tonic-gate childeath_single(); 2381*0Sstevel@tonic-gate } 2382*0Sstevel@tonic-gate 2383*0Sstevel@tonic-gate static void 2384*0Sstevel@tonic-gate powerfail() 2385*0Sstevel@tonic-gate { 2386*0Sstevel@tonic-gate (void) nice(-19); 2387*0Sstevel@tonic-gate wakeup.w_flags.w_powerhit = 1; 2388*0Sstevel@tonic-gate } 2389*0Sstevel@tonic-gate 2390*0Sstevel@tonic-gate /* 2391*0Sstevel@tonic-gate * efork() forks a child and the parent inserts the process in its table 2392*0Sstevel@tonic-gate * of processes that are directly a result of forks that it has performed. 2393*0Sstevel@tonic-gate * The child just changes the "global" with the process id for this process 2394*0Sstevel@tonic-gate * to it's new value. 2395*0Sstevel@tonic-gate * If efork() is called with a pointer into the proc_table it uses that slot, 2396*0Sstevel@tonic-gate * otherwise it searches for a free slot. Regardless of how it was called, 2397*0Sstevel@tonic-gate * it returns the pointer to the proc_table entry 2398*0Sstevel@tonic-gate * 2399*0Sstevel@tonic-gate * The SIGCLD handler is set to default (SIG_DFL) before calling efork(). 2400*0Sstevel@tonic-gate * This relies on the somewhat obscure SVR2 SIGCLD/SIG_DFL semantic 2401*0Sstevel@tonic-gate * implied by the use of signal(3c). While the meaning of SIG_DFL for 2402*0Sstevel@tonic-gate * SIGCLD is nominally to ignore the signal, once the signal disposition 2403*0Sstevel@tonic-gate * is set to childeath(), the kernel will post a SIGCLD if a child 2404*0Sstevel@tonic-gate * exited during the period the disposition was SIG_DFL. It acts more 2405*0Sstevel@tonic-gate * like a signal block. 2406*0Sstevel@tonic-gate * 2407*0Sstevel@tonic-gate * Ideally, this should be rewritten to use modern signal semantics. 2408*0Sstevel@tonic-gate */ 2409*0Sstevel@tonic-gate static struct PROC_TABLE * 2410*0Sstevel@tonic-gate efork(int action, struct PROC_TABLE *process, int modes) 2411*0Sstevel@tonic-gate { 2412*0Sstevel@tonic-gate pid_t childpid; 2413*0Sstevel@tonic-gate struct PROC_TABLE *proc; 2414*0Sstevel@tonic-gate int i; 2415*0Sstevel@tonic-gate void (*oldroutine)(); 2416*0Sstevel@tonic-gate /* 2417*0Sstevel@tonic-gate * Freshen up the proc_table, removing any entries for dead processes 2418*0Sstevel@tonic-gate * that don't have NOCLEANUP set. Perform the necessary accounting. 2419*0Sstevel@tonic-gate */ 2420*0Sstevel@tonic-gate for (proc = proc_table; (proc < proc_table + num_proc); proc++) { 2421*0Sstevel@tonic-gate if ((proc->p_flags & (OCCUPIED|LIVING|NOCLEANUP)) == 2422*0Sstevel@tonic-gate (OCCUPIED)) { 2423*0Sstevel@tonic-gate /* 2424*0Sstevel@tonic-gate * Is this a named process? 2425*0Sstevel@tonic-gate * If so, do the necessary bookkeeping. 2426*0Sstevel@tonic-gate */ 2427*0Sstevel@tonic-gate if (proc->p_flags & NAMED) 2428*0Sstevel@tonic-gate (void) account(DEAD_PROCESS, proc, NULL); 2429*0Sstevel@tonic-gate 2430*0Sstevel@tonic-gate /* 2431*0Sstevel@tonic-gate * Free this entry for new usage. 2432*0Sstevel@tonic-gate */ 2433*0Sstevel@tonic-gate proc->p_flags = 0; 2434*0Sstevel@tonic-gate } 2435*0Sstevel@tonic-gate } 2436*0Sstevel@tonic-gate 2437*0Sstevel@tonic-gate while ((childpid = fork()) == FAILURE) { 2438*0Sstevel@tonic-gate /* 2439*0Sstevel@tonic-gate * Shorten the alarm timer in case someone else's child dies 2440*0Sstevel@tonic-gate * and free up a slot in the process table. 2441*0Sstevel@tonic-gate */ 2442*0Sstevel@tonic-gate setimer(5); 2443*0Sstevel@tonic-gate 2444*0Sstevel@tonic-gate /* 2445*0Sstevel@tonic-gate * Wait for some children to die. Since efork() is normally 2446*0Sstevel@tonic-gate * called with SIGCLD in the default state, reset it to catch 2447*0Sstevel@tonic-gate * so that child death signals can come in. 2448*0Sstevel@tonic-gate */ 2449*0Sstevel@tonic-gate oldroutine = sigset(SIGCLD, childeath); 2450*0Sstevel@tonic-gate (void) pause(); 2451*0Sstevel@tonic-gate (void) sigset(SIGCLD, oldroutine); 2452*0Sstevel@tonic-gate setimer(0); 2453*0Sstevel@tonic-gate } 2454*0Sstevel@tonic-gate 2455*0Sstevel@tonic-gate if (childpid != 0) { 2456*0Sstevel@tonic-gate 2457*0Sstevel@tonic-gate if (process == NULLPROC) { 2458*0Sstevel@tonic-gate /* 2459*0Sstevel@tonic-gate * No proc table pointer specified so search 2460*0Sstevel@tonic-gate * for a free slot. 2461*0Sstevel@tonic-gate */ 2462*0Sstevel@tonic-gate for (process = proc_table; process->p_flags != 0 && 2463*0Sstevel@tonic-gate (process < proc_table + num_proc); process++) 2464*0Sstevel@tonic-gate ; 2465*0Sstevel@tonic-gate 2466*0Sstevel@tonic-gate if (process == (proc_table + num_proc)) { 2467*0Sstevel@tonic-gate int old_proc_table_size = num_proc; 2468*0Sstevel@tonic-gate 2469*0Sstevel@tonic-gate /* Increase the process table size */ 2470*0Sstevel@tonic-gate increase_proc_table_size(); 2471*0Sstevel@tonic-gate if (old_proc_table_size == num_proc) { 2472*0Sstevel@tonic-gate /* didn't grow: memory failure */ 2473*0Sstevel@tonic-gate return (NO_ROOM); 2474*0Sstevel@tonic-gate } else { 2475*0Sstevel@tonic-gate process = 2476*0Sstevel@tonic-gate proc_table + old_proc_table_size; 2477*0Sstevel@tonic-gate } 2478*0Sstevel@tonic-gate } 2479*0Sstevel@tonic-gate 2480*0Sstevel@tonic-gate process->p_time = 0L; 2481*0Sstevel@tonic-gate process->p_count = 0; 2482*0Sstevel@tonic-gate } 2483*0Sstevel@tonic-gate process->p_id[0] = '\0'; 2484*0Sstevel@tonic-gate process->p_id[1] = '\0'; 2485*0Sstevel@tonic-gate process->p_id[2] = '\0'; 2486*0Sstevel@tonic-gate process->p_id[3] = '\0'; 2487*0Sstevel@tonic-gate process->p_pid = childpid; 2488*0Sstevel@tonic-gate process->p_flags = (LIVING | OCCUPIED | modes); 2489*0Sstevel@tonic-gate process->p_exit = 0; 2490*0Sstevel@tonic-gate 2491*0Sstevel@tonic-gate st_write(); 2492*0Sstevel@tonic-gate } else { 2493*0Sstevel@tonic-gate if ((action & (M_WAIT | M_BOOTWAIT)) == 0) 2494*0Sstevel@tonic-gate (void) setpgrp(); 2495*0Sstevel@tonic-gate 2496*0Sstevel@tonic-gate process = NULLPROC; 2497*0Sstevel@tonic-gate 2498*0Sstevel@tonic-gate /* 2499*0Sstevel@tonic-gate * Reset all signals to the system defaults. 2500*0Sstevel@tonic-gate */ 2501*0Sstevel@tonic-gate for (i = SIGHUP; i <= SIGRTMAX; i++) 2502*0Sstevel@tonic-gate (void) sigset(i, SIG_DFL); 2503*0Sstevel@tonic-gate 2504*0Sstevel@tonic-gate /* 2505*0Sstevel@tonic-gate * POSIX B.2.2.2 advises that init should set SIGTTOU, 2506*0Sstevel@tonic-gate * SIGTTIN, and SIGTSTP to SIG_IGN. 2507*0Sstevel@tonic-gate * 2508*0Sstevel@tonic-gate * Make sure that SIGXCPU and SIGXFSZ also remain ignored, 2509*0Sstevel@tonic-gate * for backward compatibility. 2510*0Sstevel@tonic-gate */ 2511*0Sstevel@tonic-gate (void) sigset(SIGTTIN, SIG_IGN); 2512*0Sstevel@tonic-gate (void) sigset(SIGTTOU, SIG_IGN); 2513*0Sstevel@tonic-gate (void) sigset(SIGTSTP, SIG_IGN); 2514*0Sstevel@tonic-gate (void) sigset(SIGXCPU, SIG_IGN); 2515*0Sstevel@tonic-gate (void) sigset(SIGXFSZ, SIG_IGN); 2516*0Sstevel@tonic-gate } 2517*0Sstevel@tonic-gate return (process); 2518*0Sstevel@tonic-gate } 2519*0Sstevel@tonic-gate 2520*0Sstevel@tonic-gate 2521*0Sstevel@tonic-gate /* 2522*0Sstevel@tonic-gate * waitproc() waits for a specified process to die. For this function to 2523*0Sstevel@tonic-gate * work, the specified process must already in the proc_table. waitproc() 2524*0Sstevel@tonic-gate * returns the exit status of the specified process when it dies. 2525*0Sstevel@tonic-gate */ 2526*0Sstevel@tonic-gate static long 2527*0Sstevel@tonic-gate waitproc(struct PROC_TABLE *process) 2528*0Sstevel@tonic-gate { 2529*0Sstevel@tonic-gate int answer; 2530*0Sstevel@tonic-gate sigset_t oldmask, newmask, zeromask; 2531*0Sstevel@tonic-gate 2532*0Sstevel@tonic-gate (void) sigemptyset(&zeromask); 2533*0Sstevel@tonic-gate (void) sigemptyset(&newmask); 2534*0Sstevel@tonic-gate 2535*0Sstevel@tonic-gate (void) sigaddset(&newmask, SIGCLD); 2536*0Sstevel@tonic-gate 2537*0Sstevel@tonic-gate /* Block SIGCLD and save the current signal mask */ 2538*0Sstevel@tonic-gate if (sigprocmask(SIG_BLOCK, &newmask, &oldmask) < 0) 2539*0Sstevel@tonic-gate perror("SIG_BLOCK error"); 2540*0Sstevel@tonic-gate 2541*0Sstevel@tonic-gate /* 2542*0Sstevel@tonic-gate * Wait around until the process dies. 2543*0Sstevel@tonic-gate */ 2544*0Sstevel@tonic-gate if (process->p_flags & LIVING) 2545*0Sstevel@tonic-gate (void) sigsuspend(&zeromask); 2546*0Sstevel@tonic-gate 2547*0Sstevel@tonic-gate /* Reset signal mask to unblock SIGCLD */ 2548*0Sstevel@tonic-gate if (sigprocmask(SIG_SETMASK, &oldmask, NULL) < 0) 2549*0Sstevel@tonic-gate perror("SIG_SETMASK error"); 2550*0Sstevel@tonic-gate 2551*0Sstevel@tonic-gate if (process->p_flags & LIVING) 2552*0Sstevel@tonic-gate return (FAILURE); 2553*0Sstevel@tonic-gate 2554*0Sstevel@tonic-gate /* 2555*0Sstevel@tonic-gate * Make sure to only return 16 bits so that answer will always 2556*0Sstevel@tonic-gate * be positive whenever the process of interest really died. 2557*0Sstevel@tonic-gate */ 2558*0Sstevel@tonic-gate answer = (process->p_exit & 0xffff); 2559*0Sstevel@tonic-gate 2560*0Sstevel@tonic-gate /* 2561*0Sstevel@tonic-gate * Free the slot in the proc_table. 2562*0Sstevel@tonic-gate */ 2563*0Sstevel@tonic-gate process->p_flags = 0; 2564*0Sstevel@tonic-gate return (answer); 2565*0Sstevel@tonic-gate } 2566*0Sstevel@tonic-gate 2567*0Sstevel@tonic-gate /* 2568*0Sstevel@tonic-gate * notify_pam_dead(): calls into the PAM framework to close the given session. 2569*0Sstevel@tonic-gate */ 2570*0Sstevel@tonic-gate static void 2571*0Sstevel@tonic-gate notify_pam_dead(struct utmpx *up) 2572*0Sstevel@tonic-gate { 2573*0Sstevel@tonic-gate pam_handle_t *pamh; 2574*0Sstevel@tonic-gate char user[sizeof (up->ut_user) + 1]; 2575*0Sstevel@tonic-gate char ttyn[sizeof (up->ut_line) + 1]; 2576*0Sstevel@tonic-gate char host[sizeof (up->ut_host) + 1]; 2577*0Sstevel@tonic-gate 2578*0Sstevel@tonic-gate /* 2579*0Sstevel@tonic-gate * PAM does not take care of updating utmpx/wtmpx. 2580*0Sstevel@tonic-gate */ 2581*0Sstevel@tonic-gate (void) snprintf(user, sizeof (user), "%s", up->ut_user); 2582*0Sstevel@tonic-gate (void) snprintf(ttyn, sizeof (ttyn), "%s", up->ut_line); 2583*0Sstevel@tonic-gate (void) snprintf(host, sizeof (host), "%s", up->ut_host); 2584*0Sstevel@tonic-gate 2585*0Sstevel@tonic-gate if (pam_start("init", user, NULL, &pamh) == PAM_SUCCESS) { 2586*0Sstevel@tonic-gate (void) pam_set_item(pamh, PAM_TTY, ttyn); 2587*0Sstevel@tonic-gate (void) pam_set_item(pamh, PAM_RHOST, host); 2588*0Sstevel@tonic-gate (void) pam_close_session(pamh, 0); 2589*0Sstevel@tonic-gate (void) pam_end(pamh, PAM_SUCCESS); 2590*0Sstevel@tonic-gate } 2591*0Sstevel@tonic-gate } 2592*0Sstevel@tonic-gate 2593*0Sstevel@tonic-gate /* 2594*0Sstevel@tonic-gate * Check you can access utmpx (As / may be read-only and 2595*0Sstevel@tonic-gate * /var may not be mounted yet). 2596*0Sstevel@tonic-gate */ 2597*0Sstevel@tonic-gate static int 2598*0Sstevel@tonic-gate access_utmpx(void) 2599*0Sstevel@tonic-gate { 2600*0Sstevel@tonic-gate do { 2601*0Sstevel@tonic-gate utmpx_ok = (access(UTMPX, R_OK|W_OK) == 0); 2602*0Sstevel@tonic-gate } while (!utmpx_ok && errno == EINTR); 2603*0Sstevel@tonic-gate 2604*0Sstevel@tonic-gate return (utmpx_ok); 2605*0Sstevel@tonic-gate } 2606*0Sstevel@tonic-gate 2607*0Sstevel@tonic-gate /* 2608*0Sstevel@tonic-gate * account() updates entries in utmpx and appends new entries to the end of 2609*0Sstevel@tonic-gate * wtmpx (assuming they exist). The program argument indicates the name of 2610*0Sstevel@tonic-gate * program if INIT_PROCESS, otherwise should be NULL. 2611*0Sstevel@tonic-gate * 2612*0Sstevel@tonic-gate * account() only blocks for INIT_PROCESS requests. 2613*0Sstevel@tonic-gate * 2614*0Sstevel@tonic-gate * Returns non-zero if write failed. 2615*0Sstevel@tonic-gate */ 2616*0Sstevel@tonic-gate static int 2617*0Sstevel@tonic-gate account(short state, struct PROC_TABLE *process, char *program) 2618*0Sstevel@tonic-gate { 2619*0Sstevel@tonic-gate struct utmpx utmpbuf, *u, *oldu; 2620*0Sstevel@tonic-gate int tmplen; 2621*0Sstevel@tonic-gate char fail_buf[UT_LINE_SZ]; 2622*0Sstevel@tonic-gate sigset_t block, unblock; 2623*0Sstevel@tonic-gate 2624*0Sstevel@tonic-gate if (!utmpx_ok && !access_utmpx()) { 2625*0Sstevel@tonic-gate return (-1); 2626*0Sstevel@tonic-gate } 2627*0Sstevel@tonic-gate 2628*0Sstevel@tonic-gate /* 2629*0Sstevel@tonic-gate * Set up the prototype for the utmp structure we want to write. 2630*0Sstevel@tonic-gate */ 2631*0Sstevel@tonic-gate u = &utmpbuf; 2632*0Sstevel@tonic-gate (void) memset(u, 0, sizeof (struct utmpx)); 2633*0Sstevel@tonic-gate 2634*0Sstevel@tonic-gate /* 2635*0Sstevel@tonic-gate * Fill in the various fields of the utmp structure. 2636*0Sstevel@tonic-gate */ 2637*0Sstevel@tonic-gate u->ut_id[0] = process->p_id[0]; 2638*0Sstevel@tonic-gate u->ut_id[1] = process->p_id[1]; 2639*0Sstevel@tonic-gate u->ut_id[2] = process->p_id[2]; 2640*0Sstevel@tonic-gate u->ut_id[3] = process->p_id[3]; 2641*0Sstevel@tonic-gate u->ut_pid = process->p_pid; 2642*0Sstevel@tonic-gate 2643*0Sstevel@tonic-gate /* 2644*0Sstevel@tonic-gate * Fill the "ut_exit" structure. 2645*0Sstevel@tonic-gate */ 2646*0Sstevel@tonic-gate u->ut_exit.e_termination = WTERMSIG(process->p_exit); 2647*0Sstevel@tonic-gate u->ut_exit.e_exit = WEXITSTATUS(process->p_exit); 2648*0Sstevel@tonic-gate u->ut_type = state; 2649*0Sstevel@tonic-gate 2650*0Sstevel@tonic-gate (void) time(&u->ut_tv.tv_sec); 2651*0Sstevel@tonic-gate 2652*0Sstevel@tonic-gate /* 2653*0Sstevel@tonic-gate * Block signals for utmp update. 2654*0Sstevel@tonic-gate */ 2655*0Sstevel@tonic-gate (void) sigfillset(&block); 2656*0Sstevel@tonic-gate (void) sigprocmask(SIG_BLOCK, &block, &unblock); 2657*0Sstevel@tonic-gate 2658*0Sstevel@tonic-gate /* 2659*0Sstevel@tonic-gate * See if there already is such an entry in the "utmpx" file. 2660*0Sstevel@tonic-gate */ 2661*0Sstevel@tonic-gate setutxent(); /* Start at beginning of utmpx file. */ 2662*0Sstevel@tonic-gate 2663*0Sstevel@tonic-gate if ((oldu = getutxid(u)) != NULL) { 2664*0Sstevel@tonic-gate /* 2665*0Sstevel@tonic-gate * Copy in the old "user", "line" and "host" fields 2666*0Sstevel@tonic-gate * to our new structure. 2667*0Sstevel@tonic-gate */ 2668*0Sstevel@tonic-gate bcopy(oldu->ut_user, u->ut_user, sizeof (u->ut_user)); 2669*0Sstevel@tonic-gate bcopy(oldu->ut_line, u->ut_line, sizeof (u->ut_line)); 2670*0Sstevel@tonic-gate bcopy(oldu->ut_host, u->ut_host, sizeof (u->ut_host)); 2671*0Sstevel@tonic-gate u->ut_syslen = (tmplen = strlen(u->ut_host)) ? 2672*0Sstevel@tonic-gate min(tmplen + 1, sizeof (u->ut_host)) : 0; 2673*0Sstevel@tonic-gate 2674*0Sstevel@tonic-gate if (oldu->ut_type == USER_PROCESS && state == DEAD_PROCESS) { 2675*0Sstevel@tonic-gate notify_pam_dead(oldu); 2676*0Sstevel@tonic-gate } 2677*0Sstevel@tonic-gate } 2678*0Sstevel@tonic-gate 2679*0Sstevel@tonic-gate /* 2680*0Sstevel@tonic-gate * Perform special accounting. Insert the special string into the 2681*0Sstevel@tonic-gate * ut_line array. For INIT_PROCESSes put in the name of the 2682*0Sstevel@tonic-gate * program in the "ut_user" field. 2683*0Sstevel@tonic-gate */ 2684*0Sstevel@tonic-gate switch (state) { 2685*0Sstevel@tonic-gate case INIT_PROCESS: 2686*0Sstevel@tonic-gate (void) strncpy(u->ut_user, program, sizeof (u->ut_user)); 2687*0Sstevel@tonic-gate (void) strcpy(fail_buf, "INIT_PROCESS"); 2688*0Sstevel@tonic-gate break; 2689*0Sstevel@tonic-gate 2690*0Sstevel@tonic-gate default: 2691*0Sstevel@tonic-gate (void) strlcpy(fail_buf, u->ut_id, sizeof (u->ut_id) + 1); 2692*0Sstevel@tonic-gate break; 2693*0Sstevel@tonic-gate } 2694*0Sstevel@tonic-gate 2695*0Sstevel@tonic-gate /* 2696*0Sstevel@tonic-gate * Write out the updated entry to utmpx file. 2697*0Sstevel@tonic-gate */ 2698*0Sstevel@tonic-gate if (pututxline(u) == NULL) { 2699*0Sstevel@tonic-gate console(B_TRUE, "Failed write of utmpx entry: \"%s\": %s\n", 2700*0Sstevel@tonic-gate fail_buf, strerror(errno)); 2701*0Sstevel@tonic-gate endutxent(); 2702*0Sstevel@tonic-gate (void) sigprocmask(SIG_SETMASK, &unblock, NULL); 2703*0Sstevel@tonic-gate return (-1); 2704*0Sstevel@tonic-gate } 2705*0Sstevel@tonic-gate 2706*0Sstevel@tonic-gate /* 2707*0Sstevel@tonic-gate * If we're able to write to utmpx, then attempt to add to the 2708*0Sstevel@tonic-gate * end of the wtmpx file. 2709*0Sstevel@tonic-gate */ 2710*0Sstevel@tonic-gate updwtmpx(WTMPX, u); 2711*0Sstevel@tonic-gate 2712*0Sstevel@tonic-gate endutxent(); 2713*0Sstevel@tonic-gate 2714*0Sstevel@tonic-gate (void) sigprocmask(SIG_SETMASK, &unblock, NULL); 2715*0Sstevel@tonic-gate 2716*0Sstevel@tonic-gate return (0); 2717*0Sstevel@tonic-gate } 2718*0Sstevel@tonic-gate 2719*0Sstevel@tonic-gate static void 2720*0Sstevel@tonic-gate clearent(pid_t pid, short status) 2721*0Sstevel@tonic-gate { 2722*0Sstevel@tonic-gate struct utmpx *up; 2723*0Sstevel@tonic-gate sigset_t block, unblock; 2724*0Sstevel@tonic-gate 2725*0Sstevel@tonic-gate /* 2726*0Sstevel@tonic-gate * Block signals for utmp update. 2727*0Sstevel@tonic-gate */ 2728*0Sstevel@tonic-gate (void) sigfillset(&block); 2729*0Sstevel@tonic-gate (void) sigprocmask(SIG_BLOCK, &block, &unblock); 2730*0Sstevel@tonic-gate 2731*0Sstevel@tonic-gate /* 2732*0Sstevel@tonic-gate * No error checking for now. 2733*0Sstevel@tonic-gate */ 2734*0Sstevel@tonic-gate 2735*0Sstevel@tonic-gate setutxent(); 2736*0Sstevel@tonic-gate while (up = getutxent()) { 2737*0Sstevel@tonic-gate if (up->ut_pid == pid) { 2738*0Sstevel@tonic-gate if (up->ut_type == DEAD_PROCESS) { 2739*0Sstevel@tonic-gate /* 2740*0Sstevel@tonic-gate * Cleaned up elsewhere. 2741*0Sstevel@tonic-gate */ 2742*0Sstevel@tonic-gate continue; 2743*0Sstevel@tonic-gate } 2744*0Sstevel@tonic-gate 2745*0Sstevel@tonic-gate notify_pam_dead(up); 2746*0Sstevel@tonic-gate 2747*0Sstevel@tonic-gate up->ut_type = DEAD_PROCESS; 2748*0Sstevel@tonic-gate up->ut_exit.e_termination = WTERMSIG(status); 2749*0Sstevel@tonic-gate up->ut_exit.e_exit = WEXITSTATUS(status); 2750*0Sstevel@tonic-gate (void) time(&up->ut_tv.tv_sec); 2751*0Sstevel@tonic-gate 2752*0Sstevel@tonic-gate (void) pututxline(up); 2753*0Sstevel@tonic-gate /* 2754*0Sstevel@tonic-gate * Now attempt to add to the end of the 2755*0Sstevel@tonic-gate * wtmp and wtmpx files. Do not create 2756*0Sstevel@tonic-gate * if they don't already exist. 2757*0Sstevel@tonic-gate */ 2758*0Sstevel@tonic-gate updwtmpx(WTMPX, up); 2759*0Sstevel@tonic-gate 2760*0Sstevel@tonic-gate break; 2761*0Sstevel@tonic-gate } 2762*0Sstevel@tonic-gate } 2763*0Sstevel@tonic-gate 2764*0Sstevel@tonic-gate endutxent(); 2765*0Sstevel@tonic-gate (void) sigprocmask(SIG_SETMASK, &unblock, NULL); 2766*0Sstevel@tonic-gate } 2767*0Sstevel@tonic-gate 2768*0Sstevel@tonic-gate /* 2769*0Sstevel@tonic-gate * prog_name() searches for the word or unix path name and 2770*0Sstevel@tonic-gate * returns a pointer to the last element of the pathname. 2771*0Sstevel@tonic-gate */ 2772*0Sstevel@tonic-gate static char * 2773*0Sstevel@tonic-gate prog_name(char *string) 2774*0Sstevel@tonic-gate { 2775*0Sstevel@tonic-gate char *ptr, *ptr2; 2776*0Sstevel@tonic-gate /* XXX - utmp - fix name length */ 2777*0Sstevel@tonic-gate static char word[_POSIX_LOGIN_NAME_MAX]; 2778*0Sstevel@tonic-gate 2779*0Sstevel@tonic-gate /* 2780*0Sstevel@tonic-gate * Search for the first word skipping leading spaces and tabs. 2781*0Sstevel@tonic-gate */ 2782*0Sstevel@tonic-gate while (*string == ' ' || *string == '\t') 2783*0Sstevel@tonic-gate string++; 2784*0Sstevel@tonic-gate 2785*0Sstevel@tonic-gate /* 2786*0Sstevel@tonic-gate * If the first non-space non-tab character is not one allowed in 2787*0Sstevel@tonic-gate * a word, return a pointer to a null string, otherwise parse the 2788*0Sstevel@tonic-gate * pathname. 2789*0Sstevel@tonic-gate */ 2790*0Sstevel@tonic-gate if (*string != '.' && *string != '/' && *string != '_' && 2791*0Sstevel@tonic-gate (*string < 'a' || *string > 'z') && 2792*0Sstevel@tonic-gate (*string < 'A' || * string > 'Z') && 2793*0Sstevel@tonic-gate (*string < '0' || *string > '9')) 2794*0Sstevel@tonic-gate return (""); 2795*0Sstevel@tonic-gate 2796*0Sstevel@tonic-gate /* 2797*0Sstevel@tonic-gate * Parse the pathname looking forward for '/', ' ', '\t', '\n' or 2798*0Sstevel@tonic-gate * '\0'. Each time a '/' is found, move "ptr" to one past the 2799*0Sstevel@tonic-gate * '/', thus when a ' ', '\t', '\n', or '\0' is found, "ptr" will 2800*0Sstevel@tonic-gate * point to the last element of the pathname. 2801*0Sstevel@tonic-gate */ 2802*0Sstevel@tonic-gate for (ptr = string; 2803*0Sstevel@tonic-gate *string != ' ' && *string != '\t' && *string != '\n' && 2804*0Sstevel@tonic-gate *string != '\0'; 2805*0Sstevel@tonic-gate string++) { 2806*0Sstevel@tonic-gate if (*string == '/') 2807*0Sstevel@tonic-gate ptr = string+1; 2808*0Sstevel@tonic-gate } 2809*0Sstevel@tonic-gate 2810*0Sstevel@tonic-gate /* 2811*0Sstevel@tonic-gate * Copy out up to the size of the "ut_user" array into "word", 2812*0Sstevel@tonic-gate * null terminate it and return a pointer to it. 2813*0Sstevel@tonic-gate */ 2814*0Sstevel@tonic-gate /* XXX - utmp - fix name length */ 2815*0Sstevel@tonic-gate for (ptr2 = &word[0]; ptr2 < &word[_POSIX_LOGIN_NAME_MAX - 1] && 2816*0Sstevel@tonic-gate ptr < string; /* CSTYLED */) 2817*0Sstevel@tonic-gate *ptr2++ = *ptr++; 2818*0Sstevel@tonic-gate 2819*0Sstevel@tonic-gate *ptr2 = '\0'; 2820*0Sstevel@tonic-gate return (&word[0]); 2821*0Sstevel@tonic-gate } 2822*0Sstevel@tonic-gate 2823*0Sstevel@tonic-gate 2824*0Sstevel@tonic-gate /* 2825*0Sstevel@tonic-gate * realcon() returns a nonzero value if there is a character device 2826*0Sstevel@tonic-gate * associated with SYSCON that has the same device number as CONSOLE. 2827*0Sstevel@tonic-gate */ 2828*0Sstevel@tonic-gate static int 2829*0Sstevel@tonic-gate realcon() 2830*0Sstevel@tonic-gate { 2831*0Sstevel@tonic-gate struct stat sconbuf, conbuf; 2832*0Sstevel@tonic-gate 2833*0Sstevel@tonic-gate if (stat(SYSCON, &sconbuf) != -1 && 2834*0Sstevel@tonic-gate stat(CONSOLE, &conbuf) != -1 && 2835*0Sstevel@tonic-gate sconbuf.st_mode & S_IFCHR && 2836*0Sstevel@tonic-gate conbuf.st_mode & S_IFCHR && 2837*0Sstevel@tonic-gate sconbuf.st_rdev == conbuf.st_rdev) { 2838*0Sstevel@tonic-gate return (1); 2839*0Sstevel@tonic-gate } else { 2840*0Sstevel@tonic-gate return (0); 2841*0Sstevel@tonic-gate } 2842*0Sstevel@tonic-gate } 2843*0Sstevel@tonic-gate 2844*0Sstevel@tonic-gate 2845*0Sstevel@tonic-gate /* 2846*0Sstevel@tonic-gate * get_ioctl_syscon() retrieves the SYSCON settings from the IOCTLSYSCON file. 2847*0Sstevel@tonic-gate * Returns true if the IOCTLSYSCON file needs to be written (with 2848*0Sstevel@tonic-gate * write_ioctl_syscon() below) 2849*0Sstevel@tonic-gate */ 2850*0Sstevel@tonic-gate static int 2851*0Sstevel@tonic-gate get_ioctl_syscon() 2852*0Sstevel@tonic-gate { 2853*0Sstevel@tonic-gate FILE *fp; 2854*0Sstevel@tonic-gate unsigned int iflags, oflags, cflags, lflags, ldisc, cc[18]; 2855*0Sstevel@tonic-gate int i, valid_format = 0; 2856*0Sstevel@tonic-gate 2857*0Sstevel@tonic-gate /* 2858*0Sstevel@tonic-gate * Read in the previous modes for SYSCON from IOCTLSYSCON. 2859*0Sstevel@tonic-gate */ 2860*0Sstevel@tonic-gate if ((fp = fopen(IOCTLSYSCON, "r")) == NULL) { 2861*0Sstevel@tonic-gate stored_syscon_termios = dflt_termios; 2862*0Sstevel@tonic-gate console(B_TRUE, 2863*0Sstevel@tonic-gate "warning:%s does not exist, default settings assumed\n", 2864*0Sstevel@tonic-gate IOCTLSYSCON); 2865*0Sstevel@tonic-gate } else { 2866*0Sstevel@tonic-gate 2867*0Sstevel@tonic-gate i = fscanf(fp, 2868*0Sstevel@tonic-gate "%x:%x:%x:%x:%x:%x:%x:%x:%x:%x:%x:%x:%x:%x:%x:%x:%x:%x:%x:%x:%x:%x", 2869*0Sstevel@tonic-gate &iflags, &oflags, &cflags, &lflags, 2870*0Sstevel@tonic-gate &cc[0], &cc[1], &cc[2], &cc[3], &cc[4], &cc[5], &cc[6], 2871*0Sstevel@tonic-gate &cc[7], &cc[8], &cc[9], &cc[10], &cc[11], &cc[12], &cc[13], 2872*0Sstevel@tonic-gate &cc[14], &cc[15], &cc[16], &cc[17]); 2873*0Sstevel@tonic-gate 2874*0Sstevel@tonic-gate if (i == 22) { 2875*0Sstevel@tonic-gate stored_syscon_termios.c_iflag = iflags; 2876*0Sstevel@tonic-gate stored_syscon_termios.c_oflag = oflags; 2877*0Sstevel@tonic-gate stored_syscon_termios.c_cflag = cflags; 2878*0Sstevel@tonic-gate stored_syscon_termios.c_lflag = lflags; 2879*0Sstevel@tonic-gate for (i = 0; i < 18; i++) 2880*0Sstevel@tonic-gate stored_syscon_termios.c_cc[i] = (char)cc[i]; 2881*0Sstevel@tonic-gate valid_format = 1; 2882*0Sstevel@tonic-gate } else if (i == 13) { 2883*0Sstevel@tonic-gate rewind(fp); 2884*0Sstevel@tonic-gate i = fscanf(fp, "%x:%x:%x:%x:%x:%x:%x:%x:%x:%x:%x:%x:%x", 2885*0Sstevel@tonic-gate &iflags, &oflags, &cflags, &lflags, &ldisc, &cc[0], &cc[1], 2886*0Sstevel@tonic-gate &cc[2], &cc[3], &cc[4], &cc[5], &cc[6], &cc[7]); 2887*0Sstevel@tonic-gate 2888*0Sstevel@tonic-gate /* 2889*0Sstevel@tonic-gate * If the file is formatted properly, use the values to 2890*0Sstevel@tonic-gate * initialize the console terminal condition. 2891*0Sstevel@tonic-gate */ 2892*0Sstevel@tonic-gate stored_syscon_termios.c_iflag = (ushort_t)iflags; 2893*0Sstevel@tonic-gate stored_syscon_termios.c_oflag = (ushort_t)oflags; 2894*0Sstevel@tonic-gate stored_syscon_termios.c_cflag = (ushort_t)cflags; 2895*0Sstevel@tonic-gate stored_syscon_termios.c_lflag = (ushort_t)lflags; 2896*0Sstevel@tonic-gate for (i = 0; i < 8; i++) 2897*0Sstevel@tonic-gate stored_syscon_termios.c_cc[i] = (char)cc[i]; 2898*0Sstevel@tonic-gate valid_format = 1; 2899*0Sstevel@tonic-gate } 2900*0Sstevel@tonic-gate (void) fclose(fp); 2901*0Sstevel@tonic-gate 2902*0Sstevel@tonic-gate /* If the file is badly formatted, use the default settings. */ 2903*0Sstevel@tonic-gate if (!valid_format) 2904*0Sstevel@tonic-gate stored_syscon_termios = dflt_termios; 2905*0Sstevel@tonic-gate } 2906*0Sstevel@tonic-gate 2907*0Sstevel@tonic-gate /* If the file had a bad format, rewrite it later. */ 2908*0Sstevel@tonic-gate return (!valid_format); 2909*0Sstevel@tonic-gate } 2910*0Sstevel@tonic-gate 2911*0Sstevel@tonic-gate 2912*0Sstevel@tonic-gate static void 2913*0Sstevel@tonic-gate write_ioctl_syscon() 2914*0Sstevel@tonic-gate { 2915*0Sstevel@tonic-gate FILE *fp; 2916*0Sstevel@tonic-gate int i; 2917*0Sstevel@tonic-gate 2918*0Sstevel@tonic-gate (void) unlink(SYSCON); 2919*0Sstevel@tonic-gate (void) link(SYSTTY, SYSCON); 2920*0Sstevel@tonic-gate (void) umask(022); 2921*0Sstevel@tonic-gate fp = fopen(IOCTLSYSCON, "w"); 2922*0Sstevel@tonic-gate 2923*0Sstevel@tonic-gate (void) fprintf(fp, "%x:%x:%x:%x:0", stored_syscon_termios.c_iflag, 2924*0Sstevel@tonic-gate stored_syscon_termios.c_oflag, stored_syscon_termios.c_cflag, 2925*0Sstevel@tonic-gate stored_syscon_termios.c_lflag); 2926*0Sstevel@tonic-gate for (i = 0; i < 8; ++i) 2927*0Sstevel@tonic-gate (void) fprintf(fp, ":%x", stored_syscon_termios.c_cc[i]); 2928*0Sstevel@tonic-gate (void) putc('\n', fp); 2929*0Sstevel@tonic-gate 2930*0Sstevel@tonic-gate (void) fflush(fp); 2931*0Sstevel@tonic-gate (void) fsync(fileno(fp)); 2932*0Sstevel@tonic-gate (void) fclose(fp); 2933*0Sstevel@tonic-gate (void) umask(cmask); 2934*0Sstevel@tonic-gate } 2935*0Sstevel@tonic-gate 2936*0Sstevel@tonic-gate 2937*0Sstevel@tonic-gate /* 2938*0Sstevel@tonic-gate * void console(boolean_t, char *, ...) 2939*0Sstevel@tonic-gate * Outputs the requested message to the system console. Note that the number 2940*0Sstevel@tonic-gate * of arguments passed to console() should be determined by the print format. 2941*0Sstevel@tonic-gate * 2942*0Sstevel@tonic-gate * The "prefix" parameter indicates whether or not "INIT: " should precede the 2943*0Sstevel@tonic-gate * message. 2944*0Sstevel@tonic-gate * 2945*0Sstevel@tonic-gate * To make sure we write to the console in a sane fashion, we use the modes 2946*0Sstevel@tonic-gate * we keep in stored_syscon_termios (which we read out of /etc/ioctl.syscon). 2947*0Sstevel@tonic-gate * Afterwards we restore whatever modes were already there. 2948*0Sstevel@tonic-gate */ 2949*0Sstevel@tonic-gate /* PRINTFLIKE2 */ 2950*0Sstevel@tonic-gate static void 2951*0Sstevel@tonic-gate console(boolean_t prefix, char *format, ...) 2952*0Sstevel@tonic-gate { 2953*0Sstevel@tonic-gate char outbuf[BUFSIZ]; 2954*0Sstevel@tonic-gate va_list args; 2955*0Sstevel@tonic-gate int fd, getret; 2956*0Sstevel@tonic-gate struct termios old_syscon_termios; 2957*0Sstevel@tonic-gate FILE *f; 2958*0Sstevel@tonic-gate 2959*0Sstevel@tonic-gate /* 2960*0Sstevel@tonic-gate * We open SYSCON anew each time in case it has changed (see 2961*0Sstevel@tonic-gate * userinit()). 2962*0Sstevel@tonic-gate */ 2963*0Sstevel@tonic-gate if ((fd = open(SYSCON, O_RDWR | O_NOCTTY)) < 0 || 2964*0Sstevel@tonic-gate (f = fdopen(fd, "r+")) == NULL) { 2965*0Sstevel@tonic-gate if (prefix) 2966*0Sstevel@tonic-gate syslog(LOG_WARNING, "INIT: "); 2967*0Sstevel@tonic-gate va_start(args, format); 2968*0Sstevel@tonic-gate vsyslog(LOG_WARNING, format, args); 2969*0Sstevel@tonic-gate va_end(args); 2970*0Sstevel@tonic-gate if (fd >= 0) 2971*0Sstevel@tonic-gate (void) close(fd); 2972*0Sstevel@tonic-gate return; 2973*0Sstevel@tonic-gate } 2974*0Sstevel@tonic-gate setbuf(f, &outbuf[0]); 2975*0Sstevel@tonic-gate 2976*0Sstevel@tonic-gate getret = tcgetattr(fd, &old_syscon_termios); 2977*0Sstevel@tonic-gate old_syscon_termios.c_cflag &= ~HUPCL; 2978*0Sstevel@tonic-gate if (realcon()) 2979*0Sstevel@tonic-gate /* Don't overwrite cflag of real console. */ 2980*0Sstevel@tonic-gate stored_syscon_termios.c_cflag = old_syscon_termios.c_cflag; 2981*0Sstevel@tonic-gate 2982*0Sstevel@tonic-gate stored_syscon_termios.c_cflag &= ~HUPCL; 2983*0Sstevel@tonic-gate 2984*0Sstevel@tonic-gate (void) tcsetattr(fd, TCSANOW, &stored_syscon_termios); 2985*0Sstevel@tonic-gate 2986*0Sstevel@tonic-gate if (prefix) 2987*0Sstevel@tonic-gate (void) fprintf(f, "\nINIT: "); 2988*0Sstevel@tonic-gate va_start(args, format); 2989*0Sstevel@tonic-gate (void) vfprintf(f, format, args); 2990*0Sstevel@tonic-gate va_end(args); 2991*0Sstevel@tonic-gate 2992*0Sstevel@tonic-gate if (getret == 0) 2993*0Sstevel@tonic-gate (void) tcsetattr(fd, TCSADRAIN, &old_syscon_termios); 2994*0Sstevel@tonic-gate 2995*0Sstevel@tonic-gate (void) fclose(f); 2996*0Sstevel@tonic-gate } 2997*0Sstevel@tonic-gate 2998*0Sstevel@tonic-gate /* 2999*0Sstevel@tonic-gate * timer() is a substitute for sleep() which uses alarm() and pause(). 3000*0Sstevel@tonic-gate */ 3001*0Sstevel@tonic-gate static void 3002*0Sstevel@tonic-gate timer(int waitime) 3003*0Sstevel@tonic-gate { 3004*0Sstevel@tonic-gate setimer(waitime); 3005*0Sstevel@tonic-gate while (time_up == FALSE) 3006*0Sstevel@tonic-gate (void) pause(); 3007*0Sstevel@tonic-gate } 3008*0Sstevel@tonic-gate 3009*0Sstevel@tonic-gate static void 3010*0Sstevel@tonic-gate setimer(int timelimit) 3011*0Sstevel@tonic-gate { 3012*0Sstevel@tonic-gate alarmclk(); 3013*0Sstevel@tonic-gate (void) alarm(timelimit); 3014*0Sstevel@tonic-gate time_up = (timelimit ? FALSE : TRUE); 3015*0Sstevel@tonic-gate } 3016*0Sstevel@tonic-gate 3017*0Sstevel@tonic-gate /* 3018*0Sstevel@tonic-gate * Fails with 3019*0Sstevel@tonic-gate * ENOMEM - out of memory 3020*0Sstevel@tonic-gate * ECONNABORTED - repository connection broken 3021*0Sstevel@tonic-gate * EPERM - permission denied 3022*0Sstevel@tonic-gate * EACCES - backend access denied 3023*0Sstevel@tonic-gate * EROFS - backend readonly 3024*0Sstevel@tonic-gate */ 3025*0Sstevel@tonic-gate static int 3026*0Sstevel@tonic-gate get_or_add_startd(scf_instance_t *inst) 3027*0Sstevel@tonic-gate { 3028*0Sstevel@tonic-gate scf_handle_t *h; 3029*0Sstevel@tonic-gate scf_scope_t *scope = NULL; 3030*0Sstevel@tonic-gate scf_service_t *svc = NULL; 3031*0Sstevel@tonic-gate int ret = 0; 3032*0Sstevel@tonic-gate 3033*0Sstevel@tonic-gate h = scf_instance_handle(inst); 3034*0Sstevel@tonic-gate 3035*0Sstevel@tonic-gate if (scf_handle_decode_fmri(h, SCF_SERVICE_STARTD, NULL, NULL, inst, 3036*0Sstevel@tonic-gate NULL, NULL, SCF_DECODE_FMRI_EXACT) == 0) 3037*0Sstevel@tonic-gate return (0); 3038*0Sstevel@tonic-gate 3039*0Sstevel@tonic-gate switch (scf_error()) { 3040*0Sstevel@tonic-gate case SCF_ERROR_CONNECTION_BROKEN: 3041*0Sstevel@tonic-gate return (ECONNABORTED); 3042*0Sstevel@tonic-gate 3043*0Sstevel@tonic-gate case SCF_ERROR_NOT_FOUND: 3044*0Sstevel@tonic-gate break; 3045*0Sstevel@tonic-gate 3046*0Sstevel@tonic-gate case SCF_ERROR_HANDLE_MISMATCH: 3047*0Sstevel@tonic-gate case SCF_ERROR_INVALID_ARGUMENT: 3048*0Sstevel@tonic-gate case SCF_ERROR_CONSTRAINT_VIOLATED: 3049*0Sstevel@tonic-gate default: 3050*0Sstevel@tonic-gate bad_error("scf_handle_decode_fmri", scf_error()); 3051*0Sstevel@tonic-gate } 3052*0Sstevel@tonic-gate 3053*0Sstevel@tonic-gate /* Make sure we're right, since we're adding piece-by-piece. */ 3054*0Sstevel@tonic-gate assert(strcmp(SCF_SERVICE_STARTD, 3055*0Sstevel@tonic-gate "svc:/system/svc/restarter:default") == 0); 3056*0Sstevel@tonic-gate 3057*0Sstevel@tonic-gate if ((scope = scf_scope_create(h)) == NULL || 3058*0Sstevel@tonic-gate (svc = scf_service_create(h)) == NULL) { 3059*0Sstevel@tonic-gate ret = ENOMEM; 3060*0Sstevel@tonic-gate goto out; 3061*0Sstevel@tonic-gate } 3062*0Sstevel@tonic-gate 3063*0Sstevel@tonic-gate get_scope: 3064*0Sstevel@tonic-gate if (scf_handle_get_scope(h, SCF_SCOPE_LOCAL, scope) != 0) { 3065*0Sstevel@tonic-gate switch (scf_error()) { 3066*0Sstevel@tonic-gate case SCF_ERROR_CONNECTION_BROKEN: 3067*0Sstevel@tonic-gate ret = ECONNABORTED; 3068*0Sstevel@tonic-gate goto out; 3069*0Sstevel@tonic-gate 3070*0Sstevel@tonic-gate case SCF_ERROR_NOT_FOUND: 3071*0Sstevel@tonic-gate (void) fputs(gettext( 3072*0Sstevel@tonic-gate "smf(5) repository missing local scope.\n"), 3073*0Sstevel@tonic-gate stderr); 3074*0Sstevel@tonic-gate exit(1); 3075*0Sstevel@tonic-gate /* NOTREACHED */ 3076*0Sstevel@tonic-gate 3077*0Sstevel@tonic-gate case SCF_ERROR_HANDLE_MISMATCH: 3078*0Sstevel@tonic-gate case SCF_ERROR_INVALID_ARGUMENT: 3079*0Sstevel@tonic-gate default: 3080*0Sstevel@tonic-gate bad_error("scf_handle_get_scope", scf_error()); 3081*0Sstevel@tonic-gate } 3082*0Sstevel@tonic-gate } 3083*0Sstevel@tonic-gate 3084*0Sstevel@tonic-gate get_svc: 3085*0Sstevel@tonic-gate if (scf_scope_get_service(scope, "system/svc/restarter", svc) != 0) { 3086*0Sstevel@tonic-gate switch (scf_error()) { 3087*0Sstevel@tonic-gate case SCF_ERROR_CONNECTION_BROKEN: 3088*0Sstevel@tonic-gate ret = ECONNABORTED; 3089*0Sstevel@tonic-gate goto out; 3090*0Sstevel@tonic-gate 3091*0Sstevel@tonic-gate case SCF_ERROR_DELETED: 3092*0Sstevel@tonic-gate goto get_scope; 3093*0Sstevel@tonic-gate 3094*0Sstevel@tonic-gate case SCF_ERROR_NOT_FOUND: 3095*0Sstevel@tonic-gate break; 3096*0Sstevel@tonic-gate 3097*0Sstevel@tonic-gate case SCF_ERROR_HANDLE_MISMATCH: 3098*0Sstevel@tonic-gate case SCF_ERROR_INVALID_ARGUMENT: 3099*0Sstevel@tonic-gate case SCF_ERROR_NOT_SET: 3100*0Sstevel@tonic-gate default: 3101*0Sstevel@tonic-gate bad_error("scf_scope_get_service", scf_error()); 3102*0Sstevel@tonic-gate } 3103*0Sstevel@tonic-gate 3104*0Sstevel@tonic-gate add_svc: 3105*0Sstevel@tonic-gate if (scf_scope_add_service(scope, "system/svc/restarter", svc) != 3106*0Sstevel@tonic-gate 0) { 3107*0Sstevel@tonic-gate switch (scf_error()) { 3108*0Sstevel@tonic-gate case SCF_ERROR_CONNECTION_BROKEN: 3109*0Sstevel@tonic-gate ret = ECONNABORTED; 3110*0Sstevel@tonic-gate goto out; 3111*0Sstevel@tonic-gate 3112*0Sstevel@tonic-gate case SCF_ERROR_EXISTS: 3113*0Sstevel@tonic-gate goto get_svc; 3114*0Sstevel@tonic-gate 3115*0Sstevel@tonic-gate case SCF_ERROR_PERMISSION_DENIED: 3116*0Sstevel@tonic-gate ret = EPERM; 3117*0Sstevel@tonic-gate goto out; 3118*0Sstevel@tonic-gate 3119*0Sstevel@tonic-gate case SCF_ERROR_BACKEND_ACCESS: 3120*0Sstevel@tonic-gate ret = EACCES; 3121*0Sstevel@tonic-gate goto out; 3122*0Sstevel@tonic-gate 3123*0Sstevel@tonic-gate case SCF_ERROR_BACKEND_READONLY: 3124*0Sstevel@tonic-gate ret = EROFS; 3125*0Sstevel@tonic-gate goto out; 3126*0Sstevel@tonic-gate 3127*0Sstevel@tonic-gate case SCF_ERROR_HANDLE_MISMATCH: 3128*0Sstevel@tonic-gate case SCF_ERROR_INVALID_ARGUMENT: 3129*0Sstevel@tonic-gate case SCF_ERROR_NOT_SET: 3130*0Sstevel@tonic-gate default: 3131*0Sstevel@tonic-gate bad_error("scf_scope_add_service", scf_error()); 3132*0Sstevel@tonic-gate } 3133*0Sstevel@tonic-gate } 3134*0Sstevel@tonic-gate } 3135*0Sstevel@tonic-gate 3136*0Sstevel@tonic-gate get_inst: 3137*0Sstevel@tonic-gate if (scf_service_get_instance(svc, "default", inst) != 0) { 3138*0Sstevel@tonic-gate switch (scf_error()) { 3139*0Sstevel@tonic-gate case SCF_ERROR_CONNECTION_BROKEN: 3140*0Sstevel@tonic-gate ret = ECONNABORTED; 3141*0Sstevel@tonic-gate goto out; 3142*0Sstevel@tonic-gate 3143*0Sstevel@tonic-gate case SCF_ERROR_DELETED: 3144*0Sstevel@tonic-gate goto add_svc; 3145*0Sstevel@tonic-gate 3146*0Sstevel@tonic-gate case SCF_ERROR_NOT_FOUND: 3147*0Sstevel@tonic-gate break; 3148*0Sstevel@tonic-gate 3149*0Sstevel@tonic-gate case SCF_ERROR_HANDLE_MISMATCH: 3150*0Sstevel@tonic-gate case SCF_ERROR_INVALID_ARGUMENT: 3151*0Sstevel@tonic-gate case SCF_ERROR_NOT_SET: 3152*0Sstevel@tonic-gate default: 3153*0Sstevel@tonic-gate bad_error("scf_service_get_instance", scf_error()); 3154*0Sstevel@tonic-gate } 3155*0Sstevel@tonic-gate 3156*0Sstevel@tonic-gate if (scf_service_add_instance(svc, "default", inst) != 3157*0Sstevel@tonic-gate 0) { 3158*0Sstevel@tonic-gate switch (scf_error()) { 3159*0Sstevel@tonic-gate case SCF_ERROR_CONNECTION_BROKEN: 3160*0Sstevel@tonic-gate ret = ECONNABORTED; 3161*0Sstevel@tonic-gate goto out; 3162*0Sstevel@tonic-gate 3163*0Sstevel@tonic-gate case SCF_ERROR_DELETED: 3164*0Sstevel@tonic-gate goto add_svc; 3165*0Sstevel@tonic-gate 3166*0Sstevel@tonic-gate case SCF_ERROR_EXISTS: 3167*0Sstevel@tonic-gate goto get_inst; 3168*0Sstevel@tonic-gate 3169*0Sstevel@tonic-gate case SCF_ERROR_PERMISSION_DENIED: 3170*0Sstevel@tonic-gate ret = EPERM; 3171*0Sstevel@tonic-gate goto out; 3172*0Sstevel@tonic-gate 3173*0Sstevel@tonic-gate case SCF_ERROR_BACKEND_ACCESS: 3174*0Sstevel@tonic-gate ret = EACCES; 3175*0Sstevel@tonic-gate goto out; 3176*0Sstevel@tonic-gate 3177*0Sstevel@tonic-gate case SCF_ERROR_BACKEND_READONLY: 3178*0Sstevel@tonic-gate ret = EROFS; 3179*0Sstevel@tonic-gate goto out; 3180*0Sstevel@tonic-gate 3181*0Sstevel@tonic-gate case SCF_ERROR_HANDLE_MISMATCH: 3182*0Sstevel@tonic-gate case SCF_ERROR_INVALID_ARGUMENT: 3183*0Sstevel@tonic-gate case SCF_ERROR_NOT_SET: 3184*0Sstevel@tonic-gate default: 3185*0Sstevel@tonic-gate bad_error("scf_service_add_instance", 3186*0Sstevel@tonic-gate scf_error()); 3187*0Sstevel@tonic-gate } 3188*0Sstevel@tonic-gate } 3189*0Sstevel@tonic-gate } 3190*0Sstevel@tonic-gate 3191*0Sstevel@tonic-gate ret = 0; 3192*0Sstevel@tonic-gate 3193*0Sstevel@tonic-gate out: 3194*0Sstevel@tonic-gate scf_service_destroy(svc); 3195*0Sstevel@tonic-gate scf_scope_destroy(scope); 3196*0Sstevel@tonic-gate return (ret); 3197*0Sstevel@tonic-gate } 3198*0Sstevel@tonic-gate 3199*0Sstevel@tonic-gate /* 3200*0Sstevel@tonic-gate * Fails with 3201*0Sstevel@tonic-gate * ECONNABORTED - repository connection broken 3202*0Sstevel@tonic-gate * ECANCELED - the transaction's property group was deleted 3203*0Sstevel@tonic-gate */ 3204*0Sstevel@tonic-gate static int 3205*0Sstevel@tonic-gate transaction_add_set(scf_transaction_t *tx, scf_transaction_entry_t *ent, 3206*0Sstevel@tonic-gate const char *pname, scf_type_t type) 3207*0Sstevel@tonic-gate { 3208*0Sstevel@tonic-gate change_type: 3209*0Sstevel@tonic-gate if (scf_transaction_property_change_type(tx, ent, pname, type) == 0) 3210*0Sstevel@tonic-gate return (0); 3211*0Sstevel@tonic-gate 3212*0Sstevel@tonic-gate switch (scf_error()) { 3213*0Sstevel@tonic-gate case SCF_ERROR_CONNECTION_BROKEN: 3214*0Sstevel@tonic-gate return (ECONNABORTED); 3215*0Sstevel@tonic-gate 3216*0Sstevel@tonic-gate case SCF_ERROR_DELETED: 3217*0Sstevel@tonic-gate return (ECANCELED); 3218*0Sstevel@tonic-gate 3219*0Sstevel@tonic-gate case SCF_ERROR_NOT_FOUND: 3220*0Sstevel@tonic-gate goto new; 3221*0Sstevel@tonic-gate 3222*0Sstevel@tonic-gate case SCF_ERROR_HANDLE_MISMATCH: 3223*0Sstevel@tonic-gate case SCF_ERROR_INVALID_ARGUMENT: 3224*0Sstevel@tonic-gate case SCF_ERROR_NOT_BOUND: 3225*0Sstevel@tonic-gate case SCF_ERROR_NOT_SET: 3226*0Sstevel@tonic-gate default: 3227*0Sstevel@tonic-gate bad_error("scf_transaction_property_change_type", scf_error()); 3228*0Sstevel@tonic-gate } 3229*0Sstevel@tonic-gate 3230*0Sstevel@tonic-gate new: 3231*0Sstevel@tonic-gate if (scf_transaction_property_new(tx, ent, pname, type) == 0) 3232*0Sstevel@tonic-gate return (0); 3233*0Sstevel@tonic-gate 3234*0Sstevel@tonic-gate switch (scf_error()) { 3235*0Sstevel@tonic-gate case SCF_ERROR_CONNECTION_BROKEN: 3236*0Sstevel@tonic-gate return (ECONNABORTED); 3237*0Sstevel@tonic-gate 3238*0Sstevel@tonic-gate case SCF_ERROR_DELETED: 3239*0Sstevel@tonic-gate return (ECANCELED); 3240*0Sstevel@tonic-gate 3241*0Sstevel@tonic-gate case SCF_ERROR_EXISTS: 3242*0Sstevel@tonic-gate goto change_type; 3243*0Sstevel@tonic-gate 3244*0Sstevel@tonic-gate case SCF_ERROR_HANDLE_MISMATCH: 3245*0Sstevel@tonic-gate case SCF_ERROR_INVALID_ARGUMENT: 3246*0Sstevel@tonic-gate case SCF_ERROR_NOT_BOUND: 3247*0Sstevel@tonic-gate case SCF_ERROR_NOT_SET: 3248*0Sstevel@tonic-gate default: 3249*0Sstevel@tonic-gate bad_error("scf_transaction_property_new", scf_error()); 3250*0Sstevel@tonic-gate /* NOTREACHED */ 3251*0Sstevel@tonic-gate } 3252*0Sstevel@tonic-gate } 3253*0Sstevel@tonic-gate 3254*0Sstevel@tonic-gate static void 3255*0Sstevel@tonic-gate scferr(void) 3256*0Sstevel@tonic-gate { 3257*0Sstevel@tonic-gate switch (scf_error()) { 3258*0Sstevel@tonic-gate case SCF_ERROR_NO_MEMORY: 3259*0Sstevel@tonic-gate console(B_TRUE, gettext("Out of memory.\n")); 3260*0Sstevel@tonic-gate break; 3261*0Sstevel@tonic-gate 3262*0Sstevel@tonic-gate case SCF_ERROR_CONNECTION_BROKEN: 3263*0Sstevel@tonic-gate console(B_TRUE, gettext( 3264*0Sstevel@tonic-gate "Connection to smf(5) repository server broken.\n")); 3265*0Sstevel@tonic-gate break; 3266*0Sstevel@tonic-gate 3267*0Sstevel@tonic-gate case SCF_ERROR_NO_RESOURCES: 3268*0Sstevel@tonic-gate console(B_TRUE, gettext( 3269*0Sstevel@tonic-gate "smf(5) repository server is out of memory.\n")); 3270*0Sstevel@tonic-gate break; 3271*0Sstevel@tonic-gate 3272*0Sstevel@tonic-gate case SCF_ERROR_PERMISSION_DENIED: 3273*0Sstevel@tonic-gate console(B_TRUE, gettext("Insufficient privileges.\n")); 3274*0Sstevel@tonic-gate break; 3275*0Sstevel@tonic-gate 3276*0Sstevel@tonic-gate default: 3277*0Sstevel@tonic-gate console(B_TRUE, gettext("libscf error: %s\n"), 3278*0Sstevel@tonic-gate scf_strerror(scf_error())); 3279*0Sstevel@tonic-gate } 3280*0Sstevel@tonic-gate } 3281*0Sstevel@tonic-gate 3282*0Sstevel@tonic-gate static void 3283*0Sstevel@tonic-gate lscf_set_runlevel(char rl) 3284*0Sstevel@tonic-gate { 3285*0Sstevel@tonic-gate scf_handle_t *h; 3286*0Sstevel@tonic-gate scf_instance_t *inst = NULL; 3287*0Sstevel@tonic-gate scf_propertygroup_t *pg = NULL; 3288*0Sstevel@tonic-gate scf_transaction_t *tx = NULL; 3289*0Sstevel@tonic-gate scf_transaction_entry_t *ent = NULL; 3290*0Sstevel@tonic-gate scf_value_t *val = NULL; 3291*0Sstevel@tonic-gate char buf[2]; 3292*0Sstevel@tonic-gate int r; 3293*0Sstevel@tonic-gate 3294*0Sstevel@tonic-gate h = scf_handle_create(SCF_VERSION); 3295*0Sstevel@tonic-gate if (h == NULL) { 3296*0Sstevel@tonic-gate scferr(); 3297*0Sstevel@tonic-gate return; 3298*0Sstevel@tonic-gate } 3299*0Sstevel@tonic-gate 3300*0Sstevel@tonic-gate if (scf_handle_bind(h) != 0) { 3301*0Sstevel@tonic-gate switch (scf_error()) { 3302*0Sstevel@tonic-gate case SCF_ERROR_NO_SERVER: 3303*0Sstevel@tonic-gate console(B_TRUE, 3304*0Sstevel@tonic-gate gettext("smf(5) repository server not running.\n")); 3305*0Sstevel@tonic-gate goto bail; 3306*0Sstevel@tonic-gate 3307*0Sstevel@tonic-gate default: 3308*0Sstevel@tonic-gate scferr(); 3309*0Sstevel@tonic-gate goto bail; 3310*0Sstevel@tonic-gate } 3311*0Sstevel@tonic-gate } 3312*0Sstevel@tonic-gate 3313*0Sstevel@tonic-gate if ((inst = scf_instance_create(h)) == NULL || 3314*0Sstevel@tonic-gate (pg = scf_pg_create(h)) == NULL || 3315*0Sstevel@tonic-gate (val = scf_value_create(h)) == NULL || 3316*0Sstevel@tonic-gate (tx = scf_transaction_create(h)) == NULL || 3317*0Sstevel@tonic-gate (ent = scf_entry_create(h)) == NULL) { 3318*0Sstevel@tonic-gate scferr(); 3319*0Sstevel@tonic-gate goto bail; 3320*0Sstevel@tonic-gate } 3321*0Sstevel@tonic-gate 3322*0Sstevel@tonic-gate get_inst: 3323*0Sstevel@tonic-gate r = get_or_add_startd(inst); 3324*0Sstevel@tonic-gate switch (r) { 3325*0Sstevel@tonic-gate case 0: 3326*0Sstevel@tonic-gate break; 3327*0Sstevel@tonic-gate 3328*0Sstevel@tonic-gate case ENOMEM: 3329*0Sstevel@tonic-gate case ECONNABORTED: 3330*0Sstevel@tonic-gate case EPERM: 3331*0Sstevel@tonic-gate case EACCES: 3332*0Sstevel@tonic-gate case EROFS: 3333*0Sstevel@tonic-gate scferr(); 3334*0Sstevel@tonic-gate goto bail; 3335*0Sstevel@tonic-gate default: 3336*0Sstevel@tonic-gate bad_error("get_or_add_startd", r); 3337*0Sstevel@tonic-gate } 3338*0Sstevel@tonic-gate 3339*0Sstevel@tonic-gate get_pg: 3340*0Sstevel@tonic-gate if (scf_instance_get_pg(inst, SCF_PG_OPTIONS_OVR, pg) != 0) { 3341*0Sstevel@tonic-gate switch (scf_error()) { 3342*0Sstevel@tonic-gate case SCF_ERROR_CONNECTION_BROKEN: 3343*0Sstevel@tonic-gate scferr(); 3344*0Sstevel@tonic-gate goto bail; 3345*0Sstevel@tonic-gate 3346*0Sstevel@tonic-gate case SCF_ERROR_DELETED: 3347*0Sstevel@tonic-gate goto get_inst; 3348*0Sstevel@tonic-gate 3349*0Sstevel@tonic-gate case SCF_ERROR_NOT_FOUND: 3350*0Sstevel@tonic-gate break; 3351*0Sstevel@tonic-gate 3352*0Sstevel@tonic-gate case SCF_ERROR_HANDLE_MISMATCH: 3353*0Sstevel@tonic-gate case SCF_ERROR_INVALID_ARGUMENT: 3354*0Sstevel@tonic-gate case SCF_ERROR_NOT_SET: 3355*0Sstevel@tonic-gate default: 3356*0Sstevel@tonic-gate bad_error("scf_instance_get_pg", scf_error()); 3357*0Sstevel@tonic-gate } 3358*0Sstevel@tonic-gate 3359*0Sstevel@tonic-gate add_pg: 3360*0Sstevel@tonic-gate if (scf_instance_add_pg(inst, SCF_PG_OPTIONS_OVR, 3361*0Sstevel@tonic-gate SCF_PG_OPTIONS_OVR_TYPE, SCF_PG_OPTIONS_OVR_FLAGS, pg) != 3362*0Sstevel@tonic-gate 0) { 3363*0Sstevel@tonic-gate switch (scf_error()) { 3364*0Sstevel@tonic-gate case SCF_ERROR_CONNECTION_BROKEN: 3365*0Sstevel@tonic-gate case SCF_ERROR_PERMISSION_DENIED: 3366*0Sstevel@tonic-gate case SCF_ERROR_BACKEND_ACCESS: 3367*0Sstevel@tonic-gate scferr(); 3368*0Sstevel@tonic-gate goto bail; 3369*0Sstevel@tonic-gate 3370*0Sstevel@tonic-gate case SCF_ERROR_DELETED: 3371*0Sstevel@tonic-gate goto get_inst; 3372*0Sstevel@tonic-gate 3373*0Sstevel@tonic-gate case SCF_ERROR_EXISTS: 3374*0Sstevel@tonic-gate goto get_pg; 3375*0Sstevel@tonic-gate 3376*0Sstevel@tonic-gate case SCF_ERROR_HANDLE_MISMATCH: 3377*0Sstevel@tonic-gate case SCF_ERROR_INVALID_ARGUMENT: 3378*0Sstevel@tonic-gate case SCF_ERROR_NOT_SET: 3379*0Sstevel@tonic-gate default: 3380*0Sstevel@tonic-gate bad_error("scf_instance_add_pg", scf_error()); 3381*0Sstevel@tonic-gate } 3382*0Sstevel@tonic-gate } 3383*0Sstevel@tonic-gate } 3384*0Sstevel@tonic-gate 3385*0Sstevel@tonic-gate buf[0] = rl; 3386*0Sstevel@tonic-gate buf[1] = '\0'; 3387*0Sstevel@tonic-gate r = scf_value_set_astring(val, buf); 3388*0Sstevel@tonic-gate assert(r == 0); 3389*0Sstevel@tonic-gate 3390*0Sstevel@tonic-gate for (;;) { 3391*0Sstevel@tonic-gate if (scf_transaction_start(tx, pg) != 0) { 3392*0Sstevel@tonic-gate switch (scf_error()) { 3393*0Sstevel@tonic-gate case SCF_ERROR_CONNECTION_BROKEN: 3394*0Sstevel@tonic-gate case SCF_ERROR_PERMISSION_DENIED: 3395*0Sstevel@tonic-gate case SCF_ERROR_BACKEND_ACCESS: 3396*0Sstevel@tonic-gate scferr(); 3397*0Sstevel@tonic-gate goto bail; 3398*0Sstevel@tonic-gate 3399*0Sstevel@tonic-gate case SCF_ERROR_DELETED: 3400*0Sstevel@tonic-gate goto add_pg; 3401*0Sstevel@tonic-gate 3402*0Sstevel@tonic-gate case SCF_ERROR_HANDLE_MISMATCH: 3403*0Sstevel@tonic-gate case SCF_ERROR_NOT_BOUND: 3404*0Sstevel@tonic-gate case SCF_ERROR_IN_USE: 3405*0Sstevel@tonic-gate case SCF_ERROR_NOT_SET: 3406*0Sstevel@tonic-gate default: 3407*0Sstevel@tonic-gate bad_error("scf_transaction_start", scf_error()); 3408*0Sstevel@tonic-gate } 3409*0Sstevel@tonic-gate } 3410*0Sstevel@tonic-gate 3411*0Sstevel@tonic-gate r = transaction_add_set(tx, ent, "runlevel", SCF_TYPE_ASTRING); 3412*0Sstevel@tonic-gate switch (r) { 3413*0Sstevel@tonic-gate case 0: 3414*0Sstevel@tonic-gate break; 3415*0Sstevel@tonic-gate 3416*0Sstevel@tonic-gate case ECONNABORTED: 3417*0Sstevel@tonic-gate scferr(); 3418*0Sstevel@tonic-gate goto bail; 3419*0Sstevel@tonic-gate 3420*0Sstevel@tonic-gate case ECANCELED: 3421*0Sstevel@tonic-gate scf_transaction_reset(tx); 3422*0Sstevel@tonic-gate goto add_pg; 3423*0Sstevel@tonic-gate 3424*0Sstevel@tonic-gate default: 3425*0Sstevel@tonic-gate bad_error("transaction_add_set", r); 3426*0Sstevel@tonic-gate } 3427*0Sstevel@tonic-gate 3428*0Sstevel@tonic-gate r = scf_entry_add_value(ent, val); 3429*0Sstevel@tonic-gate assert(r == 0); 3430*0Sstevel@tonic-gate 3431*0Sstevel@tonic-gate r = scf_transaction_commit(tx); 3432*0Sstevel@tonic-gate if (r == 1) 3433*0Sstevel@tonic-gate break; 3434*0Sstevel@tonic-gate 3435*0Sstevel@tonic-gate if (r != 0) { 3436*0Sstevel@tonic-gate switch (scf_error()) { 3437*0Sstevel@tonic-gate case SCF_ERROR_CONNECTION_BROKEN: 3438*0Sstevel@tonic-gate case SCF_ERROR_PERMISSION_DENIED: 3439*0Sstevel@tonic-gate case SCF_ERROR_BACKEND_ACCESS: 3440*0Sstevel@tonic-gate case SCF_ERROR_BACKEND_READONLY: 3441*0Sstevel@tonic-gate scferr(); 3442*0Sstevel@tonic-gate goto bail; 3443*0Sstevel@tonic-gate 3444*0Sstevel@tonic-gate case SCF_ERROR_DELETED: 3445*0Sstevel@tonic-gate scf_transaction_reset(tx); 3446*0Sstevel@tonic-gate goto add_pg; 3447*0Sstevel@tonic-gate 3448*0Sstevel@tonic-gate case SCF_ERROR_INVALID_ARGUMENT: 3449*0Sstevel@tonic-gate case SCF_ERROR_NOT_BOUND: 3450*0Sstevel@tonic-gate case SCF_ERROR_NOT_SET: 3451*0Sstevel@tonic-gate default: 3452*0Sstevel@tonic-gate bad_error("scf_transaction_commit", 3453*0Sstevel@tonic-gate scf_error()); 3454*0Sstevel@tonic-gate } 3455*0Sstevel@tonic-gate } 3456*0Sstevel@tonic-gate 3457*0Sstevel@tonic-gate scf_transaction_reset(tx); 3458*0Sstevel@tonic-gate (void) scf_pg_update(pg); 3459*0Sstevel@tonic-gate } 3460*0Sstevel@tonic-gate 3461*0Sstevel@tonic-gate bail: 3462*0Sstevel@tonic-gate scf_transaction_destroy(tx); 3463*0Sstevel@tonic-gate scf_entry_destroy(ent); 3464*0Sstevel@tonic-gate scf_value_destroy(val); 3465*0Sstevel@tonic-gate scf_pg_destroy(pg); 3466*0Sstevel@tonic-gate scf_instance_destroy(inst); 3467*0Sstevel@tonic-gate 3468*0Sstevel@tonic-gate (void) scf_handle_unbind(h); 3469*0Sstevel@tonic-gate scf_handle_destroy(h); 3470*0Sstevel@tonic-gate } 3471*0Sstevel@tonic-gate 3472*0Sstevel@tonic-gate /* 3473*0Sstevel@tonic-gate * Function to handle requests from users to main init running as process 1. 3474*0Sstevel@tonic-gate */ 3475*0Sstevel@tonic-gate static void 3476*0Sstevel@tonic-gate userinit(int argc, char **argv) 3477*0Sstevel@tonic-gate { 3478*0Sstevel@tonic-gate FILE *fp; 3479*0Sstevel@tonic-gate char *ln; 3480*0Sstevel@tonic-gate int init_signal; 3481*0Sstevel@tonic-gate struct stat sconbuf, conbuf; 3482*0Sstevel@tonic-gate int turnoff = 0; 3483*0Sstevel@tonic-gate const char *usage_msg = "Usage: init [0123456SsQqabc]\n"; 3484*0Sstevel@tonic-gate 3485*0Sstevel@tonic-gate /* 3486*0Sstevel@tonic-gate * We are a user invoked init. Is there an argument and is it 3487*0Sstevel@tonic-gate * a single character? If not, print usage message and quit. 3488*0Sstevel@tonic-gate */ 3489*0Sstevel@tonic-gate if (argc != 2 || argv[1][1] != '\0') { 3490*0Sstevel@tonic-gate (void) fprintf(stderr, usage_msg); 3491*0Sstevel@tonic-gate exit(0); 3492*0Sstevel@tonic-gate } 3493*0Sstevel@tonic-gate 3494*0Sstevel@tonic-gate if ((init_signal = lvlname_to_state((char)argv[1][0])) == -1) { 3495*0Sstevel@tonic-gate (void) fprintf(stderr, usage_msg); 3496*0Sstevel@tonic-gate (void) audit_put_record(ADT_FAILURE, ADT_FAIL_VALUE_BAD_CMD, 3497*0Sstevel@tonic-gate argv[1]); 3498*0Sstevel@tonic-gate exit(1); 3499*0Sstevel@tonic-gate } 3500*0Sstevel@tonic-gate 3501*0Sstevel@tonic-gate turnoff = LSEL_NOAUDIT & state_to_flags(init_signal); 3502*0Sstevel@tonic-gate 3503*0Sstevel@tonic-gate if (init_signal == SINGLE_USER) { 3504*0Sstevel@tonic-gate /* 3505*0Sstevel@tonic-gate * Make sure this process is talking to a legal tty line 3506*0Sstevel@tonic-gate * and that /dev/syscon is linked to this line. 3507*0Sstevel@tonic-gate */ 3508*0Sstevel@tonic-gate ln = ttyname(0); /* Get the name of tty */ 3509*0Sstevel@tonic-gate if (ln == NULL) { 3510*0Sstevel@tonic-gate (void) fprintf(stderr, 3511*0Sstevel@tonic-gate "Standard input not a tty line\n"); 3512*0Sstevel@tonic-gate (void) audit_put_record(ADT_FAILURE, 3513*0Sstevel@tonic-gate ADT_FAIL_VALUE_BAD_TTY, argv[1]); 3514*0Sstevel@tonic-gate exit(1); 3515*0Sstevel@tonic-gate } 3516*0Sstevel@tonic-gate if (stat(ln, &sconbuf) != -1 && 3517*0Sstevel@tonic-gate stat(SYSCON, &conbuf) != -1 && 3518*0Sstevel@tonic-gate sconbuf.st_rdev != conbuf.st_rdev && 3519*0Sstevel@tonic-gate sconbuf.st_ino != conbuf.st_ino) { 3520*0Sstevel@tonic-gate /* 3521*0Sstevel@tonic-gate * Unlink /dev/syscon and relink it to the current line. 3522*0Sstevel@tonic-gate */ 3523*0Sstevel@tonic-gate if (unlink(SYSCON) == FAILURE) { 3524*0Sstevel@tonic-gate perror("Can't unlink /dev/syscon"); 3525*0Sstevel@tonic-gate (void) fprintf(stderr, 3526*0Sstevel@tonic-gate "Run command on the system console.\n"); 3527*0Sstevel@tonic-gate (void) audit_put_record(ADT_FAILURE, 3528*0Sstevel@tonic-gate ADT_FAIL_VALUE_PROGRAM, argv[1]); 3529*0Sstevel@tonic-gate exit(1); 3530*0Sstevel@tonic-gate } 3531*0Sstevel@tonic-gate if (link(ln, SYSCON) == FAILURE) { 3532*0Sstevel@tonic-gate (void) fprintf(stderr, 3533*0Sstevel@tonic-gate "Can't link /dev/syscon to %s: %s", ln, 3534*0Sstevel@tonic-gate strerror(errno)); 3535*0Sstevel@tonic-gate 3536*0Sstevel@tonic-gate /* Try to leave a syscon */ 3537*0Sstevel@tonic-gate (void) link(SYSTTY, SYSCON); 3538*0Sstevel@tonic-gate (void) audit_put_record(ADT_FAILURE, 3539*0Sstevel@tonic-gate ADT_FAIL_VALUE_PROGRAM, argv[1]); 3540*0Sstevel@tonic-gate exit(1); 3541*0Sstevel@tonic-gate } 3542*0Sstevel@tonic-gate 3543*0Sstevel@tonic-gate /* 3544*0Sstevel@tonic-gate * Try to leave a message on system console saying where 3545*0Sstevel@tonic-gate * /dev/syscon is currently connected. 3546*0Sstevel@tonic-gate */ 3547*0Sstevel@tonic-gate if ((fp = fopen(SYSTTY, "r+")) != NULL) { 3548*0Sstevel@tonic-gate (void) fprintf(fp, 3549*0Sstevel@tonic-gate "\n**** SYSCON CHANGED TO %s ****\n", 3550*0Sstevel@tonic-gate ln); 3551*0Sstevel@tonic-gate (void) fclose(fp); 3552*0Sstevel@tonic-gate } 3553*0Sstevel@tonic-gate } 3554*0Sstevel@tonic-gate } 3555*0Sstevel@tonic-gate 3556*0Sstevel@tonic-gate update_boot_archive(init_signal); 3557*0Sstevel@tonic-gate 3558*0Sstevel@tonic-gate if (audit_put_record(ADT_SUCCESS, ADT_SUCCESS, argv[1]) && 3559*0Sstevel@tonic-gate turnoff) { 3560*0Sstevel@tonic-gate /* turn off audit daemon and try to flush audit queue */ 3561*0Sstevel@tonic-gate 3562*0Sstevel@tonic-gate if (system("/usr/sbin/audit -t")) { 3563*0Sstevel@tonic-gate (void) fprintf(stderr, "%s: can't turn off auditd\n", 3564*0Sstevel@tonic-gate argv[0]); 3565*0Sstevel@tonic-gate } else { 3566*0Sstevel@tonic-gate (void) sleep(5); 3567*0Sstevel@tonic-gate } 3568*0Sstevel@tonic-gate } 3569*0Sstevel@tonic-gate 3570*0Sstevel@tonic-gate /* 3571*0Sstevel@tonic-gate * Signal init; init will take care of telling svc.startd. 3572*0Sstevel@tonic-gate */ 3573*0Sstevel@tonic-gate if (kill(init_pid, init_signal) == FAILURE) { 3574*0Sstevel@tonic-gate (void) fprintf(stderr, "Must be super-user\n"); 3575*0Sstevel@tonic-gate (void) audit_put_record(ADT_FAILURE, 3576*0Sstevel@tonic-gate ADT_FAIL_VALUE_AUTH, argv[1]); 3577*0Sstevel@tonic-gate exit(1); 3578*0Sstevel@tonic-gate } 3579*0Sstevel@tonic-gate 3580*0Sstevel@tonic-gate exit(0); 3581*0Sstevel@tonic-gate } 3582*0Sstevel@tonic-gate 3583*0Sstevel@tonic-gate 3584*0Sstevel@tonic-gate #define DELTA 25 /* Number of pidlist elements to allocate at a time */ 3585*0Sstevel@tonic-gate 3586*0Sstevel@tonic-gate /* ARGSUSED */ 3587*0Sstevel@tonic-gate void 3588*0Sstevel@tonic-gate sigpoll(int n) 3589*0Sstevel@tonic-gate { 3590*0Sstevel@tonic-gate struct pidrec prec; 3591*0Sstevel@tonic-gate struct pidrec *p = ≺ 3592*0Sstevel@tonic-gate struct pidlist *plp; 3593*0Sstevel@tonic-gate struct pidlist *tp, *savetp; 3594*0Sstevel@tonic-gate int i; 3595*0Sstevel@tonic-gate 3596*0Sstevel@tonic-gate if (Pfd < 0) { 3597*0Sstevel@tonic-gate return; 3598*0Sstevel@tonic-gate } 3599*0Sstevel@tonic-gate (void) sigset(SIGCLD, SIG_DFL); 3600*0Sstevel@tonic-gate for (;;) { 3601*0Sstevel@tonic-gate /* 3602*0Sstevel@tonic-gate * Important Note: Either read will really fail (in which case 3603*0Sstevel@tonic-gate * return is all we can do) or will get EAGAIN (Pfd was opened 3604*0Sstevel@tonic-gate * O_NDELAY), in which case we also want to return. 3605*0Sstevel@tonic-gate * Always return from here! 3606*0Sstevel@tonic-gate */ 3607*0Sstevel@tonic-gate if (read(Pfd, p, sizeof (struct pidrec)) != 3608*0Sstevel@tonic-gate sizeof (struct pidrec)) { 3609*0Sstevel@tonic-gate (void) sigset(SIGCLD, childeath); 3610*0Sstevel@tonic-gate return; 3611*0Sstevel@tonic-gate } 3612*0Sstevel@tonic-gate switch (p->pd_type) { 3613*0Sstevel@tonic-gate 3614*0Sstevel@tonic-gate case ADDPID: 3615*0Sstevel@tonic-gate /* 3616*0Sstevel@tonic-gate * New "godchild", add to list. 3617*0Sstevel@tonic-gate */ 3618*0Sstevel@tonic-gate if (Plfree == NULL) { 3619*0Sstevel@tonic-gate plp = (struct pidlist *)calloc(DELTA, 3620*0Sstevel@tonic-gate sizeof (struct pidlist)); 3621*0Sstevel@tonic-gate if (plp == NULL) { 3622*0Sstevel@tonic-gate /* Can't save pid */ 3623*0Sstevel@tonic-gate break; 3624*0Sstevel@tonic-gate } 3625*0Sstevel@tonic-gate /* 3626*0Sstevel@tonic-gate * Point at 2nd record allocated, we'll use plp. 3627*0Sstevel@tonic-gate */ 3628*0Sstevel@tonic-gate tp = plp + 1; 3629*0Sstevel@tonic-gate /* 3630*0Sstevel@tonic-gate * Link them into a chain. 3631*0Sstevel@tonic-gate */ 3632*0Sstevel@tonic-gate Plfree = tp; 3633*0Sstevel@tonic-gate for (i = 0; i < DELTA - 2; i++) { 3634*0Sstevel@tonic-gate tp->pl_next = tp + 1; 3635*0Sstevel@tonic-gate tp++; 3636*0Sstevel@tonic-gate } 3637*0Sstevel@tonic-gate } else { 3638*0Sstevel@tonic-gate plp = Plfree; 3639*0Sstevel@tonic-gate Plfree = plp->pl_next; 3640*0Sstevel@tonic-gate } 3641*0Sstevel@tonic-gate plp->pl_pid = p->pd_pid; 3642*0Sstevel@tonic-gate plp->pl_dflag = 0; 3643*0Sstevel@tonic-gate plp->pl_next = NULL; 3644*0Sstevel@tonic-gate /* 3645*0Sstevel@tonic-gate * Note - pid list is kept in increasing order of pids. 3646*0Sstevel@tonic-gate */ 3647*0Sstevel@tonic-gate if (Plhead == NULL) { 3648*0Sstevel@tonic-gate Plhead = plp; 3649*0Sstevel@tonic-gate /* Back up to read next record */ 3650*0Sstevel@tonic-gate break; 3651*0Sstevel@tonic-gate } else { 3652*0Sstevel@tonic-gate savetp = tp = Plhead; 3653*0Sstevel@tonic-gate while (tp) { 3654*0Sstevel@tonic-gate if (plp->pl_pid > tp->pl_pid) { 3655*0Sstevel@tonic-gate savetp = tp; 3656*0Sstevel@tonic-gate tp = tp->pl_next; 3657*0Sstevel@tonic-gate continue; 3658*0Sstevel@tonic-gate } else if (plp->pl_pid < tp->pl_pid) { 3659*0Sstevel@tonic-gate if (tp == Plhead) { 3660*0Sstevel@tonic-gate plp->pl_next = Plhead; 3661*0Sstevel@tonic-gate Plhead = plp; 3662*0Sstevel@tonic-gate } else { 3663*0Sstevel@tonic-gate plp->pl_next = 3664*0Sstevel@tonic-gate savetp->pl_next; 3665*0Sstevel@tonic-gate savetp->pl_next = plp; 3666*0Sstevel@tonic-gate } 3667*0Sstevel@tonic-gate break; 3668*0Sstevel@tonic-gate } else { 3669*0Sstevel@tonic-gate /* Already in list! */ 3670*0Sstevel@tonic-gate plp->pl_next = Plfree; 3671*0Sstevel@tonic-gate Plfree = plp; 3672*0Sstevel@tonic-gate break; 3673*0Sstevel@tonic-gate } 3674*0Sstevel@tonic-gate } 3675*0Sstevel@tonic-gate if (tp == NULL) { 3676*0Sstevel@tonic-gate /* Add to end of list */ 3677*0Sstevel@tonic-gate savetp->pl_next = plp; 3678*0Sstevel@tonic-gate } 3679*0Sstevel@tonic-gate } 3680*0Sstevel@tonic-gate /* Back up to read next record. */ 3681*0Sstevel@tonic-gate break; 3682*0Sstevel@tonic-gate 3683*0Sstevel@tonic-gate case REMPID: 3684*0Sstevel@tonic-gate /* 3685*0Sstevel@tonic-gate * This one was handled by someone else, 3686*0Sstevel@tonic-gate * purge it from the list. 3687*0Sstevel@tonic-gate */ 3688*0Sstevel@tonic-gate if (Plhead == NULL) { 3689*0Sstevel@tonic-gate /* Back up to read next record. */ 3690*0Sstevel@tonic-gate break; 3691*0Sstevel@tonic-gate } 3692*0Sstevel@tonic-gate savetp = tp = Plhead; 3693*0Sstevel@tonic-gate while (tp) { 3694*0Sstevel@tonic-gate if (p->pd_pid > tp->pl_pid) { 3695*0Sstevel@tonic-gate /* Keep on looking. */ 3696*0Sstevel@tonic-gate savetp = tp; 3697*0Sstevel@tonic-gate tp = tp->pl_next; 3698*0Sstevel@tonic-gate continue; 3699*0Sstevel@tonic-gate } else if (p->pd_pid < tp->pl_pid) { 3700*0Sstevel@tonic-gate /* Not in list. */ 3701*0Sstevel@tonic-gate break; 3702*0Sstevel@tonic-gate } else { 3703*0Sstevel@tonic-gate /* Found it. */ 3704*0Sstevel@tonic-gate if (tp == Plhead) 3705*0Sstevel@tonic-gate Plhead = tp->pl_next; 3706*0Sstevel@tonic-gate else 3707*0Sstevel@tonic-gate savetp->pl_next = tp->pl_next; 3708*0Sstevel@tonic-gate tp->pl_next = Plfree; 3709*0Sstevel@tonic-gate Plfree = tp; 3710*0Sstevel@tonic-gate break; 3711*0Sstevel@tonic-gate } 3712*0Sstevel@tonic-gate } 3713*0Sstevel@tonic-gate /* Back up to read next record. */ 3714*0Sstevel@tonic-gate break; 3715*0Sstevel@tonic-gate default: 3716*0Sstevel@tonic-gate console(B_TRUE, "Bad message on initpipe\n"); 3717*0Sstevel@tonic-gate break; 3718*0Sstevel@tonic-gate } 3719*0Sstevel@tonic-gate } 3720*0Sstevel@tonic-gate } 3721*0Sstevel@tonic-gate 3722*0Sstevel@tonic-gate 3723*0Sstevel@tonic-gate static void 3724*0Sstevel@tonic-gate cleanaux() 3725*0Sstevel@tonic-gate { 3726*0Sstevel@tonic-gate struct pidlist *savep, *p; 3727*0Sstevel@tonic-gate pid_t pid; 3728*0Sstevel@tonic-gate short status; 3729*0Sstevel@tonic-gate 3730*0Sstevel@tonic-gate (void) sigset(SIGCLD, SIG_DFL); 3731*0Sstevel@tonic-gate Gchild = 0; /* Note - Safe to do this here since no SIGCLDs */ 3732*0Sstevel@tonic-gate (void) sighold(SIGPOLL); 3733*0Sstevel@tonic-gate savep = p = Plhead; 3734*0Sstevel@tonic-gate while (p) { 3735*0Sstevel@tonic-gate if (p->pl_dflag) { 3736*0Sstevel@tonic-gate /* 3737*0Sstevel@tonic-gate * Found an entry to delete, 3738*0Sstevel@tonic-gate * remove it from list first. 3739*0Sstevel@tonic-gate */ 3740*0Sstevel@tonic-gate pid = p->pl_pid; 3741*0Sstevel@tonic-gate status = p->pl_exit; 3742*0Sstevel@tonic-gate if (p == Plhead) { 3743*0Sstevel@tonic-gate Plhead = p->pl_next; 3744*0Sstevel@tonic-gate p->pl_next = Plfree; 3745*0Sstevel@tonic-gate Plfree = p; 3746*0Sstevel@tonic-gate savep = p = Plhead; 3747*0Sstevel@tonic-gate } else { 3748*0Sstevel@tonic-gate savep->pl_next = p->pl_next; 3749*0Sstevel@tonic-gate p->pl_next = Plfree; 3750*0Sstevel@tonic-gate Plfree = p; 3751*0Sstevel@tonic-gate p = savep->pl_next; 3752*0Sstevel@tonic-gate } 3753*0Sstevel@tonic-gate clearent(pid, status); 3754*0Sstevel@tonic-gate continue; 3755*0Sstevel@tonic-gate } 3756*0Sstevel@tonic-gate savep = p; 3757*0Sstevel@tonic-gate p = p->pl_next; 3758*0Sstevel@tonic-gate } 3759*0Sstevel@tonic-gate (void) sigrelse(SIGPOLL); 3760*0Sstevel@tonic-gate (void) sigset(SIGCLD, childeath); 3761*0Sstevel@tonic-gate } 3762*0Sstevel@tonic-gate 3763*0Sstevel@tonic-gate 3764*0Sstevel@tonic-gate /* 3765*0Sstevel@tonic-gate * /etc/inittab has more entries and we have run out of room in the proc_table 3766*0Sstevel@tonic-gate * array. Double the size of proc_table to accomodate the extra entries. 3767*0Sstevel@tonic-gate */ 3768*0Sstevel@tonic-gate static void 3769*0Sstevel@tonic-gate increase_proc_table_size() 3770*0Sstevel@tonic-gate { 3771*0Sstevel@tonic-gate sigset_t block, unblock; 3772*0Sstevel@tonic-gate void *ptr; 3773*0Sstevel@tonic-gate size_t delta = num_proc * sizeof (struct PROC_TABLE); 3774*0Sstevel@tonic-gate 3775*0Sstevel@tonic-gate 3776*0Sstevel@tonic-gate /* 3777*0Sstevel@tonic-gate * Block signals for realloc. 3778*0Sstevel@tonic-gate */ 3779*0Sstevel@tonic-gate (void) sigfillset(&block); 3780*0Sstevel@tonic-gate (void) sigprocmask(SIG_BLOCK, &block, &unblock); 3781*0Sstevel@tonic-gate 3782*0Sstevel@tonic-gate 3783*0Sstevel@tonic-gate /* 3784*0Sstevel@tonic-gate * On failure we just return because callers of this function check 3785*0Sstevel@tonic-gate * for failure. 3786*0Sstevel@tonic-gate */ 3787*0Sstevel@tonic-gate do 3788*0Sstevel@tonic-gate ptr = realloc(g_state, g_state_sz + delta); 3789*0Sstevel@tonic-gate while (ptr == NULL && errno == EAGAIN); 3790*0Sstevel@tonic-gate 3791*0Sstevel@tonic-gate if (ptr != NULL) { 3792*0Sstevel@tonic-gate /* ensure that the new part is initialized to zero */ 3793*0Sstevel@tonic-gate bzero((caddr_t)ptr + g_state_sz, delta); 3794*0Sstevel@tonic-gate 3795*0Sstevel@tonic-gate g_state = ptr; 3796*0Sstevel@tonic-gate g_state_sz += delta; 3797*0Sstevel@tonic-gate num_proc <<= 1; 3798*0Sstevel@tonic-gate } 3799*0Sstevel@tonic-gate 3800*0Sstevel@tonic-gate 3801*0Sstevel@tonic-gate /* unblock our signals before returning */ 3802*0Sstevel@tonic-gate (void) sigprocmask(SIG_SETMASK, &unblock, NULL); 3803*0Sstevel@tonic-gate } 3804*0Sstevel@tonic-gate 3805*0Sstevel@tonic-gate 3806*0Sstevel@tonic-gate 3807*0Sstevel@tonic-gate /* 3808*0Sstevel@tonic-gate * Sanity check g_state. 3809*0Sstevel@tonic-gate */ 3810*0Sstevel@tonic-gate static int 3811*0Sstevel@tonic-gate st_sane() 3812*0Sstevel@tonic-gate { 3813*0Sstevel@tonic-gate int i; 3814*0Sstevel@tonic-gate struct PROC_TABLE *ptp; 3815*0Sstevel@tonic-gate 3816*0Sstevel@tonic-gate 3817*0Sstevel@tonic-gate /* Note: cur_state is encoded as a signal number */ 3818*0Sstevel@tonic-gate if (cur_state < 1 || cur_state == 9 || cur_state > 13) 3819*0Sstevel@tonic-gate return (0); 3820*0Sstevel@tonic-gate 3821*0Sstevel@tonic-gate /* Check num_proc */ 3822*0Sstevel@tonic-gate if (g_state_sz != sizeof (struct init_state) + (num_proc - 1) * 3823*0Sstevel@tonic-gate sizeof (struct PROC_TABLE)) 3824*0Sstevel@tonic-gate return (0); 3825*0Sstevel@tonic-gate 3826*0Sstevel@tonic-gate /* Check proc_table */ 3827*0Sstevel@tonic-gate for (i = 0, ptp = proc_table; i < num_proc; ++i, ++ptp) { 3828*0Sstevel@tonic-gate /* skip unoccupied entries */ 3829*0Sstevel@tonic-gate if (!(ptp->p_flags & OCCUPIED)) 3830*0Sstevel@tonic-gate continue; 3831*0Sstevel@tonic-gate 3832*0Sstevel@tonic-gate /* p_flags has no bits outside of PF_MASK */ 3833*0Sstevel@tonic-gate if (ptp->p_flags & ~(PF_MASK)) 3834*0Sstevel@tonic-gate return (0); 3835*0Sstevel@tonic-gate 3836*0Sstevel@tonic-gate /* 5 <= pid <= MAXPID */ 3837*0Sstevel@tonic-gate if (ptp->p_pid < 5 || ptp->p_pid > MAXPID) 3838*0Sstevel@tonic-gate return (0); 3839*0Sstevel@tonic-gate 3840*0Sstevel@tonic-gate /* p_count >= 0 */ 3841*0Sstevel@tonic-gate if (ptp->p_count < 0) 3842*0Sstevel@tonic-gate return (0); 3843*0Sstevel@tonic-gate 3844*0Sstevel@tonic-gate /* p_time >= 0 */ 3845*0Sstevel@tonic-gate if (ptp->p_time < 0) 3846*0Sstevel@tonic-gate return (0); 3847*0Sstevel@tonic-gate } 3848*0Sstevel@tonic-gate 3849*0Sstevel@tonic-gate return (1); 3850*0Sstevel@tonic-gate } 3851*0Sstevel@tonic-gate 3852*0Sstevel@tonic-gate /* 3853*0Sstevel@tonic-gate * Initialize our state. 3854*0Sstevel@tonic-gate * 3855*0Sstevel@tonic-gate * If the system just booted, then init_state_file, which is located on an 3856*0Sstevel@tonic-gate * everpresent tmpfs filesystem, should not exist. 3857*0Sstevel@tonic-gate * 3858*0Sstevel@tonic-gate * If we were restarted, then init_state_file should exist, in 3859*0Sstevel@tonic-gate * which case we'll read it in, sanity check it, and use it. 3860*0Sstevel@tonic-gate * 3861*0Sstevel@tonic-gate * Note: You can't call console() until proc_table is ready. 3862*0Sstevel@tonic-gate */ 3863*0Sstevel@tonic-gate void 3864*0Sstevel@tonic-gate st_init() 3865*0Sstevel@tonic-gate { 3866*0Sstevel@tonic-gate struct stat stb; 3867*0Sstevel@tonic-gate int ret, st_fd, insane = 0; 3868*0Sstevel@tonic-gate size_t to_be_read; 3869*0Sstevel@tonic-gate char *ptr; 3870*0Sstevel@tonic-gate 3871*0Sstevel@tonic-gate 3872*0Sstevel@tonic-gate booting = 1; 3873*0Sstevel@tonic-gate 3874*0Sstevel@tonic-gate do { 3875*0Sstevel@tonic-gate /* 3876*0Sstevel@tonic-gate * If we can exclusively create the file, then we're the 3877*0Sstevel@tonic-gate * initial invocation of init(1M). 3878*0Sstevel@tonic-gate */ 3879*0Sstevel@tonic-gate st_fd = open(init_state_file, O_RDWR | O_CREAT | O_EXCL, 3880*0Sstevel@tonic-gate S_IRUSR | S_IWUSR); 3881*0Sstevel@tonic-gate } while (st_fd == -1 && errno == EINTR); 3882*0Sstevel@tonic-gate if (st_fd != -1) 3883*0Sstevel@tonic-gate goto new_state; 3884*0Sstevel@tonic-gate 3885*0Sstevel@tonic-gate booting = 0; 3886*0Sstevel@tonic-gate 3887*0Sstevel@tonic-gate do { 3888*0Sstevel@tonic-gate st_fd = open(init_state_file, O_RDWR, S_IRUSR | S_IWUSR); 3889*0Sstevel@tonic-gate } while (st_fd == -1 && errno == EINTR); 3890*0Sstevel@tonic-gate if (st_fd == -1) 3891*0Sstevel@tonic-gate goto new_state; 3892*0Sstevel@tonic-gate 3893*0Sstevel@tonic-gate /* Get the size of the file. */ 3894*0Sstevel@tonic-gate do 3895*0Sstevel@tonic-gate ret = fstat(st_fd, &stb); 3896*0Sstevel@tonic-gate while (ret == -1 && errno == EINTR); 3897*0Sstevel@tonic-gate if (ret == -1) 3898*0Sstevel@tonic-gate goto new_state; 3899*0Sstevel@tonic-gate 3900*0Sstevel@tonic-gate do 3901*0Sstevel@tonic-gate g_state = malloc(stb.st_size); 3902*0Sstevel@tonic-gate while (g_state == NULL && errno == EAGAIN); 3903*0Sstevel@tonic-gate if (g_state == NULL) 3904*0Sstevel@tonic-gate goto new_state; 3905*0Sstevel@tonic-gate 3906*0Sstevel@tonic-gate to_be_read = stb.st_size; 3907*0Sstevel@tonic-gate ptr = (char *)g_state; 3908*0Sstevel@tonic-gate while (to_be_read > 0) { 3909*0Sstevel@tonic-gate ssize_t read_ret; 3910*0Sstevel@tonic-gate 3911*0Sstevel@tonic-gate read_ret = read(st_fd, ptr, to_be_read); 3912*0Sstevel@tonic-gate if (read_ret < 0) { 3913*0Sstevel@tonic-gate if (errno == EINTR) 3914*0Sstevel@tonic-gate continue; 3915*0Sstevel@tonic-gate 3916*0Sstevel@tonic-gate goto new_state; 3917*0Sstevel@tonic-gate } 3918*0Sstevel@tonic-gate 3919*0Sstevel@tonic-gate to_be_read -= read_ret; 3920*0Sstevel@tonic-gate ptr += read_ret; 3921*0Sstevel@tonic-gate } 3922*0Sstevel@tonic-gate 3923*0Sstevel@tonic-gate (void) close(st_fd); 3924*0Sstevel@tonic-gate 3925*0Sstevel@tonic-gate g_state_sz = stb.st_size; 3926*0Sstevel@tonic-gate 3927*0Sstevel@tonic-gate if (st_sane()) { 3928*0Sstevel@tonic-gate console(B_TRUE, "Restarting.\n"); 3929*0Sstevel@tonic-gate return; 3930*0Sstevel@tonic-gate } 3931*0Sstevel@tonic-gate 3932*0Sstevel@tonic-gate insane = 1; 3933*0Sstevel@tonic-gate 3934*0Sstevel@tonic-gate new_state: 3935*0Sstevel@tonic-gate if (st_fd >= 0) 3936*0Sstevel@tonic-gate (void) close(st_fd); 3937*0Sstevel@tonic-gate else 3938*0Sstevel@tonic-gate (void) unlink(init_state_file); 3939*0Sstevel@tonic-gate 3940*0Sstevel@tonic-gate if (g_state != NULL) 3941*0Sstevel@tonic-gate free(g_state); 3942*0Sstevel@tonic-gate 3943*0Sstevel@tonic-gate /* Something went wrong, so allocate new state. */ 3944*0Sstevel@tonic-gate g_state_sz = sizeof (struct init_state) + 3945*0Sstevel@tonic-gate ((init_num_proc - 1) * sizeof (struct PROC_TABLE)); 3946*0Sstevel@tonic-gate do 3947*0Sstevel@tonic-gate g_state = calloc(1, g_state_sz); 3948*0Sstevel@tonic-gate while (g_state == NULL && errno == EAGAIN); 3949*0Sstevel@tonic-gate if (g_state == NULL) { 3950*0Sstevel@tonic-gate /* Fatal error! */ 3951*0Sstevel@tonic-gate exit(errno); 3952*0Sstevel@tonic-gate } 3953*0Sstevel@tonic-gate 3954*0Sstevel@tonic-gate g_state->ist_runlevel = -1; 3955*0Sstevel@tonic-gate num_proc = init_num_proc; 3956*0Sstevel@tonic-gate 3957*0Sstevel@tonic-gate if (!booting) { 3958*0Sstevel@tonic-gate console(B_TRUE, "Restarting.\n"); 3959*0Sstevel@tonic-gate 3960*0Sstevel@tonic-gate /* Overwrite the bad state file. */ 3961*0Sstevel@tonic-gate st_write(); 3962*0Sstevel@tonic-gate 3963*0Sstevel@tonic-gate if (!insane) { 3964*0Sstevel@tonic-gate console(B_TRUE, 3965*0Sstevel@tonic-gate "Error accessing persistent state file `%s'. " 3966*0Sstevel@tonic-gate "Ignored.\n", init_state_file); 3967*0Sstevel@tonic-gate } else { 3968*0Sstevel@tonic-gate console(B_TRUE, 3969*0Sstevel@tonic-gate "Persistent state file `%s' is invalid and was " 3970*0Sstevel@tonic-gate "ignored.\n", init_state_file); 3971*0Sstevel@tonic-gate } 3972*0Sstevel@tonic-gate } 3973*0Sstevel@tonic-gate } 3974*0Sstevel@tonic-gate 3975*0Sstevel@tonic-gate /* 3976*0Sstevel@tonic-gate * Write g_state out to the state file. 3977*0Sstevel@tonic-gate */ 3978*0Sstevel@tonic-gate void 3979*0Sstevel@tonic-gate st_write() 3980*0Sstevel@tonic-gate { 3981*0Sstevel@tonic-gate static int complained = 0; 3982*0Sstevel@tonic-gate 3983*0Sstevel@tonic-gate int st_fd; 3984*0Sstevel@tonic-gate char *cp; 3985*0Sstevel@tonic-gate size_t sz; 3986*0Sstevel@tonic-gate ssize_t ret; 3987*0Sstevel@tonic-gate 3988*0Sstevel@tonic-gate 3989*0Sstevel@tonic-gate do { 3990*0Sstevel@tonic-gate st_fd = open(init_next_state_file, 3991*0Sstevel@tonic-gate O_WRONLY | O_CREAT | O_TRUNC, S_IRUSR | S_IWUSR); 3992*0Sstevel@tonic-gate } while (st_fd < 0 && errno == EINTR); 3993*0Sstevel@tonic-gate if (st_fd < 0) 3994*0Sstevel@tonic-gate goto err; 3995*0Sstevel@tonic-gate 3996*0Sstevel@tonic-gate cp = (char *)g_state; 3997*0Sstevel@tonic-gate sz = g_state_sz; 3998*0Sstevel@tonic-gate while (sz > 0) { 3999*0Sstevel@tonic-gate ret = write(st_fd, cp, sz); 4000*0Sstevel@tonic-gate if (ret < 0) { 4001*0Sstevel@tonic-gate if (errno == EINTR) 4002*0Sstevel@tonic-gate continue; 4003*0Sstevel@tonic-gate 4004*0Sstevel@tonic-gate goto err; 4005*0Sstevel@tonic-gate } 4006*0Sstevel@tonic-gate 4007*0Sstevel@tonic-gate sz -= ret; 4008*0Sstevel@tonic-gate cp += ret; 4009*0Sstevel@tonic-gate } 4010*0Sstevel@tonic-gate 4011*0Sstevel@tonic-gate (void) close(st_fd); 4012*0Sstevel@tonic-gate st_fd = -1; 4013*0Sstevel@tonic-gate if (rename(init_next_state_file, init_state_file)) { 4014*0Sstevel@tonic-gate (void) unlink(init_next_state_file); 4015*0Sstevel@tonic-gate goto err; 4016*0Sstevel@tonic-gate } 4017*0Sstevel@tonic-gate complained = 0; 4018*0Sstevel@tonic-gate 4019*0Sstevel@tonic-gate return; 4020*0Sstevel@tonic-gate 4021*0Sstevel@tonic-gate err: 4022*0Sstevel@tonic-gate if (st_fd >= 0) 4023*0Sstevel@tonic-gate (void) close(st_fd); 4024*0Sstevel@tonic-gate 4025*0Sstevel@tonic-gate if (!booting && !complained) { 4026*0Sstevel@tonic-gate /* 4027*0Sstevel@tonic-gate * Only complain after the filesystem should have come up. 4028*0Sstevel@tonic-gate * And only do it once so we don't loop between console() 4029*0Sstevel@tonic-gate * & efork(). 4030*0Sstevel@tonic-gate */ 4031*0Sstevel@tonic-gate complained = 1; 4032*0Sstevel@tonic-gate if (st_fd) 4033*0Sstevel@tonic-gate console(B_TRUE, "Couldn't write persistent state " 4034*0Sstevel@tonic-gate "file `%s'.\n", init_state_file); 4035*0Sstevel@tonic-gate else 4036*0Sstevel@tonic-gate console(B_TRUE, "Couldn't move persistent state " 4037*0Sstevel@tonic-gate "file `%s' to `%s'.\n", init_next_state_file, 4038*0Sstevel@tonic-gate init_state_file); 4039*0Sstevel@tonic-gate } 4040*0Sstevel@tonic-gate } 4041*0Sstevel@tonic-gate 4042*0Sstevel@tonic-gate /* 4043*0Sstevel@tonic-gate * Create a contract with these parameters. 4044*0Sstevel@tonic-gate */ 4045*0Sstevel@tonic-gate static int 4046*0Sstevel@tonic-gate contract_make_template(uint_t info, uint_t critical, uint_t fatal, 4047*0Sstevel@tonic-gate uint64_t cookie) 4048*0Sstevel@tonic-gate { 4049*0Sstevel@tonic-gate int fd, err; 4050*0Sstevel@tonic-gate 4051*0Sstevel@tonic-gate char *ioctl_tset_emsg = 4052*0Sstevel@tonic-gate "Couldn't set \"%s\" contract template parameter: %s.\n"; 4053*0Sstevel@tonic-gate 4054*0Sstevel@tonic-gate do 4055*0Sstevel@tonic-gate fd = open64(CTFS_ROOT "/process/template", O_RDWR); 4056*0Sstevel@tonic-gate while (fd < 0 && errno == EINTR); 4057*0Sstevel@tonic-gate if (fd < 0) { 4058*0Sstevel@tonic-gate console(B_TRUE, "Couldn't create process template: %s.\n", 4059*0Sstevel@tonic-gate strerror(errno)); 4060*0Sstevel@tonic-gate return (-1); 4061*0Sstevel@tonic-gate } 4062*0Sstevel@tonic-gate 4063*0Sstevel@tonic-gate if (err = ct_pr_tmpl_set_param(fd, CT_PR_INHERIT | CT_PR_REGENT)) 4064*0Sstevel@tonic-gate console(B_TRUE, "Contract set template inherit, regent " 4065*0Sstevel@tonic-gate "failed.\n"); 4066*0Sstevel@tonic-gate 4067*0Sstevel@tonic-gate /* 4068*0Sstevel@tonic-gate * These errors result in a misconfigured template, which is better 4069*0Sstevel@tonic-gate * than no template at all, so warn but don't abort. 4070*0Sstevel@tonic-gate */ 4071*0Sstevel@tonic-gate if (err = ct_tmpl_set_informative(fd, info)) 4072*0Sstevel@tonic-gate console(B_TRUE, ioctl_tset_emsg, "informative", strerror(err)); 4073*0Sstevel@tonic-gate 4074*0Sstevel@tonic-gate if (err = ct_tmpl_set_critical(fd, critical)) 4075*0Sstevel@tonic-gate console(B_TRUE, ioctl_tset_emsg, "critical", strerror(err)); 4076*0Sstevel@tonic-gate 4077*0Sstevel@tonic-gate if (err = ct_pr_tmpl_set_fatal(fd, fatal)) 4078*0Sstevel@tonic-gate console(B_TRUE, ioctl_tset_emsg, "fatal", strerror(err)); 4079*0Sstevel@tonic-gate 4080*0Sstevel@tonic-gate if (err = ct_tmpl_set_cookie(fd, cookie)) 4081*0Sstevel@tonic-gate console(B_TRUE, ioctl_tset_emsg, "cookie", strerror(err)); 4082*0Sstevel@tonic-gate 4083*0Sstevel@tonic-gate (void) fcntl(fd, F_SETFD, FD_CLOEXEC); 4084*0Sstevel@tonic-gate 4085*0Sstevel@tonic-gate return (fd); 4086*0Sstevel@tonic-gate } 4087*0Sstevel@tonic-gate 4088*0Sstevel@tonic-gate /* 4089*0Sstevel@tonic-gate * Create the templates and open an event file descriptor. We use dup2(2) to 4090*0Sstevel@tonic-gate * get these descriptors away from the stdin/stdout/stderr group. 4091*0Sstevel@tonic-gate */ 4092*0Sstevel@tonic-gate static void 4093*0Sstevel@tonic-gate contracts_init() 4094*0Sstevel@tonic-gate { 4095*0Sstevel@tonic-gate int err, fd; 4096*0Sstevel@tonic-gate 4097*0Sstevel@tonic-gate /* 4098*0Sstevel@tonic-gate * Create & configure a legacy template. We only want empty events so 4099*0Sstevel@tonic-gate * we know when to abandon them. 4100*0Sstevel@tonic-gate */ 4101*0Sstevel@tonic-gate legacy_tmpl = contract_make_template(0, CT_PR_EV_EMPTY, CT_PR_EV_HWERR, 4102*0Sstevel@tonic-gate ORDINARY_COOKIE); 4103*0Sstevel@tonic-gate if (legacy_tmpl >= 0) { 4104*0Sstevel@tonic-gate err = ct_tmpl_activate(legacy_tmpl); 4105*0Sstevel@tonic-gate if (err != 0) { 4106*0Sstevel@tonic-gate (void) close(legacy_tmpl); 4107*0Sstevel@tonic-gate legacy_tmpl = -1; 4108*0Sstevel@tonic-gate console(B_TRUE, 4109*0Sstevel@tonic-gate "Couldn't activate legacy template (%s); " 4110*0Sstevel@tonic-gate "legacy services will be in init's contract.\n", 4111*0Sstevel@tonic-gate strerror(err)); 4112*0Sstevel@tonic-gate } 4113*0Sstevel@tonic-gate } else 4114*0Sstevel@tonic-gate console(B_TRUE, 4115*0Sstevel@tonic-gate "Legacy services will be in init's contract.\n"); 4116*0Sstevel@tonic-gate 4117*0Sstevel@tonic-gate if (dup2(legacy_tmpl, 255) == -1) { 4118*0Sstevel@tonic-gate console(B_TRUE, "Could not duplicate legacy template: %s.\n", 4119*0Sstevel@tonic-gate strerror(errno)); 4120*0Sstevel@tonic-gate } else { 4121*0Sstevel@tonic-gate (void) close(legacy_tmpl); 4122*0Sstevel@tonic-gate legacy_tmpl = 255; 4123*0Sstevel@tonic-gate } 4124*0Sstevel@tonic-gate 4125*0Sstevel@tonic-gate (void) fcntl(legacy_tmpl, F_SETFD, FD_CLOEXEC); 4126*0Sstevel@tonic-gate 4127*0Sstevel@tonic-gate startd_tmpl = contract_make_template(0, CT_PR_EV_EMPTY, 4128*0Sstevel@tonic-gate CT_PR_EV_HWERR | CT_PR_EV_SIGNAL | CT_PR_EV_CORE, STARTD_COOKIE); 4129*0Sstevel@tonic-gate 4130*0Sstevel@tonic-gate if (dup2(startd_tmpl, 254) == -1) { 4131*0Sstevel@tonic-gate console(B_TRUE, "Could not duplicate startd template: %s.\n", 4132*0Sstevel@tonic-gate strerror(errno)); 4133*0Sstevel@tonic-gate } else { 4134*0Sstevel@tonic-gate (void) close(startd_tmpl); 4135*0Sstevel@tonic-gate startd_tmpl = 254; 4136*0Sstevel@tonic-gate } 4137*0Sstevel@tonic-gate 4138*0Sstevel@tonic-gate (void) fcntl(startd_tmpl, F_SETFD, FD_CLOEXEC); 4139*0Sstevel@tonic-gate 4140*0Sstevel@tonic-gate if (legacy_tmpl < 0 && startd_tmpl < 0) { 4141*0Sstevel@tonic-gate /* The creation errors have already been reported. */ 4142*0Sstevel@tonic-gate console(B_TRUE, 4143*0Sstevel@tonic-gate "Ignoring contract events. Core smf(5) services will not " 4144*0Sstevel@tonic-gate "be restarted.\n"); 4145*0Sstevel@tonic-gate return; 4146*0Sstevel@tonic-gate } 4147*0Sstevel@tonic-gate 4148*0Sstevel@tonic-gate /* 4149*0Sstevel@tonic-gate * Open an event endpoint. 4150*0Sstevel@tonic-gate */ 4151*0Sstevel@tonic-gate do 4152*0Sstevel@tonic-gate fd = open64(CTFS_ROOT "/process/pbundle", O_RDONLY); 4153*0Sstevel@tonic-gate while (fd < 0 && errno == EINTR); 4154*0Sstevel@tonic-gate if (fd < 0) { 4155*0Sstevel@tonic-gate console(B_TRUE, 4156*0Sstevel@tonic-gate "Couldn't open process pbundle: %s. Core smf(5) services " 4157*0Sstevel@tonic-gate "will not be restarted.\n", strerror(errno)); 4158*0Sstevel@tonic-gate return; 4159*0Sstevel@tonic-gate } 4160*0Sstevel@tonic-gate 4161*0Sstevel@tonic-gate if (dup2(fd, 253) == -1) { 4162*0Sstevel@tonic-gate console(B_TRUE, "Could not duplicate process bundle: %s.\n", 4163*0Sstevel@tonic-gate strerror(errno)); 4164*0Sstevel@tonic-gate } else { 4165*0Sstevel@tonic-gate (void) close(fd); 4166*0Sstevel@tonic-gate fd = 253; 4167*0Sstevel@tonic-gate } 4168*0Sstevel@tonic-gate 4169*0Sstevel@tonic-gate (void) fcntl(fd, F_SETFD, FD_CLOEXEC); 4170*0Sstevel@tonic-gate 4171*0Sstevel@tonic-gate /* Reset in case we've been restarted. */ 4172*0Sstevel@tonic-gate (void) ct_event_reset(fd); 4173*0Sstevel@tonic-gate 4174*0Sstevel@tonic-gate poll_fds[0].fd = fd; 4175*0Sstevel@tonic-gate poll_fds[0].events = POLLIN; 4176*0Sstevel@tonic-gate poll_nfds = 1; 4177*0Sstevel@tonic-gate } 4178*0Sstevel@tonic-gate 4179*0Sstevel@tonic-gate static int 4180*0Sstevel@tonic-gate contract_getfile(ctid_t id, const char *name, int oflag) 4181*0Sstevel@tonic-gate { 4182*0Sstevel@tonic-gate int fd; 4183*0Sstevel@tonic-gate 4184*0Sstevel@tonic-gate do 4185*0Sstevel@tonic-gate fd = contract_open(id, "process", name, oflag); 4186*0Sstevel@tonic-gate while (fd < 0 && errno == EINTR); 4187*0Sstevel@tonic-gate 4188*0Sstevel@tonic-gate if (fd < 0) 4189*0Sstevel@tonic-gate console(B_TRUE, "Couldn't open %s for contract %ld: %s.\n", 4190*0Sstevel@tonic-gate name, id, strerror(errno)); 4191*0Sstevel@tonic-gate 4192*0Sstevel@tonic-gate return (fd); 4193*0Sstevel@tonic-gate } 4194*0Sstevel@tonic-gate 4195*0Sstevel@tonic-gate static int 4196*0Sstevel@tonic-gate contract_cookie(ctid_t id, uint64_t *cp) 4197*0Sstevel@tonic-gate { 4198*0Sstevel@tonic-gate int fd, err; 4199*0Sstevel@tonic-gate ct_stathdl_t sh; 4200*0Sstevel@tonic-gate 4201*0Sstevel@tonic-gate fd = contract_getfile(id, "status", O_RDONLY); 4202*0Sstevel@tonic-gate if (fd < 0) 4203*0Sstevel@tonic-gate return (-1); 4204*0Sstevel@tonic-gate 4205*0Sstevel@tonic-gate err = ct_status_read(fd, CTD_COMMON, &sh); 4206*0Sstevel@tonic-gate if (err != 0) { 4207*0Sstevel@tonic-gate console(B_TRUE, "Couldn't read status of contract %ld: %s.\n", 4208*0Sstevel@tonic-gate id, strerror(err)); 4209*0Sstevel@tonic-gate (void) close(fd); 4210*0Sstevel@tonic-gate return (-1); 4211*0Sstevel@tonic-gate } 4212*0Sstevel@tonic-gate 4213*0Sstevel@tonic-gate (void) close(fd); 4214*0Sstevel@tonic-gate 4215*0Sstevel@tonic-gate *cp = ct_status_get_cookie(sh); 4216*0Sstevel@tonic-gate 4217*0Sstevel@tonic-gate ct_status_free(sh); 4218*0Sstevel@tonic-gate return (0); 4219*0Sstevel@tonic-gate } 4220*0Sstevel@tonic-gate 4221*0Sstevel@tonic-gate static void 4222*0Sstevel@tonic-gate contract_ack(ct_evthdl_t e) 4223*0Sstevel@tonic-gate { 4224*0Sstevel@tonic-gate int fd; 4225*0Sstevel@tonic-gate 4226*0Sstevel@tonic-gate if (ct_event_get_flags(e) & CTE_INFO) 4227*0Sstevel@tonic-gate return; 4228*0Sstevel@tonic-gate 4229*0Sstevel@tonic-gate fd = contract_getfile(ct_event_get_ctid(e), "ctl", O_WRONLY); 4230*0Sstevel@tonic-gate if (fd < 0) 4231*0Sstevel@tonic-gate return; 4232*0Sstevel@tonic-gate 4233*0Sstevel@tonic-gate (void) ct_ctl_ack(fd, ct_event_get_evid(e)); 4234*0Sstevel@tonic-gate (void) close(fd); 4235*0Sstevel@tonic-gate } 4236*0Sstevel@tonic-gate 4237*0Sstevel@tonic-gate /* 4238*0Sstevel@tonic-gate * Process a contract event. 4239*0Sstevel@tonic-gate */ 4240*0Sstevel@tonic-gate static void 4241*0Sstevel@tonic-gate contract_event(struct pollfd *poll) 4242*0Sstevel@tonic-gate { 4243*0Sstevel@tonic-gate ct_evthdl_t e; 4244*0Sstevel@tonic-gate int err; 4245*0Sstevel@tonic-gate ctid_t ctid; 4246*0Sstevel@tonic-gate 4247*0Sstevel@tonic-gate if (!(poll->revents & POLLIN)) { 4248*0Sstevel@tonic-gate if (poll->revents & POLLERR) 4249*0Sstevel@tonic-gate console(B_TRUE, 4250*0Sstevel@tonic-gate "Unknown poll error on my process contract " 4251*0Sstevel@tonic-gate "pbundle.\n"); 4252*0Sstevel@tonic-gate return; 4253*0Sstevel@tonic-gate } 4254*0Sstevel@tonic-gate 4255*0Sstevel@tonic-gate err = ct_event_read(poll->fd, &e); 4256*0Sstevel@tonic-gate if (err != 0) { 4257*0Sstevel@tonic-gate console(B_TRUE, "Error retrieving contract event: %s.\n", 4258*0Sstevel@tonic-gate strerror(err)); 4259*0Sstevel@tonic-gate return; 4260*0Sstevel@tonic-gate } 4261*0Sstevel@tonic-gate 4262*0Sstevel@tonic-gate ctid = ct_event_get_ctid(e); 4263*0Sstevel@tonic-gate 4264*0Sstevel@tonic-gate if (ct_event_get_type(e) == CT_PR_EV_EMPTY) { 4265*0Sstevel@tonic-gate uint64_t cookie; 4266*0Sstevel@tonic-gate int ret, abandon = 1; 4267*0Sstevel@tonic-gate 4268*0Sstevel@tonic-gate /* If it's svc.startd, restart it. Else, abandon. */ 4269*0Sstevel@tonic-gate ret = contract_cookie(ctid, &cookie); 4270*0Sstevel@tonic-gate 4271*0Sstevel@tonic-gate if (ret == 0) { 4272*0Sstevel@tonic-gate if (cookie == STARTD_COOKIE && 4273*0Sstevel@tonic-gate do_restart_startd) { 4274*0Sstevel@tonic-gate if (smf_debug) 4275*0Sstevel@tonic-gate console(B_TRUE, "Restarting " 4276*0Sstevel@tonic-gate "svc.startd.\n"); 4277*0Sstevel@tonic-gate 4278*0Sstevel@tonic-gate /* 4279*0Sstevel@tonic-gate * Account for the failure. If the failure rate 4280*0Sstevel@tonic-gate * exceeds a threshold, then drop to maintenance 4281*0Sstevel@tonic-gate * mode. 4282*0Sstevel@tonic-gate */ 4283*0Sstevel@tonic-gate startd_record_failure(); 4284*0Sstevel@tonic-gate if (startd_failure_rate_critical()) 4285*0Sstevel@tonic-gate enter_maintenance(); 4286*0Sstevel@tonic-gate 4287*0Sstevel@tonic-gate if (startd_tmpl < 0) 4288*0Sstevel@tonic-gate console(B_TRUE, 4289*0Sstevel@tonic-gate "Restarting svc.startd in " 4290*0Sstevel@tonic-gate "improper contract (bad " 4291*0Sstevel@tonic-gate "template).\n"); 4292*0Sstevel@tonic-gate 4293*0Sstevel@tonic-gate (void) startd_run(startd_cline, startd_tmpl, 4294*0Sstevel@tonic-gate ctid); 4295*0Sstevel@tonic-gate 4296*0Sstevel@tonic-gate abandon = 0; 4297*0Sstevel@tonic-gate } 4298*0Sstevel@tonic-gate } 4299*0Sstevel@tonic-gate 4300*0Sstevel@tonic-gate if (abandon && (err = contract_abandon_id(ctid))) { 4301*0Sstevel@tonic-gate console(B_TRUE, "Couldn't abandon contract %ld: %s.\n", 4302*0Sstevel@tonic-gate ctid, strerror(err)); 4303*0Sstevel@tonic-gate } 4304*0Sstevel@tonic-gate 4305*0Sstevel@tonic-gate /* 4306*0Sstevel@tonic-gate * No need to acknowledge the event since either way the 4307*0Sstevel@tonic-gate * originating contract should be abandoned. 4308*0Sstevel@tonic-gate */ 4309*0Sstevel@tonic-gate } else { 4310*0Sstevel@tonic-gate console(B_TRUE, 4311*0Sstevel@tonic-gate "Received contract event of unexpected type %d from " 4312*0Sstevel@tonic-gate "contract %ld.\n", ct_event_get_type(e), ctid); 4313*0Sstevel@tonic-gate 4314*0Sstevel@tonic-gate if ((ct_event_get_flags(e) & (CTE_INFO | CTE_ACK)) == 0) 4315*0Sstevel@tonic-gate /* Allow unexpected critical events to be released. */ 4316*0Sstevel@tonic-gate contract_ack(e); 4317*0Sstevel@tonic-gate } 4318*0Sstevel@tonic-gate 4319*0Sstevel@tonic-gate ct_event_free(e); 4320*0Sstevel@tonic-gate } 4321*0Sstevel@tonic-gate 4322*0Sstevel@tonic-gate /* 4323*0Sstevel@tonic-gate * svc.startd(1M) Management 4324*0Sstevel@tonic-gate */ 4325*0Sstevel@tonic-gate 4326*0Sstevel@tonic-gate /* 4327*0Sstevel@tonic-gate * (Re)start svc.startd(1M). old_ctid should be the contract ID of the old 4328*0Sstevel@tonic-gate * contract, or 0 if we're starting it for the first time. If wait is true 4329*0Sstevel@tonic-gate * we'll wait for and return the exit value of the child. 4330*0Sstevel@tonic-gate */ 4331*0Sstevel@tonic-gate static int 4332*0Sstevel@tonic-gate startd_run(const char *cline, int tmpl, ctid_t old_ctid) 4333*0Sstevel@tonic-gate { 4334*0Sstevel@tonic-gate int err, i, ret, did_activate; 4335*0Sstevel@tonic-gate pid_t pid; 4336*0Sstevel@tonic-gate struct stat sb; 4337*0Sstevel@tonic-gate 4338*0Sstevel@tonic-gate if (cline[0] == '\0') 4339*0Sstevel@tonic-gate return (-1); 4340*0Sstevel@tonic-gate 4341*0Sstevel@tonic-gate /* 4342*0Sstevel@tonic-gate * Don't restart startd if the system is rebooting or shutting down. 4343*0Sstevel@tonic-gate */ 4344*0Sstevel@tonic-gate do { 4345*0Sstevel@tonic-gate ret = stat("/etc/svc/volatile/resetting", &sb); 4346*0Sstevel@tonic-gate } while (ret == -1 && errno == EINTR); 4347*0Sstevel@tonic-gate 4348*0Sstevel@tonic-gate if (ret == 0) { 4349*0Sstevel@tonic-gate if (smf_debug) 4350*0Sstevel@tonic-gate console(B_TRUE, "Quiescing for reboot.\n"); 4351*0Sstevel@tonic-gate (void) pause(); 4352*0Sstevel@tonic-gate return (-1); 4353*0Sstevel@tonic-gate } 4354*0Sstevel@tonic-gate 4355*0Sstevel@tonic-gate err = ct_pr_tmpl_set_transfer(tmpl, old_ctid); 4356*0Sstevel@tonic-gate if (err == EINVAL) { 4357*0Sstevel@tonic-gate console(B_TRUE, "Remake startd_tmpl; reattempt transfer.\n"); 4358*0Sstevel@tonic-gate tmpl = startd_tmpl = contract_make_template(0, CT_PR_EV_EMPTY, 4359*0Sstevel@tonic-gate CT_PR_EV_HWERR, STARTD_COOKIE); 4360*0Sstevel@tonic-gate 4361*0Sstevel@tonic-gate err = ct_pr_tmpl_set_transfer(tmpl, old_ctid); 4362*0Sstevel@tonic-gate } 4363*0Sstevel@tonic-gate if (err != 0) { 4364*0Sstevel@tonic-gate console(B_TRUE, 4365*0Sstevel@tonic-gate "Couldn't set transfer parameter of contract template: " 4366*0Sstevel@tonic-gate "%s.\n", strerror(err)); 4367*0Sstevel@tonic-gate } 4368*0Sstevel@tonic-gate 4369*0Sstevel@tonic-gate did_activate = !(ct_tmpl_activate(tmpl)); 4370*0Sstevel@tonic-gate if (!did_activate) 4371*0Sstevel@tonic-gate console(B_TRUE, 4372*0Sstevel@tonic-gate "Template activation failed; not starting \"%s\" in " 4373*0Sstevel@tonic-gate "proper contract.\n", cline); 4374*0Sstevel@tonic-gate 4375*0Sstevel@tonic-gate /* Hold SIGCHLD so we can wait if necessary. */ 4376*0Sstevel@tonic-gate (void) sighold(SIGCHLD); 4377*0Sstevel@tonic-gate 4378*0Sstevel@tonic-gate while ((pid = fork()) < 0) { 4379*0Sstevel@tonic-gate if (errno == EPERM) { 4380*0Sstevel@tonic-gate console(B_TRUE, "Insufficient permission to fork.\n"); 4381*0Sstevel@tonic-gate 4382*0Sstevel@tonic-gate /* Now that's a doozy. */ 4383*0Sstevel@tonic-gate exit(1); 4384*0Sstevel@tonic-gate } 4385*0Sstevel@tonic-gate 4386*0Sstevel@tonic-gate console(B_TRUE, 4387*0Sstevel@tonic-gate "fork() for svc.startd failed: %s. Will retry in 1 " 4388*0Sstevel@tonic-gate "second...\n", strerror(errno)); 4389*0Sstevel@tonic-gate 4390*0Sstevel@tonic-gate (void) sleep(1); 4391*0Sstevel@tonic-gate 4392*0Sstevel@tonic-gate /* Eventually give up? */ 4393*0Sstevel@tonic-gate } 4394*0Sstevel@tonic-gate 4395*0Sstevel@tonic-gate if (pid == 0) { 4396*0Sstevel@tonic-gate /* child */ 4397*0Sstevel@tonic-gate 4398*0Sstevel@tonic-gate /* See the comment in efork() */ 4399*0Sstevel@tonic-gate for (i = SIGHUP; i <= SIGRTMAX; ++i) { 4400*0Sstevel@tonic-gate if (i == SIGTTOU || i == SIGTTIN || i == SIGTSTP) 4401*0Sstevel@tonic-gate (void) sigset(i, SIG_IGN); 4402*0Sstevel@tonic-gate else 4403*0Sstevel@tonic-gate (void) sigset(i, SIG_DFL); 4404*0Sstevel@tonic-gate } 4405*0Sstevel@tonic-gate 4406*0Sstevel@tonic-gate if (smf_options != NULL) { 4407*0Sstevel@tonic-gate /* Put smf_options in the environment. */ 4408*0Sstevel@tonic-gate glob_envp[glob_envn] = 4409*0Sstevel@tonic-gate malloc(sizeof ("SMF_OPTIONS=") - 1 + 4410*0Sstevel@tonic-gate strlen(smf_options) + 1); 4411*0Sstevel@tonic-gate 4412*0Sstevel@tonic-gate if (glob_envp[glob_envn] != NULL) { 4413*0Sstevel@tonic-gate /* LINTED */ 4414*0Sstevel@tonic-gate (void) sprintf(glob_envp[glob_envn], 4415*0Sstevel@tonic-gate "SMF_OPTIONS=%s", smf_options); 4416*0Sstevel@tonic-gate glob_envp[glob_envn+1] = NULL; 4417*0Sstevel@tonic-gate } else { 4418*0Sstevel@tonic-gate console(B_TRUE, 4419*0Sstevel@tonic-gate "Could not set SMF_OPTIONS (%s).\n", 4420*0Sstevel@tonic-gate strerror(errno)); 4421*0Sstevel@tonic-gate } 4422*0Sstevel@tonic-gate } 4423*0Sstevel@tonic-gate 4424*0Sstevel@tonic-gate if (smf_debug) 4425*0Sstevel@tonic-gate console(B_TRUE, "Executing svc.startd\n"); 4426*0Sstevel@tonic-gate 4427*0Sstevel@tonic-gate (void) execle(SH, "INITSH", "-c", cline, NULL, glob_envp); 4428*0Sstevel@tonic-gate 4429*0Sstevel@tonic-gate console(B_TRUE, "Could not exec \"%s\" (%s).\n", SH, 4430*0Sstevel@tonic-gate strerror(errno)); 4431*0Sstevel@tonic-gate 4432*0Sstevel@tonic-gate exit(1); 4433*0Sstevel@tonic-gate } 4434*0Sstevel@tonic-gate 4435*0Sstevel@tonic-gate /* parent */ 4436*0Sstevel@tonic-gate 4437*0Sstevel@tonic-gate if (did_activate) { 4438*0Sstevel@tonic-gate if (legacy_tmpl < 0 || ct_tmpl_activate(legacy_tmpl) != 0) 4439*0Sstevel@tonic-gate (void) ct_tmpl_clear(tmpl); 4440*0Sstevel@tonic-gate } 4441*0Sstevel@tonic-gate 4442*0Sstevel@tonic-gate /* Clear the old_ctid reference so the kernel can reclaim it. */ 4443*0Sstevel@tonic-gate if (old_ctid != 0) 4444*0Sstevel@tonic-gate (void) ct_pr_tmpl_set_transfer(tmpl, 0); 4445*0Sstevel@tonic-gate 4446*0Sstevel@tonic-gate (void) sigrelse(SIGCHLD); 4447*0Sstevel@tonic-gate 4448*0Sstevel@tonic-gate return (0); 4449*0Sstevel@tonic-gate } 4450*0Sstevel@tonic-gate 4451*0Sstevel@tonic-gate /* 4452*0Sstevel@tonic-gate * void startd_record_failure(void) 4453*0Sstevel@tonic-gate * Place the current time in our circular array of svc.startd failures. 4454*0Sstevel@tonic-gate */ 4455*0Sstevel@tonic-gate void 4456*0Sstevel@tonic-gate startd_record_failure() 4457*0Sstevel@tonic-gate { 4458*0Sstevel@tonic-gate int index = startd_failure_index++ % NSTARTD_FAILURE_TIMES; 4459*0Sstevel@tonic-gate 4460*0Sstevel@tonic-gate startd_failure_time[index] = gethrtime(); 4461*0Sstevel@tonic-gate } 4462*0Sstevel@tonic-gate 4463*0Sstevel@tonic-gate /* 4464*0Sstevel@tonic-gate * int startd_failure_rate_critical(void) 4465*0Sstevel@tonic-gate * Return true if the average failure interval is less than the permitted 4466*0Sstevel@tonic-gate * interval. Implicit success if insufficient measurements for an average 4467*0Sstevel@tonic-gate * exist. 4468*0Sstevel@tonic-gate */ 4469*0Sstevel@tonic-gate int 4470*0Sstevel@tonic-gate startd_failure_rate_critical() 4471*0Sstevel@tonic-gate { 4472*0Sstevel@tonic-gate int n = startd_failure_index; 4473*0Sstevel@tonic-gate hrtime_t avg_ns = 0; 4474*0Sstevel@tonic-gate 4475*0Sstevel@tonic-gate if (startd_failure_index < NSTARTD_FAILURE_TIMES) 4476*0Sstevel@tonic-gate return (0); 4477*0Sstevel@tonic-gate 4478*0Sstevel@tonic-gate avg_ns = 4479*0Sstevel@tonic-gate (startd_failure_time[(n - 1) % NSTARTD_FAILURE_TIMES] - 4480*0Sstevel@tonic-gate startd_failure_time[n % NSTARTD_FAILURE_TIMES]) / 4481*0Sstevel@tonic-gate NSTARTD_FAILURE_TIMES; 4482*0Sstevel@tonic-gate 4483*0Sstevel@tonic-gate return (avg_ns < STARTD_FAILURE_RATE_NS); 4484*0Sstevel@tonic-gate } 4485*0Sstevel@tonic-gate 4486*0Sstevel@tonic-gate /* 4487*0Sstevel@tonic-gate * returns string that must be free'd 4488*0Sstevel@tonic-gate */ 4489*0Sstevel@tonic-gate 4490*0Sstevel@tonic-gate static char 4491*0Sstevel@tonic-gate *audit_boot_msg() 4492*0Sstevel@tonic-gate { 4493*0Sstevel@tonic-gate char *b, *p; 4494*0Sstevel@tonic-gate char desc[] = "booted"; 4495*0Sstevel@tonic-gate zoneid_t zid = getzoneid(); 4496*0Sstevel@tonic-gate 4497*0Sstevel@tonic-gate b = malloc(sizeof (desc) + MAXNAMELEN + 3); 4498*0Sstevel@tonic-gate if (b == NULL) 4499*0Sstevel@tonic-gate return (b); 4500*0Sstevel@tonic-gate 4501*0Sstevel@tonic-gate p = b; 4502*0Sstevel@tonic-gate p += strlcpy(p, desc, sizeof (desc)); 4503*0Sstevel@tonic-gate if (zid != GLOBAL_ZONEID) { 4504*0Sstevel@tonic-gate p += strlcpy(p, ": ", 3); 4505*0Sstevel@tonic-gate (void) getzonenamebyid(zid, p, MAXNAMELEN); 4506*0Sstevel@tonic-gate } 4507*0Sstevel@tonic-gate return (b); 4508*0Sstevel@tonic-gate } 4509*0Sstevel@tonic-gate 4510*0Sstevel@tonic-gate /* 4511*0Sstevel@tonic-gate * Generate AUE_init_solaris audit record. Return 1 if 4512*0Sstevel@tonic-gate * auditing is enabled in case the caller cares. 4513*0Sstevel@tonic-gate * 4514*0Sstevel@tonic-gate * In the case of userint() or a local zone invocation of 4515*0Sstevel@tonic-gate * one_true_init, the process initially contains the audit 4516*0Sstevel@tonic-gate * characteristics of the process that invoked init. The first pass 4517*0Sstevel@tonic-gate * through here uses those characteristics then for the case of 4518*0Sstevel@tonic-gate * one_true_init in a local zone, clears them so subsequent system 4519*0Sstevel@tonic-gate * state changes won't be attributed to the person who booted the 4520*0Sstevel@tonic-gate * zone. 4521*0Sstevel@tonic-gate */ 4522*0Sstevel@tonic-gate static int 4523*0Sstevel@tonic-gate audit_put_record(int pass_fail, int status, char *msg) 4524*0Sstevel@tonic-gate { 4525*0Sstevel@tonic-gate adt_session_data_t *ah; 4526*0Sstevel@tonic-gate adt_event_data_t *event; 4527*0Sstevel@tonic-gate 4528*0Sstevel@tonic-gate if (!adt_audit_enabled()) 4529*0Sstevel@tonic-gate return (0); 4530*0Sstevel@tonic-gate 4531*0Sstevel@tonic-gate /* 4532*0Sstevel@tonic-gate * the PROC_DATA picks up the context to tell whether this is 4533*0Sstevel@tonic-gate * an attributed record (auid = -2 is unattributed) 4534*0Sstevel@tonic-gate */ 4535*0Sstevel@tonic-gate if (adt_start_session(&ah, NULL, ADT_USE_PROC_DATA)) { 4536*0Sstevel@tonic-gate console(B_TRUE, "audit failure: %s\n", strerror(errno)); 4537*0Sstevel@tonic-gate return (1); 4538*0Sstevel@tonic-gate } 4539*0Sstevel@tonic-gate event = adt_alloc_event(ah, ADT_init_solaris); 4540*0Sstevel@tonic-gate if (event == NULL) { 4541*0Sstevel@tonic-gate console(B_TRUE, "audit failure: %s\n", strerror(errno)); 4542*0Sstevel@tonic-gate (void) adt_end_session(ah); 4543*0Sstevel@tonic-gate return (1); 4544*0Sstevel@tonic-gate } 4545*0Sstevel@tonic-gate event->adt_init_solaris.info = msg; /* NULL is ok here */ 4546*0Sstevel@tonic-gate 4547*0Sstevel@tonic-gate if (adt_put_event(event, pass_fail, status)) { 4548*0Sstevel@tonic-gate console(B_TRUE, "audit failure: %s\n", strerror(errno)); 4549*0Sstevel@tonic-gate (void) adt_end_session(ah); 4550*0Sstevel@tonic-gate return (1); 4551*0Sstevel@tonic-gate } 4552*0Sstevel@tonic-gate adt_free_event(event); 4553*0Sstevel@tonic-gate 4554*0Sstevel@tonic-gate (void) adt_end_session(ah); 4555*0Sstevel@tonic-gate 4556*0Sstevel@tonic-gate return (1); 4557*0Sstevel@tonic-gate } 4558