Here's a little program I've extracted from the code to init that
demonstrates the problem.
(To build sabotage just "cc -o sabotage sabotage.c").
Whooops, forgot to attach prog, here it is:
/*
* Init A System-V Init Clone.
*
* Usage: /sbin/init
* init [0123456SsQqAaBbCc]
* telinit [0123456SsQqAaBbCc]
*
* Version: @(#)init.c 2.86 30-Jul-2004 [EMAIL PROTECTED]
*/
#define VERSION "2.86"
#define DATE "31-Jul-2004"
/*
* This file is part of the sysvinit suite,
* Copyright 1991-2004 Miquel van Smoorenburg.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version
* 2 of the License, or (at your option) any later version.
*
*/
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/ioctl.h>
#include <sys/wait.h>
#ifdef __linux__
#include <sys/kd.h>
#endif
#include <sys/resource.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <stdio.h>
#include <time.h>
#include <fcntl.h>
#include <string.h>
#include <signal.h>
#include <termios.h>
#include <utmp.h>
#include <ctype.h>
#include <stdarg.h>
#include <sys/syslog.h>
#include <sys/time.h>
#include <sys/reboot.h>
#define PATH_DFL "PATH=/bin:/usr/bin:/sbin:/usr/sbin"
#define VT_MASTER "/dev/tty0" /* Virtual console master */
#define CONSOLE "/dev/console" /* Logical system console */
/* Version information */
char *Version = "@(#) init " VERSION " " DATE " [EMAIL PROTECTED]";
char *bootmsg = "version " VERSION " %s";
#define E_VERSION "INIT_VERSION=sysvinit-" VERSION
char runlevel = 'S'; /* The current run level */
char thislevel = 'S'; /* The current runlevel */
char prevlevel = 'N'; /* Previous runlevel */
int dfl_level = 0; /* Default runlevel */
sig_atomic_t got_cont = 0; /* Set if we received the SIGCONT signal */
sig_atomic_t got_signals; /* Set if we received a signal. */
int emerg_shell = 0; /* Start emergency shell? */
int wrote_wtmp_reboot = 1; /* Set when we wrote the reboot record */
int wrote_utmp_reboot = 1; /* Set when we wrote the reboot record */
int sltime = 5; /* Sleep time between TERM and KILL */
char *argv0; /* First arguments; show up in ps listing */
int maxproclen; /* Maximal length of argv[0] with \0 */
struct utmp utproto; /* Only used for sizeof(utproto.ut_id) */
char *user_console = NULL; /* User console device */
char *console_dev; /* Console device. */
int pipe_fd = -1; /* /dev/initctl */
int did_boot = 0; /* Did we already do BOOT* stuff? */
/* Used by re-exec part */
int reload = 0; /* Should we do initialization stuff? */
char *myname="/sbin/init"; /* What should we exec */
int oops_error; /* Used by some of the re-exec code. */
const char *Signature = "12567362"; /* Signature for re-exec fd */
#ifdef __GNUC__
__attribute__ ((format (printf, 2, 3)))
#endif
void initlog(int loglevel, char *s, ...);
/*
* Sleep a number of seconds.
*
* This only works correctly because the linux select updates
* the elapsed time in the struct timeval passed to select!
*/
void do_sleep(int sec)
{
struct timeval tv;
tv.tv_sec = sec;
tv.tv_usec = 0;
while(select(0, NULL, NULL, NULL, &tv) < 0 && errno == EINTR)
;
}
/*
* Non-failing allocation routines (init cannot fail).
*/
void *imalloc(size_t size)
{
void *m;
while ((m = malloc(size)) == NULL) {
initlog(0, "out of memory");
do_sleep(5);
}
memset(m, 0, size);
return m;
}
char *istrdup(char *s)
{
char *m;
int l;
l = strlen(s) + 1;
m = imalloc(l);
memcpy(m, s, l);
return m;
}
/*
* Set console_dev to a working console.
*/
void console_init(void)
{
int fd;
int tried_devcons = 0;
int tried_vtmaster = 0;
char *s;
if (user_console) {
console_dev = user_console;
} else if ((s = getenv("CONSOLE")) != NULL)
console_dev = s;
else {
console_dev = CONSOLE;
tried_devcons++;
}
while ((fd = open(console_dev, O_RDONLY|O_NONBLOCK)) < 0) {
if (!tried_devcons) {
tried_devcons++;
console_dev = CONSOLE;
continue;
}
if (!tried_vtmaster) {
tried_vtmaster++;
console_dev = VT_MASTER;
continue;
}
break;
}
if (fd < 0)
console_dev = "/dev/null";
else
close(fd);
}
/*
* Open the console with retries.
*/
int console_open(int mode)
{
int f, fd = -1;
int m;
/*
* Open device in nonblocking mode.
*/
m = mode | O_NONBLOCK;
/*
* Retry the open five times.
*/
for(f = 0; f < 5; f++)
if ((fd = open(console_dev, m)) >= 0) break;
if (fd < 0) return fd;
/*
* Set original flags.
*/
if (m != mode)
fcntl(fd, F_SETFL, mode);
return fd;
}
/*
* Set terminal settings to reasonable defaults
*/
void console_stty(void)
{
struct termios tty;
int fd;
if ((fd = console_open(O_RDWR|O_NOCTTY)) < 0) {
initlog(0, "can't open %s", console_dev);
return;
}
(void) tcgetattr(fd, &tty);
tty.c_cflag &= CBAUD|CBAUDEX|CSIZE|CSTOPB|PARENB|PARODD;
tty.c_cflag |= HUPCL|CLOCAL|CREAD;
tty.c_cc[VINTR] = 3; /* ctrl('c') */
tty.c_cc[VQUIT] = 28; /* ctrl('\\') */
tty.c_cc[VERASE] = 127;
tty.c_cc[VKILL] = 24; /* ctrl('x') */
tty.c_cc[VEOF] = 4; /* ctrl('d') */
tty.c_cc[VTIME] = 0;
tty.c_cc[VMIN] = 1;
tty.c_cc[VSTART] = 17; /* ctrl('q') */
tty.c_cc[VSTOP] = 19; /* ctrl('s') */
tty.c_cc[VSUSP] = 26; /* ctrl('z') */
/*
* Set pre and post processing
*/
tty.c_iflag = IGNPAR|ICRNL|IXON|IXANY;
tty.c_oflag = OPOST|ONLCR;
tty.c_lflag = ISIG|ICANON|ECHO|ECHOCTL|ECHOPRT|ECHOKE;
/*
* Now set the terminal line.
* We don't care about non-transmitted output data
* and non-read input data.
*/
(void) tcsetattr(fd, TCSANOW, &tty);
(void) tcflush(fd, TCIOFLUSH);
(void) close(fd);
}
/*
* Print to the system console
*/
void print(char *s)
{
int fd;
if ((fd = console_open(O_WRONLY|O_NOCTTY|O_NDELAY)) >= 0) {
write(fd, s, strlen(s));
close(fd);
}
}
/*
* Log something to a logfile and the console.
*/
#ifdef __GNUC__
__attribute__ ((format (printf, 2, 3)))
#endif
void initlog(int loglevel, char *s, ...)
{
va_list va_alist;
char buf[256];
va_start(va_alist, s);
vsnprintf(buf, sizeof(buf), s, va_alist);
va_end(va_alist);
/*
* And log to the console.
*/
print("\rINIT: ");
print(buf);
print("\r\n");
}
/*
* The main loop
*/
int main()
{
int f;
if (!reload) {
/*
* Tell the kernel to send us SIGINT when CTRL-ALT-DEL
* is pressed, and that we want to handle keyboard signals.
*/
reboot(RB_DISABLE_CAD);
if ((f = open(VT_MASTER, O_RDWR | O_NOCTTY)) >= 0) {
(void) ioctl(f, KDSIGACCEPT, SIGWINCH);
close(f);
} else
(void) ioctl(0, KDSIGACCEPT, SIGWINCH);
}
console_init();
if (!reload) {
/* Close whatever files are open, and reset the console. */
close(0);
close(1);
close(2);
console_stty();
setsid();
/*
* Set default PATH variable.
*/
putenv(PATH_DFL);
/*
* Say hello to the world
*/
initlog(0, bootmsg, "booting");
/*
* Start normal boot procedure.
*/
sleep (10000000);
}
/*NOTREACHED*/
abort ();
}