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.
This commit is contained in:
parent
ae32cbdd4b
commit
dfc208bd25
@ -1,6 +1,12 @@
|
|||||||
# ChangeLog for Gentoo System Intialization ("rc") scripts
|
# ChangeLog for Gentoo System Intialization ("rc") scripts
|
||||||
# Copyright 1999-2007 Gentoo Foundation; Distributed under the GPLv2
|
# 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>:
|
24 Apr 2007; Roy Marples <uberlord@gentoo.org>:
|
||||||
|
|
||||||
We now buffer stdout and stderr to a file and flush that when running in
|
We now buffer stdout and stderr to a file and flush that when running in
|
||||||
|
10
conf.d/rc
10
conf.d/rc
@ -1,10 +1,16 @@
|
|||||||
# /etc/conf.d/rc: Global config file for the Gentoo RC System
|
# /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
|
# Set to "yes" if you want the rc system to try and start services
|
||||||
# in parallel for a slight speed improvement. NOTE: When enabled
|
# in parallel for a slight speed improvement.
|
||||||
# init script output is buffered and displayed in one go when finished.
|
|
||||||
RC_PARALLEL="no"
|
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
|
# 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
|
# boot so you can choose to start specific services. Set to "no" to disable
|
||||||
# this feature.
|
# this feature.
|
||||||
|
@ -64,6 +64,9 @@ int ewendv (int retval, const char *fmt, ...) EINFO_PRINTF (2, 3);
|
|||||||
void eindentv (void);
|
void eindentv (void);
|
||||||
void eoutdentv (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
|
/* Handy utils to buffer stdout and stderr so our output is always
|
||||||
* sane when forking around.
|
* sane when forking around.
|
||||||
* Don't depend on these being here though as we may take a different
|
* Don't depend on these being here though as we may take a different
|
||||||
|
@ -125,6 +125,9 @@ static int stdfd[2] = {-1, -1};
|
|||||||
static FILE *ebfp = NULL;
|
static FILE *ebfp = NULL;
|
||||||
static char ebfile[PATH_MAX] = { '\0' };
|
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)
|
static bool is_env (const char *var, const char *val)
|
||||||
{
|
{
|
||||||
char *v;
|
char *v;
|
||||||
@ -184,6 +187,10 @@ static int get_term_columns (void)
|
|||||||
return (DEFAULT_COLS);
|
return (DEFAULT_COLS);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void eprefix (const char *prefix) {
|
||||||
|
_eprefix = prefix;
|
||||||
|
}
|
||||||
|
|
||||||
void ebuffer (const char *file)
|
void ebuffer (const char *file)
|
||||||
{
|
{
|
||||||
/* Don't ebuffer if we don't have a file or we already are */
|
/* 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)
|
hidden_def(ecolor)
|
||||||
|
|
||||||
#define EINFOVN(_file, _color) \
|
#define EINFOVN(_file, _color) \
|
||||||
fprintf (_file, " %s*%s ", ecolor (_color), ecolor (ecolor_normal)); \
|
if (_eprefix) \
|
||||||
retval += _eindent (_file); \
|
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_list _ap; \
|
||||||
va_copy (_ap, ap); \
|
va_copy (_ap, ap); \
|
||||||
retval += vfprintf (_file, fmt, _ap) + 3; \
|
retval += vfprintf (_file, fmt, _ap) + 3; \
|
||||||
va_end (_ap); \
|
va_end (_ap); \
|
||||||
} \
|
} \
|
||||||
if (colour_terminal ()) \
|
if (colour_terminal ()) \
|
||||||
fprintf (_file, ECOLOR_FLUSH);
|
fprintf (_file, ECOLOR_FLUSH);
|
||||||
|
|
||||||
static int _einfovn (const char *fmt, va_list ap)
|
static int _einfovn (const char *fmt, va_list ap)
|
||||||
{
|
{
|
||||||
|
8
src/rc.c
8
src/rc.c
@ -388,14 +388,15 @@ static void sulogin (bool cont)
|
|||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
newenv = rc_filter_env ();
|
||||||
|
|
||||||
if (cont) {
|
if (cont) {
|
||||||
int status = 0;
|
int status = 0;
|
||||||
pid_t pid = vfork();
|
pid_t pid = vfork ();
|
||||||
|
|
||||||
if (pid == -1)
|
if (pid == -1)
|
||||||
eerrorx ("%s: vfork: %s", applet, strerror (errno));
|
eerrorx ("%s: vfork: %s", applet, strerror (errno));
|
||||||
if (pid == 0) {
|
if (pid == 0) {
|
||||||
newenv = rc_filter_env ();
|
|
||||||
#ifdef __linux__
|
#ifdef __linux__
|
||||||
execle ("/sbin/sulogin", "/sbin/sulogin",
|
execle ("/sbin/sulogin", "/sbin/sulogin",
|
||||||
getenv ("CONSOLE"), (char *) NULL, newenv);
|
getenv ("CONSOLE"), (char *) NULL, newenv);
|
||||||
@ -411,7 +412,6 @@ static void sulogin (bool cont)
|
|||||||
waitpid (pid, &status, 0);
|
waitpid (pid, &status, 0);
|
||||||
} else {
|
} else {
|
||||||
#ifdef __linux
|
#ifdef __linux
|
||||||
newenv = rc_filter_env ();
|
|
||||||
execle ("/sbin/sulogin", "/sbin/sulogin",
|
execle ("/sbin/sulogin", "/sbin/sulogin",
|
||||||
getenv ("CONSOLE"), (char *) NULL, newenv);
|
getenv ("CONSOLE"), (char *) NULL, newenv);
|
||||||
eerrorx ("%s: unable to exec `/sbin/sulogin': %s", applet, strerror (errno));
|
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);
|
kill (pl->pid, SIGTERM);
|
||||||
|
|
||||||
/* Notify plugins we are aborting */
|
/* 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 */
|
/* Only drop into single user mode if we're booting */
|
||||||
run = getenv ("RUNLEVEL");
|
run = getenv ("RUNLEVEL");
|
||||||
|
181
src/runscript.c
181
src/runscript.c
@ -9,10 +9,12 @@
|
|||||||
#define APPLET "runscript"
|
#define APPLET "runscript"
|
||||||
|
|
||||||
#include <sys/types.h>
|
#include <sys/types.h>
|
||||||
|
#include <sys/param.h>
|
||||||
#include <sys/stat.h>
|
#include <sys/stat.h>
|
||||||
#include <sys/wait.h>
|
#include <sys/wait.h>
|
||||||
#include <dlfcn.h>
|
#include <dlfcn.h>
|
||||||
#include <errno.h>
|
#include <errno.h>
|
||||||
|
#include <fcntl.h>
|
||||||
#include <getopt.h>
|
#include <getopt.h>
|
||||||
#include <libgen.h>
|
#include <libgen.h>
|
||||||
#include <limits.h>
|
#include <limits.h>
|
||||||
@ -50,6 +52,9 @@ static char *ibsave = NULL;
|
|||||||
static bool in_background = false;
|
static bool in_background = false;
|
||||||
static rc_hook_t hook_out = 0;
|
static rc_hook_t hook_out = 0;
|
||||||
static pid_t service_pid = 0;
|
static pid_t service_pid = 0;
|
||||||
|
static char *prefix = NULL;
|
||||||
|
|
||||||
|
/* Pipes for prefixed output */
|
||||||
|
|
||||||
extern char **environ;
|
extern char **environ;
|
||||||
|
|
||||||
@ -57,11 +62,11 @@ extern char **environ;
|
|||||||
static void (*selinux_run_init_old) (void);
|
static void (*selinux_run_init_old) (void);
|
||||||
static void (*selinux_run_init_new) (int argc, char **argv);
|
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
|
#endif
|
||||||
|
|
||||||
#ifdef __linux__
|
#ifdef __linux__
|
||||||
void setup_selinux (int argc, char **argv)
|
static void setup_selinux (int argc, char **argv)
|
||||||
{
|
{
|
||||||
void *lib_handle = NULL;
|
void *lib_handle = NULL;
|
||||||
|
|
||||||
@ -205,24 +210,15 @@ static void cleanup (void)
|
|||||||
rc_plugin_run (hook_out, applet);
|
rc_plugin_run (hook_out, applet);
|
||||||
rc_plugin_unload ();
|
rc_plugin_unload ();
|
||||||
|
|
||||||
if (deptree)
|
rc_free_deptree (deptree);
|
||||||
rc_free_deptree (deptree);
|
rc_strlist_free (services);
|
||||||
if (services)
|
rc_strlist_free (types);
|
||||||
rc_strlist_free (services);
|
rc_strlist_free (svclist);
|
||||||
if (types)
|
rc_strlist_free (providelist);
|
||||||
rc_strlist_free (types);
|
rc_strlist_free (restart_services);
|
||||||
if (svclist)
|
rc_strlist_free (need_services);
|
||||||
rc_strlist_free (svclist);
|
rc_strlist_free (tmplist);
|
||||||
if (providelist)
|
free (ibsave);
|
||||||
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);
|
|
||||||
|
|
||||||
if (in_control ()) {
|
if (in_control ()) {
|
||||||
if (rc_service_state (applet, rc_service_stopping)) {
|
if (rc_service_state (applet, rc_service_stopping)) {
|
||||||
@ -248,29 +244,71 @@ static void cleanup (void)
|
|||||||
unlink (exclusive);
|
unlink (exclusive);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (env)
|
rc_strlist_free (env);
|
||||||
rc_strlist_free (env);
|
|
||||||
|
|
||||||
if (mtime_test)
|
if (mtime_test)
|
||||||
{
|
{
|
||||||
unlink (mtime_test);
|
unlink (mtime_test);
|
||||||
free (mtime_test);
|
free (mtime_test);
|
||||||
}
|
}
|
||||||
if (exclusive)
|
free (exclusive);
|
||||||
free (exclusive);
|
free (applet);
|
||||||
|
free (prefix);
|
||||||
|
}
|
||||||
|
|
||||||
if (applet)
|
static int write_prefix (int fd, const char *buffer, size_t bytes, bool *prefixed) {
|
||||||
free (applet);
|
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)
|
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 */
|
/* To ensure any output has hit our ebuffer */
|
||||||
fflush (stdout);
|
fflush (stdout);
|
||||||
fflush (stderr);
|
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
|
/* We need to disable our child signal handler now so we block
|
||||||
until our script returns. */
|
until our script returns. */
|
||||||
signal (SIGCHLD, NULL);
|
signal (SIGCHLD, NULL);
|
||||||
@ -280,6 +318,25 @@ static bool svc_exec (const char *service, const char *arg1, const char *arg2)
|
|||||||
if (service_pid == -1)
|
if (service_pid == -1)
|
||||||
eerrorx ("%s: vfork: %s", service, strerror (errno));
|
eerrorx ("%s: vfork: %s", service, strerror (errno));
|
||||||
if (service_pid == 0) {
|
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")) {
|
if (rc_exists (RC_SVCDIR "runscript.sh")) {
|
||||||
execl (RC_SVCDIR "runscript.sh", service, service, arg1, arg2,
|
execl (RC_SVCDIR "runscript.sh", service, service, arg1, arg2,
|
||||||
(char *) NULL);
|
(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;
|
service_pid = 0;
|
||||||
|
|
||||||
/* Done, so restore the signal handler */
|
/* Done, so restore the signal handler */
|
||||||
signal (SIGCHLD, handle_signal);
|
signal (SIGCHLD, handle_signal);
|
||||||
|
|
||||||
return (retval);
|
return (execok);
|
||||||
}
|
}
|
||||||
|
|
||||||
static rc_service_state_t svc_status (const char *service)
|
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 ());
|
snprintf (pid, sizeof (pid), "%d", (int) getpid ());
|
||||||
setenv ("RC_RUNSCRIPT_PID", pid, 1);
|
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 ebname[PATH_MAX];
|
||||||
char *eb;
|
char *eb;
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user