aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorSergey Poznyakoff <gray@gnu.org.ua>2013-01-06 17:04:28 +0200
committerSergey Poznyakoff <gray@gnu.org.ua>2013-01-06 17:04:28 +0200
commit1bfa33ac7c167cd863b88a5cac7690d511851e6e (patch)
treea892e281cba3ea127af5faf5da81ff2cc0c3aae9 /src
parent4f7c28158308563dcad912d87a0031d095d4690a (diff)
downloadpies-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')
-rw-r--r--src/Makefile.am3
-rw-r--r--src/cmdline.opt9
-rw-r--r--src/diag.c31
-rw-r--r--src/pies.c16
-rw-r--r--src/pies.h36
-rw-r--r--src/prog.h85
-rw-r--r--src/progman.c125
-rw-r--r--src/socket.c106
-rw-r--r--src/sysvinit.c192
9 files changed, 488 insertions, 115 deletions
diff --git a/src/Makefile.am b/src/Makefile.am
index 3752eae..9e9bf80 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -42,3 +42,4 @@ noinst_HEADERS = \
meta1lex.h\
- pies.h
+ pies.h\
+ prog.h
diff --git a/src/cmdline.opt b/src/cmdline.opt
index 31fcf20..76fc4da 100644
--- a/src/cmdline.opt
+++ b/src/cmdline.opt
@@ -112,3 +112,10 @@ BEGIN
END
-
+
+OPTION(telinit,T,RUNLEVEL,
+ [<emulate telinit command>])
+BEGIN
+ log_to_stderr_only = 1;
+ exit (telinit (optarg));
+END
+
GROUP(Preprocessor)
diff --git a/src/diag.c b/src/diag.c
index fe057d0..bb64ab9 100644
--- a/src/diag.c
+++ b/src/diag.c
@@ -44,2 +44,23 @@ syslog_printer (int prio, const char *fmt, va_list ap)
+static FILE *
+stderr_open ()
+{
+ if (!init_process)
+ return stderr;
+ else
+ {
+ int fd = console_open (O_WRONLY|O_NOCTTY|O_NDELAY);
+ if (fd == -1)
+ return NULL;
+ return fdopen (fd, "w");
+ }
+}
+
+static void
+stderr_close (FILE *fp)
+{
+ if (init_process)
+ fclose (fp);
+}
+
void
@@ -50,7 +71,11 @@ vlogmsg (int prio, const char *fmt, va_list ap)
va_list aq;
- fprintf (stderr, "%s: ", program_name);
+ FILE *fp = stderr_open ();
+ if (!fp)
+ return;
+ fprintf (fp, "%s: ", program_name);
va_copy (aq, ap);
- vfprintf (stderr, fmt, aq);
+ vfprintf (fp, fmt, aq);
va_end (aq);
- fprintf (stderr, "\n");
+ fprintf (fp, "\n");
+ stderr_close (fp);
}
diff --git a/src/pies.c b/src/pies.c
index a2dc5e0..41ca486 100644
--- a/src/pies.c
+++ b/src/pies.c
@@ -961,4 +961,2 @@ _cb_flags (enum grecs_callback_command cmd,
-static const char valid_runlevels[] = "0123456789Ss";
-
static int
@@ -977,3 +975,3 @@ _cb_initdefault (enum grecs_callback_command cmd,
}
- if (!strchr (valid_runlevels, value->v.string[0]))
+ if (!is_valid_runlevel (value->v.string[0]))
{
@@ -997,3 +995,3 @@ _cb_runlevels (enum grecs_callback_command cmd,
{
- if (!strchr (valid_runlevels, *p))
+ if (!is_valid_runlevel (*p))
{
@@ -2334,3 +2332,3 @@ main (int argc, char **argv)
}
-
+
logmsg (LOG_INFO, _("%s %s starting"), proginfo.package, proginfo.version);
@@ -2383,2 +2381,10 @@ main (int argc, char **argv)
break;
+
+ case ACTION_STOP:
+ case ACTION_RESTART:
+ if (init_process)
+ {
+ debug (1, ("ignoring stop/restart"));
+ action = ACTION_CONT;
+ }
}
diff --git a/src/pies.h b/src/pies.h
index e7ad1c6..2203d87 100644
--- a/src/pies.h
+++ b/src/pies.h
@@ -287,2 +287,4 @@ void progman_stop (void);
void progman_cleanup (int expect_term);
+void progman_filter (int (*filter) (struct component *, void *data),
+ void *data);
void progman_stop_component (const char *name);
@@ -291,3 +293,3 @@ void progman_dump_prereq (void);
void progman_dump_depmap (void);
-int progman_accept (int socket);
+int progman_accept (int socket, void *data);
int progman_build_depmap (void);
@@ -364,3 +366,6 @@ const char * pies_url_get_arg (struct pies_url *url, const char *argname);
void pies_pause (void);
-int register_socket (int socktype, int fd);
+void *register_socket (int fd, int (*handler) (int, void *), void *data);
+void deregister_socket (int fd);
+
+int register_program_socket (int socktype, int fd, void *data);
int pass_fd (const char *socket, int fd, unsigned time_out);
@@ -458,2 +463,29 @@ int inittrans (void);
int is_comp_wait (struct component *comp);
+int is_valid_runlevel (int c);
+
+#ifndef INIT_FIFO
+# define INIT_FIFO "/dev/initctl"
+#endif
+
+#define INIT_MAGIC 0x03091969
+#define INIT_CMD_START 0
+#define INIT_CMD_RUNLVL 1
+#define INIT_CMD_POWERFAIL 2
+#define INIT_CMD_POWERFAILNOW 3
+#define INIT_CMD_POWEROK 4
+#define INIT_CMD_BSD 5
+#define INIT_CMD_SETENV 6
+#define INIT_CMD_UNSETENV 7
+
+#define INIT_CMD_CHANGECONS 12345
+
+struct sysvinit_request {
+ int magic; /* Magic number */
+ int cmd; /* What kind of request */
+ int runlevel; /* Runlevel to change to */
+ int sleeptime; /* Time between TERM and KILL */
+ char pad[368];
+};
+
+
diff --git a/src/prog.h b/src/prog.h
new file mode 100644
index 0000000..2fd4c47
--- /dev/null
+++ b/src/prog.h
@@ -0,0 +1,85 @@
+/* This file is part of GNU Pies.
+ Copyright (C) 2008, 2009, 2010, 2011 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 <http://www.gnu.org/licenses/>. */
+
+enum prog_type
+ {
+ TYPE_COMPONENT,
+ TYPE_REDIRECTOR,
+ TYPE_COMMAND
+ };
+
+enum prog_status
+ {
+ status_enabled, /* Component enabled. prog->pid!=0 shows if it is
+ actually running */
+ status_disabled, /* Component is disabled. */
+ status_listener, /* Component is an inetd listener */
+ status_sleeping, /* Component is sleeping. An attempt to start it will
+ be made at prog->v.p.timestamp + SLEEPTIME */
+ status_stopping, /* Component is being stopped */
+ status_finished, /* A "once" component has finished */
+ };
+
+struct conn_class
+{
+ const char *tag;
+ union pies_sockaddr_storage sa_storage;
+ size_t sa_len;
+ size_t count;
+};
+
+struct prog
+{
+ struct prog *next, *prev;
+ enum prog_type type;
+ pid_t pid; /* PID */
+ char *tag; /* Entry tag (for diagnostics purposes) */
+ char **prereq;
+ int facility;
+ union
+ {
+ struct
+ {
+ struct component *comp;
+ size_t idx; /* Numeric identifier */
+ int socket;
+ struct prog *redir[2]; /* Pointers to redirectors */
+ time_t timestamp; /* Time of last startup */
+ size_t failcount; /* Number of failed starts since timestamp */
+ enum prog_status status; /* Current component status */
+ char *runlevels;
+ /* If status == status_listener: */
+ size_t num_instances; /* Number of running instances */
+ /* If comp->type == pies_comp_inetd && status == status_enabled */
+ struct prog *listener;
+ union pies_sockaddr_storage sa_storage;
+ size_t sa_len;
+ struct conn_class *cclass;
+ } p;
+
+ struct
+ {
+ struct prog *master;
+ } r;
+
+ struct
+ {
+ char *command;
+ } c;
+ } v;
+};
+
+void progman_foreach (int (*filter) (struct prog *, void *data), void *data);
diff --git a/src/progman.c b/src/progman.c
index 97216b8..2b375fe 100644
--- a/src/progman.c
+++ b/src/progman.c
@@ -18,70 +18,3 @@
#include <termios.h>
-
-enum prog_type
- {
- TYPE_COMPONENT,
- TYPE_REDIRECTOR,
- TYPE_COMMAND
- };
-
-enum prog_status
- {
- status_enabled, /* Component enabled. prog->pid!=0 shows if it is
- actually running */
- status_disabled, /* Component is disabled. */
- status_listener, /* Component is an inetd listener */
- status_sleeping, /* Component is sleeping. An attempt to start it will
- be made at prog->v.p.timestamp + SLEEPTIME */
- status_stopping, /* Component is being stopped */
- status_finished, /* A "once" component has finished */
- };
-
-struct conn_class
-{
- const char *tag;
- union pies_sockaddr_storage sa_storage;
- size_t sa_len;
- size_t count;
-};
-
-struct prog
-{
- struct prog *next, *prev;
- enum prog_type type;
- pid_t pid; /* PID */
- char *tag; /* Entry tag (for diagnostics purposes) */
- char **prereq;
- int facility;
- union
- {
- struct
- {
- struct component *comp;
- size_t idx; /* Numeric identifier */
- int socket;
- struct prog *redir[2]; /* Pointers to redirectors */
- time_t timestamp; /* Time of last startup */
- size_t failcount; /* Number of failed starts since timestamp */
- enum prog_status status; /* Current component status */
- char *runlevels;
- /* If status == status_listener: */
- size_t num_instances; /* Number of running instances */
- /* If comp->type == pies_comp_inetd && status == status_enabled */
- struct prog *listener;
- union pies_sockaddr_storage sa_storage;
- size_t sa_len;
- struct conn_class *cclass;
- } p;
-
- struct
- {
- struct prog *master;
- } r;
-
- struct
- {
- char *command;
- } c;
- } v;
-};
+#include "prog.h"
@@ -211,3 +144,3 @@ prog_lookup_by_idx (unsigned idx)
-static void prog_stop (struct prog *prog, int sig);
+void prog_stop (struct prog *prog, int sig);
static int prog_start_prerequisites (struct prog *prog);
@@ -1141,3 +1074,3 @@ progman_run_comp (struct component *comp, int fd,
-static int
+int
console_open (int mode)
@@ -1567,11 +1500,5 @@ _prog_wait (struct prog *p)
int
-progman_accept (int socket)
+progman_accept (int socket, void *data)
{
- struct prog *p = prog_lookup_by_socket (socket);
- if (!p)
- {
- logmsg (LOG_EMERG,
- _("INTERNAL ERROR: no matching prog for fd %d"), socket);
- return 1;
- }
+ struct prog *p = data;
@@ -1758,3 +1685,3 @@ progman_create_sockets ()
prog->v.p.status = status_disabled;
- else if (register_socket (comp->socket_type, fd))
+ else if (register_program_socket (comp->socket_type, fd, prog))
{
@@ -1991,3 +1918,3 @@ prog_stop_dependents (struct prog *prog)
-static void
+void
prog_stop (struct prog *prog, int sig)
@@ -2025,9 +1952,8 @@ prog_stop_all (int sig)
-void
-progman_stop ()
+static int
+progman_wait ()
{
- unsigned long i;
+ time_t start = time (NULL);
- prog_stop_all (SIGTERM);
- for (i = 0; i < shutdown_timeout; i++)
+ do
{
@@ -2035,6 +1961,18 @@ progman_stop ()
if (progman_running_count () == 0)
- return;
+ return 0;
sleep (1);
}
- prog_stop_all (SIGKILL);
+ while (time (NULL) - start < shutdown_timeout);
+ return 1;
+}
+
+void
+progman_stop ()
+{
+ prog_stop_all (SIGTERM);
+ if (progman_wait ())
+ {
+ prog_stop_all (SIGKILL);
+ progman_wait ();
+ }
}
@@ -2536,2 +2474,11 @@ progman_cleanup (int expect_term)
void
+progman_foreach (int (*filter) (struct prog *, void *data), void *data)
+{
+ struct prog *prog;
+ for (prog = proghead; prog; prog = prog->next)
+ if (IS_COMPONENT (prog) && filter (prog, data))
+ break;
+}
+
+void
progman_stop_component (const char *name)
@@ -2540,3 +2487,2 @@ progman_stop_component (const char *name)
- logmsg (LOG_INFO, _("stopping component `%s'"), name);
for (prog = proghead; prog; prog = prog->next)
@@ -2544,2 +2490,3 @@ progman_stop_component (const char *name)
{
+ logmsg (LOG_INFO, _("stopping component `%s'"), prog->tag);
switch (prog->v.p.status)
@@ -2563,3 +2510,3 @@ progman_stop_component (const char *name)
_("stopping component `%s': component not started"),
- name);
+ prog->tag);
}
diff --git a/src/socket.c b/src/socket.c
index e32701f..fdce7d1 100644
--- a/src/socket.c
+++ b/src/socket.c
@@ -397,4 +397,90 @@ int fd_max;
+struct sockinst
+{
+ struct sockinst *prev, *next;
+ int fd;
+ int (*handler) (int, void *);
+ void *data;
+};
+
+struct sockinst *si_head, *si_tail;
+
+static struct sockinst *
+find_socket (int fd)
+{
+ struct sockinst *sp;
+
+ for (sp = si_head; sp; sp = sp->next)
+ if (sp->fd == fd)
+ break;
+ return sp;
+}
+
+struct sockinst *
+find_socket_handler (int (*handler) (int, void *))
+{
+ struct sockinst *sp;
+
+ for (sp = si_head; sp; sp = sp->next)
+ if (sp->handler == handler)
+ break;
+ return sp;
+}
+
+static int
+calc_fd_max ()
+{
+ struct sockinst *sp;
+
+ fd_max = -1;
+ for (sp = si_head; sp; sp = sp->next)
+ if (sp->fd > fd_max)
+ fd_max = sp->fd;
+}
+
+void *
+register_socket (int fd, int (*handler) (int, void *), void *data)
+{
+ struct sockinst *sip = xmalloc (sizeof *sip);
+ sip->fd = fd;
+ sip->handler = handler;
+ sip->data = data;
+ sip->next = NULL;
+ sip->prev = si_tail;
+ if (si_tail)
+ si_tail->next = sip;
+ else
+ si_head = sip;
+ si_tail = sip;
+ FD_SET (fd, &listenset);
+ if (fd_max == -1)
+ calc_fd_max ();
+ else if (fd > fd_max)
+ fd_max = fd;
+ return sip;
+}
+
+void
+deregister_socket (int fd)
+{
+ struct sockinst *sp = find_socket (fd);
+
+ if (!sp)
+ return;
+ if (sp->prev)
+ sp->prev->next = sp->next;
+ else
+ si_head = sp->next;
+ if (sp->next)
+ sp->next->prev = sp->prev;
+ else
+ si_tail = sp->prev;
+ free (sp);
+ FD_CLR (fd, &listenset);
+ fd_max = -1;
+}
+
+
int
-register_socket (int socktype, int fd)
+register_program_socket (int socktype, int fd, void *data)
{
@@ -405,5 +491,3 @@ register_socket (int socktype, int fd)
}
- FD_SET (fd, &listenset);
- if (fd > fd_max)
- fd_max = fd;
+ register_socket (fd, progman_accept, data);
return 0;
@@ -432,2 +516,5 @@ pies_pause ()
{
+ if (fd_max == -1)
+ calc_fd_max ();
+
while (1)
@@ -438,8 +525,7 @@ pies_pause ()
{
- int i;
- for (i = 0; i <= fd_max; i++)
- {
- if (FD_ISSET (i, &rdset))
- progman_accept (i);
- }
+ struct sockinst *sp;
+
+ for (sp = si_head; sp; sp = sp->next)
+ if (FD_ISSET (sp->fd, &rdset))
+ sp->handler (sp->fd, sp->data);
break;
diff --git a/src/sysvinit.c b/src/sysvinit.c
index ef765a1..940125f 100644
--- a/src/sysvinit.c
+++ b/src/sysvinit.c
@@ -17,2 +17,3 @@
#include "pies.h"
+#include "prog.h"
@@ -126,2 +127,142 @@ enablecomp (struct component *comp, int finished, void *data)
+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
@@ -142,5 +283,8 @@ inittrans ()
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)
@@ -162,2 +306,3 @@ inittrans ()
trans = 1;
+ wait = 0;
}
@@ -172,5 +317,5 @@ inittrans ()
case single0:
- case single1:
newlevel = 'S';
break;
+ case single1:
case normal:
@@ -178,2 +323,3 @@ inittrans ()
newlevel = dfl_level ? dfl_level : initdefault;
+ create_fifo ();
}
@@ -200,2 +346,3 @@ inittrans ()
progman_sysvinit_enable (enablecomp, NULL);
+ wait = 0;
}
@@ -221 +368,38 @@ is_comp_wait (struct component *comp)
+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);
+}

Return to:

Send suggestions and report system problems to the System administrator.