mirror of
https://github.com/systemd/systemd
synced 2026-03-14 00:54:46 +01:00
Compare commits
22 Commits
5a35a6d9d4
...
b3d73bc68e
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b3d73bc68e | ||
|
|
cf27c70d70 | ||
|
|
13d82876f5 | ||
|
|
89591e04a2 | ||
|
|
23115eeaf1 | ||
|
|
2e4c2f8df7 | ||
|
|
a4750a6612 | ||
|
|
480ebdfddd | ||
|
|
67746cd3b2 | ||
|
|
d076d0f94f | ||
|
|
a56366182e | ||
|
|
7bfc82bf5a | ||
|
|
8894f29a4e | ||
|
|
d9757721ab | ||
|
|
8731d33b58 | ||
|
|
3afb9c20b6 | ||
|
|
e90180f50c | ||
|
|
4a8f4efd44 | ||
|
|
d3b60567d7 | ||
|
|
fb0bd15d15 | ||
|
|
271f7630a2 | ||
|
|
9b2f263f30 |
@ -371,6 +371,19 @@
|
||||
<xi:include href="version-info.xml" xpointer="v248"/></listitem>
|
||||
</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>
|
||||
<term><option>--image-policy=<replaceable>policy</replaceable></option></term>
|
||||
|
||||
|
||||
@ -36,6 +36,7 @@ _systemd-sysext() {
|
||||
[ARG]='--root
|
||||
--json
|
||||
--noexec
|
||||
--always-refresh
|
||||
--image-policy
|
||||
--mutable'
|
||||
)
|
||||
|
||||
@ -29,7 +29,7 @@ _systemd_vmspawn() {
|
||||
local i verb comps
|
||||
|
||||
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'
|
||||
[BOOL]='--kvm --vsock --tpm --secure-boot --discard-disk --register --pass-ssh-key'
|
||||
[FIRMWARE]='--firmware'
|
||||
|
||||
@ -50,12 +50,13 @@ int name_to_handle_at_loop(
|
||||
const char *path,
|
||||
struct file_handle **ret_handle,
|
||||
int *ret_mnt_id,
|
||||
uint64_t *ret_unique_mnt_id,
|
||||
int flags) {
|
||||
|
||||
size_t n = ORIGINAL_MAX_HANDLE_SZ;
|
||||
|
||||
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
|
||||
* 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 (;;) {
|
||||
_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);
|
||||
if (!h)
|
||||
@ -79,11 +81,18 @@ int name_to_handle_at_loop(
|
||||
|
||||
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)
|
||||
*ret_handle = TAKE_PTR(h);
|
||||
|
||||
if (ret_unique_mnt_id)
|
||||
*ret_unique_mnt_id = unique_mnt_id;
|
||||
if (ret_mnt_id)
|
||||
*ret_mnt_id = mnt_id;
|
||||
|
||||
@ -92,13 +101,16 @@ int name_to_handle_at_loop(
|
||||
if (errno != EOVERFLOW)
|
||||
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
|
||||
* 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. */
|
||||
|
||||
*ret_mnt_id = mnt_id;
|
||||
if (ret_unique_mnt_id)
|
||||
*ret_unique_mnt_id = unique_mnt_id;
|
||||
if (ret_mnt_id)
|
||||
*ret_mnt_id = mnt_id;
|
||||
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
|
||||
* (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))
|
||||
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) {
|
||||
@ -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. */
|
||||
|
||||
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)
|
||||
return r;
|
||||
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;
|
||||
}
|
||||
|
||||
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 r;
|
||||
|
||||
@ -259,6 +331,28 @@ int path_get_mnt_id_at(int dir_fd, const char *path, int *ret) {
|
||||
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) {
|
||||
const char *x;
|
||||
|
||||
|
||||
@ -34,8 +34,9 @@
|
||||
|
||||
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_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);
|
||||
static inline int path_to_handle_u64(const char *path, uint64_t *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);
|
||||
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);
|
||||
static inline int path_get_mnt_id(const char *path, int *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 path_is_mount_point_full(const char *path, const char *root, int flags);
|
||||
|
||||
@ -17,3 +17,8 @@
|
||||
#ifndef AT_HANDLE_FID
|
||||
#define AT_HANDLE_FID AT_REMOVEDIR
|
||||
#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
|
||||
|
||||
@ -244,7 +244,7 @@ static int dhcp_server_lease_append_json(sd_dhcp_server_lease *lease, sd_json_va
|
||||
return sd_json_buildo(
|
||||
ret,
|
||||
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),
|
||||
SD_JSON_BUILD_PAIR_UNSIGNED("HardwareAddressType", lease->htype),
|
||||
SD_JSON_BUILD_PAIR_UNSIGNED("HardwareAddressLength", lease->hlen),
|
||||
|
||||
@ -190,6 +190,12 @@ enum {
|
||||
_JSON_BUILD_PAIR_TRISTATE_NON_NULL,
|
||||
_JSON_BUILD_PAIR_PIDREF_NON_NULL,
|
||||
_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,
|
||||
};
|
||||
@ -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_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_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_HEX(name, iov) SD_JSON_BUILD_PAIR(name, JSON_BUILD_IOVEC_HEX(iov))
|
||||
|
||||
@ -4741,7 +4741,7 @@ _public_ int sd_json_buildv(sd_json_variant **ret, va_list ap) {
|
||||
if (r < 0)
|
||||
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)
|
||||
goto finish;
|
||||
}
|
||||
@ -4896,6 +4896,207 @@ _public_ int sd_json_buildv(sd_json_variant **ret, va_list ap) {
|
||||
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: {
|
||||
sd_json_build_callback_t cb;
|
||||
void *userdata;
|
||||
|
||||
@ -116,7 +116,6 @@ systemd_networkd_extract_sources = files(
|
||||
)
|
||||
|
||||
systemd_networkd_wait_online_sources = files(
|
||||
'wait-online/dns-configuration.c',
|
||||
'wait-online/wait-online-link.c',
|
||||
'wait-online/wait-online-manager.c',
|
||||
'wait-online/wait-online.c',
|
||||
|
||||
@ -10,6 +10,7 @@
|
||||
#include "dhcp-server-lease-internal.h"
|
||||
#include "dhcp6-lease-internal.h"
|
||||
#include "extract-word.h"
|
||||
#include "in-addr-util.h"
|
||||
#include "ip-protocol-list.h"
|
||||
#include "json-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(
|
||||
&v,
|
||||
SD_JSON_BUILD_PAIR_INTEGER("Family", address->family),
|
||||
JSON_BUILD_PAIR_IN_ADDR("Address", &address->in_addr, address->family),
|
||||
JSON_BUILD_PAIR_IN_ADDR_NON_NULL("Peer", &address->in_addr_peer, address->family),
|
||||
JSON_BUILD_PAIR_IN_ADDR_WITH_STRING("Address", address->family, &address->in_addr),
|
||||
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_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)
|
||||
return r;
|
||||
|
||||
@ -67,7 +68,7 @@ static int address_append_json(Address *address, bool serializing, sd_json_varia
|
||||
|
||||
r = sd_json_variant_merge_objectbo(
|
||||
&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_STRING("ScopeString", scope),
|
||||
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(
|
||||
array,
|
||||
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),
|
||||
SD_JSON_BUILD_PAIR_STRING("ConfigSource", network_config_source_to_string(n->source)),
|
||||
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_INTEGER("Family", n->family),
|
||||
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)
|
||||
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(
|
||||
&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_STRING("FlagsString", strempty(flags)),
|
||||
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(
|
||||
&v,
|
||||
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),
|
||||
JSON_BUILD_PAIR_IN_ADDR_NON_NULL("Gateway", &route->nexthop.gw, route->nexthop.family),
|
||||
SD_JSON_BUILD_PAIR_CONDITION(route->src_prefixlen > 0,
|
||||
"Source", JSON_BUILD_IN_ADDR(&route->src, route->family)),
|
||||
JSON_BUILD_PAIR_IN_ADDR_WITH_STRING_NON_NULL("Gateway", route->nexthop.family, &route->nexthop.gw),
|
||||
JSON_BUILD_PAIR_IN_ADDR_WITH_STRING_NON_NULL("Source", route->family, route->src_prefixlen > 0 ? &route->src : NULL),
|
||||
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("Scope", route->scope),
|
||||
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),
|
||||
JSON_BUILD_PAIR_UNSIGNED_NON_ZERO("NextHopID", route->nexthop_id),
|
||||
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)
|
||||
return r;
|
||||
|
||||
@ -383,10 +383,10 @@ static int routing_policy_rule_append_json(RoutingPolicyRule *rule, sd_json_vari
|
||||
return sd_json_variant_append_arraybo(
|
||||
array,
|
||||
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),
|
||||
"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),
|
||||
"ToPrefixLength", SD_JSON_BUILD_UNSIGNED(rule->to.prefixlen)),
|
||||
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(
|
||||
array,
|
||||
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),
|
||||
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),
|
||||
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) {
|
||||
@ -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) {
|
||||
_cleanup_free_ char *address_str = NULL;
|
||||
int r;
|
||||
|
||||
assert(IN_SET(family, AF_INET, AF_INET6));
|
||||
assert(a);
|
||||
assert(array);
|
||||
|
||||
r = in_addr_to_string(family, a, &address_str);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
return sd_json_variant_append_arraybo(
|
||||
array,
|
||||
SD_JSON_BUILD_PAIR_INTEGER("Family", 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)),
|
||||
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) {
|
||||
@ -730,7 +738,7 @@ static int server_append_json_one_fqdn(int family, const char *fqdn, NetworkConf
|
||||
array,
|
||||
SD_JSON_BUILD_PAIR_STRING("Server", fqdn),
|
||||
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) {
|
||||
@ -829,7 +837,7 @@ static int domain_append_json(int family, const char *domain, NetworkConfigSourc
|
||||
array,
|
||||
SD_JSON_BUILD_PAIR_STRING("Domain", domain),
|
||||
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) {
|
||||
@ -1103,10 +1111,10 @@ static int pref64_append_json(Link *link, sd_json_variant **v) {
|
||||
SET_FOREACH(i, link->ndisc_pref64) {
|
||||
r = sd_json_variant_append_arraybo(
|
||||
&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),
|
||||
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)
|
||||
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;
|
||||
struct in6_addr prefix;
|
||||
uint8_t prefix_len;
|
||||
_cleanup_free_ char *prefix_str = NULL;
|
||||
|
||||
r = sd_dhcp6_lease_get_pd_prefix(link->dhcp6_lease, &prefix, &prefix_len);
|
||||
if (r < 0)
|
||||
@ -1232,9 +1241,16 @@ static int dhcp6_client_pd_append_json(Link *link, sd_json_variant **v) {
|
||||
if (r < 0)
|
||||
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(
|
||||
&array,
|
||||
JSON_BUILD_PAIR_IN6_ADDR("Prefix", &prefix),
|
||||
JSON_BUILD_PAIR_STRING_NON_EMPTY("PrefixString", prefix_str),
|
||||
SD_JSON_BUILD_PAIR_UNSIGNED("PrefixLength", prefix_len),
|
||||
JSON_BUILD_PAIR_FINITE_USEC("PreferredLifetimeUSec", lifetime_preferred_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;
|
||||
const struct in_addr *br_addresses;
|
||||
size_t n_br_addresses = 0;
|
||||
_cleanup_free_ char *prefix_str = NULL;
|
||||
int r;
|
||||
|
||||
assert(link);
|
||||
@ -1356,6 +1373,12 @@ static int dhcp_client_pd_append_json(Link *link, sd_json_variant **v) {
|
||||
if (r < 0)
|
||||
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) {
|
||||
r = sd_json_variant_append_arrayb(&addresses, JSON_BUILD_IN4_ADDR(br_address));
|
||||
if (r < 0)
|
||||
@ -1365,6 +1388,7 @@ static int dhcp_client_pd_append_json(Link *link, sd_json_variant **v) {
|
||||
r = sd_json_buildo(
|
||||
&array,
|
||||
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("IPv4MaskLength", ipv4masklen),
|
||||
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("PermanentHardwareAddress", &link->permanent_hw_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 */
|
||||
SD_JSON_BUILD_PAIR_CONDITION(link->wlan_iftype > 0, "WirelessLanInterfaceType",
|
||||
SD_JSON_BUILD_UNSIGNED(link->wlan_iftype)),
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@ -2055,6 +2055,7 @@ static int dns_configuration_json_append(
|
||||
Set *negative_trust_anchors,
|
||||
Set *dns_scopes,
|
||||
DnssecMode dnssec_mode,
|
||||
bool dnssec_supported,
|
||||
DnsOverTlsMode dns_over_tls_mode,
|
||||
ResolveSupport llmnr_support,
|
||||
ResolveSupport mdns_support,
|
||||
@ -2138,6 +2139,7 @@ static int dns_configuration_json_append(
|
||||
SD_JSON_BUILD_PAIR_CONDITION(!set_isempty(negative_trust_anchors),
|
||||
"negativeTrustAnchors",
|
||||
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("dnsOverTLS", dns_over_tls_mode_to_string(dns_over_tls_mode)),
|
||||
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,
|
||||
scopes,
|
||||
manager_get_dnssec_mode(m),
|
||||
manager_dnssec_supported(m),
|
||||
manager_get_dns_over_tls_mode(m),
|
||||
m->llmnr_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,
|
||||
scopes,
|
||||
link_get_dnssec_mode(l),
|
||||
link_dnssec_supported(l),
|
||||
link_get_dns_over_tls_mode(l),
|
||||
link_get_llmnr_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,
|
||||
scopes,
|
||||
/* dnssec_mode= */ _DNSSEC_MODE_INVALID,
|
||||
/* dnssec_supported= */ false,
|
||||
/* dns_over_tls_mode= */ _DNS_OVER_TLS_MODE_INVALID,
|
||||
/* llmnr_support= */ _RESOLVE_SUPPORT_INVALID,
|
||||
/* mdns_support= */ _RESOLVE_SUPPORT_INVALID,
|
||||
|
||||
@ -35,6 +35,7 @@
|
||||
#include "lock-util.h"
|
||||
#include "log.h"
|
||||
#include "loop-util.h"
|
||||
#include "mountpoint-util.h"
|
||||
#include "namespace-util.h"
|
||||
#include "nsresource.h"
|
||||
#include "nulstr-util.h"
|
||||
@ -148,6 +149,8 @@ static Image* image_free(Image *i) {
|
||||
free(i->name);
|
||||
free(i->path);
|
||||
|
||||
free(i->fh);
|
||||
|
||||
free(i->hostname);
|
||||
strv_free(i->machine_info);
|
||||
strv_free(i->os_release);
|
||||
@ -249,6 +252,9 @@ static int image_new(
|
||||
bool read_only,
|
||||
usec_t crtime,
|
||||
usec_t mtime,
|
||||
struct file_handle *fh,
|
||||
uint64_t on_mount_id,
|
||||
uint64_t inode,
|
||||
Image **ret) {
|
||||
|
||||
_cleanup_(image_unrefp) Image *i = NULL;
|
||||
@ -270,12 +276,20 @@ static int image_new(
|
||||
.read_only = read_only,
|
||||
.crtime = crtime,
|
||||
.mtime = mtime,
|
||||
.on_mount_id = on_mount_id,
|
||||
.inode = inode,
|
||||
.usage = UINT64_MAX,
|
||||
.usage_exclusive = UINT64_MAX,
|
||||
.limit = UINT64_MAX,
|
||||
.limit_exclusive = UINT64_MAX,
|
||||
};
|
||||
|
||||
if (fh) {
|
||||
i->fh = file_handle_dup(fh);
|
||||
if (!i->fh)
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
i->name = strdup(pretty);
|
||||
if (!i->name)
|
||||
return -ENOMEM;
|
||||
@ -437,6 +451,28 @@ static int image_make(
|
||||
path_startswith(path, "/usr") ||
|
||||
(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)) {
|
||||
unsigned file_attr = 0;
|
||||
usec_t crtime = 0;
|
||||
@ -478,6 +514,9 @@ static int image_make(
|
||||
info.read_only || read_only,
|
||||
info.otime,
|
||||
info.ctime,
|
||||
fh,
|
||||
on_mount_id,
|
||||
(uint64_t) st->st_ino,
|
||||
ret);
|
||||
if (r < 0)
|
||||
return r;
|
||||
@ -503,6 +542,9 @@ static int image_make(
|
||||
read_only || (file_attr & FS_IMMUTABLE_FL),
|
||||
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 */
|
||||
fh,
|
||||
on_mount_id,
|
||||
(uint64_t) st->st_ino,
|
||||
ret);
|
||||
if (r < 0)
|
||||
return r;
|
||||
@ -540,6 +582,9 @@ static int image_make(
|
||||
!(st->st_mode & 0222) || read_only,
|
||||
crtime,
|
||||
timespec_load(&st->st_mtim),
|
||||
fh,
|
||||
on_mount_id,
|
||||
(uint64_t) st->st_ino,
|
||||
ret);
|
||||
if (r < 0)
|
||||
return r;
|
||||
@ -597,6 +642,9 @@ static int image_make(
|
||||
!(st->st_mode & 0222) || read_only,
|
||||
0,
|
||||
0,
|
||||
fh,
|
||||
on_mount_id,
|
||||
(uint64_t) st->st_ino,
|
||||
ret);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
@ -27,6 +27,10 @@ typedef struct Image {
|
||||
usec_t crtime;
|
||||
usec_t mtime;
|
||||
|
||||
struct file_handle *fh;
|
||||
uint64_t on_mount_id;
|
||||
uint64_t inode;
|
||||
|
||||
uint64_t usage;
|
||||
uint64_t usage_exclusive;
|
||||
uint64_t limit;
|
||||
|
||||
@ -9,8 +9,10 @@
|
||||
#include "in-addr-util.h"
|
||||
#include "iovec-util.h"
|
||||
#include "json-util.h"
|
||||
#include "ordered-set.h"
|
||||
#include "set.h"
|
||||
#include "string-util.h"
|
||||
#include "strv.h"
|
||||
|
||||
DNSServer* dns_server_free(DNSServer *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),
|
||||
"Dispatched address size (%zu) is incompatible with the family (%s).",
|
||||
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);
|
||||
|
||||
@ -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) {
|
||||
Set **ret = ASSERT_PTR(userdata);
|
||||
_cleanup_set_free_ Set *dns_servers = NULL;
|
||||
OrderedSet **ret = ASSERT_PTR(userdata);
|
||||
_cleanup_ordered_set_free_ OrderedSet *dns_servers = NULL;
|
||||
sd_json_variant *v;
|
||||
int r;
|
||||
|
||||
@ -76,12 +79,13 @@ static int dispatch_dns_server_array(const char *name, sd_json_variant *variant,
|
||||
if (r < 0)
|
||||
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)
|
||||
return r;
|
||||
TAKE_PTR(s);
|
||||
}
|
||||
|
||||
set_free_and_replace(*ret, dns_servers);
|
||||
free_and_replace_full(*ret, dns_servers, ordered_set_free);
|
||||
|
||||
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) {
|
||||
Set **ret = ASSERT_PTR(userdata);
|
||||
_cleanup_set_free_ Set *search_domains = NULL;
|
||||
OrderedSet **ret = ASSERT_PTR(userdata);
|
||||
_cleanup_ordered_set_free_ OrderedSet *search_domains = NULL;
|
||||
sd_json_variant *v;
|
||||
int r;
|
||||
|
||||
@ -140,12 +144,82 @@ static int dispatch_search_domain_array(const char *name, sd_json_variant *varia
|
||||
if (r < 0)
|
||||
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)
|
||||
return r;
|
||||
}
|
||||
|
||||
set_free_and_replace(*ret, search_domains);
|
||||
set_free_and_replace(*ret, dns_scopes);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@ -155,9 +229,18 @@ DNSConfiguration* dns_configuration_free(DNSConfiguration *c) {
|
||||
return NULL;
|
||||
|
||||
dns_server_free(c->current_dns_server);
|
||||
set_free(c->dns_servers);
|
||||
set_free(c->search_domains);
|
||||
ordered_set_free(c->dns_servers);
|
||||
ordered_set_free(c->search_domains);
|
||||
ordered_set_free(c->fallback_dns_servers);
|
||||
set_free(c->dns_scopes);
|
||||
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);
|
||||
}
|
||||
@ -172,23 +255,22 @@ DEFINE_HASH_OPS_WITH_VALUE_DESTRUCTOR(
|
||||
|
||||
static int dispatch_dns_configuration(const char *name, sd_json_variant *variant, sd_json_dispatch_flags_t flags, void *userdata) {
|
||||
static const sd_json_dispatch_field dns_configuration_dispatch_table[] = {
|
||||
{ "ifname", SD_JSON_VARIANT_STRING, sd_json_dispatch_string, offsetof(DNSConfiguration, ifname), 0 },
|
||||
{ "ifindex", SD_JSON_VARIANT_UNSIGNED, json_dispatch_ifindex, offsetof(DNSConfiguration, ifindex), SD_JSON_RELAX },
|
||||
{ "defaultRoute", SD_JSON_VARIANT_BOOLEAN, sd_json_dispatch_stdbool, offsetof(DNSConfiguration, default_route), 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 },
|
||||
{ "searchDomains", SD_JSON_VARIANT_ARRAY, dispatch_search_domain_array, offsetof(DNSConfiguration, search_domains), 0 },
|
||||
|
||||
/* The remaining fields are currently unused by wait-online. */
|
||||
{ "delegate", _SD_JSON_VARIANT_TYPE_INVALID, NULL, 0, 0 },
|
||||
{ "fallbackServers", _SD_JSON_VARIANT_TYPE_INVALID, NULL, 0, 0 },
|
||||
{ "negativeTrustAnchors", _SD_JSON_VARIANT_TYPE_INVALID, NULL, 0, 0 },
|
||||
{ "dnssec", _SD_JSON_VARIANT_TYPE_INVALID, NULL, 0, 0 },
|
||||
{ "dnsOverTLS", _SD_JSON_VARIANT_TYPE_INVALID, NULL, 0, 0 },
|
||||
{ "llmnr", _SD_JSON_VARIANT_TYPE_INVALID, NULL, 0, 0 },
|
||||
{ "mDNS", _SD_JSON_VARIANT_TYPE_INVALID, NULL, 0, 0 },
|
||||
{ "resolvConfMode", _SD_JSON_VARIANT_TYPE_INVALID, NULL, 0, 0 },
|
||||
{ "scopes", _SD_JSON_VARIANT_TYPE_INVALID, NULL, 0, 0 },
|
||||
{ "ifname", SD_JSON_VARIANT_STRING, sd_json_dispatch_string, offsetof(DNSConfiguration, ifname), 0 },
|
||||
{ "ifindex", SD_JSON_VARIANT_UNSIGNED, json_dispatch_ifindex, offsetof(DNSConfiguration, ifindex), SD_JSON_RELAX },
|
||||
{ "defaultRoute", SD_JSON_VARIANT_BOOLEAN, sd_json_dispatch_stdbool, offsetof(DNSConfiguration, default_route), 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 },
|
||||
{ "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 },
|
||||
{ "dnssec", SD_JSON_VARIANT_STRING, sd_json_dispatch_string, offsetof(DNSConfiguration, dnssec_mode_str), 0 },
|
||||
{ "dnsOverTLS", SD_JSON_VARIANT_STRING, sd_json_dispatch_string, offsetof(DNSConfiguration, dns_over_tls_mode_str), 0 },
|
||||
{ "llmnr", SD_JSON_VARIANT_STRING, sd_json_dispatch_string, offsetof(DNSConfiguration, llmnr_mode_str), 0 },
|
||||
{ "mDNS", SD_JSON_VARIANT_STRING, sd_json_dispatch_string, offsetof(DNSConfiguration, mdns_mode_str), 0 },
|
||||
{ "fallbackServers", SD_JSON_VARIANT_ARRAY, dispatch_dns_server_array, offsetof(DNSConfiguration, fallback_dns_servers), 0 },
|
||||
{ "negativeTrustAnchors", SD_JSON_VARIANT_ARRAY, sd_json_dispatch_strv, offsetof(DNSConfiguration, negative_trust_anchors), 0 },
|
||||
{ "resolvConfMode", SD_JSON_VARIANT_STRING, sd_json_dispatch_string, offsetof(DNSConfiguration, resolv_conf_mode_str), 0 },
|
||||
{ "scopes", SD_JSON_VARIANT_ARRAY, dispatch_dns_scope_array, offsetof(DNSConfiguration, dns_scopes), 0 },
|
||||
{ "delegate", SD_JSON_VARIANT_STRING, sd_json_dispatch_string, offsetof(DNSConfiguration, delegate), 0 },
|
||||
{},
|
||||
|
||||
};
|
||||
@ -204,6 +286,8 @@ static int dispatch_dns_configuration(const char *name, sd_json_variant *variant
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
strv_sort(c->negative_trust_anchors);
|
||||
|
||||
*ret = TAKE_PTR(c);
|
||||
|
||||
return 0;
|
||||
@ -222,7 +306,7 @@ bool dns_is_accessible(DNSConfiguration *c) {
|
||||
if (c->current_dns_server && c->current_dns_server->accessible)
|
||||
return true;
|
||||
|
||||
SET_FOREACH(s, c->dns_servers)
|
||||
ORDERED_SET_FOREACH(s, c->dns_servers)
|
||||
if (s->accessible)
|
||||
return true;
|
||||
|
||||
@ -237,7 +321,7 @@ bool dns_configuration_contains_search_domain(DNSConfiguration *c, const char *d
|
||||
if (!c)
|
||||
return false;
|
||||
|
||||
SET_FOREACH(d, c->search_domains)
|
||||
ORDERED_SET_FOREACH(d, c->search_domains)
|
||||
if (streq(d->name, domain))
|
||||
return true;
|
||||
|
||||
@ -3,6 +3,7 @@
|
||||
|
||||
#include <sys/uio.h>
|
||||
|
||||
#include "in-addr-util.h"
|
||||
#include "shared-forward.h"
|
||||
|
||||
typedef struct DNSServer {
|
||||
@ -12,6 +13,7 @@ typedef struct DNSServer {
|
||||
int ifindex;
|
||||
char *server_name;
|
||||
bool accessible;
|
||||
union in_addr_union in_addr;
|
||||
} DNSServer;
|
||||
|
||||
DNSServer* dns_server_free(DNSServer *s);
|
||||
@ -23,6 +25,18 @@ typedef struct SearchDomain {
|
||||
int ifindex;
|
||||
} 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);
|
||||
DEFINE_TRIVIAL_CLEANUP_FUNC(SearchDomain*, search_domain_free);
|
||||
|
||||
@ -31,8 +45,18 @@ typedef struct DNSConfiguration {
|
||||
int ifindex;
|
||||
bool default_route;
|
||||
DNSServer *current_dns_server;
|
||||
Set *dns_servers;
|
||||
Set *search_domains;
|
||||
OrderedSet *dns_servers;
|
||||
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* dns_configuration_free(DNSConfiguration *c);
|
||||
@ -61,6 +61,7 @@ shared_sources = files(
|
||||
'dissect-image.c',
|
||||
'dm-util.c',
|
||||
'dns-answer.c',
|
||||
'dns-configuration.c',
|
||||
'dns-domain.c',
|
||||
'dns-packet.c',
|
||||
'dns-question.c',
|
||||
|
||||
@ -2,6 +2,13 @@
|
||||
|
||||
#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(
|
||||
LinkState,
|
||||
SD_VARLINK_DEFINE_ENUM_VALUE(pending),
|
||||
@ -35,12 +42,10 @@ static SD_VARLINK_DEFINE_STRUCT_TYPE(
|
||||
RoutingPolicyRule,
|
||||
SD_VARLINK_FIELD_COMMENT("Address family (AF_INET or AF_INET6)"),
|
||||
SD_VARLINK_DEFINE_FIELD(Family, SD_VARLINK_INT, 0),
|
||||
SD_VARLINK_FIELD_COMMENT("Source address prefix to match"),
|
||||
SD_VARLINK_DEFINE_FIELD(FromPrefix, SD_VARLINK_INT, SD_VARLINK_ARRAY|SD_VARLINK_NULLABLE),
|
||||
SD_VARLINK_DEFINE_IN_ADDR_WITH_STRING_FIELD(FromPrefix, "Source address prefix to match", SD_VARLINK_NULLABLE),
|
||||
SD_VARLINK_FIELD_COMMENT("Length of source prefix in bits"),
|
||||
SD_VARLINK_DEFINE_FIELD(FromPrefixLength, SD_VARLINK_INT, SD_VARLINK_NULLABLE),
|
||||
SD_VARLINK_FIELD_COMMENT("Destination address prefix to match"),
|
||||
SD_VARLINK_DEFINE_FIELD(ToPrefix, SD_VARLINK_INT, SD_VARLINK_ARRAY|SD_VARLINK_NULLABLE),
|
||||
SD_VARLINK_DEFINE_IN_ADDR_WITH_STRING_FIELD(ToPrefix, "Destination address prefix to match", SD_VARLINK_NULLABLE),
|
||||
SD_VARLINK_FIELD_COMMENT("Length of destination prefix in bits"),
|
||||
SD_VARLINK_DEFINE_FIELD(ToPrefixLength, SD_VARLINK_INT, SD_VARLINK_NULLABLE),
|
||||
SD_VARLINK_FIELD_COMMENT("Routing protocol identifier"),
|
||||
@ -92,18 +97,14 @@ static SD_VARLINK_DEFINE_STRUCT_TYPE(
|
||||
Route,
|
||||
SD_VARLINK_FIELD_COMMENT("Address family (AF_INET or AF_INET6)"),
|
||||
SD_VARLINK_DEFINE_FIELD(Family, SD_VARLINK_INT, 0),
|
||||
SD_VARLINK_FIELD_COMMENT("Destination network address"),
|
||||
SD_VARLINK_DEFINE_FIELD(Destination, SD_VARLINK_INT, SD_VARLINK_ARRAY),
|
||||
SD_VARLINK_DEFINE_IN_ADDR_WITH_STRING_FIELD(Destination, "Destination network address", 0),
|
||||
SD_VARLINK_FIELD_COMMENT("Destination network prefix length"),
|
||||
SD_VARLINK_DEFINE_FIELD(DestinationPrefixLength, SD_VARLINK_INT, 0),
|
||||
SD_VARLINK_FIELD_COMMENT("Gateway address for this route"),
|
||||
SD_VARLINK_DEFINE_FIELD(Gateway, SD_VARLINK_INT, SD_VARLINK_ARRAY|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_DEFINE_IN_ADDR_WITH_STRING_FIELD(Gateway, "Gateway address for this route", 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 prefix length"),
|
||||
SD_VARLINK_DEFINE_FIELD(SourcePrefixLength, SD_VARLINK_INT, SD_VARLINK_NULLABLE),
|
||||
SD_VARLINK_FIELD_COMMENT("Preferred source address for outgoing packets"),
|
||||
SD_VARLINK_DEFINE_FIELD(PreferredSource, SD_VARLINK_INT, SD_VARLINK_ARRAY|SD_VARLINK_NULLABLE),
|
||||
SD_VARLINK_DEFINE_IN_ADDR_WITH_STRING_FIELD(PreferredSource, "Preferred source address for outgoing packets", SD_VARLINK_NULLABLE),
|
||||
SD_VARLINK_FIELD_COMMENT("Type of Service (TOS) field"),
|
||||
SD_VARLINK_DEFINE_FIELD(TOS, SD_VARLINK_INT, 0),
|
||||
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_FIELD_COMMENT("Configuration source for this route"),
|
||||
SD_VARLINK_DEFINE_FIELD(ConfigSource, SD_VARLINK_STRING, 0),
|
||||
SD_VARLINK_FIELD_COMMENT("Address of the configuration provider"),
|
||||
SD_VARLINK_DEFINE_FIELD(ConfigProvider, SD_VARLINK_INT, SD_VARLINK_ARRAY|SD_VARLINK_NULLABLE),
|
||||
SD_VARLINK_DEFINE_IN_ADDR_WITH_STRING_FIELD(ConfigProvider, "Address of the configuration provider", SD_VARLINK_NULLABLE),
|
||||
SD_VARLINK_FIELD_COMMENT("Configuration state of this route"),
|
||||
SD_VARLINK_DEFINE_FIELD(ConfigState, SD_VARLINK_STRING, SD_VARLINK_NULLABLE),
|
||||
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_FIELD_COMMENT("Address family (AF_INET or AF_INET6)"),
|
||||
SD_VARLINK_DEFINE_FIELD(Family, SD_VARLINK_INT, 0),
|
||||
SD_VARLINK_FIELD_COMMENT("Gateway address for this next hop"),
|
||||
SD_VARLINK_DEFINE_FIELD(Gateway, SD_VARLINK_INT, SD_VARLINK_ARRAY|SD_VARLINK_NULLABLE),
|
||||
SD_VARLINK_DEFINE_IN_ADDR_WITH_STRING_FIELD(Gateway, "Gateway address for this next hop", SD_VARLINK_NULLABLE),
|
||||
SD_VARLINK_FIELD_COMMENT("Next hop flags (RTNH_F_* values)"),
|
||||
SD_VARLINK_DEFINE_FIELD(Flags, SD_VARLINK_INT, SD_VARLINK_NULLABLE),
|
||||
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_FIELD_COMMENT("Configuration source for this next hop"),
|
||||
SD_VARLINK_DEFINE_FIELD(ConfigSource, SD_VARLINK_STRING, 0),
|
||||
SD_VARLINK_FIELD_COMMENT("Address of the configuration provider"),
|
||||
SD_VARLINK_DEFINE_FIELD(ConfigProvider, SD_VARLINK_INT, SD_VARLINK_ARRAY|SD_VARLINK_NULLABLE),
|
||||
SD_VARLINK_DEFINE_IN_ADDR_WITH_STRING_FIELD(ConfigProvider, "Address of the configuration provider", SD_VARLINK_NULLABLE),
|
||||
SD_VARLINK_FIELD_COMMENT("Configuration state of this next hop"),
|
||||
SD_VARLINK_DEFINE_FIELD(ConfigState, SD_VARLINK_STRING, SD_VARLINK_NULLABLE));
|
||||
|
||||
@ -210,8 +208,7 @@ static SD_VARLINK_DEFINE_STRUCT_TYPE(
|
||||
DNS,
|
||||
SD_VARLINK_FIELD_COMMENT("Address family (AF_INET or AF_INET6)"),
|
||||
SD_VARLINK_DEFINE_FIELD(Family, SD_VARLINK_INT, 0),
|
||||
SD_VARLINK_FIELD_COMMENT("DNS server IP address"),
|
||||
SD_VARLINK_DEFINE_FIELD(Address, SD_VARLINK_INT, SD_VARLINK_ARRAY),
|
||||
SD_VARLINK_DEFINE_IN_ADDR_WITH_STRING_FIELD(Address, "DNS server IP address", 0),
|
||||
SD_VARLINK_FIELD_COMMENT("DNS server port number"),
|
||||
SD_VARLINK_DEFINE_FIELD(Port, SD_VARLINK_INT, SD_VARLINK_NULLABLE),
|
||||
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_FIELD_COMMENT("Configuration source for this DNS server"),
|
||||
SD_VARLINK_DEFINE_FIELD(ConfigSource, SD_VARLINK_STRING, 0),
|
||||
SD_VARLINK_FIELD_COMMENT("Address of the configuration provider"),
|
||||
SD_VARLINK_DEFINE_FIELD(ConfigProvider, SD_VARLINK_INT, SD_VARLINK_ARRAY|SD_VARLINK_NULLABLE));
|
||||
SD_VARLINK_DEFINE_IN_ADDR_WITH_STRING_FIELD(ConfigProvider, "Address of the configuration provider", SD_VARLINK_NULLABLE));
|
||||
|
||||
static SD_VARLINK_DEFINE_STRUCT_TYPE(
|
||||
NTP,
|
||||
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_FIELD_COMMENT("NTP server IP address"),
|
||||
SD_VARLINK_DEFINE_FIELD(Address, SD_VARLINK_INT, SD_VARLINK_ARRAY|SD_VARLINK_NULLABLE),
|
||||
SD_VARLINK_DEFINE_IN_ADDR_WITH_STRING_FIELD(Address, "NTP server IP address", SD_VARLINK_NULLABLE),
|
||||
SD_VARLINK_FIELD_COMMENT("NTP server"),
|
||||
SD_VARLINK_DEFINE_FIELD(Server, SD_VARLINK_STRING, SD_VARLINK_NULLABLE),
|
||||
SD_VARLINK_FIELD_COMMENT("Configuration source for this NTP server"),
|
||||
SD_VARLINK_DEFINE_FIELD(ConfigSource, SD_VARLINK_STRING, 0),
|
||||
SD_VARLINK_FIELD_COMMENT("Address of the configuration provider"),
|
||||
SD_VARLINK_DEFINE_FIELD(ConfigProvider, SD_VARLINK_INT, SD_VARLINK_ARRAY|SD_VARLINK_NULLABLE));
|
||||
SD_VARLINK_DEFINE_IN_ADDR_WITH_STRING_FIELD(ConfigProvider, "Address of the configuration provider", SD_VARLINK_NULLABLE));
|
||||
|
||||
static SD_VARLINK_DEFINE_STRUCT_TYPE(
|
||||
SIP,
|
||||
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_FIELD_COMMENT("SIP server IP address"),
|
||||
SD_VARLINK_DEFINE_FIELD(Address, SD_VARLINK_INT, SD_VARLINK_ARRAY|SD_VARLINK_NULLABLE),
|
||||
SD_VARLINK_DEFINE_IN_ADDR_WITH_STRING_FIELD(Address, "SIP server IP address", SD_VARLINK_NULLABLE),
|
||||
SD_VARLINK_FIELD_COMMENT("SIP server domain name"),
|
||||
SD_VARLINK_DEFINE_FIELD(Domain, SD_VARLINK_STRING, SD_VARLINK_NULLABLE),
|
||||
SD_VARLINK_FIELD_COMMENT("Configuration source for this SIP server"),
|
||||
SD_VARLINK_DEFINE_FIELD(ConfigSource, SD_VARLINK_STRING, 0),
|
||||
SD_VARLINK_FIELD_COMMENT("Address of the configuration provider"),
|
||||
SD_VARLINK_DEFINE_FIELD(ConfigProvider, SD_VARLINK_INT, SD_VARLINK_ARRAY|SD_VARLINK_NULLABLE));
|
||||
SD_VARLINK_DEFINE_IN_ADDR_WITH_STRING_FIELD(ConfigProvider, "Address of the configuration provider", SD_VARLINK_NULLABLE));
|
||||
|
||||
static SD_VARLINK_DEFINE_STRUCT_TYPE(
|
||||
Domain,
|
||||
@ -255,8 +247,7 @@ static SD_VARLINK_DEFINE_STRUCT_TYPE(
|
||||
SD_VARLINK_DEFINE_FIELD(Domain, SD_VARLINK_STRING, 0),
|
||||
SD_VARLINK_FIELD_COMMENT("Configuration source for this domain"),
|
||||
SD_VARLINK_DEFINE_FIELD(ConfigSource, SD_VARLINK_STRING, 0),
|
||||
SD_VARLINK_FIELD_COMMENT("Address of the configuration provider"),
|
||||
SD_VARLINK_DEFINE_FIELD(ConfigProvider, SD_VARLINK_INT, SD_VARLINK_ARRAY|SD_VARLINK_NULLABLE));
|
||||
SD_VARLINK_DEFINE_IN_ADDR_WITH_STRING_FIELD(ConfigProvider, "Address of the configuration provider", SD_VARLINK_NULLABLE));
|
||||
|
||||
static SD_VARLINK_DEFINE_STRUCT_TYPE(
|
||||
DNSSECNegativeTrustAnchor,
|
||||
@ -282,12 +273,13 @@ static SD_VARLINK_DEFINE_STRUCT_TYPE(
|
||||
Pref64,
|
||||
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_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_DEFINE_FIELD(PrefixLength, SD_VARLINK_INT, 0),
|
||||
SD_VARLINK_FIELD_COMMENT("Lifetime of the prefix in microseconds"),
|
||||
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_FIELD(ConfigProvider, SD_VARLINK_INT, SD_VARLINK_ARRAY|SD_VARLINK_NULLABLE));
|
||||
SD_VARLINK_DEFINE_IN_ADDR_WITH_STRING_FIELD(ConfigProvider, "Address of router that provided this prefix", SD_VARLINK_NULLABLE));
|
||||
|
||||
static SD_VARLINK_DEFINE_STRUCT_TYPE(
|
||||
NDisc,
|
||||
@ -298,18 +290,14 @@ static SD_VARLINK_DEFINE_STRUCT_TYPE(
|
||||
Address,
|
||||
SD_VARLINK_FIELD_COMMENT("Address family (AF_INET or AF_INET6)"),
|
||||
SD_VARLINK_DEFINE_FIELD(Family, SD_VARLINK_INT, 0),
|
||||
SD_VARLINK_FIELD_COMMENT("IP address"),
|
||||
SD_VARLINK_DEFINE_FIELD(Address, SD_VARLINK_INT, SD_VARLINK_ARRAY),
|
||||
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_DEFINE_IN_ADDR_WITH_STRING_FIELD(Address, "IP address", 0),
|
||||
SD_VARLINK_DEFINE_IN_ADDR_WITH_STRING_FIELD(Peer, "Peer address for point-to-point interfaces", SD_VARLINK_NULLABLE),
|
||||
SD_VARLINK_FIELD_COMMENT("Network prefix length"),
|
||||
SD_VARLINK_DEFINE_FIELD(PrefixLength, SD_VARLINK_INT, 0),
|
||||
SD_VARLINK_FIELD_COMMENT("Configuration source for this address"),
|
||||
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_FIELD(ConfigProvider, SD_VARLINK_INT, SD_VARLINK_ARRAY|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_DEFINE_IN_ADDR_WITH_STRING_FIELD(ConfigProvider, "Address of the configuration provider (DHCP server, router, etc.)", SD_VARLINK_NULLABLE),
|
||||
SD_VARLINK_DEFINE_IN_ADDR_WITH_STRING_FIELD(Broadcast, "Broadcast address for IPv4 networks", SD_VARLINK_NULLABLE),
|
||||
SD_VARLINK_FIELD_COMMENT("Address scope (RT_SCOPE_* value)"),
|
||||
SD_VARLINK_DEFINE_FIELD(Scope, SD_VARLINK_INT, SD_VARLINK_NULLABLE),
|
||||
SD_VARLINK_FIELD_COMMENT("Human-readable scope string"),
|
||||
@ -335,8 +323,7 @@ static SD_VARLINK_DEFINE_STRUCT_TYPE(
|
||||
Neighbor,
|
||||
SD_VARLINK_FIELD_COMMENT("Address family (AF_INET or AF_INET6)"),
|
||||
SD_VARLINK_DEFINE_FIELD(Family, SD_VARLINK_INT, 0),
|
||||
SD_VARLINK_FIELD_COMMENT("IP address of the neighbor"),
|
||||
SD_VARLINK_DEFINE_FIELD(Destination, SD_VARLINK_INT, SD_VARLINK_ARRAY),
|
||||
SD_VARLINK_DEFINE_IN_ADDR_WITH_STRING_FIELD(Destination, "IP address of the neighbor", 0),
|
||||
SD_VARLINK_FIELD_COMMENT("Link layer (MAC) address of the neighbor"),
|
||||
SD_VARLINK_DEFINE_FIELD(LinkLayerAddress, SD_VARLINK_INT, SD_VARLINK_ARRAY),
|
||||
SD_VARLINK_FIELD_COMMENT("Configuration source for this neighbor entry"),
|
||||
@ -366,12 +353,14 @@ static SD_VARLINK_DEFINE_STRUCT_TYPE(
|
||||
DHCPv6ClientPD,
|
||||
SD_VARLINK_FIELD_COMMENT("Delegated IPv6 prefix"),
|
||||
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_DEFINE_FIELD(PrefixLength, SD_VARLINK_INT, 0),
|
||||
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_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(
|
||||
DHCPv6ClientVendorOption,
|
||||
@ -397,8 +386,7 @@ static SD_VARLINK_DEFINE_STRUCT_TYPE(
|
||||
DHCPServerLease,
|
||||
SD_VARLINK_FIELD_COMMENT("DHCP client identifier"),
|
||||
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_FIELD(Address, SD_VARLINK_INT, SD_VARLINK_ARRAY|SD_VARLINK_NULLABLE),
|
||||
SD_VARLINK_DEFINE_IN_ADDR_WITH_STRING_FIELD(Address, "Address assigned to the client", SD_VARLINK_NULLABLE),
|
||||
SD_VARLINK_FIELD_COMMENT("Hostname provided by the DHCP client"),
|
||||
SD_VARLINK_DEFINE_FIELD(Hostname, SD_VARLINK_STRING, SD_VARLINK_NULLABLE),
|
||||
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_FIELD_COMMENT("Broadcast address for Ethernet interfaces"),
|
||||
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_FIELD(IPv6LinkLocalAddress, SD_VARLINK_INT, SD_VARLINK_ARRAY|SD_VARLINK_NULLABLE),
|
||||
SD_VARLINK_DEFINE_IN_ADDR_WITH_STRING_FIELD(IPv6LinkLocalAddress, "IPv6 link-local address", SD_VARLINK_NULLABLE),
|
||||
SD_VARLINK_FIELD_COMMENT("Wireless LAN interface type (NL80211_IFTYPE_* value)"),
|
||||
SD_VARLINK_DEFINE_FIELD(WirelessLanInterfaceType, SD_VARLINK_INT, SD_VARLINK_NULLABLE),
|
||||
SD_VARLINK_FIELD_COMMENT("Human-readable wireless LAN interface type"),
|
||||
|
||||
@ -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_FIELD_COMMENT("DNSSEC mode."),
|
||||
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_DEFINE_FIELD_BY_TYPE(dnsOverTLS, DNSOverTLSMode, SD_VARLINK_NULLABLE),
|
||||
SD_VARLINK_FIELD_COMMENT("LLMNR support."),
|
||||
|
||||
@ -34,6 +34,7 @@ static SD_VARLINK_DEFINE_METHOD(
|
||||
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(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),
|
||||
VARLINK_DEFINE_POLKIT_INPUT);
|
||||
|
||||
|
||||
@ -8,6 +8,7 @@
|
||||
#include <sys/mount.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "sd-json.h"
|
||||
#include "sd-varlink.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);
|
||||
|
||||
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_root = NULL;
|
||||
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_force = false;
|
||||
static bool arg_no_reload = false;
|
||||
static bool arg_always_refresh = false;
|
||||
static int arg_noexec = -1;
|
||||
static ImagePolicy *arg_image_policy = NULL;
|
||||
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)) {
|
||||
_cleanup_free_ char *path_in_root = NULL;
|
||||
_cleanup_close_ int path_fd = -EBADF, chmod_fd = -EBADF;
|
||||
|
||||
path_in_root = path_join(root, path);
|
||||
if (!path_in_root)
|
||||
return log_oom();
|
||||
|
||||
r = mkdir_p(path_in_root, 0700);
|
||||
/* This also creates, e.g., /var/lib/extensions.mutable/usr if needed and all parent
|
||||
* directories plus it also works when the last part is a symlink to the real /usr but we
|
||||
* 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);
|
||||
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);
|
||||
if (atfd < 0)
|
||||
return log_error_errno(errno, "Failed to open directory '%s': %m", path_in_root);
|
||||
chmod_fd = fd_reopen(path_fd, O_CLOEXEC|O_DIRECTORY);
|
||||
if (chmod_fd < 0)
|
||||
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)
|
||||
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);
|
||||
@ -1489,6 +1504,30 @@ static int write_extensions_file(ImageClass image_class, char **extensions, cons
|
||||
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) {
|
||||
_cleanup_free_ char *f = NULL;
|
||||
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(
|
||||
ImageClass image_class,
|
||||
char **extensions,
|
||||
const char *origin_content,
|
||||
const char *meta_path,
|
||||
const char *overlay_path,
|
||||
const char *work_dir,
|
||||
@ -1625,6 +1665,10 @@ static int store_info_in_meta(
|
||||
if (r < 0)
|
||||
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);
|
||||
if (r < 0)
|
||||
return r;
|
||||
@ -1683,6 +1727,7 @@ static int merge_hierarchy(
|
||||
int noexec,
|
||||
char **extensions,
|
||||
char **paths,
|
||||
const char *origin_content,
|
||||
const char *meta_path,
|
||||
const char *overlay_path,
|
||||
const char *workspace_path) {
|
||||
@ -1728,7 +1773,7 @@ static int merge_hierarchy(
|
||||
if (r < 0)
|
||||
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)
|
||||
return r;
|
||||
|
||||
@ -1780,19 +1825,29 @@ static int merge_subprocess(
|
||||
ImageClass image_class,
|
||||
char **hierarchies,
|
||||
bool force,
|
||||
bool always_refresh,
|
||||
int noexec,
|
||||
Hashmap *images,
|
||||
const char *workspace) {
|
||||
|
||||
_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,
|
||||
*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_(sd_json_variant_unrefp) sd_json_variant *extensions_origin_entries = NULL,
|
||||
*extensions_origin_json = NULL, *mutable_dir_entries = NULL;
|
||||
size_t n_extensions = 0;
|
||||
unsigned n_ignored = 0;
|
||||
Image *img;
|
||||
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/"));
|
||||
|
||||
/* 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 */
|
||||
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);
|
||||
if (!p)
|
||||
@ -1929,6 +1985,12 @@ static int merge_subprocess(
|
||||
if (r < 0)
|
||||
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);
|
||||
if (r < 0)
|
||||
return r;
|
||||
@ -1998,6 +2060,60 @@ static int merge_subprocess(
|
||||
if (r < 0)
|
||||
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++;
|
||||
}
|
||||
|
||||
@ -2007,13 +2123,112 @@ static int merge_subprocess(
|
||||
log_info("No suitable extensions found (%u ignored due to incompatible image(s)).", n_ignored);
|
||||
else
|
||||
log_info("No extensions found.");
|
||||
return 0;
|
||||
return MERGE_NOTHING_FOUND;
|
||||
}
|
||||
|
||||
/* Order by version sort with strverscmp_improved() */
|
||||
typesafe_qsort(extensions, 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) {
|
||||
assert(arg_mutable != MUTABLE_NO);
|
||||
log_info("No extensions found, proceeding in mutable mode.");
|
||||
@ -2096,6 +2311,7 @@ static int merge_subprocess(
|
||||
noexec,
|
||||
extensions,
|
||||
paths,
|
||||
extensions_origin_content,
|
||||
meta_path,
|
||||
overlay_path,
|
||||
merge_hierarchy_workspace);
|
||||
@ -2140,13 +2356,14 @@ static int merge_subprocess(
|
||||
log_info("Merged extensions into '%s'.", resolved);
|
||||
}
|
||||
|
||||
return 1;
|
||||
return MERGE_MOUNTED;
|
||||
}
|
||||
|
||||
static int merge(ImageClass image_class,
|
||||
char **hierarchies,
|
||||
bool force,
|
||||
bool no_reload,
|
||||
bool always_refresh,
|
||||
int noexec,
|
||||
Hashmap *images) {
|
||||
|
||||
@ -2163,21 +2380,28 @@ static int merge(ImageClass image_class,
|
||||
if (r == 0) {
|
||||
/* Child with its own mount namespace */
|
||||
|
||||
r = merge_subprocess(image_class, hierarchies, force, noexec, images, "/run/systemd/sysext");
|
||||
if (r < 0)
|
||||
_exit(EXIT_FAILURE);
|
||||
r = merge_subprocess(image_class, hierarchies, force, always_refresh, noexec, images, "/run/systemd/sysext");
|
||||
|
||||
/* Our namespace ceases to exist here, also implicitly detaching all temporary mounts we
|
||||
* 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);
|
||||
if (r < 0)
|
||||
return r;
|
||||
if (r == 123) /* exit code 123 means: didn't do anything */
|
||||
return 0;
|
||||
if (r == MERGE_EXIT_NOTHING_FOUND)
|
||||
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)
|
||||
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_force,
|
||||
arg_no_reload,
|
||||
arg_always_refresh,
|
||||
arg_noexec,
|
||||
images);
|
||||
}
|
||||
@ -2283,16 +2508,18 @@ typedef struct MethodMergeParameters {
|
||||
const char *class;
|
||||
int force;
|
||||
int no_reload;
|
||||
int always_refresh;
|
||||
int noexec;
|
||||
} MethodMergeParameters;
|
||||
|
||||
static int parse_merge_parameters(sd_varlink *link, sd_json_variant *parameters, MethodMergeParameters *p) {
|
||||
|
||||
static const sd_json_dispatch_field dispatch_table[] = {
|
||||
{ "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 },
|
||||
{ "noReload", SD_JSON_VARIANT_BOOLEAN, sd_json_dispatch_tristate, offsetof(MethodMergeParameters, no_reload), 0 },
|
||||
{ "noexec", SD_JSON_VARIANT_BOOLEAN, sd_json_dispatch_tristate, offsetof(MethodMergeParameters, noexec), 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 },
|
||||
{ "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 },
|
||||
VARLINK_DISPATCH_POLKIT_FIELD,
|
||||
{}
|
||||
};
|
||||
@ -2310,11 +2537,12 @@ static int vl_method_merge(sd_varlink *link, sd_json_variant *parameters, sd_var
|
||||
MethodMergeParameters p = {
|
||||
.force = -1,
|
||||
.no_reload = -1,
|
||||
.always_refresh = -1,
|
||||
.noexec = -1,
|
||||
};
|
||||
_cleanup_strv_free_ char **hierarchies = NULL;
|
||||
ImageClass image_class = arg_image_class;
|
||||
bool force, no_reload;
|
||||
bool force, no_reload, always_refresh;
|
||||
int r, noexec;
|
||||
|
||||
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;
|
||||
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;
|
||||
|
||||
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)
|
||||
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)
|
||||
return r;
|
||||
|
||||
@ -2370,6 +2599,7 @@ static int refresh(
|
||||
char **hierarchies,
|
||||
bool force,
|
||||
bool no_reload,
|
||||
bool always_refresh,
|
||||
int noexec) {
|
||||
|
||||
_cleanup_hashmap_free_ Hashmap *images = NULL;
|
||||
@ -2380,9 +2610,10 @@ static int refresh(
|
||||
return r;
|
||||
|
||||
/* 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. */
|
||||
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)
|
||||
return r;
|
||||
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.
|
||||
*
|
||||
* 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
|
||||
* mounted now.
|
||||
@ -2418,6 +2650,7 @@ static int verb_refresh(int argc, char **argv, void *userdata) {
|
||||
arg_hierarchies,
|
||||
arg_force,
|
||||
arg_no_reload,
|
||||
arg_always_refresh,
|
||||
arg_noexec);
|
||||
}
|
||||
|
||||
@ -2426,12 +2659,13 @@ static int vl_method_refresh(sd_varlink *link, sd_json_variant *parameters, sd_v
|
||||
MethodMergeParameters p = {
|
||||
.force = -1,
|
||||
.no_reload = -1,
|
||||
.always_refresh = -1,
|
||||
.noexec = -1,
|
||||
};
|
||||
Hashmap **polkit_registry = ASSERT_PTR(userdata);
|
||||
_cleanup_strv_free_ char **hierarchies = NULL;
|
||||
ImageClass image_class = arg_image_class;
|
||||
bool force, no_reload;
|
||||
bool force, no_reload, always_refresh;
|
||||
int r, noexec;
|
||||
|
||||
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;
|
||||
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;
|
||||
|
||||
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)
|
||||
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)
|
||||
return r;
|
||||
|
||||
@ -2590,6 +2825,8 @@ static int verb_help(int argc, char **argv, void *userdata) {
|
||||
" Generate JSON output\n"
|
||||
" --force Ignore version incompatibilities\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"
|
||||
" Specify disk image dissection policy\n"
|
||||
" --noexec=BOOL Whether to mount extension overlay with noexec\n"
|
||||
@ -2617,21 +2854,23 @@ static int parse_argv(int argc, char *argv[]) {
|
||||
ARG_IMAGE_POLICY,
|
||||
ARG_NOEXEC,
|
||||
ARG_NO_RELOAD,
|
||||
ARG_ALWAYS_REFRESH,
|
||||
ARG_MUTABLE,
|
||||
};
|
||||
|
||||
static const struct option options[] = {
|
||||
{ "help", no_argument, NULL, 'h' },
|
||||
{ "version", no_argument, NULL, ARG_VERSION },
|
||||
{ "no-pager", no_argument, NULL, ARG_NO_PAGER },
|
||||
{ "no-legend", no_argument, NULL, ARG_NO_LEGEND },
|
||||
{ "root", required_argument, NULL, ARG_ROOT },
|
||||
{ "json", required_argument, NULL, ARG_JSON },
|
||||
{ "force", no_argument, NULL, ARG_FORCE },
|
||||
{ "image-policy", required_argument, NULL, ARG_IMAGE_POLICY },
|
||||
{ "noexec", required_argument, NULL, ARG_NOEXEC },
|
||||
{ "no-reload", no_argument, NULL, ARG_NO_RELOAD },
|
||||
{ "mutable", required_argument, NULL, ARG_MUTABLE },
|
||||
{ "help", no_argument, NULL, 'h' },
|
||||
{ "version", no_argument, NULL, ARG_VERSION },
|
||||
{ "no-pager", no_argument, NULL, ARG_NO_PAGER },
|
||||
{ "no-legend", no_argument, NULL, ARG_NO_LEGEND },
|
||||
{ "root", required_argument, NULL, ARG_ROOT },
|
||||
{ "json", required_argument, NULL, ARG_JSON },
|
||||
{ "force", no_argument, NULL, ARG_FORCE },
|
||||
{ "image-policy", required_argument, NULL, ARG_IMAGE_POLICY },
|
||||
{ "noexec", required_argument, NULL, ARG_NOEXEC },
|
||||
{ "no-reload", no_argument, NULL, ARG_NO_RELOAD },
|
||||
{ "always-refresh", required_argument, NULL, ARG_ALWAYS_REFRESH },
|
||||
{ "mutable", required_argument, NULL, ARG_MUTABLE },
|
||||
{}
|
||||
};
|
||||
|
||||
@ -2698,6 +2937,12 @@ static int parse_argv(int argc, char *argv[]) {
|
||||
arg_no_reload = true;
|
||||
break;
|
||||
|
||||
case ARG_ALWAYS_REFRESH:
|
||||
r = parse_boolean_argument("--always-refresh", optarg, &arg_always_refresh);
|
||||
if (r < 0)
|
||||
return r;
|
||||
break;
|
||||
|
||||
case ARG_MUTABLE:
|
||||
if (streq(optarg, "help")) {
|
||||
if (arg_legend)
|
||||
|
||||
@ -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
|
||||
|
||||
( 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
|
||||
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user