From 2f77fc4b3720dc8f75847130498c2d4aad7c03ec Mon Sep 17 00:00:00 2001 From: David Goulet Date: Mon, 20 Aug 2012 15:42:10 -0400 Subject: [PATCH] Move code out of main.c and fix some issues This is a "code location refactoring". The file main.c was getting a bit too HUGE so this commit splits the command subsystem of the session daemon into cmd.c/.h and all helper functions used. This commit also fixes missing rcu read side lock and the consumer output hashtable memory leak on destroy. Minor changes were made to functions call to pass needed variables from the main.c to the command subsystem. The behavior was not changed nor altered in any way. Signed-off-by: David Goulet --- src/bin/lttng-sessiond/Makefile.am | 3 +- src/bin/lttng-sessiond/cmd.c | 2328 +++++++++++++++++++ src/bin/lttng-sessiond/cmd.h | 86 + src/bin/lttng-sessiond/consumer.c | 138 +- src/bin/lttng-sessiond/consumer.h | 12 + src/bin/lttng-sessiond/kernel.c | 19 + src/bin/lttng-sessiond/kernel.h | 1 + src/bin/lttng-sessiond/lttng-sessiond.h | 5 +- src/bin/lttng-sessiond/main.c | 2708 ++--------------------- src/bin/lttng-sessiond/session.c | 15 + src/bin/lttng-sessiond/session.h | 2 + src/bin/lttng-sessiond/utils.c | 5 + 12 files changed, 2748 insertions(+), 2574 deletions(-) create mode 100644 src/bin/lttng-sessiond/cmd.c create mode 100644 src/bin/lttng-sessiond/cmd.h diff --git a/src/bin/lttng-sessiond/Makefile.am b/src/bin/lttng-sessiond/Makefile.am index de5a252ca..73be02302 100644 --- a/src/bin/lttng-sessiond/Makefile.am +++ b/src/bin/lttng-sessiond/Makefile.am @@ -20,7 +20,8 @@ lttng_sessiond_SOURCES = utils.c utils.h \ fd-limit.c fd-limit.h \ kernel-consumer.c kernel-consumer.h \ consumer.h filter.c filter.h \ - health.c health.h + health.c health.h \ + cmd.c cmd.h if HAVE_LIBLTTNG_UST_CTL lttng_sessiond_SOURCES += trace-ust.c ust-app.c ust-consumer.c ust-consumer.h diff --git a/src/bin/lttng-sessiond/cmd.c b/src/bin/lttng-sessiond/cmd.c new file mode 100644 index 000000000..d36966322 --- /dev/null +++ b/src/bin/lttng-sessiond/cmd.c @@ -0,0 +1,2328 @@ +/* + * Copyright (C) 2012 - David Goulet + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License, version 2 only, as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., 51 + * Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#define _GNU_SOURCE +#include +#include +#include + +#include +#include +#include +#include + +#include "channel.h" +#include "consumer.h" +#include "event.h" +#include "kernel.h" +#include "kernel-consumer.h" +#include "lttng-sessiond.h" +#include "utils.h" + +#include "cmd.h" + +/* + * 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. + * + * This value should be incremented atomically for safety purposes and future + * possible concurrent access. + */ +static unsigned int relayd_net_seq_idx; + +/* + * 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]; + + assert(session); + 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; + } + + if (strlen(tmp_uurl) > 0) { + ret = snprintf(dst, size, "[K]: %s [data: %d] -- [U]: %s [data: %d]", + tmp_urls, kdata_port, tmp_uurl, udata_port); + } else { + ret = snprintf(dst, size, "%s [data: %d]", tmp_urls, kdata_port); + } + +error: + return ret; +} + +/* + * Fill lttng_channel array of all channels. + */ +static void list_lttng_channels(int domain, struct ltt_session *session, + struct lttng_channel *channels) +{ + int i = 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) { + /* Copy lttng_channel struct to array */ + memcpy(&channels[i], kchan->channel, sizeof(struct lttng_channel)); + channels[i].enabled = kchan->enabled; + i++; + } + } + break; + case LTTNG_DOMAIN_UST: + { + struct lttng_ht_iter iter; + struct ltt_ust_channel *uchan; + + cds_lfht_for_each_entry(session->ust_session->domain_global.channels->ht, + &iter.iter, uchan, node.node) { + strncpy(channels[i].name, uchan->name, LTTNG_SYMBOL_NAME_LEN); + 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; + switch (uchan->attr.output) { + case LTTNG_UST_MMAP: + default: + channels[i].attr.output = LTTNG_EVENT_MMAP; + break; + } + i++; + } + break; + } + default: + break; + } +} + +/* + * 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_event **events) +{ + int i = 0, ret = 0; + unsigned int nb_event = 0; + struct lttng_ht_iter iter; + struct lttng_ht_node_str *node; + struct ltt_ust_channel *uchan; + struct ltt_ust_event *uevent; + struct lttng_event *tmp; + + 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 = -LTTCOMM_UST_CHAN_NOT_FOUND; + goto error; + } + + uchan = caa_container_of(&node->node, struct ltt_ust_channel, node.node); + + nb_event += lttng_ht_get_count(uchan->events); + + if (nb_event == 0) { + ret = nb_event; + goto error; + } + + DBG3("Listing UST global %d events", nb_event); + + tmp = zmalloc(nb_event * sizeof(struct lttng_event)); + if (tmp == NULL) { + ret = -LTTCOMM_FATAL; + goto error; + } + + cds_lfht_for_each_entry(uchan->events->ht, &iter.iter, uevent, node.node) { + strncpy(tmp[i].name, uevent->attr.name, LTTNG_SYMBOL_NAME_LEN); + tmp[i].name[LTTNG_SYMBOL_NAME_LEN - 1] = '\0'; + tmp[i].enabled = uevent->enabled; + + switch (uevent->attr.instrumentation) { + case LTTNG_UST_TRACEPOINT: + tmp[i].type = LTTNG_EVENT_TRACEPOINT; + break; + case LTTNG_UST_PROBE: + tmp[i].type = LTTNG_EVENT_PROBE; + break; + case LTTNG_UST_FUNCTION: + tmp[i].type = LTTNG_EVENT_FUNCTION; + break; + } + + tmp[i].loglevel = uevent->attr.loglevel; + switch (uevent->attr.loglevel_type) { + case LTTNG_UST_LOGLEVEL_ALL: + tmp[i].loglevel_type = LTTNG_EVENT_LOGLEVEL_ALL; + break; + case LTTNG_UST_LOGLEVEL_RANGE: + tmp[i].loglevel_type = LTTNG_EVENT_LOGLEVEL_RANGE; + break; + case LTTNG_UST_LOGLEVEL_SINGLE: + tmp[i].loglevel_type = LTTNG_EVENT_LOGLEVEL_SINGLE; + break; + } + if (uevent->filter) { + tmp[i].filter = 1; + } + i++; + } + + ret = nb_event; + *events = tmp; + +error: + 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_event **events) +{ + int i = 0, ret; + unsigned int nb_event; + struct ltt_kernel_event *event; + struct ltt_kernel_channel *kchan; + + kchan = trace_kernel_get_channel_by_name(channel_name, kernel_session); + if (kchan == NULL) { + ret = LTTCOMM_KERN_CHAN_NOT_FOUND; + goto error; + } + + nb_event = kchan->event_count; + + DBG("Listing events for channel %s", kchan->channel->name); + + if (nb_event == 0) { + ret = nb_event; + goto error; + } + + *events = zmalloc(nb_event * sizeof(struct lttng_event)); + if (*events == NULL) { + ret = LTTCOMM_FATAL; + goto error; + } + + /* Kernel channels */ + cds_list_for_each_entry(event, &kchan->events_list.head , list) { + strncpy((*events)[i].name, event->event->name, LTTNG_SYMBOL_NAME_LEN); + (*events)[i].name[LTTNG_SYMBOL_NAME_LEN - 1] = '\0'; + (*events)[i].enabled = event->enabled; + + switch (event->event->instrumentation) { + case LTTNG_KERNEL_TRACEPOINT: + (*events)[i].type = LTTNG_EVENT_TRACEPOINT; + break; + case LTTNG_KERNEL_KPROBE: + case LTTNG_KERNEL_KRETPROBE: + (*events)[i].type = LTTNG_EVENT_PROBE; + memcpy(&(*events)[i].attr.probe, &event->event->u.kprobe, + sizeof(struct lttng_kernel_kprobe)); + break; + case LTTNG_KERNEL_FUNCTION: + (*events)[i].type = LTTNG_EVENT_FUNCTION; + memcpy(&((*events)[i].attr.ftrace), &event->event->u.ftrace, + sizeof(struct lttng_kernel_function)); + break; + case LTTNG_KERNEL_NOOP: + (*events)[i].type = LTTNG_EVENT_NOOP; + break; + case LTTNG_KERNEL_SYSCALL: + (*events)[i].type = LTTNG_EVENT_SYSCALL; + break; + case LTTNG_KERNEL_ALL: + assert(0); + break; + } + i++; + } + + 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 int add_uri_to_consumer(struct consumer_output *consumer, + struct lttng_uri *uri, int domain, const char *session_name) +{ + int ret = LTTCOMM_OK; + const char *default_trace_dir; + + assert(uri); + + if (consumer == NULL) { + DBG("No consumer detected. Don't add URI. Stopping."); + ret = LTTCOMM_NO_CONSUMER; + goto error; + } + + switch (domain) { + case LTTNG_DOMAIN_KERNEL: + default_trace_dir = DEFAULT_KERNEL_TRACE_DIR; + break; + case LTTNG_DOMAIN_UST: + default_trace_dir = DEFAULT_UST_TRACE_DIR; + 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. + */ + default_trace_dir = ""; + } + + switch (uri->dtype) { + case LTTNG_DST_IPV4: + case LTTNG_DST_IPV6: + DBG2("Setting network URI to consumer"); + + /* Set URI into consumer output object */ + ret = consumer_set_network_uri(consumer, uri); + if (ret < 0) { + ret = LTTCOMM_FATAL; + 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. + */ + goto error; + } + + if (uri->stype == LTTNG_STREAM_CONTROL && strlen(uri->subdir) == 0) { + ret = consumer_set_subdir(consumer, session_name); + if (ret < 0) { + ret = LTTCOMM_FATAL; + goto error; + } + } + + if (uri->stype == LTTNG_STREAM_CONTROL) { + /* On a new subdir, reappend the default trace dir. */ + strncat(consumer->subdir, default_trace_dir, sizeof(consumer->subdir)); + DBG3("Append domain trace name to subdir %s", consumer->subdir); + } + + break; + case LTTNG_DST_PATH: + DBG2("Setting trace directory path from URI to %s", uri->dst.path); + memset(consumer->dst.trace_path, 0, + sizeof(consumer->dst.trace_path)); + strncpy(consumer->dst.trace_path, uri->dst.path, + sizeof(consumer->dst.trace_path)); + /* Append default trace dir */ + strncat(consumer->dst.trace_path, default_trace_dir, + sizeof(consumer->dst.trace_path)); + /* Flag consumer as local. */ + consumer->type = CONSUMER_DST_LOCAL; + break; + } + +error: + return ret; +} + +/* + * 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; + + assert(session); + + if (session->consumer_fds_sent == 0 && session->consumer != NULL) { + cds_lfht_for_each_entry(session->consumer->socks->ht, &iter.iter, + socket, node.node) { + /* Code flow error */ + assert(socket->fd >= 0); + + pthread_mutex_lock(socket->lock); + ret = kernel_consumer_send_session(socket->fd, session); + pthread_mutex_unlock(socket->lock); + if (ret < 0) { + ret = LTTCOMM_KERN_CONSUMER_FAIL; + goto error; + } + } + } + +error: + 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's stays untouched and a lttcomm error code is returned. + */ +static int create_connect_relayd(struct consumer_output *output, + const char *session_name, struct lttng_uri *uri, + struct lttcomm_sock **relayd_sock) +{ + int ret; + struct lttcomm_sock *sock; + + /* Create socket object from URI */ + sock = lttcomm_alloc_sock_from_uri(uri); + if (sock == NULL) { + ret = LTTCOMM_FATAL; + goto error; + } + + ret = lttcomm_create_sock(sock); + if (ret < 0) { + ret = LTTCOMM_FATAL; + goto error; + } + + /* Connect to relayd so we can proceed with a session creation. */ + ret = relayd_connect(sock); + if (ret < 0) { + ERR("Unable to reach lttng-relayd"); + ret = LTTCOMM_RELAYD_SESSION_FAIL; + goto free_sock; + } + + /* Create socket for control stream. */ + if (uri->stype == LTTNG_STREAM_CONTROL) { + DBG3("Creating relayd stream socket from URI"); + + /* Check relayd version */ + ret = relayd_version_check(sock, RELAYD_VERSION_COMM_MAJOR, + RELAYD_VERSION_COMM_MINOR); + if (ret < 0) { + ret = LTTCOMM_RELAYD_VERSION_FAIL; + goto close_sock; + } + } 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); + ret = LTTCOMM_INVALID; + goto close_sock; + } + + *relayd_sock = sock; + + return LTTCOMM_OK; + +close_sock: + if (sock) { + (void) relayd_close(sock); + } +free_sock: + if (sock) { + lttcomm_destroy_sock(sock); + } +error: + return ret; +} + +/* + * Connect to the relayd using URI and send the socket to the right consumer. + */ +static int send_consumer_relayd_socket(int domain, struct ltt_session *session, + struct lttng_uri *relayd_uri, struct consumer_output *consumer, + int consumer_fd) +{ + int ret; + struct lttcomm_sock *sock = NULL; + + /* Set the network sequence index if not set. */ + if (consumer->net_seq_index == -1) { + /* + * Increment net_seq_idx because we are about to transfer the + * new relayd socket to the consumer. + */ + uatomic_inc(&relayd_net_seq_idx); + /* Assign unique key so the consumer can match streams */ + uatomic_set(&consumer->net_seq_index, + uatomic_read(&relayd_net_seq_idx)); + } + + /* Connect to relayd and make version check if uri is the control. */ + ret = create_connect_relayd(consumer, session->name, relayd_uri, &sock); + if (ret != LTTCOMM_OK) { + goto close_sock; + } + + /* If the control socket is connected, network session is ready */ + if (relayd_uri->stype == LTTNG_STREAM_CONTROL) { + session->net_handle = 1; + } + + /* Send relayd socket to consumer. */ + ret = consumer_send_relayd_socket(consumer_fd, sock, + consumer, relayd_uri->stype); + if (ret < 0) { + ret = LTTCOMM_ENABLE_CONSUMER_FAIL; + goto close_sock; + } + + ret = LTTCOMM_OK; + + /* + * 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 (sock) { + (void) relayd_close(sock); + lttcomm_destroy_sock(sock); + } + + return ret; +} + +/* + * 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. + */ +static int send_consumer_relayd_sockets(int domain, + struct ltt_session *session, struct consumer_output *consumer, int fd) +{ + int ret; + + assert(session); + assert(consumer); + + /* Don't resend the sockets to the consumer. */ + if (consumer->dst.net.relayd_socks_sent) { + ret = LTTCOMM_OK; + goto error; + } + + /* Sending control relayd socket. */ + ret = send_consumer_relayd_socket(domain, session, + &consumer->dst.net.control, consumer, fd); + if (ret != LTTCOMM_OK) { + goto error; + } + + /* Sending data relayd socket. */ + ret = send_consumer_relayd_socket(domain, session, + &consumer->dst.net.data, consumer, fd); + if (ret != LTTCOMM_OK) { + goto error; + } + + /* Flag that all relayd sockets were sent to the consumer. */ + consumer->dst.net.relayd_socks_sent = 1; + +error: + return ret; +} + +/* + * 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. + */ +static int setup_relayd(struct ltt_session *session) +{ + int ret = LTTCOMM_OK; + struct ltt_ust_session *usess; + struct ltt_kernel_session *ksess; + struct consumer_socket *socket; + struct lttng_ht_iter iter; + + assert(session); + + usess = session->ust_session; + ksess = session->kernel_session; + + DBG2("Setting relayd for session %s", session->name); + + 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) { + /* Code flow error */ + assert(socket->fd >= 0); + + pthread_mutex_lock(socket->lock); + send_consumer_relayd_sockets(LTTNG_DOMAIN_UST, session, + usess->consumer, socket->fd); + pthread_mutex_unlock(socket->lock); + if (ret != LTTCOMM_OK) { + goto error; + } + } + } + + 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) { + /* Code flow error */ + assert(socket->fd >= 0); + + pthread_mutex_lock(socket->lock); + send_consumer_relayd_sockets(LTTNG_DOMAIN_KERNEL, session, + ksess->consumer, socket->fd); + pthread_mutex_unlock(socket->lock); + if (ret != LTTCOMM_OK) { + goto error; + } + } + } + +error: + return ret; +} + +/* + * Command LTTNG_DISABLE_CHANNEL processed by the client thread. + */ +int cmd_disable_channel(struct ltt_session *session, int domain, + char *channel_name) +{ + int ret; + struct ltt_ust_session *usess; + + usess = session->ust_session; + + switch (domain) { + case LTTNG_DOMAIN_KERNEL: + { + ret = channel_kernel_disable(session->kernel_session, + channel_name); + if (ret != LTTCOMM_OK) { + goto error; + } + + kernel_wait_quiescent(kernel_tracer_fd); + 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 = LTTCOMM_UST_CHAN_NOT_FOUND; + goto error; + } + + ret = channel_ust_disable(usess, domain, uchan); + if (ret != LTTCOMM_OK) { + goto error; + } + break; + } +#if 0 + case LTTNG_DOMAIN_UST_PID_FOLLOW_CHILDREN: + case LTTNG_DOMAIN_UST_EXEC_NAME: + case LTTNG_DOMAIN_UST_PID: +#endif + default: + ret = LTTCOMM_UNKNOWN_DOMAIN; + goto error; + } + + ret = LTTCOMM_OK; + +error: + 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, + int domain, struct lttng_channel *attr, int wpipe) +{ + int ret; + struct ltt_ust_session *usess = session->ust_session; + struct lttng_ht *chan_ht; + + assert(session); + assert(attr); + + DBG("Enabling channel %s for session %s", attr->name, session->name); + + switch (domain) { + case LTTNG_DOMAIN_KERNEL: + { + struct ltt_kernel_channel *kchan; + + /* Mandatory for a kernel channel. */ + assert(wpipe > 0); + + kchan = trace_kernel_get_channel_by_name(attr->name, + session->kernel_session); + if (kchan == NULL) { + ret = channel_kernel_create(session->kernel_session, attr, wpipe); + } else { + ret = channel_kernel_enable(session->kernel_session, kchan); + } + + if (ret != LTTCOMM_OK) { + goto error; + } + + kernel_wait_quiescent(kernel_tracer_fd); + break; + } + case LTTNG_DOMAIN_UST: + { + struct ltt_ust_channel *uchan; + + chan_ht = usess->domain_global.channels; + + uchan = trace_ust_find_channel_by_name(chan_ht, attr->name); + if (uchan == NULL) { + ret = channel_ust_create(usess, domain, attr); + } else { + ret = channel_ust_enable(usess, domain, uchan); + } + break; + } +#if 0 + case LTTNG_DOMAIN_UST_PID_FOLLOW_CHILDREN: + case LTTNG_DOMAIN_UST_EXEC_NAME: + case LTTNG_DOMAIN_UST_PID: +#endif + default: + ret = LTTCOMM_UNKNOWN_DOMAIN; + goto error; + } + +error: + return ret; +} + + +/* + * Command LTTNG_DISABLE_EVENT processed by the client thread. + */ +int cmd_disable_event(struct ltt_session *session, int domain, + char *channel_name, char *event_name) +{ + int ret; + + switch (domain) { + case LTTNG_DOMAIN_KERNEL: + { + struct ltt_kernel_channel *kchan; + struct ltt_kernel_session *ksess; + + ksess = session->kernel_session; + + kchan = trace_kernel_get_channel_by_name(channel_name, ksess); + if (kchan == NULL) { + ret = LTTCOMM_KERN_CHAN_NOT_FOUND; + goto error; + } + + ret = event_kernel_disable_tracepoint(ksess, kchan, event_name); + if (ret != LTTCOMM_OK) { + goto error; + } + + kernel_wait_quiescent(kernel_tracer_fd); + break; + } + case LTTNG_DOMAIN_UST: + { + struct ltt_ust_channel *uchan; + struct ltt_ust_session *usess; + + usess = session->ust_session; + + uchan = trace_ust_find_channel_by_name(usess->domain_global.channels, + channel_name); + if (uchan == NULL) { + ret = LTTCOMM_UST_CHAN_NOT_FOUND; + goto error; + } + + ret = event_ust_disable_tracepoint(usess, domain, uchan, event_name); + if (ret != LTTCOMM_OK) { + goto error; + } + + DBG3("Disable UST event %s in channel %s completed", event_name, + channel_name); + break; + } +#if 0 + case LTTNG_DOMAIN_UST_EXEC_NAME: + case LTTNG_DOMAIN_UST_PID: + case LTTNG_DOMAIN_UST_PID_FOLLOW_CHILDREN: +#endif + default: + ret = LTTCOMM_UND; + goto error; + } + + ret = LTTCOMM_OK; + +error: + return ret; +} + +/* + * Command LTTNG_DISABLE_ALL_EVENT processed by the client thread. + */ +int cmd_disable_event_all(struct ltt_session *session, int domain, + char *channel_name) +{ + int ret; + + switch (domain) { + case LTTNG_DOMAIN_KERNEL: + { + struct ltt_kernel_session *ksess; + struct ltt_kernel_channel *kchan; + + ksess = session->kernel_session; + + kchan = trace_kernel_get_channel_by_name(channel_name, ksess); + if (kchan == NULL) { + ret = LTTCOMM_KERN_CHAN_NOT_FOUND; + goto error; + } + + ret = event_kernel_disable_all(ksess, kchan); + if (ret != LTTCOMM_OK) { + goto error; + } + + kernel_wait_quiescent(kernel_tracer_fd); + break; + } + case LTTNG_DOMAIN_UST: + { + struct ltt_ust_session *usess; + struct ltt_ust_channel *uchan; + + usess = session->ust_session; + + uchan = trace_ust_find_channel_by_name(usess->domain_global.channels, + channel_name); + if (uchan == NULL) { + ret = LTTCOMM_UST_CHAN_NOT_FOUND; + goto error; + } + + ret = event_ust_disable_all_tracepoints(usess, domain, uchan); + if (ret != 0) { + goto error; + } + + DBG3("Disable all UST events in channel %s completed", channel_name); + + break; + } +#if 0 + case LTTNG_DOMAIN_UST_EXEC_NAME: + case LTTNG_DOMAIN_UST_PID: + case LTTNG_DOMAIN_UST_PID_FOLLOW_CHILDREN: +#endif + default: + ret = LTTCOMM_UND; + goto error; + } + + ret = LTTCOMM_OK; + +error: + return ret; +} + +/* + * Command LTTNG_ADD_CONTEXT processed by the client thread. + */ +int cmd_add_context(struct ltt_session *session, int domain, + char *channel_name, char *event_name, struct lttng_event_context *ctx) +{ + int ret; + + switch (domain) { + case LTTNG_DOMAIN_KERNEL: + /* Add kernel context to kernel tracer */ + ret = context_kernel_add(session->kernel_session, ctx, + event_name, channel_name); + if (ret != LTTCOMM_OK) { + goto error; + } + break; + case LTTNG_DOMAIN_UST: + { + struct ltt_ust_session *usess = session->ust_session; + + assert(usess); + + ret = context_ust_add(usess, domain, ctx, event_name, channel_name); + if (ret != LTTCOMM_OK) { + goto error; + } + break; + } +#if 0 + case LTTNG_DOMAIN_UST_EXEC_NAME: + case LTTNG_DOMAIN_UST_PID: + case LTTNG_DOMAIN_UST_PID_FOLLOW_CHILDREN: +#endif + default: + ret = LTTCOMM_UND; + goto error; + } + + ret = LTTCOMM_OK; + +error: + return ret; +} + +/* + * Command LTTNG_SET_FILTER processed by the client thread. + */ +int cmd_set_filter(struct ltt_session *session, int domain, + char *channel_name, char *event_name, + struct lttng_filter_bytecode *bytecode) +{ + int ret; + + switch (domain) { + case LTTNG_DOMAIN_KERNEL: + ret = LTTCOMM_FATAL; + break; + case LTTNG_DOMAIN_UST: + { + struct ltt_ust_session *usess = session->ust_session; + + ret = filter_ust_set(usess, domain, bytecode, event_name, channel_name); + if (ret != LTTCOMM_OK) { + goto error; + } + break; + } +#if 0 + case LTTNG_DOMAIN_UST_EXEC_NAME: + case LTTNG_DOMAIN_UST_PID: + case LTTNG_DOMAIN_UST_PID_FOLLOW_CHILDREN: +#endif + default: + ret = LTTCOMM_UND; + goto error; + } + + ret = LTTCOMM_OK; + +error: + return ret; + +} + + +/* + * Command LTTNG_ENABLE_EVENT processed by the client thread. + */ +int cmd_enable_event(struct ltt_session *session, int domain, + char *channel_name, struct lttng_event *event, int wpipe) +{ + int ret; + struct lttng_channel *attr; + + assert(session); + assert(event); + assert(channel_name); + + switch (domain) { + case LTTNG_DOMAIN_KERNEL: + { + struct ltt_kernel_channel *kchan; + + kchan = trace_kernel_get_channel_by_name(channel_name, + session->kernel_session); + if (kchan == NULL) { + attr = channel_new_default_attr(domain); + if (attr == NULL) { + ret = LTTCOMM_FATAL; + goto error; + } + strncpy(attr->name, channel_name, sizeof(attr->name)); + + ret = cmd_enable_channel(session, domain, attr, wpipe); + if (ret != LTTCOMM_OK) { + free(attr); + goto error; + } + free(attr); + } + + /* 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 = LTTCOMM_FATAL; + goto error; + } + + ret = event_kernel_enable_tracepoint(session->kernel_session, kchan, + event); + if (ret != LTTCOMM_OK) { + goto error; + } + + kernel_wait_quiescent(kernel_tracer_fd); + break; + } + case LTTNG_DOMAIN_UST: + { + struct ltt_ust_channel *uchan; + struct ltt_ust_session *usess = session->ust_session; + + assert(usess); + + /* 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(domain); + if (attr == NULL) { + ret = LTTCOMM_FATAL; + goto error; + } + strncpy(attr->name, channel_name, sizeof(attr->name)); + + ret = cmd_enable_channel(session, domain, attr, wpipe); + if (ret != LTTCOMM_OK) { + free(attr); + goto error; + } + free(attr); + + /* Get the newly created channel reference back */ + uchan = trace_ust_find_channel_by_name( + usess->domain_global.channels, channel_name); + assert(uchan); + } + + /* At this point, the session and channel exist on the tracer */ + ret = event_ust_enable_tracepoint(usess, domain, uchan, event); + if (ret != LTTCOMM_OK) { + goto error; + } + break; + } +#if 0 + case LTTNG_DOMAIN_UST_EXEC_NAME: + case LTTNG_DOMAIN_UST_PID: + case LTTNG_DOMAIN_UST_PID_FOLLOW_CHILDREN: +#endif + default: + ret = LTTCOMM_UND; + goto error; + } + + ret = LTTCOMM_OK; + +error: + return ret; +} + +/* + * Command LTTNG_ENABLE_ALL_EVENT processed by the client thread. + */ +int cmd_enable_event_all(struct ltt_session *session, int domain, + char *channel_name, int event_type, int wpipe) +{ + int ret; + struct lttng_channel *attr; + + assert(session); + assert(channel_name); + + switch (domain) { + case LTTNG_DOMAIN_KERNEL: + { + struct ltt_kernel_channel *kchan; + + assert(session->kernel_session); + + kchan = trace_kernel_get_channel_by_name(channel_name, + session->kernel_session); + if (kchan == NULL) { + /* Create default channel */ + attr = channel_new_default_attr(domain); + if (attr == NULL) { + ret = LTTCOMM_FATAL; + goto error; + } + strncpy(attr->name, channel_name, sizeof(attr->name)); + + ret = cmd_enable_channel(session, domain, attr, wpipe); + if (ret != LTTCOMM_OK) { + free(attr); + goto error; + } + free(attr); + + /* Get the newly created kernel channel pointer */ + kchan = trace_kernel_get_channel_by_name(channel_name, + session->kernel_session); + assert(kchan); + } + + switch (event_type) { + case LTTNG_EVENT_SYSCALL: + ret = event_kernel_enable_all_syscalls(session->kernel_session, + kchan, kernel_tracer_fd); + break; + case LTTNG_EVENT_TRACEPOINT: + /* + * This call enables all LTTNG_KERNEL_TRACEPOINTS and + * events already registered to the channel. + */ + ret = event_kernel_enable_all_tracepoints(session->kernel_session, + kchan, kernel_tracer_fd); + break; + case LTTNG_EVENT_ALL: + /* Enable syscalls and tracepoints */ + ret = event_kernel_enable_all(session->kernel_session, + kchan, kernel_tracer_fd); + break; + default: + ret = LTTCOMM_KERN_ENABLE_FAIL; + goto error; + } + + /* Manage return value */ + if (ret != LTTCOMM_OK) { + goto error; + } + + kernel_wait_quiescent(kernel_tracer_fd); + break; + } + case LTTNG_DOMAIN_UST: + { + struct ltt_ust_channel *uchan; + struct ltt_ust_session *usess = session->ust_session; + + assert(usess); + + /* 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(domain); + if (attr == NULL) { + ret = LTTCOMM_FATAL; + goto error; + } + strncpy(attr->name, channel_name, sizeof(attr->name)); + + ret = cmd_enable_channel(session, domain, attr, wpipe); + if (ret != LTTCOMM_OK) { + free(attr); + goto error; + } + free(attr); + + /* Get the newly created channel reference back */ + uchan = trace_ust_find_channel_by_name( + usess->domain_global.channels, channel_name); + assert(uchan); + } + + /* At this point, the session and channel exist on the tracer */ + + switch (event_type) { + case LTTNG_EVENT_ALL: + case LTTNG_EVENT_TRACEPOINT: + ret = event_ust_enable_all_tracepoints(usess, domain, uchan); + if (ret != LTTCOMM_OK) { + goto error; + } + break; + default: + ret = LTTCOMM_UST_ENABLE_FAIL; + goto error; + } + + /* Manage return value */ + if (ret != LTTCOMM_OK) { + goto error; + } + + break; + } +#if 0 + case LTTNG_DOMAIN_UST_EXEC_NAME: + case LTTNG_DOMAIN_UST_PID: + case LTTNG_DOMAIN_UST_PID_FOLLOW_CHILDREN: +#endif + default: + ret = LTTCOMM_UND; + goto error; + } + + ret = LTTCOMM_OK; + +error: + return ret; +} + + +/* + * Command LTTNG_LIST_TRACEPOINTS processed by the client thread. + */ +ssize_t cmd_list_tracepoints(int domain, struct lttng_event **events) +{ + int ret; + ssize_t nb_events = 0; + + switch (domain) { + case LTTNG_DOMAIN_KERNEL: + nb_events = kernel_list_events(kernel_tracer_fd, events); + if (nb_events < 0) { + ret = LTTCOMM_KERN_LIST_FAIL; + goto error; + } + break; + case LTTNG_DOMAIN_UST: + nb_events = ust_app_list_events(events); + if (nb_events < 0) { + ret = LTTCOMM_UST_LIST_FAIL; + goto error; + } + break; + default: + ret = LTTCOMM_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(int 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 = LTTCOMM_UST_LIST_FAIL; + goto error; + } + break; + case LTTNG_DOMAIN_KERNEL: + default: /* fall-through */ + ret = LTTCOMM_UND; + goto error; + } + + return nb_fields; + +error: + /* Return negative value to differentiate return code */ + return -ret; +} + +/* + * Command LTTNG_START_TRACE processed by the client thread. + */ +int cmd_start_trace(struct ltt_session *session) +{ + int ret; + struct ltt_kernel_session *ksession; + struct ltt_ust_session *usess; + struct ltt_kernel_channel *kchan; + + assert(session); + + /* Ease our life a bit ;) */ + ksession = session->kernel_session; + usess = session->ust_session; + + if (session->enabled) { + /* Already started. */ + ret = LTTCOMM_TRACE_ALREADY_STARTED; + goto error; + } + + session->enabled = 1; + + ret = setup_relayd(session); + if (ret != LTTCOMM_OK) { + ERR("Error setting up relayd for session %s", session->name); + goto error; + } + + /* Kernel tracing */ + if (ksession != NULL) { + /* Open kernel metadata */ + if (ksession->metadata == NULL) { + ret = kernel_open_metadata(ksession); + if (ret < 0) { + ret = LTTCOMM_KERN_META_FAIL; + goto error; + } + } + + /* Open kernel metadata stream */ + if (ksession->metadata_stream_fd < 0) { + ret = kernel_open_metadata_stream(ksession); + if (ret < 0) { + ERR("Kernel create metadata stream failed"); + ret = LTTCOMM_KERN_STREAM_FAIL; + goto error; + } + } + + /* For each channel */ + cds_list_for_each_entry(kchan, &ksession->channel_list.head, list) { + if (kchan->stream_count == 0) { + ret = kernel_open_channel_stream(kchan); + if (ret < 0) { + ret = LTTCOMM_KERN_STREAM_FAIL; + goto error; + } + /* Update the stream global counter */ + ksession->stream_count_global += ret; + } + } + + /* Setup kernel consumer socket and send fds to it */ + ret = init_kernel_tracing(ksession); + if (ret < 0) { + ret = LTTCOMM_KERN_START_FAIL; + goto error; + } + + /* This start the kernel tracing */ + ret = kernel_start_session(ksession); + if (ret < 0) { + ret = LTTCOMM_KERN_START_FAIL; + goto error; + } + + /* Quiescent wait after starting trace */ + kernel_wait_quiescent(kernel_tracer_fd); + } + + /* Flag session that trace should start automatically */ + if (usess) { + usess->start_trace = 1; + + ret = ust_app_start_trace_all(usess); + if (ret < 0) { + ret = LTTCOMM_UST_START_FAIL; + goto error; + } + } + + ret = LTTCOMM_OK; + +error: + return ret; +} + +/* + * Command LTTNG_STOP_TRACE processed by the client thread. + */ +int cmd_stop_trace(struct ltt_session *session) +{ + int ret; + struct ltt_kernel_channel *kchan; + struct ltt_kernel_session *ksession; + struct ltt_ust_session *usess; + + assert(session); + + /* Short cut */ + ksession = session->kernel_session; + usess = session->ust_session; + + if (!session->enabled) { + ret = LTTCOMM_TRACE_ALREADY_STOPPED; + goto error; + } + + session->enabled = 0; + + /* Kernel tracer */ + if (ksession) { + DBG("Stop kernel tracing"); + + /* Flush metadata if exist */ + if (ksession->metadata_stream_fd >= 0) { + ret = kernel_metadata_flush_buffer(ksession->metadata_stream_fd); + if (ret < 0) { + ERR("Kernel metadata flush failed"); + } + } + + /* Flush all buffers before stopping */ + cds_list_for_each_entry(kchan, &ksession->channel_list.head, list) { + ret = kernel_flush_buffer(kchan); + if (ret < 0) { + ERR("Kernel flush buffer error"); + } + } + + ret = kernel_stop_session(ksession); + if (ret < 0) { + ret = LTTCOMM_KERN_STOP_FAIL; + goto error; + } + + kernel_wait_quiescent(kernel_tracer_fd); + } + + if (usess) { + usess->start_trace = 0; + + ret = ust_app_stop_trace_all(usess); + if (ret < 0) { + ret = LTTCOMM_UST_STOP_FAIL; + goto error; + } + } + + ret = LTTCOMM_OK; + +error: + return ret; +} + +/* + * Command LTTNG_SET_CONSUMER_URI processed by the client thread. + */ +int cmd_set_consumer_uri(int domain, 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; + struct consumer_output *consumer = NULL; + + assert(session); + assert(uris); + assert(nb_uri > 0); + + /* Can't enable consumer after session started. */ + if (session->enabled) { + ret = LTTCOMM_TRACE_ALREADY_STARTED; + goto error; + } + + if (!session->start_consumer) { + ret = LTTCOMM_NO_CONSUMER; + goto error; + } + + /* + * This case switch makes sure the domain session has a temporary consumer + * so the URL can be set. + */ + switch (domain) { + case 0: + /* Code flow error. A session MUST always have a consumer object */ + assert(session->consumer); + /* + * The URL will be added to the tracing session consumer instead of a + * specific domain consumer. + */ + consumer = session->consumer; + break; + case LTTNG_DOMAIN_KERNEL: + /* Code flow error if we don't have a kernel session here. */ + assert(ksess); + + /* Create consumer output if none exists */ + consumer = ksess->tmp_consumer; + if (consumer == NULL) { + consumer = consumer_copy_output(ksess->consumer); + if (consumer == NULL) { + ret = LTTCOMM_FATAL; + goto error; + } + /* Trash the consumer subdir, we are about to set a new one. */ + memset(consumer->subdir, 0, sizeof(consumer->subdir)); + ksess->tmp_consumer = consumer; + } + + break; + case LTTNG_DOMAIN_UST: + /* Code flow error if we don't have a kernel session here. */ + assert(usess); + + /* Create consumer output if none exists */ + consumer = usess->tmp_consumer; + if (consumer == NULL) { + consumer = consumer_copy_output(usess->consumer); + if (consumer == NULL) { + ret = LTTCOMM_FATAL; + goto error; + } + /* Trash the consumer subdir, we are about to set a new one. */ + memset(consumer->subdir, 0, sizeof(consumer->subdir)); + usess->tmp_consumer = consumer; + } + + break; + } + + for (i = 0; i < nb_uri; i++) { + struct consumer_socket *socket; + struct lttng_ht_iter iter; + + ret = add_uri_to_consumer(consumer, &uris[i], domain, session->name); + if (ret < 0) { + goto error; + } + + /* + * Don't send relayd socket if URI is NOT remote or if the relayd + * sockets for the session are already sent. + */ + if (uris[i].dtype == LTTNG_DST_PATH || + consumer->dst.net.relayd_socks_sent) { + continue; + } + + /* Try to send relayd URI to the consumer if exist. */ + rcu_read_lock(); + cds_lfht_for_each_entry(consumer->socks->ht, &iter.iter, + socket, node.node) { + + /* A socket in the HT should never have a negative fd */ + assert(socket->fd >= 0); + + pthread_mutex_lock(socket->lock); + ret = send_consumer_relayd_socket(domain, session, &uris[i], + consumer, socket->fd); + pthread_mutex_unlock(socket->lock); + if (ret != LTTCOMM_OK) { + rcu_read_unlock(); + goto error; + } + } + rcu_read_unlock(); + } + + /* All good! */ + ret = LTTCOMM_OK; + +error: + return ret; +} + +/* + * Command LTTNG_CREATE_SESSION processed by the client thread. + */ +int cmd_create_session_uri(char *name, struct lttng_uri *uris, + size_t nb_uri, lttng_sock_cred *creds) +{ + int ret; + char *path = NULL; + struct ltt_session *session; + + assert(name); + + /* + * Verify if the session already exist + * + * XXX: There is no need for the session lock list here since the caller + * (process_client_msg) is holding it. We might want to change that so a + * single command does not lock the entire session list. + */ + session = session_find_by_name(name); + if (session != NULL) { + ret = LTTCOMM_EXIST_SESS; + goto find_error; + } + + /* Create tracing session in the registry */ + ret = session_create(name, path, LTTNG_SOCK_GET_UID_CRED(creds), + LTTNG_SOCK_GET_GID_CRED(creds)); + if (ret != LTTCOMM_OK) { + goto session_error; + } + + /* + * Get the newly created session pointer back + * + * XXX: There is no need for the session lock list here since the caller + * (process_client_msg) is holding it. We might want to change that so a + * single command does not lock the entire session list. + */ + session = session_find_by_name(name); + assert(session); + + /* Create default consumer output for the session not yet created. */ + session->consumer = consumer_create_output(CONSUMER_DST_LOCAL); + if (session->consumer == NULL) { + ret = LTTCOMM_FATAL; + goto consumer_error; + } + + /* + * This means that the lttng_create_session call was called with the _path_ + * argument set to NULL. + */ + if (uris == NULL) { + /* + * At this point, we'll skip the consumer URI setup and create a + * session with a NULL path which will flag the session to NOT spawn a + * consumer. + */ + DBG("Create session %s with NO uri, skipping consumer setup", name); + goto end; + } + + session->start_consumer = 1; + + ret = cmd_set_consumer_uri(0, session, nb_uri, uris); + if (ret != LTTCOMM_OK) { + goto consumer_error; + } + + session->consumer->enabled = 1; + +end: + return LTTCOMM_OK; + +consumer_error: + session_destroy(session); +session_error: +find_error: + return ret; +} + +/* + * Command LTTNG_DESTROY_SESSION processed by the client thread. + */ +int cmd_destroy_session(struct ltt_session *session, int wpipe) +{ + int ret; + struct ltt_ust_session *usess; + struct ltt_kernel_session *ksess; + + /* Safety net */ + assert(session); + + usess = session->ust_session; + ksess = session->kernel_session; + + /* Clean kernel session teardown */ + kernel_destroy_session(ksess); + + /* UST session teardown */ + if (usess) { + /* Close any relayd session */ + consumer_output_send_destroy_relayd(usess->consumer); + + /* Destroy every UST application related to this session. */ + ret = ust_app_destroy_trace_all(usess); + if (ret) { + ERR("Error in ust_app_destroy_trace_all"); + } + + /* Clean up the rest. */ + trace_ust_destroy_session(usess); + } + + /* + * Must notify the kernel thread here to update it's poll set in order to + * remove the channel(s)' fd just destroyed. + */ + ret = notify_thread_pipe(wpipe); + if (ret < 0) { + PERROR("write kernel poll pipe"); + } + + ret = session_destroy(session); + + return ret; +} + +/* + * Command LTTNG_CALIBRATE processed by the client thread. + */ +int cmd_calibrate(int domain, struct lttng_calibrate *calibrate) +{ + int ret; + + switch (domain) { + case LTTNG_DOMAIN_KERNEL: + { + struct lttng_kernel_calibrate kcalibrate; + + kcalibrate.type = calibrate->type; + ret = kernel_calibrate(kernel_tracer_fd, &kcalibrate); + if (ret < 0) { + ret = LTTCOMM_KERN_ENABLE_FAIL; + goto error; + } + break; + } + case LTTNG_DOMAIN_UST: + { + struct lttng_ust_calibrate ucalibrate; + + ucalibrate.type = calibrate->type; + ret = ust_app_calibrate_glb(&ucalibrate); + if (ret < 0) { + ret = LTTCOMM_UST_CALIBRATE_FAIL; + goto error; + } + break; + } + default: + ret = LTTCOMM_UND; + goto error; + } + + ret = LTTCOMM_OK; + +error: + return ret; +} + +/* + * Command LTTNG_REGISTER_CONSUMER processed by the client thread. + */ +int cmd_register_consumer(struct ltt_session *session, int domain, + const char *sock_path, struct consumer_data *cdata) +{ + int ret, sock; + struct consumer_socket *socket; + + assert(session); + assert(cdata); + assert(sock_path); + + switch (domain) { + case LTTNG_DOMAIN_KERNEL: + { + struct ltt_kernel_session *ksess = session->kernel_session; + + assert(ksess); + + /* Can't register a consumer if there is already one */ + if (ksess->consumer_fds_sent != 0) { + ret = LTTCOMM_KERN_CONSUMER_FAIL; + goto error; + } + + sock = lttcomm_connect_unix_sock(sock_path); + if (sock < 0) { + ret = LTTCOMM_CONNECT_FAIL; + goto error; + } + + socket = consumer_allocate_socket(sock); + if (socket == NULL) { + ret = LTTCOMM_FATAL; + close(sock); + goto error; + } + + socket->lock = zmalloc(sizeof(pthread_mutex_t)); + if (socket->lock == NULL) { + PERROR("zmalloc pthread mutex"); + ret = LTTCOMM_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 = LTTCOMM_UND; + goto error; + } + + ret = LTTCOMM_OK; + +error: + 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; + + 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++; + } + + *domains = zmalloc(nb_dom * sizeof(struct lttng_domain)); + if (*domains == NULL) { + ret = -LTTCOMM_FATAL; + goto error; + } + + if (session->kernel_session != NULL) { + (*domains)[index].type = LTTNG_DOMAIN_KERNEL; + index++; + } + + if (session->ust_session != NULL) { + (*domains)[index].type = LTTNG_DOMAIN_UST; + index++; + } + + return nb_dom; + +error: + return ret; +} + + +/* + * Command LTTNG_LIST_CHANNELS processed by the client thread. + */ +ssize_t cmd_list_channels(int domain, struct ltt_session *session, + struct lttng_channel **channels) +{ + int ret; + ssize_t nb_chan = 0; + + 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); + break; + case LTTNG_DOMAIN_UST: + if (session->ust_session != NULL) { + nb_chan = lttng_ht_get_count( + session->ust_session->domain_global.channels); + } + DBG3("Number of UST global channels %zd", nb_chan); + break; + default: + *channels = NULL; + ret = -LTTCOMM_UND; + goto error; + } + + if (nb_chan > 0) { + *channels = zmalloc(nb_chan * sizeof(struct lttng_channel)); + if (*channels == NULL) { + ret = -LTTCOMM_FATAL; + goto error; + } + + list_lttng_channels(domain, session, *channels); + } else { + *channels = NULL; + } + + return nb_chan; + +error: + return ret; +} + +/* + * Command LTTNG_LIST_EVENTS processed by the client thread. + */ +ssize_t cmd_list_events(int domain, struct ltt_session *session, + char *channel_name, struct lttng_event **events) +{ + int ret = 0; + ssize_t nb_event = 0; + + switch (domain) { + case LTTNG_DOMAIN_KERNEL: + if (session->kernel_session != NULL) { + nb_event = list_lttng_kernel_events(channel_name, + session->kernel_session, events); + } + break; + case LTTNG_DOMAIN_UST: + { + if (session->ust_session != NULL) { + nb_event = list_lttng_ust_global_events(channel_name, + &session->ust_session->domain_global, events); + } + break; + } + default: + ret = -LTTCOMM_UND; + goto error; + } + + ret = nb_event; + +error: + 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, uid_t uid, + gid_t gid) +{ + int ret; + unsigned int i = 0; + struct ltt_session *session; + struct ltt_session_list *list = session_get_list(); + + 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) { + /* + * Only list the sessions the user can control. + */ + if (!session_access_ok(session, uid, gid)) { + 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(session[i].path), session); + } else { + ret = snprintf(sessions[i].path, sizeof(session[i].path), "%s", + session->consumer->dst.trace_path); + } + if (ret < 0) { + PERROR("snprintf session path"); + continue; + } + + strncpy(sessions[i].name, session->name, NAME_MAX); + sessions[i].name[NAME_MAX - 1] = '\0'; + sessions[i].enabled = session->enabled; + i++; + } +} + +/* + * Command LTTNG_DISABLE_CONSUMER processed by the client thread. + */ +int cmd_disable_consumer(int domain, struct ltt_session *session) +{ + int ret; + struct ltt_kernel_session *ksess = session->kernel_session; + struct ltt_ust_session *usess = session->ust_session; + struct consumer_output *consumer; + + assert(session); + + if (session->enabled) { + /* Can't disable consumer on an already started session */ + ret = LTTCOMM_TRACE_ALREADY_STARTED; + goto error; + } + + if (!session->start_consumer) { + ret = LTTCOMM_NO_CONSUMER; + goto error; + } + + switch (domain) { + case 0: + DBG("Disable tracing session %s consumer", session->name); + consumer = session->consumer; + break; + case LTTNG_DOMAIN_KERNEL: + /* Code flow error if we don't have a kernel session here. */ + assert(ksess); + + DBG("Disabling kernel consumer"); + consumer = ksess->consumer; + + break; + case LTTNG_DOMAIN_UST: + /* Code flow error if we don't have a UST session here. */ + assert(usess); + + DBG("Disabling UST consumer"); + consumer = usess->consumer; + + break; + default: + ret = LTTCOMM_UNKNOWN_DOMAIN; + goto error; + } + + if (consumer) { + consumer->enabled = 0; + /* Success at this point */ + ret = LTTCOMM_OK; + } else { + ret = LTTCOMM_NO_CONSUMER; + } + +error: + return ret; +} + +/* + * Command LTTNG_ENABLE_CONSUMER processed by the client thread. + */ +int cmd_enable_consumer(int domain, struct ltt_session *session) +{ + int ret; + struct ltt_kernel_session *ksess = session->kernel_session; + struct ltt_ust_session *usess = session->ust_session; + struct consumer_output *consumer = NULL; + + assert(session); + + /* Can't enable consumer after session started. */ + if (session->enabled) { + ret = LTTCOMM_TRACE_ALREADY_STARTED; + goto error; + } + + if (!session->start_consumer) { + ret = LTTCOMM_NO_CONSUMER; + goto error; + } + + switch (domain) { + case 0: + assert(session->consumer); + consumer = session->consumer; + break; + case LTTNG_DOMAIN_KERNEL: + /* Code flow error if we don't have a kernel session here. */ + assert(ksess); + + /* + * Check if we have already sent fds to the consumer. In that case, + * the enable-consumer command can't be used because a start trace + * had previously occured. + */ + if (ksess->consumer_fds_sent) { + ret = LTTCOMM_ENABLE_CONSUMER_FAIL; + goto error; + } + + consumer = ksess->tmp_consumer; + if (consumer == NULL) { + ret = LTTCOMM_OK; + /* No temp. consumer output exists. Using the current one. */ + DBG3("No temporary consumer. Using default"); + consumer = ksess->consumer; + goto error; + } + + switch (consumer->type) { + case CONSUMER_DST_LOCAL: + DBG2("Consumer output is local. Creating directory(ies)"); + + /* Create directory(ies) */ + ret = run_as_mkdir_recursive(consumer->dst.trace_path, + S_IRWXU | S_IRWXG, session->uid, session->gid); + if (ret < 0) { + if (ret != -EEXIST) { + ERR("Trace directory creation error"); + ret = LTTCOMM_FATAL; + goto error; + } + } + break; + case CONSUMER_DST_NET: + DBG2("Consumer output is network. Validating URIs"); + /* Validate if we have both control and data path set. */ + if (!consumer->dst.net.control_isset) { + ret = LTTCOMM_URL_CTRL_MISS; + goto error; + } + + if (!consumer->dst.net.data_isset) { + ret = LTTCOMM_URL_DATA_MISS; + goto error; + } + + /* Check established network session state */ + if (session->net_handle == 0) { + ret = LTTCOMM_ENABLE_CONSUMER_FAIL; + ERR("Session network handle is not set on enable-consumer"); + goto error; + } + + break; + } + + /* Append default kernel trace dir to subdir */ + strncat(ksess->consumer->subdir, DEFAULT_KERNEL_TRACE_DIR, + sizeof(ksess->consumer->subdir)); + + /* + * @session-lock + * This is race free for now since the session lock is acquired before + * ending up in this function. No other threads can access this kernel + * session without this lock hence freeing the consumer output object + * is valid. + */ + rcu_read_lock(); + consumer_destroy_output(ksess->consumer); + rcu_read_unlock(); + ksess->consumer = consumer; + ksess->tmp_consumer = NULL; + + break; + case LTTNG_DOMAIN_UST: + /* Code flow error if we don't have a UST session here. */ + assert(usess); + + /* + * Check if we have already sent fds to the consumer. In that case, + * the enable-consumer command can't be used because a start trace + * had previously occured. + */ + if (usess->start_trace) { + ret = LTTCOMM_ENABLE_CONSUMER_FAIL; + goto error; + } + + consumer = usess->tmp_consumer; + if (consumer == NULL) { + ret = LTTCOMM_OK; + /* No temp. consumer output exists. Using the current one. */ + DBG3("No temporary consumer. Using default"); + consumer = usess->consumer; + goto error; + } + + switch (consumer->type) { + case CONSUMER_DST_LOCAL: + DBG2("Consumer output is local. Creating directory(ies)"); + + /* Create directory(ies) */ + ret = run_as_mkdir_recursive(consumer->dst.trace_path, + S_IRWXU | S_IRWXG, session->uid, session->gid); + if (ret < 0) { + if (ret != -EEXIST) { + ERR("Trace directory creation error"); + ret = LTTCOMM_FATAL; + goto error; + } + } + break; + case CONSUMER_DST_NET: + DBG2("Consumer output is network. Validating URIs"); + /* Validate if we have both control and data path set. */ + if (!consumer->dst.net.control_isset) { + ret = LTTCOMM_URL_CTRL_MISS; + goto error; + } + + if (!consumer->dst.net.data_isset) { + ret = LTTCOMM_URL_DATA_MISS; + goto error; + } + + /* Check established network session state */ + if (session->net_handle == 0) { + ret = LTTCOMM_ENABLE_CONSUMER_FAIL; + DBG2("Session network handle is not set on enable-consumer"); + goto error; + } + + if (consumer->net_seq_index == -1) { + ret = LTTCOMM_ENABLE_CONSUMER_FAIL; + DBG2("Network index is not set on the consumer"); + goto error; + } + + break; + } + + /* Append default kernel trace dir to subdir */ + strncat(usess->consumer->subdir, DEFAULT_UST_TRACE_DIR, + sizeof(usess->consumer->subdir)); + + /* + * @session-lock + * This is race free for now since the session lock is acquired before + * ending up in this function. No other threads can access this kernel + * session without this lock hence freeing the consumer output object + * is valid. + */ + rcu_read_lock(); + consumer_destroy_output(usess->consumer); + rcu_read_unlock(); + usess->consumer = consumer; + usess->tmp_consumer = NULL; + + break; + } + + /* Enable it */ + if (consumer) { + consumer->enabled = 1; + /* Success at this point */ + ret = LTTCOMM_OK; + } else { + /* Should not really happend... */ + ret = LTTCOMM_NO_CONSUMER; + } + +error: + return ret; +} + +/* + * Init command subsystem. + */ +void cmd_init(void) +{ + /* + * Set network sequence index to 1 for streams to match a relayd socket on + * the consumer side. + */ + uatomic_set(&relayd_net_seq_idx, 1); + + DBG("Command subsystem initialized"); +} diff --git a/src/bin/lttng-sessiond/cmd.h b/src/bin/lttng-sessiond/cmd.h new file mode 100644 index 000000000..850c881fb --- /dev/null +++ b/src/bin/lttng-sessiond/cmd.h @@ -0,0 +1,86 @@ +/* + * Copyright (C) 2012 - David Goulet + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License, version 2 only, as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., 51 + * Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#ifndef CMD_H +#define CMD_H + +#include "context.h" +#include "filter.h" +#include "session.h" + +/* + * Init the command subsystem. Must be called before using any of the functions + * above. This is called in the main() of the session daemon. + */ +void cmd_init(void); + +/* Session commands */ +int cmd_create_session_uri(char *name, struct lttng_uri *uris, + size_t nb_uri, lttng_sock_cred *creds); +int cmd_destroy_session(struct ltt_session *session, int wpipe); + +/* Channel commands */ +int cmd_disable_channel(struct ltt_session *session, int domain, + char *channel_name); +int cmd_enable_channel(struct ltt_session *session, int domain, + struct lttng_channel *attr, int wpipe); + +/* Event commands */ +int cmd_disable_event(struct ltt_session *session, int domain, + char *channel_name, char *event_name); +int cmd_disable_event_all(struct ltt_session *session, int domain, + char *channel_name); +int cmd_add_context(struct ltt_session *session, int domain, + char *channel_name, char *event_name, struct lttng_event_context *ctx); +int cmd_set_filter(struct ltt_session *session, int domain, + char *channel_name, char *event_name, + struct lttng_filter_bytecode *bytecode); +int cmd_enable_event(struct ltt_session *session, int domain, + char *channel_name, struct lttng_event *event, int wpipe); +int cmd_enable_event_all(struct ltt_session *session, int domain, + char *channel_name, int event_type, int wpipe); + +/* Trace session action commands */ +int cmd_start_trace(struct ltt_session *session); +int cmd_stop_trace(struct ltt_session *session); + +/* Consumer commands */ +int cmd_register_consumer(struct ltt_session *session, int domain, + const char *sock_path, struct consumer_data *cdata); +int cmd_disable_consumer(int domain, struct ltt_session *session); +int cmd_enable_consumer(int domain, struct ltt_session *session); +int cmd_set_consumer_uri(int domain, struct ltt_session *session, + size_t nb_uri, struct lttng_uri *uris); + +/* Listing commands */ +ssize_t cmd_list_domains(struct ltt_session *session, + struct lttng_domain **domains); +ssize_t cmd_list_events(int domain, struct ltt_session *session, + char *channel_name, struct lttng_event **events); +ssize_t cmd_list_channels(int domain, struct ltt_session *session, + struct lttng_channel **channels); +ssize_t cmd_list_domains(struct ltt_session *session, + struct lttng_domain **domains); +void cmd_list_lttng_sessions(struct lttng_session *sessions, uid_t uid, + gid_t gid); +ssize_t cmd_list_tracepoint_fields(int domain, + struct lttng_event_field **fields); +ssize_t cmd_list_tracepoints(int domain, struct lttng_event **events); + +int cmd_calibrate(int domain, struct lttng_calibrate *calibrate); + +#endif /* CMD_H */ diff --git a/src/bin/lttng-sessiond/consumer.c b/src/bin/lttng-sessiond/consumer.c index 3503e0415..c9197bd8e 100644 --- a/src/bin/lttng-sessiond/consumer.c +++ b/src/bin/lttng-sessiond/consumer.c @@ -30,6 +30,74 @@ #include "consumer.h" +/* + * Send destroy relayd command to consumer. + * + * On success return positive value. On error, negative value. + */ +int consumer_send_destroy_relayd(struct consumer_socket *sock, + struct consumer_output *consumer) +{ + int ret; + struct lttcomm_consumer_msg msg; + + assert(consumer); + assert(sock); + + DBG2("Sending destroy relayd command to consumer..."); + + /* Bail out if consumer is disabled */ + if (!consumer->enabled) { + ret = LTTCOMM_OK; + DBG3("Consumer is disabled"); + goto error; + } + + msg.cmd_type = LTTNG_CONSUMER_DESTROY_RELAYD; + msg.u.destroy_relayd.net_seq_idx = consumer->net_seq_index; + + pthread_mutex_lock(sock->lock); + ret = lttcomm_send_unix_sock(sock->fd, &msg, sizeof(msg)); + pthread_mutex_unlock(sock->lock); + if (ret < 0) { + PERROR("send consumer destroy relayd command"); + goto error; + } + + DBG2("Consumer send destroy relayd command done"); + +error: + return ret; +} + +/* + * For each consumer socket in the consumer output object, send a destroy + * relayd command. + */ +void consumer_output_send_destroy_relayd(struct consumer_output *consumer) +{ + int ret; + struct lttng_ht_iter iter; + struct consumer_socket *socket; + + assert(consumer); + + /* Destroy any relayd connection */ + if (consumer && consumer->type == CONSUMER_DST_NET) { + rcu_read_lock(); + cds_lfht_for_each_entry(consumer->socks->ht, &iter.iter, socket, + node.node) { + /* Send destroy relayd command */ + ret = consumer_send_destroy_relayd(socket, consumer); + if (ret < 0) { + ERR("Unable to send destroy relayd command to consumer"); + /* Continue since we MUST delete everything at this point. */ + } + } + rcu_read_unlock(); + } +} + /* * From a consumer_data structure, allocate and add a consumer socket to the * consumer output. @@ -62,6 +130,7 @@ int consumer_create_socket(struct consumer_data *data, goto error; } + socket->registered = 0; socket->lock = &data->lock; rcu_read_lock(); consumer_add_socket(socket, output); @@ -176,8 +245,13 @@ void consumer_destroy_socket(struct consumer_socket *sock) /* * We DO NOT close the file descriptor here since it is global to the - * session daemon and is closed only if the consumer dies. + * session daemon and is closed only if the consumer dies or a custom + * consumer was registered, */ + if (sock->registered) { + DBG3("Consumer socket was registered. Closing fd %d", sock->fd); + lttcomm_close_unix_sock(sock->fd); + } call_rcu(&sock->node.head, destroy_socket_rcu); } @@ -221,9 +295,15 @@ void consumer_destroy_output(struct consumer_output *obj) struct lttng_ht_iter iter; struct consumer_socket *socket; + rcu_read_lock(); cds_lfht_for_each_entry(obj->socks->ht, &iter.iter, socket, node.node) { + consumer_del_socket(socket, obj); consumer_destroy_socket(socket); } + rcu_read_unlock(); + + /* Finally destroy HT */ + lttng_ht_destroy(obj->socks); } free(obj); @@ -562,40 +642,48 @@ error: } /* - * Send destroy relayd command to consumer. - * - * On success return positive value. On error, negative value. + * Set consumer subdirectory using the session name and a generated datetime if + * needed. This is appended to the current subdirectory. */ -int consumer_send_destroy_relayd(struct consumer_socket *sock, - struct consumer_output *consumer) +int consumer_set_subdir(struct consumer_output *consumer, + const char *session_name) { - int ret; - struct lttcomm_consumer_msg msg; + int ret = 0; + unsigned int have_default_name = 0; + char datetime[16], tmp_path[PATH_MAX]; + time_t rawtime; + struct tm *timeinfo; assert(consumer); - assert(sock); - - DBG2("Sending destroy relayd command to consumer..."); - - /* Bail out if consumer is disabled */ - if (!consumer->enabled) { - ret = LTTCOMM_OK; - DBG3("Consumer is disabled"); - goto error; + assert(session_name); + + memset(tmp_path, 0, sizeof(tmp_path)); + + /* Flag if we have a default session. */ + if (strncmp(session_name, DEFAULT_SESSION_NAME "-", + strlen(DEFAULT_SESSION_NAME) + 1) == 0) { + have_default_name = 1; + } else { + /* Get date and time for session path */ + time(&rawtime); + timeinfo = localtime(&rawtime); + strftime(datetime, sizeof(datetime), "%Y%m%d-%H%M%S", timeinfo); } - msg.cmd_type = LTTNG_CONSUMER_DESTROY_RELAYD; - msg.u.destroy_relayd.net_seq_idx = consumer->net_seq_index; - - pthread_mutex_lock(sock->lock); - ret = lttcomm_send_unix_sock(sock->fd, &msg, sizeof(msg)); - pthread_mutex_unlock(sock->lock); + if (have_default_name) { + ret = snprintf(tmp_path, sizeof(tmp_path), + "%s/%s", consumer->subdir, session_name); + } else { + ret = snprintf(tmp_path, sizeof(tmp_path), + "%s/%s-%s/", consumer->subdir, session_name, datetime); + } if (ret < 0) { - PERROR("send consumer destroy relayd command"); + PERROR("snprintf session name date"); goto error; } - DBG2("Consumer send destroy relayd command done"); + strncpy(consumer->subdir, tmp_path, sizeof(consumer->subdir)); + DBG2("Consumer subdir set to %s", consumer->subdir); error: return ret; diff --git a/src/bin/lttng-sessiond/consumer.h b/src/bin/lttng-sessiond/consumer.h index c85529cfd..1337f32ac 100644 --- a/src/bin/lttng-sessiond/consumer.h +++ b/src/bin/lttng-sessiond/consumer.h @@ -38,6 +38,15 @@ struct consumer_socket { * To use this socket (send/recv), this lock MUST be acquired. */ pthread_mutex_t *lock; + + /* + * Indicates if the socket was registered by a third part + * (REGISTER_CONSUMER) or is the spawn consumer of the session daemon. + * During the destroy phase of a consumer output, we close the socket if + * this flag is set to 1 since we don't need the fd anymore. + */ + unsigned int registered; + struct lttng_ht_node_ulong node; }; @@ -147,8 +156,11 @@ int consumer_send_relayd_socket(int consumer_sock, enum lttng_stream_type type); int consumer_send_destroy_relayd(struct consumer_socket *sock, struct consumer_output *consumer); +void consumer_output_send_destroy_relayd(struct consumer_output *consumer); int consumer_create_socket(struct consumer_data *data, struct consumer_output *output); +int consumer_set_subdir(struct consumer_output *consumer, + const char *session_name); void consumer_init_stream_comm_msg(struct lttcomm_consumer_msg *msg, enum lttng_consumer_command cmd, diff --git a/src/bin/lttng-sessiond/kernel.c b/src/bin/lttng-sessiond/kernel.c index f73476077..d52d9aa62 100644 --- a/src/bin/lttng-sessiond/kernel.c +++ b/src/bin/lttng-sessiond/kernel.c @@ -28,6 +28,7 @@ #include #include +#include "consumer.h" #include "kernel.h" #include "kern-modules.h" @@ -718,3 +719,21 @@ int init_kernel_workarounds(void) end_boot_id: return 0; } + +/* + * Complete teardown of a kernel session. + */ +void kernel_destroy_session(struct ltt_kernel_session *ksess) +{ + if (ksess == NULL) { + DBG3("No kernel session when tearing down session"); + return; + } + + DBG("Tearing down kernel session"); + + /* Close any relayd session */ + consumer_output_send_destroy_relayd(ksess->consumer); + + trace_kernel_destroy_session(ksess); +} diff --git a/src/bin/lttng-sessiond/kernel.h b/src/bin/lttng-sessiond/kernel.h index 4591e48f3..35f7368d1 100644 --- a/src/bin/lttng-sessiond/kernel.h +++ b/src/bin/lttng-sessiond/kernel.h @@ -53,6 +53,7 @@ ssize_t kernel_list_events(int tracer_fd, struct lttng_event **event_list); void kernel_wait_quiescent(int fd); int kernel_calibrate(int fd, struct lttng_kernel_calibrate *calibrate); int kernel_validate_version(int tracer_fd); +void kernel_destroy_session(struct ltt_kernel_session *ksess); int init_kernel_workarounds(void); diff --git a/src/bin/lttng-sessiond/lttng-sessiond.h b/src/bin/lttng-sessiond/lttng-sessiond.h index 32963044f..63e9be998 100644 --- a/src/bin/lttng-sessiond/lttng-sessiond.h +++ b/src/bin/lttng-sessiond/lttng-sessiond.h @@ -28,13 +28,14 @@ #include "session.h" #include "ust-app.h" -const char *module_proc_lttng = "/proc/lttng"; - extern const char default_home_dir[], default_tracing_group[], default_ust_sock_dir[], default_global_apps_pipe[]; +/* Set in main.c at boot time of the daemon */ +extern int kernel_tracer_fd; + /* * This contains extra data needed for processing a command received by the * session daemon from the lttng client. diff --git a/src/bin/lttng-sessiond/main.c b/src/bin/lttng-sessiond/main.c index c952fc0a9..c0cfddb96 100644 --- a/src/bin/lttng-sessiond/main.c +++ b/src/bin/lttng-sessiond/main.c @@ -48,6 +48,7 @@ #include "lttng-sessiond.h" #include "channel.h" +#include "cmd.h" #include "consumer.h" #include "context.h" #include "event.h" @@ -124,7 +125,7 @@ static char health_unix_sock_path[PATH_MAX]; /* Sockets and FDs */ static int client_sock = -1; static int apps_sock = -1; -static int kernel_tracer_fd = -1; +int kernel_tracer_fd = -1; static int kernel_poll_pipe[2] = { -1, -1 }; /* @@ -176,6 +177,8 @@ static const char *consumerd64_bin = CONFIG_CONSUMERD64_BIN; static const char *consumerd32_libdir = CONFIG_CONSUMERD32_LIBDIR; static const char *consumerd64_libdir = CONFIG_CONSUMERD64_LIBDIR; +static const char *module_proc_lttng = "/proc/lttng"; + /* * Consumer daemon state which is changed when spawning it, killing it or in * case of a fatal error. @@ -210,16 +213,6 @@ enum consumerd_state { static enum consumerd_state ust_consumerd_state; static enum consumerd_state kernel_consumerd_state; -/* - * 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. - * - * This value should be incremented atomically for safety purposes and future - * possible concurrent access. - */ -static unsigned int relayd_net_seq_idx; - /* Used for the health monitoring of the session daemon. See health.h */ struct health_state health_thread_cmd; struct health_state health_thread_app_manage; @@ -365,111 +358,6 @@ error: return ret; } -/* - * Complete teardown of a kernel session. This free all data structure related - * to a kernel session and update counter. - */ -static void teardown_kernel_session(struct ltt_session *session) -{ - int ret; - struct lttng_ht_iter iter; - struct ltt_kernel_session *ksess; - struct consumer_socket *socket; - - if (!session->kernel_session) { - DBG3("No kernel session when tearing down session"); - return; - } - - ksess = session->kernel_session; - - DBG("Tearing down kernel session"); - - /* - * Destroy relayd associated with the session consumer. This action is - * valid since in order to destroy a session we must acquire the session - * lock. This means that there CAN NOT be stream(s) being sent to a - * consumer since this action also requires the session lock at any time. - * - * At this point, we are sure that not streams data will be lost after this - * command is issued. - */ - if (ksess->consumer && ksess->consumer->type == CONSUMER_DST_NET) { - cds_lfht_for_each_entry(ksess->consumer->socks->ht, &iter.iter, socket, - node.node) { - ret = consumer_send_destroy_relayd(socket, ksess->consumer); - if (ret < 0) { - ERR("Unable to send destroy relayd command to consumer"); - /* Continue since we MUST delete everything at this point. */ - } - } - } - - /* - * If a custom kernel consumer was registered, close the socket before - * tearing down the complete kernel session structure - */ - cds_lfht_for_each_entry(ksess->consumer->socks->ht, &iter.iter, socket, - node.node) { - if (socket->fd != kconsumer_data.cmd_sock) { - rcu_read_lock(); - consumer_del_socket(socket, ksess->consumer); - lttcomm_close_unix_sock(socket->fd); - consumer_destroy_socket(socket); - rcu_read_unlock(); - } - } - - trace_kernel_destroy_session(ksess); -} - -/* - * Complete teardown of all UST sessions. This will free everything on his path - * and destroy the core essence of all ust sessions :) - */ -static void teardown_ust_session(struct ltt_session *session) -{ - int ret; - struct lttng_ht_iter iter; - struct ltt_ust_session *usess; - struct consumer_socket *socket; - - if (!session->ust_session) { - DBG3("No UST session when tearing down session"); - return; - } - usess = session->ust_session; - - DBG("Tearing down UST session(s)"); - - /* - * Destroy relayd associated with the session consumer. This action is - * valid since in order to destroy a session we must acquire the session - * lock. This means that there CAN NOT be stream(s) being sent to a - * consumer since this action also requires the session lock at any time. - * - * At this point, we are sure that no data will be lost after this command - * is issued. - */ - if (usess->consumer && usess->consumer->type == CONSUMER_DST_NET) { - cds_lfht_for_each_entry(usess->consumer->socks->ht, &iter.iter, socket, - node.node) { - ret = consumer_send_destroy_relayd(socket, usess->consumer); - if (ret < 0) { - ERR("Unable to send destroy relayd command to consumer"); - /* Continue since we MUST delete everything at this point. */ - } - } - } - - ret = ust_app_destroy_trace_all(usess); - if (ret) { - ERR("Error in ust_app_destroy_trace_all"); - } - - trace_ust_destroy_session(usess); -} - /* * Stop all threads by closing the thread quit pipe. */ @@ -500,6 +388,9 @@ static void cleanup(void) DBG("Cleaning up"); + /* First thing first, stop all threads */ + utils_close_pipe(thread_quit_pipe); + DBG("Removing %s directory", rundir); ret = asprintf(&cmd, "rm -rf %s", rundir); if (ret < 0) { @@ -522,9 +413,7 @@ static void cleanup(void) /* Cleanup ALL session */ cds_list_for_each_entry_safe(sess, stmp, &session_list_ptr->head, list) { - teardown_kernel_session(sess); - teardown_ust_session(sess); - free(sess); + cmd_destroy_session(sess, kernel_poll_pipe[1]); } } @@ -542,8 +431,8 @@ static void cleanup(void) DBG("Unloading kernel modules"); modprobe_remove_lttng_all(); } + utils_close_pipe(kernel_poll_pipe); - utils_close_pipe(thread_quit_pipe); utils_close_pipe(apps_cmd_pipe); /* */ @@ -1860,14 +1749,6 @@ static int check_consumer_health(void) return ret; } -/* - * Check version of the lttng-modules. - */ -static int validate_lttng_modules_version(void) -{ - return kernel_validate_version(kernel_tracer_fd); -} - /* * Setup necessary data for kernel tracer action. */ @@ -1885,2493 +1766,222 @@ static int init_kernel_tracer(void) kernel_tracer_fd = open(module_proc_lttng, O_RDWR); if (kernel_tracer_fd < 0) { DBG("Failed to open %s", module_proc_lttng); - ret = -1; - goto error_open; - } - - /* Validate kernel version */ - ret = validate_lttng_modules_version(); - if (ret < 0) { - goto error_version; - } - - ret = modprobe_lttng_data(); - if (ret < 0) { - goto error_modules; - } - - DBG("Kernel tracer fd %d", kernel_tracer_fd); - return 0; - -error_version: - modprobe_remove_lttng_control(); - ret = close(kernel_tracer_fd); - if (ret) { - PERROR("close"); - } - kernel_tracer_fd = -1; - return LTTCOMM_KERN_VERSION; - -error_modules: - ret = close(kernel_tracer_fd); - if (ret) { - PERROR("close"); - } - -error_open: - modprobe_remove_lttng_control(); - -error: - WARN("No kernel tracer available"); - kernel_tracer_fd = -1; - if (!is_root) { - return LTTCOMM_NEED_ROOT_SESSIOND; - } else { - return LTTCOMM_KERN_NA; - } -} - -/* - * 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; - - assert(session); - - if (session->consumer_fds_sent == 0 && session->consumer != NULL) { - cds_lfht_for_each_entry(session->consumer->socks->ht, &iter.iter, - socket, node.node) { - /* Code flow error */ - assert(socket->fd >= 0); - - pthread_mutex_lock(socket->lock); - ret = kernel_consumer_send_session(socket->fd, session); - pthread_mutex_unlock(socket->lock); - if (ret < 0) { - ret = LTTCOMM_KERN_CONSUMER_FAIL; - goto error; - } - } - } - -error: - 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 is untouched and an lttcomm error code is returned. - */ -static int create_connect_relayd(struct consumer_output *output, - const char *session_name, struct lttng_uri *uri, - struct lttcomm_sock **relayd_sock) -{ - int ret; - struct lttcomm_sock *sock; - - /* Create socket object from URI */ - sock = lttcomm_alloc_sock_from_uri(uri); - if (sock == NULL) { - ret = LTTCOMM_FATAL; - goto error; - } - - ret = lttcomm_create_sock(sock); - if (ret < 0) { - ret = LTTCOMM_FATAL; - goto error; - } - - /* Connect to relayd so we can proceed with a session creation. */ - ret = relayd_connect(sock); - if (ret < 0) { - ERR("Unable to reach lttng-relayd"); - ret = LTTCOMM_RELAYD_SESSION_FAIL; - goto free_sock; - } - - /* Create socket for control stream. */ - if (uri->stype == LTTNG_STREAM_CONTROL) { - DBG3("Creating relayd stream socket from URI"); - - /* Check relayd version */ - ret = relayd_version_check(sock, RELAYD_VERSION_COMM_MAJOR, - RELAYD_VERSION_COMM_MINOR); - if (ret < 0) { - ret = LTTCOMM_RELAYD_VERSION_FAIL; - goto close_sock; - } - } 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); - ret = LTTCOMM_INVALID; - goto close_sock; - } - - *relayd_sock = sock; - - return LTTCOMM_OK; - -close_sock: - if (sock) { - (void) relayd_close(sock); - } -free_sock: - if (sock) { - lttcomm_destroy_sock(sock); - } -error: - return ret; -} - -/* - * Connect to the relayd using URI and send the socket to the right consumer. - */ -static int send_socket_relayd_consumer(int domain, struct ltt_session *session, - struct lttng_uri *relayd_uri, struct consumer_output *consumer, - int consumer_fd) -{ - int ret; - struct lttcomm_sock *sock = NULL; - - /* Set the network sequence index if not set. */ - if (consumer->net_seq_index == -1) { - /* - * Increment net_seq_idx because we are about to transfer the - * new relayd socket to the consumer. - */ - uatomic_inc(&relayd_net_seq_idx); - /* Assign unique key so the consumer can match streams */ - consumer->net_seq_index = uatomic_read(&relayd_net_seq_idx); - } - - /* Connect to relayd and make version check if uri is the control. */ - ret = create_connect_relayd(consumer, session->name, relayd_uri, &sock); - if (ret != LTTCOMM_OK) { - goto close_sock; - } - - /* If the control socket is connected, network session is ready */ - if (relayd_uri->stype == LTTNG_STREAM_CONTROL) { - session->net_handle = 1; - } - - /* Send relayd socket to consumer. */ - ret = consumer_send_relayd_socket(consumer_fd, sock, - consumer, relayd_uri->stype); - if (ret < 0) { - ret = LTTCOMM_ENABLE_CONSUMER_FAIL; - goto close_sock; - } - - ret = LTTCOMM_OK; - - /* - * 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 (sock) { - (void) relayd_close(sock); - lttcomm_destroy_sock(sock); - } - - return ret; -} - -/* - * 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. - */ -static int send_sockets_relayd_consumer(int domain, - struct ltt_session *session, struct consumer_output *consumer, int fd) -{ - int ret; - - assert(session); - assert(consumer); - - /* Don't resend the sockets to the consumer. */ - if (consumer->dst.net.relayd_socks_sent) { - ret = LTTCOMM_OK; - goto error; - } - - /* Sending control relayd socket. */ - ret = send_socket_relayd_consumer(domain, session, - &consumer->dst.net.control, consumer, fd); - if (ret != LTTCOMM_OK) { - goto error; - } - - /* Sending data relayd socket. */ - ret = send_socket_relayd_consumer(domain, session, - &consumer->dst.net.data, consumer, fd); - if (ret != LTTCOMM_OK) { - goto error; - } - - /* Flag that all relayd sockets were sent to the consumer. */ - consumer->dst.net.relayd_socks_sent = 1; - -error: - return ret; -} - -/* - * 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. - */ -static int setup_relayd(struct ltt_session *session) -{ - int ret = LTTCOMM_OK; - struct ltt_ust_session *usess; - struct ltt_kernel_session *ksess; - struct consumer_socket *socket; - struct lttng_ht_iter iter; - - assert(session); - - usess = session->ust_session; - ksess = session->kernel_session; - - DBG2("Setting relayd for session %s", session->name); - - 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) { - /* Code flow error */ - assert(socket->fd >= 0); - - pthread_mutex_lock(socket->lock); - send_sockets_relayd_consumer(LTTNG_DOMAIN_UST, session, - usess->consumer, socket->fd); - pthread_mutex_unlock(socket->lock); - if (ret != LTTCOMM_OK) { - goto error; - } - } - } - - 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) { - /* Code flow error */ - assert(socket->fd >= 0); - - pthread_mutex_lock(socket->lock); - send_sockets_relayd_consumer(LTTNG_DOMAIN_KERNEL, session, - ksess->consumer, socket->fd); - pthread_mutex_unlock(socket->lock); - if (ret != LTTCOMM_OK) { - goto error; - } - } - } - -error: - return ret; -} - -/* - * Set consumer subdirectory using the session name and a generated datetime if - * needed. This is appended to the current subdirectory. - */ -static int set_consumer_subdir(struct consumer_output *consumer, - const char *session_name) -{ - int ret = 0; - unsigned int have_default_name = 0; - char datetime[16], tmp_path[PATH_MAX]; - time_t rawtime; - struct tm *timeinfo; - - assert(consumer); - assert(session_name); - - memset(tmp_path, 0, sizeof(tmp_path)); - - /* Flag if we have a default session. */ - if (strncmp(session_name, DEFAULT_SESSION_NAME "-", - strlen(DEFAULT_SESSION_NAME) + 1) == 0) { - have_default_name = 1; - } else { - /* Get date and time for session path */ - time(&rawtime); - timeinfo = localtime(&rawtime); - strftime(datetime, sizeof(datetime), "%Y%m%d-%H%M%S", timeinfo); - } - - if (have_default_name) { - ret = snprintf(tmp_path, sizeof(tmp_path), - "%s/%s", consumer->subdir, session_name); - } else { - ret = snprintf(tmp_path, sizeof(tmp_path), - "%s/%s-%s/", consumer->subdir, session_name, datetime); - } - if (ret < 0) { - PERROR("snprintf session name date"); - goto error; - } - - strncpy(consumer->subdir, tmp_path, sizeof(consumer->subdir)); - DBG2("Consumer subdir set to %s", consumer->subdir); - -error: - return ret; -} - -/* - * Copy consumer output from the tracing session to the domain session. The - * function also applies the right modification on a per domain basis for the - * trace files destination directory. - */ -static int copy_session_consumer(int domain, struct ltt_session *session) -{ - int ret; - const char *dir_name; - struct consumer_output *consumer; - - assert(session); - assert(session->consumer); - - switch (domain) { - case LTTNG_DOMAIN_KERNEL: - DBG3("Copying tracing session consumer output in kernel session"); - session->kernel_session->consumer = - consumer_copy_output(session->consumer); - /* Ease our life a bit for the next part */ - consumer = session->kernel_session->consumer; - dir_name = DEFAULT_KERNEL_TRACE_DIR; - break; - case LTTNG_DOMAIN_UST: - DBG3("Copying tracing session consumer output in UST session"); - session->ust_session->consumer = - consumer_copy_output(session->consumer); - /* Ease our life a bit for the next part */ - consumer = session->ust_session->consumer; - dir_name = DEFAULT_UST_TRACE_DIR; - break; - default: - ret = LTTCOMM_UNKNOWN_DOMAIN; - goto error; - } - - ret = set_consumer_subdir(session->consumer, session->name); - if (ret < 0) { - ret = LTTCOMM_FATAL; - goto error; - } - - /* Append correct directory to subdir */ - strncat(consumer->subdir, dir_name, sizeof(consumer->subdir)); - DBG3("Copy session consumer subdir %s", consumer->subdir); - - ret = LTTCOMM_OK; - -error: - return ret; -} - -/* - * Create an UST session and add it to the session ust list. - */ -static int create_ust_session(struct ltt_session *session, - struct lttng_domain *domain) -{ - int ret; - struct ltt_ust_session *lus = NULL; - - assert(session); - assert(domain); - assert(session->consumer); - - switch (domain->type) { - case LTTNG_DOMAIN_UST: - break; - default: - ERR("Unknown UST domain on create session %d", domain->type); - ret = LTTCOMM_UNKNOWN_DOMAIN; - goto error; - } - - DBG("Creating UST session"); - - lus = trace_ust_create_session(session->path, session->id, domain); - if (lus == NULL) { - ret = LTTCOMM_UST_SESS_FAIL; - goto error; - } - - lus->uid = session->uid; - lus->gid = session->gid; - session->ust_session = lus; - - /* Copy session output to the newly created UST session */ - ret = copy_session_consumer(domain->type, session); - if (ret != LTTCOMM_OK) { - goto error; - } - - return LTTCOMM_OK; - -error: - free(lus); - session->ust_session = NULL; - return ret; -} - -/* - * Create a kernel tracer session then create the default channel. - */ -static int create_kernel_session(struct ltt_session *session) -{ - int ret; - - DBG("Creating kernel session"); - - ret = kernel_create_session(session, kernel_tracer_fd); - if (ret < 0) { - ret = LTTCOMM_KERN_SESS_FAIL; - goto error; - } - - /* Code flow safety */ - assert(session->kernel_session); - - /* Copy session output to the newly created Kernel session */ - ret = copy_session_consumer(LTTNG_DOMAIN_KERNEL, session); - if (ret != LTTCOMM_OK) { - goto error; - } - - /* Create directory(ies) on local filesystem. */ - if (session->kernel_session->consumer->type == CONSUMER_DST_LOCAL && - strlen(session->kernel_session->consumer->dst.trace_path) > 0) { - ret = run_as_mkdir_recursive( - session->kernel_session->consumer->dst.trace_path, - S_IRWXU | S_IRWXG, session->uid, session->gid); - if (ret < 0) { - if (ret != -EEXIST) { - ERR("Trace directory creation error"); - goto error; - } - } - } - - session->kernel_session->uid = session->uid; - session->kernel_session->gid = session->gid; - - return LTTCOMM_OK; - -error: - trace_kernel_destroy_session(session->kernel_session); - session->kernel_session = NULL; - return ret; -} - -/* - * Check if the UID or GID match the session. Root user has access to all - * sessions. - */ -static int session_access_ok(struct ltt_session *session, uid_t uid, gid_t gid) -{ - if (uid != session->uid && gid != session->gid && uid != 0) { - return 0; - } else { - return 1; - } -} - -/* - * Count number of session permitted by uid/gid. - */ -static unsigned int lttng_sessions_count(uid_t uid, gid_t gid) -{ - unsigned int i = 0; - struct ltt_session *session; - - DBG("Counting number of available session for UID %d GID %d", - uid, gid); - cds_list_for_each_entry(session, &session_list_ptr->head, list) { - /* - * Only list the sessions the user can control. - */ - if (!session_access_ok(session, uid, gid)) { - continue; - } - i++; - } - return i; -} - -/* - * 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]; - - assert(session); - 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; - } - - if (strlen(tmp_uurl) > 0) { - ret = snprintf(dst, size, "[K]: %s [data: %d] -- [U]: %s [data: %d]", - tmp_urls, kdata_port, tmp_uurl, udata_port); - } else { - ret = snprintf(dst, size, "%s [data: %d]", tmp_urls, kdata_port); - } - -error: - 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(). - */ -static void list_lttng_sessions(struct lttng_session *sessions, uid_t uid, - gid_t gid) -{ - int ret; - unsigned int i = 0; - struct ltt_session *session; - - 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, &session_list_ptr->head, list) { - /* - * Only list the sessions the user can control. - */ - if (!session_access_ok(session, uid, gid)) { - 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(session[i].path), session); - } else { - ret = snprintf(sessions[i].path, sizeof(session[i].path), "%s", - session->consumer->dst.trace_path); - } - if (ret < 0) { - PERROR("snprintf session path"); - continue; - } - - strncpy(sessions[i].name, session->name, NAME_MAX); - sessions[i].name[NAME_MAX - 1] = '\0'; - sessions[i].enabled = session->enabled; - i++; - } -} - -/* - * Fill lttng_channel array of all channels. - */ -static void list_lttng_channels(int domain, struct ltt_session *session, - struct lttng_channel *channels) -{ - int i = 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) { - /* Copy lttng_channel struct to array */ - memcpy(&channels[i], kchan->channel, sizeof(struct lttng_channel)); - channels[i].enabled = kchan->enabled; - i++; - } - } - break; - case LTTNG_DOMAIN_UST: - { - struct lttng_ht_iter iter; - struct ltt_ust_channel *uchan; - - cds_lfht_for_each_entry(session->ust_session->domain_global.channels->ht, - &iter.iter, uchan, node.node) { - strncpy(channels[i].name, uchan->name, LTTNG_SYMBOL_NAME_LEN); - 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; - switch (uchan->attr.output) { - case LTTNG_UST_MMAP: - default: - channels[i].attr.output = LTTNG_EVENT_MMAP; - break; - } - i++; - } - break; - } - default: - break; - } -} - -/* - * 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_event **events) -{ - int i = 0, ret = 0; - unsigned int nb_event = 0; - struct lttng_ht_iter iter; - struct lttng_ht_node_str *node; - struct ltt_ust_channel *uchan; - struct ltt_ust_event *uevent; - struct lttng_event *tmp; - - 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 = -LTTCOMM_UST_CHAN_NOT_FOUND; - goto error; - } - - uchan = caa_container_of(&node->node, struct ltt_ust_channel, node.node); - - nb_event += lttng_ht_get_count(uchan->events); - - if (nb_event == 0) { - ret = nb_event; - goto error; - } - - DBG3("Listing UST global %d events", nb_event); - - tmp = zmalloc(nb_event * sizeof(struct lttng_event)); - if (tmp == NULL) { - ret = -LTTCOMM_FATAL; - goto error; - } - - cds_lfht_for_each_entry(uchan->events->ht, &iter.iter, uevent, node.node) { - strncpy(tmp[i].name, uevent->attr.name, LTTNG_SYMBOL_NAME_LEN); - tmp[i].name[LTTNG_SYMBOL_NAME_LEN - 1] = '\0'; - tmp[i].enabled = uevent->enabled; - switch (uevent->attr.instrumentation) { - case LTTNG_UST_TRACEPOINT: - tmp[i].type = LTTNG_EVENT_TRACEPOINT; - break; - case LTTNG_UST_PROBE: - tmp[i].type = LTTNG_EVENT_PROBE; - break; - case LTTNG_UST_FUNCTION: - tmp[i].type = LTTNG_EVENT_FUNCTION; - break; - } - tmp[i].loglevel = uevent->attr.loglevel; - switch (uevent->attr.loglevel_type) { - case LTTNG_UST_LOGLEVEL_ALL: - tmp[i].loglevel_type = LTTNG_EVENT_LOGLEVEL_ALL; - break; - case LTTNG_UST_LOGLEVEL_RANGE: - tmp[i].loglevel_type = LTTNG_EVENT_LOGLEVEL_RANGE; - break; - case LTTNG_UST_LOGLEVEL_SINGLE: - tmp[i].loglevel_type = LTTNG_EVENT_LOGLEVEL_SINGLE; - break; - } - if (uevent->filter) { - tmp[i].filter = 1; - } - i++; - } - - ret = nb_event; - *events = tmp; - -error: - 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_event **events) -{ - int i = 0, ret; - unsigned int nb_event; - struct ltt_kernel_event *event; - struct ltt_kernel_channel *kchan; - - kchan = trace_kernel_get_channel_by_name(channel_name, kernel_session); - if (kchan == NULL) { - ret = LTTCOMM_KERN_CHAN_NOT_FOUND; - goto error; - } - - nb_event = kchan->event_count; - - DBG("Listing events for channel %s", kchan->channel->name); - - if (nb_event == 0) { - ret = nb_event; - goto error; - } - - *events = zmalloc(nb_event * sizeof(struct lttng_event)); - if (*events == NULL) { - ret = LTTCOMM_FATAL; - goto error; - } - - /* Kernel channels */ - cds_list_for_each_entry(event, &kchan->events_list.head , list) { - strncpy((*events)[i].name, event->event->name, LTTNG_SYMBOL_NAME_LEN); - (*events)[i].name[LTTNG_SYMBOL_NAME_LEN - 1] = '\0'; - (*events)[i].enabled = event->enabled; - switch (event->event->instrumentation) { - case LTTNG_KERNEL_TRACEPOINT: - (*events)[i].type = LTTNG_EVENT_TRACEPOINT; - break; - case LTTNG_KERNEL_KPROBE: - case LTTNG_KERNEL_KRETPROBE: - (*events)[i].type = LTTNG_EVENT_PROBE; - memcpy(&(*events)[i].attr.probe, &event->event->u.kprobe, - sizeof(struct lttng_kernel_kprobe)); - break; - case LTTNG_KERNEL_FUNCTION: - (*events)[i].type = LTTNG_EVENT_FUNCTION; - memcpy(&((*events)[i].attr.ftrace), &event->event->u.ftrace, - sizeof(struct lttng_kernel_function)); - break; - case LTTNG_KERNEL_NOOP: - (*events)[i].type = LTTNG_EVENT_NOOP; - break; - case LTTNG_KERNEL_SYSCALL: - (*events)[i].type = LTTNG_EVENT_SYSCALL; - break; - case LTTNG_KERNEL_ALL: - assert(0); - break; - } - i++; - } - - 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 int add_uri_to_consumer(struct consumer_output *consumer, - struct lttng_uri *uri, int domain, const char *session_name) -{ - int ret = LTTCOMM_OK; - const char *default_trace_dir; - - assert(uri); - - if (consumer == NULL) { - DBG("No consumer detected. Don't add URI. Stopping."); - ret = LTTCOMM_NO_CONSUMER; - goto error; - } - - switch (domain) { - case LTTNG_DOMAIN_KERNEL: - default_trace_dir = DEFAULT_KERNEL_TRACE_DIR; - break; - case LTTNG_DOMAIN_UST: - default_trace_dir = DEFAULT_UST_TRACE_DIR; - 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. - */ - default_trace_dir = ""; - } - - switch (uri->dtype) { - case LTTNG_DST_IPV4: - case LTTNG_DST_IPV6: - DBG2("Setting network URI to consumer"); - - /* Set URI into consumer output object */ - ret = consumer_set_network_uri(consumer, uri); - if (ret < 0) { - ret = LTTCOMM_FATAL; - 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. - */ - goto error; - } - - if (uri->stype == LTTNG_STREAM_CONTROL && strlen(uri->subdir) == 0) { - ret = set_consumer_subdir(consumer, session_name); - if (ret < 0) { - ret = LTTCOMM_FATAL; - goto error; - } - } - - if (uri->stype == LTTNG_STREAM_CONTROL) { - /* On a new subdir, reappend the default trace dir. */ - strncat(consumer->subdir, default_trace_dir, sizeof(consumer->subdir)); - DBG3("Append domain trace name to subdir %s", consumer->subdir); - } - - break; - case LTTNG_DST_PATH: - DBG2("Setting trace directory path from URI to %s", uri->dst.path); - memset(consumer->dst.trace_path, 0, - sizeof(consumer->dst.trace_path)); - strncpy(consumer->dst.trace_path, uri->dst.path, - sizeof(consumer->dst.trace_path)); - /* Append default trace dir */ - strncat(consumer->dst.trace_path, default_trace_dir, - sizeof(consumer->dst.trace_path)); - /* Flag consumer as local. */ - consumer->type = CONSUMER_DST_LOCAL; - break; - } - -error: - return ret; -} - -/* - * Command LTTNG_DISABLE_CHANNEL processed by the client thread. - */ -static int cmd_disable_channel(struct ltt_session *session, - int domain, char *channel_name) -{ - int ret; - struct ltt_ust_session *usess; - - usess = session->ust_session; - - switch (domain) { - case LTTNG_DOMAIN_KERNEL: - { - ret = channel_kernel_disable(session->kernel_session, - channel_name); - if (ret != LTTCOMM_OK) { - goto error; - } - - kernel_wait_quiescent(kernel_tracer_fd); - 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 = LTTCOMM_UST_CHAN_NOT_FOUND; - goto error; - } - - ret = channel_ust_disable(usess, domain, uchan); - if (ret != LTTCOMM_OK) { - goto error; - } - break; - } -#if 0 - case LTTNG_DOMAIN_UST_PID_FOLLOW_CHILDREN: - case LTTNG_DOMAIN_UST_EXEC_NAME: - case LTTNG_DOMAIN_UST_PID: -#endif - default: - ret = LTTCOMM_UNKNOWN_DOMAIN; - goto error; - } - - ret = LTTCOMM_OK; - -error: - return ret; -} - -/* - * Command LTTNG_ENABLE_CHANNEL processed by the client thread. - */ -static int cmd_enable_channel(struct ltt_session *session, - int domain, struct lttng_channel *attr) -{ - int ret; - struct ltt_ust_session *usess = session->ust_session; - struct lttng_ht *chan_ht; - - DBG("Enabling channel %s for session %s", attr->name, session->name); - - switch (domain) { - case LTTNG_DOMAIN_KERNEL: - { - struct ltt_kernel_channel *kchan; - - kchan = trace_kernel_get_channel_by_name(attr->name, - session->kernel_session); - if (kchan == NULL) { - ret = channel_kernel_create(session->kernel_session, - attr, kernel_poll_pipe[1]); - } else { - ret = channel_kernel_enable(session->kernel_session, kchan); - } - - if (ret != LTTCOMM_OK) { - goto error; - } - - kernel_wait_quiescent(kernel_tracer_fd); - break; - } - case LTTNG_DOMAIN_UST: - { - struct ltt_ust_channel *uchan; - - chan_ht = usess->domain_global.channels; - - uchan = trace_ust_find_channel_by_name(chan_ht, attr->name); - if (uchan == NULL) { - ret = channel_ust_create(usess, domain, attr); - } else { - ret = channel_ust_enable(usess, domain, uchan); - } - break; - } -#if 0 - case LTTNG_DOMAIN_UST_PID_FOLLOW_CHILDREN: - case LTTNG_DOMAIN_UST_EXEC_NAME: - case LTTNG_DOMAIN_UST_PID: -#endif - default: - ret = LTTCOMM_UNKNOWN_DOMAIN; - goto error; - } - -error: - return ret; -} - -/* - * Command LTTNG_DISABLE_EVENT processed by the client thread. - */ -static int cmd_disable_event(struct ltt_session *session, int domain, - char *channel_name, char *event_name) -{ - int ret; - - switch (domain) { - case LTTNG_DOMAIN_KERNEL: - { - struct ltt_kernel_channel *kchan; - struct ltt_kernel_session *ksess; - - ksess = session->kernel_session; - - kchan = trace_kernel_get_channel_by_name(channel_name, ksess); - if (kchan == NULL) { - ret = LTTCOMM_KERN_CHAN_NOT_FOUND; - goto error; - } - - ret = event_kernel_disable_tracepoint(ksess, kchan, event_name); - if (ret != LTTCOMM_OK) { - goto error; - } - - kernel_wait_quiescent(kernel_tracer_fd); - break; - } - case LTTNG_DOMAIN_UST: - { - struct ltt_ust_channel *uchan; - struct ltt_ust_session *usess; - - usess = session->ust_session; - - uchan = trace_ust_find_channel_by_name(usess->domain_global.channels, - channel_name); - if (uchan == NULL) { - ret = LTTCOMM_UST_CHAN_NOT_FOUND; - goto error; - } - - ret = event_ust_disable_tracepoint(usess, domain, uchan, event_name); - if (ret != LTTCOMM_OK) { - goto error; - } - - DBG3("Disable UST event %s in channel %s completed", event_name, - channel_name); - break; - } -#if 0 - case LTTNG_DOMAIN_UST_EXEC_NAME: - case LTTNG_DOMAIN_UST_PID: - case LTTNG_DOMAIN_UST_PID_FOLLOW_CHILDREN: -#endif - default: - ret = LTTCOMM_UND; - goto error; - } - - ret = LTTCOMM_OK; - -error: - return ret; -} - -/* - * Command LTTNG_DISABLE_ALL_EVENT processed by the client thread. - */ -static int cmd_disable_event_all(struct ltt_session *session, int domain, - char *channel_name) -{ - int ret; - - switch (domain) { - case LTTNG_DOMAIN_KERNEL: - { - struct ltt_kernel_session *ksess; - struct ltt_kernel_channel *kchan; - - ksess = session->kernel_session; - - kchan = trace_kernel_get_channel_by_name(channel_name, ksess); - if (kchan == NULL) { - ret = LTTCOMM_KERN_CHAN_NOT_FOUND; - goto error; - } - - ret = event_kernel_disable_all(ksess, kchan); - if (ret != LTTCOMM_OK) { - goto error; - } - - kernel_wait_quiescent(kernel_tracer_fd); - break; - } - case LTTNG_DOMAIN_UST: - { - struct ltt_ust_session *usess; - struct ltt_ust_channel *uchan; - - usess = session->ust_session; - - uchan = trace_ust_find_channel_by_name(usess->domain_global.channels, - channel_name); - if (uchan == NULL) { - ret = LTTCOMM_UST_CHAN_NOT_FOUND; - goto error; - } - - ret = event_ust_disable_all_tracepoints(usess, domain, uchan); - if (ret != 0) { - goto error; - } - - DBG3("Disable all UST events in channel %s completed", channel_name); - - break; - } -#if 0 - case LTTNG_DOMAIN_UST_EXEC_NAME: - case LTTNG_DOMAIN_UST_PID: - case LTTNG_DOMAIN_UST_PID_FOLLOW_CHILDREN: -#endif - default: - ret = LTTCOMM_UND; - goto error; - } - - ret = LTTCOMM_OK; - -error: - return ret; -} - -/* - * Command LTTNG_ADD_CONTEXT processed by the client thread. - */ -static int cmd_add_context(struct ltt_session *session, int domain, - char *channel_name, char *event_name, struct lttng_event_context *ctx) -{ - int ret; - - switch (domain) { - case LTTNG_DOMAIN_KERNEL: - /* Add kernel context to kernel tracer */ - ret = context_kernel_add(session->kernel_session, ctx, - event_name, channel_name); - if (ret != LTTCOMM_OK) { - goto error; - } - break; - case LTTNG_DOMAIN_UST: - { - struct ltt_ust_session *usess = session->ust_session; - - ret = context_ust_add(usess, domain, ctx, event_name, channel_name); - if (ret != LTTCOMM_OK) { - goto error; - } - break; - } -#if 0 - case LTTNG_DOMAIN_UST_EXEC_NAME: - case LTTNG_DOMAIN_UST_PID: - case LTTNG_DOMAIN_UST_PID_FOLLOW_CHILDREN: -#endif - default: - ret = LTTCOMM_UND; - goto error; - } - - ret = LTTCOMM_OK; - -error: - return ret; -} - -/* - * Command LTTNG_SET_FILTER processed by the client thread. - */ -static int cmd_set_filter(struct ltt_session *session, int domain, - char *channel_name, char *event_name, - struct lttng_filter_bytecode *bytecode) -{ - int ret; - - switch (domain) { - case LTTNG_DOMAIN_KERNEL: - ret = LTTCOMM_FATAL; - break; - case LTTNG_DOMAIN_UST: - { - struct ltt_ust_session *usess = session->ust_session; - - ret = filter_ust_set(usess, domain, bytecode, event_name, channel_name); - if (ret != LTTCOMM_OK) { - goto error; - } - break; - } -#if 0 - case LTTNG_DOMAIN_UST_EXEC_NAME: - case LTTNG_DOMAIN_UST_PID: - case LTTNG_DOMAIN_UST_PID_FOLLOW_CHILDREN: -#endif - default: - ret = LTTCOMM_UND; - goto error; - } - - ret = LTTCOMM_OK; - -error: - return ret; - -} - -/* - * Command LTTNG_ENABLE_EVENT processed by the client thread. - */ -static int cmd_enable_event(struct ltt_session *session, int domain, - char *channel_name, struct lttng_event *event) -{ - int ret; - struct lttng_channel *attr; - struct ltt_ust_session *usess = session->ust_session; - - switch (domain) { - case LTTNG_DOMAIN_KERNEL: - { - struct ltt_kernel_channel *kchan; - - kchan = trace_kernel_get_channel_by_name(channel_name, - session->kernel_session); - if (kchan == NULL) { - attr = channel_new_default_attr(domain); - if (attr == NULL) { - ret = LTTCOMM_FATAL; - goto error; - } - snprintf(attr->name, NAME_MAX, "%s", channel_name); - - /* This call will notify the kernel thread */ - ret = channel_kernel_create(session->kernel_session, - attr, kernel_poll_pipe[1]); - if (ret != LTTCOMM_OK) { - free(attr); - goto error; - } - free(attr); - } - - /* 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 = LTTCOMM_FATAL; - goto error; - } - - ret = event_kernel_enable_tracepoint(session->kernel_session, kchan, - event); - if (ret != LTTCOMM_OK) { - goto error; - } - - kernel_wait_quiescent(kernel_tracer_fd); - break; - } - case LTTNG_DOMAIN_UST: - { - struct lttng_channel *attr; - struct ltt_ust_channel *uchan; - - /* 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(domain); - if (attr == NULL) { - ret = LTTCOMM_FATAL; - goto error; - } - snprintf(attr->name, NAME_MAX, "%s", channel_name); - attr->name[NAME_MAX - 1] = '\0'; - - ret = channel_ust_create(usess, domain, attr); - if (ret != LTTCOMM_OK) { - free(attr); - goto error; - } - free(attr); - - /* Get the newly created channel reference back */ - uchan = trace_ust_find_channel_by_name( - usess->domain_global.channels, channel_name); - if (uchan == NULL) { - /* Something is really wrong */ - ret = LTTCOMM_FATAL; - goto error; - } - } - - /* At this point, the session and channel exist on the tracer */ - ret = event_ust_enable_tracepoint(usess, domain, uchan, event); - if (ret != LTTCOMM_OK) { - goto error; - } - break; - } -#if 0 - case LTTNG_DOMAIN_UST_EXEC_NAME: - case LTTNG_DOMAIN_UST_PID: - case LTTNG_DOMAIN_UST_PID_FOLLOW_CHILDREN: -#endif - default: - ret = LTTCOMM_UND; - goto error; - } - - ret = LTTCOMM_OK; - -error: - return ret; -} - -/* - * Command LTTNG_ENABLE_ALL_EVENT processed by the client thread. - */ -static int cmd_enable_event_all(struct ltt_session *session, int domain, - char *channel_name, int event_type) -{ - int ret; - struct ltt_kernel_channel *kchan; - - switch (domain) { - case LTTNG_DOMAIN_KERNEL: - kchan = trace_kernel_get_channel_by_name(channel_name, - session->kernel_session); - if (kchan == NULL) { - /* This call will notify the kernel thread */ - ret = channel_kernel_create(session->kernel_session, NULL, - kernel_poll_pipe[1]); - if (ret != LTTCOMM_OK) { - goto error; - } - - /* 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 = LTTCOMM_FATAL; - goto error; - } - - } - - switch (event_type) { - case LTTNG_EVENT_SYSCALL: - ret = event_kernel_enable_all_syscalls(session->kernel_session, - kchan, kernel_tracer_fd); - break; - case LTTNG_EVENT_TRACEPOINT: - /* - * This call enables all LTTNG_KERNEL_TRACEPOINTS and - * events already registered to the channel. - */ - ret = event_kernel_enable_all_tracepoints(session->kernel_session, - kchan, kernel_tracer_fd); - break; - case LTTNG_EVENT_ALL: - /* Enable syscalls and tracepoints */ - ret = event_kernel_enable_all(session->kernel_session, - kchan, kernel_tracer_fd); - break; - default: - ret = LTTCOMM_KERN_ENABLE_FAIL; - goto error; - } - - /* Manage return value */ - if (ret != LTTCOMM_OK) { - goto error; - } - - kernel_wait_quiescent(kernel_tracer_fd); - break; - case LTTNG_DOMAIN_UST: - { - struct lttng_channel *attr; - struct ltt_ust_channel *uchan; - struct ltt_ust_session *usess = session->ust_session; - - /* 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(domain); - if (attr == NULL) { - ret = LTTCOMM_FATAL; - goto error; - } - snprintf(attr->name, NAME_MAX, "%s", channel_name); - attr->name[NAME_MAX - 1] = '\0'; - - /* Use the internal command enable channel */ - ret = channel_ust_create(usess, domain, attr); - if (ret != LTTCOMM_OK) { - free(attr); - goto error; - } - free(attr); - - /* Get the newly created channel reference back */ - uchan = trace_ust_find_channel_by_name( - usess->domain_global.channels, channel_name); - if (uchan == NULL) { - /* Something is really wrong */ - ret = LTTCOMM_FATAL; - goto error; - } - } - - /* At this point, the session and channel exist on the tracer */ - - switch (event_type) { - case LTTNG_EVENT_ALL: - case LTTNG_EVENT_TRACEPOINT: - ret = event_ust_enable_all_tracepoints(usess, domain, uchan); - if (ret != LTTCOMM_OK) { - goto error; - } - break; - default: - ret = LTTCOMM_UST_ENABLE_FAIL; - goto error; - } - - /* Manage return value */ - if (ret != LTTCOMM_OK) { - goto error; - } - - break; - } -#if 0 - case LTTNG_DOMAIN_UST_EXEC_NAME: - case LTTNG_DOMAIN_UST_PID: - case LTTNG_DOMAIN_UST_PID_FOLLOW_CHILDREN: -#endif - default: - ret = LTTCOMM_UND; - goto error; - } - - ret = LTTCOMM_OK; - -error: - return ret; -} - -/* - * Command LTTNG_LIST_TRACEPOINTS processed by the client thread. - */ -static ssize_t cmd_list_tracepoints(int domain, struct lttng_event **events) -{ - int ret; - ssize_t nb_events = 0; - - switch (domain) { - case LTTNG_DOMAIN_KERNEL: - nb_events = kernel_list_events(kernel_tracer_fd, events); - if (nb_events < 0) { - ret = LTTCOMM_KERN_LIST_FAIL; - goto error; - } - break; - case LTTNG_DOMAIN_UST: - nb_events = ust_app_list_events(events); - if (nb_events < 0) { - ret = LTTCOMM_UST_LIST_FAIL; - goto error; - } - break; - default: - ret = LTTCOMM_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. - */ -static ssize_t cmd_list_tracepoint_fields(int 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 = LTTCOMM_UST_LIST_FAIL; - goto error; - } - break; - case LTTNG_DOMAIN_KERNEL: - default: /* fall-through */ - ret = LTTCOMM_UND; - goto error; - } - - return nb_fields; - -error: - /* Return negative value to differentiate return code */ - return -ret; -} - -/* - * Command LTTNG_START_TRACE processed by the client thread. - */ -static int cmd_start_trace(struct ltt_session *session) -{ - int ret; - struct ltt_kernel_session *ksession; - struct ltt_ust_session *usess; - struct ltt_kernel_channel *kchan; - - /* Ease our life a bit ;) */ - ksession = session->kernel_session; - usess = session->ust_session; - - if (session->enabled) { - /* Already started. */ - ret = LTTCOMM_TRACE_ALREADY_STARTED; - goto error; - } - - session->enabled = 1; - - ret = setup_relayd(session); - if (ret != LTTCOMM_OK) { - ERR("Error setting up relayd for session %s", session->name); - goto error; - } - - /* Kernel tracing */ - if (ksession != NULL) { - /* Open kernel metadata */ - if (ksession->metadata == NULL) { - ret = kernel_open_metadata(ksession); - if (ret < 0) { - ret = LTTCOMM_KERN_META_FAIL; - goto error; - } - } - - /* Open kernel metadata stream */ - if (ksession->metadata_stream_fd < 0) { - ret = kernel_open_metadata_stream(ksession); - if (ret < 0) { - ERR("Kernel create metadata stream failed"); - ret = LTTCOMM_KERN_STREAM_FAIL; - goto error; - } - } - - /* For each channel */ - cds_list_for_each_entry(kchan, &ksession->channel_list.head, list) { - if (kchan->stream_count == 0) { - ret = kernel_open_channel_stream(kchan); - if (ret < 0) { - ret = LTTCOMM_KERN_STREAM_FAIL; - goto error; - } - /* Update the stream global counter */ - ksession->stream_count_global += ret; - } - } - - /* Setup kernel consumer socket and send fds to it */ - ret = init_kernel_tracing(ksession); - if (ret < 0) { - ret = LTTCOMM_KERN_START_FAIL; - goto error; - } - - /* This start the kernel tracing */ - ret = kernel_start_session(ksession); - if (ret < 0) { - ret = LTTCOMM_KERN_START_FAIL; - goto error; - } - - /* Quiescent wait after starting trace */ - kernel_wait_quiescent(kernel_tracer_fd); - } - - /* Flag session that trace should start automatically */ - if (usess) { - usess->start_trace = 1; - - ret = ust_app_start_trace_all(usess); - if (ret < 0) { - ret = LTTCOMM_UST_START_FAIL; - goto error; - } - } - - ret = LTTCOMM_OK; - -error: - return ret; -} - -/* - * Command LTTNG_STOP_TRACE processed by the client thread. - */ -static int cmd_stop_trace(struct ltt_session *session) -{ - int ret; - struct ltt_kernel_channel *kchan; - struct ltt_kernel_session *ksession; - struct ltt_ust_session *usess; - - /* Short cut */ - ksession = session->kernel_session; - usess = session->ust_session; - - if (!session->enabled) { - ret = LTTCOMM_TRACE_ALREADY_STOPPED; - goto error; - } - - session->enabled = 0; - - /* Kernel tracer */ - if (ksession != NULL) { - DBG("Stop kernel tracing"); - - /* Flush metadata if exist */ - if (ksession->metadata_stream_fd >= 0) { - ret = kernel_metadata_flush_buffer(ksession->metadata_stream_fd); - if (ret < 0) { - ERR("Kernel metadata flush failed"); - } - } - - /* Flush all buffers before stopping */ - cds_list_for_each_entry(kchan, &ksession->channel_list.head, list) { - ret = kernel_flush_buffer(kchan); - if (ret < 0) { - ERR("Kernel flush buffer error"); - } - } - - ret = kernel_stop_session(ksession); - if (ret < 0) { - ret = LTTCOMM_KERN_STOP_FAIL; - goto error; - } - - kernel_wait_quiescent(kernel_tracer_fd); - } - - if (usess) { - usess->start_trace = 0; - - ret = ust_app_stop_trace_all(usess); - if (ret < 0) { - ret = LTTCOMM_UST_STOP_FAIL; - goto error; - } - } - - ret = LTTCOMM_OK; - -error: - return ret; -} - -/* - * Command LTTNG_SET_CONSUMER_URI processed by the client thread. - */ -static int cmd_set_consumer_uri(int domain, 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; - struct consumer_output *consumer = NULL; - - assert(session); - assert(uris); - assert(nb_uri > 0); - - /* Can't enable consumer after session started. */ - if (session->enabled) { - ret = LTTCOMM_TRACE_ALREADY_STARTED; - goto error; - } - - if (!session->start_consumer) { - ret = LTTCOMM_NO_CONSUMER; - goto error; - } - - /* - * This case switch makes sure the domain session has a temporary consumer - * so the URL can be set. - */ - switch (domain) { - case 0: - /* Code flow error. A session MUST always have a consumer object */ - assert(session->consumer); - /* - * The URL will be added to the tracing session consumer instead of a - * specific domain consumer. - */ - consumer = session->consumer; - break; - case LTTNG_DOMAIN_KERNEL: - /* Code flow error if we don't have a kernel session here. */ - assert(ksess); - - /* Create consumer output if none exists */ - consumer = ksess->tmp_consumer; - if (consumer == NULL) { - consumer = consumer_copy_output(ksess->consumer); - if (consumer == NULL) { - ret = LTTCOMM_FATAL; - goto error; - } - /* Trash the consumer subdir, we are about to set a new one. */ - memset(consumer->subdir, 0, sizeof(consumer->subdir)); - ksess->tmp_consumer = consumer; - } - - break; - case LTTNG_DOMAIN_UST: - /* Code flow error if we don't have a kernel session here. */ - assert(usess); - - /* Create consumer output if none exists */ - consumer = usess->tmp_consumer; - if (consumer == NULL) { - consumer = consumer_copy_output(usess->consumer); - if (consumer == NULL) { - ret = LTTCOMM_FATAL; - goto error; - } - /* Trash the consumer subdir, we are about to set a new one. */ - memset(consumer->subdir, 0, sizeof(consumer->subdir)); - usess->tmp_consumer = consumer; - } - - break; - } - - for (i = 0; i < nb_uri; i++) { - struct consumer_socket *socket; - struct lttng_ht_iter iter; - - ret = add_uri_to_consumer(consumer, &uris[i], domain, session->name); - if (ret < 0) { - goto error; - } - - /* - * Don't send relayd socket if URI is NOT remote or if the relayd - * sockets for the session are already sent. - */ - if (uris[i].dtype == LTTNG_DST_PATH || - consumer->dst.net.relayd_socks_sent) { - continue; - } - - /* Try to send relayd URI to the consumer if exist. */ - cds_lfht_for_each_entry(consumer->socks->ht, &iter.iter, - socket, node.node) { - - /* A socket in the HT should never have a negative fd */ - assert(socket->fd >= 0); - - pthread_mutex_lock(socket->lock); - ret = send_socket_relayd_consumer(domain, session, &uris[i], - consumer, socket->fd); - pthread_mutex_unlock(socket->lock); - if (ret != LTTCOMM_OK) { - goto error; - } - } - } - - /* All good! */ - ret = LTTCOMM_OK; - -error: - return ret; -} - - -/* - * Command LTTNG_CREATE_SESSION processed by the client thread. - */ -static int cmd_create_session_uri(char *name, struct lttng_uri *uris, - size_t nb_uri, lttng_sock_cred *creds) -{ - int ret; - char *path = NULL; - struct ltt_session *session; - - assert(name); - - /* - * Verify if the session already exist - * - * XXX: There is no need for the session lock list here since the caller - * (process_client_msg) is holding it. We might want to change that so a - * single command does not lock the entire session list. - */ - session = session_find_by_name(name); - if (session != NULL) { - ret = LTTCOMM_EXIST_SESS; - goto find_error; - } - - /* Create tracing session in the registry */ - ret = session_create(name, path, LTTNG_SOCK_GET_UID_CRED(creds), - LTTNG_SOCK_GET_GID_CRED(creds)); - if (ret != LTTCOMM_OK) { - goto session_error; - } - - /* - * Get the newly created session pointer back - * - * XXX: There is no need for the session lock list here since the caller - * (process_client_msg) is holding it. We might want to change that so a - * single command does not lock the entire session list. - */ - session = session_find_by_name(name); - assert(session); - - /* Create default consumer output for the session not yet created. */ - session->consumer = consumer_create_output(CONSUMER_DST_LOCAL); - if (session->consumer == NULL) { - ret = LTTCOMM_FATAL; - goto consumer_error; - } - - /* - * This means that the lttng_create_session call was called with the _path_ - * argument set to NULL. - */ - if (uris == NULL) { - /* - * At this point, we'll skip the consumer URI setup and create a - * session with a NULL path which will flag the session to NOT spawn a - * consumer. - */ - DBG("Create session %s with NO uri, skipping consumer setup", name); - goto end; - } - - session->start_consumer = 1; - - ret = cmd_set_consumer_uri(0, session, nb_uri, uris); - if (ret != LTTCOMM_OK) { - goto consumer_error; - } - - session->consumer->enabled = 1; - -end: - return LTTCOMM_OK; - -consumer_error: - session_destroy(session); -session_error: -find_error: - return ret; -} - -/* - * Command LTTNG_DESTROY_SESSION processed by the client thread. - */ -static int cmd_destroy_session(struct ltt_session *session, char *name) -{ - int ret; - - /* Safety net */ - assert(session); - - /* Clean kernel session teardown */ - teardown_kernel_session(session); - /* UST session teardown */ - teardown_ust_session(session); - - /* - * Must notify the kernel thread here to update it's poll setin order - * to remove the channel(s)' fd just destroyed. - */ - ret = notify_thread_pipe(kernel_poll_pipe[1]); - if (ret < 0) { - PERROR("write kernel poll pipe"); - } - - ret = session_destroy(session); - - return ret; -} - -/* - * Command LTTNG_CALIBRATE processed by the client thread. - */ -static int cmd_calibrate(int domain, struct lttng_calibrate *calibrate) -{ - int ret; - - switch (domain) { - case LTTNG_DOMAIN_KERNEL: - { - struct lttng_kernel_calibrate kcalibrate; - - kcalibrate.type = calibrate->type; - ret = kernel_calibrate(kernel_tracer_fd, &kcalibrate); - if (ret < 0) { - ret = LTTCOMM_KERN_ENABLE_FAIL; - goto error; - } - break; - } - case LTTNG_DOMAIN_UST: - { - struct lttng_ust_calibrate ucalibrate; - - ucalibrate.type = calibrate->type; - ret = ust_app_calibrate_glb(&ucalibrate); - if (ret < 0) { - ret = LTTCOMM_UST_CALIBRATE_FAIL; - goto error; - } - break; - } - default: - ret = LTTCOMM_UND; - goto error; - } - - ret = LTTCOMM_OK; - -error: - return ret; -} - -/* - * Command LTTNG_REGISTER_CONSUMER processed by the client thread. - */ -static int cmd_register_consumer(struct ltt_session *session, int domain, - char *sock_path) -{ - int ret, sock; - struct consumer_socket *socket; - - switch (domain) { - case LTTNG_DOMAIN_KERNEL: - /* Can't register a consumer if there is already one */ - if (session->kernel_session->consumer_fds_sent != 0) { - ret = LTTCOMM_KERN_CONSUMER_FAIL; - goto error; - } - - sock = lttcomm_connect_unix_sock(sock_path); - if (sock < 0) { - ret = LTTCOMM_CONNECT_FAIL; - goto error; - } - - socket = consumer_allocate_socket(sock); - if (socket == NULL) { - ret = LTTCOMM_FATAL; - close(sock); - goto error; - } - - socket->lock = zmalloc(sizeof(pthread_mutex_t)); - if (socket->lock == NULL) { - PERROR("zmalloc pthread mutex"); - ret = LTTCOMM_FATAL; - goto error; - } - pthread_mutex_init(socket->lock, NULL); - - rcu_read_lock(); - consumer_add_socket(socket, session->kernel_session->consumer); - rcu_read_unlock(); - - pthread_mutex_lock(&kconsumer_data.pid_mutex); - kconsumer_data.pid = -1; - pthread_mutex_unlock(&kconsumer_data.pid_mutex); - - break; - default: - /* TODO: Userspace tracing */ - ret = LTTCOMM_UND; - goto error; - } - - ret = LTTCOMM_OK; - -error: - return ret; -} - -/* - * Command LTTNG_LIST_DOMAINS processed by the client thread. - */ -static ssize_t cmd_list_domains(struct ltt_session *session, - struct lttng_domain **domains) -{ - int ret, index = 0; - ssize_t nb_dom = 0; - - if (session->kernel_session != NULL) { - DBG3("Listing domains found kernel domain"); - nb_dom++; + ret = -1; + goto error_open; } - if (session->ust_session != NULL) { - DBG3("Listing domains found UST global domain"); - nb_dom++; + /* Validate kernel version */ + ret = kernel_validate_version(kernel_tracer_fd); + if (ret < 0) { + goto error_version; } - *domains = zmalloc(nb_dom * sizeof(struct lttng_domain)); - if (*domains == NULL) { - ret = -LTTCOMM_FATAL; - goto error; + ret = modprobe_lttng_data(); + if (ret < 0) { + goto error_modules; } - if (session->kernel_session != NULL) { - (*domains)[index].type = LTTNG_DOMAIN_KERNEL; - index++; + DBG("Kernel tracer fd %d", kernel_tracer_fd); + return 0; + +error_version: + modprobe_remove_lttng_control(); + ret = close(kernel_tracer_fd); + if (ret) { + PERROR("close"); } + kernel_tracer_fd = -1; + return LTTCOMM_KERN_VERSION; - if (session->ust_session != NULL) { - (*domains)[index].type = LTTNG_DOMAIN_UST; - index++; +error_modules: + ret = close(kernel_tracer_fd); + if (ret) { + PERROR("close"); } - return nb_dom; +error_open: + modprobe_remove_lttng_control(); error: - return ret; + WARN("No kernel tracer available"); + kernel_tracer_fd = -1; + if (!is_root) { + return LTTCOMM_NEED_ROOT_SESSIOND; + } else { + return LTTCOMM_KERN_NA; + } } + /* - * Command LTTNG_LIST_CHANNELS processed by the client thread. + * Copy consumer output from the tracing session to the domain session. The + * function also applies the right modification on a per domain basis for the + * trace files destination directory. */ -static ssize_t cmd_list_channels(int domain, struct ltt_session *session, - struct lttng_channel **channels) +static int copy_session_consumer(int domain, struct ltt_session *session) { int ret; - ssize_t nb_chan = 0; + const char *dir_name; + struct consumer_output *consumer; + + assert(session); + assert(session->consumer); 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); + DBG3("Copying tracing session consumer output in kernel session"); + session->kernel_session->consumer = + consumer_copy_output(session->consumer); + /* Ease our life a bit for the next part */ + consumer = session->kernel_session->consumer; + dir_name = DEFAULT_KERNEL_TRACE_DIR; break; case LTTNG_DOMAIN_UST: - if (session->ust_session != NULL) { - nb_chan = lttng_ht_get_count( - session->ust_session->domain_global.channels); - } - DBG3("Number of UST global channels %zd", nb_chan); + DBG3("Copying tracing session consumer output in UST session"); + session->ust_session->consumer = + consumer_copy_output(session->consumer); + /* Ease our life a bit for the next part */ + consumer = session->ust_session->consumer; + dir_name = DEFAULT_UST_TRACE_DIR; break; default: - *channels = NULL; - ret = -LTTCOMM_UND; + ret = LTTCOMM_UNKNOWN_DOMAIN; goto error; } - if (nb_chan > 0) { - *channels = zmalloc(nb_chan * sizeof(struct lttng_channel)); - if (*channels == NULL) { - ret = -LTTCOMM_FATAL; - goto error; - } - - list_lttng_channels(domain, session, *channels); - } else { - *channels = NULL; - } - - return nb_chan; - -error: - return ret; -} - -/* - * Command LTTNG_LIST_EVENTS processed by the client thread. - */ -static ssize_t cmd_list_events(int domain, struct ltt_session *session, - char *channel_name, struct lttng_event **events) -{ - int ret = 0; - ssize_t nb_event = 0; - - switch (domain) { - case LTTNG_DOMAIN_KERNEL: - if (session->kernel_session != NULL) { - nb_event = list_lttng_kernel_events(channel_name, - session->kernel_session, events); - } - break; - case LTTNG_DOMAIN_UST: - { - if (session->ust_session != NULL) { - nb_event = list_lttng_ust_global_events(channel_name, - &session->ust_session->domain_global, events); - } - break; - } - default: - ret = -LTTCOMM_UND; + ret = consumer_set_subdir(session->consumer, session->name); + if (ret < 0) { + ret = LTTCOMM_FATAL; goto error; } - ret = nb_event; + /* Append correct directory to subdir */ + strncat(consumer->subdir, dir_name, sizeof(consumer->subdir)); + DBG3("Copy session consumer subdir %s", consumer->subdir); + + ret = LTTCOMM_OK; error: return ret; } /* - * Command LTTNG_DISABLE_CONSUMER processed by the client thread. + * Create an UST session and add it to the session ust list. */ -static int cmd_disable_consumer(int domain, struct ltt_session *session) +static int create_ust_session(struct ltt_session *session, + struct lttng_domain *domain) { int ret; - struct ltt_kernel_session *ksess = session->kernel_session; - struct ltt_ust_session *usess = session->ust_session; - struct consumer_output *consumer; + struct ltt_ust_session *lus = NULL; assert(session); + assert(domain); + assert(session->consumer); - if (session->enabled) { - /* Can't disable consumer on an already started session */ - ret = LTTCOMM_TRACE_ALREADY_STARTED; + switch (domain->type) { + case LTTNG_DOMAIN_UST: + break; + default: + ERR("Unknown UST domain on create session %d", domain->type); + ret = LTTCOMM_UNKNOWN_DOMAIN; goto error; } - if (!session->start_consumer) { - ret = LTTCOMM_NO_CONSUMER; + DBG("Creating UST session"); + + lus = trace_ust_create_session(session->path, session->id, domain); + if (lus == NULL) { + ret = LTTCOMM_UST_SESS_FAIL; goto error; } - switch (domain) { - case 0: - DBG("Disable tracing session %s consumer", session->name); - consumer = session->consumer; - break; - case LTTNG_DOMAIN_KERNEL: - /* Code flow error if we don't have a kernel session here. */ - assert(ksess); - - DBG("Disabling kernel consumer"); - consumer = ksess->consumer; - - break; - case LTTNG_DOMAIN_UST: - /* Code flow error if we don't have a UST session here. */ - assert(usess); - - DBG("Disabling UST consumer"); - consumer = usess->consumer; + lus->uid = session->uid; + lus->gid = session->gid; + session->ust_session = lus; - break; - default: - ret = LTTCOMM_UNKNOWN_DOMAIN; + /* Copy session output to the newly created UST session */ + ret = copy_session_consumer(domain->type, session); + if (ret != LTTCOMM_OK) { goto error; } - if (consumer) { - consumer->enabled = 0; - /* Success at this point */ - ret = LTTCOMM_OK; - } else { - ret = LTTCOMM_NO_CONSUMER; - } + return LTTCOMM_OK; error: + free(lus); + session->ust_session = NULL; return ret; } /* - * Command LTTNG_ENABLE_CONSUMER processed by the client thread. + * Create a kernel tracer session then create the default channel. */ -static int cmd_enable_consumer(int domain, struct ltt_session *session) +static int create_kernel_session(struct ltt_session *session) { int ret; - struct ltt_kernel_session *ksess = session->kernel_session; - struct ltt_ust_session *usess = session->ust_session; - struct consumer_output *consumer = NULL; - assert(session); + DBG("Creating kernel session"); - /* Can't enable consumer after session started. */ - if (session->enabled) { - ret = LTTCOMM_TRACE_ALREADY_STARTED; + ret = kernel_create_session(session, kernel_tracer_fd); + if (ret < 0) { + ret = LTTCOMM_KERN_SESS_FAIL; goto error; } - if (!session->start_consumer) { - ret = LTTCOMM_NO_CONSUMER; + /* Code flow safety */ + assert(session->kernel_session); + + /* Copy session output to the newly created Kernel session */ + ret = copy_session_consumer(LTTNG_DOMAIN_KERNEL, session); + if (ret != LTTCOMM_OK) { goto error; } - switch (domain) { - case 0: - assert(session->consumer); - consumer = session->consumer; - break; - case LTTNG_DOMAIN_KERNEL: - /* Code flow error if we don't have a kernel session here. */ - assert(ksess); - - /* - * Check if we have already sent fds to the consumer. In that case, - * the enable-consumer command can't be used because a start trace - * had previously occured. - */ - if (ksess->consumer_fds_sent) { - ret = LTTCOMM_ENABLE_CONSUMER_FAIL; - goto error; - } - - consumer = ksess->tmp_consumer; - if (consumer == NULL) { - ret = LTTCOMM_OK; - /* No temp. consumer output exists. Using the current one. */ - DBG3("No temporary consumer. Using default"); - consumer = ksess->consumer; - goto error; - } - - switch (consumer->type) { - case CONSUMER_DST_LOCAL: - DBG2("Consumer output is local. Creating directory(ies)"); - - /* Create directory(ies) */ - ret = run_as_mkdir_recursive(consumer->dst.trace_path, - S_IRWXU | S_IRWXG, session->uid, session->gid); - if (ret < 0) { - if (ret != -EEXIST) { - ERR("Trace directory creation error"); - ret = LTTCOMM_FATAL; - goto error; - } - } - break; - case CONSUMER_DST_NET: - DBG2("Consumer output is network. Validating URIs"); - /* Validate if we have both control and data path set. */ - if (!consumer->dst.net.control_isset) { - ret = LTTCOMM_URL_CTRL_MISS; - goto error; - } - - if (!consumer->dst.net.data_isset) { - ret = LTTCOMM_URL_DATA_MISS; - goto error; - } - - /* Check established network session state */ - if (session->net_handle == 0) { - ret = LTTCOMM_ENABLE_CONSUMER_FAIL; - ERR("Session network handle is not set on enable-consumer"); + /* Create directory(ies) on local filesystem. */ + if (session->kernel_session->consumer->type == CONSUMER_DST_LOCAL && + strlen(session->kernel_session->consumer->dst.trace_path) > 0) { + ret = run_as_mkdir_recursive( + session->kernel_session->consumer->dst.trace_path, + S_IRWXU | S_IRWXG, session->uid, session->gid); + if (ret < 0) { + if (ret != -EEXIST) { + ERR("Trace directory creation error"); goto error; } - - break; - } - - /* Append default kernel trace dir to subdir */ - strncat(ksess->consumer->subdir, DEFAULT_KERNEL_TRACE_DIR, - sizeof(ksess->consumer->subdir)); - - /* - * @session-lock - * This is race free for now since the session lock is acquired before - * ending up in this function. No other threads can access this kernel - * session without this lock hence freeing the consumer output object - * is valid. - */ - rcu_read_lock(); - consumer_destroy_output(ksess->consumer); - rcu_read_unlock(); - ksess->consumer = consumer; - ksess->tmp_consumer = NULL; - - break; - case LTTNG_DOMAIN_UST: - /* Code flow error if we don't have a UST session here. */ - assert(usess); - - /* - * Check if we have already sent fds to the consumer. In that case, - * the enable-consumer command can't be used because a start trace - * had previously occured. - */ - if (usess->start_trace) { - ret = LTTCOMM_ENABLE_CONSUMER_FAIL; - goto error; } + } - consumer = usess->tmp_consumer; - if (consumer == NULL) { - ret = LTTCOMM_OK; - /* No temp. consumer output exists. Using the current one. */ - DBG3("No temporary consumer. Using default"); - consumer = usess->consumer; - goto error; - } - - switch (consumer->type) { - case CONSUMER_DST_LOCAL: - DBG2("Consumer output is local. Creating directory(ies)"); - - /* Create directory(ies) */ - ret = run_as_mkdir_recursive(consumer->dst.trace_path, - S_IRWXU | S_IRWXG, session->uid, session->gid); - if (ret < 0) { - if (ret != -EEXIST) { - ERR("Trace directory creation error"); - ret = LTTCOMM_FATAL; - goto error; - } - } - break; - case CONSUMER_DST_NET: - DBG2("Consumer output is network. Validating URIs"); - /* Validate if we have both control and data path set. */ - if (!consumer->dst.net.control_isset) { - ret = LTTCOMM_URL_CTRL_MISS; - goto error; - } - - if (!consumer->dst.net.data_isset) { - ret = LTTCOMM_URL_DATA_MISS; - goto error; - } - - /* Check established network session state */ - if (session->net_handle == 0) { - ret = LTTCOMM_ENABLE_CONSUMER_FAIL; - DBG2("Session network handle is not set on enable-consumer"); - goto error; - } + session->kernel_session->uid = session->uid; + session->kernel_session->gid = session->gid; - if (consumer->net_seq_index == -1) { - ret = LTTCOMM_ENABLE_CONSUMER_FAIL; - DBG2("Network index is not set on the consumer"); - goto error; - } + return LTTCOMM_OK; - break; - } +error: + trace_kernel_destroy_session(session->kernel_session); + session->kernel_session = NULL; + return ret; +} - /* Append default kernel trace dir to subdir */ - strncat(usess->consumer->subdir, DEFAULT_UST_TRACE_DIR, - sizeof(usess->consumer->subdir)); +/* + * Count number of session permitted by uid/gid. + */ +static unsigned int lttng_sessions_count(uid_t uid, gid_t gid) +{ + unsigned int i = 0; + struct ltt_session *session; + DBG("Counting number of available session for UID %d GID %d", + uid, gid); + cds_list_for_each_entry(session, &session_list_ptr->head, list) { /* - * @session-lock - * This is race free for now since the session lock is acquired before - * ending up in this function. No other threads can access this kernel - * session without this lock hence freeing the consumer output object - * is valid. + * Only list the sessions the user can control. */ - rcu_read_lock(); - consumer_destroy_output(usess->consumer); - rcu_read_unlock(); - usess->consumer = consumer; - usess->tmp_consumer = NULL; - - break; - } - - /* Enable it */ - if (consumer) { - consumer->enabled = 1; - /* Success at this point */ - ret = LTTCOMM_OK; - } else { - /* Should not really happend... */ - ret = LTTCOMM_NO_CONSUMER; + if (!session_access_ok(session, uid, gid)) { + continue; + } + i++; } - -error: - return ret; + return i; } /* @@ -4705,7 +2315,7 @@ skip_domain: case LTTNG_ENABLE_CHANNEL: { ret = cmd_enable_channel(cmd_ctx->session, cmd_ctx->lsm->domain.type, - &cmd_ctx->lsm->u.channel.chan); + &cmd_ctx->lsm->u.channel.chan, kernel_poll_pipe[1]); break; } case LTTNG_ENABLE_CONSUMER: @@ -4736,7 +2346,7 @@ skip_domain: { ret = cmd_enable_event(cmd_ctx->session, cmd_ctx->lsm->domain.type, cmd_ctx->lsm->u.enable.channel_name, - &cmd_ctx->lsm->u.enable.event); + &cmd_ctx->lsm->u.enable.event, kernel_poll_pipe[1]); break; } case LTTNG_ENABLE_ALL_EVENT: @@ -4745,7 +2355,7 @@ skip_domain: ret = cmd_enable_event_all(cmd_ctx->session, cmd_ctx->lsm->domain.type, cmd_ctx->lsm->u.enable.channel_name, - cmd_ctx->lsm->u.enable.event.type); + cmd_ctx->lsm->u.enable.event.type, kernel_poll_pipe[1]); break; } case LTTNG_LIST_TRACEPOINTS: @@ -4913,8 +2523,7 @@ skip_domain: } case LTTNG_DESTROY_SESSION: { - ret = cmd_destroy_session(cmd_ctx->session, - cmd_ctx->lsm->session.name); + ret = cmd_destroy_session(cmd_ctx->session, kernel_poll_pipe[1]); /* Set session to NULL so we do not unlock it after free. */ cmd_ctx->session = NULL; @@ -5013,7 +2622,7 @@ skip_domain: } /* Filled the session array */ - list_lttng_sessions((struct lttng_session *)(cmd_ctx->llm->payload), + cmd_list_lttng_sessions((struct lttng_session *)(cmd_ctx->llm->payload), LTTNG_SOCK_GET_UID_CRED(&cmd_ctx->creds), LTTNG_SOCK_GET_GID_CRED(&cmd_ctx->creds)); @@ -5030,8 +2639,19 @@ skip_domain: } case LTTNG_REGISTER_CONSUMER: { + struct consumer_data *cdata; + + switch (cmd_ctx->lsm->domain.type) { + case LTTNG_DOMAIN_KERNEL: + cdata = &kconsumer_data; + break; + default: + ret = LTTCOMM_UND; + goto error; + } + ret = cmd_register_consumer(cmd_ctx->session, cmd_ctx->lsm->domain.type, - cmd_ctx->lsm->u.reg.path); + cmd_ctx->lsm->u.reg.path, cdata); break; } case LTTNG_SET_FILTER: @@ -6218,11 +3838,7 @@ int main(int argc, char **argv) /* Set up max poll set size */ lttng_poll_set_max_size(); - /* - * Set network sequence index to 1 for streams to match a relayd socket on - * the consumer side. - */ - uatomic_set(&relayd_net_seq_idx, 1); + cmd_init(); /* Init all health thread counters. */ health_init(&health_thread_cmd); diff --git a/src/bin/lttng-sessiond/session.c b/src/bin/lttng-sessiond/session.c index 75fcf4bfd..3764c1253 100644 --- a/src/bin/lttng-sessiond/session.c +++ b/src/bin/lttng-sessiond/session.c @@ -246,3 +246,18 @@ error_asprintf: error_malloc: return ret; } + +/* + * Check if the UID or GID match the session. Root user has access to all + * sessions. + */ +int session_access_ok(struct ltt_session *session, uid_t uid, gid_t gid) +{ + assert(session); + + if (uid != session->uid && gid != session->gid && uid != 0) { + return 0; + } else { + return 1; + } +} diff --git a/src/bin/lttng-sessiond/session.h b/src/bin/lttng-sessiond/session.h index b90e8f467..1b40373e9 100644 --- a/src/bin/lttng-sessiond/session.h +++ b/src/bin/lttng-sessiond/session.h @@ -99,4 +99,6 @@ void session_unlock_list(void); struct ltt_session *session_find_by_name(char *name); struct ltt_session_list *session_get_list(void); +int session_access_ok(struct ltt_session *session, uid_t uid, gid_t gid); + #endif /* _LTT_SESSION_H */ diff --git a/src/bin/lttng-sessiond/utils.c b/src/bin/lttng-sessiond/utils.c index d503665a4..5ea337450 100644 --- a/src/bin/lttng-sessiond/utils.c +++ b/src/bin/lttng-sessiond/utils.c @@ -31,6 +31,11 @@ int notify_thread_pipe(int wpipe) { int ret; + /* Ignore if the pipe is invalid. */ + if (wpipe < 0) { + return 0; + } + do { ret = write(wpipe, "!", 1); } while (ret < 0 && errno == EINTR); -- 2.34.1