Home | History | Annotate | Download | only in pending
      1 /* init.c - init program.
      2  *
      3  * Copyright 2012 Harvind Singh <harvindsingh1981 (at) gmail.com>
      4  * Copyright 2013 Kyungwan Han  <asura321 (at) gmail.com>
      5  *
      6  * No Standard
      7 
      8 USE_INIT(NEWTOY(init, "", TOYFLAG_SBIN))
      9 
     10 config 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 
     25 struct 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;
     32 int 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 
     44 static 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 
     69 static 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 
     96 static 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 
    123 static 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 
    191 static 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
    226 static 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 
    272 static 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 
    288 static 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 
    299 static 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 
    316 static 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 
    334 static 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 
    369 static 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 
    405 static void catch_signal(int sig_no)
    406 {
    407   caught_signal = sig_no;
    408   error_msg("signal seen");
    409 }
    410 
    411 static 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 
    432 static 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 
    445 void 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