aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRoy Marples <roy@marples.name>2007-04-25 12:30:24 +0000
committerRoy Marples <roy@marples.name>2007-04-25 12:30:24 +0000
commitdfc208bd25288dd09d531f3e0665274bb9cf2998 (patch)
treee3253e1b287969e4abf72441894b53f17bbdc49e
parentae32cbdd4b863fbe707981f85c19e04b1faecf6c (diff)
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.
-rw-r--r--ChangeLog6
-rw-r--r--conf.d/rc10
-rw-r--r--src/einfo.h3
-rw-r--r--src/libeinfo.c17
-rw-r--r--src/rc.c8
-rw-r--r--src/runscript.c181
6 files changed, 185 insertions, 40 deletions
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 <uberlord@gentoo.org>:
+
+ 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 <uberlord@gentoo.org>:
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 <sys/types.h>
+#include <sys/param.h>
#include <sys/stat.h>
#include <sys/wait.h>
#include <dlfcn.h>
#include <errno.h>
+#include <fcntl.h>
#include <getopt.h>
#include <libgen.h>
#include <limits.h>
@@ -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);
+}
+
+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);
+ }
- if (applet)
- free (applet);
+ 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;