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(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  xsignal_all_killers(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