From a44abb9577a3ccb9202b84765d4b4c1746e6963d Mon Sep 17 00:00:00 2001 From: Roy Marples Date: Tue, 24 Apr 2007 11:29:19 +0000 Subject: [PATCH] We now buffer stdout and stderr to a file and flush that when running in parallel. RC_PARALLEL_STARTUP has been renamed to RC_PARALLEL. --- ChangeLog | 5 + conf.d/rc | 2 +- sh/functions.sh | 18 +-- src/einfo.h | 10 +- src/libeinfo.c | 320 +++++++++++++++++------------------------------ src/librc-misc.c | 14 +++ src/rc.c | 12 +- src/runscript.c | 16 ++- 8 files changed, 158 insertions(+), 239 deletions(-) diff --git a/ChangeLog b/ChangeLog index d1860c12..1feb9558 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,6 +1,11 @@ # ChangeLog for Gentoo System Intialization ("rc") scripts # Copyright 1999-2007 Gentoo Foundation; Distributed under the GPLv2 + 24 Apr 2007; Roy Marples : + + We now buffer stdout and stderr to a file and flush that when running in + parallel. RC_PARALLEL_STARTUP has been renamed to RC_PARALLEL. + 20 Apr 2007; Mike Frysinger : Automatically mount selinuxfs on /selinux in localmount. diff --git a/conf.d/rc b/conf.d/rc index 25203f3e..fb34cbe8 100644 --- a/conf.d/rc +++ b/conf.d/rc @@ -3,7 +3,7 @@ # 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. -RC_PARALLEL_STARTUP="no" +RC_PARALLEL="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 diff --git a/sh/functions.sh b/sh/functions.sh index 99c937e1..69c73cdc 100644 --- a/sh/functions.sh +++ b/sh/functions.sh @@ -6,22 +6,14 @@ RC_GOT_FUNCTIONS="yes" eindent() { - if [ -n "${RC_EBUFFER}" ] ; then - "${RC_LIBDIR}"/bin/eindent - else - RC_EINDENT=$((${RC_EINDENT:-0} + 2)) - [ "${RC_EINDENT}" -gt 40 ] && RC_EINDENT=40 - export RC_EINDENT - fi + RC_EINDENT=$((${RC_EINDENT:-0} + 2)) + [ "${RC_EINDENT}" -gt 40 ] && RC_EINDENT=40 + export RC_EINDENT } eoutdent() { - if [ -n "${RC_EBUFFER}" ] ; then - "${RC_LIBDIR}"/bin/eoutdent - else - RC_EINDENT=$((${RC_EINDENT:-0} - 2)) - [ "${RC_EINDENT}" -lt 0 ] && RC_EINDENT=0 - fi + RC_EINDENT=$((${RC_EINDENT:-0} - 2)) + [ "${RC_EINDENT}" -lt 0 ] && RC_EINDENT=0 return 0 } diff --git a/src/einfo.h b/src/einfo.h index a99af240..e1953772 100644 --- a/src/einfo.h +++ b/src/einfo.h @@ -64,8 +64,12 @@ int ewendv (int retval, const char *fmt, ...) EINFO_PRINTF (2, 3); void eindentv (void); void eoutdentv (void); -/* If RC_EBUFFER is set, then we buffer all the above commands. - As such, we need to flush the buffer when done. */ -void eflush(void); +/* 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 + * approach at a later date. */ +void ebuffer (const char *file); +void eflush (void); +void eclose (void); #endif diff --git a/src/libeinfo.c b/src/libeinfo.c index 9355021c..a6c0a2a1 100644 --- a/src/libeinfo.c +++ b/src/libeinfo.c @@ -8,6 +8,7 @@ #include #include #include +#include #include #include #include @@ -32,6 +33,7 @@ hidden_proto(eerror) hidden_proto(eerrorn) hidden_proto(eerrorx) hidden_proto(eflush) +hidden_proto(eclose) hidden_proto(eindent) hidden_proto(eindentv) hidden_proto(einfo) @@ -118,6 +120,11 @@ static const char *color_terms[] = { NULL }; +/* We use this array to save the stdout/stderr fd's for buffering */ +static int stdfd[2] = {-1, -1}; +static FILE *ebfp = NULL; +static char ebfile[PATH_MAX] = { '\0' }; + static bool is_env (const char *var, const char *val) { char *v; @@ -177,194 +184,125 @@ static int get_term_columns (void) return (DEFAULT_COLS); } -static int ebuffer (const char *cmd, int retval, const char *fmt, va_list ap) +void ebuffer (const char *file) { - char *file = getenv ("RC_EBUFFER"); - FILE *fp; - char buffer[RC_LINEBUFFER]; - int l = 1; + /* Don't ebuffer if we don't have a file or we already are */ + if (! file || stdfd[0] >= 0 || ! isatty (fileno (stdout))) + return; - if (! file || ! cmd || strlen (cmd) < 4) - return (0); + /* Save the current fd's */ + stdfd[0] = dup (fileno (stdout)); + stdfd[1] = dup (fileno (stderr)); - if (! (fp = fopen (file, "a"))) { + if (! (ebfp = fopen (file, "w+"))) { fprintf (stderr, "fopen `%s': %s\n", file, strerror (errno)); - return (0); + return; } - fprintf (fp, "%s %d ", cmd, retval); + snprintf (ebfile, sizeof (ebfile), "%s", file); - if (fmt) { - va_list apc; - va_copy (apc, ap); - l = vsnprintf (buffer, sizeof (buffer), fmt, apc); - fprintf (fp, "%d %s\n", l, buffer); - va_end (apc); - } else - fprintf (fp, "0\n"); + fflush (stdout); + fflush (stderr); - fclose (fp); - return (l); + /* Now redirect stdout and stderr */ + if ((dup2 (fileno (ebfp), fileno (stdout))) < 0) + fprintf (stderr, "dup2: %s", strerror (errno)); + if ((dup2 (fileno (ebfp), fileno (stderr))) < 0) + fprintf (stderr, "dup2: %s", strerror (errno)); + + /* Store the filename in our environment so scripts can tell if we're + * buffering or not */ + unsetenv ("RC_EBUFFER"); + setenv ("RC_EBUFFER", file, 1); + + return; } -typedef struct func +static void _eflush (bool reopen) { - const char *name; - int (*efunc) (const char *fmt, ...); - int (*eefunc) (int retval, const char *fmt, ...); - void (*eind) (void); -} func_t; - -static const func_t funcmap[] = { - { "einfon", &einfon, NULL, NULL }, - { "ewarnn", &ewarnn, NULL, NULL}, - { "eerrorn", &eerrorn, NULL, NULL}, - { "einfo", &einfo, NULL, NULL }, - { "ewarn", &ewarn, NULL, NULL }, - { "eerror", &eerror, NULL, NULL }, - { "ebegin", &ebegin, NULL, NULL }, - { "eend", NULL, &eend, NULL }, - { "ewend", NULL, &ewend, NULL }, - { "eindent", NULL, NULL, &eindent }, - { "eoutdent", NULL, NULL, &eoutdent }, - { "einfovn", &einfovn, NULL, NULL }, - { "ewarnvn", &ewarnvn, NULL, NULL }, - { "einfov", &einfov, NULL, NULL }, - { "ewarnv", &ewarnv, NULL, NULL }, - { "ebeginv", &ebeginv, NULL, NULL }, - { "eendv", NULL, &eendv, NULL }, - { "ewendv", NULL, &ewendv, NULL }, - { "eindentv" ,NULL, NULL, &eindentv }, - { "eoutdentv", NULL, NULL, &eoutdentv }, - { NULL, NULL, NULL, NULL }, -}; - -void eflush (void) -{ - FILE *fp; - char *file = getenv ("RC_EBUFFER"); char buffer[RC_LINEBUFFER]; - char *cmd; - int retval = 0; - int length = 0; - char *token; - char *p; - struct stat buf; - pid_t pid; - char newfile[PATH_MAX]; - int i = 1; - - if (! file|| (stat (file, &buf) != 0)) { - errno = 0; - return; + int serrno = errno; + + /* eflush called from an init script? */ + if (! ebfp) { + char *file = getenv ("RC_EBUFFER"); + if (file) + ebfp = fopen (file, "a+"); } - /* Find a unique name for our file */ - while (true) { - snprintf (newfile, sizeof (newfile), "%s.%d", file, i); - if (stat (newfile, &buf) != 0) { - if (rename (file, newfile)) - fprintf (stderr, "rename `%s' `%s': %s\n", file, newfile, - strerror (errno)); - break; + if (! ebfp) + return; + + fflush (stdout); + fflush (stderr); + + /* Restore stdout and stderr now */ + if (stdfd[0] >= 0) { + dup2 (stdfd[0], fileno (stdout)); + dup2 (stdfd[1], fileno (stderr)); + } else { + char *tty = getenv ("RC_TTY"); + if (tty) { + freopen (tty, "w+", stdout); + dup2 (fileno (stdout), fileno (stderr)); } - i++; } - /* We fork a child process here so we don't hold anything up */ - if ((pid = fork ()) == -1) { - fprintf (stderr, "fork: %s", strerror (errno)); - return; - } - - if (pid != 0) - return; - /* Spin until we can lock the ebuffer */ while (true) { struct timeval tv; + errno = 0; + if (mkfifo (EBUFFER_LOCK, 0700) == 0) + break; + if (errno != EEXIST) + eerror ("mkfifo `%s': %s\n", EBUFFER_LOCK, strerror (errno)); tv.tv_sec = 0; tv.tv_usec = 20000; select (0, NULL, NULL, NULL, &tv); - errno = 0; - if (link (newfile, EBUFFER_LOCK) == 0) - break; - if (errno != EEXIST) - fprintf (stderr, "link `%s' `%s': %s\n", newfile, EBUFFER_LOCK, - strerror (errno)); } + errno = serrno; - if (! (fp = fopen (newfile, "r"))) { - fprintf (stderr, "fopen `%s': %s\n", newfile, strerror (errno)); - return; - } - - unsetenv ("RC_EBUFFER"); - + /* Dump the file to stdout */ memset (buffer, 0, RC_LINEBUFFER); - while (fgets (buffer, RC_LINEBUFFER, fp)) { - i = strlen (buffer) - 1; - if (i < 1) - continue; - - if (buffer[i] == '\n') - buffer[i] = 0; - - p = buffer; - cmd = strsep (&p, " "); - token = strsep (&p, " "); - if (sscanf (token, "%d", &retval) != 1) { - fprintf (stderr, "eflush `%s': not a number", token); - continue; - } - token = strsep (&p, " "); - if (sscanf (token, "%d", &length) != 1) { - fprintf (stderr, "eflush `%s': not a number", token); - continue; - } - - i = 0; - while (funcmap[i].name) { - if (strcmp (funcmap[i].name, cmd) == 0) { - if (funcmap[i].efunc) { - if (p) - funcmap[i].efunc ("%s", p); - else - funcmap[i].efunc (NULL, NULL); - } else if (funcmap[i].eefunc) { - if (p) - funcmap[i].eefunc (retval, "%s", p); - else - funcmap[i].eefunc (retval, NULL, NULL); - } else if (funcmap[i].eind) - funcmap[i].eind (); - else - fprintf (stderr, "eflush `%s': no function defined\n", cmd); - break; - } - i++; - } - - if (! funcmap[i].name) - fprintf (stderr, "eflush `%s': invalid function\n", cmd); + if (fseek (ebfp, (off_t) 0, SEEK_SET) < 0) + eerror ("fseek: %s", strerror (errno)); + else { + while (fgets (buffer, RC_LINEBUFFER, ebfp)) + printf ("%s", buffer); } - fclose (fp); - + fflush (stdout); + fflush (stderr); + if (unlink (EBUFFER_LOCK)) - fprintf (stderr, "unlink `%s': %s", EBUFFER_LOCK, strerror (errno)); + eerror ("unlink `%s': %s", EBUFFER_LOCK, strerror (errno)); - if (unlink (newfile)) - fprintf (stderr, "unlink `%s': %s", newfile, strerror (errno)); + if (reopen) { + ftruncate (fileno (ebfp), (off_t) 0); + fseek (ebfp, (off_t) 0, SEEK_SET); + dup2 (fileno (ebfp), fileno (stdout)); + dup2 (fileno (ebfp), fileno (stderr)); + } else { + stdfd[0] = -1; + stdfd[1] = -1; + fclose (ebfp); + ebfp = NULL; + unlink (ebfile); + ebfile[0] = '\0'; + unsetenv ("RC_EBUFFER"); + } - _exit (EXIT_SUCCESS); + return; +} + +void eflush () { + _eflush (true); } hidden_def(eflush) -#define EBUFFER(_cmd, _retval, _fmt, _ap) { \ - int _i = ebuffer (_cmd, _retval, _fmt, _ap); \ - if (_i) \ - return (_i); \ +void eclose () { + _eflush (false); } +hidden_def(eclose) static void elog (int level, const char *fmt, va_list ap) { @@ -401,7 +339,6 @@ static int _eindent (FILE *stream) /* Terminate it */ memset (indent + amount, 0, 1); - return (fprintf (stream, "%s", indent)); } @@ -486,8 +423,7 @@ int einfon (const char *fmt, ...) return (0); va_start (ap, fmt); - if (! (retval = ebuffer ("einfon", 0, fmt, ap))) - retval = _einfovn (fmt, ap); + retval = _einfovn (fmt, ap); va_end (ap); return (retval); @@ -503,8 +439,7 @@ int ewarnn (const char *fmt, ...) return (0); va_start (ap, fmt); - if (! (retval = ebuffer ("ewarnn", 0, fmt, ap))) - retval = _ewarnvn (fmt, ap); + retval = _ewarnvn (fmt, ap); va_end (ap); return (retval); @@ -517,8 +452,7 @@ int eerrorn (const char *fmt, ...) va_list ap; va_start (ap, fmt); - if (! (retval = ebuffer ("eerrorn", 0, fmt, ap))) - retval = _eerrorvn (fmt, ap); + retval = _eerrorvn (fmt, ap); va_end (ap); return (retval); @@ -534,10 +468,8 @@ int einfo (const char *fmt, ...) return (0); va_start (ap, fmt); - if (! (retval = ebuffer ("einfo", 0, fmt, ap))) { - retval = _einfovn (fmt, ap); - retval += printf ("\n"); - } + retval = _einfovn (fmt, ap); + retval += printf ("\n"); va_end (ap); return (retval); @@ -554,10 +486,8 @@ int ewarn (const char *fmt, ...) va_start (ap, fmt); elog (LOG_WARNING, fmt, ap); - if (! (retval = ebuffer ("ewarn", 0, fmt, ap))) { - retval = _ewarnvn (fmt, ap); - retval += printf ("\n"); - } + retval = _ewarnvn (fmt, ap); + retval += printf ("\n"); va_end (ap); return (retval); @@ -569,6 +499,7 @@ void ewarnx (const char *fmt, ...) int retval; va_list ap; + eclose (); if (fmt && ! is_env ("RC_QUIET", "yes")) { va_start (ap, fmt); elog (LOG_WARNING, fmt, ap); @@ -594,6 +525,7 @@ int eerror (const char *fmt, ...) va_end (ap); retval += fprintf (stderr, "\n"); + eflush (); return (retval); } hidden_def(eerror) @@ -602,6 +534,7 @@ void eerrorx (const char *fmt, ...) { va_list ap; + eclose (); if (fmt) { va_start (ap, fmt); elog (LOG_ERR, fmt, ap); @@ -609,6 +542,7 @@ void eerrorx (const char *fmt, ...) va_end (ap); printf ("\n"); } + exit (EXIT_FAILURE); } hidden_def(eerrorx) @@ -622,11 +556,6 @@ int ebegin (const char *fmt, ...) return (0); va_start (ap, fmt); - if ((retval = ebuffer ("ebegin", 0, fmt, ap))) { - va_end (ap); - return (retval); - } - retval = _einfovn (fmt, ap); va_end (ap); retval += printf (" ..."); @@ -667,15 +596,6 @@ static int _do_eend (const char *cmd, int retval, const char *fmt, va_list ap) int col = 0; FILE *fp; va_list apc; - int eb; - - if (fmt) { - va_copy (apc, ap); - eb = ebuffer (cmd, retval, fmt, apc); - va_end (apc); - if (eb) - return (retval); - } if (fmt && retval != 0) { va_copy (apc, ap); @@ -739,9 +659,6 @@ void eindent (void) int amount = 0; char num[10]; - if (ebuffer ("eindent", 0, NULL, NULL)) - return; - if (env) { errno = 0; amount = strtol (env, NULL, 0); @@ -764,9 +681,6 @@ void eoutdent (void) int amount = 0; char num[10]; - if (ebuffer ("eoutdent", 0, NULL, NULL)) - return; - if (! env) return; @@ -797,8 +711,7 @@ int einfovn (const char *fmt, ...) return (0); va_start (ap, fmt); - if (! (retval = ebuffer ("einfovn", 0, fmt, ap))) - retval = _einfovn (fmt, ap); + retval = _einfovn (fmt, ap); va_end (ap); return (retval); @@ -816,8 +729,7 @@ int ewarnvn (const char *fmt, ...) return (0); va_start (ap, fmt); - if (! (retval = ebuffer ("ewarnvn", 0, fmt, ap))) - retval = _ewarnvn (fmt, ap); + retval = _ewarnvn (fmt, ap); va_end (ap); return (retval); @@ -835,10 +747,8 @@ int einfov (const char *fmt, ...) return (0); va_start (ap, fmt); - if (! (retval = ebuffer ("einfov", 0, fmt, ap))) { - retval = _einfovn (fmt, ap); - retval += printf ("\n"); - } + retval = _einfovn (fmt, ap); + retval += printf ("\n"); va_end (ap); return (retval); @@ -856,10 +766,8 @@ int ewarnv (const char *fmt, ...) return (0); va_start (ap, fmt); - if (! (retval = ebuffer ("ewarnv", 0, fmt, ap))) { - retval = _ewarnvn (fmt, ap); - retval += printf ("\n"); - } + retval = _ewarnvn (fmt, ap); + retval += printf ("\n"); va_end (ap); return (retval); @@ -877,12 +785,10 @@ int ebeginv (const char *fmt, ...) return (0); va_start (ap, fmt); - if (! (retval = ebuffer ("ebeginv", 0, fmt, ap))) { - retval = _einfovn (fmt, ap); - retval += printf (" ..."); - if (colour_terminal ()) - retval += printf ("\n"); - } + retval = _einfovn (fmt, ap); + retval += printf (" ..."); + if (colour_terminal ()) + retval += printf ("\n"); va_end (ap); return (retval); diff --git a/src/librc-misc.c b/src/librc-misc.c index 0700863e..a2b38b4a 100644 --- a/src/librc-misc.c +++ b/src/librc-misc.c @@ -644,6 +644,20 @@ char **rc_config_env (char **env) } else env = rc_strlist_add (env, "RC_DEFAULTLEVEL=" RC_LEVEL_DEFAULT); + /* Store the name of the tty that stdout is connected to + * We do this so our init scripts can call eflush without any knowledge + * of our fd's */ + if (isatty (fileno (stdout))) { + if ((p = rc_xstrdup (ttyname (fileno (stdout))))) { + i = strlen ("RC_TTY=") + strlen (p) + 1; + line = rc_xmalloc (sizeof (char *) * i); + snprintf (line, i, "RC_TTY=%s", p); + env = rc_strlist_add (env, line); + free (p); + free (line); + } + } + memset (sys, 0, sizeof (sys)); /* Linux can run some funky stuff like Xen, VServer, UML, etc diff --git a/src/rc.c b/src/rc.c index 12ce4382..c533a18b 100644 --- a/src/rc.c +++ b/src/rc.c @@ -462,13 +462,7 @@ static void set_ksoftlevel (const char *runlevel) static void wait_for_services () { int status = 0; - struct timeval tv; while (wait (&status) != -1); - - /* Wait for a little bit to flush our ebuffer */ - tv.tv_usec = 50000; - tv.tv_sec = 0; - select (0, NULL, NULL, NULL, &tv); } static void add_pid (pid_t pid) @@ -1077,7 +1071,7 @@ int main (int argc, char **argv) /* We always stop the service when in these runlevels */ if (going_down) { pid_t pid = rc_stop_service (service); - if (pid > 0 && ! rc_is_env ("RC_PARALLEL_STARTUP", "yes")) + if (pid > 0 && ! rc_is_env ("RC_PARALLEL", "yes")) rc_waitpid (pid); } @@ -1141,7 +1135,7 @@ int main (int argc, char **argv) /* After all that we can finally stop the blighter! */ if (! found) { pid_t pid = rc_stop_service (service); - if (pid > 0 && ! rc_is_env ("RC_PARALLEL_STARTUP", "yes")) + if (pid > 0 && ! rc_is_env ("RC_PARALLEL", "yes")) rc_waitpid (pid); } } @@ -1227,7 +1221,7 @@ interactive_option: if ((pid = rc_start_service (service))) add_pid (pid); - if (! rc_is_env ("RC_PARALLEL_STARTUP", "yes")) { + if (! rc_is_env ("RC_PARALLEL", "yes")) { rc_waitpid (pid); remove_pid (pid); } diff --git a/src/runscript.c b/src/runscript.c index 45f83433..e1d880b5 100644 --- a/src/runscript.c +++ b/src/runscript.c @@ -199,7 +199,7 @@ static void uncoldplug (char *service) static void cleanup (void) { /* Flush our buffered output if any */ - eflush (); + eclose (); if (hook_out) rc_plugin_run (hook_out, applet); @@ -267,6 +267,10 @@ static bool svc_exec (const char *service, const char *arg1, const char *arg2) { bool retval; + /* To ensure any output has hit our ebuffer */ + fflush (stdout); + fflush (stderr); + /* We need to disable our child signal handler now so we block until our script returns. */ signal (SIGCHLD, NULL); @@ -466,7 +470,7 @@ static void svc_start (const char *service, bool deps) STRLIST_FOREACH (services, svc, i) if (rc_service_state (svc, rc_service_stopped)) { pid_t pid = rc_start_service (svc); - if (! rc_is_env ("RC_PARALLEL_STARTUP", "yes")) + if (! rc_is_env ("RC_PARALLEL", "yes")) rc_waitpid (pid); } @@ -670,7 +674,7 @@ static void svc_stop (const char *service, bool deps) rc_service_state (svc, rc_service_inactive)) { pid_t pid = rc_stop_service (svc); - if (! rc_is_env ("RC_PARALLEL_STARTUP", "yes")) + if (! rc_is_env ("RC_PARALLEL", "yes")) rc_waitpid (pid); tmplist = rc_strlist_add (tmplist, svc); } @@ -883,7 +887,7 @@ int main (int argc, char **argv) softlevel = rc_get_runlevel (); /* If not called from RC or another service then don't be parallel */ - unsetenv ("RC_PARALLEL_STARTUP"); + unsetenv ("RC_PARALLEL"); } setenv ("RC_ELOG", service, 1); @@ -895,13 +899,13 @@ 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_STARTUP", "yes")) { + if (rc_is_env ("RC_PARALLEL", "yes")) { char ebname[PATH_MAX]; char *eb; snprintf (ebname, sizeof (ebname), "%s.%s", applet, pid); eb = rc_strcatpaths (RC_SVCDIR "ebuffer", ebname, (char *) NULL); - setenv ("RC_EBUFFER", eb, 1); + ebuffer (eb); free (eb); }