1/* vi: set sw=4 ts=4: */ 2/* 3 * Mini init implementation for busybox 4 * 5 * 6 * Copyright (C) 1995, 1996 by Bruce Perens <bruce@pixar.com>. 7 * Adjusted by so many folks, it's impossible to keep track. 8 * 9 * This program is free software; you can redistribute it and/or modify 10 * it under the terms of the GNU General Public License as published by 11 * the Free Software Foundation; either version 2 of the License, or 12 * (at your option) any later version. 13 * 14 * This program is distributed in the hope that it will be useful, 15 * but WITHOUT ANY WARRANTY; without even the implied warranty of 16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 17 * General Public License for more details. 18 * 19 * You should have received a copy of the GNU General Public License 20 * along with this program; if not, write to the Free Software 21 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 22 * 23 */ 24 25/* Turn this on to disable all the dangerous 26 rebooting stuff when debugging. 27#define DEBUG_INIT 28*/ 29 30#include <stdio.h> 31#include <stdlib.h> 32#include <errno.h> 33#include <paths.h> 34#include <signal.h> 35#include <stdarg.h> 36#include <string.h> 37#include <termios.h> 38#include <unistd.h> 39#include <limits.h> 40#include <sys/fcntl.h> 41#include <sys/ioctl.h> 42#include <sys/mount.h> 43#include <sys/types.h> 44#include <sys/wait.h> 45#include "busybox.h" 46#ifdef BB_SYSLOGD 47# include <sys/syslog.h> 48#endif 49 50 51/* From <linux/vt.h> */ 52struct vt_stat { 53 unsigned short v_active; /* active vt */ 54 unsigned short v_signal; /* signal to send */ 55 unsigned short v_state; /* vt bitmask */ 56}; 57static const int VT_GETSTATE = 0x5603; /* get global vt state info */ 58 59/* From <linux/serial.h> */ 60struct serial_struct { 61 int type; 62 int line; 63 int port; 64 int irq; 65 int flags; 66 int xmit_fifo_size; 67 int custom_divisor; 68 int baud_base; 69 unsigned short close_delay; 70 char reserved_char[2]; 71 int hub6; 72 unsigned short closing_wait; /* time to wait before closing */ 73 unsigned short closing_wait2; /* no longer used... */ 74 int reserved[4]; 75}; 76 77 78 79#ifndef RB_HALT_SYSTEM 80static const int RB_HALT_SYSTEM = 0xcdef0123; 81static const int RB_ENABLE_CAD = 0x89abcdef; 82static const int RB_DISABLE_CAD = 0; 83#define RB_POWER_OFF 0x4321fedc 84static const int RB_AUTOBOOT = 0x01234567; 85#endif 86 87#if (__GNU_LIBRARY__ > 5) || defined(__dietlibc__) 88 #include <sys/reboot.h> 89 #define init_reboot(magic) reboot(magic) 90#else 91 #define init_reboot(magic) reboot(0xfee1dead, 672274793, magic) 92#endif 93 94#ifndef _PATH_STDPATH 95#define _PATH_STDPATH "/usr/bin:/bin:/usr/sbin:/sbin" 96#endif 97 98 99#if defined BB_FEATURE_INIT_COREDUMPS 100/* 101 * When a file named CORE_ENABLE_FLAG_FILE exists, setrlimit is called 102 * before processes are spawned to set core file size as unlimited. 103 * This is for debugging only. Don't use this is production, unless 104 * you want core dumps lying about.... 105 */ 106#define CORE_ENABLE_FLAG_FILE "/.init_enable_core" 107#include <sys/resource.h> 108#include <sys/time.h> 109#endif 110 111#define KERNEL_VERSION(a,b,c) (((a) << 16) + ((b) << 8) + (c)) 112 113#if __GNU_LIBRARY__ > 5 114 #include <sys/kdaemon.h> 115#else 116 extern int bdflush (int func, long int data); 117#endif 118 119 120#define SHELL "/bin/sh" /* Default shell */ 121#define LOGIN_SHELL "-" SHELL /* Default login shell */ 122#define INITTAB "/etc/inittab" /* inittab file location */ 123#ifndef INIT_SCRIPT 124#define INIT_SCRIPT "/etc/init.d/rcS" /* Default sysinit script. */ 125#endif 126 127#define MAXENV 16 /* Number of env. vars */ 128//static const int MAXENV = 16; /* Number of env. vars */ 129static const int LOG = 0x1; 130static const int CONSOLE = 0x2; 131 132/* Allowed init action types */ 133typedef enum { 134 SYSINIT = 1, 135 RESPAWN, 136 ASKFIRST, 137 WAIT, 138 ONCE, 139 CTRLALTDEL, 140 SHUTDOWN 141} initActionEnum; 142 143/* A mapping between "inittab" action name strings and action type codes. */ 144typedef struct initActionType { 145 const char *name; 146 initActionEnum action; 147} initActionType; 148 149static const struct initActionType actions[] = { 150 {"sysinit", SYSINIT}, 151 {"respawn", RESPAWN}, 152 {"askfirst", ASKFIRST}, 153 {"wait", WAIT}, 154 {"once", ONCE}, 155 {"ctrlaltdel", CTRLALTDEL}, 156 {"shutdown", SHUTDOWN}, 157 {0, 0} 158}; 159 160/* Set up a linked list of initActions, to be read from inittab */ 161typedef struct initActionTag initAction; 162struct initActionTag { 163 pid_t pid; 164 char process[256]; 165 char console[256]; 166 initAction *nextPtr; 167 initActionEnum action; 168}; 169static initAction *initActionList = NULL; 170 171 172static char *secondConsole = VC_2; 173static char *thirdConsole = VC_3; 174static char *fourthConsole = VC_4; 175static char *log = VC_5; 176static int kernelVersion = 0; 177static char termType[32] = "TERM=linux"; 178static char console[32] = _PATH_CONSOLE; 179 180static void delete_initAction(initAction * action); 181 182static void loop_forever() 183{ 184 while (1) 185 sleep (1); 186} 187 188/* Print a message to the specified device. 189 * Device may be bitwise-or'd from LOG | CONSOLE */ 190static void message(int device, char *fmt, ...) 191 __attribute__ ((format (printf, 2, 3))); 192static void message(int device, char *fmt, ...) 193{ 194 va_list arguments; 195 int fd; 196 197#ifdef BB_SYSLOGD 198 199 /* Log the message to syslogd */ 200 if (device & LOG) { 201 char msg[1024]; 202 203 va_start(arguments, fmt); 204 vsnprintf(msg, sizeof(msg), fmt, arguments); 205 va_end(arguments); 206 openlog(applet_name, 0, LOG_USER); 207 syslog(LOG_USER|LOG_INFO, msg); 208 closelog(); 209 } 210#else 211 static int log_fd = -1; 212 213 /* Take full control of the log tty, and never close it. 214 * It's mine, all mine! Muhahahaha! */ 215 if (log_fd < 0) { 216 if (log == NULL) { 217 /* don't even try to log, because there is no such console */ 218 log_fd = -2; 219 /* log to main console instead */ 220 device = CONSOLE; 221 } else if ((log_fd = device_open(log, O_RDWR|O_NDELAY)) < 0) { 222 log_fd = -2; 223 fprintf(stderr, "Bummer, can't write to log on %s!\r\n", log); 224 log = NULL; 225 device = CONSOLE; 226 } 227 } 228 if ((device & LOG) && (log_fd >= 0)) { 229 va_start(arguments, fmt); 230 vdprintf(log_fd, fmt, arguments); 231 va_end(arguments); 232 } 233#endif 234 235 if (device & CONSOLE) { 236 /* Always send console messages to /dev/console so people will see them. */ 237 if ( 238 (fd = 239 device_open(_PATH_CONSOLE, 240 O_WRONLY | O_NOCTTY | O_NDELAY)) >= 0) { 241 va_start(arguments, fmt); 242 vdprintf(fd, fmt, arguments); 243 va_end(arguments); 244 close(fd); 245 } else { 246 fprintf(stderr, "Bummer, can't print: "); 247 va_start(arguments, fmt); 248 vfprintf(stderr, fmt, arguments); 249 va_end(arguments); 250 } 251 } 252} 253 254/* Set terminal settings to reasonable defaults */ 255static void set_term(int fd) 256{ 257 struct termios tty; 258 259 tcgetattr(fd, &tty); 260 261 /* set control chars */ 262 tty.c_cc[VINTR] = 3; /* C-c */ 263 tty.c_cc[VQUIT] = 28; /* C-\ */ 264 tty.c_cc[VERASE] = 127; /* C-? */ 265 tty.c_cc[VKILL] = 21; /* C-u */ 266 tty.c_cc[VEOF] = 4; /* C-d */ 267 tty.c_cc[VSTART] = 17; /* C-q */ 268 tty.c_cc[VSTOP] = 19; /* C-s */ 269 tty.c_cc[VSUSP] = 26; /* C-z */ 270 271 /* use line dicipline 0 */ 272 tty.c_line = 0; 273 274 /* Make it be sane */ 275 tty.c_cflag &= CBAUD|CBAUDEX|CSIZE|CSTOPB|PARENB|PARODD; 276 tty.c_cflag |= CREAD|HUPCL|CLOCAL; 277 278 279 /* input modes */ 280 tty.c_iflag = ICRNL | IXON | IXOFF; 281 282 /* output modes */ 283 tty.c_oflag = OPOST | ONLCR; 284 285 /* local modes */ 286 tty.c_lflag = 287 ISIG | ICANON | ECHO | ECHOE | ECHOK | ECHOCTL | ECHOKE | IEXTEN; 288 289 tcsetattr(fd, TCSANOW, &tty); 290} 291 292/* How much memory does this machine have? 293 Units are kBytes to avoid overflow on 4GB machines */ 294static int check_free_memory() 295{ 296 struct sysinfo info; 297 unsigned int result, u, s=10; 298 299 if (sysinfo(&info) != 0) { 300 perror_msg("Error checking free memory"); 301 return -1; 302 } 303 304 /* Kernels 2.0.x and 2.2.x return info.mem_unit==0 with values in bytes. 305 * Kernels 2.4.0 return info.mem_unit in bytes. */ 306 u = info.mem_unit; 307 if (u==0) u=1; 308 while ( (u&1) == 0 && s > 0 ) { u>>=1; s--; } 309 result = (info.totalram>>s) + (info.totalswap>>s); 310 result = result*u; 311 if (result < 0) result = INT_MAX; 312 return result; 313} 314 315static void console_init() 316{ 317 int fd; 318 int tried_devcons = 0; 319 int tried_vtprimary = 0; 320 struct vt_stat vt; 321 struct serial_struct sr; 322 char *s; 323 324 if ((s = getenv("TERM")) != NULL) { 325 snprintf(termType, sizeof(termType) - 1, "TERM=%s", s); 326 } 327 328 if ((s = getenv("CONSOLE")) != NULL) { 329 safe_strncpy(console, s, sizeof(console)); 330 } 331#if #cpu(sparc) 332 /* sparc kernel supports console=tty[ab] parameter which is also 333 * passed to init, so catch it here */ 334 else if ((s = getenv("console")) != NULL) { 335 /* remap tty[ab] to /dev/ttyS[01] */ 336 if (strcmp(s, "ttya") == 0) 337 safe_strncpy(console, SC_0, sizeof(console)); 338 else if (strcmp(s, "ttyb") == 0) 339 safe_strncpy(console, SC_1, sizeof(console)); 340 } 341#endif 342 else { 343 /* 2.2 kernels: identify the real console backend and try to use it */ 344 if (ioctl(0, TIOCGSERIAL, &sr) == 0) { 345 /* this is a serial console */ 346 snprintf(console, sizeof(console) - 1, SC_FORMAT, sr.line); 347 } else if (ioctl(0, VT_GETSTATE, &vt) == 0) { 348 /* this is linux virtual tty */ 349 snprintf(console, sizeof(console) - 1, VC_FORMAT, vt.v_active); 350 } else { 351 safe_strncpy(console, _PATH_CONSOLE, sizeof(console)); 352 tried_devcons++; 353 } 354 } 355 356 while ((fd = open(console, O_RDONLY | O_NONBLOCK)) < 0) { 357 /* Can't open selected console -- try /dev/console */ 358 if (!tried_devcons) { 359 tried_devcons++; 360 safe_strncpy(console, _PATH_CONSOLE, sizeof(console)); 361 continue; 362 } 363 /* Can't open selected console -- try vt1 */ 364 if (!tried_vtprimary) { 365 tried_vtprimary++; 366 safe_strncpy(console, VC_1, sizeof(console)); 367 continue; 368 } 369 break; 370 } 371 if (fd < 0) { 372 /* Perhaps we should panic here? */ 373 safe_strncpy(console, "/dev/null", sizeof(console)); 374 } else { 375 /* check for serial console and disable logging to tty5 & running a 376 * shell to tty2-4 */ 377 if (ioctl(0, TIOCGSERIAL, &sr) == 0) { 378 log = NULL; 379 secondConsole = NULL; 380 thirdConsole = NULL; 381 fourthConsole = NULL; 382 /* Force the TERM setting to vt102 for serial console -- 383 * iff TERM is set to linux (the default) */ 384 if (strcmp( termType, "TERM=linux" ) == 0) 385 safe_strncpy(termType, "TERM=vt102", sizeof(termType)); 386 message(LOG | CONSOLE, 387 "serial console detected. Disabling virtual terminals.\r\n"); 388 } 389 close(fd); 390 } 391 message(LOG, "console=%s\n", console); 392} 393 394static void fixup_argv(int argc, char **argv, char *new_argv0) 395{ 396 int len; 397 /* Fix up argv[0] to be certain we claim to be init */ 398 len = strlen(argv[0]); 399 memset(argv[0], 0, len); 400 strncpy(argv[0], new_argv0, len); 401 402 /* Wipe argv[1]-argv[N] so they don't clutter the ps listing */ 403 len = 1; 404 while (argc > len) { 405 memset(argv[len], 0, strlen(argv[len])); 406 len++; 407 } 408} 409 410 411static pid_t run(char *command, char *terminal, int get_enter) 412{ 413 int i, j; 414 int fd; 415 pid_t pid; 416 char *tmpCmd, *s; 417 char *cmd[255], *cmdpath; 418 char buf[255]; 419 struct stat sb; 420 static const char press_enter[] = 421 422#ifdef CUSTOMIZED_BANNER 423#include CUSTOMIZED_BANNER 424#endif 425 426 "\nPlease press Enter to activate this console. "; 427 char *environment[MAXENV+1] = { 428 termType, 429 "HOME=/", 430 "PATH=/usr/bin:/bin:/usr/sbin:/sbin", 431 "SHELL=" SHELL, 432 "USER=root", 433 NULL 434 }; 435 436 /* inherit environment to the child, merging our values -andy */ 437 for (i=0; environ[i]; i++) { 438 for (j=0; environment[j]; j++) { 439 s = strchr(environment[j], '='); 440 if (!strncmp(environ[i], environment[j], s - environment[j])) 441 break; 442 } 443 if (!environment[j]) { 444 environment[j++] = environ[i]; 445 environment[j] = NULL; 446 } 447 } 448 449 if ((pid = fork()) == 0) { 450 /* Clean up */ 451 ioctl(0, TIOCNOTTY, 0); 452 close(0); 453 close(1); 454 close(2); 455 setsid(); 456 457 /* Reset signal handlers set for parent process */ 458 signal(SIGUSR1, SIG_DFL); 459 signal(SIGUSR2, SIG_DFL); 460 signal(SIGINT, SIG_DFL); 461 signal(SIGTERM, SIG_DFL); 462 signal(SIGHUP, SIG_DFL); 463 464 if ((fd = device_open(terminal, O_RDWR)) < 0) { 465 if (stat(terminal, &sb) != 0) { 466 message(LOG | CONSOLE, "device '%s' does not exist.\n", 467 terminal); 468 exit(1); 469 } 470 message(LOG | CONSOLE, "Bummer, can't open %s\r\n", terminal); 471 exit(1); 472 } 473 dup2(fd, 0); 474 dup2(fd, 1); 475 dup2(fd, 2); 476 ioctl(0, TIOCSCTTY, 1); 477 tcsetpgrp(0, getpgrp()); 478 set_term(0); 479 480 /* See if any special /bin/sh requiring characters are present */ 481 if (strpbrk(command, "~`!$^&*()=|\\{}[];\"'<>?") != NULL) { 482 cmd[0] = SHELL; 483 cmd[1] = "-c"; 484 strcpy(buf, "exec "); 485 strncat(buf, command, sizeof(buf) - strlen(buf) - 1); 486 cmd[2] = buf; 487 cmd[3] = NULL; 488 } else { 489 /* Convert command (char*) into cmd (char**, one word per string) */ 490 for (tmpCmd = command, i = 0; 491 (tmpCmd = strsep(&command, " \t")) != NULL;) { 492 if (*tmpCmd != '\0') { 493 cmd[i] = tmpCmd; 494 tmpCmd++; 495 i++; 496 } 497 } 498 cmd[i] = NULL; 499 } 500 501 cmdpath = cmd[0]; 502 503 /* 504 Interactive shells want to see a dash in argv[0]. This 505 typically is handled by login, argv will be setup this 506 way if a dash appears at the front of the command path 507 (like "-/bin/sh"). 508 */ 509 510 if (*cmdpath == '-') { 511 512 /* skip over the dash */ 513 ++cmdpath; 514 515 /* find the last component in the command pathname */ 516 s = get_last_path_component(cmdpath); 517 518 /* make a new argv[0] */ 519 if ((cmd[0] = malloc(strlen(s)+2)) == NULL) { 520 message(LOG | CONSOLE, "malloc failed"); 521 cmd[0] = cmdpath; 522 } else { 523 cmd[0][0] = '-'; 524 strcpy(cmd[0]+1, s); 525 } 526 } 527 528 if (get_enter == TRUE) { 529 /* 530 * Save memory by not exec-ing anything large (like a shell) 531 * before the user wants it. This is critical if swap is not 532 * enabled and the system has low memory. Generally this will 533 * be run on the second virtual console, and the first will 534 * be allowed to start a shell or whatever an init script 535 * specifies. 536 */ 537#ifdef DEBUG_INIT 538 message(LOG, "Waiting for enter to start '%s' (pid %d, console %s)\r\n", 539 cmd[0], getpid(), terminal); 540#endif 541 write(fileno(stdout), press_enter, sizeof(press_enter) - 1); 542 getc(stdin); 543 } 544 545#ifdef DEBUG_INIT 546 /* Log the process name and args */ 547 message(LOG, "Starting pid %d, console %s: '%s'\r\n", 548 getpid(), terminal, command); 549#endif 550 551#if defined BB_FEATURE_INIT_COREDUMPS 552 if (stat (CORE_ENABLE_FLAG_FILE, &sb) == 0) { 553 struct rlimit limit; 554 limit.rlim_cur = RLIM_INFINITY; 555 limit.rlim_max = RLIM_INFINITY; 556 setrlimit(RLIMIT_CORE, &limit); 557 } 558#endif 559 560 /* Now run it. The new program will take over this PID, 561 * so nothing further in init.c should be run. */ 562 execve(cmdpath, cmd, environment); 563 564 /* We're still here? Some error happened. */ 565 message(LOG | CONSOLE, "Bummer, could not run '%s': %s\n", cmdpath, 566 strerror(errno)); 567 exit(-1); 568 } 569 return pid; 570} 571 572static int waitfor(char *command, char *terminal, int get_enter) 573{ 574 int status, wpid; 575 int pid = run(command, terminal, get_enter); 576 577 while (1) { 578 wpid = wait(&status); 579 if (wpid > 0 && wpid != pid) { 580 continue; 581 } 582 if (wpid == pid) 583 break; 584 } 585 return wpid; 586} 587 588/* Make sure there is enough memory to do something useful. * 589 * Calls "swapon -a" if needed so be sure /etc/fstab is present... */ 590static void check_memory() 591{ 592 struct stat statBuf; 593 594 if (check_free_memory() > 1000) 595 return; 596 597 if (stat("/etc/fstab", &statBuf) == 0) { 598 /* swapon -a requires /proc typically */ 599 waitfor("mount proc /proc -t proc", console, FALSE); 600 /* Try to turn on swap */ 601 waitfor("swapon -a", console, FALSE); 602 if (check_free_memory() < 1000) 603 goto goodnight; 604 } else 605 goto goodnight; 606 return; 607 608 goodnight: 609 message(CONSOLE, 610 "Sorry, your computer does not have enough memory.\r\n"); 611 loop_forever(); 612} 613 614/* Run all commands to be run right before halt/reboot */ 615static void run_actions(initActionEnum action) 616{ 617 initAction *a, *tmp; 618 for (a = initActionList; a; a = tmp) { 619 tmp = a->nextPtr; 620 if (a->action == action) { 621 waitfor(a->process, a->console, FALSE); 622 // delete_initAction(a); 623 } 624 } 625} 626 627 628#ifndef DEBUG_INIT 629static void shutdown_system(void) 630{ 631 632 /* first disable our SIGHUP signal */ 633 signal(SIGHUP, SIG_DFL); 634 635 /* Allow Ctrl-Alt-Del to reboot system. */ 636 init_reboot(RB_ENABLE_CAD); 637 638 message(CONSOLE|LOG, "\r\nThe system is going down NOW !!\r\n"); 639 sync(); 640 641 /* Send signals to every process _except_ pid 1 */ 642 message(CONSOLE|LOG, "Sending SIGTERM to all processes.\r\n"); 643 kill(-1, SIGTERM); 644 sleep(1); 645 sync(); 646 647 message(CONSOLE|LOG, "Sending SIGKILL to all processes.\r\n"); 648 kill(-1, SIGKILL); 649 sleep(1); 650 651 /* run everything to be run at "shutdown" */ 652 run_actions(SHUTDOWN); 653 654 sync(); 655 if (kernelVersion > 0 && kernelVersion <= KERNEL_VERSION(2,2,11)) { 656 /* bdflush, kupdate not needed for kernels >2.2.11 */ 657 bdflush(1, 0); 658 sync(); 659 } 660} 661 662static void halt_signal(int sig) 663{ 664 shutdown_system(); 665 message(CONSOLE|LOG, 666 "The system is halted. Press %s or turn off power\r\n", 667 (secondConsole == NULL) /* serial console */ 668 ? "Reset" : "CTRL-ALT-DEL"); 669 sync(); 670 671 /* allow time for last message to reach serial console */ 672 sleep(2); 673 674 if (sig == SIGUSR2 && kernelVersion >= KERNEL_VERSION(2,2,0)) 675 init_reboot(RB_POWER_OFF); 676 else 677 init_reboot(RB_HALT_SYSTEM); 678 679 loop_forever(); 680} 681 682static void reboot_signal(int sig) 683{ 684 shutdown_system(); 685 message(CONSOLE|LOG, "Please stand by while rebooting the system.\r\n"); 686 sync(); 687 688 /* allow time for last message to reach serial console */ 689 sleep(2); 690 691 init_reboot(RB_AUTOBOOT); 692 693 loop_forever(); 694} 695 696static void ctrlaltdel_signal(int sig) 697{ 698 run_actions(CTRLALTDEL); 699} 700 701#endif /* ! DEBUG_INIT */ 702 703static void new_initAction(initActionEnum action, char *process, char *cons) 704{ 705 initAction *newAction; 706 707 if (*cons == '\0') 708 cons = console; 709 710 /* If BusyBox detects that a serial console is in use, 711 * then entries not refering to the console or null devices will _not_ be run. 712 * The exception to this rule is the null device. 713 */ 714 if (secondConsole == NULL && strcmp(cons, console) 715 && strcmp(cons, "/dev/null")) 716 return; 717 718 newAction = calloc((size_t) (1), sizeof(initAction)); 719 if (!newAction) { 720 message(LOG | CONSOLE, "Memory allocation failure\n"); 721 loop_forever(); 722 } 723 newAction->nextPtr = initActionList; 724 initActionList = newAction; 725 strncpy(newAction->process, process, 255); 726 newAction->action = action; 727 strncpy(newAction->console, cons, 255); 728 newAction->pid = 0; 729// message(LOG|CONSOLE, "process='%s' action='%d' console='%s'\n", 730// newAction->process, newAction->action, newAction->console); 731} 732 733static void delete_initAction(initAction * action) 734{ 735 initAction *a, *b = NULL; 736 737 for (a = initActionList; a; b = a, a = a->nextPtr) { 738 if (a == action) { 739 if (b == NULL) { 740 initActionList = a->nextPtr; 741 } else { 742 b->nextPtr = a->nextPtr; 743 } 744 free(a); 745 break; 746 } 747 } 748} 749 750/* NOTE that if BB_FEATURE_USE_INITTAB is NOT defined, 751 * then parse_inittab() simply adds in some default 752 * actions(i.e., runs INIT_SCRIPT and then starts a pair 753 * of "askfirst" shells). If BB_FEATURE_USE_INITTAB 754 * _is_ defined, but /etc/inittab is missing, this 755 * results in the same set of default behaviors. 756 * */ 757static void parse_inittab(void) 758{ 759#ifdef BB_FEATURE_USE_INITTAB 760 FILE *file; 761 char buf[256], lineAsRead[256], tmpConsole[256]; 762 char *id, *runlev, *action, *process, *eol; 763 const struct initActionType *a = actions; 764 int foundIt; 765 766 767 file = fopen(INITTAB, "r"); 768 if (file == NULL) { 769 /* No inittab file -- set up some default behavior */ 770#endif 771 /* Reboot on Ctrl-Alt-Del */ 772 new_initAction(CTRLALTDEL, "/sbin/reboot", console); 773 /* Swapoff on halt/reboot */ 774 new_initAction(SHUTDOWN, "/sbin/swapoff -a", console); 775 /* Umount all filesystems on halt/reboot */ 776 new_initAction(SHUTDOWN, "/bin/umount -a -r", console); 777 /* Askfirst shell on tty1 */ 778 new_initAction(ASKFIRST, LOGIN_SHELL, console); 779 /* Askfirst shell on tty2 */ 780 if (secondConsole != NULL) 781 new_initAction(ASKFIRST, LOGIN_SHELL, secondConsole); 782 /* Askfirst shell on tty3 */ 783 if (thirdConsole != NULL) 784 new_initAction(ASKFIRST, LOGIN_SHELL, thirdConsole); 785 /* Askfirst shell on tty4 */ 786 if (fourthConsole != NULL) 787 new_initAction(ASKFIRST, LOGIN_SHELL, fourthConsole); 788 /* sysinit */ 789 new_initAction(SYSINIT, INIT_SCRIPT, console); 790 791 return; 792#ifdef BB_FEATURE_USE_INITTAB 793 } 794 795 while (fgets(buf, 255, file) != NULL) { 796 foundIt = FALSE; 797 /* Skip leading spaces */ 798 for (id = buf; *id == ' ' || *id == '\t'; id++); 799 800 /* Skip the line if it's a comment */ 801 if (*id == '#' || *id == '\n') 802 continue; 803 804 /* Trim the trailing \n */ 805 eol = strrchr(id, '\n'); 806 if (eol != NULL) 807 *eol = '\0'; 808 809 /* Keep a copy around for posterity's sake (and error msgs) */ 810 strcpy(lineAsRead, buf); 811 812 /* Separate the ID field from the runlevels */ 813 runlev = strchr(id, ':'); 814 if (runlev == NULL || *(runlev + 1) == '\0') { 815 message(LOG | CONSOLE, "Bad inittab entry: %s\n", lineAsRead); 816 continue; 817 } else { 818 *runlev = '\0'; 819 ++runlev; 820 } 821 822 /* Separate the runlevels from the action */ 823 action = strchr(runlev, ':'); 824 if (action == NULL || *(action + 1) == '\0') { 825 message(LOG | CONSOLE, "Bad inittab entry: %s\n", lineAsRead); 826 continue; 827 } else { 828 *action = '\0'; 829 ++action; 830 } 831 832 /* Separate the action from the process */ 833 process = strchr(action, ':'); 834 if (process == NULL || *(process + 1) == '\0') { 835 message(LOG | CONSOLE, "Bad inittab entry: %s\n", lineAsRead); 836 continue; 837 } else { 838 *process = '\0'; 839 ++process; 840 } 841 842 /* Ok, now process it */ 843 a = actions; 844 while (a->name != 0) { 845 if (strcmp(a->name, action) == 0) { 846 if (*id != '\0') { 847 strcpy(tmpConsole, "/dev/"); 848 strncat(tmpConsole, id, 200); 849 id = tmpConsole; 850 } 851 new_initAction(a->action, process, id); 852 foundIt = TRUE; 853 } 854 a++; 855 } 856 if (foundIt == TRUE) 857 continue; 858 else { 859 /* Choke on an unknown action */ 860 message(LOG | CONSOLE, "Bad inittab entry: %s\n", lineAsRead); 861 } 862 } 863 return; 864#endif /* BB_FEATURE_USE_INITTAB */ 865} 866 867 868 869extern int init_main(int argc, char **argv) 870{ 871 initAction *a, *tmp; 872 pid_t wpid; 873 int status; 874 875#ifndef DEBUG_INIT 876 /* Expect to be invoked as init with PID=1 or be invoked as linuxrc */ 877 if (getpid() != 1 878#ifdef BB_FEATURE_LINUXRC 879 && strstr(applet_name, "linuxrc") == NULL 880#endif 881 ) 882 { 883 show_usage(); 884 } 885 /* Set up sig handlers -- be sure to 886 * clear all of these in run() */ 887 signal(SIGUSR1, halt_signal); 888 signal(SIGUSR2, halt_signal); 889 signal(SIGINT, ctrlaltdel_signal); 890 signal(SIGTERM, reboot_signal); 891 892 /* Turn off rebooting via CTL-ALT-DEL -- we get a 893 * SIGINT on CAD so we can shut things down gracefully... */ 894 init_reboot(RB_DISABLE_CAD); 895#endif 896 897 /* Figure out what kernel this is running */ 898 kernelVersion = get_kernel_revision(); 899 900 /* Figure out where the default console should be */ 901 console_init(); 902 903 /* Close whatever files are open, and reset the console. */ 904 close(0); 905 close(1); 906 close(2); 907 set_term(0); 908 chdir("/"); 909 setsid(); 910 911 /* Make sure PATH is set to something sane */ 912 putenv("PATH="_PATH_STDPATH); 913 914 /* Hello world */ 915#ifndef DEBUG_INIT 916 message( 917#if ! defined BB_FEATURE_EXTRA_QUIET 918 CONSOLE| 919#endif 920 LOG, 921 "init started: %s\r\n", full_version); 922#else 923 message( 924#if ! defined BB_FEATURE_EXTRA_QUIET 925 CONSOLE| 926#endif 927 LOG, 928 "init(%d) started: %s\r\n", getpid(), full_version); 929#endif 930 931 932 /* Make sure there is enough memory to do something useful. */ 933 check_memory(); 934 935 /* Check if we are supposed to be in single user mode */ 936 if (argc > 1 && (!strcmp(argv[1], "single") || 937 !strcmp(argv[1], "-s") || !strcmp(argv[1], "1"))) { 938 /* Ask first then start a shell on tty2-4 */ 939 if (secondConsole != NULL) 940 new_initAction(ASKFIRST, LOGIN_SHELL, secondConsole); 941 if (thirdConsole != NULL) 942 new_initAction(ASKFIRST, LOGIN_SHELL, thirdConsole); 943 if (fourthConsole != NULL) 944 new_initAction(ASKFIRST, LOGIN_SHELL, fourthConsole); 945 /* Start a shell on tty1 */ 946 new_initAction(RESPAWN, LOGIN_SHELL, console); 947 } else { 948 /* Not in single user mode -- see what inittab says */ 949 950 /* NOTE that if BB_FEATURE_USE_INITTAB is NOT defined, 951 * then parse_inittab() simply adds in some default 952 * actions(i.e., runs INIT_SCRIPT and then starts a pair 953 * of "askfirst" shells */ 954 parse_inittab(); 955 } 956 957 /* Make the command line just say "init" -- thats all, nothing else */ 958 fixup_argv(argc, argv, "init"); 959 960 /* Now run everything that needs to be run */ 961 962 /* First run the sysinit command */ 963 for (a = initActionList; a; a = tmp) { 964 tmp = a->nextPtr; 965 if (a->action == SYSINIT) { 966 waitfor(a->process, a->console, FALSE); 967 /* Now remove the "sysinit" entry from the list */ 968 delete_initAction(a); 969 } 970 } 971 /* Next run anything that wants to block */ 972 for (a = initActionList; a; a = tmp) { 973 tmp = a->nextPtr; 974 if (a->action == WAIT) { 975 waitfor(a->process, a->console, FALSE); 976 /* Now remove the "wait" entry from the list */ 977 delete_initAction(a); 978 } 979 } 980 /* Next run anything to be run only once */ 981 for (a = initActionList; a; a = tmp) { 982 tmp = a->nextPtr; 983 if (a->action == ONCE) { 984 run(a->process, a->console, FALSE); 985 /* Now remove the "once" entry from the list */ 986 delete_initAction(a); 987 } 988 } 989 /* If there is nothing else to do, stop */ 990 if (initActionList == NULL) { 991 message(LOG | CONSOLE, 992 "No more tasks for init -- sleeping forever.\n"); 993 loop_forever(); 994 } 995 996 /* Now run the looping stuff for the rest of forever */ 997 while (1) { 998 for (a = initActionList; a; a = a->nextPtr) { 999 /* Only run stuff with pid==0. If they have 1000 * a pid, that means they are still running */ 1001 if (a->pid == 0) { 1002 switch (a->action) { 1003 case RESPAWN: 1004 /* run the respawn stuff */ 1005 a->pid = run(a->process, a->console, FALSE); 1006 break; 1007 case ASKFIRST: 1008 /* run the askfirst stuff */ 1009 a->pid = run(a->process, a->console, TRUE); 1010 break; 1011 /* silence the compiler's incessant whining */ 1012 default: 1013 break; 1014 } 1015 } 1016 } 1017 /* Wait for a child process to exit */ 1018 wpid = wait(&status); 1019 if (wpid > 0) { 1020 /* Find out who died and clean up their corpse */ 1021 for (a = initActionList; a; a = a->nextPtr) { 1022 if (a->pid == wpid) { 1023 a->pid = 0; 1024 message(LOG, 1025 "Process '%s' (pid %d) exited. Scheduling it for restart.\n", 1026 a->process, wpid); 1027 } 1028 } 1029 } 1030 sleep(1); 1031 } 1032} 1033 1034/* 1035Local Variables: 1036c-file-style: "linux" 1037c-basic-offset: 4 1038tab-width: 4 1039End: 1040*/ 1041