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
|
# 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
|
||||||
|
|
||||||
|
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>:
|
20 Sep 2007; Roy Marples <uberlord@gentoo.org>:
|
||||||
|
|
||||||
libeinfo now works out the number of columns from stdout rather than
|
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 \
|
RCOBJS = checkown.o env-update.o fstabinfo.o mountinfo.o \
|
||||||
rc-depend.o rc-plugin.o rc-status.o rc-update.o runscript.o \
|
rc-depend.o rc-plugin.o rc-status.o rc-update.o runscript.o \
|
||||||
start-stop-daemon.o rc.o
|
start-stop-daemon.o rc.o
|
||||||
LDLIBS_RC = $(LDLIBS_LIBRC) -lrc
|
LDLIBS_RC = $(LDLIBS_LIBRC) -lrc -lutil
|
||||||
|
|
||||||
LIB_TARGETS = $(LIBEINFOSO) $(LIBRCSO)
|
LIB_TARGETS = $(LIBEINFOSO) $(LIBRCSO)
|
||||||
SBIN_TARGETS = rc
|
SBIN_TARGETS = rc
|
||||||
|
@ -175,17 +175,10 @@ static bool colour_terminal (void)
|
|||||||
|
|
||||||
static int get_term_columns (FILE *stream)
|
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;
|
struct winsize ws;
|
||||||
|
|
||||||
if (ioctl (fileno (stream), TIOCGWINSZ, &ws) == 0)
|
if (ioctl (fileno (stream), TIOCGWINSZ, &ws) == 0)
|
||||||
return (ws.ws_col);
|
return (ws.ws_col);
|
||||||
#endif
|
|
||||||
|
|
||||||
return (DEFAULT_COLS);
|
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 rc_waitpid (pid_t pid) {
|
||||||
int status = 0;
|
int status = 0;
|
||||||
pid_t savedpid = pid;
|
pid_t savedpid = pid;
|
||||||
|
int retval = -1;
|
||||||
|
|
||||||
errno = 0;
|
errno = 0;
|
||||||
do {
|
while ((pid = waitpid (savedpid, &status, 0)) > 0) {
|
||||||
pid = waitpid (savedpid, &status, 0);
|
if (pid == savedpid)
|
||||||
if (pid < 0) {
|
retval = WIFEXITED (status) ? WEXITSTATUS (status) : EXIT_FAILURE;
|
||||||
if (errno != ECHILD)
|
}
|
||||||
eerror ("waitpid %d: %s", savedpid, strerror (errno));
|
|
||||||
return (-1);
|
return (retval);
|
||||||
}
|
|
||||||
} while (! WIFEXITED (status) && ! WIFSIGNALED (status));
|
|
||||||
|
|
||||||
return (WIFEXITED (status) ? WEXITSTATUS (status) : EXIT_FAILURE);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pid_t rc_stop_service (const char *service)
|
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/select.h>
|
||||||
#include <sys/types.h>
|
#include <sys/types.h>
|
||||||
|
#include <sys/ioctl.h>
|
||||||
#include <sys/param.h>
|
#include <sys/param.h>
|
||||||
#include <sys/stat.h>
|
#include <sys/stat.h>
|
||||||
#include <sys/wait.h>
|
#include <sys/wait.h>
|
||||||
@ -23,8 +24,15 @@
|
|||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
#include <termios.h>
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
|
|
||||||
|
#ifdef __linux__
|
||||||
|
# include <pty.h>
|
||||||
|
#else
|
||||||
|
# include <libutil.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
#include "builtins.h"
|
#include "builtins.h"
|
||||||
#include "einfo.h"
|
#include "einfo.h"
|
||||||
#include "rc.h"
|
#include "rc.h"
|
||||||
@ -60,6 +68,8 @@ static rc_hook_t hook_out = 0;
|
|||||||
static pid_t service_pid = 0;
|
static pid_t service_pid = 0;
|
||||||
static char *prefix = NULL;
|
static char *prefix = NULL;
|
||||||
static bool prefix_locked = false;
|
static bool prefix_locked = false;
|
||||||
|
static int signal_pipe[2] = { -1, -1 };
|
||||||
|
static int master_tty = -1;
|
||||||
|
|
||||||
extern char **environ;
|
extern char **environ;
|
||||||
|
|
||||||
@ -102,10 +112,9 @@ static void setup_selinux (int argc, char **argv)
|
|||||||
|
|
||||||
static void handle_signal (int sig)
|
static void handle_signal (int sig)
|
||||||
{
|
{
|
||||||
pid_t pid;
|
|
||||||
int status;
|
|
||||||
int serrno = errno;
|
int serrno = errno;
|
||||||
char signame[10] = { '\0' };
|
char signame[10] = { '\0' };
|
||||||
|
struct winsize ws;
|
||||||
|
|
||||||
switch (sig) {
|
switch (sig) {
|
||||||
case SIGHUP:
|
case SIGHUP:
|
||||||
@ -113,16 +122,19 @@ static void handle_signal (int sig)
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
case SIGCHLD:
|
case SIGCHLD:
|
||||||
do {
|
if (signal_pipe[1] > -1) {
|
||||||
pid = waitpid (-1, &status, WNOHANG);
|
if (write (signal_pipe[1], &sig, sizeof (sig)) == -1)
|
||||||
if (pid < 0) {
|
eerror ("%s: send: %s", service, strerror (errno));
|
||||||
if (errno != ECHILD)
|
} else {
|
||||||
eerror ("waitpid: %s", strerror (errno));
|
wait (0);
|
||||||
return;
|
}
|
||||||
}
|
break;
|
||||||
} while (! WIFEXITED (status) && ! WIFSIGNALED (status));
|
|
||||||
if (pid == service_pid)
|
case SIGWINCH:
|
||||||
service_pid = 0;
|
if (master_tty >= 0) {
|
||||||
|
ioctl (fileno (stdout), TIOCGWINSZ, &ws);
|
||||||
|
ioctl (master_tty, TIOCSWINSZ, &ws);
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case SIGINT:
|
case SIGINT:
|
||||||
@ -297,16 +309,12 @@ static void cleanup (void)
|
|||||||
free (service);
|
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;
|
unsigned int i;
|
||||||
const char *ec;
|
const char *ec = ecolor (ecolor_hilite);
|
||||||
const char *ec_normal = ecolor (ecolor_normal);
|
const char *ec_normal = ecolor (ecolor_normal);
|
||||||
ssize_t ret = 0;
|
ssize_t ret = 0;
|
||||||
|
int fd = fileno (stdout);
|
||||||
if (fd == fileno (stdout))
|
|
||||||
ec = ecolor (ecolor_hilite);
|
|
||||||
else
|
|
||||||
ec = ecolor (ecolor_bad);
|
|
||||||
|
|
||||||
for (i = 0; i < bytes; i++) {
|
for (i = 0; i < bytes; i++) {
|
||||||
/* We don't prefix escape codes, like eend */
|
/* 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)
|
static bool svc_exec (const char *arg1, const char *arg2)
|
||||||
{
|
{
|
||||||
bool execok;
|
bool execok;
|
||||||
int stdout_pipes[2];
|
int fdout = fileno (stdout);
|
||||||
int stderr_pipes[2];
|
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 */
|
/* Setup our signal pipe */
|
||||||
if (prefix) {
|
if (pipe (signal_pipe) == -1)
|
||||||
if (pipe (stdout_pipes))
|
eerrorx ("%s: pipe: %s", service, applet);
|
||||||
eerror ("pipe: %s", strerror (errno));
|
for (i = 0; i < 2; i++)
|
||||||
if (pipe (stderr_pipes))
|
if ((flags = fcntl (signal_pipe[i], F_GETFD, 0) == -1 ||
|
||||||
eerror ("pipe: %s", strerror (errno));
|
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();
|
service_pid = vfork();
|
||||||
|
|
||||||
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 (prefix) {
|
if (slave_tty >= 0) {
|
||||||
int flags;
|
/* Hmmm, this shouldn't work in a vfork, but it does which is
|
||||||
|
* good for us */
|
||||||
if (dup2 (stdout_pipes[1], fileno (stdout)) == -1)
|
close (master_tty);
|
||||||
eerror ("dup2 stdout: %s", strerror (errno));
|
|
||||||
close (stdout_pipes[0]);
|
dup2 (slave_tty, 0);
|
||||||
if (dup2 (stderr_pipes[1], fileno (stderr)) == -1)
|
dup2 (slave_tty, 1);
|
||||||
eerror ("dup2 stderr: %s", strerror (errno));
|
dup2 (slave_tty, 2);
|
||||||
close (stderr_pipes[0]);
|
if (slave_tty > 2)
|
||||||
|
close (slave_tty);
|
||||||
/* 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")) {
|
||||||
@ -386,86 +407,49 @@ static bool svc_exec (const char *arg1, const char *arg2)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Prefix our piped output */
|
/* We need to notify the child of window resizes now */
|
||||||
if (prefix) {
|
if (master_tty >= 0)
|
||||||
bool stdout_done = false;
|
signal (SIGWINCH, handle_signal);
|
||||||
bool stdout_prefix_shown = false;
|
|
||||||
bool stderr_done = false;
|
|
||||||
bool stderr_prefix_shown = false;
|
|
||||||
char buffer[RC_LINEBUFFER];
|
|
||||||
|
|
||||||
close (stdout_pipes[1]);
|
selfd = MAX (master_tty, signal_pipe[0]) + 1;
|
||||||
close (stderr_pipes[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);
|
if ((s = select (selfd, &rset, NULL, NULL, NULL)) == -1) {
|
||||||
while (! stdout_done && ! stderr_done) {
|
if (errno != EINTR) {
|
||||||
fd_set fds;
|
eerror ("%s: select: %s", service, strerror (errno));
|
||||||
int retval;
|
break;
|
||||||
|
|
||||||
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 > 0) {
|
||||||
|
/* Only SIGCHLD signals come down this pipe */
|
||||||
|
if (FD_ISSET (signal_pipe[0], &rset))
|
||||||
|
break;
|
||||||
|
|
||||||
/* Done now, so close the pipes */
|
if (master_tty >= 0 && FD_ISSET (master_tty, &rset)) {
|
||||||
close(stdout_pipes[0]);
|
bytes = read (master_tty, buffer, sizeof (buffer));
|
||||||
close(stderr_pipes[0]);
|
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;
|
execok = rc_waitpid (service_pid) == 0 ? true : false;
|
||||||
service_pid = 0;
|
service_pid = 0;
|
||||||
|
|
||||||
/* Done, so restore the signal handler */
|
|
||||||
signal (SIGCHLD, handle_signal);
|
|
||||||
|
|
||||||
return (execok);
|
return (execok);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user