Add the sessiond notification-handling subsystem
[lttng-tools.git] / src / bin / lttng-sessiond / main.c
index 21b4cf326bd49e48f0f2a1b2caeee34554578981..deb2edf9ba1e5189cd1d9e40934abfc3da1e43ab 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,7 @@
 #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 "lttng-sessiond.h"
 #include "buffer-registry.h"
@@ -73,6 +72,8 @@
 #include "save.h"
 #include "load-session-thread.h"
 #include "syscall.h"
+#include "agent.h"
+#include "ht-cleanup.h"
 
 #define CONSUMERD_FILE "lttng-consumerd"
 
@@ -133,34 +134,34 @@ static struct consumer_data ustconsumer32_data = {
 
 /* Command line options */
 static const struct option long_options[] = {
-       { "client-sock", 1, 0, 'c' },
-       { "apps-sock", 1, 0, 'a' },
-       { "kconsumerd-cmd-sock", 1, 0, 'C' },
-       { "kconsumerd-err-sock", 1, 0, 'E' },
-       { "ustconsumerd32-cmd-sock", 1, 0, 'G' },
-       { "ustconsumerd32-err-sock", 1, 0, 'H' },
-       { "ustconsumerd64-cmd-sock", 1, 0, 'D' },
-       { "ustconsumerd64-err-sock", 1, 0, 'F' },
-       { "consumerd32-path", 1, 0, 'u' },
-       { "consumerd32-libdir", 1, 0, 'U' },
-       { "consumerd64-path", 1, 0, 't' },
-       { "consumerd64-libdir", 1, 0, 'T' },
-       { "daemonize", 0, 0, 'd' },
-       { "background", 0, 0, 'b' },
-       { "sig-parent", 0, 0, 'S' },
-       { "help", 0, 0, 'h' },
-       { "group", 1, 0, 'g' },
-       { "version", 0, 0, 'V' },
-       { "quiet", 0, 0, 'q' },
-       { "verbose", 0, 0, 'v' },
-       { "verbose-consumer", 0, 0, 'Z' },
-       { "no-kernel", 0, 0, 'N' },
-       { "pidfile", 1, 0, 'p' },
-       { "agent-tcp-port", 1, 0, 'J' },
-       { "config", 1, 0, 'f' },
-       { "load", 1, 0, 'l' },
-       { "kmod-probes", 1, 0, 'P' },
-       { "extra-kmod-probes", 1, 0, 'e' },
+       { "client-sock", required_argument, 0, 'c' },
+       { "apps-sock", required_argument, 0, 'a' },
+       { "kconsumerd-cmd-sock", required_argument, 0, '\0' },
+       { "kconsumerd-err-sock", required_argument, 0, '\0' },
+       { "ustconsumerd32-cmd-sock", required_argument, 0, '\0' },
+       { "ustconsumerd32-err-sock", required_argument, 0, '\0' },
+       { "ustconsumerd64-cmd-sock", required_argument, 0, '\0' },
+       { "ustconsumerd64-err-sock", required_argument, 0, '\0' },
+       { "consumerd32-path", required_argument, 0, '\0' },
+       { "consumerd32-libdir", required_argument, 0, '\0' },
+       { "consumerd64-path", required_argument, 0, '\0' },
+       { "consumerd64-libdir", required_argument, 0, '\0' },
+       { "daemonize", no_argument, 0, 'd' },
+       { "background", no_argument, 0, 'b' },
+       { "sig-parent", no_argument, 0, 'S' },
+       { "help", no_argument, 0, 'h' },
+       { "group", required_argument, 0, 'g' },
+       { "version", no_argument, 0, 'V' },
+       { "quiet", no_argument, 0, 'q' },
+       { "verbose", no_argument, 0, 'v' },
+       { "verbose-consumer", no_argument, 0, '\0' },
+       { "no-kernel", no_argument, 0, '\0' },
+       { "pidfile", required_argument, 0, 'p' },
+       { "agent-tcp-port", required_argument, 0, '\0' },
+       { "config", required_argument, 0, 'f' },
+       { "load", required_argument, 0, 'l' },
+       { "kmod-probes", required_argument, 0, '\0' },
+       { "extra-kmod-probes", required_argument, 0, '\0' },
        { NULL, 0, 0, 0 }
 };
 
@@ -190,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
@@ -305,6 +305,9 @@ const char * const config_section_name = "sessiond";
 /* Load session thread information to operate. */
 struct load_session_thread_data *load_info;
 
+/* 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
@@ -313,6 +316,11 @@ struct load_session_thread_data *load_info;
 #define NR_LTTNG_SESSIOND_READY                3
 int lttng_sessiond_ready = NR_LTTNG_SESSIOND_READY;
 
+int sessiond_check_thread_quit_pipe(int fd, uint32_t events)
+{
+       return (fd == thread_quit_pipe[0] && (events & LPOLLIN)) ? 1 : 0;
+}
+
 /* Notify parents that we are ready for cmd and health check */
 LTTNG_HIDDEN
 void sessiond_notify_ready(void)
@@ -419,47 +427,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.
  *
@@ -492,11 +459,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.
  */
@@ -582,6 +544,34 @@ static int generate_lock_file_path(char *path, size_t len)
        return ret;
 }
 
