#include <sys/contract/process.h>
#include <sys/ctfs.h>
#include <sys/stat.h>
#include <sys/statvfs.h>
#include <sys/stropts.h>
#include <sys/systeminfo.h>
#include <sys/time.h>
#include <sys/termios.h>
#include <sys/tty.h>
#include <sys/types.h>
#include <sys/utsname.h>
#include <sys/bootbanner.h>
#include <bsm/adt_event.h>
#include <bsm/libbsm.h>
#include <security/pam_appl.h>
#include <assert.h>
#include <ctype.h>
#include <definit.h>
#include <dirent.h>
#include <errno.h>
#include <fcntl.h>
#include <libcontract.h>
#include <libcontract_priv.h>
#include <libintl.h>
#include <libscf.h>
#include <libscf_priv.h>
#include <poll.h>
#include <procfs.h>
#include <signal.h>
#include <stdarg.h>
#include <stdio.h>
#include <stdio_ext.h>
#include <stdlib.h>
#include <string.h>
#include <strings.h>
#include <syslog.h>
#include <time.h>
#include <ulimit.h>
#include <unistd.h>
#include <utmpx.h>
#include <wait.h>
#include <zone.h>
#include <ucontext.h>
#undef sleep
#define fioctl(p, sptr, cmd) ioctl(fileno(p), sptr, cmd)
#define min(a, b) (((a) < (b)) ? (a) : (b))
#define TRUE 1
#define FALSE 0
#define FAILURE -1
#define UT_USER_SZ 32
#define UT_LINE_SZ 32
#define SLEEPTIME (5 * 60)
#define MAXCMDL 512
#define EXEC (sizeof ("exec ") - 1)
#define TWARN 5
#define id_eq(x, y) ((x[0] == y[0] && x[1] == y[1] && x[2] == y[2] &&\
x[3] == y[3]) ? TRUE : FALSE)
static int cmask;
#define LVLQ SIGHUP
#define LVL0 SIGINT
#define LVL1 SIGQUIT
#define LVL2 SIGILL
#define LVL3 SIGTRAP
#define LVL4 SIGIOT
#define LVL5 SIGEMT
#define LVL6 SIGFPE
#define SINGLE_USER SIGBUS
#define LVLa SIGSEGV
#define LVLb SIGSYS
#define LVLc SIGPIPE
#define MASK0 0x0001
#define MASK1 0x0002
#define MASK2 0x0004
#define MASK3 0x0008
#define MASK4 0x0010
#define MASK5 0x0020
#define MASK6 0x0040
#define MASKSU 0x0080
#define MASKa 0x0100
#define MASKb 0x0200
#define MASKc 0x0400
#define MASK_NUMERIC (MASK0 | MASK1 | MASK2 | MASK3 | MASK4 | MASK5 | MASK6)
#define MASK_abc (MASKa | MASKb | MASKc)
#define LSEL_RUNLEVEL 0x0001
typedef struct lvl {
int lvl_state;
int lvl_mask;
char lvl_name;
int lvl_flags;
} lvl_t;
static lvl_t lvls[] = {
{ LVLQ, 0, 'Q', 0 },
{ LVLQ, 0, 'q', 0 },
{ LVL0, MASK0, '0', LSEL_RUNLEVEL },
{ LVL1, MASK1, '1', LSEL_RUNLEVEL },
{ LVL2, MASK2, '2', LSEL_RUNLEVEL },
{ LVL3, MASK3, '3', LSEL_RUNLEVEL },
{ LVL4, MASK4, '4', LSEL_RUNLEVEL },
{ LVL5, MASK5, '5', LSEL_RUNLEVEL },
{ LVL6, MASK6, '6', LSEL_RUNLEVEL },
{ SINGLE_USER, MASKSU, 'S', LSEL_RUNLEVEL },
{ SINGLE_USER, MASKSU, 's', LSEL_RUNLEVEL },
{ LVLa, MASKa, 'a', 0 },
{ LVLb, MASKb, 'b', 0 },
{ LVLc, MASKc, 'c', 0 }
};
#define LVL_NELEMS (sizeof (lvls) / sizeof (lvl_t))
#define OFF 0
#define RESPAWN 1
#define ONDEMAND RESPAWN
#define ONCE 2
#define WAIT 3
#define BOOT 4
#define BOOTWAIT 5
#define POWERFAIL 6
#define POWERWAIT 7
#define INITDEFAULT 8
#define SYSINIT 9
#define M_OFF 0001
#define M_RESPAWN 0002
#define M_ONDEMAND M_RESPAWN
#define M_ONCE 0004
#define M_WAIT 0010
#define M_BOOT 0020
#define M_BOOTWAIT 0040
#define M_PF 0100
#define M_PWAIT 0200
#define M_INITDEFAULT 0400
#define M_SYSINIT 01000
#define ID 1
#define LEVELS 2
#define ACTION 3
#define COMMAND 4
#define COMMENT 5
#define INITTAB_ENTRY_ID_SIZE 4
#define INITTAB_ENTRY_ID_STR_FORMAT "%.4s"
#define NORMAL_MODES (M_OFF | M_RESPAWN | M_ONCE | M_WAIT)
#define BOOT_MODES (M_BOOT | M_BOOTWAIT)
#define PF_MODES (M_PF | M_PWAIT)
struct PROC_TABLE {
char p_id[INITTAB_ENTRY_ID_SIZE];
pid_t p_pid;
short p_count;
long p_time;
short p_flags;
short p_exit;
};
#define OCCUPIED 01
#define LIVING 02
#define NOCLEANUP 04
#define NAMED 010
#define DEMANDREQUEST 020
#define TOUCHED 040
#define WARNED 0100
#define KILLED 0200
#define PF_MASK 0377
#define SPAWN_INTERVAL (2*60)
#define SPAWN_LIMIT 10
#define INHIBIT (5*60)
#define ID_MAX_STR_LEN 10
#define NULLPROC ((struct PROC_TABLE *)(0))
#define NO_ROOM ((struct PROC_TABLE *)(FAILURE))
struct CMD_LINE {
char c_id[INITTAB_ENTRY_ID_SIZE];
short c_levels;
short c_action;
char *c_command;
};
struct pidrec {
int pd_type;
pid_t pd_pid;
};
#define ADDPID 1
#define REMPID 2
static struct pidlist {
pid_t pl_pid;
int pl_dflag;
short pl_exit;
struct pidlist *pl_next;
} *Plhead, *Plfree;
static struct termios dflt_termios = {
.c_iflag = BRKINT|ICRNL|IXON|IMAXBEL,
.c_oflag = OPOST|ONLCR|TAB3,
.c_cflag = CS8|CREAD|B9600,
.c_lflag = ISIG|ICANON|ECHO|ECHOE|ECHOK|ECHOCTL|ECHOKE|IEXTEN,
.c_cc = { CINTR, CQUIT, CERASE, CKILL, CEOF, 0, 0, 0,
CSTART, CSTOP, CSWTCH, CDSUSP, CRPRNT, CFLUSH, CWERASE, CLNEXT,
CSTATUS, CERASE2, 0
}
};
static struct termios stored_syscon_termios;
static int write_ioctl = 0;
static union WAKEUP {
struct WAKEFLAGS {
unsigned w_usersignal : 1;
unsigned w_childdeath : 1;
unsigned w_powerhit : 1;
} w_flags;
int w_mask;
} wakeup;
struct init_state {
int ist_runlevel;
int ist_num_proc;
int ist_utmpx_ok;
struct PROC_TABLE ist_proc_table[1];
};
#define cur_state (g_state->ist_runlevel)
#define num_proc (g_state->ist_num_proc)
#define proc_table (g_state->ist_proc_table)
#define utmpx_ok (g_state->ist_utmpx_ok)
#define ORDINARY_COOKIE 0
#define STARTD_COOKIE 1
#ifndef NDEBUG
#define bad_error(func, err) { \
(void) fprintf(stderr, "%s:%d: %s() failed with unexpected " \
"error %d. Aborting.\n", __FILE__, __LINE__, (func), (err)); \
abort(); \
}
#else
#define bad_error(func, err) abort()
#endif
static char *CONSOLE = "/dev/console";
static char *INITPIPE_DIR = "/var/run";
static char *INITPIPE = "/var/run/initpipe";
#define INIT_STATE_DIR "/etc/svc/volatile"
static const char * const init_state_file = INIT_STATE_DIR "/init.state";
static const char * const init_next_state_file =
INIT_STATE_DIR "/init-next.state";
static const int init_num_proc = 20;
static char *UTMPX = UTMPX_FILE;
static char *WTMPX = WTMPX_FILE;
static char *INITTAB = "/etc/inittab";
static char *SYSTTY = "/dev/systty";
static char *SYSCON = "/dev/syscon";
static char *IOCTLSYSCON = "/etc/ioctl.syscon";
static char *ENVFILE = DEFINIT_DEFAULT_FILE;
static char *SU = "/etc/sulogin";
static char *SH = "/sbin/sh";
#define DEF_PATH "PATH=/usr/sbin:/usr/bin"
#define INIT_PATH "PATH=/sbin:/usr/sbin:/usr/bin"
static int prior_state;
static int prev_state;
static int new_state;
static int lvlq_received;
static int op_modes = BOOT_MODES;
static int Gchild = 0;
static int Pfd = -1;
static unsigned int spawncnt, pausecnt;
static int rsflag;
static volatile int time_up;
static int sflg = 0;
static int rflg = 0;
static int bflg = 0;
static pid_t init_pid;
static struct init_state *g_state = NULL;
static size_t g_state_sz;
static int booting = 1;
#define MAXENVENT 24
static char *glob_envp[MAXENVENT];
static int glob_envn;
static struct pollfd poll_fds[1];
static int poll_nfds = 0;
#define SVC_INIT_PREFIX "init:/"
#define SVC_AUX_SIZE (INITTAB_ENTRY_ID_SIZE + 1)
#define SVC_FMRI_SIZE (sizeof (SVC_INIT_PREFIX) + INITTAB_ENTRY_ID_SIZE)
static int legacy_tmpl = -1;
static int startd_tmpl = -1;
static char startd_svc_aux[SVC_AUX_SIZE];
static char startd_cline[256] = "";
static int do_restart_startd = 1;
static char *smf_options = NULL;
static int smf_debug = 0;
static time_t init_boot_time;
#define NSTARTD_FAILURE_TIMES 3
#define STARTD_FAILURE_RATE_NS 5000000000LL
static hrtime_t startd_failure_time[NSTARTD_FAILURE_TIMES];
static uint_t startd_failure_index;
static char *prog_name(char *);
static int state_to_mask(int);
static int lvlname_to_mask(char, int *);
static void lscf_set_runlevel(char);
static int state_to_flags(int);
static char state_to_name(int);
static int lvlname_to_state(char);
static int getcmd(struct CMD_LINE *, char *);
static int realcon();
static int spawn_processes();
static int get_ioctl_syscon();
static int account(short, struct PROC_TABLE *, char *);
static void alarmclk();
static void childeath(int);
static void cleanaux();
static void clearent(pid_t, short);
static void console(boolean_t, char *, ...);
static void init_signals(void);
static void setup_pipe();
static void killproc(pid_t);
static void init_env();
static void boot_init();
static void powerfail();
static void remv();
static void write_ioctl_syscon();
static void spawn(struct PROC_TABLE *, struct CMD_LINE *);
static void setimer(int);
static void siglvl(int, siginfo_t *, void *);
static void sigpoll(int);
static void enter_maintenance(void);
static void timer(int);
static void userinit(int, char **);
static void notify_pam_dead(struct utmpx *);
static long waitproc(struct PROC_TABLE *);
static struct PROC_TABLE *efork(int, struct PROC_TABLE *, int);
static struct PROC_TABLE *findpslot(struct CMD_LINE *);
static void increase_proc_table_size();
static void st_init();
static void st_write();
static void contracts_init();
static void contract_event(struct pollfd *);
static int startd_run(const char *, int, ctid_t);
static void startd_record_failure();
static int startd_failure_rate_critical();
static char *audit_boot_msg();
static int audit_put_record(int, int, char *);
static void update_boot_archive(int new_state);
static void init_bootbanner_print(const char *, uint_t);
int
main(int argc, char *argv[])
{
int chg_lvl_flag = FALSE, print_banner = FALSE;
int may_need_audit = 1;
int c;
char *msg;
(void) time(&init_boot_time);
cmask = umask(022);
(void) umask(cmask);
opterr = 0;
while ((c = getopt(argc, argv, "brsm:")) != EOF) {
switch (c) {
case 'b':
rflg = 0;
bflg = 1;
if (!sflg)
sflg++;
break;
case 'r':
bflg = 0;
rflg++;
break;
case 's':
if (!bflg)
sflg++;
break;
case 'm':
smf_options = optarg;
smf_debug = (strstr(smf_options, "debug") != NULL);
break;
}
}
if (zone_getattr(getzoneid(), ZONE_ATTR_INITPID, &init_pid,
sizeof (init_pid)) != sizeof (init_pid)) {
(void) fprintf(stderr, "could not get pid for init\n");
return (1);
}
if (getpid() != init_pid) {
userinit(argc, argv);
}
if (getzoneid() != GLOBAL_ZONEID) {
print_banner = TRUE;
}
st_init();
if (booting && print_banner) {
#ifdef LEGACY_BANNER
struct utsname un;
char buf[BUFSIZ];
const char *bits;
int r;
(void) uname(&un);
if ((r = sysinfo(SI_ADDRESS_WIDTH, buf, sizeof (buf))) > 0 &&
r < sizeof (buf)) {
bits = buf;
} else {
bits = "64";
}
console(B_FALSE,
"\n\n%s Release %s Version %s %s-bit\r\n",
un.sysname, un.release, un.version, bits);
console(B_FALSE,
"Copyright (c) 1983, 2010, Oracle and/or its affiliates."
" All rights reserved.\r\n");
#else
bootbanner_print(init_bootbanner_print);
#endif
}
write_ioctl = get_ioctl_syscon();
init_signals();
init_env();
contracts_init();
if (!booting) {
op_modes = NORMAL_MODES;
if (write_ioctl)
write_ioctl_syscon();
} else {
cur_state = 0;
op_modes = BOOT_MODES;
boot_init();
}
prev_state = prior_state = cur_state;
setup_pipe();
for (;;) {
if (lvlq_received) {
setup_pipe();
lvlq_received = B_FALSE;
}
if (Gchild)
cleanaux();
if (op_modes == NORMAL_MODES && cur_state != LVLa &&
cur_state != LVLb && cur_state != LVLc)
remv();
if (chg_lvl_flag) {
chg_lvl_flag = FALSE;
if (state_to_flags(cur_state) & LSEL_RUNLEVEL) {
char rl = state_to_name(cur_state);
if (rl != -1)
lscf_set_runlevel(rl);
}
may_need_audit = 1;
}
if (spawn_processes() == FAILURE) {
prior_state = prev_state;
cur_state = SINGLE_USER;
}
if (rsflag) {
rsflag = 0;
spawncnt++;
}
if (wakeup.w_flags.w_powerhit) {
op_modes = PF_MODES;
prev_state = 0;
} else if (op_modes != NORMAL_MODES) {
if (op_modes == PF_MODES)
prev_state = cur_state;
op_modes = NORMAL_MODES;
} else if (cur_state == LVLa || cur_state == LVLb ||
cur_state == LVLc) {
cur_state = prior_state;
prior_state = prev_state;
prev_state = cur_state;
} else {
prev_state = cur_state;
if (wakeup.w_mask == 0) {
int ret;
if (may_need_audit && (cur_state == LVL3)) {
msg = audit_boot_msg();
may_need_audit = 0;
(void) audit_put_record(ADT_SUCCESS,
ADT_SUCCESS, msg);
free(msg);
}
ret = poll(poll_fds, poll_nfds,
SLEEPTIME * MILLISEC);
pausecnt++;
if (ret > 0)
contract_event(&poll_fds[0]);
else if (ret < 0 && errno != EINTR)
console(B_TRUE, "poll() error: %s\n",
strerror(errno));
}
if (wakeup.w_flags.w_usersignal) {
if (new_state != cur_state) {
if (new_state == LVLa ||
new_state == LVLb ||
new_state == LVLc) {
prev_state = prior_state;
prior_state = cur_state;
cur_state = new_state;
} else {
prev_state = cur_state;
if (cur_state >= 0)
prior_state = cur_state;
cur_state = new_state;
chg_lvl_flag = TRUE;
}
}
new_state = 0;
}
if (wakeup.w_flags.w_powerhit)
op_modes = PF_MODES;
wakeup.w_mask = 0;
}
}
}
static void
init_bootbanner_print(const char *line, uint_t num)
{
const char *pfx = (num == 0) ? "\n\n" : "";
console(B_FALSE, "%s%s\r\n", pfx, line);
}
static void
update_boot_archive(int new_state)
{
if (new_state != LVL0 && new_state != LVL5 && new_state != LVL6)
return;
if (getzoneid() != GLOBAL_ZONEID)
return;
(void) system("/sbin/bootadm -ea update_all");
}
static void
enter_maintenance()
{
struct PROC_TABLE *su_process;
console(B_FALSE, "Requesting maintenance mode\n"
"(See /lib/svc/share/README for additional information.)\n");
(void) sighold(SIGCLD);
while ((su_process = efork(M_OFF, NULLPROC, NOCLEANUP)) == NO_ROOM)
(void) pause();
(void) sigrelse(SIGCLD);
if (su_process == NULLPROC) {
int fd;
(void) fclose(stdin);
(void) fclose(stdout);
(void) fclose(stderr);
closefrom(0);
fd = open(SYSCON, O_RDWR | O_NOCTTY);
if (fd >= 0) {
(void) dup2(fd, 1);
(void) dup2(fd, 2);
} else {
syslog(LOG_CRIT, "init[%d]: cannot open %s; %s\n",
getpid(), SYSCON, strerror(errno));
}
(void) execle(SU, SU, "-", (char *)0, glob_envp);
console(B_TRUE, "execle of %s failed: %s\n", SU,
strerror(errno));
timer(5);
exit(1);
}
while (waitproc(su_process) == FAILURE) {
wakeup.w_mask = 0;
}
}
static void
remv()
{
struct PROC_TABLE *process;
struct CMD_LINE cmd;
char cmd_string[MAXCMDL];
int change_level;
change_level = (cur_state != prev_state ? TRUE : FALSE);
for (process = proc_table;
(process < proc_table + num_proc); process++) {
process->p_flags &= ~TOUCHED;
}
while (getcmd(&cmd, &cmd_string[0]) == TRUE) {
for (process = proc_table;
(process < proc_table + num_proc); process++) {
if ((process->p_flags & OCCUPIED) == 0 ||
!id_eq(process->p_id, cmd.c_id))
continue;
if (cur_state == SINGLE_USER ||
cmd.c_action == M_OFF ||
((cmd.c_levels & state_to_mask(cur_state)) == 0 &&
(process->p_flags & DEMANDREQUEST) == 0)) {
if (process->p_flags & LIVING) {
process->p_flags |= TOUCHED;
if ((process->p_flags & KILLED) == 0) {
if (change_level) {
process->p_flags
|= WARNED;
(void) kill(
process->p_pid,
SIGTERM);
} else {
killproc(
process->p_pid);
}
process->p_flags |= KILLED;
}
}
} else {
if (process->p_flags &
(LIVING|NOCLEANUP|DEMANDREQUEST))
process->p_flags |= TOUCHED;
}
break;
}
}
st_write();
if (change_level) {
setimer(TWARN);
for (process = proc_table;
(process < proc_table + num_proc); process++) {
while (time_up == FALSE &&
(process->p_flags & (WARNED|LIVING|OCCUPIED)) ==
(WARNED|LIVING|OCCUPIED))
(void) pause();
if (time_up == TRUE)
break;
}
if (time_up == TRUE) {
for (process = proc_table;
(process < proc_table + num_proc); process++) {
if ((process->p_flags &
(WARNED|LIVING|OCCUPIED)) ==
(WARNED|LIVING|OCCUPIED))
(void) kill(process->p_pid, SIGKILL);
}
}
setimer(0);
}
for (process = proc_table;
(process < proc_table + num_proc); process++) {
if ((process->p_flags & (LIVING|NAMED|TOUCHED|KILLED|OCCUPIED))
== (LIVING|NAMED|OCCUPIED)) {
killproc(process->p_pid);
process->p_flags |= KILLED;
} else if ((process->p_flags & (LIVING|NAMED|OCCUPIED)) ==
(NAMED|OCCUPIED)) {
(void) account(DEAD_PROCESS, process, NULL);
if ((process->p_flags & TOUCHED) == 0)
process->p_flags = 0;
}
}
st_write();
}
static void
process_startd_line(struct CMD_LINE *cmd, char *cmd_string)
{
size_t sz;
if (sflg || rflg) {
(void) strlcpy(startd_cline, cmd_string, sizeof (startd_cline));
(void) strlcat(startd_cline, " -", sizeof (startd_cline));
if (sflg)
sz = strlcat(startd_cline, "s", sizeof (startd_cline));
if (rflg)
sz = strlcat(startd_cline, "r", sizeof (startd_cline));
} else {
sz = strlcpy(startd_cline, cmd_string, sizeof (startd_cline));
}
if (sz >= sizeof (startd_cline)) {
console(B_TRUE,
"svc.startd command line too long. Ignoring.\n");
startd_cline[0] = '\0';
return;
}
}
static int
spawn_processes()
{
struct PROC_TABLE *pp;
struct CMD_LINE cmd;
char cmd_string[MAXCMDL];
short lvl_mask;
int status;
if (wakeup.w_flags.w_powerhit) {
wakeup.w_flags.w_powerhit = 0;
op_modes = PF_MODES;
}
lvl_mask = state_to_mask(cur_state);
while ((status = getcmd(&cmd, &cmd_string[0])) == TRUE) {
if (id_eq(cmd.c_id, "smf")) {
process_startd_line(&cmd, cmd_string);
continue;
}
retry_for_proc_slot:
if ((pp = findpslot(&cmd)) == NULLPROC) {
increase_proc_table_size();
goto retry_for_proc_slot;
}
if (((pp->p_flags & (LIVING|DEMANDREQUEST)) == DEMANDREQUEST) &&
(cmd.c_levels & MASK_abc) &&
(cmd.c_action & op_modes) == M_ONDEMAND) {
spawn(pp, &cmd);
continue;
}
if ((cmd.c_action & op_modes) == 0 || pp->p_flags & LIVING ||
(cmd.c_levels & lvl_mask) == 0)
continue;
if (op_modes == NORMAL_MODES &&
(cmd.c_action == M_OFF ||
(cmd.c_action & (M_ONCE|M_WAIT)) &&
cur_state == prev_state))
continue;
if (cmd.c_action & (M_ONCE | M_RESPAWN | M_PF | M_BOOT)) {
spawn(pp, &cmd);
} else {
spawn(pp, &cmd);
while (waitproc(pp) == FAILURE)
;
(void) account(DEAD_PROCESS, pp, NULL);
pp->p_flags = 0;
}
}
return (status);
}
static void
spawn(struct PROC_TABLE *process, struct CMD_LINE *cmd)
{
int i;
int modes, maxfiles;
time_t now;
struct PROC_TABLE tmproc, *oprocess;
modes = NAMED;
if (process->p_flags & DEMANDREQUEST || cur_state == LVLa ||
cur_state == LVLb || cur_state == LVLc)
modes |= DEMANDREQUEST;
if ((cmd->c_action & (M_SYSINIT | M_WAIT | M_BOOTWAIT | M_PWAIT)) != 0)
modes |= NOCLEANUP;
if (cmd->c_action & M_RESPAWN) {
modes |= NOCLEANUP;
(void) time(&now);
if (process->p_time == 0L)
process->p_time = now;
if (process->p_count++ == SPAWN_LIMIT) {
if ((now - process->p_time) < SPAWN_INTERVAL) {
console(B_TRUE, "Command is respawning too "
"rapidly. Check for possible errors.\n"
"id:%4s \"%s\"\n",
&cmd->c_id[0], &cmd->c_command[EXEC]);
return;
}
process->p_time = now;
process->p_count = 0;
} else if (process->p_count > SPAWN_LIMIT) {
if (now - process->p_time < SPAWN_INTERVAL + INHIBIT)
return;
process->p_time = now;
process->p_count = 0;
}
rsflag = TRUE;
}
(void) sighold(SIGCLD);
oprocess = process;
while ((process = efork(cmd->c_action, oprocess, modes)) == NO_ROOM)
(void) pause();
if (process == NULLPROC) {
endutxent();
tmproc.p_id[0] = cmd->c_id[0];
tmproc.p_id[1] = cmd->c_id[1];
tmproc.p_id[2] = cmd->c_id[2];
tmproc.p_id[3] = cmd->c_id[3];
tmproc.p_pid = getpid();
tmproc.p_exit = 0;
(void) account(INIT_PROCESS, &tmproc,
prog_name(&cmd->c_command[EXEC]));
maxfiles = ulimit(UL_GDESLIM, 0);
for (i = 0; i < maxfiles; i++)
(void) fcntl(i, F_SETFD, FD_CLOEXEC);
(void) execle(SH, "INITSH", "-c", cmd->c_command, (char *)0,
glob_envp);
console(B_TRUE, "Command\n\"%s\"\n failed to execute. errno "
"= %d (exec of shell failed)\n", cmd->c_command, errno);
timer(20);
exit(1);
}
process->p_id[0] = cmd->c_id[0];
process->p_id[1] = cmd->c_id[1];
process->p_id[2] = cmd->c_id[2];
process->p_id[3] = cmd->c_id[3];
st_write();
(void) sigrelse(SIGCLD);
}
static struct PROC_TABLE *
findpslot(struct CMD_LINE *cmd)
{
struct PROC_TABLE *process;
struct PROC_TABLE *empty = NULLPROC;
for (process = proc_table;
(process < proc_table + num_proc); process++) {
if (process->p_flags & OCCUPIED &&
id_eq(process->p_id, cmd->c_id))
break;
if (empty == NULLPROC && (process->p_flags & OCCUPIED) == 0) {
empty = process;
process->p_id[0] = '\0';
process->p_id[1] = '\0';
process->p_id[2] = '\0';
process->p_id[3] = '\0';
process->p_pid = 0;
process->p_time = 0L;
process->p_count = 0;
process->p_flags = 0;
process->p_exit = 0;
}
}
if (process == (proc_table + num_proc))
process = empty;
return (process);
}
static FILE *fp_inittab = NULL;
static int
getcmd(struct CMD_LINE *cmd, char *shcmd)
{
char *ptr;
int c, lastc, state;
char *ptr1;
int answer, i, proceed;
struct stat sbuf;
static char *actions[] = {
"off", "respawn", "ondemand", "once", "wait", "boot",
"bootwait", "powerfail", "powerwait", "initdefault",
"sysinit",
};
static short act_masks[] = {
M_OFF, M_RESPAWN, M_ONDEMAND, M_ONCE, M_WAIT, M_BOOT,
M_BOOTWAIT, M_PF, M_PWAIT, M_INITDEFAULT, M_SYSINIT,
};
short su_acts = M_INITDEFAULT | M_PF | M_PWAIT | M_WAIT;
if (fp_inittab == NULL) {
for (i = 0; i < 3; i++) {
if (stat(INITTAB, &sbuf) == -1) {
if (i == 2) {
console(B_TRUE,
"Cannot stat %s, errno: %d\n",
INITTAB, errno);
return (FAILURE);
} else {
timer(3);
}
} else if (sbuf.st_size < 10) {
if (i == 2) {
console(B_TRUE,
"%s truncated or corrupted\n",
INITTAB);
return (FAILURE);
} else {
timer(3);
}
} else {
break;
}
}
if ((fp_inittab = fopen(INITTAB, "r")) == NULL) {
console(B_TRUE, "Cannot open %s errno: %d\n", INITTAB,
errno);
return (FAILURE);
}
}
for (answer = FALSE; answer == FALSE; ) {
bzero(cmd, sizeof (struct CMD_LINE));
state = FAILURE;
if ((c = fgetc(fp_inittab)) == EOF) {
answer = FALSE;
(void) fclose(fp_inittab);
fp_inittab = NULL;
break;
}
for (proceed = TRUE, ptr = shcmd, state = ID, lastc = '\0';
proceed && c != EOF;
lastc = c, c = fgetc(fp_inittab)) {
if (state != FAILURE && state != COMMAND) {
if (c == ' ' || c == '\t')
continue;
if (state == COMMENT) {
if (c == '\n') {
lastc = ' ';
break;
} else {
continue;
}
}
if (state == ID && c == '#' && ptr == shcmd) {
state = COMMENT;
continue;
}
if (c == ':') {
switch (state) {
case ID :
if ((i = ptr - shcmd) < 1 || i > 4) {
state = FAILURE;
} else {
bcopy(shcmd, &cmd->c_id[0], i);
ptr = shcmd;
state = LEVELS;
}
break;
case LEVELS :
for (cmd->c_levels = 0, ptr1 = shcmd;
ptr1 < ptr; ptr1++) {
int mask;
if (lvlname_to_mask(*ptr1,
&mask) == -1) {
state = FAILURE;
break;
}
cmd->c_levels |= mask;
}
if (state != FAILURE) {
state = ACTION;
ptr = shcmd;
}
break;
case ACTION :
if (ptr == shcmd) {
if (isdigit(cmd->c_id[0]) &&
(cmd->c_id[1] == '\0' ||
isdigit(cmd->c_id[1])) &&
(cmd->c_id[2] == '\0' ||
isdigit(cmd->c_id[2])) &&
(cmd->c_id[3] == '\0' ||
isdigit(cmd->c_id[3])))
cmd->c_action = M_RESPAWN;
else
cmd->c_action = M_OFF;
} else {
for (cmd->c_action = 0, i = 0,
*ptr = '\0';
i <
sizeof (actions)/sizeof (char *);
i++) {
if (strcmp(shcmd, actions[i]) == 0) {
if ((cmd->c_levels & MASKSU) &&
!(act_masks[i] & su_acts))
cmd->c_action = 0;
else
cmd->c_action =
act_masks[i];
break;
}
}
}
if (cmd->c_action == 0) {
state = FAILURE;
} else {
state = COMMAND;
(void) strcpy(shcmd, "exec ");
}
ptr = shcmd + EXEC;
break;
}
continue;
}
}
if (c == '\n' && lastc != '\\') {
proceed = FALSE;
*ptr = '\0';
break;
}
if (ptr >= shcmd + MAXCMDL - 1)
state = FAILURE;
else
*ptr++ = (char)c;
if (c == '\\' && lastc == '\\')
c = '\0';
}
if (state == COMMAND) {
answer = TRUE;
cmd->c_command = shcmd;
if (cmd->c_levels == 0)
cmd->c_levels = MASK_NUMERIC;
if (cmd->c_action == 0)
cmd->c_action = M_OFF;
if (ptr == shcmd + EXEC)
*shcmd = '\0';
} else
answer = FALSE;
if (c == EOF) {
(void) fclose(fp_inittab);
fp_inittab = NULL;
break;
}
}
return (answer);
}
static int
lvlname_to_state(char name)
{
int i;
for (i = 0; i < LVL_NELEMS; i++) {
if (lvls[i].lvl_name == name)
return (lvls[i].lvl_state);
}
return (-1);
}
static char
state_to_name(int state)
{
int i;
for (i = 0; i < LVL_NELEMS; i++) {
if (lvls[i].lvl_state == state)
return (lvls[i].lvl_name);
}
return (-1);
}
static int
state_to_mask(int state)
{
int i;
for (i = 0; i < LVL_NELEMS; i++) {
if (lvls[i].lvl_state == state)
return (lvls[i].lvl_mask);
}
return (0);
}
static int
lvlname_to_mask(char name, int *mask)
{
int i;
for (i = 0; i < LVL_NELEMS; i++) {
if (lvls[i].lvl_name == name) {
*mask = lvls[i].lvl_mask;
return (0);
}
}
return (-1);
}
static int
state_to_flags(int state)
{
int i;
for (i = 0; i < LVL_NELEMS; i++) {
if (lvls[i].lvl_state == state)
return (lvls[i].lvl_flags);
}
return (0);
}
void
killproc(pid_t pid)
{
struct PROC_TABLE *process;
(void) sighold(SIGCLD);
while ((process = efork(M_OFF, NULLPROC, 0)) == NO_ROOM)
(void) pause();
(void) sigrelse(SIGCLD);
if (process == NULLPROC) {
(void) sigset(SIGALRM, alarmclk);
(void) kill(pid, SIGTERM);
(void) timer(TWARN);
(void) kill(pid, SIGKILL);
(void) exit(0);
}
}
void
init_env()
{
void *dstate;
const char *tokp;
glob_envp[0] = malloc((unsigned)(strlen(DEF_PATH)+2));
(void) strcpy(glob_envp[0], DEF_PATH);
glob_envn = 1;
if (rflg) {
glob_envp[1] =
malloc((unsigned)(strlen("_DVFS_RECONFIG=YES")+2));
(void) strcpy(glob_envp[1], "_DVFS_RECONFIG=YES");
++glob_envn;
} else if (bflg == 1) {
glob_envp[1] =
malloc((unsigned)(strlen("RB_NOBOOTRC=YES")+2));
(void) strcpy(glob_envp[1], "RB_NOBOOTRC=YES");
++glob_envn;
}
if (definit_open(ENVFILE, &dstate) != 0) {
console(B_TRUE,
"Cannot open %s. Environment not initialized.\n",
ENVFILE);
return;
}
while ((tokp = definit_token(dstate)) != NULL &&
glob_envn < MAXENVENT - 2) {
if (strncmp(tokp, "CMASK=", sizeof ("CMASK=") - 1) == 0) {
long t;
t = strtol(strchr(tokp, '=') + 1, NULL, 8);
if (t >= DEFINIT_MIN_UMASK && t <= DEFINIT_MAX_UMASK)
cmask = (int)t;
(void) umask(cmask);
continue;
}
glob_envp[glob_envn] = strdup(tokp);
if (glob_envp[glob_envn] == NULL) {
console(B_TRUE, "Out of memory building environment, "
"truncated.\n");
break;
}
if (++glob_envn >= MAXENVENT - 1) {
console(B_TRUE, "Too many variables in %s; "
"environment not fully initialized.\n", ENVFILE);
break;
}
}
glob_envp[glob_envn] = NULL;
definit_close(dstate);
}
void
boot_init()
{
int i;
struct PROC_TABLE *process, *oprocess;
struct CMD_LINE cmd;
char line[MAXCMDL];
char svc_aux[SVC_AUX_SIZE];
char init_svc_fmri[SVC_FMRI_SIZE];
char *old_path;
int maxfiles;
old_path = glob_envp[0];
glob_envp[0] = malloc((unsigned)(strlen(INIT_PATH)+2));
(void) strcpy(glob_envp[0], INIT_PATH);
while (getcmd(&cmd, &line[0]) == TRUE) {
if (startd_tmpl >= 0 && id_eq(cmd.c_id, "smf")) {
process_startd_line(&cmd, line);
(void) snprintf(startd_svc_aux, SVC_AUX_SIZE,
INITTAB_ENTRY_ID_STR_FORMAT, cmd.c_id);
} else if (cmd.c_action == M_INITDEFAULT) {
console(B_TRUE,
"Ignoring legacy \"initdefault\" entry.\n");
} else if (cmd.c_action == M_SYSINIT) {
if (process = findpslot(&cmd)) {
(void) sighold(SIGCLD);
(void) snprintf(svc_aux, SVC_AUX_SIZE,
INITTAB_ENTRY_ID_STR_FORMAT, cmd.c_id);
(void) snprintf(init_svc_fmri, SVC_FMRI_SIZE,
SVC_INIT_PREFIX INITTAB_ENTRY_ID_STR_FORMAT,
cmd.c_id);
if (legacy_tmpl >= 0) {
(void) ct_pr_tmpl_set_svc_fmri(
legacy_tmpl, init_svc_fmri);
(void) ct_pr_tmpl_set_svc_aux(
legacy_tmpl, svc_aux);
}
for (oprocess = process;
(process = efork(M_OFF, oprocess,
(NAMED|NOCLEANUP))) == NO_ROOM;
)
;
(void) sigrelse(SIGCLD);
if (process == NULLPROC) {
maxfiles = ulimit(UL_GDESLIM, 0);
for (i = 0; i < maxfiles; i++)
(void) fcntl(i, F_SETFD,
FD_CLOEXEC);
(void) execle(SH, "INITSH", "-c",
cmd.c_command,
(char *)0, glob_envp);
console(B_TRUE,
"Command\n\"%s\"\n failed to execute. errno = %d (exec of shell failed)\n",
cmd.c_command, errno);
exit(1);
} else
while (waitproc(process) == FAILURE)
;
process->p_flags = 0;
st_write();
}
}
}
free(glob_envp[0]);
glob_envp[0] = old_path;
booting = 0;
if (write_ioctl)
write_ioctl_syscon();
if (startd_cline[0] != '\0' && startd_tmpl >= 0) {
if (startd_run(startd_cline, startd_tmpl, 0) == -1)
cur_state = SINGLE_USER;
} else {
console(B_TRUE, "Absent svc.startd entry or bad "
"contract template. Not starting svc.startd.\n");
enter_maintenance();
}
}
void
init_signals(void)
{
struct sigaction act;
int i;
for (i = SIGHUP; i <= SIGRTMAX; i++)
(void) sigset(i, SIG_IGN);
act.sa_sigaction = siglvl;
act.sa_flags = SA_SIGINFO;
(void) sigemptyset(&act.sa_mask);
(void) sigaddset(&act.sa_mask, LVLQ);
(void) sigaddset(&act.sa_mask, LVL0);
(void) sigaddset(&act.sa_mask, LVL1);
(void) sigaddset(&act.sa_mask, LVL2);
(void) sigaddset(&act.sa_mask, LVL3);
(void) sigaddset(&act.sa_mask, LVL4);
(void) sigaddset(&act.sa_mask, LVL5);
(void) sigaddset(&act.sa_mask, LVL6);
(void) sigaddset(&act.sa_mask, SINGLE_USER);
(void) sigaddset(&act.sa_mask, LVLa);
(void) sigaddset(&act.sa_mask, LVLb);
(void) sigaddset(&act.sa_mask, LVLc);
(void) sigaction(LVLQ, &act, NULL);
(void) sigaction(LVL0, &act, NULL);
(void) sigaction(LVL1, &act, NULL);
(void) sigaction(LVL2, &act, NULL);
(void) sigaction(LVL3, &act, NULL);
(void) sigaction(LVL4, &act, NULL);
(void) sigaction(LVL5, &act, NULL);
(void) sigaction(LVL6, &act, NULL);
(void) sigaction(SINGLE_USER, &act, NULL);
(void) sigaction(LVLa, &act, NULL);
(void) sigaction(LVLb, &act, NULL);
(void) sigaction(LVLc, &act, NULL);
(void) sigset(SIGALRM, alarmclk);
alarmclk();
(void) sigset(SIGCLD, childeath);
(void) sigset(SIGPWR, powerfail);
}
void
setup_pipe()
{
struct stat stat_buf;
struct statvfs statvfs_buf;
struct sigaction act;
if (Pfd >= 0)
(void) close(Pfd);
if ((stat(INITPIPE, &stat_buf) == 0) &&
((stat_buf.st_mode & (S_IFMT|S_IRUSR)) == (S_IFIFO|S_IRUSR)))
Pfd = open(INITPIPE, O_RDWR | O_NDELAY);
else
if ((statvfs(INITPIPE_DIR, &statvfs_buf) == 0) &&
((statvfs_buf.f_flag & ST_RDONLY) == 0)) {
(void) unlink(INITPIPE);
(void) mknod(INITPIPE, S_IFIFO | 0600, 0);
Pfd = open(INITPIPE, O_RDWR | O_NDELAY);
}
if (Pfd >= 0) {
(void) ioctl(Pfd, I_SETSIG, S_INPUT);
(void) ioctl(Pfd, I_SRDOPT, RMSGD);
act.sa_handler = sigpoll;
act.sa_flags = 0;
(void) sigemptyset(&act.sa_mask);
(void) sigaddset(&act.sa_mask, SIGCLD);
(void) sigaction(SIGPOLL, &act, NULL);
}
}
void
siglvl(int sig, siginfo_t *sip, void *arg)
{
ucontext_t *ucp = arg;
struct PROC_TABLE *process;
struct sigaction act;
if (sip != NULL && SI_FROMKERNEL(sip) &&
(sig != SIGFPE || sip->si_code == 0)) {
(void) sigemptyset(&act.sa_mask);
act.sa_handler = SIG_DFL;
act.sa_flags = 0;
(void) sigaction(sig, &act, NULL);
(void) sigfillset(&ucp->uc_sigmask);
(void) sigdelset(&ucp->uc_sigmask, sig);
ucp->uc_flags |= UC_SIGMASK;
(void) setcontext(ucp);
}
if (sig == LVLQ) {
new_state = cur_state;
lvlq_received = B_TRUE;
} else {
new_state = sig;
}
for (process = proc_table;
(process < proc_table + num_proc); process++) {
process->p_time = 0L;
process->p_count = 0;
}
wakeup.w_flags.w_usersignal = 1;
}
static void
alarmclk()
{
time_up = TRUE;
}
static void
childeath_single(pid_t pid, int status)
{
struct PROC_TABLE *process;
struct pidlist *pp;
for (process = proc_table;
(process < proc_table + num_proc); process++) {
if ((process->p_flags & (LIVING|OCCUPIED)) ==
(LIVING|OCCUPIED) && process->p_pid == pid) {
process->p_flags &= ~LIVING;
process->p_exit = (short)status;
wakeup.w_flags.w_childdeath = 1;
return;
}
}
(void) sighold(SIGPOLL);
pp = Plhead;
while (pp) {
if (pid > pp->pl_pid) {
pp = pp->pl_next;
continue;
} else if (pid < pp->pl_pid) {
break;
} else {
pp->pl_dflag = 1;
pp->pl_exit = (short)status;
wakeup.w_flags.w_childdeath = 1;
Gchild = 1;
break;
}
}
(void) sigrelse(SIGPOLL);
}
static void
childeath(int signo)
{
pid_t pid;
int status;
while ((pid = waitpid(-1, &status, WNOHANG)) > 0)
childeath_single(pid, status);
}
static void
powerfail()
{
(void) nice(-19);
wakeup.w_flags.w_powerhit = 1;
}
static struct PROC_TABLE *
efork(int action, struct PROC_TABLE *process, int modes)
{
pid_t childpid;
struct PROC_TABLE *proc;
int i;
for (proc = proc_table; (proc < proc_table + num_proc); proc++) {
if ((proc->p_flags & (OCCUPIED|LIVING|NOCLEANUP)) ==
(OCCUPIED)) {
if (proc->p_flags & NAMED)
(void) account(DEAD_PROCESS, proc, NULL);
proc->p_flags = 0;
}
}
while ((childpid = fork()) == FAILURE) {
setimer(5);
(void) sigrelse(SIGCLD);
(void) pause();
(void) sighold(SIGCLD);
setimer(0);
}
if (childpid != 0) {
if (process == NULLPROC) {
for (process = proc_table; process->p_flags != 0 &&
(process < proc_table + num_proc); process++)
;
if (process == (proc_table + num_proc)) {
int old_proc_table_size = num_proc;
increase_proc_table_size();
if (old_proc_table_size == num_proc) {
return (NO_ROOM);
} else {
process =
proc_table + old_proc_table_size;
}
}
process->p_time = 0L;
process->p_count = 0;
}
process->p_id[0] = '\0';
process->p_id[1] = '\0';
process->p_id[2] = '\0';
process->p_id[3] = '\0';
process->p_pid = childpid;
process->p_flags = (LIVING | OCCUPIED | modes);
process->p_exit = 0;
st_write();
} else {
if ((action & (M_WAIT | M_BOOTWAIT)) == 0)
(void) setpgrp();
process = NULLPROC;
for (i = SIGHUP; i <= SIGRTMAX; i++)
(void) sigset(i, SIG_DFL);
(void) sigset(SIGTTIN, SIG_IGN);
(void) sigset(SIGTTOU, SIG_IGN);
(void) sigset(SIGTSTP, SIG_IGN);
(void) sigset(SIGXCPU, SIG_IGN);
(void) sigset(SIGXFSZ, SIG_IGN);
}
return (process);
}
static long
waitproc(struct PROC_TABLE *process)
{
int answer;
sigset_t oldmask, newmask, zeromask;
(void) sigemptyset(&zeromask);
(void) sigemptyset(&newmask);
(void) sigaddset(&newmask, SIGCLD);
if (sigprocmask(SIG_BLOCK, &newmask, &oldmask) < 0)
perror("SIG_BLOCK error");
if (process->p_flags & LIVING)
(void) sigsuspend(&zeromask);
if (sigprocmask(SIG_SETMASK, &oldmask, NULL) < 0)
perror("SIG_SETMASK error");
if (process->p_flags & LIVING)
return (FAILURE);
answer = (process->p_exit & 0xffff);
process->p_flags = 0;
return (answer);
}
static void
notify_pam_dead(struct utmpx *up)
{
pam_handle_t *pamh;
char user[sizeof (up->ut_user) + 1];
char ttyn[sizeof (up->ut_line) + 1];
char host[sizeof (up->ut_host) + 1];
(void) snprintf(user, sizeof (user), "%s", up->ut_user);
(void) snprintf(ttyn, sizeof (ttyn), "%s", up->ut_line);
(void) snprintf(host, sizeof (host), "%s", up->ut_host);
if (pam_start("init", user, NULL, &pamh) == PAM_SUCCESS) {
(void) pam_set_item(pamh, PAM_TTY, ttyn);
(void) pam_set_item(pamh, PAM_RHOST, host);
(void) pam_close_session(pamh, 0);
(void) pam_end(pamh, PAM_SUCCESS);
}
}
static int
access_utmpx(void)
{
do {
utmpx_ok = (access(UTMPX, R_OK|W_OK) == 0);
} while (!utmpx_ok && errno == EINTR);
return (utmpx_ok);
}
static int
account(short state, struct PROC_TABLE *process, char *program)
{
struct utmpx utmpbuf, *u, *oldu;
int tmplen;
char fail_buf[UT_LINE_SZ];
sigset_t block, unblock;
if (!utmpx_ok && !access_utmpx()) {
return (-1);
}
u = &utmpbuf;
(void) memset(u, 0, sizeof (struct utmpx));
u->ut_id[0] = process->p_id[0];
u->ut_id[1] = process->p_id[1];
u->ut_id[2] = process->p_id[2];
u->ut_id[3] = process->p_id[3];
u->ut_pid = process->p_pid;
u->ut_exit.e_termination = WTERMSIG(process->p_exit);
u->ut_exit.e_exit = WEXITSTATUS(process->p_exit);
u->ut_type = state;
(void) time(&u->ut_tv.tv_sec);
(void) sigfillset(&block);
(void) sigprocmask(SIG_BLOCK, &block, &unblock);
setutxent();
if ((oldu = getutxid(u)) != NULL) {
bcopy(oldu->ut_user, u->ut_user, sizeof (u->ut_user));
bcopy(oldu->ut_line, u->ut_line, sizeof (u->ut_line));
bcopy(oldu->ut_host, u->ut_host, sizeof (u->ut_host));
u->ut_syslen = (tmplen = strlen(u->ut_host)) ?
min(tmplen + 1, sizeof (u->ut_host)) : 0;
if (oldu->ut_type == USER_PROCESS && state == DEAD_PROCESS) {
notify_pam_dead(oldu);
}
}
switch (state) {
case INIT_PROCESS:
(void) strncpy(u->ut_user, program, sizeof (u->ut_user));
(void) strcpy(fail_buf, "INIT_PROCESS");
break;
default:
(void) strlcpy(fail_buf, u->ut_id, sizeof (u->ut_id) + 1);
break;
}
if (pututxline(u) == NULL) {
console(B_TRUE, "Failed write of utmpx entry: \"%s\": %s\n",
fail_buf, strerror(errno));
endutxent();
(void) sigprocmask(SIG_SETMASK, &unblock, NULL);
return (-1);
}
updwtmpx(WTMPX, u);
endutxent();
(void) sigprocmask(SIG_SETMASK, &unblock, NULL);
return (0);
}
static void
clearent(pid_t pid, short status)
{
struct utmpx *up;
sigset_t block, unblock;
(void) sigfillset(&block);
(void) sigprocmask(SIG_BLOCK, &block, &unblock);
setutxent();
while (up = getutxent()) {
if (up->ut_pid == pid) {
if (up->ut_type == DEAD_PROCESS) {
continue;
}
notify_pam_dead(up);
up->ut_type = DEAD_PROCESS;
up->ut_exit.e_termination = WTERMSIG(status);
up->ut_exit.e_exit = WEXITSTATUS(status);
(void) time(&up->ut_tv.tv_sec);
(void) pututxline(up);
updwtmpx(WTMPX, up);
break;
}
}
endutxent();
(void) sigprocmask(SIG_SETMASK, &unblock, NULL);
}
static char *
prog_name(char *string)
{
char *ptr, *ptr2;
static char word[UT_USER_SZ + 1];
while (*string == ' ' || *string == '\t')
string++;
if (*string != '.' && *string != '/' && *string != '_' &&
(*string < 'a' || *string > 'z') &&
(*string < 'A' || * string > 'Z') &&
(*string < '0' || *string > '9'))
return ("");
for (ptr = string; *string != ' ' && *string != '\t' &&
*string != '\n' && *string != '\0'; string++) {
if (*string == '/')
ptr = string+1;
}
for (ptr2 = &word[0]; ptr2 < &word[UT_USER_SZ] &&
ptr < string; )
*ptr2++ = *ptr++;
*ptr2 = '\0';
return (&word[0]);
}
static int
realcon()
{
struct stat sconbuf, conbuf;
if (stat(SYSCON, &sconbuf) != -1 &&
stat(CONSOLE, &conbuf) != -1 &&
S_ISCHR(sconbuf.st_mode) &&
S_ISCHR(conbuf.st_mode) &&
sconbuf.st_rdev == conbuf.st_rdev) {
return (1);
} else {
return (0);
}
}
static int
get_ioctl_syscon()
{
FILE *fp;
unsigned int iflags, oflags, cflags, lflags, ldisc, cc[18];
int i, valid_format = 0;
if ((fp = fopen(IOCTLSYSCON, "r")) == NULL) {
stored_syscon_termios = dflt_termios;
console(B_TRUE,
"warning:%s does not exist, default settings assumed\n",
IOCTLSYSCON);
} else {
i = fscanf(fp,
"%x:%x:%x:%x:%x:%x:%x:%x:%x:%x:%x:%x:%x:%x:%x:%x:%x:%x:%x:%x:%x:%x",
&iflags, &oflags, &cflags, &lflags,
&cc[0], &cc[1], &cc[2], &cc[3], &cc[4], &cc[5], &cc[6],
&cc[7], &cc[8], &cc[9], &cc[10], &cc[11], &cc[12], &cc[13],
&cc[14], &cc[15], &cc[16], &cc[17]);
if (i == 22) {
stored_syscon_termios.c_iflag = iflags;
stored_syscon_termios.c_oflag = oflags;
stored_syscon_termios.c_cflag = cflags;
stored_syscon_termios.c_lflag = lflags;
for (i = 0; i < 18; i++)
stored_syscon_termios.c_cc[i] = (char)cc[i];
valid_format = 1;
} else if (i == 13) {
rewind(fp);
i = fscanf(fp, "%x:%x:%x:%x:%x:%x:%x:%x:%x:%x:%x:%x:%x",
&iflags, &oflags, &cflags, &lflags, &ldisc, &cc[0], &cc[1],
&cc[2], &cc[3], &cc[4], &cc[5], &cc[6], &cc[7]);
stored_syscon_termios.c_iflag = (ushort_t)iflags;
stored_syscon_termios.c_oflag = (ushort_t)oflags;
stored_syscon_termios.c_cflag = (ushort_t)cflags;
stored_syscon_termios.c_lflag = (ushort_t)lflags;
for (i = 0; i < 8; i++)
stored_syscon_termios.c_cc[i] = (char)cc[i];
valid_format = 1;
}
(void) fclose(fp);
if (!valid_format)
stored_syscon_termios = dflt_termios;
}
return (!valid_format);
}
static void
write_ioctl_syscon()
{
FILE *fp;
int i;
(void) unlink(SYSCON);
(void) link(SYSTTY, SYSCON);
(void) umask(022);
fp = fopen(IOCTLSYSCON, "w");
(void) fprintf(fp, "%x:%x:%x:%x:0", stored_syscon_termios.c_iflag,
stored_syscon_termios.c_oflag, stored_syscon_termios.c_cflag,
stored_syscon_termios.c_lflag);
for (i = 0; i < 8; ++i)
(void) fprintf(fp, ":%x", stored_syscon_termios.c_cc[i]);
(void) putc('\n', fp);
(void) fflush(fp);
(void) fsync(fileno(fp));
(void) fclose(fp);
(void) umask(cmask);
}
static void
console(boolean_t prefix, char *format, ...)
{
char outbuf[BUFSIZ];
va_list args;
int fd, getret;
struct termios old_syscon_termios;
FILE *f;
if ((fd = open(SYSCON, O_RDWR | O_NOCTTY)) < 0 ||
(f = fdopen(fd, "r+")) == NULL) {
if (prefix)
syslog(LOG_WARNING, "INIT: ");
va_start(args, format);
vsyslog(LOG_WARNING, format, args);
va_end(args);
if (fd >= 0)
(void) close(fd);
return;
}
setbuf(f, &outbuf[0]);
getret = tcgetattr(fd, &old_syscon_termios);
old_syscon_termios.c_cflag &= ~HUPCL;
if (realcon())
stored_syscon_termios.c_cflag = old_syscon_termios.c_cflag;
stored_syscon_termios.c_cflag &= ~HUPCL;
(void) tcsetattr(fd, TCSANOW, &stored_syscon_termios);
if (prefix)
(void) fprintf(f, "\nINIT: ");
va_start(args, format);
(void) vfprintf(f, format, args);
va_end(args);
if (getret == 0)
(void) tcsetattr(fd, TCSADRAIN, &old_syscon_termios);
(void) fclose(f);
}
static void
timer(int waitime)
{
setimer(waitime);
while (time_up == FALSE)
(void) pause();
}
static void
setimer(int timelimit)
{
alarmclk();
(void) alarm(timelimit);
time_up = (timelimit ? FALSE : TRUE);
}
static int
get_or_add_startd(scf_instance_t *inst)
{
scf_handle_t *h;
scf_scope_t *scope = NULL;
scf_service_t *svc = NULL;
int ret = 0;
h = scf_instance_handle(inst);
if (scf_handle_decode_fmri(h, SCF_SERVICE_STARTD, NULL, NULL, inst,
NULL, NULL, SCF_DECODE_FMRI_EXACT) == 0)
return (0);
switch (scf_error()) {
case SCF_ERROR_CONNECTION_BROKEN:
return (ECONNABORTED);
case SCF_ERROR_NOT_FOUND:
break;
case SCF_ERROR_HANDLE_MISMATCH:
case SCF_ERROR_INVALID_ARGUMENT:
case SCF_ERROR_CONSTRAINT_VIOLATED:
default:
bad_error("scf_handle_decode_fmri", scf_error());
}
assert(strcmp(SCF_SERVICE_STARTD,
"svc:/system/svc/restarter:default") == 0);
if ((scope = scf_scope_create(h)) == NULL ||
(svc = scf_service_create(h)) == NULL) {
ret = ENOMEM;
goto out;
}
get_scope:
if (scf_handle_get_scope(h, SCF_SCOPE_LOCAL, scope) != 0) {
switch (scf_error()) {
case SCF_ERROR_CONNECTION_BROKEN:
ret = ECONNABORTED;
goto out;
case SCF_ERROR_NOT_FOUND:
(void) fputs(gettext(
"smf(7) repository missing local scope.\n"),
stderr);
exit(1);
case SCF_ERROR_HANDLE_MISMATCH:
case SCF_ERROR_INVALID_ARGUMENT:
default:
bad_error("scf_handle_get_scope", scf_error());
}
}
get_svc:
if (scf_scope_get_service(scope, "system/svc/restarter", svc) != 0) {
switch (scf_error()) {
case SCF_ERROR_CONNECTION_BROKEN:
ret = ECONNABORTED;
goto out;
case SCF_ERROR_DELETED:
goto get_scope;
case SCF_ERROR_NOT_FOUND:
break;
case SCF_ERROR_HANDLE_MISMATCH:
case SCF_ERROR_INVALID_ARGUMENT:
case SCF_ERROR_NOT_SET:
default:
bad_error("scf_scope_get_service", scf_error());
}
add_svc:
if (scf_scope_add_service(scope, "system/svc/restarter", svc) !=
0) {
switch (scf_error()) {
case SCF_ERROR_CONNECTION_BROKEN:
ret = ECONNABORTED;
goto out;
case SCF_ERROR_EXISTS:
goto get_svc;
case SCF_ERROR_PERMISSION_DENIED:
ret = EPERM;
goto out;
case SCF_ERROR_BACKEND_ACCESS:
ret = EACCES;
goto out;
case SCF_ERROR_BACKEND_READONLY:
ret = EROFS;
goto out;
case SCF_ERROR_HANDLE_MISMATCH:
case SCF_ERROR_INVALID_ARGUMENT:
case SCF_ERROR_NOT_SET:
default:
bad_error("scf_scope_add_service", scf_error());
}
}
}
get_inst:
if (scf_service_get_instance(svc, "default", inst) != 0) {
switch (scf_error()) {
case SCF_ERROR_CONNECTION_BROKEN:
ret = ECONNABORTED;
goto out;
case SCF_ERROR_DELETED:
goto add_svc;
case SCF_ERROR_NOT_FOUND:
break;
case SCF_ERROR_HANDLE_MISMATCH:
case SCF_ERROR_INVALID_ARGUMENT:
case SCF_ERROR_NOT_SET:
default:
bad_error("scf_service_get_instance", scf_error());
}
if (scf_service_add_instance(svc, "default", inst) !=
0) {
switch (scf_error()) {
case SCF_ERROR_CONNECTION_BROKEN:
ret = ECONNABORTED;
goto out;
case SCF_ERROR_DELETED:
goto add_svc;
case SCF_ERROR_EXISTS:
goto get_inst;
case SCF_ERROR_PERMISSION_DENIED:
ret = EPERM;
goto out;
case SCF_ERROR_BACKEND_ACCESS:
ret = EACCES;
goto out;
case SCF_ERROR_BACKEND_READONLY:
ret = EROFS;
goto out;
case SCF_ERROR_HANDLE_MISMATCH:
case SCF_ERROR_INVALID_ARGUMENT:
case SCF_ERROR_NOT_SET:
default:
bad_error("scf_service_add_instance",
scf_error());
}
}
}
ret = 0;
out:
scf_service_destroy(svc);
scf_scope_destroy(scope);
return (ret);
}
static int
transaction_add_set(scf_transaction_t *tx, scf_transaction_entry_t *ent,
const char *pname, scf_type_t type)
{
change_type:
if (scf_transaction_property_change_type(tx, ent, pname, type) == 0)
return (0);
switch (scf_error()) {
case SCF_ERROR_CONNECTION_BROKEN:
return (ECONNABORTED);
case SCF_ERROR_DELETED:
return (ECANCELED);
case SCF_ERROR_NOT_FOUND:
goto new;
case SCF_ERROR_HANDLE_MISMATCH:
case SCF_ERROR_INVALID_ARGUMENT:
case SCF_ERROR_NOT_BOUND:
case SCF_ERROR_NOT_SET:
default:
bad_error("scf_transaction_property_change_type", scf_error());
}
new:
if (scf_transaction_property_new(tx, ent, pname, type) == 0)
return (0);
switch (scf_error()) {
case SCF_ERROR_CONNECTION_BROKEN:
return (ECONNABORTED);
case SCF_ERROR_DELETED:
return (ECANCELED);
case SCF_ERROR_EXISTS:
goto change_type;
case SCF_ERROR_HANDLE_MISMATCH:
case SCF_ERROR_INVALID_ARGUMENT:
case SCF_ERROR_NOT_BOUND:
case SCF_ERROR_NOT_SET:
default:
bad_error("scf_transaction_property_new", scf_error());
}
}
static void
scferr(void)
{
switch (scf_error()) {
case SCF_ERROR_NO_MEMORY:
console(B_TRUE, gettext("Out of memory.\n"));
break;
case SCF_ERROR_CONNECTION_BROKEN:
console(B_TRUE, gettext(
"Connection to smf(7) repository server broken.\n"));
break;
case SCF_ERROR_NO_RESOURCES:
console(B_TRUE, gettext(
"smf(7) repository server is out of memory.\n"));
break;
case SCF_ERROR_PERMISSION_DENIED:
console(B_TRUE, gettext("Insufficient privileges.\n"));
break;
default:
console(B_TRUE, gettext("libscf error: %s\n"),
scf_strerror(scf_error()));
}
}
static void
lscf_set_runlevel(char rl)
{
scf_handle_t *h;
scf_instance_t *inst = NULL;
scf_propertygroup_t *pg = NULL;
scf_transaction_t *tx = NULL;
scf_transaction_entry_t *ent = NULL;
scf_value_t *val = NULL;
char buf[2];
int r;
h = scf_handle_create(SCF_VERSION);
if (h == NULL) {
scferr();
return;
}
if (scf_handle_bind(h) != 0) {
switch (scf_error()) {
case SCF_ERROR_NO_SERVER:
console(B_TRUE,
gettext("smf(7) repository server not running.\n"));
goto bail;
default:
scferr();
goto bail;
}
}
if ((inst = scf_instance_create(h)) == NULL ||
(pg = scf_pg_create(h)) == NULL ||
(val = scf_value_create(h)) == NULL ||
(tx = scf_transaction_create(h)) == NULL ||
(ent = scf_entry_create(h)) == NULL) {
scferr();
goto bail;
}
get_inst:
r = get_or_add_startd(inst);
switch (r) {
case 0:
break;
case ENOMEM:
case ECONNABORTED:
case EPERM:
case EACCES:
case EROFS:
scferr();
goto bail;
default:
bad_error("get_or_add_startd", r);
}
get_pg:
if (scf_instance_get_pg(inst, SCF_PG_OPTIONS_OVR, pg) != 0) {
switch (scf_error()) {
case SCF_ERROR_CONNECTION_BROKEN:
scferr();
goto bail;
case SCF_ERROR_DELETED:
goto get_inst;
case SCF_ERROR_NOT_FOUND:
break;
case SCF_ERROR_HANDLE_MISMATCH:
case SCF_ERROR_INVALID_ARGUMENT:
case SCF_ERROR_NOT_SET:
default:
bad_error("scf_instance_get_pg", scf_error());
}
add_pg:
if (scf_instance_add_pg(inst, SCF_PG_OPTIONS_OVR,
SCF_PG_OPTIONS_OVR_TYPE, SCF_PG_OPTIONS_OVR_FLAGS, pg) !=
0) {
switch (scf_error()) {
case SCF_ERROR_CONNECTION_BROKEN:
case SCF_ERROR_PERMISSION_DENIED:
case SCF_ERROR_BACKEND_ACCESS:
scferr();
goto bail;
case SCF_ERROR_DELETED:
goto get_inst;
case SCF_ERROR_EXISTS:
goto get_pg;
case SCF_ERROR_HANDLE_MISMATCH:
case SCF_ERROR_INVALID_ARGUMENT:
case SCF_ERROR_NOT_SET:
default:
bad_error("scf_instance_add_pg", scf_error());
}
}
}
buf[0] = rl;
buf[1] = '\0';
r = scf_value_set_astring(val, buf);
assert(r == 0);
for (;;) {
if (scf_transaction_start(tx, pg) != 0) {
switch (scf_error()) {
case SCF_ERROR_CONNECTION_BROKEN:
case SCF_ERROR_PERMISSION_DENIED:
case SCF_ERROR_BACKEND_ACCESS:
scferr();
goto bail;
case SCF_ERROR_DELETED:
goto add_pg;
case SCF_ERROR_HANDLE_MISMATCH:
case SCF_ERROR_NOT_BOUND:
case SCF_ERROR_IN_USE:
case SCF_ERROR_NOT_SET:
default:
bad_error("scf_transaction_start", scf_error());
}
}
r = transaction_add_set(tx, ent, "runlevel", SCF_TYPE_ASTRING);
switch (r) {
case 0:
break;
case ECONNABORTED:
scferr();
goto bail;
case ECANCELED:
scf_transaction_reset(tx);
goto add_pg;
default:
bad_error("transaction_add_set", r);
}
r = scf_entry_add_value(ent, val);
assert(r == 0);
r = scf_transaction_commit(tx);
if (r == 1)
break;
if (r != 0) {
switch (scf_error()) {
case SCF_ERROR_CONNECTION_BROKEN:
case SCF_ERROR_PERMISSION_DENIED:
case SCF_ERROR_BACKEND_ACCESS:
case SCF_ERROR_BACKEND_READONLY:
scferr();
goto bail;
case SCF_ERROR_DELETED:
scf_transaction_reset(tx);
goto add_pg;
case SCF_ERROR_INVALID_ARGUMENT:
case SCF_ERROR_NOT_BOUND:
case SCF_ERROR_NOT_SET:
default:
bad_error("scf_transaction_commit",
scf_error());
}
}
scf_transaction_reset(tx);
(void) scf_pg_update(pg);
}
bail:
scf_transaction_destroy(tx);
scf_entry_destroy(ent);
scf_value_destroy(val);
scf_pg_destroy(pg);
scf_instance_destroy(inst);
(void) scf_handle_unbind(h);
scf_handle_destroy(h);
}
static void
userinit(int argc, char **argv)
{
FILE *fp;
char *ln;
int init_signal;
struct stat sconbuf, conbuf;
const char *usage_msg = "Usage: init [0123456SsQqabc]\n";
if (argc != 2 || argv[1][1] != '\0') {
(void) fprintf(stderr, usage_msg);
exit(0);
}
if ((init_signal = lvlname_to_state((char)argv[1][0])) == -1) {
(void) fprintf(stderr, usage_msg);
(void) audit_put_record(ADT_FAILURE, ADT_FAIL_VALUE_BAD_CMD,
argv[1]);
exit(1);
}
if (init_signal == SINGLE_USER) {
ln = ttyname(0);
if (ln == NULL) {
(void) fprintf(stderr,
"Standard input not a tty line\n");
(void) audit_put_record(ADT_FAILURE,
ADT_FAIL_VALUE_BAD_TTY, argv[1]);
exit(1);
}
if ((stat(ln, &sconbuf) != -1) &&
(stat(SYSCON, &conbuf) == -1 ||
sconbuf.st_rdev != conbuf.st_rdev)) {
if (lstat(SYSCON, &conbuf) != -1 &&
unlink(SYSCON) == FAILURE) {
perror("Can't unlink /dev/syscon");
(void) fprintf(stderr,
"Run command on the system console.\n");
(void) audit_put_record(ADT_FAILURE,
ADT_FAIL_VALUE_PROGRAM, argv[1]);
exit(1);
}
if (symlink(ln, SYSCON) == FAILURE) {
(void) fprintf(stderr,
"Can't symlink /dev/syscon to %s: %s", ln,
strerror(errno));
(void) link(SYSTTY, SYSCON);
(void) audit_put_record(ADT_FAILURE,
ADT_FAIL_VALUE_PROGRAM, argv[1]);
exit(1);
}
if ((fp = fopen(SYSTTY, "r+")) != NULL) {
(void) fprintf(fp,
"\n**** SYSCON CHANGED TO %s ****\n",
ln);
(void) fclose(fp);
}
}
}
update_boot_archive(init_signal);
(void) audit_put_record(ADT_SUCCESS, ADT_SUCCESS, argv[1]);
if (kill(init_pid, init_signal) == FAILURE) {
(void) fprintf(stderr, "Must be super-user\n");
(void) audit_put_record(ADT_FAILURE,
ADT_FAIL_VALUE_AUTH, argv[1]);
exit(1);
}
exit(0);
}
#define DELTA 25
void
sigpoll(int n)
{
struct pidrec prec;
struct pidrec *p = ≺
struct pidlist *plp;
struct pidlist *tp, *savetp;
int i;
if (Pfd < 0) {
return;
}
for (;;) {
if (read(Pfd, p, sizeof (struct pidrec)) !=
sizeof (struct pidrec)) {
return;
}
switch (p->pd_type) {
case ADDPID:
if (Plfree == NULL) {
plp = (struct pidlist *)calloc(DELTA,
sizeof (struct pidlist));
if (plp == NULL) {
break;
}
tp = plp + 1;
Plfree = tp;
for (i = 0; i < DELTA - 2; i++) {
tp->pl_next = tp + 1;
tp++;
}
} else {
plp = Plfree;
Plfree = plp->pl_next;
}
plp->pl_pid = p->pd_pid;
plp->pl_dflag = 0;
plp->pl_next = NULL;
if (Plhead == NULL) {
Plhead = plp;
break;
} else {
savetp = tp = Plhead;
while (tp) {
if (plp->pl_pid > tp->pl_pid) {
savetp = tp;
tp = tp->pl_next;
continue;
} else if (plp->pl_pid < tp->pl_pid) {
if (tp == Plhead) {
plp->pl_next = Plhead;
Plhead = plp;
} else {
plp->pl_next =
savetp->pl_next;
savetp->pl_next = plp;
}
break;
} else {
plp->pl_next = Plfree;
Plfree = plp;
break;
}
}
if (tp == NULL) {
savetp->pl_next = plp;
}
}
break;
case REMPID:
if (Plhead == NULL) {
break;
}
savetp = tp = Plhead;
while (tp) {
if (p->pd_pid > tp->pl_pid) {
savetp = tp;
tp = tp->pl_next;
continue;
} else if (p->pd_pid < tp->pl_pid) {
break;
} else {
if (tp == Plhead)
Plhead = tp->pl_next;
else
savetp->pl_next = tp->pl_next;
tp->pl_next = Plfree;
Plfree = tp;
break;
}
}
break;
default:
console(B_TRUE, "Bad message on initpipe\n");
break;
}
}
}
static void
cleanaux()
{
struct pidlist *savep, *p;
pid_t pid;
short status;
(void) sighold(SIGCLD);
Gchild = 0;
(void) sighold(SIGPOLL);
savep = p = Plhead;
while (p) {
if (p->pl_dflag) {
pid = p->pl_pid;
status = p->pl_exit;
if (p == Plhead) {
Plhead = p->pl_next;
p->pl_next = Plfree;
Plfree = p;
savep = p = Plhead;
} else {
savep->pl_next = p->pl_next;
p->pl_next = Plfree;
Plfree = p;
p = savep->pl_next;
}
clearent(pid, status);
continue;
}
savep = p;
p = p->pl_next;
}
(void) sigrelse(SIGPOLL);
(void) sigrelse(SIGCLD);
}
static void
increase_proc_table_size()
{
sigset_t block, unblock;
void *ptr;
size_t delta = num_proc * sizeof (struct PROC_TABLE);
(void) sigfillset(&block);
(void) sigprocmask(SIG_BLOCK, &block, &unblock);
do
ptr = realloc(g_state, g_state_sz + delta);
while (ptr == NULL && errno == EAGAIN)
;
if (ptr != NULL) {
bzero((caddr_t)ptr + g_state_sz, delta);
g_state = ptr;
g_state_sz += delta;
num_proc <<= 1;
}
(void) sigprocmask(SIG_SETMASK, &unblock, NULL);
}
static int
st_sane()
{
int i;
struct PROC_TABLE *ptp;
if (cur_state < 1 || cur_state == 9 || cur_state > 13)
return (0);
if (g_state_sz != sizeof (struct init_state) + (num_proc - 1) *
sizeof (struct PROC_TABLE))
return (0);
for (i = 0, ptp = proc_table; i < num_proc; ++i, ++ptp) {
if (!(ptp->p_flags & OCCUPIED))
continue;
if (ptp->p_flags & ~(PF_MASK))
return (0);
if (ptp->p_pid < 5 || ptp->p_pid > MAXPID)
return (0);
if (ptp->p_count < 0)
return (0);
if (ptp->p_time < 0)
return (0);
}
return (1);
}
void
st_init()
{
struct stat stb;
int ret, st_fd, insane = 0;
size_t to_be_read;
char *ptr;
booting = 1;
do {
st_fd = open(init_state_file, O_RDWR | O_CREAT | O_EXCL,
S_IRUSR | S_IWUSR);
} while (st_fd == -1 && errno == EINTR);
if (st_fd != -1)
goto new_state;
booting = 0;
do {
st_fd = open(init_state_file, O_RDWR, S_IRUSR | S_IWUSR);
} while (st_fd == -1 && errno == EINTR);
if (st_fd == -1)
goto new_state;
do
ret = fstat(st_fd, &stb);
while (ret == -1 && errno == EINTR)
;
if (ret == -1)
goto new_state;
do
g_state = malloc(stb.st_size);
while (g_state == NULL && errno == EAGAIN)
;
if (g_state == NULL)
goto new_state;
to_be_read = stb.st_size;
ptr = (char *)g_state;
while (to_be_read > 0) {
ssize_t read_ret;
read_ret = read(st_fd, ptr, to_be_read);
if (read_ret < 0) {
if (errno == EINTR)
continue;
goto new_state;
}
to_be_read -= read_ret;
ptr += read_ret;
}
(void) close(st_fd);
g_state_sz = stb.st_size;
if (st_sane()) {
console(B_TRUE, "Restarting.\n");
return;
}
insane = 1;
new_state:
if (st_fd >= 0)
(void) close(st_fd);
else
(void) unlink(init_state_file);
if (g_state != NULL)
free(g_state);
g_state_sz = sizeof (struct init_state) +
((init_num_proc - 1) * sizeof (struct PROC_TABLE));
do
g_state = calloc(1, g_state_sz);
while (g_state == NULL && errno == EAGAIN)
;
if (g_state == NULL) {
exit(errno);
}
g_state->ist_runlevel = -1;
num_proc = init_num_proc;
if (!booting) {
console(B_TRUE, "Restarting.\n");
st_write();
if (!insane) {
console(B_TRUE,
"Error accessing persistent state file `%s'. "
"Ignored.\n", init_state_file);
} else {
console(B_TRUE,
"Persistent state file `%s' is invalid and was "
"ignored.\n", init_state_file);
}
}
}
void
st_write()
{
static int complained = 0;
int st_fd;
char *cp;
size_t sz;
ssize_t ret;
do {
st_fd = open(init_next_state_file,
O_WRONLY | O_CREAT | O_TRUNC, S_IRUSR | S_IWUSR);
} while (st_fd < 0 && errno == EINTR);
if (st_fd < 0)
goto err;
cp = (char *)g_state;
sz = g_state_sz;
while (sz > 0) {
ret = write(st_fd, cp, sz);
if (ret < 0) {
if (errno == EINTR)
continue;
goto err;
}
sz -= ret;
cp += ret;
}
(void) close(st_fd);
st_fd = -1;
if (rename(init_next_state_file, init_state_file)) {
(void) unlink(init_next_state_file);
goto err;
}
complained = 0;
return;
err:
if (st_fd >= 0)
(void) close(st_fd);
if (!booting && !complained) {
complained = 1;
if (st_fd)
console(B_TRUE, "Couldn't write persistent state "
"file `%s'.\n", init_state_file);
else
console(B_TRUE, "Couldn't move persistent state "
"file `%s' to `%s'.\n", init_next_state_file,
init_state_file);
}
}
static int
contract_make_template(uint_t info, uint_t critical, uint_t fatal,
uint64_t cookie)
{
int fd, err;
char *ioctl_tset_emsg =
"Couldn't set \"%s\" contract template parameter: %s.\n";
do
fd = open64(CTFS_ROOT "/process/template", O_RDWR);
while (fd < 0 && errno == EINTR)
;
if (fd < 0) {
console(B_TRUE, "Couldn't create process template: %s.\n",
strerror(errno));
return (-1);
}
if (err = ct_pr_tmpl_set_param(fd, CT_PR_INHERIT | CT_PR_REGENT))
console(B_TRUE, "Contract set template inherit, regent "
"failed: %s.\n", strerror(err));
if (err = ct_tmpl_set_informative(fd, info))
console(B_TRUE, ioctl_tset_emsg, "informative", strerror(err));
if (err = ct_tmpl_set_critical(fd, critical))
console(B_TRUE, ioctl_tset_emsg, "critical", strerror(err));
if (err = ct_pr_tmpl_set_fatal(fd, fatal))
console(B_TRUE, ioctl_tset_emsg, "fatal", strerror(err));
if (err = ct_tmpl_set_cookie(fd, cookie))
console(B_TRUE, ioctl_tset_emsg, "cookie", strerror(err));
(void) fcntl(fd, F_SETFD, FD_CLOEXEC);
return (fd);
}
static void
contracts_init()
{
int err, fd;
legacy_tmpl = contract_make_template(0, CT_PR_EV_EMPTY, CT_PR_EV_HWERR,
ORDINARY_COOKIE);
if (legacy_tmpl >= 0) {
err = ct_tmpl_activate(legacy_tmpl);
if (err != 0) {
(void) close(legacy_tmpl);
legacy_tmpl = -1;
console(B_TRUE,
"Couldn't activate legacy template (%s); "
"legacy services will be in init's contract.\n",
strerror(err));
}
} else
console(B_TRUE,
"Legacy services will be in init's contract.\n");
if (dup2(legacy_tmpl, 255) == -1) {
console(B_TRUE, "Could not duplicate legacy template: %s.\n",
strerror(errno));
} else {
(void) close(legacy_tmpl);
legacy_tmpl = 255;
}
(void) fcntl(legacy_tmpl, F_SETFD, FD_CLOEXEC);
startd_tmpl = contract_make_template(0, CT_PR_EV_EMPTY,
CT_PR_EV_HWERR | CT_PR_EV_SIGNAL | CT_PR_EV_CORE, STARTD_COOKIE);
if (dup2(startd_tmpl, 254) == -1) {
console(B_TRUE, "Could not duplicate startd template: %s.\n",
strerror(errno));
} else {
(void) close(startd_tmpl);
startd_tmpl = 254;
}
(void) fcntl(startd_tmpl, F_SETFD, FD_CLOEXEC);
if (legacy_tmpl < 0 && startd_tmpl < 0) {
console(B_TRUE,
"Ignoring contract events. Core smf(7) services will not "
"be restarted.\n");
return;
}
do
fd = open64(CTFS_ROOT "/process/pbundle", O_RDONLY);
while (fd < 0 && errno == EINTR)
;
if (fd < 0) {
console(B_TRUE,
"Couldn't open process pbundle: %s. Core smf(7) services "
"will not be restarted.\n", strerror(errno));
return;
}
if (dup2(fd, 253) == -1) {
console(B_TRUE, "Could not duplicate process bundle: %s.\n",
strerror(errno));
} else {
(void) close(fd);
fd = 253;
}
(void) fcntl(fd, F_SETFD, FD_CLOEXEC);
(void) ct_event_reset(fd);
poll_fds[0].fd = fd;
poll_fds[0].events = POLLIN;
poll_nfds = 1;
}
static int
contract_getfile(ctid_t id, const char *name, int oflag)
{
int fd;
do
fd = contract_open(id, "process", name, oflag);
while (fd < 0 && errno == EINTR)
;
if (fd < 0)
console(B_TRUE, "Couldn't open %s for contract %ld: %s.\n",
name, id, strerror(errno));
return (fd);
}
static int
contract_cookie(ctid_t id, uint64_t *cp)
{
int fd, err;
ct_stathdl_t sh;
fd = contract_getfile(id, "status", O_RDONLY);
if (fd < 0)
return (-1);
err = ct_status_read(fd, CTD_COMMON, &sh);
if (err != 0) {
console(B_TRUE, "Couldn't read status of contract %ld: %s.\n",
id, strerror(err));
(void) close(fd);
return (-1);
}
(void) close(fd);
*cp = ct_status_get_cookie(sh);
ct_status_free(sh);
return (0);
}
static void
contract_ack(ct_evthdl_t e)
{
int fd;
if (ct_event_get_flags(e) & CTE_INFO)
return;
fd = contract_getfile(ct_event_get_ctid(e), "ctl", O_WRONLY);
if (fd < 0)
return;
(void) ct_ctl_ack(fd, ct_event_get_evid(e));
(void) close(fd);
}
static void
contract_event(struct pollfd *poll)
{
ct_evthdl_t e;
int err;
ctid_t ctid;
if (!(poll->revents & POLLIN)) {
if (poll->revents & POLLERR)
console(B_TRUE,
"Unknown poll error on my process contract "
"pbundle.\n");
return;
}
err = ct_event_read(poll->fd, &e);
if (err != 0) {
console(B_TRUE, "Error retrieving contract event: %s.\n",
strerror(err));
return;
}
ctid = ct_event_get_ctid(e);
if (ct_event_get_type(e) == CT_PR_EV_EMPTY) {
uint64_t cookie;
int ret, abandon = 1;
ret = contract_cookie(ctid, &cookie);
if (ret == 0) {
if (cookie == STARTD_COOKIE &&
do_restart_startd) {
if (smf_debug)
console(B_TRUE, "Restarting "
"svc.startd.\n");
startd_record_failure();
if (startd_failure_rate_critical())
enter_maintenance();
if (startd_tmpl < 0)
console(B_TRUE,
"Restarting svc.startd in "
"improper contract (bad "
"template).\n");
(void) startd_run(startd_cline, startd_tmpl,
ctid);
abandon = 0;
}
}
if (abandon && (err = contract_abandon_id(ctid))) {
console(B_TRUE, "Couldn't abandon contract %ld: %s.\n",
ctid, strerror(err));
}
} else {
console(B_TRUE,
"Received contract event of unexpected type %d from "
"contract %ld.\n", ct_event_get_type(e), ctid);
if ((ct_event_get_flags(e) & (CTE_INFO | CTE_ACK)) == 0)
contract_ack(e);
}
ct_event_free(e);
}
static int
startd_run(const char *cline, int tmpl, ctid_t old_ctid)
{
int err, i, ret, did_activate;
pid_t pid;
struct stat sb;
if (cline[0] == '\0')
return (-1);
do {
ret = stat("/etc/svc/volatile/resetting", &sb);
} while (ret == -1 && errno == EINTR);
if (ret == 0) {
if (smf_debug)
console(B_TRUE, "Quiescing for reboot.\n");
(void) pause();
return (-1);
}
err = ct_pr_tmpl_set_transfer(tmpl, old_ctid);
if (err == EINVAL) {
console(B_TRUE, "Remake startd_tmpl; reattempt transfer.\n");
tmpl = startd_tmpl = contract_make_template(0, CT_PR_EV_EMPTY,
CT_PR_EV_HWERR, STARTD_COOKIE);
err = ct_pr_tmpl_set_transfer(tmpl, old_ctid);
}
if (err != 0) {
console(B_TRUE,
"Couldn't set transfer parameter of contract template: "
"%s.\n", strerror(err));
}
if ((err = ct_pr_tmpl_set_svc_fmri(startd_tmpl,
SCF_SERVICE_STARTD)) != 0)
console(B_TRUE,
"Can not set svc_fmri in contract template: %s\n",
strerror(err));
if ((err = ct_pr_tmpl_set_svc_aux(startd_tmpl,
startd_svc_aux)) != 0)
console(B_TRUE,
"Can not set svc_aux in contract template: %s\n",
strerror(err));
did_activate = !(ct_tmpl_activate(tmpl));
if (!did_activate)
console(B_TRUE,
"Template activation failed; not starting \"%s\" in "
"proper contract.\n", cline);
(void) sighold(SIGCLD);
while ((pid = fork()) < 0) {
if (errno == EPERM) {
console(B_TRUE, "Insufficient permission to fork.\n");
exit(1);
}
console(B_TRUE,
"fork() for svc.startd failed: %s. Will retry in 1 "
"second...\n", strerror(errno));
(void) sleep(1);
}
if (pid == 0) {
for (i = SIGHUP; i <= SIGRTMAX; ++i) {
if (i == SIGTTOU || i == SIGTTIN || i == SIGTSTP)
(void) sigset(i, SIG_IGN);
else
(void) sigset(i, SIG_DFL);
}
if (smf_options != NULL) {
glob_envp[glob_envn] =
malloc(sizeof ("SMF_OPTIONS=") - 1 +
strlen(smf_options) + 1);
if (glob_envp[glob_envn] != NULL) {
(void) sprintf(glob_envp[glob_envn],
"SMF_OPTIONS=%s", smf_options);
glob_envp[glob_envn+1] = NULL;
} else {
console(B_TRUE,
"Could not set SMF_OPTIONS (%s).\n",
strerror(errno));
}
}
if (smf_debug)
console(B_TRUE, "Executing svc.startd\n");
(void) execle(SH, "INITSH", "-c", cline, NULL, glob_envp);
console(B_TRUE, "Could not exec \"%s\" (%s).\n", SH,
strerror(errno));
exit(1);
}
if (did_activate) {
if (legacy_tmpl < 0 || ct_tmpl_activate(legacy_tmpl) != 0)
(void) ct_tmpl_clear(tmpl);
}
if (old_ctid != 0)
(void) ct_pr_tmpl_set_transfer(tmpl, 0);
(void) sigrelse(SIGCLD);
return (0);
}
void
startd_record_failure()
{
int index = startd_failure_index++ % NSTARTD_FAILURE_TIMES;
startd_failure_time[index] = gethrtime();
}
int
startd_failure_rate_critical()
{
int n = startd_failure_index;
hrtime_t avg_ns = 0;
if (startd_failure_index < NSTARTD_FAILURE_TIMES)
return (0);
avg_ns =
(startd_failure_time[(n - 1) % NSTARTD_FAILURE_TIMES] -
startd_failure_time[n % NSTARTD_FAILURE_TIMES]) /
NSTARTD_FAILURE_TIMES;
return (avg_ns < STARTD_FAILURE_RATE_NS);
}
static char
*audit_boot_msg()
{
char *b, *p;
char desc[] = "booted";
zoneid_t zid = getzoneid();
b = malloc(sizeof (desc) + MAXNAMELEN + 3);
if (b == NULL)
return (b);
p = b;
p += strlcpy(p, desc, sizeof (desc));
if (zid != GLOBAL_ZONEID) {
p += strlcpy(p, ": ", 3);
(void) getzonenamebyid(zid, p, MAXNAMELEN);
}
return (b);
}
static int
audit_put_record(int pass_fail, int status, char *msg)
{
adt_session_data_t *ah;
adt_event_data_t *event;
if (!adt_audit_enabled())
return (0);
if (adt_start_session(&ah, NULL, ADT_USE_PROC_DATA)) {
console(B_TRUE, "audit failure: %s\n", strerror(errno));
return (1);
}
event = adt_alloc_event(ah, ADT_init_solaris);
if (event == NULL) {
console(B_TRUE, "audit failure: %s\n", strerror(errno));
(void) adt_end_session(ah);
return (1);
}
event->adt_init_solaris.info = msg;
if (adt_put_event(event, pass_fail, status)) {
console(B_TRUE, "audit failure: %s\n", strerror(errno));
(void) adt_end_session(ah);
return (1);
}
adt_free_event(event);
(void) adt_end_session(ah);
return (1);
}