+#include "common/macros.h"
+
+struct libc_pointer {
+ void **procedure;
+ const char *symbol;
+};
+
+#define DEFINE_LIBC_POINTER(name) { (void**)&plibc_## name, #name }
+
+#ifdef __linux__
+
+struct user_desc;
+
+static int (*plibc_clone)(int (*fn)(void *), void *child_stack,
+ int flags, void *arg, pid_t *ptid,
+ struct user_desc *tls, pid_t *ctid) = NULL;
+
+static int (*plibc_setns)(int fd, int nstype) = NULL;
+
+static int (*plibc_setresgid)(gid_t rgid, gid_t egid, gid_t sgid) = NULL;
+
+static int (*plibc_setresuid)(uid_t ruid, uid_t euid, uid_t suid) = NULL;
+
+static int (*plibc_unshare)(int flags) = NULL;
+
+#elif defined (__FreeBSD__)
+
+static pid_t (*plibc_rfork)(int flags) = NULL;
+
+#endif
+
+static int (*plibc_daemon)(int nochdir, int noclose) = NULL;
+
+static pid_t (*plibc_fork)(void) = NULL;
+
+static int (*plibc_setegid)(gid_t egid) = NULL;
+
+static int (*plibc_seteuid)(uid_t euid) = NULL;
+
+static int (*plibc_setgid)(gid_t gid) = NULL;
+
+static int (*plibc_setregid)(gid_t rgid, gid_t egid) = NULL;
+
+static int (*plibc_setreuid)(uid_t ruid, uid_t euid) = NULL;
+
+static int (*plibc_setuid)(uid_t uid) = NULL;
+
+static void lttng_ust_fork_wrapper_ctor(void)
+ __attribute__((constructor));
+
+static pthread_mutex_t initialization_guard = PTHREAD_MUTEX_INITIALIZER;
+static bool was_initialized = false;
+
+/*
+ * Must be called with initialization_guard held.
+ */
+static void initialize(void)
+{
+ const struct libc_pointer libc_pointers[] = {
+#ifdef __linux__
+ DEFINE_LIBC_POINTER(clone),
+ DEFINE_LIBC_POINTER(setns),
+ DEFINE_LIBC_POINTER(setresgid),
+ DEFINE_LIBC_POINTER(setresuid),
+ DEFINE_LIBC_POINTER(unshare),
+#elif defined (__FreeBSD__)
+ DEFINE_LIBC_POINTER(rfork),
+#endif
+ DEFINE_LIBC_POINTER(daemon),
+ DEFINE_LIBC_POINTER(fork),
+ DEFINE_LIBC_POINTER(setegid),
+ DEFINE_LIBC_POINTER(seteuid),
+ DEFINE_LIBC_POINTER(setgid),
+ DEFINE_LIBC_POINTER(setregid),
+ DEFINE_LIBC_POINTER(setreuid),
+ DEFINE_LIBC_POINTER(setuid),
+ };
+
+ size_t k;
+
+ for (k = 0; k < LTTNG_ARRAY_SIZE(libc_pointers); ++k) {
+ void *procedure = dlsym(RTLD_NEXT, libc_pointers[k].symbol);
+
+ if (NULL == procedure) {
+ fprintf(stderr,
+ "libustfork: unable to find \"%s\" symbol\n",
+ libc_pointers[k].symbol);
+ continue;
+ }
+
+ uatomic_set(libc_pointers[k].procedure, procedure);
+ }
+}
+
+/*
+ * Lazy initialization is required because it is possible for a shared library
+ * to have a constructor that is executed before our constructor, which could
+ * call some libc functions that we are wrapping.
+ *
+ * It is also possible for this library constructor to create a thread using the
+ * raw system call. Therefore, the lazy initialization must be multi-thread safe.
+ */
+static void *lazy_initialize(void **pfunc)
+{
+ void *func = uatomic_read(pfunc);
+
+ /*
+ * If *pfunc != NULL, then it is assumed that some thread has already
+ * called the initialization routine.
+ */
+ if (caa_likely(func)) {
+ goto out;
+ }
+
+ pthread_mutex_lock(&initialization_guard);
+ if (!was_initialized) {
+ initialize();
+ was_initialized = true;
+ }
+ func = *pfunc;
+ pthread_mutex_unlock(&initialization_guard);
+out:
+ return func;
+}
+
+#define LAZY_INITIALIZE_OR_NOSYS(ptr) \
+ ({ \
+ void *ret; \
+ \
+ ret = lazy_initialize((void**)&(ptr)); \
+ if (NULL == ret) { \
+ errno = ENOSYS; \
+ return -1; \
+ } \
+ \
+ ret; \
+ })
+
+static void lttng_ust_fork_wrapper_ctor(void)
+{
+ /*
+ * Using fork here because it is defined on all supported OS.
+ */
+ (void) lazy_initialize((void**)&plibc_fork);
+}
+