📄 init.c
字号:
while(!got_cont) pause(); got_cont = 0; errno = saved_errno;}/* * Set terminal settings to reasonable defaults */void console_stty(void){ struct termios tty; int fd; if ((fd = console_open(O_RDWR|O_NOCTTY)) < 0) { initlog(L_VB, "can't open %s", console_dev); return; } (void) tcgetattr(fd, &tty); tty.c_cflag &= CBAUD|CBAUDEX|CSIZE|CSTOPB|PARENB|PARODD; tty.c_cflag |= HUPCL|CLOCAL|CREAD; tty.c_cc[VINTR] = 3; /* ctrl('c') */ tty.c_cc[VQUIT] = 28; /* ctrl('\\') */ tty.c_cc[VERASE] = 127; tty.c_cc[VKILL] = 24; /* ctrl('x') */ tty.c_cc[VEOF] = 4; /* ctrl('d') */ tty.c_cc[VTIME] = 0; tty.c_cc[VMIN] = 1; tty.c_cc[VSTART] = 17; /* ctrl('q') */ tty.c_cc[VSTOP] = 19; /* ctrl('s') */ tty.c_cc[VSUSP] = 26; /* ctrl('z') */ /* * Set pre and post processing */ tty.c_iflag = IGNPAR|ICRNL|IXON|IXANY; tty.c_oflag = OPOST|ONLCR; tty.c_lflag = ISIG|ICANON|ECHO|ECHOCTL|ECHOPRT|ECHOKE; /* * Now set the terminal line. * We don't care about non-transmitted output data * and non-read input data. */ (void) tcsetattr(fd, TCSANOW, &tty); (void) tcflush(fd, TCIOFLUSH); (void) close(fd);}/* * Print to the system console */void print(char *s){ int fd; if ((fd = console_open(O_WRONLY|O_NOCTTY|O_NDELAY)) >= 0) { write(fd, s, strlen(s)); close(fd); }}/* * Log something to a logfile and the console. */#ifdef __GNUC____attribute__ ((format (printf, 2, 3)))#endifvoid initlog(int loglevel, char *s, ...){ va_list va_alist; char buf[256]; sigset_t nmask, omask; va_start(va_alist, s); vsnprintf(buf, sizeof(buf), s, va_alist); va_end(va_alist); if (loglevel & L_SY) { /* * Re-establish connection with syslogd every time. * Block signals while talking to syslog. */ sigfillset(&nmask); sigprocmask(SIG_BLOCK, &nmask, &omask); openlog("init", 0, LOG_DAEMON); syslog(LOG_INFO, "%s", buf); closelog(); sigprocmask(SIG_SETMASK, &omask, NULL); } /* * And log to the console. */ if (loglevel & L_CO) { print("\rINIT: "); print(buf); print("\r\n"); }}/* * Build a new environment for execve(). */char **init_buildenv(int child){ char i_lvl[] = "RUNLEVEL=x"; char i_prev[] = "PREVLEVEL=x"; char i_cons[32]; char **e; int n, i; for (n = 0; environ[n]; n++) ; n += NR_EXTRA_ENV + 8; e = calloc(n, sizeof(char *)); for (n = 0; environ[n]; n++) e[n] = istrdup(environ[n]); for (i = 0; i < NR_EXTRA_ENV; i++) if (extra_env[i]) e[n++] = istrdup(extra_env[i]); if (child) { snprintf(i_cons, sizeof(i_cons), "CONSOLE=%s", console_dev); i_lvl[9] = thislevel; i_prev[10] = prevlevel; e[n++] = istrdup(i_lvl); e[n++] = istrdup(i_prev); e[n++] = istrdup(i_cons); e[n++] = istrdup(E_VERSION); } e[n++] = NULL; return e;}void init_freeenv(char **e){ int n; for (n = 0; e[n]; n++) free(e[n]); free(e);}/* * Fork and execute. * * This function is too long and indents too deep. * */int spawn(CHILD *ch, int *res){ char *args[16]; /* Argv array */ char buf[136]; /* Line buffer */ int f, st, rc; /* Scratch variables */ char *ptr; /* Ditto */ time_t t; /* System time */ int oldAlarm; /* Previous alarm value */ char *proc = ch->process; /* Command line */ pid_t pid, pgrp; /* child, console process group. */ sigset_t nmask, omask; /* For blocking SIGCHLD */ struct sigaction sa; *res = -1; buf[sizeof(buf) - 1] = 0; /* Skip '+' if it's there */ if (proc[0] == '+') proc++; ch->flags |= XECUTED; if (ch->action == RESPAWN || ch->action == ONDEMAND) { /* Is the date stamp from less than 2 minutes ago? */ time(&t); if (ch->tm + TESTTIME > t) { ch->count++; } else { ch->count = 0; ch->tm = t; } /* Do we try to respawn too fast? */ if (ch->count >= MAXSPAWN) { initlog(L_VB, "Id \"%s\" respawning too fast: disabled for %d minutes", ch->id, SLEEPTIME / 60); ch->flags &= ~RUNNING; ch->flags |= FAILING; /* Remember the time we stopped */ ch->tm = t; /* Try again in 5 minutes */ oldAlarm = alarm(0); if (oldAlarm > SLEEPTIME || oldAlarm <= 0) oldAlarm = SLEEPTIME; alarm(oldAlarm); return(-1); } } /* See if there is an "initscript" (except in single user mode). */ if (access(INITSCRIPT, R_OK) == 0 && runlevel != 'S') { /* Build command line using "initscript" */ args[1] = SHELL; args[2] = INITSCRIPT; args[3] = ch->id; args[4] = ch->rlevel; args[5] = "unknown"; for(f = 0; actions[f].name; f++) { if (ch->action == actions[f].act) { args[5] = actions[f].name; break; } } args[6] = proc; args[7] = NULL; } else if (strpbrk(proc, "~`!$^&*()=|\\{}[];\"'<>?")) { /* See if we need to fire off a shell for this command */ /* Give command line to shell */ args[1] = SHELL; args[2] = "-c"; strcpy(buf, "exec "); strncat(buf, proc, sizeof(buf) - strlen(buf) - 1); args[3] = buf; args[4] = NULL; } else { /* Split up command line arguments */ buf[0] = 0; strncat(buf, proc, sizeof(buf) - 1); ptr = buf; for(f = 1; f < 15; f++) { /* Skip white space */ while(*ptr == ' ' || *ptr == '\t') ptr++; args[f] = ptr; /* May be trailing space.. */ if (*ptr == 0) break; /* Skip this `word' */ while(*ptr && *ptr != ' ' && *ptr != '\t' && *ptr != '#') ptr++; /* If end-of-line, break */ if (*ptr == '#' || *ptr == 0) { f++; *ptr = 0; break; } /* End word with \0 and continue */ *ptr++ = 0; } args[f] = NULL; } args[0] = args[1]; while(1) { /* * Block sigchild while forking. */ sigemptyset(&nmask); sigaddset(&nmask, SIGCHLD); sigprocmask(SIG_BLOCK, &nmask, &omask); if ((pid = fork()) == 0) { close(0); close(1); close(2); if (pipe_fd >= 0) close(pipe_fd); sigprocmask(SIG_SETMASK, &omask, NULL); /* * In sysinit, boot, bootwait or single user mode: * for any wait-type subprocess we _force_ the console * to be its controlling tty. */ if (strchr("*#sS", runlevel) && ch->flags & WAITING) { /* * We fork once extra. This is so that we can * wait and change the process group and session * of the console after exit of the leader. */ setsid(); if ((f = console_open(O_RDWR|O_NOCTTY)) >= 0) { /* Take over controlling tty by force */ (void)ioctl(f, TIOCSCTTY, 1); dup(f); dup(f); } if ((pid = fork()) < 0) { initlog(L_VB, "cannot fork"); exit(1); } if (pid > 0) { /* * Ignore keyboard signals etc. * Then wait for child to exit. */ SETSIG(sa, SIGINT, SIG_IGN, SA_RESTART); SETSIG(sa, SIGTSTP, SIG_IGN, SA_RESTART); SETSIG(sa, SIGQUIT, SIG_IGN, SA_RESTART); SETSIG(sa, SIGCHLD, SIG_DFL, SA_RESTART); while ((rc = waitpid(pid, &st, 0)) != pid) if (rc < 0 && errno == ECHILD) break; /* * Small optimization. See if stealing * controlling tty back is needed. */ pgrp = tcgetpgrp(f); if (pgrp != getpid()) exit(0); /* * Steal controlling tty away. We do * this with a temporary process. */ if ((pid = fork()) < 0) { initlog(L_VB, "cannot fork"); exit(1); } if (pid == 0) { setsid(); (void)ioctl(f, TIOCSCTTY, 1); exit(0); } while((rc = waitpid(pid, &st, 0)) != pid) if (rc < 0 && errno == ECHILD) break; exit(0); } /* Set ioctl settings to default ones */ console_stty(); } else { setsid(); if ((f = console_open(O_RDWR|O_NOCTTY)) < 0) { initlog(L_VB, "open(%s): %s", console_dev, strerror(errno)); f = open("/dev/null", O_RDWR); } dup(f); dup(f); } /* Reset all the signals, set up environment */ for(f = 1; f < NSIG; f++) SETSIG(sa, f, SIG_DFL, SA_RESTART); environ = init_buildenv(1); /* * Execute prog. In case of ENOEXEC try again * as a shell script. */ execvp(args[1], args + 1); if (errno == ENOEXEC) { args[1] = SHELL; args[2] = "-c"; strcpy(buf, "exec "); strncat(buf, proc, sizeof(buf) - strlen(buf) - 1); args[3] = buf; args[4] = NULL; execvp(args[1], args + 1); } initlog(L_VB, "cannot execute \"%s\"", args[1]); exit(1); } *res = pid; sigprocmask(SIG_SETMASK, &omask, NULL); INITDBG(L_VB, "Started id %s (pid %d)", ch->id, pid); if (pid == -1) { initlog(L_VB, "cannot fork, retry.."); do_sleep(5); continue; } return(pid); }}/* * Start a child running! */void startup(CHILD *ch){ /* * See if it's disabled */ if (ch->flags & FAILING) return; switch(ch->action) { case SYSINIT: case BOOTWAIT: case WAIT: case POWERWAIT: case POWERFAILNOW: case POWEROKWAIT: case CTRLALTDEL: if (!(ch->flags & XECUTED)) ch->flags |= WAITING; case KBREQUEST: case BOOT: case POWERFAIL: case ONCE: if (ch->flags & XECUTED) break; case ONDEMAND: case RESPAWN: ch->flags |= RUNNING; if (spawn(ch, &(ch->pid)) < 0) break; /* * Do NOT log if process field starts with '+' * FIXME: that's for compatibility with *very* * old getties - probably it can be taken out. */ if (ch->process[0] != '+') write_utmp_wtmp("", ch->id, ch->pid, INIT_PROCESS, ""); break; }}/* * Read the inittab file. */void read_inittab(void){ FILE *fp; /* The INITTAB file */ CHILD *ch, *old, *i; /* Pointers to CHILD structure */ CHILD *head = NULL; /* Head of linked list */#ifdef INITLVL struct stat st; /* To stat INITLVL */#endif sigset_t nmask, omask; /* For blocking SIGCHLD. */ char buf[256]; /* Line buffer */ char err[64]; /* Error message. */ char *id, *rlevel, *action, *process; /* Fields of a line */ char *p; int lineNo = 0; /* Line number in INITTAB file */ int actionNo; /* Decoded action field */ int f; /* Counter */ int round; /* round 0 for SIGTERM, 1 for SIGKILL */ int foundOne = 0; /* No killing no sleep */ int talk; /* Talk to the user */ int done = 0; /* Ready yet? */#if DEBUG if (newFamily != NULL) { INITDBG(L_VB, "PANIC newFamily != NULL"); exit(1); } INITDBG(L_VB, "Reading inittab");#endif /* * Open INITTAB and real line by line. */ if ((fp = fopen(INITTAB, "r")) == NULL) initlog(L_VB, "No inittab file found"); while(!done) { /* * Add single user shell entry at the end. */ if (fp == NULL || fgets(buf, sizeof(buf), fp) == NULL) { done = 1; /* * See if we have a single user entry. */ for(old = newFamily; old; old = old->next) if (strpbrk(old->rlevel, "S")) break; if (old == NULL) snprintf(buf, sizeof(buf), "~~:S:wait:%s\n", SULOGIN); else continue; } lineNo++; /* * Skip comments and empty lines */ for(p = buf; *p == ' ' || *p == '\t'; p++) ; if (*p == '#' || *p == '\n') continue; /* * Decode the fields */ id = strsep(&p, ":"); rlevel = strsep(&p, ":"); action = strsep(&p, ":"); process = strsep(&p, "\n"); /* * Check if syntax is OK. Be very verbose here, to * avoid newbie postings on comp.os.linux.setup :) */ err[0] = 0; if (!id || !*id) strcpy(err, "missing id field"); if (!rlevel) strcpy(err, "missing runlevel field"); if (!process) strcpy(err, "missing process field"); if (!action || !*action) strcpy(err, "missing action field"); if (id && strlen(id) > sizeof(utproto.ut_id)) sprintf(err, "id field too long (max %d characters)", (int)sizeof(utproto.ut_id)); if (rlevel && strlen(rlevel) > 11) strcpy(err, "rlevel field too long (max 11 characters)"); if (process && strlen(process) > 127) strcpy(err, "process field too long"); if (action && strlen(action) > 32) strcpy(err, "action field too long"); if (err[0] != 0) { initlog(L_VB, "%s[%d]: %s", INITTAB, lineNo, err); INITDBG(L_VB, "%s:%s:%s:%s", id, rlevel, action, process); continue; } /* * Decode the "action" field */ actionNo = -1; for(f = 0; actions[f].name; f++) if (strcasecmp(action, actions[f].name) == 0) { actionNo = actions[f].act; break; } if (actionNo == -1) { initlog(L_VB, "%s[%d]: %s: unknown action field", INITTAB, lineNo, action); continue; } /* * See if the id field is unique */ for(old = newFamily; old; old = old->next) { if(strcmp(old->id, id) == 0 && strcmp(id, "~~")) { initlog(L_VB, "%s[%d]: duplicate ID field \"%s\"", INITTAB, lineNo, id); break; } } if (old) continue; /* * Allocate a CHILD structure */ ch = imalloc(sizeof(CHILD)); /* * And fill it in. */ ch->action = actionNo; strncpy(ch->id, id, sizeof(utproto.ut_id) + 1); /* Hack for different libs. */ strncpy(ch->process, process, sizeof(ch->process) - 1); if (rlevel[0]) { for(f = 0; f < sizeof(rlevel) - 1 && rlevel[f]; f++) { ch->rlevel[f] = rlevel[f]; if (ch->rlevel[f] == 's') ch->rlevel[f] = 'S'; } strncpy(ch->rlevel, rlevel, sizeof(ch->rlevel) - 1); } else { strcpy(ch->rlevel, "0123456789"); if (ISPOWER(ch->action)) strcpy(ch->rlevel, "S0123456789"); } /* * We have the fake runlevel '#' for SYSINIT and * '*' for BOOT and BOOTWAIT. */ if (ch->action == SYSINIT) strcpy(ch->rlevel, "#"); if (ch->action == BOOT || ch->action == BOOTWAIT) strcpy(ch->rlevel, "*"); /* * Now add it to the linked list. Special for powerfail. */ if (ISPOWER(ch->action)) { /* * Disable by default */ ch->flags |= XECUTED; /* * Tricky: insert at the front of the list.. */ old = NULL; for(i = newFamily; i; i = i->next) { if (!ISPOWER(i->action)) break; old = i; } /* * Now add after entry "old" */ if (old) { ch->next = i; old->next = ch; if (i == NULL) head = ch; } else { ch->next = newFamily; newFamily = ch; if (ch->next == NULL) head = ch; } } else { /* * Just add at end of the list */ if (ch->action == KBREQUEST) ch->flags |= XECUTED; ch->next = NULL; if (head) head->next = ch; else newFamily = ch; head = ch; } /* * Walk through the old list comparing id fields */ for(old = family; old; old = old->next) if (strcmp(old->id, ch->id) == 0) { old->new = ch; break; } } /* * We're done. */ if (fp) fclose(fp); /* * Loop through the list of children, and see if they need to * be killed. */ INITDBG(L_VB, "Checking for children to kill"); for(round = 0; round < 2; round++) { talk = 1; for(ch = family; ch; ch = ch->next) { ch->flags &= ~KILLME; /* * Is this line deleted? */ if (ch->new == NULL) ch->flags |= KILLME; /* * If the entry has changed, kill it anyway. Note that * we do not check ch->process, only the "action" field. * This way, you can turn an entry "off" immediately, but * changes in the command line will only become effective * after the running version has exited. */ if (ch->new && ch->action != ch->new->action) ch->flags |= KILLME;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -