view toys/pending/init.c @ 1261:9e105bab92e5 draft

Revert lots of half-finished local debris I didn't mean to check in with Isaac's roadmap update. Mercurial's "import" command is still broken, committing local tree changes to files that weren't even touched by the patch because the hg developers inisist, when I point out how stupid it is, that they meant to do that. (hg record can do hunks, but import can't even track _files_.)
author Rob Landley <rob@landley.net>
date Wed, 16 Apr 2014 08:54:19 -0500
parents 6ca31490f581
children c25ee9918e65
line wrap: on
line source

/* 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

    init the system.
*/

#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 = (p = getenv("CONSOLE")) ? 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 set_sane_term(void)
{
  struct termios terminal;
 
  tcgetattr(0, &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 = terminal.c_cflag&(CRTSCTS|PARODD|PARENB|CSTOPB|CSIZE|CBAUDEX|CBAUD);
  terminal.c_cflag = terminal.c_cflag|(CLOCAL|HUPCL|CREAD);
  terminal.c_iflag = IXON|IXOFF|ICRNL;//enable start/stop input and output control + map CR to NL on input
  terminal.c_oflag = ONLCR|OPOST;//Map NL to CR-NL on output
  terminal.c_lflag = IEXTEN|ECHOKE|ECHOCTL|ECHOK|ECHOE|ECHO|ICANON|ISIG;
  tcsetattr(0, 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;
      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) continue; 
      if (action) add_new_action(action, command, tty_name);  
      free(tty_name);
      free(command);
    } //while line

    close(fd);
  }
}

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);
    }
  }
  set_sane_term();
  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;

  for(;;) {
    pid_t y = wait(NULL);
    mark_as_terminated_process(y);
    if (kill(y, 0)) break;
  }
}
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;

  signal(SIGUSR1,SIG_DFL);
  signal(SIGUSR2,SIG_DFL);
  signal(SIGTERM,SIG_DFL);
  signal(SIGQUIT,SIG_DFL);
  signal(SIGINT,SIG_DFL);
  signal(SIGHUP,SIG_DFL);
  signal(SIGTSTP,SIG_DFL);
  signal(SIGSTOP,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);
        set_sane_term();
        run_command(x->command);
      }
    }
  }
}

static void catch_signal(int sig_no)
{
  caught_signal = sig_no;
  error_msg("signal seen");
}

static void pause_handler(int sig_no)
{
  int signal_backup,errno_backup;
  pid_t pid;

  errno_backup = errno;
  signal_backup = caught_signal;
  signal(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);
  }
}

void init_main(void)
{
  struct sigaction sig_act;

  if (getpid() != 1) error_exit("Already running"); 
  printf("Started init\n"); 
  initialize_console();
  set_sane_term();

  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();  
  signal(SIGUSR1, halt_poweroff_reboot_handler);//halt
  signal(SIGUSR2, halt_poweroff_reboot_handler);//poweroff
  signal(SIGTERM, halt_poweroff_reboot_handler);//reboot
  signal(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;
    }
  }
}