1
0
mirror of https://git.tartarus.org/simon/putty.git synced 2025-01-10 01:48:00 +00:00

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.

[originally from svn r2120]
This commit is contained in:
Simon Tatham 2002-10-23 14:21:12 +00:00
parent 771b0299c3
commit bdb47167d1
3 changed files with 115 additions and 24 deletions

View 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".

View 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();

View 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 = {