]> xenbits.xensource.com Git - unikraft/unikraft.git/commitdiff
lib/posix-process: Add wait4() and waitid()
authorMichalis Pappas <michalis@unikraft.io>
Wed, 26 Mar 2025 07:52:00 +0000 (08:52 +0100)
committerUnikraft Bot <monkey@unikraft.io>
Wed, 30 Apr 2025 09:56:07 +0000 (09:56 +0000)
wait4() and waitid() block until a state change in the calling process.
In the case of termination, the wait syscall reaps the child. For more
info see wait(2).

This patch implements support for process termination (exit / kill) and
does not implement other state changes like stop and continue.

Checkpatch-Ignore: MACRO_ARG_REUSE
Checkpatch-Ignore: POINTER_LOCATION
Checkpatch-Ignore: TYPECAST_INT_CONSTANT
Signed-off-by: Michalis Pappas <michalis@unikraft.io>
Approved-by: Andrei Tatar <andrei@unikraft.io>
Reviewed-by: Andrei Tatar <andrei@unikraft.io>
Reviewed-by: Sergiu Moga <sergiu@unikraft.io>
GitHub-Closes: #1630

lib/posix-process/Makefile.uk
lib/posix-process/process.h
lib/posix-process/signal/siginfo.h
lib/posix-process/wait.c

index 4d47fdb700edf6a2eeb9e50077998674085beddc..1b46c553dda65515f9aa9cf0097069c0130a4904 100644 (file)
@@ -66,7 +66,7 @@ UK_PROVIDED_SYSCALLS-$(CONFIG_LIBPOSIX_PROCESS_SIGNALFD) += signalfd-3
 UK_PROVIDED_SYSCALLS-$(CONFIG_LIBPOSIX_PROCESS) += kill-2
 UK_PROVIDED_SYSCALLS-$(CONFIG_LIBPOSIX_PROCESS) += tgkill-3
 UK_PROVIDED_SYSCALLS-$(CONFIG_LIBPOSIX_PROCESS) += tkill-2
-UK_PROVIDED_SYSCALLS-$(CONFIG_LIBPOSIX_PROCESS) += wait4-4 waitid-4
+UK_PROVIDED_SYSCALLS-$(CONFIG_LIBPOSIX_PROCESS) += wait4-4 waitid-5
 UK_PROVIDED_SYSCALLS-$(CONFIG_LIBPOSIX_PROCESS) += getpgid-1
 UK_PROVIDED_SYSCALLS-$(CONFIG_LIBPOSIX_PROCESS) += setpgid-2
 UK_PROVIDED_SYSCALLS-$(CONFIG_LIBPOSIX_PROCESS) += setsid-0
index 70996a67d40407ebb8215773066f295cd3ea2b5e..7505b7b702b8c0a392bb10a335cc7c748b66d3b2 100644 (file)
@@ -77,6 +77,7 @@ struct posix_thread {
 #if CONFIG_LIBPOSIX_PROCESS_SIGNAL
        struct uk_signal_tdesc *signal;
 #endif /* CONFIG_LIBPOSIX_PROCESS_SIGNAL */
+       pid_t wait_pid;
 
        /* TODO: Mutex */
 };
@@ -95,6 +96,10 @@ extern __uk_tls struct posix_thread *pthread_self;
        uk_list_for_each_entry_safe((_pthread), (_pthreadn),            \
                                    &(_proc)->threads, thread_list_entry)
 
+#define uk_pprocess_foreach_child(_proc, _pchild, _pchildn)            \
+       uk_list_for_each_entry_safe((_pchild), (_pchildn),              \
+                                   &(_proc)->children, child_list_entry)
+
 #define uk_pthread_current()                                           \
        uk_thread_uktls_var(uk_thread_current(), pthread_self)
 
index 43e3a8d4c6b16b0e33ba495ba8d9b6bdfe79f567..5efcc74b44dc4d2213c72232419c8a5dd6a41a7f 100644 (file)
@@ -41,4 +41,27 @@ static inline void set_siginfo_sigqueue(int signum, siginfo_t *si,
        si->si_value = si_usr->si_value;
 }
 
+static inline void set_siginfo_wait(struct posix_process *pprocess,
+                                   siginfo_t *si)
+{
+       UK_ASSERT(pprocess);
+       UK_ASSERT(si);
+
+       si->si_pid = pprocess->pid;
+       si->si_uid = 0;
+       si->si_signo = SIGCHLD;
+       switch (pprocess->state) {
+       case POSIX_PROCESS_EXITED:
+               si->si_status = CLD_EXITED;
+               si->si_code = pprocess->exit_status;
+               break;
+       case POSIX_PROCESS_KILLED:
+               si->si_status = CLD_KILLED;
+               si->si_code = pprocess->exit_status;
+               break;
+       default:
+               uk_pr_warn("CLD_STOPPED, CLD_CONTINUED, CLD_TRAPPED, CLD_DUMPED stubbed");
+       }
+}
+
 #endif /* __UK_PROCESS_SIGINFO_H__ */
