+#include "health.h"
+#include "ust-app.h"
+
+/*
+ * Receive a reply command status message from the consumer. Consumer socket
+ * lock MUST be acquired before calling this function.
+ *
+ * Return 0 on success, -1 on recv error or a negative lttng error code which
+ * was possibly returned by the consumer.
+ */
+int consumer_recv_status_reply(struct consumer_socket *sock)
+{
+ int ret;
+ struct lttcomm_consumer_status_msg reply;
+
+ assert(sock);
+
+ ret = lttcomm_recv_unix_sock(sock->fd, &reply, sizeof(reply));
+ if (ret <= 0) {
+ if (ret == 0) {
+ /* Orderly shutdown. Don't return 0 which means success. */
+ ret = -1;
+ }
+ /* The above call will print a PERROR on error. */
+ DBG("Fail to receive status reply on sock %d", sock->fd);
+ goto end;
+ }
+
+ if (reply.ret_code == LTTNG_OK) {
+ /* All good. */
+ ret = 0;
+ } else {
+ ret = -reply.ret_code;
+ DBG("Consumer ret code %d", ret);
+ }
+
+end:
+ return ret;
+}
+
+/*
+ * Once the ASK_CHANNEL command is sent to the consumer, the channel
+ * information are sent back. This call receives that data and populates key
+ * and stream_count.
+ *
+ * On success return 0 and both key and stream_count are set. On error, a
+ * negative value is sent back and both parameters are untouched.
+ */
+int consumer_recv_status_channel(struct consumer_socket *sock,
+ uint64_t *key, unsigned int *stream_count)
+{
+ int ret;
+ struct lttcomm_consumer_status_channel reply;
+
+ assert(sock);
+ assert(stream_count);
+ assert(key);
+
+ ret = lttcomm_recv_unix_sock(sock->fd, &reply, sizeof(reply));
+ if (ret <= 0) {
+ if (ret == 0) {
+ /* Orderly shutdown. Don't return 0 which means success. */
+ ret = -1;
+ }
+ /* The above call will print a PERROR on error. */
+ DBG("Fail to receive status reply on sock %d", sock->fd);
+ goto end;
+ }
+
+ /* An error is possible so don't touch the key and stream_count. */
+ if (reply.ret_code != LTTNG_OK) {
+ ret = -1;
+ goto end;
+ }
+
+ *key = reply.key;
+ *stream_count = reply.stream_count;
+
+end:
+ return ret;
+}
+
+/*
+ * Send destroy relayd command to consumer.
+ *
+ * On success return positive value. On error, negative value.
+ */
+int consumer_send_destroy_relayd(struct consumer_socket *sock,
+ struct consumer_output *consumer)
+{
+ int ret;
+ struct lttcomm_consumer_msg msg;
+
+ assert(consumer);
+ assert(sock);
+
+ DBG2("Sending destroy relayd command to consumer sock %d", sock->fd);
+
+ /* Bail out if consumer is disabled */
+ if (!consumer->enabled) {
+ ret = LTTNG_OK;
+ DBG3("Consumer is disabled");
+ goto error;
+ }
+
+ msg.cmd_type = LTTNG_CONSUMER_DESTROY_RELAYD;
+ msg.u.destroy_relayd.net_seq_idx = consumer->net_seq_index;
+
+ pthread_mutex_lock(sock->lock);
+ ret = lttcomm_send_unix_sock(sock->fd, &msg, sizeof(msg));
+ if (ret < 0) {
+ /* Indicate that the consumer is probably closing at this point. */
+ DBG("send consumer destroy relayd command");
+ goto error_send;
+ }
+
+ /* Don't check the return value. The caller will do it. */
+ ret = consumer_recv_status_reply(sock);
+
+ DBG2("Consumer send destroy relayd command done");
+
+error_send:
+ pthread_mutex_unlock(sock->lock);
+error:
+ return ret;
+}
+
+/*
+ * For each consumer socket in the consumer output object, send a destroy
+ * relayd command.
+ */
+void consumer_output_send_destroy_relayd(struct consumer_output *consumer)
+{
+ struct lttng_ht_iter iter;
+ struct consumer_socket *socket;
+
+ assert(consumer);
+
+ /* Destroy any relayd connection */
+ if (consumer && consumer->type == CONSUMER_DST_NET) {
+ rcu_read_lock();
+ cds_lfht_for_each_entry(consumer->socks->ht, &iter.iter, socket,
+ node.node) {
+ int ret;
+
+ /* Send destroy relayd command */
+ ret = consumer_send_destroy_relayd(socket, consumer);
+ if (ret < 0) {
+ DBG("Unable to send destroy relayd command to consumer");
+ /* Continue since we MUST delete everything at this point. */
+ }
+ }
+ rcu_read_unlock();
+ }
+}
+
+/*
+ * From a consumer_data structure, allocate and add a consumer socket to the
+ * consumer output.
+ *
+ * Return 0 on success, else negative value on error
+ */
+int consumer_create_socket(struct consumer_data *data,
+ struct consumer_output *output)
+{
+ int ret = 0;
+ struct consumer_socket *socket;
+
+ assert(data);
+
+ if (output == NULL || data->cmd_sock < 0) {
+ /*
+ * Not an error. Possible there is simply not spawned consumer or it's
+ * disabled for the tracing session asking the socket.
+ */
+ goto error;
+ }
+
+ rcu_read_lock();
+ socket = consumer_find_socket(data->cmd_sock, output);
+ rcu_read_unlock();
+ if (socket == NULL) {
+ socket = consumer_allocate_socket(data->cmd_sock);
+ if (socket == NULL) {
+ ret = -1;
+ goto error;
+ }
+
+ socket->registered = 0;
+ socket->lock = &data->lock;
+ rcu_read_lock();
+ consumer_add_socket(socket, output);
+ rcu_read_unlock();
+ }
+
+ DBG3("Consumer socket created (fd: %d) and added to output",
+ data->cmd_sock);
+
+error:
+ return ret;
+}
+
+/*
+ * Return the consumer socket from the given consumer output with the right
+ * bitness. On error, returns NULL.
+ *
+ * The caller MUST acquire a rcu read side lock and keep it until the socket
+ * object reference is not needed anymore.
+ */
+struct consumer_socket *consumer_find_socket_by_bitness(int bits,
+ struct consumer_output *consumer)
+{
+ int consumer_fd;
+ struct consumer_socket *socket = NULL;
+
+ switch (bits) {
+ case 64:
+ consumer_fd = uatomic_read(&ust_consumerd64_fd);
+ break;
+ case 32:
+ consumer_fd = uatomic_read(&ust_consumerd32_fd);
+ break;
+ default:
+ assert(0);
+ goto end;
+ }
+
+ socket = consumer_find_socket(consumer_fd, consumer);
+ if (!socket) {
+ ERR("Consumer socket fd %d not found in consumer obj %p",
+ consumer_fd, consumer);
+ }
+
+end:
+ return socket;
+}