xref: /third_party/toybox/toys/pending/init.c (revision 0f66f451)
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(int 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 parse_inittab(void)
124{
125  char *line = 0;
126  size_t allocated_length = 0;
127  int line_number = 0;
128  char *act_name = "sysinit\0wait\0once\0respawn\0askfirst\0ctrlaltdel\0"
129                    "shutdown\0restart\0";
130  FILE *fp = fopen("/etc/inittab", "r");
131
132  if (!fp) {
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    return;
137  }
138
139  while (getline(&line, &allocated_length, fp) > 0) {
140    char *p = line, *x, *tty_name = 0, *command = 0, *extracted_token, *tmp;
141    int action = 0, token_count = 0, i;
142
143    if ((x = strchr(p, '#'))) *x = '\0';
144    line_number++;
145    action = 0;
146
147    while ((extracted_token = strsep(&p,":"))) {
148      token_count++;
149      switch (token_count) {
150        case 1:
151          if (*extracted_token) {
152            if (!strncmp(extracted_token, "/dev/", 5))
153              tty_name = xmprintf("%s",extracted_token);
154            else tty_name = xmprintf("/dev/%s",extracted_token);
155          } else tty_name = xstrdup("");
156          break;
157        case 2:
158          break;
159        case 3:
160          for (tmp = act_name, i = 0; *tmp; i++, tmp += strlen(tmp) +1) {
161            if (!strcmp(tmp, extracted_token)) {
162              action = 1 << i;
163              break;
164            }
165          }
166          if (!*tmp) error_msg("Invalid action at line number %d ---- ignoring",line_number);
167          break;
168        case 4:
169          command = xstrdup(extracted_token);
170          break;
171        default:
172          error_msg("Bad inittab entry at line %d", line_number);
173          break;
174      }
175    }  //while token
176
177    if (token_count == 4 && action) add_new_action(action, command, tty_name);
178    free(tty_name);
179    free(command);
180  }
181  free(line);
182  fclose(fp);
183}
184
185static void reload_inittab(void)
186{
187  // Remove all inactive actions, then reload /etc/inittab
188  struct action_list_seed **y;
189  y = &action_list_pointer;
190  while (*y) {
191    if (!(*y)->pid) {
192      struct action_list_seed *x = *y;
193      free(x->terminal_name);
194      free(x->command);
195      *y = (*y)->next;
196      free(x);
197      continue;
198    }
199    y = &(*y)->next;
200  }
201  parse_inittab();
202}
203
204static void run_command(char *command)
205{
206  char *final_command[128];
207  int hyphen = (command[0]=='-');
208
209  command = command + hyphen;
210  if (!strpbrk(command, "?<>'\";[]{}\\|=()*&^$!`~")) {
211    char *next_command;
212    char *extracted_command;
213    int x = 0;
214
215    next_command = strncpy(toybuf, command - hyphen, sizeof(toybuf));
216    next_command[sizeof(toybuf) - 1] = toybuf[sizeof(toybuf) - 1 ] = '\0';
217    command = next_command + hyphen;
218    while ((extracted_command = strsep(&next_command," \t"))) {
219      if (*extracted_command) {
220        final_command[x] = extracted_command;
221        x++;
222      }
223    }
224    final_command[x] = NULL;
225  } else {
226    snprintf(toybuf, sizeof(toybuf), "exec %s", command);
227    command = "-/bin/sh"+1;
228    final_command[0] = ("-/bin/sh"+!hyphen);
229    final_command[1] = "-c";
230    final_command[2] = toybuf;
231    final_command[3] = NULL;
232  }
233  if (hyphen) ioctl(0, TIOCSCTTY, 0);
234  execvp(command, final_command);
235  error_msg("unable to run %s",command);
236}
237
238//runs all same type of actions
239static pid_t final_run(struct action_list_seed *x)
240{
241  pid_t pid;
242  int fd;
243  sigset_t signal_set;
244
245  sigfillset(&signal_set);
246  sigprocmask(SIG_BLOCK, &signal_set, NULL);
247  if (x->action & ASKFIRST) pid = fork();
248  else pid = vfork();
249
250  if (pid > 0) {
251    //parent process or error
252    //unblock the signals
253    sigfillset(&signal_set);
254    sigprocmask(SIG_UNBLOCK, &signal_set, NULL);
255
256    return pid;
257  } else if (pid < 0) {
258    perror_msg("fork fail");
259    sleep(1);
260    return 0;
261  }
262
263  //new born child process
264  sigset_t signal_set_c;
265  sigfillset(&signal_set_c);
266  sigprocmask(SIG_UNBLOCK, &signal_set_c, NULL);
267  setsid(); //new session
268
269  if (x->terminal_name[0]) {
270    close(0);
271    fd = open(x->terminal_name, (O_RDWR|O_NONBLOCK),0600);
272    if (fd != 0) {
273      error_msg("Unable to open %s,%s\n", x->terminal_name, strerror(errno));
274      _exit(EXIT_FAILURE);
275    } else {
276      dup2(0, 1);
277      dup2(0, 2);
278    }
279  }
280  reset_term(0);
281  run_command(x->command);
282  _exit(-1);
283}
284
285static struct action_list_seed* mark_as_terminated_process(pid_t pid)
286{
287  struct action_list_seed *x;
288
289  if (pid > 0) {
290    for (x = action_list_pointer; x; x = x->next) {
291      if (x->pid == pid) {
292        x->pid = 0;
293        return x;
294      }
295    }
296  }
297
298  return NULL;
299}
300
301static void waitforpid(pid_t pid)
302{
303  if (pid <= 0) return;
304
305  while (!kill(pid, 0)) mark_as_terminated_process(wait(NULL));
306}
307
308static void run_action_from_list(int action)
309{
310  pid_t pid;
311  struct action_list_seed *x = action_list_pointer;
312
313  for (; x; x = x->next) {
314    if (!(x->action & action)) continue;
315    if (x->action & (SHUTDOWN|ONCE|SYSINIT|CTRLALTDEL|WAIT)) {
316      pid = final_run(x);
317      if (!pid) return;
318      if (x->action & (SHUTDOWN|SYSINIT|CTRLALTDEL|WAIT)) waitforpid(pid);
319    }
320    if (x->action & (ASKFIRST|RESPAWN))
321      if (!(x->pid)) x->pid = final_run(x);
322  }
323 }
324
325static void set_default(void)
326{
327  sigset_t signal_set_c;
328
329  sigatexit(SIG_DFL);
330  sigfillset(&signal_set_c);
331  sigprocmask(SIG_UNBLOCK,&signal_set_c, NULL);
332
333  run_action_from_list(SHUTDOWN);
334  error_msg("The system is going down NOW!");
335  kill(-1, SIGTERM);
336  error_msg("Sent SIGTERM to all processes");
337  sync();
338  sleep(1);
339  kill(-1,SIGKILL);
340  sync();
341}
342
343static void halt_poweroff_reboot_handler(int sig_no)
344{
345  unsigned int reboot_magic_no = 0;
346  pid_t pid;
347
348  set_default();
349
350  switch (sig_no) {
351    case SIGUSR1:
352      error_msg("Requesting system halt");
353      reboot_magic_no=RB_HALT_SYSTEM;
354      break;
355    case SIGUSR2:
356      error_msg("Requesting system poweroff");
357      reboot_magic_no=RB_POWER_OFF;
358      break;
359    case SIGTERM:
360      error_msg("Requesting system reboot");
361      reboot_magic_no=RB_AUTOBOOT;
362      break;
363    default:
364      break;
365  }
366
367  sleep(1);
368  pid = vfork();
369
370  if (pid == 0) {
371    reboot(reboot_magic_no);
372    _exit(EXIT_SUCCESS);
373  }
374
375  while(1) sleep(1);
376}
377
378static void restart_init_handler(int sig_no)
379{
380  struct action_list_seed *x;
381  pid_t pid;
382  int fd;
383
384  for (x = action_list_pointer; x; x = x->next) {
385    if (!(x->action & RESTART)) continue;
386
387    set_default();
388
389    if (x->terminal_name[0]) {
390      close(0);
391      fd = open(x->terminal_name, (O_RDWR|O_NONBLOCK),0600);
392
393      if (fd != 0) {
394        error_msg("Unable to open %s,%s\n", x->terminal_name, strerror(errno));
395        sleep(1);
396        pid = vfork();
397
398        if (pid == 0) {
399          reboot(RB_HALT_SYSTEM);
400          _exit(EXIT_SUCCESS);
401        }
402
403        while(1) sleep(1);
404      } else {
405        dup2(0, 1);
406        dup2(0, 2);
407        reset_term(0);
408        run_command(x->command);
409      }
410    }
411  }
412}
413
414static void catch_signal(int sig_no)
415{
416  caught_signal = sig_no;
417  error_msg("signal seen: %d", sig_no);
418}
419
420static void pause_handler(int sig_no)
421{
422  int signal_backup,errno_backup;
423  pid_t pid;
424
425  errno_backup = errno;
426  signal_backup = caught_signal;
427  xsignal(SIGCONT, catch_signal);
428
429  while(1) {
430    if (caught_signal == SIGCONT) break;
431    do pid = waitpid(-1,NULL,WNOHANG); while((pid==-1) && (errno=EINTR));
432    mark_as_terminated_process(pid);
433    sleep(1);
434  }
435
436  signal(SIGCONT, SIG_DFL);
437  errno = errno_backup;
438  caught_signal = signal_backup;
439}
440
441static int check_if_pending_signals(void)
442{
443  int signal_caught = 0;
444
445  while(1) {
446    int sig = caught_signal;
447    if (!sig) return signal_caught;
448    caught_signal = 0;
449    signal_caught = 1;
450    if (sig == SIGINT) run_action_from_list(CTRLALTDEL);
451    else if (sig == SIGHUP) {
452      error_msg("reloading inittab");
453      reload_inittab();
454    }
455  }
456}
457
458void init_main(void)
459{
460  struct sigaction sig_act;
461
462  if (getpid() != 1) error_exit("Already running");
463  printf("Started init\n");
464  initialize_console();
465  reset_term(0);
466
467  if (chdir("/")) perror_exit("Can't cd to /");
468  setsid();
469
470  putenv("HOME=/");
471  putenv("PATH=/sbin:/usr/sbin:/bin:/usr/bin");
472  putenv("SHELL=/bin/sh");
473  putenv("USER=root");
474
475  parse_inittab();
476  xsignal(SIGUSR1, halt_poweroff_reboot_handler);//halt
477  xsignal(SIGUSR2, halt_poweroff_reboot_handler);//poweroff
478  xsignal(SIGTERM, halt_poweroff_reboot_handler);//reboot
479  xsignal(SIGQUIT, restart_init_handler);//restart init
480  memset(&sig_act, 0, sizeof(sig_act));
481  sigfillset(&sig_act.sa_mask);
482  sigdelset(&sig_act.sa_mask, SIGCONT);
483  sig_act.sa_handler = pause_handler;
484  sigaction(SIGTSTP, &sig_act, NULL);
485  memset(&sig_act, 0, sizeof(sig_act));
486  sig_act.sa_handler = catch_signal;
487  sigaction(SIGINT, &sig_act, NULL);
488  sigaction(SIGHUP, &sig_act, NULL);
489  run_action_from_list(SYSINIT);
490  check_if_pending_signals();
491  run_action_from_list(WAIT);
492  check_if_pending_signals();
493  run_action_from_list(ONCE);
494  while (1) {
495    int suspected_WNOHANG = check_if_pending_signals();
496
497    run_action_from_list(RESPAWN | ASKFIRST);
498    suspected_WNOHANG = suspected_WNOHANG|check_if_pending_signals();
499    sleep(1);//let cpu breath
500    suspected_WNOHANG = suspected_WNOHANG|check_if_pending_signals();
501    if (suspected_WNOHANG) suspected_WNOHANG=WNOHANG;
502
503    while(1) {
504      pid_t pid = waitpid(-1, NULL, suspected_WNOHANG);
505
506      if (pid <= 0) break;
507      mark_as_terminated_process(pid);
508      suspected_WNOHANG = WNOHANG;
509    }
510  }
511}
512