1
0
mirror of https://github.com/systemd/systemd synced 2026-03-14 09:04:47 +01:00

Compare commits

...

22 Commits

Author SHA1 Message Date
Yu Watanabe
b3d73bc68e bash-completion/vmspawn: add -x/--ephemeral
Follow-up for 6e67fc2938ad690a065a984c5813c6b5c0e48806.
2026-02-04 11:22:27 +01:00
gvenugo3
cf27c70d70 networkd-json: add human-readable address strings for IP addresses
Add JSON_BUILD_PAIR_IN_ADDR_WITH_STRING, JSON_BUILD_PAIR_IN6_ADDR_WITH_STRING,
and JSON_BUILD_PAIR_IN4_ADDR_WITH_STRING macros (plus _NON_NULL variants) that
emit both the binary address array and a human-readable string representation.

For example, an address field now emits both:
  "Address": [192, 168, 1, 1]
  "AddressString": "192.168.1.1"

This improves usability of the JSON/Varlink output for debugging and tooling
that consumes networkd state.

Updated networkd-json.c to use these new macros for addresses, neighbors,
nexthops, routes, routing policy rules, DNS servers, NTP servers, SIP servers,
domains, DHCPv6 prefixes, and link-local addresses.

Also updated the Varlink interface definitions to include the new string fields.
2026-02-04 10:34:30 +01:00
Lennart Poettering
13d82876f5
sysext: Skip refresh if no changes are found (#39980)
When the extensions for the final system are already set up from the
    initrd we should avoid disrupting the boot process with the remount
    (which currently isn't atomic) and the daemon reload for
systemd-confext and systemd-sysext. Similarly, when sysupdate ran and
updated extensions it's best to avoid the remount and daemon reload if
    no changes are found.
    To do this, encode the current extension state in more detail than
    before where only the names of the extensions where encoded in the
overlay mount. This can also be used to provide more details about the
    extension origin in "systemd-sysext status (--json=)". During the
refresh add a check whether the old state matches the new state and in
    this case skip the refresh unless the user provides a flag to always
    refresh. Besides the extension name and the resolved path the best
method for identification is the verity hash but that is not available
for plain image files or directories. Therefore, also include data to
check for file/directory replacements. The creation/modification times
are not always real on reproducible images or extracted archive content.
    The file handle together with the unique mount ID is the next best
identifier we can use when we have no verity hash. Fall back to an inode
when we get no handle. With the creation/modification time and the path
this should be good enough. Using a unique mount ID is important (with
a fallback to the regular non-unique mount ID) instead of st_dev because
    st_dev gets reused too easily, e.g., by a loop device mount and the
mount ID helps to catch this. For the mount ID to be valid it has to be
resolved before we enter the new mount namespace. Thus, it gets provided
    by the image dissect logic and handed over to the sysext subprocess
    which runs in a new mount namespace.
    Luckily, we can rule out online modification of directories or image
files because this is anyway not well supported with overlay mounts, so
we don't do a file checksum nor do we recurse into a directory to look
    for the most recently touched files.  But, as said, with the
    always-refresh flag one can force a reload.
2026-02-04 10:30:10 +01:00
Yu Watanabe
89591e04a2
resolvectl: re-implement normal status output using varlink (#40529)
Currently, `resolvectl status` (and other status commands like
`resolvectl dns $iface`) uses varlink when asked to print JSON, but uses
dbus otherwise. This PR re-implements the regular status output to use
varlink instead of dbus.

This both simplifies the `resolvectl status` code, and is an incremental
step in replacing dbus usage with varlink throughout `resolvectl`.

The first several commits move `dns-configuration.[ch]` from
`src/network/wait-online` to `src/shared`, and complete the JSON
dispatching so that all fields are available in a concrete type. The
next few commits re-implement global, link, and delegate status output.
Finally, the last commits consolidate and simplify the status logic
since varlink and the `DNSConfiguration` type can be used in all cases.
2026-02-04 15:47:02 +09:00
Kai Lueke
23115eeaf1 sysext: Skip refresh if no changes are found
When the extensions for the final system are already set up from the
initrd we should avoid disrupting the boot process with the remount
(which currently isn't atomic) and the daemon reload for
systemd-confext and systemd-sysext. Similarly, when sysupdate ran and
updated extensions it's best to avoid the remount and daemon reload if
no changes are found.
To do this, encode the current extension state in more detail than
before where only the names of the extensions where encoded in the
overlay mount. This can also be used to provide more details about the
extension origin in "systemd-sysext status (--json=)". During the
refresh add a check whether the old state matches the new state and in
this case skip the refresh unless the user provides a flag to always
refresh. Besides the extension name and the resolved path the best
method for identification is the verity hash but that is not available
for plain image files or directories. Therefore, also include data to
check for file/directory replacements. The creation/modification times
are not always real on reproducible images or extracted archive content.
The file handle together with the unique mount ID is the next best
identifier we can use when we have no verity hash. Fall back to an inode
when we get no handle. With the creation/modification time and the path
this should be good enough. Using a unique mount ID is important (with
a fallback to the regular non-unique mount ID) instead of st_dev because
st_dev gets reused too easily, e.g., by a loop device mount and the
mount ID helps to catch this. For the mount ID to be valid it has to be
resolved before we enter the new mount namespace. Thus, it gets provided
by the image dissect logic and handed over to the sysext subprocess
which runs in a new mount namespace.
Luckily, we can rule out online modification of directories or image
files because this is anyway not well supported with overlay mounts, so
we don't do a file checksum nor do we recurse into a directory to look
for the most recently touched files.  But, as said, with the
always-refresh flag one can force a reload.
2026-02-04 00:05:24 +01:00
Kai Lueke
2e4c2f8df7 sysext: Create mutable directory with the right mode
When the mutable directory didn't exist but gets created with
--mutable=yes then it used to get mode 700 and later it got patched by
a chmod because it is the top layer and must match the target hierarchy.
This meant one could not call the function to resolve the mutable
directory twice before the mount because it has a check for a proper
mode when the directory exists which is the case for the second call.
Also, this resulted in /var/lib/extensions.mutable getting created with
mode 700 which is not really required.

Don't rely on the chmod for the upper dir but directly create the
directory with the right mode by first creating all missing directories
with 755 as a sane default and then changing the mode as needed for the
mutable directory.
2026-02-03 23:55:36 +01:00
Nick Rosbrook
a4750a6612 resolvectl: add print_configuration() to consolidate status logic
There is a lot of duplication across the functions for printing global,
link, and delegate output. Since these all use a common DNSConfiguration
type now, the logic can be consolidated.

Adapt printing and format helper functions to accept a DNSConfiguration
parameter to appropriately format output. Then, add print_configuration(),
a single function that prints the appropriate status output based on the
contents of DNSConfiguration.
2026-02-03 15:07:41 -05:00
Nick Rosbrook
480ebdfddd resolvectl: consolidate JSON and regular status output paths
Now that varlink is always used for status output, consolidate the JSON
and regular status code paths. Add status_full(), which is the single
point that calls DumpDNSConfiguration, filters based on link names and
mode if needed, and prints the output in the appropriate format.

This simplifies status_ifindex(), and removes the need for a separate
status_json().
2026-02-03 15:07:41 -05:00
Nick Rosbrook
67746cd3b2 resolvectl: use varlink to report delegate status
This is an incremental commit to replacing dbus usage with varlink for
all status output.

Re-implement regular delegate status output using the DumpDNSConfiguration
varlink method. And, remove the DelegateInfo type and associated functions
which were used to map dbus properties.

Since this is the last type of status output to be switched to varlink,
lots of other code becomes dead with these changes, e.g.
read_dns_server_one(), map_dns_servers_internal(), etc., so remove those
too. Finally, status_all() no longer needs sd_bus, so drop that
parameter.
2026-02-03 15:07:41 -05:00
Nick Rosbrook
d076d0f94f resolvectl: use varlink to report link status
This is an incremental commit to replacing dbus usage with varlink for
all status output.

Re-implement regular link status output using the DumpDNSConfiguration
varlink method. And, remove the LinkInfo type and associated functions
which were used to map dbus properties.
2026-02-03 15:07:41 -05:00
Nick Rosbrook
a56366182e resolvectl: use varlink to report global status
This is an incremental commit to replacing dbus usage with varlink for
all status output.

Re-implement regular global status output using the DumpDNSConfiguration
varlink method. And, remove the GlobalInfo type and associated functions
which were used to map dbus properties.
2026-02-03 15:07:41 -05:00
Nick Rosbrook
7bfc82bf5a dns-configuration: use OrderedSet for servers and domains
This only has a small effect, which mostly matters for generating status
output. It ensures that the order of servers and domains displayed
matches the order they are stored on the daemon side, which keeps the
new resolvectl output consistent with the old.
2026-02-03 15:07:41 -05:00
Nick Rosbrook
8894f29a4e dns-configuration: add DNSSEC support boolean to DNSConfiguration
This is one of several commits to dispatch all fields of the
DNSConfiguration JSON.
2026-02-03 15:07:41 -05:00
Nick Rosbrook
d9757721ab dns-configuration: add in_addr_union field to DNSServer type
This makes it easier to use DNSServer with helpers from in-addr-util.h.
2026-02-03 15:07:41 -05:00
Nick Rosbrook
8731d33b58 dns-configuration: dispatch delegate name to DNSConfiguration
This is one of several commits to dispatch all fields of the
DNSConfiguration JSON.
2026-02-03 15:07:41 -05:00
Nick Rosbrook
3afb9c20b6 dns-configuration: dispatch scopes to DNSConfiguration
This is one of several commits to dispatch all fields of the
DNSConfiguration JSON.

Introduce a DNSScope type, and all required JSON dispatching helpers.
2026-02-03 15:07:41 -05:00
Nick Rosbrook
e90180f50c dns-configuration: dispatch resolv conf mode to DNSConfiguration
This is one of several commits to dispatch all fields of the
DNSConfiguration JSON.
2026-02-03 15:07:41 -05:00
Nick Rosbrook
4a8f4efd44 dns-configuration: dispatch negative trust anchors to DNSConfiguration
This is one of several commits to dispatch all fields of the
DNSConfiguration JSON.
2026-02-03 15:07:41 -05:00
Nick Rosbrook
d3b60567d7 dns-configuration: dispatch fallback servers to DNSConfiguration
This is one of several commits to dispatch all fields of the
DNSConfiguration JSON.
2026-02-03 15:07:41 -05:00
Nick Rosbrook
fb0bd15d15 dns-configuration: dispatch all protocol modes to DNSConfiguration
This is one of several commits to dispatch all fields of the
DNSConfiguration JSON.
2026-02-03 15:07:41 -05:00
Nick Rosbrook
271f7630a2 shared: move wait-online's dns-configuration.[ch] to shared 2026-02-03 15:07:41 -05:00
Nick Rosbrook
9b2f263f30 resolve: add DNSConfiguration field to indicate DNSSEC support
The varlink API is currently missing the "DNSSEC supported" field, which
is required for to re-implement the existing resolvectl status output
with varlink instead of dbus.

Add this field to DNSConfiguration.
2026-02-03 15:07:41 -05:00
23 changed files with 1447 additions and 1228 deletions

View File

@ -371,6 +371,19 @@
<xi:include href="version-info.xml" xpointer="v248"/></listitem> <xi:include href="version-info.xml" xpointer="v248"/></listitem>
</varlistentry> </varlistentry>
<varlistentry>
<term><option>--always-refresh=yes|no</option></term>
<listitem><para>When refreshing system extensions on <filename>/usr/</filename> and <filename>/opt/</filename>
for sysext and <filename>/etc/</filename> for confext, ignore when the existing merged extensions
already match what would be merged.
By default the refresh is skipped when no changes are found. Note that changes done to an extension
directory while it's merged are ignored without this flag (unless an other extension got changed).
Note that changing the contents while merged is also undefined behavior in overlayfs.</para>
<xi:include href="version-info.xml" xpointer="v260"/></listitem>
</varlistentry>
<varlistentry> <varlistentry>
<term><option>--image-policy=<replaceable>policy</replaceable></option></term> <term><option>--image-policy=<replaceable>policy</replaceable></option></term>

View File

@ -36,6 +36,7 @@ _systemd-sysext() {
[ARG]='--root [ARG]='--root
--json --json
--noexec --noexec
--always-refresh
--image-policy --image-policy
--mutable' --mutable'
) )

View File

@ -29,7 +29,7 @@ _systemd_vmspawn() {
local i verb comps local i verb comps
local -A OPTS=( local -A OPTS=(
[STANDALONE]='-h --help --version -q --quiet --no-pager -n --network-tap --network-user-mode --user --system' [STANDALONE]='-h --help --version -q --quiet --no-pager -n --network-tap --network-user-mode --user --system -x --ephemeral'
[PATH]='-D --directory -i --image --linux --initrd --extra-drive --forward-journal' [PATH]='-D --directory -i --image --linux --initrd --extra-drive --forward-journal'
[BOOL]='--kvm --vsock --tpm --secure-boot --discard-disk --register --pass-ssh-key' [BOOL]='--kvm --vsock --tpm --secure-boot --discard-disk --register --pass-ssh-key'
[FIRMWARE]='--firmware' [FIRMWARE]='--firmware'

View File

@ -50,12 +50,13 @@ int name_to_handle_at_loop(
const char *path, const char *path,
struct file_handle **ret_handle, struct file_handle **ret_handle,
int *ret_mnt_id, int *ret_mnt_id,
uint64_t *ret_unique_mnt_id,
int flags) { int flags) {
size_t n = ORIGINAL_MAX_HANDLE_SZ; size_t n = ORIGINAL_MAX_HANDLE_SZ;
assert(fd >= 0 || fd == AT_FDCWD); assert(fd >= 0 || fd == AT_FDCWD);
assert((flags & ~(AT_SYMLINK_FOLLOW|AT_EMPTY_PATH|AT_HANDLE_FID)) == 0); assert((flags & ~(AT_SYMLINK_FOLLOW|AT_EMPTY_PATH|AT_HANDLE_FID|AT_HANDLE_MNT_ID_UNIQUE)) == 0);
/* We need to invoke name_to_handle_at() in a loop, given that it might return EOVERFLOW when the specified /* We need to invoke name_to_handle_at() in a loop, given that it might return EOVERFLOW when the specified
* buffer is too small. Note that in contrast to what the docs might suggest, MAX_HANDLE_SZ is only good as a * buffer is too small. Note that in contrast to what the docs might suggest, MAX_HANDLE_SZ is only good as a
@ -71,7 +72,8 @@ int name_to_handle_at_loop(
for (;;) { for (;;) {
_cleanup_free_ struct file_handle *h = NULL; _cleanup_free_ struct file_handle *h = NULL;
int mnt_id = -1; int mnt_id = -1, r;
uint64_t unique_mnt_id = 0;
h = malloc0(offsetof(struct file_handle, f_handle) + n); h = malloc0(offsetof(struct file_handle, f_handle) + n);
if (!h) if (!h)
@ -79,11 +81,18 @@ int name_to_handle_at_loop(
h->handle_bytes = n; h->handle_bytes = n;
if (name_to_handle_at(fd, path, h, &mnt_id, flags) >= 0) { if (FLAGS_SET(flags, AT_HANDLE_MNT_ID_UNIQUE))
/* The kernel will still use this as uint64_t pointer */
r = name_to_handle_at(fd, path, h, (int *) &unique_mnt_id, flags);
else
r = name_to_handle_at(fd, path, h, &mnt_id, flags);
if (r >= 0) {
if (ret_handle) if (ret_handle)
*ret_handle = TAKE_PTR(h); *ret_handle = TAKE_PTR(h);
if (ret_unique_mnt_id)
*ret_unique_mnt_id = unique_mnt_id;
if (ret_mnt_id) if (ret_mnt_id)
*ret_mnt_id = mnt_id; *ret_mnt_id = mnt_id;
@ -92,12 +101,15 @@ int name_to_handle_at_loop(
if (errno != EOVERFLOW) if (errno != EOVERFLOW)
return -errno; return -errno;
if (!ret_handle && ret_mnt_id && mnt_id >= 0) { if (!ret_handle && ((ret_mnt_id && mnt_id >= 0) || (ret_unique_mnt_id && unique_mnt_id > 0))) {
/* As it appears, name_to_handle_at() fills in mnt_id even when it returns EOVERFLOW when the /* As it appears, name_to_handle_at() fills in mnt_id even when it returns EOVERFLOW when the
* buffer is too small, but that's undocumented. Hence, let's make use of this if it appears to * buffer is too small, but that's undocumented. Hence, let's make use of this if it appears to
* be filled in, and the caller was interested in only the mount ID an nothing else. */ * be filled in, and the caller was interested in only the mount ID an nothing else. */
if (ret_unique_mnt_id)
*ret_unique_mnt_id = unique_mnt_id;
if (ret_mnt_id)
*ret_mnt_id = mnt_id; *ret_mnt_id = mnt_id;
return 0; return 0;
} }
@ -132,11 +144,55 @@ int name_to_handle_at_try_fid(
* we'll try without the flag, in order to support older kernels that didn't have AT_HANDLE_FID * we'll try without the flag, in order to support older kernels that didn't have AT_HANDLE_FID
* (i.e. older than Linux 6.5). */ * (i.e. older than Linux 6.5). */
r = name_to_handle_at_loop(fd, path, ret_handle, ret_mnt_id, flags | AT_HANDLE_FID); r = name_to_handle_at_loop(fd, path, ret_handle, ret_mnt_id, /* ret_unique_mnt_id= */ NULL, flags | AT_HANDLE_FID);
if (r >= 0 || is_name_to_handle_at_fatal_error(r)) if (r >= 0 || is_name_to_handle_at_fatal_error(r))
return r; return r;
return name_to_handle_at_loop(fd, path, ret_handle, ret_mnt_id, flags & ~AT_HANDLE_FID); return name_to_handle_at_loop(fd, path, ret_handle, ret_mnt_id, /* ret_unique_mnt_id= */ NULL, flags & ~AT_HANDLE_FID);
}
int name_to_handle_at_try_unique_mntid_fid(
int fd,
const char *path,
struct file_handle **ret_handle,
uint64_t *ret_mnt_id,
int flags) {
int mnt_id = -1, r;
assert(fd >= 0 || fd == AT_FDCWD);
/* First issues name_to_handle_at() with AT_HANDLE_MNT_ID_UNIQUE and AT_HANDLE_FID.
* If this fails and this is not a fatal error we'll try without the
* AT_HANDLE_MNT_ID_UNIQUE flag because it's only available from Linux 6.12 onwards. */
r = name_to_handle_at_loop(fd, path, ret_handle, /* ret_mnt_id= */ NULL, ret_mnt_id, flags | AT_HANDLE_MNT_ID_UNIQUE | AT_HANDLE_FID);
if (r >= 0 || is_name_to_handle_at_fatal_error(r))
return r;
flags &= ~AT_HANDLE_MNT_ID_UNIQUE;
/* Then issues name_to_handle_at() with AT_HANDLE_FID. If this fails and this is not a fatal error
* we'll try without the flag, in order to support older kernels that didn't have AT_HANDLE_FID
* (i.e. older than Linux 6.5). */
r = name_to_handle_at_loop(fd, path, ret_handle, &mnt_id, /* ret_unique_mnt_id= */ NULL, flags | AT_HANDLE_FID);
if (r < 0 && is_name_to_handle_at_fatal_error(r))
return r;
if (r >= 0) {
if (ret_mnt_id && mnt_id >= 0) {
/* See if we can do better because statx can do unique mount IDs since Linux 6.8
* and only if this doesn't work we use the non-unique mnt_id as returned. */
if (path_get_unique_mnt_id_at(fd, path, ret_mnt_id) < 0)
*ret_mnt_id = mnt_id;
}
return r;
}
r = name_to_handle_at_loop(fd, path, ret_handle, &mnt_id, /* ret_unique_mnt_id= */ NULL, flags & ~AT_HANDLE_FID);
if (ret_mnt_id && mnt_id >= 0)
*ret_mnt_id = mnt_id;
return r;
} }
int name_to_handle_at_u64(int fd, const char *path, uint64_t *ret) { int name_to_handle_at_u64(int fd, const char *path, uint64_t *ret) {
@ -147,7 +203,7 @@ int name_to_handle_at_u64(int fd, const char *path, uint64_t *ret) {
/* This provides the first 64bit of the file handle. */ /* This provides the first 64bit of the file handle. */
r = name_to_handle_at_loop(fd, path, &h, /* ret_mnt_id= */ NULL, /* flags= */ 0); r = name_to_handle_at_loop(fd, path, &h, /* ret_mnt_id= */ NULL, /* ret_unique_mnt_id= */ NULL, /* flags= */ 0);
if (r < 0) if (r < 0)
return r; return r;
if (h->handle_bytes < sizeof(uint64_t)) if (h->handle_bytes < sizeof(uint64_t))
@ -171,6 +227,22 @@ bool file_handle_equal(const struct file_handle *a, const struct file_handle *b)
return memcmp_nn(a->f_handle, a->handle_bytes, b->f_handle, b->handle_bytes) == 0; return memcmp_nn(a->f_handle, a->handle_bytes, b->f_handle, b->handle_bytes) == 0;
} }
struct file_handle* file_handle_dup(const struct file_handle *fh) {
_cleanup_free_ struct file_handle *fh_copy = NULL;
assert(fh);
fh_copy = malloc0(offsetof(struct file_handle, f_handle) + fh->handle_bytes);
if (!fh_copy)
return NULL;
fh_copy->handle_bytes = fh->handle_bytes;
fh_copy->handle_type = fh->handle_type;
memcpy(fh_copy->f_handle, fh->f_handle, fh->handle_bytes);
return TAKE_PTR(fh_copy);
}
int is_mount_point_at(int dir_fd, const char *path, int flags) { int is_mount_point_at(int dir_fd, const char *path, int flags) {
int r; int r;
@ -259,6 +331,28 @@ int path_get_mnt_id_at(int dir_fd, const char *path, int *ret) {
return 0; return 0;
} }
int path_get_unique_mnt_id_at(int dir_fd, const char *path, uint64_t *ret) {
struct statx sx;
assert(dir_fd >= 0 || dir_fd == AT_FDCWD);
assert(ret);
if (statx(dir_fd,
strempty(path),
(isempty(path) ? AT_EMPTY_PATH : AT_SYMLINK_NOFOLLOW) |
AT_NO_AUTOMOUNT | /* don't trigger automounts, mnt_id is a local concept */
AT_STATX_DONT_SYNC, /* don't go to the network, mnt_id is a local concept */
STATX_MNT_ID_UNIQUE,
&sx) < 0)
return -errno;
if (!FLAGS_SET(sx.stx_mask, STATX_MNT_ID_UNIQUE))
return -EOPNOTSUPP;
*ret = sx.stx_mnt_id;
return 0;
}
bool fstype_is_network(const char *fstype) { bool fstype_is_network(const char *fstype) {
const char *x; const char *x;

View File

@ -34,8 +34,9 @@
bool is_name_to_handle_at_fatal_error(int err); bool is_name_to_handle_at_fatal_error(int err);
int name_to_handle_at_loop(int fd, const char *path, struct file_handle **ret_handle, int *ret_mnt_id, int flags); int name_to_handle_at_loop(int fd, const char *path, struct file_handle **ret_handle, int *ret_mnt_id, uint64_t *ret_unique_mnt_id, int flags);
int name_to_handle_at_try_fid(int fd, const char *path, struct file_handle **ret_handle, int *ret_mnt_id, int flags); int name_to_handle_at_try_fid(int fd, const char *path, struct file_handle **ret_handle, int *ret_mnt_id, int flags);
int name_to_handle_at_try_unique_mntid_fid(int fd, const char *path, struct file_handle **ret_handle, uint64_t *ret_mnt_id, int flags);
int name_to_handle_at_u64(int fd, const char *path, uint64_t *ret); int name_to_handle_at_u64(int fd, const char *path, uint64_t *ret);
static inline int path_to_handle_u64(const char *path, uint64_t *ret) { static inline int path_to_handle_u64(const char *path, uint64_t *ret) {
return name_to_handle_at_u64(AT_FDCWD, path, ret); return name_to_handle_at_u64(AT_FDCWD, path, ret);
@ -45,11 +46,13 @@ static inline int fd_to_handle_u64(int fd, uint64_t *ret) {
} }
bool file_handle_equal(const struct file_handle *a, const struct file_handle *b); bool file_handle_equal(const struct file_handle *a, const struct file_handle *b);
struct file_handle* file_handle_dup(const struct file_handle *fh);
int path_get_mnt_id_at(int dir_fd, const char *path, int *ret); int path_get_mnt_id_at(int dir_fd, const char *path, int *ret);
static inline int path_get_mnt_id(const char *path, int *ret) { static inline int path_get_mnt_id(const char *path, int *ret) {
return path_get_mnt_id_at(AT_FDCWD, path, ret); return path_get_mnt_id_at(AT_FDCWD, path, ret);
} }
int path_get_unique_mnt_id_at(int dir_fd, const char *path, uint64_t *ret);
int is_mount_point_at(int dir_fd, const char *path, int flags); int is_mount_point_at(int dir_fd, const char *path, int flags);
int path_is_mount_point_full(const char *path, const char *root, int flags); int path_is_mount_point_full(const char *path, const char *root, int flags);

View File

@ -17,3 +17,8 @@
#ifndef AT_HANDLE_FID #ifndef AT_HANDLE_FID
#define AT_HANDLE_FID AT_REMOVEDIR #define AT_HANDLE_FID AT_REMOVEDIR
#endif #endif
/* This is defined since glibc-2.42. */
#ifndef AT_HANDLE_MNT_ID_UNIQUE
#define AT_HANDLE_MNT_ID_UNIQUE 0x001 /* Return the u64 unique mount ID. */
#endif

View File

@ -244,7 +244,7 @@ static int dhcp_server_lease_append_json(sd_dhcp_server_lease *lease, sd_json_va
return sd_json_buildo( return sd_json_buildo(
ret, ret,
SD_JSON_BUILD_PAIR_BYTE_ARRAY("ClientId", lease->client_id.raw, lease->client_id.size), SD_JSON_BUILD_PAIR_BYTE_ARRAY("ClientId", lease->client_id.raw, lease->client_id.size),
JSON_BUILD_PAIR_IN4_ADDR_NON_NULL("Address", &(struct in_addr) { .s_addr = lease->address }), JSON_BUILD_PAIR_IN4_ADDR_WITH_STRING_NON_NULL("Address", &(struct in_addr) { .s_addr = lease->address }),
JSON_BUILD_PAIR_STRING_NON_EMPTY("Hostname", lease->hostname), JSON_BUILD_PAIR_STRING_NON_EMPTY("Hostname", lease->hostname),
SD_JSON_BUILD_PAIR_UNSIGNED("HardwareAddressType", lease->htype), SD_JSON_BUILD_PAIR_UNSIGNED("HardwareAddressType", lease->htype),
SD_JSON_BUILD_PAIR_UNSIGNED("HardwareAddressLength", lease->hlen), SD_JSON_BUILD_PAIR_UNSIGNED("HardwareAddressLength", lease->hlen),

View File

@ -190,6 +190,12 @@ enum {
_JSON_BUILD_PAIR_TRISTATE_NON_NULL, _JSON_BUILD_PAIR_TRISTATE_NON_NULL,
_JSON_BUILD_PAIR_PIDREF_NON_NULL, _JSON_BUILD_PAIR_PIDREF_NON_NULL,
_JSON_BUILD_PAIR_DEVNUM, _JSON_BUILD_PAIR_DEVNUM,
_JSON_BUILD_PAIR_IN_ADDR_WITH_STRING,
_JSON_BUILD_PAIR_IN6_ADDR_WITH_STRING,
_JSON_BUILD_PAIR_IN4_ADDR_WITH_STRING,
_JSON_BUILD_PAIR_IN_ADDR_WITH_STRING_NON_NULL,
_JSON_BUILD_PAIR_IN6_ADDR_WITH_STRING_NON_NULL,
_JSON_BUILD_PAIR_IN4_ADDR_WITH_STRING_NON_NULL,
_SD_JSON_BUILD_REALLYMAX, _SD_JSON_BUILD_REALLYMAX,
}; };
@ -237,6 +243,12 @@ enum {
#define JSON_BUILD_PAIR_OCTESCAPE_NON_EMPTY(name, v, n) _JSON_BUILD_PAIR_HEX_NON_EMPTY, (const char*) { name }, (const void*) { v }, (size_t) { n } #define JSON_BUILD_PAIR_OCTESCAPE_NON_EMPTY(name, v, n) _JSON_BUILD_PAIR_HEX_NON_EMPTY, (const char*) { name }, (const void*) { v }, (size_t) { n }
#define JSON_BUILD_PAIR_TRISTATE_NON_NULL(name, i) _JSON_BUILD_PAIR_TRISTATE_NON_NULL, (const char*) { name }, (int) { i } #define JSON_BUILD_PAIR_TRISTATE_NON_NULL(name, i) _JSON_BUILD_PAIR_TRISTATE_NON_NULL, (const char*) { name }, (int) { i }
#define JSON_BUILD_PAIR_PIDREF_NON_NULL(name, p) _JSON_BUILD_PAIR_PIDREF_NON_NULL, (const char*) { name }, (const PidRef*) { p } #define JSON_BUILD_PAIR_PIDREF_NON_NULL(name, p) _JSON_BUILD_PAIR_PIDREF_NON_NULL, (const char*) { name }, (const PidRef*) { p }
#define JSON_BUILD_PAIR_IN_ADDR_WITH_STRING(name, f, v) _JSON_BUILD_PAIR_IN_ADDR_WITH_STRING, (const char*) { name }, (int) { f }, (const union in_addr_union*) { v }
#define JSON_BUILD_PAIR_IN6_ADDR_WITH_STRING(name, v) _JSON_BUILD_PAIR_IN6_ADDR_WITH_STRING, (const char*) { name }, (const struct in6_addr*) { v }
#define JSON_BUILD_PAIR_IN4_ADDR_WITH_STRING(name, v) _JSON_BUILD_PAIR_IN4_ADDR_WITH_STRING, (const char*) { name }, (const struct in_addr*) { v }
#define JSON_BUILD_PAIR_IN_ADDR_WITH_STRING_NON_NULL(name, f, v) _JSON_BUILD_PAIR_IN_ADDR_WITH_STRING_NON_NULL, (const char*) { name }, (int) { f }, (const union in_addr_union*) { v }
#define JSON_BUILD_PAIR_IN6_ADDR_WITH_STRING_NON_NULL(name, v) _JSON_BUILD_PAIR_IN6_ADDR_WITH_STRING_NON_NULL, (const char*) { name }, (const struct in6_addr*) { v }
#define JSON_BUILD_PAIR_IN4_ADDR_WITH_STRING_NON_NULL(name, v) _JSON_BUILD_PAIR_IN4_ADDR_WITH_STRING_NON_NULL, (const char*) { name }, (const struct in_addr*) { v }
#define JSON_BUILD_PAIR_IOVEC_BASE64(name, iov) SD_JSON_BUILD_PAIR(name, JSON_BUILD_IOVEC_BASE64(iov)) #define JSON_BUILD_PAIR_IOVEC_BASE64(name, iov) SD_JSON_BUILD_PAIR(name, JSON_BUILD_IOVEC_BASE64(iov))
#define JSON_BUILD_PAIR_IOVEC_HEX(name, iov) SD_JSON_BUILD_PAIR(name, JSON_BUILD_IOVEC_HEX(iov)) #define JSON_BUILD_PAIR_IOVEC_HEX(name, iov) SD_JSON_BUILD_PAIR(name, JSON_BUILD_IOVEC_HEX(iov))

View File

@ -4741,7 +4741,7 @@ _public_ int sd_json_buildv(sd_json_variant **ret, va_list ap) {
if (r < 0) if (r < 0)
goto finish; goto finish;
r = sd_json_variant_new_array_bytes(&add_more, a->bytes, FAMILY_ADDRESS_SIZE(f)); r = sd_json_variant_new_array_bytes(&add_more, a->bytes, FAMILY_ADDRESS_SIZE_SAFE(f));
if (r < 0) if (r < 0)
goto finish; goto finish;
} }
@ -4896,6 +4896,207 @@ _public_ int sd_json_buildv(sd_json_variant **ret, va_list ap) {
break; break;
} }
case _JSON_BUILD_PAIR_IN_ADDR_WITH_STRING_NON_NULL:
case _JSON_BUILD_PAIR_IN_ADDR_WITH_STRING: {
const union in_addr_union *a;
const char *n;
int f;
if (current->expect != EXPECT_OBJECT_KEY) {
r = -EINVAL;
goto finish;
}
n = va_arg(ap, const char *);
f = va_arg(ap, int);
a = va_arg(ap, const union in_addr_union *);
if (current->n_suppress == 0) {
bool have_address = a && in_addr_is_set(f, a);
if (have_address || command != _JSON_BUILD_PAIR_IN_ADDR_WITH_STRING_NON_NULL) {
_cleanup_free_ char *addr_str = NULL, *string_key_name = NULL;
_cleanup_(sd_json_variant_unrefp) sd_json_variant *string_key = NULL, *string_value = NULL;
/* For non-NON_NULL variant, always convert address to string (even if all zeros).
* For NON_NULL variant, we only get here when have_address is true. */
if (a) {
r = in_addr_to_string(f, a, &addr_str);
if (r < 0)
goto finish;
}
string_key_name = strjoin(n, "String");
if (!string_key_name) {
r = -ENOMEM;
goto finish;
}
r = sd_json_variant_new_string(&add, n);
if (r < 0)
goto finish;
r = sd_json_variant_new_array_bytes(&add_more, a->bytes, FAMILY_ADDRESS_SIZE_SAFE(f));
if (r < 0)
goto finish;
r = sd_json_variant_new_string(&string_key, string_key_name);
if (r < 0)
goto finish;
r = sd_json_variant_new_string(&string_value, addr_str);
if (r < 0)
goto finish;
if (!GREEDY_REALLOC(current->elements, current->n_elements + 2)) {
r = -ENOMEM;
goto finish;
}
current->elements[current->n_elements++] = TAKE_PTR(string_key);
current->elements[current->n_elements++] = TAKE_PTR(string_value);
}
}
n_subtract = 4; /* we generated two pairs (binary and string) */
current->expect = EXPECT_OBJECT_KEY;
break;
}
case _JSON_BUILD_PAIR_IN6_ADDR_WITH_STRING_NON_NULL:
case _JSON_BUILD_PAIR_IN6_ADDR_WITH_STRING: {
const struct in6_addr *a;
const char *n;
if (current->expect != EXPECT_OBJECT_KEY) {
r = -EINVAL;
goto finish;
}
n = va_arg(ap, const char *);
a = va_arg(ap, const struct in6_addr *);
if (current->n_suppress == 0) {
bool have_address = a && in6_addr_is_set(a);
if (have_address || command != _JSON_BUILD_PAIR_IN6_ADDR_WITH_STRING_NON_NULL) {
_cleanup_free_ char *addr_str = NULL, *string_key_name = NULL;
_cleanup_(sd_json_variant_unrefp) sd_json_variant *string_key = NULL, *string_value = NULL;
/* For non-NON_NULL variant, always convert address to string (even if all zeros).
* For NON_NULL variant, we only get here when have_address is true. */
if (a) {
r = in6_addr_to_string(a, &addr_str);
if (r < 0)
goto finish;
}
string_key_name = strjoin(n, "String");
if (!string_key_name) {
r = -ENOMEM;
goto finish;
}
r = sd_json_variant_new_string(&add, n);
if (r < 0)
goto finish;
r = sd_json_variant_new_array_bytes(&add_more, a, sizeof(struct in6_addr));
if (r < 0)
goto finish;
r = sd_json_variant_new_string(&string_key, string_key_name);
if (r < 0)
goto finish;
r = sd_json_variant_new_string(&string_value, addr_str);
if (r < 0)
goto finish;
if (!GREEDY_REALLOC(current->elements, current->n_elements + 2)) {
r = -ENOMEM;
goto finish;
}
current->elements[current->n_elements++] = TAKE_PTR(string_key);
current->elements[current->n_elements++] = TAKE_PTR(string_value);
}
}
n_subtract = 4; /* we generated two pairs (binary and string) */
current->expect = EXPECT_OBJECT_KEY;
break;
}
case _JSON_BUILD_PAIR_IN4_ADDR_WITH_STRING_NON_NULL:
case _JSON_BUILD_PAIR_IN4_ADDR_WITH_STRING: {
const struct in_addr *a;
const char *n;
if (current->expect != EXPECT_OBJECT_KEY) {
r = -EINVAL;
goto finish;
}
n = va_arg(ap, const char *);
a = va_arg(ap, const struct in_addr *);
if (current->n_suppress == 0) {
bool have_address = a && !in4_addr_is_null(a);
if (have_address || command != _JSON_BUILD_PAIR_IN4_ADDR_WITH_STRING_NON_NULL) {
_cleanup_free_ char *addr_str = NULL, *string_key_name = NULL;
_cleanup_(sd_json_variant_unrefp) sd_json_variant *string_key = NULL, *string_value = NULL;
/* For non-NON_NULL variant, always convert address to string (even if all zeros).
* For NON_NULL variant, we only get here when have_address is true. */
if (a) {
union in_addr_union addr_union = { .in = *a };
r = in_addr_to_string(AF_INET, &addr_union, &addr_str);
if (r < 0)
goto finish;
}
string_key_name = strjoin(n, "String");
if (!string_key_name) {
r = -ENOMEM;
goto finish;
}
r = sd_json_variant_new_string(&add, n);
if (r < 0)
goto finish;
r = sd_json_variant_new_array_bytes(&add_more, a, sizeof(struct in_addr));
if (r < 0)
goto finish;
r = sd_json_variant_new_string(&string_key, string_key_name);
if (r < 0)
goto finish;
r = sd_json_variant_new_string(&string_value, addr_str);
if (r < 0)
goto finish;
if (!GREEDY_REALLOC(current->elements, current->n_elements + 2)) {
r = -ENOMEM;
goto finish;
}
current->elements[current->n_elements++] = TAKE_PTR(string_key);
current->elements[current->n_elements++] = TAKE_PTR(string_value);
}
}
n_subtract = 4; /* we generated two pairs (binary and string) */
current->expect = EXPECT_OBJECT_KEY;
break;
}
case _JSON_BUILD_PAIR_CALLBACK_NON_NULL: { case _JSON_BUILD_PAIR_CALLBACK_NON_NULL: {
sd_json_build_callback_t cb; sd_json_build_callback_t cb;
void *userdata; void *userdata;

View File

@ -116,7 +116,6 @@ systemd_networkd_extract_sources = files(
) )
systemd_networkd_wait_online_sources = files( systemd_networkd_wait_online_sources = files(
'wait-online/dns-configuration.c',
'wait-online/wait-online-link.c', 'wait-online/wait-online-link.c',
'wait-online/wait-online-manager.c', 'wait-online/wait-online-manager.c',
'wait-online/wait-online.c', 'wait-online/wait-online.c',

View File

@ -10,6 +10,7 @@
#include "dhcp-server-lease-internal.h" #include "dhcp-server-lease-internal.h"
#include "dhcp6-lease-internal.h" #include "dhcp6-lease-internal.h"
#include "extract-word.h" #include "extract-word.h"
#include "in-addr-util.h"
#include "ip-protocol-list.h" #include "ip-protocol-list.h"
#include "json-util.h" #include "json-util.h"
#include "netif-util.h" #include "netif-util.h"
@ -42,11 +43,11 @@ static int address_append_json(Address *address, bool serializing, sd_json_varia
r = sd_json_buildo( r = sd_json_buildo(
&v, &v,
SD_JSON_BUILD_PAIR_INTEGER("Family", address->family), SD_JSON_BUILD_PAIR_INTEGER("Family", address->family),
JSON_BUILD_PAIR_IN_ADDR("Address", &address->in_addr, address->family), JSON_BUILD_PAIR_IN_ADDR_WITH_STRING("Address", address->family, &address->in_addr),
JSON_BUILD_PAIR_IN_ADDR_NON_NULL("Peer", &address->in_addr_peer, address->family), JSON_BUILD_PAIR_IN_ADDR_WITH_STRING_NON_NULL("Peer", address->family, &address->in_addr_peer),
SD_JSON_BUILD_PAIR_UNSIGNED("PrefixLength", address->prefixlen), SD_JSON_BUILD_PAIR_UNSIGNED("PrefixLength", address->prefixlen),
SD_JSON_BUILD_PAIR_STRING("ConfigSource", network_config_source_to_string(address->source)), SD_JSON_BUILD_PAIR_STRING("ConfigSource", network_config_source_to_string(address->source)),
JSON_BUILD_PAIR_IN_ADDR_NON_NULL("ConfigProvider", &address->provider, address->family)); JSON_BUILD_PAIR_IN_ADDR_WITH_STRING_NON_NULL("ConfigProvider", address->family, &address->provider));
if (r < 0) if (r < 0)
return r; return r;
@ -67,7 +68,7 @@ static int address_append_json(Address *address, bool serializing, sd_json_varia
r = sd_json_variant_merge_objectbo( r = sd_json_variant_merge_objectbo(
&v, &v,
JSON_BUILD_PAIR_IN4_ADDR_NON_NULL("Broadcast", &address->broadcast), JSON_BUILD_PAIR_IN4_ADDR_WITH_STRING_NON_NULL("Broadcast", &address->broadcast),
SD_JSON_BUILD_PAIR_UNSIGNED("Scope", address->scope), SD_JSON_BUILD_PAIR_UNSIGNED("Scope", address->scope),
SD_JSON_BUILD_PAIR_STRING("ScopeString", scope), SD_JSON_BUILD_PAIR_STRING("ScopeString", scope),
SD_JSON_BUILD_PAIR_UNSIGNED("Flags", address->flags), SD_JSON_BUILD_PAIR_UNSIGNED("Flags", address->flags),
@ -125,7 +126,7 @@ static int neighbor_append_json(Neighbor *n, sd_json_variant **array) {
return sd_json_variant_append_arraybo( return sd_json_variant_append_arraybo(
array, array,
SD_JSON_BUILD_PAIR_INTEGER("Family", n->dst_addr.family), SD_JSON_BUILD_PAIR_INTEGER("Family", n->dst_addr.family),
JSON_BUILD_PAIR_IN_ADDR("Destination", &n->dst_addr.address, n->dst_addr.family), JSON_BUILD_PAIR_IN_ADDR_WITH_STRING("Destination", n->dst_addr.family, &n->dst_addr.address),
JSON_BUILD_PAIR_HW_ADDR("LinkLayerAddress", &n->ll_addr), JSON_BUILD_PAIR_HW_ADDR("LinkLayerAddress", &n->ll_addr),
SD_JSON_BUILD_PAIR_STRING("ConfigSource", network_config_source_to_string(n->source)), SD_JSON_BUILD_PAIR_STRING("ConfigSource", network_config_source_to_string(n->source)),
SD_JSON_BUILD_PAIR_STRING("ConfigState", state)); SD_JSON_BUILD_PAIR_STRING("ConfigState", state));
@ -180,7 +181,7 @@ static int nexthop_append_json(NextHop *n, bool serializing, sd_json_variant **a
SD_JSON_BUILD_PAIR_UNSIGNED("ID", n->id), SD_JSON_BUILD_PAIR_UNSIGNED("ID", n->id),
SD_JSON_BUILD_PAIR_INTEGER("Family", n->family), SD_JSON_BUILD_PAIR_INTEGER("Family", n->family),
SD_JSON_BUILD_PAIR_STRING("ConfigSource", network_config_source_to_string(n->source)), SD_JSON_BUILD_PAIR_STRING("ConfigSource", network_config_source_to_string(n->source)),
JSON_BUILD_PAIR_IN_ADDR_NON_NULL("ConfigProvider", &n->provider, n->family)); JSON_BUILD_PAIR_IN_ADDR_WITH_STRING_NON_NULL("ConfigProvider", n->family, &n->provider));
if (r < 0) if (r < 0)
return r; return r;
@ -206,7 +207,7 @@ static int nexthop_append_json(NextHop *n, bool serializing, sd_json_variant **a
r = sd_json_variant_merge_objectbo( r = sd_json_variant_merge_objectbo(
&v, &v,
JSON_BUILD_PAIR_IN_ADDR_NON_NULL("Gateway", &n->gw.address, n->family), JSON_BUILD_PAIR_IN_ADDR_WITH_STRING_NON_NULL("Gateway", n->family, &n->gw.address),
SD_JSON_BUILD_PAIR_UNSIGNED("Flags", n->flags), SD_JSON_BUILD_PAIR_UNSIGNED("Flags", n->flags),
SD_JSON_BUILD_PAIR_STRING("FlagsString", strempty(flags)), SD_JSON_BUILD_PAIR_STRING("FlagsString", strempty(flags)),
SD_JSON_BUILD_PAIR_UNSIGNED("Protocol", n->protocol), SD_JSON_BUILD_PAIR_UNSIGNED("Protocol", n->protocol),
@ -260,13 +261,12 @@ static int route_append_json(Route *route, bool serializing, sd_json_variant **a
r = sd_json_buildo( r = sd_json_buildo(
&v, &v,
SD_JSON_BUILD_PAIR_INTEGER("Family", route->family), SD_JSON_BUILD_PAIR_INTEGER("Family", route->family),
JSON_BUILD_PAIR_IN_ADDR("Destination", &route->dst, route->family), JSON_BUILD_PAIR_IN_ADDR_WITH_STRING("Destination", route->family, &route->dst),
SD_JSON_BUILD_PAIR_UNSIGNED("DestinationPrefixLength", route->dst_prefixlen), SD_JSON_BUILD_PAIR_UNSIGNED("DestinationPrefixLength", route->dst_prefixlen),
JSON_BUILD_PAIR_IN_ADDR_NON_NULL("Gateway", &route->nexthop.gw, route->nexthop.family), JSON_BUILD_PAIR_IN_ADDR_WITH_STRING_NON_NULL("Gateway", route->nexthop.family, &route->nexthop.gw),
SD_JSON_BUILD_PAIR_CONDITION(route->src_prefixlen > 0, JSON_BUILD_PAIR_IN_ADDR_WITH_STRING_NON_NULL("Source", route->family, route->src_prefixlen > 0 ? &route->src : NULL),
"Source", JSON_BUILD_IN_ADDR(&route->src, route->family)),
JSON_BUILD_PAIR_UNSIGNED_NON_ZERO("SourcePrefixLength", route->src_prefixlen), JSON_BUILD_PAIR_UNSIGNED_NON_ZERO("SourcePrefixLength", route->src_prefixlen),
JSON_BUILD_PAIR_IN_ADDR_NON_NULL("PreferredSource", &route->prefsrc, route->family), JSON_BUILD_PAIR_IN_ADDR_WITH_STRING_NON_NULL("PreferredSource", route->family, &route->prefsrc),
SD_JSON_BUILD_PAIR_UNSIGNED("TOS", route->tos), SD_JSON_BUILD_PAIR_UNSIGNED("TOS", route->tos),
SD_JSON_BUILD_PAIR_UNSIGNED("Scope", route->scope), SD_JSON_BUILD_PAIR_UNSIGNED("Scope", route->scope),
SD_JSON_BUILD_PAIR_UNSIGNED("Protocol", route->protocol), SD_JSON_BUILD_PAIR_UNSIGNED("Protocol", route->protocol),
@ -276,7 +276,7 @@ static int route_append_json(Route *route, bool serializing, sd_json_variant **a
SD_JSON_BUILD_PAIR_UNSIGNED("Flags", route->flags), SD_JSON_BUILD_PAIR_UNSIGNED("Flags", route->flags),
JSON_BUILD_PAIR_UNSIGNED_NON_ZERO("NextHopID", route->nexthop_id), JSON_BUILD_PAIR_UNSIGNED_NON_ZERO("NextHopID", route->nexthop_id),
SD_JSON_BUILD_PAIR_STRING("ConfigSource", network_config_source_to_string(route->source)), SD_JSON_BUILD_PAIR_STRING("ConfigSource", network_config_source_to_string(route->source)),
JSON_BUILD_PAIR_IN_ADDR_NON_NULL("ConfigProvider", &route->provider, route->family)); JSON_BUILD_PAIR_IN_ADDR_WITH_STRING_NON_NULL("ConfigProvider", route->family, &route->provider));
if (r < 0) if (r < 0)
return r; return r;
@ -383,10 +383,10 @@ static int routing_policy_rule_append_json(RoutingPolicyRule *rule, sd_json_vari
return sd_json_variant_append_arraybo( return sd_json_variant_append_arraybo(
array, array,
SD_JSON_BUILD_PAIR_INTEGER("Family", rule->family), SD_JSON_BUILD_PAIR_INTEGER("Family", rule->family),
JSON_BUILD_PAIR_IN_ADDR_NON_NULL("FromPrefix", &rule->from.address, rule->family), JSON_BUILD_PAIR_IN_ADDR_WITH_STRING_NON_NULL("FromPrefix", rule->family, &rule->from.address),
SD_JSON_BUILD_PAIR_CONDITION(in_addr_is_set(rule->family, &rule->from.address), SD_JSON_BUILD_PAIR_CONDITION(in_addr_is_set(rule->family, &rule->from.address),
"FromPrefixLength", SD_JSON_BUILD_UNSIGNED(rule->from.prefixlen)), "FromPrefixLength", SD_JSON_BUILD_UNSIGNED(rule->from.prefixlen)),
JSON_BUILD_PAIR_IN_ADDR_NON_NULL("ToPrefix", &rule->to.address, rule->family), JSON_BUILD_PAIR_IN_ADDR_WITH_STRING_NON_NULL("ToPrefix", rule->family, &rule->to.address),
SD_JSON_BUILD_PAIR_CONDITION(in_addr_is_set(rule->family, &rule->to.address), SD_JSON_BUILD_PAIR_CONDITION(in_addr_is_set(rule->family, &rule->to.address),
"ToPrefixLength", SD_JSON_BUILD_UNSIGNED(rule->to.prefixlen)), "ToPrefixLength", SD_JSON_BUILD_UNSIGNED(rule->to.prefixlen)),
SD_JSON_BUILD_PAIR_UNSIGNED("Protocol", rule->protocol), SD_JSON_BUILD_PAIR_UNSIGNED("Protocol", rule->protocol),
@ -510,12 +510,12 @@ static int dns_append_json_one(Link *link, const struct in_addr_full *a, Network
return sd_json_variant_append_arraybo( return sd_json_variant_append_arraybo(
array, array,
SD_JSON_BUILD_PAIR_INTEGER("Family", a->family), SD_JSON_BUILD_PAIR_INTEGER("Family", a->family),
JSON_BUILD_PAIR_IN_ADDR("Address", &a->address, a->family), JSON_BUILD_PAIR_IN_ADDR_WITH_STRING("Address", a->family, &a->address),
JSON_BUILD_PAIR_UNSIGNED_NON_ZERO("Port", a->port), JSON_BUILD_PAIR_UNSIGNED_NON_ZERO("Port", a->port),
SD_JSON_BUILD_PAIR_CONDITION(a->ifindex != 0, "InterfaceIndex", SD_JSON_BUILD_INTEGER(a->ifindex)), SD_JSON_BUILD_PAIR_CONDITION(a->ifindex != 0, "InterfaceIndex", SD_JSON_BUILD_INTEGER(a->ifindex)),
JSON_BUILD_PAIR_STRING_NON_EMPTY("ServerName", a->server_name), JSON_BUILD_PAIR_STRING_NON_EMPTY("ServerName", a->server_name),
SD_JSON_BUILD_PAIR_STRING("ConfigSource", network_config_source_to_string(s)), SD_JSON_BUILD_PAIR_STRING("ConfigSource", network_config_source_to_string(s)),
JSON_BUILD_PAIR_IN_ADDR_NON_NULL("ConfigProvider", p, a->family)); JSON_BUILD_PAIR_IN_ADDR_WITH_STRING_NON_NULL("ConfigProvider", a->family, p));
} }
static int dns_append_json(Link *link, sd_json_variant **v) { static int dns_append_json(Link *link, sd_json_variant **v) {
@ -709,16 +709,24 @@ static int dnr_append_json(Link *link, sd_json_variant **v) {
} }
static int server_append_json_one_addr(int family, const union in_addr_union *a, NetworkConfigSource s, const union in_addr_union *p, sd_json_variant **array) { static int server_append_json_one_addr(int family, const union in_addr_union *a, NetworkConfigSource s, const union in_addr_union *p, sd_json_variant **array) {
_cleanup_free_ char *address_str = NULL;
int r;
assert(IN_SET(family, AF_INET, AF_INET6)); assert(IN_SET(family, AF_INET, AF_INET6));
assert(a); assert(a);
assert(array); assert(array);
r = in_addr_to_string(family, a, &address_str);
if (r < 0)
return r;
return sd_json_variant_append_arraybo( return sd_json_variant_append_arraybo(
array, array,
SD_JSON_BUILD_PAIR_INTEGER("Family", family), SD_JSON_BUILD_PAIR_INTEGER("Family", family),
JSON_BUILD_PAIR_IN_ADDR("Address", a, family), JSON_BUILD_PAIR_IN_ADDR("Address", a, family),
SD_JSON_BUILD_PAIR_STRING("AddressString", address_str),
SD_JSON_BUILD_PAIR_STRING("ConfigSource", network_config_source_to_string(s)), SD_JSON_BUILD_PAIR_STRING("ConfigSource", network_config_source_to_string(s)),
JSON_BUILD_PAIR_IN_ADDR_NON_NULL("ConfigProvider", p, family)); JSON_BUILD_PAIR_IN_ADDR_WITH_STRING_NON_NULL("ConfigProvider", family, p));
} }
static int server_append_json_one_fqdn(int family, const char *fqdn, NetworkConfigSource s, const union in_addr_union *p, sd_json_variant **array) { static int server_append_json_one_fqdn(int family, const char *fqdn, NetworkConfigSource s, const union in_addr_union *p, sd_json_variant **array) {
@ -730,7 +738,7 @@ static int server_append_json_one_fqdn(int family, const char *fqdn, NetworkConf
array, array,
SD_JSON_BUILD_PAIR_STRING("Server", fqdn), SD_JSON_BUILD_PAIR_STRING("Server", fqdn),
SD_JSON_BUILD_PAIR_STRING("ConfigSource", network_config_source_to_string(s)), SD_JSON_BUILD_PAIR_STRING("ConfigSource", network_config_source_to_string(s)),
JSON_BUILD_PAIR_IN_ADDR_NON_NULL("ConfigProvider", p, family)); JSON_BUILD_PAIR_IN_ADDR_WITH_STRING_NON_NULL("ConfigProvider", family, p));
} }
static int server_append_json_one_string(const char *str, NetworkConfigSource s, sd_json_variant **array) { static int server_append_json_one_string(const char *str, NetworkConfigSource s, sd_json_variant **array) {
@ -829,7 +837,7 @@ static int domain_append_json(int family, const char *domain, NetworkConfigSourc
array, array,
SD_JSON_BUILD_PAIR_STRING("Domain", domain), SD_JSON_BUILD_PAIR_STRING("Domain", domain),
SD_JSON_BUILD_PAIR_STRING("ConfigSource", network_config_source_to_string(s)), SD_JSON_BUILD_PAIR_STRING("ConfigSource", network_config_source_to_string(s)),
JSON_BUILD_PAIR_IN_ADDR_NON_NULL("ConfigProvider", p, family)); JSON_BUILD_PAIR_IN_ADDR_WITH_STRING_NON_NULL("ConfigProvider", family, p));
} }
static int sip_append_json(Link *link, sd_json_variant **v) { static int sip_append_json(Link *link, sd_json_variant **v) {
@ -1103,10 +1111,10 @@ static int pref64_append_json(Link *link, sd_json_variant **v) {
SET_FOREACH(i, link->ndisc_pref64) { SET_FOREACH(i, link->ndisc_pref64) {
r = sd_json_variant_append_arraybo( r = sd_json_variant_append_arraybo(
&array, &array,
JSON_BUILD_PAIR_IN6_ADDR_NON_NULL("Prefix", &i->prefix), JSON_BUILD_PAIR_IN6_ADDR_WITH_STRING("Prefix", &i->prefix),
SD_JSON_BUILD_PAIR_UNSIGNED("PrefixLength", i->prefix_len), SD_JSON_BUILD_PAIR_UNSIGNED("PrefixLength", i->prefix_len),
JSON_BUILD_PAIR_FINITE_USEC("LifetimeUSec", i->lifetime_usec), JSON_BUILD_PAIR_FINITE_USEC("LifetimeUSec", i->lifetime_usec),
JSON_BUILD_PAIR_IN6_ADDR_NON_NULL("ConfigProvider", &i->router)); JSON_BUILD_PAIR_IN6_ADDR_WITH_STRING_NON_NULL("ConfigProvider", &i->router));
if (r < 0) if (r < 0)
return r; return r;
} }
@ -1222,6 +1230,7 @@ static int dhcp6_client_pd_append_json(Link *link, sd_json_variant **v) {
usec_t lifetime_preferred_usec, lifetime_valid_usec; usec_t lifetime_preferred_usec, lifetime_valid_usec;
struct in6_addr prefix; struct in6_addr prefix;
uint8_t prefix_len; uint8_t prefix_len;
_cleanup_free_ char *prefix_str = NULL;
r = sd_dhcp6_lease_get_pd_prefix(link->dhcp6_lease, &prefix, &prefix_len); r = sd_dhcp6_lease_get_pd_prefix(link->dhcp6_lease, &prefix, &prefix_len);
if (r < 0) if (r < 0)
@ -1232,9 +1241,16 @@ static int dhcp6_client_pd_append_json(Link *link, sd_json_variant **v) {
if (r < 0) if (r < 0)
return r; return r;
if (in6_addr_is_set(&prefix)) {
r = in6_addr_to_string(&prefix, &prefix_str);
if (r < 0)
return r;
}
r = sd_json_variant_append_arraybo( r = sd_json_variant_append_arraybo(
&array, &array,
JSON_BUILD_PAIR_IN6_ADDR("Prefix", &prefix), JSON_BUILD_PAIR_IN6_ADDR("Prefix", &prefix),
JSON_BUILD_PAIR_STRING_NON_EMPTY("PrefixString", prefix_str),
SD_JSON_BUILD_PAIR_UNSIGNED("PrefixLength", prefix_len), SD_JSON_BUILD_PAIR_UNSIGNED("PrefixLength", prefix_len),
JSON_BUILD_PAIR_FINITE_USEC("PreferredLifetimeUSec", lifetime_preferred_usec), JSON_BUILD_PAIR_FINITE_USEC("PreferredLifetimeUSec", lifetime_preferred_usec),
JSON_BUILD_PAIR_FINITE_USEC("ValidLifetimeUSec", lifetime_valid_usec)); JSON_BUILD_PAIR_FINITE_USEC("ValidLifetimeUSec", lifetime_valid_usec));
@ -1343,6 +1359,7 @@ static int dhcp_client_pd_append_json(Link *link, sd_json_variant **v) {
struct in6_addr sixrd_prefix; struct in6_addr sixrd_prefix;
const struct in_addr *br_addresses; const struct in_addr *br_addresses;
size_t n_br_addresses = 0; size_t n_br_addresses = 0;
_cleanup_free_ char *prefix_str = NULL;
int r; int r;
assert(link); assert(link);
@ -1356,6 +1373,12 @@ static int dhcp_client_pd_append_json(Link *link, sd_json_variant **v) {
if (r < 0) if (r < 0)
return r; return r;
if (in6_addr_is_set(&sixrd_prefix)) {
r = in6_addr_to_string(&sixrd_prefix, &prefix_str);
if (r < 0)
return r;
}
FOREACH_ARRAY(br_address, br_addresses, n_br_addresses) { FOREACH_ARRAY(br_address, br_addresses, n_br_addresses) {
r = sd_json_variant_append_arrayb(&addresses, JSON_BUILD_IN4_ADDR(br_address)); r = sd_json_variant_append_arrayb(&addresses, JSON_BUILD_IN4_ADDR(br_address));
if (r < 0) if (r < 0)
@ -1365,6 +1388,7 @@ static int dhcp_client_pd_append_json(Link *link, sd_json_variant **v) {
r = sd_json_buildo( r = sd_json_buildo(
&array, &array,
JSON_BUILD_PAIR_IN6_ADDR("Prefix", &sixrd_prefix), JSON_BUILD_PAIR_IN6_ADDR("Prefix", &sixrd_prefix),
JSON_BUILD_PAIR_STRING_NON_EMPTY("PrefixString", prefix_str),
SD_JSON_BUILD_PAIR_UNSIGNED("PrefixLength", sixrd_prefixlen), SD_JSON_BUILD_PAIR_UNSIGNED("PrefixLength", sixrd_prefixlen),
SD_JSON_BUILD_PAIR_UNSIGNED("IPv4MaskLength", ipv4masklen), SD_JSON_BUILD_PAIR_UNSIGNED("IPv4MaskLength", ipv4masklen),
JSON_BUILD_PAIR_VARIANT_NON_NULL("BorderRouters", addresses)); JSON_BUILD_PAIR_VARIANT_NON_NULL("BorderRouters", addresses));
@ -1502,7 +1526,7 @@ int link_build_json(Link *link, sd_json_variant **ret) {
JSON_BUILD_PAIR_HW_ADDR_NON_NULL("HardwareAddress", &link->hw_addr), JSON_BUILD_PAIR_HW_ADDR_NON_NULL("HardwareAddress", &link->hw_addr),
JSON_BUILD_PAIR_HW_ADDR_NON_NULL("PermanentHardwareAddress", &link->permanent_hw_addr), JSON_BUILD_PAIR_HW_ADDR_NON_NULL("PermanentHardwareAddress", &link->permanent_hw_addr),
JSON_BUILD_PAIR_HW_ADDR_NON_NULL("BroadcastAddress", &link->bcast_addr), JSON_BUILD_PAIR_HW_ADDR_NON_NULL("BroadcastAddress", &link->bcast_addr),
JSON_BUILD_PAIR_IN6_ADDR_NON_NULL("IPv6LinkLocalAddress", &link->ipv6ll_address), JSON_BUILD_PAIR_IN6_ADDR_WITH_STRING_NON_NULL("IPv6LinkLocalAddress", &link->ipv6ll_address),
/* wlan information */ /* wlan information */
SD_JSON_BUILD_PAIR_CONDITION(link->wlan_iftype > 0, "WirelessLanInterfaceType", SD_JSON_BUILD_PAIR_CONDITION(link->wlan_iftype > 0, "WirelessLanInterfaceType",
SD_JSON_BUILD_UNSIGNED(link->wlan_iftype)), SD_JSON_BUILD_UNSIGNED(link->wlan_iftype)),

File diff suppressed because it is too large Load Diff

View File

@ -2055,6 +2055,7 @@ static int dns_configuration_json_append(
Set *negative_trust_anchors, Set *negative_trust_anchors,
Set *dns_scopes, Set *dns_scopes,
DnssecMode dnssec_mode, DnssecMode dnssec_mode,
bool dnssec_supported,
DnsOverTlsMode dns_over_tls_mode, DnsOverTlsMode dns_over_tls_mode,
ResolveSupport llmnr_support, ResolveSupport llmnr_support,
ResolveSupport mdns_support, ResolveSupport mdns_support,
@ -2138,6 +2139,7 @@ static int dns_configuration_json_append(
SD_JSON_BUILD_PAIR_CONDITION(!set_isempty(negative_trust_anchors), SD_JSON_BUILD_PAIR_CONDITION(!set_isempty(negative_trust_anchors),
"negativeTrustAnchors", "negativeTrustAnchors",
JSON_BUILD_STRING_SET(negative_trust_anchors)), JSON_BUILD_STRING_SET(negative_trust_anchors)),
JSON_BUILD_PAIR_CONDITION_BOOLEAN(dnssec_mode >= 0, "dnssecSupported", dnssec_supported),
JSON_BUILD_PAIR_STRING_NON_EMPTY("dnssec", dnssec_mode_to_string(dnssec_mode)), JSON_BUILD_PAIR_STRING_NON_EMPTY("dnssec", dnssec_mode_to_string(dnssec_mode)),
JSON_BUILD_PAIR_STRING_NON_EMPTY("dnsOverTLS", dns_over_tls_mode_to_string(dns_over_tls_mode)), JSON_BUILD_PAIR_STRING_NON_EMPTY("dnsOverTLS", dns_over_tls_mode_to_string(dns_over_tls_mode)),
JSON_BUILD_PAIR_STRING_NON_EMPTY("llmnr", resolve_support_to_string(llmnr_support)), JSON_BUILD_PAIR_STRING_NON_EMPTY("llmnr", resolve_support_to_string(llmnr_support)),
@ -2169,6 +2171,7 @@ static int global_dns_configuration_json_append(Manager *m, sd_json_variant **co
m->trust_anchor.negative_by_name, m->trust_anchor.negative_by_name,
scopes, scopes,
manager_get_dnssec_mode(m), manager_get_dnssec_mode(m),
manager_dnssec_supported(m),
manager_get_dns_over_tls_mode(m), manager_get_dns_over_tls_mode(m),
m->llmnr_support, m->llmnr_support,
m->mdns_support, m->mdns_support,
@ -2225,6 +2228,7 @@ static int link_dns_configuration_json_append(Link *l, sd_json_variant **configu
l->dnssec_negative_trust_anchors, l->dnssec_negative_trust_anchors,
scopes, scopes,
link_get_dnssec_mode(l), link_get_dnssec_mode(l),
link_dnssec_supported(l),
link_get_dns_over_tls_mode(l), link_get_dns_over_tls_mode(l),
link_get_llmnr_support(l), link_get_llmnr_support(l),
link_get_mdns_support(l), link_get_mdns_support(l),
@ -2255,6 +2259,7 @@ static int delegate_dns_configuration_json_append(DnsDelegate *d, sd_json_varian
/* negative_trust_anchors= */ NULL, /* negative_trust_anchors= */ NULL,
scopes, scopes,
/* dnssec_mode= */ _DNSSEC_MODE_INVALID, /* dnssec_mode= */ _DNSSEC_MODE_INVALID,
/* dnssec_supported= */ false,
/* dns_over_tls_mode= */ _DNS_OVER_TLS_MODE_INVALID, /* dns_over_tls_mode= */ _DNS_OVER_TLS_MODE_INVALID,
/* llmnr_support= */ _RESOLVE_SUPPORT_INVALID, /* llmnr_support= */ _RESOLVE_SUPPORT_INVALID,
/* mdns_support= */ _RESOLVE_SUPPORT_INVALID, /* mdns_support= */ _RESOLVE_SUPPORT_INVALID,

View File

@ -35,6 +35,7 @@
#include "lock-util.h" #include "lock-util.h"
#include "log.h" #include "log.h"
#include "loop-util.h" #include "loop-util.h"
#include "mountpoint-util.h"
#include "namespace-util.h" #include "namespace-util.h"
#include "nsresource.h" #include "nsresource.h"
#include "nulstr-util.h" #include "nulstr-util.h"
@ -148,6 +149,8 @@ static Image* image_free(Image *i) {
free(i->name); free(i->name);
free(i->path); free(i->path);
free(i->fh);
free(i->hostname); free(i->hostname);
strv_free(i->machine_info); strv_free(i->machine_info);
strv_free(i->os_release); strv_free(i->os_release);
@ -249,6 +252,9 @@ static int image_new(
bool read_only, bool read_only,
usec_t crtime, usec_t crtime,
usec_t mtime, usec_t mtime,
struct file_handle *fh,
uint64_t on_mount_id,
uint64_t inode,
Image **ret) { Image **ret) {
_cleanup_(image_unrefp) Image *i = NULL; _cleanup_(image_unrefp) Image *i = NULL;
@ -270,12 +276,20 @@ static int image_new(
.read_only = read_only, .read_only = read_only,
.crtime = crtime, .crtime = crtime,
.mtime = mtime, .mtime = mtime,
.on_mount_id = on_mount_id,
.inode = inode,
.usage = UINT64_MAX, .usage = UINT64_MAX,
.usage_exclusive = UINT64_MAX, .usage_exclusive = UINT64_MAX,
.limit = UINT64_MAX, .limit = UINT64_MAX,
.limit_exclusive = UINT64_MAX, .limit_exclusive = UINT64_MAX,
}; };
if (fh) {
i->fh = file_handle_dup(fh);
if (!i->fh)
return -ENOMEM;
}
i->name = strdup(pretty); i->name = strdup(pretty);
if (!i->name) if (!i->name)
return -ENOMEM; return -ENOMEM;
@ -437,6 +451,28 @@ static int image_make(
path_startswith(path, "/usr") || path_startswith(path, "/usr") ||
(faccessat(fd, "", W_OK, AT_EACCESS|AT_EMPTY_PATH) < 0 && errno == EROFS); (faccessat(fd, "", W_OK, AT_EACCESS|AT_EMPTY_PATH) < 0 && errno == EROFS);
uint64_t on_mount_id = 0;
_cleanup_free_ struct file_handle *fh = NULL;
r = name_to_handle_at_try_unique_mntid_fid(fd, /* path= */ NULL, &fh, &on_mount_id, /* flags= */ 0);
if (r < 0) {
if (is_name_to_handle_at_fatal_error(r))
return r;
r = path_get_unique_mnt_id_at(fd, /* path= */ NULL, &on_mount_id);
if (r < 0) {
if (!ERRNO_IS_NEG_NOT_SUPPORTED(r))
return r;
int on_mount_id_fallback = -1;
r = path_get_mnt_id_at(fd, /* path= */ NULL, &on_mount_id_fallback);
if (r < 0)
return r;
on_mount_id = on_mount_id_fallback;
}
}
if (S_ISDIR(st->st_mode)) { if (S_ISDIR(st->st_mode)) {
unsigned file_attr = 0; unsigned file_attr = 0;
usec_t crtime = 0; usec_t crtime = 0;
@ -478,6 +514,9 @@ static int image_make(
info.read_only || read_only, info.read_only || read_only,
info.otime, info.otime,
info.ctime, info.ctime,
fh,
on_mount_id,
(uint64_t) st->st_ino,
ret); ret);
if (r < 0) if (r < 0)
return r; return r;
@ -503,6 +542,9 @@ static int image_make(
read_only || (file_attr & FS_IMMUTABLE_FL), read_only || (file_attr & FS_IMMUTABLE_FL),
crtime, crtime,
0, /* we don't use mtime of stat() here, since it's not the time of last change of the tree, but only of the top-level dir */ 0, /* we don't use mtime of stat() here, since it's not the time of last change of the tree, but only of the top-level dir */
fh,
on_mount_id,
(uint64_t) st->st_ino,
ret); ret);
if (r < 0) if (r < 0)
return r; return r;
@ -540,6 +582,9 @@ static int image_make(
!(st->st_mode & 0222) || read_only, !(st->st_mode & 0222) || read_only,
crtime, crtime,
timespec_load(&st->st_mtim), timespec_load(&st->st_mtim),
fh,
on_mount_id,
(uint64_t) st->st_ino,
ret); ret);
if (r < 0) if (r < 0)
return r; return r;
@ -597,6 +642,9 @@ static int image_make(
!(st->st_mode & 0222) || read_only, !(st->st_mode & 0222) || read_only,
0, 0,
0, 0,
fh,
on_mount_id,
(uint64_t) st->st_ino,
ret); ret);
if (r < 0) if (r < 0)
return r; return r;

View File

@ -27,6 +27,10 @@ typedef struct Image {
usec_t crtime; usec_t crtime;
usec_t mtime; usec_t mtime;
struct file_handle *fh;
uint64_t on_mount_id;
uint64_t inode;
uint64_t usage; uint64_t usage;
uint64_t usage_exclusive; uint64_t usage_exclusive;
uint64_t limit; uint64_t limit;

View File

@ -9,8 +9,10 @@
#include "in-addr-util.h" #include "in-addr-util.h"
#include "iovec-util.h" #include "iovec-util.h"
#include "json-util.h" #include "json-util.h"
#include "ordered-set.h"
#include "set.h" #include "set.h"
#include "string-util.h" #include "string-util.h"
#include "strv.h"
DNSServer* dns_server_free(DNSServer *s) { DNSServer* dns_server_free(DNSServer *s) {
if (!s) if (!s)
@ -57,6 +59,7 @@ static int dispatch_dns_server(const char *name, sd_json_variant *variant, sd_js
return json_log(variant, flags, SYNTHETIC_ERRNO(EINVAL), return json_log(variant, flags, SYNTHETIC_ERRNO(EINVAL),
"Dispatched address size (%zu) is incompatible with the family (%s).", "Dispatched address size (%zu) is incompatible with the family (%s).",
s->addr.iov_len, af_to_ipv4_ipv6(s->family)); s->addr.iov_len, af_to_ipv4_ipv6(s->family));
memcpy_safe(&s->in_addr, s->addr.iov_base, s->addr.iov_len);
*ret = TAKE_PTR(s); *ret = TAKE_PTR(s);
@ -64,8 +67,8 @@ static int dispatch_dns_server(const char *name, sd_json_variant *variant, sd_js
} }
static int dispatch_dns_server_array(const char *name, sd_json_variant *variant, sd_json_dispatch_flags_t flags, void *userdata) { static int dispatch_dns_server_array(const char *name, sd_json_variant *variant, sd_json_dispatch_flags_t flags, void *userdata) {
Set **ret = ASSERT_PTR(userdata); OrderedSet **ret = ASSERT_PTR(userdata);
_cleanup_set_free_ Set *dns_servers = NULL; _cleanup_ordered_set_free_ OrderedSet *dns_servers = NULL;
sd_json_variant *v; sd_json_variant *v;
int r; int r;
@ -76,12 +79,13 @@ static int dispatch_dns_server_array(const char *name, sd_json_variant *variant,
if (r < 0) if (r < 0)
return json_log(v, flags, r, "JSON array element is not a valid DNSServer."); return json_log(v, flags, r, "JSON array element is not a valid DNSServer.");
r = set_ensure_consume(&dns_servers, &dns_server_hash_ops, TAKE_PTR(s)); r = ordered_set_ensure_put(&dns_servers, &dns_server_hash_ops, s);
if (r < 0) if (r < 0)
return r; return r;
TAKE_PTR(s);
} }
set_free_and_replace(*ret, dns_servers); free_and_replace_full(*ret, dns_servers, ordered_set_free);
return 0; return 0;
} }
@ -128,8 +132,8 @@ static int dispatch_search_domain(const char *name, sd_json_variant *variant, sd
} }
static int dispatch_search_domain_array(const char *name, sd_json_variant *variant, sd_json_dispatch_flags_t flags, void *userdata) { static int dispatch_search_domain_array(const char *name, sd_json_variant *variant, sd_json_dispatch_flags_t flags, void *userdata) {
Set **ret = ASSERT_PTR(userdata); OrderedSet **ret = ASSERT_PTR(userdata);
_cleanup_set_free_ Set *search_domains = NULL; _cleanup_ordered_set_free_ OrderedSet *search_domains = NULL;
sd_json_variant *v; sd_json_variant *v;
int r; int r;
@ -140,12 +144,82 @@ static int dispatch_search_domain_array(const char *name, sd_json_variant *varia
if (r < 0) if (r < 0)
return json_log(v, flags, r, "JSON array element is not a valid SearchDomain."); return json_log(v, flags, r, "JSON array element is not a valid SearchDomain.");
r = set_ensure_consume(&search_domains, &search_domain_hash_ops, TAKE_PTR(d)); r = ordered_set_ensure_put(&search_domains, &search_domain_hash_ops, d);
if (r < 0)
return r;
TAKE_PTR(d);
}
free_and_replace_full(*ret, search_domains, ordered_set_free);
return 0;
}
DNSScope* dns_scope_free(DNSScope *s) {
if (!s)
return NULL;
free(s->ifname);
free(s->protocol);
free(s->dnssec_mode_str);
free(s->dns_over_tls_mode_str);
return mfree(s);
}
DEFINE_PRIVATE_HASH_OPS_WITH_VALUE_DESTRUCTOR(
dns_scope_hash_ops,
void,
trivial_hash_func,
trivial_compare_func,
DNSScope,
dns_scope_free);
static int dispatch_dns_scope(const char *name, sd_json_variant *variant, sd_json_dispatch_flags_t flags, void *userdata) {
static const sd_json_dispatch_field dns_scope_dispatch_table[] = {
{ "protocol", SD_JSON_VARIANT_STRING, sd_json_dispatch_string, offsetof(DNSScope, protocol), SD_JSON_MANDATORY },
{ "family", SD_JSON_VARIANT_UNSIGNED, sd_json_dispatch_uint, offsetof(DNSScope, family), 0 },
{ "ifname", SD_JSON_VARIANT_STRING, sd_json_dispatch_string, offsetof(DNSScope, ifname), 0 },
{ "ifindex", SD_JSON_VARIANT_UNSIGNED, json_dispatch_ifindex, offsetof(DNSScope, ifindex), SD_JSON_RELAX },
{ "dnssec", SD_JSON_VARIANT_STRING, sd_json_dispatch_string, offsetof(DNSScope, dnssec_mode_str), 0 },
{ "dnsOverTLS", SD_JSON_VARIANT_STRING, sd_json_dispatch_string, offsetof(DNSScope, dns_over_tls_mode_str), 0 },
{},
};
DNSScope **ret = ASSERT_PTR(userdata);
int r;
_cleanup_(dns_scope_freep) DNSScope *s = new0(DNSScope, 1);
if (!s)
return log_oom();
r = sd_json_dispatch(variant, dns_scope_dispatch_table, flags, s);
if (r < 0)
return r;
*ret = TAKE_PTR(s);
return 0;
}
static int dispatch_dns_scope_array(const char *name, sd_json_variant *variant, sd_json_dispatch_flags_t flags, void *userdata) {
Set **ret = ASSERT_PTR(userdata);
_cleanup_set_free_ Set *dns_scopes = NULL;
sd_json_variant *v;
int r;
JSON_VARIANT_ARRAY_FOREACH(v, variant) {
_cleanup_(dns_scope_freep) DNSScope *s = NULL;
r = dispatch_dns_scope(name, v, flags, &s);
if (r < 0)
return json_log(v, flags, r, "JSON array element is not a valid DNSScope.");
r = set_ensure_consume(&dns_scopes, &dns_scope_hash_ops, TAKE_PTR(s));
if (r < 0) if (r < 0)
return r; return r;
} }
set_free_and_replace(*ret, search_domains); set_free_and_replace(*ret, dns_scopes);
return 0; return 0;
} }
@ -155,9 +229,18 @@ DNSConfiguration* dns_configuration_free(DNSConfiguration *c) {
return NULL; return NULL;
dns_server_free(c->current_dns_server); dns_server_free(c->current_dns_server);
set_free(c->dns_servers); ordered_set_free(c->dns_servers);
set_free(c->search_domains); ordered_set_free(c->search_domains);
ordered_set_free(c->fallback_dns_servers);
set_free(c->dns_scopes);
free(c->ifname); free(c->ifname);
free(c->dnssec_mode_str);
free(c->dns_over_tls_mode_str);
free(c->llmnr_mode_str);
free(c->mdns_mode_str);
free(c->resolv_conf_mode_str);
free(c->delegate);
strv_free(c->negative_trust_anchors);
return mfree(c); return mfree(c);
} }
@ -178,17 +261,16 @@ static int dispatch_dns_configuration(const char *name, sd_json_variant *variant
{ "currentServer", SD_JSON_VARIANT_OBJECT, dispatch_dns_server, offsetof(DNSConfiguration, current_dns_server), 0 }, { "currentServer", SD_JSON_VARIANT_OBJECT, dispatch_dns_server, offsetof(DNSConfiguration, current_dns_server), 0 },
{ "servers", SD_JSON_VARIANT_ARRAY, dispatch_dns_server_array, offsetof(DNSConfiguration, dns_servers), 0 }, { "servers", SD_JSON_VARIANT_ARRAY, dispatch_dns_server_array, offsetof(DNSConfiguration, dns_servers), 0 },
{ "searchDomains", SD_JSON_VARIANT_ARRAY, dispatch_search_domain_array, offsetof(DNSConfiguration, search_domains), 0 }, { "searchDomains", SD_JSON_VARIANT_ARRAY, dispatch_search_domain_array, offsetof(DNSConfiguration, search_domains), 0 },
{ "dnssecSupported", SD_JSON_VARIANT_BOOLEAN, sd_json_dispatch_stdbool, offsetof(DNSConfiguration, dnssec_supported), 0 },
/* The remaining fields are currently unused by wait-online. */ { "dnssec", SD_JSON_VARIANT_STRING, sd_json_dispatch_string, offsetof(DNSConfiguration, dnssec_mode_str), 0 },
{ "delegate", _SD_JSON_VARIANT_TYPE_INVALID, NULL, 0, 0 }, { "dnsOverTLS", SD_JSON_VARIANT_STRING, sd_json_dispatch_string, offsetof(DNSConfiguration, dns_over_tls_mode_str), 0 },
{ "fallbackServers", _SD_JSON_VARIANT_TYPE_INVALID, NULL, 0, 0 }, { "llmnr", SD_JSON_VARIANT_STRING, sd_json_dispatch_string, offsetof(DNSConfiguration, llmnr_mode_str), 0 },
{ "negativeTrustAnchors", _SD_JSON_VARIANT_TYPE_INVALID, NULL, 0, 0 }, { "mDNS", SD_JSON_VARIANT_STRING, sd_json_dispatch_string, offsetof(DNSConfiguration, mdns_mode_str), 0 },
{ "dnssec", _SD_JSON_VARIANT_TYPE_INVALID, NULL, 0, 0 }, { "fallbackServers", SD_JSON_VARIANT_ARRAY, dispatch_dns_server_array, offsetof(DNSConfiguration, fallback_dns_servers), 0 },
{ "dnsOverTLS", _SD_JSON_VARIANT_TYPE_INVALID, NULL, 0, 0 }, { "negativeTrustAnchors", SD_JSON_VARIANT_ARRAY, sd_json_dispatch_strv, offsetof(DNSConfiguration, negative_trust_anchors), 0 },
{ "llmnr", _SD_JSON_VARIANT_TYPE_INVALID, NULL, 0, 0 }, { "resolvConfMode", SD_JSON_VARIANT_STRING, sd_json_dispatch_string, offsetof(DNSConfiguration, resolv_conf_mode_str), 0 },
{ "mDNS", _SD_JSON_VARIANT_TYPE_INVALID, NULL, 0, 0 }, { "scopes", SD_JSON_VARIANT_ARRAY, dispatch_dns_scope_array, offsetof(DNSConfiguration, dns_scopes), 0 },
{ "resolvConfMode", _SD_JSON_VARIANT_TYPE_INVALID, NULL, 0, 0 }, { "delegate", SD_JSON_VARIANT_STRING, sd_json_dispatch_string, offsetof(DNSConfiguration, delegate), 0 },
{ "scopes", _SD_JSON_VARIANT_TYPE_INVALID, NULL, 0, 0 },
{}, {},
}; };
@ -204,6 +286,8 @@ static int dispatch_dns_configuration(const char *name, sd_json_variant *variant
if (r < 0) if (r < 0)
return r; return r;
strv_sort(c->negative_trust_anchors);
*ret = TAKE_PTR(c); *ret = TAKE_PTR(c);
return 0; return 0;
@ -222,7 +306,7 @@ bool dns_is_accessible(DNSConfiguration *c) {
if (c->current_dns_server && c->current_dns_server->accessible) if (c->current_dns_server && c->current_dns_server->accessible)
return true; return true;
SET_FOREACH(s, c->dns_servers) ORDERED_SET_FOREACH(s, c->dns_servers)
if (s->accessible) if (s->accessible)
return true; return true;
@ -237,7 +321,7 @@ bool dns_configuration_contains_search_domain(DNSConfiguration *c, const char *d
if (!c) if (!c)
return false; return false;
SET_FOREACH(d, c->search_domains) ORDERED_SET_FOREACH(d, c->search_domains)
if (streq(d->name, domain)) if (streq(d->name, domain))
return true; return true;

View File

@ -3,6 +3,7 @@
#include <sys/uio.h> #include <sys/uio.h>
#include "in-addr-util.h"
#include "shared-forward.h" #include "shared-forward.h"
typedef struct DNSServer { typedef struct DNSServer {
@ -12,6 +13,7 @@ typedef struct DNSServer {
int ifindex; int ifindex;
char *server_name; char *server_name;
bool accessible; bool accessible;
union in_addr_union in_addr;
} DNSServer; } DNSServer;
DNSServer* dns_server_free(DNSServer *s); DNSServer* dns_server_free(DNSServer *s);
@ -23,6 +25,18 @@ typedef struct SearchDomain {
int ifindex; int ifindex;
} SearchDomain; } SearchDomain;
typedef struct DNSScope {
char *ifname;
int ifindex;
char *protocol;
int family;
char *dnssec_mode_str;
char *dns_over_tls_mode_str;
} DNSScope;
DNSScope* dns_scope_free(DNSScope *s);
DEFINE_TRIVIAL_CLEANUP_FUNC(DNSScope*, dns_scope_free);
SearchDomain* search_domain_free(SearchDomain *d); SearchDomain* search_domain_free(SearchDomain *d);
DEFINE_TRIVIAL_CLEANUP_FUNC(SearchDomain*, search_domain_free); DEFINE_TRIVIAL_CLEANUP_FUNC(SearchDomain*, search_domain_free);
@ -31,8 +45,18 @@ typedef struct DNSConfiguration {
int ifindex; int ifindex;
bool default_route; bool default_route;
DNSServer *current_dns_server; DNSServer *current_dns_server;
Set *dns_servers; OrderedSet *dns_servers;
Set *search_domains; OrderedSet *search_domains;
OrderedSet *fallback_dns_servers;
Set *dns_scopes;
char *dnssec_mode_str;
char *dns_over_tls_mode_str;
char *llmnr_mode_str;
char *mdns_mode_str;
char **negative_trust_anchors;
char *resolv_conf_mode_str;
char *delegate;
bool dnssec_supported;
} DNSConfiguration; } DNSConfiguration;
DNSConfiguration* dns_configuration_free(DNSConfiguration *c); DNSConfiguration* dns_configuration_free(DNSConfiguration *c);

View File

@ -61,6 +61,7 @@ shared_sources = files(
'dissect-image.c', 'dissect-image.c',
'dm-util.c', 'dm-util.c',
'dns-answer.c', 'dns-answer.c',
'dns-configuration.c',
'dns-domain.c', 'dns-domain.c',
'dns-packet.c', 'dns-packet.c',
'dns-question.c', 'dns-question.c',

View File

@ -2,6 +2,13 @@
#include "varlink-io.systemd.Network.h" #include "varlink-io.systemd.Network.h"
/* Helper macro to define address fields with both binary and string representation */
#define SD_VARLINK_DEFINE_IN_ADDR_WITH_STRING_FIELD(field_name, comment, flags) \
SD_VARLINK_FIELD_COMMENT(comment), \
SD_VARLINK_DEFINE_FIELD(field_name, SD_VARLINK_INT, SD_VARLINK_ARRAY | (flags)), \
SD_VARLINK_FIELD_COMMENT(comment " (human-readable format)"), \
SD_VARLINK_DEFINE_FIELD(field_name##String, SD_VARLINK_STRING, (flags))
static SD_VARLINK_DEFINE_ENUM_TYPE( static SD_VARLINK_DEFINE_ENUM_TYPE(
LinkState, LinkState,
SD_VARLINK_DEFINE_ENUM_VALUE(pending), SD_VARLINK_DEFINE_ENUM_VALUE(pending),
@ -35,12 +42,10 @@ static SD_VARLINK_DEFINE_STRUCT_TYPE(
RoutingPolicyRule, RoutingPolicyRule,
SD_VARLINK_FIELD_COMMENT("Address family (AF_INET or AF_INET6)"), SD_VARLINK_FIELD_COMMENT("Address family (AF_INET or AF_INET6)"),
SD_VARLINK_DEFINE_FIELD(Family, SD_VARLINK_INT, 0), SD_VARLINK_DEFINE_FIELD(Family, SD_VARLINK_INT, 0),
SD_VARLINK_FIELD_COMMENT("Source address prefix to match"), SD_VARLINK_DEFINE_IN_ADDR_WITH_STRING_FIELD(FromPrefix, "Source address prefix to match", SD_VARLINK_NULLABLE),
SD_VARLINK_DEFINE_FIELD(FromPrefix, SD_VARLINK_INT, SD_VARLINK_ARRAY|SD_VARLINK_NULLABLE),
SD_VARLINK_FIELD_COMMENT("Length of source prefix in bits"), SD_VARLINK_FIELD_COMMENT("Length of source prefix in bits"),
SD_VARLINK_DEFINE_FIELD(FromPrefixLength, SD_VARLINK_INT, SD_VARLINK_NULLABLE), SD_VARLINK_DEFINE_FIELD(FromPrefixLength, SD_VARLINK_INT, SD_VARLINK_NULLABLE),
SD_VARLINK_FIELD_COMMENT("Destination address prefix to match"), SD_VARLINK_DEFINE_IN_ADDR_WITH_STRING_FIELD(ToPrefix, "Destination address prefix to match", SD_VARLINK_NULLABLE),
SD_VARLINK_DEFINE_FIELD(ToPrefix, SD_VARLINK_INT, SD_VARLINK_ARRAY|SD_VARLINK_NULLABLE),
SD_VARLINK_FIELD_COMMENT("Length of destination prefix in bits"), SD_VARLINK_FIELD_COMMENT("Length of destination prefix in bits"),
SD_VARLINK_DEFINE_FIELD(ToPrefixLength, SD_VARLINK_INT, SD_VARLINK_NULLABLE), SD_VARLINK_DEFINE_FIELD(ToPrefixLength, SD_VARLINK_INT, SD_VARLINK_NULLABLE),
SD_VARLINK_FIELD_COMMENT("Routing protocol identifier"), SD_VARLINK_FIELD_COMMENT("Routing protocol identifier"),
@ -92,18 +97,14 @@ static SD_VARLINK_DEFINE_STRUCT_TYPE(
Route, Route,
SD_VARLINK_FIELD_COMMENT("Address family (AF_INET or AF_INET6)"), SD_VARLINK_FIELD_COMMENT("Address family (AF_INET or AF_INET6)"),
SD_VARLINK_DEFINE_FIELD(Family, SD_VARLINK_INT, 0), SD_VARLINK_DEFINE_FIELD(Family, SD_VARLINK_INT, 0),
SD_VARLINK_FIELD_COMMENT("Destination network address"), SD_VARLINK_DEFINE_IN_ADDR_WITH_STRING_FIELD(Destination, "Destination network address", 0),
SD_VARLINK_DEFINE_FIELD(Destination, SD_VARLINK_INT, SD_VARLINK_ARRAY),
SD_VARLINK_FIELD_COMMENT("Destination network prefix length"), SD_VARLINK_FIELD_COMMENT("Destination network prefix length"),
SD_VARLINK_DEFINE_FIELD(DestinationPrefixLength, SD_VARLINK_INT, 0), SD_VARLINK_DEFINE_FIELD(DestinationPrefixLength, SD_VARLINK_INT, 0),
SD_VARLINK_FIELD_COMMENT("Gateway address for this route"), SD_VARLINK_DEFINE_IN_ADDR_WITH_STRING_FIELD(Gateway, "Gateway address for this route", SD_VARLINK_NULLABLE),
SD_VARLINK_DEFINE_FIELD(Gateway, SD_VARLINK_INT, SD_VARLINK_ARRAY|SD_VARLINK_NULLABLE), SD_VARLINK_DEFINE_IN_ADDR_WITH_STRING_FIELD(Source, "Source address prefix for route selection", SD_VARLINK_NULLABLE),
SD_VARLINK_FIELD_COMMENT("Source address prefix for route selection"),
SD_VARLINK_DEFINE_FIELD(Source, SD_VARLINK_INT, SD_VARLINK_ARRAY|SD_VARLINK_NULLABLE),
SD_VARLINK_FIELD_COMMENT("Source prefix length"), SD_VARLINK_FIELD_COMMENT("Source prefix length"),
SD_VARLINK_DEFINE_FIELD(SourcePrefixLength, SD_VARLINK_INT, SD_VARLINK_NULLABLE), SD_VARLINK_DEFINE_FIELD(SourcePrefixLength, SD_VARLINK_INT, SD_VARLINK_NULLABLE),
SD_VARLINK_FIELD_COMMENT("Preferred source address for outgoing packets"), SD_VARLINK_DEFINE_IN_ADDR_WITH_STRING_FIELD(PreferredSource, "Preferred source address for outgoing packets", SD_VARLINK_NULLABLE),
SD_VARLINK_DEFINE_FIELD(PreferredSource, SD_VARLINK_INT, SD_VARLINK_ARRAY|SD_VARLINK_NULLABLE),
SD_VARLINK_FIELD_COMMENT("Type of Service (TOS) field"), SD_VARLINK_FIELD_COMMENT("Type of Service (TOS) field"),
SD_VARLINK_DEFINE_FIELD(TOS, SD_VARLINK_INT, 0), SD_VARLINK_DEFINE_FIELD(TOS, SD_VARLINK_INT, 0),
SD_VARLINK_FIELD_COMMENT("Route scope (RT_SCOPE_* value)"), SD_VARLINK_FIELD_COMMENT("Route scope (RT_SCOPE_* value)"),
@ -138,8 +139,7 @@ static SD_VARLINK_DEFINE_STRUCT_TYPE(
SD_VARLINK_DEFINE_FIELD(LifetimeUSec, SD_VARLINK_INT, SD_VARLINK_NULLABLE), SD_VARLINK_DEFINE_FIELD(LifetimeUSec, SD_VARLINK_INT, SD_VARLINK_NULLABLE),
SD_VARLINK_FIELD_COMMENT("Configuration source for this route"), SD_VARLINK_FIELD_COMMENT("Configuration source for this route"),
SD_VARLINK_DEFINE_FIELD(ConfigSource, SD_VARLINK_STRING, 0), SD_VARLINK_DEFINE_FIELD(ConfigSource, SD_VARLINK_STRING, 0),
SD_VARLINK_FIELD_COMMENT("Address of the configuration provider"), SD_VARLINK_DEFINE_IN_ADDR_WITH_STRING_FIELD(ConfigProvider, "Address of the configuration provider", SD_VARLINK_NULLABLE),
SD_VARLINK_DEFINE_FIELD(ConfigProvider, SD_VARLINK_INT, SD_VARLINK_ARRAY|SD_VARLINK_NULLABLE),
SD_VARLINK_FIELD_COMMENT("Configuration state of this route"), SD_VARLINK_FIELD_COMMENT("Configuration state of this route"),
SD_VARLINK_DEFINE_FIELD(ConfigState, SD_VARLINK_STRING, SD_VARLINK_NULLABLE), SD_VARLINK_DEFINE_FIELD(ConfigState, SD_VARLINK_STRING, SD_VARLINK_NULLABLE),
SD_VARLINK_FIELD_COMMENT("Interface index for serialization"), SD_VARLINK_FIELD_COMMENT("Interface index for serialization"),
@ -162,8 +162,7 @@ static SD_VARLINK_DEFINE_STRUCT_TYPE(
SD_VARLINK_DEFINE_FIELD(ID, SD_VARLINK_INT, 0), SD_VARLINK_DEFINE_FIELD(ID, SD_VARLINK_INT, 0),
SD_VARLINK_FIELD_COMMENT("Address family (AF_INET or AF_INET6)"), SD_VARLINK_FIELD_COMMENT("Address family (AF_INET or AF_INET6)"),
SD_VARLINK_DEFINE_FIELD(Family, SD_VARLINK_INT, 0), SD_VARLINK_DEFINE_FIELD(Family, SD_VARLINK_INT, 0),
SD_VARLINK_FIELD_COMMENT("Gateway address for this next hop"), SD_VARLINK_DEFINE_IN_ADDR_WITH_STRING_FIELD(Gateway, "Gateway address for this next hop", SD_VARLINK_NULLABLE),
SD_VARLINK_DEFINE_FIELD(Gateway, SD_VARLINK_INT, SD_VARLINK_ARRAY|SD_VARLINK_NULLABLE),
SD_VARLINK_FIELD_COMMENT("Next hop flags (RTNH_F_* values)"), SD_VARLINK_FIELD_COMMENT("Next hop flags (RTNH_F_* values)"),
SD_VARLINK_DEFINE_FIELD(Flags, SD_VARLINK_INT, SD_VARLINK_NULLABLE), SD_VARLINK_DEFINE_FIELD(Flags, SD_VARLINK_INT, SD_VARLINK_NULLABLE),
SD_VARLINK_FIELD_COMMENT("Human-readable flags string"), SD_VARLINK_FIELD_COMMENT("Human-readable flags string"),
@ -178,8 +177,7 @@ static SD_VARLINK_DEFINE_STRUCT_TYPE(
SD_VARLINK_DEFINE_FIELD_BY_TYPE(Group, NextHopGroup, SD_VARLINK_ARRAY|SD_VARLINK_NULLABLE), SD_VARLINK_DEFINE_FIELD_BY_TYPE(Group, NextHopGroup, SD_VARLINK_ARRAY|SD_VARLINK_NULLABLE),
SD_VARLINK_FIELD_COMMENT("Configuration source for this next hop"), SD_VARLINK_FIELD_COMMENT("Configuration source for this next hop"),
SD_VARLINK_DEFINE_FIELD(ConfigSource, SD_VARLINK_STRING, 0), SD_VARLINK_DEFINE_FIELD(ConfigSource, SD_VARLINK_STRING, 0),
SD_VARLINK_FIELD_COMMENT("Address of the configuration provider"), SD_VARLINK_DEFINE_IN_ADDR_WITH_STRING_FIELD(ConfigProvider, "Address of the configuration provider", SD_VARLINK_NULLABLE),
SD_VARLINK_DEFINE_FIELD(ConfigProvider, SD_VARLINK_INT, SD_VARLINK_ARRAY|SD_VARLINK_NULLABLE),
SD_VARLINK_FIELD_COMMENT("Configuration state of this next hop"), SD_VARLINK_FIELD_COMMENT("Configuration state of this next hop"),
SD_VARLINK_DEFINE_FIELD(ConfigState, SD_VARLINK_STRING, SD_VARLINK_NULLABLE)); SD_VARLINK_DEFINE_FIELD(ConfigState, SD_VARLINK_STRING, SD_VARLINK_NULLABLE));
@ -210,8 +208,7 @@ static SD_VARLINK_DEFINE_STRUCT_TYPE(
DNS, DNS,
SD_VARLINK_FIELD_COMMENT("Address family (AF_INET or AF_INET6)"), SD_VARLINK_FIELD_COMMENT("Address family (AF_INET or AF_INET6)"),
SD_VARLINK_DEFINE_FIELD(Family, SD_VARLINK_INT, 0), SD_VARLINK_DEFINE_FIELD(Family, SD_VARLINK_INT, 0),
SD_VARLINK_FIELD_COMMENT("DNS server IP address"), SD_VARLINK_DEFINE_IN_ADDR_WITH_STRING_FIELD(Address, "DNS server IP address", 0),
SD_VARLINK_DEFINE_FIELD(Address, SD_VARLINK_INT, SD_VARLINK_ARRAY),
SD_VARLINK_FIELD_COMMENT("DNS server port number"), SD_VARLINK_FIELD_COMMENT("DNS server port number"),
SD_VARLINK_DEFINE_FIELD(Port, SD_VARLINK_INT, SD_VARLINK_NULLABLE), SD_VARLINK_DEFINE_FIELD(Port, SD_VARLINK_INT, SD_VARLINK_NULLABLE),
SD_VARLINK_FIELD_COMMENT("Interface index for link-local DNS servers"), SD_VARLINK_FIELD_COMMENT("Interface index for link-local DNS servers"),
@ -220,34 +217,29 @@ static SD_VARLINK_DEFINE_STRUCT_TYPE(
SD_VARLINK_DEFINE_FIELD(ServerName, SD_VARLINK_STRING, SD_VARLINK_NULLABLE), SD_VARLINK_DEFINE_FIELD(ServerName, SD_VARLINK_STRING, SD_VARLINK_NULLABLE),
SD_VARLINK_FIELD_COMMENT("Configuration source for this DNS server"), SD_VARLINK_FIELD_COMMENT("Configuration source for this DNS server"),
SD_VARLINK_DEFINE_FIELD(ConfigSource, SD_VARLINK_STRING, 0), SD_VARLINK_DEFINE_FIELD(ConfigSource, SD_VARLINK_STRING, 0),
SD_VARLINK_FIELD_COMMENT("Address of the configuration provider"), SD_VARLINK_DEFINE_IN_ADDR_WITH_STRING_FIELD(ConfigProvider, "Address of the configuration provider", SD_VARLINK_NULLABLE));
SD_VARLINK_DEFINE_FIELD(ConfigProvider, SD_VARLINK_INT, SD_VARLINK_ARRAY|SD_VARLINK_NULLABLE));
static SD_VARLINK_DEFINE_STRUCT_TYPE( static SD_VARLINK_DEFINE_STRUCT_TYPE(
NTP, NTP,
SD_VARLINK_FIELD_COMMENT("Address family (AF_INET or AF_INET6) for address-based servers"), SD_VARLINK_FIELD_COMMENT("Address family (AF_INET or AF_INET6) for address-based servers"),
SD_VARLINK_DEFINE_FIELD(Family, SD_VARLINK_INT, SD_VARLINK_NULLABLE), SD_VARLINK_DEFINE_FIELD(Family, SD_VARLINK_INT, SD_VARLINK_NULLABLE),
SD_VARLINK_FIELD_COMMENT("NTP server IP address"), SD_VARLINK_DEFINE_IN_ADDR_WITH_STRING_FIELD(Address, "NTP server IP address", SD_VARLINK_NULLABLE),
SD_VARLINK_DEFINE_FIELD(Address, SD_VARLINK_INT, SD_VARLINK_ARRAY|SD_VARLINK_NULLABLE),
SD_VARLINK_FIELD_COMMENT("NTP server"), SD_VARLINK_FIELD_COMMENT("NTP server"),
SD_VARLINK_DEFINE_FIELD(Server, SD_VARLINK_STRING, SD_VARLINK_NULLABLE), SD_VARLINK_DEFINE_FIELD(Server, SD_VARLINK_STRING, SD_VARLINK_NULLABLE),
SD_VARLINK_FIELD_COMMENT("Configuration source for this NTP server"), SD_VARLINK_FIELD_COMMENT("Configuration source for this NTP server"),
SD_VARLINK_DEFINE_FIELD(ConfigSource, SD_VARLINK_STRING, 0), SD_VARLINK_DEFINE_FIELD(ConfigSource, SD_VARLINK_STRING, 0),
SD_VARLINK_FIELD_COMMENT("Address of the configuration provider"), SD_VARLINK_DEFINE_IN_ADDR_WITH_STRING_FIELD(ConfigProvider, "Address of the configuration provider", SD_VARLINK_NULLABLE));
SD_VARLINK_DEFINE_FIELD(ConfigProvider, SD_VARLINK_INT, SD_VARLINK_ARRAY|SD_VARLINK_NULLABLE));
static SD_VARLINK_DEFINE_STRUCT_TYPE( static SD_VARLINK_DEFINE_STRUCT_TYPE(
SIP, SIP,
SD_VARLINK_FIELD_COMMENT("Address family (AF_INET or AF_INET6) for address-based servers"), SD_VARLINK_FIELD_COMMENT("Address family (AF_INET or AF_INET6) for address-based servers"),
SD_VARLINK_DEFINE_FIELD(Family, SD_VARLINK_INT, SD_VARLINK_NULLABLE), SD_VARLINK_DEFINE_FIELD(Family, SD_VARLINK_INT, SD_VARLINK_NULLABLE),
SD_VARLINK_FIELD_COMMENT("SIP server IP address"), SD_VARLINK_DEFINE_IN_ADDR_WITH_STRING_FIELD(Address, "SIP server IP address", SD_VARLINK_NULLABLE),
SD_VARLINK_DEFINE_FIELD(Address, SD_VARLINK_INT, SD_VARLINK_ARRAY|SD_VARLINK_NULLABLE),
SD_VARLINK_FIELD_COMMENT("SIP server domain name"), SD_VARLINK_FIELD_COMMENT("SIP server domain name"),
SD_VARLINK_DEFINE_FIELD(Domain, SD_VARLINK_STRING, SD_VARLINK_NULLABLE), SD_VARLINK_DEFINE_FIELD(Domain, SD_VARLINK_STRING, SD_VARLINK_NULLABLE),
SD_VARLINK_FIELD_COMMENT("Configuration source for this SIP server"), SD_VARLINK_FIELD_COMMENT("Configuration source for this SIP server"),
SD_VARLINK_DEFINE_FIELD(ConfigSource, SD_VARLINK_STRING, 0), SD_VARLINK_DEFINE_FIELD(ConfigSource, SD_VARLINK_STRING, 0),
SD_VARLINK_FIELD_COMMENT("Address of the configuration provider"), SD_VARLINK_DEFINE_IN_ADDR_WITH_STRING_FIELD(ConfigProvider, "Address of the configuration provider", SD_VARLINK_NULLABLE));
SD_VARLINK_DEFINE_FIELD(ConfigProvider, SD_VARLINK_INT, SD_VARLINK_ARRAY|SD_VARLINK_NULLABLE));
static SD_VARLINK_DEFINE_STRUCT_TYPE( static SD_VARLINK_DEFINE_STRUCT_TYPE(
Domain, Domain,
@ -255,8 +247,7 @@ static SD_VARLINK_DEFINE_STRUCT_TYPE(
SD_VARLINK_DEFINE_FIELD(Domain, SD_VARLINK_STRING, 0), SD_VARLINK_DEFINE_FIELD(Domain, SD_VARLINK_STRING, 0),
SD_VARLINK_FIELD_COMMENT("Configuration source for this domain"), SD_VARLINK_FIELD_COMMENT("Configuration source for this domain"),
SD_VARLINK_DEFINE_FIELD(ConfigSource, SD_VARLINK_STRING, 0), SD_VARLINK_DEFINE_FIELD(ConfigSource, SD_VARLINK_STRING, 0),
SD_VARLINK_FIELD_COMMENT("Address of the configuration provider"), SD_VARLINK_DEFINE_IN_ADDR_WITH_STRING_FIELD(ConfigProvider, "Address of the configuration provider", SD_VARLINK_NULLABLE));
SD_VARLINK_DEFINE_FIELD(ConfigProvider, SD_VARLINK_INT, SD_VARLINK_ARRAY|SD_VARLINK_NULLABLE));
static SD_VARLINK_DEFINE_STRUCT_TYPE( static SD_VARLINK_DEFINE_STRUCT_TYPE(
DNSSECNegativeTrustAnchor, DNSSECNegativeTrustAnchor,
@ -282,12 +273,13 @@ static SD_VARLINK_DEFINE_STRUCT_TYPE(
Pref64, Pref64,
SD_VARLINK_FIELD_COMMENT("IPv6 prefix for NAT64/DNS64"), SD_VARLINK_FIELD_COMMENT("IPv6 prefix for NAT64/DNS64"),
SD_VARLINK_DEFINE_FIELD(Prefix, SD_VARLINK_INT, SD_VARLINK_ARRAY|SD_VARLINK_NULLABLE), SD_VARLINK_DEFINE_FIELD(Prefix, SD_VARLINK_INT, SD_VARLINK_ARRAY|SD_VARLINK_NULLABLE),
SD_VARLINK_FIELD_COMMENT("Human-readable IPv6 prefix"),
SD_VARLINK_DEFINE_FIELD(PrefixString, SD_VARLINK_STRING, SD_VARLINK_NULLABLE),
SD_VARLINK_FIELD_COMMENT("Length of the prefix in bits"), SD_VARLINK_FIELD_COMMENT("Length of the prefix in bits"),
SD_VARLINK_DEFINE_FIELD(PrefixLength, SD_VARLINK_INT, 0), SD_VARLINK_DEFINE_FIELD(PrefixLength, SD_VARLINK_INT, 0),
SD_VARLINK_FIELD_COMMENT("Lifetime of the prefix in microseconds"), SD_VARLINK_FIELD_COMMENT("Lifetime of the prefix in microseconds"),
SD_VARLINK_DEFINE_FIELD(LifetimeUSec, SD_VARLINK_INT, SD_VARLINK_NULLABLE), SD_VARLINK_DEFINE_FIELD(LifetimeUSec, SD_VARLINK_INT, SD_VARLINK_NULLABLE),
SD_VARLINK_FIELD_COMMENT("Address of router that provided this prefix"), SD_VARLINK_DEFINE_IN_ADDR_WITH_STRING_FIELD(ConfigProvider, "Address of router that provided this prefix", SD_VARLINK_NULLABLE));
SD_VARLINK_DEFINE_FIELD(ConfigProvider, SD_VARLINK_INT, SD_VARLINK_ARRAY|SD_VARLINK_NULLABLE));
static SD_VARLINK_DEFINE_STRUCT_TYPE( static SD_VARLINK_DEFINE_STRUCT_TYPE(
NDisc, NDisc,
@ -298,18 +290,14 @@ static SD_VARLINK_DEFINE_STRUCT_TYPE(
Address, Address,
SD_VARLINK_FIELD_COMMENT("Address family (AF_INET or AF_INET6)"), SD_VARLINK_FIELD_COMMENT("Address family (AF_INET or AF_INET6)"),
SD_VARLINK_DEFINE_FIELD(Family, SD_VARLINK_INT, 0), SD_VARLINK_DEFINE_FIELD(Family, SD_VARLINK_INT, 0),
SD_VARLINK_FIELD_COMMENT("IP address"), SD_VARLINK_DEFINE_IN_ADDR_WITH_STRING_FIELD(Address, "IP address", 0),
SD_VARLINK_DEFINE_FIELD(Address, SD_VARLINK_INT, SD_VARLINK_ARRAY), SD_VARLINK_DEFINE_IN_ADDR_WITH_STRING_FIELD(Peer, "Peer address for point-to-point interfaces", SD_VARLINK_NULLABLE),
SD_VARLINK_FIELD_COMMENT("Peer address for point-to-point interfaces"),
SD_VARLINK_DEFINE_FIELD(Peer, SD_VARLINK_INT, SD_VARLINK_ARRAY|SD_VARLINK_NULLABLE),
SD_VARLINK_FIELD_COMMENT("Network prefix length"), SD_VARLINK_FIELD_COMMENT("Network prefix length"),
SD_VARLINK_DEFINE_FIELD(PrefixLength, SD_VARLINK_INT, 0), SD_VARLINK_DEFINE_FIELD(PrefixLength, SD_VARLINK_INT, 0),
SD_VARLINK_FIELD_COMMENT("Configuration source for this address"), SD_VARLINK_FIELD_COMMENT("Configuration source for this address"),
SD_VARLINK_DEFINE_FIELD(ConfigSource, SD_VARLINK_STRING, 0), SD_VARLINK_DEFINE_FIELD(ConfigSource, SD_VARLINK_STRING, 0),
SD_VARLINK_FIELD_COMMENT("Address of the configuration provider (DHCP server, router, etc.)"), SD_VARLINK_DEFINE_IN_ADDR_WITH_STRING_FIELD(ConfigProvider, "Address of the configuration provider (DHCP server, router, etc.)", SD_VARLINK_NULLABLE),
SD_VARLINK_DEFINE_FIELD(ConfigProvider, SD_VARLINK_INT, SD_VARLINK_ARRAY|SD_VARLINK_NULLABLE), SD_VARLINK_DEFINE_IN_ADDR_WITH_STRING_FIELD(Broadcast, "Broadcast address for IPv4 networks", SD_VARLINK_NULLABLE),
SD_VARLINK_FIELD_COMMENT("Broadcast address for IPv4 networks"),
SD_VARLINK_DEFINE_FIELD(Broadcast, SD_VARLINK_INT, SD_VARLINK_ARRAY|SD_VARLINK_NULLABLE),
SD_VARLINK_FIELD_COMMENT("Address scope (RT_SCOPE_* value)"), SD_VARLINK_FIELD_COMMENT("Address scope (RT_SCOPE_* value)"),
SD_VARLINK_DEFINE_FIELD(Scope, SD_VARLINK_INT, SD_VARLINK_NULLABLE), SD_VARLINK_DEFINE_FIELD(Scope, SD_VARLINK_INT, SD_VARLINK_NULLABLE),
SD_VARLINK_FIELD_COMMENT("Human-readable scope string"), SD_VARLINK_FIELD_COMMENT("Human-readable scope string"),
@ -335,8 +323,7 @@ static SD_VARLINK_DEFINE_STRUCT_TYPE(
Neighbor, Neighbor,
SD_VARLINK_FIELD_COMMENT("Address family (AF_INET or AF_INET6)"), SD_VARLINK_FIELD_COMMENT("Address family (AF_INET or AF_INET6)"),
SD_VARLINK_DEFINE_FIELD(Family, SD_VARLINK_INT, 0), SD_VARLINK_DEFINE_FIELD(Family, SD_VARLINK_INT, 0),
SD_VARLINK_FIELD_COMMENT("IP address of the neighbor"), SD_VARLINK_DEFINE_IN_ADDR_WITH_STRING_FIELD(Destination, "IP address of the neighbor", 0),
SD_VARLINK_DEFINE_FIELD(Destination, SD_VARLINK_INT, SD_VARLINK_ARRAY),
SD_VARLINK_FIELD_COMMENT("Link layer (MAC) address of the neighbor"), SD_VARLINK_FIELD_COMMENT("Link layer (MAC) address of the neighbor"),
SD_VARLINK_DEFINE_FIELD(LinkLayerAddress, SD_VARLINK_INT, SD_VARLINK_ARRAY), SD_VARLINK_DEFINE_FIELD(LinkLayerAddress, SD_VARLINK_INT, SD_VARLINK_ARRAY),
SD_VARLINK_FIELD_COMMENT("Configuration source for this neighbor entry"), SD_VARLINK_FIELD_COMMENT("Configuration source for this neighbor entry"),
@ -366,12 +353,14 @@ static SD_VARLINK_DEFINE_STRUCT_TYPE(
DHCPv6ClientPD, DHCPv6ClientPD,
SD_VARLINK_FIELD_COMMENT("Delegated IPv6 prefix"), SD_VARLINK_FIELD_COMMENT("Delegated IPv6 prefix"),
SD_VARLINK_DEFINE_FIELD(Prefix, SD_VARLINK_INT, SD_VARLINK_ARRAY|SD_VARLINK_NULLABLE), SD_VARLINK_DEFINE_FIELD(Prefix, SD_VARLINK_INT, SD_VARLINK_ARRAY|SD_VARLINK_NULLABLE),
SD_VARLINK_FIELD_COMMENT("Human-readable delegated IPv6 prefix"),
SD_VARLINK_DEFINE_FIELD(PrefixString, SD_VARLINK_STRING, SD_VARLINK_NULLABLE),
SD_VARLINK_FIELD_COMMENT("Length of the delegated prefix in bits"), SD_VARLINK_FIELD_COMMENT("Length of the delegated prefix in bits"),
SD_VARLINK_DEFINE_FIELD(PrefixLength, SD_VARLINK_INT, 0), SD_VARLINK_DEFINE_FIELD(PrefixLength, SD_VARLINK_INT, 0),
SD_VARLINK_FIELD_COMMENT("Preferred lifetime of the prefix in microseconds"), SD_VARLINK_FIELD_COMMENT("Preferred lifetime of the prefix in microseconds"),
SD_VARLINK_DEFINE_FIELD(PreferredLifetimeUsec, SD_VARLINK_INT, SD_VARLINK_NULLABLE), SD_VARLINK_DEFINE_FIELD(PreferredLifetimeUSec, SD_VARLINK_INT, SD_VARLINK_NULLABLE),
SD_VARLINK_FIELD_COMMENT("Valid lifetime of the prefix in microseconds"), SD_VARLINK_FIELD_COMMENT("Valid lifetime of the prefix in microseconds"),
SD_VARLINK_DEFINE_FIELD(ValidLifetimeUsec, SD_VARLINK_INT, SD_VARLINK_NULLABLE)); SD_VARLINK_DEFINE_FIELD(ValidLifetimeUSec, SD_VARLINK_INT, SD_VARLINK_NULLABLE));
static SD_VARLINK_DEFINE_STRUCT_TYPE( static SD_VARLINK_DEFINE_STRUCT_TYPE(
DHCPv6ClientVendorOption, DHCPv6ClientVendorOption,
@ -397,8 +386,7 @@ static SD_VARLINK_DEFINE_STRUCT_TYPE(
DHCPServerLease, DHCPServerLease,
SD_VARLINK_FIELD_COMMENT("DHCP client identifier"), SD_VARLINK_FIELD_COMMENT("DHCP client identifier"),
SD_VARLINK_DEFINE_FIELD(ClientId, SD_VARLINK_INT, SD_VARLINK_ARRAY|SD_VARLINK_NULLABLE), SD_VARLINK_DEFINE_FIELD(ClientId, SD_VARLINK_INT, SD_VARLINK_ARRAY|SD_VARLINK_NULLABLE),
SD_VARLINK_FIELD_COMMENT("Address assigned to the client"), SD_VARLINK_DEFINE_IN_ADDR_WITH_STRING_FIELD(Address, "Address assigned to the client", SD_VARLINK_NULLABLE),
SD_VARLINK_DEFINE_FIELD(Address, SD_VARLINK_INT, SD_VARLINK_ARRAY|SD_VARLINK_NULLABLE),
SD_VARLINK_FIELD_COMMENT("Hostname provided by the DHCP client"), SD_VARLINK_FIELD_COMMENT("Hostname provided by the DHCP client"),
SD_VARLINK_DEFINE_FIELD(Hostname, SD_VARLINK_STRING, SD_VARLINK_NULLABLE), SD_VARLINK_DEFINE_FIELD(Hostname, SD_VARLINK_STRING, SD_VARLINK_NULLABLE),
SD_VARLINK_FIELD_COMMENT("Hardware address type (ARPHRD_* value)"), SD_VARLINK_FIELD_COMMENT("Hardware address type (ARPHRD_* value)"),
@ -459,8 +447,7 @@ static SD_VARLINK_DEFINE_STRUCT_TYPE(
SD_VARLINK_DEFINE_FIELD(PermanentHardwareAddress, SD_VARLINK_INT, SD_VARLINK_ARRAY|SD_VARLINK_NULLABLE), SD_VARLINK_DEFINE_FIELD(PermanentHardwareAddress, SD_VARLINK_INT, SD_VARLINK_ARRAY|SD_VARLINK_NULLABLE),
SD_VARLINK_FIELD_COMMENT("Broadcast address for Ethernet interfaces"), SD_VARLINK_FIELD_COMMENT("Broadcast address for Ethernet interfaces"),
SD_VARLINK_DEFINE_FIELD(BroadcastAddress, SD_VARLINK_INT, SD_VARLINK_ARRAY|SD_VARLINK_NULLABLE), SD_VARLINK_DEFINE_FIELD(BroadcastAddress, SD_VARLINK_INT, SD_VARLINK_ARRAY|SD_VARLINK_NULLABLE),
SD_VARLINK_FIELD_COMMENT("IPv6 link-local address"), SD_VARLINK_DEFINE_IN_ADDR_WITH_STRING_FIELD(IPv6LinkLocalAddress, "IPv6 link-local address", SD_VARLINK_NULLABLE),
SD_VARLINK_DEFINE_FIELD(IPv6LinkLocalAddress, SD_VARLINK_INT, SD_VARLINK_ARRAY|SD_VARLINK_NULLABLE),
SD_VARLINK_FIELD_COMMENT("Wireless LAN interface type (NL80211_IFTYPE_* value)"), SD_VARLINK_FIELD_COMMENT("Wireless LAN interface type (NL80211_IFTYPE_* value)"),
SD_VARLINK_DEFINE_FIELD(WirelessLanInterfaceType, SD_VARLINK_INT, SD_VARLINK_NULLABLE), SD_VARLINK_DEFINE_FIELD(WirelessLanInterfaceType, SD_VARLINK_INT, SD_VARLINK_NULLABLE),
SD_VARLINK_FIELD_COMMENT("Human-readable wireless LAN interface type"), SD_VARLINK_FIELD_COMMENT("Human-readable wireless LAN interface type"),

View File

@ -274,6 +274,8 @@ SD_VARLINK_DEFINE_STRUCT_TYPE(
SD_VARLINK_DEFINE_FIELD(negativeTrustAnchors, SD_VARLINK_STRING, SD_VARLINK_ARRAY|SD_VARLINK_NULLABLE), SD_VARLINK_DEFINE_FIELD(negativeTrustAnchors, SD_VARLINK_STRING, SD_VARLINK_ARRAY|SD_VARLINK_NULLABLE),
SD_VARLINK_FIELD_COMMENT("DNSSEC mode."), SD_VARLINK_FIELD_COMMENT("DNSSEC mode."),
SD_VARLINK_DEFINE_FIELD(dnssec, SD_VARLINK_STRING, SD_VARLINK_NULLABLE), SD_VARLINK_DEFINE_FIELD(dnssec, SD_VARLINK_STRING, SD_VARLINK_NULLABLE),
SD_VARLINK_FIELD_COMMENT("Indicates if the current DNS server supports DNSSEC. Always false if DNSSEC mode is \"no\"."),
SD_VARLINK_DEFINE_FIELD(dnssecSupported, SD_VARLINK_BOOL, SD_VARLINK_NULLABLE),
SD_VARLINK_FIELD_COMMENT("DNSOverTLS mode."), SD_VARLINK_FIELD_COMMENT("DNSOverTLS mode."),
SD_VARLINK_DEFINE_FIELD_BY_TYPE(dnsOverTLS, DNSOverTLSMode, SD_VARLINK_NULLABLE), SD_VARLINK_DEFINE_FIELD_BY_TYPE(dnsOverTLS, DNSOverTLSMode, SD_VARLINK_NULLABLE),
SD_VARLINK_FIELD_COMMENT("LLMNR support."), SD_VARLINK_FIELD_COMMENT("LLMNR support."),

View File

@ -34,6 +34,7 @@ static SD_VARLINK_DEFINE_METHOD(
SD_VARLINK_DEFINE_INPUT_BY_TYPE(class, ImageClass, SD_VARLINK_NULLABLE), SD_VARLINK_DEFINE_INPUT_BY_TYPE(class, ImageClass, SD_VARLINK_NULLABLE),
SD_VARLINK_DEFINE_INPUT(force, SD_VARLINK_BOOL, SD_VARLINK_NULLABLE), SD_VARLINK_DEFINE_INPUT(force, SD_VARLINK_BOOL, SD_VARLINK_NULLABLE),
SD_VARLINK_DEFINE_INPUT(noReload, SD_VARLINK_BOOL, SD_VARLINK_NULLABLE), SD_VARLINK_DEFINE_INPUT(noReload, SD_VARLINK_BOOL, SD_VARLINK_NULLABLE),
SD_VARLINK_DEFINE_INPUT(alwaysRefresh, SD_VARLINK_BOOL, SD_VARLINK_NULLABLE),
SD_VARLINK_DEFINE_INPUT(noexec, SD_VARLINK_BOOL, SD_VARLINK_NULLABLE), SD_VARLINK_DEFINE_INPUT(noexec, SD_VARLINK_BOOL, SD_VARLINK_NULLABLE),
VARLINK_DEFINE_POLKIT_INPUT); VARLINK_DEFINE_POLKIT_INPUT);

View File

@ -8,6 +8,7 @@
#include <sys/mount.h> #include <sys/mount.h>
#include <unistd.h> #include <unistd.h>
#include "sd-json.h"
#include "sd-varlink.h" #include "sd-varlink.h"
#include "argv-util.h" #include "argv-util.h"
@ -86,6 +87,17 @@ static const char* const mutable_mode_table[_MUTABLE_MAX] = {
DEFINE_PRIVATE_STRING_TABLE_LOOKUP_WITH_BOOLEAN(mutable_mode, MutableMode, MUTABLE_YES); DEFINE_PRIVATE_STRING_TABLE_LOOKUP_WITH_BOOLEAN(mutable_mode, MutableMode, MUTABLE_YES);
enum {
MERGE_NOTHING_FOUND,
MERGE_MOUNTED,
MERGE_SKIP_REFRESH,
};
enum {
MERGE_EXIT_NOTHING_FOUND = 123,
MERGE_EXIT_SKIP_REFRESH = 124,
};
static char **arg_hierarchies = NULL; /* "/usr" + "/opt" by default for sysext and /etc by default for confext */ static char **arg_hierarchies = NULL; /* "/usr" + "/opt" by default for sysext and /etc by default for confext */
static char *arg_root = NULL; static char *arg_root = NULL;
static sd_json_format_flags_t arg_json_format_flags = SD_JSON_FORMAT_OFF; static sd_json_format_flags_t arg_json_format_flags = SD_JSON_FORMAT_OFF;
@ -93,6 +105,7 @@ static PagerFlags arg_pager_flags = 0;
static bool arg_legend = true; static bool arg_legend = true;
static bool arg_force = false; static bool arg_force = false;
static bool arg_no_reload = false; static bool arg_no_reload = false;
static bool arg_always_refresh = false;
static int arg_noexec = -1; static int arg_noexec = -1;
static ImagePolicy *arg_image_policy = NULL; static ImagePolicy *arg_image_policy = NULL;
static bool arg_image_policy_set = false; /* Tracks initialization */ static bool arg_image_policy_set = false; /* Tracks initialization */
@ -1037,23 +1050,25 @@ static int resolve_mutable_directory(
} }
if (IN_SET(arg_mutable, MUTABLE_YES, MUTABLE_EPHEMERAL, MUTABLE_EPHEMERAL_IMPORT)) { if (IN_SET(arg_mutable, MUTABLE_YES, MUTABLE_EPHEMERAL, MUTABLE_EPHEMERAL_IMPORT)) {
_cleanup_free_ char *path_in_root = NULL; _cleanup_close_ int path_fd = -EBADF, chmod_fd = -EBADF;
path_in_root = path_join(root, path); /* This also creates, e.g., /var/lib/extensions.mutable/usr if needed and all parent
if (!path_in_root) * directories plus it also works when the last part is a symlink to the real /usr but we
return log_oom(); * can't use chase_and_open here because it does not behave the same. */
r = chase(path, root, CHASE_AT_RESOLVE_IN_ROOT|CHASE_MKDIR_0755|CHASE_MUST_BE_DIRECTORY|CHASE_PREFIX_ROOT, /* ret_path */ NULL, &path_fd);
r = mkdir_p(path_in_root, 0700);
if (r < 0) if (r < 0)
return log_error_errno(r, "Failed to create a directory '%s': %m", path_in_root); return log_error_errno(r, "Failed to chase/create base directory '%s/%s': %m", strempty(root), skip_leading_slash(path));
_cleanup_close_ int atfd = open(path_in_root, O_DIRECTORY|O_CLOEXEC); chmod_fd = fd_reopen(path_fd, O_CLOEXEC|O_DIRECTORY);
if (atfd < 0) if (chmod_fd < 0)
return log_error_errno(errno, "Failed to open directory '%s': %m", path_in_root); return log_error_errno(chmod_fd, "Failed to reopen '%s/%s': %m", strempty(root), skip_leading_slash(path));
r = mac_selinux_fix_full(atfd, /* inode_path= */ NULL, hierarchy, /* flags= */ 0); if (fchmod(chmod_fd, hierarchy_mode) < 0)
return log_error_errno(errno, "Failed to chmod directory '%s/%s': %m", strempty(root), skip_leading_slash(path));
r = mac_selinux_fix_full(chmod_fd, /* inode_path= */ NULL, hierarchy, /* flags= */ 0);
if (r < 0) if (r < 0)
return log_error_errno(r, "Failed to fix SELinux label for '%s': %m", path_in_root); return log_error_errno(r, "Failed to fix SELinux label for '%s/%s': %m", strempty(root), skip_leading_slash(path));
} }
r = chase(path, root, CHASE_PREFIX_ROOT, &resolved_path, NULL); r = chase(path, root, CHASE_PREFIX_ROOT, &resolved_path, NULL);
@ -1489,6 +1504,30 @@ static int write_extensions_file(ImageClass image_class, char **extensions, cons
return 0; return 0;
} }
static int write_origin_file(ImageClass image_class, const char *origin_content, const char *meta_path, const char *hierarchy) {
_cleanup_free_ char *f = NULL;
int r;
assert(meta_path);
/* The origin file is compared to know if a refresh can be skipped (opt-in, used at service startup). */
f = path_join(meta_path, image_class_info[image_class].dot_directory_name, "origin");
if (!f)
return log_oom();
_cleanup_free_ char *hierarchy_path = path_join(hierarchy, image_class_info[image_class].dot_directory_name, image_class_info[image_class].short_identifier_plural);
if (!hierarchy_path)
return log_oom();
r = write_string_file_full(AT_FDCWD, f, strempty(origin_content),
WRITE_STRING_FILE_CREATE|WRITE_STRING_FILE_MKDIR_0755|WRITE_STRING_FILE_LABEL|WRITE_STRING_FILE_AVOID_NEWLINE,
/* ts= */ NULL, hierarchy_path);
if (r < 0)
return log_error_errno(r, "Failed to write origin meta file '%s': %m", f);
return 0;
}
static int write_dev_file(ImageClass image_class, const char *meta_path, const char *overlay_path, const char *hierarchy) { static int write_dev_file(ImageClass image_class, const char *meta_path, const char *overlay_path, const char *hierarchy) {
_cleanup_free_ char *f = NULL; _cleanup_free_ char *f = NULL;
struct stat st; struct stat st;
@ -1593,6 +1632,7 @@ static int write_work_dir_file(ImageClass image_class, const char *meta_path, co
static int store_info_in_meta( static int store_info_in_meta(
ImageClass image_class, ImageClass image_class,
char **extensions, char **extensions,
const char *origin_content,
const char *meta_path, const char *meta_path,
const char *overlay_path, const char *overlay_path,
const char *work_dir, const char *work_dir,
@ -1625,6 +1665,10 @@ static int store_info_in_meta(
if (r < 0) if (r < 0)
return r; return r;
r = write_origin_file(image_class, origin_content, meta_path, hierarchy);
if (r < 0)
return r;
r = write_dev_file(image_class, meta_path, overlay_path, hierarchy); r = write_dev_file(image_class, meta_path, overlay_path, hierarchy);
if (r < 0) if (r < 0)
return r; return r;
@ -1683,6 +1727,7 @@ static int merge_hierarchy(
int noexec, int noexec,
char **extensions, char **extensions,
char **paths, char **paths,
const char *origin_content,
const char *meta_path, const char *meta_path,
const char *overlay_path, const char *overlay_path,
const char *workspace_path) { const char *workspace_path) {
@ -1728,7 +1773,7 @@ static int merge_hierarchy(
if (r < 0) if (r < 0)
return r; return r;
r = store_info_in_meta(image_class, extensions, meta_path, overlay_path, op->work_dir, op->hierarchy, backing); r = store_info_in_meta(image_class, extensions, origin_content, meta_path, overlay_path, op->work_dir, op->hierarchy, backing);
if (r < 0) if (r < 0)
return r; return r;
@ -1780,19 +1825,29 @@ static int merge_subprocess(
ImageClass image_class, ImageClass image_class,
char **hierarchies, char **hierarchies,
bool force, bool force,
bool always_refresh,
int noexec, int noexec,
Hashmap *images, Hashmap *images,
const char *workspace) { const char *workspace) {
_cleanup_free_ char *host_os_release_id = NULL, *host_os_release_id_like = NULL, _cleanup_free_ char *host_os_release_id = NULL, *host_os_release_id_like = NULL,
*host_os_release_version_id = NULL, *host_os_release_api_level = NULL, *host_os_release_version_id = NULL, *host_os_release_api_level = NULL,
*filename = NULL; *filename = NULL, *old_origin_content = NULL,
*extensions_origin_content = NULL, *root_resolved = NULL;
_cleanup_strv_free_ char **extensions = NULL, **extensions_v = NULL, **paths = NULL; _cleanup_strv_free_ char **extensions = NULL, **extensions_v = NULL, **paths = NULL;
_cleanup_(sd_json_variant_unrefp) sd_json_variant *extensions_origin_entries = NULL,
*extensions_origin_json = NULL, *mutable_dir_entries = NULL;
size_t n_extensions = 0; size_t n_extensions = 0;
unsigned n_ignored = 0; unsigned n_ignored = 0;
Image *img; Image *img;
int r; int r;
if (!isempty(arg_root)) {
r = chase(arg_root, /* root= */ NULL, CHASE_MUST_BE_DIRECTORY, &root_resolved, /* ret_fd= */ NULL);
if (r < 0)
return log_error_errno(r, "Failed to resolve --root='%s': %m", strempty(arg_root));
}
assert(path_startswith(workspace, "/run/")); assert(path_startswith(workspace, "/run/"));
/* Mark the whole of /run as MS_SLAVE, so that we can mount stuff below it that doesn't show up on /* Mark the whole of /run as MS_SLAVE, so that we can mount stuff below it that doesn't show up on
@ -1830,7 +1885,8 @@ static int merge_subprocess(
/* Let's now mount all images */ /* Let's now mount all images */
HASHMAP_FOREACH(img, images) { HASHMAP_FOREACH(img, images) {
_cleanup_free_ char *p = NULL; _cleanup_free_ char *p = NULL, *path_without_root = NULL;
_cleanup_(sd_json_variant_unrefp) sd_json_variant *verity_hash = NULL;
p = path_join(workspace, image_class_info[image_class].short_identifier_plural, img->name); p = path_join(workspace, image_class_info[image_class].short_identifier_plural, img->name);
if (!p) if (!p)
@ -1929,6 +1985,12 @@ static int merge_subprocess(
if (r < 0) if (r < 0)
return r; return r;
if (iovec_is_set(&verity_settings.root_hash)) {
r = sd_json_variant_new_hex(&verity_hash, verity_settings.root_hash.iov_base, verity_settings.root_hash.iov_len);
if (r < 0)
return log_error_errno(r, "Failed to create origin verity entry for '%s': %m", img->name);
}
r = dissected_image_decrypt(m, arg_root, /* passphrase= */ NULL, &verity_settings, pick_image_policy(img), flags); r = dissected_image_decrypt(m, arg_root, /* passphrase= */ NULL, &verity_settings, pick_image_policy(img), flags);
if (r < 0) if (r < 0)
return r; return r;
@ -1998,6 +2060,60 @@ static int merge_subprocess(
if (r < 0) if (r < 0)
return log_oom(); return log_oom();
/* Encode extension image origin to check if we can skip the refresh.
* It can also be used to provide more detail in "systemd-sysext status". */
if (!isempty(arg_root)) {
const char *without_root = NULL;
without_root = path_startswith(img->path, root_resolved);
if (!isempty(without_root)) {
path_without_root = strjoin("/", without_root);
if (!path_without_root)
return log_oom();
}
}
if (!path_without_root) {
path_without_root = strdup(img->path);
if (!path_without_root)
return log_oom();
}
/* The verity hash is not available for all extension types, thus, but only as fallback,
* also include data to check for file/directory replacements through a file handle and
* unique mount ID (or inode and mount ID as fallback).
* A unique mount ID is best because st_dev gets reused too easily, e.g., by a loop dev
* mount. For the mount ID to be valid it has to be resolved before we enter the new mount
* namespace. Thus, here it wouldn't work and so instead it gets provided by the image
* dissect logic and handed over to this subprocess we are in.
* Online modification is not well supported with overlay mounts, so we don't do a file
* checksum nor do we recurse into a directory to look for touched files. If users want
* modifications to be picked up, they need to set the --always-refresh=yes flag (as will be
* printed out). */
_cleanup_(sd_json_variant_unrefp) sd_json_variant *origin_entry = NULL;
/* We suppress inclusion of weak identifiers when a strong one is there so that, e.g.,
* a confext image stored on /usr gets identified only by the verity hash instead of also
* the mount ID because that changes when a sysext overlay mount appears but since the
* verity hash is the same for the confext it can actually be reused. */
r = sd_json_buildo(&origin_entry,
SD_JSON_BUILD_PAIR_STRING("path", path_without_root),
SD_JSON_BUILD_PAIR_CONDITION(!!verity_hash, "verityHash", SD_JSON_BUILD_VARIANT(verity_hash)),
SD_JSON_BUILD_PAIR_CONDITION(!verity_hash, "onMountId", SD_JSON_BUILD_UNSIGNED(img->on_mount_id)),
SD_JSON_BUILD_PAIR_CONDITION(!verity_hash && !!img->fh, "fileHandle",
SD_JSON_BUILD_OBJECT(SD_JSON_BUILD_PAIR_INTEGER("type", img->fh->handle_type),
SD_JSON_BUILD_PAIR_HEX("handle", img->fh->f_handle,
img->fh->handle_bytes))),
SD_JSON_BUILD_PAIR_CONDITION(!verity_hash && !img->fh, "inode", SD_JSON_BUILD_UNSIGNED(img->inode)),
SD_JSON_BUILD_PAIR_CONDITION(!verity_hash, "crtime", SD_JSON_BUILD_UNSIGNED(img->crtime)),
SD_JSON_BUILD_PAIR_CONDITION(!verity_hash, "mtime", SD_JSON_BUILD_UNSIGNED(img->mtime)));
if (r < 0)
return log_error_errno(r, "Failed to create origin entry for '%s': %m", img->name);
r = sd_json_variant_set_field(&extensions_origin_entries, img->name, origin_entry);
if (r < 0)
return log_error_errno(r, "Failed to add origin entry for '%s': %m", img->name);
n_extensions++; n_extensions++;
} }
@ -2007,13 +2123,112 @@ static int merge_subprocess(
log_info("No suitable extensions found (%u ignored due to incompatible image(s)).", n_ignored); log_info("No suitable extensions found (%u ignored due to incompatible image(s)).", n_ignored);
else else
log_info("No extensions found."); log_info("No extensions found.");
return 0; return MERGE_NOTHING_FOUND;
} }
/* Order by version sort with strverscmp_improved() */ /* Order by version sort with strverscmp_improved() */
typesafe_qsort(extensions, n_extensions, strverscmp_improvedp); typesafe_qsort(extensions, n_extensions, strverscmp_improvedp);
typesafe_qsort(extensions_v, n_extensions, strverscmp_improvedp); typesafe_qsort(extensions_v, n_extensions, strverscmp_improvedp);
STRV_FOREACH(h, hierarchies) {
_cleanup_(overlayfs_paths_freep) OverlayFSPaths *op = NULL;
_cleanup_free_ char *f = NULL, *buf = NULL, *resolved = NULL, *mutable_directory_without_root = NULL;
/* The origin file includes the backing directories for mutable overlays. */
r = overlayfs_paths_new(*h, workspace, &op);
if (r < 0)
return r;
if (op->resolved_mutable_directory && !isempty(arg_root)) {
const char *without_root = NULL;
without_root = path_startswith(op->resolved_mutable_directory, root_resolved);
if (!isempty(without_root)) {
mutable_directory_without_root = strjoin("/", without_root);
if (!mutable_directory_without_root)
return log_oom();
}
}
if (!mutable_directory_without_root && op->resolved_mutable_directory) {
mutable_directory_without_root = strdup(op->resolved_mutable_directory);
if (!mutable_directory_without_root)
return log_oom();
}
if (mutable_directory_without_root) {
r = sd_json_variant_set_field_string(&mutable_dir_entries, *h, mutable_directory_without_root);
if (r < 0)
return log_error_errno(r, "Failed to add mutable directory to origin JSON entry: %m");
}
/* Find existing origin file for comparison. */
r = chase(*h, arg_root, CHASE_PREFIX_ROOT|CHASE_NONEXISTENT, &resolved, /* ret_fd= */ NULL);
if (r < 0)
return log_error_errno(r, "Failed to resolve hierarchy '%s%s': %m", strempty(arg_root), *h);
f = path_join(resolved, image_class_info[image_class].dot_directory_name, "origin");
if (!f)
return log_oom();
r = is_our_mount_point(image_class, resolved);
if (r < 0)
return r;
if (r == 0)
continue;
if (old_origin_content)
continue;
r = read_full_file(f, &buf, /* ret_size */ NULL);
if (r < 0) {
log_debug_errno(r, "Failed to open '%s', continuing search: %m", f);
continue;
}
old_origin_content = TAKE_PTR(buf);
}
r = sd_json_buildo(&extensions_origin_json,
SD_JSON_BUILD_PAIR_OBJECT("mutable",
SD_JSON_BUILD_PAIR_STRING("mode", mutable_mode_to_string(arg_mutable)),
SD_JSON_BUILD_PAIR_CONDITION(!!mutable_dir_entries,
"mutableDirs",
SD_JSON_BUILD_VARIANT(mutable_dir_entries))),
SD_JSON_BUILD_PAIR_CONDITION(!isempty(arg_overlayfs_mount_options),
"mountOptions",
SD_JSON_BUILD_STRING(arg_overlayfs_mount_options)),
SD_JSON_BUILD_PAIR_CONDITION(!!extensions_origin_entries,
"extensions",
SD_JSON_BUILD_VARIANT(extensions_origin_entries)));
if (r < 0)
return log_error_errno(r, "Failed to create extensions origin JSON object: %m");
r = sd_json_variant_format(extensions_origin_json, SD_JSON_FORMAT_PRETTY|SD_JSON_FORMAT_NEWLINE, &extensions_origin_content);
if (r < 0)
return log_error_errno(r, "Failed to format extension origin as JSON: %m");
log_debug("New extension origin entry (unordered):\n%s\n", extensions_origin_content);
if (old_origin_content) {
_cleanup_(sd_json_variant_unrefp) sd_json_variant *old_origin_json = NULL;
log_debug("Old extension origin entry (unordered):\n%s\n", old_origin_content);
r = sd_json_parse(old_origin_content, /* flags= */ 0, &old_origin_json, /* reterr_line= */ NULL, /* reterr_column= */ NULL);
if (r < 0)
return log_error_errno(r, "Failed to parse existing extension origin content: %m");
/* This works well with unordered entries. */
if (sd_json_variant_equal(extensions_origin_json, old_origin_json)) {
if (!always_refresh) {
/* This only happens during refresh, not merge, thus talk about refresh here. */
log_info("Skipping extension refresh because no change was found, use --always-refresh=yes to always do a refresh.");
return MERGE_SKIP_REFRESH;
}
log_debug("No change found based on origin entry but continuing as requested by --always-refresh=yes.");
} else
log_debug("Found changes based on origin entry, continuing with the refresh.");
}
if (n_extensions == 0) { if (n_extensions == 0) {
assert(arg_mutable != MUTABLE_NO); assert(arg_mutable != MUTABLE_NO);
log_info("No extensions found, proceeding in mutable mode."); log_info("No extensions found, proceeding in mutable mode.");
@ -2096,6 +2311,7 @@ static int merge_subprocess(
noexec, noexec,
extensions, extensions,
paths, paths,
extensions_origin_content,
meta_path, meta_path,
overlay_path, overlay_path,
merge_hierarchy_workspace); merge_hierarchy_workspace);
@ -2140,13 +2356,14 @@ static int merge_subprocess(
log_info("Merged extensions into '%s'.", resolved); log_info("Merged extensions into '%s'.", resolved);
} }
return 1; return MERGE_MOUNTED;
} }
static int merge(ImageClass image_class, static int merge(ImageClass image_class,
char **hierarchies, char **hierarchies,
bool force, bool force,
bool no_reload, bool no_reload,
bool always_refresh,
int noexec, int noexec,
Hashmap *images) { Hashmap *images) {
@ -2163,21 +2380,28 @@ static int merge(ImageClass image_class,
if (r == 0) { if (r == 0) {
/* Child with its own mount namespace */ /* Child with its own mount namespace */
r = merge_subprocess(image_class, hierarchies, force, noexec, images, "/run/systemd/sysext"); r = merge_subprocess(image_class, hierarchies, force, always_refresh, noexec, images, "/run/systemd/sysext");
if (r < 0)
_exit(EXIT_FAILURE);
/* Our namespace ceases to exist here, also implicitly detaching all temporary mounts we /* Our namespace ceases to exist here, also implicitly detaching all temporary mounts we
* created below /run. Nice! */ * created below /run. Nice! */
_exit(r > 0 ? EXIT_SUCCESS : 123); /* 123 means: didn't find any extensions */ if (r < 0)
_exit(EXIT_FAILURE);
if (r == MERGE_NOTHING_FOUND)
_exit(MERGE_EXIT_NOTHING_FOUND);
if (r == MERGE_SKIP_REFRESH)
_exit(MERGE_EXIT_SKIP_REFRESH);
_exit(EXIT_SUCCESS);
} }
r = pidref_wait_for_terminate_and_check("(sd-merge)", &pidref, WAIT_LOG_ABNORMAL); r = pidref_wait_for_terminate_and_check("(sd-merge)", &pidref, WAIT_LOG_ABNORMAL);
if (r < 0) if (r < 0)
return r; return r;
if (r == 123) /* exit code 123 means: didn't do anything */ if (r == MERGE_EXIT_NOTHING_FOUND)
return 0; return 0; /* Tell refresh to unmount */
if (r == MERGE_EXIT_SKIP_REFRESH)
return 1; /* Same return code as below when we have merged new */
if (r > 0) if (r > 0)
return log_error_errno(SYNTHETIC_ERRNO(EPROTO), "Failed to merge hierarchies"); return log_error_errno(SYNTHETIC_ERRNO(EPROTO), "Failed to merge hierarchies");
@ -2275,6 +2499,7 @@ static int verb_merge(int argc, char **argv, void *userdata) {
arg_hierarchies, arg_hierarchies,
arg_force, arg_force,
arg_no_reload, arg_no_reload,
arg_always_refresh,
arg_noexec, arg_noexec,
images); images);
} }
@ -2283,6 +2508,7 @@ typedef struct MethodMergeParameters {
const char *class; const char *class;
int force; int force;
int no_reload; int no_reload;
int always_refresh;
int noexec; int noexec;
} MethodMergeParameters; } MethodMergeParameters;
@ -2292,6 +2518,7 @@ static int parse_merge_parameters(sd_varlink *link, sd_json_variant *parameters,
{ "class", SD_JSON_VARIANT_STRING, sd_json_dispatch_const_string, offsetof(MethodMergeParameters, class), 0 }, { "class", SD_JSON_VARIANT_STRING, sd_json_dispatch_const_string, offsetof(MethodMergeParameters, class), 0 },
{ "force", SD_JSON_VARIANT_BOOLEAN, sd_json_dispatch_tristate, offsetof(MethodMergeParameters, force), 0 }, { "force", SD_JSON_VARIANT_BOOLEAN, sd_json_dispatch_tristate, offsetof(MethodMergeParameters, force), 0 },
{ "noReload", SD_JSON_VARIANT_BOOLEAN, sd_json_dispatch_tristate, offsetof(MethodMergeParameters, no_reload), 0 }, { "noReload", SD_JSON_VARIANT_BOOLEAN, sd_json_dispatch_tristate, offsetof(MethodMergeParameters, no_reload), 0 },
{ "alwaysRefresh", SD_JSON_VARIANT_BOOLEAN, sd_json_dispatch_tristate, offsetof(MethodMergeParameters, always_refresh), 0 },
{ "noexec", SD_JSON_VARIANT_BOOLEAN, sd_json_dispatch_tristate, offsetof(MethodMergeParameters, noexec), 0 }, { "noexec", SD_JSON_VARIANT_BOOLEAN, sd_json_dispatch_tristate, offsetof(MethodMergeParameters, noexec), 0 },
VARLINK_DISPATCH_POLKIT_FIELD, VARLINK_DISPATCH_POLKIT_FIELD,
{} {}
@ -2310,11 +2537,12 @@ static int vl_method_merge(sd_varlink *link, sd_json_variant *parameters, sd_var
MethodMergeParameters p = { MethodMergeParameters p = {
.force = -1, .force = -1,
.no_reload = -1, .no_reload = -1,
.always_refresh = -1,
.noexec = -1, .noexec = -1,
}; };
_cleanup_strv_free_ char **hierarchies = NULL; _cleanup_strv_free_ char **hierarchies = NULL;
ImageClass image_class = arg_image_class; ImageClass image_class = arg_image_class;
bool force, no_reload; bool force, no_reload, always_refresh;
int r, noexec; int r, noexec;
assert(link); assert(link);
@ -2329,6 +2557,7 @@ static int vl_method_merge(sd_varlink *link, sd_json_variant *parameters, sd_var
force = p.force >= 0 ? p.force : arg_force; force = p.force >= 0 ? p.force : arg_force;
no_reload = p.no_reload >= 0 ? p.no_reload : arg_no_reload; no_reload = p.no_reload >= 0 ? p.no_reload : arg_no_reload;
always_refresh = p.always_refresh >= 0 ? p.always_refresh : arg_always_refresh;
noexec = p.noexec >= 0 ? p.noexec : arg_noexec; noexec = p.noexec >= 0 ? p.noexec : arg_noexec;
r = varlink_verify_polkit_async( r = varlink_verify_polkit_async(
@ -2358,7 +2587,7 @@ static int vl_method_merge(sd_varlink *link, sd_json_variant *parameters, sd_var
if (r > 0) if (r > 0)
return sd_varlink_errorbo(link, "io.systemd.sysext.AlreadyMerged", SD_JSON_BUILD_PAIR_STRING("hierarchy", which)); return sd_varlink_errorbo(link, "io.systemd.sysext.AlreadyMerged", SD_JSON_BUILD_PAIR_STRING("hierarchy", which));
r = merge(image_class, hierarchies ?: arg_hierarchies, force, no_reload, noexec, images); r = merge(image_class, hierarchies ?: arg_hierarchies, force, no_reload, always_refresh, noexec, images);
if (r < 0) if (r < 0)
return r; return r;
@ -2370,6 +2599,7 @@ static int refresh(
char **hierarchies, char **hierarchies,
bool force, bool force,
bool no_reload, bool no_reload,
bool always_refresh,
int noexec) { int noexec) {
_cleanup_hashmap_free_ Hashmap *images = NULL; _cleanup_hashmap_free_ Hashmap *images = NULL;
@ -2380,9 +2610,10 @@ static int refresh(
return r; return r;
/* Returns > 0 if it did something, i.e. a new overlayfs is mounted now. When it does so it /* Returns > 0 if it did something, i.e. a new overlayfs is mounted now. When it does so it
* implicitly unmounts any overlayfs placed there before. Returns == 0 if it did nothing, i.e. no * implicitly unmounts any overlayfs placed there before. It also returns == 1 if there were
* no changes found to apply and the mount stays intact. Returns == 0 if it did nothing, i.e. no
* extension images found. In this case the old overlayfs remains in place if there was one. */ * extension images found. In this case the old overlayfs remains in place if there was one. */
r = merge(image_class, hierarchies, force, no_reload, noexec, images); r = merge(image_class, hierarchies, force, no_reload, always_refresh, noexec, images);
if (r < 0) if (r < 0)
return r; return r;
if (r == 0) /* No images found? Then unmerge. The goal of --refresh is after all that after having if (r == 0) /* No images found? Then unmerge. The goal of --refresh is after all that after having
@ -2394,7 +2625,8 @@ static int refresh(
* 1. If an overlayfs was mounted before and no extensions exist anymore, we'll have unmerged things. * 1. If an overlayfs was mounted before and no extensions exist anymore, we'll have unmerged things.
* *
* 2. If an overlayfs was mounted before, and there are still extensions installed' we'll have * 2. If an overlayfs was mounted before, and there are still extensions installed' we'll have
* unmerged and then merged things again. * unmerged and then merged things again or we have skipped the refresh because no changes
* were found.
* *
* 3. If an overlayfs so far wasn't mounted, and there are extensions installed, we'll have it * 3. If an overlayfs so far wasn't mounted, and there are extensions installed, we'll have it
* mounted now. * mounted now.
@ -2418,6 +2650,7 @@ static int verb_refresh(int argc, char **argv, void *userdata) {
arg_hierarchies, arg_hierarchies,
arg_force, arg_force,
arg_no_reload, arg_no_reload,
arg_always_refresh,
arg_noexec); arg_noexec);
} }
@ -2426,12 +2659,13 @@ static int vl_method_refresh(sd_varlink *link, sd_json_variant *parameters, sd_v
MethodMergeParameters p = { MethodMergeParameters p = {
.force = -1, .force = -1,
.no_reload = -1, .no_reload = -1,
.always_refresh = -1,
.noexec = -1, .noexec = -1,
}; };
Hashmap **polkit_registry = ASSERT_PTR(userdata); Hashmap **polkit_registry = ASSERT_PTR(userdata);
_cleanup_strv_free_ char **hierarchies = NULL; _cleanup_strv_free_ char **hierarchies = NULL;
ImageClass image_class = arg_image_class; ImageClass image_class = arg_image_class;
bool force, no_reload; bool force, no_reload, always_refresh;
int r, noexec; int r, noexec;
assert(link); assert(link);
@ -2446,6 +2680,7 @@ static int vl_method_refresh(sd_varlink *link, sd_json_variant *parameters, sd_v
force = p.force >= 0 ? p.force : arg_force; force = p.force >= 0 ? p.force : arg_force;
no_reload = p.no_reload >= 0 ? p.no_reload : arg_no_reload; no_reload = p.no_reload >= 0 ? p.no_reload : arg_no_reload;
always_refresh = p.always_refresh >= 0 ? p.always_refresh : arg_always_refresh;
noexec = p.noexec >= 0 ? p.noexec : arg_noexec; noexec = p.noexec >= 0 ? p.noexec : arg_noexec;
r = varlink_verify_polkit_async( r = varlink_verify_polkit_async(
@ -2461,7 +2696,7 @@ static int vl_method_refresh(sd_varlink *link, sd_json_variant *parameters, sd_v
if (r <= 0) if (r <= 0)
return r; return r;
r = refresh(image_class, hierarchies ?: arg_hierarchies, force, no_reload, noexec); r = refresh(image_class, hierarchies ?: arg_hierarchies, force, no_reload, always_refresh, noexec);
if (r < 0) if (r < 0)
return r; return r;
@ -2590,6 +2825,8 @@ static int verb_help(int argc, char **argv, void *userdata) {
" Generate JSON output\n" " Generate JSON output\n"
" --force Ignore version incompatibilities\n" " --force Ignore version incompatibilities\n"
" --no-reload Do not reload the service manager\n" " --no-reload Do not reload the service manager\n"
" --always-refresh=yes|no\n"
" Do not skip refresh when no changes were found\n"
" --image-policy=POLICY\n" " --image-policy=POLICY\n"
" Specify disk image dissection policy\n" " Specify disk image dissection policy\n"
" --noexec=BOOL Whether to mount extension overlay with noexec\n" " --noexec=BOOL Whether to mount extension overlay with noexec\n"
@ -2617,6 +2854,7 @@ static int parse_argv(int argc, char *argv[]) {
ARG_IMAGE_POLICY, ARG_IMAGE_POLICY,
ARG_NOEXEC, ARG_NOEXEC,
ARG_NO_RELOAD, ARG_NO_RELOAD,
ARG_ALWAYS_REFRESH,
ARG_MUTABLE, ARG_MUTABLE,
}; };
@ -2631,6 +2869,7 @@ static int parse_argv(int argc, char *argv[]) {
{ "image-policy", required_argument, NULL, ARG_IMAGE_POLICY }, { "image-policy", required_argument, NULL, ARG_IMAGE_POLICY },
{ "noexec", required_argument, NULL, ARG_NOEXEC }, { "noexec", required_argument, NULL, ARG_NOEXEC },
{ "no-reload", no_argument, NULL, ARG_NO_RELOAD }, { "no-reload", no_argument, NULL, ARG_NO_RELOAD },
{ "always-refresh", required_argument, NULL, ARG_ALWAYS_REFRESH },
{ "mutable", required_argument, NULL, ARG_MUTABLE }, { "mutable", required_argument, NULL, ARG_MUTABLE },
{} {}
}; };
@ -2698,6 +2937,12 @@ static int parse_argv(int argc, char *argv[]) {
arg_no_reload = true; arg_no_reload = true;
break; break;
case ARG_ALWAYS_REFRESH:
r = parse_boolean_argument("--always-refresh", optarg, &arg_always_refresh);
if (r < 0)
return r;
break;
case ARG_MUTABLE: case ARG_MUTABLE:
if (streq(optarg, "help")) { if (streq(optarg, "help")) {
if (arg_legend) if (arg_legend)

View File

@ -1587,6 +1587,94 @@ rm -rf "$fake_root/var/lib/extensions/test-extension.raw.v" "$fake_root/var/othe
# Done with the above vpick symlink tests for --root= and without # Done with the above vpick symlink tests for --root= and without
( init_trap
: "Check if refresh skips correctly"
fake_root=${roots_dir:+"$roots_dir/refresh-skip"}
hierarchy=/opt
findmnt --kernel=listmount >/dev/null || {
echo >&2 "Can't run test on old kernel, skipping test."
exit 0
}
prepare_root "$fake_root" "$hierarchy"
prepare_extension_image "$fake_root" "$hierarchy"
prepare_hierarchy "$fake_root" "$hierarchy"
run_systemd_sysext "$fake_root" merge
extension_verify_after_merge "$fake_root" "$hierarchy" -e -h
# The mountinfo ID gets reused and is useless here, we require a unique ID from listmount
MOUNTID1=$(findmnt --kernel=listmount -o UNIQ-ID --raw --noheadings --target "$fake_root$hierarchy")
run_systemd_sysext "$fake_root" refresh
extension_verify_after_merge "$fake_root" "$hierarchy" -e -h
MOUNTID2=$(findmnt --kernel=listmount -o UNIQ-ID --raw --noheadings --target "$fake_root$hierarchy")
if [ "$MOUNTID1" != "$MOUNTID2" ]; then
echo >&2 "Unexpected remount with 'refresh'"
exit 1
fi
rm -rf "$fake_root/var/lib/extensions/test-extension2"
cp -ar "$fake_root/var/lib/extensions/test-extension" "$fake_root/var/lib/extensions/test-extension2"
rm -rf "$fake_root/var/lib/extensions/test-extension"
mv "$fake_root/var/lib/extensions/test-extension2" "$fake_root/var/lib/extensions/test-extension"
run_systemd_sysext "$fake_root" refresh
extension_verify_after_merge "$fake_root" "$hierarchy" -e -h
MOUNTID3=$(findmnt --kernel=listmount -o UNIQ-ID --raw --noheadings --target "$fake_root$hierarchy")
if [ "$MOUNTID2" = "$MOUNTID3" ]; then
echo >&2 "Unexpected skip with 'refresh'"
exit 1
fi
run_systemd_sysext "$fake_root" unmerge
extension_verify_after_unmerge "$fake_root" "$hierarchy" -h
)
( init_trap
: "Check that refresh does a skip if verity image changes file handle but has same hash"
fake_root=${roots_dir:+"$roots_dir/refresh-skip-verity-filehandle-same-hash"}
hierarchy=/opt
# On OpenSUSE Tumbleweed EROFS is not supported
if [ -e /usr/lib/modprobe.d/60-blacklist_fs-erofs.conf ]; then
echo >&2 "Skipping test due to missing erofs support"
exit 0
fi
findmnt --kernel=listmount >/dev/null || {
echo >&2 "Can't run test on old kernel, skipping test."
exit 0
}
prepare_root "$fake_root" "$hierarchy"
prepare_extension_image_raw_verity "$fake_root" "$hierarchy"
prepare_hierarchy "$fake_root" "$hierarchy"
run_systemd_sysext "$fake_root" merge
extension_verify_after_merge "$fake_root" "$hierarchy" -e -h
# The mountinfo ID gets reused and is useless here, we require a unique ID from listmount
MOUNTID1=$(findmnt --kernel=listmount -o UNIQ-ID --raw --noheadings --target "$fake_root$hierarchy")
run_systemd_sysext "$fake_root" refresh
extension_verify_after_merge "$fake_root" "$hierarchy" -e -h
MOUNTID2=$(findmnt --kernel=listmount -o UNIQ-ID --raw --noheadings --target "$fake_root$hierarchy")
if [ "$MOUNTID1" != "$MOUNTID2" ]; then
echo >&2 "Unexpected remount with 'refresh'"
exit 1
fi
# Force a new file handle (get a new inode)
mv "$fake_root/var/lib/extensions/test-extension.raw" "$fake_root/var/lib/extensions/test-extension2.raw"
cp "$fake_root/var/lib/extensions/test-extension2.raw" "$fake_root/var/lib/extensions/test-extension.raw"
rm "$fake_root/var/lib/extensions/test-extension2.raw"
run_systemd_sysext "$fake_root" refresh
extension_verify_after_merge "$fake_root" "$hierarchy" -e -h
MOUNTID3=$(findmnt --kernel=listmount -o UNIQ-ID --raw --noheadings --target "$fake_root$hierarchy")
if [ "$MOUNTID2" != "$MOUNTID3" ]; then
echo >&2 "Unexpected remount with 'refresh' after verity image file handle changed"
exit 1
fi
run_systemd_sysext "$fake_root" unmerge
extension_verify_after_unmerge "$fake_root" "$hierarchy" -h
)
} # End of run_sysext_tests } # End of run_sysext_tests