+/*
+ * Wait on consumer process termination.
+ *
+ * Need to be called with the consumer data lock held or from a context
+ * ensuring no concurrent access to data (e.g: cleanup).
+ */
+static void wait_consumer(struct consumer_data *consumer_data)
+{
+       pid_t ret;
+       int status;
+
+       if (consumer_data->pid <= 0) {
+               return;
+       }
+
+       DBG("Waiting for complete teardown of consumerd (PID: %d)",
+                       consumer_data->pid);
+       ret = waitpid(consumer_data->pid, &status, 0);
+       if (ret == -1) {
+               PERROR("consumerd waitpid pid: %d", consumer_data->pid)
+       }
+       if (!WIFEXITED(status)) {
+               ERR("consumerd termination with error: %d",
+                               WEXITSTATUS(ret));
+       }
+       consumer_data->pid = 0;
+}
+
 /*
  * Cleanup the session daemon's data structures.
  */
@@ -676,6 +666,13 @@ static void sessiond_cleanup(void)
                }
        }
 
+       wait_consumer(&kconsumer_data);
+       wait_consumer(&ustconsumer64_data);
+       wait_consumer(&ustconsumer32_data);
+
+       DBG("Cleaning up all agent apps");
+       agent_app_ht_clean();
+
        DBG("Closing all UST sockets");
        ust_app_clean_list();
        buffer_reg_destroy_registries();
@@ -762,11 +759,7 @@ static void sessiond_cleanup_options(void)
        free(kmod_probes_list);
        free(kmod_extra_probes_list);
 
-       /* <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();
 }
 
 /*
@@ -832,34 +825,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.
@@ -994,10 +1010,30 @@ static void update_ust_app(int app_sock)
 
        /* For all tracing session(s) */
        cds_list_for_each_entry_safe(sess, stmp, &session_list_ptr->head, list) {
+               struct ust_app *app;
+
                session_lock(sess);
-               if (sess->ust_session) {
-                       ust_app_global_update(sess->ust_session, app_sock);
+               if (!sess->ust_session) {
+                       goto unlock_session;
                }
+
+               rcu_read_lock();
+               assert(app_sock >= 0);
+               app = ust_app_find_by_sock(app_sock);
+               if (app == NULL) {
+                       /*
+                        * Application can be unregistered before so
+                        * this is possible hence simply stopping the
+                        * update.
+                        */
+                       DBG3("UST app update failed to find app sock %d",
+                               app_sock);
+                       goto unlock_rcu;
+               }
+               ust_app_global_update(sess->ust_session, app);
+       unlock_rcu:
+               rcu_read_unlock();
+       unlock_session:
                session_unlock(sess);
        }
 }
@@ -1106,31 +1142,33 @@ static void *thread_manage_kernel(void *data)
                        }
 
                        /* Check for data on kernel pipe */
-                       if (pollfd == kernel_poll_pipe[0] && (revents & LPOLLIN)) {
-                               (void) lttng_read(kernel_poll_pipe[0],
-                                       &tmp, 1);
-                               /*
-                                * Ret value is useless here, if this pipe gets any actions an
-                                * update is required anyway.
-                                */
-                               update_poll_flag = 1;
-                               continue;
-                       } else {
-                               /*
-                                * New CPU detected by the kernel. Adding kernel stream to
-                                * kernel session and updating the kernel consumer
-                                */
-                               if (revents & LPOLLIN) {
+                       if (revents & LPOLLIN) {
+                               if (pollfd == kernel_poll_pipe[0]) {
+                                       (void) lttng_read(kernel_poll_pipe[0],
+                                               &tmp, 1);
+                                       /*
+                                        * Ret value is useless here, if this pipe gets any actions an
+                                        * update is required anyway.
+                                        */
+                                       update_poll_flag = 1;
+                                       continue;
+                               } else {
+                                       /*
+                                        * New CPU detected by the kernel. Adding kernel stream to
+                                        * kernel session and updating the kernel consumer
+                                        */
                                        ret = update_kernel_stream(&kconsumer_data, pollfd);
                                        if (ret < 0) {
                                                continue;
                                        }
                                        break;
-                                       /*
-                                        * TODO: We might want to handle the LPOLLERR | LPOLLHUP
-                                        * and unregister kernel stream at this point.
-                                        */
                                }
+                       } else if (revents & (LPOLLERR | LPOLLHUP | LPOLLRDHUP)) {
+                               update_poll_flag = 1;
+                               continue;
+                       } else {
+                               ERR("Unexpected poll events %u for sock %d", revents, pollfd);
+                               goto error;
                        }
                }
        }
@@ -1189,6 +1227,9 @@ static void *thread_manage_consumer(void *data)
 
        DBG("[thread] Manage consumer started");
 
