+ 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;
+ }
+
+ /*
+ * 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;
+ }
+
+ /*
+ * Record the timestamp of the first time the session is started for
+ * an eventual session rotation call.
+ */
+ if (!session->has_been_started) {
+ session->current_chunk_start_ts = time(NULL);
+ if (session->current_chunk_start_ts == (time_t) -1) {
+ PERROR("Failed to retrieve the \"%s\" session's start time",
+ session->name);
+ ret = LTTNG_ERR_FATAL;
+ goto error;
+ }
+ if (!session->snapshot_mode && session->output_traces) {
+ ret = session_mkdir(session);
+ if (ret) {
+ ERR("Failed to create the session directories");
+ ret = LTTNG_ERR_CREATE_DIR_FAIL;
+ goto error;
+ }
+ }
+ }
+
+ /* Kernel tracing */
+ if (ksession != NULL) {
+ DBG("Start kernel tracing session %s", session->name);
+ ret = start_kernel_session(ksession, kernel_tracer_fd);
+ if (ret != LTTNG_OK) {
+ goto error;
+ }
+ }
+
+ /* Flag session that trace should start automatically */
+ if (usess) {
+ /*
+ * Even though the start trace might fail, flag this session active so
+ * other application coming in are started by default.
+ */
+ usess->active = 1;
+
+ ret = ust_app_start_trace_all(usess);
+ if (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) {
+ ret = sessiond_rotate_timer_start(session,
+ session->rotate_timer_period);
+ if (ret < 0) {
+ ERR("Failed to enable rotate timer");
+ ret = LTTNG_ERR_UNK;
+ goto error;
+ }
+ }
+
+ ret = LTTNG_OK;
+
+error:
+ return ret;
+}
+
+static
+int rename_active_chunk(struct ltt_session *session)
+{
+ int ret;
+
+ session->current_archive_id++;
+
+ /*
+ * The currently active tracing path is now the folder we
+ * want to rename.
+ */
+ ret = lttng_strncpy(session->rotation_chunk.current_rotate_path,
+ session->rotation_chunk.active_tracing_path,
+ sizeof(session->rotation_chunk.current_rotate_path));
+ if (ret) {
+ ERR("Failed to copy active tracing path");
+ goto end;
+ }
+
+ ret = rename_complete_chunk(session, time(NULL));
+ if (ret < 0) {
+ ERR("Failed to rename current rotate path");
+ goto end;
+ }
+
+ /*
+ * We just renamed, the folder, we didn't do an actual rotation, so
+ * the active tracing path is now the renamed folder and we have to
+ * restore the rotate count.
+ */
+ ret = lttng_strncpy(session->rotation_chunk.active_tracing_path,
+ session->rotation_chunk.current_rotate_path,
+ sizeof(session->rotation_chunk.active_tracing_path));
+ if (ret) {
+ ERR("Failed to rename active session chunk tracing path");
+ goto end;
+ }
+end:
+ session->current_archive_id--;
+ 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_occured = 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;
+ }
+
+ if (session->rotate_relay_pending_timer_enabled) {
+ sessiond_timer_rotate_pending_stop(session);
+ }
+
+ if (session->rotate_timer_enabled) {
+ sessiond_rotate_timer_stop(session);
+ }
+
+ if (session->current_archive_id > 0 && !session->rotate_pending) {
+ ret = rename_active_chunk(session);
+ if (ret) {
+ /*
+ * This error should not prevent the user from stopping
+ * the session. However, it will be reported at the end.
+ */
+ error_occured = true;
+ }
+ }
+
+ /* 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) {
+ /*
+ * Even though the stop trace might fail, flag this session inactive so
+ * other application coming in are not started by default.
+ */
+ usess->active = 0;
+
+ 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_occured ? 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->consumer,
+ &uris[i], 0, session->name);
+ 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->ust_session->consumer,
+ &uris[i], LTTNG_DOMAIN_UST,
+ session->name);
+ 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->kernel_session->consumer,
+ &uris[i], LTTNG_DOMAIN_KERNEL,
+ session->name);
+ 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;
+}
+
+/*
+ * 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, unsigned int live_timer)
+{
+ int ret;
+ struct ltt_session *session;
+
+ assert(name);
+ assert(creds);
+
+ /*
+ * 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 = LTTNG_ERR_EXIST_SESS;
+ goto find_error;
+ }
+
+ /* Create tracing session in the registry */
+ ret = session_create(name, LTTNG_SOCK_GET_UID_CRED(creds),
+ LTTNG_SOCK_GET_GID_CRED(creds));
+ if (ret != LTTNG_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);
+
+ session->live_timer = live_timer;
+ /* Create default consumer output for the session not yet created. */
+ session->consumer = consumer_create_output(CONSUMER_DST_LOCAL);
+ if (session->consumer == NULL) {
+ ret = LTTNG_ERR_FATAL;
+ goto consumer_error;
+ }
+
+ if (uris) {
+ ret = cmd_set_consumer_uri(session, nb_uri, uris);
+ if (ret != LTTNG_OK) {
+ goto consumer_error;
+ }
+ session->output_traces = 1;
+ } else {
+ session->output_traces = 0;
+ DBG2("Session %s created with no output", session->name);
+ }
+
+ session->consumer->enabled = 1;
+
+ return LTTNG_OK;
+
+consumer_error:
+ session_destroy(session);
+session_error:
+find_error:
+ return ret;
+}
+
+/*
+ * Command LTTNG_CREATE_SESSION_SNAPSHOT processed by the client thread.
+ */
+int cmd_create_session_snapshot(char *name, struct lttng_uri *uris,
+ size_t nb_uri, lttng_sock_cred *creds)
+{
+ int ret;
+ struct ltt_session *session;
+ struct snapshot_output *new_output = NULL;
+
+ assert(name);
+ assert(creds);
+
+ /*
+ * Create session in no output mode with URIs set to NULL. The uris we've
+ * received are for a default snapshot output if one.
+ */
+ ret = cmd_create_session_uri(name, NULL, 0, creds, 0);
+ if (ret != LTTNG_OK) {
+ goto error;
+ }
+
+ /* Get the newly created session pointer back. This should NEVER fail. */
+ session = session_find_by_name(name);
+ assert(session);
+
+ /* Flag session for snapshot mode. */
+ session->snapshot_mode = 1;
+
+ /* Skip snapshot output creation if no URI is given. */
+ if (nb_uri == 0) {
+ goto end;
+ }
+
+ new_output = snapshot_output_alloc();
+ if (!new_output) {
+ ret = LTTNG_ERR_NOMEM;
+ goto error_snapshot_alloc;
+ }
+
+ ret = snapshot_output_init_with_uri(DEFAULT_SNAPSHOT_MAX_SIZE, NULL,
+ uris, nb_uri, session->consumer, new_output, &session->snapshot);
+ if (ret < 0) {
+ if (ret == -ENOMEM) {
+ ret = LTTNG_ERR_NOMEM;
+ } else {
+ ret = LTTNG_ERR_INVALID;
+ }
+ goto error_snapshot;
+ }
+
+ rcu_read_lock();
+ snapshot_add_output(&session->snapshot, new_output);
+ rcu_read_unlock();
+
+end:
+ return LTTNG_OK;
+
+error_snapshot:
+ snapshot_output_destroy(new_output);
+error_snapshot_alloc:
+ session_destroy(session);
+error:
+ return ret;
+}
+
+/*
+ * Command LTTNG_DESTROY_SESSION processed by the client thread.
+ *
+ * Called with session lock held.
+ */
+int cmd_destroy_session(struct ltt_session *session, int wpipe,
+ struct notification_thread_handle *notification_thread_handle)
+{
+ int ret;
+ struct ltt_ust_session *usess;
+ struct ltt_kernel_session *ksess;
+
+ /* Safety net */
+ assert(session);
+
+ usess = session->ust_session;
+ ksess = session->kernel_session;
+
+ DBG("Begin destroy session %s (id %" PRIu64 ")", session->name, session->id);
+
+ if (session->rotate_relay_pending_timer_enabled) {
+ sessiond_timer_rotate_pending_stop(session);
+ }
+
+ if (session->rotate_timer_enabled) {
+ sessiond_rotate_timer_stop(session);
+ }
+
+ if (session->rotate_size) {
+ unsubscribe_session_consumed_size_rotation(session, notification_thread_handle);
+ session->rotate_size = 0;
+ }
+
+ /*
+ * The rename of the current chunk is performed at stop, but if we rotated
+ * the session after the previous stop command, we need to rename the
+ * new (and empty) chunk that was started in between.
+ */
+ if (session->rotated_after_last_stop) {
+ rename_active_chunk(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");
+ }
+
+ 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);
+ }
+ ret = session_destroy(session);
+
+ 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();