struct sockaddr_un s_un;
int fd, ret, closeret;
+ if (strlen(pathname) >= sizeof(s_un.sun_path)) {
+ ERR("unix socket address (\"%s\") is longer than the platform's limit (%zu > %zu).",
+ pathname, strlen(pathname) + 1,
+ sizeof(s_un.sun_path));
+ ret = -ENAMETOOLONG;
+ goto error;
+ }
+
fd = socket(PF_UNIX, SOCK_STREAM, 0);
if (fd < 0) {
PERROR("socket");
{
int new_fd;
struct sockaddr_un s_un;
- socklen_t len = 0;
+ socklen_t len = sizeof(s_un);
/* Blocking call */
new_fd = accept(sock, (struct sockaddr *) &s_un, &len);
int lttcomm_create_unix_sock(const char *pathname)
{
struct sockaddr_un s_un;
- int fd;
+ int fd = -1;
int ret = -1;
+ if (strlen(pathname) >= sizeof(s_un.sun_path)) {
+ ERR("unix socket address (\"%s\") is longer than the platform's limit (%zu > %zu).",
+ pathname, strlen(pathname) + 1,
+ sizeof(s_un.sun_path));
+ ret = -ENAMETOOLONG;
+ goto error;
+ }
+
/* Create server socket */
if ((fd = socket(PF_UNIX, SOCK_STREAM, 0)) < 0) {
PERROR("socket");
return ret;
}
+/*
+ * Receive data of size len in put that data into the buf param. Using recvmsg
+ * API. Only use with sockets set in non-blocking mode.
+ *
+ * Return the size of received data.
+ */
+LTTNG_HIDDEN
+ssize_t lttcomm_recv_unix_sock_non_block(int sock, void *buf, size_t len)
+{
+ struct msghdr msg;
+ struct iovec iov[1];
+ ssize_t ret;
+
+ memset(&msg, 0, sizeof(msg));
+
+ iov[0].iov_base = buf;
+ iov[0].iov_len = len;
+ msg.msg_iov = iov;
+ msg.msg_iovlen = 1;
+
+retry:
+ ret = lttng_recvmsg_nosigpipe(sock, &msg);
+ if (ret < 0) {
+ if (errno == EINTR) {
+ goto retry;
+ } else {
+ /*
+ * Only warn about EPIPE when quiet mode is
+ * deactivated.
+ * We consider EPIPE as expected.
+ */
+ if (errno != EPIPE || !lttng_opt_quiet) {
+ PERROR("recvmsg");
+ }
+ goto end;
+ }
+ }
+ ret = len;
+end:
+ return ret;
+}
+
/*
* Send buf data of size len. Using sendmsg API.
*
{
struct msghdr msg;
struct iovec iov[1];
- ssize_t ret = -1;
+ ssize_t ret;
+
+ memset(&msg, 0, sizeof(msg));
+
+ iov[0].iov_base = (void *) buf;
+ iov[0].iov_len = len;
+ msg.msg_iov = iov;
+ msg.msg_iovlen = 1;
+
+ while (iov[0].iov_len) {
+ ret = sendmsg(sock, &msg, 0);
+ if (ret < 0) {
+ if (errno == EINTR) {
+ continue;
+ } else {
+ /*
+ * Only warn about EPIPE when quiet mode is
+ * deactivated.
+ * We consider EPIPE as expected.
+ */
+ if (errno != EPIPE || !lttng_opt_quiet) {
+ PERROR("sendmsg");
+ }
+ goto end;
+ }
+ }
+ iov[0].iov_len -= ret;
+ iov[0].iov_base += ret;
+ }
+ ret = len;
+end:
+ return ret;
+}
+
+/*
+ * Send buf data of size len. Using sendmsg API.
+ * Only use with non-blocking sockets. The difference with the blocking version
+ * of the function is that this one does not retry to send on partial sends,
+ * except if the interruption was caused by a signal (EINTR).
+ *
+ * Return the size of sent data.
+ */
+LTTNG_HIDDEN
+ssize_t lttcomm_send_unix_sock_non_block(int sock, const void *buf, size_t len)
+{
+ struct msghdr msg;
+ struct iovec iov[1];
+ ssize_t ret;
memset(&msg, 0, sizeof(msg));
msg.msg_iov = iov;
msg.msg_iovlen = 1;
+retry:
ret = sendmsg(sock, &msg, 0);
if (ret < 0) {
- /*
- * Only warn about EPIPE when quiet mode is deactivated.
- * We consider EPIPE as expected.
- */
- if (errno != EPIPE || !lttng_opt_quiet) {
- PERROR("sendmsg");
+ if (errno == EINTR) {
+ goto retry;
+ } else {
+ /*
+ * Only warn about EPIPE when quiet mode is
+ * deactivated.
+ * We consider EPIPE as expected.
+ */
+ if (errno != EPIPE || !lttng_opt_quiet) {
+ PERROR("sendmsg");
+ }
+ goto end;
}
}
-
+ ret = len;
+end:
return ret;
}
* Returns the size of data sent, or negative error value.
*/
LTTNG_HIDDEN
-ssize_t lttcomm_send_fds_unix_sock(int sock, int *fds, size_t nb_fd)
+ssize_t lttcomm_send_fds_unix_sock(int sock, const int *fds, size_t nb_fd)
{
struct msghdr msg;
struct cmsghdr *cmptr;
char dummy = 0;
memset(&msg, 0, sizeof(msg));
- memset(tmp, 0, CMSG_SPACE(sizeof_fds) * sizeof(char));
+ memset(tmp, 0, sizeof(tmp));
if (nb_fd > LTTCOMM_MAX_SEND_FDS)
return -EINVAL;
if (!cmptr) {
return -1;
}
+
cmptr->cmsg_level = SOL_SOCKET;
cmptr->cmsg_type = SCM_RIGHTS;
cmptr->cmsg_len = CMSG_LEN(sizeof_fds);
ssize_t ret = 0;
struct cmsghdr *cmsg;
size_t sizeof_fds = nb_fd * sizeof(int);
- char recv_fd[CMSG_SPACE(sizeof_fds)];
+
+#ifdef __linux__
+/* Account for the struct ucred cmsg in the buffer size */
+#define LTTNG_SOCK_RECV_FDS_BUF_SIZE CMSG_SPACE(sizeof_fds) + CMSG_SPACE(sizeof(struct ucred))
+#else
+#define LTTNG_SOCK_RECV_FDS_BUF_SIZE CMSG_SPACE(sizeof_fds)
+#endif /* __linux__ */
+
+ char recv_buf[LTTNG_SOCK_RECV_FDS_BUF_SIZE];
struct msghdr msg;
char dummy;
iov[0].iov_len = 1;
msg.msg_iov = iov;
msg.msg_iovlen = 1;
- msg.msg_control = recv_fd;
- msg.msg_controllen = sizeof(recv_fd);
+
+ cmsg = (struct cmsghdr *) recv_buf;
+ cmsg->cmsg_len = CMSG_LEN(sizeof_fds);
+ cmsg->cmsg_level = SOL_SOCKET;
+ cmsg->cmsg_type = SCM_RIGHTS;
+
+ msg.msg_control = cmsg;
+ msg.msg_controllen = CMSG_LEN(sizeof(recv_buf));
+ msg.msg_flags = 0;
do {
ret = recvmsg(sock, &msg, 0);
PERROR("recvmsg fds");
goto end;
}
+
if (ret != 1) {
fprintf(stderr, "Error: Received %zd bytes, expected %d\n",
ret, 1);
goto end;
}
+
if (msg.msg_flags & MSG_CTRUNC) {
fprintf(stderr, "Error: Control message truncated.\n");
ret = -1;
goto end;
}
- cmsg = CMSG_FIRSTHDR(&msg);
- if (!cmsg) {
- fprintf(stderr, "Error: Invalid control message header\n");
- ret = -1;
- goto end;
- }
- if (cmsg->cmsg_level != SOL_SOCKET || cmsg->cmsg_type != SCM_RIGHTS) {
- fprintf(stderr, "Didn't received any fd\n");
- ret = -1;
- goto end;
- }
- if (cmsg->cmsg_len != CMSG_LEN(sizeof_fds)) {
- fprintf(stderr, "Error: Received %zu bytes of ancillary data, expected %zu\n",
- (size_t) cmsg->cmsg_len, (size_t) CMSG_LEN(sizeof_fds));
- ret = -1;
- goto end;
+
+ /*
+ * If the socket was configured with SO_PASSCRED, the kernel will add a
+ * control message (cmsg) to the ancillary data of the unix socket. We
+ * need to expect a cmsg of the SCM_CREDENTIALS as the first control
+ * message.
+ */
+ for (cmsg = CMSG_FIRSTHDR(&msg); cmsg != NULL; cmsg = CMSG_NXTHDR(&msg, cmsg)) {
+ if (cmsg->cmsg_level != SOL_SOCKET) {
+ fprintf(stderr, "Error: The socket needs to be of type SOL_SOCKET\n");
+ ret = -1;
+ goto end;
+ }
+ if (cmsg->cmsg_type == SCM_RIGHTS) {
+ /*
+ * We found the controle message for file descriptors,
+ * now copy the fds to the fds ptr and return success.
+ */
+ if (cmsg->cmsg_len != CMSG_LEN(sizeof_fds)) {
+ fprintf(stderr, "Error: Received %zu bytes of"
+ "ancillary data for FDs, expected %zu\n",
+ (size_t) cmsg->cmsg_len,
+ (size_t) CMSG_LEN(sizeof_fds));
+ ret = -1;
+ goto end;
+ }
+ memcpy(fds, CMSG_DATA(cmsg), sizeof_fds);
+ ret = sizeof_fds;
+ goto end;
+ }
+#ifdef __linux__
+ if (cmsg->cmsg_type == SCM_CREDENTIALS) {
+ /*
+ * Expect credentials to be sent when expecting fds even
+ * if no credential were include in the send(). The
+ * kernel adds them...
+ */
+ ret = -1;
+ }
+#endif /* __linux__ */
}
- memcpy(fds, CMSG_DATA(cmsg), sizeof_fds);
- ret = sizeof_fds;
end:
return ret;
}