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