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 
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(uint8_t action,char * command,char * term)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 
inittab_parsing(void)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 
run_command(char * command)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
final_run(struct action_list_seed * x)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 
mark_as_terminated_process(pid_t pid)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 
waitforpid(pid_t pid)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 
run_action_from_list(int action)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 
set_default(void)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 
halt_poweroff_reboot_handler(int sig_no)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 
restart_init_handler(int sig_no)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 
catch_signal(int sig_no)405 static void catch_signal(int sig_no)
406 {
407   caught_signal = sig_no;
408   error_msg("signal seen");
409 }
410 
pause_handler(int sig_no)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 
check_if_pending_signals(void)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 
init_main(void)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