Fix: userspace probe accessors are not const-correct
[lttng-tools.git] / src / bin / lttng-sessiond / main.c
index 13bd649180452ad8346f8d7a5729446a9c58b157..ee50db21e38b2c29a400b1ccec0065f72f28900c 100644 (file)
@@ -17,7 +17,6 @@
  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
  */
 
-#define _GNU_SOURCE
 #define _LGPL_SOURCE
 #include <getopt.h>
 #include <grp.h>
@@ -38,7 +37,7 @@
 #include <sys/wait.h>
 #include <urcu/uatomic.h>
 #include <unistd.h>
-#include <config.h>
+#include <ctype.h>
 
 #include <common/common.h>
 #include <common/compat/socket.h>
@@ -49,7 +48,9 @@
 #include <common/relayd/relayd.h>
 #include <common/utils.h>
 #include <common/daemonize.h>
-#include <common/config/config.h>
+#include <common/config/session-config.h>
+#include <common/dynamic-buffer.h>
+#include <lttng/userspace-probe-internal.h>
 
 #include "lttng-sessiond.h"
 #include "buffer-registry.h"
 #include "agent-thread.h"
 #include "save.h"
 #include "load-session-thread.h"
-#include "syscall.h"
+#include "notification-thread.h"
+#include "notification-thread-commands.h"
+#include "rotation-thread.h"
+#include "lttng-syscall.h"
 #include "agent.h"
+#include "ht-cleanup.h"
+#include "sessiond-config.h"
+#include "sessiond-timer.h"
 
-#define CONSUMERD_FILE "lttng-consumerd"
+static const char *help_msg =
+#ifdef LTTNG_EMBED_HELP
+#include <lttng-sessiond.8.h>
+#else
+NULL
+#endif
+;
 
 const char *progname;
-static const char *tracing_group_name = DEFAULT_TRACING_GROUP;
-static int tracing_group_name_override;
-static char *opt_pidfile;
-static int opt_sig_parent;
-static int opt_verbose_consumer;
-static int opt_daemon, opt_background;
-static int opt_no_kernel;
-static char *opt_load_session_path;
 static pid_t ppid;          /* Parent PID for --sig-parent option */
 static pid_t child_ppid;    /* Internal parent PID use with daemonize. */
-static char *rundir;
 static int lockfile_fd = -1;
 
 /* Set to 1 when a SIGUSR1 signal is received. */
 static int recv_child_signal;
 
