/* * economy init, for mastodon * * Copyright (c) 2000 David Parsons. All rights reserved. * * This software is released under a berkeley-style copyright; the * details can be found in the file COPYRIGHT. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "init.h" char *pgm; /* program name, for diagnostics */ char *statusp; /* pointer to status area in argv[0] */ char statusl; /* length of status area in argv[0] */ volatile char runlevel=' '; /* current runlevel */ int sigmask; /* pending local signals */ #define IS_HUP 0x01 /* hup */ #define IS_CAD 0x02 /* c-a-d */ #define IS_CLK 0x04 /* alarm call */ #define IS_USR1 0x08 #define IS_USR2 0x10 #define IS_TSTP 0x20 int children = 0; /* how many children are active? */ int bornagain = 1; /* cleared when init gets a tstp signal */ struct cmd * volatile commands; /* contents of inittab */ jmp_buf goagain; /* runlevel change point */ int orders = -1; /* command fifo */ struct registry *registry = 0; /* * message types: * * say() sends a message to the console * log() sends a notice to syslog (if it's running) * info() sends an info message to syslog (if configured in) */ /* * send a message to the console */ void say(char *fmt, ...) { FILE *f; va_list ptr; if (f = fopen("/dev/console", "a")) { va_start(ptr, fmt); fprintf(f, "INIT: "); vfprintf(f, fmt, ptr); fputc('\n', f); va_end(ptr); fclose(f); } } /* error */ /* * send a message to syslog */ void log(char *fmt, ...) { va_list ptr; va_start(ptr, fmt); openlog("INIT", LOG_CONS, LOG_DAEMON); vsyslog(LOG_NOTICE, fmt, ptr); closelog(); va_end(ptr); } /* log */ /* * send an info message to syslog */ void info(char *fmt, ...) { va_list ptr; #if INFO va_start(ptr, fmt); openlog("INIT", LOG_CONS, LOG_DAEMON); vsyslog(LOG_INFO, fmt, ptr); closelog(); va_end(ptr); #endif } /* * return the name of a given signal */ char * signame(int signal) { static char bfr[20]; switch (signal) { case SIGHUP: return "HUP"; case SIGINT: return "INT"; case SIGQUIT: return "QUIT"; case SIGILL: return "ILL"; case SIGTRAP: return "TRAP"; case SIGABRT: return "ABRT"; case SIGBUS: return "BUS"; case SIGFPE: return "FPE"; case SIGKILL: return "KILL"; case SIGUSR1: return "USR1"; case SIGSEGV: return "SEGV"; case SIGUSR2: return "USR2"; case SIGPIPE: return "PIPE"; case SIGALRM: return "ALRM"; case SIGTERM: return "TERM"; case SIGSTKFLT: return "STKFLT"; case SIGCHLD: return "CHLD"; case SIGCONT: return "CONT"; case SIGSTOP: return "STOP"; case SIGTSTP: return "TSTP"; case SIGTTIN: return "TTIN"; case SIGTTOU: return "TTOU"; case SIGURG: return "URG"; case SIGXCPU: return "XCPU"; case SIGXFSZ: return "XFSZ"; case SIGVTALRM: return "VTALRM"; case SIGPROF: return "PROF"; case SIGWINCH: return "WINCH"; case SIGIO: return "IO"; case SIGPWR: return "PWR"; } sprintf(bfr, "#%d", signal); return bfr; } /* * set up a status in argv[0], so ps can see what init is up to. */ void status(char *fmt, ...) { va_list ptr; if (statusp && statusl > 5) { memset(statusp, 0, statusl); sprintf(statusp, "[%c] ", runlevel); } if (fmt != 0) { va_start(ptr, fmt); if (statusp && statusl > 5) vsnprintf(statusp+4, statusl-4, fmt, ptr); #if INFO vfprintf(stderr, fmt, ptr); putc('\n', stderr); #endif va_end(ptr); } } /* status */ /* * adjust the console for sanity. */ void fix_console(int fd) { struct termios settings; tcflush(fd, TCIFLUSH); /* just in case there's a bunch of input * piled up. */ tcgetattr(fd, &settings); #define CTRL(x) (x - '@') settings.c_cc[VINTR] = CTRL('C'); settings.c_cc[VQUIT] = CTRL('\\'); settings.c_cc[VERASE] = 127; settings.c_cc[VKILL] = CTRL('X'); settings.c_cc[VEOF] = CTRL('D'); settings.c_cc[VSTART] = CTRL('S'); settings.c_cc[VSTOP] = CTRL('Q'); settings.c_cc[VSUSP] = CTRL('Z'); settings.c_cc[VTIME] = 0; settings.c_cc[VMIN] = 1; settings.c_cflag = HUPCL|CS8|CLOCAL; settings.c_iflag = IGNPAR|ICRNL|IXON|IXANY; settings.c_oflag = OPOST|ONLCR; settings.c_lflag = ISIG|ICANON|ECHO|ECHOCTL|ECHOPRT|ECHOKE; tcsetattr(fd, TCSANOW, &settings); } /* fix_console */ /* * blowaway wipes out a cmd chain */ void blowaway(struct cmd* chain) { if (chain) { if (chain->next) blowaway(chain->next); if (chain->argv) free(chain->argv); free(chain); } } /* blowaway */ /* * return the bit in ->when corresponding to a runlevel */ int bit_for_runlevel(char level, int strict) { switch (tolower(level)) { case '*': return strict ? 0 : (AT_HALT|AT_SINGLE|AT_MULTI|AT_REBOOT); case '@': return 0; case BOOT: return AT_BOOT; case '0': case HALT: return AT_HALT; case '1': case SINGLE:return AT_SINGLE; case '2': case '3': case '4': case '5': case MULTI: return AT_MULTI; case '6': case REBOOT:return AT_REBOOT; default: return -1; } } /* bit_for_runlevel */ /* * return the character code for a runlevel */ char char_for_runlevel(int bits) { switch (bits) { case AT_BOOT: return BOOT; case AT_SINGLE: return SINGLE; case AT_MULTI: return MULTI; case AT_REBOOT: return REBOOT; case AT_HALT: return HALT; } return SINGLE; } /* char_for_runlevel */ /* * parse a single inittab line */ static struct { char *name; int how; } nametohow[] = { { "initdefault", DEFAULT }, { "once", ONCE }, { "wait", ONCE }, { "again", AGAIN }, { "respawn", AGAIN }, { "ctrlaltdel", CAD }, { "cad", CAD }, }; #define NR_HOW (sizeof nametohow/sizeof nametohow[0]) static struct cmd* parseline(char *text, struct cmd *chain) { struct cmd * tmp = calloc(1, sizeof tmp[0]); int argc; char ** argv = calloc(50, sizeof argv[0]); char level[20]; char how[20]; static char fmt[80] = { 0 }; unsigned int checksum = 0; char *p, *q; int b; if (tmp == 0 || argv == 0) { say("out of memory parsing inittab"); blowaway(chain); return 0; } if (fmt[0] == 0) { sprintf(fmt, "%%%d[^:]:%%%d[^:]:%%%d[^\n]", sizeof level, sizeof how, sizeof tmp->command); } if (sscanf(text, fmt, level, how, tmp->command) < 2) { log("malformed inittab entry (%s)", text); blowaway(chain); return 0; } for (checksum = 0, p = tmp->command; *p; ++p) { unsigned int carry = checksum >> (8 * (sizeof(checksum)-sizeof(char))); checksum ^= (unsigned char)(*p); checksum <<= sizeof(char)*8; checksum |= carry; } for (p=level; *p; ++p) { b = bit_for_runlevel(*p, 0); if (b < 0) { log("unknown runlevel %c in entry (%s)", text); blowaway(chain); return 0; } else if (b > 0) tmp->when |= b; } for (argc = 0, q = p = tmp->command; *p && (argc < 40); ) { while (isspace(*p)) ++p; if (*p) { argv[argc] = q; while (*p && !isspace(*p)) { if (*p == '"') { ++p; while (*p && *p != '"') *q++ = *p++; if (*p) ++p; } else *q++ = *p++; } if (p > q || *p) { if (*p) ++p; *q++ = 0; } argc++; } else { argv[argc] = 0; break; } } tmp->argv = argv; tmp->argc = argc; for (b=0; b < NR_HOW; b++) if (strcasecmp(how, nametohow[b].name) == 0) { tmp->how = nametohow[b].how; break; } if (b == NR_HOW) { log("unknown how field in entry (%s)", text); blowaway(chain); return 0; } tmp->status = DEAD; tmp->chksum = checksum; if (chain == 0) return tmp; else { struct cmd *tail = chain; while (tail->next != 0) tail = tail->next; tail->next = tmp; return chain; } } /* parseline */ static struct cmd* readinittab(char *inittab) { struct cmd *tab = 0; static char *defaultinit[] = { "s:initdefault:", "s:respawn:/bin/sh", 0 }; FILE *f; if ( (f = fopen(INITTAB, "r")) != 0) { char bfr[200]; char *p; status("reading %s", INITTAB); while (fgets(bfr, sizeof bfr, f)) { strtok(bfr, "\n"); for (p = bfr; *p && isspace(*p); ++p) ; if (*p == '#' || *p == 0) continue; if ( (tab = parseline(p, tab)) == 0) goto failsafe; } fclose(f); } else { int x; status("reading default inittab"); log("cannot open " INITTAB); failsafe: for (x=0; defaultinit[x]; x++) { tab = parseline(defaultinit[x], tab); if (tab == 0) say("default inittab format error!"); } } return tab; } /* readinittab */ /* * kill off all of the processes that init has started */ void killall(int signal) { struct cmd *run; if (children > 0) say("Sending processes the %s signal", signame(signal)); #if 0 if (getpid() == 1) { kill(-1,signal); sleep(1); } #endif for (run = commands; run; run = run->next) if (run->status == ALIVE) kill(run->pid, signal); sleep(1); } /* killall */ /* * ask for the runlevel of our dreams */ char ask_runlevel() { char bfr[80]; char *p; int bit; int size; int fd; char newlevel; if ((fd = open("/dev/console", O_RDWR)) < 0) { log("ask_runlevel cannot open /dev/console: %s", strerror(errno)); return SINGLE; } fix_console(fd); while (1) { write(fd,"New runlevel:", 13); if ( (size = read(fd, bfr, sizeof bfr)) <= 0) { newlevel = SINGLE; break; } bfr[size] = 0; for (p = bfr; *p && isspace(*p); ++p) ; if ((bit=bit_for_runlevel(*p, 1)) > 0) { newlevel = char_for_runlevel(bit); break; } if (*p) { write(fd, "Can't switch to runlevel ", 25); write(fd, bfr, sizeof bfr); } } close(fd); return newlevel; } /* ask_runlevel */ /* * check for a default runlevel, otherwise ask for one */ char default_runlevel() { struct cmd *run; for (run = commands; run; run = run->next) if (run->how == DEFAULT) return char_for_runlevel(run->when); return ask_runlevel(); } /* default_runlevel */ /* * forks off and runs a process */ int run(struct cmd *p) { int x; p->status = ALIVE; time( &(p->started) ); if ( (p->pid = fork()) < 0) { p->status = SUSPENDED; p->window = p->started + RUNDELAY; return -1; } else if (p->pid == 0) { #if 0 close(0); close(1); close(2); open("/dev/console", O_RDWR); dup2(0,1); dup2(0,2); #endif setsid(); for (x=0; x argv[0], p->argv); log("cannot run %s: %s", p->argv[0], strerror(errno)); exit(1); } children++; info("pid %d born + %d sibling%s", p->pid, children-1, (children==2) ? "" : "s"); return 0; } /* run */ /* * kill off commands that are tagged */ void killsome(struct cmd *list, int flags, int harder) { struct cmd *run; status("%s tagged processes", harder ? "kill" : "stop"); for (run = list; run; run = run->next) if ( (run->status == ALIVE) && (run->flags & flags) ) { #if INFO info("kill process %s", run->argv[0]); #endif kill(run->pid, harder ? SIGKILL : SIGTERM); } if (harder) blowaway(list); else { sleep(1); killsome(list, flags, 1); } } /* killsome */ /* * start tagged commands */ void startsome(int flags) { struct cmd *p; status("start tagged processes"); for (p = commands; p; p = p->next) if (p->flags & flags) { if (p->how == AGAIN) { p->window = time(0) + SUSPTIME; p->retries = 0; run(p); } p->flags &= ~flags; } } /* startsome */ /* * reload the inittab and start or stop respawn processes that have appeared * or disappeared */ void reload_inittab() { struct cmd *reload; struct cmd *p, *q; int mask = bit_for_runlevel(runlevel,0); log("re-reading inittab"); bornagain = 1; if ( (reload = readinittab(INITTAB)) == 0) return; status("processing changes"); /* identify old commands */ for (p = commands; p; p = p->next) { for (q = reload; q; q = q->next) if (q->chksum == p->chksum && q->how == p->how && (q->when & mask) == (p->when & mask) ) break; if (q == 0) p->flags |= TAG; } /* identify new commands */ for (p = reload; p; p = p->next) { for (q = commands; q; q = q->next) if (q->chksum == p->chksum && q->how == p->how && (q->when & mask) == (p->when & mask)) { p->status = q->status; p->pid = q->pid; if (p->status == SUSPENDED || p->status == DEAD) p->flags |= TAG; break; } if (q == 0) p->flags |= TAG; } p = commands; commands = reload; killsome(p, TAG, 0); startsome(TAG); status(0); } /* reload_inittab */ /* * blow away the registry */ void unregister(pid_t client) { struct registry *p, *q; for (p = registry; p ; p = q) { q = p->next; free(p->token); free(p); } registry = 0; kill(client, SIGUSR1); } /* unregister */ /* * dump the registry to the named file */ void list_registry(pid_t client, char *destination) { int destf = open(destination, O_WRONLY|O_NDELAY, 0644); struct registry *p; if (destf == -1) { log("can't list registry to %s for client %d", destination, client); kill(client, SIGUSR2); } kill(client, SIGUSR1); for (p = registry; p; p = p->next) write(destf, p->token, strlen(p->token)+1); close(destf); } /* list_registry */ /* * insert_to_registry a token */ int insert_to_registry(char *token) { struct registry *p; for (p = registry; p; p = p->next) if (strcasecmp(p->token, token) == 0) return 1; if ( (p = malloc(sizeof *p)) && (p->token = strdup(token)) ) { p->next = registry; registry = p; return 0; } say("out of memory insert_to_registrying %s"); return 1; } /* insert_to_registry */ /* * halt the computer and give the user the opportunity to either shut it * off or restart it. */ void grind_to_a_stop() { char bfr[1]; int fd; static char blurb[] = "\r\n" "\t\t**** Okay to power off ****\r\n" "\t\t*** or press [enter] to ***\r\n" "\t\t*** restart this system ***\r\n" "\r\n"; reboot(0xfee1dead, 672274793, 0xCDEF0123); /* c-a-d == reboot */ if ((fd = open("/dev/console", O_RDWR)) != -1) { fix_console(fd); write(fd, blurb, strlen(blurb)); sync(); do { read(fd,bfr,1); } while (bfr[0] != '\r' && bfr[0] != '\n'); close(fd); } reboot(0xfee1dead, 672274793, 0); /* c-a-d == sigint */ runlevel = BOOT; } /* grind_to_a_stop */ /* * goodbye_cruel_world() attempts to reboot the machine */ void goodbye_cruel_world() { /* Bye! */ reboot(0xfee1dead, 672274793, 0x01234567); exit(0); } /* goodbye_cruel_world */ /* * open (closing first if necessary) the command fifo */ void open_fifo() { int retries; struct stat st; status("opening " COMMAND); if (orders >= 0) close(orders); for (retries = 0; retries < 10; retries++) { if ((orders = open(COMMAND, O_RDONLY|O_NDELAY)) >= 0) { if (fstat(orders, &st) == 0 && (st.st_mode & S_IFIFO)) return; close(orders); say(COMMAND " exists, but is not a named pipe"); } else if (errno != ENOENT) say("cannot open " COMMAND ": %s", strerror(errno)); if (unlink(COMMAND) != 0 && errno == EROFS) /* don't even bother if we can't write to the filesystem */ break; if (mknod(COMMAND, S_IFIFO|0600, 0) != 0) say("cannot mknod " COMMAND ": %s", strerror(errno)); } } /* open_fifo */ /* * wait for something to happen */ void wait_for_orders(int mask) { struct cmd *p; char bfr[80]; pid_t pid; char cmd; char arg[200]; int size; fd_set readers; while ( (sigmask & mask) != 0) { if (sigmask & IS_CAD) { info("reboot signal"); for (p = commands; p; p = p->next) if (p->how == CAD) run(p); sigmask &= ~IS_CAD; } if (sigmask & IS_CLK) { info("timer went off"); sigmask &= ~IS_CLK; } if (sigmask & IS_HUP) { sigmask &= ~IS_HUP; reload_inittab(); } } status("waiting for fifo"); FD_ZERO(&readers); FD_SET(orders, &readers); if (select(orders+1, &readers, 0, 0, 0) > 0 && FD_ISSET(orders, &readers)) { memset(bfr, 0, sizeof bfr); memset(arg, 0, sizeof arg); if ( (size = read(orders, bfr, sizeof bfr)) > 0) { if (bfr[size-1] == '\n') --size; if (sscanf(bfr, "%d %c %[^\n]", &pid, &cmd, &arg) > 1) { if (arg[0]) info("command %c (%s) from pid %d", cmd, arg, pid); else info("command %c from pid %d", cmd, pid); switch (cmd) { case GO: /* change runlevel */ if (bit_for_runlevel(arg[0],1) > 0) { runlevel = arg[0]; log("pid %d set runlevel to %c", pid, arg[0]); kill(pid, SIGUSR1); longjmp(goagain, cmd); } else kill(pid, SIGUSR2); break; case REGISTER: if (insert_to_registry(arg) == 0) kill(pid, SIGUSR1); else kill(pid, SIGUSR2); break; case LIST: list_registry(pid, arg); break; case UNREGISTER: unregister(pid); break; default: kill(pid, SIGHUP); break; } } } open_fifo(); } status(0); } /* wait_for_orders */ /* * start up all the respawn children for this runlevel */ void respawn() { struct cmd *p; int b = bit_for_runlevel(runlevel,0); for (p = commands; p; p = p->next) if (p->how == AGAIN && p->status == DEAD && (p->when & b) != 0) { p->window = time(0) + SUSPTIME; p->retries = 0; run(p); } } /* respawn */ /* * the dirty work of init */ void go(char *arg) { struct cmd * volatile p; int when; int timer; static char runenv[30]; static char oldenv[30]; volatile int lastrunlevel = 0; runlevel = BOOT; setjmp(goagain); while (1) { bornagain = 1; open_fifo(); if (orders < 0 && runlevel == MULTI) { log("cannot go into multiuser without " COMMAND); runlevel = '?'; } log_runlevel(runlevel, lastrunlevel); sprintf(runenv, "RUNLEVEL=%c", runlevel); putenv(runenv); if (lastrunlevel) { sprintf(oldenv, "LASTRUNLEVEL=%c", lastrunlevel); putenv(oldenv); } lastrunlevel = runlevel; /* kill off all the old processes */ status("kill old processes"); killall(SIGTERM); for (timer = 20; (timer > 0) && (children > 0); --timer) sleep(1); if (children > 0) { killall(SIGKILL); for (timer = 10; (timer > 0) && (children > 0); --timer) sleep(1); } /* and make certain that they are all dead */ for (p=commands; p; p = p->next) if (p->status == ALIVE && kill(p->pid, 0) == 0) { say("process %d will not die -- trying drastic measures", p->pid); kill(p->pid, SIGTERM); } /* now all the old processes are dead. Start up all the processes for * the new runlevel. */ when = bit_for_runlevel(runlevel,0); info("running ONCE jobs"); for (p=commands; p; p = p->next) { if (p->how != ONCE || (p->when & when) == 0) continue; status("starting %s", p->argv[0]); run(p); while (p->status == ALIVE) wait_for_orders(IS_CAD); } info("finished with ONCE jobs"); #if INFO for (p=commands; p; p = p->next) if (p->status == ALIVE) info("job [%s] is still alive", p->argv[0]); #endif status(0); respawn(); while (children > 0) wait_for_orders(IS_CAD|IS_HUP); /* out of respawn children in this runlevel, progress to the next * one */ info("no more children in runlevel %c", runlevel); switch (runlevel) { case BOOT: if (arg && bit_for_runlevel(arg[0],0) > 0) runlevel = arg[0]; else runlevel = default_runlevel(); break; case SINGLE:runlevel = ask_runlevel(); break; case HALT: grind_to_a_stop(); break; case REBOOT:goodbye_cruel_world(); break; default: runlevel = SINGLE; break; } open_fifo(); } } /* go */ /* signal handlers for various important events */ #define Handler(b) void c_ ## b(int sig) { sigmask |= b; } Handler(IS_HUP) Handler(IS_CAD) Handler(IS_USR1) Handler(IS_USR2) Handler(IS_TSTP) /* * catch and mark dying children */ void reaper(int sig) { pid_t corpse; int status; struct cmd *p; time_t now; time(&now); while ( (corpse = waitpid(-1, &status, WNOHANG)) > 0 ) { for (p = commands; p; p = p->next) if (p->status == ALIVE && corpse == p->pid) { p->status = DEAD; children--; info("pid %d died - %d survivor%s", corpse, children, (children==1) ? "" : "s"); if (!bornagain) break; if (now >= (p->started + RUNDELAY)) { p->window = p->started + SUSPTIME; p->retries = 0; } else if ( (++(p->retries) > RETRIES) && (now < p->window) ) { log("process %s respawning too fast; disabled for %d minutes", p->argv[0], SUSPTIME/60); p->status = SUSPENDED; break; } if (p->how == AGAIN && (p->when & bit_for_runlevel(runlevel,0))) run(p); break; } } } /* reaper */ /* * wake up SUSPENDED respawn children */ void awaken(int sig) { struct cmd *p; time_t now; time(&now); for (p = commands; p; p = p->next) if (p->how == AGAIN && p->status == SUSPENDED && (p->when & bit_for_runlevel(runlevel,0)) && now >= p->window ) { p->window = now + SUSPTIME; p->retries = 0; run(p); } alarm(WAKEUP_TIME); } /* awaken */ /* * switch to a given runlevel based on a signal */ void sigrunlevel(int sig) { switch (sig) { case S_MULTI: runlevel = MULTI; break; case S_SINGLE: runlevel = SINGLE; break; case S_HALT: runlevel = HALT; break; case S_REBOOT: runlevel = REBOOT; break; default: log("sigrunlevel() from sig %s", signame(sig)); return; } log("runlevel set to %c by SIG%s", runlevel, signame(sig)); longjmp(goagain, 1); } /* sigrunlevel */ /* * stop respawning */ void fall_from_grace() { bornagain = 0; log("TSTP signal -- no longer respawning"); } /* fall_from_grace */ /* * set everything up */ void initialize(int argc, char **argv) { int x; struct sigaction sig; #define Catch(signal, f) (sig.sa_handler = f), \ (sig.sa_flags=SA_RESTART), \ sigaction(signal, &sig, 0) if (strlen(argv[0]) < 40 || strchr(argv[0], '@') == 0) { /* restart init but with a longer command line */ argv[0] = "init @ "; #if DEBUG execv("init", argv); #else execv("/sbin/init", argv); #endif say("execl of %s failed: %s", argv[0], strerror(errno)); } if ( (statusp = strchr(argv[0], '@')) == 0 ) statusp = argv[0]; statusl = strlen(statusp)-1; status("initialize"); setsid(); sigmask = 0; /* turn off all signals, then turn back on the ones we want */ for (x=1; x < NSIG; x++) signal(x, SIG_IGN); memset(&sig, 0, sizeof sig); Catch(SIGHUP, c_IS_HUP); /* we care about hup (of course) */ Catch(SIGINT, c_IS_CAD); /* software reboot requests */ Catch(SIGTSTP,fall_from_grace); Catch(SIGCHLD,reaper); /* dying children */ Catch(SIGALRM,awaken); /* and alarm clocks */ Catch(S_MULTI,sigrunlevel); /* => switch to runlevel `m' */ Catch(S_SINGLE,sigrunlevel);/* . . . `s' */ Catch(S_HALT,sigrunlevel); /* halt */ Catch(S_REBOOT,sigrunlevel);/* reboot */ reboot(0xfee1dead, 672274793, 0); /* make C-A-D cause a SIGINT */ putenv("PATH=/bin:/usr/bin"); alarm(WAKEUP_TIME); } /* initialize */ /* * tell init to change runlevel */ void telinit(int argc, char **argv) { FILE* command; int status; int signo; int runlevel; char *arg = argv[0]; if (argc <= 0) { fprintf(stderr, "usage: %s [cq%s]", pgm, RUNLEVELS); exit(1); } if (*arg == '-') ++arg; switch (*arg) { case 'Q': case 'q': kill(1,SIGHUP); exit(0); case 'C': case 'c': kill(1,SIGTSTP); exit(0); } if ((runlevel=bit_for_runlevel(*arg,1)) <= 0) { fprintf(stderr, "%s: can't change to runlevel %s", pgm, argv[0]); exit(1); } /* standard runlevels can be switched between via signal */ switch (runlevel) { case AT_MULTI: signo = SIGUSR1; break; case AT_SINGLE:signo = SIGUSR2; break; case AT_HALT: signo = SIGTERM; break; case AT_REBOOT:signo = SIGQUIT; break; default: signo = 0; break; } if (signo) exit( kill(1,signo) ? 1 : 0 ); signal(SIGUSR1, c_IS_USR1); signal(SIGALRM, c_IS_USR2); signal(SIGUSR2, c_IS_USR2); alarm(1); if ((command = fopen(COMMAND, "w")) == 0) { fprintf(stderr, "%s: can't communicate with init", pgm); exit(1); } alarm(0); fprintf(command, "%d %c %s\n", getpid(), GO, argv[0]); fclose(command); alarm(10); wait(&status); if (sigmask & (IS_USR2|IS_CLK)) { fprintf(stderr, "Init refused our request\n"); exit(1); } exit(0); } /* telinit */ void main(int argc, char **argv) { pgm = basename(argv[0]); if (strcasecmp(pgm, "check") == 0) exit (readinittab((argc > 1) ? argv[1] : INITTAB) == 0) ; else if (strcasecmp(pgm, "telinit") == 0) telinit(argc-1, argv+1); else if (getpid() == 1) { initialize(argc, argv); if ( (commands = readinittab(INITTAB)) == 0) { /* we're dead now -- allow c-a-d to work, then loop forever */ reboot(0xfee1dead, 672274793, 0xCDEF0123); while (1) pause(); } go(argv[1]); } else { #ifdef DEBUG initialize(argc, argv); commands = readinittab(INITTAB); #else telinit(argc-1, argv+1); #endif } exit(0); } /* main */