Commit bfabf9b4 authored by Nalin Dahyabhai's avatar Nalin Dahyabhai

exit on any error, to avoid leaving random unreaped children running.

* src/pty.c(_vte_pty_run_on_pty): exit on any error, to avoid leaving random
	unreaped children running.  Accept NULL command indicating that no
	exec() should occur, but still error out if command isn't NULL and
	exec() fails.
* src/pty.c(_vte_pty_fork_on_pty_name,_vte_pty_fork_on_pty_fd): store 0 as the
	new child's PID if we're the child.
* src/pty.c(main): don't run "tty" by default, test the forkpty() case instead.
* src/vte.c(vte_terminal_fork_command): rename to _vte_terminal_fork_basic.
	Remove code to replace NULL command with the user's shell.
* src/vte.c(vte_terminal_fork_command): new (sort of)! wrap
	_vte_terminal_fork_basic, replacing a NULL command with the user's
	shell.
* src/vte.c(vte_terminal_forkpty),src/vte.h: new! wrap _vte_terminal_fork_basic,
	passing NULL for the command and argv arguments (bug #116450).
* src/Makefile.am: update shared library version.
* doc/reference/vte-sections.txt: add vte_terminal_forkpty.
parent 99444442
2003-08-13 nalin
* doc/reference/vte-sections.txt: add vte_terminal_forkpty.
* src/pty.c(_vte_pty_run_on_pty): exit on any error, to avoid leaving
random unreaped children running. Accept NULL command indicating that
no exec() should occur, but still error out if command isn't NULL and
exec() fails.
* src/pty.c(_vte_pty_fork_on_pty_name,_vte_pty_fork_on_pty_fd): store
0 as the new child's PID if we're the child.
* src/pty.c(main): don't run "tty" by default, test the forkpty() case
instead.
* src/vte.c(vte_terminal_fork_command): rename to
_vte_terminal_fork_basic. Remove code to replace NULL command with
the user's shell.
* src/vte.c(vte_terminal_fork_command): new! wrap
_vte_terminal_fork_basic, replacing a NULL command with the user's
shell.
* src/vte.c(vte_terminal_forkpty),src/vte.h: new! wrap
_vte_terminal_fork_basic, passing NULL for the command and argv
arguments (bug #116450).
* src/Makefile.am: update shared library version.
2003-08-12 nalin
* configure.in: if <ft2build.h> isn't found, then we can't use any
freetype-specific bits, and that's not useful. Error out, and say
......
......@@ -72,6 +72,20 @@ keys.
@Returns:
<!-- ##### FUNCTION vte_terminal_forkpty ##### -->
<para>
</para>
@terminal:
@envv:
@directory:
@lastlog:
@utmp:
@wtmp:
@Returns:
<!-- ##### FUNCTION vte_terminal_feed ##### -->
<para>
......
......@@ -6,6 +6,7 @@ VteTerminalEraseBinding
vte_terminal_new
vte_terminal_im_append_menuitems
vte_terminal_fork_command
vte_terminal_forkpty
vte_terminal_feed
vte_terminal_feed_child
vte_terminal_copy_clipboard
......
......@@ -89,13 +89,13 @@ libvte_la_SOURCES = \
# Libtool shared library versioning stuffs.
# REVISION gets incremented whenever the source code changes without adding
# an API or ABI change.
VERSION_REVISION=1
# CURRENT must be incremented when an API or ABI change is made, and REVISION
# must be reset to 0 when this happens.
VERSION_CURRENT=5
VERSION_REVISION=0
# CURRENT must be incremented when an API or ABI change (addition or removal)
# is made, and REVISION must be reset to 0 when this happens.
VERSION_CURRENT=6
# AGE must be incremented when an API or ABI addition is made, and REVISION
# must be reset to 0 when this happens.
VERSION_AGE=1
VERSION_AGE=2
libvte_la_LDFLAGS = @LDFLAGS@ -version-info $(VERSION_CURRENT):$(VERSION_REVISION):$(VERSION_AGE)
CLEANFILES = marshal.c marshal.h
......
......@@ -267,8 +267,8 @@ n_write(int fd, const void *buffer, size_t count)
return n;
}
/* Run the given command, using the given descriptor as the controlling
* terminal. */
/* Run the given command (if specified), using the given descriptor as the
* controlling terminal. */
static int
_vte_pty_run_on_pty(int fd, int ready_reader, int ready_writer,
char **env_add, const char *command, char **argv,
......@@ -277,6 +277,7 @@ _vte_pty_run_on_pty(int fd, int ready_reader, int ready_writer,
int i;
char c;
char **args, *arg;
if (fd != STDIN_FILENO) {
dup2(fd, STDIN_FILENO);
}
......@@ -298,17 +299,20 @@ _vte_pty_run_on_pty(int fd, int ready_reader, int ready_writer,
#ifdef HAVE_STROPTS_H
if (!ioctl (fd, I_FIND, "ptem") && ioctl (fd, I_PUSH, "ptem") == -1) {
close (fd);
_exit (0);
return -1;
}
if (!ioctl (fd, I_FIND, "ldterm") && ioctl (fd, I_PUSH, "ldterm") == -1) {
close (fd);
_exit (0);
return -1;
}
if (!ioctl (fd, I_FIND, "ttcompat") && ioctl (fd, I_PUSH, "ttcompat") == -1) {
perror ("ioctl (fd, I_PUSH, \"ttcompat\")");
close (fd);
_exit (0);
return -1;
}
#endif /* HAVE_STROPTS_H */
......@@ -362,22 +366,26 @@ _vte_pty_run_on_pty(int fd, int ready_reader, int ready_writer,
close(ready_reader);
}
/* Outta here. */
if (argv != NULL) {
for (i = 0; (argv[i] != NULL); i++) ;
args = g_malloc0(sizeof(char*) * (i + 1));
for (i = 0; (argv[i] != NULL); i++) {
args[i] = g_strdup(argv[i]);
}
execvp(command, args);
} else {
arg = g_strdup(command);
execlp(command, arg, NULL);
/* If the caller provided a command, we can't go back, ever. */
if (command != NULL) {
/* Outta here. */
if (argv != NULL) {
for (i = 0; (argv[i] != NULL); i++) ;
args = g_malloc0(sizeof(char*) * (i + 1));
for (i = 0; (argv[i] != NULL); i++) {
args[i] = g_strdup(argv[i]);
}
execvp(command, args);
} else {
arg = g_strdup(command);
execlp(command, arg, NULL);
}
/* Avoid calling any atexit() code. */
_exit(0);
g_assert_not_reached();
}
/* Avoid calling any atexit() code. */
_exit(0);
g_assert_not_reached();
return 0;
}
......@@ -404,12 +412,45 @@ _vte_pty_fork_on_pty_name(const char *path, int parent_fd, char **env_add,
/* Start up a child. */
pid = fork();
if (pid == -1) {
switch (pid) {
case -1:
/* Error fork()ing. Bail. */
*child = -1;
return -1;
}
if (pid != 0) {
break;
case 0:
/* Child. Close the parent's ends of the pipes. */
close(ready_a[0]);
close(ready_b[1]);
/* Start a new session and become process-group leader. */
setsid();
setpgid(0, 0);
/* Close most descriptors. */
for (i = 0; i < sysconf(_SC_OPEN_MAX); i++) {
if ((i != ready_b[0]) && (i != ready_a[1])) {
close(i);
}
}
/* Open the slave PTY, acquiring it as the controlling terminal
* for this process and its children. */
fd = open(path, O_RDWR);
if (fd == -1) {
return -1;
}
#ifdef TIOCSCTTY
/* TIOCSCTTY is defined? Let's try that, too. */
ioctl(fd, TIOCSCTTY, fd);
#endif
/* Store 0 as the "child"'s ID to indicate to the caller that
* it is now the child. */
*child = 0;
return _vte_pty_run_on_pty(fd, ready_b[0], ready_a[1],
env_add, command, argv, directory);
break;
default:
/* Parent. Close the child's ends of the pipes, do the ready
* handshake, and return the child's PID. */
close(ready_b[0]);
......@@ -442,35 +483,10 @@ _vte_pty_fork_on_pty_name(const char *path, int parent_fd, char **env_add,
*child = pid;
return 0;
break;
}
/* Child. Close the parent's ends of the pipes. */
close(ready_a[0]);
close(ready_b[1]);
/* Start a new session and become process-group leader. */
setsid();
setpgid(0, 0);
/* Close all descriptors. */
for (i = 0; i < sysconf(_SC_OPEN_MAX); i++) {
if ((i != ready_b[0]) && (i != ready_a[1])) {
close(i);
}
}
/* Open the slave PTY, acquiring it as the controlling terminal for
* this process and its children. */
fd = open(path, O_RDWR);
if (fd == -1) {
return -1;
}
#ifdef TIOCSCTTY
/* TIOCSCTTY is defined? Let's try that, too. */
ioctl(fd, TIOCSCTTY, fd);
#endif
return _vte_pty_run_on_pty(fd, ready_b[0], ready_a[1],
env_add, command, argv, directory);
g_assert_not_reached();
return -1;
}
/* Fork off a child (storing its PID in child), and exec the named command
......@@ -497,12 +513,55 @@ _vte_pty_fork_on_pty_fd(int fd, char **env_add,
/* Start up a child. */
pid = fork();
if (pid == -1) {
switch (pid) {
case -1:
/* Error fork()ing. Bail. */
*child = -1;
return -1;
}
if (pid != 0) {
break;
case 0:
/* Child. CLose the parent's ends of the pipes. */
close(ready_a[0]);
close(ready_b[1]);
/* Save the name of the pty -- we'll need it later to acquire
* it as our controlling terminal. */
tty = ttyname(fd);
/* Start a new session and become process-group leader. */
setsid();
setpgid(0, 0);
/* Close all other descriptors. */
for (i = 0; i < sysconf(_SC_OPEN_MAX); i++) {
if ((i != fd) &&
(i != ready_b[0]) &&
(i != ready_a[1])) {
close(i);
}
}
/* Try to reopen the pty to acquire it as our controlling
* terminal. */
if (tty != NULL) {
i = open(tty, O_RDWR);
if (i != -1) {
close(fd);
fd = i;
}
#ifdef TIOCSCTTY
/* TIOCSCTTY is defined? Let's try that, too. */
ioctl(fd, TIOCSCTTY, fd);
#endif
}
/* Store 0 as the "child"'s ID to indicate to the caller that
* it is now the child. */
*child = 0;
return _vte_pty_run_on_pty(fd, ready_b[0], ready_a[1],
env_add, command, argv, directory);
break;
default:
/* Parent. Close the child's ends of the pipes, do the ready
* handshake, and return the child's PID. */
close(ready_b[0]);
......@@ -536,41 +595,8 @@ _vte_pty_fork_on_pty_fd(int fd, char **env_add,
*child = pid;
return 0;
}
/* Child. CLose the parent's ends of the pipes. */
close(ready_a[0]);
close(ready_b[1]);
/* Save the name of the pty -- we'll need it later to acquire it as
* our controlling terminal. */
tty = ttyname(fd);
/* Start a new session and become process-group leader. */
setsid();
setpgid(0, 0);
/* Close all other descriptors. */
for (i = 0; i < sysconf(_SC_OPEN_MAX); i++) {
if ((i != fd) && (i != ready_b[0]) && (i != ready_a[1])) {
close(i);
}
}
/* Try to reopen the pty to acquire it as our controlling terminal. */
if (tty != NULL) {
i = open(tty, O_RDWR);
if (i != -1) {
close(fd);
fd = i;
}
#ifdef TIOCSCTTY
/* TIOCSCTTY is defined? Let's try that, too. */
ioctl(fd, TIOCSCTTY, fd);
#endif
}
return _vte_pty_run_on_pty(fd, ready_b[0], ready_a[1],
env_add, command, argv, directory);
g_assert_not_reached();
return -1;
}
/**
......@@ -1140,11 +1166,29 @@ main(int argc, char **argv)
signal(SIGCHLD, sigchld_handler);
_vte_debug_parse_string(getenv("VTE_DEBUG_FLAGS"));
fd = _vte_pty_open(&child, NULL,
(argc > 1) ? argv[1] : "/usr/bin/tty",
(argc > 1) ? argv[1] : NULL,
(argc > 1) ? argv + 1 : NULL,
NULL,
0, 0,
TRUE, TRUE, TRUE);
if (child == 0) {
int i;
for (i = 0; ; i++) {
switch (i % 3) {
case 0:
case 1:
fprintf(stdout, "%d\n", i);
break;
case 2:
fprintf(stderr, "%d\n", i);
break;
default:
g_assert_not_reached();
break;
}
sleep(1);
}
}
g_print("Child pid is %d.\n", (int)child);
do {
ret = n_read(fd, &c, 1);
......@@ -1154,7 +1198,13 @@ main(int argc, char **argv)
if ((ret == -1) && (errno != EAGAIN) && (errno != EINTR)) {
break;
}
if (argc < 2) {
n_write(STDOUT_FILENO, "[", 1);
}
n_write(STDOUT_FILENO, &c, 1);
if (argc < 2) {
n_write(STDOUT_FILENO, "]", 1);
}
} while (TRUE);
return 0;
}
......
......@@ -6962,29 +6962,12 @@ _vte_terminal_disconnect_pty_write(VteTerminal *terminal)
}
}
/**
* vte_terminal_fork_command:
* @terminal: a #VteTerminal
* @command: the name of a binary to run
* @argv: the argument list to be passed to @command
* @envv: a list of environment variables to be added to the environment before
* starting @command
* @directory: the name of a directory the command should start in, or NULL
* @lastlog: TRUE if the session should be logged to the lastlog
* @utmp: TRUE if the session should be logged to the utmp/utmpx log
* @wtmp: TRUE if the session should be logged to the wtmp/wtmpx log
*
* Starts the specified command under a newly-allocated controlling
* pseudo-terminal. TERM is automatically set to reflect the terminal widget's
* emulation setting. If @lastlog, @utmp, or @wtmp are TRUE, logs the session
* to the specified system log files.
*
* Returns: the ID of the new process
*/
pid_t
vte_terminal_fork_command(VteTerminal *terminal, const char *command,
char **argv, char **envv, const char *directory,
gboolean lastlog, gboolean utmp, gboolean wtmp)
/* Basic wrapper around _vte_pty_open, which handles the pipefitting. */
static pid_t
_vte_terminal_fork_basic(VteTerminal *terminal, const char *command,
char **argv, char **envv,
const char *directory,
gboolean lastlog, gboolean utmp, gboolean wtmp)
{
char **env_add;
int i;
......@@ -6992,45 +6975,43 @@ vte_terminal_fork_command(VteTerminal *terminal, const char *command,
GtkWidget *widget;
VteReaper *reaper;
g_return_val_if_fail(VTE_IS_TERMINAL(terminal), -1);
widget = GTK_WIDGET(terminal);
/* Start up the command and get the PTY of the master. */
for (i = 0; (envv != NULL) && (envv[i] != NULL); i++) ;
env_add = g_malloc0(sizeof(char*) * (i + 2));
if (command == NULL) {
command = terminal->pvt->shell;
/* Duplicate the environment, and add one more variable. */
for (i = 0; (envv != NULL) && (envv[i] != NULL); i++) {
/* nothing */ ;
}
env_add = g_malloc0(sizeof(char*) * (i + 2));
env_add[0] = g_strdup_printf("TERM=%s", terminal->pvt->emulation);
for (i = 0; (envv != NULL) && (envv[i] != NULL); i++) {
env_add[i + 1] = g_strdup(envv[i]);
}
env_add[i + 1] = NULL;
/* Close any existing ptys. */
if (terminal->pvt->pty_master != -1) {
_vte_pty_close(terminal->pvt->pty_master);
close(terminal->pvt->pty_master);
}
terminal->pvt->pty_master = _vte_pty_open(&pid,
env_add,
command,
argv,
directory,
terminal->column_count,
terminal->row_count,
lastlog,
utmp,
wtmp);
for (i = 0; env_add[i] != NULL; i++) {
g_free(env_add[i]);
/* Open the new pty. */
pid = -1;
i = _vte_pty_open(&pid, env_add, command, argv, directory,
terminal->column_count, terminal->row_count,
lastlog, utmp, wtmp);
switch (i) {
case -1:
return -1;
break;
default:
if (pid != 0) {
terminal->pvt->pty_master = i;
}
}
g_free(env_add);
/* If we started the process, set up to listen for its output. */
if (pid != -1) {
/* If we successfully started the process, set up to listen for its
* output. */
if ((pid != -1) && (pid != 0)) {
/* Set this as the child's pid. */
terminal->pvt->pty_pid = pid;
......@@ -7064,10 +7045,88 @@ vte_terminal_fork_command(VteTerminal *terminal, const char *command,
_vte_terminal_connect_pty_read(terminal);
}
/* Clean up. */
for (i = 0; env_add[i] != NULL; i++) {
g_free(env_add[i]);
}
g_free(env_add);
/* Return the pid to the caller. */
return pid;
}
/**
* vte_terminal_fork_command:
* @terminal: a #VteTerminal
* @command: the name of a binary to run
* @argv: the argument list to be passed to @command
* @envv: a list of environment variables to be added to the environment before
* starting @command
* @directory: the name of a directory the command should start in, or NULL
* @lastlog: TRUE if the session should be logged to the lastlog
* @utmp: TRUE if the session should be logged to the utmp/utmpx log
* @wtmp: TRUE if the session should be logged to the wtmp/wtmpx log
*
* Starts the specified command under a newly-allocated controlling
* pseudo-terminal. TERM is automatically set to reflect the terminal widget's
* emulation setting. If @lastlog, @utmp, or @wtmp are TRUE, logs the session
* to the specified system log files.
*
* Returns: the ID of the new process
*/
pid_t
vte_terminal_fork_command(VteTerminal *terminal,
const char *command, char **argv, char **envv,
const char *directory,
gboolean lastlog, gboolean utmp, gboolean wtmp)
{
g_return_val_if_fail(VTE_IS_TERMINAL(terminal), -1);
/* Make the user's shell the default command. */
if (command == NULL) {
command = terminal->pvt->shell;
}
/* Start up the command and get the PTY of the master. */
return _vte_terminal_fork_basic(terminal, command, argv, envv,
directory, lastlog, utmp, wtmp);
}
/**
* vte_terminal_forkpty:
* @terminal: a #VteTerminal
* @envv: a list of environment variables to be added to the environment before
* starting returning in the child process
* @directory: the name of a directory the child process should change to, or NULL
* @lastlog: TRUE if the session should be logged to the lastlog
* @utmp: TRUE if the session should be logged to the utmp/utmpx log
* @wtmp: TRUE if the session should be logged to the wtmp/wtmpx log
*
* Starts a new child process under a newly-allocated controlling
* pseudo-terminal. TERM is automatically set to reflect the terminal widget's
* emulation setting. If @lastlog, @utmp, or @wtmp are TRUE, logs the session
* to the specified system log files.
*
* Returns: the ID of the new process in the parent, 0 in the child, and -1 if
* there was an error
*
* Since: 0.11.11
*/
pid_t
vte_terminal_forkpty(VteTerminal *terminal,
char **envv, const char *directory,
gboolean lastlog, gboolean utmp, gboolean wtmp)
{
pid_t ret;
g_return_val_if_fail(VTE_IS_TERMINAL(terminal), -1);
ret = _vte_terminal_fork_basic(terminal, NULL, NULL, envv,
directory, lastlog, utmp, wtmp);
return ret;
}
/* Handle an EOF from the client. */
static void
vte_terminal_eof(GIOChannel *channel, gpointer data)
......
......@@ -158,6 +158,13 @@ pid_t vte_terminal_fork_command(VteTerminal *terminal,
gboolean utmp,
gboolean wtmp);
/* Users of libzvt may find this useful. */
pid_t vte_terminal_forkpty(VteTerminal *terminal,
char **envv, const char *directory,
gboolean lastlog,
gboolean utmp,
gboolean wtmp);
/* Send data to the terminal to display, or to the terminal's forked command
* to handle in some way. If it's 'cat', they should be the same. */
void vte_terminal_feed(VteTerminal *terminal, const char *data, glong length);
......
......@@ -22,6 +22,7 @@
#include <sys/ioctl.h>
#include <sys/stat.h>
#include <errno.h>
#include <fcntl.h>
#include <string.h>
#include <unistd.h>
......@@ -390,7 +391,7 @@ main(int argc, char **argv)
gboolean transparent = FALSE, audible = TRUE, blink = TRUE,
debug = FALSE, dingus = FALSE, geometry = TRUE, dbuffer = TRUE,
console = FALSE, scroll = FALSE, keep = FALSE,
icon_title = FALSE;
icon_title = FALSE, shell = TRUE;
long lines = 100;
const char *message = "Launching interactive shell...\r\n";
const char *font = NULL;
......@@ -449,7 +450,7 @@ main(int argc, char **argv)
g_assert(i < (g_list_length(args) + 2));
/* Parse some command-line options. */
while ((opt = getopt(argc, argv, "B:CDT2abc:df:ghkn:st:w:-")) != -1) {
while ((opt = getopt(argc, argv, "B:CDST2abc:df:ghkn:st:w:-")) != -1) {
gboolean bail = FALSE;
switch (opt) {
case 'B':
......@@ -461,6 +462,9 @@ main(int argc, char **argv)
case 'D':
dingus = TRUE;
break;
case 'S':
shell = !shell;
break;
case 'T':
transparent = TRUE;
break;
......@@ -686,21 +690,57 @@ main(int argc, char **argv)
console = FALSE;
}
}
if (!console) {
/* Launch a shell. */
#ifdef VTE_DEBUG
if (_vte_debug_on(VTE_DEBUG_MISC)) {
vte_terminal_feed(VTE_TERMINAL(widget), message,
strlen(message));
}
#endif
vte_terminal_fork_command(VTE_TERMINAL(widget),
command, NULL, env_add,
working_directory,
TRUE, TRUE, TRUE);
if (command == NULL) {
vte_terminal_feed_child(VTE_TERMINAL(widget),
"pwd\n", -1);
if (shell) {
/* Launch a shell. */
#ifdef VTE_DEBUG
if (_vte_debug_on(VTE_DEBUG_MISC)) {
vte_terminal_feed(VTE_TERMINAL(widget), message,
strlen(message));
}
#endif
vte_terminal_fork_command(VTE_TERMINAL(widget),
command, NULL, env_add,
working_directory,
TRUE, TRUE, TRUE);
if (command == NULL) {
vte_terminal_feed_child(VTE_TERMINAL(widget),
"pwd\n", -1);
}
} else {
long i;
i = vte_terminal_forkpty(VTE_TERMINAL(widget),
env_add, working_directory,
TRUE, TRUE, TRUE);
switch (i) {
case -1:
/* abnormal */
g_warning("Error in vte_terminal_forkpty(): %s",
strerror(errno));
break;
case 0:
/* child */
for (i = 0; ; i++) {
switch (i % 3) {
case 0:
case 1:
fprintf(stdout, "%ld\n", i);
break;
case 2:
fprintf(stderr, "%ld\n", i);
break;
}
sleep(1);
}
_exit(0);
break;
default:
g_print("Child PID is %ld (mine is %ld).\n",
(long) i, (long) getpid());
/* normal */
break;
}
}
}
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment