diff options
-rw-r--r-- | man/systemd.service.xml | 16 | ||||
-rw-r--r-- | man/systemd.socket.xml | 11 | ||||
-rw-r--r-- | src/core/dbus-service.c | 2 | ||||
-rw-r--r-- | src/core/dbus-socket.c | 1 | ||||
-rw-r--r-- | src/core/load-fragment-gperf.gperf.m4 | 3 | ||||
-rw-r--r-- | src/core/load-fragment.c | 2 | ||||
-rw-r--r-- | src/core/service.c | 6 | ||||
-rw-r--r-- | src/core/service.h | 3 | ||||
-rw-r--r-- | src/core/socket.c | 165 | ||||
-rw-r--r-- | src/core/socket.h | 3 |
10 files changed, 208 insertions, 4 deletions
diff --git a/man/systemd.service.xml b/man/systemd.service.xml index 4c113a3479..642d95a029 100644 --- a/man/systemd.service.xml +++ b/man/systemd.service.xml @@ -903,6 +903,22 @@ and no job queued or being executed for it.</para></listitem> </varlistentry> + <varlistentry> + <term><varname>USBFunctionDescriptors=</varname></term> + <listitem><para>Configure the location of file containing + FunctionFS descriptors. This is is used only when socket with + <varname>ListenUSBFunction</varname> line want to activate this service. Content of + this file is writen to ep0 file after it is opened. This is required + for socket activation using <varname>ListenUSBFunction</varname> + (i.e. for passing all ffs endpoints to service).</para></listitem> + </varlistentry> + + <varlistentry> + <term><varname>USBFunctionStrings=</varname></term> + <listitem><para>Configure the location of file containing FunctionFS strings. + Behavior is similar to <varname>USBFunctionDescriptors</varname>.</para></listitem> + </varlistentry> + </variablelist> <para>Check diff --git a/man/systemd.socket.xml b/man/systemd.socket.xml index 36fa3a86be..7f884aa1be 100644 --- a/man/systemd.socket.xml +++ b/man/systemd.socket.xml @@ -261,6 +261,17 @@ </varlistentry> <varlistentry> + <term><varname>ListenUSBFunction=</varname></term> + <listitem><para>Specifies a functionfs endpoint location + to listen on. This expects an absolute file system path as + argument. Behavior otherwise is very similar to the + <varname>ListenFIFO=</varname> directive above. Use this to + open functionfs endpoint ep0. When using this option, activated + service has to have <varname>USBFunctionDescriptors</varname> + and <varname>USBFunctionStrings</varname> options set.</para></listitem> + </varlistentry> + + <varlistentry> <term><varname>BindIPv6Only=</varname></term> <listitem><para>Takes a one of <option>default</option>, <option>both</option> or <option>ipv6-only</option>. Controls diff --git a/src/core/dbus-service.c b/src/core/dbus-service.c index e1f3d56495..3436342bef 100644 --- a/src/core/dbus-service.c +++ b/src/core/dbus-service.c @@ -62,6 +62,8 @@ const sd_bus_vtable bus_service_vtable[] = { SD_BUS_PROPERTY("StatusText", "s", NULL, offsetof(Service, status_text), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE), SD_BUS_PROPERTY("StatusErrno", "i", NULL, offsetof(Service, status_errno), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE), SD_BUS_PROPERTY("Result", "s", property_get_result, offsetof(Service, result), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE), + SD_BUS_PROPERTY("USBFunctionDescriptors", "s", NULL, offsetof(Service, usb_function_descriptors), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE), + SD_BUS_PROPERTY("USBFunctionStrings", "s", NULL, offsetof(Service, usb_function_strings), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE), BUS_EXEC_STATUS_VTABLE("ExecMain", offsetof(Service, main_exec_status), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE), BUS_EXEC_COMMAND_LIST_VTABLE("ExecStartPre", offsetof(Service, exec_command[SERVICE_EXEC_START_PRE]), SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION), BUS_EXEC_COMMAND_LIST_VTABLE("ExecStart", offsetof(Service, exec_command[SERVICE_EXEC_START]), SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION), diff --git a/src/core/dbus-socket.c b/src/core/dbus-socket.c index 02599a9e55..86732e2a45 100644 --- a/src/core/dbus-socket.c +++ b/src/core/dbus-socket.c @@ -68,6 +68,7 @@ static int property_get_listen( case SOCKET_SPECIAL: case SOCKET_MQUEUE: case SOCKET_FIFO: + case SOCKET_USB_FUNCTION: a = p->path; break; diff --git a/src/core/load-fragment-gperf.gperf.m4 b/src/core/load-fragment-gperf.gperf.m4 index ec744214c1..fd293d8287 100644 --- a/src/core/load-fragment-gperf.gperf.m4 +++ b/src/core/load-fragment-gperf.gperf.m4 @@ -234,6 +234,8 @@ Service.FileDescriptorStoreMax, config_parse_unsigned, 0, Service.NotifyAccess, config_parse_notify_access, 0, offsetof(Service, notify_access) Service.Sockets, config_parse_service_sockets, 0, 0 Service.BusPolicy, config_parse_bus_endpoint_policy, 0, offsetof(Service, exec_context) +Service.USBFunctionDescriptors, config_parse_path, 0, offsetof(Service, usb_function_descriptors) +Service.USBFunctionStrings, config_parse_path, 0, offsetof(Service, usb_function_strings) EXEC_CONTEXT_CONFIG_ITEMS(Service)m4_dnl CGROUP_CONTEXT_CONFIG_ITEMS(Service)m4_dnl KILL_CONTEXT_CONFIG_ITEMS(Service)m4_dnl @@ -245,6 +247,7 @@ Socket.ListenFIFO, config_parse_socket_listen, SOCKET_FIFO Socket.ListenNetlink, config_parse_socket_listen, SOCKET_SOCKET, 0 Socket.ListenSpecial, config_parse_socket_listen, SOCKET_SPECIAL, 0 Socket.ListenMessageQueue, config_parse_socket_listen, SOCKET_MQUEUE, 0 +Socket.ListenUSBFunction, config_parse_socket_listen, SOCKET_USB_FUNCTION, 0 Socket.BindIPv6Only, config_parse_socket_bind, 0, 0, Socket.Backlog, config_parse_unsigned, 0, offsetof(Socket, backlog) Socket.BindToDevice, config_parse_socket_bindtodevice, 0, 0 diff --git a/src/core/load-fragment.c b/src/core/load-fragment.c index fcf863c5c7..f42bee4fa9 100644 --- a/src/core/load-fragment.c +++ b/src/core/load-fragment.c @@ -381,6 +381,8 @@ int config_parse_socket_listen(const char *unit, } p->fd = -1; + p->auxiliary_fds = NULL; + p->n_auxiliary_fds = 0; p->socket = s; if (s->ports) { diff --git a/src/core/service.c b/src/core/service.c index fc28ba4d07..f7de5e89ff 100644 --- a/src/core/service.c +++ b/src/core/service.c @@ -482,6 +482,12 @@ static int service_verify(Service *s) { return -EINVAL; } + if (s->usb_function_descriptors && !s->usb_function_strings) + log_unit_warning(UNIT(s), "Service has USBFunctionDescriptors= setting, but no USBFunctionStrings=. Ignoring."); + + if (!s->usb_function_descriptors && s->usb_function_strings) + log_unit_warning(UNIT(s), "Service has USBFunctionStrings= setting, but no USBFunctionDescriptors=. Ignoring."); + return 0; } diff --git a/src/core/service.h b/src/core/service.h index 7da0a93961..789dff23a9 100644 --- a/src/core/service.h +++ b/src/core/service.h @@ -212,6 +212,9 @@ struct Service { ServiceFDStore *fd_store; unsigned n_fd_store; unsigned n_fd_store_max; + + char *usb_function_descriptors; + char *usb_function_strings; }; extern const UnitVTable service_vtable; diff --git a/src/core/socket.c b/src/core/socket.c index 9db42a0333..54e94c4f74 100644 --- a/src/core/socket.c +++ b/src/core/socket.c @@ -50,6 +50,7 @@ #include "formats-util.h" #include "signal-util.h" #include "socket.h" +#include "copy.h" static const UnitActiveState state_translation_table[_SOCKET_STATE_MAX] = { [SOCKET_DEAD] = UNIT_INACTIVE, @@ -104,6 +105,16 @@ static void socket_unwatch_control_pid(Socket *s) { s->control_pid = 0; } +static void socket_cleanup_fd_list(SocketPort *p) { + int k = p->n_auxiliary_fds; + + while (k--) + safe_close(p->auxiliary_fds[k]); + + p->auxiliary_fds = mfree(p->auxiliary_fds); + p->n_auxiliary_fds = 0; +} + void socket_free_ports(Socket *s) { SocketPort *p; @@ -114,6 +125,7 @@ void socket_free_ports(Socket *s) { sd_event_source_unref(p->event_source); + socket_cleanup_fd_list(p); safe_close(p->fd); free(p->path); free(p); @@ -248,7 +260,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) @@ -639,6 +651,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 @@ -775,6 +789,7 @@ static void socket_close_fds(Socket *s) { continue; p->fd = safe_close(p->fd); + socket_cleanup_fd_list(p); /* One little note: we should normally not delete any * sockets in the file system here! After all some @@ -1051,6 +1066,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, @@ -1124,6 +1166,76 @@ static int socket_symlink(Socket *s) { return 0; } +static int ffs_write_descs(int fd, Unit *u) { + Service *s = SERVICE(u); + int r; + + if (!s->usb_function_descriptors || !s->usb_function_strings) + return -EINVAL; + + r = copy_file_fd(s->usb_function_descriptors, fd, false); + if (r < 0) + return 0; + + r = copy_file_fd(s->usb_function_strings, fd, false); + + return r; +} + +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; @@ -1220,6 +1332,21 @@ 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_write_descs(p->fd, s->service.unit); + if (r < 0) + goto rollback; + + r = ffs_dispatch_eps(p); + if (r < 0) + goto rollback; } else assert_not_reached("Unknown port type"); } @@ -2035,6 +2162,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); @@ -2184,6 +2313,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); @@ -2266,6 +2415,9 @@ const char* socket_port_type_to_string(SocketPort *p) { case SOCKET_FIFO: return "FIFO"; + case SOCKET_USB_FUNCTION: + return "USBFunction"; + default: return NULL; } @@ -2297,7 +2449,6 @@ static int socket_dispatch_io(sd_event_source *source, int fd, uint32_t revents, log_unit_error(UNIT(p->socket), "Got POLLHUP on a listening socket. The service probably invoked shutdown() on it, and should better not do that."); else log_unit_error(UNIT(p->socket), "Got unexpected poll event (0x%x) on socket.", revents); - goto fail; } @@ -2496,6 +2647,7 @@ static int socket_dispatch_timer(sd_event_source *source, usec_t usec, void *use int socket_collect_fds(Socket *s, int **fds, unsigned *n_fds) { int *rfds; unsigned rn_fds, k; + int i; SocketPort *p; assert(s); @@ -2505,9 +2657,11 @@ int socket_collect_fds(Socket *s, int **fds, unsigned *n_fds) { /* Called from the service code for requesting our fds */ rn_fds = 0; - LIST_FOREACH(port, p, s->ports) + LIST_FOREACH(port, p, s->ports) { if (p->fd >= 0) rn_fds++; + rn_fds += p->n_auxiliary_fds; + } if (rn_fds <= 0) { *fds = NULL; @@ -2520,9 +2674,12 @@ int socket_collect_fds(Socket *s, int **fds, unsigned *n_fds) { return -ENOMEM; k = 0; - LIST_FOREACH(port, p, s->ports) + LIST_FOREACH(port, p, s->ports) { if (p->fd >= 0) rfds[k++] = p->fd; + for (i = 0; i < p->n_auxiliary_fds; ++i) + rfds[k++] = p->auxiliary_fds[i]; + } assert(k == rn_fds); diff --git a/src/core/socket.h b/src/core/socket.h index fa3ebdafa0..286397b41c 100644 --- a/src/core/socket.h +++ b/src/core/socket.h @@ -60,6 +60,7 @@ typedef enum SocketType { SOCKET_FIFO, SOCKET_SPECIAL, SOCKET_MQUEUE, + SOCKET_USB_FUNCTION, _SOCKET_FIFO_MAX, _SOCKET_FIFO_INVALID = -1 } SocketType; @@ -81,6 +82,8 @@ typedef struct SocketPort { SocketType type; int fd; + int *auxiliary_fds; + int n_auxiliary_fds; SocketAddress address; char *path; |