457 lines
9.3 KiB
C
457 lines
9.3 KiB
C
/* simpleinit.c - poe@daimi.aau.dk */
|
|
/* Version 1.21 */
|
|
|
|
#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 "pathnames.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);
|
|
|
|
/*
|
|
* 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();
|
|
if((pid = fork()) == 0) {
|
|
/* reboot properly... */
|
|
execl(_PATH_REBOOT, _PATH_REBOOT, (char *)0);
|
|
reboot(0xfee1dead, 672274793, 0x1234567);
|
|
} else if(pid < 0)
|
|
/* fork failed, try the hard way... */
|
|
reboot(0xfee1dead, 672274793, 0x1234567);
|
|
}
|
|
|
|
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);
|
|
}
|
|
}
|