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 8USE_INIT(NEWTOY(init, "", TOYFLAG_SBIN)) 9 10config 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 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//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 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; //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 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; //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 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 } //while token 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 // Remove all inactive actions, then reload /etc/inittab 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//runs all same type of actions 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 //parent process or error 252 //unblock the signals 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 //new born child process 264 sigset_t signal_set_c; 265 sigfillset(&signal_set_c); 266 sigprocmask(SIG_UNBLOCK, &signal_set_c, NULL); 267 setsid(); //new session 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 sigatexit(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);//halt 477 xsignal(SIGUSR2, halt_poweroff_reboot_handler);//poweroff 478 xsignal(SIGTERM, halt_poweroff_reboot_handler);//reboot 479 xsignal(SIGQUIT, restart_init_handler);//restart init 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);//let cpu breath 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