📄 init.c
字号:
/* * Init A System-V Init Clone. * * Usage: /sbin/init * init [0123456SsQqAaBbCc] * telinit [0123456SsQqAaBbCc] * * Version: @(#)init.c 2.86 30-Jul-2004 miquels@cistron.nl */#define VERSION "2.86"#define DATE "31-Jul-2004"/* * This file is part of the sysvinit suite, * Copyright 1991-2004 Miquel van Smoorenburg. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version * 2 of the License, or (at your option) any later version. * */#include <sys/types.h>#include <sys/stat.h>#include <sys/ioctl.h>#include <sys/wait.h>#ifdef __linux__#include <sys/kd.h>#endif#include <sys/resource.h>#include <stdlib.h>#include <unistd.h>#include <errno.h>#include <stdio.h>#include <time.h>#include <fcntl.h>#include <string.h>#include <signal.h>#include <termios.h>#include <utmp.h>#include <ctype.h>#include <stdarg.h>#include <sys/syslog.h>#include <sys/time.h>#ifdef __i386__# if (__GLIBC__ >= 2) /* GNU libc 2.x */# define STACK_DEBUG 1# if (__GLIBC__ == 2 && __GLIBC_MINOR__ == 0) /* Only glibc 2.0 needs this */# include <sigcontext.h># endif# endif#endif#include "init.h"#include "initreq.h"#include "paths.h"#include "reboot.h"#include "set.h"#ifndef SIGPWR# define SIGPWR SIGUSR2#endif#ifndef CBAUD# define CBAUD 0#endif#ifndef CBAUDEX# define CBAUDEX 0#endif/* Set a signal handler. */#define SETSIG(sa, sig, fun, flags) \ do { \ sa.sa_handler = fun; \ sa.sa_flags = flags; \ sigemptyset(&sa.sa_mask); \ sigaction(sig, &sa, NULL); \ } while(0)/* Version information */char *Version = "@(#) init " VERSION " " DATE " miquels@cistron.nl";char *bootmsg = "version " VERSION " %s";#define E_VERSION "INIT_VERSION=sysvinit-" VERSIONCHILD *family = NULL; /* The linked list of all entries */CHILD *newFamily = NULL; /* The list after inittab re-read */CHILD ch_emerg = { /* Emergency shell */ 0, 0, 0, 0, 0, "~~", "S", 3, "/sbin/sulogin", NULL, NULL};char runlevel = 'S'; /* The current run level */char thislevel = 'S'; /* The current runlevel */char prevlevel = 'N'; /* Previous runlevel */int dfl_level = 0; /* Default runlevel */sig_atomic_t got_cont = 0; /* Set if we received the SIGCONT signal */sig_atomic_t got_signals; /* Set if we received a signal. */int emerg_shell = 0; /* Start emergency shell? */int wrote_wtmp_reboot = 1; /* Set when we wrote the reboot record */int wrote_utmp_reboot = 1; /* Set when we wrote the reboot record */int sltime = 5; /* Sleep time between TERM and KILL */char *argv0; /* First arguments; show up in ps listing */int maxproclen; /* Maximal length of argv[0] with \0 */struct utmp utproto; /* Only used for sizeof(utproto.ut_id) */char *user_console = NULL; /* User console device */char *console_dev; /* Console device. */int pipe_fd = -1; /* /dev/initctl */int did_boot = 0; /* Did we already do BOOT* stuff? */int main(int, char **);/* Used by re-exec part */int reload = 0; /* Should we do initialization stuff? */char *myname="/sbin/init"; /* What should we exec */int oops_error; /* Used by some of the re-exec code. */const char *Signature = "12567362"; /* Signature for re-exec fd *//* Macro to see if this is a special action */#define ISPOWER(i) ((i) == POWERWAIT || (i) == POWERFAIL || \ (i) == POWEROKWAIT || (i) == POWERFAILNOW || \ (i) == CTRLALTDEL)/* ascii values for the `action' field. */struct actions { char *name; int act;} actions[] = { { "respawn", RESPAWN }, { "wait", WAIT }, { "once", ONCE }, { "boot", BOOT }, { "bootwait", BOOTWAIT }, { "powerfail", POWERFAIL }, { "powerfailnow",POWERFAILNOW }, { "powerwait", POWERWAIT }, { "powerokwait", POWEROKWAIT }, { "ctrlaltdel", CTRLALTDEL }, { "off", OFF }, { "ondemand", ONDEMAND }, { "initdefault", INITDEFAULT }, { "sysinit", SYSINIT }, { "kbrequest", KBREQUEST }, { NULL, 0 },};/* * State parser token table (see receive_state) */struct { char name[4]; int cmd;} cmds[] = { { "VER", C_VER }, { "END", C_END }, { "REC", C_REC }, { "EOR", C_EOR }, { "LEV", C_LEV }, { "FL ", C_FLAG }, { "AC ", C_ACTION }, { "CMD", C_PROCESS }, { "PID", C_PID }, { "EXS", C_EXS }, { "-RL", D_RUNLEVEL }, { "-TL", D_THISLEVEL }, { "-PL", D_PREVLEVEL }, { "-SI", D_GOTSIGN }, { "-WR", D_WROTE_WTMP_REBOOT}, { "-WU", D_WROTE_UTMP_REBOOT}, { "-ST", D_SLTIME }, { "-DB", D_DIDBOOT }, { "", 0 }};struct { char *name; int mask;} flags[]={ {"RU",RUNNING}, {"DE",DEMAND}, {"XD",XECUTED}, {NULL,0}};#define NR_EXTRA_ENV 16char *extra_env[NR_EXTRA_ENV];/* * Sleep a number of seconds. * * This only works correctly because the linux select updates * the elapsed time in the struct timeval passed to select! */void do_sleep(int sec){ struct timeval tv; tv.tv_sec = sec; tv.tv_usec = 0; while(select(0, NULL, NULL, NULL, &tv) < 0 && errno == EINTR) ;}/* * Non-failing allocation routines (init cannot fail). */void *imalloc(size_t size){ void *m; while ((m = malloc(size)) == NULL) { initlog(L_VB, "out of memory"); do_sleep(5); } memset(m, 0, size); return m;}char *istrdup(char *s){ char *m; int l; l = strlen(s) + 1; m = imalloc(l); memcpy(m, s, l); return m;}/* * Send the state info of the previous running init to * the new one, in a version-independant way. */void send_state(int fd){ FILE *fp; CHILD *p; int i,val; fp = fdopen(fd,"w"); fprintf(fp, "VER%s\n", Version); fprintf(fp, "-RL%c\n", runlevel); fprintf(fp, "-TL%c\n", thislevel); fprintf(fp, "-PL%c\n", prevlevel); fprintf(fp, "-SI%u\n", got_signals); fprintf(fp, "-WR%d\n", wrote_wtmp_reboot); fprintf(fp, "-WU%d\n", wrote_utmp_reboot); fprintf(fp, "-ST%d\n", sltime); fprintf(fp, "-DB%d\n", did_boot); for (p = family; p; p = p->next) { fprintf(fp, "REC%s\n", p->id); fprintf(fp, "LEV%s\n", p->rlevel); for (i = 0, val = p->flags; flags[i].mask; i++) if (val & flags[i].mask) { val &= ~flags[i].mask; fprintf(fp, "FL %s\n",flags[i].name); } fprintf(fp, "PID%d\n",p->pid); fprintf(fp, "EXS%u\n",p->exstat); for(i = 0; actions[i].act; i++) if (actions[i].act == p->action) { fprintf(fp, "AC %s\n", actions[i].name); break; } fprintf(fp, "CMD%s\n", p->process); fprintf(fp, "EOR\n"); } fprintf(fp, "END\n"); fclose(fp);}/* * Read a string from a file descriptor. * FIXME: why not use fgets() ? */static int get_string(char *p, int size, FILE *f){ int c; while ((c = getc(f)) != EOF && c != '\n') { if (--size > 0) *p++ = c; } *p = '\0'; return (c != EOF) && (size > 0);}/* * Read trailing data from the state pipe until we see a newline. */static int get_void(FILE *f){ int c; while ((c = getc(f)) != EOF && c != '\n') ; return (c != EOF);}/* * Read the next "command" from the state pipe. */static int get_cmd(FILE *f){ char cmd[4] = " "; int i; if (fread(cmd, 1, sizeof(cmd) - 1, f) != sizeof(cmd) - 1) return C_EOF; for(i = 0; cmds[i].cmd && strcmp(cmds[i].name, cmd) != 0; i++) ; return cmds[i].cmd;}/* * Read a CHILD * from the state pipe. */static CHILD *get_record(FILE *f){ int cmd; char s[32]; int i; CHILD *p; do { switch (cmd = get_cmd(f)) { case C_END: get_void(f); return NULL; case 0: get_void(f); break; case C_REC: break; case D_RUNLEVEL: fscanf(f, "%c\n", &runlevel); break; case D_THISLEVEL: fscanf(f, "%c\n", &thislevel); break; case D_PREVLEVEL: fscanf(f, "%c\n", &prevlevel); break; case D_GOTSIGN: fscanf(f, "%u\n", &got_signals); break; case D_WROTE_WTMP_REBOOT: fscanf(f, "%d\n", &wrote_wtmp_reboot); break; case D_WROTE_UTMP_REBOOT: fscanf(f, "%d\n", &wrote_utmp_reboot); break; case D_SLTIME: fscanf(f, "%d\n", &sltime); break; case D_DIDBOOT: fscanf(f, "%d\n", &did_boot); break; default: if (cmd > 0 || cmd == C_EOF) { oops_error = -1; return NULL; } } } while (cmd != C_REC); p = imalloc(sizeof(CHILD)); get_string(p->id, sizeof(p->id), f); do switch(cmd = get_cmd(f)) { case 0: case C_EOR: get_void(f); break; case C_PID: fscanf(f, "%d\n", &(p->pid)); break; case C_EXS: fscanf(f, "%u\n", &(p->exstat)); break; case C_LEV: get_string(p->rlevel, sizeof(p->rlevel), f); break; case C_PROCESS: get_string(p->process, sizeof(p->process), f); break; case C_FLAG: get_string(s, sizeof(s), f); for(i = 0; flags[i].name; i++) { if (strcmp(flags[i].name,s) == 0) break; } p->flags |= flags[i].mask; break; case C_ACTION: get_string(s, sizeof(s), f); for(i = 0; actions[i].name; i++) { if (strcmp(actions[i].name, s) == 0) break; } p->action = actions[i].act ? actions[i].act : OFF; break; default: free(p); oops_error = -1; return NULL; } while( cmd != C_EOR); return p;}/* * Read the complete state info from the state pipe. * Returns 0 on success */int receive_state(int fd){ FILE *f; char old_version[256]; CHILD **pp; f = fdopen(fd, "r"); if (get_cmd(f) != C_VER) return -1; get_string(old_version, sizeof(old_version), f); oops_error = 0; for (pp = &family; (*pp = get_record(f)) != NULL; pp = &((*pp)->next)) ; fclose(f); return oops_error;}/* * Set the process title. */#ifdef __GNUC____attribute__ ((format (printf, 1, 2)))#endifstatic int setproctitle(char *fmt, ...){ va_list ap; int len; char buf[256]; buf[0] = 0; va_start(ap, fmt); len = vsnprintf(buf, sizeof(buf), fmt, ap); va_end(ap); if (maxproclen > 2) { memset(argv0, 0, maxproclen); strncpy(argv0, buf, maxproclen - 2); } return len;}/* * Set console_dev to a working console. */void console_init(void){ int fd; int tried_devcons = 0; int tried_vtmaster = 0; char *s; if (user_console) { console_dev = user_console; } else if ((s = getenv("CONSOLE")) != NULL) console_dev = s; else { console_dev = CONSOLE; tried_devcons++; } while ((fd = open(console_dev, O_RDONLY|O_NONBLOCK)) < 0) { if (!tried_devcons) { tried_devcons++; console_dev = CONSOLE; continue; } if (!tried_vtmaster) { tried_vtmaster++; console_dev = VT_MASTER; continue; } break; } if (fd < 0) console_dev = "/dev/null"; else close(fd);}/* * Open the console with retries. */int console_open(int mode){ int f, fd = -1; int m; /* * Open device in nonblocking mode. */ m = mode | O_NONBLOCK; /* * Retry the open five times. */ for(f = 0; f < 5; f++) if ((fd = open(console_dev, m)) >= 0) break; if (fd < 0) return fd; /* * Set original flags. */ if (m != mode) fcntl(fd, F_SETFL, mode); return fd;}/* * We got a signal (HUP PWR WINCH ALRM INT) */void signal_handler(int sig){ ADDSET(got_signals, sig);}/* * SIGCHLD: one of our children has died. */void chld_handler(){ CHILD *ch; int pid, st; int saved_errno = errno; /* * Find out which process(es) this was (were) */ while((pid = waitpid(-1, &st, WNOHANG)) != 0) { if (errno == ECHILD) break; for( ch = family; ch; ch = ch->next ) if ( ch->pid == pid && (ch->flags & RUNNING) ) { INITDBG(L_VB, "chld_handler: marked %d as zombie", ch->pid); ADDSET(got_signals, SIGCHLD); ch->exstat = st; ch->flags |= ZOMBIE; if (ch->new) { ch->new->exstat = st; ch->new->flags |= ZOMBIE; } break; } if (ch == NULL) INITDBG(L_VB, "chld_handler: unknown child %d exited.", pid); } errno = saved_errno;}/* * Linux ignores all signals sent to init when the * SIG_DFL handler is installed. Therefore we must catch SIGTSTP * and SIGCONT, or else they won't work.... * * The SIGCONT handler */void cont_handler(){ got_cont = 1;}/* * Fork and dump core in /. */void coredump(void){ static int dumped = 0; struct rlimit rlim; sigset_t mask; if (dumped) return; dumped = 1; if (fork() != 0) return; sigfillset(&mask); sigprocmask(SIG_SETMASK, &mask, NULL); rlim.rlim_cur = RLIM_INFINITY; rlim.rlim_max = RLIM_INFINITY; setrlimit(RLIMIT_CORE, &rlim); chdir("/"); signal(SIGSEGV, SIG_DFL); raise(SIGSEGV); sigdelset(&mask, SIGSEGV); sigprocmask(SIG_SETMASK, &mask, NULL); do_sleep(5); exit(0);}/* * OOPS: segmentation violation! * If we have the info, print where it occured. * Then sleep 30 seconds and try to continue. */#if defined(STACK_DEBUG) && defined(__linux__)void segv_handler(int sig, struct sigcontext ctx){ char *p = ""; int saved_errno = errno; if ((void *)ctx.eip >= (void *)do_sleep && (void *)ctx.eip < (void *)main) p = " (code)"; initlog(L_VB, "PANIC: segmentation violation at %p%s! " "sleeping for 30 seconds.", (void *)ctx.eip, p); coredump(); do_sleep(30); errno = saved_errno;}#elsevoid segv_handler(){ int saved_errno = errno; initlog(L_VB, "PANIC: segmentation violation! sleeping for 30 seconds."); coredump(); do_sleep(30); errno = saved_errno;}#endif/* * The SIGSTOP & SIGTSTP handler */void stop_handler(){ int saved_errno = errno; got_cont = 0;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -