| #define _GNU_SOURCE |
| #include <stdarg.h> |
| #include <unistd.h> |
| #include <sched.h> |
| #include "pthread_impl.h" |
| #include "syscall.h" |
| #include "lock.h" |
| #include "fork_impl.h" |
| |
| struct clone_start_args { |
| int (*func)(void *); |
| void *arg; |
| sigset_t sigmask; |
| }; |
| |
| static int clone_start(void *arg) |
| { |
| struct clone_start_args *csa = arg; |
| __post_Fork(0); |
| __restore_sigs(&csa->sigmask); |
| return csa->func(csa->arg); |
| } |
| |
| int clone(int (*func)(void *), void *stack, int flags, void *arg, ...) |
| { |
| struct clone_start_args csa; |
| va_list ap; |
| pid_t *ptid = 0, *ctid = 0; |
| void *tls = 0; |
| |
| /* Flags that produce an invalid thread/TLS state are disallowed. */ |
| int badflags = CLONE_THREAD | CLONE_SETTLS | CLONE_CHILD_CLEARTID; |
| |
| if ((flags & badflags) || !stack) |
| return __syscall_ret(-EINVAL); |
| |
| va_start(ap, arg); |
| if (flags & (CLONE_PIDFD | CLONE_PARENT_SETTID | CLONE_CHILD_SETTID)) |
| ptid = va_arg(ap, pid_t *); |
| if (flags & CLONE_CHILD_SETTID) { |
| tls = va_arg(ap, void *); |
| ctid = va_arg(ap, pid_t *); |
| } |
| va_end(ap); |
| |
| /* If CLONE_VM is used, it's impossible to give the child a consistent |
| * thread structure. In this case, the best we can do is assume the |
| * caller is content with an extremely restrictive execution context |
| * like the one vfork() would provide. */ |
| if (flags & CLONE_VM) return __syscall_ret( |
| __clone(func, stack, flags, arg, ptid, tls, ctid)); |
| |
| __block_all_sigs(&csa.sigmask); |
| LOCK(__abort_lock); |
| |
| /* Setup the a wrapper start function for the child process to do |
| * mimic _Fork in producing a consistent execution state. */ |
| csa.func = func; |
| csa.arg = arg; |
| int ret = __clone(clone_start, stack, flags, &csa, ptid, tls, ctid); |
| |
| __post_Fork(ret); |
| __restore_sigs(&csa.sigmask); |
| return __syscall_ret(ret); |
| } |