📄 init.c
字号:
/* * Only BOOT processes may live in all levels */ if (ch->action != BOOT && strchr(ch->rlevel, runlevel) == NULL) { /* * Ondemand procedures live always, * except in single user */ if (runlevel == 'S' || !(ch->flags & DEMAND)) ch->flags |= KILLME; } /* * Now, if this process may live note so in the new list */ if ((ch->flags & KILLME) == 0) { ch->new->flags = ch->flags; ch->new->pid = ch->pid; ch->new->exstat = ch->exstat; continue; } /* * Is this process still around? */ if ((ch->flags & RUNNING) == 0) { ch->flags &= ~KILLME; continue; } INITDBG(L_VB, "Killing \"%s\"", ch->process); switch(round) { case 0: /* Send TERM signal */ if (talk) initlog(L_CO, "Sending processes the TERM signal"); kill(-(ch->pid), SIGTERM); foundOne = 1; break; case 1: /* Send KILL signal and collect status */ if (talk) initlog(L_CO, "Sending processes the KILL signal"); kill(-(ch->pid), SIGKILL); break; } talk = 0; } /* * See if we have to wait 5 seconds */ if (foundOne && round == 0) { /* * Yup, but check every second if we still have children. */ for(f = 0; f < sltime; f++) { for(ch = family; ch; ch = ch->next) { if (!(ch->flags & KILLME)) continue; if ((ch->flags & RUNNING) && !(ch->flags & ZOMBIE)) break; } if (ch == NULL) { /* * No running children, skip SIGKILL */ round = 1; foundOne = 0; /* Skip the sleep below. */ break; } do_sleep(1); } } } /* * Now give all processes the chance to die and collect exit statuses. */ if (foundOne) do_sleep(1); for(ch = family; ch; ch = ch->next) if (ch->flags & KILLME) { if (!(ch->flags & ZOMBIE)) initlog(L_CO, "Pid %d [id %s] seems to hang", ch->pid, ch->id); else { INITDBG(L_VB, "Updating utmp for pid %d [id %s]", ch->pid, ch->id); ch->flags &= ~RUNNING; if (ch->process[0] != '+') write_utmp_wtmp("", ch->id, ch->pid, DEAD_PROCESS, NULL); } } /* * Both rounds done; clean up the list. */ sigemptyset(&nmask); sigaddset(&nmask, SIGCHLD); sigprocmask(SIG_BLOCK, &nmask, &omask); for(ch = family; ch; ch = old) { old = ch->next; free(ch); } family = newFamily; for(ch = family; ch; ch = ch->next) ch->new = NULL; newFamily = NULL; sigprocmask(SIG_SETMASK, &omask, NULL);#ifdef INITLVL /* * Dispose of INITLVL file. */ if (lstat(INITLVL, &st) >= 0 && S_ISLNK(st.st_mode)) { /* * INITLVL is a symbolic link, so just truncate the file. */ close(open(INITLVL, O_WRONLY|O_TRUNC)); } else { /* * Delete INITLVL file. */ unlink(INITLVL); }#endif#ifdef INITLVL2 /* * Dispose of INITLVL2 file. */ if (lstat(INITLVL2, &st) >= 0 && S_ISLNK(st.st_mode)) { /* * INITLVL2 is a symbolic link, so just truncate the file. */ close(open(INITLVL2, O_WRONLY|O_TRUNC)); } else { /* * Delete INITLVL2 file. */ unlink(INITLVL2); }#endif}/* * Walk through the family list and start up children. * The entries that do not belong here at all are removed * from the list. */void start_if_needed(void){ CHILD *ch; /* Pointer to child */ int delete; /* Delete this entry from list? */ INITDBG(L_VB, "Checking for children to start"); for(ch = family; ch; ch = ch->next) {#if DEBUG if (ch->rlevel[0] == 'C') { INITDBG(L_VB, "%s: flags %d", ch->process, ch->flags); }#endif /* Are we waiting for this process? Then quit here. */ if (ch->flags & WAITING) break; /* Already running? OK, don't touch it */ if (ch->flags & RUNNING) continue; /* See if we have to start it up */ delete = 1; if (strchr(ch->rlevel, runlevel) || ((ch->flags & DEMAND) && !strchr("#*Ss", runlevel))) { startup(ch); delete = 0; } if (delete) { /* FIXME: is this OK? */ ch->flags &= ~(RUNNING|WAITING); if (!ISPOWER(ch->action) && ch->action != KBREQUEST) ch->flags &= ~XECUTED; ch->pid = 0; } else /* Do we have to wait for this process? */ if (ch->flags & WAITING) break; } /* Done. */}/* * Ask the user on the console for a runlevel */int ask_runlevel(void){ const char prompt[] = "\nEnter runlevel: "; char buf[8]; int lvl = -1; int fd; console_stty(); fd = console_open(O_RDWR|O_NOCTTY); if (fd < 0) return('S'); while(!strchr("0123456789S", lvl)) { write(fd, prompt, sizeof(prompt) - 1); buf[0] = 0; read(fd, buf, sizeof(buf)); if (buf[0] != 0 && (buf[1] == '\r' || buf[1] == '\n')) lvl = buf[0]; if (islower(lvl)) lvl = toupper(lvl); } close(fd); return lvl;}/* * Search the INITTAB file for the 'initdefault' field, with the default * runlevel. If this fails, ask the user to supply a runlevel. */int get_init_default(void){ CHILD *ch; int lvl = -1; char *p; /* * Look for initdefault. */ for(ch = family; ch; ch = ch->next) if (ch->action == INITDEFAULT) { p = ch->rlevel; while(*p) { if (*p > lvl) lvl = *p; p++; } break; } /* * See if level is valid */ if (lvl > 0) { if (islower(lvl)) lvl = toupper(lvl); if (strchr("0123456789S", lvl) == NULL) { initlog(L_VB, "Initdefault level '%c' is invalid", lvl); lvl = 0; } } /* * Ask for runlevel on console if needed. */ if (lvl <= 0) lvl = ask_runlevel(); /* * Log the fact that we have a runlevel now. */ return lvl;}/* * We got signaled. * * Do actions for the new level. If we are compatible with * the "old" INITLVL and arg == 0, try to read the new * runlevel from that file first. */int read_level(int arg){ CHILD *ch; /* Walk through list */ unsigned char foo = 'X'; /* Contents of INITLVL */ int ok = 1;#ifdef INITLVL FILE *fp; struct stat stt; int st;#endif if (arg) foo = arg;#ifdef INITLVL ok = 0; if (arg == 0) { fp = NULL; if (stat(INITLVL, &stt) != 0 || stt.st_size != 0L) fp = fopen(INITLVL, "r");#ifdef INITLVL2 if (fp == NULL && (stat(INITLVL2, &stt) != 0 || stt.st_size != 0L)) fp = fopen(INITLVL2, "r");#endif if (fp == NULL) { /* INITLVL file empty or not there - act as 'init q' */ initlog(L_SY, "Re-reading inittab"); return(runlevel); } ok = fscanf(fp, "%c %d", &foo, &st); fclose(fp); } else { /* We go to the new runlevel passed as an argument. */ foo = arg; ok = 1; } if (ok == 2) sltime = st;#endif /* INITLVL */ if (islower(foo)) foo = toupper(foo); if (ok < 1 || ok > 2 || strchr("QS0123456789ABCU", foo) == NULL) { initlog(L_VB, "bad runlevel: %c", foo); return runlevel; } /* Log this action */ switch(foo) { case 'S': initlog(L_VB, "Going single user"); break; case 'Q': initlog(L_SY, "Re-reading inittab"); break; case 'A': case 'B': case 'C': initlog(L_SY, "Activating demand-procedures for '%c'", foo); break; case 'U': initlog(L_SY, "Trying to re-exec init"); return 'U'; default: initlog(L_VB, "Switching to runlevel: %c", foo); } if (foo == 'Q') return runlevel; /* Check if this is a runlevel a, b or c */ if (strchr("ABC", foo)) { if (runlevel == 'S') return(runlevel); /* Read inittab again first! */ read_inittab(); /* Mark those special tasks */ for(ch = family; ch; ch = ch->next) if (strchr(ch->rlevel, foo) != NULL || strchr(ch->rlevel, tolower(foo)) != NULL) { ch->flags |= DEMAND; ch->flags &= ~XECUTED; INITDBG(L_VB, "Marking (%s) as ondemand, flags %d", ch->id, ch->flags); } return runlevel; } /* Store both the old and the new runlevel. */ write_utmp_wtmp("runlevel", "~~", foo + 256*runlevel, RUN_LVL, "~"); thislevel = foo; prevlevel = runlevel; return foo;}/* * This procedure is called after every signal (SIGHUP, SIGALRM..) * * Only clear the 'failing' flag if the process is sleeping * longer than 5 minutes, or inittab was read again due * to user interaction. */void fail_check(void){ CHILD *ch; /* Pointer to child structure */ time_t t; /* System time */ time_t next_alarm = 0; /* When to set next alarm */ time(&t); for(ch = family; ch; ch = ch->next) { if (ch->flags & FAILING) { /* Can we free this sucker? */ if (ch->tm + SLEEPTIME < t) { ch->flags &= ~FAILING; ch->count = 0; ch->tm = 0; } else { /* No, we'll look again later */ if (next_alarm == 0 || ch->tm + SLEEPTIME > next_alarm) next_alarm = ch->tm + SLEEPTIME; } } } if (next_alarm) { next_alarm -= t; if (next_alarm < 1) next_alarm = 1; alarm(next_alarm); }}/* Set all 'Fail' timers to 0 */void fail_cancel(void){ CHILD *ch; for(ch = family; ch; ch = ch->next) { ch->count = 0; ch->tm = 0; ch->flags &= ~FAILING; }}/* * Start up powerfail entries. */void do_power_fail(int pwrstat){ CHILD *ch; /* * Tell powerwait & powerfail entries to start up */ for (ch = family; ch; ch = ch->next) { if (pwrstat == 'O') { /* * The power is OK again. */ if (ch->action == POWEROKWAIT) ch->flags &= ~XECUTED; } else if (pwrstat == 'L') { /* * Low battery, shut down now. */ if (ch->action == POWERFAILNOW) ch->flags &= ~XECUTED; } else { /* * Power is failing, shutdown imminent */ if (ch->action == POWERFAIL || ch->action == POWERWAIT) ch->flags &= ~XECUTED; } }}/* * Check for state-pipe presence */int check_pipe(int fd){ struct timeval t; fd_set s; char signature[8]; FD_ZERO(&s); FD_SET(fd, &s); t.tv_sec = t.tv_usec = 0; if (select(fd+1, &s, NULL, NULL, &t) != 1) return 0; if (read(fd, signature, 8) != 8) return 0; return strncmp(Signature, signature, 8) == 0;}/* * Make a state-pipe. */int make_pipe(int fd){ int fds[2]; pipe(fds); dup2(fds[0], fd); close(fds[0]); fcntl(fds[1], F_SETFD, 1); fcntl(fd, F_SETFD, 0); write(fds[1], Signature, 8); return fds[1];}/* * Attempt to re-exec. */void re_exec(void){ CHILD *ch; sigset_t mask, oldset; pid_t pid; char **env; int fd; if (strchr("S12345",runlevel) == NULL) return; /* * Reset the alarm, and block all signals. */ alarm(0); sigfillset(&mask); sigprocmask(SIG_BLOCK, &mask, &oldset); /* * construct a pipe fd --> STATE_PIPE and write a signature */ fd = make_pipe(STATE_PIPE); /* * It's a backup day today, so I'm pissed off. Being a BOFH, however, * does have it's advantages... */ fail_cancel(); close(pipe_fd); pipe_fd = -1; DELSET(got_signals, SIGCHLD); DELSET(got_signals, SIGHUP); DELSET(got_signals, SIGUSR1); /* * That should be cleaned. */ 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 ((pid = fork()) == 0) { /* * Child sends state information to the parent. */ send_state(fd); exit(0); } /* * The existing init process execs a new init binary. */ env = init_buildenv(0); execl(myname, myname, "--init", NULL, env); /* * We shouldn't be here, something failed. * Bitch, close the state pipe, unblock signals and return. */ close(fd); close(STATE_PIPE); sigprocmask(SIG_SETMASK, &oldset, NULL); init_freeenv(env); initlog(L_CO, "Attempt to re-exec failed");}/* * We got a change runlevel request through the * init.fifo. Process it. */void fifo_new_level(int level){#if CHANGE_WAIT CHILD *ch;#endif int oldlevel; if (level == runlevel) return;#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; runlevel = read_level(level); 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); } }}/* * Set/unset environment variables. The variables are * encoded as KEY=VAL\0KEY=VAL\0\0. With "=VAL" it means * setenv, without it means unsetenv. */void initcmd_setenv(char *data, int size){ char *env, *p, *e, *eq; int i, sz; e = data + size; while (*data && data < e) { eq = NULL; for (p = data; *p && p < e; p++) if (*p == '=') eq = p; if (*p) break; env = data; data = ++p; sz = eq ? (eq - env) : (p - env); /*initlog(L_SY, "init_setenv: %s, %s, %d", env, eq, sz);*/ /* * We only allow INIT_* to be set. */ if (strncmp(env, "INIT_", 5) != 0) continue; /* Free existing vars. */ for (i = 0; i < NR_EXTRA_ENV; i++) { if (extra_env[i] == NULL) continue; if (!strncmp(extra_env[i], env, sz) && extra_env[i][sz] == '=') { free(extra_env[i]); extra_env[i] = NULL; } } /* Set new vars if needed. */ if (eq == NULL) continue; for (i = 0; i < NR_EXTRA_ENV; i++) { if (extra_env[i] == NULL) { extra_env[i] = istrdup(env); break; } } }}/* * Read from the init FIFO. Processes like telnetd and rlogind can * ask us to create login processes on their behalf. * * FIXME: this needs to be finished. NOT that it is buggy, but we need * to add the telnetd/rlogind stuff so people can start using it. * Maybe move to using an AF_UNIX socket so we can use * the 2.2 kernel credential stuff to see who we're talking to. * */void check_init_fifo(void){ struct init_request request; struct timeval tv; struct stat st, st2; fd_set fds; int n;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -