X-Git-Url: http://git.efficios.com/?p=lttng-tools.git;a=blobdiff_plain;f=src%2Fbin%2Flttng-sessiond%2Fmain.c;h=ee50db21e38b2c29a400b1ccec0065f72f28900c;hp=b0674612b54589de311bdfa0e76117226675223e;hb=87597c2c3bbaa1502ad2025cbf16704829f3b464;hpb=389fbf04b41e2002be44a1e3392bfade2f1deeef diff --git a/src/bin/lttng-sessiond/main.c b/src/bin/lttng-sessiond/main.c index b0674612b..ee50db21e 100644 --- a/src/bin/lttng-sessiond/main.c +++ b/src/bin/lttng-sessiond/main.c @@ -49,6 +49,8 @@ #include #include #include +#include +#include #include "lttng-sessiond.h" #include "buffer-registry.h" @@ -71,39 +73,44 @@ #include "agent-thread.h" #include "save.h" #include "load-session-thread.h" -#include "syscall.h" +#include "notification-thread.h" +#include "notification-thread-commands.h" +#include "rotation-thread.h" +#include "lttng-syscall.h" #include "agent.h" #include "ht-cleanup.h" +#include "sessiond-config.h" +#include "sessiond-timer.h" -#define CONSUMERD_FILE "lttng-consumerd" +static const char *help_msg = +#ifdef LTTNG_EMBED_HELP +#include +#else +NULL +#endif +; const char *progname; -static const char *tracing_group_name = DEFAULT_TRACING_GROUP; -static int tracing_group_name_override; -static char *opt_pidfile; -static int opt_sig_parent; -static int opt_verbose_consumer; -static int opt_daemon, opt_background; -static int opt_no_kernel; -static char *opt_load_session_path; static pid_t ppid; /* Parent PID for --sig-parent option */ static pid_t child_ppid; /* Internal parent PID use with daemonize. */ -static char *rundir; static int lockfile_fd = -1; /* Set to 1 when a SIGUSR1 signal is received. */ static int recv_child_signal; +static struct lttng_kernel_tracer_version kernel_tracer_version; +static struct lttng_kernel_tracer_abi_version kernel_tracer_abi_version; + /* * Consumer daemon specific control data. Every value not initialized here is * set to 0 by the static definition. */ static struct consumer_data kconsumer_data = { .type = LTTNG_CONSUMER_KERNEL, - .err_unix_sock_path = DEFAULT_KCONSUMERD_ERR_SOCK_PATH, - .cmd_unix_sock_path = DEFAULT_KCONSUMERD_CMD_SOCK_PATH, .err_sock = -1, .cmd_sock = -1, + .channel_monitor_pipe = -1, + .channel_rotate_pipe = -1, .pid_mutex = PTHREAD_MUTEX_INITIALIZER, .lock = PTHREAD_MUTEX_INITIALIZER, .cond = PTHREAD_COND_INITIALIZER, @@ -111,10 +118,10 @@ static struct consumer_data kconsumer_data = { }; static struct consumer_data ustconsumer64_data = { .type = LTTNG_CONSUMER64_UST, - .err_unix_sock_path = DEFAULT_USTCONSUMERD64_ERR_SOCK_PATH, - .cmd_unix_sock_path = DEFAULT_USTCONSUMERD64_CMD_SOCK_PATH, .err_sock = -1, .cmd_sock = -1, + .channel_monitor_pipe = -1, + .channel_rotate_pipe = -1, .pid_mutex = PTHREAD_MUTEX_INITIALIZER, .lock = PTHREAD_MUTEX_INITIALIZER, .cond = PTHREAD_COND_INITIALIZER, @@ -122,10 +129,10 @@ static struct consumer_data ustconsumer64_data = { }; static struct consumer_data ustconsumer32_data = { .type = LTTNG_CONSUMER32_UST, - .err_unix_sock_path = DEFAULT_USTCONSUMERD32_ERR_SOCK_PATH, - .cmd_unix_sock_path = DEFAULT_USTCONSUMERD32_CMD_SOCK_PATH, .err_sock = -1, .cmd_sock = -1, + .channel_monitor_pipe = -1, + .channel_rotate_pipe = -1, .pid_mutex = PTHREAD_MUTEX_INITIALIZER, .lock = PTHREAD_MUTEX_INITIALIZER, .cond = PTHREAD_COND_INITIALIZER, @@ -165,21 +172,14 @@ static const struct option long_options[] = { { NULL, 0, 0, 0 } }; +struct sessiond_config config; + /* Command line options to ignore from configuration file */ static const char *config_ignore_options[] = { "help", "version", "config" }; /* Shared between threads */ static int dispatch_thread_exit; -/* Global application Unix socket path */ -static char apps_unix_sock_path[PATH_MAX]; -/* Global client Unix socket path */ -static char client_unix_sock_path[PATH_MAX]; -/* global wait shm path for UST */ -static char wait_shm_path[PATH_MAX]; -/* Global health check unix path */ -static char health_unix_sock_path[PATH_MAX]; - /* Sockets and FDs */ static int client_sock = -1; static int apps_sock = -1; @@ -211,6 +211,9 @@ static pthread_t health_thread; static pthread_t ht_cleanup_thread; static pthread_t agent_reg_thread; static pthread_t load_session_thread; +static pthread_t notification_thread; +static pthread_t rotation_thread; +static pthread_t timer_thread; /* * UST registration command queue. This queue is tied with a futex and uses a N @@ -238,15 +241,6 @@ static struct ltt_session_list *session_list_ptr; int ust_consumerd64_fd = -1; int ust_consumerd32_fd = -1; -static const char *consumerd32_bin = CONFIG_CONSUMERD32_BIN; -static const char *consumerd64_bin = CONFIG_CONSUMERD64_BIN; -static const char *consumerd32_libdir = CONFIG_CONSUMERD32_LIBDIR; -static const char *consumerd64_libdir = CONFIG_CONSUMERD64_LIBDIR; -static int consumerd32_bin_override; -static int consumerd64_bin_override; -static int consumerd32_libdir_override; -static int consumerd64_libdir_override; - static const char *module_proc_lttng = "/proc/lttng"; /* @@ -283,20 +277,12 @@ enum consumerd_state { static enum consumerd_state ust_consumerd_state; static enum consumerd_state kernel_consumerd_state; -/* - * Socket timeout for receiving and sending in seconds. - */ -static int app_socket_timeout; - /* Set in main() with the current page size. */ long page_size; /* Application health monitoring */ struct health_app *health_sessiond; -/* Agent TCP port for registration. Used by the agent thread. */ -unsigned int agent_tcp_port = DEFAULT_AGENT_TCP_PORT; - /* Am I root or not. */ int is_root; /* Set to 1 if the daemon is running as root */ @@ -305,16 +291,51 @@ const char * const config_section_name = "sessiond"; /* Load session thread information to operate. */ struct load_session_thread_data *load_info; +/* Notification thread handle. */ +struct notification_thread_handle *notification_thread_handle; + +/* Rotation thread handle. */ +struct rotation_thread_handle *rotation_thread_handle; + /* Global hash tables */ struct lttng_ht *agent_apps_ht_by_sock = NULL; /* - * Whether sessiond is ready for commands/health check requests. - * NR_LTTNG_SESSIOND_READY must match the number of calls to - * sessiond_notify_ready(). + * The initialization of the session daemon is done in multiple phases. + * + * While all threads are launched near-simultaneously, only some of them + * are needed to ensure the session daemon can start to respond to client + * requests. + * + * There are two important guarantees that we wish to offer with respect + * to the initialisation of the session daemon: + * - When the daemonize/background launcher process exits, the sessiond + * is fully able to respond to client requests, + * - Auto-loaded sessions are visible to clients. + * + * In order to achieve this, a number of support threads have to be launched + * to allow the "client" thread to function properly. Moreover, since the + * "load session" thread needs the client thread, we must provide a way + * for the "load session" thread to know that the "client" thread is up + * and running. + * + * Hence, the support threads decrement the lttng_sessiond_ready counter + * while the "client" threads waits for it to reach 0. Once the "client" thread + * unblocks, it posts the message_thread_ready semaphore which allows the + * "load session" thread to progress. + * + * This implies that the "load session" thread is the last to be initialized + * and will explicitly call sessiond_signal_parents(), which signals the parents + * that the session daemon is fully initialized. + * + * The four (4) support threads are: + * - agent_thread + * - notification_thread + * - rotation_thread + * - health_thread */ -#define NR_LTTNG_SESSIOND_READY 3 -int lttng_sessiond_ready = NR_LTTNG_SESSIOND_READY; +#define NR_LTTNG_SESSIOND_SUPPORT_THREADS 4 +int lttng_sessiond_ready = NR_LTTNG_SESSIOND_SUPPORT_THREADS; int sessiond_check_thread_quit_pipe(int fd, uint32_t events) { @@ -323,75 +344,45 @@ int sessiond_check_thread_quit_pipe(int fd, uint32_t events) /* Notify parents that we are ready for cmd and health check */ LTTNG_HIDDEN -void sessiond_notify_ready(void) +void sessiond_signal_parents(void) { - if (uatomic_sub_return(<tng_sessiond_ready, 1) == 0) { - /* - * Notify parent pid that we are ready to accept command - * for client side. This ppid is the one from the - * external process that spawned us. - */ - if (opt_sig_parent) { - kill(ppid, SIGUSR1); - } - - /* - * Notify the parent of the fork() process that we are - * ready. - */ - if (opt_daemon || opt_background) { - kill(child_ppid, SIGUSR1); - } + /* + * Notify parent pid that we are ready to accept command + * for client side. This ppid is the one from the + * external process that spawned us. + */ + if (config.sig_parent) { + kill(ppid, SIGUSR1); } -} - -static -void setup_consumerd_path(void) -{ - const char *bin, *libdir; /* - * Allow INSTALL_BIN_PATH to be used as a target path for the - * native architecture size consumer if CONFIG_CONSUMER*_PATH - * has not been defined. + * Notify the parent of the fork() process that we are + * ready. */ -#if (CAA_BITS_PER_LONG == 32) - if (!consumerd32_bin[0]) { - consumerd32_bin = INSTALL_BIN_PATH "/" CONSUMERD_FILE; + if (config.daemonize || config.background) { + kill(child_ppid, SIGUSR1); } - 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; - } -#else -#error "Unknown bitness" -#endif +} +LTTNG_HIDDEN +void sessiond_notify_ready(void) +{ /* - * runtime env. var. overrides the build default. + * 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. */ - bin = lttng_secure_getenv("LTTNG_CONSUMERD32_BIN"); - if (bin) { - consumerd32_bin = bin; - } - bin = lttng_secure_getenv("LTTNG_CONSUMERD64_BIN"); - if (bin) { - consumerd64_bin = bin; - } - libdir = lttng_secure_getenv("LTTNG_CONSUMERD32_LIBDIR"); - if (libdir) { - consumerd32_libdir = libdir; - } - libdir = lttng_secure_getenv("LTTNG_CONSUMERD64_LIBDIR"); - if (libdir) { - consumerd64_libdir = libdir; - } + cmm_smp_mb(); + uatomic_sub(<tng_sessiond_ready, 1); } static @@ -521,27 +512,42 @@ static void close_consumer_sockets(void) PERROR("UST consumerd64 cmd_sock close"); } } -} - -/* - * Generate the full lock file path using the rundir. - * - * Return the snprintf() return value thus a negative value is an error. - */ -static int generate_lock_file_path(char *path, size_t len) -{ - int ret; - - assert(path); - assert(rundir); - - /* Build lockfile path from rundir. */ - ret = snprintf(path, len, "%s/" DEFAULT_LTTNG_SESSIOND_LOCKFILE, rundir); - if (ret < 0) { - PERROR("snprintf lockfile path"); + if (kconsumer_data.channel_monitor_pipe >= 0) { + ret = close(kconsumer_data.channel_monitor_pipe); + if (ret < 0) { + PERROR("kernel consumer channel monitor pipe close"); + } + } + if (ustconsumer32_data.channel_monitor_pipe >= 0) { + ret = close(ustconsumer32_data.channel_monitor_pipe); + if (ret < 0) { + PERROR("UST consumerd32 channel monitor pipe close"); + } + } + if (ustconsumer64_data.channel_monitor_pipe >= 0) { + ret = close(ustconsumer64_data.channel_monitor_pipe); + if (ret < 0) { + PERROR("UST consumerd64 channel monitor pipe close"); + } + } + if (kconsumer_data.channel_rotate_pipe >= 0) { + ret = close(kconsumer_data.channel_rotate_pipe); + if (ret < 0) { + PERROR("kernel consumer channel rotate pipe close"); + } + } + if (ustconsumer32_data.channel_rotate_pipe >= 0) { + ret = close(ustconsumer32_data.channel_rotate_pipe); + if (ret < 0) { + PERROR("UST consumerd32 channel rotate pipe close"); + } + } + if (ustconsumer64_data.channel_rotate_pipe >= 0) { + ret = close(ustconsumer64_data.channel_rotate_pipe); + if (ret < 0) { + PERROR("UST consumerd64 channel rotate pipe close"); + } } - - return ret; } /* @@ -564,8 +570,7 @@ static void wait_consumer(struct consumer_data *consumer_data) ret = waitpid(consumer_data->pid, &status, 0); if (ret == -1) { PERROR("consumerd waitpid pid: %d", consumer_data->pid) - } - if (!WIFEXITED(status)) { + } else if (!WIFEXITED(status)) { ERR("consumerd termination with error: %d", WEXITSTATUS(ret)); } @@ -579,7 +584,6 @@ static void sessiond_cleanup(void) { int ret; struct ltt_session *sess, *stmp; - char path[PATH_MAX]; DBG("Cleanup sessiond"); @@ -589,69 +593,41 @@ static void sessiond_cleanup(void) */ utils_close_pipe(thread_quit_pipe); - /* - * If opt_pidfile is undefined, the default file will be wiped when - * removing the rundir. - */ - if (opt_pidfile) { - ret = remove(opt_pidfile); - if (ret < 0) { - PERROR("remove pidfile %s", opt_pidfile); - } + ret = remove(config.pid_file_path.value); + if (ret < 0) { + PERROR("remove pidfile %s", config.pid_file_path.value); } - DBG("Removing sessiond and consumerd content of directory %s", rundir); + DBG("Removing sessiond and consumerd content of directory %s", + config.rundir.value); /* sessiond */ - snprintf(path, PATH_MAX, - "%s/%s", - rundir, DEFAULT_LTTNG_SESSIOND_PIDFILE); - DBG("Removing %s", path); - (void) unlink(path); + DBG("Removing %s", config.pid_file_path.value); + (void) unlink(config.pid_file_path.value); - snprintf(path, PATH_MAX, "%s/%s", rundir, - DEFAULT_LTTNG_SESSIOND_AGENTPORT_FILE); - DBG("Removing %s", path); - (void) unlink(path); + DBG("Removing %s", config.agent_port_file_path.value); + (void) unlink(config.agent_port_file_path.value); /* kconsumerd */ - snprintf(path, PATH_MAX, - DEFAULT_KCONSUMERD_ERR_SOCK_PATH, - rundir); - DBG("Removing %s", path); - (void) unlink(path); - - snprintf(path, PATH_MAX, - DEFAULT_KCONSUMERD_PATH, - rundir); - DBG("Removing directory %s", path); - (void) rmdir(path); + DBG("Removing %s", kconsumer_data.err_unix_sock_path); + (void) unlink(kconsumer_data.err_unix_sock_path); + + DBG("Removing directory %s", config.kconsumerd_path.value); + (void) rmdir(config.kconsumerd_path.value); /* ust consumerd 32 */ - snprintf(path, PATH_MAX, - DEFAULT_USTCONSUMERD32_ERR_SOCK_PATH, - rundir); - DBG("Removing %s", path); - (void) unlink(path); - - snprintf(path, PATH_MAX, - DEFAULT_USTCONSUMERD32_PATH, - rundir); - DBG("Removing directory %s", path); - (void) rmdir(path); + DBG("Removing %s", config.consumerd32_err_unix_sock_path.value); + (void) unlink(config.consumerd32_err_unix_sock_path.value); + + DBG("Removing directory %s", config.consumerd32_path.value); + (void) rmdir(config.consumerd32_path.value); /* ust consumerd 64 */ - snprintf(path, PATH_MAX, - DEFAULT_USTCONSUMERD64_ERR_SOCK_PATH, - rundir); - DBG("Removing %s", path); - (void) unlink(path); - - snprintf(path, PATH_MAX, - DEFAULT_USTCONSUMERD64_PATH, - rundir); - DBG("Removing directory %s", path); - (void) rmdir(path); + DBG("Removing %s", config.consumerd64_err_unix_sock_path.value); + (void) unlink(config.consumerd64_err_unix_sock_path.value); + + DBG("Removing directory %s", config.consumerd64_path.value); + (void) rmdir(config.consumerd64_path.value); DBG("Cleaning up all sessions"); @@ -662,7 +638,8 @@ static void sessiond_cleanup(void) /* Cleanup ALL session */ cds_list_for_each_entry_safe(sess, stmp, &session_list_ptr->head, list) { - cmd_destroy_session(sess, kernel_poll_pipe[1]); + cmd_destroy_session(sess, kernel_poll_pipe[1], + notification_thread_handle); } } @@ -677,7 +654,7 @@ static void sessiond_cleanup(void) ust_app_clean_list(); buffer_reg_destroy_registries(); - if (is_root && !opt_no_kernel) { + if (is_root && !config.no_kernel) { DBG2("Closing kernel fd"); if (kernel_tracer_fd >= 0) { ret = close(kernel_tracer_fd); @@ -697,34 +674,11 @@ static void sessiond_cleanup(void) free(load_info); } - /* - * Cleanup lock file by deleting it and finaly closing it which will - * release the file system lock. - */ - if (lockfile_fd >= 0) { - char lockfile_path[PATH_MAX]; - - ret = generate_lock_file_path(lockfile_path, - sizeof(lockfile_path)); - if (ret > 0) { - ret = remove(lockfile_path); - if (ret < 0) { - PERROR("remove lock file"); - } - ret = close(lockfile_fd); - if (ret < 0) { - PERROR("close lock file"); - } - } - } - /* * We do NOT rmdir rundir because there are other processes * using it, for instance lttng-relayd, which can start in * parallel with this teardown. */ - - free(rundir); } /* @@ -734,38 +688,9 @@ static void sessiond_cleanup_options(void) { DBG("Cleaning up options"); - /* - * If the override option is set, the pointer points to a *non* const - * thus freeing it even though the variable type is set to const. - */ - if (tracing_group_name_override) { - free((void *) tracing_group_name); - } - if (consumerd32_bin_override) { - free((void *) consumerd32_bin); - } - if (consumerd64_bin_override) { - free((void *) consumerd64_bin); - } - if (consumerd32_libdir_override) { - free((void *) consumerd32_libdir); - } - if (consumerd64_libdir_override) { - free((void *) consumerd64_libdir); - } - - free(opt_pidfile); - free(opt_load_session_path); - free(kmod_probes_list); - free(kmod_extra_probes_list); + sessiond_config_fini(&config); run_as_destroy_worker(); - - /* */ - 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); - /* */ } /* @@ -811,7 +736,7 @@ static int notify_ust_apps(int active) DBG("Notifying applications of session daemon state: %d", active); /* See shm.c for this call implying mmap, shm and futex calls */ - wait_shm_mmap = shm_ust_get_mmap(wait_shm_path, is_root); + wait_shm_mmap = shm_ust_get_mmap(config.wait_shm_path.value, is_root); if (wait_shm_mmap == NULL) { goto error; } @@ -929,7 +854,7 @@ error: * * Useful for CPU hotplug feature. */ -static int update_kernel_stream(struct consumer_data *consumer_data, int fd) +static int update_kernel_stream(int fd) { int ret = 0; struct ltt_session *session; @@ -978,7 +903,7 @@ static int update_kernel_stream(struct consumer_data *consumer_data, int fd) cds_lfht_for_each_entry(ksess->consumer->socks->ht, &iter.iter, socket, node.node) { pthread_mutex_lock(socket->lock); - ret = kernel_consumer_send_channel_stream(socket, + ret = kernel_consumer_send_channel_streams(socket, channel, ksess, session->output_traces ? 1 : 0); pthread_mutex_unlock(socket->lock); @@ -1163,7 +1088,7 @@ static void *thread_manage_kernel(void *data) * New CPU detected by the kernel. Adding kernel stream to * kernel session and updating the kernel consumer */ - ret = update_kernel_stream(&kconsumer_data, pollfd); + ret = update_kernel_stream(pollfd); if (ret < 0) { continue; } @@ -1230,6 +1155,7 @@ static void *thread_manage_consumer(void *data) enum lttcomm_return_code code; struct lttng_poll_event events; struct consumer_data *consumer_data = data; + struct consumer_socket *cmd_socket_wrapper = NULL; DBG("[thread] Manage consumer started"); @@ -1339,39 +1265,43 @@ restart: } health_code_update(); - if (code == LTTCOMM_CONSUMERD_COMMAND_SOCK_READY) { - /* Connect both socket, command and metadata. */ - consumer_data->cmd_sock = - lttcomm_connect_unix_sock(consumer_data->cmd_unix_sock_path); - consumer_data->metadata_fd = - lttcomm_connect_unix_sock(consumer_data->cmd_unix_sock_path); - if (consumer_data->cmd_sock < 0 - || consumer_data->metadata_fd < 0) { - PERROR("consumer connect cmd socket"); - /* On error, signal condition and quit. */ - signal_consumer_condition(consumer_data, -1); - goto error; - } - consumer_data->metadata_sock.fd_ptr = &consumer_data->metadata_fd; - /* Create metadata socket lock. */ - consumer_data->metadata_sock.lock = zmalloc(sizeof(pthread_mutex_t)); - if (consumer_data->metadata_sock.lock == NULL) { - PERROR("zmalloc pthread mutex"); - 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; @@ -1392,6 +1322,36 @@ restart: health_code_update(); + /* + * Transfer the write-end of the channel monitoring and rotate pipe + * to the consumer by issuing a SET_CHANNEL_MONITOR_PIPE and + * SET_CHANNEL_ROTATE_PIPE commands. + */ + cmd_socket_wrapper = consumer_allocate_socket(&consumer_data->cmd_sock); + if (!cmd_socket_wrapper) { + goto error; + } + cmd_socket_wrapper->lock = &consumer_data->lock; + + ret = consumer_send_channel_monitor_pipe(cmd_socket_wrapper, + consumer_data->channel_monitor_pipe); + if (ret) { + goto error; + } + + ret = consumer_send_channel_rotate_pipe(cmd_socket_wrapper, + consumer_data->channel_rotate_pipe); + if (ret) { + goto error; + } + + /* Discard the socket wrapper as it is no longer needed. */ + consumer_destroy_socket(cmd_socket_wrapper); + cmd_socket_wrapper = NULL; + + /* The thread is completely initialized, signal that it is ready. */ + signal_consumer_condition(consumer_data, 1); + /* Infinite blocking call, waiting for transmission */ restart_poll: while (1) { @@ -1535,6 +1495,10 @@ error: free(consumer_data->metadata_sock.lock); } lttng_poll_clean(&events); + + if (cmd_socket_wrapper) { + consumer_destroy_socket(cmd_socket_wrapper); + } error_poll: if (err) { health_error(); @@ -1550,7 +1514,17 @@ error_poll: } /* - * This thread manage application communication. + * This thread receives application command sockets (FDs) on the + * apps_cmd_pipe and waits (polls) on them until they are closed + * or an error occurs. + * + * At that point, it flushes the data (tracing and metadata) associated + * with this application and tears down ust app sessions and other + * associated data structures through ust_app_unregister(). + * + * Note that this thread never sends commands to the applications + * through the command sockets; it merely listens for hang-ups + * and errors on those sockets and cleans-up as they occur. */ static void *thread_manage_apps(void *data) { @@ -1861,6 +1835,8 @@ static void *thread_dispatch_ust_registration(void *data) .count = 0, }; + rcu_register_thread(); + health_register(health_sessiond, HEALTH_SESSIOND_TYPE_APP_REG_DISPATCH); if (testpoint(sessiond_thread_app_reg_dispatch)) { @@ -1873,12 +1849,16 @@ static void *thread_dispatch_ust_registration(void *data) DBG("[thread] Dispatch UST command started"); - while (!CMM_LOAD_SHARED(dispatch_thread_exit)) { + for (;;) { health_code_update(); /* Atomically prepare the queue futex */ futex_nto1_prepare(&ust_cmd_queue.futex); + if (CMM_LOAD_SHARED(dispatch_thread_exit)) { + break; + } + do { struct ust_app *app = NULL; ust_cmd = NULL; @@ -2094,6 +2074,7 @@ error_testpoint: ERR("Health error occurred in %s", __func__); } health_unregister(health_sessiond); + rcu_unregister_thread(); return NULL; } @@ -2201,10 +2182,12 @@ static void *thread_registration_apps(void *data) * lttcomm_setsockopt_snd_timeout expect msec as * parameter. */ - (void) lttcomm_setsockopt_rcv_timeout(sock, - app_socket_timeout * 1000); - (void) lttcomm_setsockopt_snd_timeout(sock, - app_socket_timeout * 1000); + if (config.app_socket_timeout >= 0) { + (void) lttcomm_setsockopt_rcv_timeout(sock, + config.app_socket_timeout * 1000); + (void) lttcomm_setsockopt_snd_timeout(sock, + config.app_socket_timeout * 1000); + } /* * Set the CLOEXEC flag. Return code is useless because @@ -2304,7 +2287,7 @@ error: } lttng_fd_put(LTTNG_FD_APPS, 1); } - unlink(apps_unix_sock_path); + unlink(config.apps_unix_sock_path.value); error_poll_add: lttng_poll_clean(&events); @@ -2493,7 +2476,7 @@ static pid_t spawn_consumerd(struct consumer_data *consumer_data) /* * Exec consumerd. */ - if (opt_verbose_consumer) { + if (config.verbose_consumer) { verbosity = "--verbose"; } else if (lttng_opt_quiet) { verbosity = "--quiet"; @@ -2509,120 +2492,108 @@ static pid_t spawn_consumerd(struct consumer_data *consumer_data) * fallback on the 32-bit one, */ DBG3("Looking for a kernel consumer at these locations:"); - DBG3(" 1) %s", consumerd64_bin); - DBG3(" 2) %s/%s", INSTALL_BIN_PATH, CONSUMERD_FILE); - DBG3(" 3) %s", consumerd32_bin); - if (stat(consumerd64_bin, &st) == 0) { + DBG3(" 1) %s", config.consumerd64_bin_path.value ? : "NULL"); + DBG3(" 2) %s/%s", INSTALL_BIN_PATH, DEFAULT_CONSUMERD_FILE); + DBG3(" 3) %s", config.consumerd32_bin_path.value ? : "NULL"); + if (stat(config.consumerd64_bin_path.value, &st) == 0) { DBG3("Found location #1"); - consumer_to_use = consumerd64_bin; - } else if (stat(INSTALL_BIN_PATH "/" CONSUMERD_FILE, &st) == 0) { + consumer_to_use = config.consumerd64_bin_path.value; + } else if (stat(INSTALL_BIN_PATH "/" DEFAULT_CONSUMERD_FILE, &st) == 0) { DBG3("Found location #2"); - consumer_to_use = INSTALL_BIN_PATH "/" CONSUMERD_FILE; - } else if (stat(consumerd32_bin, &st) == 0) { + consumer_to_use = INSTALL_BIN_PATH "/" DEFAULT_CONSUMERD_FILE; + } else if (stat(config.consumerd32_bin_path.value, &st) == 0) { DBG3("Found location #3"); - consumer_to_use = consumerd32_bin; + consumer_to_use = config.consumerd32_bin_path.value; } else { DBG("Could not find any valid consumerd executable"); ret = -EINVAL; - break; + goto error; } DBG("Using kernel consumer at: %s", consumer_to_use); - ret = execl(consumer_to_use, + (void) execl(consumer_to_use, "lttng-consumerd", verbosity, "-k", "--consumerd-cmd-sock", consumer_data->cmd_unix_sock_path, "--consumerd-err-sock", consumer_data->err_unix_sock_path, - "--group", tracing_group_name, + "--group", config.tracing_group_name.value, NULL); break; case LTTNG_CONSUMER64_UST: { - char *tmpnew = NULL; - - if (consumerd64_libdir[0] != '\0') { + if (config.consumerd64_lib_dir.value) { char *tmp; size_t tmplen; + char *tmpnew; tmp = lttng_secure_getenv("LD_LIBRARY_PATH"); if (!tmp) { tmp = ""; } - tmplen = strlen("LD_LIBRARY_PATH=") - + strlen(consumerd64_libdir) + 1 /* : */ + strlen(tmp); + tmplen = strlen(config.consumerd64_lib_dir.value) + 1 /* : */ + strlen(tmp); tmpnew = zmalloc(tmplen + 1 /* \0 */); if (!tmpnew) { ret = -ENOMEM; goto error; } - strcpy(tmpnew, "LD_LIBRARY_PATH="); - strcat(tmpnew, consumerd64_libdir); + strcat(tmpnew, config.consumerd64_lib_dir.value); if (tmp[0] != '\0') { strcat(tmpnew, ":"); strcat(tmpnew, tmp); } - ret = putenv(tmpnew); + ret = setenv("LD_LIBRARY_PATH", tmpnew, 1); + free(tmpnew); if (ret) { ret = -errno; - free(tmpnew); goto error; } } - DBG("Using 64-bit UST consumer at: %s", consumerd64_bin); - ret = execl(consumerd64_bin, "lttng-consumerd", verbosity, "-u", + DBG("Using 64-bit UST consumer at: %s", config.consumerd64_bin_path.value); + (void) execl(config.consumerd64_bin_path.value, "lttng-consumerd", verbosity, "-u", "--consumerd-cmd-sock", consumer_data->cmd_unix_sock_path, "--consumerd-err-sock", consumer_data->err_unix_sock_path, - "--group", tracing_group_name, + "--group", config.tracing_group_name.value, NULL); - if (consumerd64_libdir[0] != '\0') { - free(tmpnew); - } break; } case LTTNG_CONSUMER32_UST: { - char *tmpnew = NULL; - - if (consumerd32_libdir[0] != '\0') { + if (config.consumerd32_lib_dir.value) { char *tmp; size_t tmplen; + char *tmpnew; tmp = lttng_secure_getenv("LD_LIBRARY_PATH"); if (!tmp) { tmp = ""; } - tmplen = strlen("LD_LIBRARY_PATH=") - + strlen(consumerd32_libdir) + 1 /* : */ + strlen(tmp); + tmplen = strlen(config.consumerd32_lib_dir.value) + 1 /* : */ + strlen(tmp); tmpnew = zmalloc(tmplen + 1 /* \0 */); if (!tmpnew) { ret = -ENOMEM; goto error; } - strcpy(tmpnew, "LD_LIBRARY_PATH="); - strcat(tmpnew, consumerd32_libdir); + strcat(tmpnew, config.consumerd32_lib_dir.value); if (tmp[0] != '\0') { strcat(tmpnew, ":"); strcat(tmpnew, tmp); } - ret = putenv(tmpnew); + ret = setenv("LD_LIBRARY_PATH", tmpnew, 1); + free(tmpnew); if (ret) { ret = -errno; - free(tmpnew); goto error; } } - DBG("Using 32-bit UST consumer at: %s", consumerd32_bin); - ret = execl(consumerd32_bin, "lttng-consumerd", verbosity, "-u", + DBG("Using 32-bit UST consumer at: %s", config.consumerd32_bin_path.value); + (void) execl(config.consumerd32_bin_path.value, "lttng-consumerd", verbosity, "-u", "--consumerd-cmd-sock", consumer_data->cmd_unix_sock_path, "--consumerd-err-sock", consumer_data->err_unix_sock_path, - "--group", tracing_group_name, + "--group", config.tracing_group_name.value, NULL); - if (consumerd32_libdir[0] != '\0') { - free(tmpnew); - } break; } default: - PERROR("unknown consumer type"); - exit(EXIT_FAILURE); + ERR("unknown consumer type"); + errno = 0; } if (errno != 0) { PERROR("Consumer execl()"); @@ -2714,12 +2685,12 @@ static int init_kernel_tracer(void) kernel_tracer_fd = open(module_proc_lttng, O_RDWR); if (kernel_tracer_fd < 0) { DBG("Failed to open %s", module_proc_lttng); - ret = -1; goto error_open; } /* Validate kernel version */ - ret = kernel_validate_version(kernel_tracer_fd); + ret = kernel_validate_version(kernel_tracer_fd, &kernel_tracer_version, + &kernel_tracer_abi_version); if (ret < 0) { goto error_version; } @@ -2729,6 +2700,18 @@ static int init_kernel_tracer(void) goto error_modules; } + ret = kernel_supports_ring_buffer_snapshot_sample_positions( + kernel_tracer_fd); + if (ret < 0) { + goto error_modules; + } + + if (ret < 1) { + WARN("Kernel tracer does not support buffer monitoring. " + "The monitoring timer of channels in the kernel domain " + "will be set to 0 (disabled)."); + } + DBG("Kernel tracer fd %d", kernel_tracer_fd); return 0; @@ -2914,20 +2897,6 @@ static int create_kernel_session(struct ltt_session *session) goto error; } - /* Create directory(ies) on local filesystem. */ - if (session->kernel_session->consumer->type == CONSUMER_DST_LOCAL && - strlen(session->kernel_session->consumer->dst.trace_path) > 0) { - ret = run_as_mkdir_recursive( - session->kernel_session->consumer->dst.trace_path, - S_IRWXU | S_IRWXG, session->uid, session->gid); - if (ret < 0) { - if (errno != EEXIST) { - ERR("Trace directory creation error"); - goto error; - } - } - } - session->kernel_session->uid = session->uid; session->kernel_session->gid = session->gid; session->kernel_session->output_traces = session->output_traces; @@ -2963,6 +2932,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) @@ -3005,13 +3096,20 @@ static int process_client_msg(struct command_ctx *cmd_ctx, int sock, case LTTNG_SET_SESSION_SHM_PATH: case LTTNG_REGENERATE_METADATA: case LTTNG_REGENERATE_STATEDUMP: + case LTTNG_REGISTER_TRIGGER: + case LTTNG_UNREGISTER_TRIGGER: + case LTTNG_ROTATE_SESSION: + case LTTNG_ROTATION_GET_INFO: + case LTTNG_SESSION_GET_CURRENT_OUTPUT: + case LTTNG_ROTATION_SET_SCHEDULE: + case LTTNG_SESSION_LIST_ROTATION_SCHEDULES: need_domain = 0; break; default: need_domain = 1; } - if (opt_no_kernel && need_domain + if (config.no_kernel && need_domain && cmd_ctx->lsm->domain.type == LTTNG_DOMAIN_KERNEL) { if (!is_root) { ret = LTTNG_ERR_NEED_ROOT_SESSIOND; @@ -3047,6 +3145,9 @@ static int process_client_msg(struct command_ctx *cmd_ctx, int sock, 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 */ @@ -3062,12 +3163,13 @@ static int process_client_msg(struct command_ctx *cmd_ctx, int sock, case LTTNG_CREATE_SESSION: case LTTNG_CREATE_SESSION_SNAPSHOT: case LTTNG_CREATE_SESSION_LIVE: - case LTTNG_CALIBRATE: case LTTNG_LIST_SESSIONS: case LTTNG_LIST_TRACEPOINTS: case LTTNG_LIST_SYSCALLS: case LTTNG_LIST_TRACEPOINT_FIELDS: case LTTNG_SAVE_SESSION: + case LTTNG_REGISTER_TRIGGER: + case LTTNG_UNREGISTER_TRIGGER: need_tracing_session = 0; break; default: @@ -3216,7 +3318,7 @@ static int process_client_msg(struct command_ctx *cmd_ctx, int sock, /* Start the UST consumer daemons */ /* 64-bit */ pthread_mutex_lock(&ustconsumer64_data.pid_mutex); - if (consumerd64_bin[0] != '\0' && + if (config.consumerd64_bin_path.value && ustconsumer64_data.pid == 0 && cmd_ctx->lsm->cmd_type != LTTNG_REGISTER_CONSUMER) { pthread_mutex_unlock(&ustconsumer64_data.pid_mutex); @@ -3245,7 +3347,7 @@ static int process_client_msg(struct command_ctx *cmd_ctx, int sock, /* 32-bit */ pthread_mutex_lock(&ustconsumer32_data.pid_mutex); - if (consumerd32_bin[0] != '\0' && + if (config.consumerd32_bin_path.value && ustconsumer32_data.pid == 0 && cmd_ctx->lsm->cmd_type != LTTNG_REGISTER_CONSUMER) { pthread_mutex_unlock(&ustconsumer32_data.pid_mutex); @@ -3451,8 +3553,11 @@ error_add_context: } case LTTNG_ENABLE_CHANNEL: { + cmd_ctx->lsm->u.channel.chan.attr.extended.ptr = + (struct lttng_channel_extended *) &cmd_ctx->lsm->u.channel.extended; ret = cmd_enable_channel(cmd_ctx->session, &cmd_ctx->lsm->domain, - &cmd_ctx->lsm->u.channel.chan, kernel_poll_pipe[1]); + &cmd_ctx->lsm->u.channel.chan, + kernel_poll_pipe[1]); break; } case LTTNG_TRACK_PID: @@ -3471,6 +3576,7 @@ error_add_context: } case LTTNG_ENABLE_EVENT: { + struct lttng_event *ev = NULL; struct lttng_event_exclusion *exclusion = NULL; struct lttng_filter_bytecode *bytecode = NULL; char *filter_expression = NULL; @@ -3522,7 +3628,7 @@ error_add_context: ret = lttcomm_recv_unix_sock(sock, filter_expression, expression_len); if (ret <= 0) { - DBG("Nothing recv() from client car len data... continuing"); + DBG("Nothing recv() from client var len data... continuing"); *sock_error = 1; free(filter_expression); free(exclusion); @@ -3554,7 +3660,7 @@ error_add_context: DBG("Receiving var len filter's bytecode from client ..."); ret = lttcomm_recv_unix_sock(sock, bytecode, bytecode_len); if (ret <= 0) { - DBG("Nothing recv() from client car len data... continuing"); + DBG("Nothing recv() from client var len data... continuing"); *sock_error = 1; free(filter_expression); free(bytecode); @@ -3572,11 +3678,30 @@ error_add_context: } } + ev = lttng_event_copy(&cmd_ctx->lsm->u.enable.event); + if (!ev) { + DBG("Failed to copy event: %s", + cmd_ctx->lsm->u.enable.event.name); + ret = LTTNG_ERR_NOMEM; + goto error; + } + + + if (cmd_ctx->lsm->u.enable.userspace_probe_location_len > 0) { + /* Expect a userspace probe description. */ + ret = receive_userspace_probe(cmd_ctx, sock, sock_error, ev); + if (ret) { + lttng_event_destroy(ev); + goto error; + } + } + ret = cmd_enable_event(cmd_ctx->session, &cmd_ctx->lsm->domain, cmd_ctx->lsm->u.enable.channel_name, - &cmd_ctx->lsm->u.enable.event, + ev, filter_expression, bytecode, exclusion, kernel_poll_pipe[1]); + lttng_event_destroy(ev); break; } case LTTNG_LIST_TRACEPOINTS: @@ -3734,6 +3859,20 @@ error_add_context: } 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; } @@ -3785,7 +3924,8 @@ error_add_context: } case LTTNG_DESTROY_SESSION: { - ret = cmd_destroy_session(cmd_ctx->session, kernel_poll_pipe[1]); + ret = cmd_destroy_session(cmd_ctx->session, kernel_poll_pipe[1], + notification_thread_handle); /* Set session to NULL so we do not unlock it after free. */ cmd_ctx->session = NULL; @@ -3904,13 +4044,7 @@ error_add_context: 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: + case LTTNG_REGISTER_CONSUMER: { struct consumer_data *cdata; @@ -4125,6 +4259,137 @@ error_add_context: 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_SESSION_GET_CURRENT_OUTPUT: + { + struct lttng_session_get_current_output_return output_return; + + memset(&output_return, 0, sizeof(output_return)); + ret = cmd_session_get_current_output(cmd_ctx->session, + &output_return); + if (ret < 0) { + ret = -ret; + goto error; + } + + ret = setup_lttng_msg_no_cmd_header(cmd_ctx, &output_return, + sizeof(output_return)); + if (ret < 0) { + ret = -ret; + goto error; + } + + ret = LTTNG_OK; + break; + } + case LTTNG_ROTATION_SET_SCHEDULE: + { + bool set_schedule; + enum lttng_rotation_schedule_type schedule_type; + uint64_t value; + + if (cmd_ctx->session->kernel_session && !check_rotate_compatible()) { + DBG("Kernel tracer version does not support session rotations"); + ret = LTTNG_ERR_ROTATION_WRONG_VERSION; + goto error; + } + + set_schedule = cmd_ctx->lsm->u.rotation_set_schedule.set == 1; + schedule_type = (enum lttng_rotation_schedule_type) cmd_ctx->lsm->u.rotation_set_schedule.type; + value = cmd_ctx->lsm->u.rotation_set_schedule.value; + + ret = cmd_rotation_set_schedule(cmd_ctx->session, + set_schedule, + schedule_type, + value, + notification_thread_handle); + if (ret != LTTNG_OK) { + goto error; + } + + break; + } + case LTTNG_SESSION_LIST_ROTATION_SCHEDULES: + { + struct lttng_session_list_schedules_return schedules = { + .periodic.set = !!cmd_ctx->session->rotate_timer_period, + .periodic.value = cmd_ctx->session->rotate_timer_period, + .size.set = !!cmd_ctx->session->rotate_size, + .size.value = cmd_ctx->session->rotate_size, + }; + + ret = setup_lttng_msg_no_cmd_header(cmd_ctx, &schedules, + sizeof(schedules)); + if (ret < 0) { + ret = -ret; + goto error; + } + + ret = LTTNG_OK; + break; + } default: ret = LTTNG_ERR_UND; break; @@ -4170,7 +4435,7 @@ 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"); goto error; @@ -4178,18 +4443,18 @@ static void *thread_manage_health(void *data) 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"); 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"); goto error; } @@ -4327,7 +4592,7 @@ error: ERR("Health error occurred in %s", __func__); } DBG("Health check thread dying"); - unlink(health_unix_sock_path); + unlink(config.health_unix_sock_path.value); if (sock >= 0) { ret = close(sock); if (ret) { @@ -4336,7 +4601,7 @@ error: } lttng_poll_clean(&events); - + stop_threads(); rcu_unregister_thread(); return NULL; } @@ -4381,13 +4646,50 @@ static void *thread_manage_clients(void *data) goto error; } - sessiond_notify_ready(); ret = sem_post(&load_info->message_thread_ready); if (ret) { PERROR("sem_post message_thread_ready"); goto error; } + /* + * Wait until all support threads are initialized before accepting + * commands. + */ + while (uatomic_read(<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(); + /* This testpoint is after we signal readiness to the parent. */ if (testpoint(sessiond_thread_manage_clients)) { goto error; @@ -4400,6 +4702,8 @@ static void *thread_manage_clients(void *data) health_code_update(); while (1) { + const struct cmd_completion_handler *cmd_completion_handler; + DBG("Accepting client command ..."); /* Inifinite blocking call, waiting for transmission */ @@ -4542,6 +4846,18 @@ static void *thread_manage_clients(void *data) continue; } + cmd_completion_handler = cmd_pop_completion_handler(); + if (cmd_completion_handler) { + enum lttng_error_code completion_code; + + completion_code = cmd_completion_handler->run( + cmd_completion_handler->data); + if (completion_code != LTTNG_OK) { + clean_command_ctx(&cmd_ctx); + continue; + } + } + health_code_update(); DBG("Sending response (size: %d, retcode: %s (%d))", @@ -4579,7 +4895,7 @@ error: error_listen: error_create_poll: - unlink(client_unix_sock_path); + unlink(config.client_unix_sock_path.value); if (client_sock >= 0) { ret = close(client_sock); if (ret) { @@ -4646,7 +4962,12 @@ static int set_option(int opt, const char *arg, const char *optname) WARN("Getting '%s' argument from setuid/setgid binary refused for security reasons.", "-c, --client-sock"); } else { - snprintf(client_unix_sock_path, PATH_MAX, "%s", arg); + config_string_set(&config.client_unix_sock_path, + strdup(arg)); + if (!config.client_unix_sock_path.value) { + ret = -ENOMEM; + PERROR("strdup"); + } } } else if (string_match(optname, "apps-sock") || opt == 'a') { if (!arg || *arg == '\0') { @@ -4657,12 +4978,17 @@ static int set_option(int opt, const char *arg, const char *optname) WARN("Getting '%s' argument from setuid/setgid binary refused for security reasons.", "-a, --apps-sock"); } else { - snprintf(apps_unix_sock_path, PATH_MAX, "%s", arg); + config_string_set(&config.apps_unix_sock_path, + strdup(arg)); + if (!config.apps_unix_sock_path.value) { + ret = -ENOMEM; + PERROR("strdup"); + } } } else if (string_match(optname, "daemonize") || opt == 'd') { - opt_daemon = 1; + config.daemonize = true; } else if (string_match(optname, "background") || opt == 'b') { - opt_background = 1; + config.background = true; } else if (string_match(optname, "group") || opt == 'g') { if (!arg || *arg == '\0') { ret = -EINVAL; @@ -4672,25 +4998,17 @@ static int set_option(int opt, const char *arg, const char *optname) WARN("Getting '%s' argument from setuid/setgid binary refused for security reasons.", "-g, --group"); } else { - /* - * If the override option is set, the pointer points to a - * *non* const thus freeing it even though the variable type is - * set to const. - */ - if (tracing_group_name_override) { - free((void *) tracing_group_name); - } - tracing_group_name = strdup(arg); - if (!tracing_group_name) { - PERROR("strdup"); + config_string_set(&config.tracing_group_name, + strdup(arg)); + if (!config.tracing_group_name.value) { ret = -ENOMEM; + PERROR("strdup"); } - tracing_group_name_override = 1; } } else if (string_match(optname, "help") || opt == 'h') { - ret = utils_show_man_page(8, "lttng-sessiond"); + ret = utils_show_help(8, "lttng-sessiond", help_msg); if (ret) { - ERR("Cannot view man page lttng-sessiond(8)"); + ERR("Cannot show --help for `lttng-sessiond`"); perror("exec"); } exit(ret ? EXIT_FAILURE : EXIT_SUCCESS); @@ -4698,7 +5016,7 @@ static int set_option(int opt, const char *arg, const char *optname) fprintf(stdout, "%s\n", VERSION); exit(EXIT_SUCCESS); } else if (string_match(optname, "sig-parent") || opt == 'S') { - opt_sig_parent = 1; + config.sig_parent = true; } else if (string_match(optname, "kconsumerd-err-sock")) { if (!arg || *arg == '\0') { ret = -EINVAL; @@ -4708,7 +5026,12 @@ static int set_option(int opt, const char *arg, const char *optname) WARN("Getting '%s' argument from setuid/setgid binary refused for security reasons.", "--kconsumerd-err-sock"); } else { - snprintf(kconsumer_data.err_unix_sock_path, PATH_MAX, "%s", arg); + config_string_set(&config.kconsumerd_err_unix_sock_path, + strdup(arg)); + if (!config.kconsumerd_err_unix_sock_path.value) { + ret = -ENOMEM; + PERROR("strdup"); + } } } else if (string_match(optname, "kconsumerd-cmd-sock")) { if (!arg || *arg == '\0') { @@ -4719,7 +5042,12 @@ static int set_option(int opt, const char *arg, const char *optname) WARN("Getting '%s' argument from setuid/setgid binary refused for security reasons.", "--kconsumerd-cmd-sock"); } else { - snprintf(kconsumer_data.cmd_unix_sock_path, PATH_MAX, "%s", arg); + config_string_set(&config.kconsumerd_cmd_unix_sock_path, + strdup(arg)); + if (!config.kconsumerd_cmd_unix_sock_path.value) { + ret = -ENOMEM; + PERROR("strdup"); + } } } else if (string_match(optname, "ustconsumerd64-err-sock")) { if (!arg || *arg == '\0') { @@ -4730,7 +5058,12 @@ static int set_option(int opt, const char *arg, const char *optname) WARN("Getting '%s' argument from setuid/setgid binary refused for security reasons.", "--ustconsumerd64-err-sock"); } else { - snprintf(ustconsumer64_data.err_unix_sock_path, PATH_MAX, "%s", arg); + config_string_set(&config.consumerd64_err_unix_sock_path, + strdup(arg)); + if (!config.consumerd64_err_unix_sock_path.value) { + ret = -ENOMEM; + PERROR("strdup"); + } } } else if (string_match(optname, "ustconsumerd64-cmd-sock")) { if (!arg || *arg == '\0') { @@ -4741,7 +5074,12 @@ static int set_option(int opt, const char *arg, const char *optname) WARN("Getting '%s' argument from setuid/setgid binary refused for security reasons.", "--ustconsumerd64-cmd-sock"); } else { - snprintf(ustconsumer64_data.cmd_unix_sock_path, PATH_MAX, "%s", arg); + config_string_set(&config.consumerd64_cmd_unix_sock_path, + strdup(arg)); + if (!config.consumerd64_cmd_unix_sock_path.value) { + ret = -ENOMEM; + PERROR("strdup"); + } } } else if (string_match(optname, "ustconsumerd32-err-sock")) { if (!arg || *arg == '\0') { @@ -4752,7 +5090,12 @@ static int set_option(int opt, const char *arg, const char *optname) WARN("Getting '%s' argument from setuid/setgid binary refused for security reasons.", "--ustconsumerd32-err-sock"); } else { - snprintf(ustconsumer32_data.err_unix_sock_path, PATH_MAX, "%s", arg); + config_string_set(&config.consumerd32_err_unix_sock_path, + strdup(arg)); + if (!config.consumerd32_err_unix_sock_path.value) { + ret = -ENOMEM; + PERROR("strdup"); + } } } else if (string_match(optname, "ustconsumerd32-cmd-sock")) { if (!arg || *arg == '\0') { @@ -4763,29 +5106,34 @@ static int set_option(int opt, const char *arg, const char *optname) WARN("Getting '%s' argument from setuid/setgid binary refused for security reasons.", "--ustconsumerd32-cmd-sock"); } else { - snprintf(ustconsumer32_data.cmd_unix_sock_path, PATH_MAX, "%s", arg); + config_string_set(&config.consumerd32_cmd_unix_sock_path, + strdup(arg)); + if (!config.consumerd32_cmd_unix_sock_path.value) { + ret = -ENOMEM; + PERROR("strdup"); + } } } else if (string_match(optname, "no-kernel")) { - opt_no_kernel = 1; + config.no_kernel = true; } else if (string_match(optname, "quiet") || opt == 'q') { - lttng_opt_quiet = 1; + config.quiet = true; } else if (string_match(optname, "verbose") || opt == 'v') { /* Verbose level can increase using multiple -v */ if (arg) { /* Value obtained from config file */ - lttng_opt_verbose = config_parse_value(arg); + config.verbose = config_parse_value(arg); } else { /* -v used on command line */ - lttng_opt_verbose++; + config.verbose++; } /* Clamp value to [0, 3] */ - lttng_opt_verbose = lttng_opt_verbose < 0 ? 0 : - (lttng_opt_verbose <= 3 ? lttng_opt_verbose : 3); + config.verbose = config.verbose < 0 ? 0 : + (config.verbose <= 3 ? config.verbose : 3); } else if (string_match(optname, "verbose-consumer")) { if (arg) { - opt_verbose_consumer = config_parse_value(arg); + config.verbose_consumer = config_parse_value(arg); } else { - opt_verbose_consumer++; + config.verbose_consumer++; } } else if (string_match(optname, "consumerd32-path")) { if (!arg || *arg == '\0') { @@ -4796,15 +5144,12 @@ static int set_option(int opt, const char *arg, const char *optname) WARN("Getting '%s' argument from setuid/setgid binary refused for security reasons.", "--consumerd32-path"); } else { - if (consumerd32_bin_override) { - free((void *) consumerd32_bin); - } - consumerd32_bin = strdup(arg); - if (!consumerd32_bin) { + config_string_set(&config.consumerd32_bin_path, + strdup(arg)); + if (!config.consumerd32_bin_path.value) { PERROR("strdup"); ret = -ENOMEM; } - consumerd32_bin_override = 1; } } else if (string_match(optname, "consumerd32-libdir")) { if (!arg || *arg == '\0') { @@ -4815,15 +5160,12 @@ static int set_option(int opt, const char *arg, const char *optname) WARN("Getting '%s' argument from setuid/setgid binary refused for security reasons.", "--consumerd32-libdir"); } else { - if (consumerd32_libdir_override) { - free((void *) consumerd32_libdir); - } - consumerd32_libdir = strdup(arg); - if (!consumerd32_libdir) { + config_string_set(&config.consumerd32_lib_dir, + strdup(arg)); + if (!config.consumerd32_lib_dir.value) { PERROR("strdup"); ret = -ENOMEM; } - consumerd32_libdir_override = 1; } } else if (string_match(optname, "consumerd64-path")) { if (!arg || *arg == '\0') { @@ -4834,15 +5176,12 @@ static int set_option(int opt, const char *arg, const char *optname) WARN("Getting '%s' argument from setuid/setgid binary refused for security reasons.", "--consumerd64-path"); } else { - if (consumerd64_bin_override) { - free((void *) consumerd64_bin); - } - consumerd64_bin = strdup(arg); - if (!consumerd64_bin) { + config_string_set(&config.consumerd64_bin_path, + strdup(arg)); + if (!config.consumerd64_bin_path.value) { PERROR("strdup"); ret = -ENOMEM; } - consumerd64_bin_override = 1; } } else if (string_match(optname, "consumerd64-libdir")) { if (!arg || *arg == '\0') { @@ -4853,15 +5192,12 @@ static int set_option(int opt, const char *arg, const char *optname) WARN("Getting '%s' argument from setuid/setgid binary refused for security reasons.", "--consumerd64-libdir"); } else { - if (consumerd64_libdir_override) { - free((void *) consumerd64_libdir); - } - consumerd64_libdir = strdup(arg); - if (!consumerd64_libdir) { + config_string_set(&config.consumerd64_lib_dir, + strdup(arg)); + if (!config.consumerd64_lib_dir.value) { PERROR("strdup"); ret = -ENOMEM; } - consumerd64_libdir_override = 1; } } else if (string_match(optname, "pidfile") || opt == 'p') { if (!arg || *arg == '\0') { @@ -4872,9 +5208,8 @@ static int set_option(int opt, const char *arg, const char *optname) WARN("Getting '%s' argument from setuid/setgid binary refused for security reasons.", "-p, --pidfile"); } else { - free(opt_pidfile); - opt_pidfile = strdup(arg); - if (!opt_pidfile) { + config_string_set(&config.pid_file_path, strdup(arg)); + if (!config.pid_file_path.value) { PERROR("strdup"); ret = -ENOMEM; } @@ -4900,8 +5235,8 @@ static int set_option(int opt, const char *arg, const char *optname) ERR("Port overflow in --agent-tcp-port parameter: %s", arg); return -1; } - agent_tcp_port = (uint32_t) v; - DBG3("Agent TCP port set to non default: %u", agent_tcp_port); + config.agent_tcp_port.begin = config.agent_tcp_port.end = (int) v; + DBG3("Agent TCP port set to non default: %i", (int) v); } } else if (string_match(optname, "load") || opt == 'l') { if (!arg || *arg == '\0') { @@ -4912,9 +5247,8 @@ static int set_option(int opt, const char *arg, const char *optname) WARN("Getting '%s' argument from setuid/setgid binary refused for security reasons.", "-l, --load"); } else { - free(opt_load_session_path); - opt_load_session_path = strdup(arg); - if (!opt_load_session_path) { + config_string_set(&config.load_session_path, strdup(arg)); + if (!config.load_session_path.value) { PERROR("strdup"); ret = -ENOMEM; } @@ -4928,9 +5262,8 @@ static int set_option(int opt, const char *arg, const char *optname) WARN("Getting '%s' argument from setuid/setgid binary refused for security reasons.", "--kmod-probes"); } else { - free(kmod_probes_list); - kmod_probes_list = strdup(arg); - if (!kmod_probes_list) { + config_string_set(&config.kmod_probes_list, strdup(arg)); + if (!config.kmod_probes_list.value) { PERROR("strdup"); ret = -ENOMEM; } @@ -4944,9 +5277,9 @@ static int set_option(int opt, const char *arg, const char *optname) WARN("Getting '%s' argument from setuid/setgid binary refused for security reasons.", "--extra-kmod-probes"); } else { - free(kmod_extra_probes_list); - kmod_extra_probes_list = strdup(arg); - if (!kmod_extra_probes_list) { + config_string_set(&config.kmod_extra_probes_list, + strdup(arg)); + if (!config.kmod_extra_probes_list.value) { PERROR("strdup"); ret = -ENOMEM; } @@ -5130,9 +5463,9 @@ static int init_daemon_socket(void) old_umask = umask(0); /* Create client tool unix socket */ - client_sock = lttcomm_create_unix_sock(client_unix_sock_path); + client_sock = lttcomm_create_unix_sock(config.client_unix_sock_path.value); if (client_sock < 0) { - ERR("Create unix sock failed: %s", client_unix_sock_path); + ERR("Create unix sock failed: %s", config.client_unix_sock_path.value); ret = -1; goto end; } @@ -5146,17 +5479,17 @@ static int init_daemon_socket(void) } /* File permission MUST be 660 */ - ret = chmod(client_unix_sock_path, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP); + ret = chmod(config.client_unix_sock_path.value, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP); if (ret < 0) { - ERR("Set file permissions failed: %s", client_unix_sock_path); + ERR("Set file permissions failed: %s", config.client_unix_sock_path.value); PERROR("chmod"); goto end; } /* Create the application unix socket */ - apps_sock = lttcomm_create_unix_sock(apps_unix_sock_path); + apps_sock = lttcomm_create_unix_sock(config.apps_unix_sock_path.value); if (apps_sock < 0) { - ERR("Create unix sock failed: %s", apps_unix_sock_path); + ERR("Create unix sock failed: %s", config.apps_unix_sock_path.value); ret = -1; goto end; } @@ -5170,10 +5503,10 @@ static int init_daemon_socket(void) } /* File permission MUST be 666 */ - ret = chmod(apps_unix_sock_path, + ret = chmod(config.apps_unix_sock_path.value, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH); if (ret < 0) { - ERR("Set file permissions failed: %s", apps_unix_sock_path); + ERR("Set file permissions failed: %s", config.apps_unix_sock_path.value); PERROR("chmod"); goto end; } @@ -5186,18 +5519,57 @@ 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"); + } + } } /* @@ -5211,7 +5583,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); @@ -5232,9 +5604,9 @@ static int set_permissions(char *rundir) } /* lttng client socket path */ - ret = chown(client_unix_sock_path, 0, gid); + ret = chown(config.client_unix_sock_path.value, 0, gid); if (ret < 0) { - ERR("Unable to set group on %s", client_unix_sock_path); + ERR("Unable to set group on %s", config.client_unix_sock_path.value); PERROR("chown"); } @@ -5267,16 +5639,16 @@ static int set_permissions(char *rundir) /* * Create the lttng run directory needed for all global sockets and pipe. */ -static int create_lttng_rundir(const char *rundir) +static int create_lttng_rundir(void) { int ret; - DBG3("Creating LTTng run directory: %s", rundir); + DBG3("Creating LTTng run directory: %s", config.rundir.value); - ret = mkdir(rundir, S_IRWXU); + ret = mkdir(config.rundir.value, S_IRWXU); if (ret < 0) { if (errno != EEXIST) { - ERR("Unable to create %s", rundir); + ERR("Unable to create %s", config.rundir.value); goto error; } else { ret = 0; @@ -5288,44 +5660,41 @@ error: } /* - * Setup sockets and directory needed by the kconsumerd communication with the + * Setup sockets and directory needed by the consumerds' communication with the * session daemon. */ -static int set_consumer_sockets(struct consumer_data *consumer_data, - const char *rundir) +static int set_consumer_sockets(struct consumer_data *consumer_data) { int ret; - char path[PATH_MAX]; + char *path = NULL; switch (consumer_data->type) { case LTTNG_CONSUMER_KERNEL: - snprintf(path, PATH_MAX, DEFAULT_KCONSUMERD_PATH, rundir); + path = config.kconsumerd_path.value; break; case LTTNG_CONSUMER64_UST: - snprintf(path, PATH_MAX, DEFAULT_USTCONSUMERD64_PATH, rundir); + path = config.consumerd64_path.value; break; case LTTNG_CONSUMER32_UST: - snprintf(path, PATH_MAX, DEFAULT_USTCONSUMERD32_PATH, rundir); + path = config.consumerd32_path.value; break; default: ERR("Consumer type unknown"); ret = -EINVAL; goto error; } + assert(path); DBG2("Creating consumer directory: %s", path); ret = mkdir(path, S_IRWXU | S_IRGRP | S_IXGRP); - if (ret < 0) { - if (errno != EEXIST) { - PERROR("mkdir"); - ERR("Failed to create %s", path); - goto error; - } - ret = -1; + if (ret < 0 && errno != EEXIST) { + PERROR("mkdir"); + ERR("Failed to create %s", path); + goto error; } if (is_root) { - ret = chown(path, 0, utils_get_group_id(tracing_group_name)); + ret = chown(path, 0, utils_get_group_id(config.tracing_group_name.value)); if (ret < 0) { ERR("Unable to set group on %s", path); PERROR("chown"); @@ -5333,7 +5702,7 @@ static int set_consumer_sockets(struct consumer_data *consumer_data, } } - /* Create the kconsumerd error unix socket */ + /* Create the consumerd error unix socket */ consumer_data->err_sock = lttcomm_create_unix_sock(consumer_data->err_unix_sock_path); if (consumer_data->err_sock < 0) { @@ -5374,9 +5743,6 @@ error: static void sighandler(int sig) { switch (sig) { - case SIGPIPE: - DBG("SIGPIPE caught"); - return; case SIGINT: DBG("SIGINT caught"); stop_threads(); @@ -5408,9 +5774,10 @@ static int set_signal_handler(void) return ret; } - sa.sa_handler = sighandler; sa.sa_mask = sigset; sa.sa_flags = 0; + + sa.sa_handler = sighandler; if ((ret = sigaction(SIGTERM, &sa, NULL)) < 0) { PERROR("sigaction"); return ret; @@ -5421,12 +5788,13 @@ static int set_signal_handler(void) return ret; } - if ((ret = sigaction(SIGPIPE, &sa, NULL)) < 0) { + if ((ret = sigaction(SIGUSR1, &sa, NULL)) < 0) { PERROR("sigaction"); return ret; } - if ((ret = sigaction(SIGUSR1, &sa, NULL)) < 0) { + sa.sa_handler = SIG_IGN; + if ((ret = sigaction(SIGPIPE, &sa, NULL)) < 0) { PERROR("sigaction"); return ret; } @@ -5455,81 +5823,80 @@ static void set_ulimit(void) } } -/* - * Write pidfile using the rundir and opt_pidfile. - */ static int write_pidfile(void) { - int ret; - char pidfile_path[PATH_MAX]; + return utils_create_pid_file(getpid(), config.pid_file_path.value); +} - assert(rundir); +static int set_clock_plugin_env(void) +{ + int ret = 0; + char *env_value = NULL; - if (opt_pidfile) { - if (lttng_strncpy(pidfile_path, opt_pidfile, sizeof(pidfile_path))) { - ret = -1; - goto error; - } - } else { - /* Build pidfile path from rundir and opt_pidfile. */ - ret = snprintf(pidfile_path, sizeof(pidfile_path), "%s/" - DEFAULT_LTTNG_SESSIOND_PIDFILE, rundir); - if (ret < 0) { - PERROR("snprintf pidfile path"); - goto error; - } + if (!config.lttng_ust_clock_plugin.value) { + goto end; } - /* - * Create pid file in rundir. - */ - ret = utils_create_pid_file(getpid(), pidfile_path); -error: + ret = asprintf(&env_value, "LTTNG_UST_CLOCK_PLUGIN=%s", + config.lttng_ust_clock_plugin.value); + if (ret < 0) { + PERROR("asprintf"); + goto end; + } + + ret = putenv(env_value); + if (ret) { + free(env_value); + PERROR("putenv of LTTNG_UST_CLOCK_PLUGIN"); + goto end; + } + + DBG("Updated LTTNG_UST_CLOCK_PLUGIN environment variable to \"%s\"", + config.lttng_ust_clock_plugin.value); +end: return ret; } -/* - * Create lockfile using the rundir and return its fd. - */ -static int create_lockfile(void) +static +struct rotation_thread_timer_queue *create_rotate_timer_queue(void) { - int ret; - char lockfile_path[PATH_MAX]; + struct rotation_thread_timer_queue *queue = NULL; - ret = generate_lock_file_path(lockfile_path, sizeof(lockfile_path)); - if (ret < 0) { - goto error; + queue = zmalloc(sizeof(struct rotation_thread_timer_queue)); + if (!queue) { + PERROR("Failed to allocate timer rotate queue"); + goto end; } - ret = utils_create_lock_file(lockfile_path); -error: - return ret; + queue->event_pipe = lttng_pipe_open(FD_CLOEXEC | O_NONBLOCK); + CDS_INIT_LIST_HEAD(&queue->list); + pthread_mutex_init(&queue->lock, NULL); + +end: + return queue; } -/* - * Write agent TCP port using the rundir. - */ -static int write_agent_port(void) +static +void destroy_rotate_timer_queue(struct rotation_thread_timer_queue *queue) { - int ret; - char path[PATH_MAX]; - - assert(rundir); + struct sessiond_rotation_timer *node, *tmp_node; - ret = snprintf(path, sizeof(path), "%s/" - DEFAULT_LTTNG_SESSIOND_AGENTPORT_FILE, rundir); - if (ret < 0) { - PERROR("snprintf agent port path"); - goto error; + if (!queue) { + return; } - /* - * Create TCP agent port file in rundir. - */ - ret = utils_create_pid_file(agent_tcp_port, path); + lttng_pipe_destroy(queue->event_pipe); -error: - return ret; + pthread_mutex_lock(&queue->lock); + /* Empty wait queue. */ + cds_list_for_each_entry_safe(node, tmp_node, &queue->list, head) { + cds_list_del(&node->head); + free(node); + } + pthread_mutex_unlock(&queue->lock); + + pthread_mutex_destroy(&queue->lock); + free(queue); } /* @@ -5539,7 +5906,20 @@ int main(int argc, char **argv) { int ret = 0, retval = 0; void *status; - const char *home_path, *env_app_timeout; + const char *env_app_timeout; + struct lttng_pipe *ust32_channel_monitor_pipe = NULL, + *ust64_channel_monitor_pipe = NULL, + *kernel_channel_monitor_pipe = NULL; + bool notification_thread_launched = false; + bool rotation_thread_launched = false; + bool timer_thread_launched = false; + struct lttng_pipe *ust32_channel_rotate_pipe = NULL, + *ust64_channel_rotate_pipe = NULL, + *kernel_channel_rotate_pipe = NULL; + struct timer_thread_parameters timer_thread_ctx; + /* Queue of rotation jobs populated by the sessiond-timer. */ + struct rotation_thread_timer_queue *rotation_timer_queue = NULL; + sem_t notification_thread_ready; init_kernel_workarounds(); @@ -5550,7 +5930,10 @@ int main(int argc, char **argv) goto exit_set_signal_handler; } - setup_consumerd_path(); + if (sessiond_timer_signal_init()) { + retval = -1; + goto exit_set_signal_handler; + } page_size = sysconf(_SC_PAGESIZE); if (page_size < 0) { @@ -5559,6 +5942,18 @@ int main(int argc, char **argv) WARN("Fallback page size to %ld", page_size); } + ret = sessiond_config_init(&config); + if (ret) { + retval = -1; + goto exit_set_signal_handler; + } + + /* + * Init config from environment variables. + * Command line option override env configuration per-doc. Do env first. + */ + sessiond_config_apply_env_config(&config); + /* * Parse arguments and load the daemon configuration file. * @@ -5573,12 +5968,54 @@ int main(int argc, char **argv) goto exit_options; } + /* + * Resolve all paths received as arguments, configuration option, or + * through environment variable as absolute paths. This is necessary + * since daemonizing causes the sessiond's current working directory + * to '/'. + */ + ret = sessiond_config_resolve_paths(&config); + if (ret) { + goto exit_options; + } + + /* Apply config. */ + lttng_opt_verbose = config.verbose; + lttng_opt_quiet = config.quiet; + kconsumer_data.err_unix_sock_path = + config.kconsumerd_err_unix_sock_path.value; + kconsumer_data.cmd_unix_sock_path = + config.kconsumerd_cmd_unix_sock_path.value; + ustconsumer32_data.err_unix_sock_path = + config.consumerd32_err_unix_sock_path.value; + ustconsumer32_data.cmd_unix_sock_path = + config.consumerd32_cmd_unix_sock_path.value; + ustconsumer64_data.err_unix_sock_path = + config.consumerd64_err_unix_sock_path.value; + ustconsumer64_data.cmd_unix_sock_path = + config.consumerd64_cmd_unix_sock_path.value; + set_clock_plugin_env(); + + sessiond_config_log(&config); + + if (create_lttng_rundir()) { + retval = -1; + goto exit_options; + } + + /* Abort launch if a session daemon is already running. */ + if (check_existing_daemon()) { + ERR("A session daemon is already running."); + retval = -1; + goto exit_options; + } + /* Daemonize */ - if (opt_daemon || opt_background) { + if (config.daemonize || config.background) { int i; ret = lttng_daemonize(&child_ppid, &recv_child_signal, - !opt_background); + !config.background); if (ret < 0) { retval = -1; goto exit_options; @@ -5587,9 +6024,12 @@ int main(int argc, char **argv) /* * We are in the child. Make sure all other file descriptors are * closed, in case we are called with more opened file - * descriptors than the standard ones. + * descriptors than the standard ones and the lock file. */ for (i = 3; i < sysconf(_SC_OPEN_MAX); i++) { + if (i == lockfile_fd) { + continue; + } (void) close(i); } } @@ -5628,211 +6068,99 @@ int main(int argc, char **argv) /* Check if daemon is UID = 0 */ is_root = !getuid(); - if (is_root) { - rundir = strdup(DEFAULT_LTTNG_RUNDIR); - if (!rundir) { - retval = -1; - goto exit_init_data; - } - /* Create global run dir with root access */ - if (create_lttng_rundir(rundir)) { - retval = -1; - goto exit_init_data; - } - - if (strlen(apps_unix_sock_path) == 0) { - ret = snprintf(apps_unix_sock_path, PATH_MAX, - DEFAULT_GLOBAL_APPS_UNIX_SOCK); - if (ret < 0) { - retval = -1; - goto exit_init_data; - } - } - - if (strlen(client_unix_sock_path) == 0) { - ret = snprintf(client_unix_sock_path, PATH_MAX, - DEFAULT_GLOBAL_CLIENT_UNIX_SOCK); - if (ret < 0) { - retval = -1; - goto exit_init_data; - } - } - - /* Set global SHM for ust */ - if (strlen(wait_shm_path) == 0) { - ret = snprintf(wait_shm_path, PATH_MAX, - DEFAULT_GLOBAL_APPS_WAIT_SHM_PATH); - if (ret < 0) { - retval = -1; - goto exit_init_data; - } - } - if (strlen(health_unix_sock_path) == 0) { - ret = snprintf(health_unix_sock_path, - sizeof(health_unix_sock_path), - DEFAULT_GLOBAL_HEALTH_UNIX_SOCK); - if (ret < 0) { - retval = -1; - goto exit_init_data; - } - } - - /* Setup kernel consumerd path */ - ret = snprintf(kconsumer_data.err_unix_sock_path, PATH_MAX, - DEFAULT_KCONSUMERD_ERR_SOCK_PATH, rundir); - if (ret < 0) { - retval = -1; - goto exit_init_data; - } - ret = snprintf(kconsumer_data.cmd_unix_sock_path, PATH_MAX, - DEFAULT_KCONSUMERD_CMD_SOCK_PATH, rundir); - if (ret < 0) { + 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; } - - 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."); + 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; } - - /* - * Create rundir from home path. This will create something like - * $HOME/.lttng - */ - ret = asprintf(&rundir, DEFAULT_LTTNG_HOME_RUNDIR, home_path); - if (ret < 0) { + kernel_channel_rotate_pipe = lttng_pipe_open(0); + if (!kernel_channel_rotate_pipe) { + ERR("Failed to create kernel consumer channel rotate pipe"); retval = -1; goto exit_init_data; } - - if (create_lttng_rundir(rundir)) { + kconsumer_data.channel_rotate_pipe = + lttng_pipe_release_writefd( + kernel_channel_rotate_pipe); + if (kconsumer_data.channel_rotate_pipe < 0) { retval = -1; goto exit_init_data; } - - if (strlen(apps_unix_sock_path) == 0) { - ret = snprintf(apps_unix_sock_path, PATH_MAX, - DEFAULT_HOME_APPS_UNIX_SOCK, - home_path); - if (ret < 0) { - retval = -1; - goto exit_init_data; - } - } - - /* Set the cli tool unix socket path */ - if (strlen(client_unix_sock_path) == 0) { - ret = snprintf(client_unix_sock_path, PATH_MAX, - DEFAULT_HOME_CLIENT_UNIX_SOCK, - home_path); - if (ret < 0) { - retval = -1; - goto exit_init_data; - } - } - - /* Set global SHM for ust */ - if (strlen(wait_shm_path) == 0) { - ret = snprintf(wait_shm_path, PATH_MAX, - DEFAULT_HOME_APPS_WAIT_SHM_PATH, - getuid()); - if (ret < 0) { - retval = -1; - goto exit_init_data; - } - } - - /* Set health check Unix path */ - if (strlen(health_unix_sock_path) == 0) { - ret = snprintf(health_unix_sock_path, - sizeof(health_unix_sock_path), - DEFAULT_HOME_HEALTH_UNIX_SOCK, - home_path); - if (ret < 0) { - retval = -1; - goto exit_init_data; - } - } - } - - lockfile_fd = create_lockfile(); - if (lockfile_fd < 0) { - retval = -1; - goto exit_init_data; } /* Set consumer initial state */ kernel_consumerd_state = CONSUMER_STOPPED; ust_consumerd_state = CONSUMER_STOPPED; - DBG("Client socket path %s", client_unix_sock_path); - DBG("Application socket path %s", apps_unix_sock_path); - DBG("Application wait path %s", wait_shm_path); - DBG("LTTng run directory path: %s", rundir); - - /* 32 bits consumerd path setup */ - ret = snprintf(ustconsumer32_data.err_unix_sock_path, PATH_MAX, - DEFAULT_USTCONSUMERD32_ERR_SOCK_PATH, rundir); - if (ret < 0) { - PERROR("snprintf 32-bit consumer error socket path"); + ust32_channel_monitor_pipe = lttng_pipe_open(0); + if (!ust32_channel_monitor_pipe) { + ERR("Failed to create 32-bit user space consumer channel monitor pipe"); retval = -1; goto exit_init_data; } - ret = snprintf(ustconsumer32_data.cmd_unix_sock_path, PATH_MAX, - DEFAULT_USTCONSUMERD32_CMD_SOCK_PATH, rundir); - if (ret < 0) { - PERROR("snprintf 32-bit consumer command socket path"); + ustconsumer32_data.channel_monitor_pipe = lttng_pipe_release_writefd( + ust32_channel_monitor_pipe); + if (ustconsumer32_data.channel_monitor_pipe < 0) { retval = -1; goto exit_init_data; } - - DBG2("UST consumer 32 bits err path: %s", - ustconsumer32_data.err_unix_sock_path); - DBG2("UST consumer 32 bits cmd path: %s", - ustconsumer32_data.cmd_unix_sock_path); - - /* 64 bits consumerd path setup */ - ret = snprintf(ustconsumer64_data.err_unix_sock_path, PATH_MAX, - DEFAULT_USTCONSUMERD64_ERR_SOCK_PATH, rundir); - if (ret < 0) { - PERROR("snprintf 64-bit consumer error socket path"); + ust32_channel_rotate_pipe = lttng_pipe_open(0); + if (!ust32_channel_rotate_pipe) { + ERR("Failed to create 32-bit user space consumer channel rotate pipe"); retval = -1; goto exit_init_data; } - ret = snprintf(ustconsumer64_data.cmd_unix_sock_path, PATH_MAX, - DEFAULT_USTCONSUMERD64_CMD_SOCK_PATH, rundir); - if (ret < 0) { - PERROR("snprintf 64-bit consumer command socket path"); + ustconsumer32_data.channel_rotate_pipe = lttng_pipe_release_writefd( + ust32_channel_rotate_pipe); + if (ustconsumer32_data.channel_rotate_pipe < 0) { retval = -1; goto exit_init_data; } - DBG2("UST consumer 64 bits err path: %s", - ustconsumer64_data.err_unix_sock_path); - DBG2("UST consumer 64 bits cmd path: %s", - ustconsumer64_data.cmd_unix_sock_path); - /* - * See if daemon already exist. + * The rotation_timer_queue structure is shared between the sessiond timer + * thread and the rotation thread. The main() keeps the ownership and + * destroys it when both threads have quit. */ - if (check_existing_daemon()) { - ERR("Already running daemon.\n"); - /* - * We do not goto exit because we must not cleanup() - * because a daemon is already running. - */ + rotation_timer_queue = create_rotate_timer_queue(); + if (!rotation_timer_queue) { + retval = -1; + goto exit_init_data; + } + timer_thread_ctx.rotation_timer_queue = rotation_timer_queue; + + ust64_channel_monitor_pipe = lttng_pipe_open(0); + if (!ust64_channel_monitor_pipe) { + ERR("Failed to create 64-bit user space consumer channel monitor pipe"); + retval = -1; + goto exit_init_data; + } + ustconsumer64_data.channel_monitor_pipe = lttng_pipe_release_writefd( + ust64_channel_monitor_pipe); + if (ustconsumer64_data.channel_monitor_pipe < 0) { + retval = -1; + goto exit_init_data; + } + ust64_channel_rotate_pipe = lttng_pipe_open(0); + if (!ust64_channel_rotate_pipe) { + ERR("Failed to create 64-bit user space consumer channel rotate pipe"); + retval = -1; + goto exit_init_data; + } + ustconsumer64_data.channel_rotate_pipe = lttng_pipe_release_writefd( + ust64_channel_rotate_pipe); + if (ustconsumer64_data.channel_rotate_pipe < 0) { retval = -1; goto exit_init_data; } @@ -5864,13 +6192,13 @@ int main(int argc, char **argv) * kernel tracer. */ if (is_root) { - if (set_consumer_sockets(&kconsumer_data, rundir)) { + if (set_consumer_sockets(&kconsumer_data)) { retval = -1; goto exit_init_data; } /* Setup kernel tracer */ - if (!opt_no_kernel) { + if (!config.no_kernel) { init_kernel_tracer(); if (kernel_tracer_fd >= 0) { ret = syscall_init_table(); @@ -5888,12 +6216,12 @@ int main(int argc, char **argv) /* init lttng_fd tracking must be done after set_ulimit. */ lttng_fd_init(); - if (set_consumer_sockets(&ustconsumer64_data, rundir)) { + if (set_consumer_sockets(&ustconsumer64_data)) { retval = -1; goto exit_init_data; } - if (set_consumer_sockets(&ustconsumer32_data, rundir)) { + if (set_consumer_sockets(&ustconsumer32_data)) { retval = -1; goto exit_init_data; } @@ -5905,18 +6233,18 @@ int main(int argc, char **argv) } /* Set credentials to socket */ - if (is_root && set_permissions(rundir)) { + if (is_root && set_permissions(config.rundir.value)) { retval = -1; goto exit_init_data; } /* Get parent pid if -S, --sig-parent is specified. */ - if (opt_sig_parent) { + if (config.sig_parent) { ppid = getppid(); } /* Setup the kernel pipe for waking up the kernel thread */ - if (is_root && !opt_no_kernel) { + if (is_root && !config.no_kernel) { if (utils_create_pipe_cloexec(kernel_poll_pipe)) { retval = -1; goto exit_init_data; @@ -5953,9 +6281,9 @@ int main(int argc, char **argv) /* Check for the application socket timeout env variable. */ env_app_timeout = getenv(DEFAULT_APP_SOCKET_TIMEOUT_ENV); if (env_app_timeout) { - app_socket_timeout = atoi(env_app_timeout); + config.app_socket_timeout = atoi(env_app_timeout); } else { - app_socket_timeout = DEFAULT_APP_SOCKET_RW_TIMEOUT; + config.app_socket_timeout = DEFAULT_APP_SOCKET_RW_TIMEOUT; } ret = write_pidfile(); @@ -5964,12 +6292,6 @@ int main(int argc, char **argv) retval = -1; goto exit_init_data; } - ret = write_agent_port(); - if (ret) { - ERR("Error in write_agent_port"); - retval = -1; - goto exit_init_data; - } /* Initialize communication library */ lttcomm_init(); @@ -5980,9 +6302,9 @@ int main(int argc, char **argv) retval = -1; goto exit_init_data; } - load_info->path = opt_load_session_path; + load_info->path = config.load_session_path.value; - /* Create health-check thread */ + /* Create health-check thread. */ ret = pthread_create(&health_thread, default_pthread_attr(), thread_manage_health, (void *) NULL); if (ret) { @@ -5992,6 +6314,78 @@ int main(int argc, char **argv) goto exit_health; } + /* + * The rotation thread needs the notification thread to be ready before + * creating the rotate_notification_channel, so we use this semaphore as + * a rendez-vous point. + */ + sem_init(¬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 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 timer thread. */ + ret = pthread_create(&timer_thread, default_pthread_attr(), + sessiond_timer_thread, &timer_thread_ctx); + if (ret) { + errno = ret; + PERROR("pthread_create timer"); + retval = -1; + stop_threads(); + goto exit_notification; + } + timer_thread_launched = true; + + /* rotation_thread_data acquires the pipes' read side. */ + rotation_thread_handle = rotation_thread_handle_create( + ust32_channel_rotate_pipe, + ust64_channel_rotate_pipe, + kernel_channel_rotate_pipe, + thread_quit_pipe[0], + rotation_timer_queue, + notification_thread_handle, + ¬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, default_pthread_attr(), thread_manage_clients, (void *) NULL); @@ -5999,6 +6393,7 @@ int main(int argc, char **argv) errno = ret; PERROR("pthread_create clients"); retval = -1; + stop_threads(); goto exit_client; } @@ -6009,6 +6404,7 @@ int main(int argc, char **argv) errno = ret; PERROR("pthread_create dispatch"); retval = -1; + stop_threads(); goto exit_dispatch; } @@ -6019,6 +6415,7 @@ int main(int argc, char **argv) errno = ret; PERROR("pthread_create registration"); retval = -1; + stop_threads(); goto exit_reg_apps; } @@ -6029,6 +6426,7 @@ int main(int argc, char **argv) errno = ret; PERROR("pthread_create apps"); retval = -1; + stop_threads(); goto exit_apps; } @@ -6039,6 +6437,7 @@ int main(int argc, char **argv) errno = ret; PERROR("pthread_create notify"); retval = -1; + stop_threads(); goto exit_apps_notify; } @@ -6049,11 +6448,12 @@ int main(int argc, char **argv) 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, default_pthread_attr(), thread_manage_kernel, (void *) NULL); @@ -6061,6 +6461,7 @@ int main(int argc, char **argv) errno = ret; PERROR("pthread_create kernel"); retval = -1; + stop_threads(); goto exit_kernel; } } @@ -6072,6 +6473,7 @@ int main(int argc, char **argv) errno = ret; PERROR("pthread_create load_session_thread"); retval = -1; + stop_threads(); goto exit_load_session; } @@ -6088,7 +6490,7 @@ int main(int argc, char **argv) } exit_load_session: - if (is_root && !opt_no_kernel) { + if (is_root && !config.no_kernel) { ret = pthread_join(kernel_thread, &status); if (ret) { errno = ret; @@ -6148,25 +6550,32 @@ exit_dispatch: PERROR("pthread_join"); retval = -1; } -exit_client: +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; } -exit_health: +exit_health: exit_init_data: + /* + * Wait for all pending call_rcu work to complete before tearing + * down data structures. call_rcu worker may be trying to + * perform lookups in those structures. + */ + rcu_barrier(); /* * sessiond_cleanup() is called when no other thread is running, except * the ht_cleanup thread, which is needed to destroy the hash tables. */ rcu_thread_online(); sessiond_cleanup(); - rcu_thread_offline(); - rcu_unregister_thread(); /* * Ensure all prior call_rcu are done. call_rcu callbacks may push @@ -6175,10 +6584,66 @@ exit_init_data: */ 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); + } + + 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); + } + + if (timer_thread_launched) { + kill(getpid(), LTTNG_SESSIOND_SIG_EXIT); + ret = pthread_join(timer_thread, &status); + if (ret) { + errno = ret; + PERROR("pthread_join timer thread"); + retval = -1; + } + } + + /* + * After the rotation and timer thread have quit, we can safely destroy + * the rotation_timer_queue. + */ + destroy_rotate_timer_queue(rotation_timer_queue); + + rcu_thread_offline(); + rcu_unregister_thread(); + ret = fini_ht_cleanup_thread(&ht_cleanup_thread); if (ret) { retval = -1; } + lttng_pipe_destroy(ust32_channel_monitor_pipe); + lttng_pipe_destroy(ust64_channel_monitor_pipe); + lttng_pipe_destroy(kernel_channel_monitor_pipe); + lttng_pipe_destroy(ust32_channel_rotate_pipe); + lttng_pipe_destroy(ust64_channel_rotate_pipe); + lttng_pipe_destroy(kernel_channel_rotate_pipe); exit_ht_cleanup: health_app_destroy(health_sessiond); @@ -6186,10 +6651,10 @@ 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 {