+       rcu_register_thread();
+       rcu_thread_online();
+
        health_register(health_sessiond, HEALTH_SESSIOND_TYPE_CONSUMER);
 
        health_code_update();
@@ -1257,9 +1298,14 @@ restart:
 
                /* Event on the registration socket */
                if (pollfd == consumer_data->err_sock) {
-                       if (revents & (LPOLLERR | LPOLLHUP | LPOLLRDHUP)) {
+                       if (revents & LPOLLIN) {
+                               continue;
+                       } else if (revents & (LPOLLERR | LPOLLHUP | LPOLLRDHUP)) {
                                ERR("consumer err socket poll error");
                                goto error;
+                       } else {
+                               ERR("Unexpected poll events %u for sock %d", revents, pollfd);
+                               goto error;
                        }
                }
        }
@@ -1305,7 +1351,6 @@ restart:
                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);
@@ -1389,7 +1434,8 @@ restart_poll:
 
                        if (pollfd == sock) {
                                /* Event on the consumerd socket */
-                               if (revents & (LPOLLERR | LPOLLHUP | LPOLLRDHUP)) {
+                               if (revents & (LPOLLERR | LPOLLHUP | LPOLLRDHUP)
+                                               && !(revents & LPOLLIN)) {
                                        ERR("consumer err socket second poll error");
                                        goto error;
                                }
@@ -1407,6 +1453,11 @@ restart_poll:
 
                                goto exit;
                        } else if (pollfd == consumer_data->metadata_fd) {
+                               if (revents & (LPOLLERR | LPOLLHUP | LPOLLRDHUP)
+                                               && !(revents & LPOLLIN)) {
+                                       ERR("consumer err metadata socket second poll error");
+                                       goto error;
+                               }
                                /* UST metadata requests */
                                ret = ust_consumer_metadata_request(
                                                &consumer_data->metadata_sock);
@@ -1470,7 +1521,6 @@ error:
 
        unlink(consumer_data->err_unix_sock_path);
        unlink(consumer_data->cmd_unix_sock_path);
-       consumer_data->pid = 0;
        pthread_mutex_unlock(&consumer_data->lock);
 
        /* Cleanup metadata socket mutex. */
@@ -1487,6 +1537,9 @@ error_poll:
        health_unregister(health_sessiond);
        DBG("consumer thread cleanup completed");
 
+       rcu_thread_offline();
+       rcu_unregister_thread();
+
        return NULL;
 }
 
@@ -1572,10 +1625,7 @@ static void *thread_manage_apps(void *data)
 
                        /* Inspect the apps cmd pipe */
                        if (pollfd == apps_cmd_pipe[0]) {
-                               if (revents & (LPOLLERR | LPOLLHUP | LPOLLRDHUP)) {
-                                       ERR("Apps command pipe error");
-                                       goto error;
-                               } else if (revents & LPOLLIN) {
+                               if (revents & LPOLLIN) {
                                        int sock;
 
                                        /* Empty pipe */
@@ -1588,9 +1638,8 @@ static void *thread_manage_apps(void *data)
                                        health_code_update();
 
                                        /*
-                                        * We only monitor the error events of the socket. This
-                                        * thread does not handle any incoming data from UST
-                                        * (POLLIN).
+                                        * Since this is a command socket (write then read),
+                                        * we only monitor the error events of the socket.
                                         */
                                        ret = lttng_poll_add(&events, sock,
                                                        LPOLLERR | LPOLLHUP | LPOLLRDHUP);
@@ -1599,6 +1648,12 @@ static void *thread_manage_apps(void *data)
                                        }
 
                                        DBG("Apps with sock %d added to poll set", sock);
+                               } else if (revents & (LPOLLERR | LPOLLHUP | LPOLLRDHUP)) {
+                                       ERR("Apps command pipe error");
+                                       goto error;
+                               } else {
+                                       ERR("Unknown poll events %u for sock %d", revents, pollfd);
+                                       goto error;
                                }
                        } else {
                                /*
@@ -1614,6 +1669,9 @@ static void *thread_manage_apps(void *data)
 
                                        /* Socket closed on remote end. */
                                        ust_app_unregister(pollfd);
+                               } else {
+                                       ERR("Unexpected poll events %u for sock %d", revents, pollfd);
+                                       goto error;
                                }
                        }
 
@@ -1754,7 +1812,16 @@ static void sanitize_wait_queue(struct ust_reg_wait_queue *wait_queue)
                                wait_queue->count--;
                                ust_app_destroy(wait_node->app);
                                free(wait_node);
+                               /*
+                                * Silence warning of use-after-free in
+                                * cds_list_for_each_entry_safe which uses
+                                * __typeof__(*wait_node).
+                                */
+                               wait_node = NULL;
                                break;
+                       } else {
+                               ERR("Unexpected poll events %u for sock %d", revents, pollfd);
+                               goto error;
                        }
                }
        }
