release: bump version to 1.55.3 (development)

This commit is contained in:
Filip Pokryvka 2025-08-01 16:00:57 +02:00
commit 7562b0e5f9
156 changed files with 5603 additions and 2964 deletions

12
NEWS
View file

@ -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

View file

@ -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

View file

@ -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" \

View file

@ -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 \

View file

@ -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'

View file

@ -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

View file

@ -1066,15 +1066,16 @@
<listitem><para><literal>dummy</literal></para></listitem>
<listitem><para><literal>generic</literal></para></listitem>
<listitem><para><literal>gsm</literal></para></listitem>
<listitem><para><literal>hsr</literal></para></listitem>
<listitem><para><literal>infiniband</literal></para></listitem>
<listitem><para><literal>ip-tunnel</literal></para></listitem>
<listitem><para><literal>ipvlan</literal></para></listitem>
<listitem><para><literal>loopback</literal></para></listitem>
<listitem><para><literal>macsec</literal></para></listitem>
<listitem><para><literal>macvlan</literal></para></listitem>
<listitem><para><literal>olpc-mesh</literal></para></listitem>
<listitem><para><literal>ovs-bridge</literal></para></listitem>
<listitem><para><literal>ovs-dpdk</literal></para></listitem>
<listitem><para><literal>ovs-interface</literal></para></listitem>
<listitem><para><literal>ovs-patch</literal></para></listitem>
<listitem><para><literal>ovs-port</literal></para></listitem>
<listitem><para><literal>pppoe</literal></para></listitem>
<listitem><para><literal>team</literal></para></listitem>

View file

@ -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',

View file

@ -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)')

View file

@ -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);
}

View file

@ -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__ */

View file

@ -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

View file

@ -14,7 +14,6 @@
#include <libudev.h>
#include <linux/if_ether.h>
#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;

View file

@ -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,

View file

@ -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. */

View file

@ -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()) {

View file

@ -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");

View file

@ -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();
}

View file

@ -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);
}

View file

@ -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__ */

View file

@ -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:
* <para>
* A comma-separated list of WireGuard peers. Each peer has the following syntax:
* </para>
* <para>
* <literal>
* <replaceable>public-key</replaceable>
* [<replaceable>attribute</replaceable>=<replaceable>value</replaceable>
* [<replaceable>attribute</replaceable>=<replaceable>value</replaceable>]...]
* </literal>
* </para>
* <para>
* The public key is required and must be encoded as base64; it can be
* calculated by running <command>wg pubkey</command> on the private key,
* and it is usually transmitted out of band to the author of the configuration
* file.
* </para>
* <para>
* The supported attributes are:
* <variablelist>
* <varlistentry>
* <term><varname>endpoint</varname></term>
* <listitem><para>An endpoint IP or hostname, followed by a colon, and then
* a port number.</para></listitem>
* </varlistentry>
*
* <varlistentry>
* <term><varname></varname></term>
* <listitem><para></para></listitem>
* </varlistentry>
* <varlistentry>
* <term><varname>allowed-ips</varname></term>
* <listitem><para>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
* </para></listitem>
* </varlistentry>
* <varlistentry>
* <term><varname>persistent-keepalive</varname></term>
* <listitem><para>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.
* </para></listitem>
* </varlistentry>
* <varlistentry>
* <term><varname>preshared-key</varname></term>
* <listitem><para>A base64 preshared key generated by "wg genpsk". Optional,
* and may be omitted.</para></listitem>
* </varlistentry>
* <varlistentry>
* <term><varname>preshared-key-flags</varname></term>
* <listitem><para>The secret flags for the preshared-key.</para></listitem>
* </varlistentry>
* </variablelist>
* </para>
* ---end---
*/
/* ---dbus---
* property: peers
* format: array of 'a{sv}'

View file

@ -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.
*

View file

@ -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

View file

@ -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);

View file

@ -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));

View file

@ -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)
{

View file

@ -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;

View file

@ -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;

View file

@ -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',

View file

@ -8,8 +8,8 @@
#include <net/ethernet.h>
#include <netinet/in.h>
#include "sd-event.h"
#include "sd-dhcp6-client.h"
#include "sd-event.h"
#include "dhcp-duid-internal.h"
#include "dhcp6-client-internal.h"

View file

@ -8,10 +8,10 @@
#include <inttypes.h>
#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"

View file

@ -6,13 +6,13 @@
#include "nm-sd-adapt-core.h"
#include <errno.h>
#include <linux/if_packet.h>
#include <netinet/in.h>
#include <netinet/ip6.h>
#include <stdio.h>
#include <string.h>
#include <sys/types.h>
#include <unistd.h>
#include <linux/if_packet.h>
#include "dhcp6-internal.h"
#include "dhcp6-protocol.h"

View file

@ -6,11 +6,11 @@
#include "nm-sd-adapt-core.h"
#include <errno.h>
#include <sys/ioctl.h>
#ifdef __GLIBC__
#include <linux/if_arp.h>
#endif
#include <linux/if_infiniband.h>
#include <sys/ioctl.h>
#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 = {};

View file

@ -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);

View file

@ -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 */

View file

@ -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);

View file

@ -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 */

View file

@ -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_");
}

View file

@ -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;
}

View file

@ -4,12 +4,21 @@
#include <errno.h>
#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 */

View file

@ -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 */

View file

