Use a pty for prefixed output instead of pipes for stdout/stderr. This
is so that programs can get information about the controlling terminal. This change was triggered by bug #188506 where it's possible that stdin, stdout and stderr didn't point to a terminal but ended up on one via our pipes. Using a pty means that stdout and stderr always point to a terminal, but we lose the ability to tell them apart. If there is not a pty available then we use un-prefixed output as normal. This change has also introduced the need for a signal pipe so that SIGCHLD can exit the loop cleanly.
This commit is contained in:
parent
ca58877ed0
commit
45bd125dcc
12
ChangeLog
12
ChangeLog
@ -1,6 +1,18 @@
|
||||
# ChangeLog for Gentoo System Intialization ("rc") scripts
|
||||
# Copyright 1999-2007 Gentoo Foundation; Distributed under the GPLv2
|
||||
|
||||
21 Sep 2007; Roy Marples <uberlord@gentoo.org>:
|
||||
|
||||
Use a pty for prefixed output instead of pipes for stdout/stderr. This
|
||||
is so that programs can get information about the controlling terminal.
|
||||
This change was triggered by bug #188506 where it's possible that
|
||||
stdin, stdout and stderr didn't point to a terminal but ended up on one
|
||||
via our pipes. Using a pty means that stdout and stderr always point to
|
||||
a terminal, but we lose the ability to tell them apart.
|
||||
If there is not a pty available then we use un-prefixed output as normal.
|
||||
This change has also introduced the need for a signal pipe so that
|
||||
SIGCHLD can exit the loop cleanly.
|
||||
|
||||
20 Sep 2007; Roy Marples <uberlord@gentoo.org>:
|
||||
|
||||
libeinfo now works out the number of columns from stdout rather than
|
||||
|
@ -59,7 +59,7 @@ LDLIBS_LIBRC = -leinfo
|
||||
RCOBJS = checkown.o env-update.o fstabinfo.o mountinfo.o \
|
||||
rc-depend.o rc-plugin.o rc-status.o rc-update.o runscript.o \
|
||||
start-stop-daemon.o rc.o
|
||||
LDLIBS_RC = $(LDLIBS_LIBRC) -lrc
|
||||
LDLIBS_RC = $(LDLIBS_LIBRC) -lrc -lutil
|
||||
|
||||
LIB_TARGETS = $(LIBEINFOSO) $(LIBRCSO)
|
||||
SBIN_TARGETS = rc
|
||||
|
@ -175,17 +175,10 @@ static bool colour_terminal (void)
|
||||
|
||||
static int get_term_columns (FILE *stream)
|
||||
{
|
||||
#if defined(TIOCGSIZE) /* BSD */
|
||||
struct ttysize ts;
|
||||
|
||||
if (ioctl (fileno (stream), TIOCGSIZE, &ts) == 0)
|
||||
return (ts.ts_cols);
|
||||
#elif defined(TIOCGWINSZ) /* Linux */
|
||||
struct winsize ws;
|
||||
|
||||
if (ioctl (fileno (stream), TIOCGWINSZ, &ws) == 0)
|
||||
return (ws.ws_col);
|
||||
#endif
|
||||
|
||||
return (DEFAULT_COLS);
|
||||
}
|
||||
|
17
src/librc.c
17
src/librc.c
@ -558,18 +558,15 @@ static pid_t _exec_service (const char *service, const char *arg)
|
||||
int rc_waitpid (pid_t pid) {
|
||||
int status = 0;
|
||||
pid_t savedpid = pid;
|
||||
int retval = -1;
|
||||
|
||||
errno = 0;
|
||||
do {
|
||||
pid = waitpid (savedpid, &status, 0);
|
||||
if (pid < 0) {
|
||||
if (errno != ECHILD)
|
||||
eerror ("waitpid %d: %s", savedpid, strerror (errno));
|
||||
return (-1);
|
||||
}
|
||||
} while (! WIFEXITED (status) && ! WIFSIGNALED (status));
|
||||
|
||||
return (WIFEXITED (status) ? WEXITSTATUS (status) : EXIT_FAILURE);
|
||||
while ((pid = waitpid (savedpid, &status, 0)) > 0) {
|
||||
if (pid == savedpid)
|
||||
retval = WIFEXITED (status) ? WEXITSTATUS (status) : EXIT_FAILURE;
|
||||
}
|
||||
|
||||
return (retval);
|
||||
}
|
||||
|
||||
pid_t rc_stop_service (const char *service)
|
||||
|
222
src/runscript.c
222
src/runscript.c
@ -10,6 +10,7 @@
|
||||
|
||||
#include <sys/select.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <sys/param.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/wait.h>
|
||||
@ -23,8 +24,15 @@
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <termios.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#ifdef __linux__
|
||||
# include <pty.h>
|
||||
#else
|
||||
# include <libutil.h>
|
||||
#endif
|
||||
|
||||
#include "builtins.h"
|
||||
#include "einfo.h"
|
||||
#include "rc.h"
|
||||
@ -60,6 +68,8 @@ static rc_hook_t hook_out = 0;
|
||||
static pid_t service_pid = 0;
|
||||
static char *prefix = NULL;
|
||||
static bool prefix_locked = false;
|
||||
static int signal_pipe[2] = { -1, -1 };
|
||||
static int master_tty = -1;
|
||||
|
||||
extern char **environ;
|
||||
|
||||
@ -102,10 +112,9 @@ static void setup_selinux (int argc, char **argv)
|
||||
|
||||
static void handle_signal (int sig)
|
||||
{
|
||||
pid_t pid;
|
||||
int status;
|
||||
int serrno = errno;
|
||||
char signame[10] = { '\0' };
|
||||
struct winsize ws;
|
||||
|
||||
switch (sig) {
|
||||
case SIGHUP:
|
||||
@ -113,16 +122,19 @@ static void handle_signal (int sig)
|
||||
break;
|
||||
|
||||
case SIGCHLD:
|
||||
do {
|
||||
pid = waitpid (-1, &status, WNOHANG);
|
||||
if (pid < 0) {
|
||||
if (errno != ECHILD)
|
||||
eerror ("waitpid: %s", strerror (errno));
|
||||
return;
|
||||
}
|
||||
} while (! WIFEXITED (status) && ! WIFSIGNALED (status));
|
||||
if (pid == service_pid)
|
||||
service_pid = 0;
|
||||
if (signal_pipe[1] > -1) {
|
||||
if (write (signal_pipe[1], &sig, sizeof (sig)) == -1)
|
||||
eerror ("%s: send: %s", service, strerror (errno));
|
||||
} else {
|
||||
wait (0);
|
||||
}
|
||||
break;
|
||||
|
||||
case SIGWINCH:
|
||||
if (master_tty >= 0) {
|
||||
ioctl (fileno (stdout), TIOCGWINSZ, &ws);
|
||||
ioctl (master_tty, TIOCSWINSZ, &ws);
|
||||
}
|
||||
break;
|
||||
|
||||
case SIGINT:
|
||||
@ -297,16 +309,12 @@ static void cleanup (void)
|
||||
free (service);
|
||||
}
|
||||
|
||||
static int write_prefix (int fd, const char *buffer, size_t bytes, bool *prefixed) {
|
||||
static int write_prefix (const char *buffer, size_t bytes, bool *prefixed) {
|
||||
unsigned int i;
|
||||
const char *ec;
|
||||
const char *ec = ecolor (ecolor_hilite);
|
||||
const char *ec_normal = ecolor (ecolor_normal);
|
||||
ssize_t ret = 0;
|
||||
|
||||
if (fd == fileno (stdout))
|
||||
ec = ecolor (ecolor_hilite);
|
||||
else
|
||||
ec = ecolor (ecolor_bad);
|
||||
int fd = fileno (stdout);
|
||||
|
||||
for (i = 0; i < bytes; i++) {
|
||||
/* We don't prefix escape codes, like eend */
|
||||
@ -332,43 +340,56 @@ static int write_prefix (int fd, const char *buffer, size_t bytes, bool *prefixe
|
||||
static bool svc_exec (const char *arg1, const char *arg2)
|
||||
{
|
||||
bool execok;
|
||||
int stdout_pipes[2];
|
||||
int stderr_pipes[2];
|
||||
int fdout = fileno (stdout);
|
||||
struct termios tt;
|
||||
struct winsize ws;
|
||||
int i;
|
||||
int flags;
|
||||
fd_set rset;
|
||||
int s;
|
||||
char buffer[RC_LINEBUFFER];
|
||||
size_t bytes;
|
||||
bool prefixed = false;
|
||||
int selfd;
|
||||
int slave_tty;
|
||||
|
||||
/* Setup our pipes for prefixed output */
|
||||
if (prefix) {
|
||||
if (pipe (stdout_pipes))
|
||||
eerror ("pipe: %s", strerror (errno));
|
||||
if (pipe (stderr_pipes))
|
||||
eerror ("pipe: %s", strerror (errno));
|
||||
/* Setup our signal pipe */
|
||||
if (pipe (signal_pipe) == -1)
|
||||
eerrorx ("%s: pipe: %s", service, applet);
|
||||
for (i = 0; i < 2; i++)
|
||||
if ((flags = fcntl (signal_pipe[i], F_GETFD, 0) == -1 ||
|
||||
fcntl (signal_pipe[i], F_SETFD, flags | FD_CLOEXEC) == -1))
|
||||
eerrorx ("%s: fcntl: %s", service, strerror (errno));
|
||||
|
||||
/* Open a pty for our prefixed output
|
||||
* We do this instead of mapping pipes to stdout, stderr so that
|
||||
* programs can tell if they're attached to a tty or not.
|
||||
* The only loss is that we can no longer tell the difference
|
||||
* between the childs stdout or stderr */
|
||||
master_tty = slave_tty = -1;
|
||||
if (prefix && isatty (fdout)) {
|
||||
tcgetattr (fdout, &tt);
|
||||
ioctl (fdout, TIOCGWINSZ, &ws);
|
||||
|
||||
/* If the below call fails due to not enough ptys then we don't
|
||||
* prefix the output, but we still work */
|
||||
openpty (&master_tty, &slave_tty, NULL, &tt, &ws);
|
||||
}
|
||||
|
||||
/* We need to disable our child signal handler now so we block
|
||||
until our script returns. */
|
||||
signal (SIGCHLD, NULL);
|
||||
|
||||
service_pid = vfork();
|
||||
|
||||
if (service_pid == -1)
|
||||
eerrorx ("%s: vfork: %s", service, strerror (errno));
|
||||
if (service_pid == 0) {
|
||||
if (prefix) {
|
||||
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 (slave_tty >= 0) {
|
||||
/* Hmmm, this shouldn't work in a vfork, but it does which is
|
||||
* good for us */
|
||||
close (master_tty);
|
||||
|
||||
dup2 (slave_tty, 0);
|
||||
dup2 (slave_tty, 1);
|
||||
dup2 (slave_tty, 2);
|
||||
if (slave_tty > 2)
|
||||
close (slave_tty);
|
||||
}
|
||||
|
||||
if (rc_exists (RC_SVCDIR "/runscript.sh")) {
|
||||
@ -386,86 +407,49 @@ static bool svc_exec (const char *arg1, const char *arg2)
|
||||
}
|
||||
}
|
||||
|
||||
/* Prefix our piped output */
|
||||
if (prefix) {
|
||||
bool stdout_done = false;
|
||||
bool stdout_prefix_shown = false;
|
||||
bool stderr_done = false;
|
||||
bool stderr_prefix_shown = false;
|
||||
char buffer[RC_LINEBUFFER];
|
||||
/* We need to notify the child of window resizes now */
|
||||
if (master_tty >= 0)
|
||||
signal (SIGWINCH, handle_signal);
|
||||
|
||||
close (stdout_pipes[1]);
|
||||
close (stderr_pipes[1]);
|
||||
selfd = MAX (master_tty, signal_pipe[0]) + 1;
|
||||
while (1) {
|
||||
FD_ZERO (&rset);
|
||||
FD_SET (signal_pipe[0], &rset);
|
||||
if (master_tty >= 0)
|
||||
FD_SET (master_tty, &rset);
|
||||
|
||||
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;
|
||||
|
||||
/* Wait until we get a lock */
|
||||
while (true) {
|
||||
struct timeval tv;
|
||||
|
||||
if (mkfifo (PREFIX_LOCK, 0700) == 0) {
|
||||
prefix_locked = true;
|
||||
break;
|
||||
}
|
||||
|
||||
if (errno != EEXIST)
|
||||
eerror ("mkfifo `%s': %s\n", PREFIX_LOCK, strerror (errno));
|
||||
tv.tv_sec = 0;
|
||||
tv.tv_usec = 20000;
|
||||
select (0, NULL, NULL, NULL, &tv);
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
/* Clear the lock */
|
||||
unlink (PREFIX_LOCK);
|
||||
prefix_locked = false;
|
||||
if ((s = select (selfd, &rset, NULL, NULL, NULL)) == -1) {
|
||||
if (errno != EINTR) {
|
||||
eerror ("%s: select: %s", service, strerror (errno));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (s > 0) {
|
||||
/* Only SIGCHLD signals come down this pipe */
|
||||
if (FD_ISSET (signal_pipe[0], &rset))
|
||||
break;
|
||||
|
||||
/* Done now, so close the pipes */
|
||||
close(stdout_pipes[0]);
|
||||
close(stderr_pipes[0]);
|
||||
if (master_tty >= 0 && FD_ISSET (master_tty, &rset)) {
|
||||
bytes = read (master_tty, buffer, sizeof (buffer));
|
||||
write_prefix (buffer, bytes, &prefixed);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
close (signal_pipe[0]);
|
||||
close (signal_pipe[1]);
|
||||
signal_pipe[0] = signal_pipe[1] = -1;
|
||||
|
||||
if (master_tty >= 0) {
|
||||
signal (SIGWINCH, SIG_IGN);
|
||||
close (master_tty);
|
||||
master_tty = -1;
|
||||
}
|
||||
|
||||
execok = rc_waitpid (service_pid) == 0 ? true : false;
|
||||
service_pid = 0;
|
||||
|
||||
/* Done, so restore the signal handler */
|
||||
signal (SIGCHLD, handle_signal);
|
||||
|
||||
return (execok);
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user