You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
518 lines
12 KiB
518 lines
12 KiB
/* init.c - init program.
|
|
*
|
|
* Copyright 2012 Harvind Singh <harvindsingh1981@gmail.com>
|
|
* Copyright 2013 Kyungwan Han <asura321@gmail.com>
|
|
*
|
|
* No Standard
|
|
|
|
USE_INIT(NEWTOY(init, "", TOYFLAG_SBIN))
|
|
|
|
config INIT
|
|
bool "init"
|
|
default n
|
|
help
|
|
usage: init
|
|
|
|
System V style init.
|
|
|
|
First program to run (as PID 1) when the system comes up, reading
|
|
/etc/inittab to determine actions.
|
|
*/
|
|
|
|
#include "toys.h"
|
|
#include <sys/reboot.h>
|
|
|
|
struct action_list_seed {
|
|
struct action_list_seed *next;
|
|
pid_t pid;
|
|
uint8_t action;
|
|
char *terminal_name;
|
|
char *command;
|
|
} *action_list_pointer = NULL;
|
|
int caught_signal;
|
|
|
|
//INITTAB action defination
|
|
#define SYSINIT 0x01
|
|
#define WAIT 0x02
|
|
#define ONCE 0x04
|
|
#define RESPAWN 0x08
|
|
#define ASKFIRST 0x10
|
|
#define CTRLALTDEL 0x20
|
|
#define SHUTDOWN 0x40
|
|
#define RESTART 0x80
|
|
|
|
static void initialize_console(void)
|
|
{
|
|
int fd;
|
|
char *p = getenv("CONSOLE");
|
|
|
|
if (!p) p = getenv("console");
|
|
if (!p) {
|
|
fd = open("/dev/null", O_RDWR);
|
|
if (fd >= 0) {
|
|
while (fd < 2) fd = dup(fd);
|
|
while (fd > 2) close(fd--);
|
|
}
|
|
} else {
|
|
fd = open(p, O_RDWR | O_NONBLOCK | O_NOCTTY);
|
|
if (fd < 0) printf("Unable to open console %s\n",p);
|
|
else {
|
|
dup2(fd,0);
|
|
dup2(fd,1);
|
|
dup2(fd,2);
|
|
}
|
|
}
|
|
|
|
if (!getenv("TERM")) putenv("TERM=linux");
|
|
}
|
|
|
|
static void reset_term(int fd)
|
|
{
|
|
struct termios terminal;
|
|
|
|
tcgetattr(fd, &terminal);
|
|
terminal.c_cc[VINTR] = 3; //ctrl-c
|
|
terminal.c_cc[VQUIT] = 28; /*ctrl-\*/
|
|
terminal.c_cc[VERASE] = 127; //ctrl-?
|
|
terminal.c_cc[VKILL] = 21; //ctrl-u
|
|
terminal.c_cc[VEOF] = 4; //ctrl-d
|
|
terminal.c_cc[VSTART] = 17; //ctrl-q
|
|
terminal.c_cc[VSTOP] = 19; //ctrl-s
|
|
terminal.c_cc[VSUSP] = 26; //ctrl-z
|
|
|
|
terminal.c_line = 0;
|
|
terminal.c_cflag &= CRTSCTS|PARODD|PARENB|CSTOPB|CSIZE|CBAUDEX|CBAUD;
|
|
terminal.c_cflag |= CLOCAL|HUPCL|CREAD;
|
|
|
|
//enable start/stop input and output control + map CR to NL on input
|
|
terminal.c_iflag = IXON|IXOFF|ICRNL;
|
|
|
|
//Map NL to CR-NL on output
|
|
terminal.c_oflag = ONLCR|OPOST;
|
|
terminal.c_lflag = IEXTEN|ECHOKE|ECHOCTL|ECHOK|ECHOE|ECHO|ICANON|ISIG;
|
|
tcsetattr(fd, TCSANOW, &terminal);
|
|
}
|
|
|
|
static void add_new_action(uint8_t action,char *command,char *term)
|
|
{
|
|
struct action_list_seed *x,**y;
|
|
|
|
y = &action_list_pointer;
|
|
x = *y;
|
|
while (x) {
|
|
if (!(strcmp(x->command, command)) && !(strcmp(x->terminal_name, term))) {
|
|
*y = x->next; //remove from the list
|
|
while(*y) y = &(*y)->next; //traverse through list till end
|
|
x->next = NULL;
|
|
break;
|
|
}
|
|
y = &(x)->next;
|
|
x = *y;
|
|
}
|
|
|
|
//create a new node
|
|
if (!x) {
|
|
x = xzalloc(sizeof(*x));
|
|
x->command = xstrdup(command);
|
|
x->terminal_name = xstrdup(term);
|
|
}
|
|
x->action = action;
|
|
*y = x;
|
|
}
|
|
|
|
static void inittab_parsing(void)
|
|
{
|
|
int i, fd, line_number = 0, token_count = 0;
|
|
char *p, *q, *extracted_token, *tty_name = NULL, *command = NULL, *tmp;
|
|
uint8_t action = 0;
|
|
char *act_name = "sysinit\0wait\0once\0respawn\0askfirst\0ctrlaltdel\0"
|
|
"shutdown\0restart\0";
|
|
|
|
fd = open("/etc/inittab", O_RDONLY);
|
|
if (fd < 0) {
|
|
error_msg("Unable to open /etc/inittab. Using Default inittab");
|
|
add_new_action(SYSINIT, "/etc/init.d/rcS", "");
|
|
add_new_action(RESPAWN, "/sbin/getty -n -l /bin/sh -L 115200 tty1 vt100", "");
|
|
} else {
|
|
while((q = p = get_line(fd))) { //read single line from /etc/inittab
|
|
char *x;
|
|
|
|
if ((x = strchr(p, '#'))) *x = '\0';
|
|
line_number++;
|
|
token_count = 0;
|
|
action = 0;
|
|
tty_name = command = NULL;
|
|
|
|
while ((extracted_token = strsep(&p,":"))) {
|
|
token_count++;
|
|
switch (token_count) {
|
|
case 1:
|
|
if (*extracted_token) {
|
|
if (!strncmp(extracted_token, "/dev/", 5))
|
|
tty_name = xmprintf("%s",extracted_token);
|
|
else tty_name = xmprintf("/dev/%s",extracted_token);
|
|
} else tty_name = xstrdup("");
|
|
break;
|
|
case 2:
|
|
break;
|
|
case 3:
|
|
for (tmp = act_name, i = 0; *tmp; i++, tmp += strlen(tmp) +1) {
|
|
if (!strcmp(tmp, extracted_token)) {
|
|
action = 1 << i;
|
|
break;
|
|
}
|
|
}
|
|
if (!*tmp) error_msg("Invalid action at line number %d ---- ignoring",line_number);
|
|
break;
|
|
case 4:
|
|
command = xstrdup(extracted_token);
|
|
break;
|
|
default:
|
|
error_msg("Bad inittab entry at line %d", line_number);
|
|
break;
|
|
}
|
|
} //while token
|
|
|
|
if (q) free(q);
|
|
if (token_count != 4) {
|
|
free(tty_name);
|
|
free(command);
|
|
continue;
|
|
}
|
|
if (action) add_new_action(action, command, tty_name);
|
|
free(tty_name);
|
|
free(command);
|
|
} //while line
|
|
|
|
close(fd);
|
|
}
|
|
}
|
|
|
|
static void reload_inittab(void)
|
|
{
|
|
// Remove all inactive actions, then reload /etc/inittab
|
|
struct action_list_seed **y;
|
|
y = &action_list_pointer;
|
|
while (*y) {
|
|
if (!(*y)->pid) {
|
|
struct action_list_seed *x = *y;
|
|
free(x->terminal_name);
|
|
free(x->command);
|
|
*y = (*y)->next;
|
|
free(x);
|
|
continue;
|
|
}
|
|
y = &(*y)->next;
|
|
}
|
|
inittab_parsing();
|
|
}
|
|
|
|
static void run_command(char *command)
|
|
{
|
|
char *final_command[128];
|
|
int hyphen = (command[0]=='-');
|
|
|
|
command = command + hyphen;
|
|
if (!strpbrk(command, "?<>'\";[]{}\\|=()*&^$!`~")) {
|
|
char *next_command;
|
|
char *extracted_command;
|
|
int x = 0;
|
|
|
|
next_command = strncpy(toybuf, command - hyphen, sizeof(toybuf));
|
|
next_command[sizeof(toybuf) - 1] = toybuf[sizeof(toybuf) - 1 ] = '\0';
|
|
command = next_command + hyphen;
|
|
while ((extracted_command = strsep(&next_command," \t"))) {
|
|
if (*extracted_command) {
|
|
final_command[x] = extracted_command;
|
|
x++;
|
|
}
|
|
}
|
|
final_command[x] = NULL;
|
|
} else {
|
|
snprintf(toybuf, sizeof(toybuf), "exec %s", command);
|
|
command = "-/bin/sh"+1;
|
|
final_command[0] = ("-/bin/sh"+!hyphen);
|
|
final_command[1] = "-c";
|
|
final_command[2] = toybuf;
|
|
final_command[3] = NULL;
|
|
}
|
|
if (hyphen) ioctl(0, TIOCSCTTY, 0);
|
|
execvp(command, final_command);
|
|
error_msg("unable to run %s",command);
|
|
}
|
|
|
|
//runs all same type of actions
|
|
static pid_t final_run(struct action_list_seed *x)
|
|
{
|
|
pid_t pid;
|
|
int fd;
|
|
sigset_t signal_set;
|
|
|
|
sigfillset(&signal_set);
|
|
sigprocmask(SIG_BLOCK, &signal_set, NULL);
|
|
if (x->action & ASKFIRST) pid = fork();
|
|
else pid = vfork();
|
|
|
|
if (pid > 0) {
|
|
//parent process or error
|
|
//unblock the signals
|
|
sigfillset(&signal_set);
|
|
sigprocmask(SIG_UNBLOCK, &signal_set, NULL);
|
|
|
|
return pid;
|
|
} else if (pid < 0) {
|
|
perror_msg("fork fail");
|
|
sleep(1);
|
|
return 0;
|
|
}
|
|
|
|
//new born child process
|
|
sigset_t signal_set_c;
|
|
sigfillset(&signal_set_c);
|
|
sigprocmask(SIG_UNBLOCK, &signal_set_c, NULL);
|
|
setsid(); //new session
|
|
|
|
if (x->terminal_name[0]) {
|
|
close(0);
|
|
fd = open(x->terminal_name, (O_RDWR|O_NONBLOCK),0600);
|
|
if (fd != 0) {
|
|
error_msg("Unable to open %s,%s\n", x->terminal_name, strerror(errno));
|
|
_exit(EXIT_FAILURE);
|
|
} else {
|
|
dup2(0, 1);
|
|
dup2(0, 2);
|
|
}
|
|
}
|
|
reset_term(0);
|
|
run_command(x->command);
|
|
_exit(-1);
|
|
}
|
|
|
|
static struct action_list_seed* mark_as_terminated_process(pid_t pid)
|
|
{
|
|
struct action_list_seed *x;
|
|
|
|
if (pid > 0) {
|
|
for (x = action_list_pointer; x; x = x->next) {
|
|
if (x->pid == pid) {
|
|
x->pid = 0;
|
|
return x;
|
|
}
|
|
}
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
static void waitforpid(pid_t pid)
|
|
{
|
|
if (pid <= 0) return;
|
|
|
|
while (!kill(pid, 0)) mark_as_terminated_process(wait(NULL));
|
|
}
|
|
|
|
static void run_action_from_list(int action)
|
|
{
|
|
pid_t pid;
|
|
struct action_list_seed *x = action_list_pointer;
|
|
|
|
for (; x; x = x->next) {
|
|
if (!(x->action & action)) continue;
|
|
if (x->action & (SHUTDOWN|ONCE|SYSINIT|CTRLALTDEL|WAIT)) {
|
|
pid = final_run(x);
|
|
if (!pid) return;
|
|
if (x->action & (SHUTDOWN|SYSINIT|CTRLALTDEL|WAIT)) waitforpid(pid);
|
|
}
|
|
if (x->action & (ASKFIRST|RESPAWN))
|
|
if (!(x->pid)) x->pid = final_run(x);
|
|
}
|
|
}
|
|
|
|
static void set_default(void)
|
|
{
|
|
sigset_t signal_set_c;
|
|
|
|
sigatexit(SIG_DFL);
|
|
sigfillset(&signal_set_c);
|
|
sigprocmask(SIG_UNBLOCK,&signal_set_c, NULL);
|
|
|
|
run_action_from_list(SHUTDOWN);
|
|
error_msg("The system is going down NOW!");
|
|
kill(-1, SIGTERM);
|
|
error_msg("Sent SIGTERM to all processes");
|
|
sync();
|
|
sleep(1);
|
|
kill(-1,SIGKILL);
|
|
sync();
|
|
}
|
|
|
|
static void halt_poweroff_reboot_handler(int sig_no)
|
|
{
|
|
unsigned int reboot_magic_no = 0;
|
|
pid_t pid;
|
|
|
|
set_default();
|
|
|
|
switch (sig_no) {
|
|
case SIGUSR1:
|
|
error_msg("Requesting system halt");
|
|
reboot_magic_no=RB_HALT_SYSTEM;
|
|
break;
|
|
case SIGUSR2:
|
|
error_msg("Requesting system poweroff");
|
|
reboot_magic_no=RB_POWER_OFF;
|
|
break;
|
|
case SIGTERM:
|
|
error_msg("Requesting system reboot");
|
|
reboot_magic_no=RB_AUTOBOOT;
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
sleep(1);
|
|
pid = vfork();
|
|
|
|
if (pid == 0) {
|
|
reboot(reboot_magic_no);
|
|
_exit(EXIT_SUCCESS);
|
|
}
|
|
|
|
while(1) sleep(1);
|
|
}
|
|
|
|
static void restart_init_handler(int sig_no)
|
|
{
|
|
struct action_list_seed *x;
|
|
pid_t pid;
|
|
int fd;
|
|
|
|
for (x = action_list_pointer; x; x = x->next) {
|
|
if (!(x->action & RESTART)) continue;
|
|
|
|
set_default();
|
|
|
|
if (x->terminal_name[0]) {
|
|
close(0);
|
|
fd = open(x->terminal_name, (O_RDWR|O_NONBLOCK),0600);
|
|
|
|
if (fd != 0) {
|
|
error_msg("Unable to open %s,%s\n", x->terminal_name, strerror(errno));
|
|
sleep(1);
|
|
pid = vfork();
|
|
|
|
if (pid == 0) {
|
|
reboot(RB_HALT_SYSTEM);
|
|
_exit(EXIT_SUCCESS);
|
|
}
|
|
|
|
while(1) sleep(1);
|
|
} else {
|
|
dup2(0, 1);
|
|
dup2(0, 2);
|
|
reset_term(0);
|
|
run_command(x->command);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
static void catch_signal(int sig_no)
|
|
{
|
|
caught_signal = sig_no;
|
|
error_msg("signal seen: %d", sig_no);
|
|
}
|
|
|
|
static void pause_handler(int sig_no)
|
|
{
|
|
int signal_backup,errno_backup;
|
|
pid_t pid;
|
|
|
|
errno_backup = errno;
|
|
signal_backup = caught_signal;
|
|
xsignal(SIGCONT, catch_signal);
|
|
|
|
while(1) {
|
|
if (caught_signal == SIGCONT) break;
|
|
do pid = waitpid(-1,NULL,WNOHANG); while((pid==-1) && (errno=EINTR));
|
|
mark_as_terminated_process(pid);
|
|
sleep(1);
|
|
}
|
|
|
|
signal(SIGCONT, SIG_DFL);
|
|
errno = errno_backup;
|
|
caught_signal = signal_backup;
|
|
}
|
|
|
|
static int check_if_pending_signals(void)
|
|
{
|
|
int signal_caught = 0;
|
|
|
|
while(1) {
|
|
int sig = caught_signal;
|
|
if (!sig) return signal_caught;
|
|
caught_signal = 0;
|
|
signal_caught = 1;
|
|
if (sig == SIGINT) run_action_from_list(CTRLALTDEL);
|
|
else if (sig == SIGHUP) {
|
|
error_msg("reloading inittab");
|
|
reload_inittab();
|
|
}
|
|
}
|
|
}
|
|
|
|
void init_main(void)
|
|
{
|
|
struct sigaction sig_act;
|
|
|
|
if (getpid() != 1) error_exit("Already running");
|
|
printf("Started init\n");
|
|
initialize_console();
|
|
reset_term(0);
|
|
|
|
if (chdir("/")) perror_exit("Can't cd to /");
|
|
setsid();
|
|
|
|
putenv("HOME=/");
|
|
putenv("PATH=/sbin:/usr/sbin:/bin:/usr/bin");
|
|
putenv("SHELL=/bin/sh");
|
|
putenv("USER=root");
|
|
|
|
inittab_parsing();
|
|
xsignal(SIGUSR1, halt_poweroff_reboot_handler);//halt
|
|
xsignal(SIGUSR2, halt_poweroff_reboot_handler);//poweroff
|
|
xsignal(SIGTERM, halt_poweroff_reboot_handler);//reboot
|
|
xsignal(SIGQUIT, restart_init_handler);//restart init
|
|
memset(&sig_act, 0, sizeof(sig_act));
|
|
sigfillset(&sig_act.sa_mask);
|
|
sigdelset(&sig_act.sa_mask, SIGCONT);
|
|
sig_act.sa_handler = pause_handler;
|
|
sigaction(SIGTSTP, &sig_act, NULL);
|
|
memset(&sig_act, 0, sizeof(sig_act));
|
|
sig_act.sa_handler = catch_signal;
|
|
sigaction(SIGINT, &sig_act, NULL);
|
|
sigaction(SIGHUP, &sig_act, NULL);
|
|
run_action_from_list(SYSINIT);
|
|
check_if_pending_signals();
|
|
run_action_from_list(WAIT);
|
|
check_if_pending_signals();
|
|
run_action_from_list(ONCE);
|
|
while (1) {
|
|
int suspected_WNOHANG = check_if_pending_signals();
|
|
|
|
run_action_from_list(RESPAWN | ASKFIRST);
|
|
suspected_WNOHANG = suspected_WNOHANG|check_if_pending_signals();
|
|
sleep(1);//let cpu breath
|
|
suspected_WNOHANG = suspected_WNOHANG|check_if_pending_signals();
|
|
if (suspected_WNOHANG) suspected_WNOHANG=WNOHANG;
|
|
|
|
while(1) {
|
|
pid_t pid = waitpid(-1, NULL, suspected_WNOHANG);
|
|
|
|
if (pid <= 0) break;
|
|
mark_as_terminated_process(pid);
|
|
suspected_WNOHANG = WNOHANG;
|
|
}
|
|
}
|
|
}
|