diff options
author | Nalin Dahyabhai <nalin@src.gnome.org> | 2003-08-13 04:15:26 +0000 |
---|---|---|
committer | Nalin Dahyabhai <nalin@src.gnome.org> | 2003-08-13 04:15:26 +0000 |
commit | bfabf9b46cd6739d974a0ce727278951f1fedbc7 (patch) | |
tree | 8e26166e70184a2c0723a7c4dd8782ff97971fea | |
parent | 99444442a83f4422ccad6d626d8983cf9667823c (diff) |
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.
-rw-r--r-- | ChangeLog | 21 | ||||
-rw-r--r-- | doc/reference/tmpl/vte.sgml | 14 | ||||
-rw-r--r-- | doc/reference/vte-sections.txt | 1 | ||||
-rw-r--r-- | src/Makefile.am | 10 | ||||
-rw-r--r-- | src/pty.c | 222 | ||||
-rw-r--r-- | src/vte.c | 151 | ||||
-rw-r--r-- | src/vte.h | 7 | ||||
-rw-r--r-- | src/vteapp.c | 72 |
8 files changed, 345 insertions, 153 deletions
@@ -1,3 +1,24 @@ +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 diff --git a/doc/reference/tmpl/vte.sgml b/doc/reference/tmpl/vte.sgml index bcbb8a9..0fc6f33 100644 --- a/doc/reference/tmpl/vte.sgml +++ b/doc/reference/tmpl/vte.sgml @@ -72,6 +72,20 @@ keys. @Returns: +<!-- ##### FUNCTION vte_terminal_forkpty ##### --> +<para> + +</para> + +@terminal: +@envv: +@directory: +@lastlog: +@utmp: +@wtmp: +@Returns: + + <!-- ##### FUNCTION vte_terminal_feed ##### --> <para> diff --git a/doc/reference/vte-sections.txt b/doc/reference/vte-sections.txt index c68b077..833b85c 100644 --- a/doc/reference/vte-sections.txt +++ b/doc/reference/vte-sections.txt @@ -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 diff --git a/src/Makefile.am b/src/Makefile.am index 31956cb..29933b9 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -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); diff --git a/src/vteapp.c b/src/vteapp.c index b80a181..4891c3b 100644 --- a/src/vteapp.c +++ b/src/vteapp.c @@ -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; + } } } |