Implement handling of all Close On Exit modes. Default is to close
authorsimon <simon@cda61777-01e9-0310-a592-d414129be87e>
Wed, 23 Oct 2002 14:21:12 +0000 (14:21 +0000)
committersimon <simon@cda61777-01e9-0310-a592-d414129be87e>
Wed, 23 Oct 2002 14:21:12 +0000 (14:21 +0000)
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
unix/pterm.c
unix/pty.c

index ff8e512..2e57f62 100644 (file)
@@ -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".
index c072e7a..73cd4bc 100644 (file)
@@ -3,14 +3,19 @@
  * back end, all running as a GTK application. Wish me luck.
  */
 
+#define _GNU_SOURCE
+
 #include <string.h>
 #include <assert.h>
 #include <stdlib.h>
+#include <string.h>
 #include <stdio.h>
 #include <time.h>
 #include <errno.h>
 #include <fcntl.h>
 #include <unistd.h>
+#include <sys/types.h>
+#include <sys/wait.h>
 #include <X11/Xlib.h>
 #include <X11/Xutil.h>
 #include <gtk/gtk.h>
@@ -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();
 
index 42ccec1..cede076 100644 (file)
@@ -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 = {