index 0a554f7d438221713dcba5a08fa700f60e5a4d1a..c4bdf4c42a451cd7f69f09b73e4ba5567af58264 100644 (file)
 /* SPDX-License-Identifier: BSD-3-Clause */
-/*
- * Authors: Simon Kuenzer <simon.kuenzer@neclab.eu>
- *
- * Copyright (c) 2022, NEC Laboratories Europe GmbH, NEC Corporation.
+/* Copyright (c) 2022, NEC Laboratories Europe GmbH, NEC Corporation.
  *                     All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- *
- * 1. Redistributions of source code must retain the above copyright
- *    notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- *    notice, this list of conditions and the following disclaimer in the
- *    documentation and/or other materials provided with the distribution.
- * 3. Neither the name of the copyright holder nor the names of its
- *    contributors may be used to endorse or promote products derived from
- *    this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
- * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
- * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
- * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
- * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
- * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
- * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
- * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
- * POSSIBILITY OF SUCH DAMAGE.
+ * Copyright (c) 2024, Unikraft GmbH and The Unikraft Authors.
+ * Licensed under the BSD-3-Clause License (the "License").
+ * You may not use this file except in compliance with the License.
  */
 
-#include <sys/types.h>
-/* FIXME: #include <sys/wait.h> */
-/* FIXME: #include <sys/siginfo.h> */
-#include <uk/syscall.h>
 #include <errno.h>
 #include <stddef.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+
+#include <uk/syscall.h>
+
+#if CONFIG_LIBPOSIX_PROCESS_MULTIPROCESS
+#include <uk/process.h>
+#include <uk/semaphore.h>
+
+#include "process.h"
+#include "signal/siginfo.h"
+
+#define PPROCESS_STATE_CHANGED(_pp, _state)                              \
+       (((_state) & WEXITED) && ((_pp)->state == POSIX_PROCESS_EXITED || \
+                               (_pp)->state == POSIX_PROCESS_KILLED))
+
+static inline
+struct posix_process *pprocess_state_changed(struct posix_process *pproc,
+                                            int state)
+{
+       UK_ASSERT(pproc);
+       return (PPROCESS_STATE_CHANGED(pproc, state)) ? pproc : NULL;
+}
+
+static inline
+struct posix_process *pprocess_child_state_changed(int state)
+{
+       struct posix_process *pchild, *pchildn;
+       struct posix_process *pproc;
+
+       uk_pprocess_foreach_child(uk_pprocess_current(), pchild, pchildn)
+               if ((pproc = pprocess_state_changed(pchild, state)))
+                       return pproc;
+       return NULL;
+}
+
+static int pprocess_reap(struct posix_process *pprocess, siginfo_t *si)
+{
+       pid_t pid;
+
+       UK_ASSERT(pprocess);
+       UK_ASSERT(uk_list_empty(&pprocess->threads));
+
+       if (si)
+               set_siginfo_wait(pprocess, si);
+
+       pid = pprocess->pid;
+       pprocess_release(pprocess);
+
+       return pid;
+}
+
+/* Wait helper
+ *
+ * @param pid: child pid to wait for or -1 for any
+ * @param opt: same as waitid()
+ * @return child pid, zero if no waitable, negative value on error
+ */
+static int pprocess_wait(pid_t pid, siginfo_t *si, int opt)
+{
+       struct posix_process *this_process;
+       struct posix_thread *this_thread;
+       struct posix_process *waitable = NULL;
+       struct posix_process *child = NULL;
+       int ret;
+
+       UK_ASSERT(pid > 0 || pid == -1);
+       UK_ASSERT(si);
 
-/* FIXME: Provide with sys/wait.h */
-struct rusage;
-typedef unsigned int idtype_t;
-typedef unsigned int id_t;
-typedef unsigned int siginfo_t;
+       this_thread = uk_pthread_current();
+       UK_ASSERT(this_thread);
+
+       this_process = this_thread->process;
+       UK_ASSERT(this_process);
+
+       if (uk_list_empty(&this_process->children))
+               return -ECHILD;
+
+       this_thread->state = POSIX_THREAD_BLOCKED_WAIT;
+
+       if (pid > 0) {
+               child = pid2pprocess(pid);
+               /* Child does not exist or not our child. This also
+                * covers the case where the process chose to ignore
+                * SIGCHLD or set SA_NOCLDWAIT and therefore _exit()
+                * terminated it without further ado.
+                */
+               if (unlikely(!child || child->parent != this_process)) {
+                       ret = -ECHILD;
+                       goto out;
+               }
+
+               /* If the process exited already, reap and return the pid */
+               if (child->state != POSIX_PROCESS_RUNNING) {
+                       ret = pprocess_reap(child, si);
+                       goto out;
+               }
+
+               this_thread->wait_pid = pid;
+       } else { /* any */
+               this_thread->wait_pid = -1;
+       }
+
+       for (;;) {
+               /* Find process with changed state. No change means that
+                * the state change happened to a different child, so we
+                * loop back to the semaphore.
+                */
+               if (pid > 0)
+                       waitable = pprocess_state_changed(child, opt);
+               else /* any */
+                       waitable = pprocess_child_state_changed(opt);
+
+               if (waitable) {
+                       ret = waitable->pid;
+                       if (!(opt & WNOWAIT))
+                               pprocess_reap(waitable, si);
+                       break;
+               }
+
+               if (opt & WNOHANG) {
+                       ret = 0;
+                       break;
+               }
+
+               /* Wait for a state change on one of our children */
+               uk_semaphore_down(&this_thread->process->wait_semaphore);
+       }
+
+out:
+       this_thread->state = POSIX_THREAD_RUNNING;
+       return ret;
+}
 
