1/* init.c - init program.
2 *
3 * Copyright 2012 Harvind Singh <harvindsingh1981@gmail.com>
4 * Copyright 2013 Kyungwan Han  <asura321@gmail.com>
5 *
6 * No Standard
7
8USE_INIT(NEWTOY(init, "", TOYFLAG_SBIN))
9
10config INIT
11  bool "init"
12  default n
13  help
14    usage: init
15
16    System V style init.
17
18    First program to run (as PID 1) when the system comes up, reading
19    /etc/inittab to determine actions.
20*/
21
22#include "toys.h"
23#include <sys/reboot.h>
24
25struct action_list_seed {
26  struct action_list_seed *next;
27  pid_t pid;
28  uint8_t action;
29  char *terminal_name;
30  char *command;
31} *action_list_pointer = NULL;
32int caught_signal;
33
34//INITTAB action defination
35#define SYSINIT     0x01
36#define WAIT        0x02
37#define ONCE        0x04
38#define RESPAWN     0x08
39#define ASKFIRST    0x10
40#define CTRLALTDEL  0x20
41#define SHUTDOWN    0x40
42#define RESTART     0x80
43
44static void initialize_console(void)
45{
46  int fd;
47  char *p = getenv("CONSOLE");
48
49  if (!p) p = getenv("console");
50  if (!p) {
51    fd = open("/dev/null", O_RDWR);
52    if (fd >= 0) {
53      while (fd < 2) fd = dup(fd);
54      while (fd > 2) close(fd--);
55    }
56  } else {
57    fd = open(p, O_RDWR | O_NONBLOCK | O_NOCTTY);
58    if (fd < 0) printf("Unable to open console %s\n",p);
59    else {
60      dup2(fd,0);
61      dup2(fd,1);
62      dup2(fd,2);
63    }
64  }
65
66  if (!getenv("TERM")) putenv("TERM=linux");
67}
68
69static void reset_term(int fd)
70{
71  struct termios terminal;
72
73  tcgetattr(fd, &terminal);
74  terminal.c_cc[VINTR] = 3;    //ctrl-c
75  terminal.c_cc[VQUIT] = 28;   /*ctrl-\*/
76  terminal.c_cc[VERASE] = 127; //ctrl-?
77  terminal.c_cc[VKILL] = 21;   //ctrl-u
78  terminal.c_cc[VEOF] = 4;     //ctrl-d
79  terminal.c_cc[VSTART] = 17;  //ctrl-q
80  terminal.c_cc[VSTOP] = 19;   //ctrl-s
81  terminal.c_cc[VSUSP] = 26;   //ctrl-z
82
83  terminal.c_line = 0;
84  terminal.c_cflag &= CRTSCTS|PARODD|PARENB|CSTOPB|CSIZE|CBAUDEX|CBAUD;
85  terminal.c_cflag |= CLOCAL|HUPCL|CREAD;
86
87  //enable start/stop input and output control + map CR to NL on input
88  terminal.c_iflag = IXON|IXOFF|ICRNL;
89
90  //Map NL to CR-NL on output
91  terminal.c_oflag = ONLCR|OPOST;
92  terminal.c_lflag = IEXTEN|ECHOKE|ECHOCTL|ECHOK|ECHOE|ECHO|ICANON|ISIG;
93  tcsetattr(fd, TCSANOW, &terminal);
94}
95
96static void add_new_action(uint8_t action,char *command,char *term)
97{
98  struct action_list_seed *x,**y;
99
100  y = &action_list_pointer;
101  x = *y;
102  while (x) {
103    if (!(strcmp(x->command, command)) && !(strcmp(x->terminal_name, term))) {
104      *y = x->next; //remove from the list
105      while(*y) y = &(*y)->next; //traverse through list till end
106      x->next = NULL;
107      break;
108    }
109    y = &(x)->next;
110    x = *y;
111  }
112
113  //create a new node
114  if (!x) {
115    x = xzalloc(sizeof(*x));
116    x->command = xstrdup(command);
117    x->terminal_name = xstrdup(term);
118  }
119  x->action = action;
120  *y = x;
121}
122
123static void inittab_parsing(void)
124{
125  int i, fd, line_number = 0, token_count = 0;
126  char *p, *q, *extracted_token, *tty_name = NULL, *command = NULL, *tmp;
127  uint8_t action = 0;
128  char *act_name = "sysinit\0wait\0once\0respawn\0askfirst\0ctrlaltdel\0"
129                    "shutdown\0restart\0";
130
131  fd = open("/etc/inittab", O_RDONLY);
132  if (fd < 0) {
133    error_msg("Unable to open /etc/inittab. Using Default inittab");
134    add_new_action(SYSINIT, "/etc/init.d/rcS", "");
135    add_new_action(RESPAWN, "/sbin/getty -n -l /bin/sh -L 115200 tty1 vt100", "");
136  } else {
137    while((q = p = get_line(fd))) { //read single line from /etc/inittab
138      char *x;
139
140      if ((x = strchr(p, '#'))) *x = '\0';
141      line_number++;
142      token_count = 0;
143      action = 0;
144      tty_name = command = NULL;
145
146      while ((extracted_token = strsep(&p,":"))) {
147        token_count++;
148        switch (token_count) {
149          case 1:
150            if (*extracted_token) {
151              if (!strncmp(extracted_token, "/dev/", 5))
152                tty_name = xmprintf("%s",extracted_token);
153              else tty_name = xmprintf("/dev/%s",extracted_token);
154            } else tty_name = xstrdup("");
155            break;
156          case 2:
157            break;
158          case 3:
159            for (tmp = act_name, i = 0; *tmp; i++, tmp += strlen(tmp) +1) {
160              if (!strcmp(tmp, extracted_token)) {
161                action = 1 << i;
162                break;
163              }
164            }
165            if (!*tmp) error_msg("Invalid action at line number %d ---- ignoring",line_number);
166            break;
167          case 4:
168            command = xstrdup(extracted_token);
169            break;
170          default:
171            error_msg("Bad inittab entry at line %d", line_number);
172            break;
173        }
174      }  //while token
175
176      if (q) free(q);
177      if (token_count != 4) {
178        free(tty_name);
179        free(command);
180        continue;
181      }
182      if (action) add_new_action(action, command, tty_name);
183      free(tty_name);
184      free(command);
185    } //while line
186
187    close(fd);
188  }
189}
190
191static void run_command(char *command)
192{
193  char *final_command[128];
194  int hyphen = (command[0]=='-');
195
196  command = command + hyphen;
197  if (!strpbrk(command, "?<>'\";[]{}\\|=()*&^$!`~")) {
198    char *next_command;
199    char *extracted_command;
200    int x = 0;
201
202    next_command = strncpy(toybuf, command - hyphen, sizeof(toybuf));
203    next_command[sizeof(toybuf) - 1] = toybuf[sizeof(toybuf) - 1 ] = '\0';
204    command = next_command + hyphen;
205    while ((extracted_command = strsep(&next_command," \t"))) {
206      if (*extracted_command) {
207        final_command[x] = extracted_command;
208        x++;
209      }
210    }
211    final_command[x] = NULL;
212  } else {
213    snprintf(toybuf, sizeof(toybuf), "exec %s", command);
214    command = "-/bin/sh"+1;
215    final_command[0] = ("-/bin/sh"+!hyphen);
216    final_command[1] = "-c";
217    final_command[2] = toybuf;
218    final_command[3] = NULL;
219  }
220  if (hyphen) ioctl(0, TIOCSCTTY, 0);
221  execvp(command, final_command);
222  error_msg("unable to run %s",command);
223}
224
225//runs all same type of actions
226static pid_t final_run(struct action_list_seed *x)
227{
228  pid_t pid;
229  int fd;
230  sigset_t signal_set;
231
232  sigfillset(&signal_set);
233  sigprocmask(SIG_BLOCK, &signal_set, NULL);
234  if (x->action & ASKFIRST) pid = fork();
235  else pid = vfork();
236
237  if (pid > 0) {
238    //parent process or error
239    //unblock the signals
240    sigfillset(&signal_set);
241    sigprocmask(SIG_UNBLOCK, &signal_set, NULL);
242
243    return pid;
244  } else if (pid < 0) {
245    perror_msg("fork fail");
246    sleep(1);
247    return 0;
248  }
249
250  //new born child process
251  sigset_t signal_set_c;
252  sigfillset(&signal_set_c);
253  sigprocmask(SIG_UNBLOCK, &signal_set_c, NULL);
254  setsid(); //new session
255
256  if (x->terminal_name[0]) {
257    close(0);
258    fd = open(x->terminal_name, (O_RDWR|O_NONBLOCK),0600);
259    if (fd != 0) {
260      error_msg("Unable to open %s,%s\n", x->terminal_name, strerror(errno));
261      _exit(EXIT_FAILURE);
262    } else {
263      dup2(0, 1);
264      dup2(0, 2);
265    }
266  }
267  reset_term(0);
268  run_command(x->command);
269  _exit(-1);
270}
271
272static struct action_list_seed* mark_as_terminated_process(pid_t pid)
273{
274  struct action_list_seed *x;
275
276  if (pid > 0) {
277    for (x = action_list_pointer; x; x = x->next) {
278      if (x->pid == pid) {
279        x->pid = 0;
280        return x;
281      }
282    }
283  }
284
285  return NULL;
286}
287
288static void waitforpid(pid_t pid)
289{
290  if (pid <= 0) return;
291
292  for(;;) {
293    pid_t y = wait(NULL);
294    mark_as_terminated_process(y);
295    if (kill(y, 0)) break;
296  }
297}
298
299static void run_action_from_list(int action)
300{
301  pid_t pid;
302  struct action_list_seed *x = action_list_pointer;
303
304  for (; x; x = x->next) {
305    if (!(x->action & action)) continue;
306    if (x->action & (SHUTDOWN|ONCE|SYSINIT|CTRLALTDEL|WAIT)) {
307      pid = final_run(x);
308      if (!pid) return;
309      if (x->action & (SHUTDOWN|SYSINIT|CTRLALTDEL|WAIT)) waitforpid(pid);
310    }
311    if (x->action & (ASKFIRST|RESPAWN))
312      if (!(x->pid)) x->pid = final_run(x);
313  }
314 }
315
316static void set_default(void)
317{
318  sigset_t signal_set_c;
319
320  sigatexit(SIG_DFL);
321  sigfillset(&signal_set_c);
322  sigprocmask(SIG_UNBLOCK,&signal_set_c, NULL);
323
324  run_action_from_list(SHUTDOWN);
325  error_msg("The system is going down NOW!");
326  kill(-1, SIGTERM);
327  error_msg("Sent SIGTERM to all processes");
328  sync();
329  sleep(1);
330  kill(-1,SIGKILL);
331  sync();
332}
333
334static void halt_poweroff_reboot_handler(int sig_no)
335{
336  unsigned int reboot_magic_no = 0;
337  pid_t pid;
338
339  set_default();
340
341  switch (sig_no) {
342    case SIGUSR1:
343      error_msg("Requesting system halt");
344      reboot_magic_no=RB_HALT_SYSTEM;
345      break;
346    case SIGUSR2:
347      error_msg("Requesting system poweroff");
348      reboot_magic_no=RB_POWER_OFF;
349      break;
350    case SIGTERM:
351      error_msg("Requesting system reboot");
352      reboot_magic_no=RB_AUTOBOOT;
353      break;
354    default:
355      break;
356  }
357
358  sleep(1);
359  pid = vfork();
360
361  if (pid == 0) {
362    reboot(reboot_magic_no);
363    _exit(EXIT_SUCCESS);
364  }
365
366  while(1) sleep(1);
367}
368
369static void restart_init_handler(int sig_no)
370{
371  struct action_list_seed *x;
372  pid_t pid;
373  int fd;
374
375  for (x = action_list_pointer; x; x = x->next) {
376    if (!(x->action & RESTART)) continue;
377
378    set_default();
379
380    if (x->terminal_name[0]) {
381      close(0);
382      fd = open(x->terminal_name, (O_RDWR|O_NONBLOCK),0600);
383
384      if (fd != 0) {
385        error_msg("Unable to open %s,%s\n", x->terminal_name, strerror(errno));
386        sleep(1);
387        pid = vfork();
388
389        if (pid == 0) {
390          reboot(RB_HALT_SYSTEM);
391          _exit(EXIT_SUCCESS);
392        }
393
394        while(1) sleep(1);
395      } else {
396        dup2(0, 1);
397        dup2(0, 2);
398        reset_term(0);
399        run_command(x->command);
400      }
401    }
402  }
403}
404
405static void catch_signal(int sig_no)
406{
407  caught_signal = sig_no;
408  error_msg("signal seen");
409}
410
411static void pause_handler(int sig_no)
412{
413  int signal_backup,errno_backup;
414  pid_t pid;
415
416  errno_backup = errno;
417  signal_backup = caught_signal;
418  xsignal(SIGCONT, catch_signal);
419
420  while(1) {
421    if (caught_signal == SIGCONT) break;
422    do pid = waitpid(-1,NULL,WNOHANG); while((pid==-1) && (errno=EINTR));
423    mark_as_terminated_process(pid);
424    sleep(1);
425  }
426
427  signal(SIGCONT, SIG_DFL);
428  errno = errno_backup;
429  caught_signal = signal_backup;
430}
431
432static int check_if_pending_signals(void)
433{
434  int signal_caught = 0;
435
436  while(1) {
437    int sig = caught_signal;
438    if (!sig) return signal_caught;
439    caught_signal = 0;
440    signal_caught = 1;
441    if (sig == SIGINT) run_action_from_list(CTRLALTDEL);
442  }
443}
444
445void init_main(void)
446{
447  struct sigaction sig_act;
448
449  if (getpid() != 1) error_exit("Already running");
450  printf("Started init\n");
451  initialize_console();
452  reset_term(0);
453
454  if (chdir("/")) perror_exit("Can't cd to /");
455  setsid();
456
457  putenv("HOME=/");
458  putenv("PATH=/sbin:/usr/sbin:/bin:/usr/bin");
459  putenv("SHELL=/bin/sh");
460  putenv("USER=root");
461
462  inittab_parsing();
463  xsignal(SIGUSR1, halt_poweroff_reboot_handler);//halt
464  xsignal(SIGUSR2, halt_poweroff_reboot_handler);//poweroff
465  xsignal(SIGTERM, halt_poweroff_reboot_handler);//reboot
466  xsignal(SIGQUIT, restart_init_handler);//restart init
467  memset(&sig_act, 0, sizeof(sig_act));
468  sigfillset(&sig_act.sa_mask);
469  sigdelset(&sig_act.sa_mask, SIGCONT);
470  sig_act.sa_handler = pause_handler;
471  sigaction(SIGTSTP, &sig_act, NULL);
472  memset(&sig_act, 0, sizeof(sig_act));
473  sig_act.sa_handler = catch_signal;
474  sigaction(SIGINT, &sig_act, NULL);
475  sigaction(SIGHUP, &sig_act, NULL);
476  run_action_from_list(SYSINIT);
477  check_if_pending_signals();
478  run_action_from_list(WAIT);
479  check_if_pending_signals();
480  run_action_from_list(ONCE);
481  while (1) {
482    int suspected_WNOHANG = check_if_pending_signals();
483
484    run_action_from_list(RESPAWN | ASKFIRST);
485    suspected_WNOHANG = suspected_WNOHANG|check_if_pending_signals();
486    sleep(1);//let cpu breath
487    suspected_WNOHANG = suspected_WNOHANG|check_if_pending_signals();
488    if (suspected_WNOHANG) suspected_WNOHANG=WNOHANG;
489
490    while(1) {
491      pid_t pid = waitpid(-1, NULL, suspected_WNOHANG);
492
493      if (pid <= 0) break;
494      mark_as_terminated_process(pid);
495      suspected_WNOHANG = WNOHANG;
496    }
497  }
498}
499