diff options
-rw-r--r-- | src/Makefile.am | 3 | ||||
-rw-r--r-- | src/cmdline.opt | 9 | ||||
-rw-r--r-- | src/diag.c | 31 | ||||
-rw-r--r-- | src/pies.c | 16 | ||||
-rw-r--r-- | src/pies.h | 36 | ||||
-rw-r--r-- | src/prog.h | 85 | ||||
-rw-r--r-- | src/progman.c | 125 | ||||
-rw-r--r-- | src/socket.c | 106 | ||||
-rw-r--r-- | src/sysvinit.c | 192 |
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) @@ -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); } @@ -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; + } } @@ -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); +} |