*/
int lttng_ust_loaded __attribute__((weak));
+/*
+ * Notes on async-signal-safety of ust lock: a few libc functions are used
+ * which are not strictly async-signal-safe:
+ *
+ * - pthread_setcancelstate
+ * - pthread_mutex_lock
+ * - pthread_mutex_unlock
+ *
+ * As of glibc 2.35, the implementation of pthread_setcancelstate only
+ * touches TLS data, and it appears to be safe to use from signal
+ * handlers. If the libc implementation changes, this will need to be
+ * revisited, and we may ask glibc to provide an async-signal-safe
+ * pthread_setcancelstate.
+ *
+ * As of glibc 2.35, the implementation of pthread_mutex_lock/unlock
+ * for fast mutexes only relies on the pthread_mutex_t structure.
+ * Disabling signals around all uses of this mutex ensures
+ * signal-safety. If the libc implementation changes and eventually uses
+ * other global resources, this will need to be revisited and we may
+ * need to implement our own mutex.
+ */
+
/*
* Return 0 on success, -1 if should quit.
* The lock is taken in both cases.
*/
static sem_t constructor_wait;
/*
- * Doing this for both the global and local sessiond.
+ * Doing this for the ust_app, global and local sessiond.
*/
enum {
- sem_count_initial_value = 4,
+ sem_count_initial_value = 6,
};
static int sem_count = sem_count_initial_value;
int root_handle;
int registration_done;
int allowed;
- int global;
+ bool multi_user;
int thread_active;
char sock_path[PATH_MAX];
int socket;
int notify_socket;
+ /*
+ * If wait_shm_is_file is true, use standard open to open and
+ * create the shared memory used for waiting on session daemon.
+ * Otherwise, use shm_open to create this file.
+ */
+ bool wait_shm_is_file;
char wait_shm_path[PATH_MAX];
char *wait_shm_mmap;
+
/* Keep track of lazy state dump not performed yet. */
int statedump_pending;
int initial_statedump_done;
};
/* Socket from app (connect) to session daemon (listen) for communication */
+static struct sock_info ust_app = {
+ .name = "ust_app",
+ .multi_user = true,
+
+ .root_handle = -1,
+ .registration_done = 0,
+ .allowed = 0,
+ .thread_active = 0,
+
+ .socket = -1,
+ .notify_socket = -1,
+
+ .wait_shm_is_file = true,
+
+ .statedump_pending = 0,
+ .initial_statedump_done = 0,
+ .procname[0] = '\0'
+};
+
+
static struct sock_info global_apps = {
.name = "global",
- .global = 1,
+ .multi_user = true,
.root_handle = -1,
.registration_done = 0,
.socket = -1,
.notify_socket = -1,
+ .wait_shm_is_file = false,
.wait_shm_path = "/" LTTNG_UST_WAIT_FILENAME,
.statedump_pending = 0,
.procname[0] = '\0'
};
-/* TODO: allow global_apps_sock_path override */
-
static struct sock_info local_apps = {
.name = "local",
- .global = 0,
+ .multi_user = false,
.root_handle = -1,
.registration_done = 0,
.allowed = 0, /* Check setuid bit first */
.socket = -1,
.notify_socket = -1,
+ .wait_shm_is_file = false,
+
.statedump_pending = 0,
.initial_statedump_done = 0,
.procname[0] = '\0'
return (const char *) lttng_ust_getenv("HOME");
}
+/*
+ * Returns the LTTNG_UST_APP_PATH path. If environment variable exists
+ * and contains a ':', the first path before the ':' separator is returned.
+ * The return value should be freed by the caller if it is not NULL.
+ */
+static
+char *get_lttng_ust_app_path(void)
+{
+ const char *env_val = lttng_ust_getenv("LTTNG_UST_APP_PATH");
+ char *val = NULL;
+ char *sep = NULL;
+ if (env_val == NULL)
+ goto error;
+ sep = strchr((char*)env_val, ':');
+ if (sep) {
+ /*
+ * Split into multiple paths using ':' as a separator.
+ * There is no escaping of the ':' separator.
+ */
+ WARN("':' separator in LTTNG_UST_APP_PATH, only the first path will be used.");
+ val = zmalloc(sep - env_val + 1);
+ if (!val) {
+ PERROR("zmalloc get_lttng_ust_app_path");
+ goto error;
+ }
+ memcpy(val, env_val, sep - env_val);
+ val[sep - env_val] = '\0';
+ } else {
+ val = strdup(env_val);
+ if (!val) {
+ PERROR("strdup");
+ goto error;
+ }
+ }
+
+error:
+ return val;
+}
+
/*
* Force a read (imply TLS allocation for dlopen) of TLS variables.
*/
static
-void lttng_nest_count_alloc_tls(void)
+void lttng_ust_nest_count_alloc_tls(void)
{
- asm volatile ("" : : "m" (URCU_TLS(lttng_ust_nest_count)));
+ __asm__ __volatile__ ("" : : "m" (URCU_TLS(lttng_ust_nest_count)));
}
static
void lttng_ust_mutex_nest_alloc_tls(void)
{
- asm volatile ("" : : "m" (URCU_TLS(ust_mutex_nest)));
+ __asm__ __volatile__ ("" : : "m" (URCU_TLS(ust_mutex_nest)));
}
/*
* Allocate lttng-ust urcu TLS.
*/
static
-void lttng_lttng_ust_urcu_alloc_tls(void)
+void lttng_ust_urcu_alloc_tls(void)
{
(void) lttng_ust_urcu_read_ongoing();
}
-void lttng_ust_alloc_tls(void)
+void lttng_ust_common_init_thread(int flags)
{
- lttng_lttng_ust_urcu_alloc_tls();
+ lttng_ust_urcu_alloc_tls();
lttng_ringbuffer_alloc_tls();
- lttng_vtid_alloc_tls();
- lttng_nest_count_alloc_tls();
- lttng_procname_alloc_tls();
+ lttng_ust_vtid_init_thread(flags);
+ lttng_ust_nest_count_alloc_tls();
+ lttng_ust_procname_init_thread(flags);
lttng_ust_mutex_nest_alloc_tls();
- lttng_ust_perf_counter_alloc_tls();
+ lttng_ust_perf_counter_init_thread(flags);
lttng_ust_common_alloc_tls();
- lttng_cgroup_ns_alloc_tls();
- lttng_ipc_ns_alloc_tls();
- lttng_net_ns_alloc_tls();
- lttng_time_ns_alloc_tls();
- lttng_uts_ns_alloc_tls();
+ lttng_ust_cgroup_ns_init_thread(flags);
+ lttng_ust_ipc_ns_init_thread(flags);
+ lttng_ust_net_ns_init_thread(flags);
+ lttng_ust_time_ns_init_thread(flags);
+ lttng_ust_uts_ns_init_thread(flags);
lttng_ust_ring_buffer_client_discard_alloc_tls();
lttng_ust_ring_buffer_client_discard_rt_alloc_tls();
lttng_ust_ring_buffer_client_overwrite_alloc_tls();
* ensure those are initialized before a signal handler nesting over
* this thread attempts to use them.
*/
- lttng_ust_alloc_tls();
+ lttng_ust_common_init_thread(LTTNG_UST_INIT_THREAD_MASK);
+
+ lttng_ust_urcu_register_thread();
}
int lttng_get_notify_socket(void *owner)
lttng_ust_obj_get_name(handle), handle);
}
+static
+int setup_ust_apps(void)
+{
+ char *ust_app_path = NULL;
+ int ret = 0;
+ uid_t uid;
+
+ assert(!ust_app.wait_shm_mmap);
+
+ uid = getuid();
+ /*
+ * Disallow ust apps tracing for setuid binaries, because we
+ * cannot use the environment variables anyway.
+ */
+ if (uid != geteuid()) {
+ DBG("UST app tracing disabled for setuid binary.");
+ assert(ust_app.allowed == 0);
+ ret = 0;
+ goto end;
+ }
+ ust_app_path = get_lttng_ust_app_path();
+ if (!ust_app_path) {
+ DBG("LTTNG_UST_APP_PATH environment variable not set.");
+ assert(ust_app.allowed == 0);
+ ret = -ENOENT;
+ goto end;
+ }
+ /*
+ * The LTTNG_UST_APP_PATH env. var. disables global and local
+ * sessiond connections.
+ */
+ ust_app.allowed = 1;
+ snprintf(ust_app.sock_path, PATH_MAX, "%s/%s",
+ ust_app_path, LTTNG_UST_SOCK_FILENAME);
+ snprintf(ust_app.wait_shm_path, PATH_MAX, "%s/%s",
+ ust_app_path,
+ LTTNG_UST_WAIT_FILENAME);
+
+ ust_app.wait_shm_mmap = get_map_shm(&ust_app);
+ if (!ust_app.wait_shm_mmap) {
+ WARN("Unable to get map shm for ust_app. Disabling LTTng-UST ust_app tracing.");
+ ust_app.allowed = 0;
+ ret = -EIO;
+ goto end;
+ }
+
+ lttng_pthread_getname_np(ust_app.procname, LTTNG_UST_CONTEXT_PROCNAME_LEN);
+end:
+ if (ust_app_path)
+ free(ust_app_path);
+ return ret;
+}
+
static
int setup_global_apps(void)
{
int ret = 0;
assert(!global_apps.wait_shm_mmap);
+ /*
+ * The LTTNG_UST_APP_PATH env. var. disables global sessiond
+ * connections.
+ */
+ if (ust_app.allowed)
+ return 0;
+
global_apps.wait_shm_mmap = get_map_shm(&global_apps);
if (!global_apps.wait_shm_mmap) {
WARN("Unable to get map shm for global apps. Disabling LTTng-UST global tracing.");
error:
return ret;
}
+
static
int setup_local_apps(void)
{
assert(!local_apps.wait_shm_mmap);
+ /*
+ * The LTTNG_UST_APP_PATH env. var. disables local sessiond
+ * connections.
+ */
+ if (ust_app.allowed)
+ return 0;
+
uid = getuid();
/*
* Disallow per-user tracing for setuid binaries.
}
}
+static
+int wait_shm_open(struct sock_info *sock_info, int flags, mode_t mode)
+{
+ if (sock_info->wait_shm_is_file)
+ return open(sock_info->wait_shm_path, flags, mode);
+ else
+ return shm_open(sock_info->wait_shm_path, flags, mode);
+}
+
/*
* Using fork to set umask in the child process (not multi-thread safe).
* We deal with the shm_open vs ftruncate race (happening when the
/*
* Try to open read-only.
*/
- wait_shm_fd = shm_open(sock_info->wait_shm_path, O_RDONLY, 0);
+ wait_shm_fd = wait_shm_open(sock_info, O_RDONLY, 0);
if (wait_shm_fd >= 0) {
int32_t tmp_read;
ssize_t len;
pid = fork();
URCU_TLS(lttng_ust_nest_count)--;
if (pid > 0) {
- int status;
+ int status, wait_ret;
/*
* Parent: wait for child to return, in which case the
* shared memory map will have been created.
*/
- pid = wait(&status);
- if (pid < 0 || !WIFEXITED(status) || WEXITSTATUS(status) != 0) {
+ wait_ret = waitpid(pid, &status, 0);
+ if (wait_ret < 0 || !WIFEXITED(status) || WEXITSTATUS(status) != 0) {
wait_shm_fd = -1;
goto end;
}
/*
* Try to open read-only again after creation.
*/
- wait_shm_fd = shm_open(sock_info->wait_shm_path, O_RDONLY, 0);
+ wait_shm_fd = wait_shm_open(sock_info, O_RDONLY, 0);
if (wait_shm_fd < 0) {
/*
* Real-only open did not work. It's a failure
/* Child */
create_mode = S_IRUSR | S_IWUSR | S_IRGRP;
- if (sock_info->global)
+ if (sock_info->multi_user)
create_mode |= S_IROTH | S_IWGRP | S_IWOTH;
/*
* We're alone in a child process, so we can modify the
* We don't do an exclusive open, because we allow other
* processes to create+ftruncate it concurrently.
*/
- wait_shm_fd = shm_open(sock_info->wait_shm_path,
+ wait_shm_fd = wait_shm_open(sock_info,
O_RDWR | O_CREAT, create_mode);
if (wait_shm_fd >= 0) {
ret = ftruncate(wait_shm_fd, mmap_size);
* sessiond will be able to override all rights and wake
* us up.
*/
- if (!sock_info->global && errno != EACCES) {
+ if (!sock_info->multi_user && errno != EACCES) {
ERR("Error opening shm %s", sock_info->wait_shm_path);
_exit(EXIT_FAILURE);
}
return -1;
}
end:
- if (wait_shm_fd >= 0 && !sock_info->global) {
+ if (wait_shm_fd >= 0 && !sock_info->multi_user) {
struct stat statbuf;
/*
DBG("Waiting for %s apps sessiond", sock_info->name);
/* Wait for futex wakeup */
- if (uatomic_read((int32_t *) sock_info->wait_shm_mmap))
- goto end_wait;
-
- while (lttng_ust_futex_async((int32_t *) sock_info->wait_shm_mmap,
- FUTEX_WAIT, 0, NULL, NULL, 0)) {
+ while (!uatomic_read((int32_t *) sock_info->wait_shm_mmap)) {
+ if (!lttng_ust_futex_async((int32_t *) sock_info->wait_shm_mmap, FUTEX_WAIT, 0, NULL, NULL, 0)) {
+ /*
+ * Prior queued wakeups queued by unrelated code
+ * using the same address can cause futex wait to
+ * return 0 even through the futex value is still
+ * 0 (spurious wakeups). Check the value again
+ * in user-space to validate whether it really
+ * differs from 0.
+ */
+ continue;
+ }
switch (errno) {
- case EWOULDBLOCK:
+ case EAGAIN:
/* Value already changed. */
goto end_wait;
case EINTR:
/* Retry if interrupted by signal. */
- break; /* Get out of switch. */
+ break; /* Get out of switch. Check again. */
case EFAULT:
wait_poll_fallback = 1;
DBG(
int sock, ret, prev_connect_failed = 0, has_waited = 0, fd;
long timeout;
- lttng_ust_alloc_tls();
+ lttng_ust_common_init_thread(0);
/*
* If available, add '-ust' to the end of this thread's
* process name
* to be the dynamic linker mutex) and ust_lock, taken within
* the ust lock.
*/
- lttng_ust_alloc_tls();
+ lttng_ust_common_init_thread(0);
lttng_ust_loaded = 1;
PERROR("sem_init");
}
+ ret = setup_ust_apps();
+ if (ret) {
+ assert(ust_app.allowed == 0);
+ DBG("ust_app setup returned %d", ret);
+ }
ret = setup_global_apps();
if (ret) {
assert(global_apps.allowed == 0);
ERR("pthread_attr_setdetachstate: %s", strerror(ret));
}
+ if (ust_app.allowed) {
+ pthread_mutex_lock(&ust_exit_mutex);
+ ret = pthread_create(&ust_app.ust_listener, &thread_attr,
+ ust_listener_thread, &ust_app);
+ if (ret) {
+ ERR("pthread_create ust_app: %s", strerror(ret));
+ }
+ ust_app.thread_active = 1;
+ pthread_mutex_unlock(&ust_exit_mutex);
+ } else {
+ handle_register_done(&ust_app);
+ }
+
if (global_apps.allowed) {
pthread_mutex_lock(&ust_exit_mutex);
ret = pthread_create(&global_apps.ust_listener, &thread_attr,
static
void lttng_ust_cleanup(int exiting)
{
+ cleanup_sock_info(&ust_app, exiting);
cleanup_sock_info(&global_apps, exiting);
cleanup_sock_info(&local_apps, exiting);
+ ust_app.allowed = 0;
local_apps.allowed = 0;
global_apps.allowed = 0;
/*
pthread_mutex_lock(&ust_exit_mutex);
/* cancel threads */
+ if (ust_app.thread_active) {
+ ret = pthread_cancel(ust_app.ust_listener);
+ if (ret) {
+ ERR("Error cancelling ust listener thread: %s",
+ strerror(ret));
+ } else {
+ ust_app.thread_active = 0;
+ }
+ }
if (global_apps.thread_active) {
ret = pthread_cancel(global_apps.ust_listener);
if (ret) {
int ret;
/* Allocate lttng-ust TLS. */
- lttng_ust_alloc_tls();
+ lttng_ust_common_init_thread(0);
if (URCU_TLS(lttng_ust_nest_count))
return;