5003 lines
122 KiB
C
5003 lines
122 KiB
C
/* Copyright (c) 1990, 1991 UNIX System Laboratories, Inc. */
|
|
/* Copyright (c) 1984, 1986, 1987, 1988, 1989, 1990 AT&T */
|
|
/* All Rights Reserved */
|
|
|
|
/* THIS IS UNPUBLISHED PROPRIETARY SOURCE CODE OF */
|
|
/* UNIX System Laboratories, Inc. */
|
|
/* The copyright notice above does not evidence any */
|
|
/* actual or intended publication of such source code. */
|
|
|
|
|
|
/*******************************************************************
|
|
|
|
PROPRIETARY NOTICE (Combined)
|
|
|
|
This source code is unpublished proprietary information
|
|
constituting, or derived under license from AT&T's UNIX(r) System V.
|
|
In addition, portions of such source code were derived from Berkeley
|
|
4.3 BSD under license from the Regents of the University of
|
|
California.
|
|
|
|
|
|
|
|
Copyright Notice
|
|
|
|
Notice of copyright on this source code product does not indicate
|
|
publication.
|
|
|
|
(c) 1986,1987,1988,1989 Sun Microsystems, Inc
|
|
(c) 1983,1984,1985,1986,1987,1988,1989 AT&T.
|
|
All rights reserved.
|
|
|
|
* "init" is the general process spawning program. It reads
|
|
* /etc/inittab for a script.
|
|
*
|
|
* General Note:The has been implemented to run with or without the Trusted
|
|
* Path driver configured on the system.
|
|
*
|
|
* In case of bugs, there are four flavors of debug available.
|
|
*
|
|
* UDEBUG Will generate a version of "init" that can
|
|
* be run as a user process. In this form,
|
|
* certain signals will cause core dumps and
|
|
* and a file called "debug" is written in the
|
|
* directory where "init" was started. It also
|
|
* reads the local directory for utmp, inittab
|
|
* and other administrative files. It also uses
|
|
* /dev/sysconx and /dev/systtyx instead of
|
|
* /dev/syscon and /dev/systty.
|
|
*
|
|
* DEBUG Generates an "init" which runs in the usual
|
|
* way, but generates a file, /etc/debug, with
|
|
* information about process removal, level
|
|
* changes, and accounting.
|
|
*
|
|
* DEBUG1 This symbol adds more debug to what would be
|
|
* generated by DEBUG or UDEBUG. It has
|
|
* detailed information about each process
|
|
* spawned from inittab. DEBUG1 by itself is
|
|
* equivalent to DEBUG and DEBUG1. It can be
|
|
* added to UDEBUG to get a user process version.
|
|
*
|
|
* ACCTDEBUG Generate debug from the accounting program
|
|
* only.
|
|
*/
|
|
|
|
|
|
#ident "$Revision: 1.73 $"
|
|
|
|
#ifdef ACCTDEBUG
|
|
#define DEBUGGER
|
|
#endif
|
|
|
|
#ifdef DEBUG
|
|
#ifndef DEBUGGER
|
|
#define DEBUGGER
|
|
#endif
|
|
#endif
|
|
|
|
#ifdef UDEBUG
|
|
#ifndef DEBUG
|
|
#define DEBUG
|
|
#endif
|
|
#ifndef ACCTDEBUG
|
|
#define ACCTDEBUG
|
|
#endif
|
|
#ifndef DEBUGGER
|
|
#define DEBUGGER
|
|
#endif
|
|
#endif
|
|
|
|
#ifdef DEBUG1
|
|
#ifndef DEBUG
|
|
#define DEBUG
|
|
#endif
|
|
#ifndef ACCTDEBUG
|
|
#define ACCTDEBUG
|
|
#endif
|
|
#ifndef DEBUGGER
|
|
#define DEBUGGER
|
|
#endif
|
|
#endif
|
|
|
|
#include <sys/types.h>
|
|
#include <signal.h>
|
|
#include <sys/uadmin.h>
|
|
#include <stdio.h>
|
|
#include <stdarg.h>
|
|
#include <stdlib.h>
|
|
#include <unistd.h>
|
|
#include <string.h>
|
|
#include <bstring.h>
|
|
#include <utmpx.h>
|
|
#include <errno.h>
|
|
#include <termio.h>
|
|
#include <sys/stat.h>
|
|
#include <ctype.h>
|
|
#include <fcntl.h>
|
|
#include <time.h>
|
|
#include <sys/stropts.h>
|
|
#include <sys/param.h>
|
|
#include <limits.h>
|
|
#include <sys/termios.h>
|
|
#include <sys/stream.h>
|
|
#include <sys/conf.h>
|
|
#include <sys/mkdev.h>
|
|
#include <pwd.h>
|
|
#include <paths.h>
|
|
#include <sys/sysmp.h>
|
|
#include <ulimit.h>
|
|
#include <sys/var.h>
|
|
#include <sys/wait.h>
|
|
#include <sat.h>
|
|
#include <sys/capability.h>
|
|
|
|
|
|
|
|
#define fioctl(p, sptr, cmd) ioctl(fileno(p), sptr, cmd)
|
|
|
|
#define TRUE 1
|
|
#define FALSE 0
|
|
#define FAILURE -1
|
|
#define SUCCESS 0
|
|
#define REBOOT 5
|
|
|
|
|
|
#define ERRBUFSZ (256)
|
|
#define COPYBUF(srcbuf, dstbuf, dstbufsz) {\
|
|
size_t srcbufsz; \
|
|
size_t dstremainbufsz; \
|
|
size_t catsz; \
|
|
\
|
|
if ((srcbuf != (char *)NULL) && (dstbuf != (char *)NULL)){ \
|
|
srcbufsz = strlen(srcbuf); \
|
|
dstremainbufsz = strlen(dstbuf) + 1; \
|
|
if (dstremainbufsz < dstbufsz){ \
|
|
dstremainbufsz = dstbufsz - dstremainbufsz; \
|
|
strncpy(dstbuf, srcbuf, ((srcbufsz <= dstremainbufsz) ? srcbufsz : dstremainbufsz)); \
|
|
} \
|
|
} \
|
|
}
|
|
|
|
/*
|
|
* SLEEPTIME The number of seconds "init" sleeps between wakeups if
|
|
* nothing else requires this "init" wakeup.
|
|
*/
|
|
#define SLEEPTIME 5*60
|
|
|
|
/*
|
|
* MAXCMDL The maximum length of a command string in inittab.
|
|
*/
|
|
#define MAXCMDL 512
|
|
|
|
/*
|
|
* EXEC The length of the prefix string added to all comamnds
|
|
* found in inittab.
|
|
*/
|
|
#define EXEC (sizeof("exec ") - 1)
|
|
|
|
/*
|
|
* TWARN The amount of time between warning signal, SIGTERM,
|
|
* and the fatal kill signal, SIGKILL.
|
|
*/
|
|
#define TWARN 5
|
|
|
|
/*
|
|
* WARNFREQUENCY The number of consecutive failures to find an empty slot in
|
|
* "init's" internal "proc_table" before another message will
|
|
* be generated.
|
|
*/
|
|
#define WARNFREQUENCY 25
|
|
|
|
#define id_eq(x,y) (( x[0] == y[0] && x[1] == y[1] && x[2] == y[2]\
|
|
&& x[3] == y[3] ) ? TRUE : FALSE)
|
|
|
|
#ifdef UDEBUG
|
|
|
|
pid_t SPECIALPID; /* Any pid can be made special for debugging */
|
|
|
|
#else
|
|
|
|
#define SPECIALPID 1 /* Normally the special pid is process 1 */
|
|
|
|
#endif
|
|
|
|
/*
|
|
* Correspondence of signals to init actions.
|
|
*/
|
|
#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
|
|
|
|
|
|
/*
|
|
* Bit Mask for each level. Used to determine legal levels.
|
|
*/
|
|
#define MASK0 01
|
|
#define MASK1 02
|
|
#define MASK2 04
|
|
#define MASK3 010
|
|
#define MASK4 020
|
|
#define MASK5 040
|
|
#define MASK6 0100
|
|
#define MASKSU 0200
|
|
#define MASKa 0400
|
|
#define MASKb 01000
|
|
#define MASKc 02000
|
|
|
|
#define NPROC_DEFAULT 200
|
|
|
|
/*
|
|
* Legal action field values.
|
|
*/
|
|
#define OFF 0 /* Kill process if on, else ignore */
|
|
#define RESPAWN 1 /* Continuously restart process when it dies */
|
|
#define ONDEMAND RESPAWN /* Respawn for a, b, c type processes */
|
|
#define ONCE 2 /* Start process, do not respawn when dead */
|
|
#define WAIT 3 /* Perform once and wait to complete */
|
|
#define BOOT 4 /* Start at boot time only */
|
|
#define BOOTWAIT 5 /* Start at boot time and wait to complete */
|
|
#define POWERFAIL 6 /* Start on powerfail */
|
|
#define POWERWAIT 7 /* Start and wait for complete on powerfail */
|
|
#define INITDEFAULT 8 /* Default level "init" should start at */
|
|
#define SYSINIT 9 /* Actions performed before init speaks */
|
|
|
|
#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
|
|
|
|
/*
|
|
* Init can be in any of three main states, "normal" mode where it is
|
|
* processing entries for the lines file in a normal fashion, "boot" mode,
|
|
* where it is only interested in the boot actions, and "powerfail" mode,
|
|
* where it is only interested in powerfail related actions. The following
|
|
* masks declare the legal actions for each mode.
|
|
*/
|
|
#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[4]; /* Four letter unique id of process */
|
|
pid_t p_pid; /* Process id */
|
|
short p_count; /* How many respawns of this command in */
|
|
/* the current series */
|
|
long p_time; /* Start time for a series of respawns */
|
|
short p_flags;
|
|
short p_exit; /* Exit status of a process which died */
|
|
};
|
|
|
|
int NPROC = NPROC_DEFAULT; /* size of active process table */
|
|
struct PROC_TABLE *proc_table; /* Table of active processes */
|
|
struct PROC_TABLE dummy; /* A zero table used when calling account() */
|
|
/* for non-process type accounting. */
|
|
|
|
/*
|
|
* Flags for the "p_flags" word of a PROC_TABLE entry:
|
|
*
|
|
* OCCUPIED This slot in init's proc table is in use.
|
|
*
|
|
* LIVING Process is alive.
|
|
*
|
|
* NOCLEANUP efork() is not allowed to cleanup this entry even
|
|
* if process is dead.
|
|
*
|
|
* NAMED This process has a name, i.e. came from inittab.
|
|
*
|
|
* DEMANDREQUEST Process started by a "telinit [abc]" command. Processes
|
|
* formed this way are respawnable and immune to level
|
|
* changes as long as their entry exists in inittab.
|
|
*
|
|
* TOUCHED Flag used by remv() to determine whether it has looked
|
|
* at an entry while checking for processes to be killed.
|
|
*
|
|
* WARNED Flag used by remv() to mark processes that have been
|
|
* sent the SIGTERM signal. If they don't die in 5
|
|
* seconds, they are sent the SIGKILL signal.
|
|
*
|
|
* KILLED Flag used by remv() to mark procs that have been sent
|
|
* the SIGTERM and SIGKILL signals.
|
|
*/
|
|
#define OCCUPIED 01
|
|
#define LIVING 02
|
|
#define NOCLEANUP 04
|
|
#define NAMED 010
|
|
#define DEMANDREQUEST 020
|
|
#define TOUCHED 040
|
|
#define WARNED 0100
|
|
#define KILLED 0200
|
|
|
|
/*
|
|
* Respawn limits for processes that are to be respawned:
|
|
*
|
|
* SPAWN_INTERVAL The number of seconds over which "init" will try to
|
|
* respawn a process SPAWN_LIMIT times before it gets mad.
|
|
*
|
|
* SPAWN_LIMIT The number of respawns "init" will attempt in
|
|
* SPAWN_INTERVAL seconds before it generates an
|
|
* error message and inhibits further tries for
|
|
* INHIBIT seconds.
|
|
*
|
|
* INHIBIT The number of seconds "init" ignores an entry it had
|
|
* trouble spawning unless a "telinit Q" is received.
|
|
*/
|
|
|
|
#define SPAWN_INTERVAL (2*60)
|
|
#define SPAWN_LIMIT 10
|
|
#define INHIBIT (5*60)
|
|
|
|
#define NULLPROC ((struct PROC_TABLE *)(0))
|
|
#define NO_ROOM ((struct PROC_TABLE *)(FAILURE))
|
|
|
|
struct CMD_LINE {
|
|
char c_id[4]; /* Four letter unique id of process to be */
|
|
/* affected by action */
|
|
short c_levels; /* Mask of legal levels for process */
|
|
short c_action; /* Mask for type of action required */
|
|
char *c_command; /* Pointer to init command */
|
|
};
|
|
|
|
/*
|
|
* Following are symbols for the types of errors for which "error_time" keeps
|
|
* timing entries. MAX_E_TYPES is the number of types currently being kept.
|
|
*/
|
|
#define FULLTABLE 0
|
|
#define BADLINE 1
|
|
|
|
#define MAX_E_TYPES 2
|
|
|
|
static struct ERRORTIMES {
|
|
long e_time; /* Time of last message */
|
|
long e_max; /* Amount of time to wait until next message */
|
|
} err_times[MAX_E_TYPES] = {0L, 120L,
|
|
0L, 120L};
|
|
|
|
struct pidrec {
|
|
int pd_type; /* Command type */
|
|
pid_t pd_pid; /* pid to add or remove */
|
|
};
|
|
|
|
/*
|
|
* pd_type's
|
|
*/
|
|
#define ADDPID 1
|
|
#define REMPID 2
|
|
|
|
struct pidlist {
|
|
pid_t pl_pid; /* pid to watch for */
|
|
int pl_dflag; /* Flag indicating SIGCLD from this pid */
|
|
short pl_exit; /* Exit status of proc */
|
|
struct pidlist *pl_next; /* Next in list */
|
|
} *Plhead, *Plfree;
|
|
|
|
/*
|
|
* The following structures contain a set of modes for consdevdsf.
|
|
*/
|
|
#define control(x) (x&037)
|
|
|
|
struct termio dflt_termio = {
|
|
BRKINT|IGNPAR|ISTRIP|IXON|IXANY|ICRNL,
|
|
OPOST|ONLCR|TAB3,
|
|
CLOCAL|CS8|CREAD,
|
|
ISIG|ICANON|ECHO|ECHOK|ECHOE|ECHOKE|ECHOCTL
|
|
#ifdef IEXTEN
|
|
|IEXTEN
|
|
#endif
|
|
,SSPEED,0,LDISC1,
|
|
#if defined(_STYPES_LATER)
|
|
{CINTR,CQUIT,CERASE,CKILL,CEOF,0,0,CNSWTCH,
|
|
0,0,0, 0,0,0,0,
|
|
CLNEXT,CWERASE,CRPRNT,CFLUSH, CSTOP, CSTART},
|
|
#else /* !_STYPES_LATER */
|
|
{CINTR, /* 0 */
|
|
CQUIT,
|
|
CERASE,
|
|
CKILL,
|
|
CEOF,
|
|
0, /* 5 */
|
|
0,
|
|
CNSWTCH,
|
|
CSTART,
|
|
CSTOP,
|
|
CNSWTCH, /* 10 */
|
|
0,
|
|
CRPRNT,
|
|
CFLUSH,
|
|
CWERASE,
|
|
CLNEXT, /* 15 */
|
|
0,
|
|
0,
|
|
0,
|
|
0,
|
|
0, /* 20 */
|
|
0,
|
|
0}, /* 22 */
|
|
#endif /* !_STYPES_LATER */
|
|
};
|
|
|
|
struct termio termio, curterm;
|
|
|
|
|
|
union WAKEUP {
|
|
struct WAKEFLAGS {
|
|
unsigned w_usersignal : 1; /* User sent signal to "init" */
|
|
unsigned w_childdeath : 1; /* An "init" child died */
|
|
unsigned w_powerhit : 1; /* OS experienced powerfail */
|
|
} w_flags;
|
|
int w_mask;
|
|
} wakeup;
|
|
|
|
/*
|
|
* flag definitions for getttyname()
|
|
*/
|
|
#define GETTTYNM_ONLYTP (0x01)
|
|
#define REAL_CONSDEVMAJNUM (58)
|
|
#define REAL_CONSDEVMINNUM (0)
|
|
|
|
/*
|
|
* Level definitions if MAC is not installed.
|
|
*/
|
|
#define SYS_PRIVATE_LVL ((level_t)2)
|
|
#define SYS_PUBLIC_LVL ((level_t)1)
|
|
|
|
/*
|
|
* Useful file and device names.
|
|
*/
|
|
|
|
char *CONSOLE = "/dev/console"; /* Real system console */
|
|
char *INITPIPE = "/etc/.initpipe";
|
|
/*
|
|
* When init s|S is entered, need to leave a "cookie" for the real init
|
|
* (pid == 1) to set up a Trusted Path. "/dev/sysconreal" will be linked
|
|
* to the real/physical tty device linked under a TP device.
|
|
*/
|
|
char *SYSCONREAL = "/dev/sysconreal";
|
|
|
|
#ifdef UDEBUG
|
|
|
|
char *UTMP = "utmp";
|
|
char *WTMP = "wtmp";
|
|
char *UTMPX = "utmpx";
|
|
char *WTMPX = "wtmpx";
|
|
char *INITTAB = "inittab";
|
|
char *SYSTTY = "/dev/systtyx";
|
|
char *SYSCON = "/dev/sysconx";
|
|
char *CORE_RECORD = "core_record";
|
|
char *DBG_FILE = "debug";
|
|
char *IOCTLSYSCON = "ioctl.syscon"; /* Last syscon modes */
|
|
char *ENVFILE = "/etc/TIMEZONE";
|
|
|
|
#else
|
|
|
|
|
|
char *UTMP = UTMP_FILE; /* Snapshot record file */
|
|
char *WTMP = WTMP_FILE; /* Long term record file */
|
|
char *UTMPX = UTMPX_FILE; /* Snapshot record file */
|
|
char *WTMPX = WTMPX_FILE; /* Long term record file */
|
|
char *INITTAB = "/etc/inittab"; /* Script file for "init" */
|
|
char *SYSTTY = "/dev/systty"; /* System Console */
|
|
char *SYSCON = "/dev/syscon"; /* Virtual System console */
|
|
char *IOCTLSYSCON = "/etc/ioctl.syscon"; /* Last syscon modes */
|
|
char *ENVFILE = "/etc/TIMEZONE";
|
|
|
|
#ifdef DEBUGGER
|
|
char *DBG_FILE = "/etc/debug";
|
|
#endif
|
|
|
|
#endif
|
|
|
|
|
|
/* list of super-user programs to try for single user.
|
|
* Try for sulogin first, but if it fails because it isn't there,
|
|
* try the other standard shells as well. /sbin/sulogin must
|
|
* be first in the list.
|
|
*/
|
|
char *singleuser[] = { "/sbin/sulogin", _PATH_CSHELL, _PATH_BSHELL,
|
|
"/bin/ksh", 0 };
|
|
|
|
char *SH = _PATH_BSHELL; /* Standard Shell */
|
|
|
|
|
|
|
|
/*
|
|
* Index tags for init's required Device Special Files defined in devtab
|
|
*/
|
|
#define DTTAG_SYSTTY (0)
|
|
#define DTTAG_CONSOLE (1)
|
|
#define DTTAG_SYSCON (2)
|
|
#define DTTAG_SYSCONREAL (3)
|
|
|
|
/*
|
|
* Required Device Special Files for init
|
|
* NOTE: Do not change order of entries with making cooresponding changes
|
|
* to DTTAG_**** defines
|
|
*/
|
|
enum devtabstatus {
|
|
dtsUNVERIFIED, /* initial status of devtab entry. */
|
|
dtsVERIFIED, /* state of devtab entry if verified to exist. */
|
|
dtsIGNORE, /* indicates to ingore devtab entry */
|
|
dtsENOENT, /* state of devtab entry if DSF does not exist. */
|
|
dtsWRONGDEV /* state of devtab entry if DSF Major and or Minor
|
|
* device number does not match major and or minor
|
|
* device number in devtab entry.
|
|
*/
|
|
};
|
|
enum devtabaction{
|
|
dtaNOACTION, /* indicates no action is to be performed on/for
|
|
* devtab entry.
|
|
*/
|
|
dtaMKNOD, /* indicates that a DSF needs to be created for the
|
|
* devtab entry. associated with dtsENOENT and
|
|
* dtsWRONGDEV statuses.
|
|
*/
|
|
dtaLINK /* indicates that DSF needs to be linked to DSF
|
|
* specified in dt_linkdsfname field in devtab entry.
|
|
* associated with dtsENOENT and dtsWRONGDEV statuses.
|
|
*/
|
|
};
|
|
struct devicetable {
|
|
char *dt_dsfname;
|
|
char *dt_linkdsfname;/* if action is dtaLINK,
|
|
* indicates the dsf name
|
|
* dt_dfsname will link to.
|
|
*/
|
|
major_t dt_major;
|
|
minor_t dt_minor;
|
|
enum devtabstatus dt_status;
|
|
enum devtabaction dt_action;
|
|
short dt_tag; /* tag to identify devtab entry.
|
|
* also indicates index of
|
|
* devtab entry.
|
|
*/
|
|
};
|
|
struct devicetable devtab[] = {
|
|
(char *)NULL, (char *)NULL, REAL_CONSDEVMAJNUM, REAL_CONSDEVMINNUM,
|
|
dtsUNVERIFIED, dtaNOACTION, DTTAG_SYSTTY,
|
|
(char *)NULL, (char *)NULL, (major_t)NODEV, (minor_t)NODEV,
|
|
dtsUNVERIFIED, dtaNOACTION, DTTAG_CONSOLE,
|
|
(char *)NULL, (char *)NULL, (major_t)NODEV, (minor_t)NODEV,
|
|
dtsUNVERIFIED, dtaNOACTION, DTTAG_SYSCON,
|
|
(char *)NULL, (char *)NULL, (major_t)NODEV, (minor_t)NODEV,
|
|
dtsUNVERIFIED, dtaNOACTION, DTTAG_SYSCONREAL,
|
|
"", (char *)NULL, (major_t)NODEV, (minor_t)NODEV,
|
|
dtsUNVERIFIED, dtaNOACTION, -1
|
|
};
|
|
|
|
int n_prev[NSIG-1]; /* Number of times previously in state */
|
|
int cur_state = -1; /* Current state of "init" */
|
|
int prior_state;
|
|
int prev_state; /* State "init" was in last time it woke */
|
|
int new_state; /* State user wants "init" to go to. */
|
|
int op_modes = BOOT_MODES; /* Current state of "init" */
|
|
int Pfd = -1; /* fd to receive pids thru */
|
|
unsigned int spawncnt, pausecnt;
|
|
int rsflag; /* Set if a respawn has taken place */
|
|
pid_t own_pid; /* This is the value of our own pid. If the value */
|
|
/* is SPECIALPID, then we have to fork to interact */
|
|
/* with the outside world. */
|
|
int time_up; /* Flag set to TRUE by alarm interupt routine */
|
|
/* each time an alarm interupt takes place. */
|
|
int fd_systty;
|
|
|
|
#ifdef DEBUG
|
|
char comment[120];
|
|
#endif
|
|
|
|
/*
|
|
* Array for default global environment.
|
|
*/
|
|
#define MAXENVENT 6 /* Max number of default env variables + 1 */
|
|
char *glob_envp[MAXENVENT]; /* Array of environment strings */
|
|
|
|
|
|
static char *consdevdsf =
|
|
(char *)NULL; /* indicates where console message
|
|
* output is to be displayed.
|
|
*/
|
|
static char *substitutedsf =
|
|
(char *)NULL; /* indicates whether or not the device
|
|
* name on /etc/inittab command line
|
|
* sysinit entries should be
|
|
* substituted. NULL means no.
|
|
*/
|
|
/*
|
|
* Indicates whether or not the root file system has been verified to be
|
|
* sane.
|
|
*/
|
|
static int rootfs_verified = FALSE;
|
|
|
|
FILE *fdup(FILE *);
|
|
char *prog_name(char *);
|
|
char level(int);
|
|
int error_time(int);
|
|
int getcmd(struct CMD_LINE *);
|
|
int getlvl(void);
|
|
int initialize(void);
|
|
int main(int, char **);
|
|
int mask(int);
|
|
int realcon(void);
|
|
int setup_consdev(char *);
|
|
int spawn(void);
|
|
int substitutedev(struct CMD_LINE *, char *, char *);
|
|
int update_devtab(void);
|
|
int v_pgm(char *);
|
|
long waitproc(struct PROC_TABLE *);
|
|
struct PROC_TABLE *efork(int, struct PROC_TABLE *, int);
|
|
struct PROC_TABLE *findpslot(struct CMD_LINE *);
|
|
void account(short, struct PROC_TABLE *, char *);
|
|
void alarmclk(void);
|
|
void childeath(int);
|
|
void cleanaux(void);
|
|
void clearent(pid_t, short);
|
|
void console(char *, ...);
|
|
void firmware(void);
|
|
void get_inittab(void);
|
|
void get_ioctl_syscon(void);
|
|
void handle_children(int);
|
|
void init_signals(void);
|
|
void initialize_devtab(void);
|
|
void killproc(pid_t);
|
|
void opensyscon(void);
|
|
void powerfail(void);
|
|
void remv(void);
|
|
void reset_modes(void);
|
|
void reset_syscon(void);
|
|
void respawn(struct PROC_TABLE *, struct CMD_LINE *);
|
|
void setimer(int);
|
|
void sig_poll(int);
|
|
void siglvl(int);
|
|
void single(int);
|
|
void switchcon(int);
|
|
void timer(int);
|
|
void userinit(int, char **);
|
|
void verify_devtab(void);
|
|
static void wr_ioctlsyscon(FILE *);
|
|
static int rd_ioctlsyscon(FILE *);
|
|
static int validsyscon(char *);
|
|
|
|
int cap_enabled;
|
|
cap_t cap;
|
|
|
|
static int validsyscon(char*);
|
|
static void wr_ioctlsyscon(FILE*);
|
|
static int rd_ioctlsyscon(FILE*);
|
|
|
|
#ifdef UDEBUG
|
|
void drop_core(char *);
|
|
#endif
|
|
|
|
#ifdef DEBUGGER
|
|
char *C(char *);
|
|
void debug(char *, ...);
|
|
#endif
|
|
|
|
|
|
extern int doavailmon(char state);
|
|
extern int argvtostr(char **); /* no prototype in headers.... */
|
|
|
|
int cap_enabled;
|
|
cap_t cap;
|
|
|
|
/*
|
|
* Procedure: main
|
|
*
|
|
* Restrictions:
|
|
unlink(2): None
|
|
mknod(2): None
|
|
open(2): None
|
|
ioctl(2): None
|
|
fopen: None
|
|
fclose: None
|
|
fcntl(2): None
|
|
*/
|
|
|
|
|
|
main(int argc, char **argv)
|
|
{
|
|
int defaultlevel;
|
|
int utmpflag = 0;
|
|
int chg_lvl_flag;
|
|
FILE *fp, *fp2;
|
|
mode_t old_umask;
|
|
extern int errno;
|
|
#ifdef DEBUG
|
|
debug("%d:main():ENTER\n",getpid());
|
|
#endif
|
|
|
|
if ((cap_enabled = sysconf(_SC_CAP)) > CAP_SYS_DISABLED)
|
|
cap = cap_init();
|
|
|
|
#ifdef UDEBUG
|
|
if (argc == 1)
|
|
SPECIALPID = getpid();
|
|
#endif
|
|
|
|
/*
|
|
* Determine if we are process 1, the main init, or a user
|
|
* invoked init, whose job it is to inform init to change
|
|
* levels or perform some other action.
|
|
*/
|
|
if ((own_pid = getpid()) != SPECIALPID)
|
|
userinit(argc, argv);
|
|
|
|
/*
|
|
* Set up the initial states and see if there is a default
|
|
* level supplied in the inittab file.
|
|
*/
|
|
defaultlevel = initialize();
|
|
chg_lvl_flag = FALSE;
|
|
|
|
#ifdef DEBUG
|
|
console("Debug version of init starting-pid = %ld\n", SPECIALPID);
|
|
#endif
|
|
|
|
/*
|
|
* Set up pipe for "godchildren".
|
|
*/
|
|
(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);
|
|
/*
|
|
* Read pipe in message discard mode.
|
|
*/
|
|
(void)ioctl(Pfd, I_SRDOPT, RMSGD);
|
|
sigset(SIGPOLL, sig_poll);
|
|
}
|
|
|
|
/*
|
|
* Initialize the "utmp" and "utmpx" files. Set the umask so
|
|
* that the utmp and utmpx files are created 644.
|
|
* If "utmp" can't be opened we default to single user mode.
|
|
*/
|
|
old_umask = umask(022);
|
|
if ((fp = fopen(UTMP,"w+")) == NULL ||
|
|
(fp2 = fopen(UTMPX, "w+")) == NULL) {
|
|
console("Cannot create %s or %s\n", UTMP, UTMPX);
|
|
cur_state = SINGLE_USER;
|
|
defaultlevel = -1;
|
|
} else {
|
|
utmpflag = 1;
|
|
fclose(fp);
|
|
fclose(fp2);
|
|
}
|
|
umask(old_umask); /* put back original umask */
|
|
|
|
/*
|
|
* If there is no default level supplied, ask the user to supply one.
|
|
*/
|
|
if (defaultlevel == 0) {
|
|
new_state = getlvl();
|
|
} else if (defaultlevel == -1) {
|
|
new_state = SINGLE_USER;
|
|
defaultlevel = 0;
|
|
} else {
|
|
new_state = defaultlevel;
|
|
}
|
|
if (new_state == SINGLE_USER) {
|
|
account(BOOT_TIME, &dummy, NULL); /* Put Boot Entry in "utmpx" */
|
|
account(RUN_LVL, &dummy, "S"); /* Make the run level entry */
|
|
single(defaultlevel);
|
|
while (utmpflag == 0) {
|
|
/*
|
|
* We went to single user because we couldn't get at
|
|
* "utmpx" earlier. Since we have returned from single()
|
|
* we should be able to get there now. Truncate the
|
|
* file now. The boot time we will write may be off
|
|
* since we may have been up for a while but this is the
|
|
* best we can do under the circumstances. If we still
|
|
* can't get at "utmpx" we keep going back to single
|
|
* user until we can.
|
|
*/
|
|
old_umask = umask(022);
|
|
if ((fp = fopen(UTMP,"w+")) == NULL ||
|
|
(fp2 = fopen(UTMPX, "w+")) == NULL) {
|
|
console("Cannot create %s or %s\n",UTMP,UTMPX);
|
|
new_state = cur_state = SINGLE_USER;
|
|
/* put back original umask */
|
|
umask(old_umask);
|
|
single(0);
|
|
} else {
|
|
utmpflag = 1;
|
|
fclose(fp);
|
|
fclose(fp2);
|
|
/* put back original umask */
|
|
umask(old_umask);
|
|
account(BOOT_TIME, &dummy, NULL);
|
|
}
|
|
}
|
|
chg_lvl_flag = TRUE;
|
|
} else {
|
|
prev_state = cur_state;
|
|
if(cur_state >= 0) {
|
|
n_prev[cur_state]++;
|
|
prior_state = cur_state;
|
|
}
|
|
cur_state = new_state;
|
|
account(BOOT_TIME, &dummy, NULL); /* Put Boot Entry in "utmp" */
|
|
account(RUN_LVL, &dummy, NULL); /* Make the run level entry */
|
|
}
|
|
|
|
/*
|
|
* Here is the beginning of the main process loop.
|
|
*/
|
|
for (;;) {
|
|
/*
|
|
* If in "normal" mode, check all living processes and initiate
|
|
* kill sequence on those that should not be there anymore.
|
|
*/
|
|
if (op_modes == NORMAL_MODES && cur_state != LVLa &&
|
|
cur_state != LVLb && cur_state != LVLc)
|
|
remv();
|
|
|
|
/*
|
|
* If a change in run levels is the reason we awoke, now do
|
|
* the accounting to report the change in the utmp file.
|
|
* Also report the change on the system console.
|
|
*/
|
|
if (chg_lvl_flag) {
|
|
chg_lvl_flag = FALSE;
|
|
account(RUN_LVL, &dummy, NULL);
|
|
console("New run level: %c\n", level(cur_state));
|
|
}
|
|
|
|
/*
|
|
* If new level is 0, close the utmp file, otherwise
|
|
* it's still open when the system halts and blocks
|
|
* get lost owing to the fact that efs preallocates!
|
|
*/
|
|
|
|
if (cur_state == LVL0)
|
|
endutent();
|
|
|
|
/*
|
|
* Scan the inittab file and spawn and respawn processes that
|
|
* should be alive in the current state. If inittab does not
|
|
* exist default to single user mode.
|
|
*/
|
|
if (spawn() == FAILURE) {
|
|
prior_state = prev_state;
|
|
cur_state = SINGLE_USER;
|
|
}
|
|
|
|
if (rsflag) {
|
|
rsflag = 0;
|
|
spawncnt++;
|
|
}
|
|
|
|
if (cur_state == SINGLE_USER) {
|
|
account(RUN_LVL, &dummy, NULL);
|
|
single(0);
|
|
if (cur_state != prev_state &&
|
|
cur_state != LVLa && cur_state != LVLb &&
|
|
cur_state != LVLc) {
|
|
chg_lvl_flag = TRUE;
|
|
continue;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* If a powerfail signal was received during the last
|
|
* sequence, set mode to powerfail. When spawn() is entered
|
|
* the first thing it does is to check "powerhit". If it is
|
|
* in PF_MODES then it clears "powerhit" and does a powerfail
|
|
* sequence. If it is not in PF_MODES, then it puts itself
|
|
* in PF_MODES and then clears "powerhit". Should "powerhit"
|
|
* get set again while spawn() is working on a powerfail
|
|
* sequence, the following code will see that spawn() tries to
|
|
* execute the powerfail sequence again. This guarantees that
|
|
* the powerfail sequence will be successfully completed before
|
|
* further processing takes place.
|
|
*/
|
|
if (wakeup.w_flags.w_powerhit) {
|
|
op_modes = PF_MODES;
|
|
/*
|
|
* Make sure that cur_state != prev_state so that
|
|
* ONCE and WAIT types work.
|
|
*/
|
|
prev_state = 0;
|
|
} else if (op_modes != NORMAL_MODES) {
|
|
/*
|
|
* If spawn() was not just called while in normal mode,
|
|
* we set the mode to normal and it will be called again
|
|
* to check normal modes. If we have just finished
|
|
* a powerfail sequence with prev_state equal to zero,
|
|
* we set prev_state equal to cur_state before the next
|
|
* pass through.
|
|
*/
|
|
if (op_modes == PF_MODES)
|
|
prev_state = cur_state;
|
|
op_modes = NORMAL_MODES;
|
|
} else if (cur_state == LVLa || cur_state == LVLb ||
|
|
cur_state == LVLc) {
|
|
/*
|
|
* If it was a change of levels that awakened us and the
|
|
* new level is one of the demand levels then reset
|
|
* cur_state to the previous state and do another scan
|
|
* to take care of the usual respawn actions.
|
|
*/
|
|
n_prev[cur_state]++;
|
|
cur_state = prior_state;
|
|
prior_state = prev_state;
|
|
prev_state = cur_state;
|
|
account(RUN_LVL, &dummy, NULL);
|
|
} else {
|
|
prev_state = cur_state;
|
|
|
|
/*
|
|
* "init" is finished with all actions for
|
|
* the current wakeup.
|
|
* The timer is here mainly for
|
|
* paranoic defensiveness to force init to
|
|
* wake up periodically - just in case. 5
|
|
* minutes is a long enough interval for the
|
|
* cost to be very low.
|
|
*/
|
|
sighold(SIGCLD);
|
|
if (wakeup.w_mask == 0) {
|
|
setimer(SLEEPTIME);
|
|
sigpause(SIGCLD);
|
|
setimer(0);
|
|
}
|
|
else
|
|
sigrelse(SIGCLD);
|
|
|
|
if(wakeup.w_flags.w_childdeath)
|
|
handle_children(0);
|
|
|
|
if (wakeup.w_flags.w_usersignal) {
|
|
wakeup.w_flags.w_usersignal = 0;
|
|
/*
|
|
* Install the new level. This could be a real
|
|
* change in levels or a telinit [Q|a|b|c] or
|
|
* just a telinit to the same level at which
|
|
* we are running.
|
|
*/
|
|
#ifdef DEBUG
|
|
debug("\nmain\tSignal-new:%c cur:%c prev:%c\n",
|
|
level(new_state), level(cur_state),
|
|
level(prev_state));
|
|
#endif
|
|
|
|
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;
|
|
account(RUN_LVL, &dummy, NULL);
|
|
} else {
|
|
prev_state = cur_state;
|
|
if(cur_state >= 0) {
|
|
n_prev[cur_state]++;
|
|
prior_state = cur_state;
|
|
}
|
|
cur_state = new_state;
|
|
chg_lvl_flag = TRUE;
|
|
}
|
|
}
|
|
|
|
if (new_state == SINGLE_USER)
|
|
reset_modes();
|
|
new_state = 0;
|
|
}
|
|
|
|
if (wakeup.w_flags.w_powerhit) {
|
|
wakeup.w_flags.w_powerhit = 0;
|
|
op_modes = PF_MODES;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
* Procedure: single
|
|
*
|
|
* Restrictions:
|
|
kill(2): None
|
|
open(2): None
|
|
fcntl(2): None
|
|
auditdmp(2): None
|
|
access(2): None
|
|
*/
|
|
void
|
|
single(int defaultlevel)
|
|
{
|
|
register struct PROC_TABLE *su_process;
|
|
register struct pidlist *p;
|
|
int state;
|
|
int remainsingle = FALSE;
|
|
int consfd = -1;
|
|
char **theshell;
|
|
char *arg;
|
|
|
|
#ifdef DEBUG
|
|
debug("%d:single():ENTER\n",getpid());
|
|
#endif
|
|
|
|
sighold(SIGPOLL);
|
|
/*
|
|
* Go through the godchild list and send SIGTERM, then wait for
|
|
* TWARN seconds and send SIGKILL to anything that is left.
|
|
* Also wait a little bit before doing this so SIGCLDs hopefully
|
|
* go to the right process.
|
|
*/
|
|
if (Plhead) {
|
|
int alive = 0;
|
|
int t, s;
|
|
|
|
for (s = SIGTERM, t = TWARN; t; t -= 1) {
|
|
for (p = Plhead; p; p = p->pl_next)
|
|
if (!kill(p->pl_pid, s))
|
|
alive = 1;
|
|
if (alive) {
|
|
s = 0;
|
|
timer(1);
|
|
} else
|
|
break;
|
|
}
|
|
|
|
if(wakeup.w_flags.w_childdeath)
|
|
handle_children(0);
|
|
cleanaux(); /* Clean up utmp entries. */
|
|
|
|
/*
|
|
* Any left?
|
|
*/
|
|
if (Plhead) {
|
|
alive = 0;
|
|
for (p = Plhead; p; p = p->pl_next)
|
|
if (!kill(p->pl_pid, SIGKILL))
|
|
alive = 1;
|
|
if (alive)
|
|
timer(3);/* Give them a little time to die. */
|
|
}
|
|
if(wakeup.w_flags.w_childdeath)
|
|
handle_children(0);
|
|
cleanaux(); /* Clean up utmp entries. */
|
|
}
|
|
sigrelse(SIGPOLL);
|
|
|
|
for (;;) {
|
|
struct passwd *rootpass;
|
|
struct stat shst;
|
|
|
|
/* To handle the case where /bin/su can be
|
|
* exec'ed, but fails because /etc/passwd is missing, or
|
|
* has no valid entry for root, we have to try
|
|
* to handle this by checking with getpwnam on "root", and
|
|
* explictly testing for its shell. If that fails, we skip
|
|
* the su attempt, and just try the shells. This somewhat
|
|
* gross hack is needed because looking at the exit status
|
|
* of 'su' doesn't work, because the exit status will usually
|
|
* be the exit value of the last command run within the single
|
|
* user shell (when su works). Try each time we start over
|
|
* in case someone has fixed it up after getting a shell.
|
|
*/
|
|
|
|
if (rootpass = getpwnam("root")) {
|
|
if (!rootpass->pw_shell[0]) /* do as su does */
|
|
rootpass->pw_shell = _PATH_BSHELL;
|
|
/* fail if not there, or not executable */
|
|
if (stat(rootpass->pw_shell, &shst) ||
|
|
!(shst.st_mode & 01111))
|
|
rootpass = NULL;
|
|
}
|
|
|
|
if (!rootpass) {
|
|
theshell = &singleuser[1];
|
|
arg = NULL;
|
|
console("Warning: /etc/passwd damaged, or root shell bad\n");
|
|
} else {
|
|
theshell = singleuser;
|
|
arg = "-"; /* only for su, which must be first */
|
|
}
|
|
|
|
console("SINGLE USER MODE\n");
|
|
while((su_process = efork(M_OFF,NULLPROC,NOCLEANUP)) == NO_ROOM)
|
|
pause();
|
|
if (su_process == NULLPROC) {
|
|
#ifdef DEBUG
|
|
debug("%d:single():CHILD PROCESS consdevdsf = %s\n",getpid(), consdevdsf);
|
|
#endif
|
|
opensyscon();
|
|
sigset(SIGCLD, SIG_DFL); /* paranoia */
|
|
arg = "-";
|
|
if (cap_enabled && cap_set_proc(cap) < 0) {
|
|
console("Can't clear capability set\n");
|
|
exit(1);
|
|
}
|
|
while(*theshell) {
|
|
execlp(*theshell, *theshell, arg, 0);
|
|
console("Can't start %s: %s\n",*theshell,strerror(errno));
|
|
arg = NULL;
|
|
theshell++;
|
|
}
|
|
timer(5);
|
|
(void)close(consfd);
|
|
exit(1);
|
|
}
|
|
|
|
/*
|
|
* If we are the parent, wait around for the child to die
|
|
* or for "init" to be signaled to change levels.
|
|
*/
|
|
while (waitproc(su_process) == FAILURE) {
|
|
/*
|
|
* Did we waken because a change of levels?
|
|
* If so, kill the child and then exit.
|
|
*/
|
|
if (wakeup.w_flags.w_usersignal) {
|
|
wakeup.w_flags.w_usersignal = 0;
|
|
#ifdef DEBUG
|
|
debug("%d:single():waitproc() returned (CASE: NOT BY MY CHILD):A CHILD INIT SENT A SIGNAL\n",getpid());
|
|
#endif
|
|
if (new_state >= LVL0 && new_state <= LVL6) {
|
|
/*
|
|
* Check to make sure UTMPX is
|
|
* accessible. If not, remain in
|
|
* single user mode.
|
|
*/
|
|
if (access(UTMPX, F_OK) == -1) {
|
|
console("Cannot access %s,\
|
|
remaining in single user mode.\n", UTMPX);
|
|
}
|
|
kill(su_process->p_pid,SIGKILL);
|
|
prev_state = cur_state;
|
|
if(cur_state >= 0) {
|
|
n_prev[cur_state]++;
|
|
prior_state = cur_state;
|
|
}
|
|
cur_state = new_state;
|
|
new_state = 0;
|
|
su_process->p_flags&=~NOCLEANUP;
|
|
#ifdef DEBUG
|
|
debug("%d:single():waitproc() returned (CASE: NOT BY MY CHILD):RETURN \n",getpid());
|
|
#endif
|
|
return;
|
|
}
|
|
}
|
|
/* even in single user mode, reap orphans, etc. */
|
|
if(wakeup.w_flags.w_childdeath)
|
|
handle_children(0);
|
|
}
|
|
#ifdef DEBUG
|
|
debug("%d:single():waitproc() returned (CASE: MY CHILD )\n",getpid());
|
|
#endif
|
|
|
|
if (remainsingle == TRUE){
|
|
state = SINGLE_USER;
|
|
}else if (defaultlevel != 0){
|
|
state = defaultlevel;
|
|
}else{
|
|
state = getlvl();
|
|
}
|
|
|
|
/*
|
|
* If the new level is not SINGLE_USER, then return,
|
|
* otherwise go back and make up a new "su" process.
|
|
*/
|
|
if (state != SINGLE_USER) {
|
|
#ifdef DEBUG
|
|
debug("%d:single():state != SINGLE_USER\n",getpid());
|
|
#endif
|
|
/*
|
|
* Check to make sure UTMPX is accessible.
|
|
* If not, remain in single user mode.
|
|
*/
|
|
if (access(UTMPX, F_OK) == -1) {
|
|
#ifdef DEBUG
|
|
debug("%d:single():ACCESS %s FAILED REMAIN SINGLE_USER\n",getpid(), UTMPX);
|
|
#endif
|
|
console("Cannot access %s,\
|
|
remaining in single user mode.\n", UTMPX);
|
|
} else {
|
|
prev_state = cur_state;
|
|
if(cur_state >= 0) {
|
|
n_prev[cur_state]++;
|
|
prior_state = cur_state;
|
|
}
|
|
cur_state = state;
|
|
#ifdef DEBUG
|
|
debug("%d:single():RETURN\tconsdevdsf = %s\n", getpid(), consdevdsf);
|
|
#endif
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
* Procedure: remv
|
|
*
|
|
* Restrictions:
|
|
kill(2): None
|
|
*/
|
|
|
|
/*
|
|
* remv() scans through "proc_table" and performs cleanup. If
|
|
* there is a process in the table, which shouldn't be here at
|
|
* the current run level, then remv() kills the process.
|
|
*/
|
|
void
|
|
remv(void)
|
|
{
|
|
register struct PROC_TABLE *process;
|
|
struct CMD_LINE cmd;
|
|
int change_level;
|
|
|
|
change_level = (cur_state != prev_state ? TRUE : FALSE);
|
|
|
|
/*
|
|
* Clear the TOUCHED flag on all entries so that when we have
|
|
* finished scanning inittab, we will be able to tell if we
|
|
* have any processes for which there is no entry in inittab.
|
|
*/
|
|
for (process = &proc_table[0]; process < &proc_table[NPROC]; process++)
|
|
process->p_flags &= ~TOUCHED;
|
|
|
|
/*
|
|
* Scan all inittab entries.
|
|
*/
|
|
while (getcmd(&cmd) == TRUE) {
|
|
/*
|
|
* Scan for process which goes with this entry in inittab.
|
|
*/
|
|
for (process= &proc_table[0]; process < &proc_table[NPROC];
|
|
process++) {
|
|
/*
|
|
* Does this slot contain the proc we are looking for?
|
|
*/
|
|
if ((process->p_flags & OCCUPIED) &&
|
|
id_eq(process->p_id, cmd.c_id)) {
|
|
#ifdef DEBUG
|
|
debug("remv- id:%s pid:%ld time:%lo %d %o %o\n",
|
|
C(&process->p_id[0]), process->p_pid,
|
|
process->p_time, process->p_count,
|
|
process->p_flags, process->p_exit);
|
|
#endif
|
|
/*
|
|
* Is the cur_state SINGLE_USER or is this
|
|
* process marked as "off" or was this proc
|
|
* started by some mechanism other than
|
|
* LVL{a|b|c} and the current level does
|
|
* not support this process?
|
|
*/
|
|
if (cur_state == SINGLE_USER ||
|
|
cmd.c_action == M_OFF ||
|
|
((cmd.c_levels & mask(cur_state)) == 0 &&
|
|
(process->p_flags & DEMANDREQUEST) == 0)) {
|
|
|
|
if (process->p_flags & LIVING) {
|
|
/*
|
|
* Touch this entry so we know we have
|
|
* treated it. Note that procs which are
|
|
* already dead at this point and should
|
|
* not be restarted are left untouched.
|
|
* This causes their slot to be freed later
|
|
* after dead accounting is done.
|
|
*/
|
|
process->p_flags |= TOUCHED;
|
|
|
|
if ((process->p_flags & KILLED) == 0) {
|
|
if (change_level) {
|
|
process->p_flags |= WARNED;
|
|
kill(process->p_pid, SIGTERM);
|
|
} else {
|
|
/*
|
|
* fork a killing proc so "init"
|
|
* can continue without having
|
|
* to pause for TWARN seconds.
|
|
*/
|
|
killproc(process->p_pid);
|
|
}
|
|
process->p_flags |= KILLED;
|
|
}
|
|
}
|
|
|
|
} else {
|
|
/*
|
|
* Process can exist at current level. If it is still
|
|
* alive or a DEMANDREQUEST we touch it so it will be
|
|
* left alone. Otherwise we leave it untouched so it
|
|
* will be accounted for and cleaned up later in remv().
|
|
* Dead DEMANDREQUESTs will be accounted but not freed.
|
|
*/
|
|
if (process->p_flags & (LIVING|NOCLEANUP|DEMANDREQUEST))
|
|
process->p_flags |= TOUCHED;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
* If this was a change of levels call, scan through the
|
|
* process table for processes that were warned to die. If any
|
|
* are found that haven't left yet, sleep for TWARN seconds and
|
|
* then send final terminations to any that haven't died yet.
|
|
*/
|
|
if (change_level) {
|
|
|
|
/*
|
|
* Set the alarm for TWARN seconds on the assumption
|
|
* that there will be some that need to be waited for.
|
|
* This won't harm anything except we are guaranteed to
|
|
* wakeup in TWARN seconds whether we need to or not.
|
|
*/
|
|
setimer(TWARN);
|
|
|
|
/*
|
|
* Scan for processes which should be dying. We hope they
|
|
* will die without having to be sent a SIGKILL signal.
|
|
*/
|
|
handle_children(1);
|
|
for (process = &proc_table[0]; process < &proc_table[NPROC];
|
|
process++) {
|
|
/*
|
|
* If this process should die, hasn't yet, and the
|
|
* TWARN time hasn't expired yet, wait for process
|
|
* to die or for timer to expire.
|
|
*/
|
|
while (time_up == FALSE &&
|
|
(process->p_flags & (WARNED|LIVING|OCCUPIED)) ==
|
|
(WARNED|LIVING|OCCUPIED))
|
|
sigpause(SIGCLD);
|
|
|
|
if (time_up == TRUE)
|
|
break;
|
|
}
|
|
if(wakeup.w_flags.w_childdeath)
|
|
handle_children(0);
|
|
|
|
/*
|
|
* If we reached the end of the table without the timer
|
|
* expiring, then there are no procs which will have to be
|
|
* sent the SIGKILL signal. If the timer has expired, then
|
|
* it is necessary to scan the table again and send signals
|
|
* to all processes which aren't going away nicely.
|
|
*/
|
|
if (time_up == TRUE) {
|
|
for (process = &proc_table[0];
|
|
process < &proc_table[NPROC]; process++) {
|
|
|
|
if ((process->p_flags & (WARNED|LIVING|OCCUPIED)) ==
|
|
(WARNED|LIVING|OCCUPIED))
|
|
kill(process->p_pid, SIGKILL);
|
|
}
|
|
}
|
|
setimer(0);
|
|
}
|
|
|
|
/*
|
|
* Rescan the proc_table for two kinds of entry, those marked LIVING,
|
|
* NAMED, which don't have an entry in inittab (haven't been TOUCHED
|
|
* by the above scanning), and haven't been sent kill signals, and
|
|
* those entries marked not LIVING, NAMED. The former procs are killed.
|
|
* The latter have DEAD_PROCESS accounting done and the slot cleared.
|
|
*/
|
|
for (process= &proc_table[0]; process < &proc_table[NPROC]; 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)) {
|
|
account(DEAD_PROCESS, process, NULL);
|
|
/*
|
|
* If this named proc hasn't been TOUCHED, then free the
|
|
* space. It has either died of it's own accord, but
|
|
* isn't respawnable or it was killed because it
|
|
* shouldn't exist at this level.
|
|
*/
|
|
if ((process->p_flags & TOUCHED) == 0)
|
|
process->p_flags = 0;
|
|
}
|
|
}
|
|
if(wakeup.w_flags.w_childdeath)
|
|
handle_children(0);
|
|
}
|
|
|
|
|
|
/*
|
|
* Procedure: spawn
|
|
*
|
|
* Restrictions:
|
|
open(2): None
|
|
fcntl(2): None
|
|
*/
|
|
/*
|
|
* spawn() scans inittab for entries which should be run at this mode.
|
|
* Processes which should be running but are not, are started.
|
|
*/
|
|
int
|
|
spawn(void)
|
|
{
|
|
register struct PROC_TABLE *pp;
|
|
struct CMD_LINE cmd;
|
|
char substituteline[MAXCMDL];
|
|
short lvl_mask;
|
|
int status;
|
|
|
|
/*
|
|
* First check the "powerhit" flag. If it is set, make sure the modes
|
|
* are PF_MODES and clear the "powerhit" flag. Avoid the possible race
|
|
* on the "powerhit" flag by disallowing a new powerfail interrupt
|
|
* between the test of the powerhit flag and the clearing of it.
|
|
*/
|
|
if (wakeup.w_flags.w_powerhit) {
|
|
wakeup.w_flags.w_powerhit = 0;
|
|
op_modes = PF_MODES;
|
|
}
|
|
lvl_mask = mask(cur_state);
|
|
|
|
#ifdef DEBUG1
|
|
debug("spawn\tSignal-new: %c cur: %c prev: %c\n", level(new_state),
|
|
level(cur_state),level(prev_state));
|
|
debug("spawn- lvl_mask: %o op_modes: %o\n",lvl_mask,op_modes);
|
|
#endif
|
|
|
|
/*
|
|
* Scan through all the entries in inittab.
|
|
*/
|
|
while ((status = getcmd(&cmd)) == TRUE) {
|
|
|
|
/*
|
|
* Find out if there is a process slot for this entry already.
|
|
*/
|
|
if ((pp = findpslot(&cmd)) == NULLPROC) {
|
|
/*
|
|
* Only generate an error message every WARNFREQUENCY
|
|
* seconds when the internal process table is full.
|
|
*/
|
|
if (error_time(FULLTABLE))
|
|
console("Internal process table is full.\n");
|
|
continue;
|
|
}
|
|
|
|
/*
|
|
* If there is an entry, and it is marked as DEMANDREQUEST,
|
|
* one of the levels a, b, or c is in its levels mask, and
|
|
* the action field is ONDEMAND and ONDEMAND is a permissable
|
|
* mode, and the process is dead, then respawn it.
|
|
*/
|
|
if (((pp->p_flags & (LIVING|DEMANDREQUEST)) == DEMANDREQUEST) &&
|
|
(cmd.c_levels & (MASKa|MASKb|MASKc)) &&
|
|
(cmd.c_action & op_modes) == M_ONDEMAND) {
|
|
respawn(pp, &cmd);
|
|
continue;
|
|
}
|
|
|
|
#ifdef DEBUG1
|
|
debug("process:\t%s\t%05d\n%s\t%d\t%o\t%o\n",
|
|
C(&pp->p_id[0]), pp->p_pid, ctime(&pp->p_time),
|
|
pp->p_count, pp->p_flags, pp->p_exit);
|
|
debug("cmd:\t%s\t%o\t%o\n\"%s\"\n", C(&cmd.c_id[0]),
|
|
cmd.c_levels, cmd.c_action, cmd.c_command);
|
|
#endif
|
|
|
|
/*
|
|
* If the action is not an action we are interested in,
|
|
* skip the entry.
|
|
*/
|
|
if ((cmd.c_action & op_modes) == 0 || pp->p_flags & LIVING ||
|
|
(cmd.c_levels & lvl_mask) == 0)
|
|
continue;
|
|
|
|
/*
|
|
* If the modes are the normal modes (ONCE, WAIT, RESPAWN, OFF,
|
|
* ONDEMAND) and the action field is either OFF or the action
|
|
* field is ONCE or WAIT and the current level is the same as
|
|
* the last level, then skip this entry. ONCE and WAIT only
|
|
* get run when the level changes.
|
|
*/
|
|
if (op_modes == NORMAL_MODES &&
|
|
(cmd.c_action == M_OFF ||
|
|
(cmd.c_action & (M_ONCE|M_WAIT)) &&
|
|
cur_state == prev_state))
|
|
continue;
|
|
|
|
/*
|
|
* If consdevdsf != CONSOLE, substitute all occurrences of
|
|
* CONSOLE in the command line with consdevdsf. This will
|
|
* redirect input and output to floating console device even
|
|
* though /etc/inittab entries indicate CONSOLE as the device
|
|
* to redirect input and output.
|
|
*/
|
|
if (strcmp(CONSOLE, consdevdsf) != 0){
|
|
if (substitutedev(&cmd, substituteline, consdevdsf)
|
|
== FAILURE){
|
|
console("Substituting device %s failed for command\n\"%s\"\ncommand line length exceeds %d\n",
|
|
substitutedsf, cmd.c_command, MAXCMDL);
|
|
continue;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* At this point we are interested in performing the action for
|
|
* this entry. Actions fall into two categories, spinning off
|
|
* a process and not waiting, and spinning off a process and
|
|
* waiting for it to die. If the action is ONCE, RESPAWN,
|
|
* ONDEMAND, POWERFAIL, or BOOT we don't wait for the process
|
|
* to die, for all other actions we do wait.
|
|
*/
|
|
if (cmd.c_action & (M_ONCE | M_RESPAWN | M_PF | M_BOOT)) {
|
|
respawn(pp, &cmd);
|
|
|
|
} else {
|
|
respawn(pp,&cmd);
|
|
while (waitproc(pp) == FAILURE);
|
|
account(DEAD_PROCESS, pp, NULL);
|
|
pp->p_flags = 0;
|
|
}
|
|
}
|
|
return(status);
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
* Procedure: respawn
|
|
*
|
|
* Restrictions:
|
|
fcntl(2): None
|
|
*/
|
|
|
|
/*
|
|
* respawn() spawns a shell, inserts the information about the process
|
|
* process into the proc_table, and does the startup accounting.
|
|
*/
|
|
void
|
|
respawn(struct PROC_TABLE *process, struct CMD_LINE *cmd)
|
|
{
|
|
register int i;
|
|
int modes, maxfiles;
|
|
long now;
|
|
struct PROC_TABLE tmproc, *oprocess;
|
|
|
|
#ifdef DEBUG1
|
|
debug("** respawn ** id:%s\n", C(&process->p_id[0]));
|
|
#endif
|
|
|
|
/*
|
|
* The modes to be sent to efork() are 0 unless we are
|
|
* spawning a LVLa, LVLb, or LVLc entry or we will be
|
|
* waiting for the death of the child before continuing.
|
|
*/
|
|
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 this is a respawnable process, check the threshold
|
|
* information to avoid excessive respawns.
|
|
*/
|
|
if (cmd->c_action & M_RESPAWN) {
|
|
/*
|
|
* Add NOCLEANUP to all respawnable commands so that the
|
|
* information about the frequency of respawns isn't lost.
|
|
*/
|
|
modes |= NOCLEANUP;
|
|
time(&now);
|
|
|
|
/*
|
|
* If no time is assigned, then this is the first time
|
|
* this command is being processed in this series. Assign
|
|
* the current time.
|
|
*/
|
|
if (process->p_time == 0L)
|
|
process->p_time = now;
|
|
|
|
if (process->p_count++ == SPAWN_LIMIT) {
|
|
|
|
if ((now - process->p_time) < SPAWN_INTERVAL) {
|
|
/*
|
|
* Process is respawning too rapidly. Print
|
|
* message and refuse to respawn it for now.
|
|
*/
|
|
console("Command is respawning too rapidly.\
|
|
Check for possible errors.\nid:%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 process has been respawning too rapidly and
|
|
* the inhibit time limit hasn't expired yet, we
|
|
* refuse to respawn.
|
|
*/
|
|
if (now - process->p_time < SPAWN_INTERVAL + INHIBIT)
|
|
return;
|
|
process->p_time = now;
|
|
process->p_count = 0;
|
|
}
|
|
rsflag = TRUE;
|
|
}
|
|
|
|
/*
|
|
* Spawn a child process to execute this command.
|
|
*/
|
|
oprocess = process;
|
|
if (cmd->c_action & (M_WAIT | M_BOOTWAIT))
|
|
while ((process = efork(M_WAIT, oprocess, modes)) == NO_ROOM)
|
|
pause();
|
|
else
|
|
while ((process = efork(M_OFF, oprocess, modes)) == NO_ROOM)
|
|
pause();
|
|
|
|
if (process == NULLPROC) {
|
|
|
|
/*
|
|
* We are the child. We must make sure we get a different
|
|
* file pointer for our references to utmp. Otherwise our
|
|
* seeks and reads will compete with those of the parent.
|
|
*/
|
|
endutxent();
|
|
|
|
/*
|
|
* Perform the accounting for the beginning of a process.
|
|
* Note that all processes are initially "INIT_PROCESS"es.
|
|
* HOWEVER: don't do this if new level is 0:
|
|
* we want utmp closed!
|
|
*/
|
|
|
|
if (cur_state != LVL0)
|
|
{
|
|
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;
|
|
account(INIT_PROCESS, &tmproc,
|
|
prog_name(&cmd->c_command[EXEC]));
|
|
}
|
|
|
|
maxfiles = getdtablehi(); /* was ulimit(0,4)!!! */
|
|
for (i = 0; i < maxfiles; i++ )
|
|
fcntl(i, F_SETFD, FD_CLOEXEC);
|
|
|
|
/*
|
|
* Now exec a shell with the -c option and the command
|
|
* from inittab.
|
|
*/
|
|
if (cap_enabled && cap_set_proc(cap) < 0)
|
|
exit(1);
|
|
execle(SH, "INITSH", "-c", cmd->c_command, (char *)0,glob_envp);
|
|
console("Command\n\"%s\"\n failed to execute.\
|
|
errno = %d (exec of shell failed)\n", cmd->c_command, errno);
|
|
|
|
/*
|
|
* Don't come back so quickly that "init" doesn't have a
|
|
* chance to finish putting this child in "proc_table".
|
|
*/
|
|
timer(20);
|
|
exit(1);
|
|
|
|
} else {
|
|
/*
|
|
* We are the parent. Insert the necessary
|
|
* information in the proc_table.
|
|
*/
|
|
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];
|
|
}
|
|
}
|
|
|
|
|
|
/************************/
|
|
/**** findpslot ****/
|
|
/************************/
|
|
|
|
/*
|
|
* findpslot() finds the old slot in the process table for the
|
|
* command with the same id, or it finds an empty slot.
|
|
*/
|
|
struct PROC_TABLE *
|
|
findpslot(struct CMD_LINE *cmd)
|
|
{
|
|
register struct PROC_TABLE *process;
|
|
register struct PROC_TABLE *empty = NULLPROC;
|
|
|
|
for (process = &proc_table[0]; process < &proc_table[NPROC]; process++){
|
|
if (process->p_flags & OCCUPIED &&
|
|
id_eq(process->p_id, cmd->c_id))
|
|
break;
|
|
|
|
/*
|
|
* If the entry is totally empty and "empty" is still 0,remember
|
|
* where this hole is and make sure the slot is zeroed out.
|
|
*/
|
|
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 there is no entry for this slot, then there should be an
|
|
* empty slot. If there is no empty slot, then we've run out
|
|
* of proc_table space. If the latter is true, empty will be
|
|
* NULL and the caller will have to complain.
|
|
*/
|
|
if (process == &proc_table[NPROC])
|
|
process = empty;
|
|
|
|
return(process);
|
|
}
|
|
|
|
struct CMD_LINE *thetable;
|
|
int thetablesize = -1; /* number of valid entries */
|
|
#define TABLE_INCR 100 /* number of entries to grow by */
|
|
|
|
/*
|
|
* get_inittab() reads and parses the whole inittab file, if
|
|
* it hasn't changed since last read.
|
|
*
|
|
* This costs some some small amount of memory, but saves us from
|
|
* re-reading the inittab every time an inittab-started process
|
|
* exits (or with my changes in signal handling for irix 6.4, every
|
|
* time an orphaned process exits). Small memory systems will almost
|
|
* never have a significant inittab. As shipped in 6.4, it's about 60
|
|
* entries, with about 2000 bytes of command strings, so less than a
|
|
* 4KB page of memory.
|
|
* Olson, 3/97
|
|
*/
|
|
void
|
|
get_inittab(void)
|
|
{
|
|
static time_t lastmodtime;
|
|
static ino_t lastino;
|
|
static int reportit = 1, lastsz;
|
|
FILE *fp_inittab = NULL;
|
|
struct stat itst;
|
|
char lbuf[MAXCMDL];
|
|
char *ptr;
|
|
struct CMD_LINE *cmd;
|
|
int c, lastc, state;
|
|
char *ptr1;
|
|
int i, proceed, line;
|
|
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,
|
|
};
|
|
|
|
|
|
if(thetablesize > 0) { /* we already have incore table; see if
|
|
we should (and can) re-read the inittab file */
|
|
if(stat(INITTAB, &itst)) {
|
|
if(reportit)
|
|
console("%s can't be read, using old one, errno=%d\n", INITTAB, errno);
|
|
reportit = 0;
|
|
return;
|
|
}
|
|
else if(itst.st_size < 10) {
|
|
if(reportit)
|
|
console("%s truncated or corrupted, using old data\n", INITTAB);
|
|
reportit = 0;
|
|
return;
|
|
}
|
|
else if(itst.st_mtime == lastmodtime && itst.st_ino == lastino) {
|
|
/* we don't check st_dev because we assume it's always going to
|
|
* be on the root device; system doesn't work too well otherwise...
|
|
*/
|
|
return; /* no need to re-read */
|
|
}
|
|
if(!(fp_inittab = fopen(INITTAB, "r"))) {
|
|
if(reportit)
|
|
console("%s can't be opened (errno=%d), using old data\n", INITTAB, errno);
|
|
reportit = 0;
|
|
return;
|
|
}
|
|
}
|
|
else while(!fp_inittab) {
|
|
/* try forever to open it first time, since otherwise there isn't
|
|
* anything useful system can do... Basicly same sequence as in
|
|
* checking sequence, but different error messages. We also report
|
|
* each time, so user knows system isn't dead. The retries are only
|
|
* going to help if this is a media or NFS error, but might as well...
|
|
*/
|
|
int tintvl = 5;
|
|
|
|
if(stat(INITTAB, &itst) == 0 && itst.st_size < 10)
|
|
console("%s truncated or corrupted, only %d bytes\n", INITTAB,
|
|
itst.st_size);
|
|
if(!(fp_inittab = fopen(INITTAB, "r"))) {
|
|
console("%s can't be opened (errno=%d), retrying\n", INITTAB, errno);
|
|
tintvl <<= 1;
|
|
if(tintvl > 120)
|
|
tintvl = 120; /* max out at 2 minutes */
|
|
timer(tintvl);
|
|
continue;
|
|
}
|
|
|
|
/* start out by allocating room for TABLE_INCR entries, since that's
|
|
* all that most people will need. If we need more, we allocate
|
|
* more in chunks of TABLE_INCR; done first so we can bail with some useful
|
|
* message; since this is the first process run, and it's during
|
|
* initialization, this should never fail! */
|
|
if(!(thetable = calloc(sizeof(*thetable), TABLE_INCR))) {
|
|
if(reportit)
|
|
console("Cannot allocate memory for inittab table, errno=%d\n", errno);
|
|
return;
|
|
}
|
|
lastsz = TABLE_INCR;
|
|
}
|
|
|
|
reportit = 1; /* report errors again */
|
|
thetablesize = 0;
|
|
|
|
/*
|
|
* Keep getting commands from inittab until you find a
|
|
* good one or run out of file.
|
|
*/
|
|
|
|
for(line=1;;line++) {
|
|
if(thetablesize >= lastsz) {
|
|
/* because realloc can trash the original data, allocate
|
|
* more memory and copy, even though that could waste some
|
|
* time/memory */
|
|
void *ntbl = calloc(sizeof(*thetable), lastsz+TABLE_INCR);
|
|
if(ntbl) {
|
|
bcopy(thetable, ntbl, sizeof(*thetable) * lastsz);
|
|
free(thetable);
|
|
thetable = ntbl;
|
|
lastsz += TABLE_INCR;
|
|
}
|
|
else {
|
|
console("Can't grow inittab table, entries past %d are ignored\n",
|
|
thetablesize);
|
|
break;
|
|
}
|
|
}
|
|
cmd = &thetable[thetablesize];
|
|
memset((void*)cmd, 0, sizeof(*cmd));
|
|
|
|
/*
|
|
* Read in lines of inittab, parsing at colons, until a line is
|
|
* read in which doesn't end with a backslash. Do not start if
|
|
* the first character read is an EOF. Note that this means
|
|
* that lines which don't end in a newline are still processed,
|
|
* since the "for" will terminate normally once started,
|
|
* regardless of whether line terminates with a newline or EOF.
|
|
*/
|
|
state = FAILURE;
|
|
if ((c = fgetc(fp_inittab)) == EOF) {
|
|
break;
|
|
}
|
|
|
|
/*
|
|
* Ignore blank or comment lines ('#' as first character)
|
|
*/
|
|
for (;;) {
|
|
if ('\n' == c) {
|
|
c = fgetc(fp_inittab);
|
|
line++;
|
|
continue;
|
|
}
|
|
if ('#' != c)
|
|
break;
|
|
do {
|
|
c = fgetc(fp_inittab);
|
|
} while (c != '\n' && c != EOF);
|
|
}
|
|
|
|
for (proceed = TRUE, ptr = lbuf, state = ID, lastc = '\0';
|
|
proceed && c != EOF;
|
|
lastc = c, c = fgetc(fp_inittab)) {
|
|
/*
|
|
* If we're not in the FAILURE state and haven't
|
|
* yet reached the shell command field, process
|
|
* the line, otherwise just look for a real end
|
|
* of line.
|
|
*/
|
|
if (state != FAILURE && state != COMMAND) {
|
|
/*
|
|
* Squeeze out spaces and tabs.
|
|
*/
|
|
if (c == ' ' || c == '\t')
|
|
continue;
|
|
|
|
/*
|
|
* If the character is a ':', then check the
|
|
* previous field for correctness and advance
|
|
* to the next field.
|
|
*/
|
|
if ( c == ':' ) {
|
|
switch (state) {
|
|
|
|
case ID :
|
|
/*
|
|
* Check to see that there are only
|
|
* 1 to 4 characters for the id.
|
|
*/
|
|
if ((i = ptr - lbuf) < 1 || i > 4 ) {
|
|
state = FAILURE;
|
|
} else {
|
|
bcopy(lbuf, &cmd->c_id[0], i);
|
|
ptr = lbuf;
|
|
state = LEVELS;
|
|
}
|
|
break;
|
|
|
|
case LEVELS :
|
|
/*
|
|
* Build a mask for all the levels for
|
|
* which this command will be legal.
|
|
*/
|
|
for (cmd->c_levels = 0, ptr1 = lbuf;
|
|
ptr1 < ptr; ptr1++) {
|
|
if (*ptr1 >= '0' && *ptr1 <= '6') {
|
|
cmd->c_levels |=
|
|
(MASK0 << (*ptr1 - '0'));
|
|
} else if(*ptr1 >= 'a' && *ptr1 <= 'c'){
|
|
cmd->c_levels |=
|
|
(MASKa << (*ptr1 - 'a'));
|
|
} else if(*ptr1 == 's' || *ptr1 == 'S'){
|
|
cmd->c_levels |= MASKSU;
|
|
} else {
|
|
state = FAILURE;
|
|
break;
|
|
}
|
|
}
|
|
if (state != FAILURE) {
|
|
state = ACTION;
|
|
ptr = lbuf; /* Reset the buffer */
|
|
}
|
|
break;
|
|
|
|
case ACTION :
|
|
/*
|
|
* Null terminate the string in buffer and
|
|
* then try to match against legal actions. If
|
|
* the field is of length 0, then the default of
|
|
* "RESPAWN" is used if the id is numeric,
|
|
* otherwise the default is "OFF".
|
|
*/
|
|
if (ptr == lbuf) {
|
|
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(lbuf,actions[i]) == 0) {
|
|
/*
|
|
* run process in /etc/inittab in
|
|
* single user for all actions
|
|
*/
|
|
cmd->c_action = act_masks[i];
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
* If the action didn't match any legal action,
|
|
* set state to FAILURE.
|
|
*/
|
|
if (cmd->c_action == 0) {
|
|
state = FAILURE;
|
|
} else {
|
|
state = COMMAND;
|
|
strcpy(lbuf,"exec ");
|
|
}
|
|
ptr = lbuf + EXEC;
|
|
break;
|
|
}
|
|
continue;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* If the character is a '\n', then this is the end of a
|
|
* line. If the '\n' wasn't preceded by a backslash,
|
|
* it is also the end of an inittab command. If it was
|
|
* preceded by a backslash then the next line is a
|
|
* continuation. Note that the continuation '\n' falls
|
|
* through and is treated like other characters and is
|
|
* stored in the shell command line.
|
|
*/
|
|
if (c == '\n' && lastc != '\\') {
|
|
proceed = FALSE;
|
|
*ptr = '\0';
|
|
break;
|
|
}
|
|
|
|
/*
|
|
* For all other characters just stuff them into the command
|
|
* as long as there aren't too many of them. Make sure there
|
|
* is room for a terminating '\0' also.
|
|
*/
|
|
if (ptr >= lbuf + MAXCMDL - 1)
|
|
state = FAILURE;
|
|
else
|
|
*ptr++ = (char)c;
|
|
|
|
/*
|
|
* If the character we just stored was a quoted backslash,
|
|
* then change "c" to '\0', so that this backslash will not
|
|
* cause a subsequent '\n' to appear quoted. In otherwords
|
|
* '\' '\' '\n' is the real end of a command, while '\' '\n'
|
|
* is a continuation.
|
|
*/
|
|
if ( c == '\\' && lastc == '\\')
|
|
c = '\0';
|
|
}
|
|
|
|
/*
|
|
* Make sure all the fields are properly specified
|
|
* for a good command line.
|
|
*/
|
|
if (state == COMMAND) {
|
|
if(cmd->c_command)
|
|
free(cmd->c_command);
|
|
if(!(cmd->c_command = strdup(lbuf))) {
|
|
console("No memory for %s line %d, command=%s; skipped\n",
|
|
INITTAB, line, lbuf);
|
|
continue;
|
|
}
|
|
|
|
/*
|
|
* If no default level was supplied, insert
|
|
* all numerical levels.
|
|
*/
|
|
if (cmd->c_levels == 0)
|
|
cmd->c_levels =
|
|
MASK0|MASK1|MASK2| MASK3|MASK4|MASK5|MASK6;
|
|
|
|
/*
|
|
* If no action has been supplied, declare this
|
|
* entry to be OFF.
|
|
*/
|
|
if (cmd->c_action == 0)
|
|
cmd->c_action = M_OFF;
|
|
|
|
/*
|
|
* If no shell command has been supplied, make sure
|
|
* there is a null string in the command field.
|
|
*/
|
|
if (ptr == lbuf + EXEC)
|
|
*lbuf = '\0';
|
|
thetablesize++; /* number of entries, not index of last */
|
|
}
|
|
/* else ignore the line, something about it is bad. */
|
|
|
|
}
|
|
fclose(fp_inittab); /* no benefit in having it left open with table
|
|
* kept in memory */
|
|
|
|
lastmodtime = itst.st_mtime;
|
|
lastino = itst.st_ino;
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
* getcmd() returns lines from inittab.
|
|
* It used to read the file every time, and reparse. That's clearly
|
|
* a performance issue, so now we only re-read it (via get_inittab())
|
|
* if the modtime is different from the last time we did it.
|
|
*
|
|
* Each time it finds a command line
|
|
* it will return TRUE as well as fill the passed CMD_LINE structure and
|
|
* the shell command string.
|
|
* The assumption is that we are called to get the whole file on each use,
|
|
* so when we get to the end and return FLASE, we reset our pointer back to
|
|
* the start for the next time around.
|
|
* Because callers can change part of the command line structure (in
|
|
* particular, the string "CONSOLE" to the "real" console device), we
|
|
* return a copy of the structure, with the command string a static
|
|
* buffer...
|
|
*/
|
|
int
|
|
getcmd(struct CMD_LINE *cmd)
|
|
{
|
|
static int cmdnum = 0; /* which one we are returning. If 0, we call
|
|
get_inittab() to read the inittab file if necessary */
|
|
static char cmdstr[MAXCMDL];
|
|
|
|
if(!cmdnum)
|
|
get_inittab();
|
|
|
|
if(cmdnum >= thetablesize) {
|
|
/* reached the end, or failed to get table */
|
|
cmdnum = 0; /* for next time */
|
|
return FALSE;
|
|
}
|
|
|
|
*cmd = thetable[cmdnum];
|
|
cmd->c_command = cmdstr;
|
|
strcpy(cmdstr, thetable[cmdnum].c_command);
|
|
cmdnum++;
|
|
return TRUE;
|
|
}
|
|
|
|
/********************/
|
|
/**** mask ****/
|
|
/********************/
|
|
|
|
int
|
|
mask(int lvl)
|
|
{
|
|
register int answer;
|
|
|
|
switch (lvl) {
|
|
|
|
case LVLQ:
|
|
answer = 0;
|
|
break;
|
|
case LVL0:
|
|
answer = MASK0;
|
|
break;
|
|
case LVL1:
|
|
answer = MASK1;
|
|
break;
|
|
case LVL2:
|
|
answer = MASK2;
|
|
break;
|
|
case LVL3:
|
|
answer = MASK3;
|
|
break;
|
|
case LVL4:
|
|
answer = MASK4;
|
|
break;
|
|
case LVL5:
|
|
answer = MASK5;
|
|
break;
|
|
case LVL6:
|
|
answer = MASK6;
|
|
break;
|
|
case SINGLE_USER:
|
|
answer = MASKSU;
|
|
break;
|
|
case LVLa:
|
|
answer = MASKa;
|
|
break;
|
|
case LVLb:
|
|
answer = MASKb;
|
|
break;
|
|
case LVLc:
|
|
answer = MASKc;
|
|
break;
|
|
default:
|
|
answer = FAILURE;
|
|
break;
|
|
}
|
|
return(answer);
|
|
}
|
|
|
|
|
|
/*********************/
|
|
/**** level ****/
|
|
/*********************/
|
|
|
|
char
|
|
level(int state)
|
|
{
|
|
register char answer;
|
|
|
|
switch(state) {
|
|
|
|
case LVL0:
|
|
answer = '0';
|
|
break;
|
|
case LVL1:
|
|
answer = '1';
|
|
break;
|
|
case LVL2:
|
|
answer = '2';
|
|
break;
|
|
case LVL3:
|
|
answer = '3';
|
|
break;
|
|
case LVL4:
|
|
answer = '4';
|
|
break;
|
|
case LVL5:
|
|
answer = '5';
|
|
break;
|
|
case LVL6:
|
|
answer = '6';
|
|
break;
|
|
case SINGLE_USER:
|
|
answer = 'S';
|
|
break;
|
|
case LVLa:
|
|
answer = 'a';
|
|
break;
|
|
case LVLb:
|
|
answer = 'b';
|
|
break;
|
|
case LVLc:
|
|
answer = 'c';
|
|
break;
|
|
default:
|
|
answer = '?';
|
|
break;
|
|
}
|
|
return(answer);
|
|
}
|
|
|
|
|
|
/*
|
|
* Procedure: killproc
|
|
*
|
|
* Restrictions:
|
|
kill(2): None
|
|
|
|
* Notes: creates a child which kills the process specified by pid.
|
|
*/
|
|
void
|
|
killproc(pid_t pid)
|
|
{
|
|
register struct PROC_TABLE *process;
|
|
|
|
while ((process = efork(M_OFF, NULLPROC, 0)) == NO_ROOM)
|
|
pause();
|
|
|
|
if (process == NULLPROC) {
|
|
/*
|
|
* We are the child. Try to terminate the process nicely
|
|
* first using SIGTERM and if it refuses to die in TWARN
|
|
* seconds kill it with SIGKILL.
|
|
*/
|
|
kill(pid, SIGTERM);
|
|
timer(TWARN);
|
|
kill(pid, SIGKILL);
|
|
exit(0);
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
* Procedure: initialize
|
|
*
|
|
* Restrictions:
|
|
fopen: None
|
|
fclose: None
|
|
fcntl(2): None
|
|
open(2): None
|
|
*/
|
|
|
|
static char rootpath[] = _PATH_ROOTPATH;
|
|
|
|
/*
|
|
* Perform the initial state setup and look for an
|
|
* initdefault entry in the "inittab" file.
|
|
*/
|
|
int
|
|
initialize(void)
|
|
{
|
|
register int msk, i;
|
|
register struct PROC_TABLE *process, *oprocess;
|
|
static int states[] = {LVL0,LVL1,LVL2,LVL3,LVL4,LVL5,
|
|
LVL6,SINGLE_USER};
|
|
struct CMD_LINE cmd;
|
|
char line[MAXCMDL];
|
|
char substituteline[MAXCMDL];
|
|
char lerrbuf[ERRBUFSZ];
|
|
FILE *fp;
|
|
int initstate, inquotes, length, maxfiles;
|
|
char *cp1;
|
|
char initbuf[10];
|
|
static char *init_tbl[] = {
|
|
"0", "1", "2", "3", "4", "5", "6", "s", 0,
|
|
};
|
|
char **init_tbl_ptr;
|
|
int gotlevel = 0;
|
|
off_t v_addr;
|
|
struct var v;
|
|
int kmemfd;
|
|
#ifdef DEBUG
|
|
debug("%d:initialize():ENTER\n",getpid());
|
|
#endif
|
|
|
|
/*
|
|
* Initialize the proc table.
|
|
*/
|
|
v_addr = (off_t)sysmp(MP_KERNADDR, MPKA_VAR);
|
|
if (v_addr != (off_t)-1) {
|
|
kmemfd = open("/dev/kmem", O_RDONLY);
|
|
if (kmemfd >= 0) {
|
|
if (lseek(kmemfd, v_addr, SEEK_SET) == v_addr &&
|
|
read(kmemfd, (void *)&v, sizeof(v)) == sizeof(v))
|
|
NPROC = v.v_proc;
|
|
close(kmemfd);
|
|
}
|
|
}
|
|
proc_table = (struct PROC_TABLE *)calloc(NPROC, sizeof(*proc_table));
|
|
/*
|
|
* -initialize entries in devtab
|
|
* -verify the existence of the DSF listed in devtab. If any do not
|
|
* exist, they will be made after the root file system has been
|
|
* verified to be sane.
|
|
* -setup the device to direct console message output.
|
|
* If the CONSOLE DSF is not the DSF to direct console message
|
|
* output, substitutedsf has been set to consdevdsf in setup_consdev.
|
|
* This will cause all occurences of /dev/console in /etc/inittab
|
|
* for the SYSINIT entries to be substituted with substitutedsf
|
|
* before the SYSINIT command is executed.
|
|
* If consdevdsf is NULL go into firmware mode since most
|
|
* of the SYSINIT entries in the inittab will fail without a device
|
|
* to read from and write to.
|
|
*
|
|
* NOTE: Since there is no device to direct console message output to,
|
|
* we can not display an error message, that could assist the
|
|
* administrator, before going into firmware mode.
|
|
* Need to find a way to get a message out. (Perhaps modifying
|
|
* uadmin(2) to accept a sting to be displayed.)
|
|
*/
|
|
initialize_devtab();
|
|
verify_devtab();
|
|
if (setup_consdev(lerrbuf) == FAILURE){
|
|
console("%s", lerrbuf);
|
|
}
|
|
|
|
if (consdevdsf == (char *)NULL)
|
|
firmware();
|
|
|
|
/*
|
|
* Initialize state to "SINGLE_USER" "BOOT_MODES".
|
|
*/
|
|
if(cur_state >= 0) {
|
|
n_prev[cur_state]++;
|
|
prior_state = cur_state;
|
|
}
|
|
cur_state = SINGLE_USER;
|
|
op_modes = BOOT_MODES;
|
|
|
|
/*
|
|
* Set up all signals to be caught or ignored as appropriate.
|
|
*/
|
|
init_signals();
|
|
initstate = 0;
|
|
|
|
#ifdef UDEBUG
|
|
reset_modes();
|
|
#endif
|
|
|
|
/*
|
|
* Set up the default environment for all procs to be forked from init.
|
|
* Read the values from the /etc/TIMEZONE file, except for PATH. If
|
|
* there's not enough room in the environment array, the environment
|
|
* lines that don't fit are silently discarded.
|
|
*/
|
|
glob_envp[0] = malloc(sizeof("PATH=") + sizeof(rootpath) - 1);
|
|
strcpy( glob_envp[0], "PATH=");
|
|
strcat( glob_envp[0], rootpath);
|
|
|
|
if( (fp = fopen(ENVFILE, "r")) == NULL ) {
|
|
console("Cannot open %s. Environment not initialized.\n",
|
|
ENVFILE);
|
|
} else {
|
|
i = 1;
|
|
while (fgets(line, MAXCMDL - 1, fp) != NULL &&
|
|
i < MAXENVENT - 1) {
|
|
char *cp, *eq, *ep, *tp, quotechar;
|
|
|
|
/* Make sure there's a newline at the end. */
|
|
length = strlen(line);
|
|
if (line[length - 1] != '\n') {
|
|
/* Too long, truncate it */
|
|
line[length - 1] = '\n';
|
|
}
|
|
/*
|
|
* First pass. Turn separators into newlines,
|
|
* remove balanced quotes, and turn = into tabs.
|
|
* Also handle \ and comments.
|
|
*/
|
|
cp = line;
|
|
inquotes = 0;
|
|
eq = NULL;
|
|
while (cp < &line[length]) {
|
|
if (inquotes) {
|
|
if (*cp != quotechar) {
|
|
cp++;
|
|
continue;
|
|
}
|
|
inquotes = 0;
|
|
for (cp1 = cp; cp1 < &line[length]; cp1++)
|
|
*cp1 = cp1[1];
|
|
length--;
|
|
} else if (*cp == '#') {
|
|
*cp++ = '\n';
|
|
*cp = '\0';
|
|
length = cp - line;
|
|
} else if (*cp == ';' || *cp == ' ' || *cp == '\t') {
|
|
*cp++ = '\n';
|
|
eq = NULL;
|
|
} else if (eq == NULL && *cp == '=')
|
|
*(eq = cp++) = '\t';
|
|
else if (*cp == '\'' || *cp == '"') {
|
|
quotechar = *cp;
|
|
inquotes = 1;
|
|
for (cp1 = cp; cp1 < &line[length]; cp1++)
|
|
*cp1 = cp1[1];
|
|
length--;
|
|
} else if (*cp == '\\') {
|
|
for (cp1 = cp; cp1 < &line[length]; cp1++)
|
|
*cp1 = cp1[1];
|
|
length--;
|
|
cp++;
|
|
} else
|
|
cp++;
|
|
}
|
|
/*
|
|
* Second pass. Use newlines as separators, after
|
|
* turning tabs back into = put them in the environment.
|
|
*/
|
|
cp = line;
|
|
while (*cp) {
|
|
if (*cp == '\n') {
|
|
do
|
|
cp++;
|
|
while (*cp == '\n');
|
|
}
|
|
if (!(ep = strchr(cp, '\n')))
|
|
break;
|
|
if (!(tp = strchr(cp, '\t')) || tp > ep) {
|
|
cp = ep + 1;
|
|
continue;
|
|
}
|
|
*tp = '=';
|
|
*ep = '\0';
|
|
glob_envp[i] = malloc((unsigned)(ep - cp) + 1);
|
|
strcpy(glob_envp[i], cp);
|
|
cp = ep + 1;
|
|
if (++i >= MAXENVENT - 1)
|
|
break;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Append a null pointer to the environment array
|
|
* to mark its end.
|
|
*/
|
|
glob_envp[i] = NULL;
|
|
fclose(fp);
|
|
}
|
|
if (sgikopt("initstate", initbuf, sizeof(initbuf)) >= 0) {
|
|
for (init_tbl_ptr = init_tbl; *init_tbl_ptr; init_tbl_ptr++) {
|
|
if (strcmp(*init_tbl_ptr, initbuf) == 0) {
|
|
initstate = states[init_tbl_ptr - init_tbl];
|
|
gotlevel = 1;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Scan the "inittab" file and process "initdefault"
|
|
* and "sysinit" entries.
|
|
*/
|
|
while (getcmd(&cmd) == TRUE) {
|
|
if ((cmd.c_action == M_INITDEFAULT) && (!gotlevel)) {
|
|
/*
|
|
* Look through the "c_levels" word, starting at
|
|
* the highest level. If there is more than one
|
|
* level specified, the system will come up at
|
|
* the highest of the specified levels.
|
|
*/
|
|
for (msk = MASKSU, i = sizeof(states)/sizeof(int) - 1;
|
|
msk > 0; msk >>= 1 , i--) {
|
|
if (msk & cmd.c_levels) {
|
|
initstate = states[i];
|
|
}
|
|
}
|
|
} else if (cmd.c_action == M_SYSINIT) {
|
|
/*
|
|
* Execute the "sysinit" entry and wait for it to
|
|
* complete. No bookkeeping is performed on these
|
|
* entries because we avoid writing to the file system
|
|
* until after there has been an chance to check it.
|
|
*/
|
|
if (process = findpslot(&cmd)) {
|
|
for (oprocess = process;
|
|
(process = efork(M_OFF, oprocess,
|
|
(NAMED|NOCLEANUP))) == NO_ROOM;);
|
|
|
|
if (process == NULLPROC) {
|
|
maxfiles = getdtablehi(); /* was ulimit(0,4)!!! */
|
|
for (i = 0; i < maxfiles; i++ )
|
|
fcntl(i, F_SETFD, FD_CLOEXEC);
|
|
|
|
/*
|
|
* If substitutedsf is defined, the DSF
|
|
* CONSOLE either does not exist or has
|
|
* an incorrect major and/or minor
|
|
* device number. substitutedev() will
|
|
* substitute all occurrences of the
|
|
* DSF name CONSOLE in the command line
|
|
* with the DSF name defined in
|
|
* substitutedsf.
|
|
*/
|
|
if (substitutedsf != (char *)NULL){
|
|
if (substitutedev(&cmd,
|
|
substituteline, substitutedsf)
|
|
== FAILURE){
|
|
console("Substituting device %s failed for command\n\"%s\"\ncommand line length exceeds %d\n",
|
|
substitutedsf,
|
|
cmd.c_command,
|
|
MAXCMDL);
|
|
exit(1);
|
|
}
|
|
}
|
|
if (cap_enabled && cap_set_proc(cap)< 0)
|
|
exit(1);
|
|
|
|
execle(SH,"INITSH", "-c", cmd.c_command,
|
|
(char *)0, glob_envp);
|
|
console("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);
|
|
|
|
#ifdef ACCTDEBUG
|
|
debug("SYSINIT- id: %.4s term: %o exit: %o\n",
|
|
&cmd.c_id[0],(process->p_exit&0xff),
|
|
(process->p_exit&0xff00)>>8);
|
|
#endif
|
|
process->p_flags = 0;
|
|
}
|
|
}
|
|
}
|
|
|
|
/* One of the SYSINIT entries execute /sbin/bcheckrc which fsck the
|
|
* root file system. Can assume the root file system is sane at
|
|
* this point and can modify entries in the root file system.
|
|
* Update any entries in devtab with action dtaMKNOD or dtaLINK.
|
|
* Setup the console device (consdevdsf and TP) again. If any of the
|
|
* devices in devtab were created, consdevdsf and TP (if TP is
|
|
* configured) may need to be updated or allocated also.
|
|
*/
|
|
rootfs_verified = TRUE;
|
|
(void)update_devtab();
|
|
if (setup_consdev(lerrbuf) == FAILURE){
|
|
console("%s", lerrbuf);
|
|
}
|
|
|
|
/*
|
|
* Get the ioctl settings for /dev/syscon so that it can be
|
|
* brought up in the state it was in when the system went down.
|
|
*/
|
|
get_ioctl_syscon();
|
|
|
|
#ifdef DEBUG
|
|
debug("%d:initialize():RETURN\tinitstate = %d\n",getpid(),initstate);
|
|
#endif
|
|
if (initstate == SINGLE_USER)
|
|
return(-1);
|
|
|
|
/*
|
|
* If no "initdefault" entry is found, return 0. This will
|
|
* have "init" ask the user at /dev/syscon to supply a level.
|
|
*/
|
|
if (initstate)
|
|
return(initstate);
|
|
else
|
|
return(0);
|
|
}
|
|
|
|
|
|
/****************************/
|
|
/**** init_signals ****/
|
|
/****************************/
|
|
|
|
/*
|
|
* Initialize all signals to either be caught or ignored.
|
|
*/
|
|
void
|
|
init_signals(void)
|
|
{
|
|
int i;
|
|
|
|
for(i=1; i<NSIG; i++) /* really ignore all unused signals; just */
|
|
sigset(i, SIG_IGN); /* in case somebody/program sends a wild signal */
|
|
|
|
sigset(LVLQ, siglvl);
|
|
sigset(LVL0, siglvl);
|
|
sigset(LVL1, siglvl);
|
|
sigset(LVL2, siglvl);
|
|
#ifdef UDEBUG
|
|
sigset(LVL3, SIG_DFL);
|
|
sigset(LVL4, SIG_DFL);
|
|
sigset(SIGTERM, SIG_DFL);
|
|
sigset(SIGUSR1, abort);
|
|
sigset(SIGUSR2, abort);
|
|
sigset(SIGXCPU, abort);
|
|
sigset(SIGXFSZ, abort);
|
|
#else
|
|
sigset(LVL3, siglvl);
|
|
sigset(LVL4, siglvl);
|
|
#endif
|
|
sigset(LVL5, siglvl);
|
|
sigset(LVL6, siglvl);
|
|
sigset(SINGLE_USER, siglvl);
|
|
sigset(LVLa, siglvl);
|
|
sigset(LVLb, siglvl);
|
|
sigset(LVLc, siglvl);
|
|
sigset(SIGALRM, alarmclk);
|
|
alarmclk();
|
|
sigset(SIGCLD, childeath);
|
|
sigset(SIGPWR, powerfail);
|
|
}
|
|
|
|
|
|
/**********************/
|
|
/**** siglvl ****/
|
|
/**********************/
|
|
|
|
void
|
|
siglvl(int sig)
|
|
{
|
|
register struct PROC_TABLE *process;
|
|
|
|
/*
|
|
* If the signal received is a LVLQ signal, do not really
|
|
* change levels, just restate the current level. If the
|
|
* signal is not a LVLQ, set the new level to the signal
|
|
* received.
|
|
*/
|
|
if (sig == LVLQ)
|
|
new_state = cur_state;
|
|
else
|
|
new_state = sig;
|
|
|
|
/*
|
|
* Clear all times and repeat counts in the process table
|
|
* since either the level is changing or the user has editted
|
|
* the inittab file and wants us to look at it again.
|
|
* If the user has fixed a typo, we don't want residual timing
|
|
* data preventing the fixed command line from executing.
|
|
*/
|
|
for (process = &proc_table[0]; process < &proc_table[NPROC]; process++){
|
|
process->p_time = 0L;
|
|
process->p_count = 0;
|
|
}
|
|
|
|
/*
|
|
* Set the flag to indicate that a "user signal" was received.
|
|
*/
|
|
wakeup.w_flags.w_usersignal = 1;
|
|
}
|
|
|
|
|
|
/************************/
|
|
/**** alarmclk ****/
|
|
/************************/
|
|
|
|
void
|
|
alarmclk(void)
|
|
{
|
|
time_up = TRUE;
|
|
}
|
|
|
|
|
|
/* Used to a lot in the signal handler, which is bogus.
|
|
* Now all we do is set a flag and return.
|
|
* handler. Under heavy system stress, inst ould die, and it was
|
|
* partly a side effect of this.
|
|
* Was using signal; I've changed to sigset to
|
|
* make it slightly safer. Also, we never set SIGCLD back to
|
|
* default anywhere, so we don't lose any children. use hold/relse
|
|
* instead. wakeup.w_flags.w_childdeath can only be set or cleared
|
|
* while SIGCLD is blocked. It can be tested when it's not blocked,
|
|
* because handle_children is a nop if there weren't really any childred.
|
|
*/
|
|
/* ARGSUSED */
|
|
void
|
|
childeath(int signo)
|
|
{
|
|
wakeup.w_flags.w_childdeath = 1;
|
|
}
|
|
|
|
/*
|
|
* use waitpid here, and if no children to wait for without
|
|
* blocking, return immediately; keep looping while any children to
|
|
* wait for. This all used to be part of childeath.
|
|
* we hold sigcld for the duration.
|
|
*/
|
|
void
|
|
handle_children(int leaveheld)
|
|
{
|
|
register struct PROC_TABLE *process;
|
|
register struct pidlist *pp;
|
|
register pid_t pid;
|
|
int status, gchild = 0;
|
|
|
|
sighold(SIGCLD);
|
|
wakeup.w_flags.w_childdeath = 0;
|
|
|
|
/*
|
|
* get the process id of children that have exited, if any.
|
|
* Don't block; if we get here with no children to wait for,
|
|
* somebody sent us SIGCLD directly, we waited elsewhere, or
|
|
* we have missed a timing race somewhere.
|
|
* Scan the process table to see if we are interested in
|
|
* this process.
|
|
*/
|
|
while((pid = waitpid(-1, &status, WNOHANG)) > 0) {
|
|
#ifdef UDEBUG
|
|
debug("childeath: pid- %ld status- %x\n", pid , status);
|
|
#endif
|
|
for (process = &proc_table[0]; process < &proc_table[NPROC]; process++){
|
|
if ((process->p_flags & OCCUPIED) == OCCUPIED &&
|
|
process->p_pid == pid) {
|
|
/*
|
|
* Mark this process as having died and store the exit
|
|
* status. Also set the wakeup flag for a dead child
|
|
* and break out of the loop.
|
|
*/
|
|
process->p_flags &= ~LIVING;
|
|
process->p_exit = (short)status;
|
|
|
|
#ifdef UDEBUG
|
|
if (process == &proc_table[NPROC])
|
|
debug("Didn't find process %ld.\n", pid);
|
|
#endif
|
|
break;
|
|
}
|
|
}
|
|
|
|
if(process < &proc_table[NPROC])
|
|
continue;
|
|
/* else no process was found above, look through auxiliary list. */
|
|
(void)sighold(SIGPOLL);
|
|
pp = Plhead;
|
|
while (pp) {
|
|
if (pid > pp->pl_pid) {
|
|
/* Keep on looking. */
|
|
pp = pp->pl_next;
|
|
continue;
|
|
}
|
|
else if (pid < pp->pl_pid) {
|
|
/* Not in the list. */
|
|
break;
|
|
}
|
|
else {
|
|
/* This is a dead "godchild". */
|
|
pp->pl_dflag = 1;
|
|
pp->pl_exit = (short)status;
|
|
gchild = 1;
|
|
break;
|
|
}
|
|
}
|
|
|
|
sigrelse(SIGPOLL);
|
|
}
|
|
|
|
if(gchild)
|
|
cleanaux();
|
|
|
|
if(!leaveheld)
|
|
sigrelse(SIGCLD);
|
|
}
|
|
|
|
|
|
/*************************/
|
|
/**** powerfail ****/
|
|
/*************************/
|
|
|
|
void
|
|
powerfail(void)
|
|
{
|
|
nice(-19);
|
|
wakeup.w_flags.w_powerhit = 1;
|
|
}
|
|
|
|
|
|
/*
|
|
* Procedure: getlvl
|
|
*
|
|
* Restrictions:
|
|
fopen: None
|
|
fcntl(2): None
|
|
fclose: None
|
|
fflush: None
|
|
fscanf: None
|
|
*/
|
|
|
|
/*
|
|
* Get the new run level from /dev/syscon. If someone at /dev/systty
|
|
* types a <del> while we are waiting for the user to start typing,
|
|
* relink /dev/syscon to /dev/systty.
|
|
*/
|
|
int
|
|
getlvl(void)
|
|
{
|
|
char c;
|
|
int status, flag;
|
|
FILE *fp_tmp;
|
|
register int process;
|
|
static char levels[] = {
|
|
LVL0,LVL1,LVL2,LVL3,LVL4,LVL5,LVL6,SINGLE_USER
|
|
};
|
|
#ifdef DEBUG
|
|
debug("%d:getlvl():ENTER\n",getpid());
|
|
#endif
|
|
|
|
/*
|
|
* fork a child who will request the new run level from /dev/syscon.
|
|
*/
|
|
while ((process = fork()) == -1)
|
|
;
|
|
if (process == 0) {
|
|
sigset(SIGHUP, SIG_IGN);
|
|
sigset(SIGCLD, SIG_DFL);
|
|
|
|
/*
|
|
* Open /dev/systty so that if someone types a <del>,
|
|
* we can be informed of the fact.
|
|
*/
|
|
if ((fp_tmp = fopen(SYSTTY,"r+")) != NULL) {
|
|
/*
|
|
* Make sure the file descriptor is greater than 2 so
|
|
* that it won't interfere with the standard descriptors
|
|
*/
|
|
fd_systty = fcntl(fileno(fp_tmp), F_DUPFD, 3);
|
|
(void)fdopen(fd_systty, "r+");
|
|
fclose(fp_tmp);
|
|
|
|
/*
|
|
* Prepare to catch the interupt signal if <del> typed
|
|
* at /dev/systty.
|
|
*/
|
|
sigset(SIGINT, switchcon);
|
|
sigset(SIGQUIT, switchcon);
|
|
}
|
|
|
|
#ifdef UDEBUG
|
|
sigset(SIGUSR1, abort);
|
|
sigset(SIGUSR2, abort);
|
|
#endif
|
|
|
|
for (;;) {
|
|
/*
|
|
* Close the current descriptors and open
|
|
* ones to consdevdsf.
|
|
*/
|
|
opensyscon();
|
|
|
|
/*
|
|
* Print something unimportant and pause, since reboot
|
|
* may be taking place over a line coming in over the
|
|
* dataswitch. The dataswitch sometimes gets the
|
|
* carrier up before the connection is complete and
|
|
* the first write gets lost.
|
|
*/
|
|
fprintf(stdout,"\n");
|
|
timer(2);
|
|
|
|
flag = TRUE;
|
|
while(flag) {
|
|
|
|
/*
|
|
* Now read in the user response.
|
|
*/
|
|
fprintf(stdout,
|
|
"ENTER RUN LEVEL (0-6,s or S): ");
|
|
fflush(stdout);
|
|
|
|
/*
|
|
* Get a character from the user which isn't a
|
|
* space, tab or a <cr>.
|
|
*/
|
|
while (fscanf(stdin, "%c", &c) != 1 ||
|
|
c == '\n' || c == '\t' || c == ' ')
|
|
;
|
|
c &= 0x7f;
|
|
|
|
/*
|
|
* If the character is a digit between 0 and 6
|
|
* or the letter S, exit with the level equal to
|
|
* the new desired state.
|
|
*/
|
|
if (c >= '0' && c <= '6') {
|
|
fprintf(stdout,
|
|
"will change to state %c\n", c);
|
|
exit(levels[c - '0']);
|
|
} else if (c == 'S' || c == 's') {
|
|
fprintf(stdout,
|
|
"will change to state %c\n", c);
|
|
exit(levels['7' - '0']);
|
|
} else if (c > '6' && c <= '9') {
|
|
fprintf(stdout,"\nUsage: 0123456sS\n");
|
|
fprintf(stdout,
|
|
" %c is not a valid state\n\n",c);
|
|
while ((fscanf(stdin, "%c", &c) != 1) ||
|
|
(c != '\n'))
|
|
;
|
|
} else {
|
|
fprintf(stdout,
|
|
"\nbad character <%3.3o>\n\n",c);
|
|
while ((fscanf(stdin,"%c",&c) != 1) ||
|
|
(c != '\n'))
|
|
;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Wait for the child to die and return it's status.
|
|
*/
|
|
(void)waitpid(process, &status, 0);
|
|
|
|
#ifdef DEBUG
|
|
debug("getlvl: status: %o exit: %o termination: %o\n",
|
|
status, (status & 0xff00)>>8, (status & 0xff));
|
|
debug("%d:getlvl():RETURN\n",getpid());
|
|
#endif
|
|
|
|
return((status & 0xff00) >> 8);
|
|
}
|
|
|
|
|
|
/*************************/
|
|
/**** switchcon ****/
|
|
/*************************/
|
|
/* ARGSUSED */
|
|
void
|
|
switchcon(int sig)
|
|
{
|
|
/*
|
|
* If this is the first time a <del> has been typed on the
|
|
* real/default system console device (the one associated with SYSTTY),
|
|
* then reset console device to the device associated with SYSTTY.
|
|
* Also re-establish file pointers.
|
|
*/
|
|
if (fd_systty != -1) {
|
|
reset_syscon();
|
|
opensyscon();
|
|
|
|
/*
|
|
* Set fd_systty to -1 so that we ignore any deletes from it in
|
|
* the future as far as resetting console device to real/default
|
|
* system console device (the one associated with SYSTTY).
|
|
*/
|
|
fd_systty = -1;
|
|
}
|
|
}
|
|
|
|
|
|
/*********************/
|
|
/**** efork ****/
|
|
/*********************/
|
|
|
|
/*
|
|
* efork() forks a child and the parent inserts the process in its table
|
|
* of processes that are directly a result of forks that it has performed.
|
|
* The child just changes the "global" with the process id for this process
|
|
* to it's new value.
|
|
* If efork() is called with a pointer into the proc_table it uses that slot,
|
|
* otherwise it searches for a free slot. Regardless of how it was called,
|
|
* it returns the pointer to the proc_table entry
|
|
*/
|
|
struct PROC_TABLE *
|
|
efork(int action, struct PROC_TABLE *process, int modes)
|
|
{
|
|
register pid_t childpid;
|
|
register struct PROC_TABLE *proc;
|
|
int i;
|
|
|
|
/*
|
|
* Freshen up the proc_table, removing any entries for dead processes
|
|
* that don't have NOCLEANUP set. Perform the necessary accounting.
|
|
*/
|
|
for (proc = &proc_table[0]; proc < &proc_table[NPROC]; proc++) {
|
|
if((proc->p_flags & (OCCUPIED|LIVING|NOCLEANUP)) == (OCCUPIED)){
|
|
#ifdef DEBUG
|
|
debug("efork- id:%s pid: %ld time: %lo %d %o %o\n",
|
|
C(&proc->p_id[0]), proc->p_pid, proc->p_time,
|
|
proc->p_count, proc->p_flags, proc->p_exit);
|
|
#endif
|
|
|
|
/*
|
|
* Is this a named process?
|
|
* If so, do the necessary bookkeeping.
|
|
*/
|
|
if (proc->p_flags & NAMED)
|
|
account(DEAD_PROCESS, proc, NULL);
|
|
|
|
/*
|
|
* Free this entry for new usage.
|
|
*/
|
|
proc->p_flags = 0;
|
|
}
|
|
}
|
|
|
|
while ((childpid = fork()) == FAILURE) {
|
|
/*
|
|
* Shorten the alarm timer in case someone else's child dies
|
|
* and free up a slot in the process table.
|
|
*/
|
|
setimer(5);
|
|
pause();
|
|
setimer(0);
|
|
if(wakeup.w_flags.w_childdeath)
|
|
handle_children(0);
|
|
}
|
|
|
|
if (childpid != 0) {
|
|
|
|
if (process == NULLPROC) {
|
|
/*
|
|
* No proc table pointer specified so search
|
|
* for a free slot.
|
|
*/
|
|
for (process = &proc_table[0]; process->p_flags != 0 &&
|
|
process < &proc_table[NPROC]; process++)
|
|
;
|
|
|
|
if (process == &proc_table[NPROC]) {
|
|
if (error_time(FULLTABLE))
|
|
console("Internal process table is full\n");
|
|
return(NO_ROOM);
|
|
}
|
|
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;
|
|
} else {
|
|
/*
|
|
* Reset child's concept of its own process id.
|
|
*/
|
|
own_pid = getpid();
|
|
|
|
if (action != M_WAIT)
|
|
setpgrp();
|
|
|
|
process = NULLPROC;
|
|
|
|
/*
|
|
* Reset all signals in child to the system defaults,
|
|
* making sure that SIGXCPU and SIGXFSZ remain
|
|
* ignored, for backward compatibility.
|
|
*/
|
|
for (i=1; i < NSIG; i++)
|
|
if(i != SIGXCPU && i != SIGXFSZ)
|
|
sigset(i, SIG_DFL);
|
|
}
|
|
return(process);
|
|
}
|
|
|
|
|
|
/************************/
|
|
/**** waitproc ****/
|
|
/************************/
|
|
|
|
/*
|
|
* waitproc() waits for a specified process to die. For this function to
|
|
* work, the specified process must already in the proc_table. waitproc()
|
|
* returns the exit status of the specified process when it dies.
|
|
*/
|
|
long
|
|
waitproc(struct PROC_TABLE *process)
|
|
{
|
|
int answer;
|
|
|
|
/* handle any exited children; then leave SIGCLD blocked */
|
|
handle_children(1);
|
|
if (process->p_flags & LIVING) {
|
|
sigpause(SIGCLD);
|
|
if(wakeup.w_flags.w_childdeath)
|
|
handle_children(0);
|
|
if(process->p_flags & LIVING)
|
|
return (FAILURE); /* some other signal or child */
|
|
}
|
|
|
|
/*
|
|
* Make sure to only return 16 bits so that answer will always
|
|
* be positive whenever the process of interest really died.
|
|
*/
|
|
answer = (process->p_exit & 0xffff);
|
|
|
|
/*
|
|
* Free the slot in the proc_table.
|
|
*/
|
|
process->p_flags = 0;
|
|
return(answer);
|
|
}
|
|
|
|
|
|
/***********************/
|
|
/**** account ****/
|
|
/***********************/
|
|
|
|
/*
|
|
* Procedure: account
|
|
*
|
|
* Restrictions:
|
|
getutxid: None
|
|
pututxline: None
|
|
updwtmpx: None
|
|
*/
|
|
/*
|
|
* account() updates entries in utmp and utmpx and appends new entries
|
|
* new entries to the end of wtmp and wtmpx (assuming they exist).
|
|
* The program arg is the name of program if INIT_PROCESS, otherwise NULL.
|
|
*/
|
|
void
|
|
account(short state, struct PROC_TABLE *process, char *program)
|
|
{
|
|
struct utmpx utmpbuf;
|
|
register struct utmpx *u, *oldu;
|
|
|
|
#ifdef ACCTDEBUG
|
|
debug("** account ** state: %d id:%s\n", state, C(&process->p_id[0]));
|
|
#endif
|
|
/*
|
|
* Audit dead process.
|
|
*/
|
|
if ((int)state == DEAD_PROCESS) {
|
|
satvwrite(SAT_AE_IDENTITY, SAT_SUCCESS,
|
|
"INIT|+|?|Logout pid %d id %c%c%c%c",
|
|
process->p_pid, process->p_id[0], process->p_id[1],
|
|
process->p_id[2], process->p_id[3]);
|
|
}
|
|
|
|
/*
|
|
* Set up the prototype for the utmp structure we want to write.
|
|
*/
|
|
u = &utmpbuf;
|
|
bzero(&u->ut_user[0], sizeof(u->ut_user));
|
|
bzero(&u->ut_line[0], sizeof(u->ut_line));
|
|
bzero(&u->ut_host[0], sizeof(u->ut_host));
|
|
|
|
/*
|
|
* Fill in the various fields of the utmp structure.
|
|
*/
|
|
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;
|
|
|
|
/*
|
|
* Fill the "ut_exit" structure.
|
|
*/
|
|
u->ut_exit.e_termination = process->p_exit & 0xff;
|
|
u->ut_exit.e_exit = process->p_exit >> 8 & 0xff;
|
|
u->ut_type = state;
|
|
time(&u->ut_tv.tv_sec);
|
|
|
|
/*
|
|
* See if there already is such an entry in the "utmp" file.
|
|
*/
|
|
setutxent(); /* Start at beginning of utmp file. */
|
|
|
|
if ((oldu = getutxid(u)) != NULL) {
|
|
/*
|
|
* Copy in the old "user", "line" and "host" fields
|
|
* to our new structure.
|
|
*/
|
|
bcopy(&oldu->ut_user[0], &u->ut_user[0], sizeof(u->ut_user));
|
|
bcopy(&oldu->ut_line[0], &u->ut_line[0], sizeof(u->ut_line));
|
|
bcopy(&oldu->ut_host[0], &u->ut_host[0], sizeof(u->ut_host));
|
|
#ifdef ACCTDEBUG
|
|
debug("New entry in utmp file.\n");
|
|
#endif
|
|
}
|
|
#ifdef ACCTDEBUG
|
|
else debug("Replacing old entry in utmp file.\n");
|
|
#endif
|
|
|
|
/*
|
|
* Perform special accounting. Insert the special string into the
|
|
* ut_line array. For INIT_PROCESSes put in the name of the
|
|
* program in the "ut_user" field.
|
|
*/
|
|
switch(state) {
|
|
|
|
case RUN_LVL:
|
|
u->ut_exit.e_termination = level(cur_state);
|
|
if (program != NULL && oldu != NULL && *program == 'S')
|
|
u->ut_exit.e_exit = oldu->ut_exit.e_termination;
|
|
else
|
|
u->ut_exit.e_exit = level(prior_state);
|
|
|
|
u->ut_pid = n_prev[cur_state];
|
|
sprintf(&u->ut_line[0], RUNLVL_MSG, level(cur_state));
|
|
break;
|
|
|
|
case BOOT_TIME:
|
|
sprintf(&u->ut_line[0], "%.12s",BOOT_MSG);
|
|
break;
|
|
|
|
case INIT_PROCESS:
|
|
strncpy(&u->ut_user[0], program, sizeof(u->ut_user));
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
|
|
/*
|
|
* Write out the updated entry to utmp file.
|
|
*/
|
|
if ((pututxline(u) == (struct utmpx *)NULL) && (cur_state != LVL0) && (cur_state != LVL6))
|
|
console("failed write of utmpx entry:\"%2.2s\"\n",&u->ut_id[0]);
|
|
endutxent();
|
|
|
|
/*
|
|
* Now attempt to add to the end of the wtmp and wtmpx files.
|
|
*/
|
|
updwtmpx(WTMPX, u);
|
|
}
|
|
|
|
|
|
/*************************/
|
|
/**** prog_name ****/
|
|
/*************************/
|
|
|
|
/*
|
|
* prog_name() searches for the word or unix path name and
|
|
* returns a pointer to the last element of the pathname.
|
|
*/
|
|
char *
|
|
prog_name(char *string)
|
|
{
|
|
register char *ptr, *ptr2;
|
|
struct utmp *dummy; /* Used only to get size of ut_user */
|
|
static char word[sizeof(dummy->ut_user) + 1];
|
|
|
|
/*
|
|
* Search for the first word skipping leading spaces and tabs.
|
|
*/
|
|
while (*string == ' ' || *string == '\t')
|
|
string++;
|
|
|
|
/*
|
|
* If the first non-space non-tab character is not one allowed in
|
|
* a word, return a pointer to a null string, otherwise parse the
|
|
* pathname.
|
|
*/
|
|
if (*string != '.' && *string != '/' && *string != '_' &&
|
|
(*string < 'a' || *string > 'z') &&
|
|
(*string < 'A' || * string > 'Z') &&
|
|
(*string < '0' || *string > '9'))
|
|
return("");
|
|
|
|
/*
|
|
* Parse the pathname looking forward for '/', ' ', '\t', '\n' or
|
|
* '\0'. Each time a '/' is found, move "ptr" to one past the
|
|
* '/', thus when a ' ', '\t', '\n', or '\0' is found, "ptr" will
|
|
* point to the last element of the pathname.
|
|
*/
|
|
for (ptr = string;
|
|
*string !=' ' && *string !='\t' && *string !='\n' && *string !='\0';
|
|
string++) {
|
|
if (*string == '/')
|
|
ptr = string+1;
|
|
}
|
|
|
|
/*
|
|
* Copy out up to the size of the "ut_user" array into "word",
|
|
* null terminate it and return a pointer to it.
|
|
*/
|
|
for (ptr2 = &word[0];
|
|
ptr2 < &word[sizeof(dummy->ut_user)] && ptr < string;)
|
|
*ptr2++ = *ptr++;
|
|
|
|
*ptr2 = '\0';
|
|
return(&word[0]);
|
|
}
|
|
|
|
|
|
/*
|
|
* Procedure: opensyscon
|
|
*
|
|
* Restrictions:
|
|
fclose: None
|
|
fopen: None
|
|
setbuf: None
|
|
ioctl(2): None
|
|
*/
|
|
|
|
/*
|
|
* opensyscon() opens stdin, stdout, and stderr, making sure
|
|
* that their file descriptors are 0, 1, and 2, respectively.
|
|
*/
|
|
void
|
|
opensyscon(void)
|
|
{
|
|
register FILE *fp;
|
|
#ifdef DEBUG
|
|
debug("%d:opensyscon():ENTER\n",getpid());
|
|
#endif
|
|
|
|
fclose(stdin);
|
|
fclose(stdout);
|
|
fclose(stderr);
|
|
close(0);
|
|
close(1);
|
|
close(2);
|
|
|
|
#ifdef DEBUG
|
|
debug("opensyscon():consdevdsf = %s\n",consdevdsf);
|
|
#endif
|
|
if ((fp = fopen(consdevdsf,"r+")) == NULL) {
|
|
/*
|
|
* If the open fails, switch back to real/default system
|
|
* console device (associated with SYSTTY).
|
|
*/
|
|
reset_syscon();
|
|
fp = fopen(consdevdsf,"r+");
|
|
if(!fp) fp = stderr;
|
|
}
|
|
|
|
(void)fdup(fp);
|
|
(void)fdup(fp);
|
|
setbuf(fp, NULL);
|
|
setbuf(stdout, NULL);
|
|
setbuf(stderr, NULL);
|
|
|
|
/*
|
|
* Save the current consdevdsf modes and restore the modes stored in
|
|
* termio (modes from the IOCTL.SYSCON file or default modes).
|
|
* The current modes will be restored by console() after the
|
|
* message is printed.
|
|
*/
|
|
fioctl(fp, TCGETA, &curterm);
|
|
|
|
/*
|
|
* If the baud rate is B0, reset it to the default baud rate.
|
|
*/
|
|
if (curterm.c_ospeed == B0)
|
|
curterm.c_ospeed = dflt_termio.c_ospeed;
|
|
|
|
|
|
curterm.c_cflag &= ~HUPCL; /* Make sure hangup on close is off. */
|
|
|
|
if (realcon()) {
|
|
/*
|
|
* Don't overwrite cflag when init console is real console.
|
|
*/
|
|
termio.c_cflag = curterm.c_cflag;
|
|
termio.c_ospeed = curterm.c_ospeed;
|
|
}
|
|
termio.c_cflag &= ~HUPCL; /* Make sure hangup on close is off. */
|
|
fioctl(fp, TCSETA, &termio);
|
|
#ifdef DEBUG
|
|
debug("%d:opensyscon():RETURN\n",getpid());
|
|
#endif
|
|
return;
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
* Procedure: realcon
|
|
*
|
|
* Restrictions:
|
|
stat(2): None
|
|
open(2): None
|
|
*/
|
|
/*
|
|
* realcon() returns a nonzero value if there is a character device
|
|
* associated with consdevdsf that has the same device number as real, default,
|
|
* console device.
|
|
*
|
|
* If TP is configure in the system and TP is on the console, have to call
|
|
* tp_getinf to get the TP information structure which is associated with the
|
|
* physical device linked under the TP console device. The stat(2) information
|
|
* in the TP information is compared to the stat(2) of SYSTTY instead of CONSOLE
|
|
* because CONSOLE is linked to TP_CONSDEV_DSF.
|
|
*/
|
|
int
|
|
realcon(void)
|
|
{
|
|
struct stat consdevdsfbuf, consolebuf;
|
|
int ret = 1;
|
|
|
|
if ((stat(consdevdsf, &consdevdsfbuf) == -1) ||
|
|
(stat(SYSTTY, &consolebuf) == -1)){
|
|
ret = 0;
|
|
}
|
|
if ( !(consolebuf.st_mode & S_IFCHR) ||
|
|
!(consdevdsfbuf.st_mode & S_IFCHR) ||
|
|
(consolebuf.st_rdev != consdevdsfbuf.st_rdev)){
|
|
ret = 0;
|
|
}
|
|
return (ret);
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
* Procedure: get_ioctl_syscon
|
|
*
|
|
* Restrictions:
|
|
fopen: None
|
|
fscanf: None
|
|
fclose: None
|
|
*/
|
|
/*
|
|
* get_ioctl_syscon() retrieves the consdevdsf settings from the
|
|
* IOCTLSYSCON file.
|
|
*/
|
|
void
|
|
get_ioctl_syscon(void)
|
|
{
|
|
register FILE *fp;
|
|
int valid_format = 0;
|
|
|
|
/*
|
|
* Read in the previous modes for SYSCON from IOCTLSYSCON.
|
|
*/
|
|
if ((fp = fopen(IOCTLSYSCON, "r")) == NULL) {
|
|
console("warning:%s does not exist, default settings assumed\n",
|
|
IOCTLSYSCON);
|
|
reset_syscon();
|
|
} else {
|
|
|
|
valid_format = rd_ioctlsyscon(fp);
|
|
fclose(fp);
|
|
|
|
/*
|
|
* If the file is badly formatted, use the default settings.
|
|
*/
|
|
if (!valid_format)
|
|
reset_syscon();
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
* Procedure: reset_syscon
|
|
*
|
|
* Restrictions:
|
|
fopen: None
|
|
lvlin: None
|
|
lvlfile(2): None
|
|
fclose: None
|
|
*/
|
|
|
|
|
|
/*
|
|
* Switch the console back to the default system console device SYSTTY and
|
|
* set the default ioctl settings back into IOCTLSYSCON and the incore arrays.
|
|
*
|
|
* If the root file system has not been verified, do not perform the switch
|
|
* or set the ioctl settings.
|
|
* NOTE: The code has been implemented such that this function should not be
|
|
* called before the root file system has been verified.
|
|
*
|
|
* If TP is not configured on the system, SYSCON is relinked to SYSTTY.
|
|
*/
|
|
void
|
|
reset_syscon(void)
|
|
{
|
|
register FILE *fp;
|
|
mode_t old_umask;
|
|
#ifdef DEBUG
|
|
debug("%d:reset_syscon():ENTER\n",getpid());
|
|
#endif
|
|
|
|
if (rootfs_verified != TRUE){
|
|
return;
|
|
}
|
|
if (devtab[DTTAG_SYSTTY].dt_status == dtsVERIFIED){
|
|
devtab[DTTAG_SYSCON].dt_major = devtab[DTTAG_SYSTTY].dt_major;
|
|
devtab[DTTAG_SYSCON].dt_minor = devtab[DTTAG_SYSTTY].dt_minor;
|
|
devtab[DTTAG_SYSCON].dt_linkdsfname = SYSTTY;
|
|
devtab[DTTAG_SYSCON].dt_action = dtaLINK;
|
|
devtab[DTTAG_CONSOLE].dt_action = dtaLINK;
|
|
devtab[DTTAG_SYSTTY].dt_action = dtaMKNOD;
|
|
} else {
|
|
devtab[DTTAG_SYSCON].dt_major = devtab[DTTAG_CONSOLE].dt_major;
|
|
devtab[DTTAG_SYSCON].dt_minor = devtab[DTTAG_CONSOLE].dt_minor;
|
|
devtab[DTTAG_SYSCON].dt_linkdsfname = CONSOLE;
|
|
devtab[DTTAG_SYSCON].dt_action = dtaLINK;
|
|
devtab[DTTAG_CONSOLE].dt_action = dtaMKNOD;
|
|
}
|
|
(void)update_devtab();
|
|
consdevdsf = SYSCON;
|
|
old_umask = umask(~0644);
|
|
fp = fopen(IOCTLSYSCON, "w");
|
|
|
|
bcopy((char *)&dflt_termio, (char *)&termio, sizeof(struct termio));
|
|
wr_ioctlsyscon(fp);
|
|
fclose(fp);
|
|
/* put back original umask */
|
|
umask(old_umask);
|
|
#ifdef DEBUG
|
|
debug("%d:reset_syscon():RETURN\n",getpid());
|
|
#endif
|
|
}
|
|
|
|
|
|
/*
|
|
* Procedure: reset_modes
|
|
*
|
|
* Restrictions:
|
|
fopen: None
|
|
ioctl(2): None
|
|
fclose: None
|
|
fscanf: None
|
|
*/
|
|
/*
|
|
* reset_modes() makes sure the proper terminal modes are set so init can
|
|
* continue talking to consdevdsf after coming down to single user and after
|
|
* rebooting. It must see that the proper modes are set in the driver,
|
|
* init's in-core termio structure, and the ioctl.syscon file.
|
|
*/
|
|
void
|
|
reset_modes(void)
|
|
{
|
|
register FILE *fp;
|
|
register struct PROC_TABLE *process;
|
|
struct termio tio;
|
|
ulong curcflag = 0;
|
|
speed_t curospeed = B0;
|
|
|
|
while ((process = efork(M_OFF, NULLPROC, NOCLEANUP)) == NO_ROOM)
|
|
timer(2);
|
|
|
|
if (process == NULLPROC) {
|
|
if ((fp = fopen(consdevdsf,"w")) == NULL) {
|
|
console("Unable to open %s\n", consdevdsf);
|
|
} else {
|
|
if (fioctl(fp, TCGETA, &tio) != FAILURE) {
|
|
curcflag = tio.c_cflag;
|
|
curospeed = tio.c_ospeed;
|
|
/*
|
|
* Clear HUPCL in the driver.
|
|
*/
|
|
tio.c_cflag &= ~HUPCL;
|
|
fioctl(fp, TCSETA, &tio);
|
|
}
|
|
fclose(fp);
|
|
}
|
|
|
|
if ((fp = fopen(IOCTLSYSCON, "r")) != NULL) {
|
|
/*
|
|
* Update the in-core termio structure so it agrees
|
|
* with the ioctl.syscon file. Better sanity checking
|
|
* should probably be done here on the ioctl.syscon
|
|
* data.
|
|
*/
|
|
(void)rd_ioctlsyscon(fp);
|
|
fclose(fp);
|
|
}
|
|
if (!realcon() && curcflag != 0) {
|
|
/*
|
|
* The virtual console is different from the
|
|
* physical console so we set the cflag in the
|
|
* in-core termio and in ioctl.syscon to the current
|
|
* cflag setting. This ensures that the settings for
|
|
* this device will be correct when we reach single
|
|
* user and after reboot. We don't reset the other
|
|
* (non-cflag) fields because the current settings
|
|
* may be inappropriate for the single user shell.
|
|
*/
|
|
termio.c_cflag = curcflag;
|
|
termio.c_ospeed = curospeed;
|
|
}
|
|
|
|
umask(~0644);
|
|
if ((fp = fopen(IOCTLSYSCON, "w")) == NULL) {
|
|
console("Can't open %s. errno: %d\n",IOCTLSYSCON,errno);
|
|
} else {
|
|
wr_ioctlsyscon(fp);
|
|
fclose(fp);
|
|
}
|
|
termio.c_cflag &= ~HUPCL;
|
|
exit(0);
|
|
} else {
|
|
/*
|
|
* The parent waits for the child to die.
|
|
*/
|
|
while (waitproc(process) == FAILURE)
|
|
;
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
* Procedure: console
|
|
*
|
|
* Restrictions:
|
|
setbuf: None
|
|
fflush: None
|
|
ioctl(2): None
|
|
*/
|
|
|
|
/*
|
|
* console() forks a child if it finds that it is the main "init" and outputs
|
|
* the requested message to the system console. Note that the number of
|
|
* arguments passed to console() is determined by the print format.
|
|
*
|
|
* NOTE: The following list of functions are directly or indirectly called
|
|
* from this function. They should not call the function console() or any
|
|
* other function that may directly or indirectly call console, since it may
|
|
* cause an infinite call loop to console() if one of the code pathes from
|
|
* console() is failing.
|
|
* efork(), write_messge(), opensyscon(), reset_syscon(), realcon(),
|
|
* realcon386(), tpswitchcons(), opentpdev(), update_devtab(), and
|
|
* setup_consdev()
|
|
*/
|
|
/* PRINTFLIKE1 */
|
|
void
|
|
console(char *format, ...)
|
|
{
|
|
register struct PROC_TABLE *process;
|
|
char outbuf[BUFSIZ];
|
|
va_list args;
|
|
#ifdef DEBUG
|
|
debug("%d:console():ENTER\n",getpid());
|
|
#endif
|
|
|
|
if (own_pid == SPECIALPID) {
|
|
/*
|
|
* We are the original "init" so we fork a child to do the
|
|
* printing for us.
|
|
*/
|
|
while ((process = efork(M_OFF, NULLPROC, NOCLEANUP)) == NO_ROOM)
|
|
timer(5);
|
|
if (process == NULLPROC) {
|
|
#ifdef UDEBUG
|
|
sigset(SIGUSR1, abort);
|
|
sigset(SIGUSR2, abort);
|
|
#endif
|
|
/*
|
|
* Close the standard descriptors and open the console.
|
|
*/
|
|
opensyscon();
|
|
setbuf(stdout, &outbuf[0]);
|
|
|
|
/*
|
|
* Output the message to the console.
|
|
*/
|
|
fprintf(stdout, "\nINIT: ");
|
|
va_start(args, format);
|
|
vfprintf(stdout, format, args);
|
|
va_end(args);
|
|
fflush(stdout);
|
|
|
|
/*
|
|
* Restore the settings saved in opensyscon().
|
|
*/
|
|
fioctl(stdout, TCSETAW, &curterm);
|
|
exit(0);
|
|
} else {
|
|
while ( waitproc(process) == FAILURE)
|
|
;
|
|
}
|
|
|
|
} else {
|
|
/*
|
|
* We are some other "init" so print directly
|
|
* to the standard output.
|
|
*/
|
|
opensyscon();
|
|
setbuf(stdout, &outbuf[0]);
|
|
fprintf(stdout, "\nINIT: ");
|
|
va_start(args, format);
|
|
vfprintf(stdout, format, args);
|
|
va_end(args);
|
|
fflush(stdout);
|
|
|
|
/*
|
|
* Restore the settings saved in opensyscon().
|
|
*/
|
|
fioctl(stdout, TCSETAW, &curterm);
|
|
}
|
|
|
|
|
|
#ifdef DEBUG
|
|
debug("%d:console():RETURN\n",getpid());
|
|
#endif
|
|
}
|
|
|
|
|
|
|
|
/**************************/
|
|
/**** error_time ****/
|
|
/**************************/
|
|
|
|
/*
|
|
* error_time() keeps a table of times, one for each type of error that it
|
|
* handles. If the current entry is 0 or the elapsed time since the last error
|
|
* message is large enough, error_time() returns TRUE, else it returns FALSE.
|
|
*/
|
|
int
|
|
error_time(int type)
|
|
{
|
|
long curtime;
|
|
|
|
time(&curtime);
|
|
if (err_times[type].e_time == 0 ||
|
|
curtime - err_times[type].e_time >= err_times[type].e_max) {
|
|
err_times[type].e_time = curtime;
|
|
return(TRUE);
|
|
} else {
|
|
return(FALSE);
|
|
}
|
|
}
|
|
|
|
|
|
/*********************/
|
|
/**** timer ****/
|
|
/*********************/
|
|
|
|
/*
|
|
* timer() is a substitute for sleep() which uses alarm() and pause().
|
|
*/
|
|
void
|
|
timer(int waitime)
|
|
{
|
|
setimer(waitime);
|
|
while (time_up == FALSE)
|
|
pause();
|
|
}
|
|
|
|
|
|
/***********************/
|
|
/**** setimer ****/
|
|
/***********************/
|
|
|
|
void
|
|
setimer(int timelimit)
|
|
{
|
|
alarmclk();
|
|
alarm(timelimit);
|
|
time_up = (timelimit ? FALSE : TRUE);
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
* Procedure: userinit
|
|
*
|
|
* Restrictions:
|
|
open(2): None
|
|
ttyname: None
|
|
stat(2): None
|
|
unlink(2): None
|
|
link(2): None
|
|
fopen: None
|
|
fclose: None
|
|
kill(2): None
|
|
*/
|
|
|
|
/*
|
|
* Function to handle requests from users to main init running as process 1.
|
|
*/
|
|
void
|
|
userinit(int argc, char **argv)
|
|
{
|
|
FILE *fp;
|
|
char *userttydsf;
|
|
int saverr;
|
|
int init_signal;
|
|
int fd;
|
|
int statret = 0;
|
|
struct stat consdevdsfbuf;
|
|
struct stat userttydsfbuf;
|
|
cap_t ocap;
|
|
cap_value_t kill_caps[] = {CAP_KILL, CAP_MAC_WRITE};
|
|
#ifdef DEBUG
|
|
debug("%d:userinit():ENTER\n",getpid());
|
|
#endif
|
|
|
|
/* save command line arguments for audit record */
|
|
if (argvtostr(argv) == NULL)
|
|
/* This should never happen! */
|
|
(void) fprintf(stderr,"failed argvtostr\n");
|
|
|
|
/*
|
|
* We are a user invoked init. Is there an argument and is it
|
|
* a single character? If not, print usage message and quit.
|
|
*/
|
|
if (argc != 2 || argv[1][1] != '\0') {
|
|
fprintf(stderr,"Usage: init [0123456SsQqabc]\n");
|
|
exit(0);
|
|
}
|
|
|
|
doavailmon(argv[1][0]);
|
|
|
|
switch (argv[1][0]) {
|
|
|
|
case 'Q':
|
|
case 'q':
|
|
init_signal = LVLQ;
|
|
break;
|
|
|
|
case '0':
|
|
init_signal = LVL0;
|
|
break;
|
|
|
|
case '1':
|
|
init_signal = LVL1;
|
|
break;
|
|
|
|
case '2':
|
|
init_signal = LVL2;
|
|
break;
|
|
|
|
case '3':
|
|
init_signal = LVL3;
|
|
break;
|
|
|
|
case '4':
|
|
init_signal = LVL4;
|
|
break;
|
|
|
|
case '5':
|
|
init_signal = LVL5;
|
|
break;
|
|
|
|
case '6':
|
|
init_signal = LVL6;
|
|
break;
|
|
|
|
case 'S':
|
|
case 's':
|
|
#ifdef DEBUG
|
|
#endif
|
|
|
|
/*
|
|
* Initialize all global variables needed for init s.
|
|
*
|
|
* Initialize entries in devtab.
|
|
* Verify existence of the DSFs listed in the devtab.
|
|
*/
|
|
initialize_devtab();
|
|
verify_devtab();
|
|
|
|
/*
|
|
* Get the current console device, based on the devices that
|
|
* have been verified in devtab.
|
|
*/
|
|
if ((fd = open(SYSCON, O_RDWR|O_NONBLOCK|O_NOCTTY))
|
|
== -1){
|
|
if (devtab[DTTAG_CONSOLE].dt_status ==
|
|
dtsVERIFIED){
|
|
consdevdsf =
|
|
devtab[DTTAG_CONSOLE].dt_dsfname;
|
|
}else{
|
|
/*
|
|
* if SYSTTY does not exist, consdevdsf
|
|
* is set to SYSTTY as a place holder.
|
|
*/
|
|
consdevdsf =
|
|
devtab[DTTAG_SYSTTY].dt_dsfname;
|
|
}
|
|
}else{
|
|
consdevdsf = SYSCON;
|
|
(void)close(fd);
|
|
}
|
|
|
|
|
|
/*
|
|
* Make sure this process is talking to a legal tty line
|
|
* and that consdevdsf is associated with this line.
|
|
*/
|
|
userttydsf = ttyname(0);
|
|
if (userttydsf == NULL) {
|
|
fprintf(stderr, "Standard input not a tty line\n");
|
|
exit(1);
|
|
}
|
|
|
|
if (stat(consdevdsf, &consdevdsfbuf) == -1){
|
|
statret = -1;
|
|
}
|
|
|
|
if ((statret != -1) &&
|
|
validsyscon(userttydsf) &&
|
|
(stat(userttydsf, &userttydsfbuf)) != -1 &&
|
|
(userttydsfbuf.st_rdev != consdevdsfbuf.st_rdev)){
|
|
/*
|
|
* Unlink /dev/syscon and relink it to the
|
|
* current line.
|
|
*/
|
|
if (unlink(SYSCON) == FAILURE) {
|
|
perror("Can't unlink /dev/syscon");
|
|
exit(1);
|
|
}
|
|
if (link(userttydsf, SYSCON) == FAILURE &&
|
|
/* probably hwgraph link attempt so try a symlink instead */
|
|
(errno != EXDEV || symlink(userttydsf, SYSCON) == FAILURE)) {
|
|
saverr = errno;
|
|
fprintf(stderr,
|
|
"Can't link /dev/syscon to %s",
|
|
userttydsf);
|
|
errno = saverr;
|
|
perror(" ");
|
|
link(SYSTTY,SYSCON); /* Try to leave a syscon */
|
|
exit(1);
|
|
}
|
|
|
|
/*
|
|
* Try to leave a message on system console
|
|
* saying where /dev/syscon is currently
|
|
* connected.
|
|
*/
|
|
if ((fp = fopen(SYSTTY, "r+")) != NULL) {
|
|
fprintf(fp,
|
|
"\n**** SYSCON CHANGED TO %s ****\n",
|
|
userttydsf);
|
|
fclose(fp);
|
|
}
|
|
}
|
|
init_signal = SINGLE_USER;
|
|
break;
|
|
|
|
case 'a':
|
|
init_signal = LVLa;
|
|
break;
|
|
|
|
case 'b':
|
|
init_signal = LVLb;
|
|
break;
|
|
|
|
case 'c':
|
|
init_signal = LVLc;
|
|
break;
|
|
|
|
default:
|
|
fprintf(stderr, "Usage: init [0123456SsQqabc]\n");
|
|
exit(1);
|
|
}
|
|
|
|
/*
|
|
* Now send signal to main init and then exit.
|
|
*/
|
|
ocap = cap_acquire(2, kill_caps);
|
|
if (kill(SPECIALPID, init_signal) == FAILURE) {
|
|
cap_surrender(ocap);
|
|
fprintf(stderr, "Must be privileged user\n");
|
|
#ifdef DEBUG
|
|
debug("%d:userinit():RETURN\tkill FAILED\n",getpid());
|
|
#endif
|
|
exit(1);
|
|
} else {
|
|
cap_surrender(ocap);
|
|
#ifdef DEBUG
|
|
debug("%d:userinit():RETURN\tkill SUCCEEDED\n",getpid());
|
|
#endif
|
|
exit(0);
|
|
}
|
|
}
|
|
|
|
|
|
/********************/
|
|
/**** fdup ****/
|
|
/********************/
|
|
|
|
FILE *
|
|
fdup(FILE *fp)
|
|
{
|
|
register int newfd;
|
|
register char *mode;
|
|
|
|
if(!fp)
|
|
return NULL;
|
|
|
|
/*
|
|
* Dup the file descriptor for the specified stream and then convert
|
|
* it to a stream pointer with the modes of the original stream pointer.
|
|
*/
|
|
if ((newfd = dup(fileno(fp))) != FAILURE) {
|
|
|
|
/*
|
|
* Determine the proper mode. If the old file was _IORW, then
|
|
* use the "r+" option, if _IOREAD, the "r" option, or if _IOWRT
|
|
* the "w" option. Note that since none of these force an lseek
|
|
* by fdopen(), the duped file pointer will be at the same spot
|
|
* as the original.
|
|
*/
|
|
if (fp->_flag & _IORW) {
|
|
mode = "r+";
|
|
} else if (fp->_flag & _IOREAD) {
|
|
mode = "r";
|
|
} else if (fp->_flag & _IOWRT) {
|
|
mode = "w";
|
|
} else {
|
|
/*
|
|
* Something is wrong.
|
|
*/
|
|
close(newfd);
|
|
return(NULL);
|
|
}
|
|
|
|
/*
|
|
* Now have fdopen() finish the job of establishing
|
|
* a new file pointer.
|
|
*/
|
|
return(fdopen(newfd, mode));
|
|
|
|
} else {
|
|
return(NULL);
|
|
}
|
|
}
|
|
|
|
|
|
#ifdef UDEBUG
|
|
|
|
/*************************/
|
|
/**** drop_core ****/
|
|
/*************************/
|
|
|
|
void
|
|
drop_core(char *reason)
|
|
{
|
|
FILE *fp;
|
|
|
|
if (efork(M_OFF, NULLPROC, 0) != NULLPROC)
|
|
return;
|
|
|
|
/*
|
|
* Tell user where core is going to be.
|
|
*/
|
|
if ((fp = fopen(CORE_RECORD, "a+")) == NULL) {
|
|
console("Couldn't open \"%s\".\n", CORE_RECORD);
|
|
} else {
|
|
fprintf(fp, "core.%05d: \"%s\"\n", getpid(), reason);
|
|
fclose(fp);
|
|
}
|
|
sigset(SIGIOT, SIG_DFL);
|
|
abort();
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
#ifdef DEBUGGER
|
|
|
|
/*********************/
|
|
/**** debug ****/
|
|
/*********************/
|
|
|
|
void
|
|
debug(char *format, ...)
|
|
{
|
|
static FILE *fp;
|
|
register int errnum;
|
|
va_list args;
|
|
|
|
#ifdef UDEBUG /* only open debug file once for cleaner debugging
|
|
and tracing with par or strace */
|
|
|
|
if(!fp)
|
|
#endif
|
|
if ((fp = fopen(DBG_FILE, "a+")) == NULL) {
|
|
errnum = errno;
|
|
console("Can't open \"%s\". errno: %d\n", DBG_FILE, errnum);
|
|
return;
|
|
}
|
|
va_start(args, format);
|
|
vfprintf(fp, format, args);
|
|
va_end(args);
|
|
#ifndef UDEBUG
|
|
fclose(fp);
|
|
#endif
|
|
}
|
|
|
|
|
|
/*****************/
|
|
/**** C ****/
|
|
/*****************/
|
|
|
|
char *
|
|
C(char *id)
|
|
{
|
|
static char answer[12];
|
|
register char *ptr;
|
|
register int i;
|
|
|
|
for (i = 4, ptr = &answer[0]; --i >= 0; id++) {
|
|
if (isprint(*id) == 0 ) {
|
|
*ptr++ = '^';
|
|
*ptr++ = *id + 0100;
|
|
} else {
|
|
*ptr++ = *id;
|
|
}
|
|
}
|
|
*ptr++ = '\0';
|
|
return(&answer[0]);
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
/*
|
|
* Procedure: sig_poll
|
|
*
|
|
* Restrictions:
|
|
read(2): None
|
|
*/
|
|
|
|
#define DELTA 25 /* Number of pidlist elements to allocate at a time */
|
|
|
|
/* ARGSUSED */
|
|
void
|
|
sig_poll(int n)
|
|
{
|
|
struct pidrec prec;
|
|
register struct pidrec *p = ≺
|
|
register struct pidlist *plp;
|
|
register struct pidlist *tp, *savetp;
|
|
register int i;
|
|
|
|
if (Pfd < 0) {
|
|
return;
|
|
}
|
|
for (;;) {
|
|
/*
|
|
* Important Note: Either read will really fail (in which case
|
|
* return is all we can do) or will get EAGAIN (Pfd was opened
|
|
* O_NDELAY), in which case we also want to return.
|
|
* Always return from here!
|
|
*/
|
|
if (read(Pfd,p,sizeof(struct pidrec)) != sizeof(struct pidrec))
|
|
return;
|
|
switch (p->pd_type) {
|
|
|
|
case ADDPID:
|
|
/*
|
|
* New "godchild", add to list.
|
|
*/
|
|
if (Plfree == NULL) {
|
|
plp = (struct pidlist *) calloc(DELTA,
|
|
sizeof(struct pidlist));
|
|
if (plp == NULL) {
|
|
/* Can't save pid */
|
|
break;
|
|
}
|
|
/*
|
|
* Point at 2nd record allocated, we'll use plp.
|
|
*/
|
|
tp = plp + 1;
|
|
/*
|
|
* Link them into a chain.
|
|
*/
|
|
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;
|
|
/*
|
|
* Note - pid list is kept in increasing order of pids.
|
|
*/
|
|
if (Plhead == NULL) {
|
|
Plhead = plp;
|
|
/* Back up to read next record */
|
|
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 {
|
|
/* Already in list! */
|
|
plp->pl_next = Plfree;
|
|
Plfree = plp;
|
|
break;
|
|
}
|
|
}
|
|
if (tp == NULL) {
|
|
/* Add to end of list */
|
|
savetp->pl_next = plp;
|
|
}
|
|
}
|
|
/* Back up to read next record. */
|
|
break;
|
|
|
|
case REMPID:
|
|
/*
|
|
* This one was handled by someone else,
|
|
* purge it from the list.
|
|
*/
|
|
if (Plhead == NULL) {
|
|
/* Back up to read next record. */
|
|
break;
|
|
}
|
|
savetp = tp = Plhead;
|
|
while (tp) {
|
|
if (p->pd_pid > tp->pl_pid) {
|
|
/* Keep on looking. */
|
|
savetp = tp;
|
|
tp = tp->pl_next;
|
|
continue;
|
|
}
|
|
else if (p->pd_pid < tp->pl_pid) {
|
|
/* Not in list. */
|
|
break;
|
|
}
|
|
else {
|
|
/* Found it. */
|
|
if (tp == Plhead)
|
|
Plhead = tp->pl_next;
|
|
else
|
|
savetp->pl_next = tp->pl_next;
|
|
tp->pl_next = Plfree;
|
|
Plfree = tp;
|
|
break;
|
|
}
|
|
}
|
|
/* Back up to read next record. */
|
|
break;
|
|
default:
|
|
console("Bad message on initpipe\n");
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
/************************/
|
|
/******* cleanaux *****/
|
|
/************************/
|
|
void
|
|
cleanaux(void)
|
|
{
|
|
register struct pidlist *savep, *p;
|
|
pid_t pid;
|
|
short status;
|
|
|
|
(void) sighold(SIGPOLL);
|
|
savep = p = Plhead;
|
|
while (p) {
|
|
if (p->pl_dflag) {
|
|
/*
|
|
* Found an entry to delete,
|
|
* remove it from list first.
|
|
*/
|
|
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);
|
|
}
|
|
|
|
|
|
/*
|
|
* Procedure: clearent
|
|
*
|
|
* Restrictions:
|
|
getutxent: None
|
|
pututxline: None
|
|
updwtmpx: None
|
|
*/
|
|
|
|
void
|
|
clearent(pid_t pid, short status)
|
|
{
|
|
register struct utmpx *up;
|
|
|
|
setutxent();
|
|
while (up = getutxent()) {
|
|
if (up->ut_pid == pid) {
|
|
if (up->ut_type == DEAD_PROCESS) {
|
|
/*
|
|
* Cleaned up elsewhere.
|
|
*/
|
|
endutxent();
|
|
return;
|
|
}
|
|
up->ut_type = DEAD_PROCESS;
|
|
up->ut_exit.e_termination = status & 0xff;
|
|
up->ut_exit.e_exit = (status >> 8) & 0xff;
|
|
time(&up->ut_tv.tv_sec);
|
|
pututxline(up);
|
|
|
|
/*
|
|
* Now attempt to add to the end of the wtmp and wtmpx
|
|
* files. Do not create if they don't already exist.
|
|
*/
|
|
updwtmpx(WTMPX, up);
|
|
endutxent();
|
|
return;
|
|
}
|
|
}
|
|
endutxent();
|
|
}
|
|
|
|
|
|
/*
|
|
* Procedure: v_pgm
|
|
*
|
|
* Restrictions:
|
|
stat(2): none
|
|
*/
|
|
int
|
|
v_pgm(char *pgmp)
|
|
{
|
|
struct stat statpgm;
|
|
|
|
/* absolute pathnames required */
|
|
if (*pgmp != '/')
|
|
return(1);
|
|
|
|
/*Program must exist*/
|
|
/*If pathname is greater than 1024 characters, stat will return*/
|
|
/*ENAMETOOLONG. */
|
|
if (stat(pgmp, &statpgm))
|
|
return(1);
|
|
else
|
|
{
|
|
/* pgm must be executable by owner */
|
|
if ((statpgm.st_mode & S_IFMT) == S_IFREG) {
|
|
if (statpgm.st_mode & S_IXUSR)
|
|
return(0);
|
|
else
|
|
return(1);
|
|
}else
|
|
return(1);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* initialize_devtab()
|
|
*
|
|
* Perform any initialization needed for entries in devtab.
|
|
*
|
|
* If TP is not configured into system, set dt_major, dt_minor,
|
|
* and dt_linkdsfname for CONSOLE, to REAL_CONSDEVMAJNUM,
|
|
* REAL_CONSDEVMINNUM, and SYSTTY respectively. Set dt_status for
|
|
* TP_CONSDEV_DSF, SYSCONREAL, TP_ADMDEV_DSF, TP_CTRLCLONE_DSF,
|
|
* TP_DATACLONE_DSF, and SAD_ADMDEV_DSF to dtsIGNORE.
|
|
*
|
|
*/
|
|
void
|
|
initialize_devtab(void)
|
|
{
|
|
#ifdef DEBUG
|
|
debug("%d:initialize_devtab():ENTER\n",getpid());
|
|
#endif
|
|
devtab[DTTAG_SYSTTY].dt_dsfname = SYSTTY;
|
|
devtab[DTTAG_CONSOLE].dt_dsfname = CONSOLE;
|
|
devtab[DTTAG_SYSCON].dt_dsfname = SYSCON;
|
|
devtab[DTTAG_SYSCONREAL].dt_dsfname = SYSCONREAL;
|
|
devtab[DTTAG_SYSCONREAL].dt_linkdsfname = SYSTTY;
|
|
|
|
devtab[DTTAG_CONSOLE].dt_major = REAL_CONSDEVMAJNUM;
|
|
|
|
devtab[DTTAG_CONSOLE].dt_minor = REAL_CONSDEVMINNUM;
|
|
|
|
devtab[DTTAG_CONSOLE].dt_linkdsfname = SYSTTY;
|
|
devtab[DTTAG_SYSCON].dt_linkdsfname = SYSTTY;
|
|
|
|
devtab[DTTAG_SYSCONREAL].dt_status = dtsIGNORE;
|
|
#ifdef DEBUG
|
|
debug("%d:initial_devtab():RETURN\n",getpid());
|
|
#endif
|
|
}
|
|
|
|
/*
|
|
* Procedure: verify_devtab
|
|
*
|
|
* Restrictions:
|
|
stat(2): None
|
|
*/
|
|
/*
|
|
* verify_devtab()
|
|
*
|
|
* Verify the existence, and major and minor device numbers of all devices
|
|
* in the device table devtab.
|
|
*
|
|
* If a device does not exist, set it status to dtsENOENT and action to
|
|
* dtsMKNOD, if its dt_linkdsfname field is NULL, dtsLINK, if its
|
|
* dt_linkdsfname field in not NULL. The DSF will be created or linked
|
|
* after the root file system has been checked. (This will have occured
|
|
* after all "sysinit" entries in /etc/inittab have run.)
|
|
*
|
|
* If the device's major and or minor device number stated in the devtab does
|
|
* not match its cooresponding DSF entry in the file system, set it status to
|
|
* dtsWRONGDEV. The action is set to dtsMKNOD or dtsLINK as described above.
|
|
*/
|
|
void
|
|
verify_devtab(void)
|
|
{
|
|
|
|
struct devicetable *dtp;
|
|
struct stat statbuf;
|
|
#ifdef DEBUG
|
|
debug("%d:verify_devtab():ENTER\n",getpid());
|
|
#endif
|
|
|
|
|
|
for (dtp = devtab; dtp->dt_tag != -1; dtp++){
|
|
if (dtp->dt_status == dtsIGNORE){
|
|
continue;
|
|
}else{
|
|
dtp->dt_status = dtsUNVERIFIED;
|
|
}
|
|
}
|
|
for (dtp = devtab; dtp->dt_tag != -1; dtp++){
|
|
if ((dtp->dt_status == dtsVERIFIED) ||
|
|
(dtp->dt_status == dtsIGNORE)){
|
|
continue;
|
|
}
|
|
if (stat(dtp->dt_dsfname, &statbuf) == -1){
|
|
dtp->dt_status = dtsENOENT;
|
|
|
|
}
|
|
else if (dtp->dt_major != (major_t)(NODEV)){
|
|
if ((dtp->dt_major != major(statbuf.st_rdev)) ||
|
|
(dtp->dt_minor != minor(statbuf.st_rdev))){
|
|
dtp->dt_status = dtsWRONGDEV;
|
|
}else{
|
|
dtp->dt_status = dtsVERIFIED;
|
|
}
|
|
}else{
|
|
dtp->dt_major = major(statbuf.st_rdev);
|
|
dtp->dt_minor = minor(statbuf.st_rdev);
|
|
dtp->dt_status = dtsVERIFIED;
|
|
}
|
|
if ((dtp->dt_status == dtsENOENT) ||
|
|
(dtp->dt_status == dtsWRONGDEV)){
|
|
/*
|
|
* Special Case Handling:
|
|
*/
|
|
switch (dtp->dt_tag){
|
|
/*
|
|
* If entry is missing, have SYSCONREAL_DSF Major
|
|
* and Minor device number set to default system
|
|
* console major and minor device number.
|
|
* If entry indicates the wrong device, assume its
|
|
* correct and that it was changed due to being
|
|
* switched from 'init s|S' being called from a device
|
|
* other then the real/physical console device.
|
|
*/
|
|
case DTTAG_SYSCONREAL:
|
|
if (dtp->dt_status == dtsENOENT){
|
|
dtp->dt_major = REAL_CONSDEVMAJNUM;
|
|
dtp->dt_minor = REAL_CONSDEVMINNUM;
|
|
dtp->dt_linkdsfname = SYSTTY;
|
|
}else{
|
|
dtp->dt_major = major(statbuf.st_rdev);
|
|
dtp->dt_minor = minor(statbuf.st_rdev);
|
|
dtp->dt_status = dtsVERIFIED;
|
|
}
|
|
break;
|
|
/*
|
|
* If TP is not configured, set dt_major, dt_minor, and
|
|
* dt_linkdsfname to REAL_CONSDEVMAJNUM,
|
|
* REAL_CONSDEVMINNUM, and SYSTTY respectively.
|
|
*/
|
|
case DTTAG_SYSCON:
|
|
if (dtp->dt_status == dtsENOENT){
|
|
dtp->dt_major =
|
|
REAL_CONSDEVMAJNUM;
|
|
dtp->dt_minor =
|
|
REAL_CONSDEVMINNUM;
|
|
dtp->dt_linkdsfname = SYSTTY;
|
|
}else{
|
|
dtp->dt_major =
|
|
major(statbuf.st_rdev);
|
|
dtp->dt_minor =
|
|
minor(statbuf.st_rdev);
|
|
dtp->dt_status = dtsVERIFIED;
|
|
}
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
if (dtp->dt_status != dtsVERIFIED){
|
|
if (dtp->dt_linkdsfname == (char *)NULL){
|
|
dtp->dt_action = dtaMKNOD;
|
|
}else{
|
|
dtp->dt_action = dtaLINK;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
/*
|
|
* Special Case Handling:
|
|
*
|
|
* CASE: SYSTTY does not exist or has the wrong major/minor
|
|
* device number.
|
|
* If TP is not configured, set CONSOLE's devtab entry's action field
|
|
* to dtaLINK. If SYSCONS's major and minor device number is the
|
|
* same as SYSTTY, set SYSCONS's devtab entry's action field to dtaLINK.
|
|
* The dt_status field is not changed since if the devices have
|
|
* been verified, they may be used before they are relinked to SYSTTY.
|
|
*
|
|
*/
|
|
if (devtab[DTTAG_SYSTTY].dt_status == dtsUNVERIFIED){
|
|
devtab[DTTAG_CONSOLE].dt_action = dtaLINK;
|
|
if ((devtab[DTTAG_SYSCON].dt_major ==
|
|
devtab[DTTAG_SYSTTY].dt_major) &&
|
|
(devtab[DTTAG_SYSCON].dt_minor ==
|
|
devtab[DTTAG_SYSTTY].dt_minor)){
|
|
devtab[DTTAG_SYSCON].dt_action = dtaLINK;
|
|
}
|
|
}
|
|
#ifdef DEBUG
|
|
{
|
|
char *status;
|
|
for (dtp = devtab; dtp->dt_tag != -1; dtp++){
|
|
switch (dtp->dt_status){
|
|
case dtsUNVERIFIED:
|
|
status = "UNVERIFIED";
|
|
break;
|
|
case dtsVERIFIED:
|
|
status = "VERIFIED";
|
|
break;
|
|
case dtsIGNORE:
|
|
status = "IGNORE";
|
|
break;
|
|
case dtsENOENT:
|
|
status = "ENOENT";
|
|
break;
|
|
case dtsWRONGDEV:
|
|
status = "WRONGDEV";
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
debug("verify_devtab():DSF %s\tSTATUS = %s\n",dtp->dt_dsfname, status);
|
|
}
|
|
}
|
|
debug("%d:verify_devtab():RETURN\n",getpid());
|
|
#endif
|
|
}
|
|
|
|
/*
|
|
* Procedure: setup_consdev
|
|
*
|
|
* Restrictions:
|
|
open(2): None
|
|
|
|
|
|
* Setup consdevdsf, which is the dsf init uses internally to write
|
|
* console messages.
|
|
* Setup device to display console messages output.
|
|
*
|
|
****************************************************************************
|
|
* If TP is not configured on the system, set consdevdsf to SYSCON if it
|
|
* exists and can be opened. If SYSCON does not exist or can not be opened
|
|
* set consdevdsf to the first device that exists and has the correct
|
|
* major/minor device number in the following order;
|
|
* CONSOLE, and SYSTTY.
|
|
****************************************************************************
|
|
* SIDE EFFECTS:
|
|
*
|
|
* Global consdevdsf gets set to DSF to send console messages.
|
|
* Global substitutedsf gets set to consdevdsf if consdevdsf !=
|
|
* CONSOLE, otherwise it gets set to NULL.
|
|
*/
|
|
int
|
|
setup_consdev(char *errbuf)
|
|
{
|
|
int fd;
|
|
int ret = SUCCESS;
|
|
#ifdef DEBUG
|
|
debug("%d:setup_consdev():ENTER\n",getpid());
|
|
#endif
|
|
|
|
|
|
if (errbuf != (char *)NULL){
|
|
*errbuf = '\0';
|
|
}
|
|
if ((fd = open(SYSCON, O_RDWR|O_NONBLOCK|O_NOCTTY)) == -1){
|
|
if (devtab[DTTAG_CONSOLE].dt_status == dtsVERIFIED){
|
|
consdevdsf = devtab[DTTAG_CONSOLE].dt_dsfname;
|
|
}else if (devtab[DTTAG_SYSTTY].dt_status == dtsVERIFIED){
|
|
consdevdsf = devtab[DTTAG_SYSTTY].dt_dsfname;
|
|
}else{
|
|
ret = FAILURE;
|
|
}
|
|
devtab[DTTAG_SYSCON].dt_major = REAL_CONSDEVMAJNUM;
|
|
devtab[DTTAG_SYSCON].dt_minor = REAL_CONSDEVMINNUM;
|
|
devtab[DTTAG_SYSCON].dt_status = dtsWRONGDEV;
|
|
devtab[DTTAG_SYSCON].dt_action = dtaLINK;
|
|
devtab[DTTAG_SYSCON].dt_linkdsfname = SYSTTY;
|
|
}else{
|
|
consdevdsf = SYSCON;
|
|
(void)close(fd);
|
|
}
|
|
if (strcmp(CONSOLE, consdevdsf) != 0){
|
|
substitutedsf = consdevdsf;
|
|
}else{
|
|
substitutedsf = (char *)NULL;
|
|
}
|
|
#ifdef DEBUG
|
|
debug("%d:setup_consdev():RETURN %s\t consdevdsf = %s\n",
|
|
getpid(), ((ret == SUCCESS)? "TRUE":"FALSE"), consdevdsf);
|
|
#endif
|
|
return (ret);
|
|
}
|
|
|
|
|
|
/* no reason to do fork here; console() does it already; no reason to
|
|
* ever have done it, since we will just reboot anyway */
|
|
void
|
|
firmware(void)
|
|
{
|
|
cap_t ocap;
|
|
cap_value_t cap_shutdown = CAP_SHUTDOWN;
|
|
/* console will probably fail to print, or we wouldn't be here ... */
|
|
console("Problems opening console, rebooting; use miniroot\n\n");
|
|
ocap = cap_acquire(1, &cap_shutdown);
|
|
(void)uadmin(A_SHUTDOWN, AD_IBOOT, 0);
|
|
cap_surrender(ocap);
|
|
}
|
|
|
|
|
|
/*
|
|
* substitutedev()
|
|
*
|
|
* For the given command line from /etc/inittab, substitute all Device
|
|
* Special File (ie. /dev/console entries) with the DSF specified in subdev.
|
|
*
|
|
* NOTE: There is an assumption that /etc/inittab entries always use
|
|
* /dev/console when re-directing input and/or output from the console device.
|
|
*/
|
|
int
|
|
substitutedev(struct CMD_LINE *cmd, char *line, char *subdev)
|
|
{
|
|
|
|
char *cmdlinesegp; /* pointer to beginning of a segment of the
|
|
* command line to be searched for a DSF
|
|
* entry.
|
|
*/
|
|
char *dsfp; /* pointer to beginning of a DSF located in
|
|
* the segment of the command line.
|
|
*/
|
|
int linelen = 0; /* length of new command line being created */
|
|
int seglen; /* length of segment current segment of command
|
|
* line.
|
|
*/
|
|
|
|
|
|
*line = '\0';
|
|
cmdlinesegp = cmd->c_command;
|
|
|
|
|
|
while ((dsfp = strstr(cmdlinesegp, "/dev/console")) != (char *)NULL){
|
|
/*
|
|
* Copy over segment from cmdlinesegp to dsfp to new command
|
|
* line and copy the substitute DSF to new command line.
|
|
*/
|
|
seglen = dsfp - cmdlinesegp;
|
|
linelen += seglen;
|
|
if (linelen < MAXCMDL){
|
|
strncat(line, cmdlinesegp, seglen);
|
|
#ifdef DEBUG
|
|
debug("substitutedev():After strncat of cmdlinesegp line = \n%s\n",line);
|
|
#endif
|
|
}else{
|
|
return (FAILURE);
|
|
}
|
|
seglen = strlen(subdev);
|
|
linelen += seglen;
|
|
if (linelen < MAXCMDL){
|
|
strncat(line, subdev, seglen);
|
|
#ifdef DEBUG
|
|
printf("substitutedev():After strncat of subdev line = \n%s\n",line);
|
|
#endif
|
|
}else{
|
|
return (FAILURE);
|
|
}
|
|
cmdlinesegp = dsfp + strlen("/dev/console");
|
|
} /* end while */
|
|
|
|
/*
|
|
* Copy rest of commnad line to new command line if any DSFs were
|
|
* substituted.
|
|
*/
|
|
|
|
if (cmdlinesegp == cmd->c_command){
|
|
return (SUCCESS);
|
|
}else{
|
|
seglen = strlen(cmdlinesegp);
|
|
linelen += seglen;
|
|
if (linelen < MAXCMDL){
|
|
strncat(line, cmdlinesegp, seglen);
|
|
}else{
|
|
return (FAILURE);
|
|
}
|
|
cmd->c_command = line;
|
|
}
|
|
return (SUCCESS);
|
|
}
|
|
|
|
/*
|
|
* Procedure: update_devtab
|
|
*
|
|
* Restrictions:
|
|
unlink(2): None
|
|
mknod(2): None
|
|
lvlin: None
|
|
lvlfile(2): None
|
|
link(2): None
|
|
*/
|
|
/*
|
|
* update_devtab()
|
|
*
|
|
*
|
|
* Go through devtab and make all devices that have the action dtaMKNOD.
|
|
* Go through devtab and make all devices that have the action dtaLINK.
|
|
* NOTE: Function should not be called the root file system has been verified.
|
|
*/
|
|
int
|
|
update_devtab(void)
|
|
{
|
|
struct devicetable *dtp;
|
|
int ret = SUCCESS;
|
|
#ifdef DEBUG
|
|
debug("%d:update_devtab():ENTER\n",getpid());
|
|
#endif
|
|
|
|
if (rootfs_verified != TRUE){
|
|
return (FAILURE);
|
|
}
|
|
|
|
/*
|
|
* get the level to set the devices to, if any are created.
|
|
*/
|
|
for (dtp = &devtab[0]; dtp->dt_tag != (short)-1; dtp++){
|
|
if (dtp->dt_action == dtaMKNOD){
|
|
mode_t old_umask;
|
|
#ifdef DEBUG
|
|
debug("update_devtab:making DSF %s:major %d:minor %d:reason - %s\n",
|
|
dtp->dt_dsfname, dtp->dt_major,
|
|
dtp->dt_minor, (dtp->dt_status == dtsWRONGDEV?
|
|
"incorrect major/minor number":"entry does not exit"));
|
|
#endif
|
|
(void)unlink(dtp->dt_dsfname);
|
|
/* pick sane permissions: 622, not 720 */
|
|
old_umask = umask(0);
|
|
#ifdef DEBUG
|
|
debug("umask was: %o\n", old_umask);
|
|
#endif
|
|
if (mknod(dtp->dt_dsfname, S_IFCHR|
|
|
S_IRUSR|S_IWUSR|S_IWGRP|S_IWOTH,
|
|
makedev(dtp->dt_major, dtp->dt_minor)) == -1){
|
|
#ifdef DEBUG
|
|
debug("update_devtab:mknod %s failed errno %d:%s\n",
|
|
dtp->dt_dsfname, errno, strerror(errno));
|
|
#endif
|
|
ret = FAILURE;
|
|
}else{
|
|
dtp->dt_status = dtsVERIFIED;
|
|
dtp->dt_action = dtaNOACTION;
|
|
}
|
|
umask(old_umask);
|
|
}
|
|
}
|
|
|
|
for (dtp = devtab; dtp->dt_tag != (short)-1; dtp++){
|
|
if (dtp->dt_action == dtaLINK){
|
|
#ifdef DEBUG
|
|
debug("update_devtab:linking DSF %s to %s:major %d:minor %d:reason - %s\n",
|
|
dtp->dt_linkdsfname, dtp->dt_dsfname, dtp->dt_major,
|
|
dtp->dt_minor, (dtp->dt_status == dtsWRONGDEV?
|
|
"incorrect major/minor number":"entry does not exit"));
|
|
#endif
|
|
(void)unlink(dtp->dt_dsfname);
|
|
if (link(dtp->dt_linkdsfname, dtp->dt_dsfname)){
|
|
#ifdef DEBUG
|
|
debug("update_devtab:link %s to %s failed errno %d:%s\n",
|
|
dtp->dt_linkdsfname, dtp->dt_dsfname, errno,
|
|
strerror(errno));
|
|
#endif
|
|
ret = FAILURE;
|
|
}else{
|
|
dtp->dt_status = dtsVERIFIED;
|
|
dtp->dt_action = dtaNOACTION;
|
|
}
|
|
}
|
|
}
|
|
#ifdef DEBUG
|
|
debug("%d:update_devtab():RETURN\t%s\n",getpid(), ((ret == SUCCESS)?"EVERY DEVICE UPDATED SUCCESSFULLY":"ONE OR MORE DEVICE(S) FAILED TO BE UPDATED"));
|
|
#endif
|
|
return (ret);
|
|
}
|
|
|
|
|
|
/*
|
|
* validsyscon(ttyname) -- check ttyname for validity as a new syscon
|
|
*
|
|
* If ttyname is on a list of acceptable names, return (1) else (0).
|
|
* These are names to which syscon may be legitimately linked.
|
|
*
|
|
* Specifically excluded are network ttys, so that the console doesn't
|
|
* disappear into a black hole when the network goes away while shutting
|
|
* down into single-user mode.
|
|
*
|
|
* In the patterns, "?" is recognized, as is "*" (which is assumed to be
|
|
* the last character in the pattern).
|
|
*/
|
|
|
|
char *validtty[] = { /* acceptable syscon name patterns */
|
|
"/dev/console",
|
|
"/dev/systty",
|
|
"/dev/ttyd*",
|
|
};
|
|
|
|
static int
|
|
validsyscon(char *ttyname)
|
|
{
|
|
int i;
|
|
char *v, *t;
|
|
|
|
for (i = 0; i < sizeof (validtty) / sizeof (*validtty); ++i) {
|
|
for (t = ttyname, v = validtty[i]; *v != '\0'; ++t, ++v) {
|
|
if (*v == '*') return(1);
|
|
if (*t == '\0') break;
|
|
if (*v == '?') continue;
|
|
if (*t != *v) break;
|
|
}
|
|
if (*v == '\0' && *t == '\0') return(1);
|
|
}
|
|
return(0);
|
|
}
|
|
|
|
|
|
static int
|
|
rd_ioctlsyscon(FILE *fp)
|
|
{
|
|
unsigned int iflags, oflags, cflags, lflags, ldisc, cc[NCCS];
|
|
speed_t ospeed, ispeed;
|
|
int i;
|
|
|
|
#if defined(_STYPES_LATER)
|
|
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:%x:%x:%x:%x:%x:%x:%x:%x:%x:%x:%x:%x:%x:%x:%x:%x:%x:%x:%x",
|
|
&iflags,&oflags,&cflags,&lflags,&ospeed,&ispeed,
|
|
&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],&cc[18],&cc[19],
|
|
&cc[20],&cc[21],&cc[22],&cc[23],
|
|
&cc[24],&cc[25],&cc[26],&cc[27],
|
|
&cc[28],&cc[29],&cc[30],&ldisc);
|
|
#if 31 != NCCS
|
|
error error NCCS != 31
|
|
#endif
|
|
#else /* notdef _STYPES_LATER */
|
|
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:%x:%x:%x:%x:%x:%x:%x:%x",
|
|
&iflags,&oflags,&cflags,&lflags,&ospeed,&ispeed,
|
|
&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],&cc[18],&cc[19],
|
|
&cc[20],&cc[21],&cc[22],&ldisc);
|
|
#if 23 != NCCS
|
|
error error NCCS != 23
|
|
#endif
|
|
#endif /* notdef _STYPES_LATER */
|
|
|
|
if (i != 7+NCCS)
|
|
return(0);
|
|
|
|
/*
|
|
* If the file is formatted properly, use the values to
|
|
* initialize the console terminal condition.
|
|
*/
|
|
termio.c_iflag = iflags;
|
|
termio.c_oflag = oflags;
|
|
termio.c_cflag = cflags;
|
|
termio.c_lflag = lflags;
|
|
termio.c_ospeed = ospeed;
|
|
termio.c_ispeed = ispeed;
|
|
termio.c_line = ldisc;
|
|
for (i=0; i<NCCS; i++)
|
|
termio.c_cc[i] = cc[i];
|
|
|
|
return 1;
|
|
}
|
|
|
|
static void
|
|
wr_ioctlsyscon(FILE *fp)
|
|
{
|
|
int cci;
|
|
|
|
fprintf(fp,"%x:%x:%x:%x:%x:%x", termio.c_iflag,
|
|
termio.c_oflag,termio.c_cflag,termio.c_lflag,
|
|
termio.c_ospeed, termio.c_ispeed);
|
|
for (cci = 0; cci < NCCS; cci++)
|
|
fprintf(fp, ":%x", termio.c_cc[cci]);
|
|
fprintf(fp, ":%x", termio.c_line);
|
|
fputc('\n',fp);
|
|
}
|