diff --git a/TODO.md b/TODO.md
index 1ebad3fcf8..a302415b84 100644
--- a/TODO.md
+++ b/TODO.md
@@ -126,6 +126,8 @@ SPDX-License-Identifier: LGPL-2.1-or-later
- remove any trace of "cpuacct" cgroup controller, it's a cgroupv1 thing.
similar "devices"
+- drop socket_xattr_supported() once our baseline is kernel 7.0
+
## Features
- **report:**
@@ -229,12 +231,6 @@ SPDX-License-Identifier: LGPL-2.1-or-later
sequences of this type, so that every step of the way we get the right
behaviour.
-- now that the kernel supports xattrs on sockets: mark varlink entrypoint
- sockets, server side of varlink sockets, and client sides of valrink sockets
- with distinct xattrs to make them recognizable (similar maybe for our other
- protocols, such as syslog, journal native entry point). For entrypoints might
- require new .socket unit setting.
-
- implement "varlinkctl trace" or so, that watches socket traffic on a group of
processes (select by pid, select by cgroup, select by all machine), and shows
traffic of all sockets marked via the new varlink socket xattrs. Use BPF for
diff --git a/man/org.freedesktop.systemd1.xml b/man/org.freedesktop.systemd1.xml
index 83748c6dd8..e7e56466d2 100644
--- a/man/org.freedesktop.systemd1.xml
+++ b/man/org.freedesktop.systemd1.xml
@@ -5245,6 +5245,12 @@ node /org/freedesktop/systemd1/unit/avahi_2ddaemon_2esocket {
@org.freedesktop.DBus.Property.EmitsChangedSignal("const")
readonly as Symlinks = ['...', ...];
@org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+ readonly a(ss) XAttrEntryPoint = [...];
+ @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+ readonly a(ss) XAttrListen = [...];
+ @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+ readonly a(ss) XAttrAccept = [...];
+ @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
readonly i Mark = ...;
@org.freedesktop.DBus.Property.EmitsChangedSignal("const")
readonly u MaxConnections = ...;
@@ -6616,6 +6622,12 @@ node /org/freedesktop/systemd1/unit/avahi_2ddaemon_2esocket {
+
+
+
+
+
+
@@ -7299,6 +7311,17 @@ node /org/freedesktop/systemd1/unit/avahi_2ddaemon_2esocket {
FlushPending specifies whether to flush the socket
just before entering the listening state. This setting only applies to sockets with
Accept= set to no.
+
+ XAttrEntryPoint, XAttrListen and
+ XAttrAccept each contain a list of extended attributes (as
+ NAME/VALUE
+ pairs) to set on socket inodes. XAttrEntryPoint applies to the file-system inode an
+ AF_UNIX socket is bound to, XAttrListen applies to the listening
+ socket, and XAttrAccept applies to connection sockets accepted off the listening
+ socket. See the XAttrEntryPoint=, XAttrListen= and
+ XAttrAccept= settings in
+ systemd.socket5 for
+ details.
@@ -12977,6 +13000,9 @@ $ gdbus introspect --system --dest org.freedesktop.systemd1 \
IOPressureWatch,
CPUSetPartition, and
OOMRules were added in version 261.
+ XAttrEntryPoint,
+ XAttrListen, and
+ XAttrAccept were added in version 262.
Mount Unit Objects
diff --git a/man/rules/meson.build b/man/rules/meson.build
index b37eb5f19f..974d9eee4b 100644
--- a/man/rules/meson.build
+++ b/man/rules/meson.build
@@ -977,6 +977,12 @@ manpages = [
['sd_varlink_is_connected', '3', ['sd_varlink_is_idle'], ''],
['sd_varlink_push_fd', '3', ['sd_varlink_push_dup_fd'], ''],
['sd_varlink_send', '3', ['sd_varlink_sendb', 'sd_varlink_sendbo'], ''],
+ ['sd_varlink_server_listen_address',
+ '3',
+ ['sd_varlink_server_listen_auto',
+ 'sd_varlink_server_listen_fd',
+ 'sd_varlink_server_listen_name'],
+ ''],
['sd_varlink_server_new', '3', [], ''],
['sd_varlink_set_description', '3', ['sd_varlink_get_description'], ''],
['sd_varlink_set_relative_timeout', '3', [], ''],
diff --git a/man/sd-varlink.xml b/man/sd-varlink.xml
index 576bdf1b7f..bd2b355652 100644
--- a/man/sd-varlink.xml
+++ b/man/sd-varlink.xml
@@ -55,6 +55,84 @@
makes the functionality implemented by sd-varlink available from the command line.
+
+ Recognizing Varlink Socket Inodes
+
+ On kernels that support extended attributes on socket inodes (available since Linux 7.0),
+ sd-varlink automatically tags the AF_UNIX socket inodes it creates and manages with
+ the user.varlink extended attribute. The attribute records the role the socket plays
+ in the Varlink communication, allowing other processes to discover and classify Varlink sockets, for
+ example via varlinkctl list-sockets (see
+ varlinkctl1).
+
+ Four distinct roles are defined:
+
+
+ user.varlink extended attribute roles
+
+
+
+
+
+
+ Attribute value
+ Meaning
+
+
+
+
+ client
+ Set on the connecting end of a connection, i.e. a socket created via socket() + connect(). Only for inodes on the anonymous socket file system (sockfs).
+
+
+ server
+ Set on the serving end of a connection, i.e. a socket obtained via accept() on a listening socket. Only for inodes on the anonymous socket file system (sockfs).
+
+
+ listen
+ Set on a listening socket, i.e. a socket created via socket() + listen(). Only for inodes on the anonymous socket file system (sockfs).
+
+
+ entrypoint
+ Set on the entrypoint socket inode, i.e. the socket node bound into the regular file system that clients connect to. Unlike the other three, this attribute lives on a real file-system inode (not on sockfs) and is hence visible to any process that can resolve the socket path.
+
+
+
+
+
+ Note that for socket activated Varlink services it is recommended to set these extended attributes
+ via XAttrEntryPoint=, XAttrListen= and
+ XAttrAccept= settings on the socket units, see
+ systemd.socket5 for
+ details. Specifically, for socket units for which Accept=no is set:
+
+ …
+[Socket]
+SocketMode=0644
+XAttrEntryPoint=user.varlink=entrypoint
+XAttrListen=user.varlink=listen
+…
+
+ For socket units which have Accept=yes:
+
+ …
+[Socket]
+Accept=yes
+SocketMode=0644
+XAttrEntryPoint=user.varlink=entrypoint
+XAttrListen=user.varlink=listen
+XAttrAccept=user.varlink=server
+…
+
+ Note that for Varlink sockets that shall not be world-accessible it is recommended to unset the
+ relevant "w" bits in the socket access mode, but to keep the relevant "r" bits set. As the access check
+ done by connect() looks for the "w" bits, this does not make the service
+ unnecessarily accessible, however it has the benefit that the extended attributes that identify the
+ socket as Varlink-related may still be read by other users (as that's restricted by the "r" bit). Or in
+ other words, it's recommended to set SocketMode=0644 for services that shall not be
+ accessible to arbitrary users, rather than SocketMode=0600.
+
+
@@ -87,6 +165,7 @@
sd-event3
sd-json3
varlinkctl1
+ systemd.socket5
sd-bus3
pkg-config1
diff --git a/man/sd_varlink_server_listen_address.xml b/man/sd_varlink_server_listen_address.xml
new file mode 100644
index 0000000000..55858c1c99
--- /dev/null
+++ b/man/sd_varlink_server_listen_address.xml
@@ -0,0 +1,181 @@
+
+
+
+
+
+
+
+ sd_varlink_server_listen_address
+ systemd
+
+
+
+ sd_varlink_server_listen_address
+ 3
+
+
+
+ sd_varlink_server_listen_address
+ sd_varlink_server_listen_fd
+ sd_varlink_server_listen_name
+ sd_varlink_server_listen_auto
+
+ Bind a Varlink server object to listening sockets
+
+
+
+
+ #include <systemd/sd-varlink.h>
+
+ #define SD_VARLINK_SERVER_MODE_MKDIR_0755 …
+
+
+ int sd_varlink_server_listen_address
+ sd_varlink_server* server
+ const char* address
+ mode_t mode
+
+
+
+ int sd_varlink_server_listen_fd
+ sd_varlink_server* server
+ int fd
+
+
+
+ int sd_varlink_server_listen_name
+ sd_varlink_server* server
+ const char* name
+
+
+
+ int sd_varlink_server_listen_auto
+ sd_varlink_server* server
+
+
+
+
+
+
+ Description
+
+ These functions add listening sockets to a Varlink server object, as previously allocated with
+ sd_varlink_server_new3.
+ Once the server object is attached to an event loop (see
+ sd_varlink_server_attach_event()) it will accept incoming connections on the
+ configured sockets and dispatch the registered method calls.
+
+ sd_varlink_server_listen_address() allocates a new
+ AF_UNIX socket, binds it to the specified address, and starts
+ listening on it. The address is a file system path of the socket to bind to. (Use
+ sd_varlink_server_listen_fd() below for sockets in the abstract namespace, in foreign
+ address families, or that are otherwise already allocated.)
+
+ The mode parameter specifies the file system access mode to apply to the
+ socket inode, i.e. a bit mask of permission bits as in
+ chmod2.
+ Only the lower nine bits (0777) are permitted. The special value
+ (mode_t) -1 may be specified to pick a suitable default: this is
+ 0666 in the general case, but 0644 if the server was allocated
+ with the SD_VARLINK_SERVER_ROOT_ONLY or
+ SD_VARLINK_SERVER_MYSELF_ONLY flags (the reason being that it is the
+ w bit that controls
+ connect2
+ access, while leaving the r bit on permits other users to read the socket's extended
+ attributes, which makes the socket recognizable as a Varlink entrypoint, see
+ sd-varlink3).
+
+ The constant SD_VARLINK_SERVER_MODE_MKDIR_0755 may be OR-ed into the
+ mode parameter (this is not supported if the mode is specified as
+ (mode_t) -1). If specified, and address is an absolute path,
+ the leading directories of the socket path are automatically created (with access mode
+ 0755) if they are missing.
+
+ sd_varlink_server_listen_fd() adds an already allocated and listening socket to
+ the server object, referenced by the file descriptor fd. This is typically used in
+ conjunction with socket activation, to make use of a listening socket passed in from the service manager,
+ see
+ sd_listen_fds3. The
+ file descriptor is automatically switched to non-blocking mode and the close-on-exec flag is set on
+ it.
+
+ sd_varlink_server_listen_name() picks up sockets passed in via the socket
+ activation protocol whose name (as set via $LISTEN_FDNAMES, i.e. via the
+ FileDescriptorName= setting of the
+ systemd.socket5 unit)
+ matches the specified name. For each matching file descriptor that refers to a
+ listening socket sd_varlink_server_listen_fd() is invoked; file descriptors that
+ refer to an already accepted connection are added as connections via
+ sd_varlink_server_add_connection() instead.
+
+ sd_varlink_server_listen_auto() is a convenience wrapper that automatically
+ determines the sockets to listen on. It first picks up all socket activation file descriptors named
+ varlink (by calling sd_varlink_server_listen_name() with that
+ name). In addition, if the $SYSTEMD_VARLINK_LISTEN environment variable is set, it is
+ used as an additional address to listen on: if set to - the Varlink protocol is spoken
+ on standard input and output (see sd_varlink_server_add_connection_stdio()),
+ otherwise the value is passed to sd_varlink_server_listen_address(). This is the
+ recommended way for a Varlink service to set up its listening sockets, as it transparently supports both
+ socket activation and direct invocation (for testing and debugging).
+
+
+
+ Return Value
+
+ On success, sd_varlink_server_listen_address() and
+ sd_varlink_server_listen_fd() return a non-negative integer.
+ sd_varlink_server_listen_name() and
+ sd_varlink_server_listen_auto() return the number of sockets and file descriptors
+ they added to the server object (which may be zero). On failure, these calls return a negative errno-style
+ error code.
+
+
+ Errors
+
+ Returned errors may indicate the following problems:
+
+
+
+ -EINVAL
+
+ An argument is invalid. This is also returned by
+ sd_varlink_server_listen_address() if the mode parameter
+ contains bits other than the permission bits and
+ SD_VARLINK_SERVER_MODE_MKDIR_0755.
+
+
+
+ -EBADF
+
+ The file descriptor passed to sd_varlink_server_listen_fd() is
+ negative.
+
+
+
+
+
+
+
+
+ History
+ sd_varlink_server_listen_address(),
+ sd_varlink_server_listen_fd(), and
+ sd_varlink_server_listen_auto() were added in version 257.
+ sd_varlink_server_listen_name() was added in version 258.
+
+
+
+ See Also
+
+
+ systemd1
+ sd-varlink3
+ sd_varlink_server_new3
+ sd_listen_fds3
+ systemd.socket5
+ varlinkctl1
+
+
+
+
diff --git a/man/systemd.socket.xml b/man/systemd.socket.xml
index 0de281f02e..6816b073b3 100644
--- a/man/systemd.socket.xml
+++ b/man/systemd.socket.xml
@@ -675,6 +675,40 @@
+
+ XAttrEntryPoint=
+ XAttrListen=
+ XAttrAccept=
+ Set an extended attribute on the socket, in the form
+ NAME=VALUE. The extended
+ attribute's name must be in the user. namespace, i.e. begin with the four
+ characters user.. Specifiers are expanded in both the name and the value, see
+ systemd.unit5 for
+ details on the available specifiers. These settings may be used more than once to set multiple
+ extended attributes; assigning the empty string resets the list.
+
+ The three variables control where the extended attributes are applied:
+ XAttrEntryPoint= sets them on the file-system inode an
+ AF_UNIX socket is bound to (i.e. the socket node visible in the file system at
+ the specified listening path; this only applies to AF_UNIX sockets),
+ XAttrListen= sets them on the listening socket, and
+ XAttrAccept= sets them on the connection sockets accepted off the listening socket
+ (and is thus only relevant in combination with
+ Accept=yes).
+
+ This is primarily useful to tag sockets so that they can be discovered and classified by other
+ tools. For example, Varlink entrypoint sockets are supposed to be tagged with
+ user.varlink=entrypoint via XAttrEntryPoint=, which makes them
+ discoverable via varlinkctl list-sockets, see
+ varlinkctl1.
+
+ These settings require kernel support for extended attributes on socket inodes (available since
+ Linux 7.0). Extended attributes that cannot be applied are logged at debug level and otherwise
+ ignored.
+
+
+
+
SELinuxContextFromNet=
Takes a boolean argument. When true, systemd
diff --git a/man/varlinkctl.xml b/man/varlinkctl.xml
index 72f1983c9e..539b0ea33d 100644
--- a/man/varlinkctl.xml
+++ b/man/varlinkctl.xml
@@ -81,6 +81,18 @@
CMDLINE
+
+ varlinkctl
+ OPTIONS
+ list-registry
+
+
+
+ varlinkctl
+ OPTIONS
+ list-sockets
+
+
varlinkctl
OPTIONS
@@ -221,6 +233,23 @@
+
+ list-sockets
+
+ Shows a list of listening AF_UNIX stream sockets on the local
+ system that are marked as Varlink entrypoints, along with whether the calling user has write (i.e.
+ connect) access to each of them. A socket is considered a Varlink entrypoint if its inode carries the
+ user.varlink extended attribute set to entrypoint.
+
+ Sockets are enumerated via the kernel's sock_diag netlink interface, and
+ hence this is not restricted to sockets in /run/varlink/registry/ (as
+ list-registry is), but covers all file-system bound listening Varlink sockets,
+ regardless of their location. This requires kernel support for extended attributes on socket inodes,
+ which is available since Linux 7.0.
+
+
+
+
validate-idl [FILE]
diff --git a/shell-completion/bash/varlinkctl b/shell-completion/bash/varlinkctl
index 53c62dbae5..cfde556840 100644
--- a/shell-completion/bash/varlinkctl
+++ b/shell-completion/bash/varlinkctl
@@ -63,7 +63,7 @@ _varlinkctl() {
fi
local -A VERBS=(
- [STANDALONE]='help list-registry'
+ [STANDALONE]='help list-registry list-sockets'
[CALL]='call'
[FILE]='info list-interfaces validate-idl'
[ADDRESS_INTERFACES]='list-methods introspect'
diff --git a/shell-completion/zsh/_varlinkctl b/shell-completion/zsh/_varlinkctl
index a071f6ec2e..aa44beaeb3 100644
--- a/shell-completion/zsh/_varlinkctl
+++ b/shell-completion/zsh/_varlinkctl
@@ -34,6 +34,7 @@ _regex_words varlink-commands 'varlink command' \
'introspect:show an interface definition:$varlink_interface' \
'call:invoke a method:$varlink_call' \
'validate-idl:validate an interface description:$varlink_idl' \
+ 'list-sockets:List listening Varlink entrypoint sockets' \
'help:show a help message'
local -a varlinkcmd=( /$'[^\0]#\0'/ "$reply[@]" )
diff --git a/src/basic/basic-forward.h b/src/basic/basic-forward.h
index 5f1bef4a3f..f02801a77d 100644
--- a/src/basic/basic-forward.h
+++ b/src/basic/basic-forward.h
@@ -56,6 +56,7 @@ struct statx;
struct termios;
struct tm;
struct ucred;
+struct unix_diag_msg;
/* To forward declare FILE and DIR, we have to declare the internal struct names for them. Since these are
* used for C++ symbol name mangling, they're effectively part of the ABI and won't actually change. */
diff --git a/src/basic/devnum-util.h b/src/basic/devnum-util.h
index 8588b434fb..b5ae0dee57 100644
--- a/src/basic/devnum-util.h
+++ b/src/basic/devnum-util.h
@@ -56,3 +56,9 @@ static inline bool devnum_is_zero(dev_t d) {
#define DEVNUM_TO_PTR(u) ((void*) (uintptr_t) (u))
#define PTR_TO_DEVNUM(p) ((dev_t) ((uintptr_t) (p)))
+
+/* Convert a userspace dev_t (as returned by stat()) to the kernel's internal dev_t encoding. stat() returns
+ * new_encode_dev(s_dev), while various kernel interfaces (e.g. the BPF sb helpers, or unix_diag's
+ * udiag_vfs_dev) report s_dev directly, which uses MKDEV(major, minor) = (major << 20) | minor. */
+#define STAT_DEV_TO_KERNEL(dev) \
+ ((uint32_t) major(dev) << 20 | (uint32_t) minor(dev))
diff --git a/src/basic/socket-util.c b/src/basic/socket-util.c
index d8f41f0f9c..c25e41d771 100644
--- a/src/basic/socket-util.c
+++ b/src/basic/socket-util.c
@@ -13,6 +13,7 @@
#include
#include
#include
+#include
#include
#include "alloc-util.h"
@@ -21,6 +22,7 @@
#include "fd-util.h"
#include "format-ifname.h"
#include "format-util.h"
+#include "fs-util.h"
#include "in-addr-util.h"
#include "io-util.h"
#include "log.h"
@@ -36,6 +38,8 @@
#include "string-util.h"
#include "strv.h"
#include "sysctl-util.h"
+#include "tmpfile-util.h"
+#include "xattr-util.h"
#if ENABLE_IDN
# define IDN_FLAGS NI_IDN
@@ -1891,3 +1895,41 @@ int tos_to_priority(uint8_t tos) {
return TC_PRIO_BESTEFFORT;
}
}
+
+int socket_xattr_supported(void) {
+ int r;
+
+ // FIXME: Drop this check once Linux 7.0 becomes our baseline
+
+ /* Checks if socket inodes may have xattrs on this kernel. This should pass on kernel 7.0, fail on
+ * older kernels */
+
+ static int cached = -1;
+ if (cached >= 0)
+ return cached;
+
+ const char *t;
+ r = tmp_dir(&t);
+ if (r < 0)
+ return r;
+
+ _cleanup_free_ char *sp = NULL;
+ r = tempfn_random_child(t, "sockxattrtest", &sp);
+ if (r < 0)
+ return r;
+
+ if (mknod(sp, S_IFSOCK | 0600, /* dev= */ 0) < 0)
+ return -errno;
+
+ _cleanup_(unlink_and_freep) char *sp_destroy = TAKE_PTR(sp);
+
+ /* Old kernels return EPERM. But let's also check for more appropriate error codes, to be friendly to
+ * seccomp policies */
+ r = xsetxattr(AT_FDCWD, sp_destroy, /* at_flags= */ 0, "user.testxxx", "1");
+ if (ERRNO_IS_NEG_NOT_SUPPORTED(r) || r == -EPERM)
+ return (cached = false);
+ if (r < 0)
+ return log_debug_errno(r, "Failed to set test xattr on socket inode '%s': %m", sp_destroy);
+
+ return (cached = true);
+}
diff --git a/src/basic/socket-util.h b/src/basic/socket-util.h
index b3a2733b74..f9d6ddaaad 100644
--- a/src/basic/socket-util.h
+++ b/src/basic/socket-util.h
@@ -268,3 +268,5 @@ int socket_get_cookie(int fd, uint64_t *ret);
void cmsg_close_all(struct msghdr *mh);
int tos_to_priority(uint8_t tos);
+
+int socket_xattr_supported(void);
diff --git a/src/basic/xattr-util.c b/src/basic/xattr-util.c
index fb886f6ae8..417e8650e5 100644
--- a/src/basic/xattr-util.c
+++ b/src/basic/xattr-util.c
@@ -36,8 +36,7 @@ static int normalize_and_maybe_pin_inode(
assert(ret_tfd);
assert(ret_opath);
- if (isempty(*path))
- *path = NULL; /* Normalize "" to NULL */
+ *path = empty_to_null(*path); /* Normalize "" to NULL */
if (*fd == AT_FDCWD) {
if (!*path) /* Both unspecified? Then operate on current working directory */
diff --git a/src/core/bpf-restrict-fsaccess.c b/src/core/bpf-restrict-fsaccess.c
index 4e0af5596a..daa64e6afd 100644
--- a/src/core/bpf-restrict-fsaccess.c
+++ b/src/core/bpf-restrict-fsaccess.c
@@ -5,6 +5,7 @@
#include
#include "bpf-restrict-fsaccess.h"
+#include "devnum-util.h"
#include "fd-util.h"
#include "fileio.h"
#include "initrd-util.h"
diff --git a/src/core/bpf-restrict-fsaccess.h b/src/core/bpf-restrict-fsaccess.h
index a39f602539..39b79353be 100644
--- a/src/core/bpf-restrict-fsaccess.h
+++ b/src/core/bpf-restrict-fsaccess.h
@@ -1,8 +1,6 @@
/* SPDX-License-Identifier: LGPL-2.1-or-later */
#pragma once
-#include
-
#include "core-forward.h"
#include "macro.h"
#include "shared-forward.h"
@@ -33,12 +31,6 @@ enum {
/* Maximum number of dm-verity devices tracked in the BPF hash map. */
#define DMVERITY_DEVICES_MAX (16U*1024U)
-/* Convert userspace dev_t (from stat()) to kernel dev_t encoding (MKDEV).
- * stat() returns new_encode_dev(s_dev); the BPF program reads s_dev directly
- * which uses MKDEV(major, minor) = (major << 20) | minor. */
-#define STAT_DEV_TO_KERNEL(dev) \
- ((uint32_t)major(dev) << 20 | (uint32_t)minor(dev))
-
/* Mirrors the BPF program's .bss section layout for read-modify-write via
* bpf_map_lookup_elem/bpf_map_update_elem on the serialized .bss map FD. */
struct restrict_fsaccess_bss {
diff --git a/src/core/dbus-socket.c b/src/core/dbus-socket.c
index ecb4df4dfd..26612015f0 100644
--- a/src/core/dbus-socket.c
+++ b/src/core/dbus-socket.c
@@ -58,6 +58,33 @@ static int property_get_listen(
return sd_bus_message_close_container(reply);
}
+static int property_get_xattr(
+ sd_bus *bus,
+ const char *path,
+ const char *interface,
+ const char *property,
+ sd_bus_message *reply,
+ void *userdata,
+ sd_bus_error *reterr_error) {
+
+ char ***xattr = ASSERT_PTR(userdata);
+ int r;
+
+ assert(reply);
+
+ r = sd_bus_message_open_container(reply, 'a', "(ss)");
+ if (r < 0)
+ return r;
+
+ STRV_FOREACH_PAIR(name, value, *xattr) {
+ r = sd_bus_message_append(reply, "(ss)", *name, *value);
+ if (r < 0)
+ return r;
+ }
+
+ return sd_bus_message_close_container(reply);
+}
+
const sd_bus_vtable bus_socket_vtable[] = {
SD_BUS_VTABLE_START(0),
SD_BUS_PROPERTY("BindIPv6Only", "s", property_get_bind_ipv6_only, offsetof(Socket, bind_ipv6_only), SD_BUS_VTABLE_PROPERTY_CONST),
@@ -95,6 +122,9 @@ const sd_bus_vtable bus_socket_vtable[] = {
SD_BUS_PROPERTY("RemoveOnStop", "b", bus_property_get_bool, offsetof(Socket, remove_on_stop), SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("Listen", "a(ss)", property_get_listen, 0, SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("Symlinks", "as", NULL, offsetof(Socket, symlinks), SD_BUS_VTABLE_PROPERTY_CONST),
+ SD_BUS_PROPERTY("XAttrEntryPoint", "a(ss)", property_get_xattr, offsetof(Socket, xattr_entrypoint), SD_BUS_VTABLE_PROPERTY_CONST),
+ SD_BUS_PROPERTY("XAttrListen", "a(ss)", property_get_xattr, offsetof(Socket, xattr_listen), SD_BUS_VTABLE_PROPERTY_CONST),
+ SD_BUS_PROPERTY("XAttrAccept", "a(ss)", property_get_xattr, offsetof(Socket, xattr_accept), SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("Mark", "i", bus_property_get_int, offsetof(Socket, mark), SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("MaxConnections", "u", bus_property_get_unsigned, offsetof(Socket, max_connections), SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("MaxConnectionsPerSource", "u", bus_property_get_unsigned, offsetof(Socket, max_connections_per_source), SD_BUS_VTABLE_PROPERTY_CONST),
@@ -153,6 +183,63 @@ static BUS_DEFINE_SET_TRANSIENT_TO_STRING(socket_protocol, "i", int32_t, int, "%
static BUS_DEFINE_SET_TRANSIENT_PARSE(socket_timestamping, SocketTimestamping, socket_timestamping_from_string_harder);
static BUS_DEFINE_SET_TRANSIENT_PARSE(socket_defer_trigger, SocketDeferTrigger, socket_defer_trigger_from_string);
+static int bus_socket_set_transient_xattr(
+ Unit *u,
+ const char *name,
+ char ***p,
+ sd_bus_message *message,
+ UnitWriteFlags flags,
+ sd_bus_error *reterr_error) {
+
+ _cleanup_strv_free_ char **pairs = NULL;
+ const char *xname, *xvalue;
+ bool empty = true;
+ int r;
+
+ assert(u);
+ assert(name);
+ assert(p);
+ assert(message);
+
+ r = sd_bus_message_enter_container(message, 'a', "(ss)");
+ if (r < 0)
+ return r;
+
+ while ((r = sd_bus_message_read(message, "(ss)", &xname, &xvalue)) > 0) {
+ if (!startswith(xname, "user."))
+ return sd_bus_error_setf(reterr_error, SD_BUS_ERROR_INVALID_ARGS,
+ "Extended attribute name does not begin with 'user.': %s", xname);
+
+ r = strv_extend_many(&pairs, xname, xvalue);
+ if (r < 0)
+ return -ENOMEM;
+
+ empty = false;
+ }
+ if (r < 0)
+ return r;
+
+ r = sd_bus_message_exit_container(message);
+ if (r < 0)
+ return r;
+
+ if (!UNIT_WRITE_FLAGS_NOOP(flags)) {
+ if (empty) {
+ *p = strv_free(*p);
+ unit_write_settingf(u, flags|UNIT_ESCAPE_SPECIFIERS, name, "%s=", name);
+ } else {
+ r = strv_extend_strv(p, pairs, /* filter_duplicates= */ false);
+ if (r < 0)
+ return -ENOMEM;
+
+ STRV_FOREACH_PAIR(n, v, pairs)
+ unit_write_settingf(u, flags|UNIT_ESCAPE_SPECIFIERS, name, "%s=%s=%s", name, *n, *v);
+ }
+ }
+
+ return 1;
+}
+
static int bus_socket_set_transient_property(
Socket *s,
const char *name,
@@ -335,6 +422,15 @@ static int bus_socket_set_transient_property(
&s->exec_command[ci],
message, flags, reterr_error);
+ if (streq(name, "XAttrEntryPoint"))
+ return bus_socket_set_transient_xattr(u, name, &s->xattr_entrypoint, message, flags, reterr_error);
+
+ if (streq(name, "XAttrListen"))
+ return bus_socket_set_transient_xattr(u, name, &s->xattr_listen, message, flags, reterr_error);
+
+ if (streq(name, "XAttrAccept"))
+ return bus_socket_set_transient_xattr(u, name, &s->xattr_accept, message, flags, reterr_error);
+
if (streq(name, "Symlinks")) {
_cleanup_strv_free_ char **l = NULL;
diff --git a/src/core/load-fragment-gperf.gperf.in b/src/core/load-fragment-gperf.gperf.in
index 2b8d2296f0..aa95bd4992 100644
--- a/src/core/load-fragment-gperf.gperf.in
+++ b/src/core/load-fragment-gperf.gperf.in
@@ -561,6 +561,9 @@ Socket.PollLimitIntervalSec, config_parse_sec,
Socket.PollLimitBurst, config_parse_unsigned, 0, offsetof(Socket, poll_limit.burst)
Socket.DeferTrigger, config_parse_socket_defer_trigger, 0, offsetof(Socket, defer_trigger)
Socket.DeferTriggerMaxSec, config_parse_sec_fix_0, 0, offsetof(Socket, defer_trigger_max_usec)
+Socket.XAttrEntryPoint, config_parse_xattr, 0, offsetof(Socket, xattr_entrypoint)
+Socket.XAttrListen, config_parse_xattr, 0, offsetof(Socket, xattr_listen)
+Socket.XAttrAccept, config_parse_xattr, 0, offsetof(Socket, xattr_accept)
{% if ENABLE_SMACK %}
Socket.SmackLabel, config_parse_unit_string_printf, 0, offsetof(Socket, smack)
Socket.SmackLabelIPIn, config_parse_unit_string_printf, 0, offsetof(Socket, smack_ip_in)
diff --git a/src/core/load-fragment.c b/src/core/load-fragment.c
index ff8bded569..3fa92aff56 100644
--- a/src/core/load-fragment.c
+++ b/src/core/load-fragment.c
@@ -2247,6 +2247,66 @@ int config_parse_socket_service(
return 0;
}
+int config_parse_xattr(
+ const char *unit,
+ const char *filename,
+ unsigned line,
+ const char *section,
+ unsigned section_line,
+ const char *lvalue,
+ int ltype,
+ const char *rvalue,
+ void *data,
+ void *userdata) {
+
+ char ***lp = ASSERT_PTR(data);
+ Unit *u = userdata;
+ int r;
+
+ assert(filename);
+ assert(lvalue);
+ assert(rvalue);
+
+ if (isempty(rvalue)) {
+ *lp = strv_free(*lp);
+ return 0;
+ }
+
+ _cleanup_free_ char *name = NULL, *value = NULL;
+ r = split_pair(rvalue, "=", &name, &value);
+ if (r < 0) {
+ log_syntax(unit, LOG_WARNING, filename, line, r, "Failed to parse extended attribute expression, ignoring: %s", rvalue);
+ return 0;
+ }
+
+ _cleanup_free_ char *expanded_name = NULL;
+ r = unit_full_printf(u, name, &expanded_name);
+ if (r < 0) {
+ log_syntax(unit, LOG_WARNING, filename, line, r, "Failed to expand specifiers in extended attribute expression, ignoring: %s", name);
+ return 0;
+ }
+
+ if (!startswith(expanded_name, "user.")) {
+ log_syntax(unit, LOG_WARNING, filename, line, 0, "Extended attribute name does not begin with 'user.', ignoring: %s", expanded_name);
+ return 0;
+ }
+
+ _cleanup_free_ char *expanded_value = NULL;
+ r = unit_full_printf(u, value, &expanded_value);
+ if (r < 0) {
+ log_syntax(unit, LOG_WARNING, filename, line, r, "Failed to expand specifiers in extended attribute expression, ignoring: %s", value);
+ return 0;
+ }
+
+ if (strv_push_pair(lp, expanded_name, expanded_value) < 0)
+ return log_oom();
+
+ TAKE_PTR(expanded_name);
+ TAKE_PTR(expanded_value);
+
+ return 0;
+}
+
int config_parse_fdname(
const char *unit,
const char *filename,
diff --git a/src/core/load-fragment.h b/src/core/load-fragment.h
index 48129f9602..15dd411cc5 100644
--- a/src/core/load-fragment.h
+++ b/src/core/load-fragment.h
@@ -173,6 +173,7 @@ CONFIG_PARSER_PROTOTYPE(config_parse_bind_network_interface);
CONFIG_PARSER_PROTOTYPE(config_parse_exec_memory_thp);
CONFIG_PARSER_PROTOTYPE(config_parse_cpuset_partition);
CONFIG_PARSER_PROTOTYPE(config_parse_luo_sessions);
+CONFIG_PARSER_PROTOTYPE(config_parse_xattr);
/* gperf prototypes */
const struct ConfigPerfItem* load_fragment_gperf_lookup(const char *key, GPERF_LEN_TYPE length);
diff --git a/src/core/socket.c b/src/core/socket.c
index a33a8d6359..a7aa34caeb 100644
--- a/src/core/socket.c
+++ b/src/core/socket.c
@@ -210,6 +210,10 @@ static void socket_done(Unit *u) {
strv_free(s->symlinks);
+ strv_free(s->xattr_entrypoint);
+ strv_free(s->xattr_listen);
+ strv_free(s->xattr_accept);
+
s->user = mfree(s->user);
s->group = mfree(s->group);
@@ -1510,7 +1514,9 @@ static int socket_address_listen_do(
s->directory_mode,
s->socket_mode,
selinux_label,
- s->smack);
+ s->smack,
+ s->xattr_entrypoint,
+ s->xattr_listen);
}
#define log_address_error_errno(u, address, error, fmt) \
@@ -3208,6 +3214,7 @@ static int socket_dispatch_io(sd_event_source *source, int fd, uint32_t revents,
if (cfd < 0)
goto fail;
+ (void) socket_set_xattrs(cfd, /* path= */ NULL, p->socket->xattr_accept);
socket_apply_socket_options(p->socket, p, cfd);
}
diff --git a/src/core/socket.h b/src/core/socket.h
index b9010bac49..d374bec9b7 100644
--- a/src/core/socket.h
+++ b/src/core/socket.h
@@ -126,6 +126,10 @@ typedef struct Socket {
char **symlinks;
+ char **xattr_entrypoint;
+ char **xattr_listen;
+ char **xattr_accept;
+
bool accept;
bool remove_on_stop;
bool writable;
diff --git a/src/libsystemd/sd-netlink/netlink-sock-diag.c b/src/libsystemd/sd-netlink/netlink-sock-diag.c
index 177be0c2bc..c76cef4632 100644
--- a/src/libsystemd/sd-netlink/netlink-sock-diag.c
+++ b/src/libsystemd/sd-netlink/netlink-sock-diag.c
@@ -40,3 +40,51 @@ int sd_sock_diag_message_new_unix(
*ret = TAKE_PTR(m);
return 0;
}
+
+int sd_sock_diag_message_new_unix_dump(
+ sd_netlink *sdnl,
+ sd_netlink_message **ret,
+ uint32_t states,
+ uint32_t show) {
+
+ _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *m = NULL;
+ int r;
+
+ assert_return(sdnl, -EINVAL);
+ assert_return(ret, -EINVAL);
+
+ r = message_new(sdnl, &m, SOCK_DIAG_BY_FAMILY, NLM_F_REQUEST | NLM_F_DUMP);
+ if (r < 0)
+ return r;
+
+ /* Unlike sd_sock_diag_message_new_unix() this requests a dump of all AF_UNIX sockets matching the
+ * specified state mask, rather than looking up a single socket by inode/cookie. The kernel's dump
+ * handler ignores udiag_ino/udiag_cookie, hence we leave them zeroed. */
+
+ *(struct unix_diag_req*) NLMSG_DATA(m->hdr) = (struct unix_diag_req) {
+ .sdiag_family = AF_UNIX,
+ .udiag_states = states,
+ .udiag_show = show,
+ };
+
+ *ret = TAKE_PTR(m);
+ return 0;
+}
+
+int sd_sock_diag_message_get_unix(sd_netlink_message *m, struct unix_diag_msg *ret) {
+ assert_return(m, -EINVAL);
+ assert_return(m->hdr, -EINVAL);
+ assert_return(ret, -EINVAL);
+
+ if (m->hdr->nlmsg_type != SOCK_DIAG_BY_FAMILY)
+ return -EINVAL;
+
+ if (NLMSG_PAYLOAD(m->hdr, 0) < sizeof(struct unix_diag_msg))
+ return -EBADMSG;
+
+ /* Reads out the fixed-size unix_diag_msg header that precedes the attributes in a reply. There's no
+ * sd-netlink attribute for this leading family header, hence we read it directly. */
+
+ *ret = *(struct unix_diag_msg*) NLMSG_DATA(m->hdr);
+ return 0;
+}
diff --git a/src/libsystemd/sd-netlink/netlink-sock-diag.h b/src/libsystemd/sd-netlink/netlink-sock-diag.h
index e12e55a285..7253193f10 100644
--- a/src/libsystemd/sd-netlink/netlink-sock-diag.h
+++ b/src/libsystemd/sd-netlink/netlink-sock-diag.h
@@ -7,3 +7,5 @@
int sd_sock_diag_socket_open(sd_netlink **ret);
int sd_sock_diag_message_new_unix(sd_netlink *sdnl, sd_netlink_message **ret, ino_t inode, uint64_t cookie, uint32_t show);
+int sd_sock_diag_message_new_unix_dump(sd_netlink *sdnl, sd_netlink_message **ret, uint32_t states, uint32_t show);
+int sd_sock_diag_message_get_unix(sd_netlink_message *m, struct unix_diag_msg *ret);
diff --git a/src/libsystemd/sd-netlink/netlink-types-sdnl.c b/src/libsystemd/sd-netlink/netlink-types-sdnl.c
index 6b72efb86b..6e08825513 100644
--- a/src/libsystemd/sd-netlink/netlink-types-sdnl.c
+++ b/src/libsystemd/sd-netlink/netlink-types-sdnl.c
@@ -18,6 +18,8 @@ static const NLAPolicy sdnl_req_policies[] = {
DEFINE_POLICY_SET(sdnl_req);
static const NLAPolicy unix_diag_msg_policies[] = {
+ [UNIX_DIAG_NAME] = BUILD_POLICY(STRING), /* Note: not NUL-terminated on the wire, read via sd_netlink_message_read_data() */
+ [UNIX_DIAG_VFS] = BUILD_POLICY_WITH_SIZE(BINARY, sizeof(struct unix_diag_vfs)),
[UNIX_DIAG_RQLEN] = BUILD_POLICY_WITH_SIZE(BINARY, sizeof(struct unix_diag_rqlen)),
};
DEFINE_POLICY_SET(unix_diag_msg);
diff --git a/src/libsystemd/sd-varlink/sd-varlink.c b/src/libsystemd/sd-varlink/sd-varlink.c
index f484c9da44..048dbb4747 100644
--- a/src/libsystemd/sd-varlink/sd-varlink.c
+++ b/src/libsystemd/sd-varlink/sd-varlink.c
@@ -39,6 +39,7 @@
#include "varlink-io.systemd.h"
#include "varlink-org.varlink.service.h"
#include "varlink-util.h"
+#include "xattr-util.h"
#define VARLINK_DEFAULT_CONNECTIONS_MAX 4096U
#define VARLINK_DEFAULT_CONNECTIONS_PER_UID_MAX 128U
@@ -154,6 +155,33 @@ static int varlink_new(sd_varlink **ret) {
return 0;
}
+static int mark_varlink_socket(int fd, const char *path, const char *role) {
+ int r;
+
+ assert(wildcard_fd_is_valid(fd));
+ assert(role);
+
+ /* We define four roles:
+ *
+ * 1. "client" → this socket was created via socket()+connect() [sockfs]
+ * 2. "server" → this socket was created via accept() [sockfs]
+ * 3. "listen" → this socket was created via socket()+listen() [sockfs]
+ * 4. "entrypoint" → this is the entrypoint socket inode [not sockfs]
+ */
+
+ r = socket_xattr_supported(); /* Let's check for the feature directly, to make use of the cache */
+ if (r < 0)
+ return r;
+ if (r == 0)
+ return -EOPNOTSUPP;
+
+ r = xsetxattr(fd, path, AT_EMPTY_PATH, "user.varlink", role);
+ if (r < 0)
+ return log_debug_errno(r, "Failed to set 'user.varlink' to '%s': %m", role);
+
+ return 0;
+}
+
_public_ int sd_varlink_connect_address(sd_varlink **ret, const char *address) {
_cleanup_(sd_varlink_unrefp) sd_varlink *v = NULL;
int r;
@@ -169,14 +197,31 @@ _public_ int sd_varlink_connect_address(sd_varlink **ret, const char *address) {
if (r < 0)
return r;
+ (void) mark_varlink_socket(v->stream.input_fd, /* path= */ NULL, "client");
+
varlink_set_state(v, VARLINK_IDLE_CLIENT);
*ret = TAKE_PTR(v);
return 0;
}
-_public_ int sd_varlink_connect_exec(sd_varlink **ret, const char *_command, char **_argv) {
+static int varlink_socketpair(int *ret_client_fd, int *ret_server_fd) {
+ assert(ret_client_fd);
+ assert(ret_server_fd);
+
_cleanup_close_pair_ int pair[2] = EBADF_PAIR;
+ if (socketpair(AF_UNIX, SOCK_STREAM|SOCK_CLOEXEC|SOCK_NONBLOCK, 0, pair) < 0)
+ return log_debug_errno(errno, "Failed to allocate AF_UNIX socket pair: %m");
+
+ (void) mark_varlink_socket(pair[0], /* path= */ NULL, "client");
+ (void) mark_varlink_socket(pair[1], /* path= */ NULL, "server");
+
+ *ret_client_fd = TAKE_FD(pair[0]);
+ *ret_server_fd = TAKE_FD(pair[1]);
+ return 0;
+}
+
+_public_ int sd_varlink_connect_exec(sd_varlink **ret, const char *_command, char **_argv) {
_cleanup_(pidref_done_sigkill_wait) PidRef pidref = PIDREF_NULL;
_cleanup_free_ char *command = NULL;
_cleanup_strv_free_ char **argv = NULL;
@@ -200,17 +245,19 @@ _public_ int sd_varlink_connect_exec(sd_varlink **ret, const char *_command, cha
log_debug("Forking off Varlink child process '%s'.", command);
- if (socketpair(AF_UNIX, SOCK_STREAM|SOCK_CLOEXEC|SOCK_NONBLOCK, 0, pair) < 0)
- return log_debug_errno(errno, "Failed to allocate AF_UNIX socket pair: %m");
+ _cleanup_close_ int client_fd = -EBADF, server_fd = -EBADF;
+ r = varlink_socketpair(&client_fd, &server_fd);
+ if (r < 0)
+ return r;
- r = fd_nonblock(pair[1], false);
+ r = fd_nonblock(server_fd, false);
if (r < 0)
return log_debug_errno(r, "Failed to disable O_NONBLOCK for varlink socket: %m");
r = pidref_safe_fork_full(
"(sd-vlexec)",
/* stdio_fds= */ NULL,
- /* except_fds= */ (int[]) { pair[1] },
+ /* except_fds= */ (int[]) { server_fd },
/* n_except_fds= */ 1,
FORK_RESET_SIGNALS|FORK_CLOSE_ALL_FDS|FORK_PACK_FDS|FORK_CLOEXEC_OFF|FORK_REOPEN_LOG|FORK_DEATHSIG_SIGTERM|FORK_RLIMIT_NOFILE_SAFE,
&pidref);
@@ -249,14 +296,14 @@ _public_ int sd_varlink_connect_exec(sd_varlink **ret, const char *_command, cha
_exit(EXIT_FAILURE);
}
- pair[1] = safe_close(pair[1]);
+ server_fd = safe_close(server_fd);
sd_varlink *v;
r = varlink_new(&v);
if (r < 0)
return log_debug_errno(r, "Failed to create varlink object: %m");
- int conn_fd = TAKE_FD(pair[0]);
+ int conn_fd = TAKE_FD(client_fd);
r = json_stream_attach_fds(&v->stream, conn_fd, conn_fd);
if (r < 0)
return r;
@@ -280,7 +327,6 @@ static int ssh_path(const char **ret) {
}
static int varlink_connect_ssh_unix(sd_varlink **ret, const char *where) {
- _cleanup_close_pair_ int pair[2] = EBADF_PAIR;
_cleanup_(pidref_done_sigkill_wait) PidRef pidref = PIDREF_NULL;
int r;
@@ -316,12 +362,14 @@ static int varlink_connect_ssh_unix(sd_varlink **ret, const char *where) {
log_debug("Forking off SSH child process '%s -W %s %s'.", ssh, p, h);
- if (socketpair(AF_UNIX, SOCK_STREAM|SOCK_CLOEXEC|SOCK_NONBLOCK, 0, pair) < 0)
- return log_debug_errno(errno, "Failed to allocate AF_UNIX socket pair: %m");
+ _cleanup_close_ int client_fd = -EBADF, server_fd = -EBADF;
+ r = varlink_socketpair(&client_fd, &server_fd);
+ if (r < 0)
+ return r;
r = pidref_safe_fork_full(
"(sd-vlssh)",
- /* stdio_fds= */ (int[]) { pair[1], pair[1], STDERR_FILENO },
+ /* stdio_fds= */ (int[]) { server_fd, server_fd, STDERR_FILENO },
/* except_fds= */ NULL,
/* n_except_fds= */ 0,
FORK_RESET_SIGNALS|FORK_CLOSE_ALL_FDS|FORK_DEATHSIG_SIGTERM|FORK_REOPEN_LOG|FORK_RLIMIT_NOFILE_SAFE|FORK_REARRANGE_STDIO,
@@ -336,14 +384,14 @@ static int varlink_connect_ssh_unix(sd_varlink **ret, const char *where) {
_exit(EXIT_FAILURE);
}
- pair[1] = safe_close(pair[1]);
+ server_fd = safe_close(server_fd);
sd_varlink *v;
r = varlink_new(&v);
if (r < 0)
return log_debug_errno(r, "Failed to create varlink object: %m");
- int conn_fd = TAKE_FD(pair[0]);
+ int conn_fd = TAKE_FD(client_fd);
r = json_stream_attach_fds(&v->stream, conn_fd, conn_fd);
if (r < 0)
return r;
@@ -562,6 +610,8 @@ _public_ int sd_varlink_connect_fd_pair(sd_varlink **ret, int input_fd, int outp
if (r < 0)
return r;
+ (void) mark_varlink_socket(v->stream.input_fd, /* path= */ NULL, "client");
+
if (override_ucred)
json_stream_set_peer_ucred(&v->stream, override_ucred);
@@ -3283,6 +3333,8 @@ _public_ int sd_varlink_server_add_connection_pair(
}
}
+ (void) mark_varlink_socket(input_fd, /* path= */ NULL, "server");
+
if (ret)
*ret = v;
@@ -3394,10 +3446,19 @@ _public_ int sd_varlink_server_listen_fd(sd_varlink_server *s, int fd) {
if (r < 0)
return r;
+ (void) mark_varlink_socket(ss->fd, /* path= */ NULL, "listen");
+
LIST_PREPEND(sockets, s->sockets, TAKE_PTR(ss));
return 0;
}
+static mode_t default_listen_mode(sd_varlink_server_flags_t flags) {
+ /* NB: we use 0644 rather than 0600 here, because it's the "w" flag that controls connect()
+ * privileges, but leaving the "r" flag on allows others to read our xattrs, which is good because it
+ * makes our sockets recognizable as varlink, even if not connectible. */
+ return (flags & (SD_VARLINK_SERVER_ROOT_ONLY|SD_VARLINK_SERVER_MYSELF_ONLY)) != 0 ? 0644 : 0666;
+}
+
_public_ int sd_varlink_server_listen_address(sd_varlink_server *s, const char *address, mode_t m) {
_cleanup_(varlink_server_socket_freep) VarlinkServerSocket *ss = NULL;
union sockaddr_union sockaddr;
@@ -3407,6 +3468,12 @@ _public_ int sd_varlink_server_listen_address(sd_varlink_server *s, const char *
assert_return(s, -EINVAL);
assert_return(address, -EINVAL);
+
+ /* NB: we resolve m being MODE_INVALID before checking SD_VARLINK_SERVER_MODE_MKDIR_0755, since that
+ * flag is not defined for MODE_INVALID (if we'd check we'd see it always set...) */
+ if (m == MODE_INVALID)
+ m = default_listen_mode(s->flags);
+
assert_return((m & ~(0777|SD_VARLINK_SERVER_MODE_MKDIR_0755)) == 0, -EINVAL);
/* Validate that the definition of our flag doesn't collide with the official mode_t bits. Thankfully
@@ -3431,6 +3498,8 @@ _public_ int sd_varlink_server_listen_address(sd_varlink_server *s, const char *
fd = fd_move_above_stdio(fd);
+ (void) mark_varlink_socket(fd, /* path= */ NULL, "listen");
+
/* See the comment in sd_varlink_server_listen_fd() */
if (FLAGS_SET(s->flags, SD_VARLINK_SERVER_FD_PASSING_INPUT_STRICT))
(void) setsockopt_int(fd, SOL_SOCKET, SO_PASSRIGHTS, FLAGS_SET(s->flags, SD_VARLINK_SERVER_ALLOW_FD_PASSING_INPUT));
@@ -3442,6 +3511,9 @@ _public_ int sd_varlink_server_listen_address(sd_varlink_server *s, const char *
if (r < 0)
return r;
+ if (path_is_absolute(address))
+ (void) mark_varlink_socket(AT_FDCWD, address, "entrypoint");
+
if (listen(fd, SOMAXCONN_DELUXE) < 0)
return -errno;
@@ -3577,7 +3649,7 @@ _public_ int sd_varlink_server_listen_auto(sd_varlink_server *s) {
if (streq(e, "-"))
r = sd_varlink_server_add_connection_stdio(s, /* ret= */ NULL);
else
- r = sd_varlink_server_listen_address(s, e, FLAGS_SET(s->flags, SD_VARLINK_SERVER_ROOT_ONLY) ? 0600 : 0666);
+ r = sd_varlink_server_listen_address(s, e, default_listen_mode(s->flags));
if (r < 0)
return r;
diff --git a/src/shared/bus-unit-util.c b/src/shared/bus-unit-util.c
index 8b618bf500..48a48f71b2 100644
--- a/src/shared/bus-unit-util.c
+++ b/src/shared/bus-unit-util.c
@@ -224,6 +224,58 @@ static int bus_append_strv_colon(sd_bus_message *m, const char *field, const cha
return bus_append_strv_full(m, field, eq, ":" WHITESPACE, EXTRACT_UNQUOTE);
}
+static int bus_append_xattr(sd_bus_message *m, const char *field, const char *eq) {
+ int r;
+
+ assert(m);
+ assert(field);
+
+ /* Sends an extended attribute assignment of the form "name=value" as an a(ss) array of one
+ * name/value pair (or as an empty array if the value is empty, which resets the list). */
+
+ r = sd_bus_message_open_container(m, 'r', "sv");
+ if (r < 0)
+ return bus_log_create_error(r);
+
+ r = sd_bus_message_append_basic(m, 's', field);
+ if (r < 0)
+ return bus_log_create_error(r);
+
+ r = sd_bus_message_open_container(m, 'v', "a(ss)");
+ if (r < 0)
+ return bus_log_create_error(r);
+
+ r = sd_bus_message_open_container(m, 'a', "(ss)");
+ if (r < 0)
+ return bus_log_create_error(r);
+
+ if (!isempty(eq)) {
+ _cleanup_free_ char *name = NULL, *value = NULL;
+
+ r = split_pair(eq, "=", &name, &value);
+ if (r < 0)
+ return log_error_errno(r, "Failed to parse extended attribute expression '%s': %m", eq);
+
+ r = sd_bus_message_append(m, "(ss)", name, value);
+ if (r < 0)
+ return bus_log_create_error(r);
+ }
+
+ r = sd_bus_message_close_container(m);
+ if (r < 0)
+ return bus_log_create_error(r);
+
+ r = sd_bus_message_close_container(m);
+ if (r < 0)
+ return bus_log_create_error(r);
+
+ r = sd_bus_message_close_container(m);
+ if (r < 0)
+ return bus_log_create_error(r);
+
+ return 1;
+}
+
static int bus_append_byte_array(sd_bus_message *m, const char *field, const void *buf, size_t n) {
int r;
@@ -2786,6 +2838,9 @@ static const BusProperty socket_properties[] = {
{ "Timestamping", bus_append_string },
{ "DeferTrigger", bus_append_string },
{ "Symlinks", bus_append_strv },
+ { "XAttrEntryPoint", bus_append_xattr },
+ { "XAttrListen", bus_append_xattr },
+ { "XAttrAccept", bus_append_xattr },
{ "SocketProtocol", bus_append_parse_ip_protocol },
{ "ListenStream", bus_append_listen },
{ "ListenDatagram", bus_append_listen },
diff --git a/src/shared/socket-label.c b/src/shared/socket-label.c
index ea1116983e..b06b00e085 100644
--- a/src/shared/socket-label.c
+++ b/src/shared/socket-label.c
@@ -14,7 +14,9 @@
#include "socket-label.h"
#include "socket-util.h"
#include "string-table.h"
+#include "strv.h"
#include "umask-util.h"
+#include "xattr-util.h"
static const char* const socket_address_bind_ipv6_only_table[_SOCKET_ADDRESS_BIND_IPV6_ONLY_MAX] = {
[SOCKET_ADDRESS_DEFAULT] = "default",
@@ -36,6 +38,32 @@ SocketAddressBindIPv6Only socket_address_bind_ipv6_only_or_bool_from_string(cons
return socket_address_bind_ipv6_only_from_string(s);
}
+int socket_set_xattrs(int fd, const char *path, char **xattrs) {
+ int r;
+
+ assert(wildcard_fd_is_valid(fd));
+
+ if (strv_isempty(xattrs))
+ return 0;
+
+ r = socket_xattr_supported();
+ if (r <= 0)
+ return r;
+
+ int c = 0;
+ STRV_FOREACH_PAIR(name, value, xattrs) {
+ r = xsetxattr(fd, path, AT_EMPTY_PATH, *name, *value);
+ if (r < 0) {
+ log_debug_errno(r, "Failed to set extended attribute '%s' on socket, ignoring: %m", *name);
+ continue;
+ }
+
+ c++;
+ }
+
+ return c;
+}
+
int socket_address_listen(
const SocketAddress *a,
int flags,
@@ -48,7 +76,9 @@ int socket_address_listen(
mode_t directory_mode,
mode_t socket_mode,
const char *selinux_label,
- const char *smack_label) {
+ const char *smack_label,
+ char **xattr_entrypoint,
+ char **xattr_listen) {
_cleanup_close_ int fd = -EBADF;
const char *p;
@@ -119,6 +149,8 @@ int socket_address_listen(
if (r < 0)
return r;
+ (void) socket_set_xattrs(fd, /* path= */ NULL, xattr_listen);
+
p = socket_address_get_path(a);
if (p) {
/* Create parents */
@@ -138,11 +170,14 @@ int socket_address_listen(
if (r < 0)
return r;
}
+
if (smack_label) {
r = mac_smack_apply(p, SMACK_ATTR_ACCESS, smack_label);
if (r < 0)
log_warning_errno(r, "Failed to apply SMACK label for socket path, ignoring: %m");
}
+
+ (void) socket_set_xattrs(AT_FDCWD, p, xattr_entrypoint);
} else {
if (bind(fd, &a->sockaddr.sa, a->size) < 0)
return -errno;
diff --git a/src/shared/socket-label.h b/src/shared/socket-label.h
index 48a500c081..15dc9d86fe 100644
--- a/src/shared/socket-label.h
+++ b/src/shared/socket-label.h
@@ -26,4 +26,8 @@ int socket_address_listen(
mode_t directory_mode,
mode_t socket_mode,
const char *selinux_label,
- const char *smack_label);
+ const char *smack_label,
+ char **xattr_entrypoint,
+ char **xattr_listen);
+
+int socket_set_xattrs(int fd, const char *path, char **xattrs);
diff --git a/src/shared/socket-netlink.c b/src/shared/socket-netlink.c
index 1112a09084..a9b3174418 100644
--- a/src/shared/socket-netlink.c
+++ b/src/shared/socket-netlink.c
@@ -198,7 +198,9 @@ int make_socket_fd(int log_level, const char* address, int type, int flags) {
0755,
0644,
/* selinux_label= */ NULL,
- /* smack_label= */ NULL);
+ /* smack_label= */ NULL,
+ /* xattr_entrypoint= */ NULL,
+ /* xattr_listen= */ NULL);
if (fd < 0 || log_get_max_level() >= log_level) {
_cleanup_free_ char *p = NULL;
diff --git a/src/systemctl/systemctl-show.c b/src/systemctl/systemctl-show.c
index edef1d2575..82b8568813 100644
--- a/src/systemctl/systemctl-show.c
+++ b/src/systemctl/systemctl-show.c
@@ -1467,6 +1467,25 @@ static int print_property(const char *name, const char *expected_value, sd_bus_m
return 1;
+ } else if (contents[0] == SD_BUS_TYPE_STRUCT_BEGIN &&
+ STR_IN_SET(name, "XAttrEntryPoint", "XAttrListen", "XAttrAccept")) {
+ const char *xname, *xvalue;
+
+ r = sd_bus_message_enter_container(m, SD_BUS_TYPE_ARRAY, "(ss)");
+ if (r < 0)
+ return bus_log_parse_error(r);
+
+ while ((r = sd_bus_message_read(m, "(ss)", &xname, &xvalue)) > 0)
+ bus_print_property_valuef(name, expected_value, flags, "%s=%s", xname, xvalue);
+ if (r < 0)
+ return bus_log_parse_error(r);
+
+ r = sd_bus_message_exit_container(m);
+ if (r < 0)
+ return bus_log_parse_error(r);
+
+ return 1;
+
} else if (contents[0] == SD_BUS_TYPE_STRUCT_BEGIN && streq(name, "TimersMonotonic")) {
const char *base;
uint64_t v, next_elapse;
diff --git a/src/test/test-bpf-restrict-fsaccess.c b/src/test/test-bpf-restrict-fsaccess.c
index 80d448815c..776d75eaec 100644
--- a/src/test/test-bpf-restrict-fsaccess.c
+++ b/src/test/test-bpf-restrict-fsaccess.c
@@ -25,6 +25,7 @@
#include
#include "bpf-restrict-fsaccess.h"
+#include "devnum-util.h"
#include "fd-util.h"
#include "log.h"
#include "string-util.h"
diff --git a/src/test/test-socket-util.c b/src/test/test-socket-util.c
index 713844b09b..038140d4c5 100644
--- a/src/test/test-socket-util.c
+++ b/src/test/test-socket-util.c
@@ -4,6 +4,7 @@
#include
#include
#include
+#include
#include
#include "alloc-util.h"
@@ -550,4 +551,16 @@ TEST(tos_to_priority) {
ASSERT_EQ(tos_to_priority(0xff), TC_PRIO_CONTROL);
}
+TEST(socket_xattr_supported) {
+ int r;
+
+ r = socket_xattr_supported();
+ ASSERT_OK(r);
+
+ log_info("Extended attributes on socket inodes supported: %s", yes_no(r));
+
+ /* A second call must agree with the first. */
+ ASSERT_EQ(socket_xattr_supported(), r);
+}
+
DEFINE_TEST_MAIN(LOG_DEBUG);
diff --git a/src/udev/udev-varlink.c b/src/udev/udev-varlink.c
index 355e5a7860..183265fc53 100644
--- a/src/udev/udev-varlink.c
+++ b/src/udev/udev-varlink.c
@@ -189,7 +189,7 @@ int manager_start_varlink_server(Manager *manager, int fd) {
return log_error_errno(r, "Failed to attach Varlink connection to event loop: %m");
if (fd < 0)
- r = sd_varlink_server_listen_address(v, UDEV_VARLINK_ADDRESS, 0600);
+ r = sd_varlink_server_listen_address(v, UDEV_VARLINK_ADDRESS, 0644);
else
r = sd_varlink_server_listen_fd(v, fd);
if (r < 0)
diff --git a/src/varlinkctl/varlinkctl.c b/src/varlinkctl/varlinkctl.c
index 94a4bc40b3..48c500f7c4 100644
--- a/src/varlinkctl/varlinkctl.c
+++ b/src/varlinkctl/varlinkctl.c
@@ -1,24 +1,33 @@
/* SPDX-License-Identifier: LGPL-2.1-or-later */
+#include
+#include
#include
#include
#include
#include "sd-daemon.h"
+#include "sd-netlink.h"
#include "sd-varlink.h"
#include "build.h"
#include "bus-util.h"
#include "chase.h"
+#include "devnum-util.h"
#include "env-util.h"
+#include "errno-list.h"
+#include "errno-util.h"
#include "escape.h"
#include "fd-util.h"
#include "fileio.h"
#include "format-table.h"
#include "format-util.h"
+#include "fs-util.h"
+#include "help-util.h"
#include "log.h"
#include "main-func.h"
#include "memfd-util.h"
+#include "netlink-sock-diag.h"
#include "options.h"
#include "pager.h"
#include "parse-argument.h"
@@ -32,6 +41,7 @@
#include "recurse-dir.h"
#include "runtime-scope.h"
#include "socket-forward.h"
+#include "socket-util.h"
#include "string-util.h"
#include "strv.h"
#include "terminal-util.h"
@@ -40,6 +50,7 @@
#include "varlink-util.h"
#include "verbs.h"
#include "version.h"
+#include "xattr-util.h"
typedef struct PushFds {
int *fds;
@@ -71,14 +82,9 @@ STATIC_DESTRUCTOR_REGISTER(arg_graceful, strv_freep);
STATIC_DESTRUCTOR_REGISTER(arg_push_fds, push_fds_done);
static int help(void) {
- _cleanup_free_ char *link = NULL;
_cleanup_(table_unrefp) Table *options = NULL, *verbs = NULL;
int r;
- r = terminal_urlify_man("varlinkctl", "1", &link);
- if (r < 0)
- return log_oom();
-
r = option_parser_get_help_table(&options);
if (r < 0)
return r;
@@ -91,18 +97,16 @@ static int help(void) {
pager_open(arg_pager_flags);
- printf("%s [OPTIONS...] COMMAND ...\n\n"
- "%sIntrospect Varlink Services.%s\n"
- "\nCommands:\n",
- program_invocation_short_name,
- ansi_highlight(),
- ansi_normal());
+ help_cmdline("[OPTIONS...] COMMAND ...");
+ help_abstract("Introspect Varlink Services.");
+
+ help_section("Commands");
r = table_print_or_warn(verbs);
if (r < 0)
return r;
- printf("\nOptions:\n");
+ help_section("Options");
r = table_print_or_warn(options);
if (r < 0)
@@ -112,7 +116,7 @@ static int help(void) {
" %s --exec call ADDRESS METHOD PARAMS -- CMDLINE…\n",
program_invocation_short_name);
- printf("\nSee the %s for details.\n", link);
+ help_man_page_reference("varlinkctl", "1");
return 0;
}
@@ -1159,6 +1163,182 @@ static int verb_list_registry(int argc, char *argv[], uintptr_t _data, void *use
return 0;
}
+VERB_NOARG(verb_list_sockets, "list-sockets", "List listening Varlink entrypoint sockets");
+static int verb_list_sockets(int argc, char *argv[], uintptr_t _data, void *userdata) {
+ int r;
+
+ assert(argc <= 1);
+
+ /* Enumerates listening, file-system bound AF_UNIX SOCK_STREAM sockets via the sock_diag netlink API,
+ * and lists those that are marked as Varlink entrypoints (i.e. carry the "user.varlink" xattr set to
+ * "entrypoint"). */
+
+ r = socket_xattr_supported();
+ if (r < 0)
+ return log_error_errno(r, "Failed to check if S_IFSOCK inodes support xattrs: %m");
+ if (r == 0)
+ return log_error_errno(SYNTHETIC_ERRNO(EOPNOTSUPP), "This kernel does not support extended attributes on socket inodes, cannot enumerate Varlink sockets.");
+
+ _cleanup_(sd_netlink_unrefp) sd_netlink *nl = NULL;
+ r = sd_sock_diag_socket_open(&nl);
+ if (r < 0)
+ return log_error_errno(r, "Failed to open sock_diag netlink socket: %m");
+
+ _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *req = NULL;
+ r = sd_sock_diag_message_new_unix_dump(nl, &req, 1U << TCP_LISTEN, UDIAG_SHOW_NAME|UDIAG_SHOW_VFS);
+ if (r < 0)
+ return log_error_errno(r, "Failed to allocate AF_UNIX socket dump request: %m");
+
+ _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *reply = NULL;
+ r = sd_netlink_call(nl, req, /* timeout= */ 0, &reply);
+ if (r < 0)
+ return log_error_errno(r, "Failed to issue AF_UNIX socket dump: %m");
+
+ _cleanup_(table_unrefp) Table *table = table_new("path", "access");
+ if (!table)
+ return log_oom();
+
+ (void) table_set_sort(table, (size_t) 0);
+
+ for (sd_netlink_message *m = reply; m; m = sd_netlink_message_next(m)) {
+
+ r = sd_netlink_message_get_errno(m);
+ if (r < 0) {
+ log_warning_errno(r, "Error in AF_UNIX socket dump entry, ignoring: %m");
+ continue;
+ }
+
+ struct unix_diag_msg udm;
+ r = sd_sock_diag_message_get_unix(m, &udm);
+ if (r < 0) {
+ log_warning_errno(r, "Failed to read AF_UNIX socket dump header, ignoring: %m");
+ continue;
+ }
+
+ /* We only care about listening stream sockets. The kernel already filtered by state, but
+ * there's no way to filter by type in the request, so we do that here (and double check the
+ * state for good measure). */
+ if (udm.udiag_type != SOCK_STREAM)
+ continue;
+ if (udm.udiag_state != TCP_LISTEN)
+ continue;
+
+ /* Read the bound name. This is not NUL terminated on the wire, hence read it as raw data. */
+ _cleanup_free_ void *name = NULL;
+ size_t name_size = 0;
+ r = sd_netlink_message_read_data(m, UNIX_DIAG_NAME, &name_size, &name);
+ if (r == -ENODATA) /* unnamed socket */
+ continue;
+ if (r < 0)
+ return log_error_errno(r, "Failed to read AF_UNIX socket name: %m");
+
+ /* Safely turn the raw, not necessarily NUL-terminated, name into a C string. This also
+ * rejects any name with embedded NUL bytes. */
+ _cleanup_free_ char *path = NULL;
+ r = make_cstring(name, name_size, MAKE_CSTRING_ALLOW_TRAILING_NUL, &path);
+ if (r < 0) {
+ log_debug_errno(r, "Failed to convert AF_UNIX socket name to string, skipping: %m");
+ continue;
+ }
+ if (!path_is_absolute(path)) {
+ log_debug("Got non-absolute AF_UNIX socket path '%s', skipping.", path);
+ continue;
+ }
+
+ /* The kernel also reports the backing VFS inode/device, but only for file-system bound
+ * sockets. We require it, both as a filter and to validate the path below. */
+ _cleanup_free_ void *vfs = NULL;
+ size_t vfs_size = 0;
+ r = sd_netlink_message_read_data(m, UNIX_DIAG_VFS, &vfs_size, &vfs);
+ if (r == -ENODATA)
+ continue; /* not fs bound */
+ if (r < 0)
+ return log_error_errno(r, "Failed to read AF_UNIX socket VFS data: %m");
+ if (vfs_size != sizeof(struct unix_diag_vfs)) {
+ log_warning("Got AF_UNIX socket VFS data of unexpected size, skipping.");
+ continue;
+ }
+ const struct unix_diag_vfs *uv = vfs;
+
+ /* Validate the path the kernel reported: open it (without following a final-component
+ * symlink), and verify it really is a socket whose inode and backing device match what
+ * netlink told us. This guards against the path having been unlinked/replaced in the
+ * meantime, so that we only ever read xattrs off the right inode. */
+ _cleanup_close_ int fd = open(path, O_PATH|O_CLOEXEC|O_NOFOLLOW);
+ if (fd < 0) {
+ log_debug_errno(errno, "Failed to open reported AF_UNIX socket path '%s', skipping: %m", path);
+ continue;
+ }
+
+ struct stat st;
+ if (fstat(fd, &st) < 0) {
+ log_debug_errno(errno, "Failed to stat reported AF_UNIX socket path '%s', skipping: %m", path);
+ continue;
+ }
+
+ if (!S_ISSOCK(st.st_mode)) {
+ log_debug("Reported AF_UNIX socket path '%s' is not a socket, skipping.", path);
+ continue;
+ }
+
+ /* the unix_diag_vfs structure only gives us 32bit inode numbers, which it truncates. hence lets truncate the value before comparison */
+ if (((st.st_ino ^ uv->udiag_vfs_ino) & UINT32_MAX) != 0) {
+ log_debug("Inode of reported AF_UNIX socket path '%s' does not match netlink data, skipping.", path);
+ continue;
+ }
+
+ /* udiag_vfs_dev carries the kernel-internal dev_t encoding, which differs from the userspace
+ * dev_t in st_dev — hence translate before comparing. */
+ if (STAT_DEV_TO_KERNEL(st.st_dev) != uv->udiag_vfs_dev) {
+ log_debug("Backing device of reported AF_UNIX socket path '%s' does not match netlink data, skipping.", path);
+ continue;
+ }
+
+ /* The path is validated now, hence we may safely read the Varlink role xattr off the fd. We
+ * only list sockets that are marked as Varlink entrypoints. */
+ _cleanup_free_ char *role = NULL;
+ r = fgetxattr_malloc(fd, "user.varlink", &role, /* ret_size= */ NULL);
+ if (r < 0) {
+ if (!ERRNO_IS_NEG_XATTR_ABSENT(r))
+ log_debug_errno(r, "Failed to read 'user.varlink' xattr of '%s', skipping: %m", path);
+ continue;
+ }
+ if (!streq(role, "entrypoint"))
+ continue;
+
+ _cleanup_free_ char *no = NULL;
+ r = access_fd(fd, W_OK);
+ if (r < 0) {
+ no = strjoin("no (", ERRNO_NAME(r), ")");
+ if (!no)
+ return log_oom();
+ }
+
+ r = table_add_many(
+ table,
+ TABLE_PATH, path,
+ TABLE_STRING, no ?: "yes",
+ TABLE_SET_COLOR, ansi_highlight_green_red(!no));
+ if (r < 0)
+ return r;
+ }
+
+ if (!table_isempty(table) || sd_json_format_enabled(arg_json_format_flags)) {
+ r = table_print_with_pager(table, arg_json_format_flags, arg_pager_flags, /* show_header= */ true);
+ if (r < 0)
+ return log_error_errno(r, "Failed to output table: %m");
+ }
+
+ if (arg_legend && !sd_json_format_enabled(arg_json_format_flags)) {
+ if (table_isempty(table))
+ printf("No sockets found.\n");
+ else
+ printf("\n%zu entrypoint sockets listed.\n", table_get_rows(table) - 1);
+ }
+
+ return 0;
+}
+
/* Build a minimal IDL from a qualified method name so that introspection works. The parsed interface is
* returned to the caller who must keep it alive for the lifetime of the server
* (sd_varlink_server_add_interface() borrows the pointer). */
diff --git a/src/vmspawn/vmspawn-varlink.c b/src/vmspawn/vmspawn-varlink.c
index 57230c8e8f..b75b8daa99 100644
--- a/src/vmspawn/vmspawn-varlink.c
+++ b/src/vmspawn/vmspawn-varlink.c
@@ -551,7 +551,7 @@ int vmspawn_varlink_setup(
if (!listen_address)
return log_oom();
- r = sd_varlink_server_listen_address(ctx->varlink_server, listen_address, 0600);
+ r = sd_varlink_server_listen_address(ctx->varlink_server, listen_address, 0644);
if (r < 0)
return log_error_errno(r, "Failed to listen on %s: %m", listen_address);
diff --git a/test/units/TEST-74-AUX-UTILS.socket-xattr.sh b/test/units/TEST-74-AUX-UTILS.socket-xattr.sh
new file mode 100755
index 0000000000..3832fb3f82
--- /dev/null
+++ b/test/units/TEST-74-AUX-UTILS.socket-xattr.sh
@@ -0,0 +1,108 @@
+#!/usr/bin/env bash
+# SPDX-License-Identifier: LGPL-2.1-or-later
+set -eux
+set -o pipefail
+
+# shellcheck source=test/units/util.sh
+. "$(dirname "$0")"/util.sh
+
+# Extended attributes on socket inodes (and hence the XAttr*= socket settings and
+# "varlinkctl list-sockets") require a sufficiently new kernel. Probe for actual
+# support and skip if unavailable, since the kernel version alone is not a reliable
+# indicator.
+if ! socket_inode_supports_user_xattrs; then
+ echo "Socket inode extended attributes unsupported on this kernel, skipping." >&2
+ exit 0
+fi
+
+UNIT_SOCKET_PATH="/run/test-socket-xattr.sock"
+RUN_SOCKET_PATH="/run/test-socket-xattr-run.sock"
+
+at_exit() {
+ set +e
+ systemctl stop test-socket-xattr.socket test-socket-xattr-run.socket
+ systemctl reset-failed test-socket-xattr-run.socket test-socket-xattr-run.service
+ rm -f /run/systemd/system/test-socket-xattr.socket
+ rm -f /run/systemd/system/test-socket-xattr.service
+ systemctl daemon-reload
+}
+trap at_exit EXIT
+
+# Read the single user.varlink extended attribute value off the given path.
+read_role() {
+ getfattr --absolute-names --only-values --name=user.varlink "$1"
+}
+
+# ------------------------------------------------------------------------------
+# 1) XAttr*= configured in a unit file on disk
+# ------------------------------------------------------------------------------
+
+# A matching .service unit is required to be able to start the .socket unit. It is
+# never actually triggered here (we only check the listening socket), so a trivial
+# service is sufficient.
+cat >/run/systemd/system/test-socket-xattr.service </run/systemd/system/test-socket-xattr.socket </dev/null
+systemctl show -P XAttrListen test-socket-xattr.socket | grep "user.varlink=listen" >/dev/null
+systemctl show -P XAttrAccept test-socket-xattr.socket | grep "user.varlink=server" >/dev/null
+
+# ------------------------------------------------------------------------------
+# 2) XAttr*= set via "systemd-run --socket-property="
+# ------------------------------------------------------------------------------
+
+rm -f "$RUN_SOCKET_PATH"
+
+# systemd-run synthesizes the matching test-socket-xattr-run.service for us.
+systemd-run \
+ --unit=test-socket-xattr-run \
+ --service-type=oneshot \
+ --remain-after-exit \
+ --socket-property=ListenStream="$RUN_SOCKET_PATH" \
+ --socket-property=SocketMode=0666 \
+ --socket-property=XAttrEntryPoint=user.varlink=entrypoint \
+ --socket-property=XAttrListen=user.varlink=listen \
+ --socket-property=XAttrAccept=user.varlink=server \
+ --socket-property=RemoveOnStop=true \
+ true
+
+systemctl cat test-socket-xattr-run.socket
+
+# The XAttr*= settings must have been serialized into the transient socket unit.
+grep "^XAttrEntryPoint=user.varlink=entrypoint$" /run/systemd/transient/test-socket-xattr-run.socket >/dev/null
+grep "^XAttrListen=user.varlink=listen$" /run/systemd/transient/test-socket-xattr-run.socket >/dev/null
+grep "^XAttrAccept=user.varlink=server$" /run/systemd/transient/test-socket-xattr-run.socket >/dev/null
+
+# And the transient socket must validate cleanly.
+systemd-analyze verify --recursive-errors=no "/run/systemd/transient/test-socket-xattr-run.socket"
+
+# Wait for the transient socket to be bound, then check the entrypoint xattr.
+test -S "$RUN_SOCKET_PATH"
+[[ "$(read_role "$RUN_SOCKET_PATH")" == "entrypoint" ]]
+
+systemctl show -P XAttrEntryPoint test-socket-xattr-run.socket | grep "user.varlink=entrypoint" >/dev/null
+systemctl show -P XAttrListen test-socket-xattr-run.socket | grep "user.varlink=listen" >/dev/null
+systemctl show -P XAttrAccept test-socket-xattr-run.socket | grep "user.varlink=server" >/dev/null
diff --git a/test/units/TEST-74-AUX-UTILS.varlinkctl-list-sockets.sh b/test/units/TEST-74-AUX-UTILS.varlinkctl-list-sockets.sh
new file mode 100755
index 0000000000..2097d00fb1
--- /dev/null
+++ b/test/units/TEST-74-AUX-UTILS.varlinkctl-list-sockets.sh
@@ -0,0 +1,76 @@
+#!/usr/bin/env bash
+# SPDX-License-Identifier: LGPL-2.1-or-later
+set -eux
+set -o pipefail
+
+# shellcheck source=test/units/util.sh
+. "$(dirname "$0")"/util.sh
+
+# "varlinkctl list-sockets" relies on extended attributes on socket inodes, which
+# require a sufficiently new kernel. Probe for actual support and skip if
+# unavailable, since the kernel version alone is not a reliable indicator.
+if ! socket_inode_supports_user_xattrs; then
+ echo "Socket inode extended attributes unsupported on this kernel, skipping." >&2
+ exit 0
+fi
+
+ENTRYPOINT_PATH="/run/test-list-sockets-entrypoint.sock"
+PLAIN_PATH="/run/test-list-sockets-plain.sock"
+
+at_exit() {
+ set +e
+ systemctl stop test-list-sockets-entrypoint.socket
+ systemctl reset-failed test-list-sockets-entrypoint.socket test-list-sockets-entrypoint.service
+ systemctl stop test-list-sockets-plain.socket
+ systemctl reset-failed test-list-sockets-plain.socket test-list-sockets-plain.service
+ rm -f "$ENTRYPOINT_PATH" "$PLAIN_PATH"
+}
+trap at_exit EXIT
+
+rm -f "$ENTRYPOINT_PATH" "$PLAIN_PATH"
+
+# A listening socket tagged as a Varlink entrypoint.
+systemd-run \
+ --unit=test-list-sockets-entrypoint \
+ --service-type=oneshot \
+ --remain-after-exit \
+ --socket-property=ListenStream="$ENTRYPOINT_PATH" \
+ --socket-property=SocketMode=0666 \
+ --socket-property=XAttrEntryPoint=user.varlink=entrypoint \
+ --socket-property=RemoveOnStop=true \
+ true
+
+# A listening socket *without* the entrypoint marker, which must be ignored.
+systemd-run \
+ --unit=test-list-sockets-plain \
+ --service-type=oneshot \
+ --remain-after-exit \
+ --socket-property=ListenStream="$PLAIN_PATH" \
+ --socket-property=SocketMode=0666 \
+ --socket-property=RemoveOnStop=true \
+ true
+
+test -S "$ENTRYPOINT_PATH"
+test -S "$PLAIN_PATH"
+
+# Plain text output should run cleanly and mention the entrypoint socket.
+varlinkctl list-sockets
+varlinkctl list-sockets | grep "$ENTRYPOINT_PATH" >/dev/null
+
+# JSON output is an array of {path, access} objects. The entrypoint socket must be
+# present...
+json="$(varlinkctl --json=short list-sockets)"
+echo "$json" | jq -e --arg p "$ENTRYPOINT_PATH" 'any(.[]; .path == $p)' >/dev/null
+# ...and carry an "access" field (either "yes" or "No (…)").
+echo "$json" | jq -e --arg p "$ENTRYPOINT_PATH" 'any(.[]; .path == $p and (.access | type == "string"))' >/dev/null
+
+# The socket without the entrypoint xattr must NOT be listed.
+(! echo "$json" | jq -e --arg p "$PLAIN_PATH" 'any(.[]; .path == $p)' >/dev/null)
+
+# Stopping the socket unit must make the entrypoint disappear from the listing again.
+systemctl stop test-list-sockets-entrypoint.socket
+test ! -S "$ENTRYPOINT_PATH"
+(! varlinkctl --json=short list-sockets | jq -e --arg p "$ENTRYPOINT_PATH" 'any(.[]; .path == $p)' >/dev/null)
+
+systemctl stop test-list-sockets-plain.socket
+test ! -S "$PLAIN_PATH"
diff --git a/test/units/util.sh b/test/units/util.sh
index 248e676eae..14bc29af11 100755
--- a/test/units/util.sh
+++ b/test/units/util.sh
@@ -251,6 +251,24 @@ cgroupfs_supports_user_xattrs() {
[[ "$(getfattr --name="$xattr" --absolute-names --only-values /sys/fs/cgroup)" -eq 254 ]]
}
+socket_inode_supports_user_xattrs() {
+ local socket xattr
+
+ # The XAttr*= socket settings and "varlinkctl list-sockets" rely on extended
+ # attributes on socket inodes. This needs a sufficiently new kernel, but the
+ # kernel version alone is not a reliable indicator: some kernels that report
+ # >= 7.0 still reject user.* xattrs on socket inodes (with EPERM/EOPNOTSUPP).
+ # Hence probe for actual support, mirroring socket_xattr_supported() in the
+ # C code, by binding a throwaway socket and trying to tag it.
+ socket="$(mktemp -u /run/socket-xattr-probe.XXXXXX.sock)"
+ xattr="user.probe_$RANDOM"
+ # shellcheck disable=SC2064
+ trap "rm -f '$socket'" RETURN
+
+ python3 -c 'import socket, sys; socket.socket(socket.AF_UNIX).bind(sys.argv[1])' "$socket" || return 1
+ setfattr --name="$xattr" --value=1 "$socket" 2>/dev/null
+}
+
tpm_has_pcr() {
local algorithm="${1:?}"
local pcr="${2:?}"
diff --git a/units/systemd-ask-password.socket b/units/systemd-ask-password.socket
index df5eaffc22..4251ecabf4 100644
--- a/units/systemd-ask-password.socket
+++ b/units/systemd-ask-password.socket
@@ -20,3 +20,6 @@ FileDescriptorName=varlink
SocketMode=0666
Accept=yes
MaxConnectionsPerSource=16
+XAttrEntryPoint=user.varlink=entrypoint
+XAttrListen=user.varlink=listen
+XAttrAccept=user.varlink=server
diff --git a/units/systemd-bootctl.socket b/units/systemd-bootctl.socket
index e720f24f54..64b42e3ce1 100644
--- a/units/systemd-bootctl.socket
+++ b/units/systemd-bootctl.socket
@@ -18,6 +18,9 @@ Before=sockets.target
ListenStream=/run/systemd/io.systemd.BootControl
Symlinks=/run/varlink/registry/io.systemd.BootControl
FileDescriptorName=varlink
-SocketMode=0600
+SocketMode=0644
Accept=yes
MaxConnectionsPerSource=16
+XAttrEntryPoint=user.varlink=entrypoint
+XAttrListen=user.varlink=listen
+XAttrAccept=user.varlink=server
diff --git a/units/systemd-creds.socket b/units/systemd-creds.socket
index 3ea3ca5b05..452c4d2bcb 100644
--- a/units/systemd-creds.socket
+++ b/units/systemd-creds.socket
@@ -20,3 +20,6 @@ FileDescriptorName=varlink
SocketMode=0666
Accept=yes
MaxConnectionsPerSource=16
+XAttrEntryPoint=user.varlink=entrypoint
+XAttrListen=user.varlink=listen
+XAttrAccept=user.varlink=server
diff --git a/units/systemd-factory-reset.socket b/units/systemd-factory-reset.socket
index 467517ec24..bba00e0fa9 100644
--- a/units/systemd-factory-reset.socket
+++ b/units/systemd-factory-reset.socket
@@ -20,6 +20,9 @@ FileDescriptorName=varlink
SocketMode=0666
Accept=yes
MaxConnectionsPerSource=16
+XAttrEntryPoint=user.varlink=entrypoint
+XAttrListen=user.varlink=listen
+XAttrAccept=user.varlink=server
[Install]
WantedBy=sockets.target
diff --git a/units/systemd-hostnamed.socket b/units/systemd-hostnamed.socket
index f84853ade8..da938ba019 100644
--- a/units/systemd-hostnamed.socket
+++ b/units/systemd-hostnamed.socket
@@ -21,3 +21,5 @@ ListenStream=/run/systemd/io.systemd.Hostname
Symlinks=/run/varlink/registry/io.systemd.Hostname
FileDescriptorName=varlink
SocketMode=0666
+XAttrEntryPoint=user.varlink=entrypoint
+XAttrListen=user.varlink=listen
diff --git a/units/systemd-imds-metrics.socket b/units/systemd-imds-metrics.socket
index f71a31e5b9..e828864010 100644
--- a/units/systemd-imds-metrics.socket
+++ b/units/systemd-imds-metrics.socket
@@ -20,3 +20,6 @@ SocketMode=0666
Accept=yes
MaxConnectionsPerSource=16
RemoveOnStop=yes
+XAttrEntryPoint=user.varlink=entrypoint
+XAttrListen=user.varlink=listen
+XAttrAccept=user.varlink=server
diff --git a/units/systemd-imdsd.socket b/units/systemd-imdsd.socket
index daeb7840b3..20d8eb3903 100644
--- a/units/systemd-imdsd.socket
+++ b/units/systemd-imdsd.socket
@@ -21,6 +21,9 @@ SocketMode=0666
Accept=yes
MaxConnectionsPerSource=16
RemoveOnStop=yes
+XAttrEntryPoint=user.varlink=entrypoint
+XAttrListen=user.varlink=listen
+XAttrAccept=user.varlink=server
# Note that this is typically pulled in automatically by
# systemd-imds-generator, but you can also enable it manually if you like.
diff --git a/units/systemd-importd.socket b/units/systemd-importd.socket
index a538ef0d0e..0fe7edce66 100644
--- a/units/systemd-importd.socket
+++ b/units/systemd-importd.socket
@@ -23,3 +23,5 @@ ListenStream=/run/systemd/io.systemd.Import
Symlinks=/run/varlink/registry/io.systemd.Import
FileDescriptorName=varlink
SocketMode=0666
+XAttrEntryPoint=user.varlink=entrypoint
+XAttrListen=user.varlink=listen
diff --git a/units/systemd-journalctl.socket b/units/systemd-journalctl.socket
index 59d0af3449..7284b539aa 100644
--- a/units/systemd-journalctl.socket
+++ b/units/systemd-journalctl.socket
@@ -19,6 +19,9 @@ ListenStream=/run/systemd/io.systemd.JournalAccess
Symlinks=/run/varlink/registry/io.systemd.JournalAccess
FileDescriptorName=varlink
SocketGroup=systemd-journal
-SocketMode=0660
+SocketMode=0664
Accept=yes
MaxConnectionsPerSource=16
+XAttrEntryPoint=user.varlink=entrypoint
+XAttrListen=user.varlink=listen
+XAttrAccept=user.varlink=server
diff --git a/units/systemd-journald-varlink@.socket b/units/systemd-journald-varlink@.socket
index e48a93c202..c8d09b93d4 100644
--- a/units/systemd-journald-varlink@.socket
+++ b/units/systemd-journald-varlink@.socket
@@ -16,3 +16,5 @@ StopWhenUnneeded=yes
Service=systemd-journald@%i.service
ListenStream=/run/systemd/journal.%i/io.systemd.journal
SocketMode=0666
+XAttrEntryPoint=user.varlink=entrypoint
+XAttrListen=user.varlink=listen
diff --git a/units/systemd-logind-varlink.socket b/units/systemd-logind-varlink.socket
index 377eac7006..83cd5ece8c 100644
--- a/units/systemd-logind-varlink.socket
+++ b/units/systemd-logind-varlink.socket
@@ -17,3 +17,5 @@ Symlinks=/run/varlink/registry/io.systemd.Login /run/varlink/registry/io.systemd
FileDescriptorName=varlink
SocketMode=0666
Service=systemd-logind.service
+XAttrEntryPoint=user.varlink=entrypoint
+XAttrListen=user.varlink=listen
diff --git a/units/systemd-machined.socket b/units/systemd-machined.socket
index 75a91bb0cc..dbabd9376c 100644
--- a/units/systemd-machined.socket
+++ b/units/systemd-machined.socket
@@ -16,3 +16,5 @@ ListenStream=/run/systemd/machine/io.systemd.Machine
Symlinks=/run/systemd/machine/io.systemd.MachineImage /run/varlink/registry/io.systemd.Machine /run/varlink/registry/io.systemd.MachineImage
FileDescriptorName=varlink
SocketMode=0666
+XAttrEntryPoint=user.varlink=entrypoint
+XAttrListen=user.varlink=listen
diff --git a/units/systemd-mountfsd.socket b/units/systemd-mountfsd.socket
index a3e19cc418..46dfb021dd 100644
--- a/units/systemd-mountfsd.socket
+++ b/units/systemd-mountfsd.socket
@@ -20,6 +20,8 @@ ListenStream=/run/systemd/io.systemd.MountFileSystem
Symlinks=/run/varlink/registry/io.systemd.MountFileSystem
FileDescriptorName=varlink
SocketMode=0666
+XAttrEntryPoint=user.varlink=entrypoint
+XAttrListen=user.varlink=listen
[Install]
WantedBy=sockets.target
diff --git a/units/systemd-mute-console.socket b/units/systemd-mute-console.socket
index 5eae6d5acd..1b7ce2bfdb 100644
--- a/units/systemd-mute-console.socket
+++ b/units/systemd-mute-console.socket
@@ -19,6 +19,9 @@ Before=shutdown.target
ListenStream=/run/systemd/io.systemd.MuteConsole
Symlinks=/run/varlink/registry/io.systemd.MuteConsole
FileDescriptorName=varlink
-SocketMode=0600
+SocketMode=0644
Accept=yes
MaxConnectionsPerSource=16
+XAttrEntryPoint=user.varlink=entrypoint
+XAttrListen=user.varlink=listen
+XAttrAccept=user.varlink=server
diff --git a/units/systemd-networkd-resolve-hook.socket b/units/systemd-networkd-resolve-hook.socket
index 07b596319b..e56697e77f 100644
--- a/units/systemd-networkd-resolve-hook.socket
+++ b/units/systemd-networkd-resolve-hook.socket
@@ -22,6 +22,8 @@ FileDescriptorName=resolve-hook
SocketMode=0666
Service=systemd-networkd.service
RemoveOnStop=yes
+XAttrEntryPoint=user.varlink=entrypoint
+XAttrListen=user.varlink=listen
[Install]
WantedBy=sockets.target
diff --git a/units/systemd-networkd-varlink-metrics.socket b/units/systemd-networkd-varlink-metrics.socket
index 562cc6b7f2..3ae15c13bc 100644
--- a/units/systemd-networkd-varlink-metrics.socket
+++ b/units/systemd-networkd-varlink-metrics.socket
@@ -20,6 +20,8 @@ ListenStream=/run/systemd/report/io.systemd.Network
FileDescriptorName=varlink-metrics
SocketMode=0666
Service=systemd-networkd.service
+XAttrEntryPoint=user.varlink=entrypoint
+XAttrListen=user.varlink=listen
[Install]
WantedBy=sockets.target
diff --git a/units/systemd-networkd-varlink.socket b/units/systemd-networkd-varlink.socket
index 1f4db858bc..264e81f602 100644
--- a/units/systemd-networkd-varlink.socket
+++ b/units/systemd-networkd-varlink.socket
@@ -21,6 +21,8 @@ Symlinks=/run/varlink/registry/io.systemd.Network
FileDescriptorName=varlink
SocketMode=0666
Service=systemd-networkd.service
+XAttrEntryPoint=user.varlink=entrypoint
+XAttrListen=user.varlink=listen
[Install]
WantedBy=sockets.target
diff --git a/units/systemd-nsresourced.socket b/units/systemd-nsresourced.socket
index 6b4a883df3..2840be43e2 100644
--- a/units/systemd-nsresourced.socket
+++ b/units/systemd-nsresourced.socket
@@ -20,6 +20,8 @@ ListenStream=/run/systemd/io.systemd.NamespaceResource
Symlinks=/run/systemd/userdb/io.systemd.NamespaceResource /run/varlink/registry/io.systemd.NamespaceResource
FileDescriptorName=varlink
SocketMode=0666
+XAttrEntryPoint=user.varlink=entrypoint
+XAttrListen=user.varlink=listen
[Install]
WantedBy=sockets.target
diff --git a/units/systemd-oomd.socket b/units/systemd-oomd.socket
index 4e24342f58..9b0f822d9e 100644
--- a/units/systemd-oomd.socket
+++ b/units/systemd-oomd.socket
@@ -22,6 +22,8 @@ ConditionPathExists=/proc/pressure/memory
ListenStream=/run/systemd/oom/io.systemd.ManagedOOM
SocketMode=0666
RemoveOnStop=yes
+XAttrEntryPoint=user.varlink=entrypoint
+XAttrListen=user.varlink=listen
[Install]
WantedBy=sockets.target
diff --git a/units/systemd-pcrextend.socket b/units/systemd-pcrextend.socket
index 0f4ab11e2f..2be4e51a25 100644
--- a/units/systemd-pcrextend.socket
+++ b/units/systemd-pcrextend.socket
@@ -19,9 +19,12 @@ ConditionSecurity=measured-os
ListenStream=/run/systemd/io.systemd.PCRExtend
Symlinks=/run/varlink/registry/io.systemd.PCRExtend
FileDescriptorName=varlink
-SocketMode=0600
+SocketMode=0644
Accept=yes
MaxConnectionsPerSource=16
+XAttrEntryPoint=user.varlink=entrypoint
+XAttrListen=user.varlink=listen
+XAttrAccept=user.varlink=server
[Install]
WantedBy=sockets.target
diff --git a/units/systemd-pcrlock.socket b/units/systemd-pcrlock.socket
index efb6818683..637462bb41 100644
--- a/units/systemd-pcrlock.socket
+++ b/units/systemd-pcrlock.socket
@@ -19,6 +19,9 @@ ConditionSecurity=measured-uki
ListenStream=/run/systemd/io.systemd.PCRLock
Symlinks=/run/varlink/registry/io.systemd.PCRLock
FileDescriptorName=varlink
-SocketMode=0600
+SocketMode=0644
Accept=yes
MaxConnectionsPerSource=16
+XAttrEntryPoint=user.varlink=entrypoint
+XAttrListen=user.varlink=listen
+XAttrAccept=user.varlink=server
diff --git a/units/systemd-repart.socket b/units/systemd-repart.socket
index ecd275414d..b79505fe07 100644
--- a/units/systemd-repart.socket
+++ b/units/systemd-repart.socket
@@ -19,6 +19,9 @@ Before=shutdown.target
ListenStream=/run/systemd/io.systemd.Repart
Symlinks=/run/varlink/registry/io.systemd.Repart
FileDescriptorName=varlink
-SocketMode=0600
+SocketMode=0644
Accept=yes
MaxConnectionsPerSource=16
+XAttrEntryPoint=user.varlink=entrypoint
+XAttrListen=user.varlink=listen
+XAttrAccept=user.varlink=server
diff --git a/units/systemd-report-basic.socket b/units/systemd-report-basic.socket
index ba5d88c8e7..f31dab50e2 100644
--- a/units/systemd-report-basic.socket
+++ b/units/systemd-report-basic.socket
@@ -18,6 +18,9 @@ SocketMode=0666
Accept=yes
MaxConnectionsPerSource=16
RemoveOnStop=yes
+XAttrEntryPoint=user.varlink=entrypoint
+XAttrListen=user.varlink=listen
+XAttrAccept=user.varlink=server
[Install]
WantedBy=sockets.target
diff --git a/units/systemd-report-cgroup.socket b/units/systemd-report-cgroup.socket
index 39a867cd40..2168129a7a 100644
--- a/units/systemd-report-cgroup.socket
+++ b/units/systemd-report-cgroup.socket
@@ -20,6 +20,9 @@ SocketMode=0666
Accept=yes
MaxConnectionsPerSource=16
RemoveOnStop=yes
+XAttrEntryPoint=user.varlink=entrypoint
+XAttrListen=user.varlink=listen
+XAttrAccept=user.varlink=server
[Install]
WantedBy=sockets.target
diff --git a/units/systemd-report-files.socket b/units/systemd-report-files.socket
index f37c508cf6..b5e6491a10 100644
--- a/units/systemd-report-files.socket
+++ b/units/systemd-report-files.socket
@@ -21,6 +21,9 @@ SocketMode=0666
Accept=yes
MaxConnectionsPerSource=16
RemoveOnStop=yes
+XAttrEntryPoint=user.varlink=entrypoint
+XAttrListen=user.varlink=listen
+XAttrAccept=user.varlink=server
[Install]
WantedBy=sockets.target
diff --git a/units/systemd-report-sign-plain.socket b/units/systemd-report-sign-plain.socket
index 4a6dea580d..7c3c501709 100644
--- a/units/systemd-report-sign-plain.socket
+++ b/units/systemd-report-sign-plain.socket
@@ -28,6 +28,9 @@ SocketMode=0666
Accept=yes
MaxConnectionsPerSource=16
RemoveOnStop=yes
+XAttrEntryPoint=user.varlink=entrypoint
+XAttrListen=user.varlink=listen
+XAttrAccept=user.varlink=server
[Install]
WantedBy=sockets.target
diff --git a/units/systemd-report-sign-tsm.socket b/units/systemd-report-sign-tsm.socket
index 1756dde8cf..5817075fa7 100644
--- a/units/systemd-report-sign-tsm.socket
+++ b/units/systemd-report-sign-tsm.socket
@@ -22,6 +22,9 @@ SocketMode=0600
Accept=yes
MaxConnectionsPerSource=16
RemoveOnStop=yes
+XAttrEntryPoint=user.varlink=entrypoint
+XAttrListen=user.varlink=listen
+XAttrAccept=user.varlink=server
[Install]
WantedBy=sockets.target
diff --git a/units/systemd-resolved-monitor.socket b/units/systemd-resolved-monitor.socket
index 3674a1f876..9bb4dd646f 100644
--- a/units/systemd-resolved-monitor.socket
+++ b/units/systemd-resolved-monitor.socket
@@ -20,6 +20,8 @@ ListenStream=/run/systemd/resolve/io.systemd.Resolve.Monitor
Symlinks=/run/varlink/registry/io.systemd.Resolve.Monitor
FileDescriptorName=varlink-monitor
SocketMode=0666
+XAttrEntryPoint=user.varlink=entrypoint
+XAttrListen=user.varlink=listen
[Install]
WantedBy=sockets.target
diff --git a/units/systemd-resolved-varlink.socket b/units/systemd-resolved-varlink.socket
index a570168373..65593df1d0 100644
--- a/units/systemd-resolved-varlink.socket
+++ b/units/systemd-resolved-varlink.socket
@@ -20,6 +20,8 @@ ListenStream=/run/systemd/resolve/io.systemd.Resolve
Symlinks=/run/varlink/registry/io.systemd.Resolve
FileDescriptorName=varlink
SocketMode=0666
+XAttrEntryPoint=user.varlink=entrypoint
+XAttrListen=user.varlink=listen
[Install]
WantedBy=sockets.target
diff --git a/units/systemd-storage-block.socket b/units/systemd-storage-block.socket
index 1d18b481a3..c8097e705b 100644
--- a/units/systemd-storage-block.socket
+++ b/units/systemd-storage-block.socket
@@ -19,6 +19,9 @@ FileDescriptorName=varlink
SocketMode=0666
Accept=yes
MaxConnectionsPerSource=16
+XAttrEntryPoint=user.varlink=entrypoint
+XAttrListen=user.varlink=listen
+XAttrAccept=user.varlink=server
[Install]
WantedBy=sockets.target
diff --git a/units/systemd-storage-fs.socket b/units/systemd-storage-fs.socket
index c83cf0a11f..d34e39d059 100644
--- a/units/systemd-storage-fs.socket
+++ b/units/systemd-storage-fs.socket
@@ -20,6 +20,9 @@ FileDescriptorName=varlink
SocketMode=0666
Accept=yes
MaxConnectionsPerSource=16
+XAttrEntryPoint=user.varlink=entrypoint
+XAttrListen=user.varlink=listen
+XAttrAccept=user.varlink=server
[Install]
WantedBy=sockets.target
diff --git a/units/systemd-sysext.socket b/units/systemd-sysext.socket
index 61d7268f37..209592134b 100644
--- a/units/systemd-sysext.socket
+++ b/units/systemd-sysext.socket
@@ -22,6 +22,9 @@ FileDescriptorName=varlink
SocketMode=0666
Accept=yes
MaxConnectionsPerSource=16
+XAttrEntryPoint=user.varlink=entrypoint
+XAttrListen=user.varlink=listen
+XAttrAccept=user.varlink=server
[Install]
WantedBy=sockets.target
diff --git a/units/systemd-udevd-varlink.socket b/units/systemd-udevd-varlink.socket
index c2b7652e5e..b1094b9c9b 100644
--- a/units/systemd-udevd-varlink.socket
+++ b/units/systemd-udevd-varlink.socket
@@ -19,6 +19,8 @@ Service=systemd-udevd.service
ListenStream=/run/udev/io.systemd.Udev
Symlinks=/run/varlink/registry/io.systemd.Udev
FileDescriptorName=varlink
-SocketMode=0600
+SocketMode=0644
RemoveOnStop=yes
DeferTrigger=yes
+XAttrEntryPoint=user.varlink=entrypoint
+XAttrListen=user.varlink=listen
diff --git a/units/systemd-userdbd.socket b/units/systemd-userdbd.socket
index 6793d1b41d..83f3d7e560 100644
--- a/units/systemd-userdbd.socket
+++ b/units/systemd-userdbd.socket
@@ -19,6 +19,8 @@ Symlinks=/run/systemd/userdb/io.systemd.NameServiceSwitch /run/systemd/userdb/io
FileDescriptorName=varlink
SocketMode=0666
RemoveOnStop=yes
+XAttrEntryPoint=user.varlink=entrypoint
+XAttrListen=user.varlink=listen
[Install]
WantedBy=sockets.target
diff --git a/units/user/systemd-ask-password.socket b/units/user/systemd-ask-password.socket
index 56492fae54..13a2379557 100644
--- a/units/user/systemd-ask-password.socket
+++ b/units/user/systemd-ask-password.socket
@@ -19,3 +19,6 @@ Symlinks=%t/varlink/registry/io.systemd.AskPassword
FileDescriptorName=varlink
SocketMode=0600
Accept=yes
+XAttrEntryPoint=user.varlink=entrypoint
+XAttrListen=user.varlink=listen
+XAttrAccept=user.varlink=server
diff --git a/units/user/systemd-importd.socket b/units/user/systemd-importd.socket
index 67ea62bdb0..35108341f5 100644
--- a/units/user/systemd-importd.socket
+++ b/units/user/systemd-importd.socket
@@ -17,3 +17,5 @@ ListenStream=%t/systemd/io.systemd.Import
Symlinks=%t/varlink/registry/io.systemd.Import
FileDescriptorName=varlink
SocketMode=0600
+XAttrEntryPoint=user.varlink=entrypoint
+XAttrListen=user.varlink=listen
diff --git a/units/user/systemd-journalctl.socket b/units/user/systemd-journalctl.socket
index b8a504f0f3..a711ca2127 100644
--- a/units/user/systemd-journalctl.socket
+++ b/units/user/systemd-journalctl.socket
@@ -17,3 +17,6 @@ Symlinks=%t/varlink/registry/io.systemd.JournalAccess
FileDescriptorName=varlink
SocketMode=0600
Accept=yes
+XAttrEntryPoint=user.varlink=entrypoint
+XAttrListen=user.varlink=listen
+XAttrAccept=user.varlink=server
diff --git a/units/user/systemd-machined.socket b/units/user/systemd-machined.socket
index 17e552a7dc..ffb4ff1ce3 100644
--- a/units/user/systemd-machined.socket
+++ b/units/user/systemd-machined.socket
@@ -16,3 +16,5 @@ ListenStream=%t/systemd/machine/io.systemd.Machine
Symlinks=%t/systemd/machine/io.systemd.MachineImage %t/varlink/registry/io.systemd.Machine %t/varlink/registry/io.systemd.MachineImage
FileDescriptorName=varlink
SocketMode=0600
+XAttrEntryPoint=user.varlink=entrypoint
+XAttrListen=user.varlink=listen
diff --git a/units/user/systemd-storage-fs.socket b/units/user/systemd-storage-fs.socket
index fa8018b2e8..23af4d79e6 100644
--- a/units/user/systemd-storage-fs.socket
+++ b/units/user/systemd-storage-fs.socket
@@ -18,6 +18,9 @@ FileDescriptorName=varlink
SocketMode=0600
Accept=yes
MaxConnectionsPerSource=16
+XAttrEntryPoint=user.varlink=entrypoint
+XAttrListen=user.varlink=listen
+XAttrAccept=user.varlink=server
[Install]
WantedBy=sockets.target