@ -5,6 +5,7 @@
#include <sys/epoll.h>
#include <sys/timerfd.h>
#include <sys/wait.h>
#include <threads.h>
#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 ? &copy : 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, &copy) < 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 ? &copy : 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;
}

View file

@ -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"

View file

@ -4,6 +4,7 @@
#include <errno.h>
#include <fcntl.h>
#include <threads.h>
#include <unistd.h>
#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;

View file

@ -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

View file

@ -23,11 +23,10 @@
#include <sys/sysmacros.h>
#include <sys/types.h>
#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;

View file

@ -23,14 +23,13 @@
#include <net/ethernet.h>
#include <sys/types.h>
#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 {

View file

@ -23,9 +23,8 @@
#include <netinet/in.h>
#include <sys/types.h>
#include "sd-dhcp6-option.h"
#include "_sd-common.h"
#include "sd-dhcp6-option.h"
_SD_BEGIN_DECLARATIONS;

View file

@ -20,9 +20,8 @@
#include <inttypes.h>
#include <sys/types.h>
#include "sd-dhcp6-protocol.h"
#include "_sd-common.h"
#include "sd-dhcp6-protocol.h"
_SD_BEGIN_DECLARATIONS;

View file

@ -25,14 +25,13 @@
#include <netinet/in.h>
#include <sys/types.h>
#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;

View file

@ -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',

View file

@ -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;

View file

@ -7,7 +7,9 @@
#include <stdlib.h>
#include <string.h>
#include "assert-util.h"
#include "macro.h"
#include "memory-util.h"
#if HAS_FEATURE_MEMORY_SANITIZER
# include <sanitizer/msan_interface.h>
@ -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"

View file

@ -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)

View file

@ -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); \
})

View file

@ -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))

View file

@ -6,13 +6,13 @@
#include <fcntl.h>
#include <sys/ioctl.h>
#include <sys/stat.h>
#include <linux/fs.h>
#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"

View file

@ -2,7 +2,6 @@
#pragma once
#include <fcntl.h>
#include <linux/fs.h>
#include <stdbool.h>
#include <stddef.h>
@ -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);

View file

@ -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)))

View file

@ -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"

View file

@ -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;

View file

@ -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);

View file

@ -5,6 +5,7 @@
#include <stdlib.h>
#include <string.h>
#include "assert-util.h"
#include "macro.h"
/* strerror(3) says that glibc uses a maximum length of 1024 bytes. */

View file

@ -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;

View file

@ -8,7 +8,6 @@
#include <uchar.h>
#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);

View file

@ -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);
}

View file

@ -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]){})

View file

@ -4,9 +4,7 @@
#include <errno.h>
#include <fcntl.h>
#if WANT_LINUX_FS_H
#include <linux/fs.h>
#endif
#include <linux/kcmp.h>
#include <linux/magic.h>
#include <sys/ioctl.h>
#include <sys/resource.h>
@ -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:

View file

@ -8,6 +8,7 @@
#include <sys/socket.h>
#include "macro.h"
#include "memory-util.h"
#include "missing_fcntl.h"
#include "stdio-util.h"

View file

@ -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) {

View file

@ -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);

View file

@ -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);

View file

@ -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;
}

View file

@ -3,11 +3,11 @@
#include "nm-sd-adapt-shared.h"
#include <errno.h>
#include <linux/falloc.h>
#include <linux/magic.h>
#include <stddef.h>
#include <stdlib.h>
#include <sys/file.h>
#include <linux/falloc.h>
#include <linux/magic.h>
#include <unistd.h>
#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;
}

View file

@ -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;
}

View file

@ -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];
}

View file

@ -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) : " ";
}

View file

@ -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

View file

@ -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! */

View file

@ -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;

View file

@ -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);

View file

@ -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);

View file

@ -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;

View file

@ -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_;

View file

@ -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) {

View file

@ -0,0 +1,9 @@
/* SPDX-License-Identifier: LGPL-2.1-or-later */
#pragma once
#include <linux/if.h>
#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;

View file

@ -0,0 +1,19 @@
/* SPDX-License-Identifier: LGPL-2.1-or-later */
#pragma once
#include <linux/in.h>
#include <linux/in6.h>
#include <linux/ipv6.h>
#include <stddef.h>
#include <stdint.h>
#include <sys/socket.h>
#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;

View file

@ -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)

View file

@ -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);
}

View file

@ -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))

View file

@ -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"

View file

@ -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 */

View file

@ -5,6 +5,8 @@
/* Include here so consumers have LOCK_{EX,SH,NB} available. */
#include <sys/file.h>
#include "time-util.h"
typedef struct LockFile {
int dir_fd;
char *path;

View file

@ -7,10 +7,7 @@
#include <string.h>
#include <syslog.h>
#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))

View file

@ -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"

View file

@ -2,10 +2,11 @@
#include "nm-sd-adapt-shared.h"
#include <threads.h>
#include <unistd.h>
#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);
}

View file

@ -7,7 +7,6 @@
#include <string.h>
#include <sys/types.h>
#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);

View file

@ -6,6 +6,7 @@
#include <stdlib.h>
#include "format-util.h"
#include "log.h"
#include "macro.h"
#include "memory-util.h"
#include "mempool.h"

View file

@ -3,74 +3,16 @@
#include <fcntl.h>
#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)

View file

@ -0,0 +1,14 @@
/* SPDX-License-Identifier: LGPL-2.1-or-later */
#pragma once
#include <linux/fs.h>
/* 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

Some files were not shown because too many files have changed in this diff Show more