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