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".
 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".
 .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.
  */
 
  * 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 <assert.h>
 #include <stdlib.h>
+#include <string.h>
 #include <stdio.h>
 #include <time.h>
 #include <errno.h>
 #include <fcntl.h>
 #include <unistd.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>
 #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 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;
 };
 
 static struct gui_data the_inst;
@@ -870,21 +876,79 @@ gint motion_event(GtkWidget *widget, GdkEventMotion *event, gpointer data)
     return TRUE;
 }
 
     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)
 {
 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
        /*
         * 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);
     }
 
     term_update(term);
@@ -894,7 +958,7 @@ gint timer_func(gpointer data)
 
 void pty_input_func(gpointer data, gint sourcefd, GdkInputCondition condition)
 {
 
 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;
 
     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)) {
      * 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);
        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);
        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 */
 
     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();
 
 
     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_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;
 
 #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
 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);
     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)
 }
 
 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)
 {
  */
 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) {
     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;
 }
 
     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.
  */
 /*
  * Called to query the current socket sendability status.
  */
@@ -566,8 +576,10 @@ static int pty_ldisc(int option)
 
 static int pty_exitcode(void)
 {
 
 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 = {
 }
 
 Backend pty_backend = {