+static struct lttng_kernel_tracer_version kernel_tracer_version;
+static struct lttng_kernel_tracer_abi_version kernel_tracer_abi_version;
+
 /*
  * Consumer daemon specific control data. Every value not initialized here is
  * set to 0 by the static definition.
  */
 static struct consumer_data kconsumer_data = {
        .type = LTTNG_CONSUMER_KERNEL,
-       .err_unix_sock_path = DEFAULT_KCONSUMERD_ERR_SOCK_PATH,
-       .cmd_unix_sock_path = DEFAULT_KCONSUMERD_CMD_SOCK_PATH,
        .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,
@@ -111,10 +118,10 @@ static struct consumer_data kconsumer_data = {
 };
 static struct consumer_data ustconsumer64_data = {
        .type = LTTNG_CONSUMER64_UST,
-       .err_unix_sock_path = DEFAULT_USTCONSUMERD64_ERR_SOCK_PATH,
-       .cmd_unix_sock_path = DEFAULT_USTCONSUMERD64_CMD_SOCK_PATH,
        .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,
@@ -122,10 +129,10 @@ static struct consumer_data ustconsumer64_data = {
 };
 static struct consumer_data ustconsumer32_data = {
        .type = LTTNG_CONSUMER32_UST,
-       .err_unix_sock_path = DEFAULT_USTCONSUMERD32_ERR_SOCK_PATH,
-       .cmd_unix_sock_path = DEFAULT_USTCONSUMERD32_CMD_SOCK_PATH,
        .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,
@@ -165,21 +172,14 @@ 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" };
 
 /* Shared between threads */
 static int dispatch_thread_exit;
 
-/* Global application Unix socket path */
-static char apps_unix_sock_path[PATH_MAX];
-/* Global client Unix socket path */
-static char client_unix_sock_path[PATH_MAX];
-/* global wait shm path for UST */
-static char wait_shm_path[PATH_MAX];
-/* Global health check unix path */
-static char health_unix_sock_path[PATH_MAX];
-
 /* Sockets and FDs */
 static int client_sock = -1;
 static int apps_sock = -1;
@@ -191,7 +191,6 @@ static int kernel_poll_pipe[2] = { -1, -1 };
  * for all threads when receiving an event on the pipe.
  */
 static int thread_quit_pipe[2] = { -1, -1 };
-static int ht_cleanup_quit_pipe[2] = { -1, -1 };
 
 /*
  * This pipe is used to inform the thread managing application communication
@@ -212,6 +211,9 @@ 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;
 
 /*
  * UST registration command queue. This queue is tied with a futex and uses a N
@@ -239,15 +241,6 @@ static struct ltt_session_list *session_list_ptr;
 int ust_consumerd64_fd = -1;
 int ust_consumerd32_fd = -1;
 
-static const char *consumerd32_bin = CONFIG_CONSUMERD32_BIN;
-static const char *consumerd64_bin = CONFIG_CONSUMERD64_BIN;
-static const char *consumerd32_libdir = CONFIG_CONSUMERD32_LIBDIR;
-static const char *consumerd64_libdir = CONFIG_CONSUMERD64_LIBDIR;
-static int consumerd32_bin_override;
-static int consumerd64_bin_override;
-static int consumerd32_libdir_override;
-static int consumerd64_libdir_override;
-
 static const char *module_proc_lttng = "/proc/lttng";
 
 /*
@@ -284,20 +277,12 @@ enum consumerd_state {
 static enum consumerd_state ust_consumerd_state;
 static enum consumerd_state kernel_consumerd_state;
 
-/*
- * Socket timeout for receiving and sending in seconds.
- */
-static int app_socket_timeout;
-
 /* Set in main() with the current page size. */
 long page_size;
 
 /* Application health monitoring */
 struct health_app *health_sessiond;
 
-/* Agent TCP port for registration. Used by the agent thread. */
-unsigned int agent_tcp_port = DEFAULT_AGENT_TCP_PORT;
-
 /* Am I root or not. */
 int is_root;                   /* Set to 1 if the daemon is running as root */
 
@@ -306,90 +291,100 @@ 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;
 
 /*
- * Whether sessiond is ready for commands/health check requests.
- * NR_LTTNG_SESSIOND_READY must match the number of calls to
- * sessiond_notify_ready().
+ * 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_READY                3
-int lttng_sessiond_ready = NR_LTTNG_SESSIOND_READY;
+#define NR_LTTNG_SESSIOND_SUPPORT_THREADS 4
+int lttng_sessiond_ready = NR_LTTNG_SESSIOND_SUPPORT_THREADS;
 
-/* Notify parents that we are ready for cmd and health check */
-LTTNG_HIDDEN
-void sessiond_notify_ready(void)
+int sessiond_check_thread_quit_pipe(int fd, uint32_t events)
 {
-       if (uatomic_sub_return(&lttng_sessiond_ready, 1) == 0) {
-               /*
-                * 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 (opt_sig_parent) {
-                       kill(ppid, SIGUSR1);
-               }
-
-               /*
-                * Notify the parent of the fork() process that we are
-                * ready.
-                */
-               if (opt_daemon || opt_background) {
-                       kill(child_ppid, SIGUSR1);
-               }
-       }
+       return (fd == thread_quit_pipe[0] && (events & LPOLLIN)) ? 1 : 0;
 }
 
-static
-void setup_consumerd_path(void)
+/* Notify parents that we are ready for cmd and health check */
+LTTNG_HIDDEN
+void sessiond_signal_parents(void)
 {
-       const char *bin, *libdir;
-
        /*
-        * Allow INSTALL_BIN_PATH to be used as a target path for the
-        * native architecture size consumer if CONFIG_CONSUMER*_PATH
-        * has not been defined.
+        * 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 (CAA_BITS_PER_LONG == 32)
-       if (!consumerd32_bin[0]) {
-               consumerd32_bin = INSTALL_BIN_PATH "/" CONSUMERD_FILE;
-       }
-       if (!consumerd32_libdir[0]) {
-               consumerd32_libdir = INSTALL_LIB_PATH;
-       }
-#elif (CAA_BITS_PER_LONG == 64)
-       if (!consumerd64_bin[0]) {
-               consumerd64_bin = INSTALL_BIN_PATH "/" CONSUMERD_FILE;
+       if (config.sig_parent) {
+               kill(ppid, SIGUSR1);
        }
-       if (!consumerd64_libdir[0]) {
-               consumerd64_libdir = INSTALL_LIB_PATH;
-       }
-#else
-#error "Unknown bitness"
-#endif
 
        /*
-        * runtime env. var. overrides the build default.
+        * Notify the parent of the fork() process that we are
+        * ready.
         */
-       bin = lttng_secure_getenv("LTTNG_CONSUMERD32_BIN");
-       if (bin) {
-               consumerd32_bin = bin;
-       }
-       bin = lttng_secure_getenv("LTTNG_CONSUMERD64_BIN");
-       if (bin) {
-               consumerd64_bin = bin;
-       }
-       libdir = lttng_secure_getenv("LTTNG_CONSUMERD32_LIBDIR");
-       if (libdir) {
-               consumerd32_libdir = libdir;
-       }
-       libdir = lttng_secure_getenv("LTTNG_CONSUMERD64_LIBDIR");
-       if (libdir) {
-               consumerd64_libdir = libdir;
+       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(&lttng_sessiond_ready, 1);
+}
+
 static
 int __sessiond_set_thread_pollset(struct lttng_poll_event *events, size_t size,
                int *a_pipe)
@@ -423,47 +418,6 @@ int sessiond_set_thread_pollset(struct lttng_poll_event *events, size_t size)
        return __sessiond_set_thread_pollset(events, size, thread_quit_pipe);
 }
 
-/*
- * Create a poll set with O_CLOEXEC and add the thread quit pipe to the set.
- */
-int sessiond_set_ht_cleanup_thread_pollset(struct lttng_poll_event *events,
-               size_t size)
-{
-       return __sessiond_set_thread_pollset(events, size,
-                       ht_cleanup_quit_pipe);
-}
-
-static
-int __sessiond_check_thread_quit_pipe(int fd, uint32_t events, int a_pipe)
-{
-       if (fd == a_pipe && (events & LPOLLIN)) {
-               return 1;
-       }
-       return 0;
-}
-
-/*
- * Check if the thread quit pipe was triggered.
- *
- * Return 1 if it was triggered else 0;
- */
-int sessiond_check_thread_quit_pipe(int fd, uint32_t events)
-{
-       return __sessiond_check_thread_quit_pipe(fd, events,
-                       thread_quit_pipe[0]);
-}
-
-/*
- * Check if the ht_cleanup thread quit pipe was triggered.
- *
- * Return 1 if it was triggered else 0;
- */
-int sessiond_check_ht_cleanup_quit(int fd, uint32_t events)
-{
-       return __sessiond_check_thread_quit_pipe(fd, events,
-                       ht_cleanup_quit_pipe[0]);
-}
-
 /*
  * Init thread quit pipe.
  *
@@ -496,11 +450,6 @@ static int init_thread_quit_pipe(void)
        return __init_thread_quit_pipe(thread_quit_pipe);
 }
 
-static int init_ht_cleanup_quit_pipe(void)
-{
-       return __init_thread_quit_pipe(ht_cleanup_quit_pipe);
-}
-
 /*
  * Stop all threads by closing the thread quit pipe.
  */
@@ -563,27 +512,42 @@ static void close_consumer_sockets(void)
                        PERROR("UST consumerd64 cmd_sock close");
                }
        }
-}
-
-/*
- * Generate the full lock file path using the rundir.
- *
- * Return the snprintf() return value thus a negative value is an error.
- */
-static int generate_lock_file_path(char *path, size_t len)
-{
-       int ret;
-
-       assert(path);
-       assert(rundir);
-
-       /* Build lockfile path from rundir. */
-       ret = snprintf(path, len, "%s/" DEFAULT_LTTNG_SESSIOND_LOCKFILE, rundir);
-       if (ret < 0) {
-               PERROR("snprintf lockfile path");
+       if (kconsumer_data.channel_monitor_pipe >= 0) {
+               ret = close(kconsumer_data.channel_monitor_pipe);
+               if (ret < 0) {
+                       PERROR("kernel consumer channel monitor pipe close");
+               }
+       }
+       if (ustconsumer32_data.channel_monitor_pipe >= 0) {
+               ret = close(ustconsumer32_data.channel_monitor_pipe);
+               if (ret < 0) {
+                       PERROR("UST consumerd32 channel monitor pipe close");
+               }
+       }
+       if (ustconsumer64_data.channel_monitor_pipe >= 0) {
+               ret = close(ustconsumer64_data.channel_monitor_pipe);
+               if (ret < 0) {
+                       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");
+               }
        }
-
-       return ret;
 }
 
 /*
@@ -606,8 +570,7 @@ static void wait_consumer(struct consumer_data *consumer_data)
        ret = waitpid(consumer_data->pid, &status, 0);
        if (ret == -1) {
                PERROR("consumerd waitpid pid: %d", consumer_data->pid)
-       }
-       if (!WIFEXITED(status)) {
+       } else  if (!WIFEXITED(status)) {
                ERR("consumerd termination with error: %d",
                                WEXITSTATUS(ret));
        }
@@ -621,7 +584,6 @@ static void sessiond_cleanup(void)
 {
        int ret;
        struct ltt_session *sess, *stmp;
-       char path[PATH_MAX];
 
        DBG("Cleanup sessiond");
 
@@ -631,69 +593,41 @@ static void sessiond_cleanup(void)
         */
        utils_close_pipe(thread_quit_pipe);
 
-       /*
-        * If opt_pidfile is undefined, the default file will be wiped when
-        * removing the rundir.
-        */
-       if (opt_pidfile) {
-               ret = remove(opt_pidfile);
-               if (ret < 0) {
-                       PERROR("remove pidfile %s", opt_pidfile);
-               }
+       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", rundir);
+       DBG("Removing sessiond and consumerd content of directory %s",
+               config.rundir.value);
 
        /* sessiond */
-       snprintf(path, PATH_MAX,
-               "%s/%s",
-               rundir, DEFAULT_LTTNG_SESSIOND_PIDFILE);
-       DBG("Removing %s", path);
-       (void) unlink(path);
+       DBG("Removing %s", config.pid_file_path.value);
+       (void) unlink(config.pid_file_path.value);
 
-       snprintf(path, PATH_MAX, "%s/%s", rundir,
-                       DEFAULT_LTTNG_SESSIOND_AGENTPORT_FILE);
-       DBG("Removing %s", path);
-       (void) unlink(path);
+       DBG("Removing %s", config.agent_port_file_path.value);
+       (void) unlink(config.agent_port_file_path.value);
 
        /* kconsumerd */
-       snprintf(path, PATH_MAX,
-               DEFAULT_KCONSUMERD_ERR_SOCK_PATH,
-               rundir);
-       DBG("Removing %s", path);
-       (void) unlink(path);
-
-       snprintf(path, PATH_MAX,
-               DEFAULT_KCONSUMERD_PATH,
-               rundir);
-       DBG("Removing directory %s", path);
-       (void) rmdir(path);
+       DBG("Removing %s", kconsumer_data.err_unix_sock_path);
+       (void) unlink(kconsumer_data.err_unix_sock_path);
+
+       DBG("Removing directory %s", config.kconsumerd_path.value);
+       (void) rmdir(config.kconsumerd_path.value);
 
        /* ust consumerd 32 */
-       snprintf(path, PATH_MAX,
-               DEFAULT_USTCONSUMERD32_ERR_SOCK_PATH,
-               rundir);
-       DBG("Removing %s", path);
-       (void) unlink(path);
-
-       snprintf(path, PATH_MAX,
-               DEFAULT_USTCONSUMERD32_PATH,
-               rundir);
-       DBG("Removing directory %s", path);
-       (void) rmdir(path);
+       DBG("Removing %s", config.consumerd32_err_unix_sock_path.value);
+       (void) unlink(config.consumerd32_err_unix_sock_path.value);
+
+       DBG("Removing directory %s", config.consumerd32_path.value);
+       (void) rmdir(config.consumerd32_path.value);
 
        /* ust consumerd 64 */
-       snprintf(path, PATH_MAX,
-               DEFAULT_USTCONSUMERD64_ERR_SOCK_PATH,
-               rundir);
-       DBG("Removing %s", path);
-       (void) unlink(path);
-
-       snprintf(path, PATH_MAX,
-               DEFAULT_USTCONSUMERD64_PATH,
-               rundir);
-       DBG("Removing directory %s", path);
-       (void) rmdir(path);
+       DBG("Removing %s", config.consumerd64_err_unix_sock_path.value);
+       (void) unlink(config.consumerd64_err_unix_sock_path.value);
+
+       DBG("Removing directory %s", config.consumerd64_path.value);
+       (void) rmdir(config.consumerd64_path.value);
 
        DBG("Cleaning up all sessions");
 
@@ -704,7 +638,8 @@ static void sessiond_cleanup(void)
                /* Cleanup ALL session */
                cds_list_for_each_entry_safe(sess, stmp,
                                &session_list_ptr->head, list) {
-                       cmd_destroy_session(sess, kernel_poll_pipe[1]);
+                       cmd_destroy_session(sess, kernel_poll_pipe[1],
+                                       notification_thread_handle);
                }
        }
 
@@ -719,7 +654,7 @@ static void sessiond_cleanup(void)
        ust_app_clean_list();
        buffer_reg_destroy_registries();
 
-       if (is_root && !opt_no_kernel) {
+       if (is_root && !config.no_kernel) {
                DBG2("Closing kernel fd");
                if (kernel_tracer_fd >= 0) {
                        ret = close(kernel_tracer_fd);
@@ -739,34 +674,11 @@ static void sessiond_cleanup(void)
                free(load_info);
        }
 
-       /*
-        * Cleanup lock file by deleting it and finaly closing it which will
-        * release the file system lock.
-        */
-       if (lockfile_fd >= 0) {
-               char lockfile_path[PATH_MAX];
-
-               ret = generate_lock_file_path(lockfile_path,
-                               sizeof(lockfile_path));
-               if (ret > 0) {
-                       ret = remove(lockfile_path);
-                       if (ret < 0) {
-                               PERROR("remove lock file");
-                       }
-                       ret = close(lockfile_fd);
-                       if (ret < 0) {
-                               PERROR("close lock file");
-                       }
-               }
-       }
-
        /*
         * We do NOT rmdir rundir because there are other processes
         * using it, for instance lttng-relayd, which can start in
         * parallel with this teardown.
         */
-
-       free(rundir);
 }
 
 /*
@@ -776,36 +688,9 @@ static void sessiond_cleanup_options(void)
 {
        DBG("Cleaning up options");
 
-       /*
-        * If the override option is set, the pointer points to a *non* const
-        * thus freeing it even though the variable type is set to const.
-        */
-       if (tracing_group_name_override) {
-               free((void *) tracing_group_name);
-       }
-       if (consumerd32_bin_override) {
-               free((void *) consumerd32_bin);
-       }
-       if (consumerd64_bin_override) {
-               free((void *) consumerd64_bin);
-       }
-       if (consumerd32_libdir_override) {
-               free((void *) consumerd32_libdir);
-       }
-       if (consumerd64_libdir_override) {
-               free((void *) consumerd64_libdir);
-       }
-
-       free(opt_pidfile);
-       free(opt_load_session_path);
-       free(kmod_probes_list);
-       free(kmod_extra_probes_list);
+       sessiond_config_fini(&config);
 
-       /* <fun> */
-       DBG("%c[%d;%dm*** assert failed :-) *** ==> %c[%dm%c[%d;%dm"
-                       "Matthew, BEET driven development works!%c[%dm",
-                       27, 1, 31, 27, 0, 27, 1, 33, 27, 0);
-       /* </fun> */
+       run_as_destroy_worker();
 }
 
 /*
@@ -851,7 +736,7 @@ static int notify_ust_apps(int active)
        DBG("Notifying applications of session daemon state: %d", active);
 
        /* See shm.c for this call implying mmap, shm and futex calls */
-       wait_shm_mmap = shm_ust_get_mmap(wait_shm_path, is_root);
+       wait_shm_mmap = shm_ust_get_mmap(config.wait_shm_path.value, is_root);
        if (wait_shm_mmap == NULL) {
                goto error;
        }
@@ -871,34 +756,57 @@ error:
  * right amount of memory and copying the original information from the lsm
  * structure.
  *
- * Return total size of the buffer pointed by buf.
+ * Return 0 on success, negative value on error.
  */
-static int setup_lttng_msg(struct command_ctx *cmd_ctx, size_t size)
+static int setup_lttng_msg(struct command_ctx *cmd_ctx,
+       const void *payload_buf, size_t payload_len,
+       const void *cmd_header_buf, size_t cmd_header_len)
 {
-       int ret, buf_size;
+       int ret = 0;
+       const size_t header_len = sizeof(struct lttcomm_lttng_msg);
+       const size_t cmd_header_offset = header_len;
+       const size_t payload_offset = cmd_header_offset + cmd_header_len;
+       const size_t total_msg_size = header_len + cmd_header_len + payload_len;
 
-       buf_size = size;
+       cmd_ctx->llm = zmalloc(total_msg_size);
 
-       cmd_ctx->llm = zmalloc(sizeof(struct lttcomm_lttng_msg) + buf_size);
        if (cmd_ctx->llm == NULL) {
                PERROR("zmalloc");
                ret = -ENOMEM;
-               goto error;
+               goto end;
        }
 
        /* Copy common data */
        cmd_ctx->llm->cmd_type = cmd_ctx->lsm->cmd_type;
        cmd_ctx->llm->pid = cmd_ctx->lsm->domain.attr.pid;
+       cmd_ctx->llm->cmd_header_size = cmd_header_len;
+       cmd_ctx->llm->data_size = payload_len;
+       cmd_ctx->lttng_msg_size = total_msg_size;
 
-       cmd_ctx->llm->data_size = size;
-       cmd_ctx->lttng_msg_size = sizeof(struct lttcomm_lttng_msg) + buf_size;
+       /* Copy command header */
+       if (cmd_header_len) {
+               memcpy(((uint8_t *) cmd_ctx->llm) + cmd_header_offset, cmd_header_buf,
+                       cmd_header_len);
+       }
 
-       return buf_size;
+       /* Copy payload */
+       if (payload_len) {
+               memcpy(((uint8_t *) cmd_ctx->llm) + payload_offset, payload_buf,
+                       payload_len);
+       }
 
-error:
+end:
        return ret;
 }
 
+/*
+ * Version of setup_lttng_msg() without command header.
+ */
+static int setup_lttng_msg_no_cmd_header(struct command_ctx *cmd_ctx,
+       void *payload_buf, size_t payload_len)
+{
+       return setup_lttng_msg(cmd_ctx, payload_buf, payload_len, NULL, 0);
+}
 /*
  * Update the kernel poll set of all channel fd available over all tracing
  * session. Add the wakeup pipe at the end of the set.
@@ -946,7 +854,7 @@ 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;
@@ -995,7 +903,7 @@ static int update_kernel_stream(struct consumer_data *consumer_data, int fd)
                        cds_lfht_for_each_entry(ksess->consumer->socks->ht,
                                        &iter.iter, socket, node.node) {
                                pthread_mutex_lock(socket->lock);
-                               ret = kernel_consumer_send_channel_stream(socket,
+                               ret = kernel_consumer_send_channel_streams(socket,
                                                channel, ksess,
                                                session->output_traces ? 1 : 0);
                                pthread_mutex_unlock(socket->lock);
@@ -1180,7 +1088,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;
                                        }
@@ -1247,6 +1155,7 @@ static void *thread_manage_consumer(void *data)
        enum lttcomm_return_code code;
        struct lttng_poll_event events;
        struct consumer_data *consumer_data = data;
+       struct consumer_socket *cmd_socket_wrapper = NULL;
 
        DBG("[thread] Manage consumer started");
 
@@ -1356,40 +1265,43 @@ restart:
        }
 
        health_code_update();
-       if (code == LTTCOMM_CONSUMERD_COMMAND_SOCK_READY) {
-               /* Connect both socket, command and metadata. */
-               consumer_data->cmd_sock =
-                       lttcomm_connect_unix_sock(consumer_data->cmd_unix_sock_path);
-               consumer_data->metadata_fd =
-                       lttcomm_connect_unix_sock(consumer_data->cmd_unix_sock_path);
-               if (consumer_data->cmd_sock < 0
-                               || consumer_data->metadata_fd < 0) {
-                       PERROR("consumer connect cmd socket");
-                       /* On error, signal condition and quit. */
-                       signal_consumer_condition(consumer_data, -1);
-                       goto error;
-               }
-               consumer_data->metadata_sock.fd_ptr = &consumer_data->metadata_fd;
-               /* Create metadata socket lock. */
-               consumer_data->metadata_sock.lock = zmalloc(sizeof(pthread_mutex_t));
-               if (consumer_data->metadata_sock.lock == NULL) {
-                       PERROR("zmalloc pthread mutex");
-                       ret = -1;
-                       goto error;
-               }
-               pthread_mutex_init(consumer_data->metadata_sock.lock, NULL);
-
-               signal_consumer_condition(consumer_data, 1);
-               DBG("Consumer command socket ready (fd: %d", consumer_data->cmd_sock);
-               DBG("Consumer metadata socket ready (fd: %d)",
-                               consumer_data->metadata_fd);
-       } else {
+       if (code != LTTCOMM_CONSUMERD_COMMAND_SOCK_READY) {
                ERR("consumer error when waiting for SOCK_READY : %s",
                                lttcomm_get_readable_code(-code));
                goto error;
        }
 
-       /* Remove the consumerd error sock since we've established a connexion */
+       /* Connect both command and metadata sockets. */
+       consumer_data->cmd_sock =
+                       lttcomm_connect_unix_sock(
+                               consumer_data->cmd_unix_sock_path);
+       consumer_data->metadata_fd =
+                       lttcomm_connect_unix_sock(
+                               consumer_data->cmd_unix_sock_path);
+       if (consumer_data->cmd_sock < 0 || consumer_data->metadata_fd < 0) {
+               PERROR("consumer connect cmd socket");
+               /* On error, signal condition and quit. */
+               signal_consumer_condition(consumer_data, -1);
+               goto error;
+       }
+
+       consumer_data->metadata_sock.fd_ptr = &consumer_data->metadata_fd;
+
+       /* Create metadata socket lock. */
+       consumer_data->metadata_sock.lock = zmalloc(sizeof(pthread_mutex_t));
+       if (consumer_data->metadata_sock.lock == NULL) {
+               PERROR("zmalloc pthread mutex");
+               goto error;
+       }
+       pthread_mutex_init(consumer_data->metadata_sock.lock, NULL);
+
+       DBG("Consumer command socket ready (fd: %d", consumer_data->cmd_sock);
+       DBG("Consumer metadata socket ready (fd: %d)",
+                       consumer_data->metadata_fd);
+
+       /*
+        * Remove the consumerd error sock since we've established a connection.
+        */
        ret = lttng_poll_del(&events, consumer_data->err_sock);
        if (ret < 0) {
                goto error;
@@ -1410,6 +1322,36 @@ restart:
 
        health_code_update();
 
+       /*
+        * 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.
+        */
+       cmd_socket_wrapper = consumer_allocate_socket(&consumer_data->cmd_sock);
+       if (!cmd_socket_wrapper) {
+               goto error;
+       }
+       cmd_socket_wrapper->lock = &consumer_data->lock;
+
+       ret = consumer_send_channel_monitor_pipe(cmd_socket_wrapper,
+                       consumer_data->channel_monitor_pipe);
+       if (ret) {
+               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;
+
+       /* The thread is completely initialized, signal that it is ready. */
+       signal_consumer_condition(consumer_data, 1);
+
        /* Infinite blocking call, waiting for transmission */
 restart_poll:
        while (1) {
@@ -1553,6 +1495,10 @@ error:
                free(consumer_data->metadata_sock.lock);
        }
        lttng_poll_clean(&events);
+
+       if (cmd_socket_wrapper) {
+               consumer_destroy_socket(cmd_socket_wrapper);
+       }
 error_poll:
        if (err) {
                health_error();
@@ -1568,7 +1514,17 @@ error_poll:
 }
 
 /*
- * This thread manage application communication.
+ * This thread receives application command sockets (FDs) on the
+ * apps_cmd_pipe and waits (polls) on them until they are closed
+ * or an error occurs.
+ *
+ * At that point, it flushes the data (tracing and metadata) associated
+ * with this application and tears down ust app sessions and other
+ * associated data structures through ust_app_unregister().
+ *
+ * Note that this thread never sends commands to the applications
+ * through the command sockets; it merely listens for hang-ups
+ * and errors on those sockets and cleans-up as they occur.
  */
 static void *thread_manage_apps(void *data)
 {
@@ -1879,6 +1835,8 @@ static void *thread_dispatch_ust_registration(void *data)
                .count = 0,
        };
 
+       rcu_register_thread();
+
        health_register(health_sessiond, HEALTH_SESSIOND_TYPE_APP_REG_DISPATCH);
 
        if (testpoint(sessiond_thread_app_reg_dispatch)) {
@@ -1891,12 +1849,16 @@ static void *thread_dispatch_ust_registration(void *data)
 
        DBG("[thread] Dispatch UST command started");
 
-       while (!CMM_LOAD_SHARED(dispatch_thread_exit)) {
+       for (;;) {
                health_code_update();
 
                /* Atomically prepare the queue futex */
                futex_nto1_prepare(&ust_cmd_queue.futex);
 
+               if (CMM_LOAD_SHARED(dispatch_thread_exit)) {
+                       break;
+               }
+
                do {
                        struct ust_app *app = NULL;
                        ust_cmd = NULL;
@@ -2112,6 +2074,7 @@ error_testpoint:
                ERR("Health error occurred in %s", __func__);
        }
        health_unregister(health_sessiond);
+       rcu_unregister_thread();
        return NULL;
 }
 
@@ -2219,10 +2182,12 @@ static void *thread_registration_apps(void *data)
                                         * lttcomm_setsockopt_snd_timeout expect msec as
                                         * parameter.
                                         */
-                                       (void) lttcomm_setsockopt_rcv_timeout(sock,
-                                                       app_socket_timeout * 1000);
-                                       (void) lttcomm_setsockopt_snd_timeout(sock,
-                                                       app_socket_timeout * 1000);
+                                       if (config.app_socket_timeout >= 0) {
+                                               (void) lttcomm_setsockopt_rcv_timeout(sock,
+                                                               config.app_socket_timeout * 1000);
+                                               (void) lttcomm_setsockopt_snd_timeout(sock,
+                                                               config.app_socket_timeout * 1000);
+                                       }
 
                                        /*
                                         * Set the CLOEXEC flag. Return code is useless because
@@ -2322,7 +2287,7 @@ error:
                }
                lttng_fd_put(LTTNG_FD_APPS, 1);
        }
-       unlink(apps_unix_sock_path);
+       unlink(config.apps_unix_sock_path.value);
 
 error_poll_add:
        lttng_poll_clean(&events);
@@ -2348,7 +2313,12 @@ static int spawn_consumer_thread(struct consumer_data *consumer_data)
        int ret, clock_ret;
        struct timespec timeout;
 
-       /* Make sure we set the readiness flag to 0 because we are NOT ready */
+       /*
+        * Make sure we set the readiness flag to 0 because we are NOT ready.
+        * This access to consumer_thread_is_ready does not need to be
+        * protected by consumer_data.cond_mutex (yet) since the consumer
+        * management thread has not been started at this point.
+        */
        consumer_data->consumer_thread_is_ready = 0;
 
        /* Setup pthread condition */
@@ -2378,8 +2348,8 @@ static int spawn_consumer_thread(struct consumer_data *consumer_data)
                goto error;
        }
 
-       ret = pthread_create(&consumer_data->thread, NULL, thread_manage_consumer,
-                       consumer_data);
+       ret = pthread_create(&consumer_data->thread, default_pthread_attr(),
+                       thread_manage_consumer, consumer_data);
        if (ret) {
                errno = ret;
                PERROR("pthread_create consumer");
@@ -2391,7 +2361,7 @@ static int spawn_consumer_thread(struct consumer_data *consumer_data)
        pthread_mutex_lock(&consumer_data->cond_mutex);
 
        /* Get time for sem_timedwait absolute timeout */
-       clock_ret = clock_gettime(CLOCK_MONOTONIC, &timeout);
+       clock_ret = lttng_clock_gettime(CLOCK_MONOTONIC, &timeout);
        /*
         * Set the timeout for the condition timed wait even if the clock gettime
         * call fails since we might loop on that call and we want to avoid to
@@ -2506,7 +2476,7 @@ static pid_t spawn_consumerd(struct consumer_data *consumer_data)
                /*
                 * Exec consumerd.
                 */
-               if (opt_verbose_consumer) {
+               if (config.verbose_consumer) {
                        verbosity = "--verbose";
                } else if (lttng_opt_quiet) {
                        verbosity = "--quiet";
@@ -2522,120 +2492,108 @@ static pid_t spawn_consumerd(struct consumer_data *consumer_data)
                         * fallback on the 32-bit one,
                         */
                        DBG3("Looking for a kernel consumer at these locations:");
-                       DBG3("  1) %s", consumerd64_bin);
-                       DBG3("  2) %s/%s", INSTALL_BIN_PATH, CONSUMERD_FILE);
-                       DBG3("  3) %s", consumerd32_bin);
-                       if (stat(consumerd64_bin, &st) == 0) {
+                       DBG3("  1) %s", config.consumerd64_bin_path.value ? : "NULL");
+                       DBG3("  2) %s/%s", INSTALL_BIN_PATH, DEFAULT_CONSUMERD_FILE);
+                       DBG3("  3) %s", config.consumerd32_bin_path.value ? : "NULL");
+                       if (stat(config.consumerd64_bin_path.value, &st) == 0) {
                                DBG3("Found location #1");
-                               consumer_to_use = consumerd64_bin;
-                       } else if (stat(INSTALL_BIN_PATH "/" CONSUMERD_FILE, &st) == 0) {
+                               consumer_to_use = config.consumerd64_bin_path.value;
+                       } else if (stat(INSTALL_BIN_PATH "/" DEFAULT_CONSUMERD_FILE, &st) == 0) {
                                DBG3("Found location #2");
-                               consumer_to_use = INSTALL_BIN_PATH "/" CONSUMERD_FILE;
-                       } else if (stat(consumerd32_bin, &st) == 0) {
+                               consumer_to_use = INSTALL_BIN_PATH "/" DEFAULT_CONSUMERD_FILE;
+                       } else if (stat(config.consumerd32_bin_path.value, &st) == 0) {
                                DBG3("Found location #3");
-                               consumer_to_use = consumerd32_bin;
+                               consumer_to_use = config.consumerd32_bin_path.value;
                        } else {
                                DBG("Could not find any valid consumerd executable");
                                ret = -EINVAL;
-                               break;
+                               goto error;
                        }
                        DBG("Using kernel consumer at: %s",  consumer_to_use);
-                       ret = execl(consumer_to_use,
+                       (void) execl(consumer_to_use,
                                "lttng-consumerd", verbosity, "-k",
                                "--consumerd-cmd-sock", consumer_data->cmd_unix_sock_path,
                                "--consumerd-err-sock", consumer_data->err_unix_sock_path,
-                               "--group", tracing_group_name,
+                               "--group", config.tracing_group_name.value,
                                NULL);
                        break;
                case LTTNG_CONSUMER64_UST:
                {
-                       char *tmpnew = NULL;
-
-                       if (consumerd64_libdir[0] != '\0') {
+                       if (config.consumerd64_lib_dir.value) {
                                char *tmp;
                                size_t tmplen;
+                               char *tmpnew;
 
                                tmp = lttng_secure_getenv("LD_LIBRARY_PATH");
                                if (!tmp) {
                                        tmp = "";
                                }
-                               tmplen = strlen("LD_LIBRARY_PATH=")
-                                       + strlen(consumerd64_libdir) + 1 /* : */ + strlen(tmp);
+                               tmplen = strlen(config.consumerd64_lib_dir.value) + 1 /* : */ + strlen(tmp);
                                tmpnew = zmalloc(tmplen + 1 /* \0 */);
                                if (!tmpnew) {
                                        ret = -ENOMEM;
                                        goto error;
                                }
-                               strcpy(tmpnew, "LD_LIBRARY_PATH=");
-                               strcat(tmpnew, consumerd64_libdir);
+                               strcat(tmpnew, config.consumerd64_lib_dir.value);
                                if (tmp[0] != '\0') {
                                        strcat(tmpnew, ":");
                                        strcat(tmpnew, tmp);
                                }
-                               ret = putenv(tmpnew);
+                               ret = setenv("LD_LIBRARY_PATH", tmpnew, 1);
+                               free(tmpnew);
                                if (ret) {
                                        ret = -errno;
-                                       free(tmpnew);
                                        goto error;
                                }
                        }
-                       DBG("Using 64-bit UST consumer at: %s",  consumerd64_bin);
-                       ret = execl(consumerd64_bin, "lttng-consumerd", verbosity, "-u",
+                       DBG("Using 64-bit UST consumer at: %s",  config.consumerd64_bin_path.value);
+                       (void) execl(config.consumerd64_bin_path.value, "lttng-consumerd", verbosity, "-u",
                                        "--consumerd-cmd-sock", consumer_data->cmd_unix_sock_path,
                                        "--consumerd-err-sock", consumer_data->err_unix_sock_path,
-                                       "--group", tracing_group_name,
+                                       "--group", config.tracing_group_name.value,
                                        NULL);
-                       if (consumerd64_libdir[0] != '\0') {
-                               free(tmpnew);
-                       }
                        break;
                }
                case LTTNG_CONSUMER32_UST:
                {
-                       char *tmpnew = NULL;
-
-                       if (consumerd32_libdir[0] != '\0') {
+                       if (config.consumerd32_lib_dir.value) {
                                char *tmp;
                                size_t tmplen;
+                               char *tmpnew;
 
                                tmp = lttng_secure_getenv("LD_LIBRARY_PATH");
                                if (!tmp) {
                                        tmp = "";
                                }
-                               tmplen = strlen("LD_LIBRARY_PATH=")
-                                       + strlen(consumerd32_libdir) + 1 /* : */ + strlen(tmp);
+                               tmplen = strlen(config.consumerd32_lib_dir.value) + 1 /* : */ + strlen(tmp);
                                tmpnew = zmalloc(tmplen + 1 /* \0 */);
                                if (!tmpnew) {
                                        ret = -ENOMEM;
                                        goto error;
                                }
-                               strcpy(tmpnew, "LD_LIBRARY_PATH=");
-                               strcat(tmpnew, consumerd32_libdir);
+                               strcat(tmpnew, config.consumerd32_lib_dir.value);
                                if (tmp[0] != '\0') {
                                        strcat(tmpnew, ":");
                                        strcat(tmpnew, tmp);
                                }
-                               ret = putenv(tmpnew);
+                               ret = setenv("LD_LIBRARY_PATH", tmpnew, 1);
+                               free(tmpnew);
                                if (ret) {
                                        ret = -errno;
-                                       free(tmpnew);
                                        goto error;
                                }
                        }
-                       DBG("Using 32-bit UST consumer at: %s",  consumerd32_bin);
-                       ret = execl(consumerd32_bin, "lttng-consumerd", verbosity, "-u",
+                       DBG("Using 32-bit UST consumer at: %s",  config.consumerd32_bin_path.value);
+                       (void) execl(config.consumerd32_bin_path.value, "lttng-consumerd", verbosity, "-u",
                                        "--consumerd-cmd-sock", consumer_data->cmd_unix_sock_path,
                                        "--consumerd-err-sock", consumer_data->err_unix_sock_path,
-                                       "--group", tracing_group_name,
+                                       "--group", config.tracing_group_name.value,
                                        NULL);
-                       if (consumerd32_libdir[0] != '\0') {
-                               free(tmpnew);
-                       }
                        break;
                }
                default:
-                       PERROR("unknown consumer type");
-                       exit(EXIT_FAILURE);
+                       ERR("unknown consumer type");
+                       errno = 0;
                }
                if (errno != 0) {
                        PERROR("Consumer execl()");
@@ -2727,12 +2685,12 @@ static int init_kernel_tracer(void)
        kernel_tracer_fd = open(module_proc_lttng, O_RDWR);
        if (kernel_tracer_fd < 0) {
                DBG("Failed to open %s", module_proc_lttng);
-               ret = -1;
                goto error_open;
        }
 
        /* Validate kernel version */
-       ret = kernel_validate_version(kernel_tracer_fd);
+       ret = kernel_validate_version(kernel_tracer_fd, &kernel_tracer_version,
+                       &kernel_tracer_abi_version);
        if (ret < 0) {
                goto error_version;
        }
@@ -2742,6 +2700,18 @@ static int init_kernel_tracer(void)
                goto error_modules;
        }
 
+       ret = kernel_supports_ring_buffer_snapshot_sample_positions(
+                       kernel_tracer_fd);
+       if (ret < 0) {
+               goto error_modules;
+       }
+
+       if (ret < 1) {
+               WARN("Kernel tracer does not support buffer monitoring. "
+                       "The monitoring timer of channels in the kernel domain "
+                       "will be set to 0 (disabled).");
+       }
+
        DBG("Kernel tracer fd %d", kernel_tracer_fd);
        return 0;
 
@@ -2927,20 +2897,6 @@ static int create_kernel_session(struct ltt_session *session)
                goto error;
        }
 
-       /* Create directory(ies) on local filesystem. */
-       if (session->kernel_session->consumer->type == CONSUMER_DST_LOCAL &&
-                       strlen(session->kernel_session->consumer->dst.trace_path) > 0) {
-               ret = run_as_mkdir_recursive(
-                               session->kernel_session->consumer->dst.trace_path,
-                               S_IRWXU | S_IRWXG, session->uid, session->gid);
-               if (ret < 0) {
-                       if (errno != EEXIST) {
-                               ERR("Trace directory creation error");
-                               goto error;
-                       }
-               }
-       }
-
        session->kernel_session->uid = session->uid;
        session->kernel_session->gid = session->gid;
        session->kernel_session->output_traces = session->output_traces;
@@ -2976,35 +2932,157 @@ static unsigned int lttng_sessions_count(uid_t uid, gid_t gid)
        return i;
 }
 
-/*
- * Process the command requested by the lttng client within the command
- * context structure. This function make sure that the return structure (llm)
- * is set and ready for transmission before returning.
- *
- * Return any error encountered or 0 for success.
- *
- * "sock" is only used for special-case var. len data.
- *
- * Should *NOT* be called with RCU read-side lock held.
- */
-static int process_client_msg(struct command_ctx *cmd_ctx, int sock,
-               int *sock_error)
+static int receive_userspace_probe(struct command_ctx *cmd_ctx, int sock,
+               int *sock_error, struct lttng_event *event)
 {
-       int ret = LTTNG_OK;
-       int need_tracing_session = 1;
-       int need_domain;
+       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;
 
-       DBG("Processing client command %d", cmd_ctx->lsm->cmd_type);
+       /*
+        * 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;
+       }
 
-       assert(!rcu_read_ongoing());
+       /*
+        * 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;
+       }
 
-       *sock_error = 0;
+       buffer_view = lttng_buffer_view_from_dynamic_buffer(
+                       &probe_location_buffer, 0, probe_location_buffer.size);
 
-       switch (cmd_ctx->lsm->cmd_type) {
-       case LTTNG_CREATE_SESSION:
-       case LTTNG_CREATE_SESSION_SNAPSHOT:
-       case LTTNG_CREATE_SESSION_LIVE:
-       case LTTNG_DESTROY_SESSION:
+       /*
+        * 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.
+ */
+static int check_rotate_compatible(void)
+{
+       int ret = 1;
+
+       if (kernel_tracer_version.major != 2 || kernel_tracer_version.minor < 11) {
+               DBG("Kernel tracer version is not compatible with the rotation feature");
+               ret = 0;
+       }
+
+       return ret;
+}
+
+/*
+ * Process the command requested by the lttng client within the command
+ * context structure. This function make sure that the return structure (llm)
+ * is set and ready for transmission before returning.
+ *
+ * Return any error encountered or 0 for success.
+ *
+ * "sock" is only used for special-case var. len data.
+ *
+ * Should *NOT* be called with RCU read-side lock held.
+ */
+static int process_client_msg(struct command_ctx *cmd_ctx, int sock,
+               int *sock_error)
+{
+       int ret = LTTNG_OK;
+       int need_tracing_session = 1;
+       int need_domain;
+
+       DBG("Processing client command %d", cmd_ctx->lsm->cmd_type);
+
+       assert(!rcu_read_ongoing());
+
+       *sock_error = 0;
+
+       switch (cmd_ctx->lsm->cmd_type) {
+       case LTTNG_CREATE_SESSION:
+       case LTTNG_CREATE_SESSION_SNAPSHOT:
+       case LTTNG_CREATE_SESSION_LIVE:
+       case LTTNG_DESTROY_SESSION:
        case LTTNG_LIST_SESSIONS:
        case LTTNG_LIST_DOMAINS:
        case LTTNG_START_TRACE:
@@ -3016,13 +3094,22 @@ static int process_client_msg(struct command_ctx *cmd_ctx, int sock,
        case LTTNG_SNAPSHOT_RECORD:
        case LTTNG_SAVE_SESSION:
        case LTTNG_SET_SESSION_SHM_PATH:
+       case LTTNG_REGENERATE_METADATA:
+       case LTTNG_REGENERATE_STATEDUMP:
+       case LTTNG_REGISTER_TRIGGER:
+       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_SESSION_LIST_ROTATION_SCHEDULES:
                need_domain = 0;
                break;
        default:
                need_domain = 1;
        }
 
-       if (opt_no_kernel && need_domain
+       if (config.no_kernel && need_domain
                        && cmd_ctx->lsm->domain.type == LTTNG_DOMAIN_KERNEL) {
                if (!is_root) {
                        ret = LTTNG_ERR_NEED_ROOT_SESSIOND;
@@ -3057,10 +3144,14 @@ static int process_client_msg(struct command_ctx *cmd_ctx, int sock,
        case LTTNG_LIST_EVENTS:
        case LTTNG_LIST_SYSCALLS:
        case LTTNG_LIST_TRACKER_PIDS:
+       case LTTNG_DATA_PENDING:
+       case LTTNG_ROTATE_SESSION:
+       case LTTNG_ROTATION_GET_INFO:
+       case LTTNG_SESSION_LIST_ROTATION_SCHEDULES:
                break;
        default:
                /* Setup lttng message with no payload */
-               ret = setup_lttng_msg(cmd_ctx, 0);
+               ret = setup_lttng_msg_no_cmd_header(cmd_ctx, NULL, 0);
                if (ret < 0) {
                        /* This label does not try to unlock the session */
                        goto init_setup_error;
@@ -3072,12 +3163,13 @@ static int process_client_msg(struct command_ctx *cmd_ctx, int sock,
        case LTTNG_CREATE_SESSION:
        case LTTNG_CREATE_SESSION_SNAPSHOT:
        case LTTNG_CREATE_SESSION_LIVE:
-       case LTTNG_CALIBRATE:
        case LTTNG_LIST_SESSIONS:
        case LTTNG_LIST_TRACEPOINTS:
        case LTTNG_LIST_SYSCALLS:
        case LTTNG_LIST_TRACEPOINT_FIELDS:
        case LTTNG_SAVE_SESSION:
+       case LTTNG_REGISTER_TRIGGER:
+       case LTTNG_UNREGISTER_TRIGGER:
                need_tracing_session = 0;
                break;
        default:
@@ -3226,7 +3318,7 @@ static int process_client_msg(struct command_ctx *cmd_ctx, int sock,
                        /* Start the UST consumer daemons */
                        /* 64-bit */
                        pthread_mutex_lock(&ustconsumer64_data.pid_mutex);
-                       if (consumerd64_bin[0] != '\0' &&
+                       if (config.consumerd64_bin_path.value &&
                                        ustconsumer64_data.pid == 0 &&
                                        cmd_ctx->lsm->cmd_type != LTTNG_REGISTER_CONSUMER) {
                                pthread_mutex_unlock(&ustconsumer64_data.pid_mutex);
@@ -3255,7 +3347,7 @@ static int process_client_msg(struct command_ctx *cmd_ctx, int sock,
 
                        /* 32-bit */
                        pthread_mutex_lock(&ustconsumer32_data.pid_mutex);
-                       if (consumerd32_bin[0] != '\0' &&
+                       if (config.consumerd32_bin_path.value &&
                                        ustconsumer32_data.pid == 0 &&
                                        cmd_ctx->lsm->cmd_type != LTTNG_REGISTER_CONSUMER) {
                                pthread_mutex_unlock(&ustconsumer32_data.pid_mutex);
@@ -3293,6 +3385,8 @@ skip_domain:
        if (cmd_ctx->lsm->cmd_type == LTTNG_START_TRACE ||
                        cmd_ctx->lsm->cmd_type == LTTNG_STOP_TRACE) {
                switch (cmd_ctx->lsm->domain.type) {
+               case LTTNG_DOMAIN_NONE:
+                       break;
                case LTTNG_DOMAIN_JUL:
                case LTTNG_DOMAIN_LOG4J:
                case LTTNG_DOMAIN_PYTHON:
@@ -3308,6 +3402,9 @@ skip_domain:
                                goto error;
                        }
                        break;
+               default:
+                       ret = LTTNG_ERR_UNKNOWN_DOMAIN;
+                       goto error;
                }
        }
 
@@ -3343,9 +3440,74 @@ skip_domain:
        switch (cmd_ctx->lsm->cmd_type) {
        case LTTNG_ADD_CONTEXT:
        {
-               ret = cmd_add_context(cmd_ctx->session, cmd_ctx->lsm->domain.type,
+               /*
+                * An LTTNG_ADD_CONTEXT command might have a supplementary
+                * payload if the context being added is an application context.
+                */
+               if (cmd_ctx->lsm->u.context.ctx.ctx ==
+                               LTTNG_EVENT_CONTEXT_APP_CONTEXT) {
+                       char *provider_name = NULL, *context_name = NULL;
+                       size_t provider_name_len =
+                                       cmd_ctx->lsm->u.context.provider_name_len;
+                       size_t context_name_len =
+                                       cmd_ctx->lsm->u.context.context_name_len;
+
+                       if (provider_name_len == 0 || context_name_len == 0) {
+                               /*
+                                * Application provider and context names MUST
+                                * be provided.
+                                */
+                               ret = -LTTNG_ERR_INVALID;
+                               goto error;
+                       }
+
+                       provider_name = zmalloc(provider_name_len + 1);
+                       if (!provider_name) {
+                               ret = -LTTNG_ERR_NOMEM;
+                               goto error;
+                       }
+                       cmd_ctx->lsm->u.context.ctx.u.app_ctx.provider_name =
+                                       provider_name;
+
+                       context_name = zmalloc(context_name_len + 1);
+                       if (!context_name) {
+                               ret = -LTTNG_ERR_NOMEM;
+                               goto error_add_context;
+                       }
+                       cmd_ctx->lsm->u.context.ctx.u.app_ctx.ctx_name =
+                                       context_name;
+
+                       ret = lttcomm_recv_unix_sock(sock, provider_name,
+                                       provider_name_len);
+                       if (ret < 0) {
+                               goto error_add_context;
+                       }
+
+                       ret = lttcomm_recv_unix_sock(sock, context_name,
+                                       context_name_len);
+                       if (ret < 0) {
+                               goto error_add_context;
+                       }
+               }
+
+               /*
+                * cmd_add_context assumes ownership of the provider and context
+                * names.
+                */
+               ret = cmd_add_context(cmd_ctx->session,
+                               cmd_ctx->lsm->domain.type,
                                cmd_ctx->lsm->u.context.channel_name,
-                               &cmd_ctx->lsm->u.context.ctx, kernel_poll_pipe[1]);
+                               &cmd_ctx->lsm->u.context.ctx,
+                               kernel_poll_pipe[1]);
+
+               cmd_ctx->lsm->u.context.ctx.u.app_ctx.provider_name = NULL;
+               cmd_ctx->lsm->u.context.ctx.u.app_ctx.ctx_name = NULL;
+error_add_context:
+               free(cmd_ctx->lsm->u.context.ctx.u.app_ctx.provider_name);
+               free(cmd_ctx->lsm->u.context.ctx.u.app_ctx.ctx_name);
+               if (ret < 0) {
+                       goto error;
+               }
                break;
        }
        case LTTNG_DISABLE_CHANNEL:
@@ -3391,8 +3553,11 @@ skip_domain:
        }
        case LTTNG_ENABLE_CHANNEL:
        {
+               cmd_ctx->lsm->u.channel.chan.attr.extended.ptr =
+                               (struct lttng_channel_extended *) &cmd_ctx->lsm->u.channel.extended;
                ret = cmd_enable_channel(cmd_ctx->session, &cmd_ctx->lsm->domain,
-                               &cmd_ctx->lsm->u.channel.chan, kernel_poll_pipe[1]);
+                               &cmd_ctx->lsm->u.channel.chan,
+                               kernel_poll_pipe[1]);
                break;
        }
        case LTTNG_TRACK_PID:
@@ -3411,6 +3576,7 @@ skip_domain:
        }
        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;
@@ -3462,7 +3628,7 @@ skip_domain:
                        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);
@@ -3494,7 +3660,7 @@ skip_domain:
                        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);
@@ -3512,11 +3678,30 @@ skip_domain:
                        }
                }
 
+               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);
+                       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) {
+                               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:
@@ -3537,18 +3722,14 @@ skip_domain:
                 * Setup lttng message with payload size set to the event list size in
                 * bytes and then copy list into the llm payload.
                 */
-               ret = setup_lttng_msg(cmd_ctx, sizeof(struct lttng_event) * nb_events);
+               ret = setup_lttng_msg_no_cmd_header(cmd_ctx, events,
+                       sizeof(struct lttng_event) * nb_events);
+               free(events);
+
                if (ret < 0) {
-                       free(events);
                        goto setup_error;
                }
 
-               /* Copy event list into message payload */
-               memcpy(cmd_ctx->llm->payload, events,
-                               sizeof(struct lttng_event) * nb_events);
-
-               free(events);
-
                ret = LTTNG_OK;
                break;
        }
@@ -3571,19 +3752,14 @@ skip_domain:
                 * Setup lttng message with payload size set to the event list size in
                 * bytes and then copy list into the llm payload.
                 */
-               ret = setup_lttng_msg(cmd_ctx,
+               ret = setup_lttng_msg_no_cmd_header(cmd_ctx, fields,
                                sizeof(struct lttng_event_field) * nb_fields);
+               free(fields);
+
                if (ret < 0) {
-                       free(fields);
                        goto setup_error;
                }
 
-               /* Copy event list into message payload */
-               memcpy(cmd_ctx->llm->payload, fields,
-                               sizeof(struct lttng_event_field) * nb_fields);
-
-               free(fields);
-
                ret = LTTNG_OK;
                break;
        }
@@ -3603,18 +3779,14 @@ skip_domain:
                 * Setup lttng message with payload size set to the event list size in
                 * bytes and then copy list into the llm payload.
                 */
-               ret = setup_lttng_msg(cmd_ctx, sizeof(struct lttng_event) * nb_events);
+               ret = setup_lttng_msg_no_cmd_header(cmd_ctx, events,
+                       sizeof(struct lttng_event) * nb_events);
+               free(events);
+
                if (ret < 0) {
-                       free(events);
                        goto setup_error;
                }
 
-               /* Copy event list into message payload */
-               memcpy(cmd_ctx->llm->payload, events,
-                               sizeof(struct lttng_event) * nb_events);
-
-               free(events);
-
                ret = LTTNG_OK;
                break;
        }
@@ -3635,18 +3807,14 @@ skip_domain:
                 * Setup lttng message with payload size set to the event list size in
                 * bytes and then copy list into the llm payload.
                 */
-               ret = setup_lttng_msg(cmd_ctx, sizeof(int32_t) * nr_pids);
+               ret = setup_lttng_msg_no_cmd_header(cmd_ctx, pids,
+                       sizeof(int32_t) * nr_pids);
+               free(pids);
+
                if (ret < 0) {
-                       free(pids);
                        goto setup_error;
                }
 
-               /* Copy event list into message payload */
-               memcpy(cmd_ctx->llm->payload, pids,
-                               sizeof(int) * nr_pids);
-
-               free(pids);
-
                ret = LTTNG_OK;
                break;
        }
@@ -3691,6 +3859,20 @@ skip_domain:
        }
        case LTTNG_START_TRACE:
        {
+               /*
+                * On the first start, if we have a kernel session and we have
+                * enabled time or size-based rotations, we have to make sure
+                * the kernel tracer supports it.
+                */
+               if (!cmd_ctx->session->has_been_started && \
+                               cmd_ctx->session->kernel_session && \
+                               (cmd_ctx->session->rotate_timer_period || \
+                                       cmd_ctx->session->rotate_size) && \
+                               !check_rotate_compatible()) {
+                       DBG("Kernel tracer version is not compatible with the rotation feature");
+                       ret = LTTNG_ERR_ROTATION_WRONG_VERSION;
+                       goto error;
+               }
                ret = cmd_start_trace(cmd_ctx->session);
                break;
        }
@@ -3742,7 +3924,8 @@ skip_domain:
        }
        case LTTNG_DESTROY_SESSION:
        {
-               ret = cmd_destroy_session(cmd_ctx->session, kernel_poll_pipe[1]);
+               ret = cmd_destroy_session(cmd_ctx->session, kernel_poll_pipe[1],
+                               notification_thread_handle);
 
                /* Set session to NULL so we do not unlock it after free. */
                cmd_ctx->session = NULL;
@@ -3760,46 +3943,38 @@ skip_domain:
                        goto error;
                }
 
-               ret = setup_lttng_msg(cmd_ctx, nb_dom * sizeof(struct lttng_domain));
+               ret = setup_lttng_msg_no_cmd_header(cmd_ctx, domains,
+                       nb_dom * sizeof(struct lttng_domain));
+               free(domains);
+
                if (ret < 0) {
-                       free(domains);
                        goto setup_error;
                }
 
-               /* Copy event list into message payload */
-               memcpy(cmd_ctx->llm->payload, domains,
-                               nb_dom * sizeof(struct lttng_domain));
-
-               free(domains);
-
                ret = LTTNG_OK;
                break;
        }
        case LTTNG_LIST_CHANNELS:
        {
-               int nb_chan;
+               ssize_t payload_size;
                struct lttng_channel *channels = NULL;
 
-               nb_chan = cmd_list_channels(cmd_ctx->lsm->domain.type,
+               payload_size = cmd_list_channels(cmd_ctx->lsm->domain.type,
                                cmd_ctx->session, &channels);
-               if (nb_chan < 0) {
+               if (payload_size < 0) {
                        /* Return value is a negative lttng_error_code. */
-                       ret = -nb_chan;
+                       ret = -payload_size;
                        goto error;
                }
 
-               ret = setup_lttng_msg(cmd_ctx, nb_chan * sizeof(struct lttng_channel));
+               ret = setup_lttng_msg_no_cmd_header(cmd_ctx, channels,
+                       payload_size);
+               free(channels);
+
                if (ret < 0) {
-                       free(channels);
                        goto setup_error;
                }
 
-               /* Copy event list into message payload */
-               memcpy(cmd_ctx->llm->payload, channels,
-                               nb_chan * sizeof(struct lttng_channel));
-
-               free(channels);
-
                ret = LTTNG_OK;
                break;
        }
@@ -3807,61 +3982,68 @@ skip_domain:
        {
                ssize_t nb_event;
                struct lttng_event *events = NULL;
+               struct lttcomm_event_command_header cmd_header;
+               size_t total_size;
+
+               memset(&cmd_header, 0, sizeof(cmd_header));
+               /* Extended infos are included at the end of events */
+               nb_event = cmd_list_events(cmd_ctx->lsm->domain.type,
+                       cmd_ctx->session, cmd_ctx->lsm->u.list.channel_name,
+                       &events, &total_size);
 
-               nb_event = cmd_list_events(cmd_ctx->lsm->domain.type, cmd_ctx->session,
-                               cmd_ctx->lsm->u.list.channel_name, &events);
                if (nb_event < 0) {
                        /* Return value is a negative lttng_error_code. */
                        ret = -nb_event;
                        goto error;
                }
 
-               ret = setup_lttng_msg(cmd_ctx, nb_event * sizeof(struct lttng_event));
+               cmd_header.nb_events = nb_event;
+               ret = setup_lttng_msg(cmd_ctx, events, total_size,
+                       &cmd_header, sizeof(cmd_header));
+               free(events);
+
                if (ret < 0) {
-                       free(events);
                        goto setup_error;
                }
 
-               /* Copy event list into message payload */
-               memcpy(cmd_ctx->llm->payload, events,
-                               nb_event * sizeof(struct lttng_event));
-
-               free(events);
-
                ret = LTTNG_OK;
                break;
        }
        case LTTNG_LIST_SESSIONS:
        {
                unsigned int nr_sessions;
+               void *sessions_payload;
+               size_t payload_len;
 
                session_lock_list();
                nr_sessions = lttng_sessions_count(
                                LTTNG_SOCK_GET_UID_CRED(&cmd_ctx->creds),
                                LTTNG_SOCK_GET_GID_CRED(&cmd_ctx->creds));
+               payload_len = sizeof(struct lttng_session) * nr_sessions;
+               sessions_payload = zmalloc(payload_len);
 
-               ret = setup_lttng_msg(cmd_ctx, sizeof(struct lttng_session) * nr_sessions);
-               if (ret < 0) {
+               if (!sessions_payload) {
                        session_unlock_list();
+                       ret = -ENOMEM;
                        goto setup_error;
                }
 
-               /* Filled the session array */
-               cmd_list_lttng_sessions((struct lttng_session *)(cmd_ctx->llm->payload),
+               cmd_list_lttng_sessions(sessions_payload,
                        LTTNG_SOCK_GET_UID_CRED(&cmd_ctx->creds),
                        LTTNG_SOCK_GET_GID_CRED(&cmd_ctx->creds));
-
                session_unlock_list();
 
+               ret = setup_lttng_msg_no_cmd_header(cmd_ctx, sessions_payload,
+                       payload_len);
+               free(sessions_payload);
+
+               if (ret < 0) {
+                       goto setup_error;
+               }
+
                ret = LTTNG_OK;
                break;
        }
-       case LTTNG_CALIBRATE:
-       {
-               ret = cmd_calibrate(cmd_ctx->lsm->domain.type,
-                               &cmd_ctx->lsm->u.calibrate);
-               break;
-       }
        case LTTNG_REGISTER_CONSUMER:
        {
                struct consumer_data *cdata;
@@ -3882,14 +4064,10 @@ skip_domain:
        case LTTNG_DATA_PENDING:
        {
                int pending_ret;
-
-               /* 1 byte to return whether or not data is pending */
-               ret = setup_lttng_msg(cmd_ctx, 1);
-               if (ret < 0) {
-                       goto setup_error;
-               }
+               uint8_t pending_ret_byte;
 
                pending_ret = cmd_data_pending(cmd_ctx->session);
+
                /*
                 * FIXME
                 *
@@ -3900,7 +4078,10 @@ skip_domain:
                 * "unknown" error.
                 */
                if (pending_ret == 0 || pending_ret == 1) {
-                       ret = LTTNG_OK;
+                       /*
+                        * ret will be set to LTTNG_OK at the end of
+                        * this function.
+                        */
                } else if (pending_ret < 0) {
                        ret = LTTNG_ERR_UNK;
                        goto setup_error;
@@ -3909,7 +4090,17 @@ skip_domain:
                        goto setup_error;
                }
 
-               *cmd_ctx->llm->payload = (uint8_t) pending_ret;
+               pending_ret_byte = (uint8_t) pending_ret;
+
+               /* 1 byte to return whether or not data is pending */
+               ret = setup_lttng_msg_no_cmd_header(cmd_ctx,
+                       &pending_ret_byte, 1);
+
+               if (ret < 0) {
+                       goto setup_error;
+               }
+
+               ret = LTTNG_OK;
                break;
        }
        case LTTNG_SNAPSHOT_ADD_OUTPUT:
@@ -3922,13 +4113,13 @@ skip_domain:
                        goto error;
                }
 
-               ret = setup_lttng_msg(cmd_ctx, sizeof(reply));
+               ret = setup_lttng_msg_no_cmd_header(cmd_ctx, &reply,
+                       sizeof(reply));
                if (ret < 0) {
                        goto setup_error;
                }
 
                /* Copy output list into message payload */
-               memcpy(cmd_ctx->llm->payload, &reply, sizeof(reply));
                ret = LTTNG_OK;
                break;
        }
@@ -3949,20 +4140,15 @@ skip_domain:
                        goto error;
                }
 
-               ret = setup_lttng_msg(cmd_ctx,
+               assert((nb_output > 0 && outputs) || nb_output == 0);
+               ret = setup_lttng_msg_no_cmd_header(cmd_ctx, outputs,
                                nb_output * sizeof(struct lttng_snapshot_output));
+               free(outputs);
+
                if (ret < 0) {
-                       free(outputs);
                        goto setup_error;
                }
 
-               if (outputs) {
-                       /* Copy output list into message payload */
-                       memcpy(cmd_ctx->llm->payload, outputs,
-                                       nb_output * sizeof(struct lttng_snapshot_output));
-                       free(outputs);
-               }
-
                ret = LTTNG_OK;
                break;
        }
@@ -4063,81 +4249,219 @@ skip_domain:
                                cmd_ctx->lsm->u.set_shm_path.shm_path);
                break;
        }
-       default:
-               ret = LTTNG_ERR_UND;
+       case LTTNG_REGENERATE_METADATA:
+       {
+               ret = cmd_regenerate_metadata(cmd_ctx->session);
                break;
        }
-
-error:
-       if (cmd_ctx->llm == NULL) {
-               DBG("Missing llm structure. Allocating one.");
-               if (setup_lttng_msg(cmd_ctx, 0) < 0) {
-                       goto setup_error;
-               }
+       case LTTNG_REGENERATE_STATEDUMP:
+       {
+               ret = cmd_regenerate_statedump(cmd_ctx->session);
+               break;
        }
-       /* Set return code */
-       cmd_ctx->llm->ret_code = ret;
-setup_error:
-       if (cmd_ctx->session) {
-               session_unlock(cmd_ctx->session);
+       case LTTNG_REGISTER_TRIGGER:
+       {
+               ret = cmd_register_trigger(cmd_ctx, sock,
+                               notification_thread_handle);
+               break;
        }
-       if (need_tracing_session) {
-               session_unlock_list();
+       case LTTNG_UNREGISTER_TRIGGER:
+       {
+               ret = cmd_unregister_trigger(cmd_ctx, sock,
+                               notification_thread_handle);
+               break;
        }
-init_setup_error:
-       assert(!rcu_read_ongoing());
-       return ret;
-}
+       case LTTNG_ROTATE_SESSION:
+       {
+               struct lttng_rotate_session_return rotate_return;
 
-/*
- * 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("Client rotate session \"%s\"", cmd_ctx->session->name);
 
-       DBG("[thread] Manage health check started");
+               memset(&rotate_return, 0, sizeof(rotate_return));
+               if (cmd_ctx->session->kernel_session && !check_rotate_compatible()) {
+                       DBG("Kernel tracer version is not compatible with the rotation feature");
+                       ret = LTTNG_ERR_ROTATION_WRONG_VERSION;
+                       goto error;
+               }
 
-       rcu_register_thread();
+               ret = cmd_rotate_session(cmd_ctx->session, &rotate_return);
+               if (ret < 0) {
+                       ret = -ret;
+                       goto error;
+               }
 
-       /* We might hit an error path before this is created. */
-       lttng_poll_init(&events);
+               ret = setup_lttng_msg_no_cmd_header(cmd_ctx, &rotate_return,
+                               sizeof(rotate_return));
+               if (ret < 0) {
+                       ret = -ret;
+                       goto error;
+               }
 
-       /* Create unix socket */
-       sock = lttcomm_create_unix_sock(health_unix_sock_path);
-       if (sock < 0) {
-               ERR("Unable to create health check Unix socket");
-               ret = -1;
-               goto error;
+               ret = LTTNG_OK;
+               break;
        }
+       case LTTNG_ROTATION_GET_INFO:
+       {
+               struct lttng_rotation_get_info_return get_info_return;
 
-       if (is_root) {
-               /* lttng health client socket path permissions */
-               ret = chown(health_unix_sock_path, 0,
-                               utils_get_group_id(tracing_group_name));
+               memset(&get_info_return, 0, sizeof(get_info_return));
+               ret = cmd_rotate_get_info(cmd_ctx->session, &get_info_return,
+                               cmd_ctx->lsm->u.get_rotation_info.rotation_id);
                if (ret < 0) {
-                       ERR("Unable to set group on %s", health_unix_sock_path);
-                       PERROR("chown");
-                       ret = -1;
+                       ret = -ret;
                        goto error;
                }
 
-               ret = chmod(health_unix_sock_path,
-                               S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP);
+               ret = setup_lttng_msg_no_cmd_header(cmd_ctx, &get_info_return,
+                               sizeof(get_info_return));
                if (ret < 0) {
-                       ERR("Unable to set permissions on %s", health_unix_sock_path);
-                       PERROR("chmod");
-                       ret = -1;
+                       ret = -ret;
                        goto error;
                }
-       }
 
-       /*
-        * Set the CLOEXEC flag. Return code is useless because either way, the
+               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,
+                               set_schedule,
+                               schedule_type,
+                               value,
+                               notification_thread_handle);
+               if (ret != LTTNG_OK) {
+                       goto error;
+               }
+
+               break;
+       }
+       case LTTNG_SESSION_LIST_ROTATION_SCHEDULES:
+       {
+               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;
+               }
+
+               ret = LTTNG_OK;
+               break;
+       }
+       default:
+               ret = LTTNG_ERR_UND;
+               break;
+       }
+
+error:
+       if (cmd_ctx->llm == NULL) {
+               DBG("Missing llm structure. Allocating one.");
+               if (setup_lttng_msg_no_cmd_header(cmd_ctx, NULL, 0) < 0) {
+                       goto setup_error;
+               }
+       }
+       /* Set return code */
+       cmd_ctx->llm->ret_code = ret;
+setup_error:
+       if (cmd_ctx->session) {
+               session_unlock(cmd_ctx->session);
+       }
+       if (need_tracing_session) {
+               session_unlock_list();
+       }
+init_setup_error:
+       assert(!rcu_read_ongoing());
+       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);
@@ -4232,7 +4556,6 @@ restart:
                        if (ret) {
                                PERROR("close");
                        }
-                       new_sock = -1;
                        continue;
                }
 
@@ -4261,7 +4584,6 @@ restart:
                if (ret) {
                        PERROR("close");
                }
-               new_sock = -1;
        }
 
 exit:
@@ -4270,7 +4592,7 @@ error:
                ERR("Health error occurred in %s", __func__);
        }
        DBG("Health check thread dying");
-       unlink(health_unix_sock_path);
+       unlink(config.health_unix_sock_path.value);
        if (sock >= 0) {
                ret = close(sock);
                if (ret) {
@@ -4279,7 +4601,7 @@ error:
        }
 
        lttng_poll_clean(&events);
-
+       stop_threads();
        rcu_unregister_thread();
        return NULL;
 }
@@ -4324,13 +4646,50 @@ static void *thread_manage_clients(void *data)
                goto error;
        }
 
-       sessiond_notify_ready();
        ret = sem_post(&load_info->message_thread_ready);
        if (ret) {
                PERROR("sem_post message_thread_ready");
                goto error;
        }
 
+       /*
+        * Wait until all support threads are initialized before accepting
+        * commands.
+        */
+       while (uatomic_read(&lttng_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
+                * "ready".
+                *
+                * 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)) {
+                       goto exit;
+               }
+       }
+       /*
+        * This barrier is paired with the one in sessiond_notify_ready() to
+        * ensure that loads accessing data initialized by the other threads,
+        * on which this thread was waiting, are not performed before this point.
+        *
+        * Note that this could be a 'read' memory barrier, but a full barrier
+        * is used in case the code changes. The performance implications of
+        * this choice are minimal since this is a slow path.
+        */
+       cmm_smp_mb();
+
        /* This testpoint is after we signal readiness to the parent. */
        if (testpoint(sessiond_thread_manage_clients)) {
                goto error;
@@ -4343,6 +4702,8 @@ static void *thread_manage_clients(void *data)
        health_code_update();
 
        while (1) {
+               const struct cmd_completion_handler *cmd_completion_handler;
+
                DBG("Accepting client command ...");
 
                /* Inifinite blocking call, waiting for transmission */
@@ -4485,6 +4846,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))",
@@ -4522,7 +4895,7 @@ error:
 
 error_listen:
 error_create_poll:
-       unlink(client_unix_sock_path);
+       unlink(config.client_unix_sock_path.value);
        if (client_sock >= 0) {
                ret = close(client_sock);
                if (ret) {
@@ -4565,43 +4938,6 @@ error_create_poll:
        return NULL;
 }
 
-
-/*
- * usage function on stderr
- */
-static void usage(void)
-{
-       fprintf(stderr, "Usage: %s OPTIONS\n\nOptions:\n", progname);
-       fprintf(stderr, "  -h, --help                         Display this usage.\n");
-       fprintf(stderr, "  -c, --client-sock PATH             Specify path for the client unix socket\n");
-       fprintf(stderr, "  -a, --apps-sock PATH               Specify path for apps unix socket\n");
-       fprintf(stderr, "      --kconsumerd-err-sock PATH     Specify path for the kernel consumer error socket\n");
-       fprintf(stderr, "      --kconsumerd-cmd-sock PATH     Specify path for the kernel consumer command socket\n");
-       fprintf(stderr, "      --ustconsumerd32-err-sock PATH Specify path for the 32-bit UST consumer error socket\n");
-       fprintf(stderr, "      --ustconsumerd64-err-sock PATH Specify path for the 64-bit UST consumer error socket\n");
-       fprintf(stderr, "      --ustconsumerd32-cmd-sock PATH Specify path for the 32-bit UST consumer command socket\n");
-       fprintf(stderr, "      --ustconsumerd64-cmd-sock PATH Specify path for the 64-bit UST consumer command socket\n");
-       fprintf(stderr, "      --consumerd32-path PATH     Specify path for the 32-bit UST consumer daemon binary\n");
-       fprintf(stderr, "      --consumerd32-libdir PATH   Specify path for the 32-bit UST consumer daemon libraries\n");
-       fprintf(stderr, "      --consumerd64-path PATH     Specify path for the 64-bit UST consumer daemon binary\n");
-       fprintf(stderr, "      --consumerd64-libdir PATH   Specify path for the 64-bit UST consumer daemon libraries\n");
-       fprintf(stderr, "  -d, --daemonize                    Start as a daemon.\n");
-       fprintf(stderr, "  -b, --background                   Start as a daemon, keeping console open.\n");
-       fprintf(stderr, "  -g, --group NAME                   Specify the tracing group name. (default: tracing)\n");
-       fprintf(stderr, "  -V, --version                      Show version number.\n");
-       fprintf(stderr, "  -S, --sig-parent                   Send SIGUSR1 to parent pid to notify readiness.\n");
-       fprintf(stderr, "  -q, --quiet                        No output at all.\n");
-       fprintf(stderr, "  -v, --verbose                      Verbose mode. Activate DBG() macro.\n");
-       fprintf(stderr, "  -p, --pidfile FILE                 Write a pid to FILE name overriding the default value.\n");
-       fprintf(stderr, "      --verbose-consumer             Verbose mode for consumer. Activate DBG() macro.\n");
-       fprintf(stderr, "      --no-kernel                    Disable kernel tracer\n");
-       fprintf(stderr, "      --agent-tcp-port               Agent registration TCP port\n");
-       fprintf(stderr, "  -f  --config PATH                  Load daemon configuration file\n");
-       fprintf(stderr, "  -l  --load PATH                    Load session configuration\n");
-       fprintf(stderr, "      --kmod-probes                  Specify kernel module probes to load\n");
-       fprintf(stderr, "      --extra-kmod-probes            Specify extra kernel module probes to load\n");
-}
-
 static int string_match(const char *str1, const char *str2)
 {
        return (str1 && str2) && !strcmp(str1, str2);
@@ -4617,210 +4953,278 @@ static int set_option(int opt, const char *arg, const char *optname)
 {
        int ret = 0;
 
-       if (arg && arg[0] == '\0') {
-               /*
-                * This only happens if the value is read from daemon config
-                * file. This means the option requires an argument and the
-                * configuration file contains a line such as:
-                * my_option =
-                */
-               ret = -EINVAL;
-               goto end;
-       }
-
        if (string_match(optname, "client-sock") || opt == 'c') {
+               if (!arg || *arg == '\0') {
+                       ret = -EINVAL;
+                       goto end;
+               }
                if (lttng_is_setuid_setgid()) {
                        WARN("Getting '%s' argument from setuid/setgid binary refused for security reasons.",
                                "-c, --client-sock");
                } else {
-                       snprintf(client_unix_sock_path, PATH_MAX, "%s", arg);
+                       config_string_set(&config.client_unix_sock_path,
+                                       strdup(arg));
+                       if (!config.client_unix_sock_path.value) {
+                               ret = -ENOMEM;
+                               PERROR("strdup");
+                       }
                }
        } else if (string_match(optname, "apps-sock") || opt == 'a') {
+               if (!arg || *arg == '\0') {
+                       ret = -EINVAL;
+                       goto end;
+               }
                if (lttng_is_setuid_setgid()) {
                        WARN("Getting '%s' argument from setuid/setgid binary refused for security reasons.",
                                "-a, --apps-sock");
                } else {
-                       snprintf(apps_unix_sock_path, PATH_MAX, "%s", arg);
+                       config_string_set(&config.apps_unix_sock_path,
+                                       strdup(arg));
+                       if (!config.apps_unix_sock_path.value) {
+                               ret = -ENOMEM;
+                               PERROR("strdup");
+                       }
                }
        } else if (string_match(optname, "daemonize") || opt == 'd') {
-               opt_daemon = 1;
+               config.daemonize = true;
        } else if (string_match(optname, "background") || opt == 'b') {
-               opt_background = 1;
+               config.background = true;
        } else if (string_match(optname, "group") || opt == 'g') {
+               if (!arg || *arg == '\0') {
+                       ret = -EINVAL;
+                       goto end;
+               }
                if (lttng_is_setuid_setgid()) {
                        WARN("Getting '%s' argument from setuid/setgid binary refused for security reasons.",
                                "-g, --group");
                } else {
-                       /*
-                        * If the override option is set, the pointer points to a
-                        * *non* const thus freeing it even though the variable type is
-                        * set to const.
-                        */
-                       if (tracing_group_name_override) {
-                               free((void *) tracing_group_name);
-                       }
-                       tracing_group_name = strdup(arg);
-                       if (!tracing_group_name) {
-                               PERROR("strdup");
+                       config_string_set(&config.tracing_group_name,
+                                       strdup(arg));
+                       if (!config.tracing_group_name.value) {
                                ret = -ENOMEM;
+                               PERROR("strdup");
                        }
-                       tracing_group_name_override = 1;
                }
        } else if (string_match(optname, "help") || opt == 'h') {
-               usage();
-               exit(EXIT_SUCCESS);
+               ret = utils_show_help(8, "lttng-sessiond", help_msg);
+               if (ret) {
+                       ERR("Cannot show --help for `lttng-sessiond`");
+                       perror("exec");
+               }
+               exit(ret ? EXIT_FAILURE : EXIT_SUCCESS);
        } else if (string_match(optname, "version") || opt == 'V') {
                fprintf(stdout, "%s\n", VERSION);
                exit(EXIT_SUCCESS);
        } else if (string_match(optname, "sig-parent") || opt == 'S') {
-               opt_sig_parent = 1;
+               config.sig_parent = true;
        } else if (string_match(optname, "kconsumerd-err-sock")) {
+               if (!arg || *arg == '\0') {
+                       ret = -EINVAL;
+                       goto end;
+               }
                if (lttng_is_setuid_setgid()) {
                        WARN("Getting '%s' argument from setuid/setgid binary refused for security reasons.",
                                "--kconsumerd-err-sock");
                } else {
-                       snprintf(kconsumer_data.err_unix_sock_path, PATH_MAX, "%s", arg);
+                       config_string_set(&config.kconsumerd_err_unix_sock_path,
+                                       strdup(arg));
+                       if (!config.kconsumerd_err_unix_sock_path.value) {
+                               ret = -ENOMEM;
+                               PERROR("strdup");
+                       }
                }
        } else if (string_match(optname, "kconsumerd-cmd-sock")) {
+               if (!arg || *arg == '\0') {
+                       ret = -EINVAL;
+                       goto end;
+               }
                if (lttng_is_setuid_setgid()) {
                        WARN("Getting '%s' argument from setuid/setgid binary refused for security reasons.",
                                "--kconsumerd-cmd-sock");
                } else {
-                       snprintf(kconsumer_data.cmd_unix_sock_path, PATH_MAX, "%s", arg);
+                       config_string_set(&config.kconsumerd_cmd_unix_sock_path,
+                                       strdup(arg));
+                       if (!config.kconsumerd_cmd_unix_sock_path.value) {
+                               ret = -ENOMEM;
+                               PERROR("strdup");
+                       }
                }
        } else if (string_match(optname, "ustconsumerd64-err-sock")) {
+               if (!arg || *arg == '\0') {
+                       ret = -EINVAL;
+                       goto end;
+               }
                if (lttng_is_setuid_setgid()) {
                        WARN("Getting '%s' argument from setuid/setgid binary refused for security reasons.",
                                "--ustconsumerd64-err-sock");
                } else {
-                       snprintf(ustconsumer64_data.err_unix_sock_path, PATH_MAX, "%s", arg);
+                       config_string_set(&config.consumerd64_err_unix_sock_path,
+                                       strdup(arg));
+                       if (!config.consumerd64_err_unix_sock_path.value) {
+                               ret = -ENOMEM;
+                               PERROR("strdup");
+                       }
                }
        } else if (string_match(optname, "ustconsumerd64-cmd-sock")) {
+               if (!arg || *arg == '\0') {
+                       ret = -EINVAL;
+                       goto end;
+               }
                if (lttng_is_setuid_setgid()) {
                        WARN("Getting '%s' argument from setuid/setgid binary refused for security reasons.",
                                "--ustconsumerd64-cmd-sock");
                } else {
-                       snprintf(ustconsumer64_data.cmd_unix_sock_path, PATH_MAX, "%s", arg);
+                       config_string_set(&config.consumerd64_cmd_unix_sock_path,
+                                       strdup(arg));
+                       if (!config.consumerd64_cmd_unix_sock_path.value) {
+                               ret = -ENOMEM;
+                               PERROR("strdup");
+                       }
                }
        } else if (string_match(optname, "ustconsumerd32-err-sock")) {
+               if (!arg || *arg == '\0') {
+                       ret = -EINVAL;
+                       goto end;
+               }
                if (lttng_is_setuid_setgid()) {
                        WARN("Getting '%s' argument from setuid/setgid binary refused for security reasons.",
                                "--ustconsumerd32-err-sock");
                } else {
-                       snprintf(ustconsumer32_data.err_unix_sock_path, PATH_MAX, "%s", arg);
+                       config_string_set(&config.consumerd32_err_unix_sock_path,
+                                       strdup(arg));
+                       if (!config.consumerd32_err_unix_sock_path.value) {
+                               ret = -ENOMEM;
+                               PERROR("strdup");
+                       }
                }
        } else if (string_match(optname, "ustconsumerd32-cmd-sock")) {
+               if (!arg || *arg == '\0') {
+                       ret = -EINVAL;
+                       goto end;
+               }
                if (lttng_is_setuid_setgid()) {
                        WARN("Getting '%s' argument from setuid/setgid binary refused for security reasons.",
                                "--ustconsumerd32-cmd-sock");
                } else {
-                       snprintf(ustconsumer32_data.cmd_unix_sock_path, PATH_MAX, "%s", arg);
+                       config_string_set(&config.consumerd32_cmd_unix_sock_path,
+                                       strdup(arg));
+                       if (!config.consumerd32_cmd_unix_sock_path.value) {
+                               ret = -ENOMEM;
+                               PERROR("strdup");
+                       }
                }
        } else if (string_match(optname, "no-kernel")) {
-               opt_no_kernel = 1;
+               config.no_kernel = true;
        } else if (string_match(optname, "quiet") || opt == 'q') {
-               lttng_opt_quiet = 1;
+               config.quiet = true;
        } else if (string_match(optname, "verbose") || opt == 'v') {
                /* Verbose level can increase using multiple -v */
                if (arg) {
                        /* Value obtained from config file */
-                       lttng_opt_verbose = config_parse_value(arg);
+                       config.verbose = config_parse_value(arg);
                } else {
                        /* -v used on command line */
-                       lttng_opt_verbose++;
+                       config.verbose++;
                }
                /* Clamp value to [0, 3] */
-               lttng_opt_verbose = lttng_opt_verbose < 0 ? 0 :
-                       (lttng_opt_verbose <= 3 ? lttng_opt_verbose : 3);
+               config.verbose = config.verbose < 0 ? 0 :
+                       (config.verbose <= 3 ? config.verbose : 3);
        } else if (string_match(optname, "verbose-consumer")) {
                if (arg) {
-                       opt_verbose_consumer = config_parse_value(arg);
+                       config.verbose_consumer = config_parse_value(arg);
                } else {
-                       opt_verbose_consumer += 1;
+                       config.verbose_consumer++;
                }
        } else if (string_match(optname, "consumerd32-path")) {
+               if (!arg || *arg == '\0') {
+                       ret = -EINVAL;
+                       goto end;
+               }
                if (lttng_is_setuid_setgid()) {
                        WARN("Getting '%s' argument from setuid/setgid binary refused for security reasons.",
                                "--consumerd32-path");
                } else {
-                       if (consumerd32_bin_override) {
-                               free((void *) consumerd32_bin);
-                       }
-                       consumerd32_bin = strdup(arg);
-                       if (!consumerd32_bin) {
+                       config_string_set(&config.consumerd32_bin_path,
+                                       strdup(arg));
+                       if (!config.consumerd32_bin_path.value) {
                                PERROR("strdup");
                                ret = -ENOMEM;
                        }
-                       consumerd32_bin_override = 1;
                }
        } else if (string_match(optname, "consumerd32-libdir")) {
+               if (!arg || *arg == '\0') {
+                       ret = -EINVAL;
+                       goto end;
+               }
                if (lttng_is_setuid_setgid()) {
                        WARN("Getting '%s' argument from setuid/setgid binary refused for security reasons.",
                                "--consumerd32-libdir");
                } else {
-                       if (consumerd32_libdir_override) {
-                               free((void *) consumerd32_libdir);
-                       }
-                       consumerd32_libdir = strdup(arg);
-                       if (!consumerd32_libdir) {
+                       config_string_set(&config.consumerd32_lib_dir,
+                                       strdup(arg));
+                       if (!config.consumerd32_lib_dir.value) {
                                PERROR("strdup");
                                ret = -ENOMEM;
                        }
-                       consumerd32_libdir_override = 1;
                }
        } else if (string_match(optname, "consumerd64-path")) {
+               if (!arg || *arg == '\0') {
+                       ret = -EINVAL;
+                       goto end;
+               }
                if (lttng_is_setuid_setgid()) {
                        WARN("Getting '%s' argument from setuid/setgid binary refused for security reasons.",
                                "--consumerd64-path");
                } else {
-                       if (consumerd64_bin_override) {
-                               free((void *) consumerd64_bin);
-                       }
-                       consumerd64_bin = strdup(arg);
-                       if (!consumerd64_bin) {
+                       config_string_set(&config.consumerd64_bin_path,
+                                       strdup(arg));
+                       if (!config.consumerd64_bin_path.value) {
                                PERROR("strdup");
                                ret = -ENOMEM;
                        }
-                       consumerd64_bin_override = 1;
                }
        } else if (string_match(optname, "consumerd64-libdir")) {
+               if (!arg || *arg == '\0') {
+                       ret = -EINVAL;
+                       goto end;
+               }
                if (lttng_is_setuid_setgid()) {
                        WARN("Getting '%s' argument from setuid/setgid binary refused for security reasons.",
                                "--consumerd64-libdir");
                } else {
-                       if (consumerd64_libdir_override) {
-                               free((void *) consumerd64_libdir);
-                       }
-                       consumerd64_libdir = strdup(arg);
-                       if (!consumerd64_libdir) {
+                       config_string_set(&config.consumerd64_lib_dir,
+                                       strdup(arg));
+                       if (!config.consumerd64_lib_dir.value) {
                                PERROR("strdup");
                                ret = -ENOMEM;
                        }
-                       consumerd64_libdir_override = 1;
                }
        } else if (string_match(optname, "pidfile") || opt == 'p') {
+               if (!arg || *arg == '\0') {
+                       ret = -EINVAL;
+                       goto end;
+               }
                if (lttng_is_setuid_setgid()) {
                        WARN("Getting '%s' argument from setuid/setgid binary refused for security reasons.",
                                "-p, --pidfile");
                } else {
-                       free(opt_pidfile);
-                       opt_pidfile = strdup(arg);
-                       if (!opt_pidfile) {
+                       config_string_set(&config.pid_file_path, strdup(arg));
+                       if (!config.pid_file_path.value) {
                                PERROR("strdup");
                                ret = -ENOMEM;
                        }
                }
        } else if (string_match(optname, "agent-tcp-port")) {
+               if (!arg || *arg == '\0') {
+                       ret = -EINVAL;
+                       goto end;
+               }
                if (lttng_is_setuid_setgid()) {
                        WARN("Getting '%s' argument from setuid/setgid binary refused for security reasons.",
                                "--agent-tcp-port");
                } else {
                        unsigned long v;
 
-                       if (!arg) {
-                               ret = -EINVAL;
-                               goto end;
-                       }
                        errno = 0;
                        v = strtoul(arg, NULL, 0);
                        if (errno != 0 || !isdigit(arg[0])) {
@@ -4831,41 +5235,51 @@ static int set_option(int opt, const char *arg, const char *optname)
                                ERR("Port overflow in --agent-tcp-port parameter: %s", arg);
                                return -1;
                        }
-                       agent_tcp_port = (uint32_t) v;
-                       DBG3("Agent TCP port set to non default: %u", agent_tcp_port);
+                       config.agent_tcp_port.begin = config.agent_tcp_port.end = (int) v;
+                       DBG3("Agent TCP port set to non default: %i", (int) v);
                }
        } else if (string_match(optname, "load") || opt == 'l') {
+               if (!arg || *arg == '\0') {
+                       ret = -EINVAL;
+                       goto end;
+               }
                if (lttng_is_setuid_setgid()) {
                        WARN("Getting '%s' argument from setuid/setgid binary refused for security reasons.",
                                "-l, --load");
                } else {
-                       free(opt_load_session_path);
-                       opt_load_session_path = strdup(arg);
-                       if (!opt_load_session_path) {
+                       config_string_set(&config.load_session_path, strdup(arg));
+                       if (!config.load_session_path.value) {
                                PERROR("strdup");
                                ret = -ENOMEM;
                        }
                }
        } else if (string_match(optname, "kmod-probes")) {
+               if (!arg || *arg == '\0') {
+                       ret = -EINVAL;
+                       goto end;
+               }
                if (lttng_is_setuid_setgid()) {
                        WARN("Getting '%s' argument from setuid/setgid binary refused for security reasons.",
                                "--kmod-probes");
                } else {
-                       free(kmod_probes_list);
-                       kmod_probes_list = strdup(arg);
-                       if (!kmod_probes_list) {
+                       config_string_set(&config.kmod_probes_list, strdup(arg));
+                       if (!config.kmod_probes_list.value) {
                                PERROR("strdup");
                                ret = -ENOMEM;
                        }
                }
        } else if (string_match(optname, "extra-kmod-probes")) {
+               if (!arg || *arg == '\0') {
+                       ret = -EINVAL;
+                       goto end;
+               }
                if (lttng_is_setuid_setgid()) {
                        WARN("Getting '%s' argument from setuid/setgid binary refused for security reasons.",
                                "--extra-kmod-probes");
                } else {
-                       free(kmod_extra_probes_list);
-                       kmod_extra_probes_list = strdup(arg);
-                       if (!kmod_extra_probes_list) {
+                       config_string_set(&config.kmod_extra_probes_list,
+                                       strdup(arg));
+                       if (!config.kmod_extra_probes_list.value) {
                                PERROR("strdup");
                                ret = -ENOMEM;
                        }
@@ -4901,7 +5315,7 @@ end:
 
 /*
  * config_entry_handler_cb used to handle options read from a config file.
- * See config_entry_handler_cb comment in common/config/config.h for the
+ * See config_entry_handler_cb comment in common/config/session-config.h for the
  * return value conventions.
  */
 static int config_entry_handler(const struct config_entry *entry, void *unused)
@@ -5049,9 +5463,9 @@ static int init_daemon_socket(void)
        old_umask = umask(0);
 
        /* Create client tool unix socket */
-       client_sock = lttcomm_create_unix_sock(client_unix_sock_path);
+       client_sock = lttcomm_create_unix_sock(config.client_unix_sock_path.value);
        if (client_sock < 0) {
-               ERR("Create unix sock failed: %s", client_unix_sock_path);
+               ERR("Create unix sock failed: %s", config.client_unix_sock_path.value);
                ret = -1;
                goto end;
        }
@@ -5065,17 +5479,17 @@ static int init_daemon_socket(void)
        }
 
        /* File permission MUST be 660 */
-       ret = chmod(client_unix_sock_path, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP);
+       ret = chmod(config.client_unix_sock_path.value, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP);
        if (ret < 0) {
-               ERR("Set file permissions failed: %s", client_unix_sock_path);
+               ERR("Set file permissions failed: %s", config.client_unix_sock_path.value);
                PERROR("chmod");
                goto end;
        }
 
        /* Create the application unix socket */
-       apps_sock = lttcomm_create_unix_sock(apps_unix_sock_path);
+       apps_sock = lttcomm_create_unix_sock(config.apps_unix_sock_path.value);
        if (apps_sock < 0) {
-               ERR("Create unix sock failed: %s", apps_unix_sock_path);
+               ERR("Create unix sock failed: %s", config.apps_unix_sock_path.value);
                ret = -1;
                goto end;
        }
@@ -5089,10 +5503,10 @@ static int init_daemon_socket(void)
        }
 
        /* File permission MUST be 666 */
-       ret = chmod(apps_unix_sock_path,
+       ret = chmod(config.apps_unix_sock_path.value,
                        S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH);
        if (ret < 0) {
-               ERR("Set file permissions failed: %s", apps_unix_sock_path);
+               ERR("Set file permissions failed: %s", config.apps_unix_sock_path.value);
                PERROR("chmod");
                goto end;
        }
@@ -5105,34 +5519,73 @@ end:
        return ret;
 }
 
+/*
+ * Create lockfile using the rundir and return its fd.
+ */
+static int create_lockfile(void)
+{
+        return utils_create_lock_file(config.lock_file_path.value);
+}
+
 /*
  * Check if the global socket is available, and if a daemon is answering at the
  * other side. If yes, error is returned.
+ *
+ * Also attempts to create and hold the lock file.
  */
 static int check_existing_daemon(void)
 {
+       int ret = 0;
+
        /* Is there anybody out there ? */
        if (lttng_session_daemon_alive()) {
-               return -EEXIST;
+               ret = -EEXIST;
+               goto end;
        }
 
-       return 0;
+       lockfile_fd = create_lockfile();
+       if (lockfile_fd < 0) {
+               ret = -EEXIST;
+               goto end;
+       }
+end:
+       return ret;
 }
 
-/*
- * Set the tracing group gid onto the client socket.
- *
- * Race window between mkdir and chown is OK because we are going from more
- * permissive (root.root) to less permissive (root.tracing).
- */
-static int set_permissions(char *rundir)
+static void sessiond_cleanup_lock_file(void)
 {
        int ret;
-       gid_t gid;
 
-       gid = utils_get_group_id(tracing_group_name);
-
-       /* Set lttng run dir */
+       /*
+        * Cleanup lock file by deleting it and finaly closing it which will
+        * release the file system lock.
+        */
+       if (lockfile_fd >= 0) {
+               ret = remove(config.lock_file_path.value);
+               if (ret < 0) {
+                       PERROR("remove lock file");
+               }
+               ret = close(lockfile_fd);
+               if (ret < 0) {
+                       PERROR("close lock file");
+               }
+       }
+}
+
+/*
+ * Set the tracing group gid onto the client socket.
+ *
+ * Race window between mkdir and chown is OK because we are going from more
+ * permissive (root.root) to less permissive (root.tracing).
+ */
+static int set_permissions(char *rundir)
+{
+       int ret;
+       gid_t gid;
+
+       gid = utils_get_group_id(config.tracing_group_name.value);
+
+       /* Set lttng run dir */
        ret = chown(rundir, 0, gid);
        if (ret < 0) {
                ERR("Unable to set group on %s", rundir);
@@ -5151,9 +5604,9 @@ static int set_permissions(char *rundir)
        }
 
        /* lttng client socket path */
-       ret = chown(client_unix_sock_path, 0, gid);
+       ret = chown(config.client_unix_sock_path.value, 0, gid);
        if (ret < 0) {
-               ERR("Unable to set group on %s", client_unix_sock_path);
+               ERR("Unable to set group on %s", config.client_unix_sock_path.value);
                PERROR("chown");
        }
 
@@ -5186,16 +5639,16 @@ static int set_permissions(char *rundir)
 /*
  * Create the lttng run directory needed for all global sockets and pipe.
  */
-static int create_lttng_rundir(const char *rundir)
+static int create_lttng_rundir(void)
 {
        int ret;
 
-       DBG3("Creating LTTng run directory: %s", rundir);
+       DBG3("Creating LTTng run directory: %s", config.rundir.value);
 
-       ret = mkdir(rundir, S_IRWXU);
+       ret = mkdir(config.rundir.value, S_IRWXU);
        if (ret < 0) {
                if (errno != EEXIST) {
-                       ERR("Unable to create %s", rundir);
+                       ERR("Unable to create %s", config.rundir.value);
                        goto error;
                } else {
                        ret = 0;
@@ -5207,44 +5660,41 @@ error:
 }
 
 /*
- * Setup sockets and directory needed by the kconsumerd communication with the
+ * Setup sockets and directory needed by the consumerds' communication with the
  * session daemon.
  */
-static int set_consumer_sockets(struct consumer_data *consumer_data,
-               const char *rundir)
+static int set_consumer_sockets(struct consumer_data *consumer_data)
 {
        int ret;
-       char path[PATH_MAX];
+       char *path = NULL;
 
        switch (consumer_data->type) {
        case LTTNG_CONSUMER_KERNEL:
-               snprintf(path, PATH_MAX, DEFAULT_KCONSUMERD_PATH, rundir);
+               path = config.kconsumerd_path.value;
                break;
        case LTTNG_CONSUMER64_UST:
-               snprintf(path, PATH_MAX, DEFAULT_USTCONSUMERD64_PATH, rundir);
+               path = config.consumerd64_path.value;
                break;
        case LTTNG_CONSUMER32_UST:
-               snprintf(path, PATH_MAX, DEFAULT_USTCONSUMERD32_PATH, rundir);
+               path = config.consumerd32_path.value;
                break;
        default:
                ERR("Consumer type unknown");
                ret = -EINVAL;
                goto error;
        }
+       assert(path);
 
        DBG2("Creating consumer directory: %s", path);
 
        ret = mkdir(path, S_IRWXU | S_IRGRP | S_IXGRP);
-       if (ret < 0) {
-               if (errno != EEXIST) {
-                       PERROR("mkdir");
-                       ERR("Failed to create %s", path);
-                       goto error;
-               }
-               ret = -1;
+       if (ret < 0 && errno != EEXIST) {
+               PERROR("mkdir");
+               ERR("Failed to create %s", path);
+               goto error;
        }
        if (is_root) {
-               ret = chown(path, 0, utils_get_group_id(tracing_group_name));
+               ret = chown(path, 0, utils_get_group_id(config.tracing_group_name.value));
                if (ret < 0) {
                        ERR("Unable to set group on %s", path);
                        PERROR("chown");
@@ -5252,7 +5702,7 @@ static int set_consumer_sockets(struct consumer_data *consumer_data,
                }
        }
 
-       /* Create the kconsumerd error unix socket */
+       /* Create the consumerd error unix socket */
        consumer_data->err_sock =
                lttcomm_create_unix_sock(consumer_data->err_unix_sock_path);
        if (consumer_data->err_sock < 0) {
@@ -5293,9 +5743,6 @@ error:
 static void sighandler(int sig)
 {
        switch (sig) {
-       case SIGPIPE:
-               DBG("SIGPIPE caught");
-               return;
        case SIGINT:
                DBG("SIGINT caught");
                stop_threads();
@@ -5327,9 +5774,10 @@ static int set_signal_handler(void)
                return ret;
        }
 
-       sa.sa_handler = sighandler;
        sa.sa_mask = sigset;
        sa.sa_flags = 0;
+
+       sa.sa_handler = sighandler;
        if ((ret = sigaction(SIGTERM, &sa, NULL)) < 0) {
                PERROR("sigaction");
                return ret;
@@ -5340,12 +5788,13 @@ static int set_signal_handler(void)
                return ret;
        }
 
-       if ((ret = sigaction(SIGPIPE, &sa, NULL)) < 0) {
+       if ((ret = sigaction(SIGUSR1, &sa, NULL)) < 0) {
                PERROR("sigaction");
                return ret;
        }
 
-       if ((ret = sigaction(SIGUSR1, &sa, NULL)) < 0) {
+       sa.sa_handler = SIG_IGN;
+       if ((ret = sigaction(SIGPIPE, &sa, NULL)) < 0) {
                PERROR("sigaction");
                return ret;
        }
@@ -5357,14 +5806,14 @@ static int set_signal_handler(void)
 
 /*
  * Set open files limit to unlimited. This daemon can open a large number of
- * file descriptors in order to consumer multiple kernel traces.
+ * file descriptors in order to consume multiple kernel traces.
  */
 static void set_ulimit(void)
 {
        int ret;
        struct rlimit lim;
 
-       /* The kernel does not allowed an infinite limit for open files */
+       /* The kernel does not allow an infinite limit for open files */
        lim.rlim_cur = 65535;
        lim.rlim_max = 65535;
 
@@ -5374,78 +5823,80 @@ static void set_ulimit(void)
        }
 }
 
-/*
- * Write pidfile using the rundir and opt_pidfile.
- */
 static int write_pidfile(void)
 {
-       int ret;
-       char pidfile_path[PATH_MAX];
+        return utils_create_pid_file(getpid(), config.pid_file_path.value);
+}
 
-       assert(rundir);
+static int set_clock_plugin_env(void)
+{
+       int ret = 0;
+       char *env_value = NULL;
 
-       if (opt_pidfile) {
-               strncpy(pidfile_path, opt_pidfile, sizeof(pidfile_path));
-       } else {
-               /* Build pidfile path from rundir and opt_pidfile. */
-               ret = snprintf(pidfile_path, sizeof(pidfile_path), "%s/"
-                               DEFAULT_LTTNG_SESSIOND_PIDFILE, rundir);
-               if (ret < 0) {
-                       PERROR("snprintf pidfile path");
-                       goto error;
-               }
+       if (!config.lttng_ust_clock_plugin.value) {
+               goto end;
        }
 
-       /*
-        * Create pid file in rundir.
-        */
-       ret = utils_create_pid_file(getpid(), pidfile_path);
-error:
+        ret = asprintf(&env_value, "LTTNG_UST_CLOCK_PLUGIN=%s",
+                       config.lttng_ust_clock_plugin.value);
+       if (ret < 0) {
+               PERROR("asprintf");
+               goto end;
+       }
+
+       ret = putenv(env_value);
+       if (ret) {
+               free(env_value);
+               PERROR("putenv of LTTNG_UST_CLOCK_PLUGIN");
+               goto end;
+       }
+
+       DBG("Updated LTTNG_UST_CLOCK_PLUGIN environment variable to \"%s\"",
+                       config.lttng_ust_clock_plugin.value);
+end:
        return ret;
 }
 
-/*
- * Create lockfile using the rundir and return its fd.
- */
-static int create_lockfile(void)
+static
+struct rotation_thread_timer_queue *create_rotate_timer_queue(void)
 {
-       int ret;
-       char lockfile_path[PATH_MAX];
+       struct rotation_thread_timer_queue *queue = NULL;
 
-       ret = generate_lock_file_path(lockfile_path, sizeof(lockfile_path));
-       if (ret < 0) {
-               goto error;
+       queue = zmalloc(sizeof(struct rotation_thread_timer_queue));
+       if (!queue) {
+               PERROR("Failed to allocate timer rotate queue");
+               goto end;
        }
 
-       ret = utils_create_lock_file(lockfile_path);
-error:
-       return ret;
+       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;
 }
 
-/*
- * Write agent TCP port using the rundir.
- */
-static int write_agent_port(void)
+static
+void destroy_rotate_timer_queue(struct rotation_thread_timer_queue *queue)
 {
-       int ret;
-       char path[PATH_MAX];
-
-       assert(rundir);
+       struct sessiond_rotation_timer *node, *tmp_node;
 
-       ret = snprintf(path, sizeof(path), "%s/"
-                       DEFAULT_LTTNG_SESSIOND_AGENTPORT_FILE, rundir);
-       if (ret < 0) {
-               PERROR("snprintf agent port path");
-               goto error;
+       if (!queue) {
+               return;
        }
 
-       /*
-        * Create TCP agent port file in rundir.
-        */
-       ret = utils_create_pid_file(agent_tcp_port, path);
+       lttng_pipe_destroy(queue->event_pipe);
 
-error:
-       return ret;
+       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);
+       }
+       pthread_mutex_unlock(&queue->lock);
+
+       pthread_mutex_destroy(&queue->lock);
+       free(queue);
 }
 
 /*
@@ -5455,7 +5906,20 @@ int main(int argc, char **argv)
 {
        int ret = 0, retval = 0;
        void *status;
-       const char *home_path, *env_app_timeout;
+       const char *env_app_timeout;
+       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 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();
 
@@ -5466,7 +5930,10 @@ int main(int argc, char **argv)
                goto exit_set_signal_handler;
        }
 
-       setup_consumerd_path();
+       if (sessiond_timer_signal_init()) {
+               retval = -1;
+               goto exit_set_signal_handler;
+       }
 
        page_size = sysconf(_SC_PAGESIZE);
        if (page_size < 0) {
@@ -5475,6 +5942,18 @@ int main(int argc, char **argv)
                WARN("Fallback page size to %ld", page_size);
        }
 
+       ret = sessiond_config_init(&config);
+       if (ret) {
+               retval = -1;
+               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.
         *
@@ -5489,12 +5968,54 @@ int main(int argc, char **argv)
                goto exit_options;
        }
 
+       /*
+        * Resolve all paths received as arguments, configuration option, or
+        * through environment variable as absolute paths. This is necessary
+        * since daemonizing causes the sessiond's current working directory
+        * to '/'.
+        */
+       ret = sessiond_config_resolve_paths(&config);
+       if (ret) {
+               goto exit_options;
+       }
+
+       /* Apply config. */
+       lttng_opt_verbose = config.verbose;
+       lttng_opt_quiet = config.quiet;
+       kconsumer_data.err_unix_sock_path =
+                       config.kconsumerd_err_unix_sock_path.value;
+       kconsumer_data.cmd_unix_sock_path =
+                       config.kconsumerd_cmd_unix_sock_path.value;
+       ustconsumer32_data.err_unix_sock_path =
+                       config.consumerd32_err_unix_sock_path.value;
+       ustconsumer32_data.cmd_unix_sock_path =
+                       config.consumerd32_cmd_unix_sock_path.value;
+       ustconsumer64_data.err_unix_sock_path =
+                       config.consumerd64_err_unix_sock_path.value;
+       ustconsumer64_data.cmd_unix_sock_path =
+                       config.consumerd64_cmd_unix_sock_path.value;
+       set_clock_plugin_env();
+
+       sessiond_config_log(&config);
+
+       if (create_lttng_rundir()) {
+               retval = -1;
+               goto exit_options;
+       }
+
+       /* Abort launch if a session daemon is already running. */
+       if (check_existing_daemon()) {
+               ERR("A session daemon is already running.");
+               retval = -1;
+               goto exit_options;
+       }
+
        /* Daemonize */
-       if (opt_daemon || opt_background) {
+       if (config.daemonize || config.background) {
                int i;
 
                ret = lttng_daemonize(&child_ppid, &recv_child_signal,
-                       !opt_background);
+                       !config.background);
                if (ret < 0) {
                        retval = -1;
                        goto exit_options;
@@ -5503,13 +6024,20 @@ int main(int argc, char **argv)
                /*
                 * We are in the child. Make sure all other file descriptors are
                 * closed, in case we are called with more opened file
-                * descriptors than the standard ones.
+                * descriptors than the standard ones and the lock file.
                 */
                for (i = 3; i < sysconf(_SC_OPEN_MAX); i++) {
+                       if (i == lockfile_fd) {
+                               continue;
+                       }
                        (void) close(i);
                }
        }
 
+       if (run_as_create_worker(argv[0]) < 0) {
+               goto exit_create_run_as_worker_cleanup;
+       }
+
        /*
         * Starting from here, we can create threads. This needs to be after
         * lttng_daemonize due to RCU.
@@ -5526,29 +6054,8 @@ int main(int argc, char **argv)
                goto exit_health_sessiond_cleanup;
        }
 
-       if (init_ht_cleanup_quit_pipe()) {
-               retval = -1;
-               goto exit_ht_cleanup_quit_pipe;
-       }
-
-       /* Setup the thread ht_cleanup communication pipe. */
-       if (utils_create_pipe_cloexec(ht_cleanup_pipe)) {
-               retval = -1;
-               goto exit_ht_cleanup_pipe;
-       }
-
-       /* Set up max poll set size */
-       if (lttng_poll_set_max_size()) {
-               retval = -1;
-               goto exit_set_max_size;
-       }
-
        /* Create thread to clean up RCU hash tables */
-       ret = pthread_create(&ht_cleanup_thread, NULL,
-                       thread_ht_cleanup, (void *) NULL);
-       if (ret) {
-               errno = ret;
-               PERROR("pthread_create ht_cleanup");
+       if (init_ht_cleanup_thread(&ht_cleanup_thread)) {
                retval = -1;
                goto exit_ht_cleanup;
        }
@@ -5561,211 +6068,99 @@ int main(int argc, char **argv)
 
        /* Check if daemon is UID = 0 */
        is_root = !getuid();
-
        if (is_root) {
-               rundir = strdup(DEFAULT_LTTNG_RUNDIR);
-               if (!rundir) {
-                       retval = -1;
-                       goto exit_init_data;
-               }
-
                /* Create global run dir with root access */
-               if (create_lttng_rundir(rundir)) {
-                       retval = -1;
-                       goto exit_init_data;
-               }
-
-               if (strlen(apps_unix_sock_path) == 0) {
-                       ret = snprintf(apps_unix_sock_path, PATH_MAX,
-                                       DEFAULT_GLOBAL_APPS_UNIX_SOCK);
-                       if (ret < 0) {
-                               retval = -1;
-                               goto exit_init_data;
-                       }
-               }
-
-               if (strlen(client_unix_sock_path) == 0) {
-                       ret = snprintf(client_unix_sock_path, PATH_MAX,
-                                       DEFAULT_GLOBAL_CLIENT_UNIX_SOCK);
-                       if (ret < 0) {
-                               retval = -1;
-                               goto exit_init_data;
-                       }
-               }
-
-               /* Set global SHM for ust */
-               if (strlen(wait_shm_path) == 0) {
-                       ret = snprintf(wait_shm_path, PATH_MAX,
-                                       DEFAULT_GLOBAL_APPS_WAIT_SHM_PATH);
-                       if (ret < 0) {
-                               retval = -1;
-                               goto exit_init_data;
-                       }
-               }
-
-               if (strlen(health_unix_sock_path) == 0) {
-                       ret = snprintf(health_unix_sock_path,
-                                       sizeof(health_unix_sock_path),
-                                       DEFAULT_GLOBAL_HEALTH_UNIX_SOCK);
-                       if (ret < 0) {
-                               retval = -1;
-                               goto exit_init_data;
-                       }
-               }
 
-               /* Setup kernel consumerd path */
-               ret = snprintf(kconsumer_data.err_unix_sock_path, PATH_MAX,
-                               DEFAULT_KCONSUMERD_ERR_SOCK_PATH, rundir);
-               if (ret < 0) {
+               kernel_channel_monitor_pipe = lttng_pipe_open(0);
+               if (!kernel_channel_monitor_pipe) {
+                       ERR("Failed to create kernel consumer channel monitor pipe");
                        retval = -1;
                        goto exit_init_data;
                }
-               ret = snprintf(kconsumer_data.cmd_unix_sock_path, PATH_MAX,
-                               DEFAULT_KCONSUMERD_CMD_SOCK_PATH, rundir);
-               if (ret < 0) {
+               kconsumer_data.channel_monitor_pipe =
+                               lttng_pipe_release_writefd(
+                                       kernel_channel_monitor_pipe);
+               if (kconsumer_data.channel_monitor_pipe < 0) {
                        retval = -1;
                        goto exit_init_data;
                }
-
-               DBG2("Kernel consumer err path: %s",
-                               kconsumer_data.err_unix_sock_path);
-               DBG2("Kernel consumer cmd path: %s",
-                               kconsumer_data.cmd_unix_sock_path);
-       } else {
-               home_path = utils_get_home_dir();
-               if (home_path == NULL) {
-                       /* TODO: Add --socket PATH option */
-                       ERR("Can't get HOME directory for sockets creation.");
-                       retval = -1;
-                       goto exit_init_data;
-               }
-
-               /*
-                * Create rundir from home path. This will create something like
-                * $HOME/.lttng
-                */
-               ret = asprintf(&rundir, DEFAULT_LTTNG_HOME_RUNDIR, home_path);
-               if (ret < 0) {
+               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;
                }
-
-               if (create_lttng_rundir(rundir)) {
+               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;
                }
-
-               if (strlen(apps_unix_sock_path) == 0) {
-                       ret = snprintf(apps_unix_sock_path, PATH_MAX,
-                                       DEFAULT_HOME_APPS_UNIX_SOCK,
-                                       home_path);
-                       if (ret < 0) {
-                               retval = -1;
-                               goto exit_init_data;
-                       }
-               }
-
-               /* Set the cli tool unix socket path */
-               if (strlen(client_unix_sock_path) == 0) {
-                       ret = snprintf(client_unix_sock_path, PATH_MAX,
-                                       DEFAULT_HOME_CLIENT_UNIX_SOCK,
-                                       home_path);
-                       if (ret < 0) {
-                               retval = -1;
-                               goto exit_init_data;
-                       }
-               }
-
-               /* Set global SHM for ust */
-               if (strlen(wait_shm_path) == 0) {
-                       ret = snprintf(wait_shm_path, PATH_MAX,
-                                       DEFAULT_HOME_APPS_WAIT_SHM_PATH,
-                                       getuid());
-                       if (ret < 0) {
-                               retval = -1;
-                               goto exit_init_data;
-                       }
-               }
-
-               /* Set health check Unix path */
-               if (strlen(health_unix_sock_path) == 0) {
-                       ret = snprintf(health_unix_sock_path,
-                                       sizeof(health_unix_sock_path),
-                                       DEFAULT_HOME_HEALTH_UNIX_SOCK,
-                                       home_path);
-                       if (ret < 0) {
-                               retval = -1;
-                               goto exit_init_data;
-                       }
-               }
-       }
-
-       lockfile_fd = create_lockfile();
-       if (lockfile_fd < 0) {
-               retval = -1;
-               goto exit_init_data;
        }
 
        /* Set consumer initial state */
        kernel_consumerd_state = CONSUMER_STOPPED;
        ust_consumerd_state = CONSUMER_STOPPED;
 
-       DBG("Client socket path %s", client_unix_sock_path);
-       DBG("Application socket path %s", apps_unix_sock_path);
-       DBG("Application wait path %s", wait_shm_path);
-       DBG("LTTng run directory path: %s", rundir);
-
-       /* 32 bits consumerd path setup */
-       ret = snprintf(ustconsumer32_data.err_unix_sock_path, PATH_MAX,
-                       DEFAULT_USTCONSUMERD32_ERR_SOCK_PATH, rundir);
-       if (ret < 0) {
-               PERROR("snprintf 32-bit consumer error socket path");
+       ust32_channel_monitor_pipe = lttng_pipe_open(0);
+       if (!ust32_channel_monitor_pipe) {
+               ERR("Failed to create 32-bit user space consumer channel monitor pipe");
                retval = -1;
                goto exit_init_data;
        }
-       ret = snprintf(ustconsumer32_data.cmd_unix_sock_path, PATH_MAX,
-                       DEFAULT_USTCONSUMERD32_CMD_SOCK_PATH, rundir);
-       if (ret < 0) {
-               PERROR("snprintf 32-bit consumer command socket path");
+       ustconsumer32_data.channel_monitor_pipe = lttng_pipe_release_writefd(
+                       ust32_channel_monitor_pipe);
+       if (ustconsumer32_data.channel_monitor_pipe < 0) {
                retval = -1;
                goto exit_init_data;
        }
-
-       DBG2("UST consumer 32 bits err path: %s",
-                       ustconsumer32_data.err_unix_sock_path);
-       DBG2("UST consumer 32 bits cmd path: %s",
-                       ustconsumer32_data.cmd_unix_sock_path);
-
-       /* 64 bits consumerd path setup */
-       ret = snprintf(ustconsumer64_data.err_unix_sock_path, PATH_MAX,
-                       DEFAULT_USTCONSUMERD64_ERR_SOCK_PATH, rundir);
-       if (ret < 0) {
-               PERROR("snprintf 64-bit consumer error socket path");
+       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;
        }
-       ret = snprintf(ustconsumer64_data.cmd_unix_sock_path, PATH_MAX,
-                       DEFAULT_USTCONSUMERD64_CMD_SOCK_PATH, rundir);
-       if (ret < 0) {
-               PERROR("snprintf 64-bit consumer command socket path");
+       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;
        }
 
-       DBG2("UST consumer 64 bits err path: %s",
-                       ustconsumer64_data.err_unix_sock_path);
-       DBG2("UST consumer 64 bits cmd path: %s",
-                       ustconsumer64_data.cmd_unix_sock_path);
-
        /*
-        * See if daemon already exist.
+        * 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.
         */
-       if (check_existing_daemon()) {
-               ERR("Already running daemon.\n");
-               /*
-                * We do not goto exit because we must not cleanup()
-                * because a daemon is already running.
-                */
+       rotation_timer_queue = create_rotate_timer_queue();
+       if (!rotation_timer_queue) {
+               retval = -1;
+               goto exit_init_data;
+       }
+       timer_thread_ctx.rotation_timer_queue = rotation_timer_queue;
+
+       ust64_channel_monitor_pipe = lttng_pipe_open(0);
+       if (!ust64_channel_monitor_pipe) {
+               ERR("Failed to create 64-bit user space consumer channel monitor pipe");
+               retval = -1;
+               goto exit_init_data;
+       }
+       ustconsumer64_data.channel_monitor_pipe = lttng_pipe_release_writefd(
+                       ust64_channel_monitor_pipe);
+       if (ustconsumer64_data.channel_monitor_pipe < 0) {
+               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;
        }
@@ -5797,13 +6192,13 @@ int main(int argc, char **argv)
         * kernel tracer.
         */
        if (is_root) {
-               if (set_consumer_sockets(&kconsumer_data, rundir)) {
+               if (set_consumer_sockets(&kconsumer_data)) {
                        retval = -1;
                        goto exit_init_data;
                }
 
                /* Setup kernel tracer */
-               if (!opt_no_kernel) {
+               if (!config.no_kernel) {
                        init_kernel_tracer();
                        if (kernel_tracer_fd >= 0) {
                                ret = syscall_init_table();
@@ -5821,12 +6216,12 @@ int main(int argc, char **argv)
        /* init lttng_fd tracking must be done after set_ulimit. */
        lttng_fd_init();
 
-       if (set_consumer_sockets(&ustconsumer64_data, rundir)) {
+       if (set_consumer_sockets(&ustconsumer64_data)) {
                retval = -1;
                goto exit_init_data;
        }
 
-       if (set_consumer_sockets(&ustconsumer32_data, rundir)) {
+       if (set_consumer_sockets(&ustconsumer32_data)) {
                retval = -1;
                goto exit_init_data;
        }
@@ -5838,18 +6233,18 @@ int main(int argc, char **argv)
        }
 
        /* Set credentials to socket */
-       if (is_root && set_permissions(rundir)) {
+       if (is_root && set_permissions(config.rundir.value)) {
                retval = -1;
                goto exit_init_data;
        }
 
        /* Get parent pid if -S, --sig-parent is specified. */
-       if (opt_sig_parent) {
+       if (config.sig_parent) {
                ppid = getppid();
        }
 
        /* Setup the kernel pipe for waking up the kernel thread */
-       if (is_root && !opt_no_kernel) {
+       if (is_root && !config.no_kernel) {
                if (utils_create_pipe_cloexec(kernel_poll_pipe)) {
                        retval = -1;
                        goto exit_init_data;
@@ -5886,9 +6281,9 @@ int main(int argc, char **argv)
        /* Check for the application socket timeout env variable. */
        env_app_timeout = getenv(DEFAULT_APP_SOCKET_TIMEOUT_ENV);
        if (env_app_timeout) {
-               app_socket_timeout = atoi(env_app_timeout);
+               config.app_socket_timeout = atoi(env_app_timeout);
        } else {
-               app_socket_timeout = DEFAULT_APP_SOCKET_RW_TIMEOUT;
+               config.app_socket_timeout = DEFAULT_APP_SOCKET_RW_TIMEOUT;
        }
 
        ret = write_pidfile();
@@ -5897,12 +6292,6 @@ int main(int argc, char **argv)
                retval = -1;
                goto exit_init_data;
        }
-       ret = write_agent_port();
-       if (ret) {
-               ERR("Error in write_agent_port");
-               retval = -1;
-               goto exit_init_data;
-       }
 
        /* Initialize communication library */
        lttcomm_init();
@@ -5913,10 +6302,10 @@ int main(int argc, char **argv)
                retval = -1;
                goto exit_init_data;
        }
-       load_info->path = opt_load_session_path;
+       load_info->path = config.load_session_path.value;
 
-       /* Create health-check thread */
-       ret = pthread_create(&health_thread, NULL,
+       /* Create health-check thread. */
+       ret = pthread_create(&health_thread, default_pthread_attr(),
                        thread_manage_health, (void *) NULL);
        if (ret) {
                errno = ret;
@@ -5925,86 +6314,166 @@ int main(int argc, char **argv)
                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(&notification_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,
+                       &notification_thread_ready);
+       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(&notification_thread, default_pthread_attr(),
+                       thread_notification, notification_thread_handle);
+       if (ret) {
+               errno = ret;
+               PERROR("pthread_create notification");
+               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);
+       if (ret) {
+               errno = ret;
+               PERROR("pthread_create timer");
+               retval = -1;
+               stop_threads();
+               goto exit_notification;
+       }
+       timer_thread_launched = true;
+
+       /* 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,
+                       &notification_thread_ready);
+       if (!rotation_thread_handle) {
+               retval = -1;
+               ERR("Failed to create rotation thread shared data");
+               stop_threads();
+               goto exit_rotation;
+       }
+
+       /* Create rotation thread. */
+       ret = pthread_create(&rotation_thread, default_pthread_attr(),
+                       thread_rotation, rotation_thread_handle);
+       if (ret) {
+               errno = ret;
+               PERROR("pthread_create rotation");
+               retval = -1;
+               stop_threads();
+               goto exit_rotation;
+       }
+       rotation_thread_launched = true;
+
        /* Create thread to manage the client socket */
-       ret = pthread_create(&client_thread, NULL,
+       ret = pthread_create(&client_thread, default_pthread_attr(),
                        thread_manage_clients, (void *) NULL);
        if (ret) {
                errno = ret;
                PERROR("pthread_create clients");
                retval = -1;
+               stop_threads();
                goto exit_client;
        }
 
        /* Create thread to dispatch registration */
-       ret = pthread_create(&dispatch_thread, NULL,
+       ret = pthread_create(&dispatch_thread, default_pthread_attr(),
                        thread_dispatch_ust_registration, (void *) NULL);
        if (ret) {
                errno = ret;
                PERROR("pthread_create dispatch");
                retval = -1;
+               stop_threads();
                goto exit_dispatch;
        }
 
        /* Create thread to manage application registration. */
-       ret = pthread_create(&reg_apps_thread, NULL,
+       ret = pthread_create(&reg_apps_thread, default_pthread_attr(),
                        thread_registration_apps, (void *) NULL);
        if (ret) {
                errno = ret;
                PERROR("pthread_create registration");
                retval = -1;
+               stop_threads();
                goto exit_reg_apps;
        }
 
        /* Create thread to manage application socket */
-       ret = pthread_create(&apps_thread, NULL,
+       ret = pthread_create(&apps_thread, default_pthread_attr(),
                        thread_manage_apps, (void *) NULL);
        if (ret) {
                errno = ret;
                PERROR("pthread_create apps");
                retval = -1;
+               stop_threads();
                goto exit_apps;
        }
 
        /* Create thread to manage application notify socket */
-       ret = pthread_create(&apps_notify_thread, NULL,
+       ret = pthread_create(&apps_notify_thread, default_pthread_attr(),
                        ust_thread_manage_notify, (void *) NULL);
        if (ret) {
                errno = ret;
                PERROR("pthread_create notify");
                retval = -1;
+               stop_threads();
                goto exit_apps_notify;
        }
 
        /* Create agent registration thread. */
-       ret = pthread_create(&agent_reg_thread, NULL,
+       ret = pthread_create(&agent_reg_thread, default_pthread_attr(),
                        agent_thread_manage_registration, (void *) NULL);
        if (ret) {
                errno = ret;
                PERROR("pthread_create agent");
                retval = -1;
+               stop_threads();
                goto exit_agent_reg;
        }
 
        /* Don't start this thread if kernel tracing is not requested nor root */
-       if (is_root && !opt_no_kernel) {
+       if (is_root && !config.no_kernel) {
                /* Create kernel thread to manage kernel event */
-               ret = pthread_create(&kernel_thread, NULL,
+               ret = pthread_create(&kernel_thread, default_pthread_attr(),
                                thread_manage_kernel, (void *) NULL);
                if (ret) {
                        errno = ret;
                        PERROR("pthread_create kernel");
                        retval = -1;
+                       stop_threads();
                        goto exit_kernel;
                }
        }
 
        /* Create session loading thread. */
-       ret = pthread_create(&load_session_thread, NULL, thread_load_session,
-                       load_info);
+       ret = pthread_create(&load_session_thread, default_pthread_attr(),
+                       thread_load_session, load_info);
        if (ret) {
                errno = ret;
                PERROR("pthread_create load_session_thread");
                retval = -1;
+               stop_threads();
                goto exit_load_session;
        }
 
@@ -6021,7 +6490,7 @@ int main(int argc, char **argv)
        }
 exit_load_session:
 
-       if (is_root && !opt_no_kernel) {
+       if (is_root && !config.no_kernel) {
                ret = pthread_join(kernel_thread, &status);
                if (ret) {
                        errno = ret;
@@ -6081,60 +6550,111 @@ exit_dispatch:
                PERROR("pthread_join");
                retval = -1;
        }
-exit_client:
 
+exit_client:
+exit_rotation:
+exit_notification:
+       sem_destroy(&notification_thread_ready);
        ret = pthread_join(health_thread, &status);
        if (ret) {
                errno = ret;
                PERROR("pthread_join health thread");
                retval = -1;
        }
-exit_health:
 
+exit_health:
 exit_init_data:
+       /*
+        * Wait for all pending call_rcu work to complete before tearing
+        * down data structures. call_rcu worker may be trying to
+        * perform lookups in those structures.
+        */
+       rcu_barrier();
        /*
         * sessiond_cleanup() is called when no other thread is running, except
         * the ht_cleanup thread, which is needed to destroy the hash tables.
         */
        rcu_thread_online();
        sessiond_cleanup();
-       rcu_thread_offline();
-       rcu_unregister_thread();
 
-       ret = notify_thread_pipe(ht_cleanup_quit_pipe[1]);
-       if (ret < 0) {
-               ERR("write error on ht_cleanup quit pipe");
-               retval = -1;
+       /*
+        * Ensure all prior call_rcu are done. call_rcu callbacks may push
+        * hash tables to the ht_cleanup thread. Therefore, we ensure that
+        * the queue is empty before shutting down the clean-up thread.
+        */
+       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);
        }
 
-       ret = pthread_join(ht_cleanup_thread, &status);
-       if (ret) {
-               errno = ret;
-               PERROR("pthread_join ht cleanup thread");
-               retval = -1;
+       if (rotation_thread_handle) {
+               if (rotation_thread_launched) {
+                       ret = pthread_join(rotation_thread, &status);
+                       if (ret) {
+                               errno = ret;
+                               PERROR("pthread_join rotation thread");
+                               retval = -1;
+                       }
+               }
+               rotation_thread_handle_destroy(rotation_thread_handle);
        }
-exit_ht_cleanup:
-exit_set_max_size:
 
-       utils_close_pipe(ht_cleanup_pipe);
-exit_ht_cleanup_pipe:
+       if (timer_thread_launched) {
+               kill(getpid(), LTTNG_SESSIOND_SIG_EXIT);
+               ret = pthread_join(timer_thread, &status);
+               if (ret) {
+                       errno = ret;
+                       PERROR("pthread_join timer thread");
+                       retval = -1;
+               }
+       }
 
        /*
-        * Close the ht_cleanup quit pipe.
+        * After the rotation and timer thread have quit, we can safely destroy
+        * the rotation_timer_queue.
         */
-       utils_close_pipe(ht_cleanup_quit_pipe);
-exit_ht_cleanup_quit_pipe:
+       destroy_rotate_timer_queue(rotation_timer_queue);
+
+       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);
+       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);
 exit_health_sessiond_cleanup:
+exit_create_run_as_worker_cleanup:
 
 exit_options:
+       sessiond_cleanup_lock_file();
        sessiond_cleanup_options();
 
 exit_set_signal_handler:
-       /* Ensure all prior call_rcu are done. */
-       rcu_barrier();
-
        if (!retval) {
                exit(EXIT_SUCCESS);
        } else {
This page took 0.103649 seconds and 5 git commands to generate.