From 62d2ab3e687bfc7e0a02adedee30314b8ef1b08b Mon Sep 17 00:00:00 2001
From: Nicholas Marriott <nicholas.marriott@gmail.com>
Date: Sun, 8 Feb 2009 16:11:26 +0000
Subject: [PATCH] Continue process if suspended.

---
 TODO     |  2 --
 client.c |  7 ++++++-
 server.c | 48 +++++++++++++++++++++++++++++++++++++++++++++++-
 tmux.c   |  6 +++---
 tmux.h   |  6 ++++--
 window.c |  6 ++----
 6 files changed, 62 insertions(+), 13 deletions(-)

diff --git a/TODO b/TODO
index b557f5d8..f3c2a92c 100644
--- a/TODO
+++ b/TODO
@@ -86,6 +86,4 @@
 - 88 colour support; new grid cell flag, and 256<->88 88<->16 translation tables
 - some fix for SF feature request 2527847 - now remain-by-default has gone
   cannot control it per-session
-- if the child is suspended in window with eg ^Z it should be restarted, or
-  ^Z should be ignored
 - clear window title on exit
diff --git a/client.c b/client.c
index 811d7567..dc15bdfa 100644
--- a/client.c
+++ b/client.c
@@ -1,4 +1,4 @@
-/* $Id: client.c,v 1.42 2009-01-21 22:47:31 nicm Exp $ */
+/* $Id: client.c,v 1.43 2009-02-08 16:11:26 nicm Exp $ */
 
 /*
  * Copyright (c) 2007 Nicholas Marriott <nicm@users.sourceforge.net>
@@ -21,6 +21,7 @@
 #include <sys/socket.h>
 #include <sys/stat.h>
 #include <sys/un.h>
+#include <sys/wait.h>
 
 #include <errno.h>
 #include <fcntl.h>
@@ -142,6 +143,10 @@ client_main(struct client_ctx *cctx)
 	error = NULL;
 	xtimeout = INFTIM;
 	while (!sigterm) {
+		if (sigchld) {
+			waitpid(WAIT_ANY, NULL, WNOHANG);
+			sigchld = 0;
+		}
 		if (sigwinch)
 			client_handle_winch(cctx);
 		if (sigcont) {
diff --git a/server.c b/server.c
index 02694d28..290ab8f0 100644
--- a/server.c
+++ b/server.c
@@ -1,4 +1,4 @@
-/* $Id: server.c,v 1.116 2009-01-29 20:13:12 nicm Exp $ */
+/* $Id: server.c,v 1.117 2009-02-08 16:11:26 nicm Exp $ */
 
 /*
  * Copyright (c) 2007 Nicholas Marriott <nicm@users.sourceforge.net>
@@ -21,6 +21,7 @@
 #include <sys/socket.h>
 #include <sys/stat.h>
 #include <sys/un.h>
+#include <sys/wait.h>
 
 #include <errno.h>
 #include <fcntl.h>
@@ -44,6 +45,7 @@ struct clients	 clients;
 
 int		 server_main(const char *, int);
 void		 server_shutdown(void);
+void		 server_child_signal(void);
 void		 server_fill_windows(struct pollfd **);
 void		 server_handle_windows(struct pollfd **);
 void		 server_fill_clients(struct pollfd **);
@@ -230,6 +232,12 @@ server_main(const char *srv_path, int srv_fd)
 		if (sigterm)
 			server_shutdown();
 
+		/* Handle child exit. */
+		if (sigchld) {
+			server_child_signal();
+			sigchld = 0;
+		}
+
 		/* Initialise pollfd array. */
 		nfds = 1;
 		for (i = 0; i < ARRAY_LENGTH(&windows); i++) {
@@ -358,6 +366,44 @@ server_shutdown(void)
 			server_write_client(c, MSG_SHUTDOWN, NULL, 0);
 	}
 }
+
+/* Handle SIGCHLD. */
+void
+server_child_signal(void)
+{
+	struct window		*w;
+	struct window_pane	*wp;
+	int		 	 status;
+	pid_t		 	 pid;
+	u_int		 	 i;
+
+	for (;;) {
+		switch (pid = waitpid(WAIT_ANY, &status, WNOHANG|WUNTRACED)) {
+		case -1:
+			if (errno == ECHILD)
+				return;
+			fatal("waitpid");
+		case 0:
+			return;
+		}
+		if (!WIFSTOPPED(status))
+			continue;
+		if (WSTOPSIG(status) == SIGTTIN || WSTOPSIG(status) == SIGTTOU)
+			continue;
+
+		for (i = 0; i < ARRAY_LENGTH(&windows); i++) {
+			w = ARRAY_ITEM(&windows, i);
+			if (w == NULL)
+				continue;
+			TAILQ_FOREACH(wp, &w->panes, entry) {
+				if (wp->pid == pid) {
+					if (killpg(pid, SIGCONT) != 0)
+						kill(pid, SIGCONT);
+				}
+			}
+		}
+	}
+}
 	
 /* Fill window pollfds. */
 void
