summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorFernando Fernandez Mancera <ffmancera@riseup.net>2024-02-13 18:10:36 +0100
committerFernando Fernandez Mancera <ffmancera@riseup.net>2024-02-19 13:02:22 +0100
commita1c503d55866a15e5b5baddfc120cdb1e386b865 (patch)
treed3fae0de5dc21831611d6530871aae3eb82d3731
parent9c7fa710688454a557a320415b509963e513aaea (diff)
parenta14a033efbaae425977f76f48146456d8386a6b0 (diff)
merge: branch 'systemd'ff/systemd_merge
The main change is that lease address and pd getters changed and the new functions are now used. In addition, solved several conflicts due to disabled code and added adapt header to new files.
-rw-r--r--Makefile.am17
-rw-r--r--src/core/dhcp/nm-dhcp-systemd.c52
-rw-r--r--src/libnm-systemd-core/meson.build3
-rw-r--r--src/libnm-systemd-core/sd-adapt-core/netif-util.c219
-rw-r--r--src/libnm-systemd-core/sd-adapt-core/netif-util.h22
-rw-r--r--src/libnm-systemd-core/sd-adapt-core/nm-sd-adapt-core.c9
-rw-r--r--src/libnm-systemd-core/sd-adapt-core/nm-sd-adapt-core.h1
-rw-r--r--src/libnm-systemd-core/src/libsystemd-network/dhcp-duid-internal.h83
-rw-r--r--src/libnm-systemd-core/src/libsystemd-network/dhcp-identifier.c252
-rw-r--r--src/libnm-systemd-core/src/libsystemd-network/dhcp-identifier.h75
-rw-r--r--src/libnm-systemd-core/src/libsystemd-network/dhcp6-client-internal.h10
-rw-r--r--src/libnm-systemd-core/src/libsystemd-network/dhcp6-internal.h12
-rw-r--r--src/libnm-systemd-core/src/libsystemd-network/dhcp6-lease-internal.h21
-rw-r--r--src/libnm-systemd-core/src/libsystemd-network/dhcp6-option.c62
-rw-r--r--src/libnm-systemd-core/src/libsystemd-network/dhcp6-option.h1
-rw-r--r--src/libnm-systemd-core/src/libsystemd-network/network-common.c111
-rw-r--r--src/libnm-systemd-core/src/libsystemd-network/network-common.h19
-rw-r--r--src/libnm-systemd-core/src/libsystemd-network/sd-dhcp-duid.c290
-rw-r--r--src/libnm-systemd-core/src/libsystemd-network/sd-dhcp6-client.c215
-rw-r--r--src/libnm-systemd-core/src/libsystemd-network/sd-dhcp6-lease.c355
-rw-r--r--src/libnm-systemd-core/src/libsystemd/sd-device/device-util.h74
-rw-r--r--src/libnm-systemd-core/src/libsystemd/sd-event/event-util.c32
-rw-r--r--src/libnm-systemd-core/src/libsystemd/sd-event/event-util.h6
-rw-r--r--src/libnm-systemd-core/src/libsystemd/sd-event/sd-event.c16
-rw-r--r--src/libnm-systemd-core/src/libsystemd/sd-id128/id128-util.c67
-rw-r--r--src/libnm-systemd-core/src/libsystemd/sd-id128/id128-util.h16
-rw-r--r--src/libnm-systemd-core/src/libsystemd/sd-id128/sd-id128.c22
-rw-r--r--src/libnm-systemd-core/src/systemd/_sd-common.h2
-rw-r--r--src/libnm-systemd-core/src/systemd/sd-device.h1
-rw-r--r--src/libnm-systemd-core/src/systemd/sd-dhcp-duid.h71
-rw-r--r--src/libnm-systemd-core/src/systemd/sd-dhcp6-client.h168
-rw-r--r--src/libnm-systemd-core/src/systemd/sd-dhcp6-lease.h52
-rw-r--r--src/libnm-systemd-core/src/systemd/sd-dhcp6-option.h2
-rw-r--r--src/libnm-systemd-core/src/systemd/sd-dhcp6-protocol.h174
-rw-r--r--src/libnm-systemd-core/src/systemd/sd-id128.h1
-rw-r--r--src/libnm-systemd-core/src/systemd/sd-ndisc.h48
-rw-r--r--src/libnm-systemd-shared/meson.build1
-rw-r--r--src/libnm-systemd-shared/sd-adapt-shared/sd-messages.h (renamed from src/libnm-systemd-shared/sd-adapt-shared/netif-util.h)0
-rw-r--r--src/libnm-systemd-shared/src/basic/alloc-util.c27
-rw-r--r--src/libnm-systemd-shared/src/basic/alloc-util.h53
-rw-r--r--src/libnm-systemd-shared/src/basic/arphrd-util.h10
-rw-r--r--src/libnm-systemd-shared/src/basic/async.h13
-rw-r--r--src/libnm-systemd-shared/src/basic/btrfs.c100
-rw-r--r--src/libnm-systemd-shared/src/basic/btrfs.h9
-rw-r--r--src/libnm-systemd-shared/src/basic/cgroup-util.h101
-rw-r--r--src/libnm-systemd-shared/src/basic/constants.h23
-rw-r--r--src/libnm-systemd-shared/src/basic/env-file.c39
-rw-r--r--src/libnm-systemd-shared/src/basic/env-file.h7
-rw-r--r--src/libnm-systemd-shared/src/basic/env-util.c367
-rw-r--r--src/libnm-systemd-shared/src/basic/env-util.h26
-rw-r--r--src/libnm-systemd-shared/src/basic/errno-util.h142
-rw-r--r--src/libnm-systemd-shared/src/basic/escape.c31
-rw-r--r--src/libnm-systemd-shared/src/basic/escape.h1
-rw-r--r--src/libnm-systemd-shared/src/basic/ether-addr-util.c14
-rw-r--r--src/libnm-systemd-shared/src/basic/ether-addr-util.h2
-rw-r--r--src/libnm-systemd-shared/src/basic/extract-word.c6
-rw-r--r--src/libnm-systemd-shared/src/basic/fd-util.c183
-rw-r--r--src/libnm-systemd-shared/src/basic/fd-util.h31
-rw-r--r--src/libnm-systemd-shared/src/basic/fileio.c272
-rw-r--r--src/libnm-systemd-shared/src/basic/fileio.h15
-rw-r--r--src/libnm-systemd-shared/src/basic/fs-util.c37
-rw-r--r--src/libnm-systemd-shared/src/basic/fs-util.h3
-rw-r--r--src/libnm-systemd-shared/src/basic/glyph-util.c14
-rw-r--r--src/libnm-systemd-shared/src/basic/glyph-util.h22
-rw-r--r--src/libnm-systemd-shared/src/basic/hash-funcs.c8
-rw-r--r--src/libnm-systemd-shared/src/basic/hash-funcs.h6
-rw-r--r--src/libnm-systemd-shared/src/basic/hashmap.c58
-rw-r--r--src/libnm-systemd-shared/src/basic/hashmap.h21
-rw-r--r--src/libnm-systemd-shared/src/basic/hexdecoct.c16
-rw-r--r--src/libnm-systemd-shared/src/basic/hexdecoct.h12
-rw-r--r--src/libnm-systemd-shared/src/basic/in-addr-util.c38
-rw-r--r--src/libnm-systemd-shared/src/basic/in-addr-util.h26
-rw-r--r--src/libnm-systemd-shared/src/basic/inotify-util.c37
-rw-r--r--src/libnm-systemd-shared/src/basic/inotify-util.h26
-rw-r--r--src/libnm-systemd-shared/src/basic/io-util.c158
-rw-r--r--src/libnm-systemd-shared/src/basic/io-util.h76
-rw-r--r--src/libnm-systemd-shared/src/basic/iovec-util.h99
-rw-r--r--src/libnm-systemd-shared/src/basic/list.h12
-rw-r--r--src/libnm-systemd-shared/src/basic/locale-util.c20
-rw-r--r--src/libnm-systemd-shared/src/basic/locale-util.h3
-rw-r--r--src/libnm-systemd-shared/src/basic/lock-util.h3
-rw-r--r--src/libnm-systemd-shared/src/basic/log.h3
-rw-r--r--src/libnm-systemd-shared/src/basic/macro.h56
-rw-r--r--src/libnm-systemd-shared/src/basic/memory-util.c16
-rw-r--r--src/libnm-systemd-shared/src/basic/memory-util.h51
-rw-r--r--src/libnm-systemd-shared/src/basic/missing_socket.h25
-rw-r--r--src/libnm-systemd-shared/src/basic/missing_stat.h2
-rw-r--r--src/libnm-systemd-shared/src/basic/missing_syscall.h33
-rw-r--r--src/libnm-systemd-shared/src/basic/namespace-util.h55
-rw-r--r--src/libnm-systemd-shared/src/basic/ordered-set.c5
-rw-r--r--src/libnm-systemd-shared/src/basic/parse-util.c74
-rw-r--r--src/libnm-systemd-shared/src/basic/parse-util.h11
-rw-r--r--src/libnm-systemd-shared/src/basic/path-util.c94
-rw-r--r--src/libnm-systemd-shared/src/basic/path-util.h55
-rw-r--r--src/libnm-systemd-shared/src/basic/pidref.h78
-rw-r--r--src/libnm-systemd-shared/src/basic/prioq.c2
-rw-r--r--src/libnm-systemd-shared/src/basic/process-util.c674
-rw-r--r--src/libnm-systemd-shared/src/basic/process-util.h91
-rw-r--r--src/libnm-systemd-shared/src/basic/random-util.c6
-rw-r--r--src/libnm-systemd-shared/src/basic/ratelimit.h2
-rw-r--r--src/libnm-systemd-shared/src/basic/signal-util.c58
-rw-r--r--src/libnm-systemd-shared/src/basic/siphash24.h9
-rw-r--r--src/libnm-systemd-shared/src/basic/socket-util.c255
-rw-r--r--src/libnm-systemd-shared/src/basic/socket-util.h43
-rw-r--r--src/libnm-systemd-shared/src/basic/sort-util.h7
-rw-r--r--src/libnm-systemd-shared/src/basic/stat-util.c68
-rw-r--r--src/libnm-systemd-shared/src/basic/stat-util.h9
-rw-r--r--src/libnm-systemd-shared/src/basic/string-util.c321
-rw-r--r--src/libnm-systemd-shared/src/basic/string-util.h55
-rw-r--r--src/libnm-systemd-shared/src/basic/strv.c137
-rw-r--r--src/libnm-systemd-shared/src/basic/strv.h30
-rw-r--r--src/libnm-systemd-shared/src/basic/time-util.c120
-rw-r--r--src/libnm-systemd-shared/src/basic/time-util.h21
-rw-r--r--src/libnm-systemd-shared/src/basic/tmpfile-util.h1
-rw-r--r--src/libnm-systemd-shared/src/basic/umask-util.h4
-rw-r--r--src/libnm-systemd-shared/src/basic/user-util.h23
-rw-r--r--src/libnm-systemd-shared/src/basic/utf8.c33
-rw-r--r--src/libnm-systemd-shared/src/basic/utf8.h2
-rw-r--r--src/libnm-systemd-shared/src/fundamental/macro-fundamental.h126
-rw-r--r--src/libnm-systemd-shared/src/fundamental/memory-util-fundamental.h42
-rw-r--r--src/libnm-systemd-shared/src/fundamental/sha256.c11
-rw-r--r--src/libnm-systemd-shared/src/fundamental/string-util-fundamental.c16
-rw-r--r--src/libnm-systemd-shared/src/fundamental/string-util-fundamental.h8
-rw-r--r--src/libnm-systemd-shared/src/shared/dns-domain.c43
124 files changed, 5567 insertions, 1957 deletions
diff --git a/Makefile.am b/Makefile.am
index 833e8046e9..0a9367d479 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -2351,7 +2351,6 @@ src_libnm_systemd_shared_libnm_systemd_shared_la_SOURCES = \
src/libnm-systemd-shared/sd-adapt-shared/mkdir.h \
src/libnm-systemd-shared/sd-adapt-shared/mountpoint-util.h \
src/libnm-systemd-shared/sd-adapt-shared/namespace-util.h \
- src/libnm-systemd-shared/sd-adapt-shared/netif-util.h \
src/libnm-systemd-shared/sd-adapt-shared/nm-sd-adapt-shared.h \
src/libnm-systemd-shared/sd-adapt-shared/nulstr-util.h \
src/libnm-systemd-shared/sd-adapt-shared/os-util.h \
@@ -2366,7 +2365,9 @@ src_libnm_systemd_shared_libnm_systemd_shared_la_SOURCES = \
src/libnm-systemd-shared/sd-adapt-shared/virt.h \
src/libnm-systemd-shared/src/basic/alloc-util.c \
src/libnm-systemd-shared/src/basic/alloc-util.h \
- src/libnm-systemd-shared/src/basic/async.h \
+ src/libnm-systemd-shared/src/basic/arphrd-util.h \
+ src/libnm-systemd-shared/src/basic/btrfs.c \
+ src/libnm-systemd-shared/src/basic/btrfs.h \
src/libnm-systemd-shared/src/basic/cgroup-util.h \
src/libnm-systemd-shared/src/basic/constants.h \
src/libnm-systemd-shared/src/basic/dns-def.h \
@@ -2405,6 +2406,7 @@ src_libnm_systemd_shared_libnm_systemd_shared_la_SOURCES = \
src/libnm-systemd-shared/src/basic/inotify-util.h \
src/libnm-systemd-shared/src/basic/io-util.c \
src/libnm-systemd-shared/src/basic/io-util.h \
+ src/libnm-systemd-shared/src/basic/iovec-util.h \
src/libnm-systemd-shared/src/basic/label.c \
src/libnm-systemd-shared/src/basic/label.h \
src/libnm-systemd-shared/src/basic/list.h \
@@ -2424,6 +2426,7 @@ src_libnm_systemd_shared_libnm_systemd_shared_la_SOURCES = \
src/libnm-systemd-shared/src/basic/missing_syscall.h \
src/libnm-systemd-shared/src/basic/missing_threads.h \
src/libnm-systemd-shared/src/basic/missing_type.h \
+ src/libnm-systemd-shared/src/basic/namespace-util.h \
src/libnm-systemd-shared/src/basic/ordered-set.c \
src/libnm-systemd-shared/src/basic/ordered-set.h \
src/libnm-systemd-shared/src/basic/origin-id.h \
@@ -2431,6 +2434,7 @@ src_libnm_systemd_shared_libnm_systemd_shared_la_SOURCES = \
src/libnm-systemd-shared/src/basic/parse-util.h \
src/libnm-systemd-shared/src/basic/path-util.c \
src/libnm-systemd-shared/src/basic/path-util.h \
+ src/libnm-systemd-shared/src/basic/pidref.h \
src/libnm-systemd-shared/src/basic/prioq.c \
src/libnm-systemd-shared/src/basic/prioq.h \
src/libnm-systemd-shared/src/basic/process-util.c \
@@ -2510,14 +2514,17 @@ src_libnm_systemd_core_libnm_systemd_core_la_SOURCES = \
src/libnm-systemd-core/sd-adapt-core/condition.h \
src/libnm-systemd-core/sd-adapt-core/conf-parser.h \
src/libnm-systemd-core/sd-adapt-core/khash.h \
+ src/libnm-systemd-core/sd-adapt-core/netif-util.c \
+ src/libnm-systemd-core/sd-adapt-core/netif-util.h \
src/libnm-systemd-core/sd-adapt-core/network-util.h \
src/libnm-systemd-core/sd-adapt-core/nm-sd-adapt-core.c \
src/libnm-systemd-core/sd-adapt-core/nm-sd-adapt-core.h \
src/libnm-systemd-core/sd-adapt-core/sd-daemon.h \
src/libnm-systemd-core/sd-adapt-core/sd-messages.h \
src/libnm-systemd-core/sd-adapt-core/udev-util.h \
- src/libnm-systemd-core/src/libsystemd-network/dhcp-identifier.c \
- src/libnm-systemd-core/src/libsystemd-network/dhcp-identifier.h \
+ src/libnm-systemd-core/src/libsystemd-network/sd-dhcp-duid.c \
+ src/libnm-systemd-core/src/libsystemd-network/dhcp-duid-internal.h \
+ src/libnm-systemd-core/src/libsystemd-network/dhcp6-client-internal.h \
src/libnm-systemd-core/src/libsystemd-network/dhcp6-internal.h \
src/libnm-systemd-core/src/libsystemd-network/dhcp6-lease-internal.h \
src/libnm-systemd-core/src/libsystemd-network/dhcp6-network.c \
@@ -2539,9 +2546,11 @@ src_libnm_systemd_core_libnm_systemd_core_la_SOURCES = \
src/libnm-systemd-core/src/libsystemd/sd-id128/sd-id128.c \
src/libnm-systemd-core/src/systemd/_sd-common.h \
src/libnm-systemd-core/src/systemd/sd-device.h \
+ src/libnm-systemd-core/src/systemd/sd-dhcp-duid.h \
src/libnm-systemd-core/src/systemd/sd-dhcp6-client.h \
src/libnm-systemd-core/src/systemd/sd-dhcp6-lease.h \
src/libnm-systemd-core/src/systemd/sd-dhcp6-option.h \
+ src/libnm-systemd-core/src/systemd/sd-dhcp6-protocol.h \
src/libnm-systemd-core/src/systemd/sd-event.h \
src/libnm-systemd-core/src/systemd/sd-id128.h \
src/libnm-systemd-core/src/systemd/sd-ndisc.h \
diff --git a/src/core/dhcp/nm-dhcp-systemd.c b/src/core/dhcp/nm-dhcp-systemd.c
index 0fc5f92866..8c5947e9f9 100644
--- a/src/core/dhcp/nm-dhcp-systemd.c
+++ b/src/core/dhcp/nm-dhcp-systemd.c
@@ -67,6 +67,15 @@ G_DEFINE_TYPE(NMDhcpSystemd, nm_dhcp_systemd, NM_TYPE_DHCP_CLIENT)
/*****************************************************************************/
+static guint32
+lifetime_to_uint32(guint64 lft)
+{
+ if (lft == G_MAXUINT64)
+ return G_MAXUINT32;
+
+ return lft / 1000000;
+}
+
static NML3ConfigData *
lease_to_ip6_config(NMDhcpSystemd *self, sd_dhcp6_lease *lease, gint32 ts, GError **error)
{
@@ -100,18 +109,19 @@ lease_to_ip6_config(NMDhcpSystemd *self, sd_dhcp6_lease *lease, gint32 ts, GErro
if (!config->v6.info_only) {
gboolean has_any_addresses = FALSE;
- uint32_t lft_pref;
- uint32_t lft_valid;
+ uint64_t lft_pref;
+ uint64_t lft_valid;
- sd_dhcp6_lease_reset_address_iter(lease);
+ sd_dhcp6_lease_address_iterator_reset(lease);
nm_gstring_prepare(&str);
- while (sd_dhcp6_lease_get_address(lease, &tmp_addr, &lft_pref, &lft_valid) >= 0) {
- const NMPlatformIP6Address address = {
+ while (sd_dhcp6_lease_get_address(lease, &tmp_addr) >= 0
+ && sd_dhcp6_lease_get_address_lifetime(lease, &lft_pref, &lft_valid) >= 0) {
+ NMPlatformIP6Address address = {
.plen = 128,
.address = tmp_addr,
.timestamp = ts,
- .lifetime = lft_valid,
- .preferred = lft_pref,
+ .lifetime = lifetime_to_uint32(lft_valid),
+ .preferred = lifetime_to_uint32(lft_pref),
.addr_source = NM_IP_CONFIG_SOURCE_DHCP,
};
@@ -121,6 +131,7 @@ lease_to_ip6_config(NMDhcpSystemd *self, sd_dhcp6_lease *lease, gint32 ts, GErro
g_string_append(nm_gstring_add_space_delimiter(str), addr_str);
has_any_addresses = TRUE;
+ sd_dhcp6_lease_address_iterator_next(lease);
}
if (str->len) {
@@ -160,11 +171,12 @@ lease_to_ip6_config(NMDhcpSystemd *self, sd_dhcp6_lease *lease, gint32 ts, GErro
uint8_t prefix_len;
nm_gstring_prepare(&str);
- sd_dhcp6_lease_reset_pd_prefix_iter(lease);
- while (!sd_dhcp6_lease_get_pd(lease, &prefix, &prefix_len, NULL, NULL)) {
+ sd_dhcp6_lease_pd_iterator_reset(lease);
+ while (!sd_dhcp6_lease_get_pd_prefix(lease, &prefix, &prefix_len)) {
nm_gstring_add_space_delimiter(str);
nm_inet6_ntop(&prefix, addr_str);
g_string_append_printf(str, "%s/%u", addr_str, prefix_len);
+ sd_dhcp6_lease_pd_iterator_next(lease);
}
if (str->len > 0) {
nm_dhcp_option_add_option(options,
@@ -235,6 +247,8 @@ bound6_handle(NMDhcpSystemd *self)
gs_free_error GError *error = NULL;
NMPlatformIP6Address prefix = {0};
sd_dhcp6_lease *lease = NULL;
+ guint64 lft_valid;
+ guint64 lft_pref;
if (sd_dhcp6_client_get_lease(priv->client6, &lease) < 0 || !lease) {
_LOGW(" no lease!");
@@ -254,14 +268,14 @@ bound6_handle(NMDhcpSystemd *self)
_nm_dhcp_client_notify(NM_DHCP_CLIENT(self), NM_DHCP_CLIENT_EVENT_TYPE_BOUND, l3cd);
- sd_dhcp6_lease_reset_pd_prefix_iter(lease);
- while (!sd_dhcp6_lease_get_pd(lease,
- &prefix.address,
- &prefix.plen,
- &prefix.preferred,
- &prefix.lifetime)) {
+ sd_dhcp6_lease_pd_iterator_reset(lease);
+ while (!sd_dhcp6_lease_get_pd_prefix(lease, &prefix.address, &prefix.plen)
+ && !sd_dhcp6_lease_get_pd_lifetime(lease, &lft_pref, &lft_valid)) {
+ prefix.preferred = lifetime_to_uint32(lft_pref);
+ prefix.lifetime = lifetime_to_uint32(lft_valid);
prefix.timestamp = ts;
nm_dhcp_client_emit_ipv6_prefix_delegated(NM_DHCP_CLIENT(self), &prefix);
+ sd_dhcp6_lease_pd_iterator_next(lease);
}
}
@@ -339,10 +353,10 @@ ip6_start(NMDhcpClient *client, const struct in6_addr *ll_addr, GError **error)
return FALSE;
}
- r = sd_dhcp6_client_set_duid(sd_client,
- unaligned_read_be16(&duid_arr[0]),
- &duid_arr[2],
- duid_len - 2);
+ r = sd_dhcp6_client_set_duid_raw(sd_client,
+ unaligned_read_be16(&duid_arr[0]),
+ &duid_arr[2],
+ duid_len - 2);
if (r < 0) {
nm_utils_error_set_errno(error, r, "failed to set DUID: %s");
return FALSE;
diff --git a/src/libnm-systemd-core/meson.build b/src/libnm-systemd-core/meson.build
index 7e700e6464..fbac8c0d44 100644
--- a/src/libnm-systemd-core/meson.build
+++ b/src/libnm-systemd-core/meson.build
@@ -3,11 +3,11 @@
libnm_systemd_core = static_library(
'nm-systemd-core',
sources: files(
- 'src/libsystemd-network/dhcp-identifier.c',
'src/libsystemd-network/dhcp6-network.c',
'src/libsystemd-network/dhcp6-option.c',
'src/libsystemd-network/dhcp6-protocol.c',
'src/libsystemd-network/network-common.c',
+ 'src/libsystemd-network/sd-dhcp-duid.c',
'src/libsystemd-network/sd-dhcp6-client.c',
'src/libsystemd-network/sd-dhcp6-lease.c',
'src/libsystemd/sd-event/event-util.c',
@@ -15,6 +15,7 @@ libnm_systemd_core = static_library(
'src/libsystemd/sd-id128/id128-util.c',
'src/libsystemd/sd-id128/sd-id128.c',
'nm-sd.c',
+ 'sd-adapt-core/netif-util.c',
'sd-adapt-core/nm-sd-adapt-core.c',
),
include_directories: [
diff --git a/src/libnm-systemd-core/sd-adapt-core/netif-util.c b/src/libnm-systemd-core/sd-adapt-core/netif-util.c
new file mode 100644
index 0000000000..b10bdf309b
--- /dev/null
+++ b/src/libnm-systemd-core/sd-adapt-core/netif-util.c
@@ -0,0 +1,219 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+
+#include "nm-sd-adapt-core.h"
+
+#include <linux/if.h>
+#include <linux/if_arp.h>
+
+#include "arphrd-util.h"
+#include "device-util.h"
+#include "log-link.h"
+#include "memory-util.h"
+#include "netif-util.h"
+#include "siphash24.h"
+#include "sparse-endian.h"
+#include "strv.h"
+
+#if 0 /* NM_IGNORED */
+bool netif_has_carrier(uint8_t operstate, unsigned flags) {
+ /* see Documentation/networking/operstates.txt in the kernel sources */
+
+ if (operstate == IF_OPER_UP)
+ return true;
+
+ if (operstate != IF_OPER_UNKNOWN)
+ return false;
+
+ /* operstate may not be implemented, so fall back to flags */
+ return FLAGS_SET(flags, IFF_LOWER_UP | IFF_RUNNING) &&
+ !FLAGS_SET(flags, IFF_DORMANT);
+}
+
+int net_get_type_string(sd_device *device, uint16_t iftype, char **ret) {
+ const char *t;
+ char *p;
+
+ if (device &&
+ sd_device_get_devtype(device, &t) >= 0 &&
+ !isempty(t)) {
+ p = strdup(t);
+ if (!p)
+ return -ENOMEM;
+
+ *ret = p;
+ return 0;
+ }
+
+ t = arphrd_to_name(iftype);
+ if (!t)
+ return -ENOENT;
+
+ p = strdup(t);
+ if (!p)
+ return -ENOMEM;
+
+ *ret = ascii_strlower(p);
+ return 0;
+}
+#endif /* NM_IGNORED */
+
+const char *
+net_get_persistent_name(sd_device *device)
+{
+ assert(device);
+
+ /* fetch some persistent data unique (on this machine) to this device */
+ FOREACH_STRING(field,
+ "ID_NET_NAME_ONBOARD",
+ "ID_NET_NAME_SLOT",
+ "ID_NET_NAME_PATH",
+ "ID_NET_NAME_MAC")
+ {
+ const char *name;
+
+ if (sd_device_get_property_value(device, field, &name) >= 0)
+ return name;
+ }
+
+ return NULL;
+}
+
+#if 0 /* NM_IGNORED */
+/* Used when generating hardware address by udev, and IPv4LL seed by networkd. */
+#define HASH_KEY SD_ID128_MAKE(d3, 1e, 48, fa, 90, fe, 4b, 4c, 9d, af, d5, d7, a1, b1, 2e, 8a)
+
+int net_get_unique_predictable_data(sd_device *device, bool use_sysname, uint64_t *ret) {
+ const char *name;
+
+ assert(device);
+ assert(ret);
+
+ /* net_get_persistent_name() will return one of the device names based on stable information about
+ * the device. If this is not available, we fall back to using the actual device name. */
+ name = net_get_persistent_name(device);
+ if (!name && use_sysname)
+ (void) sd_device_get_sysname(device, &name);
+ if (!name)
+ return log_device_debug_errno(device, SYNTHETIC_ERRNO(ENODATA),
+ "No stable identifying information found");
+
+ log_device_debug(device, "Using \"%s\" as stable identifying information", name);
+
+ return net_get_unique_predictable_data_from_name(name, &HASH_KEY, ret);
+}
+
+int net_get_unique_predictable_data_from_name(
+ const char *name,
+ const sd_id128_t *key,
+ uint64_t *ret) {
+
+ size_t l, sz;
+ uint8_t *v;
+ int r;
+
+ assert(name);
+ assert(key);
+ assert(ret);
+
+ l = strlen(name);
+ sz = sizeof(sd_id128_t) + l;
+ v = newa(uint8_t, sz);
+
+ /* Fetch some persistent data unique to this machine */
+ r = sd_id128_get_machine((sd_id128_t*) v);
+ if (r < 0)
+ return r;
+
+ memcpy(v + sizeof(sd_id128_t), name, l);
+
+ /* Let's hash the machine ID plus the device name. We use
+ * a fixed, but originally randomly created hash key here. */
+ *ret = htole64(siphash24(v, sz, key->bytes));
+ return 0;
+}
+
+typedef struct Link {
+ const char *ifname;
+} Link;
+
+int net_verify_hardware_address(
+ const char *ifname,
+ bool is_static,
+ uint16_t iftype,
+ const struct hw_addr_data *ib_hw_addr, /* current or parent HW address */
+ struct hw_addr_data *new_hw_addr) {
+
+ Link link = { .ifname = ifname };
+
+ assert(new_hw_addr);
+
+ if (new_hw_addr->length == 0)
+ return 0;
+
+ if (new_hw_addr->length != arphrd_to_hw_addr_len(iftype)) {
+ if (is_static)
+ log_link_warning(&link,
+ "Specified MAC address with invalid length (%zu, expected %zu), refusing.",
+ new_hw_addr->length, arphrd_to_hw_addr_len(iftype));
+ return -EINVAL;
+ }
+
+ switch (iftype) {
+ case ARPHRD_ETHER:
+ /* see eth_random_addr() in the kernel */
+
+ if (ether_addr_is_null(&new_hw_addr->ether)) {
+ if (is_static)
+ log_link_warning(&link, "Specified MAC address is null, refusing.");
+ return -EINVAL;
+ }
+
+ if (ether_addr_is_broadcast(&new_hw_addr->ether)) {
+ if (is_static)
+ log_link_warning(&link, "Specified MAC address is broadcast, refusing.");
+ return -EINVAL;
+ }
+
+ if (ether_addr_is_multicast(&new_hw_addr->ether)) {
+ if (is_static)
+ log_link_warning(&link, "Specified MAC address has the multicast bit set, clearing the bit.");
+
+ new_hw_addr->bytes[0] &= 0xfe;
+ }
+
+ if (!is_static && !ether_addr_is_local(&new_hw_addr->ether))
+ /* Adjust local assignment bit when the MAC address is generated randomly. */
+ new_hw_addr->bytes[0] |= 0x02;
+
+ break;
+
+ case ARPHRD_INFINIBAND:
+ /* see ipoib_check_lladdr() in the kernel */
+
+ assert(ib_hw_addr);
+ assert(ib_hw_addr->length == INFINIBAND_ALEN);
+
+ if (is_static &&
+ (!memeqzero(new_hw_addr->bytes, INFINIBAND_ALEN - 8) ||
+ memcmp(new_hw_addr->bytes, ib_hw_addr->bytes, INFINIBAND_ALEN - 8) != 0))
+ log_link_warning(&link, "Only the last 8 bytes of the InifniBand MAC address can be changed, ignoring the first 12 bytes.");
+
+ if (memeqzero(new_hw_addr->bytes + INFINIBAND_ALEN - 8, 8)) {
+ if (is_static)
+ log_link_warning(&link, "The last 8 bytes of the InfiniBand MAC address cannot be null, refusing.");
+ return -EINVAL;
+ }
+
+ memcpy(new_hw_addr->bytes, ib_hw_addr->bytes, INFINIBAND_ALEN - 8);
+ break;
+
+ default:
+ if (is_static)
+ log_link_warning(&link, "Unsupported interface type %s%u to set MAC address, refusing.",
+ strna(arphrd_to_name(iftype)), iftype);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+#endif /* NM_IGNORED */
diff --git a/src/libnm-systemd-core/sd-adapt-core/netif-util.h b/src/libnm-systemd-core/sd-adapt-core/netif-util.h
new file mode 100644
index 0000000000..59a80866b4
--- /dev/null
+++ b/src/libnm-systemd-core/sd-adapt-core/netif-util.h
@@ -0,0 +1,22 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+#pragma once
+
+#include <inttypes.h>
+#include <stdbool.h>
+
+#include "sd-device.h"
+#include "sd-id128.h"
+
+#include "ether-addr-util.h"
+
+bool netif_has_carrier(uint8_t operstate, unsigned flags);
+int net_get_type_string(sd_device *device, uint16_t iftype, char **ret);
+const char *net_get_persistent_name(sd_device *device);
+int net_get_unique_predictable_data(sd_device *device, bool use_sysname, uint64_t *ret);
+int
+net_get_unique_predictable_data_from_name(const char *name, const sd_id128_t *key, uint64_t *ret);
+int net_verify_hardware_address(const char *ifname,
+ bool is_static,
+ uint16_t iftype,
+ const struct hw_addr_data *ib_hw_addr,
+ struct hw_addr_data *new_hw_addr);
diff --git a/src/libnm-systemd-core/sd-adapt-core/nm-sd-adapt-core.c b/src/libnm-systemd-core/sd-adapt-core/nm-sd-adapt-core.c
index c5ef63eb4a..13effd122c 100644
--- a/src/libnm-systemd-core/sd-adapt-core/nm-sd-adapt-core.c
+++ b/src/libnm-systemd-core/sd-adapt-core/nm-sd-adapt-core.c
@@ -12,15 +12,6 @@
/*****************************************************************************/
-int
-asynchronous_close(int fd)
-{
- safe_close(fd);
- return -1;
-}
-
-/*****************************************************************************/
-
sd_device *
sd_device_ref(sd_device *self)
{
diff --git a/src/libnm-systemd-core/sd-adapt-core/nm-sd-adapt-core.h b/src/libnm-systemd-core/sd-adapt-core/nm-sd-adapt-core.h
index 9c317801de..9cb5574dda 100644
--- a/src/libnm-systemd-core/sd-adapt-core/nm-sd-adapt-core.h
+++ b/src/libnm-systemd-core/sd-adapt-core/nm-sd-adapt-core.h
@@ -86,7 +86,6 @@ sd_notify(int unset_environment, const char *state)
#include "sd-id128.h"
#include "sparse-endian.h"
-#include "async.h"
#endif /* (NETWORKMANAGER_COMPILATION) & NM_NETWORKMANAGER_COMPILATION_WITH_SYSTEMD */
diff --git a/src/libnm-systemd-core/src/libsystemd-network/dhcp-duid-internal.h b/src/libnm-systemd-core/src/libsystemd-network/dhcp-duid-internal.h
new file mode 100644
index 0000000000..f8bc15c47e
--- /dev/null
+++ b/src/libnm-systemd-core/src/libsystemd-network/dhcp-duid-internal.h
@@ -0,0 +1,83 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+#pragma once
+
+#include "sd-device.h"
+#include "sd-dhcp-duid.h"
+#include "sd-id128.h"
+
+#include "ether-addr-util.h"
+#include "macro.h"
+#include "sparse-endian.h"
+
+#define SYSTEMD_PEN 43793
+
+typedef enum DUIDType {
+ DUID_TYPE_LLT = SD_DUID_TYPE_LLT,
+ DUID_TYPE_EN = SD_DUID_TYPE_EN,
+ DUID_TYPE_LL = SD_DUID_TYPE_LL,
+ DUID_TYPE_UUID = SD_DUID_TYPE_UUID,
+ _DUID_TYPE_MAX,
+ _DUID_TYPE_INVALID = -EINVAL,
+} DUIDType;
+
+/* RFC 8415 section 11.1:
+ * A DUID consists of a 2-octet type code represented in network byte order, followed by a variable number of
+ * octets that make up the actual identifier. The length of the DUID (not including the type code) is at
+ * least 1 octet and at most 128 octets. */
+#define MIN_DUID_DATA_LEN 1
+#define MAX_DUID_DATA_LEN 128
+#define MIN_DUID_LEN (sizeof(be16_t) + MIN_DUID_DATA_LEN)
+#define MAX_DUID_LEN (sizeof(be16_t) + MAX_DUID_DATA_LEN)
+
+/* https://tools.ietf.org/html/rfc3315#section-9.1 */
+struct duid {
+ be16_t type;
+ union {
+ struct {
+ /* DUID_TYPE_LLT */
+ be16_t htype;
+ be32_t time;
+ uint8_t haddr[];
+ } _packed_ llt;
+ struct {
+ /* DUID_TYPE_EN */
+ be32_t pen;
+ uint8_t id[];
+ } _packed_ en;
+ struct {
+ /* DUID_TYPE_LL */
+ be16_t htype;
+ uint8_t haddr[];
+ } _packed_ ll;
+ struct {
+ /* DUID_TYPE_UUID */
+ sd_id128_t uuid;
+ } _packed_ uuid;
+ uint8_t data[MAX_DUID_DATA_LEN];
+ };
+} _packed_;
+
+typedef struct sd_dhcp_duid {
+ size_t size;
+ union {
+ struct duid duid;
+ uint8_t raw[MAX_DUID_LEN];
+ };
+} sd_dhcp_duid;
+
+static inline bool duid_size_is_valid(size_t size) {
+ return size >= MIN_DUID_LEN && size <= MAX_DUID_LEN;
+}
+
+static inline bool duid_data_size_is_valid(size_t size) {
+ return size >= MIN_DUID_DATA_LEN && size <= MAX_DUID_DATA_LEN;
+}
+
+const char *duid_type_to_string(DUIDType t) _const_;
+int dhcp_duid_to_string_internal(uint16_t type, const void *data, size_t data_size, char **ret);
+
+int dhcp_identifier_set_iaid(
+ sd_device *dev,
+ const struct hw_addr_data *hw_addr,
+ bool legacy_unstable_byteorder,
+ void *ret);
diff --git a/src/libnm-systemd-core/src/libsystemd-network/dhcp-identifier.c b/src/libnm-systemd-core/src/libsystemd-network/dhcp-identifier.c
deleted file mode 100644
index 05d0585a9e..0000000000
--- a/src/libnm-systemd-core/src/libsystemd-network/dhcp-identifier.c
+++ /dev/null
@@ -1,252 +0,0 @@
-/* SPDX-License-Identifier: LGPL-2.1-or-later */
-
-#include "nm-sd-adapt-core.h"
-
-#include <linux/if_infiniband.h>
-#include <net/ethernet.h>
-#include <net/if_arp.h>
-
-#include "dhcp-identifier.h"
-#include "netif-util.h"
-#include "siphash24.h"
-#include "sparse-endian.h"
-#include "string-table.h"
-
-#define HASH_KEY SD_ID128_MAKE(80,11,8c,c2,fe,4a,03,ee,3e,d6,0c,6f,36,39,14,09)
-#define APPLICATION_ID SD_ID128_MAKE(a5,0a,d1,12,bf,60,45,77,a2,fb,74,1a,b1,95,5b,03)
-#define USEC_2000 ((usec_t) 946684800000000) /* 2000-01-01 00:00:00 UTC */
-
-static const char * const duid_type_table[_DUID_TYPE_MAX] = {
- [DUID_TYPE_LLT] = "DUID-LLT",
- [DUID_TYPE_EN] = "DUID-EN/Vendor",
- [DUID_TYPE_LL] = "DUID-LL",
- [DUID_TYPE_UUID] = "UUID",
-};
-
-DEFINE_STRING_TABLE_LOOKUP_TO_STRING(duid_type, DUIDType);
-
-int dhcp_validate_duid_len(DUIDType duid_type, size_t duid_len, bool strict) {
- struct duid d;
-
- assert_cc(sizeof(d.raw) >= MAX_DUID_LEN);
- if (duid_len > MAX_DUID_LEN)
- return -EINVAL;
-
- if (!strict)
- /* Strict validation is not requested. We only ensure that the
- * DUID is not too long. */
- return 0;
-
- switch (duid_type) {
- case DUID_TYPE_LLT:
- if (duid_len <= sizeof(d.llt))
- return -EINVAL;
- break;
- case DUID_TYPE_EN:
- if (duid_len != sizeof(d.en))
- return -EINVAL;
- break;
- case DUID_TYPE_LL:
- if (duid_len <= sizeof(d.ll))
- return -EINVAL;
- break;
- case DUID_TYPE_UUID:
- if (duid_len != sizeof(d.uuid))
- return -EINVAL;
- break;
- default:
- /* accept unknown type in order to be forward compatible */
- break;
- }
- return 0;
-}
-
-#if 0 /* NM_IGNORED */
-static int dhcp_identifier_set_duid_llt(
- const struct hw_addr_data *hw_addr,
- uint16_t arp_type,
- usec_t t,
- struct duid *ret_duid,
- size_t *ret_len) {
-
- uint16_t time_from_2000y;
-
- assert(hw_addr);
- assert(ret_duid);
- assert(ret_len);
-
- if (hw_addr->length == 0)
- return -EOPNOTSUPP;
-
- if (arp_type == ARPHRD_ETHER)
- assert_return(hw_addr->length == ETH_ALEN, -EINVAL);
- else if (arp_type == ARPHRD_INFINIBAND)
- assert_return(hw_addr->length == INFINIBAND_ALEN, -EINVAL);
- else
- return -EOPNOTSUPP;
-
- if (t < USEC_2000)
- time_from_2000y = 0;
- else
- time_from_2000y = (uint16_t) (((t - USEC_2000) / USEC_PER_SEC) & 0xffffffff);
-
- unaligned_write_be16(&ret_duid->type, DUID_TYPE_LLT);
- unaligned_write_be16(&ret_duid->llt.htype, arp_type);
- unaligned_write_be32(&ret_duid->llt.time, time_from_2000y);
- memcpy(ret_duid->llt.haddr, hw_addr->bytes, hw_addr->length);
-
- *ret_len = offsetof(struct duid, llt.haddr) + hw_addr->length;
-
- return 0;
-}
-
-static int dhcp_identifier_set_duid_ll(
- const struct hw_addr_data *hw_addr,
- uint16_t arp_type,
- struct duid *ret_duid,
- size_t *ret_len) {
-
- assert(hw_addr);
- assert(ret_duid);
- assert(ret_len);
-
- if (hw_addr->length == 0)
- return -EOPNOTSUPP;
-
- if (arp_type == ARPHRD_ETHER)
- assert_return(hw_addr->length == ETH_ALEN, -EINVAL);
- else if (arp_type == ARPHRD_INFINIBAND)
- assert_return(hw_addr->length == INFINIBAND_ALEN, -EINVAL);
- else
- return -EOPNOTSUPP;
-
- unaligned_write_be16(&ret_duid->type, DUID_TYPE_LL);
- unaligned_write_be16(&ret_duid->ll.htype, arp_type);
- memcpy(ret_duid->ll.haddr, hw_addr->bytes, hw_addr->length);
-
- *ret_len = offsetof(struct duid, ll.haddr) + hw_addr->length;
-
- return 0;
-}
-#endif /* NM_IGNORED */
-
-int dhcp_identifier_set_duid_en(bool test_mode, struct duid *ret_duid, size_t *ret_len) {
- sd_id128_t machine_id;
- uint64_t hash;
- int r;
-
- assert(ret_duid);
- assert(ret_len);
-
- if (!test_mode) {
- r = sd_id128_get_machine(&machine_id);
- if (r < 0)
- return r;
- } else
- /* For tests, especially for fuzzers, reproducibility is important.
- * Hence, use a static and constant machine ID.
- * See 9216fddc5a8ac2742e6cfa7660f95c20ca4f2193. */
- machine_id = SD_ID128_MAKE(01, 02, 03, 04, 05, 06, 07, 08, 09, 0a, 0b, 0c, 0d, 0e, 0f, 10);
-
- unaligned_write_be16(&ret_duid->type, DUID_TYPE_EN);
- unaligned_write_be32(&ret_duid->en.pen, SYSTEMD_PEN);
-
- /* a bit of snake-oil perhaps, but no need to expose the machine-id
- * directly; duid->en.id might not be aligned, so we need to copy */
- hash = htole64(siphash24(&machine_id, sizeof(machine_id), HASH_KEY.bytes));
- memcpy(ret_duid->en.id, &hash, sizeof(ret_duid->en.id));
-
- *ret_len = offsetof(struct duid, en.id) + sizeof(ret_duid->en.id);
-
- if (test_mode)
- assert_se(memcmp(ret_duid, (const uint8_t[]) { 0x00, 0x02, 0x00, 0x00, 0xab, 0x11, 0x61, 0x77, 0x40, 0xde, 0x13, 0x42, 0xc3, 0xa2 }, *ret_len) == 0);
-
- return 0;
-}
-
-#if 0 /* NM_IGNORED */
-static int dhcp_identifier_set_duid_uuid(struct duid *ret_duid, size_t *ret_len) {
- sd_id128_t machine_id;
- int r;
-
- assert(ret_duid);
- assert(ret_len);
-
- r = sd_id128_get_machine_app_specific(APPLICATION_ID, &machine_id);
- if (r < 0)
- return r;
-
- unaligned_write_be16(&ret_duid->type, DUID_TYPE_UUID);
- memcpy(&ret_duid->uuid.uuid, &machine_id, sizeof(machine_id));
-
- *ret_len = offsetof(struct duid, uuid.uuid) + sizeof(machine_id);
-
- return 0;
-}
-
-int dhcp_identifier_set_duid(
- DUIDType duid_type,
- const struct hw_addr_data *hw_addr,
- uint16_t arp_type,
- usec_t llt_time,
- bool test_mode,
- struct duid *ret_duid,
- size_t *ret_len) {
-
- switch (duid_type) {
- case DUID_TYPE_LLT:
- return dhcp_identifier_set_duid_llt(hw_addr, arp_type, llt_time, ret_duid, ret_len);
- case DUID_TYPE_EN:
- return dhcp_identifier_set_duid_en(test_mode, ret_duid, ret_len);
- case DUID_TYPE_LL:
- return dhcp_identifier_set_duid_ll(hw_addr, arp_type, ret_duid, ret_len);
- case DUID_TYPE_UUID:
- return dhcp_identifier_set_duid_uuid(ret_duid, ret_len);
- default:
- return -EINVAL;
- }
-}
-#endif /* NM_IGNORED */
-
-int dhcp_identifier_set_iaid(
- sd_device *dev,
- const struct hw_addr_data *hw_addr,
- bool legacy_unstable_byteorder,
- void *ret) {
-#if 0 /* NM_IGNORED */
-
- const char *name = NULL;
- uint32_t id32;
- uint64_t id;
-
- assert(hw_addr);
- assert(ret);
-
- if (dev)
- name = net_get_persistent_name(dev);
- if (name)
- id = siphash24(name, strlen(name), HASH_KEY.bytes);
- else
- /* fall back to MAC address if no predictable name available */
- id = siphash24(hw_addr->bytes, hw_addr->length, HASH_KEY.bytes);
-
- id32 = (id & 0xffffffff) ^ (id >> 32);
-
- if (legacy_unstable_byteorder)
- /* for historical reasons (a bug), the bits were swapped and thus
- * the result was endianness dependent. Preserve that behavior. */
- id32 = bswap_32(id32);
- else
- /* the fixed behavior returns a stable byte order. Since LE is expected
- * to be more common, swap the bytes on LE to give the same as legacy
- * behavior. */
- id32 = be32toh(id32);
-
- unaligned_write_ne32(ret, id32);
- return 0;
-#else /* NM_IGNORED */
- /* for NetworkManager, we don't use this function and we should never call here.
- * This got replaced by nm_utils_create_dhcp_iaid(). */
- g_return_val_if_reached (-EINVAL);
-#endif /* NM_IGNORED */
-}
diff --git a/src/libnm-systemd-core/src/libsystemd-network/dhcp-identifier.h b/src/libnm-systemd-core/src/libsystemd-network/dhcp-identifier.h
deleted file mode 100644
index 523dfc4a71..0000000000
--- a/src/libnm-systemd-core/src/libsystemd-network/dhcp-identifier.h
+++ /dev/null
@@ -1,75 +0,0 @@
-/* SPDX-License-Identifier: LGPL-2.1-or-later */
-#pragma once
-
-#include "sd-device.h"
-#include "sd-id128.h"
-
-#include "ether-addr-util.h"
-#include "macro.h"
-#include "sparse-endian.h"
-#include "time-util.h"
-#include "unaligned.h"
-
-#define SYSTEMD_PEN 43793
-
-typedef enum DUIDType {
- DUID_TYPE_LLT = 1,
- DUID_TYPE_EN = 2,
- DUID_TYPE_LL = 3,
- DUID_TYPE_UUID = 4,
- _DUID_TYPE_MAX,
- _DUID_TYPE_INVALID = -EINVAL,
-} DUIDType;
-
-/* RFC 3315 section 9.1:
- * A DUID can be no more than 128 octets long (not including the type code).
- */
-#define MAX_DUID_LEN 128
-
-/* https://tools.ietf.org/html/rfc3315#section-9.1 */
-struct duid {
- be16_t type;
- union {
- struct {
- /* DUID_TYPE_LLT */
- be16_t htype;
- be32_t time;
- uint8_t haddr[0];
- } _packed_ llt;
- struct {
- /* DUID_TYPE_EN */
- be32_t pen;
- uint8_t id[8];
- } _packed_ en;
- struct {
- /* DUID_TYPE_LL */
- be16_t htype;
- uint8_t haddr[0];
- } _packed_ ll;
- struct {
- /* DUID_TYPE_UUID */
- sd_id128_t uuid;
- } _packed_ uuid;
- struct {
- uint8_t data[MAX_DUID_LEN];
- } _packed_ raw;
- };
-} _packed_;
-
-int dhcp_validate_duid_len(DUIDType duid_type, size_t duid_len, bool strict);
-int dhcp_identifier_set_duid_en(bool test_mode, struct duid *ret_duid, size_t *ret_len);
-int dhcp_identifier_set_duid(
- DUIDType duid_type,
- const struct hw_addr_data *hw_addr,
- uint16_t arp_type,
- usec_t llt_time,
- bool test_mode,
- struct duid *ret_duid,
- size_t *ret_len);
-int dhcp_identifier_set_iaid(
- sd_device *dev,
- const struct hw_addr_data *hw_addr,
- bool legacy_unstable_byteorder,
- void *ret);
-
-const char *duid_type_to_string(DUIDType t) _const_;
diff --git a/src/libnm-systemd-core/src/libsystemd-network/dhcp6-client-internal.h b/src/libnm-systemd-core/src/libsystemd-network/dhcp6-client-internal.h
new file mode 100644
index 0000000000..6c17f5749b
--- /dev/null
+++ b/src/libnm-systemd-core/src/libsystemd-network/dhcp6-client-internal.h
@@ -0,0 +1,10 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+#pragma once
+
+#include "sd-dhcp6-client.h"
+
+int dhcp6_client_set_state_callback(
+ sd_dhcp6_client *client,
+ sd_dhcp6_client_callback_t cb,
+ void *userdata);
+int dhcp6_client_get_state(sd_dhcp6_client *client);
diff --git a/src/libnm-systemd-core/src/libsystemd-network/dhcp6-internal.h b/src/libnm-systemd-core/src/libsystemd-network/dhcp6-internal.h
index fa43f28eb5..3fbfc028e9 100644
--- a/src/libnm-systemd-core/src/libsystemd-network/dhcp6-internal.h
+++ b/src/libnm-systemd-core/src/libsystemd-network/dhcp6-internal.h
@@ -11,7 +11,8 @@
#include "sd-event.h"
#include "sd-dhcp6-client.h"
-#include "dhcp-identifier.h"
+#include "dhcp-duid-internal.h"
+#include "dhcp6-client-internal.h"
#include "dhcp6-option.h"
#include "dhcp6-protocol.h"
#include "ether-addr-util.h"
@@ -63,8 +64,7 @@ struct sd_dhcp6_client {
DHCP6IA ia_na;
DHCP6IA ia_pd;
DHCP6RequestIA request_ia;
- struct duid duid;
- size_t duid_len;
+ sd_dhcp_duid duid;
be16_t *req_opts;
size_t n_req_opts;
char *fqdn;
@@ -79,10 +79,9 @@ struct sd_dhcp6_client {
sd_dhcp6_client_callback_t callback;
void *userdata;
+ sd_dhcp6_client_callback_t state_callback;
+ void *state_userdata;
bool send_release;
-
- /* Ignore machine-ID when generating DUID. See dhcp_identifier_set_duid_en(). */
- bool test_mode;
};
int dhcp6_network_bind_udp_socket(int ifindex, struct in6_addr *address);
@@ -90,7 +89,6 @@ int dhcp6_network_send_udp_socket(int s, struct in6_addr *address,
const void *packet, size_t len);
int dhcp6_client_send_message(sd_dhcp6_client *client);
-void dhcp6_client_set_test_mode(sd_dhcp6_client *client, bool test_mode);
int dhcp6_client_set_transaction_id(sd_dhcp6_client *client, uint32_t transaction_id);
#define log_dhcp6_client_errno(client, error, fmt, ...) \
diff --git a/src/libnm-systemd-core/src/libsystemd-network/dhcp6-lease-internal.h b/src/libnm-systemd-core/src/libsystemd-network/dhcp6-lease-internal.h
index 1f10dccbbb..e76a108f60 100644
--- a/src/libnm-systemd-core/src/libsystemd-network/dhcp6-lease-internal.h
+++ b/src/libnm-systemd-core/src/libsystemd-network/dhcp6-lease-internal.h
@@ -10,7 +10,9 @@
#include "sd-dhcp6-lease.h"
#include "dhcp6-option.h"
+#include "dhcp6-protocol.h"
#include "macro.h"
+#include "set.h"
#include "time-util.h"
struct sd_dhcp6_lease {
@@ -43,9 +45,11 @@ struct sd_dhcp6_lease {
struct in6_addr *sntp;
size_t sntp_count;
char *fqdn;
+ char *captive_portal;
+ struct sd_dhcp6_option **sorted_vendor_options;
+ Set *vendor_options;
};
-int dhcp6_lease_get_lifetime(sd_dhcp6_lease *lease, usec_t *ret_t1, usec_t *ret_t2, usec_t *ret_valid);
int dhcp6_lease_set_clientid(sd_dhcp6_lease *lease, const uint8_t *id, size_t len);
int dhcp6_lease_get_clientid(sd_dhcp6_lease *lease, uint8_t **ret_id, size_t *ret_len);
int dhcp6_lease_set_serverid(sd_dhcp6_lease *lease, const uint8_t *id, size_t len);
@@ -60,6 +64,7 @@ int dhcp6_lease_add_domains(sd_dhcp6_lease *lease, const uint8_t *optval, size_t
int dhcp6_lease_add_ntp(sd_dhcp6_lease *lease, const uint8_t *optval, size_t optlen);
int dhcp6_lease_add_sntp(sd_dhcp6_lease *lease, const uint8_t *optval, size_t optlen);
int dhcp6_lease_set_fqdn(sd_dhcp6_lease *lease, const uint8_t *optval, size_t optlen);
+int dhcp6_lease_set_captive_portal(sd_dhcp6_lease *lease, const uint8_t *optval, size_t optlen);
int dhcp6_lease_new(sd_dhcp6_lease **ret);
int dhcp6_lease_new_from_message(
@@ -69,3 +74,17 @@ int dhcp6_lease_new_from_message(
const triple_timestamp *timestamp,
const struct in6_addr *server_address,
sd_dhcp6_lease **ret);
+
+#define _FOREACH_DHCP6_ADDRESS(lease, it) \
+ for (int it = sd_dhcp6_lease_address_iterator_reset(lease); \
+ it > 0; \
+ it = sd_dhcp6_lease_address_iterator_next(lease))
+#define FOREACH_DHCP6_ADDRESS(lease) \
+ _FOREACH_DHCP6_ADDRESS(lease, UNIQ_T(i, UNIQ))
+
+#define _FOREACH_DHCP6_PD_PREFIX(lease, it) \
+ for (int it = sd_dhcp6_lease_pd_iterator_reset(lease); \
+ it > 0; \
+ it = sd_dhcp6_lease_pd_iterator_next(lease))
+#define FOREACH_DHCP6_PD_PREFIX(lease) \
+ _FOREACH_DHCP6_PD_PREFIX(lease, UNIQ_T(i, UNIQ))
diff --git a/src/libnm-systemd-core/src/libsystemd-network/dhcp6-option.c b/src/libnm-systemd-core/src/libsystemd-network/dhcp6-option.c
index 23cf8a8966..5fa9b2656e 100644
--- a/src/libnm-systemd-core/src/libsystemd-network/dhcp6-option.c
+++ b/src/libnm-systemd-core/src/libsystemd-network/dhcp6-option.c
@@ -17,6 +17,7 @@
#include "dns-domain.h"
#include "escape.h"
#include "memory-util.h"
+#include "network-common.h"
#include "strv.h"
#include "unaligned.h"
@@ -526,6 +527,26 @@ int dhcp6_option_parse_status(const uint8_t *data, size_t data_len, char **ret_s
return status;
}
+/* parse a string from dhcp option field. *ret must be initialized */
+int dhcp6_option_parse_string(const uint8_t *data, size_t data_len, char **ret) {
+ _cleanup_free_ char *string = NULL;
+ int r;
+
+ assert(data || data_len == 0);
+ assert(ret);
+
+ if (data_len <= 0) {
+ *ret = mfree(*ret);
+ return 0;
+ }
+
+ r = make_cstring((const char *) data, data_len, MAKE_CSTRING_REFUSE_TRAILING_NUL, &string);
+ if (r < 0)
+ return r;
+
+ return free_and_replace(*ret, string);
+}
+
static int dhcp6_option_parse_ia_options(sd_dhcp6_client *client, const uint8_t *buf, size_t buflen) {
int r;
@@ -567,7 +588,7 @@ static int dhcp6_option_parse_ia_options(sd_dhcp6_client *client, const uint8_t
static int dhcp6_option_parse_ia_address(sd_dhcp6_client *client, DHCP6IA *ia, const uint8_t *data, size_t len) {
_cleanup_free_ DHCP6Address *a = NULL;
- uint32_t lt_valid, lt_pref;
+ usec_t lt_valid, lt_pref;
int r;
assert(ia);
@@ -586,17 +607,18 @@ static int dhcp6_option_parse_ia_address(sd_dhcp6_client *client, DHCP6IA *ia, c
memcpy(&a->iaaddr, data, sizeof(struct iaaddr));
- lt_valid = be32toh(a->iaaddr.lifetime_valid);
- lt_pref = be32toh(a->iaaddr.lifetime_preferred);
+ lt_valid = be32_sec_to_usec(a->iaaddr.lifetime_valid, /* max_as_infinity = */ true);
+ lt_pref = be32_sec_to_usec(a->iaaddr.lifetime_preferred, /* max_as_infinity = */ true);
if (lt_valid == 0)
return log_dhcp6_client_errno(client, SYNTHETIC_ERRNO(EINVAL),
"Received an IA address with zero valid lifetime, ignoring.");
if (lt_pref > lt_valid)
return log_dhcp6_client_errno(client, SYNTHETIC_ERRNO(EINVAL),
- "Received an IA address with preferred lifetime %"PRIu32
- " larger than valid lifetime %"PRIu32", ignoring.",
- lt_pref, lt_valid);
+ "Received an IA address with preferred lifetime %s "
+ "larger than valid lifetime %s, ignoring.",
+ FORMAT_TIMESPAN(lt_pref, USEC_PER_SEC),
+ FORMAT_TIMESPAN(lt_valid, USEC_PER_SEC));
if (len > sizeof(struct iaaddr)) {
r = dhcp6_option_parse_ia_options(client, data + sizeof(struct iaaddr), len - sizeof(struct iaaddr));
@@ -610,7 +632,7 @@ static int dhcp6_option_parse_ia_address(sd_dhcp6_client *client, DHCP6IA *ia, c
static int dhcp6_option_parse_ia_pdprefix(sd_dhcp6_client *client, DHCP6IA *ia, const uint8_t *data, size_t len) {
_cleanup_free_ DHCP6Address *a = NULL;
- uint32_t lt_valid, lt_pref;
+ usec_t lt_valid, lt_pref;
int r;
assert(ia);
@@ -629,17 +651,18 @@ static int dhcp6_option_parse_ia_pdprefix(sd_dhcp6_client *client, DHCP6IA *ia,
memcpy(&a->iapdprefix, data, sizeof(struct iapdprefix));
- lt_valid = be32toh(a->iapdprefix.lifetime_valid);
- lt_pref = be32toh(a->iapdprefix.lifetime_preferred);
+ lt_valid = be32_sec_to_usec(a->iapdprefix.lifetime_valid, /* max_as_infinity = */ true);
+ lt_pref = be32_sec_to_usec(a->iapdprefix.lifetime_preferred, /* max_as_infinity = */ true);
if (lt_valid == 0)
return log_dhcp6_client_errno(client, SYNTHETIC_ERRNO(EINVAL),
"Received a PD prefix with zero valid lifetime, ignoring.");
if (lt_pref > lt_valid)
return log_dhcp6_client_errno(client, SYNTHETIC_ERRNO(EINVAL),
- "Received a PD prefix with preferred lifetime %"PRIu32
- " larger than valid lifetime %"PRIu32", ignoring.",
- lt_pref, lt_valid);
+ "Received a PD prefix with preferred lifetime %s "
+ "larger than valid lifetime %s, ignoring.",
+ FORMAT_TIMESPAN(lt_pref, USEC_PER_SEC),
+ FORMAT_TIMESPAN(lt_valid, USEC_PER_SEC));
if (len > sizeof(struct iapdprefix)) {
r = dhcp6_option_parse_ia_options(client, data + sizeof(struct iapdprefix), len - sizeof(struct iapdprefix));
@@ -660,7 +683,7 @@ int dhcp6_option_parse_ia(
DHCP6IA **ret) {
_cleanup_(dhcp6_ia_freep) DHCP6IA *ia = NULL;
- uint32_t lt_t1, lt_t2;
+ usec_t lt_t1, lt_t2;
size_t header_len;
int r;
@@ -710,17 +733,18 @@ int dhcp6_option_parse_ia(
"from the one chosen by the client, ignoring.");
/* It is not necessary to check if the lifetime_t2 is zero here, as in that case it will be updated later. */
- lt_t1 = be32toh(ia->header.lifetime_t1);
- lt_t2 = be32toh(ia->header.lifetime_t2);
+ lt_t1 = be32_sec_to_usec(ia->header.lifetime_t1, /* max_as_infinity = */ true);
+ lt_t2 = be32_sec_to_usec(ia->header.lifetime_t2, /* max_as_infinity = */ true);
if (lt_t1 > lt_t2)
return log_dhcp6_client_errno(client, SYNTHETIC_ERRNO(EINVAL),
- "Received an IA option with T1 %"PRIu32"sec > T2 %"PRIu32"sec, ignoring.",
- lt_t1, lt_t2);
+ "Received an IA option with T1 %s > T2 %s, ignoring.",
+ FORMAT_TIMESPAN(lt_t1, USEC_PER_SEC),
+ FORMAT_TIMESPAN(lt_t2, USEC_PER_SEC));
if (lt_t1 == 0 && lt_t2 > 0)
return log_dhcp6_client_errno(client, SYNTHETIC_ERRNO(EINVAL),
- "Received an IA option with zero T1 and non-zero T2 (%"PRIu32"sec), ignoring.",
- lt_t2);
+ "Received an IA option with zero T1 and non-zero T2 (%s), ignoring.",
+ FORMAT_TIMESPAN(lt_t2, USEC_PER_SEC));
for (size_t offset = header_len; offset < option_data_len;) {
const uint8_t *subdata;
diff --git a/src/libnm-systemd-core/src/libsystemd-network/dhcp6-option.h b/src/libnm-systemd-core/src/libsystemd-network/dhcp6-option.h
index 36841dd270..614b4f8a43 100644
--- a/src/libnm-systemd-core/src/libsystemd-network/dhcp6-option.h
+++ b/src/libnm-systemd-core/src/libsystemd-network/dhcp6-option.h
@@ -88,6 +88,7 @@ int dhcp6_option_parse(
size_t *ret_option_data_len,
const uint8_t **ret_option_data);
int dhcp6_option_parse_status(const uint8_t *data, size_t data_len, char **ret_status_message);
+int dhcp6_option_parse_string(const uint8_t *data, size_t data_len, char **ret);
int dhcp6_option_parse_ia(
sd_dhcp6_client *client,
be32_t iaid,
diff --git a/src/libnm-systemd-core/src/libsystemd-network/network-common.c b/src/libnm-systemd-core/src/libsystemd-network/network-common.c
index bbc289415d..8d62e6ff3b 100644
--- a/src/libnm-systemd-core/src/libsystemd-network/network-common.c
+++ b/src/libnm-systemd-core/src/libsystemd-network/network-common.c
@@ -2,8 +2,11 @@
#include "nm-sd-adapt-core.h"
+#include "env-util.h"
#include "format-util.h"
#include "network-common.h"
+#include "socket-util.h"
+#include "unaligned.h"
int get_ifname(int ifindex, char **ifname) {
assert(ifname);
@@ -15,3 +18,111 @@ int get_ifname(int ifindex, char **ifname) {
return format_ifname_alloc(ifindex, ifname);
}
+
+usec_t unaligned_be32_sec_to_usec(const void *p, bool max_as_infinity) {
+ uint32_t s = unaligned_read_be32(ASSERT_PTR(p));
+
+ if (s == UINT32_MAX && max_as_infinity)
+ return USEC_INFINITY;
+
+ return s * USEC_PER_SEC;
+}
+
+usec_t be32_sec_to_usec(be32_t t, bool max_as_infinity) {
+ uint32_t s = be32toh(t);
+
+ if (s == UINT32_MAX && max_as_infinity)
+ return USEC_INFINITY;
+
+ return s * USEC_PER_SEC;
+}
+
+usec_t be32_msec_to_usec(be32_t t, bool max_as_infinity) {
+ uint32_t s = be32toh(t);
+
+ if (s == UINT32_MAX && max_as_infinity)
+ return USEC_INFINITY;
+
+ return s * USEC_PER_MSEC;
+}
+
+usec_t be16_sec_to_usec(be16_t t, bool max_as_infinity) {
+ uint16_t s = be16toh(t);
+
+ if (s == UINT16_MAX && max_as_infinity)
+ return USEC_INFINITY;
+
+ return s * USEC_PER_SEC;
+}
+
+be32_t usec_to_be32_sec(usec_t t) {
+ if (t == USEC_INFINITY)
+ /* Some settings, e.g. a lifetime of an address, UINT32_MAX is handled as infinity. so let's
+ * map USEC_INFINITY to UINT32_MAX. */
+ return htobe32(UINT32_MAX);
+
+ if (t >= (UINT32_MAX - 1) * USEC_PER_SEC)
+ /* Finite but too large. Let's use the largest (or off-by-one from the largest) finite value. */
+ return htobe32(UINT32_MAX - 1);
+
+ return htobe32((uint32_t) DIV_ROUND_UP(t, USEC_PER_SEC));
+}
+
+be32_t usec_to_be32_msec(usec_t t) {
+ if (t == USEC_INFINITY)
+ return htobe32(UINT32_MAX);
+
+ if (t >= (UINT32_MAX - 1) * USEC_PER_MSEC)
+ return htobe32(UINT32_MAX - 1);
+
+ return htobe32((uint32_t) DIV_ROUND_UP(t, USEC_PER_MSEC));
+}
+
+be16_t usec_to_be16_sec(usec_t t) {
+ if (t == USEC_INFINITY)
+ return htobe16(UINT16_MAX);
+
+ if (t >= (UINT16_MAX - 1) * USEC_PER_SEC)
+ return htobe16(UINT16_MAX - 1);
+
+ return htobe16((uint16_t) DIV_ROUND_UP(t, USEC_PER_SEC));
+}
+
+usec_t time_span_to_stamp(usec_t span, usec_t base) {
+ /* Typically, 0 lifetime (timespan) indicates the corresponding configuration (address or so) must be
+ * dropped. So, when the timespan is zero, here we return 0 rather than 'base'. This makes the caller
+ * easily understand that the configuration needs to be dropped immediately. */
+ if (span == 0)
+ return 0;
+
+ return usec_add(base, span);
+}
+
+bool network_test_mode_enabled(void) {
+ static int test_mode = -1;
+ int r;
+
+ if (test_mode < 0) {
+ r = getenv_bool("SYSTEMD_NETWORK_TEST_MODE");
+ if (r < 0) {
+ if (r != -ENXIO)
+ log_debug_errno(r, "Failed to parse $SYSTEMD_NETWORK_TEST_MODE environment variable, ignoring: %m");
+
+ test_mode = false;
+ } else
+ test_mode = r;
+ }
+
+ return test_mode;
+}
+
+triple_timestamp* triple_timestamp_from_cmsg(triple_timestamp *t, struct msghdr *mh) {
+ assert(t);
+ assert(mh);
+
+ struct timeval *tv = CMSG_FIND_AND_COPY_DATA(mh, SOL_SOCKET, SCM_TIMESTAMP, struct timeval);
+ if (tv)
+ return triple_timestamp_from_realtime(t, timeval_load(tv));
+
+ return triple_timestamp_now(t);
+}
diff --git a/src/libnm-systemd-core/src/libsystemd-network/network-common.h b/src/libnm-systemd-core/src/libsystemd-network/network-common.h
index 2b0e3b5607..1750f1810b 100644
--- a/src/libnm-systemd-core/src/libsystemd-network/network-common.h
+++ b/src/libnm-systemd-core/src/libsystemd-network/network-common.h
@@ -1,7 +1,11 @@
/* SPDX-License-Identifier: LGPL-2.1-or-later */
#pragma once
+#include <sys/socket.h>
+
#include "log-link.h"
+#include "sparse-endian.h"
+#include "time-util.h"
#define log_interface_prefix_full_errno_zerook(prefix, type, val, error, fmt, ...) \
({ \
@@ -28,3 +32,18 @@
})
int get_ifname(int ifindex, char **ifname);
+
+usec_t unaligned_be32_sec_to_usec(const void *p, bool max_as_infinity);
+usec_t be32_sec_to_usec(be32_t t, bool max_as_infinity);
+usec_t be32_msec_to_usec(be32_t t, bool max_as_infinity);
+usec_t be16_sec_to_usec(be16_t t, bool max_as_infinity);
+be32_t usec_to_be32_sec(usec_t t);
+be32_t usec_to_be32_msec(usec_t t);
+be16_t usec_to_be16_sec(usec_t t);
+usec_t time_span_to_stamp(usec_t span, usec_t base);
+
+bool network_test_mode_enabled(void);
+
+triple_timestamp* triple_timestamp_from_cmsg(triple_timestamp *t, struct msghdr *mh);
+#define TRIPLE_TIMESTAMP_FROM_CMSG(mh) \
+ triple_timestamp_from_cmsg(&(triple_timestamp) {}, mh)
diff --git a/src/libnm-systemd-core/src/libsystemd-network/sd-dhcp-duid.c b/src/libnm-systemd-core/src/libsystemd-network/sd-dhcp-duid.c
new file mode 100644
index 0000000000..e664a4a720
--- /dev/null
+++ b/src/libnm-systemd-core/src/libsystemd-network/sd-dhcp-duid.c
@@ -0,0 +1,290 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+
+#include "nm-sd-adapt-core.h"
+
+#include <linux/if_infiniband.h>
+#include <net/ethernet.h>
+#include <net/if_arp.h>
+
+#include "dhcp-duid-internal.h"
+#include "hexdecoct.h"
+#include "netif-util.h"
+#include "network-common.h"
+#include "siphash24.h"
+#include "string-table.h"
+#include "unaligned.h"
+
+#define HASH_KEY SD_ID128_MAKE(80,11,8c,c2,fe,4a,03,ee,3e,d6,0c,6f,36,39,14,09)
+#define APPLICATION_ID SD_ID128_MAKE(a5,0a,d1,12,bf,60,45,77,a2,fb,74,1a,b1,95,5b,03)
+#define USEC_2000 ((usec_t) 946684800000000) /* 2000-01-01 00:00:00 UTC */
+
+static const char * const duid_type_table[_DUID_TYPE_MAX] = {
+ [DUID_TYPE_LLT] = "DUID-LLT",
+ [DUID_TYPE_EN] = "DUID-EN/Vendor",
+ [DUID_TYPE_LL] = "DUID-LL",
+ [DUID_TYPE_UUID] = "UUID",
+};
+
+DEFINE_STRING_TABLE_LOOKUP_TO_STRING(duid_type, DUIDType);
+
+int sd_dhcp_duid_clear(sd_dhcp_duid *duid) {
+ assert_return(duid, -EINVAL);
+
+ *duid = (sd_dhcp_duid) {};
+ return 0;
+}
+
+int sd_dhcp_duid_is_set(const sd_dhcp_duid *duid) {
+ if (!duid)
+ return false;
+
+ return duid_size_is_valid(duid->size);
+}
+
+int sd_dhcp_duid_get(const sd_dhcp_duid *duid, uint16_t *ret_type, const void **ret_data, size_t *ret_size) {
+ assert_return(sd_dhcp_duid_is_set(duid), -EINVAL);
+ assert_return(ret_type, -EINVAL);
+ assert_return(ret_data, -EINVAL);
+ assert_return(ret_size, -EINVAL);
+
+ *ret_type = be16toh(duid->duid.type);
+ *ret_data = duid->duid.data;
+ *ret_size = duid->size - offsetof(struct duid, data);
+ return 0;
+}
+
+int sd_dhcp_duid_get_raw(const sd_dhcp_duid *duid, const void **ret_data, size_t *ret_size) {
+ assert_return(sd_dhcp_duid_is_set(duid), -EINVAL);
+ assert_return(ret_data, -EINVAL);
+ assert_return(ret_size, -EINVAL);
+
+ /* Unlike sd_dhcp_duid_get(), this returns whole DUID including its type. */
+
+ *ret_data = duid->raw;
+ *ret_size = duid->size;
+ return 0;
+}
+
+int sd_dhcp_duid_set(
+ sd_dhcp_duid *duid,
+ uint16_t duid_type,
+ const void *data,
+ size_t data_size) {
+
+ assert_return(duid, -EINVAL);
+ assert_return(data, -EINVAL);
+
+ if (!duid_data_size_is_valid(data_size))
+ return -EINVAL;
+
+ unaligned_write_be16(&duid->duid.type, duid_type);
+ memcpy(duid->duid.data, data, data_size);
+
+ duid->size = offsetof(struct duid, data) + data_size;
+ return 0;
+}
+
+int sd_dhcp_duid_set_raw(
+ sd_dhcp_duid *duid,
+ const void *data,
+ size_t data_size) {
+
+ assert_return(duid, -EINVAL);
+ assert_return(data, -EINVAL);
+
+ /* Unlike sd_dhcp_duid_set(), this takes whole DUID including its type. */
+
+ if (!duid_size_is_valid(data_size))
+ return -EINVAL;
+
+ memcpy(duid->raw, data, data_size);
+
+ duid->size = data_size;
+ return 0;
+}
+
+int sd_dhcp_duid_set_llt(
+ sd_dhcp_duid *duid,
+ const void *hw_addr,
+ size_t hw_addr_size,
+ uint16_t arp_type,
+ uint64_t usec) {
+
+ uint16_t time_from_2000y;
+
+ assert_return(duid, -EINVAL);
+ assert_return(hw_addr, -EINVAL);
+
+ if (arp_type == ARPHRD_ETHER)
+ assert_return(hw_addr_size == ETH_ALEN, -EINVAL);
+ else if (arp_type == ARPHRD_INFINIBAND)
+ assert_return(hw_addr_size == INFINIBAND_ALEN, -EINVAL);
+ else
+ return -EOPNOTSUPP;
+
+ time_from_2000y = (uint16_t) ((usec_sub_unsigned(usec, USEC_2000) / USEC_PER_SEC) & 0xffffffff);
+
+ unaligned_write_be16(&duid->duid.type, SD_DUID_TYPE_LLT);
+ unaligned_write_be16(&duid->duid.llt.htype, arp_type);
+ unaligned_write_be32(&duid->duid.llt.time, time_from_2000y);
+ memcpy(duid->duid.llt.haddr, hw_addr, hw_addr_size);
+
+ duid->size = offsetof(struct duid, llt.haddr) + hw_addr_size;
+ return 0;
+}
+
+int sd_dhcp_duid_set_ll(
+ sd_dhcp_duid *duid,
+ const void *hw_addr,
+ size_t hw_addr_size,
+ uint16_t arp_type) {
+
+ assert_return(duid, -EINVAL);
+ assert_return(hw_addr, -EINVAL);
+
+ if (arp_type == ARPHRD_ETHER)
+ assert_return(hw_addr_size == ETH_ALEN, -EINVAL);
+ else if (arp_type == ARPHRD_INFINIBAND)
+ assert_return(hw_addr_size == INFINIBAND_ALEN, -EINVAL);
+ else
+ return -EOPNOTSUPP;
+
+ unaligned_write_be16(&duid->duid.type, SD_DUID_TYPE_LL);
+ unaligned_write_be16(&duid->duid.ll.htype, arp_type);
+ memcpy(duid->duid.ll.haddr, hw_addr, hw_addr_size);
+
+ duid->size = offsetof(struct duid, ll.haddr) + hw_addr_size;
+ return 0;
+}
+
+int sd_dhcp_duid_set_en(sd_dhcp_duid *duid) {
+ sd_id128_t machine_id;
+ bool test_mode;
+ uint64_t hash;
+ int r;
+
+ assert_return(duid, -EINVAL);
+
+ test_mode = network_test_mode_enabled();
+
+ if (!test_mode) {
+ r = sd_id128_get_machine(&machine_id);
+ if (r < 0)
+ return r;
+ } else
+ /* For tests, especially for fuzzers, reproducibility is important.
+ * Hence, use a static and constant machine ID.
+ * See 9216fddc5a8ac2742e6cfa7660f95c20ca4f2193. */
+ machine_id = SD_ID128_MAKE(01, 02, 03, 04, 05, 06, 07, 08, 09, 0a, 0b, 0c, 0d, 0e, 0f, 10);
+
+ unaligned_write_be16(&duid->duid.type, SD_DUID_TYPE_EN);
+ unaligned_write_be32(&duid->duid.en.pen, SYSTEMD_PEN);
+
+ /* a bit of snake-oil perhaps, but no need to expose the machine-id
+ * directly; duid->en.id might not be aligned, so we need to copy */
+ hash = htole64(siphash24(&machine_id, sizeof(machine_id), HASH_KEY.bytes));
+ memcpy(duid->duid.en.id, &hash, sizeof(hash));
+
+ duid->size = offsetof(struct duid, en.id) + sizeof(hash);
+
+ if (test_mode)
+ assert_se(memcmp(&duid->duid, (const uint8_t[]) { 0x00, 0x02, 0x00, 0x00, 0xab, 0x11, 0x61, 0x77, 0x40, 0xde, 0x13, 0x42, 0xc3, 0xa2 }, duid->size) == 0);
+
+ return 0;
+}
+
+int sd_dhcp_duid_set_uuid(sd_dhcp_duid *duid) {
+ sd_id128_t machine_id;
+ int r;
+
+ assert_return(duid, -EINVAL);
+
+ r = sd_id128_get_machine_app_specific(APPLICATION_ID, &machine_id);
+ if (r < 0)
+ return r;
+
+ unaligned_write_be16(&duid->duid.type, SD_DUID_TYPE_UUID);
+ memcpy(&duid->duid.uuid.uuid, &machine_id, sizeof(machine_id));
+
+ duid->size = offsetof(struct duid, uuid.uuid) + sizeof(machine_id);
+ return 0;
+}
+
+int dhcp_duid_to_string_internal(uint16_t type, const void *data, size_t data_size, char **ret) {
+ _cleanup_free_ char *p = NULL, *x = NULL;
+ const char *t;
+
+ assert(data);
+ assert(ret);
+
+ if (!duid_data_size_is_valid(data_size))
+ return -EINVAL;
+
+ x = hexmem(data, data_size);
+ if (!x)
+ return -ENOMEM;
+
+ t = duid_type_to_string(type);
+ if (!t)
+ return asprintf(ret, "%04x:%s", htobe16(type), x);
+
+ p = strjoin(t, ":", x);
+ if (!p)
+ return -ENOMEM;
+
+ *ret = TAKE_PTR(p);
+ return 0;
+}
+
+int sd_dhcp_duid_to_string(const sd_dhcp_duid *duid, char **ret) {
+ uint16_t type;
+ const void *data;
+ size_t data_size;
+ int r;
+
+ assert_return(sd_dhcp_duid_is_set(duid), -EINVAL);
+ assert_return(ret, -EINVAL);
+
+ r = sd_dhcp_duid_get(duid, &type, &data, &data_size);
+ if (r < 0)
+ return r;
+
+ return dhcp_duid_to_string_internal(type, data, data_size, ret);
+}
+
+int dhcp_identifier_set_iaid(
+ sd_device *dev,
+ const struct hw_addr_data *hw_addr,
+ bool legacy_unstable_byteorder,
+ void *ret) {
+
+ const char *name = NULL;
+ uint32_t id32;
+ uint64_t id;
+
+ assert(hw_addr);
+ assert(ret);
+
+ if (dev)
+ name = net_get_persistent_name(dev);
+ if (name)
+ id = siphash24(name, strlen(name), HASH_KEY.bytes);
+ else
+ /* fall back to MAC address if no predictable name available */
+ id = siphash24(hw_addr->bytes, hw_addr->length, HASH_KEY.bytes);
+
+ id32 = (id & 0xffffffff) ^ (id >> 32);
+
+ if (legacy_unstable_byteorder)
+ /* for historical reasons (a bug), the bits were swapped and thus
+ * the result was endianness dependent. Preserve that behavior. */
+ id32 = bswap_32(id32);
+ else
+ /* the fixed behavior returns a stable byte order. Since LE is expected
+ * to be more common, swap the bytes on LE to give the same as legacy
+ * behavior. */
+ id32 = be32toh(id32);
+
+ unaligned_write_ne32(ret, id32);
+ return 0;
+}
diff --git a/src/libnm-systemd-core/src/libsystemd-network/sd-dhcp6-client.c b/src/libnm-systemd-core/src/libsystemd-network/sd-dhcp6-client.c
index 0e6f21f47b..943630ab8d 100644
--- a/src/libnm-systemd-core/src/libsystemd-network/sd-dhcp6-client.c
+++ b/src/libnm-systemd-core/src/libsystemd-network/sd-dhcp6-client.c
@@ -7,27 +7,22 @@
#include <errno.h>
#include <sys/ioctl.h>
-#if 0 /* NM_IGNORED */
#include <linux/if_arp.h>
-#else /* NM_IGNORED */
-#include <net/if_arp.h>
-#endif /* NM_IGNORED */
#include <linux/if_infiniband.h>
#include "sd-dhcp6-client.h"
#include "alloc-util.h"
#include "device-util.h"
-#include "dhcp-identifier.h"
+#include "dhcp-duid-internal.h"
#include "dhcp6-internal.h"
#include "dhcp6-lease-internal.h"
#include "dns-domain.h"
#include "event-util.h"
#include "fd-util.h"
-#include "hexdecoct.h"
#include "hostname-util.h"
#include "in-addr-util.h"
-#include "io-util.h"
+#include "iovec-util.h"
#include "random-util.h"
#include "socket-util.h"
#include "sort-util.h"
@@ -52,6 +47,19 @@ int sd_dhcp6_client_set_callback(
return 0;
}
+int dhcp6_client_set_state_callback(
+ sd_dhcp6_client *client,
+ sd_dhcp6_client_callback_t cb,
+ void *userdata) {
+
+ assert_return(client, -EINVAL);
+
+ client->state_callback = cb;
+ client->state_userdata = userdata;
+
+ return 0;
+}
+
int sd_dhcp6_client_set_ifindex(sd_dhcp6_client *client, int ifindex) {
assert_return(client, -EINVAL);
assert_return(!sd_dhcp6_client_is_running(client), -EBUSY);
@@ -184,10 +192,10 @@ int sd_dhcp6_client_add_vendor_option(sd_dhcp6_client *client, sd_dhcp6_option *
static int client_ensure_duid(sd_dhcp6_client *client) {
assert(client);
- if (client->duid_len != 0)
+ if (sd_dhcp_duid_is_set(&client->duid))
return 0;
- return dhcp_identifier_set_duid_en(client->test_mode, &client->duid, &client->duid_len);
+ return sd_dhcp6_client_set_duid_en(client);
}
/**
@@ -195,101 +203,102 @@ static int client_ensure_duid(sd_dhcp6_client *client) {
* without further modification. Otherwise, if duid_type is supported, DUID
* is set based on that type. Otherwise, an error is returned.
*/
-static int dhcp6_client_set_duid_internal(
- sd_dhcp6_client *client,
- DUIDType duid_type,
- const void *duid,
- size_t duid_len,
- usec_t llt_time) {
+int sd_dhcp6_client_set_duid_llt(sd_dhcp6_client *client, uint64_t llt_time) {
int r;
assert_return(client, -EINVAL);
assert_return(!sd_dhcp6_client_is_running(client), -EBUSY);
- assert_return(duid_len == 0 || duid, -EINVAL);
- if (duid) {
- r = dhcp_validate_duid_len(duid_type, duid_len, true);
- if (r < 0) {
- r = dhcp_validate_duid_len(duid_type, duid_len, false);
- if (r < 0)
- return log_dhcp6_client_errno(client, r, "Failed to validate length of DUID: %m");
+ r = sd_dhcp_duid_set_llt(&client->duid, client->hw_addr.bytes, client->hw_addr.length, client->arp_type, llt_time);
+ if (r < 0)
+ return log_dhcp6_client_errno(client, r, "Failed to set DUID-LLT: %m");
- log_dhcp6_client(client, "Using DUID of type %i of incorrect length, proceeding.", duid_type);
- }
+ return 0;
+}
- client->duid.type = htobe16(duid_type);
- memcpy(&client->duid.raw.data, duid, duid_len);
- client->duid_len = sizeof(client->duid.type) + duid_len;
+int sd_dhcp6_client_set_duid_ll(sd_dhcp6_client *client) {
+ int r;
- } else {
-#if 0 /* NM_IGNORED */
- r = dhcp_identifier_set_duid(duid_type, &client->hw_addr, client->arp_type, llt_time,
- client->test_mode, &client->duid, &client->duid_len);
- if (r == -EOPNOTSUPP)
- return log_dhcp6_client_errno(client, r,
- "Failed to set %s. MAC address is not set or "
- "interface type is not supported.",
- duid_type_to_string(duid_type));
- if (r < 0)
- return log_dhcp6_client_errno(client, r, "Failed to set %s: %m",
- duid_type_to_string(duid_type));
-#else /* NM_IGNORED */
- g_return_val_if_reached (-EINVAL);
-#endif /* NM_IGNORED */
- }
+ assert_return(client, -EINVAL);
+ assert_return(!sd_dhcp6_client_is_running(client), -EBUSY);
+
+ r = sd_dhcp_duid_set_ll(&client->duid, client->hw_addr.bytes, client->hw_addr.length, client->arp_type);
+ if (r < 0)
+ return log_dhcp6_client_errno(client, r, "Failed to set DUID-LL: %m");
return 0;
}
-int sd_dhcp6_client_set_duid(
- sd_dhcp6_client *client,
- uint16_t duid_type,
- const void *duid,
- size_t duid_len) {
- return dhcp6_client_set_duid_internal(client, duid_type, duid, duid_len, 0);
+int sd_dhcp6_client_set_duid_en(sd_dhcp6_client *client) {
+ int r;
+
+ assert_return(client, -EINVAL);
+ assert_return(!sd_dhcp6_client_is_running(client), -EBUSY);
+
+ r = sd_dhcp_duid_set_en(&client->duid);
+ if (r < 0)
+ return log_dhcp6_client_errno(client, r, "Failed to set DUID-EN: %m");
+
+ return 0;
}
-int sd_dhcp6_client_set_duid_llt(
- sd_dhcp6_client *client,
- usec_t llt_time) {
- return dhcp6_client_set_duid_internal(client, DUID_TYPE_LLT, NULL, 0, llt_time);
+int sd_dhcp6_client_set_duid_uuid(sd_dhcp6_client *client) {
+ int r;
+
+ assert_return(client, -EINVAL);
+ assert_return(!sd_dhcp6_client_is_running(client), -EBUSY);
+
+ r = sd_dhcp_duid_set_uuid(&client->duid);
+ if (r < 0)
+ return log_dhcp6_client_errno(client, r, "Failed to set DUID-UUID: %m");
+
+ return 0;
}
-int sd_dhcp6_client_duid_as_string(
- sd_dhcp6_client *client,
- char **duid) {
- _cleanup_free_ char *p = NULL, *s = NULL, *t = NULL;
- const char *v;
+int sd_dhcp6_client_set_duid_raw(sd_dhcp6_client *client, uint16_t duid_type, const uint8_t *duid, size_t duid_len) {
int r;
assert_return(client, -EINVAL);
- assert_return(client->duid_len > 0, -ENODATA);
- assert_return(duid, -EINVAL);
+ assert_return(!sd_dhcp6_client_is_running(client), -EBUSY);
+ assert_return(duid || duid_len == 0, -EINVAL);
- v = duid_type_to_string(be16toh(client->duid.type));
- if (v) {
- s = strdup(v);
- if (!s)
- return -ENOMEM;
- } else {
- r = asprintf(&s, "%0x", client->duid.type);
- if (r < 0)
- return -ENOMEM;
- }
+ r = sd_dhcp_duid_set(&client->duid, duid_type, duid, duid_len);
+ if (r < 0)
+ return log_dhcp6_client_errno(client, r, "Failed to set DUID: %m");
- t = hexmem(&client->duid.raw.data, client->duid_len);
- if (!t)
- return -ENOMEM;
+ return 0;
+}
- p = strjoin(s, ":", t);
- if (!p)
- return -ENOMEM;
+int sd_dhcp6_client_set_duid(sd_dhcp6_client *client, const sd_dhcp_duid *duid) {
+ assert_return(client, -EINVAL);
+ assert_return(!sd_dhcp6_client_is_running(client), -EBUSY);
+ assert_return(sd_dhcp_duid_is_set(duid), -EINVAL);
- *duid = TAKE_PTR(p);
+ client->duid = *duid;
+ return 0;
+}
+int sd_dhcp6_client_get_duid(sd_dhcp6_client *client, const sd_dhcp_duid **ret) {
+ assert_return(client, -EINVAL);
+ assert_return(ret, -EINVAL);
+
+ if (!sd_dhcp_duid_is_set(&client->duid))
+ return -ENODATA;
+
+ *ret = &client->duid;
return 0;
}
+int sd_dhcp6_client_get_duid_as_string(sd_dhcp6_client *client, char **ret) {
+ assert_return(client, -EINVAL);
+ assert_return(ret, -EINVAL);
+
+ if (!sd_dhcp_duid_is_set(&client->duid))
+ return -ENODATA;
+
+ return sd_dhcp_duid_to_string(&client->duid, ret);
+}
+
int sd_dhcp6_client_set_iaid(sd_dhcp6_client *client, uint32_t iaid) {
assert_return(client, -EINVAL);
assert_return(!sd_dhcp6_client_is_running(client), -EBUSY);
@@ -335,12 +344,6 @@ int sd_dhcp6_client_get_iaid(sd_dhcp6_client *client, uint32_t *iaid) {
return 0;
}
-void dhcp6_client_set_test_mode(sd_dhcp6_client *client, bool test_mode) {
- assert(client);
-
- client->test_mode = test_mode;
-}
-
int sd_dhcp6_client_set_fqdn(
sd_dhcp6_client *client,
const char *fqdn) {
@@ -491,7 +494,7 @@ int sd_dhcp6_client_set_address_request(sd_dhcp6_client *client, int request) {
int dhcp6_client_set_transaction_id(sd_dhcp6_client *client, uint32_t transaction_id) {
assert(client);
- assert(client->test_mode);
+ assert_se(network_test_mode_enabled());
/* This is for tests or fuzzers. */
@@ -510,7 +513,6 @@ int sd_dhcp6_client_set_rapid_commit(sd_dhcp6_client *client, int enable) {
int sd_dhcp6_client_set_send_release(sd_dhcp6_client *client, int enable) {
assert_return(client, -EINVAL);
- assert_return(!sd_dhcp6_client_is_running(client), -EBUSY);
client->send_release = enable;
return 0;
@@ -552,6 +554,15 @@ static void client_set_state(sd_dhcp6_client *client, DHCP6State state) {
dhcp6_state_to_string(client->state), dhcp6_state_to_string(state));
client->state = state;
+
+ if (client->state_callback)
+ client->state_callback(client, state, client->state_userdata);
+}
+
+int dhcp6_client_get_state(sd_dhcp6_client *client) {
+ assert_return(client, -EINVAL);
+
+ return client->state;
}
static void client_notify(sd_dhcp6_client *client, int event) {
@@ -809,9 +820,9 @@ int dhcp6_client_send_message(sd_dhcp6_client *client) {
if (r < 0)
return r;
- assert(client->duid_len > 0);
+ assert(sd_dhcp_duid_is_set(&client->duid));
r = dhcp6_option_append(&buf, &offset, SD_DHCP6_OPTION_CLIENTID,
- client->duid_len, &client->duid);
+ client->duid.size, &client->duid.duid);
if (r < 0)
return r;
@@ -824,7 +835,7 @@ int dhcp6_client_send_message(sd_dhcp6_client *client) {
/* RFC 8415 Section 21.9.
* A client MUST include an Elapsed Time option in messages to indicate how long the client has
* been trying to complete a DHCP message exchange. */
- elapsed_usec = NM_MIN(usec_sub_unsigned(time_now, client->transaction_start) / USEC_PER_MSEC / 10, (usec_t) UINT16_MAX);
+ elapsed_usec = MIN(usec_sub_unsigned(time_now, client->transaction_start) / USEC_PER_MSEC / 10, (usec_t) UINT16_MAX);
elapsed_time = htobe16(elapsed_usec);
r = dhcp6_option_append(&buf, &offset, SD_DHCP6_OPTION_ELAPSED_TIME, sizeof(elapsed_time), &elapsed_time);
if (r < 0)
@@ -1047,12 +1058,20 @@ static int client_enter_bound_state(sd_dhcp6_client *client) {
(void) event_source_disable(client->receive_message);
(void) event_source_disable(client->timeout_resend);
- r = dhcp6_lease_get_lifetime(client->lease, &lifetime_t1, &lifetime_t2, &lifetime_valid);
+ r = sd_dhcp6_lease_get_t1(client->lease, &lifetime_t1);
+ if (r < 0)
+ goto error;
+
+ r = sd_dhcp6_lease_get_t2(client->lease, &lifetime_t2);
+ if (r < 0)
+ goto error;
+
+ r = sd_dhcp6_lease_get_valid_lifetime(client->lease, &lifetime_valid);
if (r < 0)
goto error;
lifetime_t2 = client_timeout_compute_random(lifetime_t2);
- lifetime_t1 = client_timeout_compute_random(NM_MIN(lifetime_t1, lifetime_t2));
+ lifetime_t1 = client_timeout_compute_random(MIN(lifetime_t1, lifetime_t2));
if (lifetime_t1 == USEC_INFINITY) {
log_dhcp6_client(client, "Infinite T1");
@@ -1286,16 +1305,15 @@ static int client_receive_message(
.msg_control = &control,
.msg_controllen = sizeof(control),
};
- triple_timestamp t = {};
+ triple_timestamp t;
_cleanup_free_ DHCP6Message *message = NULL;
struct in6_addr *server_address = NULL;
ssize_t buflen, len;
buflen = next_datagram_size_fd(fd);
+ if (ERRNO_IS_NEG_TRANSIENT(buflen) || ERRNO_IS_NEG_DISCONNECT(buflen))
+ return 0;
if (buflen < 0) {
- if (ERRNO_IS_TRANSIENT(buflen) || ERRNO_IS_DISCONNECT(buflen))
- return 0;
-
log_dhcp6_client_errno(client, buflen, "Failed to determine datagram size to read, ignoring: %m");
return 0;
}
@@ -1307,10 +1325,9 @@ static int client_receive_message(
iov = IOVEC_MAKE(message, buflen);
len = recvmsg_safe(fd, &msg, MSG_DONTWAIT);
+ if (ERRNO_IS_NEG_TRANSIENT(len) || ERRNO_IS_NEG_DISCONNECT(len))
+ return 0;
if (len < 0) {
- if (ERRNO_IS_TRANSIENT(len) || ERRNO_IS_DISCONNECT(len))
- return 0;
-
log_dhcp6_client_errno(client, len, "Could not receive message from UDP socket, ignoring: %m");
return 0;
}
@@ -1329,9 +1346,7 @@ static int client_receive_message(
server_address = &sa.in6.sin6_addr;
}
- struct timeval *tv = CMSG_FIND_AND_COPY_DATA(&msg, SOL_SOCKET, SCM_TIMESTAMP, struct timeval);
- if (tv)
- triple_timestamp_from_realtime(&t, timeval_load(tv));
+ triple_timestamp_from_cmsg(&t, &msg);
if (client->transaction_id != (message->transaction_id & htobe32(0x00ffffff)))
return 0;
diff --git a/src/libnm-systemd-core/src/libsystemd-network/sd-dhcp6-lease.c b/src/libnm-systemd-core/src/libsystemd-network/sd-dhcp6-lease.c
index 3e25b4e819..a42df24375 100644
--- a/src/libnm-systemd-core/src/libsystemd-network/sd-dhcp6-lease.c
+++ b/src/libnm-systemd-core/src/libsystemd-network/sd-dhcp6-lease.c
@@ -10,7 +10,9 @@
#include "alloc-util.h"
#include "dhcp6-internal.h"
#include "dhcp6-lease-internal.h"
+#include "network-common.h"
#include "strv.h"
+#include "unaligned.h"
#define IRT_DEFAULT (1 * USEC_PER_DAY)
#define IRT_MINIMUM (600 * USEC_PER_SEC)
@@ -21,7 +23,7 @@ static void dhcp6_lease_set_timestamp(sd_dhcp6_lease *lease, const triple_timest
if (timestamp && triple_timestamp_is_set(timestamp))
lease->timestamp = *timestamp;
else
- triple_timestamp_get(&lease->timestamp);
+ triple_timestamp_now(&lease->timestamp);
}
int sd_dhcp6_lease_get_timestamp(sd_dhcp6_lease *lease, clockid_t clock, uint64_t *ret) {
@@ -37,30 +39,26 @@ int sd_dhcp6_lease_get_timestamp(sd_dhcp6_lease *lease, clockid_t clock, uint64_
return 0;
}
-static usec_t sec2usec(uint32_t sec) {
- return sec == UINT32_MAX ? USEC_INFINITY : sec * USEC_PER_SEC;
-}
-
static void dhcp6_lease_set_lifetime(sd_dhcp6_lease *lease) {
- uint32_t t1 = UINT32_MAX, t2 = UINT32_MAX, min_valid_lt = UINT32_MAX;
+ usec_t t1 = USEC_INFINITY, t2 = USEC_INFINITY, min_valid_lt = USEC_INFINITY;
assert(lease);
assert(lease->ia_na || lease->ia_pd);
if (lease->ia_na) {
- t1 = MIN(t1, be32toh(lease->ia_na->header.lifetime_t1));
- t2 = MIN(t2, be32toh(lease->ia_na->header.lifetime_t2));
+ t1 = MIN(t1, be32_sec_to_usec(lease->ia_na->header.lifetime_t1, /* max_as_infinity = */ true));
+ t2 = MIN(t2, be32_sec_to_usec(lease->ia_na->header.lifetime_t2, /* max_as_infinity = */ true));
LIST_FOREACH(addresses, a, lease->ia_na->addresses)
- min_valid_lt = MIN(min_valid_lt, be32toh(a->iaaddr.lifetime_valid));
+ min_valid_lt = MIN(min_valid_lt, be32_sec_to_usec(a->iaaddr.lifetime_valid, /* max_as_infinity = */ true));
}
if (lease->ia_pd) {
- t1 = MIN(t1, be32toh(lease->ia_pd->header.lifetime_t1));
- t2 = MIN(t2, be32toh(lease->ia_pd->header.lifetime_t2));
+ t1 = MIN(t1, be32_sec_to_usec(lease->ia_pd->header.lifetime_t1, /* max_as_infinity = */ true));
+ t2 = MIN(t2, be32_sec_to_usec(lease->ia_pd->header.lifetime_t2, /* max_as_infinity = */ true));
LIST_FOREACH(addresses, a, lease->ia_pd->addresses)
- min_valid_lt = MIN(min_valid_lt, be32toh(a->iapdprefix.lifetime_valid));
+ min_valid_lt = MIN(min_valid_lt, be32_sec_to_usec(a->iapdprefix.lifetime_valid, /* max_as_infinity = */ true));
}
if (t2 == 0 || t2 > min_valid_lt) {
@@ -70,25 +68,52 @@ static void dhcp6_lease_set_lifetime(sd_dhcp6_lease *lease) {
t2 = min_valid_lt / 10 * 8;
}
- lease->lifetime_valid = sec2usec(min_valid_lt);
- lease->lifetime_t1 = sec2usec(t1);
- lease->lifetime_t2 = sec2usec(t2);
-}
-
-int dhcp6_lease_get_lifetime(sd_dhcp6_lease *lease, usec_t *ret_t1, usec_t *ret_t2, usec_t *ret_valid) {
- assert(lease);
+ lease->lifetime_valid = min_valid_lt;
+ lease->lifetime_t1 = t1;
+ lease->lifetime_t2 = t2;
+}
+
+#define DEFINE_GET_TIME_FUNCTIONS(name, val) \
+ int sd_dhcp6_lease_get_##name( \
+ sd_dhcp6_lease *lease, \
+ uint64_t *ret) { \
+ \
+ assert_return(lease, -EINVAL); \
+ \
+ if (!lease->ia_na && !lease->ia_pd) \
+ return -ENODATA; \
+ \
+ if (ret) \
+ *ret = lease->val; \
+ return 0; \
+ } \
+ \
+ int sd_dhcp6_lease_get_##name##_timestamp( \
+ sd_dhcp6_lease *lease, \
+ clockid_t clock, \
+ uint64_t *ret) { \
+ \
+ usec_t s, t; \
+ int r; \
+ \
+ assert_return(lease, -EINVAL); \
+ \
+ r = sd_dhcp6_lease_get_##name(lease, &s); \
+ if (r < 0) \
+ return r; \
+ \
+ r = sd_dhcp6_lease_get_timestamp(lease, clock, &t); \
+ if (r < 0) \
+ return r; \
+ \
+ if (ret) \
+ *ret = time_span_to_stamp(s, t); \
+ return 0; \
+ }
- if (!lease->ia_na && !lease->ia_pd)
- return -ENODATA;
-
- if (ret_t1)
- *ret_t1 = lease->lifetime_t1;
- if (ret_t2)
- *ret_t2 = lease->lifetime_t2;
- if (ret_valid)
- *ret_valid = lease->lifetime_valid;
- return 0;
-}
+DEFINE_GET_TIME_FUNCTIONS(t1, lifetime_t1);
+DEFINE_GET_TIME_FUNCTIONS(t2, lifetime_t1);
+DEFINE_GET_TIME_FUNCTIONS(valid_lifetime, lifetime_valid);
static void dhcp6_lease_set_server_address(sd_dhcp6_lease *lease, const struct in6_addr *server_address) {
assert(lease);
@@ -218,61 +243,151 @@ int dhcp6_lease_get_rapid_commit(sd_dhcp6_lease *lease, bool *ret) {
return 0;
}
-int sd_dhcp6_lease_get_address(
+int sd_dhcp6_lease_get_address(sd_dhcp6_lease *lease, struct in6_addr *ret) {
+ assert_return(lease, -EINVAL);
+
+ if (!lease->addr_iter)
+ return -ENODATA;
+
+ if (ret)
+ *ret = lease->addr_iter->iaaddr.address;
+ return 0;
+}
+
+int sd_dhcp6_lease_get_address_lifetime(
sd_dhcp6_lease *lease,
- struct in6_addr *ret_addr,
- uint32_t *ret_lifetime_preferred,
- uint32_t *ret_lifetime_valid) {
+ usec_t *ret_lifetime_preferred,
+ usec_t *ret_lifetime_valid) {
+
+ const struct iaaddr *a;
assert_return(lease, -EINVAL);
if (!lease->addr_iter)
return -ENODATA;
- if (ret_addr)
- *ret_addr = lease->addr_iter->iaaddr.address;
+ a = &lease->addr_iter->iaaddr;
+
if (ret_lifetime_preferred)
- *ret_lifetime_preferred = be32toh(lease->addr_iter->iaaddr.lifetime_preferred);
+ *ret_lifetime_preferred = be32_sec_to_usec(a->lifetime_preferred, /* max_as_infinity = */ true);
if (ret_lifetime_valid)
- *ret_lifetime_valid = be32toh(lease->addr_iter->iaaddr.lifetime_valid);
+ *ret_lifetime_valid = be32_sec_to_usec(a->lifetime_valid, /* max_as_infinity = */ true);
+ return 0;
+}
+
+int sd_dhcp6_lease_address_iterator_reset(sd_dhcp6_lease *lease) {
+ if (!lease)
+ return false;
+
+ lease->addr_iter = lease->ia_na ? lease->ia_na->addresses : NULL;
+ return !!lease->addr_iter;
+}
+
+int sd_dhcp6_lease_address_iterator_next(sd_dhcp6_lease *lease) {
+ if (!lease || !lease->addr_iter)
+ return false;
lease->addr_iter = lease->addr_iter->addresses_next;
- return 0;
+ return !!lease->addr_iter;
}
-void sd_dhcp6_lease_reset_address_iter(sd_dhcp6_lease *lease) {
- if (lease)
- lease->addr_iter = lease->ia_na ? lease->ia_na->addresses : NULL;
+int sd_dhcp6_lease_has_address(sd_dhcp6_lease *lease) {
+ return lease && lease->ia_na;
}
-int sd_dhcp6_lease_get_pd(
+int sd_dhcp6_lease_get_pd_prefix(
sd_dhcp6_lease *lease,
struct in6_addr *ret_prefix,
- uint8_t *ret_prefix_len,
- uint32_t *ret_lifetime_preferred,
- uint32_t *ret_lifetime_valid) {
+ uint8_t *ret_prefix_len) {
+
+ const struct iapdprefix *a;
assert_return(lease, -EINVAL);
if (!lease->prefix_iter)
return -ENODATA;
+ a = &lease->prefix_iter->iapdprefix;
+
if (ret_prefix)
- *ret_prefix = lease->prefix_iter->iapdprefix.address;
+ *ret_prefix = a->address;
if (ret_prefix_len)
- *ret_prefix_len = lease->prefix_iter->iapdprefix.prefixlen;
+ *ret_prefix_len = a->prefixlen;
+ return 0;
+}
+
+int sd_dhcp6_lease_get_pd_lifetime(
+ sd_dhcp6_lease *lease,
+ uint64_t *ret_lifetime_preferred,
+ uint64_t *ret_lifetime_valid) {
+
+ const struct iapdprefix *a;
+
+ assert_return(lease, -EINVAL);
+
+ if (!lease->prefix_iter)
+ return -ENODATA;
+
+ a = &lease->prefix_iter->iapdprefix;
+
if (ret_lifetime_preferred)
- *ret_lifetime_preferred = be32toh(lease->prefix_iter->iapdprefix.lifetime_preferred);
+ *ret_lifetime_preferred = be32_sec_to_usec(a->lifetime_preferred, /* max_as_infinity = */ true);
if (ret_lifetime_valid)
- *ret_lifetime_valid = be32toh(lease->prefix_iter->iapdprefix.lifetime_valid);
-
- lease->prefix_iter = lease->prefix_iter->addresses_next;
+ *ret_lifetime_valid = be32_sec_to_usec(a->lifetime_valid, /* max_as_infinity = */ true);
return 0;
}
-void sd_dhcp6_lease_reset_pd_prefix_iter(sd_dhcp6_lease *lease) {
- if (lease)
- lease->prefix_iter = lease->ia_pd ? lease->ia_pd->addresses : NULL;
+int sd_dhcp6_lease_pd_iterator_reset(sd_dhcp6_lease *lease) {
+ if (!lease)
+ return false;
+
+ lease->prefix_iter = lease->ia_pd ? lease->ia_pd->addresses : NULL;
+ return !!lease->prefix_iter;
+}
+
+int sd_dhcp6_lease_pd_iterator_next(sd_dhcp6_lease *lease) {
+ if (!lease || !lease->prefix_iter)
+ return false;
+
+ lease->prefix_iter = lease->prefix_iter->addresses_next;
+ return !!lease->prefix_iter;
+}
+
+#define DEFINE_GET_TIMESTAMP2(name) \
+ int sd_dhcp6_lease_get_##name##_lifetime_timestamp( \
+ sd_dhcp6_lease *lease, \
+ clockid_t clock, \
+ uint64_t *ret_lifetime_preferred, \
+ uint64_t *ret_lifetime_valid) { \
+ \
+ usec_t t, p, v; \
+ int r; \
+ \
+ assert_return(lease, -EINVAL); \
+ \
+ r = sd_dhcp6_lease_get_##name##_lifetime( \
+ lease, \
+ ret_lifetime_preferred ? &p : NULL, \
+ ret_lifetime_valid ? &v : NULL); \
+ if (r < 0) \
+ return r; \
+ \
+ r = sd_dhcp6_lease_get_timestamp(lease, clock, &t); \
+ if (r < 0) \
+ return r; \
+ \
+ if (ret_lifetime_preferred) \
+ *ret_lifetime_preferred = time_span_to_stamp(p, t); \
+ if (ret_lifetime_valid) \
+ *ret_lifetime_valid = time_span_to_stamp(v, t); \
+ return 0; \
+ }
+
+DEFINE_GET_TIMESTAMP2(address);
+DEFINE_GET_TIMESTAMP2(pd);
+
+int sd_dhcp6_lease_has_pd_prefix(sd_dhcp6_lease *lease) {
+ return lease && lease->ia_pd;
}
int dhcp6_lease_add_dns(sd_dhcp6_lease *lease, const uint8_t *optval, size_t optlen) {
@@ -447,6 +562,111 @@ int sd_dhcp6_lease_get_fqdn(sd_dhcp6_lease *lease, const char **ret) {
return 0;
}
+int dhcp6_lease_set_captive_portal(sd_dhcp6_lease *lease, const uint8_t *optval, size_t optlen) {
+ _cleanup_free_ char *uri = NULL;
+ int r;
+
+ assert(lease);
+ assert(optval || optlen == 0);
+
+ r = dhcp6_option_parse_string(optval, optlen, &uri);
+ if (r < 0)
+ return r;
+
+ if (uri && !in_charset(uri, URI_VALID))
+ return -EINVAL;
+
+ return free_and_replace(lease->captive_portal, uri);
+}
+
+int sd_dhcp6_lease_get_captive_portal(sd_dhcp6_lease *lease, const char **ret) {
+ assert_return(lease, -EINVAL);
+ assert_return(ret, -EINVAL);
+
+ if (!lease->captive_portal)
+ return -ENODATA;
+
+ *ret = lease->captive_portal;
+ return 0;
+}
+
+int sd_dhcp6_lease_get_vendor_options(sd_dhcp6_lease *lease, sd_dhcp6_option ***ret) {
+ int r;
+
+ assert_return(lease, -EINVAL);
+
+ if (set_isempty(lease->vendor_options))
+ return -ENODATA;
+
+ if (ret) {
+ if (!lease->sorted_vendor_options) {
+ r = set_dump_sorted(lease->vendor_options, (void***) &lease->sorted_vendor_options, NULL);
+ if (r < 0)
+ return r;
+ }
+
+ *ret = lease->sorted_vendor_options;
+ }
+
+ return set_size(lease->vendor_options);
+}
+
+static int dhcp6_lease_insert_vendor_option(
+ sd_dhcp6_lease *lease,
+ uint16_t option_code,
+ const void *data,
+ size_t len,
+ uint32_t enterprise_id) {
+
+ _cleanup_(sd_dhcp6_option_unrefp) sd_dhcp6_option *option = NULL;
+
+ assert(lease);
+
+ option = new(sd_dhcp6_option, 1);
+ if (!option)
+ return -ENOMEM;
+
+ *option = (sd_dhcp6_option) {
+ .n_ref = 1,
+ .enterprise_identifier = enterprise_id,
+ .option = option_code,
+ .length = len,
+ };
+ option->data = memdup_suffix0(data, len);
+ if (!option->data)
+ return -ENOMEM;
+
+ return set_ensure_consume(&lease->vendor_options, &dhcp6_option_hash_ops, TAKE_PTR(option));
+}
+
+static int dhcp6_lease_add_vendor_option(sd_dhcp6_lease *lease, const uint8_t *optval, size_t optlen) {
+ int r;
+ uint32_t enterprise_id;
+
+ assert(lease);
+ assert(optval || optlen == 0);
+
+ if (optlen < sizeof(be32_t))
+ return -EBADMSG;
+
+ enterprise_id = unaligned_read_be32(optval);
+
+ for (size_t offset = 4; offset < optlen;) {
+ const uint8_t *subval;
+ size_t sublen;
+ uint16_t subopt;
+
+ r = dhcp6_option_parse(optval, optlen, &offset, &subopt, &sublen, &subval);
+ if (r < 0)
+ return r;
+
+ r = dhcp6_lease_insert_vendor_option(lease, subopt, subval, sublen, enterprise_id);
+ if (r < 0)
+ return r;
+ }
+ return 0;
+}
+
static int dhcp6_lease_parse_message(
sd_dhcp6_client *client,
sd_dhcp6_lease *lease,
@@ -467,6 +687,11 @@ static int dhcp6_lease_parse_message(
size_t optlen;
const uint8_t *optval;
+ if (len - offset < offsetof(DHCP6Option, data)) {
+ log_dhcp6_client(client, "Ignoring %zu invalid byte(s) at the end of the packet", len - offset);
+ break;
+ }
+
r = dhcp6_option_parse(message->options, len, &offset, &optcode, &optlen, &optval);
if (r < 0)
return log_dhcp6_client_errno(client, r,
@@ -607,6 +832,12 @@ static int dhcp6_lease_parse_message(
break;
+ case SD_DHCP6_OPTION_CAPTIVE_PORTAL:
+ r = dhcp6_lease_set_captive_portal(lease, optval, optlen);
+ if (r < 0)
+ log_dhcp6_client_errno(client, r, "Failed to parse captive portal option, ignoring: %m");
+ break;
+
case SD_DHCP6_OPTION_CLIENT_FQDN:
r = dhcp6_lease_set_fqdn(lease, optval, optlen);
if (r < 0)
@@ -619,7 +850,14 @@ static int dhcp6_lease_parse_message(
return log_dhcp6_client_errno(client, SYNTHETIC_ERRNO(EINVAL),
"Received information refresh time option with an invalid length (%zu).", optlen);
- irt = unaligned_read_be32(optval) * USEC_PER_SEC;
+ irt = unaligned_be32_sec_to_usec(optval, /* max_as_infinity = */ false);
+ break;
+
+ case SD_DHCP6_OPTION_VENDOR_OPTS:
+ r = dhcp6_lease_add_vendor_option(lease, optval, optlen);
+ if (r < 0)
+ log_dhcp6_client_errno(client, r, "Failed to parse vendor option, ignoring: %m");
+
break;
}
}
@@ -631,7 +869,7 @@ static int dhcp6_lease_parse_message(
"%s message does not contain client ID. Ignoring.",
dhcp6_message_type_to_string(message->type));
- if (memcmp_nn(clientid, clientid_len, &client->duid, client->duid_len) != 0)
+ if (memcmp_nn(clientid, clientid_len, &client->duid.duid, client->duid.size) != 0)
return log_dhcp6_client_errno(client, SYNTHETIC_ERRNO(EINVAL),
"The client ID in %s message does not match. Ignoring.",
dhcp6_message_type_to_string(message->type));
@@ -661,12 +899,15 @@ static sd_dhcp6_lease *dhcp6_lease_free(sd_dhcp6_lease *lease) {
if (!lease)
return NULL;
+ set_free(lease->vendor_options);
+ free(lease->sorted_vendor_options);
free(lease->clientid);
free(lease->serverid);
dhcp6_ia_free(lease->ia_na);
dhcp6_ia_free(lease->ia_pd);
free(lease->dns);
free(lease->fqdn);
+ free(lease->captive_portal);
strv_free(lease->domains);
free(lease->ntp);
strv_free(lease->ntp_fqdn);
diff --git a/src/libnm-systemd-core/src/libsystemd/sd-device/device-util.h b/src/libnm-systemd-core/src/libsystemd/sd-device/device-util.h
index a1b5e91edf..534a296715 100644
--- a/src/libnm-systemd-core/src/libsystemd/sd-device/device-util.h
+++ b/src/libnm-systemd-core/src/libsystemd/sd-device/device-util.h
@@ -7,39 +7,41 @@
#include "sd-device.h"
+#include "alloc-util.h"
#include "log.h"
#include "macro.h"
+#include "strv.h"
#define device_unref_and_replace(a, b) \
unref_and_replace_full(a, b, sd_device_ref, sd_device_unref)
-#define FOREACH_DEVICE_PROPERTY(device, key, value) \
- for (key = sd_device_get_property_first(device, &(value)); \
- key; \
- key = sd_device_get_property_next(device, &(value)))
+#define FOREACH_DEVICE_PROPERTY(device, key, value) \
+ for (const char *value, *key = sd_device_get_property_first(device, &value); \
+ key; \
+ key = sd_device_get_property_next(device, &value))
-#define FOREACH_DEVICE_TAG(device, tag) \
- for (tag = sd_device_get_tag_first(device); \
- tag; \
+#define FOREACH_DEVICE_TAG(device, tag) \
+ for (const char *tag = sd_device_get_tag_first(device); \
+ tag; \
tag = sd_device_get_tag_next(device))
-#define FOREACH_DEVICE_CURRENT_TAG(device, tag) \
- for (tag = sd_device_get_current_tag_first(device); \
- tag; \
+#define FOREACH_DEVICE_CURRENT_TAG(device, tag) \
+ for (const char *tag = sd_device_get_current_tag_first(device); \
+ tag; \
tag = sd_device_get_current_tag_next(device))
-#define FOREACH_DEVICE_SYSATTR(device, attr) \
- for (attr = sd_device_get_sysattr_first(device); \
- attr; \
+#define FOREACH_DEVICE_SYSATTR(device, attr) \
+ for (const char *attr = sd_device_get_sysattr_first(device); \
+ attr; \
attr = sd_device_get_sysattr_next(device))
-#define FOREACH_DEVICE_DEVLINK(device, devlink) \
- for (devlink = sd_device_get_devlink_first(device); \
- devlink; \
+#define FOREACH_DEVICE_DEVLINK(device, devlink) \
+ for (const char *devlink = sd_device_get_devlink_first(device); \
+ devlink; \
devlink = sd_device_get_devlink_next(device))
#define _FOREACH_DEVICE_CHILD(device, child, suffix_ptr) \
- for (child = sd_device_get_child_first(device, suffix_ptr); \
+ for (sd_device *child = sd_device_get_child_first(device, suffix_ptr); \
child; \
child = sd_device_get_child_next(device, suffix_ptr))
@@ -49,14 +51,14 @@
#define FOREACH_DEVICE_CHILD_WITH_SUFFIX(device, child, suffix) \
_FOREACH_DEVICE_CHILD(device, child, &suffix)
-#define FOREACH_DEVICE(enumerator, device) \
- for (device = sd_device_enumerator_get_device_first(enumerator); \
- device; \
+#define FOREACH_DEVICE(enumerator, device) \
+ for (sd_device *device = sd_device_enumerator_get_device_first(enumerator); \
+ device; \
device = sd_device_enumerator_get_device_next(enumerator))
-#define FOREACH_SUBSYSTEM(enumerator, device) \
- for (device = sd_device_enumerator_get_subsystem_first(enumerator); \
- device; \
+#define FOREACH_SUBSYSTEM(enumerator, device) \
+ for (sd_device *device = sd_device_enumerator_get_subsystem_first(enumerator); \
+ device; \
device = sd_device_enumerator_get_subsystem_next(enumerator))
#define log_device_full_errno_zerook(device, level, error, ...) \
@@ -81,17 +83,17 @@
#define log_device_full(device, level, ...) (void) log_device_full_errno_zerook(device, level, 0, __VA_ARGS__)
-#define log_device_debug(device, ...) log_device_full(device, LOG_DEBUG, __VA_ARGS__)
-#define log_device_info(device, ...) log_device_full(device, LOG_INFO, __VA_ARGS__)
-#define log_device_notice(device, ...) log_device_full(device, LOG_NOTICE, __VA_ARGS__)
+#define log_device_debug(device, ...) log_device_full(device, LOG_DEBUG, __VA_ARGS__)
+#define log_device_info(device, ...) log_device_full(device, LOG_INFO, __VA_ARGS__)
+#define log_device_notice(device, ...) log_device_full(device, LOG_NOTICE, __VA_ARGS__)
#define log_device_warning(device, ...) log_device_full(device, LOG_WARNING, __VA_ARGS__)
-#define log_device_error(device, ...) log_device_full(device, LOG_ERR, __VA_ARGS__)
+#define log_device_error(device, ...) log_device_full(device, LOG_ERR, __VA_ARGS__)
-#define log_device_debug_errno(device, error, ...) log_device_full_errno(device, LOG_DEBUG, error, __VA_ARGS__)
-#define log_device_info_errno(device, error, ...) log_device_full_errno(device, LOG_INFO, error, __VA_ARGS__)
-#define log_device_notice_errno(device, error, ...) log_device_full_errno(device, LOG_NOTICE, error, __VA_ARGS__)
+#define log_device_debug_errno(device, error, ...) log_device_full_errno(device, LOG_DEBUG, error, __VA_ARGS__)
+#define log_device_info_errno(device, error, ...) log_device_full_errno(device, LOG_INFO, error, __VA_ARGS__)
+#define log_device_notice_errno(device, error, ...) log_device_full_errno(device, LOG_NOTICE, error, __VA_ARGS__)
#define log_device_warning_errno(device, error, ...) log_device_full_errno(device, LOG_WARNING, error, __VA_ARGS__)
-#define log_device_error_errno(device, error, ...) log_device_full_errno(device, LOG_ERR, error, __VA_ARGS__)
+#define log_device_error_errno(device, error, ...) log_device_full_errno(device, LOG_ERR, error, __VA_ARGS__)
int devname_from_devnum(mode_t mode, dev_t devnum, char **ret);
static inline int devname_from_stat_rdev(const struct stat *st, char **ret) {
@@ -101,3 +103,13 @@ static inline int devname_from_stat_rdev(const struct stat *st, char **ret) {
int device_open_from_devnum(mode_t mode, dev_t devnum, int flags, char **ret);
char** device_make_log_fields(sd_device *device);
+
+bool device_in_subsystem(sd_device *device, const char *subsystem);
+bool device_is_devtype(sd_device *device, const char *devtype);
+
+static inline bool device_property_can_set(const char *property) {
+ return property &&
+ !STR_IN_SET(property,
+ "ACTION", "DEVLINKS", "DEVNAME", "DEVPATH", "DEVTYPE", "DRIVER",
+ "IFINDEX", "MAJOR", "MINOR", "SEQNUM", "SUBSYSTEM", "TAGS");
+}
diff --git a/src/libnm-systemd-core/src/libsystemd/sd-event/event-util.c b/src/libnm-systemd-core/src/libsystemd/sd-event/event-util.c
index 663296f408..ef0c2d2af2 100644
--- a/src/libnm-systemd-core/src/libsystemd/sd-event/event-util.c
+++ b/src/libnm-systemd-core/src/libsystemd/sd-event/event-util.c
@@ -99,16 +99,21 @@ int event_reset_time_relative(
const char *description,
bool force_reset) {
- usec_t usec_now;
int r;
assert(e);
- r = sd_event_now(e, clock, &usec_now);
- if (r < 0)
- return log_debug_errno(r, "sd-event: Failed to get the current time: %m");
+ if (usec > 0) {
+ usec_t usec_now;
+
+ r = sd_event_now(e, clock, &usec_now);
+ if (r < 0)
+ return log_debug_errno(r, "sd-event: Failed to get the current time: %m");
- return event_reset_time(e, s, clock, usec_add(usec_now, usec), accuracy, callback, userdata, priority, description, force_reset);
+ usec = usec_add(usec_now, usec);
+ }
+
+ return event_reset_time(e, s, clock, usec, accuracy, callback, userdata, priority, description, force_reset);
}
#if 0 /* NM_IGNORED */
@@ -149,4 +154,21 @@ int event_add_time_change(sd_event *e, sd_event_source **ret, sd_event_io_handle
return 0;
}
+
+int event_add_child_pidref(
+ sd_event *e,
+ sd_event_source **s,
+ const PidRef *pid,
+ int options,
+ sd_event_child_handler_t callback,
+ void *userdata) {
+
+ if (!pidref_is_set(pid))
+ return -ESRCH;
+
+ if (pid->fd >= 0)
+ return sd_event_add_child_pidfd(e, s, pid->fd, options, callback, userdata);
+
+ return sd_event_add_child(e, s, pid->pid, options, callback, userdata);
+}
#endif /* NM_IGNORED */
diff --git a/src/libnm-systemd-core/src/libsystemd/sd-event/event-util.h b/src/libnm-systemd-core/src/libsystemd/sd-event/event-util.h
index c185584412..ad0f2e7838 100644
--- a/src/libnm-systemd-core/src/libsystemd/sd-event/event-util.h
+++ b/src/libnm-systemd-core/src/libsystemd/sd-event/event-util.h
@@ -5,6 +5,8 @@
#include "sd-event.h"
+#include "pidref.h"
+
int event_reset_time(
sd_event *e,
sd_event_source **s,
@@ -32,3 +34,7 @@ static inline int event_source_disable(sd_event_source *s) {
}
int event_add_time_change(sd_event *e, sd_event_source **ret, sd_event_io_handler_t callback, void *userdata);
+
+#if 0 /* NM_IGNORED */
+int event_add_child_pidref(sd_event *e, sd_event_source **s, const PidRef *pid, int options, sd_event_child_handler_t callback, void *userdata);
+#endif /* NM_IGNORED */
diff --git a/src/libnm-systemd-core/src/libsystemd/sd-event/sd-event.c b/src/libnm-systemd-core/src/libsystemd/sd-event/sd-event.c
index 5c7ba1821b..33cf30800c 100644
--- a/src/libnm-systemd-core/src/libsystemd/sd-event/sd-event.c
+++ b/src/libnm-systemd-core/src/libsystemd/sd-event/sd-event.c
@@ -1169,10 +1169,10 @@ static int source_set_pending(sd_event_source *s, bool b) {
assert(s->inotify.inode_data->inotify_data);
if (b)
- s->inotify.inode_data->inotify_data->n_pending ++;
+ s->inotify.inode_data->inotify_data->n_pending++;
else {
assert(s->inotify.inode_data->inotify_data->n_pending > 0);
- s->inotify.inode_data->inotify_data->n_pending --;
+ s->inotify.inode_data->inotify_data->n_pending--;
}
}
@@ -1983,7 +1983,7 @@ _public_ int sd_event_add_memory_pressure(
env = secure_getenv("MEMORY_PRESSURE_WRITE");
if (env) {
- r = unbase64mem(env, SIZE_MAX, &write_buffer, &write_buffer_size);
+ r = unbase64mem(env, &write_buffer, &write_buffer_size);
if (r < 0)
return r;
}
@@ -2239,8 +2239,8 @@ static int inode_data_compare(const struct inode_data *x, const struct inode_dat
static void inode_data_hash_func(const struct inode_data *d, struct siphash *state) {
assert(d);
- siphash24_compress(&d->dev, sizeof(d->dev), state);
- siphash24_compress(&d->ino, sizeof(d->ino), state);
+ siphash24_compress_typesafe(d->dev, state);
+ siphash24_compress_typesafe(d->ino, state);
}
DEFINE_PRIVATE_HASH_OPS(inode_data_hash_ops, struct inode_data, inode_data_hash_func, inode_data_compare);
@@ -4008,7 +4008,7 @@ static int process_inotify(sd_event *e) {
if (r < 0)
return r;
if (r > 0)
- done ++;
+ done++;
}
return done;
@@ -4620,7 +4620,7 @@ static int process_epoll(sd_event *e, usec_t timeout, int64_t threshold, int64_t
/* Set timestamp only when this is called first time. */
if (threshold == INT64_MAX)
- triple_timestamp_get(&e->timestamp);
+ triple_timestamp_now(&e->timestamp);
for (size_t i = 0; i < m; i++) {
@@ -5047,7 +5047,7 @@ _public_ int sd_event_set_watchdog(sd_event *e, int b) {
}
}
- e->watchdog = !!b;
+ e->watchdog = b;
return e->watchdog;
fail:
diff --git a/src/libnm-systemd-core/src/libsystemd/sd-id128/id128-util.c b/src/libnm-systemd-core/src/libsystemd/sd-id128/id128-util.c
index c20f7325ad..1fc011c7f3 100644
--- a/src/libnm-systemd-core/src/libsystemd/sd-id128/id128-util.c
+++ b/src/libnm-systemd-core/src/libsystemd/sd-id128/id128-util.c
@@ -11,9 +11,29 @@
#include "hexdecoct.h"
#include "id128-util.h"
#include "io-util.h"
+#include "sha256.h"
#include "stdio-util.h"
#include "string-util.h"
+#include "strv.h"
#include "sync-util.h"
+#include "virt.h"
+
+int id128_from_string_nonzero(const char *s, sd_id128_t *ret) {
+ sd_id128_t t;
+ int r;
+
+ assert(ret);
+
+ r = sd_id128_from_string(ASSERT_PTR(s), &t);
+ if (r < 0)
+ return r;
+
+ if (sd_id128_is_null(t))
+ return -ENXIO;
+
+ *ret = t;
+ return 0;
+}
#if 0 /* NM_IGNORED */
bool id128_is_valid(const char *s) {
@@ -24,7 +44,7 @@ bool id128_is_valid(const char *s) {
l = strlen(s);
if (l == SD_ID128_STRING_MAX - 1)
- /* Plain formatted 128bit hex string */
+ /* Plain formatted 128-bit hex string */
return in_charset(s, HEXDIGITS);
if (l == SD_ID128_UUID_STRING_MAX - 1) {
@@ -53,7 +73,7 @@ int id128_read_fd(int fd, Id128Flag f, sd_id128_t *ret) {
assert(fd >= 0);
- /* Reads an 128bit ID from a file, which may either be in plain format (32 hex digits), or in UUID format, both
+ /* Reads an 128-bit ID from a file, which may either be in plain format (32 hex digits), or in UUID format, both
* optionally followed by a newline and nothing else. ID files should really be newline terminated, but if they
* aren't that's OK too, following the rule of "Be conservative in what you send, be liberal in what you
* accept".
@@ -151,7 +171,7 @@ int id128_write_fd(int fd, Id128Flag f, sd_id128_t id) {
}
buffer[sz - 1] = '\n';
- r = loop_write(fd, buffer, sz, false);
+ r = loop_write(fd, buffer, sz);
if (r < 0)
return r;
@@ -178,11 +198,11 @@ int id128_write_at(int dir_fd, const char *path, Id128Flag f, sd_id128_t id) {
}
void id128_hash_func(const sd_id128_t *p, struct siphash *state) {
- siphash24_compress(p, sizeof(sd_id128_t), state);
+ siphash24_compress_typesafe(*p, state);
}
int id128_compare_func(const sd_id128_t *a, const sd_id128_t *b) {
- return memcmp(a, b, 16);
+ return memcmp(a, b, sizeof(sd_id128_t));
}
sd_id128_t id128_make_v4_uuid(sd_id128_t id) {
@@ -210,9 +230,22 @@ int id128_get_product(sd_id128_t *ret) {
/* Reads the systems product UUID from DMI or devicetree (where it is located on POWER). This is
* particularly relevant in VM environments, where VM managers typically place a VM uuid there. */
- r = id128_read("/sys/class/dmi/id/product_uuid", ID128_FORMAT_UUID, &uuid);
- if (r == -ENOENT)
- r = id128_read("/proc/device-tree/vm,uuid", ID128_FORMAT_UUID, &uuid);
+ r = detect_container();
+ if (r < 0)
+ return r;
+ if (r > 0) /* Refuse returning this in containers, as this is not a property of our system then, but
+ * of the host */
+ return -ENOENT;
+
+ FOREACH_STRING(i,
+ "/sys/class/dmi/id/product_uuid", /* KVM */
+ "/proc/device-tree/vm,uuid", /* Device tree */
+ "/sys/hypervisor/uuid") { /* Xen */
+
+ r = id128_read(i, ID128_FORMAT_UUID, &uuid);
+ if (r != -ENOENT)
+ break;
+ }
if (r < 0)
return r;
@@ -222,4 +255,22 @@ int id128_get_product(sd_id128_t *ret) {
*ret = uuid;
return 0;
}
+
+sd_id128_t id128_digest(const void *data, size_t size) {
+ assert(data || size == 0);
+
+ /* Hashes a UUID from some arbitrary data */
+
+ if (size == SIZE_MAX)
+ size = strlen(data);
+
+ uint8_t h[SHA256_DIGEST_SIZE];
+ sd_id128_t id;
+
+ /* Take the first half of the SHA256 result */
+ assert_cc(sizeof(h) >= sizeof(id.bytes));
+ memcpy(id.bytes, sha256_direct(data, size, h), sizeof(id.bytes));
+
+ return id128_make_v4_uuid(id);
+}
#endif /* NM_IGNORED */
diff --git a/src/libnm-systemd-core/src/libsystemd/sd-id128/id128-util.h b/src/libnm-systemd-core/src/libsystemd/sd-id128/id128-util.h
index 7bcbd8e558..53ba50a8ac 100644
--- a/src/libnm-systemd-core/src/libsystemd/sd-id128/id128-util.h
+++ b/src/libnm-systemd-core/src/libsystemd/sd-id128/id128-util.h
@@ -6,6 +6,7 @@
#include "sd-id128.h"
+#include "errno-util.h"
#include "hash-funcs.h"
#include "macro.h"
@@ -20,6 +21,8 @@ typedef enum Id128Flag {
ID128_REFUSE_NULL = 1 << 3, /* Refuse all zero ID with -ENOMEDIUM. */
} Id128Flag;
+int id128_from_string_nonzero(const char *s, sd_id128_t *ret);
+
int id128_read_fd(int fd, Id128Flag f, sd_id128_t *ret);
int id128_read_at(int dir_fd, const char *path, Id128Flag f, sd_id128_t *ret);
static inline int id128_read(const char *path, Id128Flag f, sd_id128_t *ret) {
@@ -44,9 +47,12 @@ sd_id128_t id128_make_v4_uuid(sd_id128_t id);
int id128_get_product(sd_id128_t *ret);
+sd_id128_t id128_digest(const void *data, size_t size);
+
/* A helper to check for the three relevant cases of "machine ID not initialized" */
-#define ERRNO_IS_MACHINE_ID_UNSET(r) \
- IN_SET(abs(r), \
- ENOENT, \
- ENOMEDIUM, \
- ENOPKG)
+#define ERRNO_IS_NEG_MACHINE_ID_UNSET(r) \
+ IN_SET(r, \
+ -ENOENT, \
+ -ENOMEDIUM, \
+ -ENOPKG)
+_DEFINE_ABS_WRAPPER(MACHINE_ID_UNSET);
diff --git a/src/libnm-systemd-core/src/libsystemd/sd-id128/sd-id128.c b/src/libnm-systemd-core/src/libsystemd/sd-id128/sd-id128.c
index c63e1a973f..ff0db77626 100644
--- a/src/libnm-systemd-core/src/libsystemd/sd-id128/sd-id128.c
+++ b/src/libnm-systemd-core/src/libsystemd/sd-id128/sd-id128.c
@@ -345,18 +345,20 @@ _public_ int sd_id128_randomize(sd_id128_t *ret) {
return 0;
}
-static int get_app_specific(sd_id128_t base, sd_id128_t app_id, sd_id128_t *ret) {
- uint8_t hmac[SHA256_DIGEST_SIZE];
- sd_id128_t result;
+_public_ int sd_id128_get_app_specific(sd_id128_t base, sd_id128_t app_id, sd_id128_t *ret) {
+ assert_cc(sizeof(sd_id128_t) < SHA256_DIGEST_SIZE); /* Check that we don't need to pad with zeros. */
+ union {
+ uint8_t hmac[SHA256_DIGEST_SIZE];
+ sd_id128_t result;
+ } buf;
- assert(ret);
+ assert_return(ret, -EINVAL);
+ assert_return(!sd_id128_is_null(app_id), -ENXIO);
- hmac_sha256(&base, sizeof(base), &app_id, sizeof(app_id), hmac);
+ hmac_sha256(&base, sizeof(base), &app_id, sizeof(app_id), buf.hmac);
/* Take only the first half. */
- memcpy(&result, hmac, MIN(sizeof(hmac), sizeof(result)));
-
- *ret = id128_make_v4_uuid(result);
+ *ret = id128_make_v4_uuid(buf.result);
return 0;
}
@@ -370,7 +372,7 @@ _public_ int sd_id128_get_machine_app_specific(sd_id128_t app_id, sd_id128_t *re
if (r < 0)
return r;
- return get_app_specific(id, app_id, ret);
+ return sd_id128_get_app_specific(id, app_id, ret);
}
_public_ int sd_id128_get_boot_app_specific(sd_id128_t app_id, sd_id128_t *ret) {
@@ -383,6 +385,6 @@ _public_ int sd_id128_get_boot_app_specific(sd_id128_t app_id, sd_id128_t *ret)
if (r < 0)
return r;
- return get_app_specific(id, app_id, ret);
+ return sd_id128_get_app_specific(id, app_id, ret);
}
#endif /* NM_IGNORED */
diff --git a/src/libnm-systemd-core/src/systemd/_sd-common.h b/src/libnm-systemd-core/src/systemd/_sd-common.h
index 6f657c2254..d4381d90f4 100644
--- a/src/libnm-systemd-core/src/systemd/_sd-common.h
+++ b/src/libnm-systemd-core/src/systemd/_sd-common.h
@@ -99,7 +99,7 @@ typedef void (*_sd_destroy_t)(void *userdata);
} \
struct _sd_useless_struct_to_allow_trailing_semicolon_
-/* The following macro should be used in all public enums, to force 64bit wideness on them, so that we can
+/* The following macro should be used in all public enums, to force 64-bit wideness on them, so that we can
* freely extend them later on, without breaking compatibility. */
#define _SD_ENUM_FORCE_S64(id) \
_SD_##id##_INT64_MIN = INT64_MIN, \
diff --git a/src/libnm-systemd-core/src/systemd/sd-device.h b/src/libnm-systemd-core/src/systemd/sd-device.h
index e3d647f75d..b67ec0f34d 100644
--- a/src/libnm-systemd-core/src/systemd/sd-device.h
+++ b/src/libnm-systemd-core/src/systemd/sd-device.h
@@ -129,6 +129,7 @@ sd_device *sd_device_enumerator_get_subsystem_next(sd_device_enumerator *enumera
int sd_device_enumerator_add_match_subsystem(sd_device_enumerator *enumerator, const char *subsystem, int match);
int sd_device_enumerator_add_match_sysattr(sd_device_enumerator *enumerator, const char *sysattr, const char *value, int match);
int sd_device_enumerator_add_match_property(sd_device_enumerator *enumerator, const char *property, const char *value);
+int sd_device_enumerator_add_match_property_required(sd_device_enumerator *enumerator, const char *property, const char *value);
int sd_device_enumerator_add_match_sysname(sd_device_enumerator *enumerator, const char *sysname);
int sd_device_enumerator_add_nomatch_sysname(sd_device_enumerator *enumerator, const char *sysname);
int sd_device_enumerator_add_match_tag(sd_device_enumerator *enumerator, const char *tag);
diff --git a/src/libnm-systemd-core/src/systemd/sd-dhcp-duid.h b/src/libnm-systemd-core/src/systemd/sd-dhcp-duid.h
new file mode 100644
index 0000000000..b1d2772a3e
--- /dev/null
+++ b/src/libnm-systemd-core/src/systemd/sd-dhcp-duid.h
@@ -0,0 +1,71 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+#ifndef foosddhcpduidhfoo
+#define foosddhcpduidhfoo
+
+/***
+ Copyright © 2013 Intel Corporation. All rights reserved.
+ systemd is free software; you can redistribute it and/or modify it
+ under the terms of the GNU Lesser General Public License as published by
+ the Free Software Foundation; either version 2.1 of the License, or
+ (at your option) any later version.
+
+ systemd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with systemd; If not, see <https://www.gnu.org/licenses/>.
+***/
+
+#include <inttypes.h>
+#include <sys/types.h>
+
+#include "_sd-common.h"
+
+_SD_BEGIN_DECLARATIONS;
+
+enum {
+ SD_DUID_TYPE_LLT = 1,
+ SD_DUID_TYPE_EN = 2,
+ SD_DUID_TYPE_LL = 3,
+ SD_DUID_TYPE_UUID = 4
+};
+
+typedef struct sd_dhcp_duid sd_dhcp_duid;
+
+int sd_dhcp_duid_clear(sd_dhcp_duid *duid);
+
+int sd_dhcp_duid_is_set(const sd_dhcp_duid *duid);
+
+int sd_dhcp_duid_get(const sd_dhcp_duid *duid, uint16_t *ret_type, const void **ret_data, size_t *ret_size);
+int sd_dhcp_duid_get_raw(const sd_dhcp_duid *duid, const void **ret_data, size_t *ret_size);
+
+int sd_dhcp_duid_set(
+ sd_dhcp_duid *duid,
+ uint16_t duid_type,
+ const void *data,
+ size_t data_size);
+int sd_dhcp_duid_set_raw(
+ sd_dhcp_duid *duid,
+ const void *data,
+ size_t data_size);
+int sd_dhcp_duid_set_llt(
+ sd_dhcp_duid *duid,
+ const void *hw_addr,
+ size_t hw_addr_size,
+ uint16_t arp_type,
+ uint64_t usec);
+int sd_dhcp_duid_set_ll(
+ sd_dhcp_duid *duid,
+ const void *hw_addr,
+ size_t hw_addr_size,
+ uint16_t arp_type);
+int sd_dhcp_duid_set_en(sd_dhcp_duid *duid);
+int sd_dhcp_duid_set_uuid(sd_dhcp_duid *duid);
+
+int sd_dhcp_duid_to_string(const sd_dhcp_duid *duid, char **ret);
+
+_SD_END_DECLARATIONS;
+
+#endif
diff --git a/src/libnm-systemd-core/src/systemd/sd-dhcp6-client.h b/src/libnm-systemd-core/src/systemd/sd-dhcp6-client.h
index a9fa78569d..d551b4dd90 100644
--- a/src/libnm-systemd-core/src/systemd/sd-dhcp6-client.h
+++ b/src/libnm-systemd-core/src/systemd/sd-dhcp6-client.h
@@ -24,6 +24,7 @@
#include <sys/types.h>
#include "sd-device.h"
+#include "sd-dhcp-duid.h"
#include "sd-dhcp6-lease.h"
#include "sd-dhcp6-option.h"
#include "sd-event.h"
@@ -40,154 +41,6 @@ enum {
SD_DHCP6_CLIENT_EVENT_INFORMATION_REQUEST = 13
};
-/* https://www.iana.org/assignments/dhcpv6-parameters/dhcpv6-parameters.xhtml#dhcpv6-parameters-2 */
-enum {
- SD_DHCP6_OPTION_CLIENTID = 1, /* RFC 8415 */
- SD_DHCP6_OPTION_SERVERID = 2, /* RFC 8415 */
- SD_DHCP6_OPTION_IA_NA = 3, /* RFC 8415 */
- SD_DHCP6_OPTION_IA_TA = 4, /* RFC 8415 */
- SD_DHCP6_OPTION_IAADDR = 5, /* RFC 8415 */
- SD_DHCP6_OPTION_ORO = 6, /* RFC 8415 */
- SD_DHCP6_OPTION_PREFERENCE = 7, /* RFC 8415 */
- SD_DHCP6_OPTION_ELAPSED_TIME = 8, /* RFC 8415 */
- SD_DHCP6_OPTION_RELAY_MSG = 9, /* RFC 8415 */
- /* option code 10 is unassigned */
- SD_DHCP6_OPTION_AUTH = 11, /* RFC 8415 */
- SD_DHCP6_OPTION_UNICAST = 12, /* RFC 8415 */
- SD_DHCP6_OPTION_STATUS_CODE = 13, /* RFC 8415 */
- SD_DHCP6_OPTION_RAPID_COMMIT = 14, /* RFC 8415 */
- SD_DHCP6_OPTION_USER_CLASS = 15, /* RFC 8415 */
- SD_DHCP6_OPTION_VENDOR_CLASS = 16, /* RFC 8415 */
- SD_DHCP6_OPTION_VENDOR_OPTS = 17, /* RFC 8415 */
- SD_DHCP6_OPTION_INTERFACE_ID = 18, /* RFC 8415 */
- SD_DHCP6_OPTION_RECONF_MSG = 19, /* RFC 8415 */
- SD_DHCP6_OPTION_RECONF_ACCEPT = 20, /* RFC 8415 */
- SD_DHCP6_OPTION_SIP_SERVER_DOMAIN_NAME = 21, /* RFC 3319 */
- SD_DHCP6_OPTION_SIP_SERVER_ADDRESS = 22, /* RFC 3319 */
- SD_DHCP6_OPTION_DNS_SERVER = 23, /* RFC 3646 */
- SD_DHCP6_OPTION_DOMAIN = 24, /* RFC 3646 */
- SD_DHCP6_OPTION_IA_PD = 25, /* RFC 3633, RFC 8415 */
- SD_DHCP6_OPTION_IA_PD_PREFIX = 26, /* RFC 3633, RFC 8415 */
- SD_DHCP6_OPTION_NIS_SERVER = 27, /* RFC 3898 */
- SD_DHCP6_OPTION_NISP_SERVER = 28, /* RFC 3898 */
- SD_DHCP6_OPTION_NIS_DOMAIN_NAME = 29, /* RFC 3898 */
- SD_DHCP6_OPTION_NISP_DOMAIN_NAME = 30, /* RFC 3898 */
- SD_DHCP6_OPTION_SNTP_SERVER = 31, /* RFC 4075, deprecated */
- SD_DHCP6_OPTION_INFORMATION_REFRESH_TIME = 32, /* RFC 4242, 8415, sec. 21.23 */
- SD_DHCP6_OPTION_BCMCS_SERVER_D = 33, /* RFC 4280 */
- SD_DHCP6_OPTION_BCMCS_SERVER_A = 34, /* RFC 4280 */
- /* option code 35 is unassigned */
- SD_DHCP6_OPTION_GEOCONF_CIVIC = 36, /* RFC 4776 */
- SD_DHCP6_OPTION_REMOTE_ID = 37, /* RFC 4649 */
- SD_DHCP6_OPTION_SUBSCRIBER_ID = 38, /* RFC 4580 */
- SD_DHCP6_OPTION_CLIENT_FQDN = 39, /* RFC 4704 */
- SD_DHCP6_OPTION_PANA_AGENT = 40, /* RFC 5192 */
- SD_DHCP6_OPTION_POSIX_TIMEZONE = 41, /* RFC 4833 */
- SD_DHCP6_OPTION_TZDB_TIMEZONE = 42, /* RFC 4833 */
- SD_DHCP6_OPTION_ERO = 43, /* RFC 4994 */
- SD_DHCP6_OPTION_LQ_QUERY = 44, /* RFC 5007 */
- SD_DHCP6_OPTION_CLIENT_DATA = 45, /* RFC 5007 */
- SD_DHCP6_OPTION_CLT_TIME = 46, /* RFC 5007 */
- SD_DHCP6_OPTION_LQ_RELAY_DATA = 47, /* RFC 5007 */
- SD_DHCP6_OPTION_LQ_CLIENT_LINK = 48, /* RFC 5007 */
- SD_DHCP6_OPTION_MIP6_HNIDF = 49, /* RFC 6610 */
- SD_DHCP6_OPTION_MIP6_VDINF = 50, /* RFC 6610 */
- SD_DHCP6_OPTION_V6_LOST = 51, /* RFC 5223 */
- SD_DHCP6_OPTION_CAPWAP_AC_V6 = 52, /* RFC 5417 */
- SD_DHCP6_OPTION_RELAY_ID = 53, /* RFC 5460 */
- SD_DHCP6_OPTION_IPV6_ADDRESS_MOS = 54, /* RFC 5678 */
- SD_DHCP6_OPTION_IPV6_FQDN_MOS = 55, /* RFC 5678 */
- SD_DHCP6_OPTION_NTP_SERVER = 56, /* RFC 5908 */
- SD_DHCP6_OPTION_V6_ACCESS_DOMAIN = 57, /* RFC 5986 */
- SD_DHCP6_OPTION_SIP_UA_CS_LIST = 58, /* RFC 6011 */
- SD_DHCP6_OPTION_BOOTFILE_URL = 59, /* RFC 5970 */
- SD_DHCP6_OPTION_BOOTFILE_PARAM = 60, /* RFC 5970 */
- SD_DHCP6_OPTION_CLIENT_ARCH_TYPE = 61, /* RFC 5970 */
- SD_DHCP6_OPTION_NII = 62, /* RFC 5970 */
- SD_DHCP6_OPTION_GEOLOCATION = 63, /* RFC 6225 */
- SD_DHCP6_OPTION_AFTR_NAME = 64, /* RFC 6334 */
- SD_DHCP6_OPTION_ERP_LOCAL_DOMAIN_NAME = 65, /* RFC 6440 */
- SD_DHCP6_OPTION_RSOO = 66, /* RFC 6422 */
- SD_DHCP6_OPTION_PD_EXCLUDE = 67, /* RFC 6603 */
- SD_DHCP6_OPTION_VSS = 68, /* RFC 6607 */
- SD_DHCP6_OPTION_MIP6_IDINF = 69, /* RFC 6610 */
- SD_DHCP6_OPTION_MIP6_UDINF = 70, /* RFC 6610 */
- SD_DHCP6_OPTION_MIP6_HNP = 71, /* RFC 6610 */
- SD_DHCP6_OPTION_MIP6_HAA = 72, /* RFC 6610 */
- SD_DHCP6_OPTION_MIP6_HAF = 73, /* RFC 6610 */
- SD_DHCP6_OPTION_RDNSS_SELECTION = 74, /* RFC 6731 */
- SD_DHCP6_OPTION_KRB_PRINCIPAL_NAME = 75, /* RFC 6784 */
- SD_DHCP6_OPTION_KRB_REALM_NAME = 76, /* RFC 6784 */
- SD_DHCP6_OPTION_KRB_DEFAULT_REALM_NAME = 77, /* RFC 6784 */
- SD_DHCP6_OPTION_KRB_KDC = 78, /* RFC 6784 */
- SD_DHCP6_OPTION_CLIENT_LINKLAYER_ADDR = 79, /* RFC 6939 */
- SD_DHCP6_OPTION_LINK_ADDRESS = 80, /* RFC 6977 */
- SD_DHCP6_OPTION_RADIUS = 81, /* RFC 7037 */
- SD_DHCP6_OPTION_SOL_MAX_RT = 82, /* RFC 7083, RFC 8415 */
- SD_DHCP6_OPTION_INF_MAX_RT = 83, /* RFC 7083, RFC 8415 */
- SD_DHCP6_OPTION_ADDRSEL = 84, /* RFC 7078 */
- SD_DHCP6_OPTION_ADDRSEL_TABLE = 85, /* RFC 7078 */
- SD_DHCP6_OPTION_V6_PCP_SERVER = 86, /* RFC 7291 */
- SD_DHCP6_OPTION_DHCPV4_MSG = 87, /* RFC 7341 */
- SD_DHCP6_OPTION_DHCP4_O_DHCP6_SERVER = 88, /* RFC 7341 */
- SD_DHCP6_OPTION_S46_RULE = 89, /* RFC 7598 */
- SD_DHCP6_OPTION_S46_BR = 90, /* RFC 7598, RFC 8539 */
- SD_DHCP6_OPTION_S46_DMR = 91, /* RFC 7598 */
- SD_DHCP6_OPTION_S46_V4V6BIND = 92, /* RFC 7598 */
- SD_DHCP6_OPTION_S46_PORTPARAMS = 93, /* RFC 7598 */
- SD_DHCP6_OPTION_S46_CONT_MAPE = 94, /* RFC 7598 */
- SD_DHCP6_OPTION_S46_CONT_MAPT = 95, /* RFC 7598 */
- SD_DHCP6_OPTION_S46_CONT_LW = 96, /* RFC 7598 */
- SD_DHCP6_OPTION_4RD = 97, /* RFC 7600 */
- SD_DHCP6_OPTION_4RD_MAP_RULE = 98, /* RFC 7600 */
- SD_DHCP6_OPTION_4RD_NON_MAP_RULE = 99, /* RFC 7600 */
- SD_DHCP6_OPTION_LQ_BASE_TIME = 100, /* RFC 7653 */
- SD_DHCP6_OPTION_LQ_START_TIME = 101, /* RFC 7653 */
- SD_DHCP6_OPTION_LQ_END_TIME = 102, /* RFC 7653 */
- SD_DHCP6_OPTION_CAPTIVE_PORTAL = 103, /* RFC 8910 */
- SD_DHCP6_OPTION_MPL_PARAMETERS = 104, /* RFC 7774 */
- SD_DHCP6_OPTION_ANI_ATT = 105, /* RFC 7839 */
- SD_DHCP6_OPTION_ANI_NETWORK_NAME = 106, /* RFC 7839 */
- SD_DHCP6_OPTION_ANI_AP_NAME = 107, /* RFC 7839 */
- SD_DHCP6_OPTION_ANI_AP_BSSID = 108, /* RFC 7839 */
- SD_DHCP6_OPTION_ANI_OPERATOR_ID = 109, /* RFC 7839 */
- SD_DHCP6_OPTION_ANI_OPERATOR_REALM = 110, /* RFC 7839 */
- SD_DHCP6_OPTION_S46_PRIORITY = 111, /* RFC 8026 */
- SD_DHCP6_OPTION_MUD_URL_V6 = 112, /* RFC 8520 */
- SD_DHCP6_OPTION_V6_PREFIX64 = 113, /* RFC 8115 */
- SD_DHCP6_OPTION_F_BINDING_STATUS = 114, /* RFC 8156 */
- SD_DHCP6_OPTION_F_CONNECT_FLAGS = 115, /* RFC 8156 */
- SD_DHCP6_OPTION_F_DNS_REMOVAL_INFO = 116, /* RFC 8156 */
- SD_DHCP6_OPTION_F_DNS_HOST_NAME = 117, /* RFC 8156 */
- SD_DHCP6_OPTION_F_DNS_ZONE_NAME = 118, /* RFC 8156 */
- SD_DHCP6_OPTION_F_DNS_FLAGS = 119, /* RFC 8156 */
- SD_DHCP6_OPTION_F_EXPIRATION_TIME = 120, /* RFC 8156 */
- SD_DHCP6_OPTION_F_MAX_UNACKED_BNDUPD = 121, /* RFC 8156 */
- SD_DHCP6_OPTION_F_MCLT = 122, /* RFC 8156 */
- SD_DHCP6_OPTION_F_PARTNER_LIFETIME = 123, /* RFC 8156 */
- SD_DHCP6_OPTION_F_PARTNER_LIFETIME_SENT = 124, /* RFC 8156 */
- SD_DHCP6_OPTION_F_PARTNER_DOWN_TIME = 125, /* RFC 8156 */
- SD_DHCP6_OPTION_F_PARTNER_RAW_CLT_TIME = 126, /* RFC 8156 */
- SD_DHCP6_OPTION_F_PROTOCOL_VERSION = 127, /* RFC 8156 */
- SD_DHCP6_OPTION_F_KEEPALIVE_TIME = 128, /* RFC 8156 */
- SD_DHCP6_OPTION_F_RECONFIGURE_DATA = 129, /* RFC 8156 */
- SD_DHCP6_OPTION_F_RELATIONSHIP_NAME = 130, /* RFC 8156 */
- SD_DHCP6_OPTION_F_SERVER_FLAGS = 131, /* RFC 8156 */
- SD_DHCP6_OPTION_F_SERVER_STATE = 132, /* RFC 8156 */
- SD_DHCP6_OPTION_F_START_TIME_OF_STATE = 133, /* RFC 8156 */
- SD_DHCP6_OPTION_F_STATE_EXPIRATION_TIME = 134, /* RFC 8156 */
- SD_DHCP6_OPTION_RELAY_PORT = 135, /* RFC 8357 */
- SD_DHCP6_OPTION_V6_SZTP_REDIRECT = 136, /* RFC 8572 */
- SD_DHCP6_OPTION_S46_BIND_IPV6_PREFIX = 137, /* RFC 8539 */
- SD_DHCP6_OPTION_IA_LL = 138, /* RFC 8947 */
- SD_DHCP6_OPTION_LLADDR = 139, /* RFC 8947 */
- SD_DHCP6_OPTION_SLAP_QUAD = 140, /* RFC 8948 */
- SD_DHCP6_OPTION_V6_DOTS_RI = 141, /* RFC 8973 */
- SD_DHCP6_OPTION_V6_DOTS_ADDRESS = 142, /* RFC 8973 */
- SD_DHCP6_OPTION_IPV6_ADDRESS_ANDSF = 143 /* RFC 6153 */
- /* option codes 144-65535 are unassigned */
-};
-
typedef struct sd_dhcp6_client sd_dhcp6_client;
typedef void (*sd_dhcp6_client_callback_t)(sd_dhcp6_client *client, int event, void *userdata);
@@ -211,23 +64,20 @@ int sd_dhcp6_client_set_mac(
const uint8_t *addr,
size_t addr_len,
uint16_t arp_type);
-int sd_dhcp6_client_set_duid(
- sd_dhcp6_client *client,
- uint16_t duid_type,
- const void *duid,
- size_t duid_len);
-int sd_dhcp6_client_set_duid_llt(
- sd_dhcp6_client *client,
- uint64_t llt_time);
+int sd_dhcp6_client_set_duid_llt(sd_dhcp6_client *client, uint64_t llt_time);
+int sd_dhcp6_client_set_duid_ll(sd_dhcp6_client *client);
+int sd_dhcp6_client_set_duid_en(sd_dhcp6_client *client);
+int sd_dhcp6_client_set_duid_uuid(sd_dhcp6_client *client);
+int sd_dhcp6_client_set_duid_raw(sd_dhcp6_client *client, uint16_t duid_type, const uint8_t *duid, size_t duid_len);
+int sd_dhcp6_client_set_duid(sd_dhcp6_client *client, const sd_dhcp_duid *duid);
+int sd_dhcp6_client_get_duid(sd_dhcp6_client *client, const sd_dhcp_duid **ret);
+int sd_dhcp6_client_get_duid_as_string(sd_dhcp6_client *client, char **ret);
int sd_dhcp6_client_set_iaid(
sd_dhcp6_client *client,
uint32_t iaid);
int sd_dhcp6_client_get_iaid(
sd_dhcp6_client *client,
uint32_t *iaid);
-int sd_dhcp6_client_duid_as_string(
- sd_dhcp6_client *client,
- char **duid);
int sd_dhcp6_client_set_fqdn(
sd_dhcp6_client *client,
const char *fqdn);
diff --git a/src/libnm-systemd-core/src/systemd/sd-dhcp6-lease.h b/src/libnm-systemd-core/src/systemd/sd-dhcp6-lease.h
index 716f6fc17c..e18d57817f 100644
--- a/src/libnm-systemd-core/src/systemd/sd-dhcp6-lease.h
+++ b/src/libnm-systemd-core/src/systemd/sd-dhcp6-lease.h
@@ -23,6 +23,8 @@
#include <netinet/in.h>
#include <sys/types.h>
+#include "sd-dhcp6-option.h"
+
#include "_sd-common.h"
_SD_BEGIN_DECLARATIONS;
@@ -30,24 +32,54 @@ _SD_BEGIN_DECLARATIONS;
typedef struct sd_dhcp6_lease sd_dhcp6_lease;
int sd_dhcp6_lease_get_timestamp(sd_dhcp6_lease *lease, clockid_t clock, uint64_t *ret);
+int sd_dhcp6_lease_get_t1(sd_dhcp6_lease *lease, uint64_t *ret);
+int sd_dhcp6_lease_get_t1_timestamp(sd_dhcp6_lease *lease, clockid_t clock, uint64_t *ret);
+int sd_dhcp6_lease_get_t2(sd_dhcp6_lease *lease, uint64_t *ret);
+int sd_dhcp6_lease_get_t2_timestamp(sd_dhcp6_lease *lease, clockid_t clock, uint64_t *ret);
+int sd_dhcp6_lease_get_valid_lifetime(sd_dhcp6_lease *lease, uint64_t *ret);
+int sd_dhcp6_lease_get_valid_lifetime_timestamp(sd_dhcp6_lease *lease, clockid_t clock, uint64_t *ret);
int sd_dhcp6_lease_get_server_address(sd_dhcp6_lease *lease, struct in6_addr *ret);
-void sd_dhcp6_lease_reset_address_iter(sd_dhcp6_lease *lease);
-int sd_dhcp6_lease_get_address(sd_dhcp6_lease *lease,
- struct in6_addr *addr,
- uint32_t *lifetime_preferred,
- uint32_t *lifetime_valid);
-void sd_dhcp6_lease_reset_pd_prefix_iter(sd_dhcp6_lease *lease);
-int sd_dhcp6_lease_get_pd(sd_dhcp6_lease *lease, struct in6_addr *prefix,
- uint8_t *prefix_len,
- uint32_t *lifetime_preferred,
- uint32_t *lifetime_valid);
+int sd_dhcp6_lease_address_iterator_reset(sd_dhcp6_lease *lease);
+int sd_dhcp6_lease_address_iterator_next(sd_dhcp6_lease *lease);
+int sd_dhcp6_lease_get_address(
+ sd_dhcp6_lease *lease,
+ struct in6_addr *ret);
+int sd_dhcp6_lease_get_address_lifetime(
+ sd_dhcp6_lease *lease,
+ uint64_t *ret_lifetime_preferred,
+ uint64_t *ret_lifetime_valid);
+int sd_dhcp6_lease_get_address_lifetime_timestamp(
+ sd_dhcp6_lease *lease,
+ clockid_t clock,
+ uint64_t *ret_lifetime_preferred,
+ uint64_t *ret_lifetime_valid);
+int sd_dhcp6_lease_has_address(sd_dhcp6_lease *lease);
+
+int sd_dhcp6_lease_pd_iterator_reset(sd_dhcp6_lease *lease);
+int sd_dhcp6_lease_pd_iterator_next(sd_dhcp6_lease *lease);
+int sd_dhcp6_lease_get_pd_prefix(
+ sd_dhcp6_lease *lease,
+ struct in6_addr *ret_prefix,
+ uint8_t *ret_prefix_length);
+int sd_dhcp6_lease_get_pd_lifetime(
+ sd_dhcp6_lease *lease,
+ uint64_t *ret_lifetime_preferred,
+ uint64_t *ret_lifetime_valid);
+int sd_dhcp6_lease_get_pd_lifetime_timestamp(
+ sd_dhcp6_lease *lease,
+ clockid_t clock,
+ uint64_t *ret_lifetime_preferred,
+ uint64_t *ret_lifetime_valid);
+int sd_dhcp6_lease_has_pd_prefix(sd_dhcp6_lease *lease);
int sd_dhcp6_lease_get_dns(sd_dhcp6_lease *lease, const struct in6_addr **ret);
int sd_dhcp6_lease_get_domains(sd_dhcp6_lease *lease, char ***ret);
int sd_dhcp6_lease_get_ntp_addrs(sd_dhcp6_lease *lease, const struct in6_addr **ret);
int sd_dhcp6_lease_get_ntp_fqdn(sd_dhcp6_lease *lease, char ***ret);
int sd_dhcp6_lease_get_fqdn(sd_dhcp6_lease *lease, const char **ret);
+int sd_dhcp6_lease_get_captive_portal(sd_dhcp6_lease *lease, const char **ret);
+int sd_dhcp6_lease_get_vendor_options(sd_dhcp6_lease *lease, sd_dhcp6_option ***ret);
sd_dhcp6_lease *sd_dhcp6_lease_ref(sd_dhcp6_lease *lease);
sd_dhcp6_lease *sd_dhcp6_lease_unref(sd_dhcp6_lease *lease);
diff --git a/src/libnm-systemd-core/src/systemd/sd-dhcp6-option.h b/src/libnm-systemd-core/src/systemd/sd-dhcp6-option.h
index b4b4671e4a..320124266a 100644
--- a/src/libnm-systemd-core/src/systemd/sd-dhcp6-option.h
+++ b/src/libnm-systemd-core/src/systemd/sd-dhcp6-option.h
@@ -20,6 +20,8 @@
#include <inttypes.h>
#include <sys/types.h>
+#include "sd-dhcp6-protocol.h"
+
#include "_sd-common.h"
_SD_BEGIN_DECLARATIONS;
diff --git a/src/libnm-systemd-core/src/systemd/sd-dhcp6-protocol.h b/src/libnm-systemd-core/src/systemd/sd-dhcp6-protocol.h
new file mode 100644
index 0000000000..78c80f7c7e
--- /dev/null
+++ b/src/libnm-systemd-core/src/systemd/sd-dhcp6-protocol.h
@@ -0,0 +1,174 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+#ifndef foosddhcp6protocolhfoo
+#define foosddhcp6protocolhfoo
+
+/***
+ systemd is free software; you can redistribute it and/or modify it
+ under the terms of the GNU Lesser General Public License as published by
+ the Free Software Foundation; either version 2.1 of the License, or
+ (at your option) any later version.
+
+ systemd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with systemd; If not, see <https://www.gnu.org/licenses/>.
+***/
+
+#include "_sd-common.h"
+
+_SD_BEGIN_DECLARATIONS;
+
+/* https://www.iana.org/assignments/dhcpv6-parameters/dhcpv6-parameters.xhtml#dhcpv6-parameters-2 */
+enum {
+ SD_DHCP6_OPTION_CLIENTID = 1, /* RFC 8415 */
+ SD_DHCP6_OPTION_SERVERID = 2, /* RFC 8415 */
+ SD_DHCP6_OPTION_IA_NA = 3, /* RFC 8415 */
+ SD_DHCP6_OPTION_IA_TA = 4, /* RFC 8415 */
+ SD_DHCP6_OPTION_IAADDR = 5, /* RFC 8415 */
+ SD_DHCP6_OPTION_ORO = 6, /* RFC 8415 */
+ SD_DHCP6_OPTION_PREFERENCE = 7, /* RFC 8415 */
+ SD_DHCP6_OPTION_ELAPSED_TIME = 8, /* RFC 8415 */
+ SD_DHCP6_OPTION_RELAY_MSG = 9, /* RFC 8415 */
+ /* option code 10 is unassigned */
+ SD_DHCP6_OPTION_AUTH = 11, /* RFC 8415 */
+ SD_DHCP6_OPTION_UNICAST = 12, /* RFC 8415 */
+ SD_DHCP6_OPTION_STATUS_CODE = 13, /* RFC 8415 */
+ SD_DHCP6_OPTION_RAPID_COMMIT = 14, /* RFC 8415 */
+ SD_DHCP6_OPTION_USER_CLASS = 15, /* RFC 8415 */
+ SD_DHCP6_OPTION_VENDOR_CLASS = 16, /* RFC 8415 */
+ SD_DHCP6_OPTION_VENDOR_OPTS = 17, /* RFC 8415 */
+ SD_DHCP6_OPTION_INTERFACE_ID = 18, /* RFC 8415 */
+ SD_DHCP6_OPTION_RECONF_MSG = 19, /* RFC 8415 */
+ SD_DHCP6_OPTION_RECONF_ACCEPT = 20, /* RFC 8415 */
+ SD_DHCP6_OPTION_SIP_SERVER_DOMAIN_NAME = 21, /* RFC 3319 */
+ SD_DHCP6_OPTION_SIP_SERVER_ADDRESS = 22, /* RFC 3319 */
+ SD_DHCP6_OPTION_DNS_SERVER = 23, /* RFC 3646 */
+ SD_DHCP6_OPTION_DOMAIN = 24, /* RFC 3646 */
+ SD_DHCP6_OPTION_IA_PD = 25, /* RFC 3633, RFC 8415 */
+ SD_DHCP6_OPTION_IA_PD_PREFIX = 26, /* RFC 3633, RFC 8415 */
+ SD_DHCP6_OPTION_NIS_SERVER = 27, /* RFC 3898 */
+ SD_DHCP6_OPTION_NISP_SERVER = 28, /* RFC 3898 */
+ SD_DHCP6_OPTION_NIS_DOMAIN_NAME = 29, /* RFC 3898 */
+ SD_DHCP6_OPTION_NISP_DOMAIN_NAME = 30, /* RFC 3898 */
+ SD_DHCP6_OPTION_SNTP_SERVER = 31, /* RFC 4075, deprecated */
+ SD_DHCP6_OPTION_INFORMATION_REFRESH_TIME = 32, /* RFC 4242, 8415, sec. 21.23 */
+ SD_DHCP6_OPTION_BCMCS_SERVER_D = 33, /* RFC 4280 */
+ SD_DHCP6_OPTION_BCMCS_SERVER_A = 34, /* RFC 4280 */
+ /* option code 35 is unassigned */
+ SD_DHCP6_OPTION_GEOCONF_CIVIC = 36, /* RFC 4776 */
+ SD_DHCP6_OPTION_REMOTE_ID = 37, /* RFC 4649 */
+ SD_DHCP6_OPTION_SUBSCRIBER_ID = 38, /* RFC 4580 */
+ SD_DHCP6_OPTION_CLIENT_FQDN = 39, /* RFC 4704 */
+ SD_DHCP6_OPTION_PANA_AGENT = 40, /* RFC 5192 */
+ SD_DHCP6_OPTION_POSIX_TIMEZONE = 41, /* RFC 4833 */
+ SD_DHCP6_OPTION_TZDB_TIMEZONE = 42, /* RFC 4833 */
+ SD_DHCP6_OPTION_ERO = 43, /* RFC 4994 */
+ SD_DHCP6_OPTION_LQ_QUERY = 44, /* RFC 5007 */
+ SD_DHCP6_OPTION_CLIENT_DATA = 45, /* RFC 5007 */
+ SD_DHCP6_OPTION_CLT_TIME = 46, /* RFC 5007 */
+ SD_DHCP6_OPTION_LQ_RELAY_DATA = 47, /* RFC 5007 */
+ SD_DHCP6_OPTION_LQ_CLIENT_LINK = 48, /* RFC 5007 */
+ SD_DHCP6_OPTION_MIP6_HNIDF = 49, /* RFC 6610 */
+ SD_DHCP6_OPTION_MIP6_VDINF = 50, /* RFC 6610 */
+ SD_DHCP6_OPTION_V6_LOST = 51, /* RFC 5223 */
+ SD_DHCP6_OPTION_CAPWAP_AC_V6 = 52, /* RFC 5417 */
+ SD_DHCP6_OPTION_RELAY_ID = 53, /* RFC 5460 */
+ SD_DHCP6_OPTION_IPV6_ADDRESS_MOS = 54, /* RFC 5678 */
+ SD_DHCP6_OPTION_IPV6_FQDN_MOS = 55, /* RFC 5678 */
+ SD_DHCP6_OPTION_NTP_SERVER = 56, /* RFC 5908 */
+ SD_DHCP6_OPTION_V6_ACCESS_DOMAIN = 57, /* RFC 5986 */
+ SD_DHCP6_OPTION_SIP_UA_CS_LIST = 58, /* RFC 6011 */
+ SD_DHCP6_OPTION_BOOTFILE_URL = 59, /* RFC 5970 */
+ SD_DHCP6_OPTION_BOOTFILE_PARAM = 60, /* RFC 5970 */
+ SD_DHCP6_OPTION_CLIENT_ARCH_TYPE = 61, /* RFC 5970 */
+ SD_DHCP6_OPTION_NII = 62, /* RFC 5970 */
+ SD_DHCP6_OPTION_GEOLOCATION = 63, /* RFC 6225 */
+ SD_DHCP6_OPTION_AFTR_NAME = 64, /* RFC 6334 */
+ SD_DHCP6_OPTION_ERP_LOCAL_DOMAIN_NAME = 65, /* RFC 6440 */
+ SD_DHCP6_OPTION_RSOO = 66, /* RFC 6422 */
+ SD_DHCP6_OPTION_PD_EXCLUDE = 67, /* RFC 6603 */
+ SD_DHCP6_OPTION_VSS = 68, /* RFC 6607 */
+ SD_DHCP6_OPTION_MIP6_IDINF = 69, /* RFC 6610 */
+ SD_DHCP6_OPTION_MIP6_UDINF = 70, /* RFC 6610 */
+ SD_DHCP6_OPTION_MIP6_HNP = 71, /* RFC 6610 */
+ SD_DHCP6_OPTION_MIP6_HAA = 72, /* RFC 6610 */
+ SD_DHCP6_OPTION_MIP6_HAF = 73, /* RFC 6610 */
+ SD_DHCP6_OPTION_RDNSS_SELECTION = 74, /* RFC 6731 */
+ SD_DHCP6_OPTION_KRB_PRINCIPAL_NAME = 75, /* RFC 6784 */
+ SD_DHCP6_OPTION_KRB_REALM_NAME = 76, /* RFC 6784 */
+ SD_DHCP6_OPTION_KRB_DEFAULT_REALM_NAME = 77, /* RFC 6784 */
+ SD_DHCP6_OPTION_KRB_KDC = 78, /* RFC 6784 */
+ SD_DHCP6_OPTION_CLIENT_LINKLAYER_ADDR = 79, /* RFC 6939 */
+ SD_DHCP6_OPTION_LINK_ADDRESS = 80, /* RFC 6977 */
+ SD_DHCP6_OPTION_RADIUS = 81, /* RFC 7037 */
+ SD_DHCP6_OPTION_SOL_MAX_RT = 82, /* RFC 7083, RFC 8415 */
+ SD_DHCP6_OPTION_INF_MAX_RT = 83, /* RFC 7083, RFC 8415 */
+ SD_DHCP6_OPTION_ADDRSEL = 84, /* RFC 7078 */
+ SD_DHCP6_OPTION_ADDRSEL_TABLE = 85, /* RFC 7078 */
+ SD_DHCP6_OPTION_V6_PCP_SERVER = 86, /* RFC 7291 */
+ SD_DHCP6_OPTION_DHCPV4_MSG = 87, /* RFC 7341 */
+ SD_DHCP6_OPTION_DHCP4_O_DHCP6_SERVER = 88, /* RFC 7341 */
+ SD_DHCP6_OPTION_S46_RULE = 89, /* RFC 7598 */
+ SD_DHCP6_OPTION_S46_BR = 90, /* RFC 7598, RFC 8539 */
+ SD_DHCP6_OPTION_S46_DMR = 91, /* RFC 7598 */
+ SD_DHCP6_OPTION_S46_V4V6BIND = 92, /* RFC 7598 */
+ SD_DHCP6_OPTION_S46_PORTPARAMS = 93, /* RFC 7598 */
+ SD_DHCP6_OPTION_S46_CONT_MAPE = 94, /* RFC 7598 */
+ SD_DHCP6_OPTION_S46_CONT_MAPT = 95, /* RFC 7598 */
+ SD_DHCP6_OPTION_S46_CONT_LW = 96, /* RFC 7598 */
+ SD_DHCP6_OPTION_4RD = 97, /* RFC 7600 */
+ SD_DHCP6_OPTION_4RD_MAP_RULE = 98, /* RFC 7600 */
+ SD_DHCP6_OPTION_4RD_NON_MAP_RULE = 99, /* RFC 7600 */
+ SD_DHCP6_OPTION_LQ_BASE_TIME = 100, /* RFC 7653 */
+ SD_DHCP6_OPTION_LQ_START_TIME = 101, /* RFC 7653 */
+ SD_DHCP6_OPTION_LQ_END_TIME = 102, /* RFC 7653 */
+ SD_DHCP6_OPTION_CAPTIVE_PORTAL = 103, /* RFC 8910 */
+ SD_DHCP6_OPTION_MPL_PARAMETERS = 104, /* RFC 7774 */
+ SD_DHCP6_OPTION_ANI_ATT = 105, /* RFC 7839 */
+ SD_DHCP6_OPTION_ANI_NETWORK_NAME = 106, /* RFC 7839 */
+ SD_DHCP6_OPTION_ANI_AP_NAME = 107, /* RFC 7839 */
+ SD_DHCP6_OPTION_ANI_AP_BSSID = 108, /* RFC 7839 */
+ SD_DHCP6_OPTION_ANI_OPERATOR_ID = 109, /* RFC 7839 */
+ SD_DHCP6_OPTION_ANI_OPERATOR_REALM = 110, /* RFC 7839 */
+ SD_DHCP6_OPTION_S46_PRIORITY = 111, /* RFC 8026 */
+ SD_DHCP6_OPTION_MUD_URL_V6 = 112, /* RFC 8520 */
+ SD_DHCP6_OPTION_V6_PREFIX64 = 113, /* RFC 8115 */
+ SD_DHCP6_OPTION_F_BINDING_STATUS = 114, /* RFC 8156 */
+ SD_DHCP6_OPTION_F_CONNECT_FLAGS = 115, /* RFC 8156 */
+ SD_DHCP6_OPTION_F_DNS_REMOVAL_INFO = 116, /* RFC 8156 */
+ SD_DHCP6_OPTION_F_DNS_HOST_NAME = 117, /* RFC 8156 */
+ SD_DHCP6_OPTION_F_DNS_ZONE_NAME = 118, /* RFC 8156 */
+ SD_DHCP6_OPTION_F_DNS_FLAGS = 119, /* RFC 8156 */
+ SD_DHCP6_OPTION_F_EXPIRATION_TIME = 120, /* RFC 8156 */
+ SD_DHCP6_OPTION_F_MAX_UNACKED_BNDUPD = 121, /* RFC 8156 */
+ SD_DHCP6_OPTION_F_MCLT = 122, /* RFC 8156 */
+ SD_DHCP6_OPTION_F_PARTNER_LIFETIME = 123, /* RFC 8156 */
+ SD_DHCP6_OPTION_F_PARTNER_LIFETIME_SENT = 124, /* RFC 8156 */
+ SD_DHCP6_OPTION_F_PARTNER_DOWN_TIME = 125, /* RFC 8156 */
+ SD_DHCP6_OPTION_F_PARTNER_RAW_CLT_TIME = 126, /* RFC 8156 */
+ SD_DHCP6_OPTION_F_PROTOCOL_VERSION = 127, /* RFC 8156 */
+ SD_DHCP6_OPTION_F_KEEPALIVE_TIME = 128, /* RFC 8156 */
+ SD_DHCP6_OPTION_F_RECONFIGURE_DATA = 129, /* RFC 8156 */
+ SD_DHCP6_OPTION_F_RELATIONSHIP_NAME = 130, /* RFC 8156 */
+ SD_DHCP6_OPTION_F_SERVER_FLAGS = 131, /* RFC 8156 */
+ SD_DHCP6_OPTION_F_SERVER_STATE = 132, /* RFC 8156 */
+ SD_DHCP6_OPTION_F_START_TIME_OF_STATE = 133, /* RFC 8156 */
+ SD_DHCP6_OPTION_F_STATE_EXPIRATION_TIME = 134, /* RFC 8156 */
+ SD_DHCP6_OPTION_RELAY_PORT = 135, /* RFC 8357 */
+ SD_DHCP6_OPTION_V6_SZTP_REDIRECT = 136, /* RFC 8572 */
+ SD_DHCP6_OPTION_S46_BIND_IPV6_PREFIX = 137, /* RFC 8539 */
+ SD_DHCP6_OPTION_IA_LL = 138, /* RFC 8947 */
+ SD_DHCP6_OPTION_LLADDR = 139, /* RFC 8947 */
+ SD_DHCP6_OPTION_SLAP_QUAD = 140, /* RFC 8948 */
+ SD_DHCP6_OPTION_V6_DOTS_RI = 141, /* RFC 8973 */
+ SD_DHCP6_OPTION_V6_DOTS_ADDRESS = 142, /* RFC 8973 */
+ SD_DHCP6_OPTION_IPV6_ADDRESS_ANDSF = 143 /* RFC 6153 */
+ /* option codes 144-65535 are unassigned */
+};
+
+_SD_END_DECLARATIONS;
+
+#endif
diff --git a/src/libnm-systemd-core/src/systemd/sd-id128.h b/src/libnm-systemd-core/src/systemd/sd-id128.h
index 3303c374ce..a984a9d85e 100644
--- a/src/libnm-systemd-core/src/systemd/sd-id128.h
+++ b/src/libnm-systemd-core/src/systemd/sd-id128.h
@@ -50,6 +50,7 @@ int sd_id128_get_machine(sd_id128_t *ret);
int sd_id128_get_boot(sd_id128_t *ret);
int sd_id128_get_invocation(sd_id128_t *ret);
+int sd_id128_get_app_specific(sd_id128_t base, sd_id128_t app_id, sd_id128_t *ret);
int sd_id128_get_machine_app_specific(sd_id128_t app_id, sd_id128_t *ret);
int sd_id128_get_boot_app_specific(sd_id128_t app_id, sd_id128_t *ret);
diff --git a/src/libnm-systemd-core/src/systemd/sd-ndisc.h b/src/libnm-systemd-core/src/systemd/sd-ndisc.h
index ee309a4253..a5ccd5f644 100644
--- a/src/libnm-systemd-core/src/systemd/sd-ndisc.h
+++ b/src/libnm-systemd-core/src/systemd/sd-ndisc.h
@@ -42,7 +42,8 @@ enum {
SD_NDISC_OPTION_RDNSS = 25,
SD_NDISC_OPTION_FLAGS_EXTENSION = 26,
SD_NDISC_OPTION_DNSSL = 31,
- SD_NDISC_OPTION_CAPTIVE_PORTAL = 37
+ SD_NDISC_OPTION_CAPTIVE_PORTAL = 37,
+ SD_NDISC_OPTION_PREF64 = 38
};
/* Route preference, RFC 4191, Section 2.1 */
@@ -85,14 +86,17 @@ int sd_ndisc_set_mac(sd_ndisc *nd, const struct ether_addr *mac_addr);
sd_ndisc_router *sd_ndisc_router_ref(sd_ndisc_router *rt);
sd_ndisc_router *sd_ndisc_router_unref(sd_ndisc_router *rt);
-int sd_ndisc_router_get_address(sd_ndisc_router *rt, struct in6_addr *ret_addr);
+int sd_ndisc_router_get_address(sd_ndisc_router *rt, struct in6_addr *ret);
int sd_ndisc_router_get_timestamp(sd_ndisc_router *rt, clockid_t clock, uint64_t *ret);
-int sd_ndisc_router_get_raw(sd_ndisc_router *rt, const void **ret, size_t *size);
+int sd_ndisc_router_get_raw(sd_ndisc_router *rt, const void **ret, size_t *ret_size);
int sd_ndisc_router_get_hop_limit(sd_ndisc_router *rt, uint8_t *ret);
-int sd_ndisc_router_get_flags(sd_ndisc_router *rt, uint64_t *ret_flags);
+int sd_ndisc_router_get_icmp6_ratelimit(sd_ndisc_router *rt, uint64_t *ret);
+int sd_ndisc_router_get_flags(sd_ndisc_router *rt, uint64_t *ret);
int sd_ndisc_router_get_preference(sd_ndisc_router *rt, unsigned *ret);
-int sd_ndisc_router_get_lifetime(sd_ndisc_router *rt, uint16_t *ret_lifetime);
+int sd_ndisc_router_get_lifetime(sd_ndisc_router *rt, uint64_t *ret);
+int sd_ndisc_router_get_lifetime_timestamp(sd_ndisc_router *rt, clockid_t clock, uint64_t *ret);
+int sd_ndisc_router_get_retransmission_time(sd_ndisc_router *rt, uint64_t *ret);
int sd_ndisc_router_get_mtu(sd_ndisc_router *rt, uint32_t *ret);
/* Generic option access */
@@ -100,28 +104,42 @@ int sd_ndisc_router_option_rewind(sd_ndisc_router *rt);
int sd_ndisc_router_option_next(sd_ndisc_router *rt);
int sd_ndisc_router_option_get_type(sd_ndisc_router *rt, uint8_t *ret);
int sd_ndisc_router_option_is_type(sd_ndisc_router *rt, uint8_t type);
-int sd_ndisc_router_option_get_raw(sd_ndisc_router *rt, const void **ret, size_t *size);
+int sd_ndisc_router_option_get_raw(sd_ndisc_router *rt, const void **ret, size_t *ret_size);
/* Specific option access: SD_NDISC_OPTION_PREFIX_INFORMATION */
-int sd_ndisc_router_prefix_get_valid_lifetime(sd_ndisc_router *rt, uint32_t *ret);
-int sd_ndisc_router_prefix_get_preferred_lifetime(sd_ndisc_router *rt, uint32_t *ret);
+int sd_ndisc_router_prefix_get_valid_lifetime(sd_ndisc_router *rt, uint64_t *ret);
+int sd_ndisc_router_prefix_get_valid_lifetime_timestamp(sd_ndisc_router *rt, clockid_t clock, uint64_t *ret);
+int sd_ndisc_router_prefix_get_preferred_lifetime(sd_ndisc_router *rt, uint64_t *ret);
+int sd_ndisc_router_prefix_get_preferred_lifetime_timestamp(sd_ndisc_router *rt, clockid_t clock, uint64_t *ret);
int sd_ndisc_router_prefix_get_flags(sd_ndisc_router *rt, uint8_t *ret);
-int sd_ndisc_router_prefix_get_address(sd_ndisc_router *rt, struct in6_addr *ret_addr);
-int sd_ndisc_router_prefix_get_prefixlen(sd_ndisc_router *rt, unsigned *prefixlen);
+int sd_ndisc_router_prefix_get_address(sd_ndisc_router *rt, struct in6_addr *ret);
+int sd_ndisc_router_prefix_get_prefixlen(sd_ndisc_router *rt, unsigned *ret);
/* Specific option access: SD_NDISC_OPTION_ROUTE_INFORMATION */
-int sd_ndisc_router_route_get_lifetime(sd_ndisc_router *rt, uint32_t *ret);
-int sd_ndisc_router_route_get_address(sd_ndisc_router *rt, struct in6_addr *ret_addr);
-int sd_ndisc_router_route_get_prefixlen(sd_ndisc_router *rt, unsigned *prefixlen);
+int sd_ndisc_router_route_get_lifetime(sd_ndisc_router *rt, uint64_t *ret);
+int sd_ndisc_router_route_get_lifetime_timestamp(sd_ndisc_router *rt, clockid_t clock, uint64_t *ret);
+int sd_ndisc_router_route_get_address(sd_ndisc_router *rt, struct in6_addr *ret);
+int sd_ndisc_router_route_get_prefixlen(sd_ndisc_router *rt, unsigned *ret);
int sd_ndisc_router_route_get_preference(sd_ndisc_router *rt, unsigned *ret);
/* Specific option access: SD_NDISC_OPTION_RDNSS */
int sd_ndisc_router_rdnss_get_addresses(sd_ndisc_router *rt, const struct in6_addr **ret);
-int sd_ndisc_router_rdnss_get_lifetime(sd_ndisc_router *rt, uint32_t *ret);
+int sd_ndisc_router_rdnss_get_lifetime(sd_ndisc_router *rt, uint64_t *ret);
+int sd_ndisc_router_rdnss_get_lifetime_timestamp(sd_ndisc_router *rt, clockid_t clock, uint64_t *ret);
/* Specific option access: SD_NDISC_OPTION_DNSSL */
int sd_ndisc_router_dnssl_get_domains(sd_ndisc_router *rt, char ***ret);
-int sd_ndisc_router_dnssl_get_lifetime(sd_ndisc_router *rt, uint32_t *ret);
+int sd_ndisc_router_dnssl_get_lifetime(sd_ndisc_router *rt, uint64_t *ret);
+int sd_ndisc_router_dnssl_get_lifetime_timestamp(sd_ndisc_router *rt, clockid_t clock, uint64_t *ret);
+
+/* Specific option access: SD_NDISC_OPTION_CAPTIVE_PORTAL */
+int sd_ndisc_router_captive_portal_get_uri(sd_ndisc_router *rt, const char **ret, size_t *ret_size);
+
+/* Specific option access: SD_NDISC_OPTION_PREF64 */
+int sd_ndisc_router_prefix64_get_prefix(sd_ndisc_router *rt, struct in6_addr *ret);
+int sd_ndisc_router_prefix64_get_prefixlen(sd_ndisc_router *rt, unsigned *ret);
+int sd_ndisc_router_prefix64_get_lifetime(sd_ndisc_router *rt, uint64_t *ret);
+int sd_ndisc_router_prefix64_get_lifetime_timestamp(sd_ndisc_router *rt, clockid_t clock, uint64_t *ret);
_SD_DEFINE_POINTER_CLEANUP_FUNC(sd_ndisc, sd_ndisc_unref);
_SD_DEFINE_POINTER_CLEANUP_FUNC(sd_ndisc_router, sd_ndisc_router_unref);
diff --git a/src/libnm-systemd-shared/meson.build b/src/libnm-systemd-shared/meson.build
index 3e5d1db859..3d3e42709f 100644
--- a/src/libnm-systemd-shared/meson.build
+++ b/src/libnm-systemd-shared/meson.build
@@ -5,6 +5,7 @@ libnm_systemd_shared = static_library(
sources: files(
'nm-sd-utils-shared.c',
'src/basic/alloc-util.c',
+ 'src/basic/btrfs.c',
'src/basic/env-file.c',
'src/basic/env-util.c',
'src/basic/escape.c',
diff --git a/src/libnm-systemd-shared/sd-adapt-shared/netif-util.h b/src/libnm-systemd-shared/sd-adapt-shared/sd-messages.h
index 637892c2d6..637892c2d6 100644
--- a/src/libnm-systemd-shared/sd-adapt-shared/netif-util.h
+++ b/src/libnm-systemd-shared/sd-adapt-shared/sd-messages.h
diff --git a/src/libnm-systemd-shared/src/basic/alloc-util.c b/src/libnm-systemd-shared/src/basic/alloc-util.c
index c07ab58942..243ff521a7 100644
--- a/src/libnm-systemd-shared/src/basic/alloc-util.c
+++ b/src/libnm-systemd-shared/src/basic/alloc-util.c
@@ -105,6 +105,33 @@ void* greedy_realloc0(
return q;
}
+void* greedy_realloc_append(
+ void **p,
+ size_t *n_p,
+ const void *from,
+ size_t n_from,
+ size_t size) {
+
+ uint8_t *q;
+
+ assert(p);
+ assert(n_p);
+ assert(from || n_from == 0);
+
+ if (n_from > SIZE_MAX - *n_p)
+ return NULL;
+
+ q = greedy_realloc(p, *n_p + n_from, size);
+ if (!q)
+ return NULL;
+
+ memcpy_safe(q + *n_p * size, from, n_from * size);
+
+ *n_p += n_from;
+
+ return q;
+}
+
void *expand_to_usable(void *ptr, size_t newsize _unused_) {
return ptr;
}
diff --git a/src/libnm-systemd-shared/src/basic/alloc-util.h b/src/libnm-systemd-shared/src/basic/alloc-util.h
index 9a62381df1..c215c33f4b 100644
--- a/src/libnm-systemd-shared/src/basic/alloc-util.h
+++ b/src/libnm-systemd-shared/src/basic/alloc-util.h
@@ -15,13 +15,12 @@
typedef void (*free_func_t)(void *p);
typedef void* (*mfree_func_t)(void *p);
-typedef void (*free_array_func_t)(void *p, size_t n);
/* If for some reason more than 4M are allocated on the stack, let's abort immediately. It's better than
* proceeding and smashing the stack limits. Note that by default RLIMIT_STACK is 8M on Linux. */
#define ALLOCA_MAX (4U*1024U*1024U)
-#define new(t, n) ((t*) malloc_multiply(sizeof(t), (n)))
+#define new(t, n) ((t*) malloc_multiply(n, sizeof(t)))
#define new0(t, n) ((t*) calloc((n) ?: 1, sizeof(t)))
@@ -46,9 +45,9 @@ typedef void (*free_array_func_t)(void *p, size_t n);
(t*) alloca0((sizeof(t)*_n_)); \
})
-#define newdup(t, p, n) ((t*) memdup_multiply(p, sizeof(t), (n)))
+#define newdup(t, p, n) ((t*) memdup_multiply(p, n, sizeof(t)))
-#define newdup_suffix0(t, p, n) ((t*) memdup_suffix0_multiply(p, sizeof(t), (n)))
+#define newdup_suffix0(t, p, n) ((t*) memdup_suffix0_multiply(p, n, sizeof(t)))
#define malloc0(n) (calloc(1, (n) ?: 1))
@@ -113,7 +112,7 @@ static inline bool size_multiply_overflow(size_t size, size_t need) {
return _unlikely_(need != 0 && size > (SIZE_MAX / need));
}
-_malloc_ _alloc_(1, 2) static inline void *malloc_multiply(size_t size, size_t need) {
+_malloc_ _alloc_(1, 2) static inline void *malloc_multiply(size_t need, size_t size) {
if (size_multiply_overflow(size, need))
return NULL;
@@ -129,7 +128,7 @@ _alloc_(2, 3) static inline void *reallocarray(void *p, size_t need, size_t size
}
#endif
-_alloc_(2, 3) static inline void *memdup_multiply(const void *p, size_t size, size_t need) {
+_alloc_(2, 3) static inline void *memdup_multiply(const void *p, size_t need, size_t size) {
if (size_multiply_overflow(size, need))
return NULL;
@@ -138,7 +137,7 @@ _alloc_(2, 3) static inline void *memdup_multiply(const void *p, size_t size, si
/* Note that we can't decorate this function with _alloc_() since the returned memory area is one byte larger
* than the product of its parameters. */
-static inline void *memdup_suffix0_multiply(const void *p, size_t size, size_t need) {
+static inline void *memdup_suffix0_multiply(const void *p, size_t need, size_t size) {
if (size_multiply_overflow(size, need))
return NULL;
@@ -147,6 +146,7 @@ static inline void *memdup_suffix0_multiply(const void *p, size_t size, size_t n
void* greedy_realloc(void **p, size_t need, size_t size);
void* greedy_realloc0(void **p, size_t need, size_t size);
+void* greedy_realloc_append(void **p, size_t *n_p, const void *from, size_t n_from, size_t size);
#define GREEDY_REALLOC(array, need) \
greedy_realloc((void**) &(array), (need), sizeof((array)[0]))
@@ -154,6 +154,9 @@ void* greedy_realloc0(void **p, size_t need, size_t size);
#define GREEDY_REALLOC0(array, need) \
greedy_realloc0((void**) &(array), (need), sizeof((array)[0]))
+#define GREEDY_REALLOC_APPEND(array, n_array, from, n_from) \
+ greedy_realloc_append((void**) &(array), (size_t*) &(n_array), (from), (n_from), sizeof((array)[0]))
+
#define alloca0(n) \
({ \
char *_new_; \
@@ -224,7 +227,6 @@ static inline size_t malloc_sizeof_safe(void **xp) {
MALLOC_SIZEOF_SAFE(x)/sizeof((x)[0]), \
VOID_0))
-
/* These are like strdupa()/strndupa(), but honour ALLOCA_MAX */
#define strdupa_safe(s) \
({ \
@@ -235,7 +237,40 @@ static inline size_t malloc_sizeof_safe(void **xp) {
#define strndupa_safe(s, n) \
({ \
const char *_t = (s); \
- (char*) memdupa_suffix0(_t, strnlen(_t, (n))); \
+ (char*) memdupa_suffix0(_t, strnlen(_t, n)); \
})
+/* Free every element of the array. */
+static inline void free_many(void **p, size_t n) {
+ assert(p || n == 0);
+
+ FOREACH_ARRAY(i, p, n)
+ *i = mfree(*i);
+}
+
+/* Typesafe wrapper for char** rather than void**. Unfortunately C won't implicitly cast this. */
+static inline void free_many_charp(char **c, size_t n) {
+ free_many((void**) c, n);
+}
+
+_alloc_(2) static inline void *realloc0(void *p, size_t new_size) {
+ size_t old_size;
+ void *q;
+
+ /* Like realloc(), but initializes anything appended to zero */
+
+ old_size = MALLOC_SIZEOF_SAFE(p);
+
+ q = realloc(p, new_size);
+ if (!q)
+ return NULL;
+
+ new_size = MALLOC_SIZEOF_SAFE(q); /* Update with actually allocated space */
+
+ if (new_size > old_size)
+ memset((uint8_t*) q + old_size, 0, new_size - old_size);
+
+ return q;
+}
+
#include "memory-util.h"
diff --git a/src/libnm-systemd-shared/src/basic/arphrd-util.h b/src/libnm-systemd-shared/src/basic/arphrd-util.h
new file mode 100644
index 0000000000..33f5694abd
--- /dev/null
+++ b/src/libnm-systemd-shared/src/basic/arphrd-util.h
@@ -0,0 +1,10 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+#pragma once
+
+#include <inttypes.h>
+#include <stddef.h>
+
+const char *arphrd_to_name(int id);
+int arphrd_from_name(const char *name);
+
+size_t arphrd_to_hw_addr_len(uint16_t arphrd);
diff --git a/src/libnm-systemd-shared/src/basic/async.h b/src/libnm-systemd-shared/src/basic/async.h
deleted file mode 100644
index e0bbaa5658..0000000000
--- a/src/libnm-systemd-shared/src/basic/async.h
+++ /dev/null
@@ -1,13 +0,0 @@
-/* SPDX-License-Identifier: LGPL-2.1-or-later */
-#pragma once
-
-#include <sys/types.h>
-
-#include "macro.h"
-
-int asynchronous_job(void* (*func)(void *p), void *arg);
-
-int asynchronous_sync(pid_t *ret_pid);
-int asynchronous_close(int fd);
-
-DEFINE_TRIVIAL_CLEANUP_FUNC(int, asynchronous_close);
diff --git a/src/libnm-systemd-shared/src/basic/btrfs.c b/src/libnm-systemd-shared/src/basic/btrfs.c
new file mode 100644
index 0000000000..3b23607849
--- /dev/null
+++ b/src/libnm-systemd-shared/src/basic/btrfs.c
@@ -0,0 +1,100 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+
+#include "nm-sd-adapt-shared.h"
+
+#include <linux/btrfs.h>
+#include <sys/ioctl.h>
+
+#include "btrfs.h"
+#include "fd-util.h"
+#include "fs-util.h"
+#include "path-util.h"
+
+int btrfs_validate_subvolume_name(const char *name) {
+
+ if (!filename_is_valid(name))
+ return -EINVAL;
+
+ if (strlen(name) > BTRFS_SUBVOL_NAME_MAX)
+ return -E2BIG;
+
+ return 0;
+}
+
+static int extract_subvolume_name(const char *path, char **ret) {
+ _cleanup_free_ char *fn = NULL;
+ int r;
+
+ assert(path);
+ assert(ret);
+
+ r = path_extract_filename(path, &fn);
+ if (r < 0)
+ return r;
+
+ r = btrfs_validate_subvolume_name(fn);
+ if (r < 0)
+ return r;
+
+ *ret = TAKE_PTR(fn);
+ return 0;
+}
+
+int btrfs_subvol_make(int dir_fd, const char *path) {
+ struct btrfs_ioctl_vol_args args = {};
+ _cleanup_free_ char *subvolume = NULL, *parent = NULL;
+ _cleanup_close_ int fd = -EBADF;
+ int r;
+
+ assert(dir_fd >= 0 || dir_fd == AT_FDCWD);
+ assert(!isempty(path));
+
+ r = extract_subvolume_name(path, &subvolume);
+ if (r < 0)
+ return r;
+
+ r = path_extract_directory(path, &parent);
+ if (r < 0) {
+ if (r != -EDESTADDRREQ) /* Propagate error, unless only a filename was specified, which is OK */
+ return r;
+
+ dir_fd = fd_reopen_condition(dir_fd, O_CLOEXEC, O_PATH, &fd); /* drop O_PATH if it is set */
+ if (dir_fd < 0)
+ return dir_fd;
+ } else {
+ fd = openat(dir_fd, parent, O_DIRECTORY|O_RDONLY|O_CLOEXEC, 0);
+ if (fd < 0)
+ return -errno;
+
+ dir_fd = fd;
+ }
+
+ strncpy(args.name, subvolume, sizeof(args.name)-1);
+
+ return RET_NERRNO(ioctl(dir_fd, BTRFS_IOC_SUBVOL_CREATE, &args));
+}
+
+int btrfs_subvol_make_fallback(int dir_fd, const char *path, mode_t mode) {
+ mode_t old, combined;
+ int r;
+
+ assert(path);
+
+ /* Let's work like mkdir(), i.e. take the specified mode, and mask it with the current umask. */
+ old = umask(~mode);
+ combined = old | ~mode;
+ if (combined != ~mode)
+ umask(combined);
+ r = btrfs_subvol_make(dir_fd, path);
+ umask(old);
+
+ if (r >= 0)
+ return 1; /* subvol worked */
+ if (!ERRNO_IS_NOT_SUPPORTED(r))
+ return r;
+
+ if (mkdirat(dir_fd, path, mode) < 0)
+ return -errno;
+
+ return 0; /* plain directory */
+}
diff --git a/src/libnm-systemd-shared/src/basic/btrfs.h b/src/libnm-systemd-shared/src/basic/btrfs.h
new file mode 100644
index 0000000000..38be9d2b3b
--- /dev/null
+++ b/src/libnm-systemd-shared/src/basic/btrfs.h
@@ -0,0 +1,9 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+
+#include <fcntl.h>
+
+int btrfs_validate_subvolume_name(const char *name);
+
+int btrfs_subvol_make(int dir_fd, const char *path);
+
+int btrfs_subvol_make_fallback(int dir_fd, const char *path, mode_t mode);
diff --git a/src/libnm-systemd-shared/src/basic/cgroup-util.h b/src/libnm-systemd-shared/src/basic/cgroup-util.h
index 9b30ae0396..244f3b657b 100644
--- a/src/libnm-systemd-shared/src/basic/cgroup-util.h
+++ b/src/libnm-systemd-shared/src/basic/cgroup-util.h
@@ -10,6 +10,7 @@
#include <sys/types.h>
#include "constants.h"
+#include "pidref.h"
#include "set.h"
#define SYSTEMD_CGROUP_CONTROLLER_LEGACY "name=systemd"
@@ -35,7 +36,7 @@ typedef enum CGroupController {
CGROUP_CONTROLLER_BPF_SOCKET_BIND,
CGROUP_CONTROLLER_BPF_RESTRICT_NETWORK_INTERFACES,
/* The BPF hook implementing RestrictFileSystems= is not defined here.
- * It's applied as late as possible in exec_child() so we don't block
+ * It's applied as late as possible in exec_invoke() so we don't block
* our own unit setup code. */
_CGROUP_CONTROLLER_MAX,
@@ -66,10 +67,13 @@ typedef enum CGroupMask {
/* All real cgroup v2 controllers */
CGROUP_MASK_V2 = CGROUP_MASK_CPU|CGROUP_MASK_CPUSET|CGROUP_MASK_IO|CGROUP_MASK_MEMORY|CGROUP_MASK_PIDS,
+ /* All controllers we want to delegate in case of Delegate=yes. Which are pretty much the v2 controllers only, as delegation on v1 is not safe, and bpf stuff isn't a real controller */
+ CGROUP_MASK_DELEGATE = CGROUP_MASK_V2,
+
/* All cgroup v2 BPF pseudo-controllers */
CGROUP_MASK_BPF = CGROUP_MASK_BPF_FIREWALL|CGROUP_MASK_BPF_DEVICES|CGROUP_MASK_BPF_FOREIGN|CGROUP_MASK_BPF_SOCKET_BIND|CGROUP_MASK_BPF_RESTRICT_NETWORK_INTERFACES,
- _CGROUP_MASK_ALL = CGROUP_CONTROLLER_TO_MASK(_CGROUP_CONTROLLER_MAX) - 1
+ _CGROUP_MASK_ALL = CGROUP_CONTROLLER_TO_MASK(_CGROUP_CONTROLLER_MAX) - 1,
} CGroupMask;
static inline CGroupMask CGROUP_MASK_EXTEND_JOINED(CGroupMask mask) {
@@ -176,13 +180,13 @@ typedef enum CGroupUnified {
* generate paths with multiple adjacent / removed.
*/
-int cg_enumerate_processes(const char *controller, const char *path, FILE **_f);
-int cg_read_pid(FILE *f, pid_t *_pid);
-int cg_read_event(const char *controller, const char *path, const char *event,
- char **val);
+int cg_enumerate_processes(const char *controller, const char *path, FILE **ret);
+int cg_read_pid(FILE *f, pid_t *ret);
+int cg_read_pidref(FILE *f, PidRef *ret);
+int cg_read_event(const char *controller, const char *path, const char *event, char **ret);
-int cg_enumerate_subgroups(const char *controller, const char *path, DIR **_d);
-int cg_read_subgroup(DIR *d, char **fn);
+int cg_enumerate_subgroups(const char *controller, const char *path, DIR **ret);
+int cg_read_subgroup(DIR *d, char **ret);
typedef enum CGroupFlags {
CGROUP_SIGCONT = 1 << 0,
@@ -190,25 +194,31 @@ typedef enum CGroupFlags {
CGROUP_REMOVE = 1 << 2,
} CGroupFlags;
-typedef int (*cg_kill_log_func_t)(pid_t pid, int sig, void *userdata);
+typedef int (*cg_kill_log_func_t)(const PidRef *pid, int sig, void *userdata);
-int cg_kill(const char *controller, const char *path, int sig, CGroupFlags flags, Set *s, cg_kill_log_func_t kill_log, void *userdata);
-int cg_kill_kernel_sigkill(const char *controller, const char *path);
-int cg_kill_recursive(const char *controller, const char *path, int sig, CGroupFlags flags, Set *s, cg_kill_log_func_t kill_log, void *userdata);
+int cg_kill(const char *path, int sig, CGroupFlags flags, Set *s, cg_kill_log_func_t kill_log, void *userdata);
+int cg_kill_kernel_sigkill(const char *path);
+int cg_kill_recursive(const char *path, int sig, CGroupFlags flags, Set *s, cg_kill_log_func_t kill_log, void *userdata);
int cg_split_spec(const char *spec, char **ret_controller, char **ret_path);
-int cg_mangle_path(const char *path, char **result);
+int cg_mangle_path(const char *path, char **ret);
-int cg_get_path(const char *controller, const char *path, const char *suffix, char **fs);
-int cg_get_path_and_check(const char *controller, const char *path, const char *suffix, char **fs);
+int cg_get_path(const char *controller, const char *path, const char *suffix, char **ret);
+int cg_get_path_and_check(const char *controller, const char *path, const char *suffix, char **ret);
-int cg_pid_get_path(const char *controller, pid_t pid, char **path);
+int cg_pid_get_path(const char *controller, pid_t pid, char **ret);
+int cg_pidref_get_path(const char *controller, const PidRef *pidref, char **ret);
int cg_rmdir(const char *controller, const char *path);
-int cg_is_threaded(const char *controller, const char *path);
+int cg_is_threaded(const char *path);
+
+int cg_is_delegated(const char *path);
+int cg_is_delegated_fd(int fd);
+
+int cg_has_coredump_receive(const char *path);
-typedef enum {
+typedef enum {
CG_KEY_MODE_GRACEFUL = 1 << 0,
} CGroupKeyMode;
@@ -239,14 +249,14 @@ int cg_get_attribute_as_uint64(const char *controller, const char *path, const c
/* Does a parse_boolean() on the attribute contents and sets ret accordingly */
int cg_get_attribute_as_bool(const char *controller, const char *path, const char *attribute, bool *ret);
-int cg_get_owner(const char *controller, const char *path, uid_t *ret_uid);
+int cg_get_owner(const char *path, uid_t *ret_uid);
-int cg_set_xattr(const char *controller, const char *path, const char *name, const void *value, size_t size, int flags);
-int cg_get_xattr(const char *controller, const char *path, const char *name, void *value, size_t size);
-int cg_get_xattr_malloc(const char *controller, const char *path, const char *name, char **ret);
+int cg_set_xattr(const char *path, const char *name, const void *value, size_t size, int flags);
+int cg_get_xattr(const char *path, const char *name, void *value, size_t size);
+int cg_get_xattr_malloc(const char *path, const char *name, char **ret);
/* Returns negative on error, and 0 or 1 on success for the bool value */
-int cg_get_xattr_bool(const char *controller, const char *path, const char *name);
-int cg_remove_xattr(const char *controller, const char *path, const char *name);
+int cg_get_xattr_bool(const char *path, const char *name);
+int cg_remove_xattr(const char *path, const char *name);
int cg_install_release_agent(const char *controller, const char *agent);
int cg_uninstall_release_agent(const char *controller);
@@ -257,27 +267,28 @@ int cg_is_empty_recursive(const char *controller, const char *path);
int cg_get_root_path(char **path);
int cg_path_get_cgroupid(const char *path, uint64_t *ret);
-int cg_path_get_session(const char *path, char **session);
-int cg_path_get_owner_uid(const char *path, uid_t *uid);
-int cg_path_get_unit(const char *path, char **unit);
-int cg_path_get_unit_path(const char *path, char **unit);
-int cg_path_get_user_unit(const char *path, char **unit);
-int cg_path_get_machine_name(const char *path, char **machine);
-int cg_path_get_slice(const char *path, char **slice);
-int cg_path_get_user_slice(const char *path, char **slice);
-
-int cg_shift_path(const char *cgroup, const char *cached_root, const char **shifted);
-int cg_pid_get_path_shifted(pid_t pid, const char *cached_root, char **cgroup);
-
-int cg_pid_get_session(pid_t pid, char **session);
-int cg_pid_get_owner_uid(pid_t pid, uid_t *uid);
-int cg_pid_get_unit(pid_t pid, char **unit);
-int cg_pid_get_user_unit(pid_t pid, char **unit);
-int cg_pid_get_machine_name(pid_t pid, char **machine);
-int cg_pid_get_slice(pid_t pid, char **slice);
-int cg_pid_get_user_slice(pid_t pid, char **slice);
-
-int cg_path_decode_unit(const char *cgroup, char **unit);
+int cg_path_get_session(const char *path, char **ret_session);
+int cg_path_get_owner_uid(const char *path, uid_t *ret_uid);
+int cg_path_get_unit(const char *path, char **ret_unit);
+int cg_path_get_unit_path(const char *path, char **ret_unit);
+int cg_path_get_user_unit(const char *path, char **ret_unit);
+int cg_path_get_machine_name(const char *path, char **ret_machine);
+int cg_path_get_slice(const char *path, char **ret_slice);
+int cg_path_get_user_slice(const char *path, char **ret_slice);
+
+int cg_shift_path(const char *cgroup, const char *cached_root, const char **ret_shifted);
+int cg_pid_get_path_shifted(pid_t pid, const char *cached_root, char **ret_cgroup);
+
+int cg_pid_get_session(pid_t pid, char **ret_session);
+int cg_pid_get_owner_uid(pid_t pid, uid_t *ret_uid);
+int cg_pid_get_unit(pid_t pid, char **ret_unit);
+int cg_pidref_get_unit(const PidRef *pidref, char **ret);
+int cg_pid_get_user_unit(pid_t pid, char **ret_unit);
+int cg_pid_get_machine_name(pid_t pid, char **ret_machine);
+int cg_pid_get_slice(pid_t pid, char **ret_slice);
+int cg_pid_get_user_slice(pid_t pid, char **ret_slice);
+
+int cg_path_decode_unit(const char *cgroup, char **ret_unit);
bool cg_needs_escape(const char *p);
int cg_escape(const char *p, char **ret);
diff --git a/src/libnm-systemd-shared/src/basic/constants.h b/src/libnm-systemd-shared/src/basic/constants.h
index 3f96786da9..6bb5f3c281 100644
--- a/src/libnm-systemd-shared/src/basic/constants.h
+++ b/src/libnm-systemd-shared/src/basic/constants.h
@@ -59,22 +59,13 @@
#define NOTIFY_FD_MAX 768
#define NOTIFY_BUFFER_MAX PIPE_BUF
-#if HAVE_SPLIT_USR
-# define _CONF_PATHS_SPLIT_USR_NULSTR(n) "/lib/" n "\0"
-# define _CONF_PATHS_SPLIT_USR(n) , "/lib/" n
-#else
-# define _CONF_PATHS_SPLIT_USR_NULSTR(n)
-# define _CONF_PATHS_SPLIT_USR(n)
-#endif
-
/* Return a nulstr for a standard cascade of configuration paths, suitable to pass to
* conf_files_list_nulstr() to implement drop-in directories for extending configuration files. */
#define CONF_PATHS_NULSTR(n) \
"/etc/" n "\0" \
"/run/" n "\0" \
"/usr/local/lib/" n "\0" \
- "/usr/lib/" n "\0" \
- _CONF_PATHS_SPLIT_USR_NULSTR(n)
+ "/usr/lib/" n "\0"
#define CONF_PATHS_USR(n) \
"/etc/" n, \
@@ -83,8 +74,7 @@
"/usr/lib/" n
#define CONF_PATHS(n) \
- CONF_PATHS_USR(n) \
- _CONF_PATHS_SPLIT_USR(n)
+ CONF_PATHS_USR(n)
#define CONF_PATHS_USR_STRV(n) \
STRV_MAKE(CONF_PATHS_USR(n))
@@ -99,14 +89,9 @@
* in containers so that our children inherit that. */
#define DEFAULT_RLIMIT_MEMLOCK (1024ULL*1024ULL*8ULL)
-#define PLYMOUTH_SOCKET { \
- .un.sun_family = AF_UNIX, \
- .un.sun_path = "\0/org/freedesktop/plymouthd", \
- }
-
/* Path where PID1 listens for varlink subscriptions from systemd-oomd to notify of changes in ManagedOOM settings. */
-#define VARLINK_ADDR_PATH_MANAGED_OOM_SYSTEM "/run/systemd/io.system.ManagedOOM"
+#define VARLINK_ADDR_PATH_MANAGED_OOM_SYSTEM "/run/systemd/io.systemd.ManagedOOM"
/* Path where systemd-oomd listens for varlink connections from user managers to report changes in ManagedOOM settings. */
-#define VARLINK_ADDR_PATH_MANAGED_OOM_USER "/run/systemd/oom/io.system.ManagedOOM"
+#define VARLINK_ADDR_PATH_MANAGED_OOM_USER "/run/systemd/oom/io.systemd.ManagedOOM"
#define KERNEL_BASELINE_VERSION "4.15"
diff --git a/src/libnm-systemd-shared/src/basic/env-file.c b/src/libnm-systemd-shared/src/basic/env-file.c
index db270bedce..75b2febf7d 100644
--- a/src/libnm-systemd-shared/src/basic/env-file.c
+++ b/src/libnm-systemd-shared/src/basic/env-file.c
@@ -127,7 +127,7 @@ static int parse_env_file_internal(
state = VALUE;
if (!GREEDY_REALLOC(value, n_value+2))
- return -ENOMEM;
+ return -ENOMEM;
value[n_value++] = c;
}
@@ -245,7 +245,13 @@ static int parse_env_file_internal(
break;
case COMMENT_ESCAPE:
- state = COMMENT;
+ log_debug("The line which doesn't begin with \";\" or \"#\", but follows a comment" \
+ " line trailing with escape is now treated as a non comment line since v254.");
+ if (strchr(NEWLINE, c)) {
+ state = PRE_KEY;
+ line++;
+ } else
+ state = COMMENT;
break;
}
}
@@ -522,6 +528,7 @@ static int merge_env_file_push(
char ***env = ASSERT_PTR(userdata);
char *expanded_value;
+ int r;
assert(key);
@@ -536,12 +543,12 @@ static int merge_env_file_push(
return 0;
}
- expanded_value = replace_env(value, *env,
- REPLACE_ENV_USE_ENVIRONMENT|
- REPLACE_ENV_ALLOW_BRACELESS|
- REPLACE_ENV_ALLOW_EXTENDED);
- if (!expanded_value)
- return -ENOMEM;
+ r = replace_env(value,
+ *env,
+ REPLACE_ENV_USE_ENVIRONMENT|REPLACE_ENV_ALLOW_BRACELESS|REPLACE_ENV_ALLOW_EXTENDED,
+ &expanded_value);
+ if (r < 0)
+ return log_error_errno(r, "%s:%u: Failed to expand variable '%s': %m", strna(filename), line, value);
free_and_replace(value, expanded_value);
@@ -599,7 +606,7 @@ static void write_env_var(FILE *f, const char *v) {
fputc_unlocked('\n', f);
}
-int write_env_file_at(int dir_fd, const char *fname, char **l) {
+int write_env_file(int dir_fd, const char *fname, char **headers, char **l) {
_cleanup_fclose_ FILE *f = NULL;
_cleanup_free_ char *p = NULL;
int r;
@@ -613,6 +620,12 @@ int write_env_file_at(int dir_fd, const char *fname, char **l) {
(void) fchmod_umask(fileno(f), 0644);
+ STRV_FOREACH(i, headers) {
+ assert(isempty(*i) || startswith(*i, "#"));
+ fputs_unlocked(*i, f);
+ fputc_unlocked('\n', f);
+ }
+
STRV_FOREACH(i, l)
write_env_var(f, *i);
@@ -627,4 +640,12 @@ int write_env_file_at(int dir_fd, const char *fname, char **l) {
(void) unlinkat(dir_fd, p, 0);
return r;
}
+
+int write_vconsole_conf(int dir_fd, const char *fname, char **l) {
+ char **headers = STRV_MAKE(
+ "# Written by systemd-localed(8) or systemd-firstboot(1), read by systemd-localed",
+ "# and systemd-vconsole-setup(8). Use localectl(1) to update this file.");
+
+ return write_env_file(dir_fd, fname, headers, l);
+}
#endif /* NM_IGNORED */
diff --git a/src/libnm-systemd-shared/src/basic/env-file.h b/src/libnm-systemd-shared/src/basic/env-file.h
index 2465eeddf4..37db30765b 100644
--- a/src/libnm-systemd-shared/src/basic/env-file.h
+++ b/src/libnm-systemd-shared/src/basic/env-file.h
@@ -19,7 +19,6 @@ int load_env_file_pairs_fd(int fd, const char *fname, char ***ret);
int merge_env_file(char ***env, FILE *f, const char *fname);
-int write_env_file_at(int dir_fd, const char *fname, char **l);
-static inline int write_env_file(const char *fname, char **l) {
- return write_env_file_at(AT_FDCWD, fname, l);
-}
+int write_env_file(int dir_fd, const char *fname, char **headers, char **l);
+
+int write_vconsole_conf(int dir_fd, const char *fname, char **l);
diff --git a/src/libnm-systemd-shared/src/basic/env-util.c b/src/libnm-systemd-shared/src/basic/env-util.c
index fa2753bccb..c12caa2e55 100644
--- a/src/libnm-systemd-shared/src/basic/env-util.c
+++ b/src/libnm-systemd-shared/src/basic/env-util.c
@@ -29,20 +29,21 @@
"_"
static bool env_name_is_valid_n(const char *e, size_t n) {
- if (!e)
- return false;
+
+ if (n == SIZE_MAX)
+ n = strlen_ptr(e);
if (n <= 0)
return false;
+ assert(e);
+
if (ascii_isdigit(e[0]))
return false;
- /* POSIX says the overall size of the environment block cannot
- * be > ARG_MAX, an individual assignment hence cannot be
- * either. Discounting the equal sign and trailing NUL this
- * hence leaves ARG_MAX-2 as longest possible variable
- * name. */
+ /* POSIX says the overall size of the environment block cannot be > ARG_MAX, an individual assignment
+ * hence cannot be either. Discounting the equal sign and trailing NUL this hence leaves ARG_MAX-2 as
+ * longest possible variable name. */
if (n > (size_t) sysconf(_SC_ARG_MAX) - 2)
return false;
@@ -246,9 +247,9 @@ static bool env_match(const char *t, const char *pattern) {
return true;
if (!strchr(pattern, '=')) {
- size_t l = strlen(pattern);
+ t = startswith(t, pattern);
- return strneq(t, pattern, l) && t[l] == '=';
+ return t && *t == '=';
}
return false;
@@ -460,6 +461,35 @@ int strv_env_assign(char ***l, const char *key, const char *value) {
return strv_env_replace_consume(l, p);
}
+int strv_env_assignf(char ***l, const char *key, const char *valuef, ...) {
+ int r;
+
+ assert(l);
+ assert(key);
+
+ if (!env_name_is_valid(key))
+ return -EINVAL;
+
+ if (!valuef) {
+ strv_env_unset(*l, key);
+ return 0;
+ }
+
+ _cleanup_free_ char *value = NULL;
+ va_list ap;
+ va_start(ap, valuef);
+ r = vasprintf(&value, valuef, ap);
+ va_end(ap);
+ if (r < 0)
+ return -ENOMEM;
+
+ char *p = strjoin(key, "=", value);
+ if (!p)
+ return -ENOMEM;
+
+ return strv_env_replace_consume(l, p);
+}
+
int _strv_env_assign_many(char ***l, ...) {
va_list ap;
int r;
@@ -502,32 +532,31 @@ int _strv_env_assign_many(char ***l, ...) {
return 0;
}
-char *strv_env_get_n(char **l, const char *name, size_t k, unsigned flags) {
+char* strv_env_get_n(char * const *l, const char *name, size_t k, ReplaceEnvFlags flags) {
assert(name);
+ if (k == SIZE_MAX)
+ k = strlen(name);
if (k <= 0)
return NULL;
STRV_FOREACH_BACKWARDS(i, l)
- if (strneq(*i, name, k) &&
- (*i)[k] == '=')
- return *i + k + 1;
+ if (strneq(*i, name, k) && (*i)[k] == '=')
+ return (char*) *i + k + 1;
if (flags & REPLACE_ENV_USE_ENVIRONMENT) {
const char *t;
+ /* Safety check that the name is not overly long, before we do a stack allocation */
+ if (k > (size_t) sysconf(_SC_ARG_MAX) - 2)
+ return NULL;
+
t = strndupa_safe(name, k);
return getenv(t);
};
return NULL;
}
-
-char *strv_env_get(char **l, const char *name) {
- assert(name);
-
- return strv_env_get_n(l, name, strlen(name), 0);
-}
#endif /* NM_IGNORED */
char *strv_env_pairs_get(char **l, const char *name) {
@@ -578,7 +607,61 @@ char **strv_env_clean_with_callback(char **e, void (*invalid_callback)(const cha
return e;
}
-char *replace_env_n(const char *format, size_t n, char **env, unsigned flags) {
+static int strv_extend_with_length(char ***l, const char *s, size_t n) {
+ char *c;
+
+ c = strndup(s, n);
+ if (!c)
+ return -ENOMEM;
+
+ return strv_consume(l, c);
+}
+
+static int strv_env_get_n_validated(
+ char **env,
+ const char *name,
+ size_t l,
+ ReplaceEnvFlags flags,
+ char **ret, /* points into the env block! do not free! */
+ char ***unset_variables, /* updated in place */
+ char ***bad_variables) { /* ditto */
+
+ char *e;
+ int r;
+
+ assert(l == 0 || name);
+ assert(ret);
+
+ if (env_name_is_valid_n(name, l)) {
+ e = strv_env_get_n(env, name, l, flags);
+ if (!e && unset_variables) {
+ r = strv_extend_with_length(unset_variables, name, l);
+ if (r < 0)
+ return r;
+ }
+ } else {
+ e = NULL; /* Resolve invalid variable names the same way as unset ones */
+
+ if (bad_variables) {
+ r = strv_extend_with_length(bad_variables, name, l);
+ if (r < 0)
+ return r;
+ }
+ }
+
+ *ret = e;
+ return !!e;
+}
+
+int replace_env_full(
+ const char *format,
+ size_t n,
+ char **env,
+ ReplaceEnvFlags flags,
+ char **ret,
+ char ***ret_unset_variables,
+ char ***ret_bad_variables) {
+
enum {
WORD,
CURLY,
@@ -589,15 +672,22 @@ char *replace_env_n(const char *format, size_t n, char **env, unsigned flags) {
ALTERNATE_VALUE,
} state = WORD;
+ _cleanup_strv_free_ char **unset_variables = NULL, **bad_variables = NULL;
const char *e, *word = format, *test_value = NULL; /* test_value is initialized to appease gcc */
- char *k;
_cleanup_free_ char *s = NULL;
+ char ***pu, ***pb, *k;
size_t i, len = 0; /* len is initialized to appease gcc */
- int nest = 0;
+ int nest = 0, r;
assert(format);
- for (e = format, i = 0; *e && i < n; e ++, i ++)
+ if (n == SIZE_MAX)
+ n = strlen(format);
+
+ pu = ret_unset_variables ? &unset_variables : NULL;
+ pb = ret_bad_variables ? &bad_variables : NULL;
+
+ for (e = format, i = 0; *e && i < n; e++, i++)
switch (state) {
case WORD:
@@ -609,27 +699,28 @@ char *replace_env_n(const char *format, size_t n, char **env, unsigned flags) {
if (*e == '{') {
k = strnappend(s, word, e-word-1);
if (!k)
- return NULL;
+ return -ENOMEM;
free_and_replace(s, k);
word = e-1;
state = VARIABLE;
nest++;
+
} else if (*e == '$') {
k = strnappend(s, word, e-word);
if (!k)
- return NULL;
+ return -ENOMEM;
free_and_replace(s, k);
word = e+1;
state = WORD;
- } else if (flags & REPLACE_ENV_ALLOW_BRACELESS && strchr(VALID_BASH_ENV_NAME_CHARS, *e)) {
+ } else if (FLAGS_SET(flags, REPLACE_ENV_ALLOW_BRACELESS) && strchr(VALID_BASH_ENV_NAME_CHARS, *e)) {
k = strnappend(s, word, e-word-1);
if (!k)
- return NULL;
+ return -ENOMEM;
free_and_replace(s, k);
@@ -642,12 +733,14 @@ char *replace_env_n(const char *format, size_t n, char **env, unsigned flags) {
case VARIABLE:
if (*e == '}') {
- const char *t;
+ char *t;
- t = strv_env_get_n(env, word+2, e-word-2, flags);
+ r = strv_env_get_n_validated(env, word+2, e-word-2, flags, &t, pu, pb);
+ if (r < 0)
+ return r;
if (!strextend(&s, t))
- return NULL;
+ return -ENOMEM;
word = e+1;
state = WORD;
@@ -689,18 +782,37 @@ char *replace_env_n(const char *format, size_t n, char **env, unsigned flags) {
nest--;
if (nest == 0) {
- const char *t;
+ _cleanup_strv_free_ char **u = NULL, **b = NULL;
_cleanup_free_ char *v = NULL;
+ char *t = NULL;
+
+ r = strv_env_get_n_validated(env, word+2, len, flags, &t, pu, pb);
+ if (r < 0)
+ return r;
+
+ if (t && state == ALTERNATE_VALUE) {
+ r = replace_env_full(test_value, e-test_value, env, flags, &v, pu ? &u : NULL, pb ? &b : NULL);
+ if (r < 0)
+ return r;
+
+ t = v;
+ } else if (!t && state == DEFAULT_VALUE) {
+ r = replace_env_full(test_value, e-test_value, env, flags, &v, pu ? &u : NULL, pb ? &b : NULL);
+ if (r < 0)
+ return r;
- t = strv_env_get_n(env, word+2, len, flags);
+ t = v;
+ }
- if (t && state == ALTERNATE_VALUE)
- t = v = replace_env_n(test_value, e-test_value, env, flags);
- else if (!t && state == DEFAULT_VALUE)
- t = v = replace_env_n(test_value, e-test_value, env, flags);
+ r = strv_extend_strv(&unset_variables, u, /* filter_duplicates= */ true);
+ if (r < 0)
+ return r;
+ r = strv_extend_strv(&bad_variables, b, /* filter_duplicates= */ true);
+ if (r < 0)
+ return r;
if (!strextend(&s, t))
- return NULL;
+ return -ENOMEM;
word = e+1;
state = WORD;
@@ -711,12 +823,14 @@ char *replace_env_n(const char *format, size_t n, char **env, unsigned flags) {
assert(flags & REPLACE_ENV_ALLOW_BRACELESS);
if (!strchr(VALID_BASH_ENV_NAME_CHARS, *e)) {
- const char *t;
+ char *t = NULL;
- t = strv_env_get_n(env, word+1, e-word-1, flags);
+ r = strv_env_get_n_validated(env, word+1, e-word-1, flags, &t, &unset_variables, &bad_variables);
+ if (r < 0)
+ return r;
if (!strextend(&s, t))
- return NULL;
+ return -ENOMEM;
word = e--;
i--;
@@ -726,58 +840,83 @@ char *replace_env_n(const char *format, size_t n, char **env, unsigned flags) {
}
if (state == VARIABLE_RAW) {
- const char *t;
+ char *t;
assert(flags & REPLACE_ENV_ALLOW_BRACELESS);
- t = strv_env_get_n(env, word+1, e-word-1, flags);
- return strjoin(s, t);
- } else
- return strnappend(s, word, e-word);
+ r = strv_env_get_n_validated(env, word+1, e-word-1, flags, &t, &unset_variables, &bad_variables);
+ if (r < 0)
+ return r;
+
+ if (!strextend(&s, t))
+ return -ENOMEM;
+
+ } else if (!strextendn(&s, word, e-word))
+ return -ENOMEM;
+
+ if (ret_unset_variables)
+ *ret_unset_variables = TAKE_PTR(unset_variables);
+ if (ret_bad_variables)
+ *ret_bad_variables = TAKE_PTR(bad_variables);
+
+ if (ret)
+ *ret = TAKE_PTR(s);
+
+ return 0;
}
-char **replace_env_argv(char **argv, char **env) {
- _cleanup_strv_free_ char **ret = NULL;
+int replace_env_argv(
+ char **argv,
+ char **env,
+ char ***ret,
+ char ***ret_unset_variables,
+ char ***ret_bad_variables) {
+
+ _cleanup_strv_free_ char **n = NULL, **unset_variables = NULL, **bad_variables = NULL;
size_t k = 0, l = 0;
+ int r;
l = strv_length(argv);
- ret = new(char*, l+1);
- if (!ret)
- return NULL;
+ n = new(char*, l+1);
+ if (!n)
+ return -ENOMEM;
STRV_FOREACH(i, argv) {
+ const char *word = *i;
/* If $FOO appears as single word, replace it by the split up variable */
- if ((*i)[0] == '$' && !IN_SET((*i)[1], '{', '$')) {
- char *e;
- char **w;
+ if (word[0] == '$' && !IN_SET(word[1], '{', '$')) {
_cleanup_strv_free_ char **m = NULL;
+ const char *name = word + 1;
+ char *e, **w;
size_t q;
- e = strv_env_get(env, *i+1);
- if (e) {
- int r;
-
- r = strv_split_full(&m, e, WHITESPACE, EXTRACT_RELAX|EXTRACT_UNQUOTE);
- if (r < 0) {
- ret[k] = NULL;
- return NULL;
- }
- }
+ if (env_name_is_valid(name)) {
+ e = strv_env_get(env, name);
+ if (e)
+ r = strv_split_full(&m, e, WHITESPACE, EXTRACT_RELAX|EXTRACT_UNQUOTE);
+ else if (ret_unset_variables)
+ r = strv_extend(&unset_variables, name);
+ else
+ r = 0;
+ } else if (ret_bad_variables)
+ r = strv_extend(&bad_variables, name);
+ else
+ r = 0;
+ if (r < 0)
+ return r;
q = strv_length(m);
l = l + q - 1;
- w = reallocarray(ret, l + 1, sizeof(char *));
- if (!w) {
- ret[k] = NULL;
- return NULL;
- }
+ w = reallocarray(n, l + 1, sizeof(char*));
+ if (!w)
+ return -ENOMEM;
- ret = w;
+ n = w;
if (m) {
- memcpy(ret + k, m, q * sizeof(char*));
+ memcpy(n + k, m, (q + 1) * sizeof(char*));
m = mfree(m);
}
@@ -785,15 +924,41 @@ char **replace_env_argv(char **argv, char **env) {
continue;
}
+ _cleanup_strv_free_ char **u = NULL, **b = NULL;
+
/* If ${FOO} appears as part of a word, replace it by the variable as-is */
- ret[k] = replace_env(*i, env, 0);
- if (!ret[k])
- return NULL;
- k++;
+ r = replace_env_full(
+ word,
+ /* length= */ SIZE_MAX,
+ env,
+ /* flags= */ 0,
+ n + k,
+ ret_unset_variables ? &u : NULL,
+ ret_bad_variables ? &b : NULL);
+ if (r < 0)
+ return r;
+ n[++k] = NULL;
+
+ r = strv_extend_strv(&unset_variables, u, /* filter_duplicates= */ true);
+ if (r < 0)
+ return r;
+
+ r = strv_extend_strv(&bad_variables, b, /*filter_duplicates= */ true);
+ if (r < 0)
+ return r;
+ }
+
+ if (ret_unset_variables) {
+ strv_uniq(strv_sort(unset_variables));
+ *ret_unset_variables = TAKE_PTR(unset_variables);
+ }
+ if (ret_bad_variables) {
+ strv_uniq(strv_sort(bad_variables));
+ *ret_bad_variables = TAKE_PTR(bad_variables);
}
- ret[k] = NULL;
- return TAKE_PTR(ret);
+ *ret = TAKE_PTR(n);
+ return 0;
}
#endif /* NM_IGNORED */
@@ -853,8 +1018,8 @@ int putenv_dup(const char *assignment, bool override) {
}
int setenv_systemd_exec_pid(bool update_only) {
- char str[DECIMAL_STR_MAX(pid_t)];
const char *e;
+ int r;
/* Update $SYSTEMD_EXEC_PID=pid except when '*' is set for the variable. */
@@ -865,10 +1030,9 @@ int setenv_systemd_exec_pid(bool update_only) {
if (streq_ptr(e, "*"))
return 0;
- xsprintf(str, PID_FMT, getpid_cached());
-
- if (setenv("SYSTEMD_EXEC_PID", str, 1) < 0)
- return -errno;
+ r = setenvf("SYSTEMD_EXEC_PID", /* overwrite= */ 1, PID_FMT, getpid_cached());
+ if (r < 0)
+ return r;
return 1;
}
@@ -944,4 +1108,45 @@ int getenv_steal_erase(const char *name, char **ret) {
return 1;
}
+
+int set_full_environment(char **env) {
+ int r;
+
+ clearenv();
+
+ STRV_FOREACH(e, env) {
+ _cleanup_free_ char *k = NULL, *v = NULL;
+
+ r = split_pair(*e, "=", &k, &v);
+ if (r < 0)
+ return r;
+
+ if (setenv(k, v, /* overwrite= */ true) < 0)
+ return -errno;
+ }
+
+ return 0;
+}
+
+int setenvf(const char *name, bool overwrite, const char *valuef, ...) {
+ _cleanup_free_ char *value = NULL;
+ va_list ap;
+ int r;
+
+ assert(name);
+
+ if (!valuef)
+ return RET_NERRNO(unsetenv(name));
+
+ va_start(ap, valuef);
+ DISABLE_WARNING_FORMAT_NONLITERAL;
+ r = vasprintf(&value, valuef, ap);
+ REENABLE_WARNING;
+ va_end(ap);
+
+ if (r < 0)
+ return -ENOMEM;
+
+ return RET_NERRNO(setenv(name, value, overwrite));
+}
#endif /* NM_IGNORED */
diff --git a/src/libnm-systemd-shared/src/basic/env-util.h b/src/libnm-systemd-shared/src/basic/env-util.h
index b0ff5a11d1..ad127de39f 100644
--- a/src/libnm-systemd-shared/src/basic/env-util.h
+++ b/src/libnm-systemd-shared/src/basic/env-util.h
@@ -19,19 +19,19 @@ bool env_name_is_valid(const char *e);
bool env_value_is_valid(const char *e);
bool env_assignment_is_valid(const char *e);
-enum {
+typedef enum ReplaceEnvFlags {
REPLACE_ENV_USE_ENVIRONMENT = 1 << 0,
REPLACE_ENV_ALLOW_BRACELESS = 1 << 1,
REPLACE_ENV_ALLOW_EXTENDED = 1 << 2,
-};
+} ReplaceEnvFlags;
-char *replace_env_n(const char *format, size_t n, char **env, unsigned flags);
-char **replace_env_argv(char **argv, char **env);
-
-static inline char *replace_env(const char *format, char **env, unsigned flags) {
- return replace_env_n(format, strlen(format), env, flags);
+int replace_env_full(const char *format, size_t n, char **env, ReplaceEnvFlags flags, char **ret, char ***ret_unset_variables, char ***ret_bad_variables);
+static inline int replace_env(const char *format, char **env, ReplaceEnvFlags flags, char **ret) {
+ return replace_env_full(format, SIZE_MAX, env, flags, ret, NULL, NULL);
}
+int replace_env_argv(char **argv, char **env, char ***ret, char ***ret_unset_variables, char ***ret_bad_variables);
+
bool strv_env_is_valid(char **e);
#define strv_env_clean(l) strv_env_clean_with_callback(l, NULL, NULL)
char **strv_env_clean_with_callback(char **l, void (*invalid_callback)(const char *p, void *userdata), void *userdata);
@@ -49,11 +49,15 @@ int strv_env_replace_consume(char ***l, char *p); /* In place ... */
int strv_env_replace_strdup(char ***l, const char *assignment);
int strv_env_replace_strdup_passthrough(char ***l, const char *assignment);
int strv_env_assign(char ***l, const char *key, const char *value);
+int strv_env_assignf(char ***l, const char *key, const char *valuef, ...) _printf_(3, 4);
int _strv_env_assign_many(char ***l, ...) _sentinel_;
#define strv_env_assign_many(l, ...) _strv_env_assign_many(l, __VA_ARGS__, NULL)
-char *strv_env_get_n(char **l, const char *name, size_t k, unsigned flags) _pure_;
-char *strv_env_get(char **x, const char *n) _pure_;
+char* strv_env_get_n(char * const *l, const char *name, size_t k, ReplaceEnvFlags flags);
+static inline char* strv_env_get(char * const *x, const char *n) {
+ return strv_env_get_n(x, n, SIZE_MAX, 0);
+}
+
char *strv_env_pairs_get(char **l, const char *name) _pure_;
int getenv_bool(const char *p);
@@ -74,3 +78,7 @@ int setenv_systemd_exec_pid(bool update_only);
int getenv_path_list(const char *name, char ***ret_paths);
int getenv_steal_erase(const char *name, char **ret);
+
+int set_full_environment(char **env);
+
+int setenvf(const char *name, bool overwrite, const char *valuef, ...) _printf_(3,4);
diff --git a/src/libnm-systemd-shared/src/basic/errno-util.h b/src/libnm-systemd-shared/src/basic/errno-util.h
index 091f99c590..27804e6382 100644
--- a/src/libnm-systemd-shared/src/basic/errno-util.h
+++ b/src/libnm-systemd-shared/src/basic/errno-util.h
@@ -1,6 +1,7 @@
/* SPDX-License-Identifier: LGPL-2.1-or-later */
#pragma once
+#include <inttypes.h>
#include <stdlib.h>
#include <string.h>
@@ -73,6 +74,16 @@ static inline int RET_NERRNO(int ret) {
return ret;
}
+/* Collect possible errors in <acc>, so that the first error can be returned.
+ * Returns (possibly updated) <acc>. */
+#define RET_GATHER(acc, err) \
+ ({ \
+ int *__a = &(acc), __e = (err); \
+ if (*__a >= 0 && __e < 0) \
+ *__a = __e; \
+ *__a; \
+ })
+
static inline int errno_or_else(int fallback) {
/* To be used when invoking library calls where errno handling is not defined clearly: we return
* errno if it is set, and the specified error otherwise. The idea is that the caller initializes
@@ -84,12 +95,23 @@ static inline int errno_or_else(int fallback) {
return -abs(fallback);
}
+/* abs(3) says: Trying to take the absolute value of the most negative integer is not defined. */
+#define _DEFINE_ABS_WRAPPER(name) \
+ static inline bool ERRNO_IS_##name(intmax_t r) { \
+ if (r == INTMAX_MIN) \
+ return false; \
+ return ERRNO_IS_NEG_##name(-imaxabs(r)); \
+ }
+
+assert_cc(INT_MAX <= INTMAX_MAX);
+
/* For send()/recv() or read()/write(). */
-static inline bool ERRNO_IS_TRANSIENT(int r) {
- return IN_SET(abs(r),
- EAGAIN,
- EINTR);
+static inline bool ERRNO_IS_NEG_TRANSIENT(intmax_t r) {
+ return IN_SET(r,
+ -EAGAIN,
+ -EINTR);
}
+_DEFINE_ABS_WRAPPER(TRANSIENT);
/* Hint #1: ENETUNREACH happens if we try to connect to "non-existing" special IP addresses, such as ::5.
*
@@ -98,79 +120,87 @@ static inline bool ERRNO_IS_TRANSIENT(int r) {
*
* Hint #3: When asynchronous connect() on TCP fails because the host never acknowledges a single packet,
* kernel tells us that with ETIMEDOUT, see tcp(7). */
-static inline bool ERRNO_IS_DISCONNECT(int r) {
- return IN_SET(abs(r),
- ECONNABORTED,
- ECONNREFUSED,
- ECONNRESET,
- EHOSTDOWN,
- EHOSTUNREACH,
- ENETDOWN,
- ENETRESET,
- ENETUNREACH,
- ENONET,
- ENOPROTOOPT,
- ENOTCONN,
- EPIPE,
- EPROTO,
- ESHUTDOWN,
- ETIMEDOUT);
+static inline bool ERRNO_IS_NEG_DISCONNECT(intmax_t r) {
+ return IN_SET(r,
+ -ECONNABORTED,
+ -ECONNREFUSED,
+ -ECONNRESET,
+ -EHOSTDOWN,
+ -EHOSTUNREACH,
+ -ENETDOWN,
+ -ENETRESET,
+ -ENETUNREACH,
+ -ENONET,
+ -ENOPROTOOPT,
+ -ENOTCONN,
+ -EPIPE,
+ -EPROTO,
+ -ESHUTDOWN,
+ -ETIMEDOUT);
}
+_DEFINE_ABS_WRAPPER(DISCONNECT);
/* Transient errors we might get on accept() that we should ignore. As per error handling comment in
* the accept(2) man page. */
-static inline bool ERRNO_IS_ACCEPT_AGAIN(int r) {
- return ERRNO_IS_DISCONNECT(r) ||
- ERRNO_IS_TRANSIENT(r) ||
- abs(r) == EOPNOTSUPP;
+static inline bool ERRNO_IS_NEG_ACCEPT_AGAIN(intmax_t r) {
+ return ERRNO_IS_NEG_DISCONNECT(r) ||
+ ERRNO_IS_NEG_TRANSIENT(r) ||
+ r == -EOPNOTSUPP;
}
+_DEFINE_ABS_WRAPPER(ACCEPT_AGAIN);
/* Resource exhaustion, could be our fault or general system trouble */
-static inline bool ERRNO_IS_RESOURCE(int r) {
- return IN_SET(abs(r),
- EMFILE,
- ENFILE,
- ENOMEM);
+static inline bool ERRNO_IS_NEG_RESOURCE(intmax_t r) {
+ return IN_SET(r,
+ -EMFILE,
+ -ENFILE,
+ -ENOMEM);
}
+_DEFINE_ABS_WRAPPER(RESOURCE);
/* Seven different errors for "operation/system call/ioctl/socket feature not supported" */
-static inline bool ERRNO_IS_NOT_SUPPORTED(int r) {
- return IN_SET(abs(r),
- EOPNOTSUPP,
- ENOTTY,
- ENOSYS,
- EAFNOSUPPORT,
- EPFNOSUPPORT,
- EPROTONOSUPPORT,
- ESOCKTNOSUPPORT);
+static inline bool ERRNO_IS_NEG_NOT_SUPPORTED(intmax_t r) {
+ return IN_SET(r,
+ -EOPNOTSUPP,
+ -ENOTTY,
+ -ENOSYS,
+ -EAFNOSUPPORT,
+ -EPFNOSUPPORT,
+ -EPROTONOSUPPORT,
+ -ESOCKTNOSUPPORT);
}
+_DEFINE_ABS_WRAPPER(NOT_SUPPORTED);
/* Two different errors for access problems */
-static inline bool ERRNO_IS_PRIVILEGE(int r) {
- return IN_SET(abs(r),
- EACCES,
- EPERM);
+static inline bool ERRNO_IS_NEG_PRIVILEGE(intmax_t r) {
+ return IN_SET(r,
+ -EACCES,
+ -EPERM);
}
+_DEFINE_ABS_WRAPPER(PRIVILEGE);
/* Three different errors for "not enough disk space" */
-static inline bool ERRNO_IS_DISK_SPACE(int r) {
- return IN_SET(abs(r),
- ENOSPC,
- EDQUOT,
- EFBIG);
+static inline bool ERRNO_IS_NEG_DISK_SPACE(intmax_t r) {
+ return IN_SET(r,
+ -ENOSPC,
+ -EDQUOT,
+ -EFBIG);
}
+_DEFINE_ABS_WRAPPER(DISK_SPACE);
/* Three different errors for "this device does not quite exist" */
-static inline bool ERRNO_IS_DEVICE_ABSENT(int r) {
- return IN_SET(abs(r),
- ENODEV,
- ENXIO,
- ENOENT);
+static inline bool ERRNO_IS_NEG_DEVICE_ABSENT(intmax_t r) {
+ return IN_SET(r,
+ -ENODEV,
+ -ENXIO,
+ -ENOENT);
}
+_DEFINE_ABS_WRAPPER(DEVICE_ABSENT);
/* Quite often we want to handle cases where the backing FS doesn't support extended attributes at all and
* where it simply doesn't have the requested xattr the same way */
-static inline bool ERRNO_IS_XATTR_ABSENT(int r) {
- return abs(r) == ENODATA ||
- ERRNO_IS_NOT_SUPPORTED(r);
+static inline bool ERRNO_IS_NEG_XATTR_ABSENT(intmax_t r) {
+ return r == -ENODATA ||
+ ERRNO_IS_NEG_NOT_SUPPORTED(r);
}
+_DEFINE_ABS_WRAPPER(XATTR_ABSENT);
diff --git a/src/libnm-systemd-shared/src/basic/escape.c b/src/libnm-systemd-shared/src/basic/escape.c
index 6d2c1d4d66..29f8b9cd1b 100644
--- a/src/libnm-systemd-shared/src/basic/escape.c
+++ b/src/libnm-systemd-shared/src/basic/escape.c
@@ -184,7 +184,7 @@ int cunescape_one(const char *p, size_t length, char32_t *ret, bool *eight_bit,
}
case 'u': {
- /* C++11 style 16bit unicode */
+ /* C++11 style 16-bit unicode */
int a[4];
size_t i;
@@ -211,7 +211,7 @@ int cunescape_one(const char *p, size_t length, char32_t *ret, bool *eight_bit,
}
case 'U': {
- /* C++11 style 32bit unicode */
+ /* C++11 style 32-bit unicode */
int a[8];
size_t i;
@@ -474,6 +474,33 @@ char* octescape(const char *s, size_t len) {
return buf;
}
+char* decescape(const char *s, const char *bad, size_t len) {
+ char *buf, *t;
+
+ /* Escapes all chars in bad, in addition to \ and " chars, in \nnn decimal style escaping. */
+
+ assert(s || len == 0);
+
+ t = buf = new(char, len * 4 + 1);
+ if (!buf)
+ return NULL;
+
+ for (size_t i = 0; i < len; i++) {
+ uint8_t u = (uint8_t) s[i];
+
+ if (u < ' ' || u >= 127 || IN_SET(u, '\\', '"') || strchr(bad, u)) {
+ *(t++) = '\\';
+ *(t++) = '0' + (u / 100);
+ *(t++) = '0' + ((u / 10) % 10);
+ *(t++) = '0' + (u % 10);
+ } else
+ *(t++) = u;
+ }
+
+ *t = 0;
+ return buf;
+}
+
static char* strcpy_backslash_escaped(char *t, const char *s, const char *bad) {
assert(bad);
assert(t);
diff --git a/src/libnm-systemd-shared/src/basic/escape.h b/src/libnm-systemd-shared/src/basic/escape.h
index 318da6f220..65caf0dbcf 100644
--- a/src/libnm-systemd-shared/src/basic/escape.h
+++ b/src/libnm-systemd-shared/src/basic/escape.h
@@ -65,6 +65,7 @@ static inline char* xescape(const char *s, const char *bad) {
return xescape_full(s, bad, SIZE_MAX, 0);
}
char* octescape(const char *s, size_t len);
+char* decescape(const char *s, const char *bad, size_t len);
char* escape_non_printable_full(const char *str, size_t console_width, XEscapeFlags flags);
char* shell_escape(const char *s, const char *bad);
diff --git a/src/libnm-systemd-shared/src/basic/ether-addr-util.c b/src/libnm-systemd-shared/src/basic/ether-addr-util.c
index 7984ddf4ad..1eb2e70065 100644
--- a/src/libnm-systemd-shared/src/basic/ether-addr-util.c
+++ b/src/libnm-systemd-shared/src/basic/ether-addr-util.c
@@ -61,8 +61,8 @@ void hw_addr_hash_func(const struct hw_addr_data *p, struct siphash *state) {
assert(p);
assert(state);
- siphash24_compress(&p->length, sizeof(p->length), state);
- siphash24_compress(p->bytes, p->length, state);
+ siphash24_compress_typesafe(p->length, state);
+ siphash24_compress_safe(p->bytes, p->length, state);
}
DEFINE_HASH_OPS(hw_addr_hash_ops, struct hw_addr_data, hw_addr_hash_func, hw_addr_compare);
@@ -108,7 +108,7 @@ int ether_addr_compare(const struct ether_addr *a, const struct ether_addr *b) {
}
static void ether_addr_hash_func(const struct ether_addr *p, struct siphash *state) {
- siphash24_compress(p, sizeof(struct ether_addr), state);
+ siphash24_compress_typesafe(*p, state);
}
DEFINE_HASH_OPS(ether_addr_hash_ops, struct ether_addr, ether_addr_hash_func, ether_addr_compare);
@@ -272,3 +272,11 @@ int parse_ether_addr(const char *s, struct ether_addr *ret) {
*ret = a.ether;
return 0;
}
+
+void ether_addr_mark_random(struct ether_addr *addr) {
+ assert(addr);
+
+ /* see eth_random_addr in the kernel */
+ addr->ether_addr_octet[0] &= 0xfe; /* clear multicast bit */
+ addr->ether_addr_octet[0] |= 0x02; /* set local assignment bit (IEEE802) */
+}
diff --git a/src/libnm-systemd-shared/src/basic/ether-addr-util.h b/src/libnm-systemd-shared/src/basic/ether-addr-util.h
index 83ed77d634..187e4ef583 100644
--- a/src/libnm-systemd-shared/src/basic/ether-addr-util.h
+++ b/src/libnm-systemd-shared/src/basic/ether-addr-util.h
@@ -113,3 +113,5 @@ static inline bool ether_addr_is_global(const struct ether_addr *addr) {
extern const struct hash_ops ether_addr_hash_ops;
extern const struct hash_ops ether_addr_hash_ops_free;
+
+void ether_addr_mark_random(struct ether_addr *addr);
diff --git a/src/libnm-systemd-shared/src/basic/extract-word.c b/src/libnm-systemd-shared/src/basic/extract-word.c
index 6781fb5560..2910a4cb60 100644
--- a/src/libnm-systemd-shared/src/basic/extract-word.c
+++ b/src/libnm-systemd-shared/src/basic/extract-word.c
@@ -275,11 +275,7 @@ int extract_many_words(const char **p, const char *separators, unsigned flags, .
r = extract_first_word(p, &l[c], separators, flags);
if (r < 0) {
- int j;
-
- for (j = 0; j < c; j++)
- free(l[j]);
-
+ free_many_charp(l, c);
return r;
}
diff --git a/src/libnm-systemd-shared/src/basic/fd-util.c b/src/libnm-systemd-shared/src/basic/fd-util.c
index a0e2f4eb8c..afdc05f2ab 100644
--- a/src/libnm-systemd-shared/src/basic/fd-util.c
+++ b/src/libnm-systemd-shared/src/basic/fd-util.c
@@ -94,11 +94,25 @@ void safe_close_pair(int p[static 2]) {
p[1] = safe_close(p[1]);
}
-void close_many(const int fds[], size_t n_fd) {
- assert(fds || n_fd <= 0);
+void close_many(const int fds[], size_t n_fds) {
+ assert(fds || n_fds == 0);
- for (size_t i = 0; i < n_fd; i++)
- safe_close(fds[i]);
+ FOREACH_ARRAY(fd, fds, n_fds)
+ safe_close(*fd);
+}
+
+void close_many_unset(int fds[], size_t n_fds) {
+ assert(fds || n_fds == 0);
+
+ FOREACH_ARRAY(fd, fds, n_fds)
+ *fd = safe_close(*fd);
+}
+
+void close_many_and_free(int *fds, size_t n_fds) {
+ assert(fds || n_fds == 0);
+
+ close_many(fds, n_fds);
+ free(fds);
}
int fclose_nointr(FILE *f) {
@@ -158,6 +172,19 @@ int fd_nonblock(int fd, bool nonblock) {
return RET_NERRNO(fcntl(fd, F_SETFL, nflags));
}
+int stdio_disable_nonblock(void) {
+ int ret = 0;
+
+ /* stdin/stdout/stderr really should have O_NONBLOCK, which would confuse apps if left on, as
+ * write()s might unexpectedly fail with EAGAIN. */
+
+ RET_GATHER(ret, fd_nonblock(STDIN_FILENO, false));
+ RET_GATHER(ret, fd_nonblock(STDOUT_FILENO, false));
+ RET_GATHER(ret, fd_nonblock(STDERR_FILENO, false));
+
+ return ret;
+}
+
int fd_cloexec(int fd, bool cloexec) {
int flags, nflags;
@@ -176,32 +203,32 @@ int fd_cloexec(int fd, bool cloexec) {
#if 0 /* NM_IGNORED */
int fd_cloexec_many(const int fds[], size_t n_fds, bool cloexec) {
- int ret = 0, r;
+ int r = 0;
- assert(n_fds == 0 || fds);
+ assert(fds || n_fds == 0);
- for (size_t i = 0; i < n_fds; i++) {
- if (fds[i] < 0) /* Skip gracefully over already invalidated fds */
+ FOREACH_ARRAY(fd, fds, n_fds) {
+ if (*fd < 0) /* Skip gracefully over already invalidated fds */
continue;
- r = fd_cloexec(fds[i], cloexec);
- if (r < 0 && ret >= 0) /* Continue going, but return first error */
- ret = r;
- else
- ret = 1; /* report if we did anything */
+ RET_GATHER(r, fd_cloexec(*fd, cloexec));
+
+ if (r >= 0)
+ r = 1; /* report if we did anything */
}
- return ret;
+ return r;
}
-_pure_ static bool fd_in_set(int fd, const int fdset[], size_t n_fdset) {
- assert(n_fdset == 0 || fdset);
+static bool fd_in_set(int fd, const int fds[], size_t n_fds) {
+ assert(fd >= 0);
+ assert(fds || n_fds == 0);
- for (size_t i = 0; i < n_fdset; i++) {
- if (fdset[i] < 0)
+ FOREACH_ARRAY(i, fds, n_fds) {
+ if (*i < 0)
continue;
- if (fdset[i] == fd)
+ if (*i == fd)
return true;
}
@@ -232,7 +259,7 @@ int get_max_fd(void) {
static int close_all_fds_frugal(const int except[], size_t n_except) {
int max_fd, r = 0;
- assert(n_except == 0 || except);
+ assert(except || n_except == 0);
/* This is the inner fallback core of close_all_fds(). This never calls malloc() or opendir() or so
* and hence is safe to be called in signal handler context. Most users should call close_all_fds(),
@@ -247,8 +274,7 @@ static int close_all_fds_frugal(const int except[], size_t n_except) {
* spin the CPU for a long time. */
if (max_fd > MAX_FD_LOOP_LIMIT)
return log_debug_errno(SYNTHETIC_ERRNO(EPERM),
- "Refusing to loop over %d potential fds.",
- max_fd);
+ "Refusing to loop over %d potential fds.", max_fd);
for (int fd = 3; fd >= 0; fd = fd < max_fd ? fd + 1 : -EBADF) {
int q;
@@ -257,8 +283,8 @@ static int close_all_fds_frugal(const int except[], size_t n_except) {
continue;
q = close_nointr(fd);
- if (q < 0 && q != -EBADF && r >= 0)
- r = q;
+ if (q != -EBADF)
+ RET_GATHER(r, q);
}
return r;
@@ -589,7 +615,7 @@ int move_fd(int from, int to, int cloexec) {
if (fl < 0)
return -errno;
- cloexec = !!(fl & FD_CLOEXEC);
+ cloexec = FLAGS_SET(fl, FD_CLOEXEC);
}
r = dup3(from, to, cloexec ? O_CLOEXEC : 0);
@@ -647,7 +673,7 @@ int rearrange_stdio(int original_input_fd, int original_output_fd, int original_
original_output_fd,
original_error_fd },
null_fd = -EBADF, /* If we open /dev/null, we store the fd to it here */
- copy_fd[3] = { -EBADF, -EBADF, -EBADF }, /* This contains all fds we duplicate here
+ copy_fd[3] = EBADF_TRIPLET, /* This contains all fds we duplicate here
* temporarily, and hence need to close at the end. */
r;
bool null_readable, null_writable;
@@ -745,8 +771,7 @@ finish:
safe_close_above_stdio(original_error_fd);
/* Close the copies we moved > 2 */
- for (int i = 0; i < 3; i++)
- safe_close(copy_fd[i]);
+ close_many(copy_fd, 3);
/* Close our null fd, if it's > 2 */
safe_close_above_stdio(null_fd);
@@ -756,9 +781,10 @@ finish:
#endif /* NM_IGNORED */
int fd_reopen(int fd, int flags) {
- int new_fd, r;
+ int r;
assert(fd >= 0 || fd == AT_FDCWD);
+ assert(!FLAGS_SET(flags, O_CREAT));
/* Reopens the specified fd with new flags. This is useful for convert an O_PATH fd into a regular one, or to
* turn O_RDWR fds into O_RDONLY fds.
@@ -782,19 +808,12 @@ int fd_reopen(int fd, int flags) {
* the same way as the non-O_DIRECTORY case. */
return -ELOOP;
- if (FLAGS_SET(flags, O_DIRECTORY) || fd == AT_FDCWD) {
+ if (FLAGS_SET(flags, O_DIRECTORY) || fd == AT_FDCWD)
/* If we shall reopen the fd as directory we can just go via "." and thus bypass the whole
* magic /proc/ directory, and make ourselves independent of that being mounted. */
- new_fd = openat(fd, ".", flags | O_DIRECTORY);
- if (new_fd < 0)
- return -errno;
-
- return new_fd;
- }
+ return RET_NERRNO(openat(fd, ".", flags | O_DIRECTORY));
- assert(fd >= 0);
-
- new_fd = open(FORMAT_PROC_FD_PATH(fd), flags);
+ int new_fd = open(FORMAT_PROC_FD_PATH(fd), flags);
if (new_fd < 0) {
if (errno != ENOENT)
return -errno;
@@ -811,7 +830,6 @@ int fd_reopen(int fd, int flags) {
return new_fd;
}
-#if 0 /* NM_IGNORED */
int fd_reopen_condition(
int fd,
int flags,
@@ -821,6 +839,7 @@ int fd_reopen_condition(
int r, new_fd;
assert(fd >= 0);
+ assert(!FLAGS_SET(flags, O_CREAT));
/* Invokes fd_reopen(fd, flags), but only if the existing F_GETFL flags don't match the specified
* flags (masked by the specified mask). This is useful for converting O_PATH fds into real fds if
@@ -843,6 +862,7 @@ int fd_reopen_condition(
return new_fd;
}
+#if 0 /* NM_IGNORED */
int fd_is_opath(int fd) {
int r;
@@ -901,75 +921,86 @@ int fd_get_diskseq(int fd, uint64_t *ret) {
}
int path_is_root_at(int dir_fd, const char *path) {
- STRUCT_NEW_STATX_DEFINE(st);
- STRUCT_NEW_STATX_DEFINE(pst);
- _cleanup_close_ int fd = -EBADF;
- int r;
+ _cleanup_close_ int fd = -EBADF, pfd = -EBADF;
assert(dir_fd >= 0 || dir_fd == AT_FDCWD);
if (!isempty(path)) {
- fd = openat(dir_fd, path, O_PATH|O_CLOEXEC);
+ fd = openat(dir_fd, path, O_PATH|O_DIRECTORY|O_CLOEXEC);
if (fd < 0)
- return -errno;
+ return errno == ENOTDIR ? false : -errno;
dir_fd = fd;
}
- r = statx_fallback(dir_fd, ".", 0, STATX_TYPE|STATX_INO|STATX_MNT_ID, &st.sx);
- if (r == -ENOTDIR)
- return false;
+ pfd = openat(dir_fd, "..", O_PATH|O_DIRECTORY|O_CLOEXEC);
+ if (pfd < 0)
+ return errno == ENOTDIR ? false : -errno;
+
+ /* Even if the parent directory has the same inode, the fd may not point to the root directory "/",
+ * and we also need to check that the mount ids are the same. Otherwise, a construct like the
+ * following could be used to trick us:
+ *
+ * $ mkdir /tmp/x /tmp/x/y
+ * $ mount --bind /tmp/x /tmp/x/y
+ */
+
+ return fds_are_same_mount(dir_fd, pfd);
+}
+
+int fds_are_same_mount(int fd1, int fd2) {
+ STRUCT_NEW_STATX_DEFINE(st1);
+ STRUCT_NEW_STATX_DEFINE(st2);
+ int r;
+
+ assert(fd1 >= 0);
+ assert(fd2 >= 0);
+
+ r = statx_fallback(fd1, "", AT_EMPTY_PATH, STATX_TYPE|STATX_INO|STATX_MNT_ID, &st1.sx);
if (r < 0)
return r;
- r = statx_fallback(dir_fd, "..", 0, STATX_TYPE|STATX_INO|STATX_MNT_ID, &pst.sx);
+ r = statx_fallback(fd2, "", AT_EMPTY_PATH, STATX_TYPE|STATX_INO|STATX_MNT_ID, &st2.sx);
if (r < 0)
return r;
/* First, compare inode. If these are different, the fd does not point to the root directory "/". */
- if (!statx_inode_same(&st.sx, &pst.sx))
+ if (!statx_inode_same(&st1.sx, &st2.sx))
return false;
- /* Even if the parent directory has the same inode, the fd may not point to the root directory "/",
- * and we also need to check that the mount ids are the same. Otherwise, a construct like the
- * following could be used to trick us:
- *
- * $ mkdir /tmp/x /tmp/x/y
- * $ mount --bind /tmp/x /tmp/x/y
- *
- * Note, statx() does not provide the mount ID and path_get_mnt_id_at() does not work when an old
- * kernel is used without /proc mounted. In that case, let's assume that we do not have such spurious
- * mount points in an early boot stage, and silently skip the following check. */
+ /* Note, statx() does not provide the mount ID and path_get_mnt_id_at() does not work when an old
+ * kernel is used. In that case, let's assume that we do not have such spurious mount points in an
+ * early boot stage, and silently skip the following check. */
- if (!FLAGS_SET(st.nsx.stx_mask, STATX_MNT_ID)) {
+ if (!FLAGS_SET(st1.nsx.stx_mask, STATX_MNT_ID)) {
int mntid;
- r = path_get_mnt_id_at(dir_fd, "", &mntid);
- if (r == -ENOSYS)
+ r = path_get_mnt_id_at_fallback(fd1, "", &mntid);
+ if (ERRNO_IS_NEG_NOT_SUPPORTED(r))
return true; /* skip the mount ID check */
if (r < 0)
return r;
assert(mntid >= 0);
- st.nsx.stx_mnt_id = mntid;
- st.nsx.stx_mask |= STATX_MNT_ID;
+ st1.nsx.stx_mnt_id = mntid;
+ st1.nsx.stx_mask |= STATX_MNT_ID;
}
- if (!FLAGS_SET(pst.nsx.stx_mask, STATX_MNT_ID)) {
+ if (!FLAGS_SET(st2.nsx.stx_mask, STATX_MNT_ID)) {
int mntid;
- r = path_get_mnt_id_at(dir_fd, "..", &mntid);
- if (r == -ENOSYS)
+ r = path_get_mnt_id_at_fallback(fd2, "", &mntid);
+ if (ERRNO_IS_NEG_NOT_SUPPORTED(r))
return true; /* skip the mount ID check */
if (r < 0)
return r;
assert(mntid >= 0);
- pst.nsx.stx_mnt_id = mntid;
- pst.nsx.stx_mask |= STATX_MNT_ID;
+ st2.nsx.stx_mnt_id = mntid;
+ st2.nsx.stx_mask |= STATX_MNT_ID;
}
- return statx_mount_same(&st.nsx, &pst.nsx);
+ return statx_mount_same(&st1.nsx, &st2.nsx);
}
const char *accmode_to_string(int flags) {
@@ -984,4 +1015,12 @@ const char *accmode_to_string(int flags) {
return NULL;
}
}
+
+char *format_proc_pid_fd_path(char buf[static PROC_PID_FD_PATH_MAX], pid_t pid, int fd) {
+ assert(buf);
+ assert(fd >= 0);
+ assert(pid >= 0);
+ assert_se(snprintf_ok(buf, PROC_PID_FD_PATH_MAX, "/proc/" PID_FMT "/fd/%i", pid == 0 ? getpid_cached() : pid, fd));
+ return buf;
+}
#endif /* NM_IGNORED */
diff --git a/src/libnm-systemd-shared/src/basic/fd-util.h b/src/libnm-systemd-shared/src/basic/fd-util.h
index c870a1b899..183266513a 100644
--- a/src/libnm-systemd-shared/src/basic/fd-util.h
+++ b/src/libnm-systemd-shared/src/basic/fd-util.h
@@ -16,7 +16,10 @@
/* Make sure we can distinguish fd 0 and NULL */
#define FD_TO_PTR(fd) INT_TO_PTR((fd)+1)
#define PTR_TO_FD(p) (PTR_TO_INT(p)-1)
-#define PIPE_EBADF { -EBADF, -EBADF }
+
+/* Useful helpers for initializing pipe(), socketpair() or stdio fd arrays */
+#define EBADF_PAIR { -EBADF, -EBADF }
+#define EBADF_TRIPLET { -EBADF, -EBADF, -EBADF }
int close_nointr(int fd);
int safe_close(int fd);
@@ -29,7 +32,9 @@ static inline int safe_close_above_stdio(int fd) {
return safe_close(fd);
}
-void close_many(const int fds[], size_t n_fd);
+void close_many(const int fds[], size_t n_fds);
+void close_many_unset(int fds[], size_t n_fds);
+void close_many_and_free(int *fds, size_t n_fds);
int fclose_nointr(FILE *f);
FILE* safe_fclose(FILE *f);
@@ -47,6 +52,11 @@ static inline void fclosep(FILE **f) {
safe_fclose(*f);
}
+static inline void* close_fd_ptr(void *p) {
+ safe_close(PTR_TO_FD(p));
+ return NULL;
+}
+
DEFINE_TRIVIAL_CLEANUP_FUNC_FULL(FILE*, pclose, NULL);
DEFINE_TRIVIAL_CLEANUP_FUNC_FULL(DIR*, closedir, NULL);
@@ -57,6 +67,8 @@ DEFINE_TRIVIAL_CLEANUP_FUNC_FULL(DIR*, closedir, NULL);
#define _cleanup_close_pair_ _cleanup_(close_pairp)
int fd_nonblock(int fd, bool nonblock);
+int stdio_disable_nonblock(void);
+
int fd_cloexec(int fd, bool cloexec);
int fd_cloexec_many(const int fds[], size_t n_fds, bool cloexec);
@@ -102,6 +114,9 @@ int read_nr_open(void);
int fd_get_diskseq(int fd, uint64_t *ret);
int path_is_root_at(int dir_fd, const char *path);
+static inline int path_is_root(const char *path) {
+ return path_is_root_at(AT_FDCWD, path);
+}
static inline int dir_fd_is_root(int dir_fd) {
return path_is_root_at(dir_fd, NULL);
}
@@ -109,6 +124,8 @@ static inline int dir_fd_is_root_or_cwd(int dir_fd) {
return dir_fd == AT_FDCWD ? true : path_is_root_at(dir_fd, NULL);
}
+int fds_are_same_mount(int fd1, int fd2);
+
/* The maximum length a buffer for a /proc/self/fd/<fd> path needs */
#define PROC_FD_PATH_MAX \
(STRLEN("/proc/self/fd/") + DECIMAL_STR_MAX(int))
@@ -123,6 +140,16 @@ static inline char *format_proc_fd_path(char buf[static PROC_FD_PATH_MAX], int f
#define FORMAT_PROC_FD_PATH(fd) \
format_proc_fd_path((char[PROC_FD_PATH_MAX]) {}, (fd))
+/* The maximum length a buffer for a /proc/<pid>/fd/<fd> path needs */
+#define PROC_PID_FD_PATH_MAX \
+ (STRLEN("/proc//fd/") + DECIMAL_STR_MAX(pid_t) + DECIMAL_STR_MAX(int))
+
+char *format_proc_pid_fd_path(char buf[static PROC_PID_FD_PATH_MAX], pid_t pid, int fd);
+
+/* Kinda the same as FORMAT_PROC_FD_PATH(), but goes by PID rather than "self" symlink */
+#define FORMAT_PROC_PID_FD_PATH(pid, fd) \
+ format_proc_pid_fd_path((char[PROC_PID_FD_PATH_MAX]) {}, (pid), (fd))
+
const char *accmode_to_string(int flags);
/* Like ASSERT_PTR, but for fds */
diff --git a/src/libnm-systemd-shared/src/basic/fileio.c b/src/libnm-systemd-shared/src/basic/fileio.c
index 908a030911..7ab29816fe 100644
--- a/src/libnm-systemd-shared/src/basic/fileio.c
+++ b/src/libnm-systemd-shared/src/basic/fileio.c
@@ -30,10 +30,13 @@
#include "stdio-util.h"
#include "string-util.h"
#include "sync-util.h"
+#include "terminal-util.h"
#include "tmpfile-util.h"
/* The maximum size of the file we'll read in one go in read_full_file() (64M). */
#define READ_FULL_BYTES_MAX (64U*1024U*1024U - 1U)
+/* Used when a size is specified for read_full_file() with READ_FULL_FILE_UNBASE64 or _UNHEX */
+#define READ_FULL_FILE_ENCODED_STRING_AMPLIFICATION_BOUNDARY 3
/* The maximum size of virtual files (i.e. procfs, sysfs, and other virtual "API" files) we'll read in one go
* in read_virtual_file(). Note that this limit is different (and much lower) than the READ_FULL_BYTES_MAX
@@ -200,6 +203,19 @@ int write_string_stream_ts(
return 0;
}
+static mode_t write_string_file_flags_to_mode(WriteStringFileFlags flags) {
+
+ /* We support three different modes, that are the ones that really make sense for text files like this:
+ *
+ * → 0600 (i.e. root-only)
+ * → 0444 (i.e. read-only)
+ * → 0644 (i.e. writable for root, readable for everyone else)
+ */
+
+ return FLAGS_SET(flags, WRITE_STRING_FILE_MODE_0600) ? 0600 :
+ FLAGS_SET(flags, WRITE_STRING_FILE_MODE_0444) ? 0444 : 0644;
+}
+
static int write_string_file_atomic_at(
int dir_fd,
const char *fn,
@@ -225,7 +241,7 @@ static int write_string_file_atomic_at(
if (r < 0)
goto fail;
- r = fchmod_umask(fileno(f), FLAGS_SET(flags, WRITE_STRING_FILE_MODE_0600) ? 0600 : 0644);
+ r = fchmod_umask(fileno(f), write_string_file_flags_to_mode(flags));
if (r < 0)
goto fail;
@@ -288,7 +304,7 @@ int write_string_file_ts_at(
(FLAGS_SET(flags, WRITE_STRING_FILE_CREATE) ? O_CREAT : 0) |
(FLAGS_SET(flags, WRITE_STRING_FILE_TRUNCATE) ? O_TRUNC : 0) |
(FLAGS_SET(flags, WRITE_STRING_FILE_SUPPRESS_REDUNDANT_VIRTUAL) ? O_RDWR : O_WRONLY),
- (FLAGS_SET(flags, WRITE_STRING_FILE_MODE_0600) ? 0600 : 0666));
+ write_string_file_flags_to_mode(flags));
if (fd < 0) {
r = -errno;
goto fail;
@@ -576,7 +592,7 @@ int read_full_stream_full(
size_t *ret_size) {
_cleanup_free_ char *buf = NULL;
- size_t n, n_next = 0, l;
+ size_t n, n_next = 0, l, expected_decoded_size = size;
int fd, r;
assert(f);
@@ -587,6 +603,13 @@ int read_full_stream_full(
if (offset != UINT64_MAX && offset > LONG_MAX) /* fseek() can only deal with "long" offsets */
return -ERANGE;
+ if ((flags & (READ_FULL_FILE_UNBASE64 | READ_FULL_FILE_UNHEX)) != 0) {
+ if (size <= SIZE_MAX / READ_FULL_FILE_ENCODED_STRING_AMPLIFICATION_BOUNDARY)
+ size *= READ_FULL_FILE_ENCODED_STRING_AMPLIFICATION_BOUNDARY;
+ else
+ size = SIZE_MAX;
+ }
+
fd = fileno(f);
if (fd >= 0) { /* If the FILE* object is backed by an fd (as opposed to memory or such, see
* fmemopen()), let's optimize our buffering */
@@ -711,6 +734,11 @@ int read_full_stream_full(
explicit_bzero_safe(buf, n);
free_and_replace(buf, decoded);
n = l = decoded_size;
+
+ if (FLAGS_SET(flags, READ_FULL_FILE_FAIL_WHEN_LARGER) && l > expected_decoded_size) {
+ r = -E2BIG;
+ goto finalize;
+ }
}
if (!ret_size) {
@@ -1057,7 +1085,9 @@ int fdopen_independent(int fd, const char *mode, FILE **ret) {
if (mode_flags < 0)
return mode_flags;
- copy_fd = fd_reopen(fd, mode_flags);
+ /* Flags returned by fopen_mode_to_flags might contain O_CREAT, but it doesn't make sense for fd_reopen
+ * since we're working on an existing fd anyway. Let's drop it here to avoid triggering assertion. */
+ copy_fd = fd_reopen(fd, mode_flags & ~O_CREAT);
if (copy_fd < 0)
return copy_fd;
@@ -1069,123 +1099,171 @@ int fdopen_independent(int fd, const char *mode, FILE **ret) {
return 0;
}
-static int search_and_fopen_internal(
+static int search_and_open_internal(
const char *path,
- const char *mode,
+ int mode, /* if ret_fd is NULL this is an [FRWX]_OK mode for access(), otherwise an open mode for open() */
const char *root,
char **search,
- FILE **ret,
+ int *ret_fd,
char **ret_path) {
+ int r;
+
+ assert(!ret_fd || !FLAGS_SET(mode, O_CREAT)); /* We don't support O_CREAT for this */
assert(path);
- assert(mode);
- assert(ret);
+
+ if (path_is_absolute(path)) {
+ _cleanup_close_ int fd = -EBADF;
+
+ if (ret_fd)
+ /* We only specify 0777 here to appease static analyzers, it's never used since we
+ * don't support O_CREAT here */
+ r = fd = RET_NERRNO(open(path, mode, 0777));
+ else
+ r = RET_NERRNO(access(path, mode));
+ if (r < 0)
+ return r;
+
+ if (ret_path) {
+ r = path_simplify_alloc(path, ret_path);
+ if (r < 0)
+ return r;
+ }
+
+ if (ret_fd)
+ *ret_fd = TAKE_FD(fd);
+
+ return 0;
+ }
if (!path_strv_resolve_uniq(search, root))
return -ENOMEM;
STRV_FOREACH(i, search) {
+ _cleanup_close_ int fd = -EBADF;
_cleanup_free_ char *p = NULL;
- FILE *f;
p = path_join(root, *i, path);
if (!p)
return -ENOMEM;
- f = fopen(p, mode);
- if (f) {
+ if (ret_fd)
+ /* as above, 0777 is static analyzer appeasement */
+ r = fd = RET_NERRNO(open(p, mode, 0777));
+ else
+ r = RET_NERRNO(access(p, F_OK));
+ if (r >= 0) {
if (ret_path)
*ret_path = path_simplify(TAKE_PTR(p));
- *ret = f;
+ if (ret_fd)
+ *ret_fd = TAKE_FD(fd);
+
return 0;
}
-
- if (errno != ENOENT)
- return -errno;
+ if (r != -ENOENT)
+ return r;
}
return -ENOENT;
}
-int search_and_fopen(
- const char *filename,
- const char *mode,
+int search_and_open(
+ const char *path,
+ int mode,
const char *root,
- const char **search,
- FILE **ret,
+ char **search,
+ int *ret_fd,
char **ret_path) {
_cleanup_strv_free_ char **copy = NULL;
- assert(filename);
- assert(mode);
- assert(ret);
+ assert(path);
- if (path_is_absolute(filename)) {
- _cleanup_fclose_ FILE *f = NULL;
+ copy = strv_copy((char**) search);
+ if (!copy)
+ return -ENOMEM;
+
+ return search_and_open_internal(path, mode, root, copy, ret_fd, ret_path);
+}
+
+static int search_and_fopen_internal(
+ const char *path,
+ const char *mode,
+ const char *root,
+ char **search,
+ FILE **ret_file,
+ char **ret_path) {
+
+ _cleanup_free_ char *found_path = NULL;
+ _cleanup_close_ int fd = -EBADF;
+ int r;
+
+ assert(path);
+ assert(mode || !ret_file);
+
+ r = search_and_open(
+ path,
+ mode ? fopen_mode_to_flags(mode) : 0,
+ root,
+ search,
+ ret_file ? &fd : NULL,
+ ret_path ? &found_path : NULL);
+ if (r < 0)
+ return r;
- f = fopen(filename, mode);
+ if (ret_file) {
+ FILE *f = take_fdopen(&fd, mode);
if (!f)
return -errno;
- if (ret_path) {
- char *p;
+ *ret_file = f;
+ }
- p = strdup(filename);
- if (!p)
- return -ENOMEM;
+ if (ret_path)
+ *ret_path = TAKE_PTR(found_path);
- *ret_path = path_simplify(p);
- }
+ return 0;
+}
- *ret = TAKE_PTR(f);
- return 0;
- }
+int search_and_fopen(
+ const char *path,
+ const char *mode,
+ const char *root,
+ const char **search,
+ FILE **ret_file,
+ char **ret_path) {
+
+ _cleanup_strv_free_ char **copy = NULL;
+
+ assert(path);
+ assert(mode || !ret_file);
copy = strv_copy((char**) search);
if (!copy)
return -ENOMEM;
- return search_and_fopen_internal(filename, mode, root, copy, ret, ret_path);
+ return search_and_fopen_internal(path, mode, root, copy, ret_file, ret_path);
}
int search_and_fopen_nulstr(
- const char *filename,
+ const char *path,
const char *mode,
const char *root,
const char *search,
- FILE **ret,
+ FILE **ret_file,
char **ret_path) {
- _cleanup_strv_free_ char **s = NULL;
-
- if (path_is_absolute(filename)) {
- _cleanup_fclose_ FILE *f = NULL;
-
- f = fopen(filename, mode);
- if (!f)
- return -errno;
-
- if (ret_path) {
- char *p;
-
- p = strdup(filename);
- if (!p)
- return -ENOMEM;
-
- *ret_path = path_simplify(p);
- }
+ _cleanup_strv_free_ char **l = NULL;
- *ret = TAKE_PTR(f);
- return 0;
- }
+ assert(path);
+ assert(mode || !ret_file);
- s = strv_split_nulstr(search);
- if (!s)
+ l = strv_split_nulstr(search);
+ if (!l)
return -ENOMEM;
- return search_and_fopen_internal(filename, mode, root, s, ret, ret_path);
+ return search_and_fopen_internal(path, mode, root, l, ret_file, ret_path);
}
#endif /* NM_IGNORED */
@@ -1259,33 +1337,31 @@ int read_timestamp_file(const char *fn, usec_t *ret) {
}
#endif /* NM_IGNORED */
-int fputs_with_space(FILE *f, const char *s, const char *separator, bool *space) {
- int r;
-
+int fputs_with_separator(FILE *f, const char *s, const char *separator, bool *space) {
assert(s);
+ assert(space);
- /* Outputs the specified string with fputs(), but optionally prefixes it with a separator. The *space parameter
- * when specified shall initially point to a boolean variable initialized to false. It is set to true after the
- * first invocation. This call is supposed to be use in loops, where a separator shall be inserted between each
- * element, but not before the first one. */
+ /* Outputs the specified string with fputs(), but optionally prefixes it with a separator.
+ * The *space parameter when specified shall initially point to a boolean variable initialized
+ * to false. It is set to true after the first invocation. This call is supposed to be use in loops,
+ * where a separator shall be inserted between each element, but not before the first one. */
if (!f)
f = stdout;
- if (space) {
- if (!separator)
- separator = " ";
+ if (!separator)
+ separator = " ";
- if (*space) {
- r = fputs(separator, f);
- if (r < 0)
- return r;
- }
+ if (*space)
+ if (fputs(separator, f) < 0)
+ return -EIO;
- *space = true;
- }
+ *space = true;
+
+ if (fputs(s, f) < 0)
+ return -EIO;
- return fputs(s, f);
+ return 0;
}
#if 0 /* NM_IGNORED */
@@ -1406,7 +1482,7 @@ int read_line_full(FILE *f, size_t limit, ReadLineFlags flags, char **ret) {
* and don't call isatty() on an invalid fd */
flags |= READ_LINE_NOT_A_TTY;
else
- flags |= isatty(fd) ? READ_LINE_IS_A_TTY : READ_LINE_NOT_A_TTY;
+ flags |= isatty_safe(fd) ? READ_LINE_IS_A_TTY : READ_LINE_NOT_A_TTY;
}
if (FLAGS_SET(flags, READ_LINE_IS_A_TTY))
break;
@@ -1437,6 +1513,36 @@ int read_line_full(FILE *f, size_t limit, ReadLineFlags flags, char **ret) {
return (int) count;
}
+int read_stripped_line(FILE *f, size_t limit, char **ret) {
+ _cleanup_free_ char *s = NULL;
+ int r;
+
+ assert(f);
+
+ r = read_line(f, limit, ret ? &s : NULL);
+ if (r < 0)
+ return r;
+
+ if (ret) {
+ const char *p;
+
+ p = strstrip(s);
+ if (p == s)
+ *ret = TAKE_PTR(s);
+ else {
+ char *copy;
+
+ copy = strdup(p);
+ if (!copy)
+ return -ENOMEM;
+
+ *ret = copy;
+ }
+ }
+
+ return r;
+}
+
int safe_fgetc(FILE *f, char *ret) {
int k;
diff --git a/src/libnm-systemd-shared/src/basic/fileio.h b/src/libnm-systemd-shared/src/basic/fileio.h
index 769bf394fd..03c3f3ff28 100644
--- a/src/libnm-systemd-shared/src/basic/fileio.h
+++ b/src/libnm-systemd-shared/src/basic/fileio.h
@@ -26,7 +26,8 @@ typedef enum {
WRITE_STRING_FILE_NOFOLLOW = 1 << 8,
WRITE_STRING_FILE_MKDIR_0755 = 1 << 9,
WRITE_STRING_FILE_MODE_0600 = 1 << 10,
- WRITE_STRING_FILE_SUPPRESS_REDUNDANT_VIRTUAL = 1 << 11,
+ WRITE_STRING_FILE_MODE_0444 = 1 << 11,
+ WRITE_STRING_FILE_SUPPRESS_REDUNDANT_VIRTUAL = 1 << 12,
/* And before you wonder, why write_string_file_atomic_label_ts() is a separate function instead of just one
more flag here: it's about linking: we don't want to pull -lselinux into all users of write_string_file()
@@ -129,8 +130,12 @@ static inline int fopen_unlocked(const char *path, const char *mode, FILE **ret)
int fdopen_independent(int fd, const char *mode, FILE **ret);
-int search_and_fopen(const char *path, const char *mode, const char *root, const char **search, FILE **ret, char **ret_path);
-int search_and_fopen_nulstr(const char *path, const char *mode, const char *root, const char *search, FILE **ret, char **ret_path);
+int search_and_open(const char *path, int mode, const char *root, char **search, int *ret_fd, char **ret_path);
+static inline int search_and_access(const char *path, int mode, const char *root, char**search, char **ret_path) {
+ return search_and_open(path, mode, root, search, NULL, ret_path);
+}
+int search_and_fopen(const char *path, const char *mode, const char *root, const char **search, FILE **ret_file, char **ret_path);
+int search_and_fopen_nulstr(const char *path, const char *mode, const char *root, const char *search, FILE **ret_file, char **ret_path);
int fflush_and_check(FILE *f);
int fflush_sync_and_check(FILE *f);
@@ -138,7 +143,7 @@ int fflush_sync_and_check(FILE *f);
int write_timestamp_file_atomic(const char *fn, usec_t n);
int read_timestamp_file(const char *fn, usec_t *ret);
-int fputs_with_space(FILE *f, const char *s, const char *separator, bool *space);
+int fputs_with_separator(FILE *f, const char *s, const char *separator, bool *space);
typedef enum ReadLineFlags {
READ_LINE_ONLY_NUL = 1 << 0,
@@ -162,6 +167,8 @@ static inline int read_nul_string(FILE *f, size_t limit, char **ret) {
return read_line_full(f, limit, READ_LINE_ONLY_NUL, ret);
}
+int read_stripped_line(FILE *f, size_t limit, char **ret);
+
int safe_fgetc(FILE *f, char *ret);
int warn_file_is_world_accessible(const char *filename, struct stat *st, const char *unit, unsigned line);
diff --git a/src/libnm-systemd-shared/src/basic/fs-util.c b/src/libnm-systemd-shared/src/basic/fs-util.c
index 32fd849d34..8804c0c1a1 100644
--- a/src/libnm-systemd-shared/src/basic/fs-util.c
+++ b/src/libnm-systemd-shared/src/basic/fs-util.c
@@ -11,6 +11,7 @@
#include <unistd.h>
#include "alloc-util.h"
+#include "btrfs.h"
#include "dirent-util.h"
#include "fd-util.h"
#include "fileio.h"
@@ -292,8 +293,22 @@ int fchmod_umask(int fd, mode_t m) {
int fchmod_opath(int fd, mode_t m) {
/* This function operates also on fd that might have been opened with
- * O_PATH. Indeed fchmodat() doesn't have the AT_EMPTY_PATH flag like
- * fchownat() does. */
+ * O_PATH. The tool set we have is non-intuitive:
+ * - fchmod(2) only operates on open files (i. e., fds with an open file description);
+ * - fchmodat(2) does not have a flag arg like fchownat(2) does, so no way to pass AT_EMPTY_PATH;
+ * + it should not be confused with the libc fchmodat(3) interface, which adds 4th flag argument,
+ * but does not support AT_EMPTY_PATH (only supports AT_SYMLINK_NOFOLLOW);
+ * - fchmodat2(2) supports all the AT_* flags, but is still very recent.
+ *
+ * We try to use fchmodat2(), and, if it is not supported, resort
+ * to the /proc/self/fd dance. */
+
+ assert(fd >= 0);
+
+ if (fchmodat2(fd, "", m, AT_EMPTY_PATH) >= 0)
+ return 0;
+ if (!IN_SET(errno, ENOSYS, EPERM)) /* Some container managers block unknown syscalls with EPERM */
+ return -errno;
if (chmod(FORMAT_PROC_FD_PATH(fd), m) < 0) {
if (errno != ENOENT)
@@ -1108,6 +1123,16 @@ int xopenat(int dir_fd, const char *path, int open_flags, XOpenFlags xopen_flags
assert(dir_fd >= 0 || dir_fd == AT_FDCWD);
+ /* This is like openat(), but has a few tricks up its sleeves, extending behaviour:
+ *
+ * • O_DIRECTORY|O_CREAT is supported, which causes a directory to be created, and immediately
+ * opened. When used with the XO_SUBVOLUME flag this will even create a btrfs subvolume.
+ *
+ * • If O_CREAT is used with XO_LABEL, any created file will be immediately relabelled.
+ *
+ * • If the path is specified NULL or empty, behaves like fd_reopen().
+ */
+
if (isempty(path)) {
assert(!FLAGS_SET(open_flags, O_CREAT|O_EXCL));
return fd_reopen(dir_fd, open_flags & ~O_NOFOLLOW);
@@ -1120,7 +1145,10 @@ int xopenat(int dir_fd, const char *path, int open_flags, XOpenFlags xopen_flags
}
if (FLAGS_SET(open_flags, O_DIRECTORY|O_CREAT)) {
- r = RET_NERRNO(mkdirat(dir_fd, path, mode));
+ if (FLAGS_SET(xopen_flags, XO_SUBVOLUME))
+ r = btrfs_subvol_make_fallback(dir_fd, path, mode);
+ else
+ r = RET_NERRNO(mkdirat(dir_fd, path, mode));
if (r == -EEXIST) {
if (FLAGS_SET(open_flags, O_EXCL))
return -EEXIST;
@@ -1183,12 +1211,11 @@ int xopenat_lock(
int r;
assert(dir_fd >= 0 || dir_fd == AT_FDCWD);
- assert(path);
assert(IN_SET(operation & ~LOCK_NB, LOCK_EX, LOCK_SH));
/* POSIX/UNPOSIX locks don't work on directories (errno is set to -EBADF so let's return early with
* the same error here). */
- if (FLAGS_SET(open_flags, O_DIRECTORY) && locktype != LOCK_BSD)
+ if (FLAGS_SET(open_flags, O_DIRECTORY) && !IN_SET(locktype, LOCK_BSD, LOCK_NONE))
return -EBADF;
for (;;) {
diff --git a/src/libnm-systemd-shared/src/basic/fs-util.h b/src/libnm-systemd-shared/src/basic/fs-util.h
index a19836d138..1023ab73ca 100644
--- a/src/libnm-systemd-shared/src/basic/fs-util.h
+++ b/src/libnm-systemd-shared/src/basic/fs-util.h
@@ -133,7 +133,8 @@ int open_mkdir_at(int dirfd, const char *path, int flags, mode_t mode);
int openat_report_new(int dirfd, const char *pathname, int flags, mode_t mode, bool *ret_newly_created);
typedef enum XOpenFlags {
- XO_LABEL = 1 << 0,
+ XO_LABEL = 1 << 0,
+ XO_SUBVOLUME = 1 << 1,
} XOpenFlags;
int xopenat(int dir_fd, const char *path, int open_flags, XOpenFlags xopen_flags, mode_t mode);
diff --git a/src/libnm-systemd-shared/src/basic/glyph-util.c b/src/libnm-systemd-shared/src/basic/glyph-util.c
index 1ea8a645b2..1de60e105b 100644
--- a/src/libnm-systemd-shared/src/basic/glyph-util.c
+++ b/src/libnm-systemd-shared/src/basic/glyph-util.c
@@ -25,7 +25,7 @@ bool emoji_enabled(void) {
return cached_emoji_enabled;
}
-const char *special_glyph(SpecialGlyph code) {
+const char *special_glyph_full(SpecialGlyph code, bool force_utf) {
/* A list of a number of interesting unicode glyphs we can use to decorate our output. It's probably wise to be
* conservative here, and primarily stick to the glyphs defined in the eurlatgr font, so that display still
@@ -54,11 +54,12 @@ const char *special_glyph(SpecialGlyph code) {
[SPECIAL_GLYPH_CROSS_MARK] = "-",
[SPECIAL_GLYPH_LIGHT_SHADE] = "-",
[SPECIAL_GLYPH_DARK_SHADE] = "X",
+ [SPECIAL_GLYPH_FULL_BLOCK] = "#",
[SPECIAL_GLYPH_SIGMA] = "S",
- [SPECIAL_GLYPH_ARROW_LEFT] = "<-",
- [SPECIAL_GLYPH_ARROW_RIGHT] = "->",
[SPECIAL_GLYPH_ARROW_UP] = "^",
[SPECIAL_GLYPH_ARROW_DOWN] = "v",
+ [SPECIAL_GLYPH_ARROW_LEFT] = "<-",
+ [SPECIAL_GLYPH_ARROW_RIGHT] = "->",
[SPECIAL_GLYPH_ELLIPSIS] = "...",
[SPECIAL_GLYPH_EXTERNAL_LINK] = "[LNK]",
[SPECIAL_GLYPH_ECSTATIC_SMILEY] = ":-]",
@@ -73,6 +74,7 @@ const char *special_glyph(SpecialGlyph code) {
[SPECIAL_GLYPH_RECYCLING] = "~",
[SPECIAL_GLYPH_DOWNLOAD] = "\\",
[SPECIAL_GLYPH_SPARKLES] = "*",
+ [SPECIAL_GLYPH_LOW_BATTERY] = "!",
[SPECIAL_GLYPH_WARNING_SIGN] = "!",
},
@@ -98,6 +100,7 @@ const char *special_glyph(SpecialGlyph code) {
[SPECIAL_GLYPH_CROSS_MARK] = u8"✗", /* actually called: BALLOT X */
[SPECIAL_GLYPH_LIGHT_SHADE] = u8"░",
[SPECIAL_GLYPH_DARK_SHADE] = u8"▒",
+ [SPECIAL_GLYPH_FULL_BLOCK] = u8"█",
[SPECIAL_GLYPH_SIGMA] = u8"Σ",
[SPECIAL_GLYPH_ARROW_UP] = u8"↑", /* actually called: UPWARDS ARROW */
[SPECIAL_GLYPH_ARROW_DOWN] = u8"↓", /* actually called: DOWNWARDS ARROW */
@@ -131,7 +134,10 @@ const char *special_glyph(SpecialGlyph code) {
[SPECIAL_GLYPH_RECYCLING] = u8"♻️", /* actually called: UNIVERSAL RECYCLNG SYMBOL */
[SPECIAL_GLYPH_DOWNLOAD] = u8"⤵️", /* actually called: RIGHT ARROW CURVING DOWN */
[SPECIAL_GLYPH_SPARKLES] = u8"✨",
+ [SPECIAL_GLYPH_LOW_BATTERY] = u8"🪫",
[SPECIAL_GLYPH_WARNING_SIGN] = u8"⚠️",
+ [SPECIAL_GLYPH_COMPUTER_DISK] = u8"💽",
+ [SPECIAL_GLYPH_WORLD] = u8"🌍",
},
};
@@ -139,5 +145,5 @@ const char *special_glyph(SpecialGlyph code) {
return NULL;
assert(code < _SPECIAL_GLYPH_MAX);
- return draw_table[code >= _SPECIAL_GLYPH_FIRST_EMOJI ? emoji_enabled() : is_locale_utf8()][code];
+ return draw_table[force_utf || (code >= _SPECIAL_GLYPH_FIRST_EMOJI ? emoji_enabled() : is_locale_utf8())][code];
}
diff --git a/src/libnm-systemd-shared/src/basic/glyph-util.h b/src/libnm-systemd-shared/src/basic/glyph-util.h
index b64639622e..a7709976e1 100644
--- a/src/libnm-systemd-shared/src/basic/glyph-util.h
+++ b/src/libnm-systemd-shared/src/basic/glyph-util.h
@@ -22,14 +22,15 @@ typedef enum SpecialGlyph {
SPECIAL_GLYPH_MU,
SPECIAL_GLYPH_CHECK_MARK,
SPECIAL_GLYPH_CROSS_MARK,
- SPECIAL_GLYPH_ARROW_LEFT,
- SPECIAL_GLYPH_ARROW_RIGHT,
- SPECIAL_GLYPH_ARROW_UP,
- SPECIAL_GLYPH_ARROW_DOWN,
- SPECIAL_GLYPH_ELLIPSIS,
SPECIAL_GLYPH_LIGHT_SHADE,
SPECIAL_GLYPH_DARK_SHADE,
+ SPECIAL_GLYPH_FULL_BLOCK,
SPECIAL_GLYPH_SIGMA,
+ SPECIAL_GLYPH_ARROW_UP,
+ SPECIAL_GLYPH_ARROW_DOWN,
+ SPECIAL_GLYPH_ARROW_LEFT,
+ SPECIAL_GLYPH_ARROW_RIGHT,
+ SPECIAL_GLYPH_ELLIPSIS,
SPECIAL_GLYPH_EXTERNAL_LINK,
_SPECIAL_GLYPH_FIRST_EMOJI,
SPECIAL_GLYPH_ECSTATIC_SMILEY = _SPECIAL_GLYPH_FIRST_EMOJI,
@@ -44,15 +45,22 @@ typedef enum SpecialGlyph {
SPECIAL_GLYPH_RECYCLING,
SPECIAL_GLYPH_DOWNLOAD,
SPECIAL_GLYPH_SPARKLES,
+ SPECIAL_GLYPH_LOW_BATTERY,
SPECIAL_GLYPH_WARNING_SIGN,
+ SPECIAL_GLYPH_COMPUTER_DISK,
+ SPECIAL_GLYPH_WORLD,
_SPECIAL_GLYPH_MAX,
_SPECIAL_GLYPH_INVALID = -EINVAL,
} SpecialGlyph;
-const char *special_glyph(SpecialGlyph code) _const_;
-
bool emoji_enabled(void);
+const char *special_glyph_full(SpecialGlyph code, bool force_utf) _const_;
+
+static inline const char *special_glyph(SpecialGlyph code) {
+ return special_glyph_full(code, false);
+}
+
static inline const char *special_glyph_check_mark(bool b) {
return b ? special_glyph(SPECIAL_GLYPH_CHECK_MARK) : special_glyph(SPECIAL_GLYPH_CROSS_MARK);
}
diff --git a/src/libnm-systemd-shared/src/basic/hash-funcs.c b/src/libnm-systemd-shared/src/basic/hash-funcs.c
index eed30f0944..6dcc2f36d9 100644
--- a/src/libnm-systemd-shared/src/basic/hash-funcs.c
+++ b/src/libnm-systemd-shared/src/basic/hash-funcs.c
@@ -36,7 +36,7 @@ void path_hash_func(const char *q, struct siphash *state) {
/* if path is absolute, add one "/" to the hash. */
if (path_is_absolute(q))
- siphash24_compress("/", 1, state);
+ siphash24_compress_byte('/', state);
for (;;) {
const char *e;
@@ -71,7 +71,7 @@ DEFINE_HASH_OPS_FULL(path_hash_ops_free_free,
#endif /* NM_IGNORED */
void trivial_hash_func(const void *p, struct siphash *state) {
- siphash24_compress(&p, sizeof(p), state);
+ siphash24_compress_typesafe(p, state);
}
int trivial_compare_func(const void *a, const void *b) {
@@ -97,7 +97,7 @@ const struct hash_ops trivial_hash_ops_free_free = {
};
void uint64_hash_func(const uint64_t *p, struct siphash *state) {
- siphash24_compress(p, sizeof(uint64_t), state);
+ siphash24_compress_typesafe(*p, state);
}
int uint64_compare_func(const uint64_t *a, const uint64_t *b) {
@@ -109,7 +109,7 @@ DEFINE_HASH_OPS(uint64_hash_ops, uint64_t, uint64_hash_func, uint64_compare_func
#if 0 /* NM_IGNORED */
#if SIZEOF_DEV_T != 8
void devt_hash_func(const dev_t *p, struct siphash *state) {
- siphash24_compress(p, sizeof(dev_t), state);
+ siphash24_compress_typesafe(*p, state);
}
#endif
diff --git a/src/libnm-systemd-shared/src/basic/hash-funcs.h b/src/libnm-systemd-shared/src/basic/hash-funcs.h
index be64289252..3804e94d98 100644
--- a/src/libnm-systemd-shared/src/basic/hash-funcs.h
+++ b/src/libnm-systemd-shared/src/basic/hash-funcs.h
@@ -93,14 +93,14 @@ extern const struct hash_ops trivial_hash_ops;
extern const struct hash_ops trivial_hash_ops_free;
extern const struct hash_ops trivial_hash_ops_free_free;
-/* 32bit values we can always just embed in the pointer itself, but in order to support 32bit archs we need store 64bit
+/* 32-bit values we can always just embed in the pointer itself, but in order to support 32-bit archs we need store 64-bit
* values indirectly, since they don't fit in a pointer. */
void uint64_hash_func(const uint64_t *p, struct siphash *state);
int uint64_compare_func(const uint64_t *a, const uint64_t *b) _pure_;
extern const struct hash_ops uint64_hash_ops;
-/* On some archs dev_t is 32bit, and on others 64bit. And sometimes it's 64bit on 32bit archs, and sometimes 32bit on
- * 64bit archs. Yuck! */
+/* On some archs dev_t is 32-bit, and on others 64-bit. And sometimes it's 64-bit on 32-bit archs, and sometimes 32-bit on
+ * 64-bit archs. Yuck! */
#if SIZEOF_DEV_T != 8
void devt_hash_func(const dev_t *p, struct siphash *state);
#else
diff --git a/src/libnm-systemd-shared/src/basic/hashmap.c b/src/libnm-systemd-shared/src/basic/hashmap.c
index 356200cfcc..28d2efa0e2 100644
--- a/src/libnm-systemd-shared/src/basic/hashmap.c
+++ b/src/libnm-systemd-shared/src/basic/hashmap.c
@@ -23,6 +23,7 @@
#include "random-util.h"
#include "set.h"
#include "siphash24.h"
+#include "sort-util.h"
#include "string-util.h"
#include "strv.h"
@@ -176,9 +177,9 @@ struct _packed_ indirect_storage {
};
struct direct_storage {
- /* This gives us 39 bytes on 64bit, or 35 bytes on 32bit.
- * That's room for 4 set_entries + 4 DIB bytes + 3 unused bytes on 64bit,
- * or 7 set_entries + 7 DIB bytes + 0 unused bytes on 32bit. */
+ /* This gives us 39 bytes on 64-bit, or 35 bytes on 32-bit.
+ * That's room for 4 set_entries + 4 DIB bytes + 3 unused bytes on 64-bit,
+ * or 7 set_entries + 7 DIB bytes + 0 unused bytes on 32-bit. */
uint8_t storage[sizeof(struct indirect_storage)];
};
@@ -2112,3 +2113,54 @@ bool set_fnmatch(Set *include_patterns, Set *exclude_patterns, const char *needl
return set_fnmatch_one(include_patterns, needle);
}
+
+static int hashmap_entry_compare(
+ struct hashmap_base_entry * const *a,
+ struct hashmap_base_entry * const *b,
+ compare_func_t compare) {
+
+ assert(a && *a);
+ assert(b && *b);
+ assert(compare);
+
+ return compare((*a)->key, (*b)->key);
+}
+
+int _hashmap_dump_sorted(HashmapBase *h, void ***ret, size_t *ret_n) {
+ _cleanup_free_ struct hashmap_base_entry **entries = NULL;
+ Iterator iter;
+ unsigned idx;
+ size_t n = 0;
+
+ assert(ret);
+
+ if (_hashmap_size(h) == 0) {
+ *ret = NULL;
+ if (ret_n)
+ *ret_n = 0;
+ return 0;
+ }
+
+ /* We append one more element than needed so that the resulting array can be used as a strv. We
+ * don't count this entry in the returned size. */
+ entries = new(struct hashmap_base_entry*, _hashmap_size(h) + 1);
+ if (!entries)
+ return -ENOMEM;
+
+ HASHMAP_FOREACH_IDX(idx, h, iter)
+ entries[n++] = bucket_at(h, idx);
+
+ assert(n == _hashmap_size(h));
+ entries[n] = NULL;
+
+ typesafe_qsort_r(entries, n, hashmap_entry_compare, h->hash_ops->compare);
+
+ /* Reuse the array. */
+ FOREACH_ARRAY(e, entries, n)
+ *e = entry_value(h, *e);
+
+ *ret = (void**) TAKE_PTR(entries);
+ if (ret_n)
+ *ret_n = n;
+ return 0;
+}
diff --git a/src/libnm-systemd-shared/src/basic/hashmap.h b/src/libnm-systemd-shared/src/basic/hashmap.h
index 68d9b81cf2..233f1d7a1e 100644
--- a/src/libnm-systemd-shared/src/basic/hashmap.h
+++ b/src/libnm-systemd-shared/src/basic/hashmap.h
@@ -398,12 +398,28 @@ static inline char** ordered_hashmap_get_strv(OrderedHashmap *h) {
return _hashmap_get_strv(HASHMAP_BASE(h));
}
+int _hashmap_dump_sorted(HashmapBase *h, void ***ret, size_t *ret_n);
+static inline int hashmap_dump_sorted(Hashmap *h, void ***ret, size_t *ret_n) {
+ return _hashmap_dump_sorted(HASHMAP_BASE(h), ret, ret_n);
+}
+static inline int ordered_hashmap_dump_sorted(OrderedHashmap *h, void ***ret, size_t *ret_n) {
+ return _hashmap_dump_sorted(HASHMAP_BASE(h), ret, ret_n);
+}
+static inline int set_dump_sorted(Set *h, void ***ret, size_t *ret_n) {
+ return _hashmap_dump_sorted(HASHMAP_BASE(h), ret, ret_n);
+}
+
/*
* Hashmaps are iterated in unpredictable order.
* OrderedHashmaps are an exception to this. They are iterated in the order
* the entries were inserted.
* It is safe to remove the current entry.
*/
+#define _HASHMAP_BASE_FOREACH(e, h, i) \
+ for (Iterator i = ITERATOR_FIRST; _hashmap_iterate((h), &i, (void**)&(e), NULL); )
+#define HASHMAP_BASE_FOREACH(e, h) \
+ _HASHMAP_BASE_FOREACH(e, h, UNIQ_T(i, UNIQ))
+
#define _HASHMAP_FOREACH(e, h, i) \
for (Iterator i = ITERATOR_FIRST; hashmap_iterate((h), &i, (void**)&(e), NULL); )
#define HASHMAP_FOREACH(e, h) \
@@ -414,6 +430,11 @@ static inline char** ordered_hashmap_get_strv(OrderedHashmap *h) {
#define ORDERED_HASHMAP_FOREACH(e, h) \
_ORDERED_HASHMAP_FOREACH(e, h, UNIQ_T(i, UNIQ))
+#define _HASHMAP_BASE_FOREACH_KEY(e, k, h, i) \
+ for (Iterator i = ITERATOR_FIRST; _hashmap_iterate((h), &i, (void**)&(e), (const void**) &(k)); )
+#define HASHMAP_BASE_FOREACH_KEY(e, k, h) \
+ _HASHMAP_BASE_FOREACH_KEY(e, k, h, UNIQ_T(i, UNIQ))
+
#define _HASHMAP_FOREACH_KEY(e, k, h, i) \
for (Iterator i = ITERATOR_FIRST; hashmap_iterate((h), &i, (void**)&(e), (const void**) &(k)); )
#define HASHMAP_FOREACH_KEY(e, k, h) \
diff --git a/src/libnm-systemd-shared/src/basic/hexdecoct.c b/src/libnm-systemd-shared/src/basic/hexdecoct.c
index d41d2ea079..41228520a5 100644
--- a/src/libnm-systemd-shared/src/basic/hexdecoct.c
+++ b/src/libnm-systemd-shared/src/basic/hexdecoct.c
@@ -116,7 +116,7 @@ int unhexmem_full(
const char *p,
size_t l,
bool secure,
- void **ret,
+ void **ret_data,
size_t *ret_len) {
_cleanup_free_ uint8_t *buf = NULL;
@@ -157,8 +157,8 @@ int unhexmem_full(
if (ret_len)
*ret_len = (size_t) (z - buf);
- if (ret)
- *ret = TAKE_PTR(buf);
+ if (ret_data)
+ *ret_data = TAKE_PTR(buf);
return 0;
}
@@ -557,12 +557,12 @@ int unbase64char(char c) {
offset += '9' - '0' + 1;
- if (c == '+')
+ if (IN_SET(c, '+', '-')) /* Support both the regular and the URL safe character set (see above) */
return offset;
offset++;
- if (c == '/')
+ if (IN_SET(c, '/', '_')) /* ditto */
return offset;
return -EINVAL;
@@ -772,7 +772,7 @@ int unbase64mem_full(
const char *p,
size_t l,
bool secure,
- void **ret,
+ void **ret_data,
size_t *ret_size) {
_cleanup_free_ uint8_t *buf = NULL;
@@ -860,8 +860,8 @@ int unbase64mem_full(
if (ret_size)
*ret_size = (size_t) (z - buf);
- if (ret)
- *ret = TAKE_PTR(buf);
+ if (ret_data)
+ *ret_data = TAKE_PTR(buf);
return 0;
}
diff --git a/src/libnm-systemd-shared/src/basic/hexdecoct.h b/src/libnm-systemd-shared/src/basic/hexdecoct.h
index 319b21a17c..0a10af3e16 100644
--- a/src/libnm-systemd-shared/src/basic/hexdecoct.h
+++ b/src/libnm-systemd-shared/src/basic/hexdecoct.h
@@ -18,9 +18,9 @@ char hexchar(int x) _const_;
int unhexchar(char c) _const_;
char *hexmem(const void *p, size_t l);
-int unhexmem_full(const char *p, size_t l, bool secure, void **mem, size_t *len);
-static inline int unhexmem(const char *p, size_t l, void **mem, size_t *len) {
- return unhexmem_full(p, l, false, mem, len);
+int unhexmem_full(const char *p, size_t l, bool secure, void **ret_data, size_t *ret_size);
+static inline int unhexmem(const char *p, void **ret_data, size_t *ret_size) {
+ return unhexmem_full(p, SIZE_MAX, false, ret_data, ret_size);
}
char base32hexchar(int x) _const_;
@@ -45,9 +45,9 @@ ssize_t base64_append(
size_t l,
size_t margin,
size_t width);
-int unbase64mem_full(const char *p, size_t l, bool secure, void **mem, size_t *len);
-static inline int unbase64mem(const char *p, size_t l, void **mem, size_t *len) {
- return unbase64mem_full(p, l, false, mem, len);
+int unbase64mem_full(const char *p, size_t l, bool secure, void **ret_data, size_t *ret_size);
+static inline int unbase64mem(const char *p, void **ret_data, size_t *ret_size) {
+ return unbase64mem_full(p, SIZE_MAX, false, ret_data, ret_size);
}
void hexdump(FILE *f, const void *p, size_t s);
diff --git a/src/libnm-systemd-shared/src/basic/in-addr-util.c b/src/libnm-systemd-shared/src/basic/in-addr-util.c
index b863aec399..0c3923b0ef 100644
--- a/src/libnm-systemd-shared/src/basic/in-addr-util.c
+++ b/src/libnm-systemd-shared/src/basic/in-addr-util.c
@@ -736,10 +736,11 @@ int in_addr_mask(int family, union in_addr_union *addr, unsigned char prefixlen)
}
}
-int in4_addr_prefix_covers(
+int in4_addr_prefix_covers_full(
const struct in_addr *prefix,
unsigned char prefixlen,
- const struct in_addr *address) {
+ const struct in_addr *address,
+ unsigned char address_prefixlen) {
struct in_addr masked_prefix, masked_address;
int r;
@@ -747,6 +748,9 @@ int in4_addr_prefix_covers(
assert(prefix);
assert(address);
+ if (prefixlen > address_prefixlen)
+ return false;
+
masked_prefix = *prefix;
r = in4_addr_mask(&masked_prefix, prefixlen);
if (r < 0)
@@ -760,10 +764,11 @@ int in4_addr_prefix_covers(
return in4_addr_equal(&masked_prefix, &masked_address);
}
-int in6_addr_prefix_covers(
+int in6_addr_prefix_covers_full(
const struct in6_addr *prefix,
unsigned char prefixlen,
- const struct in6_addr *address) {
+ const struct in6_addr *address,
+ unsigned char address_prefixlen) {
struct in6_addr masked_prefix, masked_address;
int r;
@@ -771,6 +776,9 @@ int in6_addr_prefix_covers(
assert(prefix);
assert(address);
+ if (prefixlen > address_prefixlen)
+ return false;
+
masked_prefix = *prefix;
r = in6_addr_mask(&masked_prefix, prefixlen);
if (r < 0)
@@ -784,20 +792,21 @@ int in6_addr_prefix_covers(
return in6_addr_equal(&masked_prefix, &masked_address);
}
-int in_addr_prefix_covers(
+int in_addr_prefix_covers_full(
int family,
const union in_addr_union *prefix,
unsigned char prefixlen,
- const union in_addr_union *address) {
+ const union in_addr_union *address,
+ unsigned char address_prefixlen) {
assert(prefix);
assert(address);
switch (family) {
case AF_INET:
- return in4_addr_prefix_covers(&prefix->in, prefixlen, &address->in);
+ return in4_addr_prefix_covers_full(&prefix->in, prefixlen, &address->in, address_prefixlen);
case AF_INET6:
- return in6_addr_prefix_covers(&prefix->in6, prefixlen, &address->in6);
+ return in6_addr_prefix_covers_full(&prefix->in6, prefixlen, &address->in6, address_prefixlen);
default:
return -EAFNOSUPPORT;
}
@@ -922,12 +931,19 @@ int in_addr_prefix_from_string_auto_internal(
}
+void in_addr_hash_func(const union in_addr_union *u, int family, struct siphash *state) {
+ assert(u);
+ assert(state);
+
+ siphash24_compress(u->bytes, FAMILY_ADDRESS_SIZE(family), state);
+}
+
void in_addr_data_hash_func(const struct in_addr_data *a, struct siphash *state) {
assert(a);
assert(state);
- siphash24_compress(&a->family, sizeof(a->family), state);
- siphash24_compress(&a->address, FAMILY_ADDRESS_SIZE(a->family), state);
+ siphash24_compress_typesafe(a->family, state);
+ in_addr_hash_func(&a->address, a->family, state);
}
int in_addr_data_compare_func(const struct in_addr_data *x, const struct in_addr_data *y) {
@@ -960,7 +976,7 @@ void in6_addr_hash_func(const struct in6_addr *addr, struct siphash *state) {
assert(addr);
assert(state);
- siphash24_compress(addr, sizeof(*addr), state);
+ siphash24_compress_typesafe(*addr, state);
}
int in6_addr_compare_func(const struct in6_addr *a, const struct in6_addr *b) {
diff --git a/src/libnm-systemd-shared/src/basic/in-addr-util.h b/src/libnm-systemd-shared/src/basic/in-addr-util.h
index 200b9eb69d..9fae3cae45 100644
--- a/src/libnm-systemd-shared/src/basic/in-addr-util.h
+++ b/src/libnm-systemd-shared/src/basic/in-addr-util.h
@@ -144,9 +144,18 @@ int in4_addr_default_subnet_mask(const struct in_addr *addr, struct in_addr *mas
int in4_addr_mask(struct in_addr *addr, unsigned char prefixlen);
int in6_addr_mask(struct in6_addr *addr, unsigned char prefixlen);
int in_addr_mask(int family, union in_addr_union *addr, unsigned char prefixlen);
-int in4_addr_prefix_covers(const struct in_addr *prefix, unsigned char prefixlen, const struct in_addr *address);
-int in6_addr_prefix_covers(const struct in6_addr *prefix, unsigned char prefixlen, const struct in6_addr *address);
-int in_addr_prefix_covers(int family, const union in_addr_union *prefix, unsigned char prefixlen, const union in_addr_union *address);
+int in4_addr_prefix_covers_full(const struct in_addr *prefix, unsigned char prefixlen, const struct in_addr *address, unsigned char address_prefixlen);
+int in6_addr_prefix_covers_full(const struct in6_addr *prefix, unsigned char prefixlen, const struct in6_addr *address, unsigned char address_prefixlen);
+int in_addr_prefix_covers_full(int family, const union in_addr_union *prefix, unsigned char prefixlen, const union in_addr_union *address, unsigned char address_prefixlen);
+static inline int in4_addr_prefix_covers(const struct in_addr *prefix, unsigned char prefixlen, const struct in_addr *address) {
+ return in4_addr_prefix_covers_full(prefix, prefixlen, address, 32);
+}
+static inline int in6_addr_prefix_covers(const struct in6_addr *prefix, unsigned char prefixlen, const struct in6_addr *address) {
+ return in6_addr_prefix_covers_full(prefix, prefixlen, address, 128);
+}
+static inline int in_addr_prefix_covers(int family, const union in_addr_union *prefix, unsigned char prefixlen, const union in_addr_union *address) {
+ return in_addr_prefix_covers_full(family, prefix, prefixlen, address, family == AF_INET ? 32 : family == AF_INET6 ? 128 : 0);
+}
int in_addr_parse_prefixlen(int family, const char *p, unsigned char *ret);
int in_addr_prefix_from_string(const char *p, int family, union in_addr_union *ret_prefix, unsigned char *ret_prefixlen);
@@ -176,6 +185,7 @@ static inline size_t FAMILY_ADDRESS_SIZE(int family) {
* See also oss-fuzz#11344. */
#define IN_ADDR_NULL ((union in_addr_union) { .in6 = {} })
+void in_addr_hash_func(const union in_addr_union *u, int family, struct siphash *state);
void in_addr_data_hash_func(const struct in_addr_data *a, struct siphash *state);
int in_addr_data_compare_func(const struct in_addr_data *x, const struct in_addr_data *y);
void in6_addr_hash_func(const struct in6_addr *addr, struct siphash *state);
@@ -186,6 +196,16 @@ extern const struct hash_ops in_addr_data_hash_ops_free;
extern const struct hash_ops in6_addr_hash_ops;
extern const struct hash_ops in6_addr_hash_ops_free;
+static inline void PTR_TO_IN4_ADDR(const void *p, struct in_addr *ret) {
+ assert(ret);
+ ret->s_addr = (uint32_t) ((uintptr_t) p);
+}
+
+static inline void* IN4_ADDR_TO_PTR(const struct in_addr *a) {
+ assert(a);
+ return (void*) ((uintptr_t) a->s_addr);
+}
+
#define IPV4_ADDRESS_FMT_STR "%u.%u.%u.%u"
#define IPV4_ADDRESS_FMT_VAL(address) \
be32toh((address).s_addr) >> 24, \
diff --git a/src/libnm-systemd-shared/src/basic/inotify-util.c b/src/libnm-systemd-shared/src/basic/inotify-util.c
index 59e03e620e..c748bf1bf0 100644
--- a/src/libnm-systemd-shared/src/basic/inotify-util.c
+++ b/src/libnm-systemd-shared/src/basic/inotify-util.c
@@ -6,6 +6,43 @@
#include "inotify-util.h"
#include "stat-util.h"
+bool inotify_event_next(
+ union inotify_event_buffer *buffer,
+ size_t size,
+ struct inotify_event **iterator,
+ int log_level) {
+
+ struct inotify_event *e;
+ size_t offset = 0;
+
+ assert(buffer);
+ assert(iterator);
+
+ if (*iterator) {
+ assert((uint8_t*) *iterator >= buffer->raw);
+ offset = (uint8_t*) *iterator - buffer->raw;
+ offset += offsetof(struct inotify_event, name) + (*iterator)->len;
+ }
+
+ if (size == offset)
+ return false; /* reached end of list */
+
+ if (size < offset ||
+ size - offset < offsetof(struct inotify_event, name)) {
+ log_full(log_level, "Received invalid inotify event, ignoring.");
+ return false;
+ }
+
+ e = CAST_ALIGN_PTR(struct inotify_event, buffer->raw + offset);
+ if (size - offset - offsetof(struct inotify_event, name) < e->len) {
+ log_full(log_level, "Received invalid inotify event, ignoring.");
+ return false;
+ }
+
+ *iterator = e;
+ return true;
+}
+
int inotify_add_watch_fd(int fd, int what, uint32_t mask) {
int wd, r;
diff --git a/src/libnm-systemd-shared/src/basic/inotify-util.h b/src/libnm-systemd-shared/src/basic/inotify-util.h
index 61951ff3e3..665fdacaa6 100644
--- a/src/libnm-systemd-shared/src/basic/inotify-util.h
+++ b/src/libnm-systemd-shared/src/basic/inotify-util.h
@@ -10,29 +10,27 @@
#define INOTIFY_EVENT_MAX (offsetof(struct inotify_event, name) + NAME_MAX + 1)
-#define _FOREACH_INOTIFY_EVENT(e, buffer, sz, log_level, start, end) \
- for (struct inotify_event \
- *start = &((buffer).ev), \
- *end = (struct inotify_event*) ((uint8_t*) start + (sz)), \
- *e = start; \
- (size_t) ((uint8_t*) end - (uint8_t*) e) >= sizeof(struct inotify_event) && \
- ((size_t) ((uint8_t*) end - (uint8_t*) e) >= sizeof(struct inotify_event) + e->len || \
- (log_full(log_level, "Received invalid inotify event, ignoring."), false)); \
- e = (struct inotify_event*) ((uint8_t*) e + sizeof(struct inotify_event) + e->len))
-
-#define _FOREACH_INOTIFY_EVENT_FULL(e, buffer, sz, log_level) \
- _FOREACH_INOTIFY_EVENT(e, buffer, sz, log_level, UNIQ_T(start, UNIQ), UNIQ_T(end, UNIQ))
+/* This evaluates arguments multiple times */
+#define FOREACH_INOTIFY_EVENT_FULL(e, buffer, sz, log_level) \
+ for (struct inotify_event *e = NULL; \
+ inotify_event_next(&buffer, sz, &e, log_level); )
#define FOREACH_INOTIFY_EVENT(e, buffer, sz) \
- _FOREACH_INOTIFY_EVENT_FULL(e, buffer, sz, LOG_DEBUG)
+ FOREACH_INOTIFY_EVENT_FULL(e, buffer, sz, LOG_DEBUG)
#define FOREACH_INOTIFY_EVENT_WARN(e, buffer, sz) \
- _FOREACH_INOTIFY_EVENT_FULL(e, buffer, sz, LOG_WARNING)
+ FOREACH_INOTIFY_EVENT_FULL(e, buffer, sz, LOG_WARNING)
union inotify_event_buffer {
struct inotify_event ev;
uint8_t raw[INOTIFY_EVENT_MAX];
};
+bool inotify_event_next(
+ union inotify_event_buffer *buffer,
+ size_t size,
+ struct inotify_event **iterator,
+ int log_level);
+
int inotify_add_watch_fd(int fd, int what, uint32_t mask);
int inotify_add_watch_and_warn(int fd, const char *pathname, uint32_t mask);
diff --git a/src/libnm-systemd-shared/src/basic/io-util.c b/src/libnm-systemd-shared/src/basic/io-util.c
index 0c480091b2..abe61ed56c 100644
--- a/src/libnm-systemd-shared/src/basic/io-util.c
+++ b/src/libnm-systemd-shared/src/basic/io-util.c
@@ -7,7 +7,9 @@
#include <stdio.h>
#include <unistd.h>
+#include "errno-util.h"
#include "io-util.h"
+#include "iovec-util.h"
#include "string-util.h"
#include "time-util.h"
@@ -58,8 +60,7 @@ ssize_t loop_read(int fd, void *buf, size_t nbytes, bool do_poll) {
assert(fd >= 0);
- /* If called with nbytes == 0, let's call read() at least
- * once, to validate the operation */
+ /* If called with nbytes == 0, let's call read() at least once, to validate the operation */
if (nbytes > (size_t) SSIZE_MAX)
return -EINVAL;
@@ -111,13 +112,29 @@ int loop_read_exact(int fd, void *buf, size_t nbytes, bool do_poll) {
}
#if 0 /* NM_IGNORED */
-int loop_write(int fd, const void *buf, size_t nbytes, bool do_poll) {
- const uint8_t *p = ASSERT_PTR(buf);
+int loop_write_full(int fd, const void *buf, size_t nbytes, usec_t timeout) {
+ const uint8_t *p;
+ usec_t end;
+ int r;
assert(fd >= 0);
+ assert(buf || nbytes == 0);
+
+ if (nbytes == 0) {
+ static const dummy_t dummy[0];
+ assert_cc(sizeof(dummy) == 0);
+ p = (const void*) dummy; /* Some valid pointer, in case NULL was specified */
+ } else {
+ if (nbytes == SIZE_MAX)
+ nbytes = strlen(buf);
+ else if (_unlikely_(nbytes > (size_t) SSIZE_MAX))
+ return -EINVAL;
+
+ p = buf;
+ }
- if (_unlikely_(nbytes > (size_t) SSIZE_MAX))
- return -EINVAL;
+ /* When timeout is 0 or USEC_INFINITY this is not used. But we initialize it to a sensible value. */
+ end = timestamp_is_set(timeout) ? usec_add(now(CLOCK_MONOTONIC), timeout) : USEC_INFINITY;
do {
ssize_t k;
@@ -127,16 +144,31 @@ int loop_write(int fd, const void *buf, size_t nbytes, bool do_poll) {
if (errno == EINTR)
continue;
- if (errno == EAGAIN && do_poll) {
- /* We knowingly ignore any return value here,
- * and expect that any error/EOF is reported
- * via write() */
+ if (errno != EAGAIN || timeout == 0)
+ return -errno;
- (void) fd_wait_for_event(fd, POLLOUT, USEC_INFINITY);
- continue;
+ usec_t wait_for;
+
+ if (timeout == USEC_INFINITY)
+ wait_for = USEC_INFINITY;
+ else {
+ usec_t t = now(CLOCK_MONOTONIC);
+ if (t >= end)
+ return -ETIME;
+
+ wait_for = usec_sub_unsigned(end, t);
}
- return -errno;
+ r = fd_wait_for_event(fd, POLLOUT, wait_for);
+ if (timeout == USEC_INFINITY || ERRNO_IS_NEG_TRANSIENT(r))
+ /* If timeout == USEC_INFINITY we knowingly ignore any return value
+ * here, and expect that any error/EOF is reported via write() */
+ continue;
+ if (r < 0)
+ return r;
+ if (r == 0)
+ return -ETIME;
+ continue;
}
if (_unlikely_(nbytes > 0 && k == 0)) /* Can't really happen */
@@ -260,7 +292,7 @@ ssize_t sparse_write(int fd, const void *p, size_t sz, size_t run_length) {
return -EIO;
}
- if (lseek(fd, n, SEEK_CUR) == (off_t) -1)
+ if (lseek(fd, n, SEEK_CUR) < 0)
return -errno;
q += n;
@@ -281,102 +313,4 @@ ssize_t sparse_write(int fd, const void *p, size_t sz, size_t run_length) {
return q - (const uint8_t*) p;
}
-
-char* set_iovec_string_field(struct iovec *iovec, size_t *n_iovec, const char *field, const char *value) {
- char *x;
-
- x = strjoin(field, value);
- if (x)
- iovec[(*n_iovec)++] = IOVEC_MAKE_STRING(x);
- return x;
-}
-
-char* set_iovec_string_field_free(struct iovec *iovec, size_t *n_iovec, const char *field, char *value) {
- char *x;
-
- x = set_iovec_string_field(iovec, n_iovec, field, value);
- free(value);
- return x;
-}
-
-struct iovec_wrapper *iovw_new(void) {
- return malloc0(sizeof(struct iovec_wrapper));
-}
-
-void iovw_free_contents(struct iovec_wrapper *iovw, bool free_vectors) {
- if (free_vectors)
- for (size_t i = 0; i < iovw->count; i++)
- free(iovw->iovec[i].iov_base);
-
- iovw->iovec = mfree(iovw->iovec);
- iovw->count = 0;
-}
-
-struct iovec_wrapper *iovw_free_free(struct iovec_wrapper *iovw) {
- iovw_free_contents(iovw, true);
-
- return mfree(iovw);
-}
-
-struct iovec_wrapper *iovw_free(struct iovec_wrapper *iovw) {
- iovw_free_contents(iovw, false);
-
- return mfree(iovw);
-}
-
-int iovw_put(struct iovec_wrapper *iovw, void *data, size_t len) {
- if (iovw->count >= IOV_MAX)
- return -E2BIG;
-
- if (!GREEDY_REALLOC(iovw->iovec, iovw->count + 1))
- return -ENOMEM;
-
- iovw->iovec[iovw->count++] = IOVEC_MAKE(data, len);
- return 0;
-}
-
-int iovw_put_string_field(struct iovec_wrapper *iovw, const char *field, const char *value) {
- _cleanup_free_ char *x = NULL;
- int r;
-
- x = strjoin(field, value);
- if (!x)
- return -ENOMEM;
-
- r = iovw_put(iovw, x, strlen(x));
- if (r >= 0)
- TAKE_PTR(x);
-
- return r;
-}
-
-int iovw_put_string_field_free(struct iovec_wrapper *iovw, const char *field, char *value) {
- _cleanup_free_ _unused_ char *free_ptr = value;
-
- return iovw_put_string_field(iovw, field, value);
-}
-
-void iovw_rebase(struct iovec_wrapper *iovw, char *old, char *new) {
- for (size_t i = 0; i < iovw->count; i++)
- iovw->iovec[i].iov_base = (char *)iovw->iovec[i].iov_base - old + new;
-}
-
-size_t iovw_size(struct iovec_wrapper *iovw) {
- size_t n = 0;
-
- for (size_t i = 0; i < iovw->count; i++)
- n += iovw->iovec[i].iov_len;
-
- return n;
-}
-
-void iovec_array_free(struct iovec *iov, size_t n) {
- if (!iov)
- return;
-
- for (size_t i = 0; i < n; i++)
- free(iov[i].iov_base);
-
- free(iov);
-}
#endif /* NM_IGNORED */
diff --git a/src/libnm-systemd-shared/src/basic/io-util.h b/src/libnm-systemd-shared/src/basic/io-util.h
index 3ad8267962..e027c1a878 100644
--- a/src/libnm-systemd-shared/src/basic/io-util.h
+++ b/src/libnm-systemd-shared/src/basic/io-util.h
@@ -6,7 +6,6 @@
#include <stddef.h>
#include <stdint.h>
#include <sys/types.h>
-#include <sys/uio.h>
#include "macro.h"
#include "time-util.h"
@@ -15,7 +14,11 @@ int flush_fd(int fd);
ssize_t loop_read(int fd, void *buf, size_t nbytes, bool do_poll);
int loop_read_exact(int fd, void *buf, size_t nbytes, bool do_poll);
-int loop_write(int fd, const void *buf, size_t nbytes, bool do_poll);
+
+int loop_write_full(int fd, const void *buf, size_t nbytes, usec_t timeout);
+static inline int loop_write(int fd, const void *buf, size_t nbytes) {
+ return loop_write_full(fd, buf, nbytes, 0);
+}
int pipe_eof(int fd);
@@ -24,38 +27,6 @@ int fd_wait_for_event(int fd, int event, usec_t timeout);
ssize_t sparse_write(int fd, const void *p, size_t sz, size_t run_length);
-static inline size_t IOVEC_TOTAL_SIZE(const struct iovec *i, size_t n) {
- size_t r = 0;
-
- for (size_t j = 0; j < n; j++)
- r += i[j].iov_len;
-
- return r;
-}
-
-static inline bool IOVEC_INCREMENT(struct iovec *i, size_t n, size_t k) {
- /* Returns true if there is nothing else to send (bytes written cover all of the iovec),
- * false if there's still work to do. */
-
- for (size_t j = 0; j < n; j++) {
- size_t sub;
-
- if (i[j].iov_len == 0)
- continue;
- if (k == 0)
- return false;
-
- sub = MIN(i[j].iov_len, k);
- i[j].iov_len -= sub;
- i[j].iov_base = (uint8_t*) i[j].iov_base + sub;
- k -= sub;
- }
-
- assert(k == 0); /* Anything else would mean that we wrote more bytes than available,
- * or the kernel reported writing more bytes than sent. */
- return true;
-}
-
static inline bool FILE_SIZE_VALID(uint64_t l) {
/* ftruncate() and friends take an unsigned file size, but actually cannot deal with file sizes larger than
* 2^63 since the kernel internally handles it as signed value. This call allows checking for this early. */
@@ -73,40 +44,3 @@ static inline bool FILE_SIZE_VALID_OR_INFINITY(uint64_t l) {
return FILE_SIZE_VALID(l);
}
-
-#define IOVEC_NULL (struct iovec) {}
-#define IOVEC_MAKE(base, len) (struct iovec) { .iov_base = (base), .iov_len = (len) }
-#define IOVEC_MAKE_STRING(string) \
- ({ \
- char *_s = (char*) (string); \
- IOVEC_MAKE(_s, strlen(_s)); \
- })
-
-char* set_iovec_string_field(struct iovec *iovec, size_t *n_iovec, const char *field, const char *value);
-char* set_iovec_string_field_free(struct iovec *iovec, size_t *n_iovec, const char *field, char *value);
-
-struct iovec_wrapper {
- struct iovec *iovec;
- size_t count;
-};
-
-struct iovec_wrapper *iovw_new(void);
-struct iovec_wrapper *iovw_free(struct iovec_wrapper *iovw);
-struct iovec_wrapper *iovw_free_free(struct iovec_wrapper *iovw);
-void iovw_free_contents(struct iovec_wrapper *iovw, bool free_vectors);
-
-int iovw_put(struct iovec_wrapper *iovw, void *data, size_t len);
-static inline int iovw_consume(struct iovec_wrapper *iovw, void *data, size_t len) {
- /* Move data into iovw or free on error */
- int r = iovw_put(iovw, data, len);
- if (r < 0)
- free(data);
- return r;
-}
-
-int iovw_put_string_field(struct iovec_wrapper *iovw, const char *field, const char *value);
-int iovw_put_string_field_free(struct iovec_wrapper *iovw, const char *field, char *value);
-void iovw_rebase(struct iovec_wrapper *iovw, char *old, char *new);
-size_t iovw_size(struct iovec_wrapper *iovw);
-
-void iovec_array_free(struct iovec *iov, size_t n);
diff --git a/src/libnm-systemd-shared/src/basic/iovec-util.h b/src/libnm-systemd-shared/src/basic/iovec-util.h
new file mode 100644
index 0000000000..8cfa5717dc
--- /dev/null
+++ b/src/libnm-systemd-shared/src/basic/iovec-util.h
@@ -0,0 +1,99 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+#pragma once
+
+#include <stdbool.h>
+#include <sys/types.h>
+#include <sys/uio.h>
+
+#include "alloc-util.h"
+#include "macro.h"
+
+/* An iovec pointing to a single NUL byte */
+#define IOVEC_NUL_BYTE (const struct iovec) { \
+ .iov_base = (void*) (const uint8_t[1]) { 0 }, \
+ .iov_len = 1, \
+ }
+
+size_t iovec_total_size(const struct iovec *iovec, size_t n);
+
+bool iovec_increment(struct iovec *iovec, size_t n, size_t k);
+
+/* This accepts both const and non-const pointers */
+#define IOVEC_MAKE(base, len) \
+ (struct iovec) { \
+ .iov_base = (void*) (base), \
+ .iov_len = (len), \
+ }
+
+static inline struct iovec* iovec_make_string(struct iovec *iovec, const char *s) {
+ assert(iovec);
+ /* We don't use strlen_ptr() here, because we don't want to include string-util.h for now */
+ *iovec = IOVEC_MAKE(s, s ? strlen(s) : 0);
+ return iovec;
+}
+
+#define IOVEC_MAKE_STRING(s) \
+ *iovec_make_string(&(struct iovec) {}, s)
+
+#define CONST_IOVEC_MAKE_STRING(s) \
+ (const struct iovec) { \
+ .iov_base = (char*) s, \
+ .iov_len = STRLEN(s), \
+ }
+
+static inline void iovec_done(struct iovec *iovec) {
+ /* A _cleanup_() helper that frees the iov_base in the iovec */
+ assert(iovec);
+
+ iovec->iov_base = mfree(iovec->iov_base);
+ iovec->iov_len = 0;
+}
+
+static inline void iovec_done_erase(struct iovec *iovec) {
+ assert(iovec);
+
+ iovec->iov_base = erase_and_free(iovec->iov_base);
+ iovec->iov_len = 0;
+}
+
+static inline bool iovec_is_set(const struct iovec *iovec) {
+ /* Checks if the iovec points to a non-empty chunk of memory */
+ return iovec && iovec->iov_len > 0 && iovec->iov_base;
+}
+
+static inline bool iovec_is_valid(const struct iovec *iovec) {
+ /* Checks if the iovec is either NULL, empty or points to a valid bit of memory */
+ return !iovec || (iovec->iov_base || iovec->iov_len == 0);
+}
+
+char* set_iovec_string_field(struct iovec *iovec, size_t *n_iovec, const char *field, const char *value);
+char* set_iovec_string_field_free(struct iovec *iovec, size_t *n_iovec, const char *field, char *value);
+
+void iovec_array_free(struct iovec *iovec, size_t n_iovec);
+
+static inline int iovec_memcmp(const struct iovec *a, const struct iovec *b) {
+
+ if (a == b)
+ return 0;
+
+ return memcmp_nn(a ? a->iov_base : NULL,
+ a ? a->iov_len : 0,
+ b ? b->iov_base : NULL,
+ b ? b->iov_len : 0);
+}
+
+static inline struct iovec *iovec_memdup(const struct iovec *source, struct iovec *ret) {
+ assert(ret);
+
+ if (!iovec_is_set(source))
+ *ret = (struct iovec) {};
+ else {
+ void *p = memdup(source->iov_base, source->iov_len);
+ if (!p)
+ return NULL;
+
+ *ret = IOVEC_MAKE(p, source->iov_len);
+ }
+
+ return ret;
+}
diff --git a/src/libnm-systemd-shared/src/basic/list.h b/src/libnm-systemd-shared/src/basic/list.h
index e4e5dff3ea..10e69541d4 100644
--- a/src/libnm-systemd-shared/src/basic/list.h
+++ b/src/libnm-systemd-shared/src/basic/list.h
@@ -192,6 +192,18 @@
_p; \
})
+#define LIST_CLEAR(name, head, free_func) \
+ _LIST_CLEAR(name, head, free_func, UNIQ_T(elem, UNIQ))
+
+/* Clear the list, destroying each element with free_func */
+#define _LIST_CLEAR(name, head, free_func, elem) \
+ ({ \
+ typeof(head) elem; \
+ while ((elem = LIST_POP(name, head))) \
+ free_func(elem); \
+ head; \
+ })
+
/* Now include "macro.h", because we want our definition of assert() which the macros above use. We include
* it down here instead of up top, since macro.h pulls in log.h which in turn needs our own definitions. */
#include "macro.h"
diff --git a/src/libnm-systemd-shared/src/basic/locale-util.c b/src/libnm-systemd-shared/src/basic/locale-util.c
index 2f3701eba6..78564a74a0 100644
--- a/src/libnm-systemd-shared/src/basic/locale-util.c
+++ b/src/libnm-systemd-shared/src/basic/locale-util.c
@@ -19,6 +19,7 @@
#include "fileio.h"
#include "hashmap.h"
#include "locale-util.h"
+#include "missing_syscall.h"
#include "path-util.h"
#include "set.h"
#include "string-table.h"
@@ -223,7 +224,7 @@ int get_locales(char ***ret) {
locales = set_free(locales);
r = getenv_bool("SYSTEMD_LIST_NON_UTF8_LOCALES");
- if (r == -ENXIO || r == 0) {
+ if (IN_SET(r, -ENXIO, 0)) {
char **a, **b;
/* Filter out non-UTF-8 locales, because it's 2019, by default */
@@ -282,11 +283,6 @@ int locale_is_installed(const char *name) {
return true;
}
-
-void init_gettext(void) {
- setlocale(LC_ALL, "");
- textdomain(GETTEXT_PACKAGE);
-}
#endif /* NM_IGNORED */
bool is_locale_utf8(void) {
@@ -307,6 +303,12 @@ bool is_locale_utf8(void) {
} else if (r != -ENXIO)
log_debug_errno(r, "Failed to parse $SYSTEMD_UTF8, ignoring: %m");
+ /* This function may be called from libsystemd, and setlocale() is not thread safe. Assuming yes. */
+ if (gettid() != raw_getpid()) {
+ cached_answer = true;
+ goto out;
+ }
+
if (!setlocale(LC_ALL, "")) {
cached_answer = true;
goto out;
@@ -345,11 +347,7 @@ out:
#if 0 /* NM_IGNORED */
void locale_variables_free(char *l[_VARIABLE_LC_MAX]) {
- if (!l)
- return;
-
- for (LocaleVariable i = 0; i < _VARIABLE_LC_MAX; i++)
- l[i] = mfree(l[i]);
+ free_many_charp(l, _VARIABLE_LC_MAX);
}
void locale_variables_simplify(char *l[_VARIABLE_LC_MAX]) {
diff --git a/src/libnm-systemd-shared/src/basic/locale-util.h b/src/libnm-systemd-shared/src/basic/locale-util.h
index 8990cb6a75..81fe8d1084 100644
--- a/src/libnm-systemd-shared/src/basic/locale-util.h
+++ b/src/libnm-systemd-shared/src/basic/locale-util.h
@@ -33,9 +33,8 @@ int get_locales(char ***l);
bool locale_is_valid(const char *name);
int locale_is_installed(const char *name);
-#define _(String) gettext(String)
+#define _(String) dgettext(GETTEXT_PACKAGE, String)
#define N_(String) String
-void init_gettext(void);
bool is_locale_utf8(void);
diff --git a/src/libnm-systemd-shared/src/basic/lock-util.h b/src/libnm-systemd-shared/src/basic/lock-util.h
index e7744476bb..91b332f803 100644
--- a/src/libnm-systemd-shared/src/basic/lock-util.h
+++ b/src/libnm-systemd-shared/src/basic/lock-util.h
@@ -34,9 +34,12 @@ void unposix_unlockpp(int **fd);
_cleanup_(unposix_unlockpp) _unused_ int *CONCATENATE(_cleanup_unposix_unlock_, UNIQ) = &(fd)
typedef enum LockType {
+ LOCK_NONE, /* Don't lock the file descriptor. Useful if you need to conditionally lock a file. */
LOCK_BSD,
LOCK_POSIX,
LOCK_UNPOSIX,
} LockType;
int lock_generic(int fd, LockType type, int operation);
+
+int lock_generic_with_timeout(int fd, LockType type, int operation, usec_t timeout);
diff --git a/src/libnm-systemd-shared/src/basic/log.h b/src/libnm-systemd-shared/src/basic/log.h
index eb7b51cb82..cc59b0775c 100644
--- a/src/libnm-systemd-shared/src/basic/log.h
+++ b/src/libnm-systemd-shared/src/basic/log.h
@@ -467,6 +467,9 @@ void log_set_open_when_needed(bool b);
* stderr, the console or kmsg */
void log_set_prohibit_ipc(bool b);
+void log_set_assert_return_is_critical(bool b);
+bool log_get_assert_return_is_critical(void) _pure_;
+
int log_dup_console(void);
#if 0 /* NM_IGNORED */
diff --git a/src/libnm-systemd-shared/src/basic/macro.h b/src/libnm-systemd-shared/src/basic/macro.h
index ce7350cb0e..eec8cba673 100644
--- a/src/libnm-systemd-shared/src/basic/macro.h
+++ b/src/libnm-systemd-shared/src/basic/macro.h
@@ -14,7 +14,7 @@
/* Note: on GCC "no_sanitize_address" is a function attribute only, on llvm it may also be applied to global
* variables. We define a specific macro which knows this. Note that on GCC we don't need this decorator so much, since
- * our primary usecase for this attribute is registration structures placed in named ELF sections which shall not be
+ * our primary use case for this attribute is registration structures placed in named ELF sections which shall not be
* padded, but GCC doesn't pad those anyway if AddressSanitizer is enabled. */
#if HAS_FEATURE_ADDRESS_SANITIZER && defined(__clang__)
#define _variable_no_sanitize_address_ __attribute__((__no_sanitize_address__))
@@ -84,7 +84,7 @@
#define REENABLE_WARNING
#endif
-/* automake test harness */
+/* test harness */
#define EXIT_TEST_SKIP 77
/* builtins */
@@ -96,6 +96,13 @@
#error "neither int nor long are four bytes long?!?"
#endif
+static inline uint64_t u64_multiply_safe(uint64_t a, uint64_t b) {
+ if (_unlikely_(a != 0 && b > (UINT64_MAX / a)))
+ return 0; /* overflow */
+
+ return a * b;
+}
+
/* align to next higher power-of-2 (except for: 0 => 0, overflow => 0) */
static inline unsigned long ALIGN_POWER2(unsigned long u) {
@@ -198,7 +205,7 @@ static inline int __coverity_check_and_return__(int condition) {
/* We override the glibc assert() here. */
#undef assert
#ifdef NDEBUG
-#define assert(expr) do {} while (false)
+#define assert(expr) ({ if (!(expr)) __builtin_unreachable(); })
#else
#define assert(expr) assert_message_se(expr, #expr)
#endif
@@ -304,12 +311,6 @@ static inline int __coverity_check_and_return__(int condition) {
/* Pointers range from NULL to POINTER_MAX */
#define POINTER_MAX ((void*) UINTPTR_MAX)
-/* Iterates through a specified list of pointers. Accepts NULL pointers, but uses POINTER_MAX as internal marker for EOL. */
-#define FOREACH_POINTER(p, x, ...) \
- for (typeof(p) *_l = (typeof(p)[]) { ({ p = x; }), ##__VA_ARGS__, POINTER_MAX }; \
- p != (typeof(p)) POINTER_MAX; \
- p = *(++_l))
-
#define _FOREACH_ARRAY(i, array, num, m, end) \
for (typeof(array[0]) *i = (array), *end = ({ \
typeof(num) m = (num); \
@@ -319,31 +320,6 @@ static inline int __coverity_check_and_return__(int condition) {
#define FOREACH_ARRAY(i, array, num) \
_FOREACH_ARRAY(i, array, num, UNIQ_T(m, UNIQ), UNIQ_T(end, UNIQ))
-#define DEFINE_TRIVIAL_DESTRUCTOR(name, type, func) \
- static inline void name(type *p) { \
- func(p); \
- }
-
-/* When func() returns the void value (NULL, -1, …) of the appropriate type */
-#define DEFINE_TRIVIAL_CLEANUP_FUNC(type, func) \
- static inline void func##p(type *p) { \
- if (*p) \
- *p = func(*p); \
- }
-
-/* When func() doesn't return the appropriate type, set variable to empty afterwards.
- * The func() may be provided by a dynamically loaded shared library, hence add an assertion. */
-#define DEFINE_TRIVIAL_CLEANUP_FUNC_FULL(type, func, empty) \
- static inline void func##p(type *p) { \
- if (*p != (empty)) { \
- DISABLE_WARNING_ADDRESS; \
- assert(func); \
- REENABLE_WARNING; \
- func(*p); \
- *p = (empty); \
- } \
- }
-
#define _DEFINE_TRIVIAL_REF_FUNC(type, name, scope) \
scope type *name##_ref(type *p) { \
if (!p) \
@@ -443,13 +419,13 @@ assert_cc(sizeof(dummy_t) == 0);
_q && _q > (base) ? &_q[-1] : NULL; \
})
-/* Iterate through each variadic arg. All must be the same type as 'entry' or must be implicitly
+/* Iterate through each argument passed. All must be the same type as 'entry' or must be implicitly
* convertible. The iteration variable 'entry' must already be defined. */
-#define VA_ARGS_FOREACH(entry, ...) \
- _VA_ARGS_FOREACH(entry, UNIQ_T(_entries_, UNIQ), UNIQ_T(_current_, UNIQ), ##__VA_ARGS__)
-#define _VA_ARGS_FOREACH(entry, _entries_, _current_, ...) \
- for (typeof(entry) _entries_[] = { __VA_ARGS__ }, *_current_ = _entries_; \
- ((long)(_current_ - _entries_) < (long)ELEMENTSOF(_entries_)) && ({ entry = *_current_; true; }); \
+#define FOREACH_ARGUMENT(entry, ...) \
+ _FOREACH_ARGUMENT(entry, UNIQ_T(_entries_, UNIQ), UNIQ_T(_current_, UNIQ), UNIQ_T(_va_sentinel_, UNIQ), ##__VA_ARGS__)
+#define _FOREACH_ARGUMENT(entry, _entries_, _current_, _va_sentinel_, ...) \
+ for (typeof(entry) _va_sentinel_[1] = {}, _entries_[] = { __VA_ARGS__ __VA_OPT__(,) _va_sentinel_[0] }, *_current_ = _entries_; \
+ ((long)(_current_ - _entries_) < (long)(ELEMENTSOF(_entries_) - 1)) && ({ entry = *_current_; true; }); \
_current_++)
#include "log.h"
diff --git a/src/libnm-systemd-shared/src/basic/memory-util.c b/src/libnm-systemd-shared/src/basic/memory-util.c
index c1e0a742b1..789e96a9c3 100644
--- a/src/libnm-systemd-shared/src/basic/memory-util.c
+++ b/src/libnm-systemd-shared/src/basic/memory-util.c
@@ -41,3 +41,19 @@ bool memeqbyte(uint8_t byte, const void *data, size_t length) {
/* Now we know first 16 bytes match, memcmp() with self. */
return memcmp(data, p + 16, length) == 0;
}
+
+void *memdup_reverse(const void *mem, size_t size) {
+ assert(mem);
+ assert(size != 0);
+
+ void *p = malloc(size);
+ if (!p)
+ return NULL;
+
+ uint8_t *p_dst = p;
+ const uint8_t *p_src = mem;
+ for (size_t i = 0, k = size; i < size; i++, k--)
+ p_dst[i] = p_src[k-1];
+
+ return p;
+}
diff --git a/src/libnm-systemd-shared/src/basic/memory-util.h b/src/libnm-systemd-shared/src/basic/memory-util.h
index d26a0918e1..294aed67df 100644
--- a/src/libnm-systemd-shared/src/basic/memory-util.h
+++ b/src/libnm-systemd-shared/src/basic/memory-util.h
@@ -12,9 +12,12 @@
#include "memory-util-fundamental.h"
size_t page_size(void) _pure_;
-#define PAGE_ALIGN(l) ALIGN_TO((l), page_size())
-#define PAGE_ALIGN_DOWN(l) ((l) & ~(page_size() - 1))
-#define PAGE_OFFSET(l) ((l) & (page_size() - 1))
+#define PAGE_ALIGN(l) ALIGN_TO(l, page_size())
+#define PAGE_ALIGN_U64(l) ALIGN_TO_U64(l, page_size())
+#define PAGE_ALIGN_DOWN(l) ALIGN_DOWN(l, page_size())
+#define PAGE_ALIGN_DOWN_U64(l) ALIGN_DOWN_U64(l, page_size())
+#define PAGE_OFFSET(l) ALIGN_OFFSET(l, page_size())
+#define PAGE_OFFSET_U64(l) ALIGN_OFFSET_U64(l, page_size())
/* Normal memcpy() requires src to be nonnull. We do nothing if n is 0. */
static inline void *memcpy_safe(void *dst, const void *src, size_t n) {
@@ -47,13 +50,6 @@ static inline int memcmp_nn(const void *s1, size_t n1, const void *s2, size_t n2
?: CMP(n1, n2);
}
-#define memzero(x,l) \
- ({ \
- size_t _l_ = (l); \
- if (_l_ > 0) \
- memset(x, 0, _l_); \
- })
-
#define zero(x) (memzero(&(x), sizeof(x)))
bool memeqbyte(uint8_t byte, const void *data, size_t length);
@@ -112,36 +108,5 @@ static inline void erase_char(char *p) {
explicit_bzero_safe(p, sizeof(char));
}
-/* An automatic _cleanup_-like logic for destroy arrays (i.e. pointers + size) when leaving scope */
-typedef struct ArrayCleanup {
- void **parray;
- size_t *pn;
- free_array_func_t pfunc;
-} ArrayCleanup;
-
-static inline void array_cleanup(const ArrayCleanup *c) {
- assert(c);
-
- assert(!c->parray == !c->pn);
-
- if (!c->parray)
- return;
-
- if (*c->parray) {
- assert(c->pfunc);
- c->pfunc(*c->parray, *c->pn);
- *c->parray = NULL;
- }
-
- *c->pn = 0;
-}
-
-#define CLEANUP_ARRAY(array, n, func) \
- _cleanup_(array_cleanup) _unused_ const ArrayCleanup CONCATENATE(_cleanup_array_, UNIQ) = { \
- .parray = (void**) &(array), \
- .pn = &(n), \
- .pfunc = (free_array_func_t) ({ \
- void (*_f)(typeof(array[0]) *a, size_t b) = func; \
- _f; \
- }), \
- }
+/* Makes a copy of the buffer with reversed order of bytes */
+void *memdup_reverse(const void *mem, size_t size);
diff --git a/src/libnm-systemd-shared/src/basic/missing_socket.h b/src/libnm-systemd-shared/src/basic/missing_socket.h
index a4f6836fd4..ffda7cc684 100644
--- a/src/libnm-systemd-shared/src/basic/missing_socket.h
+++ b/src/libnm-systemd-shared/src/basic/missing_socket.h
@@ -7,7 +7,6 @@
#if HAVE_LINUX_VM_SOCKETS_H
#include <linux/vm_sockets.h>
#else
-#define VMADDR_CID_ANY -1U
struct sockaddr_vm {
unsigned short svm_family;
unsigned short svm_reserved1;
@@ -22,6 +21,26 @@ struct sockaddr_vm {
#endif /* !HAVE_LINUX_VM_SOCKETS_H */
#endif /* NM_IGNORED */
+#ifndef VMADDR_CID_ANY
+#define VMADDR_CID_ANY -1U
+#endif
+
+#ifndef VMADDR_CID_HYPERVISOR
+#define VMADDR_CID_HYPERVISOR 0U
+#endif
+
+#ifndef VMADDR_CID_LOCAL
+#define VMADDR_CID_LOCAL 1U
+#endif
+
+#ifndef VMADDR_CID_HOST
+#define VMADDR_CID_HOST 2U
+#endif
+
+#ifndef VMADDR_PORT_ANY
+#define VMADDR_PORT_ANY -1U
+#endif
+
#ifndef AF_VSOCK
#define AF_VSOCK 40
#endif
@@ -34,6 +53,10 @@ struct sockaddr_vm {
#define SO_PEERGROUPS 59
#endif
+#ifndef SO_PEERPIDFD
+#define SO_PEERPIDFD 77
+#endif
+
#ifndef SO_BINDTOIFINDEX
#define SO_BINDTOIFINDEX 62
#endif
diff --git a/src/libnm-systemd-shared/src/basic/missing_stat.h b/src/libnm-systemd-shared/src/basic/missing_stat.h
index 8b39d4f44f..18a15ab00a 100644
--- a/src/libnm-systemd-shared/src/basic/missing_stat.h
+++ b/src/libnm-systemd-shared/src/basic/missing_stat.h
@@ -9,7 +9,7 @@
#include <linux/stat.h>
#endif
-/* Thew newest definition we are aware of (fa2fcf4f1df1559a0a4ee0f46915b496cc2ebf60; 5.8) */
+/* The newest definition we are aware of (fa2fcf4f1df1559a0a4ee0f46915b496cc2ebf60; 5.8) */
#define STATX_DEFINITION { \
__u32 stx_mask; \
__u32 stx_blksize; \
diff --git a/src/libnm-systemd-shared/src/basic/missing_syscall.h b/src/libnm-systemd-shared/src/basic/missing_syscall.h
index 610a7cef2e..a04633510d 100644
--- a/src/libnm-systemd-shared/src/basic/missing_syscall.h
+++ b/src/libnm-systemd-shared/src/basic/missing_syscall.h
@@ -34,6 +34,21 @@
/* ======================================================================= */
+#if !HAVE_FCHMODAT2
+static inline int missing_fchmodat2(int dirfd, const char *path, mode_t mode, int flags) {
+# ifdef __NR_fchmodat2
+ return syscall(__NR_fchmodat2, dirfd, path, mode, flags);
+# else
+ errno = ENOSYS;
+ return -1;
+# endif
+}
+
+# define fchmodat2 missing_fchmodat2
+#endif
+
+/* ======================================================================= */
+
#if !HAVE_PIVOT_ROOT
static inline int missing_pivot_root(const char *new_root, const char *put_old) {
return syscall(__NR_pivot_root, new_root, put_old);
@@ -546,6 +561,10 @@ static inline int missing_open_tree(
/* ======================================================================= */
+#ifndef MOVE_MOUNT_BENEATH
+#define MOVE_MOUNT_BENEATH 0x00000200
+#endif
+
#if !HAVE_MOVE_MOUNT
#ifndef MOVE_MOUNT_F_EMPTY_PATH
@@ -662,3 +681,17 @@ static inline ssize_t missing_getdents64(int fd, void *buffer, size_t length) {
# define getdents64 missing_getdents64
#endif
#endif /* NM_IGNORED */
+
+/* ======================================================================= */
+
+/* glibc does not provide clone() on ia64, only clone2(). Not only that, but it also doesn't provide a
+ * prototype, only the symbol in the shared library (it provides a prototype for clone(), but not the
+ * symbol in the shared library). */
+#if defined(__ia64__)
+int __clone2(int (*fn)(void *), void *stack_base, size_t stack_size, int flags, void *arg);
+#define HAVE_CLONE 0
+#else
+/* We know that everywhere else clone() is available, so we don't bother with a meson check (that takes time
+ * at build time) and just define it. Once the kernel drops ia64 support, we can drop this too. */
+#define HAVE_CLONE 1
+#endif
diff --git a/src/libnm-systemd-shared/src/basic/namespace-util.h b/src/libnm-systemd-shared/src/basic/namespace-util.h
new file mode 100644
index 0000000000..d1d015612f
--- /dev/null
+++ b/src/libnm-systemd-shared/src/basic/namespace-util.h
@@ -0,0 +1,55 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+#pragma once
+
+#include <sys/types.h>
+
+typedef enum NamespaceType {
+ NAMESPACE_CGROUP,
+ NAMESPACE_IPC,
+ NAMESPACE_NET,
+ NAMESPACE_MOUNT,
+ NAMESPACE_PID,
+ NAMESPACE_USER,
+ NAMESPACE_UTS,
+ NAMESPACE_TIME,
+ _NAMESPACE_TYPE_MAX,
+ _NAMESPACE_TYPE_INVALID = -EINVAL,
+} NamespaceType;
+
+extern const struct namespace_info {
+ const char *proc_name;
+ const char *proc_path;
+ unsigned int clone_flag;
+} namespace_info[_NAMESPACE_TYPE_MAX + 1];
+
+int namespace_open(
+ pid_t pid,
+ int *ret_pidns_fd,
+ int *ret_mntns_fd,
+ int *ret_netns_fd,
+ int *ret_userns_fd,
+ int *ret_root_fd);
+int namespace_enter(int pidns_fd, int mntns_fd, int netns_fd, int userns_fd, int root_fd);
+
+int fd_is_ns(int fd, unsigned long nsflag);
+
+int detach_mount_namespace(void);
+
+static inline bool userns_shift_range_valid(uid_t shift, uid_t range) {
+ /* Checks that the specified userns range makes sense, i.e. contains at least one UID, and the end
+ * doesn't overflow uid_t. */
+
+ assert_cc((uid_t) -1 > 0); /* verify that uid_t is unsigned */
+
+ if (range <= 0)
+ return false;
+
+ if (shift > (uid_t) -1 - range)
+ return false;
+
+ return true;
+}
+
+int userns_acquire(const char *uid_map, const char *gid_map);
+int netns_acquire(void);
+int in_same_namespace(pid_t pid1, pid_t pid2, NamespaceType type);
diff --git a/src/libnm-systemd-shared/src/basic/ordered-set.c b/src/libnm-systemd-shared/src/basic/ordered-set.c
index f402bb5b0c..ae50070f17 100644
--- a/src/libnm-systemd-shared/src/basic/ordered-set.c
+++ b/src/libnm-systemd-shared/src/basic/ordered-set.c
@@ -93,13 +93,16 @@ void ordered_set_print(FILE *f, const char *field, OrderedSet *s) {
bool space = false;
char *p;
+ assert(f);
+ assert(field);
+
if (ordered_set_isempty(s))
return;
fputs(field, f);
ORDERED_SET_FOREACH(p, s)
- fputs_with_space(f, p, NULL, &space);
+ fputs_with_separator(f, p, NULL, &space);
fputc('\n', f);
}
diff --git a/src/libnm-systemd-shared/src/basic/parse-util.c b/src/libnm-systemd-shared/src/basic/parse-util.c
index 2b22039c1c..34a5375f85 100644
--- a/src/libnm-systemd-shared/src/basic/parse-util.c
+++ b/src/libnm-systemd-shared/src/basic/parse-util.c
@@ -47,6 +47,24 @@ int parse_boolean(const char *v) {
}
#if 0 /* NM_IGNORED */
+int parse_tristate_full(const char *v, const char *third, int *ret) {
+ int r;
+
+ if (isempty(v) || streq_ptr(v, third)) { /* Empty string is always taken as the third/invalid/auto state */
+ if (ret)
+ *ret = -1;
+ } else {
+ r = parse_boolean(v);
+ if (r < 0)
+ return r;
+
+ if (ret)
+ *ret = r;
+ }
+
+ return 0;
+}
+
int parse_pid(const char *s, pid_t* ret_pid) {
unsigned long ul = 0;
pid_t pid;
@@ -110,8 +128,7 @@ int parse_ifindex(const char *s) {
#if 0 /* NM_IGNORED */
int parse_mtu(int family, const char *s, uint32_t *ret) {
- uint64_t u;
- size_t m;
+ uint64_t u, m;
int r;
r = parse_size(s, 1024, &u);
@@ -121,10 +138,16 @@ int parse_mtu(int family, const char *s, uint32_t *ret) {
if (u > UINT32_MAX)
return -ERANGE;
- if (family == AF_INET6)
+ switch (family) {
+ case AF_INET:
+ m = IPV4_MIN_MTU; /* This is 68 */
+ break;
+ case AF_INET6:
m = IPV6_MIN_MTU; /* This is 1280 */
- else
- m = IPV4_MIN_MTU; /* For all other protocols, including 'unspecified' we assume the IPv4 minimal MTU */
+ break;
+ default:
+ m = 0;
+ }
if (u < m)
return -ERANGE;
@@ -431,6 +454,21 @@ int safe_atou_full(const char *s, unsigned base, unsigned *ret_u) {
return 0;
}
+int safe_atou_bounded(const char *s, unsigned min, unsigned max, unsigned *ret) {
+ unsigned v;
+ int r;
+
+ r = safe_atou(s, &v);
+ if (r < 0)
+ return r;
+
+ if (v < min || v > max)
+ return -ERANGE;
+
+ *ret = v;
+ return 0;
+}
+
int safe_atoi(const char *s, int *ret_i) {
unsigned base = 0;
char *x = NULL;
@@ -660,7 +698,7 @@ int parse_ip_port(const char *s, uint16_t *ret) {
return 0;
}
-int parse_ip_port_range(const char *s, uint16_t *low, uint16_t *high) {
+int parse_ip_port_range(const char *s, uint16_t *low, uint16_t *high, bool allow_zero) {
unsigned l, h;
int r;
@@ -668,7 +706,10 @@ int parse_ip_port_range(const char *s, uint16_t *low, uint16_t *high) {
if (r < 0)
return r;
- if (l <= 0 || l > 65535 || h <= 0 || h > 65535)
+ if (l > 65535 || h > 65535)
+ return -EINVAL;
+
+ if (!allow_zero && (l == 0 || h == 0))
return -EINVAL;
if (h < l)
@@ -754,4 +795,23 @@ int parse_loadavg_fixed_point(const char *s, loadavg_t *ret) {
return store_loadavg_fixed_point(i, f, ret);
}
+
+/* Limitations are described in https://www.netfilter.org/projects/nftables/manpage.html and
+ * https://bugzilla.netfilter.org/show_bug.cgi?id=1175 */
+bool nft_identifier_valid(const char *id) {
+ if (!id)
+ return false;
+
+ size_t len = strlen(id);
+ if (len == 0 || len > 31)
+ return false;
+
+ if (!ascii_isalpha(id[0]))
+ return false;
+
+ for (size_t i = 1; i < len; i++)
+ if (!ascii_isalpha(id[i]) && !ascii_isdigit(id[i]) && !IN_SET(id[i], '/', '\\', '_', '.'))
+ return false;
+ return true;
+}
#endif /* NM_IGNORED */
diff --git a/src/libnm-systemd-shared/src/basic/parse-util.h b/src/libnm-systemd-shared/src/basic/parse-util.h
index c480407c2a..c12988ef20 100644
--- a/src/libnm-systemd-shared/src/basic/parse-util.h
+++ b/src/libnm-systemd-shared/src/basic/parse-util.h
@@ -12,6 +12,10 @@
typedef unsigned long loadavg_t;
int parse_boolean(const char *v) _pure_;
+int parse_tristate_full(const char *v, const char *third, int *ret);
+static inline int parse_tristate(const char *v, int *ret) {
+ return parse_tristate_full(v, NULL, ret);
+}
int parse_pid(const char *s, pid_t* ret_pid);
int parse_mode(const char *s, mode_t *ret);
int parse_ifindex(const char *s);
@@ -30,11 +34,12 @@ int parse_fd(const char *t);
#define SAFE_ATO_MASK_FLAGS(base) ((base) & ~SAFE_ATO_ALL_FLAGS)
int safe_atou_full(const char *s, unsigned base, unsigned *ret_u);
-
static inline int safe_atou(const char *s, unsigned *ret_u) {
return safe_atou_full(s, 0, ret_u);
}
+int safe_atou_bounded(const char *s, unsigned min, unsigned max, unsigned *ret);
+
int safe_atoi(const char *s, int *ret_i);
int safe_atolli(const char *s, long long int *ret_i);
@@ -134,7 +139,7 @@ int parse_fractional_part_u(const char **s, size_t digits, unsigned *res);
int parse_nice(const char *p, int *ret);
int parse_ip_port(const char *s, uint16_t *ret);
-int parse_ip_port_range(const char *s, uint16_t *low, uint16_t *high);
+int parse_ip_port_range(const char *s, uint16_t *low, uint16_t *high, bool allow_zero);
int parse_ip_prefix_length(const char *s, int *ret);
@@ -152,3 +157,5 @@ int parse_oom_score_adjust(const char *s, int *ret);
* to a loadavg_t. */
int store_loadavg_fixed_point(unsigned long i, unsigned long f, loadavg_t *ret);
int parse_loadavg_fixed_point(const char *s, loadavg_t *ret);
+
+bool nft_identifier_valid(const char *id);
diff --git a/src/libnm-systemd-shared/src/basic/path-util.c b/src/libnm-systemd-shared/src/basic/path-util.c
index a2af9e0ce2..638e4860a6 100644
--- a/src/libnm-systemd-shared/src/basic/path-util.c
+++ b/src/libnm-systemd-shared/src/basic/path-util.c
@@ -43,7 +43,7 @@ int path_split_and_make_absolute(const char *p, char ***ret) {
return r;
}
-char *path_make_absolute(const char *p, const char *prefix) {
+char* path_make_absolute(const char *p, const char *prefix) {
assert(p);
/* Makes every item in the list an absolute path by prepending
@@ -135,11 +135,9 @@ int path_make_relative(const char *from, const char *to, char **ret) {
return -ENOMEM;
} else {
/* 'to' is inside of 'from'. */
- result = strdup(t);
- if (!result)
- return -ENOMEM;
-
- path_simplify(result);
+ r = path_simplify_alloc(t, &result);
+ if (r < 0)
+ return r;
if (!path_is_valid(result))
return -EINVAL;
@@ -255,7 +253,7 @@ int path_strv_make_absolute_cwd(char **l) {
return 0;
}
-char **path_strv_resolve(char **l, const char *root) {
+char** path_strv_resolve(char **l, const char *root) {
unsigned k = 0;
bool enomem = false;
int r;
@@ -336,7 +334,7 @@ char **path_strv_resolve(char **l, const char *root) {
return l;
}
-char **path_strv_resolve_uniq(char **l, const char *root) {
+char** path_strv_resolve_uniq(char **l, const char *root) {
if (strv_isempty(l))
return l;
@@ -348,9 +346,9 @@ char **path_strv_resolve_uniq(char **l, const char *root) {
}
#endif /* NM_IGNORED */
-char *path_simplify(char *path) {
- bool add_slash = false;
- char *f = ASSERT_PTR(path);
+char* path_simplify_full(char *path, PathSimplifyFlags flags) {
+ bool add_slash = false, keep_trailing_slash, absolute, beginning = true;
+ char *f = path;
int r;
/* Removes redundant inner and trailing slashes. Also removes unnecessary dots.
@@ -358,13 +356,17 @@ char *path_simplify(char *path) {
*
* ///foo//./bar/. becomes /foo/bar
* .//./foo//./bar/. becomes foo/bar
+ * /../foo/bar becomes /foo/bar
+ * /../foo/bar/.. becomes /foo/bar/..
*/
if (isempty(path))
return path;
- if (path_is_absolute(path))
- f++;
+ keep_trailing_slash = FLAGS_SET(flags, PATH_SIMPLIFY_KEEP_TRAILING_SLASH) && endswith(path, "/");
+
+ absolute = path_is_absolute(path);
+ f += absolute; /* Keep leading /, if present. */
for (const char *p = f;;) {
const char *e;
@@ -373,11 +375,17 @@ char *path_simplify(char *path) {
if (r == 0)
break;
+ if (r > 0 && absolute && beginning && path_startswith(e, ".."))
+ /* If we're at the beginning of an absolute path, we can safely skip ".." */
+ continue;
+
+ beginning = false;
+
if (add_slash)
*f++ = '/';
if (r < 0) {
- /* if path is invalid, then refuse to simplify remaining part. */
+ /* if path is invalid, then refuse to simplify the remaining part. */
memmove(f, p, strlen(p) + 1);
return path;
}
@@ -392,11 +400,14 @@ char *path_simplify(char *path) {
if (f == path)
*f++ = '.';
+ if (*(f-1) != '/' && keep_trailing_slash)
+ *f++ = '/';
+
*f = '\0';
return path;
}
-char *path_startswith_full(const char *path, const char *prefix, bool accept_dot_dot) {
+char* path_startswith_full(const char *path, const char *prefix, bool accept_dot_dot) {
assert(path);
assert(prefix);
@@ -489,10 +500,6 @@ int path_compare(const char *a, const char *b) {
}
}
-bool path_equal_or_inode_same(const char *a, const char *b, int flags) {
- return path_equal(a, b) || inode_same(a, b, flags) > 0;
-}
-
int path_compare_filename(const char *a, const char *b) {
_cleanup_free_ char *fa = NULL, *fb = NULL;
int r, j, k;
@@ -661,7 +668,14 @@ static int find_executable_impl(const char *name, const char *root, char **ret_f
return 0;
}
-int find_executable_full(const char *name, const char *root, char **exec_search_path, bool use_path_envvar, char **ret_filename, int *ret_fd) {
+int find_executable_full(
+ const char *name,
+ const char *root,
+ char **exec_search_path,
+ bool use_path_envvar,
+ char **ret_filename,
+ int *ret_fd) {
+
int last_error = -ENOENT, r = 0;
const char *p = NULL;
@@ -812,7 +826,7 @@ int fsck_exists_for_fstype(const char *fstype) {
}
#endif /* NM_IGNORED */
-static const char *skip_slash_or_dot(const char *p) {
+static const char* skip_slash_or_dot(const char *p) {
for (; !isempty(p); p++) {
if (*p == '/')
continue;
@@ -896,7 +910,7 @@ int path_find_first_component(const char **p, bool accept_dot_dot, const char **
return len;
}
-static const char *skip_slash_or_dot_backward(const char *path, const char *q) {
+static const char* skip_slash_or_dot_backward(const char *path, const char *q) {
assert(path);
assert(!q || q >= path);
@@ -1005,7 +1019,7 @@ int path_find_last_component(const char *path, bool accept_dot_dot, const char *
return len;
}
-const char *last_path_component(const char *path) {
+const char* last_path_component(const char *path) {
/* Finds the last component of the path, preserving the optional trailing slash that signifies a directory.
*
@@ -1126,17 +1140,19 @@ int path_extract_directory(const char *path, char **ret) {
if (!path_is_valid(a))
return -EINVAL;
- *ret = TAKE_PTR(a);
+ if (ret)
+ *ret = TAKE_PTR(a);
+
return 0;
}
-bool filename_is_valid(const char *p) {
+bool filename_part_is_valid(const char *p) {
const char *e;
- if (isempty(p))
- return false;
+ /* Checks f the specified string is OK to be *part* of a filename. This is different from
+ * filename_is_valid() as "." and ".." and "" are OK by this call, but not by filename_is_valid(). */
- if (dot_or_dot_dot(p)) /* Yes, in this context we consider "." and ".." invalid */
+ if (!p)
return false;
e = strchrnul(p, '/');
@@ -1149,6 +1165,17 @@ bool filename_is_valid(const char *p) {
return true;
}
+bool filename_is_valid(const char *p) {
+
+ if (isempty(p))
+ return false;
+
+ if (dot_or_dot_dot(p)) /* Yes, in this context we consider "." and ".." invalid */
+ return false;
+
+ return filename_part_is_valid(p);
+}
+
bool path_is_valid_full(const char *p, bool accept_dot_dot) {
if (isempty(p))
return false;
@@ -1265,9 +1292,16 @@ bool hidden_or_backup_file(const char *filename) {
bool is_device_path(const char *path) {
/* Returns true for paths that likely refer to a device, either by path in sysfs or to something in
- * /dev. */
+ * /dev. This accepts any path that starts with /dev/ or /sys/ and has something after that prefix.
+ * It does not actually resolve the path.
+ *
+ * Examples:
+ * /dev/sda, /dev/sda/foo, /sys/class, /dev/.., /sys/.., /./dev/foo → yes.
+ * /../dev/sda, /dev, /sys, /usr/path, /usr/../dev/sda → no.
+ */
- return PATH_STARTSWITH_SET(path, "/dev/", "/sys/");
+ const char *p = PATH_STARTSWITH_SET(ASSERT_PTR(path), "/dev/", "/sys/");
+ return !isempty(p);
}
bool valid_device_node_path(const char *path) {
diff --git a/src/libnm-systemd-shared/src/basic/path-util.h b/src/libnm-systemd-shared/src/basic/path-util.h
index fee6e8ee49..5bb51ff599 100644
--- a/src/libnm-systemd-shared/src/basic/path-util.h
+++ b/src/libnm-systemd-shared/src/basic/path-util.h
@@ -6,6 +6,7 @@
#include <stddef.h>
#include "macro.h"
+#include "stat-util.h"
#include "string-util.h"
#include "strv.h"
#include "time-util.h"
@@ -25,24 +26,14 @@
# define PATH_SBIN_BIN_NULSTR(x) PATH_NORMAL_SBIN_BIN_NULSTR(x)
#endif
-#define DEFAULT_PATH_NORMAL PATH_SBIN_BIN("/usr/local/") ":" PATH_SBIN_BIN("/usr/")
-#define DEFAULT_PATH_NORMAL_NULSTR PATH_SBIN_BIN_NULSTR("/usr/local/") PATH_SBIN_BIN_NULSTR("/usr/")
-#define DEFAULT_PATH_SPLIT_USR DEFAULT_PATH_NORMAL ":" PATH_SBIN_BIN("/")
-#define DEFAULT_PATH_SPLIT_USR_NULSTR DEFAULT_PATH_NORMAL_NULSTR PATH_SBIN_BIN_NULSTR("/")
+#define DEFAULT_PATH PATH_SBIN_BIN("/usr/local/") ":" PATH_SBIN_BIN("/usr/")
+#define DEFAULT_PATH_NULSTR PATH_SBIN_BIN_NULSTR("/usr/local/") PATH_SBIN_BIN_NULSTR("/usr/")
#define DEFAULT_PATH_COMPAT PATH_SPLIT_SBIN_BIN("/usr/local/") ":" PATH_SPLIT_SBIN_BIN("/usr/") ":" PATH_SPLIT_SBIN_BIN("/")
-#if HAVE_SPLIT_USR
-# define DEFAULT_PATH DEFAULT_PATH_SPLIT_USR
-# define DEFAULT_PATH_NULSTR DEFAULT_PATH_SPLIT_USR_NULSTR
-#else
-# define DEFAULT_PATH DEFAULT_PATH_NORMAL
-# define DEFAULT_PATH_NULSTR DEFAULT_PATH_NORMAL_NULSTR
-#endif
-#endif /* NM_IGNORED */
-
#ifndef DEFAULT_USER_PATH
# define DEFAULT_USER_PATH DEFAULT_PATH
#endif
+#endif /* NM_IGNORED */
static inline bool is_path(const char *p) {
if (!p) /* A NULL pointer is definitely not a path */
@@ -64,7 +55,7 @@ int safe_getcwd(char **ret);
int path_make_absolute_cwd(const char *p, char **ret);
int path_make_relative(const char *from, const char *to, char **ret);
int path_make_relative_parent(const char *from_child, const char *to, char **ret);
-char *path_startswith_full(const char *path, const char *prefix, bool accept_dot_dot) _pure_;
+char* path_startswith_full(const char *path, const char *prefix, bool accept_dot_dot) _pure_;
static inline char* path_startswith(const char *path, const char *prefix) {
return path_startswith_full(path, prefix, true);
}
@@ -79,13 +70,38 @@ static inline bool path_equal_filename(const char *a, const char *b) {
return path_compare_filename(a, b) == 0;
}
-bool path_equal_or_inode_same(const char *a, const char *b, int flags);
+static inline bool path_equal_or_inode_same(const char *a, const char *b, int flags) {
+ return path_equal(a, b) || inode_same(a, b, flags) > 0;
+}
char* path_extend_internal(char **x, ...);
#define path_extend(x, ...) path_extend_internal(x, __VA_ARGS__, POINTER_MAX)
#define path_join(...) path_extend_internal(NULL, __VA_ARGS__, POINTER_MAX)
-char* path_simplify(char *path);
+typedef enum PathSimplifyFlags {
+ PATH_SIMPLIFY_KEEP_TRAILING_SLASH = 1 << 0,
+} PathSimplifyFlags;
+
+char* path_simplify_full(char *path, PathSimplifyFlags flags);
+static inline char* path_simplify(char *path) {
+ return path_simplify_full(path, 0);
+}
+
+static inline int path_simplify_alloc(const char *path, char **ret) {
+ assert(ret);
+
+ if (!path) {
+ *ret = NULL;
+ return 0;
+ }
+
+ char *t = strdup(path);
+ if (!t)
+ return -ENOMEM;
+
+ *ret = path_simplify(t);
+ return 0;
+}
static inline bool path_equal_ptr(const char *a, const char *b) {
return !!a == !!b && (!a || path_equal(a, b));
@@ -142,7 +158,7 @@ int fsck_exists_for_fstype(const char *fstype);
char *_p, *_n; \
size_t _l; \
while (_path[0] == '/' && _path[1] == '/') \
- _path ++; \
+ _path++; \
if (isempty(_root)) \
_ret = _path; \
else { \
@@ -161,10 +177,11 @@ int fsck_exists_for_fstype(const char *fstype);
int path_find_first_component(const char **p, bool accept_dot_dot, const char **ret);
int path_find_last_component(const char *path, bool accept_dot_dot, const char **next, const char **ret);
-const char *last_path_component(const char *path);
+const char* last_path_component(const char *path);
int path_extract_filename(const char *path, char **ret);
int path_extract_directory(const char *path, char **ret);
+bool filename_part_is_valid(const char *p) _pure_;
bool filename_is_valid(const char *p) _pure_;
bool path_is_valid_full(const char *p, bool accept_dot_dot) _pure_;
static inline bool path_is_valid(const char *p) {
@@ -197,7 +214,7 @@ static inline const char *skip_dev_prefix(const char *p) {
}
bool empty_or_root(const char *path);
-static inline const char *empty_to_root(const char *path) {
+static inline const char* empty_to_root(const char *path) {
return isempty(path) ? "/" : path;
}
diff --git a/src/libnm-systemd-shared/src/basic/pidref.h b/src/libnm-systemd-shared/src/basic/pidref.h
new file mode 100644
index 0000000000..c440c8b0e0
--- /dev/null
+++ b/src/libnm-systemd-shared/src/basic/pidref.h
@@ -0,0 +1,78 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+#pragma once
+
+#include "macro.h"
+
+/* An embeddable structure carrying a reference to a process. Supposed to be used when tracking processes continuously. */
+typedef struct PidRef {
+ pid_t pid; /* always valid */
+ int fd; /* only valid if pidfd are available in the kernel, and we manage to get an fd */
+} PidRef;
+
+#define PIDREF_NULL (const PidRef) { .fd = -EBADF }
+
+/* Turns a pid_t into a PidRef structure on-the-fly *without* acquiring a pidfd for it. (As opposed to
+ * pidref_set_pid() which does so *with* acquiring one, see below) */
+#define PIDREF_MAKE_FROM_PID(x) (PidRef) { .pid = (x), .fd = -EBADF }
+
+static inline bool pidref_is_set(const PidRef *pidref) {
+ return pidref && pidref->pid > 0;
+}
+
+static inline bool pidref_equal(const PidRef *a, const PidRef *b) {
+
+ if (pidref_is_set(a)) {
+ if (!pidref_is_set(b))
+ return false;
+
+ return a->pid == b->pid;
+ }
+
+ return !pidref_is_set(b);
+}
+
+/* This turns a pid_t into a PidRef structure, and acquires a pidfd for it, if possible. (As opposed to
+ * PIDREF_MAKE_FROM_PID() above, which does not acquire a pidfd.) */
+int pidref_set_pid(PidRef *pidref, pid_t pid);
+int pidref_set_pidstr(PidRef *pidref, const char *pid);
+int pidref_set_pidfd(PidRef *pidref, int fd);
+int pidref_set_pidfd_take(PidRef *pidref, int fd); /* takes ownership of the passed pidfd on success*/
+int pidref_set_pidfd_consume(PidRef *pidref, int fd); /* takes ownership of the passed pidfd in both success and failure */
+int pidref_set_parent(PidRef *ret);
+static inline int pidref_set_self(PidRef *pidref) {
+ return pidref_set_pid(pidref, 0);
+}
+
+bool pidref_is_self(const PidRef *pidref);
+
+void pidref_done(PidRef *pidref);
+PidRef *pidref_free(PidRef *pidref);
+DEFINE_TRIVIAL_CLEANUP_FUNC(PidRef*, pidref_free);
+
+int pidref_copy(const PidRef *pidref, PidRef *dest);
+int pidref_dup(const PidRef *pidref, PidRef **ret);
+
+int pidref_new_from_pid(pid_t pid, PidRef **ret);
+
+int pidref_kill(const PidRef *pidref, int sig);
+int pidref_kill_and_sigcont(const PidRef *pidref, int sig);
+int pidref_sigqueue(const PidRef *pidref, int sig, int value);
+
+int pidref_wait(const PidRef *pidref, siginfo_t *siginfo, int options);
+int pidref_wait_for_terminate(const PidRef *pidref, siginfo_t *ret);
+
+static inline void pidref_done_sigkill_wait(PidRef *pidref) {
+ if (!pidref_is_set(pidref))
+ return;
+
+ (void) pidref_kill(pidref, SIGKILL);
+ (void) pidref_wait_for_terminate(pidref, NULL);
+ pidref_done(pidref);
+}
+
+int pidref_verify(const PidRef *pidref);
+
+#define TAKE_PIDREF(p) TAKE_GENERIC((p), PidRef, PIDREF_NULL)
+
+extern const struct hash_ops pidref_hash_ops;
+extern const struct hash_ops pidref_hash_ops_free; /* Has destructor call for pidref_free(), i.e. expects heap allocated PidRef as keys */
diff --git a/src/libnm-systemd-shared/src/basic/prioq.c b/src/libnm-systemd-shared/src/basic/prioq.c
index 0af84bd273..b05b08da4a 100644
--- a/src/libnm-systemd-shared/src/basic/prioq.c
+++ b/src/libnm-systemd-shared/src/basic/prioq.c
@@ -215,7 +215,7 @@ static void remove_item(Prioq *q, struct prioq_item *i) {
}
}
-_pure_ static struct prioq_item* find_item(Prioq *q, void *data, unsigned *idx) {
+static struct prioq_item* find_item(Prioq *q, void *data, unsigned *idx) {
struct prioq_item *i;
assert(q);
diff --git a/src/libnm-systemd-shared/src/basic/process-util.c b/src/libnm-systemd-shared/src/basic/process-util.c
index 8601e0da54..0f1894b3af 100644
--- a/src/libnm-systemd-shared/src/basic/process-util.c
+++ b/src/libnm-systemd-shared/src/basic/process-util.c
@@ -7,6 +7,7 @@
#include <limits.h>
#include <linux/oom.h>
#include <pthread.h>
+#include <spawn.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
@@ -26,6 +27,7 @@
#include "alloc-util.h"
#include "architecture.h"
#include "argv-util.h"
+#include "dirent-util.h"
#include "env-file.h"
#include "env-util.h"
#include "errno-util.h"
@@ -97,7 +99,7 @@ static int get_process_state(pid_t pid) {
return (unsigned char) state;
}
-int get_process_comm(pid_t pid, char **ret) {
+int pid_get_comm(pid_t pid, char **ret) {
_cleanup_free_ char *escaped = NULL, *comm = NULL;
int r;
@@ -135,15 +137,35 @@ int get_process_comm(pid_t pid, char **ret) {
return 0;
}
-static int get_process_cmdline_nulstr(
+int pidref_get_comm(const PidRef *pid, char **ret) {
+ _cleanup_free_ char *comm = NULL;
+ int r;
+
+ if (!pidref_is_set(pid))
+ return -ESRCH;
+
+ r = pid_get_comm(pid->pid, &comm);
+ if (r < 0)
+ return r;
+
+ r = pidref_verify(pid);
+ if (r < 0)
+ return r;
+
+ if (ret)
+ *ret = TAKE_PTR(comm);
+ return 0;
+}
+
+static int pid_get_cmdline_nulstr(
pid_t pid,
size_t max_size,
ProcessCmdlineFlags flags,
char **ret,
size_t *ret_size) {
+ _cleanup_free_ char *t = NULL;
const char *p;
- char *t;
size_t k;
int r;
@@ -167,18 +189,17 @@ static int get_process_cmdline_nulstr(
return r;
if (k == 0) {
- t = mfree(t);
-
if (!(flags & PROCESS_CMDLINE_COMM_FALLBACK))
return -ENOENT;
/* Kernel threads have no argv[] */
_cleanup_free_ char *comm = NULL;
- r = get_process_comm(pid, &comm);
+ r = pid_get_comm(pid, &comm);
if (r < 0)
return r;
+ free(t);
t = strjoin("[", comm, "]");
if (!t)
return -ENOMEM;
@@ -189,12 +210,15 @@ static int get_process_cmdline_nulstr(
t[max_size] = '\0';
}
- *ret = t;
- *ret_size = k;
+ if (ret)
+ *ret = TAKE_PTR(t);
+ if (ret_size)
+ *ret_size = k;
+
return r;
}
-int get_process_cmdline(pid_t pid, size_t max_columns, ProcessCmdlineFlags flags, char **ret) {
+int pid_get_cmdline(pid_t pid, size_t max_columns, ProcessCmdlineFlags flags, char **ret) {
_cleanup_free_ char *t = NULL;
size_t k;
char *ans;
@@ -202,7 +226,7 @@ int get_process_cmdline(pid_t pid, size_t max_columns, ProcessCmdlineFlags flags
assert(pid >= 0);
assert(ret);
- /* Retrieve and format a commandline. See above for discussion of retrieval options.
+ /* Retrieve and format a command line. See above for discussion of retrieval options.
*
* There are two main formatting modes:
*
@@ -216,7 +240,7 @@ int get_process_cmdline(pid_t pid, size_t max_columns, ProcessCmdlineFlags flags
* Returns -ESRCH if the process doesn't exist, and -ENOENT if the process has no command line (and
* PROCESS_CMDLINE_COMM_FALLBACK is not specified). Returns 0 and sets *line otherwise. */
- int full = get_process_cmdline_nulstr(pid, max_columns, flags, &t, &k);
+ int full = pid_get_cmdline_nulstr(pid, max_columns, flags, &t, &k);
if (full < 0)
return full;
@@ -259,7 +283,27 @@ int get_process_cmdline(pid_t pid, size_t max_columns, ProcessCmdlineFlags flags
return 0;
}
-int get_process_cmdline_strv(pid_t pid, ProcessCmdlineFlags flags, char ***ret) {
+int pidref_get_cmdline(const PidRef *pid, size_t max_columns, ProcessCmdlineFlags flags, char **ret) {
+ _cleanup_free_ char *s = NULL;
+ int r;
+
+ if (!pidref_is_set(pid))
+ return -ESRCH;
+
+ r = pid_get_cmdline(pid->pid, max_columns, flags, &s);
+ if (r < 0)
+ return r;
+
+ r = pidref_verify(pid);
+ if (r < 0)
+ return r;
+
+ if (ret)
+ *ret = TAKE_PTR(s);
+ return 0;
+}
+
+int pid_get_cmdline_strv(pid_t pid, ProcessCmdlineFlags flags, char ***ret) {
_cleanup_free_ char *t = NULL;
char **args;
size_t k;
@@ -269,7 +313,7 @@ int get_process_cmdline_strv(pid_t pid, ProcessCmdlineFlags flags, char ***ret)
assert((flags & ~PROCESS_CMDLINE_COMM_FALLBACK) == 0);
assert(ret);
- r = get_process_cmdline_nulstr(pid, SIZE_MAX, flags, &t, &k);
+ r = pid_get_cmdline_nulstr(pid, SIZE_MAX, flags, &t, &k);
if (r < 0)
return r;
@@ -281,6 +325,27 @@ int get_process_cmdline_strv(pid_t pid, ProcessCmdlineFlags flags, char ***ret)
return 0;
}
+int pidref_get_cmdline_strv(const PidRef *pid, ProcessCmdlineFlags flags, char ***ret) {
+ _cleanup_strv_free_ char **args = NULL;
+ int r;
+
+ if (!pidref_is_set(pid))
+ return -ESRCH;
+
+ r = pid_get_cmdline_strv(pid->pid, flags, &args);
+ if (r < 0)
+ return r;
+
+ r = pidref_verify(pid);
+ if (r < 0)
+ return r;
+
+ if (ret)
+ *ret = TAKE_PTR(args);
+
+ return 0;
+}
+
int container_get_leader(const char *machine, pid_t *pid) {
_cleanup_free_ char *s = NULL, *class = NULL;
const char *p;
@@ -322,7 +387,34 @@ int container_get_leader(const char *machine, pid_t *pid) {
return 0;
}
-int is_kernel_thread(pid_t pid) {
+int namespace_get_leader(pid_t pid, NamespaceType type, pid_t *ret) {
+ int r;
+
+ assert(ret);
+
+ for (;;) {
+ pid_t ppid;
+
+ r = get_process_ppid(pid, &ppid);
+ if (r < 0)
+ return r;
+
+ r = in_same_namespace(pid, ppid, type);
+ if (r < 0)
+ return r;
+ if (r == 0) {
+ /* If the parent and the child are not in the same
+ * namespace, then the child is the leader we are
+ * looking for. */
+ *ret = pid;
+ return 0;
+ }
+
+ pid = ppid;
+ }
+}
+
+int pid_is_kernel_thread(pid_t pid) {
_cleanup_free_ char *line = NULL;
unsigned long long flags;
size_t l, i;
@@ -380,6 +472,23 @@ int is_kernel_thread(pid_t pid) {
return !!(flags & PF_KTHREAD);
}
+int pidref_is_kernel_thread(const PidRef *pid) {
+ int result, r;
+
+ if (!pidref_is_set(pid))
+ return -ESRCH;
+
+ result = pid_is_kernel_thread(pid->pid);
+ if (result < 0)
+ return result;
+
+ r = pidref_verify(pid); /* Verify that the PID wasn't reused since */
+ if (r < 0)
+ return r;
+
+ return result;
+}
+
int get_process_capeff(pid_t pid, char **ret) {
const char *p;
int r;
@@ -449,16 +558,14 @@ static int get_process_id(pid_t pid, const char *field, uid_t *ret) {
_cleanup_free_ char *line = NULL;
char *l;
- r = read_line(f, LONG_LINE_MAX, &line);
+ r = read_stripped_line(f, LONG_LINE_MAX, &line);
if (r < 0)
return r;
if (r == 0)
break;
- l = strstrip(line);
-
- if (startswith(l, field)) {
- l += strlen(field);
+ l = startswith(line, field);
+ if (l) {
l += strspn(l, WHITESPACE);
l[strcspn(l, WHITESPACE)] = 0;
@@ -470,7 +577,8 @@ static int get_process_id(pid_t pid, const char *field, uid_t *ret) {
return -EIO;
}
-int get_process_uid(pid_t pid, uid_t *ret) {
+int pid_get_uid(pid_t pid, uid_t *ret) {
+ assert(ret);
if (pid == 0 || pid == getpid_cached()) {
*ret = getuid();
@@ -480,6 +588,26 @@ int get_process_uid(pid_t pid, uid_t *ret) {
return get_process_id(pid, "Uid:", ret);
}
+int pidref_get_uid(const PidRef *pid, uid_t *ret) {
+ uid_t uid;
+ int r;
+
+ if (!pidref_is_set(pid))
+ return -ESRCH;
+
+ r = pid_get_uid(pid->pid, &uid);
+ if (r < 0)
+ return r;
+
+ r = pidref_verify(pid);
+ if (r < 0)
+ return r;
+
+ if (ret)
+ *ret = uid;
+ return 0;
+}
+
int get_process_gid(pid_t pid, gid_t *ret) {
if (pid == 0 || pid == getpid_cached()) {
@@ -606,6 +734,82 @@ int get_process_ppid(pid_t pid, pid_t *ret) {
return 0;
}
+int pid_get_start_time(pid_t pid, uint64_t *ret) {
+ _cleanup_free_ char *line = NULL;
+ const char *p;
+ int r;
+
+ assert(pid >= 0);
+
+ p = procfs_file_alloca(pid, "stat");
+ r = read_one_line_file(p, &line);
+ if (r == -ENOENT)
+ return -ESRCH;
+ if (r < 0)
+ return r;
+
+ /* Let's skip the pid and comm fields. The latter is enclosed in () but does not escape any () in its
+ * value, so let's skip over it manually */
+
+ p = strrchr(line, ')');
+ if (!p)
+ return -EIO;
+
+ p++;
+
+ unsigned long llu;
+
+ if (sscanf(p, " "
+ "%*c " /* state */
+ "%*u " /* ppid */
+ "%*u " /* pgrp */
+ "%*u " /* session */
+ "%*u " /* tty_nr */
+ "%*u " /* tpgid */
+ "%*u " /* flags */
+ "%*u " /* minflt */
+ "%*u " /* cminflt */
+ "%*u " /* majflt */
+ "%*u " /* cmajflt */
+ "%*u " /* utime */
+ "%*u " /* stime */
+ "%*u " /* cutime */
+ "%*u " /* cstime */
+ "%*i " /* priority */
+ "%*i " /* nice */
+ "%*u " /* num_threads */
+ "%*u " /* itrealvalue */
+ "%lu ", /* starttime */
+ &llu) != 1)
+ return -EIO;
+
+ if (ret)
+ *ret = llu;
+
+ return 0;
+}
+
+int pidref_get_start_time(const PidRef *pid, uint64_t *ret) {
+ uint64_t t;
+ int r;
+
+ if (!pidref_is_set(pid))
+ return -ESRCH;
+
+ r = pid_get_start_time(pid->pid, ret ? &t : NULL);
+ if (r < 0)
+ return r;
+
+ r = pidref_verify(pid);
+ if (r < 0)
+ return r;
+
+ if (ret)
+ *ret = t;
+
+ return 0;
+}
+
int get_process_umask(pid_t pid, mode_t *ret) {
_cleanup_free_ char *m = NULL;
const char *p;
@@ -670,7 +874,7 @@ int wait_for_terminate_and_check(const char *name, pid_t pid, WaitFlags flags) {
assert(pid > 1);
if (!name) {
- r = get_process_comm(pid, &buffer);
+ r = pid_get_comm(pid, &buffer);
if (r < 0)
log_debug_errno(r, "Failed to acquire process name of " PID_FMT ", ignoring: %m", pid);
else
@@ -824,7 +1028,7 @@ int getenv_for_pid(pid_t pid, const char *field, char **ret) {
_cleanup_fclose_ FILE *f = NULL;
char *value = NULL;
const char *path;
- size_t l, sum = 0;
+ size_t sum = 0;
int r;
assert(pid >= 0);
@@ -859,9 +1063,9 @@ int getenv_for_pid(pid_t pid, const char *field, char **ret) {
if (r < 0)
return r;
- l = strlen(field);
for (;;) {
_cleanup_free_ char *line = NULL;
+ const char *match;
if (sum > ENVIRONMENT_BLOCK_MAX) /* Give up searching eventually */
return -ENOBUFS;
@@ -874,8 +1078,9 @@ int getenv_for_pid(pid_t pid, const char *field, char **ret) {
sum += r;
- if (strneq(line, field, l) && line[l] == '=') {
- value = strdup(line + l + 1);
+ match = startswith(line, field);
+ if (match && *match == '=') {
+ value = strdup(match + 1);
if (!value)
return -ENOMEM;
@@ -892,6 +1097,9 @@ int pid_is_my_child(pid_t pid) {
pid_t ppid;
int r;
+ if (pid < 0)
+ return -ESRCH;
+
if (pid <= 1)
return false;
@@ -902,11 +1110,28 @@ int pid_is_my_child(pid_t pid) {
return ppid == getpid_cached();
}
-bool pid_is_unwaited(pid_t pid) {
+int pidref_is_my_child(const PidRef *pid) {
+ int r, result;
+
+ if (!pidref_is_set(pid))
+ return -ESRCH;
+
+ result = pid_is_my_child(pid->pid);
+ if (result < 0)
+ return result;
+
+ r = pidref_verify(pid);
+ if (r < 0)
+ return r;
+
+ return result;
+}
+
+int pid_is_unwaited(pid_t pid) {
/* Checks whether a PID is still valid at all, including a zombie */
if (pid < 0)
- return false;
+ return -ESRCH;
if (pid <= 1) /* If we or PID 1 would be dead and have been waited for, this code would not be running */
return true;
@@ -920,13 +1145,31 @@ bool pid_is_unwaited(pid_t pid) {
return errno != ESRCH;
}
-bool pid_is_alive(pid_t pid) {
+int pidref_is_unwaited(const PidRef *pid) {
+ int r;
+
+ if (!pidref_is_set(pid))
+ return -ESRCH;
+
+ if (pid->pid == 1 || pidref_is_self(pid))
+ return true;
+
+ r = pidref_kill(pid, 0);
+ if (r == -ESRCH)
+ return false;
+ if (r < 0)
+ return r;
+
+ return true;
+}
+
+int pid_is_alive(pid_t pid) {
int r;
/* Checks whether a PID is still valid and not a zombie */
if (pid < 0)
- return false;
+ return -ESRCH;
if (pid <= 1) /* If we or PID 1 would be a zombie, this code would not be running */
return true;
@@ -935,10 +1178,33 @@ bool pid_is_alive(pid_t pid) {
return true;
r = get_process_state(pid);
- if (IN_SET(r, -ESRCH, 'Z'))
+ if (r == -ESRCH)
return false;
+ if (r < 0)
+ return r;
- return true;
+ return r != 'Z';
+}
+
+int pidref_is_alive(const PidRef *pidref) {
+ int r, result;
+
+ if (!pidref_is_set(pidref))
+ return -ESRCH;
+
+ result = pid_is_alive(pidref->pid);
+ if (result < 0) {
+ assert(result != -ESRCH);
+ return result;
+ }
+
+ r = pidref_verify(pidref);
+ if (r == -ESRCH)
+ return false;
+ if (r < 0)
+ return r;
+
+ return result;
}
int pid_from_same_root_fs(pid_t pid) {
@@ -1057,7 +1323,10 @@ void valgrind_summary_hack(void) {
pid_t pid;
pid = raw_clone(SIGCHLD);
if (pid < 0)
- log_emergency_errno(errno, "Failed to fork off valgrind helper: %m");
+ log_struct_errno(
+ LOG_EMERG, errno,
+ "MESSAGE_ID=" SD_MESSAGE_VALGRIND_HELPER_FORK_STR,
+ LOG_MESSAGE( "Failed to fork off valgrind helper: %m"));
else if (pid == 0)
exit(EXIT_SUCCESS);
else {
@@ -1104,7 +1373,7 @@ pid_t getpid_cached(void) {
* https://sourceware.org/git/gitweb.cgi?p=glibc.git;h=c579f48edba88380635ab98cb612030e3ed8691e
*/
- __atomic_compare_exchange_n(
+ (void) __atomic_compare_exchange_n(
&cached_pid,
&current_value,
CACHED_PID_BUSY,
@@ -1159,6 +1428,51 @@ static void restore_sigsetp(sigset_t **ssp) {
(void) sigprocmask(SIG_SETMASK, *ssp, NULL);
}
+pid_t clone_with_nested_stack(int (*fn)(void *), int flags, void *userdata) {
+ size_t ps;
+ pid_t pid;
+ void *mystack;
+
+ /* A wrapper around glibc's clone() call that automatically sets up a "nested" stack. Only supports
+ * invocations without CLONE_VM, so that we can continue to use the parent's stack mapping.
+ *
+ * Note: glibc's clone() wrapper does not synchronize malloc() locks. This means that if the parent
+ * is threaded these locks will be in an undefined state in the child, and hence memory allocations
+ * are likely going to run into deadlocks. Hence: if you use this function make sure your parent is
+ * strictly single-threaded or your child never calls malloc(). */
+
+ assert((flags & (CLONE_VM|CLONE_PARENT_SETTID|CLONE_CHILD_SETTID|
+ CLONE_CHILD_CLEARTID|CLONE_SETTLS)) == 0);
+
+ /* We allocate some space on the stack to use as the stack for the child (hence "nested"). Note that
+ * the net effect is that the child will have the start of its stack inside the stack of the parent,
+ * but since they are a CoW copy of each other that's fine. We allocate one page-aligned page. But
+ * since we don't want to deal with differences between systems where the stack grows backwards or
+ * forwards we'll allocate one more and place the stack address in the middle. Except that we also
+ * want it page aligned, hence we'll allocate one page more. Makes 3. */
+
+ ps = page_size();
+ mystack = alloca(ps*3);
+ mystack = (uint8_t*) mystack + ps; /* move pointer one page ahead since stacks usually grow backwards */
+ mystack = (void*) ALIGN_TO((uintptr_t) mystack, ps); /* align to page size (moving things further ahead) */
+
+#if HAVE_CLONE
+ pid = clone(fn, mystack, flags, userdata);
+#else
+ pid = __clone2(fn, mystack, ps, flags, userdata);
+#endif
+ if (pid < 0)
+ return -errno;
+
+ return pid;
+}
+
+static int fork_flags_to_signal(ForkFlags flags) {
+ return (flags & FORK_DEATHSIG_SIGTERM) ? SIGTERM :
+ (flags & FORK_DEATHSIG_SIGINT) ? SIGINT :
+ SIGKILL;
+}
+
int safe_fork_full(
const char *name,
const int stdio_fds[3],
@@ -1170,9 +1484,12 @@ int safe_fork_full(
pid_t original_pid, pid;
sigset_t saved_ss, ss;
_unused_ _cleanup_(restore_sigsetp) sigset_t *saved_ssp = NULL;
- bool block_signals = false, block_all = false;
+ bool block_signals = false, block_all = false, intermediary = false;
int prio, r;
+ assert(!FLAGS_SET(flags, FORK_DETACH) || !ret_pid);
+ assert(!FLAGS_SET(flags, FORK_DETACH|FORK_WAIT));
+
/* A wrapper around fork(), that does a couple of important initializations in addition to mere forking. Always
* returns the child's PID in *ret_pid. Returns == 0 in the child, and > 0 in the parent. */
@@ -1185,9 +1502,10 @@ int safe_fork_full(
fflush(stderr); /* This one shouldn't be necessary, stderr should be unbuffered anyway, but let's better be safe than sorry */
}
- if (flags & (FORK_RESET_SIGNALS|FORK_DEATHSIG)) {
- /* We temporarily block all signals, so that the new child has them blocked initially. This way, we can
- * be sure that SIGTERMs are not lost we might send to the child. */
+ if (flags & (FORK_RESET_SIGNALS|FORK_DEATHSIG_SIGTERM|FORK_DEATHSIG_SIGINT)) {
+ /* We temporarily block all signals, so that the new child has them blocked initially. This
+ * way, we can be sure that SIGTERMs are not lost we might send to the child. (Note that for
+ * FORK_DEATHSIG_SIGKILL we don't bother, since it cannot be blocked anyway.) */
assert_se(sigfillset(&ss) >= 0);
block_signals = block_all = true;
@@ -1206,17 +1524,47 @@ int safe_fork_full(
saved_ssp = &saved_ss;
}
- if ((flags & (FORK_NEW_MOUNTNS|FORK_NEW_USERNS)) != 0)
+ if (FLAGS_SET(flags, FORK_DETACH)) {
+ assert(!FLAGS_SET(flags, FORK_WAIT));
+ assert(!ret_pid);
+
+ /* Fork off intermediary child if needed */
+
+ r = is_reaper_process();
+ if (r < 0)
+ return log_full_errno(prio, r, "Failed to determine if we are a reaper process: %m");
+
+ if (!r) {
+ /* Not a reaper process, hence do a double fork() so we are reparented to one */
+
+ pid = fork();
+ if (pid < 0)
+ return log_full_errno(prio, errno, "Failed to fork off '%s': %m", strna(name));
+ if (pid > 0) {
+ log_debug("Successfully forked off intermediary '%s' as PID " PID_FMT ".", strna(name), pid);
+ return 1; /* return in the parent */
+ }
+
+ intermediary = true;
+ }
+ }
+
+ if ((flags & (FORK_NEW_MOUNTNS|FORK_NEW_USERNS|FORK_NEW_NETNS)) != 0)
pid = raw_clone(SIGCHLD|
(FLAGS_SET(flags, FORK_NEW_MOUNTNS) ? CLONE_NEWNS : 0) |
- (FLAGS_SET(flags, FORK_NEW_USERNS) ? CLONE_NEWUSER : 0));
+ (FLAGS_SET(flags, FORK_NEW_USERNS) ? CLONE_NEWUSER : 0) |
+ (FLAGS_SET(flags, FORK_NEW_NETNS) ? CLONE_NEWNET : 0));
else
pid = fork();
if (pid < 0)
return log_full_errno(prio, errno, "Failed to fork off '%s': %m", strna(name));
if (pid > 0) {
- /* We are in the parent process */
+ /* If we are in the intermediary process, exit now */
+ if (intermediary)
+ _exit(EXIT_SUCCESS);
+
+ /* We are in the parent process */
log_debug("Successfully forked off '%s' as PID " PID_FMT ".", strna(name), pid);
if (flags & FORK_WAIT) {
@@ -1259,8 +1607,8 @@ int safe_fork_full(
r, "Failed to rename process, ignoring: %m");
}
- if (flags & (FORK_DEATHSIG|FORK_DEATHSIG_SIGINT))
- if (prctl(PR_SET_PDEATHSIG, (flags & FORK_DEATHSIG_SIGINT) ? SIGINT : SIGTERM) < 0) {
+ if (flags & (FORK_DEATHSIG_SIGTERM|FORK_DEATHSIG_SIGINT|FORK_DEATHSIG_SIGKILL))
+ if (prctl(PR_SET_PDEATHSIG, fork_flags_to_signal(flags)) < 0) {
log_full_errno(prio, errno, "Failed to set death signal: %m");
_exit(EXIT_FAILURE);
}
@@ -1285,7 +1633,7 @@ int safe_fork_full(
}
}
- if (flags & FORK_DEATHSIG) {
+ if (flags & (FORK_DEATHSIG_SIGTERM|FORK_DEATHSIG_SIGKILL|FORK_DEATHSIG_SIGINT)) {
pid_t ppid;
/* Let's see if the parent PID is still the one we started from? If not, then the parent
* already died by the time we set PR_SET_PDEATHSIG, hence let's emulate the effect */
@@ -1294,8 +1642,9 @@ int safe_fork_full(
if (ppid == 0)
/* Parent is in a different PID namespace. */;
else if (ppid != original_pid) {
- log_debug("Parent died early, raising SIGTERM.");
- (void) raise(SIGTERM);
+ int sig = fork_flags_to_signal(flags);
+ log_debug("Parent died early, raising %s.", signal_to_string(sig));
+ (void) raise(sig);
_exit(EXIT_FAILURE);
}
}
@@ -1328,6 +1677,9 @@ int safe_fork_full(
log_full_errno(prio, r, "Failed to rearrange stdio fds: %m");
_exit(EXIT_FAILURE);
}
+
+ /* Turn off O_NONBLOCK on the fdio fds, in case it was left on */
+ stdio_disable_nonblock();
} else {
r = make_null_stdio();
if (r < 0) {
@@ -1389,6 +1741,30 @@ int safe_fork_full(
return 0;
}
+int pidref_safe_fork_full(
+ const char *name,
+ const int stdio_fds[3],
+ const int except_fds[],
+ size_t n_except_fds,
+ ForkFlags flags,
+ PidRef *ret_pid) {
+
+ pid_t pid;
+ int r, q;
+
+ assert(!FLAGS_SET(flags, FORK_WAIT));
+
+ r = safe_fork_full(name, stdio_fds, except_fds, n_except_fds, flags, &pid);
+ if (r < 0)
+ return r;
+
+ q = pidref_set_pid(ret_pid, pid);
+ if (q < 0) /* Let's not fail for this, no matter what, the process exists after all, and that's key */
+ *ret_pid = PIDREF_MAKE_FROM_PID(pid);
+
+ return r;
+}
+
int namespace_fork(
const char *outer_name,
const char *inner_name,
@@ -1411,7 +1787,7 @@ int namespace_fork(
r = safe_fork_full(outer_name,
NULL,
except_fds, n_except_fds,
- (flags|FORK_DEATHSIG) & ~(FORK_REOPEN_LOG|FORK_NEW_MOUNTNS|FORK_MOUNTNS_SLAVE), ret_pid);
+ (flags|FORK_DEATHSIG_SIGINT|FORK_DEATHSIG_SIGTERM|FORK_DEATHSIG_SIGKILL) & ~(FORK_REOPEN_LOG|FORK_NEW_MOUNTNS|FORK_MOUNTNS_SLAVE), ret_pid);
if (r < 0)
return r;
if (r == 0) {
@@ -1633,6 +2009,212 @@ int get_process_threads(pid_t pid) {
return n;
}
+int is_reaper_process(void) {
+ int b = 0;
+
+ /* Checks if we are running in a reaper process, i.e. if we are expected to deal with processes
+ * reparented to us. This simply checks if we are PID 1 or if PR_SET_CHILD_SUBREAPER was called. */
+
+ if (getpid_cached() == 1)
+ return true;
+
+ if (prctl(PR_GET_CHILD_SUBREAPER, (unsigned long) &b, 0UL, 0UL, 0UL) < 0)
+ return -errno;
+
+ return b != 0;
+}
+
+int make_reaper_process(bool b) {
+
+ if (getpid_cached() == 1) {
+
+ if (!b)
+ return -EINVAL;
+
+ return 0;
+ }
+
+ /* Some prctl()s insist that all 5 arguments are specified, others do not. Let's always specify all,
+ * to avoid any ambiguities */
+ if (prctl(PR_SET_CHILD_SUBREAPER, (unsigned long) b, 0UL, 0UL, 0UL) < 0)
+ return -errno;
+
+ return 0;
+}
+
+DEFINE_TRIVIAL_CLEANUP_FUNC_FULL(posix_spawnattr_t*, posix_spawnattr_destroy, NULL);
+
+int posix_spawn_wrapper(
+ const char *path,
+ char * const *argv,
+ char * const *envp,
+ const char *cgroup,
+ PidRef *ret_pidref) {
+
+ short flags = POSIX_SPAWN_SETSIGMASK|POSIX_SPAWN_SETSIGDEF;
+ posix_spawnattr_t attr;
+ sigset_t mask;
+ int r;
+
+ /* Forks and invokes 'path' with 'argv' and 'envp' using CLONE_VM and CLONE_VFORK, which means the
+ * caller will be blocked until the child either exits or exec's. The memory of the child will be
+ * fully shared with the memory of the parent, so that there are no copy-on-write or memory.max
+ * issues.
+ *
+ * Also, move the newly-created process into 'cgroup' through POSIX_SPAWN_SETCGROUP (clone3())
+ * if available. Note that CLONE_INTO_CGROUP is only supported on cgroup v2.
+ * returns 1: We're already in the right cgroup
+ * 0: 'cgroup' not specified or POSIX_SPAWN_SETCGROUP is not supported. The caller
+ * needs to call 'cg_attach' on their own */
+
+ assert(path);
+ assert(argv);
+ assert(ret_pidref);
+
+ assert_se(sigfillset(&mask) >= 0);
+
+ r = posix_spawnattr_init(&attr);
+ if (r != 0)
+ return -r; /* These functions return a positive errno on failure */
+
+ /* Initialization needs to succeed before we can set up a destructor. */
+ _unused_ _cleanup_(posix_spawnattr_destroyp) posix_spawnattr_t *attr_destructor = &attr;
+
+#if HAVE_PIDFD_SPAWN
+ _cleanup_close_ int cgroup_fd = -EBADF;
+
+ if (cgroup) {
+ _cleanup_free_ char *resolved_cgroup = NULL;
+
+ r = cg_get_path_and_check(
+ SYSTEMD_CGROUP_CONTROLLER,
+ cgroup,
+ /* suffix= */ NULL,
+ &resolved_cgroup);
+ if (r < 0)
+ return r;
+
+ cgroup_fd = open(resolved_cgroup, O_PATH|O_DIRECTORY|O_CLOEXEC);
+ if (cgroup_fd < 0)
+ return -errno;
+
+ r = posix_spawnattr_setcgroup_np(&attr, cgroup_fd);
+ if (r != 0)
+ return -r;
+
+ flags |= POSIX_SPAWN_SETCGROUP;
+ }
+#endif
+
+ r = posix_spawnattr_setflags(&attr, flags);
+ if (r != 0)
+ return -r;
+ r = posix_spawnattr_setsigmask(&attr, &mask);
+ if (r != 0)
+ return -r;
+
+#if HAVE_PIDFD_SPAWN
+ _cleanup_close_ int pidfd = -EBADF;
+
+ r = pidfd_spawn(&pidfd, path, NULL, &attr, argv, envp);
+ if (r == 0) {
+ r = pidref_set_pidfd_consume(ret_pidref, TAKE_FD(pidfd));
+ if (r < 0)
+ return r;
+
+ return FLAGS_SET(flags, POSIX_SPAWN_SETCGROUP);
+ }
+ if (!(ERRNO_IS_NOT_SUPPORTED(r) || ERRNO_IS_PRIVILEGE(r)))
+ return -r;
+
+ /* Compiled on a newer host, or seccomp&friends blocking clone3()? Fallback, but need to change the
+ * flags to remove the cgroup one, which is what redirects to clone3() */
+ flags &= ~POSIX_SPAWN_SETCGROUP;
+ r = posix_spawnattr_setflags(&attr, flags);
+ if (r != 0)
+ return -r;
+#endif
+
+ pid_t pid;
+ r = posix_spawn(&pid, path, NULL, &attr, argv, envp);
+ if (r != 0)
+ return -r;
+
+ r = pidref_set_pid(ret_pidref, pid);
+ if (r < 0)
+ return r;
+
+ return 0; /* We did not use CLONE_INTO_CGROUP so return 0, the caller will have to move the child */
+}
+
+int proc_dir_open(DIR **ret) {
+ DIR *d;
+
+ assert(ret);
+
+ d = opendir("/proc");
+ if (!d)
+ return -errno;
+
+ *ret = d;
+ return 0;
+}
+
+int proc_dir_read(DIR *d, pid_t *ret) {
+ assert(d);
+
+ for (;;) {
+ struct dirent *de;
+
+ errno = 0;
+ de = readdir_no_dot(d);
+ if (!de) {
+ if (errno != 0)
+ return -errno;
+
+ break;
+ }
+
+ if (!IN_SET(de->d_type, DT_DIR, DT_UNKNOWN))
+ continue;
+
+ if (parse_pid(de->d_name, ret) >= 0)
+ return 1;
+ }
+
+ if (ret)
+ *ret = 0;
+ return 0;
+}
+
+int proc_dir_read_pidref(DIR *d, PidRef *ret) {
+ int r;
+
+ assert(d);
+
+ for (;;) {
+ pid_t pid;
+
+ r = proc_dir_read(d, &pid);
+ if (r < 0)
+ return r;
+ if (r == 0)
+ break;
+
+ r = pidref_set_pid(ret, pid);
+ if (r == -ESRCH) /* gone by now? skip it */
+ continue;
+ if (r < 0)
+ return r;
+
+ return 1;
+ }
+
+ if (ret)
+ *ret = PIDREF_NULL;
+ return 0;
+}
+
static const char *const sigchld_code_table[] = {
[CLD_EXITED] = "exited",
[CLD_KILLED] = "killed",
diff --git a/src/libnm-systemd-shared/src/basic/process-util.h b/src/libnm-systemd-shared/src/basic/process-util.h
index 5cf5c7c6ec..a75c44cfad 100644
--- a/src/libnm-systemd-shared/src/basic/process-util.h
+++ b/src/libnm-systemd-shared/src/basic/process-util.h
@@ -14,6 +14,7 @@
#include "alloc-util.h"
#include "format-util.h"
#include "macro.h"
+#include "namespace-util.h"
#include "time-util.h"
#define procfs_file_alloca(pid, field) \
@@ -38,21 +39,29 @@ typedef enum ProcessCmdlineFlags {
PROCESS_CMDLINE_QUOTE_POSIX = 1 << 3,
} ProcessCmdlineFlags;
-int get_process_comm(pid_t pid, char **ret);
-int get_process_cmdline(pid_t pid, size_t max_columns, ProcessCmdlineFlags flags, char **ret);
-int get_process_cmdline_strv(pid_t pid, ProcessCmdlineFlags flags, char ***ret);
+int pid_get_comm(pid_t pid, char **ret);
+int pidref_get_comm(const PidRef *pid, char **ret);
+int pid_get_cmdline(pid_t pid, size_t max_columns, ProcessCmdlineFlags flags, char **ret);
+int pidref_get_cmdline(const PidRef *pid, size_t max_columns, ProcessCmdlineFlags flags, char **ret);
+int pid_get_cmdline_strv(pid_t pid, ProcessCmdlineFlags flags, char ***ret);
+int pidref_get_cmdline_strv(const PidRef *pid, ProcessCmdlineFlags flags, char ***ret);
int get_process_exe(pid_t pid, char **ret);
-int get_process_uid(pid_t pid, uid_t *ret);
+int pid_get_uid(pid_t pid, uid_t *ret);
+int pidref_get_uid(const PidRef *pid, uid_t *ret);
int get_process_gid(pid_t pid, gid_t *ret);
int get_process_capeff(pid_t pid, char **ret);
int get_process_cwd(pid_t pid, char **ret);
int get_process_root(pid_t pid, char **ret);
int get_process_environ(pid_t pid, char **ret);
int get_process_ppid(pid_t pid, pid_t *ret);
+int pid_get_start_time(pid_t pid, uint64_t *ret);
+int pidref_get_start_time(const PidRef* pid, uint64_t *ret);
int get_process_umask(pid_t pid, mode_t *ret);
int container_get_leader(const char *machine, pid_t *pid);
+int namespace_get_leader(pid_t pid, NamespaceType type, pid_t *ret);
+
int wait_for_terminate(pid_t pid, siginfo_t *status);
typedef enum WaitFlags {
@@ -74,13 +83,17 @@ void sigkill_nowaitp(pid_t *pid);
int kill_and_sigcont(pid_t pid, int sig);
-int is_kernel_thread(pid_t pid);
+int pid_is_kernel_thread(pid_t pid);
+int pidref_is_kernel_thread(const PidRef *pid);
int getenv_for_pid(pid_t pid, const char *field, char **_value);
-bool pid_is_alive(pid_t pid);
-bool pid_is_unwaited(pid_t pid);
+int pid_is_alive(pid_t pid);
+int pidref_is_alive(const PidRef *pidref);
+int pid_is_unwaited(pid_t pid);
+int pidref_is_unwaited(const PidRef *pidref);
int pid_is_my_child(pid_t pid);
+int pidref_is_my_child(const PidRef *pidref);
int pid_from_same_root_fs(pid_t pid);
bool is_main_thread(void);
@@ -141,24 +154,34 @@ void reset_cached_pid(void);
int must_be_root(void);
+pid_t clone_with_nested_stack(int (*fn)(void *), int flags, void *userdata);
+
+/* 💣 Note that FORK_NEW_USERNS, FORK_NEW_MOUNTNS, or FORK_NEW_NETNS should not be called in threaded
+ * programs, because they cause us to use raw_clone() which does not synchronize the glibc malloc() locks,
+ * and thus will cause deadlocks if the parent uses threads and the child does memory allocations. Hence: if
+ * the parent is threaded these flags may not be used. These flags cannot be used if the parent uses threads
+ * or the child uses malloc(). 💣 */
typedef enum ForkFlags {
FORK_RESET_SIGNALS = 1 << 0, /* Reset all signal handlers and signal mask */
FORK_CLOSE_ALL_FDS = 1 << 1, /* Close all open file descriptors in the child, except for 0,1,2 */
- FORK_DEATHSIG = 1 << 2, /* Set PR_DEATHSIG in the child to SIGTERM */
+ FORK_DEATHSIG_SIGTERM = 1 << 2, /* Set PR_DEATHSIG in the child to SIGTERM */
FORK_DEATHSIG_SIGINT = 1 << 3, /* Set PR_DEATHSIG in the child to SIGINT */
- FORK_REARRANGE_STDIO = 1 << 4, /* Connect 0,1,2 to specified fds or /dev/null */
- FORK_REOPEN_LOG = 1 << 5, /* Reopen log connection */
- FORK_LOG = 1 << 6, /* Log above LOG_DEBUG log level about failures */
- FORK_WAIT = 1 << 7, /* Wait until child exited */
- FORK_NEW_MOUNTNS = 1 << 8, /* Run child in its own mount namespace */
- FORK_MOUNTNS_SLAVE = 1 << 9, /* Make child's mount namespace MS_SLAVE */
- FORK_PRIVATE_TMP = 1 << 10, /* Mount new /tmp/ in the child (combine with FORK_NEW_MOUNTNS!) */
- FORK_RLIMIT_NOFILE_SAFE = 1 << 11, /* Set RLIMIT_NOFILE soft limit to 1K for select() compat */
- FORK_STDOUT_TO_STDERR = 1 << 12, /* Make stdout a copy of stderr */
- FORK_FLUSH_STDIO = 1 << 13, /* fflush() stdout (and stderr) before forking */
- FORK_NEW_USERNS = 1 << 14, /* Run child in its own user namespace */
- FORK_CLOEXEC_OFF = 1 << 15, /* In the child: turn off O_CLOEXEC on all fds in except_fds[] */
- FORK_KEEP_NOTIFY_SOCKET = 1 << 16, /* Unless this specified, $NOTIFY_SOCKET will be unset. */
+ FORK_DEATHSIG_SIGKILL = 1 << 4, /* Set PR_DEATHSIG in the child to SIGKILL */
+ FORK_REARRANGE_STDIO = 1 << 5, /* Connect 0,1,2 to specified fds or /dev/null */
+ FORK_REOPEN_LOG = 1 << 6, /* Reopen log connection */
+ FORK_LOG = 1 << 7, /* Log above LOG_DEBUG log level about failures */
+ FORK_WAIT = 1 << 8, /* Wait until child exited */
+ FORK_NEW_MOUNTNS = 1 << 9, /* Run child in its own mount namespace 💣 DO NOT USE IN THREADED PROGRAMS! 💣 */
+ FORK_MOUNTNS_SLAVE = 1 << 10, /* Make child's mount namespace MS_SLAVE */
+ FORK_PRIVATE_TMP = 1 << 11, /* Mount new /tmp/ in the child (combine with FORK_NEW_MOUNTNS!) */
+ FORK_RLIMIT_NOFILE_SAFE = 1 << 12, /* Set RLIMIT_NOFILE soft limit to 1K for select() compat */
+ FORK_STDOUT_TO_STDERR = 1 << 13, /* Make stdout a copy of stderr */
+ FORK_FLUSH_STDIO = 1 << 14, /* fflush() stdout (and stderr) before forking */
+ FORK_NEW_USERNS = 1 << 15, /* Run child in its own user namespace 💣 DO NOT USE IN THREADED PROGRAMS! 💣 */
+ FORK_CLOEXEC_OFF = 1 << 16, /* In the child: turn off O_CLOEXEC on all fds in except_fds[] */
+ FORK_KEEP_NOTIFY_SOCKET = 1 << 17, /* Unless this specified, $NOTIFY_SOCKET will be unset. */
+ FORK_DETACH = 1 << 18, /* Double fork if needed to ensure PID1/subreaper is parent */
+ FORK_NEW_NETNS = 1 << 19, /* Run child in its own network namespace 💣 DO NOT USE IN THREADED PROGRAMS! 💣 */
} ForkFlags;
int safe_fork_full(
@@ -173,6 +196,18 @@ static inline int safe_fork(const char *name, ForkFlags flags, pid_t *ret_pid) {
return safe_fork_full(name, NULL, NULL, 0, flags, ret_pid);
}
+int pidref_safe_fork_full(
+ const char *name,
+ const int stdio_fds[3],
+ const int except_fds[],
+ size_t n_except_fds,
+ ForkFlags flags,
+ PidRef *ret_pid);
+
+static inline int pidref_safe_fork(const char *name, ForkFlags flags, PidRef *ret_pid) {
+ return pidref_safe_fork_full(name, NULL, NULL, 0, flags, ret_pid);
+}
+
int namespace_fork(const char *outer_name, const char *inner_name, const int except_fds[], size_t n_except_fds, ForkFlags flags, int pidns_fd, int mntns_fd, int netns_fd, int userns_fd, int root_fd, pid_t *ret_pid);
int set_oom_score_adjust(int value);
@@ -201,3 +236,17 @@ int setpriority_closest(int priority);
_noreturn_ void freeze(void);
int get_process_threads(pid_t pid);
+
+int is_reaper_process(void);
+int make_reaper_process(bool b);
+
+int posix_spawn_wrapper(
+ const char *path,
+ char * const *argv,
+ char * const *envp,
+ const char *cgroup,
+ PidRef *ret_pidref);
+
+int proc_dir_open(DIR **ret);
+int proc_dir_read(DIR *d, pid_t *ret);
+int proc_dir_read_pidref(DIR *d, PidRef *ret);
diff --git a/src/libnm-systemd-shared/src/basic/random-util.c b/src/libnm-systemd-shared/src/basic/random-util.c
index 934d5e2531..c7b9551646 100644
--- a/src/libnm-systemd-shared/src/basic/random-util.c
+++ b/src/libnm-systemd-shared/src/basic/random-util.c
@@ -6,7 +6,6 @@
#include <errno.h>
#include <fcntl.h>
#include <linux/random.h>
-#include <pthread.h>
#include <stdbool.h>
#include <stdint.h>
#include <stdlib.h>
@@ -28,6 +27,7 @@
#include "missing_syscall.h"
#include "missing_threads.h"
#include "parse-util.h"
+#include "process-util.h"
#include "random-util.h"
#include "sha256.h"
#include "time-util.h"
@@ -50,7 +50,7 @@ static void fallback_random_bytes(void *p, size_t n) {
.call_id = fallback_counter++,
.stamp_mono = now(CLOCK_MONOTONIC),
.stamp_real = now(CLOCK_REALTIME),
- .pid = getpid(),
+ .pid = getpid_cached(),
.tid = gettid(),
};
@@ -226,7 +226,7 @@ int random_write_entropy(int fd, const void *seed, size_t size, bool credit) {
if (ioctl(fd, RNDADDENTROPY, info) < 0)
return -errno;
} else {
- r = loop_write(fd, seed, size, false);
+ r = loop_write(fd, seed, size);
if (r < 0)
return r;
}
diff --git a/src/libnm-systemd-shared/src/basic/ratelimit.h b/src/libnm-systemd-shared/src/basic/ratelimit.h
index bb7160a895..492ea3b48d 100644
--- a/src/libnm-systemd-shared/src/basic/ratelimit.h
+++ b/src/libnm-systemd-shared/src/basic/ratelimit.h
@@ -12,6 +12,8 @@ typedef struct RateLimit {
usec_t begin;
} RateLimit;
+#define RATELIMIT_OFF (const RateLimit) { .interval = USEC_INFINITY, .burst = UINT_MAX }
+
static inline void ratelimit_reset(RateLimit *rl) {
rl->num = rl->begin = 0;
}
diff --git a/src/libnm-systemd-shared/src/basic/signal-util.c b/src/libnm-systemd-shared/src/basic/signal-util.c
index 270d397d50..4354edca7e 100644
--- a/src/libnm-systemd-shared/src/basic/signal-util.c
+++ b/src/libnm-systemd-shared/src/basic/signal-util.c
@@ -123,39 +123,39 @@ int sigprocmask_many(int how, sigset_t *old, ...) {
}
static const char *const static_signal_table[] = {
- [SIGHUP] = "HUP",
- [SIGINT] = "INT",
- [SIGQUIT] = "QUIT",
- [SIGILL] = "ILL",
- [SIGTRAP] = "TRAP",
- [SIGABRT] = "ABRT",
- [SIGBUS] = "BUS",
- [SIGFPE] = "FPE",
- [SIGKILL] = "KILL",
- [SIGUSR1] = "USR1",
- [SIGSEGV] = "SEGV",
- [SIGUSR2] = "USR2",
- [SIGPIPE] = "PIPE",
- [SIGALRM] = "ALRM",
- [SIGTERM] = "TERM",
+ [SIGHUP] = "HUP",
+ [SIGINT] = "INT",
+ [SIGQUIT] = "QUIT",
+ [SIGILL] = "ILL",
+ [SIGTRAP] = "TRAP",
+ [SIGABRT] = "ABRT",
+ [SIGBUS] = "BUS",
+ [SIGFPE] = "FPE",
+ [SIGKILL] = "KILL",
+ [SIGUSR1] = "USR1",
+ [SIGSEGV] = "SEGV",
+ [SIGUSR2] = "USR2",
+ [SIGPIPE] = "PIPE",
+ [SIGALRM] = "ALRM",
+ [SIGTERM] = "TERM",
#ifdef SIGSTKFLT
[SIGSTKFLT] = "STKFLT", /* Linux on SPARC doesn't know SIGSTKFLT */
#endif
- [SIGCHLD] = "CHLD",
- [SIGCONT] = "CONT",
- [SIGSTOP] = "STOP",
- [SIGTSTP] = "TSTP",
- [SIGTTIN] = "TTIN",
- [SIGTTOU] = "TTOU",
- [SIGURG] = "URG",
- [SIGXCPU] = "XCPU",
- [SIGXFSZ] = "XFSZ",
+ [SIGCHLD] = "CHLD",
+ [SIGCONT] = "CONT",
+ [SIGSTOP] = "STOP",
+ [SIGTSTP] = "TSTP",
+ [SIGTTIN] = "TTIN",
+ [SIGTTOU] = "TTOU",
+ [SIGURG] = "URG",
+ [SIGXCPU] = "XCPU",
+ [SIGXFSZ] = "XFSZ",
[SIGVTALRM] = "VTALRM",
- [SIGPROF] = "PROF",
- [SIGWINCH] = "WINCH",
- [SIGIO] = "IO",
- [SIGPWR] = "PWR",
- [SIGSYS] = "SYS"
+ [SIGPROF] = "PROF",
+ [SIGWINCH] = "WINCH",
+ [SIGIO] = "IO",
+ [SIGPWR] = "PWR",
+ [SIGSYS] = "SYS"
};
DEFINE_PRIVATE_STRING_TABLE_LOOKUP(static_signal, int);
diff --git a/src/libnm-systemd-shared/src/basic/siphash24.h b/src/libnm-systemd-shared/src/basic/siphash24.h
index e46f3cc5be..f9867630af 100644
--- a/src/libnm-systemd-shared/src/basic/siphash24.h
+++ b/src/libnm-systemd-shared/src/basic/siphash24.h
@@ -52,15 +52,16 @@ siphash24 (const void *in, size_t inlen, const uint8_t k[16])
void siphash24_init(struct siphash *state, const uint8_t k[static 16]);
void siphash24_compress(const void *in, size_t inlen, struct siphash *state);
#define siphash24_compress_byte(byte, state) siphash24_compress((const uint8_t[]) { (byte) }, 1, (state))
+#define siphash24_compress_typesafe(in, state) \
+ siphash24_compress(&(in), sizeof(typeof(in)), (state))
static inline void siphash24_compress_boolean(bool in, struct siphash *state) {
- uint8_t i = in;
-
- siphash24_compress(&i, sizeof i, state);
+ siphash24_compress_byte(in, state);
}
static inline void siphash24_compress_usec_t(usec_t in, struct siphash *state) {
- siphash24_compress(&in, sizeof in, state);
+ uint64_t u = htole64(in);
+ siphash24_compress_typesafe(u, state);
}
static inline void siphash24_compress_safe(const void *in, size_t inlen, struct siphash *state) {
diff --git a/src/libnm-systemd-shared/src/basic/socket-util.c b/src/libnm-systemd-shared/src/basic/socket-util.c
index 9b411e07a2..df3e2c17c2 100644
--- a/src/libnm-systemd-shared/src/basic/socket-util.c
+++ b/src/libnm-systemd-shared/src/basic/socket-util.c
@@ -46,6 +46,11 @@
# define IDN_FLAGS 0
#endif
+/* From the kernel's include/net/scm.h */
+#ifndef SCM_MAX_FD
+# define SCM_MAX_FD 253
+#endif
+
static const char* const socket_address_type_table[] = {
[SOCK_STREAM] = "Stream",
[SOCK_DGRAM] = "Datagram",
@@ -547,7 +552,7 @@ int sockaddr_pretty(
} else {
if (path[path_len - 1] == '\0')
/* We expect a terminating NUL and don't print it */
- path_len --;
+ path_len--;
p = cescape_length(path, path_len);
}
@@ -628,28 +633,33 @@ int getsockname_pretty(int fd, char **ret) {
return sockaddr_pretty(&sa.sa, salen, false, true, ret);
}
-int socknameinfo_pretty(union sockaddr_union *sa, socklen_t salen, char **_ret) {
+int socknameinfo_pretty(const struct sockaddr *sa, socklen_t salen, char **ret) {
+ char host[NI_MAXHOST];
int r;
- char host[NI_MAXHOST], *ret;
- assert(_ret);
+ assert(sa);
+ assert(salen > sizeof(sa_family_t));
- r = getnameinfo(&sa->sa, salen, host, sizeof(host), NULL, 0, IDN_FLAGS);
+ r = getnameinfo(sa, salen, host, sizeof(host), /* service= */ NULL, /* service_len= */ 0, IDN_FLAGS);
if (r != 0) {
- int saved_errno = errno;
+ if (r == EAI_MEMORY)
+ return log_oom_debug();
+ if (r == EAI_SYSTEM)
+ log_debug_errno(errno, "getnameinfo() failed, ignoring: %m");
+ else
+ log_debug("getnameinfo() failed, ignoring: %s", gai_strerror(r));
- r = sockaddr_pretty(&sa->sa, salen, true, true, &ret);
- if (r < 0)
- return r;
+ return sockaddr_pretty(sa, salen, /* translate_ipv6= */ true, /* include_port= */ true, ret);
+ }
- log_debug_errno(saved_errno, "getnameinfo(%s) failed: %m", ret);
- } else {
- ret = strdup(host);
- if (!ret)
+ if (ret) {
+ char *copy = strdup(host);
+ if (!copy)
return -ENOMEM;
+
+ *ret = copy;
}
- *_ret = ret;
return 0;
}
@@ -874,13 +884,11 @@ bool address_label_valid(const char *p) {
int getpeercred(int fd, struct ucred *ucred) {
socklen_t n = sizeof(struct ucred);
struct ucred u;
- int r;
assert(fd >= 0);
assert(ucred);
- r = getsockopt(fd, SOL_SOCKET, SO_PEERCRED, &u, &n);
- if (r < 0)
+ if (getsockopt(fd, SOL_SOCKET, SO_PEERCRED, &u, &n) < 0)
return -errno;
if (n != sizeof(struct ucred))
@@ -909,8 +917,10 @@ int getpeersec(int fd, char **ret) {
if (!s)
return -ENOMEM;
- if (getsockopt(fd, SOL_SOCKET, SO_PEERSEC, s, &n) >= 0)
+ if (getsockopt(fd, SOL_SOCKET, SO_PEERSEC, s, &n) >= 0) {
+ s[n] = 0;
break;
+ }
if (errno != ERANGE)
return -errno;
@@ -927,12 +937,16 @@ int getpeersec(int fd, char **ret) {
}
int getpeergroups(int fd, gid_t **ret) {
- socklen_t n = sizeof(gid_t) * 64;
+ socklen_t n = sizeof(gid_t) * 64U;
_cleanup_free_ gid_t *d = NULL;
assert(fd >= 0);
assert(ret);
+ long ngroups_max = sysconf(_SC_NGROUPS_MAX);
+ if (ngroups_max > 0)
+ n = MAX(n, sizeof(gid_t) * (socklen_t) ngroups_max);
+
for (;;) {
d = malloc(n);
if (!d)
@@ -950,7 +964,7 @@ int getpeergroups(int fd, gid_t **ret) {
assert_se(n % sizeof(gid_t) == 0);
n /= sizeof(gid_t);
- if ((socklen_t) (int) n != n)
+ if (n > INT_MAX)
return -E2BIG;
*ret = TAKE_PTR(d);
@@ -958,6 +972,68 @@ int getpeergroups(int fd, gid_t **ret) {
return (int) n;
}
+int getpeerpidfd(int fd) {
+ socklen_t n = sizeof(int);
+ int pidfd = -EBADF;
+
+ assert(fd >= 0);
+
+ if (getsockopt(fd, SOL_SOCKET, SO_PEERPIDFD, &pidfd, &n) < 0)
+ return -errno;
+
+ if (n != sizeof(int))
+ return -EIO;
+
+ return pidfd;
+}
+
+ssize_t send_many_fds_iov_sa(
+ int transport_fd,
+ int *fds_array, size_t n_fds_array,
+ const struct iovec *iov, size_t iovlen,
+ const struct sockaddr *sa, socklen_t len,
+ int flags) {
+
+ _cleanup_free_ struct cmsghdr *cmsg = NULL;
+ struct msghdr mh = {
+ .msg_name = (struct sockaddr*) sa,
+ .msg_namelen = len,
+ .msg_iov = (struct iovec *)iov,
+ .msg_iovlen = iovlen,
+ };
+ ssize_t k;
+
+ assert(transport_fd >= 0);
+ assert(fds_array || n_fds_array == 0);
+
+ /* The kernel will reject sending more than SCM_MAX_FD FDs at once */
+ if (n_fds_array > SCM_MAX_FD)
+ return -E2BIG;
+
+ /* We need either an FD array or data to send. If there's nothing, return an error. */
+ if (n_fds_array == 0 && !iov)
+ return -EINVAL;
+
+ if (n_fds_array > 0) {
+ mh.msg_controllen = CMSG_SPACE(sizeof(int) * n_fds_array);
+ mh.msg_control = cmsg = malloc(mh.msg_controllen);
+ if (!cmsg)
+ return -ENOMEM;
+
+ *cmsg = (struct cmsghdr) {
+ .cmsg_len = CMSG_LEN(sizeof(int) * n_fds_array),
+ .cmsg_level = SOL_SOCKET,
+ .cmsg_type = SCM_RIGHTS,
+ };
+ memcpy(CMSG_DATA(cmsg), fds_array, sizeof(int) * n_fds_array);
+ }
+ k = sendmsg(transport_fd, &mh, MSG_NOSIGNAL | flags);
+ if (k < 0)
+ return (ssize_t) -errno;
+
+ return k;
+}
+
ssize_t send_one_fd_iov_sa(
int transport_fd,
int fd,
@@ -1013,6 +1089,78 @@ int send_one_fd_sa(
return (int) send_one_fd_iov_sa(transport_fd, fd, NULL, 0, sa, len, flags);
}
+ssize_t receive_many_fds_iov(
+ int transport_fd,
+ struct iovec *iov, size_t iovlen,
+ int **ret_fds_array, size_t *ret_n_fds_array,
+ int flags) {
+
+ CMSG_BUFFER_TYPE(CMSG_SPACE(sizeof(int) * SCM_MAX_FD)) control;
+ struct msghdr mh = {
+ .msg_control = &control,
+ .msg_controllen = sizeof(control),
+ .msg_iov = iov,
+ .msg_iovlen = iovlen,
+ };
+ _cleanup_free_ int *fds_array = NULL;
+ size_t n_fds_array = 0;
+ struct cmsghdr *cmsg;
+ ssize_t k;
+
+ assert(transport_fd >= 0);
+ assert(ret_fds_array);
+ assert(ret_n_fds_array);
+
+ /*
+ * Receive many FDs via @transport_fd. We don't care for the transport-type. We retrieve all the FDs
+ * at once. This is best used in combination with send_many_fds().
+ */
+
+ k = recvmsg_safe(transport_fd, &mh, MSG_CMSG_CLOEXEC | flags);
+ if (k < 0)
+ return k;
+
+ CMSG_FOREACH(cmsg, &mh)
+ if (cmsg->cmsg_level == SOL_SOCKET && cmsg->cmsg_type == SCM_RIGHTS) {
+ size_t n = (cmsg->cmsg_len - CMSG_LEN(0)) / sizeof(int);
+
+ fds_array = GREEDY_REALLOC(fds_array, n_fds_array + n);
+ if (!fds_array) {
+ cmsg_close_all(&mh);
+ return -ENOMEM;
+ }
+
+ memcpy(fds_array + n_fds_array, CMSG_TYPED_DATA(cmsg, int), sizeof(int) * n);
+ n_fds_array += n;
+ }
+
+ if (n_fds_array == 0) {
+ cmsg_close_all(&mh);
+
+ /* If didn't receive an FD or any data, return an error. */
+ if (k == 0)
+ return -EIO;
+ }
+
+ *ret_fds_array = TAKE_PTR(fds_array);
+ *ret_n_fds_array = n_fds_array;
+
+ return k;
+}
+
+int receive_many_fds(int transport_fd, int **ret_fds_array, size_t *ret_n_fds_array, int flags) {
+ ssize_t k;
+
+ k = receive_many_fds_iov(transport_fd, NULL, 0, ret_fds_array, ret_n_fds_array, flags);
+ if (k == 0)
+ return 0;
+
+ /* k must be negative, since receive_many_fds_iov() only returns a positive value if data was received
+ * through the iov. */
+ assert(k < 0);
+ return (int) k;
+}
+
ssize_t receive_one_fd_iov(
int transport_fd,
struct iovec *iov, size_t iovlen,
@@ -1189,7 +1337,7 @@ void* cmsg_find_and_copy_data(struct msghdr *mh, int level, int type, void *buf,
assert(buf_len > 0);
/* This is similar to cmsg_find_data(), but copy the found data to buf. This should be typically used
- * when reading possibly unaligned data such as timestamp, as time_t is 64bit and size_t is 32bit on
+ * when reading possibly unaligned data such as timestamp, as time_t is 64-bit and size_t is 32-bit on
* RISCV32. See issue #27241. */
cmsg = cmsg_find(mh, level, type, CMSG_LEN(buf_len));
@@ -1532,6 +1680,50 @@ int socket_address_parse_unix(SocketAddress *ret_address, const char *s) {
}
#if 0 /* NM_IGNORED */
+int vsock_parse_port(const char *s, unsigned *ret) {
+ int r;
+
+ assert(ret);
+
+ if (!s)
+ return -EINVAL;
+
+ unsigned u;
+ r = safe_atou(s, &u);
+ if (r < 0)
+ return r;
+
+ /* Port 0 is apparently valid and not special in AF_VSOCK (unlike on IP). But VMADDR_PORT_ANY
+ * (UINT32_MAX) is. Hence refuse that. */
+
+ if (u == VMADDR_PORT_ANY)
+ return -EINVAL;
+
+ *ret = u;
+ return 0;
+}
+
+int vsock_parse_cid(const char *s, unsigned *ret) {
+ assert(ret);
+
+ if (!s)
+ return -EINVAL;
+
+ /* Parsed an AF_VSOCK "CID". This is a 32bit entity, and the usual type is "unsigned". We recognize
+ * the three special CIDs as strings, and otherwise parse the numeric CIDs. */
+
+ if (streq(s, "hypervisor"))
+ *ret = VMADDR_CID_HYPERVISOR;
+ else if (streq(s, "local"))
+ *ret = VMADDR_CID_LOCAL;
+ else if (streq(s, "host"))
+ *ret = VMADDR_CID_HOST;
+ else
+ return safe_atou(s, ret);
+
+ return 0;
+}
+
int socket_address_parse_vsock(SocketAddress *ret_address, const char *s) {
/* AF_VSOCK socket in vsock:cid:port notation */
_cleanup_free_ char *n = NULL;
@@ -1557,7 +1749,7 @@ int socket_address_parse_vsock(SocketAddress *ret_address, const char *s) {
if (!e)
return -EINVAL;
- r = safe_atou(e+1, &port);
+ r = vsock_parse_port(e+1, &port);
if (r < 0)
return r;
@@ -1568,15 +1760,15 @@ int socket_address_parse_vsock(SocketAddress *ret_address, const char *s) {
if (isempty(n))
cid = VMADDR_CID_ANY;
else {
- r = safe_atou(n, &cid);
+ r = vsock_parse_cid(n, &cid);
if (r < 0)
return r;
}
*ret_address = (SocketAddress) {
.sockaddr.vm = {
- .svm_cid = cid,
.svm_family = AF_VSOCK,
+ .svm_cid = cid,
.svm_port = port,
},
.type = type,
@@ -1585,4 +1777,19 @@ int socket_address_parse_vsock(SocketAddress *ret_address, const char *s) {
return 0;
}
+
+int vsock_get_local_cid(unsigned *ret) {
+ _cleanup_close_ int vsock_fd = -EBADF;
+
+ assert(ret);
+
+ vsock_fd = open("/dev/vsock", O_RDONLY|O_CLOEXEC);
+ if (vsock_fd < 0)
+ return log_debug_errno(errno, "Failed to open /dev/vsock: %m");
+
+ if (ioctl(vsock_fd, IOCTL_VM_SOCKETS_GET_LOCAL_CID, ret) < 0)
+ return log_debug_errno(errno, "Failed to query local AF_VSOCK CID: %m");
+
+ return 0;
+}
#endif /* NM_IGNORED */
diff --git a/src/libnm-systemd-shared/src/basic/socket-util.h b/src/libnm-systemd-shared/src/basic/socket-util.h
index 26f9636fa6..15c7d1c52f 100644
--- a/src/libnm-systemd-shared/src/basic/socket-util.h
+++ b/src/libnm-systemd-shared/src/basic/socket-util.h
@@ -115,7 +115,7 @@ int sockaddr_pretty(const struct sockaddr *_sa, socklen_t salen, bool translate_
int getpeername_pretty(int fd, bool include_port, char **ret);
int getsockname_pretty(int fd, char **ret);
-int socknameinfo_pretty(union sockaddr_union *sa, socklen_t salen, char **_ret);
+int socknameinfo_pretty(const struct sockaddr *sa, socklen_t salen, char **_ret);
const char* socket_address_bind_ipv6_only_to_string(SocketAddressBindIPv6Only b) _const_;
SocketAddressBindIPv6Only socket_address_bind_ipv6_only_from_string(const char *s) _pure_;
@@ -154,7 +154,30 @@ bool address_label_valid(const char *p);
int getpeercred(int fd, struct ucred *ucred);
int getpeersec(int fd, char **ret);
int getpeergroups(int fd, gid_t **ret);
+int getpeerpidfd(int fd);
+ssize_t send_many_fds_iov_sa(
+ int transport_fd,
+ int *fds_array, size_t n_fds_array,
+ const struct iovec *iov, size_t iovlen,
+ const struct sockaddr *sa, socklen_t len,
+ int flags);
+static inline ssize_t send_many_fds_iov(
+ int transport_fd,
+ int *fds_array, size_t n_fds_array,
+ const struct iovec *iov, size_t iovlen,
+ int flags) {
+
+ return send_many_fds_iov_sa(transport_fd, fds_array, n_fds_array, iov, iovlen, NULL, 0, flags);
+}
+static inline int send_many_fds(
+ int transport_fd,
+ int *fds_array,
+ size_t n_fds_array,
+ int flags) {
+
+ return send_many_fds_iov_sa(transport_fd, fds_array, n_fds_array, NULL, 0, NULL, 0, flags);
+}
ssize_t send_one_fd_iov_sa(
int transport_fd,
int fd,
@@ -169,6 +192,8 @@ int send_one_fd_sa(int transport_fd,
#define send_one_fd(transport_fd, fd, flags) send_one_fd_iov_sa(transport_fd, fd, NULL, 0, NULL, 0, flags)
ssize_t receive_one_fd_iov(int transport_fd, struct iovec *iov, size_t iovlen, int flags, int *ret_fd);
int receive_one_fd(int transport_fd, int flags);
+ssize_t receive_many_fds_iov(int transport_fd, struct iovec *iov, size_t iovlen, int **ret_fds_array, size_t *ret_n_fds_array, int flags);
+int receive_many_fds(int transport_fd, int **ret_fds_array, size_t *ret_n_fds_array, int flags);
ssize_t next_datagram_size_fd(int fd);
@@ -181,7 +206,7 @@ int flush_accept(int fd);
* at compile time, that the requested type has a smaller or same alignment as 'struct cmsghdr', and one
* during runtime, that the actual pointer matches the alignment too. This is supposed to catch cases such as
* 'struct timeval' is embedded into 'struct cmsghdr' on architectures where the alignment of the former is 8
- * bytes (because of a 64bit time_t), but of the latter is 4 bytes (because size_t is 32bit), such as
+ * bytes (because of a 64-bit time_t), but of the latter is 4 bytes (because size_t is 32 bits), such as
* riscv32. */
#define CMSG_TYPED_DATA(cmsg, type) \
({ \
@@ -296,7 +321,7 @@ static inline int getsockopt_int(int fd, int level, int optname, int *ret) {
int socket_bind_to_ifname(int fd, const char *ifname);
int socket_bind_to_ifindex(int fd, int ifindex);
-/* Define a 64bit version of timeval/timespec in any case, even on 32bit userspace. */
+/* Define a 64-bit version of timeval/timespec in any case, even on 32-bit userspace. */
struct timeval_large {
uint64_t tvl_sec, tvl_usec;
};
@@ -304,7 +329,7 @@ struct timespec_large {
uint64_t tvl_sec, tvl_nsec;
};
-/* glibc duplicates timespec/timeval on certain 32bit archs, once in 32bit and once in 64bit.
+/* glibc duplicates timespec/timeval on certain 32-bit arches, once in 32-bit and once in 64-bit.
* See __convert_scm_timestamps() in glibc source code. Hence, we need additional buffer space for them
* to prevent from recvmsg_safe() returning -EXFULL. */
#define CMSG_SPACE_TIMEVAL \
@@ -353,6 +378,14 @@ int socket_get_mtu(int fd, int af, size_t *ret);
int connect_unix_path(int fd, int dir_fd, const char *path);
+static inline bool VSOCK_CID_IS_REGULAR(unsigned cid) {
+ /* 0, 1, 2, UINT32_MAX are special, refuse those */
+ return cid > 2 && cid < UINT32_MAX;
+}
+
+int vsock_parse_port(const char *s, unsigned *ret);
+int vsock_parse_cid(const char *s, unsigned *ret);
+
/* Parses AF_UNIX and AF_VSOCK addresses. AF_INET[6] require some netlink calls, so it cannot be in
* src/basic/ and is done from 'socket_local_address from src/shared/. Return -EPROTO in case of
* protocol mismatch. */
@@ -365,3 +398,5 @@ int socket_address_parse_vsock(SocketAddress *ret_address, const char *s);
* /proc/sys/net/core/somaxconn anyway, thus by setting this to unbounded we just make that sysctl file
* authoritative. */
#define SOMAXCONN_DELUXE INT_MAX
+
+int vsock_get_local_cid(unsigned *ret);
diff --git a/src/libnm-systemd-shared/src/basic/sort-util.h b/src/libnm-systemd-shared/src/basic/sort-util.h
index 52d611b820..9c818bd747 100644
--- a/src/libnm-systemd-shared/src/basic/sort-util.h
+++ b/src/libnm-systemd-shared/src/basic/sort-util.h
@@ -18,7 +18,7 @@ void *xbsearch_r(const void *key, const void *base, size_t nmemb, size_t size,
({ \
const typeof((b)[0]) *_k = k; \
int (*_func_)(const typeof((b)[0])*, const typeof((b)[0])*, typeof(userdata)) = func; \
- xbsearch_r((const void*) _k, (b), (n), sizeof((b)[0]), (comparison_userdata_fn_t) _func_, userdata); \
+ (typeof((b)[0])*) xbsearch_r((const void*) _k, (b), (n), sizeof((b)[0]), (comparison_userdata_fn_t) _func_, userdata); \
})
/**
@@ -38,7 +38,7 @@ static inline void* bsearch_safe(const void *key, const void *base,
({ \
const typeof((b)[0]) *_k = k; \
int (*_func_)(const typeof((b)[0])*, const typeof((b)[0])*) = func; \
- bsearch_safe((const void*) _k, (b), (n), sizeof((b)[0]), (comparison_fn_t) _func_); \
+ (typeof((b)[0])*) bsearch_safe((const void*) _k, (b), (n), sizeof((b)[0]), (comparison_fn_t) _func_); \
})
/**
@@ -61,7 +61,6 @@ static inline void _qsort_safe(void *base, size_t nmemb, size_t size, comparison
_qsort_safe((p), (n), sizeof((p)[0]), (comparison_fn_t) _func_); \
})
-#if 0 /* NM_IGNORED */
static inline void qsort_r_safe(void *base, size_t nmemb, size_t size, comparison_userdata_fn_t compar, void *userdata) {
if (nmemb <= 1)
return;
@@ -75,6 +74,6 @@ static inline void qsort_r_safe(void *base, size_t nmemb, size_t size, compariso
int (*_func_)(const typeof((p)[0])*, const typeof((p)[0])*, typeof(userdata)) = func; \
qsort_r_safe((p), (n), sizeof((p)[0]), (comparison_userdata_fn_t) _func_, userdata); \
})
-#endif /* NM_IGNORED */
int cmp_int(const int *a, const int *b);
+int cmp_uint16(const uint16_t *a, const uint16_t *b);
diff --git a/src/libnm-systemd-shared/src/basic/stat-util.c b/src/libnm-systemd-shared/src/basic/stat-util.c
index a81ee468ff..b50544c9f3 100644
--- a/src/libnm-systemd-shared/src/basic/stat-util.c
+++ b/src/libnm-systemd-shared/src/basic/stat-util.c
@@ -193,14 +193,12 @@ int inode_same_at(int fda, const char *filea, int fdb, const char *fileb, int fl
struct stat a, b;
assert(fda >= 0 || fda == AT_FDCWD);
- assert(filea);
assert(fdb >= 0 || fdb == AT_FDCWD);
- assert(fileb);
- if (fstatat(fda, filea, &a, flags) < 0)
+ if (fstatat(fda, strempty(filea), &a, flags) < 0)
return log_debug_errno(errno, "Cannot stat %s: %m", filea);
- if (fstatat(fdb, fileb, &b, flags) < 0)
+ if (fstatat(fdb, strempty(fileb), &b, flags) < 0)
return log_debug_errno(errno, "Cannot stat %s: %m", fileb);
return stat_inode_same(&a, &b);
@@ -405,21 +403,35 @@ bool statx_mount_same(const struct new_statx *a, const struct new_statx *b) {
a->stx_dev_minor == b->stx_dev_minor;
}
+static bool is_statx_fatal_error(int err, int flags) {
+ assert(err < 0);
+
+ /* If statx() is not supported or if we see EPERM (which might indicate seccomp filtering or so),
+ * let's do a fallback. Note that on EACCES we'll not fall back, since that is likely an indication of
+ * fs access issues, which we should propagate. */
+ if (ERRNO_IS_NOT_SUPPORTED(err) || err == -EPERM)
+ return false;
+
+ /* When unsupported flags are specified, glibc's fallback function returns -EINVAL.
+ * See statx_generic() in glibc. */
+ if (err != -EINVAL)
+ return true;
+
+ if ((flags & ~(AT_EMPTY_PATH | AT_NO_AUTOMOUNT | AT_SYMLINK_NOFOLLOW | AT_STATX_SYNC_AS_STAT)) != 0)
+ return false; /* Unsupported flags are specified. Let's try to use our implementation. */
+
+ return true;
+}
+
int statx_fallback(int dfd, const char *path, int flags, unsigned mask, struct statx *sx) {
static bool avoid_statx = false;
struct stat st;
+ int r;
if (!avoid_statx) {
- if (statx(dfd, path, flags, mask, sx) < 0) {
- if (!ERRNO_IS_NOT_SUPPORTED(errno) && errno != EPERM)
- return -errno;
-
- /* If statx() is not supported or if we see EPERM (which might indicate seccomp
- * filtering or so), let's do a fallback. Not that on EACCES we'll not fall back,
- * since that is likely an indication of fs access issues, which we should
- * propagate */
- } else
- return 0;
+ r = RET_NERRNO(statx(dfd, path, flags, mask, sx));
+ if (r >= 0 || is_statx_fatal_error(r, flags))
+ return r;
avoid_statx = true;
}
@@ -477,8 +489,8 @@ int xstatfsat(int dir_fd, const char *path, struct statfs *ret) {
#if 0 /* NM_IGNORED */
void inode_hash_func(const struct stat *q, struct siphash *state) {
- siphash24_compress(&q->st_dev, sizeof(q->st_dev), state);
- siphash24_compress(&q->st_ino, sizeof(q->st_ino), state);
+ siphash24_compress_typesafe(q->st_dev, state);
+ siphash24_compress_typesafe(q->st_ino, state);
}
int inode_compare_func(const struct stat *a, const struct stat *b) {
@@ -503,6 +515,8 @@ const char* inode_type_to_string(mode_t m) {
return "reg";
case S_IFDIR:
return "dir";
+ case S_IFLNK:
+ return "lnk";
case S_IFCHR:
return "chr";
case S_IFBLK:
@@ -515,4 +529,26 @@ const char* inode_type_to_string(mode_t m) {
return NULL;
}
+
+mode_t inode_type_from_string(const char *s) {
+ if (!s)
+ return MODE_INVALID;
+
+ if (streq(s, "reg"))
+ return S_IFREG;
+ if (streq(s, "dir"))
+ return S_IFDIR;
+ if (streq(s, "lnk"))
+ return S_IFLNK;
+ if (streq(s, "chr"))
+ return S_IFCHR;
+ if (streq(s, "blk"))
+ return S_IFBLK;
+ if (streq(s, "fifo"))
+ return S_IFIFO;
+ if (streq(s, "sock"))
+ return S_IFSOCK;
+
+ return MODE_INVALID;
+}
#endif /* NM_IGNORED */
diff --git a/src/libnm-systemd-shared/src/basic/stat-util.h b/src/libnm-systemd-shared/src/basic/stat-util.h
index ae0aaf8f51..dc11a85f62 100644
--- a/src/libnm-systemd-shared/src/basic/stat-util.h
+++ b/src/libnm-systemd-shared/src/basic/stat-util.h
@@ -12,6 +12,7 @@
#include "macro.h"
#include "missing_stat.h"
#include "siphash24.h"
+#include "time-util.h"
int is_symlink(const char *path);
int is_dir_full(int atfd, const char *fname, bool follow);
@@ -109,8 +110,16 @@ int xstatfsat(int dir_fd, const char *path, struct statfs *ret);
} var
#endif
+static inline usec_t statx_timestamp_load(const struct statx_timestamp *ts) {
+ return timespec_load(&(const struct timespec) { .tv_sec = ts->tv_sec, .tv_nsec = ts->tv_nsec });
+}
+static inline nsec_t statx_timestamp_load_nsec(const struct statx_timestamp *ts) {
+ return timespec_load_nsec(&(const struct timespec) { .tv_sec = ts->tv_sec, .tv_nsec = ts->tv_nsec });
+}
+
void inode_hash_func(const struct stat *q, struct siphash *state);
int inode_compare_func(const struct stat *a, const struct stat *b);
extern const struct hash_ops inode_hash_ops;
const char* inode_type_to_string(mode_t m);
+mode_t inode_type_from_string(const char *s);
diff --git a/src/libnm-systemd-shared/src/basic/string-util.c b/src/libnm-systemd-shared/src/basic/string-util.c
index 1afa49bba0..539c0c76fe 100644
--- a/src/libnm-systemd-shared/src/basic/string-util.c
+++ b/src/libnm-systemd-shared/src/basic/string-util.c
@@ -18,6 +18,7 @@
#include "macro.h"
#include "memory-util.h"
#include "memstream-util.h"
+#include "path-util.h"
#include "string-util.h"
#include "strv.h"
#include "terminal-util.h"
@@ -174,10 +175,15 @@ char *delete_trailing_chars(char *s, const char *bad) {
}
#endif /* NM_IGNORED */
-char *truncate_nl(char *s) {
+char *truncate_nl_full(char *s, size_t *ret_len) {
+ size_t n;
+
assert(s);
- s[strcspn(s, NEWLINE)] = 0;
+ n = strcspn(s, NEWLINE);
+ s[n] = '\0';
+ if (ret_len)
+ *ret_len = n;
return s;
}
@@ -294,6 +300,62 @@ static int write_ellipsis(char *buf, bool unicode) {
return 3;
}
+static size_t ansi_sequence_length(const char *s, size_t len) {
+ assert(s);
+
+ if (len < 2)
+ return 0;
+
+ if (s[0] != 0x1B) /* ASCII 27, aka ESC, aka Ctrl-[ */
+ return 0; /* Not the start of a sequence */
+
+ if (s[1] == 0x5B) { /* [, start of CSI sequence */
+ size_t i = 2;
+
+ if (i == len)
+ return 0;
+
+ while (s[i] >= 0x30 && s[i] <= 0x3F) /* Parameter bytes */
+ if (++i == len)
+ return 0;
+ while (s[i] >= 0x20 && s[i] <= 0x2F) /* Intermediate bytes */
+ if (++i == len)
+ return 0;
+ if (s[i] >= 0x40 && s[i] <= 0x7E) /* Final byte */
+ return i + 1;
+ return 0; /* Bad sequence */
+
+ } else if (s[1] >= 0x40 && s[1] <= 0x5F) /* other non-CSI Fe sequence */
+ return 2;
+
+ return 0; /* Bad escape? */
+}
+
+static bool string_has_ansi_sequence(const char *s, size_t len) {
+ const char *t = s;
+
+ while ((t = memchr(s, 0x1B, len - (t - s))))
+ if (ansi_sequence_length(t, len - (t - s)) > 0)
+ return true;
+ return false;
+}
+
+static size_t previous_ansi_sequence(const char *s, size_t length, const char **ret_where) {
+ /* Locate the previous ANSI sequence and save its start in *ret_where and return length. */
+
+ for (size_t i = length - 2; i > 0; i--) { /* -2 because at least two bytes are needed */
+ size_t slen = ansi_sequence_length(s + (i - 1), length - (i - 1));
+ if (slen == 0)
+ continue;
+
+ *ret_where = s + (i - 1);
+ return slen;
+ }
+
+ *ret_where = NULL;
+ return 0;
+}
+
static char *ascii_ellipsize_mem(const char *s, size_t old_length, size_t new_length, unsigned percent) {
size_t x, need_space, suffix_len;
char *t;
@@ -353,7 +415,6 @@ static char *ascii_ellipsize_mem(const char *s, size_t old_length, size_t new_le
char *ellipsize_mem(const char *s, size_t old_length, size_t new_length, unsigned percent) {
size_t x, k, len, len2;
const char *i, *j;
- char *e;
int r;
/* Note that 'old_length' refers to bytes in the string, while 'new_length' refers to character cells taken up
@@ -377,73 +438,116 @@ char *ellipsize_mem(const char *s, size_t old_length, size_t new_length, unsigne
if (new_length == 0)
return strdup("");
- /* If no multibyte characters use ascii_ellipsize_mem for speed */
- if (ascii_is_valid_n(s, old_length))
+ bool has_ansi_seq = string_has_ansi_sequence(s, old_length);
+
+ /* If no multibyte characters or ANSI sequences, use ascii_ellipsize_mem for speed */
+ if (!has_ansi_seq && ascii_is_valid_n(s, old_length))
return ascii_ellipsize_mem(s, old_length, new_length, percent);
- x = ((new_length - 1) * percent) / 100;
+ x = (new_length - 1) * percent / 100;
assert(x <= new_length - 1);
k = 0;
- for (i = s; i < s + old_length; i = utf8_next_char(i)) {
- char32_t c;
- int w;
+ for (i = s; i < s + old_length; ) {
+ size_t slen = has_ansi_seq ? ansi_sequence_length(i, old_length - (i - s)) : 0;
+ if (slen > 0) {
+ i += slen;
+ continue; /* ANSI sequences don't take up any space in output */
+ }
+ char32_t c;
r = utf8_encoded_to_unichar(i, &c);
if (r < 0)
return NULL;
- w = unichar_iswide(c) ? 2 : 1;
- if (k + w <= x)
- k += w;
- else
+ int w = unichar_iswide(c) ? 2 : 1;
+ if (k + w > x)
break;
+
+ k += w;
+ i += r;
}
- for (j = s + old_length; j > i; ) {
+ const char *ansi_start = s + old_length;
+ size_t ansi_len = 0;
+
+ for (const char *t = j = s + old_length; t > i && k < new_length; ) {
char32_t c;
int w;
- const char *jj;
+ const char *tt;
- jj = utf8_prev_char(j);
- r = utf8_encoded_to_unichar(jj, &c);
+ if (has_ansi_seq && ansi_start >= t)
+ /* Figure out the previous ANSI sequence, if any */
+ ansi_len = previous_ansi_sequence(s, t - s, &ansi_start);
+
+ /* If the sequence extends all the way to the current position, skip it. */
+ if (has_ansi_seq && ansi_len > 0 && ansi_start + ansi_len == t) {
+ t = ansi_start;
+ continue;
+ }
+
+ tt = utf8_prev_char(t);
+ r = utf8_encoded_to_unichar(tt, &c);
if (r < 0)
return NULL;
w = unichar_iswide(c) ? 2 : 1;
- if (k + w <= new_length) {
- k += w;
- j = jj;
- } else
+ if (k + w > new_length)
break;
+
+ k += w;
+ j = t = tt; /* j should always point to the first "real" character */
}
- assert(i <= j);
- /* we don't actually need to ellipsize */
- if (i == j)
+ /* We don't actually need to ellipsize */
+ if (i >= j)
return memdup_suffix0(s, old_length);
- /* make space for ellipsis, if possible */
- if (j < s + old_length)
- j = utf8_next_char(j);
- else if (i > s)
- i = utf8_prev_char(i);
+ if (k >= new_length) {
+ /* Make space for ellipsis, if required and possible. We know that the edge character is not
+ * part of an ANSI sequence (because then we'd skip it). If the last character we looked at
+ * was wide, we don't need to make space. */
+ if (j < s + old_length)
+ j = utf8_next_char(j);
+ else if (i > s)
+ i = utf8_prev_char(i);
+ }
len = i - s;
len2 = s + old_length - j;
- e = new(char, len + 3 + len2 + 1);
+
+ /* If we have ANSI, allow the same length as the source string + ellipsis. It'd be too involved to
+ * figure out what exact space is needed. Strings with ANSI sequences are most likely to be fairly
+ * short anyway. */
+ size_t alloc_len = has_ansi_seq ? old_length + 3 + 1 : len + 3 + len2 + 1;
+
+ char *e = new(char, alloc_len);
if (!e)
return NULL;
/*
- printf("old_length=%zu new_length=%zu x=%zu len=%u len2=%u k=%u\n",
+ printf("old_length=%zu new_length=%zu x=%zu len=%zu len2=%zu k=%zu\n",
old_length, new_length, x, len, len2, k);
*/
- memcpy(e, s, len);
+ memcpy_safe(e, s, len);
write_ellipsis(e + len, true);
- memcpy(e + len + 3, j, len2);
- *(e + len + 3 + len2) = '\0';
+
+ char *dst = e + len + 3;
+
+ if (has_ansi_seq)
+ /* Copy over any ANSI sequences in full */
+ for (const char *p = s + len; p < j; ) {
+ size_t slen = ansi_sequence_length(p, j - p);
+ if (slen > 0) {
+ dst = mempcpy(dst, p, slen);
+ p += slen;
+ } else
+ p = utf8_next_char(p);
+ }
+
+ memcpy_safe(dst, j, len2);
+ dst[len2] = '\0';
return e;
}
@@ -528,14 +632,23 @@ char* strshorten(char *s, size_t l) {
}
int strgrowpad0(char **s, size_t l) {
+ size_t sz;
+
assert(s);
+ if (*s) {
+ sz = strlen(*s) + 1;
+ if (sz >= l) /* never shrink */
+ return 0;
+ } else
+ sz = 0;
+
char *q = realloc(*s, l);
if (!q)
return -ENOMEM;
+
*s = q;
- size_t sz = strlen(*s);
memzero(*s + sz, l - sz);
return 0;
}
@@ -893,6 +1006,33 @@ oom:
return -ENOMEM;
}
+char *strextendn(char **x, const char *s, size_t l) {
+ assert(x);
+ assert(s || l == 0);
+
+ if (l == SIZE_MAX)
+ l = strlen_ptr(s);
+ else if (l > 0)
+ l = strnlen(s, l); /* ignore trailing noise */
+
+ if (l > 0 || !*x) {
+ size_t q;
+ char *m;
+
+ q = strlen_ptr(*x);
+ m = realloc(*x, q + l + 1);
+ if (!m)
+ return NULL;
+
+ memcpy_safe(m + q, s, l);
+ m[q + l] = 0;
+
+ *x = m;
+ }
+
+ return *x;
+}
+
char *strrep(const char *s, unsigned n) {
char *r, *p;
size_t l;
@@ -1191,6 +1331,7 @@ char *string_replace_char(char *str, char old_char, char new_char) {
return str;
}
+#endif /* NM_IGNORED */
int make_cstring(const char *s, size_t n, MakeCStringMode mode, char **ret) {
char *b;
@@ -1235,6 +1376,7 @@ int make_cstring(const char *s, size_t n, MakeCStringMode mode, char **ret) {
return 0;
}
+#if 0 /* NM_IGNORED */
size_t strspn_from_end(const char *str, const char *accept) {
size_t n = 0;
@@ -1290,14 +1432,109 @@ char *find_line_startswith(const char *haystack, const char *needle) {
}
#endif /* NM_IGNORED */
-char *startswith_strv(const char *string, char **strv) {
- char *found = NULL;
+bool version_is_valid(const char *s) {
+ if (isempty(s))
+ return false;
- STRV_FOREACH(i, strv) {
- found = startswith(string, *i);
- if (found)
- break;
+ if (!filename_part_is_valid(s))
+ return false;
+
+ /* This is a superset of the characters used by semver. We additionally allow "," and "_". */
+ if (!in_charset(s, ALPHANUMERICAL ".,_-+"))
+ return false;
+
+ return true;
+}
+
+bool version_is_valid_versionspec(const char *s) {
+ if (!filename_part_is_valid(s))
+ return false;
+
+ if (!in_charset(s, ALPHANUMERICAL "-.~^"))
+ return false;
+
+ return true;
+}
+
+ssize_t strlevenshtein(const char *x, const char *y) {
+ _cleanup_free_ size_t *t0 = NULL, *t1 = NULL, *t2 = NULL;
+ size_t xl, yl;
+
+ /* This is inspired from the Linux kernel's Levenshtein implementation */
+
+ if (streq_ptr(x, y))
+ return 0;
+
+ xl = strlen_ptr(x);
+ if (xl > SSIZE_MAX)
+ return -E2BIG;
+
+ yl = strlen_ptr(y);
+ if (yl > SSIZE_MAX)
+ return -E2BIG;
+
+ if (isempty(x))
+ return yl;
+ if (isempty(y))
+ return xl;
+
+ t0 = new0(size_t, yl + 1);
+ if (!t0)
+ return -ENOMEM;
+ t1 = new0(size_t, yl + 1);
+ if (!t1)
+ return -ENOMEM;
+ t2 = new0(size_t, yl + 1);
+ if (!t2)
+ return -ENOMEM;
+
+ for (size_t i = 0; i <= yl; i++)
+ t1[i] = i;
+
+ for (size_t i = 0; i < xl; i++) {
+ t2[0] = i + 1;
+
+ for (size_t j = 0; j < yl; j++) {
+ /* Substitution */
+ t2[j+1] = t1[j] + (x[i] != y[j]);
+
+ /* Swap */
+ if (i > 0 && j > 0 && x[i-1] == y[j] && x[i] == y[j-1] && t2[j+1] > t0[j-1] + 1)
+ t2[j+1] = t0[j-1] + 1;
+
+ /* Deletion */
+ if (t2[j+1] > t1[j+1] + 1)
+ t2[j+1] = t1[j+1] + 1;
+
+ /* Insertion */
+ if (t2[j+1] > t2[j] + 1)
+ t2[j+1] = t2[j] + 1;
+ }
+
+ size_t *dummy = t0;
+ t0 = t1;
+ t1 = t2;
+ t2 = dummy;
}
- return found;
+ return t1[yl];
+}
+
+char *strrstr(const char *haystack, const char *needle) {
+ /* Like strstr() but returns the last rather than the first occurrence of "needle" in "haystack". */
+
+ if (!haystack || !needle)
+ return NULL;
+
+ /* Special case: for the empty string we return the very last possible occurrence, i.e. *after* the
+ * last char, not before. */
+ if (*needle == 0)
+ return strchr(haystack, 0);
+
+ for (const char *p = strstr(haystack, needle), *q; p; p = q) {
+ q = strstr(p + 1, needle);
+ if (!q)
+ return (char *) p;
+ }
+ return NULL;
}
diff --git a/src/libnm-systemd-shared/src/basic/string-util.h b/src/libnm-systemd-shared/src/basic/string-util.h
index 4430910e22..e162765aa7 100644
--- a/src/libnm-systemd-shared/src/basic/string-util.h
+++ b/src/libnm-systemd-shared/src/basic/string-util.h
@@ -22,6 +22,9 @@
#define ALPHANUMERICAL LETTERS DIGITS
#define HEXDIGITS DIGITS "abcdefABCDEF"
#define LOWERCASE_HEXDIGITS DIGITS "abcdef"
+#define URI_RESERVED ":/?#[]@!$&'()*+;=" /* [RFC3986] */
+#define URI_UNRESERVED ALPHANUMERICAL "-._~" /* [RFC3986] */
+#define URI_VALID URI_RESERVED URI_UNRESERVED /* [RFC3986] */
static inline char* strstr_ptr(const char *haystack, const char *needle) {
if (!haystack || !needle)
@@ -65,6 +68,10 @@ static inline const char* enable_disable(bool b) {
return b ? "enable" : "disable";
}
+static inline const char* enabled_disabled(bool b) {
+ return b ? "enabled" : "disabled";
+}
+
/* This macro's return pointer will have the "const" qualifier set or unset the same way as the input
* pointer. */
#define empty_to_null(p) \
@@ -121,7 +128,10 @@ char *strjoin_real(const char *x, ...) _sentinel_;
char *strstrip(char *s);
char *delete_chars(char *s, const char *bad);
char *delete_trailing_chars(char *s, const char *bad);
-char *truncate_nl(char *s);
+char *truncate_nl_full(char *s, size_t *ret_len);
+static inline char *truncate_nl(char *s) {
+ return truncate_nl_full(s, NULL);
+}
static inline char *skip_leading_chars(const char *s, const char *bad) {
if (!s)
@@ -183,11 +193,24 @@ char *strextend_with_separator_internal(char **x, const char *separator, ...) _s
#define strextend_with_separator(x, separator, ...) strextend_with_separator_internal(x, separator, __VA_ARGS__, NULL)
#define strextend(x, ...) strextend_with_separator_internal(x, NULL, __VA_ARGS__, NULL)
+char *strextendn(char **x, const char *s, size_t l);
+
int strextendf_with_separator(char **x, const char *separator, const char *format, ...) _printf_(3,4);
#define strextendf(x, ...) strextendf_with_separator(x, NULL, __VA_ARGS__)
char *strrep(const char *s, unsigned n);
+#define strrepa(s, n) \
+ ({ \
+ char *_d_, *_p_; \
+ size_t _len_ = strlen(s) * n; \
+ _p_ = _d_ = newa(char, _len_ + 1); \
+ for (unsigned _i_ = 0; _i_ < n; _i_++) \
+ _p_ = stpcpy(_p_, s); \
+ *_p_ = 0; \
+ _d_; \
+ })
+
int split_pair(const char *s, const char *sep, char **l, char **r);
int free_and_strdup(char **p, const char *s);
@@ -268,7 +291,31 @@ char *strdupcspn(const char *a, const char *reject);
char *find_line_startswith(const char *haystack, const char *needle);
-char *startswith_strv(const char *string, char **strv);
+bool version_is_valid(const char *s);
+
+bool version_is_valid_versionspec(const char *s);
+
+ssize_t strlevenshtein(const char *x, const char *y);
+
+static inline int strdup_or_null(const char *s, char **ret) {
+ char *c;
+
+ assert(ret);
+
+ /* This is a lot like strdup(), but is happy with NULL strings, and does not treat that as error, but
+ * copies the NULL value. */
+
+ if (!s) {
+ *ret = NULL;
+ return 0;
+ }
+
+ c = strdup(s);
+ if (!c)
+ return -ENOMEM;
+
+ *ret = c;
+ return 1;
+}
-#define STARTSWITH_SET(p, ...) \
- startswith_strv(p, STRV_MAKE(__VA_ARGS__))
+char *strrstr(const char *haystack, const char *needle);
diff --git a/src/libnm-systemd-shared/src/basic/strv.c b/src/libnm-systemd-shared/src/basic/strv.c
index 9ad5330739..7c6de915b2 100644
--- a/src/libnm-systemd-shared/src/basic/strv.c
+++ b/src/libnm-systemd-shared/src/basic/strv.c
@@ -90,6 +90,15 @@ char** strv_free_erase(char **l) {
return mfree(l);
}
+void strv_free_many(char ***strvs, size_t n) {
+ assert(strvs || n == 0);
+
+ FOREACH_ARRAY (i, strvs, n)
+ strv_free(*i);
+
+ free(strvs);
+}
+
char** strv_copy_n(char * const *l, size_t m) {
_cleanup_strv_free_ char **result = NULL;
char **k;
@@ -116,6 +125,22 @@ char** strv_copy_n(char * const *l, size_t m) {
return TAKE_PTR(result);
}
+int strv_copy_unless_empty(char * const *l, char ***ret) {
+ assert(ret);
+
+ if (strv_isempty(l)) {
+ *ret = NULL;
+ return 0;
+ }
+
+ char **copy = strv_copy(l);
+ if (!copy)
+ return -ENOMEM;
+
+ *ret = TAKE_PTR(copy);
+ return 1;
+}
+
size_t strv_length(char * const *l) {
size_t n = 0;
@@ -214,9 +239,7 @@ int strv_extend_strv(char ***a, char * const *b, bool filter_duplicates) {
return (int) i;
rollback:
- for (size_t j = 0; j < i; j++)
- free(t[p + j]);
-
+ free_many_charp(t + p, i);
t[p] = NULL;
return -ENOMEM;
}
@@ -488,29 +511,31 @@ int strv_insert(char ***l, size_t position, char *value) {
char **c;
size_t n, m;
+ assert(l);
+
if (!value)
return 0;
n = strv_length(*l);
position = MIN(position, n);
- /* increase and check for overflow */
- m = n + 2;
- if (m < n)
+ /* check for overflow and increase*/
+ if (n > SIZE_MAX - 2)
return -ENOMEM;
+ m = n + 2;
- c = new(char*, m);
+ c = reallocarray(*l, GREEDY_ALLOC_ROUND_UP(m), sizeof(char*));
if (!c)
return -ENOMEM;
- for (size_t i = 0; i < position; i++)
- c[i] = (*l)[i];
+ if (n > position)
+ memmove(c + position + 1, c + position, (n - position) * sizeof(char*));
+
c[position] = value;
- for (size_t i = position; i < n; i++)
- c[i+1] = (*l)[i];
- c[n+1] = NULL;
+ c[n + 1] = NULL;
- return free_and_replace(*l, c);
+ *l = c;
+ return 0;
}
int strv_consume_with_size(char ***l, size_t *n, char *value) {
@@ -571,39 +596,63 @@ int strv_extend_with_size(char ***l, size_t *n, const char *value) {
return strv_consume_with_size(l, n, v);
}
-int strv_extend_front(char ***l, const char *value) {
+int strv_extend_many_internal(char ***l, const char *value, ...) {
+ va_list ap;
size_t n, m;
- char *v, **c;
+ int r;
assert(l);
- /* Like strv_extend(), but prepends rather than appends the new entry */
+ m = n = strv_length(*l);
- if (!value)
- return 0;
+ r = 0;
+ va_start(ap, value);
+ for (const char *s = value; s != POINTER_MAX; s = va_arg(ap, const char*)) {
+ if (!s)
+ continue;
- n = strv_length(*l);
+ if (m > SIZE_MAX-1) { /* overflow */
+ r = -ENOMEM;
+ break;
+ }
+ m++;
+ }
+ va_end(ap);
- /* Increase and overflow check. */
- m = n + 2;
- if (m < n)
+ if (r < 0)
+ return r;
+ if (m > SIZE_MAX-1)
return -ENOMEM;
- v = strdup(value);
- if (!v)
+ char **c = reallocarray(*l, GREEDY_ALLOC_ROUND_UP(m+1), sizeof(char*));
+ if (!c)
return -ENOMEM;
+ *l = c;
- c = reallocarray(*l, m, sizeof(char*));
- if (!c) {
- free(v);
- return -ENOMEM;
+ r = 0;
+ size_t i = n;
+ va_start(ap, value);
+ for (const char *s = value; s != POINTER_MAX; s = va_arg(ap, const char*)) {
+ if (!s)
+ continue;
+
+ c[i] = strdup(s);
+ if (!c[i]) {
+ r = -ENOMEM;
+ break;
+ }
+ i++;
}
+ va_end(ap);
- memmove(c+1, c, n * sizeof(char*));
- c[0] = v;
- c[n+1] = NULL;
+ if (r < 0) {
+ /* rollback on error */
+ for (size_t j = n; j < i; j++)
+ c[j] = mfree(c[j]);
+ return r;
+ }
- *l = c;
+ c[i] = NULL;
return 0;
}
@@ -707,6 +756,26 @@ int strv_extendf(char ***l, const char *format, ...) {
return strv_consume(l, x);
}
+char* startswith_strv(const char *s, char * const *l) {
+ STRV_FOREACH(i, l) {
+ char *found = startswith(s, *i);
+ if (found)
+ return found;
+ }
+
+ return NULL;
+}
+
+char* endswith_strv(const char *s, char * const *l) {
+ STRV_FOREACH(i, l) {
+ char *found = endswith(s, *i);
+ if (found)
+ return found;
+ }
+
+ return NULL;
+}
+
char** strv_reverse(char **l) {
size_t n;
@@ -835,13 +904,15 @@ int fputstrv(FILE *f, char * const *l, const char *separator, bool *space) {
bool b = false;
int r;
+ assert(f);
+
/* Like fputs(), but for strv, and with a less stupid argument order */
if (!space)
space = &b;
STRV_FOREACH(s, l) {
- r = fputs_with_space(f, *s, separator, space);
+ r = fputs_with_separator(f, *s, separator, space);
if (r < 0)
return r;
}
diff --git a/src/libnm-systemd-shared/src/basic/strv.h b/src/libnm-systemd-shared/src/basic/strv.h
index 544d46a3f8..91337b9287 100644
--- a/src/libnm-systemd-shared/src/basic/strv.h
+++ b/src/libnm-systemd-shared/src/basic/strv.h
@@ -32,10 +32,14 @@ char** strv_free_erase(char **l);
DEFINE_TRIVIAL_CLEANUP_FUNC(char**, strv_free_erase);
#define _cleanup_strv_free_erase_ _cleanup_(strv_free_erasep)
+void strv_free_many(char ***strvs, size_t n);
+
char** strv_copy_n(char * const *l, size_t n);
static inline char** strv_copy(char * const *l) {
return strv_copy_n(l, SIZE_MAX);
}
+int strv_copy_unless_empty(char * const *l, char ***ret);
+
size_t strv_length(char * const *l) _pure_;
int strv_extend_strv(char ***a, char * const *b, bool filter_duplicates);
@@ -51,8 +55,10 @@ static inline int strv_extend(char ***l, const char *value) {
return strv_extend_with_size(l, NULL, value);
}
+int strv_extend_many_internal(char ***l, const char *value, ...);
+#define strv_extend_many(l, ...) strv_extend_many_internal(l, __VA_ARGS__, POINTER_MAX)
+
int strv_extendf(char ***l, const char *format, ...) _printf_(2,3);
-int strv_extend_front(char ***l, const char *value);
int strv_push_with_size(char ***l, size_t *n, char *value);
static inline int strv_push(char ***l, char *value) {
@@ -157,6 +163,16 @@ static inline void strv_print(char * const *l) {
strv_print_full(l, NULL);
}
+char* startswith_strv(const char *s, char * const *l);
+
+#define STARTSWITH_SET(p, ...) \
+ startswith_strv(p, STRV_MAKE(__VA_ARGS__))
+
+char* endswith_strv(const char *s, char * const *l);
+
+#define ENDSWITH_SET(p, ...) \
+ endswith_strv(p, STRV_MAKE(__VA_ARGS__))
+
#define strv_from_stdarg_alloca(first) \
({ \
char **_l; \
@@ -200,18 +216,6 @@ static inline void strv_print(char * const *l) {
_x && strv_contains_case(STRV_MAKE(__VA_ARGS__), _x); \
})
-#define ENDSWITH_SET(p, ...) \
- ({ \
- const char *_p = (p); \
- char *_found = NULL; \
- STRV_FOREACH(_i, STRV_MAKE(__VA_ARGS__)) { \
- _found = endswith(_p, *_i); \
- if (_found) \
- break; \
- } \
- _found; \
- })
-
#define _FOREACH_STRING(uniq, x, y, ...) \
for (const char *x, * const*UNIQ_T(l, uniq) = STRV_MAKE_CONST(({ x = y; }), ##__VA_ARGS__); \
x; \
diff --git a/src/libnm-systemd-shared/src/basic/time-util.c b/src/libnm-systemd-shared/src/basic/time-util.c
index 092912b2b0..2147156e55 100644
--- a/src/libnm-systemd-shared/src/basic/time-util.c
+++ b/src/libnm-systemd-shared/src/basic/time-util.c
@@ -66,7 +66,7 @@ nsec_t now_nsec(clockid_t clock_id) {
return timespec_load_nsec(&ts);
}
-dual_timestamp* dual_timestamp_get(dual_timestamp *ts) {
+dual_timestamp* dual_timestamp_now(dual_timestamp *ts) {
assert(ts);
ts->realtime = now(CLOCK_REALTIME);
@@ -75,7 +75,7 @@ dual_timestamp* dual_timestamp_get(dual_timestamp *ts) {
return ts;
}
-triple_timestamp* triple_timestamp_get(triple_timestamp *ts) {
+triple_timestamp* triple_timestamp_now(triple_timestamp *ts) {
assert(ts);
ts->realtime = now(CLOCK_REALTIME);
@@ -158,6 +158,25 @@ triple_timestamp* triple_timestamp_from_realtime(triple_timestamp *ts, usec_t u)
return ts;
}
+triple_timestamp* triple_timestamp_from_boottime(triple_timestamp *ts, usec_t u) {
+ usec_t nowb;
+
+ assert(ts);
+
+ if (u == USEC_INFINITY) {
+ ts->realtime = ts->monotonic = ts->boottime = u;
+ return ts;
+ }
+
+ nowb = now(CLOCK_BOOTTIME);
+
+ ts->boottime = u;
+ ts->monotonic = map_clock_usec_internal(u, nowb, now(CLOCK_MONOTONIC));
+ ts->realtime = map_clock_usec_internal(u, nowb, now(CLOCK_REALTIME));
+
+ return ts;
+}
+
dual_timestamp* dual_timestamp_from_monotonic(dual_timestamp *ts, usec_t u) {
assert(ts);
@@ -330,7 +349,7 @@ char *format_timestamp_style(
if (l < (size_t) (1 + 1 + 1))
return NULL; /* not enough space for even the shortest of forms */
- return snprintf_ok(buf, l, "@" USEC_FMT, t / USEC_PER_SEC); /* round down µs → s */
+ return snprintf_ok(buf, l, "@" USEC_FMT, t / USEC_PER_SEC); /* round down μs → s */
}
utc = IN_SET(style, TIMESTAMP_UTC, TIMESTAMP_US_UTC, TIMESTAMP_DATE);
@@ -618,7 +637,7 @@ char* format_timespan(char *buf, size_t l, usec_t t, usec_t accuracy) {
#if 0 /* NM_IGNORED */
static int parse_timestamp_impl(
const char *t,
- size_t tz_offset,
+ size_t max_len,
bool utc,
int isdst,
long gmtoff,
@@ -655,8 +674,12 @@ static int parse_timestamp_impl(
/* Allowed syntaxes:
*
- * 2012-09-22 16:34:22
+ * 2012-09-22 16:34:22.1[2[3[4[5[6]]]]]
+ * 2012-09-22 16:34:22 (µsec will be set to 0)
* 2012-09-22 16:34 (seconds will be set to 0)
+ * 2012-09-22T16:34:22.1[2[3[4[5[6]]]]]
+ * 2012-09-22T16:34:22 (µsec will be set to 0)
+ * 2012-09-22T16:34 (seconds will be set to 0)
* 2012-09-22 (time will be set to 00:00:00)
* 16:34:22 (date will be set to today)
* 16:34 (date will be set to today, seconds to 0)
@@ -670,17 +693,26 @@ static int parse_timestamp_impl(
*
* Note, on DST change, 00:00:00 may not exist and in that case the time part may be shifted.
* E.g. "Sun 2023-03-13 America/Havana" is parsed as "Sun 2023-03-13 01:00:00 CDT".
+ *
+ * A simplified strptime-spelled RFC3339 ABNF looks like
+ * "%Y-%m-%d" "T" "%H" ":" "%M" ":" "%S" [".%N"] ("Z" / (("+" / "-") "%H:%M"))
+ * We additionally allow no seconds and inherited timezone
+ * for symmetry with our other syntaxes and improved interactive usability:
+ * "%Y-%m-%d" "T" "%H" ":" "%M" ":" ["%S" [".%N"]] ["Z" / (("+" / "-") "%H:%M")]
+ * RFC3339 defines time-secfrac to as "." 1*DIGIT, but we limit to 6 digits,
+ * since we're limited to 1µs resolution.
+ * We also accept "Sat 2012-09-22T16:34:22", RFC3339 warns against it.
*/
assert(t);
- if (tz_offset != SIZE_MAX) {
+ if (max_len != SIZE_MAX) {
/* If the input string contains timezone, then cut it here. */
- if (tz_offset <= 1) /* timezone must be after a space. */
+ if (max_len == 0) /* Can't be the only field */
return -EINVAL;
- t_alloc = strndup(t, tz_offset - 1);
+ t_alloc = strndup(t, max_len);
if (!t_alloc)
return -ENOMEM;
@@ -792,6 +824,7 @@ static int parse_timestamp_impl(
goto from_tm;
}
+ /* Our "canonical" RFC3339 syntax variant */
tm = copy;
k = strptime(t, "%Y-%m-%d %H:%M:%S", &tm);
if (k) {
@@ -801,6 +834,16 @@ static int parse_timestamp_impl(
goto from_tm;
}
+ /* RFC3339 syntax */
+ tm = copy;
+ k = strptime(t, "%Y-%m-%dT%H:%M:%S", &tm);
+ if (k) {
+ if (*k == '.')
+ goto parse_usec;
+ else if (*k == 0)
+ goto from_tm;
+ }
+
/* Support OUTPUT_SHORT and OUTPUT_SHORT_PRECISE formats */
tm = copy;
k = strptime(t, "%b %d %H:%M:%S", &tm);
@@ -818,6 +861,7 @@ static int parse_timestamp_impl(
goto from_tm;
}
+ /* Our "canonical" RFC3339 syntax variant without seconds */
tm = copy;
k = strptime(t, "%Y-%m-%d %H:%M", &tm);
if (k && *k == 0) {
@@ -825,6 +869,14 @@ static int parse_timestamp_impl(
goto from_tm;
}
+ /* RFC3339 syntax without seconds */
+ tm = copy;
+ k = strptime(t, "%Y-%m-%dT%H:%M", &tm);
+ if (k && *k == 0) {
+ tm.tm_sec = 0;
+ goto from_tm;
+ }
+
tm = copy;
k = strptime(t, "%y-%m-%d", &tm);
if (k && *k == 0) {
@@ -927,13 +979,13 @@ static int parse_timestamp_maybe_with_tz(const char *t, size_t tz_offset, bool v
continue;
/* The specified timezone matches tzname[] of the local timezone. */
- return parse_timestamp_impl(t, tz_offset, /* utc = */ false, /* isdst = */ j, /* gmtoff = */ 0, ret);
+ return parse_timestamp_impl(t, tz_offset - 1, /* utc = */ false, /* isdst = */ j, /* gmtoff = */ 0, ret);
}
/* If we know that the last word is a valid timezone (e.g. Asia/Tokyo), then simply drop the timezone
* and parse the remaining string as a local time. If we know that the last word is not a timezone,
* then assume that it is a part of the time and try to parse the whole string as a local time. */
- return parse_timestamp_impl(t, valid_tz ? tz_offset : SIZE_MAX,
+ return parse_timestamp_impl(t, valid_tz ? tz_offset - 1 : SIZE_MAX,
/* utc = */ false, /* isdst = */ -1, /* gmtoff = */ 0, ret);
}
@@ -945,40 +997,50 @@ typedef struct ParseTimestampResult {
int parse_timestamp(const char *t, usec_t *ret) {
ParseTimestampResult *shared, tmp;
const char *k, *tz, *current_tz;
- size_t tz_offset;
+ size_t max_len, t_len;
struct tm tm;
int r;
assert(t);
+ t_len = strlen(t);
+ if (t_len > 2 && t[t_len - 1] == 'Z' && t[t_len - 2] != ' ') /* RFC3339-style welded UTC: "1985-04-12T23:20:50.52Z" */
+ return parse_timestamp_impl(t, t_len - 1, /* utc = */ true, /* isdst = */ -1, /* gmtoff = */ 0, ret);
+
+ if (t_len > 7 && IN_SET(t[t_len - 6], '+', '-') && t[t_len - 7] != ' ') { /* RFC3339-style welded offset: "1990-12-31T15:59:60-08:00" */
+ k = strptime(&t[t_len - 6], "%z", &tm);
+ if (k && *k == '\0')
+ return parse_timestamp_impl(t, t_len - 6, /* utc = */ true, /* isdst = */ -1, /* gmtoff = */ tm.tm_gmtoff, ret);
+ }
+
tz = strrchr(t, ' ');
if (!tz)
- return parse_timestamp_impl(t, /* tz_offset = */ SIZE_MAX, /* utc = */ false, /* isdst = */ -1, /* gmtoff = */ 0, ret);
+ return parse_timestamp_impl(t, /* max_len = */ SIZE_MAX, /* utc = */ false, /* isdst = */ -1, /* gmtoff = */ 0, ret);
+ max_len = tz - t;
tz++;
- tz_offset = tz - t;
/* Shortcut, parse the string as UTC. */
if (streq(tz, "UTC"))
- return parse_timestamp_impl(t, tz_offset, /* utc = */ true, /* isdst = */ -1, /* gmtoff = */ 0, ret);
+ return parse_timestamp_impl(t, max_len, /* utc = */ true, /* isdst = */ -1, /* gmtoff = */ 0, ret);
/* If the timezone is compatible with RFC-822/ISO 8601 (e.g. +06, or -03:00) then parse the string as
* UTC and shift the result. Note, this must be earlier than the timezone check with tzname[], as
* tzname[] may be in the same format. */
k = strptime(tz, "%z", &tm);
if (k && *k == '\0')
- return parse_timestamp_impl(t, tz_offset, /* utc = */ true, /* isdst = */ -1, /* gmtoff = */ tm.tm_gmtoff, ret);
+ return parse_timestamp_impl(t, max_len, /* utc = */ true, /* isdst = */ -1, /* gmtoff = */ tm.tm_gmtoff, ret);
/* If the last word is not a timezone file (e.g. Asia/Tokyo), then let's check if it matches
* tzname[] of the local timezone, e.g. JST or CEST. */
if (!timezone_is_valid(tz, LOG_DEBUG))
- return parse_timestamp_maybe_with_tz(t, tz_offset, /* valid_tz = */ false, ret);
+ return parse_timestamp_maybe_with_tz(t, tz - t, /* valid_tz = */ false, ret);
/* Shortcut. If the current $TZ is equivalent to the specified timezone, it is not necessary to fork
* the process. */
current_tz = getenv("TZ");
if (current_tz && *current_tz == ':' && streq(current_tz + 1, tz))
- return parse_timestamp_maybe_with_tz(t, tz_offset, /* valid_tz = */ true, ret);
+ return parse_timestamp_maybe_with_tz(t, tz - t, /* valid_tz = */ true, ret);
/* Otherwise, to avoid polluting the current environment variables, let's fork the process and set
* the specified timezone in the child process. */
@@ -987,7 +1049,7 @@ int parse_timestamp(const char *t, usec_t *ret) {
if (shared == MAP_FAILED)
return negative_errno();
- r = safe_fork("(sd-timestamp)", FORK_RESET_SIGNALS|FORK_CLOSE_ALL_FDS|FORK_DEATHSIG|FORK_WAIT, NULL);
+ r = safe_fork("(sd-timestamp)", FORK_RESET_SIGNALS|FORK_CLOSE_ALL_FDS|FORK_DEATHSIG_SIGKILL|FORK_WAIT, NULL);
if (r < 0) {
(void) munmap(shared, sizeof *shared);
return r;
@@ -1003,7 +1065,7 @@ int parse_timestamp(const char *t, usec_t *ret) {
_exit(EXIT_FAILURE);
}
- shared->return_value = parse_timestamp_maybe_with_tz(t, tz_offset, /* valid_tz = */ true, &shared->usec);
+ shared->return_value = parse_timestamp_maybe_with_tz(t, tz - t, /* valid_tz = */ true, &shared->usec);
_exit(EXIT_SUCCESS);
}
@@ -1051,7 +1113,8 @@ static const char* extract_multiplier(const char *p, usec_t *ret) {
{ "y", USEC_PER_YEAR },
{ "usec", 1ULL },
{ "us", 1ULL },
- { "µs", 1ULL },
+ { "μs", 1ULL }, /* U+03bc (aka GREEK SMALL LETTER MU) */
+ { "µs", 1ULL }, /* U+b5 (aka MICRO SIGN) */
};
assert(p);
@@ -1229,7 +1292,8 @@ static const char* extract_nsec_multiplier(const char *p, nsec_t *ret) {
{ "y", NSEC_PER_YEAR },
{ "usec", NSEC_PER_USEC },
{ "us", NSEC_PER_USEC },
- { "µs", NSEC_PER_USEC },
+ { "μs", NSEC_PER_USEC }, /* U+03bc (aka GREEK LETTER MU) */
+ { "µs", NSEC_PER_USEC }, /* U+b5 (aka MICRO SIGN) */
{ "nsec", 1ULL },
{ "ns", 1ULL },
{ "", 1ULL }, /* default is nsec */
@@ -1458,7 +1522,7 @@ int get_timezones(char ***ret) {
/* Always include UTC */
r = strv_extend(&zones, "UTC");
if (r < 0)
- return -ENOMEM;
+ return r;
strv_sort(zones);
strv_uniq(zones);
@@ -1666,13 +1730,13 @@ int time_change_fd(void) {
if (timerfd_settime(fd, TFD_TIMER_ABSTIME|TFD_TIMER_CANCEL_ON_SET, &its, NULL) >= 0)
return TAKE_FD(fd);
- /* So apparently there are systems where time_t is 64bit, but the kernel actually doesn't support
- * 64bit time_t. In that case configuring a timer to TIME_T_MAX will fail with EOPNOTSUPP or a
+ /* So apparently there are systems where time_t is 64-bit, but the kernel actually doesn't support
+ * 64-bit time_t. In that case configuring a timer to TIME_T_MAX will fail with EOPNOTSUPP or a
* similar error. If that's the case let's try with INT32_MAX instead, maybe that works. It's a bit
* of a black magic thing though, but what can we do?
*
- * We don't want this code on x86-64, hence let's conditionalize this for systems with 64bit time_t
- * but where "long" is shorter than 64bit, i.e. 32bit archs.
+ * We don't want this code on x86-64, hence let's conditionalize this for systems with 64-bit time_t
+ * but where "long" is shorter than 64-bit, i.e. 32-bit archs.
*
* See: https://github.com/systemd/systemd/issues/14362 */
@@ -1708,9 +1772,9 @@ TimestampStyle timestamp_style_from_string(const char *s) {
t = (TimestampStyle) string_table_lookup(timestamp_style_table, ELEMENTSOF(timestamp_style_table), s);
if (t >= 0)
return t;
- if (streq_ptr(s, "µs"))
+ if (STRPTR_IN_SET(s, "µs", "μs")) /* accept both µ symbols in unicode, i.e. micro symbol + Greek small letter mu. */
return TIMESTAMP_US;
- if (streq_ptr(s, "µs+utc"))
+ if (STRPTR_IN_SET(s, "µs+utc", "μs+utc"))
return TIMESTAMP_US_UTC;
return t;
}
diff --git a/src/libnm-systemd-shared/src/basic/time-util.h b/src/libnm-systemd-shared/src/basic/time-util.h
index b49137d5c3..29373477f4 100644
--- a/src/libnm-systemd-shared/src/basic/time-util.h
+++ b/src/libnm-systemd-shared/src/basic/time-util.h
@@ -79,13 +79,14 @@ nsec_t now_nsec(clockid_t clock);
usec_t map_clock_usec(usec_t from, clockid_t from_clock, clockid_t to_clock);
-dual_timestamp* dual_timestamp_get(dual_timestamp *ts);
+dual_timestamp* dual_timestamp_now(dual_timestamp *ts);
dual_timestamp* dual_timestamp_from_realtime(dual_timestamp *ts, usec_t u);
dual_timestamp* dual_timestamp_from_monotonic(dual_timestamp *ts, usec_t u);
dual_timestamp* dual_timestamp_from_boottime(dual_timestamp *ts, usec_t u);
-triple_timestamp* triple_timestamp_get(triple_timestamp *ts);
+triple_timestamp* triple_timestamp_now(triple_timestamp *ts);
triple_timestamp* triple_timestamp_from_realtime(triple_timestamp *ts, usec_t u);
+triple_timestamp* triple_timestamp_from_boottime(triple_timestamp *ts, usec_t u);
#define DUAL_TIMESTAMP_HAS_CLOCK(clock) \
IN_SET(clock, CLOCK_REALTIME, CLOCK_REALTIME_ALARM, CLOCK_MONOTONIC)
@@ -211,10 +212,24 @@ static inline usec_t usec_sub_signed(usec_t timestamp, int64_t delta) {
return usec_sub_unsigned(timestamp, (usec_t) delta);
}
+static inline int usleep_safe(usec_t usec) {
+ /* usleep() takes useconds_t that is (typically?) uint32_t. Also, usleep() may only support the
+ * range [0, 1000000]. See usleep(3). Let's override usleep() with clock_nanosleep().
+ *
+ * ⚠️ Note we are not using plain nanosleep() here, since that operates on CLOCK_REALTIME, not
+ * CLOCK_MONOTONIC! */
+
+ if (usec == 0)
+ return 0;
+
+ // FIXME: use RET_NERRNO() macro here. Currently, this header cannot include errno-util.h.
+ return clock_nanosleep(CLOCK_MONOTONIC, 0, TIMESPEC_STORE(usec), NULL) < 0 ? -errno : 0;
+}
+
/* The last second we can format is 31. Dec 9999, 1s before midnight, because otherwise we'd enter 5 digit
* year territory. However, since we want to stay away from this in all timezones we take one day off. */
#define USEC_TIMESTAMP_FORMATTABLE_MAX_64BIT ((usec_t) 253402214399000000) /* Thu 9999-12-30 23:59:59 UTC */
-/* With a 32bit time_t we can't go beyond 2038...
+/* With a 32-bit time_t we can't go beyond 2038...
* We parse timestamp with RFC-822/ISO 8601 (e.g. +06, or -03:00) as UTC, hence the upper bound must be off
* by USEC_PER_DAY. See parse_timestamp() for more details. */
#define USEC_TIMESTAMP_FORMATTABLE_MAX_32BIT (((usec_t) INT32_MAX) * USEC_PER_SEC - USEC_PER_DAY)
diff --git a/src/libnm-systemd-shared/src/basic/tmpfile-util.h b/src/libnm-systemd-shared/src/basic/tmpfile-util.h
index 50904ecac1..8c917c0680 100644
--- a/src/libnm-systemd-shared/src/basic/tmpfile-util.h
+++ b/src/libnm-systemd-shared/src/basic/tmpfile-util.h
@@ -29,7 +29,6 @@ static inline int open_tmpfile_linkable(const char *target, int flags, char **re
}
int fopen_tmpfile_linkable(const char *target, int flags, char **ret_path, FILE **ret_file);
-
typedef enum LinkTmpfileFlags {
LINK_TMPFILE_REPLACE = 1 << 0,
LINK_TMPFILE_SYNC = 1 << 1,
diff --git a/src/libnm-systemd-shared/src/basic/umask-util.h b/src/libnm-systemd-shared/src/basic/umask-util.h
index 6f0e1cc2b2..00417fa304 100644
--- a/src/libnm-systemd-shared/src/basic/umask-util.h
+++ b/src/libnm-systemd-shared/src/basic/umask-util.h
@@ -8,12 +8,12 @@
#include "macro.h"
static inline void umaskp(mode_t *u) {
- umask(*u & 0777);
+ umask(*u);
}
#define _cleanup_umask_ _cleanup_(umaskp)
-/* We make use of the fact here that the umask() concept is using only the lower 9 bits of mode_t, although
+/* We make use of the fact here that the umask() syscall uses only the lower 9 bits of mode_t, although
* mode_t has space for the file type in the bits further up. We simply OR in the file type mask S_IFMT to
* distinguish the first and the second iteration of the WITH_UMASK() loop, so that we can run the first one,
* and exit on the second. */
diff --git a/src/libnm-systemd-shared/src/basic/user-util.h b/src/libnm-systemd-shared/src/basic/user-util.h
index 8b829a9ae2..9d07ef31d2 100644
--- a/src/libnm-systemd-shared/src/basic/user-util.h
+++ b/src/libnm-systemd-shared/src/basic/user-util.h
@@ -42,8 +42,8 @@ typedef enum UserCredsFlags {
USER_CREDS_CLEAN = 1 << 2, /* try to clean up shell and home fields with invalid data */
} UserCredsFlags;
-int get_user_creds(const char **username, uid_t *uid, gid_t *gid, const char **home, const char **shell, UserCredsFlags flags);
-int get_group_creds(const char **groupname, gid_t *gid, UserCredsFlags flags);
+int get_user_creds(const char **username, uid_t *ret_uid, gid_t *ret_gid, const char **ret_home, const char **ret_shell, UserCredsFlags flags);
+int get_group_creds(const char **groupname, gid_t *ret_gid, UserCredsFlags flags);
char* uid_to_name(uid_t uid);
char* gid_to_name(gid_t gid);
@@ -57,7 +57,10 @@ int getgroups_alloc(gid_t** gids);
int get_home_dir(char **ret);
int get_shell(char **ret);
-int reset_uid_gid(void);
+int fully_set_uid_gid(uid_t uid, gid_t gid, const gid_t supplementary_gids[], size_t n_supplementary_gids);
+static inline int reset_uid_gid(void) {
+ return fully_set_uid_gid(0, 0, NULL, 0);
+}
int take_etc_passwd_lock(const char *root);
@@ -69,13 +72,13 @@ int take_etc_passwd_lock(const char *root);
/* If REMOUNT_IDMAPPING_HOST_ROOT is set for remount_idmap() we'll include a mapping here that maps the host
* root user accessing the idmapped mount to the this user ID on the backing fs. This is the last valid UID in
- * the *signed* 32bit range. You might wonder why precisely use this specific UID for this purpose? Well, we
+ * the *signed* 32-bit range. You might wonder why precisely use this specific UID for this purpose? Well, we
* definitely cannot use the first 0…65536 UIDs for that, since in most cases that's precisely the file range
* we intend to map to some high UID range, and since UID mappings have to be bijective we thus cannot use
- * them at all. Furthermore the UID range beyond INT32_MAX (i.e. the range above the signed 32bit range) is
+ * them at all. Furthermore the UID range beyond INT32_MAX (i.e. the range above the signed 32-bit range) is
* icky, since many APIs cannot use it (example: setfsuid() returns the old UID as signed integer). Following
- * our usual logic of assigning a 16bit UID range to each container, so that the upper 16bit of a 32bit UID
- * value indicate kind of a "container ID" and the lower 16bit map directly to the intended user you can read
+ * our usual logic of assigning a 16-bit UID range to each container, so that the upper 16-bit of a 32-bit UID
+ * value indicate kind of a "container ID" and the lower 16-bit map directly to the intended user you can read
* this specific UID as the "nobody" user of the container with ID 0x7FFF, which is kinda nice. */
#define UID_MAPPED_ROOT ((uid_t) (INT32_MAX-1))
#define GID_MAPPED_ROOT ((gid_t) (INT32_MAX-1))
@@ -155,3 +158,9 @@ static inline bool hashed_password_is_locked_or_invalid(const char *password) {
* Also see https://github.com/systemd/systemd/pull/24680#pullrequestreview-1439464325.
*/
#define PASSWORD_UNPROVISIONED "!unprovisioned"
+
+int getpwuid_malloc(uid_t uid, struct passwd **ret);
+int getpwnam_malloc(const char *name, struct passwd **ret);
+
+int getgrnam_malloc(const char *name, struct group **ret);
+int getgrgid_malloc(gid_t gid, struct group **ret);
diff --git a/src/libnm-systemd-shared/src/basic/utf8.c b/src/libnm-systemd-shared/src/basic/utf8.c
index c8e39fe45e..cf24e82f09 100644
--- a/src/libnm-systemd-shared/src/basic/utf8.c
+++ b/src/libnm-systemd-shared/src/basic/utf8.c
@@ -92,7 +92,7 @@ int utf8_encoded_to_unichar(const char *str, char32_t *ret_unichar) {
switch (len) {
case 1:
*ret_unichar = (char32_t)str[0];
- return 0;
+ return 1;
case 2:
unichar = str[0] & 0x1f;
break;
@@ -121,15 +121,14 @@ int utf8_encoded_to_unichar(const char *str, char32_t *ret_unichar) {
}
*ret_unichar = unichar;
-
- return 0;
+ return len;
}
bool utf8_is_printable_newline(const char* str, size_t length, bool allow_newline) {
assert(str);
for (const char *p = str; length > 0;) {
- int encoded_len, r;
+ int encoded_len;
char32_t val;
encoded_len = utf8_encoded_valid_unichar(p, length);
@@ -137,8 +136,7 @@ bool utf8_is_printable_newline(const char* str, size_t length, bool allow_newlin
return false;
assert(encoded_len > 0 && (size_t) encoded_len <= length);
- r = utf8_encoded_to_unichar(p, &val);
- if (r < 0 ||
+ if (utf8_encoded_to_unichar(p, &val) < 0 ||
unichar_is_control(val) ||
(!allow_newline && val == '\n'))
return false;
@@ -398,11 +396,23 @@ char *utf16_to_utf8(const char16_t *s, size_t length /* bytes! */) {
const uint8_t *f;
char *r, *t;
+ if (length == 0)
+ return new0(char, 1);
+
assert(s);
+ if (length == SIZE_MAX) {
+ length = char16_strlen(s);
+
+ if (length > SIZE_MAX/2)
+ return NULL; /* overflow */
+
+ length *= 2;
+ }
+
/* Input length is in bytes, i.e. the shortest possible character takes 2 bytes. Each unicode character may
* take up to 4 bytes in UTF-8. Let's also account for a trailing NUL byte. */
- if (length * 2 < length)
+ if (length > (SIZE_MAX - 1) / 2)
return NULL; /* overflow */
r = new(char, length * 2 + 1);
@@ -472,8 +482,17 @@ char16_t *utf8_to_utf16(const char *s, size_t length) {
char16_t *n, *p;
int r;
+ if (length == 0)
+ return new0(char16_t, 1);
+
assert(s);
+ if (length == SIZE_MAX)
+ length = strlen(s);
+
+ if (length > SIZE_MAX - 1)
+ return NULL; /* overflow */
+
n = new(char16_t, length + 1);
if (!n)
return NULL;
diff --git a/src/libnm-systemd-shared/src/basic/utf8.h b/src/libnm-systemd-shared/src/basic/utf8.h
index 4a06dd62c5..962312c5fb 100644
--- a/src/libnm-systemd-shared/src/basic/utf8.h
+++ b/src/libnm-systemd-shared/src/basic/utf8.h
@@ -38,7 +38,7 @@ size_t utf16_encode_unichar(char16_t *out, char32_t c);
char *utf16_to_utf8(const char16_t *s, size_t length /* bytes! */);
char16_t *utf8_to_utf16(const char *s, size_t length);
-size_t char16_strlen(const char16_t *s); /* returns the number of 16bit words in the string (not bytes!) */
+size_t char16_strlen(const char16_t *s); /* returns the number of 16-bit words in the string (not bytes!) */
int utf8_encoded_valid_unichar(const char *str, size_t length);
int utf8_encoded_to_unichar(const char *str, char32_t *ret_unichar);
diff --git a/src/libnm-systemd-shared/src/fundamental/macro-fundamental.h b/src/libnm-systemd-shared/src/fundamental/macro-fundamental.h
index 89b83e7d0b..c810261308 100644
--- a/src/libnm-systemd-shared/src/fundamental/macro-fundamental.h
+++ b/src/libnm-systemd-shared/src/fundamental/macro-fundamental.h
@@ -11,6 +11,42 @@
#include <stddef.h>
#include <stdint.h>
+/* Temporarily disable some warnings */
+#define DISABLE_WARNING_DEPRECATED_DECLARATIONS \
+ _Pragma("GCC diagnostic push"); \
+ _Pragma("GCC diagnostic ignored \"-Wdeprecated-declarations\"")
+
+#define DISABLE_WARNING_FORMAT_NONLITERAL \
+ _Pragma("GCC diagnostic push"); \
+ _Pragma("GCC diagnostic ignored \"-Wformat-nonliteral\"")
+
+#define DISABLE_WARNING_MISSING_PROTOTYPES \
+ _Pragma("GCC diagnostic push"); \
+ _Pragma("GCC diagnostic ignored \"-Wmissing-prototypes\"")
+
+#define DISABLE_WARNING_NONNULL \
+ _Pragma("GCC diagnostic push"); \
+ _Pragma("GCC diagnostic ignored \"-Wnonnull\"")
+
+#define DISABLE_WARNING_SHADOW \
+ _Pragma("GCC diagnostic push"); \
+ _Pragma("GCC diagnostic ignored \"-Wshadow\"")
+
+#define DISABLE_WARNING_INCOMPATIBLE_POINTER_TYPES \
+ _Pragma("GCC diagnostic push"); \
+ _Pragma("GCC diagnostic ignored \"-Wincompatible-pointer-types\"")
+
+#define DISABLE_WARNING_TYPE_LIMITS \
+ _Pragma("GCC diagnostic push"); \
+ _Pragma("GCC diagnostic ignored \"-Wtype-limits\"")
+
+#define DISABLE_WARNING_ADDRESS \
+ _Pragma("GCC diagnostic push"); \
+ _Pragma("GCC diagnostic ignored \"-Waddress\"")
+
+#define REENABLE_WARNING \
+ _Pragma("GCC diagnostic pop")
+
#define _align_(x) __attribute__((__aligned__(x)))
#define _alignas_(x) __attribute__((__aligned__(alignof(x))))
#define _alignptr_ __attribute__((__aligned__(sizeof(void *))))
@@ -79,7 +115,7 @@
_noreturn_ void efi_assert(const char *expr, const char *file, unsigned line, const char *function);
#ifdef NDEBUG
- #define assert(expr)
+ #define assert(expr) ({ if (!(expr)) __builtin_unreachable(); })
#define assert_not_reached() __builtin_unreachable()
#else
#define assert(expr) ({ _likely_(expr) ? VOID_0 : efi_assert(#expr, __FILE__, __LINE__, __func__); })
@@ -129,6 +165,10 @@
__atomic_exchange_n(&(o), true, __ATOMIC_SEQ_CST); \
})
+#define U64_KB UINT64_C(1024)
+#define U64_MB (UINT64_C(1024) * U64_KB)
+#define U64_GB (UINT64_C(1024) * U64_MB)
+
#undef MAX
#define MAX(a, b) __MAX(UNIQ, (a), UNIQ, (b))
#define __MAX(aq, a, bq, b) \
@@ -355,7 +395,40 @@ static inline size_t ALIGN_TO(size_t l, size_t ali) {
if (l > SIZE_MAX - (ali - 1))
return SIZE_MAX; /* indicate overflow */
- return ((l + ali - 1) & ~(ali - 1));
+ return ((l + (ali - 1)) & ~(ali - 1));
+}
+
+static inline uint64_t ALIGN_TO_U64(uint64_t l, uint64_t ali) {
+ assert(ISPOWEROF2(ali));
+
+ if (l > UINT64_MAX - (ali - 1))
+ return UINT64_MAX; /* indicate overflow */
+
+ return ((l + (ali - 1)) & ~(ali - 1));
+}
+
+static inline size_t ALIGN_DOWN(size_t l, size_t ali) {
+ assert(ISPOWEROF2(ali));
+
+ return l & ~(ali - 1);
+}
+
+static inline uint64_t ALIGN_DOWN_U64(uint64_t l, uint64_t ali) {
+ assert(ISPOWEROF2(ali));
+
+ return l & ~(ali - 1);
+}
+
+static inline size_t ALIGN_OFFSET(size_t l, size_t ali) {
+ assert(ISPOWEROF2(ali));
+
+ return l & (ali - 1);
+}
+
+static inline uint64_t ALIGN_OFFSET_U64(uint64_t l, uint64_t ali) {
+ assert(ISPOWEROF2(ali));
+
+ return l & (ali - 1);
}
#define ALIGN2(l) ALIGN_TO(l, 2)
@@ -399,6 +472,42 @@ static inline size_t ALIGN_TO(size_t l, size_t ali) {
#define FLAGS_SET(v, flags) \
((~(v) & (flags)) == 0)
+/* A wrapper for 'func' to return void.
+ * Only useful when a void-returning function is required by some API. */
+#define DEFINE_TRIVIAL_DESTRUCTOR(name, type, func) \
+ static inline void name(type *p) { \
+ func(p); \
+ }
+
+/* When func() returns the void value (NULL, -1, …) of the appropriate type */
+#define DEFINE_TRIVIAL_CLEANUP_FUNC(type, func) \
+ static inline void func##p(type *p) { \
+ if (*p) \
+ *p = func(*p); \
+ }
+
+/* When func() doesn't return the appropriate type, set variable to empty afterwards.
+ * The func() may be provided by a dynamically loaded shared library, hence add an assertion. */
+#define DEFINE_TRIVIAL_CLEANUP_FUNC_FULL(type, func, empty) \
+ static inline void func##p(type *p) { \
+ if (*p != (empty)) { \
+ DISABLE_WARNING_ADDRESS; \
+ assert(func); \
+ REENABLE_WARNING; \
+ func(*p); \
+ *p = (empty); \
+ } \
+ }
+
+/* When func() doesn't return the appropriate type, and is also a macro, set variable to empty afterwards. */
+#define DEFINE_TRIVIAL_CLEANUP_FUNC_FULL_MACRO(type, func, empty) \
+ static inline void func##p(type *p) { \
+ if (*p != (empty)) { \
+ func(*p); \
+ *p = (empty); \
+ } \
+ }
+
/* Declare a flexible array usable in a union.
* This is essentially a work-around for a pointless constraint in C99
* and might go away in some future version of the standard.
@@ -410,3 +519,16 @@ static inline size_t ALIGN_TO(size_t l, size_t ali) {
dummy_t __empty__ ## name; \
type name[]; \
}
+
+/* Declares an ELF read-only string section that does not occupy memory at runtime. */
+#define DECLARE_NOALLOC_SECTION(name, text) \
+ asm(".pushsection " name ",\"S\"\n\t" \
+ ".ascii " STRINGIFY(text) "\n\t" \
+ ".zero 1\n\t" \
+ ".popsection\n")
+
+#ifdef SBAT_DISTRO
+ #define DECLARE_SBAT(text) DECLARE_NOALLOC_SECTION(".sbat", text)
+#else
+ #define DECLARE_SBAT(text)
+#endif
diff --git a/src/libnm-systemd-shared/src/fundamental/memory-util-fundamental.h b/src/libnm-systemd-shared/src/fundamental/memory-util-fundamental.h
index 78e2dbec59..6870f54f58 100644
--- a/src/libnm-systemd-shared/src/fundamental/memory-util-fundamental.h
+++ b/src/libnm-systemd-shared/src/fundamental/memory-util-fundamental.h
@@ -11,6 +11,12 @@
#include "macro-fundamental.h"
+#define memzero(x, l) \
+ ({ \
+ size_t _l_ = (l); \
+ _l_ > 0 ? memset((x), 0, _l_) : (x); \
+ })
+
#if !SD_BOOT && HAVE_EXPLICIT_BZERO
static inline void *explicit_bzero_safe(void *p, size_t l) {
if (p && l > 0)
@@ -64,3 +70,39 @@ static inline void erase_varp(struct VarEraser *e) {
.p = (ptr), \
.size = (sz), \
}
+
+typedef void (*free_array_func_t)(void *p, size_t n);
+
+/* An automatic _cleanup_-like logic for destroy arrays (i.e. pointers + size) when leaving scope */
+typedef struct ArrayCleanup {
+ void **parray;
+ size_t *pn;
+ free_array_func_t pfunc;
+} ArrayCleanup;
+
+static inline void array_cleanup(const ArrayCleanup *c) {
+ assert(c);
+
+ assert(!c->parray == !c->pn);
+
+ if (!c->parray)
+ return;
+
+ if (*c->parray) {
+ assert(c->pfunc);
+ c->pfunc(*c->parray, *c->pn);
+ *c->parray = NULL;
+ }
+
+ *c->pn = 0;
+}
+
+#define CLEANUP_ARRAY(array, n, func) \
+ _cleanup_(array_cleanup) _unused_ const ArrayCleanup CONCATENATE(_cleanup_array_, UNIQ) = { \
+ .parray = (void**) &(array), \
+ .pn = &(n), \
+ .pfunc = (free_array_func_t) ({ \
+ void (*_f)(typeof(array[0]) *a, size_t b) = func; \
+ _f; \
+ }), \
+ }
diff --git a/src/libnm-systemd-shared/src/fundamental/sha256.c b/src/libnm-systemd-shared/src/fundamental/sha256.c
index a4c6d627b7..84113aed6b 100644
--- a/src/libnm-systemd-shared/src/fundamental/sha256.c
+++ b/src/libnm-systemd-shared/src/fundamental/sha256.c
@@ -36,16 +36,9 @@
#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
# define SWAP(n) \
- (((n) << 24) | (((n) & 0xff00) << 8) | (((n) >> 8) & 0xff00) | ((n) >> 24))
+ __builtin_bswap32(n)
# define SWAP64(n) \
- (((n) << 56) \
- | (((n) & 0xff00) << 40) \
- | (((n) & 0xff0000) << 24) \
- | (((n) & 0xff000000) << 8) \
- | (((n) >> 8) & 0xff000000) \
- | (((n) >> 24) & 0xff0000) \
- | (((n) >> 40) & 0xff00) \
- | ((n) >> 56))
+ __builtin_bswap64(n)
#else
# define SWAP(n) (n)
# define SWAP64(n) (n)
diff --git a/src/libnm-systemd-shared/src/fundamental/string-util-fundamental.c b/src/libnm-systemd-shared/src/fundamental/string-util-fundamental.c
index 3a3e7f593a..da810cb749 100644
--- a/src/libnm-systemd-shared/src/fundamental/string-util-fundamental.c
+++ b/src/libnm-systemd-shared/src/fundamental/string-util-fundamental.c
@@ -35,14 +35,14 @@ sd_char *startswith_no_case(const sd_char *s, const sd_char *prefix) {
return (sd_char*) s + l;
}
-sd_char* endswith(const sd_char *s, const sd_char *postfix) {
+sd_char* endswith(const sd_char *s, const sd_char *suffix) {
size_t sl, pl;
assert(s);
- assert(postfix);
+ assert(suffix);
sl = strlen(s);
- pl = strlen(postfix);
+ pl = strlen(suffix);
if (pl == 0)
return (sd_char*) s + sl;
@@ -50,20 +50,20 @@ sd_char* endswith(const sd_char *s, const sd_char *postfix) {
if (sl < pl)
return NULL;
- if (strcmp(s + sl - pl, postfix) != 0)
+ if (!streq(s + sl - pl, suffix))
return NULL;
return (sd_char*) s + sl - pl;
}
-sd_char* endswith_no_case(const sd_char *s, const sd_char *postfix) {
+sd_char* endswith_no_case(const sd_char *s, const sd_char *suffix) {
size_t sl, pl;
assert(s);
- assert(postfix);
+ assert(suffix);
sl = strlen(s);
- pl = strlen(postfix);
+ pl = strlen(suffix);
if (pl == 0)
return (sd_char*) s + sl;
@@ -71,7 +71,7 @@ sd_char* endswith_no_case(const sd_char *s, const sd_char *postfix) {
if (sl < pl)
return NULL;
- if (strcasecmp(s + sl - pl, postfix) != 0)
+ if (!strcaseeq(s + sl - pl, suffix))
return NULL;
return (sd_char*) s + sl - pl;
diff --git a/src/libnm-systemd-shared/src/fundamental/string-util-fundamental.h b/src/libnm-systemd-shared/src/fundamental/string-util-fundamental.h
index 9019542b16..419f1cc3da 100644
--- a/src/libnm-systemd-shared/src/fundamental/string-util-fundamental.h
+++ b/src/libnm-systemd-shared/src/fundamental/string-util-fundamental.h
@@ -59,8 +59,8 @@ static inline size_t strlen_ptr(const sd_char *s) {
sd_char *startswith(const sd_char *s, const sd_char *prefix) _pure_;
sd_char *startswith_no_case(const sd_char *s, const sd_char *prefix) _pure_;
-sd_char *endswith(const sd_char *s, const sd_char *postfix) _pure_;
-sd_char *endswith_no_case(const sd_char *s, const sd_char *postfix) _pure_;
+sd_char *endswith(const sd_char *s, const sd_char *suffix) _pure_;
+sd_char *endswith_no_case(const sd_char *s, const sd_char *suffix) _pure_;
static inline bool isempty(const sd_char *a) {
return !a || a[0] == '\0';
@@ -74,6 +74,10 @@ static inline const sd_char *yes_no(bool b) {
return b ? STR_C("yes") : STR_C("no");
}
+static inline const sd_char *on_off(bool b) {
+ return b ? STR_C("on") : STR_C("off");
+}
+
static inline const sd_char* comparison_operator(int result) {
return result < 0 ? STR_C("<") : result > 0 ? STR_C(">") : STR_C("==");
}
diff --git a/src/libnm-systemd-shared/src/shared/dns-domain.c b/src/libnm-systemd-shared/src/shared/dns-domain.c
index 43f43197a9..a07eaa33c8 100644
--- a/src/libnm-systemd-shared/src/shared/dns-domain.c
+++ b/src/libnm-systemd-shared/src/shared/dns-domain.c
@@ -87,12 +87,9 @@ int dns_label_unescape(const char **name, char *dest, size_t sz, DNSLabelFlags f
((unsigned) (n[1] - '0') * 10) +
((unsigned) (n[2] - '0'));
- /* Don't allow anything that doesn't
- * fit in 8bit. Note that we do allow
- * control characters, as some servers
- * (e.g. cloudflare) are happy to
- * generate labels with them
- * inside. */
+ /* Don't allow anything that doesn't fit in 8 bits. Note that we do allow
+ * control characters, as some servers (e.g. cloudflare) are happy to
+ * generate labels with them inside. */
if (k > 255)
return -EINVAL;
@@ -213,7 +210,7 @@ int dns_label_escape(const char *p, size_t l, char *dest, size_t sz) {
char *q;
/* DNS labels must be between 1 and 63 characters long. A
- * zero-length label does not exist. See RFC 2182, Section
+ * zero-length label does not exist. See RFC 2181, Section
* 11. */
if (l <= 0 || l > DNS_LABEL_MAX)
@@ -302,14 +299,14 @@ int dns_label_escape_new(const char *p, size_t l, char **ret) {
int dns_label_apply_idna(const char *encoded, size_t encoded_size, char *decoded, size_t decoded_max) {
_cleanup_free_ uint32_t *input = NULL;
size_t input_size, l;
- bool contains_8bit = false;
+ bool contains_8_bit = false;
char buffer[DNS_LABEL_MAX+1];
int r;
assert(encoded);
assert(decoded);
- /* Converts an U-label into an A-label */
+ /* Converts a U-label into an A-label */
r = dlopen_idn();
if (r < 0)
@@ -320,9 +317,9 @@ int dns_label_apply_idna(const char *encoded, size_t encoded_size, char *decoded
for (const char *p = encoded; p < encoded + encoded_size; p++)
if ((uint8_t) *p > 127)
- contains_8bit = true;
+ contains_8_bit = true;
- if (!contains_8bit) {
+ if (!contains_8_bit) {
if (encoded_size > DNS_LABEL_MAX)
return -EINVAL;
@@ -361,7 +358,7 @@ int dns_label_undo_idna(const char *encoded, size_t encoded_size, char *decoded,
size_t w;
int r;
- /* To be invoked after unescaping. Converts an A-label into an U-label. */
+ /* To be invoked after unescaping. Converts an A-label into a U-label. */
assert(encoded);
assert(decoded);
@@ -419,7 +416,7 @@ int dns_name_concat(const char *a, const char *b, DNSLabelFlags flags, char **_r
goto finish;
for (;;) {
- char label[DNS_LABEL_MAX];
+ char label[DNS_LABEL_MAX+1];
r = dns_label_unescape(&p, label, sizeof label, flags);
if (r < 0)
@@ -517,7 +514,7 @@ int dns_name_compare_func(const char *a, const char *b) {
y = b + strlen(b);
for (;;) {
- char la[DNS_LABEL_MAX], lb[DNS_LABEL_MAX];
+ char la[DNS_LABEL_MAX+1], lb[DNS_LABEL_MAX+1];
if (x == NULL && y == NULL)
return 0;
@@ -553,7 +550,7 @@ int dns_name_equal(const char *x, const char *y) {
assert(y);
for (;;) {
- char la[DNS_LABEL_MAX], lb[DNS_LABEL_MAX];
+ char la[DNS_LABEL_MAX+1], lb[DNS_LABEL_MAX+1];
r = dns_label_unescape(&x, la, sizeof la, 0);
if (r < 0)
@@ -584,7 +581,7 @@ int dns_name_endswith(const char *name, const char *suffix) {
s = suffix;
for (;;) {
- char ln[DNS_LABEL_MAX], ls[DNS_LABEL_MAX];
+ char ln[DNS_LABEL_MAX+1], ls[DNS_LABEL_MAX+1];
r = dns_label_unescape(&n, ln, sizeof ln, 0);
if (r < 0)
@@ -622,7 +619,7 @@ int dns_name_startswith(const char *name, const char *prefix) {
p = prefix;
for (;;) {
- char ln[DNS_LABEL_MAX], lp[DNS_LABEL_MAX];
+ char ln[DNS_LABEL_MAX+1], lp[DNS_LABEL_MAX+1];
r = dns_label_unescape(&p, lp, sizeof lp, 0);
if (r < 0)
@@ -654,7 +651,7 @@ int dns_name_change_suffix(const char *name, const char *old_suffix, const char
s = old_suffix;
for (;;) {
- char ln[DNS_LABEL_MAX], ls[DNS_LABEL_MAX];
+ char ln[DNS_LABEL_MAX+1], ls[DNS_LABEL_MAX+1];
if (!saved_before)
saved_before = n;
@@ -941,7 +938,7 @@ bool dns_srv_type_is_valid(const char *name) {
return false;
for (;;) {
- char label[DNS_LABEL_MAX];
+ char label[DNS_LABEL_MAX+1];
/* This more or less implements RFC 6335, Section 5.1 */
@@ -1239,7 +1236,7 @@ int dns_name_common_suffix(const char *a, const char *b, const char **ret) {
return m;
for (;;) {
- char la[DNS_LABEL_MAX], lb[DNS_LABEL_MAX];
+ char la[DNS_LABEL_MAX+1], lb[DNS_LABEL_MAX+1];
const char *x, *y;
if (k >= n || k >= m) {
@@ -1340,7 +1337,7 @@ int dns_name_apply_idna(const char *name, char **ret) {
assert(ret);
for (;;) {
- char label[DNS_LABEL_MAX];
+ char label[DNS_LABEL_MAX+1];
r = dns_label_unescape(&name, label, sizeof label, 0);
if (r < 0)
@@ -1425,6 +1422,10 @@ bool dns_name_dont_resolve(const char *name) {
if (dns_name_endswith(name, "invalid") > 0)
return true;
+ /* Never respond to some of the domains listed in RFC9476 */
+ if (dns_name_endswith(name, "alt") > 0)
+ return true;
+
return false;
}
#endif /* NM_IGNORED */