Mercurial > hg > toybox
comparison toys/pending/init.c @ 986:69adf14d70e1
System V style init, submitted by Kyungwan Han.
author | Rob Landley <rob@landley.net> |
---|---|
date | Sun, 04 Aug 2013 00:31:27 -0500 |
parents | |
children | a2f80613be36 |
comparison
equal
deleted
inserted
replaced
985:32644e4439bd | 986:69adf14d70e1 |
---|---|
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 init the system. | |
17 */ | |
18 | |
19 #include "toys.h" | |
20 #include<linux/vt.h> | |
21 #include<sys/reboot.h> | |
22 | |
23 struct action_list_seed { | |
24 struct action_list_seed *next; | |
25 pid_t pid; | |
26 uint8_t action; | |
27 char *terminal_name; | |
28 char *command; | |
29 }; | |
30 struct action_list_seed *action_list_pointer = NULL; | |
31 int caught_signal; | |
32 | |
33 //INITTAB action defination | |
34 #define SYSINIT 0x01 | |
35 #define WAIT 0x02 | |
36 #define ONCE 0x04 | |
37 #define RESPAWN 0x08 | |
38 #define ASKFIRST 0x10 | |
39 #define CTRLALTDEL 0x20 | |
40 #define SHUTDOWN 0x40 | |
41 #define RESTART 0x80 | |
42 | |
43 static void initialize_console(void) | |
44 { | |
45 int fd; | |
46 char *p = (p = getenv("CONSOLE")) ? p : getenv("console"); | |
47 if (!p) { | |
48 fd = open("/dev/null", O_RDWR); | |
49 if (fd >= 0) { | |
50 while(fd < 2) fd = dup(fd); | |
51 while(fd > 2) close(fd--); | |
52 } | |
53 } else { | |
54 fd = open(p, O_RDWR | O_NONBLOCK | O_NOCTTY); | |
55 if (fd < 0) xprintf("Unable to open console %s\n",p); | |
56 else { | |
57 dup2(fd,0); | |
58 dup2(fd,1); | |
59 dup2(fd,2); | |
60 } | |
61 } | |
62 p = getenv("TERM"); | |
63 #ifdef VT_OPENQRY | |
64 int terminal_no; | |
65 if (ioctl(0, VT_OPENQRY, &terminal_no)) { | |
66 if (!p || !strcmp(p,"linux")) putenv((char*)"TERM=vt102"); | |
67 } else | |
68 #endif | |
69 if (!p) putenv((char*)"TERM=linux"); | |
70 } | |
71 | |
72 static void set_sane_term(void) | |
73 { | |
74 struct termios terminal; | |
75 tcgetattr(0, &terminal); | |
76 terminal.c_cc[VINTR] = 3;//ctrl-c | |
77 terminal.c_cc[VQUIT] = 28;/*ctrl-\*/ | |
78 terminal.c_cc[VERASE] = 127;//ctrl-? | |
79 terminal.c_cc[VKILL] = 21;//ctrl-u | |
80 terminal.c_cc[VEOF] = 4;//ctrl-d | |
81 terminal.c_cc[VSTART] = 17;//ctrl-q | |
82 terminal.c_cc[VSTOP] = 19;//ctrl-s | |
83 terminal.c_cc[VSUSP] = 26;//ctrl-z | |
84 | |
85 terminal.c_line = 0; | |
86 terminal.c_cflag = terminal.c_cflag&(CRTSCTS|PARODD|PARENB|CSTOPB|CSIZE|CBAUDEX|CBAUD); | |
87 terminal.c_cflag = terminal.c_cflag|(CLOCAL|HUPCL|CREAD); | |
88 terminal.c_iflag = IXON|IXOFF|ICRNL;//enable start/stop input and output control + map CR to NL on input | |
89 terminal.c_oflag = ONLCR|OPOST;//Map NL to CR-NL on output | |
90 terminal.c_lflag = IEXTEN|ECHOKE|ECHOCTL|ECHOK|ECHOE|ECHO|ICANON|ISIG; | |
91 tcsetattr(0, TCSANOW, &terminal); | |
92 } | |
93 | |
94 static void set_enviornment(void) | |
95 { | |
96 putenv((char*)"HOME=/"); | |
97 putenv((char*)"PATH=/sbin:/usr/sbin:/bin:/usr/bin"); | |
98 putenv((char*)"SHELL=/bin/sh"); | |
99 putenv((char*)"USER=root"); | |
100 } | |
101 | |
102 static void add_new_action(uint8_t action,char *command,char *term) | |
103 { | |
104 struct action_list_seed *x,**y; | |
105 y = &action_list_pointer; | |
106 x = *y; | |
107 while(x) { | |
108 if (!(strcmp(x->command,command)) && !(strcmp(x->terminal_name,term))) { | |
109 *y = x->next;//remove from the list | |
110 while(*y) y = &(*y)->next;//traverse through list till end | |
111 x->next = NULL; | |
112 break; | |
113 } | |
114 y = &(x)->next; | |
115 x = *y; | |
116 } | |
117 //create a new node | |
118 if (!x) { | |
119 x = xzalloc(sizeof(*x)); | |
120 x->command = xstrdup(command); | |
121 x->terminal_name = xstrdup(term); | |
122 } | |
123 x->action = action; | |
124 *y = x; | |
125 } | |
126 | |
127 static void inittab_parsing(void) | |
128 { | |
129 int i, fd, line_number = 0, token_count = 0; | |
130 char *p, *q, *extracted_token, *tty_name = NULL, *command = NULL, *tmp; | |
131 uint8_t action = 0; | |
132 char *act_name = "sysinit\0wait\0once\0respawn\0askfirst\0ctrlaltdel\0" | |
133 "shutdown\0restart\0"; | |
134 | |
135 fd = open("/etc/inittab", O_RDONLY); | |
136 if (fd < 0) { | |
137 error_msg("Unable to open /etc/inittab. Using Default inittab"); | |
138 add_new_action(SYSINIT, "/etc/init.d/rcS", ""); | |
139 add_new_action(RESPAWN, "/sbin/getty -n -l /bin/sh -L 115200 tty1 vt100", ""); | |
140 } else { | |
141 while((q = p = get_line(fd))) { //read single line from /etc/inittab | |
142 char *x; | |
143 if((x = strchr(p, '#'))) *x = '\0'; | |
144 line_number++; | |
145 token_count = 0; | |
146 action = 0; | |
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 = xmsprintf("%s",extracted_token); | |
154 else tty_name = xmsprintf("/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 } //while token | |
176 if (q) free(q); | |
177 if (token_count != 4) continue; | |
178 if (action) add_new_action(action, command, tty_name); | |
179 free(tty_name); | |
180 free(command); | |
181 }//while line | |
182 close(fd); | |
183 } | |
184 } | |
185 | |
186 static void run_command(char *command) | |
187 { | |
188 char *final_command[128]; | |
189 int hyphen = (command[0]=='-'); | |
190 | |
191 command = command + hyphen; | |
192 if (!strpbrk(command, "?<>'\";[]{}\\|=()*&^$!`~")) { | |
193 char *next_command; | |
194 char *extracted_command; | |
195 int x = 0; | |
196 next_command = strncpy(toybuf, command - hyphen, sizeof(toybuf)); | |
197 next_command[sizeof(toybuf) - 1] = toybuf[sizeof(toybuf) - 1 ] = '\0'; | |
198 command = next_command + hyphen; | |
199 while((extracted_command = strsep(&next_command," \t"))) { | |
200 if (*extracted_command) { | |
201 final_command[x] = extracted_command; | |
202 x++; | |
203 } | |
204 } | |
205 final_command[x] = NULL; | |
206 } else { | |
207 snprintf(toybuf, sizeof(toybuf), "exec %s", command); | |
208 command = "-/bin/sh"+1; | |
209 final_command[0] = (char*)("-/bin/sh"+!hyphen); | |
210 final_command[1] = (char*)"-c"; | |
211 final_command[2] = toybuf; | |
212 final_command[3] = NULL; | |
213 } | |
214 if (hyphen) ioctl(0, TIOCSCTTY, 0); | |
215 execvp(command, final_command); | |
216 error_msg("unable to run %s",command); | |
217 } | |
218 | |
219 //runs all same type of actions | |
220 static pid_t final_run(struct action_list_seed *x) | |
221 { | |
222 pid_t pid; | |
223 int fd; | |
224 sigset_t signal_set; | |
225 | |
226 sigfillset(&signal_set); | |
227 sigprocmask(SIG_BLOCK, &signal_set, NULL); | |
228 if (x->action & ASKFIRST) pid = fork(); | |
229 else pid = vfork(); | |
230 if (pid > 0) { | |
231 //parent process or error | |
232 //unblock the signals | |
233 sigfillset(&signal_set); | |
234 sigprocmask(SIG_UNBLOCK, &signal_set, NULL); | |
235 return pid; | |
236 } else if (pid < 0) perror_exit("fork fail"); | |
237 //new born child process | |
238 sigset_t signal_set_c; | |
239 sigfillset(&signal_set_c); | |
240 sigprocmask(SIG_UNBLOCK, &signal_set_c, NULL); | |
241 setsid();//new session | |
242 if (x->terminal_name[0]) { | |
243 close(0); | |
244 fd = open(x->terminal_name,(O_RDWR|O_NONBLOCK),0600); | |
245 if (fd != 0) { | |
246 error_msg("Unable to open %s,%s\n",x->terminal_name,strerror(errno)); | |
247 _exit(EXIT_FAILURE); | |
248 } else { | |
249 dup2(0,1); | |
250 dup2(0,2); | |
251 } | |
252 } | |
253 set_sane_term(); | |
254 run_command(x->command); | |
255 _exit(-1); | |
256 } | |
257 | |
258 static struct action_list_seed* mark_as_terminated_process(pid_t pid) | |
259 { | |
260 struct action_list_seed *x; | |
261 if (pid > 0) { | |
262 for (x = action_list_pointer; x; x = x->next) { | |
263 if (x->pid == pid) { | |
264 x->pid = 0; | |
265 return x; | |
266 } | |
267 } | |
268 } | |
269 return NULL; | |
270 } | |
271 | |
272 static void waitforpid(pid_t pid) | |
273 { | |
274 if (pid <= 0) return; | |
275 for(;;) { | |
276 pid_t y = wait(NULL); | |
277 mark_as_terminated_process(y); | |
278 if (kill(y,0)) break; | |
279 } | |
280 } | |
281 static void run_action_from_list(int action) | |
282 { | |
283 pid_t pid; | |
284 struct action_list_seed *x = action_list_pointer; | |
285 for (; x; x = x->next) { | |
286 if (!(x->action & action)) continue; | |
287 if (x->action & (SHUTDOWN|ONCE|SYSINIT|CTRLALTDEL|WAIT)) { | |
288 pid = final_run(x); | |
289 if (x->action & (SHUTDOWN|SYSINIT|CTRLALTDEL|WAIT)) waitforpid(pid); | |
290 } | |
291 if (x->action & (ASKFIRST|RESPAWN)) | |
292 if (!(x->pid)) x->pid = final_run(x); | |
293 } | |
294 } | |
295 | |
296 static void set_defualt(void) | |
297 { | |
298 sigset_t signal_set_c; | |
299 | |
300 signal(SIGUSR1,SIG_DFL); | |
301 signal(SIGUSR2,SIG_DFL); | |
302 signal(SIGTERM,SIG_DFL); | |
303 signal(SIGQUIT,SIG_DFL); | |
304 signal(SIGINT,SIG_DFL); | |
305 signal(SIGHUP,SIG_DFL); | |
306 signal(SIGTSTP,SIG_DFL); | |
307 signal(SIGSTOP,SIG_DFL); | |
308 sigfillset(&signal_set_c); | |
309 sigprocmask(SIG_UNBLOCK,&signal_set_c, NULL); | |
310 run_action_from_list(SHUTDOWN); | |
311 error_msg("The system is going down NOW!"); | |
312 kill(-1, SIGTERM); | |
313 error_msg("Sent SIGTERM to all processes"); | |
314 sync(); | |
315 sleep(1); | |
316 kill(-1,SIGKILL); | |
317 sync(); | |
318 } | |
319 | |
320 static void halt_poweroff_reboot_handler(int sig_no) | |
321 { | |
322 unsigned int reboot_magic_no = 0; | |
323 pid_t pid; | |
324 set_defualt(); | |
325 switch (sig_no) { | |
326 case SIGUSR1: | |
327 error_msg("Requesting system halt"); | |
328 reboot_magic_no=RB_HALT_SYSTEM; | |
329 break; | |
330 case SIGUSR2: | |
331 error_msg("Requesting system poweroff"); | |
332 reboot_magic_no=RB_POWER_OFF; | |
333 break; | |
334 case SIGTERM: | |
335 error_msg("Requesting system reboot"); | |
336 reboot_magic_no=RB_AUTOBOOT; | |
337 break; | |
338 default: | |
339 break; | |
340 } | |
341 sleep(1); | |
342 pid = vfork(); | |
343 if (pid == 0) { | |
344 reboot(reboot_magic_no); | |
345 _exit(EXIT_SUCCESS); | |
346 } | |
347 while(1) sleep(1); | |
348 } | |
349 static void restart_init_handler(int sig_no) | |
350 { | |
351 struct action_list_seed *x; | |
352 pid_t pid; | |
353 int fd; | |
354 for (x = action_list_pointer; x; x = x->next) { | |
355 if (!(x->action & RESTART)) continue; | |
356 | |
357 set_defualt(); | |
358 if (x->terminal_name[0]) { | |
359 close(0); | |
360 fd = open(x->terminal_name,(O_RDWR|O_NONBLOCK),0600); | |
361 if (fd != 0) { | |
362 error_msg("Unable to open %s,%s\n",x->terminal_name,strerror(errno)); | |
363 sleep(1); | |
364 pid = vfork(); | |
365 if (pid == 0) { | |
366 reboot(RB_HALT_SYSTEM); | |
367 _exit(EXIT_SUCCESS); | |
368 } | |
369 while(1) sleep(1); | |
370 } else { | |
371 dup2(0,1); | |
372 dup2(0,2); | |
373 set_sane_term(); | |
374 run_command(x->command); | |
375 } | |
376 } | |
377 } | |
378 } | |
379 | |
380 static void catch_signal(int sig_no) | |
381 { | |
382 caught_signal = sig_no; | |
383 error_msg("signal seen"); | |
384 } | |
385 | |
386 static void pause_handler(int sig_no) | |
387 { | |
388 int signal_backup,errno_backup; | |
389 pid_t pid; | |
390 errno_backup = errno; | |
391 signal_backup = caught_signal; | |
392 signal(SIGCONT, catch_signal); | |
393 while(1) { | |
394 if (caught_signal == SIGCONT) break; | |
395 do | |
396 pid = waitpid(-1,NULL,WNOHANG); | |
397 while((pid==-1) && (errno=EINTR)); | |
398 mark_as_terminated_process(pid); | |
399 sleep(1); | |
400 } | |
401 signal(SIGCONT,SIG_DFL); | |
402 errno = errno_backup; | |
403 caught_signal = signal_backup; | |
404 } | |
405 | |
406 static void assign_signal_handler(void) | |
407 { | |
408 struct sigaction sig_act; | |
409 signal(SIGUSR1, halt_poweroff_reboot_handler);//halt | |
410 signal(SIGUSR2, halt_poweroff_reboot_handler);//poweroff | |
411 signal(SIGTERM, halt_poweroff_reboot_handler);//reboot | |
412 signal(SIGQUIT, restart_init_handler);//restart init | |
413 memset(&sig_act, 0, sizeof(sig_act)); | |
414 sigfillset(&sig_act.sa_mask); | |
415 sigdelset(&sig_act.sa_mask, SIGCONT); | |
416 sig_act.sa_handler = pause_handler; | |
417 sigaction(SIGTSTP, &sig_act, NULL); | |
418 | |
419 memset(&sig_act, 0, sizeof(sig_act)); | |
420 sig_act.sa_handler = catch_signal; | |
421 sigaction(SIGINT, &sig_act, NULL); | |
422 sigaction(SIGHUP, &sig_act, NULL); | |
423 } | |
424 | |
425 static int check_if_pending_signals(void) | |
426 { | |
427 int signal_caught = 0; | |
428 while(1) { | |
429 int sig = caught_signal; | |
430 if (!sig) return signal_caught; | |
431 caught_signal = 0; | |
432 signal_caught = 1; | |
433 if (sig == SIGINT) run_action_from_list(CTRLALTDEL); | |
434 } | |
435 } | |
436 void init_main(void) | |
437 { | |
438 if (getpid() != 1) error_exit("Already running"); | |
439 xprintf("Started init\n"); | |
440 initialize_console(); | |
441 set_sane_term(); | |
442 | |
443 if (chdir("/")) perror_exit("Can't cd to /"); | |
444 setsid(); | |
445 | |
446 set_enviornment(); | |
447 inittab_parsing(); | |
448 assign_signal_handler(); | |
449 run_action_from_list(SYSINIT); | |
450 check_if_pending_signals(); | |
451 run_action_from_list(WAIT); | |
452 check_if_pending_signals(); | |
453 run_action_from_list(ONCE); | |
454 while(1) { | |
455 int suspected_WNOHANG; | |
456 suspected_WNOHANG = check_if_pending_signals(); | |
457 run_action_from_list(RESPAWN | ASKFIRST); | |
458 suspected_WNOHANG = suspected_WNOHANG|check_if_pending_signals(); | |
459 sleep(1);//let cpu breath | |
460 suspected_WNOHANG = suspected_WNOHANG|check_if_pending_signals(); | |
461 if (suspected_WNOHANG) suspected_WNOHANG=WNOHANG; | |
462 while(1) { | |
463 pid_t pid; | |
464 pid = waitpid(-1, NULL, suspected_WNOHANG); | |
465 if (pid <= 0) break; | |
466 mark_as_terminated_process(pid); | |
467 suspected_WNOHANG = WNOHANG; | |
468 } | |
469 } | |
470 } |