Fix: relayd: missing metadata stream causes all traces to be skipped
[lttng-tools.git] / src / bin / lttng-relayd / live.c
index 57ff9197ac71a106e90e1500affd24612f6e8b0e..f00f07db1b8e63fc280c5a868379da2a47a68ade 100644 (file)
@@ -17,7 +17,6 @@
  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
  */
 
-#define _GNU_SOURCE
 #define _LGPL_SOURCE
 #include <getopt.h>
 #include <grp.h>
@@ -40,7 +39,6 @@
 #include <urcu/rculist.h>
 #include <unistd.h>
 #include <fcntl.h>
-#include <config.h>
 
 #include <lttng/lttng.h>
 #include <common/common.h>
@@ -199,7 +197,7 @@ end:
  */
 static
 ssize_t send_viewer_streams(struct lttcomm_sock *sock,
-               struct relay_session *session, unsigned int ignore_sent_flag)
+               uint64_t session_id, unsigned int ignore_sent_flag)
 {
        ssize_t ret;
        struct lttng_viewer_stream send_stream;
@@ -220,7 +218,7 @@ ssize_t send_viewer_streams(struct lttcomm_sock *sock,
 
                pthread_mutex_lock(&vstream->stream->lock);
                /* Ignore if not the same session. */
-               if (vstream->stream->trace->session->id != session->id ||
+               if (vstream->stream->trace->session->id != session_id ||
                                (!ignore_sent_flag && vstream->sent_flag)) {
                        pthread_mutex_unlock(&vstream->stream->lock);
                        viewer_stream_put(vstream);
@@ -232,10 +230,21 @@ ssize_t send_viewer_streams(struct lttcomm_sock *sock,
                send_stream.ctf_trace_id = htobe64(ctf_trace->id);
                send_stream.metadata_flag = htobe32(
                                vstream->stream->is_metadata);
-               strncpy(send_stream.path_name, vstream->path_name,
-                               sizeof(send_stream.path_name));
-               strncpy(send_stream.channel_name, vstream->channel_name,
-                               sizeof(send_stream.channel_name));
+               if (lttng_strncpy(send_stream.path_name, vstream->path_name,
+                               sizeof(send_stream.path_name))) {
+                       pthread_mutex_unlock(&vstream->stream->lock);
+                       viewer_stream_put(vstream);
+                       ret = -1;       /* Error. */
+                       goto end_unlock;
+               }
+               if (lttng_strncpy(send_stream.channel_name,
+                               vstream->channel_name,
+                               sizeof(send_stream.channel_name))) {
+                       pthread_mutex_unlock(&vstream->stream->lock);
+                       viewer_stream_put(vstream);
+                       ret = -1;       /* Error. */
+                       goto end_unlock;
+               }
 
                DBG("Sending stream %" PRIu64 " to viewer",
                                vstream->stream->stream_handle);
@@ -262,24 +271,32 @@ end_unlock:
  * viewer stream of the session, the number of unsent stream and the number of
  * stream created. Those counters can be NULL and thus will be ignored.
  *
+ * session must be locked to ensure that we see either none or all initial
+ * streams for a session, but no intermediate state..
+ *
  * Return 0 on success or else a negative value.
  */
-static
-int make_viewer_streams(struct relay_session *session,
-               enum lttng_viewer_seek seek_t, uint32_t *nb_total, uint32_t *nb_unsent,
-               uint32_t *nb_created, bool *closed)
+static int make_viewer_streams(struct relay_session *session,
+               struct lttng_trace_chunk *viewer_trace_chunk,
+               enum lttng_viewer_seek seek_t,
+               uint32_t *nb_total,
+               uint32_t *nb_unsent,
+               uint32_t *nb_created,
+               bool *closed)
 {
        int ret;
        struct lttng_ht_iter iter;
        struct ctf_trace *ctf_trace;
 
        assert(session);
+       ASSERT_LOCKED(session->lock);
 
-       /*
-        * Hold the session lock to ensure that we see either none or
-        * all initial streams for a session, but no intermediate state.
-        */
-       pthread_mutex_lock(&session->lock);
+       if (!viewer_trace_chunk) {
+               ERR("Internal error: viewer session associated with session \"%s\" has a NULL trace chunk",
+                               session->session_name);
+               ret = -1;
+               goto error;
+       }
 
        if (session->connection_closed) {
                *closed = true;
@@ -292,6 +309,7 @@ int make_viewer_streams(struct relay_session *session,
        rcu_read_lock();
        cds_lfht_for_each_entry(session->ctf_traces_ht->ht, &iter.iter, ctf_trace,
                        node.node) {
+               bool trace_has_metadata_stream = false;
                struct relay_stream *stream;
 
                health_code_update();
@@ -300,6 +318,30 @@ int make_viewer_streams(struct relay_session *session,
                        continue;
                }
 
+               /*
+                * Iterate over all the streams of the trace to see if we have a
+                * metadata stream.
+                */
+               cds_list_for_each_entry_rcu(
+                               stream, &ctf_trace->stream_list, stream_node)
+               {
+                       if (stream->is_metadata) {
+                               trace_has_metadata_stream = true;
+                               break;
+                       }
+               }
+
+               /*
+                * If there is no metadata stream in this trace at the moment
+                * and we never sent one to the viewer, skip the trace. We
+                * accept that the viewer will not see this trace at all.
+                */
+               if (!trace_has_metadata_stream &&
+                               !ctf_trace->metadata_stream_sent_to_viewer) {
+                       ctf_trace_put(ctf_trace);
+                       continue;
+               }
+
                cds_list_for_each_entry_rcu(stream, &ctf_trace->stream_list, stream_node) {
                        struct relay_viewer_stream *vstream;
 
@@ -307,15 +349,24 @@ int make_viewer_streams(struct relay_session *session,
                                continue;
                        }
                        /*
-                        * stream published is protected by the session
-                        * lock.
+                        * stream published is protected by the session lock.
                         */
                        if (!stream->published) {
                                goto next;
                        }
                        vstream = viewer_stream_get_by_id(stream->stream_handle);
                        if (!vstream) {
-                               vstream = viewer_stream_create(stream, seek_t);
+                               /*
+                                * Save that we sent the metadata stream to the
+                                * viewer. So that we know what trace the viewer
+                                * is aware of.
+                                */
+                               if (stream->is_metadata) {
+                                       ctf_trace->metadata_stream_sent_to_viewer =
+                                                       true;
+                               }
+                               vstream = viewer_stream_create(stream,
+                                               viewer_trace_chunk, seek_t);
                                if (!vstream) {
                                        ret = -1;
                                        ctf_trace_put(ctf_trace);
@@ -331,7 +382,10 @@ int make_viewer_streams(struct relay_session *session,
                                 * Ensure a self-reference is preserved even
                                 * after we have put our local reference.
                                 */
-                               viewer_stream_get(vstream);
+                               if (!viewer_stream_get(vstream)) {
+                                       ERR("Unable to get self-reference on viewer stream, logic error.");
+                                       abort();
+                               }
                        } else {
                                if (!vstream->sent_flag && nb_unsent) {
                                        /* Update number of unsent stream counter. */
@@ -347,7 +401,7 @@ int make_viewer_streams(struct relay_session *session,
                                        }
                                } else {
                                        if (!stream->closed ||
-                                               !(((int64_t) (stream->prev_seq - stream->last_net_seq_num)) >= 0)) {
+                                               !(((int64_t) (stream->prev_data_seq - stream->last_net_seq_num)) >= 0)) {
 
                                                (*nb_total)++;
                                        }
@@ -365,7 +419,7 @@ int make_viewer_streams(struct relay_session *session,
 
 error_unlock:
        rcu_read_unlock();
-       pthread_mutex_unlock(&session->lock);
+error:
        return ret;
 }
 
@@ -441,10 +495,11 @@ struct lttcomm_sock *init_socket(struct lttng_uri *uri)
        if (ret < 0) {
                goto error;
        }
-       DBG("Listening on sock %d for live", sock->fd);
+       DBG("Listening on sock %d for lttng-live", sock->fd);
 
        ret = sock->ops->bind(sock);
        if (ret < 0) {
+               PERROR("Failed to bind lttng-live socket");
                goto error;
        }
 
@@ -531,11 +586,6 @@ restart:
                        revents = LTTNG_POLL_GETEV(&events, i);
                        pollfd = LTTNG_POLL_GETFD(&events, i);
 
-                       if (!revents) {
-                               /* No activity for this FD (poll implementation). */
-                               continue;
-                       }
-
                        /* Thread quit pipe has been closed. Killing thread. */
                        ret = check_thread_quit_pipe(pollfd, revents);
                        if (ret) {
@@ -543,10 +593,7 @@ restart:
                                goto exit;
                        }
 
-                       if (revents & (LPOLLERR | LPOLLHUP | LPOLLRDHUP)) {
-                               ERR("socket poll error");
-                               goto error;
-                       } else if (revents & LPOLLIN) {
+                       if (revents & LPOLLIN) {
                                /*
                                 * A new connection is requested, therefore a
                                 * viewer connection is allocated in this
@@ -589,6 +636,12 @@ restart:
                                 * exchange in cds_wfcq_enqueue.
                                 */
                                futex_nto1_wake(&viewer_conn_queue.futex);
+                       } else if (revents & (LPOLLERR | LPOLLHUP | LPOLLRDHUP)) {
+                               ERR("socket poll error");
+                               goto error;
+                       } else {
+                               ERR("Unexpected poll events %u for sock %d", revents, pollfd);
+                               goto error;
                        }
                }
        }
@@ -640,12 +693,16 @@ void *thread_dispatcher(void *data)
 
        health_code_update();
 
-       while (!CMM_LOAD_SHARED(live_dispatch_thread_exit)) {
+       for (;;) {
                health_code_update();
 
                /* Atomically prepare the queue futex */
                futex_nto1_prepare(&viewer_conn_queue.futex);
 
+               if (CMM_LOAD_SHARED(live_dispatch_thread_exit)) {
+                       break;
+               }
+
                do {
                        health_code_update();
 
@@ -794,7 +851,7 @@ end:
 static
 int viewer_list_sessions(struct relay_connection *conn)
 {
-       int ret;
+       int ret = 0;
        struct lttng_viewer_list_sessions session_list;
        struct lttng_ht_iter iter;
        struct relay_session *session;
@@ -816,6 +873,20 @@ int viewer_list_sessions(struct relay_connection *conn)
 
                health_code_update();
 
+               pthread_mutex_lock(&session->lock);
+               if (session->connection_closed) {
+                       /* Skip closed session */
+                       goto next_session;
+               }
+               if (!session->current_trace_chunk) {
+                       /*
+                        * Skip un-attachable session. It is either
+                        * being destroyed or has not had a trace
+                        * chunk created against it yet.
+                        */
+                       goto next_session;
+               }
+
                if (count >= buf_count) {
                        struct lttng_viewer_session *newbuf;
                        uint32_t new_buf_count = buf_count << 1;
@@ -824,17 +895,23 @@ int viewer_list_sessions(struct relay_connection *conn)
                                new_buf_count * sizeof(*send_session_buf));
                        if (!newbuf) {
                                ret = -1;
-                               rcu_read_unlock();
-                               goto end_free;
+                               goto break_loop;
                        }
                        send_session_buf = newbuf;
                        buf_count = new_buf_count;
                }
                send_session = &send_session_buf[count];
-               strncpy(send_session->session_name, session->session_name,
-                               sizeof(send_session->session_name));
-               strncpy(send_session->hostname, session->hostname,
-                               sizeof(send_session->hostname));
+               if (lttng_strncpy(send_session->session_name,
+                               session->session_name,
+                               sizeof(send_session->session_name))) {
+                       ret = -1;
+                       goto break_loop;
+               }
+               if (lttng_strncpy(send_session->hostname, session->hostname,
+                               sizeof(send_session->hostname))) {
+                       ret = -1;
+                       goto break_loop;
+               }
                send_session->id = htobe64(session->id);
                send_session->live_timer = htobe32(session->live_timer);
                if (session->viewer_attached) {
@@ -844,8 +921,17 @@ int viewer_list_sessions(struct relay_connection *conn)
                }
                send_session->streams = htobe32(session->stream_count);
                count++;
+       next_session:
+               pthread_mutex_unlock(&session->lock);
+               continue;
+       break_loop:
+               pthread_mutex_unlock(&session->lock);
+               break;
        }
        rcu_read_unlock();
+       if (ret < 0) {
+               goto end_free;
+       }
 
        session_list.sessions_count = htobe32(count);
 
@@ -881,7 +967,7 @@ int viewer_get_new_streams(struct relay_connection *conn)
        uint32_t nb_created = 0, nb_unsent = 0, nb_streams = 0, nb_total = 0;
        struct lttng_viewer_new_streams_request request;
        struct lttng_viewer_new_streams_response response;
-       struct relay_session *session;
+       struct relay_session *session = NULL;
        uint64_t session_id;
        bool closed = false;
 
@@ -910,19 +996,21 @@ int viewer_get_new_streams(struct relay_connection *conn)
        }
 
        if (!viewer_session_is_attached(conn->viewer_session, session)) {
-               send_streams = 0;
                response.status = htobe32(LTTNG_VIEWER_NEW_STREAMS_ERR);
                goto send_reply;
        }
 
-       send_streams = 1;
-       response.status = htobe32(LTTNG_VIEWER_NEW_STREAMS_OK);
-
-       ret = make_viewer_streams(session, LTTNG_VIEWER_SEEK_LAST, &nb_total, &nb_unsent,
+       pthread_mutex_lock(&session->lock);
+       ret = make_viewer_streams(session,
+                       conn->viewer_session->current_trace_chunk,
+                       LTTNG_VIEWER_SEEK_LAST, &nb_total, &nb_unsent,
                        &nb_created, &closed);
        if (ret < 0) {
-               goto end_put_session;
+               goto error_unlock_session;
        }
+       send_streams = 1;
+       response.status = htobe32(LTTNG_VIEWER_NEW_STREAMS_OK);
+
        /* Only send back the newly created streams with the unsent ones. */
        nb_streams = nb_created + nb_unsent;
        response.streams_count = htobe32(nb_streams);
@@ -935,8 +1023,10 @@ int viewer_get_new_streams(struct relay_connection *conn)
                send_streams = 0;
                response.streams_count = 0;
                response.status = htobe32(LTTNG_VIEWER_NEW_STREAMS_HUP);
-               goto send_reply;
+               goto send_reply_unlock;
        }
+send_reply_unlock:
+       pthread_mutex_unlock(&session->lock);
 
 send_reply:
        health_code_update();
@@ -960,7 +1050,7 @@ send_reply:
         * streams that were not sent from that point will be sent to
         * the viewer.
         */
-       ret = send_viewer_streams(conn->sock, session, 0);
+       ret = send_viewer_streams(conn->sock, session_id, 0);
        if (ret < 0) {
                goto end_put_session;
        }
@@ -971,6 +1061,10 @@ end_put_session:
        }
 error:
        return ret;
+error_unlock_session:
+       pthread_mutex_unlock(&session->lock);
+       session_put(session);
+       return ret;
 }
 
 /*
@@ -986,7 +1080,9 @@ int viewer_attach_session(struct relay_connection *conn)
        struct lttng_viewer_attach_session_request request;
        struct lttng_viewer_attach_session_response response;
        struct relay_session *session = NULL;
+       enum lttng_viewer_attach_return_code viewer_attach_status;
        bool closed = false;
+       uint64_t session_id;
 
        assert(conn);
 
@@ -998,6 +1094,7 @@ int viewer_attach_session(struct relay_connection *conn)
                goto error;
        }
 
+       session_id = be64toh(request.session_id);
        health_code_update();
 
        memset(&response, 0, sizeof(response));
@@ -1008,16 +1105,24 @@ int viewer_attach_session(struct relay_connection *conn)
                goto send_reply;
        }
 
-       session = session_get_by_id(be64toh(request.session_id));
+       session = session_get_by_id(session_id);
        if (!session) {
-               DBG("Relay session %" PRIu64 " not found",
-                               be64toh(request.session_id));
+               DBG("Relay session %" PRIu64 " not found", session_id);
                response.status = htobe32(LTTNG_VIEWER_ATTACH_UNK);
                goto send_reply;
        }
-       DBG("Attach session ID %" PRIu64 " received",
-               be64toh(request.session_id));
+       DBG("Attach session ID %" PRIu64 " received", session_id);
 
+       pthread_mutex_lock(&session->lock);
+       if (!session->current_trace_chunk) {
+               /*
+                * Session is either being destroyed or it never had a trace
+                * chunk created against it.
+                */
+               DBG("Session requested by live client has no current trace chunk, returning unknown session");
+               response.status = htobe32(LTTNG_VIEWER_ATTACH_UNK);
+               goto send_reply;
+       }
        if (session->live_timer == 0) {
                DBG("Not live session");
                response.status = htobe32(LTTNG_VIEWER_ATTACH_NOT_LIVE);
@@ -1025,10 +1130,10 @@ int viewer_attach_session(struct relay_connection *conn)
        }
 
        send_streams = 1;
-       ret = viewer_session_attach(conn->viewer_session, session);
-       if (ret) {
-               DBG("Already a viewer attached");
-               response.status = htobe32(LTTNG_VIEWER_ATTACH_ALREADY);
+       viewer_attach_status = viewer_session_attach(conn->viewer_session,
+                       session);
+       if (viewer_attach_status != LTTNG_VIEWER_ATTACH_OK) {
+               response.status = htobe32(viewer_attach_status);
                goto send_reply;
        }
 
@@ -1045,13 +1150,17 @@ int viewer_attach_session(struct relay_connection *conn)
                goto send_reply;
        }
 
-       ret = make_viewer_streams(session, seek_type, &nb_streams, NULL,
-                       NULL, &closed);
+       ret = make_viewer_streams(session,
+                       conn->viewer_session->current_trace_chunk, seek_type,
+                       &nb_streams, NULL, NULL, &closed);
        if (ret < 0) {
                goto end_put_session;
        }
-       response.streams_count = htobe32(nb_streams);
+       pthread_mutex_unlock(&session->lock);
+       session_put(session);
+       session = NULL;
 
+       response.streams_count = htobe32(nb_streams);
        /*
         * If the session is closed when the viewer is attaching, it
         * means some of the streams may have been concurrently removed,
@@ -1061,7 +1170,7 @@ int viewer_attach_session(struct relay_connection *conn)
        if (closed) {
                send_streams = 0;
                response.streams_count = 0;
-               response.status = htobe32(LTTNG_VIEWER_NEW_STREAMS_HUP);
+               response.status = htobe32(LTTNG_VIEWER_ATTACH_UNK);
                goto send_reply;
        }
 
@@ -1083,13 +1192,14 @@ send_reply:
        }
 
        /* Send stream and ignore the sent flag. */
-       ret = send_viewer_streams(conn->sock, session, 1);
+       ret = send_viewer_streams(conn->sock, session_id, 1);
        if (ret < 0) {
                goto end_put_session;
        }
 
 end_put_session:
        if (session) {
+               pthread_mutex_unlock(&session->lock);
                session_put(session);
        }
 error:
@@ -1099,8 +1209,8 @@ error:
 /*
  * Open the index file if needed for the given vstream.
  *
- * If an index file is successfully opened, the vstream index_fd set with
- * it.
+ * If an index file is successfully opened, the vstream will set it as its
+ * current index file.
  *
  * Return 0 on success, a negative value on error (-ENOENT if not ready yet).
  *
@@ -1110,32 +1220,28 @@ static int try_open_index(struct relay_viewer_stream *vstream,
                struct relay_stream *rstream)
 {
        int ret = 0;
+       const uint32_t connection_major = rstream->trace->session->major;
+       const uint32_t connection_minor = rstream->trace->session->minor;
 
-       if (vstream->index_fd) {
+       if (vstream->index_file) {
                goto end;
        }
 
        /*
         * First time, we open the index file and at least one index is ready.
         */
-       if (rstream->total_index_received == 0) {
+       if (rstream->index_received_seqcount == 0) {
                ret = -ENOENT;
                goto end;
        }
-       ret = index_open(vstream->path_name, vstream->channel_name,
-                       vstream->stream->tracefile_count,
-                       vstream->current_tracefile_id);
-       if (ret >= 0) {
-               vstream->index_fd = stream_fd_create(ret);
-               if (!vstream->index_fd) {
-                       if (close(ret)) {
-                               PERROR("close");
-                       }
-                       ret = -1;
-               } else {
-                       ret = 0;
-               }
-               goto end;
+       vstream->index_file = lttng_index_file_create_from_trace_chunk_read_only(
+                       vstream->stream_file.trace_chunk, rstream->path_name,
+                       rstream->channel_name, rstream->tracefile_size,
+                       vstream->current_tracefile_id,
+                       lttng_to_index_major(connection_major, connection_minor),
+                       lttng_to_index_minor(connection_major, connection_minor));
+       if (!vstream->index_file) {
+               ret = -1;
        }
 
 end:
@@ -1159,15 +1265,18 @@ static int check_index_status(struct relay_viewer_stream *vstream,
 {
        int ret;
 
-       if (trace->session->connection_closed
-                       && rstream->total_index_received
-                               == vstream->last_sent_index) {
-               /* Last index sent and session connection is closed. */
+       if ((trace->session->connection_closed || rstream->closed)
+                       && rstream->index_received_seqcount
+                               == vstream->index_sent_seqcount) {
+               /*
+                * Last index sent and session connection or relay
+                * stream are closed.
+                */
                index->status = htobe32(LTTNG_VIEWER_INDEX_HUP);
                goto hup;
        } else if (rstream->beacon_ts_end != -1ULL &&
-                       rstream->total_index_received
-                               == vstream->last_sent_index) {
+                       rstream->index_received_seqcount
+                               == vstream->index_sent_seqcount) {
                /*
                 * We've received a synchronization beacon and the last index
                 * available has been sent, the index for now is inactive.
@@ -1181,21 +1290,24 @@ static int check_index_status(struct relay_viewer_stream *vstream,
                index->timestamp_end = htobe64(rstream->beacon_ts_end);
                index->stream_id = htobe64(rstream->ctf_stream_id);
                goto index_ready;
-       } else if (rstream->total_index_received <= vstream->last_sent_index) {
+       } else if (rstream->index_received_seqcount
+                       == vstream->index_sent_seqcount) {
                /*
-                * This actually checks the case where recv == last_sent.
-                * In this case, we have not received a beacon. Therefore, we
-                * can only ask the client to retry later.
+                * This checks whether received == sent seqcount. In
+                * this case, we have not received a beacon. Therefore,
+                * we can only ask the client to retry later.
                 */
                index->status = htobe32(LTTNG_VIEWER_INDEX_RETRY);
                goto index_ready;
-       } else if (!viewer_stream_is_tracefile_seq_readable(vstream,
-                       vstream->current_tracefile_seq)) {
+       } else if (!tracefile_array_seq_in_file(rstream->tfa,
+                       vstream->current_tracefile_id,
+                       vstream->index_sent_seqcount)) {
                /*
-                * The producer has overwritten our current file. We
-                * need to rotate.
+                * The next index we want to send cannot be read either
+                * because we need to perform a rotation, or due to
+                * the producer having overwritten its trace file.
                 */
-               DBG("Viewer stream %" PRIu64 " rotation due to overwrite",
+               DBG("Viewer stream %" PRIu64 " rotation",
                                vstream->stream->stream_handle);
                ret = viewer_stream_rotate(vstream);
                if (ret < 0) {
@@ -1205,50 +1317,34 @@ static int check_index_status(struct relay_viewer_stream *vstream,
                        index->status = htobe32(LTTNG_VIEWER_INDEX_HUP);
                        goto hup;
                }
-               assert(viewer_stream_is_tracefile_seq_readable(vstream,
-                       vstream->current_tracefile_seq));
-               /* ret == 0 means successful so we continue. */
-               ret = 0;
-       } else {
-               ssize_t read_ret;
-               char tmp[1];
-
                /*
-                * Use EOF on current index file to find out when we
-                * need to rotate.
+                * If we have been pushed due to overwrite, it
+                * necessarily means there is data that can be read in
+                * the stream. If we rotated because we reached the end
+                * of a tracefile, it means the following tracefile
+                * needs to contain at least one index, else we would
+                * have already returned LTTNG_VIEWER_INDEX_RETRY to the
+                * viewer. The updated index_sent_seqcount needs to
+                * point to a readable index entry now.
+                *
+                * In the case where we "rotate" on a single file, we
+                * can end up in a case where the requested index is
+                * still unavailable.
                 */
-               read_ret = lttng_read(vstream->index_fd->fd, tmp, 1);
-               if (read_ret == 1) {
-                       off_t seek_ret;
-
-                       /* There is still data to read. Rewind position. */
-                       seek_ret = lseek(vstream->index_fd->fd, -1, SEEK_CUR);
-                       if (seek_ret < 0) {
-                               ret = -1;
-                               goto end;
-                       }
-                       ret = 0;
-               } else if (read_ret == 0) {
-                       /* EOF. We need to rotate. */
-                       DBG("Viewer stream %" PRIu64 " rotation due to EOF",
-                                       vstream->stream->stream_handle);
-                       ret = viewer_stream_rotate(vstream);
-                       if (ret < 0) {
-                               goto end;
-                       } else if (ret == 1) {
-                               /* EOF across entire stream. */
-                               index->status = htobe32(LTTNG_VIEWER_INDEX_HUP);
-                               goto hup;
-                       }
-                       assert(viewer_stream_is_tracefile_seq_readable(vstream,
-                               vstream->current_tracefile_seq));
-                       /* ret == 0 means successful so we continue. */
-                       ret = 0;
-               } else {
-                       /* Error reading index. */
-                       ret = -1;
+               if (rstream->tracefile_count == 1 &&
+                               !tracefile_array_seq_in_file(
+                                       rstream->tfa,
+                                       vstream->current_tracefile_id,
+                                       vstream->index_sent_seqcount)) {
+                       index->status = htobe32(LTTNG_VIEWER_INDEX_RETRY);
+                       goto index_ready;
                }
+               assert(tracefile_array_seq_in_file(rstream->tfa,
+                               vstream->current_tracefile_id,
+                               vstream->index_sent_seqcount));
        }
+       /* ret == 0 means successful so we continue. */
+       ret = 0;
 end:
        return ret;
 
@@ -1267,7 +1363,6 @@ static
 int viewer_get_next_index(struct relay_connection *conn)
 {
        int ret;
-       ssize_t read_ret;
        struct lttng_viewer_get_next_index request_index;
        struct lttng_viewer_index viewer_index;
        struct ctf_packet_index packet_index;
@@ -1291,6 +1386,8 @@ int viewer_get_next_index(struct relay_connection *conn)
 
        vstream = viewer_stream_get_by_id(be64toh(request_index.stream_id));
        if (!vstream) {
+               DBG("Client requested index of unknown stream id %" PRIu64,
+                               (uint64_t) be64toh(request_index.stream_id));
                viewer_index.status = htobe32(LTTNG_VIEWER_INDEX_ERR);
                goto send_reply;
        }
@@ -1350,31 +1447,30 @@ int viewer_get_next_index(struct relay_connection *conn)
         * overwrite caused by tracefile rotation (in association with
         * unlink performed before overwrite).
         */
-       if (!vstream->stream_fd) {
-               char fullpath[PATH_MAX];
-
-               if (vstream->stream->tracefile_count > 0) {
-                       ret = snprintf(fullpath, PATH_MAX, "%s/%s_%" PRIu64,
-                                       vstream->path_name,
-                                       vstream->channel_name,
-                                       vstream->current_tracefile_id);
-               } else {
-                       ret = snprintf(fullpath, PATH_MAX, "%s/%s",
-                                       vstream->path_name,
-                                       vstream->channel_name);
-               }
+       if (!vstream->stream_file.fd) {
+               int fd;
+               char file_path[LTTNG_PATH_MAX];
+               enum lttng_trace_chunk_status status;
+
+               ret = utils_stream_file_path(rstream->path_name,
+                               rstream->channel_name, rstream->tracefile_size,
+                               vstream->current_tracefile_id, NULL, file_path,
+                               sizeof(file_path));
                if (ret < 0) {
                        goto error_put;
                }
-               ret = open(fullpath, O_RDONLY);
-               if (ret < 0) {
-                       PERROR("Relay opening trace file");
+
+               status = lttng_trace_chunk_open_file(
+                               vstream->stream_file.trace_chunk,
+                               file_path, O_RDONLY, 0, &fd);
+               if (status != LTTNG_TRACE_CHUNK_STATUS_OK) {
+                       PERROR("Failed to open trace file for viewer stream");
                        goto error_put;
                }
-               vstream->stream_fd = stream_fd_create(ret);
-               if (!vstream->stream_fd) {
-                       if (close(ret)) {
-                               PERROR("close");
+               vstream->stream_file.fd = stream_fd_create(fd);
+               if (!vstream->stream_file.fd) {
+                       if (close(fd)) {
+                               PERROR("Failed to close viewer stream file");
                        }
                        goto error_put;
                }
@@ -1388,16 +1484,15 @@ int viewer_get_next_index(struct relay_connection *conn)
                viewer_index.flags |= LTTNG_VIEWER_FLAG_NEW_STREAM;
        }
 
-       read_ret = lttng_read(vstream->index_fd->fd, &packet_index,
-                       sizeof(packet_index));
-       if (read_ret < sizeof(packet_index)) {
-               ERR("Relay reading index file %d returned %zd",
-                       vstream->index_fd->fd, read_ret);
+       ret = lttng_index_file_read(vstream->index_file, &packet_index);
+       if (ret) {
+               ERR("Relay error reading index file %d",
+                               vstream->index_file->fd);
                viewer_index.status = htobe32(LTTNG_VIEWER_INDEX_ERR);
                goto send_reply;
        } else {
                viewer_index.status = htobe32(LTTNG_VIEWER_INDEX_OK);
-               vstream->last_sent_index++;
+               vstream->index_sent_seqcount++;
        }
 
        /*
@@ -1405,7 +1500,7 @@ int viewer_get_next_index(struct relay_connection *conn)
         */
        DBG("Sending viewer index for stream %" PRIu64 " offset %" PRIu64,
                rstream->stream_handle,
-               be64toh(packet_index.offset));
+               (uint64_t) be64toh(packet_index.offset));
        viewer_index.offset = packet_index.offset;
        viewer_index.packet_size = packet_index.packet_size;
        viewer_index.content_size = packet_index.content_size;
@@ -1442,9 +1537,11 @@ send_reply:
        }
        health_code_update();
 
-       DBG("Index %" PRIu64 " for stream %" PRIu64 " sent",
-                       vstream->last_sent_index,
-                       vstream->stream->stream_handle);
+       if (vstream) {
+               DBG("Index %" PRIu64 " for stream %" PRIu64 " sent",
+                               vstream->index_sent_seqcount,
+                               vstream->stream->stream_handle);
+       }
 end:
        if (metadata_viewer_stream) {
                viewer_stream_put(metadata_viewer_stream);
@@ -1471,15 +1568,15 @@ error_put:
 static
 int viewer_get_packet(struct relay_connection *conn)
 {
-       int ret, send_data = 0;
-       char *data = NULL;
-       uint32_t len = 0;
-       ssize_t read_len;
+       int ret;
+       off_t lseek_ret;
+       char *reply = NULL;
        struct lttng_viewer_get_packet get_packet_info;
-       struct lttng_viewer_trace_packet reply;
+       struct lttng_viewer_trace_packet reply_header;
        struct relay_viewer_stream *vstream = NULL;
-       struct ctf_trace *ctf_trace;
-       struct relay_viewer_stream *metadata_viewer_stream = NULL;
+       uint32_t reply_size = sizeof(reply_header);
+       uint32_t packet_data_len = 0;
+       ssize_t read_len;
 
        DBG2("Relay get data packet");
 
@@ -1493,107 +1590,78 @@ int viewer_get_packet(struct relay_connection *conn)
        health_code_update();
 
        /* From this point on, the error label can be reached. */
-       memset(&reply, 0, sizeof(reply));
+       memset(&reply_header, 0, sizeof(reply_header));
 
        vstream = viewer_stream_get_by_id(be64toh(get_packet_info.stream_id));
        if (!vstream) {
-               reply.status = htobe32(LTTNG_VIEWER_GET_PACKET_ERR);
+               DBG("Client requested packet of unknown stream id %" PRIu64,
+                               (uint64_t) be64toh(get_packet_info.stream_id));
+               reply_header.status = htobe32(LTTNG_VIEWER_GET_PACKET_ERR);
                goto send_reply_nolock;
+       } else {
+               packet_data_len = be32toh(get_packet_info.len);
+               reply_size += packet_data_len;
        }
 
-       ctf_trace = vstream->stream->trace;
-
-       /* metadata_viewer_stream may be NULL. */
-       metadata_viewer_stream =
-                       ctf_trace_get_viewer_metadata_stream(ctf_trace);
-
-       if (metadata_viewer_stream) {
-               bool get_packet_err = false;
-
-               pthread_mutex_lock(&metadata_viewer_stream->stream->lock);
-               DBG("get packet metadata check: recv %" PRIu64 " sent %" PRIu64,
-                       metadata_viewer_stream->stream->metadata_received,
-                       metadata_viewer_stream->metadata_sent);
-               if (!metadata_viewer_stream->stream->metadata_received ||
-                               metadata_viewer_stream->stream->metadata_received >
-                                       metadata_viewer_stream->metadata_sent) {
-                       /*
-                        * We prevent the client from reading a data stream as
-                        * long as there is metadata left to consume. This
-                        * ensures that the client won't receive data of which
-                        * it can't make sense.
-                        */
-                       get_packet_err = true;
-               }
-               pthread_mutex_unlock(&metadata_viewer_stream->stream->lock);
-               viewer_stream_put(metadata_viewer_stream);
-               if (get_packet_err) {
-                       reply.status = htobe32(LTTNG_VIEWER_GET_PACKET_ERR);
-                       reply.flags |= LTTNG_VIEWER_FLAG_NEW_METADATA;
-                       goto send_reply_nolock;
-               }
-       }
-
-       pthread_mutex_lock(&vstream->stream->lock);
-
-       len = be32toh(get_packet_info.len);
-       data = zmalloc(len);
-       if (!data) {
-               PERROR("relay data zmalloc");
+       reply = zmalloc(reply_size);
+       if (!reply) {
+               PERROR("packet reply zmalloc");
+               reply_size = sizeof(reply_header);
                goto error;
        }
 
-       ret = lseek(vstream->stream_fd->fd, be64toh(get_packet_info.offset),
-                       SEEK_SET);
-       if (ret < 0) {
-               PERROR("lseek fd %d to offset %" PRIu64, vstream->stream_fd->fd,
-                       be64toh(get_packet_info.offset));
+       pthread_mutex_lock(&vstream->stream->lock);
+       lseek_ret = lseek(vstream->stream_file.fd->fd,
+                       be64toh(get_packet_info.offset), SEEK_SET);
+       if (lseek_ret < 0) {
+               PERROR("lseek fd %d to offset %" PRIu64,
+                               vstream->stream_file.fd->fd,
+                               (uint64_t) be64toh(get_packet_info.offset));
                goto error;
        }
-       read_len = lttng_read(vstream->stream_fd->fd, data, len);
-       if (read_len < len) {
+       read_len = lttng_read(vstream->stream_file.fd->fd,
+                       reply + sizeof(reply_header), packet_data_len);
+       if (read_len < packet_data_len) {
                PERROR("Relay reading trace file, fd: %d, offset: %" PRIu64,
-                               vstream->stream_fd->fd,
-                               be64toh(get_packet_info.offset));
+                               vstream->stream_file.fd->fd,
+                               (uint64_t) be64toh(get_packet_info.offset));
                goto error;
        }
-       reply.status = htobe32(LTTNG_VIEWER_GET_PACKET_OK);
-       reply.len = htobe32(len);
-       send_data = 1;
+       reply_header.status = htobe32(LTTNG_VIEWER_GET_PACKET_OK);
+       reply_header.len = htobe32(packet_data_len);
        goto send_reply;
 
 error:
-       reply.status = htobe32(LTTNG_VIEWER_GET_PACKET_ERR);
+       reply_header.status = htobe32(LTTNG_VIEWER_GET_PACKET_ERR);
 
 send_reply:
        if (vstream) {
                pthread_mutex_unlock(&vstream->stream->lock);
        }
 send_reply_nolock:
-       reply.flags = htobe32(reply.flags);
 
        health_code_update();
 
-       ret = send_response(conn->sock, &reply, sizeof(reply));
-       if (ret < 0) {
-               goto end_free;
+       if (reply) {
+               memcpy(reply, &reply_header, sizeof(reply_header));
+               ret = send_response(conn->sock, reply, reply_size);
+       } else {
+               /* No reply to send. */
+               ret = send_response(conn->sock, &reply_header,
+                               reply_size);
        }
-       health_code_update();
 
-       if (send_data) {
-               health_code_update();
-               ret = send_response(conn->sock, data, len);
-               if (ret < 0) {
-                       goto end_free;
-               }
-               health_code_update();
+       health_code_update();
+       if (ret < 0) {
+               PERROR("sendmsg of packet data failed");
+               goto end_free;
        }
 
-       DBG("Sent %u bytes for stream %" PRIu64, len,
-                       be64toh(get_packet_info.stream_id));
+       DBG("Sent %u bytes for stream %" PRIu64, reply_size,
+                       (uint64_t) be64toh(get_packet_info.stream_id));
 
 end_free:
-       free(data);
+       free(reply);
 end:
        if (vstream) {
                viewer_stream_put(vstream);
@@ -1641,6 +1709,8 @@ int viewer_get_metadata(struct relay_connection *conn)
                 * Reply back to the client with an error if we cannot
                 * find it.
                 */
+               DBG("Client requested metadata of unknown stream id %" PRIu64,
+                               (uint64_t) be64toh(request.stream_id));
                reply.status = htobe32(LTTNG_VIEWER_METADATA_ERR);
                goto send_reply;
        }
@@ -1659,23 +1729,31 @@ int viewer_get_metadata(struct relay_connection *conn)
        }
 
        /* first time, we open the metadata file */
-       if (!vstream->stream_fd) {
-               char fullpath[PATH_MAX];
-
-               ret = snprintf(fullpath, PATH_MAX, "%s/%s", vstream->path_name,
-                               vstream->channel_name);
+       if (!vstream->stream_file.fd) {
+               int fd;
+               char file_path[LTTNG_PATH_MAX];
+               enum lttng_trace_chunk_status status;
+               struct relay_stream *rstream = vstream->stream;
+
+               ret = utils_stream_file_path(rstream->path_name,
+                               rstream->channel_name, rstream->tracefile_size,
+                               vstream->current_tracefile_id, NULL, file_path,
+                               sizeof(file_path));
                if (ret < 0) {
                        goto error;
                }
-               ret = open(fullpath, O_RDONLY);
-               if (ret < 0) {
-                       PERROR("Relay opening metadata file");
+
+               status = lttng_trace_chunk_open_file(
+                               vstream->stream_file.trace_chunk,
+                               file_path, O_RDONLY, 0, &fd);
+               if (status != LTTNG_TRACE_CHUNK_STATUS_OK) {
+                       PERROR("Failed to open metadata file for viewer stream");
                        goto error;
                }
-               vstream->stream_fd = stream_fd_create(ret);
-               if (!vstream->stream_fd) {
-                       if (close(ret)) {
-                               PERROR("close");
+               vstream->stream_file.fd = stream_fd_create(fd);
+               if (!vstream->stream_file.fd) {
+                       if (close(fd)) {
+                               PERROR("Failed to close viewer metadata file");
                        }
                        goto error;
                }
@@ -1688,7 +1766,7 @@ int viewer_get_metadata(struct relay_connection *conn)
                goto error;
        }
 
-       read_len = lttng_read(vstream->stream_fd->fd, data, len);
+       read_len = lttng_read(vstream->stream_file.fd->fd, data, len);
        if (read_len < len) {
                PERROR("Relay reading metadata file");
                goto error;
@@ -1726,7 +1804,7 @@ send_reply:
        }
 
        DBG("Sent %" PRIu64 " bytes of metadata for stream %" PRIu64, len,
-                       be64toh(request.stream_id));
+                       (uint64_t) be64toh(request.stream_id));
 
        DBG("Metadata sent");
 
@@ -1774,6 +1852,78 @@ end:
        return ret;
 }
 
+/*
+ * Detach a viewer session.
+ *
+ * Return 0 on success or else a negative value.
+ */
+static
+int viewer_detach_session(struct relay_connection *conn)
+{
+       int ret;
+       struct lttng_viewer_detach_session_response response;
+       struct lttng_viewer_detach_session_request request;
+       struct relay_session *session = NULL;
+       uint64_t viewer_session_to_close;
+
+       DBG("Viewer detach session received");
+
+       assert(conn);
+
+       health_code_update();
+
+       /* Receive the request from the connected client. */
+       ret = recv_request(conn->sock, &request, sizeof(request));
+       if (ret < 0) {
+               goto end;
+       }
+       viewer_session_to_close = be64toh(request.session_id);
+
+       if (!conn->viewer_session) {
+               DBG("Client trying to detach before creating a live viewer session");
+               response.status = htobe32(LTTNG_VIEWER_DETACH_SESSION_ERR);
+               goto send_reply;
+       }
+
+       health_code_update();
+
+       memset(&response, 0, sizeof(response));
+       DBG("Detaching from session ID %" PRIu64, viewer_session_to_close);
+
+       session = session_get_by_id(be64toh(request.session_id));
+       if (!session) {
+               DBG("Relay session %" PRIu64 " not found",
+                               (uint64_t) be64toh(request.session_id));
+               response.status = htobe32(LTTNG_VIEWER_DETACH_SESSION_UNK);
+               goto send_reply;
+       }
+
+       ret = viewer_session_is_attached(conn->viewer_session, session);
+       if (ret != 1) {
+               DBG("Not attached to this session");
+               response.status = htobe32(LTTNG_VIEWER_DETACH_SESSION_ERR);
+               goto send_reply_put;
+       }
+
+       viewer_session_close_one_session(conn->viewer_session, session);
+       response.status = htobe32(LTTNG_VIEWER_DETACH_SESSION_OK);
+       DBG("Session %" PRIu64 " detached.", viewer_session_to_close);
+
+send_reply_put:
+       session_put(session);
+
+send_reply:
+       health_code_update();
+       ret = send_response(conn->sock, &response, sizeof(response));
+       if (ret < 0) {
+               goto end;
+       }
+       health_code_update();
+       ret = 0;
+
+end:
+       return ret;
+}
 
 /*
  * live_relay_unknown_command: send -1 if received unknown command
@@ -1835,6 +1985,9 @@ int process_control(struct lttng_viewer_cmd *recv_hdr,
        case LTTNG_VIEWER_CREATE_SESSION:
                ret = viewer_create_session(conn);
                break;
+       case LTTNG_VIEWER_DETACH_SESSION:
+               ret = viewer_detach_session(conn);
+               break;
        default:
                ERR("Received unknown viewer command (%u)",
                                be32toh(recv_hdr->cmd));
@@ -1935,11 +2088,6 @@ restart:
 
                        health_code_update();
 
-                       if (!revents) {
-                               /* No activity for this FD (poll implementation). */
-                               continue;
-                       }
-
                        /* Thread quit pipe has been closed. Killing thread. */
                        ret = check_thread_quit_pipe(pollfd, revents);
                        if (ret) {
@@ -1949,10 +2097,7 @@ restart:
 
                        /* Inspect the relay conn pipe for new connection. */
                        if (pollfd == live_conn_pipe[0]) {
-                               if (revents & (LPOLLERR | LPOLLHUP | LPOLLRDHUP)) {
-                                       ERR("Relay live pipe error");
-                                       goto error;
-                               } else if (revents & LPOLLIN) {
+                               if (revents & LPOLLIN) {
                                        struct relay_connection *conn;
 
                                        ret = lttng_read(live_conn_pipe[0],
@@ -1960,10 +2105,21 @@ restart:
                                        if (ret < 0) {
                                                goto error;
                                        }
-                                       lttng_poll_add(&events, conn->sock->fd,
+                                       ret = lttng_poll_add(&events,
+                                                       conn->sock->fd,
                                                        LPOLLIN | LPOLLRDHUP);
+                                       if (ret) {
+                                               ERR("Failed to add new live connection file descriptor to poll set");
+                                               goto error;
+                                       }
                                        connection_ht_add(viewer_connections_ht, conn);
                                        DBG("Connection socket %d added to poll", conn->sock->fd);
+                               } else if (revents & (LPOLLERR | LPOLLHUP | LPOLLRDHUP)) {
+                                       ERR("Relay live pipe error");
+                                       goto error;
+                               } else {
+                                       ERR("Unexpected poll events %u for sock %d", revents, pollfd);
+                                       goto error;
                                }
                        } else {
                                /* Connection activity. */
@@ -1974,11 +2130,7 @@ restart:
                                        continue;
                                }
 
-                               if (revents & (LPOLLERR | LPOLLHUP | LPOLLRDHUP)) {
-                                       cleanup_connection_pollfd(&events, pollfd);
-                                       /* Put "create" ownership reference. */
-                                       connection_put(conn);
-                               } else if (revents & LPOLLIN) {
+                               if (revents & LPOLLIN) {
                                        ret = conn->sock->ops->recvmsg(conn->sock, &recv_hdr,
                                                        sizeof(recv_hdr), 0);
                                        if (ret <= 0) {
@@ -1997,6 +2149,14 @@ restart:
                                                        DBG("Viewer connection closed with %d", pollfd);
                                                }
                                        }
+                               } else if (revents & (LPOLLERR | LPOLLHUP | LPOLLRDHUP)) {
+                                       cleanup_connection_pollfd(&events, pollfd);
+                                       /* Put "create" ownership reference. */
+                                       connection_put(conn);
+                               } else {
+                                       ERR("Unexpected poll events %u for sock %d", revents, pollfd);
+                                       connection_put(conn);
+                                       goto error;
                                }
                                /* Put local "get_by_sock" reference. */
                                connection_put(conn);
@@ -2008,7 +2168,7 @@ exit:
 error:
        lttng_poll_clean(&events);
 
-       /* Cleanup reamaining connection object. */
+       /* Cleanup remaining connection object. */
        rcu_read_lock();
        cds_lfht_for_each_entry(viewer_connections_ht->ht, &iter.iter,
                        destroy_conn,
@@ -2121,7 +2281,7 @@ int relayd_live_create(struct lttng_uri *uri)
        }
 
        /* Setup the dispatcher thread */
-       ret = pthread_create(&live_dispatcher_thread, NULL,
+       ret = pthread_create(&live_dispatcher_thread, default_pthread_attr(),
                        thread_dispatcher, (void *) NULL);
        if (ret) {
                errno = ret;
@@ -2131,7 +2291,7 @@ int relayd_live_create(struct lttng_uri *uri)
        }
 
        /* Setup the worker thread */
-       ret = pthread_create(&live_worker_thread, NULL,
+       ret = pthread_create(&live_worker_thread, default_pthread_attr(),
                        thread_worker, NULL);
        if (ret) {
                errno = ret;
@@ -2141,7 +2301,7 @@ int relayd_live_create(struct lttng_uri *uri)
        }
 
        /* Setup the listener thread */
-       ret = pthread_create(&live_listener_thread, NULL,
+       ret = pthread_create(&live_listener_thread, default_pthread_attr(),
                        thread_listener, (void *) NULL);
        if (ret) {
                errno = ret;
This page took 0.03958 seconds and 4 git commands to generate.