From 1a8f65f2b8d33dc09f6ccb6a7bd6ca85760cf720 Mon Sep 17 00:00:00 2001
From: Stefan O'Rear <sorear@fastmail.com>
Date: Thu, 3 Sep 2020 05:20:45 -0400
Subject: [PATCH 07/16] Emulate wait4 using waitid

riscv32 and future architectures lack wait4.

waitpid is required by POSIX to be a cancellation point.  pclose is
specified as undefined if a cancellation occurs, so it would be
permitted for it to call a cancellable wait function; however, as a
quality of implementation matter, pclose must close the pipe fd before
it can wait (consider popen("yes","r")) and if the wait could be
interrupted the pipe FILE would be left in an intermediate state that
portable software cannot recover from, so the only useful behavior is
for pclose to NOT be a cancellation point.  We therefore support both at
a small cost in code size.

wait4 is historically not a cancellation point in musl; we retain that
since we need the non-cancellable version of __wait4 anyway.
---
 src/internal/__wait4.c | 55 ++++++++++++++++++++++++++++++++++++++++++
 src/internal/syscall.h | 12 +++++++++
 src/linux/wait4.c      |  2 +-
 src/process/waitpid.c  |  2 +-
 src/stdio/pclose.c     |  2 +-
 src/unistd/faccessat.c |  6 ++++-
 6 files changed, 75 insertions(+), 4 deletions(-)
 create mode 100644 src/internal/__wait4.c

diff --git a/src/internal/__wait4.c b/src/internal/__wait4.c
new file mode 100644
index 00000000..04d7dc64
--- /dev/null
+++ b/src/internal/__wait4.c
@@ -0,0 +1,55 @@
+#include <sys/wait.h>
+#include "syscall.h"
+
+#ifndef SYS_wait4
+hidden pid_t __wait4(pid_t pid, int *status, int options, void *kru, int cp)
+{
+	idtype_t t;
+	int r;
+	siginfo_t info;
+
+	info.si_pid = 0;
+	if (pid < -1) {
+		t = P_PGID;
+		pid = -pid;
+	} else if (pid == -1) {
+		t = P_ALL;
+	} else if (pid == 0) {
+		t = P_PGID;
+	} else {
+		t = P_PID;
+	}
+
+	if (cp) r = __syscall_cp(SYS_waitid, t, pid, &info, options|WEXITED, kru);
+	else r = __syscall(SYS_waitid, t, pid, &info, options|WEXITED, kru);
+
+	if (r<0) return r;
+
+	if (info.si_pid && status) {
+		int sw=0;
+		switch (info.si_code) {
+		case CLD_CONTINUED:
+			sw = 0xffff;
+			break;
+		case CLD_DUMPED:
+			sw = info.si_status&0x7f | 0x80;
+			break;
+		case CLD_EXITED:
+			sw = (info.si_status&0xff) << 8;
+			break;
+		case CLD_KILLED:
+			sw = info.si_status&0x7f;
+			break;
+		case CLD_STOPPED:
+		case CLD_TRAPPED:
+			/* see ptrace(2); the high bits of si_status can contain */
+			/* PTRACE_EVENT_ values which must be preserved */
+			sw = (info.si_status << 8) + 0x7f;
+			break;
+		}
+		*status = sw;
+	}
+
+	return info.si_pid;
+}
+#endif
diff --git a/src/internal/syscall.h b/src/internal/syscall.h
index 4f41e1dc..27642938 100644
--- a/src/internal/syscall.h
+++ b/src/internal/syscall.h
@@ -5,6 +5,8 @@
 #include <errno.h>
 #include <sys/syscall.h>
 #include "syscall_arch.h"
+#define __NEED_pid_t
+#include <bits/alltypes.h>
 
 #ifndef SYSCALL_RLIM_INFINITY
 #define SYSCALL_RLIM_INFINITY (~0ULL)
@@ -395,4 +397,14 @@ hidden void __procfdname(char __buf[static 15+3*sizeof(int)], unsigned);
 
 hidden void *__vdsosym(const char *, const char *);
 
+#ifdef SYS_wait4
+static inline pid_t __wait4(pid_t pid, int *status, int options, void *kru, int cp)
+{
+	if (cp) return __syscall_cp(SYS_wait4, pid, status, options, kru);
+	else return __syscall(SYS_wait4, pid, status, options, kru);
+}
+#else
+hidden pid_t __wait4(pid_t pid, int *status, int options, void *kru, int cp);
+#endif
+
 #endif
diff --git a/src/linux/wait4.c b/src/linux/wait4.c
index 83650e34..32652dc2 100644
--- a/src/linux/wait4.c
+++ b/src/linux/wait4.c
@@ -26,7 +26,7 @@ pid_t wait4(pid_t pid, int *status, int options, struct rusage *ru)
 	}
 #endif
 	char *dest = ru ? (char *)&ru->ru_maxrss - 4*sizeof(long) : 0;
-	r = __syscall(SYS_wait4, pid, status, options, dest);
+	r = __wait4(pid, status, options, dest, 0);
 	if (r>0 && ru && sizeof(time_t) > sizeof(long)) {
 		long kru[4];
 		memcpy(kru, dest, 4*sizeof(long));
diff --git a/src/process/waitpid.c b/src/process/waitpid.c
index 1b65bf05..e5ff27ca 100644
--- a/src/process/waitpid.c
+++ b/src/process/waitpid.c
@@ -3,5 +3,5 @@
 
 pid_t waitpid(pid_t pid, int *status, int options)
 {
-	return syscall_cp(SYS_wait4, pid, status, options, 0);
+	return __wait4(pid, status, options, 0, 1);
 }
diff --git a/src/stdio/pclose.c b/src/stdio/pclose.c
index 080a4262..b60d8f2c 100644
--- a/src/stdio/pclose.c
+++ b/src/stdio/pclose.c
@@ -7,7 +7,7 @@ int pclose(FILE *f)
 	int status, r;
 	pid_t pid = f->pipe_pid;
 	fclose(f);
-	while ((r=__syscall(SYS_wait4, pid, &status, 0, 0)) == -EINTR);
+	while ((r=__wait4(pid, &status, 0, 0, 0)) == -EINTR);
 	if (r<0) return __syscall_ret(r);
 	return status;
 }
diff --git a/src/unistd/faccessat.c b/src/unistd/faccessat.c
index 557503eb..8e8689c1 100644
--- a/src/unistd/faccessat.c
+++ b/src/unistd/faccessat.c
@@ -39,7 +39,6 @@ int faccessat(int fd, const char *filename, int amode, int flag)
 	char stack[1024];
 	sigset_t set;
 	pid_t pid;
-	int status;
 	int ret, p[2];
 
 	if (pipe2(p, O_CLOEXEC)) return __syscall_ret(-EBUSY);
@@ -53,7 +52,12 @@ int faccessat(int fd, const char *filename, int amode, int flag)
 	if (pid<0 || __syscall(SYS_read, p[0], &ret, sizeof ret) != sizeof(ret))
 		ret = -EBUSY;
 	__syscall(SYS_close, p[0]);
+#ifdef SYS_wait4
+	int status;
 	__syscall(SYS_wait4, pid, &status, __WCLONE, 0);
+#else
+	__syscall(SYS_waitid, P_PID, pid, &(siginfo_t){0}, __WCLONE|WEXITED, 0);
+#endif
 
 	__restore_sigs(&set);
 
-- 
2.29.2