@@ -1788,6 +1855,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)) {
@@ -1956,7 +2025,7 @@ static void *thread_dispatch_ust_registration(void *data)
                                 * Don't care about return value. Let the manage apps threads
                                 * handle app unregistration upon socket close.
                                 */
-                               (void) ust_app_register_done(app->sock);
+                               (void) ust_app_register_done(app);
 
                                /*
                                 * Even if the application socket has been closed, send the app
@@ -1998,6 +2067,22 @@ error:
                free(wait_node);
        }
 
+       /* Empty command queue. */
+       for (;;) {
+               /* Dequeue command for registration */
+               node = cds_wfcq_dequeue_blocking(&ust_cmd_queue.head, &ust_cmd_queue.tail);
+               if (node == NULL) {
+                       break;
+               }
+               ust_cmd = caa_container_of(node, struct ust_command, node);
+               ret = close(ust_cmd->sock);
+               if (ret < 0) {
+                       PERROR("close ust sock exit dispatch %d", ust_cmd->sock);
+               }
+               lttng_fd_put(LTTNG_FD_APPS, 1);
+               free(ust_cmd);
+       }
+
 error_testpoint:
        DBG("Dispatch thread dying");
        if (err) {
@@ -2005,6 +2090,7 @@ error_testpoint:
                ERR("Health error occurred in %s", __func__);
        }
        health_unregister(health_sessiond);
+       rcu_unregister_thread();
        return NULL;
 }
 
@@ -2099,10 +2185,7 @@ static void *thread_registration_apps(void *data)
 
                        /* Event on the registration socket */
                        if (pollfd == apps_sock) {
-                               if (revents & (LPOLLERR | LPOLLHUP | LPOLLRDHUP)) {
-                                       ERR("Register apps socket poll error");
-                                       goto error;
-                               } else if (revents & LPOLLIN) {
+                               if (revents & LPOLLIN) {
                                        sock = lttcomm_accept_unix_sock(apps_sock);
                                        if (sock < 0) {
                                                goto error;
@@ -2115,10 +2198,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 (app_socket_timeout >= 0) {
+                                               (void) lttcomm_setsockopt_rcv_timeout(sock,
+                                                               app_socket_timeout * 1000);
+                                               (void) lttcomm_setsockopt_snd_timeout(sock,
+                                                               app_socket_timeout * 1000);
+                                       }
 
                                        /*
                                         * Set the CLOEXEC flag. Return code is useless because
@@ -2130,6 +2215,10 @@ static void *thread_registration_apps(void *data)
                                        ust_cmd = zmalloc(sizeof(struct ust_command));
                                        if (ust_cmd == NULL) {
                                                PERROR("ust command zmalloc");
+                                               ret = close(sock);
+                                               if (ret) {
+                                                       PERROR("close");
+                                               }
                                                goto error;
                                        }
 
@@ -2185,6 +2274,12 @@ static void *thread_registration_apps(void *data)
                                         * barrier with the exchange in cds_wfcq_enqueue.
                                         */
                                        futex_nto1_wake(&ust_cmd_queue.futex);
+                               } else if (revents & (LPOLLERR | LPOLLHUP | LPOLLRDHUP)) {
+                                       ERR("Register apps socket poll error");
+                                       goto error;
+                               } else {
+                                       ERR("Unexpected poll events %u for sock %d", revents, pollfd);
+                                       goto error;
                                }
                        }
                }
@@ -2234,7 +2329,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 */
@@ -2264,8 +2364,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");
@@ -2277,7 +2377,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
@@ -2686,7 +2786,7 @@ static int copy_session_consumer(int domain, struct ltt_session *session)
                 * domain.
                 */
                if (session->kernel_session->consumer) {
-                       consumer_destroy_output(session->kernel_session->consumer);
+                       consumer_output_put(session->kernel_session->consumer);
                }
                session->kernel_session->consumer =
                        consumer_copy_output(session->consumer);
@@ -2700,7 +2800,7 @@ static int copy_session_consumer(int domain, struct ltt_session *session)
        case LTTNG_DOMAIN_UST:
                DBG3("Copying tracing session consumer output in UST session");
                if (session->ust_session->consumer) {
-                       consumer_destroy_output(session->ust_session->consumer);
+                       consumer_output_put(session->ust_session->consumer);
                }
                session->ust_session->consumer =
                        consumer_copy_output(session->consumer);
@@ -2820,7 +2920,7 @@ static int create_kernel_session(struct ltt_session *session)
                                session->kernel_session->consumer->dst.trace_path,
                                S_IRWXU | S_IRWXG, session->uid, session->gid);
                if (ret < 0) {
-                       if (ret != -EEXIST) {
+                       if (errno != EEXIST) {
                                ERR("Trace directory creation error");
                                goto error;
                        }
@@ -2882,6 +2982,8 @@ static int process_client_msg(struct command_ctx *cmd_ctx, int sock,
 
        DBG("Processing client command %d", cmd_ctx->lsm->cmd_type);
 
+       assert(!rcu_read_ongoing());
+
        *sock_error = 0;
 
        switch (cmd_ctx->lsm->cmd_type) {
@@ -2900,6 +3002,8 @@ 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:
                need_domain = 0;
                break;
        default:
@@ -2940,10 +3044,12 @@ static int process_client_msg(struct command_ctx *cmd_ctx, int sock,
        case LTTNG_LIST_CHANNELS:
        case LTTNG_LIST_EVENTS:
        case LTTNG_LIST_SYSCALLS:
+       case LTTNG_LIST_TRACKER_PIDS:
+       case LTTNG_DATA_PENDING:
                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;
@@ -2955,7 +3061,6 @@ 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:
@@ -3176,6 +3281,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:
@@ -3191,6 +3298,9 @@ skip_domain:
                                goto error;
                        }
                        break;
+               default:
+                       ret = LTTNG_ERR_UNKNOWN_DOMAIN;
+                       goto error;
                }
        }
 
@@ -3226,9 +3336,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:
@@ -3239,8 +3414,34 @@ skip_domain:
        }
        case LTTNG_DISABLE_EVENT:
        {
+
+               /*
+                * FIXME: handle filter; for now we just receive the filter's
+                * bytecode along with the filter expression which are sent by
+                * liblttng-ctl and discard them.
+                *
+                * This fixes an issue where the client may block while sending
+                * the filter payload and encounter an error because the session
+                * daemon closes the socket without ever handling this data.
+                */
+               size_t count = cmd_ctx->lsm->u.disable.expression_len +
+                       cmd_ctx->lsm->u.disable.bytecode_len;
+
+               if (count) {
+                       char data[LTTNG_FILTER_MAX_LEN];
+
+                       DBG("Discarding disable event command payload of size %zu", count);
+                       while (count) {
+                               ret = lttcomm_recv_unix_sock(sock, data,
+                                       count > sizeof(data) ? sizeof(data) : count);
+                               if (ret < 0) {
+                                       goto error;
+                               }
+
+                               count -= (size_t) ret;
+                       }
+               }
                /* FIXME: passing packed structure to non-packed pointer */
-               /* TODO: handle filter */
                ret = cmd_disable_event(cmd_ctx->session, cmd_ctx->lsm->domain.type,
                                cmd_ctx->lsm->u.disable.channel_name,
                                &cmd_ctx->lsm->u.disable.event);
@@ -3394,18 +3595,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;
        }
@@ -3428,19 +3625,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;
        }
