+
+ stream = zmalloc(sizeof(*stream));
+ if (!stream)
+ goto alloc_error;
+ stream->handle = handle;
+ stream->buf = buf;
+ stream->chan = channel;
+ stream->shm_fd = shm_fd;
+ stream->wait_fd = wait_fd;
+ stream->wakeup_fd = wakeup_fd;
+ stream->memory_map_size = memory_map_size;
+ stream->cpu = cpu;
+ return stream;
+
+alloc_error:
+ return NULL;
+}
+
+void ustctl_destroy_stream(struct ustctl_consumer_stream *stream)
+{
+ struct lttng_ust_lib_ring_buffer *buf;
+ struct ustctl_consumer_channel *consumer_chan;
+
+ assert(stream);
+ buf = stream->buf;
+ consumer_chan = stream->chan;
+ (void) ustctl_stream_close_wait_fd(stream);
+ (void) ustctl_stream_close_wakeup_fd(stream);
+ lib_ring_buffer_release_read(buf, consumer_chan->chan->handle);
+ free(stream);
+}
+
+int ustctl_channel_get_wait_fd(struct ustctl_consumer_channel *chan)
+{
+ if (!chan)
+ return -EINVAL;
+ return shm_get_wait_fd(chan->chan->handle,
+ &chan->chan->handle->chan._ref);
+}
+
+int ustctl_channel_get_wakeup_fd(struct ustctl_consumer_channel *chan)
+{
+ if (!chan)
+ return -EINVAL;
+ return shm_get_wakeup_fd(chan->chan->handle,
+ &chan->chan->handle->chan._ref);
+}
+
+int ustctl_stream_get_wait_fd(struct ustctl_consumer_stream *stream)
+{
+ struct lttng_ust_lib_ring_buffer *buf;
+ struct ustctl_consumer_channel *consumer_chan;
+
+ if (!stream)
+ return -EINVAL;
+ buf = stream->buf;
+ consumer_chan = stream->chan;
+ return shm_get_wait_fd(consumer_chan->chan->handle, &buf->self._ref);
+}
+
+int ustctl_stream_get_wakeup_fd(struct ustctl_consumer_stream *stream)
+{
+ struct lttng_ust_lib_ring_buffer *buf;
+ struct ustctl_consumer_channel *consumer_chan;
+
+ if (!stream)
+ return -EINVAL;
+ buf = stream->buf;
+ consumer_chan = stream->chan;
+ return shm_get_wakeup_fd(consumer_chan->chan->handle, &buf->self._ref);
+}
+
+/* For mmap mode, readable without "get" operation */
+
+void *ustctl_get_mmap_base(struct ustctl_consumer_stream *stream)
+{
+ struct lttng_ust_lib_ring_buffer *buf;
+ struct ustctl_consumer_channel *consumer_chan;
+
+ if (!stream)
+ return NULL;
+ buf = stream->buf;
+ consumer_chan = stream->chan;
+ return shmp(consumer_chan->chan->handle, buf->backend.memory_map);
+}
+
+/* returns the length to mmap. */
+int ustctl_get_mmap_len(struct ustctl_consumer_stream *stream,
+ unsigned long *len)
+{
+ struct ustctl_consumer_channel *consumer_chan;
+ unsigned long mmap_buf_len;
+ struct channel *chan;
+
+ if (!stream)
+ return -EINVAL;
+ consumer_chan = stream->chan;
+ chan = consumer_chan->chan->chan;
+ if (chan->backend.config.output != RING_BUFFER_MMAP)
+ return -EINVAL;
+ mmap_buf_len = chan->backend.buf_size;
+ if (chan->backend.extra_reader_sb)
+ mmap_buf_len += chan->backend.subbuf_size;
+ if (mmap_buf_len > INT_MAX)
+ return -EFBIG;
+ *len = mmap_buf_len;
+ return 0;
+}
+
+/* returns the maximum size for sub-buffers. */
+int ustctl_get_max_subbuf_size(struct ustctl_consumer_stream *stream,
+ unsigned long *len)
+{
+ struct ustctl_consumer_channel *consumer_chan;
+ struct channel *chan;
+
+ if (!stream)
+ return -EINVAL;
+ consumer_chan = stream->chan;
+ chan = consumer_chan->chan->chan;
+ *len = chan->backend.subbuf_size;
+ return 0;
+}
+
+/*
+ * For mmap mode, operate on the current packet (between get/put or
+ * get_next/put_next).
+ */
+
+/* returns the offset of the subbuffer belonging to the mmap reader. */
+int ustctl_get_mmap_read_offset(struct ustctl_consumer_stream *stream,
+ unsigned long *off)
+{
+ struct channel *chan;
+ unsigned long sb_bindex;
+ struct lttng_ust_lib_ring_buffer *buf;
+ struct ustctl_consumer_channel *consumer_chan;
+ struct lttng_ust_lib_ring_buffer_backend_pages_shmp *barray_idx;
+ struct lttng_ust_lib_ring_buffer_backend_pages *pages;
+
+ if (!stream)
+ return -EINVAL;
+ buf = stream->buf;
+ consumer_chan = stream->chan;
+ chan = consumer_chan->chan->chan;
+ if (chan->backend.config.output != RING_BUFFER_MMAP)
+ return -EINVAL;
+ sb_bindex = subbuffer_id_get_index(&chan->backend.config,
+ buf->backend.buf_rsb.id);
+ barray_idx = shmp_index(consumer_chan->chan->handle, buf->backend.array,
+ sb_bindex);
+ if (!barray_idx)
+ return -EINVAL;
+ pages = shmp(consumer_chan->chan->handle, barray_idx->shmp);
+ if (!pages)
+ return -EINVAL;
+ *off = pages->mmap_offset;
+ return 0;
+}
+
+/* returns the size of the current sub-buffer, without padding (for mmap). */
+int ustctl_get_subbuf_size(struct ustctl_consumer_stream *stream,
+ unsigned long *len)
+{
+ struct ustctl_consumer_channel *consumer_chan;
+ struct channel *chan;
+ struct lttng_ust_lib_ring_buffer *buf;
+
+ if (!stream)
+ return -EINVAL;
+
+ buf = stream->buf;
+ consumer_chan = stream->chan;
+ chan = consumer_chan->chan->chan;
+ *len = lib_ring_buffer_get_read_data_size(&chan->backend.config, buf,
+ consumer_chan->chan->handle);
+ return 0;
+}
+
+/* returns the size of the current sub-buffer, without padding (for mmap). */
+int ustctl_get_padded_subbuf_size(struct ustctl_consumer_stream *stream,
+ unsigned long *len)
+{
+ struct ustctl_consumer_channel *consumer_chan;
+ struct channel *chan;
+ struct lttng_ust_lib_ring_buffer *buf;
+
+ if (!stream)
+ return -EINVAL;
+ buf = stream->buf;
+ consumer_chan = stream->chan;
+ chan = consumer_chan->chan->chan;
+ *len = lib_ring_buffer_get_read_data_size(&chan->backend.config, buf,
+ consumer_chan->chan->handle);
+ *len = LTTNG_UST_PAGE_ALIGN(*len);
+ return 0;
+}
+
+/* Get exclusive read access to the next sub-buffer that can be read. */
+int ustctl_get_next_subbuf(struct ustctl_consumer_stream *stream)
+{
+ struct lttng_ust_lib_ring_buffer *buf;
+ struct ustctl_consumer_channel *consumer_chan;
+
+ if (!stream)
+ return -EINVAL;
+ buf = stream->buf;
+ consumer_chan = stream->chan;
+ return lib_ring_buffer_get_next_subbuf(buf,
+ consumer_chan->chan->handle);
+}
+
+
+/* Release exclusive sub-buffer access, move consumer forward. */
+int ustctl_put_next_subbuf(struct ustctl_consumer_stream *stream)
+{
+ struct lttng_ust_lib_ring_buffer *buf;
+ struct ustctl_consumer_channel *consumer_chan;
+
+ if (!stream)
+ return -EINVAL;
+ buf = stream->buf;
+ consumer_chan = stream->chan;
+ lib_ring_buffer_put_next_subbuf(buf, consumer_chan->chan->handle);
+ return 0;
+}
+
+/* snapshot */
+
+/* Get a snapshot of the current ring buffer producer and consumer positions */
+int ustctl_snapshot(struct ustctl_consumer_stream *stream)
+{
+ struct lttng_ust_lib_ring_buffer *buf;
+ struct ustctl_consumer_channel *consumer_chan;
+
+ if (!stream)
+ return -EINVAL;
+ buf = stream->buf;
+ consumer_chan = stream->chan;
+ return lib_ring_buffer_snapshot(buf, &buf->cons_snapshot,
+ &buf->prod_snapshot, consumer_chan->chan->handle);
+}
+
+/*
+ * Get a snapshot of the current ring buffer producer and consumer positions
+ * even if the consumed and produced positions are contained within the same
+ * subbuffer.
+ */
+int ustctl_snapshot_sample_positions(struct ustctl_consumer_stream *stream)
+{
+ struct lttng_ust_lib_ring_buffer *buf;
+ struct ustctl_consumer_channel *consumer_chan;
+
+ if (!stream)
+ return -EINVAL;
+ buf = stream->buf;
+ consumer_chan = stream->chan;
+ return lib_ring_buffer_snapshot_sample_positions(buf,
+ &buf->cons_snapshot, &buf->prod_snapshot,
+ consumer_chan->chan->handle);
+}
+
+/* Get the consumer position (iteration start) */
+int ustctl_snapshot_get_consumed(struct ustctl_consumer_stream *stream,
+ unsigned long *pos)
+{
+ struct lttng_ust_lib_ring_buffer *buf;
+
+ if (!stream)
+ return -EINVAL;
+ buf = stream->buf;
+ *pos = buf->cons_snapshot;
+ return 0;
+}
+
+/* Get the producer position (iteration end) */
+int ustctl_snapshot_get_produced(struct ustctl_consumer_stream *stream,
+ unsigned long *pos)
+{
+ struct lttng_ust_lib_ring_buffer *buf;
+
+ if (!stream)
+ return -EINVAL;
+ buf = stream->buf;
+ *pos = buf->prod_snapshot;
+ return 0;
+}
+
+/* Get exclusive read access to the specified sub-buffer position */
+int ustctl_get_subbuf(struct ustctl_consumer_stream *stream,
+ unsigned long *pos)
+{
+ struct lttng_ust_lib_ring_buffer *buf;
+ struct ustctl_consumer_channel *consumer_chan;
+
+ if (!stream)
+ return -EINVAL;
+ buf = stream->buf;
+ consumer_chan = stream->chan;
+ return lib_ring_buffer_get_subbuf(buf, *pos,
+ consumer_chan->chan->handle);
+}
+
+/* Release exclusive sub-buffer access */
+int ustctl_put_subbuf(struct ustctl_consumer_stream *stream)
+{
+ struct lttng_ust_lib_ring_buffer *buf;
+ struct ustctl_consumer_channel *consumer_chan;
+
+ if (!stream)
+ return -EINVAL;
+ buf = stream->buf;
+ consumer_chan = stream->chan;
+ lib_ring_buffer_put_subbuf(buf, consumer_chan->chan->handle);
+ return 0;
+}
+
+void ustctl_flush_buffer(struct ustctl_consumer_stream *stream,
+ int producer_active)
+{
+ struct lttng_ust_lib_ring_buffer *buf;
+ struct ustctl_consumer_channel *consumer_chan;
+
+ assert(stream);
+ buf = stream->buf;
+ consumer_chan = stream->chan;
+ lib_ring_buffer_switch_slow(buf,
+ producer_active ? SWITCH_ACTIVE : SWITCH_FLUSH,
+ consumer_chan->chan->handle);
+}
+
+void ustctl_clear_buffer(struct ustctl_consumer_stream *stream)
+{
+ struct lttng_ust_lib_ring_buffer *buf;
+ struct ustctl_consumer_channel *consumer_chan;
+
+ assert(stream);
+ buf = stream->buf;
+ consumer_chan = stream->chan;
+ lib_ring_buffer_switch_slow(buf, SWITCH_ACTIVE,
+ consumer_chan->chan->handle);
+ lib_ring_buffer_clear_reader(buf, consumer_chan->chan->handle);
+}
+
+static
+struct lttng_ust_client_lib_ring_buffer_client_cb *get_client_cb(
+ struct lttng_ust_lib_ring_buffer *buf,
+ struct lttng_ust_shm_handle *handle)
+{
+ struct channel *chan;
+ const struct lttng_ust_lib_ring_buffer_config *config;
+ struct lttng_ust_client_lib_ring_buffer_client_cb *client_cb;
+
+ chan = shmp(handle, buf->backend.chan);
+ if (!chan)
+ return NULL;
+ config = &chan->backend.config;
+ if (!config->cb_ptr)
+ return NULL;
+ client_cb = caa_container_of(config->cb_ptr,
+ struct lttng_ust_client_lib_ring_buffer_client_cb,
+ parent);
+ return client_cb;
+}
+
+int ustctl_get_timestamp_begin(struct ustctl_consumer_stream *stream,
+ uint64_t *timestamp_begin)
+{
+ struct lttng_ust_client_lib_ring_buffer_client_cb *client_cb;
+ struct lttng_ust_lib_ring_buffer *buf;
+ struct lttng_ust_shm_handle *handle;
+
+ if (!stream || !timestamp_begin)
+ return -EINVAL;
+ buf = stream->buf;
+ handle = stream->chan->chan->handle;
+ client_cb = get_client_cb(buf, handle);
+ if (!client_cb)
+ return -ENOSYS;
+ return client_cb->timestamp_begin(buf, handle, timestamp_begin);
+}
+
+int ustctl_get_timestamp_end(struct ustctl_consumer_stream *stream,
+ uint64_t *timestamp_end)
+{
+ struct lttng_ust_client_lib_ring_buffer_client_cb *client_cb;
+ struct lttng_ust_lib_ring_buffer *buf;
+ struct lttng_ust_shm_handle *handle;
+
+ if (!stream || !timestamp_end)
+ return -EINVAL;
+ buf = stream->buf;
+ handle = stream->chan->chan->handle;
+ client_cb = get_client_cb(buf, handle);
+ if (!client_cb)
+ return -ENOSYS;
+ return client_cb->timestamp_end(buf, handle, timestamp_end);
+}
+
+int ustctl_get_events_discarded(struct ustctl_consumer_stream *stream,
+ uint64_t *events_discarded)
+{
+ struct lttng_ust_client_lib_ring_buffer_client_cb *client_cb;
+ struct lttng_ust_lib_ring_buffer *buf;
+ struct lttng_ust_shm_handle *handle;
+
+ if (!stream || !events_discarded)
+ return -EINVAL;
+ buf = stream->buf;
+ handle = stream->chan->chan->handle;
+ client_cb = get_client_cb(buf, handle);
+ if (!client_cb)
+ return -ENOSYS;
+ return client_cb->events_discarded(buf, handle, events_discarded);
+}
+
+int ustctl_get_content_size(struct ustctl_consumer_stream *stream,
+ uint64_t *content_size)
+{
+ struct lttng_ust_client_lib_ring_buffer_client_cb *client_cb;
+ struct lttng_ust_lib_ring_buffer *buf;
+ struct lttng_ust_shm_handle *handle;
+
+ if (!stream || !content_size)
+ return -EINVAL;
+ buf = stream->buf;
+ handle = stream->chan->chan->handle;
+ client_cb = get_client_cb(buf, handle);
+ if (!client_cb)
+ return -ENOSYS;
+ return client_cb->content_size(buf, handle, content_size);
+}
+
+int ustctl_get_packet_size(struct ustctl_consumer_stream *stream,
+ uint64_t *packet_size)
+{
+ struct lttng_ust_client_lib_ring_buffer_client_cb *client_cb;
+ struct lttng_ust_lib_ring_buffer *buf;
+ struct lttng_ust_shm_handle *handle;
+
+ if (!stream || !packet_size)
+ return -EINVAL;
+ buf = stream->buf;
+ handle = stream->chan->chan->handle;
+ client_cb = get_client_cb(buf, handle);
+ if (!client_cb)
+ return -ENOSYS;
+ return client_cb->packet_size(buf, handle, packet_size);
+}
+
+int ustctl_get_stream_id(struct ustctl_consumer_stream *stream,
+ uint64_t *stream_id)
+{
+ struct lttng_ust_client_lib_ring_buffer_client_cb *client_cb;
+ struct lttng_ust_lib_ring_buffer *buf;
+ struct lttng_ust_shm_handle *handle;
+
+ if (!stream || !stream_id)
+ return -EINVAL;
+ buf = stream->buf;
+ handle = stream->chan->chan->handle;
+ client_cb = get_client_cb(buf, handle);
+ if (!client_cb)
+ return -ENOSYS;
+ return client_cb->stream_id(buf, handle, stream_id);
+}
+
+int ustctl_get_current_timestamp(struct ustctl_consumer_stream *stream,
+ uint64_t *ts)
+{
+ struct lttng_ust_client_lib_ring_buffer_client_cb *client_cb;
+ struct lttng_ust_lib_ring_buffer *buf;
+ struct lttng_ust_shm_handle *handle;
+
+ if (!stream || !ts)
+ return -EINVAL;
+ buf = stream->buf;
+ handle = stream->chan->chan->handle;
+ client_cb = get_client_cb(buf, handle);
+ if (!client_cb || !client_cb->current_timestamp)
+ return -ENOSYS;
+ return client_cb->current_timestamp(buf, handle, ts);
+}
+
+int ustctl_get_sequence_number(struct ustctl_consumer_stream *stream,
+ uint64_t *seq)
+{
+ struct lttng_ust_client_lib_ring_buffer_client_cb *client_cb;
+ struct lttng_ust_lib_ring_buffer *buf;
+ struct lttng_ust_shm_handle *handle;
+
+ if (!stream || !seq)
+ return -EINVAL;
+ buf = stream->buf;
+ handle = stream->chan->chan->handle;
+ client_cb = get_client_cb(buf, handle);
+ if (!client_cb || !client_cb->sequence_number)
+ return -ENOSYS;
+ return client_cb->sequence_number(buf, handle, seq);
+}
+
+int ustctl_get_instance_id(struct ustctl_consumer_stream *stream,
+ uint64_t *id)
+{
+ struct lttng_ust_client_lib_ring_buffer_client_cb *client_cb;
+ struct lttng_ust_lib_ring_buffer *buf;
+ struct lttng_ust_shm_handle *handle;
+
+ if (!stream || !id)
+ return -EINVAL;
+ buf = stream->buf;
+ handle = stream->chan->chan->handle;
+ client_cb = get_client_cb(buf, handle);
+ if (!client_cb)
+ return -ENOSYS;
+ return client_cb->instance_id(buf, handle, id);
+}
+
+#ifdef LTTNG_UST_HAVE_PERF_EVENT
+
+int ustctl_has_perf_counters(void)
+{
+ return 1;
+}
+
+#else
+
+int ustctl_has_perf_counters(void)
+{
+ return 0;
+}
+
+#endif
+
+#ifdef __linux__
+/*
+ * Override application pid/uid/gid with unix socket credentials. If
+ * the application announced a pid matching our view, it means it is
+ * within the same pid namespace, so expose the ppid provided by the
+ * application.
+ */
+static
+int get_cred(int sock,
+ const struct ustctl_reg_msg *reg_msg,
+ uint32_t *pid,
+ uint32_t *ppid,
+ uint32_t *uid,
+ uint32_t *gid)
+{
+ struct ucred ucred;
+ socklen_t ucred_len = sizeof(struct ucred);
+ int ret;
+
+ ret = getsockopt(sock, SOL_SOCKET, SO_PEERCRED, &ucred, &ucred_len);
+ if (ret) {
+ return -LTTNG_UST_ERR_PEERCRED;
+ }
+ DBG("Unix socket peercred [ pid: %u, uid: %u, gid: %u ], "
+ "application registered claiming [ pid: %u, ppid: %u, uid: %u, gid: %u ]",
+ ucred.pid, ucred.uid, ucred.gid,
+ reg_msg->pid, reg_msg->ppid, reg_msg->uid, reg_msg->gid);
+ if (!ucred.pid) {
+ ERR("Unix socket credential pid=0. Refusing application in distinct, non-nested pid namespace.");
+ return -LTTNG_UST_ERR_PEERCRED_PID;
+ }
+ *pid = ucred.pid;
+ *uid = ucred.uid;
+ *gid = ucred.gid;
+ if (ucred.pid == reg_msg->pid) {
+ *ppid = reg_msg->ppid;
+ } else {
+ *ppid = 0;
+ }
+ return 0;
+}
+#elif defined(__FreeBSD__)
+#include <sys/ucred.h>
+
+/*
+ * Override application uid/gid with unix socket credentials. Use the
+ * first group of the cr_groups.
+ * Use the pid and ppid provided by the application on registration.
+ */
+static
+int get_cred(int sock,
+ const struct ustctl_reg_msg *reg_msg,
+ uint32_t *pid,
+ uint32_t *ppid,
+ uint32_t *uid,
+ uint32_t *gid)
+{
+ struct xucred xucred;
+ socklen_t xucred_len = sizeof(struct xucred);
+ int ret;
+
+ ret = getsockopt(sock, SOL_SOCKET, LOCAL_PEERCRED, &xucred, &xucred_len);
+ if (ret) {
+ return -LTTNG_UST_ERR_PEERCRED;
+ }
+ if (xucred.cr_version != XUCRED_VERSION || xucred.cr_ngroups < 1) {
+ return -LTTNG_UST_ERR_PEERCRED;
+ }
+ DBG("Unix socket peercred [ uid: %u, gid: %u ], "
+ "application registered claiming [ pid: %d, ppid: %d, uid: %u, gid: %u ]",
+ xucred.uid, xucred.cr_groups[0],
+ reg_msg->pid, reg_msg->ppid, reg_msg->uid, reg_msg->gid);
+ *pid = reg_msg->pid;
+ *ppid = reg_msg->ppid;
+ *uid = xucred.uid;
+ *gid = xucred.cr_groups[0];
+ return 0;
+}
+#else
+#warning "Using insecure fallback: trusting user id provided by registered applications. Please consider implementing use of unix socket credentials on your platform."
+static
+int get_cred(int sock,
+ const struct ustctl_reg_msg *reg_msg,
+ uint32_t *pid,
+ uint32_t *ppid,
+ uint32_t *uid,
+ uint32_t *gid)
+{
+ DBG("Application registered claiming [ pid: %u, ppid: %d, uid: %u, gid: %u ]",
+ reg_msg->pid, reg_msg->ppid, reg_msg->uid, reg_msg->gid);
+ *pid = reg_msg->pid;
+ *ppid = reg_msg->ppid;
+ *uid = reg_msg->uid;
+ *gid = reg_msg->gid;
+ return 0;
+}
+#endif
+
+/*
+ * Returns 0 on success, negative error value on error.
+ */
+int ustctl_recv_reg_msg(int sock,
+ enum ustctl_socket_type *type,
+ uint32_t *major,
+ uint32_t *minor,
+ uint32_t *pid,
+ uint32_t *ppid,
+ uint32_t *uid,
+ uint32_t *gid,
+ uint32_t *bits_per_long,
+ uint32_t *uint8_t_alignment,
+ uint32_t *uint16_t_alignment,
+ uint32_t *uint32_t_alignment,
+ uint32_t *uint64_t_alignment,
+ uint32_t *long_alignment,
+ int *byte_order,
+ char *name)
+{
+ ssize_t len;
+ struct ustctl_reg_msg reg_msg;
+
+ len = ustcomm_recv_unix_sock(sock, ®_msg, sizeof(reg_msg));
+ if (len > 0 && len != sizeof(reg_msg))
+ return -EIO;
+ if (len == 0)
+ return -EPIPE;
+ if (len < 0)
+ return len;
+
+ if (reg_msg.magic == LTTNG_UST_COMM_MAGIC) {
+ *byte_order = BYTE_ORDER == BIG_ENDIAN ?
+ BIG_ENDIAN : LITTLE_ENDIAN;
+ } else if (reg_msg.magic == bswap_32(LTTNG_UST_COMM_MAGIC)) {
+ *byte_order = BYTE_ORDER == BIG_ENDIAN ?
+ LITTLE_ENDIAN : BIG_ENDIAN;
+ } else {
+ return -LTTNG_UST_ERR_INVAL_MAGIC;