toybox/toys/pending/init.c
<<
>>
Prefs
   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