From: Michalis Pappas Date: Wed, 26 Mar 2025 07:52:00 +0000 (+0100) Subject: lib/posix-process: Add wait4() and waitid() X-Git-Url: http://xenbits.xensource.com/gitweb?a=commitdiff_plain;h=fab66b5531e28d75e4138c2051276f8b6cb6dca0;p=unikraft%2Funikraft.git lib/posix-process: Add wait4() and waitid() 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 Approved-by: Andrei Tatar Reviewed-by: Andrei Tatar Reviewed-by: Sergiu Moga GitHub-Closes: #1630 --- diff --git a/lib/posix-process/Makefile.uk b/lib/posix-process/Makefile.uk index 4d47fdb70..1b46c553d 100644 --- a/lib/posix-process/Makefile.uk +++ b/lib/posix-process/Makefile.uk @@ -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 diff --git a/lib/posix-process/process.h b/lib/posix-process/process.h index 70996a67d..7505b7b70 100644 --- a/lib/posix-process/process.h +++ b/lib/posix-process/process.h @@ -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) diff --git a/lib/posix-process/signal/siginfo.h b/lib/posix-process/signal/siginfo.h index 43e3a8d4c..5efcc74b4 100644 --- a/lib/posix-process/signal/siginfo.h +++ b/lib/posix-process/signal/siginfo.h @@ -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__ */ diff --git a/lib/posix-process/wait.c b/lib/posix-process/wait.c index 0a554f7d4..c4bdf4c42 100644 --- a/lib/posix-process/wait.c +++ b/lib/posix-process/wait.c @@ -1,49 +1,207 @@ /* SPDX-License-Identifier: BSD-3-Clause */ -/* - * Authors: Simon Kuenzer - * - * 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 -/* FIXME: #include */ -/* FIXME: #include */ -#include #include #include +#include +#include + +#include + +#if CONFIG_LIBPOSIX_PROCESS_MULTIPROCESS +#include +#include + +#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 */