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