summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRoy Marples <roy@marples.name>2007-09-21 08:49:43 +0000
committerRoy Marples <roy@marples.name>2007-09-21 08:49:43 +0000
commit45bd125dccdc7aef32af99cc6624a74dd2e24371 (patch)
tree130770fac1c33514444f9155947f56fc5bcb84ff
parentca58877ed06b259ce37a6240746c733d47b0a179 (diff)
downloadopenrc-45bd125dccdc7aef32af99cc6624a74dd2e24371.tar.gz
openrc-45bd125dccdc7aef32af99cc6624a74dd2e24371.tar.bz2
openrc-45bd125dccdc7aef32af99cc6624a74dd2e24371.tar.xz
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.
-rw-r--r--ChangeLog12
-rw-r--r--src/Makefile2
-rw-r--r--src/libeinfo.c7
-rw-r--r--src/librc.c17
-rw-r--r--src/runscript.c222
5 files changed, 123 insertions, 137 deletions
diff --git a/ChangeLog b/ChangeLog
index 1b4307e..6ff533f 100644
--- a/ChangeLog
+++ b/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
diff --git a/src/Makefile b/src/Makefile
index 96799aa..fd79b18 100644
--- a/src/Makefile
+++ b/src/Makefile
@@ -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
diff --git a/src/libeinfo.c b/src/libeinfo.c
index b85b2f4..5025f33 100644
--- a/src/libeinfo.c
+++ b/src/libeinfo.c
@@ -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);
}
diff --git a/src/librc.c b/src/librc.c
index 728f1c4..f501719 100644
--- a/src/librc.c
+++ b/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)
diff --git a/src/runscript.c b/src/runscript.c
index d80d1b8..200d8d8 100644
--- a/src/runscript.c
+++ b/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];
-
- /* 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));
+ 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 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];
-
- 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;
+ /* We need to notify the child of window resizes now */
+ if (master_tty >= 0)
+ signal (SIGWINCH, handle_signal);
- /* Wait until we get a lock */
- while (true) {
- struct timeval tv;
+ 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);
- 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);
- }
+ 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;
- /* Clear the lock */
- unlink (PREFIX_LOCK);
- prefix_locked = false;
+ 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;
- /* Done now, so close the pipes */
- close(stdout_pipes[0]);
- close(stderr_pipes[0]);
+ 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);
}