@@ -3460,17 +3652,41 @@ 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);
+               ret = LTTNG_OK;
+               break;
+       }
+       case LTTNG_LIST_TRACKER_PIDS:
+       {
+               int32_t *pids = NULL;
+               ssize_t nr_pids;
 
-               free(events);
+               nr_pids = cmd_list_tracker_pids(cmd_ctx->session,
+                               cmd_ctx->lsm->domain.type, &pids);
+               if (nr_pids < 0) {
+                       /* Return value is a negative lttng_error_code. */
+                       ret = -nr_pids;
+                       goto error;
+               }
+
+               /*
+                * 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_no_cmd_header(cmd_ctx, pids,
+                       sizeof(int32_t) * nr_pids);
+               free(pids);
+
+               if (ret < 0) {
+                       goto setup_error;
+               }
 
                ret = LTTNG_OK;
                break;
@@ -3585,46 +3801,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;
        }
@@ -3632,61 +3840,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;
@@ -3706,7 +3921,44 @@ skip_domain:
        }
        case LTTNG_DATA_PENDING:
        {
-               ret = cmd_data_pending(cmd_ctx->session);
+               int pending_ret;
+               uint8_t pending_ret_byte;
+
+               pending_ret = cmd_data_pending(cmd_ctx->session);
+
+               /*
+                * FIXME
+                *
+                * This function may returns 0 or 1 to indicate whether or not
+                * there is data pending. In case of error, it should return an
+                * LTTNG_ERR code. However, some code paths may still return
+                * a nondescript error code, which we handle by returning an
+                * "unknown" error.
+                */
+               if (pending_ret == 0 || pending_ret == 1) {
+                       /*
+                        * 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;
+               } else {
+                       ret = pending_ret;
+                       goto setup_error;
+               }
+
+               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:
@@ -3719,13 +3971,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;
        }
@@ -3746,20 +3998,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;
        }
@@ -3860,6 +4107,16 @@ skip_domain:
                                cmd_ctx->lsm->u.set_shm_path.shm_path);
                break;
        }
+       case LTTNG_REGENERATE_METADATA:
+       {
+               ret = cmd_regenerate_metadata(cmd_ctx->session);
+               break;
+       }
+       case LTTNG_REGENERATE_STATEDUMP:
+       {
+               ret = cmd_regenerate_statedump(cmd_ctx->session);
+               break;
+       }
        default:
                ret = LTTNG_ERR_UND;
                break;
@@ -3868,7 +4125,7 @@ skip_domain:
 error:
        if (cmd_ctx->llm == NULL) {
                DBG("Missing llm structure. Allocating one.");
-               if (setup_lttng_msg(cmd_ctx, 0) < 0) {
+               if (setup_lttng_msg_no_cmd_header(cmd_ctx, NULL, 0) < 0) {
                        goto setup_error;
                }
        }
@@ -3882,6 +4139,7 @@ setup_error:
                session_unlock_list();
        }
 init_setup_error:
+       assert(!rcu_read_ongoing());
        return ret;
 }
 
@@ -3907,7 +4165,6 @@ static void *thread_manage_health(void *data)
        sock = lttcomm_create_unix_sock(health_unix_sock_path);
        if (sock < 0) {
                ERR("Unable to create health check Unix socket");
-               ret = -1;
                goto error;
        }
 
@@ -3918,7 +4175,6 @@ static void *thread_manage_health(void *data)
                if (ret < 0) {
                        ERR("Unable to set group on %s", health_unix_sock_path);
                        PERROR("chown");
-                       ret = -1;
                        goto error;
                }
 
@@ -3927,7 +4183,6 @@ static void *thread_manage_health(void *data)
                if (ret < 0) {
                        ERR("Unable to set permissions on %s", health_unix_sock_path);
                        PERROR("chmod");
-                       ret = -1;
                        goto error;
                }
        }
@@ -3997,9 +4252,14 @@ restart:
 
                        /* Event on the registration socket */
                        if (pollfd == sock) {
-                               if (revents & (LPOLLERR | LPOLLHUP | LPOLLRDHUP)) {
+                               if (revents & LPOLLIN) {
+                                       continue;
+                               } else if (revents & (LPOLLERR | LPOLLHUP | LPOLLRDHUP)) {
                                        ERR("Health socket poll error");
                                        goto error;
+                               } else {
+                                       ERR("Unexpected poll events %u for sock %d", revents, pollfd);
+                                       goto error;
                                }
                        }
                }
@@ -4023,7 +4283,6 @@ restart:
                        if (ret) {
                                PERROR("close");
                        }
-                       new_sock = -1;
                        continue;
                }
 
@@ -4052,7 +4311,6 @@ restart:
                if (ret) {
                        PERROR("close");
                }
-               new_sock = -1;
        }
 
 exit:
