diff options
author | Sergey Poznyakoff <gray@gnu.org.ua> | 2013-01-06 17:04:28 +0200 |
---|---|---|
committer | Sergey Poznyakoff <gray@gnu.org.ua> | 2013-01-06 17:04:28 +0200 |
commit | 1bfa33ac7c167cd863b88a5cac7690d511851e6e (patch) | |
tree | a892e281cba3ea127af5faf5da81ff2cc0c3aae9 /src/sysvinit.c | |
parent | 4f7c28158308563dcad912d87a0031d095d4690a (diff) | |
download | pies-1bfa33ac7c167cd863b88a5cac7690d511851e6e.tar.gz pies-1bfa33ac7c167cd863b88a5cac7690d511851e6e.tar.bz2 |
Fix runlevel transition algorithm, implement SysV-style fifo interface.
* src/prog.h: New file.
* src/Makefile.am: Add new file.
* src/cmdline.opt: New option --telinit (-T).
* src/diag.c (vlogmsg): In sysvin it mode, write
directly to the console. Close it when finished.
* src/pies.c (_cb_initdefault, _cb_runlevels): Use is_valid_runlevel
to check if the specified runlevels are ok.
(main): In sysvinit mode, reset action to ACTION_CONT.
* src/pies.h (progman_filter): New proto.
(progman_accept,register_socket): Change signature.
(deregister_socket): New proto.
(register_program_socket): New proto.
* src/progman.c: Move constant and adatatype definitions to
prog.h
(prog_stop): Remove static qualifier.
(console_open): Likewise.
(progman_accept): Use new socket API.
(progman_stop): Correctly handle timeouts.
(progman_foreach): New function.
* src/socket.c: Register all sockets along with their handlers
in a doubly-linked list.
(sockinst): New struct.
(register_socket,deregister_socket): New functions.
(register_program_socket): New function.
(pies_pause): Traverse the list to find which fd has changed.
Use its registered handler to handle the event.
* src/sysvinit.c: Include prog.h
(is_valid_runlevel): New function.
(sysvinit_fifo_handler,check_fifo): New static functions.
(inittrans): Fix transition algorithm.
(telinit): New function.
Diffstat (limited to 'src/sysvinit.c')
-rw-r--r-- | src/sysvinit.c | 192 |
1 files changed, 188 insertions, 4 deletions
diff --git a/src/sysvinit.c b/src/sysvinit.c index ef765a1..940125f 100644 --- a/src/sysvinit.c +++ b/src/sysvinit.c @@ -15,6 +15,7 @@ along with GNU Pies. If not, see <http://www.gnu.org/licenses/>. */ #include "pies.h" +#include "prog.h" enum boot_state { @@ -124,6 +125,146 @@ enablecomp (struct component *comp, int finished, void *data) return rc; } +static const char valid_runlevel_arg[] = "0123456789SsQqAaBbCcUu"; + +int +is_valid_runlevel (int c) +{ + return !!strchr (valid_runlevel_arg, c); +} + +static void create_fifo (void); + +static int +sysvinit_stop_filter (struct prog *prog, void *data) +{ + switch (prog->v.p.status) + { + case status_enabled: + case status_listener: + prog_stop (prog, SIGTERM); + prog->v.p.status = status_disabled; /* See FIXME, progman.c:2364 */ + break; + } + return 0; +} + +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; + close (fd); + 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); + if (buf.req.runlevel != runlevel) + { + progman_stop (); + dfl_level = buf.req.runlevel; + inittrans (); + } + 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); + close (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); +} + void sysvinit_begin () { @@ -140,9 +281,12 @@ inittrans () static int wait = 0; if (progman_running_p ()) - /* Noting to do if there are processes left in the previous runlevel */ - return 0; - + { + debug (1, ("%s exiting: some components still running",__FUNCTION__)); + /* Noting to do if there are processes left in the previous runlevel */ + return 0; + } + if (runlevel == 0) n = runlevel_index (dfl_level ? dfl_level : initdefault); else @@ -160,6 +304,7 @@ inittrans () boot_state_name[newstate])); boot_state = newstate; trans = 1; + wait = 0; } switch (boot_state) @@ -170,12 +315,13 @@ inittrans () sysvinit_acct (SYSV_ACCT_BOOT, "reboot", "~~", 0, "~"); break; case single0: - case single1: newlevel = 'S'; break; + case single1: case normal: /* boot -> normal */ newlevel = dfl_level ? dfl_level : initdefault; + create_fifo (); } if (newlevel && newlevel != runlevel) { @@ -198,6 +344,7 @@ inittrans () return 1; } progman_sysvinit_enable (enablecomp, NULL); + wait = 0; } return trans; } @@ -219,3 +366,40 @@ is_comp_wait (struct component *comp) return 1; } +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); +} |