mirror of
https://github.com/systemd/systemd.git
synced 2025-03-31 14:50:15 +03:00
Merge pull request #1336 from pszewczyk/functionfs_sockets_v3
core: add support for usb functionfs v3
This commit is contained in:
commit
f98f4ace4d
@ -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
|
||||
|
@ -260,6 +260,17 @@
|
||||
can be inherited between processes.</para></listitem>
|
||||
</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>,
|
||||
|
@ -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),
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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
|
||||
|
@ -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) {
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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;
|
||||
|
Loading…
x
Reference in New Issue
Block a user