]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
socket-util: add new connect_unix_path() helper
authorLennart Poettering <lennart@poettering.net>
Tue, 10 May 2022 14:16:29 +0000 (16:16 +0200)
committerYu Watanabe <watanabe.yu+github@gmail.com>
Fri, 13 May 2022 20:01:38 +0000 (05:01 +0900)
This is a short helper for connecting to AF_UNIX sockets in the file
system. It works around the 108ch limit of sockaddr_un, and supports
"at" style fds.

This doesn't come with a test of its own, but the next patch will add
that.

src/basic/socket-util.c
src/basic/socket-util.h

index 0dfe2a7dbca0e9c791835d59c04e7049a2412d88..bd6d329d941512e1e600607722d4bfc3bf56e4e7 100644 (file)
@@ -1426,3 +1426,51 @@ int socket_get_mtu(int fd, int af, size_t *ret) {
         *ret = (size_t) mtu;
         return 0;
 }
+
+int connect_unix_path(int fd, int dir_fd, const char *path) {
+        _cleanup_close_ int inode_fd = -1;
+        union sockaddr_union sa = {
+                .un.sun_family = AF_UNIX,
+        };
+        size_t path_len;
+        socklen_t salen;
+
+        assert(fd >= 0);
+        assert(dir_fd == AT_FDCWD || dir_fd >= 0);
+        assert(path);
+
+        /* Connects to the specified AF_UNIX socket in the file system. Works around the 108 byte size limit
+         * in sockaddr_un, by going via O_PATH if needed. This hence works for any kind of path. */
+
+        path_len = strlen(path);
+
+        /* Refuse zero length path early, to make sure AF_UNIX stack won't mistake this for an abstract
+         * namespace path, since first char is NUL */
+        if (path_len <= 0)
+                return -EINVAL;
+
+        if (dir_fd == AT_FDCWD && path_len < sizeof(sa.un.sun_path)) {
+                memcpy(sa.un.sun_path, path, path_len + 1);
+                salen = offsetof(struct sockaddr_un, sun_path) + path_len + 1;
+        } else {
+                const char *proc;
+                size_t proc_len;
+
+                /* If dir_fd is specified, then we need to go the indirect O_PATH route, because connectat()
+                 * does not exist. If the path is too long, we also need to take the indirect route, since we
+                 * can't fit this into a sockaddr_un directly. */
+
+                inode_fd = openat(dir_fd, path, O_PATH|O_CLOEXEC);
+                if (inode_fd < 0)
+                        return -errno;
+
+                proc = FORMAT_PROC_FD_PATH(inode_fd);
+                proc_len = strlen(proc);
+
+                assert(proc_len < sizeof(sa.un.sun_path));
+                memcpy(sa.un.sun_path, proc, proc_len + 1);
+                salen = offsetof(struct sockaddr_un, sun_path) + proc_len + 1;
+        }
+
+        return RET_NERRNO(connect(fd, &sa.sa, salen));
+}
index 5d9c05674431708eb9f5481b14ffb9f5e64e3a5a..fdadc9c6869915e96bccd9bb1ace4376d9d6a2b0 100644 (file)
@@ -332,3 +332,5 @@ int socket_get_mtu(int fd, int af, size_t *ret);
 
 /* an initializer for struct ucred that initialized all fields to the invalid value appropriate for each */
 #define UCRED_INVALID { .pid = 0, .uid = UID_INVALID, .gid = GID_INVALID }
+
+int connect_unix_path(int fd, int dir_fd, const char *path);