#include <sys/syscall.h>
#include "namespace.h"
#include <errno.h>
#include <link.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <pthread.h>
#include <spinlock.h>
#include "un-namespace.h"
#include "libc_private.h"
#include "rtld_lock.h"
#include "thr_private.h"
__weak_reference(_thr_atfork, _pthread_atfork);
__weak_reference(_thr_atfork, pthread_atfork);
bool _thr_after_fork = false;
int
_thr_atfork(void (*prepare)(void), void (*parent)(void),
void (*child)(void))
{
struct pthread *curthread;
struct pthread_atfork *af;
if ((af = malloc(sizeof(struct pthread_atfork))) == NULL)
return (ENOMEM);
af->prepare = prepare;
af->parent = parent;
af->child = child;
if (_thr_initial != NULL) {
curthread = _get_curthread();
THR_CRITICAL_ENTER(curthread);
_thr_rwl_wrlock(&_thr_atfork_lock);
TAILQ_INSERT_TAIL(&_thr_atfork_list, af, qe);
_thr_rwl_unlock(&_thr_atfork_lock);
THR_CRITICAL_LEAVE(curthread);
} else {
TAILQ_INSERT_TAIL(&_thr_atfork_list, af, qe);
}
return (0);
}
void
__pthread_cxa_finalize(struct dl_phdr_info *phdr_info)
{
atfork_head temp_list = TAILQ_HEAD_INITIALIZER(temp_list);
struct pthread *curthread;
struct pthread_atfork *af, *af1;
_thr_check_init();
curthread = _get_curthread();
THR_CRITICAL_ENTER(curthread);
_thr_rwl_wrlock(&_thr_atfork_lock);
TAILQ_FOREACH_SAFE(af, &_thr_atfork_list, qe, af1) {
if (__elf_phdr_match_addr(phdr_info, af->prepare) ||
__elf_phdr_match_addr(phdr_info, af->parent) ||
__elf_phdr_match_addr(phdr_info, af->child)) {
TAILQ_REMOVE(&_thr_atfork_list, af, qe);
TAILQ_INSERT_TAIL(&temp_list, af, qe);
}
}
_thr_rwl_unlock(&_thr_atfork_lock);
THR_CRITICAL_LEAVE(curthread);
while ((af = TAILQ_FIRST(&temp_list)) != NULL) {
TAILQ_REMOVE(&temp_list, af, qe);
free(af);
}
_thr_tsd_unload(phdr_info);
_thr_sigact_unload(phdr_info);
}
enum thr_fork_mode {
MODE_FORK,
MODE_PDFORK,
};
struct thr_fork_args {
enum thr_fork_mode mode;
void *fdp;
int flags;
};
static pid_t
thr_fork_impl(const struct thr_fork_args *a)
{
struct pthread *curthread;
struct pthread_atfork *af;
pid_t ret;
int errsave, cancelsave;
int was_threaded;
int rtld_locks[MAX_RTLD_LOCKS];
if (!_thr_is_inited()) {
switch (a->mode) {
case MODE_FORK:
return (__sys_fork());
case MODE_PDFORK:
return (__sys_pdfork(a->fdp, a->flags));
default:
errno = EDOOFUS;
return (-1);
}
}
curthread = _get_curthread();
cancelsave = curthread->no_cancel;
curthread->no_cancel = 1;
_thr_rwl_rdlock(&_thr_atfork_lock);
TAILQ_FOREACH_REVERSE(af, &_thr_atfork_list, atfork_head, qe) {
if (af->prepare != NULL)
af->prepare();
}
_thr_signal_block(curthread);
_thr_signal_prefork();
if (_thr_isthreaded() != 0) {
was_threaded = 1;
__thr_malloc_prefork(curthread);
_malloc_prefork();
__thr_pshared_atfork_pre();
_rtld_atfork_pre(rtld_locks);
} else {
was_threaded = 0;
}
switch (a->mode) {
case MODE_FORK:
ret = syscall(SYS_fork);
break;
case MODE_PDFORK:
ret = syscall(SYS_pdfork, a->fdp, a->flags);
break;
default:
ret = -1;
errno = EDOOFUS;
break;
}
if (ret == 0) {
errsave = errno;
curthread->cancel_pending = 0;
curthread->flags &= ~(THR_FLAGS_NEED_SUSPEND|THR_FLAGS_DETACHED);
curthread->tlflags &= ~TLFLAGS_IN_TDLIST;
if (was_threaded)
__thr_malloc_postfork(curthread);
thr_self(&curthread->tid);
_thr_umutex_init(&curthread->lock);
_mutex_fork(curthread);
_thr_signal_postfork_child();
if (was_threaded) {
_thr_after_fork = true;
_rtld_atfork_post(rtld_locks);
_thr_after_fork = false;
__thr_pshared_atfork_post();
}
_thr_setthreaded(0);
_libpthread_init(curthread);
_thr_rwl_rdlock(&_thr_atfork_lock);
if (was_threaded) {
_thr_setthreaded(1);
_malloc_postfork();
_thr_setthreaded(0);
}
_thr_signal_unblock(curthread);
TAILQ_FOREACH(af, &_thr_atfork_list, qe) {
if (af->child != NULL)
af->child();
}
_thr_rwlock_unlock(&_thr_atfork_lock);
curthread->no_cancel = cancelsave;
} else {
errsave = errno;
_thr_signal_postfork();
if (was_threaded) {
__thr_malloc_postfork(curthread);
_rtld_atfork_post(rtld_locks);
__thr_pshared_atfork_post();
_malloc_postfork();
}
_thr_signal_unblock(curthread);
TAILQ_FOREACH(af, &_thr_atfork_list, qe) {
if (af->parent != NULL)
af->parent();
}
_thr_rwlock_unlock(&_thr_atfork_lock);
curthread->no_cancel = cancelsave;
if (curthread->cancel_async)
_thr_testcancel(curthread);
}
errno = errsave;
return (ret);
}
__weak_reference(__thr_fork, _fork);
pid_t
__thr_fork(void)
{
struct thr_fork_args a;
a.mode = MODE_FORK;
return (thr_fork_impl(&a));
}
pid_t
__thr_pdfork(int *fdp, int flags)
{
struct thr_fork_args a;
a.mode = MODE_PDFORK;
a.fdp = fdp;
a.flags = flags;
return (thr_fork_impl(&a));
}