actions: introduce action group
[lttng-tools.git] / src / common / unix.c
index 0e94bbc2b42b39c8c375a33ce619d5a7b343a612..26eda52d97d1ad992c7b534b461c121eee9b6d00 100644 (file)
@@ -1,19 +1,9 @@
 /*
- * Copyright (C) 2011 David Goulet <david.goulet@polymtl.ca>
- *                      Mathieu Desnoyers <mathieu.desnoyers@efficios.com>
+ * Copyright (C) 2011 David Goulet <david.goulet@polymtl.ca>
+ * Copyright (C) 2011 Mathieu Desnoyers <mathieu.desnoyers@efficios.com>
  *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License, version 2 only,
- * as published by the Free Software Foundation.
+ * SPDX-License-Identifier: GPL-2.0-only
  *
- * This program is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
- * more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
  */
 
 #define _LGPL_SOURCE
@@ -41,6 +31,14 @@ int lttcomm_connect_unix_sock(const char *pathname)
        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");
@@ -82,7 +80,7 @@ int lttcomm_accept_unix_sock(int sock)
 {
        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);
@@ -111,9 +109,17 @@ LTTNG_HIDDEN
 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");
@@ -200,6 +206,45 @@ ssize_t lttcomm_recv_unix_sock(int sock, void *buf, size_t len)
        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 {
+                       /* We consider EPIPE and EAGAIN as expected. */
+                       if (!lttng_opt_quiet &&
+                                       (errno != EPIPE && errno != EAGAIN)) {
+                               PERROR("recvmsg");
+                       }
+                       goto end;
+               }
+       }
+       ret = len;
+end:
+       return ret;
+}
+
 /*
  * Send buf data of size len. Using sendmsg API.
  *
@@ -210,7 +255,7 @@ ssize_t lttcomm_send_unix_sock(int sock, const void *buf, size_t len)
 {
        struct msghdr msg;
        struct iovec iov[1];
-       ssize_t ret = -1;
+       ssize_t ret;
 
        memset(&msg, 0, sizeof(msg));
 
@@ -219,17 +264,69 @@ ssize_t lttcomm_send_unix_sock(int sock, const void *buf, size_t 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));
+
+       iov[0].iov_base = (void *) buf;
+       iov[0].iov_len = len;
+       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 {
+                       /* We consider EPIPE and EAGAIN as expected. */
+                       if (!lttng_opt_quiet &&
+                                       (errno != EPIPE && errno != EAGAIN)) {
+                               PERROR("sendmsg");
+                       }
+                       goto end;
                }
        }
-
+       ret = len;
+end:
        return ret;
 }
 
@@ -261,7 +358,7 @@ int lttcomm_close_unix_sock(int sock)
  * 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;
@@ -272,7 +369,7 @@ ssize_t lttcomm_send_fds_unix_sock(int sock, int *fds, size_t nb_fd)
        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;
@@ -284,6 +381,7 @@ ssize_t lttcomm_send_fds_unix_sock(int sock, int *fds, size_t nb_fd)
        if (!cmptr) {
                return -1;
        }
+
        cmptr->cmsg_level = SOL_SOCKET;
        cmptr->cmsg_type = SCM_RIGHTS;
        cmptr->cmsg_len = CMSG_LEN(sizeof_fds);
@@ -326,7 +424,15 @@ ssize_t lttcomm_recv_fds_unix_sock(int sock, int *fds, size_t nb_fd)
        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;
 
@@ -337,8 +443,15 @@ ssize_t lttcomm_recv_fds_unix_sock(int sock, int *fds, size_t nb_fd)
        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);
@@ -347,35 +460,59 @@ ssize_t lttcomm_recv_fds_unix_sock(int sock, int *fds, size_t nb_fd)
                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;
 }
This page took 0.026805 seconds and 5 git commands to generate.