blob: 1e496ad59724a3ee789556b3122551f956e14301 [file] [log] [blame]
/* simpleinit.c - poe@daimi.aau.dk */
/* Version 1.21 */
/* 1999-02-22 Arkadiusz Mi¶kiewicz <misiek@misiek.eu.org>
* - added Native Language Support
*/
#include <sys/types.h>
#include <stdlib.h>
#include <unistd.h>
#include <limits.h>
#include <stdio.h>
#include <fcntl.h>
#include <string.h>
#include <signal.h>
#include <pwd.h>
#include <sys/file.h>
#include <sys/wait.h>
#include <sys/stat.h>
#include <sys/sysmacros.h>
#include <utmp.h>
#ifdef SHADOW_PWD
# include <shadow.h>
#endif
#include "my_crypt.h"
#include "pathnames.h"
#include "linux_reboot.h"
#include "nls.h"
#define CMDSIZ 150 /* max size of a line in inittab */
#define NUMCMD 30 /* max number of lines in inittab */
#define NUMTOK 20 /* max number of tokens in inittab command */
#define RUN_RC
#define TZFILE "/etc/TZ"
char tzone[CMDSIZ];
/* #define DEBUGGING */
/* Define this if you want init to ignore the termcap field in inittab for
console ttys. */
/* #define SPECIAL_CONSOLE_TERM */
#define ever (;;)
struct initline {
pid_t pid;
char tty[10];
char termcap[30];
char *toks[NUMTOK];
char line[CMDSIZ];
};
struct initline inittab[NUMCMD];
int numcmd;
int stopped = 0; /* are we stopped */
int do_rc();
void spawn(), hup_handler(), read_inittab();
void tstp_handler(), int_handler(), set_tz(), write_wtmp();
int boot_single();
void err(char *s)
{
int fd;
if((fd = open("/dev/console", O_WRONLY)) < 0) return;
write(fd, "init: ", 6);
write(fd, s, strlen(s));
close(fd);
}
void
enter_single()
{
pid_t pid;
int i;
err(_("Booting to single user mode.\n"));
if((pid = fork()) == 0) {
/* the child */
execl(_PATH_BSHELL, _PATH_BSHELL, NULL);
err(_("exec of single user shell failed\n"));
} else if(pid > 0) {
while(wait(&i) != pid) /* nothing */;
} else if(pid < 0) {
err(_("fork of single user shell failed\n"));
}
unlink(_PATH_SINGLE);
}
int main(int argc, char *argv[])
{
int vec,i;
pid_t pid;
#ifdef SET_TZ
set_tz();
#endif
signal(SIGTSTP, tstp_handler);
signal(SIGINT, int_handler);
setlocale(LC_ALL, "");
bindtextdomain(PACKAGE, LOCALEDIR);
textdomain(PACKAGE);
/*
* start up in single user mode if /etc/singleboot exists or if
* argv[1] is "single".
*/
if(boot_single(0, argc, argv)) enter_single();
#ifdef RUN_RC
/*If we get a SIGTSTP before multi-user mode, do nothing*/
while(stopped)
pause();
if(do_rc() != 0 && boot_single(1, argc, argv) && !stopped)
enter_single();
while(stopped) /*Also if /etc/rc fails & we get SIGTSTP*/
pause();
#endif
write_wtmp(); /* write boottime record */
for(i = 0; i < NUMCMD; i++)
inittab[i].pid = -1;
read_inittab();
#ifdef DEBUGGING
for(i = 0; i < numcmd; i++) {
char **p;
p = inittab[i].toks;
printf("toks= %s %s %s %s\n",p[0], p[1], p[2], p[3]);
printf("tty= %s\n", inittab[i].tty);
printf("termcap= %s\n", inittab[i].termcap);
}
exit(0);
#endif
signal(SIGHUP, hup_handler);
for(i = 0; i < getdtablesize(); i++) close(i);
for(i = 0; i < numcmd; i++)
spawn(i);
for ever {
pid = wait(&vec);
/* clear utmp entry, and append to wtmp if possible */
{
struct utmp *ut;
int ut_fd, lf;
utmpname(_PATH_UTMP);
setutent();
while((ut = getutent())) {
if(ut->ut_pid == pid) {
time(&ut->ut_time);
memset(&ut->ut_user, 0, UT_NAMESIZE);
memset(&ut->ut_host, 0, sizeof(ut->ut_host));
ut->ut_type = DEAD_PROCESS;
ut->ut_pid = 0;
ut->ut_addr = 0;
/*endutent();*/
pututline(ut);
if ((lf = open(_PATH_WTMPLOCK, O_CREAT|O_WRONLY, 0660)) >= 0) {
flock(lf, LOCK_EX|LOCK_NB);
if((ut_fd = open(_PATH_WTMP, O_APPEND|O_WRONLY)) >= 0) {
write(ut_fd, ut, sizeof(struct utmp));
close(ut_fd);
}
flock(lf, LOCK_UN|LOCK_NB);
close(lf);
}
break;
}
}
endutent();
}
for(i = 0; i < numcmd; i++) {
if(pid == inittab[i].pid || inittab[i].pid < 0) {
if(stopped) inittab[i].pid = -1;
else spawn(i);
break;
}
}
}
}
#define MAXTRIES 3 /* number of tries allowed when giving the password */
/*
* return true if we should boot up in singleuser mode. If argv[i] is
* "single" or the file /etc/singleboot exists, then singleuser mode should
* be entered. If /etc/securesingle exists ask for root password first.
*/
int boot_single(int singlearg, int argc, char *argv[])
{
char *pass, *rootpass = NULL;
struct passwd *pwd;
int i;
for(i = 1; i < argc; i++) {
if(argv[i] && !strcmp(argv[i], "single")) singlearg = 1;
}
if(access(_PATH_SINGLE, 04) == 0 || singlearg) {
if(access(_PATH_SECURE, 04) == 0) {
if((pwd = getpwnam("root")) || (pwd = getpwuid(0)))
rootpass = pwd->pw_passwd;
else
return 1; /* a bad /etc/passwd should not lock out */
for(i = 0; i < MAXTRIES; i++) {
pass = getpass(_("Password: "));
if(pass == NULL) continue;
if(!strcmp(crypt(pass, rootpass), rootpass)) {
return 1;
}
puts(_("\nWrong password.\n"));
}
} else return 1;
}
return 0;
}
/*
* run /etc/rc. The environment is passed to the script, so the RC environment
* variable can be used to decide what to do. RC may be set from LILO.
*/
int do_rc()
{
pid_t pid;
int stat;
if((pid = fork()) == 0) {
/* the child */
char *argv[2];
argv[0] = _PATH_BSHELL;
argv[1] = (char *)0;
close(0);
if(open(_PATH_RC, O_RDONLY, 0) == 0) {
execv(_PATH_BSHELL, argv);
err(_("exec rc failed\n"));
_exit(2);
}
err(_("open of rc file failed\n"));
_exit(1);
} else if(pid > 0) {
/* parent, wait till rc process dies before spawning */
while(wait(&stat) != pid) /* nothing */;
} else if(pid < 0) {
err(_("fork of rc shell failed\n"));
}
return WEXITSTATUS(stat);
}
void spawn(int i)
{
pid_t pid;
int j;
if((pid = fork()) < 0) {
inittab[i].pid = -1;
err(_("fork failed\n"));
return;
}
if(pid) {
/* this is the parent */
inittab[i].pid = pid;
return;
} else {
/* this is the child */
char term[40];
#ifdef SET_TZ
char tz[CMDSIZ];
#endif
char *env[3];
setsid();
for(j = 0; j < getdtablesize(); j++)
(void) close(j);
(void) sprintf(term, "TERM=%s", inittab[i].termcap);
env[0] = term;
env[1] = (char *)0;
#ifdef SET_TZ
(void) sprintf(tz, "TZ=%s", tzone);
env[1] = tz;
#endif
env[2] = (char *)0;
execve(inittab[i].toks[0], inittab[i].toks, env);
err(_("exec failed\n"));
sleep(5);
_exit(1);
}
}
void read_inittab()
{
FILE *f;
char buf[CMDSIZ];
int i,j,k;
char *ptr, *getty;
#ifdef SPECIAL_CONSOLE_TERM
char tty[50];
struct stat stb;
#endif
char *termenv, *getenv();
termenv = getenv("TERM"); /* set by kernel */
/* termenv = "vt100"; */
if(!(f = fopen(_PATH_INITTAB, "r"))) {
err(_("cannot open inittab\n"));
_exit(1);
}
i = 0;
while(!feof(f) && i < NUMCMD - 2) {
if(fgets(buf, CMDSIZ - 1, f) == 0) break;
buf[CMDSIZ-1] = 0;
for(k = 0; k < CMDSIZ && buf[k]; k++) {
if(buf[k] == '#') {
buf[k] = 0; break;
}
}
if(buf[0] == 0 || buf[0] == '\n') continue;
(void) strcpy(inittab[i].line, buf);
(void) strtok(inittab[i].line, ":");
(void) strncpy(inittab[i].tty, inittab[i].line, 10);
inittab[i].tty[9] = 0;
(void) strncpy(inittab[i].termcap,
strtok((char *)0, ":"), 30);
inittab[i].termcap[29] = 0;
getty = strtok((char *)0, ":");
(void) strtok(getty, " \t\n");
inittab[i].toks[0] = getty;
j = 1;
while((ptr = strtok((char *)0, " \t\n")))
inittab[i].toks[j++] = ptr;
inittab[i].toks[j] = (char *)0;
#ifdef SPECIAL_CONSOLE_TERM
/* special-case termcap for the console ttys */
(void) sprintf(tty, "/dev/%s", inittab[i].tty);
if(!termenv || stat(tty, &stb) < 0) {
err(_("no TERM or cannot stat tty\n"));
} else {
/* is it a console tty? */
if(major(stb.st_rdev) == 4 && minor(stb.st_rdev) < 64) {
strncpy(inittab[i].termcap, termenv, 30);
inittab[i].termcap[29] = 0;
}
}
#endif
i++;
}
fclose(f);
numcmd = i;
}
void hup_handler()
{
int i,j;
int oldnum;
struct initline savetab[NUMCMD];
int had_already;
(void) signal(SIGHUP, SIG_IGN);
memcpy(savetab, inittab, NUMCMD * sizeof(struct initline));
oldnum = numcmd;
read_inittab();
for(i = 0; i < numcmd; i++) {
had_already = 0;
for(j = 0; j < oldnum; j++) {
if(!strcmp(savetab[j].tty, inittab[i].tty)) {
had_already = 1;
if((inittab[i].pid = savetab[j].pid) < 0)
spawn(i);
}
}
if(!had_already) spawn(i);
}
(void) signal(SIGHUP, hup_handler);
}
void tstp_handler()
{
stopped = ~stopped;
if(!stopped) hup_handler();
signal(SIGTSTP, tstp_handler);
}
void int_handler()
{
/*
* After Linux 0.96b PL1, we get a SIGINT when
* the user presses Ctrl-Alt-Del...
*/
int pid;
sync();
sync();
pid = fork();
if (pid > 0)
return;
if (pid == 0) /* reboot properly... */
execl(_PATH_REBOOT, _PATH_REBOOT, (char *)0);
/* fork or exec failed, try the hard way... */
my_reboot(LINUX_REBOOT_CMD_RESTART);
}
void set_tz()
{
FILE *f;
int len;
if((f=fopen(TZFILE, "r")) == (FILE *)NULL) return;
fgets(tzone, CMDSIZ-2, f);
fclose(f);
if((len=strlen(tzone)) < 2) return;
tzone[len-1] = 0; /* get rid of the '\n' */
setenv("TZ", tzone, 0);
}
void write_wtmp()
{
int fd, lf;
struct utmp ut;
memset((char *)&ut, 0, sizeof(ut));
strcpy(ut.ut_line, "~");
memset(ut.ut_name, 0, sizeof(ut.ut_name));
time(&ut.ut_time);
ut.ut_type = BOOT_TIME;
if ((lf = open(_PATH_WTMPLOCK, O_CREAT|O_WRONLY, 0660)) >= 0) {
flock(lf, LOCK_EX|LOCK_NB); /* make sure init won't hang */
if((fd = open(_PATH_WTMP, O_WRONLY|O_APPEND)) >= 0) {
write(fd, (char *)&ut, sizeof(ut));
close(fd);
}
flock(lf, LOCK_UN|LOCK_NB);
close(lf);
}
}