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