mirror of
https://gitlab.freedesktop.org/NetworkManager/NetworkManager.git
synced 2026-02-03 20:40:34 +01:00
release: bump version to 1.55.3 (development)
This commit is contained in:
commit
7562b0e5f9
156 changed files with 5603 additions and 2964 deletions
12
NEWS
12
NEWS
|
|
@ -1,3 +1,15 @@
|
|||
=============================================
|
||||
NetworkManager-1.56
|
||||
Overview of changes since NetworkManager-1.54
|
||||
=============================================
|
||||
|
||||
This is a snapshot of NetworkManager development. The API is
|
||||
subject to change and not guaranteed to be compatible with
|
||||
the later release.
|
||||
USE AT YOUR OWN RISK. NOT RECOMMENDED FOR PRODUCTION USE!
|
||||
|
||||
* nmcli now supports viewing and managing WireGuard peers.
|
||||
|
||||
=============================================
|
||||
NetworkManager-1.54
|
||||
Overview of changes since NetworkManager-1.52
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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" \
|
||||
|
|
|
|||
|
|
@ -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 \
|
||||
|
|
|
|||
|
|
@ -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'
|
||||
|
||||
|
|
|
|||
128
man/meson.build
128
man/meson.build
|
|
@ -1,29 +1,5 @@
|
|||
# SPDX-License-Identifier: LGPL-2.1-or-later
|
||||
|
||||
common_ent_file = configure_file(
|
||||
input: 'common.ent.in',
|
||||
output: '@BASENAME@',
|
||||
configuration: data_conf,
|
||||
)
|
||||
|
||||
xsltproc_options = [
|
||||
xsltproc,
|
||||
'--output', '@OUTPUT@',
|
||||
'--path', meson.current_build_dir(),
|
||||
'--xinclude',
|
||||
'--nonet',
|
||||
'--stringparam', 'man.output.quietly', '1',
|
||||
'--stringparam', 'funcsynopsis.style', 'ansi',
|
||||
'--stringparam', 'man.th.extra1.suppress', '1',
|
||||
'--stringparam', 'man.authors.section.enabled', '0',
|
||||
'--stringparam', 'man.copyright.section.enabled', '0',
|
||||
'--stringparam', 'man.th.title.max.length', '30',
|
||||
]
|
||||
|
||||
docbook_xls = 'http://docbook.sourceforge.net/release/xsl/current/manpages/docbook.xsl'
|
||||
|
||||
mans_xmls = []
|
||||
|
||||
mans = [
|
||||
['NetworkManager', '8'],
|
||||
['NetworkManager-dispatcher', '8'],
|
||||
|
|
@ -44,24 +20,74 @@ if enable_nm_cloud_setup
|
|||
mans += [['nm-cloud-setup', '8']]
|
||||
endif
|
||||
|
||||
foreach man: mans
|
||||
input = man[0] + '.xml'
|
||||
content_files += join_paths(meson.current_source_dir(), input)
|
||||
introspection_mans = [
|
||||
['nm-settings-keyfile', '5'],
|
||||
['nm-settings-dbus', '5'],
|
||||
['nm-settings-nmcli', '5'],
|
||||
]
|
||||
|
||||
output = '@0@.@1@'.format(man[0], man[1])
|
||||
if enable_ifcfg_rh
|
||||
introspection_mans += [['nm-settings-ifcfg-rh', '5']]
|
||||
endif
|
||||
|
||||
custom_target(
|
||||
output,
|
||||
input: input,
|
||||
output: output,
|
||||
command: xsltproc_options + [docbook_xls, '@INPUT@'],
|
||||
depend_files: common_ent_file,
|
||||
install: true,
|
||||
install_dir: join_paths(nm_mandir, 'man' + man[1]),
|
||||
)
|
||||
built_mans = []
|
||||
foreach man: mans + introspection_mans
|
||||
name = man[0] + '.' + man[1]
|
||||
if not fs.exists(name)
|
||||
built_mans = []
|
||||
break
|
||||
endif
|
||||
|
||||
built_mans += name
|
||||
endforeach
|
||||
|
||||
if enable_introspection
|
||||
if enable_introspection or enable_docs
|
||||
common_ent_file = configure_file(
|
||||
input: 'common.ent.in',
|
||||
output: '@BASENAME@',
|
||||
configuration: data_conf,
|
||||
)
|
||||
endif
|
||||
|
||||
if enable_introspection and (enable_man or enable_docs)
|
||||
xsltproc_options = [
|
||||
find_program('xsltproc'),
|
||||
'--output', '@OUTPUT@',
|
||||
'--path', meson.current_build_dir(),
|
||||
'--xinclude',
|
||||
'--nonet',
|
||||
'--stringparam', 'man.output.quietly', '1',
|
||||
'--stringparam', 'funcsynopsis.style', 'ansi',
|
||||
'--stringparam', 'man.th.extra1.suppress', '1',
|
||||
'--stringparam', 'man.authors.section.enabled', '0',
|
||||
'--stringparam', 'man.copyright.section.enabled', '0',
|
||||
'--stringparam', 'man.th.title.max.length', '30',
|
||||
]
|
||||
|
||||
docbook_xls = 'http://docbook.sourceforge.net/release/xsl/current/manpages/docbook.xsl'
|
||||
|
||||
mans_xmls = []
|
||||
|
||||
foreach man: mans
|
||||
input = man[0] + '.xml'
|
||||
content_files += join_paths(meson.current_source_dir(), input)
|
||||
|
||||
output = '@0@.@1@'.format(man[0], man[1])
|
||||
|
||||
# not needed if only html requested
|
||||
if enable_man
|
||||
custom_target(
|
||||
output,
|
||||
input: input,
|
||||
output: output,
|
||||
command: xsltproc_options + [docbook_xls, '@INPUT@'],
|
||||
depend_files: common_ent_file,
|
||||
install: true,
|
||||
install_dir: join_paths(nm_mandir, 'man' + man[1]),
|
||||
)
|
||||
endif
|
||||
endforeach
|
||||
|
||||
merge_cmd = files(source_root / 'tools' / 'generate-docs-nm-settings-docs-merge.py')
|
||||
|
||||
name = 'dbus'
|
||||
|
|
@ -124,13 +150,23 @@ if enable_introspection
|
|||
|
||||
output = '@0@.@1@'.format(man[0], man[1])
|
||||
|
||||
custom_target(
|
||||
output,
|
||||
input: input,
|
||||
output: output,
|
||||
command: xsltproc_options + [docbook_xls, '@INPUT@'],
|
||||
install: true,
|
||||
install_dir: join_paths(nm_mandir, 'man' + man[1]),
|
||||
)
|
||||
# not needed if only html requested
|
||||
if enable_man
|
||||
custom_target(
|
||||
output,
|
||||
input: input,
|
||||
output: output,
|
||||
command: xsltproc_options + [docbook_xls, '@INPUT@'],
|
||||
install: true,
|
||||
install_dir: join_paths(nm_mandir, 'man' + man[1]),
|
||||
)
|
||||
endif
|
||||
endforeach
|
||||
# not needed if only html requested
|
||||
elif enable_man
|
||||
if built_mans.length() > 0
|
||||
install_man(built_mans)
|
||||
else
|
||||
error('Building manpages requires xsltproc and -Dintrospection=true, and no prebuilt manpages were found. Try building from a release tarball or using -Dman=false.')
|
||||
endif
|
||||
endif
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
|
|||
48
meson.build
48
meson.build
|
|
@ -5,14 +5,14 @@ project(
|
|||
# NOTE: When incrementing version also add corresponding
|
||||
# NM_VERSION_x_y_z macros in
|
||||
# "src/libnm-core-public/nm-version-macros.h.in"
|
||||
version: '1.54.0',
|
||||
version: '1.55.3',
|
||||
license: 'GPL2+',
|
||||
default_options: [
|
||||
'buildtype=debugoptimized',
|
||||
'c_std=gnu11',
|
||||
'warning_level=2' # value "2" will add "-Wall" and "-Wextra" to the compiler flags
|
||||
],
|
||||
meson_version: '>= 0.51.0',
|
||||
meson_version: '>= 0.53.0',
|
||||
)
|
||||
|
||||
nm_name = meson.project_name()
|
||||
|
|
@ -77,6 +77,7 @@ libnm_version = '@0@.@1@.@2@'.format(current - age, age, revision)
|
|||
|
||||
libnm_pkgincludedir = join_paths(nm_includedir, libnm_name)
|
||||
|
||||
fs = import('fs')
|
||||
gnome = import('gnome')
|
||||
i18n = import('i18n')
|
||||
pkg = import('pkgconfig')
|
||||
|
|
@ -89,7 +90,6 @@ po_dir = source_root / 'po'
|
|||
top_inc = include_directories('.')
|
||||
|
||||
perl = find_program('perl')
|
||||
xsltproc = find_program('xsltproc')
|
||||
|
||||
check_exports = find_program(join_paths(source_root, 'tools', 'check-exports.sh'))
|
||||
|
||||
|
|
@ -816,6 +816,7 @@ if enable_nm_cloud_setup
|
|||
assert(jansson_dep.found(), 'nm-cloud-setup requires jansson library. Use -Dnm_cloud_setup=false to disable it')
|
||||
endif
|
||||
|
||||
enable_man = get_option('man')
|
||||
enable_docs = get_option('docs')
|
||||
|
||||
more_asserts = get_option('more_asserts')
|
||||
|
|
@ -964,38 +965,6 @@ data_conf.set('nmstatedir', nm_pkgstatedir)
|
|||
data_conf.set('sbindir', nm_sbindir)
|
||||
data_conf.set('sysconfdir', nm_sysconfdir)
|
||||
|
||||
# check if we can build setting property documentation
|
||||
'''
|
||||
build_docs=no
|
||||
if test -n "$INTROSPECTION_MAKEFILE"; then
|
||||
# If g-i is installed we know we have python, but we might not have pygobject
|
||||
if ! "$PYTHON" -c 'from gi.repository import GObject' >& /dev/null; then
|
||||
AC_MSG_ERROR(["--enable-introspection aims to build the settings documentation. This requires GObject introspection for python (pygobject)])
|
||||
fi
|
||||
|
||||
AC_PATH_PROG(PERL, perl)
|
||||
if test -z "$PERL"; then
|
||||
AC_MSG_ERROR([--enable-introspection requires perl])
|
||||
fi
|
||||
AC_PATH_PROG(XSLTPROC, xsltproc)
|
||||
if test -z "$XSLTPROC"; then
|
||||
AC_MSG_ERROR([--enable-introspection requires xsltproc])
|
||||
fi
|
||||
|
||||
have_introspection=yes
|
||||
if test "$enable_gtk_doc" = "yes"; then
|
||||
build_docs=yes
|
||||
fi
|
||||
else
|
||||
if test "$enable_gtk_doc" = "yes"; then
|
||||
# large parts of the documentation require introspection/pygobject to extract
|
||||
# the documentation out of the source files. You cannot enable gtk-doc without alone.
|
||||
AC_MSG_ERROR(["--with-gtk-doc requires --enable-introspection"])
|
||||
fi
|
||||
have_introspection=no
|
||||
fi
|
||||
'''
|
||||
|
||||
content_files = []
|
||||
|
||||
subdir('introspection')
|
||||
|
|
@ -1033,9 +1002,14 @@ if enable_qt != 'false'
|
|||
endif
|
||||
endif
|
||||
|
||||
# The man/ directory builds a couple targets needed by the docs build too.
|
||||
# If we build with docs but no man, then enter the subdir and only build
|
||||
# some targets.
|
||||
if enable_docs or enable_man
|
||||
subdir('man')
|
||||
endif
|
||||
if enable_docs
|
||||
assert(enable_introspection, '-Ddocs=true requires -Dintrospection=true')
|
||||
subdir('man')
|
||||
subdir('docs')
|
||||
meson.add_dist_script(
|
||||
'tools/meson-dist-data.sh',
|
||||
|
|
@ -1086,7 +1060,7 @@ meson.add_install_script(
|
|||
nm_pkgstatedir,
|
||||
nm_mandir,
|
||||
nm_sysconfdir,
|
||||
enable_docs ? '1' : '0',
|
||||
enable_man ? '1' : '0',
|
||||
enable_ifcfg_rh ? '1' : '0',
|
||||
enable_nm_cloud_setup ? '1' : '0',
|
||||
install_systemdunitdir ? '1' : '0',
|
||||
|
|
|
|||
|
|
@ -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)')
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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__ */
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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. */
|
||||
|
|
|
|||
|
|
@ -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()) {
|
||||
|
|
|
|||
|
|
@ -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");
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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__ */
|
||||
|
|
|
|||
|
|
@ -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}'
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
*
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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));
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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',
|
||||
|
|
|
|||
|
|
@ -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"
|
||||
|
|
|
|||
|
|
@ -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"
|
||||
|
|
|
|||
|
|
@ -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"
|
||||
|
|
|
|||
|
|
@ -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 = {};
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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 */
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
156
src/libnm-systemd-core/src/libsystemd/sd-device/device-util.c
Normal file
156
src/libnm-systemd-core/src/libsystemd/sd-device/device-util.c
Normal 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 */
|
||||
|
|
@ -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_");
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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 */
|
||||
|
|
|
|||
|
|
@ -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 */
|
||||
|
|
|
|||
|
|
@ -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 ? © : NULL, 0) < 0)
|
||||
return -errno;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Flags are only supported for pidfd_send_signal(), not for rt_sigqueueinfo(), hence let's refuse
|
||||
* this here. */
|
||||
if (flags != 0)
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
if (si) {
|
||||
/* We use rt_sigqueueinfo() only if siginfo_t is specified. */
|
||||
siginfo_t copy = *si;
|
||||
|
||||
if (rt_sigqueueinfo(s->child.pid, sig, ©) < 0)
|
||||
return -errno;
|
||||
} else if (kill(s->child.pid, sig) < 0)
|
||||
return -errno;
|
||||
|
||||
return 0;
|
||||
return RET_NERRNO(pidfd_send_signal(s->child.pidfd, sig, si ? © : NULL, flags));
|
||||
}
|
||||
|
||||
_public_ int sd_event_source_get_child_pidfd_own(sd_event_source *s) {
|
||||
assert_return(s, -EINVAL);
|
||||
assert_return(s->type == SOURCE_CHILD, -EDOM);
|
||||
assert_return(!event_origin_changed(s->event), -ECHILD);
|
||||
|
||||
if (s->child.pidfd < 0)
|
||||
return -EOPNOTSUPP;
|
||||
assert(s->child.pidfd >= 0);
|
||||
|
||||
return s->child.pidfd_owned;
|
||||
}
|
||||
|
|
@ -3272,9 +3241,7 @@ _public_ int sd_event_source_set_child_pidfd_own(sd_event_source *s, int own) {
|
|||
assert_return(s, -EINVAL);
|
||||
assert_return(s->type == SOURCE_CHILD, -EDOM);
|
||||
assert_return(!event_origin_changed(s->event), -ECHILD);
|
||||
|
||||
if (s->child.pidfd < 0)
|
||||
return -EOPNOTSUPP;
|
||||
assert(s->child.pidfd >= 0);
|
||||
|
||||
s->child.pidfd_owned = own;
|
||||
return 0;
|
||||
|
|
@ -3733,9 +3700,9 @@ static int process_child(sd_event *e, int64_t threshold, int64_t *ret_min_priori
|
|||
|
||||
e->need_process_child = false;
|
||||
|
||||
/* So, this is ugly. We iteratively invoke waitid() with P_PID + WNOHANG for each PID we wait
|
||||
* for, instead of using P_ALL. This is because we only want to get child information of very
|
||||
* specific child processes, and not all of them. We might not have processed the SIGCHLD event
|
||||
/* So, this is ugly. We iteratively invoke waitid() + WNOHANG with each child process we shall wait for,
|
||||
* instead of using P_ALL. This is because we only want to get child information of very specific
|
||||
* child processes, and not all of them. We might not have processed the SIGCHLD event
|
||||
* of a previous invocation and we don't want to maintain a unbounded *per-child* event queue,
|
||||
* hence we really don't want anything flushed out of the kernel's queue that we don't care
|
||||
* about. Since this is O(n) this means that if you have a lot of processes you probably want
|
||||
|
|
@ -3746,6 +3713,7 @@ static int process_child(sd_event *e, int64_t threshold, int64_t *ret_min_priori
|
|||
|
||||
HASHMAP_FOREACH(s, e->child_sources) {
|
||||
assert(s->type == SOURCE_CHILD);
|
||||
assert(s->child.pidfd >= 0);
|
||||
|
||||
if (s->priority > threshold)
|
||||
continue;
|
||||
|
|
@ -3765,23 +3733,21 @@ static int process_child(sd_event *e, int64_t threshold, int64_t *ret_min_priori
|
|||
continue;
|
||||
|
||||
zero(s->child.siginfo);
|
||||
if (waitid(P_PID, s->child.pid, &s->child.siginfo,
|
||||
if (waitid(P_PIDFD, s->child.pidfd, &s->child.siginfo,
|
||||
WNOHANG | (s->child.options & WEXITED ? WNOWAIT : 0) | s->child.options) < 0)
|
||||
return negative_errno();
|
||||
|
||||
if (s->child.siginfo.si_pid != 0) {
|
||||
bool zombie = IN_SET(s->child.siginfo.si_code, CLD_EXITED, CLD_KILLED, CLD_DUMPED);
|
||||
bool zombie = SIGINFO_CODE_IS_DEAD(s->child.siginfo.si_code);
|
||||
|
||||
if (zombie)
|
||||
s->child.exited = true;
|
||||
|
||||
if (!zombie && (s->child.options & WEXITED)) {
|
||||
/* If the child isn't dead then let's immediately remove the state
|
||||
* change from the queue, since there's no benefit in leaving it
|
||||
* queued. */
|
||||
else if (s->child.options & WEXITED) {
|
||||
/* If the child isn't dead then let's immediately remove the state change
|
||||
* from the queue, since there's no benefit in leaving it queued. */
|
||||
|
||||
assert(s->child.options & (WSTOPPED|WCONTINUED));
|
||||
(void) waitid(P_PID, s->child.pid, &s->child.siginfo, WNOHANG|(s->child.options & (WSTOPPED|WCONTINUED)));
|
||||
(void) waitid(P_PIDFD, s->child.pidfd, &s->child.siginfo, WNOHANG|(s->child.options & (WSTOPPED|WCONTINUED)));
|
||||
}
|
||||
|
||||
r = source_set_pending(s, true);
|
||||
|
|
@ -3802,6 +3768,7 @@ static int process_pidfd(sd_event *e, sd_event_source *s, uint32_t revents) {
|
|||
assert(e);
|
||||
assert(s);
|
||||
assert(s->type == SOURCE_CHILD);
|
||||
assert(s->child.pidfd >= 0);
|
||||
|
||||
if (s->pending)
|
||||
return 0;
|
||||
|
|
@ -3812,14 +3779,19 @@ static int process_pidfd(sd_event *e, sd_event_source *s, uint32_t revents) {
|
|||
if (!EVENT_SOURCE_WATCH_PIDFD(s))
|
||||
return 0;
|
||||
|
||||
/* Note that pidfd would also generate EPOLLHUP when the process gets reaped. But at this point we
|
||||
* only permit EPOLLIN, under the assumption that upon EPOLLHUP the child source should already
|
||||
* be set to pending, and we would have returned early above. */
|
||||
assert(!s->child.exited);
|
||||
|
||||
zero(s->child.siginfo);
|
||||
if (waitid(P_PID, s->child.pid, &s->child.siginfo, WNOHANG | WNOWAIT | s->child.options) < 0)
|
||||
if (waitid(P_PIDFD, s->child.pidfd, &s->child.siginfo, WNOHANG | WNOWAIT | s->child.options) < 0)
|
||||
return -errno;
|
||||
|
||||
if (s->child.siginfo.si_pid == 0)
|
||||
return 0;
|
||||
|
||||
if (IN_SET(s->child.siginfo.si_code, CLD_EXITED, CLD_KILLED, CLD_DUMPED))
|
||||
if (SIGINFO_CODE_IS_DEAD(s->child.siginfo.si_code))
|
||||
s->child.exited = true;
|
||||
|
||||
return source_set_pending(s, true);
|
||||
|
|
@ -4232,15 +4204,13 @@ static int source_dispatch(sd_event_source *s) {
|
|||
break;
|
||||
|
||||
case SOURCE_CHILD: {
|
||||
bool zombie;
|
||||
|
||||
zombie = IN_SET(s->child.siginfo.si_code, CLD_EXITED, CLD_KILLED, CLD_DUMPED);
|
||||
bool zombie = SIGINFO_CODE_IS_DEAD(s->child.siginfo.si_code);
|
||||
|
||||
r = s->child.callback(s, &s->child.siginfo, s->userdata);
|
||||
|
||||
/* Now, reap the PID for good. */
|
||||
if (zombie) {
|
||||
(void) waitid(P_PID, s->child.pid, &s->child.siginfo, WNOHANG|WEXITED);
|
||||
(void) waitid(P_PIDFD, s->child.pidfd, &s->child.siginfo, WNOHANG|WEXITED);
|
||||
s->child.waited = true;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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"
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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',
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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"
|
||||
|
|
|
|||
48
src/libnm-systemd-shared/src/basic/assert-util.h
Normal file
48
src/libnm-systemd-shared/src/basic/assert-util.h
Normal 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)
|
||||
|
|
@ -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); \
|
||||
})
|
||||
|
|
|
|||
|
|
@ -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))
|
||||
|
|
|
|||
|
|
@ -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"
|
||||
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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)))
|
||||
|
|
|
|||
|
|
@ -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"
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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. */
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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]){})
|
||||
|
||||
|
|
|
|||
|
|
@ -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:
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@
|
|||
#include <sys/socket.h>
|
||||
|
||||
#include "macro.h"
|
||||
#include "memory-util.h"
|
||||
#include "missing_fcntl.h"
|
||||
#include "stdio-util.h"
|
||||
|
||||
|
|
|
|||
|
|
@ -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) {
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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];
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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) : " ";
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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! */
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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_;
|
||||
|
|
|
|||
|
|
@ -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) {
|
||||
|
|
|
|||
9
src/libnm-systemd-shared/src/basic/include/net/if.h
Normal file
9
src/libnm-systemd-shared/src/basic/include/net/if.h
Normal 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;
|
||||
19
src/libnm-systemd-shared/src/basic/include/netinet/in.h
Normal file
19
src/libnm-systemd-shared/src/basic/include/netinet/in.h
Normal 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;
|
||||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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))
|
||||
|
|
|
|||
|
|
@ -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"
|
||||
|
|
|
|||
|
|
@ -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 */
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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))
|
||||
|
|
|
|||
|
|
@ -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"
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@
|
|||
#include <stdlib.h>
|
||||
|
||||
#include "format-util.h"
|
||||
#include "log.h"
|
||||
#include "macro.h"
|
||||
#include "memory-util.h"
|
||||
#include "mempool.h"
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
14
src/libnm-systemd-shared/src/basic/missing_fs.h
Normal file
14
src/libnm-systemd-shared/src/basic/missing_fs.h
Normal 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
Loading…
Add table
Reference in a new issue