@@ -4070,7 +4328,7 @@ error:
        }
 
        lttng_poll_clean(&events);
-
+       stop_threads();
        rcu_unregister_thread();
        return NULL;
 }
@@ -4174,9 +4432,14 @@ static void *thread_manage_clients(void *data)
 
                        /* Event on the registration socket */
                        if (pollfd == client_sock) {
-                               if (revents & (LPOLLERR | LPOLLHUP | LPOLLRDHUP)) {
+                               if (revents & LPOLLIN) {
+                                       continue;
+                               } else if (revents & (LPOLLERR | LPOLLHUP | LPOLLRDHUP)) {
                                        ERR("Client socket poll error");
                                        goto error;
+                               } else {
+                                       ERR("Unexpected poll events %u for sock %d", revents, pollfd);
+                                       goto error;
                                }
                        }
                }
@@ -4273,9 +4536,10 @@ static void *thread_manage_clients(void *data)
 
                health_code_update();
 
-               DBG("Sending response (size: %d, retcode: %s)",
+               DBG("Sending response (size: %d, retcode: %s (%d))",
                                cmd_ctx->lttng_msg_size,
-                               lttng_strerror(-cmd_ctx->llm->ret_code));
+                               lttng_strerror(-cmd_ctx->llm->ret_code),
+                               cmd_ctx->llm->ret_code);
                ret = send_unix_sock(sock, cmd_ctx->llm, cmd_ctx->lttng_msg_size);
                if (ret < 0) {
                        ERR("Failed to send data back to client");
@@ -4350,41 +4614,9 @@ error_create_poll:
        return NULL;
 }
 
