1 /* init.c - init program. 2 * 3 * Copyright 2012 Harvind Singh <harvindsingh1981 (at) gmail.com> 4 * Copyright 2013 Kyungwan Han <asura321 (at) 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 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 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 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 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 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 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 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 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 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 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 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 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 405 static void catch_signal(int sig_no) 406 { 407 caught_signal = sig_no; 408 error_msg("signal seen"); 409 } 410 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 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 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