blob: 257c1cecdbf3161bc505b9bc214857c056924b48 [file] [log] [blame]
#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);
}