* Copyright (c) 1993 Christoph M. Robitschko
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. All advertising materials mentioning features or use of this software
* must display the following acknowledgement:
* This product includes software developed by Christoph M. Robitschko
* 4. The name of the author may not be used to endorse or promote products
* derived from this software withough specific prior written permission
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
* Also contains definitions for global variables etc.
/* global variables, preset to their defaults */
int timeout_m2s_TERM
= INIT_M2S_TERMTO
;
int timeout_m2s_KILL
= INIT_M2S_KILLTO
;
int retrytime
= RETRYTIME
;
char *config_file
= INIT_CONFIG
;
static ttytab_t
*ttytab
= (ttytab_t
*)0;
static callout_t
*callout_tab
= (callout_t
*)0;
static callout_t
*callout_free
= (callout_t
*)0;
static int callout_nfree
= 0;
jmp_buf boing_singleuser
,
static enum { SINGLEUSER
, MULTIUSER
, SINGLE2MULTI
, MULTI2SINGLE
}
struct ttyent RCent_auto
= {
"/bin/sh sh /etc/rc autoboot",
struct ttyent RCent_fast
= {
struct ttyent Single_ent
= {
static struct ttyent
*RCent
= &RCent_auto
;
static struct ttyent
*Singlesh
= &Single_ent
;
/**********************************************************
**********************************************************/
/* make it a session leader */
openlog("init", LOG_CONS
| LOG_PID
, LOG_DAEMON
);
if(!strcmp(argv
[1], "-s")) /* Singleuser */
force_single
= startup_single
= 1;
else if(!strcmp(argv
[1], "-f")) /* Fastboot */
else if (!strcmp(argv
[1], "-d")) /* Debug level */
if ((force_debug
= str2u(argv
[2])) >= 0) {
syslog(LOG_ERR
, "option -d needs positive integer argument");
syslog(LOG_ERR
, "option -d needs an argument");
else if (!strcmp(argv
[1], "-C")) /* Configuration file */
syslog(LOG_ERR
, "option -C needs an argument");
else if (!strcmp(argv
[1], "-S")) /* Syntaxcheck only */
else if (!strcmp(argv
[1], "-")) /* ignore this */
else if (!strcmp(argv
[1], "--")) /* ... and this */
syslog(LOG_ERR
, "unknown option \"%s\"", argv
[1]);
/* did some idiot try to run init ? */
if((getpid() != 1) && !checkonly
) {
const char errmsg
[] = "init: system daemon, not runnable by user\r\n";
write(2, errmsg
, strlen(errmsg
));
/* read the default configuration (limits etc) */
/* read configuration file */
/* set global configuration parameters */
/* values configured by command-line arguments take precedence */
/* over values in the config file */
startup_single
= force_single
;
* initialize callout table
* initialize the longjmp buffers;
* after a longjmp(), the appropriate function is called and
if (setjmp(boing_singleuser
))
if (setjmp(boing_single2multi
))
if (setjmp(boing_multiuser
))
if (setjmp(boing_multi2single
))
if (setjmp(boing_waitforboot
))
/* install signal handlers for catched signals */
signal(SIGTSTP
, sig_tstp
);
signal(SIGTERM
, sig_term
);
signal(SIGALRM
, sig_alrm
);
signal(SIGUSR1
, sig_usr1
);
signal(SIGUSR2
, sig_usr2
);
signal(SIGTTIN
, sig_ttin
);
#if defined (UNTRUSTED) && !defined (TESTRUN)
/* define Set of signals to be blocked for critical parts */
(void) sigemptyset (&block_set
);
(void) sigaddset (&block_set
, SIGTSTP
);
(void) sigaddset (&block_set
, SIGTERM
);
(void) sigaddset (&block_set
, SIGHUP
);
(void) sigaddset (&block_set
, SIGUSR1
);
(void) sigaddset (&block_set
, SIGUSR2
);
(void) sigaddset (&block_set
, SIGALRM
);
longjmp(boing_singleuser
, 1);
longjmp(boing_single2multi
, 1);
/**********************************************************
**********************************************************/
/* TSTP -- wait for children, but don't spawn new ones */
Debug(3, "TSTP Signal received");
longjmp(boing_waitforboot
, 2);
/* TERM -- Go to singleuser mode */
Debug(3, "Terminate Signal received");
longjmp(boing_multi2single
, 2);
/* HUP -- Reread /etc/ttys file */
Debug(3, "Hangup Signal received");
longjmp(boing_multiuser
, 2);
/* ALRM -- Timeout Signal */
Debug(3, "Alarm Signal received");
/* USR1 -- Increment debugging level */
Debug(0, "I will chat like a gossip");
Debug(0, "I will chat like %d gossips", debug
);
/* USR2 -- switch off debugging */
Debug(0, "OK, I will shut up now.");
#if defined (UNTRUSTED) && !defined (TESTRUN)
/* INT -- execute original init (Signal can be generated from the kernel
debugger with 'call pfind(1)' and then 'call psignal(XXXXX, 2)'
where XXXXX is the return value of the pfind call).
This isn't very pretty, but it saved me from booting from floppy
Debug(0, "Interrupt signal received; trying to execute /sbin/init.ori");
Debug(0, "(Are you not satisfied with me ?)");
execl("/sbin/init.ori", "init", "-s", 0);
Debug(0, "Could not execute /sbin/init.ori (%m)");
longjmp(boing_multi2single
, 1);
/* TTIN -- reread configuration file; only valid when in singleuser mode */
if (State
== SINGLEUSER
) {
Debug(0, "TTIN signal received, re-reading configuration file");
syslog(LOG_NOTICE
, "TTIN signal received, but not in singleuser mode");
/**********************************************************
**********************************************************/
Debug(1, "Entered State singleuser");
syslog(LOG_ERR
, "internal error: multiple users in singleusermode");
longjmp(boing_multi2single
, 1);
ttytab
= ent_to_tab(Singlesh
, (ttytab_t
*)0, ttytab
, INIT_NODEV
| INIT_OPEN
| INIT_ARG0
);
if (do_getty(ttytab
, 0) < 0) {
syslog(LOG_EMERG
, "Unable to start singleuser shell");
_exit(1); /* What else should we do about this ? */
while(wait(&status
) != ttytab
->pid
);
scanf("%d\n", &status
); /* XXX */
Debug(1, "Singleusershell exited with status %d", status
);
ttytab
= free_tty(ttytab
, ttytab
);
longjmp(boing_single2multi
, 1);
/**********************************************************
**********************************************************/
Debug(1, "Entered State single2multi");
syslog(LOG_ERR
, "internal error: users in single2multi");
longjmp(boing_multi2single
, 1);
ttytab
= ent_to_tab(RCent
, (ttytab_t
*)0, ttytab
, INIT_NODEV
| INIT_OPEN
| INIT_ARG0
);
if (do_getty(ttytab
, 0) < 0) {
syslog(LOG_ERR
, "Unable to execute /etc/rc");
ttytab
= free_tty(ttytab
, ttytab
);
longjmp(boing_singleuser
, 2);
while(wait(&status
) != ttytab
->pid
);
Debug(1, "/etc/rc exited with status %d", status
);
ttytab
= free_tty(ttytab
, ttytab
);
longjmp(boing_singleuser
, 1);
logwtmp("~", "reboot", "");
longjmp(boing_multiuser
, 1);
/**********************************************************
**********************************************************/
/* Note that the State variable is not set here */
Debug(1, "Entered State waitforboot");
Debug(4, "Process %d exited with status %d", pid
, status
);
for (tt
=ttytab
; tt
; tt
= tt
->next
)
ttytab
= free_tty(ttytab
, tt
);
/**********************************************************
**********************************************************/
Debug(1, "Entered State multiuser");
/* First, (re)build ttytab based on what is in /etc/ttys */
for (tt
= ttytab
; tt
; tt
= tt
->next
)
tt
->intflags
&= ~(INIT_SEEN
| INIT_CHANGED
| INIT_NEW
);
while ((tent
= getttyent()))
if (tent
->ty_status
& TTY_ON
) {
for (tt
= ttytab
; tt
; tt
= tt
->next
)
if (!strcmp(tent
->ty_name
, tt
->name
))
ttytab
= ent_to_tab(tent
, tt
, ttytab
, 0);
/* Kill the processes whose entries are deleted or changed */
/* Also start the getty process on the lines that were just added */
for (tt
= ttytab
; tt
; tt
= tt
->next
)
if (!(tt
->intflags
& INIT_SEEN
)) {
Debug(5, "killing %s (PID %d): Not seen", tt
->name
, tt
->pid
);
tt
->intflags
|= INIT_DONTSPAWN
;
else if (tt
->intflags
& INIT_NEW
)
else if (tt
->intflags
& INIT_CHANGED
) {
Debug(5, "killing %s (PID %d): Changed", tt
->name
, tt
->pid
);
else if (tt
->intflags
& INIT_FAILSLEEP
) {
Debug(5, "continuing %s (PID %d)", tt
->name
, tt
->pid
);
tt
->intflags
&= ~INIT_FAILED
| INIT_FAILSLEEP
;
/* Now handle terminating children and respawn gettys for lines */
syslog(LOG_ERR
, "wait() found no child processes -- going singleuser.");
longjmp(boing_multi2single
, 2);
syslog(LOG_ERR
, "wait() failed: %m");
Debug(4, "Process %d terminated with status %d", pid
, status
);
for (tt
= ttytab
; tt
; tt
= tt
->next
)
if (tt
->intflags
& INIT_DONTSPAWN
) {
ttytab
= free_tty(ttytab
, tt
);
/**********************************************************
**********************************************************/
static jmp_buf boing_timeout
;
Debug(1, "Entering State multi2single");
/* forget about the gettys */
ttytab
= free_tty(ttytab
, ttytab
);
* round = 1: TERMinate children, then wait for them (default 10 seconds)
* round = 2: KILL children, then wait for them (default 30 seconds)
* round = 3: timeout expired; go to singleuser mode
boing_m2stimeout
= &boing_timeout
;
Debug(3, "TERMinating processes");
callout (timeout_m2s_TERM
, CO_MUL2SIN
, (void *)0);
Debug(3, "KILLing processes");
callout (timeout_m2s_KILL
, CO_MUL2SIN
, (void *)0);
while ((pid
= wait(&status
)) >= 0)
Debug(4, "Process %d exited with status %d", pid
, status
);
Debug(2, "Wait returned error: %m");
syslog(LOG_NOTICE
, "There are still some (hung) processes.");
/* We don't need no steenkin timeout any more... */
boing_m2stimeout
= (jmp_buf *)0;
/* Jump ! (Rein ins Vergnuegen) */
longjmp(boing_singleuser
, 2);
/**********************************************************
* Schedule a retry operation for a later time *
**********************************************************/
Debug(3, "Scheduling callout in %d seconds.", when
);
/* find a free callout entry */
if (callout_nfree
<= CALLOUT_MINFREE
)
syslog(LOG_WARNING
, "Callout table is full !");
callout_free
= ntp
->next
;
/* look at which point we put it in the callout list */
when
+= (unsigned int)time(NULL
);
for (octp
= NULL
, ctp
= callout_tab
; ctp
; octp
= ctp
, ctp
= ctp
->next
)
if (ctp
) ctp
->sleept
-= when
;
when
= callout_tab
->sleept
- (unsigned int)time(NULL
);
Debug(4, "Next callout: NOW !");
kill (getpid(), SIGALRM
);
Debug(4, "Next callout in %d seconds.", when
);
/**********************************************************
* allocate (further) elements to the callout table *
*********************************************************/
ntp
= malloc(sizeof(callout_t
) * CALLOUT_CHUNK
);
for (i
=1; i
< CALLOUT_CHUNK
; i
++)
ntp
->next
= callout_free
;
callout_free
= &ntp
[i
-1];
callout_nfree
+= CALLOUT_CHUNK
;
/**********************************************************
* Removes all callout entries *
*********************************************************/
Debug(4, "All callouts for today cancelled.");
for (ctp
= callout_tab
; ctp
; ctp
= nctp
) {
ctp
->next
= callout_free
;
callout_tab
= (callout_t
*)0;
/**********************************************************
* calls the callback routines when the time has expired *
*********************************************************/
now
= (unsigned int) time(NULL
);
for (ctp
= callout_tab
; ctp
;) {
ctp
->sleept
+= callout_tab
->sleept
;
callout_tab
->next
= callout_free
;
callout_free
= callout_tab
;
switch (callout_free
->what
) {
Debug(3, "Callout -> Multiuser");
longjmp(boing_multiuser
, 2);
Debug(3, "Callout -> do_getty()");
(void)do_getty((ttytab_t
*)callout_free
->arg
, 0);
Debug(3, "Callout -> M2S timeout");
longjmp(*boing_m2stimeout
, 2);
/* schedule next alarm */
Debug(4, "Next callout in %d seconds.", callout_tab
->sleept
- now
);
alarm(callout_tab
->sleept
- now
);
/**********************************************************
* Set up signals for the child processes *
*********************************************************/
signal(SIGTSTP
, SIG_DFL
);
signal(SIGTERM
, SIG_DFL
);
signal(SIGHUP
, SIG_DFL
);
signal(SIGUSR1
, SIG_DFL
);
signal(SIGUSR2
, SIG_DFL
);
signal(SIGALRM
, SIG_DFL
);
signal(SIGTTIN
, SIG_DFL
);