]> git.ipfire.org Git - thirdparty/systemd.git/blobdiff - src/core/socket.c
core: Add socket type for usb functionfs endpoints
[thirdparty/systemd.git] / src / core / socket.c
index 518c935f96a84ad18ef189a9dfaba1564f64e9e7..d8de58ef156dfde943d1727094e3589338576e9c 100644 (file)
@@ -259,7 +259,7 @@ static int socket_add_mount_links(Socket *s) {
 
                 if (p->type == SOCKET_SOCKET)
                         path = socket_address_get_path(&p->address);
-                else if (p->type == SOCKET_FIFO || p->type == SOCKET_SPECIAL)
+                else if (IN_SET(p->type, SOCKET_FIFO, SOCKET_SPECIAL, SOCKET_USB_FUNCTION))
                         path = p->path;
 
                 if (!path)
@@ -650,6 +650,8 @@ static void socket_dump(Unit *u, FILE *f, const char *prefix) {
                         free(k);
                 } else if (p->type == SOCKET_SPECIAL)
                         fprintf(f, "%sListenSpecial: %s\n", prefix, p->path);
+                else if (p->type == SOCKET_USB_FUNCTION)
+                        fprintf(f, "%sListenUSBFunction: %s\n", prefix, p->path);
                 else if (p->type == SOCKET_MQUEUE)
                         fprintf(f, "%sListenMessageQueue: %s\n", prefix, p->path);
                 else
@@ -1063,6 +1065,33 @@ fail:
         return r;
 }
 
+static int ffs_address_create(
+                const char *path,
+                int *_fd) {
+
+        _cleanup_close_ int fd = -1;
+        struct stat st;
+
+        assert(path);
+        assert(_fd);
+
+        fd = open(path, O_RDWR|O_CLOEXEC|O_NOCTTY|O_NONBLOCK|O_NOFOLLOW);
+        if (fd < 0)
+                return -errno;
+
+        if (fstat(fd, &st) < 0)
+                return -errno;
+
+        /* Check whether this is a regular file (ffs endpoint)*/
+        if (!S_ISREG(st.st_mode))
+                return -EEXIST;
+
+        *_fd = fd;
+        fd = -1;
+
+        return 0;
+}
+
 static int mq_address_create(
                 const char *path,
                 mode_t mq_mode,
@@ -1136,6 +1165,60 @@ static int socket_symlink(Socket *s) {
         return 0;
 }
 
+static int select_ep(const struct dirent *d) {
+        return d->d_name[0] != '.' && !streq(d->d_name, "ep0");
+}
+
+static int ffs_dispatch_eps(SocketPort *p) {
+        _cleanup_free_ struct dirent **ent = NULL;
+        int r, i, n, k;
+        _cleanup_free_ char *path = NULL;
+
+        r = path_get_parent(p->path, &path);
+        if (r < 0)
+                return r;
+
+        r = scandir(path, &ent, select_ep, alphasort);
+        if (r < 0)
+                return -errno;
+
+        n = r;
+        p->auxiliary_fds = new(int, n);
+        if (!p->auxiliary_fds)
+                return -ENOMEM;
+
+        p->n_auxiliary_fds = n;
+
+        k = 0;
+        for (i = 0; i < n; ++i) {
+                _cleanup_free_ char *ep = NULL;
+
+                ep = path_make_absolute(ent[i]->d_name, path);
+                if (!ep)
+                        return -ENOMEM;
+
+                path_kill_slashes(ep);
+
+                r = ffs_address_create(ep, &p->auxiliary_fds[k]);
+                if (r < 0)
+                        goto fail;
+
+                ++k;
+                free(ent[i]);
+        }
+
+        return r;
+
+fail:
+        while (k)
+                safe_close(p->auxiliary_fds[--k]);
+
+        p->auxiliary_fds = mfree(p->auxiliary_fds);
+        p->n_auxiliary_fds = 0;
+
+        return r;
+}
+
 static int socket_open_fds(Socket *s) {
         SocketPort *p;
         int r;
@@ -1232,6 +1315,17 @@ static int socket_open_fds(Socket *s) {
                                         &p->fd);
                         if (r < 0)
                                 goto rollback;
+                } else  if (p->type == SOCKET_USB_FUNCTION) {
+
+                        r = ffs_address_create(
+                                        p->path,
+                                        &p->fd);
+                        if (r < 0)
+                                goto rollback;
+
+                        r = ffs_dispatch_eps(p);
+                        if (r < 0)
+                                goto rollback;
                 } else
                         assert_not_reached("Unknown port type");
         }
@@ -2047,6 +2141,8 @@ static int socket_serialize(Unit *u, FILE *f, FDSet *fds) {
                         unit_serialize_item_format(u, f, "special", "%i %s", copy, p->path);
                 else if (p->type == SOCKET_MQUEUE)
                         unit_serialize_item_format(u, f, "mqueue", "%i %s", copy, p->path);
+                else if (p->type == SOCKET_USB_FUNCTION)
+                        unit_serialize_item_format(u, f, "ffs", "%i %s", copy, p->path);
                 else {
                         assert(p->type == SOCKET_FIFO);
                         unit_serialize_item_format(u, f, "fifo", "%i %s", copy, p->path);
@@ -2196,6 +2292,26 @@ static int socket_deserialize_item(Unit *u, const char *key, const char *value,
                                 p->fd = fdset_remove(fds, fd);
                         }
                 }
+
+        } else if (streq(key, "ffs")) {
+                int fd, skip = 0;
+                SocketPort *p;
+
+                if (sscanf(value, "%i %n", &fd, &skip) < 1 || fd < 0 || !fdset_contains(fds, fd))
+                        log_unit_debug(u, "Failed to parse ffs value: %s", value);
+                else {
+
+                        LIST_FOREACH(port, p, s->ports)
+                                if (p->type == SOCKET_USB_FUNCTION &&
+                                    path_equal_or_files_same(p->path, value+skip))
+                                        break;
+
+                        if (p) {
+                                safe_close(p->fd);
+                                p->fd = fdset_remove(fds, fd);
+                        }
+                }
+
         } else
                 log_unit_debug(UNIT(s), "Unknown serialization key: %s", key);
 
@@ -2278,6 +2394,9 @@ const char* socket_port_type_to_string(SocketPort *p) {
         case SOCKET_FIFO:
                 return "FIFO";
 
+        case SOCKET_USB_FUNCTION:
+                return "USBFunction";
+
         default:
                 return NULL;
         }