+/*
+ * Command LTTNG_SET_CONSUMER_URI processed by the client thread.
+ */
+static int cmd_set_consumer_uri(int domain, struct ltt_session *session,
+ struct lttng_uri *uri)
+{
+ int ret;
+ struct ltt_kernel_session *ksess = session->kernel_session;
+ struct ltt_ust_session *usess = session->ust_session;
+ struct consumer_output *consumer;
+
+ /* Can't enable consumer after session started. */
+ if (session->enabled) {
+ ret = LTTCOMM_TRACE_ALREADY_STARTED;
+ goto error;
+ }
+
+ switch (domain) {
+ 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;
+ }
+ /* Reassign new pointer */
+ ksess->tmp_consumer = consumer;
+ }
+
+ switch (uri->dtype) {
+ case LTTNG_DST_IPV4:
+ case LTTNG_DST_IPV6:
+ DBG2("Setting network URI for kernel session %s", session->name);
+
+ /* Set URI into consumer output object */
+ ret = consumer_set_network_uri(consumer, uri);
+ if (ret < 0) {
+ ret = LTTCOMM_FATAL;
+ goto error;
+ }
+
+ /* On a new subdir, reappend the default trace dir. */
+ if (strlen(uri->subdir) != 0) {
+ strncat(consumer->subdir, DEFAULT_KERNEL_TRACE_DIR,
+ sizeof(consumer->subdir));
+ }
+
+ ret = send_socket_relayd_consumer(domain, session, uri, consumer,
+ ksess->consumer_fd);
+ if (ret != LTTCOMM_OK) {
+ goto error;
+ }
+ 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 kernel trace dir */
+ strncat(consumer->dst.trace_path, DEFAULT_KERNEL_TRACE_DIR,
+ sizeof(consumer->dst.trace_path));
+ break;
+ }
+
+ /* All good! */
+ 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;
+ }
+ /* Reassign new pointer */
+ usess->tmp_consumer = consumer;
+ }
+
+ switch (uri->dtype) {
+ case LTTNG_DST_IPV4:
+ case LTTNG_DST_IPV6:
+ {
+ DBG2("Setting network URI for UST session %s", session->name);
+
+ /* Set URI into consumer object */
+ ret = consumer_set_network_uri(consumer, uri);
+ if (ret < 0) {
+ ret = LTTCOMM_FATAL;
+ goto error;
+ }
+
+ /* On a new subdir, reappend the default trace dir. */
+ if (strlen(uri->subdir) != 0) {
+ strncat(consumer->subdir, DEFAULT_UST_TRACE_DIR,
+ sizeof(consumer->subdir));
+ }
+
+ if (ust_consumerd64_fd >= 0) {
+ ret = send_socket_relayd_consumer(domain, session, uri,
+ consumer, ust_consumerd64_fd);
+ if (ret != LTTCOMM_OK) {
+ goto error;
+ }
+ }
+
+ if (ust_consumerd32_fd >= 0) {
+ ret = send_socket_relayd_consumer(domain, session, uri,
+ consumer, ust_consumerd32_fd);
+ if (ret != LTTCOMM_OK) {
+ goto error;
+ }
+ }
+
+ 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 UST trace dir */
+ strncat(consumer->dst.trace_path, DEFAULT_UST_TRACE_DIR,
+ sizeof(consumer->dst.trace_path));
+ break;
+ }
+ break;
+ }
+
+ /* All good! */
+ ret = LTTCOMM_OK;
+
+error:
+ return ret;
+}
+
+/*
+ * Command LTTNG_DISABLE_CONSUMER processed by the client thread.
+ */
+static 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;
+
+ if (session->enabled) {
+ /* Can't disable consumer on an already started session */
+ ret = LTTCOMM_TRACE_ALREADY_STARTED;
+ goto error;
+ }
+
+ switch (domain) {
+ 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;
+ }
+
+ assert(consumer);
+ consumer->enabled = 0;
+
+ /* Success at this point */
+ ret = LTTCOMM_OK;
+
+error:
+ return ret;
+}
+
+/*
+ * Command LTTNG_ENABLE_CONSUMER processed by the client thread.
+ */
+static 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 *tmp_out;
+
+ /* Can't enable consumer after session started. */
+ if (session->enabled) {
+ ret = LTTCOMM_TRACE_ALREADY_STARTED;
+ goto error;
+ }
+
+ switch (domain) {
+ 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;
+ }
+
+ tmp_out = ksess->tmp_consumer;
+ if (tmp_out == NULL) {
+ /* No temp. consumer output exists. Using the current one. */
+ DBG3("No temporary consumer. Using default");
+ ret = LTTCOMM_OK;
+ goto error;
+ }
+
+ switch (tmp_out->type) {
+ case CONSUMER_DST_LOCAL:
+ DBG2("Consumer output is local. Creating directory(ies)");
+
+ /* Create directory(ies) */
+ ret = run_as_mkdir_recursive(tmp_out->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 (!tmp_out->dst.net.control_isset) {
+ ret = LTTCOMM_URI_CTRL_MISS;
+ goto error;
+ }
+
+ if (!tmp_out->dst.net.data_isset) {
+ ret = LTTCOMM_URI_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;
+ }
+
+ /* Append default kernel trace dir to subdir */
+ strncat(ksess->consumer->subdir, DEFAULT_KERNEL_TRACE_DIR,
+ sizeof(ksess->consumer->subdir));
+
+ break;
+ }
+
+ /*
+ * @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.
+ */
+ consumer_destroy_output(ksess->consumer);
+ ksess->consumer = tmp_out;
+ 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;
+ }
+
+ tmp_out = usess->tmp_consumer;
+ if (tmp_out == NULL) {
+ /* No temp. consumer output exists. Using the current one. */
+ DBG3("No temporary consumer. Using default");
+ ret = LTTCOMM_OK;
+ goto error;
+ }
+
+ switch (tmp_out->type) {
+ case CONSUMER_DST_LOCAL:
+ DBG2("Consumer output is local. Creating directory(ies)");
+
+ /* Create directory(ies) */
+ ret = run_as_mkdir_recursive(tmp_out->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 (!tmp_out->dst.net.control_isset) {
+ ret = LTTCOMM_URI_CTRL_MISS;
+ goto error;
+ }
+
+ if (!tmp_out->dst.net.data_isset) {
+ ret = LTTCOMM_URI_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 (tmp_out->net_seq_index == -1) {
+ ret = LTTCOMM_ENABLE_CONSUMER_FAIL;
+ DBG2("Network index is not set on the consumer");
+ goto error;
+ }
+
+ /* Append default kernel trace dir to subdir */
+ strncat(usess->consumer->subdir, DEFAULT_UST_TRACE_DIR,
+ sizeof(usess->consumer->subdir));
+
+ break;
+ }
+
+ /*
+ * @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.
+ */
+ consumer_destroy_output(usess->consumer);
+ usess->consumer = tmp_out;
+ usess->tmp_consumer = NULL;
+
+ break;
+ }
+
+ /* Success at this point */
+ ret = LTTCOMM_OK;
+
+error:
+ return ret;
+}
+