mirror of
https://github.com/clearlinux/graphene.git
synced 2026-09-06 13:51:28 +00:00
[LibOS] Implement waitid syscall
For now it supports only WEXITED and WNOWAIT (no WSTOPPED and WCONTINUED) and P_ALL, P_PID and P_PGID, but not P_PIDFD.
This commit is contained in:
committed by
Michał Kowalczyk
parent
558c1cd49b
commit
cbb3bc2958
@@ -5,6 +5,8 @@
|
||||
#include "shim_types.h"
|
||||
#include "ucontext.h"
|
||||
|
||||
#define __WCOREDUMP_BIT 0x80
|
||||
|
||||
void sigaction_make_defaults(struct __kernel_sigaction* sig_action);
|
||||
void thread_sigaction_reset_on_execve(struct shim_thread* thread);
|
||||
|
||||
|
||||
@@ -384,7 +384,8 @@ int shim_do_fork(void);
|
||||
int shim_do_vfork(void);
|
||||
int shim_do_execve(const char* file, const char** argv, const char** envp);
|
||||
noreturn int shim_do_exit(int error_code);
|
||||
pid_t shim_do_wait4(pid_t pid, int* stat_addr, int option, struct __kernel_rusage* ru);
|
||||
long shim_do_waitid(int which, pid_t id, siginfo_t* infop, int options, struct __kernel_rusage* ru);
|
||||
long shim_do_wait4(pid_t pid, int* stat_addr, int options, struct __kernel_rusage* ru);
|
||||
int shim_do_kill(pid_t pid, int sig);
|
||||
int shim_do_uname(struct new_utsname* buf);
|
||||
int shim_do_semget(key_t key, int nsems, int semflg);
|
||||
|
||||
@@ -844,8 +844,6 @@ int append_signal(struct shim_thread* thread, siginfo_t* info) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
#define __WCOREDUMP_BIT 0x80
|
||||
|
||||
static void sighandler_kill(int sig, siginfo_t* info, void* ucontext) {
|
||||
__UNUSED(info);
|
||||
__UNUSED(ucontext);
|
||||
|
||||
@@ -49,7 +49,8 @@ static void parse_ioctlop(va_list*);
|
||||
static void parse_fcntlop(va_list*);
|
||||
static void parse_seek(va_list*);
|
||||
static void parse_at_fdcwd(va_list*);
|
||||
static void parse_wait_option(va_list*);
|
||||
static void parse_wait_options(va_list*);
|
||||
static void parse_waitid_which(va_list*);
|
||||
|
||||
struct parser_table {
|
||||
int slow;
|
||||
@@ -122,7 +123,7 @@ struct parser_table {
|
||||
[__NR_execve] = {.slow = 1,
|
||||
.parser = {NULL, &parse_exec_args, &parse_exec_envp}},
|
||||
[__NR_exit] = {.slow = 0, .parser = {NULL}},
|
||||
[__NR_wait4] = {.slow = 1, .parser = {NULL, NULL, &parse_wait_option, NULL}},
|
||||
[__NR_wait4] = {.slow = 1, .parser = {NULL, NULL, &parse_wait_options, NULL}},
|
||||
[__NR_kill] = {.slow = 0, .parser = {NULL, &parse_signum, }},
|
||||
[__NR_uname] = {.slow = 0, .parser = {NULL}},
|
||||
[__NR_semget] = {.slow = 0, .parser = {NULL}},
|
||||
@@ -320,7 +321,8 @@ struct parser_table {
|
||||
[__NR_mq_notify] = {.slow = 0, .parser = {NULL}},
|
||||
[__NR_mq_getsetattr] = {.slow = 0, .parser = {NULL}},
|
||||
[__NR_kexec_load] = {.slow = 0, .parser = {NULL}},
|
||||
[__NR_waitid] = {.slow = 1, .parser = {NULL}},
|
||||
[__NR_waitid] = {.slow = 1,
|
||||
.parser = {&parse_waitid_which, NULL, NULL, &parse_wait_options, NULL}},
|
||||
[__NR_add_key] = {.slow = 0, .parser = {NULL}},
|
||||
[__NR_request_key] = {.slow = 0, .parser = {NULL}},
|
||||
[__NR_keyctl] = {.slow = 0, .parser = {NULL}},
|
||||
@@ -450,6 +452,27 @@ static inline int is_pointer(const char* type) {
|
||||
debug_vprintf(fmt, ap); \
|
||||
} while (0)
|
||||
|
||||
struct flag_table {
|
||||
const char *name;
|
||||
int flag;
|
||||
};
|
||||
|
||||
static int parse_flags(int flags, const struct flag_table* all_flags, size_t count) {
|
||||
bool first = true;
|
||||
for (size_t i = 0; i < count; i++)
|
||||
if (flags & all_flags[i].flag) {
|
||||
if (first)
|
||||
first = false;
|
||||
else
|
||||
PUTCH('|');
|
||||
|
||||
PUTS(all_flags[i].name);
|
||||
flags &= ~all_flags[i].flag;
|
||||
}
|
||||
|
||||
return flags;
|
||||
}
|
||||
|
||||
static inline void parse_string_arg(va_list* ap) {
|
||||
va_list ap_test_arg;
|
||||
va_copy(ap_test_arg, *ap);
|
||||
@@ -649,10 +672,7 @@ static void parse_clone_flags(va_list* ap) {
|
||||
|
||||
#define FLG(n) \
|
||||
{ "CLONE_" #n, CLONE_##n, }
|
||||
const struct {
|
||||
const char* name;
|
||||
int flag;
|
||||
} all_flags[] = {
|
||||
const struct flag_table all_flags[] = {
|
||||
FLG(VM),
|
||||
FLG(FS),
|
||||
FLG(FILES),
|
||||
@@ -678,16 +698,7 @@ static void parse_clone_flags(va_list* ap) {
|
||||
};
|
||||
#undef FLG
|
||||
|
||||
bool printed = false;
|
||||
for (size_t i = 0; i < ARRAY_SIZE(all_flags); i++)
|
||||
if (flags & all_flags[i].flag) {
|
||||
if (printed)
|
||||
PUTCH('|');
|
||||
else
|
||||
printed = true;
|
||||
PUTS(all_flags[i].name);
|
||||
flags &= ~all_flags[i].flag;
|
||||
}
|
||||
flags = parse_flags(flags, all_flags, ARRAY_SIZE(all_flags));
|
||||
|
||||
#define CLONE_SIGNAL_MASK 0xff
|
||||
int exit_signal = flags & CLONE_SIGNAL_MASK;
|
||||
@@ -1312,9 +1323,45 @@ static void parse_at_fdcwd(va_list* ap) {
|
||||
}
|
||||
}
|
||||
|
||||
static void parse_wait_option(va_list* ap) {
|
||||
int option = va_arg(*ap, int);
|
||||
static void parse_wait_options(va_list* ap) {
|
||||
int flags = va_arg(*ap, int);
|
||||
|
||||
if (option & WNOHANG)
|
||||
PUTS("WNOHANG");
|
||||
#define FLG(n) { #n, n }
|
||||
const struct flag_table all_flags[] = {
|
||||
FLG(WNOHANG),
|
||||
FLG(WNOWAIT),
|
||||
FLG(WEXITED),
|
||||
FLG(WSTOPPED),
|
||||
FLG(WCONTINUED),
|
||||
FLG(WUNTRACED),
|
||||
};
|
||||
#undef FLG
|
||||
|
||||
flags = parse_flags(flags, all_flags, ARRAY_SIZE(all_flags));
|
||||
if (flags)
|
||||
PRINTF("|0x%x", flags);
|
||||
}
|
||||
|
||||
static void parse_waitid_which(va_list* ap) {
|
||||
int which = va_arg(*ap, int);
|
||||
|
||||
switch (which) {
|
||||
case P_ALL:
|
||||
PUTS("P_ALL");
|
||||
break;
|
||||
case P_PID:
|
||||
PUTS("P_PID");
|
||||
break;
|
||||
case P_PGID:
|
||||
PUTS("P_PGID");
|
||||
break;
|
||||
#ifdef P_PIDFD
|
||||
case P_PIDFD:
|
||||
PUTS("P_PIDFD");
|
||||
break;
|
||||
#endif
|
||||
default:
|
||||
PRINTF("%d", which);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -307,8 +307,12 @@ DEFINE_SHIM_SYSCALL(execve, 3, shim_do_execve, int, const char*, file, const cha
|
||||
/* exit: sys/shim_exit.c */
|
||||
DEFINE_SHIM_SYSCALL(exit, 1, shim_do_exit, int, int, error_code)
|
||||
|
||||
/* waitid: sys/shim_wait.c */
|
||||
DEFINE_SHIM_SYSCALL(waitid, 5, shim_do_waitid, long, int, which, pid_t, id, siginfo_t*, infop,
|
||||
int, options, struct __kernel_rusage*, ru)
|
||||
|
||||
/* wait4: sys/shim_wait.c */
|
||||
DEFINE_SHIM_SYSCALL(wait4, 4, shim_do_wait4, pid_t, pid_t, pid, int*, stat_addr, int, option,
|
||||
DEFINE_SHIM_SYSCALL(wait4, 4, shim_do_wait4, long, pid_t, pid, int*, stat_addr, int, options,
|
||||
struct __kernel_rusage*, ru)
|
||||
|
||||
/* kill: sys/shim_sigaction.c */
|
||||
@@ -840,9 +844,6 @@ SHIM_SYSCALL_RETURN_ENOSYS(kexec_load, 4, int, unsigned long, entry, unsigned lo
|
||||
struct kexec_segment*, segments, unsigned long, flags)
|
||||
*/
|
||||
|
||||
SHIM_SYSCALL_RETURN_ENOSYS(waitid, 5, int, int, which, pid_t, pid, siginfo_t*, infop, int, options,
|
||||
struct __kernel_rusage*, ru)
|
||||
|
||||
/*
|
||||
SHIM_SYSCALL_RETURN_ENOSYS(add_key, 5, int, const char*, type, const char*, description,
|
||||
const void*, payload, size_t, plen, key_serial_t, destringid)
|
||||
|
||||
+128
-26
@@ -15,25 +15,43 @@
|
||||
#include "pal.h"
|
||||
#include "pal_error.h"
|
||||
#include "shim_internal.h"
|
||||
#include "shim_signal.h"
|
||||
#include "shim_table.h"
|
||||
#include "shim_thread.h"
|
||||
#include "shim_utils.h"
|
||||
|
||||
pid_t shim_do_wait4(pid_t pid, int* status, int option, struct __kernel_rusage* ru) {
|
||||
|
||||
/* For wait4() return value */
|
||||
#define WCOREFLAG 0x80
|
||||
|
||||
|
||||
long shim_do_waitid(int which, pid_t id, siginfo_t* infop, int options, struct __kernel_rusage* ru) {
|
||||
struct shim_thread* cur = get_cur_thread();
|
||||
struct shim_thread* thread = NULL;
|
||||
int ret = 0;
|
||||
int ret;
|
||||
__UNUSED(ru);
|
||||
|
||||
if (option & ~(WNOHANG | WUNTRACED | WCONTINUED | __WNOTHREAD | __WCLONE | __WALL)) {
|
||||
/* Note that we don't support WSTOPPED or WCONTINUED correctly. */
|
||||
if (options & ~(WNOHANG | WNOWAIT | WEXITED | WSTOPPED | WCONTINUED |
|
||||
__WNOTHREAD| __WCLONE | __WALL))
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (pid > 0) {
|
||||
if (!(options & (WEXITED | WSTOPPED | WCONTINUED)))
|
||||
return -EINVAL;
|
||||
|
||||
if (!(which == P_PGID || which == P_ALL || which == P_PID))
|
||||
return -EINVAL;
|
||||
|
||||
if (infop && test_user_memory(infop, sizeof(*infop), /*write=*/true))
|
||||
return -EFAULT;
|
||||
|
||||
if (which == P_PID) {
|
||||
pid_t pid = id;
|
||||
|
||||
if (!(thread = lookup_thread(pid)))
|
||||
return -ECHILD;
|
||||
|
||||
if (!(option & WNOHANG)) {
|
||||
if (!(options & WNOHANG)) {
|
||||
block_pid:
|
||||
object_wait_with_retry(thread->exit_event);
|
||||
}
|
||||
@@ -42,13 +60,18 @@ pid_t shim_do_wait4(pid_t pid, int* status, int option, struct __kernel_rusage*
|
||||
|
||||
if (thread->is_alive) {
|
||||
unlock(&thread->lock);
|
||||
if (!(option & WNOHANG))
|
||||
if (!(options & WNOHANG))
|
||||
goto block_pid;
|
||||
put_thread(thread);
|
||||
|
||||
if (infop) {
|
||||
infop->si_pid = 0;
|
||||
infop->si_signo = 0;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!LIST_EMPTY(thread, siblings)) {
|
||||
if (!(options & WNOWAIT) && !LIST_EMPTY(thread, siblings)) {
|
||||
debug("reaping thread %p\n", thread);
|
||||
struct shim_thread* parent = thread->parent;
|
||||
assert(parent);
|
||||
@@ -75,7 +98,7 @@ pid_t shim_do_wait4(pid_t pid, int* status, int option, struct __kernel_rusage*
|
||||
return -ECHILD;
|
||||
}
|
||||
|
||||
if (!(option & WNOHANG)) {
|
||||
if (!(options & WNOHANG)) {
|
||||
block:
|
||||
if (cur->child_exit_event)
|
||||
while (LISTP_EMPTY(&cur->exited_children)) {
|
||||
@@ -85,47 +108,126 @@ pid_t shim_do_wait4(pid_t pid, int* status, int option, struct __kernel_rusage*
|
||||
}
|
||||
}
|
||||
|
||||
if (pid == 0 || pid < -1) {
|
||||
if (pid == 0)
|
||||
pid = -cur->pgid;
|
||||
if (which == P_PGID) {
|
||||
IDTYPE pgid;
|
||||
if (id == 0)
|
||||
pgid = cur->pgid;
|
||||
else
|
||||
pgid = id;
|
||||
|
||||
LISTP_FOR_EACH_ENTRY(thread, &cur->exited_children, siblings) {
|
||||
if (thread->pgid == (IDTYPE)-pid)
|
||||
if (thread->pgid == pgid) {
|
||||
get_thread(thread);
|
||||
goto found_child;
|
||||
}
|
||||
}
|
||||
|
||||
if (!(option & WNOHANG))
|
||||
if (!(options & WNOHANG))
|
||||
goto block;
|
||||
} else {
|
||||
assert(which == P_ALL);
|
||||
|
||||
if (!LISTP_EMPTY(&cur->exited_children)) {
|
||||
thread = LISTP_FIRST_ENTRY(&cur->exited_children, struct shim_thread, siblings);
|
||||
get_thread(thread);
|
||||
goto found_child;
|
||||
}
|
||||
}
|
||||
|
||||
unlock(&cur->lock);
|
||||
|
||||
if (infop) {
|
||||
infop->si_pid = 0;
|
||||
infop->si_signo = 0;
|
||||
}
|
||||
return 0;
|
||||
|
||||
found_child:
|
||||
LISTP_DEL_INIT(thread, &cur->exited_children, siblings);
|
||||
put_thread(cur);
|
||||
thread->parent = NULL;
|
||||
if (!(options & WNOWAIT)) {
|
||||
LISTP_DEL_INIT(thread, &cur->exited_children, siblings);
|
||||
put_thread(cur);
|
||||
thread->parent = NULL;
|
||||
|
||||
if (LISTP_EMPTY(&cur->exited_children))
|
||||
DkEventClear(cur->child_exit_event);
|
||||
if (LISTP_EMPTY(&cur->exited_children))
|
||||
DkEventClear(cur->child_exit_event);
|
||||
}
|
||||
|
||||
unlock(&cur->lock);
|
||||
|
||||
found:
|
||||
if (status) {
|
||||
/* Bits 0--7 are for the signal, if any.
|
||||
* Bits 8--15 are for the exit code */
|
||||
*status = thread->term_signal;
|
||||
*status |= ((thread->exit_code & 0xff) << 8);
|
||||
ret = 0;
|
||||
if (infop) {
|
||||
infop->si_pid = thread->tid;
|
||||
infop->si_uid = thread->uid;
|
||||
infop->si_signo = SIGCHLD;
|
||||
|
||||
if (thread->term_signal == 0) {
|
||||
infop->si_code = CLD_EXITED;
|
||||
infop->si_status = thread->exit_code;
|
||||
} else if (thread->term_signal & __WCOREDUMP_BIT) {
|
||||
infop->si_code = CLD_DUMPED;
|
||||
infop->si_status = thread->term_signal & ~__WCOREDUMP_BIT;
|
||||
} else {
|
||||
infop->si_code = CLD_KILLED;
|
||||
infop->si_status = thread->term_signal;
|
||||
}
|
||||
}
|
||||
|
||||
ret = thread->tid;
|
||||
del_thread(thread);
|
||||
if (!(options & WNOWAIT))
|
||||
del_thread(thread);
|
||||
|
||||
put_thread(thread);
|
||||
return ret;
|
||||
}
|
||||
|
||||
long shim_do_wait4(pid_t pid, int* status, int options, struct __kernel_rusage* ru) {
|
||||
int which;
|
||||
pid_t id;
|
||||
siginfo_t info;
|
||||
|
||||
/* Note that only WNOHANG is handled correctly. */
|
||||
if (options & ~(WNOHANG | WUNTRACED | WCONTINUED | __WNOTHREAD | __WCLONE | __WALL)) {
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (status && test_user_memory(status, sizeof(*status), /*write=*/true))
|
||||
return -EFAULT;
|
||||
|
||||
/* Prepare options for shim_do_waitid(). */
|
||||
options |= WEXITED;
|
||||
if (options & WUNTRACED)
|
||||
options &= ~WUNTRACED;
|
||||
|
||||
if (pid < -1) {
|
||||
which = P_PGID;
|
||||
id = -pid;
|
||||
} else if (pid == -1) {
|
||||
which = P_ALL;
|
||||
id = 0;
|
||||
} else if (pid == 0) {
|
||||
which = P_PGID;
|
||||
id = 0;
|
||||
} else {
|
||||
which = P_PID;
|
||||
id = pid;
|
||||
}
|
||||
|
||||
info.si_pid = 0;
|
||||
int ret = shim_do_waitid(which, id, &info, options, ru);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
if (info.si_pid == 0)
|
||||
return 0;
|
||||
|
||||
if (status) {
|
||||
if (info.si_code == CLD_EXITED) {
|
||||
*status = (info.si_status & 0xff) << 8;
|
||||
} else if (info.si_code == CLD_DUMPED) {
|
||||
*status = info.si_status | WCOREFLAG;
|
||||
} else {
|
||||
*status = info.si_status;
|
||||
}
|
||||
}
|
||||
return info.si_pid;
|
||||
}
|
||||
|
||||
@@ -3685,9 +3685,7 @@ skip = yes
|
||||
[wait402]
|
||||
skip = yes
|
||||
|
||||
[waitid01]
|
||||
skip = yes
|
||||
|
||||
# Not supported: waitid with WSTOPPED and WCONTINUED
|
||||
[waitid02]
|
||||
skip = yes
|
||||
|
||||
|
||||
@@ -3164,18 +3164,18 @@ skip = yes
|
||||
[vmsplice02]
|
||||
skip = yes
|
||||
|
||||
# tries to open '/proc/2/stat'
|
||||
[wait401]
|
||||
timeout = 40
|
||||
must-pass =
|
||||
2
|
||||
3
|
||||
|
||||
# tries to open '/proc/sys/kernel/pid_max'
|
||||
[wait402]
|
||||
skip = yes
|
||||
|
||||
[waitid01]
|
||||
skip = yes
|
||||
|
||||
# Not supported: waitid with WSTOPPED and WCONTINUED
|
||||
[waitid02]
|
||||
skip = yes
|
||||
|
||||
|
||||
Reference in New Issue
Block a user