diff --git a/NEWS b/NEWS
index 5b0a981391..d90768e12b 100644
--- a/NEWS
+++ b/NEWS
@@ -1,3 +1,15 @@
+=============================================
+NetworkManager-1.56
+Overview of changes since NetworkManager-1.54
+=============================================
+
+This is a snapshot of NetworkManager development. The API is
+subject to change and not guaranteed to be compatible with
+the later release.
+USE AT YOUR OWN RISK. NOT RECOMMENDED FOR PRODUCTION USE!
+
+* nmcli now supports viewing and managing WireGuard peers.
+
=============================================
NetworkManager-1.54
Overview of changes since NetworkManager-1.52
diff --git a/contrib/fedora/rpm/NetworkManager.spec b/contrib/fedora/rpm/NetworkManager.spec
index 788e9ead57..8325152279 100644
--- a/contrib/fedora/rpm/NetworkManager.spec
+++ b/contrib/fedora/rpm/NetworkManager.spec
@@ -14,6 +14,7 @@
%global epoch_version 1
%global real_version __VERSION__
+%global git_tag_version __GIT_TAG_VERSION__
%global rpm_version %{real_version}
%global release_version __RELEASE_VERSION__
%global snapshot __SNAPSHOT__
@@ -180,7 +181,7 @@ Group: System Environment/Base
License: GPL-2.0-or-later AND LGPL-2.1-or-later
URL: https://networkmanager.dev/
-#Source: https://download.gnome.org/sources/NetworkManager/%{real_version_major}/%{name}-%{real_version}.tar.xz
+#Source: https://gitlab.freedesktop.org/NetworkManager/NetworkManager/-/releases/%{git_tag_version}/downloads/%{name}-%{real_version}.tar.xz
Source: __SOURCE1__
Source1: NetworkManager.conf
Source2: 00-server.conf
diff --git a/contrib/fedora/rpm/build.sh b/contrib/fedora/rpm/build.sh
index 4a89a6b45b..1f8188a2f1 100755
--- a/contrib/fedora/rpm/build.sh
+++ b/contrib/fedora/rpm/build.sh
@@ -12,6 +12,7 @@ set -o pipefail
# RELEASE_VERSION=
# SNAPSHOT=
# VERSION=
+# GIT_TAG_VERSION=
# COMMIT_FULL=
# COMMIT=
# USERNAME=
@@ -112,6 +113,7 @@ UUID=`uuidgen`
RELEASE_VERSION="${RELEASE_VERSION:-$(git rev-list HEAD | wc -l)}"
SNAPSHOT="${SNAPSHOT:-%{nil\}}"
VERSION="${VERSION:-$(get_version || die "Could not read $VERSION")}"
+GIT_TAG_VERSION="${GIT_TAG_VERSION:-$VERSION}"
COMMIT_FULL="${COMMIT_FULL:-$(git rev-parse --verify HEAD || die "Error reading HEAD revision")}"
COMMIT="${COMMIT:-$(printf '%s' "$COMMIT_FULL" | sed 's/^\(.\{10\}\).*/\1/' || die "Error reading HEAD revision")}"
BCOND_DEFAULT_DEBUG="${BCOND_DEFAULT_DEBUG:-0}"
@@ -155,6 +157,7 @@ if [[ "$SOURCE_FROM_GIT" == "1" ]]; then
fi
LOG "VERSION=$VERSION"
+LOG "GIT_TAG_VERSION=$GIT_TAG_VERSION"
LOG "RELEASE_VERSION=$RELEASE_VERSION"
LOG "SNAPSHOT=$SNAPSHOT"
LOG "COMMIT_FULL=$COMMIT_FULL"
@@ -207,6 +210,7 @@ cp "$SOURCE_README_IFCFG_MIGRATED" "$TEMP/SOURCES/readme-ifcfg-rh-migrated.txt"
write_changelog
sed -e "s/__VERSION__/$VERSION/g" \
+ -e "s/__GIT_TAG_VERSION__/$GIT_TAG_VERSION/g" \
-e "s/__RELEASE_VERSION__/$RELEASE_VERSION/g" \
-e "s/__SNAPSHOT__/$SNAPSHOT/g" \
-e "s/__COMMIT__/$COMMIT/g" \
diff --git a/contrib/scripts/nm-ci-run.sh b/contrib/scripts/nm-ci-run.sh
index 4881d110d3..ff9aadf898 100755
--- a/contrib/scripts/nm-ci-run.sh
+++ b/contrib/scripts/nm-ci-run.sh
@@ -167,6 +167,7 @@ meson setup build \
-D libpsl=false \
-D vapi=false \
-D introspection=$_WITH_DOCS \
+ -D man=$_WITH_DOCS \
-D qt=false \
-D crypto=$_WITH_CRYPTO \
-D docs=$_WITH_DOCS \
diff --git a/docs/api/meson.build b/docs/api/meson.build
index 6dc4adc17b..aea7e5a9a6 100644
--- a/docs/api/meson.build
+++ b/docs/api/meson.build
@@ -1,6 +1,8 @@
# SPDX-License-Identifier: LGPL-2.1-or-later
if enable_introspection
+ xsltproc = find_program('xsltproc')
+
settings = 'settings-spec'
output = settings + '.xml'
diff --git a/man/meson.build b/man/meson.build
index 56f52bb4b3..d3310dc1de 100644
--- a/man/meson.build
+++ b/man/meson.build
@@ -1,29 +1,5 @@
# SPDX-License-Identifier: LGPL-2.1-or-later
-common_ent_file = configure_file(
- input: 'common.ent.in',
- output: '@BASENAME@',
- configuration: data_conf,
-)
-
-xsltproc_options = [
- xsltproc,
- '--output', '@OUTPUT@',
- '--path', meson.current_build_dir(),
- '--xinclude',
- '--nonet',
- '--stringparam', 'man.output.quietly', '1',
- '--stringparam', 'funcsynopsis.style', 'ansi',
- '--stringparam', 'man.th.extra1.suppress', '1',
- '--stringparam', 'man.authors.section.enabled', '0',
- '--stringparam', 'man.copyright.section.enabled', '0',
- '--stringparam', 'man.th.title.max.length', '30',
-]
-
-docbook_xls = 'http://docbook.sourceforge.net/release/xsl/current/manpages/docbook.xsl'
-
-mans_xmls = []
-
mans = [
['NetworkManager', '8'],
['NetworkManager-dispatcher', '8'],
@@ -44,24 +20,74 @@ if enable_nm_cloud_setup
mans += [['nm-cloud-setup', '8']]
endif
-foreach man: mans
- input = man[0] + '.xml'
- content_files += join_paths(meson.current_source_dir(), input)
+introspection_mans = [
+ ['nm-settings-keyfile', '5'],
+ ['nm-settings-dbus', '5'],
+ ['nm-settings-nmcli', '5'],
+]
- output = '@0@.@1@'.format(man[0], man[1])
+if enable_ifcfg_rh
+ introspection_mans += [['nm-settings-ifcfg-rh', '5']]
+endif
- custom_target(
- output,
- input: input,
- output: output,
- command: xsltproc_options + [docbook_xls, '@INPUT@'],
- depend_files: common_ent_file,
- install: true,
- install_dir: join_paths(nm_mandir, 'man' + man[1]),
- )
+built_mans = []
+foreach man: mans + introspection_mans
+ name = man[0] + '.' + man[1]
+ if not fs.exists(name)
+ built_mans = []
+ break
+ endif
+
+ built_mans += name
endforeach
-if enable_introspection
+if enable_introspection or enable_docs
+ common_ent_file = configure_file(
+ input: 'common.ent.in',
+ output: '@BASENAME@',
+ configuration: data_conf,
+ )
+endif
+
+if enable_introspection and (enable_man or enable_docs)
+ xsltproc_options = [
+ find_program('xsltproc'),
+ '--output', '@OUTPUT@',
+ '--path', meson.current_build_dir(),
+ '--xinclude',
+ '--nonet',
+ '--stringparam', 'man.output.quietly', '1',
+ '--stringparam', 'funcsynopsis.style', 'ansi',
+ '--stringparam', 'man.th.extra1.suppress', '1',
+ '--stringparam', 'man.authors.section.enabled', '0',
+ '--stringparam', 'man.copyright.section.enabled', '0',
+ '--stringparam', 'man.th.title.max.length', '30',
+ ]
+
+ docbook_xls = 'http://docbook.sourceforge.net/release/xsl/current/manpages/docbook.xsl'
+
+ mans_xmls = []
+
+ foreach man: mans
+ input = man[0] + '.xml'
+ content_files += join_paths(meson.current_source_dir(), input)
+
+ output = '@0@.@1@'.format(man[0], man[1])
+
+ # not needed if only html requested
+ if enable_man
+ custom_target(
+ output,
+ input: input,
+ output: output,
+ command: xsltproc_options + [docbook_xls, '@INPUT@'],
+ depend_files: common_ent_file,
+ install: true,
+ install_dir: join_paths(nm_mandir, 'man' + man[1]),
+ )
+ endif
+ endforeach
+
merge_cmd = files(source_root / 'tools' / 'generate-docs-nm-settings-docs-merge.py')
name = 'dbus'
@@ -124,13 +150,23 @@ if enable_introspection
output = '@0@.@1@'.format(man[0], man[1])
- custom_target(
- output,
- input: input,
- output: output,
- command: xsltproc_options + [docbook_xls, '@INPUT@'],
- install: true,
- install_dir: join_paths(nm_mandir, 'man' + man[1]),
- )
+ # not needed if only html requested
+ if enable_man
+ custom_target(
+ output,
+ input: input,
+ output: output,
+ command: xsltproc_options + [docbook_xls, '@INPUT@'],
+ install: true,
+ install_dir: join_paths(nm_mandir, 'man' + man[1]),
+ )
+ endif
endforeach
+# not needed if only html requested
+elif enable_man
+ if built_mans.length() > 0
+ install_man(built_mans)
+ else
+ error('Building manpages requires xsltproc and -Dintrospection=true, and no prebuilt manpages were found. Try building from a release tarball or using -Dman=false.')
+ endif
endif
diff --git a/man/nmcli.xml b/man/nmcli.xml
index f3b6706ece..7bce0de737 100644
--- a/man/nmcli.xml
+++ b/man/nmcli.xml
@@ -1066,15 +1066,16 @@
dummy
generic
gsm
+ hsr
infiniband
ip-tunnel
+ ipvlan
+ loopback
macsec
macvlan
olpc-mesh
ovs-bridge
- ovs-dpdk
ovs-interface
- ovs-patch
ovs-port
pppoe
team
diff --git a/meson.build b/meson.build
index 2eac4ed73d..89834dd750 100644
--- a/meson.build
+++ b/meson.build
@@ -5,14 +5,14 @@ project(
# NOTE: When incrementing version also add corresponding
# NM_VERSION_x_y_z macros in
# "src/libnm-core-public/nm-version-macros.h.in"
- version: '1.54.0',
+ version: '1.55.3',
license: 'GPL2+',
default_options: [
'buildtype=debugoptimized',
'c_std=gnu11',
'warning_level=2' # value "2" will add "-Wall" and "-Wextra" to the compiler flags
],
- meson_version: '>= 0.51.0',
+ meson_version: '>= 0.53.0',
)
nm_name = meson.project_name()
@@ -77,6 +77,7 @@ libnm_version = '@0@.@1@.@2@'.format(current - age, age, revision)
libnm_pkgincludedir = join_paths(nm_includedir, libnm_name)
+fs = import('fs')
gnome = import('gnome')
i18n = import('i18n')
pkg = import('pkgconfig')
@@ -89,7 +90,6 @@ po_dir = source_root / 'po'
top_inc = include_directories('.')
perl = find_program('perl')
-xsltproc = find_program('xsltproc')
check_exports = find_program(join_paths(source_root, 'tools', 'check-exports.sh'))
@@ -816,6 +816,7 @@ if enable_nm_cloud_setup
assert(jansson_dep.found(), 'nm-cloud-setup requires jansson library. Use -Dnm_cloud_setup=false to disable it')
endif
+enable_man = get_option('man')
enable_docs = get_option('docs')
more_asserts = get_option('more_asserts')
@@ -964,38 +965,6 @@ data_conf.set('nmstatedir', nm_pkgstatedir)
data_conf.set('sbindir', nm_sbindir)
data_conf.set('sysconfdir', nm_sysconfdir)
-# check if we can build setting property documentation
-'''
-build_docs=no
-if test -n "$INTROSPECTION_MAKEFILE"; then
- # If g-i is installed we know we have python, but we might not have pygobject
- if ! "$PYTHON" -c 'from gi.repository import GObject' >& /dev/null; then
- AC_MSG_ERROR(["--enable-introspection aims to build the settings documentation. This requires GObject introspection for python (pygobject)])
- fi
-
- AC_PATH_PROG(PERL, perl)
- if test -z "$PERL"; then
- AC_MSG_ERROR([--enable-introspection requires perl])
- fi
- AC_PATH_PROG(XSLTPROC, xsltproc)
- if test -z "$XSLTPROC"; then
- AC_MSG_ERROR([--enable-introspection requires xsltproc])
- fi
-
- have_introspection=yes
- if test "$enable_gtk_doc" = "yes"; then
- build_docs=yes
- fi
-else
- if test "$enable_gtk_doc" = "yes"; then
- # large parts of the documentation require introspection/pygobject to extract
- # the documentation out of the source files. You cannot enable gtk-doc without alone.
- AC_MSG_ERROR(["--with-gtk-doc requires --enable-introspection"])
- fi
- have_introspection=no
-fi
-'''
-
content_files = []
subdir('introspection')
@@ -1033,9 +1002,14 @@ if enable_qt != 'false'
endif
endif
+# The man/ directory builds a couple targets needed by the docs build too.
+# If we build with docs but no man, then enter the subdir and only build
+# some targets.
+if enable_docs or enable_man
+ subdir('man')
+endif
if enable_docs
assert(enable_introspection, '-Ddocs=true requires -Dintrospection=true')
- subdir('man')
subdir('docs')
meson.add_dist_script(
'tools/meson-dist-data.sh',
@@ -1086,7 +1060,7 @@ meson.add_install_script(
nm_pkgstatedir,
nm_mandir,
nm_sysconfdir,
- enable_docs ? '1' : '0',
+ enable_man ? '1' : '0',
enable_ifcfg_rh ? '1' : '0',
enable_nm_cloud_setup ? '1' : '0',
install_systemdunitdir ? '1' : '0',
diff --git a/meson_options.txt b/meson_options.txt
index 18d8bfc44c..55762f65d5 100644
--- a/meson_options.txt
+++ b/meson_options.txt
@@ -67,6 +67,7 @@ option('config_dhcp_default', type: 'combo', choices: ['dhclient', 'dhcpcd', 'in
option('introspection', type: 'boolean', value: true, description: 'Enable introspection for this build')
option('vapi', type : 'combo', choices : ['auto', 'true', 'false'], description: 'build Vala bindings')
option('docs', type: 'boolean', value: false, description: 'use to build documentation')
+option('man', type: 'boolean', value: true, description: 'Install manpages')
option('tests', type: 'combo', choices: ['yes', 'no', 'root'], value: 'yes', description: 'Build NetworkManager tests')
option('firewalld_zone', type: 'boolean', value: true, description: 'Install and use firewalld zone for shared mode')
option('more_asserts', type: 'string', value: 'auto', description: 'Enable more assertions for debugging (0 = no, 100 = all, default: auto)')
diff --git a/src/contrib/nm-vpn-plugin-utils.c b/src/contrib/nm-vpn-plugin-utils.c
index a9407608db..24b08ac6df 100644
--- a/src/contrib/nm-vpn-plugin-utils.c
+++ b/src/contrib/nm-vpn-plugin-utils.c
@@ -155,3 +155,33 @@ nm_vpn_plugin_utils_load_editor(const char *module_path,
g_return_val_if_fail(NM_IS_VPN_EDITOR(editor), NULL);
return editor;
}
+
+char *
+nm_vpn_plugin_utils_get_cert_path(const char *plugin)
+{
+ const char *path;
+
+ g_return_val_if_fail(plugin, NULL);
+
+ /* Users can set NM_CERT_PATH=~/.cert to be compatible with the certificate
+ * directory used in the past. */
+ path = g_getenv("NM_CERT_PATH");
+ if (path)
+ return g_build_filename(path, plugin, NULL);
+
+ /* Otherwise use XDG_DATA_HOME. We use subdirectory "networkmanagement/certificates"
+ * because the SELinux policy already has rules to set the correct labels in that
+ * directory. */
+ path = g_getenv("XDG_DATA_HOME");
+ if (path)
+ return g_build_filename(path, "networkmanagement", "certificates", plugin, NULL);
+
+ /* Use the default value for XDG_DATA_HOME */
+ return g_build_filename(g_get_home_dir(),
+ ".local",
+ "share",
+ "networkmanagement",
+ "certificates",
+ plugin,
+ NULL);
+}
diff --git a/src/contrib/nm-vpn-plugin-utils.h b/src/contrib/nm-vpn-plugin-utils.h
index 6a6ea0b99c..78e29d0bf8 100644
--- a/src/contrib/nm-vpn-plugin-utils.h
+++ b/src/contrib/nm-vpn-plugin-utils.h
@@ -24,4 +24,6 @@ NMVpnEditor *nm_vpn_plugin_utils_load_editor(const char *modul
gpointer user_data,
GError **error);
+char *nm_vpn_plugin_utils_get_cert_path(const char *plugin);
+
#endif /* __NM_VPN_PLUGIN_UTILS_H__ */
diff --git a/src/core/devices/nm-device-bridge.c b/src/core/devices/nm-device-bridge.c
index 45cdd83f07..6ca3737f9d 100644
--- a/src/core/devices/nm-device-bridge.c
+++ b/src/core/devices/nm-device-bridge.c
@@ -1066,7 +1066,7 @@ attach_port(NMDevice *device,
plat_vlans = setting_vlans_to_platform(vlans, &num_vlans);
- /* Since the link was just enportd, there are no existing VLANs
+ /* Since the link was just attached, there are no existing VLANs
* (except for the default one) and so there's no need to flush. */
if (plat_vlans
diff --git a/src/core/devices/nm-device-ethernet.c b/src/core/devices/nm-device-ethernet.c
index 4034fdaad6..2059ce4279 100644
--- a/src/core/devices/nm-device-ethernet.c
+++ b/src/core/devices/nm-device-ethernet.c
@@ -14,7 +14,6 @@
#include
#include
-#include "NetworkManagerUtils.h"
#include "NetworkManagerUtils.h"
#include "libnm-core-aux-intern/nm-libnm-core-utils.h"
#include "libnm-core-intern/nm-core-internal.h"
@@ -1894,7 +1893,7 @@ get_ip_method_auto(NMDevice *device, int addr_family)
/* We cannot do DHCPv4 on a PPP link, instead we get "auto" IP addresses
* by pppd. Return "manual" here, which has the suitable effect to a
* (zero) manual addresses in addition. */
- return NM_SETTING_IP6_CONFIG_METHOD_MANUAL;
+ return NM_SETTING_IP4_CONFIG_METHOD_MANUAL;
}
return NM_SETTING_IP6_CONFIG_METHOD_AUTO;
diff --git a/src/core/devices/nm-device.c b/src/core/devices/nm-device.c
index f6057e529f..198e75ec70 100644
--- a/src/core/devices/nm-device.c
+++ b/src/core/devices/nm-device.c
@@ -13008,7 +13008,6 @@ _dev_addrgenmode6_set(NMDevice *self, guint8 addr_gen_mode)
if (!priv->addrgenmode6_data.previous_mode_has) {
priv->addrgenmode6_data.previous_mode_has = TRUE;
priv->addrgenmode6_data.previous_mode_val = cur_addr_gen_mode;
- nm_assert(priv->addrgenmode6_data.previous_mode_val == cur_addr_gen_mode);
}
_LOGD_ip(AF_INET6,
diff --git a/src/core/nm-l3cfg.c b/src/core/nm-l3cfg.c
index 1797cd0f93..35dc81cff9 100644
--- a/src/core/nm-l3cfg.c
+++ b/src/core/nm-l3cfg.c
@@ -40,8 +40,7 @@ G_STATIC_ASSERT(NM_ACD_TIMEOUT_RFC5227_MSEC == N_ACD_TIMEOUT_RFC5227);
#define ACD_SUPPORTED_ETH_ALEN ETH_ALEN
#define ACD_ENSURE_RATELIMIT_MSEC ((guint32) 4000u)
-#define ACD_WAIT_PROBING_EXTRA_TIME_MSEC ((guint32) (1000u + ACD_ENSURE_RATELIMIT_MSEC))
-#define ACD_WAIT_PROBING_EXTRA_TIME2_MSEC ((guint32) 1000u)
+#define ACD_WAIT_PROBING_EXTRA_TIME_MSEC ((guint32) (2000u + ACD_ENSURE_RATELIMIT_MSEC))
#define ACD_WAIT_TIME_PROBING_FULL_RESTART_MSEC ((guint32) 30000u)
#define ACD_WAIT_TIME_CONFLICT_RESTART_MSEC ((guint32) 120000u)
#define ACD_WAIT_TIME_ANNOUNCE_RESTART_MSEC ((guint32) 30000u)
@@ -2740,9 +2739,8 @@ handle_init:
nm_utils_get_monotonic_timestamp_msec_cached(p_now_msec);
if (acd_data->info.state == NM_L3_ACD_ADDR_STATE_PROBING) {
- if ((*p_now_msec) > acd_data->probing_timestamp_msec
- + ACD_WAIT_PROBING_EXTRA_TIME_MSEC
- + ACD_WAIT_PROBING_EXTRA_TIME2_MSEC) {
+ if ((*p_now_msec)
+ > acd_data->probing_timestamp_msec + ACD_WAIT_PROBING_EXTRA_TIME_MSEC) {
/* hm. We failed to create a new probe too long. Something is really wrong
* internally, but let's ignore the issue and assume the address is good. What
* else would we do? Assume the address is USED? */
@@ -2948,7 +2946,7 @@ handle_init:
nm_utils_get_monotonic_timestamp_msec_cached(p_now_msec);
if (acd_data->probing_timestamp_msec + acd_data->probing_timeout_msec
- + ACD_WAIT_PROBING_EXTRA_TIME_MSEC + ACD_WAIT_PROBING_EXTRA_TIME2_MSEC
+ + ACD_WAIT_PROBING_EXTRA_TIME_MSEC
>= (*p_now_msec)) {
/* The probing already started quite a while ago. We ignore the link event
* and let the probe come to it's natural end. */
diff --git a/src/core/platform/tests/test-route.c b/src/core/platform/tests/test-route.c
index f8f7070bff..fbad2447a9 100644
--- a/src/core/platform/tests/test-route.c
+++ b/src/core/platform/tests/test-route.c
@@ -623,6 +623,79 @@ test_ip4_zero_gateway(void)
nmtstp_wait_for_signal(NM_PLATFORM_GET, 50);
}
+static void
+test_via(void)
+{
+ int ifindex = nm_platform_link_get_ifindex(NM_PLATFORM_GET, DEVICE_NAME);
+ GPtrArray *routes;
+ NMPlatformIP4Route rts[1];
+ struct in6_addr gateway6;
+ const int metric = 22987;
+ NMPlatformIP4Route route4;
+ guint mss = 1000;
+ in_addr_t net4;
+
+ /* Test IPv4 routes with a IPv6 gateway (using RTA_VIA attribute) */
+
+ inet_pton(AF_INET6, "fd01::1", &gateway6);
+ inet_pton(AF_INET, "1.2.3.4", &net4);
+
+ /* Add direct route to IPv6 gateway: ip route add dev $DEV fd01::1/128 */
+ nmtstp_ip6_route_add(NM_PLATFORM_GET,
+ ifindex,
+ NM_IP_CONFIG_SOURCE_USER,
+ gateway6,
+ 128,
+ in6addr_any,
+ in6addr_any,
+ metric,
+ mss);
+ g_assert(nmtstp_ip6_route_get(NM_PLATFORM_GET, ifindex, &gateway6, 128, metric, NULL, 0));
+
+ /* Add IPv4 route via IPv6 gateway: ip route add dev $DEV 1.2.3.4/32 via inet6 fd01::1 */
+ route4 = (NMPlatformIP4Route) {
+ .ifindex = ifindex,
+ .rt_source = NM_IP_CONFIG_SOURCE_USER,
+ .network = net4,
+ .plen = 32,
+ .metric = metric,
+ .via.addr_family = AF_INET6,
+ .via.addr.addr6 = gateway6,
+ .mss = mss,
+ };
+ g_assert(NMTST_NM_ERR_SUCCESS(
+ nm_platform_ip4_route_add(NM_PLATFORM_GET, NMP_NLM_FLAG_REPLACE, &route4, NULL)));
+ g_assert(nmtstp_ip4_route_get(NM_PLATFORM_GET, ifindex, net4, 32, metric, 0));
+
+ /* Test route listing */
+ routes = nmtstp_ip4_route_get_all(NM_PLATFORM_GET, ifindex);
+ g_assert_cmpint(routes->len, ==, 1);
+
+ memset(rts, 0, sizeof(rts));
+ rts[0].rt_source = nmp_utils_ip_config_source_round_trip_rtprot(NM_IP_CONFIG_SOURCE_USER);
+ rts[0].scope_inv = nm_platform_route_scope_inv(RT_SCOPE_LINK);
+ rts[0].network = net4;
+ rts[0].plen = 32;
+ rts[0].ifindex = ifindex;
+ rts[0].gateway = INADDR_ANY;
+ rts[0].metric = metric;
+ rts[0].mss = mss;
+ rts[0].via.addr_family = AF_INET6;
+ rts[0].via.addr.addr6 = gateway6;
+ rts[0].n_nexthops = 1;
+ nmtst_platform_ip4_routes_equal_aptr((const NMPObject *const *) routes->pdata,
+ rts,
+ routes->len,
+ TRUE);
+ g_ptr_array_unref(routes);
+
+ /* Delete routes */
+ g_assert(nmtstp_platform_ip6_route_delete(NM_PLATFORM_GET, ifindex, gateway6, 128, metric));
+ g_assert(!nmtstp_ip6_route_get(NM_PLATFORM_GET, ifindex, &gateway6, 128, metric, NULL, 0));
+ g_assert(nmtstp_platform_ip4_route_delete(NM_PLATFORM_GET, ifindex, net4, 32, metric));
+ g_assert(!nmtstp_ip4_route_get(NM_PLATFORM_GET, ifindex, net4, 32, metric, 0));
+}
+
static void
test_ip4_route_options(gconstpointer test_data)
{
@@ -2421,6 +2494,7 @@ _nmtstp_setup_tests(void)
add_test_func("/route/ip4_route_get", test_ip4_route_get);
add_test_func("/route/ip6_route_get", test_ip6_route_get);
add_test_func("/route/ip4_zero_gateway", test_ip4_zero_gateway);
+ add_test_func("/route/via", test_via);
}
if (nmtstp_is_root_test()) {
diff --git a/src/core/supplicant/nm-supplicant-config.c b/src/core/supplicant/nm-supplicant-config.c
index 9e06639510..38294e89a3 100644
--- a/src/core/supplicant/nm-supplicant-config.c
+++ b/src/core/supplicant/nm-supplicant-config.c
@@ -1012,7 +1012,7 @@ nm_supplicant_config_add_setting_wireless_security(NMSupplicantConfig
if (_get_capability(priv, NM_SUPPL_CAP_TYPE_SAE)
&& _get_capability(priv, NM_SUPPL_CAP_TYPE_PMF)
&& _get_capability(priv, NM_SUPPL_CAP_TYPE_BIP)
- && (!is_ap || pmf != NM_SETTING_WIRELESS_SECURITY_PMF_DISABLE)) {
+ && (pmf != NM_SETTING_WIRELESS_SECURITY_PMF_DISABLE)) {
g_string_append(key_mgmt_conf, " SAE");
if (!is_ap && _get_capability(priv, NM_SUPPL_CAP_TYPE_FT))
g_string_append(key_mgmt_conf, " FT-SAE");
diff --git a/src/libnm-client-aux-extern/tests/test-libnm-client-aux.c b/src/libnm-client-aux-extern/tests/test-libnm-client-aux.c
index f727b3d93e..6a25b652d1 100644
--- a/src/libnm-client-aux-extern/tests/test-libnm-client-aux.c
+++ b/src/libnm-client-aux-extern/tests/test-libnm-client-aux.c
@@ -234,6 +234,227 @@ test_team_link_watcher_tofro_string(void)
NM_TEAM_LINK_WATCHER_ARP_PING_FLAG_NONE);
}
+static void
+test_wireguard_peer(void)
+{
+ guint i;
+ struct {
+ const char *input;
+ const char *canonical; /* canonical string representation */
+
+ gboolean invalid;
+ const char *pubkey;
+ const char *endpoint;
+ guint16 keepalive;
+ guint num_allowed_ips;
+ const char *allowed_ips[2];
+ const char *psk;
+ int psk_flags;
+ } tests[] = {{
+ /* Public key only */
+ .input = "MWEKYcE9MEh5RoGDuJYrJ2YgkoosONGhuHRBAC00e14=",
+ .canonical = "MWEKYcE9MEh5RoGDuJYrJ2YgkoosONGhuHRBAC00e14=",
+ .pubkey = "MWEKYcE9MEh5RoGDuJYrJ2YgkoosONGhuHRBAC00e14=",
+ },
+ {
+ /* IPv4 endpoint */
+ .input = "+DIX0qWKQ4E6hy7MWzsSRXjqAHCtffWrXTdJPe/xS04="
+ " endpoint=1.2.3.4:5555",
+ .canonical = "+DIX0qWKQ4E6hy7MWzsSRXjqAHCtffWrXTdJPe/xS04="
+ " endpoint=1.2.3.4:5555",
+ .pubkey = "+DIX0qWKQ4E6hy7MWzsSRXjqAHCtffWrXTdJPe/xS04=",
+ .endpoint = "1.2.3.4:5555",
+ },
+ {
+ /* IPv6 endpoint */
+ .input = "aPsdPkeqH4l5Nax3g3e8A8f7g0hJk2l3m4N5p6q7R8s="
+ " endpoint=[fd01:db8::1]:8080",
+ .canonical = "aPsdPkeqH4l5Nax3g3e8A8f7g0hJk2l3m4N5p6q7R8s="
+ " endpoint=[fd01:db8::1]:8080",
+ .pubkey = "aPsdPkeqH4l5Nax3g3e8A8f7g0hJk2l3m4N5p6q7R8s=",
+ .endpoint = "[fd01:db8::1]:8080",
+ },
+ {
+ /* IPv6 endpoint, without brackets */
+ .input = "+DIX0qWKQ4E6hy7MWzsSRXjqAHCtffWrXTdJPe/xS04="
+ " endpoint=fd01::12:8080",
+ .canonical = "+DIX0qWKQ4E6hy7MWzsSRXjqAHCtffWrXTdJPe/xS04="
+ " endpoint=fd01::12:8080",
+ .pubkey = "+DIX0qWKQ4E6hy7MWzsSRXjqAHCtffWrXTdJPe/xS04=",
+ .endpoint = "fd01::12:8080",
+ },
+ {
+ /* Single IPv4 allowed-ip */
+ .input = "s4fmZZA3gMGVv8+0hkSwrmeLC6nNd+Pd6DlSaufLKhY="
+ " allowed-ips=172.16.0.0/16",
+ .canonical = "s4fmZZA3gMGVv8+0hkSwrmeLC6nNd+Pd6DlSaufLKhY="
+ " allowed-ips=172.16.0.0/16",
+ .pubkey = "s4fmZZA3gMGVv8+0hkSwrmeLC6nNd+Pd6DlSaufLKhY=",
+ .num_allowed_ips = 1,
+ .allowed_ips = {"172.16.0.0/16"},
+ },
+ {
+ /* Multiple allowed-ips */
+ .input = "V02J2zmCi2LHX2KMK+ZOgDNhZzK4JXjGNr7CYfz9DxQ="
+ " allowed-ips=192.168.2.0/24;2001:db8:a::/48",
+ .canonical = "V02J2zmCi2LHX2KMK+ZOgDNhZzK4JXjGNr7CYfz9DxQ="
+ " allowed-ips=192.168.2.0/24;2001:db8:a::/48",
+ .pubkey = "V02J2zmCi2LHX2KMK+ZOgDNhZzK4JXjGNr7CYfz9DxQ=",
+ .num_allowed_ips = 2,
+ .allowed_ips = {"192.168.2.0/24", "2001:db8:a::/48"},
+ },
+ {
+ /* Persistent-keepalive */
+ .input = "D1FTp8Wy1oJQI045yXo9EMdxJqjXHC3VhTCPTh3lSQM="
+ " persistent-keepalive=25",
+ .canonical = "D1FTp8Wy1oJQI045yXo9EMdxJqjXHC3VhTCPTh3lSQM="
+ " persistent-keepalive=25",
+ .pubkey = "D1FTp8Wy1oJQI045yXo9EMdxJqjXHC3VhTCPTh3lSQM=",
+ .keepalive = 25,
+ },
+ {
+ /* Preshared-key without flags (should default to 0) */
+ .input = "H5cWWgpWgJH+nHFhsuPS3adgZHuc6Z4cRzfiNRTinE0="
+ " preshared-key=16uGwZvROnwyNGoW6Z3pvJB5GKbd6ncYROA/FFleLQA=",
+ .canonical = "H5cWWgpWgJH+nHFhsuPS3adgZHuc6Z4cRzfiNRTinE0="
+ " preshared-key=16uGwZvROnwyNGoW6Z3pvJB5GKbd6ncYROA/FFleLQA="
+ " preshared-key-flags=0",
+ .pubkey = "H5cWWgpWgJH+nHFhsuPS3adgZHuc6Z4cRzfiNRTinE0=",
+ .psk = "16uGwZvROnwyNGoW6Z3pvJB5GKbd6ncYROA/FFleLQA=",
+ .psk_flags = 0,
+ },
+ {
+ /* Preshared-key flags as string */
+ .input = "H5cWWgpWgJH+nHFhsuPS3adgZHuc6Z4cRzfiNRTinE0="
+ " preshared-key=16uGwZvROnwyNGoW6Z3pvJB5GKbd6ncYROA/FFleLQA="
+ " preshared-key-flags=not-saved",
+ .canonical = "H5cWWgpWgJH+nHFhsuPS3adgZHuc6Z4cRzfiNRTinE0="
+ " preshared-key=16uGwZvROnwyNGoW6Z3pvJB5GKbd6ncYROA/FFleLQA="
+ " preshared-key-flags=2",
+ .pubkey = "H5cWWgpWgJH+nHFhsuPS3adgZHuc6Z4cRzfiNRTinE0=",
+ .psk = "16uGwZvROnwyNGoW6Z3pvJB5GKbd6ncYROA/FFleLQA=",
+ .psk_flags = 2,
+ },
+ {
+ /* Non-canonical order and extra whitespaces */
+ .input = "gqQ9dUqKQNfz/KOqELJpS0MKBvRcYWL8sm/LGEWKKQY="
+ " preshared-key=EVVP8pOzn8R3nQtv62/hnGsXzyagEgykSboFe4EFhQc="
+ " endpoint=vpn.example.com:51820 "
+ " preshared-key-flags=1"
+ " persistent-keepalive=45"
+ " allowed-ips=0.0.0.0/0;::/0",
+ .canonical = "gqQ9dUqKQNfz/KOqELJpS0MKBvRcYWL8sm/LGEWKKQY="
+ " allowed-ips=0.0.0.0/0;::/0"
+ " endpoint=vpn.example.com:51820"
+ " persistent-keepalive=45"
+ " preshared-key=EVVP8pOzn8R3nQtv62/hnGsXzyagEgykSboFe4EFhQc="
+ " preshared-key-flags=1",
+ .pubkey = "gqQ9dUqKQNfz/KOqELJpS0MKBvRcYWL8sm/LGEWKKQY=",
+ .endpoint = "vpn.example.com:51820",
+ .keepalive = 45,
+ .num_allowed_ips = 2,
+ .allowed_ips = {"0.0.0.0/0", "::/0"},
+ .psk = "EVVP8pOzn8R3nQtv62/hnGsXzyagEgykSboFe4EFhQc=",
+ .psk_flags = 1,
+ },
+ {
+ /* Empty string */
+ .input = "",
+ .invalid = TRUE,
+ },
+ {
+ /* Invalid public key*/
+ .input = "aaaaaaaaaaaaaaaaaaaaaaa=",
+ .invalid = TRUE,
+ },
+ {
+ /* Missing value*/
+ .input = "gqQ9dUqKQNfz/KOqELJpS0MKBvRcYWL8sm/LGEWKKQY= "
+ "persistent-keepalive=",
+ .invalid = TRUE,
+ },
+ {
+ /* Unknown attribute */
+ .input = "gqQ9dUqKQNfz/KOqELJpS0MKBvRcYWL8sm/LGEWKKQY= "
+ "persistent-keepalive=12 foobarness=13",
+ .invalid = TRUE,
+ },
+ {
+ /* Invalid IPv4 allowed-ip*/
+ .input = "gqQ9dUqKQNfz/KOqELJpS0MKBvRcYWL8sm/LGEWKKQY= "
+ "allowed-ips=192.168.10.256/32",
+ .invalid = TRUE,
+ },
+ {
+ /* Invalid IPv6 allowed-ip */
+ .input = "gqQ9dUqKQNfz/KOqELJpS0MKBvRcYWL8sm/LGEWKKQY= "
+ "allowed-ips=fd01::1::3/64",
+ .invalid = TRUE,
+ },
+ {
+ /* Endpoint with no port */
+ .input = "+DIX0qWKQ4E6hy7MWzsSRXjqAHCtffWrXTdJPe/xS04="
+ " endpoint=1.2.3.4",
+ .invalid = TRUE,
+ },
+ {
+ /* Invalid endpoint */
+ .input = "+DIX0qWKQ4E6hy7MWzsSRXjqAHCtffWrXTdJPe/xS04="
+ " endpoint=1.2.3.5.6",
+ .invalid = TRUE,
+ },
+ {
+ /* Invalid persistent-keepalive */
+ .input = "gqQ9dUqKQNfz/KOqELJpS0MKBvRcYWL8sm/LGEWKKQY= "
+ "persistent-keepalive=yes",
+ .invalid = TRUE,
+ },
+ {
+ /* Invalid PSK */
+ .input = "gqQ9dUqKQNfz/KOqELJpS0MKBvRcYWL8sm/LGEWKKQY="
+ " preshared-key=pskpskpskpskpskpskpskpskpskpskpskpsk",
+ .invalid = TRUE,
+ }};
+
+ for (i = 0; i < G_N_ELEMENTS(tests); i++) {
+ nm_auto_unref_wgpeer NMWireGuardPeer *peer = NULL;
+ gs_free_error GError *error = NULL;
+ gs_free char *newstr = NULL;
+ guint j;
+
+ peer = _nm_utils_wireguard_peer_from_string(tests[i].input, &error);
+ if (tests[i].invalid) {
+ g_assert(!peer);
+ g_assert(error);
+ continue;
+ }
+ g_assert_no_error(error);
+ g_assert_nonnull(peer);
+
+ newstr = _nm_utils_wireguard_peer_to_string(peer);
+ g_assert_nonnull(newstr);
+ g_assert_cmpstr(tests[i].canonical, ==, newstr);
+
+ g_assert_cmpstr(tests[i].pubkey, ==, nm_wireguard_peer_get_public_key(peer));
+ g_assert_cmpstr(tests[i].endpoint, ==, nm_wireguard_peer_get_endpoint(peer));
+
+ g_assert_cmpint(tests[i].num_allowed_ips, ==, nm_wireguard_peer_get_allowed_ips_len(peer));
+ for (j = 0; j < tests[i].num_allowed_ips; j++) {
+ g_assert_cmpstr(tests[i].allowed_ips[j],
+ ==,
+ nm_wireguard_peer_get_allowed_ip(peer, j, NULL));
+ }
+
+ g_assert_cmpint(tests[i].keepalive, ==, nm_wireguard_peer_get_persistent_keepalive(peer));
+ g_assert_cmpstr(tests[i].psk, ==, nm_wireguard_peer_get_preshared_key(peer));
+ if (tests[i].psk) {
+ g_assert_cmpint(tests[i].psk_flags,
+ ==,
+ nm_wireguard_peer_get_preshared_key_flags(peer));
+ }
+ }
+}
+
/*****************************************************************************/
NMTST_DEFINE();
@@ -245,6 +466,7 @@ main(int argc, char **argv)
g_test_add_func("/libnm-core-aux/test_team_link_watcher_tofro_string",
test_team_link_watcher_tofro_string);
+ g_test_add_func("/libnm-core-aux/test-wireguard-peer", test_wireguard_peer);
return g_test_run();
}
diff --git a/src/libnm-core-aux-extern/nm-libnm-core-aux.c b/src/libnm-core-aux-extern/nm-libnm-core-aux.c
index dfc70e35b4..b12000fc14 100644
--- a/src/libnm-core-aux-extern/nm-libnm-core-aux.c
+++ b/src/libnm-core-aux-extern/nm-libnm-core-aux.c
@@ -7,6 +7,7 @@
#include "nm-libnm-core-aux.h"
+#include "nm-errors.h"
#include "libnm-core-aux-intern/nm-libnm-core-utils.h"
#include "libnm-glib-aux/nm-str-buf.h"
@@ -475,3 +476,177 @@ _nm_ip_route_to_string(NMIPRoute *route, NMStrBuf *strbuf)
nm_str_buf_append_printf(strbuf, " metric %" G_GINT64_FORMAT, metric);
}
}
+
+/*****************************************************************************/
+
+char *
+_nm_utils_wireguard_peer_to_string(NMWireGuardPeer *peer)
+{
+ GString *str;
+ const char *endpoint;
+ const char *psk;
+ guint16 keepalive;
+ guint i;
+ guint len;
+
+ g_return_val_if_fail(peer, "");
+
+ nm_assert(nm_wireguard_peer_is_valid(peer, TRUE, TRUE, NULL));
+
+ str = g_string_new("");
+ g_string_append(str, nm_wireguard_peer_get_public_key(peer));
+
+ len = nm_wireguard_peer_get_allowed_ips_len(peer);
+ if (len > 0) {
+ g_string_append(str, " allowed-ips=");
+ for (i = 0; i < len; i++) {
+ g_string_append(str, nm_wireguard_peer_get_allowed_ip(peer, i, NULL));
+ if (i < len - 1)
+ g_string_append(str, ";");
+ }
+ }
+
+ endpoint = nm_wireguard_peer_get_endpoint(peer);
+ if (endpoint) {
+ g_string_append_printf(str, " endpoint=%s", endpoint);
+ }
+
+ keepalive = nm_wireguard_peer_get_persistent_keepalive(peer);
+ if (keepalive != 0) {
+ g_string_append_printf(str, " persistent-keepalive=%hu", keepalive);
+ }
+
+ psk = nm_wireguard_peer_get_preshared_key(peer);
+ if (psk) {
+ g_string_append_printf(str, " preshared-key=%s", psk);
+ g_string_append_printf(str,
+ " preshared-key-flags=%u",
+ (guint) nm_wireguard_peer_get_preshared_key_flags(peer));
+ }
+
+ return g_string_free(str, FALSE);
+}
+
+NMWireGuardPeer *
+_nm_utils_wireguard_peer_from_string(const char *str, GError **error)
+{
+ nm_auto_unref_wgpeer NMWireGuardPeer *peer = NULL;
+ gs_strfreev char **tokens = NULL;
+ gboolean has_psk = FALSE;
+ gboolean has_psk_flags = FALSE;
+ char *value;
+ guint i;
+
+ peer = nm_wireguard_peer_new();
+
+ tokens = g_strsplit_set(str, " ", 0);
+ for (i = 0; tokens[i]; i++) {
+ if (i == 0) {
+ if (!nm_wireguard_peer_set_public_key(peer, tokens[i], FALSE)) {
+ g_set_error(error,
+ NM_CONNECTION_ERROR,
+ NM_CONNECTION_ERROR_INVALID_PROPERTY,
+ "invalid public key '%s'",
+ tokens[i]);
+ return NULL;
+ }
+ continue;
+ }
+
+ if (tokens[i][0] == '\0')
+ continue;
+
+ value = strchr(tokens[i], '=');
+ if (!value || value[1] == '\0') {
+ g_set_error(error,
+ NM_CONNECTION_ERROR,
+ NM_CONNECTION_ERROR_INVALID_PROPERTY,
+ "attribute without value '%s'",
+ tokens[i]);
+ return NULL;
+ }
+
+ *value = '\0';
+ value++;
+
+ if (nm_streq(tokens[i], "allowed-ips")) {
+ gs_strfreev char **ips = NULL;
+ guint j;
+
+ ips = g_strsplit_set(value, ";", 0);
+ for (j = 0; ips[j]; j++) {
+ if (!nm_wireguard_peer_append_allowed_ip(peer, ips[j], FALSE)) {
+ g_set_error(error,
+ NM_CONNECTION_ERROR,
+ NM_CONNECTION_ERROR_INVALID_PROPERTY,
+ "invalid allowed-ip '%s'",
+ ips[j]);
+ return NULL;
+ }
+ }
+ } else if (nm_streq(tokens[i], "endpoint")) {
+ if (!nm_wireguard_peer_set_endpoint(peer, value, FALSE)) {
+ g_set_error(error,
+ NM_CONNECTION_ERROR,
+ NM_CONNECTION_ERROR_INVALID_PROPERTY,
+ "invalid endpoint '%s'",
+ value);
+ return NULL;
+ }
+ } else if (nm_streq(tokens[i], "persistent-keepalive")) {
+ gint64 keepalive;
+
+ keepalive = _nm_utils_ascii_str_to_int64(value, 10, 0, G_MAXUINT16, -1);
+ if (keepalive == -1) {
+ g_set_error(error,
+ NM_CONNECTION_ERROR,
+ NM_CONNECTION_ERROR_INVALID_PROPERTY,
+ "invalid persistent-keepalive value '%s'",
+ value);
+ return NULL;
+ }
+ nm_wireguard_peer_set_persistent_keepalive(peer, (guint16) keepalive);
+ } else if (nm_streq(tokens[i], "preshared-key")) {
+ if (!nm_wireguard_peer_set_preshared_key(peer, value, FALSE)) {
+ g_set_error(error,
+ NM_CONNECTION_ERROR,
+ NM_CONNECTION_ERROR_INVALID_PROPERTY,
+ "invalid preshared-key '%s'",
+ value);
+ return NULL;
+ }
+ has_psk = TRUE;
+ } else if (nm_streq(tokens[i], "preshared-key-flags")) {
+ int flags;
+
+ if (!nm_utils_enum_from_str(NM_TYPE_SETTING_SECRET_FLAGS, value, &flags, NULL)) {
+ g_set_error(error,
+ NM_CONNECTION_ERROR,
+ NM_CONNECTION_ERROR_INVALID_PROPERTY,
+ "invalid preshared-key-flags '%s'",
+ value);
+ return NULL;
+ }
+ nm_wireguard_peer_set_preshared_key_flags(peer, (NMSettingSecretFlags) flags);
+ has_psk_flags = TRUE;
+ } else {
+ g_set_error(error,
+ NM_CONNECTION_ERROR,
+ NM_CONNECTION_ERROR_INVALID_PROPERTY,
+ "invalid attribute '%s'",
+ tokens[i]);
+ return NULL;
+ }
+ }
+
+ if (has_psk && !has_psk_flags) {
+ /* The flags are NOT_REQUIRED by default. With this flag, the PSK would not
+ * be saved by default, unless the user explicitly sets a different value. */
+ nm_wireguard_peer_set_preshared_key_flags(peer, NM_SETTING_SECRET_FLAG_NONE);
+ }
+
+ if (!nm_wireguard_peer_is_valid(peer, TRUE, TRUE, error))
+ return NULL;
+
+ return g_steal_pointer(&peer);
+}
diff --git a/src/libnm-core-aux-extern/nm-libnm-core-aux.h b/src/libnm-core-aux-extern/nm-libnm-core-aux.h
index e45e98ab2d..aa7ea631ef 100644
--- a/src/libnm-core-aux-extern/nm-libnm-core-aux.h
+++ b/src/libnm-core-aux-extern/nm-libnm-core-aux.h
@@ -6,8 +6,9 @@
#ifndef __NM_LIBNM_CORE_AUX_H__
#define __NM_LIBNM_CORE_AUX_H__
-#include "nm-setting-team.h"
#include "nm-setting-ip-config.h"
+#include "nm-setting-team.h"
+#include "nm-setting-wireguard.h"
typedef enum {
NM_TEAM_LINK_WATCHER_TYPE_NONE = 0,
@@ -39,4 +40,7 @@ void _nm_ip_route_to_string(NMIPRoute *route, struct _NMStrBuf *strbuf);
NMTeamLinkWatcher *nm_utils_team_link_watcher_from_string(const char *str, GError **error);
+char *_nm_utils_wireguard_peer_to_string(NMWireGuardPeer *peer);
+NMWireGuardPeer *_nm_utils_wireguard_peer_from_string(const char *str, GError **error);
+
#endif /* __NM_LIBNM_CORE_AUX_H__ */
diff --git a/src/libnm-core-impl/nm-setting-wireguard.c b/src/libnm-core-impl/nm-setting-wireguard.c
index 668af1f6e0..a03090865f 100644
--- a/src/libnm-core-impl/nm-setting-wireguard.c
+++ b/src/libnm-core-impl/nm-setting-wireguard.c
@@ -2515,6 +2515,73 @@ nm_setting_wireguard_class_init(NMSettingWireGuardClass *klass)
NMSettingWireGuard,
_priv.ip6_auto_default_route);
+ /* ---nmcli---
+ * property: peers
+ * format: a comma-separated list of WireGuard peers
+ * description:
+ * A comma-separated list of WireGuard peers. Each peer has the following syntax:
+ *
+ * PUBLIC_KEY [ATTRIBUTE=VALUE [ATTRIBUTE=VALUE]...]
+ *
+ * The supported attributes are: endpoint, allowed-ips, persistent-keepalive,
+ * preshared-key, preshared-key-flags.
+ * description-docbook:
+ *
+ * A comma-separated list of WireGuard peers. Each peer has the following syntax:
+ *
+ *
+ *
+ * public-key
+ * [attribute=value
+ * [attribute=value]...]
+ *
+ *
+ *
+ * The public key is required and must be encoded as base64; it can be
+ * calculated by running wg pubkey on the private key,
+ * and it is usually transmitted out of band to the author of the configuration
+ * file.
+ *
+ *
+ * The supported attributes are:
+ *
+ *
+ * endpoint
+ * An endpoint IP or hostname, followed by a colon, and then
+ * a port number.
+ *
+ *
+ *
+ *
+ *
+ *
+ *
+ * allowed-ips
+ * A semicolon-separated list of IP (v4 or v6) addresses
+ * with CIDR masks from which incoming traffic for this peer is allowed
+ * and to which outgoing traffic for this peer is directed
+ *
+ *
+ *
+ * persistent-keepalive
+ * An interval in seconds, between 1 and 65535, of
+ * how often to send an authenticated empty packet to the peer for the
+ * purpose of keeping a stateful firewall or NAT mapping valid persistently.
+ *
+ *
+ *
+ * preshared-key
+ * A base64 preshared key generated by "wg genpsk". Optional,
+ * and may be omitted.
+ *
+ *
+ * preshared-key-flags
+ * The secret flags for the preshared-key.
+ *
+ *
+ *
+ * ---end---
+ */
/* ---dbus---
* property: peers
* format: array of 'a{sv}'
diff --git a/src/libnm-core-public/nm-version-macros.h.in b/src/libnm-core-public/nm-version-macros.h.in
index 4c4772d293..49f283f6ea 100644
--- a/src/libnm-core-public/nm-version-macros.h.in
+++ b/src/libnm-core-public/nm-version-macros.h.in
@@ -78,6 +78,7 @@
#define NM_VERSION_1_50 (NM_ENCODE_VERSION(1, 50, 0))
#define NM_VERSION_1_52 (NM_ENCODE_VERSION(1, 52, 0))
#define NM_VERSION_1_54 (NM_ENCODE_VERSION(1, 54, 0))
+#define NM_VERSION_1_56 (NM_ENCODE_VERSION(1, 56, 0))
/* For releases, NM_API_VERSION is equal to NM_VERSION.
*
diff --git a/src/libnm-core-public/nm-version.h b/src/libnm-core-public/nm-version.h
index 33daf65f15..775ed62747 100644
--- a/src/libnm-core-public/nm-version.h
+++ b/src/libnm-core-public/nm-version.h
@@ -439,6 +439,20 @@
#define NM_AVAILABLE_IN_1_54
#endif
+#if NM_VERSION_MIN_REQUIRED >= NM_VERSION_1_56
+#define NM_DEPRECATED_IN_1_56 G_DEPRECATED
+#define NM_DEPRECATED_IN_1_56_FOR(f) G_DEPRECATED_FOR(f)
+#else
+#define NM_DEPRECATED_IN_1_56
+#define NM_DEPRECATED_IN_1_56_FOR(f)
+#endif
+
+#if NM_VERSION_MAX_ALLOWED < NM_VERSION_1_56
+#define NM_AVAILABLE_IN_1_56 G_UNAVAILABLE(1, 56)
+#else
+#define NM_AVAILABLE_IN_1_56
+#endif
+
/*
* Synchronous API for calling D-Bus in libnm is deprecated. See
* https://networkmanager.dev/docs/libnm/latest/usage.html#sync-api
diff --git a/src/libnm-platform/nm-linux-platform.c b/src/libnm-platform/nm-linux-platform.c
index d45893fe98..c7c78265c6 100644
--- a/src/libnm-platform/nm-linux-platform.c
+++ b/src/libnm-platform/nm-linux-platform.c
@@ -4013,6 +4013,7 @@ _new_from_nl_route(const struct nlmsghdr *nlh, gboolean id_only, ParseNlmsgIter
[RTA_PREF] = {.type = NLA_U8},
[RTA_FLOW] = {.type = NLA_U32},
[RTA_CACHEINFO] = {.minlen = nm_offsetofend(struct rta_cacheinfo, rta_tsage)},
+ [RTA_VIA] = {.minlen = nm_offsetofend(struct rtvia, rtvia_family)},
[RTA_METRICS] = {.type = NLA_NESTED},
[RTA_MULTIPATH] = {.type = NLA_NESTED},
};
@@ -4029,9 +4030,11 @@ _new_from_nl_route(const struct nlmsghdr *nlh, gboolean id_only, ParseNlmsgIter
guint8 weight;
int ifindex;
NMIPAddr gateway;
+ gboolean is_via;
} nh = {
.found = FALSE,
.has_more = FALSE,
+ .is_via = FALSE,
};
guint v4_n_nexthops = 0;
NMPlatformIP4RtNextHop v4_nh_extra_nexthops_stack[10];
@@ -4200,13 +4203,26 @@ rta_multipath_done:
return nm_assert_unreachable_val(NULL);
}
- if (tb[RTA_OIF] || tb[RTA_GATEWAY] || tb[RTA_FLOW]) {
+ if (tb[RTA_OIF] || tb[RTA_GATEWAY] || tb[RTA_FLOW] || tb[RTA_VIA]) {
int ifindex = 0;
NMIPAddr gateway = {};
if (tb[RTA_OIF])
ifindex = nla_get_u32(tb[RTA_OIF]);
- if (_check_addr_or_return_null(tb, RTA_GATEWAY, addr_len))
+
+ if (tb[RTA_VIA]) {
+ struct rtvia *rtvia;
+
+ /* Used when the gateway family doesn't match the route family */
+ rtvia = nla_data(tb[RTA_VIA]);
+ if (rtvia->rtvia_family != AF_INET6)
+ return NULL;
+ if (nla_len(tb[RTA_VIA]) < sizeof(struct rtvia) + sizeof(struct in6_addr))
+ return NULL;
+
+ nh.is_via = TRUE;
+ memcpy(&gateway, rtvia->rtvia_addr, sizeof(struct in6_addr));
+ } else if (_check_addr_or_return_null(tb, RTA_GATEWAY, addr_len))
memcpy(&gateway, nla_data(tb[RTA_GATEWAY]), addr_len);
if (!nh.found) {
@@ -4222,6 +4238,9 @@ rta_multipath_done:
} else {
/* Kernel supports new style nexthop configuration,
* verify that it is a duplicate and ignore old-style nexthop. */
+ if (nh.is_via)
+ return NULL;
+
if (nh.ifindex != ifindex || memcmp(&nh.gateway, &gateway, addr_len) != 0) {
/* we have a RTA_MULTIPATH attribute that does not agree.
* That seems not right. Error out. */
@@ -4237,7 +4256,7 @@ rta_multipath_done:
* 1 (lo). Of course it does!! */
if (nh.found) {
if (IS_IPv4) {
- if (nh.ifindex != 0 || nh.gateway.addr4 != 0) {
+ if (nh.ifindex != 0 || nh.gateway.addr4 != 0 || nh.is_via) {
/* we only accept kernel to notify about the ifindex/gateway, if it
* is zero. This is only to be a bit forgiving, but we really don't
* know how to handle such routes that have an ifindex. */
@@ -4336,9 +4355,14 @@ rta_multipath_done:
if (tb[RTA_PRIORITY])
obj->ip_route.metric = nla_get_u32(tb[RTA_PRIORITY]);
- if (IS_IPv4)
- obj->ip4_route.gateway = nh.gateway.addr4;
- else
+ if (IS_IPv4) {
+ if (nh.is_via) {
+ obj->ip4_route.via.addr_family = AF_INET6;
+ obj->ip4_route.via.addr = nh.gateway;
+ } else {
+ obj->ip4_route.gateway = nh.gateway.addr4;
+ }
+ } else
obj->ip6_route.gateway = nh.gateway.addr6;
if (IS_IPv4)
@@ -5828,7 +5852,24 @@ _nl_msg_new_route(uint16_t nlmsg_type, uint16_t nlmsg_flags, const NMPObject *ob
/* We currently don't have need for multi-hop routes... */
if (IS_IPv4) {
- NLA_PUT(msg, RTA_GATEWAY, addr_len, &obj->ip4_route.gateway);
+ if (obj->ip4_route.gateway == INADDR_ANY && obj->ip4_route.via.addr_family != AF_UNSPEC) {
+ struct rtvia *rtvia;
+
+ nm_assert(obj->ip4_route.via.addr_family == AF_INET6);
+
+ rtvia = nla_data(nla_reserve(
+ msg,
+ RTA_VIA,
+ sizeof(*rtvia) + nm_utils_addr_family_to_size(obj->ip4_route.via.addr_family)));
+ if (!rtvia)
+ goto nla_put_failure;
+ rtvia->rtvia_family = obj->ip4_route.via.addr_family;
+ memcpy(rtvia->rtvia_addr,
+ obj->ip4_route.via.addr.addr_ptr,
+ nm_utils_addr_family_to_size(obj->ip4_route.via.addr_family));
+ } else {
+ NLA_PUT(msg, RTA_GATEWAY, addr_len, &obj->ip4_route.gateway);
+ }
} else {
if (!IN6_IS_ADDR_UNSPECIFIED(&obj->ip6_route.gateway))
NLA_PUT(msg, RTA_GATEWAY, addr_len, &obj->ip6_route.gateway);
diff --git a/src/libnm-platform/nm-platform.c b/src/libnm-platform/nm-platform.c
index fd966d2ce7..559c7dc3a4 100644
--- a/src/libnm-platform/nm-platform.c
+++ b/src/libnm-platform/nm-platform.c
@@ -7199,7 +7199,7 @@ nm_platform_ip4_route_to_string_full(const NMPlatformIP4Route *route,
{
char *buf0;
char s_network[INET_ADDRSTRLEN];
- char s_gateway[INET_ADDRSTRLEN];
+ char s_gateway[INET6_ADDRSTRLEN];
char s_pref_src[INET_ADDRSTRLEN];
char str_dev[30];
char str_mss[32];
@@ -7228,10 +7228,13 @@ nm_platform_ip4_route_to_string_full(const NMPlatformIP4Route *route,
inet_ntop(AF_INET, &route->network, s_network, sizeof(s_network));
- if (route->gateway == 0)
- s_gateway[0] = '\0';
- else
+ if (route->gateway != INADDR_ANY) {
inet_ntop(AF_INET, &route->gateway, s_gateway, sizeof(s_gateway));
+ } else if (route->via.addr_family == AF_INET6) {
+ inet_ntop(AF_INET6, route->via.addr.addr_ptr, s_gateway, sizeof(s_gateway));
+ } else {
+ s_gateway[0] = '\0';
+ }
nm_strbuf_append(
&buf,
@@ -8967,6 +8970,9 @@ nm_platform_ip4_route_hash_update(const NMPlatformIP4Route *obj,
nm_hash_update_vals(h,
obj->ifindex,
n_nexthops,
+ obj->via.addr_family,
+ obj->via.addr_family == AF_INET6 ? obj->via.addr.addr6
+ : in6addr_any,
obj->gateway,
_ip4_route_weight_normalize(n_nexthops, obj->weight, FALSE));
}
@@ -9117,6 +9123,10 @@ nm_platform_ip4_route_cmp(const NMPlatformIP4Route *a,
if (cmp_type == NM_PLATFORM_IP_ROUTE_CMP_TYPE_ID) {
NM_CMP_FIELD(a, b, ifindex);
NM_CMP_FIELD(a, b, gateway);
+ NM_CMP_FIELD(a, b, via.addr_family);
+ if (a->via.addr_family == AF_INET6) {
+ NM_CMP_FIELD_IN6ADDR(a, b, via.addr.addr6);
+ }
n_nexthops = nm_platform_ip4_route_get_n_nexthops(a);
NM_CMP_DIRECT(n_nexthops, nm_platform_ip4_route_get_n_nexthops(b));
NM_CMP_DIRECT(_ip4_route_weight_normalize(n_nexthops, a->weight, FALSE),
@@ -9142,6 +9152,10 @@ nm_platform_ip4_route_cmp(const NMPlatformIP4Route *a,
NM_CMP_FIELD_UNSAFE(a, b, metric_any);
NM_CMP_FIELD(a, b, metric);
NM_CMP_FIELD(a, b, gateway);
+ NM_CMP_FIELD(a, b, via.addr_family);
+ if (a->via.addr_family == AF_INET6) {
+ NM_CMP_FIELD_IN6ADDR(a, b, via.addr.addr6);
+ }
if (cmp_type == NM_PLATFORM_IP_ROUTE_CMP_TYPE_SEMANTICALLY) {
n_nexthops = nm_platform_ip4_route_get_n_nexthops(a);
NM_CMP_DIRECT(n_nexthops, nm_platform_ip4_route_get_n_nexthops(b));
diff --git a/src/libnm-platform/nm-platform.h b/src/libnm-platform/nm-platform.h
index e4ccb9b1cb..18204e39bc 100644
--- a/src/libnm-platform/nm-platform.h
+++ b/src/libnm-platform/nm-platform.h
@@ -443,6 +443,12 @@ struct _NMPlatformIP4Route {
* the first hop. */
in_addr_t gateway;
+ /* RTA_VIA. Part of the primary key for a route. Allows a gateway for a
+ * route to exist in a different address family.
+ * Only valid if: n_nexthops == 1, gateway == 0, via.family != AF_UNSPEC
+ */
+ NMIPAddrTyped via;
+
/* RTA_PREFSRC (called "src" by iproute2).
*
* pref_src is part of the ID of an IPv4 route. When deleting a route,
@@ -2404,6 +2410,14 @@ nm_platform_ip_route_get_gateway(int addr_family, const NMPlatformIPRoute *route
return &((NMPlatformIP6Route *) route)->gateway;
}
+static inline const NMIPAddrTyped *
+nm_platform_ip4_route_get_via(const NMPlatformIP4Route *route)
+{
+ nm_assert(route);
+
+ return &route->via;
+}
+
static inline gconstpointer
nm_platform_ip_route_get_pref_src(int addr_family, const NMPlatformIPRoute *route)
{
diff --git a/src/libnm-platform/nmp-ethtool.c b/src/libnm-platform/nmp-ethtool.c
index ed028f6a1c..28d12ffb1e 100644
--- a/src/libnm-platform/nmp-ethtool.c
+++ b/src/libnm-platform/nmp-ethtool.c
@@ -153,7 +153,7 @@ ethtool_send_and_recv(struct nl_sock *sock,
out:
if (nle < 0 && err_msg && *err_msg == NULL)
- *err_msg = strdup(nm_strerror(nle));
+ *err_msg = g_strdup(nm_strerror(nle));
if (nle >= 0 && cb_result < 0)
nle = cb_result;
diff --git a/src/libnm-platform/nmp-object.c b/src/libnm-platform/nmp-object.c
index e11bfb3f5b..56533c99c5 100644
--- a/src/libnm-platform/nmp-object.c
+++ b/src/libnm-platform/nmp-object.c
@@ -893,7 +893,7 @@ const NMPObject *
nmp_object_stackinit_id_ip6_address(NMPObject *obj, int ifindex, const struct in6_addr *address)
{
_nmp_object_stackinit_from_type(obj, NMP_OBJECT_TYPE_IP6_ADDRESS);
- obj->ip4_address.ifindex = ifindex;
+ obj->ip6_address.ifindex = ifindex;
if (address)
obj->ip6_address.address = *address;
return obj;
diff --git a/src/libnm-systemd-core/meson.build b/src/libnm-systemd-core/meson.build
index 15e1c8a62b..00d8a77baf 100644
--- a/src/libnm-systemd-core/meson.build
+++ b/src/libnm-systemd-core/meson.build
@@ -12,6 +12,7 @@ libnm_systemd_core = static_library(
'src/libsystemd-network/sd-dhcp6-lease.c',
'src/libsystemd/sd-device/device-private.c',
'src/libsystemd/sd-device/sd-device.c',
+ 'src/libsystemd/sd-device/device-util.c',
'src/libsystemd/sd-event/event-util.c',
'src/libsystemd/sd-event/sd-event.c',
'src/libsystemd/sd-id128/id128-util.c',
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 ecd62ea802..2ef8dc8690 100644
--- a/src/libnm-systemd-core/src/libsystemd-network/dhcp6-internal.h
+++ b/src/libnm-systemd-core/src/libsystemd-network/dhcp6-internal.h
@@ -8,8 +8,8 @@
#include
#include
-#include "sd-event.h"
#include "sd-dhcp6-client.h"
+#include "sd-event.h"
#include "dhcp-duid-internal.h"
#include "dhcp6-client-internal.h"
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 60cd84f2d8..62c3858841 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
@@ -8,10 +8,10 @@
#include
#include "sd-dhcp6-lease.h"
-#include "dns-resolver-internal.h"
#include "dhcp6-option.h"
#include "dhcp6-protocol.h"
+#include "dns-resolver-internal.h"
#include "macro.h"
#include "set.h"
#include "time-util.h"
diff --git a/src/libnm-systemd-core/src/libsystemd-network/dhcp6-network.c b/src/libnm-systemd-core/src/libsystemd-network/dhcp6-network.c
index 0373269288..1c695a8700 100644
--- a/src/libnm-systemd-core/src/libsystemd-network/dhcp6-network.c
+++ b/src/libnm-systemd-core/src/libsystemd-network/dhcp6-network.c
@@ -6,13 +6,13 @@
#include "nm-sd-adapt-core.h"
#include
+#include
#include
#include
#include
#include
#include
#include
-#include
#include "dhcp6-internal.h"
#include "dhcp6-protocol.h"
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 5655c0d747..a7cd6a24b9 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
@@ -6,11 +6,11 @@
#include "nm-sd-adapt-core.h"
#include
-#include
#ifdef __GLIBC__
#include
#endif
#include
+#include
#include "sd-dhcp6-client.h"
@@ -1295,7 +1295,8 @@ static int client_receive_message(
sd_dhcp6_client *client = ASSERT_PTR(userdata);
DHCP6_CLIENT_DONT_DESTROY(client);
- /* This needs to be initialized with zero. See #20741. */
+ /* This needs to be initialized with zero. See #20741.
+ * The issue is fixed on glibc-2.35 (8fba672472ae0055387e9315fc2eddfa6775ca79). */
CMSG_BUFFER_TYPE(CMSG_SPACE_TIMEVAL) control = {};
struct iovec iov;
union sockaddr_union sa = {};
diff --git a/src/libnm-systemd-core/src/libsystemd/sd-device/device-internal.h b/src/libnm-systemd-core/src/libsystemd/sd-device/device-internal.h
index a465eb25fd..cbd89849bd 100644
--- a/src/libnm-systemd-core/src/libsystemd/sd-device/device-internal.h
+++ b/src/libnm-systemd-core/src/libsystemd/sd-device/device-internal.h
@@ -13,6 +13,43 @@
struct sd_device {
unsigned n_ref;
+ /* syspath */
+ char *syspath;
+ const char *devpath;
+ const char *sysnum;
+ char *sysname;
+
+ /* only set when device is passed through netlink */
+ sd_device_action_t action;
+ uint64_t seqnum;
+
+ /* basic kernel properties */
+ char *subsystem;
+ char *driver_subsystem; /* only set for the 'drivers' subsystem */
+ char *driver;
+ char *devtype;
+
+ /* device node properties */
+ char *devname;
+ dev_t devnum;
+ mode_t devmode;
+ uid_t devuid;
+ gid_t devgid;
+
+ /* block device properties */
+ uint64_t diskseq; /* Block device sequence number, monothonically incremented by the kernel on create/attach */
+
+ /* network interface properties */
+ int ifindex;
+
+ /* determined by devnnum, ifindex, subsystem, and sysname */
+ char *device_id;
+
+ /* sysfs attributes */
+ Hashmap *sysattr_values; /* cached sysattr values */
+ Set *sysattrs; /* names of sysattrs */
+ Iterator sysattrs_iterator;
+
/* The database version indicates the supported features by the udev database.
* This is saved and parsed in V field.
*
@@ -21,68 +58,38 @@ struct sd_device {
*/
unsigned database_version;
- sd_device *parent;
+ /* when device is initialized by udevd */
+ usec_t usec_initialized;
- OrderedHashmap *properties;
+ /* properties */
+ OrderedHashmap *properties; /* all properties set from uevent and by udevd */
Iterator properties_iterator;
uint64_t properties_generation; /* changes whenever the properties are changed */
uint64_t properties_iterator_generation; /* generation when iteration was started */
+ OrderedHashmap *properties_db; /* the subset of the properties that should be written to the db */
+ char **properties_strv; /* the properties hashmap as a strv */
+ char *properties_nulstr; /* the same as a nulstr */
+ size_t properties_nulstr_len;
- /* the subset of the properties that should be written to the db */
- OrderedHashmap *properties_db;
-
- Hashmap *sysattr_values; /* cached sysattr values */
-
- Set *sysattrs; /* names of sysattrs */
- Iterator sysattrs_iterator;
-
+ /* TAG keyword */
Set *all_tags, *current_tags;
Iterator all_tags_iterator, current_tags_iterator;
uint64_t all_tags_iterator_generation, current_tags_iterator_generation; /* generation when iteration was started */
uint64_t tags_generation; /* changes whenever the tags are changed */
+ /* SYMLINK keyword */
Set *devlinks;
Iterator devlinks_iterator;
uint64_t devlinks_generation; /* changes whenever the devlinks are changed */
uint64_t devlinks_iterator_generation; /* generation when iteration was started */
int devlink_priority;
+ /* parent and child devices */
+ sd_device *parent;
Hashmap *children;
Iterator children_iterator;
bool children_enumerated;
- int ifindex;
- char *devtype;
- char *devname;
- dev_t devnum;
-
- char **properties_strv; /* the properties hashmap as a strv */
- char *properties_nulstr; /* the same as a nulstr */
- size_t properties_nulstr_len;
-
- char *syspath;
- const char *devpath;
- const char *sysnum;
- char *sysname;
-
- char *subsystem;
- char *driver_subsystem; /* only set for the 'drivers' subsystem */
- char *driver;
-
- char *device_id;
-
- usec_t usec_initialized;
-
- mode_t devmode;
- uid_t devuid;
- gid_t devgid;
-
- uint64_t diskseq; /* Block device sequence number, monothonically incremented by the kernel on create/attach */
-
- /* only set when device is passed through netlink */
- sd_device_action_t action;
- uint64_t seqnum;
-
bool parent_set:1; /* no need to try to reload parent */
bool sysattrs_read:1; /* don't try to re-read sysattrs once read */
bool property_tags_outdated:1; /* need to update TAGS= or CURRENT_TAGS= property */
@@ -92,7 +99,6 @@ struct sd_device {
bool driver_set:1; /* don't reread driver */
bool uevent_loaded:1; /* don't reread uevent */
bool db_loaded; /* don't reread db */
-
bool is_initialized:1;
bool sealed:1; /* don't read more information from uevent/db */
bool db_persist:1; /* don't clean up the db when switching from initrd to real root */
@@ -106,6 +112,8 @@ static inline int device_add_property_internal(sd_device *device, const char *ke
int device_set_syspath(sd_device *device, const char *_syspath, bool verify);
int device_set_ifindex(sd_device *device, const char *ifindex);
+int device_set_devuid(sd_device *device, const char *uid);
+int device_set_devgid(sd_device *device, const char *gid);
int device_set_devmode(sd_device *device, const char *devmode);
int device_set_devname(sd_device *device, const char *devname);
int device_set_devtype(sd_device *device, const char *devtype);
diff --git a/src/libnm-systemd-core/src/libsystemd/sd-device/device-private.c b/src/libnm-systemd-core/src/libsystemd/sd-device/device-private.c
index 6b17e7724a..aa73184d1b 100644
--- a/src/libnm-systemd-core/src/libsystemd/sd-device/device-private.c
+++ b/src/libnm-systemd-core/src/libsystemd/sd-device/device-private.c
@@ -119,6 +119,10 @@ int device_get_devnode_mode(sd_device *device, mode_t *ret) {
assert(device);
+ r = device_read_uevent_file(device);
+ if (r < 0)
+ return r;
+
r = device_read_db(device);
if (r < 0)
return r;
@@ -137,6 +141,10 @@ int device_get_devnode_uid(sd_device *device, uid_t *ret) {
assert(device);
+ r = device_read_uevent_file(device);
+ if (r < 0)
+ return r;
+
r = device_read_db(device);
if (r < 0)
return r;
@@ -150,7 +158,9 @@ int device_get_devnode_uid(sd_device *device, uid_t *ret) {
return 0;
}
-static int device_set_devuid(sd_device *device, const char *uid) {
+#endif /* NM_IGNORED */
+
+int device_set_devuid(sd_device *device, const char *uid) {
uid_t u;
int r;
@@ -175,6 +185,10 @@ int device_get_devnode_gid(sd_device *device, gid_t *ret) {
assert(device);
+ r = device_read_uevent_file(device);
+ if (r < 0)
+ return r;
+
r = device_read_db(device);
if (r < 0)
return r;
@@ -188,7 +202,7 @@ int device_get_devnode_gid(sd_device *device, gid_t *ret) {
return 0;
}
-static int device_set_devgid(sd_device *device, const char *gid) {
+int device_set_devgid(sd_device *device, const char *gid) {
gid_t g;
int r;
@@ -208,6 +222,8 @@ static int device_set_devgid(sd_device *device, const char *gid) {
return 0;
}
+#if 0 /* NM_IGNORED */
+
int device_set_action(sd_device *device, sd_device_action_t a) {
int r;
@@ -432,10 +448,11 @@ static int device_verify(sd_device *device) {
return log_device_debug_errno(device, SYNTHETIC_ERRNO(EINVAL),
"sd-device: Device created from strv or nulstr lacks devpath, subsystem, action or seqnum.");
- if (streq(device->subsystem, "drivers")) {
+ if (device_in_subsystem(device, "drivers")) {
r = device_set_drivers_subsystem(device);
if (r < 0)
- return r;
+ return log_device_debug_errno(device, r,
+ "sd-device: Failed to set driver subsystem: %m");
}
device->sealed = true;
@@ -682,8 +699,8 @@ int device_clone_with_db(sd_device *device, sd_device **ret) {
void device_cleanup_tags(sd_device *device) {
assert(device);
- device->all_tags = set_free_free(device->all_tags);
- device->current_tags = set_free_free(device->current_tags);
+ device->all_tags = set_free(device->all_tags);
+ device->current_tags = set_free(device->current_tags);
device->property_tags_outdated = true;
device->tags_generation++;
}
@@ -691,7 +708,7 @@ void device_cleanup_tags(sd_device *device) {
void device_cleanup_devlinks(sd_device *device) {
assert(device);
- set_free_free(device->devlinks);
+ set_free(device->devlinks);
device->devlinks = NULL;
device->property_devlinks_outdated = true;
device->devlinks_generation++;
@@ -955,8 +972,4 @@ static const char* const device_action_table[_SD_DEVICE_ACTION_MAX] = {
};
DEFINE_STRING_TABLE_LOOKUP(device_action, sd_device_action_t);
-
-void dump_device_action_table(void) {
- DUMP_STRING_TABLE(device_action, sd_device_action_t, _SD_DEVICE_ACTION_MAX);
-}
#endif /* NM_IGNORED */
diff --git a/src/libnm-systemd-core/src/libsystemd/sd-device/device-private.h b/src/libnm-systemd-core/src/libsystemd/sd-device/device-private.h
index eab54203f0..e07ca6026b 100644
--- a/src/libnm-systemd-core/src/libsystemd/sd-device/device-private.h
+++ b/src/libnm-systemd-core/src/libsystemd/sd-device/device-private.h
@@ -9,6 +9,7 @@
#include "sd-device.h"
+#include "chase.h"
#include "macro.h"
int device_new_from_mode_and_devnum(sd_device **ret, mode_t mode, dev_t devnum);
@@ -17,8 +18,10 @@ int device_new_from_strv(sd_device **ret, char **strv);
int device_opendir(sd_device *device, const char *subdir, DIR **ret);
+int device_get_sysnum_unsigned(sd_device *device, unsigned *ret);
int device_get_property_bool(sd_device *device, const char *key);
int device_get_property_int(sd_device *device, const char *key, int *ret);
+int device_get_ifname(sd_device *device, const char **ret);
int device_get_sysattr_int(sd_device *device, const char *sysattr, int *ret_value);
int device_get_sysattr_unsigned_full(sd_device *device, const char *sysattr, unsigned base, unsigned *ret_value);
static inline int device_get_sysattr_unsigned(sd_device *device, const char *sysattr, unsigned *ret_value) {
@@ -31,9 +34,11 @@ int device_get_devnode_mode(sd_device *device, mode_t *ret);
int device_get_devnode_uid(sd_device *device, uid_t *ret);
int device_get_devnode_gid(sd_device *device, gid_t *ret);
+#if 0 /* NM_IGNORED */
+int device_chase(sd_device *device, const char *path, ChaseFlags flags, char **ret_resolved, int *ret_fd);
void device_clear_sysattr_cache(sd_device *device);
-int device_cache_sysattr_value(sd_device *device, const char *key, char *value);
-int device_get_cached_sysattr_value(sd_device *device, const char *key, const char **ret_value);
+int device_cache_sysattr_value(sd_device *device, char *key, char *value, int error);
+#endif /* NM_IGNORED */
void device_seal(sd_device *device);
void device_set_is_initialized(sd_device *device);
@@ -76,4 +81,3 @@ int device_read_uevent_file(sd_device *device);
int device_set_action(sd_device *device, sd_device_action_t a);
sd_device_action_t device_action_from_string(const char *s) _pure_;
const char* device_action_to_string(sd_device_action_t a) _const_;
-void dump_device_action_table(void);
diff --git a/src/libnm-systemd-core/src/libsystemd/sd-device/device-util.c b/src/libnm-systemd-core/src/libsystemd/sd-device/device-util.c
new file mode 100644
index 0000000000..e289523f72
--- /dev/null
+++ b/src/libnm-systemd-core/src/libsystemd/sd-device/device-util.c
@@ -0,0 +1,156 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+
+#include "nm-sd-adapt-shared.h"
+
+#include "device-private.h"
+#include "device-util.h"
+#include "devnum-util.h"
+#include "fd-util.h"
+#include "string-util.h"
+#include "strv.h"
+
+#if 0 /* NM_IGNORED */
+int devname_from_devnum(mode_t mode, dev_t devnum, char **ret) {
+ _cleanup_(sd_device_unrefp) sd_device *dev = NULL;
+ const char *devname;
+ int r;
+
+ assert(ret);
+
+ if (devnum_is_zero(devnum))
+ return device_path_make_inaccessible(mode, ret);
+
+ r = device_new_from_mode_and_devnum(&dev, mode, devnum);
+ if (r < 0)
+ return r;
+
+ r = sd_device_get_devname(dev, &devname);
+ if (r < 0)
+ return r;
+
+ return strdup_to(ret, devname);
+}
+
+int device_open_from_devnum(mode_t mode, dev_t devnum, int flags, char **ret_devname) {
+ _cleanup_(sd_device_unrefp) sd_device *dev = NULL;
+ _cleanup_close_ int fd = -EBADF;
+ int r;
+
+ r = device_new_from_mode_and_devnum(&dev, mode, devnum);
+ if (r < 0)
+ return r;
+
+ fd = sd_device_open(dev, flags);
+ if (fd < 0)
+ return fd;
+
+ if (ret_devname) {
+ const char *devname;
+
+ r = sd_device_get_devname(dev, &devname);
+ if (r < 0)
+ return r;
+
+ r = strdup_to(ret_devname, devname);
+ if (r < 0)
+ return r;
+ }
+
+ return TAKE_FD(fd);
+}
+
+static int add_string_field(
+ sd_device *device,
+ const char *field,
+ int (*func)(sd_device *dev, const char **s),
+ char ***strv) {
+
+ const char *s;
+ int r;
+
+ assert(device);
+ assert(field);
+ assert(func);
+ assert(strv);
+
+ r = func(device, &s);
+ if (r < 0 && r != -ENOENT)
+ log_device_debug_errno(device, r, "Failed to get device \"%s\" property, ignoring: %m", field);
+ if (r >= 0)
+ (void) strv_extend_assignment(strv, field, s);
+
+ return 0;
+}
+
+char** device_make_log_fields(sd_device *device) {
+ _cleanup_strv_free_ char **strv = NULL;
+ dev_t devnum;
+ int ifindex;
+ sd_device_action_t action;
+ uint64_t seqnum, diskseq;
+ int r;
+
+ assert(device);
+
+ (void) add_string_field(device, "SYSPATH", sd_device_get_syspath, &strv);
+ (void) add_string_field(device, "SUBSYSTEM", sd_device_get_subsystem, &strv);
+ (void) add_string_field(device, "DEVTYPE", sd_device_get_devtype, &strv);
+ (void) add_string_field(device, "DRIVER", sd_device_get_driver, &strv);
+ (void) add_string_field(device, "DEVPATH", sd_device_get_devpath, &strv);
+ (void) add_string_field(device, "DEVNAME", sd_device_get_devname, &strv);
+ (void) add_string_field(device, "SYSNAME", sd_device_get_sysname, &strv);
+ (void) add_string_field(device, "SYSNUM", sd_device_get_sysnum, &strv);
+
+ r = sd_device_get_devnum(device, &devnum);
+ if (r < 0 && r != -ENOENT)
+ log_device_debug_errno(device, r, "Failed to get device \"DEVNUM\" property, ignoring: %m");
+ if (r >= 0)
+ (void) strv_extendf(&strv, "DEVNUM="DEVNUM_FORMAT_STR, DEVNUM_FORMAT_VAL(devnum));
+
+ r = sd_device_get_ifindex(device, &ifindex);
+ if (r < 0 && r != -ENOENT)
+ log_device_debug_errno(device, r, "Failed to get device \"IFINDEX\" property, ignoring: %m");
+ if (r >= 0)
+ (void) strv_extendf(&strv, "IFINDEX=%i", ifindex);
+
+ r = sd_device_get_action(device, &action);
+ if (r < 0 && r != -ENOENT)
+ log_device_debug_errno(device, r, "Failed to get device \"ACTION\" property, ignoring: %m");
+ if (r >= 0)
+ (void) strv_extendf(&strv, "ACTION=%s", device_action_to_string(action));
+
+ r = sd_device_get_seqnum(device, &seqnum);
+ if (r < 0 && r != -ENOENT)
+ log_device_debug_errno(device, r, "Failed to get device \"SEQNUM\" property, ignoring: %m");
+ if (r >= 0)
+ (void) strv_extendf(&strv, "SEQNUM=%"PRIu64, seqnum);
+
+ r = sd_device_get_diskseq(device, &diskseq);
+ if (r < 0 && r != -ENOENT)
+ log_device_debug_errno(device, r, "Failed to get device \"DISKSEQ\" property, ignoring: %m");
+ if (r >= 0)
+ (void) strv_extendf(&strv, "DISKSEQ=%"PRIu64, diskseq);
+
+ return TAKE_PTR(strv);
+}
+#endif /* NM_IGNORED */
+
+bool device_in_subsystem(sd_device *device, const char *subsystem) {
+ const char *s = NULL;
+
+ assert(device);
+
+ (void) sd_device_get_subsystem(device, &s);
+ return streq_ptr(s, subsystem);
+}
+
+#if 0 /* NM_IGNORED */
+bool device_is_devtype(sd_device *device, const char *devtype) {
+ const char *s = NULL;
+
+ assert(device);
+
+ (void) sd_device_get_devtype(device, &s);
+ return streq_ptr(s, devtype);
+}
+#endif /* NM_IGNORED */
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 b17993d554..070e564a52 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
@@ -110,6 +110,19 @@ 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");
+ /* basic properties set by kernel, only in netlink event */
+ "ACTION", "SEQNUM", "SYNTH_UUID",
+ /* basic properties set by kernel, both in netlink event and uevent file */
+ "DEVPATH", "DEVPATH_OLD", "SUBSYSTEM", "DEVTYPE", "DRIVER", "MODALIAS",
+ /* device node */
+ "DEVNAME", "DEVMODE", "DEVUID", "DEVGID", "MAJOR", "MINOR",
+ /* block device */
+ "DISKSEQ", "PARTN",
+ /* network interface (INTERFACE_OLD is set by udevd) */
+ "IFINDEX", "INTERFACE", "INTERFACE_OLD",
+ /* basic properties set by udevd */
+ "DEVLINKS", "TAGS", "CURRENT_TAGS", "USEC_INITIALIZED", "UDEV_DATABASE_VERSION") &&
+ /* Similar to SYNTH_UUID, but set based on KEY=VALUE arguments passed by userspace.
+ * See kernel's f36776fafbaa0094390dd4e7e3e29805e0b82730 (v4.13) */
+ !startswith(property, "SYNTH_ARG_");
}
diff --git a/src/libnm-systemd-core/src/libsystemd/sd-device/sd-device.c b/src/libnm-systemd-core/src/libsystemd/sd-device/sd-device.c
index 480223d08c..fce184df0d 100644
--- a/src/libnm-systemd-core/src/libsystemd/sd-device/sd-device.c
+++ b/src/libnm-systemd-core/src/libsystemd/sd-device/sd-device.c
@@ -375,7 +375,7 @@ _public_ int sd_device_new_from_ifindex(sd_device **ret, int ifindex) {
assert_return(ret, -EINVAL);
assert_return(ifindex > 0, -EINVAL);
- r = rtnl_get_ifname_full(NULL, ifindex, &ifname, NULL);
+ r = rtnl_get_ifname(NULL, ifindex, &ifname);
if (r < 0)
return r;
@@ -516,7 +516,7 @@ _public_ int sd_device_new_from_subsystem_sysname(
if (streq(sep, "drivers")) /* If the sysname is "drivers", then it's the drivers directory itself that is meant. */
r = device_new_from_path_join(&device, subsystem, subsys, "drivers", "/sys/bus/", subsys, "/drivers", NULL);
else
- r = device_new_from_path_join(&device, subsystem, subsys, sep, "/sys/bus/", subsys, "/drivers/", sep);
+ r = device_new_from_path_join(&device, subsystem, subsys, sysname + (sep - name), "/sys/bus/", subsys, "/drivers/", sep);
if (r < 0)
return r;
}
@@ -776,16 +776,24 @@ static int handle_uevent_line(
assert(major);
assert(minor);
+ if (streq(key, "SUBSYSTEM"))
+ return device_set_subsystem(device, value);
if (streq(key, "DEVTYPE"))
return device_set_devtype(device, value);
if (streq(key, "IFINDEX"))
return device_set_ifindex(device, value);
if (streq(key, "DEVNAME"))
return device_set_devname(device, value);
+ if (streq(key, "DEVUID"))
+ return device_set_devuid(device, value);
+ if (streq(key, "DEVGID"))
+ return device_set_devgid(device, value);
if (streq(key, "DEVMODE"))
return device_set_devmode(device, value);
if (streq(key, "DISKSEQ"))
return device_set_diskseq(device, value);
+ if (streq(key, "DRIVER"))
+ return device_set_driver(device, value);
if (streq(key, "MAJOR"))
*major = value;
else if (streq(key, "MINOR"))
@@ -800,7 +808,7 @@ int device_read_uevent_file(sd_device *device) {
_cleanup_free_ char *uevent = NULL;
const char *syspath, *key = NULL, *value = NULL, *major = NULL, *minor = NULL;
char *path;
- size_t uevent_len = 0;
+ size_t uevent_len;
int r;
enum {
@@ -882,6 +890,13 @@ int device_read_uevent_file(sd_device *device) {
log_device_debug_errno(device, r, "sd-device: Failed to set 'MAJOR=%s' or 'MINOR=%s' from '%s', ignoring: %m", major, strna(minor), path);
}
+ if (device_in_subsystem(device, "drivers")) {
+ r = device_set_drivers_subsystem(device);
+ if (r < 0)
+ log_device_debug_errno(device, r,
+ "sd-device: Failed to set driver subsystem, ignoring: %m");
+ }
+
return 0;
}
@@ -904,6 +919,21 @@ _public_ int sd_device_get_ifindex(sd_device *device, int *ifindex) {
}
#if 0 /* NM_IGNORED */
+int device_get_ifname(sd_device *device, const char **ret) {
+ int r;
+
+ assert_return(device, -EINVAL);
+
+ /* First, check if the device is a network interface. */
+ r = sd_device_get_ifindex(device, NULL);
+ if (r < 0)
+ return r;
+
+ /* The sysname and ifname may be different, as '!' in sysname are replaced with '/'.
+ * For network interfaces, we can use INTERFACE property. */
+ return sd_device_get_property_value(device, "INTERFACE", ret);
+}
+
_public_ int sd_device_new_from_device_id(sd_device **ret, const char *id) {
int r;
@@ -1228,6 +1258,10 @@ _public_ int sd_device_get_subsystem(sd_device *device, const char **ret) {
assert_return(device, -EINVAL);
+ r = device_read_uevent_file(device);
+ if (r < 0)
+ return r;
+
if (!device->subsystem_set) {
_cleanup_free_ char *subsystem = NULL;
const char *syspath;
@@ -1371,6 +1405,10 @@ int device_set_driver(sd_device *device, const char *driver) {
_public_ int sd_device_get_driver(sd_device *device, const char **ret) {
assert_return(device, -EINVAL);
+ r = device_read_uevent_file(device);
+ if (r < 0)
+ return r;
+
if (!device->driver_set) {
_cleanup_free_ char *driver = NULL;
const char *syspath;
@@ -1497,6 +1535,26 @@ _public_ int sd_device_get_sysnum(sd_device *device, const char **ret) {
return 0;
}
+int device_get_sysnum_unsigned(sd_device *device, unsigned *ret) {
+ int r;
+
+ assert(device);
+
+ const char *s;
+ r = sd_device_get_sysnum(device, &s);
+ if (r < 0)
+ return r;
+
+ unsigned n;
+ r = safe_atou_full(s, SAFE_ATO_REFUSE_PLUS_MINUS | SAFE_ATO_REFUSE_LEADING_WHITESPACE | 10, &n);
+ if (r < 0)
+ return r;
+
+ if (ret)
+ *ret = n;
+ return 0;
+}
+
_public_ int sd_device_get_action(sd_device *device, sd_device_action_t *ret) {
assert_return(device, -EINVAL);
@@ -1726,18 +1784,13 @@ _public_ int sd_device_get_device_id(sd_device *device, const char **ret) {
if (!device->device_id) {
_cleanup_free_ char *id = NULL;
- const char *subsystem;
dev_t devnum;
int ifindex, r;
- r = sd_device_get_subsystem(device, &subsystem);
- if (r < 0)
- return r;
-
if (sd_device_get_devnum(device, &devnum) >= 0) {
/* use dev_t — b259:131072, c254:0 */
if (asprintf(&id, "%c" DEVNUM_FORMAT_STR,
- streq(subsystem, "block") ? 'b' : 'c',
+ device_in_subsystem(device, "block") ? 'b' : 'c',
DEVNUM_FORMAT_VAL(devnum)) < 0)
return -ENOMEM;
} else if (sd_device_get_ifindex(device, &ifindex) >= 0) {
@@ -1755,13 +1808,18 @@ _public_ int sd_device_get_device_id(sd_device *device, const char **ret) {
if (r == O_DIRECTORY)
return -EINVAL;
- if (streq(subsystem, "drivers")) {
+ if (device_in_subsystem(device, "drivers"))
/* the 'drivers' pseudo-subsystem is special, and needs the real
* subsystem encoded as well */
- assert(device->driver_subsystem);
- id = strjoin("+drivers:", device->driver_subsystem, ":", sysname);
- } else
+ id = strjoin("+drivers:", ASSERT_PTR(device->driver_subsystem), ":", sysname);
+ else {
+ const char *subsystem;
+ r = sd_device_get_subsystem(device, &subsystem);
+ if (r < 0)
+ return r;
+
id = strjoin("+", subsystem, ":", sysname);
+ }
if (!id)
return -ENOMEM;
}
@@ -2355,134 +2413,224 @@ void device_clear_sysattr_cache(sd_device *device) {
device->sysattr_values = hashmap_free(device->sysattr_values);
}
-int device_cache_sysattr_value(sd_device *device, const char *key, char *value) {
- _unused_ _cleanup_free_ char *old_value = NULL;
- _cleanup_free_ char *new_key = NULL;
+typedef struct SysAttrCacheEntry {
+ char *key;
+ char *value;
+ int error;
+} SysAttrCacheEntry;
+
+static SysAttrCacheEntry* sysattr_cache_entry_free(SysAttrCacheEntry *p) {
+ if (!p)
+ return NULL;
+
+ free(p->key);
+ free(p->value);
+ return mfree(p);
+}
+
+DEFINE_PRIVATE_HASH_OPS_WITH_VALUE_DESTRUCTOR(
+ sysattr_cache_hash_ops,
+ char, path_hash_func, path_compare,
+ SysAttrCacheEntry, sysattr_cache_entry_free);
+
+static int device_cache_sysattr_value_full(sd_device *device, char *key, char *value, int error, bool ignore_uevent) {
int r;
assert(device);
assert(key);
+ assert(value || error > 0);
- /* This takes the reference of the input value. The input value may be NULL.
- * This replaces the value if it already exists. */
+ /* This takes the reference of the input arguments when cached, hence the caller must not free them
+ * when a positive return value is returned. The input value may be NULL. This replaces an already
+ * existing entry. */
- /* First, remove the old cache entry. So, we do not need to clear cache on error. */
- old_value = hashmap_remove2(device->sysattr_values, key, (void **) &new_key);
- if (!new_key) {
- new_key = strdup(key);
- if (!new_key)
- return -ENOMEM;
- }
+ if (ignore_uevent && streq(last_path_component(key), "uevent"))
+ return 0; /* not cached */
- r = hashmap_ensure_put(&device->sysattr_values, &path_hash_ops_free_free, new_key, value);
+ /* Remove the old cache entry. So, we do not need to clear cache on error. */
+ sysattr_cache_entry_free(hashmap_remove(device->sysattr_values, key));
+
+ /* We use ENOANO as a recognizable error code when we have not read the attribute. */
+ if (error == ENOANO)
+ error = ESTALE;
+
+ _cleanup_free_ SysAttrCacheEntry *entry = new(SysAttrCacheEntry, 1);
+ if (!entry)
+ return -ENOMEM;
+
+ *entry = (SysAttrCacheEntry) {
+ .key = key,
+ .value = value,
+ .error = error,
+ };
+
+ r = hashmap_ensure_put(&device->sysattr_values, &sysattr_cache_hash_ops, entry->key, entry);
if (r < 0)
return r;
- TAKE_PTR(new_key);
-
- return 0;
+ TAKE_PTR(entry);
+ return 1; /* cached */
}
-int device_get_cached_sysattr_value(sd_device *device, const char *key, const char **ret_value) {
- const char *k = NULL, *value;
+int device_cache_sysattr_value(sd_device *device, char *key, char *value, int error) {
+ return device_cache_sysattr_value_full(device, key, value, error, /* ignore_uevent = */ true);
+}
+
+static int device_get_cached_sysattr_value(sd_device *device, const char *key, const char **ret_value) {
+ SysAttrCacheEntry *entry;
assert(device);
assert(key);
- value = hashmap_get2(device->sysattr_values, key, (void **) &k);
- if (!k)
- return -ESTALE; /* We have not read the attribute. */
- if (!value)
- return -ENOENT; /* We have looked up the attribute before and it did not exist. */
+ entry = hashmap_get(device->sysattr_values, key);
+ if (!entry)
+ return -ENOANO; /* We have not read the attribute. */
+ if (!entry->value) {
+ /* We have looked up the attribute before and failed. Return the cached error code. */
+ assert(entry->error > 0);
+ return -entry->error;
+ }
if (ret_value)
- *ret_value = value;
+ *ret_value = entry->value;
return 0;
}
-/* We cache all sysattr lookups. If an attribute does not exist, it is stored
- * with a NULL value in the cache, otherwise the returned string is stored */
-_public_ int sd_device_get_sysattr_value(sd_device *device, const char *sysattr, const char **ret_value) {
- _cleanup_free_ char *value = NULL, *path = NULL;
+int device_chase(sd_device *device, const char *path, ChaseFlags flags, char **ret_resolved, int *ret_fd) {
+ int r;
+
+ assert(device);
+ assert(path);
+
const char *syspath;
- struct stat statbuf;
+ r = sd_device_get_syspath(device, &syspath);
+ if (r < 0)
+ return r;
+
+ /* Here, CHASE_PREFIX_ROOT is borrowed. If the flag is set or the specified path is relative, then
+ * the path will be prefixed with the syspath. Note, we do not pass CHASE_PREFIX_ROOT flag with
+ * syspath as root to chase(), but we manually concatenate the specified path with syspath before
+ * calling chase(). Otherwise, we cannot set/get attributes of parent or sibling devices. */
+ _cleanup_free_ char *prefixed = NULL;
+ if (FLAGS_SET(flags, CHASE_PREFIX_ROOT) || !path_is_absolute(path)) {
+ prefixed = path_join(syspath, path);
+ if (!prefixed)
+ return -ENOMEM;
+ path = prefixed;
+ flags &= ~CHASE_PREFIX_ROOT;
+ }
+
+ _cleanup_free_ char *resolved = NULL;
+ _cleanup_close_ int fd = -EBADF;
+ r = chase(path, /* root = */ NULL, CHASE_NO_AUTOFS | flags, &resolved, ret_fd ? &fd : NULL);
+ if (r < 0)
+ return r;
+
+ /* Refuse to reading/writing files outside of sysfs. */
+ if (!path_startswith(resolved, "/sys/"))
+ return -EINVAL;
+
+ if (ret_resolved) {
+ /* Always return relative path. */
+ r = path_make_relative(syspath, resolved, ret_resolved);
+ if (r < 0)
+ return r;
+ }
+
+ if (ret_fd)
+ *ret_fd = TAKE_FD(fd);
+
+ return 0;
+}
+
+_public_ int sd_device_get_sysattr_value(sd_device *device, const char *sysattr, const char **ret_value) {
+ _cleanup_free_ char *resolved = NULL, *value = NULL;
+ _cleanup_close_ int fd = -EBADF;
int r;
assert_return(device, -EINVAL);
assert_return(sysattr, -EINVAL);
- /* look for possibly already cached result */
+ /* Look for possibly already cached result. */
r = device_get_cached_sysattr_value(device, sysattr, ret_value);
- if (r != -ESTALE)
+ if (r != -ENOANO)
return r;
- r = sd_device_get_syspath(device, &syspath);
- if (r < 0)
- return r;
+ /* Special cases: read the symlink and return the last component of the value. Some core links return
+ * only the last element of the target path, these are just values, the paths should not be exposed. */
+ if (STR_IN_SET(sysattr, "driver", "subsystem", "module")) {
+ _cleanup_free_ char *prefixed = NULL;
+ const char *syspath;
- path = path_join(syspath, sysattr);
- if (!path)
- return -ENOMEM;
-
- if (lstat(path, &statbuf) < 0) {
- int k;
-
- r = -errno;
-
- /* remember that we could not access the sysattr */
- k = device_cache_sysattr_value(device, sysattr, NULL);
- if (k < 0)
- log_device_debug_errno(device, k,
- "sd-device: failed to cache attribute '%s' with NULL, ignoring: %m",
- sysattr);
-
- return r;
- } else if (S_ISLNK(statbuf.st_mode)) {
- /* Some core links return only the last element of the target path,
- * these are just values, the paths should not be exposed. */
- if (STR_IN_SET(sysattr, "driver", "subsystem", "module")) {
- r = readlink_value(path, &value);
- if (r < 0)
- return r;
- } else
- return -EINVAL;
- } else if (S_ISDIR(statbuf.st_mode))
- /* skip directories */
- return -EISDIR;
- else if (!(statbuf.st_mode & S_IRUSR))
- /* skip non-readable files */
- return -EPERM;
- else {
- size_t size;
-
- /* Read attribute value, Some attributes contain embedded '\0'. So, it is necessary to
- * also get the size of the result. See issue #20025. */
- r = read_full_virtual_file(path, &value, &size);
+ r = sd_device_get_syspath(device, &syspath);
if (r < 0)
return r;
- /* drop trailing newlines */
- while (size > 0 && strchr(NEWLINE, value[--size]))
- value[size] = '\0';
+ prefixed = path_join(syspath, sysattr);
+ if (!prefixed)
+ return -ENOMEM;
+
+ r = readlink_value(prefixed, &value);
+ if (r != -EINVAL) /* -EINVAL means the path is not a symlink. */
+ goto cache_result;
}
- /* Unfortunately, we need to return 'const char*' instead of 'char*'. Hence, failure in caching
- * sysattr value is critical unlike the other places. */
- r = device_cache_sysattr_value(device, sysattr, value);
- if (r < 0) {
- log_device_debug_errno(device, r,
- "sd-device: failed to cache attribute '%s' with '%s'%s: %m",
- sysattr, value, ret_value ? "" : ", ignoring");
- if (ret_value)
- return r;
+ r = device_chase(device, sysattr, CHASE_PREFIX_ROOT, &resolved, &fd);
+ if (r < 0)
+ goto cache_result;
- return 0;
+ /* Look for cached result again with the resolved path. */
+ r = device_get_cached_sysattr_value(device, resolved, ret_value);
+ if (r != -ENOANO)
+ return r;
+
+ /* Read attribute value, Some attributes contain embedded '\0'. So, it is necessary to also get the
+ * size of the result. See issue #20025. */
+ size_t size;
+ r = read_virtual_file_fd(fd, SIZE_MAX, &value, &size);
+ if (r < 0)
+ goto cache_result;
+
+ delete_trailing_chars(value, NEWLINE);
+ r = 0;
+
+cache_result:
+ if (r == -ENOMEM)
+ return r; /* Do not cache -ENOMEM, as the failure may be transient. */
+
+ if (!resolved) {
+ /* If we have not or could not chase the path, assume 'sysattr' is normalized. */
+ resolved = strdup(sysattr);
+ if (!resolved)
+ return RET_GATHER(r, -ENOMEM);
}
- if (ret_value)
+ int k = device_cache_sysattr_value_full(device, resolved, value, -r, /* ignore_uevent = */ false);
+ if (k < 0) {
+ if (r < 0)
+ log_device_debug_errno(device, k,
+ "sd-device: failed to cache error code (%i) in reading attribute '%s', ignoring: %m",
+ -r, resolved);
+ else {
+ /* Unfortunately, we need to return 'const char*' instead of 'char*'. Hence, failure in caching
+ * sysattr value is critical unlike the other places. */
+ log_device_debug_errno(device, k,
+ "sd-device: failed to cache attribute '%s' with '%s'%s: %m",
+ resolved, value, ret_value ? "" : ", ignoring");
+ if (ret_value)
+ return k;
+ }
+
+ return r;
+ }
+ assert(k > 0);
+
+ if (ret_value && r >= 0)
*ret_value = value;
+ /* device_cache_sysattr_value_full() takes 'resolved' and 'value' on success. */
+ TAKE_PTR(resolved);
TAKE_PTR(value);
- return 0;
+ return r;
}
int device_get_sysattr_int(sd_device *device, const char *sysattr, int *ret_value) {
@@ -2556,19 +2704,22 @@ int device_get_sysattr_bool(sd_device *device, const char *sysattr) {
return parse_boolean(value);
}
-static void device_remove_cached_sysattr_value(sd_device *device, const char *_key) {
- _cleanup_free_ char *key = NULL;
+static int device_remove_cached_sysattr_value(sd_device *device, const char *sysattr) {
+ int r;
assert(device);
- assert(_key);
+ assert(sysattr);
- free(hashmap_remove2(device->sysattr_values, _key, (void **) &key));
+ _cleanup_free_ char *resolved = NULL;
+ r = device_chase(device, sysattr, CHASE_PREFIX_ROOT | CHASE_NONEXISTENT, &resolved, /* ret_fd = */ NULL);
+ if (r < 0)
+ return r;
+
+ sysattr_cache_entry_free(hashmap_remove(device->sysattr_values, resolved));
+ return 0;
}
-_public_ int sd_device_set_sysattr_value(sd_device *device, const char *sysattr, const char *_value) {
- _cleanup_free_ char *value = NULL, *path = NULL;
- const char *syspath;
- size_t len;
+_public_ int sd_device_set_sysattr_value(sd_device *device, const char *sysattr, const char *value) {
int r;
assert_return(device, -EINVAL);
@@ -2576,52 +2727,43 @@ _public_ int sd_device_set_sysattr_value(sd_device *device, const char *sysattr,
/* Set the attribute and save it in the cache. */
- if (!_value) {
+ if (!value)
/* If input value is NULL, then clear cache and not write anything. */
- device_remove_cached_sysattr_value(device, sysattr);
- return 0;
- }
+ return device_remove_cached_sysattr_value(device, sysattr);
- r = sd_device_get_syspath(device, &syspath);
- if (r < 0)
+ _cleanup_free_ char *resolved = NULL;
+ _cleanup_close_ int fd = -EBADF;
+ r = device_chase(device, sysattr, CHASE_PREFIX_ROOT, &resolved, &fd);
+ if (r < 0) {
+ /* On failure, clear cache entry, hopefully, 'sysattr' is normalized. */
+ sysattr_cache_entry_free(hashmap_remove(device->sysattr_values, sysattr));
return r;
-
- path = path_join(syspath, sysattr);
- if (!path)
- return -ENOMEM;
-
- len = strlen(_value);
-
- /* drop trailing newlines */
- while (len > 0 && strchr(NEWLINE, _value[len - 1]))
- len--;
+ }
/* value length is limited to 4k */
- if (len > 4096)
- return -EINVAL;
-
- value = strndup(_value, len);
- if (!value)
+ _cleanup_free_ char *copied = strndup(value, 4096);
+ if (!copied)
return -ENOMEM;
- r = write_string_file(path, value, WRITE_STRING_FILE_DISABLE_BUFFER | WRITE_STRING_FILE_NOFOLLOW);
+ /* drop trailing newlines */
+ delete_trailing_chars(copied, NEWLINE);
+
+ r = write_string_file_fd(fd, copied, WRITE_STRING_FILE_DISABLE_BUFFER | WRITE_STRING_FILE_AVOID_NEWLINE);
if (r < 0) {
/* On failure, clear cache entry, as we do not know how it fails. */
- device_remove_cached_sysattr_value(device, sysattr);
+ sysattr_cache_entry_free(hashmap_remove(device->sysattr_values, resolved));
return r;
}
- /* Do not cache action string written into uevent file. */
- if (streq(sysattr, "uevent"))
- return 0;
-
- r = device_cache_sysattr_value(device, sysattr, value);
+ r = device_cache_sysattr_value(device, resolved, copied, 0);
if (r < 0)
log_device_debug_errno(device, r,
- "sd-device: failed to cache attribute '%s' with '%s', ignoring: %m",
- sysattr, value);
- else
- TAKE_PTR(value);
+ "sd-device: failed to cache written attribute '%s' with '%s', ignoring: %m",
+ resolved, copied);
+ else if (r > 0) {
+ TAKE_PTR(resolved);
+ TAKE_PTR(copied);
+ }
return 0;
}
@@ -2634,10 +2776,8 @@ _public_ int sd_device_set_sysattr_valuef(sd_device *device, const char *sysattr
assert_return(device, -EINVAL);
assert_return(sysattr, -EINVAL);
- if (!format) {
- device_remove_cached_sysattr_value(device, sysattr);
- return 0;
- }
+ if (!format)
+ return device_remove_cached_sysattr_value(device, sysattr);
va_start(ap, format);
r = vasprintf(&value, format, ap);
@@ -2650,16 +2790,7 @@ _public_ int sd_device_set_sysattr_valuef(sd_device *device, const char *sysattr
}
_public_ int sd_device_trigger(sd_device *device, sd_device_action_t action) {
- const char *s;
-
- assert_return(device, -EINVAL);
-
- s = device_action_to_string(action);
- if (!s)
- return -EINVAL;
-
- /* This uses the simple no-UUID interface of kernel < 4.13 */
- return sd_device_set_sysattr_value(device, "uevent", s);
+ return sd_device_trigger_with_uuid(device, action, NULL);
}
_public_ int sd_device_trigger_with_uuid(
@@ -2673,10 +2804,6 @@ _public_ int sd_device_trigger_with_uuid(
assert_return(device, -EINVAL);
- /* If no one wants to know the UUID, use the simple interface from pre-4.13 times */
- if (!ret_uuid)
- return sd_device_trigger(device, action);
-
s = device_action_to_string(action);
if (!s)
return -EINVAL;
@@ -2691,7 +2818,8 @@ _public_ int sd_device_trigger_with_uuid(
if (r < 0)
return r;
- *ret_uuid = u;
+ if (ret_uuid)
+ *ret_uuid = u;
return 0;
}
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 ac986e4897..f8c374b95e 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
@@ -4,12 +4,21 @@
#include
+#include "errno-util.h"
#include "event-source.h"
#include "event-util.h"
#include "fd-util.h"
#include "log.h"
#include "string-util.h"
+#define SI_FLAG_FORWARD (INT32_C(1) << 30)
+#define SI_FLAG_POSITIVE (INT32_C(1) << 29)
+
+DEFINE_HASH_OPS_WITH_VALUE_DESTRUCTOR(
+ event_source_hash_ops,
+ void, trivial_hash_func, trivial_compare_func,
+ sd_event_source, sd_event_source_disable_unref);
+
int event_reset_time(
sd_event *e,
sd_event_source **s,
@@ -157,19 +166,73 @@ int event_add_time_change(sd_event *e, sd_event_source **ret, sd_event_io_handle
int event_add_child_pidref(
sd_event *e,
- sd_event_source **s,
+ sd_event_source **ret,
const PidRef *pid,
int options,
sd_event_child_handler_t callback,
void *userdata) {
+ int r;
+
+ assert(e);
+
if (!pidref_is_set(pid))
return -ESRCH;
- if (pid->fd >= 0)
- return sd_event_add_child_pidfd(e, s, pid->fd, options, callback, userdata);
+ if (pidref_is_remote(pid))
+ return -EREMOTE;
- return sd_event_add_child(e, s, pid->pid, options, callback, userdata);
+ if (pid->fd < 0)
+ return sd_event_add_child(e, ret, pid->pid, options, callback, userdata);
+
+ _cleanup_close_ int copy_fd = fcntl(pid->fd, F_DUPFD_CLOEXEC, 3);
+ if (copy_fd < 0)
+ return -errno;
+
+ _cleanup_(sd_event_source_unrefp) sd_event_source *s = NULL;
+ r = sd_event_add_child_pidfd(e, &s, copy_fd, options, callback, userdata);
+ if (r < 0)
+ return r;
+
+ r = sd_event_source_set_child_pidfd_own(s, true);
+ if (r < 0)
+ return r;
+
+ TAKE_FD(copy_fd);
+
+ if (ret)
+ *ret = TAKE_PTR(s);
+ else {
+ r = sd_event_source_set_floating(s, true);
+ if (r < 0)
+ return r;
+ }
+
+ return 0;
+}
+
+int event_source_get_child_pidref(sd_event_source *s, PidRef *ret) {
+ int r;
+
+ assert(s);
+ assert(ret);
+
+ pid_t pid;
+ r = sd_event_source_get_child_pid(s, &pid);
+ if (r < 0)
+ return r;
+
+ int pidfd = sd_event_source_get_child_pidfd(s);
+ if (pidfd < 0)
+ return pidfd;
+
+ /* Note, we don't actually duplicate the fd here, i.e. we do not pass ownership of this PidRef to the caller */
+ *ret = (PidRef) {
+ .pid = pid,
+ .fd = pidfd,
+ };
+
+ return 0;
}
dual_timestamp* event_dual_timestamp_now(sd_event *e, dual_timestamp *ts) {
@@ -180,4 +243,91 @@ dual_timestamp* event_dual_timestamp_now(sd_event *e, dual_timestamp *ts) {
assert_se(sd_event_now(e, CLOCK_MONOTONIC, &ts->monotonic) >= 0);
return ts;
}
+
+void event_source_unref_many(sd_event_source **array, size_t n) {
+ FOREACH_ARRAY(v, array, n)
+ sd_event_source_unref(*v);
+
+ free(array);
+}
+
+static int event_forward_signal_callback(sd_event_source *s, const struct signalfd_siginfo *ssi, void *userdata) {
+ sd_event_source *child = ASSERT_PTR(userdata);
+
+ assert(ssi);
+
+ siginfo_t si = {
+ .si_signo = ssi->ssi_signo,
+ /* We include some extra information to indicate the signal was forwarded and originally a positive
+ * value since we can only set negative values ourselves as positive values are prohibited by the
+ * kernel. */
+ .si_code = (ssi->ssi_code & (SI_FLAG_FORWARD|SI_FLAG_POSITIVE)) ? INT_MIN :
+ (ssi->ssi_code >= 0 ? (-ssi->ssi_code - 1) | SI_FLAG_POSITIVE | SI_FLAG_FORWARD : ssi->ssi_code | SI_FLAG_FORWARD),
+ .si_errno = ssi->ssi_errno,
+ };
+
+ /* The following fields are implemented as macros, hence we cannot use compound initialization for them. */
+ si.si_pid = ssi->ssi_pid;
+ si.si_uid = ssi->ssi_uid;
+ si.si_int = ssi->ssi_int;
+ si.si_ptr = UINT64_TO_PTR(ssi->ssi_ptr);
+
+ return sd_event_source_send_child_signal(child, ssi->ssi_signo, &si, /* flags = */ 0);
+}
+
+static void event_forward_signal_destroy(void *userdata) {
+ sd_event_source *child = ASSERT_PTR(userdata);
+ sd_event_source_unref(child);
+}
+
+int event_forward_signals(
+ sd_event *e,
+ sd_event_source *child,
+ const int *signals,
+ size_t n_signals,
+ sd_event_source ***ret_sources,
+ size_t *ret_n_sources) {
+
+ sd_event_source **sources = NULL;
+ size_t n_sources = 0;
+ int r;
+
+ CLEANUP_ARRAY(sources, n_sources, event_source_unref_many);
+
+ assert(e);
+ assert(child);
+ assert(child->type == SOURCE_CHILD);
+ assert(signals || n_signals == 0);
+ assert(ret_sources);
+ assert(ret_n_sources);
+
+ if (n_signals == 0) {
+ *ret_sources = NULL;
+ *ret_n_sources = 0;
+ return 0;
+ }
+
+ sources = new0(sd_event_source*, n_signals);
+ if (!sources)
+ return -ENOMEM;
+
+ FOREACH_ARRAY(sig, signals, n_signals) {
+ _cleanup_(sd_event_source_unrefp) sd_event_source *s = NULL;
+ r = sd_event_add_signal(e, &s, *sig | SD_EVENT_SIGNAL_PROCMASK, event_forward_signal_callback, child);
+ if (r < 0)
+ return r;
+
+ r = sd_event_source_set_destroy_callback(s, event_forward_signal_destroy);
+ if (r < 0)
+ return r;
+
+ sd_event_source_ref(child);
+ sources[n_sources++] = TAKE_PTR(s);
+ }
+
+ *ret_sources = TAKE_PTR(sources);
+ *ret_n_sources = n_sources;
+
+ return 0;
+}
#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 c0db014f8f..692184db3f 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,8 +5,11 @@
#include "sd-event.h"
+#include "hash-funcs.h"
#include "pidref.h"
+extern const struct hash_ops event_source_hash_ops;
+
int event_reset_time(
sd_event *e,
sd_event_source **s,
@@ -38,5 +41,11 @@ int event_add_time_change(sd_event *e, sd_event_source **ret, sd_event_io_handle
#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);
+int event_source_get_child_pidref(sd_event_source *s, PidRef *ret);
+
dual_timestamp* event_dual_timestamp_now(sd_event *e, dual_timestamp *ts);
+
+void event_source_unref_many(sd_event_source **array, size_t n);
+
+int event_forward_signals(sd_event *e, sd_event_source *child, const int *signals, size_t n_signals, sd_event_source ***ret_sources, size_t *ret_n_sources);
#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 b345b14522..4f04e05835 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
@@ -5,6 +5,7 @@
#include
#include
#include
+#include
#include "sd-daemon.h"
#include "sd-event.h"
@@ -26,12 +27,11 @@
#include "memory-util.h"
#include "missing_magic.h"
#include "missing_syscall.h"
-#include "missing_threads.h"
#include "missing_wait.h"
#include "origin-id.h"
#include "path-util.h"
-#include "prioq.h"
#include "pidfd-util.h"
+#include "prioq.h"
#include "process-util.h"
#include "psi-util.h"
#include "set.h"
@@ -45,11 +45,10 @@
#define DEFAULT_ACCURACY_USEC (250 * USEC_PER_MSEC)
-static bool EVENT_SOURCE_WATCH_PIDFD(sd_event_source *s) {
+static bool EVENT_SOURCE_WATCH_PIDFD(const sd_event_source *s) {
/* Returns true if this is a PID event source and can be implemented by watching EPOLLIN */
return s &&
s->type == SOURCE_CHILD &&
- s->child.pidfd >= 0 &&
s->child.options == WEXITED;
}
@@ -434,7 +433,7 @@ _public_ int sd_event_new(sd_event** ret) {
if (secure_getenv("SD_EVENT_PROFILE_DELAYS")) {
log_debug("Event loop profiling enabled. Logarithmic histogram of event loop iterations in the range 2^0 %s 2^63 us will be logged every 5s.",
- special_glyph(SPECIAL_GLYPH_ELLIPSIS));
+ glyph(GLYPH_ELLIPSIS));
e->profile_delays = true;
}
@@ -992,7 +991,7 @@ static void source_disconnect(sd_event_source *s) {
s->event->n_online_child_sources--;
}
- (void) hashmap_remove(s->event->child_sources, PID_TO_PTR(s->child.pid));
+ assert_se(hashmap_remove(s->event->child_sources, PID_TO_PTR(s->child.pid)));
}
if (EVENT_SOURCE_WATCH_PIDFD(s))
@@ -1092,12 +1091,11 @@ static sd_event_source* source_free(sd_event_source *s) {
/* Eventually the kernel will do this automatically for us, but for now let's emulate this (unreliably) in userspace. */
if (s->child.process_owned) {
+ assert(s->child.pid > 0);
+ assert(s->child.pidfd >= 0);
if (!s->child.exited) {
- if (s->child.pidfd >= 0)
- r = RET_NERRNO(pidfd_send_signal(s->child.pidfd, SIGKILL, NULL, 0));
- else
- r = RET_NERRNO(kill(s->child.pid, SIGKILL));
+ r = RET_NERRNO(pidfd_send_signal(s->child.pidfd, SIGKILL, NULL, 0));
if (r < 0 && r != -ESRCH)
log_debug_errno(r, "Failed to kill process " PID_FMT ", ignoring: %m",
s->child.pid);
@@ -1107,10 +1105,7 @@ static sd_event_source* source_free(sd_event_source *s) {
siginfo_t si = {};
/* Reap the child if we can */
- if (s->child.pidfd >= 0)
- (void) waitid(P_PIDFD, s->child.pidfd, &si, WEXITED);
- else
- (void) waitid(P_PID, s->child.pid, &si, WEXITED);
+ (void) waitid(P_PIDFD, s->child.pidfd, &si, WEXITED);
}
}
@@ -1903,15 +1898,15 @@ _public_ int sd_event_trim_memory(void) {
LOG_MESSAGE("Memory trimming took %s, returned %s to OS.",
FORMAT_TIMESPAN(period, 0),
FORMAT_BYTES(l)),
- "MESSAGE_ID=" SD_MESSAGE_MEMORY_TRIM_STR,
- "TRIMMED_BYTES=%zu", l,
- "TRIMMED_USEC=" USEC_FMT, period);
+ LOG_MESSAGE_ID(SD_MESSAGE_MEMORY_TRIM_STR),
+ LOG_ITEM("TRIMMED_BYTES=%zu", l),
+ LOG_ITEM("TRIMMED_USEC=" USEC_FMT, period));
#else
log_struct(LOG_DEBUG,
LOG_MESSAGE("Memory trimming took %s.",
FORMAT_TIMESPAN(period, 0)),
- "MESSAGE_ID=" SD_MESSAGE_MEMORY_TRIM_STR,
- "TRIMMED_USEC=" USEC_FMT, period);
+ LOG_MESSAGE_ID(SD_MESSAGE_MEMORY_TRIM_STR),
+ LOG_ITEM("TRIMMED_USEC=" USEC_FMT, period));
#endif
return 0;
@@ -2740,9 +2735,11 @@ _public_ int sd_event_source_get_io_revents(sd_event_source *s, uint32_t *ret) {
assert_return(s, -EINVAL);
assert_return(ret, -EINVAL);
assert_return(s->type == SOURCE_IO, -EDOM);
- assert_return(s->pending, -ENODATA);
assert_return(!event_origin_changed(s->event), -ECHILD);
+ if (!s->pending)
+ return -ENODATA;
+
*ret = s->io.revents;
return 0;
}
@@ -3009,13 +3006,13 @@ static int event_source_online(
case SOURCE_CHILD:
if (EVENT_SOURCE_WATCH_PIDFD(s)) {
- /* yes, we have pidfd */
+ /* yes, we can rely on pidfd */
r = source_child_pidfd_register(s, enabled);
if (r < 0)
return r;
} else {
- /* no pidfd, or something other to watch for than WEXITED */
+ /* something other to watch for than WEXITED */
r = event_make_signal_data(s->event, SIGCHLD, NULL);
if (r < 0) {
@@ -3208,9 +3205,6 @@ _public_ int sd_event_source_get_child_pidfd(sd_event_source *s) {
assert_return(s->type == SOURCE_CHILD, -EDOM);
assert_return(!event_origin_changed(s->event), -ECHILD);
- if (s->child.pidfd < 0)
- return -EOPNOTSUPP;
-
return s->child.pidfd;
}
@@ -3219,51 +3213,26 @@ _public_ int sd_event_source_send_child_signal(sd_event_source *s, int sig, cons
assert_return(s->type == SOURCE_CHILD, -EDOM);
assert_return(!event_origin_changed(s->event), -ECHILD);
assert_return(SIGNAL_VALID(sig), -EINVAL);
+ assert(s->child.pidfd >= 0);
- /* If we already have seen indication the process exited refuse sending a signal early. This way we
- * can be sure we don't accidentally kill the wrong process on PID reuse when pidfds are not
- * available. */
+ /* If we already have seen indication the process exited refuse sending a signal early. */
if (s->child.exited)
return -ESRCH;
+ assert(!s->child.waited);
- if (s->child.pidfd >= 0) {
- siginfo_t copy;
+ /* pidfd_send_signal() changes the siginfo_t argument. This is weird, let's hence copy the structure here. */
+ siginfo_t copy;
+ if (si)
+ copy = *si;
- /* pidfd_send_signal() changes the siginfo_t argument. This is weird, let's hence copy the
- * structure here */
- if (si)
- copy = *si;
-
- if (pidfd_send_signal(s->child.pidfd, sig, si ? © : NULL, 0) < 0)
- return -errno;
-
- return 0;
- }
-
- /* Flags are only supported for pidfd_send_signal(), not for rt_sigqueueinfo(), hence let's refuse
- * this here. */
- if (flags != 0)
- return -EOPNOTSUPP;
-
- if (si) {
- /* We use rt_sigqueueinfo() only if siginfo_t is specified. */
- siginfo_t copy = *si;
-
- if (rt_sigqueueinfo(s->child.pid, sig, ©) < 0)
- return -errno;
- } else if (kill(s->child.pid, sig) < 0)
- return -errno;
-
- return 0;
+ return RET_NERRNO(pidfd_send_signal(s->child.pidfd, sig, si ? © : NULL, flags));
}
_public_ int sd_event_source_get_child_pidfd_own(sd_event_source *s) {
assert_return(s, -EINVAL);
assert_return(s->type == SOURCE_CHILD, -EDOM);
assert_return(!event_origin_changed(s->event), -ECHILD);
-
- if (s->child.pidfd < 0)
- return -EOPNOTSUPP;
+ assert(s->child.pidfd >= 0);
return s->child.pidfd_owned;
}
@@ -3272,9 +3241,7 @@ _public_ int sd_event_source_set_child_pidfd_own(sd_event_source *s, int own) {
assert_return(s, -EINVAL);
assert_return(s->type == SOURCE_CHILD, -EDOM);
assert_return(!event_origin_changed(s->event), -ECHILD);
-
- if (s->child.pidfd < 0)
- return -EOPNOTSUPP;
+ assert(s->child.pidfd >= 0);
s->child.pidfd_owned = own;
return 0;
@@ -3733,9 +3700,9 @@ static int process_child(sd_event *e, int64_t threshold, int64_t *ret_min_priori
e->need_process_child = false;
- /* So, this is ugly. We iteratively invoke waitid() with P_PID + WNOHANG for each PID we wait
- * for, instead of using P_ALL. This is because we only want to get child information of very
- * specific child processes, and not all of them. We might not have processed the SIGCHLD event
+ /* So, this is ugly. We iteratively invoke waitid() + WNOHANG with each child process we shall wait for,
+ * instead of using P_ALL. This is because we only want to get child information of very specific
+ * child processes, and not all of them. We might not have processed the SIGCHLD event
* of a previous invocation and we don't want to maintain a unbounded *per-child* event queue,
* hence we really don't want anything flushed out of the kernel's queue that we don't care
* about. Since this is O(n) this means that if you have a lot of processes you probably want
@@ -3746,6 +3713,7 @@ static int process_child(sd_event *e, int64_t threshold, int64_t *ret_min_priori
HASHMAP_FOREACH(s, e->child_sources) {
assert(s->type == SOURCE_CHILD);
+ assert(s->child.pidfd >= 0);
if (s->priority > threshold)
continue;
@@ -3765,23 +3733,21 @@ static int process_child(sd_event *e, int64_t threshold, int64_t *ret_min_priori
continue;
zero(s->child.siginfo);
- if (waitid(P_PID, s->child.pid, &s->child.siginfo,
+ if (waitid(P_PIDFD, s->child.pidfd, &s->child.siginfo,
WNOHANG | (s->child.options & WEXITED ? WNOWAIT : 0) | s->child.options) < 0)
return negative_errno();
if (s->child.siginfo.si_pid != 0) {
- bool zombie = IN_SET(s->child.siginfo.si_code, CLD_EXITED, CLD_KILLED, CLD_DUMPED);
+ bool zombie = SIGINFO_CODE_IS_DEAD(s->child.siginfo.si_code);
if (zombie)
s->child.exited = true;
-
- if (!zombie && (s->child.options & WEXITED)) {
- /* If the child isn't dead then let's immediately remove the state
- * change from the queue, since there's no benefit in leaving it
- * queued. */
+ else if (s->child.options & WEXITED) {
+ /* If the child isn't dead then let's immediately remove the state change
+ * from the queue, since there's no benefit in leaving it queued. */
assert(s->child.options & (WSTOPPED|WCONTINUED));
- (void) waitid(P_PID, s->child.pid, &s->child.siginfo, WNOHANG|(s->child.options & (WSTOPPED|WCONTINUED)));
+ (void) waitid(P_PIDFD, s->child.pidfd, &s->child.siginfo, WNOHANG|(s->child.options & (WSTOPPED|WCONTINUED)));
}
r = source_set_pending(s, true);
@@ -3802,6 +3768,7 @@ static int process_pidfd(sd_event *e, sd_event_source *s, uint32_t revents) {
assert(e);
assert(s);
assert(s->type == SOURCE_CHILD);
+ assert(s->child.pidfd >= 0);
if (s->pending)
return 0;
@@ -3812,14 +3779,19 @@ static int process_pidfd(sd_event *e, sd_event_source *s, uint32_t revents) {
if (!EVENT_SOURCE_WATCH_PIDFD(s))
return 0;
+ /* Note that pidfd would also generate EPOLLHUP when the process gets reaped. But at this point we
+ * only permit EPOLLIN, under the assumption that upon EPOLLHUP the child source should already
+ * be set to pending, and we would have returned early above. */
+ assert(!s->child.exited);
+
zero(s->child.siginfo);
- if (waitid(P_PID, s->child.pid, &s->child.siginfo, WNOHANG | WNOWAIT | s->child.options) < 0)
+ if (waitid(P_PIDFD, s->child.pidfd, &s->child.siginfo, WNOHANG | WNOWAIT | s->child.options) < 0)
return -errno;
if (s->child.siginfo.si_pid == 0)
return 0;
- if (IN_SET(s->child.siginfo.si_code, CLD_EXITED, CLD_KILLED, CLD_DUMPED))
+ if (SIGINFO_CODE_IS_DEAD(s->child.siginfo.si_code))
s->child.exited = true;
return source_set_pending(s, true);
@@ -4232,15 +4204,13 @@ static int source_dispatch(sd_event_source *s) {
break;
case SOURCE_CHILD: {
- bool zombie;
-
- zombie = IN_SET(s->child.siginfo.si_code, CLD_EXITED, CLD_KILLED, CLD_DUMPED);
+ bool zombie = SIGINFO_CODE_IS_DEAD(s->child.siginfo.si_code);
r = s->child.callback(s, &s->child.siginfo, s->userdata);
/* Now, reap the PID for good. */
if (zombie) {
- (void) waitid(P_PID, s->child.pid, &s->child.siginfo, WNOHANG|WEXITED);
+ (void) waitid(P_PIDFD, s->child.pidfd, &s->child.siginfo, WNOHANG|WEXITED);
s->child.waited = true;
}
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 6d515a94db..406e6f2269 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,6 +11,7 @@
#include "hexdecoct.h"
#include "id128-util.h"
#include "io-util.h"
+#include "log.h"
#include "namespace-util.h"
#include "process-util.h"
#include "sha256.h"
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 9f6ef719c6..8f3cd8c452 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
@@ -4,6 +4,7 @@
#include
#include
+#include
#include
#include "sd-id128.h"
@@ -16,16 +17,16 @@
#include "id128-util.h"
#include "io-util.h"
#include "keyring-util.h"
+#include "log.h"
#include "macro.h"
#include "missing_syscall.h"
-#include "missing_threads.h"
#include "path-util.h"
#include "random-util.h"
#include "stat-util.h"
#include "user-util.h"
#if 0 /* NM_IGNORED */
-_public_ char *sd_id128_to_string(sd_id128_t id, char s[_SD_ARRAY_STATIC SD_ID128_STRING_MAX]) {
+_public_ char *sd_id128_to_string(sd_id128_t id, char s[static SD_ID128_STRING_MAX]) {
size_t k = 0;
assert_return(s, NULL);
@@ -41,7 +42,7 @@ _public_ char *sd_id128_to_string(sd_id128_t id, char s[_SD_ARRAY_STATIC SD_ID12
return s;
}
-_public_ char *sd_id128_to_uuid_string(sd_id128_t id, char s[_SD_ARRAY_STATIC SD_ID128_UUID_STRING_MAX]) {
+_public_ char *sd_id128_to_uuid_string(sd_id128_t id, char s[static SD_ID128_UUID_STRING_MAX]) {
size_t k = 0;
assert_return(s, NULL);
@@ -221,8 +222,10 @@ static int get_invocation_from_keyring(sd_id128_t *ret) {
key = request_key("user", "invocation_id", NULL, 0);
if (key == -1) {
- /* Keyring support not available? No invocation key stored? */
- if (IN_SET(errno, ENOSYS, ENOKEY))
+ /* Keyring support not available? Keyring access locked down? No invocation key stored? */
+ if (ERRNO_IS_NOT_SUPPORTED(errno) ||
+ ERRNO_IS_PRIVILEGE(errno) ||
+ errno == ENOKEY)
return -ENXIO;
return -errno;
diff --git a/src/libnm-systemd-core/src/systemd/_sd-common.h b/src/libnm-systemd-core/src/systemd/_sd-common.h
index 5792dd8106..00537eaf16 100644
--- a/src/libnm-systemd-core/src/systemd/_sd-common.h
+++ b/src/libnm-systemd-core/src/systemd/_sd-common.h
@@ -19,7 +19,7 @@
/* This is a private header; never even think of including this directly! */
-#if defined(__INCLUDE_LEVEL__) && __INCLUDE_LEVEL__ <= 1 && !defined(__COVERITY__)
+#if defined(__INCLUDE_LEVEL__) && __INCLUDE_LEVEL__ <= 1 && !defined(__COVERITY__) && !defined(__clang_analyzer__)
# error "Do not include _sd-common.h directly; it is a private header."
#endif
diff --git a/src/libnm-systemd-core/src/systemd/sd-device.h b/src/libnm-systemd-core/src/systemd/sd-device.h
index f627ae6dae..8f6141b3df 100644
--- a/src/libnm-systemd-core/src/systemd/sd-device.h
+++ b/src/libnm-systemd-core/src/systemd/sd-device.h
@@ -23,11 +23,10 @@
#include
#include
+#include "_sd-common.h"
#include "sd-event.h"
#include "sd-id128.h"
-#include "_sd-common.h"
-
_SD_BEGIN_DECLARATIONS;
typedef struct sd_device sd_device;
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 d551b4dd90..6054dc4432 100644
--- a/src/libnm-systemd-core/src/systemd/sd-dhcp6-client.h
+++ b/src/libnm-systemd-core/src/systemd/sd-dhcp6-client.h
@@ -23,14 +23,13 @@
#include
#include
+#include "_sd-common.h"
#include "sd-device.h"
#include "sd-dhcp-duid.h"
#include "sd-dhcp6-lease.h"
#include "sd-dhcp6-option.h"
#include "sd-event.h"
-#include "_sd-common.h"
-
_SD_BEGIN_DECLARATIONS;
enum {
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 d6bcceb2a2..5d082b0cbe 100644
--- a/src/libnm-systemd-core/src/systemd/sd-dhcp6-lease.h
+++ b/src/libnm-systemd-core/src/systemd/sd-dhcp6-lease.h
@@ -23,9 +23,8 @@
#include
#include
-#include "sd-dhcp6-option.h"
-
#include "_sd-common.h"
+#include "sd-dhcp6-option.h"
_SD_BEGIN_DECLARATIONS;
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 320124266a..69f3eaa39d 100644
--- a/src/libnm-systemd-core/src/systemd/sd-dhcp6-option.h
+++ b/src/libnm-systemd-core/src/systemd/sd-dhcp6-option.h
@@ -20,9 +20,8 @@
#include
#include
-#include "sd-dhcp6-protocol.h"
-
#include "_sd-common.h"
+#include "sd-dhcp6-protocol.h"
_SD_BEGIN_DECLARATIONS;
diff --git a/src/libnm-systemd-core/src/systemd/sd-ndisc.h b/src/libnm-systemd-core/src/systemd/sd-ndisc.h
index 85fcf6bc03..a8f8c47b68 100644
--- a/src/libnm-systemd-core/src/systemd/sd-ndisc.h
+++ b/src/libnm-systemd-core/src/systemd/sd-ndisc.h
@@ -25,14 +25,13 @@
#include
#include
+#include "_sd-common.h"
#include "sd-event.h"
#include "sd-ndisc-neighbor.h"
#include "sd-ndisc-protocol.h"
#include "sd-ndisc-redirect.h"
#include "sd-ndisc-router.h"
-#include "_sd-common.h"
-
_SD_BEGIN_DECLARATIONS;
typedef struct sd_ndisc sd_ndisc;
diff --git a/src/libnm-systemd-shared/meson.build b/src/libnm-systemd-shared/meson.build
index 7a88d78d96..2719954647 100644
--- a/src/libnm-systemd-shared/meson.build
+++ b/src/libnm-systemd-shared/meson.build
@@ -30,6 +30,7 @@ libnm_systemd_shared = static_library(
'src/basic/locale-util.c',
'src/basic/memory-util.c',
'src/basic/mempool.c',
+ 'src/basic/mountpoint-util.c',
'src/basic/ordered-set.c',
'src/basic/parse-util.c',
'src/basic/path-util.c',
@@ -49,6 +50,7 @@ libnm_systemd_shared = static_library(
'src/basic/time-util.c',
'src/basic/tmpfile-util.c',
'src/basic/utf8.c',
+ 'src/basic/user-util.c',
'src/fundamental/sha256-fundamental.c',
'src/fundamental/string-util-fundamental.c',
'src/shared/dns-domain.c',
diff --git a/src/libnm-systemd-shared/src/basic/alloc-util.c b/src/libnm-systemd-shared/src/basic/alloc-util.c
index f968666676..a2c0caa165 100644
--- a/src/libnm-systemd-shared/src/basic/alloc-util.c
+++ b/src/libnm-systemd-shared/src/basic/alloc-util.c
@@ -8,7 +8,6 @@
#include "alloc-util.h"
#include "macro.h"
-#include "memory-util.h"
void* memdup(const void *p, size_t l) {
void *ret;
diff --git a/src/libnm-systemd-shared/src/basic/alloc-util.h b/src/libnm-systemd-shared/src/basic/alloc-util.h
index ba71298287..764a364a71 100644
--- a/src/libnm-systemd-shared/src/basic/alloc-util.h
+++ b/src/libnm-systemd-shared/src/basic/alloc-util.h
@@ -7,7 +7,9 @@
#include
#include
+#include "assert-util.h"
#include "macro.h"
+#include "memory-util.h"
#if HAS_FEATURE_MEMORY_SANITIZER
# include
@@ -119,15 +121,6 @@ _malloc_ _alloc_(1, 2) static inline void *malloc_multiply(size_t need, size_t s
return malloc(size * need ?: 1);
}
-#if !HAVE_REALLOCARRAY
-_alloc_(2, 3) static inline void *reallocarray(void *p, size_t need, size_t size) {
- if (size_multiply_overflow(size, need))
- return NULL;
-
- return realloc(p, size * need ?: 1);
-}
-#endif
-
_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;
@@ -275,5 +268,3 @@ _alloc_(2) static inline void *realloc0(void *p, size_t new_size) {
return q;
}
-
-#include "memory-util.h"
diff --git a/src/libnm-systemd-shared/src/basic/assert-util.h b/src/libnm-systemd-shared/src/basic/assert-util.h
new file mode 100644
index 0000000000..308605945a
--- /dev/null
+++ b/src/libnm-systemd-shared/src/basic/assert-util.h
@@ -0,0 +1,48 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+#pragma once
+
+#include "assert-fundamental.h"
+#include "macro.h"
+
+/* Logging for various assertions */
+
+void log_set_assert_return_is_critical(bool b);
+bool log_get_assert_return_is_critical(void) _pure_;
+
+#if 0 /* NM_IGNORED */
+void log_assert_failed_return(const char *text, const char *file, int line, const char *func);
+#else /* NM_IGNORED */
+#define log_assert_failed_return(text, file, line, func) \
+ ({ \
+ log_internal(LOG_DEBUG, \
+ 0, \
+ file, \
+ line, \
+ func, \
+ "Assertion '%s' failed at %s:%u, function %s(). Ignoring.", \
+ text, \
+ file, \
+ line, \
+ func); \
+ g_return_if_fail_warning(G_LOG_DOMAIN, G_STRFUNC, text); \
+ (void) 0; \
+ })
+#endif /* NM_IGNORED */
+
+#define assert_log(expr, message) ((_likely_(expr)) \
+ ? (true) \
+ : (log_assert_failed_return(message, PROJECT_FILE, __LINE__, __func__), false))
+
+#define assert_return(expr, r) \
+ do { \
+ if (!assert_log(expr, #expr)) \
+ return (r); \
+ } while (false)
+
+#define assert_return_errno(expr, r, err) \
+ do { \
+ if (!assert_log(expr, #expr)) { \
+ errno = err; \
+ return (r); \
+ } \
+ } while (false)
diff --git a/src/libnm-systemd-shared/src/basic/bitfield.h b/src/libnm-systemd-shared/src/basic/bitfield.h
index 048e08d753..c6eaaa484f 100644
--- a/src/libnm-systemd-shared/src/basic/bitfield.h
+++ b/src/libnm-systemd-shared/src/basic/bitfield.h
@@ -7,6 +7,7 @@
#define _INDEX_TO_MASK(type, i, uniq) \
({ \
int UNIQ_T(_i, uniq) = (i); \
+ assert(UNIQ_T(_i, uniq) >= 0); \
assert(UNIQ_T(_i, uniq) < (int)sizeof(type) * 8); \
((type)1) << UNIQ_T(_i, uniq); \
})
diff --git a/src/libnm-systemd-shared/src/basic/cgroup-util.h b/src/libnm-systemd-shared/src/basic/cgroup-util.h
index 77294779fe..269d68c5c6 100644
--- a/src/libnm-systemd-shared/src/basic/cgroup-util.h
+++ b/src/libnm-systemd-shared/src/basic/cgroup-util.h
@@ -263,8 +263,7 @@ int cg_get_attribute_as_bool(const char *controller, const char *path, const cha
int cg_get_owner(const char *path, uid_t *ret_uid);
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);
+int cg_get_xattr_malloc(const char *path, const char *name, char **ret, size_t *ret_size);
/* Returns negative on error, and 0 or 1 on success for the bool value */
int cg_get_xattr_bool(const char *path, const char *name);
int cg_remove_xattr(const char *path, const char *name);
@@ -312,10 +311,6 @@ int cg_mask_supported_subtree(const char *root, CGroupMask *ret);
int cg_mask_from_string(const char *s, CGroupMask *ret);
int cg_mask_to_string(CGroupMask mask, char **ret);
-int cg_kernel_controllers(Set **controllers);
-
-bool cg_ns_supported(void);
-bool cg_freezer_supported(void);
bool cg_kill_supported(void);
int cg_all_unified(void);
@@ -329,9 +324,6 @@ static inline int cg_unified(void) {
const char* cgroup_controller_to_string(CGroupController c) _const_;
CGroupController cgroup_controller_from_string(const char *s) _pure_;
-bool is_cgroup_fs(const struct statfs *s);
-bool fd_is_cgroup_fs(int fd);
-
typedef enum ManagedOOMMode {
MANAGED_OOM_AUTO,
MANAGED_OOM_KILL,
@@ -365,4 +357,4 @@ typedef union {
.file_handle.handle_type = FILEID_KERNFS, \
}
-#define CG_FILE_HANDLE_CGROUPID(fh) (*(uint64_t*) (fh).file_handle.f_handle)
+#define CG_FILE_HANDLE_CGROUPID(fh) (*CAST_ALIGN_PTR(uint64_t, (fh).file_handle.f_handle))
diff --git a/src/libnm-systemd-shared/src/basic/chattr-util.c b/src/libnm-systemd-shared/src/basic/chattr-util.c
index 2a63e46d3f..0a27e02313 100644
--- a/src/libnm-systemd-shared/src/basic/chattr-util.c
+++ b/src/libnm-systemd-shared/src/basic/chattr-util.c
@@ -6,13 +6,13 @@
#include
#include
#include
-#include
#include "bitfield.h"
#include "chattr-util.h"
#include "errno-util.h"
#include "fd-util.h"
#include "fs-util.h"
+#include "log.h"
#include "macro.h"
#include "string-util.h"
diff --git a/src/libnm-systemd-shared/src/basic/chattr-util.h b/src/libnm-systemd-shared/src/basic/chattr-util.h
index 1fe38e32b1..472054d57e 100644
--- a/src/libnm-systemd-shared/src/basic/chattr-util.h
+++ b/src/libnm-systemd-shared/src/basic/chattr-util.h
@@ -2,7 +2,6 @@
#pragma once
#include
-#include
#include
#include
@@ -41,14 +40,14 @@ typedef enum ChattrApplyFlags {
} ChattrApplyFlags;
int chattr_full(int dir_fd, const char *path, unsigned value, unsigned mask, unsigned *ret_previous, unsigned *ret_final, ChattrApplyFlags flags);
-static inline int chattr_at(int dir_fd, const char *path, unsigned value, unsigned mask, unsigned *previous) {
- return chattr_full(dir_fd, path, value, mask, previous, NULL, 0);
+static inline int chattr_at(int dir_fd, const char *path, unsigned value, unsigned mask) {
+ return chattr_full(dir_fd, path, value, mask, NULL, NULL, 0);
}
-static inline int chattr_fd(int fd, unsigned value, unsigned mask, unsigned *previous) {
- return chattr_full(fd, NULL, value, mask, previous, NULL, 0);
+static inline int chattr_fd(int fd, unsigned value, unsigned mask) {
+ return chattr_full(fd, NULL, value, mask, NULL, NULL, 0);
}
-static inline int chattr_path(const char *path, unsigned value, unsigned mask, unsigned *previous) {
- return chattr_full(AT_FDCWD, path, value, mask, previous, NULL, 0);
+static inline int chattr_path(const char *path, unsigned value, unsigned mask) {
+ return chattr_full(AT_FDCWD, path, value, mask, NULL, NULL, 0);
}
int read_attr_fd(int fd, unsigned *ret);
diff --git a/src/libnm-systemd-shared/src/basic/devnum-util.h b/src/libnm-systemd-shared/src/basic/devnum-util.h
index e109de9913..0efca56780 100644
--- a/src/libnm-systemd-shared/src/basic/devnum-util.h
+++ b/src/libnm-systemd-shared/src/basic/devnum-util.h
@@ -9,6 +9,9 @@
int parse_devnum(const char *s, dev_t *ret);
+#define DEVNUM_MAJOR_MAX ((UINT32_C(1) << 12) - 1U)
+#define DEVNUM_MINOR_MAX ((UINT32_C(1) << 20) - 1U)
+
/* glibc and the Linux kernel have different ideas about the major/minor size. These calls will check whether the
* specified major is valid by the Linux kernel's standards, not by glibc's. Linux has 20bits of minor, and 12 bits of
* major space. See MINORBITS in linux/kdev_t.h in the kernel sources. (If you wonder why we define _y here, instead of
@@ -18,14 +21,14 @@ int parse_devnum(const char *s, dev_t *ret);
#define DEVICE_MAJOR_VALID(x) \
({ \
typeof(x) _x = (x), _y = 0; \
- _x >= _y && _x < (UINT32_C(1) << 12); \
+ _x >= _y && _x <= DEVNUM_MAJOR_MAX; \
\
})
#define DEVICE_MINOR_VALID(x) \
({ \
typeof(x) _x = (x), _y = 0; \
- _x >= _y && _x < (UINT32_C(1) << 20); \
+ _x >= _y && _x <= DEVNUM_MINOR_MAX; \
})
int device_path_make_major_minor(mode_t mode, dev_t devnum, char **ret);
@@ -54,3 +57,6 @@ static inline char *format_devnum(dev_t d, char buf[static DEVNUM_STR_MAX]) {
static inline bool devnum_is_zero(dev_t d) {
return major(d) == 0 && minor(d) == 0;
}
+
+#define DEVNUM_TO_PTR(u) ((void*) (uintptr_t) (u))
+#define PTR_TO_DEVNUM(p) ((dev_t) ((uintptr_t) (p)))
diff --git a/src/libnm-systemd-shared/src/basic/env-file.c b/src/libnm-systemd-shared/src/basic/env-file.c
index 75b2febf7d..d705765b0d 100644
--- a/src/libnm-systemd-shared/src/basic/env-file.c
+++ b/src/libnm-systemd-shared/src/basic/env-file.c
@@ -9,6 +9,7 @@
#include "fd-util.h"
#include "fileio.h"
#include "fs-util.h"
+#include "log.h"
#include "string-util.h"
#include "strv.h"
#include "tmpfile-util.h"
diff --git a/src/libnm-systemd-shared/src/basic/env-util.c b/src/libnm-systemd-shared/src/basic/env-util.c
index 2fe7332c43..8dd52c09c8 100644
--- a/src/libnm-systemd-shared/src/basic/env-util.c
+++ b/src/libnm-systemd-shared/src/basic/env-util.c
@@ -13,6 +13,7 @@
#include "errno-util.h"
#include "escape.h"
#include "extract-word.h"
+#include "log.h"
#include "macro.h"
#include "parse-util.h"
#include "path-util.h"
@@ -549,7 +550,7 @@ char* strv_env_get_n(char * const *l, const char *name, size_t k, ReplaceEnvFlag
return NULL;
t = strndupa_safe(name, k);
- return getenv(t);
+ return secure_getenv(t);
};
return NULL;
@@ -700,7 +701,7 @@ int replace_env_full(
_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 */
_cleanup_free_ char *s = NULL;
- char ***pu, ***pb, *k;
+ char ***pu, ***pb;
size_t i, len = 0; /* len is initialized to appease gcc */
int nest = 0, r;
@@ -722,33 +723,24 @@ int replace_env_full(
case CURLY:
if (*e == '{') {
- k = strnappend(s, word, e-word-1);
- if (!k)
+ if (!strextendn(&s, word, e-word-1))
return -ENOMEM;
- free_and_replace(s, k);
-
word = e-1;
state = VARIABLE;
nest++;
} else if (*e == '$') {
- k = strnappend(s, word, e-word);
- if (!k)
+ if (!strextendn(&s, word, e-word))
return -ENOMEM;
- free_and_replace(s, k);
-
word = e+1;
state = WORD;
} 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)
+ if (!strextendn(&s, word, e-word-1))
return -ENOMEM;
- free_and_replace(s, k);
-
word = e-1;
state = VARIABLE_RAW;
@@ -1121,7 +1113,7 @@ int getenv_steal_erase(const char *name, char **ret) {
* it from there. Usecase: reading passwords from the env block (which is a bad idea, but useful for
* testing, and given that people are likely going to misuse this, be thorough) */
- e = getenv(name);
+ e = secure_getenv(name);
if (!e) {
if (ret)
*ret = NULL;
@@ -1145,25 +1137,6 @@ 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;
diff --git a/src/libnm-systemd-shared/src/basic/env-util.h b/src/libnm-systemd-shared/src/basic/env-util.h
index 203ed65bd1..52771ecc81 100644
--- a/src/libnm-systemd-shared/src/basic/env-util.h
+++ b/src/libnm-systemd-shared/src/basic/env-util.h
@@ -82,6 +82,4 @@ 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 02572e3bdc..01b4d59e04 100644
--- a/src/libnm-systemd-shared/src/basic/errno-util.h
+++ b/src/libnm-systemd-shared/src/basic/errno-util.h
@@ -5,6 +5,7 @@
#include
#include
+#include "assert-util.h"
#include "macro.h"
/* strerror(3) says that glibc uses a maximum length of 1024 bytes. */
diff --git a/src/libnm-systemd-shared/src/basic/escape.c b/src/libnm-systemd-shared/src/basic/escape.c
index e882aced8c..ff1d817c1b 100644
--- a/src/libnm-systemd-shared/src/basic/escape.c
+++ b/src/libnm-systemd-shared/src/basic/escape.c
@@ -82,10 +82,15 @@ char* cescape_length(const char *s, size_t n) {
const char *f;
char *r, *t;
+ /* Does C style string escaping. May be reversed with cunescape(). */
+
assert(s || n == 0);
- /* Does C style string escaping. May be reversed with
- * cunescape(). */
+ if (n == SIZE_MAX)
+ n = strlen(s);
+
+ if (n > (SIZE_MAX - 1) / 4)
+ return NULL;
r = new(char, n*4 + 1);
if (!r)
@@ -99,12 +104,6 @@ char* cescape_length(const char *s, size_t n) {
return r;
}
-char* cescape(const char *s) {
- assert(s);
-
- return cescape_length(s, strlen(s));
-}
-
int cunescape_one(const char *p, size_t length, char32_t *ret, bool *eight_bit, bool accept_nul) {
int r = 1;
@@ -452,7 +451,7 @@ char* escape_non_printable_full(const char *str, size_t console_width, XEscapeFl
char* octescape(const char *s, size_t len) {
char *buf, *t;
- /* Escapes all chars in bad, in addition to \ and " chars, in \nnn style escaping. */
+ /* Escapes \ and " chars, in \nnn style escaping. */
assert(s || len == 0);
@@ -482,13 +481,19 @@ char* octescape(const char *s, size_t len) {
return buf;
}
-char* decescape(const char *s, const char *bad, size_t len) {
+char* decescape(const char *s, size_t len, const char *bad) {
char *buf, *t;
/* Escapes all chars in bad, in addition to \ and " chars, in \nnn decimal style escaping. */
assert(s || len == 0);
+ if (len == SIZE_MAX)
+ len = strlen(s);
+
+ if (len > (SIZE_MAX - 1) / 4)
+ return NULL;
+
t = buf = new(char, len * 4 + 1);
if (!buf)
return NULL;
diff --git a/src/libnm-systemd-shared/src/basic/escape.h b/src/libnm-systemd-shared/src/basic/escape.h
index 65caf0dbcf..05c27f688e 100644
--- a/src/libnm-systemd-shared/src/basic/escape.h
+++ b/src/libnm-systemd-shared/src/basic/escape.h
@@ -8,7 +8,6 @@
#include
#include "string-util.h"
-#include "missing_type.h"
/* What characters are special in the shell? */
/* must be escaped outside and inside double-quotes */
@@ -41,9 +40,11 @@ typedef enum ShellEscapeFlags {
SHELL_ESCAPE_EMPTY = 1 << 2, /* Format empty arguments as "". */
} ShellEscapeFlags;
-char* cescape(const char *s);
-char* cescape_length(const char *s, size_t n);
int cescape_char(char c, char *buf);
+char* cescape_length(const char *s, size_t n) _nonnull_if_nonzero_(1, 2);
+static inline char* cescape(const char *s) {
+ return cescape_length(s, SIZE_MAX);
+}
int cunescape_one(const char *p, size_t length, char32_t *ret, bool *eight_bit, bool accept_nul);
@@ -65,7 +66,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* decescape(const char *s, size_t len, const char *bad) _nonnull_if_nonzero_(1, 2);
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 2643c9749a..168fd32937 100644
--- a/src/libnm-systemd-shared/src/basic/ether-addr-util.c
+++ b/src/libnm-systemd-shared/src/basic/ether-addr-util.c
@@ -10,6 +10,7 @@
#include "ether-addr-util.h"
#include "hexdecoct.h"
+#include "log.h"
#include "macro.h"
#include "string-util.h"
@@ -87,22 +88,6 @@ char* ether_addr_to_string(const struct ether_addr *addr, char buffer[ETHER_ADDR
return buffer;
}
-int ether_addr_to_string_alloc(const struct ether_addr *addr, char **ret) {
- char *buf;
-
- assert(addr);
- assert(ret);
-
- buf = new(char, ETHER_ADDR_TO_STRING_MAX);
- if (!buf)
- return -ENOMEM;
-
- ether_addr_to_string(addr, buf);
-
- *ret = buf;
- return 0;
-}
-
int ether_addr_compare(const struct ether_addr *a, const struct ether_addr *b) {
return memcmp(a, b, ETH_ALEN);
}
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 8ebf9c031d..168c6500dc 100644
--- a/src/libnm-systemd-shared/src/basic/ether-addr-util.h
+++ b/src/libnm-systemd-shared/src/basic/ether-addr-util.h
@@ -72,7 +72,6 @@ extern const struct hash_ops hw_addr_hash_ops_free;
#define ETHER_ADDR_TO_STRING_MAX (3*6)
char* ether_addr_to_string(const struct ether_addr *addr, char buffer[ETHER_ADDR_TO_STRING_MAX]);
-int ether_addr_to_string_alloc(const struct ether_addr *addr, char **ret);
/* Use only as function argument, never stand-alone! */
#define ETHER_ADDR_TO_STR(addr) ether_addr_to_string((addr), (char[ETHER_ADDR_TO_STRING_MAX]){})
diff --git a/src/libnm-systemd-shared/src/basic/fd-util.c b/src/libnm-systemd-shared/src/basic/fd-util.c
index e052a6a26c..fd6a95e052 100644
--- a/src/libnm-systemd-shared/src/basic/fd-util.c
+++ b/src/libnm-systemd-shared/src/basic/fd-util.c
@@ -4,9 +4,7 @@
#include
#include
-#if WANT_LINUX_FS_H
-#include
-#endif
+#include
#include
#include
#include
@@ -19,6 +17,7 @@
#include "fileio.h"
#include "fs-util.h"
#include "io-util.h"
+#include "log.h"
#include "macro.h"
#include "missing_fcntl.h"
#include "missing_fs.h"
@@ -31,7 +30,6 @@
#include "sort-util.h"
#include "stat-util.h"
#include "stdio-util.h"
-#include "tmpfile-util.h"
/* The maximum number of iterations in the loop to close descriptors in the fallback case
* when /proc/self/fd/ is inaccessible. */
@@ -1012,13 +1010,13 @@ int fd_verify_safe_flags_full(int fd, int extra_flags) {
if (flags < 0)
return -errno;
- unexpected_flags = flags & ~(O_ACCMODE|O_NOFOLLOW|RAW_O_LARGEFILE|extra_flags);
+ unexpected_flags = flags & ~(O_ACCMODE_STRICT|O_NOFOLLOW|RAW_O_LARGEFILE|extra_flags);
if (unexpected_flags != 0)
return log_debug_errno(SYNTHETIC_ERRNO(EREMOTEIO),
"Unexpected flags set for extrinsic fd: 0%o",
(unsigned) unexpected_flags);
- return flags & (O_ACCMODE | extra_flags); /* return the flags variable, but remove the noise */
+ return flags & (O_ACCMODE_STRICT | extra_flags); /* return the flags variable, but remove the noise */
}
int read_nr_open(void) {
@@ -1095,30 +1093,27 @@ int path_is_root_at(int dir_fd, const char *path) {
}
int fds_are_same_mount(int fd1, int fd2) {
- STRUCT_NEW_STATX_DEFINE(st1);
- STRUCT_NEW_STATX_DEFINE(st2);
+ struct statx sx1 = {}, sx2 = {}; /* explicitly initialize the struct to make msan silent. */
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;
+ if (statx(fd1, "", AT_EMPTY_PATH, STATX_TYPE|STATX_INO|STATX_MNT_ID, &sx1) < 0)
+ return -errno;
- r = statx_fallback(fd2, "", AT_EMPTY_PATH, STATX_TYPE|STATX_INO|STATX_MNT_ID, &st2.sx);
- if (r < 0)
- return r;
+ if (statx(fd2, "", AT_EMPTY_PATH, STATX_TYPE|STATX_INO|STATX_MNT_ID, &sx2) < 0)
+ return -errno;
/* First, compare inode. If these are different, the fd does not point to the root directory "/". */
- if (!statx_inode_same(&st1.sx, &st2.sx))
+ if (!statx_inode_same(&sx1, &sx2))
return false;
/* 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(st1.nsx.stx_mask, STATX_MNT_ID)) {
+ if (!FLAGS_SET(sx1.stx_mask, STATX_MNT_ID)) {
int mntid;
r = path_get_mnt_id_at_fallback(fd1, "", &mntid);
@@ -1126,11 +1121,11 @@ int fds_are_same_mount(int fd1, int fd2) {
return r;
assert(mntid >= 0);
- st1.nsx.stx_mnt_id = mntid;
- st1.nsx.stx_mask |= STATX_MNT_ID;
+ sx1.stx_mnt_id = mntid;
+ sx1.stx_mask |= STATX_MNT_ID;
}
- if (!FLAGS_SET(st2.nsx.stx_mask, STATX_MNT_ID)) {
+ if (!FLAGS_SET(sx2.stx_mask, STATX_MNT_ID)) {
int mntid;
r = path_get_mnt_id_at_fallback(fd2, "", &mntid);
@@ -1138,15 +1133,15 @@ int fds_are_same_mount(int fd1, int fd2) {
return r;
assert(mntid >= 0);
- st2.nsx.stx_mnt_id = mntid;
- st2.nsx.stx_mask |= STATX_MNT_ID;
+ sx2.stx_mnt_id = mntid;
+ sx2.stx_mask |= STATX_MNT_ID;
}
- return statx_mount_same(&st1.nsx, &st2.nsx);
+ return statx_mount_same(&sx1, &sx2);
}
const char* accmode_to_string(int flags) {
- switch (flags & O_ACCMODE) {
+ switch (flags & O_ACCMODE_STRICT) {
case O_RDONLY:
return "ro";
case O_WRONLY:
diff --git a/src/libnm-systemd-shared/src/basic/fd-util.h b/src/libnm-systemd-shared/src/basic/fd-util.h
index 93b254c680..59c43e8140 100644
--- a/src/libnm-systemd-shared/src/basic/fd-util.h
+++ b/src/libnm-systemd-shared/src/basic/fd-util.h
@@ -8,6 +8,7 @@
#include
#include "macro.h"
+#include "memory-util.h"
#include "missing_fcntl.h"
#include "stdio-util.h"
diff --git a/src/libnm-systemd-shared/src/basic/fileio.c b/src/libnm-systemd-shared/src/basic/fileio.c
index 78231228ca..0d83faf278 100644
--- a/src/libnm-systemd-shared/src/basic/fileio.c
+++ b/src/libnm-systemd-shared/src/basic/fileio.c
@@ -247,17 +247,13 @@ static int write_string_file_atomic_at(
}
r = fopen_temporary_at(dir_fd, fn, &f, &p);
+ if (call_label_ops_post)
+ /* If fopen_temporary_at() failed in the above, propagate the error code, and ignore failures
+ * in label_ops_post(). */
+ RET_GATHER(r, label_ops_post(f ? fileno(f) : dir_fd, f ? NULL : fn, /* created= */ !!f));
if (r < 0)
goto fail;
- if (call_label_ops_post) {
- call_label_ops_post = false;
-
- r = label_ops_post(fileno(f), /* path= */ NULL, /* created= */ true);
- if (r < 0)
- goto fail;
- }
-
r = write_string_stream_full(f, line, flags, ts);
if (r < 0)
goto fail;
@@ -280,9 +276,6 @@ static int write_string_file_atomic_at(
return 0;
fail:
- if (call_label_ops_post)
- (void) label_ops_post(f ? fileno(f) : dir_fd, f ? NULL : fn, /* created= */ !!f);
-
if (f)
(void) unlinkat(dir_fd, p, 0);
return r;
@@ -296,24 +289,27 @@ int write_string_file_full(
const struct timespec *ts,
const char *label_fn) {
- bool call_label_ops_post = false, made_file = false;
+ bool made_file = false;
_cleanup_fclose_ FILE *f = NULL;
_cleanup_close_ int fd = -EBADF;
int r;
- assert(fn);
+ assert(dir_fd == AT_FDCWD || dir_fd >= 0);
assert(line);
/* We don't know how to verify whether the file contents was already on-disk. */
assert(!((flags & WRITE_STRING_FILE_VERIFY_ON_FAILURE) && (flags & WRITE_STRING_FILE_SYNC)));
if (flags & WRITE_STRING_FILE_MKDIR_0755) {
+ assert(fn);
+
r = mkdirat_parents(dir_fd, fn, 0755);
if (r < 0)
return r;
}
if (flags & WRITE_STRING_FILE_ATOMIC) {
+ assert(fn);
assert(flags & WRITE_STRING_FILE_CREATE);
r = write_string_file_atomic_at(dir_fd, fn, line, flags, ts);
@@ -323,37 +319,39 @@ int write_string_file_full(
return r;
}
- mode_t mode = write_string_file_flags_to_mode(flags);
-
- if (FLAGS_SET(flags, WRITE_STRING_FILE_LABEL|WRITE_STRING_FILE_CREATE)) {
- r = label_ops_pre(dir_fd, label_fn ?: fn, mode);
- if (r < 0)
- goto fail;
-
- call_label_ops_post = true;
- }
-
/* We manually build our own version of fopen(..., "we") that works without O_CREAT and with O_NOFOLLOW if needed. */
- fd = openat_report_new(
- dir_fd, fn, O_CLOEXEC | O_NOCTTY |
- (FLAGS_SET(flags, WRITE_STRING_FILE_NOFOLLOW) ? O_NOFOLLOW : 0) |
- (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),
- mode,
- &made_file);
- if (fd < 0) {
- r = fd;
+ if (isempty(fn))
+ r = fd = fd_reopen(
+ ASSERT_FD(dir_fd), O_CLOEXEC | O_NOCTTY |
+ (FLAGS_SET(flags, WRITE_STRING_FILE_TRUNCATE) ? O_TRUNC : 0) |
+ (FLAGS_SET(flags, WRITE_STRING_FILE_SUPPRESS_REDUNDANT_VIRTUAL) ? O_RDWR : O_WRONLY));
+ else {
+ mode_t mode = write_string_file_flags_to_mode(flags);
+ bool call_label_ops_post = false;
+
+ if (FLAGS_SET(flags, WRITE_STRING_FILE_LABEL|WRITE_STRING_FILE_CREATE)) {
+ r = label_ops_pre(dir_fd, label_fn ?: fn, mode);
+ if (r < 0)
+ goto fail;
+
+ call_label_ops_post = true;
+ }
+
+ r = fd = openat_report_new(
+ dir_fd, fn, O_CLOEXEC | O_NOCTTY |
+ (FLAGS_SET(flags, WRITE_STRING_FILE_NOFOLLOW) ? O_NOFOLLOW : 0) |
+ (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),
+ mode,
+ &made_file);
+ if (call_label_ops_post)
+ /* If openat_report_new() failed in the above, propagate the error code, and ignore
+ * failures in label_ops_post(). */
+ RET_GATHER(r, label_ops_post(fd >= 0 ? fd : dir_fd, fd >= 0 ? NULL : fn, made_file));
+ }
+ if (r < 0)
goto fail;
- }
-
- if (call_label_ops_post) {
- call_label_ops_post = false;
-
- r = label_ops_post(fd, /* path= */ NULL, made_file);
- if (r < 0)
- goto fail;
- }
r = take_fdopen_unlocked(&fd, "w", &f);
if (r < 0)
@@ -369,9 +367,6 @@ int write_string_file_full(
return 0;
fail:
- if (call_label_ops_post)
- (void) label_ops_post(fd >= 0 ? fd : dir_fd, fd >= 0 ? NULL : fn, made_file);
-
if (made_file)
(void) unlinkat(dir_fd, fn, 0);
@@ -383,7 +378,7 @@ fail:
/* OK, the operation failed, but let's see if the right contents in place already. If so, eat up the
* error. */
- if (verify_file(fn, line, !(flags & WRITE_STRING_FILE_AVOID_NEWLINE) || (flags & WRITE_STRING_FILE_VERIFY_IGNORE_NEWLINE)) > 0)
+ if (verify_file_at(dir_fd, fn, line, !(flags & WRITE_STRING_FILE_AVOID_NEWLINE) || (flags & WRITE_STRING_FILE_VERIFY_IGNORE_NEWLINE)) > 0)
return 0;
return r;
@@ -445,7 +440,6 @@ int verify_file_at(int dir_fd, const char *fn, const char *blob, bool accept_ext
size_t l, k;
int r;
- assert(fn);
assert(blob);
l = strlen(blob);
@@ -457,7 +451,7 @@ int verify_file_at(int dir_fd, const char *fn, const char *blob, bool accept_ext
if (!buf)
return -ENOMEM;
- r = fopen_unlocked_at(dir_fd, fn, "re", 0, &f);
+ r = fopen_unlocked_at(dir_fd, strempty(fn), "re", 0, &f);
if (r < 0)
return r;
@@ -478,7 +472,13 @@ int verify_file_at(int dir_fd, const char *fn, const char *blob, bool accept_ext
}
#endif /* NM_IGNORED */
-int read_virtual_file_fd(int fd, size_t max_size, char **ret_contents, size_t *ret_size) {
+int read_virtual_file_at(
+ int dir_fd,
+ const char *filename,
+ size_t max_size,
+ char **ret_contents,
+ size_t *ret_size) {
+
_cleanup_free_ char *buf = NULL;
size_t n, size;
int n_retries;
@@ -495,11 +495,23 @@ int read_virtual_file_fd(int fd, size_t max_size, char **ret_contents, size_t *r
* max_size specifies a limit on the bytes read. If max_size is SIZE_MAX, the full file is read. If
* the full file is too large to read, an error is returned. For other values of max_size, *partial
* contents* may be returned. (Though the read is still done using one syscall.) Returns 0 on
- * partial success, 1 if untruncated contents were read. */
+ * partial success, 1 if untruncated contents were read.
+ *
+ * Rule: for kernfs files using "seq_file" → use regular read_full_file_at()
+ * for kernfs files using "raw" → use read_virtual_file_at()
+ */
- assert(fd >= 0);
+ assert(dir_fd >= 0 || dir_fd == AT_FDCWD);
assert(max_size <= READ_VIRTUAL_BYTES_MAX || max_size == SIZE_MAX);
+ _cleanup_close_ int fd = -EBADF;
+ if (isempty(filename))
+ fd = fd_reopen(ASSERT_FD(dir_fd), O_RDONLY | O_NOCTTY | O_CLOEXEC);
+ else
+ fd = RET_NERRNO(openat(dir_fd, filename, O_RDONLY | O_NOCTTY | O_CLOEXEC));
+ if (fd < 0)
+ return fd;
+
/* Limit the number of attempts to read the number of bytes returned by fstat(). */
n_retries = 3;
@@ -623,31 +635,6 @@ int read_virtual_file_fd(int fd, size_t max_size, char **ret_contents, size_t *r
return !truncated;
}
-int read_virtual_file_at(
- int dir_fd,
- const char *filename,
- size_t max_size,
- char **ret_contents,
- size_t *ret_size) {
-
- _cleanup_close_ int fd = -EBADF;
-
- assert(dir_fd >= 0 || dir_fd == AT_FDCWD);
-
- if (!filename) {
- if (dir_fd == AT_FDCWD)
- return -EBADF;
-
- return read_virtual_file_fd(dir_fd, max_size, ret_contents, ret_size);
- }
-
- fd = openat(dir_fd, filename, O_RDONLY | O_NOCTTY | O_CLOEXEC);
- if (fd < 0)
- return -errno;
-
- return read_virtual_file_fd(fd, max_size, ret_contents, ret_size);
-}
-
int read_full_stream_full(
FILE *f,
const char *filename,
@@ -727,7 +714,7 @@ int read_full_stream_full(
size_t k;
/* If we shall fail when reading overly large data, then read exactly one byte more than the
- * specified size at max, since that'll tell us if there's anymore data beyond the limit*/
+ * specified size at max, since that'll tell us if there's anymore data beyond the limit. */
if (FLAGS_SET(flags, READ_FULL_FILE_FAIL_WHEN_LARGER) && n_next > size)
n_next = size + 1;
@@ -907,74 +894,46 @@ int script_get_shebang_interpreter(const char *path, char **ret) {
return 0;
}
-/**
- * Retrieve one field from a file like /proc/self/status. pattern
- * should not include whitespace or the delimiter (':'). pattern matches only
- * the beginning of a line. Whitespace before ':' is skipped. Whitespace and
- * zeros after the ':' will be skipped. field must be freed afterwards.
- * terminator specifies the terminating characters of the field value (not
- * included in the value).
- */
-int get_proc_field(const char *filename, const char *pattern, const char *terminator, char **field) {
- _cleanup_free_ char *status = NULL;
- char *t, *f;
+int get_proc_field(const char *path, const char *key, char **ret) {
+ _cleanup_fclose_ FILE *f = NULL;
int r;
- assert(terminator);
- assert(filename);
- assert(pattern);
- assert(field);
+ /* Retrieve one field from a file like /proc/self/status. "key" matches the beginning of the line
+ * and should not include whitespace or the delimiter (':').
+ * Whitespaces after the ':' will be skipped. Only the first element is returned
+ * (i.e. for /proc/meminfo line "MemTotal: 1024 kB" -> return "1024"). */
- r = read_full_virtual_file(filename, &status, NULL);
+ assert(path);
+ assert(key);
+
+ r = fopen_unlocked(path, "re", &f);
+ if (r == -ENOENT && proc_mounted() == 0)
+ return -ENOSYS;
if (r < 0)
return r;
- t = status;
+ for (;;) {
+ _cleanup_free_ char *line = NULL;
- do {
- bool pattern_ok;
+ r = read_line(f, LONG_LINE_MAX, &line);
+ if (r < 0)
+ return r;
+ if (r == 0)
+ return -ENODATA;
- do {
- t = strstr(t, pattern);
- if (!t)
- return -ENOENT;
+ char *l = startswith(line, key);
+ if (l && *l == ':') {
+ if (ret) {
+ char *s = strdupcspn(skip_leading_chars(l + 1, " \t"), WHITESPACE);
+ if (!s)
+ return -ENOMEM;
- /* Check that pattern occurs in beginning of line. */
- pattern_ok = (t == status || t[-1] == '\n');
+ *ret = s;
+ }
- t += strlen(pattern);
-
- } while (!pattern_ok);
-
- t += strspn(t, " \t");
- if (!*t)
- return -ENOENT;
-
- } while (*t != ':');
-
- t++;
-
- if (*t) {
- t += strspn(t, " \t");
-
- /* Also skip zeros, because when this is used for
- * capabilities, we don't want the zeros. This way the
- * same capability set always maps to the same string,
- * irrespective of the total capability set size. For
- * other numbers it shouldn't matter. */
- t += strspn(t, "0");
- /* Back off one char if there's nothing but whitespace
- and zeros */
- if (!*t || isspace(*t))
- t--;
+ return 0;
+ }
}
-
- f = strdupcspn(t, terminator);
- if (!f)
- return -ENOMEM;
-
- *field = f;
- return 0;
}
DIR* xopendirat(int dir_fd, const char *name, int flags) {
diff --git a/src/libnm-systemd-shared/src/basic/fileio.h b/src/libnm-systemd-shared/src/basic/fileio.h
index bd053050e1..eff9880292 100644
--- a/src/libnm-systemd-shared/src/basic/fileio.h
+++ b/src/libnm-systemd-shared/src/basic/fileio.h
@@ -56,6 +56,9 @@ int write_string_file_full(int dir_fd, const char *fn, const char *line, WriteSt
static inline int write_string_file_at(int dir_fd, const char *fn, const char *line, WriteStringFileFlags flags) {
return write_string_file_full(dir_fd, fn, line, flags, NULL, NULL);
}
+static inline int write_string_file_fd(int dir_fd, const char *line, WriteStringFileFlags flags) {
+ return write_string_file_at(dir_fd, NULL, line, flags);
+}
static inline int write_string_file(const char *fn, const char *line, WriteStringFileFlags flags) {
return write_string_file_at(AT_FDCWD, fn, line, flags);
}
@@ -75,8 +78,10 @@ static inline int read_full_file(const char *filename, char **ret_contents, size
return read_full_file_full(AT_FDCWD, filename, UINT64_MAX, SIZE_MAX, 0, NULL, ret_contents, ret_size);
}
-int read_virtual_file_fd(int fd, size_t max_size, char **ret_contents, size_t *ret_size);
int read_virtual_file_at(int dir_fd, const char *filename, size_t max_size, char **ret_contents, size_t *ret_size);
+static inline int read_virtual_file_fd(int fd, size_t max_size, char **ret_contents, size_t *ret_size) {
+ return read_virtual_file_at(fd, NULL, max_size, ret_contents, ret_size);
+}
static inline int read_virtual_file(const char *filename, size_t max_size, char **ret_contents, size_t *ret_size) {
return read_virtual_file_at(AT_FDCWD, filename, max_size, ret_contents, ret_size);
}
@@ -90,13 +95,10 @@ static inline int read_full_stream(FILE *f, char **ret_contents, size_t *ret_siz
}
int verify_file_at(int dir_fd, const char *fn, const char *blob, bool accept_extra_nl);
-static inline int verify_file(const char *fn, const char *blob, bool accept_extra_nl) {
- return verify_file_at(AT_FDCWD, fn, blob, accept_extra_nl);
-}
int script_get_shebang_interpreter(const char *path, char **ret);
-int get_proc_field(const char *filename, const char *pattern, const char *terminator, char **field);
+int get_proc_field(const char *path, const char *key, char **ret);
DIR* xopendirat(int dir_fd, const char *name, int flags);
diff --git a/src/libnm-systemd-shared/src/basic/format-ifname.c b/src/libnm-systemd-shared/src/basic/format-ifname.c
index 8159299893..57f39c30c6 100644
--- a/src/libnm-systemd-shared/src/basic/format-ifname.c
+++ b/src/libnm-systemd-shared/src/basic/format-ifname.c
@@ -3,6 +3,8 @@
#include "nm-sd-adapt-shared.h"
#include "format-ifname.h"
+#include "log.h"
+#include "stdio-util.h"
#include "string-util.h"
assert_cc(STRLEN("%") + DECIMAL_STR_MAX(int) <= IF_NAMESIZE);
diff --git a/src/libnm-systemd-shared/src/basic/format-util.c b/src/libnm-systemd-shared/src/basic/format-util.c
index b2a5d2d048..6b767ef23c 100644
--- a/src/libnm-systemd-shared/src/basic/format-util.c
+++ b/src/libnm-systemd-shared/src/basic/format-util.c
@@ -64,5 +64,4 @@ char* format_bytes_full(char *buf, size_t l, uint64_t t, FormatBytesFlag flag) {
finish:
buf[l-1] = 0;
return buf;
-
}
diff --git a/src/libnm-systemd-shared/src/basic/fs-util.c b/src/libnm-systemd-shared/src/basic/fs-util.c
index 0202933df6..bdc341f640 100644
--- a/src/libnm-systemd-shared/src/basic/fs-util.c
+++ b/src/libnm-systemd-shared/src/basic/fs-util.c
@@ -3,11 +3,11 @@
#include "nm-sd-adapt-shared.h"
#include
+#include
+#include
#include
#include
#include
-#include
-#include
#include
#include "alloc-util.h"
@@ -80,6 +80,11 @@ int rmdir_parents(const char *path, const char *stop) {
int rename_noreplace(int olddirfd, const char *oldpath, int newdirfd, const char *newpath) {
int r;
+ assert(olddirfd >= 0 || olddirfd == AT_FDCWD);
+ assert(oldpath);
+ assert(newdirfd >= 0 || newdirfd == AT_FDCWD);
+ assert(newpath);
+
/* Try the ideal approach first */
if (renameat2(olddirfd, oldpath, newdirfd, newpath, RENAME_NOREPLACE) >= 0)
return 0;
@@ -792,7 +797,7 @@ int unlinkat_deallocate(int fd, const char *name, UnlinkDeallocateFlags flags) {
}
}
- /* Don't dallocate if there's nothing to deallocate or if the file is linked elsewhere */
+ /* Don't deallocate if there's nothing to deallocate or if the file is linked elsewhere */
if (st.st_blocks == 0 || st.st_nlink > 0)
return 0;
@@ -1041,7 +1046,7 @@ int open_mkdir_at_full(int dirfd, const char *path, int flags, XOpenFlags xopen_
if (flags & ~(O_RDONLY|O_CLOEXEC|O_DIRECTORY|O_EXCL|O_NOATIME|O_NOFOLLOW|O_PATH))
return -EINVAL;
- if ((flags & O_ACCMODE) != O_RDONLY)
+ if ((flags & O_ACCMODE_STRICT) != O_RDONLY)
return -EINVAL;
/* Note that O_DIRECTORY|O_NOFOLLOW is implied, but we allow specifying it anyway. The following
@@ -1264,7 +1269,7 @@ int xopenat_full(int dir_fd, const char *path, int open_flags, XOpenFlags xopen_
}
if (FLAGS_SET(xopen_flags, XO_NOCOW)) {
- r = chattr_fd(fd, FS_NOCOW_FL, FS_NOCOW_FL, NULL);
+ r = chattr_fd(fd, FS_NOCOW_FL, FS_NOCOW_FL);
if (r < 0 && !ERRNO_IS_NOT_SUPPORTED(r))
goto error;
}
diff --git a/src/libnm-systemd-shared/src/basic/fs-util.h b/src/libnm-systemd-shared/src/basic/fs-util.h
index eb031a0ccd..b2c0107990 100644
--- a/src/libnm-systemd-shared/src/basic/fs-util.h
+++ b/src/libnm-systemd-shared/src/basic/fs-util.h
@@ -172,3 +172,12 @@ static inline int at_flags_normalize_nofollow(int flags) {
flags |= AT_SYMLINK_NOFOLLOW;
return flags;
}
+
+static inline int at_flags_normalize_follow(int flags) {
+ if (FLAGS_SET(flags, AT_SYMLINK_NOFOLLOW)) {
+ assert(!FLAGS_SET(flags, AT_SYMLINK_FOLLOW));
+ flags &= ~AT_SYMLINK_NOFOLLOW;
+ } else
+ flags |= AT_SYMLINK_FOLLOW;
+ return flags;
+}
diff --git a/src/libnm-systemd-shared/src/basic/glyph-util.c b/src/libnm-systemd-shared/src/basic/glyph-util.c
index d38698b688..aedd47c23d 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_full(SpecialGlyph code, bool force_utf) {
+const char* glyph_full(Glyph 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
@@ -34,133 +34,139 @@ const char* special_glyph_full(SpecialGlyph code, bool force_utf) {
* http://git.altlinux.org/people/legion/packages/kbd.git?p=kbd.git;a=blob;f=data/consolefonts/README.eurlatgr
*/
- static const char* const draw_table[2][_SPECIAL_GLYPH_MAX] = {
+ static const char* const draw_table[2][_GLYPH_MAX] = {
/* ASCII fallback */
[false] = {
- [SPECIAL_GLYPH_TREE_VERTICAL] = "| ",
- [SPECIAL_GLYPH_TREE_BRANCH] = "|-",
- [SPECIAL_GLYPH_TREE_RIGHT] = "`-",
- [SPECIAL_GLYPH_TREE_SPACE] = " ",
- [SPECIAL_GLYPH_TREE_TOP] = ",-",
- [SPECIAL_GLYPH_VERTICAL_DOTTED] = ":",
- [SPECIAL_GLYPH_HORIZONTAL_DOTTED] = "-",
- [SPECIAL_GLYPH_HORIZONTAL_FAT] = "=",
- [SPECIAL_GLYPH_TRIANGULAR_BULLET] = ">",
- [SPECIAL_GLYPH_BLACK_CIRCLE] = "*",
- [SPECIAL_GLYPH_WHITE_CIRCLE] = "*",
- [SPECIAL_GLYPH_MULTIPLICATION_SIGN] = "x",
- [SPECIAL_GLYPH_CIRCLE_ARROW] = "*",
- [SPECIAL_GLYPH_BULLET] = "*",
- [SPECIAL_GLYPH_MU] = "u",
- [SPECIAL_GLYPH_CHECK_MARK] = "+",
- [SPECIAL_GLYPH_CROSS_MARK] = "-",
- [SPECIAL_GLYPH_LIGHT_SHADE] = "-",
- [SPECIAL_GLYPH_DARK_SHADE] = "X",
- [SPECIAL_GLYPH_FULL_BLOCK] = "#",
- [SPECIAL_GLYPH_SIGMA] = "S",
- [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] = ":-]",
- [SPECIAL_GLYPH_HAPPY_SMILEY] = ":-}",
- [SPECIAL_GLYPH_SLIGHTLY_HAPPY_SMILEY] = ":-)",
- [SPECIAL_GLYPH_NEUTRAL_SMILEY] = ":-|",
- [SPECIAL_GLYPH_SLIGHTLY_UNHAPPY_SMILEY] = ":-(",
- [SPECIAL_GLYPH_UNHAPPY_SMILEY] = ":-{",
- [SPECIAL_GLYPH_DEPRESSED_SMILEY] = ":-[",
- [SPECIAL_GLYPH_LOCK_AND_KEY] = "o-,",
- [SPECIAL_GLYPH_TOUCH] = "O=", /* Yeah, not very convincing, can you do it better? */
- [SPECIAL_GLYPH_RECYCLING] = "~",
- [SPECIAL_GLYPH_DOWNLOAD] = "\\",
- [SPECIAL_GLYPH_SPARKLES] = "*",
- [SPECIAL_GLYPH_LOW_BATTERY] = "!",
- [SPECIAL_GLYPH_WARNING_SIGN] = "!",
- [SPECIAL_GLYPH_RED_CIRCLE] = "o",
- [SPECIAL_GLYPH_YELLOW_CIRCLE] = "o",
- [SPECIAL_GLYPH_BLUE_CIRCLE] = "o",
- [SPECIAL_GLYPH_GREEN_CIRCLE] = "o",
- [SPECIAL_GLYPH_SUPERHERO] = "S",
- [SPECIAL_GLYPH_IDCARD] = "@",
+ [GLYPH_SPACE] = " ",
+ [GLYPH_TREE_VERTICAL] = "| ",
+ [GLYPH_TREE_BRANCH] = "|-",
+ [GLYPH_TREE_RIGHT] = "`-",
+ [GLYPH_TREE_SPACE] = " ",
+ [GLYPH_TREE_TOP] = ",-",
+ [GLYPH_VERTICAL_DOTTED] = ":",
+ [GLYPH_HORIZONTAL_DOTTED] = "-",
+ [GLYPH_HORIZONTAL_FAT] = "=",
+ [GLYPH_TRIANGULAR_BULLET] = ">",
+ [GLYPH_BLACK_CIRCLE] = "*",
+ [GLYPH_WHITE_CIRCLE] = "*",
+ [GLYPH_MULTIPLICATION_SIGN] = "x",
+ [GLYPH_CIRCLE_ARROW] = "*",
+ [GLYPH_BULLET] = "*",
+ [GLYPH_MU] = "u",
+ [GLYPH_CHECK_MARK] = "+",
+ [GLYPH_CROSS_MARK] = "-",
+ [GLYPH_LIGHT_SHADE] = "-",
+ [GLYPH_DARK_SHADE] = "X",
+ [GLYPH_FULL_BLOCK] = "#",
+ [GLYPH_SIGMA] = "S",
+ [GLYPH_ARROW_UP] = "^",
+ [GLYPH_ARROW_DOWN] = "v",
+ [GLYPH_ARROW_LEFT] = "<-",
+ [GLYPH_ARROW_RIGHT] = "->",
+ [GLYPH_ELLIPSIS] = "...",
+ [GLYPH_EXTERNAL_LINK] = "[LNK]",
+ [GLYPH_ECSTATIC_SMILEY] = ":-]",
+ [GLYPH_HAPPY_SMILEY] = ":-}",
+ [GLYPH_SLIGHTLY_HAPPY_SMILEY] = ":-)",
+ [GLYPH_NEUTRAL_SMILEY] = ":-|",
+ [GLYPH_SLIGHTLY_UNHAPPY_SMILEY] = ":-(",
+ [GLYPH_UNHAPPY_SMILEY] = ":-{",
+ [GLYPH_DEPRESSED_SMILEY] = ":-[",
+ [GLYPH_LOCK_AND_KEY] = "o-,",
+ [GLYPH_TOUCH] = "O=", /* Yeah, not very convincing, can you do it better? */
+ [GLYPH_RECYCLING] = "~",
+ [GLYPH_DOWNLOAD] = "\\",
+ [GLYPH_SPARKLES] = "*",
+ [GLYPH_LOW_BATTERY] = "!",
+ [GLYPH_WARNING_SIGN] = "!",
+ [GLYPH_RED_CIRCLE] = "o",
+ [GLYPH_YELLOW_CIRCLE] = "o",
+ [GLYPH_BLUE_CIRCLE] = "o",
+ [GLYPH_GREEN_CIRCLE] = "o",
+ [GLYPH_SUPERHERO] = "S",
+ [GLYPH_IDCARD] = "@",
+ [GLYPH_HOME] = "^",
},
/* UTF-8 */
[true] = {
+ /* This exists to allow more consistent handling of optional whitespace */
+ [GLYPH_SPACE] = " ",
+
/* The following are multiple glyphs in both ASCII and in UNICODE */
- [SPECIAL_GLYPH_TREE_VERTICAL] = u8"│ ",
- [SPECIAL_GLYPH_TREE_BRANCH] = u8"├─",
- [SPECIAL_GLYPH_TREE_RIGHT] = u8"└─",
- [SPECIAL_GLYPH_TREE_SPACE] = u8" ",
- [SPECIAL_GLYPH_TREE_TOP] = u8"┌─",
+ [GLYPH_TREE_VERTICAL] = UTF8("│ "),
+ [GLYPH_TREE_BRANCH] = UTF8("├─"),
+ [GLYPH_TREE_RIGHT] = UTF8("└─"),
+ [GLYPH_TREE_SPACE] = UTF8(" "),
+ [GLYPH_TREE_TOP] = UTF8("┌─"),
/* Single glyphs in both cases */
- [SPECIAL_GLYPH_VERTICAL_DOTTED] = u8"┆",
- [SPECIAL_GLYPH_HORIZONTAL_DOTTED] = u8"┄",
- [SPECIAL_GLYPH_HORIZONTAL_FAT] = u8"━",
- [SPECIAL_GLYPH_TRIANGULAR_BULLET] = u8"‣",
- [SPECIAL_GLYPH_BLACK_CIRCLE] = u8"●",
- [SPECIAL_GLYPH_WHITE_CIRCLE] = u8"○",
- [SPECIAL_GLYPH_MULTIPLICATION_SIGN] = u8"×",
- [SPECIAL_GLYPH_CIRCLE_ARROW] = u8"↻",
- [SPECIAL_GLYPH_BULLET] = u8"•",
- [SPECIAL_GLYPH_MU] = u8"μ", /* actually called: GREEK SMALL LETTER MU */
- [SPECIAL_GLYPH_CHECK_MARK] = u8"✓",
- [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 */
+ [GLYPH_VERTICAL_DOTTED] = UTF8("┆"),
+ [GLYPH_HORIZONTAL_DOTTED] = UTF8("┄"),
+ [GLYPH_HORIZONTAL_FAT] = UTF8("━"),
+ [GLYPH_TRIANGULAR_BULLET] = UTF8("‣"),
+ [GLYPH_BLACK_CIRCLE] = UTF8("●"),
+ [GLYPH_WHITE_CIRCLE] = UTF8("○"),
+ [GLYPH_MULTIPLICATION_SIGN] = UTF8("×"),
+ [GLYPH_CIRCLE_ARROW] = UTF8("↻"),
+ [GLYPH_BULLET] = UTF8("•"),
+ [GLYPH_MU] = UTF8("μ"), /* actually called: GREEK SMALL LETTER MU */
+ [GLYPH_CHECK_MARK] = UTF8("✓"),
+ [GLYPH_CROSS_MARK] = UTF8("✗"), /* actually called: BALLOT X */
+ [GLYPH_LIGHT_SHADE] = UTF8("░"),
+ [GLYPH_DARK_SHADE] = UTF8("▒"),
+ [GLYPH_FULL_BLOCK] = UTF8("█"),
+ [GLYPH_SIGMA] = UTF8("Σ"),
+ [GLYPH_ARROW_UP] = UTF8("↑"), /* actually called: UPWARDS ARROW */
+ [GLYPH_ARROW_DOWN] = UTF8("↓"), /* actually called: DOWNWARDS ARROW */
/* Single glyph in Unicode, two in ASCII */
- [SPECIAL_GLYPH_ARROW_LEFT] = u8"←", /* actually called: LEFTWARDS ARROW */
- [SPECIAL_GLYPH_ARROW_RIGHT] = u8"→", /* actually called: RIGHTWARDS ARROW */
+ [GLYPH_ARROW_LEFT] = UTF8("←"), /* actually called: LEFTWARDS ARROW */
+ [GLYPH_ARROW_RIGHT] = UTF8("→"), /* actually called: RIGHTWARDS ARROW */
/* Single glyph in Unicode, three in ASCII */
- [SPECIAL_GLYPH_ELLIPSIS] = u8"…", /* actually called: HORIZONTAL ELLIPSIS */
+ [GLYPH_ELLIPSIS] = UTF8("…"), /* actually called: HORIZONTAL ELLIPSIS */
/* Three glyphs in Unicode, five in ASCII */
- [SPECIAL_GLYPH_EXTERNAL_LINK] = u8"[🡕]", /* actually called: NORTH EAST SANS-SERIF ARROW, enclosed in [] */
+ [GLYPH_EXTERNAL_LINK] = UTF8("[🡕]"), /* actually called: NORTH EAST SANS-SERIF ARROW, enclosed in [] */
/* These smileys are a single glyph in Unicode, and three in ASCII */
- [SPECIAL_GLYPH_ECSTATIC_SMILEY] = u8"😇", /* actually called: SMILING FACE WITH HALO */
- [SPECIAL_GLYPH_HAPPY_SMILEY] = u8"😀", /* actually called: GRINNING FACE */
- [SPECIAL_GLYPH_SLIGHTLY_HAPPY_SMILEY] = u8"🙂", /* actually called: SLIGHTLY SMILING FACE */
- [SPECIAL_GLYPH_NEUTRAL_SMILEY] = u8"😐", /* actually called: NEUTRAL FACE */
- [SPECIAL_GLYPH_SLIGHTLY_UNHAPPY_SMILEY] = u8"🙁", /* actually called: SLIGHTLY FROWNING FACE */
- [SPECIAL_GLYPH_UNHAPPY_SMILEY] = u8"😨", /* actually called: FEARFUL FACE */
- [SPECIAL_GLYPH_DEPRESSED_SMILEY] = u8"🤢", /* actually called: NAUSEATED FACE */
+ [GLYPH_ECSTATIC_SMILEY] = UTF8("😇"), /* actually called: SMILING FACE WITH HALO */
+ [GLYPH_HAPPY_SMILEY] = UTF8("😀"), /* actually called: GRINNING FACE */
+ [GLYPH_SLIGHTLY_HAPPY_SMILEY] = UTF8("🙂"), /* actually called: SLIGHTLY SMILING FACE */
+ [GLYPH_NEUTRAL_SMILEY] = UTF8("😐"), /* actually called: NEUTRAL FACE */
+ [GLYPH_SLIGHTLY_UNHAPPY_SMILEY] = UTF8("🙁"), /* actually called: SLIGHTLY FROWNING FACE */
+ [GLYPH_UNHAPPY_SMILEY] = UTF8("😨"), /* actually called: FEARFUL FACE */
+ [GLYPH_DEPRESSED_SMILEY] = UTF8("🤢"), /* actually called: NAUSEATED FACE */
/* This emoji is a single character cell glyph in Unicode, and three in ASCII */
- [SPECIAL_GLYPH_LOCK_AND_KEY] = u8"🔐", /* actually called: CLOSED LOCK WITH KEY */
+ [GLYPH_LOCK_AND_KEY] = UTF8("🔐"), /* actually called: CLOSED LOCK WITH KEY */
/* This emoji is a single character cell glyph in Unicode, and two in ASCII */
- [SPECIAL_GLYPH_TOUCH] = u8"👆", /* actually called: BACKHAND INDEX POINTING UP */
+ [GLYPH_TOUCH] = UTF8("👆"), /* actually called: BACKHAND INDEX POINTING UP */
/* These four emojis are single character cell glyphs in Unicode and also in ASCII. */
- [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"🌍",
+ [GLYPH_RECYCLING] = UTF8("♻️"), /* actually called: UNIVERSAL RECYCLNG SYMBOL */
+ [GLYPH_DOWNLOAD] = UTF8("⤵️"), /* actually called: RIGHT ARROW CURVING DOWN */
+ [GLYPH_SPARKLES] = UTF8("✨"),
+ [GLYPH_LOW_BATTERY] = UTF8("🪫"),
+ [GLYPH_WARNING_SIGN] = UTF8("⚠️"),
+ [GLYPH_COMPUTER_DISK] = UTF8("💽"),
+ [GLYPH_WORLD] = UTF8("🌍"),
- [SPECIAL_GLYPH_RED_CIRCLE] = u8"🔴",
- [SPECIAL_GLYPH_YELLOW_CIRCLE] = u8"🟡",
- [SPECIAL_GLYPH_BLUE_CIRCLE] = u8"🔵",
- [SPECIAL_GLYPH_GREEN_CIRCLE] = u8"🟢",
- [SPECIAL_GLYPH_SUPERHERO] = u8"🦸",
- [SPECIAL_GLYPH_IDCARD] = u8"🪪",
+ [GLYPH_RED_CIRCLE] = UTF8("🔴"),
+ [GLYPH_YELLOW_CIRCLE] = UTF8("🟡"),
+ [GLYPH_BLUE_CIRCLE] = UTF8("🔵"),
+ [GLYPH_GREEN_CIRCLE] = UTF8("🟢"),
+ [GLYPH_SUPERHERO] = UTF8("🦸"),
+ [GLYPH_IDCARD] = UTF8("🪪"),
+ [GLYPH_HOME] = UTF8("🏠"),
},
};
if (code < 0)
return NULL;
- assert(code < _SPECIAL_GLYPH_MAX);
- return draw_table[force_utf || (code >= _SPECIAL_GLYPH_FIRST_EMOJI ? emoji_enabled() : is_locale_utf8())][code];
+ assert(code < _GLYPH_MAX);
+ return draw_table[force_utf || (code >= _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 ca4d4eda71..730f269560 100644
--- a/src/libnm-systemd-shared/src/basic/glyph-util.h
+++ b/src/libnm-systemd-shared/src/basic/glyph-util.h
@@ -6,73 +6,79 @@
#include "macro.h"
-typedef enum SpecialGlyph {
- SPECIAL_GLYPH_TREE_VERTICAL,
- SPECIAL_GLYPH_TREE_BRANCH,
- SPECIAL_GLYPH_TREE_RIGHT,
- SPECIAL_GLYPH_TREE_SPACE,
- SPECIAL_GLYPH_TREE_TOP,
- SPECIAL_GLYPH_VERTICAL_DOTTED,
- SPECIAL_GLYPH_HORIZONTAL_DOTTED,
- SPECIAL_GLYPH_HORIZONTAL_FAT,
- SPECIAL_GLYPH_TRIANGULAR_BULLET,
- SPECIAL_GLYPH_BLACK_CIRCLE,
- SPECIAL_GLYPH_WHITE_CIRCLE,
- SPECIAL_GLYPH_MULTIPLICATION_SIGN,
- SPECIAL_GLYPH_CIRCLE_ARROW,
- SPECIAL_GLYPH_BULLET,
- SPECIAL_GLYPH_MU,
- SPECIAL_GLYPH_CHECK_MARK,
- SPECIAL_GLYPH_CROSS_MARK,
- 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,
- SPECIAL_GLYPH_HAPPY_SMILEY,
- SPECIAL_GLYPH_SLIGHTLY_HAPPY_SMILEY,
- SPECIAL_GLYPH_NEUTRAL_SMILEY,
- SPECIAL_GLYPH_SLIGHTLY_UNHAPPY_SMILEY,
- SPECIAL_GLYPH_UNHAPPY_SMILEY,
- SPECIAL_GLYPH_DEPRESSED_SMILEY,
- SPECIAL_GLYPH_LOCK_AND_KEY,
- SPECIAL_GLYPH_TOUCH,
- 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_RED_CIRCLE,
- SPECIAL_GLYPH_YELLOW_CIRCLE,
- SPECIAL_GLYPH_BLUE_CIRCLE,
- SPECIAL_GLYPH_GREEN_CIRCLE,
- SPECIAL_GLYPH_SUPERHERO,
- SPECIAL_GLYPH_IDCARD,
- _SPECIAL_GLYPH_MAX,
- _SPECIAL_GLYPH_INVALID = -EINVAL,
-} SpecialGlyph;
+typedef enum Glyph {
+ GLYPH_SPACE,
+ GLYPH_TREE_VERTICAL,
+ GLYPH_TREE_BRANCH,
+ GLYPH_TREE_RIGHT,
+ GLYPH_TREE_SPACE,
+ GLYPH_TREE_TOP,
+ GLYPH_VERTICAL_DOTTED,
+ GLYPH_HORIZONTAL_DOTTED,
+ GLYPH_HORIZONTAL_FAT,
+ GLYPH_TRIANGULAR_BULLET,
+ GLYPH_BLACK_CIRCLE,
+ GLYPH_WHITE_CIRCLE,
+ GLYPH_MULTIPLICATION_SIGN,
+ GLYPH_CIRCLE_ARROW,
+ GLYPH_BULLET,
+ GLYPH_MU,
+ GLYPH_CHECK_MARK,
+ GLYPH_CROSS_MARK,
+ GLYPH_LIGHT_SHADE,
+ GLYPH_DARK_SHADE,
+ GLYPH_FULL_BLOCK,
+ GLYPH_SIGMA,
+ GLYPH_ARROW_UP,
+ GLYPH_ARROW_DOWN,
+ GLYPH_ARROW_LEFT,
+ GLYPH_ARROW_RIGHT,
+ GLYPH_ELLIPSIS,
+ GLYPH_EXTERNAL_LINK,
+ _GLYPH_FIRST_EMOJI,
+ GLYPH_ECSTATIC_SMILEY = _GLYPH_FIRST_EMOJI,
+ GLYPH_HAPPY_SMILEY,
+ GLYPH_SLIGHTLY_HAPPY_SMILEY,
+ GLYPH_NEUTRAL_SMILEY,
+ GLYPH_SLIGHTLY_UNHAPPY_SMILEY,
+ GLYPH_UNHAPPY_SMILEY,
+ GLYPH_DEPRESSED_SMILEY,
+ GLYPH_LOCK_AND_KEY,
+ GLYPH_TOUCH,
+ GLYPH_RECYCLING,
+ GLYPH_DOWNLOAD,
+ GLYPH_SPARKLES,
+ GLYPH_LOW_BATTERY,
+ GLYPH_WARNING_SIGN,
+ GLYPH_COMPUTER_DISK,
+ GLYPH_WORLD,
+ GLYPH_RED_CIRCLE,
+ GLYPH_YELLOW_CIRCLE,
+ GLYPH_BLUE_CIRCLE,
+ GLYPH_GREEN_CIRCLE,
+ GLYPH_SUPERHERO,
+ GLYPH_IDCARD,
+ GLYPH_HOME,
+ _GLYPH_MAX,
+ _GLYPH_INVALID = -EINVAL,
+} Glyph;
bool emoji_enabled(void);
-const char* special_glyph_full(SpecialGlyph code, bool force_utf) _const_;
+const char* glyph_full(Glyph code, bool force_utf) _const_;
-static inline const char* special_glyph(SpecialGlyph code) {
- return special_glyph_full(code, false);
+static inline const char* glyph(Glyph code) {
+ return 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);
+static inline const char* optional_glyph(Glyph code) {
+ return emoji_enabled() ? glyph(code) : "";
}
-static inline const char* special_glyph_check_mark_space(bool b) {
- return b ? special_glyph(SPECIAL_GLYPH_CHECK_MARK) : " ";
+static inline const char* glyph_check_mark(bool b) {
+ return b ? glyph(GLYPH_CHECK_MARK) : glyph(GLYPH_CROSS_MARK);
+}
+
+static inline const char* glyph_check_mark_space(bool b) {
+ return b ? glyph(GLYPH_CHECK_MARK) : " ";
}
diff --git a/src/libnm-systemd-shared/src/basic/hash-funcs.c b/src/libnm-systemd-shared/src/basic/hash-funcs.c
index d20ca3c336..b1975cfe49 100644
--- a/src/libnm-systemd-shared/src/basic/hash-funcs.c
+++ b/src/libnm-systemd-shared/src/basic/hash-funcs.c
@@ -12,15 +12,23 @@ void string_hash_func(const char *p, struct siphash *state) {
siphash24_compress(p, strlen(p) + 1, state);
}
-DEFINE_HASH_OPS(string_hash_ops, char, string_hash_func, string_compare_func);
-DEFINE_HASH_OPS_WITH_KEY_DESTRUCTOR(string_hash_ops_free,
- char, string_hash_func, string_compare_func, free);
-DEFINE_HASH_OPS_FULL(string_hash_ops_free_free,
- char, string_hash_func, string_compare_func, free,
- void, free);
-DEFINE_HASH_OPS_FULL(string_hash_ops_free_strv_free,
- char, string_hash_func, string_compare_func, free,
- char*, strv_free);
+DEFINE_HASH_OPS(string_hash_ops,
+ char, string_hash_func, string_compare_func);
+DEFINE_HASH_OPS_WITH_KEY_DESTRUCTOR(
+ string_hash_ops_free,
+ char, string_hash_func, string_compare_func, free);
+DEFINE_HASH_OPS_WITH_VALUE_DESTRUCTOR(
+ string_hash_ops_value_free,
+ char, string_hash_func, string_compare_func,
+ void, free);
+DEFINE_HASH_OPS_FULL(
+ string_hash_ops_free_free,
+ char, string_hash_func, string_compare_func, free,
+ void, free);
+DEFINE_HASH_OPS_FULL(
+ string_hash_ops_free_strv_free,
+ char, string_hash_func, string_compare_func, free,
+ char*, strv_free);
void path_hash_func(const char *q, struct siphash *state) {
bool add_slash = false;
@@ -61,12 +69,15 @@ void path_hash_func(const char *q, struct siphash *state) {
}
}
-DEFINE_HASH_OPS(path_hash_ops, char, path_hash_func, path_compare);
-DEFINE_HASH_OPS_WITH_KEY_DESTRUCTOR(path_hash_ops_free,
- char, path_hash_func, path_compare, free);
-DEFINE_HASH_OPS_FULL(path_hash_ops_free_free,
- char, path_hash_func, path_compare, free,
- void, free);
+DEFINE_HASH_OPS(path_hash_ops,
+ char, path_hash_func, path_compare);
+DEFINE_HASH_OPS_WITH_KEY_DESTRUCTOR(
+ path_hash_ops_free,
+ char, path_hash_func, path_compare, free);
+DEFINE_HASH_OPS_FULL(
+ path_hash_ops_free_free,
+ char, path_hash_func, path_compare, free,
+ void, free);
void trivial_hash_func(const void *p, struct siphash *state) {
siphash24_compress_typesafe(p, state);
@@ -76,23 +87,19 @@ int trivial_compare_func(const void *a, const void *b) {
return CMP(a, b);
}
-const struct hash_ops trivial_hash_ops = {
- .hash = trivial_hash_func,
- .compare = trivial_compare_func,
-};
-
-const struct hash_ops trivial_hash_ops_free = {
- .hash = trivial_hash_func,
- .compare = trivial_compare_func,
- .free_key = free,
-};
-
-const struct hash_ops trivial_hash_ops_free_free = {
- .hash = trivial_hash_func,
- .compare = trivial_compare_func,
- .free_key = free,
- .free_value = free,
-};
+DEFINE_HASH_OPS(trivial_hash_ops,
+ void, trivial_hash_func, trivial_compare_func);
+DEFINE_HASH_OPS_WITH_KEY_DESTRUCTOR(
+ trivial_hash_ops_free,
+ void, trivial_hash_func, trivial_compare_func, free);
+DEFINE_HASH_OPS_WITH_VALUE_DESTRUCTOR(
+ trivial_hash_ops_value_free,
+ void, trivial_hash_func, trivial_compare_func,
+ void, free);
+DEFINE_HASH_OPS_FULL(
+ trivial_hash_ops_free_free,
+ void, trivial_hash_func, trivial_compare_func, free,
+ void, free);
void uint64_hash_func(const uint64_t *p, struct siphash *state) {
siphash24_compress_typesafe(*p, state);
@@ -102,7 +109,12 @@ int uint64_compare_func(const uint64_t *a, const uint64_t *b) {
return CMP(*a, *b);
}
-DEFINE_HASH_OPS(uint64_hash_ops, uint64_t, uint64_hash_func, uint64_compare_func);
+DEFINE_HASH_OPS(uint64_hash_ops,
+ uint64_t, uint64_hash_func, uint64_compare_func);
+DEFINE_HASH_OPS_WITH_VALUE_DESTRUCTOR(
+ uint64_hash_ops_value_free,
+ uint64_t, uint64_hash_func, uint64_compare_func,
+ void, free);
#if 0 /* NM_IGNORED */
#if SIZEOF_DEV_T != 8
diff --git a/src/libnm-systemd-shared/src/basic/hash-funcs.h b/src/libnm-systemd-shared/src/basic/hash-funcs.h
index 3804e94d98..d0736807ba 100644
--- a/src/libnm-systemd-shared/src/basic/hash-funcs.h
+++ b/src/libnm-systemd-shared/src/basic/hash-funcs.h
@@ -77,6 +77,7 @@ void string_hash_func(const char *p, struct siphash *state);
#define string_compare_func strcmp
extern const struct hash_ops string_hash_ops;
extern const struct hash_ops string_hash_ops_free;
+extern const struct hash_ops string_hash_ops_value_free;
extern const struct hash_ops string_hash_ops_free_free;
extern const struct hash_ops string_hash_ops_free_strv_free;
@@ -91,6 +92,7 @@ void trivial_hash_func(const void *p, struct siphash *state);
int trivial_compare_func(const void *a, const void *b) _const_;
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_value_free;
extern const struct hash_ops trivial_hash_ops_free_free;
/* 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
@@ -98,6 +100,7 @@ extern const struct hash_ops trivial_hash_ops_free_free;
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;
+extern const struct hash_ops uint64_hash_ops_value_free;
/* 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! */
diff --git a/src/libnm-systemd-shared/src/basic/hashmap.c b/src/libnm-systemd-shared/src/basic/hashmap.c
index 6b0247c338..e58375c919 100644
--- a/src/libnm-systemd-shared/src/basic/hashmap.c
+++ b/src/libnm-systemd-shared/src/basic/hashmap.c
@@ -14,6 +14,7 @@
#include "alloc-util.h"
#include "fileio.h"
#include "hashmap.h"
+#include "log.h"
#include "logarithm.h"
#include "macro.h"
#include "memory-util.h"
@@ -916,24 +917,20 @@ static void hashmap_free_no_clear(HashmapBase *h) {
free(h);
}
-HashmapBase* _hashmap_free(HashmapBase *h, free_func_t default_free_key, free_func_t default_free_value) {
+HashmapBase* _hashmap_free(HashmapBase *h) {
if (h) {
- _hashmap_clear(h, default_free_key, default_free_value);
+ _hashmap_clear(h);
hashmap_free_no_clear(h);
}
return NULL;
}
-void _hashmap_clear(HashmapBase *h, free_func_t default_free_key, free_func_t default_free_value) {
- free_func_t free_key, free_value;
+void _hashmap_clear(HashmapBase *h) {
if (!h)
return;
- free_key = h->hash_ops->free_key ?: default_free_key;
- free_value = h->hash_ops->free_value ?: default_free_value;
-
- if (free_key || free_value) {
+ if (h->hash_ops->free_key || h->hash_ops->free_value) {
/* If destructor calls are defined, let's destroy things defensively: let's take the item out of the
* hash table, and only then call the destructor functions. If these destructors then try to unregister
@@ -945,11 +942,11 @@ void _hashmap_clear(HashmapBase *h, free_func_t default_free_key, free_func_t de
v = _hashmap_first_key_and_value(h, true, &k);
- if (free_key)
- free_key(k);
+ if (h->hash_ops->free_key)
+ h->hash_ops->free_key(k);
- if (free_value)
- free_value(v);
+ if (h->hash_ops->free_value)
+ h->hash_ops->free_value(v);
}
}
@@ -1784,7 +1781,7 @@ HashmapBase* _hashmap_copy(HashmapBase *h HASHMAP_DEBUG_PARAMS) {
}
if (r < 0)
- return _hashmap_free(copy, NULL, NULL);
+ return _hashmap_free(copy);
return copy;
}
@@ -1809,6 +1806,23 @@ char** _hashmap_get_strv(HashmapBase *h) {
return sv;
}
+char** set_to_strv(Set **s) {
+ assert(s);
+
+ /* This is similar to set_get_strv(), but invalidates the set on success. */
+
+ char **v = new(char*, set_size(*s) + 1);
+ if (!v)
+ return NULL;
+
+ for (char **p = v; (*p = set_steal_first(*s)); p++)
+ ;
+
+ assert(set_isempty(*s));
+ *s = set_free(*s);
+ return v;
+}
+
void* ordered_hashmap_next(OrderedHashmap *h, const void *key) {
struct ordered_hashmap_entry *e;
unsigned hash, idx;
diff --git a/src/libnm-systemd-shared/src/basic/hashmap.h b/src/libnm-systemd-shared/src/basic/hashmap.h
index 01a4fb3204..8ced7e6a99 100644
--- a/src/libnm-systemd-shared/src/basic/hashmap.h
+++ b/src/libnm-systemd-shared/src/basic/hashmap.h
@@ -88,36 +88,17 @@ OrderedHashmap* _ordered_hashmap_new(const struct hash_ops *hash_ops HASHMAP_DE
#define hashmap_new(ops) _hashmap_new(ops HASHMAP_DEBUG_SRC_ARGS)
#define ordered_hashmap_new(ops) _ordered_hashmap_new(ops HASHMAP_DEBUG_SRC_ARGS)
-#define hashmap_free_and_replace(a, b) \
+#define hashmap_free_and_replace(a, b) \
free_and_replace_full(a, b, hashmap_free)
+#define ordered_hashmap_free_and_replace(a, b) \
+ free_and_replace_full(a, b, ordered_hashmap_free)
-HashmapBase* _hashmap_free(HashmapBase *h, free_func_t default_free_key, free_func_t default_free_value);
+HashmapBase* _hashmap_free(HashmapBase *h);
static inline Hashmap* hashmap_free(Hashmap *h) {
- return (void*) _hashmap_free(HASHMAP_BASE(h), NULL, NULL);
+ return (void*) _hashmap_free(HASHMAP_BASE(h));
}
static inline OrderedHashmap* ordered_hashmap_free(OrderedHashmap *h) {
- return (void*) _hashmap_free(HASHMAP_BASE(h), NULL, NULL);
-}
-
-static inline Hashmap* hashmap_free_free(Hashmap *h) {
- return (void*) _hashmap_free(HASHMAP_BASE(h), NULL, free);
-}
-static inline OrderedHashmap* ordered_hashmap_free_free(OrderedHashmap *h) {
- return (void*) _hashmap_free(HASHMAP_BASE(h), NULL, free);
-}
-
-static inline Hashmap* hashmap_free_free_key(Hashmap *h) {
- return (void*) _hashmap_free(HASHMAP_BASE(h), free, NULL);
-}
-static inline OrderedHashmap* ordered_hashmap_free_free_key(OrderedHashmap *h) {
- return (void*) _hashmap_free(HASHMAP_BASE(h), free, NULL);
-}
-
-static inline Hashmap* hashmap_free_free_free(Hashmap *h) {
- return (void*) _hashmap_free(HASHMAP_BASE(h), free, free);
-}
-static inline OrderedHashmap* ordered_hashmap_free_free_free(OrderedHashmap *h) {
- return (void*) _hashmap_free(HASHMAP_BASE(h), free, free);
+ return (void*) _hashmap_free(HASHMAP_BASE(h));
}
IteratedCache* iterated_cache_free(IteratedCache *cache);
@@ -285,33 +266,12 @@ static inline bool ordered_hashmap_iterate(OrderedHashmap *h, Iterator *i, void
return _hashmap_iterate(HASHMAP_BASE(h), i, value, key);
}
-void _hashmap_clear(HashmapBase *h, free_func_t default_free_key, free_func_t default_free_value);
+void _hashmap_clear(HashmapBase *h);
static inline void hashmap_clear(Hashmap *h) {
- _hashmap_clear(HASHMAP_BASE(h), NULL, NULL);
+ _hashmap_clear(HASHMAP_BASE(h));
}
static inline void ordered_hashmap_clear(OrderedHashmap *h) {
- _hashmap_clear(HASHMAP_BASE(h), NULL, NULL);
-}
-
-static inline void hashmap_clear_free(Hashmap *h) {
- _hashmap_clear(HASHMAP_BASE(h), NULL, free);
-}
-static inline void ordered_hashmap_clear_free(OrderedHashmap *h) {
- _hashmap_clear(HASHMAP_BASE(h), NULL, free);
-}
-
-static inline void hashmap_clear_free_key(Hashmap *h) {
- _hashmap_clear(HASHMAP_BASE(h), free, NULL);
-}
-static inline void ordered_hashmap_clear_free_key(OrderedHashmap *h) {
- _hashmap_clear(HASHMAP_BASE(h), free, NULL);
-}
-
-static inline void hashmap_clear_free_free(Hashmap *h) {
- _hashmap_clear(HASHMAP_BASE(h), free, free);
-}
-static inline void ordered_hashmap_clear_free_free(OrderedHashmap *h) {
- _hashmap_clear(HASHMAP_BASE(h), free, free);
+ _hashmap_clear(HASHMAP_BASE(h));
}
/*
@@ -371,27 +331,6 @@ static inline void *ordered_hashmap_first_key(OrderedHashmap *h) {
return _hashmap_first_key(HASHMAP_BASE(h), false);
}
-#define hashmap_clear_with_destructor(h, f) \
- ({ \
- Hashmap *_h = (h); \
- void *_item; \
- while ((_item = hashmap_steal_first(_h))) \
- f(_item); \
- _h; \
- })
-#define hashmap_free_with_destructor(h, f) \
- hashmap_free(hashmap_clear_with_destructor(h, f))
-#define ordered_hashmap_clear_with_destructor(h, f) \
- ({ \
- OrderedHashmap *_h = (h); \
- void *_item; \
- while ((_item = ordered_hashmap_steal_first(_h))) \
- f(_item); \
- _h; \
- })
-#define ordered_hashmap_free_with_destructor(h, f) \
- ordered_hashmap_free(ordered_hashmap_clear_with_destructor(h, f))
-
/* no hashmap_next */
void* ordered_hashmap_next(OrderedHashmap *h, const void *key);
@@ -459,20 +398,10 @@ static inline int ordered_hashmap_dump_keys_sorted(OrderedHashmap *h, void ***re
_ORDERED_HASHMAP_FOREACH_KEY(e, k, h, UNIQ_T(i, UNIQ))
DEFINE_TRIVIAL_CLEANUP_FUNC(Hashmap*, hashmap_free);
-DEFINE_TRIVIAL_CLEANUP_FUNC(Hashmap*, hashmap_free_free);
-DEFINE_TRIVIAL_CLEANUP_FUNC(Hashmap*, hashmap_free_free_key);
-DEFINE_TRIVIAL_CLEANUP_FUNC(Hashmap*, hashmap_free_free_free);
DEFINE_TRIVIAL_CLEANUP_FUNC(OrderedHashmap*, ordered_hashmap_free);
-DEFINE_TRIVIAL_CLEANUP_FUNC(OrderedHashmap*, ordered_hashmap_free_free);
-DEFINE_TRIVIAL_CLEANUP_FUNC(OrderedHashmap*, ordered_hashmap_free_free_key);
-DEFINE_TRIVIAL_CLEANUP_FUNC(OrderedHashmap*, ordered_hashmap_free_free_free);
#define _cleanup_hashmap_free_ _cleanup_(hashmap_freep)
-#define _cleanup_hashmap_free_free_ _cleanup_(hashmap_free_freep)
-#define _cleanup_hashmap_free_free_free_ _cleanup_(hashmap_free_free_freep)
#define _cleanup_ordered_hashmap_free_ _cleanup_(ordered_hashmap_freep)
-#define _cleanup_ordered_hashmap_free_free_ _cleanup_(ordered_hashmap_free_freep)
-#define _cleanup_ordered_hashmap_free_free_free_ _cleanup_(ordered_hashmap_free_free_freep)
DEFINE_TRIVIAL_CLEANUP_FUNC(IteratedCache*, iterated_cache_free);
diff --git a/src/libnm-systemd-shared/src/basic/hexdecoct.h b/src/libnm-systemd-shared/src/basic/hexdecoct.h
index d160ca28c9..b456200e1b 100644
--- a/src/libnm-systemd-shared/src/basic/hexdecoct.h
+++ b/src/libnm-systemd-shared/src/basic/hexdecoct.h
@@ -17,8 +17,8 @@ int undecchar(char c) _const_;
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 **ret_data, size_t *ret_size);
+char* hexmem(const void *p, size_t l) _nonnull_if_nonzero_(1, 2);
+int unhexmem_full(const char *p, size_t l, bool secure, void **ret_data, size_t *ret_size) _nonnull_if_nonzero_(1, 2);
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);
}
@@ -30,10 +30,10 @@ char base64char(int x) _const_;
char urlsafe_base64char(int x) _const_;
int unbase64char(char c) _const_;
-char* base32hexmem(const void *p, size_t l, bool padding);
-int unbase32hexmem(const char *p, size_t l, bool padding, void **mem, size_t *len);
+char* base32hexmem(const void *p, size_t l, bool padding) _nonnull_if_nonzero_(1, 2);
+int unbase32hexmem(const char *p, size_t l, bool padding, void **mem, size_t *len) _nonnull_if_nonzero_(1, 2);
-ssize_t base64mem_full(const void *p, size_t l, size_t line_break, char **ret);
+ssize_t base64mem_full(const void *p, size_t l, size_t line_break, char **ret) _nonnull_if_nonzero_(1, 2);
static inline ssize_t base64mem(const void *p, size_t l, char **ret) {
return base64mem_full(p, l, SIZE_MAX, ret);
}
@@ -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 **ret_data, size_t *ret_size);
+int unbase64mem_full(const char *p, size_t l, bool secure, void **ret_data, size_t *ret_size) _nonnull_if_nonzero_(1, 2);
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);
+void hexdump(FILE *f, const void *p, size_t s) _nonnull_if_nonzero_(2, 3);
diff --git a/src/libnm-systemd-shared/src/basic/hostname-util.c b/src/libnm-systemd-shared/src/basic/hostname-util.c
index 26e196f35e..eed3bf0aba 100644
--- a/src/libnm-systemd-shared/src/basic/hostname-util.c
+++ b/src/libnm-systemd-shared/src/basic/hostname-util.c
@@ -12,18 +12,22 @@
#include "alloc-util.h"
#include "env-file.h"
#include "hostname-util.h"
+#include "log.h"
#include "os-util.h"
#include "string-util.h"
#include "strv.h"
#if 0 /* NM_IGNORED */
-char* get_default_hostname(void) {
+char* get_default_hostname_raw(void) {
int r;
+ /* Returns the default hostname, and leaves any ??? in place. */
+
const char *e = secure_getenv("SYSTEMD_DEFAULT_HOSTNAME");
if (e) {
- if (hostname_is_valid(e, 0))
+ if (hostname_is_valid(e, VALID_HOSTNAME_QUESTION_MARK))
return strdup(e);
+
log_debug("Invalid hostname in $SYSTEMD_DEFAULT_HOSTNAME, ignoring: %s", e);
}
@@ -32,48 +36,14 @@ char* get_default_hostname(void) {
if (r < 0)
log_debug_errno(r, "Failed to parse os-release, ignoring: %m");
else if (f) {
- if (hostname_is_valid(f, 0))
+ if (hostname_is_valid(f, VALID_HOSTNAME_QUESTION_MARK))
return TAKE_PTR(f);
+
log_debug("Invalid hostname in os-release, ignoring: %s", f);
}
return strdup(FALLBACK_HOSTNAME);
}
-
-int gethostname_full(GetHostnameFlags flags, char **ret) {
- _cleanup_free_ char *buf = NULL, *fallback = NULL;
- struct utsname u;
- const char *s;
-
- assert(ret);
-
- assert_se(uname(&u) >= 0);
-
- s = u.nodename;
- if (isempty(s) || streq(s, "(none)") ||
- (!FLAGS_SET(flags, GET_HOSTNAME_ALLOW_LOCALHOST) && is_localhost(s)) ||
- (FLAGS_SET(flags, GET_HOSTNAME_SHORT) && s[0] == '.')) {
- if (!FLAGS_SET(flags, GET_HOSTNAME_FALLBACK_DEFAULT))
- return -ENXIO;
-
- s = fallback = get_default_hostname();
- if (!s)
- return -ENOMEM;
-
- if (FLAGS_SET(flags, GET_HOSTNAME_SHORT) && s[0] == '.')
- return -ENXIO;
- }
-
- if (FLAGS_SET(flags, GET_HOSTNAME_SHORT))
- buf = strdupcspn(s, ".");
- else
- buf = strdup(s);
- if (!buf)
- return -ENOMEM;
-
- *ret = TAKE_PTR(buf);
- return 0;
-}
#endif /* NM_IGNORED */
bool valid_ldh_char(char c) {
@@ -120,7 +90,7 @@ bool hostname_is_valid(const char *s, ValidHostnameFlags flags) {
hyphen = true;
} else {
- if (!valid_ldh_char(*p))
+ if (!valid_ldh_char(*p) && (*p != '?' || !FLAGS_SET(flags, VALID_HOSTNAME_QUESTION_MARK)))
return false;
dot = false;
@@ -163,7 +133,7 @@ char* hostname_cleanup(char *s) {
dot = false;
hyphen = true;
- } else if (valid_ldh_char(*p)) {
+ } else if (valid_ldh_char(*p) || *p == '?') {
*(d++) = *p;
dot = false;
hyphen = false;
diff --git a/src/libnm-systemd-shared/src/basic/hostname-util.h b/src/libnm-systemd-shared/src/basic/hostname-util.h
index bcac3d9fb0..4c5abe760f 100644
--- a/src/libnm-systemd-shared/src/basic/hostname-util.h
+++ b/src/libnm-systemd-shared/src/basic/hostname-util.h
@@ -7,42 +7,14 @@
#include "macro.h"
#include "strv.h"
-typedef enum GetHostnameFlags {
- GET_HOSTNAME_ALLOW_LOCALHOST = 1 << 0, /* accepts "localhost" or friends. */
- GET_HOSTNAME_FALLBACK_DEFAULT = 1 << 1, /* use default hostname if no hostname is set. */
- GET_HOSTNAME_SHORT = 1 << 2, /* kills the FQDN part if present. */
-} GetHostnameFlags;
-
-int gethostname_full(GetHostnameFlags flags, char **ret);
-static inline int gethostname_strict(char **ret) {
- return gethostname_full(0, ret);
-}
-
-static inline char* gethostname_malloc(void) {
- char *s;
-
- if (gethostname_full(GET_HOSTNAME_ALLOW_LOCALHOST | GET_HOSTNAME_FALLBACK_DEFAULT, &s) < 0)
- return NULL;
-
- return s;
-}
-
-static inline char* gethostname_short_malloc(void) {
- char *s;
-
- if (gethostname_full(GET_HOSTNAME_ALLOW_LOCALHOST | GET_HOSTNAME_FALLBACK_DEFAULT | GET_HOSTNAME_SHORT, &s) < 0)
- return NULL;
-
- return s;
-}
-
-char* get_default_hostname(void);
+char* get_default_hostname_raw(void);
bool valid_ldh_char(char c) _const_;
typedef enum ValidHostnameFlags {
- VALID_HOSTNAME_TRAILING_DOT = 1 << 0, /* Accept trailing dot on multi-label names */
- VALID_HOSTNAME_DOT_HOST = 1 << 1, /* Accept ".host" as valid hostname */
+ VALID_HOSTNAME_TRAILING_DOT = 1 << 0, /* Accept trailing dot on multi-label names */
+ VALID_HOSTNAME_DOT_HOST = 1 << 1, /* Accept ".host" as valid hostname */
+ VALID_HOSTNAME_QUESTION_MARK = 1 << 2, /* Accept "?" as place holder for hashed machine ID value */
} ValidHostnameFlags;
bool hostname_is_valid(const char *s, ValidHostnameFlags flags) _pure_;
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 44445f1e43..b7a64b1821 100644
--- a/src/libnm-systemd-shared/src/basic/in-addr-util.c
+++ b/src/libnm-systemd-shared/src/basic/in-addr-util.c
@@ -15,6 +15,7 @@
#include "in-addr-util.h"
#include "logarithm.h"
#include "macro.h"
+#include "memory-util.h"
#include "parse-util.h"
#include "random-util.h"
#include "stdio-util.h"
@@ -30,7 +31,7 @@ bool in4_addr_is_null(const struct in_addr *a) {
bool in6_addr_is_null(const struct in6_addr *a) {
assert(a);
- return IN6_IS_ADDR_UNSPECIFIED(a);
+ return eqzero(a->s6_addr32);
}
int in_addr_is_null(int family, const union in_addr_union *u) {
@@ -68,7 +69,7 @@ bool in4_addr_is_link_local_dynamic(const struct in_addr *a) {
bool in6_addr_is_link_local(const struct in6_addr *a) {
assert(a);
- return IN6_IS_ADDR_LINKLOCAL(a);
+ return (a->s6_addr32[0] & htobe32(0xffc00000)) == htobe32(0xfe800000);
}
int in_addr_is_link_local(int family, const union in_addr_union *u) {
@@ -102,7 +103,7 @@ bool in4_addr_is_multicast(const struct in_addr *a) {
bool in6_addr_is_multicast(const struct in6_addr *a) {
assert(a);
- return IN6_IS_ADDR_MULTICAST(a);
+ return a->s6_addr[0] == 0xff;
}
int in_addr_is_multicast(int family, const union in_addr_union *u) {
@@ -138,6 +139,10 @@ bool in4_addr_is_non_local(const struct in_addr *a) {
!in4_addr_is_localhost(a);
}
+static bool in6_addr_is_loopback(const struct in6_addr *a) {
+ return memcmp(a, &(struct in6_addr) IN6ADDR_LOOPBACK_INIT, sizeof(struct in6_addr)) == 0;
+}
+
int in_addr_is_localhost(int family, const union in_addr_union *u) {
assert(u);
@@ -145,7 +150,7 @@ int in_addr_is_localhost(int family, const union in_addr_union *u) {
return in4_addr_is_localhost(&u->in);
if (family == AF_INET6)
- return IN6_IS_ADDR_LOOPBACK(&u->in6);
+ return in6_addr_is_loopback(&u->in6);
return -EAFNOSUPPORT;
}
@@ -158,7 +163,7 @@ int in_addr_is_localhost_one(int family, const union in_addr_union *u) {
return be32toh(u->in.s_addr) == UINT32_C(0x7F000001);
if (family == AF_INET6)
- return IN6_IS_ADDR_LOOPBACK(&u->in6);
+ return in6_addr_is_loopback(&u->in6);
return -EAFNOSUPPORT;
}
@@ -180,7 +185,7 @@ bool in6_addr_equal(const struct in6_addr *a, const struct in6_addr *b) {
assert(a);
assert(b);
- return IN6_ARE_ADDR_EQUAL(a, b);
+ return memcmp(a, b, sizeof(struct in6_addr)) == 0;
}
int in_addr_equal(int family, const union in_addr_union *a, const union in_addr_union *b) {
@@ -951,7 +956,6 @@ int in_addr_prefix_from_string_auto_full(
*ret_prefixlen = k;
return 0;
-
}
void in_addr_hash_func(const union in_addr_union *u, int family, struct siphash *state) {
diff --git a/src/libnm-systemd-shared/src/basic/include/net/if.h b/src/libnm-systemd-shared/src/basic/include/net/if.h
new file mode 100644
index 0000000000..7d5b61ba06
--- /dev/null
+++ b/src/libnm-systemd-shared/src/basic/include/net/if.h
@@ -0,0 +1,9 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+#pragma once
+
+#include
+
+#define IF_NAMESIZE 16
+
+extern unsigned int if_nametoindex(const char *__ifname) __THROW;
+extern char *if_indextoname(unsigned int __ifindex, char __ifname[IF_NAMESIZE]) __THROW;
diff --git a/src/libnm-systemd-shared/src/basic/include/netinet/in.h b/src/libnm-systemd-shared/src/basic/include/netinet/in.h
new file mode 100644
index 0000000000..97475ac882
--- /dev/null
+++ b/src/libnm-systemd-shared/src/basic/include/netinet/in.h
@@ -0,0 +1,19 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+#pragma once
+
+#include
+#include
+#include
+#include
+#include
+#include
+
+#define INET_ADDRSTRLEN 16
+#define INET6_ADDRSTRLEN 46
+
+extern const struct in6_addr in6addr_any; /* :: */
+extern const struct in6_addr in6addr_loopback; /* ::1 */
+#define IN6ADDR_ANY_INIT { { { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 } } }
+#define IN6ADDR_LOOPBACK_INIT { { { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1 } } }
+
+typedef uint32_t in_addr_t;
diff --git a/src/libnm-systemd-shared/src/basic/io-util.c b/src/libnm-systemd-shared/src/basic/io-util.c
index abe61ed56c..706535a10d 100644
--- a/src/libnm-systemd-shared/src/basic/io-util.c
+++ b/src/libnm-systemd-shared/src/basic/io-util.c
@@ -90,6 +90,7 @@ ssize_t loop_read(int fd, void *buf, size_t nbytes, bool do_poll) {
return n;
assert((size_t) k <= nbytes);
+ assert(k <= SSIZE_MAX - n);
p += k;
nbytes -= k;
@@ -194,7 +195,7 @@ int pipe_eof(int fd) {
}
#endif /* NM_IGNORED */
-int ppoll_usec(struct pollfd *fds, size_t nfds, usec_t timeout) {
+int ppoll_usec_full(struct pollfd *fds, size_t nfds, usec_t timeout, const sigset_t *ss) {
int r;
assert(fds || nfds == 0);
@@ -214,10 +215,10 @@ int ppoll_usec(struct pollfd *fds, size_t nfds, usec_t timeout) {
* to handle signals, such as signalfd() or signal handlers. ⚠️ ⚠️ ⚠️
*/
- if (nfds == 0)
+ if (nfds == 0 && timeout == 0)
return 0;
- r = ppoll(fds, nfds, timeout == USEC_INFINITY ? NULL : TIMESPEC_STORE(timeout), NULL);
+ r = ppoll(fds, nfds, timeout == USEC_INFINITY ? NULL : TIMESPEC_STORE(timeout), ss);
if (r < 0)
return -errno;
if (r == 0)
diff --git a/src/libnm-systemd-shared/src/basic/io-util.h b/src/libnm-systemd-shared/src/basic/io-util.h
index e027c1a878..208e168317 100644
--- a/src/libnm-systemd-shared/src/basic/io-util.h
+++ b/src/libnm-systemd-shared/src/basic/io-util.h
@@ -15,14 +15,18 @@ 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_full(int fd, const void *buf, size_t nbytes, usec_t timeout);
+int loop_write_full(int fd, const void *buf, size_t nbytes, usec_t timeout) _nonnull_if_nonzero_(2, 3);
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);
-int ppoll_usec(struct pollfd *fds, size_t nfds, usec_t timeout);
+int ppoll_usec_full(struct pollfd *fds, size_t nfds, usec_t timeout, const sigset_t *ss) _nonnull_if_nonzero_(1, 2);
+_nonnull_if_nonzero_(1, 2) static inline int ppoll_usec(struct pollfd *fds, size_t nfds, usec_t timeout) {
+ return ppoll_usec_full(fds, nfds, timeout, NULL);
+}
+
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);
@@ -42,5 +46,4 @@ static inline bool FILE_SIZE_VALID_OR_INFINITY(uint64_t l) {
return true;
return FILE_SIZE_VALID(l);
-
}
diff --git a/src/libnm-systemd-shared/src/basic/iovec-util.h b/src/libnm-systemd-shared/src/basic/iovec-util.h
index 868454040b..ace20098c8 100644
--- a/src/libnm-systemd-shared/src/basic/iovec-util.h
+++ b/src/libnm-systemd-shared/src/basic/iovec-util.h
@@ -12,9 +12,9 @@
extern const struct iovec iovec_nul_byte; /* Points to a single NUL byte */
extern const struct iovec iovec_empty; /* Points to an empty, but valid (i.e. non-NULL) pointer */
-size_t iovec_total_size(const struct iovec *iovec, size_t n);
+size_t iovec_total_size(const struct iovec *iovec, size_t n) _nonnull_if_nonzero_(1, 2);
-bool iovec_increment(struct iovec *iovec, size_t n, size_t k);
+bool iovec_increment(struct iovec *iovec, size_t n, size_t k) _nonnull_if_nonzero_(1, 2);
static inline struct iovec* iovec_make_string(struct iovec *iovec, const char *s) {
assert(iovec);
@@ -42,7 +42,7 @@ static inline void iovec_done_erase(struct iovec *iovec) {
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);
+void iovec_array_free(struct iovec *iovec, size_t n_iovec) _nonnull_if_nonzero_(1, 2);
static inline int iovec_memcmp(const struct iovec *a, const struct iovec *b) {
@@ -55,7 +55,7 @@ static inline int iovec_memcmp(const struct iovec *a, const struct iovec *b) {
b ? b->iov_len : 0);
}
-static inline struct iovec *iovec_memdup(const struct iovec *source, struct iovec *ret) {
+static inline struct iovec* iovec_memdup(const struct iovec *source, struct iovec *ret) {
assert(ret);
if (!iovec_is_set(source))
diff --git a/src/libnm-systemd-shared/src/basic/list.h b/src/libnm-systemd-shared/src/basic/list.h
index 10e69541d4..090bdc7b60 100644
--- a/src/libnm-systemd-shared/src/basic/list.h
+++ b/src/libnm-systemd-shared/src/basic/list.h
@@ -1,6 +1,8 @@
/* SPDX-License-Identifier: LGPL-2.1-or-later */
#pragma once
+#include "macro.h"
+
/* The head of the linked list. Use this in the structure that shall
* contain the head of the linked list */
#define LIST_HEAD(t,name) \
@@ -203,7 +205,3 @@
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 0654fde835..d491d81f46 100644
--- a/src/libnm-systemd-shared/src/basic/locale-util.c
+++ b/src/libnm-systemd-shared/src/basic/locale-util.c
@@ -19,8 +19,9 @@
#include "fileio.h"
#include "hashmap.h"
#include "locale-util.h"
-#include "missing_syscall.h"
+#include "log.h"
#include "path-util.h"
+#include "process-util.h"
#include "set.h"
#include "string-table.h"
#include "string-util.h"
@@ -28,7 +29,7 @@
#include "utf8.h"
#if 0 /* NM_IGNORED */
-static char *normalize_locale(const char *name) {
+static char* normalize_locale(const char *name) {
const char *e;
/* Locale names are weird: glibc has some magic rules when looking for the charset name on disk: it
@@ -96,18 +97,15 @@ static int add_locales_from_archive(Set *locales) {
uint32_t locrec_offset;
};
- const struct locarhead *h;
- const struct namehashent *e;
- const void *p = MAP_FAILED;
- _cleanup_close_ int fd = -EBADF;
- size_t sz = 0;
- struct stat st;
int r;
- fd = open("/usr/lib/locale/locale-archive", O_RDONLY|O_NOCTTY|O_CLOEXEC);
+ assert(locales);
+
+ _cleanup_close_ int fd = open("/usr/lib/locale/locale-archive", O_RDONLY|O_NOCTTY|O_CLOEXEC);
if (fd < 0)
return errno == ENOENT ? 0 : -errno;
+ struct stat st;
if (fstat(fd, &st) < 0)
return -errno;
@@ -120,11 +118,12 @@ static int add_locales_from_archive(Set *locales) {
if (file_offset_beyond_memory_size(st.st_size))
return -EFBIG;
- p = mmap(NULL, st.st_size, PROT_READ, MAP_SHARED, fd, 0);
+ void *p = mmap(NULL, st.st_size, PROT_READ, MAP_SHARED, fd, 0);
if (p == MAP_FAILED)
return -errno;
- h = (const struct locarhead *) p;
+ const struct namehashent *e;
+ const struct locarhead *h = p;
if (h->magic != 0xde020109 ||
h->namehash_offset + h->namehash_size > st.st_size ||
h->string_offset + h->string_size > st.st_size ||
@@ -157,9 +156,9 @@ static int add_locales_from_archive(Set *locales) {
r = 0;
- finish:
+finish:
if (p != MAP_FAILED)
- munmap((void*) p, sz);
+ munmap((void*) p, st.st_size);
return r;
}
@@ -168,6 +167,8 @@ static int add_locales_from_libdir(Set *locales) {
_cleanup_closedir_ DIR *dir = NULL;
int r;
+ assert(locales);
+
dir = opendir("/usr/lib/locale");
if (!dir)
return errno == ENOENT ? 0 : -errno;
@@ -183,7 +184,7 @@ static int add_locales_from_libdir(Set *locales) {
return -ENOMEM;
r = set_consume(locales, z);
- if (r < 0 && r != -EEXIST)
+ if (r < 0)
return r;
}
@@ -191,11 +192,10 @@ static int add_locales_from_libdir(Set *locales) {
}
int get_locales(char ***ret) {
- _cleanup_set_free_free_ Set *locales = NULL;
- _cleanup_strv_free_ char **l = NULL;
+ _cleanup_set_free_ Set *locales = NULL;
int r;
- locales = set_new(&string_hash_ops);
+ locales = set_new(&string_hash_ops_free);
if (!locales)
return -ENOMEM;
@@ -216,31 +216,25 @@ int get_locales(char ***ret) {
free(set_remove(locales, locale));
}
- l = set_get_strv(locales);
+ _cleanup_strv_free_ char **l = set_to_strv(&locales);
if (!l)
return -ENOMEM;
- /* Now, all elements are owned by strv 'l'. Hence, do not call set_free_free(). */
- locales = set_free(locales);
-
r = getenv_bool("SYSTEMD_LIST_NON_UTF8_LOCALES");
- if (IN_SET(r, -ENXIO, 0)) {
- char **a, **b;
+ if (r <= 0) {
+ if (!IN_SET(r, -ENXIO, 0))
+ log_debug_errno(r, "Failed to parse $SYSTEMD_LIST_NON_UTF8_LOCALES as boolean, ignoring: %m");
/* Filter out non-UTF-8 locales, because it's 2019, by default */
- for (a = b = l; *a; a++) {
-
- if (endswith(*a, "UTF-8") ||
- strstr(*a, ".UTF-8@"))
+ char **b = l;
+ STRV_FOREACH(a, l)
+ if (endswith(*a, "UTF-8") || strstr(*a, ".UTF-8@"))
*(b++) = *a;
else
free(*a);
- }
*b = NULL;
-
- } else if (r < 0)
- log_debug_errno(r, "Failed to parse $SYSTEMD_LIST_NON_UTF8_LOCALES as boolean");
+ }
strv_sort(l);
@@ -287,64 +281,48 @@ int locale_is_installed(const char *name) {
}
#endif /* NM_IGNORED */
-bool is_locale_utf8(void) {
- static int cached_answer = -1;
+static bool is_locale_utf8_impl(void) {
const char *set;
int r;
- /* Note that we default to 'true' here, since today UTF8 is
- * pretty much supported everywhere. */
-
- if (cached_answer >= 0)
- goto out;
+ /* Note that we default to 'true' here, since today UTF8 is pretty much supported everywhere. */
r = secure_getenv_bool("SYSTEMD_UTF8");
- if (r >= 0) {
- cached_answer = r;
- goto out;
- } else if (r != -ENXIO)
+ if (r >= 0)
+ return r;
+ 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 (!is_main_thread())
+ return true;
- if (!setlocale(LC_ALL, "")) {
- cached_answer = true;
- goto out;
- }
+ if (!setlocale(LC_ALL, ""))
+ return true;
set = nl_langinfo(CODESET);
- if (!set) {
- cached_answer = true;
- goto out;
- }
+ if (!set || streq(set, "UTF-8"))
+ return true;
- if (streq(set, "UTF-8")) {
- cached_answer = true;
- goto out;
- }
-
- /* For LC_CTYPE=="C" return true, because CTYPE is effectively
- * unset and everything can do to UTF-8 nowadays. */
set = setlocale(LC_CTYPE, NULL);
- if (!set) {
- cached_answer = true;
- goto out;
- }
+ if (!set)
+ return true;
- /* Check result, but ignore the result if C was set
- * explicitly. */
- cached_answer =
- STR_IN_SET(set, "C", "POSIX") &&
+ /* Unless LC_CTYPE is explicitly overridden, return true. Because here CTYPE is effectively unset
+ * and everything can do to UTF-8 nowadays. */
+ return STR_IN_SET(set, "C", "POSIX") &&
!getenv("LC_ALL") &&
!getenv("LC_CTYPE") &&
!getenv("LANG");
+}
-out:
- return (bool) cached_answer;
+bool is_locale_utf8(void) {
+ static int cached = -1;
+
+ if (cached < 0)
+ cached = is_locale_utf8_impl();
+
+ return cached;
}
#if 0 /* NM_IGNORED */
diff --git a/src/libnm-systemd-shared/src/basic/lock-util.h b/src/libnm-systemd-shared/src/basic/lock-util.h
index a67d8b2c93..b327b8e561 100644
--- a/src/libnm-systemd-shared/src/basic/lock-util.h
+++ b/src/libnm-systemd-shared/src/basic/lock-util.h
@@ -5,6 +5,8 @@
/* Include here so consumers have LOCK_{EX,SH,NB} available. */
#include
+#include "time-util.h"
+
typedef struct LockFile {
int dir_fd;
char *path;
diff --git a/src/libnm-systemd-shared/src/basic/log.h b/src/libnm-systemd-shared/src/basic/log.h
index 6477bff449..4741f58030 100644
--- a/src/libnm-systemd-shared/src/basic/log.h
+++ b/src/libnm-systemd-shared/src/basic/log.h
@@ -7,10 +7,7 @@
#include
#include
-#include "list.h"
#include "macro.h"
-#include "ratelimit.h"
-#include "stdio-util.h"
/* Some structures we reference but don't want to pull in headers for */
struct iovec;
@@ -90,13 +87,6 @@ int log_show_tid_from_string(const char *e);
* environment should not be called from library code — this is always a job
* for the application itself. */
-#if 0 /* NM_IGNORED */
-assert_cc(STRLEN(__FILE__) > STRLEN(RELATIVE_SOURCE_PATH) + 1);
-#define PROJECT_FILE (&__FILE__[STRLEN(RELATIVE_SOURCE_PATH) + 1])
-#else /* NM_IGNORED */
-#define PROJECT_FILE __FILE__
-#endif /* NM_IGNORED */
-
bool stderr_is_journal(void);
int log_open(void);
void log_close(void);
@@ -224,7 +214,7 @@ int log_struct_internal(
const char *file,
int line,
const char *func,
- const char *format, ...) _printf_(6,0) _sentinel_;
+ const char *format, ...) _sentinel_;
#endif /* NM_IGNORED */
#if 0 /* NM_IGNORED */
@@ -293,53 +283,6 @@ _noreturn_ void log_assert_failed(
G_STMT_END
#endif /* NM_IGNORED */
-#if 0 /* NM_IGNORED */
-_noreturn_ void log_assert_failed_unreachable(
- const char *file,
- int line,
- const char *func);
-#else /* NM_IGNORED */
-#define log_assert_failed_unreachable(file, line, func) \
- G_STMT_START \
- { \
- log_internal(LOG_CRIT, \
- 0, \
- file, \
- line, \
- func, \
- "Code should not be reached at %s:%u, function %s(). Aborting.", \
- file, \
- line, \
- func); \
- g_assert_not_reached(); \
- } \
- G_STMT_END
-#endif /* NM_IGNORED */
-
-#if 0 /* NM_IGNORED */
-void log_assert_failed_return(
- const char *text,
- const char *file,
- int line,
- const char *func);
-#else /* NM_IGNORED */
-#define log_assert_failed_return(text, file, line, func) \
- ({ \
- log_internal(LOG_DEBUG, \
- 0, \
- file, \
- line, \
- func, \
- "Assertion '%s' failed at %s:%u, function %s(). Ignoring.", \
- text, \
- file, \
- line, \
- func); \
- g_return_if_fail_warning(G_LOG_DOMAIN, G_STRFUNC, text); \
- (void) 0; \
- })
-#endif /* NM_IGNORED */
-
#if 0 /* NM_IGNORED */
#define log_dispatch(level, error, buffer) \
log_dispatch_internal(level, error, PROJECT_FILE, __LINE__, __func__, NULL, NULL, NULL, NULL, buffer)
@@ -450,11 +393,15 @@ bool log_on_console(void) _pure_;
/* Do a fake formatting of the message string to let the scanner verify the arguments against the format
* message. The variable will never be set to true, but we don't tell the compiler that :) */
extern bool _log_message_dummy;
-# define LOG_MESSAGE(fmt, ...) "MESSAGE=%.0d" fmt, (_log_message_dummy && printf(fmt, ##__VA_ARGS__)), ##__VA_ARGS__
+# define LOG_ITEM(fmt, ...) "%.0d" fmt, (_log_message_dummy && printf(fmt, ##__VA_ARGS__)), ##__VA_ARGS__
+# define LOG_MESSAGE(fmt, ...) LOG_ITEM("MESSAGE=" fmt, ##__VA_ARGS__)
#else
+# define LOG_ITEM(fmt, ...) fmt, ##__VA_ARGS__
# define LOG_MESSAGE(fmt, ...) "MESSAGE=" fmt, ##__VA_ARGS__
#endif
+#define LOG_MESSAGE_ID(id) LOG_ITEM("MESSAGE_ID=" id)
+
void log_received_signal(int level, const struct signalfd_siginfo *si);
/* If turned on, any requests for a log target involving "syslog" will be implicitly upgraded to the equivalent journal target */
@@ -471,9 +418,6 @@ 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 */
@@ -540,58 +484,6 @@ int log_syntax_parse_error_internal(
void log_setup(void);
-typedef struct LogRateLimit {
- int error;
- int level;
- RateLimit ratelimit;
-} LogRateLimit;
-
-#define log_ratelimit_internal(_level, _error, _ratelimit, _file, _line, _func, _format, ...) \
-({ \
- int _log_ratelimit_error = (_error); \
- int _log_ratelimit_level = (_level); \
- static LogRateLimit _log_ratelimit = { \
- .ratelimit = (_ratelimit), \
- }; \
- unsigned _num_dropped_errors = ratelimit_num_dropped(&_log_ratelimit.ratelimit); \
- if (_log_ratelimit_error != _log_ratelimit.error || _log_ratelimit_level != _log_ratelimit.level) { \
- ratelimit_reset(&_log_ratelimit.ratelimit); \
- _log_ratelimit.error = _log_ratelimit_error; \
- _log_ratelimit.level = _log_ratelimit_level; \
- } \
- if (log_get_max_level() == LOG_DEBUG || ratelimit_below(&_log_ratelimit.ratelimit)) \
- _log_ratelimit_error = _num_dropped_errors > 0 \
- ? log_internal(_log_ratelimit_level, _log_ratelimit_error, _file, _line, _func, _format " (Dropped %u similar message(s))", ##__VA_ARGS__, _num_dropped_errors) \
- : log_internal(_log_ratelimit_level, _log_ratelimit_error, _file, _line, _func, _format, ##__VA_ARGS__); \
- _log_ratelimit_error; \
-})
-
-#define log_ratelimit_full_errno(level, error, _ratelimit, format, ...) \
- ({ \
- int _level = (level), _e = (error); \
- _e = (log_get_max_level() >= LOG_PRI(_level)) \
- ? log_ratelimit_internal(_level, _e, _ratelimit, PROJECT_FILE, __LINE__, __func__, format, ##__VA_ARGS__) \
- : -ERRNO_VALUE(_e); \
- _e < 0 ? _e : -ESTRPIPE; \
- })
-
-#define log_ratelimit_full(level, _ratelimit, format, ...) \
- log_ratelimit_full_errno(level, 0, _ratelimit, format, ##__VA_ARGS__)
-
-/* Normal logging */
-#define log_ratelimit_info(...) log_ratelimit_full(LOG_INFO, __VA_ARGS__)
-#define log_ratelimit_notice(...) log_ratelimit_full(LOG_NOTICE, __VA_ARGS__)
-#define log_ratelimit_warning(...) log_ratelimit_full(LOG_WARNING, __VA_ARGS__)
-#define log_ratelimit_error(...) log_ratelimit_full(LOG_ERR, __VA_ARGS__)
-#define log_ratelimit_emergency(...) log_ratelimit_full(log_emergency_level(), __VA_ARGS__)
-
-/* Logging triggered by an errno-like error */
-#define log_ratelimit_info_errno(error, ...) log_ratelimit_full_errno(LOG_INFO, error, __VA_ARGS__)
-#define log_ratelimit_notice_errno(error, ...) log_ratelimit_full_errno(LOG_NOTICE, error, __VA_ARGS__)
-#define log_ratelimit_warning_errno(error, ...) log_ratelimit_full_errno(LOG_WARNING, error, __VA_ARGS__)
-#define log_ratelimit_error_errno(error, ...) log_ratelimit_full_errno(LOG_ERR, error, __VA_ARGS__)
-#define log_ratelimit_emergency_errno(error, ...) log_ratelimit_full_errno(log_emergency_level(), error, __VA_ARGS__)
-
const char* _log_set_prefix(const char *prefix, bool force);
static inline const char* _log_unset_prefixp(const char **p) {
assert(p);
@@ -601,112 +493,3 @@ static inline const char* _log_unset_prefixp(const char **p) {
#define LOG_SET_PREFIX(prefix) \
_cleanup_(_log_unset_prefixp) _unused_ const char *CONCATENATE(_cleanup_log_unset_prefix_, UNIQ) = _log_set_prefix(prefix, false);
-
-/*
- * The log context allows attaching extra metadata to log messages written to the journal via log.h. We keep
- * track of a thread local log context onto which we can push extra metadata fields that should be logged.
- *
- * LOG_CONTEXT_PUSH() will add the provided field to the log context and will remove it again when the
- * current block ends. LOG_CONTEXT_PUSH_STRV() will do the same but for all fields in the given strv.
- * LOG_CONTEXT_PUSHF() is like LOG_CONTEXT_PUSH() but takes a format string and arguments.
- *
- * Using the macros is as simple as putting them anywhere inside a block to add a field to all following log
- * messages logged from inside that block.
- *
- * void myfunction(...) {
- * ...
- *
- * LOG_CONTEXT_PUSHF("MYMETADATA=%s", "abc");
- *
- * // Every journal message logged will now have the MYMETADATA=abc
- * // field included.
- * }
- *
- * One special case to note is async code, where we use callbacks that are invoked to continue processing
- * when some event occurs. For async code, there's usually an associated "userdata" struct containing all the
- * information associated with the async operation. In this "userdata" struct, we can store a log context
- * allocated with log_context_new() and freed with log_context_free(). We can then add and remove fields to
- * the `fields` member of the log context object and all those fields will be logged along with each log
- * message.
- */
-
-typedef struct LogContext LogContext;
-
-bool log_context_enabled(void);
-
-LogContext* log_context_new(const char *key, const char *value);
-LogContext* log_context_new_strv(char **fields, bool owned);
-LogContext* log_context_new_iov(struct iovec *input_iovec, size_t n_input_iovec, bool owned);
-
-/* Same as log_context_new(), but frees the given fields strv/iovec on failure. */
-LogContext* log_context_new_strv_consume(char **fields);
-LogContext* log_context_new_iov_consume(struct iovec *input_iovec, size_t n_input_iovec);
-
-LogContext *log_context_ref(LogContext *c);
-LogContext *log_context_unref(LogContext *c);
-
-DEFINE_TRIVIAL_CLEANUP_FUNC(LogContext*, log_context_unref);
-
-/* Returns the number of attached log context objects. */
-size_t log_context_num_contexts(void);
-/* Returns the number of fields in all attached log contexts. */
-size_t log_context_num_fields(void);
-
-static inline void _reset_log_level(int *saved_log_level) {
- assert(saved_log_level);
-
- log_set_max_level(*saved_log_level);
-}
-
-#define LOG_CONTEXT_SET_LOG_LEVEL(level) \
- _cleanup_(_reset_log_level) _unused_ int _saved_log_level_ = log_set_max_level(level);
-
-#define LOG_CONTEXT_PUSH(...) \
- LOG_CONTEXT_PUSH_STRV(STRV_MAKE(__VA_ARGS__))
-
-#define LOG_CONTEXT_PUSHF(...) \
- LOG_CONTEXT_PUSH(snprintf_ok((char[LINE_MAX]) {}, LINE_MAX, __VA_ARGS__))
-
-#define _LOG_CONTEXT_PUSH_KEY_VALUE(key, value, c) \
- _unused_ _cleanup_(log_context_unrefp) LogContext *c = log_context_new(key, value);
-
-#define LOG_CONTEXT_PUSH_KEY_VALUE(key, value) \
- _LOG_CONTEXT_PUSH_KEY_VALUE(key, value, UNIQ_T(c, UNIQ))
-
-#define _LOG_CONTEXT_PUSH_STRV(strv, c) \
- _unused_ _cleanup_(log_context_unrefp) LogContext *c = log_context_new_strv(strv, /*owned=*/ false);
-
-#define LOG_CONTEXT_PUSH_STRV(strv) \
- _LOG_CONTEXT_PUSH_STRV(strv, UNIQ_T(c, UNIQ))
-
-#define _LOG_CONTEXT_PUSH_IOV(input_iovec, n_input_iovec, c) \
- _unused_ _cleanup_(log_context_unrefp) LogContext *c = log_context_new_iov(input_iovec, n_input_iovec, /*owned=*/ false);
-
-#define LOG_CONTEXT_PUSH_IOV(input_iovec, n_input_iovec) \
- _LOG_CONTEXT_PUSH_IOV(input_iovec, n_input_iovec, UNIQ_T(c, UNIQ))
-
-/* LOG_CONTEXT_CONSUME_STR()/LOG_CONTEXT_CONSUME_STRV()/LOG_CONTEXT_CONSUME_IOV() are identical to
- * LOG_CONTEXT_PUSH_STR()/LOG_CONTEXT_PUSH_STRV()/LOG_CONTEXT_PUSH_IOV() except they take ownership of the
- * given str/strv argument.
- */
-
-#define _LOG_CONTEXT_CONSUME_STR(s, c, strv) \
- _unused_ _cleanup_strv_free_ strv = strv_new(s); \
- if (!strv) \
- free(s); \
- _unused_ _cleanup_(log_context_unrefp) LogContext *c = log_context_new_strv_consume(TAKE_PTR(strv))
-
-#define LOG_CONTEXT_CONSUME_STR(s) \
- _LOG_CONTEXT_CONSUME_STR(s, UNIQ_T(c, UNIQ), UNIQ_T(sv, UNIQ))
-
-#define _LOG_CONTEXT_CONSUME_STRV(strv, c) \
- _unused_ _cleanup_(log_context_unrefp) LogContext *c = log_context_new_strv_consume(strv);
-
-#define LOG_CONTEXT_CONSUME_STRV(strv) \
- _LOG_CONTEXT_CONSUME_STRV(strv, UNIQ_T(c, UNIQ))
-
-#define _LOG_CONTEXT_CONSUME_IOV(input_iovec, n_input_iovec, c) \
- _unused_ _cleanup_(log_context_unrefp) LogContext *c = log_context_new_iov_consume(input_iovec, n_input_iovec);
-
-#define LOG_CONTEXT_CONSUME_IOV(input_iovec, n_input_iovec) \
- _LOG_CONTEXT_CONSUME_IOV(input_iovec, n_input_iovec, UNIQ_T(c, UNIQ))
diff --git a/src/libnm-systemd-shared/src/basic/macro.h b/src/libnm-systemd-shared/src/basic/macro.h
index 026ec13637..4ee1ad4d57 100644
--- a/src/libnm-systemd-shared/src/basic/macro.h
+++ b/src/libnm-systemd-shared/src/basic/macro.h
@@ -103,76 +103,6 @@ static inline size_t GREEDY_ALLOC_ROUND_UP(size_t l) {
(type*)( (char *)UNIQ_T(A, uniq) - offsetof(type, member) ); \
})
-#ifdef __COVERITY__
-
-/* Use special definitions of assertion macros in order to prevent
- * false positives of ASSERT_SIDE_EFFECT on Coverity static analyzer
- * for uses of assert_se() and assert_return().
- *
- * These definitions make expression go through a (trivial) function
- * call to ensure they are not discarded. Also use ! or !! to ensure
- * the boolean expressions are seen as such.
- *
- * This technique has been described and recommended in:
- * https://community.synopsys.com/s/question/0D534000046Yuzb/suppressing-assertsideeffect-for-functions-that-allow-for-sideeffects
- */
-
-extern void __coverity_panic__(void);
-
-static inline void __coverity_check__(int condition) {
- if (!condition)
- __coverity_panic__();
-}
-
-static inline int __coverity_check_and_return__(int condition) {
- return condition;
-}
-
-#define assert_message_se(expr, message) __coverity_check__(!!(expr))
-
-#define assert_log(expr, message) __coverity_check_and_return__(!!(expr))
-
-#else /* ! __COVERITY__ */
-
-#define assert_message_se(expr, message) \
- do { \
- if (_unlikely_(!(expr))) \
- log_assert_failed(message, PROJECT_FILE, __LINE__, __func__); \
- } while (false)
-
-#define assert_log(expr, message) ((_likely_(expr)) \
- ? (true) \
- : (log_assert_failed_return(message, PROJECT_FILE, __LINE__, __func__), false))
-
-#endif /* __COVERITY__ */
-
-#define assert_se(expr) assert_message_se(expr, #expr)
-
-/* We override the glibc assert() here. */
-#undef assert
-#ifdef NDEBUG
-#define assert(expr) ({ if (!(expr)) __builtin_unreachable(); })
-#else
-#define assert(expr) assert_message_se(expr, #expr)
-#endif
-
-#define assert_not_reached() \
- log_assert_failed_unreachable(PROJECT_FILE, __LINE__, __func__)
-
-#define assert_return(expr, r) \
- do { \
- if (!assert_log(expr, #expr)) \
- return (r); \
- } while (false)
-
-#define assert_return_errno(expr, r, err) \
- do { \
- if (!assert_log(expr, #expr)) { \
- errno = err; \
- return (r); \
- } \
- } while (false)
-
#define return_with_errno(r, err) \
do { \
errno = abs(err); \
@@ -251,59 +181,6 @@ static inline int __coverity_check_and_return__(int condition) {
/* Pointers range from NULL to POINTER_MAX */
#define POINTER_MAX ((void*) UINTPTR_MAX)
-#define _DEFINE_TRIVIAL_REF_FUNC(type, name, scope) \
- scope type *name##_ref(type *p) { \
- if (!p) \
- return NULL; \
- \
- /* For type check. */ \
- unsigned *q = &p->n_ref; \
- assert(*q > 0); \
- assert_se(*q < UINT_MAX); \
- \
- (*q)++; \
- return p; \
- }
-
-#define _DEFINE_TRIVIAL_UNREF_FUNC(type, name, free_func, scope) \
- scope type *name##_unref(type *p) { \
- if (!p) \
- return NULL; \
- \
- assert(p->n_ref > 0); \
- p->n_ref--; \
- if (p->n_ref > 0) \
- return NULL; \
- \
- return free_func(p); \
- }
-
-#define DEFINE_TRIVIAL_REF_FUNC(type, name) \
- _DEFINE_TRIVIAL_REF_FUNC(type, name,)
-#define DEFINE_PRIVATE_TRIVIAL_REF_FUNC(type, name) \
- _DEFINE_TRIVIAL_REF_FUNC(type, name, static)
-#define DEFINE_PUBLIC_TRIVIAL_REF_FUNC(type, name) \
- _DEFINE_TRIVIAL_REF_FUNC(type, name, _public_)
-
-#define DEFINE_TRIVIAL_UNREF_FUNC(type, name, free_func) \
- _DEFINE_TRIVIAL_UNREF_FUNC(type, name, free_func,)
-#define DEFINE_PRIVATE_TRIVIAL_UNREF_FUNC(type, name, free_func) \
- _DEFINE_TRIVIAL_UNREF_FUNC(type, name, free_func, static)
-#define DEFINE_PUBLIC_TRIVIAL_UNREF_FUNC(type, name, free_func) \
- _DEFINE_TRIVIAL_UNREF_FUNC(type, name, free_func, _public_)
-
-#define DEFINE_TRIVIAL_REF_UNREF_FUNC(type, name, free_func) \
- DEFINE_TRIVIAL_REF_FUNC(type, name); \
- DEFINE_TRIVIAL_UNREF_FUNC(type, name, free_func);
-
-#define DEFINE_PRIVATE_TRIVIAL_REF_UNREF_FUNC(type, name, free_func) \
- DEFINE_PRIVATE_TRIVIAL_REF_FUNC(type, name); \
- DEFINE_PRIVATE_TRIVIAL_UNREF_FUNC(type, name, free_func);
-
-#define DEFINE_PUBLIC_TRIVIAL_REF_UNREF_FUNC(type, name, free_func) \
- DEFINE_PUBLIC_TRIVIAL_REF_FUNC(type, name); \
- DEFINE_PUBLIC_TRIVIAL_UNREF_FUNC(type, name, free_func);
-
/* A macro to force copying of a variable from memory. This is useful whenever we want to read something from
* memory and want to make sure the compiler won't optimize away the destination variable for us. It's not
* supposed to be a full CPU memory barrier, i.e. CPU is still allowed to reorder the reads, but it is not
@@ -328,12 +205,6 @@ static inline size_t size_add(size_t x, size_t y) {
return saturate_add(x, y, SIZE_MAX);
}
-typedef struct {
- int _empty[0];
-} dummy_t;
-
-assert_cc(sizeof(dummy_t) == 0);
-
/* A little helper for subtracting 1 off a pointer in a safe UB-free way. This is intended to be used for
* loops that count down from a high pointer until some base. A naive loop would implement this like this:
*
@@ -358,5 +229,3 @@ assert_cc(sizeof(dummy_t) == 0);
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 789e96a9c3..3e9a3683ff 100644
--- a/src/libnm-systemd-shared/src/basic/memory-util.c
+++ b/src/libnm-systemd-shared/src/basic/memory-util.c
@@ -2,10 +2,11 @@
#include "nm-sd-adapt-shared.h"
+#include
#include
+#include "alloc-util.h"
#include "memory-util.h"
-#include "missing_threads.h"
size_t page_size(void) {
static thread_local size_t pgsz = 0;
@@ -42,7 +43,7 @@ bool memeqbyte(uint8_t byte, const void *data, size_t length) {
return memcmp(data, p + 16, length) == 0;
}
-void *memdup_reverse(const void *mem, size_t size) {
+void* memdup_reverse(const void *mem, size_t size) {
assert(mem);
assert(size != 0);
@@ -57,3 +58,14 @@ void *memdup_reverse(const void *mem, size_t size) {
return p;
}
+
+void* erase_and_free(void *p) {
+ size_t l;
+
+ if (!p)
+ return NULL;
+
+ l = MALLOC_SIZEOF_SAFE(p);
+ explicit_bzero_safe(p, l);
+ return mfree(p);
+}
diff --git a/src/libnm-systemd-shared/src/basic/memory-util.h b/src/libnm-systemd-shared/src/basic/memory-util.h
index 1f604cc452..443fc3a8ab 100644
--- a/src/libnm-systemd-shared/src/basic/memory-util.h
+++ b/src/libnm-systemd-shared/src/basic/memory-util.h
@@ -7,7 +7,6 @@
#include
#include
-#include "alloc-util.h"
#include "macro.h"
#include "memory-util-fundamental.h"
@@ -35,13 +34,16 @@ static inline void* mempcpy_safe(void *dst, const void *src, size_t n) {
return mempcpy(dst, src, n);
}
-#define mempcpy_typesafe(dst, src, n) \
+#define _mempcpy_typesafe(dst, src, n, sz) \
({ \
- size_t _sz_; \
- assert_se(MUL_SAFE(&_sz_, sizeof((dst)[0]), n)); \
- (typeof((dst)[0])*) mempcpy_safe(dst, src, _sz_); \
+ size_t sz; \
+ assert_se(MUL_SAFE(&sz, sizeof((dst)[0]), n)); \
+ (typeof((dst)[0])*) mempcpy_safe(dst, src, sz); \
})
+#define mempcpy_typesafe(dst, src, n) \
+ _mempcpy_typesafe(dst, src, n, UNIQ_T(sz, UNIQ))
+
/* Normal memcmp() requires s1 and s2 to be nonnull. We do nothing if n is 0. */
static inline int memcmp_safe(const void *s1, const void *s2, size_t n) {
if (n == 0)
@@ -59,19 +61,19 @@ static inline int memcmp_nn(const void *s1, size_t n1, const void *s2, size_t n2
#define zero(x) (memzero(&(x), sizeof(x)))
-bool memeqbyte(uint8_t byte, const void *data, size_t length);
+bool memeqbyte(uint8_t byte, const void *data, size_t length) _nonnull_if_nonzero_(2, 3);
#define memeqzero(data, length) memeqbyte(0x00, data, length)
#define eqzero(x) memeqzero(x, sizeof(x))
-static inline void *mempset(void *s, int c, size_t n) {
+static inline void* mempset(void *s, int c, size_t n) {
memset(s, c, n);
- return (uint8_t*)s + n;
+ return (uint8_t*) s + n;
}
/* Normal memmem() requires haystack to be nonnull, which is annoying for zero-length buffers */
-static inline void *memmem_safe(const void *haystack, size_t haystacklen, const void *needle, size_t needlelen) {
+static inline void* memmem_safe(const void *haystack, size_t haystacklen, const void *needle, size_t needlelen) {
if (needlelen <= 0)
return (void*) haystack;
@@ -85,7 +87,7 @@ static inline void *memmem_safe(const void *haystack, size_t haystacklen, const
return memmem(haystack, haystacklen, needle, needlelen);
}
-static inline void *mempmem_safe(const void *haystack, size_t haystacklen, const void *needle, size_t needlelen) {
+static inline void* mempmem_safe(const void *haystack, size_t haystacklen, const void *needle, size_t needlelen) {
const uint8_t *p;
p = memmem_safe(haystack, haystacklen, needle, needlelen);
@@ -95,16 +97,7 @@ static inline void *mempmem_safe(const void *haystack, size_t haystacklen, const
return (uint8_t*) p + needlelen;
}
-static inline void* erase_and_free(void *p) {
- size_t l;
-
- if (!p)
- return NULL;
-
- l = MALLOC_SIZEOF_SAFE(p);
- explicit_bzero_safe(p, l);
- return mfree(p);
-}
+void* erase_and_free(void *p);
static inline void erase_and_freep(void *p) {
erase_and_free(*(void**) p);
@@ -116,4 +109,57 @@ static inline void erase_char(char *p) {
}
/* Makes a copy of the buffer with reversed order of bytes */
-void *memdup_reverse(const void *mem, size_t size);
+void* memdup_reverse(const void *mem, size_t size);
+
+#define _DEFINE_TRIVIAL_REF_FUNC(type, name, scope) \
+ scope type *name##_ref(type *p) { \
+ if (!p) \
+ return NULL; \
+ \
+ /* For type check. */ \
+ unsigned *q = &p->n_ref; \
+ assert(*q > 0); \
+ assert_se(*q < UINT_MAX); \
+ \
+ (*q)++; \
+ return p; \
+ }
+
+#define _DEFINE_TRIVIAL_UNREF_FUNC(type, name, free_func, scope) \
+ scope type *name##_unref(type *p) { \
+ if (!p) \
+ return NULL; \
+ \
+ assert(p->n_ref > 0); \
+ p->n_ref--; \
+ if (p->n_ref > 0) \
+ return NULL; \
+ \
+ return free_func(p); \
+ }
+
+#define DEFINE_TRIVIAL_REF_FUNC(type, name) \
+ _DEFINE_TRIVIAL_REF_FUNC(type, name,)
+#define DEFINE_PRIVATE_TRIVIAL_REF_FUNC(type, name) \
+ _DEFINE_TRIVIAL_REF_FUNC(type, name, static)
+#define DEFINE_PUBLIC_TRIVIAL_REF_FUNC(type, name) \
+ _DEFINE_TRIVIAL_REF_FUNC(type, name, _public_)
+
+#define DEFINE_TRIVIAL_UNREF_FUNC(type, name, free_func) \
+ _DEFINE_TRIVIAL_UNREF_FUNC(type, name, free_func,)
+#define DEFINE_PRIVATE_TRIVIAL_UNREF_FUNC(type, name, free_func) \
+ _DEFINE_TRIVIAL_UNREF_FUNC(type, name, free_func, static)
+#define DEFINE_PUBLIC_TRIVIAL_UNREF_FUNC(type, name, free_func) \
+ _DEFINE_TRIVIAL_UNREF_FUNC(type, name, free_func, _public_)
+
+#define DEFINE_TRIVIAL_REF_UNREF_FUNC(type, name, free_func) \
+ DEFINE_TRIVIAL_REF_FUNC(type, name); \
+ DEFINE_TRIVIAL_UNREF_FUNC(type, name, free_func);
+
+#define DEFINE_PRIVATE_TRIVIAL_REF_UNREF_FUNC(type, name, free_func) \
+ DEFINE_PRIVATE_TRIVIAL_REF_FUNC(type, name); \
+ DEFINE_PRIVATE_TRIVIAL_UNREF_FUNC(type, name, free_func);
+
+#define DEFINE_PUBLIC_TRIVIAL_REF_UNREF_FUNC(type, name, free_func) \
+ DEFINE_PUBLIC_TRIVIAL_REF_FUNC(type, name); \
+ DEFINE_PUBLIC_TRIVIAL_UNREF_FUNC(type, name, free_func);
diff --git a/src/libnm-systemd-shared/src/basic/mempool.c b/src/libnm-systemd-shared/src/basic/mempool.c
index e467d7065e..60a89a6bda 100644
--- a/src/libnm-systemd-shared/src/basic/mempool.c
+++ b/src/libnm-systemd-shared/src/basic/mempool.c
@@ -6,6 +6,7 @@
#include
#include "format-util.h"
+#include "log.h"
#include "macro.h"
#include "memory-util.h"
#include "mempool.h"
diff --git a/src/libnm-systemd-shared/src/basic/missing_fcntl.h b/src/libnm-systemd-shared/src/basic/missing_fcntl.h
index a6188879c1..280e233b26 100644
--- a/src/libnm-systemd-shared/src/basic/missing_fcntl.h
+++ b/src/libnm-systemd-shared/src/basic/missing_fcntl.h
@@ -3,74 +3,16 @@
#include
-#ifndef F_LINUX_SPECIFIC_BASE
-#define F_LINUX_SPECIFIC_BASE 1024
-#endif
-
+/* This is defined since glibc-2.41. */
#ifndef F_DUPFD_QUERY
-#define F_DUPFD_QUERY (F_LINUX_SPECIFIC_BASE + 3)
-#endif
-
-#ifndef F_SETPIPE_SZ
-#define F_SETPIPE_SZ (F_LINUX_SPECIFIC_BASE + 7)
-#endif
-
-#ifndef F_GETPIPE_SZ
-#define F_GETPIPE_SZ (F_LINUX_SPECIFIC_BASE + 8)
-#endif
-
-#ifndef F_ADD_SEALS
-#define F_ADD_SEALS (F_LINUX_SPECIFIC_BASE + 9)
-#define F_GET_SEALS (F_LINUX_SPECIFIC_BASE + 10)
-
-#define F_SEAL_SEAL 0x0001 /* prevent further seals from being set */
-#define F_SEAL_SHRINK 0x0002 /* prevent file from shrinking */
-#define F_SEAL_GROW 0x0004 /* prevent file from growing */
-#define F_SEAL_WRITE 0x0008 /* prevent writes */
-#endif
-
-#ifndef F_SEAL_FUTURE_WRITE
-#define F_SEAL_FUTURE_WRITE 0x0010 /* prevent future writes while mapped */
+#define F_DUPFD_QUERY 1027
#endif
+/* This is defined since glibc-2.39. */
#ifndef F_SEAL_EXEC
#define F_SEAL_EXEC 0x0020 /* prevent chmod modifying exec bits */
#endif
-#ifndef F_OFD_GETLK
-#define F_OFD_GETLK 36
-#define F_OFD_SETLK 37
-#define F_OFD_SETLKW 38
-#endif
-
-#ifndef MAX_HANDLE_SZ
-#define MAX_HANDLE_SZ 128
-#endif
-
-/* The precise definition of __O_TMPFILE is arch specific; use the
- * values defined by the kernel (note: some are hexa, some are octal,
- * duplicated as-is from the kernel definitions):
- * - alpha, parisc, sparc: each has a specific value;
- * - others: they use the "generic" value.
- */
-
-#ifndef __O_TMPFILE
-#if defined(__alpha__)
-#define __O_TMPFILE 0100000000
-#elif defined(__parisc__) || defined(__hppa__)
-#define __O_TMPFILE 0400000000
-#elif defined(__sparc__) || defined(__sparc64__)
-#define __O_TMPFILE 0x2000000
-#else
-#define __O_TMPFILE 020000000
-#endif
-#endif
-
-/* a horrid kludge trying to make sure that this will fail on old kernels */
-#ifndef O_TMPFILE
-#define O_TMPFILE (__O_TMPFILE | O_DIRECTORY)
-#endif
-
/* So O_LARGEFILE is generally implied by glibc, and defined to zero hence, because we only build in LFS
* mode. However, when invoking fcntl(F_GETFL) the flag is ORed into the result anyway — glibc does not mask
* it away. Which sucks. Let's define the actual value here, so that we can mask it ourselves.
@@ -79,6 +21,7 @@
* are hexa and others are octal; duplicated as-is from the kernel definitions):
* - alpha, arm, arm64, m68k, mips, parisc, powerpc, sparc: each has a specific value;
* - others: they use the "generic" value (defined in include/uapi/asm-generic/fcntl.h) */
+#if 0 /* NM_IGNORED */
#if O_LARGEFILE != 0
#define RAW_O_LARGEFILE O_LARGEFILE
#else
@@ -96,7 +39,15 @@
#define RAW_O_LARGEFILE 00100000
#endif
#endif
+#endif /* NM_IGNORED */
+/* This is defined since glibc-2.39. */
#ifndef AT_HANDLE_FID
#define AT_HANDLE_FID AT_REMOVEDIR
#endif
+
+/* On musl, O_ACCMODE is defined as (03|O_SEARCH), unlike glibc which defines it as
+ * (O_RDONLY|O_WRONLY|O_RDWR). Additionally, O_SEARCH is simply defined as O_PATH. This changes the behaviour
+ * of O_ACCMODE in certain situations, which we don't want. This definition is copied from glibc and works
+ * around the problems with musl's definition. */
+#define O_ACCMODE_STRICT (O_RDONLY|O_WRONLY|O_RDWR)
diff --git a/src/libnm-systemd-shared/src/basic/missing_fs.h b/src/libnm-systemd-shared/src/basic/missing_fs.h
new file mode 100644
index 0000000000..1e2f07e55f
--- /dev/null
+++ b/src/libnm-systemd-shared/src/basic/missing_fs.h
@@ -0,0 +1,14 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+#pragma once
+
+#include
+
+/* Not exposed yet. Defined at fs/ext4/ext4.h */
+#ifndef EXT4_IOC_RESIZE_FS
+#define EXT4_IOC_RESIZE_FS _IOW('f', 16, __u64)
+#endif
+
+/* linux/exportfs.h (33c5ac9175195c36a0b7005aaf503a2e81f117a1, 5.5) */
+#ifndef FILEID_KERNFS
+#define FILEID_KERNFS 0xfe
+#endif
diff --git a/src/libnm-systemd-shared/src/basic/missing_random.h b/src/libnm-systemd-shared/src/basic/missing_random.h
index 5f40c4e58c..690021969e 100644
--- a/src/libnm-systemd-shared/src/basic/missing_random.h
+++ b/src/libnm-systemd-shared/src/basic/missing_random.h
@@ -1,26 +1,11 @@
/* SPDX-License-Identifier: LGPL-2.1-or-later */
#pragma once
+#include
+
#include "macro.h"
-#if HAVE_GETRANDOM
-# include
-#else
-# include
-#endif
-
-#ifndef GRND_NONBLOCK
-# define GRND_NONBLOCK 0x0001
-#else
-assert_cc(GRND_NONBLOCK == 0x0001);
-#endif
-
-#ifndef GRND_RANDOM
-# define GRND_RANDOM 0x0002
-#else
-assert_cc(GRND_RANDOM == 0x0002);
-#endif
-
+/* Defined since glibc-2.32. */
#ifndef GRND_INSECURE
# define GRND_INSECURE 0x0004
#else
diff --git a/src/libnm-systemd-shared/src/basic/missing_socket.h b/src/libnm-systemd-shared/src/basic/missing_socket.h
index 8460ce13bf..874e93f37e 100644
--- a/src/libnm-systemd-shared/src/basic/missing_socket.h
+++ b/src/libnm-systemd-shared/src/basic/missing_socket.h
@@ -3,75 +3,36 @@
#include
-#ifndef AF_VSOCK
-#define AF_VSOCK 40
-#endif
-
-#ifndef SO_REUSEPORT
-#define SO_REUSEPORT 15
-#endif
-
-#ifndef SO_PEERGROUPS
-#define SO_PEERGROUPS 59
-#endif
-
+/* Supported since kernel v6.5 (5e2ff6704a275be009be8979af17c52361b79b89) */
#ifndef SO_PASSPIDFD
#define SO_PASSPIDFD 76
#endif
+/* Supported since kernel v6.5 (7b26952a91cf65ff1cc867a2382a8964d8c0ee7d) */
#ifndef SO_PEERPIDFD
#define SO_PEERPIDFD 77
#endif
-#ifndef SO_BINDTOIFINDEX
-#define SO_BINDTOIFINDEX 62
-#endif
-
-#ifndef SOL_NETLINK
-#define SOL_NETLINK 270
-#endif
-
-#ifndef SOL_ALG
-#define SOL_ALG 279
-#endif
-
/* Not exposed yet. Defined in include/linux/socket.h. */
#ifndef SOL_SCTP
#define SOL_SCTP 132
#endif
+/* Supported since kernel v2.6.17 (2c7946a7bf45ae86736ab3b43d0085e43947945c).
+ * Defined since glibc-2.39 */
#ifndef SCM_SECURITY
#define SCM_SECURITY 0x03
#endif
+/* Supported since kernel v6.5 (5e2ff6704a275be009be8979af17c52361b79b89).
+ * Defined since glibc-2.39 */
#ifndef SCM_PIDFD
#define SCM_PIDFD 0x04
#endif
-/* netinet/in.h */
-#ifndef IP_FREEBIND
-#define IP_FREEBIND 15
-#endif
-
-#ifndef IP_TRANSPARENT
-#define IP_TRANSPARENT 19
-#endif
-
-#ifndef IPV6_FREEBIND
-#define IPV6_FREEBIND 78
-#endif
-
-#ifndef IP_RECVFRAGSIZE
-#define IP_RECVFRAGSIZE 25
-#endif
-
-#ifndef IPV6_RECVFRAGSIZE
-#define IPV6_RECVFRAGSIZE 77
-#endif
-
-/* The maximum number of fds that SCM_RIGHTS accepts. This is an internal kernel constant, but very much
- * useful for userspace too. It's documented in unix(7) these days, hence should be fairly reliable to define
- * here. */
+/* The maximum number of fds that SCM_RIGHTS accepts. This is an internal kernel constant defined in
+ * include/net/scm.h, but very much useful for userspace too. It's documented in unix(7) these days, hence
+ * should be fairly reliable to define here. */
#ifndef SCM_MAX_FD
#define SCM_MAX_FD 253U
#endif
diff --git a/src/libnm-systemd-shared/src/basic/missing_stat.h b/src/libnm-systemd-shared/src/basic/missing_stat.h
deleted file mode 100644
index 18a15ab00a..0000000000
--- a/src/libnm-systemd-shared/src/basic/missing_stat.h
+++ /dev/null
@@ -1,139 +0,0 @@
-/* SPDX-License-Identifier: LGPL-2.1-or-later */
-#pragma once
-
-#include
-#include
-
-#if 0 /* NM_IGNORED */
-#if WANT_LINUX_STAT_H
-#include
-#endif
-
-/* The newest definition we are aware of (fa2fcf4f1df1559a0a4ee0f46915b496cc2ebf60; 5.8) */
-#define STATX_DEFINITION { \
- __u32 stx_mask; \
- __u32 stx_blksize; \
- __u64 stx_attributes; \
- __u32 stx_nlink; \
- __u32 stx_uid; \
- __u32 stx_gid; \
- __u16 stx_mode; \
- __u16 __spare0[1]; \
- __u64 stx_ino; \
- __u64 stx_size; \
- __u64 stx_blocks; \
- __u64 stx_attributes_mask; \
- struct statx_timestamp stx_atime; \
- struct statx_timestamp stx_btime; \
- struct statx_timestamp stx_ctime; \
- struct statx_timestamp stx_mtime; \
- __u32 stx_rdev_major; \
- __u32 stx_rdev_minor; \
- __u32 stx_dev_major; \
- __u32 stx_dev_minor; \
- __u64 stx_mnt_id; \
- __u64 __spare2; \
- __u64 __spare3[12]; \
-}
-
-#if !HAVE_STRUCT_STATX
-struct statx_timestamp {
- __s64 tv_sec;
- __u32 tv_nsec;
- __s32 __reserved;
-};
-
-struct statx STATX_DEFINITION;
-#endif
-
-/* Always define the newest version we are aware of as a distinct type, so that we can use it even if glibc
- * defines an older definition */
-struct new_statx STATX_DEFINITION;
-#else /* NM_IGNORED */
-struct new_statx;
-#endif /* NM_IGNORED */
-
-/* a528d35e8bfcc521d7cb70aaf03e1bd296c8493f (4.11) */
-#ifndef AT_STATX_SYNC_AS_STAT
-#define AT_STATX_SYNC_AS_STAT 0x0000
-#endif
-
-/* a528d35e8bfcc521d7cb70aaf03e1bd296c8493f (4.11) */
-#ifndef AT_STATX_FORCE_SYNC
-#define AT_STATX_FORCE_SYNC 0x2000
-#endif
-
-/* a528d35e8bfcc521d7cb70aaf03e1bd296c8493f (4.11) */
-#ifndef AT_STATX_DONT_SYNC
-#define AT_STATX_DONT_SYNC 0x4000
-#endif
-
-/* a528d35e8bfcc521d7cb70aaf03e1bd296c8493f (4.11) */
-#ifndef STATX_TYPE
-#define STATX_TYPE 0x00000001U
-#endif
-
-/* a528d35e8bfcc521d7cb70aaf03e1bd296c8493f (4.11) */
-#ifndef STATX_MODE
-#define STATX_MODE 0x00000002U
-#endif
-
-/* a528d35e8bfcc521d7cb70aaf03e1bd296c8493f (4.11) */
-#ifndef STATX_NLINK
-#define STATX_NLINK 0x00000004U
-#endif
-
-/* a528d35e8bfcc521d7cb70aaf03e1bd296c8493f (4.11) */
-#ifndef STATX_UID
-#define STATX_UID 0x00000008U
-#endif
-
-/* a528d35e8bfcc521d7cb70aaf03e1bd296c8493f (4.11) */
-#ifndef STATX_GID
-#define STATX_GID 0x00000010U
-#endif
-
-/* a528d35e8bfcc521d7cb70aaf03e1bd296c8493f (4.11) */
-#ifndef STATX_ATIME
-#define STATX_ATIME 0x00000020U
-#endif
-
-/* a528d35e8bfcc521d7cb70aaf03e1bd296c8493f (4.11) */
-#ifndef STATX_MTIME
-#define STATX_MTIME 0x00000040U
-#endif
-
-/* a528d35e8bfcc521d7cb70aaf03e1bd296c8493f (4.11) */
-#ifndef STATX_CTIME
-#define STATX_CTIME 0x00000080U
-#endif
-
-/* a528d35e8bfcc521d7cb70aaf03e1bd296c8493f (4.11) */
-#ifndef STATX_INO
-#define STATX_INO 0x00000100U
-#endif
-
-/* a528d35e8bfcc521d7cb70aaf03e1bd296c8493f (4.11) */
-#ifndef STATX_SIZE
-#define STATX_SIZE 0x00000200U
-#endif
-
-/* a528d35e8bfcc521d7cb70aaf03e1bd296c8493f (4.11) */
-#ifndef STATX_BLOCKS
-#define STATX_BLOCKS 0x00000400U
-#endif
-
-/* a528d35e8bfcc521d7cb70aaf03e1bd296c8493f (4.11) */
-#ifndef STATX_BTIME
-#define STATX_BTIME 0x00000800U
-#endif
-
-/* fa2fcf4f1df1559a0a4ee0f46915b496cc2ebf60 (5.8) */
-#ifndef STATX_MNT_ID
-#define STATX_MNT_ID 0x00001000U
-#endif
-
-/* 80340fe3605c0e78cfe496c3b3878be828cfdbfe (5.8) */
-#ifndef STATX_ATTR_MOUNT_ROOT
-#define STATX_ATTR_MOUNT_ROOT 0x00002000 /* Root of a mount */
-#endif
diff --git a/src/libnm-systemd-shared/src/basic/missing_syscall.h b/src/libnm-systemd-shared/src/basic/missing_syscall.h
index da6f9827f0..55475f7e33 100644
--- a/src/libnm-systemd-shared/src/basic/missing_syscall.h
+++ b/src/libnm-systemd-shared/src/basic/missing_syscall.h
@@ -4,12 +4,7 @@
/* Missing glibc definitions to access certain kernel APIs */
#include
-#include
-#if HAVE_LINUX_TIME_TYPES_H
-/* This header defines __kernel_timespec for us, but is only available since Linux 5.1, hence conditionally
- * include this. */
#include
-#endif
#include
#include
#include
@@ -23,26 +18,15 @@
#include "macro.h"
#include "missing_keyctl.h"
#include "missing_sched.h"
-#include "missing_stat.h"
#include "missing_syscall_def.h"
#if 0 /* NM_IGNORED */
-
-/* linux/kcmp.h */
-#ifndef KCMP_FILE /* 3f4994cfc15f38a3159c6e3a4b3ab2e1481a6b02 (3.19) */
-#define KCMP_FILE 0
-#endif
-
/* ======================================================================= */
#if !HAVE_FCHMODAT2
+/* since kernel v6.6 (78252deb023cf0879256fcfbafe37022c390762b) */
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
@@ -80,95 +64,6 @@ static inline int missing_ioprio_set(int which, int who, int ioprio) {
/* ======================================================================= */
-#if !HAVE_MEMFD_CREATE
-static inline int missing_memfd_create(const char *name, unsigned int flags) {
- return syscall(__NR_memfd_create, name, flags);
-}
-
-# define memfd_create missing_memfd_create
-#endif
-#endif /* NM_IGNORED */
-
-/* ======================================================================= */
-
-#if !HAVE_GETRANDOM
-/* glibc says getrandom() returns ssize_t */
-static inline ssize_t missing_getrandom(void *buffer, size_t count, unsigned flags) {
- return syscall(__NR_getrandom, buffer, count, flags);
-}
-
-# define getrandom missing_getrandom
-#endif
-
-/* ======================================================================= */
-
-#if 0 /* NM_IGNORED */
-/* The syscall has been defined since forever, but the glibc wrapper was missing. */
-#if !HAVE_GETTID
-static inline pid_t missing_gettid(void) {
-# if defined __NR_gettid && __NR_gettid >= 0
- return (pid_t) syscall(__NR_gettid);
-# else
-# error "__NR_gettid not defined"
-# endif
-}
-
-# define gettid missing_gettid
-#endif
-
-/* ======================================================================= */
-
-#if !HAVE_NAME_TO_HANDLE_AT
-struct file_handle {
- unsigned int handle_bytes;
- int handle_type;
- unsigned char f_handle[0];
-};
-
-static inline int missing_name_to_handle_at(int fd, const char *name, struct file_handle *handle, int *mnt_id, int flags) {
-# ifdef __NR_name_to_handle_at
- return syscall(__NR_name_to_handle_at, fd, name, handle, mnt_id, flags);
-# else
- errno = ENOSYS;
- return -1;
-# endif
-}
-
-# define name_to_handle_at missing_name_to_handle_at
-#endif
-
-/* ======================================================================= */
-
-#if !HAVE_SETNS
-static inline int missing_setns(int fd, int nstype) {
- return syscall(__NR_setns, fd, nstype);
-}
-
-# define setns missing_setns
-#endif
-
-/* ======================================================================= */
-
-static inline pid_t raw_getpid(void) {
-#if defined(__alpha__)
- return (pid_t) syscall(__NR_getxpid);
-#else
- return (pid_t) syscall(__NR_getpid);
-#endif
-}
-
-/* ======================================================================= */
-
-#if !HAVE_RENAMEAT2
-static inline int missing_renameat2(int oldfd, const char *oldname, int newfd, const char *newname, unsigned flags) {
- return syscall(__NR_renameat2, oldfd, oldname, newfd, newname, flags);
-}
-
-# define renameat2 missing_renameat2
-#endif
-
-/* ======================================================================= */
-
#if !HAVE_KCMP
static inline int missing_kcmp(pid_t pid1, pid_t pid2, int type, unsigned long idx1, unsigned long idx2) {
return syscall(__NR_kcmp, pid1, pid2, type, idx1, idx2);
@@ -186,12 +81,16 @@ static inline long missing_keyctl(int cmd, unsigned long arg2, unsigned long arg
# define keyctl missing_keyctl
}
+/* ======================================================================= */
+
static inline key_serial_t missing_add_key(const char *type, const char *description, const void *payload, size_t plen, key_serial_t ringid) {
return syscall(__NR_add_key, type, description, payload, plen, ringid);
# define add_key missing_add_key
}
+/* ======================================================================= */
+
static inline key_serial_t missing_request_key(const char *type, const char *description, const char * callout_info, key_serial_t destringid) {
return syscall(__NR_request_key, type, description, callout_info, destringid);
@@ -201,34 +100,11 @@ static inline key_serial_t missing_request_key(const char *type, const char *des
/* ======================================================================= */
-#if !HAVE_COPY_FILE_RANGE
-static inline ssize_t missing_copy_file_range(int fd_in, loff_t *off_in,
- int fd_out, loff_t *off_out,
- size_t len,
- unsigned int flags) {
-# ifdef __NR_copy_file_range
- return syscall(__NR_copy_file_range, fd_in, off_in, fd_out, off_out, len, flags);
-# else
- errno = ENOSYS;
- return -1;
-# endif
-}
-
-# define copy_file_range missing_copy_file_range
-#endif
-
-/* ======================================================================= */
-
#if !HAVE_BPF
union bpf_attr;
static inline int missing_bpf(int cmd, union bpf_attr *attr, size_t size) {
-#ifdef __NR_bpf
return (int) syscall(__NR_bpf, cmd, attr, size);
-#else
- errno = ENOSYS;
- return -1;
-#endif
}
# define bpf missing_bpf
@@ -236,28 +112,6 @@ static inline int missing_bpf(int cmd, union bpf_attr *attr, size_t size) {
/* ======================================================================= */
-#if !HAVE_STATX
-struct statx;
-
-static inline ssize_t missing_statx(int dfd, const char *filename, unsigned flags, unsigned int mask, struct statx *buffer) {
-# ifdef __NR_statx
- return syscall(__NR_statx, dfd, filename, flags, mask, buffer);
-# else
- errno = ENOSYS;
- return -1;
-# endif
-}
-#endif
-
-/* This typedef is supposed to be always defined. */
-typedef struct statx struct_statx;
-
-#if !HAVE_STATX
-# define statx(dfd, filename, flags, mask, buffer) missing_statx(dfd, filename, flags, mask, buffer)
-#endif
-
-/* ======================================================================= */
-
#if !HAVE_SET_MEMPOLICY
enum {
MPOL_DEFAULT,
@@ -269,14 +123,7 @@ enum {
static inline long missing_set_mempolicy(int mode, const unsigned long *nodemask,
unsigned long maxnode) {
- long i;
-# if defined __NR_set_mempolicy && __NR_set_mempolicy >= 0
- i = syscall(__NR_set_mempolicy, mode, nodemask, maxnode);
-# else
- errno = ENOSYS;
- i = -1;
-# endif
- return i;
+ return syscall(__NR_set_mempolicy, mode, nodemask, maxnode);
}
# define set_mempolicy missing_set_mempolicy
@@ -286,14 +133,7 @@ static inline long missing_set_mempolicy(int mode, const unsigned long *nodemask
static inline long missing_get_mempolicy(int *mode, unsigned long *nodemask,
unsigned long maxnode, void *addr,
unsigned long flags) {
- long i;
-# if defined __NR_get_mempolicy && __NR_get_mempolicy >= 0
- i = syscall(__NR_get_mempolicy, mode, nodemask, maxnode, addr, flags);
-# else
- errno = ENOSYS;
- i = -1;
-# endif
- return i;
+ return syscall(__NR_get_mempolicy, mode, nodemask, maxnode, addr, flags);
}
# define get_mempolicy missing_get_mempolicy
@@ -304,6 +144,7 @@ static inline long missing_get_mempolicy(int *mode, unsigned long *nodemask,
/* ======================================================================= */
#if !HAVE_PIDFD_SEND_SIGNAL
+/* since kernel v5.1 (3eb39f47934f9d5a3027fe00d906a45fe3a15fad) */
static inline int missing_pidfd_send_signal(int fd, int sig, siginfo_t *info, unsigned flags) {
return syscall(__NR_pidfd_send_signal, fd, sig, info, flags);
}
@@ -311,7 +152,10 @@ static inline int missing_pidfd_send_signal(int fd, int sig, siginfo_t *info, un
# define pidfd_send_signal missing_pidfd_send_signal
#endif
+/* ======================================================================= */
+
#if !HAVE_PIDFD_OPEN
+/* since kernel v5.3 (7615d9e1780e26e0178c93c55b73309a5dc093d7) */
static inline int missing_pidfd_open(pid_t pid, unsigned flags) {
return syscall(__NR_pidfd_open, pid, flags);
}
@@ -321,28 +165,10 @@ static inline int missing_pidfd_open(pid_t pid, unsigned flags) {
/* ======================================================================= */
-#if !HAVE_RT_SIGQUEUEINFO
-static inline int missing_rt_sigqueueinfo(pid_t tgid, int sig, siginfo_t *info) {
-# if defined __NR_rt_sigqueueinfo && __NR_rt_sigqueueinfo >= 0
- return syscall(__NR_rt_sigqueueinfo, tgid, sig, info);
-# else
-# error "__NR_rt_sigqueueinfo not defined"
-# endif
-}
-
-# define rt_sigqueueinfo missing_rt_sigqueueinfo
-#endif
-
-/* ======================================================================= */
-
#if 0 /* NM_IGNORED */
#if !HAVE_RT_TGSIGQUEUEINFO
static inline int missing_rt_tgsigqueueinfo(pid_t tgid, pid_t tid, int sig, siginfo_t *info) {
-# if defined __NR_rt_tgsigqueueinfo && __NR_rt_tgsigqueueinfo >= 0
return syscall(__NR_rt_tgsigqueueinfo, tgid, tid, sig, info);
-# else
-# error "__NR_rt_tgsigqueueinfo not defined"
-# endif
}
# define rt_tgsigqueueinfo missing_rt_tgsigqueueinfo
@@ -351,27 +177,21 @@ static inline int missing_rt_tgsigqueueinfo(pid_t tgid, pid_t tid, int sig, sigi
/* ======================================================================= */
#if !HAVE_EXECVEAT
+/* since kernel v3.19 (51f39a1f0cea1cacf8c787f652f26dfee9611874) */
static inline int missing_execveat(int dirfd, const char *pathname,
char *const argv[], char *const envp[],
int flags) {
-# if defined __NR_execveat && __NR_execveat >= 0
return syscall(__NR_execveat, dirfd, pathname, argv, envp, flags);
-# else
- errno = ENOSYS;
- return -1;
-# endif
}
-# undef AT_EMPTY_PATH
-# define AT_EMPTY_PATH 0x1000
# define execveat missing_execveat
#endif
/* ======================================================================= */
#if !HAVE_CLOSE_RANGE
+/* since kernel v5.9 (9b4feb630e8e9801603f3cab3a36369e3c1cf88d) */
static inline int missing_close_range(unsigned first_fd, unsigned end_fd, unsigned flags) {
-# ifdef __NR_close_range
/* Kernel-side the syscall expects fds as unsigned integers (just like close() actually), while
* userspace exclusively uses signed integers for fds. glibc chose to expose it 1:1 however, hence we
* do so here too, even if we end up passing signed fds to it most of the time. */
@@ -379,10 +199,6 @@ static inline int missing_close_range(unsigned first_fd, unsigned end_fd, unsign
first_fd,
end_fd,
flags);
-# else
- errno = ENOSYS;
- return -1;
-# endif
}
# define close_range missing_close_range
@@ -390,243 +206,8 @@ static inline int missing_close_range(unsigned first_fd, unsigned end_fd, unsign
/* ======================================================================= */
-#if !HAVE_MOUNT_SETATTR
-
-#if !HAVE_STRUCT_MOUNT_ATTR
-struct mount_attr {
- uint64_t attr_set;
- uint64_t attr_clr;
- uint64_t propagation;
- uint64_t userns_fd;
-};
-#else
-struct mount_attr;
-#endif
-
-#ifndef MOUNT_ATTR_RDONLY
-#define MOUNT_ATTR_RDONLY 0x00000001 /* Mount read-only */
-#endif
-
-#ifndef MOUNT_ATTR_NOSUID
-#define MOUNT_ATTR_NOSUID 0x00000002 /* Ignore suid and sgid bits */
-#endif
-
-#ifndef MOUNT_ATTR_NODEV
-#define MOUNT_ATTR_NODEV 0x00000004 /* Disallow access to device special files */
-#endif
-
-#ifndef MOUNT_ATTR_NOEXEC
-#define MOUNT_ATTR_NOEXEC 0x00000008 /* Disallow program execution */
-#endif
-
-#ifndef MOUNT_ATTR__ATIME
-#define MOUNT_ATTR__ATIME 0x00000070 /* Setting on how atime should be updated */
-#endif
-
-#ifndef MOUNT_ATTR_RELATIME
-#define MOUNT_ATTR_RELATIME 0x00000000 /* - Update atime relative to mtime/ctime. */
-#endif
-
-#ifndef MOUNT_ATTR_NOATIME
-#define MOUNT_ATTR_NOATIME 0x00000010 /* - Do not update access times. */
-#endif
-
-#ifndef MOUNT_ATTR_STRICTATIME
-#define MOUNT_ATTR_STRICTATIME 0x00000020 /* - Always perform atime updates */
-#endif
-
-#ifndef MOUNT_ATTR_NODIRATIME
-#define MOUNT_ATTR_NODIRATIME 0x00000080 /* Do not update directory access times */
-#endif
-
-#ifndef MOUNT_ATTR_IDMAP
-#define MOUNT_ATTR_IDMAP 0x00100000 /* Idmap mount to @userns_fd in struct mount_attr. */
-#endif
-
-#ifndef MOUNT_ATTR_NOSYMFOLLOW
-#define MOUNT_ATTR_NOSYMFOLLOW 0x00200000 /* Do not follow symlinks */
-#endif
-
-#ifndef MOUNT_ATTR_SIZE_VER0
-#define MOUNT_ATTR_SIZE_VER0 32 /* sizeof first published struct */
-#endif
-
-#ifndef AT_RECURSIVE
-#define AT_RECURSIVE 0x8000
-#endif
-
-static inline int missing_mount_setattr(
- int dfd,
- const char *path,
- unsigned flags,
- struct mount_attr *attr,
- size_t size) {
-
-# if defined __NR_mount_setattr && __NR_mount_setattr >= 0
- return syscall(__NR_mount_setattr, dfd, path, flags, attr, size);
-# else
- errno = ENOSYS;
- return -1;
-# endif
-}
-
-# define mount_setattr missing_mount_setattr
-#endif
-
-/* ======================================================================= */
-
-#if !HAVE_OPEN_TREE
-
-#ifndef OPEN_TREE_CLONE
-#define OPEN_TREE_CLONE 1
-#endif
-
-#ifndef OPEN_TREE_CLOEXEC
-#define OPEN_TREE_CLOEXEC O_CLOEXEC
-#endif
-
-static inline int missing_open_tree(
- int dfd,
- const char *filename,
- unsigned flags) {
-
-# if defined __NR_open_tree && __NR_open_tree >= 0
- return syscall(__NR_open_tree, dfd, filename, flags);
-# else
- errno = ENOSYS;
- return -1;
-# endif
-}
-
-# define open_tree missing_open_tree
-#endif
-
-/* ======================================================================= */
-
-#ifndef MOVE_MOUNT_BENEATH
-#define MOVE_MOUNT_BENEATH 0x00000200
-#endif
-
-#if !HAVE_MOVE_MOUNT
-
-#ifndef MOVE_MOUNT_F_EMPTY_PATH
-#define MOVE_MOUNT_F_EMPTY_PATH 0x00000004 /* Empty from path permitted */
-#endif
-
-#ifndef MOVE_MOUNT_T_EMPTY_PATH
-#define MOVE_MOUNT_T_EMPTY_PATH 0x00000040 /* Empty to path permitted */
-#endif
-
-static inline int missing_move_mount(
- int from_dfd,
- const char *from_pathname,
- int to_dfd,
- const char *to_pathname,
- unsigned flags) {
-
-# if defined __NR_move_mount && __NR_move_mount >= 0
- return syscall(__NR_move_mount, from_dfd, from_pathname, to_dfd, to_pathname, flags);
-# else
- errno = ENOSYS;
- return -1;
-# endif
-}
-
-# define move_mount missing_move_mount
-#endif
-
-/* ======================================================================= */
-
-#if !HAVE_FSOPEN
-
-#ifndef FSOPEN_CLOEXEC
-#define FSOPEN_CLOEXEC 0x00000001
-#endif
-
-static inline int missing_fsopen(const char *fsname, unsigned flags) {
-# if defined __NR_fsopen && __NR_fsopen >= 0
- return syscall(__NR_fsopen, fsname, flags);
-# else
- errno = ENOSYS;
- return -1;
-# endif
-}
-
-# define fsopen missing_fsopen
-#endif
-
-/* ======================================================================= */
-
-#if !HAVE_FSCONFIG
-
-#ifndef FSCONFIG_SET_FLAG
-#define FSCONFIG_SET_FLAG 0 /* Set parameter, supplying no value */
-#endif
-
-#ifndef FSCONFIG_SET_STRING
-#define FSCONFIG_SET_STRING 1 /* Set parameter, supplying a string value */
-#endif
-
-#ifndef FSCONFIG_SET_FD
-#define FSCONFIG_SET_FD 5 /* Set parameter, supplying an object by fd */
-#endif
-
-#ifndef FSCONFIG_CMD_CREATE
-#define FSCONFIG_CMD_CREATE 6 /* Invoke superblock creation */
-#endif
-
-static inline int missing_fsconfig(int fd, unsigned cmd, const char *key, const void *value, int aux) {
-# if defined __NR_fsconfig && __NR_fsconfig >= 0
- return syscall(__NR_fsconfig, fd, cmd, key, value, aux);
-# else
- errno = ENOSYS;
- return -1;
-# endif
-}
-
-# define fsconfig missing_fsconfig
-#endif
-
-/* ======================================================================= */
-
-#if !HAVE_FSMOUNT
-
-#ifndef FSMOUNT_CLOEXEC
-#define FSMOUNT_CLOEXEC 0x00000001
-#endif
-
-static inline int missing_fsmount(int fd, unsigned flags, unsigned ms_flags) {
-# if defined __NR_fsmount && __NR_fsmount >= 0
- return syscall(__NR_fsmount, fd, flags, ms_flags);
-# else
- errno = ENOSYS;
- return -1;
-# endif
-}
-
-# define fsmount missing_fsmount
-#endif
-
-/* ======================================================================= */
-
-#if !HAVE_GETDENTS64
-
-static inline ssize_t missing_getdents64(int fd, void *buffer, size_t length) {
-# if defined __NR_getdents64 && __NR_getdents64 >= 0
- return syscall(__NR_getdents64, fd, buffer, length);
-# else
- errno = ENOSYS;
- return -1;
-# endif
-}
-
-# define getdents64 missing_getdents64
-#endif
-
-/* ======================================================================= */
-
#if !HAVE_SCHED_SETATTR
-
+/* since kernel 3.14 (e6cfc0295c7d51b008999a8b13a44fb43f8685ea) */
static inline ssize_t missing_sched_setattr(pid_t pid, struct sched_attr *attr, unsigned int flags) {
return syscall(__NR_sched_setattr, pid, attr, flags);
}
@@ -653,16 +234,39 @@ int __clone2(int (*fn)(void *), void *stack_base, size_t stack_size, int flags,
#if 0 /* NM_IGNORED */
#if !HAVE_QUOTACTL_FD
-
+/* since kernel v5.14 (64c2c2c62f92339b176ea24403d8db16db36f9e6) */
static inline int missing_quotactl_fd(int fd, int cmd, int id, void *addr) {
-#if defined __NR_quotactl_fd
return syscall(__NR_quotactl_fd, fd, cmd, id, addr);
-#else
- errno = ENOSYS;
- return -1;
-#endif
}
# define quotactl_fd missing_quotactl_fd
#endif
+
+/* ======================================================================= */
+
+#if !HAVE_SETXATTRAT
+/* since kernel v6.13 (6140be90ec70c39fa844741ca3cc807dd0866394) */
+struct xattr_args {
+ _align_(8) uint64_t value;
+ uint32_t size;
+ uint32_t flags;
+};
+
+static inline int missing_setxattrat(int fd, const char *path, int at_flags, const char *name, const struct xattr_args *args, size_t size) {
+ return syscall(__NR_setxattrat, fd, path, at_flags, name, args, size);
+}
+
+# define setxattrat missing_setxattrat
+#endif
+
+/* ======================================================================= */
+
+#if !HAVE_REMOVEXATTRAT
+/* since kernel v6.13 (6140be90ec70c39fa844741ca3cc807dd0866394) */
+static inline int missing_removexattrat(int fd, const char *path, int at_flags, const char *name) {
+ return syscall(__NR_removexattrat, fd, path, at_flags, name);
+}
+
+# define removexattrat missing_removexattrat
+#endif
#endif /* NM_IGNORED */
diff --git a/src/libnm-systemd-shared/src/basic/missing_threads.h b/src/libnm-systemd-shared/src/basic/missing_threads.h
deleted file mode 100644
index c7da1dbd5e..0000000000
--- a/src/libnm-systemd-shared/src/basic/missing_threads.h
+++ /dev/null
@@ -1,13 +0,0 @@
-/* SPDX-License-Identifier: LGPL-2.1-or-later */
-#pragma once
-
-/* If threads.h doesn't exist, then define our own thread_local to match C11's thread_local. */
-#if HAVE_THREADS_H
-# include
-#elif !(defined(thread_local))
-# ifndef __STDC_NO_THREADS__
-# define thread_local _Thread_local
-# else
-# define thread_local __thread
-# endif
-#endif
diff --git a/src/libnm-systemd-shared/src/basic/missing_type.h b/src/libnm-systemd-shared/src/basic/missing_type.h
deleted file mode 100644
index 1d17705c35..0000000000
--- a/src/libnm-systemd-shared/src/basic/missing_type.h
+++ /dev/null
@@ -1,12 +0,0 @@
-/* SPDX-License-Identifier: LGPL-2.1-or-later */
-#pragma once
-
-#include
-
-#if !HAVE_CHAR32_T
-# define char32_t uint32_t
-#endif
-
-#if !HAVE_CHAR16_T
-# define char16_t uint16_t
-#endif
diff --git a/src/libnm-systemd-shared/src/basic/missing_wait.h b/src/libnm-systemd-shared/src/basic/missing_wait.h
index 3965b5bdbf..05648779e3 100644
--- a/src/libnm-systemd-shared/src/basic/missing_wait.h
+++ b/src/libnm-systemd-shared/src/basic/missing_wait.h
@@ -5,6 +5,7 @@
#include "macro.h"
+/* since glibc-2.36 */
#ifndef P_PIDFD
# define P_PIDFD 3
#else
diff --git a/src/libnm-systemd-shared/src/basic/mountpoint-util.c b/src/libnm-systemd-shared/src/basic/mountpoint-util.c
new file mode 100644
index 0000000000..7d7ef5eb73
--- /dev/null
+++ b/src/libnm-systemd-shared/src/basic/mountpoint-util.c
@@ -0,0 +1,823 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+
+#include "nm-sd-adapt-shared.h"
+
+#include
+#include
+#include
+
+#include "alloc-util.h"
+#include "chase.h"
+#include "fd-util.h"
+#include "fileio.h"
+#include "filesystems.h"
+#include "fs-util.h"
+#include "log.h"
+#include "missing_fcntl.h"
+#include "missing_fs.h"
+#include "missing_syscall.h"
+#include "mkdir.h"
+#include "mountpoint-util.h"
+#include "nulstr-util.h"
+#include "parse-util.h"
+#include "path-util.h"
+#include "stat-util.h"
+#include "stdio-util.h"
+#include "strv.h"
+#include "user-util.h"
+
+/* This is the original MAX_HANDLE_SZ definition from the kernel, when the API was introduced. We use that in place of
+ * any more currently defined value to future-proof things: if the size is increased in the API headers, and our code
+ * is recompiled then it would cease working on old kernels, as those refuse any sizes larger than this value with
+ * EINVAL right-away. Hence, let's disconnect ourselves from any such API changes, and stick to the original definition
+ * from when it was introduced. We use it as a start value only anyway (see below), and hence should be able to deal
+ * with large file handles anyway. */
+#define ORIGINAL_MAX_HANDLE_SZ 128
+
+bool is_name_to_handle_at_fatal_error(int err) {
+ /* name_to_handle_at() can return "acceptable" errors that are due to the context. For example
+ * the file system does not support name_to_handle_at() (EOPNOTSUPP), or the syscall was blocked
+ * (EACCES/EPERM; maybe through seccomp, because we are running inside of a container), or
+ * the mount point is not triggered yet (EOVERFLOW, think autofs+nfs4), or some general name_to_handle_at()
+ * flakiness (EINVAL). However other errors are not supposed to happen and therefore are considered
+ * fatal ones. */
+
+ assert(err < 0);
+
+ if (ERRNO_IS_NEG_NOT_SUPPORTED(err))
+ return false;
+ if (ERRNO_IS_NEG_PRIVILEGE(err))
+ return false;
+
+ return !IN_SET(err, -EOVERFLOW, -EINVAL);
+}
+
+#if 0 /* NM_IGNORED */
+int name_to_handle_at_loop(
+ int fd,
+ const char *path,
+ struct file_handle **ret_handle,
+ int *ret_mnt_id,
+ int flags) {
+
+ size_t n = ORIGINAL_MAX_HANDLE_SZ;
+
+ assert(fd >= 0 || fd == AT_FDCWD);
+ assert((flags & ~(AT_SYMLINK_FOLLOW|AT_EMPTY_PATH|AT_HANDLE_FID)) == 0);
+
+ /* We need to invoke name_to_handle_at() in a loop, given that it might return EOVERFLOW when the specified
+ * buffer is too small. Note that in contrast to what the docs might suggest, MAX_HANDLE_SZ is only good as a
+ * start value, it is not an upper bound on the buffer size required.
+ *
+ * This improves on raw name_to_handle_at() also in one other regard: ret_handle and ret_mnt_id can be passed
+ * as NULL if there's no interest in either. */
+
+ for (;;) {
+ _cleanup_free_ struct file_handle *h = NULL;
+ int mnt_id = -1;
+
+ h = malloc0(offsetof(struct file_handle, f_handle) + n);
+ if (!h)
+ return -ENOMEM;
+
+ h->handle_bytes = n;
+
+ if (name_to_handle_at(fd, strempty(path), h, &mnt_id, flags) >= 0) {
+
+ if (ret_handle)
+ *ret_handle = TAKE_PTR(h);
+
+ if (ret_mnt_id)
+ *ret_mnt_id = mnt_id;
+
+ return 0;
+ }
+ if (errno != EOVERFLOW)
+ return -errno;
+
+ if (!ret_handle && ret_mnt_id && mnt_id >= 0) {
+
+ /* As it appears, name_to_handle_at() fills in mnt_id even when it returns EOVERFLOW when the
+ * buffer is too small, but that's undocumented. Hence, let's make use of this if it appears to
+ * be filled in, and the caller was interested in only the mount ID an nothing else. */
+
+ *ret_mnt_id = mnt_id;
+ return 0;
+ }
+
+ /* If name_to_handle_at() didn't increase the byte size, then this EOVERFLOW is caused by
+ * something else (apparently EOVERFLOW is returned for untriggered nfs4 autofs mounts
+ * sometimes), not by the too small buffer. In that case propagate EOVERFLOW */
+ if (h->handle_bytes <= n)
+ return -EOVERFLOW;
+
+ /* The buffer was too small. Size the new buffer by what name_to_handle_at() returned. */
+ n = h->handle_bytes;
+
+ /* paranoia: check for overflow (note that .handle_bytes is unsigned only) */
+ if (n > UINT_MAX - offsetof(struct file_handle, f_handle))
+ return -EOVERFLOW;
+ }
+}
+
+int name_to_handle_at_try_fid(
+ int fd,
+ const char *path,
+ struct file_handle **ret_handle,
+ int *ret_mnt_id,
+ int flags) {
+
+ int r;
+
+ assert(fd >= 0 || fd == AT_FDCWD);
+
+ /* First issues name_to_handle_at() with AT_HANDLE_FID. If this fails and this is not a fatal error
+ * we'll try without the flag, in order to support older kernels that didn't have AT_HANDLE_FID
+ * (i.e. older than Linux 6.5). */
+
+ r = name_to_handle_at_loop(fd, path, ret_handle, ret_mnt_id, flags | AT_HANDLE_FID);
+ if (r >= 0 || is_name_to_handle_at_fatal_error(r))
+ return r;
+
+ return name_to_handle_at_loop(fd, path, ret_handle, ret_mnt_id, flags & ~AT_HANDLE_FID);
+}
+
+static int fd_fdinfo_mnt_id(int fd, const char *filename, int flags, int *ret_mnt_id) {
+ char path[STRLEN("/proc/self/fdinfo/") + DECIMAL_STR_MAX(int)];
+ _cleanup_close_ int subfd = -EBADF;
+ int r;
+
+ assert((flags & ~(AT_SYMLINK_FOLLOW|AT_EMPTY_PATH)) == 0);
+ assert(ret_mnt_id);
+
+ if ((flags & AT_EMPTY_PATH) && isempty(filename))
+ xsprintf(path, "/proc/self/fdinfo/%i", fd);
+ else {
+ subfd = openat(fd, filename, O_CLOEXEC|O_PATH|(flags & AT_SYMLINK_FOLLOW ? 0 : O_NOFOLLOW));
+ if (subfd < 0)
+ return -errno;
+
+ xsprintf(path, "/proc/self/fdinfo/%i", subfd);
+ }
+
+ _cleanup_free_ char *p = NULL;
+ r = get_proc_field(path, "mnt_id", &p);
+ if (r == -ENOENT)
+ return -EBADF;
+ if (r < 0)
+ return r;
+
+ return safe_atoi(p, ret_mnt_id);
+}
+
+static bool filename_possibly_with_slash_suffix(const char *s) {
+ const char *slash, *copied;
+
+ /* Checks whether the specified string is either file name, or a filename with a suffix of
+ * slashes. But nothing else.
+ *
+ * this is OK: foo, bar, foo/, bar/, foo//, bar///
+ * this is not OK: "", "/", "/foo", "foo/bar", ".", ".." … */
+
+ slash = strchr(s, '/');
+ if (!slash)
+ return filename_is_valid(s);
+
+ if (slash - s > PATH_MAX) /* We want to allocate on the stack below, hence do a size check first */
+ return false;
+
+ if (slash[strspn(slash, "/")] != 0) /* Check that the suffix consist only of one or more slashes */
+ return false;
+
+ copied = strndupa_safe(s, slash - s);
+ return filename_is_valid(copied);
+}
+
+bool file_handle_equal(const struct file_handle *a, const struct file_handle *b) {
+ if (a == b)
+ return true;
+ if (!a != !b)
+ return false;
+ if (a->handle_type != b->handle_type)
+ return false;
+
+ return memcmp_nn(a->f_handle, a->handle_bytes, b->f_handle, b->handle_bytes) == 0;
+}
+
+int is_mount_point_at(int fd, const char *filename, int flags) {
+ bool fd_is_self;
+ int r;
+
+ assert(fd >= 0 || fd == AT_FDCWD);
+ assert((flags & ~AT_SYMLINK_FOLLOW) == 0);
+
+ if (isempty(filename)) {
+ if (fd == AT_FDCWD)
+ filename = ".";
+ else {
+ /* If the file name is empty we'll see if the specified 'fd' is a mount point.
+ * That's only supported by statx(), or if the inode specified via 'fd' refers to a
+ * directory. Otherwise, we'll have to fail (ENOTDIR), because we have no kernel API
+ * to query the information we need. */
+ flags |= AT_EMPTY_PATH;
+ filename = "";
+ }
+
+ fd_is_self = true;
+ } else if (STR_IN_SET(filename, ".", "./"))
+ fd_is_self = true;
+ else {
+ /* Insist that the specified filename is actually a filename, and not a path, i.e. some inode
+ * further up or down the tree then immediately below the specified directory fd. */
+ if (!filename_possibly_with_slash_suffix(filename))
+ return -EINVAL;
+
+ fd_is_self = false;
+ }
+
+ /* First we will try statx()' STATX_ATTR_MOUNT_ROOT attribute, which is our ideal API, available
+ * since kernel 5.8.
+ *
+ * If that fails, our second try is the name_to_handle_at() syscall, which tells us the mount id and
+ * an opaque file "handle". It is not supported everywhere though (kernel compile-time option, not
+ * all file systems are hooked up). If it works the mount id is usually good enough to tell us
+ * whether something is a mount point.
+ *
+ * If that didn't work we will try to read the mount id from /proc/self/fdinfo/. This is almost
+ * as good as name_to_handle_at(), however, does not return the opaque file handle. The opaque file
+ * handle is pretty useful to detect the root directory, which we should always consider a mount
+ * point. Hence we use this only as fallback.
+ *
+ * Note that traditionally the check is done via fstat()-based st_dev comparisons. However, various
+ * file systems don't guarantee same st_dev across single fs anymore, e.g. unionfs exposes file systems
+ * with a variety of st_dev reported. Also, btrfs subvolumes have different st_dev, even though
+ * they aren't real mounts of their own. */
+
+ struct statx sx = {}; /* explicitly initialize the struct to make msan silent. */
+ if (statx(fd, filename,
+ at_flags_normalize_nofollow(flags) |
+ AT_NO_AUTOMOUNT | /* don't trigger automounts – mounts are a local concept, hence no need to trigger automounts to determine STATX_ATTR_MOUNT_ROOT */
+ AT_STATX_DONT_SYNC, /* don't go to the network for this – for similar reasons */
+ STATX_TYPE,
+ &sx) < 0)
+ return -errno;
+
+ if (FLAGS_SET(sx.stx_attributes_mask, STATX_ATTR_MOUNT_ROOT)) /* yay! */
+ return FLAGS_SET(sx.stx_attributes, STATX_ATTR_MOUNT_ROOT);
+
+ _cleanup_free_ struct file_handle *h = NULL, *h_parent = NULL;
+ int mount_id = -1, mount_id_parent = -1;
+ bool nosupp = false;
+
+ r = name_to_handle_at_try_fid(fd, filename, &h, &mount_id, flags);
+ if (r < 0) {
+ if (is_name_to_handle_at_fatal_error(r))
+ return r;
+ if (!ERRNO_IS_NOT_SUPPORTED(r))
+ goto fallback_fdinfo;
+
+ /* This file system does not support name_to_handle_at(), hence let's see if the upper fs
+ * supports it (in which case it is a mount point), otherwise fall back to the fdinfo logic. */
+ nosupp = true;
+ }
+
+ if (fd_is_self)
+ r = name_to_handle_at_try_fid(fd, "..", &h_parent, &mount_id_parent, 0); /* can't work for non-directories 😢 */
+ else
+ r = name_to_handle_at_try_fid(fd, "", &h_parent, &mount_id_parent, AT_EMPTY_PATH);
+ if (r < 0) {
+ if (is_name_to_handle_at_fatal_error(r))
+ return r;
+ if (!ERRNO_IS_NOT_SUPPORTED(r))
+ goto fallback_fdinfo;
+ if (nosupp)
+ /* Both the parent and the directory can't do name_to_handle_at() */
+ goto fallback_fdinfo;
+
+ /* The parent can't do name_to_handle_at() but the directory we are
+ * interested in can? If so, it must be a mount point. */
+ return 1;
+ }
+
+ /* The parent can do name_to_handle_at() but the directory we are interested in can't? If
+ * so, it must be a mount point. */
+ if (nosupp)
+ return 1;
+
+ /* If the file handle for the directory we are interested in and its parent are identical,
+ * we assume this is the root directory, which is a mount point. */
+ if (file_handle_equal(h_parent, h))
+ return 1;
+
+ return mount_id != mount_id_parent;
+
+fallback_fdinfo:
+ r = fd_fdinfo_mnt_id(fd, filename, flags, &mount_id);
+ if (r < 0)
+ return r;
+
+ if (fd_is_self)
+ r = fd_fdinfo_mnt_id(fd, "..", 0, &mount_id_parent); /* can't work for non-directories 😢 */
+ else
+ r = fd_fdinfo_mnt_id(fd, "", AT_EMPTY_PATH, &mount_id_parent);
+ if (r < 0)
+ return r;
+
+ if (mount_id != mount_id_parent)
+ return 1;
+
+ /* Hmm, so, the mount ids are the same. This leaves one special case though for the root file
+ * system. For that, let's see if the parent directory has the same inode as we are interested
+ * in. */
+
+ struct stat a, b;
+
+ /* yay for fstatat() taking a different set of flags than the other _at() above */
+ if (fstatat(fd, filename, &a, at_flags_normalize_nofollow(flags)) < 0)
+ return -errno;
+
+ if (fd_is_self)
+ r = fstatat(fd, "..", &b, 0);
+ else
+ r = fstatat(fd, "", &b, AT_EMPTY_PATH);
+ if (r < 0)
+ return -errno;
+
+ /* A directory with same device and inode as its parent must be the root directory. Otherwise
+ * not a mount point.
+ *
+ * NB: we avoid inode_same_at() here because it internally attempts name_to_handle_at_try_fid() first,
+ * which is redundant. */
+ return stat_inode_same(&a, &b);
+}
+
+/* flags can be AT_SYMLINK_FOLLOW or 0 */
+int path_is_mount_point_full(const char *path, const char *root, int flags) {
+ _cleanup_close_ int dfd = -EBADF;
+ _cleanup_free_ char *fn = NULL;
+
+ assert(path);
+ assert((flags & ~AT_SYMLINK_FOLLOW) == 0);
+
+ if (path_equal(path, "/"))
+ return 1;
+
+ /* we need to resolve symlinks manually, we can't just rely on is_mount_point_at() to do that for us;
+ * if we have a structure like /bin -> /usr/bin/ and /usr is a mount point, then the parent that we
+ * look at needs to be /usr, not /. */
+ dfd = chase_and_open_parent(path, root,
+ CHASE_TRAIL_SLASH|(FLAGS_SET(flags, AT_SYMLINK_FOLLOW) ? 0 : CHASE_NOFOLLOW),
+ &fn);
+ if (dfd < 0)
+ return dfd;
+
+ return is_mount_point_at(dfd, fn, flags);
+}
+
+int path_get_mnt_id_at_fallback(int dir_fd, const char *path, int *ret) {
+ int r;
+
+ assert(dir_fd >= 0 || dir_fd == AT_FDCWD);
+ assert(ret);
+
+ r = name_to_handle_at_loop(dir_fd, path, NULL, ret, isempty(path) ? AT_EMPTY_PATH : 0);
+ if (r >= 0 || is_name_to_handle_at_fatal_error(r))
+ return r;
+
+ return fd_fdinfo_mnt_id(dir_fd, path, isempty(path) ? AT_EMPTY_PATH : 0, ret);
+}
+
+int path_get_mnt_id_at(int dir_fd, const char *path, int *ret) {
+ struct statx sx;
+
+ assert(dir_fd >= 0 || dir_fd == AT_FDCWD);
+ assert(ret);
+
+ if (statx(dir_fd,
+ strempty(path),
+ (isempty(path) ? AT_EMPTY_PATH : AT_SYMLINK_NOFOLLOW) |
+ AT_NO_AUTOMOUNT | /* don't trigger automounts, mnt_id is a local concept */
+ AT_STATX_DONT_SYNC, /* don't go to the network, mnt_id is a local concept */
+ STATX_MNT_ID,
+ &sx) < 0)
+ return -errno;
+
+ if (FLAGS_SET(sx.stx_mask, STATX_MNT_ID)) {
+ *ret = sx.stx_mnt_id;
+ return 0;
+ }
+
+ return path_get_mnt_id_at_fallback(dir_fd, path, ret);
+}
+
+bool fstype_is_network(const char *fstype) {
+ const char *x;
+
+ x = startswith(fstype, "fuse.");
+ if (x)
+ fstype = x;
+
+ if (nulstr_contains(filesystem_sets[FILESYSTEM_SET_NETWORK].value, fstype))
+ return true;
+
+ /* Filesystems not present in the internal database */
+ return STR_IN_SET(fstype,
+ "davfs",
+ "glusterfs",
+ "lustre",
+ "sshfs");
+}
+
+bool fstype_needs_quota(const char *fstype) {
+ /* 1. quotacheck needs to be run for some filesystems after they are mounted
+ * if the filesystem was not unmounted cleanly.
+ * 2. You may need to run quotaon to enable quota usage tracking and/or
+ * enforcement.
+ * ext2 - needs 1) and 2)
+ * ext3 - needs 2) if configured using usrjquota/grpjquota mount options
+ * ext4 - needs 1) if created without journal, needs 2) if created without QUOTA
+ * filesystem feature
+ * reiserfs - needs 2).
+ * jfs - needs 2)
+ * f2fs - needs 2) if configured using usrjquota/grpjquota/prjjquota mount options
+ * xfs - nothing needed
+ * gfs2 - nothing needed
+ * ocfs2 - nothing needed
+ * btrfs - nothing needed
+ * for reference see filesystem and quota manpages */
+ return STR_IN_SET(fstype,
+ "ext2",
+ "ext3",
+ "ext4",
+ "reiserfs",
+ "jfs",
+ "f2fs");
+}
+
+bool fstype_is_api_vfs(const char *fstype) {
+ assert(fstype);
+
+ const FilesystemSet *fs;
+ FOREACH_ARGUMENT(fs,
+ filesystem_sets + FILESYSTEM_SET_BASIC_API,
+ filesystem_sets + FILESYSTEM_SET_AUXILIARY_API,
+ filesystem_sets + FILESYSTEM_SET_PRIVILEGED_API,
+ filesystem_sets + FILESYSTEM_SET_TEMPORARY)
+ if (nulstr_contains(fs->value, fstype))
+ return true;
+
+ /* Filesystems not present in the internal database */
+ return STR_IN_SET(fstype,
+ "autofs",
+ "cpuset",
+ "devtmpfs");
+}
+
+bool fstype_is_blockdev_backed(const char *fstype) {
+ const char *x;
+
+ x = startswith(fstype, "fuse.");
+ if (x)
+ fstype = x;
+
+ return !streq(fstype, "9p") && !fstype_is_network(fstype) && !fstype_is_api_vfs(fstype);
+}
+
+bool fstype_is_ro(const char *fstype) {
+ /* All Linux file systems that are necessarily read-only */
+ return STR_IN_SET(fstype,
+ "DM_verity_hash",
+ "cramfs",
+ "erofs",
+ "iso9660",
+ "squashfs");
+}
+
+bool fstype_can_discard(const char *fstype) {
+ assert(fstype);
+
+ /* Use a curated list as first check, to avoid calling fsopen() which might load kmods, which might
+ * not be allowed in our MAC context. */
+ if (STR_IN_SET(fstype, "btrfs", "f2fs", "ext4", "vfat", "xfs"))
+ return true;
+
+ /* On new kernels we can just ask the kernel */
+ return mount_option_supported(fstype, "discard", NULL) > 0;
+}
+
+const char* fstype_norecovery_option(const char *fstype) {
+ int r;
+
+ assert(fstype);
+
+ /* Use a curated list as first check, to avoid calling fsopen() which might load kmods, which might
+ * not be allowed in our MAC context. */
+ if (STR_IN_SET(fstype, "ext3", "ext4", "xfs"))
+ return "norecovery";
+
+ /* btrfs dropped support for the "norecovery" option in 6.8
+ * (https://github.com/torvalds/linux/commit/a1912f712188291f9d7d434fba155461f1ebef66) and replaced
+ * it with rescue=nologreplay, so we check for the new name first and fall back to checking for the
+ * old name if the new name doesn't work. */
+ if (streq(fstype, "btrfs")) {
+ r = mount_option_supported(fstype, "rescue=nologreplay", NULL);
+ if (r == -EAGAIN) {
+ log_debug_errno(r, "Failed to check for btrfs 'rescue=nologreplay' option, assuming old kernel with 'norecovery': %m");
+ return "norecovery";
+ }
+ if (r < 0)
+ log_debug_errno(r, "Failed to check for btrfs 'rescue=nologreplay' option, assuming it is not supported: %m");
+ if (r > 0)
+ return "rescue=nologreplay";
+ }
+
+ /* On new kernels we can just ask the kernel */
+ return mount_option_supported(fstype, "norecovery", NULL) > 0 ? "norecovery" : NULL;
+}
+
+bool fstype_can_fmask_dmask(const char *fstype) {
+ assert(fstype);
+
+ /* Use a curated list as first check, to avoid calling fsopen() which might load kmods, which might
+ * not be allowed in our MAC context. If we don't know ourselves, on new kernels we can just ask the
+ * kernel. */
+ return streq(fstype, "vfat") || (mount_option_supported(fstype, "fmask", "0177") > 0 && mount_option_supported(fstype, "dmask", "0077") > 0);
+}
+
+bool fstype_can_uid_gid(const char *fstype) {
+ /* All file systems that have a uid=/gid= mount option that fixates the owners of all files and
+ * directories, current and future. Note that this does *not* ask the kernel via
+ * mount_option_supported() here because the uid=/gid= setting of various file systems mean different
+ * things: some apply it only to the root dir inode, others to all inodes in the file system. Thus we
+ * maintain the curated list below. 😢 */
+
+ return STR_IN_SET(fstype,
+ "adfs",
+ "exfat",
+ "fat",
+ "hfs",
+ "hpfs",
+ "iso9660",
+ "msdos",
+ "ntfs",
+ "vfat");
+}
+
+int dev_is_devtmpfs(void) {
+ _cleanup_fclose_ FILE *proc_self_mountinfo = NULL;
+ int mount_id, r;
+ char *e;
+
+ r = path_get_mnt_id("/dev", &mount_id);
+ if (r < 0)
+ return r;
+
+ r = fopen_unlocked("/proc/self/mountinfo", "re", &proc_self_mountinfo);
+ if (r == -ENOENT)
+ return proc_mounted() > 0 ? -ENOENT : -ENOSYS;
+ if (r < 0)
+ return r;
+
+ for (;;) {
+ _cleanup_free_ char *line = NULL;
+ int mid;
+
+ r = read_line(proc_self_mountinfo, LONG_LINE_MAX, &line);
+ if (r < 0)
+ return r;
+ if (r == 0)
+ break;
+
+ if (sscanf(line, "%i", &mid) != 1)
+ continue;
+
+ if (mid != mount_id)
+ continue;
+
+ e = strstrafter(line, " - ");
+ if (!e)
+ continue;
+
+ /* accept any name that starts with the currently expected type */
+ if (startswith(e, "devtmpfs"))
+ return true;
+ }
+
+ return false;
+}
+
+static int mount_fd(
+ const char *source,
+ int target_fd,
+ const char *filesystemtype,
+ unsigned long mountflags,
+ const void *data) {
+
+ assert(target_fd >= 0);
+
+ if (mount(source, FORMAT_PROC_FD_PATH(target_fd), filesystemtype, mountflags, data) < 0) {
+ if (errno != ENOENT)
+ return -errno;
+
+ /* ENOENT can mean two things: either that the source is missing, or that /proc/ isn't
+ * mounted. Check for the latter to generate better error messages. */
+ if (proc_mounted() == 0)
+ return -ENOSYS;
+
+ return -ENOENT;
+ }
+
+ return 0;
+}
+
+int mount_nofollow(
+ const char *source,
+ const char *target,
+ const char *filesystemtype,
+ unsigned long mountflags,
+ const void *data) {
+
+ _cleanup_close_ int fd = -EBADF;
+
+ assert(target);
+
+ /* In almost all cases we want to manipulate the mount table without following symlinks, hence
+ * mount_nofollow() is usually the way to go. The only exceptions are environments where /proc/ is
+ * not available yet, since we need /proc/self/fd/ for this logic to work. i.e. during the early
+ * initialization of namespacing/container stuff where /proc is not yet mounted (and maybe even the
+ * fs to mount) we can only use traditional mount() directly.
+ *
+ * Note that this disables following only for the final component of the target, i.e symlinks within
+ * the path of the target are honoured, as are symlinks in the source path everywhere. */
+
+ fd = open(target, O_PATH|O_CLOEXEC|O_NOFOLLOW);
+ if (fd < 0)
+ return -errno;
+
+ return mount_fd(source, fd, filesystemtype, mountflags, data);
+}
+
+const char* mount_propagation_flag_to_string(unsigned long flags) {
+
+ switch (flags & (MS_SHARED|MS_SLAVE|MS_PRIVATE)) {
+ case 0:
+ return "";
+ case MS_SHARED:
+ return "shared";
+ case MS_SLAVE:
+ return "slave";
+ case MS_PRIVATE:
+ return "private";
+ }
+
+ return NULL;
+}
+
+int mount_propagation_flag_from_string(const char *name, unsigned long *ret) {
+
+ if (isempty(name))
+ *ret = 0;
+ else if (streq(name, "shared"))
+ *ret = MS_SHARED;
+ else if (streq(name, "slave"))
+ *ret = MS_SLAVE;
+ else if (streq(name, "private"))
+ *ret = MS_PRIVATE;
+ else
+ return -EINVAL;
+ return 0;
+}
+
+bool mount_propagation_flag_is_valid(unsigned long flag) {
+ return IN_SET(flag, 0, MS_SHARED, MS_PRIVATE, MS_SLAVE);
+}
+
+bool mount_new_api_supported(void) {
+ static int cache = -1;
+ int r;
+
+ if (cache >= 0)
+ return cache;
+
+ /* This is the newer API among the ones we use, so use it as boundary */
+ r = RET_NERRNO(mount_setattr(-EBADF, NULL, 0, NULL, 0));
+ if (r == 0 || ERRNO_IS_NOT_SUPPORTED(r)) /* This should return an error if it is working properly */
+ return (cache = false);
+
+ return (cache = true);
+}
+
+unsigned long ms_nosymfollow_supported(void) {
+ _cleanup_close_ int fsfd = -EBADF, mntfd = -EBADF;
+ static int cache = -1;
+
+ /* Returns MS_NOSYMFOLLOW if it is supported, zero otherwise. */
+
+ if (cache >= 0)
+ return cache ? MS_NOSYMFOLLOW : 0;
+
+ if (!mount_new_api_supported())
+ goto not_supported;
+
+ /* Checks if MS_NOSYMFOLLOW is supported (which was added in 5.10). We use the new mount API's
+ * mount_setattr() call for that, which was added in 5.12, which is close enough. */
+
+ fsfd = fsopen("tmpfs", FSOPEN_CLOEXEC);
+ if (fsfd < 0) {
+ if (ERRNO_IS_NOT_SUPPORTED(errno))
+ goto not_supported;
+
+ log_debug_errno(errno, "Failed to open superblock context for tmpfs: %m");
+ return 0;
+ }
+
+ if (fsconfig(fsfd, FSCONFIG_CMD_CREATE, NULL, NULL, 0) < 0) {
+ if (ERRNO_IS_NOT_SUPPORTED(errno))
+ goto not_supported;
+
+ log_debug_errno(errno, "Failed to create tmpfs superblock: %m");
+ return 0;
+ }
+
+ mntfd = fsmount(fsfd, FSMOUNT_CLOEXEC, 0);
+ if (mntfd < 0) {
+ if (ERRNO_IS_NOT_SUPPORTED(errno))
+ goto not_supported;
+
+ log_debug_errno(errno, "Failed to turn superblock fd into mount fd: %m");
+ return 0;
+ }
+
+ if (mount_setattr(mntfd, "", AT_EMPTY_PATH|AT_RECURSIVE,
+ &(struct mount_attr) {
+ .attr_set = MOUNT_ATTR_NOSYMFOLLOW,
+ }, sizeof(struct mount_attr)) < 0) {
+ if (ERRNO_IS_NOT_SUPPORTED(errno))
+ goto not_supported;
+
+ log_debug_errno(errno, "Failed to set MOUNT_ATTR_NOSYMFOLLOW mount attribute: %m");
+ return 0;
+ }
+
+ cache = true;
+ return MS_NOSYMFOLLOW;
+
+not_supported:
+ cache = false;
+ return 0;
+}
+
+int mount_option_supported(const char *fstype, const char *key, const char *value) {
+ _cleanup_close_ int fd = -EBADF;
+ int r;
+
+ /* Checks if the specified file system supports a mount option. Returns > 0 if it supports it, == 0 if
+ * it does not. Return -EAGAIN if we can't determine it. And any other error otherwise. */
+
+ assert(fstype);
+ assert(key);
+
+ fd = fsopen(fstype, FSOPEN_CLOEXEC);
+ if (fd < 0)
+ return log_debug_errno(errno, "Failed to open superblock context for '%s': %m", fstype);
+
+ /* Various file systems support fs context only in recent kernels (e.g. btrfs). For older kernels
+ * fsconfig() with FSCONFIG_SET_STRING/FSCONFIG_SET_FLAG never fail. Which sucks, because we want to
+ * use it for testing support, after all. Let's hence do a check if the file system got converted yet
+ * first. */
+ if (fsconfig(fd, FSCONFIG_SET_FD, "adefinitelynotexistingmountoption", NULL, fd) < 0) {
+ /* If FSCONFIG_SET_FD is not supported for the fs, then the file system was not converted to
+ * the new mount API yet. If it returns EINVAL the mount option doesn't exist, but the fstype
+ * is converted. */
+ if (errno == EOPNOTSUPP)
+ return -EAGAIN; /* fs not converted to new mount API → don't know */
+ if (errno != EINVAL)
+ return log_debug_errno(errno, "Failed to check if file system '%s' has been converted to new mount API: %m", fstype);
+
+ /* So FSCONFIG_SET_FD worked, but the option didn't exist (we got EINVAL), this means the fs
+ * is converted. Let's now ask the actual question we wonder about. */
+ } else
+ return log_debug_errno(SYNTHETIC_ERRNO(EAGAIN), "FSCONFIG_SET_FD worked unexpectedly for '%s', whoa!", fstype);
+
+ if (value)
+ r = fsconfig(fd, FSCONFIG_SET_STRING, key, value, 0);
+ else
+ r = fsconfig(fd, FSCONFIG_SET_FLAG, key, NULL, 0);
+ if (r < 0) {
+ if (errno == EINVAL)
+ return false; /* EINVAL means option not supported. */
+
+ return log_debug_errno(errno, "Failed to set '%s%s%s' on '%s' superblock context: %m",
+ key, value ? "=" : "", strempty(value), fstype);
+ }
+
+ return true; /* works! */
+}
+
+bool path_below_api_vfs(const char *p) {
+ assert(p);
+
+ /* API VFS are either directly mounted on any of these three paths, or below it. */
+ return PATH_STARTSWITH_SET(p, "/dev", "/sys", "/proc");
+}
+#endif /* NM_IGNORED */
\ No newline at end of file
diff --git a/src/libnm-systemd-shared/src/basic/mountpoint-util.h b/src/libnm-systemd-shared/src/basic/mountpoint-util.h
new file mode 100644
index 0000000000..43e4758143
--- /dev/null
+++ b/src/libnm-systemd-shared/src/basic/mountpoint-util.h
@@ -0,0 +1,87 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+#pragma once
+
+#include
+#include
+#include
+#include
+
+/* The limit used for /dev itself. 4MB should be enough since device nodes and symlinks don't
+ * consume any space and udev isn't supposed to create regular file either. There's no limit on the
+ * max number of inodes since such limit is hard to guess especially on large storage array
+ * systems. */
+#define TMPFS_LIMITS_DEV ",size=4m"
+
+/* The limit used for /dev in private namespaces. 4MB for contents of regular files. The number of
+ * inodes should be relatively low in private namespaces but for now use a 64k limit. */
+#define TMPFS_LIMITS_PRIVATE_DEV ",size=4m,nr_inodes=64k"
+
+/* Very little, if any use expected */
+#define TMPFS_LIMITS_EMPTY_OR_ALMOST ",size=4m,nr_inodes=1k"
+#define TMPFS_LIMITS_SYS TMPFS_LIMITS_EMPTY_OR_ALMOST
+#define TMPFS_LIMITS_SYS_FS_CGROUP TMPFS_LIMITS_EMPTY_OR_ALMOST
+
+/* On an extremely small device with only 256MB of RAM, 20% of RAM should be enough for the re-execution of
+ * PID1 because 16MB of free space is required. */
+#define TMPFS_LIMITS_RUN ",size=20%,nr_inodes=800k"
+
+/* The limit used for various nested tmpfs mounts, in particular for guests started by systemd-nspawn.
+ * 10% of RAM (using 16GB of RAM as a baseline) translates to 400k inodes (assuming 4k each) and 25%
+ * translates to 1M inodes.
+ * (On the host, /tmp is configured through a .mount unit file.) */
+#define NESTED_TMPFS_LIMITS ",size=10%,nr_inodes=400k"
+
+/* More space for volatile root and /var */
+#define TMPFS_LIMITS_VAR ",size=25%,nr_inodes=1m"
+#define TMPFS_LIMITS_ROOTFS TMPFS_LIMITS_VAR
+#define TMPFS_LIMITS_VOLATILE_STATE TMPFS_LIMITS_VAR
+
+bool is_name_to_handle_at_fatal_error(int err);
+
+int name_to_handle_at_loop(int fd, const char *path, struct file_handle **ret_handle, int *ret_mnt_id, int flags);
+int name_to_handle_at_try_fid(int fd, const char *path, struct file_handle **ret_handle, int *ret_mnt_id, int flags);
+
+bool file_handle_equal(const struct file_handle *a, const struct file_handle *b);
+
+int path_get_mnt_id_at_fallback(int dir_fd, const char *path, int *ret);
+int path_get_mnt_id_at(int dir_fd, const char *path, int *ret);
+static inline int path_get_mnt_id(const char *path, int *ret) {
+ return path_get_mnt_id_at(AT_FDCWD, path, ret);
+}
+
+int is_mount_point_at(int fd, const char *filename, int flags);
+int path_is_mount_point_full(const char *path, const char *root, int flags);
+static inline int path_is_mount_point(const char *path) {
+ return path_is_mount_point_full(path, NULL, 0);
+}
+
+bool fstype_is_network(const char *fstype);
+bool fstype_needs_quota(const char *fstype);
+bool fstype_is_api_vfs(const char *fstype);
+bool fstype_is_blockdev_backed(const char *fstype);
+bool fstype_is_ro(const char *fsype);
+bool fstype_can_discard(const char *fstype);
+bool fstype_can_uid_gid(const char *fstype);
+bool fstype_can_fmask_dmask(const char *fstype);
+
+const char* fstype_norecovery_option(const char *fstype);
+
+int dev_is_devtmpfs(void);
+
+int mount_nofollow(
+ const char *source,
+ const char *target,
+ const char *filesystemtype,
+ unsigned long mountflags,
+ const void *data);
+
+const char* mount_propagation_flag_to_string(unsigned long flags);
+int mount_propagation_flag_from_string(const char *name, unsigned long *ret);
+bool mount_propagation_flag_is_valid(unsigned long flag);
+
+bool mount_new_api_supported(void);
+unsigned long ms_nosymfollow_supported(void);
+
+int mount_option_supported(const char *fstype, const char *key, const char *value);
+
+bool path_below_api_vfs(const char *p);
diff --git a/src/libnm-systemd-shared/src/basic/namespace-util.h b/src/libnm-systemd-shared/src/basic/namespace-util.h
index 3d40a515e7..d7ac8156f9 100644
--- a/src/libnm-systemd-shared/src/basic/namespace-util.h
+++ b/src/libnm-systemd-shared/src/basic/namespace-util.h
@@ -28,6 +28,8 @@ extern const struct namespace_info {
NamespaceType clone_flag_to_namespace_type(unsigned long clone_flag);
+bool namespace_type_supported(NamespaceType type);
+
int pidref_namespace_open_by_type(const PidRef *pidref, NamespaceType type);
int namespace_open_by_type(NamespaceType type);
@@ -86,7 +88,8 @@ static inline bool userns_shift_range_valid(uid_t shift, uid_t range) {
int parse_userns_uid_range(const char *s, uid_t *ret_uid_shift, uid_t *ret_uid_range);
int userns_acquire_empty(void);
-int userns_acquire(const char *uid_map, const char *gid_map);
+int userns_acquire(const char *uid_map, const char *gid_map, bool setgroups_deny);
+int userns_acquire_self_root(void);
int userns_enter_and_pin(int userns_fd, pid_t *ret_pid);
int userns_get_base_uid(int userns_fd, uid_t *ret_uid, gid_t *ret_gid);
diff --git a/src/libnm-systemd-shared/src/basic/ordered-set.h b/src/libnm-systemd-shared/src/basic/ordered-set.h
index e73da20573..b7113ad918 100644
--- a/src/libnm-systemd-shared/src/basic/ordered-set.h
+++ b/src/libnm-systemd-shared/src/basic/ordered-set.h
@@ -22,18 +22,10 @@ static inline void ordered_set_clear(OrderedSet *s) {
return ordered_hashmap_clear((OrderedHashmap*) s);
}
-static inline void ordered_set_clear_free(OrderedSet *s) {
- return ordered_hashmap_clear_free((OrderedHashmap*) s);
-}
-
static inline OrderedSet* ordered_set_free(OrderedSet *s) {
return (OrderedSet*) ordered_hashmap_free((OrderedHashmap*) s);
}
-static inline OrderedSet* ordered_set_free_free(OrderedSet *s) {
- return (OrderedSet*) ordered_hashmap_free_free((OrderedHashmap*) s);
-}
-
static inline int ordered_set_contains(OrderedSet *s, const void *p) {
return ordered_hashmap_contains((OrderedHashmap*) s, p);
}
@@ -91,19 +83,6 @@ void ordered_set_print(FILE *f, const char *field, OrderedSet *s);
#define ORDERED_SET_FOREACH(e, s) \
_ORDERED_SET_FOREACH(e, s, UNIQ_T(i, UNIQ))
-#define ordered_set_clear_with_destructor(s, f) \
- ({ \
- OrderedSet *_s = (s); \
- void *_item; \
- while ((_item = ordered_set_steal_first(_s))) \
- f(_item); \
- _s; \
- })
-#define ordered_set_free_with_destructor(s, f) \
- ordered_set_free(ordered_set_clear_with_destructor(s, f))
-
DEFINE_TRIVIAL_CLEANUP_FUNC(OrderedSet*, ordered_set_free);
-DEFINE_TRIVIAL_CLEANUP_FUNC(OrderedSet*, ordered_set_free_free);
#define _cleanup_ordered_set_free_ _cleanup_(ordered_set_freep)
-#define _cleanup_ordered_set_free_free_ _cleanup_(ordered_set_free_freep)
diff --git a/src/libnm-systemd-shared/src/basic/parse-util.c b/src/libnm-systemd-shared/src/basic/parse-util.c
index bff09b7416..e524d9d52d 100644
--- a/src/libnm-systemd-shared/src/basic/parse-util.c
+++ b/src/libnm-systemd-shared/src/basic/parse-util.c
@@ -5,6 +5,7 @@
#include
#include
#include
+#include
#include
#include
#include
@@ -14,6 +15,7 @@
#include "errno-list.h"
#include "extract-word.h"
#include "locale-util.h"
+#include "log.h"
#include "macro.h"
#include "missing_network.h"
#include "parse-util.h"
@@ -533,7 +535,7 @@ int safe_atollu_full(const char *s, unsigned base, unsigned long long *ret_llu)
return 0;
}
-int safe_atolli(const char *s, long long int *ret_lli) {
+int safe_atolli(const char *s, long long *ret_lli) {
unsigned base = 0;
char *x = NULL;
long long l;
@@ -784,19 +786,15 @@ int parse_loadavg_fixed_point(const char *s, loadavg_t *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)
+ if (isempty(id))
return false;
- size_t len = strlen(id);
- if (len == 0 || len > 31)
+ if (strlen(id) >= NFT_NAME_MAXLEN)
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;
+ return in_charset(id + 1, ALPHANUMERICAL "/\\_.");
}
#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 a47c8c7935..0af407b335 100644
--- a/src/libnm-systemd-shared/src/basic/parse-util.h
+++ b/src/libnm-systemd-shared/src/basic/parse-util.h
@@ -41,7 +41,7 @@ static inline int safe_atou(const char *s, unsigned *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);
+int safe_atolli(const char *s, long long *ret_i);
int safe_atou8_full(const char *s, unsigned base, uint8_t *ret);
@@ -87,8 +87,8 @@ static inline int safe_atou64(const char *s, uint64_t *ret_u) {
}
static inline int safe_atoi64(const char *s, int64_t *ret_i) {
- assert_cc(sizeof(int64_t) == sizeof(long long int));
- return safe_atolli(s, (long long int*) ret_i);
+ assert_cc(sizeof(int64_t) == sizeof(long long));
+ return safe_atolli(s, (long long*) ret_i);
}
static inline int safe_atoux64(const char *s, uint64_t *ret) {
@@ -101,8 +101,8 @@ static inline int safe_atolu_full(const char *s, unsigned base, unsigned long *r
assert_cc(sizeof(unsigned long) == sizeof(unsigned));
return safe_atou_full(s, base, (unsigned*) ret_u);
}
-static inline int safe_atoli(const char *s, long int *ret_u) {
- assert_cc(sizeof(long int) == sizeof(int));
+static inline int safe_atoli(const char *s, long *ret_u) {
+ assert_cc(sizeof(long) == sizeof(int));
return safe_atoi(s, (int*) ret_u);
}
#else
@@ -110,9 +110,9 @@ static inline int safe_atolu_full(const char *s, unsigned base, unsigned long *r
assert_cc(sizeof(unsigned long) == sizeof(unsigned long long));
return safe_atollu_full(s, base, (unsigned long long*) ret_u);
}
-static inline int safe_atoli(const char *s, long int *ret_u) {
- assert_cc(sizeof(long int) == sizeof(long long int));
- return safe_atolli(s, (long long int*) ret_u);
+static inline int safe_atoli(const char *s, long *ret_u) {
+ assert_cc(sizeof(long) == sizeof(long long));
+ return safe_atolli(s, (long long*) ret_u);
}
#endif
diff --git a/src/libnm-systemd-shared/src/basic/path-util.c b/src/libnm-systemd-shared/src/basic/path-util.c
index 1de055d540..74f256a69f 100644
--- a/src/libnm-systemd-shared/src/basic/path-util.c
+++ b/src/libnm-systemd-shared/src/basic/path-util.c
@@ -624,37 +624,9 @@ char* path_extend_internal(char **x, ...) {
}
#if 0 /* NM_IGNORED */
-static int check_x_access(const char *path, int *ret_fd) {
+int open_and_check_executable(const char *name, const char *root, char **ret_path, int *ret_fd) {
_cleanup_close_ int fd = -EBADF;
- int r;
-
- /* We need to use O_PATH because there may be executables for which we have only exec
- * permissions, but not read (usually suid executables). */
- fd = open(path, O_PATH|O_CLOEXEC);
- if (fd < 0)
- return -errno;
-
- r = fd_verify_regular(fd);
- if (r < 0)
- return r;
-
- r = access_fd(fd, X_OK);
- if (r == -ENOSYS) {
- /* /proc is not mounted. Fallback to access(). */
- if (access(path, X_OK) < 0)
- return -errno;
- } else if (r < 0)
- return r;
-
- if (ret_fd)
- *ret_fd = TAKE_FD(fd);
-
- return 0;
-}
-
-static int find_executable_impl(const char *name, const char *root, char **ret_filename, int *ret_fd) {
- _cleanup_close_ int fd = -EBADF;
- _cleanup_free_ char *path_name = NULL;
+ _cleanup_free_ char *resolved = NULL;
int r;
assert(name);
@@ -665,23 +637,40 @@ static int find_executable_impl(const char *name, const char *root, char **ret_f
* needed to avoid unforeseen regression or other complicated changes. */
if (root) {
/* prefix root to name in case full paths are not specified */
- r = chase(name, root, CHASE_PREFIX_ROOT, &path_name, /* ret_fd= */ NULL);
+ r = chase(name, root, CHASE_PREFIX_ROOT, &resolved, &fd);
if (r < 0)
return r;
- name = path_name;
+ name = resolved;
+ } else {
+ /* We need to use O_PATH because there may be executables for which we have only exec permissions,
+ * but not read (usually suid executables). */
+ fd = open(name, O_PATH|O_CLOEXEC);
+ if (fd < 0)
+ return -errno;
}
- r = check_x_access(name, ret_fd ? &fd : NULL);
+ r = fd_verify_regular(fd);
if (r < 0)
return r;
- if (ret_filename) {
- r = path_make_absolute_cwd(name, ret_filename);
- if (r < 0)
- return r;
+ r = access_fd(fd, X_OK);
+ if (r == -ENOSYS)
+ /* /proc/ is not mounted. Fall back to access(). */
+ r = RET_NERRNO(access(name, X_OK));
+ if (r < 0)
+ return r;
- path_simplify(*ret_filename);
+ if (ret_path) {
+ if (resolved)
+ *ret_path = TAKE_PTR(resolved);
+ else {
+ r = path_make_absolute_cwd(name, ret_path);
+ if (r < 0)
+ return r;
+
+ path_simplify(*ret_path);
+ }
}
if (ret_fd)
@@ -703,7 +692,7 @@ int find_executable_full(
assert(name);
if (is_path(name))
- return find_executable_impl(name, root, ret_filename, ret_fd);
+ return open_and_check_executable(name, root, ret_filename, ret_fd);
if (exec_search_path) {
STRV_FOREACH(element, exec_search_path) {
@@ -718,7 +707,7 @@ int find_executable_full(
if (!full_path)
return -ENOMEM;
- r = find_executable_impl(full_path, root, ret_filename, ret_fd);
+ r = open_and_check_executable(full_path, root, ret_filename, ret_fd);
if (r >= 0)
return 0;
if (r != -EACCES)
@@ -754,7 +743,7 @@ int find_executable_full(
if (!path_extend(&element, name))
return -ENOMEM;
- r = find_executable_impl(element, root, ret_filename, ret_fd);
+ r = open_and_check_executable(element, root, ret_filename, ret_fd);
if (r >= 0) /* Found it! */
return 0;
/* PATH entries which we don't have access to are ignored, as per tradition. */
diff --git a/src/libnm-systemd-shared/src/basic/path-util.h b/src/libnm-systemd-shared/src/basic/path-util.h
index 9b13d958db..5bfa8fc7d4 100644
--- a/src/libnm-systemd-shared/src/basic/path-util.h
+++ b/src/libnm-systemd-shared/src/basic/path-util.h
@@ -117,6 +117,7 @@ int path_strv_make_absolute_cwd(char **l);
char** path_strv_resolve(char **l, const char *root);
char** path_strv_resolve_uniq(char **l, const char *root);
+int open_and_check_executable(const char *name, const char *root, char **ret_path, int *ret_fd);
int find_executable_full(
const char *name,
const char *root,
diff --git a/src/libnm-systemd-shared/src/basic/pidfd-util.c b/src/libnm-systemd-shared/src/basic/pidfd-util.c
index d6bd551758..91638c770a 100644
--- a/src/libnm-systemd-shared/src/basic/pidfd-util.c
+++ b/src/libnm-systemd-shared/src/basic/pidfd-util.c
@@ -3,15 +3,18 @@
#include "nm-sd-adapt-shared.h"
#include
+#include
#include
#include "errno-util.h"
#include "fd-util.h"
#include "fileio.h"
+#include "log.h"
#include "macro.h"
#include "memory-util.h"
+#include "missing_fs.h"
#include "missing_magic.h"
-#include "missing_threads.h"
+#include "mountpoint-util.h"
#include "parse-util.h"
#include "path-util.h"
#include "pidfd-util.h"
@@ -89,26 +92,21 @@ static int pidfd_get_info(int fd, struct pidfd_info *info) {
static int pidfd_get_pid_fdinfo(int fd, pid_t *ret) {
char path[STRLEN("/proc/self/fdinfo/") + DECIMAL_STR_MAX(int)];
- _cleanup_free_ char *fdinfo = NULL;
+ _cleanup_free_ char *p = NULL;
int r;
assert(fd >= 0);
xsprintf(path, "/proc/self/fdinfo/%i", fd);
- r = read_full_virtual_file(path, &fdinfo, NULL);
+ r = get_proc_field(path, "Pid", &p);
if (r == -ENOENT)
- return proc_fd_enoent_errno();
+ return -EBADF;
+ if (r == -ENODATA) /* not a pidfd? */
+ return -ENOTTY;
if (r < 0)
return r;
- char *p = find_line_startswith(fdinfo, "Pid:");
- if (!p)
- return -ENOTTY; /* not a pidfd? */
-
- p = skip_leading_chars(p, /* bad = */ NULL);
- p[strcspn(p, WHITESPACE)] = 0;
-
if (streq(p, "0"))
return -EREMOTE; /* PID is in foreign PID namespace? */
if (streq(p, "-1"))
@@ -230,6 +228,7 @@ int pidfd_get_cgroupid(int fd, uint64_t *ret) {
#endif /* NM_IGNORED */
int pidfd_get_inode_id(int fd, uint64_t *ret) {
+ static bool file_handle_supported = true;
int r;
assert(fd >= 0);
@@ -240,6 +239,30 @@ int pidfd_get_inode_id(int fd, uint64_t *ret) {
if (r == 0)
return -EOPNOTSUPP;
+ if (file_handle_supported) {
+ union {
+ struct file_handle file_handle;
+ uint8_t space[offsetof(struct file_handle, f_handle) + sizeof(uint64_t)];
+ } fh = {
+ .file_handle.handle_bytes = sizeof(uint64_t),
+ .file_handle.handle_type = FILEID_KERNFS,
+ };
+ int mnt_id;
+
+ r = RET_NERRNO(name_to_handle_at(fd, "", &fh.file_handle, &mnt_id, AT_EMPTY_PATH));
+ if (r >= 0) {
+ if (ret)
+ *ret = *CAST_ALIGN_PTR(uint64_t, fh.file_handle.f_handle);
+ return 0;
+ }
+ assert(r != -EOVERFLOW);
+ if (is_name_to_handle_at_fatal_error(r))
+ return r;
+
+ file_handle_supported = false;
+ }
+
+#if SIZEOF_INO_T == 8
struct stat st;
if (fstat(fd, &st) < 0)
return -errno;
@@ -247,6 +270,16 @@ int pidfd_get_inode_id(int fd, uint64_t *ret) {
if (ret)
*ret = (uint64_t) st.st_ino;
return 0;
+
+#elif SIZEOF_INO_T == 4
+ /* On 32-bit systems (where sizeof(ino_t) == 4), the inode id returned by fstat() cannot be used to
+ * reliably identify the process, nor can we communicate the origin of the id with the clients.
+ * Hence let's just refuse to acquire pidfdid through fstat() here. All clients shall also insist on
+ * the 64-bit id from name_to_handle_at(). */
+ return -EOPNOTSUPP;
+#else
+# error Unsupported ino_t size
+#endif
}
int pidfd_get_inode_id_self_cached(uint64_t *ret) {
diff --git a/src/libnm-systemd-shared/src/basic/pidref.h b/src/libnm-systemd-shared/src/basic/pidref.h
index 9e8a39ecfb..064b9d851a 100644
--- a/src/libnm-systemd-shared/src/basic/pidref.h
+++ b/src/libnm-systemd-shared/src/basic/pidref.h
@@ -1,10 +1,8 @@
/* SPDX-License-Identifier: LGPL-2.1-or-later */
#pragma once
-typedef struct PidRef PidRef;
-
#include "macro.h"
-#include "process-util.h"
+#include "memory-util.h"
/* An embeddable structure carrying a reference to a process. Supposed to be used when tracking processes
* continuously. This combines a PID, a modern Linux pidfd and the 64bit inode number of the pidfd into one
@@ -29,22 +27,22 @@ typedef struct PidRef PidRef;
* process. Moreover, most operations will fail with -EREMOTE. Only PidRef structures that are not marked
* *unset* can be marked *remote*.
*/
-struct PidRef {
- pid_t pid; /* > 0 if the PidRef is set, otherwise set to PID_AUTOMATIC if automatic mode is
- * desired, or 0 otherwise. */
+typedef struct PidRef {
+ pid_t pid; /* > 0 if the PidRef is set, otherwise set to INT_MIN (PID_AUTOMATIC) if automatic
+ * mode is desired, or 0 otherwise. */
int fd; /* only valid if pidfd are available in the kernel, and we manage to get an fd. If we
* know that the PID is not from the local machine we set this to -EREMOTE, otherwise
* we use -EBADF as indicator the fd is invalid. */
uint64_t fd_id; /* the inode number of pidfd. only useful in kernel 6.9+ where pidfds live in
their own pidfs and each process comes with a unique inode number */
-};
+} PidRef;
#define PIDREF_NULL (PidRef) { .fd = -EBADF }
/* A special pidref value that we are using when a PID shall be automatically acquired from some surrounding
* context, for example connection peer. Much like PIDREF_NULL it will be considered unset by
- * pidref_is_set().*/
-#define PIDREF_AUTOMATIC (const PidRef) { .pid = PID_AUTOMATIC, .fd = -EBADF }
+ * pidref_is_set(). */
+#define PIDREF_AUTOMATIC (const PidRef) { .pid = (pid_t) INT_MIN, .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) */
@@ -70,7 +68,7 @@ bool pidref_equal(PidRef *a, PidRef *b);
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_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) {
@@ -108,5 +106,9 @@ int pidref_verify(const PidRef *pidref);
#define TAKE_PIDREF(p) TAKE_GENERIC((p), PidRef, PIDREF_NULL)
+struct siphash;
+void pidref_hash_func(const PidRef *pidref, struct siphash *state);
+int pidref_compare_func(const PidRef *a, const PidRef *b);
+
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 c36d0ddd38..0a007a29e5 100644
--- a/src/libnm-systemd-shared/src/basic/prioq.c
+++ b/src/libnm-systemd-shared/src/basic/prioq.c
@@ -48,6 +48,11 @@ Prioq* prioq_free(Prioq *q) {
if (!q)
return NULL;
+ /* Invalidate the index fields of any remaining objects */
+ FOREACH_ARRAY(item, q->items, q->n_items)
+ if (item->idx)
+ *(item->idx) = PRIOQ_IDX_NULL;
+
free(q->items);
return mfree(q);
}
@@ -180,6 +185,11 @@ static void remove_item(Prioq *q, struct prioq_item *i) {
assert(q);
assert(i);
+ /* Let's invalidate the index pointer stored in the user's object to indicate the item is now removed
+ * from the priority queue */
+ if (i->idx)
+ *(i->idx) = PRIOQ_IDX_NULL;
+
l = q->items + q->n_items - 1;
if (i == l)
@@ -191,6 +201,7 @@ static void remove_item(Prioq *q, struct prioq_item *i) {
/* Not last entry, let's replace the last entry with
* this one, and reshuffle */
+ assert(i >= q->items);
k = i - q->items;
i->data = l->data;
@@ -254,6 +265,7 @@ void prioq_reshuffle(Prioq *q, void *data, unsigned *idx) {
if (!i)
return;
+ assert(i >= q->items);
k = i - q->items;
k = shuffle_down(q, k);
shuffle_up(q, k);
diff --git a/src/libnm-systemd-shared/src/basic/process-util.c b/src/libnm-systemd-shared/src/basic/process-util.c
index 8518756222..517990b5c9 100644
--- a/src/libnm-systemd-shared/src/basic/process-util.c
+++ b/src/libnm-systemd-shared/src/basic/process-util.c
@@ -17,6 +17,7 @@
#include
#include
#include
+#include
#include
#if 0 /* NM_IGNORED */
#if HAVE_VALGRIND_VALGRIND_H
@@ -38,13 +39,13 @@
#include "fs-util.h"
#include "hostname-util.h"
#include "io-util.h"
+#include "iovec-util.h"
#include "locale-util.h"
#include "log.h"
#include "macro.h"
#include "memory-util.h"
#include "missing_sched.h"
#include "missing_syscall.h"
-#include "missing_threads.h"
#include "mountpoint-util.h"
#include "namespace-util.h"
#include "nulstr-util.h"
@@ -55,6 +56,7 @@
#include "raw-clone.h"
#include "rlimit-util.h"
#include "signal-util.h"
+#include "socket-util.h"
#include "stat-util.h"
#include "stdio-util.h"
#include "string-table.h"
@@ -509,48 +511,10 @@ int get_process_exe(pid_t pid, char **ret) {
return 0;
}
-static int get_process_id(pid_t pid, const char *field, uid_t *ret) {
- _cleanup_fclose_ FILE *f = NULL;
- const char *p;
+int pid_get_uid(pid_t pid, uid_t *ret) {
int r;
- assert(field);
- assert(ret);
-
- if (pid < 0)
- return -EINVAL;
-
- p = procfs_file_alloca(pid, "status");
- r = fopen_unlocked(p, "re", &f);
- if (r == -ENOENT)
- return -ESRCH;
- if (r < 0)
- return r;
-
- for (;;) {
- _cleanup_free_ char *line = NULL;
- char *l;
-
- r = read_stripped_line(f, LONG_LINE_MAX, &line);
- if (r < 0)
- return r;
- if (r == 0)
- break;
-
- l = startswith(line, field);
- if (l) {
- l += strspn(l, WHITESPACE);
-
- l[strcspn(l, WHITESPACE)] = 0;
-
- return parse_uid(l, ret);
- }
- }
-
- return -EIO;
-}
-
-int pid_get_uid(pid_t pid, uid_t *ret) {
+ assert(pid >= 0);
assert(ret);
if (pid == 0 || pid == getpid_cached()) {
@@ -558,7 +522,14 @@ int pid_get_uid(pid_t pid, uid_t *ret) {
return 0;
}
- return get_process_id(pid, "Uid:", ret);
+ _cleanup_free_ char *v = NULL;
+ r = procfs_file_get_field(pid, "status", "Uid", &v);
+ if (r == -ENOENT)
+ return -ESRCH;
+ if (r < 0)
+ return r;
+
+ return parse_uid(v, ret);
}
int pidref_get_uid(const PidRef *pid, uid_t *ret) {
@@ -591,14 +562,24 @@ int pidref_get_uid(const PidRef *pid, uid_t *ret) {
}
int get_process_gid(pid_t pid, gid_t *ret) {
+ int r;
+
+ assert(pid >= 0);
+ assert(ret);
if (pid == 0 || pid == getpid_cached()) {
*ret = getgid();
return 0;
}
- assert_cc(sizeof(uid_t) == sizeof(gid_t));
- return get_process_id(pid, "Gid:", ret);
+ _cleanup_free_ char *v = NULL;
+ r = procfs_file_get_field(pid, "status", "Gid", &v);
+ if (r == -ENOENT)
+ return -ESRCH;
+ if (r < 0)
+ return r;
+
+ return parse_gid(v, ret);
}
int get_process_cwd(pid_t pid, char **ret) {
@@ -864,15 +845,12 @@ int pidref_get_start_time(const PidRef *pid, usec_t *ret) {
int get_process_umask(pid_t pid, mode_t *ret) {
_cleanup_free_ char *m = NULL;
- const char *p;
int r;
assert(pid >= 0);
assert(ret);
- p = procfs_file_alloca(pid, "status");
-
- r = get_proc_field(p, "Umask", WHITESPACE, &m);
+ r = procfs_file_get_field(pid, "status", "Umask", &m);
if (r == -ENOENT)
return -ESRCH;
if (r < 0)
@@ -881,27 +859,8 @@ int get_process_umask(pid_t pid, mode_t *ret) {
return parse_mode(m, ret);
}
-int wait_for_terminate(pid_t pid, siginfo_t *status) {
- siginfo_t dummy;
-
- assert(pid >= 1);
-
- if (!status)
- status = &dummy;
-
- for (;;) {
- zero(*status);
-
- if (waitid(P_PID, pid, status, WEXITED) < 0) {
-
- if (errno == EINTR)
- continue;
-
- return negative_errno();
- }
-
- return 0;
- }
+int wait_for_terminate(pid_t pid, siginfo_t *ret) {
+ return pidref_wait_for_terminate(&PIDREF_MAKE_FROM_PID(pid), ret);
}
/*
@@ -918,24 +877,29 @@ int wait_for_terminate(pid_t pid, siginfo_t *status) {
* A warning is emitted if the process terminates abnormally,
* and also if it returns non-zero unless check_exit_code is true.
*/
-int wait_for_terminate_and_check(const char *name, pid_t pid, WaitFlags flags) {
+int pidref_wait_for_terminate_and_check(const char *name, PidRef *pidref, WaitFlags flags) {
+ int r;
+
+ if (!pidref_is_set(pidref))
+ return -ESRCH;
+ if (pidref_is_remote(pidref))
+ return -EREMOTE;
+ if (pidref->pid == 1 || pidref_is_self(pidref))
+ return -ECHILD;
+
_cleanup_free_ char *buffer = NULL;
- siginfo_t status;
- int r, prio;
-
- assert(pid > 1);
-
if (!name) {
- r = pid_get_comm(pid, &buffer);
+ r = pidref_get_comm(pidref, &buffer);
if (r < 0)
- log_debug_errno(r, "Failed to acquire process name of " PID_FMT ", ignoring: %m", pid);
+ log_debug_errno(r, "Failed to acquire process name of " PID_FMT ", ignoring: %m", pidref->pid);
else
name = buffer;
}
- prio = flags & WAIT_LOG_ABNORMAL ? LOG_ERR : LOG_DEBUG;
+ int prio = flags & WAIT_LOG_ABNORMAL ? LOG_ERR : LOG_DEBUG;
- r = wait_for_terminate(pid, &status);
+ siginfo_t status;
+ r = pidref_wait_for_terminate(pidref, &status);
if (r < 0)
return log_full_errno(prio, r, "Failed to wait for %s: %m", strna(name));
@@ -958,6 +922,10 @@ int wait_for_terminate_and_check(const char *name, pid_t pid, WaitFlags flags) {
return -EPROTO;
}
+int wait_for_terminate_and_check(const char *name, pid_t pid, WaitFlags flags) {
+ return pidref_wait_for_terminate_and_check(name, &PIDREF_MAKE_FROM_PID(pid), flags);
+}
+
/*
* Return values:
*
@@ -1385,8 +1353,8 @@ void valgrind_summary_hack(void) {
if (pid < 0)
log_struct_errno(
LOG_EMERG, errno,
- "MESSAGE_ID=" SD_MESSAGE_VALGRIND_HELPER_FORK_STR,
- LOG_MESSAGE( "Failed to fork off valgrind helper: %m"));
+ LOG_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 {
@@ -1446,7 +1414,7 @@ pid_t getpid_cached(void) {
case CACHED_PID_UNSET: { /* Not initialized yet, then do so now */
pid_t new_pid;
- new_pid = raw_getpid();
+ new_pid = getpid();
if (!installed) {
/* __register_atfork() either returns 0 or -ENOMEM, in its glibc implementation. Since it's
@@ -1467,7 +1435,7 @@ pid_t getpid_cached(void) {
}
case CACHED_PID_BUSY: /* Somebody else is currently initializing */
- return raw_getpid();
+ return getpid();
default: /* Properly initialized */
return current_value;
@@ -1533,25 +1501,28 @@ static int fork_flags_to_signal(ForkFlags flags) {
SIGKILL;
}
-int safe_fork_full(
+int pidref_safe_fork_full(
const char *name,
const int stdio_fds[3],
int except_fds[],
size_t n_except_fds,
ForkFlags flags,
- pid_t *ret_pid) {
+ PidRef *ret_pid) {
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, intermediary = false;
+ _cleanup_close_pair_ int pidref_transport_fds[2] = EBADF_PAIR;
int prio, r;
+ assert(!FLAGS_SET(flags, FORK_WAIT|FORK_FREEZE));
assert(!FLAGS_SET(flags, FORK_DETACH) ||
- (!ret_pid && (flags & (FORK_WAIT|FORK_DEATHSIG_SIGTERM|FORK_DEATHSIG_SIGINT|FORK_DEATHSIG_SIGKILL)) == 0));
+ (flags & (FORK_WAIT|FORK_DEATHSIG_SIGTERM|FORK_DEATHSIG_SIGINT|FORK_DEATHSIG_SIGKILL)) == 0);
- /* 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. */
+ /* A wrapper around fork(), that does a couple of important initializations in addition to mere
+ * forking. If provided, ret_pid is initialized in both the parent and the child process, both times
+ * referencing the child process. Returns == 0 in the child and > 0 in the parent. */
prio = flags & FORK_LOG ? LOG_ERR : LOG_DEBUG;
@@ -1594,14 +1565,43 @@ int safe_fork_full(
if (!r) {
/* Not a reaper process, hence do a double fork() so we are reparented to one */
+ if (ret_pid && socketpair(AF_UNIX, SOCK_DGRAM|SOCK_CLOEXEC, 0, pidref_transport_fds) < 0)
+ return log_full_errno(prio, errno, "Failed to allocate pidref socket: %m");
+
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);
+
+ pidref_transport_fds[1] = safe_close(pidref_transport_fds[1]);
+
+ if (pidref_transport_fds[0] >= 0) {
+ /* Wait for the intermediary child to exit so the caller can be certain the actual child
+ * process has been reparented by the time this function returns. */
+ r = wait_for_terminate_and_check(name, pid, FLAGS_SET(flags, FORK_LOG) ? WAIT_LOG : 0);
+ if (r < 0)
+ return log_full_errno(prio, r, "Failed to wait for intermediary process: %m");
+ if (r != EXIT_SUCCESS) /* exit status > 0 should be treated as failure, too */
+ return -EPROTO;
+
+ int pidfd;
+ ssize_t n = receive_one_fd_iov(
+ pidref_transport_fds[0],
+ &IOVEC_MAKE(&pid, sizeof(pid)),
+ /* iovlen= */ 1,
+ /* flags= */ 0,
+ &pidfd);
+ if (n < 0)
+ return log_full_errno(prio, n, "Failed to receive child pidref: %m");
+
+ *ret_pid = (PidRef) { .pid = pid, .fd = pidfd };
+ }
+
return 1; /* return in the parent */
}
+ pidref_transport_fds[0] = safe_close(pidref_transport_fds[0]);
intermediary = true;
}
}
@@ -1619,8 +1619,30 @@ int safe_fork_full(
if (pid > 0) {
/* If we are in the intermediary process, exit now */
- if (intermediary)
+ if (intermediary) {
+ if (pidref_transport_fds[1] >= 0) {
+ _cleanup_(pidref_done) PidRef pidref = PIDREF_NULL;
+
+ r = pidref_set_pid(&pidref, pid);
+ if (r < 0) {
+ log_full_errno(prio, r, "Failed to open reference to PID "PID_FMT": %m", pid);
+ _exit(EXIT_FAILURE);
+ }
+
+ r = send_one_fd_iov(
+ pidref_transport_fds[1],
+ pidref.fd,
+ &IOVEC_MAKE(&pidref.pid, sizeof(pidref.pid)),
+ /* iovlen= */ 1,
+ /* flags= */ 0);
+ if (r < 0) {
+ log_full_errno(prio, r, "Failed to send child pidref: %m");
+ _exit(EXIT_FAILURE);
+ }
+ }
+
_exit(EXIT_SUCCESS);
+ }
/* We are in the parent process */
log_debug("Successfully forked off '%s' as PID " PID_FMT ".", strna(name), pid);
@@ -1638,16 +1660,31 @@ int safe_fork_full(
return r;
if (r != EXIT_SUCCESS) /* exit status > 0 should be treated as failure, too */
return -EPROTO;
+
+ /* If we are in the parent and successfully waited, then the process doesn't exist anymore. */
+ if (ret_pid)
+ *ret_pid = PIDREF_NULL;
+
+ return 1;
}
- if (ret_pid)
- *ret_pid = pid;
+ if (ret_pid) {
+ if (FLAGS_SET(flags, FORK_PID_ONLY))
+ *ret_pid = PIDREF_MAKE_FROM_PID(pid);
+ else {
+ r = pidref_set_pid(ret_pid, pid);
+ if (r < 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 1;
}
/* We are in the child process */
+ pidref_transport_fds[1] = safe_close(pidref_transport_fds[1]);
+
/* Restore signal mask manually */
saved_ssp = NULL;
@@ -1809,36 +1846,41 @@ int safe_fork_full(
if (FLAGS_SET(flags, FORK_FREEZE))
freeze();
- if (ret_pid)
- *ret_pid = getpid_cached();
+ if (ret_pid) {
+ if (FLAGS_SET(flags, FORK_PID_ONLY))
+ *ret_pid = PIDREF_MAKE_FROM_PID(getpid_cached());
+ else {
+ r = pidref_set_self(ret_pid);
+ if (r < 0) {
+ log_full_errno(prio, r, "Failed to acquire PID reference on ourselves: %m");
+ _exit(EXIT_FAILURE);
+ }
+ }
+ }
return 0;
}
-int pidref_safe_fork_full(
+int safe_fork_full(
const char *name,
const int stdio_fds[3],
int except_fds[],
size_t n_except_fds,
ForkFlags flags,
- PidRef *ret_pid) {
+ pid_t *ret_pid) {
- pid_t pid;
- int r, q;
+ _cleanup_(pidref_done) PidRef pidref = PIDREF_NULL;
+ int r;
- r = safe_fork_full(name, stdio_fds, except_fds, n_except_fds, flags, &pid);
+ /* Getting the detached child process pid without pidfd is racy, so don't allow it if not returning
+ * a pidref to the caller. */
+ assert(!FLAGS_SET(flags, FORK_DETACH) || !ret_pid);
+
+ r = pidref_safe_fork_full(name, stdio_fds, except_fds, n_except_fds, flags|FORK_PID_ONLY, ret_pid ? &pidref : NULL);
if (r < 0 || !ret_pid)
return r;
- if (r > 0 && FLAGS_SET(flags, FORK_WAIT)) {
- /* If we are in the parent and successfully waited, then the process doesn't exist anymore */
- *ret_pid = PIDREF_NULL;
- 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);
+ *ret_pid = pidref.pid;
return r;
}
@@ -2013,17 +2055,14 @@ _noreturn_ void freeze(void) {
int get_process_threads(pid_t pid) {
_cleanup_free_ char *t = NULL;
- const char *p;
int n, r;
if (pid < 0)
return -EINVAL;
- p = procfs_file_alloca(pid, "status");
-
- r = get_proc_field(p, "Threads", WHITESPACE, &t);
+ r = procfs_file_get_field(pid, "status", "Threads", &t);
if (r == -ENOENT)
- return proc_mounted() == 0 ? -ENOSYS : -ESRCH;
+ return -ESRCH;
if (r < 0)
return r;
diff --git a/src/libnm-systemd-shared/src/basic/process-util.h b/src/libnm-systemd-shared/src/basic/process-util.h
index 19ac55ddf7..395df64ae5 100644
--- a/src/libnm-systemd-shared/src/basic/process-util.h
+++ b/src/libnm-systemd-shared/src/basic/process-util.h
@@ -12,6 +12,8 @@
#include
#include "alloc-util.h"
+#include "assert-util.h"
+#include "fileio.h"
#include "format-util.h"
#include "macro.h"
#include "pidref.h"
@@ -22,16 +24,20 @@
pid_t _pid_ = (pid); \
const char *_field_ = (field); \
char *_r_; \
- if (_pid_ == 0) { \
- _r_ = newa(char, STRLEN("/proc/self/") + strlen(_field_) + 1); \
- strcpy(stpcpy(_r_, "/proc/self/"), _field_); \
- } else { \
+ if (_pid_ == 0) \
+ _r_ = strjoina("/proc/self/", _field_); \
+ else { \
+ assert(_pid_ > 0); \
_r_ = newa(char, STRLEN("/proc/") + DECIMAL_STR_MAX(pid_t) + 1 + strlen(_field_) + 1); \
sprintf(_r_, "/proc/" PID_FMT "/%s", _pid_, _field_); \
} \
(const char*) _r_; \
})
+static inline int procfs_file_get_field(pid_t pid, const char *name, const char *key, char **ret) {
+ return get_proc_field(procfs_file_alloca(pid, name), key, ret);
+}
+
typedef enum ProcessCmdlineFlags {
PROCESS_CMDLINE_COMM_FALLBACK = 1 << 0,
PROCESS_CMDLINE_USE_LOCALE = 1 << 1,
@@ -61,7 +67,11 @@ int get_process_umask(pid_t pid, mode_t *ret);
int container_get_leader(const char *machine, pid_t *pid);
-int wait_for_terminate(pid_t pid, siginfo_t *status);
+static inline bool SIGINFO_CODE_IS_DEAD(int code) {
+ return IN_SET(code, CLD_EXITED, CLD_KILLED, CLD_DUMPED);
+}
+
+int wait_for_terminate(pid_t pid, siginfo_t *ret);
typedef enum WaitFlags {
WAIT_LOG_ABNORMAL = 1 << 0,
@@ -71,7 +81,9 @@ typedef enum WaitFlags {
WAIT_LOG = WAIT_LOG_ABNORMAL|WAIT_LOG_NON_ZERO_EXIT_STATUS,
} WaitFlags;
+int pidref_wait_for_terminate_and_check(const char *name, PidRef *pidref, WaitFlags flags);
int wait_for_terminate_and_check(const char *name, pid_t pid, WaitFlags flags);
+
int wait_for_terminate_with_timeout(pid_t pid, usec_t timeout);
void sigkill_wait(pid_t pid);
@@ -195,20 +207,9 @@ typedef enum ForkFlags {
FORK_NEW_NETNS = 1 << 20, /* Run child in its own network namespace 💣 DO NOT USE IN THREADED PROGRAMS! 💣 */
FORK_NEW_PIDNS = 1 << 21, /* Run child in its own PID namespace 💣 DO NOT USE IN THREADED PROGRAMS! 💣 */
FORK_FREEZE = 1 << 22, /* Don't return in child, just call freeze() instead */
+ FORK_PID_ONLY = 1 << 23, /* Don't open a pidfd referencing the child process */
} ForkFlags;
-int safe_fork_full(
- const char *name,
- const int stdio_fds[3],
- int except_fds[],
- size_t n_except_fds,
- ForkFlags flags,
- pid_t *ret_pid);
-
-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],
@@ -221,6 +222,18 @@ static inline int pidref_safe_fork(const char *name, ForkFlags flags, PidRef *re
return pidref_safe_fork_full(name, NULL, NULL, 0, flags, ret_pid);
}
+int safe_fork_full(
+ const char *name,
+ const int stdio_fds[3],
+ int except_fds[],
+ size_t n_except_fds,
+ ForkFlags flags,
+ pid_t *ret_pid);
+
+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 namespace_fork(
const char *outer_name,
const char *inner_name,
diff --git a/src/libnm-systemd-shared/src/basic/random-util.c b/src/libnm-systemd-shared/src/basic/random-util.c
index 1e1a24bdfa..dab4334ad9 100644
--- a/src/libnm-systemd-shared/src/basic/random-util.c
+++ b/src/libnm-systemd-shared/src/basic/random-util.c
@@ -13,6 +13,7 @@
#include
#include
#include
+#include
#include "alloc-util.h"
#include "env-util.h"
@@ -21,9 +22,9 @@
#include "fileio.h"
#include "io-util.h"
#include "iovec-util.h"
+#include "log.h"
#include "missing_random.h"
#include "missing_syscall.h"
-#include "missing_threads.h"
#include "parse-util.h"
#include "pidfd-util.h"
#include "process-util.h"
diff --git a/src/libnm-systemd-shared/src/basic/random-util.h b/src/libnm-systemd-shared/src/basic/random-util.h
index 0b5ba77190..587ca1c283 100644
--- a/src/libnm-systemd-shared/src/basic/random-util.h
+++ b/src/libnm-systemd-shared/src/basic/random-util.h
@@ -6,8 +6,10 @@
#include
#include
-void random_bytes(void *p, size_t n); /* Returns random bytes suitable for most uses, but may be insecure sometimes. */
-int crypto_random_bytes(void *p, size_t n); /* Returns secure random bytes after waiting for the RNG to initialize. */
+#include "macro.h"
+
+void random_bytes(void *p, size_t n) _nonnull_if_nonzero_(1, 2); /* Returns random bytes suitable for most uses, but may be insecure sometimes. */
+int crypto_random_bytes(void *p, size_t n) _nonnull_if_nonzero_(1, 2); /* Returns secure random bytes after waiting for the RNG to initialize. */
int crypto_random_bytes_allocate_iovec(size_t n, struct iovec *ret);
static inline uint64_t random_u64(void) {
@@ -29,6 +31,6 @@ static inline uint32_t random_u32(void) {
size_t random_pool_size(void);
-int random_write_entropy(int fd, const void *seed, size_t size, bool credit);
+int random_write_entropy(int fd, const void *seed, size_t size, bool credit) _nonnull_if_nonzero_(2, 3);
uint64_t random_u64_range(uint64_t max);
diff --git a/src/libnm-systemd-shared/src/basic/ratelimit.h b/src/libnm-systemd-shared/src/basic/ratelimit.h
index 7801ef4270..fd83426375 100644
--- a/src/libnm-systemd-shared/src/basic/ratelimit.h
+++ b/src/libnm-systemd-shared/src/basic/ratelimit.h
@@ -28,3 +28,55 @@ unsigned ratelimit_num_dropped(const RateLimit *rl);
usec_t ratelimit_end(const RateLimit *rl);
usec_t ratelimit_left(const RateLimit *rl);
+
+typedef struct LogRateLimit {
+ int error;
+ int level;
+ RateLimit ratelimit;
+} LogRateLimit;
+
+#define log_ratelimit_internal(_level, _error, _ratelimit, _file, _line, _func, _format, ...) \
+({ \
+ int _log_ratelimit_error = (_error); \
+ int _log_ratelimit_level = (_level); \
+ static LogRateLimit _log_ratelimit = { \
+ .ratelimit = (_ratelimit), \
+ }; \
+ unsigned _num_dropped_errors = ratelimit_num_dropped(&_log_ratelimit.ratelimit); \
+ if (_log_ratelimit_error != _log_ratelimit.error || _log_ratelimit_level != _log_ratelimit.level) { \
+ ratelimit_reset(&_log_ratelimit.ratelimit); \
+ _log_ratelimit.error = _log_ratelimit_error; \
+ _log_ratelimit.level = _log_ratelimit_level; \
+ } \
+ if (log_get_max_level() == LOG_DEBUG || ratelimit_below(&_log_ratelimit.ratelimit)) \
+ _log_ratelimit_error = _num_dropped_errors > 0 \
+ ? log_internal(_log_ratelimit_level, _log_ratelimit_error, _file, _line, _func, _format " (Dropped %u similar message(s))", ##__VA_ARGS__, _num_dropped_errors) \
+ : log_internal(_log_ratelimit_level, _log_ratelimit_error, _file, _line, _func, _format, ##__VA_ARGS__); \
+ _log_ratelimit_error; \
+})
+
+#define log_ratelimit_full_errno(level, error, _ratelimit, format, ...) \
+ ({ \
+ int _level = (level), _e = (error); \
+ _e = (log_get_max_level() >= LOG_PRI(_level)) \
+ ? log_ratelimit_internal(_level, _e, _ratelimit, PROJECT_FILE, __LINE__, __func__, format, ##__VA_ARGS__) \
+ : -ERRNO_VALUE(_e); \
+ _e < 0 ? _e : -ESTRPIPE; \
+ })
+
+#define log_ratelimit_full(level, _ratelimit, format, ...) \
+ log_ratelimit_full_errno(level, 0, _ratelimit, format, ##__VA_ARGS__)
+
+/* Normal logging */
+#define log_ratelimit_info(...) log_ratelimit_full(LOG_INFO, __VA_ARGS__)
+#define log_ratelimit_notice(...) log_ratelimit_full(LOG_NOTICE, __VA_ARGS__)
+#define log_ratelimit_warning(...) log_ratelimit_full(LOG_WARNING, __VA_ARGS__)
+#define log_ratelimit_error(...) log_ratelimit_full(LOG_ERR, __VA_ARGS__)
+#define log_ratelimit_emergency(...) log_ratelimit_full(log_emergency_level(), __VA_ARGS__)
+
+/* Logging triggered by an errno-like error */
+#define log_ratelimit_info_errno(error, ...) log_ratelimit_full_errno(LOG_INFO, error, __VA_ARGS__)
+#define log_ratelimit_notice_errno(error, ...) log_ratelimit_full_errno(LOG_NOTICE, error, __VA_ARGS__)
+#define log_ratelimit_warning_errno(error, ...) log_ratelimit_full_errno(LOG_WARNING, error, __VA_ARGS__)
+#define log_ratelimit_error_errno(error, ...) log_ratelimit_full_errno(LOG_ERR, error, __VA_ARGS__)
+#define log_ratelimit_emergency_errno(error, ...) log_ratelimit_full_errno(log_emergency_level(), error, __VA_ARGS__)
diff --git a/src/libnm-systemd-shared/src/basic/set.h b/src/libnm-systemd-shared/src/basic/set.h
index 618e729744..c7b84e0a54 100644
--- a/src/libnm-systemd-shared/src/basic/set.h
+++ b/src/libnm-systemd-shared/src/basic/set.h
@@ -12,15 +12,9 @@ Set* _set_new(const struct hash_ops *hash_ops HASHMAP_DEBUG_PARAMS);
#define set_new(ops) _set_new(ops HASHMAP_DEBUG_SRC_ARGS)
static inline Set* set_free(Set *s) {
- return (Set*) _hashmap_free(HASHMAP_BASE(s), NULL, NULL);
+ return (Set*) _hashmap_free(HASHMAP_BASE(s));
}
-static inline Set* set_free_free(Set *s) {
- return (Set*) _hashmap_free(HASHMAP_BASE(s), free, NULL);
-}
-
-/* no set_free_free_free */
-
#define set_copy(s) ((Set*) _hashmap_copy(HASHMAP_BASE(s) HASHMAP_DEBUG_SRC_ARGS))
int _set_ensure_allocated(Set **s, const struct hash_ops *hash_ops HASHMAP_DEBUG_PARAMS);
@@ -77,30 +71,13 @@ static inline bool set_iterate(const Set *s, Iterator *i, void **value) {
}
static inline void set_clear(Set *s) {
- _hashmap_clear(HASHMAP_BASE(s), NULL, NULL);
+ _hashmap_clear(HASHMAP_BASE(s));
}
-static inline void set_clear_free(Set *s) {
- _hashmap_clear(HASHMAP_BASE(s), free, NULL);
-}
-
-/* no set_clear_free_free */
-
static inline void *set_steal_first(Set *s) {
return _hashmap_first_key_and_value(HASHMAP_BASE(s), true, NULL);
}
-#define set_clear_with_destructor(s, f) \
- ({ \
- Set *_s = (s); \
- void *_item; \
- while ((_item = set_steal_first(_s))) \
- f(_item); \
- _s; \
- })
-#define set_free_with_destructor(s, f) \
- set_free(set_clear_with_destructor(s, f))
-
/* no set_steal_first_key */
/* no set_first_key */
@@ -114,6 +91,8 @@ static inline char **set_get_strv(Set *s) {
return _hashmap_get_strv(HASHMAP_BASE(s));
}
+char** set_to_strv(Set **s);
+
int _set_ensure_put(Set **s, const struct hash_ops *hash_ops, const void *key HASHMAP_DEBUG_PARAMS);
#define set_ensure_put(s, hash_ops, key) _set_ensure_put(s, hash_ops, key HASHMAP_DEBUG_SRC_ARGS)
@@ -143,10 +122,8 @@ int set_put_strsplit(Set *s, const char *v, const char *separators, ExtractFlags
for (; ({ e = set_first(s); assert_se(!e || set_move_one(d, s, e) >= 0); e; }); )
DEFINE_TRIVIAL_CLEANUP_FUNC(Set*, set_free);
-DEFINE_TRIVIAL_CLEANUP_FUNC(Set*, set_free_free);
#define _cleanup_set_free_ _cleanup_(set_freep)
-#define _cleanup_set_free_free_ _cleanup_(set_free_freep)
int set_strjoin(Set *s, const char *separator, bool wrap_with_separator, char **ret);
diff --git a/src/libnm-systemd-shared/src/basic/signal-util.c b/src/libnm-systemd-shared/src/basic/signal-util.c
index 05b2b50d42..6722e4bcda 100644
--- a/src/libnm-systemd-shared/src/basic/signal-util.c
+++ b/src/libnm-systemd-shared/src/basic/signal-util.c
@@ -4,11 +4,11 @@
#include
#include
+#include
#include "errno-util.h"
#include "macro.h"
#include "missing_syscall.h"
-#include "missing_threads.h"
#include "parse-util.h"
#include "signal-util.h"
#include "stdio-util.h"
@@ -283,13 +283,13 @@ void propagate_signal(int sig, siginfo_t *siginfo) {
/* To be called from a signal handler. Will raise the same signal again, in our process + in our threads.
*
- * Note that we use raw_getpid() instead of getpid_cached(). We might have forked with raw_clone()
+ * Note that we use getpid() instead of getpid_cached(). We might have forked with raw_clone()
* earlier (see PID 1), and hence let's go to the raw syscall here. In particular as this is not
* performance sensitive code.
*
* Note that we use kill() rather than raise() as fallback, for similar reasons. */
- p = raw_getpid();
+ p = getpid();
if (rt_tgsigqueueinfo(p, gettid(), sig, siginfo) < 0)
assert_se(kill(p, sig) >= 0);
diff --git a/src/libnm-systemd-shared/src/basic/signal-util.h b/src/libnm-systemd-shared/src/basic/signal-util.h
index dc2b9de147..d64d503124 100644
--- a/src/libnm-systemd-shared/src/basic/signal-util.h
+++ b/src/libnm-systemd-shared/src/basic/signal-util.h
@@ -3,6 +3,7 @@
#include
+#include "assert-util.h"
#include "macro.h"
int reset_all_signal_handlers(void);
diff --git a/src/libnm-systemd-shared/src/basic/socket-util.c b/src/libnm-systemd-shared/src/basic/socket-util.c
index 9cb0d7f46c..7c83551cfa 100644
--- a/src/libnm-systemd-shared/src/basic/socket-util.c
+++ b/src/libnm-systemd-shared/src/basic/socket-util.c
@@ -2,11 +2,12 @@
#include "nm-sd-adapt-shared.h"
-/* Make sure the net/if.h header is included before any linux/ one */
-#include
#include
#include
#include
+#include
+#include
+#include
#include
#include
#include
@@ -16,9 +17,6 @@
#include
#include
#include
-#if 0 /* NM_IGNORED */
-#include
-#endif /* NM_IGNORED */
#include "alloc-util.h"
#include "errno-util.h"
@@ -32,6 +30,7 @@
#include "parse-util.h"
#include "path-util.h"
#include "process-util.h"
+#include "random-util.h"
#include "socket-util.h"
#include "string-table.h"
#include "string-util.h"
@@ -1361,6 +1360,53 @@ void* cmsg_find_and_copy_data(struct msghdr *mh, int level, int type, void *buf,
}
#if 0 /* NM_IGNORED */
+size_t sockaddr_ll_len(const struct sockaddr_ll *sa) {
+ /* Certain hardware address types (e.g Infiniband) do not fit into sll_addr
+ * (8 bytes) and run over the structure. This function returns the correct size that
+ * must be passed to kernel. */
+
+ assert(sa->sll_family == AF_PACKET);
+
+ size_t mac_len = sizeof(sa->sll_addr);
+
+ if (be16toh(sa->sll_hatype) == ARPHRD_ETHER)
+ mac_len = MAX(mac_len, (size_t) ETH_ALEN);
+ if (be16toh(sa->sll_hatype) == ARPHRD_INFINIBAND)
+ mac_len = MAX(mac_len, (size_t) INFINIBAND_ALEN);
+
+ return offsetof(struct sockaddr_ll, sll_addr) + mac_len;
+}
+
+size_t sockaddr_un_len(const struct sockaddr_un *sa) {
+ /* Covers only file system and abstract AF_UNIX socket addresses, but not unnamed socket addresses. */
+
+ assert(sa->sun_family == AF_UNIX);
+
+ return offsetof(struct sockaddr_un, sun_path) +
+ (sa->sun_path[0] == 0 ?
+ 1 + strnlen(sa->sun_path+1, sizeof(sa->sun_path)-1) :
+ strnlen(sa->sun_path, sizeof(sa->sun_path))+1);
+}
+
+size_t sockaddr_len(const union sockaddr_union *sa) {
+ switch (sa->sa.sa_family) {
+ case AF_INET:
+ return sizeof(struct sockaddr_in);
+ case AF_INET6:
+ return sizeof(struct sockaddr_in6);
+ case AF_UNIX:
+ return sockaddr_un_len(&sa->un);
+ case AF_PACKET:
+ return sockaddr_ll_len(&sa->ll);
+ case AF_NETLINK:
+ return sizeof(struct sockaddr_nl);
+ case AF_VSOCK:
+ return sizeof(struct sockaddr_vm);
+ default:
+ assert_not_reached();
+ }
+}
+
int socket_ioctl_fd(void) {
int fd;
@@ -1457,25 +1503,43 @@ int socket_bind_to_ifname(int fd, const char *ifname) {
}
int socket_bind_to_ifindex(int fd, int ifindex) {
- char ifname[IF_NAMESIZE];
- int r;
-
assert(fd >= 0);
if (ifindex <= 0)
/* Drop binding */
return RET_NERRNO(setsockopt(fd, SOL_SOCKET, SO_BINDTODEVICE, NULL, 0));
- r = setsockopt_int(fd, SOL_SOCKET, SO_BINDTOIFINDEX, ifindex);
- if (r != -ENOPROTOOPT)
- return r;
+ return setsockopt_int(fd, SOL_SOCKET, SO_BINDTOIFINDEX, ifindex);
+}
- /* Fall back to SO_BINDTODEVICE on kernels < 5.0 which didn't have SO_BINDTOIFINDEX */
- r = format_ifname(ifindex, ifname);
+int socket_autobind(int fd, char **ret_name) {
+ _cleanup_free_ char *name = NULL;
+ uint64_t random;
+ int r;
+
+ /* Generate a random abstract socket name and bind fd to it. This is modeled after the kernel
+ * "autobind" feature, but uses 64-bit random number internally. */
+
+ assert(fd >= 0);
+ assert(ret_name);
+
+ random = random_u64();
+
+ if (asprintf(&name, "@%" PRIu64, random) < 0)
+ return -ENOMEM;
+
+ union sockaddr_union sa;
+ assert_cc(DECIMAL_STR_MAX(uint64_t) < sizeof(sa.un.sun_path));
+
+ r = sockaddr_un_set_path(&sa.un, name);
if (r < 0)
return r;
- return socket_bind_to_ifname(fd, ifname);
+ if (bind(fd, &sa.sa, r) < 0)
+ return -errno;
+
+ *ret_name = TAKE_PTR(name);
+ return 0;
}
ssize_t recvmsg_safe(int sockfd, struct msghdr *msg, int flags) {
@@ -1815,8 +1879,6 @@ int netlink_socket_get_multicast_groups(int fd, size_t *ret_len, uint32_t **ret_
assert(fd >= 0);
- /* This returns ENOPROTOOPT if the kernel is older than 4.2. */
-
if (getsockopt(fd, SOL_NETLINK, NETLINK_LIST_MEMBERSHIPS, NULL, &len) < 0)
return -errno;
diff --git a/src/libnm-systemd-shared/src/basic/socket-util.h b/src/libnm-systemd-shared/src/basic/socket-util.h
index b6c387252e..e5eda8559b 100644
--- a/src/libnm-systemd-shared/src/basic/socket-util.h
+++ b/src/libnm-systemd-shared/src/basic/socket-util.h
@@ -6,12 +6,12 @@
#include
#include
#include
-#include /* linux/vms_sockets.h requires 'struct sockaddr' */
#include
#include
#include
#include
#include
+#include
#include
#include
@@ -240,62 +240,11 @@ void* cmsg_find_and_copy_data(struct msghdr *mh, int level, int type, void *buf,
(size) == CMSG_ALIGN(size) ? 1 : -1]; \
}
-/*
- * Certain hardware address types (e.g Infiniband) do not fit into sll_addr
- * (8 bytes) and run over the structure. This macro returns the correct size that
- * must be passed to kernel.
- */
-#define SOCKADDR_LL_LEN(sa) \
- ({ \
- const struct sockaddr_ll *_sa = &(sa); \
- size_t _mac_len = sizeof(_sa->sll_addr); \
- assert(_sa->sll_family == AF_PACKET); \
- if (be16toh(_sa->sll_hatype) == ARPHRD_ETHER) \
- _mac_len = MAX(_mac_len, (size_t) ETH_ALEN); \
- if (be16toh(_sa->sll_hatype) == ARPHRD_INFINIBAND) \
- _mac_len = MAX(_mac_len, (size_t) INFINIBAND_ALEN); \
- offsetof(struct sockaddr_ll, sll_addr) + _mac_len; \
- })
+size_t sockaddr_ll_len(const struct sockaddr_ll *sa);
-/* Covers only file system and abstract AF_UNIX socket addresses, but not unnamed socket addresses. */
-#define SOCKADDR_UN_LEN(sa) \
- ({ \
- const struct sockaddr_un *_sa = &(sa); \
- assert(_sa->sun_family == AF_UNIX); \
- offsetof(struct sockaddr_un, sun_path) + \
- (_sa->sun_path[0] == 0 ? \
- 1 + strnlen(_sa->sun_path+1, sizeof(_sa->sun_path)-1) : \
- strnlen(_sa->sun_path, sizeof(_sa->sun_path))+1); \
- })
+size_t sockaddr_un_len(const struct sockaddr_un *sa);
-#define SOCKADDR_LEN(saddr) \
- ({ \
- const union sockaddr_union *__sa = &(saddr); \
- size_t _len; \
- switch (__sa->sa.sa_family) { \
- case AF_INET: \
- _len = sizeof(struct sockaddr_in); \
- break; \
- case AF_INET6: \
- _len = sizeof(struct sockaddr_in6); \
- break; \
- case AF_UNIX: \
- _len = SOCKADDR_UN_LEN(__sa->un); \
- break; \
- case AF_PACKET: \
- _len = SOCKADDR_LL_LEN(__sa->ll); \
- break; \
- case AF_NETLINK: \
- _len = sizeof(struct sockaddr_nl); \
- break; \
- case AF_VSOCK: \
- _len = sizeof(struct sockaddr_vm); \
- break; \
- default: \
- assert_not_reached(); \
- } \
- _len; \
- })
+size_t sockaddr_len(const union sockaddr_union *sa);
int socket_ioctl_fd(void);
@@ -324,6 +273,8 @@ 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);
+int socket_autobind(int fd, char **ret_name);
+
/* 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;
diff --git a/src/libnm-systemd-shared/src/basic/stat-util.c b/src/libnm-systemd-shared/src/basic/stat-util.c
index ef813e190b..1b352290e0 100644
--- a/src/libnm-systemd-shared/src/basic/stat-util.c
+++ b/src/libnm-systemd-shared/src/basic/stat-util.c
@@ -18,10 +18,10 @@
#include "filesystems.h"
#include "fs-util.h"
#include "hash-funcs.h"
+#include "log.h"
#include "macro.h"
#include "missing_fs.h"
#include "missing_magic.h"
-#include "missing_syscall.h"
#include "mountpoint-util.h"
#include "nulstr-util.h"
#include "parse-util.h"
@@ -175,7 +175,7 @@ int dir_is_empty_at(int dir_fd, const char *path, bool ignore_hidden_or_backup)
struct dirent *de;
ssize_t n;
- n = getdents64(fd, buf, m);
+ n = posix_getdents(fd, buf, m, /* flags = */ 0);
if (n < 0)
return -errno;
if (n == 0)
@@ -301,7 +301,7 @@ int inode_same_at(int fda, const char *filea, int fdb, const char *fileb, int fl
flags |= AT_EMPTY_PATH;
}
- int ntha_flags = (flags & AT_EMPTY_PATH) | (FLAGS_SET(flags, AT_SYMLINK_NOFOLLOW) ? 0 : AT_SYMLINK_FOLLOW);
+ int ntha_flags = at_flags_normalize_follow(flags) & (AT_EMPTY_PATH|AT_SYMLINK_FOLLOW);
_cleanup_free_ struct file_handle *ha = NULL, *hb = NULL;
int mntida = -1, mntidb = -1;
@@ -478,8 +478,8 @@ bool statx_inode_same(const struct statx *a, const struct statx *b) {
a->stx_ino == b->stx_ino;
}
-bool statx_mount_same(const struct new_statx *a, const struct new_statx *b) {
- if (!new_statx_is_set(a) || !new_statx_is_set(b))
+bool statx_mount_same(const struct statx *a, const struct statx *b) {
+ if (!statx_is_set(a) || !statx_is_set(b))
return false;
/* if we have the mount ID, that's all we need */
@@ -490,76 +490,6 @@ bool statx_mount_same(const struct new_statx *a, const struct new_statx *b) {
return a->stx_dev_major == b->stx_dev_major &&
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) {
- r = RET_NERRNO(statx(dfd, path, flags, mask, sx));
- if (r >= 0 || is_statx_fatal_error(r, flags))
- return r;
-
- avoid_statx = true;
- }
-
- /* Only do fallback if fstatat() supports the flag too, or if it's one of the sync flags, which are
- * OK to ignore */
- if ((flags & ~(AT_EMPTY_PATH|AT_NO_AUTOMOUNT|AT_SYMLINK_NOFOLLOW|
- AT_STATX_SYNC_AS_STAT|AT_STATX_FORCE_SYNC|AT_STATX_DONT_SYNC)) != 0)
- return -EOPNOTSUPP;
-
- if (fstatat(dfd, path, &st, flags & (AT_EMPTY_PATH|AT_NO_AUTOMOUNT|AT_SYMLINK_NOFOLLOW)) < 0)
- return -errno;
-
- *sx = (struct statx) {
- .stx_mask = STATX_TYPE|STATX_MODE|
- STATX_NLINK|STATX_UID|STATX_GID|
- STATX_ATIME|STATX_MTIME|STATX_CTIME|
- STATX_INO|STATX_SIZE|STATX_BLOCKS,
- .stx_blksize = st.st_blksize,
- .stx_nlink = st.st_nlink,
- .stx_uid = st.st_uid,
- .stx_gid = st.st_gid,
- .stx_mode = st.st_mode,
- .stx_ino = st.st_ino,
- .stx_size = st.st_size,
- .stx_blocks = st.st_blocks,
- .stx_rdev_major = major(st.st_rdev),
- .stx_rdev_minor = minor(st.st_rdev),
- .stx_dev_major = major(st.st_dev),
- .stx_dev_minor = minor(st.st_dev),
- .stx_atime.tv_sec = st.st_atim.tv_sec,
- .stx_atime.tv_nsec = st.st_atim.tv_nsec,
- .stx_mtime.tv_sec = st.st_mtim.tv_sec,
- .stx_mtime.tv_nsec = st.st_mtim.tv_nsec,
- .stx_ctime.tv_sec = st.st_ctim.tv_sec,
- .stx_ctime.tv_nsec = st.st_ctim.tv_nsec,
- };
-
- return 0;
-}
#endif /* NM_IGNORED */
int xstatfsat(int dir_fd, const char *path, struct statfs *ret) {
@@ -568,11 +498,14 @@ int xstatfsat(int dir_fd, const char *path, struct statfs *ret) {
assert(dir_fd >= 0 || dir_fd == AT_FDCWD);
assert(ret);
- fd = xopenat(dir_fd, path, O_PATH|O_CLOEXEC|O_NOCTTY);
- if (fd < 0)
- return fd;
+ if (!isempty(path)) {
+ fd = xopenat(dir_fd, path, O_PATH|O_CLOEXEC|O_NOCTTY);
+ if (fd < 0)
+ return fd;
+ dir_fd = fd;
+ }
- return RET_NERRNO(fstatfs(fd, ret));
+ return RET_NERRNO(fstatfs(dir_fd, ret));
}
#if 0 /* 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 17fb520f74..fcde7784b2 100644
--- a/src/libnm-systemd-shared/src/basic/stat-util.h
+++ b/src/libnm-systemd-shared/src/basic/stat-util.h
@@ -11,7 +11,6 @@
#include "fs-util.h"
#include "macro.h"
-#include "missing_stat.h"
#include "siphash24.h"
#include "time-util.h"
@@ -52,10 +51,11 @@ int inode_same_at(int fda, const char *filea, int fdb, const char *fileb, int fl
static inline int inode_same(const char *filea, const char *fileb, int flags) {
return inode_same_at(AT_FDCWD, filea, AT_FDCWD, fileb, flags);
}
+#if 0 /* NM_IGNORED */
static inline int fd_inode_same(int fda, int fdb) {
return inode_same_at(fda, NULL, fdb, NULL, AT_EMPTY_PATH);
}
-
+#endif /* NM_IGNORED */
/* The .f_type field of struct statfs is really weird defined on
* different archs. Let's give its type a name. */
typedef typeof(((struct statfs*)NULL)->f_type) statfs_f_type_t;
@@ -90,31 +90,10 @@ bool stat_inode_same(const struct stat *a, const struct stat *b);
bool stat_inode_unmodified(const struct stat *a, const struct stat *b);
bool statx_inode_same(const struct statx *a, const struct statx *b);
-bool statx_mount_same(const struct new_statx *a, const struct new_statx *b);
-
-int statx_fallback(int dfd, const char *path, int flags, unsigned mask, struct statx *sx);
+bool statx_mount_same(const struct statx *a, const struct statx *b);
int xstatfsat(int dir_fd, const char *path, struct statfs *ret);
-#if HAS_FEATURE_MEMORY_SANITIZER
-# warning "Explicitly initializing struct statx, to work around msan limitation. Please remove as soon as msan has been updated to not require this."
-# define STRUCT_STATX_DEFINE(var) \
- struct statx var = {}
-# define STRUCT_NEW_STATX_DEFINE(var) \
- union { \
- struct statx sx; \
- struct new_statx nsx; \
- } var = {}
-#else
-# define STRUCT_STATX_DEFINE(var) \
- struct statx var
-# define STRUCT_NEW_STATX_DEFINE(var) \
- union { \
- struct statx sx; \
- struct new_statx nsx; \
- } var
-#endif
-
#if 0 /* NM_IGNORED */
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 });
@@ -143,7 +122,4 @@ static inline bool stat_is_set(const struct stat *st) {
static inline bool statx_is_set(const struct statx *sx) {
return sx && sx->stx_mask != 0;
}
-static inline bool new_statx_is_set(const struct new_statx *sx) {
- return sx && sx->stx_mask != 0;
-}
#endif /* NM_IGNORED */
diff --git a/src/libnm-systemd-shared/src/basic/stdio-util.h b/src/libnm-systemd-shared/src/basic/stdio-util.h
index c35e665504..6914d9982e 100644
--- a/src/libnm-systemd-shared/src/basic/stdio-util.h
+++ b/src/libnm-systemd-shared/src/basic/stdio-util.h
@@ -8,6 +8,7 @@
#include
#include
+#include "assert-util.h"
#include "macro.h"
_printf_(3, 4)
diff --git a/src/libnm-systemd-shared/src/basic/string-util.c b/src/libnm-systemd-shared/src/basic/string-util.c
index 6a06594a67..5efd11eabc 100644
--- a/src/libnm-systemd-shared/src/basic/string-util.c
+++ b/src/libnm-systemd-shared/src/basic/string-util.c
@@ -16,6 +16,7 @@
#include "glyph-util.h"
#include "gunicode.h"
#include "locale-util.h"
+#include "log.h"
#include "macro.h"
#include "memory-util.h"
#include "memstream-util.h"
@@ -48,35 +49,42 @@ char* first_word(const char *s, const char *word) {
return (char*) nw;
}
-char* strnappend(const char *s, const char *suffix, size_t b) {
- size_t a;
- char *r;
+char* strprepend(char **x, const char *s) {
+ assert(x);
- if (!s && !suffix)
- return strdup("");
+ if (isempty(s) && *x)
+ return *x;
- if (!s)
- return strndup(suffix, b);
-
- if (!suffix)
- return strdup(s);
-
- assert(s);
- assert(suffix);
-
- a = strlen(s);
- if (b > SIZE_MAX - a)
+ char *p = strjoin(strempty(s), *x);
+ if (!p)
return NULL;
- r = new(char, a+b+1);
- if (!r)
- return NULL;
+ free_and_replace(*x, p);
+ return *x;
+}
- memcpy(r, s, a);
- memcpy(r+a, suffix, b);
- r[a+b] = 0;
+char* strextendn(char **x, const char *s, size_t l) {
+ assert(x);
+ assert(s || l == 0);
- return r;
+ 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;
+
+ *mempcpy_typesafe(m + q, s, l) = 0;
+
+ *x = m;
+ }
+
+ return *x;
}
#if 0 /* NM_IGNORED */
@@ -245,7 +253,7 @@ bool string_has_cc(const char *p, const char *ok) {
#if 0 /* NM_IGNORED */
static int write_ellipsis(char *buf, bool unicode) {
- const char *s = special_glyph_full(SPECIAL_GLYPH_ELLIPSIS, unicode);
+ const char *s = glyph_full(GLYPH_ELLIPSIS, unicode);
assert(strlen(s) == 3);
memcpy(buf, s, 3);
return 3;
@@ -333,10 +341,6 @@ static char *ascii_ellipsize_mem(const char *s, size_t old_length, size_t new_le
case 2:
if (!is_locale_utf8())
return strdup("..");
-
- break;
-
- default:
break;
}
@@ -983,33 +987,6 @@ 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;
@@ -1074,6 +1051,15 @@ int free_and_strdup(char **p, const char *s) {
return 1;
}
+int free_and_strdup_warn(char **p, const char *s) {
+ int r;
+
+ r = free_and_strdup(p, s);
+ if (r < 0)
+ return log_oom();
+ return r;
+}
+
int free_and_strndup(char **p, const char *s, size_t l) {
char *t;
@@ -1502,3 +1488,19 @@ char* strrstr(const char *haystack, const char *needle) {
}
return NULL;
}
+
+size_t str_common_prefix(const char *a, const char *b) {
+ assert(a);
+ assert(b);
+
+ /* Returns the length of the common prefix of the two specified strings, or SIZE_MAX in case the
+ * strings are fully identical. */
+
+ for (size_t n = 0;; n++) {
+ char c = a[n];
+ if (c != b[n])
+ return n;
+ if (c == 0)
+ return SIZE_MAX;
+ }
+}
diff --git a/src/libnm-systemd-shared/src/basic/string-util.h b/src/libnm-systemd-shared/src/basic/string-util.h
index 1bcb1c40e3..91e63c9b12 100644
--- a/src/libnm-systemd-shared/src/basic/string-util.h
+++ b/src/libnm-systemd-shared/src/basic/string-util.h
@@ -106,7 +106,8 @@ static inline const char* empty_or_dash_to_null(const char *p) {
char* first_word(const char *s, const char *word) _pure_;
-char* strnappend(const char *s, const char *suffix, size_t length);
+char* strprepend(char **x, const char *s);
+char* strextendn(char **x, const char *s, size_t l) _nonnull_if_nonzero_(2, 3);
#define strjoin(a, ...) strextend_with_separator_internal(NULL, NULL, a, __VA_ARGS__, NULL)
@@ -193,8 +194,6 @@ 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__)
@@ -216,15 +215,8 @@ char* strrep(const char *s, unsigned n);
int split_pair(const char *s, const char *sep, char **ret_first, char **ret_second);
int free_and_strdup(char **p, const char *s);
-static inline int free_and_strdup_warn(char **p, const char *s) {
- int r;
-
- r = free_and_strdup(p, s);
- if (r < 0)
- return log_oom();
- return r;
-}
-int free_and_strndup(char **p, const char *s, size_t l);
+int free_and_strdup_warn(char **p, const char *s);
+int free_and_strndup(char **p, const char *s, size_t l) _nonnull_if_nonzero_(2, 3);
int strdup_to_full(char **ret, const char *src);
static inline int strdup_to(char **ret, const char *src) {
@@ -309,3 +301,5 @@ bool version_is_valid_versionspec(const char *s);
ssize_t strlevenshtein(const char *x, const char *y);
char* strrstr(const char *haystack, const char *needle);
+
+size_t str_common_prefix(const char *a, const char *b);
diff --git a/src/libnm-systemd-shared/src/basic/strv.c b/src/libnm-systemd-shared/src/basic/strv.c
index 3846669630..f433afc7ac 100644
--- a/src/libnm-systemd-shared/src/basic/strv.c
+++ b/src/libnm-systemd-shared/src/basic/strv.c
@@ -14,6 +14,7 @@
#include "extract-word.h"
#include "fileio.h"
#include "gunicode.h"
+#include "log.h"
#include "memory-util.h"
#include "nulstr-util.h"
#include "sort-util.h"
@@ -637,7 +638,7 @@ int strv_insert(char ***l, size_t position, char *value) {
n = strv_length(*l);
position = MIN(position, n);
- /* check for overflow and increase*/
+ /* check for overflow and increase */
if (n > SIZE_MAX - 2)
return -ENOMEM;
m = n + 2;
@@ -868,11 +869,11 @@ int strv_compare(char * const *a, char * const *b) {
return 0;
}
-bool strv_equal_ignore_order(char **a, char **b) {
+bool strv_equal_ignore_order(char * const *a, char * const *b) {
/* Just like strv_equal(), but doesn't care about the order of elements or about redundant entries
* (i.e. it's even ok if the number of entries in the array differ, as long as the difference just
- * consists of repititions) */
+ * consists of repetitions). */
if (a == b)
return true;
@@ -892,6 +893,7 @@ void strv_print_full(char * const *l, const char *prefix) {
STRV_FOREACH(s, l)
printf("%s%s\n", strempty(prefix), *s);
}
+#endif /* NM_IGNORED */
int strv_extendf(char ***l, const char *format, ...) {
va_list ap;
@@ -907,7 +909,7 @@ int strv_extendf(char ***l, const char *format, ...) {
return strv_consume(l, x);
}
-#endif /* NM_IGNORED */
+
char* startswith_strv(const char *s, char * const *l) {
STRV_FOREACH(i, l) {
@@ -986,14 +988,16 @@ bool strv_fnmatch_full(
}
char** strv_skip(char **l, size_t n) {
-
while (n > 0) {
if (strv_isempty(l))
- return l;
+ return NULL;
l++, n--;
}
+ /* To simplify callers, always return NULL instead of a zero-item array. */
+ if (strv_isempty(l))
+ return NULL;
return l;
}
@@ -1074,7 +1078,22 @@ int fputstrv(FILE *f, char * const *l, const char *separator, bool *space) {
}
#if 0 /* NM_IGNORED */
-DEFINE_PRIVATE_HASH_OPS_FULL(string_strv_hash_ops, char, string_hash_func, string_compare_func, free, char*, strv_free);
+void string_strv_hashmap_remove(Hashmap *h, const char *key, const char *value) {
+ assert(key);
+
+ if (value) {
+ char **l = hashmap_get(h, key);
+ if (!l)
+ return;
+
+ strv_remove(l, value);
+ if (!strv_isempty(l))
+ return;
+ }
+
+ _unused_ _cleanup_free_ char *key_free = NULL;
+ strv_free(hashmap_remove2(h, key, (void**) &key_free));
+}
static int string_strv_hashmap_put_internal(Hashmap *h, const char *key, const char *value) {
char **l;
@@ -1126,7 +1145,7 @@ int _string_strv_hashmap_put(Hashmap **h, const char *key, const char *value HA
assert(key);
assert(value);
- r = _hashmap_ensure_allocated(h, &string_strv_hash_ops HASHMAP_DEBUG_PASS_ARGS);
+ r = _hashmap_ensure_allocated(h, &string_hash_ops_free_strv_free HASHMAP_DEBUG_PASS_ARGS);
if (r < 0)
return r;
@@ -1140,7 +1159,7 @@ int _string_strv_ordered_hashmap_put(OrderedHashmap **h, const char *key, const
assert(key);
assert(value);
- r = _ordered_hashmap_ensure_allocated(h, &string_strv_hash_ops HASHMAP_DEBUG_PASS_ARGS);
+ r = _ordered_hashmap_ensure_allocated(h, &string_hash_ops_free_strv_free HASHMAP_DEBUG_PASS_ARGS);
if (r < 0)
return r;
@@ -1234,4 +1253,25 @@ int strv_rebreak_lines(char **l, size_t width, char ***ret) {
*ret = TAKE_PTR(broken);
return 0;
}
+
+char** strv_filter_prefix(char * const *l, const char *prefix) {
+
+ /* Allocates a copy of 'l', but only copies over entries starting with 'prefix' */
+
+ if (isempty(prefix))
+ return strv_copy(l);
+
+ _cleanup_strv_free_ char **f = NULL;
+ size_t sz = 0;
+
+ STRV_FOREACH(i, l) {
+ if (!startswith(*i, prefix))
+ continue;
+
+ if (strv_extend_with_size(&f, &sz, *i) < 0)
+ return NULL;
+ }
+
+ return TAKE_PTR(f);
+}
#endif /* NM_IGNORED */
diff --git a/src/libnm-systemd-shared/src/basic/strv.h b/src/libnm-systemd-shared/src/basic/strv.h
index 86ba06f835..0ca90087b4 100644
--- a/src/libnm-systemd-shared/src/basic/strv.h
+++ b/src/libnm-systemd-shared/src/basic/strv.h
@@ -33,7 +33,7 @@ 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);
+void strv_free_many(char ***strvs, size_t n) _nonnull_if_nonzero_(1, 2);
char** strv_copy_n(char * const *l, size_t n);
static inline char** strv_copy(char * const *l) {
@@ -96,7 +96,7 @@ static inline bool strv_equal(char * const *a, char * const *b) {
return strv_compare(a, b) == 0;
}
-bool strv_equal_ignore_order(char **a, char **b);
+bool strv_equal_ignore_order(char * const *a, char * const *b);
char** strv_new_internal(const char *x, ...) _sentinel_;
char** strv_new_ap(const char *x, va_list ap);
@@ -258,9 +258,15 @@ int fputstrv(FILE *f, char * const *l, const char *separator, bool *space);
#define strv_free_and_replace(a, b) \
free_and_replace_full(a, b, strv_free)
+void string_strv_hashmap_remove(Hashmap *h, const char *key, const char *value);
+static inline void string_strv_ordered_hashmap_remove(OrderedHashmap *h, const char *key, const char *value) {
+ string_strv_hashmap_remove(PLAIN_HASHMAP(h), key, value);
+}
int _string_strv_hashmap_put(Hashmap **h, const char *key, const char *value HASHMAP_DEBUG_PARAMS);
int _string_strv_ordered_hashmap_put(OrderedHashmap **h, const char *key, const char *value HASHMAP_DEBUG_PARAMS);
#define string_strv_hashmap_put(h, k, v) _string_strv_hashmap_put(h, k, v HASHMAP_DEBUG_SRC_ARGS)
#define string_strv_ordered_hashmap_put(h, k, v) _string_strv_ordered_hashmap_put(h, k, v HASHMAP_DEBUG_SRC_ARGS)
int strv_rebreak_lines(char **l, size_t width, char ***ret);
+
+char** strv_filter_prefix(char * const *l, const char *prefix);
diff --git a/src/libnm-systemd-shared/src/basic/time-util.c b/src/libnm-systemd-shared/src/basic/time-util.c
index 594d531511..06e27cfb39 100644
--- a/src/libnm-systemd-shared/src/basic/time-util.c
+++ b/src/libnm-systemd-shared/src/basic/time-util.c
@@ -10,6 +10,7 @@
#include
#include
#include
+#include
#include
#include "alloc-util.h"
@@ -19,8 +20,6 @@
#include "io-util.h"
#include "log.h"
#include "macro.h"
-#include "missing_threads.h"
-#include "missing_timerfd.h"
#include "parse-util.h"
#include "path-util.h"
#include "process-util.h"
@@ -1643,7 +1642,7 @@ int mktime_or_timegm_usec(
assert(tm);
- if (tm->tm_year < 69) /* early check for negative (i.e. before 1970) time_t (Note that in some timezones the epoch is in the year 1969!)*/
+ if (tm->tm_year < 69) /* early check for negative (i.e. before 1970) time_t (Note that in some timezones the epoch is in the year 1969!) */
return -ERANGE;
if ((usec_t) tm->tm_year > CONST_MIN(USEC_INFINITY / USEC_PER_YEAR, (usec_t) TIME_T_MAX / (365U * 24U * 60U * 60U)) - 1900) /* early check for possible overrun of usec_t or time_t */
return -ERANGE;
diff --git a/src/libnm-systemd-shared/src/basic/time-util.h b/src/libnm-systemd-shared/src/basic/time-util.h
index 14d660ee7e..3f6e3c9f41 100644
--- a/src/libnm-systemd-shared/src/basic/time-util.h
+++ b/src/libnm-systemd-shared/src/basic/time-util.h
@@ -222,8 +222,8 @@ static inline int usleep_safe(usec_t usec) {
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;
+ /* `clock_nanosleep()` does not use `errno`, but returns positive error codes. */
+ return -clock_nanosleep(CLOCK_MONOTONIC, 0, TIMESPEC_STORE(usec), NULL);
}
/* The last second we can format is 31. Dec 9999, 1s before midnight, because otherwise we'd enter 5 digit
diff --git a/src/libnm-systemd-shared/src/basic/tmpfile-util.c b/src/libnm-systemd-shared/src/basic/tmpfile-util.c
index 3d1b8d0447..4e1fe711e0 100644
--- a/src/libnm-systemd-shared/src/basic/tmpfile-util.c
+++ b/src/libnm-systemd-shared/src/basic/tmpfile-util.c
@@ -9,6 +9,7 @@
#include "fileio.h"
#include "fs-util.h"
#include "hexdecoct.h"
+#include "log.h"
#include "macro.h"
#include "memfd-util.h"
#include "missing_fcntl.h"
diff --git a/src/libnm-systemd-shared/src/basic/user-util.c b/src/libnm-systemd-shared/src/basic/user-util.c
new file mode 100644
index 0000000000..9b80750a10
--- /dev/null
+++ b/src/libnm-systemd-shared/src/basic/user-util.c
@@ -0,0 +1,1243 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+
+#include "nm-sd-adapt-shared.h"
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+#include "sd-messages.h"
+
+#include "alloc-util.h"
+#include "chase.h"
+#include "errno-util.h"
+#include "fd-util.h"
+#include "fileio.h"
+#include "format-util.h"
+#include "lock-util.h"
+#include "log.h"
+#include "macro.h"
+#include "mkdir.h"
+#include "parse-util.h"
+#include "path-util.h"
+#include "random-util.h"
+#include "string-util.h"
+#include "strv.h"
+#include "terminal-util.h"
+#include "user-util.h"
+#include "utf8.h"
+
+bool uid_is_valid(uid_t uid) {
+
+ /* Also see POSIX IEEE Std 1003.1-2008, 2016 Edition, 3.436. */
+
+ /* Some libc APIs use UID_INVALID as special placeholder */
+ if (uid == (uid_t) UINT32_C(0xFFFFFFFF))
+ return false;
+
+ /* A long time ago UIDs where 16 bit, hence explicitly avoid the 16-bit -1 too */
+ if (uid == (uid_t) UINT32_C(0xFFFF))
+ return false;
+
+ return true;
+}
+
+int parse_uid(const char *s, uid_t *ret) {
+ uint32_t uid = 0;
+ int r;
+
+ assert(s);
+
+ assert_cc(sizeof(uid_t) == sizeof(uint32_t));
+
+ /* We are very strict when parsing UIDs, and prohibit +/- as prefix, leading zero as prefix, and
+ * whitespace. We do this, since this call is often used in a context where we parse things as UID
+ * first, and if that doesn't work we fall back to NSS. Thus we really want to make sure that UIDs
+ * are parsed as UIDs only if they really really look like UIDs. */
+ r = safe_atou32_full(s, 10
+ | SAFE_ATO_REFUSE_PLUS_MINUS
+ | SAFE_ATO_REFUSE_LEADING_ZERO
+ | SAFE_ATO_REFUSE_LEADING_WHITESPACE, &uid);
+ if (r < 0)
+ return r;
+
+ if (!uid_is_valid(uid))
+ return -ENXIO; /* we return ENXIO instead of EINVAL
+ * here, to make it easy to distinguish
+ * invalid numeric uids from invalid
+ * strings. */
+
+ if (ret)
+ *ret = uid;
+
+ return 0;
+}
+
+#if 0 /* NM_IGNORED */
+int parse_uid_range(const char *s, uid_t *ret_lower, uid_t *ret_upper) {
+ _cleanup_free_ char *word = NULL;
+ uid_t l, u;
+ int r;
+
+ assert(s);
+ assert(ret_lower);
+ assert(ret_upper);
+
+ r = extract_first_word(&s, &word, "-", EXTRACT_DONT_COALESCE_SEPARATORS);
+ if (r < 0)
+ return r;
+ if (r == 0)
+ return -EINVAL;
+
+ r = parse_uid(word, &l);
+ if (r < 0)
+ return r;
+
+ /* Check for the upper bound and extract it if needed */
+ if (!s)
+ /* Single number with no dash. */
+ u = l;
+ else if (!*s)
+ /* Trailing dash is an error. */
+ return -EINVAL;
+ else {
+ r = parse_uid(s, &u);
+ if (r < 0)
+ return r;
+
+ if (l > u)
+ return -EINVAL;
+ }
+
+ *ret_lower = l;
+ *ret_upper = u;
+ return 0;
+}
+
+char* getlogname_malloc(void) {
+ uid_t uid;
+ struct stat st;
+
+ if (isatty_safe(STDIN_FILENO) && fstat(STDIN_FILENO, &st) >= 0)
+ uid = st.st_uid;
+ else
+ uid = getuid();
+
+ return uid_to_name(uid);
+}
+
+char* getusername_malloc(void) {
+ const char *e;
+
+ e = secure_getenv("USER");
+ if (e)
+ return strdup(e);
+
+ return uid_to_name(getuid());
+}
+
+bool is_nologin_shell(const char *shell) {
+ return PATH_IN_SET(shell,
+ /* 'nologin' is the friendliest way to disable logins for a user account. It prints a nice
+ * message and exits. Different distributions place the binary at different places though,
+ * hence let's list them all. */
+ "/bin/nologin",
+ "/sbin/nologin",
+ "/usr/bin/nologin",
+ "/usr/sbin/nologin",
+ /* 'true' and 'false' work too for the same purpose, but are less friendly as they don't do
+ * any message printing. Different distributions place the binary at various places but at
+ * least not in the 'sbin' directory. */
+ "/bin/false",
+ "/usr/bin/false",
+ "/bin/true",
+ "/usr/bin/true");
+}
+
+const char* default_root_shell_at(int rfd) {
+ /* We want to use the preferred shell, i.e. DEFAULT_USER_SHELL, which usually
+ * will be /bin/bash. Fall back to /bin/sh if DEFAULT_USER_SHELL is not found,
+ * or any access errors. */
+
+ assert(rfd >= 0 || rfd == AT_FDCWD);
+
+ int r = chaseat(rfd, DEFAULT_USER_SHELL, CHASE_AT_RESOLVE_IN_ROOT, NULL, NULL);
+ if (r < 0 && r != -ENOENT)
+ log_debug_errno(r, "Failed to look up shell '%s': %m", DEFAULT_USER_SHELL);
+ if (r > 0)
+ return DEFAULT_USER_SHELL;
+
+ return "/bin/sh";
+}
+
+const char* default_root_shell(const char *root) {
+ _cleanup_close_ int rfd = -EBADF;
+
+ rfd = open(empty_to_root(root), O_CLOEXEC | O_DIRECTORY | O_PATH);
+ if (rfd < 0)
+ return "/bin/sh";
+
+ return default_root_shell_at(rfd);
+}
+
+static int synthesize_user_creds(
+ const char **username,
+ uid_t *ret_uid, gid_t *ret_gid,
+ const char **ret_home,
+ const char **ret_shell,
+ UserCredsFlags flags) {
+
+ assert(username);
+ assert(*username);
+
+ /* We enforce some special rules for uid=0 and uid=65534: in order to avoid NSS lookups for root we hardcode
+ * their user record data. */
+
+ if (STR_IN_SET(*username, "root", "0")) {
+ *username = "root";
+
+ if (ret_uid)
+ *ret_uid = 0;
+ if (ret_gid)
+ *ret_gid = 0;
+ if (ret_home)
+ *ret_home = "/root";
+ if (ret_shell)
+ *ret_shell = default_root_shell(NULL);
+
+ return 0;
+ }
+
+ if (STR_IN_SET(*username, NOBODY_USER_NAME, "65534") &&
+ synthesize_nobody()) {
+ *username = NOBODY_USER_NAME;
+
+ if (ret_uid)
+ *ret_uid = UID_NOBODY;
+ if (ret_gid)
+ *ret_gid = GID_NOBODY;
+ if (ret_home)
+ *ret_home = FLAGS_SET(flags, USER_CREDS_SUPPRESS_PLACEHOLDER) ? NULL : "/";
+ if (ret_shell)
+ *ret_shell = FLAGS_SET(flags, USER_CREDS_SUPPRESS_PLACEHOLDER) ? NULL : NOLOGIN;
+
+ return 0;
+ }
+
+ return -ENOMEDIUM;
+}
+
+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) {
+
+ bool patch_username = false;
+ uid_t u = UID_INVALID;
+ struct passwd *p;
+ int r;
+
+ assert(username);
+ assert(*username);
+ assert((ret_home || ret_shell) || !(flags & (USER_CREDS_SUPPRESS_PLACEHOLDER|USER_CREDS_CLEAN)));
+
+ if (!FLAGS_SET(flags, USER_CREDS_PREFER_NSS) ||
+ (!ret_home && !ret_shell)) {
+
+ /* So here's the deal: normally, we'll try to synthesize all records we can synthesize, and override
+ * the user database with that. However, if the user specifies USER_CREDS_PREFER_NSS then the
+ * user database will override the synthetic records instead — except if the user is only interested in
+ * the UID and/or GID (but not the home directory, or the shell), in which case we'll always override
+ * the user database (i.e. the USER_CREDS_PREFER_NSS flag has no effect in this case). Why?
+ * Simply because there are valid usecase where the user might change the home directory or the shell
+ * of the relevant users, but changing the UID/GID mappings for them is something we explicitly don't
+ * support. */
+
+ r = synthesize_user_creds(username, ret_uid, ret_gid, ret_home, ret_shell, flags);
+ if (r >= 0)
+ return 0;
+ if (r != -ENOMEDIUM) /* not a username we can synthesize */
+ return r;
+ }
+
+ if (parse_uid(*username, &u) >= 0) {
+ errno = 0;
+ p = getpwuid(u);
+
+ /* If there are multiple users with the same id, make sure to leave $USER to the configured value
+ * instead of the first occurrence in the database. However if the uid was configured by a numeric uid,
+ * then let's pick the real username from /etc/passwd. */
+ if (p)
+ patch_username = true;
+ else if (FLAGS_SET(flags, USER_CREDS_ALLOW_MISSING) && !ret_gid && !ret_home && !ret_shell) {
+
+ /* If the specified user is a numeric UID and it isn't in the user database, and the caller
+ * passed USER_CREDS_ALLOW_MISSING and was only interested in the UID, then just return that
+ * and don't complain. */
+
+ if (ret_uid)
+ *ret_uid = u;
+
+ return 0;
+ }
+ } else {
+ errno = 0;
+ p = getpwnam(*username);
+ }
+ if (!p) {
+ /* getpwnam() may fail with ENOENT if /etc/passwd is missing.
+ * For us that is equivalent to the name not being defined. */
+ r = IN_SET(errno, 0, ENOENT) ? -ESRCH : -errno;
+
+ /* If the user requested that we only synthesize as fallback, do so now */
+ if (FLAGS_SET(flags, USER_CREDS_PREFER_NSS))
+ if (synthesize_user_creds(username, ret_uid, ret_gid, ret_home, ret_shell, flags) >= 0)
+ return 0;
+
+ return r;
+ }
+
+ if (ret_uid && !uid_is_valid(p->pw_uid))
+ return -EBADMSG;
+
+ if (ret_gid && !gid_is_valid(p->pw_gid))
+ return -EBADMSG;
+
+ if (ret_uid)
+ *ret_uid = p->pw_uid;
+
+ if (ret_gid)
+ *ret_gid = p->pw_gid;
+
+ if (ret_home)
+ /* Note: we don't insist on normalized paths, since there are setups that have /./ in the path */
+ *ret_home = (FLAGS_SET(flags, USER_CREDS_SUPPRESS_PLACEHOLDER) && empty_or_root(p->pw_dir)) ||
+ (FLAGS_SET(flags, USER_CREDS_CLEAN) && (!path_is_valid(p->pw_dir) || !path_is_absolute(p->pw_dir)))
+ ? NULL : p->pw_dir;
+
+ if (ret_shell)
+ *ret_shell = (FLAGS_SET(flags, USER_CREDS_SUPPRESS_PLACEHOLDER) && shell_is_placeholder(p->pw_shell)) ||
+ (FLAGS_SET(flags, USER_CREDS_CLEAN) && (!path_is_valid(p->pw_shell) || !path_is_absolute(p->pw_shell)))
+ ? NULL : p->pw_shell;
+
+ if (patch_username)
+ *username = p->pw_name;
+
+ return 0;
+}
+
+static int synthesize_group_creds(
+ const char **groupname,
+ gid_t *ret_gid) {
+
+ assert(groupname);
+ assert(*groupname);
+
+ if (STR_IN_SET(*groupname, "root", "0")) {
+ *groupname = "root";
+
+ if (ret_gid)
+ *ret_gid = 0;
+
+ return 0;
+ }
+
+ if (STR_IN_SET(*groupname, NOBODY_GROUP_NAME, "65534") &&
+ synthesize_nobody()) {
+ *groupname = NOBODY_GROUP_NAME;
+
+ if (ret_gid)
+ *ret_gid = GID_NOBODY;
+
+ return 0;
+ }
+
+ return -ENOMEDIUM;
+}
+
+int get_group_creds(const char **groupname, gid_t *ret_gid, UserCredsFlags flags) {
+ bool patch_groupname = false;
+ struct group *g;
+ gid_t id;
+ int r;
+
+ assert(groupname);
+ assert(*groupname);
+
+ if (!FLAGS_SET(flags, USER_CREDS_PREFER_NSS)) {
+ r = synthesize_group_creds(groupname, ret_gid);
+ if (r >= 0)
+ return 0;
+ if (r != -ENOMEDIUM) /* not a groupname we can synthesize */
+ return r;
+ }
+
+ if (parse_gid(*groupname, &id) >= 0) {
+ errno = 0;
+ g = getgrgid(id);
+
+ if (g)
+ patch_groupname = true;
+ else if (FLAGS_SET(flags, USER_CREDS_ALLOW_MISSING)) {
+ if (ret_gid)
+ *ret_gid = id;
+
+ return 0;
+ }
+ } else {
+ errno = 0;
+ g = getgrnam(*groupname);
+ }
+
+ if (!g) {
+ /* getgrnam() may fail with ENOENT if /etc/group is missing.
+ * For us that is equivalent to the name not being defined. */
+ r = IN_SET(errno, 0, ENOENT) ? -ESRCH : -errno;
+
+ if (FLAGS_SET(flags, USER_CREDS_PREFER_NSS))
+ if (synthesize_group_creds(groupname, ret_gid) >= 0)
+ return 0;
+
+ return r;
+ }
+
+ if (ret_gid) {
+ if (!gid_is_valid(g->gr_gid))
+ return -EBADMSG;
+
+ *ret_gid = g->gr_gid;
+ }
+
+ if (patch_groupname)
+ *groupname = g->gr_name;
+
+ return 0;
+}
+
+char* uid_to_name(uid_t uid) {
+ char *ret;
+ int r;
+
+ /* Shortcut things to avoid NSS lookups */
+ if (uid == 0)
+ return strdup("root");
+ if (uid == UID_NOBODY && synthesize_nobody())
+ return strdup(NOBODY_USER_NAME);
+
+ if (uid_is_valid(uid)) {
+ _cleanup_free_ struct passwd *pw = NULL;
+
+ r = getpwuid_malloc(uid, &pw);
+ if (r >= 0)
+ return strdup(pw->pw_name);
+ }
+
+ if (asprintf(&ret, UID_FMT, uid) < 0)
+ return NULL;
+
+ return ret;
+}
+
+char* gid_to_name(gid_t gid) {
+ char *ret;
+ int r;
+
+ if (gid == 0)
+ return strdup("root");
+ if (gid == GID_NOBODY && synthesize_nobody())
+ return strdup(NOBODY_GROUP_NAME);
+
+ if (gid_is_valid(gid)) {
+ _cleanup_free_ struct group *gr = NULL;
+
+ r = getgrgid_malloc(gid, &gr);
+ if (r >= 0)
+ return strdup(gr->gr_name);
+ }
+
+ if (asprintf(&ret, GID_FMT, gid) < 0)
+ return NULL;
+
+ return ret;
+}
+
+static bool gid_list_has(const gid_t *list, size_t size, gid_t val) {
+ assert(list || size == 0);
+
+ FOREACH_ARRAY(i, list, size)
+ if (*i == val)
+ return true;
+
+ return false;
+}
+
+int in_gid(gid_t gid) {
+ _cleanup_free_ gid_t *gids = NULL;
+ int ngroups;
+
+ if (getgid() == gid)
+ return 1;
+
+ if (getegid() == gid)
+ return 1;
+
+ if (!gid_is_valid(gid))
+ return -EINVAL;
+
+ ngroups = getgroups_alloc(&gids);
+ if (ngroups < 0)
+ return ngroups;
+
+ return gid_list_has(gids, ngroups, gid);
+}
+
+int in_group(const char *name) {
+ int r;
+ gid_t gid;
+
+ r = get_group_creds(&name, &gid, 0);
+ if (r < 0)
+ return r;
+
+ return in_gid(gid);
+}
+
+int merge_gid_lists(const gid_t *list1, size_t size1, const gid_t *list2, size_t size2, gid_t **ret) {
+ size_t nresult = 0;
+ assert(ret);
+
+ if (size2 > INT_MAX - size1)
+ return -ENOBUFS;
+
+ gid_t *buf = new(gid_t, size1 + size2);
+ if (!buf)
+ return -ENOMEM;
+
+ /* Duplicates need to be skipped on merging, otherwise they'll be passed on and stored in the kernel. */
+ for (size_t i = 0; i < size1; i++)
+ if (!gid_list_has(buf, nresult, list1[i]))
+ buf[nresult++] = list1[i];
+ for (size_t i = 0; i < size2; i++)
+ if (!gid_list_has(buf, nresult, list2[i]))
+ buf[nresult++] = list2[i];
+ *ret = buf;
+ return (int)nresult;
+}
+
+int getgroups_alloc(gid_t **ret) {
+ int ngroups = 8;
+
+ assert(ret);
+
+ for (unsigned attempt = 0;;) {
+ _cleanup_free_ gid_t *p = NULL;
+
+ p = new(gid_t, ngroups);
+ if (!p)
+ return -ENOMEM;
+
+ ngroups = getgroups(ngroups, p);
+ if (ngroups > 0) {
+ *ret = TAKE_PTR(p);
+ return ngroups;
+ }
+ if (ngroups == 0)
+ break;
+ if (errno != EINVAL)
+ return -errno;
+
+ /* Give up eventually */
+ if (attempt++ > 10)
+ return -EINVAL;
+
+ /* Get actual size needed, and size the array explicitly. Note that this is potentially racy
+ * to use (in multi-threaded programs), hence let's call this in a loop. */
+ ngroups = getgroups(0, NULL);
+ if (ngroups < 0)
+ return -errno;
+ if (ngroups == 0)
+ break;
+ }
+
+ *ret = NULL;
+ return 0;
+}
+
+int get_home_dir(char **ret) {
+ _cleanup_free_ struct passwd *p = NULL;
+ const char *e;
+ uid_t u;
+ int r;
+
+ assert(ret);
+
+ /* Take the user specified one */
+ e = secure_getenv("HOME");
+ if (e && path_is_valid(e) && path_is_absolute(e))
+ goto found;
+
+ /* Hardcode home directory for root and nobody to avoid NSS */
+ u = getuid();
+ if (u == 0) {
+ e = "/root";
+ goto found;
+ }
+ if (u == UID_NOBODY && synthesize_nobody()) {
+ e = "/";
+ goto found;
+ }
+
+ /* Check the database... */
+ r = getpwuid_malloc(u, &p);
+ if (r < 0)
+ return r;
+
+ e = p->pw_dir;
+ if (!path_is_valid(e) || !path_is_absolute(e))
+ return -EINVAL;
+
+ found:
+ return path_simplify_alloc(e, ret);
+}
+
+int get_shell(char **ret) {
+ _cleanup_free_ struct passwd *p = NULL;
+ const char *e;
+ uid_t u;
+ int r;
+
+ assert(ret);
+
+ /* Take the user specified one */
+ e = secure_getenv("SHELL");
+ if (e && path_is_valid(e) && path_is_absolute(e))
+ goto found;
+
+ /* Hardcode shell for root and nobody to avoid NSS */
+ u = getuid();
+ if (u == 0) {
+ e = default_root_shell(NULL);
+ goto found;
+ }
+ if (u == UID_NOBODY && synthesize_nobody()) {
+ e = NOLOGIN;
+ goto found;
+ }
+
+ /* Check the database... */
+ r = getpwuid_malloc(u, &p);
+ if (r < 0)
+ return r;
+
+ e = p->pw_shell;
+ if (!path_is_valid(e) || !path_is_absolute(e))
+ return -EINVAL;
+
+ found:
+ return path_simplify_alloc(e, ret);
+}
+
+int fully_set_uid_gid(uid_t uid, gid_t gid, const gid_t supplementary_gids[], size_t n_supplementary_gids) {
+ int r;
+
+ assert(supplementary_gids || n_supplementary_gids == 0);
+
+ /* Sets all UIDs and all GIDs to the specified ones. Drops all auxiliary GIDs */
+
+ r = maybe_setgroups(n_supplementary_gids, supplementary_gids);
+ if (r < 0)
+ return r;
+
+ if (gid_is_valid(gid))
+ if (setresgid(gid, gid, gid) < 0)
+ return -errno;
+
+ if (uid_is_valid(uid))
+ if (setresuid(uid, uid, uid) < 0)
+ return -errno;
+
+ return 0;
+}
+
+int take_etc_passwd_lock(const char *root) {
+ int r;
+
+ /* This is roughly the same as lckpwdf(), but not as awful. We don't want to use alarm() and signals,
+ * hence we implement our own trivial version of this.
+ *
+ * Note that shadow-utils also takes per-database locks in addition to lckpwdf(). However, we don't,
+ * given that they are redundant: they invoke lckpwdf() first and keep it during everything they do.
+ * The per-database locks are awfully racy, and thus we just won't do them. */
+
+ _cleanup_free_ char *path = path_join(root, ETC_PASSWD_LOCK_PATH);
+ if (!path)
+ return log_oom_debug();
+
+ (void) mkdir_parents(path, 0755);
+
+ _cleanup_close_ int fd = open(path, O_WRONLY|O_CREAT|O_CLOEXEC|O_NOCTTY|O_NOFOLLOW, 0600);
+ if (fd < 0)
+ return log_debug_errno(errno, "Cannot open %s: %m", path);
+
+ r = unposix_lock(fd, LOCK_EX);
+ if (r < 0)
+ return log_debug_errno(r, "Locking %s failed: %m", path);
+
+ return TAKE_FD(fd);
+}
+
+bool valid_user_group_name(const char *u, ValidUserFlags flags) {
+ const char *i;
+
+ /* Checks if the specified name is a valid user/group name. There are two flavours of this call:
+ * strict mode is the default which is POSIX plus some extra rules; and relaxed mode where we accept
+ * pretty much everything except the really worst offending names.
+ *
+ * Whenever we synthesize users ourselves we should use the strict mode. But when we process users
+ * created by other stuff, let's be more liberal. */
+
+ if (isempty(u)) /* An empty user name is never valid */
+ return false;
+
+ if (parse_uid(u, NULL) >= 0) /* Something that parses as numeric UID string is valid exactly when the
+ * flag for it is set */
+ return FLAGS_SET(flags, VALID_USER_ALLOW_NUMERIC);
+
+ if (FLAGS_SET(flags, VALID_USER_RELAX)) {
+
+ /* In relaxed mode we just check very superficially. Apparently SSSD and other stuff is
+ * extremely liberal (way too liberal if you ask me, even inserting "@" in user names, which
+ * is bound to cause problems for example when used with an MTA), hence only filter the most
+ * obvious cases, or where things would result in an invalid entry if such a user name would
+ * show up in /etc/passwd (or equivalent getent output).
+ *
+ * Note that we stepped far out of POSIX territory here. It's not our fault though, but
+ * SSSD's, Samba's and everybody else who ignored POSIX on this. (I mean, I am happy to step
+ * outside of POSIX' bounds any day, but I must say in this case I probably wouldn't
+ * have...) */
+
+ if (startswith(u, " ") || endswith(u, " ")) /* At least expect whitespace padding is removed
+ * at front and back (accept in the middle, since
+ * that's apparently a thing on Windows). Note
+ * that this also blocks usernames consisting of
+ * whitespace only. */
+ return false;
+
+ if (!utf8_is_valid(u)) /* We want to synthesize JSON from this, hence insist on UTF-8 */
+ return false;
+
+ if (string_has_cc(u, NULL)) /* CC characters are just dangerous (and \n in particular is the
+ * record separator in /etc/passwd), so we can't allow that. */
+ return false;
+
+ if (strpbrk(u, ":/")) /* Colons are the field separator in /etc/passwd, we can't allow
+ * that. Slashes are special to file systems paths and user names
+ * typically show up in the file system as home directories, hence
+ * don't allow slashes. */
+ return false;
+
+ if (in_charset(u, "0123456789")) /* Don't allow fully numeric strings, they might be confused
+ * with UIDs (note that this test is more broad than
+ * the parse_uid() test above, as it will cover more than
+ * the 32-bit range, and it will detect 65535 (which is in
+ * invalid UID, even though in the unsigned 32 bit range) */
+ return false;
+
+ if (u[0] == '-' && in_charset(u + 1, "0123456789")) /* Don't allow negative fully numeric
+ * strings either. After all some people
+ * write 65535 as -1 (even though that's
+ * not even true on 32-bit uid_t
+ * anyway) */
+ return false;
+
+ if (dot_or_dot_dot(u)) /* User names typically become home directory names, and these two are
+ * special in that context, don't allow that. */
+ return false;
+
+ /* Compare with strict result and warn if result doesn't match */
+ if (FLAGS_SET(flags, VALID_USER_WARN) && !valid_user_group_name(u, 0))
+ log_struct(LOG_NOTICE,
+ LOG_MESSAGE("Accepting user/group name '%s', which does not match strict user/group name rules.", u),
+ LOG_ITEM("USER_GROUP_NAME=%s", u),
+ LOG_MESSAGE_ID(SD_MESSAGE_UNSAFE_USER_NAME_STR));
+
+ /* Note that we make no restrictions on the length in relaxed mode! */
+ } else {
+ long sz;
+ size_t l;
+
+ /* Also see POSIX IEEE Std 1003.1-2008, 2016 Edition, 3.437. We are a bit stricter here
+ * however. Specifically we deviate from POSIX rules:
+ *
+ * - We don't allow empty user names (see above)
+ * - We require that names fit into the appropriate utmp field
+ * - We don't allow any dots (this conflicts with chown syntax which permits dots as user/group name separator)
+ * - We don't allow dashes or digit as the first character
+ *
+ * Note that other systems are even more restrictive, and don't permit underscores or uppercase characters.
+ */
+
+ if (!ascii_isalpha(u[0]) &&
+ u[0] != '_')
+ return false;
+
+ for (i = u+1; *i; i++)
+ if (!ascii_isalpha(*i) &&
+ !ascii_isdigit(*i) &&
+ !IN_SET(*i, '_', '-'))
+ return false;
+
+ l = i - u;
+
+ sz = sysconf(_SC_LOGIN_NAME_MAX);
+ assert_se(sz > 0);
+
+ if (l > (size_t) sz) /* glibc: 256 */
+ return false;
+ if (l > NAME_MAX) /* must fit in a filename: 255 */
+ return false;
+ if (l > sizeof_field(struct utmpx, ut_user) - 1) /* must fit in utmp: 31 */
+ return false;
+ }
+
+ return true;
+}
+
+bool valid_gecos(const char *d) {
+
+ if (!d)
+ return false;
+
+ if (!utf8_is_valid(d))
+ return false;
+
+ if (string_has_cc(d, NULL))
+ return false;
+
+ /* Colons are used as field separators, and hence not OK */
+ if (strchr(d, ':'))
+ return false;
+
+ return true;
+}
+
+char* mangle_gecos(const char *d) {
+ char *mangled;
+
+ /* Makes sure the provided string becomes valid as a GEGOS field, by dropping bad chars. glibc's
+ * putwent() only changes \n and : to spaces. We do more: replace all CC too, and remove invalid
+ * UTF-8 */
+
+ mangled = strdup(d);
+ if (!mangled)
+ return NULL;
+
+ for (char *i = mangled; *i; i++) {
+ int len;
+
+ if ((uint8_t) *i < (uint8_t) ' ' || *i == ':') {
+ *i = ' ';
+ continue;
+ }
+
+ len = utf8_encoded_valid_unichar(i, SIZE_MAX);
+ if (len < 0) {
+ *i = ' ';
+ continue;
+ }
+
+ i += len - 1;
+ }
+
+ return mangled;
+}
+
+bool valid_home(const char *p) {
+ /* Note that this function is also called by valid_shell(), any
+ * changes must account for that. */
+
+ if (isempty(p))
+ return false;
+
+ if (!utf8_is_valid(p))
+ return false;
+
+ if (string_has_cc(p, NULL))
+ return false;
+
+ if (!path_is_absolute(p))
+ return false;
+
+ if (!path_is_normalized(p))
+ return false;
+
+ /* Colons are used as field separators, and hence not OK */
+ if (strchr(p, ':'))
+ return false;
+
+ return true;
+}
+
+bool valid_shell(const char *p) {
+ /* We have the same requirements, so just piggy-back on the home check.
+ *
+ * Let's ignore /etc/shells because this is only applicable to real and not system users. It is also
+ * incompatible with the idea of empty /etc/. */
+ if (!valid_home(p))
+ return false;
+
+ return !endswith(p, "/"); /* one additional restriction: shells may not be dirs */
+}
+
+int maybe_setgroups(size_t size, const gid_t *list) {
+ int r;
+
+ /* Check if setgroups is allowed before we try to drop all the auxiliary groups */
+ if (size == 0) { /* Dropping all aux groups? */
+ _cleanup_free_ char *setgroups_content = NULL;
+ bool can_setgroups;
+
+ r = read_one_line_file("/proc/self/setgroups", &setgroups_content);
+ if (r == -ENOENT)
+ /* Old kernels don't have /proc/self/setgroups, so assume we can use setgroups */
+ can_setgroups = true;
+ else if (r < 0)
+ return r;
+ else
+ can_setgroups = streq(setgroups_content, "allow");
+
+ if (!can_setgroups) {
+ log_debug("Skipping setgroups(), /proc/self/setgroups is set to 'deny'");
+ return 0;
+ }
+ }
+
+ return RET_NERRNO(setgroups(size, list));
+}
+
+bool synthesize_nobody(void) {
+ /* Returns true when we shall synthesize the "nobody" user (which we do by default). This can be turned off by
+ * touching /etc/systemd/dont-synthesize-nobody in order to provide upgrade compatibility with legacy systems
+ * that used the "nobody" user name and group name for other UIDs/GIDs than 65534.
+ *
+ * Note that we do not employ any kind of synchronization on the following caching variable. If the variable is
+ * accessed in multi-threaded programs in the worst case it might happen that we initialize twice, but that
+ * shouldn't matter as each initialization should come to the same result. */
+ static int cache = -1;
+
+ if (cache < 0)
+ cache = access("/etc/systemd/dont-synthesize-nobody", F_OK) < 0;
+
+ return cache;
+}
+
+int putpwent_sane(const struct passwd *pw, FILE *stream) {
+ assert(pw);
+ assert(stream);
+
+ errno = 0;
+ if (putpwent(pw, stream) != 0)
+ return errno_or_else(EIO);
+
+ return 0;
+}
+
+int putspent_sane(const struct spwd *sp, FILE *stream) {
+ assert(sp);
+ assert(stream);
+
+ errno = 0;
+ if (putspent(sp, stream) != 0)
+ return errno_or_else(EIO);
+
+ return 0;
+}
+
+int putgrent_sane(const struct group *gr, FILE *stream) {
+ assert(gr);
+ assert(stream);
+
+ errno = 0;
+ if (putgrent(gr, stream) != 0)
+ return errno_or_else(EIO);
+
+ return 0;
+}
+
+#if ENABLE_GSHADOW
+int putsgent_sane(const struct sgrp *sg, FILE *stream) {
+ assert(sg);
+ assert(stream);
+
+ errno = 0;
+ if (putsgent(sg, stream) != 0)
+ return errno_or_else(EIO);
+
+ return 0;
+}
+#endif
+
+int fgetpwent_sane(FILE *stream, struct passwd **pw) {
+ assert(stream);
+ assert(pw);
+
+ errno = 0;
+ struct passwd *p = fgetpwent(stream);
+ if (!p && !IN_SET(errno, 0, ENOENT))
+ return -errno;
+
+ *pw = p;
+ return !!p;
+}
+
+int fgetspent_sane(FILE *stream, struct spwd **sp) {
+ assert(stream);
+ assert(sp);
+
+ errno = 0;
+ struct spwd *s = fgetspent(stream);
+ if (!s && !IN_SET(errno, 0, ENOENT))
+ return -errno;
+
+ *sp = s;
+ return !!s;
+}
+
+int fgetgrent_sane(FILE *stream, struct group **gr) {
+ assert(stream);
+ assert(gr);
+
+ errno = 0;
+ struct group *g = fgetgrent(stream);
+ if (!g && !IN_SET(errno, 0, ENOENT))
+ return -errno;
+
+ *gr = g;
+ return !!g;
+}
+
+#if ENABLE_GSHADOW
+int fgetsgent_sane(FILE *stream, struct sgrp **sg) {
+ assert(stream);
+ assert(sg);
+
+ errno = 0;
+ struct sgrp *s = fgetsgent(stream);
+ if (!s && !IN_SET(errno, 0, ENOENT))
+ return -errno;
+
+ *sg = s;
+ return !!s;
+}
+#endif
+
+int is_this_me(const char *username) {
+ uid_t uid;
+ int r;
+
+ /* Checks if the specified username is our current one. Passed string might be a UID or a user name. */
+
+ r = get_user_creds(&username, &uid, NULL, NULL, NULL, USER_CREDS_ALLOW_MISSING);
+ if (r < 0)
+ return r;
+
+ return uid == getuid();
+}
+
+const char* get_home_root(void) {
+ const char *e;
+
+ /* For debug purposes allow overriding where we look for home dirs */
+ e = secure_getenv("SYSTEMD_HOME_ROOT");
+ if (e && path_is_absolute(e) && path_is_normalized(e))
+ return e;
+
+ return "/home";
+}
+
+static size_t getpw_buffer_size(void) {
+ long bufsize = sysconf(_SC_GETPW_R_SIZE_MAX);
+ return bufsize <= 0 ? 4096U : (size_t) bufsize;
+}
+
+static bool errno_is_user_doesnt_exist(int error) {
+ /* See getpwnam(3) and getgrnam(3): those codes and others can be returned if the user or group are
+ * not found. */
+ return IN_SET(abs(error), ENOENT, ESRCH, EBADF, EPERM);
+}
+
+int getpwnam_malloc(const char *name, struct passwd **ret) {
+ size_t bufsize = getpw_buffer_size();
+ int r;
+
+ /* A wrapper around getpwnam_r() that allocates the necessary buffer on the heap. The caller must
+ * free() the returned structures! */
+
+ if (isempty(name))
+ return -EINVAL;
+
+ for (;;) {
+ _cleanup_free_ void *buf = NULL;
+
+ buf = malloc(ALIGN(sizeof(struct passwd)) + bufsize);
+ if (!buf)
+ return -ENOMEM;
+
+ struct passwd *pw = NULL;
+ r = getpwnam_r(name, buf, (char*) buf + ALIGN(sizeof(struct passwd)), (size_t) bufsize, &pw);
+ if (r == 0) {
+ if (pw) {
+ if (ret)
+ *ret = TAKE_PTR(buf);
+ return 0;
+ }
+
+ return -ESRCH;
+ }
+
+ assert(r > 0);
+
+ /* getpwnam() may fail with ENOENT if /etc/passwd is missing. For us that is equivalent to
+ * the name not being defined. */
+ if (errno_is_user_doesnt_exist(r))
+ return -ESRCH;
+ if (r != ERANGE)
+ return -r;
+
+ if (bufsize > SIZE_MAX/2 - ALIGN(sizeof(struct passwd)))
+ return -ENOMEM;
+ bufsize *= 2;
+ }
+}
+
+int getpwuid_malloc(uid_t uid, struct passwd **ret) {
+ size_t bufsize = getpw_buffer_size();
+ int r;
+
+ if (!uid_is_valid(uid))
+ return -EINVAL;
+
+ for (;;) {
+ _cleanup_free_ void *buf = NULL;
+
+ buf = malloc(ALIGN(sizeof(struct passwd)) + bufsize);
+ if (!buf)
+ return -ENOMEM;
+
+ struct passwd *pw = NULL;
+ r = getpwuid_r(uid, buf, (char*) buf + ALIGN(sizeof(struct passwd)), (size_t) bufsize, &pw);
+ if (r == 0) {
+ if (pw) {
+ if (ret)
+ *ret = TAKE_PTR(buf);
+ return 0;
+ }
+
+ return -ESRCH;
+ }
+
+ assert(r > 0);
+
+ if (errno_is_user_doesnt_exist(r))
+ return -ESRCH;
+ if (r != ERANGE)
+ return -r;
+
+ if (bufsize > SIZE_MAX/2 - ALIGN(sizeof(struct passwd)))
+ return -ENOMEM;
+ bufsize *= 2;
+ }
+}
+
+static size_t getgr_buffer_size(void) {
+ long bufsize = sysconf(_SC_GETGR_R_SIZE_MAX);
+ return bufsize <= 0 ? 4096U : (size_t) bufsize;
+}
+
+int getgrnam_malloc(const char *name, struct group **ret) {
+ size_t bufsize = getgr_buffer_size();
+ int r;
+
+ if (isempty(name))
+ return -EINVAL;
+
+ for (;;) {
+ _cleanup_free_ void *buf = NULL;
+
+ buf = malloc(ALIGN(sizeof(struct group)) + bufsize);
+ if (!buf)
+ return -ENOMEM;
+
+ struct group *gr = NULL;
+ r = getgrnam_r(name, buf, (char*) buf + ALIGN(sizeof(struct group)), (size_t) bufsize, &gr);
+ if (r == 0) {
+ if (gr) {
+ if (ret)
+ *ret = TAKE_PTR(buf);
+ return 0;
+ }
+
+ return -ESRCH;
+ }
+
+ assert(r > 0);
+
+ if (errno_is_user_doesnt_exist(r))
+ return -ESRCH;
+ if (r != ERANGE)
+ return -r;
+
+ if (bufsize > SIZE_MAX/2 - ALIGN(sizeof(struct group)))
+ return -ENOMEM;
+ bufsize *= 2;
+ }
+}
+
+int getgrgid_malloc(gid_t gid, struct group **ret) {
+ size_t bufsize = getgr_buffer_size();
+ int r;
+
+ if (!gid_is_valid(gid))
+ return -EINVAL;
+
+ for (;;) {
+ _cleanup_free_ void *buf = NULL;
+
+ buf = malloc(ALIGN(sizeof(struct group)) + bufsize);
+ if (!buf)
+ return -ENOMEM;
+
+ struct group *gr = NULL;
+ r = getgrgid_r(gid, buf, (char*) buf + ALIGN(sizeof(struct group)), (size_t) bufsize, &gr);
+ if (r == 0) {
+ if (gr) {
+ if (ret)
+ *ret = TAKE_PTR(buf);
+ return 0;
+ }
+
+ return -ESRCH;
+ }
+
+ assert(r > 0);
+
+ if (errno_is_user_doesnt_exist(r))
+ return -ESRCH;
+ if (r != ERANGE)
+ return -r;
+
+ if (bufsize > SIZE_MAX/2 - ALIGN(sizeof(struct group)))
+ return -ENOMEM;
+ bufsize *= 2;
+ }
+}
+#endif /* NM_IGNORED */
\ No newline at end of file
diff --git a/src/libnm-systemd-shared/src/basic/utf8.c b/src/libnm-systemd-shared/src/basic/utf8.c
index 33fdd96e9c..74d60f5668 100644
--- a/src/libnm-systemd-shared/src/basic/utf8.c
+++ b/src/libnm-systemd-shared/src/basic/utf8.c
@@ -616,4 +616,29 @@ size_t utf8_console_width(const char *str) {
return n;
}
+
+size_t utf8_last_length(const char *s, size_t n) {
+ int r;
+
+ assert(s);
+
+ if (n == SIZE_MAX)
+ n = strlen(s);
+
+ /* Determines length in bytes of last UTF-8 codepoint in string. If the string is empty, returns
+ * zero. Treats invalid UTF-8 codepoints as 1 sized ones. */
+
+ for (size_t last = 0;;) {
+ if (n == 0)
+ return last;
+
+ r = utf8_encoded_valid_unichar(s, n);
+ if (r <= 0)
+ r = 1; /* treat invalid UTF-8 as byte-wide */
+
+ s += r;
+ n -= r;
+ last = r;
+ }
+}
#endif /* NM_IGNORED */
diff --git a/src/libnm-systemd-shared/src/basic/utf8.h b/src/libnm-systemd-shared/src/basic/utf8.h
index 221bc46a2d..3b1a468c4f 100644
--- a/src/libnm-systemd-shared/src/basic/utf8.h
+++ b/src/libnm-systemd-shared/src/basic/utf8.h
@@ -7,7 +7,6 @@
#include
#include "macro.h"
-#include "missing_type.h"
#define UTF8_REPLACEMENT_CHARACTER "\xef\xbf\xbd"
#define UTF8_BYTE_ORDER_MARK "\xef\xbb\xbf"
@@ -62,3 +61,5 @@ static inline char32_t utf16_surrogate_pair_to_unichar(char16_t lead, char16_t t
size_t utf8_n_codepoints(const char *str);
int utf8_char_console_width(const char *str);
size_t utf8_console_width(const char *str);
+
+size_t utf8_last_length(const char *s, size_t n);
diff --git a/src/libnm-systemd-shared/src/fundamental/assert-fundamental.h b/src/libnm-systemd-shared/src/fundamental/assert-fundamental.h
new file mode 100644
index 0000000000..e207958b4a
--- /dev/null
+++ b/src/libnm-systemd-shared/src/fundamental/assert-fundamental.h
@@ -0,0 +1,141 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+#pragma once
+
+#if !SD_BOOT
+# include
+#endif
+
+#include "macro-fundamental.h"
+#include "log.h"
+
+#if SD_BOOT
+ _noreturn_ void efi_assert(const char *expr, const char *file, unsigned line, const char *function);
+
+ #ifdef NDEBUG
+ #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__); })
+ #define assert_not_reached() efi_assert("Code should not be reached", __FILE__, __LINE__, __func__)
+ #endif
+ #define assert_se(expr) ({ _likely_(expr) ? VOID_0 : efi_assert(#expr, __FILE__, __LINE__, __func__); })
+#else
+
+#if 0 /* NM_IGNORED */
+_noreturn_ void log_assert_failed(const char *text, const char *file, int line, const char *func);
+#else /* NM_IGNORED */
+#define log_assert_failed(text, file, line, func) \
+ G_STMT_START \
+ { \
+ log_internal(LOG_CRIT, \
+ 0, \
+ file, \
+ line, \
+ func, \
+ "Assertion '%s' failed at %s:%u, function %s(). Aborting.", \
+ text, \
+ file, \
+ line, \
+ func); \
+ g_assert_not_reached(); \
+ } \
+ G_STMT_END
+#endif /* NM_IGNORED */
+
+#if 0 /* NM_IGNORED */
+_noreturn_ void log_assert_failed_unreachable(const char *file, int line, const char *func);
+#else /* NM_IGNORED */
+#define log_assert_failed_unreachable(file, line, func) \
+ G_STMT_START \
+ { \
+ log_internal(LOG_CRIT, \
+ 0, \
+ file, \
+ line, \
+ func, \
+ "Code should not be reached at %s:%u, function %s(). Aborting.", \
+ file, \
+ line, \
+ func); \
+ g_assert_not_reached(); \
+ } \
+ G_STMT_END
+#endif /* NM_IGNORED */
+
+#ifdef __COVERITY__
+
+/* Use special definitions of assertion macros in order to prevent
+ * false positives of ASSERT_SIDE_EFFECT on Coverity static analyzer
+ * for uses of assert_se() and assert_return().
+ *
+ * These definitions make expression go through a (trivial) function
+ * call to ensure they are not discarded. Also use ! or !! to ensure
+ * the boolean expressions are seen as such.
+ *
+ * This technique has been described and recommended in:
+ * https://community.synopsys.com/s/question/0D534000046Yuzb/suppressing-assertsideeffect-for-functions-that-allow-for-sideeffects
+ */
+
+extern void __coverity_panic__(void);
+
+static inline void __coverity_check__(int condition) {
+ if (!condition)
+ __coverity_panic__();
+}
+
+static inline int __coverity_check_and_return__(int condition) {
+ return condition;
+}
+
+#define assert_message_se(expr, message) __coverity_check__(!!(expr))
+
+#define assert_log(expr, message) __coverity_check_and_return__(!!(expr))
+
+#else /* ! __COVERITY__ */
+
+#define assert_message_se(expr, message) \
+ do { \
+ if (_unlikely_(!(expr))) \
+ log_assert_failed(message, PROJECT_FILE, __LINE__, __func__); \
+ } while (false)
+
+#endif /* __COVERITY__ */
+
+#define assert_se(expr) assert_message_se(expr, #expr)
+
+/* We override the glibc assert() here. */
+#undef assert
+#ifdef NDEBUG
+#define assert(expr) ({ if (!(expr)) __builtin_unreachable(); })
+#else
+#define assert(expr) assert_message_se(expr, #expr)
+#endif
+
+#define assert_not_reached() \
+ log_assert_failed_unreachable(PROJECT_FILE, __LINE__, __func__)
+
+#endif
+
+/* This passes the argument through after (if asserts are enabled) checking that it is not null. */
+#define ASSERT_PTR(expr) _ASSERT_PTR(expr, UNIQ_T(_expr_, UNIQ), assert)
+#define ASSERT_SE_PTR(expr) _ASSERT_PTR(expr, UNIQ_T(_expr_, UNIQ), assert_se)
+#define _ASSERT_PTR(expr, var, check) \
+ ({ \
+ typeof(expr) var = (expr); \
+ check(var); \
+ var; \
+ })
+
+#define ASSERT_NONNEG(expr) \
+ ({ \
+ typeof(expr) _expr_ = (expr), _zero = 0; \
+ assert(_expr_ >= _zero); \
+ _expr_; \
+ })
+
+#define ASSERT_SE_NONNEG(expr) \
+ ({ \
+ typeof(expr) _expr_ = (expr), _zero = 0; \
+ assert_se(_expr_ >= _zero); \
+ _expr_; \
+ })
diff --git a/src/libnm-systemd-shared/src/fundamental/iovec-util-fundamental.h b/src/libnm-systemd-shared/src/fundamental/iovec-util-fundamental.h
index 68d5bf4ee0..4c86197e6f 100644
--- a/src/libnm-systemd-shared/src/fundamental/iovec-util-fundamental.h
+++ b/src/libnm-systemd-shared/src/fundamental/iovec-util-fundamental.h
@@ -1,6 +1,14 @@
/* SPDX-License-Identifier: LGPL-2.1-or-later */
#pragma once
+#if !SD_BOOT
+#include
+#include
+#endif
+
+#include "assert-fundamental.h"
+#include "macro-fundamental.h"
+
#if SD_BOOT
/* struct iovec is a POSIX userspace construct. Let's introduce it also in EFI mode, it's just so useful */
struct iovec {
@@ -8,7 +16,9 @@ struct iovec {
size_t iov_len;
};
-static inline void free(void *p);
+DISABLE_WARNING_REDUNDANT_DECLS;
+void free(void *p);
+REENABLE_WARNING;
#endif
/* This accepts both const and non-const pointers */
diff --git a/src/libnm-systemd-shared/src/fundamental/macro-fundamental.h b/src/libnm-systemd-shared/src/fundamental/macro-fundamental.h
index 418ea55f1d..abdfb9b6bd 100644
--- a/src/libnm-systemd-shared/src/fundamental/macro-fundamental.h
+++ b/src/libnm-systemd-shared/src/fundamental/macro-fundamental.h
@@ -1,16 +1,19 @@
/* SPDX-License-Identifier: LGPL-2.1-or-later */
#pragma once
-#if !SD_BOOT
-# include
-#endif
-
#include
#include
#include
#include
#include
+/* This header unconditionally defines MAX() so include it here already so
+ * it won't override our own definition of MAX() that we define later in this
+ * file. */
+#if !SD_BOOT
+#include
+#endif
+
/* Temporarily disable some warnings */
#define DISABLE_WARNING_DEPRECATED_DECLARATIONS \
_Pragma("GCC diagnostic push"); \
@@ -53,6 +56,9 @@
_Pragma("GCC diagnostic ignored \"-Wstringop-truncation\"")
#if 0 /* NM_IGNORED */
+#define DISABLE_WARNING_REDUNDANT_DECLS \
+ _Pragma("GCC diagnostic push"); \
+ _Pragma("GCC diagnostic ignored \"-Wredundant-decls\"")
#if HAVE_WARNING_ZERO_LENGTH_BOUNDS
# define DISABLE_WARNING_ZERO_LENGTH_BOUNDS \
@@ -123,9 +129,19 @@
# define _fallthrough_ __attribute__((__fallthrough__))
#endif
+#if __GNUC__ >= 15
+# define _nonnull_if_nonzero_(p, n) __attribute__((nonnull_if_nonzero(p, n)))
+#else
+# define _nonnull_if_nonzero_(p, n)
+#endif
+
#define XSTRINGIFY(x) #x
#define STRINGIFY(x) XSTRINGIFY(x)
+/* C23 changed char8_t from char to unsigned char, hence we cannot pass u8 literals to e.g. fputs() without
+ * casting. Let's introduce our own way to declare UTF-8 literals, which casts u8 literals to const char*. */
+#define UTF8(s) ((const char*) (u8"" s))
+
#ifndef __COVERITY__
# define VOID_0 ((void)0)
#else
@@ -141,45 +157,7 @@
#define XCONCATENATE(x, y) x ## y
#define CONCATENATE(x, y) XCONCATENATE(x, y)
-#if SD_BOOT
- _noreturn_ void efi_assert(const char *expr, const char *file, unsigned line, const char *function);
-
- #ifdef NDEBUG
- #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__); })
- #define assert_not_reached() efi_assert("Code should not be reached", __FILE__, __LINE__, __func__)
- #endif
- #define static_assert _Static_assert
- #define assert_se(expr) ({ _likely_(expr) ? VOID_0 : efi_assert(#expr, __FILE__, __LINE__, __func__); })
-#endif
-
-/* This passes the argument through after (if asserts are enabled) checking that it is not null. */
-#define ASSERT_PTR(expr) _ASSERT_PTR(expr, UNIQ_T(_expr_, UNIQ), assert)
-#define ASSERT_SE_PTR(expr) _ASSERT_PTR(expr, UNIQ_T(_expr_, UNIQ), assert_se)
-#define _ASSERT_PTR(expr, var, check) \
- ({ \
- typeof(expr) var = (expr); \
- check(var); \
- var; \
- })
-
-#define ASSERT_NONNEG(expr) \
- ({ \
- typeof(expr) _expr_ = (expr), _zero = 0; \
- assert(_expr_ >= _zero); \
- _expr_; \
- })
-
-#define ASSERT_SE_NONNEG(expr) \
- ({ \
- typeof(expr) _expr_ = (expr), _zero = 0; \
- assert_se(_expr_ >= _zero); \
- _expr_; \
- })
-
-#define assert_cc(expr) static_assert(expr, #expr)
+#define assert_cc(expr) _Static_assert(expr, #expr)
#define UNIQ_T(x, uniq) CONCATENATE(__unique_prefix_, CONCATENATE(x, uniq))
#define UNIQ __COUNTER__
@@ -434,7 +412,7 @@
_found = true; \
break; \
default: \
- break; \
+ ; \
} \
_found; \
})
@@ -467,82 +445,6 @@
(typeof(memory)) NULL; \
})
-static inline size_t ALIGN_TO(size_t l, size_t ali) {
- assert(ISPOWEROF2(ali));
-
- if (l > SIZE_MAX - (ali - 1))
- return SIZE_MAX; /* indicate overflow */
-
- 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)
-#define ALIGN4(l) ALIGN_TO(l, 4)
-#define ALIGN8(l) ALIGN_TO(l, 8)
-#define ALIGN2_PTR(p) ((void*) ALIGN2((uintptr_t) p))
-#define ALIGN4_PTR(p) ((void*) ALIGN4((uintptr_t) p))
-#define ALIGN8_PTR(p) ((void*) ALIGN8((uintptr_t) p))
-#define ALIGN(l) ALIGN_TO(l, sizeof(void*))
-#define ALIGN_PTR(p) ((void*) ALIGN((uintptr_t) (p)))
-
-/* Checks if the specified pointer is aligned as appropriate for the specific type */
-#define IS_ALIGNED16(p) (((uintptr_t) p) % alignof(uint16_t) == 0)
-#define IS_ALIGNED32(p) (((uintptr_t) p) % alignof(uint32_t) == 0)
-#define IS_ALIGNED64(p) (((uintptr_t) p) % alignof(uint64_t) == 0)
-
-/* Same as ALIGN_TO but callable in constant contexts. */
-#define CONST_ALIGN_TO(l, ali) \
- __builtin_choose_expr( \
- __builtin_constant_p(l) && \
- __builtin_constant_p(ali) && \
- CONST_ISPOWEROF2(ali) && \
- (l <= SIZE_MAX - (ali - 1)), /* overflow? */ \
- ((l) + (ali) - 1) & ~((ali) - 1), \
- VOID_0)
-
-/* Similar to ((t *) (void *) (p)) to cast a pointer. The macro asserts that the pointer has a suitable
- * alignment for type "t". This exists for places where otherwise "-Wcast-align=strict" would issue a
- * warning or if you want to assert that the cast gives a pointer of suitable alignment. */
-#define CAST_ALIGN_PTR(t, p) \
- ({ \
- const void *_p = (p); \
- assert(((uintptr_t) _p) % alignof(t) == 0); \
- (t *) _p; \
- })
-
#define UPDATE_FLAG(orig, flag, b) \
((b) ? ((orig) | (flag)) : ((orig) & ~(flag)))
#define SET_FLAG(v, flag, b) \
@@ -550,41 +452,11 @@ static inline uint64_t ALIGN_OFFSET_U64(uint64_t l, uint64_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); \
- }
+typedef struct {
+ int _empty[0];
+} dummy_t;
-/* 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); \
- } \
- }
+assert_cc(sizeof(dummy_t) == 0);
/* Restriction/bug (see below) was fixed in GCC 15 and clang 19. */
#if __GNUC__ >= 15 || (defined(__clang__) && __clang_major__ >= 19)
@@ -633,3 +505,10 @@ static inline uint64_t ALIGN_OFFSET_U64(uint64_t l, uint64_t ali) {
#define PTR_TO_SIZE(p) ((size_t) ((uintptr_t) (p)))
#define SIZE_TO_PTR(u) ((void *) ((uintptr_t) (u)))
+
+#if 0 /* NM_IGNORED */
+assert_cc(STRLEN(__FILE__) > STRLEN(RELATIVE_SOURCE_PATH) + 1);
+#define PROJECT_FILE (&__FILE__[STRLEN(RELATIVE_SOURCE_PATH) + 1])
+#else /* NM_IGNORED */
+#define PROJECT_FILE __FILE__
+#endif /* NM_IGNORED */
\ No newline at end of file
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 6870f54f58..4b50714f5e 100644
--- a/src/libnm-systemd-shared/src/fundamental/memory-util-fundamental.h
+++ b/src/libnm-systemd-shared/src/fundamental/memory-util-fundamental.h
@@ -9,6 +9,7 @@
# include
#endif
+#include "assert-fundamental.h"
#include "macro-fundamental.h"
#define memzero(x, l) \
@@ -17,7 +18,7 @@
_l_ > 0 ? memset((x), 0, _l_) : (x); \
})
-#if !SD_BOOT && HAVE_EXPLICIT_BZERO
+#if !SD_BOOT
static inline void *explicit_bzero_safe(void *p, size_t l) {
if (p && l > 0)
explicit_bzero(p, l);
@@ -106,3 +107,115 @@ static inline void array_cleanup(const ArrayCleanup *c) {
_f; \
}), \
}
+
+/* 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); \
+ } \
+ }
+
+static inline size_t ALIGN_TO(size_t l, size_t ali) {
+ assert(ISPOWEROF2(ali));
+
+ if (l > SIZE_MAX - (ali - 1))
+ return SIZE_MAX; /* indicate overflow */
+
+ 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)
+#define ALIGN4(l) ALIGN_TO(l, 4)
+#define ALIGN8(l) ALIGN_TO(l, 8)
+#define ALIGN2_PTR(p) ((void*) ALIGN2((uintptr_t) p))
+#define ALIGN4_PTR(p) ((void*) ALIGN4((uintptr_t) p))
+#define ALIGN8_PTR(p) ((void*) ALIGN8((uintptr_t) p))
+#define ALIGN(l) ALIGN_TO(l, sizeof(void*))
+#define ALIGN_PTR(p) ((void*) ALIGN((uintptr_t) (p)))
+
+/* Checks if the specified pointer is aligned as appropriate for the specific type */
+#define IS_ALIGNED16(p) (((uintptr_t) p) % alignof(uint16_t) == 0)
+#define IS_ALIGNED32(p) (((uintptr_t) p) % alignof(uint32_t) == 0)
+#define IS_ALIGNED64(p) (((uintptr_t) p) % alignof(uint64_t) == 0)
+
+/* Same as ALIGN_TO but callable in constant contexts. */
+#define CONST_ALIGN_TO(l, ali) \
+ __builtin_choose_expr( \
+ __builtin_constant_p(l) && \
+ __builtin_constant_p(ali) && \
+ CONST_ISPOWEROF2(ali) && \
+ (l <= SIZE_MAX - (ali - 1)), /* overflow? */ \
+ ((l) + (ali) - 1) & ~((ali) - 1), \
+ VOID_0)
+
+/* Similar to ((t *) (void *) (p)) to cast a pointer. The macro asserts that the pointer has a suitable
+ * alignment for type "t". This exists for places where otherwise "-Wcast-align=strict" would issue a
+ * warning or if you want to assert that the cast gives a pointer of suitable alignment. */
+#define CAST_ALIGN_PTR(t, p) \
+ ({ \
+ const void *_p = (p); \
+ assert(((uintptr_t) _p) % alignof(t) == 0); \
+ (t *) _p; \
+ })
diff --git a/src/libnm-systemd-shared/src/fundamental/sha256-fundamental.c b/src/libnm-systemd-shared/src/fundamental/sha256-fundamental.c
index 6682bc87a3..8e84253564 100644
--- a/src/libnm-systemd-shared/src/fundamental/sha256-fundamental.c
+++ b/src/libnm-systemd-shared/src/fundamental/sha256-fundamental.c
@@ -23,6 +23,13 @@
License along with the GNU C Library; if not, see
. */
+#if SD_BOOT
+# include "efi-string.h"
+#else
+# include
+#endif
+
+#include "assert-fundamental.h"
#include "macro-fundamental.h"
#include "memory-util-fundamental.h"
#include "sha256-fundamental.h"
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 da810cb749..0a62dbb22f 100644
--- a/src/libnm-systemd-shared/src/fundamental/string-util-fundamental.c
+++ b/src/libnm-systemd-shared/src/fundamental/string-util-fundamental.c
@@ -85,7 +85,7 @@ static bool is_valid_version_char(sd_char a) {
int strverscmp_improved(const sd_char *a, const sd_char *b) {
/* This function is similar to strverscmp(3), but it treats '-' and '.' as separators.
*
- * The logic is based on rpm's rpmvercmp(), but unlike rpmvercmp(), it distiguishes e.g.
+ * The logic is based on rpm's rpmvercmp(), but unlike rpmvercmp(), it distinguishes e.g.
* '123a' and '123.a', with '123a' being newer.
*
* It allows direct comparison of strings which contain both a version and a release; e.g.
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 419f1cc3da..a9638b4d0e 100644
--- a/src/libnm-systemd-shared/src/fundamental/string-util-fundamental.h
+++ b/src/libnm-systemd-shared/src/fundamental/string-util-fundamental.h
@@ -8,6 +8,7 @@
# include
#endif
+#include "assert-fundamental.h"
#include "macro-fundamental.h"
#if SD_BOOT
@@ -16,6 +17,8 @@
# define strncmp strncmp16
# define strcasecmp strcasecmp16
# define strncasecmp strncasecmp16
+# define strspn strspn16
+# define strcspn strcspn16
# define STR_C(str) (L ## str)
typedef char16_t sd_char;
#else
diff --git a/src/libnm-systemd-shared/src/shared/dns-domain.c b/src/libnm-systemd-shared/src/shared/dns-domain.c
index 42efd04a3e..eaca07159b 100644
--- a/src/libnm-systemd-shared/src/shared/dns-domain.c
+++ b/src/libnm-systemd-shared/src/shared/dns-domain.c
@@ -15,6 +15,7 @@
#include "hostname-util.h"
#include "idn-util.h"
#include "in-addr-util.h"
+#include "log.h"
#include "macro.h"
#include "parse-util.h"
#include "string-util.h"
@@ -1395,7 +1396,7 @@ int dns_name_apply_idna(const char *name, char **ret) {
r = sym_idn2_lookup_u8((uint8_t*) name, (uint8_t**) &t,
IDN2_NFC_INPUT | IDN2_TRANSITIONAL);
- log_debug("idn2_lookup_u8: %s %s %s", name, special_glyph(SPECIAL_GLYPH_ARROW_RIGHT), t);
+ log_debug("idn2_lookup_u8: %s %s %s", name, glyph(GLYPH_ARROW_RIGHT), t);
if (r == IDN2_OK) {
if (!startswith(name, "xn--")) {
_cleanup_free_ char *s = NULL;
@@ -1410,8 +1411,8 @@ int dns_name_apply_idna(const char *name, char **ret) {
if (!streq_ptr(name, s)) {
log_debug("idn2 roundtrip failed: \"%s\" %s \"%s\" %s \"%s\", ignoring.",
- name, special_glyph(SPECIAL_GLYPH_ARROW_RIGHT), t,
- special_glyph(SPECIAL_GLYPH_ARROW_RIGHT), s);
+ name, glyph(GLYPH_ARROW_RIGHT), t,
+ glyph(GLYPH_ARROW_RIGHT), s);
*ret = NULL;
return 0;
}
diff --git a/src/libnmc-setting/nm-meta-setting-desc.c b/src/libnmc-setting/nm-meta-setting-desc.c
index aa290b9f10..f62830312f 100644
--- a/src/libnmc-setting/nm-meta-setting-desc.c
+++ b/src/libnmc-setting/nm-meta-setting-desc.c
@@ -4156,6 +4156,50 @@ _optionlist_set_fcn_vpn_secrets(NMSetting *setting,
return TRUE;
}
+static void
+_objlist_obj_to_str_fcn_wireguard_peers(NMMetaAccessorGetType get_type,
+ NMSetting *setting,
+ guint idx,
+ GString *str)
+{
+ NMWireGuardPeer *peer;
+ gs_free char *peer_str = NULL;
+
+ peer = nm_setting_wireguard_get_peer(NM_SETTING_WIREGUARD(setting), idx);
+ peer_str = _nm_utils_wireguard_peer_to_string(peer);
+ g_string_append(str, peer_str);
+}
+
+static gboolean
+_objlist_set_fcn_wireguard_peers(NMSetting *setting,
+ gboolean do_add,
+ const char *value,
+ GError **error)
+{
+ NMSettingWireGuard *s_wg = NM_SETTING_WIREGUARD(setting);
+ nm_auto_unref_wgpeer NMWireGuardPeer *peer = NULL;
+
+ peer = _nm_utils_wireguard_peer_from_string(value, error);
+ if (!peer)
+ return FALSE;
+
+ if (do_add) {
+ nm_setting_wireguard_append_peer(s_wg, peer);
+ } else {
+ NMWireGuardPeer *match;
+ guint idx;
+
+ match = nm_setting_wireguard_get_peer_by_public_key(s_wg,
+ nm_wireguard_peer_get_public_key(peer),
+ &idx);
+ if (match) {
+ nm_setting_wireguard_remove_peer(s_wg, idx);
+ }
+ }
+
+ return TRUE;
+}
+
static gboolean
_set_fcn_wired_s390_subchannels(ARGS_SET_FCN)
{
@@ -8426,6 +8470,21 @@ static const NMMetaPropertyInfo *const property_infos_WIREGUARD[] = {
PROPERTY_INFO_WITH_DESC (NM_SETTING_WIREGUARD_IP6_AUTO_DEFAULT_ROUTE,
.property_type = &_pt_gobject_ternary,
),
+
+ PROPERTY_INFO_WITH_DESC (NM_SETTING_WIREGUARD_PEERS,
+ .property_type = &_pt_objlist,
+ .property_typ_data = DEFINE_PROPERTY_TYP_DATA (
+ PROPERTY_TYP_DATA_SUBTYPE (objlist,
+ .get_num_fcn = OBJLIST_GET_NUM_FCN (NMSettingWireGuard, nm_setting_wireguard_get_peers_len),
+ .clear_all_fcn = (void (*) (NMSetting *))(void (*)(void)) nm_setting_wireguard_clear_peers,
+ .obj_to_str_fcn = _objlist_obj_to_str_fcn_wireguard_peers,
+ .set_fcn = _objlist_set_fcn_wireguard_peers,
+ .remove_by_idx_fcn_u = (void (*) (NMSetting *, guint idx))(void (*)(void)) nm_setting_wireguard_remove_peer,
+ .strsplit_plain = TRUE,
+ ),
+ ),
+ ),
+
NULL
};
diff --git a/src/libnmc-setting/settings-docs.h.in b/src/libnmc-setting/settings-docs.h.in
index 56c7c29cd5..c7b13d196c 100644
--- a/src/libnmc-setting/settings-docs.h.in
+++ b/src/libnmc-setting/settings-docs.h.in
@@ -444,6 +444,7 @@
#define DESCRIBE_DOC_NM_SETTING_WIREGUARD_LISTEN_PORT N_("The listen-port. If listen-port is not specified, the port will be chosen randomly when the interface comes up.")
#define DESCRIBE_DOC_NM_SETTING_WIREGUARD_MTU N_("If non-zero, only transmit packets of the specified size or smaller, breaking larger packets up into multiple fragments. If zero a default MTU is used. Note that contrary to wg-quick's MTU setting, this does not take into account the current routes at the time of activation.")
#define DESCRIBE_DOC_NM_SETTING_WIREGUARD_PEER_ROUTES N_("Whether to automatically add routes for the AllowedIPs ranges of the peers. If TRUE (the default), NetworkManager will automatically add routes in the routing tables according to ipv4.route-table and ipv6.route-table. Usually you want this automatism enabled. If FALSE, no such routes are added automatically. In this case, the user may want to configure static routes in ipv4.routes and ipv6.routes, respectively. Note that if the peer's AllowedIPs is \"0.0.0.0/0\" or \"::/0\" and the profile's ipv4.never-default or ipv6.never-default setting is enabled, the peer route for this peer won't be added automatically.")
+#define DESCRIBE_DOC_NM_SETTING_WIREGUARD_PEERS N_("A comma-separated list of WireGuard peers. Each peer has the following syntax: PUBLIC_KEY [ATTRIBUTE=VALUE [ATTRIBUTE=VALUE]...] The supported attributes are: endpoint, allowed-ips, persistent-keepalive, preshared-key, preshared-key-flags.")
#define DESCRIBE_DOC_NM_SETTING_WIREGUARD_PRIVATE_KEY N_("The 256 bit private-key in base64 encoding.")
#define DESCRIBE_DOC_NM_SETTING_WIREGUARD_PRIVATE_KEY_FLAGS N_("Flags indicating how to handle the \"private-key\" property.")
#define DESCRIBE_DOC_NM_SETTING_WIRELESS_AP_ISOLATION N_("Configures AP isolation, which prevents communication between wireless devices connected to this AP. This property can be set to a value different from \"default\" (-1) only when the interface is configured in AP mode. If set to \"true\" (1), devices are not able to communicate with each other. This increases security because it protects devices against attacks from other clients in the network. At the same time, it prevents devices to access resources on the same wireless networks as file shares, printers, etc. If set to \"false\" (0), devices can talk to each other. When set to \"default\" (-1), the global default is used; in case the global default is unspecified it is assumed to be \"false\" (0).")
diff --git a/src/nm-initrd-generator/nmi-cmdline-reader.c b/src/nm-initrd-generator/nmi-cmdline-reader.c
index ba5380afb2..89deddf0a3 100644
--- a/src/nm-initrd-generator/nmi-cmdline-reader.c
+++ b/src/nm-initrd-generator/nmi-cmdline-reader.c
@@ -1063,27 +1063,44 @@ reader_parse_vlan(Reader *reader, char *argument)
const char *vlan;
const char *phy;
const char *vlanid;
+ guint64 id;
vlan = get_word(&argument, ':');
phy = get_word(&argument, ':');
+ if (!vlan) {
+ _LOGW(LOGD_CORE, "missing VLAN interface name");
+ return;
+ }
+
+ if (!phy) {
+ _LOGW(LOGD_CORE, "missing VLAN parent");
+ return;
+ }
+
for (vlanid = vlan + strlen(vlan); vlanid > vlan; vlanid--) {
if (!g_ascii_isdigit(*(vlanid - 1)))
break;
}
+ if (vlanid[0] == '\0') {
+ _LOGW(LOGD_CORE, "missing VLAN id in '%s'", vlan);
+ return;
+ }
+
+ id = _nm_utils_ascii_str_to_int64(vlanid, 10, 0, 4094, G_MAXUINT);
+ if (id == G_MAXUINT) {
+ _LOGW(LOGD_CORE, "invalid VLAN id '%s'", vlanid);
+ return;
+ }
+
connection = reader_get_connection(reader, vlan, NM_SETTING_VLAN_SETTING_NAME, TRUE);
s_vlan = nm_connection_get_setting_vlan(connection);
- g_object_set(s_vlan,
- NM_SETTING_VLAN_PARENT,
- phy,
- NM_SETTING_VLAN_ID,
- (guint) _nm_utils_ascii_str_to_int64(vlanid, 10, 0, G_MAXUINT, G_MAXUINT),
- NULL);
+ g_object_set(s_vlan, NM_SETTING_VLAN_PARENT, phy, NM_SETTING_VLAN_ID, (guint32) id, NULL);
if (argument && *argument)
- _LOGW(LOGD_CORE, "Ignoring extra: '%s'.", argument);
+ _LOGW(LOGD_CORE, "ignoring extra VLAN argument '%s'", argument);
if (!nm_strv_ptrarray_contains(reader->vlan_parents, phy))
g_ptr_array_add(reader->vlan_parents, g_strdup(phy));
diff --git a/src/nm-initrd-generator/tests/test-cmdline-reader.c b/src/nm-initrd-generator/tests/test-cmdline-reader.c
index cd7b1069b6..413f61c655 100644
--- a/src/nm-initrd-generator/tests/test-cmdline-reader.c
+++ b/src/nm-initrd-generator/tests/test-cmdline-reader.c
@@ -1846,6 +1846,66 @@ test_vlan_over_bond(void)
}
}
+static void
+test_vlan_invalid(void)
+{
+ {
+ /* Case 1: Missing name */
+ const char *const *ARGV0 = NM_MAKE_STRV("vlan=");
+ gs_unref_hashtable GHashTable *connections = NULL;
+
+ NMTST_EXPECT_NM_WARN("cmdline-reader: missing VLAN interface name");
+ connections = _parse_cons(ARGV0);
+ g_assert_cmpint(g_hash_table_size(connections), ==, 0);
+ g_test_assert_expected_messages();
+ }
+
+ {
+ /* Case 2: Missing parent */
+ const char *const *ARGV0 = NM_MAKE_STRV("vlan=vlan12");
+ gs_unref_hashtable GHashTable *connections = NULL;
+
+ NMTST_EXPECT_NM_WARN("cmdline-reader: missing VLAN parent");
+ connections = _parse_cons(ARGV0);
+ g_assert_cmpint(g_hash_table_size(connections), ==, 0);
+ g_test_assert_expected_messages();
+ }
+
+ {
+ /* Case 3: Interface name without trailing digits should fail,
+ * not trigger a GLib assertion. */
+ const char *const *ARGV0 = NM_MAKE_STRV("vlan=myvlan:eth0");
+ gs_unref_hashtable GHashTable *connections = NULL;
+
+ NMTST_EXPECT_NM_WARN("cmdline-reader: missing VLAN id in 'myvlan'");
+ connections = _parse_cons(ARGV0);
+ g_assert_cmpint(g_hash_table_size(connections), ==, 0);
+ g_test_assert_expected_messages();
+ }
+
+ {
+ /* Case 4: An invalid VLAN id should be rejected */
+ const char *const *ARGV0 = NM_MAKE_STRV("vlan=myvlan4095:eth0");
+ gs_unref_hashtable GHashTable *connections = NULL;
+
+ NMTST_EXPECT_NM_WARN("cmdline-reader: invalid VLAN id '4095'");
+ connections = _parse_cons(ARGV0);
+ g_assert_cmpint(g_hash_table_size(connections), ==, 0);
+ g_test_assert_expected_messages();
+ }
+
+ {
+ /* Case 5: Extra arguments */
+ const char *const *ARGV0 = NM_MAKE_STRV("vlan=eth0.80:eth0:reorder_hdr=on");
+ gs_unref_hashtable GHashTable *connections = NULL;
+
+ NMTST_EXPECT_NM_WARN("cmdline-reader: ignoring extra VLAN argument 'reorder_hdr=on'");
+ connections = _parse_cons(ARGV0);
+ g_assert_cmpint(g_hash_table_size(connections), ==, 2);
+ g_test_assert_expected_messages();
+ }
+}
+
static void
test_ibft_ip_dev(void)
{
@@ -2820,6 +2880,7 @@ main(int argc, char **argv)
g_test_add_func("/initrd/cmdline/vlan", test_vlan);
g_test_add_func("/initrd/cmdline/vlan/dhcp-on-parent", test_vlan_with_dhcp_on_parent);
g_test_add_func("/initrd/cmdline/vlan/over-bond", test_vlan_over_bond);
+ g_test_add_func("/initrd/cmdline/vlan/invalid", test_vlan_invalid);
g_test_add_func("/initrd/cmdline/bridge", test_bridge);
g_test_add_func("/initrd/cmdline/bridge/default", test_bridge_default);
g_test_add_func("/initrd/cmdline/bridge/ip", test_bridge_ip);
diff --git a/src/nmcli/gen-metadata-nm-settings-nmcli.xml.in b/src/nmcli/gen-metadata-nm-settings-nmcli.xml.in
index 67b14d4c6e..a9476e38f9 100644
--- a/src/nmcli/gen-metadata-nm-settings-nmcli.xml.in
+++ b/src/nmcli/gen-metadata-nm-settings-nmcli.xml.in
@@ -2324,6 +2324,9 @@
nmcli-description="Like ip4-auto-default-route, but for the IPv6 default route."
format="ternary"
values="true/yes/on, false/no/off, default/unknown" />
+
\\(.*\\) (0x[0-9A-Fa-f]*)$/\1/p' | xargs -n1 readlink -f) -y
+RUN dnf debuginfo-install --skip-unavailable --skip-broken NetworkManager \$(ldd /usr/sbin/NetworkManager | sed -n 's/.* => \\(.*\\) (0x[0-9A-Fa-f]*)$/\1/p' | xargs -n1 readlink -f) -y
RUN dnf clean all