+ switch (event->type) {
+ case LTTNG_EVENT_ALL:
+ {
+ char *filter_expression_a = NULL;
+ struct lttng_filter_bytecode *filter_a = NULL;
+
+ /*
+ * We need to duplicate filter_expression and filter,
+ * because ownership is passed to first enable
+ * event.
+ */
+ if (filter_expression) {
+ filter_expression_a = strdup(filter_expression);
+ if (!filter_expression_a) {
+ ret = LTTNG_ERR_FATAL;
+ goto error;
+ }
+ }
+ if (filter) {
+ filter_a = zmalloc(sizeof(*filter_a) + filter->len);
+ if (!filter_a) {
+ free(filter_expression_a);
+ ret = LTTNG_ERR_FATAL;
+ goto error;
+ }
+ memcpy(filter_a, filter, sizeof(*filter_a) + filter->len);
+ }
+ event->type = LTTNG_EVENT_TRACEPOINT; /* Hack */
+ ret = event_kernel_enable_event(kchan, event,
+ filter_expression, filter);
+ /* We have passed ownership */
+ filter_expression = NULL;
+ filter = NULL;
+ if (ret != LTTNG_OK) {
+ if (channel_created) {
+ /* Let's not leak a useless channel. */
+ kernel_destroy_channel(kchan);
+ }
+ free(filter_expression_a);
+ free(filter_a);
+ goto error;
+ }
+ event->type = LTTNG_EVENT_SYSCALL; /* Hack */
+ ret = event_kernel_enable_event(kchan, event,
+ filter_expression_a, filter_a);
+ /* We have passed ownership */
+ filter_expression_a = NULL;
+ filter_a = NULL;
+ if (ret != LTTNG_OK) {
+ goto error;
+ }
+ break;
+ }
+ case LTTNG_EVENT_PROBE:
+ case LTTNG_EVENT_USERSPACE_PROBE:
+ case LTTNG_EVENT_FUNCTION:
+ case LTTNG_EVENT_FUNCTION_ENTRY:
+ case LTTNG_EVENT_TRACEPOINT:
+ ret = event_kernel_enable_event(kchan, event,
+ filter_expression, filter);
+ /* We have passed ownership */
+ filter_expression = NULL;
+ filter = NULL;
+ if (ret != LTTNG_OK) {
+ if (channel_created) {
+ /* Let's not leak a useless channel. */
+ kernel_destroy_channel(kchan);
+ }
+ goto error;
+ }
+ break;
+ case LTTNG_EVENT_SYSCALL:
+ ret = event_kernel_enable_event(kchan, event,
+ filter_expression, filter);
+ /* We have passed ownership */
+ filter_expression = NULL;
+ filter = NULL;
+ if (ret != LTTNG_OK) {
+ goto error;
+ }
+ break;
+ default:
+ ret = LTTNG_ERR_UNK;
+ goto error;
+ }
+
+ kernel_wait_quiescent();
+ break;
+ }
+ case LTTNG_DOMAIN_UST:
+ {
+ struct ltt_ust_channel *uchan;
+ struct ltt_ust_session *usess = session->ust_session;
+
+ assert(usess);
+
+ /*
+ * If a non-default channel has been created in the
+ * session, explicitely require that -c chan_name needs
+ * to be provided.
+ */
+ if (usess->has_non_default_channel && channel_name[0] == '\0') {
+ ret = LTTNG_ERR_NEED_CHANNEL_NAME;
+ goto error;
+ }
+
+ /* Get channel from global UST domain */
+ uchan = trace_ust_find_channel_by_name(usess->domain_global.channels,
+ channel_name);
+ if (uchan == NULL) {
+ /* Create default channel */
+ attr = channel_new_default_attr(LTTNG_DOMAIN_UST,
+ usess->buffer_type);
+ if (attr == NULL) {
+ ret = LTTNG_ERR_FATAL;
+ goto error;
+ }
+ if (lttng_strncpy(attr->name, channel_name,
+ sizeof(attr->name))) {
+ ret = LTTNG_ERR_INVALID;
+ goto error;
+ }
+
+ ret = cmd_enable_channel(session, domain, attr, wpipe);
+ if (ret != LTTNG_OK) {
+ goto error;
+ }
+
+ /* Get the newly created channel reference back */
+ uchan = trace_ust_find_channel_by_name(
+ usess->domain_global.channels, channel_name);
+ assert(uchan);
+ }
+
+ if (uchan->domain != LTTNG_DOMAIN_UST && !internal_event) {
+ /*
+ * Don't allow users to add UST events to channels which
+ * are assigned to a userspace subdomain (JUL, Log4J,
+ * Python, etc.).
+ */
+ ret = LTTNG_ERR_INVALID_CHANNEL_DOMAIN;
+ goto error;
+ }
+
+ if (!internal_event) {
+ /*
+ * Ensure the event name is not reserved for internal
+ * use.
+ */
+ ret = validate_ust_event_name(event->name);
+ if (ret) {
+ WARN("Userspace event name %s failed validation.",
+ event->name);
+ ret = LTTNG_ERR_INVALID_EVENT_NAME;
+ goto error;
+ }
+ }
+
+ /* At this point, the session and channel exist on the tracer */
+ ret = event_ust_enable_tracepoint(usess, uchan, event,
+ filter_expression, filter, exclusion,
+ internal_event);
+ /* We have passed ownership */
+ filter_expression = NULL;
+ filter = NULL;
+ exclusion = NULL;
+ if (ret == LTTNG_ERR_UST_EVENT_ENABLED) {
+ goto already_enabled;
+ } else if (ret != LTTNG_OK) {
+ goto error;
+ }
+ break;
+ }
+ case LTTNG_DOMAIN_LOG4J:
+ case LTTNG_DOMAIN_JUL:
+ case LTTNG_DOMAIN_PYTHON:
+ {
+ const char *default_event_name, *default_chan_name;
+ struct agent *agt;
+ struct lttng_event uevent;
+ struct lttng_domain tmp_dom;
+ struct ltt_ust_session *usess = session->ust_session;
+
+ assert(usess);
+
+ if (!agent_tracing_is_enabled()) {
+ DBG("Attempted to enable an event in an agent domain but the agent thread is not running");
+ ret = LTTNG_ERR_AGENT_TRACING_DISABLED;
+ goto error;
+ }
+
+ agt = trace_ust_find_agent(usess, domain->type);
+ if (!agt) {
+ agt = agent_create(domain->type);
+ if (!agt) {
+ ret = LTTNG_ERR_NOMEM;
+ goto error;
+ }
+ agent_add(agt, usess->agents);
+ }
+
+ /* Create the default tracepoint. */
+ memset(&uevent, 0, sizeof(uevent));
+ uevent.type = LTTNG_EVENT_TRACEPOINT;
+ uevent.loglevel_type = LTTNG_EVENT_LOGLEVEL_ALL;
+ default_event_name = event_get_default_agent_ust_name(
+ domain->type);
+ if (!default_event_name) {
+ ret = LTTNG_ERR_FATAL;
+ goto error;
+ }
+ strncpy(uevent.name, default_event_name, sizeof(uevent.name));
+ uevent.name[sizeof(uevent.name) - 1] = '\0';
+
+ /*
+ * The domain type is changed because we are about to enable the
+ * default channel and event for the JUL domain that are hardcoded.
+ * This happens in the UST domain.
+ */
+ memcpy(&tmp_dom, domain, sizeof(tmp_dom));
+ tmp_dom.type = LTTNG_DOMAIN_UST;
+
+ switch (domain->type) {
+ case LTTNG_DOMAIN_LOG4J:
+ default_chan_name = DEFAULT_LOG4J_CHANNEL_NAME;
+ break;
+ case LTTNG_DOMAIN_JUL:
+ default_chan_name = DEFAULT_JUL_CHANNEL_NAME;
+ break;
+ case LTTNG_DOMAIN_PYTHON:
+ default_chan_name = DEFAULT_PYTHON_CHANNEL_NAME;
+ break;
+ default:
+ /* The switch/case we are in makes this impossible */
+ assert(0);
+ }
+
+ {
+ char *filter_expression_copy = NULL;
+ struct lttng_filter_bytecode *filter_copy = NULL;
+
+ if (filter) {
+ const size_t filter_size = sizeof(
+ struct lttng_filter_bytecode)
+ + filter->len;
+
+ filter_copy = zmalloc(filter_size);
+ if (!filter_copy) {
+ ret = LTTNG_ERR_NOMEM;
+ goto error;
+ }
+ memcpy(filter_copy, filter, filter_size);
+
+ filter_expression_copy =
+ strdup(filter_expression);
+ if (!filter_expression) {
+ ret = LTTNG_ERR_NOMEM;
+ }
+
+ if (!filter_expression_copy || !filter_copy) {
+ free(filter_expression_copy);
+ free(filter_copy);
+ goto error;
+ }
+ }
+
+ ret = cmd_enable_event_internal(session, &tmp_dom,
+ (char *) default_chan_name,
+ &uevent, filter_expression_copy,
+ filter_copy, NULL, wpipe);
+ }
+
+ if (ret == LTTNG_ERR_UST_EVENT_ENABLED) {
+ goto already_enabled;
+ } else if (ret != LTTNG_OK) {
+ goto error;
+ }
+
+ /* The wild card * means that everything should be enabled. */
+ if (strncmp(event->name, "*", 1) == 0 && strlen(event->name) == 1) {
+ ret = event_agent_enable_all(usess, agt, event, filter,
+ filter_expression);
+ } else {
+ ret = event_agent_enable(usess, agt, event, filter,
+ filter_expression);
+ }
+ filter = NULL;
+ filter_expression = NULL;
+ if (ret != LTTNG_OK) {
+ goto error;
+ }
+
+ break;
+ }
+ default:
+ ret = LTTNG_ERR_UND;
+ goto error;
+ }
+
+ ret = LTTNG_OK;
+
+already_enabled:
+error:
+ free(filter_expression);
+ free(filter);
+ free(exclusion);
+ channel_attr_destroy(attr);
+ rcu_read_unlock();
+ return ret;
+}
+
+/*
+ * Command LTTNG_ENABLE_EVENT processed by the client thread.
+ * We own filter, exclusion, and filter_expression.
+ */
+int cmd_enable_event(struct ltt_session *session,
+ const struct lttng_domain *domain,
+ char *channel_name, struct lttng_event *event,
+ char *filter_expression,
+ struct lttng_filter_bytecode *filter,
+ struct lttng_event_exclusion *exclusion,
+ int wpipe)
+{
+ return _cmd_enable_event(session, domain, channel_name, event,
+ filter_expression, filter, exclusion, wpipe, false);
+}
+
+/*
+ * Enable an event which is internal to LTTng. An internal should
+ * never be made visible to clients and are immune to checks such as
+ * reserved names.
+ */
+static int cmd_enable_event_internal(struct ltt_session *session,
+ const struct lttng_domain *domain,
+ char *channel_name, struct lttng_event *event,
+ char *filter_expression,
+ struct lttng_filter_bytecode *filter,
+ struct lttng_event_exclusion *exclusion,
+ int wpipe)
+{
+ return _cmd_enable_event(session, domain, channel_name, event,
+ filter_expression, filter, exclusion, wpipe, true);
+}
+
+/*
+ * Command LTTNG_LIST_TRACEPOINTS processed by the client thread.
+ */
+ssize_t cmd_list_tracepoints(enum lttng_domain_type domain,
+ struct lttng_event **events)
+{
+ int ret;
+ ssize_t nb_events = 0;
+
+ switch (domain) {
+ case LTTNG_DOMAIN_KERNEL:
+ nb_events = kernel_list_events(events);
+ if (nb_events < 0) {
+ ret = LTTNG_ERR_KERN_LIST_FAIL;
+ goto error;
+ }
+ break;
+ case LTTNG_DOMAIN_UST:
+ nb_events = ust_app_list_events(events);
+ if (nb_events < 0) {
+ ret = LTTNG_ERR_UST_LIST_FAIL;
+ goto error;
+ }
+ break;
+ case LTTNG_DOMAIN_LOG4J:
+ case LTTNG_DOMAIN_JUL:
+ case LTTNG_DOMAIN_PYTHON:
+ nb_events = agent_list_events(events, domain);
+ if (nb_events < 0) {
+ ret = LTTNG_ERR_UST_LIST_FAIL;
+ goto error;
+ }
+ break;
+ default:
+ ret = LTTNG_ERR_UND;
+ goto error;
+ }
+
+ return nb_events;
+
+error:
+ /* Return negative value to differentiate return code */
+ return -ret;
+}
+
+/*
+ * Command LTTNG_LIST_TRACEPOINT_FIELDS processed by the client thread.
+ */
+ssize_t cmd_list_tracepoint_fields(enum lttng_domain_type domain,
+ struct lttng_event_field **fields)
+{
+ int ret;
+ ssize_t nb_fields = 0;
+
+ switch (domain) {
+ case LTTNG_DOMAIN_UST:
+ nb_fields = ust_app_list_event_fields(fields);
+ if (nb_fields < 0) {
+ ret = LTTNG_ERR_UST_LIST_FAIL;
+ goto error;
+ }
+ break;
+ case LTTNG_DOMAIN_KERNEL:
+ default: /* fall-through */
+ ret = LTTNG_ERR_UND;
+ goto error;
+ }
+
+ return nb_fields;
+
+error:
+ /* Return negative value to differentiate return code */
+ return -ret;
+}
+
+ssize_t cmd_list_syscalls(struct lttng_event **events)
+{
+ return syscall_table_list(events);
+}
+
+/*
+ * Command LTTNG_LIST_TRACKER_PIDS processed by the client thread.
+ *
+ * Called with session lock held.
+ */
+ssize_t cmd_list_tracker_pids(struct ltt_session *session,
+ enum lttng_domain_type domain, int32_t **pids)
+{
+ int ret;
+ ssize_t nr_pids = 0;
+
+ switch (domain) {
+ case LTTNG_DOMAIN_KERNEL:
+ {
+ struct ltt_kernel_session *ksess;
+
+ ksess = session->kernel_session;
+ nr_pids = kernel_list_tracker_pids(ksess, pids);
+ if (nr_pids < 0) {
+ ret = LTTNG_ERR_KERN_LIST_FAIL;
+ goto error;
+ }
+ break;
+ }
+ case LTTNG_DOMAIN_UST:
+ {
+ struct ltt_ust_session *usess;
+
+ usess = session->ust_session;
+ nr_pids = trace_ust_list_tracker_pids(usess, pids);
+ if (nr_pids < 0) {
+ ret = LTTNG_ERR_UST_LIST_FAIL;
+ goto error;
+ }
+ break;
+ }
+ case LTTNG_DOMAIN_LOG4J:
+ case LTTNG_DOMAIN_JUL:
+ case LTTNG_DOMAIN_PYTHON:
+ default:
+ ret = LTTNG_ERR_UND;
+ goto error;
+ }
+
+ return nr_pids;
+
+error:
+ /* Return negative value to differentiate return code */
+ return -ret;
+}
+
+/*
+ * Command LTTNG_START_TRACE processed by the client thread.
+ *
+ * Called with session mutex held.
+ */
+int cmd_start_trace(struct ltt_session *session)
+{
+ enum lttng_error_code ret;
+ unsigned long nb_chan = 0;
+ struct ltt_kernel_session *ksession;
+ struct ltt_ust_session *usess;
+ const bool session_rotated_after_last_stop =
+ session->rotated_after_last_stop;
+
+ assert(session);
+
+ /* Ease our life a bit ;) */
+ ksession = session->kernel_session;
+ usess = session->ust_session;
+
+ /* Is the session already started? */
+ if (session->active) {
+ ret = LTTNG_ERR_TRACE_ALREADY_STARTED;
+ goto error;
+ }
+
+ if (session->rotation_state == LTTNG_ROTATION_STATE_ONGOING &&
+ !session->current_trace_chunk) {
+ /*
+ * A rotation was launched while the session was stopped and
+ * it has not been completed yet. It is not possible to start
+ * the session since starting the session here would require a
+ * rotation from "NULL" to a new trace chunk. That rotation
+ * would overlap with the ongoing rotation, which is not
+ * supported.
+ */
+ WARN("Refusing to start session \"%s\" as a rotation launched after the last \"stop\" is still ongoing",
+ session->name);
+ ret = LTTNG_ERR_ROTATION_PENDING;
+ goto error;
+ }
+
+ /*
+ * Starting a session without channel is useless since after that it's not
+ * possible to enable channel thus inform the client.
+ */
+ if (usess && usess->domain_global.channels) {
+ nb_chan += lttng_ht_get_count(usess->domain_global.channels);
+ }
+ if (ksession) {
+ nb_chan += ksession->channel_count;
+ }
+ if (!nb_chan) {
+ ret = LTTNG_ERR_NO_CHANNEL;
+ goto error;
+ }
+
+ session->active = 1;
+ session->rotated_after_last_stop = false;
+ if (session->output_traces && !session->current_trace_chunk) {
+ if (!session->has_been_started) {
+ struct lttng_trace_chunk *trace_chunk;
+
+ DBG("Creating initial trace chunk of session \"%s\"",
+ session->name);
+ trace_chunk = session_create_new_trace_chunk(
+ session, NULL, NULL, NULL);
+ if (!trace_chunk) {
+ ret = LTTNG_ERR_CREATE_DIR_FAIL;
+ goto error;
+ }
+ assert(!session->current_trace_chunk);
+ ret = session_set_trace_chunk(session, trace_chunk,
+ NULL);
+ lttng_trace_chunk_put(trace_chunk);
+ if (ret) {
+ ret = LTTNG_ERR_CREATE_TRACE_CHUNK_FAIL_CONSUMER;
+ goto error;
+ }
+ } else {
+ DBG("Rotating session \"%s\" from its current \"NULL\" trace chunk to a new chunk",
+ session->name);
+ /*
+ * Rotate existing streams into the new chunk.
+ * This is a "quiet" rotation has no client has
+ * explicitly requested this operation.
+ *
+ * There is also no need to wait for the rotation
+ * to complete as it will happen immediately. No data
+ * was produced as the session was stopped, so the
+ * rotation should happen on reception of the command.
+ */
+ ret = cmd_rotate_session(session, NULL, true);
+ if (ret != LTTNG_OK) {
+ goto error;
+ }
+ }
+ }
+
+ /* Kernel tracing */
+ if (ksession != NULL) {
+ DBG("Start kernel tracing session %s", session->name);
+ ret = start_kernel_session(ksession);
+ if (ret != LTTNG_OK) {
+ goto error;
+ }
+ }
+
+ /* Flag session that trace should start automatically */
+ if (usess) {
+ int int_ret = ust_app_start_trace_all(usess);
+
+ if (int_ret < 0) {
+ ret = LTTNG_ERR_UST_START_FAIL;
+ goto error;
+ }
+ }
+
+ /*
+ * Clear the flag that indicates that a rotation was done while the
+ * session was stopped.
+ */
+ session->rotated_after_last_stop = false;
+
+ if (session->rotate_timer_period) {
+ int int_ret = timer_session_rotation_schedule_timer_start(
+ session, session->rotate_timer_period);
+
+ if (int_ret < 0) {
+ ERR("Failed to enable rotate timer");
+ ret = LTTNG_ERR_UNK;
+ goto error;
+ }
+ }
+
+ ret = LTTNG_OK;
+
+error:
+ if (ret == LTTNG_OK) {
+ /* Flag this after a successful start. */
+ session->has_been_started |= 1;
+ } else {
+ session->active = 0;
+ /* Restore initial state on error. */
+ session->rotated_after_last_stop =
+ session_rotated_after_last_stop;
+ }
+ 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();
+
+ /* 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");
+ error_occurred = true;
+ }
+ }
+
+ /* 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");
+ error_occurred = true;
+ }
+ }
+
+ 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;
+}
+
+/*
+ * Set the base_path of the session only if subdir of a control uris is set.
+ * Return LTTNG_OK on success, otherwise LTTNG_ERR_*.
+ */
+static int set_session_base_path_from_uris(struct ltt_session *session,
+ size_t nb_uri,
+ struct lttng_uri *uris)
+{
+ int ret;
+ size_t i;
+
+ for (i = 0; i < nb_uri; i++) {
+ if (uris[i].stype != LTTNG_STREAM_CONTROL ||
+ uris[i].subdir[0] == '\0') {
+ /* Not interested in these URIs */
+ continue;
+ }
+
+ if (session->base_path != NULL) {
+ free(session->base_path);
+ session->base_path = NULL;
+ }
+
+ /* Set session base_path */
+ session->base_path = strdup(uris[i].subdir);
+ if (!session->base_path) {
+ PERROR("Failed to copy base path \"%s\" to session \"%s\"",
+ uris[i].subdir, session->name);
+ ret = LTTNG_ERR_NOMEM;
+ goto error;
+ }
+ DBG2("Setting base path \"%s\" for session \"%s\"",
+ session->base_path, session->name);
+ }
+ ret = LTTNG_OK;
+error:
+ return ret;
+}
+
+/*
+ * Command LTTNG_SET_CONSUMER_URI processed by the client thread.
+ */
+int cmd_set_consumer_uri(struct ltt_session *session, size_t nb_uri,
+ struct lttng_uri *uris)
+{
+ int ret, i;
+ struct ltt_kernel_session *ksess = session->kernel_session;
+ struct ltt_ust_session *usess = session->ust_session;
+
+ 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 session base path if any. This is done inside
+ * cmd_set_consumer_uri to preserve backward compatibility of the
+ * previous session creation api vs the session descriptor api.
+ */
+ ret = set_session_base_path_from_uris(session, nb_uri, uris);
+ if (ret != LTTNG_OK) {
+ goto error;
+ }
+
+ /* Set the "global" consumer URIs */
+ for (i = 0; i < nb_uri; i++) {
+ ret = add_uri_to_consumer(session, session->consumer, &uris[i],
+ LTTNG_DOMAIN_NONE);
+ if (ret != LTTNG_OK) {
+ goto error;
+ }
+ }
+
+ /* Set UST session URIs */
+ if (session->ust_session) {
+ for (i = 0; i < nb_uri; i++) {
+ ret = add_uri_to_consumer(session,
+ session->ust_session->consumer,
+ &uris[i], LTTNG_DOMAIN_UST);
+ if (ret != LTTNG_OK) {
+ goto error;
+ }
+ }
+ }
+
+ /* Set kernel session URIs */
+ if (session->kernel_session) {
+ for (i = 0; i < nb_uri; i++) {
+ ret = add_uri_to_consumer(session,
+ session->kernel_session->consumer,
+ &uris[i], LTTNG_DOMAIN_KERNEL);
+ if (ret != LTTNG_OK) {
+ goto error;
+ }
+ }
+ }
+
+ /*
+ * Make sure to set the session in output mode after we set URI since a
+ * session can be created without URL (thus flagged in no output mode).
+ */
+ session->output_traces = 1;
+ if (ksess) {
+ ksess->output_traces = 1;
+ }
+
+ if (usess) {
+ usess->output_traces = 1;
+ }
+
+ /* All good! */
+ ret = LTTNG_OK;
+
+error:
+ return ret;
+}
+
+static
+enum lttng_error_code set_session_output_from_descriptor(
+ struct ltt_session *session,
+ const struct lttng_session_descriptor *descriptor)
+{
+ int ret;
+ enum lttng_error_code ret_code = LTTNG_OK;
+ enum lttng_session_descriptor_type session_type =
+ lttng_session_descriptor_get_type(descriptor);
+ enum lttng_session_descriptor_output_type output_type =
+ lttng_session_descriptor_get_output_type(descriptor);
+ struct lttng_uri uris[2] = {};
+ size_t uri_count = 0;
+
+ switch (output_type) {
+ case LTTNG_SESSION_DESCRIPTOR_OUTPUT_TYPE_NONE:
+ goto end;
+ case LTTNG_SESSION_DESCRIPTOR_OUTPUT_TYPE_LOCAL:
+ lttng_session_descriptor_get_local_output_uri(descriptor,
+ &uris[0]);
+ uri_count = 1;
+ break;
+ case LTTNG_SESSION_DESCRIPTOR_OUTPUT_TYPE_NETWORK:
+ lttng_session_descriptor_get_network_output_uris(descriptor,
+ &uris[0], &uris[1]);
+ uri_count = 2;
+ break;
+ default:
+ ret_code = LTTNG_ERR_INVALID;
+ goto end;
+ }
+
+ switch (session_type) {
+ case LTTNG_SESSION_DESCRIPTOR_TYPE_SNAPSHOT:
+ {
+ struct snapshot_output *new_output = NULL;
+
+ new_output = snapshot_output_alloc();
+ if (!new_output) {
+ ret_code = LTTNG_ERR_NOMEM;
+ goto end;
+ }
+
+ ret = snapshot_output_init_with_uri(session,
+ DEFAULT_SNAPSHOT_MAX_SIZE,
+ NULL, uris, uri_count, session->consumer,
+ new_output, &session->snapshot);
+ if (ret < 0) {
+ ret_code = (ret == -ENOMEM) ?
+ LTTNG_ERR_NOMEM : LTTNG_ERR_INVALID;
+ snapshot_output_destroy(new_output);
+ goto end;
+ }
+ snapshot_add_output(&session->snapshot, new_output);
+ break;
+ }
+ case LTTNG_SESSION_DESCRIPTOR_TYPE_REGULAR:
+ case LTTNG_SESSION_DESCRIPTOR_TYPE_LIVE:
+ {
+ ret_code = cmd_set_consumer_uri(session, uri_count, uris);
+ break;
+ }
+ default:
+ ret_code = LTTNG_ERR_INVALID;
+ goto end;
+ }
+end:
+ return ret_code;
+}
+
+static
+enum lttng_error_code cmd_create_session_from_descriptor(
+ struct lttng_session_descriptor *descriptor,
+ const lttng_sock_cred *creds,
+ const char *home_path)
+{
+ int ret;
+ enum lttng_error_code ret_code;
+ const char *session_name;
+ struct ltt_session *new_session = NULL;
+ enum lttng_session_descriptor_status descriptor_status;
+
+ session_lock_list();
+ if (home_path) {
+ if (*home_path != '/') {
+ ERR("Home path provided by client is not absolute");
+ ret_code = LTTNG_ERR_INVALID;
+ goto end;
+ }
+ }
+
+ descriptor_status = lttng_session_descriptor_get_session_name(
+ descriptor, &session_name);
+ switch (descriptor_status) {
+ case LTTNG_SESSION_DESCRIPTOR_STATUS_OK:
+ break;
+ case LTTNG_SESSION_DESCRIPTOR_STATUS_UNSET:
+ session_name = NULL;
+ break;
+ default:
+ ret_code = LTTNG_ERR_INVALID;
+ goto end;
+ }
+
+ ret_code = session_create(session_name, creds->uid, creds->gid,
+ &new_session);
+ if (ret_code != LTTNG_OK) {
+ goto end;
+ }
+
+ if (!session_name) {
+ ret = lttng_session_descriptor_set_session_name(descriptor,
+ new_session->name);
+ if (ret) {
+ ret_code = LTTNG_ERR_SESSION_FAIL;
+ goto end;
+ }
+ }
+
+ if (!lttng_session_descriptor_is_output_destination_initialized(
+ descriptor)) {
+ /*
+ * Only include the session's creation time in the output
+ * destination if the name of the session itself was
+ * not auto-generated.
+ */
+ ret_code = lttng_session_descriptor_set_default_output(
+ descriptor,
+ session_name ? &new_session->creation_time : NULL,
+ home_path);
+ if (ret_code != LTTNG_OK) {
+ goto end;
+ }
+ } else {
+ new_session->has_user_specified_directory =
+ lttng_session_descriptor_has_output_directory(
+ descriptor);
+ }
+
+ switch (lttng_session_descriptor_get_type(descriptor)) {
+ case LTTNG_SESSION_DESCRIPTOR_TYPE_SNAPSHOT:
+ new_session->snapshot_mode = 1;
+ break;
+ case LTTNG_SESSION_DESCRIPTOR_TYPE_LIVE:
+ new_session->live_timer =
+ lttng_session_descriptor_live_get_timer_interval(
+ descriptor);
+ break;
+ default:
+ break;
+ }
+
+ ret_code = set_session_output_from_descriptor(new_session, descriptor);
+ if (ret_code != LTTNG_OK) {
+ goto end;
+ }
+ new_session->consumer->enabled = 1;
+ ret_code = LTTNG_OK;
+end:
+ /* Release reference provided by the session_create function. */
+ session_put(new_session);
+ if (ret_code != LTTNG_OK && new_session) {
+ /* Release the global reference on error. */
+ session_destroy(new_session);
+ }
+ session_unlock_list();
+ return ret_code;
+}
+
+enum lttng_error_code cmd_create_session(struct command_ctx *cmd_ctx, int sock,
+ struct lttng_session_descriptor **return_descriptor)
+{
+ int ret;
+ size_t payload_size;
+ struct lttng_dynamic_buffer payload;
+ struct lttng_buffer_view home_dir_view;
+ struct lttng_buffer_view session_descriptor_view;
+ struct lttng_session_descriptor *session_descriptor = NULL;
+ enum lttng_error_code ret_code;
+
+ lttng_dynamic_buffer_init(&payload);
+ if (cmd_ctx->lsm->u.create_session.home_dir_size >=
+ LTTNG_PATH_MAX) {
+ ret_code = LTTNG_ERR_INVALID;
+ goto error;
+ }
+ if (cmd_ctx->lsm->u.create_session.session_descriptor_size >
+ LTTNG_SESSION_DESCRIPTOR_MAX_LEN) {
+ ret_code = LTTNG_ERR_INVALID;
+ goto error;
+ }
+
+ payload_size = cmd_ctx->lsm->u.create_session.home_dir_size +
+ cmd_ctx->lsm->u.create_session.session_descriptor_size;
+ ret = lttng_dynamic_buffer_set_size(&payload, payload_size);
+ if (ret) {
+ ret_code = LTTNG_ERR_NOMEM;
+ goto error;
+ }
+
+ ret = lttcomm_recv_unix_sock(sock, payload.data, payload.size);
+ if (ret <= 0) {
+ ERR("Reception of session descriptor failed, aborting.");
+ ret_code = LTTNG_ERR_SESSION_FAIL;
+ goto error;
+ }
+
+ home_dir_view = lttng_buffer_view_from_dynamic_buffer(
+ &payload,
+ 0,
+ cmd_ctx->lsm->u.create_session.home_dir_size);
+ 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 = reply_context->destruction_status,
+ .pid = UINT32_MAX,
+ .cmd_header_size =
+ sizeof(struct lttcomm_session_destroy_command_header),
+ .data_size = 0,
+ };
+ size_t payload_size_before_location;
+
+ lttng_dynamic_buffer_init(&payload);
+
+ ret = lttng_dynamic_buffer_append(&payload, &llm, sizeof(llm));
+ if (ret) {
+ ERR("Failed to append session destruction message");
+ goto error;
+ }
+
+ cmd_header.rotation_state =
+ (int32_t) (reply_context->implicit_rotation_on_destroy ?
+ session->rotation_state :
+ LTTNG_ROTATION_STATE_NO_ROTATION);
+ ret = lttng_dynamic_buffer_append(&payload, &cmd_header,
+ sizeof(cmd_header));
+ if (ret) {
+ ERR("Failed to append session destruction command header");
+ goto error;
+ }
+
+ if (!reply_context->implicit_rotation_on_destroy) {
+ DBG("No implicit rotation performed during the destruction of session \"%s\", sending reply",
+ session->name);
+ goto send_reply;
+ }
+ if (session->rotation_state != LTTNG_ROTATION_STATE_COMPLETED) {
+ DBG("Rotation state of session \"%s\" is not \"completed\", sending session destruction reply",
+ session->name);
+ goto send_reply;
+ }
+
+ location = session_get_trace_archive_location(session);
+ if (!location) {
+ ERR("Failed to get the location of the trace archive produced during the destruction of session \"%s\"",
+ session->name);
+ goto error;
+ }
+
+ payload_size_before_location = payload.size;
+ comm_ret = lttng_trace_archive_location_serialize(location,
+ &payload);
+ if (comm_ret < 0) {
+ ERR("Failed to serialize the location of the trace archive produced during the destruction of session \"%s\"",
+ session->name);
+ goto error;
+ }
+ /* Update the message to indicate the location's length. */
+ ((struct lttcomm_lttng_msg *) payload.data)->data_size =
+ payload.size - payload_size_before_location;
+send_reply:
+ comm_ret = lttcomm_send_unix_sock(reply_context->reply_sock_fd,
+ payload.data, payload.size);
+ if (comm_ret != (ssize_t) payload.size) {
+ ERR("Failed to send result of the destruction of session \"%s\" to client",
+ session->name);
+ }
+error:
+ ret = close(reply_context->reply_sock_fd);
+ if (ret) {
+ PERROR("Failed to close client socket in deferred session destroy reply");
+ }
+ lttng_dynamic_buffer_reset(&payload);
+ free(_reply_context);
+}
+
+/*
+ * Command LTTNG_DESTROY_SESSION processed by the client thread.
+ *
+ * Called with session lock held.
+ */
+int cmd_destroy_session(struct ltt_session *session,
+ struct notification_thread_handle *notification_thread_handle,
+ int *sock_fd)
+{
+ int ret;
+ enum lttng_error_code destruction_last_error = LTTNG_OK;
+ struct cmd_destroy_session_reply_context *reply_context = NULL;
+
+ if (sock_fd) {
+ reply_context = zmalloc(sizeof(*reply_context));
+ if (!reply_context) {
+ ret = LTTNG_ERR_NOMEM;
+ goto end;
+ }
+ reply_context->reply_sock_fd = *sock_fd;
+ }
+
+ /* Safety net */
+ assert(session);
+
+ DBG("Begin destroy session %s (id %" PRIu64 ")", session->name,
+ session->id);
+ if (session->active) {
+ DBG("Session \"%s\" is active, attempting to stop it before destroying it",
+ session->name);
+ ret = cmd_stop_trace(session);
+ if (ret != LTTNG_OK && ret != LTTNG_ERR_TRACE_ALREADY_STOPPED) {
+ /* Carry on with the destruction of the session. */
+ ERR("Failed to stop session \"%s\" as part of its destruction: %s",
+ session->name, lttng_strerror(-ret));
+ destruction_last_error = ret;
+ }
+ }
+
+ if (session->rotation_schedule_timer_enabled) {
+ if (timer_session_rotation_schedule_timer_stop(
+ session)) {
+ ERR("Failed to stop the \"rotation schedule\" timer of session %s",
+ session->name);
+ destruction_last_error = LTTNG_ERR_TIMER_STOP_ERROR;
+ }
+ }
+
+ if (session->rotate_size) {
+ unsubscribe_session_consumed_size_rotation(session, notification_thread_handle);
+ session->rotate_size = 0;
+ }
+
+ if (session->most_recent_chunk_id.is_set &&
+ session->most_recent_chunk_id.value != 0 &&
+ session->current_trace_chunk && session->output_traces) {
+ /*
+ * Perform a last rotation on destruction if rotations have
+ * occurred during the session's lifetime.
+ */
+ ret = cmd_rotate_session(session, NULL, false);
+ if (ret != LTTNG_OK) {
+ ERR("Failed to perform an implicit rotation as part of the destruction of session \"%s\": %s",
+ session->name, lttng_strerror(-ret));
+ destruction_last_error = -ret;
+ }
+ if (reply_context) {
+ reply_context->implicit_rotation_on_destroy = true;
+ }
+ } else if (session->has_been_started && session->current_trace_chunk) {
+ /*
+ * The user has not triggered a session rotation. However, to
+ * ensure all data has been consumed, the session is rotated
+ * to a 'null' trace chunk before it is destroyed.
+ *
+ * This is a "quiet" rotation meaning that no notification is
+ * emitted and no renaming of the current trace chunk takes
+ * place.
+ */
+ ret = cmd_rotate_session(session, NULL, true);
+ if (ret != LTTNG_OK) {
+ ERR("Failed to perform a quiet rotation as part of the destruction of session \"%s\": %s",
+ session->name, lttng_strerror(-ret));
+ destruction_last_error = -ret;
+ }
+ }
+
+ if (session->shm_path[0]) {
+ /*
+ * When a session is created with an explicit shm_path,
+ * the consumer daemon will create its shared memory files
+ * at that location and will *not* unlink them. This is normal
+ * as the intention of that feature is to make it possible
+ * to retrieve the content of those files should a crash occur.
+ *
+ * To ensure the content of those files can be used, the
+ * sessiond daemon will replicate the content of the metadata
+ * cache in a metadata file.
+ *
+ * On clean-up, it is expected that the consumer daemon will
+ * unlink the shared memory files and that the session daemon
+ * will unlink the metadata file. Then, the session's directory
+ * in the shm path can be removed.
+ *
+ * Unfortunately, a flaw in the design of the sessiond's and
+ * consumerd's tear down of channels makes it impossible to
+ * determine when the sessiond _and_ the consumerd have both
+ * destroyed their representation of a channel. For one, the
+ * unlinking, close, and rmdir happen in deferred 'call_rcu'
+ * callbacks in both daemons.
+ *
+ * However, it is also impossible for the sessiond to know when
+ * the consumer daemon is done destroying its channel(s) since
+ * it occurs as a reaction to the closing of the channel's file
+ * descriptor. There is no resulting communication initiated
+ * from the consumerd to the sessiond to confirm that the
+ * operation is completed (and was successful).
+ *
+ * Until this is all fixed, the session daemon checks for the
+ * removal of the session's shm path which makes it possible
+ * to safely advertise a session as having been destroyed.
+ *
+ * Prior to this fix, it was not possible to reliably save
+ * a session making use of the --shm-path option, destroy it,
+ * and load it again. This is because the creation of the
+ * session would fail upon seeing the session's shm path
+ * already in existence.
+ *
+ * Note that none of the error paths in the check for the
+ * directory's existence return an error. This is normal
+ * as there isn't much that can be done. The session will
+ * be destroyed properly, except that we can't offer the
+ * guarantee that the same session can be re-created.
+ */
+ current_completion_handler = &destroy_completion_handler.handler;
+ ret = lttng_strncpy(destroy_completion_handler.shm_path,
+ session->shm_path,
+ sizeof(destroy_completion_handler.shm_path));
+ assert(!ret);
+ }
+
+ /*
+ * The session is destroyed. However, note that the command context
+ * still holds a reference to the session, thus delaying its destruction
+ * _at least_ up to the point when that reference is released.
+ */
+ session_destroy(session);
+ if (reply_context) {
+ reply_context->destruction_status = destruction_last_error;
+ ret = session_add_destroy_notifier(session,
+ cmd_destroy_session_reply,
+ (void *) reply_context);
+ if (ret) {
+ ret = LTTNG_ERR_FATAL;
+ goto end;
+ } else {
+ *sock_fd = -1;
+ }
+ }
+ ret = LTTNG_OK;
+end:
+ return ret;
+}
+
+/*
+ * Command LTTNG_REGISTER_CONSUMER processed by the client thread.
+ */
+int cmd_register_consumer(struct ltt_session *session,
+ enum lttng_domain_type domain, const char *sock_path,
+ struct consumer_data *cdata)
+{
+ int ret, sock;
+ struct consumer_socket *socket = NULL;
+
+ 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;
+
+ if (session->consumer->type == CONSUMER_DST_NET ||
+ (ksess && ksess->consumer->type == CONSUMER_DST_NET) ||
+ (usess && usess->consumer->type == CONSUMER_DST_NET)) {
+ ret = build_network_session_path(sessions[i].path,
+ sizeof(sessions[i].path), session);
+ } else {
+ ret = snprintf(sessions[i].path, sizeof(sessions[i].path), "%s",
+ session->consumer->dst.session_root_path);
+ }
+ if (ret < 0) {
+ PERROR("snprintf session path");
+ session_put(session);
+ continue;
+ }
+
+ strncpy(sessions[i].name, session->name, NAME_MAX);
+ sessions[i].name[NAME_MAX - 1] = '\0';
+ sessions[i].enabled = session->active;
+ sessions[i].snapshot_mode = session->snapshot_mode;
+ sessions[i].live_timer_interval = session->live_timer;
+ extended[i].creation_time.value = (uint64_t) session->creation_time;
+ extended[i].creation_time.is_set = 1;
+ i++;
+ session_put(session);
+ }
+}
+
+/*
+ * Command LTTNG_DATA_PENDING returning 0 if the data is NOT pending meaning
+ * ready for trace analysis (or any kind of reader) or else 1 for pending data.
+ */
+int cmd_data_pending(struct ltt_session *session)
+{
+ int ret;
+ struct ltt_kernel_session *ksess = session->kernel_session;
+ struct ltt_ust_session *usess = session->ust_session;
+
+ assert(session);
+
+ DBG("Data pending for session %s", session->name);
+
+ /* Session MUST be stopped to ask for data availability. */
+ if (session->active) {
+ ret = LTTNG_ERR_SESSION_STARTED;
+ goto error;
+ } else {
+ /*
+ * If stopped, just make sure we've started before else the above call
+ * will always send that there is data pending.
+ *
+ * The consumer assumes that when the data pending command is received,
+ * the trace has been started before or else no output data is written
+ * by the streams which is a condition for data pending. So, this is
+ * *VERY* important that we don't ask the consumer before a start
+ * trace.
+ */
+ if (!session->has_been_started) {
+ ret = 0;
+ goto error;
+ }
+ }
+
+ /* A rotation is still pending, we have to wait. */
+ if (session->rotation_state == LTTNG_ROTATION_STATE_ONGOING) {
+ DBG("Rotate still pending for session %s", session->name);
+ ret = 1;
+ goto error;
+ }
+
+ if (ksess && ksess->consumer) {
+ ret = consumer_is_data_pending(ksess->id, ksess->consumer);
+ if (ret == 1) {
+ /* Data is still being extracted for the kernel. */
+ goto error;
+ }
+ }
+
+ if (usess && usess->consumer) {
+ ret = consumer_is_data_pending(usess->id, usess->consumer);
+ if (ret == 1) {
+ /* Data is still being extracted for the kernel. */
+ goto error;
+ }
+ }
+
+ /* Data is ready to be read by a viewer */
+ ret = 0;