diff --git a/ChangeLog b/ChangeLog index 1feb9558..f32ff96e 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,6 +1,12 @@ # ChangeLog for Gentoo System Intialization ("rc") scripts # Copyright 1999-2007 Gentoo Foundation; Distributed under the GPLv2 + 25 Apr 2007; Roy Marples : + + We now have an alternative to buffering stdout and stderr. + RC_PREFIX="yes" will put the service name as a prefix to all output + made by the service. Thanks to Ciaran McCreesh for the idea. + 24 Apr 2007; Roy Marples : We now buffer stdout and stderr to a file and flush that when running in diff --git a/conf.d/rc b/conf.d/rc index fb34cbe8..8ea03b4f 100644 --- a/conf.d/rc +++ b/conf.d/rc @@ -1,10 +1,16 @@ # /etc/conf.d/rc: Global config file for the Gentoo RC System # Set to "yes" if you want the rc system to try and start services -# in parallel for a slight speed improvement. NOTE: When enabled -# init script output is buffered and displayed in one go when finished. +# in parallel for a slight speed improvement. RC_PARALLEL="no" +# If we're running in parallel then the output of each service is buffered +# until the service finishes. This is so the output one service is not mixed +# with the output of another service. +# To avoid buffering can prefix each line of output to see the service which +# it belongs to by setting the RC_PREFIX="yes". +RC_PREFIX="no" + # Set RC_INTERACTIVE to "yes" and you'll be able to press the I key during # boot so you can choose to start specific services. Set to "no" to disable # this feature. diff --git a/src/einfo.h b/src/einfo.h index e1953772..a2719a22 100644 --- a/src/einfo.h +++ b/src/einfo.h @@ -64,6 +64,9 @@ int ewendv (int retval, const char *fmt, ...) EINFO_PRINTF (2, 3); void eindentv (void); void eoutdentv (void); +/* Pointer to a string that is always prefixed to einfo/ewarn/error */ +void eprefix (const char *prefix); + /* Handy utils to buffer stdout and stderr so our output is always * sane when forking around. * Don't depend on these being here though as we may take a different diff --git a/src/libeinfo.c b/src/libeinfo.c index a6c0a2a1..4b2f9589 100644 --- a/src/libeinfo.c +++ b/src/libeinfo.c @@ -125,6 +125,9 @@ static int stdfd[2] = {-1, -1}; static FILE *ebfp = NULL; static char ebfile[PATH_MAX] = { '\0' }; +/* A pointer to a string to prefix to einfo/ewarn/eerror messages */ +static const char *_eprefix = NULL; + static bool is_env (const char *var, const char *val) { char *v; @@ -184,6 +187,10 @@ static int get_term_columns (void) return (DEFAULT_COLS); } +void eprefix (const char *prefix) { + _eprefix = prefix; +} + void ebuffer (const char *file) { /* Don't ebuffer if we don't have a file or we already are */ @@ -379,16 +386,18 @@ const char *ecolor (einfo_color_t color) { hidden_def(ecolor) #define EINFOVN(_file, _color) \ -fprintf (_file, " %s*%s ", ecolor (_color), ecolor (ecolor_normal)); \ -retval += _eindent (_file); \ + if (_eprefix) \ + fprintf (_file, "%s%s%s|", ecolor (_color), _eprefix, ecolor (ecolor_normal)); \ + fprintf (_file, " %s*%s ", ecolor (_color), ecolor (ecolor_normal)); \ + retval += _eindent (_file); \ { \ va_list _ap; \ va_copy (_ap, ap); \ retval += vfprintf (_file, fmt, _ap) + 3; \ va_end (_ap); \ } \ -if (colour_terminal ()) \ -fprintf (_file, ECOLOR_FLUSH); + if (colour_terminal ()) \ + fprintf (_file, ECOLOR_FLUSH); static int _einfovn (const char *fmt, va_list ap) { diff --git a/src/rc.c b/src/rc.c index c533a18b..3b9eaf1a 100644 --- a/src/rc.c +++ b/src/rc.c @@ -388,14 +388,15 @@ static void sulogin (bool cont) } #endif + newenv = rc_filter_env (); + if (cont) { int status = 0; - pid_t pid = vfork(); + pid_t pid = vfork (); if (pid == -1) eerrorx ("%s: vfork: %s", applet, strerror (errno)); if (pid == 0) { - newenv = rc_filter_env (); #ifdef __linux__ execle ("/sbin/sulogin", "/sbin/sulogin", getenv ("CONSOLE"), (char *) NULL, newenv); @@ -411,7 +412,6 @@ static void sulogin (bool cont) waitpid (pid, &status, 0); } else { #ifdef __linux - newenv = rc_filter_env (); execle ("/sbin/sulogin", "/sbin/sulogin", getenv ("CONSOLE"), (char *) NULL, newenv); eerrorx ("%s: unable to exec `/sbin/sulogin': %s", applet, strerror (errno)); @@ -542,7 +542,7 @@ static void handle_signal (int sig) kill (pl->pid, SIGTERM); /* Notify plugins we are aborting */ - rc_plugin_run (rc_hook_abort, "rc"); + rc_plugin_run (rc_hook_abort, NULL); /* Only drop into single user mode if we're booting */ run = getenv ("RUNLEVEL"); diff --git a/src/runscript.c b/src/runscript.c index e1d880b5..3a9c9d7b 100644 --- a/src/runscript.c +++ b/src/runscript.c @@ -9,10 +9,12 @@ #define APPLET "runscript" #include +#include #include #include #include #include +#include #include #include #include @@ -50,6 +52,9 @@ static char *ibsave = NULL; static bool in_background = false; static rc_hook_t hook_out = 0; static pid_t service_pid = 0; +static char *prefix = NULL; + +/* Pipes for prefixed output */ extern char **environ; @@ -57,11 +62,11 @@ extern char **environ; static void (*selinux_run_init_old) (void); static void (*selinux_run_init_new) (int argc, char **argv); -void setup_selinux (int argc, char **argv); +static void setup_selinux (int argc, char **argv); #endif #ifdef __linux__ -void setup_selinux (int argc, char **argv) +static void setup_selinux (int argc, char **argv) { void *lib_handle = NULL; @@ -205,24 +210,15 @@ static void cleanup (void) rc_plugin_run (hook_out, applet); rc_plugin_unload (); - if (deptree) - rc_free_deptree (deptree); - if (services) - rc_strlist_free (services); - if (types) - rc_strlist_free (types); - if (svclist) - rc_strlist_free (svclist); - if (providelist) - rc_strlist_free (providelist); - if (restart_services) - rc_strlist_free (restart_services); - if (need_services) - rc_strlist_free (need_services); - if (tmplist) - rc_strlist_free (tmplist); - if (ibsave) - free (ibsave); + rc_free_deptree (deptree); + rc_strlist_free (services); + rc_strlist_free (types); + rc_strlist_free (svclist); + rc_strlist_free (providelist); + rc_strlist_free (restart_services); + rc_strlist_free (need_services); + rc_strlist_free (tmplist); + free (ibsave); if (in_control ()) { if (rc_service_state (applet, rc_service_stopping)) { @@ -248,29 +244,71 @@ static void cleanup (void) unlink (exclusive); } - if (env) - rc_strlist_free (env); + rc_strlist_free (env); if (mtime_test) { unlink (mtime_test); free (mtime_test); } - if (exclusive) - free (exclusive); + free (exclusive); + free (applet); + free (prefix); +} - if (applet) - free (applet); +static int write_prefix (int fd, const char *buffer, size_t bytes, bool *prefixed) { + unsigned int i; + int j; + const char *ec; + const char *ec_normal = ecolor (ecolor_normal); + ssize_t ret = 0; + + if (fd == fileno (stdout)) + ec = ecolor (ecolor_hilite); + else + ec = ecolor (ecolor_bad); + + for (i = 0; i < bytes; i++) { + /* We don't prefix escape codes, like eend */ + if (buffer[i] == '\033') + *prefixed = true; + + if (! *prefixed) { + ret += write (fd, ec, strlen (ec)); + ret += write (fd, applet, strlen (applet)); + for (j = strlen (applet); j < 11; j++) + ret += write (fd, " ", 1); + ret += write (fd, ec_normal, strlen (ec_normal)); + ret += write (fd, " |", 2); + *prefixed = true; + } + + if (buffer[i] == '\n') + *prefixed = false; + ret += write (fd, buffer + i, 1); + } + + return (ret); } static bool svc_exec (const char *service, const char *arg1, const char *arg2) { - bool retval; + bool execok; + int stdout_pipes[2]; + int stderr_pipes[2]; /* To ensure any output has hit our ebuffer */ fflush (stdout); fflush (stderr); + /* Setup our pipes for prefixed output */ + if (rc_is_env ("RC_PREFIX", "yes")) { + if (pipe (stdout_pipes)) + eerror ("pipe: %s", strerror (errno)); + if (pipe (stderr_pipes)) + eerror ("pipe: %s", strerror (errno)); + } + /* We need to disable our child signal handler now so we block until our script returns. */ signal (SIGCHLD, NULL); @@ -280,6 +318,25 @@ static bool svc_exec (const char *service, const char *arg1, const char *arg2) if (service_pid == -1) eerrorx ("%s: vfork: %s", service, strerror (errno)); if (service_pid == 0) { + if (rc_is_env ("RC_PREFIX", "yes")) { + int flags; + + if (dup2 (stdout_pipes[1], fileno (stdout)) == -1) + eerror ("dup2 stdout: %s", strerror (errno)); + close (stdout_pipes[0]); + if (dup2 (stderr_pipes[1], fileno (stderr)) == -1) + eerror ("dup2 stderr: %s", strerror (errno)); + close (stderr_pipes[0]); + + /* Stop any scripts from inheriting us */ + if ((flags = fcntl (stdout_pipes[1], F_GETFD, 0)) < 0 || + fcntl (stdout_pipes[1], F_SETFD, flags | FD_CLOEXEC) < 0) + eerror ("fcntl: %s", strerror (errno)); + if ((flags = fcntl (stderr_pipes[1], F_GETFD, 0)) < 0 || + fcntl (stderr_pipes[1], F_SETFD, flags | FD_CLOEXEC) < 0) + eerror ("fcntl: %s", strerror (errno)); + } + if (rc_exists (RC_SVCDIR "runscript.sh")) { execl (RC_SVCDIR "runscript.sh", service, service, arg1, arg2, (char *) NULL); @@ -295,13 +352,66 @@ static bool svc_exec (const char *service, const char *arg1, const char *arg2) } } - retval = rc_waitpid (service_pid) == 0 ? true : false; + /* Prefix our piped output */ + if (rc_is_env ("RC_PREFIX", "yes")) { + bool stdout_done = false; + bool stdout_prefix_shown = false; + bool stderr_done = false; + bool stderr_prefix_shown = false; + char buffer[RC_LINEBUFFER]; + close (stdout_pipes[1]); + close (stderr_pipes[1]); + + memset (buffer, 0, RC_LINEBUFFER); + while (! stdout_done && ! stderr_done) { + fd_set fds; + int retval; + + FD_ZERO (&fds); + FD_SET (stdout_pipes[0], &fds); + FD_SET (stderr_pipes[0], &fds); + retval = select (MAX (stdout_pipes[0], stderr_pipes[0]) + 1, + &fds, 0, 0, 0); + if (retval < 0) { + if (errno != EINTR) + eerror ("select: %s", strerror (errno)); + break; + } else if (retval) { + ssize_t nr; + + if (FD_ISSET (stdout_pipes[0], &fds)) { + if ((nr = read (stdout_pipes[0], buffer, + sizeof (buffer))) <= 0) + stdout_done = true; + else + write_prefix (fileno (stdout), buffer, nr, + &stdout_prefix_shown); + } + + if (FD_ISSET (stderr_pipes[0], &fds)) { + if ((nr = read (stderr_pipes[0], buffer, + sizeof (buffer))) <= 0) + stderr_done = true; + else + write_prefix (fileno (stderr), buffer, nr, + &stderr_prefix_shown); + } + } + } + + /* Done now, so close the pipes */ + close(stdout_pipes[0]); + close(stderr_pipes[0]); + } + + execok = rc_waitpid (service_pid) == 0 ? true : false; service_pid = 0; + /* Done, so restore the signal handler */ signal (SIGCHLD, handle_signal); - return (retval); + return (execok); } static rc_service_state_t svc_status (const char *service) @@ -899,7 +1009,18 @@ int main (int argc, char **argv) snprintf (pid, sizeof (pid), "%d", (int) getpid ()); setenv ("RC_RUNSCRIPT_PID", pid, 1); - if (rc_is_env ("RC_PARALLEL", "yes")) { + /* eprefix is kinda klunky, but it works for our purposes */ + if (rc_is_env ("RC_PREFIX", "yes")) { + int l = strlen (applet); + if (l < 13) + l = 13; + prefix = rc_xmalloc (sizeof (char *) * l); + snprintf (prefix, l, "%s%s", applet, " "); + eprefix (prefix); + } + + /* If we're in parallel and we're not prefixing then we need the ebuffer */ + if (rc_is_env ("RC_PARALLEL", "yes") && ! rc_is_env ("RC_PREFIX", "yes")) { char ebname[PATH_MAX]; char *eb;