-
-/*
- * usage function on stderr
- */
-static void usage(void)
+static int string_match(const char *str1, const char *str2)
 {
-       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");
+       return (str1 && str2) && !strcmp(str1, str2);
 }
 
 /*
@@ -4397,47 +4629,37 @@ 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;
-       }
-
-       switch (opt) {
-       case 0:
-               fprintf(stderr, "option %s", optname);
-               if (arg) {
-                       fprintf(stderr, " with arg %s\n", arg);
+       if (string_match(optname, "client-sock") || opt == 'c') {
+               if (!arg || *arg == '\0') {
+                       ret = -EINVAL;
+                       goto end;
                }
-               break;
-       case 'c':
                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);
                }
-               break;
-       case 'a':
+       } 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);
                }
-               break;
-       case 'd':
+       } else if (string_match(optname, "daemonize") || opt == 'd') {
                opt_daemon = 1;
-               break;
-       case 'b':
+       } else if (string_match(optname, "background") || opt == 'b') {
                opt_background = 1;
-               break;
-       case 'g':
+       } 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");
@@ -4457,71 +4679,89 @@ static int set_option(int opt, const char *arg, const char *optname)
                        }
                        tracing_group_name_override = 1;
                }
-               break;
-       case 'h':
-               usage();
-               exit(EXIT_SUCCESS);
-       case 'V':
+       } else if (string_match(optname, "help") || opt == 'h') {
+               ret = utils_show_man_page(8, "lttng-sessiond");
+               if (ret) {
+                       ERR("Cannot view man page lttng-sessiond(8)");
+                       perror("exec");
+               }
+               exit(ret ? EXIT_FAILURE : EXIT_SUCCESS);
+       } else if (string_match(optname, "version") || opt == 'V') {
                fprintf(stdout, "%s\n", VERSION);
                exit(EXIT_SUCCESS);
-       case 'S':
+       } else if (string_match(optname, "sig-parent") || opt == 'S') {
                opt_sig_parent = 1;
-               break;
-       case 'E':
+       } 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);
                }
-               break;
-       case 'C':
+       } 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);
                }
-               break;
-       case 'F':
+       } 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);
                }
-               break;
-       case 'D':
+       } 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);
                }
-               break;
-       case 'H':
+       } 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);
                }
-               break;
-       case 'G':
+       } 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);
                }
-               break;
-       case 'N':
+       } else if (string_match(optname, "no-kernel")) {
                opt_no_kernel = 1;
-               break;
-       case 'q':
+       } else if (string_match(optname, "quiet") || opt == 'q') {
                lttng_opt_quiet = 1;
-               break;
-       case 'v':
+       } else if (string_match(optname, "verbose") || opt == 'v') {
                /* Verbose level can increase using multiple -v */
                if (arg) {
                        /* Value obtained from config file */
@@ -4533,15 +4773,17 @@ static int set_option(int opt, const char *arg, const char *optname)
                /* Clamp value to [0, 3] */
                lttng_opt_verbose = lttng_opt_verbose < 0 ? 0 :
                        (lttng_opt_verbose <= 3 ? lttng_opt_verbose : 3);
-               break;
-       case 'Z':
+       } else if (string_match(optname, "verbose-consumer")) {
                if (arg) {
                        opt_verbose_consumer = config_parse_value(arg);
                } else {
-                       opt_verbose_consumer += 1;
+                       opt_verbose_consumer++;
+               }
+       } else if (string_match(optname, "consumerd32-path")) {
+               if (!arg || *arg == '\0') {
+                       ret = -EINVAL;
+                       goto end;
                }
-               break;
-       case 'u':
                if (lttng_is_setuid_setgid()) {
                        WARN("Getting '%s' argument from setuid/setgid binary refused for security reasons.",
                                "--consumerd32-path");
@@ -4556,8 +4798,11 @@ static int set_option(int opt, const char *arg, const char *optname)
                        }
                        consumerd32_bin_override = 1;
                }
-               break;
-       case 'U':
+       } 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");
@@ -4572,8 +4817,11 @@ static int set_option(int opt, const char *arg, const char *optname)
                        }
                        consumerd32_libdir_override = 1;
                }
-               break;
-       case 't':
+       } 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");
@@ -4588,8 +4836,11 @@ static int set_option(int opt, const char *arg, const char *optname)
                        }
                        consumerd64_bin_override = 1;
                }
-               break;
-       case 'T':
+       } 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");
@@ -4604,8 +4855,11 @@ static int set_option(int opt, const char *arg, const char *optname)
                        }
                        consumerd64_libdir_override = 1;
                }
-               break;
-       case 'p':
+       } 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");
@@ -4617,19 +4871,17 @@ static int set_option(int opt, const char *arg, const char *optname)
                                ret = -ENOMEM;
                        }
                }
-               break;
-       case 'J': /* Agent TCP port. */
-       {
+       } 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])) {
@@ -4643,9 +4895,11 @@ static int set_option(int opt, const char *arg, const char *optname)
                        agent_tcp_port = (uint32_t) v;
                        DBG3("Agent TCP port set to non default: %u", agent_tcp_port);
                }
-               break;
-       }
-       case 'l':
+       } 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");
@@ -4657,8 +4911,11 @@ static int set_option(int opt, const char *arg, const char *optname)
                                ret = -ENOMEM;
                        }
                }
-               break;
-       case 'P': /* probe modules list */
+       } 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");
@@ -4670,8 +4927,11 @@ static int set_option(int opt, const char *arg, const char *optname)
                                ret = -ENOMEM;
                        }
                }
-               break;
-       case 'e':
+       } 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");
@@ -4683,11 +4943,10 @@ static int set_option(int opt, const char *arg, const char *optname)
                                ret = -ENOMEM;
                        }
                }
