X-Git-Url: http://git.efficios.com/?p=lttng-tools.git;a=blobdiff_plain;f=src%2Fbin%2Flttng-sessiond%2Fmain.c;h=4749f8621b62d1b0512304dcbac6677fd1e79124;hp=3d2cad9361464534128a44f5917d01f1e58899fe;hb=c8a9de5a85fb150d3ceaa5ca1a8b1b2b91d050d5;hpb=8b0d46366f5c87043d22aa29b9f26e5249bf7c3a diff --git a/src/bin/lttng-sessiond/main.c b/src/bin/lttng-sessiond/main.c index 3d2cad936..4749f8621 100644 --- a/src/bin/lttng-sessiond/main.c +++ b/src/bin/lttng-sessiond/main.c @@ -49,6 +49,9 @@ #include #include #include +#include +#include +#include #include "lttng-sessiond.h" #include "buffer-registry.h" @@ -78,7 +81,8 @@ #include "agent.h" #include "ht-cleanup.h" #include "sessiond-config.h" -#include "sessiond-timer.h" +#include "timer.h" +#include "thread.h" static const char *help_msg = #ifdef LTTNG_EMBED_HELP @@ -89,8 +93,6 @@ NULL ; const char *progname; -static pid_t ppid; /* Parent PID for --sig-parent option */ -static pid_t child_ppid; /* Internal parent PID use with daemonize. */ static int lockfile_fd = -1; /* Set to 1 when a SIGUSR1 signal is received. */ @@ -108,7 +110,6 @@ static struct consumer_data kconsumer_data = { .err_sock = -1, .cmd_sock = -1, .channel_monitor_pipe = -1, - .channel_rotate_pipe = -1, .pid_mutex = PTHREAD_MUTEX_INITIALIZER, .lock = PTHREAD_MUTEX_INITIALIZER, .cond = PTHREAD_COND_INITIALIZER, @@ -119,7 +120,6 @@ static struct consumer_data ustconsumer64_data = { .err_sock = -1, .cmd_sock = -1, .channel_monitor_pipe = -1, - .channel_rotate_pipe = -1, .pid_mutex = PTHREAD_MUTEX_INITIALIZER, .lock = PTHREAD_MUTEX_INITIALIZER, .cond = PTHREAD_COND_INITIALIZER, @@ -130,7 +130,6 @@ static struct consumer_data ustconsumer32_data = { .err_sock = -1, .cmd_sock = -1, .channel_monitor_pipe = -1, - .channel_rotate_pipe = -1, .pid_mutex = PTHREAD_MUTEX_INITIALIZER, .lock = PTHREAD_MUTEX_INITIALIZER, .cond = PTHREAD_COND_INITIALIZER, @@ -170,8 +169,6 @@ static const struct option long_options[] = { { NULL, 0, 0, 0 } }; -struct sessiond_config config; - /* Command line options to ignore from configuration file */ static const char *config_ignore_options[] = { "help", "version", "config" }; @@ -181,14 +178,6 @@ static int dispatch_thread_exit; /* Sockets and FDs */ static int client_sock = -1; static int apps_sock = -1; -int kernel_tracer_fd = -1; -static int kernel_poll_pipe[2] = { -1, -1 }; - -/* - * Quit pipe for all threads. This permits a single cancellation point - * for all threads when receiving an event on the pipe. - */ -static int thread_quit_pipe[2] = { -1, -1 }; /* * This pipe is used to inform the thread managing application communication @@ -196,8 +185,6 @@ static int thread_quit_pipe[2] = { -1, -1 }; */ static int apps_cmd_pipe[2] = { -1, -1 }; -int apps_cmd_notify_pipe[2] = { -1, -1 }; - /* Pthread, Mutexes and Semaphores */ static pthread_t apps_thread; static pthread_t apps_notify_thread; @@ -205,11 +192,8 @@ static pthread_t reg_apps_thread; static pthread_t client_thread; static pthread_t kernel_thread; static pthread_t dispatch_thread; -static pthread_t health_thread; -static pthread_t ht_cleanup_thread; static pthread_t agent_reg_thread; static pthread_t load_session_thread; -static pthread_t notification_thread; static pthread_t rotation_thread; static pthread_t timer_thread; @@ -224,21 +208,6 @@ static pthread_t timer_thread; */ static struct ust_cmd_queue ust_cmd_queue; -/* - * Pointer initialized before thread creation. - * - * This points to the tracing session list containing the session count and a - * mutex lock. The lock MUST be taken if you iterate over the list. The lock - * MUST NOT be taken if you call a public function in session.c. - * - * The lock is nested inside the structure: session_list_ptr->lock. Please use - * session_lock_list and session_unlock_list for lock acquisition. - */ -static struct ltt_session_list *session_list_ptr; - -int ust_consumerd64_fd = -1; -int ust_consumerd32_fd = -1; - static const char *module_proc_lttng = "/proc/lttng"; /* @@ -275,178 +244,19 @@ enum consumerd_state { static enum consumerd_state ust_consumerd_state; static enum consumerd_state kernel_consumerd_state; -/* Set in main() with the current page size. */ -long page_size; - -/* Application health monitoring */ -struct health_app *health_sessiond; - -/* Am I root or not. */ -int is_root; /* Set to 1 if the daemon is running as root */ - -const char * const config_section_name = "sessiond"; - /* Load session thread information to operate. */ -struct load_session_thread_data *load_info; - -/* Notification thread handle. */ -struct notification_thread_handle *notification_thread_handle; - -/* Rotation thread handle. */ -struct rotation_thread_handle *rotation_thread_handle; - -/* Global hash tables */ -struct lttng_ht *agent_apps_ht_by_sock = NULL; - -/* - * The initialization of the session daemon is done in multiple phases. - * - * While all threads are launched near-simultaneously, only some of them - * are needed to ensure the session daemon can start to respond to client - * requests. - * - * There are two important guarantees that we wish to offer with respect - * to the initialisation of the session daemon: - * - When the daemonize/background launcher process exits, the sessiond - * is fully able to respond to client requests, - * - Auto-loaded sessions are visible to clients. - * - * In order to achieve this, a number of support threads have to be launched - * to allow the "client" thread to function properly. Moreover, since the - * "load session" thread needs the client thread, we must provide a way - * for the "load session" thread to know that the "client" thread is up - * and running. - * - * Hence, the support threads decrement the lttng_sessiond_ready counter - * while the "client" threads waits for it to reach 0. Once the "client" thread - * unblocks, it posts the message_thread_ready semaphore which allows the - * "load session" thread to progress. - * - * This implies that the "load session" thread is the last to be initialized - * and will explicitly call sessiond_signal_parents(), which signals the parents - * that the session daemon is fully initialized. - * - * The four (4) support threads are: - * - agent_thread - * - notification_thread - * - rotation_thread - * - health_thread - */ -#define NR_LTTNG_SESSIOND_SUPPORT_THREADS 4 -int lttng_sessiond_ready = NR_LTTNG_SESSIOND_SUPPORT_THREADS; - -int sessiond_check_thread_quit_pipe(int fd, uint32_t events) -{ - return (fd == thread_quit_pipe[0] && (events & LPOLLIN)) ? 1 : 0; -} - -/* Notify parents that we are ready for cmd and health check */ -LTTNG_HIDDEN -void sessiond_signal_parents(void) -{ - /* - * Notify parent pid that we are ready to accept command - * for client side. This ppid is the one from the - * external process that spawned us. - */ - if (config.sig_parent) { - kill(ppid, SIGUSR1); - } - - /* - * Notify the parent of the fork() process that we are - * ready. - */ - if (config.daemonize || config.background) { - kill(child_ppid, SIGUSR1); - } -} - -LTTNG_HIDDEN -void sessiond_notify_ready(void) -{ - /* - * This memory barrier is paired with the one performed by - * the client thread after it has seen that 'lttng_sessiond_ready' is 0. - * - * The purpose of these memory barriers is to ensure that all - * initialization operations of the various threads that call this - * function to signal that they are ready are commited/published - * before the client thread can see the 'lttng_sessiond_ready' counter - * reach 0. - * - * Note that this could be a 'write' memory barrier, but a full barrier - * is used in case the code using this utility changes. The performance - * implications of this choice are minimal since this is a slow path. - */ - cmm_smp_mb(); - uatomic_sub(<tng_sessiond_ready, 1); -} - -static -int __sessiond_set_thread_pollset(struct lttng_poll_event *events, size_t size, - int *a_pipe) -{ - int ret; - - assert(events); - - ret = lttng_poll_create(events, size, LTTNG_CLOEXEC); - if (ret < 0) { - goto error; - } - - /* Add quit pipe */ - ret = lttng_poll_add(events, a_pipe[0], LPOLLIN | LPOLLERR); - if (ret < 0) { - goto error; - } - - return 0; - -error: - return ret; -} +static struct load_session_thread_data *load_info; /* - * Create a poll set with O_CLOEXEC and add the thread quit pipe to the set. - */ -int sessiond_set_thread_pollset(struct lttng_poll_event *events, size_t size) -{ - return __sessiond_set_thread_pollset(events, size, thread_quit_pipe); -} - -/* - * Init thread quit pipe. - * - * Return -1 on error or 0 if all pipes are created. + * Section name to look for in the daemon configuration file. */ -static int __init_thread_quit_pipe(int *a_pipe) -{ - int ret, i; +static const char * const config_section_name = "sessiond"; - ret = pipe(a_pipe); - if (ret < 0) { - PERROR("thread quit pipe"); - goto error; - } +/* Am I root or not. Set to 1 if the daemon is running as root */ +static int is_root; - for (i = 0; i < 2; i++) { - ret = fcntl(a_pipe[i], F_SETFD, FD_CLOEXEC); - if (ret < 0) { - PERROR("fcntl"); - goto error; - } - } - -error: - return ret; -} - -static int init_thread_quit_pipe(void) -{ - return __init_thread_quit_pipe(thread_quit_pipe); -} +/* Rotation thread handle. */ +static struct rotation_thread_handle *rotation_thread_handle; /* * Stop all threads by closing the thread quit pipe. @@ -457,7 +267,7 @@ static void stop_threads(void) /* Stopping all threads */ DBG("Terminating all threads"); - ret = notify_thread_pipe(thread_quit_pipe[1]); + ret = sessiond_notify_quit_pipe(); if (ret < 0) { ERR("write error on thread quit pipe"); } @@ -528,24 +338,6 @@ static void close_consumer_sockets(void) PERROR("UST consumerd64 channel monitor pipe close"); } } - if (kconsumer_data.channel_rotate_pipe >= 0) { - ret = close(kconsumer_data.channel_rotate_pipe); - if (ret < 0) { - PERROR("kernel consumer channel rotate pipe close"); - } - } - if (ustconsumer32_data.channel_rotate_pipe >= 0) { - ret = close(ustconsumer32_data.channel_rotate_pipe); - if (ret < 0) { - PERROR("UST consumerd32 channel rotate pipe close"); - } - } - if (ustconsumer64_data.channel_rotate_pipe >= 0) { - ret = close(ustconsumer64_data.channel_rotate_pipe); - if (ret < 0) { - PERROR("UST consumerd64 channel rotate pipe close"); - } - } } /* @@ -581,7 +373,7 @@ static void wait_consumer(struct consumer_data *consumer_data) static void sessiond_cleanup(void) { int ret; - struct ltt_session *sess, *stmp; + struct ltt_session_list *session_list = session_get_list(); DBG("Cleanup sessiond"); @@ -589,17 +381,11 @@ static void sessiond_cleanup(void) * Close the thread quit pipe. It has already done its job, * since we are now called. */ - utils_close_pipe(thread_quit_pipe); + sessiond_close_quit_pipe(); - /* - * If config.pid_file_path.value is undefined, the default file will be - * wiped when removing the rundir. - */ - if (config.pid_file_path.value) { - ret = remove(config.pid_file_path.value); - if (ret < 0) { - PERROR("remove pidfile %s", config.pid_file_path.value); - } + ret = remove(config.pid_file_path.value); + if (ret < 0) { + PERROR("remove pidfile %s", config.pid_file_path.value); } DBG("Removing sessiond and consumerd content of directory %s", @@ -633,19 +419,7 @@ static void sessiond_cleanup(void) DBG("Removing directory %s", config.consumerd64_path.value); (void) rmdir(config.consumerd64_path.value); - DBG("Cleaning up all sessions"); - - /* Destroy session list mutex */ - if (session_list_ptr != NULL) { - pthread_mutex_destroy(&session_list_ptr->lock); - - /* Cleanup ALL session */ - cds_list_for_each_entry_safe(sess, stmp, - &session_list_ptr->head, list) { - cmd_destroy_session(sess, kernel_poll_pipe[1], - notification_thread_handle); - } - } + pthread_mutex_destroy(&session_list->lock); wait_consumer(&kconsumer_data); wait_consumer(&ustconsumer64_data); @@ -818,16 +592,21 @@ static int setup_lttng_msg_no_cmd_header(struct command_ctx *cmd_ctx, static int update_kernel_poll(struct lttng_poll_event *events) { int ret; - struct ltt_session *session; struct ltt_kernel_channel *channel; + struct ltt_session *session; + const struct ltt_session_list *session_list = session_get_list(); DBG("Updating kernel poll set"); session_lock_list(); - cds_list_for_each_entry(session, &session_list_ptr->head, list) { + cds_list_for_each_entry(session, &session_list->head, list) { + if (!session_get(session)) { + continue; + } session_lock(session); if (session->kernel_session == NULL) { session_unlock(session); + session_put(session); continue; } @@ -837,6 +616,7 @@ static int update_kernel_poll(struct lttng_poll_event *events) ret = lttng_poll_add(events, channel->fd, LPOLLIN | LPOLLRDNORM); if (ret < 0) { session_unlock(session); + session_put(session); goto error; } DBG("Channel fd %d added to kernel set", channel->fd); @@ -858,20 +638,25 @@ error: * * Useful for CPU hotplug feature. */ -static int update_kernel_stream(struct consumer_data *consumer_data, int fd) +static int update_kernel_stream(int fd) { int ret = 0; struct ltt_session *session; struct ltt_kernel_session *ksess; struct ltt_kernel_channel *channel; + const struct ltt_session_list *session_list = session_get_list(); DBG("Updating kernel streams for channel fd %d", fd); session_lock_list(); - cds_list_for_each_entry(session, &session_list_ptr->head, list) { + cds_list_for_each_entry(session, &session_list->head, list) { + if (!session_get(session)) { + continue; + } session_lock(session); if (session->kernel_session == NULL) { session_unlock(session); + session_put(session); continue; } ksess = session->kernel_session; @@ -919,12 +704,14 @@ static int update_kernel_stream(struct consumer_data *consumer_data, int fd) rcu_read_unlock(); } session_unlock(session); + session_put(session); } session_unlock_list(); return ret; error: session_unlock(session); + session_put(session); session_unlock_list(); return ret; } @@ -936,6 +723,7 @@ error: static void update_ust_app(int app_sock) { struct ltt_session *sess, *stmp; + const struct ltt_session_list *session_list = session_get_list(); /* Consumer is in an ERROR state. Stop any application update. */ if (uatomic_read(&ust_consumerd_state) == CONSUMER_ERROR) { @@ -944,9 +732,12 @@ static void update_ust_app(int app_sock) } /* For all tracing session(s) */ - cds_list_for_each_entry_safe(sess, stmp, &session_list_ptr->head, list) { + cds_list_for_each_entry_safe(sess, stmp, &session_list->head, list) { struct ust_app *app; + if (!session_get(sess)) { + continue; + } session_lock(sess); if (!sess->ust_session) { goto unlock_session; @@ -970,6 +761,7 @@ static void update_ust_app(int app_sock) rcu_read_unlock(); unlock_session: session_unlock(sess); + session_put(sess); } } @@ -1092,7 +884,7 @@ static void *thread_manage_kernel(void *data) * New CPU detected by the kernel. Adding kernel stream to * kernel session and updating the kernel consumer */ - ret = update_kernel_stream(&kconsumer_data, pollfd); + ret = update_kernel_stream(pollfd); if (ret < 0) { continue; } @@ -1328,8 +1120,7 @@ restart: /* * Transfer the write-end of the channel monitoring and rotate pipe - * to the consumer by issuing a SET_CHANNEL_MONITOR_PIPE and - * SET_CHANNEL_ROTATE_PIPE commands. + * to the consumer by issuing a SET_CHANNEL_MONITOR_PIPE command. */ cmd_socket_wrapper = consumer_allocate_socket(&consumer_data->cmd_sock); if (!cmd_socket_wrapper) { @@ -1343,12 +1134,6 @@ restart: goto error; } - ret = consumer_send_channel_rotate_pipe(cmd_socket_wrapper, - consumer_data->channel_rotate_pipe); - if (ret) { - goto error; - } - /* Discard the socket wrapper as it is no longer needed. */ consumer_destroy_socket(cmd_socket_wrapper); cmd_socket_wrapper = NULL; @@ -2505,7 +2290,8 @@ static pid_t spawn_consumerd(struct consumer_data *consumer_data) } else if (stat(INSTALL_BIN_PATH "/" DEFAULT_CONSUMERD_FILE, &st) == 0) { DBG3("Found location #2"); consumer_to_use = INSTALL_BIN_PATH "/" DEFAULT_CONSUMERD_FILE; - } else if (stat(config.consumerd32_bin_path.value, &st) == 0) { + } else if (config.consumerd32_bin_path.value && + stat(config.consumerd32_bin_path.value, &st) == 0) { DBG3("Found location #3"); consumer_to_use = config.consumerd32_bin_path.value; } else { @@ -2921,21 +2707,132 @@ static unsigned int lttng_sessions_count(uid_t uid, gid_t gid) { unsigned int i = 0; struct ltt_session *session; + const struct ltt_session_list *session_list = session_get_list(); DBG("Counting number of available session for UID %d GID %d", uid, gid); - cds_list_for_each_entry(session, &session_list_ptr->head, list) { - /* - * Only list the sessions the user can control. - */ - if (!session_access_ok(session, uid, gid)) { + cds_list_for_each_entry(session, &session_list->head, list) { + if (!session_get(session)) { continue; } - i++; + session_lock(session); + /* Only count the sessions the user can control. */ + if (session_access_ok(session, uid, gid) && + !session->destroyed) { + i++; + } + session_unlock(session); + session_put(session); } return i; } +static int receive_userspace_probe(struct command_ctx *cmd_ctx, int sock, + int *sock_error, struct lttng_event *event) +{ + int fd, ret; + struct lttng_userspace_probe_location *probe_location; + const struct lttng_userspace_probe_location_lookup_method *lookup = NULL; + struct lttng_dynamic_buffer probe_location_buffer; + struct lttng_buffer_view buffer_view; + + /* + * Create a buffer to store the serialized version of the probe + * location. + */ + lttng_dynamic_buffer_init(&probe_location_buffer); + ret = lttng_dynamic_buffer_set_size(&probe_location_buffer, + cmd_ctx->lsm->u.enable.userspace_probe_location_len); + if (ret) { + ret = LTTNG_ERR_NOMEM; + goto error; + } + + /* + * Receive the probe location. + */ + ret = lttcomm_recv_unix_sock(sock, probe_location_buffer.data, + probe_location_buffer.size); + if (ret <= 0) { + DBG("Nothing recv() from client var len data... continuing"); + *sock_error = 1; + lttng_dynamic_buffer_reset(&probe_location_buffer); + ret = LTTNG_ERR_PROBE_LOCATION_INVAL; + goto error; + } + + buffer_view = lttng_buffer_view_from_dynamic_buffer( + &probe_location_buffer, 0, probe_location_buffer.size); + + /* + * Extract the probe location from the serialized version. + */ + ret = lttng_userspace_probe_location_create_from_buffer( + &buffer_view, &probe_location); + if (ret < 0) { + WARN("Failed to create a userspace probe location from the received buffer"); + lttng_dynamic_buffer_reset( &probe_location_buffer); + ret = LTTNG_ERR_PROBE_LOCATION_INVAL; + goto error; + } + + /* + * Receive the file descriptor to the target binary from the client. + */ + DBG("Receiving userspace probe target FD from client ..."); + ret = lttcomm_recv_fds_unix_sock(sock, &fd, 1); + if (ret <= 0) { + DBG("Nothing recv() from client userspace probe fd... continuing"); + *sock_error = 1; + ret = LTTNG_ERR_PROBE_LOCATION_INVAL; + goto error; + } + + /* + * Set the file descriptor received from the client through the unix + * socket in the probe location. + */ + lookup = lttng_userspace_probe_location_get_lookup_method(probe_location); + if (!lookup) { + ret = LTTNG_ERR_PROBE_LOCATION_INVAL; + goto error; + } + + /* + * From the kernel tracer's perspective, all userspace probe event types + * are all the same: a file and an offset. + */ + switch (lttng_userspace_probe_location_lookup_method_get_type(lookup)) { + case LTTNG_USERSPACE_PROBE_LOCATION_LOOKUP_METHOD_TYPE_FUNCTION_ELF: + ret = lttng_userspace_probe_location_function_set_binary_fd( + probe_location, fd); + break; + case LTTNG_USERSPACE_PROBE_LOCATION_LOOKUP_METHOD_TYPE_TRACEPOINT_SDT: + ret = lttng_userspace_probe_location_tracepoint_set_binary_fd( + probe_location, fd); + break; + default: + ret = LTTNG_ERR_PROBE_LOCATION_INVAL; + goto error; + } + + if (ret) { + ret = LTTNG_ERR_PROBE_LOCATION_INVAL; + goto error; + } + + /* Attach the probe location to the event. */ + ret = lttng_event_set_userspace_probe_location(event, probe_location); + if (ret) { + ret = LTTNG_ERR_PROBE_LOCATION_INVAL; + goto error; + } + + lttng_dynamic_buffer_reset(&probe_location_buffer); +error: + return ret; +} + /* * Check if the current kernel tracer supports the session rotation feature. * Return 1 if it does, 0 otherwise. @@ -2998,10 +2895,8 @@ static int process_client_msg(struct command_ctx *cmd_ctx, int sock, case LTTNG_UNREGISTER_TRIGGER: case LTTNG_ROTATE_SESSION: case LTTNG_ROTATION_GET_INFO: - case LTTNG_SESSION_GET_CURRENT_OUTPUT: case LTTNG_ROTATION_SET_SCHEDULE: - case LTTNG_ROTATION_SCHEDULE_GET_TIMER_PERIOD: - case LTTNG_ROTATION_SCHEDULE_GET_SIZE: + case LTTNG_SESSION_LIST_ROTATION_SCHEDULES: need_domain = 0; break; default: @@ -3046,8 +2941,7 @@ static int process_client_msg(struct command_ctx *cmd_ctx, int sock, case LTTNG_DATA_PENDING: case LTTNG_ROTATE_SESSION: case LTTNG_ROTATION_GET_INFO: - case LTTNG_ROTATION_SCHEDULE_GET_TIMER_PERIOD: - case LTTNG_ROTATION_SCHEDULE_GET_SIZE: + case LTTNG_SESSION_LIST_ROTATION_SCHEDULES: break; default: /* Setup lttng message with no payload */ @@ -3265,7 +3159,7 @@ static int process_client_msg(struct command_ctx *cmd_ctx, int sock, } /* - * Setup socket for consumer 64 bit. No need for atomic access + * Setup socket for consumer 32 bit. No need for atomic access * since it was set above and can ONLY be set in this thread. */ ret = consumer_create_socket(&ustconsumer32_data, @@ -3315,7 +3209,8 @@ skip_domain: if (need_tracing_session) { if (!session_access_ok(cmd_ctx->session, LTTNG_SOCK_GET_UID_CRED(&cmd_ctx->creds), - LTTNG_SOCK_GET_GID_CRED(&cmd_ctx->creds))) { + LTTNG_SOCK_GET_GID_CRED(&cmd_ctx->creds)) || + cmd_ctx->session->destroyed) { ret = LTTNG_ERR_EPERM; goto error; } @@ -3476,6 +3371,7 @@ error_add_context: } case LTTNG_ENABLE_EVENT: { + struct lttng_event *ev = NULL; struct lttng_event_exclusion *exclusion = NULL; struct lttng_filter_bytecode *bytecode = NULL; char *filter_expression = NULL; @@ -3527,7 +3423,7 @@ error_add_context: ret = lttcomm_recv_unix_sock(sock, filter_expression, expression_len); if (ret <= 0) { - DBG("Nothing recv() from client car len data... continuing"); + DBG("Nothing recv() from client var len data... continuing"); *sock_error = 1; free(filter_expression); free(exclusion); @@ -3559,7 +3455,7 @@ error_add_context: DBG("Receiving var len filter's bytecode from client ..."); ret = lttcomm_recv_unix_sock(sock, bytecode, bytecode_len); if (ret <= 0) { - DBG("Nothing recv() from client car len data... continuing"); + DBG("Nothing recv() from client var len data... continuing"); *sock_error = 1; free(filter_expression); free(bytecode); @@ -3577,11 +3473,36 @@ error_add_context: } } + ev = lttng_event_copy(&cmd_ctx->lsm->u.enable.event); + if (!ev) { + DBG("Failed to copy event: %s", + cmd_ctx->lsm->u.enable.event.name); + free(filter_expression); + free(bytecode); + free(exclusion); + ret = LTTNG_ERR_NOMEM; + goto error; + } + + + if (cmd_ctx->lsm->u.enable.userspace_probe_location_len > 0) { + /* Expect a userspace probe description. */ + ret = receive_userspace_probe(cmd_ctx, sock, sock_error, ev); + if (ret) { + free(filter_expression); + free(bytecode); + free(exclusion); + lttng_event_destroy(ev); + goto error; + } + } + ret = cmd_enable_event(cmd_ctx->session, &cmd_ctx->lsm->domain, cmd_ctx->lsm->u.enable.channel_name, - &cmd_ctx->lsm->u.enable.event, + ev, filter_expression, bytecode, exclusion, kernel_poll_pipe[1]); + lttng_event_destroy(ev); break; } case LTTNG_LIST_TRACEPOINTS: @@ -3804,11 +3725,8 @@ error_add_context: } case LTTNG_DESTROY_SESSION: { - ret = cmd_destroy_session(cmd_ctx->session, kernel_poll_pipe[1], + ret = cmd_destroy_session(cmd_ctx->session, notification_thread_handle); - - /* Set session to NULL so we do not unlock it after free. */ - cmd_ctx->session = NULL; break; } case LTTNG_LIST_DOMAINS: @@ -4202,39 +4120,26 @@ error_add_context: ret = LTTNG_OK; break; } - case LTTNG_SESSION_GET_CURRENT_OUTPUT: - { - struct lttng_session_get_current_output_return output_return; - - memset(&output_return, 0, sizeof(output_return)); - ret = cmd_session_get_current_output(cmd_ctx->session, - &output_return); - if (ret < 0) { - ret = -ret; - goto error; - } - - ret = setup_lttng_msg_no_cmd_header(cmd_ctx, &output_return, - sizeof(output_return)); - if (ret < 0) { - ret = -ret; - goto error; - } - - ret = LTTNG_OK; - break; - } case LTTNG_ROTATION_SET_SCHEDULE: { + bool set_schedule; + enum lttng_rotation_schedule_type schedule_type; + uint64_t value; + if (cmd_ctx->session->kernel_session && !check_rotate_compatible()) { DBG("Kernel tracer version does not support session rotations"); ret = LTTNG_ERR_ROTATION_WRONG_VERSION; goto error; } + set_schedule = cmd_ctx->lsm->u.rotation_set_schedule.set == 1; + schedule_type = (enum lttng_rotation_schedule_type) cmd_ctx->lsm->u.rotation_set_schedule.type; + value = cmd_ctx->lsm->u.rotation_set_schedule.value; + ret = cmd_rotation_set_schedule(cmd_ctx->session, - cmd_ctx->lsm->u.rotate_setup.timer_us, - cmd_ctx->lsm->u.rotate_setup.size, + set_schedule, + schedule_type, + value, notification_thread_handle); if (ret != LTTNG_OK) { goto error; @@ -4242,42 +4147,17 @@ error_add_context: break; } - case LTTNG_ROTATION_SCHEDULE_GET_TIMER_PERIOD: - { - struct lttng_rotation_schedule_get_timer_period *get_timer; - - get_timer = zmalloc(sizeof(struct lttng_rotation_schedule_get_timer_period)); - if (!get_timer) { - ret = ENOMEM; - goto error; - } - get_timer->rotate_timer = cmd_ctx->session->rotate_timer_period; - - ret = setup_lttng_msg_no_cmd_header(cmd_ctx, get_timer, - sizeof(struct lttng_rotation_schedule_get_timer_period)); - free(get_timer); - if (ret < 0) { - ret = -ret; - goto error; - } - - ret = LTTNG_OK; - break; - } - case LTTNG_ROTATION_SCHEDULE_GET_SIZE: + case LTTNG_SESSION_LIST_ROTATION_SCHEDULES: { - struct lttng_rotation_schedule_get_size *get_size; - - get_size = zmalloc(sizeof(struct lttng_rotation_schedule_get_size)); - if (!get_size) { - ret = ENOMEM; - goto error; - } - get_size->rotate_size = cmd_ctx->session->rotate_size; - - ret = setup_lttng_msg_no_cmd_header(cmd_ctx, get_size, - sizeof(struct lttng_rotation_schedule_get_size)); - free(get_size); + struct lttng_session_list_schedules_return schedules = { + .periodic.set = !!cmd_ctx->session->rotate_timer_period, + .periodic.value = cmd_ctx->session->rotate_timer_period, + .size.set = !!cmd_ctx->session->rotate_size, + .size.value = cmd_ctx->session->rotate_size, + }; + + ret = setup_lttng_msg_no_cmd_header(cmd_ctx, &schedules, + sizeof(schedules)); if (ret < 0) { ret = -ret; goto error; @@ -4303,6 +4183,7 @@ error: setup_error: if (cmd_ctx->session) { session_unlock(cmd_ctx->session); + session_put(cmd_ctx->session); } if (need_tracing_session) { session_unlock_list(); @@ -4312,196 +4193,6 @@ init_setup_error: return ret; } -/* - * Thread managing health check socket. - */ -static void *thread_manage_health(void *data) -{ - int sock = -1, new_sock = -1, ret, i, pollfd, err = -1; - uint32_t revents, nb_fd; - struct lttng_poll_event events; - struct health_comm_msg msg; - struct health_comm_reply reply; - - DBG("[thread] Manage health check started"); - - rcu_register_thread(); - - /* We might hit an error path before this is created. */ - lttng_poll_init(&events); - - /* Create unix socket */ - sock = lttcomm_create_unix_sock(config.health_unix_sock_path.value); - if (sock < 0) { - ERR("Unable to create health check Unix socket"); - goto error; - } - - if (is_root) { - /* lttng health client socket path permissions */ - ret = chown(config.health_unix_sock_path.value, 0, - utils_get_group_id(config.tracing_group_name.value)); - if (ret < 0) { - ERR("Unable to set group on %s", config.health_unix_sock_path.value); - PERROR("chown"); - goto error; - } - - ret = chmod(config.health_unix_sock_path.value, - S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP); - if (ret < 0) { - ERR("Unable to set permissions on %s", config.health_unix_sock_path.value); - PERROR("chmod"); - goto error; - } - } - - /* - * Set the CLOEXEC flag. Return code is useless because either way, the - * show must go on. - */ - (void) utils_set_fd_cloexec(sock); - - ret = lttcomm_listen_unix_sock(sock); - if (ret < 0) { - goto error; - } - - /* - * Pass 2 as size here for the thread quit pipe and client_sock. Nothing - * more will be added to this poll set. - */ - ret = sessiond_set_thread_pollset(&events, 2); - if (ret < 0) { - goto error; - } - - /* Add the application registration socket */ - ret = lttng_poll_add(&events, sock, LPOLLIN | LPOLLPRI); - if (ret < 0) { - goto error; - } - - sessiond_notify_ready(); - - while (1) { - DBG("Health check ready"); - - /* Inifinite blocking call, waiting for transmission */ -restart: - ret = lttng_poll_wait(&events, -1); - if (ret < 0) { - /* - * Restart interrupted system call. - */ - if (errno == EINTR) { - goto restart; - } - goto error; - } - - nb_fd = ret; - - for (i = 0; i < nb_fd; i++) { - /* Fetch once the poll data */ - revents = LTTNG_POLL_GETEV(&events, i); - pollfd = LTTNG_POLL_GETFD(&events, i); - - if (!revents) { - /* No activity for this FD (poll implementation). */ - continue; - } - - /* Thread quit pipe has been closed. Killing thread. */ - ret = sessiond_check_thread_quit_pipe(pollfd, revents); - if (ret) { - err = 0; - goto exit; - } - - /* Event on the registration socket */ - if (pollfd == sock) { - if (revents & LPOLLIN) { - continue; - } else if (revents & (LPOLLERR | LPOLLHUP | LPOLLRDHUP)) { - ERR("Health socket poll error"); - goto error; - } else { - ERR("Unexpected poll events %u for sock %d", revents, pollfd); - goto error; - } - } - } - - new_sock = lttcomm_accept_unix_sock(sock); - if (new_sock < 0) { - goto error; - } - - /* - * Set the CLOEXEC flag. Return code is useless because either way, the - * show must go on. - */ - (void) utils_set_fd_cloexec(new_sock); - - DBG("Receiving data from client for health..."); - ret = lttcomm_recv_unix_sock(new_sock, (void *)&msg, sizeof(msg)); - if (ret <= 0) { - DBG("Nothing recv() from client... continuing"); - ret = close(new_sock); - if (ret) { - PERROR("close"); - } - continue; - } - - rcu_thread_online(); - - memset(&reply, 0, sizeof(reply)); - for (i = 0; i < NR_HEALTH_SESSIOND_TYPES; i++) { - /* - * health_check_state returns 0 if health is - * bad. - */ - if (!health_check_state(health_sessiond, i)) { - reply.ret_code |= 1ULL << i; - } - } - - DBG2("Health check return value %" PRIx64, reply.ret_code); - - ret = send_unix_sock(new_sock, (void *) &reply, sizeof(reply)); - if (ret < 0) { - ERR("Failed to send health data back to client"); - } - - /* End of transmission */ - ret = close(new_sock); - if (ret) { - PERROR("close"); - } - } - -exit: -error: - if (err) { - ERR("Health error occurred in %s", __func__); - } - DBG("Health check thread dying"); - unlink(config.health_unix_sock_path.value); - if (sock >= 0) { - ret = close(sock); - if (ret) { - PERROR("close"); - } - } - - lttng_poll_clean(&events); - stop_threads(); - rcu_unregister_thread(); - return NULL; -} - /* * This thread manage all clients request using the unix client socket for * communication. @@ -4553,14 +4244,6 @@ static void *thread_manage_clients(void *data) * commands. */ while (uatomic_read(<tng_sessiond_ready) != 0) { - fd_set read_fds; - struct timeval timeout; - - FD_ZERO(&read_fds); - FD_SET(thread_quit_pipe[0], &read_fds); - memset(&timeout, 0, sizeof(timeout)); - timeout.tv_usec = 1000; - /* * If a support thread failed to launch, it may signal that * we must exit and the sessiond would never be marked as @@ -4569,9 +4252,8 @@ static void *thread_manage_clients(void *data) * The timeout is set to 1ms, which serves as a way to * pace down this check. */ - ret = select(thread_quit_pipe[0] + 1, &read_fds, NULL, NULL, - &timeout); - if (ret > 0 || (ret < 0 && errno != EINTR)) { + ret = sessiond_wait_for_quit_pipe(1000); + if (ret > 0) { goto exit; } } @@ -4597,7 +4279,12 @@ static void *thread_manage_clients(void *data) health_code_update(); + /* Set state as running. */ + sessiond_set_client_thread_state(true); + while (1) { + const struct cmd_completion_handler *cmd_completion_handler; + DBG("Accepting client command ..."); /* Inifinite blocking call, waiting for transmission */ @@ -4740,6 +4427,18 @@ static void *thread_manage_clients(void *data) continue; } + cmd_completion_handler = cmd_pop_completion_handler(); + if (cmd_completion_handler) { + enum lttng_error_code completion_code; + + completion_code = cmd_completion_handler->run( + cmd_completion_handler->data); + if (completion_code != LTTNG_OK) { + clean_command_ctx(&cmd_ctx); + continue; + } + } + health_code_update(); DBG("Sending response (size: %d, retcode: %s (%d))", @@ -4817,6 +4516,9 @@ error_create_poll: errno = ret; PERROR("join_consumer ust64"); } + + /* Set state as non-running. */ + sessiond_set_client_thread_state(false); return NULL; } @@ -5739,46 +5441,48 @@ end: return ret; } -static -struct rotation_thread_timer_queue *create_rotate_timer_queue(void) +static void destroy_all_sessions_and_wait(void) { - struct rotation_thread_timer_queue *queue = NULL; - - queue = zmalloc(sizeof(struct rotation_thread_timer_queue)); - if (!queue) { - PERROR("Failed to allocate timer rotate queue"); - goto end; - } - - queue->event_pipe = lttng_pipe_open(FD_CLOEXEC | O_NONBLOCK); - CDS_INIT_LIST_HEAD(&queue->list); - pthread_mutex_init(&queue->lock, NULL); - -end: - return queue; -} + struct ltt_session *session, *tmp; + struct ltt_session_list *session_list; -static -void destroy_rotate_timer_queue(struct rotation_thread_timer_queue *queue) -{ - struct sessiond_rotation_timer *node, *tmp_node; + session_list = session_get_list(); + DBG("Initiating destruction of all sessions"); - if (!queue) { + if (!session_list) { return; } - lttng_pipe_destroy(queue->event_pipe); + /* + * Ensure that the client thread is no longer accepting new commands, + * which could cause new sessions to be created. + */ + sessiond_wait_client_thread_stopped(); - pthread_mutex_lock(&queue->lock); - /* Empty wait queue. */ - cds_list_for_each_entry_safe(node, tmp_node, &queue->list, head) { - cds_list_del(&node->head); - free(node); + session_lock_list(); + /* Initiate the destruction of all sessions. */ + cds_list_for_each_entry_safe(session, tmp, + &session_list->head, list) { + if (!session_get(session)) { + continue; + } + + session_lock(session); + if (session->destroyed) { + goto unlock_session; + } + (void) cmd_destroy_session(session, + notification_thread_handle); + unlock_session: + session_unlock(session); + session_put(session); } - pthread_mutex_unlock(&queue->lock); + session_unlock_list(); - pthread_mutex_destroy(&queue->lock); - free(queue); + /* Wait for the destruction of all sessions to complete. */ + DBG("Waiting for the destruction of all sessions to complete"); + session_list_wait_empty(); + DBG("Destruction of all sessions completed"); } /* @@ -5792,16 +5496,12 @@ int main(int argc, char **argv) struct lttng_pipe *ust32_channel_monitor_pipe = NULL, *ust64_channel_monitor_pipe = NULL, *kernel_channel_monitor_pipe = NULL; - bool notification_thread_launched = false; bool rotation_thread_launched = false; bool timer_thread_launched = false; - struct lttng_pipe *ust32_channel_rotate_pipe = NULL, - *ust64_channel_rotate_pipe = NULL, - *kernel_channel_rotate_pipe = NULL; + struct lttng_thread *ht_cleanup_thread = NULL; struct timer_thread_parameters timer_thread_ctx; /* Queue of rotation jobs populated by the sessiond-timer. */ struct rotation_thread_timer_queue *rotation_timer_queue = NULL; - sem_t notification_thread_ready; init_kernel_workarounds(); @@ -5812,7 +5512,7 @@ int main(int argc, char **argv) goto exit_set_signal_handler; } - if (sessiond_timer_signal_init()) { + if (timer_signal_init()) { retval = -1; goto exit_set_signal_handler; } @@ -5830,6 +5530,12 @@ int main(int argc, char **argv) goto exit_set_signal_handler; } + /* + * Init config from environment variables. + * Command line option override env configuration per-doc. Do env first. + */ + sessiond_config_apply_env_config(&config); + /* * Parse arguments and load the daemon configuration file. * @@ -5844,9 +5550,6 @@ int main(int argc, char **argv) goto exit_options; } - /* Init config from environment variables. */ - sessiond_config_apply_env_config(&config); - /* * Resolve all paths received as arguments, configuration option, or * through environment variable as absolute paths. This is necessary @@ -5934,13 +5637,14 @@ int main(int argc, char **argv) } /* Create thread to clean up RCU hash tables */ - if (init_ht_cleanup_thread(&ht_cleanup_thread)) { + ht_cleanup_thread = launch_ht_cleanup_thread(); + if (!ht_cleanup_thread) { retval = -1; goto exit_ht_cleanup; } /* Create thread quit pipe */ - if (init_thread_quit_pipe()) { + if (sessiond_init_thread_quit_pipe()) { retval = -1; goto exit_init_data; } @@ -5963,19 +5667,6 @@ int main(int argc, char **argv) retval = -1; goto exit_init_data; } - kernel_channel_rotate_pipe = lttng_pipe_open(0); - if (!kernel_channel_rotate_pipe) { - ERR("Failed to create kernel consumer channel rotate pipe"); - retval = -1; - goto exit_init_data; - } - kconsumer_data.channel_rotate_pipe = - lttng_pipe_release_writefd( - kernel_channel_rotate_pipe); - if (kconsumer_data.channel_rotate_pipe < 0) { - retval = -1; - goto exit_init_data; - } } /* Set consumer initial state */ @@ -5994,30 +5685,18 @@ int main(int argc, char **argv) retval = -1; goto exit_init_data; } - ust32_channel_rotate_pipe = lttng_pipe_open(0); - if (!ust32_channel_rotate_pipe) { - ERR("Failed to create 32-bit user space consumer channel rotate pipe"); - retval = -1; - goto exit_init_data; - } - ustconsumer32_data.channel_rotate_pipe = lttng_pipe_release_writefd( - ust32_channel_rotate_pipe); - if (ustconsumer32_data.channel_rotate_pipe < 0) { - retval = -1; - goto exit_init_data; - } /* - * The rotation_timer_queue structure is shared between the sessiond timer - * thread and the rotation thread. The main() keeps the ownership and - * destroys it when both threads have quit. + * The rotation_thread_timer_queue structure is shared between the + * sessiond timer thread and the rotation thread. The main thread keeps + * its ownership and destroys it when both threads have been joined. */ - rotation_timer_queue = create_rotate_timer_queue(); + rotation_timer_queue = rotation_thread_timer_queue_create(); if (!rotation_timer_queue) { retval = -1; goto exit_init_data; } - timer_thread_ctx.rotation_timer_queue = rotation_timer_queue; + timer_thread_ctx.rotation_thread_job_queue = rotation_timer_queue; ust64_channel_monitor_pipe = lttng_pipe_open(0); if (!ust64_channel_monitor_pipe) { @@ -6031,18 +5710,6 @@ int main(int argc, char **argv) retval = -1; goto exit_init_data; } - ust64_channel_rotate_pipe = lttng_pipe_open(0); - if (!ust64_channel_rotate_pipe) { - ERR("Failed to create 64-bit user space consumer channel rotate pipe"); - retval = -1; - goto exit_init_data; - } - ustconsumer64_data.channel_rotate_pipe = lttng_pipe_release_writefd( - ust64_channel_rotate_pipe); - if (ustconsumer64_data.channel_rotate_pipe < 0) { - retval = -1; - goto exit_init_data; - } /* * Init UST app hash table. Alloc hash table before this point since @@ -6149,12 +5816,6 @@ int main(int argc, char **argv) /* Init UST command queue. */ cds_wfcq_init(&ust_cmd_queue.head, &ust_cmd_queue.tail); - /* - * Get session list pointer. This pointer MUST NOT be free'd. This list - * is statically declared in session.c - */ - session_list_ptr = session_get_list(); - cmd_init(); /* Check for the application socket timeout env variable. */ @@ -6184,50 +5845,32 @@ int main(int argc, char **argv) load_info->path = config.load_session_path.value; /* Create health-check thread. */ - ret = pthread_create(&health_thread, default_pthread_attr(), - thread_manage_health, (void *) NULL); - if (ret) { - errno = ret; - PERROR("pthread_create health"); + if (!launch_health_management_thread()) { retval = -1; goto exit_health; } - /* - * The rotation thread needs the notification thread to be ready before - * creating the rotate_notification_channel, so we use this semaphore as - * a rendez-vous point. - */ - sem_init(¬ification_thread_ready, 0, 0); - /* notification_thread_data acquires the pipes' read side. */ notification_thread_handle = notification_thread_handle_create( ust32_channel_monitor_pipe, ust64_channel_monitor_pipe, - kernel_channel_monitor_pipe, - ¬ification_thread_ready); + kernel_channel_monitor_pipe); if (!notification_thread_handle) { retval = -1; ERR("Failed to create notification thread shared data"); - stop_threads(); goto exit_notification; } /* Create notification thread. */ - ret = pthread_create(¬ification_thread, default_pthread_attr(), - thread_notification, notification_thread_handle); - if (ret) { - errno = ret; - PERROR("pthread_create notification"); + if (!launch_notification_thread(notification_thread_handle)) { retval = -1; - stop_threads(); goto exit_notification; + } - notification_thread_launched = true; /* Create timer thread. */ ret = pthread_create(&timer_thread, default_pthread_attr(), - sessiond_timer_thread, &timer_thread_ctx); + timer_thread_func, &timer_thread_ctx); if (ret) { errno = ret; PERROR("pthread_create timer"); @@ -6239,13 +5882,8 @@ int main(int argc, char **argv) /* rotation_thread_data acquires the pipes' read side. */ rotation_thread_handle = rotation_thread_handle_create( - ust32_channel_rotate_pipe, - ust64_channel_rotate_pipe, - kernel_channel_rotate_pipe, - thread_quit_pipe[0], rotation_timer_queue, - notification_thread_handle, - ¬ification_thread_ready); + notification_thread_handle); if (!rotation_thread_handle) { retval = -1; ERR("Failed to create rotation thread shared data"); @@ -6367,6 +6005,10 @@ int main(int argc, char **argv) PERROR("pthread_join load_session_thread"); retval = -1; } + + /* Initiate teardown once activity occurs on the quit pipe. */ + sessiond_wait_for_quit_pipe(-1U); + destroy_all_sessions_and_wait(); exit_load_session: if (is_root && !config.no_kernel) { @@ -6433,14 +6075,7 @@ exit_dispatch: exit_client: exit_rotation: exit_notification: - sem_destroy(¬ification_thread_ready); - ret = pthread_join(health_thread, &status); - if (ret) { - errno = ret; - PERROR("pthread_join health thread"); - retval = -1; - } - + lttng_thread_list_shutdown_orphans(); exit_health: exit_init_data: /* @@ -6463,25 +6098,6 @@ exit_init_data: */ rcu_barrier(); - /* - * The teardown of the notification system is performed after the - * session daemon's teardown in order to allow it to be notified - * of the active session and channels at the moment of the teardown. - */ - if (notification_thread_handle) { - if (notification_thread_launched) { - notification_thread_command_quit( - notification_thread_handle); - ret = pthread_join(notification_thread, &status); - if (ret) { - errno = ret; - PERROR("pthread_join notification thread"); - retval = -1; - } - } - notification_thread_handle_destroy(notification_thread_handle); - } - if (rotation_thread_handle) { if (rotation_thread_launched) { ret = pthread_join(rotation_thread, &status); @@ -6495,7 +6111,7 @@ exit_init_data: } if (timer_thread_launched) { - kill(getpid(), LTTNG_SESSIOND_SIG_EXIT); + timer_exit(); ret = pthread_join(timer_thread, &status); if (ret) { errno = ret; @@ -6504,25 +6120,31 @@ exit_init_data: } } + if (ht_cleanup_thread) { + lttng_thread_shutdown(ht_cleanup_thread); + lttng_thread_put(ht_cleanup_thread); + } + /* * After the rotation and timer thread have quit, we can safely destroy * the rotation_timer_queue. */ - destroy_rotate_timer_queue(rotation_timer_queue); + rotation_thread_timer_queue_destroy(rotation_timer_queue); rcu_thread_offline(); rcu_unregister_thread(); - ret = fini_ht_cleanup_thread(&ht_cleanup_thread); - if (ret) { - retval = -1; + /* + * The teardown of the notification system is performed after the + * session daemon's teardown in order to allow it to be notified + * of the active session and channels at the moment of the teardown. + */ + if (notification_thread_handle) { + notification_thread_handle_destroy(notification_thread_handle); } lttng_pipe_destroy(ust32_channel_monitor_pipe); lttng_pipe_destroy(ust64_channel_monitor_pipe); lttng_pipe_destroy(kernel_channel_monitor_pipe); - lttng_pipe_destroy(ust32_channel_rotate_pipe); - lttng_pipe_destroy(ust64_channel_rotate_pipe); - lttng_pipe_destroy(kernel_channel_rotate_pipe); exit_ht_cleanup: health_app_destroy(health_sessiond);