10Sstevel@tonic-gate /* 20Sstevel@tonic-gate * CDDL HEADER START 30Sstevel@tonic-gate * 40Sstevel@tonic-gate * The contents of this file are subject to the terms of the 51401Smjnelson * Common Development and Distribution License (the "License"). 61401Smjnelson * You may not use this file except in compliance with the License. 70Sstevel@tonic-gate * 80Sstevel@tonic-gate * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 90Sstevel@tonic-gate * or http://www.opensolaris.org/os/licensing. 100Sstevel@tonic-gate * See the License for the specific language governing permissions 110Sstevel@tonic-gate * and limitations under the License. 120Sstevel@tonic-gate * 130Sstevel@tonic-gate * When distributing Covered Code, include this CDDL HEADER in each 140Sstevel@tonic-gate * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 150Sstevel@tonic-gate * If applicable, add the following below this CDDL HEADER, with the 160Sstevel@tonic-gate * fields enclosed by brackets "[]" replaced with your own identifying 170Sstevel@tonic-gate * information: Portions Copyright [yyyy] [name of copyright owner] 180Sstevel@tonic-gate * 190Sstevel@tonic-gate * CDDL HEADER END 200Sstevel@tonic-gate */ 211401Smjnelson 220Sstevel@tonic-gate /* 238576SNick.Todd@Sun.COM * Copyright 2009 Sun Microsystems, Inc. All rights reserved. 240Sstevel@tonic-gate * Use is subject to license terms. 250Sstevel@tonic-gate */ 260Sstevel@tonic-gate 270Sstevel@tonic-gate /* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */ 280Sstevel@tonic-gate /* All Rights Reserved */ 290Sstevel@tonic-gate 300Sstevel@tonic-gate /* 310Sstevel@tonic-gate * University Copyright- Copyright (c) 1982, 1986, 1988 320Sstevel@tonic-gate * The Regents of the University of California 330Sstevel@tonic-gate * All Rights Reserved 340Sstevel@tonic-gate * 350Sstevel@tonic-gate * University Acknowledgment- Portions of this document are derived from 360Sstevel@tonic-gate * software developed by the University of California, Berkeley, and its 370Sstevel@tonic-gate * contributors. 380Sstevel@tonic-gate */ 390Sstevel@tonic-gate 400Sstevel@tonic-gate /* 410Sstevel@tonic-gate * init(1M) is the general process spawning program. Its primary job is to 420Sstevel@tonic-gate * start and restart svc.startd for smf(5). For backwards-compatibility it also 430Sstevel@tonic-gate * spawns and respawns processes according to /etc/inittab and the current 440Sstevel@tonic-gate * run-level. It reads /etc/default/inittab for general configuration. 450Sstevel@tonic-gate * 460Sstevel@tonic-gate * To change run-levels the system administrator runs init from the command 470Sstevel@tonic-gate * line with a level name. init signals svc.startd via libscf and directs the 480Sstevel@tonic-gate * zone's init (pid 1 in the global zone) what to do by sending it a signal; 490Sstevel@tonic-gate * these signal numbers are commonly refered to in the code as 'states'. Valid 500Sstevel@tonic-gate * run-levels are [sS0123456]. Additionally, init can be given directives 510Sstevel@tonic-gate * [qQabc], which indicate actions to be taken pertaining to /etc/inittab. 520Sstevel@tonic-gate * 530Sstevel@tonic-gate * When init processes inittab entries, it finds processes that are to be 540Sstevel@tonic-gate * spawned at various run-levels. inittab contains the set of the levels for 550Sstevel@tonic-gate * which each inittab entry is valid. 560Sstevel@tonic-gate * 570Sstevel@tonic-gate * State File and Restartability 580Sstevel@tonic-gate * Premature exit by init(1M) is handled as a special case by the kernel: 590Sstevel@tonic-gate * init(1M) will be immediately re-executed, retaining its original PID. (PID 600Sstevel@tonic-gate * 1 in the global zone.) To track the processes it has previously spawned, 610Sstevel@tonic-gate * as well as other mutable state, init(1M) regularly updates a state file 620Sstevel@tonic-gate * such that its subsequent invocations have knowledge of its various 630Sstevel@tonic-gate * dependent processes and duties. 640Sstevel@tonic-gate * 650Sstevel@tonic-gate * Process Contracts 660Sstevel@tonic-gate * We start svc.startd(1M) in a contract and transfer inherited contracts when 670Sstevel@tonic-gate * restarting it. Everything else is started using the legacy contract 680Sstevel@tonic-gate * template, and the created contracts are abandoned when they become empty. 690Sstevel@tonic-gate * 700Sstevel@tonic-gate * utmpx Entry Handling 710Sstevel@tonic-gate * Because init(1M) no longer governs the startup process, its knowledge of 720Sstevel@tonic-gate * when utmpx becomes writable is indirect. However, spawned processes 730Sstevel@tonic-gate * expect to be constructed with valid utmpx entries. As a result, attempts 740Sstevel@tonic-gate * to write normal entries will be retried until successful. 750Sstevel@tonic-gate * 760Sstevel@tonic-gate * Maintenance Mode 770Sstevel@tonic-gate * In certain failure scenarios, init(1M) will enter a maintenance mode, in 780Sstevel@tonic-gate * which it invokes sulogin(1M) to allow the operator an opportunity to 790Sstevel@tonic-gate * repair the system. Normally, this operation is performed as a 800Sstevel@tonic-gate * fork(2)-exec(2)-waitpid(3C) sequence with the parent waiting for repair or 810Sstevel@tonic-gate * diagnosis to be completed. In the cases that fork(2) requests themselves 820Sstevel@tonic-gate * fail, init(1M) will directly execute sulogin(1M), and allow the kernel to 830Sstevel@tonic-gate * restart init(1M) on exit from the operator session. 840Sstevel@tonic-gate * 850Sstevel@tonic-gate * One scenario where init(1M) enters its maintenance mode is when 860Sstevel@tonic-gate * svc.startd(1M) begins to fail rapidly, defined as when the average time 870Sstevel@tonic-gate * between recent failures drops below a given threshold. 880Sstevel@tonic-gate */ 890Sstevel@tonic-gate 900Sstevel@tonic-gate #include <sys/contract/process.h> 910Sstevel@tonic-gate #include <sys/ctfs.h> 920Sstevel@tonic-gate #include <sys/stat.h> 930Sstevel@tonic-gate #include <sys/statvfs.h> 940Sstevel@tonic-gate #include <sys/stropts.h> 950Sstevel@tonic-gate #include <sys/systeminfo.h> 960Sstevel@tonic-gate #include <sys/time.h> 970Sstevel@tonic-gate #include <sys/termios.h> 980Sstevel@tonic-gate #include <sys/tty.h> 990Sstevel@tonic-gate #include <sys/types.h> 1000Sstevel@tonic-gate #include <sys/utsname.h> 1010Sstevel@tonic-gate 1020Sstevel@tonic-gate #include <bsm/adt_event.h> 1030Sstevel@tonic-gate #include <bsm/libbsm.h> 1040Sstevel@tonic-gate #include <security/pam_appl.h> 1050Sstevel@tonic-gate 1060Sstevel@tonic-gate #include <assert.h> 1070Sstevel@tonic-gate #include <ctype.h> 1080Sstevel@tonic-gate #include <dirent.h> 1090Sstevel@tonic-gate #include <errno.h> 1100Sstevel@tonic-gate #include <fcntl.h> 1110Sstevel@tonic-gate #include <libcontract.h> 1120Sstevel@tonic-gate #include <libcontract_priv.h> 1130Sstevel@tonic-gate #include <libintl.h> 1140Sstevel@tonic-gate #include <libscf.h> 1150Sstevel@tonic-gate #include <libscf_priv.h> 1160Sstevel@tonic-gate #include <poll.h> 1170Sstevel@tonic-gate #include <procfs.h> 1180Sstevel@tonic-gate #include <signal.h> 1190Sstevel@tonic-gate #include <stdarg.h> 1200Sstevel@tonic-gate #include <stdio.h> 1210Sstevel@tonic-gate #include <stdio_ext.h> 1220Sstevel@tonic-gate #include <stdlib.h> 1230Sstevel@tonic-gate #include <string.h> 1240Sstevel@tonic-gate #include <strings.h> 1250Sstevel@tonic-gate #include <syslog.h> 1260Sstevel@tonic-gate #include <time.h> 1270Sstevel@tonic-gate #include <ulimit.h> 1280Sstevel@tonic-gate #include <unistd.h> 1290Sstevel@tonic-gate #include <utmpx.h> 1300Sstevel@tonic-gate #include <wait.h> 1310Sstevel@tonic-gate #include <zone.h> 1320Sstevel@tonic-gate #include <ucontext.h> 1330Sstevel@tonic-gate 1340Sstevel@tonic-gate #undef sleep 1350Sstevel@tonic-gate 1360Sstevel@tonic-gate #define fioctl(p, sptr, cmd) ioctl(fileno(p), sptr, cmd) 1370Sstevel@tonic-gate #define min(a, b) (((a) < (b)) ? (a) : (b)) 1380Sstevel@tonic-gate 1390Sstevel@tonic-gate #define TRUE 1 1400Sstevel@tonic-gate #define FALSE 0 1410Sstevel@tonic-gate #define FAILURE -1 1420Sstevel@tonic-gate 1430Sstevel@tonic-gate #define UT_LINE_SZ 32 /* Size of a utmpx ut_line field */ 1440Sstevel@tonic-gate 1450Sstevel@tonic-gate /* 1460Sstevel@tonic-gate * SLEEPTIME The number of seconds "init" sleeps between wakeups if 1470Sstevel@tonic-gate * nothing else requires this "init" wakeup. 1480Sstevel@tonic-gate */ 1490Sstevel@tonic-gate #define SLEEPTIME (5 * 60) 1500Sstevel@tonic-gate 1510Sstevel@tonic-gate /* 1520Sstevel@tonic-gate * MAXCMDL The maximum length of a command string in inittab. 1530Sstevel@tonic-gate */ 1540Sstevel@tonic-gate #define MAXCMDL 512 1550Sstevel@tonic-gate 1560Sstevel@tonic-gate /* 1570Sstevel@tonic-gate * EXEC The length of the prefix string added to all comamnds 1580Sstevel@tonic-gate * found in inittab. 1590Sstevel@tonic-gate */ 1600Sstevel@tonic-gate #define EXEC (sizeof ("exec ") - 1) 1610Sstevel@tonic-gate 1620Sstevel@tonic-gate /* 1630Sstevel@tonic-gate * TWARN The amount of time between warning signal, SIGTERM, 1640Sstevel@tonic-gate * and the fatal kill signal, SIGKILL. 1650Sstevel@tonic-gate */ 1660Sstevel@tonic-gate #define TWARN 5 1670Sstevel@tonic-gate 1680Sstevel@tonic-gate #define id_eq(x, y) ((x[0] == y[0] && x[1] == y[1] && x[2] == y[2] &&\ 1690Sstevel@tonic-gate x[3] == y[3]) ? TRUE : FALSE) 1700Sstevel@tonic-gate 1710Sstevel@tonic-gate /* 1720Sstevel@tonic-gate * The kernel's default umask is 022 these days; since some processes inherit 1730Sstevel@tonic-gate * their umask from init, init will set it from CMASK in /etc/default/init. 1740Sstevel@tonic-gate * init gets the default umask from the kernel, it sets it to 022 whenever 1750Sstevel@tonic-gate * it wants to create a file and reverts to CMASK afterwards. 1760Sstevel@tonic-gate */ 1770Sstevel@tonic-gate 1780Sstevel@tonic-gate static int cmask; 1790Sstevel@tonic-gate 1800Sstevel@tonic-gate /* 1810Sstevel@tonic-gate * The following definitions, concluding with the 'lvls' array, provide a 1820Sstevel@tonic-gate * common mapping between level-name (like 'S'), signal number (state), 1830Sstevel@tonic-gate * run-level mask, and specific properties associated with a run-level. 1840Sstevel@tonic-gate * This array should be accessed using the routines lvlname_to_state(), 1850Sstevel@tonic-gate * lvlname_to_mask(), state_to_mask(), and state_to_flags(). 1860Sstevel@tonic-gate */ 1870Sstevel@tonic-gate 1880Sstevel@tonic-gate /* 1890Sstevel@tonic-gate * Correspondence of signals to init actions. 1900Sstevel@tonic-gate */ 1910Sstevel@tonic-gate #define LVLQ SIGHUP 1920Sstevel@tonic-gate #define LVL0 SIGINT 1930Sstevel@tonic-gate #define LVL1 SIGQUIT 1940Sstevel@tonic-gate #define LVL2 SIGILL 1950Sstevel@tonic-gate #define LVL3 SIGTRAP 1960Sstevel@tonic-gate #define LVL4 SIGIOT 1970Sstevel@tonic-gate #define LVL5 SIGEMT 1980Sstevel@tonic-gate #define LVL6 SIGFPE 1990Sstevel@tonic-gate #define SINGLE_USER SIGBUS 2000Sstevel@tonic-gate #define LVLa SIGSEGV 2010Sstevel@tonic-gate #define LVLb SIGSYS 2020Sstevel@tonic-gate #define LVLc SIGPIPE 2030Sstevel@tonic-gate 2040Sstevel@tonic-gate /* 2050Sstevel@tonic-gate * Bit Mask for each level. Used to determine legal levels. 2060Sstevel@tonic-gate */ 2070Sstevel@tonic-gate #define MASK0 0x0001 2080Sstevel@tonic-gate #define MASK1 0x0002 2090Sstevel@tonic-gate #define MASK2 0x0004 2100Sstevel@tonic-gate #define MASK3 0x0008 2110Sstevel@tonic-gate #define MASK4 0x0010 2120Sstevel@tonic-gate #define MASK5 0x0020 2130Sstevel@tonic-gate #define MASK6 0x0040 2140Sstevel@tonic-gate #define MASKSU 0x0080 2150Sstevel@tonic-gate #define MASKa 0x0100 2160Sstevel@tonic-gate #define MASKb 0x0200 2170Sstevel@tonic-gate #define MASKc 0x0400 2180Sstevel@tonic-gate 2190Sstevel@tonic-gate #define MASK_NUMERIC (MASK0 | MASK1 | MASK2 | MASK3 | MASK4 | MASK5 | MASK6) 2200Sstevel@tonic-gate #define MASK_abc (MASKa | MASKb | MASKc) 2210Sstevel@tonic-gate 2220Sstevel@tonic-gate /* 2230Sstevel@tonic-gate * Flags to indicate properties of various states. 2240Sstevel@tonic-gate */ 2250Sstevel@tonic-gate #define LSEL_RUNLEVEL 0x0001 /* runlevels you can transition to */ 2260Sstevel@tonic-gate 2270Sstevel@tonic-gate typedef struct lvl { 2280Sstevel@tonic-gate int lvl_state; 2290Sstevel@tonic-gate int lvl_mask; 2300Sstevel@tonic-gate char lvl_name; 2310Sstevel@tonic-gate int lvl_flags; 2320Sstevel@tonic-gate } lvl_t; 2330Sstevel@tonic-gate 2340Sstevel@tonic-gate static lvl_t lvls[] = { 2350Sstevel@tonic-gate { LVLQ, 0, 'Q', 0 }, 2360Sstevel@tonic-gate { LVLQ, 0, 'q', 0 }, 2372481Spaulson { LVL0, MASK0, '0', LSEL_RUNLEVEL }, 2382481Spaulson { LVL1, MASK1, '1', LSEL_RUNLEVEL }, 2390Sstevel@tonic-gate { LVL2, MASK2, '2', LSEL_RUNLEVEL }, 2400Sstevel@tonic-gate { LVL3, MASK3, '3', LSEL_RUNLEVEL }, 2410Sstevel@tonic-gate { LVL4, MASK4, '4', LSEL_RUNLEVEL }, 2422481Spaulson { LVL5, MASK5, '5', LSEL_RUNLEVEL }, 2432481Spaulson { LVL6, MASK6, '6', LSEL_RUNLEVEL }, 2442481Spaulson { SINGLE_USER, MASKSU, 'S', LSEL_RUNLEVEL }, 2452481Spaulson { SINGLE_USER, MASKSU, 's', LSEL_RUNLEVEL }, 2460Sstevel@tonic-gate { LVLa, MASKa, 'a', 0 }, 2470Sstevel@tonic-gate { LVLb, MASKb, 'b', 0 }, 2480Sstevel@tonic-gate { LVLc, MASKc, 'c', 0 } 2490Sstevel@tonic-gate }; 2500Sstevel@tonic-gate 2510Sstevel@tonic-gate #define LVL_NELEMS (sizeof (lvls) / sizeof (lvl_t)) 2520Sstevel@tonic-gate 2530Sstevel@tonic-gate /* 2540Sstevel@tonic-gate * Legal action field values. 2550Sstevel@tonic-gate */ 2560Sstevel@tonic-gate #define OFF 0 /* Kill process if on, else ignore */ 2570Sstevel@tonic-gate #define RESPAWN 1 /* Continuously restart process when it dies */ 2580Sstevel@tonic-gate #define ONDEMAND RESPAWN /* Respawn for a, b, c type processes */ 2590Sstevel@tonic-gate #define ONCE 2 /* Start process, do not respawn when dead */ 2600Sstevel@tonic-gate #define WAIT 3 /* Perform once and wait to complete */ 2610Sstevel@tonic-gate #define BOOT 4 /* Start at boot time only */ 2620Sstevel@tonic-gate #define BOOTWAIT 5 /* Start at boot time and wait to complete */ 2630Sstevel@tonic-gate #define POWERFAIL 6 /* Start on powerfail */ 2640Sstevel@tonic-gate #define POWERWAIT 7 /* Start and wait for complete on powerfail */ 2650Sstevel@tonic-gate #define INITDEFAULT 8 /* Default level "init" should start at */ 2660Sstevel@tonic-gate #define SYSINIT 9 /* Actions performed before init speaks */ 2670Sstevel@tonic-gate 2680Sstevel@tonic-gate #define M_OFF 0001 2690Sstevel@tonic-gate #define M_RESPAWN 0002 2700Sstevel@tonic-gate #define M_ONDEMAND M_RESPAWN 2710Sstevel@tonic-gate #define M_ONCE 0004 2720Sstevel@tonic-gate #define M_WAIT 0010 2730Sstevel@tonic-gate #define M_BOOT 0020 2740Sstevel@tonic-gate #define M_BOOTWAIT 0040 2750Sstevel@tonic-gate #define M_PF 0100 2760Sstevel@tonic-gate #define M_PWAIT 0200 2770Sstevel@tonic-gate #define M_INITDEFAULT 0400 2780Sstevel@tonic-gate #define M_SYSINIT 01000 2790Sstevel@tonic-gate 2800Sstevel@tonic-gate /* States for the inittab parser in getcmd(). */ 2810Sstevel@tonic-gate #define ID 1 2820Sstevel@tonic-gate #define LEVELS 2 2830Sstevel@tonic-gate #define ACTION 3 2840Sstevel@tonic-gate #define COMMAND 4 2850Sstevel@tonic-gate #define COMMENT 5 2860Sstevel@tonic-gate 2870Sstevel@tonic-gate /* 2886073Sacruz * inittab entry id constants 2896073Sacruz */ 2906073Sacruz #define INITTAB_ENTRY_ID_SIZE 4 2916073Sacruz #define INITTAB_ENTRY_ID_STR_FORMAT "%.4s" /* if INITTAB_ENTRY_ID_SIZE */ 2926073Sacruz /* changes, this should */ 2936073Sacruz /* change accordingly */ 2946073Sacruz 2956073Sacruz /* 2960Sstevel@tonic-gate * Init can be in any of three main states, "normal" mode where it is 2970Sstevel@tonic-gate * processing entries for the lines file in a normal fashion, "boot" mode, 2980Sstevel@tonic-gate * where it is only interested in the boot actions, and "powerfail" mode, 2990Sstevel@tonic-gate * where it is only interested in powerfail related actions. The following 3000Sstevel@tonic-gate * masks declare the legal actions for each mode. 3010Sstevel@tonic-gate */ 3020Sstevel@tonic-gate #define NORMAL_MODES (M_OFF | M_RESPAWN | M_ONCE | M_WAIT) 3030Sstevel@tonic-gate #define BOOT_MODES (M_BOOT | M_BOOTWAIT) 3040Sstevel@tonic-gate #define PF_MODES (M_PF | M_PWAIT) 3050Sstevel@tonic-gate 3060Sstevel@tonic-gate struct PROC_TABLE { 3076073Sacruz char p_id[INITTAB_ENTRY_ID_SIZE]; /* Four letter unique id of */ 3086073Sacruz /* process */ 3090Sstevel@tonic-gate pid_t p_pid; /* Process id */ 3100Sstevel@tonic-gate short p_count; /* How many respawns of this command in */ 3110Sstevel@tonic-gate /* the current series */ 3120Sstevel@tonic-gate long p_time; /* Start time for a series of respawns */ 3130Sstevel@tonic-gate short p_flags; 3140Sstevel@tonic-gate short p_exit; /* Exit status of a process which died */ 3150Sstevel@tonic-gate }; 3160Sstevel@tonic-gate 3170Sstevel@tonic-gate /* 3180Sstevel@tonic-gate * Flags for the "p_flags" word of a PROC_TABLE entry: 3190Sstevel@tonic-gate * 3200Sstevel@tonic-gate * OCCUPIED This slot in init's proc table is in use. 3210Sstevel@tonic-gate * 3220Sstevel@tonic-gate * LIVING Process is alive. 3230Sstevel@tonic-gate * 3240Sstevel@tonic-gate * NOCLEANUP efork() is not allowed to cleanup this entry even 3250Sstevel@tonic-gate * if process is dead. 3260Sstevel@tonic-gate * 3270Sstevel@tonic-gate * NAMED This process has a name, i.e. came from inittab. 3280Sstevel@tonic-gate * 3290Sstevel@tonic-gate * DEMANDREQUEST Process started by a "telinit [abc]" command. Processes 3300Sstevel@tonic-gate * formed this way are respawnable and immune to level 3310Sstevel@tonic-gate * changes as long as their entry exists in inittab. 3320Sstevel@tonic-gate * 3330Sstevel@tonic-gate * TOUCHED Flag used by remv() to determine whether it has looked 3340Sstevel@tonic-gate * at an entry while checking for processes to be killed. 3350Sstevel@tonic-gate * 3360Sstevel@tonic-gate * WARNED Flag used by remv() to mark processes that have been 3370Sstevel@tonic-gate * sent the SIGTERM signal. If they don't die in 5 3380Sstevel@tonic-gate * seconds, they are sent the SIGKILL signal. 3390Sstevel@tonic-gate * 3400Sstevel@tonic-gate * KILLED Flag used by remv() to mark procs that have been sent 3410Sstevel@tonic-gate * the SIGTERM and SIGKILL signals. 3420Sstevel@tonic-gate * 3430Sstevel@tonic-gate * PF_MASK Bitwise or of legal flags, for sanity checking. 3440Sstevel@tonic-gate */ 3450Sstevel@tonic-gate #define OCCUPIED 01 3460Sstevel@tonic-gate #define LIVING 02 3470Sstevel@tonic-gate #define NOCLEANUP 04 3480Sstevel@tonic-gate #define NAMED 010 3490Sstevel@tonic-gate #define DEMANDREQUEST 020 3500Sstevel@tonic-gate #define TOUCHED 040 3510Sstevel@tonic-gate #define WARNED 0100 3520Sstevel@tonic-gate #define KILLED 0200 3530Sstevel@tonic-gate #define PF_MASK 0377 3540Sstevel@tonic-gate 3550Sstevel@tonic-gate /* 3560Sstevel@tonic-gate * Respawn limits for processes that are to be respawned: 3570Sstevel@tonic-gate * 3580Sstevel@tonic-gate * SPAWN_INTERVAL The number of seconds over which "init" will try to 3590Sstevel@tonic-gate * respawn a process SPAWN_LIMIT times before it gets mad. 3600Sstevel@tonic-gate * 3610Sstevel@tonic-gate * SPAWN_LIMIT The number of respawns "init" will attempt in 3620Sstevel@tonic-gate * SPAWN_INTERVAL seconds before it generates an 3630Sstevel@tonic-gate * error message and inhibits further tries for 3640Sstevel@tonic-gate * INHIBIT seconds. 3650Sstevel@tonic-gate * 3660Sstevel@tonic-gate * INHIBIT The number of seconds "init" ignores an entry it had 3670Sstevel@tonic-gate * trouble spawning unless a "telinit Q" is received. 3680Sstevel@tonic-gate */ 3690Sstevel@tonic-gate 3700Sstevel@tonic-gate #define SPAWN_INTERVAL (2*60) 3710Sstevel@tonic-gate #define SPAWN_LIMIT 10 3720Sstevel@tonic-gate #define INHIBIT (5*60) 3730Sstevel@tonic-gate 3740Sstevel@tonic-gate /* 3750Sstevel@tonic-gate * The maximum number of decimal digits for an id_t. (ceil(log10 (max_id))) 3760Sstevel@tonic-gate */ 3770Sstevel@tonic-gate #define ID_MAX_STR_LEN 10 3780Sstevel@tonic-gate 3790Sstevel@tonic-gate #define NULLPROC ((struct PROC_TABLE *)(0)) 3800Sstevel@tonic-gate #define NO_ROOM ((struct PROC_TABLE *)(FAILURE)) 3810Sstevel@tonic-gate 3820Sstevel@tonic-gate struct CMD_LINE { 3836073Sacruz char c_id[INITTAB_ENTRY_ID_SIZE]; /* Four letter unique id of */ 3846073Sacruz /* process to be affected by */ 3856073Sacruz /* action */ 3860Sstevel@tonic-gate short c_levels; /* Mask of legal levels for process */ 3870Sstevel@tonic-gate short c_action; /* Mask for type of action required */ 3880Sstevel@tonic-gate char *c_command; /* Pointer to init command */ 3890Sstevel@tonic-gate }; 3900Sstevel@tonic-gate 3910Sstevel@tonic-gate struct pidrec { 3920Sstevel@tonic-gate int pd_type; /* Command type */ 3930Sstevel@tonic-gate pid_t pd_pid; /* pid to add or remove */ 3940Sstevel@tonic-gate }; 3950Sstevel@tonic-gate 3960Sstevel@tonic-gate /* 3970Sstevel@tonic-gate * pd_type's 3980Sstevel@tonic-gate */ 3990Sstevel@tonic-gate #define ADDPID 1 4000Sstevel@tonic-gate #define REMPID 2 4010Sstevel@tonic-gate 4020Sstevel@tonic-gate static struct pidlist { 4030Sstevel@tonic-gate pid_t pl_pid; /* pid to watch for */ 4040Sstevel@tonic-gate int pl_dflag; /* Flag indicating SIGCLD from this pid */ 4050Sstevel@tonic-gate short pl_exit; /* Exit status of proc */ 4060Sstevel@tonic-gate struct pidlist *pl_next; /* Next in list */ 4070Sstevel@tonic-gate } *Plhead, *Plfree; 4080Sstevel@tonic-gate 4090Sstevel@tonic-gate /* 4100Sstevel@tonic-gate * The following structure contains a set of modes for /dev/syscon 4110Sstevel@tonic-gate * and should match the default contents of /etc/ioctl.syscon. 4120Sstevel@tonic-gate */ 4130Sstevel@tonic-gate static struct termios dflt_termios = { 4140Sstevel@tonic-gate BRKINT|ICRNL|IXON|IMAXBEL, /* iflag */ 4150Sstevel@tonic-gate OPOST|ONLCR|TAB3, /* oflag */ 4160Sstevel@tonic-gate CS8|CREAD|B9600, /* cflag */ 4170Sstevel@tonic-gate ISIG|ICANON|ECHO|ECHOE|ECHOK|ECHOCTL|ECHOKE|IEXTEN, /* lflag */ 4180Sstevel@tonic-gate CINTR, CQUIT, CERASE, CKILL, CEOF, 0, 0, 0, 4190Sstevel@tonic-gate 0, 0, 0, 0, 0, 0, 0, 0, 4200Sstevel@tonic-gate 0, 0, 0 4210Sstevel@tonic-gate }; 4220Sstevel@tonic-gate 4230Sstevel@tonic-gate static struct termios stored_syscon_termios; 4240Sstevel@tonic-gate static int write_ioctl = 0; /* Rewrite /etc/ioctl.syscon */ 4250Sstevel@tonic-gate 4260Sstevel@tonic-gate static union WAKEUP { 4270Sstevel@tonic-gate struct WAKEFLAGS { 4280Sstevel@tonic-gate unsigned w_usersignal : 1; /* User sent signal to "init" */ 4290Sstevel@tonic-gate unsigned w_childdeath : 1; /* An "init" child died */ 4300Sstevel@tonic-gate unsigned w_powerhit : 1; /* OS experienced powerfail */ 4310Sstevel@tonic-gate } w_flags; 4320Sstevel@tonic-gate int w_mask; 4330Sstevel@tonic-gate } wakeup; 4340Sstevel@tonic-gate 4350Sstevel@tonic-gate 4360Sstevel@tonic-gate struct init_state { 4370Sstevel@tonic-gate int ist_runlevel; 4380Sstevel@tonic-gate int ist_num_proc; 4390Sstevel@tonic-gate int ist_utmpx_ok; 4400Sstevel@tonic-gate struct PROC_TABLE ist_proc_table[1]; 4410Sstevel@tonic-gate }; 4420Sstevel@tonic-gate 4430Sstevel@tonic-gate #define cur_state (g_state->ist_runlevel) 4440Sstevel@tonic-gate #define num_proc (g_state->ist_num_proc) 4450Sstevel@tonic-gate #define proc_table (g_state->ist_proc_table) 4460Sstevel@tonic-gate #define utmpx_ok (g_state->ist_utmpx_ok) 4470Sstevel@tonic-gate 4480Sstevel@tonic-gate /* Contract cookies. */ 4490Sstevel@tonic-gate #define ORDINARY_COOKIE 0 4500Sstevel@tonic-gate #define STARTD_COOKIE 1 4510Sstevel@tonic-gate 4520Sstevel@tonic-gate 4530Sstevel@tonic-gate #ifndef NDEBUG 4540Sstevel@tonic-gate #define bad_error(func, err) { \ 4550Sstevel@tonic-gate (void) fprintf(stderr, "%s:%d: %s() failed with unexpected " \ 4560Sstevel@tonic-gate "error %d. Aborting.\n", __FILE__, __LINE__, (func), (err)); \ 4570Sstevel@tonic-gate abort(); \ 4580Sstevel@tonic-gate } 4590Sstevel@tonic-gate #else 4600Sstevel@tonic-gate #define bad_error(func, err) abort() 4610Sstevel@tonic-gate #endif 4620Sstevel@tonic-gate 4630Sstevel@tonic-gate 4640Sstevel@tonic-gate /* 4650Sstevel@tonic-gate * Useful file and device names. 4660Sstevel@tonic-gate */ 4670Sstevel@tonic-gate static char *CONSOLE = "/dev/console"; /* Real system console */ 4685017Seschrock static char *INITPIPE_DIR = "/var/run"; 4695017Seschrock static char *INITPIPE = "/var/run/initpipe"; 4700Sstevel@tonic-gate 4710Sstevel@tonic-gate #define INIT_STATE_DIR "/etc/svc/volatile" 4720Sstevel@tonic-gate static const char * const init_state_file = INIT_STATE_DIR "/init.state"; 4730Sstevel@tonic-gate static const char * const init_next_state_file = 4740Sstevel@tonic-gate INIT_STATE_DIR "/init-next.state"; 4750Sstevel@tonic-gate 4760Sstevel@tonic-gate static const int init_num_proc = 20; /* Initial size of process table. */ 4770Sstevel@tonic-gate 4780Sstevel@tonic-gate static char *UTMPX = UTMPX_FILE; /* Snapshot record file */ 4790Sstevel@tonic-gate static char *WTMPX = WTMPX_FILE; /* Long term record file */ 4800Sstevel@tonic-gate static char *INITTAB = "/etc/inittab"; /* Script file for "init" */ 4810Sstevel@tonic-gate static char *SYSTTY = "/dev/systty"; /* System Console */ 4820Sstevel@tonic-gate static char *SYSCON = "/dev/syscon"; /* Virtual System console */ 4830Sstevel@tonic-gate static char *IOCTLSYSCON = "/etc/ioctl.syscon"; /* Last syscon modes */ 4840Sstevel@tonic-gate static char *ENVFILE = "/etc/default/init"; /* Default env. */ 4850Sstevel@tonic-gate static char *SU = "/etc/sulogin"; /* Super-user program for single user */ 4860Sstevel@tonic-gate static char *SH = "/sbin/sh"; /* Standard shell */ 4870Sstevel@tonic-gate 4880Sstevel@tonic-gate /* 4890Sstevel@tonic-gate * Default Path. /sbin is included in path only during sysinit phase 4900Sstevel@tonic-gate */ 4910Sstevel@tonic-gate #define DEF_PATH "PATH=/usr/sbin:/usr/bin" 4920Sstevel@tonic-gate #define INIT_PATH "PATH=/sbin:/usr/sbin:/usr/bin" 4930Sstevel@tonic-gate 4940Sstevel@tonic-gate static int prior_state; 4950Sstevel@tonic-gate static int prev_state; /* State "init" was in last time it woke */ 4960Sstevel@tonic-gate static int new_state; /* State user wants "init" to go to. */ 4975017Seschrock static int lvlq_received; /* Explicit request to examine state */ 4980Sstevel@tonic-gate static int op_modes = BOOT_MODES; /* Current state of "init" */ 4990Sstevel@tonic-gate static int Gchild = 0; /* Flag to indicate "godchild" died, set in */ 5000Sstevel@tonic-gate /* childeath() and cleared in cleanaux() */ 5010Sstevel@tonic-gate static int Pfd = -1; /* fd to receive pids thru */ 5020Sstevel@tonic-gate static unsigned int spawncnt, pausecnt; 5030Sstevel@tonic-gate static int rsflag; /* Set if a respawn has taken place */ 5040Sstevel@tonic-gate static volatile int time_up; /* Flag set to TRUE by the alarm interrupt */ 5050Sstevel@tonic-gate /* routine each time an alarm interrupt */ 5060Sstevel@tonic-gate /* takes place. */ 5070Sstevel@tonic-gate static int sflg = 0; /* Set if we were booted -s to single user */ 5080Sstevel@tonic-gate static int rflg = 0; /* Set if booted -r, reconfigure devices */ 5090Sstevel@tonic-gate static int bflg = 0; /* Set if booted -b, don't run rc scripts */ 5100Sstevel@tonic-gate static pid_t init_pid; /* PID of "one true" init for current zone */ 5110Sstevel@tonic-gate 5120Sstevel@tonic-gate static struct init_state *g_state = NULL; 5130Sstevel@tonic-gate static size_t g_state_sz; 5140Sstevel@tonic-gate static int booting = 1; /* Set while we're booting. */ 5150Sstevel@tonic-gate 5160Sstevel@tonic-gate /* 5170Sstevel@tonic-gate * Array for default global environment. 5180Sstevel@tonic-gate */ 5190Sstevel@tonic-gate #define MAXENVENT 24 /* Max number of default env variables + 1 */ 5200Sstevel@tonic-gate /* init can use three itself, so this leaves */ 5210Sstevel@tonic-gate /* 20 for the administrator in ENVFILE. */ 5220Sstevel@tonic-gate static char *glob_envp[MAXENVENT]; /* Array of environment strings */ 5230Sstevel@tonic-gate static int glob_envn; /* Number of environment strings */ 5240Sstevel@tonic-gate 5250Sstevel@tonic-gate 5260Sstevel@tonic-gate static struct pollfd poll_fds[1]; 5270Sstevel@tonic-gate static int poll_nfds = 0; /* poll_fds is uninitialized */ 5280Sstevel@tonic-gate 5296073Sacruz /* 5306073Sacruz * Contracts constants 5316073Sacruz */ 5326073Sacruz #define SVC_INIT_PREFIX "init:/" 5336073Sacruz #define SVC_AUX_SIZE (INITTAB_ENTRY_ID_SIZE + 1) 5346073Sacruz #define SVC_FMRI_SIZE (sizeof (SVC_INIT_PREFIX) + INITTAB_ENTRY_ID_SIZE) 5356073Sacruz 5360Sstevel@tonic-gate static int legacy_tmpl = -1; /* fd for legacy contract template */ 5370Sstevel@tonic-gate static int startd_tmpl = -1; /* fd for svc.startd's template */ 5386073Sacruz static char startd_svc_aux[SVC_AUX_SIZE]; 5390Sstevel@tonic-gate 5400Sstevel@tonic-gate static char startd_cline[256] = ""; /* svc.startd's command line */ 5410Sstevel@tonic-gate static int do_restart_startd = 1; /* Whether to restart svc.startd. */ 5420Sstevel@tonic-gate static char *smf_options = NULL; /* Options to give to startd. */ 5430Sstevel@tonic-gate static int smf_debug = 0; /* Messages for debugging smf(5) */ 5440Sstevel@tonic-gate static time_t init_boot_time; /* Substitute for kernel boot time. */ 5450Sstevel@tonic-gate 5460Sstevel@tonic-gate #define NSTARTD_FAILURE_TIMES 3 /* trigger after 3 failures */ 5470Sstevel@tonic-gate #define STARTD_FAILURE_RATE_NS 5000000000LL /* 1 failure/5 seconds */ 5480Sstevel@tonic-gate 5490Sstevel@tonic-gate static hrtime_t startd_failure_time[NSTARTD_FAILURE_TIMES]; 5500Sstevel@tonic-gate static uint_t startd_failure_index; 5510Sstevel@tonic-gate 5520Sstevel@tonic-gate 5530Sstevel@tonic-gate static char *prog_name(char *); 5540Sstevel@tonic-gate static int state_to_mask(int); 5550Sstevel@tonic-gate static int lvlname_to_mask(char, int *); 5560Sstevel@tonic-gate static void lscf_set_runlevel(char); 5570Sstevel@tonic-gate static int state_to_flags(int); 5580Sstevel@tonic-gate static char state_to_name(int); 5590Sstevel@tonic-gate static int lvlname_to_state(char); 5600Sstevel@tonic-gate static int getcmd(struct CMD_LINE *, char *); 5610Sstevel@tonic-gate static int realcon(); 5620Sstevel@tonic-gate static int spawn_processes(); 5630Sstevel@tonic-gate static int get_ioctl_syscon(); 5640Sstevel@tonic-gate static int account(short, struct PROC_TABLE *, char *); 5650Sstevel@tonic-gate static void alarmclk(); 5660Sstevel@tonic-gate static void childeath(int); 5670Sstevel@tonic-gate static void cleanaux(); 5680Sstevel@tonic-gate static void clearent(pid_t, short); 5690Sstevel@tonic-gate static void console(boolean_t, char *, ...); 5700Sstevel@tonic-gate static void init_signals(void); 5710Sstevel@tonic-gate static void setup_pipe(); 5720Sstevel@tonic-gate static void killproc(pid_t); 5730Sstevel@tonic-gate static void init_env(); 5740Sstevel@tonic-gate static void boot_init(); 5750Sstevel@tonic-gate static void powerfail(); 5760Sstevel@tonic-gate static void remv(); 5770Sstevel@tonic-gate static void write_ioctl_syscon(); 5780Sstevel@tonic-gate static void spawn(struct PROC_TABLE *, struct CMD_LINE *); 5790Sstevel@tonic-gate static void setimer(int); 5800Sstevel@tonic-gate static void siglvl(int, siginfo_t *, ucontext_t *); 5810Sstevel@tonic-gate static void sigpoll(int); 5820Sstevel@tonic-gate static void enter_maintenance(void); 5830Sstevel@tonic-gate static void timer(int); 5840Sstevel@tonic-gate static void userinit(int, char **); 5850Sstevel@tonic-gate static void notify_pam_dead(struct utmpx *); 5860Sstevel@tonic-gate static long waitproc(struct PROC_TABLE *); 5870Sstevel@tonic-gate static struct PROC_TABLE *efork(int, struct PROC_TABLE *, int); 5880Sstevel@tonic-gate static struct PROC_TABLE *findpslot(struct CMD_LINE *); 5890Sstevel@tonic-gate static void increase_proc_table_size(); 5900Sstevel@tonic-gate static void st_init(); 5910Sstevel@tonic-gate static void st_write(); 5920Sstevel@tonic-gate static void contracts_init(); 5930Sstevel@tonic-gate static void contract_event(struct pollfd *); 5940Sstevel@tonic-gate static int startd_run(const char *, int, ctid_t); 5950Sstevel@tonic-gate static void startd_record_failure(); 5960Sstevel@tonic-gate static int startd_failure_rate_critical(); 5970Sstevel@tonic-gate static char *audit_boot_msg(); 5980Sstevel@tonic-gate static int audit_put_record(int, int, char *); 5990Sstevel@tonic-gate static void update_boot_archive(int new_state); 6000Sstevel@tonic-gate 6010Sstevel@tonic-gate int 6020Sstevel@tonic-gate main(int argc, char *argv[]) 6030Sstevel@tonic-gate { 6040Sstevel@tonic-gate int chg_lvl_flag = FALSE, print_banner = FALSE; 6050Sstevel@tonic-gate int may_need_audit = 1; 6060Sstevel@tonic-gate int c; 6070Sstevel@tonic-gate char *msg; 6080Sstevel@tonic-gate 6090Sstevel@tonic-gate /* Get a timestamp for use as boot time, if needed. */ 6100Sstevel@tonic-gate (void) time(&init_boot_time); 6110Sstevel@tonic-gate 6120Sstevel@tonic-gate /* Get the default umask */ 6130Sstevel@tonic-gate cmask = umask(022); 6140Sstevel@tonic-gate (void) umask(cmask); 6150Sstevel@tonic-gate 6160Sstevel@tonic-gate /* Parse the arguments to init. Check for single user */ 6170Sstevel@tonic-gate opterr = 0; 6180Sstevel@tonic-gate while ((c = getopt(argc, argv, "brsm:")) != EOF) { 6190Sstevel@tonic-gate switch (c) { 6200Sstevel@tonic-gate case 'b': 6210Sstevel@tonic-gate rflg = 0; 6220Sstevel@tonic-gate bflg = 1; 6230Sstevel@tonic-gate if (!sflg) 6240Sstevel@tonic-gate sflg++; 6250Sstevel@tonic-gate break; 6260Sstevel@tonic-gate case 'r': 6270Sstevel@tonic-gate bflg = 0; 6280Sstevel@tonic-gate rflg++; 6290Sstevel@tonic-gate break; 6300Sstevel@tonic-gate case 's': 6310Sstevel@tonic-gate if (!bflg) 6320Sstevel@tonic-gate sflg++; 6330Sstevel@tonic-gate break; 6340Sstevel@tonic-gate case 'm': 6350Sstevel@tonic-gate smf_options = optarg; 6360Sstevel@tonic-gate smf_debug = (strstr(smf_options, "debug") != NULL); 6370Sstevel@tonic-gate break; 6380Sstevel@tonic-gate } 6390Sstevel@tonic-gate } 6400Sstevel@tonic-gate 6410Sstevel@tonic-gate /* 6420Sstevel@tonic-gate * Determine if we are the main init, or a user invoked init, whose job 6430Sstevel@tonic-gate * it is to inform init to change levels or perform some other action. 6440Sstevel@tonic-gate */ 6450Sstevel@tonic-gate if (zone_getattr(getzoneid(), ZONE_ATTR_INITPID, &init_pid, 6460Sstevel@tonic-gate sizeof (init_pid)) != sizeof (init_pid)) { 6470Sstevel@tonic-gate (void) fprintf(stderr, "could not get pid for init\n"); 6480Sstevel@tonic-gate return (1); 6490Sstevel@tonic-gate } 6500Sstevel@tonic-gate 6510Sstevel@tonic-gate /* 6520Sstevel@tonic-gate * If this PID is not the same as the "true" init for the zone, then we 6530Sstevel@tonic-gate * must be in 'user' mode. 6540Sstevel@tonic-gate */ 6550Sstevel@tonic-gate if (getpid() != init_pid) { 6560Sstevel@tonic-gate userinit(argc, argv); 6570Sstevel@tonic-gate } 6580Sstevel@tonic-gate 6590Sstevel@tonic-gate if (getzoneid() != GLOBAL_ZONEID) { 6600Sstevel@tonic-gate print_banner = TRUE; 6610Sstevel@tonic-gate } 6620Sstevel@tonic-gate 6630Sstevel@tonic-gate /* 6640Sstevel@tonic-gate * Initialize state (and set "booting"). 6650Sstevel@tonic-gate */ 6660Sstevel@tonic-gate st_init(); 6670Sstevel@tonic-gate 6680Sstevel@tonic-gate if (booting && print_banner) { 6690Sstevel@tonic-gate struct utsname un; 6700Sstevel@tonic-gate char buf[BUFSIZ], *isa; 6710Sstevel@tonic-gate long ret; 6720Sstevel@tonic-gate int bits = 32; 6730Sstevel@tonic-gate 6740Sstevel@tonic-gate /* 6750Sstevel@tonic-gate * We want to print the boot banner as soon as 6760Sstevel@tonic-gate * possible. In the global zone, the kernel does it, 6770Sstevel@tonic-gate * but we do not have that luxury in non-global zones, 6780Sstevel@tonic-gate * so we will print it here. 6790Sstevel@tonic-gate */ 6800Sstevel@tonic-gate (void) uname(&un); 6810Sstevel@tonic-gate ret = sysinfo(SI_ISALIST, buf, sizeof (buf)); 6820Sstevel@tonic-gate if (ret != -1L && ret <= sizeof (buf)) { 6830Sstevel@tonic-gate for (isa = strtok(buf, " "); isa; 6840Sstevel@tonic-gate isa = strtok(NULL, " ")) { 6850Sstevel@tonic-gate if (strcmp(isa, "sparcv9") == 0 || 6860Sstevel@tonic-gate strcmp(isa, "amd64") == 0) { 6870Sstevel@tonic-gate bits = 64; 6880Sstevel@tonic-gate break; 6890Sstevel@tonic-gate } 6900Sstevel@tonic-gate } 6910Sstevel@tonic-gate } 6920Sstevel@tonic-gate 6930Sstevel@tonic-gate console(B_FALSE, 6940Sstevel@tonic-gate "\n\n%s Release %s Version %s %d-bit\r\n", 6950Sstevel@tonic-gate un.sysname, un.release, un.version, bits); 6960Sstevel@tonic-gate console(B_FALSE, 6978576SNick.Todd@Sun.COM "Copyright 1983-2009 Sun Microsystems, Inc. " 6980Sstevel@tonic-gate " All rights reserved.\r\n"); 6990Sstevel@tonic-gate console(B_FALSE, 7000Sstevel@tonic-gate "Use is subject to license terms.\r\n"); 7010Sstevel@tonic-gate } 7020Sstevel@tonic-gate 7030Sstevel@tonic-gate /* 7040Sstevel@tonic-gate * Get the ioctl settings for /dev/syscon from /etc/ioctl.syscon 7050Sstevel@tonic-gate * so that it can be brought up in the state it was in when the 7060Sstevel@tonic-gate * system went down; or set to defaults if ioctl.syscon isn't 7070Sstevel@tonic-gate * valid. 7080Sstevel@tonic-gate * 7090Sstevel@tonic-gate * This needs to be done even if we're restarting so reset_modes() 7100Sstevel@tonic-gate * will work in case we need to go down to single user mode. 7110Sstevel@tonic-gate */ 7120Sstevel@tonic-gate write_ioctl = get_ioctl_syscon(); 7130Sstevel@tonic-gate 7140Sstevel@tonic-gate /* 7150Sstevel@tonic-gate * Set up all signals to be caught or ignored as appropriate. 7160Sstevel@tonic-gate */ 7170Sstevel@tonic-gate init_signals(); 7180Sstevel@tonic-gate 7190Sstevel@tonic-gate /* Load glob_envp from ENVFILE. */ 7200Sstevel@tonic-gate init_env(); 7210Sstevel@tonic-gate 7220Sstevel@tonic-gate contracts_init(); 7230Sstevel@tonic-gate 7240Sstevel@tonic-gate if (!booting) { 7250Sstevel@tonic-gate /* cur_state should have been read in. */ 7260Sstevel@tonic-gate 7270Sstevel@tonic-gate op_modes = NORMAL_MODES; 7280Sstevel@tonic-gate 7290Sstevel@tonic-gate /* Rewrite the ioctl file if it was bad. */ 7300Sstevel@tonic-gate if (write_ioctl) 7310Sstevel@tonic-gate write_ioctl_syscon(); 7320Sstevel@tonic-gate } else { 7330Sstevel@tonic-gate /* 7340Sstevel@tonic-gate * It's fine to boot up with state as zero, because 7350Sstevel@tonic-gate * startd will later tell us the real state. 7360Sstevel@tonic-gate */ 7370Sstevel@tonic-gate cur_state = 0; 7380Sstevel@tonic-gate op_modes = BOOT_MODES; 7390Sstevel@tonic-gate 7400Sstevel@tonic-gate boot_init(); 7410Sstevel@tonic-gate } 7420Sstevel@tonic-gate 7430Sstevel@tonic-gate prev_state = prior_state = cur_state; 7440Sstevel@tonic-gate 7455017Seschrock setup_pipe(); 7465017Seschrock 7470Sstevel@tonic-gate /* 7480Sstevel@tonic-gate * Here is the beginning of the main process loop. 7490Sstevel@tonic-gate */ 7500Sstevel@tonic-gate for (;;) { 7515017Seschrock if (lvlq_received) { 7520Sstevel@tonic-gate setup_pipe(); 7535017Seschrock lvlq_received = B_FALSE; 7545017Seschrock } 7550Sstevel@tonic-gate 7560Sstevel@tonic-gate /* 7570Sstevel@tonic-gate * Clean up any accounting records for dead "godchildren". 7580Sstevel@tonic-gate */ 7590Sstevel@tonic-gate if (Gchild) 7600Sstevel@tonic-gate cleanaux(); 7610Sstevel@tonic-gate 7620Sstevel@tonic-gate /* 7630Sstevel@tonic-gate * If in "normal" mode, check all living processes and initiate 7640Sstevel@tonic-gate * kill sequence on those that should not be there anymore. 7650Sstevel@tonic-gate */ 7660Sstevel@tonic-gate if (op_modes == NORMAL_MODES && cur_state != LVLa && 7670Sstevel@tonic-gate cur_state != LVLb && cur_state != LVLc) 7680Sstevel@tonic-gate remv(); 7690Sstevel@tonic-gate 7700Sstevel@tonic-gate /* 7710Sstevel@tonic-gate * If a change in run levels is the reason we awoke, now do 7720Sstevel@tonic-gate * the accounting to report the change in the utmp file. 7730Sstevel@tonic-gate * Also report the change on the system console. 7740Sstevel@tonic-gate */ 7750Sstevel@tonic-gate if (chg_lvl_flag) { 7760Sstevel@tonic-gate chg_lvl_flag = FALSE; 7770Sstevel@tonic-gate 7780Sstevel@tonic-gate if (state_to_flags(cur_state) & LSEL_RUNLEVEL) { 7790Sstevel@tonic-gate char rl = state_to_name(cur_state); 7800Sstevel@tonic-gate 7810Sstevel@tonic-gate if (rl != -1) 7820Sstevel@tonic-gate lscf_set_runlevel(rl); 7830Sstevel@tonic-gate } 7840Sstevel@tonic-gate 7850Sstevel@tonic-gate may_need_audit = 1; 7860Sstevel@tonic-gate } 7870Sstevel@tonic-gate 7880Sstevel@tonic-gate /* 7890Sstevel@tonic-gate * Scan the inittab file and spawn and respawn processes that 7900Sstevel@tonic-gate * should be alive in the current state. If inittab does not 7910Sstevel@tonic-gate * exist default to single user mode. 7920Sstevel@tonic-gate */ 7930Sstevel@tonic-gate if (spawn_processes() == FAILURE) { 7940Sstevel@tonic-gate prior_state = prev_state; 7950Sstevel@tonic-gate cur_state = SINGLE_USER; 7960Sstevel@tonic-gate } 7970Sstevel@tonic-gate 7980Sstevel@tonic-gate /* If any respawns occurred, take note. */ 7990Sstevel@tonic-gate if (rsflag) { 8000Sstevel@tonic-gate rsflag = 0; 8010Sstevel@tonic-gate spawncnt++; 8020Sstevel@tonic-gate } 8030Sstevel@tonic-gate 8040Sstevel@tonic-gate /* 8050Sstevel@tonic-gate * If a powerfail signal was received during the last 8060Sstevel@tonic-gate * sequence, set mode to powerfail. When spawn_processes() is 8070Sstevel@tonic-gate * entered the first thing it does is to check "powerhit". If 8080Sstevel@tonic-gate * it is in PF_MODES then it clears "powerhit" and does 8090Sstevel@tonic-gate * a powerfail sequence. If it is not in PF_MODES, then it 8100Sstevel@tonic-gate * puts itself in PF_MODES and then clears "powerhit". Should 8110Sstevel@tonic-gate * "powerhit" get set again while spawn_processes() is working 8120Sstevel@tonic-gate * on a powerfail sequence, the following code will see that 8130Sstevel@tonic-gate * spawn_processes() tries to execute the powerfail sequence 8140Sstevel@tonic-gate * again. This guarantees that the powerfail sequence will be 8150Sstevel@tonic-gate * successfully completed before further processing takes 8160Sstevel@tonic-gate * place. 8170Sstevel@tonic-gate */ 8180Sstevel@tonic-gate if (wakeup.w_flags.w_powerhit) { 8190Sstevel@tonic-gate op_modes = PF_MODES; 8200Sstevel@tonic-gate /* 8210Sstevel@tonic-gate * Make sure that cur_state != prev_state so that 8220Sstevel@tonic-gate * ONCE and WAIT types work. 8230Sstevel@tonic-gate */ 8240Sstevel@tonic-gate prev_state = 0; 8250Sstevel@tonic-gate } else if (op_modes != NORMAL_MODES) { 8260Sstevel@tonic-gate /* 8270Sstevel@tonic-gate * If spawn_processes() was not just called while in 8280Sstevel@tonic-gate * normal mode, we set the mode to normal and it will 8290Sstevel@tonic-gate * be called again to check normal modes. If we have 8300Sstevel@tonic-gate * just finished a powerfail sequence with prev_state 8310Sstevel@tonic-gate * equal to zero, we set prev_state equal to cur_state 8320Sstevel@tonic-gate * before the next pass through. 8330Sstevel@tonic-gate */ 8340Sstevel@tonic-gate if (op_modes == PF_MODES) 8350Sstevel@tonic-gate prev_state = cur_state; 8360Sstevel@tonic-gate op_modes = NORMAL_MODES; 8370Sstevel@tonic-gate } else if (cur_state == LVLa || cur_state == LVLb || 8380Sstevel@tonic-gate cur_state == LVLc) { 8390Sstevel@tonic-gate /* 8400Sstevel@tonic-gate * If it was a change of levels that awakened us and the 8410Sstevel@tonic-gate * new level is one of the demand levels then reset 8420Sstevel@tonic-gate * cur_state to the previous state and do another scan 8430Sstevel@tonic-gate * to take care of the usual respawn actions. 8440Sstevel@tonic-gate */ 8450Sstevel@tonic-gate cur_state = prior_state; 8460Sstevel@tonic-gate prior_state = prev_state; 8470Sstevel@tonic-gate prev_state = cur_state; 8480Sstevel@tonic-gate } else { 8490Sstevel@tonic-gate prev_state = cur_state; 8500Sstevel@tonic-gate 8510Sstevel@tonic-gate if (wakeup.w_mask == 0) { 8520Sstevel@tonic-gate int ret; 8530Sstevel@tonic-gate 8540Sstevel@tonic-gate if (may_need_audit && (cur_state == LVL3)) { 8550Sstevel@tonic-gate msg = audit_boot_msg(); 8560Sstevel@tonic-gate 8570Sstevel@tonic-gate may_need_audit = 0; 8580Sstevel@tonic-gate (void) audit_put_record(ADT_SUCCESS, 8590Sstevel@tonic-gate ADT_SUCCESS, msg); 8600Sstevel@tonic-gate free(msg); 8610Sstevel@tonic-gate } 8620Sstevel@tonic-gate 8630Sstevel@tonic-gate /* 8640Sstevel@tonic-gate * "init" is finished with all actions for 8650Sstevel@tonic-gate * the current wakeup. 8660Sstevel@tonic-gate */ 8670Sstevel@tonic-gate ret = poll(poll_fds, poll_nfds, 8680Sstevel@tonic-gate SLEEPTIME * MILLISEC); 8690Sstevel@tonic-gate pausecnt++; 8700Sstevel@tonic-gate if (ret > 0) 8710Sstevel@tonic-gate contract_event(&poll_fds[0]); 8720Sstevel@tonic-gate else if (ret < 0 && errno != EINTR) 8730Sstevel@tonic-gate console(B_TRUE, "poll() error: %s\n", 8740Sstevel@tonic-gate strerror(errno)); 8750Sstevel@tonic-gate } 8760Sstevel@tonic-gate 8770Sstevel@tonic-gate if (wakeup.w_flags.w_usersignal) { 8780Sstevel@tonic-gate /* 8790Sstevel@tonic-gate * Install the new level. This could be a real 8800Sstevel@tonic-gate * change in levels or a telinit [Q|a|b|c] or 8810Sstevel@tonic-gate * just a telinit to the same level at which 8820Sstevel@tonic-gate * we are running. 8830Sstevel@tonic-gate */ 8840Sstevel@tonic-gate if (new_state != cur_state) { 8850Sstevel@tonic-gate if (new_state == LVLa || 8860Sstevel@tonic-gate new_state == LVLb || 8870Sstevel@tonic-gate new_state == LVLc) { 8880Sstevel@tonic-gate prev_state = prior_state; 8890Sstevel@tonic-gate prior_state = cur_state; 8900Sstevel@tonic-gate cur_state = new_state; 8910Sstevel@tonic-gate } else { 8920Sstevel@tonic-gate prev_state = cur_state; 8930Sstevel@tonic-gate if (cur_state >= 0) 8940Sstevel@tonic-gate prior_state = cur_state; 8950Sstevel@tonic-gate cur_state = new_state; 8960Sstevel@tonic-gate chg_lvl_flag = TRUE; 8970Sstevel@tonic-gate } 8980Sstevel@tonic-gate } 8990Sstevel@tonic-gate 9000Sstevel@tonic-gate new_state = 0; 9010Sstevel@tonic-gate } 9020Sstevel@tonic-gate 9030Sstevel@tonic-gate if (wakeup.w_flags.w_powerhit) 9040Sstevel@tonic-gate op_modes = PF_MODES; 9050Sstevel@tonic-gate 9060Sstevel@tonic-gate /* 9070Sstevel@tonic-gate * Clear all wakeup reasons. 9080Sstevel@tonic-gate */ 9090Sstevel@tonic-gate wakeup.w_mask = 0; 9100Sstevel@tonic-gate } 9110Sstevel@tonic-gate } 9120Sstevel@tonic-gate 9130Sstevel@tonic-gate /*NOTREACHED*/ 9140Sstevel@tonic-gate } 9150Sstevel@tonic-gate 9160Sstevel@tonic-gate static void 9170Sstevel@tonic-gate update_boot_archive(int new_state) 9180Sstevel@tonic-gate { 9190Sstevel@tonic-gate if (new_state != LVL0 && new_state != LVL5 && new_state != LVL6) 9200Sstevel@tonic-gate return; 9210Sstevel@tonic-gate 9220Sstevel@tonic-gate if (getzoneid() != GLOBAL_ZONEID) 9230Sstevel@tonic-gate return; 9240Sstevel@tonic-gate 925*8735SEnrico.Perla@Sun.COM (void) system("/sbin/bootadm -ea update_all"); 9260Sstevel@tonic-gate } 9270Sstevel@tonic-gate 9280Sstevel@tonic-gate /* 9290Sstevel@tonic-gate * void enter_maintenance() 9300Sstevel@tonic-gate * A simple invocation of sulogin(1M), with no baggage, in the case that we 9310Sstevel@tonic-gate * are unable to activate svc.startd(1M). We fork; the child runs sulogin; 9320Sstevel@tonic-gate * we wait for it to exit. 9330Sstevel@tonic-gate */ 9340Sstevel@tonic-gate static void 9350Sstevel@tonic-gate enter_maintenance() 9360Sstevel@tonic-gate { 9370Sstevel@tonic-gate struct PROC_TABLE *su_process; 9380Sstevel@tonic-gate 9390Sstevel@tonic-gate console(B_FALSE, "Requesting maintenance mode\n" 9400Sstevel@tonic-gate "(See /lib/svc/share/README for additional information.)\n"); 9410Sstevel@tonic-gate (void) sigset(SIGCLD, SIG_DFL); 9420Sstevel@tonic-gate while ((su_process = efork(M_OFF, NULLPROC, NOCLEANUP)) == NO_ROOM) 9430Sstevel@tonic-gate (void) pause(); 9440Sstevel@tonic-gate (void) sigset(SIGCLD, childeath); 9450Sstevel@tonic-gate if (su_process == NULLPROC) { 9460Sstevel@tonic-gate int fd; 9470Sstevel@tonic-gate 9480Sstevel@tonic-gate (void) fclose(stdin); 9490Sstevel@tonic-gate (void) fclose(stdout); 9500Sstevel@tonic-gate (void) fclose(stderr); 9510Sstevel@tonic-gate closefrom(0); 9520Sstevel@tonic-gate 9530Sstevel@tonic-gate fd = open(SYSCON, O_RDWR | O_NOCTTY); 9540Sstevel@tonic-gate if (fd >= 0) { 9550Sstevel@tonic-gate (void) dup2(fd, 1); 9560Sstevel@tonic-gate (void) dup2(fd, 2); 9570Sstevel@tonic-gate } else { 9580Sstevel@tonic-gate /* 9590Sstevel@tonic-gate * Need to issue an error message somewhere. 9600Sstevel@tonic-gate */ 9610Sstevel@tonic-gate syslog(LOG_CRIT, "init[%d]: cannot open %s; %s\n", 9620Sstevel@tonic-gate getpid(), SYSCON, strerror(errno)); 9630Sstevel@tonic-gate } 9640Sstevel@tonic-gate 9650Sstevel@tonic-gate /* 9660Sstevel@tonic-gate * Execute the "su" program. 9670Sstevel@tonic-gate */ 9680Sstevel@tonic-gate (void) execle(SU, SU, "-", (char *)0, glob_envp); 9690Sstevel@tonic-gate console(B_TRUE, "execle of %s failed: %s\n", SU, 9700Sstevel@tonic-gate strerror(errno)); 9710Sstevel@tonic-gate timer(5); 9720Sstevel@tonic-gate exit(1); 9730Sstevel@tonic-gate } 9740Sstevel@tonic-gate 9750Sstevel@tonic-gate /* 9760Sstevel@tonic-gate * If we are the parent, wait around for the child to die 9770Sstevel@tonic-gate * or for "init" to be signaled to change levels. 9780Sstevel@tonic-gate */ 9790Sstevel@tonic-gate while (waitproc(su_process) == FAILURE) { 9800Sstevel@tonic-gate /* 9810Sstevel@tonic-gate * All other reasons for waking are ignored when in 9820Sstevel@tonic-gate * single-user mode. The only child we are interested 9830Sstevel@tonic-gate * in is being waited for explicitly by waitproc(). 9840Sstevel@tonic-gate */ 9850Sstevel@tonic-gate wakeup.w_mask = 0; 9860Sstevel@tonic-gate } 9870Sstevel@tonic-gate } 9880Sstevel@tonic-gate 9890Sstevel@tonic-gate /* 9900Sstevel@tonic-gate * remv() scans through "proc_table" and performs cleanup. If 9910Sstevel@tonic-gate * there is a process in the table, which shouldn't be here at 9920Sstevel@tonic-gate * the current run level, then remv() kills the process. 9930Sstevel@tonic-gate */ 9940Sstevel@tonic-gate static void 9950Sstevel@tonic-gate remv() 9960Sstevel@tonic-gate { 9970Sstevel@tonic-gate struct PROC_TABLE *process; 9980Sstevel@tonic-gate struct CMD_LINE cmd; 9990Sstevel@tonic-gate char cmd_string[MAXCMDL]; 10000Sstevel@tonic-gate int change_level; 10010Sstevel@tonic-gate 10020Sstevel@tonic-gate change_level = (cur_state != prev_state ? TRUE : FALSE); 10030Sstevel@tonic-gate 10040Sstevel@tonic-gate /* 10050Sstevel@tonic-gate * Clear the TOUCHED flag on all entries so that when we have 10060Sstevel@tonic-gate * finished scanning inittab, we will be able to tell if we 10070Sstevel@tonic-gate * have any processes for which there is no entry in inittab. 10080Sstevel@tonic-gate */ 10090Sstevel@tonic-gate for (process = proc_table; 10100Sstevel@tonic-gate (process < proc_table + num_proc); process++) { 10110Sstevel@tonic-gate process->p_flags &= ~TOUCHED; 10120Sstevel@tonic-gate } 10130Sstevel@tonic-gate 10140Sstevel@tonic-gate /* 10150Sstevel@tonic-gate * Scan all inittab entries. 10160Sstevel@tonic-gate */ 10170Sstevel@tonic-gate while (getcmd(&cmd, &cmd_string[0]) == TRUE) { 10180Sstevel@tonic-gate /* Scan for process which goes with this entry in inittab. */ 10190Sstevel@tonic-gate for (process = proc_table; 10200Sstevel@tonic-gate (process < proc_table + num_proc); process++) { 10210Sstevel@tonic-gate if ((process->p_flags & OCCUPIED) == 0 || 10220Sstevel@tonic-gate !id_eq(process->p_id, cmd.c_id)) 10230Sstevel@tonic-gate continue; 10240Sstevel@tonic-gate 10250Sstevel@tonic-gate /* 10260Sstevel@tonic-gate * This slot contains the process we are looking for. 10270Sstevel@tonic-gate */ 10280Sstevel@tonic-gate 10290Sstevel@tonic-gate /* 10300Sstevel@tonic-gate * Is the cur_state SINGLE_USER or is this process 10310Sstevel@tonic-gate * marked as "off" or was this proc started by some 10320Sstevel@tonic-gate * mechanism other than LVL{a|b|c} and the current level 10330Sstevel@tonic-gate * does not support this process? 10340Sstevel@tonic-gate */ 10350Sstevel@tonic-gate if (cur_state == SINGLE_USER || 10360Sstevel@tonic-gate cmd.c_action == M_OFF || 10370Sstevel@tonic-gate ((cmd.c_levels & state_to_mask(cur_state)) == 0 && 10380Sstevel@tonic-gate (process->p_flags & DEMANDREQUEST) == 0)) { 10390Sstevel@tonic-gate if (process->p_flags & LIVING) { 10400Sstevel@tonic-gate /* 10410Sstevel@tonic-gate * Touch this entry so we know we have 10420Sstevel@tonic-gate * treated it. Note that procs which 10430Sstevel@tonic-gate * are already dead at this point and 10440Sstevel@tonic-gate * should not be restarted are left 10450Sstevel@tonic-gate * untouched. This causes their slot to 10460Sstevel@tonic-gate * be freed later after dead accounting 10470Sstevel@tonic-gate * is done. 10480Sstevel@tonic-gate */ 10490Sstevel@tonic-gate process->p_flags |= TOUCHED; 10500Sstevel@tonic-gate 10510Sstevel@tonic-gate if ((process->p_flags & KILLED) == 0) { 10520Sstevel@tonic-gate if (change_level) { 10530Sstevel@tonic-gate process->p_flags 10540Sstevel@tonic-gate |= WARNED; 10550Sstevel@tonic-gate (void) kill( 10560Sstevel@tonic-gate process->p_pid, 10570Sstevel@tonic-gate SIGTERM); 10580Sstevel@tonic-gate } else { 10590Sstevel@tonic-gate /* 10600Sstevel@tonic-gate * Fork a killing proc 10610Sstevel@tonic-gate * so "init" can 10620Sstevel@tonic-gate * continue without 10630Sstevel@tonic-gate * having to pause for 10640Sstevel@tonic-gate * TWARN seconds. 10650Sstevel@tonic-gate */ 10660Sstevel@tonic-gate killproc( 10670Sstevel@tonic-gate process->p_pid); 10680Sstevel@tonic-gate } 10690Sstevel@tonic-gate process->p_flags |= KILLED; 10700Sstevel@tonic-gate } 10710Sstevel@tonic-gate } 10720Sstevel@tonic-gate } else { 10730Sstevel@tonic-gate /* 10740Sstevel@tonic-gate * Process can exist at current level. If it is 10750Sstevel@tonic-gate * still alive or a DEMANDREQUEST we touch it so 10760Sstevel@tonic-gate * it will be left alone. Otherwise we leave it 10770Sstevel@tonic-gate * untouched so it will be accounted for and 10780Sstevel@tonic-gate * cleaned up later in remv(). Dead 10790Sstevel@tonic-gate * DEMANDREQUESTs will be accounted but not 10800Sstevel@tonic-gate * freed. 10810Sstevel@tonic-gate */ 10820Sstevel@tonic-gate if (process->p_flags & 10830Sstevel@tonic-gate (LIVING|NOCLEANUP|DEMANDREQUEST)) 10840Sstevel@tonic-gate process->p_flags |= TOUCHED; 10850Sstevel@tonic-gate } 10860Sstevel@tonic-gate 10870Sstevel@tonic-gate break; 10880Sstevel@tonic-gate } 10890Sstevel@tonic-gate } 10900Sstevel@tonic-gate 10910Sstevel@tonic-gate st_write(); 10920Sstevel@tonic-gate 10930Sstevel@tonic-gate /* 10940Sstevel@tonic-gate * If this was a change of levels call, scan through the 10950Sstevel@tonic-gate * process table for processes that were warned to die. If any 10960Sstevel@tonic-gate * are found that haven't left yet, sleep for TWARN seconds and 10970Sstevel@tonic-gate * then send final terminations to any that haven't died yet. 10980Sstevel@tonic-gate */ 10990Sstevel@tonic-gate if (change_level) { 11000Sstevel@tonic-gate 11010Sstevel@tonic-gate /* 11020Sstevel@tonic-gate * Set the alarm for TWARN seconds on the assumption 11030Sstevel@tonic-gate * that there will be some that need to be waited for. 11040Sstevel@tonic-gate * This won't harm anything except we are guaranteed to 11050Sstevel@tonic-gate * wakeup in TWARN seconds whether we need to or not. 11060Sstevel@tonic-gate */ 11070Sstevel@tonic-gate setimer(TWARN); 11080Sstevel@tonic-gate 11090Sstevel@tonic-gate /* 11100Sstevel@tonic-gate * Scan for processes which should be dying. We hope they 11110Sstevel@tonic-gate * will die without having to be sent a SIGKILL signal. 11120Sstevel@tonic-gate */ 11130Sstevel@tonic-gate for (process = proc_table; 11140Sstevel@tonic-gate (process < proc_table + num_proc); process++) { 11150Sstevel@tonic-gate /* 11160Sstevel@tonic-gate * If this process should die, hasn't yet, and the 11170Sstevel@tonic-gate * TWARN time hasn't expired yet, wait for process 11180Sstevel@tonic-gate * to die or for timer to expire. 11190Sstevel@tonic-gate */ 11200Sstevel@tonic-gate while (time_up == FALSE && 11210Sstevel@tonic-gate (process->p_flags & (WARNED|LIVING|OCCUPIED)) == 11220Sstevel@tonic-gate (WARNED|LIVING|OCCUPIED)) 11230Sstevel@tonic-gate (void) pause(); 11240Sstevel@tonic-gate 11250Sstevel@tonic-gate if (time_up == TRUE) 11260Sstevel@tonic-gate break; 11270Sstevel@tonic-gate } 11280Sstevel@tonic-gate 11290Sstevel@tonic-gate /* 11300Sstevel@tonic-gate * If we reached the end of the table without the timer 11310Sstevel@tonic-gate * expiring, then there are no procs which will have to be 11320Sstevel@tonic-gate * sent the SIGKILL signal. If the timer has expired, then 11330Sstevel@tonic-gate * it is necessary to scan the table again and send signals 11340Sstevel@tonic-gate * to all processes which aren't going away nicely. 11350Sstevel@tonic-gate */ 11360Sstevel@tonic-gate if (time_up == TRUE) { 11370Sstevel@tonic-gate for (process = proc_table; 11380Sstevel@tonic-gate (process < proc_table + num_proc); process++) { 11390Sstevel@tonic-gate if ((process->p_flags & 11400Sstevel@tonic-gate (WARNED|LIVING|OCCUPIED)) == 11410Sstevel@tonic-gate (WARNED|LIVING|OCCUPIED)) 11420Sstevel@tonic-gate (void) kill(process->p_pid, SIGKILL); 11430Sstevel@tonic-gate } 11440Sstevel@tonic-gate } 11450Sstevel@tonic-gate setimer(0); 11460Sstevel@tonic-gate } 11470Sstevel@tonic-gate 11480Sstevel@tonic-gate /* 11490Sstevel@tonic-gate * Rescan the proc_table for two kinds of entry, those marked LIVING, 11500Sstevel@tonic-gate * NAMED, which don't have an entry in inittab (haven't been TOUCHED 11510Sstevel@tonic-gate * by the above scanning), and haven't been sent kill signals, and 11520Sstevel@tonic-gate * those entries marked not LIVING, NAMED. The former procs are killed. 11530Sstevel@tonic-gate * The latter have DEAD_PROCESS accounting done and the slot cleared. 11540Sstevel@tonic-gate */ 11550Sstevel@tonic-gate for (process = proc_table; 11560Sstevel@tonic-gate (process < proc_table + num_proc); process++) { 11570Sstevel@tonic-gate if ((process->p_flags & (LIVING|NAMED|TOUCHED|KILLED|OCCUPIED)) 11580Sstevel@tonic-gate == (LIVING|NAMED|OCCUPIED)) { 11590Sstevel@tonic-gate killproc(process->p_pid); 11600Sstevel@tonic-gate process->p_flags |= KILLED; 11610Sstevel@tonic-gate } else if ((process->p_flags & (LIVING|NAMED|OCCUPIED)) == 11620Sstevel@tonic-gate (NAMED|OCCUPIED)) { 11630Sstevel@tonic-gate (void) account(DEAD_PROCESS, process, NULL); 11640Sstevel@tonic-gate /* 11650Sstevel@tonic-gate * If this named proc hasn't been TOUCHED, then free the 11660Sstevel@tonic-gate * space. It has either died of it's own accord, but 11670Sstevel@tonic-gate * isn't respawnable or it was killed because it 11680Sstevel@tonic-gate * shouldn't exist at this level. 11690Sstevel@tonic-gate */ 11700Sstevel@tonic-gate if ((process->p_flags & TOUCHED) == 0) 11710Sstevel@tonic-gate process->p_flags = 0; 11720Sstevel@tonic-gate } 11730Sstevel@tonic-gate } 11740Sstevel@tonic-gate 11750Sstevel@tonic-gate st_write(); 11760Sstevel@tonic-gate } 11770Sstevel@tonic-gate 11780Sstevel@tonic-gate /* 11790Sstevel@tonic-gate * Extract the svc.startd command line and whether to restart it from its 11800Sstevel@tonic-gate * inittab entry. 11810Sstevel@tonic-gate */ 11820Sstevel@tonic-gate /*ARGSUSED*/ 11830Sstevel@tonic-gate static void 11840Sstevel@tonic-gate process_startd_line(struct CMD_LINE *cmd, char *cmd_string) 11850Sstevel@tonic-gate { 11860Sstevel@tonic-gate size_t sz; 11870Sstevel@tonic-gate 11880Sstevel@tonic-gate /* Save the command line. */ 11890Sstevel@tonic-gate if (sflg || rflg) { 11900Sstevel@tonic-gate /* Also append -r or -s. */ 11910Sstevel@tonic-gate (void) strlcpy(startd_cline, cmd_string, sizeof (startd_cline)); 11920Sstevel@tonic-gate (void) strlcat(startd_cline, " -", sizeof (startd_cline)); 11930Sstevel@tonic-gate if (sflg) 11940Sstevel@tonic-gate sz = strlcat(startd_cline, "s", sizeof (startd_cline)); 11950Sstevel@tonic-gate if (rflg) 11960Sstevel@tonic-gate sz = strlcat(startd_cline, "r", sizeof (startd_cline)); 11970Sstevel@tonic-gate } else { 11980Sstevel@tonic-gate sz = strlcpy(startd_cline, cmd_string, sizeof (startd_cline)); 11990Sstevel@tonic-gate } 12000Sstevel@tonic-gate 12010Sstevel@tonic-gate if (sz >= sizeof (startd_cline)) { 12020Sstevel@tonic-gate console(B_TRUE, 12030Sstevel@tonic-gate "svc.startd command line too long. Ignoring.\n"); 12040Sstevel@tonic-gate startd_cline[0] = '\0'; 12050Sstevel@tonic-gate return; 12060Sstevel@tonic-gate } 12070Sstevel@tonic-gate } 12080Sstevel@tonic-gate 12090Sstevel@tonic-gate /* 12100Sstevel@tonic-gate * spawn_processes() scans inittab for entries which should be run at this 12110Sstevel@tonic-gate * mode. Processes which should be running but are not, are started. 12120Sstevel@tonic-gate */ 12130Sstevel@tonic-gate static int 12140Sstevel@tonic-gate spawn_processes() 12150Sstevel@tonic-gate { 12160Sstevel@tonic-gate struct PROC_TABLE *pp; 12170Sstevel@tonic-gate struct CMD_LINE cmd; 12180Sstevel@tonic-gate char cmd_string[MAXCMDL]; 12190Sstevel@tonic-gate short lvl_mask; 12200Sstevel@tonic-gate int status; 12210Sstevel@tonic-gate 12220Sstevel@tonic-gate /* 12230Sstevel@tonic-gate * First check the "powerhit" flag. If it is set, make sure the modes 12240Sstevel@tonic-gate * are PF_MODES and clear the "powerhit" flag. Avoid the possible race 12250Sstevel@tonic-gate * on the "powerhit" flag by disallowing a new powerfail interrupt 12260Sstevel@tonic-gate * between the test of the powerhit flag and the clearing of it. 12270Sstevel@tonic-gate */ 12280Sstevel@tonic-gate if (wakeup.w_flags.w_powerhit) { 12290Sstevel@tonic-gate wakeup.w_flags.w_powerhit = 0; 12300Sstevel@tonic-gate op_modes = PF_MODES; 12310Sstevel@tonic-gate } 12320Sstevel@tonic-gate lvl_mask = state_to_mask(cur_state); 12330Sstevel@tonic-gate 12340Sstevel@tonic-gate /* 12350Sstevel@tonic-gate * Scan through all the entries in inittab. 12360Sstevel@tonic-gate */ 12370Sstevel@tonic-gate while ((status = getcmd(&cmd, &cmd_string[0])) == TRUE) { 12380Sstevel@tonic-gate if (id_eq(cmd.c_id, "smf")) { 12390Sstevel@tonic-gate process_startd_line(&cmd, cmd_string); 12400Sstevel@tonic-gate continue; 12410Sstevel@tonic-gate } 12420Sstevel@tonic-gate 12430Sstevel@tonic-gate retry_for_proc_slot: 12440Sstevel@tonic-gate 12450Sstevel@tonic-gate /* 12460Sstevel@tonic-gate * Find out if there is a process slot for this entry already. 12470Sstevel@tonic-gate */ 12480Sstevel@tonic-gate if ((pp = findpslot(&cmd)) == NULLPROC) { 12490Sstevel@tonic-gate /* 12500Sstevel@tonic-gate * we've run out of proc table entries 12510Sstevel@tonic-gate * increase proc_table. 12520Sstevel@tonic-gate */ 12530Sstevel@tonic-gate increase_proc_table_size(); 12540Sstevel@tonic-gate 12550Sstevel@tonic-gate /* 12560Sstevel@tonic-gate * Retry now as we have an empty proc slot. 12570Sstevel@tonic-gate * In case increase_proc_table_size() fails, 12580Sstevel@tonic-gate * we will keep retrying. 12590Sstevel@tonic-gate */ 12600Sstevel@tonic-gate goto retry_for_proc_slot; 12610Sstevel@tonic-gate } 12620Sstevel@tonic-gate 12630Sstevel@tonic-gate /* 12640Sstevel@tonic-gate * If there is an entry, and it is marked as DEMANDREQUEST, 12650Sstevel@tonic-gate * one of the levels a, b, or c is in its levels mask, and 12660Sstevel@tonic-gate * the action field is ONDEMAND and ONDEMAND is a permissable 12670Sstevel@tonic-gate * mode, and the process is dead, then respawn it. 12680Sstevel@tonic-gate */ 12690Sstevel@tonic-gate if (((pp->p_flags & (LIVING|DEMANDREQUEST)) == DEMANDREQUEST) && 12700Sstevel@tonic-gate (cmd.c_levels & MASK_abc) && 12710Sstevel@tonic-gate (cmd.c_action & op_modes) == M_ONDEMAND) { 12720Sstevel@tonic-gate spawn(pp, &cmd); 12730Sstevel@tonic-gate continue; 12740Sstevel@tonic-gate } 12750Sstevel@tonic-gate 12760Sstevel@tonic-gate /* 12770Sstevel@tonic-gate * If the action is not an action we are interested in, 12780Sstevel@tonic-gate * skip the entry. 12790Sstevel@tonic-gate */ 12800Sstevel@tonic-gate if ((cmd.c_action & op_modes) == 0 || pp->p_flags & LIVING || 12810Sstevel@tonic-gate (cmd.c_levels & lvl_mask) == 0) 12820Sstevel@tonic-gate continue; 12830Sstevel@tonic-gate 12840Sstevel@tonic-gate /* 12850Sstevel@tonic-gate * If the modes are the normal modes (ONCE, WAIT, RESPAWN, OFF, 12860Sstevel@tonic-gate * ONDEMAND) and the action field is either OFF or the action 12870Sstevel@tonic-gate * field is ONCE or WAIT and the current level is the same as 12880Sstevel@tonic-gate * the last level, then skip this entry. ONCE and WAIT only 12890Sstevel@tonic-gate * get run when the level changes. 12900Sstevel@tonic-gate */ 12910Sstevel@tonic-gate if (op_modes == NORMAL_MODES && 12920Sstevel@tonic-gate (cmd.c_action == M_OFF || 1293*8735SEnrico.Perla@Sun.COM (cmd.c_action & (M_ONCE|M_WAIT)) && 1294*8735SEnrico.Perla@Sun.COM cur_state == prev_state)) 12950Sstevel@tonic-gate continue; 12960Sstevel@tonic-gate 12970Sstevel@tonic-gate /* 12980Sstevel@tonic-gate * At this point we are interested in performing the action for 12990Sstevel@tonic-gate * this entry. Actions fall into two categories, spinning off 13000Sstevel@tonic-gate * a process and not waiting, and spinning off a process and 13010Sstevel@tonic-gate * waiting for it to die. If the action is ONCE, RESPAWN, 13020Sstevel@tonic-gate * ONDEMAND, POWERFAIL, or BOOT we don't wait for the process 13030Sstevel@tonic-gate * to die, for all other actions we do wait. 13040Sstevel@tonic-gate */ 13050Sstevel@tonic-gate if (cmd.c_action & (M_ONCE | M_RESPAWN | M_PF | M_BOOT)) { 13060Sstevel@tonic-gate spawn(pp, &cmd); 13070Sstevel@tonic-gate 13080Sstevel@tonic-gate } else { 13090Sstevel@tonic-gate spawn(pp, &cmd); 1310*8735SEnrico.Perla@Sun.COM while (waitproc(pp) == FAILURE) 1311*8735SEnrico.Perla@Sun.COM ; 13120Sstevel@tonic-gate (void) account(DEAD_PROCESS, pp, NULL); 13130Sstevel@tonic-gate pp->p_flags = 0; 13140Sstevel@tonic-gate } 13150Sstevel@tonic-gate } 13160Sstevel@tonic-gate return (status); 13170Sstevel@tonic-gate } 13180Sstevel@tonic-gate 13190Sstevel@tonic-gate /* 13200Sstevel@tonic-gate * spawn() spawns a shell, inserts the information about the process 13210Sstevel@tonic-gate * process into the proc_table, and does the startup accounting. 13220Sstevel@tonic-gate */ 13230Sstevel@tonic-gate static void 13240Sstevel@tonic-gate spawn(struct PROC_TABLE *process, struct CMD_LINE *cmd) 13250Sstevel@tonic-gate { 13260Sstevel@tonic-gate int i; 13270Sstevel@tonic-gate int modes, maxfiles; 13280Sstevel@tonic-gate time_t now; 13290Sstevel@tonic-gate struct PROC_TABLE tmproc, *oprocess; 13300Sstevel@tonic-gate 13310Sstevel@tonic-gate /* 13320Sstevel@tonic-gate * The modes to be sent to efork() are 0 unless we are 13330Sstevel@tonic-gate * spawning a LVLa, LVLb, or LVLc entry or we will be 13340Sstevel@tonic-gate * waiting for the death of the child before continuing. 13350Sstevel@tonic-gate */ 13360Sstevel@tonic-gate modes = NAMED; 13370Sstevel@tonic-gate if (process->p_flags & DEMANDREQUEST || cur_state == LVLa || 13380Sstevel@tonic-gate cur_state == LVLb || cur_state == LVLc) 13390Sstevel@tonic-gate modes |= DEMANDREQUEST; 13400Sstevel@tonic-gate if ((cmd->c_action & (M_SYSINIT | M_WAIT | M_BOOTWAIT | M_PWAIT)) != 0) 13410Sstevel@tonic-gate modes |= NOCLEANUP; 13420Sstevel@tonic-gate 13430Sstevel@tonic-gate /* 13440Sstevel@tonic-gate * If this is a respawnable process, check the threshold 13450Sstevel@tonic-gate * information to avoid excessive respawns. 13460Sstevel@tonic-gate */ 13470Sstevel@tonic-gate if (cmd->c_action & M_RESPAWN) { 13480Sstevel@tonic-gate /* 13490Sstevel@tonic-gate * Add NOCLEANUP to all respawnable commands so that the 13500Sstevel@tonic-gate * information about the frequency of respawns isn't lost. 13510Sstevel@tonic-gate */ 13520Sstevel@tonic-gate modes |= NOCLEANUP; 13530Sstevel@tonic-gate (void) time(&now); 13540Sstevel@tonic-gate 13550Sstevel@tonic-gate /* 13560Sstevel@tonic-gate * If no time is assigned, then this is the first time 13570Sstevel@tonic-gate * this command is being processed in this series. Assign 13580Sstevel@tonic-gate * the current time. 13590Sstevel@tonic-gate */ 13600Sstevel@tonic-gate if (process->p_time == 0L) 13610Sstevel@tonic-gate process->p_time = now; 13620Sstevel@tonic-gate 13630Sstevel@tonic-gate if (process->p_count++ == SPAWN_LIMIT) { 13640Sstevel@tonic-gate 13650Sstevel@tonic-gate if ((now - process->p_time) < SPAWN_INTERVAL) { 13660Sstevel@tonic-gate /* 13670Sstevel@tonic-gate * Process is respawning too rapidly. Print 13680Sstevel@tonic-gate * message and refuse to respawn it for now. 13690Sstevel@tonic-gate */ 13700Sstevel@tonic-gate console(B_TRUE, "Command is respawning too " 13710Sstevel@tonic-gate "rapidly. Check for possible errors.\n" 13720Sstevel@tonic-gate "id:%4s \"%s\"\n", 13730Sstevel@tonic-gate &cmd->c_id[0], &cmd->c_command[EXEC]); 13740Sstevel@tonic-gate return; 13750Sstevel@tonic-gate } 13760Sstevel@tonic-gate process->p_time = now; 13770Sstevel@tonic-gate process->p_count = 0; 13780Sstevel@tonic-gate 13790Sstevel@tonic-gate } else if (process->p_count > SPAWN_LIMIT) { 13800Sstevel@tonic-gate /* 13810Sstevel@tonic-gate * If process has been respawning too rapidly and 13820Sstevel@tonic-gate * the inhibit time limit hasn't expired yet, we 13830Sstevel@tonic-gate * refuse to respawn. 13840Sstevel@tonic-gate */ 13850Sstevel@tonic-gate if (now - process->p_time < SPAWN_INTERVAL + INHIBIT) 13860Sstevel@tonic-gate return; 13870Sstevel@tonic-gate process->p_time = now; 13880Sstevel@tonic-gate process->p_count = 0; 13890Sstevel@tonic-gate } 13900Sstevel@tonic-gate rsflag = TRUE; 13910Sstevel@tonic-gate } 13920Sstevel@tonic-gate 13930Sstevel@tonic-gate /* 13940Sstevel@tonic-gate * Spawn a child process to execute this command. 13950Sstevel@tonic-gate */ 13960Sstevel@tonic-gate (void) sigset(SIGCLD, SIG_DFL); 13970Sstevel@tonic-gate oprocess = process; 13980Sstevel@tonic-gate while ((process = efork(cmd->c_action, oprocess, modes)) == NO_ROOM) 13990Sstevel@tonic-gate (void) pause(); 14000Sstevel@tonic-gate 14010Sstevel@tonic-gate if (process == NULLPROC) { 14020Sstevel@tonic-gate 14030Sstevel@tonic-gate /* 14040Sstevel@tonic-gate * We are the child. We must make sure we get a different 14050Sstevel@tonic-gate * file pointer for our references to utmpx. Otherwise our 14060Sstevel@tonic-gate * seeks and reads will compete with those of the parent. 14070Sstevel@tonic-gate */ 14080Sstevel@tonic-gate endutxent(); 14090Sstevel@tonic-gate 14100Sstevel@tonic-gate /* 14110Sstevel@tonic-gate * Perform the accounting for the beginning of a process. 14120Sstevel@tonic-gate * Note that all processes are initially "INIT_PROCESS"es. 14130Sstevel@tonic-gate */ 14140Sstevel@tonic-gate tmproc.p_id[0] = cmd->c_id[0]; 14150Sstevel@tonic-gate tmproc.p_id[1] = cmd->c_id[1]; 14160Sstevel@tonic-gate tmproc.p_id[2] = cmd->c_id[2]; 14170Sstevel@tonic-gate tmproc.p_id[3] = cmd->c_id[3]; 14180Sstevel@tonic-gate tmproc.p_pid = getpid(); 14190Sstevel@tonic-gate tmproc.p_exit = 0; 14200Sstevel@tonic-gate (void) account(INIT_PROCESS, &tmproc, 14210Sstevel@tonic-gate prog_name(&cmd->c_command[EXEC])); 14220Sstevel@tonic-gate maxfiles = ulimit(UL_GDESLIM, 0); 14230Sstevel@tonic-gate for (i = 0; i < maxfiles; i++) 14240Sstevel@tonic-gate (void) fcntl(i, F_SETFD, FD_CLOEXEC); 14250Sstevel@tonic-gate 14260Sstevel@tonic-gate /* 14270Sstevel@tonic-gate * Now exec a shell with the -c option and the command 14280Sstevel@tonic-gate * from inittab. 14290Sstevel@tonic-gate */ 14300Sstevel@tonic-gate (void) execle(SH, "INITSH", "-c", cmd->c_command, (char *)0, 14310Sstevel@tonic-gate glob_envp); 14320Sstevel@tonic-gate console(B_TRUE, "Command\n\"%s\"\n failed to execute. errno " 14330Sstevel@tonic-gate "= %d (exec of shell failed)\n", cmd->c_command, errno); 14340Sstevel@tonic-gate 14350Sstevel@tonic-gate /* 14360Sstevel@tonic-gate * Don't come back so quickly that "init" doesn't have a 14370Sstevel@tonic-gate * chance to finish putting this child in "proc_table". 14380Sstevel@tonic-gate */ 14390Sstevel@tonic-gate timer(20); 14400Sstevel@tonic-gate exit(1); 14410Sstevel@tonic-gate 14420Sstevel@tonic-gate } 14430Sstevel@tonic-gate 14440Sstevel@tonic-gate /* 14450Sstevel@tonic-gate * We are the parent. Insert the necessary 14460Sstevel@tonic-gate * information in the proc_table. 14470Sstevel@tonic-gate */ 14480Sstevel@tonic-gate process->p_id[0] = cmd->c_id[0]; 14490Sstevel@tonic-gate process->p_id[1] = cmd->c_id[1]; 14500Sstevel@tonic-gate process->p_id[2] = cmd->c_id[2]; 14510Sstevel@tonic-gate process->p_id[3] = cmd->c_id[3]; 14520Sstevel@tonic-gate 14530Sstevel@tonic-gate st_write(); 14540Sstevel@tonic-gate 14550Sstevel@tonic-gate (void) sigset(SIGCLD, childeath); 14560Sstevel@tonic-gate } 14570Sstevel@tonic-gate 14580Sstevel@tonic-gate /* 14590Sstevel@tonic-gate * findpslot() finds the old slot in the process table for the 14600Sstevel@tonic-gate * command with the same id, or it finds an empty slot. 14610Sstevel@tonic-gate */ 14620Sstevel@tonic-gate static struct PROC_TABLE * 14630Sstevel@tonic-gate findpslot(struct CMD_LINE *cmd) 14640Sstevel@tonic-gate { 14650Sstevel@tonic-gate struct PROC_TABLE *process; 14660Sstevel@tonic-gate struct PROC_TABLE *empty = NULLPROC; 14670Sstevel@tonic-gate 14680Sstevel@tonic-gate for (process = proc_table; 14690Sstevel@tonic-gate (process < proc_table + num_proc); process++) { 14700Sstevel@tonic-gate if (process->p_flags & OCCUPIED && 14710Sstevel@tonic-gate id_eq(process->p_id, cmd->c_id)) 14720Sstevel@tonic-gate break; 14730Sstevel@tonic-gate 14740Sstevel@tonic-gate /* 14750Sstevel@tonic-gate * If the entry is totally empty and "empty" is still 0, 14760Sstevel@tonic-gate * remember where this hole is and make sure the slot is 14770Sstevel@tonic-gate * zeroed out. 14780Sstevel@tonic-gate */ 14790Sstevel@tonic-gate if (empty == NULLPROC && (process->p_flags & OCCUPIED) == 0) { 14800Sstevel@tonic-gate empty = process; 14810Sstevel@tonic-gate process->p_id[0] = '\0'; 14820Sstevel@tonic-gate process->p_id[1] = '\0'; 14830Sstevel@tonic-gate process->p_id[2] = '\0'; 14840Sstevel@tonic-gate process->p_id[3] = '\0'; 14850Sstevel@tonic-gate process->p_pid = 0; 14860Sstevel@tonic-gate process->p_time = 0L; 14870Sstevel@tonic-gate process->p_count = 0; 14880Sstevel@tonic-gate process->p_flags = 0; 14890Sstevel@tonic-gate process->p_exit = 0; 14900Sstevel@tonic-gate } 14910Sstevel@tonic-gate } 14920Sstevel@tonic-gate 14930Sstevel@tonic-gate /* 14940Sstevel@tonic-gate * If there is no entry for this slot, then there should be an 14950Sstevel@tonic-gate * empty slot. If there is no empty slot, then we've run out 14960Sstevel@tonic-gate * of proc_table space. If the latter is true, empty will be 14970Sstevel@tonic-gate * NULL and the caller will have to complain. 14980Sstevel@tonic-gate */ 14990Sstevel@tonic-gate if (process == (proc_table + num_proc)) 15000Sstevel@tonic-gate process = empty; 15010Sstevel@tonic-gate 15020Sstevel@tonic-gate return (process); 15030Sstevel@tonic-gate } 15040Sstevel@tonic-gate 15050Sstevel@tonic-gate /* 15060Sstevel@tonic-gate * getcmd() parses lines from inittab. Each time it finds a command line 15070Sstevel@tonic-gate * it will return TRUE as well as fill the passed CMD_LINE structure and 15080Sstevel@tonic-gate * the shell command string. When the end of inittab is reached, FALSE 15090Sstevel@tonic-gate * is returned inittab is automatically opened if it is not currently open 15100Sstevel@tonic-gate * and is closed when the end of the file is reached. 15110Sstevel@tonic-gate */ 15120Sstevel@tonic-gate static FILE *fp_inittab = NULL; 15130Sstevel@tonic-gate 15140Sstevel@tonic-gate static int 15150Sstevel@tonic-gate getcmd(struct CMD_LINE *cmd, char *shcmd) 15160Sstevel@tonic-gate { 15170Sstevel@tonic-gate char *ptr; 15180Sstevel@tonic-gate int c, lastc, state; 15190Sstevel@tonic-gate char *ptr1; 15200Sstevel@tonic-gate int answer, i, proceed; 15210Sstevel@tonic-gate struct stat sbuf; 15220Sstevel@tonic-gate static char *actions[] = { 15230Sstevel@tonic-gate "off", "respawn", "ondemand", "once", "wait", "boot", 15240Sstevel@tonic-gate "bootwait", "powerfail", "powerwait", "initdefault", 15250Sstevel@tonic-gate "sysinit", 15260Sstevel@tonic-gate }; 15270Sstevel@tonic-gate static short act_masks[] = { 15280Sstevel@tonic-gate M_OFF, M_RESPAWN, M_ONDEMAND, M_ONCE, M_WAIT, M_BOOT, 15290Sstevel@tonic-gate M_BOOTWAIT, M_PF, M_PWAIT, M_INITDEFAULT, M_SYSINIT, 15300Sstevel@tonic-gate }; 15310Sstevel@tonic-gate /* 15320Sstevel@tonic-gate * Only these actions will be allowed for entries which 15330Sstevel@tonic-gate * are specified for single-user mode. 15340Sstevel@tonic-gate */ 15350Sstevel@tonic-gate short su_acts = M_INITDEFAULT | M_PF | M_PWAIT | M_WAIT; 15360Sstevel@tonic-gate 15370Sstevel@tonic-gate if (fp_inittab == NULL) { 15380Sstevel@tonic-gate /* 15390Sstevel@tonic-gate * Before attempting to open inittab we stat it to make 15400Sstevel@tonic-gate * sure it currently exists and is not empty. We try 15410Sstevel@tonic-gate * several times because someone may have temporarily 15420Sstevel@tonic-gate * unlinked or truncated the file. 15430Sstevel@tonic-gate */ 15440Sstevel@tonic-gate for (i = 0; i < 3; i++) { 15450Sstevel@tonic-gate if (stat(INITTAB, &sbuf) == -1) { 15460Sstevel@tonic-gate if (i == 2) { 15470Sstevel@tonic-gate console(B_TRUE, 15480Sstevel@tonic-gate "Cannot stat %s, errno: %d\n", 15490Sstevel@tonic-gate INITTAB, errno); 15500Sstevel@tonic-gate return (FAILURE); 15510Sstevel@tonic-gate } else { 15520Sstevel@tonic-gate timer(3); 15530Sstevel@tonic-gate } 15540Sstevel@tonic-gate } else if (sbuf.st_size < 10) { 15550Sstevel@tonic-gate if (i == 2) { 15560Sstevel@tonic-gate console(B_TRUE, 15570Sstevel@tonic-gate "%s truncated or corrupted\n", 15580Sstevel@tonic-gate INITTAB); 15590Sstevel@tonic-gate return (FAILURE); 15600Sstevel@tonic-gate } else { 15610Sstevel@tonic-gate timer(3); 15620Sstevel@tonic-gate } 15630Sstevel@tonic-gate } else { 15640Sstevel@tonic-gate break; 15650Sstevel@tonic-gate } 15660Sstevel@tonic-gate } 15670Sstevel@tonic-gate 15680Sstevel@tonic-gate /* 15690Sstevel@tonic-gate * If unable to open inittab, print error message and 15700Sstevel@tonic-gate * return FAILURE to caller. 15710Sstevel@tonic-gate */ 15720Sstevel@tonic-gate if ((fp_inittab = fopen(INITTAB, "r")) == NULL) { 15730Sstevel@tonic-gate console(B_TRUE, "Cannot open %s errno: %d\n", INITTAB, 15740Sstevel@tonic-gate errno); 15750Sstevel@tonic-gate return (FAILURE); 15760Sstevel@tonic-gate } 15770Sstevel@tonic-gate } 15780Sstevel@tonic-gate 15790Sstevel@tonic-gate /* 15800Sstevel@tonic-gate * Keep getting commands from inittab until you find a 15810Sstevel@tonic-gate * good one or run out of file. 15820Sstevel@tonic-gate */ 15830Sstevel@tonic-gate for (answer = FALSE; answer == FALSE; ) { 15840Sstevel@tonic-gate /* 15850Sstevel@tonic-gate * Zero out the cmd itself before trying next line. 15860Sstevel@tonic-gate */ 15870Sstevel@tonic-gate bzero(cmd, sizeof (struct CMD_LINE)); 15880Sstevel@tonic-gate 15890Sstevel@tonic-gate /* 15900Sstevel@tonic-gate * Read in lines of inittab, parsing at colons, until a line is 15910Sstevel@tonic-gate * read in which doesn't end with a backslash. Do not start if 15920Sstevel@tonic-gate * the first character read is an EOF. Note that this means 15930Sstevel@tonic-gate * that lines which don't end in a newline are still processed, 15940Sstevel@tonic-gate * since the "for" will terminate normally once started, 15950Sstevel@tonic-gate * regardless of whether line terminates with a newline or EOF. 15960Sstevel@tonic-gate */ 15970Sstevel@tonic-gate state = FAILURE; 15980Sstevel@tonic-gate if ((c = fgetc(fp_inittab)) == EOF) { 15990Sstevel@tonic-gate answer = FALSE; 16000Sstevel@tonic-gate (void) fclose(fp_inittab); 16010Sstevel@tonic-gate fp_inittab = NULL; 16020Sstevel@tonic-gate break; 16030Sstevel@tonic-gate } 16040Sstevel@tonic-gate 16050Sstevel@tonic-gate for (proceed = TRUE, ptr = shcmd, state = ID, lastc = '\0'; 16060Sstevel@tonic-gate proceed && c != EOF; 16070Sstevel@tonic-gate lastc = c, c = fgetc(fp_inittab)) { 16080Sstevel@tonic-gate /* If we're not in the FAILURE state and haven't */ 16090Sstevel@tonic-gate /* yet reached the shell command field, process */ 16100Sstevel@tonic-gate /* the line, otherwise just look for a real end */ 16110Sstevel@tonic-gate /* of line. */ 16120Sstevel@tonic-gate if (state != FAILURE && state != COMMAND) { 16130Sstevel@tonic-gate /* 16140Sstevel@tonic-gate * Squeeze out spaces and tabs. 16150Sstevel@tonic-gate */ 16160Sstevel@tonic-gate if (c == ' ' || c == '\t') 16170Sstevel@tonic-gate continue; 16180Sstevel@tonic-gate 16190Sstevel@tonic-gate /* 16200Sstevel@tonic-gate * Ignore characters in a comment, except for the \n. 16210Sstevel@tonic-gate */ 16220Sstevel@tonic-gate if (state == COMMENT) { 16230Sstevel@tonic-gate if (c == '\n') { 16240Sstevel@tonic-gate lastc = ' '; 16250Sstevel@tonic-gate break; 16260Sstevel@tonic-gate } else { 16270Sstevel@tonic-gate continue; 16280Sstevel@tonic-gate } 16290Sstevel@tonic-gate } 16300Sstevel@tonic-gate 16310Sstevel@tonic-gate /* 16320Sstevel@tonic-gate * Detect comments (lines whose first non-whitespace 16330Sstevel@tonic-gate * character is '#') by checking that we're at the 16340Sstevel@tonic-gate * beginning of a line, have seen a '#', and haven't 16350Sstevel@tonic-gate * yet accumulated any characters. 16360Sstevel@tonic-gate */ 16370Sstevel@tonic-gate if (state == ID && c == '#' && ptr == shcmd) { 16380Sstevel@tonic-gate state = COMMENT; 16390Sstevel@tonic-gate continue; 16400Sstevel@tonic-gate } 16410Sstevel@tonic-gate 16420Sstevel@tonic-gate /* 16430Sstevel@tonic-gate * If the character is a ':', then check the 16440Sstevel@tonic-gate * previous field for correctness and advance 16450Sstevel@tonic-gate * to the next field. 16460Sstevel@tonic-gate */ 16470Sstevel@tonic-gate if (c == ':') { 16480Sstevel@tonic-gate switch (state) { 16490Sstevel@tonic-gate 16500Sstevel@tonic-gate case ID : 16510Sstevel@tonic-gate /* 16520Sstevel@tonic-gate * Check to see that there are only 16530Sstevel@tonic-gate * 1 to 4 characters for the id. 16540Sstevel@tonic-gate */ 16550Sstevel@tonic-gate if ((i = ptr - shcmd) < 1 || i > 4) { 16560Sstevel@tonic-gate state = FAILURE; 16570Sstevel@tonic-gate } else { 16580Sstevel@tonic-gate bcopy(shcmd, &cmd->c_id[0], i); 16590Sstevel@tonic-gate ptr = shcmd; 16600Sstevel@tonic-gate state = LEVELS; 16610Sstevel@tonic-gate } 16620Sstevel@tonic-gate break; 16630Sstevel@tonic-gate 16640Sstevel@tonic-gate case LEVELS : 16650Sstevel@tonic-gate /* 16660Sstevel@tonic-gate * Build a mask for all the levels for 16670Sstevel@tonic-gate * which this command will be legal. 16680Sstevel@tonic-gate */ 16690Sstevel@tonic-gate for (cmd->c_levels = 0, ptr1 = shcmd; 16700Sstevel@tonic-gate ptr1 < ptr; ptr1++) { 16710Sstevel@tonic-gate int mask; 16720Sstevel@tonic-gate if (lvlname_to_mask(*ptr1, 16730Sstevel@tonic-gate &mask) == -1) { 16740Sstevel@tonic-gate state = FAILURE; 16750Sstevel@tonic-gate break; 16760Sstevel@tonic-gate } 16770Sstevel@tonic-gate cmd->c_levels |= mask; 16780Sstevel@tonic-gate } 16790Sstevel@tonic-gate if (state != FAILURE) { 16800Sstevel@tonic-gate state = ACTION; 16810Sstevel@tonic-gate ptr = shcmd; /* Reset the buffer */ 16820Sstevel@tonic-gate } 16830Sstevel@tonic-gate break; 16840Sstevel@tonic-gate 16850Sstevel@tonic-gate case ACTION : 16860Sstevel@tonic-gate /* 16870Sstevel@tonic-gate * Null terminate the string in shcmd buffer and 16880Sstevel@tonic-gate * then try to match against legal actions. If 16890Sstevel@tonic-gate * the field is of length 0, then the default of 16900Sstevel@tonic-gate * "RESPAWN" is used if the id is numeric, 16910Sstevel@tonic-gate * otherwise the default is "OFF". 16920Sstevel@tonic-gate */ 16930Sstevel@tonic-gate if (ptr == shcmd) { 16940Sstevel@tonic-gate if (isdigit(cmd->c_id[0]) && 16950Sstevel@tonic-gate (cmd->c_id[1] == '\0' || 16960Sstevel@tonic-gate isdigit(cmd->c_id[1])) && 16970Sstevel@tonic-gate (cmd->c_id[2] == '\0' || 16980Sstevel@tonic-gate isdigit(cmd->c_id[2])) && 16990Sstevel@tonic-gate (cmd->c_id[3] == '\0' || 17000Sstevel@tonic-gate isdigit(cmd->c_id[3]))) 17010Sstevel@tonic-gate cmd->c_action = M_RESPAWN; 17020Sstevel@tonic-gate else 17030Sstevel@tonic-gate cmd->c_action = M_OFF; 17040Sstevel@tonic-gate } else { 17050Sstevel@tonic-gate for (cmd->c_action = 0, i = 0, *ptr = '\0'; 17060Sstevel@tonic-gate i < sizeof (actions)/sizeof (char *); 17070Sstevel@tonic-gate i++) { 17080Sstevel@tonic-gate if (strcmp(shcmd, actions[i]) == 0) { 17090Sstevel@tonic-gate if ((cmd->c_levels & MASKSU) && 17100Sstevel@tonic-gate !(act_masks[i] & su_acts)) 17110Sstevel@tonic-gate cmd->c_action = 0; 17120Sstevel@tonic-gate else 17130Sstevel@tonic-gate cmd->c_action = act_masks[i]; 17140Sstevel@tonic-gate break; 17150Sstevel@tonic-gate } 17160Sstevel@tonic-gate } 17170Sstevel@tonic-gate } 17180Sstevel@tonic-gate 17190Sstevel@tonic-gate /* 17200Sstevel@tonic-gate * If the action didn't match any legal action, 17210Sstevel@tonic-gate * set state to FAILURE. 17220Sstevel@tonic-gate */ 17230Sstevel@tonic-gate if (cmd->c_action == 0) { 17240Sstevel@tonic-gate state = FAILURE; 17250Sstevel@tonic-gate } else { 17260Sstevel@tonic-gate state = COMMAND; 17270Sstevel@tonic-gate (void) strcpy(shcmd, "exec "); 17280Sstevel@tonic-gate } 17290Sstevel@tonic-gate ptr = shcmd + EXEC; 17300Sstevel@tonic-gate break; 17310Sstevel@tonic-gate } 17320Sstevel@tonic-gate continue; 17330Sstevel@tonic-gate } 17340Sstevel@tonic-gate } 17350Sstevel@tonic-gate 17360Sstevel@tonic-gate /* If the character is a '\n', then this is the end of a */ 17370Sstevel@tonic-gate /* line. If the '\n' wasn't preceded by a backslash, */ 17380Sstevel@tonic-gate /* it is also the end of an inittab command. If it was */ 17390Sstevel@tonic-gate /* preceded by a backslash then the next line is a */ 17400Sstevel@tonic-gate /* continuation. Note that the continuation '\n' falls */ 17410Sstevel@tonic-gate /* through and is treated like other characters and is */ 17420Sstevel@tonic-gate /* stored in the shell command line. */ 17430Sstevel@tonic-gate if (c == '\n' && lastc != '\\') { 17440Sstevel@tonic-gate proceed = FALSE; 17450Sstevel@tonic-gate *ptr = '\0'; 17460Sstevel@tonic-gate break; 17470Sstevel@tonic-gate } 17480Sstevel@tonic-gate 17490Sstevel@tonic-gate /* For all other characters just stuff them into the */ 17500Sstevel@tonic-gate /* command as long as there aren't too many of them. */ 17510Sstevel@tonic-gate /* Make sure there is room for a terminating '\0' also. */ 17520Sstevel@tonic-gate if (ptr >= shcmd + MAXCMDL - 1) 17530Sstevel@tonic-gate state = FAILURE; 17540Sstevel@tonic-gate else 17550Sstevel@tonic-gate *ptr++ = (char)c; 17560Sstevel@tonic-gate 17570Sstevel@tonic-gate /* If the character we just stored was a quoted */ 17580Sstevel@tonic-gate /* backslash, then change "c" to '\0', so that this */ 17590Sstevel@tonic-gate /* backslash will not cause a subsequent '\n' to appear */ 17600Sstevel@tonic-gate /* quoted. In otherwords '\' '\' '\n' is the real end */ 17610Sstevel@tonic-gate /* of a command, while '\' '\n' is a continuation. */ 17620Sstevel@tonic-gate if (c == '\\' && lastc == '\\') 17630Sstevel@tonic-gate c = '\0'; 17640Sstevel@tonic-gate } 17650Sstevel@tonic-gate 17660Sstevel@tonic-gate /* 17670Sstevel@tonic-gate * Make sure all the fields are properly specified 17680Sstevel@tonic-gate * for a good command line. 17690Sstevel@tonic-gate */ 17700Sstevel@tonic-gate if (state == COMMAND) { 17710Sstevel@tonic-gate answer = TRUE; 17720Sstevel@tonic-gate cmd->c_command = shcmd; 17730Sstevel@tonic-gate 17740Sstevel@tonic-gate /* 17750Sstevel@tonic-gate * If no default level was supplied, insert 17760Sstevel@tonic-gate * all numerical levels. 17770Sstevel@tonic-gate */ 17780Sstevel@tonic-gate if (cmd->c_levels == 0) 17790Sstevel@tonic-gate cmd->c_levels = MASK_NUMERIC; 17800Sstevel@tonic-gate 17810Sstevel@tonic-gate /* 17820Sstevel@tonic-gate * If no action has been supplied, declare this 17830Sstevel@tonic-gate * entry to be OFF. 17840Sstevel@tonic-gate */ 17850Sstevel@tonic-gate if (cmd->c_action == 0) 17860Sstevel@tonic-gate cmd->c_action = M_OFF; 17870Sstevel@tonic-gate 17880Sstevel@tonic-gate /* 17890Sstevel@tonic-gate * If no shell command has been supplied, make sure 17900Sstevel@tonic-gate * there is a null string in the command field. 17910Sstevel@tonic-gate */ 17920Sstevel@tonic-gate if (ptr == shcmd + EXEC) 17930Sstevel@tonic-gate *shcmd = '\0'; 17940Sstevel@tonic-gate } else 17950Sstevel@tonic-gate answer = FALSE; 17960Sstevel@tonic-gate 17970Sstevel@tonic-gate /* 17980Sstevel@tonic-gate * If we have reached the end of inittab, then close it 17990Sstevel@tonic-gate * and quit trying to find a good command line. 18000Sstevel@tonic-gate */ 18010Sstevel@tonic-gate if (c == EOF) { 18020Sstevel@tonic-gate (void) fclose(fp_inittab); 18030Sstevel@tonic-gate fp_inittab = NULL; 18040Sstevel@tonic-gate break; 18050Sstevel@tonic-gate } 18060Sstevel@tonic-gate } 18070Sstevel@tonic-gate return (answer); 18080Sstevel@tonic-gate } 18090Sstevel@tonic-gate 18100Sstevel@tonic-gate /* 18110Sstevel@tonic-gate * lvlname_to_state(): convert the character name of a state to its level 18120Sstevel@tonic-gate * (its corresponding signal number). 18130Sstevel@tonic-gate */ 18140Sstevel@tonic-gate static int 18150Sstevel@tonic-gate lvlname_to_state(char name) 18160Sstevel@tonic-gate { 18170Sstevel@tonic-gate int i; 18180Sstevel@tonic-gate for (i = 0; i < LVL_NELEMS; i++) { 18190Sstevel@tonic-gate if (lvls[i].lvl_name == name) 18200Sstevel@tonic-gate return (lvls[i].lvl_state); 18210Sstevel@tonic-gate } 18220Sstevel@tonic-gate return (-1); 18230Sstevel@tonic-gate } 18240Sstevel@tonic-gate 18250Sstevel@tonic-gate /* 18260Sstevel@tonic-gate * state_to_name(): convert the level to the character name. 18270Sstevel@tonic-gate */ 18280Sstevel@tonic-gate static char 18290Sstevel@tonic-gate state_to_name(int state) 18300Sstevel@tonic-gate { 18310Sstevel@tonic-gate int i; 18320Sstevel@tonic-gate for (i = 0; i < LVL_NELEMS; i++) { 18330Sstevel@tonic-gate if (lvls[i].lvl_state == state) 18340Sstevel@tonic-gate return (lvls[i].lvl_name); 18350Sstevel@tonic-gate } 18360Sstevel@tonic-gate return (-1); 18370Sstevel@tonic-gate } 18380Sstevel@tonic-gate 18390Sstevel@tonic-gate /* 18400Sstevel@tonic-gate * state_to_mask(): return the mask corresponding to a signal number 18410Sstevel@tonic-gate */ 18420Sstevel@tonic-gate static int 18430Sstevel@tonic-gate state_to_mask(int state) 18440Sstevel@tonic-gate { 18450Sstevel@tonic-gate int i; 18460Sstevel@tonic-gate for (i = 0; i < LVL_NELEMS; i++) { 18470Sstevel@tonic-gate if (lvls[i].lvl_state == state) 18480Sstevel@tonic-gate return (lvls[i].lvl_mask); 18490Sstevel@tonic-gate } 18500Sstevel@tonic-gate return (0); /* return 0, since that represents an empty mask */ 18510Sstevel@tonic-gate } 18520Sstevel@tonic-gate 18530Sstevel@tonic-gate /* 18540Sstevel@tonic-gate * lvlname_to_mask(): return the mask corresponding to a levels character name 18550Sstevel@tonic-gate */ 18560Sstevel@tonic-gate static int 18570Sstevel@tonic-gate lvlname_to_mask(char name, int *mask) 18580Sstevel@tonic-gate { 18590Sstevel@tonic-gate int i; 18600Sstevel@tonic-gate for (i = 0; i < LVL_NELEMS; i++) { 18610Sstevel@tonic-gate if (lvls[i].lvl_name == name) { 18620Sstevel@tonic-gate *mask = lvls[i].lvl_mask; 18630Sstevel@tonic-gate return (0); 18640Sstevel@tonic-gate } 18650Sstevel@tonic-gate } 18660Sstevel@tonic-gate return (-1); 18670Sstevel@tonic-gate } 18680Sstevel@tonic-gate 18690Sstevel@tonic-gate /* 18700Sstevel@tonic-gate * state_to_flags(): return the flags corresponding to a runlevel. These 18710Sstevel@tonic-gate * indicate properties of that runlevel. 18720Sstevel@tonic-gate */ 18730Sstevel@tonic-gate static int 18740Sstevel@tonic-gate state_to_flags(int state) 18750Sstevel@tonic-gate { 18760Sstevel@tonic-gate int i; 18770Sstevel@tonic-gate for (i = 0; i < LVL_NELEMS; i++) { 18780Sstevel@tonic-gate if (lvls[i].lvl_state == state) 18790Sstevel@tonic-gate return (lvls[i].lvl_flags); 18800Sstevel@tonic-gate } 18810Sstevel@tonic-gate return (0); 18820Sstevel@tonic-gate } 18830Sstevel@tonic-gate 18840Sstevel@tonic-gate /* 18850Sstevel@tonic-gate * killproc() creates a child which kills the process specified by pid. 18860Sstevel@tonic-gate */ 18870Sstevel@tonic-gate void 18880Sstevel@tonic-gate killproc(pid_t pid) 18890Sstevel@tonic-gate { 18900Sstevel@tonic-gate struct PROC_TABLE *process; 18910Sstevel@tonic-gate 18920Sstevel@tonic-gate (void) sigset(SIGCLD, SIG_DFL); 18930Sstevel@tonic-gate while ((process = efork(M_OFF, NULLPROC, 0)) == NO_ROOM) 18940Sstevel@tonic-gate (void) pause(); 18950Sstevel@tonic-gate (void) sigset(SIGCLD, childeath); 18960Sstevel@tonic-gate 18970Sstevel@tonic-gate if (process == NULLPROC) { 18980Sstevel@tonic-gate /* 18990Sstevel@tonic-gate * efork() sets all signal handlers to the default, so reset 19000Sstevel@tonic-gate * the ALRM handler to make timer() work as expected. 19010Sstevel@tonic-gate */ 19020Sstevel@tonic-gate (void) sigset(SIGALRM, alarmclk); 19030Sstevel@tonic-gate 19040Sstevel@tonic-gate /* 19050Sstevel@tonic-gate * We are the child. Try to terminate the process nicely 19060Sstevel@tonic-gate * first using SIGTERM and if it refuses to die in TWARN 19070Sstevel@tonic-gate * seconds kill it with SIGKILL. 19080Sstevel@tonic-gate */ 19090Sstevel@tonic-gate (void) kill(pid, SIGTERM); 19100Sstevel@tonic-gate (void) timer(TWARN); 19110Sstevel@tonic-gate (void) kill(pid, SIGKILL); 19120Sstevel@tonic-gate (void) exit(0); 19130Sstevel@tonic-gate } 19140Sstevel@tonic-gate } 19150Sstevel@tonic-gate 19160Sstevel@tonic-gate /* 19170Sstevel@tonic-gate * Set up the default environment for all procs to be forked from init. 19180Sstevel@tonic-gate * Read the values from the /etc/default/init file, except for PATH. If 19190Sstevel@tonic-gate * there's not enough room in the environment array, the environment 19200Sstevel@tonic-gate * lines that don't fit are silently discarded. 19210Sstevel@tonic-gate */ 19220Sstevel@tonic-gate void 19230Sstevel@tonic-gate init_env() 19240Sstevel@tonic-gate { 19250Sstevel@tonic-gate char line[MAXCMDL]; 19260Sstevel@tonic-gate FILE *fp; 19270Sstevel@tonic-gate int inquotes, length, wslength; 19280Sstevel@tonic-gate char *tokp, *cp1, *cp2; 19290Sstevel@tonic-gate 19300Sstevel@tonic-gate glob_envp[0] = malloc((unsigned)(strlen(DEF_PATH)+2)); 19310Sstevel@tonic-gate (void) strcpy(glob_envp[0], DEF_PATH); 19320Sstevel@tonic-gate glob_envn = 1; 19330Sstevel@tonic-gate 19340Sstevel@tonic-gate if (rflg) { 19350Sstevel@tonic-gate glob_envp[1] = 1936*8735SEnrico.Perla@Sun.COM malloc((unsigned)(strlen("_DVFS_RECONFIG=YES")+2)); 19370Sstevel@tonic-gate (void) strcpy(glob_envp[1], "_DVFS_RECONFIG=YES"); 19380Sstevel@tonic-gate ++glob_envn; 19390Sstevel@tonic-gate } else if (bflg == 1) { 19400Sstevel@tonic-gate glob_envp[1] = 1941*8735SEnrico.Perla@Sun.COM malloc((unsigned)(strlen("RB_NOBOOTRC=YES")+2)); 19420Sstevel@tonic-gate (void) strcpy(glob_envp[1], "RB_NOBOOTRC=YES"); 19430Sstevel@tonic-gate ++glob_envn; 19440Sstevel@tonic-gate } 19450Sstevel@tonic-gate 19460Sstevel@tonic-gate if ((fp = fopen(ENVFILE, "r")) == NULL) { 19470Sstevel@tonic-gate console(B_TRUE, 19480Sstevel@tonic-gate "Cannot open %s. Environment not initialized.\n", 19490Sstevel@tonic-gate ENVFILE); 19500Sstevel@tonic-gate } else { 19510Sstevel@tonic-gate while (fgets(line, MAXCMDL - 1, fp) != NULL && 19520Sstevel@tonic-gate glob_envn < MAXENVENT - 2) { 19530Sstevel@tonic-gate /* 19540Sstevel@tonic-gate * Toss newline 19550Sstevel@tonic-gate */ 19560Sstevel@tonic-gate length = strlen(line); 19570Sstevel@tonic-gate if (line[length - 1] == '\n') 19580Sstevel@tonic-gate line[length - 1] = '\0'; 19590Sstevel@tonic-gate 19600Sstevel@tonic-gate /* 19610Sstevel@tonic-gate * Ignore blank or comment lines. 19620Sstevel@tonic-gate */ 19630Sstevel@tonic-gate if (line[0] == '#' || line[0] == '\0' || 19640Sstevel@tonic-gate (wslength = strspn(line, " \t\n")) == 19650Sstevel@tonic-gate strlen(line) || 19660Sstevel@tonic-gate strchr(line, '#') == line + wslength) 19670Sstevel@tonic-gate continue; 19680Sstevel@tonic-gate 19690Sstevel@tonic-gate /* 19700Sstevel@tonic-gate * First make a pass through the line and change 19710Sstevel@tonic-gate * any non-quoted semi-colons to blanks so they 19720Sstevel@tonic-gate * will be treated as token separators below. 19730Sstevel@tonic-gate */ 19740Sstevel@tonic-gate inquotes = 0; 19750Sstevel@tonic-gate for (cp1 = line; *cp1 != '\0'; cp1++) { 19760Sstevel@tonic-gate if (*cp1 == '"') { 19770Sstevel@tonic-gate if (inquotes == 0) 19780Sstevel@tonic-gate inquotes = 1; 19790Sstevel@tonic-gate else 19800Sstevel@tonic-gate inquotes = 0; 19810Sstevel@tonic-gate } else if (*cp1 == ';') { 19820Sstevel@tonic-gate if (inquotes == 0) 19830Sstevel@tonic-gate *cp1 = ' '; 19840Sstevel@tonic-gate } 19850Sstevel@tonic-gate } 19860Sstevel@tonic-gate 19870Sstevel@tonic-gate /* 19880Sstevel@tonic-gate * Tokens within the line are separated by blanks 19890Sstevel@tonic-gate * and tabs. For each token in the line which 19900Sstevel@tonic-gate * contains a '=' we strip out any quotes and then 19910Sstevel@tonic-gate * stick the token in the environment array. 19920Sstevel@tonic-gate */ 19930Sstevel@tonic-gate if ((tokp = strtok(line, " \t")) == NULL) 19940Sstevel@tonic-gate continue; 19950Sstevel@tonic-gate do { 19960Sstevel@tonic-gate if (strchr(tokp, '=') == NULL) 19970Sstevel@tonic-gate continue; 19980Sstevel@tonic-gate length = strlen(tokp); 19990Sstevel@tonic-gate while ((cp1 = strpbrk(tokp, "\"\'")) != NULL) { 20000Sstevel@tonic-gate for (cp2 = cp1; 20010Sstevel@tonic-gate cp2 < &tokp[length]; cp2++) 20020Sstevel@tonic-gate *cp2 = *(cp2 + 1); 20030Sstevel@tonic-gate length--; 20040Sstevel@tonic-gate } 20050Sstevel@tonic-gate 20060Sstevel@tonic-gate if (strncmp(tokp, "CMASK=", 20070Sstevel@tonic-gate sizeof ("CMASK=") - 1) == 0) { 20080Sstevel@tonic-gate long t; 20090Sstevel@tonic-gate 20100Sstevel@tonic-gate /* We know there's an = */ 20110Sstevel@tonic-gate t = strtol(strchr(tokp, '=') + 1, NULL, 20120Sstevel@tonic-gate 8); 20130Sstevel@tonic-gate 20140Sstevel@tonic-gate /* Sanity */ 20150Sstevel@tonic-gate if (t <= 077 && t >= 0) 20160Sstevel@tonic-gate cmask = (int)t; 20170Sstevel@tonic-gate (void) umask(cmask); 20180Sstevel@tonic-gate continue; 20190Sstevel@tonic-gate } 20200Sstevel@tonic-gate glob_envp[glob_envn] = 20210Sstevel@tonic-gate malloc((unsigned)(length + 1)); 20220Sstevel@tonic-gate (void) strcpy(glob_envp[glob_envn], tokp); 20230Sstevel@tonic-gate if (++glob_envn >= MAXENVENT - 1) 20240Sstevel@tonic-gate break; 20250Sstevel@tonic-gate } while ((tokp = strtok(NULL, " \t")) != NULL); 20260Sstevel@tonic-gate } 20270Sstevel@tonic-gate 20280Sstevel@tonic-gate /* 20290Sstevel@tonic-gate * Append a null pointer to the environment array 20300Sstevel@tonic-gate * to mark its end. 20310Sstevel@tonic-gate */ 20320Sstevel@tonic-gate glob_envp[glob_envn] = NULL; 20330Sstevel@tonic-gate (void) fclose(fp); 20340Sstevel@tonic-gate } 20350Sstevel@tonic-gate } 20360Sstevel@tonic-gate 20370Sstevel@tonic-gate /* 20380Sstevel@tonic-gate * boot_init(): Do initialization things that should be done at boot. 20390Sstevel@tonic-gate */ 20400Sstevel@tonic-gate void 20410Sstevel@tonic-gate boot_init() 20420Sstevel@tonic-gate { 20430Sstevel@tonic-gate int i; 20440Sstevel@tonic-gate struct PROC_TABLE *process, *oprocess; 20450Sstevel@tonic-gate struct CMD_LINE cmd; 20460Sstevel@tonic-gate char line[MAXCMDL]; 20476073Sacruz char svc_aux[SVC_AUX_SIZE]; 20486073Sacruz char init_svc_fmri[SVC_FMRI_SIZE]; 20490Sstevel@tonic-gate char *old_path; 20500Sstevel@tonic-gate int maxfiles; 20510Sstevel@tonic-gate 20520Sstevel@tonic-gate /* Use INIT_PATH for sysinit cmds */ 20530Sstevel@tonic-gate old_path = glob_envp[0]; 20540Sstevel@tonic-gate glob_envp[0] = malloc((unsigned)(strlen(INIT_PATH)+2)); 20550Sstevel@tonic-gate (void) strcpy(glob_envp[0], INIT_PATH); 20560Sstevel@tonic-gate 20570Sstevel@tonic-gate /* 20580Sstevel@tonic-gate * Scan inittab(4) and process the special svc.startd entry, initdefault 20590Sstevel@tonic-gate * and sysinit entries. 20600Sstevel@tonic-gate */ 20610Sstevel@tonic-gate while (getcmd(&cmd, &line[0]) == TRUE) { 20626073Sacruz if (startd_tmpl >= 0 && id_eq(cmd.c_id, "smf")) { 20630Sstevel@tonic-gate process_startd_line(&cmd, line); 20646073Sacruz (void) snprintf(startd_svc_aux, SVC_AUX_SIZE, 20656073Sacruz INITTAB_ENTRY_ID_STR_FORMAT, cmd.c_id); 20666073Sacruz } else if (cmd.c_action == M_INITDEFAULT) { 20670Sstevel@tonic-gate /* 20680Sstevel@tonic-gate * initdefault is no longer meaningful, as the SMF 20690Sstevel@tonic-gate * milestone controls what (legacy) run level we 20700Sstevel@tonic-gate * boot to. 20710Sstevel@tonic-gate */ 20720Sstevel@tonic-gate console(B_TRUE, 20730Sstevel@tonic-gate "Ignoring legacy \"initdefault\" entry.\n"); 20740Sstevel@tonic-gate } else if (cmd.c_action == M_SYSINIT) { 20750Sstevel@tonic-gate /* 20760Sstevel@tonic-gate * Execute the "sysinit" entry and wait for it to 20770Sstevel@tonic-gate * complete. No bookkeeping is performed on these 20780Sstevel@tonic-gate * entries because we avoid writing to the file system 20790Sstevel@tonic-gate * until after there has been an chance to check it. 20800Sstevel@tonic-gate */ 20810Sstevel@tonic-gate if (process = findpslot(&cmd)) { 20820Sstevel@tonic-gate (void) sigset(SIGCLD, SIG_DFL); 20836073Sacruz (void) snprintf(svc_aux, SVC_AUX_SIZE, 20846073Sacruz INITTAB_ENTRY_ID_STR_FORMAT, cmd.c_id); 20856073Sacruz (void) snprintf(init_svc_fmri, SVC_FMRI_SIZE, 20866073Sacruz SVC_INIT_PREFIX INITTAB_ENTRY_ID_STR_FORMAT, 20876073Sacruz cmd.c_id); 20886073Sacruz if (legacy_tmpl >= 0) { 20896073Sacruz (void) ct_pr_tmpl_set_svc_fmri( 20906073Sacruz legacy_tmpl, init_svc_fmri); 20916073Sacruz (void) ct_pr_tmpl_set_svc_aux( 20926073Sacruz legacy_tmpl, svc_aux); 20936073Sacruz } 20940Sstevel@tonic-gate 20950Sstevel@tonic-gate for (oprocess = process; 20960Sstevel@tonic-gate (process = efork(M_OFF, oprocess, 20970Sstevel@tonic-gate (NAMED|NOCLEANUP))) == NO_ROOM; 20980Sstevel@tonic-gate /* CSTYLED */) 20990Sstevel@tonic-gate ; 21000Sstevel@tonic-gate (void) sigset(SIGCLD, childeath); 21010Sstevel@tonic-gate 21020Sstevel@tonic-gate if (process == NULLPROC) { 21030Sstevel@tonic-gate maxfiles = ulimit(UL_GDESLIM, 0); 21040Sstevel@tonic-gate 21050Sstevel@tonic-gate for (i = 0; i < maxfiles; i++) 21060Sstevel@tonic-gate (void) fcntl(i, F_SETFD, 21070Sstevel@tonic-gate FD_CLOEXEC); 21080Sstevel@tonic-gate (void) execle(SH, "INITSH", "-c", 21090Sstevel@tonic-gate cmd.c_command, 21100Sstevel@tonic-gate (char *)0, glob_envp); 21110Sstevel@tonic-gate console(B_TRUE, 21120Sstevel@tonic-gate "Command\n\"%s\"\n failed to execute. errno = %d (exec of shell failed)\n", 2113*8735SEnrico.Perla@Sun.COM cmd.c_command, errno); 21140Sstevel@tonic-gate exit(1); 21150Sstevel@tonic-gate } else while (waitproc(process) == FAILURE); 21160Sstevel@tonic-gate process->p_flags = 0; 21170Sstevel@tonic-gate st_write(); 21180Sstevel@tonic-gate } 21190Sstevel@tonic-gate } 21200Sstevel@tonic-gate } 21210Sstevel@tonic-gate 21220Sstevel@tonic-gate /* Restore the path. */ 21230Sstevel@tonic-gate free(glob_envp[0]); 21240Sstevel@tonic-gate glob_envp[0] = old_path; 21250Sstevel@tonic-gate 21260Sstevel@tonic-gate /* 21270Sstevel@tonic-gate * This will enable st_write() to complain about init_state_file. 21280Sstevel@tonic-gate */ 21290Sstevel@tonic-gate booting = 0; 21300Sstevel@tonic-gate 21310Sstevel@tonic-gate /* 21320Sstevel@tonic-gate * If the /etc/ioctl.syscon didn't exist or had invalid contents write 21330Sstevel@tonic-gate * out a correct version. 21340Sstevel@tonic-gate */ 21350Sstevel@tonic-gate if (write_ioctl) 21360Sstevel@tonic-gate write_ioctl_syscon(); 21370Sstevel@tonic-gate 21380Sstevel@tonic-gate /* 21390Sstevel@tonic-gate * Start svc.startd(1M), which does most of the work. 21400Sstevel@tonic-gate */ 21410Sstevel@tonic-gate if (startd_cline[0] != '\0' && startd_tmpl >= 0) { 21420Sstevel@tonic-gate /* Start svc.startd. */ 21430Sstevel@tonic-gate if (startd_run(startd_cline, startd_tmpl, 0) == -1) 21440Sstevel@tonic-gate cur_state = SINGLE_USER; 21450Sstevel@tonic-gate } else { 21460Sstevel@tonic-gate console(B_TRUE, "Absent svc.startd entry or bad " 21470Sstevel@tonic-gate "contract template. Not starting svc.startd.\n"); 21480Sstevel@tonic-gate enter_maintenance(); 21490Sstevel@tonic-gate } 21500Sstevel@tonic-gate } 21510Sstevel@tonic-gate 21520Sstevel@tonic-gate /* 21530Sstevel@tonic-gate * init_signals(): Initialize all signals to either be caught or ignored. 21540Sstevel@tonic-gate */ 21550Sstevel@tonic-gate void 21560Sstevel@tonic-gate init_signals(void) 21570Sstevel@tonic-gate { 21580Sstevel@tonic-gate struct sigaction act; 21590Sstevel@tonic-gate int i; 21600Sstevel@tonic-gate 21610Sstevel@tonic-gate /* 21620Sstevel@tonic-gate * Start by ignoring all signals, then selectively re-enable some. 21630Sstevel@tonic-gate * The SIG_IGN disposition will only affect asynchronous signals: 21640Sstevel@tonic-gate * any signal that we trigger synchronously that doesn't end up 21650Sstevel@tonic-gate * being handled by siglvl() will be forcibly delivered by the kernel. 21660Sstevel@tonic-gate */ 21670Sstevel@tonic-gate for (i = SIGHUP; i <= SIGRTMAX; i++) 21680Sstevel@tonic-gate (void) sigset(i, SIG_IGN); 21690Sstevel@tonic-gate 21700Sstevel@tonic-gate /* 21710Sstevel@tonic-gate * Handle all level-changing signals using siglvl() and set sa_mask so 21720Sstevel@tonic-gate * that all level-changing signals are blocked while in siglvl(). 21730Sstevel@tonic-gate */ 21740Sstevel@tonic-gate act.sa_handler = siglvl; 21750Sstevel@tonic-gate act.sa_flags = SA_SIGINFO; 21760Sstevel@tonic-gate (void) sigemptyset(&act.sa_mask); 21770Sstevel@tonic-gate 21780Sstevel@tonic-gate (void) sigaddset(&act.sa_mask, LVLQ); 21790Sstevel@tonic-gate (void) sigaddset(&act.sa_mask, LVL0); 21800Sstevel@tonic-gate (void) sigaddset(&act.sa_mask, LVL1); 21810Sstevel@tonic-gate (void) sigaddset(&act.sa_mask, LVL2); 21820Sstevel@tonic-gate (void) sigaddset(&act.sa_mask, LVL3); 21830Sstevel@tonic-gate (void) sigaddset(&act.sa_mask, LVL4); 21840Sstevel@tonic-gate (void) sigaddset(&act.sa_mask, LVL5); 21850Sstevel@tonic-gate (void) sigaddset(&act.sa_mask, LVL6); 21860Sstevel@tonic-gate (void) sigaddset(&act.sa_mask, SINGLE_USER); 21870Sstevel@tonic-gate (void) sigaddset(&act.sa_mask, LVLa); 21880Sstevel@tonic-gate (void) sigaddset(&act.sa_mask, LVLb); 21890Sstevel@tonic-gate (void) sigaddset(&act.sa_mask, LVLc); 21900Sstevel@tonic-gate 21910Sstevel@tonic-gate (void) sigaction(LVLQ, &act, NULL); 21920Sstevel@tonic-gate (void) sigaction(LVL0, &act, NULL); 21930Sstevel@tonic-gate (void) sigaction(LVL1, &act, NULL); 21940Sstevel@tonic-gate (void) sigaction(LVL2, &act, NULL); 21950Sstevel@tonic-gate (void) sigaction(LVL3, &act, NULL); 21960Sstevel@tonic-gate (void) sigaction(LVL4, &act, NULL); 21970Sstevel@tonic-gate (void) sigaction(LVL5, &act, NULL); 21980Sstevel@tonic-gate (void) sigaction(LVL6, &act, NULL); 21990Sstevel@tonic-gate (void) sigaction(SINGLE_USER, &act, NULL); 22000Sstevel@tonic-gate (void) sigaction(LVLa, &act, NULL); 22010Sstevel@tonic-gate (void) sigaction(LVLb, &act, NULL); 22020Sstevel@tonic-gate (void) sigaction(LVLc, &act, NULL); 22030Sstevel@tonic-gate 22040Sstevel@tonic-gate (void) sigset(SIGALRM, alarmclk); 22050Sstevel@tonic-gate alarmclk(); 22060Sstevel@tonic-gate 22070Sstevel@tonic-gate (void) sigset(SIGCLD, childeath); 22080Sstevel@tonic-gate (void) sigset(SIGPWR, powerfail); 22090Sstevel@tonic-gate } 22100Sstevel@tonic-gate 22110Sstevel@tonic-gate /* 22120Sstevel@tonic-gate * Set up pipe for "godchildren". If the file exists and is a pipe just open 22130Sstevel@tonic-gate * it. Else, if the file system is r/w create it. Otherwise, defer its 22145017Seschrock * creation and open until after /var/run has been mounted. This function is 22155017Seschrock * only called on startup and when explicitly requested via LVLQ. 22160Sstevel@tonic-gate */ 22170Sstevel@tonic-gate void 22180Sstevel@tonic-gate setup_pipe() 22190Sstevel@tonic-gate { 22200Sstevel@tonic-gate struct stat stat_buf; 22210Sstevel@tonic-gate struct statvfs statvfs_buf; 22222691Snakanon struct sigaction act; 22230Sstevel@tonic-gate 22245017Seschrock /* 22255017Seschrock * Always close the previous pipe descriptor as the mounted filesystems 22265017Seschrock * may have changed. 22275017Seschrock */ 22285017Seschrock if (Pfd >= 0) 22295017Seschrock (void) close(Pfd); 22305017Seschrock 22310Sstevel@tonic-gate if ((stat(INITPIPE, &stat_buf) == 0) && 22320Sstevel@tonic-gate ((stat_buf.st_mode & (S_IFMT|S_IRUSR)) == (S_IFIFO|S_IRUSR))) 22330Sstevel@tonic-gate Pfd = open(INITPIPE, O_RDWR | O_NDELAY); 22340Sstevel@tonic-gate else 22350Sstevel@tonic-gate if ((statvfs(INITPIPE_DIR, &statvfs_buf) == 0) && 22360Sstevel@tonic-gate ((statvfs_buf.f_flag & ST_RDONLY) == 0)) { 22370Sstevel@tonic-gate (void) unlink(INITPIPE); 22380Sstevel@tonic-gate (void) mknod(INITPIPE, S_IFIFO | 0600, 0); 22390Sstevel@tonic-gate Pfd = open(INITPIPE, O_RDWR | O_NDELAY); 22400Sstevel@tonic-gate } 22410Sstevel@tonic-gate 22420Sstevel@tonic-gate if (Pfd >= 0) { 22430Sstevel@tonic-gate (void) ioctl(Pfd, I_SETSIG, S_INPUT); 22440Sstevel@tonic-gate /* 22450Sstevel@tonic-gate * Read pipe in message discard mode. 22460Sstevel@tonic-gate */ 22470Sstevel@tonic-gate (void) ioctl(Pfd, I_SRDOPT, RMSGD); 22482691Snakanon 22492691Snakanon act.sa_handler = sigpoll; 22502691Snakanon act.sa_flags = 0; 22512691Snakanon (void) sigemptyset(&act.sa_mask); 22522691Snakanon (void) sigaddset(&act.sa_mask, SIGCLD); 22532691Snakanon (void) sigaction(SIGPOLL, &act, NULL); 22540Sstevel@tonic-gate } 22550Sstevel@tonic-gate } 22560Sstevel@tonic-gate 22570Sstevel@tonic-gate /* 22580Sstevel@tonic-gate * siglvl - handle an asynchronous signal from init(1M) telling us that we 22590Sstevel@tonic-gate * should change the current run level. We set new_state accordingly. 22600Sstevel@tonic-gate */ 22610Sstevel@tonic-gate void 22620Sstevel@tonic-gate siglvl(int sig, siginfo_t *sip, ucontext_t *ucp) 22630Sstevel@tonic-gate { 22640Sstevel@tonic-gate struct PROC_TABLE *process; 22650Sstevel@tonic-gate struct sigaction act; 22660Sstevel@tonic-gate 22670Sstevel@tonic-gate /* 22680Sstevel@tonic-gate * If the signal was from the kernel (rather than init(1M)) then init 22690Sstevel@tonic-gate * itself tripped the signal. That is, we might have a bug and tripped 22700Sstevel@tonic-gate * a real SIGSEGV instead of receiving it as an alias for SIGLVLa. In 22710Sstevel@tonic-gate * such a case we reset the disposition to SIG_DFL, block all signals 22720Sstevel@tonic-gate * in uc_mask but the current one, and return to the interrupted ucp 22730Sstevel@tonic-gate * to effect an appropriate death. The kernel will then restart us. 22740Sstevel@tonic-gate * 22750Sstevel@tonic-gate * The one exception to SI_FROMKERNEL() is SIGFPE (a.k.a. LVL6), which 22760Sstevel@tonic-gate * the kernel can send us when it wants to effect an orderly reboot. 22770Sstevel@tonic-gate * For this case we must also verify si_code is zero, rather than a 22780Sstevel@tonic-gate * code such as FPE_INTDIV which a bug might have triggered. 22790Sstevel@tonic-gate */ 22800Sstevel@tonic-gate if (sip != NULL && SI_FROMKERNEL(sip) && 22810Sstevel@tonic-gate (sig != SIGFPE || sip->si_code == 0)) { 22820Sstevel@tonic-gate 22830Sstevel@tonic-gate (void) sigemptyset(&act.sa_mask); 22840Sstevel@tonic-gate act.sa_handler = SIG_DFL; 22850Sstevel@tonic-gate act.sa_flags = 0; 22860Sstevel@tonic-gate (void) sigaction(sig, &act, NULL); 22870Sstevel@tonic-gate 22880Sstevel@tonic-gate (void) sigfillset(&ucp->uc_sigmask); 22890Sstevel@tonic-gate (void) sigdelset(&ucp->uc_sigmask, sig); 22900Sstevel@tonic-gate ucp->uc_flags |= UC_SIGMASK; 22910Sstevel@tonic-gate 22920Sstevel@tonic-gate (void) setcontext(ucp); 22930Sstevel@tonic-gate } 22940Sstevel@tonic-gate 22950Sstevel@tonic-gate /* 22960Sstevel@tonic-gate * If the signal received is a LVLQ signal, do not really 22970Sstevel@tonic-gate * change levels, just restate the current level. If the 22980Sstevel@tonic-gate * signal is not a LVLQ, set the new level to the signal 22990Sstevel@tonic-gate * received. 23000Sstevel@tonic-gate */ 23015017Seschrock if (sig == LVLQ) { 23020Sstevel@tonic-gate new_state = cur_state; 23035017Seschrock lvlq_received = B_TRUE; 23045017Seschrock } else { 23050Sstevel@tonic-gate new_state = sig; 23065017Seschrock } 23070Sstevel@tonic-gate 23080Sstevel@tonic-gate /* 23090Sstevel@tonic-gate * Clear all times and repeat counts in the process table 23100Sstevel@tonic-gate * since either the level is changing or the user has editted 23110Sstevel@tonic-gate * the inittab file and wants us to look at it again. 23120Sstevel@tonic-gate * If the user has fixed a typo, we don't want residual timing 23130Sstevel@tonic-gate * data preventing the fixed command line from executing. 23140Sstevel@tonic-gate */ 23150Sstevel@tonic-gate for (process = proc_table; 2316*8735SEnrico.Perla@Sun.COM (process < proc_table + num_proc); process++) { 23170Sstevel@tonic-gate process->p_time = 0L; 23180Sstevel@tonic-gate process->p_count = 0; 23190Sstevel@tonic-gate } 23200Sstevel@tonic-gate 23210Sstevel@tonic-gate /* 23220Sstevel@tonic-gate * Set the flag to indicate that a "user signal" was received. 23230Sstevel@tonic-gate */ 23240Sstevel@tonic-gate wakeup.w_flags.w_usersignal = 1; 23250Sstevel@tonic-gate } 23260Sstevel@tonic-gate 23270Sstevel@tonic-gate 23280Sstevel@tonic-gate /* 23290Sstevel@tonic-gate * alarmclk 23300Sstevel@tonic-gate */ 23310Sstevel@tonic-gate static void 23320Sstevel@tonic-gate alarmclk() 23330Sstevel@tonic-gate { 23340Sstevel@tonic-gate time_up = TRUE; 23350Sstevel@tonic-gate } 23360Sstevel@tonic-gate 23370Sstevel@tonic-gate /* 23380Sstevel@tonic-gate * childeath_single(): 23390Sstevel@tonic-gate * 23400Sstevel@tonic-gate * This used to be the SIGCLD handler and it was set with signal() 23410Sstevel@tonic-gate * (as opposed to sigset()). When a child exited we'd come to the 23420Sstevel@tonic-gate * handler, wait for the child, and reenable the handler with 23430Sstevel@tonic-gate * signal() just before returning. The implementation of signal() 23440Sstevel@tonic-gate * checks with waitid() for waitable children and sends a SIGCLD 23450Sstevel@tonic-gate * if there are some. If children are exiting faster than the 23460Sstevel@tonic-gate * handler can run we keep sending signals and the handler never 23470Sstevel@tonic-gate * gets to return and eventually the stack runs out and init dies. 23480Sstevel@tonic-gate * To prevent that we set the handler with sigset() so the handler 23490Sstevel@tonic-gate * doesn't need to be reset, and in childeath() (see below) we 23500Sstevel@tonic-gate * call childeath_single() as long as there are children to be 23510Sstevel@tonic-gate * waited for. If a child exits while init is in the handler a 23520Sstevel@tonic-gate * SIGCLD will be pending and delivered on return from the handler. 23530Sstevel@tonic-gate * If the child was already waited for the handler will have nothing 23540Sstevel@tonic-gate * to do and return, otherwise the child will be waited for. 23550Sstevel@tonic-gate */ 23560Sstevel@tonic-gate static void 23570Sstevel@tonic-gate childeath_single() 23580Sstevel@tonic-gate { 23590Sstevel@tonic-gate struct PROC_TABLE *process; 23600Sstevel@tonic-gate struct pidlist *pp; 23610Sstevel@tonic-gate pid_t pid; 23620Sstevel@tonic-gate int status; 23630Sstevel@tonic-gate 23640Sstevel@tonic-gate /* 23650Sstevel@tonic-gate * Perform wait to get the process id of the child that died and 23660Sstevel@tonic-gate * then scan the process table to see if we are interested in 23670Sstevel@tonic-gate * this process. NOTE: if a super-user sends the SIGCLD signal 23680Sstevel@tonic-gate * to init, the following wait will not immediately return and 23690Sstevel@tonic-gate * init will be inoperative until one of its child really does die. 23700Sstevel@tonic-gate */ 23710Sstevel@tonic-gate pid = wait(&status); 23720Sstevel@tonic-gate 23730Sstevel@tonic-gate for (process = proc_table; 2374*8735SEnrico.Perla@Sun.COM (process < proc_table + num_proc); process++) { 23750Sstevel@tonic-gate if ((process->p_flags & (LIVING|OCCUPIED)) == 23760Sstevel@tonic-gate (LIVING|OCCUPIED) && process->p_pid == pid) { 23770Sstevel@tonic-gate 23780Sstevel@tonic-gate /* 23790Sstevel@tonic-gate * Mark this process as having died and store the exit 23800Sstevel@tonic-gate * status. Also set the wakeup flag for a dead child 23810Sstevel@tonic-gate * and break out of the loop. 23820Sstevel@tonic-gate */ 23830Sstevel@tonic-gate process->p_flags &= ~LIVING; 23840Sstevel@tonic-gate process->p_exit = (short)status; 23850Sstevel@tonic-gate wakeup.w_flags.w_childdeath = 1; 23860Sstevel@tonic-gate 23870Sstevel@tonic-gate return; 23880Sstevel@tonic-gate } 23890Sstevel@tonic-gate } 23900Sstevel@tonic-gate 23910Sstevel@tonic-gate /* 23920Sstevel@tonic-gate * No process was found above, look through auxiliary list. 23930Sstevel@tonic-gate */ 23940Sstevel@tonic-gate (void) sighold(SIGPOLL); 23950Sstevel@tonic-gate pp = Plhead; 23960Sstevel@tonic-gate while (pp) { 23970Sstevel@tonic-gate if (pid > pp->pl_pid) { 23980Sstevel@tonic-gate /* 23990Sstevel@tonic-gate * Keep on looking. 24000Sstevel@tonic-gate */ 24010Sstevel@tonic-gate pp = pp->pl_next; 24020Sstevel@tonic-gate continue; 24030Sstevel@tonic-gate } else if (pid < pp->pl_pid) { 24040Sstevel@tonic-gate /* 24050Sstevel@tonic-gate * Not in the list. 24060Sstevel@tonic-gate */ 24070Sstevel@tonic-gate break; 24080Sstevel@tonic-gate } else { 24090Sstevel@tonic-gate /* 24100Sstevel@tonic-gate * This is a dead "godchild". 24110Sstevel@tonic-gate */ 24120Sstevel@tonic-gate pp->pl_dflag = 1; 24130Sstevel@tonic-gate pp->pl_exit = (short)status; 24140Sstevel@tonic-gate wakeup.w_flags.w_childdeath = 1; 24150Sstevel@tonic-gate Gchild = 1; /* Notice to call cleanaux(). */ 24160Sstevel@tonic-gate break; 24170Sstevel@tonic-gate } 24180Sstevel@tonic-gate } 24190Sstevel@tonic-gate 24200Sstevel@tonic-gate (void) sigrelse(SIGPOLL); 24210Sstevel@tonic-gate } 24220Sstevel@tonic-gate 24230Sstevel@tonic-gate /* ARGSUSED */ 24240Sstevel@tonic-gate static void 24250Sstevel@tonic-gate childeath(int signo) 24260Sstevel@tonic-gate { 24270Sstevel@tonic-gate siginfo_t info; 24280Sstevel@tonic-gate 24290Sstevel@tonic-gate while ((waitid(P_ALL, (id_t)0, &info, WEXITED|WNOHANG|WNOWAIT) == 0) && 24300Sstevel@tonic-gate info.si_pid != 0) 24310Sstevel@tonic-gate childeath_single(); 24320Sstevel@tonic-gate } 24330Sstevel@tonic-gate 24340Sstevel@tonic-gate static void 24350Sstevel@tonic-gate powerfail() 24360Sstevel@tonic-gate { 24370Sstevel@tonic-gate (void) nice(-19); 24380Sstevel@tonic-gate wakeup.w_flags.w_powerhit = 1; 24390Sstevel@tonic-gate } 24400Sstevel@tonic-gate 24410Sstevel@tonic-gate /* 24420Sstevel@tonic-gate * efork() forks a child and the parent inserts the process in its table 24430Sstevel@tonic-gate * of processes that are directly a result of forks that it has performed. 24440Sstevel@tonic-gate * The child just changes the "global" with the process id for this process 24450Sstevel@tonic-gate * to it's new value. 24460Sstevel@tonic-gate * If efork() is called with a pointer into the proc_table it uses that slot, 24470Sstevel@tonic-gate * otherwise it searches for a free slot. Regardless of how it was called, 24480Sstevel@tonic-gate * it returns the pointer to the proc_table entry 24490Sstevel@tonic-gate * 24500Sstevel@tonic-gate * The SIGCLD handler is set to default (SIG_DFL) before calling efork(). 24510Sstevel@tonic-gate * This relies on the somewhat obscure SVR2 SIGCLD/SIG_DFL semantic 24520Sstevel@tonic-gate * implied by the use of signal(3c). While the meaning of SIG_DFL for 24530Sstevel@tonic-gate * SIGCLD is nominally to ignore the signal, once the signal disposition 24540Sstevel@tonic-gate * is set to childeath(), the kernel will post a SIGCLD if a child 24550Sstevel@tonic-gate * exited during the period the disposition was SIG_DFL. It acts more 24560Sstevel@tonic-gate * like a signal block. 24570Sstevel@tonic-gate * 24580Sstevel@tonic-gate * Ideally, this should be rewritten to use modern signal semantics. 24590Sstevel@tonic-gate */ 24600Sstevel@tonic-gate static struct PROC_TABLE * 24610Sstevel@tonic-gate efork(int action, struct PROC_TABLE *process, int modes) 24620Sstevel@tonic-gate { 24630Sstevel@tonic-gate pid_t childpid; 24640Sstevel@tonic-gate struct PROC_TABLE *proc; 24650Sstevel@tonic-gate int i; 24660Sstevel@tonic-gate void (*oldroutine)(); 24670Sstevel@tonic-gate /* 24680Sstevel@tonic-gate * Freshen up the proc_table, removing any entries for dead processes 24690Sstevel@tonic-gate * that don't have NOCLEANUP set. Perform the necessary accounting. 24700Sstevel@tonic-gate */ 24710Sstevel@tonic-gate for (proc = proc_table; (proc < proc_table + num_proc); proc++) { 24720Sstevel@tonic-gate if ((proc->p_flags & (OCCUPIED|LIVING|NOCLEANUP)) == 24730Sstevel@tonic-gate (OCCUPIED)) { 24740Sstevel@tonic-gate /* 24750Sstevel@tonic-gate * Is this a named process? 24760Sstevel@tonic-gate * If so, do the necessary bookkeeping. 24770Sstevel@tonic-gate */ 24780Sstevel@tonic-gate if (proc->p_flags & NAMED) 24790Sstevel@tonic-gate (void) account(DEAD_PROCESS, proc, NULL); 24800Sstevel@tonic-gate 24810Sstevel@tonic-gate /* 24820Sstevel@tonic-gate * Free this entry for new usage. 24830Sstevel@tonic-gate */ 24840Sstevel@tonic-gate proc->p_flags = 0; 24850Sstevel@tonic-gate } 24860Sstevel@tonic-gate } 24870Sstevel@tonic-gate 24880Sstevel@tonic-gate while ((childpid = fork()) == FAILURE) { 24890Sstevel@tonic-gate /* 24900Sstevel@tonic-gate * Shorten the alarm timer in case someone else's child dies 24910Sstevel@tonic-gate * and free up a slot in the process table. 24920Sstevel@tonic-gate */ 24930Sstevel@tonic-gate setimer(5); 24940Sstevel@tonic-gate 24950Sstevel@tonic-gate /* 24960Sstevel@tonic-gate * Wait for some children to die. Since efork() is normally 24970Sstevel@tonic-gate * called with SIGCLD in the default state, reset it to catch 24980Sstevel@tonic-gate * so that child death signals can come in. 24990Sstevel@tonic-gate */ 25000Sstevel@tonic-gate oldroutine = sigset(SIGCLD, childeath); 25010Sstevel@tonic-gate (void) pause(); 25020Sstevel@tonic-gate (void) sigset(SIGCLD, oldroutine); 25030Sstevel@tonic-gate setimer(0); 25040Sstevel@tonic-gate } 25050Sstevel@tonic-gate 25060Sstevel@tonic-gate if (childpid != 0) { 25070Sstevel@tonic-gate 25080Sstevel@tonic-gate if (process == NULLPROC) { 25090Sstevel@tonic-gate /* 25100Sstevel@tonic-gate * No proc table pointer specified so search 25110Sstevel@tonic-gate * for a free slot. 25120Sstevel@tonic-gate */ 25130Sstevel@tonic-gate for (process = proc_table; process->p_flags != 0 && 2514*8735SEnrico.Perla@Sun.COM (process < proc_table + num_proc); process++) 25150Sstevel@tonic-gate ; 25160Sstevel@tonic-gate 25170Sstevel@tonic-gate if (process == (proc_table + num_proc)) { 25180Sstevel@tonic-gate int old_proc_table_size = num_proc; 25190Sstevel@tonic-gate 25200Sstevel@tonic-gate /* Increase the process table size */ 25210Sstevel@tonic-gate increase_proc_table_size(); 25220Sstevel@tonic-gate if (old_proc_table_size == num_proc) { 25230Sstevel@tonic-gate /* didn't grow: memory failure */ 25240Sstevel@tonic-gate return (NO_ROOM); 25250Sstevel@tonic-gate } else { 25260Sstevel@tonic-gate process = 25270Sstevel@tonic-gate proc_table + old_proc_table_size; 25280Sstevel@tonic-gate } 25290Sstevel@tonic-gate } 25300Sstevel@tonic-gate 25310Sstevel@tonic-gate process->p_time = 0L; 25320Sstevel@tonic-gate process->p_count = 0; 25330Sstevel@tonic-gate } 25340Sstevel@tonic-gate process->p_id[0] = '\0'; 25350Sstevel@tonic-gate process->p_id[1] = '\0'; 25360Sstevel@tonic-gate process->p_id[2] = '\0'; 25370Sstevel@tonic-gate process->p_id[3] = '\0'; 25380Sstevel@tonic-gate process->p_pid = childpid; 25390Sstevel@tonic-gate process->p_flags = (LIVING | OCCUPIED | modes); 25400Sstevel@tonic-gate process->p_exit = 0; 25410Sstevel@tonic-gate 25420Sstevel@tonic-gate st_write(); 25430Sstevel@tonic-gate } else { 25440Sstevel@tonic-gate if ((action & (M_WAIT | M_BOOTWAIT)) == 0) 25450Sstevel@tonic-gate (void) setpgrp(); 25460Sstevel@tonic-gate 25470Sstevel@tonic-gate process = NULLPROC; 25480Sstevel@tonic-gate 25490Sstevel@tonic-gate /* 25500Sstevel@tonic-gate * Reset all signals to the system defaults. 25510Sstevel@tonic-gate */ 25520Sstevel@tonic-gate for (i = SIGHUP; i <= SIGRTMAX; i++) 25530Sstevel@tonic-gate (void) sigset(i, SIG_DFL); 25540Sstevel@tonic-gate 25550Sstevel@tonic-gate /* 25560Sstevel@tonic-gate * POSIX B.2.2.2 advises that init should set SIGTTOU, 25570Sstevel@tonic-gate * SIGTTIN, and SIGTSTP to SIG_IGN. 25580Sstevel@tonic-gate * 25590Sstevel@tonic-gate * Make sure that SIGXCPU and SIGXFSZ also remain ignored, 25600Sstevel@tonic-gate * for backward compatibility. 25610Sstevel@tonic-gate */ 25620Sstevel@tonic-gate (void) sigset(SIGTTIN, SIG_IGN); 25630Sstevel@tonic-gate (void) sigset(SIGTTOU, SIG_IGN); 25640Sstevel@tonic-gate (void) sigset(SIGTSTP, SIG_IGN); 25650Sstevel@tonic-gate (void) sigset(SIGXCPU, SIG_IGN); 25660Sstevel@tonic-gate (void) sigset(SIGXFSZ, SIG_IGN); 25670Sstevel@tonic-gate } 25680Sstevel@tonic-gate return (process); 25690Sstevel@tonic-gate } 25700Sstevel@tonic-gate 25710Sstevel@tonic-gate 25720Sstevel@tonic-gate /* 25730Sstevel@tonic-gate * waitproc() waits for a specified process to die. For this function to 25740Sstevel@tonic-gate * work, the specified process must already in the proc_table. waitproc() 25750Sstevel@tonic-gate * returns the exit status of the specified process when it dies. 25760Sstevel@tonic-gate */ 25770Sstevel@tonic-gate static long 25780Sstevel@tonic-gate waitproc(struct PROC_TABLE *process) 25790Sstevel@tonic-gate { 25800Sstevel@tonic-gate int answer; 25810Sstevel@tonic-gate sigset_t oldmask, newmask, zeromask; 25820Sstevel@tonic-gate 25830Sstevel@tonic-gate (void) sigemptyset(&zeromask); 25840Sstevel@tonic-gate (void) sigemptyset(&newmask); 25850Sstevel@tonic-gate 25860Sstevel@tonic-gate (void) sigaddset(&newmask, SIGCLD); 25870Sstevel@tonic-gate 25880Sstevel@tonic-gate /* Block SIGCLD and save the current signal mask */ 25890Sstevel@tonic-gate if (sigprocmask(SIG_BLOCK, &newmask, &oldmask) < 0) 25900Sstevel@tonic-gate perror("SIG_BLOCK error"); 25910Sstevel@tonic-gate 25920Sstevel@tonic-gate /* 25930Sstevel@tonic-gate * Wait around until the process dies. 25940Sstevel@tonic-gate */ 25950Sstevel@tonic-gate if (process->p_flags & LIVING) 25960Sstevel@tonic-gate (void) sigsuspend(&zeromask); 25970Sstevel@tonic-gate 25980Sstevel@tonic-gate /* Reset signal mask to unblock SIGCLD */ 25990Sstevel@tonic-gate if (sigprocmask(SIG_SETMASK, &oldmask, NULL) < 0) 26000Sstevel@tonic-gate perror("SIG_SETMASK error"); 26010Sstevel@tonic-gate 26020Sstevel@tonic-gate if (process->p_flags & LIVING) 26030Sstevel@tonic-gate return (FAILURE); 26040Sstevel@tonic-gate 26050Sstevel@tonic-gate /* 26060Sstevel@tonic-gate * Make sure to only return 16 bits so that answer will always 26070Sstevel@tonic-gate * be positive whenever the process of interest really died. 26080Sstevel@tonic-gate */ 26090Sstevel@tonic-gate answer = (process->p_exit & 0xffff); 26100Sstevel@tonic-gate 26110Sstevel@tonic-gate /* 26120Sstevel@tonic-gate * Free the slot in the proc_table. 26130Sstevel@tonic-gate */ 26140Sstevel@tonic-gate process->p_flags = 0; 26150Sstevel@tonic-gate return (answer); 26160Sstevel@tonic-gate } 26170Sstevel@tonic-gate 26180Sstevel@tonic-gate /* 26190Sstevel@tonic-gate * notify_pam_dead(): calls into the PAM framework to close the given session. 26200Sstevel@tonic-gate */ 26210Sstevel@tonic-gate static void 26220Sstevel@tonic-gate notify_pam_dead(struct utmpx *up) 26230Sstevel@tonic-gate { 26240Sstevel@tonic-gate pam_handle_t *pamh; 26250Sstevel@tonic-gate char user[sizeof (up->ut_user) + 1]; 26260Sstevel@tonic-gate char ttyn[sizeof (up->ut_line) + 1]; 26270Sstevel@tonic-gate char host[sizeof (up->ut_host) + 1]; 26280Sstevel@tonic-gate 26290Sstevel@tonic-gate /* 26300Sstevel@tonic-gate * PAM does not take care of updating utmpx/wtmpx. 26310Sstevel@tonic-gate */ 26320Sstevel@tonic-gate (void) snprintf(user, sizeof (user), "%s", up->ut_user); 26330Sstevel@tonic-gate (void) snprintf(ttyn, sizeof (ttyn), "%s", up->ut_line); 26340Sstevel@tonic-gate (void) snprintf(host, sizeof (host), "%s", up->ut_host); 26350Sstevel@tonic-gate 26360Sstevel@tonic-gate if (pam_start("init", user, NULL, &pamh) == PAM_SUCCESS) { 26370Sstevel@tonic-gate (void) pam_set_item(pamh, PAM_TTY, ttyn); 26380Sstevel@tonic-gate (void) pam_set_item(pamh, PAM_RHOST, host); 26390Sstevel@tonic-gate (void) pam_close_session(pamh, 0); 26400Sstevel@tonic-gate (void) pam_end(pamh, PAM_SUCCESS); 26410Sstevel@tonic-gate } 26420Sstevel@tonic-gate } 26430Sstevel@tonic-gate 26440Sstevel@tonic-gate /* 26450Sstevel@tonic-gate * Check you can access utmpx (As / may be read-only and 26460Sstevel@tonic-gate * /var may not be mounted yet). 26470Sstevel@tonic-gate */ 26480Sstevel@tonic-gate static int 26490Sstevel@tonic-gate access_utmpx(void) 26500Sstevel@tonic-gate { 26510Sstevel@tonic-gate do { 26520Sstevel@tonic-gate utmpx_ok = (access(UTMPX, R_OK|W_OK) == 0); 26530Sstevel@tonic-gate } while (!utmpx_ok && errno == EINTR); 26540Sstevel@tonic-gate 26550Sstevel@tonic-gate return (utmpx_ok); 26560Sstevel@tonic-gate } 26570Sstevel@tonic-gate 26580Sstevel@tonic-gate /* 26590Sstevel@tonic-gate * account() updates entries in utmpx and appends new entries to the end of 26600Sstevel@tonic-gate * wtmpx (assuming they exist). The program argument indicates the name of 26610Sstevel@tonic-gate * program if INIT_PROCESS, otherwise should be NULL. 26620Sstevel@tonic-gate * 26630Sstevel@tonic-gate * account() only blocks for INIT_PROCESS requests. 26640Sstevel@tonic-gate * 26650Sstevel@tonic-gate * Returns non-zero if write failed. 26660Sstevel@tonic-gate */ 26670Sstevel@tonic-gate static int 26680Sstevel@tonic-gate account(short state, struct PROC_TABLE *process, char *program) 26690Sstevel@tonic-gate { 26700Sstevel@tonic-gate struct utmpx utmpbuf, *u, *oldu; 26710Sstevel@tonic-gate int tmplen; 26720Sstevel@tonic-gate char fail_buf[UT_LINE_SZ]; 26730Sstevel@tonic-gate sigset_t block, unblock; 26740Sstevel@tonic-gate 26750Sstevel@tonic-gate if (!utmpx_ok && !access_utmpx()) { 26760Sstevel@tonic-gate return (-1); 26770Sstevel@tonic-gate } 26780Sstevel@tonic-gate 26790Sstevel@tonic-gate /* 26800Sstevel@tonic-gate * Set up the prototype for the utmp structure we want to write. 26810Sstevel@tonic-gate */ 26820Sstevel@tonic-gate u = &utmpbuf; 26830Sstevel@tonic-gate (void) memset(u, 0, sizeof (struct utmpx)); 26840Sstevel@tonic-gate 26850Sstevel@tonic-gate /* 26860Sstevel@tonic-gate * Fill in the various fields of the utmp structure. 26870Sstevel@tonic-gate */ 26880Sstevel@tonic-gate u->ut_id[0] = process->p_id[0]; 26890Sstevel@tonic-gate u->ut_id[1] = process->p_id[1]; 26900Sstevel@tonic-gate u->ut_id[2] = process->p_id[2]; 26910Sstevel@tonic-gate u->ut_id[3] = process->p_id[3]; 26920Sstevel@tonic-gate u->ut_pid = process->p_pid; 26930Sstevel@tonic-gate 26940Sstevel@tonic-gate /* 26950Sstevel@tonic-gate * Fill the "ut_exit" structure. 26960Sstevel@tonic-gate */ 26970Sstevel@tonic-gate u->ut_exit.e_termination = WTERMSIG(process->p_exit); 26980Sstevel@tonic-gate u->ut_exit.e_exit = WEXITSTATUS(process->p_exit); 26990Sstevel@tonic-gate u->ut_type = state; 27000Sstevel@tonic-gate 27010Sstevel@tonic-gate (void) time(&u->ut_tv.tv_sec); 27020Sstevel@tonic-gate 27030Sstevel@tonic-gate /* 27040Sstevel@tonic-gate * Block signals for utmp update. 27050Sstevel@tonic-gate */ 27060Sstevel@tonic-gate (void) sigfillset(&block); 27070Sstevel@tonic-gate (void) sigprocmask(SIG_BLOCK, &block, &unblock); 27080Sstevel@tonic-gate 27090Sstevel@tonic-gate /* 27100Sstevel@tonic-gate * See if there already is such an entry in the "utmpx" file. 27110Sstevel@tonic-gate */ 27120Sstevel@tonic-gate setutxent(); /* Start at beginning of utmpx file. */ 27130Sstevel@tonic-gate 27140Sstevel@tonic-gate if ((oldu = getutxid(u)) != NULL) { 27150Sstevel@tonic-gate /* 27160Sstevel@tonic-gate * Copy in the old "user", "line" and "host" fields 27170Sstevel@tonic-gate * to our new structure. 27180Sstevel@tonic-gate */ 27190Sstevel@tonic-gate bcopy(oldu->ut_user, u->ut_user, sizeof (u->ut_user)); 27200Sstevel@tonic-gate bcopy(oldu->ut_line, u->ut_line, sizeof (u->ut_line)); 27210Sstevel@tonic-gate bcopy(oldu->ut_host, u->ut_host, sizeof (u->ut_host)); 27220Sstevel@tonic-gate u->ut_syslen = (tmplen = strlen(u->ut_host)) ? 2723*8735SEnrico.Perla@Sun.COM min(tmplen + 1, sizeof (u->ut_host)) : 0; 27240Sstevel@tonic-gate 27250Sstevel@tonic-gate if (oldu->ut_type == USER_PROCESS && state == DEAD_PROCESS) { 27260Sstevel@tonic-gate notify_pam_dead(oldu); 27270Sstevel@tonic-gate } 27280Sstevel@tonic-gate } 27290Sstevel@tonic-gate 27300Sstevel@tonic-gate /* 27310Sstevel@tonic-gate * Perform special accounting. Insert the special string into the 27320Sstevel@tonic-gate * ut_line array. For INIT_PROCESSes put in the name of the 27330Sstevel@tonic-gate * program in the "ut_user" field. 27340Sstevel@tonic-gate */ 27350Sstevel@tonic-gate switch (state) { 27360Sstevel@tonic-gate case INIT_PROCESS: 27370Sstevel@tonic-gate (void) strncpy(u->ut_user, program, sizeof (u->ut_user)); 27380Sstevel@tonic-gate (void) strcpy(fail_buf, "INIT_PROCESS"); 27390Sstevel@tonic-gate break; 27400Sstevel@tonic-gate 27410Sstevel@tonic-gate default: 27420Sstevel@tonic-gate (void) strlcpy(fail_buf, u->ut_id, sizeof (u->ut_id) + 1); 27430Sstevel@tonic-gate break; 27440Sstevel@tonic-gate } 27450Sstevel@tonic-gate 27460Sstevel@tonic-gate /* 27470Sstevel@tonic-gate * Write out the updated entry to utmpx file. 27480Sstevel@tonic-gate */ 27490Sstevel@tonic-gate if (pututxline(u) == NULL) { 27500Sstevel@tonic-gate console(B_TRUE, "Failed write of utmpx entry: \"%s\": %s\n", 27510Sstevel@tonic-gate fail_buf, strerror(errno)); 27520Sstevel@tonic-gate endutxent(); 27530Sstevel@tonic-gate (void) sigprocmask(SIG_SETMASK, &unblock, NULL); 27540Sstevel@tonic-gate return (-1); 27550Sstevel@tonic-gate } 27560Sstevel@tonic-gate 27570Sstevel@tonic-gate /* 27580Sstevel@tonic-gate * If we're able to write to utmpx, then attempt to add to the 27590Sstevel@tonic-gate * end of the wtmpx file. 27600Sstevel@tonic-gate */ 27610Sstevel@tonic-gate updwtmpx(WTMPX, u); 27620Sstevel@tonic-gate 27630Sstevel@tonic-gate endutxent(); 27640Sstevel@tonic-gate 27650Sstevel@tonic-gate (void) sigprocmask(SIG_SETMASK, &unblock, NULL); 27660Sstevel@tonic-gate 27670Sstevel@tonic-gate return (0); 27680Sstevel@tonic-gate } 27690Sstevel@tonic-gate 27700Sstevel@tonic-gate static void 27710Sstevel@tonic-gate clearent(pid_t pid, short status) 27720Sstevel@tonic-gate { 27730Sstevel@tonic-gate struct utmpx *up; 27740Sstevel@tonic-gate sigset_t block, unblock; 27750Sstevel@tonic-gate 27760Sstevel@tonic-gate /* 27770Sstevel@tonic-gate * Block signals for utmp update. 27780Sstevel@tonic-gate */ 27790Sstevel@tonic-gate (void) sigfillset(&block); 27800Sstevel@tonic-gate (void) sigprocmask(SIG_BLOCK, &block, &unblock); 27810Sstevel@tonic-gate 27820Sstevel@tonic-gate /* 27830Sstevel@tonic-gate * No error checking for now. 27840Sstevel@tonic-gate */ 27850Sstevel@tonic-gate 27860Sstevel@tonic-gate setutxent(); 27870Sstevel@tonic-gate while (up = getutxent()) { 27880Sstevel@tonic-gate if (up->ut_pid == pid) { 27890Sstevel@tonic-gate if (up->ut_type == DEAD_PROCESS) { 27900Sstevel@tonic-gate /* 27910Sstevel@tonic-gate * Cleaned up elsewhere. 27920Sstevel@tonic-gate */ 27930Sstevel@tonic-gate continue; 27940Sstevel@tonic-gate } 27950Sstevel@tonic-gate 27960Sstevel@tonic-gate notify_pam_dead(up); 27970Sstevel@tonic-gate 27980Sstevel@tonic-gate up->ut_type = DEAD_PROCESS; 27990Sstevel@tonic-gate up->ut_exit.e_termination = WTERMSIG(status); 28000Sstevel@tonic-gate up->ut_exit.e_exit = WEXITSTATUS(status); 28010Sstevel@tonic-gate (void) time(&up->ut_tv.tv_sec); 28020Sstevel@tonic-gate 28030Sstevel@tonic-gate (void) pututxline(up); 28040Sstevel@tonic-gate /* 28050Sstevel@tonic-gate * Now attempt to add to the end of the 28060Sstevel@tonic-gate * wtmp and wtmpx files. Do not create 28070Sstevel@tonic-gate * if they don't already exist. 28080Sstevel@tonic-gate */ 28090Sstevel@tonic-gate updwtmpx(WTMPX, up); 28100Sstevel@tonic-gate 28110Sstevel@tonic-gate break; 28120Sstevel@tonic-gate } 28130Sstevel@tonic-gate } 28140Sstevel@tonic-gate 28150Sstevel@tonic-gate endutxent(); 28160Sstevel@tonic-gate (void) sigprocmask(SIG_SETMASK, &unblock, NULL); 28170Sstevel@tonic-gate } 28180Sstevel@tonic-gate 28190Sstevel@tonic-gate /* 28200Sstevel@tonic-gate * prog_name() searches for the word or unix path name and 28210Sstevel@tonic-gate * returns a pointer to the last element of the pathname. 28220Sstevel@tonic-gate */ 28230Sstevel@tonic-gate static char * 28240Sstevel@tonic-gate prog_name(char *string) 28250Sstevel@tonic-gate { 28260Sstevel@tonic-gate char *ptr, *ptr2; 28270Sstevel@tonic-gate /* XXX - utmp - fix name length */ 28280Sstevel@tonic-gate static char word[_POSIX_LOGIN_NAME_MAX]; 28290Sstevel@tonic-gate 28300Sstevel@tonic-gate /* 28310Sstevel@tonic-gate * Search for the first word skipping leading spaces and tabs. 28320Sstevel@tonic-gate */ 28330Sstevel@tonic-gate while (*string == ' ' || *string == '\t') 28340Sstevel@tonic-gate string++; 28350Sstevel@tonic-gate 28360Sstevel@tonic-gate /* 28370Sstevel@tonic-gate * If the first non-space non-tab character is not one allowed in 28380Sstevel@tonic-gate * a word, return a pointer to a null string, otherwise parse the 28390Sstevel@tonic-gate * pathname. 28400Sstevel@tonic-gate */ 28410Sstevel@tonic-gate if (*string != '.' && *string != '/' && *string != '_' && 28420Sstevel@tonic-gate (*string < 'a' || *string > 'z') && 28430Sstevel@tonic-gate (*string < 'A' || * string > 'Z') && 28440Sstevel@tonic-gate (*string < '0' || *string > '9')) 28450Sstevel@tonic-gate return (""); 28460Sstevel@tonic-gate 28470Sstevel@tonic-gate /* 28480Sstevel@tonic-gate * Parse the pathname looking forward for '/', ' ', '\t', '\n' or 28490Sstevel@tonic-gate * '\0'. Each time a '/' is found, move "ptr" to one past the 28500Sstevel@tonic-gate * '/', thus when a ' ', '\t', '\n', or '\0' is found, "ptr" will 28510Sstevel@tonic-gate * point to the last element of the pathname. 28520Sstevel@tonic-gate */ 2853*8735SEnrico.Perla@Sun.COM for (ptr = string; *string != ' ' && *string != '\t' && 2854*8735SEnrico.Perla@Sun.COM *string != '\n' && *string != '\0'; string++) { 28550Sstevel@tonic-gate if (*string == '/') 28560Sstevel@tonic-gate ptr = string+1; 28570Sstevel@tonic-gate } 28580Sstevel@tonic-gate 28590Sstevel@tonic-gate /* 28600Sstevel@tonic-gate * Copy out up to the size of the "ut_user" array into "word", 28610Sstevel@tonic-gate * null terminate it and return a pointer to it. 28620Sstevel@tonic-gate */ 28630Sstevel@tonic-gate /* XXX - utmp - fix name length */ 28640Sstevel@tonic-gate for (ptr2 = &word[0]; ptr2 < &word[_POSIX_LOGIN_NAME_MAX - 1] && 28650Sstevel@tonic-gate ptr < string; /* CSTYLED */) 28660Sstevel@tonic-gate *ptr2++ = *ptr++; 28670Sstevel@tonic-gate 28680Sstevel@tonic-gate *ptr2 = '\0'; 28690Sstevel@tonic-gate return (&word[0]); 28700Sstevel@tonic-gate } 28710Sstevel@tonic-gate 28720Sstevel@tonic-gate 28730Sstevel@tonic-gate /* 28740Sstevel@tonic-gate * realcon() returns a nonzero value if there is a character device 28750Sstevel@tonic-gate * associated with SYSCON that has the same device number as CONSOLE. 28760Sstevel@tonic-gate */ 28770Sstevel@tonic-gate static int 28780Sstevel@tonic-gate realcon() 28790Sstevel@tonic-gate { 28800Sstevel@tonic-gate struct stat sconbuf, conbuf; 28810Sstevel@tonic-gate 28820Sstevel@tonic-gate if (stat(SYSCON, &sconbuf) != -1 && 28830Sstevel@tonic-gate stat(CONSOLE, &conbuf) != -1 && 2884871Scasper S_ISCHR(sconbuf.st_mode) && 2885871Scasper S_ISCHR(conbuf.st_mode) && 28860Sstevel@tonic-gate sconbuf.st_rdev == conbuf.st_rdev) { 28870Sstevel@tonic-gate return (1); 28880Sstevel@tonic-gate } else { 28890Sstevel@tonic-gate return (0); 28900Sstevel@tonic-gate } 28910Sstevel@tonic-gate } 28920Sstevel@tonic-gate 28930Sstevel@tonic-gate 28940Sstevel@tonic-gate /* 28950Sstevel@tonic-gate * get_ioctl_syscon() retrieves the SYSCON settings from the IOCTLSYSCON file. 28960Sstevel@tonic-gate * Returns true if the IOCTLSYSCON file needs to be written (with 28970Sstevel@tonic-gate * write_ioctl_syscon() below) 28980Sstevel@tonic-gate */ 28990Sstevel@tonic-gate static int 29000Sstevel@tonic-gate get_ioctl_syscon() 29010Sstevel@tonic-gate { 29020Sstevel@tonic-gate FILE *fp; 29030Sstevel@tonic-gate unsigned int iflags, oflags, cflags, lflags, ldisc, cc[18]; 29040Sstevel@tonic-gate int i, valid_format = 0; 29050Sstevel@tonic-gate 29060Sstevel@tonic-gate /* 29070Sstevel@tonic-gate * Read in the previous modes for SYSCON from IOCTLSYSCON. 29080Sstevel@tonic-gate */ 29090Sstevel@tonic-gate if ((fp = fopen(IOCTLSYSCON, "r")) == NULL) { 29100Sstevel@tonic-gate stored_syscon_termios = dflt_termios; 29110Sstevel@tonic-gate console(B_TRUE, 29120Sstevel@tonic-gate "warning:%s does not exist, default settings assumed\n", 29130Sstevel@tonic-gate IOCTLSYSCON); 29140Sstevel@tonic-gate } else { 29150Sstevel@tonic-gate 29160Sstevel@tonic-gate i = fscanf(fp, 29170Sstevel@tonic-gate "%x:%x:%x:%x:%x:%x:%x:%x:%x:%x:%x:%x:%x:%x:%x:%x:%x:%x:%x:%x:%x:%x", 29180Sstevel@tonic-gate &iflags, &oflags, &cflags, &lflags, 29190Sstevel@tonic-gate &cc[0], &cc[1], &cc[2], &cc[3], &cc[4], &cc[5], &cc[6], 29200Sstevel@tonic-gate &cc[7], &cc[8], &cc[9], &cc[10], &cc[11], &cc[12], &cc[13], 29210Sstevel@tonic-gate &cc[14], &cc[15], &cc[16], &cc[17]); 29220Sstevel@tonic-gate 29230Sstevel@tonic-gate if (i == 22) { 29240Sstevel@tonic-gate stored_syscon_termios.c_iflag = iflags; 29250Sstevel@tonic-gate stored_syscon_termios.c_oflag = oflags; 29260Sstevel@tonic-gate stored_syscon_termios.c_cflag = cflags; 29270Sstevel@tonic-gate stored_syscon_termios.c_lflag = lflags; 29280Sstevel@tonic-gate for (i = 0; i < 18; i++) 29290Sstevel@tonic-gate stored_syscon_termios.c_cc[i] = (char)cc[i]; 29300Sstevel@tonic-gate valid_format = 1; 29310Sstevel@tonic-gate } else if (i == 13) { 29320Sstevel@tonic-gate rewind(fp); 29330Sstevel@tonic-gate i = fscanf(fp, "%x:%x:%x:%x:%x:%x:%x:%x:%x:%x:%x:%x:%x", 29340Sstevel@tonic-gate &iflags, &oflags, &cflags, &lflags, &ldisc, &cc[0], &cc[1], 29350Sstevel@tonic-gate &cc[2], &cc[3], &cc[4], &cc[5], &cc[6], &cc[7]); 29360Sstevel@tonic-gate 29370Sstevel@tonic-gate /* 29380Sstevel@tonic-gate * If the file is formatted properly, use the values to 29390Sstevel@tonic-gate * initialize the console terminal condition. 29400Sstevel@tonic-gate */ 29410Sstevel@tonic-gate stored_syscon_termios.c_iflag = (ushort_t)iflags; 29420Sstevel@tonic-gate stored_syscon_termios.c_oflag = (ushort_t)oflags; 29430Sstevel@tonic-gate stored_syscon_termios.c_cflag = (ushort_t)cflags; 29440Sstevel@tonic-gate stored_syscon_termios.c_lflag = (ushort_t)lflags; 29450Sstevel@tonic-gate for (i = 0; i < 8; i++) 29460Sstevel@tonic-gate stored_syscon_termios.c_cc[i] = (char)cc[i]; 29470Sstevel@tonic-gate valid_format = 1; 29480Sstevel@tonic-gate } 29490Sstevel@tonic-gate (void) fclose(fp); 29500Sstevel@tonic-gate 29510Sstevel@tonic-gate /* If the file is badly formatted, use the default settings. */ 29520Sstevel@tonic-gate if (!valid_format) 29530Sstevel@tonic-gate stored_syscon_termios = dflt_termios; 29540Sstevel@tonic-gate } 29550Sstevel@tonic-gate 29560Sstevel@tonic-gate /* If the file had a bad format, rewrite it later. */ 29570Sstevel@tonic-gate return (!valid_format); 29580Sstevel@tonic-gate } 29590Sstevel@tonic-gate 29600Sstevel@tonic-gate 29610Sstevel@tonic-gate static void 29620Sstevel@tonic-gate write_ioctl_syscon() 29630Sstevel@tonic-gate { 29640Sstevel@tonic-gate FILE *fp; 29650Sstevel@tonic-gate int i; 29660Sstevel@tonic-gate 29670Sstevel@tonic-gate (void) unlink(SYSCON); 29680Sstevel@tonic-gate (void) link(SYSTTY, SYSCON); 29690Sstevel@tonic-gate (void) umask(022); 29700Sstevel@tonic-gate fp = fopen(IOCTLSYSCON, "w"); 29710Sstevel@tonic-gate 29720Sstevel@tonic-gate (void) fprintf(fp, "%x:%x:%x:%x:0", stored_syscon_termios.c_iflag, 29730Sstevel@tonic-gate stored_syscon_termios.c_oflag, stored_syscon_termios.c_cflag, 29740Sstevel@tonic-gate stored_syscon_termios.c_lflag); 29750Sstevel@tonic-gate for (i = 0; i < 8; ++i) 29760Sstevel@tonic-gate (void) fprintf(fp, ":%x", stored_syscon_termios.c_cc[i]); 29770Sstevel@tonic-gate (void) putc('\n', fp); 29780Sstevel@tonic-gate 29790Sstevel@tonic-gate (void) fflush(fp); 29800Sstevel@tonic-gate (void) fsync(fileno(fp)); 29810Sstevel@tonic-gate (void) fclose(fp); 29820Sstevel@tonic-gate (void) umask(cmask); 29830Sstevel@tonic-gate } 29840Sstevel@tonic-gate 29850Sstevel@tonic-gate 29860Sstevel@tonic-gate /* 29870Sstevel@tonic-gate * void console(boolean_t, char *, ...) 29880Sstevel@tonic-gate * Outputs the requested message to the system console. Note that the number 29890Sstevel@tonic-gate * of arguments passed to console() should be determined by the print format. 29900Sstevel@tonic-gate * 29910Sstevel@tonic-gate * The "prefix" parameter indicates whether or not "INIT: " should precede the 29920Sstevel@tonic-gate * message. 29930Sstevel@tonic-gate * 29940Sstevel@tonic-gate * To make sure we write to the console in a sane fashion, we use the modes 29950Sstevel@tonic-gate * we keep in stored_syscon_termios (which we read out of /etc/ioctl.syscon). 29960Sstevel@tonic-gate * Afterwards we restore whatever modes were already there. 29970Sstevel@tonic-gate */ 29980Sstevel@tonic-gate /* PRINTFLIKE2 */ 29990Sstevel@tonic-gate static void 30000Sstevel@tonic-gate console(boolean_t prefix, char *format, ...) 30010Sstevel@tonic-gate { 30020Sstevel@tonic-gate char outbuf[BUFSIZ]; 30030Sstevel@tonic-gate va_list args; 30040Sstevel@tonic-gate int fd, getret; 30050Sstevel@tonic-gate struct termios old_syscon_termios; 30060Sstevel@tonic-gate FILE *f; 30070Sstevel@tonic-gate 30080Sstevel@tonic-gate /* 30090Sstevel@tonic-gate * We open SYSCON anew each time in case it has changed (see 30100Sstevel@tonic-gate * userinit()). 30110Sstevel@tonic-gate */ 30120Sstevel@tonic-gate if ((fd = open(SYSCON, O_RDWR | O_NOCTTY)) < 0 || 30130Sstevel@tonic-gate (f = fdopen(fd, "r+")) == NULL) { 30140Sstevel@tonic-gate if (prefix) 30150Sstevel@tonic-gate syslog(LOG_WARNING, "INIT: "); 30160Sstevel@tonic-gate va_start(args, format); 30170Sstevel@tonic-gate vsyslog(LOG_WARNING, format, args); 30180Sstevel@tonic-gate va_end(args); 30190Sstevel@tonic-gate if (fd >= 0) 30200Sstevel@tonic-gate (void) close(fd); 30210Sstevel@tonic-gate return; 30220Sstevel@tonic-gate } 30230Sstevel@tonic-gate setbuf(f, &outbuf[0]); 30240Sstevel@tonic-gate 30250Sstevel@tonic-gate getret = tcgetattr(fd, &old_syscon_termios); 30260Sstevel@tonic-gate old_syscon_termios.c_cflag &= ~HUPCL; 30270Sstevel@tonic-gate if (realcon()) 30280Sstevel@tonic-gate /* Don't overwrite cflag of real console. */ 30290Sstevel@tonic-gate stored_syscon_termios.c_cflag = old_syscon_termios.c_cflag; 30300Sstevel@tonic-gate 30310Sstevel@tonic-gate stored_syscon_termios.c_cflag &= ~HUPCL; 30320Sstevel@tonic-gate 30330Sstevel@tonic-gate (void) tcsetattr(fd, TCSANOW, &stored_syscon_termios); 30340Sstevel@tonic-gate 30350Sstevel@tonic-gate if (prefix) 30360Sstevel@tonic-gate (void) fprintf(f, "\nINIT: "); 30370Sstevel@tonic-gate va_start(args, format); 30380Sstevel@tonic-gate (void) vfprintf(f, format, args); 30390Sstevel@tonic-gate va_end(args); 30400Sstevel@tonic-gate 30410Sstevel@tonic-gate if (getret == 0) 30420Sstevel@tonic-gate (void) tcsetattr(fd, TCSADRAIN, &old_syscon_termios); 30430Sstevel@tonic-gate 30440Sstevel@tonic-gate (void) fclose(f); 30450Sstevel@tonic-gate } 30460Sstevel@tonic-gate 30470Sstevel@tonic-gate /* 30480Sstevel@tonic-gate * timer() is a substitute for sleep() which uses alarm() and pause(). 30490Sstevel@tonic-gate */ 30500Sstevel@tonic-gate static void 30510Sstevel@tonic-gate timer(int waitime) 30520Sstevel@tonic-gate { 30530Sstevel@tonic-gate setimer(waitime); 30540Sstevel@tonic-gate while (time_up == FALSE) 30550Sstevel@tonic-gate (void) pause(); 30560Sstevel@tonic-gate } 30570Sstevel@tonic-gate 30580Sstevel@tonic-gate static void 30590Sstevel@tonic-gate setimer(int timelimit) 30600Sstevel@tonic-gate { 30610Sstevel@tonic-gate alarmclk(); 30620Sstevel@tonic-gate (void) alarm(timelimit); 30630Sstevel@tonic-gate time_up = (timelimit ? FALSE : TRUE); 30640Sstevel@tonic-gate } 30650Sstevel@tonic-gate 30660Sstevel@tonic-gate /* 30670Sstevel@tonic-gate * Fails with 30680Sstevel@tonic-gate * ENOMEM - out of memory 30690Sstevel@tonic-gate * ECONNABORTED - repository connection broken 30700Sstevel@tonic-gate * EPERM - permission denied 30710Sstevel@tonic-gate * EACCES - backend access denied 30720Sstevel@tonic-gate * EROFS - backend readonly 30730Sstevel@tonic-gate */ 30740Sstevel@tonic-gate static int 30750Sstevel@tonic-gate get_or_add_startd(scf_instance_t *inst) 30760Sstevel@tonic-gate { 30770Sstevel@tonic-gate scf_handle_t *h; 30780Sstevel@tonic-gate scf_scope_t *scope = NULL; 30790Sstevel@tonic-gate scf_service_t *svc = NULL; 30800Sstevel@tonic-gate int ret = 0; 30810Sstevel@tonic-gate 30820Sstevel@tonic-gate h = scf_instance_handle(inst); 30830Sstevel@tonic-gate 30840Sstevel@tonic-gate if (scf_handle_decode_fmri(h, SCF_SERVICE_STARTD, NULL, NULL, inst, 30850Sstevel@tonic-gate NULL, NULL, SCF_DECODE_FMRI_EXACT) == 0) 30860Sstevel@tonic-gate return (0); 30870Sstevel@tonic-gate 30880Sstevel@tonic-gate switch (scf_error()) { 30890Sstevel@tonic-gate case SCF_ERROR_CONNECTION_BROKEN: 30900Sstevel@tonic-gate return (ECONNABORTED); 30910Sstevel@tonic-gate 30920Sstevel@tonic-gate case SCF_ERROR_NOT_FOUND: 30930Sstevel@tonic-gate break; 30940Sstevel@tonic-gate 30950Sstevel@tonic-gate case SCF_ERROR_HANDLE_MISMATCH: 30960Sstevel@tonic-gate case SCF_ERROR_INVALID_ARGUMENT: 30970Sstevel@tonic-gate case SCF_ERROR_CONSTRAINT_VIOLATED: 30980Sstevel@tonic-gate default: 30990Sstevel@tonic-gate bad_error("scf_handle_decode_fmri", scf_error()); 31000Sstevel@tonic-gate } 31010Sstevel@tonic-gate 31020Sstevel@tonic-gate /* Make sure we're right, since we're adding piece-by-piece. */ 31030Sstevel@tonic-gate assert(strcmp(SCF_SERVICE_STARTD, 31040Sstevel@tonic-gate "svc:/system/svc/restarter:default") == 0); 31050Sstevel@tonic-gate 31060Sstevel@tonic-gate if ((scope = scf_scope_create(h)) == NULL || 31070Sstevel@tonic-gate (svc = scf_service_create(h)) == NULL) { 31080Sstevel@tonic-gate ret = ENOMEM; 31090Sstevel@tonic-gate goto out; 31100Sstevel@tonic-gate } 31110Sstevel@tonic-gate 31120Sstevel@tonic-gate get_scope: 31130Sstevel@tonic-gate if (scf_handle_get_scope(h, SCF_SCOPE_LOCAL, scope) != 0) { 31140Sstevel@tonic-gate switch (scf_error()) { 31150Sstevel@tonic-gate case SCF_ERROR_CONNECTION_BROKEN: 31160Sstevel@tonic-gate ret = ECONNABORTED; 31170Sstevel@tonic-gate goto out; 31180Sstevel@tonic-gate 31190Sstevel@tonic-gate case SCF_ERROR_NOT_FOUND: 31200Sstevel@tonic-gate (void) fputs(gettext( 31210Sstevel@tonic-gate "smf(5) repository missing local scope.\n"), 31220Sstevel@tonic-gate stderr); 31230Sstevel@tonic-gate exit(1); 31240Sstevel@tonic-gate /* NOTREACHED */ 31250Sstevel@tonic-gate 31260Sstevel@tonic-gate case SCF_ERROR_HANDLE_MISMATCH: 31270Sstevel@tonic-gate case SCF_ERROR_INVALID_ARGUMENT: 31280Sstevel@tonic-gate default: 31290Sstevel@tonic-gate bad_error("scf_handle_get_scope", scf_error()); 31300Sstevel@tonic-gate } 31310Sstevel@tonic-gate } 31320Sstevel@tonic-gate 31330Sstevel@tonic-gate get_svc: 31340Sstevel@tonic-gate if (scf_scope_get_service(scope, "system/svc/restarter", svc) != 0) { 31350Sstevel@tonic-gate switch (scf_error()) { 31360Sstevel@tonic-gate case SCF_ERROR_CONNECTION_BROKEN: 31370Sstevel@tonic-gate ret = ECONNABORTED; 31380Sstevel@tonic-gate goto out; 31390Sstevel@tonic-gate 31400Sstevel@tonic-gate case SCF_ERROR_DELETED: 31410Sstevel@tonic-gate goto get_scope; 31420Sstevel@tonic-gate 31430Sstevel@tonic-gate case SCF_ERROR_NOT_FOUND: 31440Sstevel@tonic-gate break; 31450Sstevel@tonic-gate 31460Sstevel@tonic-gate case SCF_ERROR_HANDLE_MISMATCH: 31470Sstevel@tonic-gate case SCF_ERROR_INVALID_ARGUMENT: 31480Sstevel@tonic-gate case SCF_ERROR_NOT_SET: 31490Sstevel@tonic-gate default: 31500Sstevel@tonic-gate bad_error("scf_scope_get_service", scf_error()); 31510Sstevel@tonic-gate } 31520Sstevel@tonic-gate 31530Sstevel@tonic-gate add_svc: 31540Sstevel@tonic-gate if (scf_scope_add_service(scope, "system/svc/restarter", svc) != 31550Sstevel@tonic-gate 0) { 31560Sstevel@tonic-gate switch (scf_error()) { 31570Sstevel@tonic-gate case SCF_ERROR_CONNECTION_BROKEN: 31580Sstevel@tonic-gate ret = ECONNABORTED; 31590Sstevel@tonic-gate goto out; 31600Sstevel@tonic-gate 31610Sstevel@tonic-gate case SCF_ERROR_EXISTS: 31620Sstevel@tonic-gate goto get_svc; 31630Sstevel@tonic-gate 31640Sstevel@tonic-gate case SCF_ERROR_PERMISSION_DENIED: 31650Sstevel@tonic-gate ret = EPERM; 31660Sstevel@tonic-gate goto out; 31670Sstevel@tonic-gate 31680Sstevel@tonic-gate case SCF_ERROR_BACKEND_ACCESS: 31690Sstevel@tonic-gate ret = EACCES; 31700Sstevel@tonic-gate goto out; 31710Sstevel@tonic-gate 31720Sstevel@tonic-gate case SCF_ERROR_BACKEND_READONLY: 31730Sstevel@tonic-gate ret = EROFS; 31740Sstevel@tonic-gate goto out; 31750Sstevel@tonic-gate 31760Sstevel@tonic-gate case SCF_ERROR_HANDLE_MISMATCH: 31770Sstevel@tonic-gate case SCF_ERROR_INVALID_ARGUMENT: 31780Sstevel@tonic-gate case SCF_ERROR_NOT_SET: 31790Sstevel@tonic-gate default: 31800Sstevel@tonic-gate bad_error("scf_scope_add_service", scf_error()); 31810Sstevel@tonic-gate } 31820Sstevel@tonic-gate } 31830Sstevel@tonic-gate } 31840Sstevel@tonic-gate 31850Sstevel@tonic-gate get_inst: 31860Sstevel@tonic-gate if (scf_service_get_instance(svc, "default", inst) != 0) { 31870Sstevel@tonic-gate switch (scf_error()) { 31880Sstevel@tonic-gate case SCF_ERROR_CONNECTION_BROKEN: 31890Sstevel@tonic-gate ret = ECONNABORTED; 31900Sstevel@tonic-gate goto out; 31910Sstevel@tonic-gate 31920Sstevel@tonic-gate case SCF_ERROR_DELETED: 31930Sstevel@tonic-gate goto add_svc; 31940Sstevel@tonic-gate 31950Sstevel@tonic-gate case SCF_ERROR_NOT_FOUND: 31960Sstevel@tonic-gate break; 31970Sstevel@tonic-gate 31980Sstevel@tonic-gate case SCF_ERROR_HANDLE_MISMATCH: 31990Sstevel@tonic-gate case SCF_ERROR_INVALID_ARGUMENT: 32000Sstevel@tonic-gate case SCF_ERROR_NOT_SET: 32010Sstevel@tonic-gate default: 32020Sstevel@tonic-gate bad_error("scf_service_get_instance", scf_error()); 32030Sstevel@tonic-gate } 32040Sstevel@tonic-gate 32050Sstevel@tonic-gate if (scf_service_add_instance(svc, "default", inst) != 32060Sstevel@tonic-gate 0) { 32070Sstevel@tonic-gate switch (scf_error()) { 32080Sstevel@tonic-gate case SCF_ERROR_CONNECTION_BROKEN: 32090Sstevel@tonic-gate ret = ECONNABORTED; 32100Sstevel@tonic-gate goto out; 32110Sstevel@tonic-gate 32120Sstevel@tonic-gate case SCF_ERROR_DELETED: 32130Sstevel@tonic-gate goto add_svc; 32140Sstevel@tonic-gate 32150Sstevel@tonic-gate case SCF_ERROR_EXISTS: 32160Sstevel@tonic-gate goto get_inst; 32170Sstevel@tonic-gate 32180Sstevel@tonic-gate case SCF_ERROR_PERMISSION_DENIED: 32190Sstevel@tonic-gate ret = EPERM; 32200Sstevel@tonic-gate goto out; 32210Sstevel@tonic-gate 32220Sstevel@tonic-gate case SCF_ERROR_BACKEND_ACCESS: 32230Sstevel@tonic-gate ret = EACCES; 32240Sstevel@tonic-gate goto out; 32250Sstevel@tonic-gate 32260Sstevel@tonic-gate case SCF_ERROR_BACKEND_READONLY: 32270Sstevel@tonic-gate ret = EROFS; 32280Sstevel@tonic-gate goto out; 32290Sstevel@tonic-gate 32300Sstevel@tonic-gate case SCF_ERROR_HANDLE_MISMATCH: 32310Sstevel@tonic-gate case SCF_ERROR_INVALID_ARGUMENT: 32320Sstevel@tonic-gate case SCF_ERROR_NOT_SET: 32330Sstevel@tonic-gate default: 32340Sstevel@tonic-gate bad_error("scf_service_add_instance", 32350Sstevel@tonic-gate scf_error()); 32360Sstevel@tonic-gate } 32370Sstevel@tonic-gate } 32380Sstevel@tonic-gate } 32390Sstevel@tonic-gate 32400Sstevel@tonic-gate ret = 0; 32410Sstevel@tonic-gate 32420Sstevel@tonic-gate out: 32430Sstevel@tonic-gate scf_service_destroy(svc); 32440Sstevel@tonic-gate scf_scope_destroy(scope); 32450Sstevel@tonic-gate return (ret); 32460Sstevel@tonic-gate } 32470Sstevel@tonic-gate 32480Sstevel@tonic-gate /* 32490Sstevel@tonic-gate * Fails with 32500Sstevel@tonic-gate * ECONNABORTED - repository connection broken 32510Sstevel@tonic-gate * ECANCELED - the transaction's property group was deleted 32520Sstevel@tonic-gate */ 32530Sstevel@tonic-gate static int 32540Sstevel@tonic-gate transaction_add_set(scf_transaction_t *tx, scf_transaction_entry_t *ent, 32550Sstevel@tonic-gate const char *pname, scf_type_t type) 32560Sstevel@tonic-gate { 32570Sstevel@tonic-gate change_type: 32580Sstevel@tonic-gate if (scf_transaction_property_change_type(tx, ent, pname, type) == 0) 32590Sstevel@tonic-gate return (0); 32600Sstevel@tonic-gate 32610Sstevel@tonic-gate switch (scf_error()) { 32620Sstevel@tonic-gate case SCF_ERROR_CONNECTION_BROKEN: 32630Sstevel@tonic-gate return (ECONNABORTED); 32640Sstevel@tonic-gate 32650Sstevel@tonic-gate case SCF_ERROR_DELETED: 32660Sstevel@tonic-gate return (ECANCELED); 32670Sstevel@tonic-gate 32680Sstevel@tonic-gate case SCF_ERROR_NOT_FOUND: 32690Sstevel@tonic-gate goto new; 32700Sstevel@tonic-gate 32710Sstevel@tonic-gate case SCF_ERROR_HANDLE_MISMATCH: 32720Sstevel@tonic-gate case SCF_ERROR_INVALID_ARGUMENT: 32730Sstevel@tonic-gate case SCF_ERROR_NOT_BOUND: 32740Sstevel@tonic-gate case SCF_ERROR_NOT_SET: 32750Sstevel@tonic-gate default: 32760Sstevel@tonic-gate bad_error("scf_transaction_property_change_type", scf_error()); 32770Sstevel@tonic-gate } 32780Sstevel@tonic-gate 32790Sstevel@tonic-gate new: 32800Sstevel@tonic-gate if (scf_transaction_property_new(tx, ent, pname, type) == 0) 32810Sstevel@tonic-gate return (0); 32820Sstevel@tonic-gate 32830Sstevel@tonic-gate switch (scf_error()) { 32840Sstevel@tonic-gate case SCF_ERROR_CONNECTION_BROKEN: 32850Sstevel@tonic-gate return (ECONNABORTED); 32860Sstevel@tonic-gate 32870Sstevel@tonic-gate case SCF_ERROR_DELETED: 32880Sstevel@tonic-gate return (ECANCELED); 32890Sstevel@tonic-gate 32900Sstevel@tonic-gate case SCF_ERROR_EXISTS: 32910Sstevel@tonic-gate goto change_type; 32920Sstevel@tonic-gate 32930Sstevel@tonic-gate case SCF_ERROR_HANDLE_MISMATCH: 32940Sstevel@tonic-gate case SCF_ERROR_INVALID_ARGUMENT: 32950Sstevel@tonic-gate case SCF_ERROR_NOT_BOUND: 32960Sstevel@tonic-gate case SCF_ERROR_NOT_SET: 32970Sstevel@tonic-gate default: 32980Sstevel@tonic-gate bad_error("scf_transaction_property_new", scf_error()); 32990Sstevel@tonic-gate /* NOTREACHED */ 33000Sstevel@tonic-gate } 33010Sstevel@tonic-gate } 33020Sstevel@tonic-gate 33030Sstevel@tonic-gate static void 33040Sstevel@tonic-gate scferr(void) 33050Sstevel@tonic-gate { 33060Sstevel@tonic-gate switch (scf_error()) { 33070Sstevel@tonic-gate case SCF_ERROR_NO_MEMORY: 33080Sstevel@tonic-gate console(B_TRUE, gettext("Out of memory.\n")); 33090Sstevel@tonic-gate break; 33100Sstevel@tonic-gate 33110Sstevel@tonic-gate case SCF_ERROR_CONNECTION_BROKEN: 33120Sstevel@tonic-gate console(B_TRUE, gettext( 33130Sstevel@tonic-gate "Connection to smf(5) repository server broken.\n")); 33140Sstevel@tonic-gate break; 33150Sstevel@tonic-gate 33160Sstevel@tonic-gate case SCF_ERROR_NO_RESOURCES: 33170Sstevel@tonic-gate console(B_TRUE, gettext( 33180Sstevel@tonic-gate "smf(5) repository server is out of memory.\n")); 33190Sstevel@tonic-gate break; 33200Sstevel@tonic-gate 33210Sstevel@tonic-gate case SCF_ERROR_PERMISSION_DENIED: 33220Sstevel@tonic-gate console(B_TRUE, gettext("Insufficient privileges.\n")); 33230Sstevel@tonic-gate break; 33240Sstevel@tonic-gate 33250Sstevel@tonic-gate default: 33260Sstevel@tonic-gate console(B_TRUE, gettext("libscf error: %s\n"), 33270Sstevel@tonic-gate scf_strerror(scf_error())); 33280Sstevel@tonic-gate } 33290Sstevel@tonic-gate } 33300Sstevel@tonic-gate 33310Sstevel@tonic-gate static void 33320Sstevel@tonic-gate lscf_set_runlevel(char rl) 33330Sstevel@tonic-gate { 33340Sstevel@tonic-gate scf_handle_t *h; 33350Sstevel@tonic-gate scf_instance_t *inst = NULL; 33360Sstevel@tonic-gate scf_propertygroup_t *pg = NULL; 33370Sstevel@tonic-gate scf_transaction_t *tx = NULL; 33380Sstevel@tonic-gate scf_transaction_entry_t *ent = NULL; 33390Sstevel@tonic-gate scf_value_t *val = NULL; 33400Sstevel@tonic-gate char buf[2]; 33410Sstevel@tonic-gate int r; 33420Sstevel@tonic-gate 33430Sstevel@tonic-gate h = scf_handle_create(SCF_VERSION); 33440Sstevel@tonic-gate if (h == NULL) { 33450Sstevel@tonic-gate scferr(); 33460Sstevel@tonic-gate return; 33470Sstevel@tonic-gate } 33480Sstevel@tonic-gate 33490Sstevel@tonic-gate if (scf_handle_bind(h) != 0) { 33500Sstevel@tonic-gate switch (scf_error()) { 33510Sstevel@tonic-gate case SCF_ERROR_NO_SERVER: 33520Sstevel@tonic-gate console(B_TRUE, 33530Sstevel@tonic-gate gettext("smf(5) repository server not running.\n")); 33540Sstevel@tonic-gate goto bail; 33550Sstevel@tonic-gate 33560Sstevel@tonic-gate default: 33570Sstevel@tonic-gate scferr(); 33580Sstevel@tonic-gate goto bail; 33590Sstevel@tonic-gate } 33600Sstevel@tonic-gate } 33610Sstevel@tonic-gate 33620Sstevel@tonic-gate if ((inst = scf_instance_create(h)) == NULL || 33630Sstevel@tonic-gate (pg = scf_pg_create(h)) == NULL || 33640Sstevel@tonic-gate (val = scf_value_create(h)) == NULL || 33650Sstevel@tonic-gate (tx = scf_transaction_create(h)) == NULL || 33660Sstevel@tonic-gate (ent = scf_entry_create(h)) == NULL) { 33670Sstevel@tonic-gate scferr(); 33680Sstevel@tonic-gate goto bail; 33690Sstevel@tonic-gate } 33700Sstevel@tonic-gate 33710Sstevel@tonic-gate get_inst: 33720Sstevel@tonic-gate r = get_or_add_startd(inst); 33730Sstevel@tonic-gate switch (r) { 33740Sstevel@tonic-gate case 0: 33750Sstevel@tonic-gate break; 33760Sstevel@tonic-gate 33770Sstevel@tonic-gate case ENOMEM: 33780Sstevel@tonic-gate case ECONNABORTED: 33790Sstevel@tonic-gate case EPERM: 33800Sstevel@tonic-gate case EACCES: 33810Sstevel@tonic-gate case EROFS: 33820Sstevel@tonic-gate scferr(); 33830Sstevel@tonic-gate goto bail; 33840Sstevel@tonic-gate default: 33850Sstevel@tonic-gate bad_error("get_or_add_startd", r); 33860Sstevel@tonic-gate } 33870Sstevel@tonic-gate 33880Sstevel@tonic-gate get_pg: 33890Sstevel@tonic-gate if (scf_instance_get_pg(inst, SCF_PG_OPTIONS_OVR, pg) != 0) { 33900Sstevel@tonic-gate switch (scf_error()) { 33910Sstevel@tonic-gate case SCF_ERROR_CONNECTION_BROKEN: 33920Sstevel@tonic-gate scferr(); 33930Sstevel@tonic-gate goto bail; 33940Sstevel@tonic-gate 33950Sstevel@tonic-gate case SCF_ERROR_DELETED: 33960Sstevel@tonic-gate goto get_inst; 33970Sstevel@tonic-gate 33980Sstevel@tonic-gate case SCF_ERROR_NOT_FOUND: 33990Sstevel@tonic-gate break; 34000Sstevel@tonic-gate 34010Sstevel@tonic-gate case SCF_ERROR_HANDLE_MISMATCH: 34020Sstevel@tonic-gate case SCF_ERROR_INVALID_ARGUMENT: 34030Sstevel@tonic-gate case SCF_ERROR_NOT_SET: 34040Sstevel@tonic-gate default: 34050Sstevel@tonic-gate bad_error("scf_instance_get_pg", scf_error()); 34060Sstevel@tonic-gate } 34070Sstevel@tonic-gate 34080Sstevel@tonic-gate add_pg: 34090Sstevel@tonic-gate if (scf_instance_add_pg(inst, SCF_PG_OPTIONS_OVR, 34100Sstevel@tonic-gate SCF_PG_OPTIONS_OVR_TYPE, SCF_PG_OPTIONS_OVR_FLAGS, pg) != 34110Sstevel@tonic-gate 0) { 34120Sstevel@tonic-gate switch (scf_error()) { 34130Sstevel@tonic-gate case SCF_ERROR_CONNECTION_BROKEN: 34140Sstevel@tonic-gate case SCF_ERROR_PERMISSION_DENIED: 34150Sstevel@tonic-gate case SCF_ERROR_BACKEND_ACCESS: 34160Sstevel@tonic-gate scferr(); 34170Sstevel@tonic-gate goto bail; 34180Sstevel@tonic-gate 34190Sstevel@tonic-gate case SCF_ERROR_DELETED: 34200Sstevel@tonic-gate goto get_inst; 34210Sstevel@tonic-gate 34220Sstevel@tonic-gate case SCF_ERROR_EXISTS: 34230Sstevel@tonic-gate goto get_pg; 34240Sstevel@tonic-gate 34250Sstevel@tonic-gate case SCF_ERROR_HANDLE_MISMATCH: 34260Sstevel@tonic-gate case SCF_ERROR_INVALID_ARGUMENT: 34270Sstevel@tonic-gate case SCF_ERROR_NOT_SET: 34280Sstevel@tonic-gate default: 34290Sstevel@tonic-gate bad_error("scf_instance_add_pg", scf_error()); 34300Sstevel@tonic-gate } 34310Sstevel@tonic-gate } 34320Sstevel@tonic-gate } 34330Sstevel@tonic-gate 34340Sstevel@tonic-gate buf[0] = rl; 34350Sstevel@tonic-gate buf[1] = '\0'; 34360Sstevel@tonic-gate r = scf_value_set_astring(val, buf); 34370Sstevel@tonic-gate assert(r == 0); 34380Sstevel@tonic-gate 34390Sstevel@tonic-gate for (;;) { 34400Sstevel@tonic-gate if (scf_transaction_start(tx, pg) != 0) { 34410Sstevel@tonic-gate switch (scf_error()) { 34420Sstevel@tonic-gate case SCF_ERROR_CONNECTION_BROKEN: 34430Sstevel@tonic-gate case SCF_ERROR_PERMISSION_DENIED: 34440Sstevel@tonic-gate case SCF_ERROR_BACKEND_ACCESS: 34450Sstevel@tonic-gate scferr(); 34460Sstevel@tonic-gate goto bail; 34470Sstevel@tonic-gate 34480Sstevel@tonic-gate case SCF_ERROR_DELETED: 34490Sstevel@tonic-gate goto add_pg; 34500Sstevel@tonic-gate 34510Sstevel@tonic-gate case SCF_ERROR_HANDLE_MISMATCH: 34520Sstevel@tonic-gate case SCF_ERROR_NOT_BOUND: 34530Sstevel@tonic-gate case SCF_ERROR_IN_USE: 34540Sstevel@tonic-gate case SCF_ERROR_NOT_SET: 34550Sstevel@tonic-gate default: 34560Sstevel@tonic-gate bad_error("scf_transaction_start", scf_error()); 34570Sstevel@tonic-gate } 34580Sstevel@tonic-gate } 34590Sstevel@tonic-gate 34600Sstevel@tonic-gate r = transaction_add_set(tx, ent, "runlevel", SCF_TYPE_ASTRING); 34610Sstevel@tonic-gate switch (r) { 34620Sstevel@tonic-gate case 0: 34630Sstevel@tonic-gate break; 34640Sstevel@tonic-gate 34650Sstevel@tonic-gate case ECONNABORTED: 34660Sstevel@tonic-gate scferr(); 34670Sstevel@tonic-gate goto bail; 34680Sstevel@tonic-gate 34690Sstevel@tonic-gate case ECANCELED: 34700Sstevel@tonic-gate scf_transaction_reset(tx); 34710Sstevel@tonic-gate goto add_pg; 34720Sstevel@tonic-gate 34730Sstevel@tonic-gate default: 34740Sstevel@tonic-gate bad_error("transaction_add_set", r); 34750Sstevel@tonic-gate } 34760Sstevel@tonic-gate 34770Sstevel@tonic-gate r = scf_entry_add_value(ent, val); 34780Sstevel@tonic-gate assert(r == 0); 34790Sstevel@tonic-gate 34800Sstevel@tonic-gate r = scf_transaction_commit(tx); 34810Sstevel@tonic-gate if (r == 1) 34820Sstevel@tonic-gate break; 34830Sstevel@tonic-gate 34840Sstevel@tonic-gate if (r != 0) { 34850Sstevel@tonic-gate switch (scf_error()) { 34860Sstevel@tonic-gate case SCF_ERROR_CONNECTION_BROKEN: 34870Sstevel@tonic-gate case SCF_ERROR_PERMISSION_DENIED: 34880Sstevel@tonic-gate case SCF_ERROR_BACKEND_ACCESS: 34890Sstevel@tonic-gate case SCF_ERROR_BACKEND_READONLY: 34900Sstevel@tonic-gate scferr(); 34910Sstevel@tonic-gate goto bail; 34920Sstevel@tonic-gate 34930Sstevel@tonic-gate case SCF_ERROR_DELETED: 34940Sstevel@tonic-gate scf_transaction_reset(tx); 34950Sstevel@tonic-gate goto add_pg; 34960Sstevel@tonic-gate 34970Sstevel@tonic-gate case SCF_ERROR_INVALID_ARGUMENT: 34980Sstevel@tonic-gate case SCF_ERROR_NOT_BOUND: 34990Sstevel@tonic-gate case SCF_ERROR_NOT_SET: 35000Sstevel@tonic-gate default: 35010Sstevel@tonic-gate bad_error("scf_transaction_commit", 35020Sstevel@tonic-gate scf_error()); 35030Sstevel@tonic-gate } 35040Sstevel@tonic-gate } 35050Sstevel@tonic-gate 35060Sstevel@tonic-gate scf_transaction_reset(tx); 35070Sstevel@tonic-gate (void) scf_pg_update(pg); 35080Sstevel@tonic-gate } 35090Sstevel@tonic-gate 35100Sstevel@tonic-gate bail: 35110Sstevel@tonic-gate scf_transaction_destroy(tx); 35120Sstevel@tonic-gate scf_entry_destroy(ent); 35130Sstevel@tonic-gate scf_value_destroy(val); 35140Sstevel@tonic-gate scf_pg_destroy(pg); 35150Sstevel@tonic-gate scf_instance_destroy(inst); 35160Sstevel@tonic-gate 35170Sstevel@tonic-gate (void) scf_handle_unbind(h); 35180Sstevel@tonic-gate scf_handle_destroy(h); 35190Sstevel@tonic-gate } 35200Sstevel@tonic-gate 35210Sstevel@tonic-gate /* 35220Sstevel@tonic-gate * Function to handle requests from users to main init running as process 1. 35230Sstevel@tonic-gate */ 35240Sstevel@tonic-gate static void 35250Sstevel@tonic-gate userinit(int argc, char **argv) 35260Sstevel@tonic-gate { 35270Sstevel@tonic-gate FILE *fp; 35280Sstevel@tonic-gate char *ln; 35290Sstevel@tonic-gate int init_signal; 35300Sstevel@tonic-gate struct stat sconbuf, conbuf; 35310Sstevel@tonic-gate const char *usage_msg = "Usage: init [0123456SsQqabc]\n"; 35320Sstevel@tonic-gate 35330Sstevel@tonic-gate /* 35340Sstevel@tonic-gate * We are a user invoked init. Is there an argument and is it 35350Sstevel@tonic-gate * a single character? If not, print usage message and quit. 35360Sstevel@tonic-gate */ 35370Sstevel@tonic-gate if (argc != 2 || argv[1][1] != '\0') { 35380Sstevel@tonic-gate (void) fprintf(stderr, usage_msg); 35390Sstevel@tonic-gate exit(0); 35400Sstevel@tonic-gate } 35410Sstevel@tonic-gate 35420Sstevel@tonic-gate if ((init_signal = lvlname_to_state((char)argv[1][0])) == -1) { 35430Sstevel@tonic-gate (void) fprintf(stderr, usage_msg); 35440Sstevel@tonic-gate (void) audit_put_record(ADT_FAILURE, ADT_FAIL_VALUE_BAD_CMD, 35450Sstevel@tonic-gate argv[1]); 35460Sstevel@tonic-gate exit(1); 35470Sstevel@tonic-gate } 35480Sstevel@tonic-gate 35490Sstevel@tonic-gate if (init_signal == SINGLE_USER) { 35500Sstevel@tonic-gate /* 35510Sstevel@tonic-gate * Make sure this process is talking to a legal tty line 35520Sstevel@tonic-gate * and that /dev/syscon is linked to this line. 35530Sstevel@tonic-gate */ 35540Sstevel@tonic-gate ln = ttyname(0); /* Get the name of tty */ 35550Sstevel@tonic-gate if (ln == NULL) { 35560Sstevel@tonic-gate (void) fprintf(stderr, 35570Sstevel@tonic-gate "Standard input not a tty line\n"); 35580Sstevel@tonic-gate (void) audit_put_record(ADT_FAILURE, 35590Sstevel@tonic-gate ADT_FAIL_VALUE_BAD_TTY, argv[1]); 35600Sstevel@tonic-gate exit(1); 35610Sstevel@tonic-gate } 35622703Svikram 35632703Svikram if ((stat(ln, &sconbuf) != -1) && 35642703Svikram (stat(SYSCON, &conbuf) == -1 || 35652703Svikram sconbuf.st_rdev != conbuf.st_rdev)) { 35660Sstevel@tonic-gate /* 35672703Svikram * /dev/syscon needs to change. 35680Sstevel@tonic-gate * Unlink /dev/syscon and relink it to the current line. 35690Sstevel@tonic-gate */ 35702703Svikram if (lstat(SYSCON, &conbuf) != -1 && 35712703Svikram unlink(SYSCON) == FAILURE) { 35720Sstevel@tonic-gate perror("Can't unlink /dev/syscon"); 35730Sstevel@tonic-gate (void) fprintf(stderr, 35740Sstevel@tonic-gate "Run command on the system console.\n"); 35750Sstevel@tonic-gate (void) audit_put_record(ADT_FAILURE, 35760Sstevel@tonic-gate ADT_FAIL_VALUE_PROGRAM, argv[1]); 35770Sstevel@tonic-gate exit(1); 35780Sstevel@tonic-gate } 35792703Svikram if (symlink(ln, SYSCON) == FAILURE) { 35800Sstevel@tonic-gate (void) fprintf(stderr, 35812703Svikram "Can't symlink /dev/syscon to %s: %s", ln, 35820Sstevel@tonic-gate strerror(errno)); 35830Sstevel@tonic-gate 35840Sstevel@tonic-gate /* Try to leave a syscon */ 35850Sstevel@tonic-gate (void) link(SYSTTY, SYSCON); 35860Sstevel@tonic-gate (void) audit_put_record(ADT_FAILURE, 35870Sstevel@tonic-gate ADT_FAIL_VALUE_PROGRAM, argv[1]); 35880Sstevel@tonic-gate exit(1); 35890Sstevel@tonic-gate } 35900Sstevel@tonic-gate 35910Sstevel@tonic-gate /* 35920Sstevel@tonic-gate * Try to leave a message on system console saying where 35930Sstevel@tonic-gate * /dev/syscon is currently connected. 35940Sstevel@tonic-gate */ 35950Sstevel@tonic-gate if ((fp = fopen(SYSTTY, "r+")) != NULL) { 35960Sstevel@tonic-gate (void) fprintf(fp, 35970Sstevel@tonic-gate "\n**** SYSCON CHANGED TO %s ****\n", 35980Sstevel@tonic-gate ln); 35990Sstevel@tonic-gate (void) fclose(fp); 36000Sstevel@tonic-gate } 36010Sstevel@tonic-gate } 36020Sstevel@tonic-gate } 36030Sstevel@tonic-gate 36040Sstevel@tonic-gate update_boot_archive(init_signal); 36050Sstevel@tonic-gate 36062481Spaulson (void) audit_put_record(ADT_SUCCESS, ADT_SUCCESS, argv[1]); 36070Sstevel@tonic-gate 36080Sstevel@tonic-gate /* 36090Sstevel@tonic-gate * Signal init; init will take care of telling svc.startd. 36100Sstevel@tonic-gate */ 36110Sstevel@tonic-gate if (kill(init_pid, init_signal) == FAILURE) { 36120Sstevel@tonic-gate (void) fprintf(stderr, "Must be super-user\n"); 36130Sstevel@tonic-gate (void) audit_put_record(ADT_FAILURE, 36140Sstevel@tonic-gate ADT_FAIL_VALUE_AUTH, argv[1]); 36150Sstevel@tonic-gate exit(1); 36160Sstevel@tonic-gate } 36170Sstevel@tonic-gate 36180Sstevel@tonic-gate exit(0); 36190Sstevel@tonic-gate } 36200Sstevel@tonic-gate 36210Sstevel@tonic-gate 36220Sstevel@tonic-gate #define DELTA 25 /* Number of pidlist elements to allocate at a time */ 36230Sstevel@tonic-gate 36240Sstevel@tonic-gate /* ARGSUSED */ 36250Sstevel@tonic-gate void 36260Sstevel@tonic-gate sigpoll(int n) 36270Sstevel@tonic-gate { 36280Sstevel@tonic-gate struct pidrec prec; 36290Sstevel@tonic-gate struct pidrec *p = ≺ 36300Sstevel@tonic-gate struct pidlist *plp; 36310Sstevel@tonic-gate struct pidlist *tp, *savetp; 36320Sstevel@tonic-gate int i; 36330Sstevel@tonic-gate 36340Sstevel@tonic-gate if (Pfd < 0) { 36350Sstevel@tonic-gate return; 36360Sstevel@tonic-gate } 36372691Snakanon 36380Sstevel@tonic-gate for (;;) { 36390Sstevel@tonic-gate /* 36400Sstevel@tonic-gate * Important Note: Either read will really fail (in which case 36410Sstevel@tonic-gate * return is all we can do) or will get EAGAIN (Pfd was opened 36420Sstevel@tonic-gate * O_NDELAY), in which case we also want to return. 36430Sstevel@tonic-gate * Always return from here! 36440Sstevel@tonic-gate */ 36450Sstevel@tonic-gate if (read(Pfd, p, sizeof (struct pidrec)) != 36460Sstevel@tonic-gate sizeof (struct pidrec)) { 36470Sstevel@tonic-gate return; 36480Sstevel@tonic-gate } 36490Sstevel@tonic-gate switch (p->pd_type) { 36500Sstevel@tonic-gate 36510Sstevel@tonic-gate case ADDPID: 36520Sstevel@tonic-gate /* 36530Sstevel@tonic-gate * New "godchild", add to list. 36540Sstevel@tonic-gate */ 36550Sstevel@tonic-gate if (Plfree == NULL) { 36560Sstevel@tonic-gate plp = (struct pidlist *)calloc(DELTA, 36570Sstevel@tonic-gate sizeof (struct pidlist)); 36580Sstevel@tonic-gate if (plp == NULL) { 36590Sstevel@tonic-gate /* Can't save pid */ 36600Sstevel@tonic-gate break; 36610Sstevel@tonic-gate } 36620Sstevel@tonic-gate /* 36630Sstevel@tonic-gate * Point at 2nd record allocated, we'll use plp. 36640Sstevel@tonic-gate */ 36650Sstevel@tonic-gate tp = plp + 1; 36660Sstevel@tonic-gate /* 36670Sstevel@tonic-gate * Link them into a chain. 36680Sstevel@tonic-gate */ 36690Sstevel@tonic-gate Plfree = tp; 36700Sstevel@tonic-gate for (i = 0; i < DELTA - 2; i++) { 36710Sstevel@tonic-gate tp->pl_next = tp + 1; 36720Sstevel@tonic-gate tp++; 36730Sstevel@tonic-gate } 36740Sstevel@tonic-gate } else { 36750Sstevel@tonic-gate plp = Plfree; 36760Sstevel@tonic-gate Plfree = plp->pl_next; 36770Sstevel@tonic-gate } 36780Sstevel@tonic-gate plp->pl_pid = p->pd_pid; 36790Sstevel@tonic-gate plp->pl_dflag = 0; 36800Sstevel@tonic-gate plp->pl_next = NULL; 36810Sstevel@tonic-gate /* 36820Sstevel@tonic-gate * Note - pid list is kept in increasing order of pids. 36830Sstevel@tonic-gate */ 36840Sstevel@tonic-gate if (Plhead == NULL) { 36850Sstevel@tonic-gate Plhead = plp; 36860Sstevel@tonic-gate /* Back up to read next record */ 36870Sstevel@tonic-gate break; 36880Sstevel@tonic-gate } else { 36890Sstevel@tonic-gate savetp = tp = Plhead; 36900Sstevel@tonic-gate while (tp) { 36910Sstevel@tonic-gate if (plp->pl_pid > tp->pl_pid) { 36920Sstevel@tonic-gate savetp = tp; 36930Sstevel@tonic-gate tp = tp->pl_next; 36940Sstevel@tonic-gate continue; 36950Sstevel@tonic-gate } else if (plp->pl_pid < tp->pl_pid) { 36960Sstevel@tonic-gate if (tp == Plhead) { 36970Sstevel@tonic-gate plp->pl_next = Plhead; 36980Sstevel@tonic-gate Plhead = plp; 36990Sstevel@tonic-gate } else { 37000Sstevel@tonic-gate plp->pl_next = 37010Sstevel@tonic-gate savetp->pl_next; 37020Sstevel@tonic-gate savetp->pl_next = plp; 37030Sstevel@tonic-gate } 37040Sstevel@tonic-gate break; 37050Sstevel@tonic-gate } else { 37060Sstevel@tonic-gate /* Already in list! */ 37070Sstevel@tonic-gate plp->pl_next = Plfree; 37080Sstevel@tonic-gate Plfree = plp; 37090Sstevel@tonic-gate break; 37100Sstevel@tonic-gate } 37110Sstevel@tonic-gate } 37120Sstevel@tonic-gate if (tp == NULL) { 37130Sstevel@tonic-gate /* Add to end of list */ 37140Sstevel@tonic-gate savetp->pl_next = plp; 37150Sstevel@tonic-gate } 37160Sstevel@tonic-gate } 37170Sstevel@tonic-gate /* Back up to read next record. */ 37180Sstevel@tonic-gate break; 37190Sstevel@tonic-gate 37200Sstevel@tonic-gate case REMPID: 37210Sstevel@tonic-gate /* 37220Sstevel@tonic-gate * This one was handled by someone else, 37230Sstevel@tonic-gate * purge it from the list. 37240Sstevel@tonic-gate */ 37250Sstevel@tonic-gate if (Plhead == NULL) { 37260Sstevel@tonic-gate /* Back up to read next record. */ 37270Sstevel@tonic-gate break; 37280Sstevel@tonic-gate } 37290Sstevel@tonic-gate savetp = tp = Plhead; 37300Sstevel@tonic-gate while (tp) { 37310Sstevel@tonic-gate if (p->pd_pid > tp->pl_pid) { 37320Sstevel@tonic-gate /* Keep on looking. */ 37330Sstevel@tonic-gate savetp = tp; 37340Sstevel@tonic-gate tp = tp->pl_next; 37350Sstevel@tonic-gate continue; 37360Sstevel@tonic-gate } else if (p->pd_pid < tp->pl_pid) { 37370Sstevel@tonic-gate /* Not in list. */ 37380Sstevel@tonic-gate break; 37390Sstevel@tonic-gate } else { 37400Sstevel@tonic-gate /* Found it. */ 37410Sstevel@tonic-gate if (tp == Plhead) 37420Sstevel@tonic-gate Plhead = tp->pl_next; 37430Sstevel@tonic-gate else 37440Sstevel@tonic-gate savetp->pl_next = tp->pl_next; 37450Sstevel@tonic-gate tp->pl_next = Plfree; 37460Sstevel@tonic-gate Plfree = tp; 37470Sstevel@tonic-gate break; 37480Sstevel@tonic-gate } 37490Sstevel@tonic-gate } 37500Sstevel@tonic-gate /* Back up to read next record. */ 37510Sstevel@tonic-gate break; 37520Sstevel@tonic-gate default: 37530Sstevel@tonic-gate console(B_TRUE, "Bad message on initpipe\n"); 37540Sstevel@tonic-gate break; 37550Sstevel@tonic-gate } 37560Sstevel@tonic-gate } 37570Sstevel@tonic-gate } 37580Sstevel@tonic-gate 37590Sstevel@tonic-gate 37600Sstevel@tonic-gate static void 37610Sstevel@tonic-gate cleanaux() 37620Sstevel@tonic-gate { 37630Sstevel@tonic-gate struct pidlist *savep, *p; 37640Sstevel@tonic-gate pid_t pid; 37650Sstevel@tonic-gate short status; 37660Sstevel@tonic-gate 37670Sstevel@tonic-gate (void) sigset(SIGCLD, SIG_DFL); 37680Sstevel@tonic-gate Gchild = 0; /* Note - Safe to do this here since no SIGCLDs */ 37690Sstevel@tonic-gate (void) sighold(SIGPOLL); 37700Sstevel@tonic-gate savep = p = Plhead; 37710Sstevel@tonic-gate while (p) { 37720Sstevel@tonic-gate if (p->pl_dflag) { 37730Sstevel@tonic-gate /* 37740Sstevel@tonic-gate * Found an entry to delete, 37750Sstevel@tonic-gate * remove it from list first. 37760Sstevel@tonic-gate */ 37770Sstevel@tonic-gate pid = p->pl_pid; 37780Sstevel@tonic-gate status = p->pl_exit; 37790Sstevel@tonic-gate if (p == Plhead) { 37800Sstevel@tonic-gate Plhead = p->pl_next; 37810Sstevel@tonic-gate p->pl_next = Plfree; 37820Sstevel@tonic-gate Plfree = p; 37830Sstevel@tonic-gate savep = p = Plhead; 37840Sstevel@tonic-gate } else { 37850Sstevel@tonic-gate savep->pl_next = p->pl_next; 37860Sstevel@tonic-gate p->pl_next = Plfree; 37870Sstevel@tonic-gate Plfree = p; 37880Sstevel@tonic-gate p = savep->pl_next; 37890Sstevel@tonic-gate } 37900Sstevel@tonic-gate clearent(pid, status); 37910Sstevel@tonic-gate continue; 37920Sstevel@tonic-gate } 37930Sstevel@tonic-gate savep = p; 37940Sstevel@tonic-gate p = p->pl_next; 37950Sstevel@tonic-gate } 37960Sstevel@tonic-gate (void) sigrelse(SIGPOLL); 37970Sstevel@tonic-gate (void) sigset(SIGCLD, childeath); 37980Sstevel@tonic-gate } 37990Sstevel@tonic-gate 38000Sstevel@tonic-gate 38010Sstevel@tonic-gate /* 38020Sstevel@tonic-gate * /etc/inittab has more entries and we have run out of room in the proc_table 38030Sstevel@tonic-gate * array. Double the size of proc_table to accomodate the extra entries. 38040Sstevel@tonic-gate */ 38050Sstevel@tonic-gate static void 38060Sstevel@tonic-gate increase_proc_table_size() 38070Sstevel@tonic-gate { 38080Sstevel@tonic-gate sigset_t block, unblock; 38090Sstevel@tonic-gate void *ptr; 38100Sstevel@tonic-gate size_t delta = num_proc * sizeof (struct PROC_TABLE); 38110Sstevel@tonic-gate 38120Sstevel@tonic-gate 38130Sstevel@tonic-gate /* 38140Sstevel@tonic-gate * Block signals for realloc. 38150Sstevel@tonic-gate */ 38160Sstevel@tonic-gate (void) sigfillset(&block); 38170Sstevel@tonic-gate (void) sigprocmask(SIG_BLOCK, &block, &unblock); 38180Sstevel@tonic-gate 38190Sstevel@tonic-gate 38200Sstevel@tonic-gate /* 38210Sstevel@tonic-gate * On failure we just return because callers of this function check 38220Sstevel@tonic-gate * for failure. 38230Sstevel@tonic-gate */ 38240Sstevel@tonic-gate do 38250Sstevel@tonic-gate ptr = realloc(g_state, g_state_sz + delta); 38260Sstevel@tonic-gate while (ptr == NULL && errno == EAGAIN); 38270Sstevel@tonic-gate 38280Sstevel@tonic-gate if (ptr != NULL) { 38290Sstevel@tonic-gate /* ensure that the new part is initialized to zero */ 38300Sstevel@tonic-gate bzero((caddr_t)ptr + g_state_sz, delta); 38310Sstevel@tonic-gate 38320Sstevel@tonic-gate g_state = ptr; 38330Sstevel@tonic-gate g_state_sz += delta; 38340Sstevel@tonic-gate num_proc <<= 1; 38350Sstevel@tonic-gate } 38360Sstevel@tonic-gate 38370Sstevel@tonic-gate 38380Sstevel@tonic-gate /* unblock our signals before returning */ 38390Sstevel@tonic-gate (void) sigprocmask(SIG_SETMASK, &unblock, NULL); 38400Sstevel@tonic-gate } 38410Sstevel@tonic-gate 38420Sstevel@tonic-gate 38430Sstevel@tonic-gate 38440Sstevel@tonic-gate /* 38450Sstevel@tonic-gate * Sanity check g_state. 38460Sstevel@tonic-gate */ 38470Sstevel@tonic-gate static int 38480Sstevel@tonic-gate st_sane() 38490Sstevel@tonic-gate { 38500Sstevel@tonic-gate int i; 38510Sstevel@tonic-gate struct PROC_TABLE *ptp; 38520Sstevel@tonic-gate 38530Sstevel@tonic-gate 38540Sstevel@tonic-gate /* Note: cur_state is encoded as a signal number */ 38550Sstevel@tonic-gate if (cur_state < 1 || cur_state == 9 || cur_state > 13) 38560Sstevel@tonic-gate return (0); 38570Sstevel@tonic-gate 38580Sstevel@tonic-gate /* Check num_proc */ 38590Sstevel@tonic-gate if (g_state_sz != sizeof (struct init_state) + (num_proc - 1) * 38600Sstevel@tonic-gate sizeof (struct PROC_TABLE)) 38610Sstevel@tonic-gate return (0); 38620Sstevel@tonic-gate 38630Sstevel@tonic-gate /* Check proc_table */ 38640Sstevel@tonic-gate for (i = 0, ptp = proc_table; i < num_proc; ++i, ++ptp) { 38650Sstevel@tonic-gate /* skip unoccupied entries */ 38660Sstevel@tonic-gate if (!(ptp->p_flags & OCCUPIED)) 38670Sstevel@tonic-gate continue; 38680Sstevel@tonic-gate 38690Sstevel@tonic-gate /* p_flags has no bits outside of PF_MASK */ 38700Sstevel@tonic-gate if (ptp->p_flags & ~(PF_MASK)) 38710Sstevel@tonic-gate return (0); 38720Sstevel@tonic-gate 38730Sstevel@tonic-gate /* 5 <= pid <= MAXPID */ 38740Sstevel@tonic-gate if (ptp->p_pid < 5 || ptp->p_pid > MAXPID) 38750Sstevel@tonic-gate return (0); 38760Sstevel@tonic-gate 38770Sstevel@tonic-gate /* p_count >= 0 */ 38780Sstevel@tonic-gate if (ptp->p_count < 0) 38790Sstevel@tonic-gate return (0); 38800Sstevel@tonic-gate 38810Sstevel@tonic-gate /* p_time >= 0 */ 38820Sstevel@tonic-gate if (ptp->p_time < 0) 38830Sstevel@tonic-gate return (0); 38840Sstevel@tonic-gate } 38850Sstevel@tonic-gate 38860Sstevel@tonic-gate return (1); 38870Sstevel@tonic-gate } 38880Sstevel@tonic-gate 38890Sstevel@tonic-gate /* 38900Sstevel@tonic-gate * Initialize our state. 38910Sstevel@tonic-gate * 38920Sstevel@tonic-gate * If the system just booted, then init_state_file, which is located on an 38930Sstevel@tonic-gate * everpresent tmpfs filesystem, should not exist. 38940Sstevel@tonic-gate * 38950Sstevel@tonic-gate * If we were restarted, then init_state_file should exist, in 38960Sstevel@tonic-gate * which case we'll read it in, sanity check it, and use it. 38970Sstevel@tonic-gate * 38980Sstevel@tonic-gate * Note: You can't call console() until proc_table is ready. 38990Sstevel@tonic-gate */ 39000Sstevel@tonic-gate void 39010Sstevel@tonic-gate st_init() 39020Sstevel@tonic-gate { 39030Sstevel@tonic-gate struct stat stb; 39040Sstevel@tonic-gate int ret, st_fd, insane = 0; 39050Sstevel@tonic-gate size_t to_be_read; 39060Sstevel@tonic-gate char *ptr; 39070Sstevel@tonic-gate 39080Sstevel@tonic-gate 39090Sstevel@tonic-gate booting = 1; 39100Sstevel@tonic-gate 39110Sstevel@tonic-gate do { 39120Sstevel@tonic-gate /* 39130Sstevel@tonic-gate * If we can exclusively create the file, then we're the 39140Sstevel@tonic-gate * initial invocation of init(1M). 39150Sstevel@tonic-gate */ 39160Sstevel@tonic-gate st_fd = open(init_state_file, O_RDWR | O_CREAT | O_EXCL, 39170Sstevel@tonic-gate S_IRUSR | S_IWUSR); 39180Sstevel@tonic-gate } while (st_fd == -1 && errno == EINTR); 39190Sstevel@tonic-gate if (st_fd != -1) 39200Sstevel@tonic-gate goto new_state; 39210Sstevel@tonic-gate 39220Sstevel@tonic-gate booting = 0; 39230Sstevel@tonic-gate 39240Sstevel@tonic-gate do { 39250Sstevel@tonic-gate st_fd = open(init_state_file, O_RDWR, S_IRUSR | S_IWUSR); 39260Sstevel@tonic-gate } while (st_fd == -1 && errno == EINTR); 39270Sstevel@tonic-gate if (st_fd == -1) 39280Sstevel@tonic-gate goto new_state; 39290Sstevel@tonic-gate 39300Sstevel@tonic-gate /* Get the size of the file. */ 39310Sstevel@tonic-gate do 39320Sstevel@tonic-gate ret = fstat(st_fd, &stb); 39330Sstevel@tonic-gate while (ret == -1 && errno == EINTR); 39340Sstevel@tonic-gate if (ret == -1) 39350Sstevel@tonic-gate goto new_state; 39360Sstevel@tonic-gate 39370Sstevel@tonic-gate do 39380Sstevel@tonic-gate g_state = malloc(stb.st_size); 39390Sstevel@tonic-gate while (g_state == NULL && errno == EAGAIN); 39400Sstevel@tonic-gate if (g_state == NULL) 39410Sstevel@tonic-gate goto new_state; 39420Sstevel@tonic-gate 39430Sstevel@tonic-gate to_be_read = stb.st_size; 39440Sstevel@tonic-gate ptr = (char *)g_state; 39450Sstevel@tonic-gate while (to_be_read > 0) { 39460Sstevel@tonic-gate ssize_t read_ret; 39470Sstevel@tonic-gate 39480Sstevel@tonic-gate read_ret = read(st_fd, ptr, to_be_read); 39490Sstevel@tonic-gate if (read_ret < 0) { 39500Sstevel@tonic-gate if (errno == EINTR) 39510Sstevel@tonic-gate continue; 39520Sstevel@tonic-gate 39530Sstevel@tonic-gate goto new_state; 39540Sstevel@tonic-gate } 39550Sstevel@tonic-gate 39560Sstevel@tonic-gate to_be_read -= read_ret; 39570Sstevel@tonic-gate ptr += read_ret; 39580Sstevel@tonic-gate } 39590Sstevel@tonic-gate 39600Sstevel@tonic-gate (void) close(st_fd); 39610Sstevel@tonic-gate 39620Sstevel@tonic-gate g_state_sz = stb.st_size; 39630Sstevel@tonic-gate 39640Sstevel@tonic-gate if (st_sane()) { 39650Sstevel@tonic-gate console(B_TRUE, "Restarting.\n"); 39660Sstevel@tonic-gate return; 39670Sstevel@tonic-gate } 39680Sstevel@tonic-gate 39690Sstevel@tonic-gate insane = 1; 39700Sstevel@tonic-gate 39710Sstevel@tonic-gate new_state: 39720Sstevel@tonic-gate if (st_fd >= 0) 39730Sstevel@tonic-gate (void) close(st_fd); 39740Sstevel@tonic-gate else 39750Sstevel@tonic-gate (void) unlink(init_state_file); 39760Sstevel@tonic-gate 39770Sstevel@tonic-gate if (g_state != NULL) 39780Sstevel@tonic-gate free(g_state); 39790Sstevel@tonic-gate 39800Sstevel@tonic-gate /* Something went wrong, so allocate new state. */ 39810Sstevel@tonic-gate g_state_sz = sizeof (struct init_state) + 39820Sstevel@tonic-gate ((init_num_proc - 1) * sizeof (struct PROC_TABLE)); 39830Sstevel@tonic-gate do 39840Sstevel@tonic-gate g_state = calloc(1, g_state_sz); 39850Sstevel@tonic-gate while (g_state == NULL && errno == EAGAIN); 39860Sstevel@tonic-gate if (g_state == NULL) { 39870Sstevel@tonic-gate /* Fatal error! */ 39880Sstevel@tonic-gate exit(errno); 39890Sstevel@tonic-gate } 39900Sstevel@tonic-gate 39910Sstevel@tonic-gate g_state->ist_runlevel = -1; 39920Sstevel@tonic-gate num_proc = init_num_proc; 39930Sstevel@tonic-gate 39940Sstevel@tonic-gate if (!booting) { 39950Sstevel@tonic-gate console(B_TRUE, "Restarting.\n"); 39960Sstevel@tonic-gate 39970Sstevel@tonic-gate /* Overwrite the bad state file. */ 39980Sstevel@tonic-gate st_write(); 39990Sstevel@tonic-gate 40000Sstevel@tonic-gate if (!insane) { 40010Sstevel@tonic-gate console(B_TRUE, 40020Sstevel@tonic-gate "Error accessing persistent state file `%s'. " 40030Sstevel@tonic-gate "Ignored.\n", init_state_file); 40040Sstevel@tonic-gate } else { 40050Sstevel@tonic-gate console(B_TRUE, 40060Sstevel@tonic-gate "Persistent state file `%s' is invalid and was " 40070Sstevel@tonic-gate "ignored.\n", init_state_file); 40080Sstevel@tonic-gate } 40090Sstevel@tonic-gate } 40100Sstevel@tonic-gate } 40110Sstevel@tonic-gate 40120Sstevel@tonic-gate /* 40130Sstevel@tonic-gate * Write g_state out to the state file. 40140Sstevel@tonic-gate */ 40150Sstevel@tonic-gate void 40160Sstevel@tonic-gate st_write() 40170Sstevel@tonic-gate { 40180Sstevel@tonic-gate static int complained = 0; 40190Sstevel@tonic-gate 40200Sstevel@tonic-gate int st_fd; 40210Sstevel@tonic-gate char *cp; 40220Sstevel@tonic-gate size_t sz; 40230Sstevel@tonic-gate ssize_t ret; 40240Sstevel@tonic-gate 40250Sstevel@tonic-gate 40260Sstevel@tonic-gate do { 40270Sstevel@tonic-gate st_fd = open(init_next_state_file, 40280Sstevel@tonic-gate O_WRONLY | O_CREAT | O_TRUNC, S_IRUSR | S_IWUSR); 40290Sstevel@tonic-gate } while (st_fd < 0 && errno == EINTR); 40300Sstevel@tonic-gate if (st_fd < 0) 40310Sstevel@tonic-gate goto err; 40320Sstevel@tonic-gate 40330Sstevel@tonic-gate cp = (char *)g_state; 40340Sstevel@tonic-gate sz = g_state_sz; 40350Sstevel@tonic-gate while (sz > 0) { 40360Sstevel@tonic-gate ret = write(st_fd, cp, sz); 40370Sstevel@tonic-gate if (ret < 0) { 40380Sstevel@tonic-gate if (errno == EINTR) 40390Sstevel@tonic-gate continue; 40400Sstevel@tonic-gate 40410Sstevel@tonic-gate goto err; 40420Sstevel@tonic-gate } 40430Sstevel@tonic-gate 40440Sstevel@tonic-gate sz -= ret; 40450Sstevel@tonic-gate cp += ret; 40460Sstevel@tonic-gate } 40470Sstevel@tonic-gate 40480Sstevel@tonic-gate (void) close(st_fd); 40490Sstevel@tonic-gate st_fd = -1; 40500Sstevel@tonic-gate if (rename(init_next_state_file, init_state_file)) { 40510Sstevel@tonic-gate (void) unlink(init_next_state_file); 40520Sstevel@tonic-gate goto err; 40530Sstevel@tonic-gate } 40540Sstevel@tonic-gate complained = 0; 40550Sstevel@tonic-gate 40560Sstevel@tonic-gate return; 40570Sstevel@tonic-gate 40580Sstevel@tonic-gate err: 40590Sstevel@tonic-gate if (st_fd >= 0) 40600Sstevel@tonic-gate (void) close(st_fd); 40610Sstevel@tonic-gate 40620Sstevel@tonic-gate if (!booting && !complained) { 40630Sstevel@tonic-gate /* 40640Sstevel@tonic-gate * Only complain after the filesystem should have come up. 40650Sstevel@tonic-gate * And only do it once so we don't loop between console() 40660Sstevel@tonic-gate * & efork(). 40670Sstevel@tonic-gate */ 40680Sstevel@tonic-gate complained = 1; 40690Sstevel@tonic-gate if (st_fd) 40700Sstevel@tonic-gate console(B_TRUE, "Couldn't write persistent state " 40710Sstevel@tonic-gate "file `%s'.\n", init_state_file); 40720Sstevel@tonic-gate else 40730Sstevel@tonic-gate console(B_TRUE, "Couldn't move persistent state " 40740Sstevel@tonic-gate "file `%s' to `%s'.\n", init_next_state_file, 40750Sstevel@tonic-gate init_state_file); 40760Sstevel@tonic-gate } 40770Sstevel@tonic-gate } 40780Sstevel@tonic-gate 40790Sstevel@tonic-gate /* 40800Sstevel@tonic-gate * Create a contract with these parameters. 40810Sstevel@tonic-gate */ 40820Sstevel@tonic-gate static int 40830Sstevel@tonic-gate contract_make_template(uint_t info, uint_t critical, uint_t fatal, 40840Sstevel@tonic-gate uint64_t cookie) 40850Sstevel@tonic-gate { 40860Sstevel@tonic-gate int fd, err; 40870Sstevel@tonic-gate 40880Sstevel@tonic-gate char *ioctl_tset_emsg = 40890Sstevel@tonic-gate "Couldn't set \"%s\" contract template parameter: %s.\n"; 40900Sstevel@tonic-gate 40910Sstevel@tonic-gate do 40920Sstevel@tonic-gate fd = open64(CTFS_ROOT "/process/template", O_RDWR); 40930Sstevel@tonic-gate while (fd < 0 && errno == EINTR); 40940Sstevel@tonic-gate if (fd < 0) { 40950Sstevel@tonic-gate console(B_TRUE, "Couldn't create process template: %s.\n", 40960Sstevel@tonic-gate strerror(errno)); 40970Sstevel@tonic-gate return (-1); 40980Sstevel@tonic-gate } 40990Sstevel@tonic-gate 41000Sstevel@tonic-gate if (err = ct_pr_tmpl_set_param(fd, CT_PR_INHERIT | CT_PR_REGENT)) 41010Sstevel@tonic-gate console(B_TRUE, "Contract set template inherit, regent " 41026073Sacruz "failed: %s.\n", strerror(err)); 41030Sstevel@tonic-gate 41040Sstevel@tonic-gate /* 41050Sstevel@tonic-gate * These errors result in a misconfigured template, which is better 41060Sstevel@tonic-gate * than no template at all, so warn but don't abort. 41070Sstevel@tonic-gate */ 41080Sstevel@tonic-gate if (err = ct_tmpl_set_informative(fd, info)) 41090Sstevel@tonic-gate console(B_TRUE, ioctl_tset_emsg, "informative", strerror(err)); 41100Sstevel@tonic-gate 41110Sstevel@tonic-gate if (err = ct_tmpl_set_critical(fd, critical)) 41120Sstevel@tonic-gate console(B_TRUE, ioctl_tset_emsg, "critical", strerror(err)); 41130Sstevel@tonic-gate 41140Sstevel@tonic-gate if (err = ct_pr_tmpl_set_fatal(fd, fatal)) 41150Sstevel@tonic-gate console(B_TRUE, ioctl_tset_emsg, "fatal", strerror(err)); 41160Sstevel@tonic-gate 41170Sstevel@tonic-gate if (err = ct_tmpl_set_cookie(fd, cookie)) 41180Sstevel@tonic-gate console(B_TRUE, ioctl_tset_emsg, "cookie", strerror(err)); 41190Sstevel@tonic-gate 41200Sstevel@tonic-gate (void) fcntl(fd, F_SETFD, FD_CLOEXEC); 41210Sstevel@tonic-gate 41220Sstevel@tonic-gate return (fd); 41230Sstevel@tonic-gate } 41240Sstevel@tonic-gate 41250Sstevel@tonic-gate /* 41260Sstevel@tonic-gate * Create the templates and open an event file descriptor. We use dup2(2) to 41270Sstevel@tonic-gate * get these descriptors away from the stdin/stdout/stderr group. 41280Sstevel@tonic-gate */ 41290Sstevel@tonic-gate static void 41300Sstevel@tonic-gate contracts_init() 41310Sstevel@tonic-gate { 41320Sstevel@tonic-gate int err, fd; 41330Sstevel@tonic-gate 41340Sstevel@tonic-gate /* 41350Sstevel@tonic-gate * Create & configure a legacy template. We only want empty events so 41360Sstevel@tonic-gate * we know when to abandon them. 41370Sstevel@tonic-gate */ 41380Sstevel@tonic-gate legacy_tmpl = contract_make_template(0, CT_PR_EV_EMPTY, CT_PR_EV_HWERR, 41390Sstevel@tonic-gate ORDINARY_COOKIE); 41400Sstevel@tonic-gate if (legacy_tmpl >= 0) { 41410Sstevel@tonic-gate err = ct_tmpl_activate(legacy_tmpl); 41420Sstevel@tonic-gate if (err != 0) { 41430Sstevel@tonic-gate (void) close(legacy_tmpl); 41440Sstevel@tonic-gate legacy_tmpl = -1; 41450Sstevel@tonic-gate console(B_TRUE, 41460Sstevel@tonic-gate "Couldn't activate legacy template (%s); " 41470Sstevel@tonic-gate "legacy services will be in init's contract.\n", 41480Sstevel@tonic-gate strerror(err)); 41490Sstevel@tonic-gate } 41500Sstevel@tonic-gate } else 41510Sstevel@tonic-gate console(B_TRUE, 41520Sstevel@tonic-gate "Legacy services will be in init's contract.\n"); 41530Sstevel@tonic-gate 41540Sstevel@tonic-gate if (dup2(legacy_tmpl, 255) == -1) { 41550Sstevel@tonic-gate console(B_TRUE, "Could not duplicate legacy template: %s.\n", 41560Sstevel@tonic-gate strerror(errno)); 41570Sstevel@tonic-gate } else { 41580Sstevel@tonic-gate (void) close(legacy_tmpl); 41590Sstevel@tonic-gate legacy_tmpl = 255; 41600Sstevel@tonic-gate } 41610Sstevel@tonic-gate 41620Sstevel@tonic-gate (void) fcntl(legacy_tmpl, F_SETFD, FD_CLOEXEC); 41630Sstevel@tonic-gate 41640Sstevel@tonic-gate startd_tmpl = contract_make_template(0, CT_PR_EV_EMPTY, 41650Sstevel@tonic-gate CT_PR_EV_HWERR | CT_PR_EV_SIGNAL | CT_PR_EV_CORE, STARTD_COOKIE); 41660Sstevel@tonic-gate 41670Sstevel@tonic-gate if (dup2(startd_tmpl, 254) == -1) { 41680Sstevel@tonic-gate console(B_TRUE, "Could not duplicate startd template: %s.\n", 41690Sstevel@tonic-gate strerror(errno)); 41700Sstevel@tonic-gate } else { 41710Sstevel@tonic-gate (void) close(startd_tmpl); 41720Sstevel@tonic-gate startd_tmpl = 254; 41730Sstevel@tonic-gate } 41740Sstevel@tonic-gate 41750Sstevel@tonic-gate (void) fcntl(startd_tmpl, F_SETFD, FD_CLOEXEC); 41760Sstevel@tonic-gate 41770Sstevel@tonic-gate if (legacy_tmpl < 0 && startd_tmpl < 0) { 41780Sstevel@tonic-gate /* The creation errors have already been reported. */ 41790Sstevel@tonic-gate console(B_TRUE, 41800Sstevel@tonic-gate "Ignoring contract events. Core smf(5) services will not " 41810Sstevel@tonic-gate "be restarted.\n"); 41820Sstevel@tonic-gate return; 41830Sstevel@tonic-gate } 41840Sstevel@tonic-gate 41850Sstevel@tonic-gate /* 41860Sstevel@tonic-gate * Open an event endpoint. 41870Sstevel@tonic-gate */ 41880Sstevel@tonic-gate do 41890Sstevel@tonic-gate fd = open64(CTFS_ROOT "/process/pbundle", O_RDONLY); 41900Sstevel@tonic-gate while (fd < 0 && errno == EINTR); 41910Sstevel@tonic-gate if (fd < 0) { 41920Sstevel@tonic-gate console(B_TRUE, 41930Sstevel@tonic-gate "Couldn't open process pbundle: %s. Core smf(5) services " 41940Sstevel@tonic-gate "will not be restarted.\n", strerror(errno)); 41950Sstevel@tonic-gate return; 41960Sstevel@tonic-gate } 41970Sstevel@tonic-gate 41980Sstevel@tonic-gate if (dup2(fd, 253) == -1) { 41990Sstevel@tonic-gate console(B_TRUE, "Could not duplicate process bundle: %s.\n", 42000Sstevel@tonic-gate strerror(errno)); 42010Sstevel@tonic-gate } else { 42020Sstevel@tonic-gate (void) close(fd); 42030Sstevel@tonic-gate fd = 253; 42040Sstevel@tonic-gate } 42050Sstevel@tonic-gate 42060Sstevel@tonic-gate (void) fcntl(fd, F_SETFD, FD_CLOEXEC); 42070Sstevel@tonic-gate 42080Sstevel@tonic-gate /* Reset in case we've been restarted. */ 42090Sstevel@tonic-gate (void) ct_event_reset(fd); 42100Sstevel@tonic-gate 42110Sstevel@tonic-gate poll_fds[0].fd = fd; 42120Sstevel@tonic-gate poll_fds[0].events = POLLIN; 42130Sstevel@tonic-gate poll_nfds = 1; 42140Sstevel@tonic-gate } 42150Sstevel@tonic-gate 42160Sstevel@tonic-gate static int 42170Sstevel@tonic-gate contract_getfile(ctid_t id, const char *name, int oflag) 42180Sstevel@tonic-gate { 42190Sstevel@tonic-gate int fd; 42200Sstevel@tonic-gate 42210Sstevel@tonic-gate do 42220Sstevel@tonic-gate fd = contract_open(id, "process", name, oflag); 42230Sstevel@tonic-gate while (fd < 0 && errno == EINTR); 42240Sstevel@tonic-gate 42250Sstevel@tonic-gate if (fd < 0) 42260Sstevel@tonic-gate console(B_TRUE, "Couldn't open %s for contract %ld: %s.\n", 42270Sstevel@tonic-gate name, id, strerror(errno)); 42280Sstevel@tonic-gate 42290Sstevel@tonic-gate return (fd); 42300Sstevel@tonic-gate } 42310Sstevel@tonic-gate 42320Sstevel@tonic-gate static int 42330Sstevel@tonic-gate contract_cookie(ctid_t id, uint64_t *cp) 42340Sstevel@tonic-gate { 42350Sstevel@tonic-gate int fd, err; 42360Sstevel@tonic-gate ct_stathdl_t sh; 42370Sstevel@tonic-gate 42380Sstevel@tonic-gate fd = contract_getfile(id, "status", O_RDONLY); 42390Sstevel@tonic-gate if (fd < 0) 42400Sstevel@tonic-gate return (-1); 42410Sstevel@tonic-gate 42420Sstevel@tonic-gate err = ct_status_read(fd, CTD_COMMON, &sh); 42430Sstevel@tonic-gate if (err != 0) { 42440Sstevel@tonic-gate console(B_TRUE, "Couldn't read status of contract %ld: %s.\n", 42450Sstevel@tonic-gate id, strerror(err)); 42460Sstevel@tonic-gate (void) close(fd); 42470Sstevel@tonic-gate return (-1); 42480Sstevel@tonic-gate } 42490Sstevel@tonic-gate 42500Sstevel@tonic-gate (void) close(fd); 42510Sstevel@tonic-gate 42520Sstevel@tonic-gate *cp = ct_status_get_cookie(sh); 42530Sstevel@tonic-gate 42540Sstevel@tonic-gate ct_status_free(sh); 42550Sstevel@tonic-gate return (0); 42560Sstevel@tonic-gate } 42570Sstevel@tonic-gate 42580Sstevel@tonic-gate static void 42590Sstevel@tonic-gate contract_ack(ct_evthdl_t e) 42600Sstevel@tonic-gate { 42610Sstevel@tonic-gate int fd; 42620Sstevel@tonic-gate 42630Sstevel@tonic-gate if (ct_event_get_flags(e) & CTE_INFO) 42640Sstevel@tonic-gate return; 42650Sstevel@tonic-gate 42660Sstevel@tonic-gate fd = contract_getfile(ct_event_get_ctid(e), "ctl", O_WRONLY); 42670Sstevel@tonic-gate if (fd < 0) 42680Sstevel@tonic-gate return; 42690Sstevel@tonic-gate 42700Sstevel@tonic-gate (void) ct_ctl_ack(fd, ct_event_get_evid(e)); 42710Sstevel@tonic-gate (void) close(fd); 42720Sstevel@tonic-gate } 42730Sstevel@tonic-gate 42740Sstevel@tonic-gate /* 42750Sstevel@tonic-gate * Process a contract event. 42760Sstevel@tonic-gate */ 42770Sstevel@tonic-gate static void 42780Sstevel@tonic-gate contract_event(struct pollfd *poll) 42790Sstevel@tonic-gate { 42800Sstevel@tonic-gate ct_evthdl_t e; 42810Sstevel@tonic-gate int err; 42820Sstevel@tonic-gate ctid_t ctid; 42830Sstevel@tonic-gate 42840Sstevel@tonic-gate if (!(poll->revents & POLLIN)) { 42850Sstevel@tonic-gate if (poll->revents & POLLERR) 42860Sstevel@tonic-gate console(B_TRUE, 42870Sstevel@tonic-gate "Unknown poll error on my process contract " 42880Sstevel@tonic-gate "pbundle.\n"); 42890Sstevel@tonic-gate return; 42900Sstevel@tonic-gate } 42910Sstevel@tonic-gate 42920Sstevel@tonic-gate err = ct_event_read(poll->fd, &e); 42930Sstevel@tonic-gate if (err != 0) { 42940Sstevel@tonic-gate console(B_TRUE, "Error retrieving contract event: %s.\n", 42950Sstevel@tonic-gate strerror(err)); 42960Sstevel@tonic-gate return; 42970Sstevel@tonic-gate } 42980Sstevel@tonic-gate 42990Sstevel@tonic-gate ctid = ct_event_get_ctid(e); 43000Sstevel@tonic-gate 43010Sstevel@tonic-gate if (ct_event_get_type(e) == CT_PR_EV_EMPTY) { 43020Sstevel@tonic-gate uint64_t cookie; 43030Sstevel@tonic-gate int ret, abandon = 1; 43040Sstevel@tonic-gate 43050Sstevel@tonic-gate /* If it's svc.startd, restart it. Else, abandon. */ 43060Sstevel@tonic-gate ret = contract_cookie(ctid, &cookie); 43070Sstevel@tonic-gate 43080Sstevel@tonic-gate if (ret == 0) { 43090Sstevel@tonic-gate if (cookie == STARTD_COOKIE && 43100Sstevel@tonic-gate do_restart_startd) { 43110Sstevel@tonic-gate if (smf_debug) 43120Sstevel@tonic-gate console(B_TRUE, "Restarting " 43130Sstevel@tonic-gate "svc.startd.\n"); 43140Sstevel@tonic-gate 43150Sstevel@tonic-gate /* 43160Sstevel@tonic-gate * Account for the failure. If the failure rate 43170Sstevel@tonic-gate * exceeds a threshold, then drop to maintenance 43180Sstevel@tonic-gate * mode. 43190Sstevel@tonic-gate */ 43200Sstevel@tonic-gate startd_record_failure(); 43210Sstevel@tonic-gate if (startd_failure_rate_critical()) 43220Sstevel@tonic-gate enter_maintenance(); 43230Sstevel@tonic-gate 43240Sstevel@tonic-gate if (startd_tmpl < 0) 43250Sstevel@tonic-gate console(B_TRUE, 43260Sstevel@tonic-gate "Restarting svc.startd in " 43270Sstevel@tonic-gate "improper contract (bad " 43280Sstevel@tonic-gate "template).\n"); 43290Sstevel@tonic-gate 43300Sstevel@tonic-gate (void) startd_run(startd_cline, startd_tmpl, 43310Sstevel@tonic-gate ctid); 43320Sstevel@tonic-gate 43330Sstevel@tonic-gate abandon = 0; 43340Sstevel@tonic-gate } 43350Sstevel@tonic-gate } 43360Sstevel@tonic-gate 43370Sstevel@tonic-gate if (abandon && (err = contract_abandon_id(ctid))) { 43380Sstevel@tonic-gate console(B_TRUE, "Couldn't abandon contract %ld: %s.\n", 43390Sstevel@tonic-gate ctid, strerror(err)); 43400Sstevel@tonic-gate } 43410Sstevel@tonic-gate 43420Sstevel@tonic-gate /* 43430Sstevel@tonic-gate * No need to acknowledge the event since either way the 43440Sstevel@tonic-gate * originating contract should be abandoned. 43450Sstevel@tonic-gate */ 43460Sstevel@tonic-gate } else { 43470Sstevel@tonic-gate console(B_TRUE, 43480Sstevel@tonic-gate "Received contract event of unexpected type %d from " 43490Sstevel@tonic-gate "contract %ld.\n", ct_event_get_type(e), ctid); 43500Sstevel@tonic-gate 43510Sstevel@tonic-gate if ((ct_event_get_flags(e) & (CTE_INFO | CTE_ACK)) == 0) 43520Sstevel@tonic-gate /* Allow unexpected critical events to be released. */ 43530Sstevel@tonic-gate contract_ack(e); 43540Sstevel@tonic-gate } 43550Sstevel@tonic-gate 43560Sstevel@tonic-gate ct_event_free(e); 43570Sstevel@tonic-gate } 43580Sstevel@tonic-gate 43590Sstevel@tonic-gate /* 43600Sstevel@tonic-gate * svc.startd(1M) Management 43610Sstevel@tonic-gate */ 43620Sstevel@tonic-gate 43630Sstevel@tonic-gate /* 43640Sstevel@tonic-gate * (Re)start svc.startd(1M). old_ctid should be the contract ID of the old 43650Sstevel@tonic-gate * contract, or 0 if we're starting it for the first time. If wait is true 43660Sstevel@tonic-gate * we'll wait for and return the exit value of the child. 43670Sstevel@tonic-gate */ 43680Sstevel@tonic-gate static int 43690Sstevel@tonic-gate startd_run(const char *cline, int tmpl, ctid_t old_ctid) 43700Sstevel@tonic-gate { 43710Sstevel@tonic-gate int err, i, ret, did_activate; 43720Sstevel@tonic-gate pid_t pid; 43730Sstevel@tonic-gate struct stat sb; 43740Sstevel@tonic-gate 43750Sstevel@tonic-gate if (cline[0] == '\0') 43760Sstevel@tonic-gate return (-1); 43770Sstevel@tonic-gate 43780Sstevel@tonic-gate /* 43790Sstevel@tonic-gate * Don't restart startd if the system is rebooting or shutting down. 43800Sstevel@tonic-gate */ 43810Sstevel@tonic-gate do { 43820Sstevel@tonic-gate ret = stat("/etc/svc/volatile/resetting", &sb); 43830Sstevel@tonic-gate } while (ret == -1 && errno == EINTR); 43840Sstevel@tonic-gate 43850Sstevel@tonic-gate if (ret == 0) { 43860Sstevel@tonic-gate if (smf_debug) 43870Sstevel@tonic-gate console(B_TRUE, "Quiescing for reboot.\n"); 43880Sstevel@tonic-gate (void) pause(); 43890Sstevel@tonic-gate return (-1); 43900Sstevel@tonic-gate } 43910Sstevel@tonic-gate 43920Sstevel@tonic-gate err = ct_pr_tmpl_set_transfer(tmpl, old_ctid); 43930Sstevel@tonic-gate if (err == EINVAL) { 43940Sstevel@tonic-gate console(B_TRUE, "Remake startd_tmpl; reattempt transfer.\n"); 43950Sstevel@tonic-gate tmpl = startd_tmpl = contract_make_template(0, CT_PR_EV_EMPTY, 43960Sstevel@tonic-gate CT_PR_EV_HWERR, STARTD_COOKIE); 43970Sstevel@tonic-gate 43980Sstevel@tonic-gate err = ct_pr_tmpl_set_transfer(tmpl, old_ctid); 43990Sstevel@tonic-gate } 44000Sstevel@tonic-gate if (err != 0) { 44010Sstevel@tonic-gate console(B_TRUE, 44020Sstevel@tonic-gate "Couldn't set transfer parameter of contract template: " 44030Sstevel@tonic-gate "%s.\n", strerror(err)); 44040Sstevel@tonic-gate } 44050Sstevel@tonic-gate 44066073Sacruz if ((err = ct_pr_tmpl_set_svc_fmri(startd_tmpl, 44076073Sacruz SCF_SERVICE_STARTD)) != 0) 44086073Sacruz console(B_TRUE, 44096073Sacruz "Can not set svc_fmri in contract template: %s\n", 44106073Sacruz strerror(err)); 44116073Sacruz if ((err = ct_pr_tmpl_set_svc_aux(startd_tmpl, 44126073Sacruz startd_svc_aux)) != 0) 44136073Sacruz console(B_TRUE, 44146073Sacruz "Can not set svc_aux in contract template: %s\n", 44156073Sacruz strerror(err)); 44160Sstevel@tonic-gate did_activate = !(ct_tmpl_activate(tmpl)); 44170Sstevel@tonic-gate if (!did_activate) 44180Sstevel@tonic-gate console(B_TRUE, 44190Sstevel@tonic-gate "Template activation failed; not starting \"%s\" in " 44200Sstevel@tonic-gate "proper contract.\n", cline); 44210Sstevel@tonic-gate 44220Sstevel@tonic-gate /* Hold SIGCHLD so we can wait if necessary. */ 44230Sstevel@tonic-gate (void) sighold(SIGCHLD); 44240Sstevel@tonic-gate 44250Sstevel@tonic-gate while ((pid = fork()) < 0) { 44260Sstevel@tonic-gate if (errno == EPERM) { 44270Sstevel@tonic-gate console(B_TRUE, "Insufficient permission to fork.\n"); 44280Sstevel@tonic-gate 44290Sstevel@tonic-gate /* Now that's a doozy. */ 44300Sstevel@tonic-gate exit(1); 44310Sstevel@tonic-gate } 44320Sstevel@tonic-gate 44330Sstevel@tonic-gate console(B_TRUE, 44340Sstevel@tonic-gate "fork() for svc.startd failed: %s. Will retry in 1 " 44350Sstevel@tonic-gate "second...\n", strerror(errno)); 44360Sstevel@tonic-gate 44370Sstevel@tonic-gate (void) sleep(1); 44380Sstevel@tonic-gate 44390Sstevel@tonic-gate /* Eventually give up? */ 44400Sstevel@tonic-gate } 44410Sstevel@tonic-gate 44420Sstevel@tonic-gate if (pid == 0) { 44430Sstevel@tonic-gate /* child */ 44440Sstevel@tonic-gate 44450Sstevel@tonic-gate /* See the comment in efork() */ 44460Sstevel@tonic-gate for (i = SIGHUP; i <= SIGRTMAX; ++i) { 44470Sstevel@tonic-gate if (i == SIGTTOU || i == SIGTTIN || i == SIGTSTP) 44480Sstevel@tonic-gate (void) sigset(i, SIG_IGN); 44490Sstevel@tonic-gate else 44500Sstevel@tonic-gate (void) sigset(i, SIG_DFL); 44510Sstevel@tonic-gate } 44520Sstevel@tonic-gate 44530Sstevel@tonic-gate if (smf_options != NULL) { 44540Sstevel@tonic-gate /* Put smf_options in the environment. */ 44550Sstevel@tonic-gate glob_envp[glob_envn] = 44560Sstevel@tonic-gate malloc(sizeof ("SMF_OPTIONS=") - 1 + 4457*8735SEnrico.Perla@Sun.COM strlen(smf_options) + 1); 44580Sstevel@tonic-gate 44590Sstevel@tonic-gate if (glob_envp[glob_envn] != NULL) { 44600Sstevel@tonic-gate /* LINTED */ 44610Sstevel@tonic-gate (void) sprintf(glob_envp[glob_envn], 44620Sstevel@tonic-gate "SMF_OPTIONS=%s", smf_options); 44630Sstevel@tonic-gate glob_envp[glob_envn+1] = NULL; 44640Sstevel@tonic-gate } else { 44650Sstevel@tonic-gate console(B_TRUE, 44660Sstevel@tonic-gate "Could not set SMF_OPTIONS (%s).\n", 44670Sstevel@tonic-gate strerror(errno)); 44680Sstevel@tonic-gate } 44690Sstevel@tonic-gate } 44700Sstevel@tonic-gate 44710Sstevel@tonic-gate if (smf_debug) 44720Sstevel@tonic-gate console(B_TRUE, "Executing svc.startd\n"); 44730Sstevel@tonic-gate 44740Sstevel@tonic-gate (void) execle(SH, "INITSH", "-c", cline, NULL, glob_envp); 44750Sstevel@tonic-gate 44760Sstevel@tonic-gate console(B_TRUE, "Could not exec \"%s\" (%s).\n", SH, 44770Sstevel@tonic-gate strerror(errno)); 44780Sstevel@tonic-gate 44790Sstevel@tonic-gate exit(1); 44800Sstevel@tonic-gate } 44810Sstevel@tonic-gate 44820Sstevel@tonic-gate /* parent */ 44830Sstevel@tonic-gate 44840Sstevel@tonic-gate if (did_activate) { 44850Sstevel@tonic-gate if (legacy_tmpl < 0 || ct_tmpl_activate(legacy_tmpl) != 0) 44860Sstevel@tonic-gate (void) ct_tmpl_clear(tmpl); 44870Sstevel@tonic-gate } 44880Sstevel@tonic-gate 44890Sstevel@tonic-gate /* Clear the old_ctid reference so the kernel can reclaim it. */ 44900Sstevel@tonic-gate if (old_ctid != 0) 44910Sstevel@tonic-gate (void) ct_pr_tmpl_set_transfer(tmpl, 0); 44920Sstevel@tonic-gate 44930Sstevel@tonic-gate (void) sigrelse(SIGCHLD); 44940Sstevel@tonic-gate 44950Sstevel@tonic-gate return (0); 44960Sstevel@tonic-gate } 44970Sstevel@tonic-gate 44980Sstevel@tonic-gate /* 44990Sstevel@tonic-gate * void startd_record_failure(void) 45000Sstevel@tonic-gate * Place the current time in our circular array of svc.startd failures. 45010Sstevel@tonic-gate */ 45020Sstevel@tonic-gate void 45030Sstevel@tonic-gate startd_record_failure() 45040Sstevel@tonic-gate { 45050Sstevel@tonic-gate int index = startd_failure_index++ % NSTARTD_FAILURE_TIMES; 45060Sstevel@tonic-gate 45070Sstevel@tonic-gate startd_failure_time[index] = gethrtime(); 45080Sstevel@tonic-gate } 45090Sstevel@tonic-gate 45100Sstevel@tonic-gate /* 45110Sstevel@tonic-gate * int startd_failure_rate_critical(void) 45120Sstevel@tonic-gate * Return true if the average failure interval is less than the permitted 45130Sstevel@tonic-gate * interval. Implicit success if insufficient measurements for an average 45140Sstevel@tonic-gate * exist. 45150Sstevel@tonic-gate */ 45160Sstevel@tonic-gate int 45170Sstevel@tonic-gate startd_failure_rate_critical() 45180Sstevel@tonic-gate { 45190Sstevel@tonic-gate int n = startd_failure_index; 45200Sstevel@tonic-gate hrtime_t avg_ns = 0; 45210Sstevel@tonic-gate 45220Sstevel@tonic-gate if (startd_failure_index < NSTARTD_FAILURE_TIMES) 45230Sstevel@tonic-gate return (0); 45240Sstevel@tonic-gate 45250Sstevel@tonic-gate avg_ns = 45260Sstevel@tonic-gate (startd_failure_time[(n - 1) % NSTARTD_FAILURE_TIMES] - 45270Sstevel@tonic-gate startd_failure_time[n % NSTARTD_FAILURE_TIMES]) / 45280Sstevel@tonic-gate NSTARTD_FAILURE_TIMES; 45290Sstevel@tonic-gate 45300Sstevel@tonic-gate return (avg_ns < STARTD_FAILURE_RATE_NS); 45310Sstevel@tonic-gate } 45320Sstevel@tonic-gate 45330Sstevel@tonic-gate /* 45340Sstevel@tonic-gate * returns string that must be free'd 45350Sstevel@tonic-gate */ 45360Sstevel@tonic-gate 45370Sstevel@tonic-gate static char 45380Sstevel@tonic-gate *audit_boot_msg() 45390Sstevel@tonic-gate { 45400Sstevel@tonic-gate char *b, *p; 45410Sstevel@tonic-gate char desc[] = "booted"; 45420Sstevel@tonic-gate zoneid_t zid = getzoneid(); 45430Sstevel@tonic-gate 45440Sstevel@tonic-gate b = malloc(sizeof (desc) + MAXNAMELEN + 3); 45450Sstevel@tonic-gate if (b == NULL) 45460Sstevel@tonic-gate return (b); 45470Sstevel@tonic-gate 45480Sstevel@tonic-gate p = b; 45490Sstevel@tonic-gate p += strlcpy(p, desc, sizeof (desc)); 45500Sstevel@tonic-gate if (zid != GLOBAL_ZONEID) { 45510Sstevel@tonic-gate p += strlcpy(p, ": ", 3); 45520Sstevel@tonic-gate (void) getzonenamebyid(zid, p, MAXNAMELEN); 45530Sstevel@tonic-gate } 45540Sstevel@tonic-gate return (b); 45550Sstevel@tonic-gate } 45560Sstevel@tonic-gate 45570Sstevel@tonic-gate /* 45580Sstevel@tonic-gate * Generate AUE_init_solaris audit record. Return 1 if 45590Sstevel@tonic-gate * auditing is enabled in case the caller cares. 45600Sstevel@tonic-gate * 45610Sstevel@tonic-gate * In the case of userint() or a local zone invocation of 45620Sstevel@tonic-gate * one_true_init, the process initially contains the audit 45630Sstevel@tonic-gate * characteristics of the process that invoked init. The first pass 45640Sstevel@tonic-gate * through here uses those characteristics then for the case of 45650Sstevel@tonic-gate * one_true_init in a local zone, clears them so subsequent system 45660Sstevel@tonic-gate * state changes won't be attributed to the person who booted the 45670Sstevel@tonic-gate * zone. 45680Sstevel@tonic-gate */ 45690Sstevel@tonic-gate static int 45700Sstevel@tonic-gate audit_put_record(int pass_fail, int status, char *msg) 45710Sstevel@tonic-gate { 45720Sstevel@tonic-gate adt_session_data_t *ah; 45730Sstevel@tonic-gate adt_event_data_t *event; 45740Sstevel@tonic-gate 45750Sstevel@tonic-gate if (!adt_audit_enabled()) 45760Sstevel@tonic-gate return (0); 45770Sstevel@tonic-gate 45780Sstevel@tonic-gate /* 45790Sstevel@tonic-gate * the PROC_DATA picks up the context to tell whether this is 45800Sstevel@tonic-gate * an attributed record (auid = -2 is unattributed) 45810Sstevel@tonic-gate */ 45820Sstevel@tonic-gate if (adt_start_session(&ah, NULL, ADT_USE_PROC_DATA)) { 45830Sstevel@tonic-gate console(B_TRUE, "audit failure: %s\n", strerror(errno)); 45840Sstevel@tonic-gate return (1); 45850Sstevel@tonic-gate } 45860Sstevel@tonic-gate event = adt_alloc_event(ah, ADT_init_solaris); 45870Sstevel@tonic-gate if (event == NULL) { 45880Sstevel@tonic-gate console(B_TRUE, "audit failure: %s\n", strerror(errno)); 45890Sstevel@tonic-gate (void) adt_end_session(ah); 45900Sstevel@tonic-gate return (1); 45910Sstevel@tonic-gate } 45920Sstevel@tonic-gate event->adt_init_solaris.info = msg; /* NULL is ok here */ 45930Sstevel@tonic-gate 45940Sstevel@tonic-gate if (adt_put_event(event, pass_fail, status)) { 45950Sstevel@tonic-gate console(B_TRUE, "audit failure: %s\n", strerror(errno)); 45960Sstevel@tonic-gate (void) adt_end_session(ah); 45970Sstevel@tonic-gate return (1); 45980Sstevel@tonic-gate } 45990Sstevel@tonic-gate adt_free_event(event); 46000Sstevel@tonic-gate 46010Sstevel@tonic-gate (void) adt_end_session(ah); 46020Sstevel@tonic-gate 46030Sstevel@tonic-gate return (1); 46040Sstevel@tonic-gate } 4605