-               break;
-       case 'f':
-               /* This is handled in set_options() thus silent break. */
-               break;
-       default:
+       } else if (string_match(optname, "config") || opt == 'f') {
+               /* This is handled in set_options() thus silent skip. */
+               goto end;
+       } else {
                /* Unknown option or other error.
                 * Error is printed by getopt, just return */
                ret = -1;
@@ -4715,7 +4974,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)
@@ -4822,12 +5081,24 @@ static int set_options(int argc, char **argv)
        optopt = orig_optopt;
        optind = orig_optind;
        while (1) {
-               c = getopt_long(argc, argv, optstring, long_options, &option_index);
+               option_index = -1;
+               /*
+                * getopt_long() will not set option_index if it encounters a
+                * short option.
+                */
+               c = getopt_long(argc, argv, optstring, long_options,
+                               &option_index);
                if (c == -1) {
                        break;
                }
 
-               ret = set_option(c, optarg, long_options[option_index].name);
+               /*
+                * Pass NULL as the long option name if popt left the index
+                * unset.
+                */
+               ret = set_option(c, optarg,
+                               option_index < 0 ? NULL :
+                               long_options[option_index].name);
                if (ret < 0) {
                        break;
                }
@@ -5095,9 +5366,6 @@ error:
 static void sighandler(int sig)
 {
        switch (sig) {
-       case SIGPIPE:
-               DBG("SIGPIPE caught");
-               return;
        case SIGINT:
                DBG("SIGINT caught");
                stop_threads();
@@ -5129,9 +5397,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;
@@ -5142,12 +5411,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;
        }
@@ -5159,14 +5429,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;
 
@@ -5187,7 +5457,10 @@ static int write_pidfile(void)
        assert(rundir);
 
        if (opt_pidfile) {
-               strncpy(pidfile_path, opt_pidfile, sizeof(pidfile_path));
+               if (lttng_strncpy(pidfile_path, opt_pidfile, sizeof(pidfile_path))) {
+                       ret = -1;
+                       goto error;
+               }
        } else {
                /* Build pidfile path from rundir and opt_pidfile. */
                ret = snprintf(pidfile_path, sizeof(pidfile_path), "%s/"
@@ -5312,6 +5585,10 @@ int main(int argc, char **argv)
                }
        }
 
+       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.
@@ -5328,29 +5605,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;
        }
@@ -5577,13 +5833,17 @@ int main(int argc, char **argv)
         * cleanup() can get called after that point.
         */
        if (ust_app_ht_alloc()) {
+               ERR("Failed to allocate UST app hash table");
                retval = -1;
                goto exit_init_data;
        }
 
-       /* Initialize agent domain subsystem. */
-       if (agent_setup()) {
-               /* ENOMEM at this point. */
+       /*
+        * Initialize agent app hash table. We allocate the hash table here
+        * since cleanup() can get called after this point.
+        */
+       if (agent_app_ht_alloc()) {
+               ERR("Failed to allocate Agent app hash table");
                retval = -1;
                goto exit_init_data;
        }
@@ -5713,8 +5973,8 @@ int main(int argc, char **argv)
        }
        load_info->path = opt_load_session_path;
 
-       /* 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;
@@ -5724,7 +5984,7 @@ int main(int argc, char **argv)
        }
 
        /* 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;
@@ -5734,7 +5994,7 @@ int main(int argc, char **argv)
        }
 
        /* 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;
@@ -5744,7 +6004,7 @@ int main(int argc, char **argv)
        }
 
        /* 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;
@@ -5754,7 +6014,7 @@ int main(int argc, char **argv)
        }
 
        /* 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;
@@ -5764,7 +6024,7 @@ int main(int argc, char **argv)
        }
 
        /* 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;
@@ -5774,7 +6034,7 @@ int main(int argc, char **argv)
        }
 
        /* 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;
@@ -5786,7 +6046,7 @@ int main(int argc, char **argv)
        /* Don't start this thread if kernel tracing is not requested nor root */
        if (is_root && !opt_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;
@@ -5797,8 +6057,8 @@ int main(int argc, char **argv)
        }
 
        /* 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");
@@ -5861,6 +6121,10 @@ exit_apps:
        }
 exit_reg_apps:
 
+       /*
+        * Join dispatch thread after joining reg_apps_thread to ensure
+        * we don't leak applications in the queue.
+        */
        ret = pthread_join(dispatch_thread, &status);
        if (ret) {
                errno = ret;
@@ -5886,6 +6150,12 @@ exit_client:
 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.
@@ -5895,37 +6165,28 @@ exit_init_data:
        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();
 
-       ret = pthread_join(ht_cleanup_thread, &status);
+       ret = fini_ht_cleanup_thread(&ht_cleanup_thread);
        if (ret) {
-               errno = ret;
-               PERROR("pthread_join ht cleanup thread");
                retval = -1;
        }
 exit_ht_cleanup:
-exit_set_max_size:
-
-       utils_close_pipe(ht_cleanup_pipe);
-exit_ht_cleanup_pipe:
-
-       /*
-        * Close the ht_cleanup quit pipe.
-        */
-       utils_close_pipe(ht_cleanup_quit_pipe);
-exit_ht_cleanup_quit_pipe:
 
        health_app_destroy(health_sessiond);
 exit_health_sessiond_cleanup:
+exit_create_run_as_worker_cleanup:
 
 exit_options:
        sessiond_cleanup_options();
 
 exit_set_signal_handler:
+
        if (!retval) {
                exit(EXIT_SUCCESS);
        } else {
This page took 0.082575 seconds and 5 git commands to generate.