From dfc208bd25288dd09d531f3e0665274bb9cf2998 Mon Sep 17 00:00:00 2001
From: Roy Marples <roy@marples.name>
Date: Wed, 25 Apr 2007 12:30:24 +0000
Subject: 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.

---
 src/einfo.h     |   3 +
 src/libeinfo.c  |  17 ++++--
 src/rc.c        |   8 +--
 src/runscript.c | 181 ++++++++++++++++++++++++++++++++++++++++++++++----------
 4 files changed, 171 insertions(+), 38 deletions(-)

(limited to 'src')

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;
 
-- 
cgit v1.2.3