From 90cfd8f45ac8602f4c533a71e67d7a08cebdc58b Mon Sep 17 00:00:00 2001 From: simon Date: Wed, 23 Oct 2002 14:21:12 +0000 Subject: [PATCH] Implement handling of all Close On Exit modes. Default is to close only on clean exit, which is a departure from most xterm-alikes but Ian reckons people will love me for it. If this turns out to be wrong, we can always change the default for Unix. git-svn-id: svn://svn.tartarus.org/sgt/putty@2120 cda61777-01e9-0310-a592-d414129be87e --- unix/pterm.1 | 15 ++++++++++ unix/pterm.c | 94 ++++++++++++++++++++++++++++++++++++++++++++++++++---------- unix/pty.c | 30 +++++++++++++------ 3 files changed, 115 insertions(+), 24 deletions(-) diff --git a/unix/pterm.1 b/unix/pterm.1 index ff8e5123..2e57f624 100644 --- a/unix/pterm.1 +++ b/unix/pterm.1 @@ -109,6 +109,21 @@ resources. All of these resources are of the form \fIpterm.FOO\fP for some FOO; you can make \fIpterm\fP look them up under another name, such as \fIxyz.FOO\fP, by specifying the command-line option "-name xyz". +.IP "\fBpterm.CloseOnExit\fP" +This option should be set to 0, 1 or 2; the default is 1. It +controls what \fIpterm\fP does when the process running inside it +terminates. When set to 2, \fIpterm\fP will close its window as soon +as the process inside it terminates. When set to 0, \fIpterm\fP will +print the process's exit status, and the window will remain present +until a key is pressed (allowing you to inspect the scrollback, and +copy and paste text out of it). + +When this setting is set to 1 (the default), \fIpterm\fP will close +immediately if the process exits cleanly (with an exit status of +zero), but the window will stay around if the process exits with a +non-zero code or on a signal. This enables you to see what went +wrong if the process suffers an error, but not to have to bother +closing the window in normal circumstances. .IP "\fBpterm.TerminalType\fP" This controls the value set in the TERM environment variable inside the new terminal. The default is "xterm". diff --git a/unix/pterm.c b/unix/pterm.c index c072e7a9..73cd4bcb 100644 --- a/unix/pterm.c +++ b/unix/pterm.c @@ -3,14 +3,19 @@ * back end, all running as a GTK application. Wish me luck. */ +#define _GNU_SOURCE + #include #include #include +#include #include #include #include #include #include +#include +#include #include #include #include @@ -47,6 +52,7 @@ struct gui_data { int alt_keycode; char wintitle[sizeof(((Config *)0)->wintitle)]; char icontitle[sizeof(((Config *)0)->wintitle)]; + int master_fd, master_func_id, exited; }; static struct gui_data the_inst; @@ -870,21 +876,79 @@ gint motion_event(GtkWidget *widget, GdkEventMotion *event, gpointer data) return TRUE; } +void done_with_pty(struct gui_data *inst) +{ + extern void pty_close(void); + + if (inst->master_fd >= 0) { + pty_close(); + inst->master_fd = -1; + gtk_input_remove(inst->master_func_id); + } + + if (!inst->exited && back->exitcode() >= 0) { + int exitcode = back->exitcode(); + int clean; + + clean = WIFEXITED(exitcode) && (WEXITSTATUS(exitcode) == 0); + + /* + * Terminate now, if the Close On Exit setting is + * appropriate. + */ + if (cfg.close_on_exit == COE_ALWAYS || + (cfg.close_on_exit == COE_NORMAL && clean)) + exit(0); + + /* + * Otherwise, output an indication that the session has + * closed. + */ + { + char message[512]; + if (WIFEXITED(exitcode)) + sprintf(message, "\r\n[pterm: process terminated with exit" + " code %d]\r\n", WEXITSTATUS(exitcode)); + else if (WIFSIGNALED(exitcode)) +#ifdef HAVE_NO_STRSIGNAL + sprintf(message, "\r\n[pterm: process terminated on signal" + " %d]\r\n", WTERMSIG(exitcode)); +#else + sprintf(message, "\r\n[pterm: process terminated on signal" + " %d (%.400s)]\r\n", WTERMSIG(exitcode), + strsignal(WTERMSIG(exitcode))); +#endif + from_backend((void *)term, 0, message, strlen(message)); + } + inst->exited = 1; + } +} + +void frontend_keypress(void) +{ + /* + * If our child process has exited but not closed, terminate on + * any keypress. + */ + if (inst->exited) + exit(0); +} + gint timer_func(gpointer data) { - /* struct gui_data *inst = (struct gui_data *)data; */ - extern int pty_child_is_dead(); /* declared in pty.c */ + struct gui_data *inst = (struct gui_data *)data; - if (pty_child_is_dead()) { + if (back->exitcode() >= 0) { /* * The primary child process died. We could keep the * terminal open for remaining subprocesses to output to, * but conventional wisdom seems to feel that that's the - * Wrong Thing for an xterm-alike, so we bail out now. This - * would be easy enough to change or make configurable if - * necessary. + * Wrong Thing for an xterm-alike, so we bail out now + * (though we don't necessarily _close_ the window, + * depending on the state of Close On Exit). This would be + * easy enough to change or make configurable if necessary. */ - exit(0); + done_with_pty(inst); } term_update(term); @@ -894,7 +958,7 @@ gint timer_func(gpointer data) void pty_input_func(gpointer data, gint sourcefd, GdkInputCondition condition) { - /* struct gui_data *inst = (struct gui_data *)data; */ + struct gui_data *inst = (struct gui_data *)data; char buf[4096]; int ret; @@ -906,14 +970,11 @@ void pty_input_func(gpointer data, gint sourcefd, GdkInputCondition condition) * to happen. Boo. */ if (ret == 0 || (ret < 0 && errno == EIO)) { - exit(0); - } - - if (ret < 0) { + done_with_pty(inst); + } else if (ret < 0) { perror("read pty master"); exit(1); - } - if (ret > 0) + } else if (ret > 0) from_backend(term, 0, buf, ret); term_blink(term, 1); term_out(term); @@ -1885,7 +1946,10 @@ int main(int argc, char **argv) term_size(term, cfg.height, cfg.width, cfg.savelines); ldisc_send(NULL, 0, 0); /* cause ldisc to notice changes */ - gdk_input_add(pty_master_fd, GDK_INPUT_READ, pty_input_func, inst); + inst->master_fd = pty_master_fd; + inst->exited = FALSE; + inst->master_func_id = gdk_input_add(pty_master_fd, GDK_INPUT_READ, + pty_input_func, inst); gtk_main(); diff --git a/unix/pty.c b/unix/pty.c index 42ccec16..cede0768 100644 --- a/unix/pty.c +++ b/unix/pty.c @@ -61,16 +61,12 @@ static int pty_child_pid; static int pty_utmp_helper_pid, pty_utmp_helper_pipe; static int pty_term_width, pty_term_height; static sig_atomic_t pty_child_dead; +static int pty_exit_code; #ifndef OMIT_UTMP static struct utmp utmp_entry; #endif char **pty_argv; -int pty_child_is_dead(void) -{ - return pty_child_dead; -} - static void setup_utmp(char *ttyname, char *location) { #ifndef OMIT_UTMP @@ -156,8 +152,10 @@ static void sigchld_handler(int signum) pid_t pid; int status; pid = waitpid(-1, &status, WNOHANG); - if (pid == pty_child_pid && (WIFEXITED(status) || WIFSIGNALED(status))) - pty_child_dead = TRUE; + if (pid == pty_child_pid && (WIFEXITED(status) || WIFSIGNALED(status))) { + pty_exit_code = status; + pty_child_dead = TRUE; + } } static void fatal_sig_handler(int signum) @@ -497,6 +495,9 @@ static char *pty_init(void *frontend, */ static int pty_send(char *buf, int len) { + if (pty_master_fd < 0) + return 0; /* ignore all writes if fd closed */ + while (len > 0) { int ret = write(pty_master_fd, buf, len); if (ret < 0) { @@ -509,6 +510,15 @@ static int pty_send(char *buf, int len) return 0; } +void pty_close(void) +{ + if (pty_master_fd >= 0) { + close(pty_master_fd); + pty_master_fd = -1; + } + close(pty_utmp_helper_pipe); /* this causes utmp to be cleaned up */ +} + /* * Called to query the current socket sendability status. */ @@ -566,8 +576,10 @@ static int pty_ldisc(int option) static int pty_exitcode(void) { - /* Shouldn't ever be required */ - return 0; + if (!pty_child_dead) + return -1; /* not dead yet */ + else + return pty_exit_code; } Backend pty_backend = { -- 2.11.0