chg: forward job control signals to worker process (issue5051)
authorYuya Nishihara <yuya@tcha.org>
Tue, 19 Jan 2016 22:31:59 +0900
changeset 28086 65d24ca35496
parent 28085 c0d1bf1b26b7
child 28087 0b7ce0b16d8a
chg: forward job control signals to worker process (issue5051) This is necessary to suspend/resume long pulls, interactive curses session, etc. The implementation is based on emacsclient, but our version doesn't test if chg process is foreground or not before propagating SIGCONT. This is because chg isn't always an interactive session. If we copy the SIGTTIN/SIGTTOU emulation from emacsclient, non-interactive session can't be moved to a background job. $ chg pull ^Z suspended $ bg %1 [1] continued [1] suspended (tty input) # wrong https://github.com/emacs-mirror/emacs/blob/0e96320/lib-src/emacsclient.c#L1094
contrib/chg/chg.c
--- a/contrib/chg/chg.c	Fri Jan 29 22:52:16 2016 +0900
+++ b/contrib/chg/chg.c	Tue Jan 19 22:31:59 2016 +0900
@@ -231,6 +231,38 @@
 	debugmsg("forward signal %d", sig);
 }
 
+static void handlestopsignal(int sig)
+{
+	sigset_t unblockset, oldset;
+	struct sigaction sa, oldsa;
+	if (sigemptyset(&unblockset) < 0)
+		goto error;
+	if (sigaddset(&unblockset, sig) < 0)
+		goto error;
+	memset(&sa, 0, sizeof(sa));
+	sa.sa_handler = SIG_DFL;
+	sa.sa_flags = SA_RESTART;
+	if (sigemptyset(&sa.sa_mask) < 0)
+		goto error;
+
+	forwardsignal(sig);
+	if (raise(sig) < 0)  /* resend to self */
+		goto error;
+	if (sigaction(sig, &sa, &oldsa) < 0)
+		goto error;
+	if (sigprocmask(SIG_UNBLOCK, &unblockset, &oldset) < 0)
+		goto error;
+	/* resent signal will be handled before sigprocmask() returns */
+	if (sigprocmask(SIG_SETMASK, &oldset, NULL) < 0)
+		goto error;
+	if (sigaction(sig, &oldsa, NULL) < 0)
+		goto error;
+	return;
+
+error:
+	abortmsg("failed to handle stop signal (errno = %d)", errno);
+}
+
 static void setupsignalhandler(pid_t pid)
 {
 	if (pid <= 0)
@@ -253,6 +285,17 @@
 	sa.sa_flags |= SA_RESETHAND;
 	if (sigaction(SIGTERM, &sa, NULL) < 0)
 		goto error;
+
+	/* propagate job control requests to worker */
+	sa.sa_handler = forwardsignal;
+	sa.sa_flags = SA_RESTART;
+	if (sigaction(SIGCONT, &sa, NULL) < 0)
+		goto error;
+	sa.sa_handler = handlestopsignal;
+	sa.sa_flags = SA_RESTART;
+	if (sigaction(SIGTSTP, &sa, NULL) < 0)
+		goto error;
+
 	return;
 
 error: