/* This file is part of GNU Pies. Copyright (C) 2013-2016 Sergey Poznyakoff GNU Pies 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 3, or (at your option) any later version. GNU Pies is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with GNU Pies. If not, see . */ #include "pies.h" #include "prog.h" #include #include enum boot_state { sysinit, boot, single, normal, }; static char *boot_state_name[] = { [sysinit] = "sysinit", [boot] = "boot", [single] = "single", [normal] = "normal" }; #define max_boot_state sizeof(boot_state_name) / sizeof (boot_state_name[0]) static char boot_state_str[] = "#*s "; static const char valid_runlevels[] = "0123456789S"; static int boot_trans_tab[max_boot_state][sizeof(valid_runlevels)-1] = { /* 0, 1, 2, 3, 4, */ /* 5, 6, 7, 8, 9, S */ /* sysinit */ { boot, boot, boot, boot, boot, boot, boot, boot, boot, boot, single }, /* boot */ { normal, normal, normal, normal, normal, normal, normal, normal, normal, normal, normal }, /* single */ { boot, boot, boot, boot, boot, boot, boot, boot, boot, boot, single }, /* normal */ { normal, normal, normal, normal, normal, normal, normal, normal, normal, normal, normal }, }; enum boot_state boot_state; int runlevel = 0; int prevlevel = 'N'; int initdefault; /* Default runlevel */ int dfl_level; int emergency_shell; #define DIAG_CON (DIAG_TO_STDERR|DIAG_REOPEN_LOG) int console_open (int mode) { int i, fd; for (i = 0; i < 5; i++) { fd = open (console_device, mode | O_NONBLOCK); if (fd >= 0) { fcntl (fd, F_SETFL, mode); return fd; } } return -1; } void console_stty () { struct termios tty; int fd; if ((fd = console_open (O_RDWR|O_NOCTTY)) < 0) { logmsg (LOG_CRIT, "can't open %s", console_device); return; } 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. */ tcsetattr (fd, TCSANOW, &tty); tcflush(fd, TCIOFLUSH); close (fd); } int askrunlevel () { int fd; char c, lvl = -1; enum { srl_init, srl_char, srl_r, srl_n, srl_skip } srl = srl_init; static const char prompt[] = "\nEnter runlevel: "; console_stty (); fd = console_open (O_RDWR|O_NOCTTY); if (fd == -1) return 'S'; while (1) { if (srl == srl_init) { write (fd, prompt, sizeof (prompt) - 1); if (read (fd, &lvl, 1) != 1) return 'S'; srl = srl_char; } else if (srl == srl_n) { if (is_valid_runlevel (lvl)) break; srl = srl_init; } else { if (read (fd, &c, 1) != 1) { if (!is_valid_runlevel (lvl)) lvl = 'S'; break; } switch (srl) { case srl_char: if (c == '\r') srl = srl_r; else if (c == '\n') srl = srl_n; else srl = srl_skip; break; case srl_r: if (c == '\n') srl = srl_n; else srl = srl_skip; break; case srl_skip: if (c == '\n') srl = srl_init; default: break; } } } close (fd); return toupper (lvl); } static int getinitdefault () { return initdefault ? initdefault : askrunlevel (); } static int runlevel_index (int n) { char *p; if (n == 0) return -1; p = strchr (valid_runlevels, n); if (!p) return -1; return p - valid_runlevels; } struct enstate { int mask; }; static int enablecomp (struct prog *prog, void *data) { struct enstate *s = data; struct component *comp = prog->v.p.comp; switch (boot_state) { case sysinit: return comp->mode == pies_comp_sysinit; case boot: return comp->mode == pies_comp_boot || comp->mode == pies_comp_bootwait; case single: case normal: switch (comp->mode) { case pies_comp_sysinit: case pies_comp_boot: case pies_comp_bootwait: return 0; case pies_comp_ondemand: /* Active flag persists: */ return prog->v.p.active; case pies_comp_powerfail: case pies_comp_powerwait: case pies_comp_powerokwait: case pies_comp_ctrlaltdel: case pies_comp_powerfailnow: case pies_comp_kbrequest: return s && (s->mask & PIES_COMP_MASK (comp->mode)); default: break; } } if (!comp->runlevels) return -1; if (!strchr (comp->runlevels, runlevel)) { if (prog->v.p.status == status_finished && comp->mode != pies_comp_once) prog->v.p.status = status_stopped; return 0; } if (prog->v.p.status == status_finished) return -1; return 1; } static int runlevel_setup_prog (struct prog *prog, void *data) { if (IS_COMPONENT (prog) && !(prog->v.p.comp->flags & CF_DISABLED) && is_sysvinit (prog->v.p.comp)) { int rc = enablecomp (prog, data); if (rc < 0) return 0; prog->v.p.active = rc; if (rc) prog->v.p.wait = is_comp_wait (prog->v.p.comp); debug (2, ("%s: %s%s", prog_tag (prog), prog->v.p.active ? "enabled" : "disabled", prog->v.p.active && prog->v.p.wait ? " (wait)" : "")); } return 0; } void sysvinit_runlevel_setup (int mask) { struct enstate s; s.mask = mask; progman_foreach (runlevel_setup_prog, &s); } static int demand_prog (struct prog *prog, void *data) { int *rl = data; struct component *comp = prog->v.p.comp; if (comp->mode == pies_comp_ondemand && comp->runlevels && strchr (comp->runlevels, *rl)) { prog->v.p.active = 1; debug (1, ("%s: %s", prog_tag (prog), "enabled")); } return 0; } static void sysvinit_demand (int rl) { progman_foreach (demand_prog, &rl); } static const char valid_runlevel_arg[] = "0123456789SsQqAaBbCcUu"; int is_valid_runlevel (int c) { return !!strchr (valid_runlevel_arg, c); } #define ENVAR_CONSOLE "CONSOLE=" #define ENVTMPL_CONSOLE "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" static char env_prevlevel[] = "PREVLEVEL=x"; static char env_runlevel[] = "RUNLEVEL=x"; static char env_console[] = ENVAR_CONSOLE ENVTMPL_CONSOLE; #define NR_ENVHINT 32 char *sysvinit_environ_hint[NR_ENVHINT] = { env_prevlevel, env_runlevel, env_console, "INIT_VERSION=" PACKAGE_STRING, "PATH=/bin:/usr/bin:/sbin:/usr/sbin", NULL }; #define ENVI_PREVLEVEL 0 #define ENVI_RUNLEVEL 1 #define ENVI_CONSOLE 2 #define ENVI_VERSION 3 #define ENVI_PATH 4 #define ENVI_AVAIL 5 static void envsetup () { int i; for (i = 0; i < ENVI_AVAIL; i++) { char *str = sysvinit_environ_hint[i]; switch (i) { case ENVI_PREVLEVEL: str[strlen (str) - 1] = prevlevel; break; case ENVI_RUNLEVEL: str[strlen (str) - 1] = boot_state_str[boot_state] == ' ' ? (runlevel ? runlevel : '#') : boot_state_str[boot_state]; break; case ENVI_CONSOLE: if (strlen (console_device) >= sizeof (ENVTMPL_CONSOLE)) logmsg (LOG_ERR, "console device name too long"); else strcpy (str + sizeof (ENVAR_CONSOLE) - 1, console_device); } } } static void sysvinit_setenv (char const *data, int size) { int i, j; while (size) { char const *var = data; size_t len = strlen (var) + 1; size -= len; if (size < 0) break; data += len; if (strncmp (var, "INIT_", 5) != 0) continue; len = strcspn (var, "="); for (i = ENVI_AVAIL; i < NR_ENVHINT; i++) { char *s = sysvinit_environ_hint[i]; if (s) { for (j = 0; *s && j < len; j++, s++) if (var[j] != *s) break; if (*s != '=' || j != len) continue; free (sysvinit_environ_hint[i]); } if (var[len] == '=') sysvinit_environ_hint[i] = grecs_strdup (var); else for (j = i + 1; j < NR_ENVHINT; j++, i++) sysvinit_environ_hint[i] = sysvinit_environ_hint[j]; break; } } } int sysvinit_set_runlevel (int newlevel) { switch (newlevel) { case 'A': case 'B': case 'C': diagmsg (DIAG_ALL, LOG_INFO, "Activating on-demand level '%c'", newlevel); sysvinit_demand (newlevel); break; default: if (newlevel == runlevel) { } else if (runlevel_index (newlevel) == -1) return -1; else { diagmsg (DIAG_ALL, LOG_INFO, "Switching to runlevel: %c", newlevel); dfl_level = newlevel; raise (SIGALRM); } } return 0; } char *init_fifo = INIT_FIFO; static void create_fifo (void); static void powerfailcmd (int power_stat); static int sysvinit_fifo_handler (int fd, void *data) { static size_t size; union { struct sysvinit_request req; char data[sizeof (struct sysvinit_request)]; } buf; int rc; rc = read (fd, buf.data + size, sizeof (struct sysvinit_request) - size); if (rc == -1) { logmsg (LOG_ERR, _("error reading from %s: %s"), init_fifo, strerror (errno)); size = 0; return 0; } if (rc == 0) { logmsg (LOG_ERR, _("end of file on %s: reopening"), init_fifo); size = 0; deregister_socket (fd); create_fifo (); return 0; } size += rc; if (size == sizeof (struct sysvinit_request)) { if (buf.req.magic != INIT_MAGIC) logmsg (LOG_ERR, _("got invalid initreq")); else { debug (1, ("INITREQ: cmd=%d, runlevel=%d, sleeptime=%d", buf.req.cmd, buf.req.runlevel, buf.req.sleeptime)); switch (buf.req.cmd) { case INIT_CMD_RUNLVL: buf.req.runlevel = toupper (buf.req.runlevel); pies_schedule_action (ACTION_RELOAD); switch (buf.req.runlevel) { case 'Q': break; default: sysvinit_set_runlevel (buf.req.runlevel); } break; case INIT_CMD_SETENV: sysvinit_setenv (buf.req.data, sizeof (buf.req.data)); break; case INIT_CMD_POWERFAIL: powerfailcmd (POWER_STAT_FAIL); break; case INIT_CMD_POWERFAILNOW: powerfailcmd (POWER_STAT_LOW); break; case INIT_CMD_POWEROK: powerfailcmd (POWER_STAT_OK); break; /* FIXME: react on other commands */ } } size = 0; } return 0; } static void create_fifo () { static int fd = -1; struct stat st, fst; if (stat (init_fifo, &st) < 0) { if (errno != ENOENT) { logmsg (LOG_ERR, "cannot stat fifo %s: %s", init_fifo, strerror (errno)); return; } else if (mkfifo (init_fifo, 0600)) { logmsg (LOG_ERR, "cannot create fifo %s: %s", init_fifo, strerror (errno)); return; } } else { if (!S_ISFIFO (st.st_mode)) { logmsg (LOG_ERR, "not a fifo: %s", init_fifo); return; } chmod (init_fifo, 0600); } if (fd != -1) { fstat (fd, &fst); if (fst.st_dev != st.st_dev || fst.st_ino != st.st_ino) deregister_socket (fd); debug (1, ("reopening %s", init_fifo)); } /* Opening the socket in read-write mode ensures we won't get EOF on it when the caller party closes connection (at least on Linux). Nevertheless, the svinit_fifo_handler is prepared for that eventuality, too. */ fd = open (init_fifo, O_RDWR|O_NONBLOCK); if (fd == -1) { logmsg (LOG_ERR, "cannot open %s: %s", init_fifo, strerror (errno)); return; } register_socket (fd, sysvinit_fifo_handler, NULL, NULL, NULL); } static char *try_console[] = { NULL, "/dev/console", "/dev/tty0" }; char *console_device; static void set_console_dev () { int i; for (i = 0; i < ARRAY_SIZE (try_console); i++) { if (try_console[i]) { int fd = open (try_console[i], O_RDONLY|O_NONBLOCK); if (fd >= 0) { close (fd); console_device = try_console[i]; return; } } } /* provide default */ console_device = "/dev/null"; } int sysvinit_sigtrans (int sig, int *pact) { switch (sig) { case SIGINT: *pact = ACTION_CTRLALTDEL; break; case SIGWINCH: *pact = ACTION_KBREQUEST; break; case SIGTERM: case SIGQUIT: /* Ignore these signals. */ *pact = ACTION_CONT; break; case SIGPWR: *pact = ACTION_POWER; break; default: return 0; } return 1; } static void unintr_sleep (unsigned n) { struct timeval tv; tv.tv_sec = n; tv.tv_usec = 0; while (select (0, NULL, NULL, NULL, &tv) < 0 && errno == EINTR) ; } static void start_shell (const char *shell) { pid_t pid, rc; int st; struct sigaction act, old; act.sa_flags = SA_RESTART; act.sa_handler = SIG_DFL; sigemptyset (&act.sa_mask); sigaction (SIGCHLD, &act, &old); pid = fork (); if (pid == -1) { logmsg (LOG_CRIT, "cannot run `%s': fork failed: %s", shell, strerror (errno)); exit (1); } if (pid == 0) { int fd; signal_setup (SIG_DFL); setsid (); fd = console_open (O_RDWR|O_NOCTTY); if (fd < 0) { logmsg (LOG_CRIT, "open(%s): %s", console_device, strerror (errno)); exit (1); } ioctl (fd, TIOCSCTTY, 1); dup (fd); dup (fd); execl (shell, shell, NULL); _exit (127); } while ((rc = wait (&st)) != pid) if (rc == (pid_t) -1 && errno == ECHILD) break; logmsg (LOG_CRIT, "shell finished"); sigaction (SIGCHLD, &old, NULL); } /* Memory allocation functions for INIT. They may not fail, therefore they just retry allocation until it eventually succeeds. */ static void * sysvinit_malloc (size_t size) { void *p; while (!(p = malloc (size))) { logmsg (LOG_ERR, _("out of memory")); unintr_sleep (5); } return p; } static void * sysvinit_realloc (void *ptr, size_t size) { void *p; while (!(p = realloc (ptr, size))) { logmsg (LOG_ERR, _("out of memory")); unintr_sleep (5); } return p; } void sysvinit_begin () { int sigv[] = { SIGINT, SIGPWR, SIGWINCH, }; grecs_malloc_fun = sysvinit_malloc; grecs_realloc_fun = sysvinit_realloc; close (0); close (1); close (2); set_console_dev (); console_stty (); setsid (); envsetup (); sysvinit_runlevel_setup (PIES_COMP_DEFAULT); add_extra_sigv (sigv, ARRAY_SIZE (sigv)); sysvinit_sysdep_begin (); if (emergency_shell) start_shell ("/sbin/sulogin"); } #define IS_RUNNING_DISABLED_PROG(prog) \ (IS_COMPONENT (prog) \ && prog->v.p.status == status_running \ && !prog->v.p.active) static int cond_running_disabled (struct prog *prog, void *data) { return IS_RUNNING_DISABLED_PROG (prog); } static int no_disabled_running (void *data) { return !progman_foreach (cond_running_disabled, NULL); } static int terminate_disabled (struct prog *prog, void *data) { if (IS_RUNNING_DISABLED_PROG (prog)) prog_stop (prog, *(int*)data); return 0; } int inittrans () { int n; int newlevel = 0; enum boot_state newstate; int trans = 0; if (progman_waiting_p ()) /* Noting to do if there are processes left in the previous runlevel */ return 0; if (runlevel == 0) n = runlevel_index (dfl_level ? dfl_level : getinitdefault ()); else n = runlevel_index (runlevel); if (n == -1) n = runlevel_index ('S'); newstate = boot_trans_tab[boot_state][n]; if (newstate != boot_state) { debug (1, ("STATE TRANS: %s -> %s", boot_state_name[boot_state], boot_state_name[newstate])); boot_state = newstate; trans = 1; } switch (boot_state) { case sysinit: break; case boot: sysvinit_acct (SYSV_ACCT_BOOT, "reboot", "~~", 0, "~"); break; case single: newlevel = 'S'; break; case normal: newlevel = dfl_level ? dfl_level : getinitdefault (); if (trans) { /* boot -> normal */ create_fifo (); ctl_open (); diag_setup (DIAG_TO_SYSLOG | DIAG_REOPEN_LOG); } } if (newlevel && newlevel != runlevel) { prevlevel = runlevel ? runlevel : 'N'; debug (1, ("RL TRANS: %c -> %c", prevlevel, newlevel)); sysvinit_acct (SYSV_ACCT_RUNLEVEL, "runlevel", "~~", newlevel + 256 * runlevel, "~"); mf_proctitle_format ("init [%c]", newlevel); runlevel = newlevel; trans = 1; } if (trans) { int sig; envsetup (); sysvinit_runlevel_setup (PIES_COMP_DEFAULT); /* Stop disabled programs */ sig = SIGTERM; diagmsg (DIAG_CON, LOG_INFO, "Sending processes the TERM signal"); progman_foreach (terminate_disabled, &sig); if (progman_wait_until (no_disabled_running, NULL)) { sig = SIGKILL; diagmsg (DIAG_CON, LOG_INFO, "Sending processes the KILL signal"); progman_foreach (terminate_disabled, &sig); progman_wait_until (no_disabled_running, NULL); } pies_schedule_children (PIES_CHLD_WAKEUP); } return trans; } /* Return true if the progman should wait for the component to terminate. */ int is_comp_wait (struct component *comp) { switch (comp->mode) { case pies_comp_sysinit: case pies_comp_bootwait: case pies_comp_wait: case pies_comp_powerwait: case pies_comp_powerfailnow: case pies_comp_powerokwait: case pies_comp_ctrlaltdel: return 1; default: break; } return 0; } int telinit (const char *arg) { int fd; struct sysvinit_request req; if (arg[1] || !is_valid_runlevel (*arg)) { logmsg (LOG_CRIT, "invalid argument"); exit (EX_USAGE); } memset (&req, 0, sizeof (req)); req.magic = INIT_MAGIC; req.cmd = INIT_CMD_RUNLVL; req.runlevel = *arg; #if 0 req.sleeptime = sltime; #endif signal (SIGALRM, SIG_DFL); alarm (5); fd = open (init_fifo, O_WRONLY); if (fd < 0) { logmsg (LOG_ERR, _("can't open %s: %s"), init_fifo, strerror (errno)); exit (EX_UNAVAILABLE); } if (write (fd, &req, sizeof (req)) != sizeof (req)) { logmsg (LOG_ERR, _("error writing to %s: %s"), init_fifo, strerror (errno)); exit (EX_UNAVAILABLE); } alarm (0); close (fd); exit (0); } static char * getfld (char *str, char **endp) { char *p; if (str) { p = strchr (str, ':'); if (p) *p++ = 0; *endp = p; } else *endp = NULL; return str; } struct action_parser { const char *action; enum pies_comp_mode mode; int (*parser) (struct component *comp, const char *file, unsigned line); }; static struct action_parser action_tab[] = { { "wait", pies_comp_wait }, { "once", pies_comp_once }, { "boot", pies_comp_boot }, { "bootwait", pies_comp_bootwait }, { "powerfail", pies_comp_powerfail }, { "powerwait", pies_comp_powerwait }, { "powerokwait", pies_comp_powerokwait }, { "ctrlaltdel", pies_comp_ctrlaltdel }, { "ondemand", pies_comp_ondemand }, { "sysinit", pies_comp_sysinit }, { "powerfailnow", pies_comp_powerfailnow }, { "kbrequest", pies_comp_kbrequest }, { "respawn", pies_comp_respawn }, { NULL } }; static struct action_parser * find_action_parser (const char *action) { struct action_parser *ap; for (ap = action_tab; ap->action; ap++) if (strcmp (ap->action, action) == 0) return ap; return NULL; } static char * strupr (char *s) { char *p; for (p = s; *p; p++) *p = toupper (*p); return s; } int inittab_parse (const char *file) { FILE *fp; size_t size = 0; char *buf = NULL; unsigned line_no = 0; int err = 0; fp = fopen (file, "r"); if (!fp) { logmsg (LOG_ERR, _("cannot open configuration file %s: %s"), file, strerror (errno)); return 1; } while (getline (&buf, &size, fp) >= 0) { char *id, *runlevels, *action, *process, *p; struct action_parser *ap; struct component *comp; struct wordsplit ws; line_no++; for (p = buf; *p && c_isblank (*p); p++) ; if (!p || *p == '\n') continue; if (*p == '#') { if (wordsplit (p+1, &ws, WRDSF_DEFFLAGS)) { logmsg (LOG_ERR, "%s:%u: wordsplit: %s", file, line_no, strerror (errno)); err = 1; } /* pies pragma debug N */ /* pies pragma next FORMAT FILE */ /* pies pragma stop */ if (ws.ws_wordc > 2 && strcmp (ws.ws_wordv[0], "pies") == 0 && strcmp (ws.ws_wordv[1], "pragma") == 0) { if (strcmp (ws.ws_wordv[2], "debug") == 0) { if (ws.ws_wordc == 4) debug_level = atoi (ws.ws_wordv[3]); } else if (strcmp (ws.ws_wordv[2], "next") == 0) { if (ws.ws_wordc >= 5) { struct config_syntax *synt = str_to_config_syntax (ws.ws_wordv[3]); if (!synt) logmsg (LOG_ERR, "%s:%u: %s", file, line_no, _("unknown syntax type")); else config_file_add (synt, ws.ws_wordv[4]); } else if (ws.ws_wordc == 4) config_file_add_type (CONF_PIES, ws.ws_wordv[3]); } else if (strcmp (ws.ws_wordv[2], "stop") == 0) { wordsplit_free (&ws); break; } } wordsplit_free (&ws); continue; } id = getfld (p, &p); runlevels = getfld (p, &p); action = getfld (p, &p); process = p; if (!id || !runlevels || !action || !process) { logmsg (LOG_ERR, "%s:%u: %s", file, line_no, _("not enough fields")); err = 1; continue; } if (strcmp (action, "initdefault") == 0) { if (!runlevels[0] || !is_valid_runlevel (runlevels[0])) { logmsg (LOG_ERR, "%s:%u: %s", file, line_no, _("invalid runlevel")); err = 1; } else initdefault = toupper (runlevels[0]); continue; } if (strcmp (action, "off") == 0) /* Ignore the entry */ continue; ap = find_action_parser (action); if (!ap) { logmsg (LOG_ERR, "%s:%u: %s", file, line_no, _("unknown action")); err = 1; continue; } comp = component_create (id); comp->mode = ap->mode; comp->runlevels = grecs_strdup (strupr (runlevels)); if (wordsplit (process, &ws, WRDSF_DEFFLAGS)) { component_free (comp); logmsg (LOG_ERR, "%s:%u: wordsplit: %s", file, line_no, strerror (errno)); err = 1; continue; } wordsplit_getwords (&ws, &comp->argc, &comp->argv); comp->program = grecs_strdup (comp->argv[0]); wordsplit_free (&ws); comp->flags |= CF_SIGGROUP; if (ap->parser && ap->parser (comp, file, line_no)) { component_free (comp); err = 1; continue; } } free (buf); fclose (fp); return err; } char *power_stat_file = POWER_STAT_FILE; void sysvinit_power (void) { int power_stat = POWER_STAT_FAIL; int fd; fd = open (power_stat_file, O_RDONLY); if (fd >= 0) { char c; switch (read (fd, &c, 1)) { case 1: power_stat = c; break; case 0: logmsg (LOG_NOTICE, _("unexpected EOF on %s"), power_stat_file); break; case -1: logmsg (LOG_ERR, _("error reading from %s: %s"), power_stat_file, strerror (errno)); } close (fd); if (unlink (power_stat_file)) logmsg (LOG_ERR, _("can't unlink %s: %s"), power_stat_file, strerror (errno)); } else debug (1, (_("can't open %s: %s"), power_stat_file, strerror (errno))); powerfailcmd (power_stat); } static void powerfailcmd (int power_stat) { int mask; switch (power_stat) { case POWER_STAT_OK: /* The power is OK */ mask = PIES_COMP_MASK (pies_comp_powerokwait); break; case POWER_STAT_LOW: /* Low battery: shut down now */ mask = PIES_COMP_MASK (pies_comp_powerfailnow); break; default: /* Power failure */ mask = PIES_COMP_MASK (pies_comp_powerfail) | PIES_COMP_MASK (pies_comp_powerwait); } sysvinit_runlevel_setup (mask); } void sysvinit_report (struct json_value *obj) { json_object_set_string (obj, "runlevel", "%c", runlevel); json_object_set_string (obj, "prevlevel", "%c", prevlevel); json_object_set_string (obj, "bootstate", "%s", boot_state_name[boot_state]); if (initdefault) json_object_set_string (obj, "initdefault", "%c", initdefault); } void sysvinit_parse_argv (int argc, char **argv) { while (--argc) { int c; char *arg = *++argv; if (!strcmp (arg, "single") || !strcmp (arg, "-s")) dfl_level = 'S'; else if (!strcmp (arg, "-b") || !strcmp (arg, "emergency")) emergency_shell = 1; else if (!arg[1] && strchr (valid_runlevels, (c = toupper (arg[0])))) dfl_level = c; } }