Skip to content

Commit 8d4dd85

Browse files
committed
Implement libc_pidfd_spawn_impl()
With this, KOS's `pidfd_spawn(3)` API should now be functional
1 parent f98b087 commit 8d4dd85

File tree

3 files changed

+80
-11
lines changed

3 files changed

+80
-11
lines changed

kos/include/kos/except/reason/inval.h

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -151,6 +151,7 @@ enum {
151151
E_INVALID_ARGUMENT_CONTEXT_CLONE_SIGHAND_WITHOUT_VM, /* E_INVALID_ARGUMENT_BAD_FLAG_COMBINATION: `CLONE_SIGHAND` was given without 'CLONE_VM'. */
152152
E_INVALID_ARGUMENT_CONTEXT_CLONE3_INVALID_FLAGS, /* E_INVALID_ARGUMENT_UNKNOWN_FLAG: Unrecognized `struct clone_args::ca_flags' passed to `clone3(2)' */
153153
E_INVALID_ARGUMENT_CONTEXT_CLONE3_INVALID_SIZE, /* E_INVALID_ARGUMENT_BAD_VALUE: Unrecognized `size' argument passed to `clone3(2)' */
154+
E_INVALID_ARGUMENT_CONTEXT_CLONE3_INVALID_SIGNO, /* E_INVALID_ARGUMENT_BAD_VALUE: Bad signal number passed in `struct clone_args::ca_exit_signal' */
154155
E_INVALID_ARGUMENT_CONTEXT_UNSHARE_WHAT, /* E_INVALID_ARGUMENT_UNKNOWN_FLAG: The `what' argument passed to `unshare(2)' isn't a subset of:
155156
* `CLONE_VM | CLONE_FS | CLONE_FILES | CLONE_SIGHAND | CLONE_CRED | CLONE_NEWNS | CLONE_SYSVSEM |
156157
* CLONE_NEWUTS | CLONE_NEWIPC | CLONE_NEWUSER | CLONE_NEWPID | CLONE_NEWNET | CLONE_IO' */
@@ -457,6 +458,7 @@ enum {
457458
#define E_INVALID_ARGUMENT_CONTEXT_CLONE_SIGHAND_WITHOUT_VM E_INVALID_ARGUMENT_CONTEXT_CLONE_SIGHAND_WITHOUT_VM /* E_INVALID_ARGUMENT_BAD_FLAG_COMBINATION: `CLONE_SIGHAND` was given without 'CLONE_VM'. */
458459
#define E_INVALID_ARGUMENT_CONTEXT_CLONE3_INVALID_FLAGS E_INVALID_ARGUMENT_CONTEXT_CLONE3_INVALID_FLAGS /* E_INVALID_ARGUMENT_UNKNOWN_FLAG: Unrecognized `struct clone_args::ca_flags' passed to `clone3(2)' */
459460
#define E_INVALID_ARGUMENT_CONTEXT_CLONE3_INVALID_SIZE E_INVALID_ARGUMENT_CONTEXT_CLONE3_INVALID_SIZE /* E_INVALID_ARGUMENT_BAD_VALUE: Unrecognized `size' argument passed to `clone3(2)' */
461+
#define E_INVALID_ARGUMENT_CONTEXT_CLONE3_INVALID_SIGNO E_INVALID_ARGUMENT_CONTEXT_CLONE3_INVALID_SIGNO /* E_INVALID_ARGUMENT_BAD_VALUE: Bad signal number passed in `struct clone_args::ca_exit_signal' */
460462
#define E_INVALID_ARGUMENT_CONTEXT_UNSHARE_WHAT E_INVALID_ARGUMENT_CONTEXT_UNSHARE_WHAT /* E_INVALID_ARGUMENT_UNKNOWN_FLAG: The `what' argument passed to `unshare(2)' isn't a subset of:
461463
* `CLONE_VM | CLONE_FS | CLONE_FILES | CLONE_SIGHAND | CLONE_CRED | CLONE_NEWNS | CLONE_SYSVSEM |
462464
* CLONE_NEWUTS | CLONE_NEWIPC | CLONE_NEWUSER | CLONE_NEWPID | CLONE_NEWNET | CLONE_IO' */
@@ -734,7 +736,8 @@ enum {
734736
#define E_INVALID_ARGUMENT_CONTEXT_CLONE_SIGHAND_WITHOUT_VM 899 /* E_INVALID_ARGUMENT_BAD_FLAG_COMBINATION: `CLONE_SIGHAND` was given without 'CLONE_VM'. */
735737
#define E_INVALID_ARGUMENT_CONTEXT_CLONE3_INVALID_FLAGS 900 /* E_INVALID_ARGUMENT_UNKNOWN_FLAG: Unrecognized `struct clone_args::ca_flags' passed to `clone3(2)' */
736738
#define E_INVALID_ARGUMENT_CONTEXT_CLONE3_INVALID_SIZE 901 /* E_INVALID_ARGUMENT_BAD_VALUE: Unrecognized `size' argument passed to `clone3(2)' */
737-
#define E_INVALID_ARGUMENT_CONTEXT_UNSHARE_WHAT 902 /* E_INVALID_ARGUMENT_UNKNOWN_FLAG: The `what' argument passed to `unshare(2)' isn't a subset of:
739+
#define E_INVALID_ARGUMENT_CONTEXT_CLONE3_INVALID_SIGNO 902 /* E_INVALID_ARGUMENT_BAD_VALUE: Bad signal number passed in `struct clone_args::ca_exit_signal' */
740+
#define E_INVALID_ARGUMENT_CONTEXT_UNSHARE_WHAT 903 /* E_INVALID_ARGUMENT_UNKNOWN_FLAG: The `what' argument passed to `unshare(2)' isn't a subset of:
738741
* `CLONE_VM | CLONE_FS | CLONE_FILES | CLONE_SIGHAND | CLONE_CRED | CLONE_NEWNS | CLONE_SYSVSEM |
739742
* CLONE_NEWUTS | CLONE_NEWIPC | CLONE_NEWUSER | CLONE_NEWPID | CLONE_NEWNET | CLONE_IO' */
740743
/* System calls: PIDFD */

kos/src/kernel/core/sched/task-clone.c

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
#ifndef GUARD_KERNEL_SRC_SCHED_TASK_CLONE_C
2121
#define GUARD_KERNEL_SRC_SCHED_TASK_CLONE_C 1
2222
#define _GNU_SOURCE 1
23+
#define _KOS_SOURCE 1
2324

2425
#include <kernel/compiler.h>
2526

@@ -1045,6 +1046,17 @@ sys_clone3_impl(struct icpustate const *__restrict state,
10451046
~(CLONE_CLEAR_SIGHAND | CLONE_INTO_CGROUP | CLONE_NEWTIME | CLONE_CRED));
10461047
}
10471048

1049+
/* Verify the user-given `exit_signal' argument. */
1050+
if (!sigvalid(cargs.tca_exit_signal)) {
1051+
if (cargs.tca_exit_signal == 0) {
1052+
cargs.tca_exit_signal = SIGCHLD;
1053+
} else {
1054+
THROW(E_INVALID_ARGUMENT_BAD_VALUE,
1055+
E_INVALID_ARGUMENT_CONTEXT_CLONE3_INVALID_SIGNO,
1056+
cargs.tca_exit_signal);
1057+
}
1058+
}
1059+
10481060
/* Spawn a new child thread/process */
10491061
child = task_clone(state, &cargs);
10501062
cpid = task_gettid_of(child);

kos/src/libc/user/spawn.c

Lines changed: 64 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,16 @@
2323
#include "../api.h"
2424
/**/
2525

26+
#include <kos/syscalls.h>
27+
#include <sys/wait.h>
28+
29+
#include <errno.h>
30+
#include <sched.h>
31+
#include <signal.h>
32+
#include <stdlib.h>
33+
#include <string.h>
34+
#include <unistd.h>
35+
2636
#include "spawn.h"
2737

2838
DECL_BEGIN
@@ -37,16 +47,60 @@ NOTHROW_RPC(LIBCCALL libc_pidfd_spawn_impl)(fd_t *__restrict pidfd,
3747
__TARGV,
3848
__TENVP)
3949
/*[[[body:libc_pidfd_spawn_impl]]]*/
40-
/*AUTO*/{
41-
(void)pidfd;
42-
(void)exec_type;
43-
(void)exec_arg;
44-
(void)file_actions;
45-
(void)attrp;
46-
(void)___argv;
47-
(void)___envp;
48-
CRT_UNIMPLEMENTEDF("pidfd_spawn_impl(pidfd: %p, exec_type: %x, exec_arg: %p, file_actions: %p, attrp: %p, ___argv: %p, ___envp: %p)", pidfd, exec_type, exec_arg, file_actions, attrp, ___argv, ___envp); /* TODO */
49-
return ENOSYS;
50+
{
51+
int status;
52+
errno_t result, error, old_errno;
53+
pid_t child;
54+
struct clone_args cargs;
55+
old_errno = libc_geterrno();
56+
(void)libc_seterrno(0);
57+
58+
/* This is the magic right here: do a VFORK+PIDFD clone(2) system call. */
59+
bzero(&cargs, sizeof(cargs));
60+
cargs.ca_flags = CLONE_VM | CLONE_VFORK | CLONE_PIDFD;
61+
cargs.ca_exit_signal = SIGCHLD;
62+
cargs.ca_pidfd = pidfd;
63+
child = sys_clone3(&cargs, sizeof(cargs));
64+
if (child == 0)
65+
goto do_exec;
66+
if (E_ISERR(child)) {
67+
/* The vfork() itself failed. */
68+
(void)libc_seterrno(old_errno);
69+
return -child;
70+
}
71+
72+
/* Check if something within the child failed after vfork(). */
73+
result = libc_geterrno();
74+
if (result != 0)
75+
goto err_join_zombie_child;
76+
77+
/* Restore the old errno */
78+
(void)libc_seterrno(old_errno);
79+
return result;
80+
err_join_zombie_child:
81+
/* Unless the child was already spawned as detached,
82+
* we still have to re-join it, or else it will be
83+
* left dangling as a zombie process! */
84+
if (waitpid(child, &status, 0) < 0) {
85+
if (libc_geterrno() == EINTR)
86+
goto err_join_zombie_child;
87+
}
88+
/* Must close the (already-opened) PIDfd file descriptor. Even though
89+
* we *did* manage to spawn a new process, the exec part that came
90+
* after failed, meaning we can't let our caller inherit the FD. */
91+
(void)close(*pidfd);
92+
(void)libc_seterrno(old_errno);
93+
return result;
94+
do_exec:
95+
error = libc_posix_spawn_child(exec_type, exec_arg, file_actions, attrp, ___argv, ___envp);
96+
if (error != 0) {
97+
/* If the exec fails, it will have modified `errno' to indicate this fact.
98+
* And since we're sharing VMs with our parent process, the error reason
99+
* will have already been written back to our parent's VM, so there's
100+
* actually nothing left for us to do, but to simply exit! */
101+
libc_seterrno(error);
102+
}
103+
_Exit(127);
50104
}
51105
/*[[[end:libc_pidfd_spawn_impl]]]*/
52106

0 commit comments

Comments
 (0)