+UK_SYSCALL_R_DEFINE(pid_t, wait4, pid_t, pid,
+                   int *, wstatus, int, options,
+                   struct rusage *, rusage)
+{
+       siginfo_t si;
+       int ret;
+
+       if (options)
+               if (unlikely(!(options & (WNOHANG | WUNTRACED | WCONTINUED))))
+                       return -EINVAL;
+
+       if (pid < -1 || pid == 0) /* process groups */
+               return -ENOTSUP;
+
+       ret = pprocess_wait(pid, &si, options | WEXITED);
+
+       if (ret > 0 && wstatus) {
+               if (si.si_status == CLD_KILLED)
+                       *wstatus = si.si_code;
+               else /* CLD_EXITED */
+                       *wstatus = si.si_code << 8;
+       }
+
+       return ret;
+}
+
+UK_LLSYSCALL_R_DEFINE(int, waitid, idtype_t, idtype, id_t, id,
+                     siginfo_t *, infop, int, options,
+                     struct rusage * __unused, rusage)
+{
+       int ret;
+
+       if (options)
+               if (unlikely(!(options & (WEXITED | WSTOPPED | WCONTINUED |
+                                         WNOHANG | WNOWAIT))))
+                       return -EINVAL;
+
+       switch (idtype) {
+       case P_PID:
+               ret = pprocess_wait(id, infop, options);
+               break;
+       case P_PIDFD:
+       case P_PGID:
+               return -ENOTSUP;
+       case P_ALL:
+               ret = pprocess_wait(-1, infop, options);
+               break;
+       default:
+               return -EINVAL;
+       }
+
+       /* POSIX.1-2008 requires that infop must not be NULL, yet Linux
+        * violates the standard and returns the pid of the waited child
+        * when that happens. wait(2) discourages applications to rely on
+        * that behavior, still for backwards compatibility we implement
+        * Linux's behavior nevertheless.
+        */
+       return (infop && ret >= 0) ? 0 : ret;
+}
+#else /* !CONFIG_LIBPOSIX_PROCESS_MULTIPROCESS */
 UK_SYSCALL_R_DEFINE(pid_t, wait4, pid_t, pid,
                    int *, wstatus, int, options,
                    struct rusage *, rusage)
@@ -51,28 +209,36 @@ UK_SYSCALL_R_DEFINE(pid_t, wait4, pid_t, pid,
        return -ECHILD;
 }
 
-UK_SYSCALL_R_DEFINE(int, waitid, idtype_t, idtype, id_t, id,
-                   siginfo_t *, infop, int, options)
+UK_LLSYSCALL_R_DEFINE(int, waitid, idtype_t, idtype, id_t, id,
+                     siginfo_t *, infop, int, options,
+                     struct rusage *, rusage)
 {
        return -ECHILD;
 }
+#endif /* !CONFIG_LIBPOSIX_PROCESS_MULTIPROCESS */
 
 #if UK_LIBC_SYSCALLS
 pid_t wait3(int *wstatus, int options, struct rusage *rusage)
 {
-       return uk_syscall_e_wait4((long) -1, (long) wstatus,
-                                 (long) options, (long) rusage);
+       return uk_syscall_e_wait4((long)-1, (long)wstatus,
+                                 (long)options, (long)rusage);
 }
 
 int waitpid(pid_t pid, int *wstatus, int options)
 {
-       return uk_syscall_e_wait4((long) pid, (long) wstatus,
-                                 (long) options, (long) NULL);
+       return uk_syscall_e_wait4((long)pid, (long)wstatus,
+                                 (long)options, (long)NULL);
 }
 
 int wait(int *wstatus)
 {
-       return uk_syscall_e_wait4((long) -1, (long) wstatus,
-                                 (long) 0x0, (long) NULL);
+       return uk_syscall_e_wait4((long)-1, (long)wstatus,
+                                 (long)0x0, (long)NULL);
+}
+
+int waitid(idtype_t idtype, id_t id, siginfo_t *infop, int options)
+{
+       return uk_syscall_e_waitid((long)idtype, (long)id, (long)infop,
+                                  (long)options, (long)NULL);
 }
 #endif /* !UK_LIBC_SYSCALLS */