📄 init.c
字号:
int quit = 0; /* * First, try to create /dev/initctl if not present. */ if (stat(INIT_FIFO, &st2) < 0 && errno == ENOENT) (void)mkfifo(INIT_FIFO, 0600); /* * If /dev/initctl is open, stat the file to see if it * is still the _same_ inode. */ if (pipe_fd >= 0) { fstat(pipe_fd, &st); if (stat(INIT_FIFO, &st2) < 0 || st.st_dev != st2.st_dev || st.st_ino != st2.st_ino) { close(pipe_fd); pipe_fd = -1; } } /* * Now finally try to open /dev/initctl */ if (pipe_fd < 0) { if ((pipe_fd = open(INIT_FIFO, O_RDWR|O_NONBLOCK)) >= 0) { fstat(pipe_fd, &st); if (!S_ISFIFO(st.st_mode)) { initlog(L_VB, "%s is not a fifo", INIT_FIFO); close(pipe_fd); pipe_fd = -1; } } if (pipe_fd >= 0) { /* * Don't use fd's 0, 1 or 2. */ (void) dup2(pipe_fd, PIPE_FD); close(pipe_fd); pipe_fd = PIPE_FD; /* * Return to caller - we'll be back later. */ } } /* Wait for data to appear, _if_ the pipe was opened. */ if (pipe_fd >= 0) while(!quit) { /* Do select, return on EINTR. */ FD_ZERO(&fds); FD_SET(pipe_fd, &fds); tv.tv_sec = 5; tv.tv_usec = 0; n = select(pipe_fd + 1, &fds, NULL, NULL, &tv); if (n <= 0) { if (n == 0 || errno == EINTR) return; continue; } /* Read the data, return on EINTR. */ n = read(pipe_fd, &request, sizeof(request)); if (n == 0) { /* * End of file. This can't happen under Linux (because * the pipe is opened O_RDWR - see select() in the * kernel) but you never know... */ close(pipe_fd); pipe_fd = -1; return; } if (n <= 0) { if (errno == EINTR) return; initlog(L_VB, "error reading initrequest"); continue; } /* * This is a convenient point to also try to * find the console device or check if it changed. */ console_init(); /* * Process request. */ if (request.magic != INIT_MAGIC || n != sizeof(request)) { initlog(L_VB, "got bogus initrequest"); continue; } switch(request.cmd) { case INIT_CMD_RUNLVL: sltime = request.sleeptime; fifo_new_level(request.runlevel); quit = 1; break; case INIT_CMD_POWERFAIL: sltime = request.sleeptime; do_power_fail('F'); quit = 1; break; case INIT_CMD_POWERFAILNOW: sltime = request.sleeptime; do_power_fail('L'); quit = 1; break; case INIT_CMD_POWEROK: sltime = request.sleeptime; do_power_fail('O'); quit = 1; break; case INIT_CMD_SETENV: initcmd_setenv(request.i.data, sizeof(request.i.data)); break; case INIT_CMD_CHANGECONS: if (user_console) { free(user_console); user_console = NULL; } if (!request.i.bsd.reserved[0]) user_console = NULL; else user_console = strdup(request.i.bsd.reserved); console_init(); quit = 1; break; default: initlog(L_VB, "got unimplemented initrequest."); break; } } /* * We come here if the pipe couldn't be opened. */ if (pipe_fd < 0) pause();}/* * This function is used in the transition * sysinit (-> single user) boot -> multi-user. */void boot_transitions(){ CHILD *ch; static int newlevel = 0; static int warn = 1; int loglevel; int oldlevel; /* Check if there is something to wait for! */ for( ch = family; ch; ch = ch->next ) if ((ch->flags & RUNNING) && ch->action != BOOT) break; if (ch == NULL) { /* No processes left in this level, proceed to next level. */ loglevel = -1; oldlevel = 'N'; switch(runlevel) { case '#': /* SYSINIT -> BOOT */ INITDBG(L_VB, "SYSINIT -> BOOT"); /* Write a boot record. */ wrote_utmp_reboot = 0; wrote_wtmp_reboot = 0; write_utmp_wtmp("reboot", "~~", 0, BOOT_TIME, "~"); /* Get our run level */ newlevel = dfl_level ? dfl_level : get_init_default(); if (newlevel == 'S') { runlevel = newlevel; /* Not really 'S' but show anyway. */ setproctitle("init [S]"); } else runlevel = '*'; break; case '*': /* BOOT -> NORMAL */ INITDBG(L_VB, "BOOT -> NORMAL"); if (runlevel != newlevel) loglevel = newlevel; runlevel = newlevel; did_boot = 1; warn = 1; break; case 'S': /* Ended SU mode */ case 's': INITDBG(L_VB, "END SU MODE"); newlevel = get_init_default(); if (!did_boot && newlevel != 'S') runlevel = '*'; else { if (runlevel != newlevel) loglevel = newlevel; runlevel = newlevel; oldlevel = 'S'; } warn = 1; for(ch = family; ch; ch = ch->next) if (strcmp(ch->rlevel, "S") == 0) ch->flags &= ~(FAILING|WAITING|XECUTED); break; default: if (warn) initlog(L_VB, "no more processes left in this runlevel"); warn = 0; loglevel = -1; if (got_signals == 0) check_init_fifo(); break; } if (loglevel > 0) { initlog(L_VB, "Entering runlevel: %c", runlevel); write_utmp_wtmp("runlevel", "~~", runlevel + 256 * oldlevel, RUN_LVL, "~"); thislevel = runlevel; prevlevel = oldlevel; setproctitle("init [%c]", runlevel); } }}/* * Init got hit by a signal. See which signal it is, * and act accordingly. */void process_signals(){ CHILD *ch; int pwrstat; int oldlevel; int fd; char c; if (ISMEMBER(got_signals, SIGPWR)) { INITDBG(L_VB, "got SIGPWR"); /* See _what_ kind of SIGPWR this is. */ pwrstat = 0; if ((fd = open(PWRSTAT, O_RDONLY)) >= 0) { c = 0; read(fd, &c, 1); pwrstat = c; close(fd); unlink(PWRSTAT); } do_power_fail(pwrstat); DELSET(got_signals, SIGPWR); } if (ISMEMBER(got_signals, SIGINT)) { INITDBG(L_VB, "got SIGINT"); /* Tell ctrlaltdel entry to start up */ for(ch = family; ch; ch = ch->next) if (ch->action == CTRLALTDEL) ch->flags &= ~XECUTED; DELSET(got_signals, SIGINT); } if (ISMEMBER(got_signals, SIGWINCH)) { INITDBG(L_VB, "got SIGWINCH"); /* Tell kbrequest entry to start up */ for(ch = family; ch; ch = ch->next) if (ch->action == KBREQUEST) ch->flags &= ~XECUTED; DELSET(got_signals, SIGWINCH); } if (ISMEMBER(got_signals, SIGALRM)) { INITDBG(L_VB, "got SIGALRM"); /* The timer went off: check it out */ DELSET(got_signals, SIGALRM); } if (ISMEMBER(got_signals, SIGCHLD)) { INITDBG(L_VB, "got SIGCHLD"); /* First set flag to 0 */ DELSET(got_signals, SIGCHLD); /* See which child this was */ for(ch = family; ch; ch = ch->next) if (ch->flags & ZOMBIE) { INITDBG(L_VB, "Child died, PID= %d", ch->pid); ch->flags &= ~(RUNNING|ZOMBIE|WAITING); if (ch->process[0] != '+') write_utmp_wtmp("", ch->id, ch->pid, DEAD_PROCESS, NULL); } } if (ISMEMBER(got_signals, SIGHUP)) { INITDBG(L_VB, "got SIGHUP");#if CHANGE_WAIT /* Are we waiting for a child? */ for(ch = family; ch; ch = ch->next) if (ch->flags & WAITING) break; if (ch == NULL)#endif { /* We need to go into a new runlevel */ oldlevel = runlevel;#ifdef INITLVL runlevel = read_level(0);#endif if (runlevel == 'U') { runlevel = oldlevel; re_exec(); } else { if (oldlevel != 'S' && runlevel == 'S') console_stty(); if (runlevel == '6' || runlevel == '0' || runlevel == '1') console_stty(); read_inittab(); fail_cancel(); setproctitle("init [%c]", runlevel); DELSET(got_signals, SIGHUP); } } } if (ISMEMBER(got_signals, SIGUSR1)) { /* * SIGUSR1 means close and reopen /dev/initctl */ INITDBG(L_VB, "got SIGUSR1"); close(pipe_fd); pipe_fd = -1; DELSET(got_signals, SIGUSR1); }}/* * The main loop */ int init_main(){ CHILD *ch; struct sigaction sa; sigset_t sgt; pid_t rc; int f, st; if (!reload) { #if INITDEBUG /* * Fork so we can debug the init process. */ if ((f = fork()) > 0) { static const char killmsg[] = "PRNT: init killed.\r\n"; pid_t rc; while((rc = wait(&st)) != f) if (rc < 0 && errno == ECHILD) break; write(1, killmsg, sizeof(killmsg) - 1); while(1) pause(); }#endif#ifdef __linux__ /* * Tell the kernel to send us SIGINT when CTRL-ALT-DEL * is pressed, and that we want to handle keyboard signals. */ init_reboot(BMAGIC_SOFT); if ((f = open(VT_MASTER, O_RDWR | O_NOCTTY)) >= 0) { (void) ioctl(f, KDSIGACCEPT, SIGWINCH); close(f); } else (void) ioctl(0, KDSIGACCEPT, SIGWINCH);#endif /* * Ignore all signals. */ for(f = 1; f <= NSIG; f++) SETSIG(sa, f, SIG_IGN, SA_RESTART); } SETSIG(sa, SIGALRM, signal_handler, 0); SETSIG(sa, SIGHUP, signal_handler, 0); SETSIG(sa, SIGINT, signal_handler, 0); SETSIG(sa, SIGCHLD, chld_handler, SA_RESTART); SETSIG(sa, SIGPWR, signal_handler, 0); SETSIG(sa, SIGWINCH, signal_handler, 0); SETSIG(sa, SIGUSR1, signal_handler, 0); SETSIG(sa, SIGSTOP, stop_handler, SA_RESTART); SETSIG(sa, SIGTSTP, stop_handler, SA_RESTART); SETSIG(sa, SIGCONT, cont_handler, SA_RESTART); SETSIG(sa, SIGSEGV, (void (*)(int))segv_handler, SA_RESTART); console_init(); if (!reload) { /* Close whatever files are open, and reset the console. */ close(0); close(1); close(2); console_stty(); setsid(); /* * Set default PATH variable. */ putenv(PATH_DFL); /* * Initialize /var/run/utmp (only works if /var is on * root and mounted rw) */ (void) close(open(UTMP_FILE, O_WRONLY|O_CREAT|O_TRUNC, 0644)); /* * Say hello to the world */ initlog(L_CO, bootmsg, "booting"); /* * See if we have to start an emergency shell. */ if (emerg_shell) { SETSIG(sa, SIGCHLD, SIG_DFL, SA_RESTART); if (spawn(&ch_emerg, &f) > 0) { while((rc = wait(&st)) != f) if (rc < 0 && errno == ECHILD) break; } SETSIG(sa, SIGCHLD, chld_handler, SA_RESTART); } /* * Start normal boot procedure. */ runlevel = '#'; read_inittab(); } else { /* * Restart: unblock signals and let the show go on */ initlog(L_CO, bootmsg, "reloading"); sigfillset(&sgt); sigprocmask(SIG_UNBLOCK, &sgt, NULL); } start_if_needed(); while(1) { /* See if we need to make the boot transitions. */ boot_transitions(); INITDBG(L_VB, "init_main: waiting.."); /* Check if there are processes to be waited on. */ for(ch = family; ch; ch = ch->next) if ((ch->flags & RUNNING) && ch->action != BOOT) break;#if CHANGE_WAIT /* Wait until we get hit by some signal. */ while (ch != NULL && got_signals == 0) { if (ISMEMBER(got_signals, SIGHUP)) { /* See if there are processes to be waited on. */ for(ch = family; ch; ch = ch->next) if (ch->flags & WAITING) break; } if (ch != NULL) check_init_fifo(); }#else /* CHANGE_WAIT */ if (ch != NULL && got_signals == 0) check_init_fifo();#endif /* CHANGE_WAIT */ /* Check the 'failing' flags */ fail_check(); /* Process any signals. */ process_signals(); /* See what we need to start up (again) */ start_if_needed(); } /*NOTREACHED*/}/* * Tell the user about the syntax we expect. */void usage(char *s){ fprintf(stderr, "Usage: %s 0123456SsQqAaBbCcUu\n", s); exit(1);}int telinit(char *progname, int argc, char **argv){#ifdef TELINIT_USES_INITLVL FILE *fp;#endif struct init_request request; struct sigaction sa; int f, fd, l; char *env = NULL; memset(&request, 0, sizeof(request)); request.magic = INIT_MAGIC; while ((f = getopt(argc, argv, "t:e:")) != EOF) switch(f) { case 't': sltime = atoi(optarg); break; case 'e': if (env == NULL) env = request.i.data; l = strlen(optarg); if (env + l + 2 > request.i.data + sizeof(request.i.data)) { fprintf(stderr, "%s: -e option data " "too large\n", progname); exit(1); } memcpy(env, optarg, l); env += l; *env++ = 0; break; default: usage(progname); break; } if (env) *env++ = 0; if (env) { if (argc != optind) usage(progname); request.cmd = INIT_CMD_SETENV; } else { if (argc - optind != 1 || strlen(argv[optind]) != 1) usage(progname); if (!strchr("0123456789SsQqAaBbCcUu", argv[optind][0])) usage(progname); request.cmd = INIT_CMD_RUNLVL; request.runlevel = env ? 0 : argv[optind][0]; request.sleeptime = sltime; } /* Open the fifo and write a command. */ /* Make sure we don't hang on opening /dev/initctl */ SETSIG(sa, SIGALRM, signal_handler, 0); alarm(3); if ((fd = open(INIT_FIFO, O_WRONLY)) >= 0 && write(fd, &request, sizeof(request)) == sizeof(request)) { close(fd); alarm(0); return 0; }#ifdef TELINIT_USES_INITLVL if (request.cmd == INIT_CMD_RUNLVL) { /* Fallthrough to the old method. */ /* Now write the new runlevel. */ if ((fp = fopen(INITLVL, "w")) == NULL) { fprintf(stderr, "%s: cannot create %s\n", progname, INITLVL); exit(1); } fprintf(fp, "%s %d", argv[optind], sltime); fclose(fp); /* And tell init about the pending runlevel change. */ if (kill(INITPID, SIGHUP) < 0) perror(progname); return 0; }#endif fprintf(stderr, "%s: ", progname); if (ISMEMBER(got_signals, SIGALRM)) { fprintf(stderr, "timeout opening/writing control channel %s\n", INIT_FIFO); } else { perror(INIT_FIFO); } return 1;}/* * Main entry for init and telinit. */int main(int argc, char **argv){ char *p; int f; int isinit; /* Get my own name */ if ((p = strrchr(argv[0], '/')) != NULL) p++; else p = argv[0]; umask(022); /* Quick check */ if (geteuid() != 0) { fprintf(stderr, "%s: must be superuser.\n", p); exit(1); } /* * Is this telinit or init ? */ isinit = (getpid() == 1); for (f = 1; f < argc; f++) { if (!strcmp(argv[f], "-i") || !strcmp(argv[f], "--init")) isinit = 1; break; } if (!isinit) exit(telinit(p, argc, argv)); /* * Check for re-exec */ if (check_pipe(STATE_PIPE)) { receive_state(STATE_PIPE); myname = istrdup(argv[0]); argv0 = argv[0]; maxproclen = 0; for (f = 0; f < argc; f++) maxproclen += strlen(argv[f]) + 1; reload = 1; setproctitle("init [%c]",runlevel); init_main(); } /* Check command line arguments */ maxproclen = strlen(argv[0]) + 1; for(f = 1; f < argc; f++) { if (!strcmp(argv[f], "single") || !strcmp(argv[f], "-s")) dfl_level = 'S'; else if (!strcmp(argv[f], "-a") || !strcmp(argv[f], "auto")) putenv("AUTOBOOT=YES"); else if (!strcmp(argv[f], "-b") || !strcmp(argv[f],"emergency")) emerg_shell = 1; else if (!strcmp(argv[f], "-z")) { /* Ignore -z xxx */ if (argv[f + 1]) f++; } else if (strchr("0123456789sS", argv[f][0]) && strlen(argv[f]) == 1) dfl_level = argv[f][0]; /* "init u" in the very beginning makes no sense */ if (dfl_level == 's') dfl_level = 'S'; maxproclen += strlen(argv[f]) + 1; } /* Start booting. */ argv0 = argv[0]; argv[1] = NULL; setproctitle("init boot"); init_main(dfl_level); /*NOTREACHED*/ return 0;}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -