X-Git-Url: http://git.efficios.com/?p=lttng-tools.git;a=blobdiff_plain;f=src%2Fbin%2Flttng-sessiond%2Fmain.c;h=7c48e4da40b2955156d36a0511e2e7f2b35e137f;hp=bedc61c110654744223f25de55f1713c97be8abd;hb=8e319828707210e48a5f3e74495881594dba73e8;hpb=0bb7724acebb12e986b55e04f13233710d8c71fa diff --git a/src/bin/lttng-sessiond/main.c b/src/bin/lttng-sessiond/main.c index bedc61c11..67d990027 100644 --- a/src/bin/lttng-sessiond/main.c +++ b/src/bin/lttng-sessiond/main.c @@ -1,6 +1,7 @@ /* * Copyright (C) 2011 - David Goulet * Mathieu Desnoyers + * 2013 - Jérémie Galarneau * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License, version 2 only, @@ -16,7 +17,7 @@ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ -#define _GNU_SOURCE +#define _LGPL_SOURCE #include #include #include @@ -36,15 +37,21 @@ #include #include #include -#include +#include #include #include +#include #include #include #include #include #include +#include +#include +#include +#include +#include #include "lttng-sessiond.h" #include "buffer-registry.h" @@ -64,35 +71,46 @@ #include "health-sessiond.h" #include "testpoint.h" #include "ust-thread.h" -#include "jul-thread.h" - -#define CONSUMERD_FILE "lttng-consumerd" +#include "agent-thread.h" +#include "save.h" +#include "load-session-thread.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 "timer.h" + +static const char *help_msg = +#ifdef LTTNG_EMBED_HELP +#include +#else +NULL +#endif +; const char *progname; -static const char *tracing_group_name = DEFAULT_TRACING_GROUP; -static const char *opt_pidfile; -static int opt_sig_parent; -static int opt_verbose_consumer; -static int opt_daemon; -static int opt_no_kernel; -static int is_root; /* Set to 1 if the daemon is running as root */ 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, .pid_mutex = PTHREAD_MUTEX_INITIALIZER, .lock = PTHREAD_MUTEX_INITIALIZER, .cond = PTHREAD_COND_INITIALIZER, @@ -100,10 +118,9 @@ 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, .pid_mutex = PTHREAD_MUTEX_INITIALIZER, .lock = PTHREAD_MUTEX_INITIALIZER, .cond = PTHREAD_COND_INITIALIZER, @@ -111,28 +128,56 @@ 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, .pid_mutex = PTHREAD_MUTEX_INITIALIZER, .lock = PTHREAD_MUTEX_INITIALIZER, .cond = PTHREAD_COND_INITIALIZER, .cond_mutex = PTHREAD_MUTEX_INITIALIZER, }; +/* Command line options */ +static const struct option long_options[] = { + { "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 } +}; + +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; @@ -162,14 +207,20 @@ static pthread_t kernel_thread; static pthread_t dispatch_thread; static pthread_t health_thread; static pthread_t ht_cleanup_thread; -static pthread_t jul_reg_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 * wakers / 1 waiter implemented and detailed in futex.c/.h * - * The thread_manage_apps and thread_dispatch_ust_registration interact with - * this queue and the wait/wake scheme. + * The thread_registration_apps and thread_dispatch_ust_registration uses this + * queue along with the wait/wake scheme. The thread_manage_apps receives down + * the line new application socket and monitors it for any I/O error or clean + * close that triggers an unregistration of the application. */ static struct ust_cmd_queue ust_cmd_queue; @@ -188,11 +239,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 const char *module_proc_lttng = "/proc/lttng"; /* @@ -229,73 +275,117 @@ 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; -/* JUL TCP port for registration. Used by the JUL thread. */ -unsigned int jul_tcp_port = DEFAULT_JUL_TCP_PORT; +/* Am I root or not. */ +int is_root; /* Set to 1 if the daemon is running as root */ -static -void setup_consumerd_path(void) +const char * const config_section_name = "sessiond"; + +/* Load session thread information to operate. */ +struct load_session_thread_data *load_info; + +/* Notification thread handle. */ +struct notification_thread_handle *notification_thread_handle; + +/* Rotation thread handle. */ +struct rotation_thread_handle *rotation_thread_handle; + +/* Global hash tables */ +struct lttng_ht *agent_apps_ht_by_sock = NULL; + +/* + * The initialization of the session daemon is done in multiple phases. + * + * While all threads are launched near-simultaneously, only some of them + * are needed to ensure the session daemon can start to respond to client + * requests. + * + * There are two important guarantees that we wish to offer with respect + * to the initialisation of the session daemon: + * - When the daemonize/background launcher process exits, the sessiond + * is fully able to respond to client requests, + * - Auto-loaded sessions are visible to clients. + * + * In order to achieve this, a number of support threads have to be launched + * to allow the "client" thread to function properly. Moreover, since the + * "load session" thread needs the client thread, we must provide a way + * for the "load session" thread to know that the "client" thread is up + * and running. + * + * Hence, the support threads decrement the lttng_sessiond_ready counter + * while the "client" threads waits for it to reach 0. Once the "client" thread + * unblocks, it posts the message_thread_ready semaphore which allows the + * "load session" thread to progress. + * + * This implies that the "load session" thread is the last to be initialized + * and will explicitly call sessiond_signal_parents(), which signals the parents + * that the session daemon is fully initialized. + * + * The four (4) support threads are: + * - agent_thread + * - notification_thread + * - rotation_thread + * - health_thread + */ +#define NR_LTTNG_SESSIOND_SUPPORT_THREADS 4 +int lttng_sessiond_ready = NR_LTTNG_SESSIOND_SUPPORT_THREADS; + +int sessiond_check_thread_quit_pipe(int fd, uint32_t events) { - const char *bin, *libdir; + return (fd == thread_quit_pipe[0] && (events & LPOLLIN)) ? 1 : 0; +} +/* Notify parents that we are ready for cmd and health check */ +LTTNG_HIDDEN +void sessiond_signal_parents(void) +{ /* - * 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 (!consumerd64_libdir[0]) { - consumerd64_libdir = INSTALL_LIB_PATH; + if (config.sig_parent) { + kill(ppid, SIGUSR1); } -#else -#error "Unknown bitness" -#endif /* - * runtime env. var. overrides the build default. + * Notify the parent of the fork() process that we are + * ready. */ - bin = getenv("LTTNG_CONSUMERD32_BIN"); - if (bin) { - consumerd32_bin = bin; - } - bin = getenv("LTTNG_CONSUMERD64_BIN"); - if (bin) { - consumerd64_bin = bin; - } - libdir = getenv("LTTNG_CONSUMERD32_LIBDIR"); - if (libdir) { - consumerd32_libdir = libdir; - } - libdir = getenv("LTTNG_CONSUMERD64_LIBDIR"); - if (libdir) { - consumerd64_libdir = libdir; + if (config.daemonize || config.background) { + kill(child_ppid, SIGUSR1); } } -/* - * Create a poll set with O_CLOEXEC and add the thread quit pipe to the set. - */ -int sessiond_set_thread_pollset(struct lttng_poll_event *events, size_t size) +LTTNG_HIDDEN +void sessiond_notify_ready(void) +{ + /* + * This memory barrier is paired with the one performed by + * the client thread after it has seen that 'lttng_sessiond_ready' is 0. + * + * The purpose of these memory barriers is to ensure that all + * initialization operations of the various threads that call this + * function to signal that they are ready are commited/published + * before the client thread can see the 'lttng_sessiond_ready' counter + * reach 0. + * + * Note that this could be a 'write' memory barrier, but a full barrier + * is used in case the code using this utility changes. The performance + * implications of this choice are minimal since this is a slow path. + */ + cmm_smp_mb(); + uatomic_sub(<tng_sessiond_ready, 1); +} + +static +int __sessiond_set_thread_pollset(struct lttng_poll_event *events, size_t size, + int *a_pipe) { int ret; @@ -307,7 +397,7 @@ int sessiond_set_thread_pollset(struct lttng_poll_event *events, size_t size) } /* Add quit pipe */ - ret = lttng_poll_add(events, thread_quit_pipe[0], LPOLLIN | LPOLLERR); + ret = lttng_poll_add(events, a_pipe[0], LPOLLIN | LPOLLERR); if (ret < 0) { goto error; } @@ -319,17 +409,11 @@ error: } /* - * Check if the thread quit pipe was triggered. - * - * Return 1 if it was triggered else 0; + * Create a poll set with O_CLOEXEC and add the thread quit pipe to the set. */ -int sessiond_check_thread_quit_pipe(int fd, uint32_t events) +int sessiond_set_thread_pollset(struct lttng_poll_event *events, size_t size) { - if (fd == thread_quit_pipe[0] && (events & LPOLLIN)) { - return 1; - } - - return 0; + return __sessiond_set_thread_pollset(events, size, thread_quit_pipe); } /* @@ -337,18 +421,18 @@ int sessiond_check_thread_quit_pipe(int fd, uint32_t events) * * Return -1 on error or 0 if all pipes are created. */ -static int init_thread_quit_pipe(void) +static int __init_thread_quit_pipe(int *a_pipe) { int ret, i; - ret = pipe(thread_quit_pipe); + ret = pipe(a_pipe); if (ret < 0) { PERROR("thread quit pipe"); goto error; } for (i = 0; i < 2; i++) { - ret = fcntl(thread_quit_pipe[i], F_SETFD, FD_CLOEXEC); + ret = fcntl(a_pipe[i], F_SETFD, FD_CLOEXEC); if (ret < 0) { PERROR("fcntl"); goto error; @@ -359,6 +443,11 @@ error: return ret; } +static int init_thread_quit_pipe(void) +{ + return __init_thread_quit_pipe(thread_quit_pipe); +} + /* * Stop all threads by closing the thread quit pipe. */ @@ -421,18 +510,62 @@ static void close_consumer_sockets(void) PERROR("UST consumerd64 cmd_sock close"); } } + 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"); + } + } +} + +/* + * 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) + } else if (!WIFEXITED(status)) { + ERR("consumerd termination with error: %d", + WEXITSTATUS(ret)); + } + consumer_data->pid = 0; } /* - * Cleanup the daemon + * Cleanup the session daemon's data structures. */ -static void cleanup(void) +static void sessiond_cleanup(void) { int ret; struct ltt_session *sess, *stmp; - char path[PATH_MAX]; - DBG("Cleaning up"); + DBG("Cleanup sessiond"); /* * Close the thread quit pipe. It has already done its job, @@ -440,77 +573,41 @@ static void 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_JULPORT_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); - /* 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 directory %s", config.consumerd32_path.value); + (void) rmdir(config.consumerd32_path.value); - /* - * We do NOT rmdir rundir because there are other processes - * using it, for instance lttng-relayd, which can start in - * parallel with this teardown. - */ + /* ust consumerd 64 */ + DBG("Removing %s", config.consumerd64_err_unix_sock_path.value); + (void) unlink(config.consumerd64_err_unix_sock_path.value); - free(rundir); + DBG("Removing directory %s", config.consumerd64_path.value); + (void) rmdir(config.consumerd64_path.value); DBG("Cleaning up all sessions"); @@ -521,15 +618,23 @@ static void 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); } } + 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(); - 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); @@ -539,15 +644,33 @@ static void cleanup(void) } DBG("Unloading kernel modules"); modprobe_remove_lttng_all(); + free(syscall_table); } close_consumer_sockets(); - /* */ - 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); - /* */ + if (load_info) { + load_session_destroy_data(load_info); + free(load_info); + } + + /* + * We do NOT rmdir rundir because there are other processes + * using it, for instance lttng-relayd, which can start in + * parallel with this teardown. + */ +} + +/* + * Cleanup the daemon's option data structures. + */ +static void sessiond_cleanup_options(void) +{ + DBG("Cleaning up options"); + + sessiond_config_fini(&config); + + run_as_destroy_worker(); } /* @@ -593,7 +716,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; } @@ -613,34 +736,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. @@ -688,7 +834,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; @@ -706,42 +852,47 @@ static int update_kernel_stream(struct consumer_data *consumer_data, int fd) } ksess = session->kernel_session; - cds_list_for_each_entry(channel, &ksess->channel_list.head, list) { - if (channel->fd == fd) { - DBG("Channel found, updating kernel streams"); - ret = kernel_open_channel_stream(channel); - if (ret < 0) { - goto error; - } - /* Update the stream global counter */ - ksess->stream_count_global += ret; + cds_list_for_each_entry(channel, + &ksess->channel_list.head, list) { + struct lttng_ht_iter iter; + struct consumer_socket *socket; - /* - * Have we already sent fds to the consumer? If yes, it means - * that tracing is started so it is safe to send our updated - * stream fds. - */ - if (ksess->consumer_fds_sent == 1 && ksess->consumer != NULL) { - struct lttng_ht_iter iter; - struct consumer_socket *socket; - - rcu_read_lock(); - 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, - channel, ksess, - session->output_traces ? 1 : 0); - pthread_mutex_unlock(socket->lock); - if (ret < 0) { - rcu_read_unlock(); - goto error; - } - } + if (channel->fd != fd) { + continue; + } + DBG("Channel found, updating kernel streams"); + ret = kernel_open_channel_stream(channel); + if (ret < 0) { + goto error; + } + /* Update the stream global counter */ + ksess->stream_count_global += ret; + + /* + * Have we already sent fds to the consumer? If yes, it + * means that tracing is started so it is safe to send + * our updated stream fds. + */ + if (ksess->consumer_fds_sent != 1 + || ksess->consumer == NULL) { + ret = -1; + goto error; + } + + rcu_read_lock(); + cds_lfht_for_each_entry(ksess->consumer->socks->ht, + &iter.iter, socket, node.node) { + pthread_mutex_lock(socket->lock); + ret = kernel_consumer_send_channel_streams(socket, + channel, ksess, + session->output_traces ? 1 : 0); + pthread_mutex_unlock(socket->lock); + if (ret < 0) { rcu_read_unlock(); + goto error; } - goto error; } + rcu_read_unlock(); } session_unlock(session); } @@ -770,10 +921,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); } } @@ -801,13 +972,13 @@ static void *thread_manage_kernel(void *data) */ lttng_poll_init(&events); - if (testpoint(thread_manage_kernel)) { + if (testpoint(sessiond_thread_manage_kernel)) { goto error_testpoint; } health_code_update(); - if (testpoint(thread_manage_kernel_before_loop)) { + if (testpoint(sessiond_thread_manage_kernel_before_loop)) { goto error_testpoint; } @@ -836,12 +1007,14 @@ static void *thread_manage_kernel(void *data) update_poll_flag = 0; } - DBG("Thread kernel polling on %d fds", LTTNG_POLL_GETNB(&events)); + DBG("Thread kernel polling"); /* Poll infinite value of time */ restart: health_poll_entry(); ret = lttng_poll_wait(&events, -1); + DBG("Thread kernel return from poll on %d fds", + LTTNG_POLL_GETNB(&events)); health_poll_exit(); if (ret < 0) { /* @@ -867,6 +1040,11 @@ static void *thread_manage_kernel(void *data) health_code_update(); + if (!revents) { + /* No activity for this FD (poll implementation). */ + continue; + } + /* Thread quit pipe has been closed. Killing thread. */ ret = sessiond_check_thread_quit_pipe(pollfd, revents); if (ret) { @@ -875,31 +1053,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) { - ret = update_kernel_stream(&kconsumer_data, pollfd); + 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(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; } } } @@ -950,14 +1130,18 @@ static void signal_consumer_condition(struct consumer_data *data, int state) */ static void *thread_manage_consumer(void *data) { - int sock = -1, i, ret, pollfd, err = -1; + int sock = -1, i, ret, pollfd, err = -1, should_quit = 0; uint32_t revents, nb_fd; 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"); + rcu_register_thread(); + rcu_thread_online(); + health_register(health_sessiond, HEALTH_SESSIOND_TYPE_CONSUMER); health_code_update(); @@ -987,7 +1171,7 @@ static void *thread_manage_consumer(void *data) restart: health_poll_entry(); - if (testpoint(thread_manage_consumer)) { + if (testpoint(sessiond_thread_manage_consumer)) { goto error; } @@ -1012,6 +1196,11 @@ restart: health_code_update(); + if (!revents) { + /* No activity for this FD (poll implementation). */ + continue; + } + /* Thread quit pipe has been closed. Killing thread. */ ret = sessiond_check_thread_quit_pipe(pollfd, revents); if (ret) { @@ -1021,9 +1210,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; } } } @@ -1051,41 +1245,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; @@ -1106,9 +1302,41 @@ 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 command. + */ + 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; + } + + /* 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) { + health_code_update(); + + /* Exit the thread because the thread quit pipe has been triggered. */ + if (should_quit) { + /* Not a health error. */ + err = 0; + goto exit; + } + health_poll_entry(); ret = lttng_poll_wait(&events, -1); health_poll_exit(); @@ -1131,16 +1359,22 @@ restart_poll: health_code_update(); - /* Thread quit pipe has been closed. Killing thread. */ - ret = sessiond_check_thread_quit_pipe(pollfd, revents); - if (ret) { - err = 0; - goto exit; + if (!revents) { + /* No activity for this FD (poll implementation). */ + continue; } + /* + * Thread quit pipe has been triggered, flag that we should stop + * but continue the current loop to handle potential data from + * consumer. + */ + should_quit = sessiond_check_thread_quit_pipe(pollfd, revents); + 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; } @@ -1158,6 +1392,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); @@ -1165,11 +1404,8 @@ restart_poll: ERR("Handling metadata request"); goto error; } - break; - } else { - ERR("Unknown pollfd"); - goto error; } + /* No need for an else branch all FDs are tested prior. */ } health_code_update(); } @@ -1208,13 +1444,13 @@ error: } consumer_data->cmd_sock = -1; } - if (*consumer_data->metadata_sock.fd_ptr >= 0) { + if (consumer_data->metadata_sock.fd_ptr && + *consumer_data->metadata_sock.fd_ptr >= 0) { ret = close(*consumer_data->metadata_sock.fd_ptr); if (ret) { PERROR("close"); } } - if (sock >= 0) { ret = close(sock); if (ret) { @@ -1224,14 +1460,18 @@ 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. */ - pthread_mutex_destroy(consumer_data->metadata_sock.lock); - free(consumer_data->metadata_sock.lock); - + if (consumer_data->metadata_sock.lock) { + pthread_mutex_destroy(consumer_data->metadata_sock.lock); + 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(); @@ -1240,11 +1480,24 @@ error_poll: health_unregister(health_sessiond); DBG("consumer thread cleanup completed"); + rcu_thread_offline(); + rcu_unregister_thread(); + return NULL; } /* - * 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) { @@ -1260,7 +1513,7 @@ static void *thread_manage_apps(void *data) health_register(health_sessiond, HEALTH_SESSIOND_TYPE_APP_MANAGE); - if (testpoint(thread_manage_apps)) { + if (testpoint(sessiond_thread_manage_apps)) { goto error_testpoint; } @@ -1276,19 +1529,21 @@ static void *thread_manage_apps(void *data) goto error; } - if (testpoint(thread_manage_apps_before_loop)) { + if (testpoint(sessiond_thread_manage_apps_before_loop)) { goto error; } health_code_update(); while (1) { - DBG("Apps thread polling on %d fds", LTTNG_POLL_GETNB(&events)); + DBG("Apps thread polling"); /* Inifinite blocking call, waiting for transmission */ restart: health_poll_entry(); ret = lttng_poll_wait(&events, -1); + DBG("Apps thread return from poll on %d fds", + LTTNG_POLL_GETNB(&events)); health_poll_exit(); if (ret < 0) { /* @@ -1309,6 +1564,11 @@ static void *thread_manage_apps(void *data) health_code_update(); + if (!revents) { + /* No activity for this FD (poll implementation). */ + continue; + } + /* Thread quit pipe has been closed. Killing thread. */ ret = sessiond_check_thread_quit_pipe(pollfd, revents); if (ret) { @@ -1318,10 +1578,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 */ @@ -1334,9 +1591,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); @@ -1345,10 +1601,12 @@ static void *thread_manage_apps(void *data) } DBG("Apps with sock %d added to poll set", sock); - - health_code_update(); - - break; + } 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 { /* @@ -1364,7 +1622,9 @@ static void *thread_manage_apps(void *data) /* Socket closed on remote end. */ ust_app_unregister(pollfd); - break; + } else { + ERR("Unexpected poll events %u for sock %d", revents, pollfd); + goto error; } } @@ -1492,6 +1752,11 @@ static void sanitize_wait_queue(struct ust_reg_wait_queue *wait_queue) uint32_t revents = LTTNG_POLL_GETEV(&events, i); int pollfd = LTTNG_POLL_GETFD(&events, i); + if (!revents) { + /* No activity for this FD (poll implementation). */ + continue; + } + cds_list_for_each_entry_safe(wait_node, tmp_wait_node, &wait_queue->head, head) { if (pollfd == wait_node->app->sock && @@ -1500,7 +1765,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; } } } @@ -1527,27 +1801,37 @@ error_create: static void *thread_dispatch_ust_registration(void *data) { int ret, err = -1; - struct cds_wfq_node *node; + struct cds_wfcq_node *node; struct ust_command *ust_cmd = NULL; struct ust_reg_wait_node *wait_node = NULL, *tmp_wait_node; struct ust_reg_wait_queue wait_queue = { .count = 0, }; + rcu_register_thread(); + health_register(health_sessiond, HEALTH_SESSIOND_TYPE_APP_REG_DISPATCH); + if (testpoint(sessiond_thread_app_reg_dispatch)) { + goto error_testpoint; + } + health_code_update(); CDS_INIT_LIST_HEAD(&wait_queue.head); 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; @@ -1561,7 +1845,7 @@ static void *thread_dispatch_ust_registration(void *data) health_code_update(); /* Dequeue command for registration */ - node = cds_wfq_dequeue_blocking(&ust_cmd_queue.queue); + node = cds_wfcq_dequeue_blocking(&ust_cmd_queue.head, &ust_cmd_queue.tail); if (node == NULL) { DBG("Woken up but nothing in the UST command queue"); /* Continue thread execution */ @@ -1698,7 +1982,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 @@ -1740,12 +2024,30 @@ 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) { health_error(); ERR("Health error occurred in %s", __func__); } health_unregister(health_sessiond); + rcu_unregister_thread(); return NULL; } @@ -1767,7 +2069,7 @@ static void *thread_registration_apps(void *data) health_register(health_sessiond, HEALTH_SESSIOND_TYPE_APP_REG); - if (testpoint(thread_registration_apps)) { + if (testpoint(sessiond_thread_registration_apps)) { goto error_testpoint; } @@ -1826,6 +2128,11 @@ static void *thread_registration_apps(void *data) revents = LTTNG_POLL_GETEV(&events, i); pollfd = LTTNG_POLL_GETFD(&events, i); + if (!revents) { + /* No activity for this FD (poll implementation). */ + continue; + } + /* Thread quit pipe has been closed. Killing thread. */ ret = sessiond_check_thread_quit_pipe(pollfd, revents); if (ret) { @@ -1835,10 +2142,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; @@ -1851,10 +2155,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 @@ -1866,6 +2172,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; } @@ -1914,13 +2224,19 @@ static void *thread_registration_apps(void *data) * Lock free enqueue the registration request. The red pill * has been taken! This apps will be part of the *system*. */ - cds_wfq_enqueue(&ust_cmd_queue.queue, &ust_cmd->node); + cds_wfcq_enqueue(&ust_cmd_queue.head, &ust_cmd_queue.tail, &ust_cmd->node); /* * Wake the registration queue futex. Implicit memory - * barrier with the exchange in cds_wfq_enqueue. + * 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; } } } @@ -1928,11 +2244,6 @@ static void *thread_registration_apps(void *data) exit: error: - if (err) { - health_error(); - ERR("Health error occurred in %s", __func__); - } - /* Notify that the registration thread is gone */ notify_ust_apps(0); @@ -1949,7 +2260,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); @@ -1957,6 +2268,10 @@ error_listen: error_create_poll: error_testpoint: DBG("UST Registration thread cleanup complete"); + if (err) { + health_error(); + ERR("Health error occurred in %s", __func__); + } health_unregister(health_sessiond); return NULL; @@ -1971,12 +2286,17 @@ 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 */ ret = pthread_condattr_init(&consumer_data->condattr); - if (ret != 0) { + if (ret) { errno = ret; PERROR("pthread_condattr_init consumer data"); goto error; @@ -1988,22 +2308,23 @@ static int spawn_consumer_thread(struct consumer_data *consumer_data) * for a more details and how we noticed it. */ ret = pthread_condattr_setclock(&consumer_data->condattr, CLOCK_MONOTONIC); - if (ret != 0) { + if (ret) { errno = ret; PERROR("pthread_condattr_setclock consumer data"); goto error; } ret = pthread_cond_init(&consumer_data->cond, &consumer_data->condattr); - if (ret != 0) { + if (ret) { errno = ret; PERROR("pthread_cond_init consumer data"); goto error; } - ret = pthread_create(&consumer_data->thread, NULL, thread_manage_consumer, - consumer_data); - if (ret != 0) { + ret = pthread_create(&consumer_data->thread, default_pthread_attr(), + thread_manage_consumer, consumer_data); + if (ret) { + errno = ret; PERROR("pthread_create consumer"); ret = -1; goto error; @@ -2013,7 +2334,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 @@ -2053,19 +2374,23 @@ static int spawn_consumer_thread(struct consumer_data *consumer_data) if (ret != 0) { errno = ret; if (ret == ETIMEDOUT) { + int pth_ret; + /* * Call has timed out so we kill the kconsumerd_thread and return * an error. */ ERR("Condition timed out. The consumer thread was never ready." " Killing it"); - ret = pthread_cancel(consumer_data->thread); - if (ret < 0) { + pth_ret = pthread_cancel(consumer_data->thread); + if (pth_ret < 0) { PERROR("pthread_cancel consumer thread"); } } else { PERROR("pthread_cond_wait failed consumer thread"); } + /* Caller is expecting a negative value on failure. */ + ret = -1; goto error; } @@ -2095,7 +2420,7 @@ static int join_consumer_thread(struct consumer_data *consumer_data) int ret; ret = kill(consumer_data->pid, SIGTERM); if (ret) { - ERR("Error killing consumer daemon"); + PERROR("Error killing consumer daemon"); return ret; } return pthread_join(consumer_data->thread, &status); @@ -2124,11 +2449,14 @@ static pid_t spawn_consumerd(struct consumer_data *consumer_data) /* * Exec consumerd. */ - if (opt_verbose_consumer) { + if (config.verbose_consumer) { verbosity = "--verbose"; - } else { + } else if (lttng_opt_quiet) { verbosity = "--quiet"; + } else { + verbosity = ""; } + switch (consumer_data->type) { case LTTNG_CONSUMER_KERNEL: /* @@ -2137,129 +2465,114 @@ 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 (config.consumerd32_bin_path.value && + 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"); - break; + ret = -EINVAL; + goto error; } DBG("Using kernel consumer at: %s", consumer_to_use); - 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 = getenv("LD_LIBRARY_PATH"); + 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); - } - if (ret) { - goto error; - } 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 = getenv("LD_LIBRARY_PATH"); + 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); - } - if (ret) { - goto error; - } break; } default: - PERROR("unknown consumer type"); - exit(EXIT_FAILURE); + ERR("unknown consumer type"); + errno = 0; } if (errno != 0) { - PERROR("kernel start consumer exec"); + PERROR("Consumer execl()"); } + /* Reaching this point, we got a failure on our execl(). */ exit(EXIT_FAILURE); } else if (pid > 0) { ret = pid; @@ -2346,12 +2659,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; } @@ -2361,6 +2674,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; @@ -2419,7 +2744,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); @@ -2428,10 +2753,12 @@ static int copy_session_consumer(int domain, struct ltt_session *session) dir_name = DEFAULT_KERNEL_TRACE_DIR; break; case LTTNG_DOMAIN_JUL: + case LTTNG_DOMAIN_LOG4J: + case LTTNG_DOMAIN_PYTHON: 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); @@ -2472,6 +2799,8 @@ static int create_ust_session(struct ltt_session *session, switch (domain->type) { case LTTNG_DOMAIN_JUL: + case LTTNG_DOMAIN_LOG4J: + case LTTNG_DOMAIN_PYTHON: case LTTNG_DOMAIN_UST: break; default: @@ -2494,7 +2823,16 @@ static int create_ust_session(struct ltt_session *session, lus->snapshot_mode = session->snapshot_mode; lus->live_timer_interval = session->live_timer; session->ust_session = lus; - + if (session->shm_path[0]) { + strncpy(lus->root_shm_path, session->shm_path, + sizeof(lus->root_shm_path)); + lus->root_shm_path[sizeof(lus->root_shm_path) - 1] = '\0'; + strncpy(lus->shm_path, session->shm_path, + sizeof(lus->shm_path)); + lus->shm_path[sizeof(lus->shm_path) - 1] = '\0'; + strncat(lus->shm_path, "/ust", + sizeof(lus->shm_path) - strlen(lus->shm_path) - 1); + } /* Copy session output to the newly created UST session */ ret = copy_session_consumer(domain->type, session); if (ret != LTTNG_OK) { @@ -2533,20 +2871,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 (ret != -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; @@ -2582,6 +2906,128 @@ static unsigned int lttng_sessions_count(uid_t uid, gid_t gid) return i; } +static int receive_userspace_probe(struct command_ctx *cmd_ctx, int sock, + int *sock_error, struct lttng_event *event) +{ + int fd, ret; + struct lttng_userspace_probe_location *probe_location; + const struct lttng_userspace_probe_location_lookup_method *lookup = NULL; + struct lttng_dynamic_buffer probe_location_buffer; + struct lttng_buffer_view buffer_view; + + /* + * Create a buffer to store the serialized version of the probe + * location. + */ + lttng_dynamic_buffer_init(&probe_location_buffer); + ret = lttng_dynamic_buffer_set_size(&probe_location_buffer, + cmd_ctx->lsm->u.enable.userspace_probe_location_len); + if (ret) { + ret = LTTNG_ERR_NOMEM; + goto error; + } + + /* + * Receive the probe location. + */ + ret = lttcomm_recv_unix_sock(sock, probe_location_buffer.data, + probe_location_buffer.size); + if (ret <= 0) { + DBG("Nothing recv() from client var len data... continuing"); + *sock_error = 1; + lttng_dynamic_buffer_reset(&probe_location_buffer); + ret = LTTNG_ERR_PROBE_LOCATION_INVAL; + goto error; + } + + buffer_view = lttng_buffer_view_from_dynamic_buffer( + &probe_location_buffer, 0, probe_location_buffer.size); + + /* + * Extract the probe location from the serialized version. + */ + ret = lttng_userspace_probe_location_create_from_buffer( + &buffer_view, &probe_location); + if (ret < 0) { + WARN("Failed to create a userspace probe location from the received buffer"); + lttng_dynamic_buffer_reset( &probe_location_buffer); + ret = LTTNG_ERR_PROBE_LOCATION_INVAL; + goto error; + } + + /* + * Receive the file descriptor to the target binary from the client. + */ + DBG("Receiving userspace probe target FD from client ..."); + ret = lttcomm_recv_fds_unix_sock(sock, &fd, 1); + if (ret <= 0) { + DBG("Nothing recv() from client userspace probe fd... continuing"); + *sock_error = 1; + ret = LTTNG_ERR_PROBE_LOCATION_INVAL; + goto error; + } + + /* + * Set the file descriptor received from the client through the unix + * socket in the probe location. + */ + lookup = lttng_userspace_probe_location_get_lookup_method(probe_location); + if (!lookup) { + ret = LTTNG_ERR_PROBE_LOCATION_INVAL; + goto error; + } + + /* + * From the kernel tracer's perspective, all userspace probe event types + * are all the same: a file and an offset. + */ + switch (lttng_userspace_probe_location_lookup_method_get_type(lookup)) { + case LTTNG_USERSPACE_PROBE_LOCATION_LOOKUP_METHOD_TYPE_FUNCTION_ELF: + ret = lttng_userspace_probe_location_function_set_binary_fd( + probe_location, fd); + break; + case LTTNG_USERSPACE_PROBE_LOCATION_LOOKUP_METHOD_TYPE_TRACEPOINT_SDT: + ret = lttng_userspace_probe_location_tracepoint_set_binary_fd( + probe_location, fd); + break; + default: + ret = LTTNG_ERR_PROBE_LOCATION_INVAL; + goto error; + } + + if (ret) { + ret = LTTNG_ERR_PROBE_LOCATION_INVAL; + goto error; + } + + /* Attach the probe location to the event. */ + ret = lttng_event_set_userspace_probe_location(event, probe_location); + if (ret) { + ret = LTTNG_ERR_PROBE_LOCATION_INVAL; + goto error; + } + + lttng_dynamic_buffer_reset(&probe_location_buffer); +error: + return ret; +} + +/* + * Check if the current kernel tracer supports the session rotation feature. + * Return 1 if it does, 0 otherwise. + */ +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) @@ -2602,6 +3048,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) { @@ -2618,13 +3066,23 @@ static int process_client_msg(struct command_ctx *cmd_ctx, int sock, case LTTNG_SNAPSHOT_DEL_OUTPUT: case LTTNG_SNAPSHOT_LIST_OUTPUT: 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_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; @@ -2657,10 +3115,16 @@ static int process_client_msg(struct command_ctx *cmd_ctx, int sock, case LTTNG_LIST_DOMAINS: case LTTNG_LIST_CHANNELS: 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; @@ -2672,10 +3136,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: @@ -2697,6 +3164,39 @@ static int process_client_msg(struct command_ctx *cmd_ctx, int sock, break; } + /* + * Commands that need a valid session but should NOT create one if none + * exists. Instead of creating one and destroying it when the command is + * handled, process that right before so we save some round trip in useless + * code path. + */ + switch (cmd_ctx->lsm->cmd_type) { + case LTTNG_DISABLE_CHANNEL: + case LTTNG_DISABLE_EVENT: + switch (cmd_ctx->lsm->domain.type) { + case LTTNG_DOMAIN_KERNEL: + if (!cmd_ctx->session->kernel_session) { + ret = LTTNG_ERR_NO_CHANNEL; + goto error; + } + break; + case LTTNG_DOMAIN_JUL: + case LTTNG_DOMAIN_LOG4J: + case LTTNG_DOMAIN_PYTHON: + case LTTNG_DOMAIN_UST: + if (!cmd_ctx->session->ust_session) { + ret = LTTNG_ERR_NO_CHANNEL; + goto error; + } + break; + default: + ret = LTTNG_ERR_UNKNOWN_DOMAIN; + goto error; + } + default: + break; + } + if (!need_domain) { goto skip_domain; } @@ -2764,6 +3264,8 @@ static int process_client_msg(struct command_ctx *cmd_ctx, int sock, break; case LTTNG_DOMAIN_JUL: + case LTTNG_DOMAIN_LOG4J: + case LTTNG_DOMAIN_PYTHON: case LTTNG_DOMAIN_UST: { if (!ust_app_supported()) { @@ -2789,7 +3291,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); @@ -2817,7 +3319,8 @@ static int process_client_msg(struct command_ctx *cmd_ctx, int sock, } /* 32-bit */ - if (consumerd32_bin[0] != '\0' && + pthread_mutex_lock(&ustconsumer32_data.pid_mutex); + 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); @@ -2835,7 +3338,7 @@ static int process_client_msg(struct command_ctx *cmd_ctx, int sock, } /* - * Setup socket for consumer 64 bit. No need for atomic access + * Setup socket for consumer 32 bit. No need for atomic access * since it was set above and can ONLY be set in this thread. */ ret = consumer_create_socket(&ustconsumer32_data, @@ -2855,7 +3358,11 @@ 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: case LTTNG_DOMAIN_UST: if (uatomic_read(&ust_consumerd_state) != CONSUMER_STARTED) { ret = LTTNG_ERR_NO_USTCONSUMERD; @@ -2868,6 +3375,9 @@ skip_domain: goto error; } break; + default: + ret = LTTNG_ERR_UNKNOWN_DOMAIN; + goto error; } } @@ -2903,9 +3413,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: @@ -2916,69 +3491,224 @@ 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 */ ret = cmd_disable_event(cmd_ctx->session, cmd_ctx->lsm->domain.type, cmd_ctx->lsm->u.disable.channel_name, - cmd_ctx->lsm->u.disable.name); - break; - } - case LTTNG_DISABLE_ALL_EVENT: - { - DBG("Disabling all events"); - - ret = cmd_disable_event_all(cmd_ctx->session, cmd_ctx->lsm->domain.type, - cmd_ctx->lsm->u.disable.channel_name); + &cmd_ctx->lsm->u.disable.event); break; } 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_ENABLE_EVENT: + case LTTNG_TRACK_PID: { - ret = cmd_enable_event(cmd_ctx->session, &cmd_ctx->lsm->domain, - cmd_ctx->lsm->u.enable.channel_name, - &cmd_ctx->lsm->u.enable.event, NULL, kernel_poll_pipe[1]); + ret = cmd_track_pid(cmd_ctx->session, + cmd_ctx->lsm->domain.type, + cmd_ctx->lsm->u.pid_tracker.pid); break; } - case LTTNG_ENABLE_ALL_EVENT: + case LTTNG_UNTRACK_PID: { - DBG("Enabling all events"); - - ret = cmd_enable_event_all(cmd_ctx->session, &cmd_ctx->lsm->domain, - cmd_ctx->lsm->u.enable.channel_name, - cmd_ctx->lsm->u.enable.event.type, NULL, kernel_poll_pipe[1]); + ret = cmd_untrack_pid(cmd_ctx->session, + cmd_ctx->lsm->domain.type, + cmd_ctx->lsm->u.pid_tracker.pid); break; } - case LTTNG_LIST_TRACEPOINTS: + case LTTNG_ENABLE_EVENT: { - struct lttng_event *events; - ssize_t nb_events; + struct lttng_event *ev = NULL; + struct lttng_event_exclusion *exclusion = NULL; + struct lttng_filter_bytecode *bytecode = NULL; + char *filter_expression = NULL; + + /* Handle exclusion events and receive it from the client. */ + if (cmd_ctx->lsm->u.enable.exclusion_count > 0) { + size_t count = cmd_ctx->lsm->u.enable.exclusion_count; + + exclusion = zmalloc(sizeof(struct lttng_event_exclusion) + + (count * LTTNG_SYMBOL_NAME_LEN)); + if (!exclusion) { + ret = LTTNG_ERR_EXCLUSION_NOMEM; + goto error; + } - nb_events = cmd_list_tracepoints(cmd_ctx->lsm->domain.type, &events); - if (nb_events < 0) { - /* Return value is a negative lttng_error_code. */ - ret = -nb_events; - goto error; + DBG("Receiving var len exclusion event list from client ..."); + exclusion->count = count; + ret = lttcomm_recv_unix_sock(sock, exclusion->names, + count * LTTNG_SYMBOL_NAME_LEN); + if (ret <= 0) { + DBG("Nothing recv() from client var len data... continuing"); + *sock_error = 1; + free(exclusion); + ret = LTTNG_ERR_EXCLUSION_INVAL; + 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(cmd_ctx, sizeof(struct lttng_event) * nb_events); - if (ret < 0) { - free(events); - goto setup_error; + /* Get filter expression from client. */ + if (cmd_ctx->lsm->u.enable.expression_len > 0) { + size_t expression_len = + cmd_ctx->lsm->u.enable.expression_len; + + if (expression_len > LTTNG_FILTER_MAX_LEN) { + ret = LTTNG_ERR_FILTER_INVAL; + free(exclusion); + goto error; + } + + filter_expression = zmalloc(expression_len); + if (!filter_expression) { + free(exclusion); + ret = LTTNG_ERR_FILTER_NOMEM; + goto error; + } + + /* Receive var. len. data */ + DBG("Receiving var len filter's expression from client ..."); + ret = lttcomm_recv_unix_sock(sock, filter_expression, + expression_len); + if (ret <= 0) { + DBG("Nothing recv() from client var len data... continuing"); + *sock_error = 1; + free(filter_expression); + free(exclusion); + ret = LTTNG_ERR_FILTER_INVAL; + goto error; + } + } + + /* Handle filter and get bytecode from client. */ + if (cmd_ctx->lsm->u.enable.bytecode_len > 0) { + size_t bytecode_len = cmd_ctx->lsm->u.enable.bytecode_len; + + if (bytecode_len > LTTNG_FILTER_MAX_LEN) { + ret = LTTNG_ERR_FILTER_INVAL; + free(filter_expression); + free(exclusion); + goto error; + } + + bytecode = zmalloc(bytecode_len); + if (!bytecode) { + free(filter_expression); + free(exclusion); + ret = LTTNG_ERR_FILTER_NOMEM; + goto error; + } + + /* Receive var. len. data */ + 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 var len data... continuing"); + *sock_error = 1; + free(filter_expression); + free(bytecode); + free(exclusion); + ret = LTTNG_ERR_FILTER_INVAL; + goto error; + } + + if ((bytecode->len + sizeof(*bytecode)) != bytecode_len) { + free(filter_expression); + free(bytecode); + free(exclusion); + ret = LTTNG_ERR_FILTER_INVAL; + goto error; + } + } + + ev = lttng_event_copy(&cmd_ctx->lsm->u.enable.event); + if (!ev) { + DBG("Failed to copy event: %s", + cmd_ctx->lsm->u.enable.event.name); + free(filter_expression); + free(bytecode); + free(exclusion); + ret = LTTNG_ERR_NOMEM; + goto error; } - /* Copy event list into message payload */ - memcpy(cmd_ctx->llm->payload, events, - sizeof(struct lttng_event) * nb_events); + if (cmd_ctx->lsm->u.enable.userspace_probe_location_len > 0) { + /* Expect a userspace probe description. */ + ret = receive_userspace_probe(cmd_ctx, sock, sock_error, ev); + if (ret) { + free(filter_expression); + free(bytecode); + free(exclusion); + lttng_event_destroy(ev); + goto error; + } + } + + ret = cmd_enable_event(cmd_ctx->session, &cmd_ctx->lsm->domain, + cmd_ctx->lsm->u.enable.channel_name, + ev, + filter_expression, bytecode, exclusion, + kernel_poll_pipe[1]); + lttng_event_destroy(ev); + break; + } + case LTTNG_LIST_TRACEPOINTS: + { + struct lttng_event *events; + ssize_t nb_events; + + session_lock_list(); + nb_events = cmd_list_tracepoints(cmd_ctx->lsm->domain.type, &events); + session_unlock_list(); + if (nb_events < 0) { + /* Return value is a negative lttng_error_code. */ + ret = -nb_events; + 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, events, + sizeof(struct lttng_event) * nb_events); free(events); + if (ret < 0) { + goto setup_error; + } + ret = LTTNG_OK; break; } @@ -2987,8 +3717,10 @@ skip_domain: struct lttng_event_field *fields; ssize_t nb_fields; + session_lock_list(); nb_fields = cmd_list_tracepoint_fields(cmd_ctx->lsm->domain.type, &fields); + session_unlock_list(); if (nb_fields < 0) { /* Return value is a negative lttng_error_code. */ ret = -nb_fields; @@ -2999,18 +3731,68 @@ 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); + ret = LTTNG_OK; + break; + } + case LTTNG_LIST_SYSCALLS: + { + struct lttng_event *events; + ssize_t nb_events; - free(fields); + nb_events = cmd_list_syscalls(&events); + if (nb_events < 0) { + /* Return value is a negative lttng_error_code. */ + ret = -nb_events; + 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, events, + sizeof(struct lttng_event) * nb_events); + free(events); + + if (ret < 0) { + goto setup_error; + } + + ret = LTTNG_OK; + break; + } + case LTTNG_LIST_TRACKER_PIDS: + { + int32_t *pids = NULL; + ssize_t nr_pids; + + 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; @@ -3045,36 +3827,31 @@ skip_domain: goto error; } - ret = cmd_set_consumer_uri(cmd_ctx->lsm->domain.type, cmd_ctx->session, - nb_uri, uris); + ret = cmd_set_consumer_uri(cmd_ctx->session, nb_uri, uris); + free(uris); if (ret != LTTNG_OK) { - free(uris); goto error; } - /* - * XXX: 0 means that this URI should be applied on the session. Should - * be a DOMAIN enuam. - */ - if (cmd_ctx->lsm->domain.type == 0) { - /* Add the URI for the UST session if a consumer is present. */ - if (cmd_ctx->session->ust_session && - cmd_ctx->session->ust_session->consumer) { - ret = cmd_set_consumer_uri(LTTNG_DOMAIN_UST, cmd_ctx->session, - nb_uri, uris); - } else if (cmd_ctx->session->kernel_session && - cmd_ctx->session->kernel_session->consumer) { - ret = cmd_set_consumer_uri(LTTNG_DOMAIN_KERNEL, - cmd_ctx->session, nb_uri, uris); - } - } - - free(uris); break; } 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; } @@ -3126,7 +3903,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; @@ -3135,7 +3913,7 @@ skip_domain: case LTTNG_LIST_DOMAINS: { ssize_t nb_dom; - struct lttng_domain *domains; + struct lttng_domain *domains = NULL; nb_dom = cmd_list_domains(cmd_ctx->session, &domains); if (nb_dom < 0) { @@ -3144,46 +3922,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; - struct lttng_channel *channels; + 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; } @@ -3191,61 +3961,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; @@ -3263,49 +4040,46 @@ skip_domain: cmd_ctx->lsm->u.reg.path, cdata); break; } - case LTTNG_ENABLE_EVENT_WITH_FILTER: + case LTTNG_DATA_PENDING: { - struct lttng_filter_bytecode *bytecode; + int pending_ret; + uint8_t pending_ret_byte; - if (cmd_ctx->lsm->u.enable.bytecode_len > LTTNG_FILTER_MAX_LEN) { - ret = LTTNG_ERR_FILTER_INVAL; - goto error; - } - if (cmd_ctx->lsm->u.enable.bytecode_len == 0) { - ret = LTTNG_ERR_FILTER_INVAL; - goto error; - } - bytecode = zmalloc(cmd_ctx->lsm->u.enable.bytecode_len); - if (!bytecode) { - ret = LTTNG_ERR_FILTER_NOMEM; - goto error; - } - /* Receive var. len. data */ - DBG("Receiving var len data from client ..."); - ret = lttcomm_recv_unix_sock(sock, bytecode, - cmd_ctx->lsm->u.enable.bytecode_len); - if (ret <= 0) { - DBG("Nothing recv() from client var len data... continuing"); - *sock_error = 1; - ret = LTTNG_ERR_FILTER_INVAL; - goto error; + 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; } - if (bytecode->len + sizeof(*bytecode) - != cmd_ctx->lsm->u.enable.bytecode_len) { - free(bytecode); - ret = LTTNG_ERR_FILTER_INVAL; - goto 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 = cmd_enable_event(cmd_ctx->session, &cmd_ctx->lsm->domain, - cmd_ctx->lsm->u.enable.channel_name, - &cmd_ctx->lsm->u.enable.event, bytecode, kernel_poll_pipe[1]); - break; - } - case LTTNG_DATA_PENDING: - { - ret = cmd_data_pending(cmd_ctx->session); + ret = LTTNG_OK; break; } case LTTNG_SNAPSHOT_ADD_OUTPUT: @@ -3318,13 +4092,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; } @@ -3345,20 +4119,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; } @@ -3447,6 +4216,137 @@ skip_domain: free(uris); break; } + case LTTNG_SAVE_SESSION: + { + ret = cmd_save_sessions(&cmd_ctx->lsm->u.save_session.attr, + &cmd_ctx->creds); + break; + } + case LTTNG_SET_SESSION_SHM_PATH: + { + ret = cmd_set_session_shm_path(cmd_ctx->session, + 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; + } + case LTTNG_REGISTER_TRIGGER: + { + ret = cmd_register_trigger(cmd_ctx, sock, + notification_thread_handle); + break; + } + case LTTNG_UNREGISTER_TRIGGER: + { + ret = cmd_unregister_trigger(cmd_ctx, sock, + notification_thread_handle); + break; + } + case LTTNG_ROTATE_SESSION: + { + struct lttng_rotate_session_return rotate_return; + + DBG("Client rotate session \"%s\"", cmd_ctx->session->name); + + 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; + } + + ret = cmd_rotate_session(cmd_ctx->session, &rotate_return); + if (ret < 0) { + ret = -ret; + goto error; + } + + ret = setup_lttng_msg_no_cmd_header(cmd_ctx, &rotate_return, + sizeof(rotate_return)); + if (ret < 0) { + ret = -ret; + goto error; + } + + ret = LTTNG_OK; + break; + } + case LTTNG_ROTATION_GET_INFO: + { + struct lttng_rotation_get_info_return get_info_return; + + 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) { + ret = -ret; + goto error; + } + + ret = setup_lttng_msg_no_cmd_header(cmd_ctx, &get_info_return, + sizeof(get_info_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; @@ -3455,7 +4355,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; } } @@ -3469,6 +4369,7 @@ setup_error: session_unlock_list(); } init_setup_error: + assert(!rcu_read_ongoing()); return ret; } @@ -3491,30 +4392,27 @@ static void *thread_manage_health(void *data) lttng_poll_init(&events); /* Create unix socket */ - sock = lttcomm_create_unix_sock(health_unix_sock_path); + sock = lttcomm_create_unix_sock(config.health_unix_sock_path.value); if (sock < 0) { ERR("Unable to create health check Unix socket"); - ret = -1; goto error; } if (is_root) { /* lttng health client socket path permissions */ - ret = chown(health_unix_sock_path, 0, - utils_get_group_id(tracing_group_name)); + 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", health_unix_sock_path); + ERR("Unable to set group on %s", config.health_unix_sock_path.value); PERROR("chown"); - ret = -1; goto error; } - ret = chmod(health_unix_sock_path, + 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", health_unix_sock_path); + ERR("Unable to set permissions on %s", config.health_unix_sock_path.value); PERROR("chmod"); - ret = -1; goto error; } } @@ -3545,6 +4443,8 @@ static void *thread_manage_health(void *data) goto error; } + sessiond_notify_ready(); + while (1) { DBG("Health check ready"); @@ -3568,6 +4468,11 @@ restart: revents = LTTNG_POLL_GETEV(&events, i); pollfd = LTTNG_POLL_GETFD(&events, i); + if (!revents) { + /* No activity for this FD (poll implementation). */ + continue; + } + /* Thread quit pipe has been closed. Killing thread. */ ret = sessiond_check_thread_quit_pipe(pollfd, revents); if (ret) { @@ -3577,9 +4482,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; } } } @@ -3603,13 +4513,12 @@ restart: if (ret) { PERROR("close"); } - new_sock = -1; continue; } rcu_thread_online(); - reply.ret_code = 0; + memset(&reply, 0, sizeof(reply)); for (i = 0; i < NR_HEALTH_SESSIOND_TYPES; i++) { /* * health_check_state returns 0 if health is @@ -3632,7 +4541,6 @@ restart: if (ret) { PERROR("close"); } - new_sock = -1; } exit: @@ -3641,7 +4549,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) { @@ -3650,7 +4558,7 @@ error: } lttng_poll_clean(&events); - + stop_threads(); rcu_unregister_thread(); return NULL; } @@ -3673,10 +4581,6 @@ static void *thread_manage_clients(void *data) health_register(health_sessiond, HEALTH_SESSIOND_TYPE_CMD); - if (testpoint(thread_manage_clients)) { - goto error_testpoint; - } - health_code_update(); ret = lttcomm_listen_unix_sock(client_sock); @@ -3699,26 +4603,64 @@ static void *thread_manage_clients(void *data) goto error; } + ret = sem_post(&load_info->message_thread_ready); + if (ret) { + PERROR("sem_post message_thread_ready"); + goto error; + } + /* - * 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. + * Wait until all support threads are initialized before accepting + * commands. */ - if (opt_sig_parent) { - kill(ppid, SIGUSR1); + while (uatomic_read(<tng_sessiond_ready) != 0) { + fd_set read_fds; + struct timeval timeout; + + FD_ZERO(&read_fds); + FD_SET(thread_quit_pipe[0], &read_fds); + memset(&timeout, 0, sizeof(timeout)); + timeout.tv_usec = 1000; + + /* + * If a support thread failed to launch, it may signal that + * we must exit and the sessiond would never be marked as + * "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(); - /* Notify the parent of the fork() process that we are ready. */ - if (opt_daemon) { - kill(child_ppid, SIGUSR1); + /* This testpoint is after we signal readiness to the parent. */ + if (testpoint(sessiond_thread_manage_clients)) { + goto error; } - if (testpoint(thread_manage_clients_before_loop)) { + if (testpoint(sessiond_thread_manage_clients_before_loop)) { goto error; } health_code_update(); while (1) { + const struct cmd_completion_handler *cmd_completion_handler; + DBG("Accepting client command ..."); /* Inifinite blocking call, waiting for transmission */ @@ -3745,6 +4687,11 @@ static void *thread_manage_clients(void *data) health_code_update(); + if (!revents) { + /* No activity for this FD (poll implementation). */ + continue; + } + /* Thread quit pipe has been closed. Killing thread. */ ret = sessiond_check_thread_quit_pipe(pollfd, revents); if (ret) { @@ -3754,9 +4701,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; } } } @@ -3851,11 +4803,24 @@ 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)", + 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"); @@ -3887,8 +4852,7 @@ error: error_listen: error_create_poll: -error_testpoint: - unlink(client_unix_sock_path); + unlink(config.client_unix_sock_path.value); if (client_sock >= 0) { ret = close(client_sock); if (ret) { @@ -3906,183 +4870,541 @@ error_testpoint: DBG("Client thread dying"); rcu_unregister_thread(); + + /* + * Since we are creating the consumer threads, we own them, so we need + * to join them before our thread exits. + */ + ret = join_consumer_thread(&kconsumer_data); + if (ret) { + errno = ret; + PERROR("join_consumer"); + } + + ret = join_consumer_thread(&ustconsumer32_data); + if (ret) { + errno = ret; + PERROR("join_consumer ust32"); + } + + ret = join_consumer_thread(&ustconsumer64_data); + if (ret) { + errno = ret; + PERROR("join_consumer ust64"); + } 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, " -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, " --jul-tcp-port JUL application registration TCP port\n"); + return (str1 && str2) && !strcmp(str1, str2); } /* - * daemon argument parsing + * Take an option from the getopt output and set it in the right variable to be + * used later. + * + * Return 0 on success else a negative value. */ -static int parse_args(int argc, char **argv) +static int set_option(int opt, const char *arg, const char *optname) { - int c; - - static 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' }, - { "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' }, - { "jul-tcp-port", 1, 0, 'J' }, - { NULL, 0, 0, 0 } - }; + int ret = 0; - while (1) { - int option_index = 0; - c = getopt_long(argc, argv, "dhqvVSN" "a:c:g:s:C:E:D:F:Z:u:t:p:J:", - long_options, &option_index); - if (c == -1) { - break; + if (string_match(optname, "client-sock") || opt == 'c') { + if (!arg || *arg == '\0') { + ret = -EINVAL; + goto end; } - - switch (c) { - case 0: - fprintf(stderr, "option %s", long_options[option_index].name); - if (optarg) { - fprintf(stderr, " with arg %s\n", optarg); + if (lttng_is_setuid_setgid()) { + WARN("Getting '%s' argument from setuid/setgid binary refused for security reasons.", + "-c, --client-sock"); + } else { + config_string_set(&config.client_unix_sock_path, + strdup(arg)); + if (!config.client_unix_sock_path.value) { + ret = -ENOMEM; + PERROR("strdup"); } - break; - case 'c': - snprintf(client_unix_sock_path, PATH_MAX, "%s", optarg); - break; - case 'a': - snprintf(apps_unix_sock_path, PATH_MAX, "%s", optarg); - break; - case 'd': - opt_daemon = 1; - break; - case 'g': - tracing_group_name = optarg; - break; - case 'h': - usage(); - exit(EXIT_FAILURE); - case 'V': - fprintf(stdout, "%s\n", VERSION); - exit(EXIT_SUCCESS); - case 'S': - opt_sig_parent = 1; - break; - case 'E': - snprintf(kconsumer_data.err_unix_sock_path, PATH_MAX, "%s", optarg); - break; - case 'C': - snprintf(kconsumer_data.cmd_unix_sock_path, PATH_MAX, "%s", optarg); - break; - case 'F': - snprintf(ustconsumer64_data.err_unix_sock_path, PATH_MAX, "%s", optarg); - break; - case 'D': - snprintf(ustconsumer64_data.cmd_unix_sock_path, PATH_MAX, "%s", optarg); - break; - case 'H': - snprintf(ustconsumer32_data.err_unix_sock_path, PATH_MAX, "%s", optarg); - break; - case 'G': - snprintf(ustconsumer32_data.cmd_unix_sock_path, PATH_MAX, "%s", optarg); - break; - case 'N': - opt_no_kernel = 1; - break; - case 'q': - lttng_opt_quiet = 1; - break; - case 'v': - /* Verbose level can increase using multiple -v */ - lttng_opt_verbose += 1; - break; - case 'Z': - opt_verbose_consumer += 1; - break; - case 'u': - consumerd32_bin= optarg; - break; - case 'U': - consumerd32_libdir = optarg; - break; - case 't': - consumerd64_bin = optarg; - break; - case 'T': - consumerd64_libdir = optarg; - break; - case 'p': - opt_pidfile = optarg; - break; - case 'J': /* JUL TCP port. */ - { + } + } 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 { + 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') { + config.daemonize = true; + } else if (string_match(optname, "background") || opt == 'b') { + 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 { + config_string_set(&config.tracing_group_name, + strdup(arg)); + if (!config.tracing_group_name.value) { + ret = -ENOMEM; + PERROR("strdup"); + } + } + } else if (string_match(optname, "help") || opt == 'h') { + 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') { + 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 { + 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 { + 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 { + 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 { + 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 { + 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 { + 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")) { + config.no_kernel = true; + } else if (string_match(optname, "quiet") || opt == 'q') { + 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 */ + config.verbose = config_parse_value(arg); + } else { + /* -v used on command line */ + config.verbose++; + } + /* Clamp value to [0, 3] */ + config.verbose = config.verbose < 0 ? 0 : + (config.verbose <= 3 ? config.verbose : 3); + } else if (string_match(optname, "verbose-consumer")) { + if (arg) { + config.verbose_consumer = config_parse_value(arg); + } else { + 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 { + config_string_set(&config.consumerd32_bin_path, + strdup(arg)); + if (!config.consumerd32_bin_path.value) { + PERROR("strdup"); + ret = -ENOMEM; + } + } + } 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 { + config_string_set(&config.consumerd32_lib_dir, + strdup(arg)); + if (!config.consumerd32_lib_dir.value) { + PERROR("strdup"); + ret = -ENOMEM; + } + } + } 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 { + config_string_set(&config.consumerd64_bin_path, + strdup(arg)); + if (!config.consumerd64_bin_path.value) { + PERROR("strdup"); + ret = -ENOMEM; + } + } + } 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 { + config_string_set(&config.consumerd64_lib_dir, + strdup(arg)); + if (!config.consumerd64_lib_dir.value) { + PERROR("strdup"); + ret = -ENOMEM; + } + } + } 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 { + 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; errno = 0; - v = strtoul(optarg, NULL, 0); - if (errno != 0 || !isdigit(optarg[0])) { - ERR("Wrong value in --jul-tcp-port parameter: %s", optarg); + v = strtoul(arg, NULL, 0); + if (errno != 0 || !isdigit(arg[0])) { + ERR("Wrong value in --agent-tcp-port parameter: %s", arg); return -1; } if (v == 0 || v >= 65535) { - ERR("Port overflow in --jul-tcp-port parameter: %s", optarg); + ERR("Port overflow in --agent-tcp-port parameter: %s", arg); return -1; } - jul_tcp_port = (uint32_t) v; - DBG3("JUL TCP port set to non default: %u", jul_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 { + 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 { + 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 { + config_string_set(&config.kmod_extra_probes_list, + strdup(arg)); + if (!config.kmod_extra_probes_list.value) { + PERROR("strdup"); + ret = -ENOMEM; + } + } + } 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; + } + +end: + if (ret == -EINVAL) { + const char *opt_name = "unknown"; + int i; + + for (i = 0; i < sizeof(long_options) / sizeof(struct option); + i++) { + if (opt == long_options[i].val) { + opt_name = long_options[i].name; + break; + } + } + + WARN("Invalid argument provided for option \"%s\", using default value.", + opt_name); + } + + return ret; +} + +/* + * config_entry_handler_cb used to handle options read from a config file. + * 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) +{ + int ret = 0, i; + + if (!entry || !entry->name || !entry->value) { + ret = -EINVAL; + goto end; + } + + /* Check if the option is to be ignored */ + for (i = 0; i < sizeof(config_ignore_options) / sizeof(char *); i++) { + if (!strcmp(entry->name, config_ignore_options[i])) { + goto end; + } + } + + for (i = 0; i < (sizeof(long_options) / sizeof(struct option)) - 1; + i++) { + + /* Ignore if not fully matched. */ + if (strcmp(entry->name, long_options[i].name)) { + continue; + } + + /* + * If the option takes no argument on the command line, we have to + * check if the value is "true". We support non-zero numeric values, + * true, on and yes. + */ + if (!long_options[i].has_arg) { + ret = config_parse_value(entry->value); + if (ret <= 0) { + if (ret) { + WARN("Invalid configuration value \"%s\" for option %s", + entry->value, entry->name); + } + /* False, skip boolean config option. */ + goto end; + } + } + + ret = set_option(long_options[i].val, entry->value, entry->name); + goto end; + } + + WARN("Unrecognized option \"%s\" in daemon configuration file.", entry->name); + +end: + return ret; +} + +/* + * daemon configuration loading and argument parsing + */ +static int set_options(int argc, char **argv) +{ + int ret = 0, c = 0, option_index = 0; + int orig_optopt = optopt, orig_optind = optind; + char *optstring; + const char *config_path = NULL; + + optstring = utils_generate_optstring(long_options, + sizeof(long_options) / sizeof(struct option)); + if (!optstring) { + ret = -ENOMEM; + goto end; + } + + /* Check for the --config option */ + while ((c = getopt_long(argc, argv, optstring, long_options, + &option_index)) != -1) { + if (c == '?') { + ret = -EINVAL; + goto end; + } else if (c != 'f') { + /* if not equal to --config option. */ + continue; + } + + if (lttng_is_setuid_setgid()) { + WARN("Getting '%s' argument from setuid/setgid binary refused for security reasons.", + "-f, --config"); + } else { + config_path = utils_expand_path(optarg); + if (!config_path) { + ERR("Failed to resolve path: %s", optarg); + } + } + } + + ret = config_get_section_entries(config_path, config_section_name, + config_entry_handler, NULL); + if (ret) { + if (ret > 0) { + ERR("Invalid configuration option at line %i", ret); + ret = -1; + } + goto end; + } + + /* Reset getopt's global state */ + optopt = orig_optopt; + optind = orig_optind; + while (1) { + 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; } - default: - /* Unknown option or other error. - * Error is printed by getopt, just return */ - return -1; + + /* + * 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; } } - return 0; +end: + free(optstring); + return ret; } /* @@ -4098,9 +5420,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; } @@ -4114,17 +5436,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; } @@ -4138,10 +5460,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; } @@ -4154,19 +5476,58 @@ 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; +} + +static void sessiond_cleanup_lock_file(void) +{ + int ret; + + /* + * 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. @@ -4179,7 +5540,7 @@ static int set_permissions(char *rundir) int ret; gid_t gid; - gid = utils_get_group_id(tracing_group_name); + gid = utils_get_group_id(config.tracing_group_name.value); /* Set lttng run dir */ ret = chown(rundir, 0, gid); @@ -4200,9 +5561,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"); } @@ -4235,16 +5596,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; @@ -4256,44 +5617,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"); @@ -4301,7 +5659,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) { @@ -4342,9 +5700,6 @@ error: static void sighandler(int sig) { switch (sig) { - case SIGPIPE: - DBG("SIGPIPE caught"); - return; case SIGINT: DBG("SIGINT caught"); stop_threads(); @@ -4376,9 +5731,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; @@ -4389,12 +5745,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; } @@ -4406,14 +5763,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; @@ -4423,153 +5780,38 @@ static void set_ulimit(void) } } -/* - * Write pidfile using the rundir and opt_pidfile. - */ -static void write_pidfile(void) +static int write_pidfile(void) { - int ret; - char pidfile_path[PATH_MAX]; - - assert(rundir); - - 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; - } - } - - /* - * Create pid file in rundir. Return value is of no importance. The - * execution will continue even though we are not able to write the file. - */ - (void) utils_create_pid_file(getpid(), pidfile_path); - -error: - return; + return utils_create_pid_file(getpid(), config.pid_file_path.value); } -/* - * Write JUL TCP port using the rundir. - */ -static void write_julport(void) +static int set_clock_plugin_env(void) { - int ret; - char path[PATH_MAX]; + int ret = 0; + char *env_value = NULL; - assert(rundir); + if (!config.lttng_ust_clock_plugin.value) { + goto end; + } - ret = snprintf(path, sizeof(path), "%s/" - DEFAULT_LTTNG_SESSIOND_JULPORT_FILE, rundir); + ret = asprintf(&env_value, "LTTNG_UST_CLOCK_PLUGIN=%s", + config.lttng_ust_clock_plugin.value); if (ret < 0) { - PERROR("snprintf julport path"); - goto error; + PERROR("asprintf"); + goto end; } - /* - * Create TCP JUL port file in rundir. Return value is of no importance. - * The execution will continue even though we are not able to write the - * file. - */ - (void) utils_create_pid_file(jul_tcp_port, path); - -error: - return; -} - -/* - * Daemonize this process by forking and making the parent wait for the child - * to signal it indicating readiness. Once received, the parent successfully - * quits. - * - * The child process undergoes the same action that daemon(3) does meaning - * setsid, chdir, and dup /dev/null into 0, 1 and 2. - * - * Return 0 on success else -1 on error. - */ -static int daemonize(void) -{ - int ret; - pid_t pid; - - /* Get parent pid of this process. */ - child_ppid = getppid(); - - pid = fork(); - if (pid < 0) { - PERROR("fork"); - goto error; - } else if (pid == 0) { - int fd; - pid_t sid; - - /* Child */ - - /* - * Get the newly created parent pid so we can signal that process when - * we are ready to operate. - */ - child_ppid = getppid(); - - sid = setsid(); - if (sid < 0) { - PERROR("setsid"); - goto error; - } - - /* Try to change directory to /. If we can't well at least notify. */ - ret = chdir("/"); - if (ret < 0) { - PERROR("chdir"); - } - - fd = open(_PATH_DEVNULL, O_RDWR, 0); - if (fd < 0) { - PERROR("open %s", _PATH_DEVNULL); - /* Let 0, 1 and 2 open since we can't bind them to /dev/null. */ - } else { - (void) dup2(fd, STDIN_FILENO); - (void) dup2(fd, STDOUT_FILENO); - (void) dup2(fd, STDERR_FILENO); - if (fd > 2) { - ret = close(fd); - if (ret < 0) { - PERROR("close"); - } - } - } + ret = putenv(env_value); + if (ret) { + free(env_value); + PERROR("putenv of LTTNG_UST_CLOCK_PLUGIN"); goto end; - } else { - /* Parent */ - - /* - * Waiting for child to notify this parent that it can exit. Note that - * sleep() is interrupted before the 1 second delay as soon as the - * signal is received, so it will not cause visible delay for the - * user. - */ - while (!CMM_LOAD_SHARED(recv_child_signal)) { - sleep(1); - } - - /* - * From this point on, the parent can exit and the child is now an - * operationnal session daemon ready to serve clients and applications. - */ - exit(EXIT_SUCCESS); } + DBG("Updated LTTNG_UST_CLOCK_PLUGIN environment variable to \"%s\"", + config.lttng_ust_clock_plugin.value); end: - return 0; - -error: - return -1; + return ret; } /* @@ -4577,19 +5819,33 @@ error: */ int main(int argc, char **argv) { - int ret = 0; + 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 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(); rcu_register_thread(); - if ((ret = set_signal_handler()) < 0) { - goto error; + if (set_signal_handler()) { + retval = -1; + goto exit_set_signal_handler; } - setup_consumerd_path(); + if (timer_signal_init()) { + retval = -1; + goto exit_set_signal_handler; + } page_size = sysconf(_SC_PAGESIZE); if (page_size < 0) { @@ -4598,124 +5854,147 @@ int main(int argc, char **argv) WARN("Fallback page size to %ld", page_size); } - /* Parse arguments */ + 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. + * + * We have an exit_options exit path to free memory reserved by + * set_options. This is needed because the rest of sessiond_cleanup() + * depends on ht_cleanup_thread, which depends on lttng_daemonize, which + * depends on set_options. + */ progname = argv[0]; - if ((ret = parse_args(argc, argv)) < 0) { - goto error; + if (set_options(argc, argv)) { + retval = -1; + 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) { + if (config.daemonize || config.background) { int i; - ret = daemonize(); + ret = lttng_daemonize(&child_ppid, &recv_child_signal, + !config.background); if (ret < 0) { - goto error; + retval = -1; + goto exit_options; } /* * 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. + * closed, in case we are called with more opened file + * 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. + */ + + /* + * Initialize the health check subsystem. This call should set the + * appropriate time values. + */ + health_sessiond = health_app_create(NR_HEALTH_SESSIOND_TYPES); + if (!health_sessiond) { + PERROR("health_app_create error"); + retval = -1; + goto exit_health_sessiond_cleanup; + } + + /* Create thread to clean up RCU hash tables */ + if (init_ht_cleanup_thread(&ht_cleanup_thread)) { + retval = -1; + goto exit_ht_cleanup; + } + /* Create thread quit pipe */ - if ((ret = init_thread_quit_pipe()) < 0) { - goto error; + if (init_thread_quit_pipe()) { + retval = -1; + goto exit_init_data; } /* Check if daemon is UID = 0 */ is_root = !getuid(); - if (is_root) { - rundir = strdup(DEFAULT_LTTNG_RUNDIR); - /* Create global run dir with root access */ - ret = create_lttng_rundir(rundir); - if (ret < 0) { - goto error; - } - - if (strlen(apps_unix_sock_path) == 0) { - snprintf(apps_unix_sock_path, PATH_MAX, - DEFAULT_GLOBAL_APPS_UNIX_SOCK); - } - - if (strlen(client_unix_sock_path) == 0) { - snprintf(client_unix_sock_path, PATH_MAX, - DEFAULT_GLOBAL_CLIENT_UNIX_SOCK); - } - /* Set global SHM for ust */ - if (strlen(wait_shm_path) == 0) { - snprintf(wait_shm_path, PATH_MAX, - DEFAULT_GLOBAL_APPS_WAIT_SHM_PATH); + 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; } - - if (strlen(health_unix_sock_path) == 0) { - snprintf(health_unix_sock_path, sizeof(health_unix_sock_path), - DEFAULT_GLOBAL_HEALTH_UNIX_SOCK); - } - - /* Setup kernel consumerd path */ - snprintf(kconsumer_data.err_unix_sock_path, PATH_MAX, - DEFAULT_KCONSUMERD_ERR_SOCK_PATH, rundir); - snprintf(kconsumer_data.cmd_unix_sock_path, PATH_MAX, - DEFAULT_KCONSUMERD_CMD_SOCK_PATH, rundir); - - 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."); - ret = -EPERM; - goto error; - } - - /* - * Create rundir from home path. This will create something like - * $HOME/.lttng - */ - ret = asprintf(&rundir, DEFAULT_LTTNG_HOME_RUNDIR, home_path); - if (ret < 0) { - ret = -ENOMEM; - goto error; - } - - ret = create_lttng_rundir(rundir); - if (ret < 0) { - goto error; - } - - if (strlen(apps_unix_sock_path) == 0) { - snprintf(apps_unix_sock_path, PATH_MAX, - DEFAULT_HOME_APPS_UNIX_SOCK, home_path); - } - - /* Set the cli tool unix socket path */ - if (strlen(client_unix_sock_path) == 0) { - snprintf(client_unix_sock_path, PATH_MAX, - DEFAULT_HOME_CLIENT_UNIX_SOCK, home_path); - } - - /* Set global SHM for ust */ - if (strlen(wait_shm_path) == 0) { - snprintf(wait_shm_path, PATH_MAX, - DEFAULT_HOME_APPS_WAIT_SHM_PATH, getuid()); - } - - /* Set health check Unix path */ - if (strlen(health_unix_sock_path) == 0) { - snprintf(health_unix_sock_path, sizeof(health_unix_sock_path), - DEFAULT_HOME_HEALTH_UNIX_SOCK, home_path); + 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; } } @@ -4723,58 +6002,63 @@ int main(int argc, char **argv) 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 */ - snprintf(ustconsumer32_data.err_unix_sock_path, PATH_MAX, - DEFAULT_USTCONSUMERD32_ERR_SOCK_PATH, rundir); - snprintf(ustconsumer32_data.cmd_unix_sock_path, PATH_MAX, - DEFAULT_USTCONSUMERD32_CMD_SOCK_PATH, rundir); - - 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 */ - snprintf(ustconsumer64_data.err_unix_sock_path, PATH_MAX, - DEFAULT_USTCONSUMERD64_ERR_SOCK_PATH, rundir); - snprintf(ustconsumer64_data.cmd_unix_sock_path, PATH_MAX, - DEFAULT_USTCONSUMERD64_CMD_SOCK_PATH, rundir); - - 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); + 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; + } + 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; + } /* - * See if daemon already exist. + * The rotation_thread_timer_queue structure is shared between the + * sessiond timer thread and the rotation thread. The main thread keeps + * its ownership and destroys it when both threads have been joined. */ - if ((ret = check_existing_daemon()) < 0) { - ERR("Already running daemon.\n"); - /* - * We do not goto exit because we must not cleanup() - * because a daemon is already running. - */ - goto error; + rotation_timer_queue = rotation_thread_timer_queue_create(); + if (!rotation_timer_queue) { + retval = -1; + goto exit_init_data; + } + timer_thread_ctx.rotation_thread_job_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; } /* * Init UST app hash table. Alloc hash table before this point since * cleanup() can get called after that point. */ - ust_app_ht_alloc(); - - /* Initialize JUL domain subsystem. */ - if ((ret = jul_init()) < 0) { - /* ENOMEM at this point. */ - goto error; + if (ust_app_ht_alloc()) { + ERR("Failed to allocate UST app hash table"); + retval = -1; + goto exit_init_data; } - /* After this point, we can safely call cleanup() with "goto exit" */ + /* + * 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; + } /* * These actions must be executed as root. We do that *after* setting up @@ -4783,14 +6067,22 @@ int main(int argc, char **argv) * kernel tracer. */ if (is_root) { - ret = set_consumer_sockets(&kconsumer_data, rundir); - if (ret < 0) { - goto exit; + 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(); + if (ret < 0) { + ERR("Unable to populate syscall table. " + "Syscall tracing won't work " + "for this session daemon."); + } + } } /* Set ulimit for open files */ @@ -4799,51 +6091,51 @@ int main(int argc, char **argv) /* init lttng_fd tracking must be done after set_ulimit. */ lttng_fd_init(); - ret = set_consumer_sockets(&ustconsumer64_data, rundir); - if (ret < 0) { - goto exit; + if (set_consumer_sockets(&ustconsumer64_data)) { + retval = -1; + goto exit_init_data; } - ret = set_consumer_sockets(&ustconsumer32_data, rundir); - if (ret < 0) { - goto exit; + if (set_consumer_sockets(&ustconsumer32_data)) { + retval = -1; + goto exit_init_data; } /* Setup the needed unix socket */ - if ((ret = init_daemon_socket()) < 0) { - goto exit; + if (init_daemon_socket()) { + retval = -1; + goto exit_init_data; } /* Set credentials to socket */ - if (is_root && ((ret = set_permissions(rundir)) < 0)) { - goto exit; + 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 ((ret = utils_create_pipe_cloexec(kernel_poll_pipe)) < 0) { - goto exit; + if (is_root && !config.no_kernel) { + if (utils_create_pipe_cloexec(kernel_poll_pipe)) { + retval = -1; + goto exit_init_data; } } - /* Setup the thread ht_cleanup communication pipe. */ - if (utils_create_pipe_cloexec(ht_cleanup_pipe) < 0) { - goto exit; - } - /* Setup the thread apps communication pipe. */ - if ((ret = utils_create_pipe_cloexec(apps_cmd_pipe)) < 0) { - goto exit; + if (utils_create_pipe_cloexec(apps_cmd_pipe)) { + retval = -1; + goto exit_init_data; } /* Setup the thread apps notify communication pipe. */ - if (utils_create_pipe_cloexec(apps_cmd_notify_pipe) < 0) { - goto exit; + if (utils_create_pipe_cloexec(apps_cmd_notify_pipe)) { + retval = -1; + goto exit_init_data; } /* Initialize global buffer per UID and PID registry. */ @@ -4851,214 +6143,390 @@ int main(int argc, char **argv) buffer_reg_init_pid_registry(); /* Init UST command queue. */ - cds_wfq_init(&ust_cmd_queue.queue); + cds_wfcq_init(&ust_cmd_queue.head, &ust_cmd_queue.tail); /* - * Get session list pointer. This pointer MUST NOT be free(). This list is - * statically declared in session.c + * Get session list pointer. This pointer MUST NOT be free'd. This list + * is statically declared in session.c */ session_list_ptr = session_get_list(); - /* Set up max poll set size */ - lttng_poll_set_max_size(); - cmd_init(); /* 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; } - write_pidfile(); - write_julport(); + ret = write_pidfile(); + if (ret) { + ERR("Error in write_pidfile"); + retval = -1; + goto exit_init_data; + } /* Initialize communication library */ lttcomm_init(); - /* This is to get the TCP timeout value. */ + /* Initialize TCP timeout values */ lttcomm_inet_init(); + if (load_session_init_data(&load_info) < 0) { + retval = -1; + goto exit_init_data; + } + load_info->path = config.load_session_path.value; + + /* Create health-check thread. */ + ret = pthread_create(&health_thread, default_pthread_attr(), + thread_manage_health, (void *) NULL); + if (ret) { + errno = ret; + PERROR("pthread_create health"); + retval = -1; + goto exit_health; + } + /* - * Initialize the health check subsystem. This call should set the - * appropriate time values. + * 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. */ - health_sessiond = health_app_create(NR_HEALTH_SESSIOND_TYPES); - if (!health_sessiond) { - PERROR("health_app_create error"); - goto exit_health_sessiond_cleanup; + sem_init(¬ification_thread_ready, 0, 0); + + /* notification_thread_data acquires the pipes' read side. */ + notification_thread_handle = notification_thread_handle_create( + ust32_channel_monitor_pipe, + ust64_channel_monitor_pipe, + kernel_channel_monitor_pipe, + ¬ification_thread_ready); + if (!notification_thread_handle) { + retval = -1; + ERR("Failed to create notification thread shared data"); + stop_threads(); + goto exit_notification; } - /* Create thread to clean up RCU hash tables */ - ret = pthread_create(&ht_cleanup_thread, NULL, - thread_ht_cleanup, (void *) NULL); - if (ret != 0) { - PERROR("pthread_create ht_cleanup"); - goto exit_ht_cleanup; + /* Create notification thread. */ + ret = pthread_create(¬ification_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 health-check thread */ - ret = pthread_create(&health_thread, NULL, - thread_manage_health, (void *) NULL); - if (ret != 0) { - PERROR("pthread_create health"); - goto exit_health; + /* Create timer thread. */ + ret = pthread_create(&timer_thread, default_pthread_attr(), + timer_thread_func, &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( + thread_quit_pipe[0], + rotation_timer_queue, + notification_thread_handle, + ¬ification_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 != 0) { + 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 != 0) { + if (ret) { + errno = ret; PERROR("pthread_create dispatch"); + retval = -1; + stop_threads(); goto exit_dispatch; } /* Create thread to manage application registration. */ - ret = pthread_create(®_apps_thread, NULL, + ret = pthread_create(®_apps_thread, default_pthread_attr(), thread_registration_apps, (void *) NULL); - if (ret != 0) { + 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 != 0) { + 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 != 0) { - PERROR("pthread_create apps"); + if (ret) { + errno = ret; + PERROR("pthread_create notify"); + retval = -1; + stop_threads(); goto exit_apps_notify; } - /* Create JUL registration thread. */ - ret = pthread_create(&jul_reg_thread, NULL, - jul_thread_manage_registration, (void *) NULL); - if (ret != 0) { - PERROR("pthread_create apps"); - goto exit_jul_reg; + /* Create agent registration thread. */ + 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 != 0) { + 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, 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; + } + + /* + * This is where we start awaiting program completion (e.g. through + * signal that asks threads to teardown). + */ + + ret = pthread_join(load_session_thread, &status); + if (ret) { + errno = ret; + PERROR("pthread_join load_session_thread"); + retval = -1; + } +exit_load_session: + if (is_root && !config.no_kernel) { ret = pthread_join(kernel_thread, &status); - if (ret != 0) { + if (ret) { + errno = ret; PERROR("pthread_join"); - goto error; /* join error, exit without cleanup */ + retval = -1; } } - exit_kernel: - ret = pthread_join(jul_reg_thread, &status); - if (ret != 0) { - PERROR("pthread_join JUL"); - goto error; /* join error, exit without cleanup */ + + ret = pthread_join(agent_reg_thread, &status); + if (ret) { + errno = ret; + PERROR("pthread_join agent"); + retval = -1; } +exit_agent_reg: -exit_jul_reg: ret = pthread_join(apps_notify_thread, &status); - if (ret != 0) { + if (ret) { + errno = ret; PERROR("pthread_join apps notify"); - goto error; /* join error, exit without cleanup */ + retval = -1; } - exit_apps_notify: + ret = pthread_join(apps_thread, &status); - if (ret != 0) { + if (ret) { + errno = ret; PERROR("pthread_join apps"); - goto error; /* join error, exit without cleanup */ + retval = -1; } - - exit_apps: + ret = pthread_join(reg_apps_thread, &status); - if (ret != 0) { + if (ret) { + errno = ret; PERROR("pthread_join"); - goto error; /* join error, exit without cleanup */ + retval = -1; } - 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 != 0) { + if (ret) { + errno = ret; PERROR("pthread_join"); - goto error; /* join error, exit without cleanup */ + retval = -1; } - exit_dispatch: + ret = pthread_join(client_thread, &status); - if (ret != 0) { + if (ret) { + errno = ret; PERROR("pthread_join"); - goto error; /* join error, exit without cleanup */ + retval = -1; } - ret = join_consumer_thread(&kconsumer_data); - if (ret != 0) { - PERROR("join_consumer"); - goto error; /* join error, exit without cleanup */ +exit_client: +exit_rotation: +exit_notification: + sem_destroy(¬ification_thread_ready); + ret = pthread_join(health_thread, &status); + if (ret) { + errno = ret; + PERROR("pthread_join health thread"); + retval = -1; } - ret = join_consumer_thread(&ustconsumer32_data); - if (ret != 0) { - PERROR("join_consumer ust32"); - goto error; /* join error, exit without cleanup */ - } +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(); - ret = join_consumer_thread(&ustconsumer64_data); - if (ret != 0) { - PERROR("join_consumer ust64"); - goto error; /* join error, exit without cleanup */ + /* + * 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); } -exit_client: - ret = pthread_join(health_thread, &status); - if (ret != 0) { - PERROR("pthread_join health thread"); - goto error; /* join error, exit without cleanup */ + 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_health: - ret = pthread_join(ht_cleanup_thread, &status); - if (ret != 0) { - PERROR("pthread_join ht cleanup thread"); - goto error; /* join error, exit without cleanup */ + if (timer_thread_launched) { + timer_exit(); + ret = pthread_join(timer_thread, &status); + if (ret) { + errno = ret; + PERROR("pthread_join timer thread"); + retval = -1; + } } -exit_ht_cleanup: - health_app_destroy(health_sessiond); -exit_health_sessiond_cleanup: -exit: + /* - * cleanup() is called when no other thread is running. + * After the rotation and timer thread have quit, we can safely destroy + * the rotation_timer_queue. */ - rcu_thread_online(); - cleanup(); + rotation_thread_timer_queue_destroy(rotation_timer_queue); + rcu_thread_offline(); rcu_unregister_thread(); - if (!ret) { + + 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); +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: + if (!retval) { exit(EXIT_SUCCESS); + } else { + exit(EXIT_FAILURE); } -error: - exit(EXIT_FAILURE); }