#include <lttng/ust.h>
#include <ust-comm.h>
#include <usterr-signal-safe.h>
+#include <helper.h>
#include "tracepoint-internal.h"
#include "ltt-tracer-core.h"
#include "compat.h"
+#include "../libringbuffer/tlsfixup.h"
/*
* Has lttng ust comm constructor been called ?
*/
static int sem_count = { 2 };
+/*
+ * Counting nesting within lttng-ust. Used to ensure that calling fork()
+ * from liblttng-ust does not execute the pre/post fork handlers.
+ */
+static int __thread lttng_ust_nest_count;
+
/*
* Info about socket and associated listener thread.
*/
extern void ltt_ring_buffer_client_discard_exit(void);
extern void ltt_ring_buffer_metadata_client_exit(void);
+/*
+ * Force a read (imply TLS fixup for dlopen) of TLS variables.
+ */
+static
+void lttng_fixup_nest_count_tls(void)
+{
+ asm volatile ("" : : "m" (lttng_ust_nest_count));
+}
+
static
int setup_local_apps(void)
{
struct ustcomm_ust_reply lur;
int shm_fd, wait_fd;
union ust_args args;
+ ssize_t len;
ust_lock();
else
ret = lttng_ust_objd_unref(lum->handle);
break;
+ case LTTNG_UST_FILTER:
+ {
+ /* Receive filter data */
+ struct lttng_ust_filter_bytecode *bytecode;
+
+ if (lum->u.filter.data_size > FILTER_BYTECODE_MAX_LEN) {
+ ERR("Filter data size is too large: %u bytes\n",
+ lum->u.filter.data_size);
+ ret = -EINVAL;
+ goto error;
+ }
+
+ if (lum->u.filter.reloc_offset > lum->u.filter.data_size) {
+ ERR("Filter reloc offset %u is not within data\n",
+ lum->u.filter.reloc_offset);
+ ret = -EINVAL;
+ goto error;
+ }
+
+ bytecode = zmalloc(sizeof(*bytecode) + lum->u.filter.data_size);
+ if (!bytecode) {
+ ret = -ENOMEM;
+ goto error;
+ }
+ len = ustcomm_recv_unix_sock(sock, bytecode->data,
+ lum->u.filter.data_size);
+ switch (len) {
+ case 0: /* orderly shutdown */
+ ret = 0;
+ free(bytecode);
+ goto error;
+ case -1:
+ DBG("Receive failed from lttng-sessiond with errno %d", errno);
+ if (errno == ECONNRESET) {
+ ERR("%s remote end closed connection\n", sock_info->name);
+ ret = -EINVAL;
+ free(bytecode);
+ goto error;
+ }
+ ret = -EINVAL;
+ goto end;
+ default:
+ if (len == lum->u.filter.data_size) {
+ DBG("filter data received\n");
+ break;
+ } else {
+ ERR("incorrect filter data message size: %zd\n", len);
+ ret = -EINVAL;
+ free(bytecode);
+ goto end;
+ }
+ }
+ bytecode->len = lum->u.filter.data_size;
+ bytecode->reloc_offset = lum->u.filter.reloc_offset;
+ if (ops->cmd) {
+ ret = ops->cmd(lum->handle, lum->cmd,
+ (unsigned long) bytecode,
+ &args);
+ if (ret) {
+ free(bytecode);
+ }
+ /* don't free bytecode if everything went fine. */
+ } else {
+ ret = -ENOSYS;
+ free(bytecode);
+ }
+ break;
+ }
default:
if (ops->cmd)
ret = ops->cmd(lum->handle, lum->cmd,
|| lum->cmd == LTTNG_UST_CHANNEL
|| lum->cmd == LTTNG_UST_METADATA)
&& lur.ret_code == USTCOMM_OK) {
+ int sendret = 0;
+
/* we also need to send the file descriptors. */
ret = ustcomm_send_fds_unix_sock(sock,
&shm_fd, &shm_fd,
1, sizeof(int));
if (ret < 0) {
perror("send shm_fd");
- goto error;
+ sendret = ret;
}
+ /*
+ * The sessiond expects 2 file descriptors, even upon
+ * error.
+ */
ret = ustcomm_send_fds_unix_sock(sock,
&wait_fd, &wait_fd,
1, sizeof(int));
perror("send wait_fd");
goto error;
}
+ if (sendret) {
+ ret = sendret;
+ goto error;
+ }
+ }
+ /*
+ * LTTNG_UST_TRACEPOINT_FIELD_LIST_GET needs to send the field
+ * after the reply.
+ */
+ if (lur.ret_code == USTCOMM_OK) {
+ switch (lum->cmd) {
+ case LTTNG_UST_TRACEPOINT_FIELD_LIST_GET:
+ len = ustcomm_send_unix_sock(sock,
+ &args.field_list.entry,
+ sizeof(args.field_list.entry));
+ if (len != sizeof(args.field_list.entry)) {
+ ret = -1;
+ goto error;
+ }
+ }
}
/*
* We still have the memory map reference, and the fds have been
int ret;
if (sock_info->socket != -1) {
- ret = close(sock_info->socket);
+ ret = ustcomm_close_unix_sock(sock_info->socket);
if (ret) {
ERR("Error closing apps socket");
}
* If the open failed because the file did not exist, try
* creating it ourself.
*/
+ lttng_ust_nest_count++;
pid = fork();
+ lttng_ust_nest_count--;
if (pid > 0) {
int status;
ret = ftruncate(wait_shm_fd, mmap_size);
if (ret) {
PERROR("ftruncate");
- exit(EXIT_FAILURE);
+ _exit(EXIT_FAILURE);
}
- exit(EXIT_SUCCESS);
+ _exit(EXIT_SUCCESS);
}
/*
* For local shm, we need to have rw access to accept
*/
if (!sock_info->global && errno != EACCES) {
ERR("Error opening shm %s", sock_info->wait_shm_path);
- exit(EXIT_FAILURE);
+ _exit(EXIT_FAILURE);
}
/*
* The shm exists, but we cannot open it RW. Report
* success.
*/
- exit(EXIT_SUCCESS);
+ _exit(EXIT_SUCCESS);
} else {
return -1;
}
* This thread does not allocate any resource, except within
* handle_message, within mutex protection. This mutex protects against
* fork and exit.
- * The other moment it allocates resources is at socket connexion, which
+ * The other moment it allocates resources is at socket connection, which
* is also protected by the mutex.
*/
static
}
if (sock_info->socket != -1) {
- ret = close(sock_info->socket);
+ ret = ustcomm_close_unix_sock(sock_info->socket);
if (ret) {
ERR("Error closing %s apps socket", sock_info->name);
}
void __attribute__((constructor)) lttng_ust_init(void)
{
struct timespec constructor_timeout;
+ sigset_t sig_all_blocked, orig_parent_mask;
+ pthread_attr_t thread_attr;
int timeout_mode;
int ret;
if (uatomic_xchg(&initialized, 1) == 1)
return;
+ /*
+ * Fixup interdependency between TLS fixup mutex (which happens
+ * to be the dynamic linker mutex) and ust_lock, taken within
+ * the ust lock.
+ */
+ lttng_fixup_event_tls();
+ lttng_fixup_ringbuffer_tls();
+ lttng_fixup_vtid_tls();
+ lttng_fixup_nest_count_tls();
+ lttng_fixup_procname_tls();
+
/*
* We want precise control over the order in which we construct
* our sub-libraries vs starting to receive commands from
if (ret) {
ERR("Error setting up to local apps");
}
- ret = pthread_create(&local_apps.ust_listener, NULL,
- ust_listener_thread, &local_apps);
+ /* A new thread created by pthread_create inherits the signal mask
+ * from the parent. To avoid any signal being received by the
+ * listener thread, we block all signals temporarily in the parent,
+ * while we create the listener thread.
+ */
+ sigfillset(&sig_all_blocked);
+ ret = pthread_sigmask(SIG_SETMASK, &sig_all_blocked, &orig_parent_mask);
+ if (ret) {
+ ERR("pthread_sigmask: %s", strerror(ret));
+ }
+
+ ret = pthread_attr_init(&thread_attr);
+ if (ret) {
+ ERR("pthread_attr_init: %s", strerror(ret));
+ }
+ ret = pthread_attr_setdetachstate(&thread_attr, PTHREAD_CREATE_DETACHED);
+ if (ret) {
+ ERR("pthread_attr_setdetachstate: %s", strerror(ret));
+ }
+
+ ret = pthread_create(&global_apps.ust_listener, &thread_attr,
+ ust_listener_thread, &global_apps);
+ if (ret) {
+ ERR("pthread_create global: %s", strerror(ret));
+ }
if (local_apps.allowed) {
- ret = pthread_create(&global_apps.ust_listener, NULL,
- ust_listener_thread, &global_apps);
+ ret = pthread_create(&local_apps.ust_listener, &thread_attr,
+ ust_listener_thread, &local_apps);
+ if (ret) {
+ ERR("pthread_create local: %s", strerror(ret));
+ }
} else {
handle_register_done(&local_apps);
}
+ ret = pthread_attr_destroy(&thread_attr);
+ if (ret) {
+ ERR("pthread_attr_destroy: %s", strerror(ret));
+ }
+
+ /* Restore original signal mask in parent */
+ ret = pthread_sigmask(SIG_SETMASK, &orig_parent_mask, NULL);
+ if (ret) {
+ ERR("pthread_sigmask: %s", strerror(ret));
+ }
switch (timeout_mode) {
case 1: /* timeout wait */
/* cancel threads */
ret = pthread_cancel(global_apps.ust_listener);
if (ret) {
- ERR("Error cancelling global ust listener thread");
+ ERR("Error cancelling global ust listener thread: %s",
+ strerror(ret));
}
if (local_apps.allowed) {
ret = pthread_cancel(local_apps.ust_listener);
if (ret) {
- ERR("Error cancelling local ust listener thread");
+ ERR("Error cancelling local ust listener thread: %s",
+ strerror(ret));
}
}
/*
sigset_t all_sigs;
int ret;
+ if (lttng_ust_nest_count)
+ return;
/* Disable signals */
sigfillset(&all_sigs);
ret = sigprocmask(SIG_BLOCK, &all_sigs, save_sigset);
void ust_after_fork_parent(sigset_t *restore_sigset)
{
+ if (lttng_ust_nest_count)
+ return;
DBG("process %d", getpid());
rcu_bp_after_fork_parent();
/* Release mutexes and reenable signals */
*/
void ust_after_fork_child(sigset_t *restore_sigset)
{
+ if (lttng_ust_nest_count)
+ return;
DBG("process %d", getpid());
/* Release urcu mutexes */
rcu_bp_after_fork_child();