+/*
+ * Send the fd(s) of a payload view over a unix socket.
+ *
+ * Returns the size of data sent, or negative error value.
+ */
+static
+ssize_t _lttcomm_send_payload_view_fds_unix_sock(int sock,
+ struct lttng_payload_view *view,
+ bool blocking)
+{
+ int i;
+ ssize_t ret;
+ struct lttng_dynamic_array raw_fds;
+ const int fd_count = lttng_payload_view_get_fd_handle_count(view);
+
+ lttng_dynamic_array_init(&raw_fds, sizeof(int), NULL);
+
+ if (fd_count < 0) {
+ ret = -LTTNG_ERR_INVALID;
+ goto end;
+ }
+
+ /*
+ * Prepare a contiguous array of file descriptors to send them.
+ *
+ * Note that the reference to each fd is released during the iteration;
+ * we're just getting the numerical value of the fds to conform to the
+ * syscall's interface. We rely on the fact that "view" must remain
+ * valid for the duration of the call and that the underlying payload
+ * owns a reference to the fd_handles.
+ */
+ for (i = 0; i < fd_count; i++) {
+ struct fd_handle *handle =
+ lttng_payload_view_pop_fd_handle(view);
+ const int raw_fd = fd_handle_get_fd(handle);
+ const int add_ret = lttng_dynamic_array_add_element(
+ &raw_fds, &raw_fd);
+
+ fd_handle_put(handle);
+ if (add_ret) {
+ ret = -LTTNG_ERR_NOMEM;
+ goto end;
+ }
+ }
+
+ if (blocking) {
+ ret = lttcomm_send_fds_unix_sock(sock,
+ (const int *) raw_fds.buffer.data, fd_count);
+ } else {
+ ret = lttcomm_send_fds_unix_sock_non_block(sock,
+ (const int *) raw_fds.buffer.data, fd_count);
+ }
+
+end:
+ lttng_dynamic_array_reset(&raw_fds);
+ return ret;
+}
+
+LTTNG_HIDDEN
+ssize_t lttcomm_send_payload_view_fds_unix_sock(int sock,
+ struct lttng_payload_view *view)
+{
+ return _lttcomm_send_payload_view_fds_unix_sock(sock, view, true);
+}
+
+LTTNG_HIDDEN
+ssize_t lttcomm_send_payload_view_fds_unix_sock_non_block(int sock,
+ struct lttng_payload_view *view)
+{
+ return _lttcomm_send_payload_view_fds_unix_sock(sock, view, false);
+}
+
+/*
+ * Send a message accompanied by fd(s) over a unix socket.
+ * Only use for non blocking socket.
+ *
+ * Returns the size of data sent, or negative error value.
+ */
+LTTNG_HIDDEN
+ssize_t lttcomm_send_fds_unix_sock_non_block(int sock, const int *fds, size_t nb_fd)
+{
+ struct msghdr msg;
+ struct cmsghdr *cmptr;
+ struct iovec iov[1];
+ ssize_t ret = -1;
+ unsigned int sizeof_fds = nb_fd * sizeof(int);
+ char tmp[CMSG_SPACE(sizeof_fds)];
+ char dummy = 0;
+
+ memset(&msg, 0, sizeof(msg));
+ memset(tmp, 0, sizeof(tmp));
+
+ if (nb_fd > LTTCOMM_MAX_SEND_FDS)
+ return -EINVAL;
+
+ msg.msg_control = (caddr_t)tmp;
+ msg.msg_controllen = CMSG_LEN(sizeof_fds);
+
+ cmptr = CMSG_FIRSTHDR(&msg);
+ if (!cmptr) {
+ return -1;
+ }
+
+ cmptr->cmsg_level = SOL_SOCKET;
+ cmptr->cmsg_type = SCM_RIGHTS;
+ cmptr->cmsg_len = CMSG_LEN(sizeof_fds);
+ memcpy(CMSG_DATA(cmptr), fds, sizeof_fds);
+ /* Sum of the length of all control messages in the buffer: */
+ msg.msg_controllen = cmptr->cmsg_len;
+
+ iov[0].iov_base = &dummy;
+ iov[0].iov_len = 1;
+ msg.msg_iov = iov;
+ msg.msg_iovlen = 1;
+
+retry:
+ ret = sendmsg(sock, &msg, 0);
+ if (ret < 0) {
+ if (errno == EINTR) {
+ goto retry;
+ } else {
+ /*
+ * We consider EPIPE and EAGAIN/EWOULDBLOCK as expected.
+ */
+ if (errno == EAGAIN || errno == EWOULDBLOCK) {
+ /*
+ * This can happen in non blocking mode.
+ * Nothing was sent.
+ */
+ ret = 0;
+ goto end;
+ }
+
+ if (errno == EPIPE) {
+ /* Expected error, pass error to caller */
+ DBG3("EPIPE on sendmsg");
+ ret = -1;
+ goto end;
+ }
+
+ /* Unexpected error */
+ PERROR("sendmsg");
+ ret = -1;
+ goto end;
+ }
+ }
+
+end:
+ return ret;
+}
+