diff --git a/tmux.c b/tmux.c
index b0d15595..14b317c5 100644
--- a/tmux.c
+++ b/tmux.c
@@ -1,4 +1,4 @@
-/* $Id: tmux.c,v 1.104 2009-01-30 00:24:49 nicm Exp $ */
+/* $Id: tmux.c,v 1.105 2009-02-08 16:11:26 nicm Exp $ */
 
 /*
  * Copyright (c) 2007 Nicholas Marriott <nicm@users.sourceforge.net>
@@ -17,7 +17,6 @@
  */
 
 #include <sys/types.h>
-#include <sys/wait.h>
 
 #include <errno.h>
 #include <pwd.h>
@@ -46,6 +45,7 @@ const char	*_malloc_options = "AJX";
 volatile sig_atomic_t sigwinch;
 volatile sig_atomic_t sigterm;
 volatile sig_atomic_t sigcont;
+volatile sig_atomic_t sigchld;
 
 char		*cfg_file;
 struct options	 global_options;
@@ -103,7 +103,7 @@ sighandler(int sig)
 		sigterm = 1;
 		break;
 	case SIGCHLD:
-		waitpid(WAIT_ANY, NULL, WNOHANG);
+		sigchld = 1;
 		break;
 	case SIGCONT:
 		sigcont = 1;
diff --git a/tmux.h b/tmux.h
index 8f53d599..ddbd2e28 100644
--- a/tmux.h
+++ b/tmux.h
@@ -1,4 +1,4 @@
-/* $Id: tmux.h,v 1.259 2009-02-03 17:21:19 tcunha Exp $ */
+/* $Id: tmux.h,v 1.260 2009-02-08 16:11:26 nicm Exp $ */
 
 /*
  * Copyright (c) 2007 Nicholas Marriott <nicm@users.sourceforge.net>
@@ -585,10 +585,12 @@ struct window_pane {
 
 	int		 flags;
 #define PANE_HIDDEN 0x1
+#define PANE_RESTART 0x2
 	
 	char		*cmd;
 	char		*cwd;
 
+	pid_t		 pid;
 	int		 fd;
 	char		 tty[TTY_NAME_MAX];
 	struct buffer	*in;
@@ -610,7 +612,6 @@ TAILQ_HEAD(window_panes, window_pane);
 struct window {
 	char		*name;
 	struct timeval	 name_timer;
-	pid_t		 pgrp;
 
 	struct window_pane *active;	
 	struct window_panes panes;
@@ -974,6 +975,7 @@ char   *fgetln(FILE *, size_t *);
 extern volatile sig_atomic_t sigwinch;
 extern volatile sig_atomic_t sigterm;
 extern volatile sig_atomic_t sigcont;
+extern volatile sig_atomic_t sigchld;
 extern struct options global_options;
 extern struct options global_window_options;
 extern char	*cfg_file;
diff --git a/window.c b/window.c
index 2526289e..c8780e31 100644
--- a/window.c
+++ b/window.c
@@ -1,4 +1,4 @@
-/* $Id: window.c,v 1.64 2009-01-28 19:52:21 nicm Exp $ */
+/* $Id: window.c,v 1.65 2009-02-08 16:11:26 nicm Exp $ */
 
 /*
  * Copyright (c) 2007 Nicholas Marriott <nicm@users.sourceforge.net>
@@ -213,7 +213,6 @@ window_create(const char *name, const char *cmd, const char *cwd,
 
 	TAILQ_INIT(&w->panes);
 	w->active = NULL;
-	w->pgrp = -1;
 
 	w->sx = sx;
 	w->sy = sy;
@@ -562,14 +561,13 @@ window_pane_spawn(struct window_pane *wp,
 	ws.ws_col = screen_size_x(&wp->base);
 	ws.ws_row = screen_size_y(&wp->base);
 
-	wp->window->pgrp = -1;
 	if (gettimeofday(&wp->window->name_timer, NULL) != 0)
 		fatal("gettimeofday");
 	tv.tv_sec = 0;
 	tv.tv_usec = NAME_INTERVAL * 1000L;
 	timeradd(&wp->window->name_timer, &tv, &wp->window->name_timer);
 
- 	switch (forkpty(&wp->fd, wp->tty, NULL, &ws)) {
+ 	switch (wp->pid = forkpty(&wp->fd, wp->tty, NULL, &ws)) {
 	case -1:
 		wp->fd = -1;
 		xasprintf(cause, "%s: %s", cmd, strerror(errno));