X-Git-Url: http://git.lttng.org./?a=blobdiff_plain;ds=inline;f=src%2Flib%2Flttng-ust%2Flttng-ust-comm.c;h=9745c18246690b35e778cb19b5e593452cb5e141;hb=HEAD;hp=c84594ba8c69408ce316eb83b3a057a11a6e7cbd;hpb=4667b1924a9b01081722535013b8386c04700fe8;p=lttng-ust.git diff --git a/src/lib/lttng-ust/lttng-ust-comm.c b/src/lib/lttng-ust/lttng-ust-comm.c index c84594ba..c3325556 100644 --- a/src/lib/lttng-ust/lttng-ust-comm.c +++ b/src/lib/lttng-ust/lttng-ust-comm.c @@ -1,7 +1,7 @@ /* * SPDX-License-Identifier: LGPL-2.1-only * - * Copyright (C) 2011 David Goulet + * Copyright (C) 2011 EfficiOS Inc. * Copyright (C) 2011 Mathieu Desnoyers */ @@ -38,8 +38,9 @@ #include #include #include +#include #include -#include "common/compat/futex.h" +#include "lib/lttng-ust/futex.h" #include "common/ustcomm.h" #include "common/ust-fd.h" #include "common/logging.h" @@ -50,14 +51,13 @@ #include "common/procname.h" #include "common/ringbuffer/rb-init.h" #include "lttng-ust-statedump.h" -#include "clock.h" -#include "lib/lttng-ust/getcpu.h" +#include "common/clock.h" #include "common/getenv.h" #include "lib/lttng-ust/events.h" #include "context-internal.h" #include "common/align.h" -#include "lttng-counter-client.h" -#include "lttng-rb-clients.h" +#include "common/counter-clients/clients.h" +#include "common/ringbuffer-clients/clients.h" /* * Has lttng ust comm constructor been called ? @@ -118,6 +118,28 @@ static int lttng_ust_comm_should_quit; */ 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. @@ -126,25 +148,21 @@ int lttng_ust_loaded __attribute__((weak)); int ust_lock(void) { sigset_t sig_all_blocked, orig_mask; - int ret, oldstate; + int ret; - ret = pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &oldstate); - if (ret) { - ERR("pthread_setcancelstate: %s", strerror(ret)); - } - if (oldstate != PTHREAD_CANCEL_ENABLE) { - ERR("pthread_setcancelstate: unexpected oldstate"); + if (lttng_ust_cancelstate_disable_push()) { + ERR("lttng_ust_cancelstate_disable_push"); } sigfillset(&sig_all_blocked); ret = pthread_sigmask(SIG_SETMASK, &sig_all_blocked, &orig_mask); if (ret) { - ERR("pthread_sigmask: %s", strerror(ret)); + ERR("pthread_sigmask: ret=%d", ret); } if (!URCU_TLS(ust_mutex_nest)++) pthread_mutex_lock(&ust_mutex); ret = pthread_sigmask(SIG_SETMASK, &orig_mask, NULL); if (ret) { - ERR("pthread_sigmask: %s", strerror(ret)); + ERR("pthread_sigmask: ret=%d", ret); } if (lttng_ust_comm_should_quit) { return -1; @@ -162,25 +180,21 @@ int ust_lock(void) void ust_lock_nocheck(void) { sigset_t sig_all_blocked, orig_mask; - int ret, oldstate; + int ret; - ret = pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &oldstate); - if (ret) { - ERR("pthread_setcancelstate: %s", strerror(ret)); - } - if (oldstate != PTHREAD_CANCEL_ENABLE) { - ERR("pthread_setcancelstate: unexpected oldstate"); + if (lttng_ust_cancelstate_disable_push()) { + ERR("lttng_ust_cancelstate_disable_push"); } sigfillset(&sig_all_blocked); ret = pthread_sigmask(SIG_SETMASK, &sig_all_blocked, &orig_mask); if (ret) { - ERR("pthread_sigmask: %s", strerror(ret)); + ERR("pthread_sigmask: ret=%d", ret); } if (!URCU_TLS(ust_mutex_nest)++) pthread_mutex_lock(&ust_mutex); ret = pthread_sigmask(SIG_SETMASK, &orig_mask, NULL); if (ret) { - ERR("pthread_sigmask: %s", strerror(ret)); + ERR("pthread_sigmask: ret=%d", ret); } } @@ -190,25 +204,21 @@ void ust_lock_nocheck(void) void ust_unlock(void) { sigset_t sig_all_blocked, orig_mask; - int ret, oldstate; + int ret; sigfillset(&sig_all_blocked); ret = pthread_sigmask(SIG_SETMASK, &sig_all_blocked, &orig_mask); if (ret) { - ERR("pthread_sigmask: %s", strerror(ret)); + ERR("pthread_sigmask: ret=%d", ret); } if (!--URCU_TLS(ust_mutex_nest)) pthread_mutex_unlock(&ust_mutex); ret = pthread_sigmask(SIG_SETMASK, &orig_mask, NULL); if (ret) { - ERR("pthread_sigmask: %s", strerror(ret)); + ERR("pthread_sigmask: ret=%d", ret); } - ret = pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, &oldstate); - if (ret) { - ERR("pthread_setcancelstate: %s", strerror(ret)); - } - if (oldstate != PTHREAD_CANCEL_DISABLE) { - ERR("pthread_setcancelstate: unexpected oldstate"); + if (lttng_ust_cancelstate_disable_pop()) { + ERR("lttng_ust_cancelstate_disable_pop"); } } @@ -224,10 +234,10 @@ void ust_unlock(void) */ 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; @@ -247,26 +257,53 @@ struct sock_info { 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; /* Keep procname for statedump */ - char procname[LTTNG_UST_ABI_PROCNAME_LEN]; + char procname[LTTNG_UST_CONTEXT_PROCNAME_LEN]; }; /* Socket from app (connect) to session daemon (listen) for communication */ -struct sock_info global_apps = { +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, @@ -277,6 +314,7 @@ struct sock_info global_apps = { .socket = -1, .notify_socket = -1, + .wait_shm_is_file = false, .wait_shm_path = "/" LTTNG_UST_WAIT_FILENAME, .statedump_pending = 0, @@ -284,11 +322,9 @@ struct sock_info global_apps = { .procname[0] = '\0' }; -/* TODO: allow global_apps_sock_path override */ - -struct sock_info local_apps = { +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 */ @@ -297,6 +333,8 @@ struct sock_info local_apps = { .socket = -1, .notify_socket = -1, + .wait_shm_is_file = false, + .statedump_pending = 0, .initial_statedump_done = 0, .procname[0] = '\0' @@ -350,6 +388,7 @@ static const char *cmd_name_mapping[] = { /* Counter commands */ [ LTTNG_UST_ABI_COUNTER_GLOBAL ] = "Create Counter Global", [ LTTNG_UST_ABI_COUNTER_CPU ] = "Create Counter CPU", + [ LTTNG_UST_ABI_COUNTER_EVENT ] = "Create Counter Event", }; static const char *str_timeout; @@ -374,48 +413,87 @@ const char *get_lttng_home_dir(void) } /* - * Force a read (imply TLS fixup for dlopen) of TLS variables. + * 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_fixup_nest_count_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_fixup_ust_mutex_nest_tls(void) +void lttng_ust_mutex_nest_alloc_tls(void) { - asm volatile ("" : : "m" (URCU_TLS(ust_mutex_nest))); + __asm__ __volatile__ ("" : : "m" (URCU_TLS(ust_mutex_nest))); } /* - * Fixup lttng-ust urcu TLS. + * Allocate lttng-ust urcu TLS. */ static -void lttng_fixup_lttng_ust_urcu_tls(void) +void lttng_ust_urcu_alloc_tls(void) { (void) lttng_ust_urcu_read_ongoing(); } -void lttng_ust_fixup_tls(void) +void lttng_ust_common_init_thread(int flags) { - lttng_fixup_lttng_ust_urcu_tls(); - lttng_fixup_ringbuffer_tls(); - lttng_fixup_vtid_tls(); - lttng_fixup_nest_count_tls(); - lttng_fixup_procname_tls(); - lttng_fixup_ust_mutex_nest_tls(); - lttng_ust_fixup_perf_counter_tls(); - lttng_ust_fixup_fd_tracker_tls(); - lttng_fixup_cgroup_ns_tls(); - lttng_fixup_ipc_ns_tls(); - lttng_fixup_net_ns_tls(); - lttng_fixup_time_ns_tls(); - lttng_fixup_uts_ns_tls(); - lttng_ust_fixup_ring_buffer_client_discard_tls(); - lttng_ust_fixup_ring_buffer_client_discard_rt_tls(); - lttng_ust_fixup_ring_buffer_client_overwrite_tls(); - lttng_ust_fixup_ring_buffer_client_overwrite_rt_tls(); + lttng_ust_urcu_alloc_tls(); + lttng_ringbuffer_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_init_thread(flags); + lttng_ust_common_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(); + lttng_ust_ring_buffer_client_overwrite_rt_alloc_tls(); } /* @@ -436,7 +514,9 @@ void lttng_ust_init_thread(void) * ensure those are initialized before a signal handler nesting over * this thread attempts to use them. */ - lttng_ust_fixup_tls(); + lttng_ust_common_init_thread(LTTNG_UST_INIT_THREAD_MASK); + + lttng_ust_urcu_register_thread(); } int lttng_get_notify_socket(void *owner) @@ -468,12 +548,72 @@ void print_cmd(int cmd, int handle) 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."); @@ -483,10 +623,11 @@ int setup_global_apps(void) } global_apps.allowed = 1; - lttng_pthread_getname_np(global_apps.procname, LTTNG_UST_ABI_PROCNAME_LEN); + lttng_pthread_getname_np(global_apps.procname, LTTNG_UST_CONTEXT_PROCNAME_LEN); error: return ret; } + static int setup_local_apps(void) { @@ -496,6 +637,13 @@ 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. @@ -529,7 +677,7 @@ int setup_local_apps(void) goto end; } - lttng_pthread_getname_np(local_apps.procname, LTTNG_UST_ABI_PROCNAME_LEN); + lttng_pthread_getname_np(local_apps.procname, LTTNG_UST_CONTEXT_PROCNAME_LEN); end: return ret; } @@ -621,7 +769,8 @@ void get_allow_blocking(void) } static -int register_to_sessiond(int socket, enum ustctl_socket_type type) +int register_to_sessiond(int socket, enum lttng_ust_ctl_socket_type type, + const char *procname) { return ustcomm_send_reg_msg(socket, type, @@ -630,7 +779,8 @@ int register_to_sessiond(int socket, enum ustctl_socket_type type) lttng_ust_rb_alignof(uint16_t) * CHAR_BIT, lttng_ust_rb_alignof(uint32_t) * CHAR_BIT, lttng_ust_rb_alignof(uint64_t) * CHAR_BIT, - lttng_ust_rb_alignof(unsigned long) * CHAR_BIT); + lttng_ust_rb_alignof(unsigned long) * CHAR_BIT, + procname); } static @@ -743,14 +893,53 @@ const char *bytecode_type_str(uint32_t cmd) { switch (cmd) { case LTTNG_UST_ABI_CAPTURE: - return "capture"; + return "capture bytecode"; case LTTNG_UST_ABI_FILTER: - return "filter"; + return "filter bytecode"; default: abort(); } } +enum handle_message_error { + MSG_OK = 0, + MSG_ERROR = 1, + MSG_SHUTDOWN = 2, +}; + +/* + * Return: + * < 0: error + * 0: OK, handle command. + * > 0: shutdown (no error). + */ +static +enum handle_message_error handle_error(struct sock_info *sock_info, ssize_t len, + ssize_t expected_len, const char *str, int *error_code) +{ + if (!len) { + /* orderly shutdown */ + *error_code = 0; + return MSG_SHUTDOWN; + } + if (len == expected_len) { + DBG("%s data received", str); + *error_code = 0; + return MSG_OK; + } + if (len < 0) { + DBG("Receive failed from lttng-sessiond with errno %d", (int) -len); + if (len == -ECONNRESET) { + ERR("%s remote end closed connection", sock_info->name); + } + *error_code = len; + return MSG_ERROR; + } + DBG("incorrect %s data message size: %zd", str, len); + *error_code = -EINVAL; + return MSG_ERROR; +} + static int handle_bytecode_recv(struct sock_info *sock_info, int sock, struct ustcomm_ust_msg *lum) @@ -783,14 +972,14 @@ int handle_bytecode_recv(struct sock_info *sock_info, } if (data_size > data_size_max) { - ERR("Bytecode %s data size is too large: %u bytes", + ERR("%s data size is too large: %u bytes", bytecode_type_str(lum->cmd), data_size); ret = -EINVAL; goto end; } if (reloc_offset > data_size) { - ERR("Bytecode %s reloc offset %u is not within data", + ERR("%s reloc offset %u is not within data", bytecode_type_str(lum->cmd), reloc_offset); ret = -EINVAL; goto end; @@ -809,34 +998,13 @@ int handle_bytecode_recv(struct sock_info *sock_info, bytecode->type = type; len = ustcomm_recv_unix_sock(sock, bytecode->bc.data, bytecode->bc.len); - switch (len) { - case 0: /* orderly shutdown */ - ret = 0; + switch (handle_error(sock_info, len, bytecode->bc.len, bytecode_type_str(lum->cmd), &ret)) { + case MSG_OK: + break; + case MSG_ERROR: /* Fallthrough */ + case MSG_SHUTDOWN: goto end; - default: - if (len == bytecode->bc.len) { - DBG("Bytecode %s data received", - bytecode_type_str(lum->cmd)); - break; - } else if (len < 0) { - DBG("Receive failed from lttng-sessiond with errno %d", - (int) -len); - if (len == -ECONNRESET) { - ERR("%s remote end closed connection", - sock_info->name); - ret = len; - goto end; - } - ret = len; - goto end; - } else { - DBG("Incorrect %s bytecode data message size: %zd", - bytecode_type_str(lum->cmd), len); - ret = -EINVAL; - goto end; - } } - ops = lttng_ust_abi_objd_ops(lum->handle); if (!ops) { ret = -ENOENT; @@ -855,18 +1023,60 @@ end: return ret; } +static +void prepare_cmd_reply(struct ustcomm_ust_reply *lur, uint32_t handle, uint32_t cmd, int ret) +{ + lur->handle = handle; + lur->cmd = cmd; + lur->ret_val = ret; + if (ret >= 0) { + lur->ret_code = LTTNG_UST_OK; + } else { + /* + * Use -LTTNG_UST_ERR as wildcard for UST internal + * error that are not caused by the transport, except if + * we already have a more precise error message to + * report. + */ + if (ret > -LTTNG_UST_ERR) { + /* Translate code to UST error. */ + switch (ret) { + case -EEXIST: + lur->ret_code = -LTTNG_UST_ERR_EXIST; + break; + case -EINVAL: + lur->ret_code = -LTTNG_UST_ERR_INVAL; + break; + case -ENOENT: + lur->ret_code = -LTTNG_UST_ERR_NOENT; + break; + case -EPERM: + lur->ret_code = -LTTNG_UST_ERR_PERM; + break; + case -ENOSYS: + lur->ret_code = -LTTNG_UST_ERR_NOSYS; + break; + default: + lur->ret_code = -LTTNG_UST_ERR; + break; + } + } else { + lur->ret_code = ret; + } + } +} + static int handle_message(struct sock_info *sock_info, int sock, struct ustcomm_ust_msg *lum) { int ret = 0; const struct lttng_ust_abi_objd_ops *ops; - struct ustcomm_ust_reply lur; + struct ustcomm_ust_reply lur = {}; union lttng_ust_abi_args args; char ctxstr[LTTNG_UST_ABI_SYM_NAME_LEN]; /* App context string. */ ssize_t len; - - memset(&lur, 0, sizeof(lur)); + void *var_len_cmd_data = NULL; if (ust_lock()) { ret = -LTTNG_UST_ERR_EXITING; @@ -879,6 +1089,53 @@ int handle_message(struct sock_info *sock_info, goto error; } + switch (lum->cmd) { + case LTTNG_UST_ABI_FILTER: + case LTTNG_UST_ABI_EXCLUSION: + case LTTNG_UST_ABI_CHANNEL: + case LTTNG_UST_ABI_STREAM: + case LTTNG_UST_ABI_CONTEXT: + /* + * Those commands send additional payload after struct + * ustcomm_ust_msg, which makes it pretty much impossible to + * deal with "unknown command" errors without leaving the + * communication pipe in a out-of-sync state. This is part of + * the ABI between liblttng-ust-ctl and liblttng-ust, and + * should be fixed on the next breaking + * LTTNG_UST_ABI_MAJOR_VERSION protocol bump by indicating the + * total command message length as part of a message header so + * that the protocol can recover from invalid command errors. + */ + break; + + case LTTNG_UST_ABI_CAPTURE: + case LTTNG_UST_ABI_COUNTER: + case LTTNG_UST_ABI_COUNTER_GLOBAL: + case LTTNG_UST_ABI_COUNTER_CPU: + case LTTNG_UST_ABI_COUNTER_EVENT: + case LTTNG_UST_ABI_EVENT_NOTIFIER_CREATE: + case LTTNG_UST_ABI_EVENT_NOTIFIER_GROUP_CREATE: + /* + * Those commands expect a reply to the struct ustcomm_ust_msg + * before sending additional payload. + */ + prepare_cmd_reply(&lur, lum->handle, lum->cmd, 0); + + ret = send_reply(sock, &lur); + if (ret < 0) { + DBG("error sending reply"); + goto error; + } + break; + + default: + /* + * Other commands either don't send additional payload, or are + * unknown. + */ + break; + } + switch (lum->cmd) { case LTTNG_UST_ABI_REGISTER_DONE: if (lum->handle == LTTNG_UST_ABI_ROOT_HANDLE) @@ -919,32 +1176,13 @@ int handle_message(struct sock_info *sock_info, node->excluder.count = count; len = ustcomm_recv_unix_sock(sock, node->excluder.names, count * LTTNG_UST_ABI_SYM_NAME_LEN); - switch (len) { - case 0: /* orderly shutdown */ - ret = 0; + switch (handle_error(sock_info, len, count * LTTNG_UST_ABI_SYM_NAME_LEN, "exclusion", &ret)) { + case MSG_OK: + break; + case MSG_ERROR: /* Fallthrough */ + case MSG_SHUTDOWN: free(node); goto error; - default: - if (len == count * LTTNG_UST_ABI_SYM_NAME_LEN) { - DBG("Exclusion data received"); - break; - } else if (len < 0) { - DBG("Receive failed from lttng-sessiond with errno %d", (int) -len); - if (len == -ECONNRESET) { - ERR("%s remote end closed connection", sock_info->name); - ret = len; - free(node); - goto error; - } - ret = len; - free(node); - goto error; - } else { - DBG("Incorrect exclusion data message size: %zd", len); - ret = -EINVAL; - free(node); - goto error; - } } if (ops->cmd) ret = ops->cmd(lum->handle, lum->cmd, @@ -961,33 +1199,14 @@ int handle_message(struct sock_info *sock_info, len = ustcomm_recv_event_notifier_notif_fd_from_sessiond(sock, &event_notifier_notif_fd); - switch (len) { - case 0: /* orderly shutdown */ - ret = 0; - goto error; - case 1: + switch (handle_error(sock_info, len, 1, "event notifier group", &ret)) { + case MSG_OK: break; - default: - if (len < 0) { - DBG("Receive failed from lttng-sessiond with errno %d", - (int) -len); - if (len == -ECONNRESET) { - ERR("%s remote end closed connection", - sock_info->name); - ret = len; - goto error; - } - ret = len; - goto error; - } else { - DBG("Incorrect event notifier fd message size: %zd", - len); - ret = -EINVAL; - goto error; - } + case MSG_ERROR: /* Fallthrough */ + case MSG_SHUTDOWN: + goto error; } - args.event_notifier_handle.event_notifier_notif_fd = - event_notifier_notif_fd; + args.event_notifier_handle.event_notifier_notif_fd = event_notifier_notif_fd; if (ops->cmd) ret = ops->cmd(lum->handle, lum->cmd, (unsigned long) &lum->u, @@ -1011,28 +1230,12 @@ int handle_message(struct sock_info *sock_info, len = ustcomm_recv_channel_from_sessiond(sock, &chan_data, lum->u.channel.len, &wakeup_fd); - switch (len) { - case 0: /* orderly shutdown */ - ret = 0; + switch (handle_error(sock_info, len, lum->u.channel.len, "channel", &ret)) { + case MSG_OK: + break; + case MSG_ERROR: /* Fallthrough */ + case MSG_SHUTDOWN: goto error; - default: - if (len == lum->u.channel.len) { - DBG("channel data received"); - break; - } else if (len < 0) { - DBG("Receive failed from lttng-sessiond with errno %d", (int) -len); - if (len == -ECONNRESET) { - ERR("%s remote end closed connection", sock_info->name); - ret = len; - goto error; - } - ret = len; - goto error; - } else { - DBG("incorrect channel data message size: %zd", len); - ret = -EINVAL; - goto error; - } } args.channel.chan_data = chan_data; args.channel.wakeup_fd = wakeup_fd; @@ -1111,28 +1314,12 @@ int handle_message(struct sock_info *sock_info, p = &ctxstr[strlen("$app.")]; recvlen = ctxlen - strlen("$app."); len = ustcomm_recv_unix_sock(sock, p, recvlen); - switch (len) { - case 0: /* orderly shutdown */ - ret = 0; + switch (handle_error(sock_info, len, recvlen, "app context", &ret)) { + case MSG_OK: + break; + case MSG_ERROR: /* Fallthrough */ + case MSG_SHUTDOWN: goto error; - default: - if (len == recvlen) { - DBG("app context data received"); - break; - } else if (len < 0) { - DBG("Receive failed from lttng-sessiond with errno %d", (int) -len); - if (len == -ECONNRESET) { - ERR("%s remote end closed connection", sock_info->name); - ret = len; - goto error; - } - ret = len; - goto error; - } else { - DBG("incorrect app context data message size: %zd", len); - ret = -EINVAL; - goto error; - } } /* Put : between provider and ctxname. */ p[lum->u.context.u.app_ctx.provider_name_len - 1] = ':'; @@ -1152,55 +1339,44 @@ int handle_message(struct sock_info *sock_info, break; case LTTNG_UST_ABI_COUNTER: { - void *counter_data; - - len = ustcomm_recv_counter_from_sessiond(sock, - &counter_data, lum->u.counter.len); - switch (len) { - case 0: /* orderly shutdown */ - ret = 0; + len = ustcomm_recv_var_len_cmd_from_sessiond(sock, + &var_len_cmd_data, lum->u.var_len_cmd.cmd_len); + switch (handle_error(sock_info, len, lum->u.var_len_cmd.cmd_len, "counter", &ret)) { + case MSG_OK: + break; + case MSG_ERROR: /* Fallthrough */ + case MSG_SHUTDOWN: goto error; - default: - if (len == lum->u.counter.len) { - DBG("counter data received"); - break; - } else if (len < 0) { - DBG("Receive failed from lttng-sessiond with errno %d", (int) -len); - if (len == -ECONNRESET) { - ERR("%s remote end closed connection", sock_info->name); - ret = len; - goto error; - } - ret = len; - goto error; - } else { - DBG("incorrect counter data message size: %zd", len); - ret = -EINVAL; - goto error; - } } - args.counter.counter_data = counter_data; + args.counter.len = lum->u.var_len_cmd.cmd_len; if (ops->cmd) ret = ops->cmd(lum->handle, lum->cmd, - (unsigned long) &lum->u, + (unsigned long) var_len_cmd_data, &args, sock_info); else ret = -ENOSYS; - free(args.counter.counter_data); break; } case LTTNG_UST_ABI_COUNTER_GLOBAL: { + len = ustcomm_recv_var_len_cmd_from_sessiond(sock, + &var_len_cmd_data, lum->u.var_len_cmd.cmd_len); + switch (handle_error(sock_info, len, lum->u.var_len_cmd.cmd_len, "counter global", &ret)) { + case MSG_OK: + break; + case MSG_ERROR: /* Fallthrough */ + case MSG_SHUTDOWN: + goto error; + } /* Receive shm_fd */ - ret = ustcomm_recv_counter_shm_from_sessiond(sock, - &args.counter_shm.shm_fd); + ret = ustcomm_recv_counter_shm_from_sessiond(sock, &args.counter_shm.shm_fd); if (ret) { goto error; } - + args.counter_shm.len = lum->u.var_len_cmd.cmd_len; if (ops->cmd) ret = ops->cmd(lum->handle, lum->cmd, - (unsigned long) &lum->u, + (unsigned long) var_len_cmd_data, &args, sock_info); else ret = -ENOSYS; @@ -1218,16 +1394,24 @@ int handle_message(struct sock_info *sock_info, } case LTTNG_UST_ABI_COUNTER_CPU: { + len = ustcomm_recv_var_len_cmd_from_sessiond(sock, + &var_len_cmd_data, lum->u.var_len_cmd.cmd_len); + switch (handle_error(sock_info, len, lum->u.var_len_cmd.cmd_len, "counter cpu", &ret)) { + case MSG_OK: + break; + case MSG_ERROR: /* Fallthrough */ + case MSG_SHUTDOWN: + goto error; + } /* Receive shm_fd */ - ret = ustcomm_recv_counter_shm_from_sessiond(sock, - &args.counter_shm.shm_fd); + ret = ustcomm_recv_counter_shm_from_sessiond(sock, &args.counter_shm.shm_fd); if (ret) { goto error; } - + args.counter_shm.len = lum->u.var_len_cmd.cmd_len; if (ops->cmd) ret = ops->cmd(lum->handle, lum->cmd, - (unsigned long) &lum->u, + (unsigned long) var_len_cmd_data, &args, sock_info); else ret = -ENOSYS; @@ -1243,43 +1427,41 @@ int handle_message(struct sock_info *sock_info, } break; } - case LTTNG_UST_ABI_EVENT_NOTIFIER_CREATE: + case LTTNG_UST_ABI_COUNTER_EVENT: { - /* Receive struct lttng_ust_event_notifier */ - struct lttng_ust_abi_event_notifier event_notifier; - - if (sizeof(event_notifier) != lum->u.event_notifier.len) { - DBG("incorrect event notifier data message size: %u", lum->u.event_notifier.len); - ret = -EINVAL; + len = ustcomm_recv_var_len_cmd_from_sessiond(sock, + &var_len_cmd_data, lum->u.var_len_cmd.cmd_len); + switch (handle_error(sock_info, len, lum->u.var_len_cmd.cmd_len, "counter event", &ret)) { + case MSG_OK: + break; + case MSG_ERROR: /* Fallthrough */ + case MSG_SHUTDOWN: goto error; } - len = ustcomm_recv_unix_sock(sock, &event_notifier, sizeof(event_notifier)); - switch (len) { - case 0: /* orderly shutdown */ - ret = 0; + args.counter_event.len = lum->u.var_len_cmd.cmd_len; + if (ops->cmd) + ret = ops->cmd(lum->handle, lum->cmd, + (unsigned long) var_len_cmd_data, + &args, sock_info); + else + ret = -ENOSYS; + break; + } + case LTTNG_UST_ABI_EVENT_NOTIFIER_CREATE: + { + len = ustcomm_recv_var_len_cmd_from_sessiond(sock, + &var_len_cmd_data, lum->u.var_len_cmd.cmd_len); + switch (handle_error(sock_info, len, lum->u.var_len_cmd.cmd_len, "event notifier", &ret)) { + case MSG_OK: + break; + case MSG_ERROR: /* Fallthrough */ + case MSG_SHUTDOWN: goto error; - default: - if (len == sizeof(event_notifier)) { - DBG("event notifier data received"); - break; - } else if (len < 0) { - DBG("Receive failed from lttng-sessiond with errno %d", (int) -len); - if (len == -ECONNRESET) { - ERR("%s remote end closed connection", sock_info->name); - ret = len; - goto error; - } - ret = len; - goto error; - } else { - DBG("incorrect event notifier data message size: %zd", len); - ret = -EINVAL; - goto error; - } } + args.event_notifier.len = lum->u.var_len_cmd.cmd_len; if (ops->cmd) ret = ops->cmd(lum->handle, lum->cmd, - (unsigned long) &event_notifier, + (unsigned long) var_len_cmd_data, &args, sock_info); else ret = -ENOSYS; @@ -1296,44 +1478,8 @@ int handle_message(struct sock_info *sock_info, break; } - lur.handle = lum->handle; - lur.cmd = lum->cmd; - lur.ret_val = ret; - if (ret >= 0) { - lur.ret_code = LTTNG_UST_OK; - } else { - /* - * Use -LTTNG_UST_ERR as wildcard for UST internal - * error that are not caused by the transport, except if - * we already have a more precise error message to - * report. - */ - if (ret > -LTTNG_UST_ERR) { - /* Translate code to UST error. */ - switch (ret) { - case -EEXIST: - lur.ret_code = -LTTNG_UST_ERR_EXIST; - break; - case -EINVAL: - lur.ret_code = -LTTNG_UST_ERR_INVAL; - break; - case -ENOENT: - lur.ret_code = -LTTNG_UST_ERR_NOENT; - break; - case -EPERM: - lur.ret_code = -LTTNG_UST_ERR_PERM; - break; - case -ENOSYS: - lur.ret_code = -LTTNG_UST_ERR_NOSYS; - break; - default: - lur.ret_code = -LTTNG_UST_ERR; - break; - } - } else { - lur.ret_code = ret; - } - } + prepare_cmd_reply(&lur, lum->handle, lum->cmd, ret); + if (ret >= 0) { switch (lum->cmd) { case LTTNG_UST_ABI_TRACER_VERSION: @@ -1390,6 +1536,7 @@ int handle_message(struct sock_info *sock_info, error: ust_unlock(); + free(var_len_cmd_data); return ret; } @@ -1405,8 +1552,7 @@ void cleanup_sock_info(struct sock_info *sock_info, int exiting) } sock_info->root_handle = -1; } - sock_info->registration_done = 0; - sock_info->initial_statedump_done = 0; + /* * wait_shm_mmap, socket and notify socket are used by listener @@ -1418,6 +1564,9 @@ void cleanup_sock_info(struct sock_info *sock_info, int exiting) if (exiting) return; + sock_info->registration_done = 0; + sock_info->initial_statedump_done = 0; + if (sock_info->socket != -1) { ret = ustcomm_close_unix_sock(sock_info->socket); if (ret) { @@ -1451,6 +1600,15 @@ void cleanup_sock_info(struct sock_info *sock_info, int exiting) } } +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 @@ -1469,7 +1627,7 @@ int get_wait_shm(struct sock_info *sock_info, size_t mmap_size) /* * 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; @@ -1515,21 +1673,21 @@ open_write: 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 @@ -1544,7 +1702,7 @@ open_write: /* 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 @@ -1556,7 +1714,7 @@ open_write: * 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); @@ -1574,7 +1732,7 @@ open_write: * 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); } @@ -1587,7 +1745,7 @@ open_write: return -1; } end: - if (wait_shm_fd >= 0 && !sock_info->global) { + if (wait_shm_fd >= 0 && !sock_info->multi_user) { struct stat statbuf; /* @@ -1690,18 +1848,25 @@ void wait_for_sessiond(struct sock_info *sock_info) 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( @@ -1741,7 +1906,7 @@ void *ust_listener_thread(void *arg) int sock, ret, prev_connect_failed = 0, has_waited = 0, fd; long timeout; - lttng_ust_fixup_tls(); + lttng_ust_common_init_thread(0); /* * If available, add '-ust' to the end of this thread's * process name @@ -1859,7 +2024,8 @@ restart: sock_info->root_handle = ret; } - ret = register_to_sessiond(sock_info->socket, USTCTL_SOCKET_CMD); + ret = register_to_sessiond(sock_info->socket, LTTNG_UST_CTL_SOCKET_CMD, + sock_info->procname); if (ret < 0) { ERR("Error registering to %s ust cmd socket", sock_info->name); @@ -1952,7 +2118,7 @@ restart: } ret = register_to_sessiond(sock_info->notify_socket, - USTCTL_SOCKET_NOTIFY); + LTTNG_UST_CTL_SOCKET_NOTIFY, sock_info->procname); if (ret < 0) { ERR("Error registering to %s ust notify socket", sock_info->name); @@ -2048,6 +2214,41 @@ void lttng_ust_libc_wrapper_malloc_ctor(void) { } +/* + * Use a symbol of the previous ABI to detect if liblttng-ust.so.0 is loaded in + * the current process. + */ +#define LTTNG_UST_SONAME_0_SYM "ltt_probe_register" + +static +void lttng_ust_check_soname_0(void) +{ + if (!dlsym(RTLD_DEFAULT, LTTNG_UST_SONAME_0_SYM)) + return; + + CRIT("Incompatible library ABIs detected within the same process. " + "The process is likely linked against different major soname of LTTng-UST which is unsupported. " + "The detection was triggered by lookup of ABI 0 symbol \"%s\" in the Global Symbol Table\n", + LTTNG_UST_SONAME_0_SYM); +} + +/* + * Expose a canary symbol of the previous ABI to ensure we catch uses of a + * liblttng-ust.so.0 dlopen'd after .so.1 has been loaded. Use a different + * symbol than the detection code to ensure we don't detect ourself. + * + * This scheme will only work on systems where the global symbol table has + * priority when resolving the symbols of a dlopened shared object, which is + * the case on Linux but not on FreeBSD. + */ +void init_usterr(void); +void init_usterr(void) +{ + CRIT("Incompatible library ABIs detected within the same process. " + "The process is likely linked against different major soname of LTTng-UST which is unsupported. " + "The detection was triggered by canary symbol \"%s\"\n", __func__); +} + /* * sessiond monitoring thread: monitor presence of global and per-user * sessiond by polling the application common named pipe. @@ -2069,14 +2270,22 @@ void lttng_ust_ctor(void) return; /* - * Fixup interdependency between TLS fixup mutex (which happens + * Fixup interdependency between TLS allocation mutex (which happens * to be the dynamic linker mutex) and ust_lock, taken within * the ust lock. */ - lttng_ust_fixup_tls(); + lttng_ust_common_init_thread(0); lttng_ust_loaded = 1; + /* + * Check if we find a symbol of the previous ABI in the current process + * as different ABIs of liblttng-ust can't co-exist in a process. If we + * do so, emit a critical log message which will also abort if the + * LTTNG_UST_ABORT_ON_CRITICAL environment variable is set. + */ + lttng_ust_check_soname_0(); + /* * We need to ensure that the liblttng-ust library is not unloaded to avoid * the unloading of code used by the ust_listener_threads as we can not @@ -2085,7 +2294,7 @@ void lttng_ust_ctor(void) * this library so it never becomes zero, thus never gets unloaded from the * address space of the process. Since we are already running in the * constructor of the LTTNG_UST_LIB_SONAME library, calling dlopen will - * simply increment the refcount and no additionnal work is needed by the + * simply increment the refcount and no additional work is needed by the * dynamic loader as the shared library is already loaded in the address * space. As a safe guard, we use the RTLD_NODELETE flag to prevent * unloading of the UST library if its refcount becomes zero (which should @@ -2095,6 +2304,8 @@ void lttng_ust_ctor(void) handle = dlopen(LTTNG_UST_LIB_SONAME, RTLD_LAZY | RTLD_NODELETE); if (!handle) { ERR("dlopen of liblttng-ust shared library (%s).", LTTNG_UST_LIB_SONAME); + } else { + DBG("dlopened liblttng-ust shared library (%s).", LTTNG_UST_LIB_SONAME); } /* @@ -2118,8 +2329,6 @@ void lttng_ust_ctor(void) lttng_ust_common_ctor(); lttng_ust_tp_init(); - lttng_ust_clock_init(); - lttng_ust_getcpu_plugin_init(); lttng_ust_statedump_init(); lttng_ust_ring_buffer_clients_init(); lttng_ust_counter_clients_init(); @@ -2138,6 +2347,11 @@ void lttng_ust_ctor(void) 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); @@ -2170,6 +2384,19 @@ void lttng_ust_ctor(void) 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, @@ -2249,8 +2476,10 @@ void lttng_ust_ctor(void) 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; /* @@ -2300,6 +2529,15 @@ void lttng_ust_exit(void) 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) { @@ -2378,8 +2616,8 @@ void lttng_ust_before_fork(sigset_t *save_sigset) sigset_t all_sigs; int ret; - /* Fixup lttng-ust TLS. */ - lttng_ust_fixup_tls(); + /* Allocate lttng-ust TLS. */ + lttng_ust_common_init_thread(0); if (URCU_TLS(lttng_ust_nest_count)) return; @@ -2422,7 +2660,7 @@ void lttng_ust_after_fork_parent(sigset_t *restore_sigset) return; DBG("process %d", getpid()); lttng_ust_urcu_after_fork_parent(); - /* Release mutexes and reenable signals */ + /* Release mutexes and re-enable signals */ ust_after_fork_common(restore_sigset); } @@ -2449,7 +2687,7 @@ void lttng_ust_after_fork_child(sigset_t *restore_sigset) /* Release urcu mutexes */ lttng_ust_urcu_after_fork_child(); lttng_ust_cleanup(0); - /* Release mutexes and reenable signals */ + /* Release mutexes and re-enable signals */ ust_after_fork_common(restore_sigset); lttng_ust_ctor(); }