[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:
Paweł Marczewski
2020-09-23 18:22:42 +02:00
committed by Michał Kowalczyk
parent 558c1cd49b
commit cbb3bc2958
8 changed files with 209 additions and 60 deletions
+2
View File
@@ -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);
+2 -1
View File
@@ -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);
-2
View File
@@ -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);
+68 -21
View File
@@ -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;
}
}
+5 -4
View File
@@ -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
View File
@@ -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;
}
+1 -3
View File
@@ -3685,9 +3685,7 @@ skip = yes
[wait402]
skip = yes
[waitid01]
skip = yes
# Not supported: waitid with WSTOPPED and WCONTINUED
[waitid02]
skip = yes
+3 -3
View File
@@ -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