summary refs log tree commit diff
diff options
context:
space:
mode:
authorLeah Neukirchen <leah@vuxu.org>2022-02-03 21:38:47 +0100
committerLeah Neukirchen <leah@vuxu.org>2022-02-03 21:38:47 +0100
commit069b5cac49980f9ff2bb6eaa27a9a91ccfbc7b2f (patch)
treec6cfe82fec1d651804fb6461a9fb4c698fa7055d
parent6faa8273d9106223cdd909b25f6130ef52a63472 (diff)
downloadrvnit-069b5cac49980f9ff2bb6eaa27a9a91ccfbc7b2f.tar.gz
rvnit-069b5cac49980f9ff2bb6eaa27a9a91ccfbc7b2f.tar.xz
rvnit-069b5cac49980f9ff2bb6eaa27a9a91ccfbc7b2f.zip
rewrite timedwait using a self-pipe
OpenBSD doesn't implement sigtimedwait.
-rw-r--r--rvnit.c95
1 files changed, 56 insertions, 39 deletions
diff --git a/rvnit.c b/rvnit.c
index 1babf0b..52ba9ec 100644
--- a/rvnit.c
+++ b/rvnit.c
@@ -58,6 +58,7 @@ pthread_t main_thread;
 pthread_t socket_thread;
 pthread_t logger_thread;
 
+int selfpipe[2];
 int selflogfd[2];
 int newlogfd[2];
 int globallogfd[2];
@@ -87,6 +88,49 @@ on_signal(int sig)
 	case SIGCONT:
 		/* do nothing, but interrupt the system call */
 		break;
+	case SIGCHLD:
+		write(selfpipe[1], "", 1);
+		break;
+	}
+}
+
+pid_t
+timedwait(int *wstatus, int secs)
+{
+	struct pollfd fds[1];
+	fds[0].fd = selfpipe[0];
+	fds[0].events = POLLIN;
+
+	pid_t pid = waitpid(-1, wstatus, WNOHANG);
+	if (pid > 0)
+		return pid;
+	if (pid < 0 && errno == ECHILD)
+		return -1;
+
+	while (1) {
+		int n = poll(fds, 1, secs < 0 ? -1 : secs * 1000);
+
+		if (n < 0 && errno == EINTR)
+			return -1;
+
+		if (n == 0) // timeout
+			return 0;
+
+		if (n == 1) {
+			char trash;
+			read(selfpipe[0], &trash, 1);
+
+			pid = waitpid(-1, wstatus, WNOHANG);
+			if (pid < 0) {
+				if (errno == ECHILD)
+					return -1;
+				if (errno == EINTR)
+					return -1;
+			}
+
+			if (pid > 0)
+				return pid;
+		}
 	}
 }
 
@@ -520,42 +564,6 @@ reap(pid_t pid, int status)
 	return -1;
 }
 
-pid_t
-timedwait(int *wstatus, int secs)
-{
-	struct timespec timeout = {secs, 0};
-	sigset_t childset;
-	sigemptyset(&childset);
-	sigaddset(&childset, SIGCHLD);
-
-	/* we block SIGCHLD here, so that we do not lose the signal
-	   possibly sent between waitpid(-1) and sigtimedwait */
-	sigprocmask(SIG_BLOCK, &childset, 0);
-
-	pid_t pid;
-
-	while (1) {
-		pid = waitpid(-1, wstatus, WNOHANG);
-		if (pid == 0) { // nothing to reap
-			if (sigtimedwait(&childset, 0, &timeout) == SIGCHLD)
-				continue;
-			if (errno == EAGAIN) // hit timeout, return pid = 0
-				break;
-		} else if (pid < 0) {
-			if (errno == ECHILD)
-				break;
-			if (errno == EINTR)
-				continue;
-		} else {
-			break; // pid > 0
-		}
-	}
-
-	sigprocmask(SIG_UNBLOCK, &childset, 0);
-
-	return pid;
-}
-
 /* -1: no error handler, 0: error handler successful, 1: error handler failed */
 int
 on_error()
@@ -706,6 +714,12 @@ main(int argc, char *argv[])
 		return 111;
 	}
 
+	pipe(selfpipe);
+	fcntl(selfpipe[0], F_SETFL, O_NONBLOCK);
+	fcntl(selfpipe[1], F_SETFL, O_NONBLOCK);
+	fcntl(selfpipe[0], F_SETFD, FD_CLOEXEC);
+	fcntl(selfpipe[1], F_SETFD, FD_CLOEXEC);
+
 	pipe(selflogfd);
 	fcntl(selflogfd[0], F_SETFL, O_NONBLOCK);
 	fcntl(selflogfd[1], F_SETFL, O_NONBLOCK);
@@ -770,6 +784,9 @@ main(int argc, char *argv[])
 	if (pid1)
 		ioctl(0, TIOCNOTTY);
 
+	sigaction(SIGCHLD, &(struct sigaction){
+		    .sa_handler=on_signal, .sa_mask=allset,
+		    .sa_flags=SA_NOCLDSTOP|SA_RESTART }, 0);
 	sigaction(SIGINT, &(struct sigaction){
 		    .sa_handler=on_signal, .sa_mask=allset }, 0);
 	if (!real_pid1) {
@@ -824,7 +841,7 @@ main(int argc, char *argv[])
 
 		while (oneshot) {
 			int status = 0;
-			int pid = wait(&status);
+			int pid = timedwait(&status, -1);
 
 			if (pid < 0 && errno == ECHILD)
 				break;
@@ -933,7 +950,7 @@ cont1:
 
 		int status = 0;
 		errno = 0;
-		int pid = wait(&status);
+		int pid = timedwait(&status, -1);
 
 		if (pid < 0) {
 			if (errno == ECHILD)
@@ -1019,7 +1036,7 @@ fatal:
 
 		while (oneshot) {
 			int status = 0;
-			int pid = wait(&status);
+			int pid = timedwait(&status, -1);
 
 			if (pid < 0 && errno == ECHILD)
 				break;