+ int int_ret = ust_app_start_trace_all(usess);
+
+ if (int_ret < 0) {
+ ret = LTTNG_ERR_UST_START_FAIL;
+ goto error;
+ }
+ }
+
+ /* Flag this after a successful start. */
+ session->has_been_started = 1;
+ session->active = 1;
+
+ /*
+ * Clear the flag that indicates that a rotation was done while the
+ * session was stopped.
+ */
+ session->rotated_after_last_stop = false;
+
+ if (session->rotate_timer_period) {
+ int int_ret = timer_session_rotation_schedule_timer_start(
+ session, session->rotate_timer_period);
+
+ if (int_ret < 0) {
+ ERR("Failed to enable rotate timer");
+ ret = LTTNG_ERR_UNK;
+ goto error;
+ }
+ }
+
+ ret = LTTNG_OK;
+
+error:
+ 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;
+ bool error_occurred = false;
+
+ assert(session);
+
+ DBG("Begin stop session %s (id %" PRIu64 ")", session->name, session->id);
+ /* Short cut */
+ ksession = session->kernel_session;
+ usess = session->ust_session;
+
+ /* Session is not active. Skip everythong and inform the client. */
+ if (!session->active) {
+ ret = LTTNG_ERR_TRACE_ALREADY_STOPPED;
+ goto error;
+ }
+
+ /* Kernel tracer */
+ if (ksession && ksession->active) {
+ DBG("Stop kernel tracing");
+
+ ret = kernel_stop_session(ksession);
+ if (ret < 0) {
+ ret = LTTNG_ERR_KERN_STOP_FAIL;
+ goto error;
+ }
+
+ kernel_wait_quiescent(kernel_tracer_fd);
+
+ /* Flush metadata after stopping (if exists) */
+ 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 after 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");
+ }
+ }
+
+ ksession->active = 0;
+ DBG("Kernel session stopped %s (id %" PRIu64 ")", session->name,
+ session->id);
+ }
+
+ if (usess && usess->active) {
+ ret = ust_app_stop_trace_all(usess);
+ if (ret < 0) {
+ ret = LTTNG_ERR_UST_STOP_FAIL;
+ goto error;
+ }
+ }
+
+ /* Flag inactive after a successful stop. */
+ session->active = 0;
+ ret = !error_occurred ? LTTNG_OK : LTTNG_ERR_UNK;
+
+error:
+ return ret;
+}
+
+/*
+ * Command LTTNG_SET_CONSUMER_URI processed by the client thread.
+ */
+int cmd_set_consumer_uri(struct ltt_session *session, size_t nb_uri,
+ struct lttng_uri *uris)
+{
+ int ret, i;
+ struct ltt_kernel_session *ksess = session->kernel_session;
+ struct ltt_ust_session *usess = session->ust_session;
+
+ assert(session);
+ assert(uris);
+ assert(nb_uri > 0);
+
+ /* Can't set consumer URI if the session is active. */
+ if (session->active) {
+ ret = LTTNG_ERR_TRACE_ALREADY_STARTED;
+ goto error;
+ }
+
+ /* Set the "global" consumer URIs */
+ for (i = 0; i < nb_uri; i++) {
+ ret = add_uri_to_consumer(session,
+ session->consumer,
+ &uris[i], LTTNG_DOMAIN_NONE);
+ if (ret != LTTNG_OK) {
+ goto error;
+ }
+ }
+
+ /* Set UST session URIs */
+ if (session->ust_session) {
+ for (i = 0; i < nb_uri; i++) {
+ ret = add_uri_to_consumer(session,
+ session->ust_session->consumer,
+ &uris[i], LTTNG_DOMAIN_UST);
+ if (ret != LTTNG_OK) {
+ goto error;
+ }
+ }
+ }
+
+ /* Set kernel session URIs */
+ if (session->kernel_session) {
+ for (i = 0; i < nb_uri; i++) {
+ ret = add_uri_to_consumer(session,
+ session->kernel_session->consumer,
+ &uris[i], LTTNG_DOMAIN_KERNEL);
+ if (ret != LTTNG_OK) {
+ goto error;
+ }
+ }
+ }
+
+ /*
+ * Make sure to set the session in output mode after we set URI since a
+ * session can be created without URL (thus flagged in no output mode).
+ */
+ session->output_traces = 1;
+ if (ksess) {
+ ksess->output_traces = 1;
+ }
+
+ if (usess) {
+ usess->output_traces = 1;
+ }
+
+ /* All good! */
+ ret = LTTNG_OK;
+
+error:
+ return ret;
+}
+
+static
+enum lttng_error_code set_session_output_from_descriptor(
+ struct ltt_session *session,
+ const struct lttng_session_descriptor *descriptor)
+{
+ int ret;
+ enum lttng_error_code ret_code = LTTNG_OK;
+ enum lttng_session_descriptor_type session_type =
+ lttng_session_descriptor_get_type(descriptor);
+ enum lttng_session_descriptor_output_type output_type =
+ lttng_session_descriptor_get_output_type(descriptor);
+ struct lttng_uri uris[2] = {};
+ size_t uri_count = 0;
+
+ switch (output_type) {
+ case LTTNG_SESSION_DESCRIPTOR_OUTPUT_TYPE_NONE:
+ goto end;
+ case LTTNG_SESSION_DESCRIPTOR_OUTPUT_TYPE_LOCAL:
+ lttng_session_descriptor_get_local_output_uri(descriptor,
+ &uris[0]);
+ uri_count = 1;
+ break;
+ case LTTNG_SESSION_DESCRIPTOR_OUTPUT_TYPE_NETWORK:
+ lttng_session_descriptor_get_network_output_uris(descriptor,
+ &uris[0], &uris[1]);
+ uri_count = 2;
+ break;
+ default:
+ ret_code = LTTNG_ERR_INVALID;
+ goto end;
+ }
+
+ switch (session_type) {
+ case LTTNG_SESSION_DESCRIPTOR_TYPE_SNAPSHOT:
+ {
+ struct snapshot_output *new_output = NULL;
+
+ new_output = snapshot_output_alloc();
+ if (!new_output) {
+ ret_code = LTTNG_ERR_NOMEM;
+ goto end;
+ }
+
+ ret = snapshot_output_init_with_uri(session,
+ DEFAULT_SNAPSHOT_MAX_SIZE,
+ NULL, uris, uri_count, session->consumer,
+ new_output, &session->snapshot);
+ if (ret < 0) {
+ ret_code = (ret == -ENOMEM) ?
+ LTTNG_ERR_NOMEM : LTTNG_ERR_INVALID;
+ snapshot_output_destroy(new_output);
+ goto end;
+ }
+ snapshot_add_output(&session->snapshot, new_output);
+ break;
+ }
+ case LTTNG_SESSION_DESCRIPTOR_TYPE_REGULAR:
+ case LTTNG_SESSION_DESCRIPTOR_TYPE_LIVE:
+ {
+ ret_code = cmd_set_consumer_uri(session, uri_count, uris);
+ break;
+ }
+ default:
+ ret_code = LTTNG_ERR_INVALID;
+ goto end;
+ }
+end:
+ return ret_code;
+}
+
+static
+enum lttng_error_code cmd_create_session_from_descriptor(
+ struct lttng_session_descriptor *descriptor,
+ const lttng_sock_cred *creds,
+ const char *home_path)
+{
+ int ret;
+ enum lttng_error_code ret_code;
+ const char *session_name;
+ struct ltt_session *new_session = NULL;
+ enum lttng_session_descriptor_status descriptor_status;
+
+ session_lock_list();
+ if (home_path) {
+ if (*home_path != '/') {
+ ERR("Home path provided by client is not absolute");
+ ret_code = LTTNG_ERR_INVALID;
+ goto end;
+ }
+ }
+
+ descriptor_status = lttng_session_descriptor_get_session_name(
+ descriptor, &session_name);
+ switch (descriptor_status) {
+ case LTTNG_SESSION_DESCRIPTOR_STATUS_OK:
+ break;
+ case LTTNG_SESSION_DESCRIPTOR_STATUS_UNSET:
+ session_name = NULL;
+ break;
+ default:
+ ret_code = LTTNG_ERR_INVALID;
+ goto end;
+ }
+ ret_code = session_create(session_name, creds->uid, creds->gid,
+ &new_session);
+ if (ret_code != LTTNG_OK) {
+ goto end;
+ }
+
+ if (!session_name) {
+ ret = lttng_session_descriptor_set_session_name(descriptor,
+ new_session->name);
+ if (ret) {
+ ret_code = LTTNG_ERR_SESSION_FAIL;
+ goto end;
+ }
+ }
+
+ if (!lttng_session_descriptor_is_output_destination_initialized(
+ descriptor)) {
+ /*
+ * Only include the session's creation time in the output
+ * destination if the name of the session itself was
+ * not auto-generated.
+ */
+ ret_code = lttng_session_descriptor_set_default_output(
+ descriptor,
+ session_name ? &new_session->creation_time : NULL,
+ home_path);
+ if (ret_code != LTTNG_OK) {
+ goto end;
+ }
+ } else {
+ new_session->has_user_specified_directory =
+ lttng_session_descriptor_has_output_directory(
+ descriptor);
+ }
+
+ switch (lttng_session_descriptor_get_type(descriptor)) {
+ case LTTNG_SESSION_DESCRIPTOR_TYPE_SNAPSHOT:
+ new_session->snapshot_mode = 1;
+ break;
+ case LTTNG_SESSION_DESCRIPTOR_TYPE_LIVE:
+ new_session->live_timer =
+ lttng_session_descriptor_live_get_timer_interval(
+ descriptor);
+ break;
+ default:
+ break;
+ }
+
+ ret_code = set_session_output_from_descriptor(new_session, descriptor);
+ if (ret_code != LTTNG_OK) {
+ goto end;
+ }
+ new_session->consumer->enabled = 1;
+ ret_code = LTTNG_OK;
+end:
+ /* Release reference provided by the session_create function. */
+ session_put(new_session);
+ if (ret_code != LTTNG_OK && new_session) {
+ /* Release the global reference on error. */
+ session_destroy(new_session);
+ }
+ session_unlock_list();
+ return ret_code;
+}
+
+enum lttng_error_code cmd_create_session(struct command_ctx *cmd_ctx, int sock,
+ struct lttng_session_descriptor **return_descriptor)
+{
+ int ret;
+ size_t payload_size;
+ struct lttng_dynamic_buffer payload;
+ struct lttng_buffer_view home_dir_view;
+ struct lttng_buffer_view session_descriptor_view;
+ struct lttng_session_descriptor *session_descriptor = NULL;
+ enum lttng_error_code ret_code;
+
+ lttng_dynamic_buffer_init(&payload);
+ if (cmd_ctx->lsm->u.create_session.home_dir_size >=
+ LTTNG_PATH_MAX) {
+ ret_code = LTTNG_ERR_INVALID;
+ goto error;
+ }
+ if (cmd_ctx->lsm->u.create_session.session_descriptor_size >
+ LTTNG_SESSION_DESCRIPTOR_MAX_LEN) {
+ ret_code = LTTNG_ERR_INVALID;
+ goto error;
+ }
+
+ payload_size = cmd_ctx->lsm->u.create_session.home_dir_size +
+ cmd_ctx->lsm->u.create_session.session_descriptor_size;
+ ret = lttng_dynamic_buffer_set_size(&payload, payload_size);
+ if (ret) {
+ ret_code = LTTNG_ERR_NOMEM;
+ goto error;
+ }
+
+ ret = lttcomm_recv_unix_sock(sock, payload.data, payload.size);
+ if (ret <= 0) {
+ ERR("Reception of session descriptor failed, aborting.");
+ ret_code = LTTNG_ERR_SESSION_FAIL;
+ goto error;
+ }
+
+ home_dir_view = lttng_buffer_view_from_dynamic_buffer(
+ &payload,
+ 0,
+ cmd_ctx->lsm->u.create_session.home_dir_size);
+ session_descriptor_view = lttng_buffer_view_from_dynamic_buffer(
+ &payload,
+ cmd_ctx->lsm->u.create_session.home_dir_size,
+ cmd_ctx->lsm->u.create_session.session_descriptor_size);
+
+ ret = lttng_session_descriptor_create_from_buffer(
+ &session_descriptor_view, &session_descriptor);
+ if (ret < 0) {
+ ERR("Failed to create session descriptor from payload of \"create session\" command");
+ ret_code = LTTNG_ERR_INVALID;
+ goto error;
+ }
+
+ /*
+ * Sets the descriptor's auto-generated properties (name, output) if
+ * needed.
+ */
+ ret_code = cmd_create_session_from_descriptor(session_descriptor,
+ &cmd_ctx->creds,
+ home_dir_view.size ? home_dir_view.data : NULL);
+ if (ret_code != LTTNG_OK) {
+ goto error;
+ }
+
+ ret_code = LTTNG_OK;
+ *return_descriptor = session_descriptor;
+ session_descriptor = NULL;
+error:
+ lttng_dynamic_buffer_reset(&payload);
+ lttng_session_descriptor_destroy(session_descriptor);
+ return ret_code;
+}
+
+static
+void cmd_destroy_session_reply(const struct ltt_session *session,
+ void *_reply_context)
+{
+ int ret;
+ ssize_t comm_ret;
+ const struct cmd_destroy_session_reply_context *reply_context =
+ _reply_context;
+ struct lttng_dynamic_buffer payload;
+ struct lttcomm_session_destroy_command_header cmd_header;
+ struct lttng_trace_archive_location *location = NULL;
+ struct lttcomm_lttng_msg llm = {
+ .cmd_type = LTTNG_DESTROY_SESSION,
+ .ret_code = LTTNG_OK,
+ .pid = UINT32_MAX,
+ .cmd_header_size =
+ sizeof(struct lttcomm_session_destroy_command_header),
+ .data_size = 0,
+ };
+ size_t payload_size_before_location;
+
+ lttng_dynamic_buffer_init(&payload);
+
+ ret = lttng_dynamic_buffer_append(&payload, &llm, sizeof(llm));
+ if (ret) {
+ ERR("Failed to append session destruction message");
+ goto error;
+ }
+
+ cmd_header.rotation_state =
+ (int32_t) (reply_context->implicit_rotation_on_destroy ?
+ session->rotation_state :
+ LTTNG_ROTATION_STATE_NO_ROTATION);
+ ret = lttng_dynamic_buffer_append(&payload, &cmd_header,
+ sizeof(cmd_header));
+ if (ret) {
+ ERR("Failed to append session destruction command header");
+ goto error;
+ }
+
+ if (!reply_context->implicit_rotation_on_destroy) {
+ DBG("No implicit rotation performed during the destruction of session \"%s\", sending reply",
+ session->name);
+ goto send_reply;
+ }
+ if (session->rotation_state != LTTNG_ROTATION_STATE_COMPLETED) {
+ DBG("Rotation state of session \"%s\" is not \"completed\", sending session destruction reply",
+ session->name);
+ goto send_reply;
+ }
+
+ location = session_get_trace_archive_location(session);
+ if (!location) {
+ ERR("Failed to get the location of the trace archive produced during the destruction of session \"%s\"",
+ session->name);
+ goto error;
+ }
+
+ payload_size_before_location = payload.size;
+ comm_ret = lttng_trace_archive_location_serialize(location,
+ &payload);
+ if (comm_ret < 0) {
+ ERR("Failed to serialize the location of the trace archive produced during the destruction of session \"%s\"",
+ session->name);
+ goto error;
+ }
+ /* Update the message to indicate the location's length. */
+ ((struct lttcomm_lttng_msg *) payload.data)->data_size =
+ payload.size - payload_size_before_location;
+send_reply:
+ comm_ret = lttcomm_send_unix_sock(reply_context->reply_sock_fd,
+ payload.data, payload.size);
+ if (comm_ret != (ssize_t) payload.size) {
+ ERR("Failed to send result of the destruction of session \"%s\" to client",
+ session->name);
+ }
+error:
+ ret = close(reply_context->reply_sock_fd);
+ if (ret) {
+ PERROR("Failed to close client socket in deferred session destroy reply");
+ }
+ lttng_dynamic_buffer_reset(&payload);
+ free(_reply_context);
+}
+
+/*
+ * Command LTTNG_DESTROY_SESSION processed by the client thread.
+ *
+ * Called with session lock held.
+ */
+int cmd_destroy_session(struct ltt_session *session,
+ struct notification_thread_handle *notification_thread_handle,
+ int *sock_fd)
+{
+ int ret;
+ struct cmd_destroy_session_reply_context *reply_context = NULL;
+
+ if (sock_fd) {
+ reply_context = zmalloc(sizeof(*reply_context));
+ if (!reply_context) {
+ ret = LTTNG_ERR_NOMEM;
+ goto end;
+ }
+ reply_context->reply_sock_fd = *sock_fd;
+ }
+
+ /* Safety net */
+ assert(session);
+
+ DBG("Begin destroy session %s (id %" PRIu64 ")", session->name,
+ session->id);
+ if (session->active) {
+ DBG("Session \"%s\" is active, attempting to stop it before destroying it",
+ session->name);
+ ret = cmd_stop_trace(session);
+ if (ret != LTTNG_OK && ret != LTTNG_ERR_TRACE_ALREADY_STOPPED) {
+ /* Carry on with the destruction of the session. */
+ ERR("Failed to stop session \"%s\" as part of its destruction: %s",
+ session->name, lttng_strerror(-ret));
+ }
+ }
+
+ if (session->rotation_schedule_timer_enabled) {
+ if (timer_session_rotation_schedule_timer_stop(
+ session)) {
+ ERR("Failed to stop the \"rotation schedule\" timer of session %s",
+ session->name);
+ }
+ }
+
+ if (session->rotate_size) {
+ unsubscribe_session_consumed_size_rotation(session, notification_thread_handle);
+ session->rotate_size = 0;
+ }
+
+ if (session->most_recent_chunk_id.is_set &&
+ session->most_recent_chunk_id.value != 0 &&
+ session->current_trace_chunk) {
+ ret = cmd_rotate_session(session, NULL);
+ if (ret != LTTNG_OK) {
+ ERR("Failed to perform an implicit rotation as part of the destruction of session \"%s\": %s",
+ session->name, lttng_strerror(-ret));
+ }
+ if (reply_context) {
+ reply_context->implicit_rotation_on_destroy = true;
+ }
+ }
+
+ if (session->shm_path[0]) {
+ /*
+ * When a session is created with an explicit shm_path,
+ * the consumer daemon will create its shared memory files
+ * at that location and will *not* unlink them. This is normal
+ * as the intention of that feature is to make it possible
+ * to retrieve the content of those files should a crash occur.
+ *
+ * To ensure the content of those files can be used, the
+ * sessiond daemon will replicate the content of the metadata
+ * cache in a metadata file.
+ *
+ * On clean-up, it is expected that the consumer daemon will
+ * unlink the shared memory files and that the session daemon
+ * will unlink the metadata file. Then, the session's directory
+ * in the shm path can be removed.
+ *
+ * Unfortunately, a flaw in the design of the sessiond's and
+ * consumerd's tear down of channels makes it impossible to
+ * determine when the sessiond _and_ the consumerd have both
+ * destroyed their representation of a channel. For one, the
+ * unlinking, close, and rmdir happen in deferred 'call_rcu'
+ * callbacks in both daemons.
+ *
+ * However, it is also impossible for the sessiond to know when
+ * the consumer daemon is done destroying its channel(s) since
+ * it occurs as a reaction to the closing of the channel's file
+ * descriptor. There is no resulting communication initiated
+ * from the consumerd to the sessiond to confirm that the
+ * operation is completed (and was successful).
+ *
+ * Until this is all fixed, the session daemon checks for the
+ * removal of the session's shm path which makes it possible
+ * to safely advertise a session as having been destroyed.
+ *
+ * Prior to this fix, it was not possible to reliably save
+ * a session making use of the --shm-path option, destroy it,
+ * and load it again. This is because the creation of the
+ * session would fail upon seeing the session's shm path
+ * already in existence.
+ *
+ * Note that none of the error paths in the check for the
+ * directory's existence return an error. This is normal
+ * as there isn't much that can be done. The session will
+ * be destroyed properly, except that we can't offer the
+ * guarantee that the same session can be re-created.
+ */
+ current_completion_handler = &destroy_completion_handler.handler;
+ ret = lttng_strncpy(destroy_completion_handler.shm_path,
+ session->shm_path,
+ sizeof(destroy_completion_handler.shm_path));
+ assert(!ret);
+ }
+
+ /*
+ * The session is destroyed. However, note that the command context
+ * still holds a reference to the session, thus delaying its destruction
+ * _at least_ up to the point when that reference is released.
+ */
+ session_destroy(session);
+ if (reply_context) {
+ ret = session_add_destroy_notifier(session,
+ cmd_destroy_session_reply,
+ (void *) reply_context);
+ if (ret) {
+ ret = LTTNG_ERR_FATAL;
+ goto end;
+ } else {
+ *sock_fd = -1;
+ }
+ }
+ ret = LTTNG_OK;
+end:
+ return ret;
+}
+
+/*
+ * Command LTTNG_REGISTER_CONSUMER processed by the client thread.
+ */
+int cmd_register_consumer(struct ltt_session *session,
+ enum lttng_domain_type domain, const char *sock_path,
+ struct consumer_data *cdata)
+{
+ int ret, sock;
+ struct consumer_socket *socket = NULL;
+
+ 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 = LTTNG_ERR_KERN_CONSUMER_FAIL;
+ goto error;
+ }
+
+ sock = lttcomm_connect_unix_sock(sock_path);
+ if (sock < 0) {
+ ret = LTTNG_ERR_CONNECT_FAIL;
+ goto error;
+ }
+ cdata->cmd_sock = sock;
+
+ socket = consumer_allocate_socket(&cdata->cmd_sock);
+ if (socket == NULL) {
+ ret = close(sock);
+ if (ret < 0) {
+ PERROR("close register consumer");
+ }
+ cdata->cmd_sock = -1;
+ ret = LTTNG_ERR_FATAL;
+ goto error;
+ }
+
+ socket->lock = zmalloc(sizeof(pthread_mutex_t));
+ if (socket->lock == NULL) {
+ PERROR("zmalloc pthread mutex");
+ ret = LTTNG_ERR_FATAL;
+ goto error;
+ }
+ pthread_mutex_init(socket->lock, NULL);
+ socket->registered = 1;
+
+ rcu_read_lock();
+ consumer_add_socket(socket, ksess->consumer);
+ rcu_read_unlock();
+
+ pthread_mutex_lock(&cdata->pid_mutex);
+ cdata->pid = -1;
+ pthread_mutex_unlock(&cdata->pid_mutex);
+
+ break;
+ }
+ default:
+ /* TODO: Userspace tracing */
+ ret = LTTNG_ERR_UND;
+ goto error;
+ }
+
+ return LTTNG_OK;
+
+error:
+ if (socket) {
+ consumer_destroy_socket(socket);
+ }
+ return ret;
+}
+
+/*
+ * Command LTTNG_LIST_DOMAINS processed by the client thread.
+ */
+ssize_t cmd_list_domains(struct ltt_session *session,
+ struct lttng_domain **domains)
+{
+ int ret, index = 0;
+ ssize_t nb_dom = 0;
+ struct agent *agt;
+ struct lttng_ht_iter iter;
+
+ if (session->kernel_session != NULL) {
+ DBG3("Listing domains found kernel domain");
+ nb_dom++;
+ }
+
+ if (session->ust_session != NULL) {
+ DBG3("Listing domains found UST global domain");
+ nb_dom++;
+
+ rcu_read_lock();
+ cds_lfht_for_each_entry(session->ust_session->agents->ht, &iter.iter,
+ agt, node.node) {
+ if (agt->being_used) {
+ nb_dom++;
+ }
+ }
+ rcu_read_unlock();
+ }
+
+ if (!nb_dom) {
+ goto end;
+ }
+
+ *domains = zmalloc(nb_dom * sizeof(struct lttng_domain));
+ if (*domains == NULL) {
+ ret = LTTNG_ERR_FATAL;
+ goto error;
+ }
+
+ if (session->kernel_session != NULL) {
+ (*domains)[index].type = LTTNG_DOMAIN_KERNEL;
+
+ /* Kernel session buffer type is always GLOBAL */
+ (*domains)[index].buf_type = LTTNG_BUFFER_GLOBAL;
+
+ index++;
+ }
+
+ if (session->ust_session != NULL) {
+ (*domains)[index].type = LTTNG_DOMAIN_UST;
+ (*domains)[index].buf_type = session->ust_session->buffer_type;
+ index++;
+
+ rcu_read_lock();
+ cds_lfht_for_each_entry(session->ust_session->agents->ht, &iter.iter,
+ agt, node.node) {
+ if (agt->being_used) {
+ (*domains)[index].type = agt->domain;
+ (*domains)[index].buf_type = session->ust_session->buffer_type;
+ index++;
+ }
+ }
+ rcu_read_unlock();
+ }
+end:
+ return nb_dom;
+
+error:
+ /* Return negative value to differentiate return code */
+ return -ret;
+}
+
+
+/*
+ * Command LTTNG_LIST_CHANNELS processed by the client thread.
+ */
+ssize_t cmd_list_channels(enum lttng_domain_type domain,
+ struct ltt_session *session, struct lttng_channel **channels)
+{
+ ssize_t nb_chan = 0, payload_size = 0, ret;
+
+ switch (domain) {
+ case LTTNG_DOMAIN_KERNEL:
+ if (session->kernel_session != NULL) {
+ nb_chan = session->kernel_session->channel_count;
+ }
+ DBG3("Number of kernel channels %zd", nb_chan);
+ if (nb_chan <= 0) {
+ ret = -LTTNG_ERR_KERN_CHAN_NOT_FOUND;
+ goto end;
+ }
+ break;
+ case LTTNG_DOMAIN_UST:
+ if (session->ust_session != NULL) {
+ rcu_read_lock();
+ nb_chan = lttng_ht_get_count(
+ session->ust_session->domain_global.channels);
+ rcu_read_unlock();
+ }
+ DBG3("Number of UST global channels %zd", nb_chan);
+ if (nb_chan < 0) {
+ ret = -LTTNG_ERR_UST_CHAN_NOT_FOUND;
+ goto end;
+ }
+ break;
+ default:
+ ret = -LTTNG_ERR_UND;
+ goto end;
+ }
+
+ if (nb_chan > 0) {
+ const size_t channel_size = sizeof(struct lttng_channel) +
+ sizeof(struct lttng_channel_extended);
+ struct lttng_channel_extended *channel_exts;
+
+ payload_size = nb_chan * channel_size;
+ *channels = zmalloc(payload_size);
+ if (*channels == NULL) {
+ ret = -LTTNG_ERR_FATAL;
+ goto end;
+ }
+
+ channel_exts = ((void *) *channels) +
+ (nb_chan * sizeof(struct lttng_channel));
+ ret = list_lttng_channels(domain, session, *channels, channel_exts);
+ if (ret != LTTNG_OK) {
+ free(*channels);
+ *channels = NULL;
+ goto end;
+ }
+ } else {
+ *channels = NULL;
+ }
+
+ ret = payload_size;
+end:
+ return ret;
+}
+
+/*
+ * Command LTTNG_LIST_EVENTS processed by the client thread.
+ */
+ssize_t cmd_list_events(enum lttng_domain_type domain,
+ struct ltt_session *session, char *channel_name,
+ struct lttng_event **events, size_t *total_size)
+{
+ 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,
+ total_size);
+ }
+ 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,
+ total_size);
+ }
+ break;
+ }
+ case LTTNG_DOMAIN_LOG4J:
+ case LTTNG_DOMAIN_JUL:
+ case LTTNG_DOMAIN_PYTHON:
+ if (session->ust_session) {
+ struct lttng_ht_iter iter;
+ struct agent *agt;
+
+ rcu_read_lock();
+ cds_lfht_for_each_entry(session->ust_session->agents->ht,
+ &iter.iter, agt, node.node) {
+ if (agt->domain == domain) {
+ nb_event = list_lttng_agent_events(
+ agt, events,
+ total_size);
+ break;
+ }
+ }
+ rcu_read_unlock();
+ }
+ break;
+ default:
+ ret = LTTNG_ERR_UND;
+ goto error;
+ }
+
+ return nb_event;
+
+error:
+ /* Return negative value to differentiate return code */
+ return -ret;
+}
+
+/*
+ * Using the session list, filled a lttng_session array to send back to the
+ * client for session listing.
+ *
+ * The session list lock MUST be acquired before calling this function. Use
+ * session_lock_list() and session_unlock_list().
+ */
+void cmd_list_lttng_sessions(struct lttng_session *sessions,
+ size_t session_count, uid_t uid, gid_t gid)
+{
+ int ret;
+ unsigned int i = 0;
+ struct ltt_session *session;
+ struct ltt_session_list *list = session_get_list();
+ struct lttng_session_extended *extended =
+ (typeof(extended)) (&sessions[session_count]);
+
+ DBG("Getting all available session for UID %d GID %d",
+ uid, gid);
+ /*
+ * Iterate over session list and append data after the control struct in
+ * the buffer.
+ */
+ cds_list_for_each_entry(session, &list->head, list) {
+ if (!session_get(session)) {
+ continue;
+ }
+ /*
+ * Only list the sessions the user can control.
+ */
+ if (!session_access_ok(session, uid, gid) ||
+ session->destroyed) {
+ session_put(session);
+ continue;
+ }
+
+ struct ltt_kernel_session *ksess = session->kernel_session;
+ struct ltt_ust_session *usess = session->ust_session;