#include "agent.h"
#include "ht-cleanup.h"
#include "sessiond-config.h"
-#include "sessiond-timer.h"
+#include "timer.h"
static const char *help_msg =
#ifdef LTTNG_EMBED_HELP
;
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. */
{ 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" };
/* 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
*/
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;
*/
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";
/*
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
+ * Section name to look for in the daemon configuration file.
*/
-#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;
-}
+static const char * const config_section_name = "sessiond";
-/* 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);
- }
+/* Am I root or not. Set to 1 if the daemon is running as root */
+static int is_root;
- /*
- * 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.
- */
-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;
- }
-
- 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.
/* 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");
}
static void sessiond_cleanup(void)
{
int ret;
- struct ltt_session *sess, *stmp;
+ struct ltt_session_list *session_list = session_get_list();
DBG("Cleanup sessiond");
* 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) {
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);
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;
}
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);
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;
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;
}
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) {
}
/* 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;
rcu_read_unlock();
unlock_session:
session_unlock(sess);
+ session_put(sess);
}
}
{
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;
}
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;
}
}
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:
setup_error:
if (cmd_ctx->session) {
session_unlock(cmd_ctx->session);
+ session_put(cmd_ctx->session);
}
if (need_tracing_session) {
session_unlock_list();
* 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
* 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;
}
}
health_code_update();
+ /* Set state as running. */
+ sessiond_set_client_thread_state(true);
+
while (1) {
const struct cmd_completion_handler *cmd_completion_handler;
errno = ret;
PERROR("join_consumer ust64");
}
+
+ /* Set state as non-running. */
+ sessiond_set_client_thread_state(false);
return NULL;
}
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
*/
}
/* Create thread quit pipe */
- if (init_thread_quit_pipe()) {
+ if (sessiond_init_thread_quit_pipe()) {
retval = -1;
goto exit_init_data;
}
/* 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. */
/* 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);
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) {