X-Git-Url: https://git.lttng.org/?p=lttng-tools.git;a=blobdiff_plain;f=src%2Fbin%2Flttng-sessiond%2Fcmd.c;fp=src%2Fbin%2Flttng-sessiond%2Fcmd.c;h=0000000000000000000000000000000000000000;hp=2c0fa462d38fe63a728679b0dc3072ae6e88502a;hb=7966af5763c4aaca39df9bbfa9277ff15715c720;hpb=3a5f70173aa04d11ccb22694d5d31a702cad33ab diff --git a/src/bin/lttng-sessiond/cmd.c b/src/bin/lttng-sessiond/cmd.c deleted file mode 100644 index 2c0fa462d..000000000 --- a/src/bin/lttng-sessiond/cmd.c +++ /dev/null @@ -1,5893 +0,0 @@ -/* - * Copyright (C) 2012 David Goulet - * Copyright (C) 2016 Jérémie Galarneau - * - * SPDX-License-Identifier: GPL-2.0-only - * - */ - - -#define _LGPL_SOURCE -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "agent-thread.h" -#include "agent.h" -#include "buffer-registry.h" -#include "channel.h" -#include "cmd.h" -#include "consumer.h" -#include "event-notifier-error-accounting.h" -#include "event.h" -#include "health-sessiond.h" -#include "kernel-consumer.h" -#include "kernel.h" -#include "lttng-sessiond.h" -#include "lttng-syscall.h" -#include "notification-thread-commands.h" -#include "notification-thread.h" -#include "rotate.h" -#include "rotation-thread.h" -#include "session.h" -#include "timer.h" -#include "tracker.h" -#include "utils.h" - -/* Sleep for 100ms between each check for the shm path's deletion. */ -#define SESSION_DESTROY_SHM_PATH_CHECK_DELAY_US 100000 - -struct cmd_destroy_session_reply_context { - int reply_sock_fd; - bool implicit_rotation_on_destroy; - /* - * Indicates whether or not an error occurred while launching the - * destruction of a session. - */ - enum lttng_error_code destruction_status; -}; - -static enum lttng_error_code wait_on_path(void *path); - -/* - * Command completion handler that is used by the destroy command - * when a session that has a non-default shm_path is being destroyed. - * - * See comment in cmd_destroy_session() for the rationale. - */ -static struct destroy_completion_handler { - struct cmd_completion_handler handler; - char shm_path[member_sizeof(struct ltt_session, shm_path)]; -} destroy_completion_handler = { - .handler = { - .run = wait_on_path, - .data = destroy_completion_handler.shm_path - }, - .shm_path = { 0 }, -}; - -static struct cmd_completion_handler *current_completion_handler; - -/* - * Used to keep a unique index for each relayd socket created where this value - * is associated with streams on the consumer so it can match the right relayd - * to send to. It must be accessed with the relayd_net_seq_idx_lock - * held. - */ -static pthread_mutex_t relayd_net_seq_idx_lock = PTHREAD_MUTEX_INITIALIZER; -static uint64_t relayd_net_seq_idx; - -static int validate_ust_event_name(const char *); -static int cmd_enable_event_internal(struct ltt_session *session, - const struct lttng_domain *domain, - char *channel_name, struct lttng_event *event, - char *filter_expression, - struct lttng_bytecode *filter, - struct lttng_event_exclusion *exclusion, - int wpipe); - -/* - * Create a session path used by list_lttng_sessions for the case that the - * session consumer is on the network. - */ -static int build_network_session_path(char *dst, size_t size, - struct ltt_session *session) -{ - int ret, kdata_port, udata_port; - struct lttng_uri *kuri = NULL, *uuri = NULL, *uri = NULL; - char tmp_uurl[PATH_MAX], tmp_urls[PATH_MAX]; - - LTTNG_ASSERT(session); - LTTNG_ASSERT(dst); - - memset(tmp_urls, 0, sizeof(tmp_urls)); - memset(tmp_uurl, 0, sizeof(tmp_uurl)); - - kdata_port = udata_port = DEFAULT_NETWORK_DATA_PORT; - - if (session->kernel_session && session->kernel_session->consumer) { - kuri = &session->kernel_session->consumer->dst.net.control; - kdata_port = session->kernel_session->consumer->dst.net.data.port; - } - - if (session->ust_session && session->ust_session->consumer) { - uuri = &session->ust_session->consumer->dst.net.control; - udata_port = session->ust_session->consumer->dst.net.data.port; - } - - if (uuri == NULL && kuri == NULL) { - uri = &session->consumer->dst.net.control; - kdata_port = session->consumer->dst.net.data.port; - } else if (kuri && uuri) { - ret = uri_compare(kuri, uuri); - if (ret) { - /* Not Equal */ - uri = kuri; - /* Build uuri URL string */ - ret = uri_to_str_url(uuri, tmp_uurl, sizeof(tmp_uurl)); - if (ret < 0) { - goto error; - } - } else { - uri = kuri; - } - } else if (kuri && uuri == NULL) { - uri = kuri; - } else if (uuri && kuri == NULL) { - uri = uuri; - } - - ret = uri_to_str_url(uri, tmp_urls, sizeof(tmp_urls)); - if (ret < 0) { - goto error; - } - - /* - * Do we have a UST url set. If yes, this means we have both kernel and UST - * to print. - */ - if (*tmp_uurl != '\0') { - ret = snprintf(dst, size, "[K]: %s [data: %d] -- [U]: %s [data: %d]", - tmp_urls, kdata_port, tmp_uurl, udata_port); - } else { - int dport; - if (kuri || (!kuri && !uuri)) { - dport = kdata_port; - } else { - /* No kernel URI, use the UST port. */ - dport = udata_port; - } - ret = snprintf(dst, size, "%s [data: %d]", tmp_urls, dport); - } - -error: - return ret; -} - -/* - * Get run-time attributes if the session has been started (discarded events, - * lost packets). - */ -static int get_kernel_runtime_stats(struct ltt_session *session, - struct ltt_kernel_channel *kchan, uint64_t *discarded_events, - uint64_t *lost_packets) -{ - int ret; - - if (!session->has_been_started) { - ret = 0; - *discarded_events = 0; - *lost_packets = 0; - goto end; - } - - ret = consumer_get_discarded_events(session->id, kchan->key, - session->kernel_session->consumer, - discarded_events); - if (ret < 0) { - goto end; - } - - ret = consumer_get_lost_packets(session->id, kchan->key, - session->kernel_session->consumer, - lost_packets); - if (ret < 0) { - goto end; - } - -end: - return ret; -} - -/* - * Get run-time attributes if the session has been started (discarded events, - * lost packets). - */ -static int get_ust_runtime_stats(struct ltt_session *session, - struct ltt_ust_channel *uchan, uint64_t *discarded_events, - uint64_t *lost_packets) -{ - int ret; - struct ltt_ust_session *usess; - - if (!discarded_events || !lost_packets) { - ret = -1; - goto end; - } - - usess = session->ust_session; - LTTNG_ASSERT(discarded_events); - LTTNG_ASSERT(lost_packets); - - if (!usess || !session->has_been_started) { - *discarded_events = 0; - *lost_packets = 0; - ret = 0; - goto end; - } - - if (usess->buffer_type == LTTNG_BUFFER_PER_UID) { - ret = ust_app_uid_get_channel_runtime_stats(usess->id, - &usess->buffer_reg_uid_list, - usess->consumer, uchan->id, - uchan->attr.overwrite, - discarded_events, - lost_packets); - } else if (usess->buffer_type == LTTNG_BUFFER_PER_PID) { - ret = ust_app_pid_get_channel_runtime_stats(usess, - uchan, usess->consumer, - uchan->attr.overwrite, - discarded_events, - lost_packets); - if (ret < 0) { - goto end; - } - *discarded_events += uchan->per_pid_closed_app_discarded; - *lost_packets += uchan->per_pid_closed_app_lost; - } else { - ERR("Unsupported buffer type"); - abort(); - ret = -1; - goto end; - } - -end: - return ret; -} - -/* - * Fill lttng_channel array of all channels. - */ -static ssize_t list_lttng_channels(enum lttng_domain_type domain, - struct ltt_session *session, struct lttng_channel *channels, - struct lttng_channel_extended *chan_exts) -{ - int i = 0, ret = 0; - struct ltt_kernel_channel *kchan; - - DBG("Listing channels for session %s", session->name); - - switch (domain) { - case LTTNG_DOMAIN_KERNEL: - /* Kernel channels */ - if (session->kernel_session != NULL) { - cds_list_for_each_entry(kchan, - &session->kernel_session->channel_list.head, list) { - uint64_t discarded_events, lost_packets; - struct lttng_channel_extended *extended; - - extended = (struct lttng_channel_extended *) - kchan->channel->attr.extended.ptr; - - ret = get_kernel_runtime_stats(session, kchan, - &discarded_events, &lost_packets); - if (ret < 0) { - goto end; - } - /* Copy lttng_channel struct to array */ - memcpy(&channels[i], kchan->channel, sizeof(struct lttng_channel)); - channels[i].enabled = kchan->enabled; - chan_exts[i].discarded_events = - discarded_events; - chan_exts[i].lost_packets = lost_packets; - chan_exts[i].monitor_timer_interval = - extended->monitor_timer_interval; - chan_exts[i].blocking_timeout = 0; - i++; - } - } - break; - case LTTNG_DOMAIN_UST: - { - struct lttng_ht_iter iter; - struct ltt_ust_channel *uchan; - - rcu_read_lock(); - cds_lfht_for_each_entry(session->ust_session->domain_global.channels->ht, - &iter.iter, uchan, node.node) { - uint64_t discarded_events = 0, lost_packets = 0; - - if (lttng_strncpy(channels[i].name, uchan->name, - LTTNG_SYMBOL_NAME_LEN)) { - break; - } - channels[i].attr.overwrite = uchan->attr.overwrite; - channels[i].attr.subbuf_size = uchan->attr.subbuf_size; - channels[i].attr.num_subbuf = uchan->attr.num_subbuf; - channels[i].attr.switch_timer_interval = - uchan->attr.switch_timer_interval; - channels[i].attr.read_timer_interval = - uchan->attr.read_timer_interval; - channels[i].enabled = uchan->enabled; - channels[i].attr.tracefile_size = uchan->tracefile_size; - channels[i].attr.tracefile_count = uchan->tracefile_count; - - /* - * Map enum lttng_ust_output to enum lttng_event_output. - */ - switch (uchan->attr.output) { - case LTTNG_UST_ABI_MMAP: - channels[i].attr.output = LTTNG_EVENT_MMAP; - break; - default: - /* - * LTTNG_UST_MMAP is the only supported UST - * output mode. - */ - abort(); - break; - } - - chan_exts[i].monitor_timer_interval = - uchan->monitor_timer_interval; - chan_exts[i].blocking_timeout = - uchan->attr.u.s.blocking_timeout; - - ret = get_ust_runtime_stats(session, uchan, - &discarded_events, &lost_packets); - if (ret < 0) { - break; - } - chan_exts[i].discarded_events = discarded_events; - chan_exts[i].lost_packets = lost_packets; - i++; - } - rcu_read_unlock(); - break; - } - default: - break; - } - -end: - if (ret < 0) { - return -LTTNG_ERR_FATAL; - } else { - return LTTNG_OK; - } -} - -static int append_extended_info(const char *filter_expression, - struct lttng_event_exclusion *exclusion, - struct lttng_userspace_probe_location *probe_location, - struct lttng_payload *payload) -{ - int ret = 0; - size_t filter_len = 0; - size_t nb_exclusions = 0; - size_t userspace_probe_location_len = 0; - struct lttcomm_event_extended_header extended_header = {}; - struct lttcomm_event_extended_header *p_extended_header; - const size_t original_payload_size = payload->buffer.size; - - ret = lttng_dynamic_buffer_append(&payload->buffer, &extended_header, - sizeof(extended_header)); - if (ret) { - goto end; - } - - if (filter_expression) { - filter_len = strlen(filter_expression) + 1; - ret = lttng_dynamic_buffer_append(&payload->buffer, - filter_expression, filter_len); - if (ret) { - goto end; - } - } - - if (exclusion) { - const size_t len = exclusion->count * LTTNG_SYMBOL_NAME_LEN; - - nb_exclusions = exclusion->count; - - ret = lttng_dynamic_buffer_append( - &payload->buffer, &exclusion->names, len); - if (ret) { - goto end; - } - } - - if (probe_location) { - const size_t size_before_probe = payload->buffer.size; - - ret = lttng_userspace_probe_location_serialize(probe_location, - payload); - if (ret < 0) { - ret = -1; - goto end; - } - - userspace_probe_location_len = - payload->buffer.size - size_before_probe; - } - - /* Set header fields */ - p_extended_header = (struct lttcomm_event_extended_header *) - (payload->buffer.data + original_payload_size); - - p_extended_header->filter_len = filter_len; - p_extended_header->nb_exclusions = nb_exclusions; - p_extended_header->userspace_probe_location_len = - userspace_probe_location_len; - - ret = 0; -end: - return ret; -} - -/* - * Create a list of agent domain events. - * - * Return number of events in list on success or else a negative value. - */ -static int list_lttng_agent_events(struct agent *agt, - struct lttng_payload *payload) -{ - int nb_events = 0, ret = 0; - const struct agent_event *agent_event; - struct lttng_ht_iter iter; - - LTTNG_ASSERT(agt); - - DBG3("Listing agent events"); - - rcu_read_lock(); - cds_lfht_for_each_entry ( - agt->events->ht, &iter.iter, agent_event, node.node) { - struct lttng_event event = { - .enabled = AGENT_EVENT_IS_ENABLED(agent_event), - .loglevel = agent_event->loglevel_value, - .loglevel_type = agent_event->loglevel_type, - }; - - ret = lttng_strncpy(event.name, agent_event->name, sizeof(event.name)); - if (ret) { - /* Internal error, invalid name. */ - ERR("Invalid event name while listing agent events: '%s' exceeds the maximal allowed length of %zu bytes", - agent_event->name, sizeof(event.name)); - ret = -LTTNG_ERR_UNK; - goto end; - } - - ret = lttng_dynamic_buffer_append( - &payload->buffer, &event, sizeof(event)); - if (ret) { - ERR("Failed to append event to payload"); - ret = -LTTNG_ERR_NOMEM; - goto end; - } - - nb_events++; - } - - cds_lfht_for_each_entry ( - agt->events->ht, &iter.iter, agent_event, node.node) { - /* Append extended info. */ - ret = append_extended_info(agent_event->filter_expression, NULL, - NULL, payload); - if (ret) { - ERR("Failed to append extended event info to payload"); - ret = -LTTNG_ERR_NOMEM; - goto end; - } - } - - ret = nb_events; -end: - rcu_read_unlock(); - return ret; -} - -/* - * Create a list of ust global domain events. - */ -static int list_lttng_ust_global_events(char *channel_name, - struct ltt_ust_domain_global *ust_global, - struct lttng_payload *payload) -{ - int ret = 0; - unsigned int nb_events = 0; - struct lttng_ht_iter iter; - const struct lttng_ht_node_str *node; - const struct ltt_ust_channel *uchan; - const struct ltt_ust_event *uevent; - - DBG("Listing UST global events for channel %s", channel_name); - - rcu_read_lock(); - - lttng_ht_lookup(ust_global->channels, (void *) channel_name, &iter); - node = lttng_ht_iter_get_node_str(&iter); - if (node == NULL) { - ret = LTTNG_ERR_UST_CHAN_NOT_FOUND; - goto end; - } - - uchan = caa_container_of(&node->node, struct ltt_ust_channel, node.node); - - DBG3("Listing UST global events"); - - cds_lfht_for_each_entry(uchan->events->ht, &iter.iter, uevent, node.node) { - struct lttng_event event = {}; - - if (uevent->internal) { - continue; - } - - ret = lttng_strncpy(event.name, uevent->attr.name, sizeof(event.name)); - if (ret) { - /* Internal error, invalid name. */ - ERR("Invalid event name while listing user space tracer events: '%s' exceeds the maximal allowed length of %zu bytes", - uevent->attr.name, sizeof(event.name)); - ret = -LTTNG_ERR_UNK; - goto end; - } - - event.enabled = uevent->enabled; - - switch (uevent->attr.instrumentation) { - case LTTNG_UST_ABI_TRACEPOINT: - event.type = LTTNG_EVENT_TRACEPOINT; - break; - case LTTNG_UST_ABI_PROBE: - event.type = LTTNG_EVENT_PROBE; - break; - case LTTNG_UST_ABI_FUNCTION: - event.type = LTTNG_EVENT_FUNCTION; - break; - } - - event.loglevel = uevent->attr.loglevel; - switch (uevent->attr.loglevel_type) { - case LTTNG_UST_ABI_LOGLEVEL_ALL: - event.loglevel_type = LTTNG_EVENT_LOGLEVEL_ALL; - break; - case LTTNG_UST_ABI_LOGLEVEL_RANGE: - event.loglevel_type = LTTNG_EVENT_LOGLEVEL_RANGE; - break; - case LTTNG_UST_ABI_LOGLEVEL_SINGLE: - event.loglevel_type = LTTNG_EVENT_LOGLEVEL_SINGLE; - break; - } - - if (uevent->filter) { - event.filter = 1; - } - - if (uevent->exclusion) { - event.exclusion = 1; - } - - ret = lttng_dynamic_buffer_append(&payload->buffer, &event, sizeof(event)); - if (ret) { - ERR("Failed to append event to payload"); - ret = -LTTNG_ERR_NOMEM; - goto end; - } - - nb_events++; - } - - cds_lfht_for_each_entry(uchan->events->ht, &iter.iter, uevent, node.node) { - /* Append extended info. */ - ret = append_extended_info(uevent->filter_expression, - uevent->exclusion, NULL, payload); - if (ret) { - ERR("Failed to append extended event info to payload"); - ret = -LTTNG_ERR_FATAL; - goto end; - } - } - - ret = nb_events; -end: - rcu_read_unlock(); - return ret; -} - -/* - * Fill lttng_event array of all kernel events in the channel. - */ -static int list_lttng_kernel_events(char *channel_name, - struct ltt_kernel_session *kernel_session, - struct lttng_payload *payload) -{ - int ret; - unsigned int nb_event; - const struct ltt_kernel_event *kevent; - const struct ltt_kernel_channel *kchan; - - kchan = trace_kernel_get_channel_by_name(channel_name, kernel_session); - if (kchan == NULL) { - ret = LTTNG_ERR_KERN_CHAN_NOT_FOUND; - goto error; - } - - nb_event = kchan->event_count; - - DBG("Listing events for channel %s", kchan->channel->name); - - /* Kernel channels */ - cds_list_for_each_entry(kevent, &kchan->events_list.head , list) { - struct lttng_event event = {}; - - ret = lttng_strncpy(event.name, kevent->event->name, sizeof(event.name)); - if (ret) { - /* Internal error, invalid name. */ - ERR("Invalid event name while listing kernel events: '%s' exceeds the maximal allowed length of %zu bytes", - kevent->event->name, - sizeof(event.name)); - ret = -LTTNG_ERR_UNK; - goto end; - } - - event.enabled = kevent->enabled; - event.filter = (unsigned char) !!kevent->filter_expression; - - switch (kevent->event->instrumentation) { - case LTTNG_KERNEL_ABI_TRACEPOINT: - event.type = LTTNG_EVENT_TRACEPOINT; - break; - case LTTNG_KERNEL_ABI_KRETPROBE: - event.type = LTTNG_EVENT_FUNCTION; - memcpy(&event.attr.probe, &kevent->event->u.kprobe, - sizeof(struct lttng_kernel_abi_kprobe)); - break; - case LTTNG_KERNEL_ABI_KPROBE: - event.type = LTTNG_EVENT_PROBE; - memcpy(&event.attr.probe, &kevent->event->u.kprobe, - sizeof(struct lttng_kernel_abi_kprobe)); - break; - case LTTNG_KERNEL_ABI_UPROBE: - event.type = LTTNG_EVENT_USERSPACE_PROBE; - break; - case LTTNG_KERNEL_ABI_FUNCTION: - event.type = LTTNG_EVENT_FUNCTION; - memcpy(&event.attr.ftrace, &kevent->event->u.ftrace, - sizeof(struct lttng_kernel_abi_function)); - break; - case LTTNG_KERNEL_ABI_NOOP: - event.type = LTTNG_EVENT_NOOP; - break; - case LTTNG_KERNEL_ABI_SYSCALL: - event.type = LTTNG_EVENT_SYSCALL; - break; - case LTTNG_KERNEL_ABI_ALL: - /* fall-through. */ - default: - abort(); - break; - } - - ret = lttng_dynamic_buffer_append( - &payload->buffer, &event, sizeof(event)); - if (ret) { - ERR("Failed to append event to payload"); - ret = -LTTNG_ERR_NOMEM; - goto end; - } - } - - cds_list_for_each_entry(kevent, &kchan->events_list.head , list) { - /* Append extended info. */ - ret = append_extended_info(kevent->filter_expression, NULL, - kevent->userspace_probe_location, payload); - if (ret) { - DBG("Error appending extended info message"); - ret = -LTTNG_ERR_FATAL; - goto error; - } - } - -end: - return nb_event; -error: - return ret; -} - -/* - * Add URI so the consumer output object. Set the correct path depending on the - * domain adding the default trace directory. - */ -static enum lttng_error_code add_uri_to_consumer( - const struct ltt_session *session, - struct consumer_output *consumer, - struct lttng_uri *uri, enum lttng_domain_type domain) -{ - int ret; - enum lttng_error_code ret_code = LTTNG_OK; - - LTTNG_ASSERT(uri); - - if (consumer == NULL) { - DBG("No consumer detected. Don't add URI. Stopping."); - ret_code = LTTNG_ERR_NO_CONSUMER; - goto error; - } - - switch (domain) { - case LTTNG_DOMAIN_KERNEL: - ret = lttng_strncpy(consumer->domain_subdir, - DEFAULT_KERNEL_TRACE_DIR, - sizeof(consumer->domain_subdir)); - break; - case LTTNG_DOMAIN_UST: - ret = lttng_strncpy(consumer->domain_subdir, - DEFAULT_UST_TRACE_DIR, - sizeof(consumer->domain_subdir)); - break; - default: - /* - * This case is possible is we try to add the URI to the global - * tracing session consumer object which in this case there is - * no subdir. - */ - memset(consumer->domain_subdir, 0, - sizeof(consumer->domain_subdir)); - ret = 0; - } - if (ret) { - ERR("Failed to initialize consumer output domain subdirectory"); - ret_code = LTTNG_ERR_FATAL; - goto error; - } - - switch (uri->dtype) { - case LTTNG_DST_IPV4: - case LTTNG_DST_IPV6: - DBG2("Setting network URI to consumer"); - - if (consumer->type == CONSUMER_DST_NET) { - if ((uri->stype == LTTNG_STREAM_CONTROL && - consumer->dst.net.control_isset) || - (uri->stype == LTTNG_STREAM_DATA && - consumer->dst.net.data_isset)) { - ret_code = LTTNG_ERR_URL_EXIST; - goto error; - } - } else { - memset(&consumer->dst, 0, sizeof(consumer->dst)); - } - - /* Set URI into consumer output object */ - ret = consumer_set_network_uri(session, consumer, uri); - if (ret < 0) { - ret_code = -ret; - goto error; - } else if (ret == 1) { - /* - * URI was the same in the consumer so we do not append the subdir - * again so to not duplicate output dir. - */ - ret_code = LTTNG_OK; - goto error; - } - break; - case LTTNG_DST_PATH: - if (*uri->dst.path != '/' || strstr(uri->dst.path, "../")) { - ret_code = LTTNG_ERR_INVALID; - goto error; - } - DBG2("Setting trace directory path from URI to %s", - uri->dst.path); - memset(&consumer->dst, 0, sizeof(consumer->dst)); - - ret = lttng_strncpy(consumer->dst.session_root_path, - uri->dst.path, - sizeof(consumer->dst.session_root_path)); - if (ret) { - ret_code = LTTNG_ERR_FATAL; - goto error; - } - consumer->type = CONSUMER_DST_LOCAL; - break; - } - - ret_code = LTTNG_OK; -error: - return ret_code; -} - -/* - * Init tracing by creating trace directory and sending fds kernel consumer. - */ -static int init_kernel_tracing(struct ltt_kernel_session *session) -{ - int ret = 0; - struct lttng_ht_iter iter; - struct consumer_socket *socket; - - LTTNG_ASSERT(session); - - rcu_read_lock(); - - if (session->consumer_fds_sent == 0 && session->consumer != NULL) { - cds_lfht_for_each_entry(session->consumer->socks->ht, &iter.iter, - socket, node.node) { - pthread_mutex_lock(socket->lock); - ret = kernel_consumer_send_session(socket, session); - pthread_mutex_unlock(socket->lock); - if (ret < 0) { - ret = LTTNG_ERR_KERN_CONSUMER_FAIL; - goto error; - } - } - } - -error: - rcu_read_unlock(); - return ret; -} - -/* - * Create a socket to the relayd using the URI. - * - * On success, the relayd_sock pointer is set to the created socket. - * Else, it remains untouched and an LTTng error code is returned. - */ -static enum lttng_error_code create_connect_relayd(struct lttng_uri *uri, - struct lttcomm_relayd_sock **relayd_sock, - struct consumer_output *consumer) -{ - int ret; - enum lttng_error_code status = LTTNG_OK; - struct lttcomm_relayd_sock *rsock; - - rsock = lttcomm_alloc_relayd_sock(uri, RELAYD_VERSION_COMM_MAJOR, - RELAYD_VERSION_COMM_MINOR); - if (!rsock) { - status = LTTNG_ERR_FATAL; - goto error; - } - - /* - * Connect to relayd so we can proceed with a session creation. This call - * can possibly block for an arbitrary amount of time to set the health - * state to be in poll execution. - */ - health_poll_entry(); - ret = relayd_connect(rsock); - health_poll_exit(); - if (ret < 0) { - ERR("Unable to reach lttng-relayd"); - status = LTTNG_ERR_RELAYD_CONNECT_FAIL; - goto free_sock; - } - - /* Create socket for control stream. */ - if (uri->stype == LTTNG_STREAM_CONTROL) { - uint64_t result_flags; - - DBG3("Creating relayd stream socket from URI"); - - /* Check relayd version */ - ret = relayd_version_check(rsock); - if (ret == LTTNG_ERR_RELAYD_VERSION_FAIL) { - status = LTTNG_ERR_RELAYD_VERSION_FAIL; - goto close_sock; - } else if (ret < 0) { - ERR("Unable to reach lttng-relayd"); - status = LTTNG_ERR_RELAYD_CONNECT_FAIL; - goto close_sock; - } - consumer->relay_major_version = rsock->major; - consumer->relay_minor_version = rsock->minor; - ret = relayd_get_configuration(rsock, 0, - &result_flags); - if (ret < 0) { - ERR("Unable to get relayd configuration"); - status = LTTNG_ERR_RELAYD_CONNECT_FAIL; - goto close_sock; - } - if (result_flags & LTTCOMM_RELAYD_CONFIGURATION_FLAG_CLEAR_ALLOWED) { - consumer->relay_allows_clear = true; - } - } else if (uri->stype == LTTNG_STREAM_DATA) { - DBG3("Creating relayd data socket from URI"); - } else { - /* Command is not valid */ - ERR("Relayd invalid stream type: %d", uri->stype); - status = LTTNG_ERR_INVALID; - goto close_sock; - } - - *relayd_sock = rsock; - - return status; - -close_sock: - /* The returned value is not useful since we are on an error path. */ - (void) relayd_close(rsock); -free_sock: - free(rsock); -error: - return status; -} - -/* - * Connect to the relayd using URI and send the socket to the right consumer. - * - * The consumer socket lock must be held by the caller. - * - * Returns LTTNG_OK on success or an LTTng error code on failure. - */ -static enum lttng_error_code send_consumer_relayd_socket( - unsigned int session_id, - struct lttng_uri *relayd_uri, - struct consumer_output *consumer, - struct consumer_socket *consumer_sock, - const char *session_name, const char *hostname, - const char *base_path, int session_live_timer, - const uint64_t *current_chunk_id, - time_t session_creation_time, - bool session_name_contains_creation_time) -{ - int ret; - struct lttcomm_relayd_sock *rsock = NULL; - enum lttng_error_code status; - - /* Connect to relayd and make version check if uri is the control. */ - status = create_connect_relayd(relayd_uri, &rsock, consumer); - if (status != LTTNG_OK) { - goto relayd_comm_error; - } - LTTNG_ASSERT(rsock); - - /* Set the network sequence index if not set. */ - if (consumer->net_seq_index == (uint64_t) -1ULL) { - pthread_mutex_lock(&relayd_net_seq_idx_lock); - /* - * Increment net_seq_idx because we are about to transfer the - * new relayd socket to the consumer. - * Assign unique key so the consumer can match streams. - */ - consumer->net_seq_index = ++relayd_net_seq_idx; - pthread_mutex_unlock(&relayd_net_seq_idx_lock); - } - - /* Send relayd socket to consumer. */ - ret = consumer_send_relayd_socket(consumer_sock, rsock, consumer, - relayd_uri->stype, session_id, - session_name, hostname, base_path, - session_live_timer, current_chunk_id, - session_creation_time, session_name_contains_creation_time); - if (ret < 0) { - status = LTTNG_ERR_ENABLE_CONSUMER_FAIL; - goto close_sock; - } - - /* Flag that the corresponding socket was sent. */ - if (relayd_uri->stype == LTTNG_STREAM_CONTROL) { - consumer_sock->control_sock_sent = 1; - } else if (relayd_uri->stype == LTTNG_STREAM_DATA) { - consumer_sock->data_sock_sent = 1; - } - - /* - * Close socket which was dup on the consumer side. The session daemon does - * NOT keep track of the relayd socket(s) once transfer to the consumer. - */ - -close_sock: - if (status != LTTNG_OK) { - /* - * The consumer output for this session should not be used anymore - * since the relayd connection failed thus making any tracing or/and - * streaming not usable. - */ - consumer->enabled = 0; - } - (void) relayd_close(rsock); - free(rsock); - -relayd_comm_error: - return status; -} - -/* - * Send both relayd sockets to a specific consumer and domain. This is a - * helper function to facilitate sending the information to the consumer for a - * session. - * - * The consumer socket lock must be held by the caller. - * - * Returns LTTNG_OK, or an LTTng error code on failure. - */ -static enum lttng_error_code send_consumer_relayd_sockets( - enum lttng_domain_type domain, - unsigned int session_id, struct consumer_output *consumer, - struct consumer_socket *sock, const char *session_name, - const char *hostname, const char *base_path, int session_live_timer, - const uint64_t *current_chunk_id, time_t session_creation_time, - bool session_name_contains_creation_time) -{ - enum lttng_error_code status = LTTNG_OK; - - LTTNG_ASSERT(consumer); - LTTNG_ASSERT(sock); - - /* Sending control relayd socket. */ - if (!sock->control_sock_sent) { - status = send_consumer_relayd_socket(session_id, - &consumer->dst.net.control, consumer, sock, - session_name, hostname, base_path, session_live_timer, - current_chunk_id, session_creation_time, - session_name_contains_creation_time); - if (status != LTTNG_OK) { - goto error; - } - } - - /* Sending data relayd socket. */ - if (!sock->data_sock_sent) { - status = send_consumer_relayd_socket(session_id, - &consumer->dst.net.data, consumer, sock, - session_name, hostname, base_path, session_live_timer, - current_chunk_id, session_creation_time, - session_name_contains_creation_time); - if (status != LTTNG_OK) { - goto error; - } - } - -error: - return status; -} - -/* - * Setup relayd connections for a tracing session. First creates the socket to - * the relayd and send them to the right domain consumer. Consumer type MUST be - * network. - */ -int cmd_setup_relayd(struct ltt_session *session) -{ - int ret = LTTNG_OK; - struct ltt_ust_session *usess; - struct ltt_kernel_session *ksess; - struct consumer_socket *socket; - struct lttng_ht_iter iter; - LTTNG_OPTIONAL(uint64_t) current_chunk_id = {}; - - LTTNG_ASSERT(session); - - usess = session->ust_session; - ksess = session->kernel_session; - - DBG("Setting relayd for session %s", session->name); - - rcu_read_lock(); - if (session->current_trace_chunk) { - enum lttng_trace_chunk_status status = lttng_trace_chunk_get_id( - session->current_trace_chunk, ¤t_chunk_id.value); - - if (status == LTTNG_TRACE_CHUNK_STATUS_OK) { - current_chunk_id.is_set = true; - } else { - ERR("Failed to get current trace chunk id"); - ret = LTTNG_ERR_UNK; - goto error; - } - } - - if (usess && usess->consumer && usess->consumer->type == CONSUMER_DST_NET - && usess->consumer->enabled) { - /* For each consumer socket, send relayd sockets */ - cds_lfht_for_each_entry(usess->consumer->socks->ht, &iter.iter, - socket, node.node) { - pthread_mutex_lock(socket->lock); - ret = send_consumer_relayd_sockets(LTTNG_DOMAIN_UST, session->id, - usess->consumer, socket, - session->name, session->hostname, - session->base_path, - session->live_timer, - current_chunk_id.is_set ? ¤t_chunk_id.value : NULL, - session->creation_time, - session->name_contains_creation_time); - pthread_mutex_unlock(socket->lock); - if (ret != LTTNG_OK) { - goto error; - } - /* Session is now ready for network streaming. */ - session->net_handle = 1; - } - session->consumer->relay_major_version = - usess->consumer->relay_major_version; - session->consumer->relay_minor_version = - usess->consumer->relay_minor_version; - session->consumer->relay_allows_clear = - usess->consumer->relay_allows_clear; - } - - if (ksess && ksess->consumer && ksess->consumer->type == CONSUMER_DST_NET - && ksess->consumer->enabled) { - cds_lfht_for_each_entry(ksess->consumer->socks->ht, &iter.iter, - socket, node.node) { - pthread_mutex_lock(socket->lock); - ret = send_consumer_relayd_sockets(LTTNG_DOMAIN_KERNEL, session->id, - ksess->consumer, socket, - session->name, session->hostname, - session->base_path, - session->live_timer, - current_chunk_id.is_set ? ¤t_chunk_id.value : NULL, - session->creation_time, - session->name_contains_creation_time); - pthread_mutex_unlock(socket->lock); - if (ret != LTTNG_OK) { - goto error; - } - /* Session is now ready for network streaming. */ - session->net_handle = 1; - } - session->consumer->relay_major_version = - ksess->consumer->relay_major_version; - session->consumer->relay_minor_version = - ksess->consumer->relay_minor_version; - session->consumer->relay_allows_clear = - ksess->consumer->relay_allows_clear; - } - -error: - rcu_read_unlock(); - return ret; -} - -/* - * Start a kernel session by opening all necessary streams. - */ -int start_kernel_session(struct ltt_kernel_session *ksess) -{ - int ret; - struct ltt_kernel_channel *kchan; - - /* Open kernel metadata */ - if (ksess->metadata == NULL && ksess->output_traces) { - ret = kernel_open_metadata(ksess); - if (ret < 0) { - ret = LTTNG_ERR_KERN_META_FAIL; - goto error; - } - } - - /* Open kernel metadata stream */ - if (ksess->metadata && ksess->metadata_stream_fd < 0) { - ret = kernel_open_metadata_stream(ksess); - if (ret < 0) { - ERR("Kernel create metadata stream failed"); - ret = LTTNG_ERR_KERN_STREAM_FAIL; - goto error; - } - } - - /* For each channel */ - cds_list_for_each_entry(kchan, &ksess->channel_list.head, list) { - if (kchan->stream_count == 0) { - ret = kernel_open_channel_stream(kchan); - if (ret < 0) { - ret = LTTNG_ERR_KERN_STREAM_FAIL; - goto error; - } - /* Update the stream global counter */ - ksess->stream_count_global += ret; - } - } - - /* Setup kernel consumer socket and send fds to it */ - ret = init_kernel_tracing(ksess); - if (ret != 0) { - ret = LTTNG_ERR_KERN_START_FAIL; - goto error; - } - - /* This start the kernel tracing */ - ret = kernel_start_session(ksess); - if (ret < 0) { - ret = LTTNG_ERR_KERN_START_FAIL; - goto error; - } - - /* Quiescent wait after starting trace */ - kernel_wait_quiescent(); - - ksess->active = 1; - - ret = LTTNG_OK; - -error: - return ret; -} - -int stop_kernel_session(struct ltt_kernel_session *ksess) -{ - struct ltt_kernel_channel *kchan; - bool error_occurred = false; - int ret; - - if (!ksess || !ksess->active) { - return LTTNG_OK; - } - DBG("Stopping kernel tracing"); - - ret = kernel_stop_session(ksess); - if (ret < 0) { - ret = LTTNG_ERR_KERN_STOP_FAIL; - goto error; - } - - kernel_wait_quiescent(); - - /* Flush metadata after stopping (if exists) */ - if (ksess->metadata_stream_fd >= 0) { - ret = kernel_metadata_flush_buffer(ksess->metadata_stream_fd); - if (ret < 0) { - ERR("Kernel metadata flush failed"); - error_occurred = true; - } - } - - /* Flush all buffers after stopping */ - cds_list_for_each_entry(kchan, &ksess->channel_list.head, list) { - ret = kernel_flush_buffer(kchan); - if (ret < 0) { - ERR("Kernel flush buffer error"); - error_occurred = true; - } - } - - ksess->active = 0; - if (error_occurred) { - ret = LTTNG_ERR_UNK; - } else { - ret = LTTNG_OK; - } -error: - return ret; -} - -/* - * Command LTTNG_DISABLE_CHANNEL processed by the client thread. - */ -int cmd_disable_channel(struct ltt_session *session, - enum lttng_domain_type domain, char *channel_name) -{ - int ret; - struct ltt_ust_session *usess; - - usess = session->ust_session; - - rcu_read_lock(); - - switch (domain) { - case LTTNG_DOMAIN_KERNEL: - { - ret = channel_kernel_disable(session->kernel_session, - channel_name); - if (ret != LTTNG_OK) { - goto error; - } - - kernel_wait_quiescent(); - break; - } - case LTTNG_DOMAIN_UST: - { - struct ltt_ust_channel *uchan; - struct lttng_ht *chan_ht; - - chan_ht = usess->domain_global.channels; - - uchan = trace_ust_find_channel_by_name(chan_ht, channel_name); - if (uchan == NULL) { - ret = LTTNG_ERR_UST_CHAN_NOT_FOUND; - goto error; - } - - ret = channel_ust_disable(usess, uchan); - if (ret != LTTNG_OK) { - goto error; - } - break; - } - default: - ret = LTTNG_ERR_UNKNOWN_DOMAIN; - goto error; - } - - ret = LTTNG_OK; - -error: - rcu_read_unlock(); - return ret; -} - -/* - * Command LTTNG_ENABLE_CHANNEL processed by the client thread. - * - * The wpipe arguments is used as a notifier for the kernel thread. - */ -int cmd_enable_channel(struct ltt_session *session, - const struct lttng_domain *domain, const struct lttng_channel *_attr, int wpipe) -{ - int ret; - struct ltt_ust_session *usess = session->ust_session; - struct lttng_ht *chan_ht; - size_t len; - struct lttng_channel attr; - - LTTNG_ASSERT(session); - LTTNG_ASSERT(_attr); - LTTNG_ASSERT(domain); - - attr = *_attr; - len = lttng_strnlen(attr.name, sizeof(attr.name)); - - /* Validate channel name */ - if (attr.name[0] == '.' || - memchr(attr.name, '/', len) != NULL) { - ret = LTTNG_ERR_INVALID_CHANNEL_NAME; - goto end; - } - - DBG("Enabling channel %s for session %s", attr.name, session->name); - - rcu_read_lock(); - - /* - * If the session is a live session, remove the switch timer, the - * live timer does the same thing but sends also synchronisation - * beacons for inactive streams. - */ - if (session->live_timer > 0) { - attr.attr.live_timer_interval = session->live_timer; - attr.attr.switch_timer_interval = 0; - } - - /* Check for feature support */ - switch (domain->type) { - case LTTNG_DOMAIN_KERNEL: - { - if (kernel_supports_ring_buffer_snapshot_sample_positions() != 1) { - /* Sampling position of buffer is not supported */ - WARN("Kernel tracer does not support buffer monitoring. " - "Setting the monitor interval timer to 0 " - "(disabled) for channel '%s' of session '%s'", - attr.name, session->name); - lttng_channel_set_monitor_timer_interval(&attr, 0); - } - break; - } - case LTTNG_DOMAIN_UST: - break; - case LTTNG_DOMAIN_JUL: - case LTTNG_DOMAIN_LOG4J: - case LTTNG_DOMAIN_PYTHON: - if (!agent_tracing_is_enabled()) { - DBG("Attempted to enable a channel in an agent domain but the agent thread is not running"); - ret = LTTNG_ERR_AGENT_TRACING_DISABLED; - goto error; - } - break; - default: - ret = LTTNG_ERR_UNKNOWN_DOMAIN; - goto error; - } - - switch (domain->type) { - case LTTNG_DOMAIN_KERNEL: - { - struct ltt_kernel_channel *kchan; - - kchan = trace_kernel_get_channel_by_name(attr.name, - session->kernel_session); - if (kchan == NULL) { - /* - * Don't try to create a channel if the session has been started at - * some point in time before. The tracer does not allow it. - */ - if (session->has_been_started) { - ret = LTTNG_ERR_TRACE_ALREADY_STARTED; - goto error; - } - - if (session->snapshot.nb_output > 0 || - session->snapshot_mode) { - /* Enforce mmap output for snapshot sessions. */ - attr.attr.output = LTTNG_EVENT_MMAP; - } - ret = channel_kernel_create(session->kernel_session, &attr, wpipe); - if (attr.name[0] != '\0') { - session->kernel_session->has_non_default_channel = 1; - } - } else { - ret = channel_kernel_enable(session->kernel_session, kchan); - } - - if (ret != LTTNG_OK) { - goto error; - } - - kernel_wait_quiescent(); - break; - } - case LTTNG_DOMAIN_UST: - case LTTNG_DOMAIN_JUL: - case LTTNG_DOMAIN_LOG4J: - case LTTNG_DOMAIN_PYTHON: - { - struct ltt_ust_channel *uchan; - - /* - * FIXME - * - * Current agent implementation limitations force us to allow - * only one channel at once in "agent" subdomains. Each - * subdomain has a default channel name which must be strictly - * adhered to. - */ - if (domain->type == LTTNG_DOMAIN_JUL) { - if (strncmp(attr.name, DEFAULT_JUL_CHANNEL_NAME, - LTTNG_SYMBOL_NAME_LEN)) { - ret = LTTNG_ERR_INVALID_CHANNEL_NAME; - goto error; - } - } else if (domain->type == LTTNG_DOMAIN_LOG4J) { - if (strncmp(attr.name, DEFAULT_LOG4J_CHANNEL_NAME, - LTTNG_SYMBOL_NAME_LEN)) { - ret = LTTNG_ERR_INVALID_CHANNEL_NAME; - goto error; - } - } else if (domain->type == LTTNG_DOMAIN_PYTHON) { - if (strncmp(attr.name, DEFAULT_PYTHON_CHANNEL_NAME, - LTTNG_SYMBOL_NAME_LEN)) { - ret = LTTNG_ERR_INVALID_CHANNEL_NAME; - goto error; - } - } - - chan_ht = usess->domain_global.channels; - - uchan = trace_ust_find_channel_by_name(chan_ht, attr.name); - if (uchan == NULL) { - /* - * Don't try to create a channel if the session has been started at - * some point in time before. The tracer does not allow it. - */ - if (session->has_been_started) { - ret = LTTNG_ERR_TRACE_ALREADY_STARTED; - goto error; - } - - ret = channel_ust_create(usess, &attr, domain->buf_type); - if (attr.name[0] != '\0') { - usess->has_non_default_channel = 1; - } - } else { - ret = channel_ust_enable(usess, uchan); - } - break; - } - default: - ret = LTTNG_ERR_UNKNOWN_DOMAIN; - goto error; - } - - if (ret == LTTNG_OK && attr.attr.output != LTTNG_EVENT_MMAP) { - session->has_non_mmap_channel = true; - } -error: - rcu_read_unlock(); -end: - return ret; -} - -enum lttng_error_code cmd_process_attr_tracker_get_tracking_policy( - struct ltt_session *session, - enum lttng_domain_type domain, - enum lttng_process_attr process_attr, - enum lttng_tracking_policy *policy) -{ - enum lttng_error_code ret_code = LTTNG_OK; - const struct process_attr_tracker *tracker; - - switch (domain) { - case LTTNG_DOMAIN_KERNEL: - if (!session->kernel_session) { - ret_code = LTTNG_ERR_INVALID; - goto end; - } - tracker = kernel_get_process_attr_tracker( - session->kernel_session, process_attr); - break; - case LTTNG_DOMAIN_UST: - if (!session->ust_session) { - ret_code = LTTNG_ERR_INVALID; - goto end; - } - tracker = trace_ust_get_process_attr_tracker( - session->ust_session, process_attr); - break; - default: - ret_code = LTTNG_ERR_UNSUPPORTED_DOMAIN; - goto end; - } - if (tracker) { - *policy = process_attr_tracker_get_tracking_policy(tracker); - } else { - ret_code = LTTNG_ERR_INVALID; - } -end: - return ret_code; -} - -enum lttng_error_code cmd_process_attr_tracker_set_tracking_policy( - struct ltt_session *session, - enum lttng_domain_type domain, - enum lttng_process_attr process_attr, - enum lttng_tracking_policy policy) -{ - enum lttng_error_code ret_code = LTTNG_OK; - - switch (policy) { - case LTTNG_TRACKING_POLICY_INCLUDE_SET: - case LTTNG_TRACKING_POLICY_EXCLUDE_ALL: - case LTTNG_TRACKING_POLICY_INCLUDE_ALL: - break; - default: - ret_code = LTTNG_ERR_INVALID; - goto end; - } - - switch (domain) { - case LTTNG_DOMAIN_KERNEL: - if (!session->kernel_session) { - ret_code = LTTNG_ERR_INVALID; - goto end; - } - ret_code = kernel_process_attr_tracker_set_tracking_policy( - session->kernel_session, process_attr, policy); - break; - case LTTNG_DOMAIN_UST: - if (!session->ust_session) { - ret_code = LTTNG_ERR_INVALID; - goto end; - } - ret_code = trace_ust_process_attr_tracker_set_tracking_policy( - session->ust_session, process_attr, policy); - break; - default: - ret_code = LTTNG_ERR_UNSUPPORTED_DOMAIN; - break; - } -end: - return ret_code; -} - -enum lttng_error_code cmd_process_attr_tracker_inclusion_set_add_value( - struct ltt_session *session, - enum lttng_domain_type domain, - enum lttng_process_attr process_attr, - const struct process_attr_value *value) -{ - enum lttng_error_code ret_code = LTTNG_OK; - - switch (domain) { - case LTTNG_DOMAIN_KERNEL: - if (!session->kernel_session) { - ret_code = LTTNG_ERR_INVALID; - goto end; - } - ret_code = kernel_process_attr_tracker_inclusion_set_add_value( - session->kernel_session, process_attr, value); - break; - case LTTNG_DOMAIN_UST: - if (!session->ust_session) { - ret_code = LTTNG_ERR_INVALID; - goto end; - } - ret_code = trace_ust_process_attr_tracker_inclusion_set_add_value( - session->ust_session, process_attr, value); - break; - default: - ret_code = LTTNG_ERR_UNSUPPORTED_DOMAIN; - break; - } -end: - return ret_code; -} - -enum lttng_error_code cmd_process_attr_tracker_inclusion_set_remove_value( - struct ltt_session *session, - enum lttng_domain_type domain, - enum lttng_process_attr process_attr, - const struct process_attr_value *value) -{ - enum lttng_error_code ret_code = LTTNG_OK; - - switch (domain) { - case LTTNG_DOMAIN_KERNEL: - if (!session->kernel_session) { - ret_code = LTTNG_ERR_INVALID; - goto end; - } - ret_code = kernel_process_attr_tracker_inclusion_set_remove_value( - session->kernel_session, process_attr, value); - break; - case LTTNG_DOMAIN_UST: - if (!session->ust_session) { - ret_code = LTTNG_ERR_INVALID; - goto end; - } - ret_code = trace_ust_process_attr_tracker_inclusion_set_remove_value( - session->ust_session, process_attr, value); - break; - default: - ret_code = LTTNG_ERR_UNSUPPORTED_DOMAIN; - break; - } -end: - return ret_code; -} - -enum lttng_error_code cmd_process_attr_tracker_get_inclusion_set( - struct ltt_session *session, - enum lttng_domain_type domain, - enum lttng_process_attr process_attr, - struct lttng_process_attr_values **values) -{ - enum lttng_error_code ret_code = LTTNG_OK; - const struct process_attr_tracker *tracker; - enum process_attr_tracker_status status; - - switch (domain) { - case LTTNG_DOMAIN_KERNEL: - if (!session->kernel_session) { - ret_code = LTTNG_ERR_INVALID; - goto end; - } - tracker = kernel_get_process_attr_tracker( - session->kernel_session, process_attr); - break; - case LTTNG_DOMAIN_UST: - if (!session->ust_session) { - ret_code = LTTNG_ERR_INVALID; - goto end; - } - tracker = trace_ust_get_process_attr_tracker( - session->ust_session, process_attr); - break; - default: - ret_code = LTTNG_ERR_UNSUPPORTED_DOMAIN; - goto end; - } - - if (!tracker) { - ret_code = LTTNG_ERR_INVALID; - goto end; - } - - status = process_attr_tracker_get_inclusion_set(tracker, values); - switch (status) { - case PROCESS_ATTR_TRACKER_STATUS_OK: - ret_code = LTTNG_OK; - break; - case PROCESS_ATTR_TRACKER_STATUS_INVALID_TRACKING_POLICY: - ret_code = LTTNG_ERR_PROCESS_ATTR_TRACKER_INVALID_TRACKING_POLICY; - break; - case PROCESS_ATTR_TRACKER_STATUS_ERROR: - ret_code = LTTNG_ERR_NOMEM; - break; - default: - ret_code = LTTNG_ERR_UNK; - break; - } - -end: - return ret_code; -} - -/* - * Command LTTNG_DISABLE_EVENT processed by the client thread. - */ -int cmd_disable_event(struct ltt_session *session, - enum lttng_domain_type domain, const char *channel_name, - const struct lttng_event *event) -{ - int ret; - const char *event_name; - - DBG("Disable event command for event \'%s\'", event->name); - - event_name = event->name; - - /* Error out on unhandled search criteria */ - if (event->loglevel_type || event->loglevel != -1 || event->enabled - || event->pid || event->filter || event->exclusion) { - ret = LTTNG_ERR_UNK; - goto error; - } - - rcu_read_lock(); - - switch (domain) { - case LTTNG_DOMAIN_KERNEL: - { - struct ltt_kernel_channel *kchan; - struct ltt_kernel_session *ksess; - - ksess = session->kernel_session; - - /* - * If a non-default channel has been created in the - * session, explicitely require that -c chan_name needs - * to be provided. - */ - if (ksess->has_non_default_channel && channel_name[0] == '\0') { - ret = LTTNG_ERR_NEED_CHANNEL_NAME; - goto error_unlock; - } - - kchan = trace_kernel_get_channel_by_name(channel_name, ksess); - if (kchan == NULL) { - ret = LTTNG_ERR_KERN_CHAN_NOT_FOUND; - goto error_unlock; - } - - switch (event->type) { - case LTTNG_EVENT_ALL: - case LTTNG_EVENT_TRACEPOINT: - case LTTNG_EVENT_SYSCALL: - case LTTNG_EVENT_PROBE: - case LTTNG_EVENT_FUNCTION: - case LTTNG_EVENT_FUNCTION_ENTRY:/* fall-through */ - if (event_name[0] == '\0') { - ret = event_kernel_disable_event(kchan, - NULL, event->type); - } else { - ret = event_kernel_disable_event(kchan, - event_name, event->type); - } - if (ret != LTTNG_OK) { - goto error_unlock; - } - break; - default: - ret = LTTNG_ERR_UNK; - goto error_unlock; - } - - kernel_wait_quiescent(); - break; - } - case LTTNG_DOMAIN_UST: - { - struct ltt_ust_channel *uchan; - struct ltt_ust_session *usess; - - usess = session->ust_session; - - if (validate_ust_event_name(event_name)) { - ret = LTTNG_ERR_INVALID_EVENT_NAME; - goto error_unlock; - } - - /* - * If a non-default channel has been created in the - * session, explicitly require that -c chan_name needs - * to be provided. - */ - if (usess->has_non_default_channel && channel_name[0] == '\0') { - ret = LTTNG_ERR_NEED_CHANNEL_NAME; - goto error_unlock; - } - - uchan = trace_ust_find_channel_by_name(usess->domain_global.channels, - channel_name); - if (uchan == NULL) { - ret = LTTNG_ERR_UST_CHAN_NOT_FOUND; - goto error_unlock; - } - - switch (event->type) { - case LTTNG_EVENT_ALL: - /* - * An empty event name means that everything - * should be disabled. - */ - if (event->name[0] == '\0') { - ret = event_ust_disable_all_tracepoints(usess, uchan); - } else { - ret = event_ust_disable_tracepoint(usess, uchan, - event_name); - } - if (ret != LTTNG_OK) { - goto error_unlock; - } - break; - default: - ret = LTTNG_ERR_UNK; - goto error_unlock; - } - - DBG3("Disable UST event %s in channel %s completed", event_name, - channel_name); - break; - } - case LTTNG_DOMAIN_LOG4J: - case LTTNG_DOMAIN_JUL: - case LTTNG_DOMAIN_PYTHON: - { - struct agent *agt; - struct ltt_ust_session *usess = session->ust_session; - - LTTNG_ASSERT(usess); - - switch (event->type) { - case LTTNG_EVENT_ALL: - break; - default: - ret = LTTNG_ERR_UNK; - goto error_unlock; - } - - agt = trace_ust_find_agent(usess, domain); - if (!agt) { - ret = -LTTNG_ERR_UST_EVENT_NOT_FOUND; - goto error_unlock; - } - /* - * An empty event name means that everything - * should be disabled. - */ - if (event->name[0] == '\0') { - ret = event_agent_disable_all(usess, agt); - } else { - ret = event_agent_disable(usess, agt, event_name); - } - if (ret != LTTNG_OK) { - goto error_unlock; - } - - break; - } - default: - ret = LTTNG_ERR_UND; - goto error_unlock; - } - - ret = LTTNG_OK; - -error_unlock: - rcu_read_unlock(); -error: - return ret; -} - -/* - * Command LTTNG_ADD_CONTEXT processed by the client thread. - */ -int cmd_add_context(struct ltt_session *session, enum lttng_domain_type domain, - char *channel_name, const struct lttng_event_context *ctx, int kwpipe) -{ - int ret, chan_kern_created = 0, chan_ust_created = 0; - char *app_ctx_provider_name = NULL, *app_ctx_name = NULL; - - /* - * Don't try to add a context if the session has been started at - * some point in time before. The tracer does not allow it and would - * result in a corrupted trace. - */ - if (session->has_been_started) { - ret = LTTNG_ERR_TRACE_ALREADY_STARTED; - goto end; - } - - if (ctx->ctx == LTTNG_EVENT_CONTEXT_APP_CONTEXT) { - app_ctx_provider_name = ctx->u.app_ctx.provider_name; - app_ctx_name = ctx->u.app_ctx.ctx_name; - } - - switch (domain) { - case LTTNG_DOMAIN_KERNEL: - LTTNG_ASSERT(session->kernel_session); - - if (session->kernel_session->channel_count == 0) { - /* Create default channel */ - ret = channel_kernel_create(session->kernel_session, NULL, kwpipe); - if (ret != LTTNG_OK) { - goto error; - } - chan_kern_created = 1; - } - /* Add kernel context to kernel tracer */ - ret = context_kernel_add(session->kernel_session, ctx, channel_name); - if (ret != LTTNG_OK) { - goto error; - } - break; - case LTTNG_DOMAIN_JUL: - case LTTNG_DOMAIN_LOG4J: - { - /* - * Validate channel name. - * If no channel name is given and the domain is JUL or LOG4J, - * set it to the appropriate domain-specific channel name. If - * a name is provided but does not match the expexted channel - * name, return an error. - */ - if (domain == LTTNG_DOMAIN_JUL && *channel_name && - strcmp(channel_name, - DEFAULT_JUL_CHANNEL_NAME)) { - ret = LTTNG_ERR_UST_CHAN_NOT_FOUND; - goto error; - } else if (domain == LTTNG_DOMAIN_LOG4J && *channel_name && - strcmp(channel_name, - DEFAULT_LOG4J_CHANNEL_NAME)) { - ret = LTTNG_ERR_UST_CHAN_NOT_FOUND; - goto error; - } - /* break is _not_ missing here. */ - } - case LTTNG_DOMAIN_UST: - { - struct ltt_ust_session *usess = session->ust_session; - unsigned int chan_count; - - LTTNG_ASSERT(usess); - - chan_count = lttng_ht_get_count(usess->domain_global.channels); - if (chan_count == 0) { - struct lttng_channel *attr; - /* Create default channel */ - attr = channel_new_default_attr(domain, usess->buffer_type); - if (attr == NULL) { - ret = LTTNG_ERR_FATAL; - goto error; - } - - ret = channel_ust_create(usess, attr, usess->buffer_type); - if (ret != LTTNG_OK) { - free(attr); - goto error; - } - channel_attr_destroy(attr); - chan_ust_created = 1; - } - - ret = context_ust_add(usess, domain, ctx, channel_name); - free(app_ctx_provider_name); - free(app_ctx_name); - app_ctx_name = NULL; - app_ctx_provider_name = NULL; - if (ret != LTTNG_OK) { - goto error; - } - break; - } - default: - ret = LTTNG_ERR_UND; - goto error; - } - - ret = LTTNG_OK; - goto end; - -error: - if (chan_kern_created) { - struct ltt_kernel_channel *kchan = - trace_kernel_get_channel_by_name(DEFAULT_CHANNEL_NAME, - session->kernel_session); - /* Created previously, this should NOT fail. */ - LTTNG_ASSERT(kchan); - kernel_destroy_channel(kchan); - } - - if (chan_ust_created) { - struct ltt_ust_channel *uchan = - trace_ust_find_channel_by_name( - session->ust_session->domain_global.channels, - DEFAULT_CHANNEL_NAME); - /* Created previously, this should NOT fail. */ - LTTNG_ASSERT(uchan); - /* Remove from the channel list of the session. */ - trace_ust_delete_channel(session->ust_session->domain_global.channels, - uchan); - trace_ust_destroy_channel(uchan); - } -end: - free(app_ctx_provider_name); - free(app_ctx_name); - return ret; -} - -static inline bool name_starts_with(const char *name, const char *prefix) -{ - const size_t max_cmp_len = min(strlen(prefix), LTTNG_SYMBOL_NAME_LEN); - - return !strncmp(name, prefix, max_cmp_len); -} - -/* Perform userspace-specific event name validation */ -static int validate_ust_event_name(const char *name) -{ - int ret = 0; - - if (!name) { - ret = -1; - goto end; - } - - /* - * Check name against all internal UST event component namespaces used - * by the agents. - */ - if (name_starts_with(name, DEFAULT_JUL_EVENT_COMPONENT) || - name_starts_with(name, DEFAULT_LOG4J_EVENT_COMPONENT) || - name_starts_with(name, DEFAULT_PYTHON_EVENT_COMPONENT)) { - ret = -1; - } - -end: - return ret; -} - -/* - * Internal version of cmd_enable_event() with a supplemental - * "internal_event" flag which is used to enable internal events which should - * be hidden from clients. Such events are used in the agent implementation to - * enable the events through which all "agent" events are funeled. - */ -static int _cmd_enable_event(struct ltt_session *session, - const struct lttng_domain *domain, - char *channel_name, struct lttng_event *event, - char *filter_expression, - struct lttng_bytecode *filter, - struct lttng_event_exclusion *exclusion, - int wpipe, bool internal_event) -{ - int ret = 0, channel_created = 0; - struct lttng_channel *attr = NULL; - - LTTNG_ASSERT(session); - LTTNG_ASSERT(event); - LTTNG_ASSERT(channel_name); - - /* If we have a filter, we must have its filter expression */ - LTTNG_ASSERT(!(!!filter_expression ^ !!filter)); - - /* Normalize event name as a globbing pattern */ - strutils_normalize_star_glob_pattern(event->name); - - /* Normalize exclusion names as globbing patterns */ - if (exclusion) { - size_t i; - - for (i = 0; i < exclusion->count; i++) { - char *name = LTTNG_EVENT_EXCLUSION_NAME_AT(exclusion, i); - - strutils_normalize_star_glob_pattern(name); - } - } - - DBG("Enable event command for event \'%s\'", event->name); - - rcu_read_lock(); - - switch (domain->type) { - case LTTNG_DOMAIN_KERNEL: - { - struct ltt_kernel_channel *kchan; - - /* - * If a non-default channel has been created in the - * session, explicitely require that -c chan_name needs - * to be provided. - */ - if (session->kernel_session->has_non_default_channel - && channel_name[0] == '\0') { - ret = LTTNG_ERR_NEED_CHANNEL_NAME; - goto error; - } - - kchan = trace_kernel_get_channel_by_name(channel_name, - session->kernel_session); - if (kchan == NULL) { - attr = channel_new_default_attr(LTTNG_DOMAIN_KERNEL, - LTTNG_BUFFER_GLOBAL); - if (attr == NULL) { - ret = LTTNG_ERR_FATAL; - goto error; - } - if (lttng_strncpy(attr->name, channel_name, - sizeof(attr->name))) { - ret = LTTNG_ERR_INVALID; - goto error; - } - - ret = cmd_enable_channel(session, domain, attr, wpipe); - if (ret != LTTNG_OK) { - goto error; - } - channel_created = 1; - } - - /* Get the newly created kernel channel pointer */ - kchan = trace_kernel_get_channel_by_name(channel_name, - session->kernel_session); - if (kchan == NULL) { - /* This sould not happen... */ - ret = LTTNG_ERR_FATAL; - goto error; - } - - switch (event->type) { - case LTTNG_EVENT_ALL: - { - char *filter_expression_a = NULL; - struct lttng_bytecode *filter_a = NULL; - - /* - * We need to duplicate filter_expression and filter, - * because ownership is passed to first enable - * event. - */ - if (filter_expression) { - filter_expression_a = strdup(filter_expression); - if (!filter_expression_a) { - ret = LTTNG_ERR_FATAL; - goto error; - } - } - if (filter) { - filter_a = zmalloc(sizeof(*filter_a) + filter->len); - if (!filter_a) { - free(filter_expression_a); - ret = LTTNG_ERR_FATAL; - goto error; - } - memcpy(filter_a, filter, sizeof(*filter_a) + filter->len); - } - event->type = LTTNG_EVENT_TRACEPOINT; /* Hack */ - ret = event_kernel_enable_event(kchan, event, - filter_expression, filter); - /* We have passed ownership */ - filter_expression = NULL; - filter = NULL; - if (ret != LTTNG_OK) { - if (channel_created) { - /* Let's not leak a useless channel. */ - kernel_destroy_channel(kchan); - } - free(filter_expression_a); - free(filter_a); - goto error; - } - event->type = LTTNG_EVENT_SYSCALL; /* Hack */ - ret = event_kernel_enable_event(kchan, event, - filter_expression_a, filter_a); - /* We have passed ownership */ - filter_expression_a = NULL; - filter_a = NULL; - if (ret != LTTNG_OK) { - goto error; - } - break; - } - case LTTNG_EVENT_PROBE: - case LTTNG_EVENT_USERSPACE_PROBE: - case LTTNG_EVENT_FUNCTION: - case LTTNG_EVENT_FUNCTION_ENTRY: - case LTTNG_EVENT_TRACEPOINT: - ret = event_kernel_enable_event(kchan, event, - filter_expression, filter); - /* We have passed ownership */ - filter_expression = NULL; - filter = NULL; - if (ret != LTTNG_OK) { - if (channel_created) { - /* Let's not leak a useless channel. */ - kernel_destroy_channel(kchan); - } - goto error; - } - break; - case LTTNG_EVENT_SYSCALL: - ret = event_kernel_enable_event(kchan, event, - filter_expression, filter); - /* We have passed ownership */ - filter_expression = NULL; - filter = NULL; - if (ret != LTTNG_OK) { - goto error; - } - break; - default: - ret = LTTNG_ERR_UNK; - goto error; - } - - kernel_wait_quiescent(); - break; - } - case LTTNG_DOMAIN_UST: - { - struct ltt_ust_channel *uchan; - struct ltt_ust_session *usess = session->ust_session; - - LTTNG_ASSERT(usess); - - /* - * If a non-default channel has been created in the - * session, explicitely require that -c chan_name needs - * to be provided. - */ - if (usess->has_non_default_channel && channel_name[0] == '\0') { - ret = LTTNG_ERR_NEED_CHANNEL_NAME; - goto error; - } - - /* Get channel from global UST domain */ - uchan = trace_ust_find_channel_by_name(usess->domain_global.channels, - channel_name); - if (uchan == NULL) { - /* Create default channel */ - attr = channel_new_default_attr(LTTNG_DOMAIN_UST, - usess->buffer_type); - if (attr == NULL) { - ret = LTTNG_ERR_FATAL; - goto error; - } - if (lttng_strncpy(attr->name, channel_name, - sizeof(attr->name))) { - ret = LTTNG_ERR_INVALID; - goto error; - } - - ret = cmd_enable_channel(session, domain, attr, wpipe); - if (ret != LTTNG_OK) { - goto error; - } - - /* Get the newly created channel reference back */ - uchan = trace_ust_find_channel_by_name( - usess->domain_global.channels, channel_name); - LTTNG_ASSERT(uchan); - } - - if (uchan->domain != LTTNG_DOMAIN_UST && !internal_event) { - /* - * Don't allow users to add UST events to channels which - * are assigned to a userspace subdomain (JUL, Log4J, - * Python, etc.). - */ - ret = LTTNG_ERR_INVALID_CHANNEL_DOMAIN; - goto error; - } - - if (!internal_event) { - /* - * Ensure the event name is not reserved for internal - * use. - */ - ret = validate_ust_event_name(event->name); - if (ret) { - WARN("Userspace event name %s failed validation.", - event->name); - ret = LTTNG_ERR_INVALID_EVENT_NAME; - goto error; - } - } - - /* At this point, the session and channel exist on the tracer */ - ret = event_ust_enable_tracepoint(usess, uchan, event, - filter_expression, filter, exclusion, - internal_event); - /* We have passed ownership */ - filter_expression = NULL; - filter = NULL; - exclusion = NULL; - if (ret == LTTNG_ERR_UST_EVENT_ENABLED) { - goto already_enabled; - } else if (ret != LTTNG_OK) { - goto error; - } - break; - } - case LTTNG_DOMAIN_LOG4J: - case LTTNG_DOMAIN_JUL: - case LTTNG_DOMAIN_PYTHON: - { - const char *default_event_name, *default_chan_name; - struct agent *agt; - struct lttng_event uevent; - struct lttng_domain tmp_dom; - struct ltt_ust_session *usess = session->ust_session; - - LTTNG_ASSERT(usess); - - if (!agent_tracing_is_enabled()) { - DBG("Attempted to enable an event in an agent domain but the agent thread is not running"); - ret = LTTNG_ERR_AGENT_TRACING_DISABLED; - goto error; - } - - agt = trace_ust_find_agent(usess, domain->type); - if (!agt) { - agt = agent_create(domain->type); - if (!agt) { - ret = LTTNG_ERR_NOMEM; - goto error; - } - agent_add(agt, usess->agents); - } - - /* Create the default tracepoint. */ - memset(&uevent, 0, sizeof(uevent)); - uevent.type = LTTNG_EVENT_TRACEPOINT; - uevent.loglevel_type = LTTNG_EVENT_LOGLEVEL_ALL; - default_event_name = event_get_default_agent_ust_name( - domain->type); - if (!default_event_name) { - ret = LTTNG_ERR_FATAL; - goto error; - } - strncpy(uevent.name, default_event_name, sizeof(uevent.name)); - uevent.name[sizeof(uevent.name) - 1] = '\0'; - - /* - * The domain type is changed because we are about to enable the - * default channel and event for the JUL domain that are hardcoded. - * This happens in the UST domain. - */ - memcpy(&tmp_dom, domain, sizeof(tmp_dom)); - tmp_dom.type = LTTNG_DOMAIN_UST; - - switch (domain->type) { - case LTTNG_DOMAIN_LOG4J: - default_chan_name = DEFAULT_LOG4J_CHANNEL_NAME; - break; - case LTTNG_DOMAIN_JUL: - default_chan_name = DEFAULT_JUL_CHANNEL_NAME; - break; - case LTTNG_DOMAIN_PYTHON: - default_chan_name = DEFAULT_PYTHON_CHANNEL_NAME; - break; - default: - /* The switch/case we are in makes this impossible */ - abort(); - } - - { - char *filter_expression_copy = NULL; - struct lttng_bytecode *filter_copy = NULL; - - if (filter) { - const size_t filter_size = sizeof( - struct lttng_bytecode) - + filter->len; - - filter_copy = zmalloc(filter_size); - if (!filter_copy) { - ret = LTTNG_ERR_NOMEM; - goto error; - } - memcpy(filter_copy, filter, filter_size); - - filter_expression_copy = - strdup(filter_expression); - if (!filter_expression) { - ret = LTTNG_ERR_NOMEM; - } - - if (!filter_expression_copy || !filter_copy) { - free(filter_expression_copy); - free(filter_copy); - goto error; - } - } - - ret = cmd_enable_event_internal(session, &tmp_dom, - (char *) default_chan_name, - &uevent, filter_expression_copy, - filter_copy, NULL, wpipe); - } - - if (ret == LTTNG_ERR_UST_EVENT_ENABLED) { - goto already_enabled; - } else if (ret != LTTNG_OK) { - goto error; - } - - /* The wild card * means that everything should be enabled. */ - if (strncmp(event->name, "*", 1) == 0 && strlen(event->name) == 1) { - ret = event_agent_enable_all(usess, agt, event, filter, - filter_expression); - } else { - ret = event_agent_enable(usess, agt, event, filter, - filter_expression); - } - filter = NULL; - filter_expression = NULL; - if (ret != LTTNG_OK) { - goto error; - } - - break; - } - default: - ret = LTTNG_ERR_UND; - goto error; - } - - ret = LTTNG_OK; - -already_enabled: -error: - free(filter_expression); - free(filter); - free(exclusion); - channel_attr_destroy(attr); - rcu_read_unlock(); - return ret; -} - -/* - * Command LTTNG_ENABLE_EVENT processed by the client thread. - * We own filter, exclusion, and filter_expression. - */ -int cmd_enable_event(struct ltt_session *session, - const struct lttng_domain *domain, - char *channel_name, struct lttng_event *event, - char *filter_expression, - struct lttng_bytecode *filter, - struct lttng_event_exclusion *exclusion, - int wpipe) -{ - return _cmd_enable_event(session, domain, channel_name, event, - filter_expression, filter, exclusion, wpipe, false); -} - -/* - * Enable an event which is internal to LTTng. An internal should - * never be made visible to clients and are immune to checks such as - * reserved names. - */ -static int cmd_enable_event_internal(struct ltt_session *session, - const struct lttng_domain *domain, - char *channel_name, struct lttng_event *event, - char *filter_expression, - struct lttng_bytecode *filter, - struct lttng_event_exclusion *exclusion, - int wpipe) -{ - return _cmd_enable_event(session, domain, channel_name, event, - filter_expression, filter, exclusion, wpipe, true); -} - -/* - * Command LTTNG_LIST_TRACEPOINTS processed by the client thread. - */ -ssize_t cmd_list_tracepoints(enum lttng_domain_type domain, - struct lttng_event **events) -{ - int ret; - ssize_t nb_events = 0; - - switch (domain) { - case LTTNG_DOMAIN_KERNEL: - nb_events = kernel_list_events(events); - if (nb_events < 0) { - ret = LTTNG_ERR_KERN_LIST_FAIL; - goto error; - } - break; - case LTTNG_DOMAIN_UST: - nb_events = ust_app_list_events(events); - if (nb_events < 0) { - ret = LTTNG_ERR_UST_LIST_FAIL; - goto error; - } - break; - case LTTNG_DOMAIN_LOG4J: - case LTTNG_DOMAIN_JUL: - case LTTNG_DOMAIN_PYTHON: - nb_events = agent_list_events(events, domain); - if (nb_events < 0) { - ret = LTTNG_ERR_UST_LIST_FAIL; - goto error; - } - break; - default: - ret = LTTNG_ERR_UND; - goto error; - } - - return nb_events; - -error: - /* Return negative value to differentiate return code */ - return -ret; -} - -/* - * Command LTTNG_LIST_TRACEPOINT_FIELDS processed by the client thread. - */ -ssize_t cmd_list_tracepoint_fields(enum lttng_domain_type domain, - struct lttng_event_field **fields) -{ - int ret; - ssize_t nb_fields = 0; - - switch (domain) { - case LTTNG_DOMAIN_UST: - nb_fields = ust_app_list_event_fields(fields); - if (nb_fields < 0) { - ret = LTTNG_ERR_UST_LIST_FAIL; - goto error; - } - break; - case LTTNG_DOMAIN_KERNEL: - default: /* fall-through */ - ret = LTTNG_ERR_UND; - goto error; - } - - return nb_fields; - -error: - /* Return negative value to differentiate return code */ - return -ret; -} - -ssize_t cmd_list_syscalls(struct lttng_event **events) -{ - return syscall_table_list(events); -} - -/* - * Command LTTNG_START_TRACE processed by the client thread. - * - * Called with session mutex held. - */ -int cmd_start_trace(struct ltt_session *session) -{ - enum lttng_error_code ret; - unsigned long nb_chan = 0; - struct ltt_kernel_session *ksession; - struct ltt_ust_session *usess; - const bool session_rotated_after_last_stop = - session->rotated_after_last_stop; - const bool session_cleared_after_last_stop = - session->cleared_after_last_stop; - - LTTNG_ASSERT(session); - - /* Ease our life a bit ;) */ - ksession = session->kernel_session; - usess = session->ust_session; - - /* Is the session already started? */ - if (session->active) { - ret = LTTNG_ERR_TRACE_ALREADY_STARTED; - /* Perform nothing */ - goto end; - } - - if (session->rotation_state == LTTNG_ROTATION_STATE_ONGOING && - !session->current_trace_chunk) { - /* - * A rotation was launched while the session was stopped and - * it has not been completed yet. It is not possible to start - * the session since starting the session here would require a - * rotation from "NULL" to a new trace chunk. That rotation - * would overlap with the ongoing rotation, which is not - * supported. - */ - WARN("Refusing to start session \"%s\" as a rotation launched after the last \"stop\" is still ongoing", - session->name); - ret = LTTNG_ERR_ROTATION_PENDING; - goto error; - } - - /* - * Starting a session without channel is useless since after that it's not - * possible to enable channel thus inform the client. - */ - if (usess && usess->domain_global.channels) { - nb_chan += lttng_ht_get_count(usess->domain_global.channels); - } - if (ksession) { - nb_chan += ksession->channel_count; - } - if (!nb_chan) { - ret = LTTNG_ERR_NO_CHANNEL; - goto error; - } - - session->active = 1; - session->rotated_after_last_stop = false; - session->cleared_after_last_stop = false; - if (session->output_traces && !session->current_trace_chunk) { - if (!session->has_been_started) { - struct lttng_trace_chunk *trace_chunk; - - DBG("Creating initial trace chunk of session \"%s\"", - session->name); - trace_chunk = session_create_new_trace_chunk( - session, NULL, NULL, NULL); - if (!trace_chunk) { - ret = LTTNG_ERR_CREATE_DIR_FAIL; - goto error; - } - LTTNG_ASSERT(!session->current_trace_chunk); - ret = session_set_trace_chunk(session, trace_chunk, - NULL); - lttng_trace_chunk_put(trace_chunk); - if (ret) { - ret = LTTNG_ERR_CREATE_TRACE_CHUNK_FAIL_CONSUMER; - goto error; - } - } else { - DBG("Rotating session \"%s\" from its current \"NULL\" trace chunk to a new chunk", - session->name); - /* - * Rotate existing streams into the new chunk. - * This is a "quiet" rotation has no client has - * explicitly requested this operation. - * - * There is also no need to wait for the rotation - * to complete as it will happen immediately. No data - * was produced as the session was stopped, so the - * rotation should happen on reception of the command. - */ - ret = cmd_rotate_session(session, NULL, true, - LTTNG_TRACE_CHUNK_COMMAND_TYPE_NO_OPERATION); - if (ret != LTTNG_OK) { - goto error; - } - } - } - - /* Kernel tracing */ - if (ksession != NULL) { - DBG("Start kernel tracing session %s", session->name); - ret = start_kernel_session(ksession); - if (ret != LTTNG_OK) { - goto error; - } - } - - /* Flag session that trace should start automatically */ - if (usess) { - int int_ret = ust_app_start_trace_all(usess); - - if (int_ret < 0) { - ret = LTTNG_ERR_UST_START_FAIL; - goto error; - } - } - - /* - * Open a packet in every stream of the session to ensure that viewers - * can correctly identify the boundaries of the periods during which - * tracing was active for this session. - */ - ret = session_open_packets(session); - if (ret != LTTNG_OK) { - goto error; - } - - /* - * Clear the flag that indicates that a rotation was done while the - * session was stopped. - */ - session->rotated_after_last_stop = false; - - if (session->rotate_timer_period) { - int int_ret = timer_session_rotation_schedule_timer_start( - session, session->rotate_timer_period); - - if (int_ret < 0) { - ERR("Failed to enable rotate timer"); - ret = LTTNG_ERR_UNK; - goto error; - } - } - - ret = LTTNG_OK; - -error: - if (ret == LTTNG_OK) { - /* Flag this after a successful start. */ - session->has_been_started |= 1; - } else { - session->active = 0; - /* Restore initial state on error. */ - session->rotated_after_last_stop = - session_rotated_after_last_stop; - session->cleared_after_last_stop = - session_cleared_after_last_stop; - } -end: - return ret; -} - -/* - * Command LTTNG_STOP_TRACE processed by the client thread. - */ -int cmd_stop_trace(struct ltt_session *session) -{ - int ret; - struct ltt_kernel_session *ksession; - struct ltt_ust_session *usess; - - LTTNG_ASSERT(session); - - DBG("Begin stop session \"%s\" (id %" PRIu64 ")", session->name, session->id); - /* Short cut */ - ksession = session->kernel_session; - usess = session->ust_session; - - /* Session is not active. Skip everythong and inform the client. */ - if (!session->active) { - ret = LTTNG_ERR_TRACE_ALREADY_STOPPED; - goto error; - } - - ret = stop_kernel_session(ksession); - if (ret != LTTNG_OK) { - goto error; - } - - if (usess && usess->active) { - ret = ust_app_stop_trace_all(usess); - if (ret < 0) { - ret = LTTNG_ERR_UST_STOP_FAIL; - goto error; - } - } - - DBG("Completed stop session \"%s\" (id %" PRIu64 ")", session->name, - session->id); - /* Flag inactive after a successful stop. */ - session->active = 0; - ret = LTTNG_OK; - -error: - return ret; -} - -/* - * Set the base_path of the session only if subdir of a control uris is set. - * Return LTTNG_OK on success, otherwise LTTNG_ERR_*. - */ -static int set_session_base_path_from_uris(struct ltt_session *session, - size_t nb_uri, - struct lttng_uri *uris) -{ - int ret; - size_t i; - - for (i = 0; i < nb_uri; i++) { - if (uris[i].stype != LTTNG_STREAM_CONTROL || - uris[i].subdir[0] == '\0') { - /* Not interested in these URIs */ - continue; - } - - if (session->base_path != NULL) { - free(session->base_path); - session->base_path = NULL; - } - - /* Set session base_path */ - session->base_path = strdup(uris[i].subdir); - if (!session->base_path) { - PERROR("Failed to copy base path \"%s\" to session \"%s\"", - uris[i].subdir, session->name); - ret = LTTNG_ERR_NOMEM; - goto error; - } - DBG2("Setting base path \"%s\" for session \"%s\"", - session->base_path, session->name); - } - ret = LTTNG_OK; -error: - return ret; -} - -/* - * Command LTTNG_SET_CONSUMER_URI processed by the client thread. - */ -int cmd_set_consumer_uri(struct ltt_session *session, size_t nb_uri, - struct lttng_uri *uris) -{ - int ret, i; - struct ltt_kernel_session *ksess = session->kernel_session; - struct ltt_ust_session *usess = session->ust_session; - - LTTNG_ASSERT(session); - LTTNG_ASSERT(uris); - LTTNG_ASSERT(nb_uri > 0); - - /* Can't set consumer URI if the session is active. */ - if (session->active) { - ret = LTTNG_ERR_TRACE_ALREADY_STARTED; - goto error; - } - - /* - * Set the session base path if any. This is done inside - * cmd_set_consumer_uri to preserve backward compatibility of the - * previous session creation api vs the session descriptor api. - */ - ret = set_session_base_path_from_uris(session, nb_uri, uris); - if (ret != LTTNG_OK) { - goto error; - } - - /* Set the "global" consumer URIs */ - for (i = 0; i < nb_uri; i++) { - ret = add_uri_to_consumer(session, session->consumer, &uris[i], - LTTNG_DOMAIN_NONE); - if (ret != LTTNG_OK) { - goto error; - } - } - - /* Set UST session URIs */ - if (session->ust_session) { - for (i = 0; i < nb_uri; i++) { - ret = add_uri_to_consumer(session, - session->ust_session->consumer, - &uris[i], LTTNG_DOMAIN_UST); - if (ret != LTTNG_OK) { - goto error; - } - } - } - - /* Set kernel session URIs */ - if (session->kernel_session) { - for (i = 0; i < nb_uri; i++) { - ret = add_uri_to_consumer(session, - session->kernel_session->consumer, - &uris[i], LTTNG_DOMAIN_KERNEL); - if (ret != LTTNG_OK) { - goto error; - } - } - } - - /* - * Make sure to set the session in output mode after we set URI since a - * session can be created without URL (thus flagged in no output mode). - */ - session->output_traces = 1; - if (ksess) { - ksess->output_traces = 1; - } - - if (usess) { - usess->output_traces = 1; - } - - /* All good! */ - ret = LTTNG_OK; - -error: - return ret; -} - -static -enum lttng_error_code set_session_output_from_descriptor( - struct ltt_session *session, - const struct lttng_session_descriptor *descriptor) -{ - int ret; - enum lttng_error_code ret_code = LTTNG_OK; - enum lttng_session_descriptor_type session_type = - lttng_session_descriptor_get_type(descriptor); - enum lttng_session_descriptor_output_type output_type = - lttng_session_descriptor_get_output_type(descriptor); - struct lttng_uri uris[2] = {}; - size_t uri_count = 0; - - switch (output_type) { - case LTTNG_SESSION_DESCRIPTOR_OUTPUT_TYPE_NONE: - goto end; - case LTTNG_SESSION_DESCRIPTOR_OUTPUT_TYPE_LOCAL: - lttng_session_descriptor_get_local_output_uri(descriptor, - &uris[0]); - uri_count = 1; - break; - case LTTNG_SESSION_DESCRIPTOR_OUTPUT_TYPE_NETWORK: - lttng_session_descriptor_get_network_output_uris(descriptor, - &uris[0], &uris[1]); - uri_count = 2; - break; - default: - ret_code = LTTNG_ERR_INVALID; - goto end; - } - - switch (session_type) { - case LTTNG_SESSION_DESCRIPTOR_TYPE_SNAPSHOT: - { - struct snapshot_output *new_output = NULL; - - new_output = snapshot_output_alloc(); - if (!new_output) { - ret_code = LTTNG_ERR_NOMEM; - goto end; - } - - ret = snapshot_output_init_with_uri(session, - DEFAULT_SNAPSHOT_MAX_SIZE, - NULL, uris, uri_count, session->consumer, - new_output, &session->snapshot); - if (ret < 0) { - ret_code = (ret == -ENOMEM) ? - LTTNG_ERR_NOMEM : LTTNG_ERR_INVALID; - snapshot_output_destroy(new_output); - goto end; - } - snapshot_add_output(&session->snapshot, new_output); - break; - } - case LTTNG_SESSION_DESCRIPTOR_TYPE_REGULAR: - case LTTNG_SESSION_DESCRIPTOR_TYPE_LIVE: - { - ret_code = cmd_set_consumer_uri(session, uri_count, uris); - break; - } - default: - ret_code = LTTNG_ERR_INVALID; - goto end; - } -end: - return ret_code; -} - -static -enum lttng_error_code cmd_create_session_from_descriptor( - struct lttng_session_descriptor *descriptor, - const lttng_sock_cred *creds, - const char *home_path) -{ - int ret; - enum lttng_error_code ret_code; - const char *session_name; - struct ltt_session *new_session = NULL; - enum lttng_session_descriptor_status descriptor_status; - - session_lock_list(); - if (home_path) { - if (*home_path != '/') { - ERR("Home path provided by client is not absolute"); - ret_code = LTTNG_ERR_INVALID; - goto end; - } - } - - descriptor_status = lttng_session_descriptor_get_session_name( - descriptor, &session_name); - switch (descriptor_status) { - case LTTNG_SESSION_DESCRIPTOR_STATUS_OK: - break; - case LTTNG_SESSION_DESCRIPTOR_STATUS_UNSET: - session_name = NULL; - break; - default: - ret_code = LTTNG_ERR_INVALID; - goto end; - } - - ret_code = session_create(session_name, creds->uid, creds->gid, - &new_session); - if (ret_code != LTTNG_OK) { - goto end; - } - - if (!session_name) { - ret = lttng_session_descriptor_set_session_name(descriptor, - new_session->name); - if (ret) { - ret_code = LTTNG_ERR_SESSION_FAIL; - goto end; - } - } - - if (!lttng_session_descriptor_is_output_destination_initialized( - descriptor)) { - /* - * Only include the session's creation time in the output - * destination if the name of the session itself was - * not auto-generated. - */ - ret_code = lttng_session_descriptor_set_default_output( - descriptor, - session_name ? &new_session->creation_time : NULL, - home_path); - if (ret_code != LTTNG_OK) { - goto end; - } - } else { - new_session->has_user_specified_directory = - lttng_session_descriptor_has_output_directory( - descriptor); - } - - switch (lttng_session_descriptor_get_type(descriptor)) { - case LTTNG_SESSION_DESCRIPTOR_TYPE_SNAPSHOT: - new_session->snapshot_mode = 1; - break; - case LTTNG_SESSION_DESCRIPTOR_TYPE_LIVE: - new_session->live_timer = - lttng_session_descriptor_live_get_timer_interval( - descriptor); - break; - default: - break; - } - - ret_code = set_session_output_from_descriptor(new_session, descriptor); - if (ret_code != LTTNG_OK) { - goto end; - } - new_session->consumer->enabled = 1; - ret_code = LTTNG_OK; -end: - /* Release reference provided by the session_create function. */ - session_put(new_session); - if (ret_code != LTTNG_OK && new_session) { - /* Release the global reference on error. */ - session_destroy(new_session); - } - session_unlock_list(); - return ret_code; -} - -enum lttng_error_code cmd_create_session(struct command_ctx *cmd_ctx, int sock, - struct lttng_session_descriptor **return_descriptor) -{ - int ret; - size_t payload_size; - struct lttng_dynamic_buffer payload; - struct lttng_buffer_view home_dir_view; - struct lttng_buffer_view session_descriptor_view; - struct lttng_session_descriptor *session_descriptor = NULL; - enum lttng_error_code ret_code; - - lttng_dynamic_buffer_init(&payload); - if (cmd_ctx->lsm.u.create_session.home_dir_size >= - LTTNG_PATH_MAX) { - ret_code = LTTNG_ERR_INVALID; - goto error; - } - if (cmd_ctx->lsm.u.create_session.session_descriptor_size > - LTTNG_SESSION_DESCRIPTOR_MAX_LEN) { - ret_code = LTTNG_ERR_INVALID; - goto error; - } - - payload_size = cmd_ctx->lsm.u.create_session.home_dir_size + - cmd_ctx->lsm.u.create_session.session_descriptor_size; - ret = lttng_dynamic_buffer_set_size(&payload, payload_size); - if (ret) { - ret_code = LTTNG_ERR_NOMEM; - goto error; - } - - ret = lttcomm_recv_unix_sock(sock, payload.data, payload.size); - if (ret <= 0) { - ERR("Reception of session descriptor failed, aborting."); - ret_code = LTTNG_ERR_SESSION_FAIL; - goto error; - } - - home_dir_view = lttng_buffer_view_from_dynamic_buffer( - &payload, - 0, - cmd_ctx->lsm.u.create_session.home_dir_size); - if (cmd_ctx->lsm.u.create_session.home_dir_size > 0 && - !lttng_buffer_view_is_valid(&home_dir_view)) { - ERR("Invalid payload in \"create session\" command: buffer too short to contain home directory"); - ret_code = LTTNG_ERR_INVALID_PROTOCOL; - goto error; - } - - session_descriptor_view = lttng_buffer_view_from_dynamic_buffer( - &payload, - cmd_ctx->lsm.u.create_session.home_dir_size, - cmd_ctx->lsm.u.create_session.session_descriptor_size); - if (!lttng_buffer_view_is_valid(&session_descriptor_view)) { - ERR("Invalid payload in \"create session\" command: buffer too short to contain session descriptor"); - ret_code = LTTNG_ERR_INVALID_PROTOCOL; - goto error; - } - - ret = lttng_session_descriptor_create_from_buffer( - &session_descriptor_view, &session_descriptor); - if (ret < 0) { - ERR("Failed to create session descriptor from payload of \"create session\" command"); - ret_code = LTTNG_ERR_INVALID; - goto error; - } - - /* - * Sets the descriptor's auto-generated properties (name, output) if - * needed. - */ - ret_code = cmd_create_session_from_descriptor(session_descriptor, - &cmd_ctx->creds, - home_dir_view.size ? home_dir_view.data : NULL); - if (ret_code != LTTNG_OK) { - goto error; - } - - ret_code = LTTNG_OK; - *return_descriptor = session_descriptor; - session_descriptor = NULL; -error: - lttng_dynamic_buffer_reset(&payload); - lttng_session_descriptor_destroy(session_descriptor); - return ret_code; -} - -static -void cmd_destroy_session_reply(const struct ltt_session *session, - void *_reply_context) -{ - int ret; - ssize_t comm_ret; - const struct cmd_destroy_session_reply_context *reply_context = - _reply_context; - struct lttng_dynamic_buffer payload; - struct lttcomm_session_destroy_command_header cmd_header; - struct lttng_trace_archive_location *location = NULL; - struct lttcomm_lttng_msg llm = { - .cmd_type = LTTNG_DESTROY_SESSION, - .ret_code = reply_context->destruction_status, - .pid = UINT32_MAX, - .cmd_header_size = - sizeof(struct lttcomm_session_destroy_command_header), - .data_size = 0, - }; - size_t payload_size_before_location; - - lttng_dynamic_buffer_init(&payload); - - ret = lttng_dynamic_buffer_append(&payload, &llm, sizeof(llm)); - if (ret) { - ERR("Failed to append session destruction message"); - goto error; - } - - cmd_header.rotation_state = - (int32_t) (reply_context->implicit_rotation_on_destroy ? - session->rotation_state : - LTTNG_ROTATION_STATE_NO_ROTATION); - ret = lttng_dynamic_buffer_append(&payload, &cmd_header, - sizeof(cmd_header)); - if (ret) { - ERR("Failed to append session destruction command header"); - goto error; - } - - if (!reply_context->implicit_rotation_on_destroy) { - DBG("No implicit rotation performed during the destruction of session \"%s\", sending reply", - session->name); - goto send_reply; - } - if (session->rotation_state != LTTNG_ROTATION_STATE_COMPLETED) { - DBG("Rotation state of session \"%s\" is not \"completed\", sending session destruction reply", - session->name); - goto send_reply; - } - - location = session_get_trace_archive_location(session); - if (!location) { - ERR("Failed to get the location of the trace archive produced during the destruction of session \"%s\"", - session->name); - goto error; - } - - payload_size_before_location = payload.size; - comm_ret = lttng_trace_archive_location_serialize(location, - &payload); - lttng_trace_archive_location_put(location); - if (comm_ret < 0) { - ERR("Failed to serialize the location of the trace archive produced during the destruction of session \"%s\"", - session->name); - goto error; - } - /* Update the message to indicate the location's length. */ - ((struct lttcomm_lttng_msg *) payload.data)->data_size = - payload.size - payload_size_before_location; -send_reply: - comm_ret = lttcomm_send_unix_sock(reply_context->reply_sock_fd, - payload.data, payload.size); - if (comm_ret != (ssize_t) payload.size) { - ERR("Failed to send result of the destruction of session \"%s\" to client", - session->name); - } -error: - ret = close(reply_context->reply_sock_fd); - if (ret) { - PERROR("Failed to close client socket in deferred session destroy reply"); - } - lttng_dynamic_buffer_reset(&payload); - free(_reply_context); -} - -/* - * Command LTTNG_DESTROY_SESSION processed by the client thread. - * - * Called with session lock held. - */ -int cmd_destroy_session(struct ltt_session *session, - struct notification_thread_handle *notification_thread_handle, - int *sock_fd) -{ - int ret; - enum lttng_error_code destruction_last_error = LTTNG_OK; - struct cmd_destroy_session_reply_context *reply_context = NULL; - - if (sock_fd) { - reply_context = zmalloc(sizeof(*reply_context)); - if (!reply_context) { - ret = LTTNG_ERR_NOMEM; - goto end; - } - reply_context->reply_sock_fd = *sock_fd; - } - - /* Safety net */ - LTTNG_ASSERT(session); - - DBG("Begin destroy session %s (id %" PRIu64 ")", session->name, - session->id); - if (session->active) { - DBG("Session \"%s\" is active, attempting to stop it before destroying it", - session->name); - ret = cmd_stop_trace(session); - if (ret != LTTNG_OK && ret != LTTNG_ERR_TRACE_ALREADY_STOPPED) { - /* Carry on with the destruction of the session. */ - ERR("Failed to stop session \"%s\" as part of its destruction: %s", - session->name, lttng_strerror(-ret)); - destruction_last_error = ret; - } - } - - if (session->rotation_schedule_timer_enabled) { - if (timer_session_rotation_schedule_timer_stop( - session)) { - ERR("Failed to stop the \"rotation schedule\" timer of session %s", - session->name); - destruction_last_error = LTTNG_ERR_TIMER_STOP_ERROR; - } - } - - if (session->rotate_size) { - unsubscribe_session_consumed_size_rotation(session, notification_thread_handle); - session->rotate_size = 0; - } - - if (session->rotated && session->current_trace_chunk && session->output_traces) { - /* - * Perform a last rotation on destruction if rotations have - * occurred during the session's lifetime. - */ - ret = cmd_rotate_session(session, NULL, false, - LTTNG_TRACE_CHUNK_COMMAND_TYPE_MOVE_TO_COMPLETED); - if (ret != LTTNG_OK) { - ERR("Failed to perform an implicit rotation as part of the destruction of session \"%s\": %s", - session->name, lttng_strerror(-ret)); - destruction_last_error = -ret; - } - if (reply_context) { - reply_context->implicit_rotation_on_destroy = true; - } - } else if (session->has_been_started && session->current_trace_chunk) { - /* - * The user has not triggered a session rotation. However, to - * ensure all data has been consumed, the session is rotated - * to a 'null' trace chunk before it is destroyed. - * - * This is a "quiet" rotation meaning that no notification is - * emitted and no renaming of the current trace chunk takes - * place. - */ - ret = cmd_rotate_session(session, NULL, true, - LTTNG_TRACE_CHUNK_COMMAND_TYPE_NO_OPERATION); - /* - * Rotation operations may not be supported by the kernel - * tracer. Hence, do not consider this implicit rotation as - * a session destruction error. The library has already stopped - * the session and waited for pending data; there is nothing - * left to do but complete the destruction of the session. - */ - if (ret != LTTNG_OK && - ret != -LTTNG_ERR_ROTATION_NOT_AVAILABLE_KERNEL) { - ERR("Failed to perform a quiet rotation as part of the destruction of session \"%s\": %s", - session->name, lttng_strerror(ret)); - destruction_last_error = -ret; - } - } - - if (session->shm_path[0]) { - /* - * When a session is created with an explicit shm_path, - * the consumer daemon will create its shared memory files - * at that location and will *not* unlink them. This is normal - * as the intention of that feature is to make it possible - * to retrieve the content of those files should a crash occur. - * - * To ensure the content of those files can be used, the - * sessiond daemon will replicate the content of the metadata - * cache in a metadata file. - * - * On clean-up, it is expected that the consumer daemon will - * unlink the shared memory files and that the session daemon - * will unlink the metadata file. Then, the session's directory - * in the shm path can be removed. - * - * Unfortunately, a flaw in the design of the sessiond's and - * consumerd's tear down of channels makes it impossible to - * determine when the sessiond _and_ the consumerd have both - * destroyed their representation of a channel. For one, the - * unlinking, close, and rmdir happen in deferred 'call_rcu' - * callbacks in both daemons. - * - * However, it is also impossible for the sessiond to know when - * the consumer daemon is done destroying its channel(s) since - * it occurs as a reaction to the closing of the channel's file - * descriptor. There is no resulting communication initiated - * from the consumerd to the sessiond to confirm that the - * operation is completed (and was successful). - * - * Until this is all fixed, the session daemon checks for the - * removal of the session's shm path which makes it possible - * to safely advertise a session as having been destroyed. - * - * Prior to this fix, it was not possible to reliably save - * a session making use of the --shm-path option, destroy it, - * and load it again. This is because the creation of the - * session would fail upon seeing the session's shm path - * already in existence. - * - * Note that none of the error paths in the check for the - * directory's existence return an error. This is normal - * as there isn't much that can be done. The session will - * be destroyed properly, except that we can't offer the - * guarantee that the same session can be re-created. - */ - current_completion_handler = &destroy_completion_handler.handler; - ret = lttng_strncpy(destroy_completion_handler.shm_path, - session->shm_path, - sizeof(destroy_completion_handler.shm_path)); - LTTNG_ASSERT(!ret); - } - - /* - * The session is destroyed. However, note that the command context - * still holds a reference to the session, thus delaying its destruction - * _at least_ up to the point when that reference is released. - */ - session_destroy(session); - if (reply_context) { - reply_context->destruction_status = destruction_last_error; - ret = session_add_destroy_notifier(session, - cmd_destroy_session_reply, - (void *) reply_context); - if (ret) { - ret = LTTNG_ERR_FATAL; - goto end; - } else { - *sock_fd = -1; - } - } - ret = LTTNG_OK; -end: - return ret; -} - -/* - * Command LTTNG_REGISTER_CONSUMER processed by the client thread. - */ -int cmd_register_consumer(struct ltt_session *session, - enum lttng_domain_type domain, const char *sock_path, - struct consumer_data *cdata) -{ - int ret, sock; - struct consumer_socket *socket = NULL; - - LTTNG_ASSERT(session); - LTTNG_ASSERT(cdata); - LTTNG_ASSERT(sock_path); - - switch (domain) { - case LTTNG_DOMAIN_KERNEL: - { - struct ltt_kernel_session *ksess = session->kernel_session; - - LTTNG_ASSERT(ksess); - - /* Can't register a consumer if there is already one */ - if (ksess->consumer_fds_sent != 0) { - ret = LTTNG_ERR_KERN_CONSUMER_FAIL; - goto error; - } - - sock = lttcomm_connect_unix_sock(sock_path); - if (sock < 0) { - ret = LTTNG_ERR_CONNECT_FAIL; - goto error; - } - cdata->cmd_sock = sock; - - socket = consumer_allocate_socket(&cdata->cmd_sock); - if (socket == NULL) { - ret = close(sock); - if (ret < 0) { - PERROR("close register consumer"); - } - cdata->cmd_sock = -1; - ret = LTTNG_ERR_FATAL; - goto error; - } - - socket->lock = zmalloc(sizeof(pthread_mutex_t)); - if (socket->lock == NULL) { - PERROR("zmalloc pthread mutex"); - ret = LTTNG_ERR_FATAL; - goto error; - } - pthread_mutex_init(socket->lock, NULL); - socket->registered = 1; - - rcu_read_lock(); - consumer_add_socket(socket, ksess->consumer); - rcu_read_unlock(); - - pthread_mutex_lock(&cdata->pid_mutex); - cdata->pid = -1; - pthread_mutex_unlock(&cdata->pid_mutex); - - break; - } - default: - /* TODO: Userspace tracing */ - ret = LTTNG_ERR_UND; - goto error; - } - - return LTTNG_OK; - -error: - if (socket) { - consumer_destroy_socket(socket); - } - return ret; -} - -/* - * Command LTTNG_LIST_DOMAINS processed by the client thread. - */ -ssize_t cmd_list_domains(struct ltt_session *session, - struct lttng_domain **domains) -{ - int ret, index = 0; - ssize_t nb_dom = 0; - struct agent *agt; - struct lttng_ht_iter iter; - - if (session->kernel_session != NULL) { - DBG3("Listing domains found kernel domain"); - nb_dom++; - } - - if (session->ust_session != NULL) { - DBG3("Listing domains found UST global domain"); - nb_dom++; - - rcu_read_lock(); - cds_lfht_for_each_entry(session->ust_session->agents->ht, &iter.iter, - agt, node.node) { - if (agt->being_used) { - nb_dom++; - } - } - rcu_read_unlock(); - } - - if (!nb_dom) { - goto end; - } - - *domains = zmalloc(nb_dom * sizeof(struct lttng_domain)); - if (*domains == NULL) { - ret = LTTNG_ERR_FATAL; - goto error; - } - - if (session->kernel_session != NULL) { - (*domains)[index].type = LTTNG_DOMAIN_KERNEL; - - /* Kernel session buffer type is always GLOBAL */ - (*domains)[index].buf_type = LTTNG_BUFFER_GLOBAL; - - index++; - } - - if (session->ust_session != NULL) { - (*domains)[index].type = LTTNG_DOMAIN_UST; - (*domains)[index].buf_type = session->ust_session->buffer_type; - index++; - - rcu_read_lock(); - cds_lfht_for_each_entry(session->ust_session->agents->ht, &iter.iter, - agt, node.node) { - if (agt->being_used) { - (*domains)[index].type = agt->domain; - (*domains)[index].buf_type = session->ust_session->buffer_type; - index++; - } - } - rcu_read_unlock(); - } -end: - return nb_dom; - -error: - /* Return negative value to differentiate return code */ - return -ret; -} - - -/* - * Command LTTNG_LIST_CHANNELS processed by the client thread. - */ -ssize_t cmd_list_channels(enum lttng_domain_type domain, - struct ltt_session *session, struct lttng_channel **channels) -{ - ssize_t nb_chan = 0, payload_size = 0, ret; - - switch (domain) { - case LTTNG_DOMAIN_KERNEL: - if (session->kernel_session != NULL) { - nb_chan = session->kernel_session->channel_count; - } - DBG3("Number of kernel channels %zd", nb_chan); - if (nb_chan <= 0) { - ret = -LTTNG_ERR_KERN_CHAN_NOT_FOUND; - goto end; - } - break; - case LTTNG_DOMAIN_UST: - if (session->ust_session != NULL) { - rcu_read_lock(); - nb_chan = lttng_ht_get_count( - session->ust_session->domain_global.channels); - rcu_read_unlock(); - } - DBG3("Number of UST global channels %zd", nb_chan); - if (nb_chan < 0) { - ret = -LTTNG_ERR_UST_CHAN_NOT_FOUND; - goto end; - } - break; - default: - ret = -LTTNG_ERR_UND; - goto end; - } - - if (nb_chan > 0) { - const size_t channel_size = sizeof(struct lttng_channel) + - sizeof(struct lttng_channel_extended); - struct lttng_channel_extended *channel_exts; - - payload_size = nb_chan * channel_size; - *channels = zmalloc(payload_size); - if (*channels == NULL) { - ret = -LTTNG_ERR_FATAL; - goto end; - } - - channel_exts = ((void *) *channels) + - (nb_chan * sizeof(struct lttng_channel)); - ret = list_lttng_channels(domain, session, *channels, channel_exts); - if (ret != LTTNG_OK) { - free(*channels); - *channels = NULL; - goto end; - } - } else { - *channels = NULL; - } - - ret = payload_size; -end: - return ret; -} - -/* - * Command LTTNG_LIST_EVENTS processed by the client thread. - */ -ssize_t cmd_list_events(enum lttng_domain_type domain, - struct ltt_session *session, char *channel_name, - struct lttng_payload *payload) -{ - int ret = 0; - ssize_t nb_events = 0; - struct lttcomm_event_command_header cmd_header = {}; - const size_t cmd_header_offset = payload->buffer.size; - - ret = lttng_dynamic_buffer_append( - &payload->buffer, &cmd_header, sizeof(cmd_header)); - if (ret) { - ret = LTTNG_ERR_NOMEM; - goto error; - } - - switch (domain) { - case LTTNG_DOMAIN_KERNEL: - if (session->kernel_session != NULL) { - nb_events = list_lttng_kernel_events(channel_name, - session->kernel_session, payload); - } - break; - case LTTNG_DOMAIN_UST: - { - if (session->ust_session != NULL) { - nb_events = list_lttng_ust_global_events(channel_name, - &session->ust_session->domain_global, - payload); - } - break; - } - case LTTNG_DOMAIN_LOG4J: - case LTTNG_DOMAIN_JUL: - case LTTNG_DOMAIN_PYTHON: - if (session->ust_session) { - struct lttng_ht_iter iter; - struct agent *agt; - - rcu_read_lock(); - cds_lfht_for_each_entry(session->ust_session->agents->ht, - &iter.iter, agt, node.node) { - if (agt->domain == domain) { - nb_events = list_lttng_agent_events( - agt, payload); - break; - } - } - rcu_read_unlock(); - } - break; - default: - ret = LTTNG_ERR_UND; - goto error; - } - - ((struct lttcomm_event_command_header *) (payload->buffer.data + - cmd_header_offset))->nb_events = (uint32_t) nb_events; - - return nb_events; - -error: - /* Return negative value to differentiate return code */ - return -ret; -} - -/* - * Using the session list, filled a lttng_session array to send back to the - * client for session listing. - * - * The session list lock MUST be acquired before calling this function. Use - * session_lock_list() and session_unlock_list(). - */ -void cmd_list_lttng_sessions(struct lttng_session *sessions, - size_t session_count, uid_t uid, gid_t gid) -{ - int ret; - unsigned int i = 0; - struct ltt_session *session; - struct ltt_session_list *list = session_get_list(); - struct lttng_session_extended *extended = - (typeof(extended)) (&sessions[session_count]); - - DBG("Getting all available session for UID %d GID %d", - uid, gid); - /* - * Iterate over session list and append data after the control struct in - * the buffer. - */ - cds_list_for_each_entry(session, &list->head, list) { - if (!session_get(session)) { - continue; - } - /* - * Only list the sessions the user can control. - */ - if (!session_access_ok(session, uid) || - session->destroyed) { - session_put(session); - continue; - } - - struct ltt_kernel_session *ksess = session->kernel_session; - struct ltt_ust_session *usess = session->ust_session; - - if (session->consumer->type == CONSUMER_DST_NET || - (ksess && ksess->consumer->type == CONSUMER_DST_NET) || - (usess && usess->consumer->type == CONSUMER_DST_NET)) { - ret = build_network_session_path(sessions[i].path, - sizeof(sessions[i].path), session); - } else { - ret = snprintf(sessions[i].path, sizeof(sessions[i].path), "%s", - session->consumer->dst.session_root_path); - } - if (ret < 0) { - PERROR("snprintf session path"); - session_put(session); - continue; - } - - strncpy(sessions[i].name, session->name, NAME_MAX); - sessions[i].name[NAME_MAX - 1] = '\0'; - sessions[i].enabled = session->active; - sessions[i].snapshot_mode = session->snapshot_mode; - sessions[i].live_timer_interval = session->live_timer; - extended[i].creation_time.value = (uint64_t) session->creation_time; - extended[i].creation_time.is_set = 1; - i++; - session_put(session); - } -} - -/* - * Command LTTNG_DATA_PENDING returning 0 if the data is NOT pending meaning - * ready for trace analysis (or any kind of reader) or else 1 for pending data. - */ -int cmd_data_pending(struct ltt_session *session) -{ - int ret; - struct ltt_kernel_session *ksess = session->kernel_session; - struct ltt_ust_session *usess = session->ust_session; - - LTTNG_ASSERT(session); - - DBG("Data pending for session %s", session->name); - - /* Session MUST be stopped to ask for data availability. */ - if (session->active) { - ret = LTTNG_ERR_SESSION_STARTED; - goto error; - } else { - /* - * If stopped, just make sure we've started before else the above call - * will always send that there is data pending. - * - * The consumer assumes that when the data pending command is received, - * the trace has been started before or else no output data is written - * by the streams which is a condition for data pending. So, this is - * *VERY* important that we don't ask the consumer before a start - * trace. - */ - if (!session->has_been_started) { - ret = 0; - goto error; - } - } - - /* A rotation is still pending, we have to wait. */ - if (session->rotation_state == LTTNG_ROTATION_STATE_ONGOING) { - DBG("Rotate still pending for session %s", session->name); - ret = 1; - goto error; - } - - if (ksess && ksess->consumer) { - ret = consumer_is_data_pending(ksess->id, ksess->consumer); - if (ret == 1) { - /* Data is still being extracted for the kernel. */ - goto error; - } - } - - if (usess && usess->consumer) { - ret = consumer_is_data_pending(usess->id, usess->consumer); - if (ret == 1) { - /* Data is still being extracted for the kernel. */ - goto error; - } - } - - /* Data is ready to be read by a viewer */ - ret = 0; - -error: - return ret; -} - -/* - * Command LTTNG_SNAPSHOT_ADD_OUTPUT from the lttng ctl library. - * - * Return LTTNG_OK on success or else a LTTNG_ERR code. - */ -int cmd_snapshot_add_output(struct ltt_session *session, - const struct lttng_snapshot_output *output, uint32_t *id) -{ - int ret; - struct snapshot_output *new_output; - - LTTNG_ASSERT(session); - LTTNG_ASSERT(output); - - DBG("Cmd snapshot add output for session %s", session->name); - - /* - * Can't create an output if the session is not set in no-output mode. - */ - if (session->output_traces) { - ret = LTTNG_ERR_NOT_SNAPSHOT_SESSION; - goto error; - } - - if (session->has_non_mmap_channel) { - ret = LTTNG_ERR_SNAPSHOT_UNSUPPORTED; - goto error; - } - - /* Only one output is allowed until we have the "tee" feature. */ - if (session->snapshot.nb_output == 1) { - ret = LTTNG_ERR_SNAPSHOT_OUTPUT_EXIST; - goto error; - } - - new_output = snapshot_output_alloc(); - if (!new_output) { - ret = LTTNG_ERR_NOMEM; - goto error; - } - - ret = snapshot_output_init(session, output->max_size, output->name, - output->ctrl_url, output->data_url, session->consumer, new_output, - &session->snapshot); - if (ret < 0) { - if (ret == -ENOMEM) { - ret = LTTNG_ERR_NOMEM; - } else { - ret = LTTNG_ERR_INVALID; - } - goto free_error; - } - - rcu_read_lock(); - snapshot_add_output(&session->snapshot, new_output); - if (id) { - *id = new_output->id; - } - rcu_read_unlock(); - - return LTTNG_OK; - -free_error: - snapshot_output_destroy(new_output); -error: - return ret; -} - -/* - * Command LTTNG_SNAPSHOT_DEL_OUTPUT from lib lttng ctl. - * - * Return LTTNG_OK on success or else a LTTNG_ERR code. - */ -int cmd_snapshot_del_output(struct ltt_session *session, - const struct lttng_snapshot_output *output) -{ - int ret; - struct snapshot_output *sout = NULL; - - LTTNG_ASSERT(session); - LTTNG_ASSERT(output); - - rcu_read_lock(); - - /* - * Permission denied to create an output if the session is not - * set in no output mode. - */ - if (session->output_traces) { - ret = LTTNG_ERR_NOT_SNAPSHOT_SESSION; - goto error; - } - - if (output->id) { - DBG("Cmd snapshot del output id %" PRIu32 " for session %s", output->id, - session->name); - sout = snapshot_find_output_by_id(output->id, &session->snapshot); - } else if (*output->name != '\0') { - DBG("Cmd snapshot del output name %s for session %s", output->name, - session->name); - sout = snapshot_find_output_by_name(output->name, &session->snapshot); - } - if (!sout) { - ret = LTTNG_ERR_INVALID; - goto error; - } - - snapshot_delete_output(&session->snapshot, sout); - snapshot_output_destroy(sout); - ret = LTTNG_OK; - -error: - rcu_read_unlock(); - return ret; -} - -/* - * Command LTTNG_SNAPSHOT_LIST_OUTPUT from lib lttng ctl. - * - * If no output is available, outputs is untouched and 0 is returned. - * - * Return the size of the newly allocated outputs or a negative LTTNG_ERR code. - */ -ssize_t cmd_snapshot_list_outputs(struct ltt_session *session, - struct lttng_snapshot_output **outputs) -{ - int ret, idx = 0; - struct lttng_snapshot_output *list = NULL; - struct lttng_ht_iter iter; - struct snapshot_output *output; - - LTTNG_ASSERT(session); - LTTNG_ASSERT(outputs); - - DBG("Cmd snapshot list outputs for session %s", session->name); - - /* - * Permission denied to create an output if the session is not - * set in no output mode. - */ - if (session->output_traces) { - ret = -LTTNG_ERR_NOT_SNAPSHOT_SESSION; - goto end; - } - - if (session->snapshot.nb_output == 0) { - ret = 0; - goto end; - } - - list = zmalloc(session->snapshot.nb_output * sizeof(*list)); - if (!list) { - ret = -LTTNG_ERR_NOMEM; - goto end; - } - - /* Copy list from session to the new list object. */ - rcu_read_lock(); - cds_lfht_for_each_entry(session->snapshot.output_ht->ht, &iter.iter, - output, node.node) { - LTTNG_ASSERT(output->consumer); - list[idx].id = output->id; - list[idx].max_size = output->max_size; - if (lttng_strncpy(list[idx].name, output->name, - sizeof(list[idx].name))) { - ret = -LTTNG_ERR_INVALID; - goto error; - } - if (output->consumer->type == CONSUMER_DST_LOCAL) { - if (lttng_strncpy(list[idx].ctrl_url, - output->consumer->dst.session_root_path, - sizeof(list[idx].ctrl_url))) { - ret = -LTTNG_ERR_INVALID; - goto error; - } - } else { - /* Control URI. */ - ret = uri_to_str_url(&output->consumer->dst.net.control, - list[idx].ctrl_url, sizeof(list[idx].ctrl_url)); - if (ret < 0) { - ret = -LTTNG_ERR_NOMEM; - goto error; - } - - /* Data URI. */ - ret = uri_to_str_url(&output->consumer->dst.net.data, - list[idx].data_url, sizeof(list[idx].data_url)); - if (ret < 0) { - ret = -LTTNG_ERR_NOMEM; - goto error; - } - } - idx++; - } - - *outputs = list; - list = NULL; - ret = session->snapshot.nb_output; -error: - rcu_read_unlock(); - free(list); -end: - return ret; -} - -/* - * Check if we can regenerate the metadata for this session. - * Only kernel, UST per-uid and non-live sessions are supported. - * - * Return 0 if the metadata can be generated, a LTTNG_ERR code otherwise. - */ -static -int check_regenerate_metadata_support(struct ltt_session *session) -{ - int ret; - - LTTNG_ASSERT(session); - - if (session->live_timer != 0) { - ret = LTTNG_ERR_LIVE_SESSION; - goto end; - } - if (!session->active) { - ret = LTTNG_ERR_SESSION_NOT_STARTED; - goto end; - } - if (session->ust_session) { - switch (session->ust_session->buffer_type) { - case LTTNG_BUFFER_PER_UID: - break; - case LTTNG_BUFFER_PER_PID: - ret = LTTNG_ERR_PER_PID_SESSION; - goto end; - default: - abort(); - ret = LTTNG_ERR_UNK; - goto end; - } - } - if (session->consumer->type == CONSUMER_DST_NET && - session->consumer->relay_minor_version < 8) { - ret = LTTNG_ERR_RELAYD_VERSION_FAIL; - goto end; - } - ret = 0; - -end: - return ret; -} - -static -int clear_metadata_file(int fd) -{ - int ret; - off_t lseek_ret; - - lseek_ret = lseek(fd, 0, SEEK_SET); - if (lseek_ret < 0) { - PERROR("lseek"); - ret = -1; - goto end; - } - - ret = ftruncate(fd, 0); - if (ret < 0) { - PERROR("ftruncate"); - goto end; - } - -end: - return ret; -} - -static -int ust_regenerate_metadata(struct ltt_ust_session *usess) -{ - int ret = 0; - struct buffer_reg_uid *uid_reg = NULL; - struct buffer_reg_session *session_reg = NULL; - - rcu_read_lock(); - cds_list_for_each_entry(uid_reg, &usess->buffer_reg_uid_list, lnode) { - struct ust_registry_session *registry; - struct ust_registry_channel *chan; - struct lttng_ht_iter iter_chan; - - session_reg = uid_reg->registry; - registry = session_reg->reg.ust; - - pthread_mutex_lock(®istry->lock); - registry->metadata_len_sent = 0; - memset(registry->metadata, 0, registry->metadata_alloc_len); - registry->metadata_len = 0; - registry->metadata_version++; - if (registry->metadata_fd > 0) { - /* Clear the metadata file's content. */ - ret = clear_metadata_file(registry->metadata_fd); - if (ret) { - pthread_mutex_unlock(®istry->lock); - goto end; - } - } - - ret = ust_metadata_session_statedump(registry, NULL, - registry->major, registry->minor); - if (ret) { - pthread_mutex_unlock(®istry->lock); - ERR("Failed to generate session metadata (err = %d)", - ret); - goto end; - } - cds_lfht_for_each_entry(registry->channels->ht, &iter_chan.iter, - chan, node.node) { - struct ust_registry_event *event; - struct lttng_ht_iter iter_event; - - ret = ust_metadata_channel_statedump(registry, chan); - if (ret) { - pthread_mutex_unlock(®istry->lock); - ERR("Failed to generate channel metadata " - "(err = %d)", ret); - goto end; - } - cds_lfht_for_each_entry(chan->ht->ht, &iter_event.iter, - event, node.node) { - ret = ust_metadata_event_statedump(registry, - chan, event); - if (ret) { - pthread_mutex_unlock(®istry->lock); - ERR("Failed to generate event metadata " - "(err = %d)", ret); - goto end; - } - } - } - pthread_mutex_unlock(®istry->lock); - } - -end: - rcu_read_unlock(); - return ret; -} - -/* - * Command LTTNG_REGENERATE_METADATA from the lttng-ctl library. - * - * Ask the consumer to truncate the existing metadata file(s) and - * then regenerate the metadata. Live and per-pid sessions are not - * supported and return an error. - * - * Return LTTNG_OK on success or else a LTTNG_ERR code. - */ -int cmd_regenerate_metadata(struct ltt_session *session) -{ - int ret; - - LTTNG_ASSERT(session); - - ret = check_regenerate_metadata_support(session); - if (ret) { - goto end; - } - - if (session->kernel_session) { - ret = kernctl_session_regenerate_metadata( - session->kernel_session->fd); - if (ret < 0) { - ERR("Failed to regenerate the kernel metadata"); - goto end; - } - } - - if (session->ust_session) { - ret = ust_regenerate_metadata(session->ust_session); - if (ret < 0) { - ERR("Failed to regenerate the UST metadata"); - goto end; - } - } - DBG("Cmd metadata regenerate for session %s", session->name); - ret = LTTNG_OK; - -end: - return ret; -} - -/* - * Command LTTNG_REGENERATE_STATEDUMP from the lttng-ctl library. - * - * Ask the tracer to regenerate a new statedump. - * - * Return LTTNG_OK on success or else a LTTNG_ERR code. - */ -int cmd_regenerate_statedump(struct ltt_session *session) -{ - int ret; - - LTTNG_ASSERT(session); - - if (!session->active) { - ret = LTTNG_ERR_SESSION_NOT_STARTED; - goto end; - } - - if (session->kernel_session) { - ret = kernctl_session_regenerate_statedump( - session->kernel_session->fd); - /* - * Currently, the statedump in kernel can only fail if out - * of memory. - */ - if (ret < 0) { - if (ret == -ENOMEM) { - ret = LTTNG_ERR_REGEN_STATEDUMP_NOMEM; - } else { - ret = LTTNG_ERR_REGEN_STATEDUMP_FAIL; - } - ERR("Failed to regenerate the kernel statedump"); - goto end; - } - } - - if (session->ust_session) { - ret = ust_app_regenerate_statedump_all(session->ust_session); - /* - * Currently, the statedump in UST always returns 0. - */ - if (ret < 0) { - ret = LTTNG_ERR_REGEN_STATEDUMP_FAIL; - ERR("Failed to regenerate the UST statedump"); - goto end; - } - } - DBG("Cmd regenerate statedump for session %s", session->name); - ret = LTTNG_OK; - -end: - return ret; -} - -static -enum lttng_error_code synchronize_tracer_notifier_register( - struct notification_thread_handle *notification_thread, - struct lttng_trigger *trigger, const struct lttng_credentials *cmd_creds) -{ - enum lttng_error_code ret_code; - const struct lttng_condition *condition = - lttng_trigger_get_const_condition(trigger); - const char *trigger_name; - uid_t trigger_owner; - enum lttng_trigger_status trigger_status; - const enum lttng_domain_type trigger_domain = - lttng_trigger_get_underlying_domain_type_restriction( - trigger); - - trigger_status = lttng_trigger_get_owner_uid(trigger, &trigger_owner); - LTTNG_ASSERT(trigger_status == LTTNG_TRIGGER_STATUS_OK); - - LTTNG_ASSERT(condition); - LTTNG_ASSERT(lttng_condition_get_type(condition) == - LTTNG_CONDITION_TYPE_EVENT_RULE_MATCHES); - - trigger_status = lttng_trigger_get_name(trigger, &trigger_name); - trigger_name = trigger_status == LTTNG_TRIGGER_STATUS_OK ? - trigger_name : "(anonymous)"; - - session_lock_list(); - switch (trigger_domain) { - case LTTNG_DOMAIN_KERNEL: - { - ret_code = kernel_register_event_notifier(trigger, cmd_creds); - if (ret_code != LTTNG_OK) { - enum lttng_error_code notif_thread_unregister_ret; - - notif_thread_unregister_ret = - notification_thread_command_unregister_trigger( - notification_thread, trigger); - - if (notif_thread_unregister_ret != LTTNG_OK) { - /* Return the original error code. */ - ERR("Failed to unregister trigger from notification thread during error recovery: trigger name = '%s', trigger owner uid = %d, error code = %d", - trigger_name, - (int) trigger_owner, - ret_code); - } - } - break; - } - case LTTNG_DOMAIN_UST: - ust_app_global_update_all_event_notifier_rules(); - break; - case LTTNG_DOMAIN_JUL: - case LTTNG_DOMAIN_LOG4J: - case LTTNG_DOMAIN_PYTHON: - { - /* Agent domains. */ - struct agent *agt = agent_find_by_event_notifier_domain( - trigger_domain); - - if (!agt) { - agt = agent_create(trigger_domain); - if (!agt) { - ret_code = LTTNG_ERR_NOMEM; - goto end_unlock_session_list; - } - - agent_add(agt, the_trigger_agents_ht_by_domain); - } - - ret_code = trigger_agent_enable(trigger, agt); - if (ret_code != LTTNG_OK) { - goto end_unlock_session_list; - } - - break; - } - case LTTNG_DOMAIN_NONE: - default: - abort(); - } - - ret_code = LTTNG_OK; -end_unlock_session_list: - session_unlock_list(); - return ret_code; -} - -enum lttng_error_code cmd_register_trigger(const struct lttng_credentials *cmd_creds, - struct lttng_trigger *trigger, - bool is_trigger_anonymous, - struct notification_thread_handle *notification_thread, - struct lttng_trigger **return_trigger) -{ - enum lttng_error_code ret_code; - const char *trigger_name; - uid_t trigger_owner; - enum lttng_trigger_status trigger_status; - - trigger_status = lttng_trigger_get_name(trigger, &trigger_name); - trigger_name = trigger_status == LTTNG_TRIGGER_STATUS_OK ? - trigger_name : "(anonymous)"; - - trigger_status = lttng_trigger_get_owner_uid( - trigger, &trigger_owner); - LTTNG_ASSERT(trigger_status == LTTNG_TRIGGER_STATUS_OK); - - DBG("Running register trigger command: trigger name = '%s', trigger owner uid = %d, command creds uid = %d", - trigger_name, (int) trigger_owner, - (int) lttng_credentials_get_uid(cmd_creds)); - - /* - * Validate the trigger credentials against the command credentials. - * Only the root user can register a trigger with non-matching - * credentials. - */ - if (!lttng_credentials_is_equal_uid( - lttng_trigger_get_credentials(trigger), - cmd_creds)) { - if (lttng_credentials_get_uid(cmd_creds) != 0) { - ERR("Trigger credentials do not match the command credentials: trigger name = '%s', trigger owner uid = %d, command creds uid = %d", - trigger_name, (int) trigger_owner, - (int) lttng_credentials_get_uid(cmd_creds)); - ret_code = LTTNG_ERR_INVALID_TRIGGER; - goto end; - } - } - - /* - * The bytecode generation also serves as a validation step for the - * bytecode expressions. - */ - ret_code = lttng_trigger_generate_bytecode(trigger, cmd_creds); - if (ret_code != LTTNG_OK) { - ERR("Failed to generate bytecode of trigger: trigger name = '%s', trigger owner uid = %d, error code = %d", - trigger_name, (int) trigger_owner, ret_code); - goto end; - } - - /* - * A reference to the trigger is acquired by the notification thread. - * It is safe to return the same trigger to the caller since it the - * other user holds a reference. - * - * The trigger is modified during the execution of the - * "register trigger" command. However, by the time the command returns, - * it is safe to use without any locking as its properties are - * immutable. - */ - ret_code = notification_thread_command_register_trigger( - notification_thread, trigger, is_trigger_anonymous); - if (ret_code != LTTNG_OK) { - DBG("Failed to register trigger to notification thread: trigger name = '%s', trigger owner uid = %d, error code = %d", - trigger_name, (int) trigger_owner, ret_code); - goto end; - } - - trigger_status = lttng_trigger_get_name(trigger, &trigger_name); - trigger_name = trigger_status == LTTNG_TRIGGER_STATUS_OK ? - trigger_name : "(anonymous)"; - - /* - * Synchronize tracers if the trigger adds an event notifier. - */ - if (lttng_trigger_needs_tracer_notifier(trigger)) { - ret_code = synchronize_tracer_notifier_register(notification_thread, - trigger, cmd_creds); - if (ret_code != LTTNG_OK) { - ERR("Error registering tracer notifier: %s", - lttng_strerror(-ret_code)); - goto end; - } - } - - /* - * Return an updated trigger to the client. - * - * Since a modified version of the same trigger is returned, acquire a - * reference to the trigger so the caller doesn't have to care if those - * are distinct instances or not. - */ - if (ret_code == LTTNG_OK) { - lttng_trigger_get(trigger); - *return_trigger = trigger; - /* Ownership of trigger was transferred to caller. */ - trigger = NULL; - } -end: - return ret_code; -} - -static -enum lttng_error_code synchronize_tracer_notifier_unregister( - const struct lttng_trigger *trigger) -{ - enum lttng_error_code ret_code; - const struct lttng_condition *condition = - lttng_trigger_get_const_condition(trigger); - const enum lttng_domain_type trigger_domain = - lttng_trigger_get_underlying_domain_type_restriction( - trigger); - - LTTNG_ASSERT(condition); - LTTNG_ASSERT(lttng_condition_get_type(condition) == - LTTNG_CONDITION_TYPE_EVENT_RULE_MATCHES); - - session_lock_list(); - switch (trigger_domain) { - case LTTNG_DOMAIN_KERNEL: - ret_code = kernel_unregister_event_notifier(trigger); - if (ret_code != LTTNG_OK) { - goto end_unlock_session_list; - } - - break; - case LTTNG_DOMAIN_UST: - ust_app_global_update_all_event_notifier_rules(); - break; - case LTTNG_DOMAIN_JUL: - case LTTNG_DOMAIN_LOG4J: - case LTTNG_DOMAIN_PYTHON: - { - /* Agent domains. */ - struct agent *agt = agent_find_by_event_notifier_domain( - trigger_domain); - - /* - * This trigger was never registered in the first place. Calling - * this function under those circumstances is an internal error. - */ - LTTNG_ASSERT(agt); - ret_code = trigger_agent_disable(trigger, agt); - if (ret_code != LTTNG_OK) { - goto end_unlock_session_list; - } - - break; - } - case LTTNG_DOMAIN_NONE: - default: - abort(); - } - - ret_code = LTTNG_OK; - -end_unlock_session_list: - session_unlock_list(); - return ret_code; -} - -enum lttng_error_code cmd_unregister_trigger(const struct lttng_credentials *cmd_creds, - const struct lttng_trigger *trigger, - struct notification_thread_handle *notification_thread) -{ - enum lttng_error_code ret_code; - const char *trigger_name; - uid_t trigger_owner; - enum lttng_trigger_status trigger_status; - struct lttng_trigger *sessiond_trigger = NULL; - - trigger_status = lttng_trigger_get_name(trigger, &trigger_name); - trigger_name = trigger_status == LTTNG_TRIGGER_STATUS_OK ? trigger_name : "(anonymous)"; - trigger_status = lttng_trigger_get_owner_uid(trigger, &trigger_owner); - LTTNG_ASSERT(trigger_status == LTTNG_TRIGGER_STATUS_OK); - - DBG("Running unregister trigger command: trigger name = '%s', trigger owner uid = %d, command creds uid = %d", - trigger_name, (int) trigger_owner, - (int) lttng_credentials_get_uid(cmd_creds)); - - /* - * Validate the trigger credentials against the command credentials. - * Only the root user can unregister a trigger with non-matching - * credentials. - */ - if (!lttng_credentials_is_equal_uid( - lttng_trigger_get_credentials(trigger), - cmd_creds)) { - if (lttng_credentials_get_uid(cmd_creds) != 0) { - ERR("Trigger credentials do not match the command credentials: trigger name = '%s', trigger owner uid = %d, command creds uid = %d", - trigger_name, (int) trigger_owner, - (int) lttng_credentials_get_uid(cmd_creds)); - ret_code = LTTNG_ERR_INVALID_TRIGGER; - goto end; - } - } - - /* Fetch the sessiond side trigger object. */ - ret_code = notification_thread_command_get_trigger( - notification_thread, trigger, &sessiond_trigger); - if (ret_code != LTTNG_OK) { - DBG("Failed to get trigger from notification thread during unregister: trigger name = '%s', trigger owner uid = %d, error code = %d", - trigger_name, (int) trigger_owner, ret_code); - goto end; - } - - LTTNG_ASSERT(sessiond_trigger); - - /* - * From this point on, no matter what, consider the trigger - * unregistered. - * - * We set the unregistered state of the sessiond side trigger object in - * the client thread since we want to minimize the possibility of the - * notification thread being stalled due to a long execution of an - * action that required the trigger lock. - */ - lttng_trigger_set_as_unregistered(sessiond_trigger); - - ret_code = notification_thread_command_unregister_trigger(notification_thread, - trigger); - if (ret_code != LTTNG_OK) { - DBG("Failed to unregister trigger from notification thread: trigger name = '%s', trigger owner uid = %d, error code = %d", - trigger_name, (int) trigger_owner, ret_code); - goto end; - } - - /* - * Synchronize tracers if the trigger removes an event notifier. - * Do this even if the trigger unregistration failed to at least stop - * the tracers from producing notifications associated with this - * event notifier. - */ - if (lttng_trigger_needs_tracer_notifier(trigger)) { - ret_code = synchronize_tracer_notifier_unregister(trigger); - if (ret_code != LTTNG_OK) { - ERR("Error unregistering trigger to tracer."); - goto end; - } - - } - -end: - lttng_trigger_put(sessiond_trigger); - return ret_code; -} - -enum lttng_error_code cmd_list_triggers(struct command_ctx *cmd_ctx, - struct notification_thread_handle *notification_thread, - struct lttng_triggers **return_triggers) -{ - int ret; - enum lttng_error_code ret_code; - struct lttng_triggers *triggers = NULL; - - /* Get the set of triggers from the notification thread. */ - ret_code = notification_thread_command_list_triggers( - notification_thread, cmd_ctx->creds.uid, &triggers); - if (ret_code != LTTNG_OK) { - goto end; - } - - ret = lttng_triggers_remove_hidden_triggers(triggers); - if (ret) { - ret_code = LTTNG_ERR_UNK; - goto end; - } - - *return_triggers = triggers; - triggers = NULL; - ret_code = LTTNG_OK; -end: - lttng_triggers_destroy(triggers); - return ret_code; -} - -enum lttng_error_code cmd_execute_error_query(const struct lttng_credentials *cmd_creds, - const struct lttng_error_query *query, - struct lttng_error_query_results **_results, - struct notification_thread_handle *notification_thread) -{ - enum lttng_error_code ret_code; - const struct lttng_trigger *query_target_trigger; - const struct lttng_action *query_target_action = NULL; - struct lttng_trigger *matching_trigger = NULL; - const char *trigger_name; - uid_t trigger_owner; - enum lttng_trigger_status trigger_status; - struct lttng_error_query_results *results = NULL; - - switch (lttng_error_query_get_target_type(query)) { - case LTTNG_ERROR_QUERY_TARGET_TYPE_TRIGGER: - query_target_trigger = lttng_error_query_trigger_borrow_target(query); - break; - case LTTNG_ERROR_QUERY_TARGET_TYPE_CONDITION: - query_target_trigger = - lttng_error_query_condition_borrow_target(query); - break; - case LTTNG_ERROR_QUERY_TARGET_TYPE_ACTION: - query_target_trigger = lttng_error_query_action_borrow_trigger_target( - query); - break; - default: - abort(); - } - - LTTNG_ASSERT(query_target_trigger); - - ret_code = notification_thread_command_get_trigger(notification_thread, - query_target_trigger, &matching_trigger); - if (ret_code != LTTNG_OK) { - goto end; - } - - /* No longer needed. */ - query_target_trigger = NULL; - - if (lttng_error_query_get_target_type(query) == - LTTNG_ERROR_QUERY_TARGET_TYPE_ACTION) { - /* Get the sessiond-side version of the target action. */ - query_target_action = - lttng_error_query_action_borrow_action_target( - query, matching_trigger); - } - - trigger_status = lttng_trigger_get_name(matching_trigger, &trigger_name); - trigger_name = trigger_status == LTTNG_TRIGGER_STATUS_OK ? - trigger_name : "(anonymous)"; - trigger_status = lttng_trigger_get_owner_uid(matching_trigger, - &trigger_owner); - LTTNG_ASSERT(trigger_status == LTTNG_TRIGGER_STATUS_OK); - - results = lttng_error_query_results_create(); - if (!results) { - ret_code = LTTNG_ERR_NOMEM; - goto end; - } - - DBG("Running \"execute error query\" command: trigger name = '%s', trigger owner uid = %d, command creds uid = %d", - trigger_name, (int) trigger_owner, - (int) lttng_credentials_get_uid(cmd_creds)); - - /* - * Validate the trigger credentials against the command credentials. - * Only the root user can target a trigger with non-matching - * credentials. - */ - if (!lttng_credentials_is_equal_uid( - lttng_trigger_get_credentials(matching_trigger), - cmd_creds)) { - if (lttng_credentials_get_uid(cmd_creds) != 0) { - ERR("Trigger credentials do not match the command credentials: trigger name = '%s', trigger owner uid = %d, command creds uid = %d", - trigger_name, (int) trigger_owner, - (int) lttng_credentials_get_uid(cmd_creds)); - ret_code = LTTNG_ERR_INVALID_TRIGGER; - goto end; - } - } - - switch (lttng_error_query_get_target_type(query)) { - case LTTNG_ERROR_QUERY_TARGET_TYPE_TRIGGER: - trigger_status = lttng_trigger_add_error_results( - matching_trigger, results); - - switch (trigger_status) { - case LTTNG_TRIGGER_STATUS_OK: - break; - default: - ret_code = LTTNG_ERR_UNK; - goto end; - } - - break; - case LTTNG_ERROR_QUERY_TARGET_TYPE_CONDITION: - { - trigger_status = lttng_trigger_condition_add_error_results( - matching_trigger, results); - - switch (trigger_status) { - case LTTNG_TRIGGER_STATUS_OK: - break; - default: - ret_code = LTTNG_ERR_UNK; - goto end; - } - - break; - } - case LTTNG_ERROR_QUERY_TARGET_TYPE_ACTION: - { - const enum lttng_action_status action_status = - lttng_action_add_error_query_results( - query_target_action, results); - - switch (action_status) { - case LTTNG_ACTION_STATUS_OK: - break; - default: - ret_code = LTTNG_ERR_UNK; - goto end; - } - - break; - } - default: - abort(); - break; - } - - *_results = results; - results = NULL; - ret_code = LTTNG_OK; -end: - lttng_trigger_put(matching_trigger); - lttng_error_query_results_destroy(results); - return ret_code; -} - -/* - * Send relayd sockets from snapshot output to consumer. Ignore request if the - * snapshot output is *not* set with a remote destination. - * - * Return LTTNG_OK on success or a LTTNG_ERR code. - */ -static enum lttng_error_code set_relayd_for_snapshot( - struct consumer_output *output, - const struct ltt_session *session) -{ - enum lttng_error_code status = LTTNG_OK; - struct lttng_ht_iter iter; - struct consumer_socket *socket; - LTTNG_OPTIONAL(uint64_t) current_chunk_id = {}; - const char *base_path; - - LTTNG_ASSERT(output); - LTTNG_ASSERT(session); - - DBG2("Set relayd object from snapshot output"); - - if (session->current_trace_chunk) { - enum lttng_trace_chunk_status chunk_status = - lttng_trace_chunk_get_id( - session->current_trace_chunk, - ¤t_chunk_id.value); - - if (chunk_status == LTTNG_TRACE_CHUNK_STATUS_OK) { - current_chunk_id.is_set = true; - } else { - ERR("Failed to get current trace chunk id"); - status = LTTNG_ERR_UNK; - goto error; - } - } - - /* Ignore if snapshot consumer output is not network. */ - if (output->type != CONSUMER_DST_NET) { - goto error; - } - - /* - * The snapshot record URI base path overrides the session - * base path. - */ - if (output->dst.net.control.subdir[0] != '\0') { - base_path = output->dst.net.control.subdir; - } else { - base_path = session->base_path; - } - - /* - * For each consumer socket, create and send the relayd object of the - * snapshot output. - */ - rcu_read_lock(); - cds_lfht_for_each_entry(output->socks->ht, &iter.iter, - socket, node.node) { - pthread_mutex_lock(socket->lock); - status = send_consumer_relayd_sockets(0, session->id, - output, socket, - session->name, session->hostname, - base_path, - session->live_timer, - current_chunk_id.is_set ? ¤t_chunk_id.value : NULL, - session->creation_time, - session->name_contains_creation_time); - pthread_mutex_unlock(socket->lock); - if (status != LTTNG_OK) { - rcu_read_unlock(); - goto error; - } - } - rcu_read_unlock(); - -error: - return status; -} - -/* - * Record a kernel snapshot. - * - * Return LTTNG_OK on success or a LTTNG_ERR code. - */ -static enum lttng_error_code record_kernel_snapshot( - struct ltt_kernel_session *ksess, - const struct consumer_output *output, - const struct ltt_session *session, - int wait, uint64_t nb_packets_per_stream) -{ - enum lttng_error_code status; - - LTTNG_ASSERT(ksess); - LTTNG_ASSERT(output); - LTTNG_ASSERT(session); - - status = kernel_snapshot_record( - ksess, output, wait, nb_packets_per_stream); - return status; -} - -/* - * Record a UST snapshot. - * - * Returns LTTNG_OK on success or a LTTNG_ERR error code. - */ -static enum lttng_error_code record_ust_snapshot(struct ltt_ust_session *usess, - const struct consumer_output *output, - const struct ltt_session *session, - int wait, uint64_t nb_packets_per_stream) -{ - enum lttng_error_code status; - - LTTNG_ASSERT(usess); - LTTNG_ASSERT(output); - LTTNG_ASSERT(session); - - status = ust_app_snapshot_record( - usess, output, wait, nb_packets_per_stream); - return status; -} - -static -uint64_t get_session_size_one_more_packet_per_stream( - const struct ltt_session *session, uint64_t cur_nr_packets) -{ - uint64_t tot_size = 0; - - if (session->kernel_session) { - struct ltt_kernel_channel *chan; - const struct ltt_kernel_session *ksess = - session->kernel_session; - - cds_list_for_each_entry(chan, &ksess->channel_list.head, list) { - if (cur_nr_packets >= chan->channel->attr.num_subbuf) { - /* - * Don't take channel into account if we - * already grab all its packets. - */ - continue; - } - tot_size += chan->channel->attr.subbuf_size - * chan->stream_count; - } - } - - if (session->ust_session) { - const struct ltt_ust_session *usess = session->ust_session; - - tot_size += ust_app_get_size_one_more_packet_per_stream(usess, - cur_nr_packets); - } - - return tot_size; -} - -/* - * Calculate the number of packets we can grab from each stream that - * fits within the overall snapshot max size. - * - * Returns -1 on error, 0 means infinite number of packets, else > 0 is - * the number of packets per stream. - * - * TODO: this approach is not perfect: we consider the worse case - * (packet filling the sub-buffers) as an upper bound, but we could do - * better if we do this calculation while we actually grab the packet - * content: we would know how much padding we don't actually store into - * the file. - * - * This algorithm is currently bounded by the number of packets per - * stream. - * - * Since we call this algorithm before actually grabbing the data, it's - * an approximation: for instance, applications could appear/disappear - * in between this call and actually grabbing data. - */ -static -int64_t get_session_nb_packets_per_stream(const struct ltt_session *session, - uint64_t max_size) -{ - int64_t size_left; - uint64_t cur_nb_packets = 0; - - if (!max_size) { - return 0; /* Infinite */ - } - - size_left = max_size; - for (;;) { - uint64_t one_more_packet_tot_size; - - one_more_packet_tot_size = get_session_size_one_more_packet_per_stream( - session, cur_nb_packets); - if (!one_more_packet_tot_size) { - /* We are already grabbing all packets. */ - break; - } - size_left -= one_more_packet_tot_size; - if (size_left < 0) { - break; - } - cur_nb_packets++; - } - if (!cur_nb_packets && size_left != max_size) { - /* Not enough room to grab one packet of each stream, error. */ - return -1; - } - return cur_nb_packets; -} - -static -enum lttng_error_code snapshot_record(struct ltt_session *session, - const struct snapshot_output *snapshot_output, int wait) -{ - int64_t nb_packets_per_stream; - char snapshot_chunk_name[LTTNG_NAME_MAX]; - int ret; - enum lttng_error_code ret_code = LTTNG_OK; - struct lttng_trace_chunk *snapshot_trace_chunk; - struct consumer_output *original_ust_consumer_output = NULL; - struct consumer_output *original_kernel_consumer_output = NULL; - struct consumer_output *snapshot_ust_consumer_output = NULL; - struct consumer_output *snapshot_kernel_consumer_output = NULL; - - ret = snprintf(snapshot_chunk_name, sizeof(snapshot_chunk_name), - "%s-%s-%" PRIu64, - snapshot_output->name, - snapshot_output->datetime, - snapshot_output->nb_snapshot); - if (ret < 0 || ret >= sizeof(snapshot_chunk_name)) { - ERR("Failed to format snapshot name"); - ret_code = LTTNG_ERR_INVALID; - goto error; - } - DBG("Recording snapshot \"%s\" for session \"%s\" with chunk name \"%s\"", - snapshot_output->name, session->name, - snapshot_chunk_name); - if (!session->kernel_session && !session->ust_session) { - ERR("Failed to record snapshot as no channels exist"); - ret_code = LTTNG_ERR_NO_CHANNEL; - goto error; - } - - if (session->kernel_session) { - original_kernel_consumer_output = - session->kernel_session->consumer; - snapshot_kernel_consumer_output = - consumer_copy_output(snapshot_output->consumer); - strcpy(snapshot_kernel_consumer_output->chunk_path, - snapshot_chunk_name); - - /* Copy the original domain subdir. */ - strcpy(snapshot_kernel_consumer_output->domain_subdir, - original_kernel_consumer_output->domain_subdir); - - ret = consumer_copy_sockets(snapshot_kernel_consumer_output, - original_kernel_consumer_output); - if (ret < 0) { - ERR("Failed to copy consumer sockets from snapshot output configuration"); - ret_code = LTTNG_ERR_NOMEM; - goto error; - } - ret_code = set_relayd_for_snapshot( - snapshot_kernel_consumer_output, session); - if (ret_code != LTTNG_OK) { - ERR("Failed to setup relay daemon for kernel tracer snapshot"); - goto error; - } - session->kernel_session->consumer = - snapshot_kernel_consumer_output; - } - if (session->ust_session) { - original_ust_consumer_output = session->ust_session->consumer; - snapshot_ust_consumer_output = - consumer_copy_output(snapshot_output->consumer); - strcpy(snapshot_ust_consumer_output->chunk_path, - snapshot_chunk_name); - - /* Copy the original domain subdir. */ - strcpy(snapshot_ust_consumer_output->domain_subdir, - original_ust_consumer_output->domain_subdir); - - ret = consumer_copy_sockets(snapshot_ust_consumer_output, - original_ust_consumer_output); - if (ret < 0) { - ERR("Failed to copy consumer sockets from snapshot output configuration"); - ret_code = LTTNG_ERR_NOMEM; - goto error; - } - ret_code = set_relayd_for_snapshot( - snapshot_ust_consumer_output, session); - if (ret_code != LTTNG_OK) { - ERR("Failed to setup relay daemon for userspace tracer snapshot"); - goto error; - } - session->ust_session->consumer = - snapshot_ust_consumer_output; - } - - snapshot_trace_chunk = session_create_new_trace_chunk(session, - snapshot_kernel_consumer_output ?: - snapshot_ust_consumer_output, - consumer_output_get_base_path( - snapshot_output->consumer), - snapshot_chunk_name); - if (!snapshot_trace_chunk) { - ERR("Failed to create temporary trace chunk to record a snapshot of session \"%s\"", - session->name); - ret_code = LTTNG_ERR_CREATE_DIR_FAIL; - goto error; - } - LTTNG_ASSERT(!session->current_trace_chunk); - ret = session_set_trace_chunk(session, snapshot_trace_chunk, NULL); - lttng_trace_chunk_put(snapshot_trace_chunk); - snapshot_trace_chunk = NULL; - if (ret) { - ERR("Failed to set temporary trace chunk to record a snapshot of session \"%s\"", - session->name); - ret_code = LTTNG_ERR_CREATE_TRACE_CHUNK_FAIL_CONSUMER; - goto error; - } - - nb_packets_per_stream = get_session_nb_packets_per_stream(session, - snapshot_output->max_size); - if (nb_packets_per_stream < 0) { - ret_code = LTTNG_ERR_MAX_SIZE_INVALID; - goto error_close_trace_chunk; - } - - if (session->kernel_session) { - ret_code = record_kernel_snapshot(session->kernel_session, - snapshot_kernel_consumer_output, session, - wait, nb_packets_per_stream); - if (ret_code != LTTNG_OK) { - goto error_close_trace_chunk; - } - } - - if (session->ust_session) { - ret_code = record_ust_snapshot(session->ust_session, - snapshot_ust_consumer_output, session, - wait, nb_packets_per_stream); - if (ret_code != LTTNG_OK) { - goto error_close_trace_chunk; - } - } - -error_close_trace_chunk: - if (session_set_trace_chunk(session, NULL, &snapshot_trace_chunk)) { - ERR("Failed to release the current trace chunk of session \"%s\"", - session->name); - ret_code = LTTNG_ERR_UNK; - } - - if (session_close_trace_chunk(session, snapshot_trace_chunk, - LTTNG_TRACE_CHUNK_COMMAND_TYPE_NO_OPERATION, NULL)) { - /* - * Don't goto end; make sure the chunk is closed for the session - * to allow future snapshots. - */ - ERR("Failed to close snapshot trace chunk of session \"%s\"", - session->name); - ret_code = LTTNG_ERR_CLOSE_TRACE_CHUNK_FAIL_CONSUMER; - } -error: - if (original_ust_consumer_output) { - session->ust_session->consumer = original_ust_consumer_output; - } - if (original_kernel_consumer_output) { - session->kernel_session->consumer = - original_kernel_consumer_output; - } - consumer_output_put(snapshot_ust_consumer_output); - consumer_output_put(snapshot_kernel_consumer_output); - return ret_code; -} - -/* - * Command LTTNG_SNAPSHOT_RECORD from lib lttng ctl. - * - * The wait parameter is ignored so this call always wait for the snapshot to - * complete before returning. - * - * Return LTTNG_OK on success or else a LTTNG_ERR code. - */ -int cmd_snapshot_record(struct ltt_session *session, - const struct lttng_snapshot_output *output, int wait) -{ - enum lttng_error_code cmd_ret = LTTNG_OK; - int ret; - unsigned int snapshot_success = 0; - char datetime[16]; - struct snapshot_output *tmp_output = NULL; - - LTTNG_ASSERT(session); - LTTNG_ASSERT(output); - - DBG("Cmd snapshot record for session %s", session->name); - - /* Get the datetime for the snapshot output directory. */ - ret = utils_get_current_time_str("%Y%m%d-%H%M%S", datetime, - sizeof(datetime)); - if (!ret) { - cmd_ret = LTTNG_ERR_INVALID; - goto error; - } - - /* - * Permission denied to create an output if the session is not - * set in no output mode. - */ - if (session->output_traces) { - cmd_ret = LTTNG_ERR_NOT_SNAPSHOT_SESSION; - goto error; - } - - /* The session needs to be started at least once. */ - if (!session->has_been_started) { - cmd_ret = LTTNG_ERR_START_SESSION_ONCE; - goto error; - } - - /* Use temporary output for the session. */ - if (*output->ctrl_url != '\0') { - tmp_output = snapshot_output_alloc(); - if (!tmp_output) { - cmd_ret = LTTNG_ERR_NOMEM; - goto error; - } - - ret = snapshot_output_init(session, output->max_size, - output->name, - output->ctrl_url, output->data_url, - session->consumer, - tmp_output, NULL); - if (ret < 0) { - if (ret == -ENOMEM) { - cmd_ret = LTTNG_ERR_NOMEM; - } else { - cmd_ret = LTTNG_ERR_INVALID; - } - goto error; - } - /* Use the global session count for the temporary snapshot. */ - tmp_output->nb_snapshot = session->snapshot.nb_snapshot; - - /* Use the global datetime */ - memcpy(tmp_output->datetime, datetime, sizeof(datetime)); - cmd_ret = snapshot_record(session, tmp_output, wait); - if (cmd_ret != LTTNG_OK) { - goto error; - } - snapshot_success = 1; - } else { - struct snapshot_output *sout; - struct lttng_ht_iter iter; - - rcu_read_lock(); - cds_lfht_for_each_entry(session->snapshot.output_ht->ht, - &iter.iter, sout, node.node) { - struct snapshot_output output_copy; - - /* - * Make a local copy of the output and override output - * parameters with those provided as part of the - * command. - */ - memcpy(&output_copy, sout, sizeof(output_copy)); - - if (output->max_size != (uint64_t) -1ULL) { - output_copy.max_size = output->max_size; - } - - output_copy.nb_snapshot = session->snapshot.nb_snapshot; - memcpy(output_copy.datetime, datetime, - sizeof(datetime)); - - /* Use temporary name. */ - if (*output->name != '\0') { - if (lttng_strncpy(output_copy.name, - output->name, - sizeof(output_copy.name))) { - cmd_ret = LTTNG_ERR_INVALID; - rcu_read_unlock(); - goto error; - } - } - - cmd_ret = snapshot_record(session, &output_copy, wait); - if (cmd_ret != LTTNG_OK) { - rcu_read_unlock(); - goto error; - } - snapshot_success = 1; - } - rcu_read_unlock(); - } - - if (snapshot_success) { - session->snapshot.nb_snapshot++; - } else { - cmd_ret = LTTNG_ERR_SNAPSHOT_FAIL; - } - -error: - if (tmp_output) { - snapshot_output_destroy(tmp_output); - } - return cmd_ret; -} - -/* - * Command LTTNG_SET_SESSION_SHM_PATH processed by the client thread. - */ -int cmd_set_session_shm_path(struct ltt_session *session, - const char *shm_path) -{ - /* Safety net */ - LTTNG_ASSERT(session); - - /* - * Can only set shm path before session is started. - */ - if (session->has_been_started) { - return LTTNG_ERR_SESSION_STARTED; - } - - strncpy(session->shm_path, shm_path, - sizeof(session->shm_path)); - session->shm_path[sizeof(session->shm_path) - 1] = '\0'; - - return LTTNG_OK; -} - -/* - * Command LTTNG_ROTATE_SESSION from the lttng-ctl library. - * - * Ask the consumer to rotate the session output directory. - * The session lock must be held. - * - * Returns LTTNG_OK on success or else a negative LTTng error code. - */ -int cmd_rotate_session(struct ltt_session *session, - struct lttng_rotate_session_return *rotate_return, - bool quiet_rotation, - enum lttng_trace_chunk_command_type command) -{ - int ret; - uint64_t ongoing_rotation_chunk_id; - enum lttng_error_code cmd_ret = LTTNG_OK; - struct lttng_trace_chunk *chunk_being_archived = NULL; - struct lttng_trace_chunk *new_trace_chunk = NULL; - enum lttng_trace_chunk_status chunk_status; - bool failed_to_rotate = false; - enum lttng_error_code rotation_fail_code = LTTNG_OK; - - LTTNG_ASSERT(session); - - if (!session->has_been_started) { - cmd_ret = LTTNG_ERR_START_SESSION_ONCE; - goto end; - } - - /* - * Explicit rotation is not supported for live sessions. - * However, live sessions can perform a quiet rotation on - * destroy. - * Rotation is not supported for snapshot traces (no output). - */ - if ((!quiet_rotation && session->live_timer) || - !session->output_traces) { - cmd_ret = LTTNG_ERR_ROTATION_NOT_AVAILABLE; - goto end; - } - - /* Unsupported feature in lttng-relayd before 2.11. */ - if (!quiet_rotation && session->consumer->type == CONSUMER_DST_NET && - (session->consumer->relay_major_version == 2 && - session->consumer->relay_minor_version < 11)) { - cmd_ret = LTTNG_ERR_ROTATION_NOT_AVAILABLE_RELAY; - goto end; - } - - /* Unsupported feature in lttng-modules before 2.8 (lack of sequence number). */ - if (session->kernel_session && !kernel_supports_ring_buffer_packet_sequence_number()) { - cmd_ret = LTTNG_ERR_ROTATION_NOT_AVAILABLE_KERNEL; - goto end; - } - - if (session->rotation_state == LTTNG_ROTATION_STATE_ONGOING) { - DBG("Refusing to launch a rotation; a rotation is already in progress for session %s", - session->name); - cmd_ret = LTTNG_ERR_ROTATION_PENDING; - goto end; - } - - /* - * After a stop, we only allow one rotation to occur, the other ones are - * useless until a new start. - */ - if (session->rotated_after_last_stop) { - DBG("Session \"%s\" was already rotated after stop, refusing rotation", - session->name); - cmd_ret = LTTNG_ERR_ROTATION_MULTIPLE_AFTER_STOP; - goto end; - } - - /* - * After a stop followed by a clear, disallow following rotations a they would - * generate empty chunks. - */ - if (session->cleared_after_last_stop) { - DBG("Session \"%s\" was already cleared after stop, refusing rotation", - session->name); - cmd_ret = LTTNG_ERR_ROTATION_AFTER_STOP_CLEAR; - goto end; - } - - if (session->active) { - new_trace_chunk = session_create_new_trace_chunk(session, NULL, - NULL, NULL); - if (!new_trace_chunk) { - cmd_ret = LTTNG_ERR_CREATE_DIR_FAIL; - goto error; - } - } - - /* - * The current trace chunk becomes the chunk being archived. - * - * After this point, "chunk_being_archived" must absolutely - * be closed on the consumer(s), otherwise it will never be - * cleaned-up, which will result in a leak. - */ - ret = session_set_trace_chunk(session, new_trace_chunk, - &chunk_being_archived); - if (ret) { - cmd_ret = LTTNG_ERR_CREATE_TRACE_CHUNK_FAIL_CONSUMER; - goto error; - } - - if (session->kernel_session) { - cmd_ret = kernel_rotate_session(session); - if (cmd_ret != LTTNG_OK) { - failed_to_rotate = true; - rotation_fail_code = cmd_ret; - } - } - if (session->ust_session) { - cmd_ret = ust_app_rotate_session(session); - if (cmd_ret != LTTNG_OK) { - failed_to_rotate = true; - rotation_fail_code = cmd_ret; - } - } - - if (!session->active) { - session->rotated_after_last_stop = true; - } - - if (!chunk_being_archived) { - DBG("Rotating session \"%s\" from a \"NULL\" trace chunk to a new trace chunk, skipping completion check", - session->name); - if (failed_to_rotate) { - cmd_ret = rotation_fail_code; - goto error; - } - cmd_ret = LTTNG_OK; - goto end; - } - - session->rotation_state = LTTNG_ROTATION_STATE_ONGOING; - chunk_status = lttng_trace_chunk_get_id(chunk_being_archived, - &ongoing_rotation_chunk_id); - LTTNG_ASSERT(chunk_status == LTTNG_TRACE_CHUNK_STATUS_OK); - - ret = session_close_trace_chunk(session, chunk_being_archived, - command, session->last_chunk_path); - if (ret) { - cmd_ret = LTTNG_ERR_CLOSE_TRACE_CHUNK_FAIL_CONSUMER; - goto error; - } - - if (failed_to_rotate) { - cmd_ret = rotation_fail_code; - goto error; - } - - session->quiet_rotation = quiet_rotation; - ret = timer_session_rotation_pending_check_start(session, - DEFAULT_ROTATE_PENDING_TIMER); - if (ret) { - cmd_ret = LTTNG_ERR_UNK; - goto error; - } - - if (rotate_return) { - rotate_return->rotation_id = ongoing_rotation_chunk_id; - } - - session->chunk_being_archived = chunk_being_archived; - chunk_being_archived = NULL; - if (!quiet_rotation) { - ret = notification_thread_command_session_rotation_ongoing( - the_notification_thread_handle, session->name, - session->uid, session->gid, - ongoing_rotation_chunk_id); - if (ret != LTTNG_OK) { - ERR("Failed to notify notification thread that a session rotation is ongoing for session %s", - session->name); - cmd_ret = ret; - } - } - - DBG("Cmd rotate session %s, archive_id %" PRIu64 " sent", - session->name, ongoing_rotation_chunk_id); -end: - lttng_trace_chunk_put(new_trace_chunk); - lttng_trace_chunk_put(chunk_being_archived); - ret = (cmd_ret == LTTNG_OK) ? cmd_ret : -((int) cmd_ret); - return ret; -error: - if (session_reset_rotation_state(session, - LTTNG_ROTATION_STATE_ERROR)) { - ERR("Failed to reset rotation state of session \"%s\"", - session->name); - } - goto end; -} - -/* - * Command LTTNG_ROTATION_GET_INFO from the lttng-ctl library. - * - * Check if the session has finished its rotation. - * - * Return LTTNG_OK on success or else an LTTNG_ERR code. - */ -int cmd_rotate_get_info(struct ltt_session *session, - struct lttng_rotation_get_info_return *info_return, - uint64_t rotation_id) -{ - enum lttng_error_code cmd_ret = LTTNG_OK; - enum lttng_rotation_state rotation_state; - - DBG("Cmd rotate_get_info session %s, rotation id %" PRIu64, session->name, - session->most_recent_chunk_id.value); - - if (session->chunk_being_archived) { - enum lttng_trace_chunk_status chunk_status; - uint64_t chunk_id; - - chunk_status = lttng_trace_chunk_get_id( - session->chunk_being_archived, - &chunk_id); - LTTNG_ASSERT(chunk_status == LTTNG_TRACE_CHUNK_STATUS_OK); - - rotation_state = rotation_id == chunk_id ? - LTTNG_ROTATION_STATE_ONGOING : - LTTNG_ROTATION_STATE_EXPIRED; - } else { - if (session->last_archived_chunk_id.is_set && - rotation_id != session->last_archived_chunk_id.value) { - rotation_state = LTTNG_ROTATION_STATE_EXPIRED; - } else { - rotation_state = session->rotation_state; - } - } - - switch (rotation_state) { - case LTTNG_ROTATION_STATE_NO_ROTATION: - DBG("Reporting that no rotation has occurred within the lifetime of session \"%s\"", - session->name); - goto end; - case LTTNG_ROTATION_STATE_EXPIRED: - DBG("Reporting that the rotation state of rotation id %" PRIu64 " of session \"%s\" has expired", - rotation_id, session->name); - break; - case LTTNG_ROTATION_STATE_ONGOING: - DBG("Reporting that rotation id %" PRIu64 " of session \"%s\" is still pending", - rotation_id, session->name); - break; - case LTTNG_ROTATION_STATE_COMPLETED: - { - int fmt_ret; - char *chunk_path; - char *current_tracing_path_reply; - size_t current_tracing_path_reply_len; - - DBG("Reporting that rotation id %" PRIu64 " of session \"%s\" is completed", - rotation_id, session->name); - - switch (session_get_consumer_destination_type(session)) { - case CONSUMER_DST_LOCAL: - current_tracing_path_reply = - info_return->location.local.absolute_path; - current_tracing_path_reply_len = - sizeof(info_return->location.local.absolute_path); - info_return->location_type = - (int8_t) LTTNG_TRACE_ARCHIVE_LOCATION_TYPE_LOCAL; - fmt_ret = asprintf(&chunk_path, - "%s/" DEFAULT_ARCHIVED_TRACE_CHUNKS_DIRECTORY "/%s", - session_get_base_path(session), - session->last_archived_chunk_name); - if (fmt_ret == -1) { - PERROR("Failed to format the path of the last archived trace chunk"); - info_return->status = LTTNG_ROTATION_STATUS_ERROR; - cmd_ret = LTTNG_ERR_UNK; - goto end; - } - break; - case CONSUMER_DST_NET: - { - uint16_t ctrl_port, data_port; - - current_tracing_path_reply = - info_return->location.relay.relative_path; - current_tracing_path_reply_len = - sizeof(info_return->location.relay.relative_path); - /* Currently the only supported relay protocol. */ - info_return->location.relay.protocol = - (int8_t) LTTNG_TRACE_ARCHIVE_LOCATION_RELAY_PROTOCOL_TYPE_TCP; - - fmt_ret = lttng_strncpy(info_return->location.relay.host, - session_get_net_consumer_hostname(session), - sizeof(info_return->location.relay.host)); - if (fmt_ret) { - ERR("Failed to copy host name to rotate_get_info reply"); - info_return->status = LTTNG_ROTATION_STATUS_ERROR; - cmd_ret = LTTNG_ERR_SET_URL; - goto end; - } - - session_get_net_consumer_ports(session, &ctrl_port, &data_port); - info_return->location.relay.ports.control = ctrl_port; - info_return->location.relay.ports.data = data_port; - info_return->location_type = - (int8_t) LTTNG_TRACE_ARCHIVE_LOCATION_TYPE_RELAY; - chunk_path = strdup(session->last_chunk_path); - if (!chunk_path) { - ERR("Failed to allocate the path of the last archived trace chunk"); - info_return->status = LTTNG_ROTATION_STATUS_ERROR; - cmd_ret = LTTNG_ERR_UNK; - goto end; - } - break; - } - default: - abort(); - } - - fmt_ret = lttng_strncpy(current_tracing_path_reply, - chunk_path, current_tracing_path_reply_len); - free(chunk_path); - if (fmt_ret) { - ERR("Failed to copy path of the last archived trace chunk to rotate_get_info reply"); - info_return->status = LTTNG_ROTATION_STATUS_ERROR; - cmd_ret = LTTNG_ERR_UNK; - goto end; - } - - break; - } - case LTTNG_ROTATION_STATE_ERROR: - DBG("Reporting that an error occurred during rotation %" PRIu64 " of session \"%s\"", - rotation_id, session->name); - break; - default: - abort(); - } - - cmd_ret = LTTNG_OK; -end: - info_return->status = (int32_t) rotation_state; - return cmd_ret; -} - -/* - * Command LTTNG_ROTATION_SET_SCHEDULE from the lttng-ctl library. - * - * Configure the automatic rotation parameters. - * 'activate' to true means activate the rotation schedule type with 'new_value'. - * 'activate' to false means deactivate the rotation schedule and validate that - * 'new_value' has the same value as the currently active value. - * - * Return LTTNG_OK on success or else a positive LTTNG_ERR code. - */ -int cmd_rotation_set_schedule(struct ltt_session *session, - bool activate, enum lttng_rotation_schedule_type schedule_type, - uint64_t new_value, - struct notification_thread_handle *notification_thread_handle) -{ - int ret; - uint64_t *parameter_value; - - LTTNG_ASSERT(session); - - DBG("Cmd rotate set schedule session %s", session->name); - - if (session->live_timer || !session->output_traces) { - DBG("Failing ROTATION_SET_SCHEDULE command as the rotation feature is not available for this session"); - ret = LTTNG_ERR_ROTATION_NOT_AVAILABLE; - goto end; - } - - switch (schedule_type) { - case LTTNG_ROTATION_SCHEDULE_TYPE_SIZE_THRESHOLD: - parameter_value = &session->rotate_size; - break; - case LTTNG_ROTATION_SCHEDULE_TYPE_PERIODIC: - parameter_value = &session->rotate_timer_period; - if (new_value >= UINT_MAX) { - DBG("Failing ROTATION_SET_SCHEDULE command as the value requested for a periodic rotation schedule is invalid: %" PRIu64 " > %u (UINT_MAX)", - new_value, UINT_MAX); - ret = LTTNG_ERR_INVALID; - goto end; - } - break; - default: - WARN("Failing ROTATION_SET_SCHEDULE command on unknown schedule type"); - ret = LTTNG_ERR_INVALID; - goto end; - } - - /* Improper use of the API. */ - if (new_value == -1ULL) { - WARN("Failing ROTATION_SET_SCHEDULE command as the value requested is -1"); - ret = LTTNG_ERR_INVALID; - goto end; - } - - /* - * As indicated in struct ltt_session's comments, a value of == 0 means - * this schedule rotation type is not in use. - * - * Reject the command if we were asked to activate a schedule that was - * already active. - */ - if (activate && *parameter_value != 0) { - DBG("Failing ROTATION_SET_SCHEDULE (activate) command as the schedule is already active"); - ret = LTTNG_ERR_ROTATION_SCHEDULE_SET; - goto end; - } - - /* - * Reject the command if we were asked to deactivate a schedule that was - * not active. - */ - if (!activate && *parameter_value == 0) { - DBG("Failing ROTATION_SET_SCHEDULE (deactivate) command as the schedule is already inactive"); - ret = LTTNG_ERR_ROTATION_SCHEDULE_NOT_SET; - goto end; - } - - /* - * Reject the command if we were asked to deactivate a schedule that - * doesn't exist. - */ - if (!activate && *parameter_value != new_value) { - DBG("Failing ROTATION_SET_SCHEDULE (deactivate) command as an inexistant schedule was provided"); - ret = LTTNG_ERR_ROTATION_SCHEDULE_NOT_SET; - goto end; - } - - *parameter_value = activate ? new_value : 0; - - switch (schedule_type) { - case LTTNG_ROTATION_SCHEDULE_TYPE_PERIODIC: - if (activate && session->active) { - /* - * Only start the timer if the session is active, - * otherwise it will be started when the session starts. - */ - ret = timer_session_rotation_schedule_timer_start( - session, new_value); - if (ret) { - ERR("Failed to enable session rotation timer in ROTATION_SET_SCHEDULE command"); - ret = LTTNG_ERR_UNK; - goto end; - } - } else { - ret = timer_session_rotation_schedule_timer_stop( - session); - if (ret) { - ERR("Failed to disable session rotation timer in ROTATION_SET_SCHEDULE command"); - ret = LTTNG_ERR_UNK; - goto end; - } - } - break; - case LTTNG_ROTATION_SCHEDULE_TYPE_SIZE_THRESHOLD: - if (activate) { - ret = subscribe_session_consumed_size_rotation(session, - new_value, notification_thread_handle); - if (ret) { - ERR("Failed to enable consumed-size notification in ROTATION_SET_SCHEDULE command"); - ret = LTTNG_ERR_UNK; - goto end; - } - } else { - ret = unsubscribe_session_consumed_size_rotation(session, - notification_thread_handle); - if (ret) { - ERR("Failed to disable consumed-size notification in ROTATION_SET_SCHEDULE command"); - ret = LTTNG_ERR_UNK; - goto end; - } - - } - break; - default: - /* Would have been caught before. */ - abort(); - } - - ret = LTTNG_OK; - - goto end; - -end: - return ret; -} - -/* Wait for a given path to be removed before continuing. */ -static enum lttng_error_code wait_on_path(void *path_data) -{ - const char *shm_path = path_data; - - DBG("Waiting for the shm path at %s to be removed before completing session destruction", - shm_path); - while (true) { - int ret; - struct stat st; - - ret = stat(shm_path, &st); - if (ret) { - if (errno != ENOENT) { - PERROR("stat() returned an error while checking for the existence of the shm path"); - } else { - DBG("shm path no longer exists, completing the destruction of session"); - } - break; - } else { - if (!S_ISDIR(st.st_mode)) { - ERR("The type of shm path %s returned by stat() is not a directory; aborting the wait for shm path removal", - shm_path); - break; - } - } - usleep(SESSION_DESTROY_SHM_PATH_CHECK_DELAY_US); - } - return LTTNG_OK; -} - -/* - * Returns a pointer to a handler to run on completion of a command. - * Returns NULL if no handler has to be run for the last command executed. - */ -const struct cmd_completion_handler *cmd_pop_completion_handler(void) -{ - struct cmd_completion_handler *handler = current_completion_handler; - - current_completion_handler = NULL; - return handler; -} - -/* - * Init command subsystem. - */ -void cmd_init(void) -{ - /* - * Set network sequence index to 1 for streams to match a relayd - * socket on the consumer side. - */ - pthread_mutex_lock(&relayd_net_seq_idx_lock); - relayd_net_seq_idx = 1; - pthread_mutex_unlock(&relayd_net_seq_idx_lock); - - DBG("Command subsystem initialized"); -}