[RFC,v5,04/21] sysdeps/wait: Use waitid if avaliable
Commit Message
If the waitid syscall is avaliable let's use that as waitpid
and wait4 aren't always avaliable (they aren't avaliable on RV32).
Unfortunately waitid is substantially differnt to waitpid and wait4, so
the conversion ends up being complex.
For full support we need the 5.4+ kernel as that allows a pid of 0 with
the P_PGID idtype.
Signed-off-by: Alistair Francis <alistair.francis@wdc.com>
* sysdeps/unix/sysv/linux/wait.c: Use __NR_waitid if avaliable.
* sysdeps/unix/sysv/linux/waitpid.c: Likewise.
* sysdeps/unix/sysv/linux/waitpid_nocancel.c: Likewise.
---
sysdeps/unix/sysv/linux/wait.c | 41 +++++++++++++--
sysdeps/unix/sysv/linux/waitpid.c | 59 +++++++++++++++++++++-
sysdeps/unix/sysv/linux/waitpid_nocancel.c | 56 +++++++++++++++++++-
3 files changed, 151 insertions(+), 5 deletions(-)
@@ -26,9 +26,44 @@
pid_t
__libc_wait (int *stat_loc)
{
- pid_t result = SYSCALL_CANCEL (wait4, WAIT_ANY, stat_loc, 0,
- (struct rusage *) NULL);
- return result;
+#ifdef __NR_wait4
+ return SYSCALL_CANCEL (wait4, WAIT_ANY, stat_loc, 0,
+ (struct rusage *) NULL);
+#else
+ siginfo_t infop;
+ __pid_t ret;
+
+ ret = SYSCALL_CANCEL (waitid, P_ALL, 0, &infop, WEXITED, NULL);
+
+ if (ret < 0)
+ return ret;
+
+ if (stat_loc)
+ {
+ *stat_loc = 0;
+ switch (infop.si_code)
+ {
+ case CLD_EXITED:
+ *stat_loc = infop.si_status << 8;
+ break;
+ case CLD_DUMPED:
+ *stat_loc = 0x80;
+ /* Fallthrough */
+ case CLD_KILLED:
+ *stat_loc |= infop.si_status;
+ break;
+ case CLD_TRAPPED:
+ case CLD_STOPPED:
+ *stat_loc = infop.si_status << 8 | 0x7f;
+ break;
+ case CLD_CONTINUED:
+ *stat_loc = 0xffff;
+ break;
+ }
+ }
+
+ return infop.si_pid;
+#endif
}
weak_alias (__libc_wait, __wait)
@@ -20,14 +20,71 @@
#include <sysdep-cancel.h>
#include <stdlib.h>
#include <sys/wait.h>
+#include <unistd.h>
__pid_t
__waitpid (__pid_t pid, int *stat_loc, int options)
{
#ifdef __NR_waitpid
return SYSCALL_CANCEL (waitpid, pid, stat_loc, options);
-#else
+#elif defined(__NR_wait4)
return SYSCALL_CANCEL (wait4, pid, stat_loc, options, NULL);
+#else
+ __pid_t ret;
+ idtype_t idtype = P_PID;
+ siginfo_t infop;
+
+ if (pid < -1)
+ {
+ idtype = P_PGID;
+ pid *= -1;
+ }
+ else if (pid == -1)
+ {
+ idtype = P_ALL;
+ }
+ else if (pid == 0)
+ {
+ /* Linux Kernels 5.4+ support pid 0 with P_PGID to specify wait on
+ * the current PID's group. Earlier kernels will return -EINVAL.
+ */
+ idtype = P_PGID;
+ }
+
+ options |= WEXITED;
+
+ ret = SYSCALL_CANCEL (waitid, idtype, pid, &infop, options, NULL);
+
+ if (ret < 0)
+ {
+ return ret;
+ }
+
+ if (stat_loc)
+ {
+ *stat_loc = 0;
+ switch (infop.si_code)
+ {
+ case CLD_EXITED:
+ *stat_loc = infop.si_status << 8;
+ break;
+ case CLD_DUMPED:
+ *stat_loc = 0x80;
+ /* Fallthrough */
+ case CLD_KILLED:
+ *stat_loc |= infop.si_status;
+ break;
+ case CLD_TRAPPED:
+ case CLD_STOPPED:
+ *stat_loc = infop.si_status << 8 | 0x7f;
+ break;
+ case CLD_CONTINUED:
+ *stat_loc = 0xffff;
+ break;
+ }
+ }
+
+ return infop.si_pid;
#endif
}
libc_hidden_def (__waitpid)
@@ -27,8 +27,62 @@ __waitpid_nocancel (__pid_t pid, int *stat_loc, int options)
{
#ifdef __NR_waitpid
return INLINE_SYSCALL_CALL (waitpid, pid, stat_loc, options);
-#else
+#elif defined (__NR_wait4)
return INLINE_SYSCALL_CALL (wait4, pid, stat_loc, options, NULL);
+#else
+ __pid_t ret;
+ idtype_t idtype = P_PID;
+ siginfo_t infop;
+
+ if (pid < -1)
+ {
+ idtype = P_PGID;
+ pid *= -1;
+ }
+ else if (pid == -1)
+ {
+ idtype = P_ALL;
+ }
+ else if (pid == 0)
+ {
+ /* Linux Kernels 5.4+ support pid 0 with P_PGID to specify wait on
+ * the current PID's group. Earlier kernels will return -EINVAL.
+ */
+ idtype = P_PGID;
+ }
+
+ options |= WEXITED;
+
+ ret = INLINE_SYSCALL_CALL (waitid, idtype, pid, &infop, options, NULL);
+
+ if (ret < 0)
+ return ret;
+
+ if (stat_loc)
+ {
+ *stat_loc = 0;
+ switch (infop.si_code)
+ {
+ case CLD_EXITED:
+ *stat_loc = infop.si_status << 8;
+ break;
+ case CLD_DUMPED:
+ *stat_loc = 0x80;
+ /* Fallthrough */
+ case CLD_KILLED:
+ *stat_loc |= infop.si_status;
+ break;
+ case CLD_TRAPPED:
+ case CLD_STOPPED:
+ *stat_loc = infop.si_status << 8 | 0x7f;
+ break;
+ case CLD_CONTINUED:
+ *stat_loc = 0xffff;
+ break;
+ }
+ }
+
+ return infop.si_pid;
#endif
}
libc_hidden_def (__waitpid_nocancel)