X-Git-Url: http://git.efficios.com/?p=lttng-tools.git;a=blobdiff_plain;f=src%2Fbin%2Flttng-sessiond%2Fmain.c;h=7e4e2950b19e9cf1903a793553cca0351fa1b225;hp=67d990027f523079e3ae5635f5ec605573bc9c20;hb=a3707772aa2b063a321dd6d7dfb2ea369bc02c43;hpb=8e319828707210e48a5f3e74495881594dba73e8 diff --git a/src/bin/lttng-sessiond/main.c b/src/bin/lttng-sessiond/main.c index 67d990027..7e4e2950b 100644 --- a/src/bin/lttng-sessiond/main.c +++ b/src/bin/lttng-sessiond/main.c @@ -82,6 +82,7 @@ #include "ht-cleanup.h" #include "sessiond-config.h" #include "timer.h" +#include "thread.h" static const char *help_msg = #ifdef LTTNG_EMBED_HELP @@ -92,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. */ @@ -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; @@ -206,7 +193,6 @@ 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; @@ -224,21 +210,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 +246,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; +static struct load_session_thread_data *load_info; /* - * 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; -} - -/* - * 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; - - ret = pipe(a_pipe); - if (ret < 0) { - PERROR("thread quit pipe"); - goto error; - } +static const char * const config_section_name = "sessiond"; - for (i = 0; i < 2; i++) { - ret = fcntl(a_pipe[i], F_SETFD, FD_CLOEXEC); - if (ret < 0) { - PERROR("fcntl"); - goto error; - } - } +/* Am I root or not. Set to 1 if the daemon is running as root */ +static int is_root; -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 +269,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"); } @@ -563,7 +375,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"); @@ -571,7 +383,7 @@ 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(); ret = remove(config.pid_file_path.value); if (ret < 0) { @@ -609,19 +421,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); @@ -794,16 +594,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; } @@ -813,6 +618,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); @@ -840,14 +646,19 @@ static int update_kernel_stream(int fd) 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; @@ -895,12 +706,14 @@ static int update_kernel_stream(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; } @@ -912,6 +725,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) { @@ -920,9 +734,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; @@ -946,6 +763,7 @@ static void update_ust_app(int app_sock) rcu_read_unlock(); unlock_session: session_unlock(sess); + session_put(sess); } } @@ -2891,17 +2709,22 @@ 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; } @@ -3388,7 +3211,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; } @@ -3903,11 +3727,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: @@ -4364,6 +4185,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(); @@ -4614,14 +4436,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 @@ -4630,9 +4444,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; } } @@ -4658,6 +4471,9 @@ 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; @@ -4892,6 +4708,9 @@ error_create_poll: errno = ret; PERROR("join_consumer ust64"); } + + /* Set state as non-running. */ + sessiond_set_client_thread_state(false); return NULL; } @@ -5814,6 +5633,50 @@ end: return ret; } +static void destroy_all_sessions_and_wait(void) +{ + struct ltt_session *session, *tmp; + struct ltt_session_list *session_list; + + session_list = session_get_list(); + DBG("Initiating destruction of all sessions"); + + if (!session_list) { + return; + } + + /* + * Ensure that the client thread is no longer accepting new commands, + * which could cause new sessions to be created. + */ + sessiond_wait_client_thread_stopped(); + + 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); + } + session_unlock_list(); + + /* 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"); +} + /* * main */ @@ -5828,6 +5691,7 @@ int main(int argc, char **argv) bool notification_thread_launched = false; bool rotation_thread_launched = false; bool timer_thread_launched = false; + 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; @@ -5967,13 +5831,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; } @@ -6145,12 +6010,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. */ @@ -6235,7 +6094,6 @@ int main(int argc, char **argv) /* rotation_thread_data acquires the pipes' read side. */ rotation_thread_handle = rotation_thread_handle_create( - thread_quit_pipe[0], rotation_timer_queue, notification_thread_handle, ¬ification_thread_ready); @@ -6360,6 +6218,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,6 +6295,7 @@ exit_notification: PERROR("pthread_join health thread"); retval = -1; } + lttng_thread_list_shutdown_orphans(); exit_health: exit_init_data: @@ -6497,6 +6360,11 @@ 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. @@ -6506,10 +6374,6 @@ exit_init_data: rcu_thread_offline(); rcu_unregister_thread(); - ret = fini_ht_cleanup_thread(&ht_cleanup_thread); - if (ret) { - retval = -1; - } lttng_pipe_destroy(ust32_channel_monitor_pipe); lttng_pipe_destroy(ust64_channel_monitor_pipe); lttng_pipe_destroy(kernel_channel_monitor_pipe);