diff options
Diffstat (limited to 'shared')
186 files changed, 10013 insertions, 2623 deletions
diff --git a/shared/README.md b/shared/README.md new file mode 100644 index 00000000..ed950fce --- /dev/null +++ b/shared/README.md @@ -0,0 +1,108 @@ +The "shared/" Directory +======================= + +For NetworkManager we place helper/utility code under "shared/" +in static libraries. The idea is to avoid code duplication but also +provide high quality helper functions that simplify the higher layers. +In NetworkManager there are complicated parts, for example "src/nm-manager.c" +is huge. On the other hand, this helper code should be simple and easy +to understand, so that we can build more complex code on top of it. + +As we statically link them into our binaries, they are all inherently +internal API, that means they cannot be part of libnm's (libnm-core's) public API. +It also means that their API/ABI is not stable. + +We don't care these libraries to be minimal and contain only symbols that are +used by all users. Instead, we expect the linker to throw away unused symbols. +We achieve this by having a symbol versioning file to hide internal symbols +(which gives the linker a possibility to remove them if they are unused) and +compiling with LTO or `"-Wl,--gc-sections"`. Let the tool solve this and not +manual organization. + +Hence these libraries (and their content) are structured this way to satisfy +the following questions: + +1) which dependencies (libraries) do they have? That determines which + other libraries can use it. For example: + + - "shared/nm-std-aux" and "shared/nm-glib-aux" both provide general + purpose helpers, the difference is that the former has no dependency + on glib2 library. Both these libraries are a basic dependency for + many other parts of the code. + + - "shared/nm-udev-aux" has a dependency on libudev, it thus cannot + be in "shared/nm-glib-aux". + + - client code also has a glib2 dependency. That means it can link with + "shared/nm-std-aux" and "shared/nm-glib-aux", but must not link + with "shared/nm-udev-aux" (as it has no direct udev dependenct -- + although clients get it indirectly because libnm already requires + it). + +2) what is their overall purpose? As said, we rely on the linker to + prune unused symbols. But in a few cases we avoid to merge different + code in the same library. For example: + + - "shared/nm-glib-aux" and "shared/nm-base" both only have a + glib2 dependency. Hence, they could be merged. However we + don't do that because "shared/nm-base" is more about NetworkManager + specific code, while "shared/nm-glib-aux" is about general + purpose helpers. + +3) some of these libraries are forked from an upstream. They are kept + separate so that we can re-import future upstream versions. + +Detail +====== + +- `shared/c-list` +- `shared/c-rbtree` +- `shared/c-siphash` +- `shared/c-stdoux` +- `shared/n-acd` +- `shared/n-dhcp4` + + These are forked from upstream and imported with git-subtree. They + in general only have a libc dependency (or dependencies between each + other). + +- `shared/nm-std-aux` + + This contains helper code with only a libc dependency. + Almost all C code depends on this library. + +- `shared/nm-glib-aux` + + Like "shared/nm-std-aux" but also has a glib2 dependency. + Almost all glib2 code depends on this library. + +- `shared/nm-udev/aux` + + Like "shared/nm-glib-aux" but also has a libudev dependency. It + has code related to libudev. + +- `shared/systemd` + + These are forked from upstream systemd and imported with a script. + Under "shared/systemd/src" we try to keep the sources as close to + the original as possible. There is also some adapter code to make + it useable for us. It has a dependency on "shared/nm-glib-aux" + and will need a logging implementation for "shared/nm-glib-aux/nm-logging-fwd.h". + +- `shared/nm-base` + + Depends on "shared/nm-glib-aux" and glib2 but it provides helper code + that more about NetworkManager specifc things. + +- `shared/nm-log-core` + + This is the logging implementation as used by NetworkManager core ("src/"). + It is also a dependency for "shared/nm-platform". + +- `shared/nm-platform` + + Platform implementation. It depends on "shared/nm-log-core", "shared/nm-base" + and "shared/nm-glib-aux". + +- Other than that, there are still a few unorganized files/directories here. + These should be cleaned up. diff --git a/shared/c-list/src/c-list.h b/shared/c-list/src/c-list.h index 3d44e330..c92aa6f3 100644 --- a/shared/c-list/src/c-list.h +++ b/shared/c-list/src/c-list.h @@ -87,10 +87,12 @@ static inline _Bool c_list_is_linked(const CList *what) { * c_list_is_empty() - check whether a list is empty * @list: list to check, or NULL * + * This is the same as !c_list_is_linked(). + * * Return: True if @list is empty, false if not. */ static inline _Bool c_list_is_empty(const CList *list) { - return !list || !c_list_is_linked(list); + return !c_list_is_linked(list); } /** diff --git a/shared/c-stdaux/src/c-stdaux.h b/shared/c-stdaux/src/c-stdaux.h index 08d155ce..1cdbbbcf 100644 --- a/shared/c-stdaux/src/c-stdaux.h +++ b/shared/c-stdaux/src/c-stdaux.h @@ -84,7 +84,10 @@ extern "C" { * * Return: Evaluates to @_expr. */ -#define C_EXPR_ASSERT(_expr, _assertion, _message) \ +#if defined(__COVERITY__) // Coverity cannot const-fold __builtin_choose_expr() +# define C_EXPR_ASSERT(_expr, _assertion, _message) (_expr) +#else +# define C_EXPR_ASSERT(_expr, _assertion, _message) \ /* indentation and line-split to get better diagnostics */ \ (__builtin_choose_expr( \ !!(1 + 0 * sizeof( \ @@ -95,6 +98,7 @@ _Static_assert(_assertion, _message); \ (_expr), \ ((void)0) \ )) +#endif /** * C_STRINGIFY() - stringify a token, but evaluate it first diff --git a/shared/meson.build b/shared/meson.build index 0f46a00c..52eb6b9b 100644 --- a/shared/meson.build +++ b/shared/meson.build @@ -1,10 +1,16 @@ -# SPDX-License-Identifier: LGPL-2.1+ +# SPDX-License-Identifier: LGPL-2.1-or-later shared_inc = include_directories('.') -nm_default_dep = declare_dependency(include_directories: [top_inc, shared_inc]) - -glib_nm_default_dep = declare_dependency(dependencies: [glib_dep, nm_default_dep]) +glib_nm_default_dep = declare_dependency( + include_directories: [ + top_inc, + shared_inc, + ], + dependencies: [ + glib_dep, + ], +) libc_siphash = static_library( 'c-siphash', @@ -19,44 +25,37 @@ libc_rbtree = static_library( c_args: '-std=c11', ) -sources = files( - 'n-acd/src/n-acd.c', - 'n-acd/src/n-acd-probe.c', - 'n-acd/src/util/timer.c', -) - if enable_ebpf - sources += files('n-acd/src/n-acd-bpf.c') + n_acd_bpf_source = 'n-acd/src/n-acd-bpf.c' else - sources += files('n-acd/src/n-acd-bpf-fallback.c') + n_acd_bpf_source = 'n-acd/src/n-acd-bpf-fallback.c' endif -incs = include_directories( - 'c-list/src', - 'c-rbtree/src', - 'c-siphash/src', - 'c-stdaux/src', -) - -c_flags = [ - '-D_GNU_SOURCE', - '-DSO_ATTACH_BPF=50', - '-std=c11', - '-Wno-pointer-arith', - '-Wno-vla', -] - -links = [ - libc_rbtree, - libc_siphash, -] - libn_acd = static_library( 'n-acd', - sources: sources, - include_directories: incs, - c_args: c_flags, - link_with: links, + sources: files( + 'n-acd/src/n-acd.c', + 'n-acd/src/n-acd-probe.c', + 'n-acd/src/util/timer.c', + n_acd_bpf_source, + ), + include_directories: include_directories( + 'c-list/src', + 'c-rbtree/src', + 'c-siphash/src', + 'c-stdaux/src', + ), + c_args: [ + '-D_GNU_SOURCE', + '-DSO_ATTACH_BPF=50', + '-std=c11', + '-Wno-pointer-arith', + '-Wno-vla', + ], + link_with: [ + libc_rbtree, + libc_siphash, + ], ) libn_acd_dep = declare_dependency( @@ -64,35 +63,29 @@ libn_acd_dep = declare_dependency( link_with: libn_acd, ) -sources = files( - 'n-dhcp4/src/n-dhcp4-c-connection.c', - 'n-dhcp4/src/n-dhcp4-c-lease.c', - 'n-dhcp4/src/n-dhcp4-client.c', - 'n-dhcp4/src/n-dhcp4-c-probe.c', - 'n-dhcp4/src/n-dhcp4-incoming.c', - 'n-dhcp4/src/n-dhcp4-outgoing.c', - 'n-dhcp4/src/n-dhcp4-socket.c', - 'n-dhcp4/src/util/packet.c', - 'n-dhcp4/src/util/socket.c', -) - -incs = include_directories( - 'c-list/src', - 'c-siphash/src', - 'c-stdaux/src', -) - -c_flags = [ - '-D_GNU_SOURCE', - '-Wno-declaration-after-statement', - '-Wno-pointer-arith', -] - libn_dhcp4 = static_library( 'n-dhcp4', - sources: sources, - c_args: c_flags, - include_directories: incs, + sources: files( + 'n-dhcp4/src/n-dhcp4-c-connection.c', + 'n-dhcp4/src/n-dhcp4-c-lease.c', + 'n-dhcp4/src/n-dhcp4-client.c', + 'n-dhcp4/src/n-dhcp4-c-probe.c', + 'n-dhcp4/src/n-dhcp4-incoming.c', + 'n-dhcp4/src/n-dhcp4-outgoing.c', + 'n-dhcp4/src/n-dhcp4-socket.c', + 'n-dhcp4/src/util/packet.c', + 'n-dhcp4/src/util/socket.c', + ), + c_args: [ + '-D_GNU_SOURCE', + '-Wno-declaration-after-statement', + '-Wno-pointer-arith', + ], + include_directories: include_directories( + 'c-list/src', + 'c-siphash/src', + 'c-stdaux/src', + ), link_with: libc_siphash, ) @@ -101,14 +94,6 @@ libn_dhcp4_dep = declare_dependency( link_with: libn_dhcp4, ) -nm_version_macro_header = configure_file( - input: 'nm-version-macros.h.in', - output: '@BASENAME@', - configuration: data_conf, -) - -nm_meta_setting_source = files('nm-meta-setting.c') - nm_test_utils_impl_source = files('nm-test-utils-impl.c') nm_vpn_plugin_utils_source = files('nm-utils/nm-vpn-plugin-utils.c') @@ -121,52 +106,43 @@ libnm_std_aux = static_library( ], include_directories: top_inc, c_args: [ - '-DG_LOG_DOMAIN="@0@"'.format(libnm_name), - '-DNETWORKMANAGER_COMPILATION=0', + '-DG_LOG_DOMAIN="libnm"', ], ) -sources = files( - 'nm-glib-aux/nm-dbus-aux.c', - 'nm-glib-aux/nm-dedup-multi.c', - 'nm-glib-aux/nm-enum-utils.c', - 'nm-glib-aux/nm-errno.c', - 'nm-glib-aux/nm-hash-utils.c', - 'nm-glib-aux/nm-io-utils.c', - 'nm-glib-aux/nm-json-aux.c', - 'nm-glib-aux/nm-keyfile-aux.c', - 'nm-glib-aux/nm-logging-base.c', - 'nm-glib-aux/nm-random-utils.c', - 'nm-glib-aux/nm-ref-string.c', - 'nm-glib-aux/nm-secret-utils.c', - 'nm-glib-aux/nm-shared-utils.c', - 'nm-glib-aux/nm-time-utils.c', -) - -c_flags = [ - '-DG_LOG_DOMAIN="@0@"'.format(libnm_name), - '-DNETWORKMANAGER_COMPILATION=(NM_NETWORKMANAGER_COMPILATION_GLIB|NM_NETWORKMANAGER_COMPILATION_WITH_GLIB_I18N_LIB)', -] - -links = [ - libc_siphash, - libnm_std_aux, -] - -libnm_utils_base = static_library( - 'nm-utils-base', - sources: sources, +libnm_glib_aux = static_library( + 'nm-glib-aux', + sources: files( + 'nm-glib-aux/nm-dbus-aux.c', + 'nm-glib-aux/nm-dedup-multi.c', + 'nm-glib-aux/nm-enum-utils.c', + 'nm-glib-aux/nm-errno.c', + 'nm-glib-aux/nm-hash-utils.c', + 'nm-glib-aux/nm-io-utils.c', + 'nm-glib-aux/nm-json-aux.c', + 'nm-glib-aux/nm-keyfile-aux.c', + 'nm-glib-aux/nm-logging-base.c', + 'nm-glib-aux/nm-random-utils.c', + 'nm-glib-aux/nm-ref-string.c', + 'nm-glib-aux/nm-secret-utils.c', + 'nm-glib-aux/nm-shared-utils.c', + 'nm-glib-aux/nm-time-utils.c', + ), dependencies: glib_nm_default_dep, - c_args: c_flags, - link_with: links, + c_args: [ + '-DG_LOG_DOMAIN="libnm"', + ], + link_with: [ + libc_siphash, + libnm_std_aux, + ], ) -libnm_utils_base_dep = declare_dependency( +libnm_glib_aux_dep = declare_dependency( dependencies: glib_nm_default_dep, - link_with: libnm_utils_base, + link_with: libnm_glib_aux, ) - libnm_udev_aux = static_library( 'nm-udev-aux', sources: 'nm-udev-aux/nm-udev-utils.c', @@ -174,7 +150,9 @@ libnm_udev_aux = static_library( glib_nm_default_dep, libudev_dep, ], - c_args: c_flags, + c_args: [ + '-DG_LOG_DOMAIN="libnm"', + ], ) libnm_udev_aux_dep = declare_dependency( @@ -182,67 +160,125 @@ libnm_udev_aux_dep = declare_dependency( link_with: libnm_udev_aux, ) -sources = files( - 'systemd/nm-sd-utils-shared.c', - 'systemd/src/basic/alloc-util.c', - 'systemd/src/basic/env-file.c', - 'systemd/src/basic/env-util.c', - 'systemd/src/basic/escape.c', - 'systemd/src/basic/ether-addr-util.c', - 'systemd/src/basic/extract-word.c', - 'systemd/src/basic/fd-util.c', - 'systemd/src/basic/fileio.c', - 'systemd/src/basic/format-util.c', - 'systemd/src/basic/fs-util.c', - 'systemd/src/basic/hash-funcs.c', - 'systemd/src/basic/hashmap.c', - 'systemd/src/basic/hexdecoct.c', - 'systemd/src/basic/hostname-util.c', - 'systemd/src/basic/in-addr-util.c', - 'systemd/src/basic/io-util.c', - 'systemd/src/basic/memory-util.c', - 'systemd/src/basic/mempool.c', - 'systemd/src/basic/parse-util.c', - 'systemd/src/basic/path-util.c', - 'systemd/src/basic/prioq.c', - 'systemd/src/basic/process-util.c', - 'systemd/src/basic/random-util.c', - 'systemd/src/basic/signal-util.c', - 'systemd/src/basic/socket-util.c', - 'systemd/src/basic/stat-util.c', - 'systemd/src/basic/string-table.c', - 'systemd/src/basic/string-util.c', - 'systemd/src/basic/strv.c', - 'systemd/src/basic/strxcpyx.c', - 'systemd/src/basic/time-util.c', - 'systemd/src/basic/tmpfile-util.c', - 'systemd/src/basic/utf8.c', - 'systemd/src/basic/util.c', - 'systemd/src/shared/dns-domain.c', - 'systemd/src/shared/web-util.c', +libnm_base = static_library( + 'nm-base', + sources: files( + 'nm-base/nm-ethtool-base.c', + ), + dependencies: libnm_glib_aux_dep, + c_args: [ + '-DG_LOG_DOMAIN="libnm"', + ], ) -incs = include_directories( - 'systemd/sd-adapt-shared', - 'systemd/src/basic', - 'systemd/src/shared', +libnm_base_dep = declare_dependency( + include_directories: shared_inc, + dependencies: libnm_glib_aux_dep, + link_with: libnm_base, ) -c_flags = [ - '-DG_LOG_DOMAIN="@0@"'.format(libnm_name), - '-DNETWORKMANAGER_COMPILATION=NM_NETWORKMANAGER_COMPILATION_SYSTEMD_SHARED', -] +libnm_log_core = static_library( + 'nm-log-core', + sources: 'nm-log-core/nm-logging.c', + dependencies: [ + glib_nm_default_dep, + libsystemd_dep, + ], + c_args: [ + '-DG_LOG_DOMAIN="NetworkManager"', + ], +) + +libnm_log_core_dep = declare_dependency( + include_directories: shared_inc, + dependencies: [ + libnm_glib_aux_dep, + ], + link_with: libnm_log_core, +) + +libnm_platform = static_library( + 'nm-platform', + sources: [ + 'nm-platform/nm-netlink.c', + 'nm-platform/nm-platform-utils.c', + 'nm-platform/nmp-netns.c', + ], + dependencies: [ + glib_nm_default_dep, + ], + c_args: [ + '-DG_LOG_DOMAIN="NetworkManager"', + ], +) + +libnm_platform_dep = declare_dependency( + include_directories: shared_inc, + dependencies: [ + libnm_glib_aux_dep, + ], + link_with: libnm_platform, +) libnm_systemd_shared = static_library( 'nm-systemd-shared', - sources: sources, - include_directories: incs, + sources: files( + 'systemd/nm-sd-utils-shared.c', + 'systemd/src/basic/alloc-util.c', + 'systemd/src/basic/env-file.c', + 'systemd/src/basic/env-util.c', + 'systemd/src/basic/escape.c', + 'systemd/src/basic/ether-addr-util.c', + 'systemd/src/basic/extract-word.c', + 'systemd/src/basic/fd-util.c', + 'systemd/src/basic/fileio.c', + 'systemd/src/basic/format-util.c', + 'systemd/src/basic/fs-util.c', + 'systemd/src/basic/hash-funcs.c', + 'systemd/src/basic/hashmap.c', + 'systemd/src/basic/hexdecoct.c', + 'systemd/src/basic/hostname-util.c', + 'systemd/src/basic/in-addr-util.c', + 'systemd/src/basic/io-util.c', + 'systemd/src/basic/memory-util.c', + 'systemd/src/basic/mempool.c', + 'systemd/src/basic/parse-util.c', + 'systemd/src/basic/path-util.c', + 'systemd/src/basic/prioq.c', + 'systemd/src/basic/process-util.c', + 'systemd/src/basic/random-util.c', + 'systemd/src/basic/ratelimit.c', + 'systemd/src/basic/signal-util.c', + 'systemd/src/basic/socket-util.c', + 'systemd/src/basic/stat-util.c', + 'systemd/src/basic/string-table.c', + 'systemd/src/basic/string-util.c', + 'systemd/src/basic/strv.c', + 'systemd/src/basic/strxcpyx.c', + 'systemd/src/basic/time-util.c', + 'systemd/src/basic/tmpfile-util.c', + 'systemd/src/basic/utf8.c', + 'systemd/src/basic/util.c', + 'systemd/src/shared/dns-domain.c', + 'systemd/src/shared/web-util.c', + ), + include_directories: include_directories( + 'systemd/sd-adapt-shared', + 'systemd/src/basic', + 'systemd/src/shared', + ), dependencies: glib_nm_default_dep, - c_args: c_flags, + c_args: [ + '-DG_LOG_DOMAIN="libnm"', + ], ) libnm_systemd_shared_dep = declare_dependency( - include_directories: incs, + include_directories: include_directories( + 'systemd/sd-adapt-shared', + 'systemd/src/basic', + 'systemd/src/shared', + ), dependencies: glib_dep, link_with: libnm_systemd_shared, ) @@ -251,9 +287,12 @@ libnm_systemd_logging_stub = static_library( 'nm-systemd-logging-stub', sources: 'systemd/nm-logging-stub.c', dependencies: glib_nm_default_dep, - c_args: c_flags, + c_args: [ + '-DG_LOG_DOMAIN="libnm"', + ], ) if enable_tests subdir('nm-glib-aux/tests') + subdir('nm-platform/tests') endif diff --git a/shared/n-dhcp4/src/util/packet.c b/shared/n-dhcp4/src/util/packet.c index ef18b0b4..48f2c85e 100644 --- a/shared/n-dhcp4/src/util/packet.c +++ b/shared/n-dhcp4/src/util/packet.c @@ -223,7 +223,7 @@ int packet_sendto_udp(int sockfd, pktlen = sendmsg(sockfd, &msg, 0); if (pktlen < 0) - return -errno; + return -c_errno(); /* * Kernel never truncates. Worst case, we get -EMSGSIZE. Kernel *might* diff --git a/shared/nm-base/nm-base.h b/shared/nm-base/nm-base.h new file mode 100644 index 00000000..105d1783 --- /dev/null +++ b/shared/nm-base/nm-base.h @@ -0,0 +1,210 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +/* + * Copyright (C) 2018 Red Hat, Inc. + */ + +#ifndef __NM_LIBNM_BASE_H__ +#define __NM_LIBNM_BASE_H__ + +/*****************************************************************************/ + +/* this must be the same as NM_UTILS_HWADDR_LEN_MAX from libnm. */ +#define _NM_UTILS_HWADDR_LEN_MAX 20 + +/*****************************************************************************/ + +typedef enum { + NM_ETHTOOL_ID_UNKNOWN = -1, + + _NM_ETHTOOL_ID_FIRST = 0, + + _NM_ETHTOOL_ID_COALESCE_FIRST = _NM_ETHTOOL_ID_FIRST, + NM_ETHTOOL_ID_COALESCE_ADAPTIVE_RX = _NM_ETHTOOL_ID_COALESCE_FIRST, + NM_ETHTOOL_ID_COALESCE_ADAPTIVE_TX, + NM_ETHTOOL_ID_COALESCE_PKT_RATE_HIGH, + NM_ETHTOOL_ID_COALESCE_PKT_RATE_LOW, + NM_ETHTOOL_ID_COALESCE_RX_FRAMES, + NM_ETHTOOL_ID_COALESCE_RX_FRAMES_HIGH, + NM_ETHTOOL_ID_COALESCE_RX_FRAMES_IRQ, + NM_ETHTOOL_ID_COALESCE_RX_FRAMES_LOW, + NM_ETHTOOL_ID_COALESCE_RX_USECS, + NM_ETHTOOL_ID_COALESCE_RX_USECS_HIGH, + NM_ETHTOOL_ID_COALESCE_RX_USECS_IRQ, + NM_ETHTOOL_ID_COALESCE_RX_USECS_LOW, + NM_ETHTOOL_ID_COALESCE_SAMPLE_INTERVAL, + NM_ETHTOOL_ID_COALESCE_STATS_BLOCK_USECS, + NM_ETHTOOL_ID_COALESCE_TX_FRAMES, + NM_ETHTOOL_ID_COALESCE_TX_FRAMES_HIGH, + NM_ETHTOOL_ID_COALESCE_TX_FRAMES_IRQ, + NM_ETHTOOL_ID_COALESCE_TX_FRAMES_LOW, + NM_ETHTOOL_ID_COALESCE_TX_USECS, + NM_ETHTOOL_ID_COALESCE_TX_USECS_HIGH, + NM_ETHTOOL_ID_COALESCE_TX_USECS_IRQ, + NM_ETHTOOL_ID_COALESCE_TX_USECS_LOW, + _NM_ETHTOOL_ID_COALESCE_LAST = NM_ETHTOOL_ID_COALESCE_TX_USECS_LOW, + + _NM_ETHTOOL_ID_FEATURE_FIRST = _NM_ETHTOOL_ID_COALESCE_LAST + 1, + NM_ETHTOOL_ID_FEATURE_ESP_HW_OFFLOAD = _NM_ETHTOOL_ID_FEATURE_FIRST, + NM_ETHTOOL_ID_FEATURE_ESP_TX_CSUM_HW_OFFLOAD, + NM_ETHTOOL_ID_FEATURE_FCOE_MTU, + NM_ETHTOOL_ID_FEATURE_GRO, + NM_ETHTOOL_ID_FEATURE_GSO, + NM_ETHTOOL_ID_FEATURE_HIGHDMA, + NM_ETHTOOL_ID_FEATURE_HW_TC_OFFLOAD, + NM_ETHTOOL_ID_FEATURE_L2_FWD_OFFLOAD, + NM_ETHTOOL_ID_FEATURE_LOOPBACK, + NM_ETHTOOL_ID_FEATURE_LRO, + NM_ETHTOOL_ID_FEATURE_MACSEC_HW_OFFLOAD, + NM_ETHTOOL_ID_FEATURE_NTUPLE, + NM_ETHTOOL_ID_FEATURE_RX, + NM_ETHTOOL_ID_FEATURE_RXHASH, + NM_ETHTOOL_ID_FEATURE_RXVLAN, + NM_ETHTOOL_ID_FEATURE_RX_ALL, + NM_ETHTOOL_ID_FEATURE_RX_FCS, + NM_ETHTOOL_ID_FEATURE_RX_GRO_HW, + NM_ETHTOOL_ID_FEATURE_RX_GRO_LIST, + NM_ETHTOOL_ID_FEATURE_RX_UDP_GRO_FORWARDING, + NM_ETHTOOL_ID_FEATURE_RX_UDP_TUNNEL_PORT_OFFLOAD, + NM_ETHTOOL_ID_FEATURE_RX_VLAN_FILTER, + NM_ETHTOOL_ID_FEATURE_RX_VLAN_STAG_FILTER, + NM_ETHTOOL_ID_FEATURE_RX_VLAN_STAG_HW_PARSE, + NM_ETHTOOL_ID_FEATURE_SG, + NM_ETHTOOL_ID_FEATURE_TLS_HW_RECORD, + NM_ETHTOOL_ID_FEATURE_TLS_HW_RX_OFFLOAD, + NM_ETHTOOL_ID_FEATURE_TLS_HW_TX_OFFLOAD, + NM_ETHTOOL_ID_FEATURE_TSO, + NM_ETHTOOL_ID_FEATURE_TX, + NM_ETHTOOL_ID_FEATURE_TXVLAN, + NM_ETHTOOL_ID_FEATURE_TX_CHECKSUM_FCOE_CRC, + NM_ETHTOOL_ID_FEATURE_TX_CHECKSUM_IPV4, + NM_ETHTOOL_ID_FEATURE_TX_CHECKSUM_IPV6, + NM_ETHTOOL_ID_FEATURE_TX_CHECKSUM_IP_GENERIC, + NM_ETHTOOL_ID_FEATURE_TX_CHECKSUM_SCTP, + NM_ETHTOOL_ID_FEATURE_TX_ESP_SEGMENTATION, + NM_ETHTOOL_ID_FEATURE_TX_FCOE_SEGMENTATION, + NM_ETHTOOL_ID_FEATURE_TX_GRE_CSUM_SEGMENTATION, + NM_ETHTOOL_ID_FEATURE_TX_GRE_SEGMENTATION, + NM_ETHTOOL_ID_FEATURE_TX_GSO_LIST, + NM_ETHTOOL_ID_FEATURE_TX_GSO_PARTIAL, + NM_ETHTOOL_ID_FEATURE_TX_GSO_ROBUST, + NM_ETHTOOL_ID_FEATURE_TX_IPXIP4_SEGMENTATION, + NM_ETHTOOL_ID_FEATURE_TX_IPXIP6_SEGMENTATION, + NM_ETHTOOL_ID_FEATURE_TX_NOCACHE_COPY, + NM_ETHTOOL_ID_FEATURE_TX_SCATTER_GATHER, + NM_ETHTOOL_ID_FEATURE_TX_SCATTER_GATHER_FRAGLIST, + NM_ETHTOOL_ID_FEATURE_TX_SCTP_SEGMENTATION, + NM_ETHTOOL_ID_FEATURE_TX_TCP6_SEGMENTATION, + NM_ETHTOOL_ID_FEATURE_TX_TCP_ECN_SEGMENTATION, + NM_ETHTOOL_ID_FEATURE_TX_TCP_MANGLEID_SEGMENTATION, + NM_ETHTOOL_ID_FEATURE_TX_TCP_SEGMENTATION, + NM_ETHTOOL_ID_FEATURE_TX_TUNNEL_REMCSUM_SEGMENTATION, + NM_ETHTOOL_ID_FEATURE_TX_UDP_SEGMENTATION, + NM_ETHTOOL_ID_FEATURE_TX_UDP_TNL_CSUM_SEGMENTATION, + NM_ETHTOOL_ID_FEATURE_TX_UDP_TNL_SEGMENTATION, + NM_ETHTOOL_ID_FEATURE_TX_VLAN_STAG_HW_INSERT, + _NM_ETHTOOL_ID_FEATURE_LAST = NM_ETHTOOL_ID_FEATURE_TX_VLAN_STAG_HW_INSERT, + + _NM_ETHTOOL_ID_RING_FIRST = _NM_ETHTOOL_ID_FEATURE_LAST + 1, + NM_ETHTOOL_ID_RING_RX = _NM_ETHTOOL_ID_RING_FIRST, + NM_ETHTOOL_ID_RING_RX_JUMBO, + NM_ETHTOOL_ID_RING_RX_MINI, + NM_ETHTOOL_ID_RING_TX, + _NM_ETHTOOL_ID_RING_LAST = NM_ETHTOOL_ID_RING_TX, + + _NM_ETHTOOL_ID_LAST = _NM_ETHTOOL_ID_RING_LAST, + + _NM_ETHTOOL_ID_COALESCE_NUM = + (_NM_ETHTOOL_ID_COALESCE_LAST - _NM_ETHTOOL_ID_COALESCE_FIRST + 1), + _NM_ETHTOOL_ID_FEATURE_NUM = (_NM_ETHTOOL_ID_FEATURE_LAST - _NM_ETHTOOL_ID_FEATURE_FIRST + 1), + _NM_ETHTOOL_ID_RING_NUM = (_NM_ETHTOOL_ID_RING_LAST - _NM_ETHTOOL_ID_RING_FIRST + 1), + _NM_ETHTOOL_ID_NUM = (_NM_ETHTOOL_ID_LAST - _NM_ETHTOOL_ID_FIRST + 1), +} NMEthtoolID; + +#define _NM_ETHTOOL_ID_FEATURE_AS_IDX(ethtool_id) ((ethtool_id) -_NM_ETHTOOL_ID_FEATURE_FIRST) +#define _NM_ETHTOOL_ID_COALESCE_AS_IDX(ethtool_id) ((ethtool_id) -_NM_ETHTOOL_ID_COALESCE_FIRST) + +typedef enum { + NM_ETHTOOL_TYPE_UNKNOWN, + NM_ETHTOOL_TYPE_COALESCE, + NM_ETHTOOL_TYPE_FEATURE, + NM_ETHTOOL_TYPE_RING, +} NMEthtoolType; + +/****************************************************************************/ + +static inline gboolean +nm_ethtool_id_is_feature(NMEthtoolID id) +{ + return id >= _NM_ETHTOOL_ID_FEATURE_FIRST && id <= _NM_ETHTOOL_ID_FEATURE_LAST; +} + +static inline gboolean +nm_ethtool_id_is_coalesce(NMEthtoolID id) +{ + return id >= _NM_ETHTOOL_ID_COALESCE_FIRST && id <= _NM_ETHTOOL_ID_COALESCE_LAST; +} + +static inline gboolean +nm_ethtool_id_is_ring(NMEthtoolID id) +{ + return id >= _NM_ETHTOOL_ID_RING_FIRST && id <= _NM_ETHTOOL_ID_RING_LAST; +} + +/*****************************************************************************/ + +typedef enum { + _NM_SETTING_WIRED_WAKE_ON_LAN_NONE = 0, + _NM_SETTING_WIRED_WAKE_ON_LAN_PHY = 0x2, + _NM_SETTING_WIRED_WAKE_ON_LAN_UNICAST = 0x4, + _NM_SETTING_WIRED_WAKE_ON_LAN_MULTICAST = 0x8, + _NM_SETTING_WIRED_WAKE_ON_LAN_BROADCAST = 0x10, + _NM_SETTING_WIRED_WAKE_ON_LAN_ARP = 0x20, + _NM_SETTING_WIRED_WAKE_ON_LAN_MAGIC = 0x40, + + _NM_SETTING_WIRED_WAKE_ON_LAN_ALL = 0x7E, + + _NM_SETTING_WIRED_WAKE_ON_LAN_DEFAULT = 0x1, + _NM_SETTING_WIRED_WAKE_ON_LAN_IGNORE = 0x8000, + _NM_SETTING_WIRED_WAKE_ON_LAN_EXCLUSIVE_FLAGS = 0x8001, +} _NMSettingWiredWakeOnLan; + +/*****************************************************************************/ + +typedef enum { + /* In priority order; higher number == higher priority */ + + NM_IP_CONFIG_SOURCE_UNKNOWN = 0, + + /* for routes, the source is mapped to the uint8 field rtm_protocol. + * Reserve the range [1,0x100] for native RTPROT values. */ + + NM_IP_CONFIG_SOURCE_RTPROT_UNSPEC = 1 + 0, + NM_IP_CONFIG_SOURCE_RTPROT_REDIRECT = 1 + 1, + NM_IP_CONFIG_SOURCE_RTPROT_KERNEL = 1 + 2, + NM_IP_CONFIG_SOURCE_RTPROT_BOOT = 1 + 3, + NM_IP_CONFIG_SOURCE_RTPROT_STATIC = 1 + 4, + NM_IP_CONFIG_SOURCE_RTPROT_RA = 1 + 9, + NM_IP_CONFIG_SOURCE_RTPROT_DHCP = 1 + 16, + _NM_IP_CONFIG_SOURCE_RTPROT_LAST = 1 + 0xFF, + + NM_IP_CONFIG_SOURCE_KERNEL, + NM_IP_CONFIG_SOURCE_SHARED, + NM_IP_CONFIG_SOURCE_IP4LL, + NM_IP_CONFIG_SOURCE_IP6LL, + NM_IP_CONFIG_SOURCE_PPP, + NM_IP_CONFIG_SOURCE_WWAN, + NM_IP_CONFIG_SOURCE_VPN, + NM_IP_CONFIG_SOURCE_DHCP, + NM_IP_CONFIG_SOURCE_NDISC, + NM_IP_CONFIG_SOURCE_USER, +} NMIPConfigSource; + +static inline gboolean +NM_IS_IP_CONFIG_SOURCE_RTPROT(NMIPConfigSource source) +{ + return source > NM_IP_CONFIG_SOURCE_UNKNOWN && source <= _NM_IP_CONFIG_SOURCE_RTPROT_LAST; +} + +/****************************************************************************/ + +#endif /* __NM_LIBNM_BASE_H__ */ diff --git a/shared/nm-base/nm-ethtool-base.c b/shared/nm-base/nm-ethtool-base.c new file mode 100644 index 00000000..52ff2871 --- /dev/null +++ b/shared/nm-base/nm-ethtool-base.c @@ -0,0 +1,288 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +/* + * Copyright (C) 2018 Red Hat, Inc. + */ + +#include "nm-glib-aux/nm-default-glib-i18n-lib.h" + +#include "nm-ethtool-base.h" + +#include "nm-ethtool-utils-base.h" + +/*****************************************************************************/ + +#define ETHT_DATA(xname) \ + [NM_ETHTOOL_ID_##xname] = (&((const NMEthtoolData){ \ + .optname = NM_ETHTOOL_OPTNAME_##xname, \ + .id = NM_ETHTOOL_ID_##xname, \ + })) + +const NMEthtoolData *const nm_ethtool_data[_NM_ETHTOOL_ID_NUM + 1] = { + /* indexed by NMEthtoolID */ + ETHT_DATA(COALESCE_ADAPTIVE_RX), + ETHT_DATA(COALESCE_ADAPTIVE_TX), + ETHT_DATA(COALESCE_PKT_RATE_HIGH), + ETHT_DATA(COALESCE_PKT_RATE_LOW), + ETHT_DATA(COALESCE_RX_FRAMES), + ETHT_DATA(COALESCE_RX_FRAMES_HIGH), + ETHT_DATA(COALESCE_RX_FRAMES_IRQ), + ETHT_DATA(COALESCE_RX_FRAMES_LOW), + ETHT_DATA(COALESCE_RX_USECS), + ETHT_DATA(COALESCE_RX_USECS_HIGH), + ETHT_DATA(COALESCE_RX_USECS_IRQ), + ETHT_DATA(COALESCE_RX_USECS_LOW), + ETHT_DATA(COALESCE_SAMPLE_INTERVAL), + ETHT_DATA(COALESCE_STATS_BLOCK_USECS), + ETHT_DATA(COALESCE_TX_FRAMES), + ETHT_DATA(COALESCE_TX_FRAMES_HIGH), + ETHT_DATA(COALESCE_TX_FRAMES_IRQ), + ETHT_DATA(COALESCE_TX_FRAMES_LOW), + ETHT_DATA(COALESCE_TX_USECS), + ETHT_DATA(COALESCE_TX_USECS_HIGH), + ETHT_DATA(COALESCE_TX_USECS_IRQ), + ETHT_DATA(COALESCE_TX_USECS_LOW), + ETHT_DATA(FEATURE_ESP_HW_OFFLOAD), + ETHT_DATA(FEATURE_ESP_TX_CSUM_HW_OFFLOAD), + ETHT_DATA(FEATURE_FCOE_MTU), + ETHT_DATA(FEATURE_GRO), + ETHT_DATA(FEATURE_GSO), + ETHT_DATA(FEATURE_HIGHDMA), + ETHT_DATA(FEATURE_HW_TC_OFFLOAD), + ETHT_DATA(FEATURE_L2_FWD_OFFLOAD), + ETHT_DATA(FEATURE_LOOPBACK), + ETHT_DATA(FEATURE_LRO), + ETHT_DATA(FEATURE_MACSEC_HW_OFFLOAD), + ETHT_DATA(FEATURE_NTUPLE), + ETHT_DATA(FEATURE_RX), + ETHT_DATA(FEATURE_RXHASH), + ETHT_DATA(FEATURE_RXVLAN), + ETHT_DATA(FEATURE_RX_ALL), + ETHT_DATA(FEATURE_RX_FCS), + ETHT_DATA(FEATURE_RX_GRO_HW), + ETHT_DATA(FEATURE_RX_GRO_LIST), + ETHT_DATA(FEATURE_RX_UDP_GRO_FORWARDING), + ETHT_DATA(FEATURE_RX_UDP_TUNNEL_PORT_OFFLOAD), + ETHT_DATA(FEATURE_RX_VLAN_FILTER), + ETHT_DATA(FEATURE_RX_VLAN_STAG_FILTER), + ETHT_DATA(FEATURE_RX_VLAN_STAG_HW_PARSE), + ETHT_DATA(FEATURE_SG), + ETHT_DATA(FEATURE_TLS_HW_RECORD), + ETHT_DATA(FEATURE_TLS_HW_RX_OFFLOAD), + ETHT_DATA(FEATURE_TLS_HW_TX_OFFLOAD), + ETHT_DATA(FEATURE_TSO), + ETHT_DATA(FEATURE_TX), + ETHT_DATA(FEATURE_TXVLAN), + ETHT_DATA(FEATURE_TX_CHECKSUM_FCOE_CRC), + ETHT_DATA(FEATURE_TX_CHECKSUM_IPV4), + ETHT_DATA(FEATURE_TX_CHECKSUM_IPV6), + ETHT_DATA(FEATURE_TX_CHECKSUM_IP_GENERIC), + ETHT_DATA(FEATURE_TX_CHECKSUM_SCTP), + ETHT_DATA(FEATURE_TX_ESP_SEGMENTATION), + ETHT_DATA(FEATURE_TX_FCOE_SEGMENTATION), + ETHT_DATA(FEATURE_TX_GRE_CSUM_SEGMENTATION), + ETHT_DATA(FEATURE_TX_GRE_SEGMENTATION), + ETHT_DATA(FEATURE_TX_GSO_LIST), + ETHT_DATA(FEATURE_TX_GSO_PARTIAL), + ETHT_DATA(FEATURE_TX_GSO_ROBUST), + ETHT_DATA(FEATURE_TX_IPXIP4_SEGMENTATION), + ETHT_DATA(FEATURE_TX_IPXIP6_SEGMENTATION), + ETHT_DATA(FEATURE_TX_NOCACHE_COPY), + ETHT_DATA(FEATURE_TX_SCATTER_GATHER), + ETHT_DATA(FEATURE_TX_SCATTER_GATHER_FRAGLIST), + ETHT_DATA(FEATURE_TX_SCTP_SEGMENTATION), + ETHT_DATA(FEATURE_TX_TCP6_SEGMENTATION), + ETHT_DATA(FEATURE_TX_TCP_ECN_SEGMENTATION), + ETHT_DATA(FEATURE_TX_TCP_MANGLEID_SEGMENTATION), + ETHT_DATA(FEATURE_TX_TCP_SEGMENTATION), + ETHT_DATA(FEATURE_TX_TUNNEL_REMCSUM_SEGMENTATION), + ETHT_DATA(FEATURE_TX_UDP_SEGMENTATION), + ETHT_DATA(FEATURE_TX_UDP_TNL_CSUM_SEGMENTATION), + ETHT_DATA(FEATURE_TX_UDP_TNL_SEGMENTATION), + ETHT_DATA(FEATURE_TX_VLAN_STAG_HW_INSERT), + ETHT_DATA(RING_RX), + ETHT_DATA(RING_RX_JUMBO), + ETHT_DATA(RING_RX_MINI), + ETHT_DATA(RING_TX), + [_NM_ETHTOOL_ID_NUM] = NULL, +}; + +static const guint8 _by_name[_NM_ETHTOOL_ID_NUM] = { + /* sorted by optname. */ + NM_ETHTOOL_ID_COALESCE_ADAPTIVE_RX, + NM_ETHTOOL_ID_COALESCE_ADAPTIVE_TX, + NM_ETHTOOL_ID_COALESCE_PKT_RATE_HIGH, + NM_ETHTOOL_ID_COALESCE_PKT_RATE_LOW, + NM_ETHTOOL_ID_COALESCE_RX_FRAMES, + NM_ETHTOOL_ID_COALESCE_RX_FRAMES_HIGH, + NM_ETHTOOL_ID_COALESCE_RX_FRAMES_IRQ, + NM_ETHTOOL_ID_COALESCE_RX_FRAMES_LOW, + NM_ETHTOOL_ID_COALESCE_RX_USECS, + NM_ETHTOOL_ID_COALESCE_RX_USECS_HIGH, + NM_ETHTOOL_ID_COALESCE_RX_USECS_IRQ, + NM_ETHTOOL_ID_COALESCE_RX_USECS_LOW, + NM_ETHTOOL_ID_COALESCE_SAMPLE_INTERVAL, + NM_ETHTOOL_ID_COALESCE_STATS_BLOCK_USECS, + NM_ETHTOOL_ID_COALESCE_TX_FRAMES, + NM_ETHTOOL_ID_COALESCE_TX_FRAMES_HIGH, + NM_ETHTOOL_ID_COALESCE_TX_FRAMES_IRQ, + NM_ETHTOOL_ID_COALESCE_TX_FRAMES_LOW, + NM_ETHTOOL_ID_COALESCE_TX_USECS, + NM_ETHTOOL_ID_COALESCE_TX_USECS_HIGH, + NM_ETHTOOL_ID_COALESCE_TX_USECS_IRQ, + NM_ETHTOOL_ID_COALESCE_TX_USECS_LOW, + NM_ETHTOOL_ID_FEATURE_ESP_HW_OFFLOAD, + NM_ETHTOOL_ID_FEATURE_ESP_TX_CSUM_HW_OFFLOAD, + NM_ETHTOOL_ID_FEATURE_FCOE_MTU, + NM_ETHTOOL_ID_FEATURE_GRO, + NM_ETHTOOL_ID_FEATURE_GSO, + NM_ETHTOOL_ID_FEATURE_HIGHDMA, + NM_ETHTOOL_ID_FEATURE_HW_TC_OFFLOAD, + NM_ETHTOOL_ID_FEATURE_L2_FWD_OFFLOAD, + NM_ETHTOOL_ID_FEATURE_LOOPBACK, + NM_ETHTOOL_ID_FEATURE_LRO, + NM_ETHTOOL_ID_FEATURE_MACSEC_HW_OFFLOAD, + NM_ETHTOOL_ID_FEATURE_NTUPLE, + NM_ETHTOOL_ID_FEATURE_RX, + NM_ETHTOOL_ID_FEATURE_RX_ALL, + NM_ETHTOOL_ID_FEATURE_RX_FCS, + NM_ETHTOOL_ID_FEATURE_RX_GRO_HW, + NM_ETHTOOL_ID_FEATURE_RX_GRO_LIST, + NM_ETHTOOL_ID_FEATURE_RX_UDP_GRO_FORWARDING, + NM_ETHTOOL_ID_FEATURE_RX_UDP_TUNNEL_PORT_OFFLOAD, + NM_ETHTOOL_ID_FEATURE_RX_VLAN_FILTER, + NM_ETHTOOL_ID_FEATURE_RX_VLAN_STAG_FILTER, + NM_ETHTOOL_ID_FEATURE_RX_VLAN_STAG_HW_PARSE, + NM_ETHTOOL_ID_FEATURE_RXHASH, + NM_ETHTOOL_ID_FEATURE_RXVLAN, + NM_ETHTOOL_ID_FEATURE_SG, + NM_ETHTOOL_ID_FEATURE_TLS_HW_RECORD, + NM_ETHTOOL_ID_FEATURE_TLS_HW_RX_OFFLOAD, + NM_ETHTOOL_ID_FEATURE_TLS_HW_TX_OFFLOAD, + NM_ETHTOOL_ID_FEATURE_TSO, + NM_ETHTOOL_ID_FEATURE_TX, + NM_ETHTOOL_ID_FEATURE_TX_CHECKSUM_FCOE_CRC, + NM_ETHTOOL_ID_FEATURE_TX_CHECKSUM_IP_GENERIC, + NM_ETHTOOL_ID_FEATURE_TX_CHECKSUM_IPV4, + NM_ETHTOOL_ID_FEATURE_TX_CHECKSUM_IPV6, + NM_ETHTOOL_ID_FEATURE_TX_CHECKSUM_SCTP, + NM_ETHTOOL_ID_FEATURE_TX_ESP_SEGMENTATION, + NM_ETHTOOL_ID_FEATURE_TX_FCOE_SEGMENTATION, + NM_ETHTOOL_ID_FEATURE_TX_GRE_CSUM_SEGMENTATION, + NM_ETHTOOL_ID_FEATURE_TX_GRE_SEGMENTATION, + NM_ETHTOOL_ID_FEATURE_TX_GSO_LIST, + NM_ETHTOOL_ID_FEATURE_TX_GSO_PARTIAL, + NM_ETHTOOL_ID_FEATURE_TX_GSO_ROBUST, + NM_ETHTOOL_ID_FEATURE_TX_IPXIP4_SEGMENTATION, + NM_ETHTOOL_ID_FEATURE_TX_IPXIP6_SEGMENTATION, + NM_ETHTOOL_ID_FEATURE_TX_NOCACHE_COPY, + NM_ETHTOOL_ID_FEATURE_TX_SCATTER_GATHER, + NM_ETHTOOL_ID_FEATURE_TX_SCATTER_GATHER_FRAGLIST, + NM_ETHTOOL_ID_FEATURE_TX_SCTP_SEGMENTATION, + NM_ETHTOOL_ID_FEATURE_TX_TCP_ECN_SEGMENTATION, + NM_ETHTOOL_ID_FEATURE_TX_TCP_MANGLEID_SEGMENTATION, + NM_ETHTOOL_ID_FEATURE_TX_TCP_SEGMENTATION, + NM_ETHTOOL_ID_FEATURE_TX_TCP6_SEGMENTATION, + NM_ETHTOOL_ID_FEATURE_TX_TUNNEL_REMCSUM_SEGMENTATION, + NM_ETHTOOL_ID_FEATURE_TX_UDP_SEGMENTATION, + NM_ETHTOOL_ID_FEATURE_TX_UDP_TNL_CSUM_SEGMENTATION, + NM_ETHTOOL_ID_FEATURE_TX_UDP_TNL_SEGMENTATION, + NM_ETHTOOL_ID_FEATURE_TX_VLAN_STAG_HW_INSERT, + NM_ETHTOOL_ID_FEATURE_TXVLAN, + NM_ETHTOOL_ID_RING_RX, + NM_ETHTOOL_ID_RING_RX_JUMBO, + NM_ETHTOOL_ID_RING_RX_MINI, + NM_ETHTOOL_ID_RING_TX, +}; + +/*****************************************************************************/ + +static void +_ASSERT_data(void) +{ + int i; + + if (!NM_MORE_ASSERT_ONCE(10)) + return; + + G_STATIC_ASSERT_EXPR(_NM_ETHTOOL_ID_FIRST == 0); + G_STATIC_ASSERT_EXPR(_NM_ETHTOOL_ID_LAST == _NM_ETHTOOL_ID_NUM - 1); + G_STATIC_ASSERT_EXPR(_NM_ETHTOOL_ID_NUM > 0); + + nm_assert(NM_PTRARRAY_LEN(nm_ethtool_data) == _NM_ETHTOOL_ID_NUM); + nm_assert(G_N_ELEMENTS(_by_name) == _NM_ETHTOOL_ID_NUM); + nm_assert(G_N_ELEMENTS(nm_ethtool_data) == _NM_ETHTOOL_ID_NUM + 1); + + for (i = 0; i < _NM_ETHTOOL_ID_NUM; i++) { + const NMEthtoolData *d = nm_ethtool_data[i]; + + nm_assert(d); + nm_assert(d->id == (NMEthtoolID) i); + nm_assert(d->optname && d->optname[0]); + } + + for (i = 0; i < _NM_ETHTOOL_ID_NUM; i++) { + NMEthtoolID id = _by_name[i]; + const NMEthtoolData *d; + + nm_assert(id >= 0); + nm_assert(id < _NM_ETHTOOL_ID_NUM); + + d = nm_ethtool_data[id]; + if (i > 0) { + /* since we assert that all optnames are sorted strictly monotonically increasing, + * it also follows that there are no duplicates in the _by_name. + * It also follows, that all names in nm_ethtool_data are unique. */ + if (strcmp(nm_ethtool_data[_by_name[i - 1]]->optname, d->optname) >= 0) { + g_error("nm_ethtool_data is not sorted asciibetically: %u/%s should be after %u/%s", + i - 1, + nm_ethtool_data[_by_name[i - 1]]->optname, + i, + d->optname); + } + } + } +} + +static int +_by_name_cmp(gconstpointer a, gconstpointer b, gpointer user_data) +{ + const guint8 *p_id = a; + const char * optname = b; + + nm_assert(p_id && p_id >= _by_name && p_id <= &_by_name[_NM_ETHTOOL_ID_NUM]); + nm_assert(*p_id < _NM_ETHTOOL_ID_NUM); + + return strcmp(nm_ethtool_data[*p_id]->optname, optname); +} + +const NMEthtoolData * +nm_ethtool_data_get_by_optname(const char *optname) +{ + gssize idx; + + if (!optname) + return NULL; + + _ASSERT_data(); + + idx = nm_utils_array_find_binary_search((gconstpointer *) _by_name, + sizeof(_by_name[0]), + _NM_ETHTOOL_ID_NUM, + optname, + _by_name_cmp, + NULL); + return (idx < 0) ? NULL : nm_ethtool_data[_by_name[idx]]; +} + +NMEthtoolType +nm_ethtool_id_to_type(NMEthtoolID id) +{ + if (nm_ethtool_id_is_coalesce(id)) + return NM_ETHTOOL_TYPE_COALESCE; + if (nm_ethtool_id_is_feature(id)) + return NM_ETHTOOL_TYPE_FEATURE; + if (nm_ethtool_id_is_ring(id)) + return NM_ETHTOOL_TYPE_RING; + + return NM_ETHTOOL_TYPE_UNKNOWN; +} diff --git a/shared/nm-base/nm-ethtool-base.h b/shared/nm-base/nm-ethtool-base.h new file mode 100644 index 00000000..be90ce75 --- /dev/null +++ b/shared/nm-base/nm-ethtool-base.h @@ -0,0 +1,37 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +/* + * Copyright (C) 2018 Red Hat, Inc. + */ + +#ifndef __NM_ETHTOOL_BASE_H__ +#define __NM_ETHTOOL_BASE_H__ + +#include "nm-base/nm-base.h" + +/*****************************************************************************/ + +typedef struct { + const char *optname; + NMEthtoolID id; +} NMEthtoolData; + +extern const NMEthtoolData *const nm_ethtool_data[_NM_ETHTOOL_ID_NUM + 1]; + +const NMEthtoolData *nm_ethtool_data_get_by_optname(const char *optname); + +NMEthtoolType nm_ethtool_id_to_type(NMEthtoolID id); + +/****************************************************************************/ + +static inline NMEthtoolID +nm_ethtool_id_get_by_name(const char *optname) +{ + const NMEthtoolData *d; + + d = nm_ethtool_data_get_by_optname(optname); + return d ? d->id : NM_ETHTOOL_ID_UNKNOWN; +} + +/****************************************************************************/ + +#endif /* __NM_ETHTOOL_BASE_H__ */ diff --git a/shared/nm-base/nm-ethtool-utils-base.h b/shared/nm-base/nm-ethtool-utils-base.h new file mode 100644 index 00000000..d422724b --- /dev/null +++ b/shared/nm-base/nm-ethtool-utils-base.h @@ -0,0 +1,107 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +/* + * Copyright (C) 2018 Red Hat, Inc. + */ + +#ifndef __NM_ETHTOOL_UTILS_H__ +#define __NM_ETHTOOL_UTILS_H__ + +G_BEGIN_DECLS + +/*****************************************************************************/ + +#define NM_ETHTOOL_OPTNAME_FEATURE_ESP_HW_OFFLOAD "feature-esp-hw-offload" +#define NM_ETHTOOL_OPTNAME_FEATURE_ESP_TX_CSUM_HW_OFFLOAD "feature-esp-tx-csum-hw-offload" +#define NM_ETHTOOL_OPTNAME_FEATURE_FCOE_MTU "feature-fcoe-mtu" +#define NM_ETHTOOL_OPTNAME_FEATURE_GRO "feature-gro" +#define NM_ETHTOOL_OPTNAME_FEATURE_GSO "feature-gso" +#define NM_ETHTOOL_OPTNAME_FEATURE_HIGHDMA "feature-highdma" +#define NM_ETHTOOL_OPTNAME_FEATURE_HW_TC_OFFLOAD "feature-hw-tc-offload" +#define NM_ETHTOOL_OPTNAME_FEATURE_L2_FWD_OFFLOAD "feature-l2-fwd-offload" +#define NM_ETHTOOL_OPTNAME_FEATURE_LOOPBACK "feature-loopback" +#define NM_ETHTOOL_OPTNAME_FEATURE_LRO "feature-lro" +#define NM_ETHTOOL_OPTNAME_FEATURE_MACSEC_HW_OFFLOAD "feature-macsec-hw-offload" +#define NM_ETHTOOL_OPTNAME_FEATURE_NTUPLE "feature-ntuple" +#define NM_ETHTOOL_OPTNAME_FEATURE_RX "feature-rx" +#define NM_ETHTOOL_OPTNAME_FEATURE_RXHASH "feature-rxhash" +#define NM_ETHTOOL_OPTNAME_FEATURE_RXVLAN "feature-rxvlan" +#define NM_ETHTOOL_OPTNAME_FEATURE_RX_ALL "feature-rx-all" +#define NM_ETHTOOL_OPTNAME_FEATURE_RX_FCS "feature-rx-fcs" +#define NM_ETHTOOL_OPTNAME_FEATURE_RX_GRO_HW "feature-rx-gro-hw" +#define NM_ETHTOOL_OPTNAME_FEATURE_RX_GRO_LIST "feature-rx-gro-list" +#define NM_ETHTOOL_OPTNAME_FEATURE_RX_UDP_GRO_FORWARDING "feature-rx-udp-gro-forwarding" +#define NM_ETHTOOL_OPTNAME_FEATURE_RX_UDP_TUNNEL_PORT_OFFLOAD "feature-rx-udp_tunnel-port-offload" +#define NM_ETHTOOL_OPTNAME_FEATURE_RX_VLAN_FILTER "feature-rx-vlan-filter" +#define NM_ETHTOOL_OPTNAME_FEATURE_RX_VLAN_STAG_FILTER "feature-rx-vlan-stag-filter" +#define NM_ETHTOOL_OPTNAME_FEATURE_RX_VLAN_STAG_HW_PARSE "feature-rx-vlan-stag-hw-parse" +#define NM_ETHTOOL_OPTNAME_FEATURE_SG "feature-sg" +#define NM_ETHTOOL_OPTNAME_FEATURE_TLS_HW_RECORD "feature-tls-hw-record" +#define NM_ETHTOOL_OPTNAME_FEATURE_TLS_HW_RX_OFFLOAD "feature-tls-hw-rx-offload" +#define NM_ETHTOOL_OPTNAME_FEATURE_TLS_HW_TX_OFFLOAD "feature-tls-hw-tx-offload" +#define NM_ETHTOOL_OPTNAME_FEATURE_TSO "feature-tso" +#define NM_ETHTOOL_OPTNAME_FEATURE_TX "feature-tx" +#define NM_ETHTOOL_OPTNAME_FEATURE_TXVLAN "feature-txvlan" +#define NM_ETHTOOL_OPTNAME_FEATURE_TX_CHECKSUM_FCOE_CRC "feature-tx-checksum-fcoe-crc" +#define NM_ETHTOOL_OPTNAME_FEATURE_TX_CHECKSUM_IPV4 "feature-tx-checksum-ipv4" +#define NM_ETHTOOL_OPTNAME_FEATURE_TX_CHECKSUM_IPV6 "feature-tx-checksum-ipv6" +#define NM_ETHTOOL_OPTNAME_FEATURE_TX_CHECKSUM_IP_GENERIC "feature-tx-checksum-ip-generic" +#define NM_ETHTOOL_OPTNAME_FEATURE_TX_CHECKSUM_SCTP "feature-tx-checksum-sctp" +#define NM_ETHTOOL_OPTNAME_FEATURE_TX_ESP_SEGMENTATION "feature-tx-esp-segmentation" +#define NM_ETHTOOL_OPTNAME_FEATURE_TX_FCOE_SEGMENTATION "feature-tx-fcoe-segmentation" +#define NM_ETHTOOL_OPTNAME_FEATURE_TX_GRE_CSUM_SEGMENTATION "feature-tx-gre-csum-segmentation" +#define NM_ETHTOOL_OPTNAME_FEATURE_TX_GRE_SEGMENTATION "feature-tx-gre-segmentation" +#define NM_ETHTOOL_OPTNAME_FEATURE_TX_GSO_LIST "feature-tx-gso-list" +#define NM_ETHTOOL_OPTNAME_FEATURE_TX_GSO_PARTIAL "feature-tx-gso-partial" +#define NM_ETHTOOL_OPTNAME_FEATURE_TX_GSO_ROBUST "feature-tx-gso-robust" +#define NM_ETHTOOL_OPTNAME_FEATURE_TX_IPXIP4_SEGMENTATION "feature-tx-ipxip4-segmentation" +#define NM_ETHTOOL_OPTNAME_FEATURE_TX_IPXIP6_SEGMENTATION "feature-tx-ipxip6-segmentation" +#define NM_ETHTOOL_OPTNAME_FEATURE_TX_NOCACHE_COPY "feature-tx-nocache-copy" +#define NM_ETHTOOL_OPTNAME_FEATURE_TX_SCATTER_GATHER "feature-tx-scatter-gather" +#define NM_ETHTOOL_OPTNAME_FEATURE_TX_SCATTER_GATHER_FRAGLIST "feature-tx-scatter-gather-fraglist" +#define NM_ETHTOOL_OPTNAME_FEATURE_TX_SCTP_SEGMENTATION "feature-tx-sctp-segmentation" +#define NM_ETHTOOL_OPTNAME_FEATURE_TX_TCP6_SEGMENTATION "feature-tx-tcp6-segmentation" +#define NM_ETHTOOL_OPTNAME_FEATURE_TX_TCP_ECN_SEGMENTATION "feature-tx-tcp-ecn-segmentation" +#define NM_ETHTOOL_OPTNAME_FEATURE_TX_TCP_MANGLEID_SEGMENTATION \ + "feature-tx-tcp-mangleid-segmentation" +#define NM_ETHTOOL_OPTNAME_FEATURE_TX_TCP_SEGMENTATION "feature-tx-tcp-segmentation" +#define NM_ETHTOOL_OPTNAME_FEATURE_TX_TUNNEL_REMCSUM_SEGMENTATION \ + "feature-tx-tunnel-remcsum-segmentation" +#define NM_ETHTOOL_OPTNAME_FEATURE_TX_UDP_SEGMENTATION "feature-tx-udp-segmentation" +#define NM_ETHTOOL_OPTNAME_FEATURE_TX_UDP_TNL_CSUM_SEGMENTATION \ + "feature-tx-udp_tnl-csum-segmentation" +#define NM_ETHTOOL_OPTNAME_FEATURE_TX_UDP_TNL_SEGMENTATION "feature-tx-udp_tnl-segmentation" +#define NM_ETHTOOL_OPTNAME_FEATURE_TX_VLAN_STAG_HW_INSERT "feature-tx-vlan-stag-hw-insert" + +#define NM_ETHTOOL_OPTNAME_COALESCE_ADAPTIVE_RX "coalesce-adaptive-rx" +#define NM_ETHTOOL_OPTNAME_COALESCE_ADAPTIVE_TX "coalesce-adaptive-tx" +#define NM_ETHTOOL_OPTNAME_COALESCE_PKT_RATE_HIGH "coalesce-pkt-rate-high" +#define NM_ETHTOOL_OPTNAME_COALESCE_PKT_RATE_LOW "coalesce-pkt-rate-low" +#define NM_ETHTOOL_OPTNAME_COALESCE_RX_FRAMES "coalesce-rx-frames" +#define NM_ETHTOOL_OPTNAME_COALESCE_RX_FRAMES_HIGH "coalesce-rx-frames-high" +#define NM_ETHTOOL_OPTNAME_COALESCE_RX_FRAMES_IRQ "coalesce-rx-frames-irq" +#define NM_ETHTOOL_OPTNAME_COALESCE_RX_FRAMES_LOW "coalesce-rx-frames-low" +#define NM_ETHTOOL_OPTNAME_COALESCE_RX_USECS "coalesce-rx-usecs" +#define NM_ETHTOOL_OPTNAME_COALESCE_RX_USECS_HIGH "coalesce-rx-usecs-high" +#define NM_ETHTOOL_OPTNAME_COALESCE_RX_USECS_IRQ "coalesce-rx-usecs-irq" +#define NM_ETHTOOL_OPTNAME_COALESCE_RX_USECS_LOW "coalesce-rx-usecs-low" +#define NM_ETHTOOL_OPTNAME_COALESCE_SAMPLE_INTERVAL "coalesce-sample-interval" +#define NM_ETHTOOL_OPTNAME_COALESCE_STATS_BLOCK_USECS "coalesce-stats-block-usecs" +#define NM_ETHTOOL_OPTNAME_COALESCE_TX_FRAMES "coalesce-tx-frames" +#define NM_ETHTOOL_OPTNAME_COALESCE_TX_FRAMES_HIGH "coalesce-tx-frames-high" +#define NM_ETHTOOL_OPTNAME_COALESCE_TX_FRAMES_IRQ "coalesce-tx-frames-irq" +#define NM_ETHTOOL_OPTNAME_COALESCE_TX_FRAMES_LOW "coalesce-tx-frames-low" +#define NM_ETHTOOL_OPTNAME_COALESCE_TX_USECS "coalesce-tx-usecs" +#define NM_ETHTOOL_OPTNAME_COALESCE_TX_USECS_HIGH "coalesce-tx-usecs-high" +#define NM_ETHTOOL_OPTNAME_COALESCE_TX_USECS_IRQ "coalesce-tx-usecs-irq" +#define NM_ETHTOOL_OPTNAME_COALESCE_TX_USECS_LOW "coalesce-tx-usecs-low" + +#define NM_ETHTOOL_OPTNAME_RING_RX "ring-rx" +#define NM_ETHTOOL_OPTNAME_RING_RX_JUMBO "ring-rx-jumbo" +#define NM_ETHTOOL_OPTNAME_RING_RX_MINI "ring-rx-mini" +#define NM_ETHTOOL_OPTNAME_RING_TX "ring-tx" + +/*****************************************************************************/ + +G_END_DECLS + +#endif /* __NM_ETHTOOL_UTILS_H__ */ diff --git a/shared/nm-default.h b/shared/nm-default.h deleted file mode 100644 index b322f1d3..00000000 --- a/shared/nm-default.h +++ /dev/null @@ -1,302 +0,0 @@ -/* SPDX-License-Identifier: LGPL-2.1+ */ -/* - * Copyright (C) 2015 Red Hat, Inc. - */ - -/* With autotools (and also meson), all our source files are expected to include <config.h>. - * as very first header. Then they are expected to include "config-extra.h" and a small set - * of headers that we always want to have included (depending on what sources we compile - * (as determined by NETWORKMANAGER_COMPILATION define) that can be "nm-glib-aux/nm-macros-internal.h" - * and similar. - * - * To simplify that, all our source files are only expected to include "nm-default.h" as first, - * and thereby rely on getting a basic set of headers already. - */ - -#ifndef __NM_DEFAULT_H__ -#define __NM_DEFAULT_H__ - -#define NM_NETWORKMANAGER_COMPILATION_WITH_GLIB (1 << 0) -#define NM_NETWORKMANAGER_COMPILATION_WITH_GLIB_I18N_LIB (1 << 1) -#define NM_NETWORKMANAGER_COMPILATION_WITH_GLIB_I18N_PROG (1 << 2) -#define NM_NETWORKMANAGER_COMPILATION_WITH_LIBNM (1 << 3) -#define NM_NETWORKMANAGER_COMPILATION_WITH_LIBNM_PRIVATE (1 << 4) -#define NM_NETWORKMANAGER_COMPILATION_WITH_LIBNM_CORE (1 << 5) -#define NM_NETWORKMANAGER_COMPILATION_WITH_LIBNM_CORE_INTERNAL (1 << 6) -#define NM_NETWORKMANAGER_COMPILATION_WITH_LIBNM_CORE_PRIVATE (1 << 7) -#define NM_NETWORKMANAGER_COMPILATION_WITH_DAEMON (1 << 10) -#define NM_NETWORKMANAGER_COMPILATION_WITH_SYSTEMD (1 << 11) - -#define NM_NETWORKMANAGER_COMPILATION_LIBNM_CORE \ - (0 | NM_NETWORKMANAGER_COMPILATION_WITH_GLIB \ - | NM_NETWORKMANAGER_COMPILATION_WITH_GLIB_I18N_LIB \ - | NM_NETWORKMANAGER_COMPILATION_WITH_LIBNM_CORE \ - | NM_NETWORKMANAGER_COMPILATION_WITH_LIBNM_CORE_PRIVATE \ - | NM_NETWORKMANAGER_COMPILATION_WITH_LIBNM_CORE_INTERNAL) - -#define NM_NETWORKMANAGER_COMPILATION_LIBNM \ - (0 | NM_NETWORKMANAGER_COMPILATION_WITH_GLIB \ - | NM_NETWORKMANAGER_COMPILATION_WITH_GLIB_I18N_LIB | NM_NETWORKMANAGER_COMPILATION_WITH_LIBNM \ - | NM_NETWORKMANAGER_COMPILATION_WITH_LIBNM_PRIVATE \ - | NM_NETWORKMANAGER_COMPILATION_WITH_LIBNM_CORE \ - | NM_NETWORKMANAGER_COMPILATION_WITH_LIBNM_CORE_INTERNAL) - -#define NM_NETWORKMANAGER_COMPILATION_CLIENT \ - (0 | NM_NETWORKMANAGER_COMPILATION_WITH_GLIB \ - | NM_NETWORKMANAGER_COMPILATION_WITH_GLIB_I18N_PROG \ - | NM_NETWORKMANAGER_COMPILATION_WITH_LIBNM | NM_NETWORKMANAGER_COMPILATION_WITH_LIBNM_CORE) - -#define NM_NETWORKMANAGER_COMPILATION_DAEMON \ - (0 | NM_NETWORKMANAGER_COMPILATION_WITH_GLIB \ - | NM_NETWORKMANAGER_COMPILATION_WITH_GLIB_I18N_PROG \ - | NM_NETWORKMANAGER_COMPILATION_WITH_LIBNM_CORE \ - | NM_NETWORKMANAGER_COMPILATION_WITH_LIBNM_CORE_INTERNAL \ - | NM_NETWORKMANAGER_COMPILATION_WITH_DAEMON) - -#define NM_NETWORKMANAGER_COMPILATION_SYSTEMD_SHARED \ - (0 | NM_NETWORKMANAGER_COMPILATION_WITH_GLIB | NM_NETWORKMANAGER_COMPILATION_WITH_SYSTEMD) - -#define NM_NETWORKMANAGER_COMPILATION_SYSTEMD \ - (0 | NM_NETWORKMANAGER_COMPILATION_DAEMON | NM_NETWORKMANAGER_COMPILATION_SYSTEMD_SHARED) - -#define NM_NETWORKMANAGER_COMPILATION_GLIB (0 | NM_NETWORKMANAGER_COMPILATION_WITH_GLIB) - -#ifndef NETWORKMANAGER_COMPILATION - #error Define NETWORKMANAGER_COMPILATION accordingly -#endif - -#ifndef G_LOG_DOMAIN - #if defined(NETWORKMANAGER_COMPILATION_TEST) - #define G_LOG_DOMAIN "test" - #elif NETWORKMANAGER_COMPILATION & NM_NETWORKMANAGER_COMPILATION_WITH_DAEMON - #define G_LOG_DOMAIN "NetworkManager" - #else - #error Need to define G_LOG_DOMAIN - #endif -#endif - -/*****************************************************************************/ - -/* always include these headers for our internal source files. */ - -#ifndef ___CONFIG_H__ - #define ___CONFIG_H__ - #include <config.h> -#endif - -#include "config-extra.h" - -/* for internal compilation we don't want the deprecation macros - * to be in effect. Define the widest range of versions to effectively - * disable deprecation checks */ -#define NM_VERSION_MIN_REQUIRED NM_VERSION_0_9_8 - -#ifndef NM_MORE_ASSERTS - #define NM_MORE_ASSERTS 0 -#endif - -#if NM_MORE_ASSERTS == 0 - /* The cast macros like NM_TYPE() are implemented via G_TYPE_CHECK_INSTANCE_CAST() - * and _G_TYPE_CIC(). The latter, by default performs runtime checks of the type - * by calling g_type_check_instance_cast(). - * This check has a certain overhead without being helpful. - * - * Example 1: - * static void foo (NMType *obj) - * { - * access_obj_without_check (obj); - * } - * foo ((NMType *) obj); - * // There is no runtime check and passing an invalid pointer - * // leads to a crash. - * - * Example 2: - * static void foo (NMType *obj) - * { - * access_obj_without_check (obj); - * } - * foo (NM_TYPE (obj)); - * // There is a runtime check which prints a g_warning(), but that doesn't - * // avoid the crash as NM_TYPE() cannot do anything then passing on the - * // invalid pointer. - * - * Example 3: - * static void foo (NMType *obj) - * { - * g_return_if_fail (NM_IS_TYPE (obj)); - * access_obj_without_check (obj); - * } - * foo ((NMType *) obj); - * // There is a runtime check which prints a g_critical() which also avoids - * // the crash. That is actually helpful to catch bugs and avoid crashes. - * - * Example 4: - * static void foo (NMType *obj) - * { - * g_return_if_fail (NM_IS_TYPE (obj)); - * access_obj_without_check (obj); - * } - * foo (NM_TYPE (obj)); - * // The runtime check is performed twice, with printing a g_warning() and - * // a g_critical() and avoiding the crash. - * - * Example 3 is how it should be done. Type checks in NM_TYPE() are pointless. - * Disable them for our production builds. - */ - #ifndef G_DISABLE_CAST_CHECKS - #define G_DISABLE_CAST_CHECKS - #endif -#endif - -#if NM_MORE_ASSERTS == 0 - #ifndef G_DISABLE_CAST_CHECKS - /* Unless compiling with G_DISABLE_CAST_CHECKS, glib performs type checking - * during G_VARIANT_TYPE() via g_variant_type_checked_(). This is not necessary - * because commonly this cast is needed during something like - * - * g_variant_builder_init (&props, G_VARIANT_TYPE ("a{sv}")); - * - * Note that in if the variant type would be invalid, the check still - * wouldn't make the buggy code magically work. Instead of passing a - * bogus type string (bad), it would pass %NULL to g_variant_builder_init() - * (also bad). - * - * Also, a function like g_variant_builder_init() already validates - * the input type via something like - * - * g_return_if_fail (g_variant_type_is_container (type)); - * - * So, by having G_VARIANT_TYPE() also validate the type, we validate - * twice, whereas the first validation is rather pointless because it - * doesn't prevent the function to be called with invalid arguments. - * - * Just patch G_VARIANT_TYPE() to perform no check. - */ - #undef G_VARIANT_TYPE - #define G_VARIANT_TYPE(type_string) ((const GVariantType *) (type_string)) - #endif -#endif - -#include <stdlib.h> - -/*****************************************************************************/ - -#if (NETWORKMANAGER_COMPILATION) & NM_NETWORKMANAGER_COMPILATION_WITH_GLIB - - #include <glib.h> - - #if (NETWORKMANAGER_COMPILATION) & NM_NETWORKMANAGER_COMPILATION_WITH_GLIB_I18N_PROG - #if (NETWORKMANAGER_COMPILATION) & NM_NETWORKMANAGER_COMPILATION_WITH_GLIB_I18N_LIB - #error Cannot define NM_NETWORKMANAGER_COMPILATION_WITH_GLIB_I18N_PROG and NM_NETWORKMANAGER_COMPILATION_WITH_GLIB_I18N_LIB - #endif - #include <glib/gi18n.h> - #elif (NETWORKMANAGER_COMPILATION) & NM_NETWORKMANAGER_COMPILATION_WITH_GLIB_I18N_LIB - #include <glib/gi18n-lib.h> - #endif - - /*****************************************************************************/ - - #if NM_MORE_ASSERTS == 0 - -/* glib assertions (g_return_*(), g_assert*()) contain a textual representation - * of the checked statement. This part of the assertion blows up the size of the - * binary. Unless we compile a debug-build with NM_MORE_ASSERTS, drop these - * parts. Note that the failed assertion still prints the file and line where the - * assertion fails. That shall suffice. */ - -static inline void -_nm_g_return_if_fail_warning(const char *log_domain, const char *file, int line) -{ - char file_buf[256 + 15]; - - g_snprintf(file_buf, sizeof(file_buf), "((%s:%d))", file, line); - g_return_if_fail_warning(log_domain, file_buf, "<dropped>"); -} - - #define g_return_if_fail_warning(log_domain, pretty_function, expression) \ - _nm_g_return_if_fail_warning(log_domain, __FILE__, __LINE__) - - #define g_assertion_message_expr(domain, file, line, func, expr) \ - g_assertion_message_expr(domain, \ - file, \ - line, \ - "<unknown-fcn>", \ - (expr) ? "<dropped>" : NULL) - - #undef g_return_val_if_reached - #define g_return_val_if_reached(val) \ - G_STMT_START \ - { \ - g_log(G_LOG_DOMAIN, \ - G_LOG_LEVEL_CRITICAL, \ - "file %s: line %d (%s): should not be reached", \ - __FILE__, \ - __LINE__, \ - "<dropped>"); \ - return (val); \ - } \ - G_STMT_END - - #undef g_return_if_reached - #define g_return_if_reached() \ - G_STMT_START \ - { \ - g_log(G_LOG_DOMAIN, \ - G_LOG_LEVEL_CRITICAL, \ - "file %s: line %d (%s): should not be reached", \ - __FILE__, \ - __LINE__, \ - "<dropped>"); \ - return; \ - } \ - G_STMT_END - - #define NM_ASSERT_G_RETURN_EXPR(expr) "<dropped>" - #define NM_ASSERT_NO_MSG 1 - - #else - - #define NM_ASSERT_G_RETURN_EXPR(expr) "" expr "" - #define NM_ASSERT_NO_MSG 0 - - #endif - - /*****************************************************************************/ - - #include "nm-std-aux/nm-std-aux.h" - #include "nm-std-aux/nm-std-utils.h" - #include "nm-glib-aux/nm-macros-internal.h" - #include "nm-glib-aux/nm-shared-utils.h" - #include "nm-glib-aux/nm-errno.h" - #include "nm-glib-aux/nm-hash-utils.h" - - /*****************************************************************************/ - - #if (NETWORKMANAGER_COMPILATION) & NM_NETWORKMANAGER_COMPILATION_WITH_LIBNM_CORE - #include "nm-version.h" - #endif - - /*****************************************************************************/ - - #if (NETWORKMANAGER_COMPILATION) & NM_NETWORKMANAGER_COMPILATION_WITH_DAEMON - #include "nm-core-types.h" - #include "nm-types.h" - #include "nm-logging.h" - #endif - - #if (NETWORKMANAGER_COMPILATION) & NM_NETWORKMANAGER_COMPILATION_WITH_LIBNM_PRIVATE - #include "nm-libnm-utils.h" - #endif - - #if ((NETWORKMANAGER_COMPILATION) &NM_NETWORKMANAGER_COMPILATION_WITH_LIBNM) \ - && !((NETWORKMANAGER_COMPILATION) \ - & (NM_NETWORKMANAGER_COMPILATION_WITH_LIBNM_PRIVATE \ - | NM_NETWORKMANAGER_COMPILATION_WITH_LIBNM_CORE_INTERNAL)) - #include "NetworkManager.h" - #endif - -#endif /* NM_NETWORKMANAGER_COMPILATION_WITH_GLIB */ - -/*****************************************************************************/ - -#endif /* __NM_DEFAULT_H__ */ diff --git a/shared/nm-glib-aux/nm-c-list.h b/shared/nm-glib-aux/nm-c-list.h index e19774dd..6dd3ac72 100644 --- a/shared/nm-glib-aux/nm-c-list.h +++ b/shared/nm-glib-aux/nm-c-list.h @@ -1,4 +1,4 @@ -/* SPDX-License-Identifier: LGPL-2.1+ */ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ /* * Copyright (C) 2014 Red Hat, Inc. */ @@ -17,11 +17,6 @@ _what &&c_list_contains(list, &_what->member); \ }) -/* iterate over the list backwards. */ -#define nm_c_list_for_each_entry_prev(_iter, _list, _m) \ - for (_iter = c_list_entry((_list)->prev, __typeof__(*_iter), _m); &(_iter)->_m != (_list); \ - _iter = c_list_entry((_iter)->_m.prev, __typeof__(*_iter), _m)) - /*****************************************************************************/ typedef struct { diff --git a/shared/nm-glib-aux/nm-dbus-aux.c b/shared/nm-glib-aux/nm-dbus-aux.c index e47797a8..ec409ff1 100644 --- a/shared/nm-glib-aux/nm-dbus-aux.c +++ b/shared/nm-glib-aux/nm-dbus-aux.c @@ -1,9 +1,9 @@ -/* SPDX-License-Identifier: LGPL-2.1+ */ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ /* * Copyright (C) 2019 Red Hat, Inc. */ -#include "nm-default.h" +#include "nm-glib-aux/nm-default-glib-i18n-lib.h" #include "nm-dbus-aux.h" diff --git a/shared/nm-glib-aux/nm-dbus-aux.h b/shared/nm-glib-aux/nm-dbus-aux.h index 7b0b008d..4e3ae22d 100644 --- a/shared/nm-glib-aux/nm-dbus-aux.h +++ b/shared/nm-glib-aux/nm-dbus-aux.h @@ -1,4 +1,4 @@ -/* SPDX-License-Identifier: LGPL-2.1+ */ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ /* * Copyright (C) 2019 Red Hat, Inc. */ diff --git a/shared/nm-glib-aux/nm-dedup-multi.c b/shared/nm-glib-aux/nm-dedup-multi.c index 4a16f850..99da3b35 100644 --- a/shared/nm-glib-aux/nm-dedup-multi.c +++ b/shared/nm-glib-aux/nm-dedup-multi.c @@ -1,9 +1,9 @@ -/* SPDX-License-Identifier: LGPL-2.1+ */ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ /* * Copyright (C) 2017 Red Hat, Inc. */ -#include "nm-default.h" +#include "nm-glib-aux/nm-default-glib-i18n-lib.h" #include "nm-dedup-multi.h" diff --git a/shared/nm-glib-aux/nm-dedup-multi.h b/shared/nm-glib-aux/nm-dedup-multi.h index 6941dc63..1c0761bf 100644 --- a/shared/nm-glib-aux/nm-dedup-multi.h +++ b/shared/nm-glib-aux/nm-dedup-multi.h @@ -1,4 +1,4 @@ -/* SPDX-License-Identifier: LGPL-2.1+ */ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ /* * Copyright (C) 2017 Red Hat, Inc. */ diff --git a/shared/nm-glib-aux/nm-default-glib-i18n-lib.h b/shared/nm-glib-aux/nm-default-glib-i18n-lib.h new file mode 100644 index 00000000..9393d192 --- /dev/null +++ b/shared/nm-glib-aux/nm-default-glib-i18n-lib.h @@ -0,0 +1,21 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +/* + * Copyright (C) 2015 Red Hat, Inc. + */ + +#ifndef __NM_DEFAULT_GLIB_I18N_LIB_H__ +#define __NM_DEFAULT_GLIB_I18N_LIB_H__ + +/*****************************************************************************/ + +#define _NETWORKMANAGER_COMPILATION_GLIB_I18N_LIB + +#include "nm-glib-aux/nm-default-glib.h" + +#undef NETWORKMANAGER_COMPILATION +#define NETWORKMANAGER_COMPILATION \ + (NM_NETWORKMANAGER_COMPILATION_GLIB | NM_NETWORKMANAGER_COMPILATION_WITH_GLIB_I18N_LIB) + +/*****************************************************************************/ + +#endif /* __NM_DEFAULT_GLIB_I18N_LIB_H__ */ diff --git a/shared/nm-glib-aux/nm-default-glib-i18n-prog.h b/shared/nm-glib-aux/nm-default-glib-i18n-prog.h new file mode 100644 index 00000000..0abe807b --- /dev/null +++ b/shared/nm-glib-aux/nm-default-glib-i18n-prog.h @@ -0,0 +1,21 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +/* + * Copyright (C) 2015 Red Hat, Inc. + */ + +#ifndef __NM_DEFAULT_GLIB_I18N_PROG_H__ +#define __NM_DEFAULT_GLIB_I18N_PROG_H__ + +/*****************************************************************************/ + +#define _NETWORKMANAGER_COMPILATION_GLIB_I18N_PROG + +#include "nm-glib-aux/nm-default-glib.h" + +#undef NETWORKMANAGER_COMPILATION +#define NETWORKMANAGER_COMPILATION \ + (NM_NETWORKMANAGER_COMPILATION_GLIB | NM_NETWORKMANAGER_COMPILATION_WITH_GLIB_I18N_PROG) + +/*****************************************************************************/ + +#endif /* __NM_DEFAULT_GLIB_I18N_PROG_H__ */ diff --git a/shared/nm-glib-aux/nm-default-glib.h b/shared/nm-glib-aux/nm-default-glib.h new file mode 100644 index 00000000..34b23f77 --- /dev/null +++ b/shared/nm-glib-aux/nm-default-glib.h @@ -0,0 +1,75 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +/* + * Copyright (C) 2015 Red Hat, Inc. + */ + +#ifndef __NM_DEFAULT_GLIB_H__ +#define __NM_DEFAULT_GLIB_H__ + +/*****************************************************************************/ + +#include "nm-std-aux/nm-default-std.h" + +#undef NETWORKMANAGER_COMPILATION +#define NETWORKMANAGER_COMPILATION NM_NETWORKMANAGER_COMPILATION_WITH_GLIB + +/*****************************************************************************/ + +#include <glib.h> + +#if defined(_NETWORKMANAGER_COMPILATION_GLIB_I18N_PROG) + #if defined(_NETWORKMANAGER_COMPILATION_GLIB_I18N_LIB) + #error Cannot define _NETWORKMANAGER_COMPILATION_GLIB_I18N_LIB and _NETWORKMANAGER_COMPILATION_GLIB_I18N_PROG together + #endif + #undef _NETWORKMANAGER_COMPILATION_GLIB_I18N_PROG + #include <glib/gi18n.h> +#elif defined(_NETWORKMANAGER_COMPILATION_GLIB_I18N_LIB) + #undef _NETWORKMANAGER_COMPILATION_GLIB_I18N_LIB + #include <glib/gi18n-lib.h> +#endif + +/*****************************************************************************/ + +#if NM_MORE_ASSERTS == 0 + #ifndef G_DISABLE_CAST_CHECKS + /* Unless compiling with G_DISABLE_CAST_CHECKS, glib performs type checking + * during G_VARIANT_TYPE() via g_variant_type_checked_(). This is not necessary + * because commonly this cast is needed during something like + * + * g_variant_builder_init (&props, G_VARIANT_TYPE ("a{sv}")); + * + * Note that in if the variant type would be invalid, the check still + * wouldn't make the buggy code magically work. Instead of passing a + * bogus type string (bad), it would pass %NULL to g_variant_builder_init() + * (also bad). + * + * Also, a function like g_variant_builder_init() already validates + * the input type via something like + * + * g_return_if_fail (g_variant_type_is_container (type)); + * + * So, by having G_VARIANT_TYPE() also validate the type, we validate + * twice, whereas the first validation is rather pointless because it + * doesn't prevent the function to be called with invalid arguments. + * + * Just patch G_VARIANT_TYPE() to perform no check. + */ + #undef G_VARIANT_TYPE + #define G_VARIANT_TYPE(type_string) ((const GVariantType *) (type_string)) + #endif +#endif + +/*****************************************************************************/ + +#include "nm-gassert-patch.h" + +#include "nm-std-aux/nm-std-aux.h" +#include "nm-std-aux/nm-std-utils.h" +#include "nm-glib-aux/nm-macros-internal.h" +#include "nm-glib-aux/nm-shared-utils.h" +#include "nm-glib-aux/nm-errno.h" +#include "nm-glib-aux/nm-hash-utils.h" + +/*****************************************************************************/ + +#endif /* __NM_DEFAULT_GLIB_H__ */ diff --git a/shared/nm-glib-aux/nm-enum-utils.c b/shared/nm-glib-aux/nm-enum-utils.c index 5292755d..b06f2bb2 100644 --- a/shared/nm-glib-aux/nm-enum-utils.c +++ b/shared/nm-glib-aux/nm-enum-utils.c @@ -1,9 +1,9 @@ -/* SPDX-License-Identifier: LGPL-2.1+ */ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ /* * Copyright (C) 2017 Red Hat, Inc. */ -#include "nm-default.h" +#include "nm-glib-aux/nm-default-glib-i18n-lib.h" #include "nm-enum-utils.h" #include "nm-str-buf.h" @@ -76,30 +76,34 @@ _ASSERT_enum_values_info(GType type, const NMUtilsEnumValueInfo *value_infos) } static gboolean -_is_hex_string(const char *str) +_is_hex_string(const char *str, gboolean allow_sign) { + if (allow_sign && str[0] == '-') + str++; return str[0] == '0' && str[1] == 'x' && str[2] && NM_STRCHAR_ALL(&str[2], ch, g_ascii_isxdigit(ch)); } static gboolean -_is_dec_string(const char *str) +_is_dec_string(const char *str, gboolean allow_sign) { + if (allow_sign && str[0] == '-') + str++; return str[0] && NM_STRCHAR_ALL(&str[0], ch, g_ascii_isdigit(ch)); } static gboolean _enum_is_valid_enum_nick(const char *str) { - return str[0] && !NM_STRCHAR_ANY(str, ch, g_ascii_isspace(ch)) && !_is_dec_string(str) - && !_is_hex_string(str); + return str[0] && !NM_STRCHAR_ANY(str, ch, g_ascii_isspace(ch)) && !_is_dec_string(str, TRUE) + && !_is_hex_string(str, TRUE); } static gboolean _enum_is_valid_flags_nick(const char *str) { - return str[0] && !NM_STRCHAR_ANY(str, ch, IS_FLAGS_SEPARATOR(ch)) && !_is_dec_string(str) - && !_is_hex_string(str); + return str[0] && !NM_STRCHAR_ANY(str, ch, IS_FLAGS_SEPARATOR(ch)) && !_is_dec_string(str, FALSE) + && !_is_hex_string(str, FALSE); } char * @@ -212,8 +216,7 @@ _nm_utils_enum_from_str_full(GType type, _ASSERT_enum_values_info(type, value_infos); - str_clone = strdup(str); - s = nm_str_skip_leading_spaces(str_clone); + s = nm_strdup_maybe_a(300, nm_str_skip_leading_spaces(str), &str_clone); g_strchomp(s); klass = g_type_class_ref(type); @@ -221,16 +224,31 @@ _nm_utils_enum_from_str_full(GType type, if (G_IS_ENUM_CLASS(klass)) { GEnumValue *enum_value; + G_STATIC_ASSERT(G_MAXINT < G_MAXINT64); + G_STATIC_ASSERT(G_MININT > G_MININT64); + if (s[0]) { - if (_is_hex_string(s)) { - v64 = _nm_utils_ascii_str_to_int64(s, 16, 0, G_MAXUINT, -1); - if (v64 != -1) { - value = (int) v64; - ret = TRUE; + if (_is_hex_string(s, TRUE)) { + if (s[0] == '-') { + v64 = _nm_utils_ascii_str_to_int64(&s[3], + 16, + -((gint64) G_MAXINT), + -((gint64) G_MININT), + G_MAXINT64); + if (v64 != G_MAXINT64) { + value = (int) (-v64); + ret = TRUE; + } + } else { + v64 = _nm_utils_ascii_str_to_int64(&s[2], 16, G_MININT, G_MAXINT, G_MAXINT64); + if (v64 != G_MAXINT64) { + value = (int) v64; + ret = TRUE; + } } - } else if (_is_dec_string(s)) { - v64 = _nm_utils_ascii_str_to_int64(s, 10, 0, G_MAXUINT, -1); - if (v64 != -1) { + } else if (_is_dec_string(s, TRUE)) { + v64 = _nm_utils_ascii_str_to_int64(s, 10, G_MININT, G_MAXINT, G_MAXINT64); + if (v64 != G_MAXINT64) { value = (int) v64; ret = TRUE; } @@ -259,14 +277,14 @@ _nm_utils_enum_from_str_full(GType type, } if (s[0]) { - if (_is_hex_string(s)) { + if (_is_hex_string(s, FALSE)) { v64 = _nm_utils_ascii_str_to_int64(&s[2], 16, 0, G_MAXUINT, -1); if (v64 == -1) { ret = FALSE; break; } uvalue |= (unsigned) v64; - } else if (_is_dec_string(s)) { + } else if (_is_dec_string(s, FALSE)) { v64 = _nm_utils_ascii_str_to_int64(s, 10, 0, G_MAXUINT, -1); if (v64 == -1) { ret = FALSE; diff --git a/shared/nm-glib-aux/nm-enum-utils.h b/shared/nm-glib-aux/nm-enum-utils.h index 38acf8fd..89be54e7 100644 --- a/shared/nm-glib-aux/nm-enum-utils.h +++ b/shared/nm-glib-aux/nm-enum-utils.h @@ -1,4 +1,4 @@ -/* SPDX-License-Identifier: LGPL-2.1+ */ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ /* * Copyright (C) 2017 Red Hat, Inc. */ diff --git a/shared/nm-glib-aux/nm-errno.c b/shared/nm-glib-aux/nm-errno.c index f7a7685d..668606ca 100644 --- a/shared/nm-glib-aux/nm-errno.c +++ b/shared/nm-glib-aux/nm-errno.c @@ -1,9 +1,9 @@ -/* SPDX-License-Identifier: LGPL-2.1+ */ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ /* * Copyright (C) 2018 Red Hat, Inc. */ -#include "nm-default.h" +#include "nm-glib-aux/nm-default-glib-i18n-lib.h" #include "nm-errno.h" @@ -106,7 +106,7 @@ nm_strerror_native_r(int errsv, char *buf, gsize buf_size) nm_assert(buf); nm_assert(buf_size > 0); -#if (_POSIX_C_SOURCE >= 200112L) && !_GNU_SOURCE +#if (!defined(__GLIBC__) && !defined(__UCLIBC__)) || ((_POSIX_C_SOURCE >= 200112L) && !_GNU_SOURCE) /* XSI-compliant */ { int errno_saved = errno; diff --git a/shared/nm-glib-aux/nm-errno.h b/shared/nm-glib-aux/nm-errno.h index bf7189c0..62c8379f 100644 --- a/shared/nm-glib-aux/nm-errno.h +++ b/shared/nm-glib-aux/nm-errno.h @@ -1,4 +1,4 @@ -/* SPDX-License-Identifier: LGPL-2.1+ */ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ /* * Copyright (C) 2018 Red Hat, Inc. */ diff --git a/shared/nm-glib-aux/nm-gassert-patch.h b/shared/nm-glib-aux/nm-gassert-patch.h new file mode 100644 index 00000000..bac8697c --- /dev/null +++ b/shared/nm-glib-aux/nm-gassert-patch.h @@ -0,0 +1,76 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +/* + * Copyright (C) 2015 Red Hat, Inc. + */ + +#ifndef __NM_GASSERT_PATCH_H__ +#define __NM_GASSERT_PATCH_H__ + +/*****************************************************************************/ + +#if NM_MORE_ASSERTS == 0 + +/* glib assertions (g_return_*(), g_assert*()) contain a textual representation + * of the checked statement. This part of the assertion blows up the size of the + * binary. Unless we compile a debug-build with NM_MORE_ASSERTS, drop these + * parts. Note that the failed assertion still prints the file and line where the + * assertion fails. That shall suffice. */ + +static inline void +_nm_g_return_if_fail_warning(const char *log_domain, const char *file, int line) +{ + char file_buf[256 + 15]; + + g_snprintf(file_buf, sizeof(file_buf), "((%s:%d))", file, line); + g_return_if_fail_warning(log_domain, file_buf, "<dropped>"); +} + + #define g_return_if_fail_warning(log_domain, pretty_function, expression) \ + _nm_g_return_if_fail_warning(log_domain, __FILE__, __LINE__) + + #define g_assertion_message_expr(domain, file, line, func, expr) \ + g_assertion_message_expr(domain, file, line, "<unknown-fcn>", (expr) ? "<dropped>" : NULL) + + #undef g_return_val_if_reached + #define g_return_val_if_reached(val) \ + G_STMT_START \ + { \ + g_log(G_LOG_DOMAIN, \ + G_LOG_LEVEL_CRITICAL, \ + "file %s: line %d (%s): should not be reached", \ + __FILE__, \ + __LINE__, \ + "<dropped>"); \ + return (val); \ + } \ + G_STMT_END + + #undef g_return_if_reached + #define g_return_if_reached() \ + G_STMT_START \ + { \ + g_log(G_LOG_DOMAIN, \ + G_LOG_LEVEL_CRITICAL, \ + "file %s: line %d (%s): should not be reached", \ + __FILE__, \ + __LINE__, \ + "<dropped>"); \ + return; \ + } \ + G_STMT_END +#endif + +/*****************************************************************************/ + +#if NM_MORE_ASSERTS == 0 + #define NM_ASSERT_G_RETURN_EXPR(expr) "<dropped>" + #define NM_ASSERT_NO_MSG 1 + +#else + #define NM_ASSERT_G_RETURN_EXPR(expr) "" expr "" + #define NM_ASSERT_NO_MSG 0 +#endif + +/*****************************************************************************/ + +#endif /* __NM_GASSERT_PATCH_H__ */ diff --git a/shared/nm-glib-aux/nm-glib.h b/shared/nm-glib-aux/nm-glib.h index 9794e1fb..befb8d90 100644 --- a/shared/nm-glib-aux/nm-glib.h +++ b/shared/nm-glib-aux/nm-glib.h @@ -1,4 +1,4 @@ -/* SPDX-License-Identifier: LGPL-2.1+ */ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ /* * Copyright (C) 2008 - 2018 Red Hat, Inc. */ @@ -683,7 +683,7 @@ g_hash_table_steal_extended(GHashTable * hash_table, gpointer *_stolen_value = (stolen_value); \ \ /* we cannot allow NULL arguments, because then we would leak the values in - * the compat implementation. */ \ + * the compat implementation. */ \ g_assert(_stolen_key); \ g_assert(_stolen_value); \ \ diff --git a/shared/nm-glib-aux/nm-hash-utils.c b/shared/nm-glib-aux/nm-hash-utils.c index 8a1c87fd..29349b1d 100644 --- a/shared/nm-glib-aux/nm-hash-utils.c +++ b/shared/nm-glib-aux/nm-hash-utils.c @@ -1,9 +1,9 @@ -/* SPDX-License-Identifier: LGPL-2.1+ */ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ /* * Copyright (C) 2017 Red Hat, Inc. */ -#include "nm-default.h" +#include "nm-glib-aux/nm-default-glib-i18n-lib.h" #include "nm-hash-utils.h" diff --git a/shared/nm-glib-aux/nm-hash-utils.h b/shared/nm-glib-aux/nm-hash-utils.h index 1cfdcaf6..a7b8677b 100644 --- a/shared/nm-glib-aux/nm-hash-utils.h +++ b/shared/nm-glib-aux/nm-hash-utils.h @@ -1,4 +1,4 @@ -/* SPDX-License-Identifier: LGPL-2.1+ */ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ /* * Copyright (C) 2017 Red Hat, Inc. */ @@ -416,6 +416,14 @@ nm_hash_obfuscate_ptr(guint static_seed, gconstpointer val) * values in a global context. */ #define NM_HASH_OBFUSCATE_PTR(ptr) (nm_hash_obfuscate_ptr(1678382159u, ptr)) +#define NM_HASH_OBFUSCATE_PTR_STR(ptr, buf) \ + ({ \ + gconstpointer _ptr = (ptr); \ + \ + _ptr ? nm_sprintf_buf(buf, "[" NM_HASH_OBFUSCATE_PTR_FMT "]", NM_HASH_OBFUSCATE_PTR(_ptr)) \ + : "(null)"; \ + }) + static inline const char * nm_hash_obfuscated_ptr_str(gconstpointer ptr, char buf[static 17]) { diff --git a/shared/nm-glib-aux/nm-io-utils.c b/shared/nm-glib-aux/nm-io-utils.c index 32f92156..429591ad 100644 --- a/shared/nm-glib-aux/nm-io-utils.c +++ b/shared/nm-glib-aux/nm-io-utils.c @@ -1,9 +1,9 @@ -/* SPDX-License-Identifier: LGPL-2.1+ */ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ /* * Copyright (C) 2018 Red Hat, Inc. */ -#include "nm-default.h" +#include "nm-glib-aux/nm-default-glib-i18n-lib.h" #include "nm-io-utils.h" diff --git a/shared/nm-glib-aux/nm-io-utils.h b/shared/nm-glib-aux/nm-io-utils.h index 81be7b60..8182f5cf 100644 --- a/shared/nm-glib-aux/nm-io-utils.h +++ b/shared/nm-glib-aux/nm-io-utils.h @@ -1,4 +1,4 @@ -/* SPDX-License-Identifier: LGPL-2.1+ */ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ /* * Copyright (C) 2018 Red Hat, Inc. */ diff --git a/shared/nm-glib-aux/nm-jansson.h b/shared/nm-glib-aux/nm-jansson.h index 9b7731a4..6173a7ac 100644 --- a/shared/nm-glib-aux/nm-jansson.h +++ b/shared/nm-glib-aux/nm-jansson.h @@ -1,4 +1,4 @@ -/* SPDX-License-Identifier: LGPL-2.1+ */ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ /* * Copyright (C) 2018 Red Hat, Inc. */ diff --git a/shared/nm-glib-aux/nm-json-aux.c b/shared/nm-glib-aux/nm-json-aux.c index 4212e628..97ee606f 100644 --- a/shared/nm-glib-aux/nm-json-aux.c +++ b/shared/nm-glib-aux/nm-json-aux.c @@ -1,9 +1,9 @@ -/* SPDX-License-Identifier: LGPL-2.1+ */ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ /* * Copyright (C) 2017 - 2019 Red Hat, Inc. */ -#include "nm-default.h" +#include "nm-glib-aux/nm-default-glib-i18n-lib.h" #include "nm-json-aux.h" @@ -11,6 +11,16 @@ /*****************************************************************************/ +/* If RTLD_DEEPBIND isn't available just ignore it. This can cause problems + * with jansson, json-glib, and cjson symbols clashing (and as such crashing the + * program). But that needs to be fixed by the json libraries, and it is by adding + * symbol versioning in recent versions. */ +#ifndef RTLD_DEEPBIND + #define RTLD_DEEPBIND 0 +#endif + +/*****************************************************************************/ + static void _gstr_append_string_len(GString *gstr, const char *str, gsize len) { diff --git a/shared/nm-glib-aux/nm-json-aux.h b/shared/nm-glib-aux/nm-json-aux.h index 348f0f58..99759a8d 100644 --- a/shared/nm-glib-aux/nm-json-aux.h +++ b/shared/nm-glib-aux/nm-json-aux.h @@ -1,4 +1,4 @@ -/* SPDX-License-Identifier: LGPL-2.1+ */ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ /* * Copyright (C) 2017 - 2019 Red Hat, Inc. */ diff --git a/shared/nm-glib-aux/nm-keyfile-aux.c b/shared/nm-glib-aux/nm-keyfile-aux.c index ae90fb0d..b5962712 100644 --- a/shared/nm-glib-aux/nm-keyfile-aux.c +++ b/shared/nm-glib-aux/nm-keyfile-aux.c @@ -1,9 +1,9 @@ -/* SPDX-License-Identifier: LGPL-2.1+ */ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ /* * Copyright (C) 2019 Red Hat, Inc. */ -#include "nm-default.h" +#include "nm-glib-aux/nm-default-glib-i18n-lib.h" #include "nm-keyfile-aux.h" diff --git a/shared/nm-glib-aux/nm-keyfile-aux.h b/shared/nm-glib-aux/nm-keyfile-aux.h index 198a2c53..72d2f418 100644 --- a/shared/nm-glib-aux/nm-keyfile-aux.h +++ b/shared/nm-glib-aux/nm-keyfile-aux.h @@ -1,4 +1,4 @@ -/* SPDX-License-Identifier: LGPL-2.1+ */ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ /* * Copyright (C) 2019 Red Hat, Inc. */ diff --git a/shared/nm-glib-aux/nm-logging-base.c b/shared/nm-glib-aux/nm-logging-base.c index 229470f6..66b591b2 100644 --- a/shared/nm-glib-aux/nm-logging-base.c +++ b/shared/nm-glib-aux/nm-logging-base.c @@ -1,6 +1,6 @@ -/* SPDX-License-Identifier: LGPL-2.1+ */ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ -#include "nm-default.h" +#include "nm-glib-aux/nm-default-glib-i18n-lib.h" #include "nm-logging-base.h" diff --git a/shared/nm-glib-aux/nm-logging-base.h b/shared/nm-glib-aux/nm-logging-base.h index eb71c2a4..d9ac03c7 100644 --- a/shared/nm-glib-aux/nm-logging-base.h +++ b/shared/nm-glib-aux/nm-logging-base.h @@ -1,4 +1,4 @@ -/* SPDX-License-Identifier: LGPL-2.1+ */ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ #ifndef __NM_LOGGING_BASE_H__ #define __NM_LOGGING_BASE_H__ diff --git a/shared/nm-glib-aux/nm-logging-fwd.h b/shared/nm-glib-aux/nm-logging-fwd.h index ee75b713..df0bb161 100644 --- a/shared/nm-glib-aux/nm-logging-fwd.h +++ b/shared/nm-glib-aux/nm-logging-fwd.h @@ -1,4 +1,4 @@ -/* SPDX-License-Identifier: LGPL-2.1+ */ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ /* * Copyright (C) 2006 - 2018 Red Hat, Inc. * Copyright (C) 2006 - 2008 Novell, Inc. @@ -57,15 +57,11 @@ typedef enum { /*< skip >*/ /* aliases: */ LOGD_DHCP = LOGD_DHCP4 | LOGD_DHCP6, LOGD_IP = LOGD_IP4 | LOGD_IP6, -} NMLogDomain; -static inline NMLogDomain -LOGD_DHCP_from_addr_family(int addr_family) -{ - nm_assert_addr_family(addr_family); +#define LOGD_DHCPX(is_ipv4) ((is_ipv4) ? LOGD_DHCP4 : LOGD_DHCP6) +#define LOGD_IPX(is_ipv4) ((is_ipv4) ? LOGD_IP4 : LOGD_IP6) - return addr_family == AF_INET6 ? LOGD_DHCP6 : LOGD_DHCP4; -} +} NMLogDomain; /* Log levels */ typedef enum { /*< skip >*/ diff --git a/shared/nm-glib-aux/nm-macros-internal.h b/shared/nm-glib-aux/nm-macros-internal.h index d495dd8e..113a67a0 100644 --- a/shared/nm-glib-aux/nm-macros-internal.h +++ b/shared/nm-glib-aux/nm-macros-internal.h @@ -1,4 +1,4 @@ -/* SPDX-License-Identifier: LGPL-2.1+ */ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ /* * Copyright (C) 2012 Colin Walters <walters@verbum.org>. * Copyright (C) 2014 Red Hat, Inc. @@ -656,11 +656,11 @@ NM_G_ERROR_MSG(GError *error) #if _NM_CC_SUPPORT_GENERIC /* returns @value, if the type of @value matches @type. - * This requires support for C11 _Generic(). If no support is - * present, this returns @value directly. - * - * It's useful to check the let the compiler ensure that @value is - * of a certain type. */ + * This requires support for C11 _Generic(). If no support is + * present, this returns @value directly. + * + * It's useful to check the let the compiler ensure that @value is + * of a certain type. */ #define _NM_ENSURE_TYPE(type, value) (_Generic((value), type : (value))) #define _NM_ENSURE_TYPE_CONST(type, value) \ (_Generic((value), const type \ @@ -770,7 +770,7 @@ NM_G_ERROR_MSG(GError *error) { \ return NM_CACHED_QUARK(string); \ } \ - struct _dummy_struct_for_trailing_semicolon + _NM_DUMMY_STRUCT_FOR_TRAILING_SEMICOLON /*****************************************************************************/ @@ -1038,6 +1038,14 @@ nm_g_object_unref(gpointer obj) */ #define nm_clear_g_free(pp) nm_clear_pointer(pp, g_free) +/* Our nm_clear_pointer() is more typesafe than g_clear_pointer() and + * should be preferred. + * + * For g_clear_object() that is not the case (because g_object_unref() + * anyway takes a void pointer). So using g_clear_object() is fine. + * + * Still have a nm_clear_g_object() because that returns a boolean + * indication whether anything was cleared. */ #define nm_clear_g_object(pp) nm_clear_pointer(pp, g_object_unref) /** @@ -1604,6 +1612,16 @@ _nm_strndup_a_step(char *s, const char *str, gsize len) _nm_strndup_a_step(_s_snd, _str_snd, _len_snd); \ }) +#define nm_strdup_maybe_a(alloca_maxlen, str, out_str_free) \ + ({ \ + const char *const _str_snd = (str); \ + \ + (char *) nm_memdup_maybe_a(alloca_maxlen, \ + _str_snd, \ + _str_snd ? strlen(_str_snd) + 1u : 0u, \ + out_str_free); \ + }) + /*****************************************************************************/ /* generic macro to convert an int to a (heap allocated) string. diff --git a/shared/nm-glib-aux/nm-obj.h b/shared/nm-glib-aux/nm-obj.h index 9bbe05a4..2062f661 100644 --- a/shared/nm-glib-aux/nm-obj.h +++ b/shared/nm-glib-aux/nm-obj.h @@ -1,4 +1,4 @@ -/* SPDX-License-Identifier: LGPL-2.1+ */ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ /* * Copyright (C) 2017 Red Hat, Inc. */ diff --git a/shared/nm-glib-aux/nm-random-utils.c b/shared/nm-glib-aux/nm-random-utils.c index 0c8b2758..c95d368d 100644 --- a/shared/nm-glib-aux/nm-random-utils.c +++ b/shared/nm-glib-aux/nm-random-utils.c @@ -1,9 +1,9 @@ -/* SPDX-License-Identifier: LGPL-2.1+ */ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ /* * Copyright (C) 2017 Red Hat, Inc. */ -#include "nm-default.h" +#include "nm-glib-aux/nm-default-glib-i18n-lib.h" #include "nm-random-utils.h" diff --git a/shared/nm-glib-aux/nm-random-utils.h b/shared/nm-glib-aux/nm-random-utils.h index 5c34d8ab..d0eae103 100644 --- a/shared/nm-glib-aux/nm-random-utils.h +++ b/shared/nm-glib-aux/nm-random-utils.h @@ -1,4 +1,4 @@ -/* SPDX-License-Identifier: LGPL-2.1+ */ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ /* * Copyright (C) 2017 Red Hat, Inc. */ diff --git a/shared/nm-glib-aux/nm-ref-string.c b/shared/nm-glib-aux/nm-ref-string.c index 526185f3..902f1c80 100644 --- a/shared/nm-glib-aux/nm-ref-string.c +++ b/shared/nm-glib-aux/nm-ref-string.c @@ -1,6 +1,6 @@ -/* SPDX-License-Identifier: LGPL-2.1+ */ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ -#include "nm-default.h" +#include "nm-glib-aux/nm-default-glib-i18n-lib.h" #include "nm-ref-string.h" @@ -168,24 +168,25 @@ void _nm_ref_string_unref_non_null(NMRefString *rstr) { RefString *const rstr0 = (RefString *) rstr; + int r; _ASSERT(rstr0); - if (G_LIKELY(!g_atomic_int_dec_and_test(&rstr0->ref_count))) + /* fast-path: first try to decrement the ref-count without bringing it + * to zero. */ + r = rstr0->ref_count; + if (G_LIKELY(r > 1 && g_atomic_int_compare_and_exchange(&rstr0->ref_count, r, r - 1))) return; + /* We apparently are about to return the last reference. Take a lock. */ + G_LOCK(gl_lock); - /* in the fast-path above, we already decremented the ref-count to zero. - * We need recheck that the ref-count is still zero. */ + nm_assert(g_hash_table_lookup(gl_hash, rstr0) == rstr0); - if (g_atomic_int_get(&rstr0->ref_count) == 0) { + if (G_LIKELY(g_atomic_int_dec_and_test(&rstr0->ref_count))) { if (!g_hash_table_remove(gl_hash, rstr0)) nm_assert_not_reached(); - } else { -#if NM_MORE_ASSERTS > 5 - nm_assert(g_hash_table_lookup(gl_hash, rstr0) == rstr0); -#endif } G_UNLOCK(gl_lock); diff --git a/shared/nm-glib-aux/nm-ref-string.h b/shared/nm-glib-aux/nm-ref-string.h index fe65d6a3..97c263b0 100644 --- a/shared/nm-glib-aux/nm-ref-string.h +++ b/shared/nm-glib-aux/nm-ref-string.h @@ -1,4 +1,4 @@ -/* SPDX-License-Identifier: LGPL-2.1+ */ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ #ifndef __NM_REF_STRING_H__ #define __NM_REF_STRING_H__ diff --git a/shared/nm-glib-aux/nm-secret-utils.c b/shared/nm-glib-aux/nm-secret-utils.c index 3a488673..8188b503 100644 --- a/shared/nm-glib-aux/nm-secret-utils.c +++ b/shared/nm-glib-aux/nm-secret-utils.c @@ -1,10 +1,10 @@ -/* SPDX-License-Identifier: LGPL-2.1+ */ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ /* * Copyright (C) 2018 Red Hat, Inc. * Copyright (C) 2015 - 2019 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved. */ -#include "nm-default.h" +#include "nm-glib-aux/nm-default-glib-i18n-lib.h" #include "nm-secret-utils.h" diff --git a/shared/nm-glib-aux/nm-secret-utils.h b/shared/nm-glib-aux/nm-secret-utils.h index 2d342e59..ac279635 100644 --- a/shared/nm-glib-aux/nm-secret-utils.h +++ b/shared/nm-glib-aux/nm-secret-utils.h @@ -1,4 +1,4 @@ -/* SPDX-License-Identifier: LGPL-2.1+ */ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ /* * Copyright (C) 2018 Red Hat, Inc. */ diff --git a/shared/nm-glib-aux/nm-shared-utils.c b/shared/nm-glib-aux/nm-shared-utils.c index 7f766871..3215a33b 100644 --- a/shared/nm-glib-aux/nm-shared-utils.c +++ b/shared/nm-glib-aux/nm-shared-utils.c @@ -1,9 +1,9 @@ -/* SPDX-License-Identifier: LGPL-2.1+ */ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ /* * Copyright (C) 2016 Red Hat, Inc. */ -#include "nm-default.h" +#include "nm-glib-aux/nm-default-glib-i18n-lib.h" #include "nm-shared-utils.h" @@ -19,6 +19,9 @@ #include "nm-errno.h" #include "nm-str-buf.h" +G_STATIC_ASSERT(sizeof(NMEtherAddr) == 6); +G_STATIC_ASSERT(_nm_alignof(NMEtherAddr) == 1); + G_STATIC_ASSERT(sizeof(NMUtilsNamedEntry) == sizeof(const char *)); G_STATIC_ASSERT(G_STRUCT_OFFSET(NMUtilsNamedValue, value_ptr) == sizeof(const char *)); @@ -361,6 +364,21 @@ again: return b; } +GBytes * +nm_g_bytes_new_from_str(const char *str) +{ + gsize l; + + if (!str) + return NULL; + + /* the returned array is guaranteed to have a trailing '\0' + * character *after* the length. */ + + l = strlen(str); + return g_bytes_new_take(nm_memdup(str, l + 1u), l); +} + /** * nm_utils_gbytes_equals: * @bytes: (allow-none): a #GBytes array to compare. Note that @@ -434,6 +452,24 @@ nm_g_variant_singleton_u_0(void) /*****************************************************************************/ +GHashTable * +nm_utils_strdict_clone(GHashTable *src) +{ + GHashTable * dst; + GHashTableIter iter; + const char * key; + const char * val; + + if (!src) + return NULL; + + dst = g_hash_table_new_full(nm_str_hash, g_str_equal, g_free, g_free); + g_hash_table_iter_init(&iter, src); + while (g_hash_table_iter_next(&iter, (gpointer *) &key, (gpointer *) &val)) + g_hash_table_insert(dst, g_strdup(key), g_strdup(val)); + return dst; +} + /* Convert a hash table with "char *" keys and values to an "a{ss}" GVariant. * The keys will be sorted asciibetically. * Returns a floating reference. @@ -750,27 +786,27 @@ nm_utils_ip6_address_same_prefix_cmp(const struct in6_addr *addr_a, return 0; } -/** - * _nm_utils_ip4_get_default_prefix: - * @ip: an IPv4 address (in network byte order) - * - * When the Internet was originally set up, various ranges of IP addresses were - * segmented into three network classes: A, B, and C. This function will return - * a prefix that is associated with the IP address specified defining where it - * falls in the predefined classes. - * - * Returns: the default class prefix for the given IP - **/ -/* The function is originally from ipcalc.c of Red Hat's initscripts. */ +/*****************************************************************************/ + guint32 -_nm_utils_ip4_get_default_prefix(guint32 ip) +_nm_utils_ip4_get_default_prefix0(in_addr_t ip) { - if (((ntohl(ip) & 0xFF000000) >> 24) <= 127) - return 8; /* Class A - 255.0.0.0 */ - else if (((ntohl(ip) & 0xFF000000) >> 24) <= 191) - return 16; /* Class B - 255.255.0.0 */ + /* The function is originally from ipcalc.c of Red Hat's initscripts. */ + switch (ntohl(ip) >> 24) { + case 0 ... 127: + return 8; /* Class A */ + case 128 ... 191: + return 16; /* Class B */ + case 192 ... 223: + return 24; /* Class C */ + } + return 0; +} - return 24; /* Class C - 255.255.255.0 */ +guint32 +_nm_utils_ip4_get_default_prefix(in_addr_t ip) +{ + return _nm_utils_ip4_get_default_prefix0(ip) ?: 24; } gboolean @@ -2231,6 +2267,8 @@ _nm_utils_ascii_str_to_bool(const char *str, int default_value) /*****************************************************************************/ +NM_CACHED_QUARK_FCN("nm-manager-error-quark", nm_manager_error_quark); + NM_CACHED_QUARK_FCN("nm-utils-error-quark", nm_utils_error_quark); void @@ -3066,6 +3104,18 @@ nm_utils_fd_read_loop_exact(int fd, void *buf, size_t nbytes, bool do_poll) /*****************************************************************************/ +void +nm_utils_named_value_clear_with_g_free(NMUtilsNamedValue *val) +{ + if (val) { + gs_free gpointer x_name = NULL; + gs_free gpointer x_value = NULL; + + x_name = (gpointer) g_steal_pointer(&val->name); + x_value = g_steal_pointer(&val->value_ptr); + } +} + G_STATIC_ASSERT(G_STRUCT_OFFSET(NMUtilsNamedValue, name) == 0); NMUtilsNamedValue * @@ -3262,6 +3312,62 @@ nm_utils_hash_values_to_array(GHashTable * hash, return arr; } +/*****************************************************************************/ + +/** + * nm_utils_hashtable_equal: + * @a: one #GHashTable + * @b: other #GHashTable + * @treat_null_as_empty: if %TRUE, when either @a or @b is %NULL, it is + * treated like an empty hash. It means, a %NULL hash will compare equal + * to an empty hash. + * @equal_func: the equality function, for comparing the values. + * If %NULL, the values are not compared. In that case, the function + * only checks, if both dictionaries have the same keys -- according + * to @b's key equality function. + * Note that the values of @a will be passed as first argument + * to @equal_func. + * + * Compares two hash tables, whether they have equal content. + * This only makes sense, if @a and @b have the same key types and + * the same key compare-function. + * + * Returns: %TRUE, if both dictionaries have the same content. + */ +gboolean +nm_utils_hashtable_equal(const GHashTable *a, + const GHashTable *b, + gboolean treat_null_as_empty, + GEqualFunc equal_func) +{ + guint n; + GHashTableIter iter; + gconstpointer key, v_a, v_b; + + if (a == b) + return TRUE; + if (!treat_null_as_empty) { + if (!a || !b) + return FALSE; + } + + n = a ? g_hash_table_size((GHashTable *) a) : 0; + if (n != (b ? g_hash_table_size((GHashTable *) b) : 0)) + return FALSE; + + if (n > 0) { + g_hash_table_iter_init(&iter, (GHashTable *) a); + while (g_hash_table_iter_next(&iter, (gpointer *) &key, (gpointer *) &v_a)) { + if (!g_hash_table_lookup_extended((GHashTable *) b, key, NULL, (gpointer *) &v_b)) + return FALSE; + if (equal_func && !equal_func(v_a, v_b)) + return FALSE; + } + } + + return TRUE; +} + static gboolean _utils_hashtable_equal(GHashTable * hash_a, GHashTable * hash_b, @@ -3301,7 +3407,7 @@ _utils_hashtable_equal(GHashTable * hash_a, } /** - * nm_utils_hashtable_equal: + * nm_utils_hashtable_cmp_equal: * @a: (allow-none): the hash table or %NULL * @b: (allow-none): the other hash table or %NULL * @cmp_values: (allow-none): if %NULL, only the keys @@ -3316,10 +3422,10 @@ _utils_hashtable_equal(GHashTable * hash_a, * @cmp_values is given) all values are the same. */ gboolean -nm_utils_hashtable_equal(const GHashTable *a, - const GHashTable *b, - GCompareDataFunc cmp_values, - gpointer user_data) +nm_utils_hashtable_cmp_equal(const GHashTable *a, + const GHashTable *b, + GCompareDataFunc cmp_values, + gpointer user_data) { GHashTable *hash_a = (GHashTable *) a; GHashTable *hash_b = (GHashTable *) b; @@ -3374,7 +3480,7 @@ _hashtable_cmp_func(gconstpointer a, gconstpointer b, gpointer user_data) * @a: (allow-none): the hash to compare. May be %NULL. * @b: (allow-none): the other hash to compare. May be %NULL. * @do_fast_precheck: if %TRUE, assume that the hashes are equal - * and that it is worth calling nm_utils_hashtable_equal() first. + * and that it is worth calling nm_utils_hashtable_cmp_equal() first. * That requires, that both hashes have the same equals function * which is compatible with the @cmp_keys function. * @cmp_keys: the compare function for keys. Usually, the hash/equal function @@ -3827,62 +3933,6 @@ nm_utils_array_find_binary_search(gconstpointer list, /*****************************************************************************/ /** - * nm_utils_hash_table_equal: - * @a: one #GHashTable - * @b: other #GHashTable - * @treat_null_as_empty: if %TRUE, when either @a or @b is %NULL, it is - * treated like an empty hash. It means, a %NULL hash will compare equal - * to an empty hash. - * @equal_func: the equality function, for comparing the values. - * If %NULL, the values are not compared. In that case, the function - * only checks, if both dictionaries have the same keys -- according - * to @b's key equality function. - * Note that the values of @a will be passed as first argument - * to @equal_func. - * - * Compares two hash tables, whether they have equal content. - * This only makes sense, if @a and @b have the same key types and - * the same key compare-function. - * - * Returns: %TRUE, if both dictionaries have the same content. - */ -gboolean -nm_utils_hash_table_equal(const GHashTable * a, - const GHashTable * b, - gboolean treat_null_as_empty, - NMUtilsHashTableEqualFunc equal_func) -{ - guint n; - GHashTableIter iter; - gconstpointer key, v_a, v_b; - - if (a == b) - return TRUE; - if (!treat_null_as_empty) { - if (!a || !b) - return FALSE; - } - - n = a ? g_hash_table_size((GHashTable *) a) : 0; - if (n != (b ? g_hash_table_size((GHashTable *) b) : 0)) - return FALSE; - - if (n > 0) { - g_hash_table_iter_init(&iter, (GHashTable *) a); - while (g_hash_table_iter_next(&iter, (gpointer *) &key, (gpointer *) &v_a)) { - if (!g_hash_table_lookup_extended((GHashTable *) b, key, NULL, (gpointer *) &v_b)) - return FALSE; - if (equal_func && !equal_func(v_a, v_b)) - return FALSE; - } - } - - return TRUE; -} - -/*****************************************************************************/ - -/** * nm_utils_get_start_time_for_pid: * @pid: the process identifier * @out_state: return the state character, like R, S, Z. See `man 5 proc`. @@ -4749,7 +4799,7 @@ typedef struct { GMainContext *context; GHashTable * fds; GPollFD * fds_arr; - int fds_len; + guint fds_len; int max_priority; bool acquired : 1; } CtxIntegSource; @@ -4792,13 +4842,15 @@ _ctx_integ_source_prepare(GSource *source, int *out_timeout) int max_priority; int timeout = -1; gboolean any_ready; - int fds_allocated; - int fds_len_old; - gs_free GPollFD *fds_arr_old = NULL; - GHashTableIter h_iter; - PollData * poll_data; - gboolean fds_changed; - int i; + GHashTableIter h_iter; + PollData * poll_data; + gboolean fds_changed; + GPollFD new_fds_stack[300u / sizeof(GPollFD)]; + gs_free GPollFD *new_fds_heap = NULL; + GPollFD * new_fds; + guint new_fds_len; + guint new_fds_alloc; + guint i; _CTX_LOG("prepare..."); @@ -4806,30 +4858,44 @@ _ctx_integ_source_prepare(GSource *source, int *out_timeout) any_ready = g_main_context_prepare(ctx_src->context, &max_priority); - fds_arr_old = g_steal_pointer(&ctx_src->fds_arr); - fds_len_old = ctx_src->fds_len; + new_fds_alloc = NM_MAX(G_N_ELEMENTS(new_fds_stack), ctx_src->fds_len); - fds_allocated = NM_MAX(1, fds_len_old); /* there is at least the wakeup's FD */ - ctx_src->fds_arr = g_new(GPollFD, fds_allocated); + if (new_fds_alloc > G_N_ELEMENTS(new_fds_stack)) { + new_fds_heap = g_new(GPollFD, new_fds_alloc); + new_fds = new_fds_heap; + } else + new_fds = new_fds_stack; - while ((ctx_src->fds_len = g_main_context_query(ctx_src->context, - max_priority, - &timeout, - ctx_src->fds_arr, - fds_allocated)) - > fds_allocated) { - fds_allocated = ctx_src->fds_len; - g_free(ctx_src->fds_arr); - ctx_src->fds_arr = g_new(GPollFD, fds_allocated); + for (;;) { + int l; + + nm_assert(new_fds_alloc <= (guint) G_MAXINT); + + l = g_main_context_query(ctx_src->context, + max_priority, + &timeout, + new_fds, + (int) new_fds_alloc); + nm_assert(l >= 0); + + new_fds_len = (guint) l; + + if (G_LIKELY(new_fds_len <= new_fds_alloc)) + break; + + new_fds_alloc = new_fds_len; + g_free(new_fds_heap); + new_fds_heap = g_new(GPollFD, new_fds_alloc); + new_fds = new_fds_heap; } fds_changed = FALSE; - if (fds_len_old != ctx_src->fds_len) + if (new_fds_len != ctx_src->fds_len) fds_changed = TRUE; else { - for (i = 0; i < ctx_src->fds_len; i++) { - if (fds_arr_old[i].fd != ctx_src->fds_arr[i].fd - || fds_arr_old[i].events != ctx_src->fds_arr[i].events) { + for (i = 0; i < new_fds_len; i++) { + if (new_fds[i].fd != ctx_src->fds_arr[i].fd + || new_fds[i].events != ctx_src->fds_arr[i].events) { fds_changed = TRUE; break; } @@ -4837,6 +4903,13 @@ _ctx_integ_source_prepare(GSource *source, int *out_timeout) } if (G_UNLIKELY(fds_changed)) { + g_free(ctx_src->fds_arr); + ctx_src->fds_len = new_fds_len; + if (G_LIKELY(new_fds == new_fds_stack) || new_fds_alloc != new_fds_len) + ctx_src->fds_arr = nm_memdup(new_fds, sizeof(*new_fds) * new_fds_len); + else + ctx_src->fds_arr = g_steal_pointer(&new_fds_heap); + g_hash_table_iter_init(&h_iter, ctx_src->fds); while (g_hash_table_iter_next(&h_iter, (gpointer *) &poll_data, NULL)) poll_data->stale = TRUE; @@ -4971,10 +5044,12 @@ _ctx_integ_source_check(GSource *source) ctx_src->fds_arr[poll_data->idx.one].revents = revents; } + nm_assert(ctx_src->fds_len <= (guint) G_MAXINT); + some_ready = g_main_context_check(ctx_src->context, ctx_src->max_priority, ctx_src->fds_arr, - ctx_src->fds_len); + (int) ctx_src->fds_len); _CTX_LOG("check (some-ready=%d)...", some_ready); @@ -5088,6 +5163,31 @@ nm_utils_g_main_context_create_integrate_source(GMainContext *inner_context) return &ctx_src->source; } +/*****************************************************************************/ + +void +nm_utils_ifname_cpy(char *dst, const char *name) +{ + int i; + + g_return_if_fail(dst); + g_return_if_fail(name && name[0]); + + nm_assert(nm_utils_ifname_valid_kernel(name, NULL)); + + /* ensures NUL padding of the entire IFNAMSIZ buffer. */ + + for (i = 0; i < (int) IFNAMSIZ && name[i] != '\0'; i++) + dst[i] = name[i]; + + nm_assert(name[i] == '\0'); + + for (; i < (int) IFNAMSIZ; i++) + dst[i] = '\0'; +} + +/*****************************************************************************/ + gboolean nm_utils_ifname_valid_kernel(const char *name, GError **error) { diff --git a/shared/nm-glib-aux/nm-shared-utils.h b/shared/nm-glib-aux/nm-shared-utils.h index 23884a37..7d330458 100644 --- a/shared/nm-glib-aux/nm-shared-utils.h +++ b/shared/nm-glib-aux/nm-shared-utils.h @@ -1,4 +1,4 @@ -/* SPDX-License-Identifier: LGPL-2.1+ */ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ /* * Copyright (C) 2016 Red Hat, Inc. */ @@ -8,6 +8,18 @@ #include <netinet/in.h> +/*****************************************************************************/ + +/* An optional boolean (like NMTernary, with identical numerical + * enum values). Note that this enum type is _nm_packed! */ +typedef enum _nm_packed { + NM_OPTION_BOOL_DEFAULT = -1, + NM_OPTION_BOOL_FALSE = 0, + NM_OPTION_BOOL_TRUE = 1, +} NMOptionBool; + +/*****************************************************************************/ + static inline gboolean nm_is_ascii(char ch) { @@ -88,14 +100,25 @@ typedef struct { } NMEtherAddr; #define NM_ETHER_ADDR_FORMAT_STR "%02X:%02X:%02X:%02X:%02X:%02X" -#define NM_ETHER_ADDR_FORMAT_VAL(x) \ - (x).ether_addr_octet[0], (x).ether_addr_octet[1], (x).ether_addr_octet[2], \ - (x).ether_addr_octet[3], (x).ether_addr_octet[4], (x).ether_addr_octet[5] -#define NM_ETHER_ADDR_INIT(...) \ - { \ - .ether_addr_octet = {__VA_ARGS__}, \ + +#define NM_ETHER_ADDR_FORMAT_VAL(x) \ + (x)->ether_addr_octet[0], (x)->ether_addr_octet[1], (x)->ether_addr_octet[2], \ + (x)->ether_addr_octet[3], (x)->ether_addr_octet[4], (x)->ether_addr_octet[5] + +#define _NM_ETHER_ADDR_INIT(a0, a1, a2, a3, a4, a5) \ + { \ + .ether_addr_octet = { \ + (a0), \ + (a1), \ + (a2), \ + (a3), \ + (a4), \ + (a5), \ + }, \ } +#define NM_ETHER_ADDR_INIT(...) ((NMEtherAddr) _NM_ETHER_ADDR_INIT(__VA_ARGS__)) + static inline int nm_ether_addr_cmp(const NMEtherAddr *a, const NMEtherAddr *b) { @@ -308,6 +331,8 @@ nm_utils_is_separator(const char c) GBytes *nm_gbytes_get_empty(void); +GBytes *nm_g_bytes_new_from_str(const char *str); + static inline gboolean nm_gbytes_equal0(GBytes *a, GBytes *b) { @@ -318,6 +343,8 @@ gboolean nm_utils_gbytes_equal_mem(GBytes *bytes, gconstpointer mem_data, gsize GVariant *nm_utils_gbytes_to_variant_ay(GBytes *bytes); +GHashTable *nm_utils_strdict_clone(GHashTable *src); + GVariant *nm_utils_strdict_to_variant_ass(GHashTable *strdict); GVariant *nm_utils_strdict_to_variant_asv(GHashTable *strdict); @@ -672,7 +699,8 @@ nm_utils_escaped_tokens_options_escape_val(const char *val, char **out_to_free) /*****************************************************************************/ guint32 _nm_utils_ip4_prefix_to_netmask(guint32 prefix); -guint32 _nm_utils_ip4_get_default_prefix(guint32 ip); +guint32 _nm_utils_ip4_get_default_prefix0(in_addr_t ip); +guint32 _nm_utils_ip4_get_default_prefix(in_addr_t ip); gconstpointer nm_utils_ipx_address_clear_host_address(int family, gpointer dst, gconstpointer src, guint8 plen); @@ -758,8 +786,10 @@ typedef struct { { \ static const NMUtilsFlags2StrDesc descs[] = {__VA_ARGS__}; \ G_STATIC_ASSERT(sizeof(flags_type) <= sizeof(unsigned)); \ + \ return nm_utils_flags2str(descs, G_N_ELEMENTS(descs), flags, buf, len); \ - }; + } \ + _NM_DUMMY_STRUCT_FOR_TRAILING_SEMICOLON const char *nm_utils_flags2str(const NMUtilsFlags2StrDesc *descs, gsize n_descs, @@ -796,7 +826,8 @@ case v: \ g_snprintf(buf, len, "(%" int_fmt ")", val); \ } \ return buf; \ - } + } \ + _NM_DUMMY_STRUCT_FOR_TRAILING_SEMICOLON #define NM_UTILS_ENUM2STR_DEFINE(fcn_name, lookup_type, ...) \ NM_UTILS_ENUM2STR_DEFINE_FULL(fcn_name, lookup_type, "d", __VA_ARGS__) @@ -918,6 +949,8 @@ _nm_g_slice_free_fcn_define(1) _nm_g_slice_free_fcn_define(2) _nm_g_slice_free_f * error reason. Depending on the usage, this might indicate a bug because * usually the target object should stay alive as long as there are pending * operations. + * @NM_UTILS_ERROR_NOT_READY: the failure is related to being currently + * not ready to perform the operation. * * @NM_UTILS_ERROR_CONNECTION_AVAILABLE_INCOMPATIBLE: used for a very particular * purpose during nm_device_check_connection_compatible() to indicate that @@ -938,6 +971,7 @@ typedef enum { NM_UTILS_ERROR_UNKNOWN = 0, /*< nick=Unknown >*/ NM_UTILS_ERROR_CANCELLED_DISPOSING, /*< nick=CancelledDisposing >*/ NM_UTILS_ERROR_INVALID_ARGUMENT, /*< nick=InvalidArgument >*/ + NM_UTILS_ERROR_NOT_READY, /*< nick=NotReady >*/ /* the following codes have a special meaning and are exactly used for * nm_device_check_connection_compatible() and nm_device_check_connection_available(). @@ -961,6 +995,12 @@ typedef enum { #define NM_UTILS_ERROR (nm_utils_error_quark()) GQuark nm_utils_error_quark(void); +GQuark nm_manager_error_quark(void); +#define _NM_MANAGER_ERROR (nm_manager_error_quark()) + +#define _NM_MANAGER_ERROR_UNKNOWN_LOG_LEVEL 10 +#define _NM_MANAGER_ERROR_UNKNOWN_LOG_DOMAIN 11 + void nm_utils_error_set_cancelled(GError **error, gboolean is_disposing, const char *instance_name); static inline GError * @@ -1214,6 +1254,27 @@ nm_g_variant_is_of_type(GVariant *value, const GVariantType *type) return value && g_variant_is_of_type(value, type); } +static inline GVariant * +nm_g_variant_new_ay_inaddr(int addr_family, gconstpointer addr) +{ + return g_variant_new_fixed_array(G_VARIANT_TYPE_BYTE, + addr ?: &nm_ip_addr_zero, + nm_utils_addr_family_to_size(addr_family), + 1); +} + +static inline GVariant * +nm_g_variant_new_ay_in4addr(in_addr_t addr) +{ + return nm_g_variant_new_ay_inaddr(AF_INET, &addr); +} + +static inline GVariant * +nm_g_variant_new_ay_in6addr(const struct in6_addr *addr) +{ + return nm_g_variant_new_ay_inaddr(AF_INET6, addr); +} + static inline void nm_g_variant_builder_add_sv(GVariantBuilder *builder, const char *key, GVariant *val) { @@ -1304,6 +1365,59 @@ nm_g_source_attach(GSource *source, GMainContext *context) return source; } +static inline GSource * +nm_g_idle_add_source(GSourceFunc func, gpointer user_data) +{ + /* G convenience function to attach a new timeout source to the default GMainContext. + * In that sense it's very similar to g_idle_add() except that it returns a + * reference to the new source. */ + return nm_g_source_attach(nm_g_idle_source_new(G_PRIORITY_DEFAULT, func, user_data, NULL), + NULL); +} + +static inline GSource * +nm_g_timeout_add_source(guint timeout_msec, GSourceFunc func, gpointer user_data) +{ + /* G convenience function to attach a new timeout source to the default GMainContext. + * In that sense it's very similar to g_timeout_add() except that it returns a + * reference to the new source. */ + return nm_g_source_attach( + nm_g_timeout_source_new(timeout_msec, G_PRIORITY_DEFAULT, func, user_data, NULL), + NULL); +} + +static inline GSource * +nm_g_timeout_add_source_seconds(guint timeout_sec, GSourceFunc func, gpointer user_data) +{ + /* G convenience function to attach a new timeout source to the default GMainContext. + * In that sense it's very similar to g_timeout_add_seconds() except that it returns a + * reference to the new source. */ + return nm_g_source_attach( + nm_g_timeout_source_new_seconds(timeout_sec, G_PRIORITY_DEFAULT, func, user_data, NULL), + NULL); +} + +static inline GSource * +nm_g_timeout_add_source_approx(guint timeout_msec, + guint timeout_sec_threshold, + GSourceFunc func, + gpointer user_data) +{ + GSource *source; + + /* If timeout_msec is larger or equal than a threshold, then we use g_timeout_source_new_seconds() + * instead. */ + if (timeout_msec / 1000u >= timeout_sec_threshold) + source = nm_g_timeout_source_new_seconds(timeout_msec / 1000u, + G_PRIORITY_DEFAULT, + func, + user_data, + NULL); + else + source = nm_g_timeout_source_new(timeout_msec, G_PRIORITY_DEFAULT, func, user_data, NULL); + return nm_g_source_attach(source, NULL); +} + NM_AUTO_DEFINE_FCN0(GMainContext *, _nm_auto_unref_gmaincontext, g_main_context_unref); #define nm_auto_unref_gmaincontext nm_auto(_nm_auto_unref_gmaincontext) @@ -1434,6 +1548,8 @@ void nm_utils_named_value_list_sort(NMUtilsNamedValue *arr, GCompareDataFunc compare_func, gpointer user_data); +void nm_utils_named_value_clear_with_g_free(NMUtilsNamedValue *val); + /*****************************************************************************/ gpointer *nm_utils_hash_keys_to_array(GHashTable * hash, @@ -1457,13 +1573,18 @@ nm_utils_strdict_get_keys(const GHashTable *hash, gboolean sorted, guint *out_le gboolean nm_utils_hashtable_equal(const GHashTable *a, const GHashTable *b, - GCompareDataFunc cmp_values, - gpointer user_data); + gboolean treat_null_as_empty, + GEqualFunc equal_func); + +gboolean nm_utils_hashtable_cmp_equal(const GHashTable *a, + const GHashTable *b, + GCompareDataFunc cmp_values, + gpointer user_data); static inline gboolean nm_utils_hashtable_same_keys(const GHashTable *a, const GHashTable *b) { - return nm_utils_hashtable_equal(a, b, NULL, NULL); + return nm_utils_hashtable_cmp_equal(a, b, NULL, NULL); } int nm_utils_hashtable_cmp(const GHashTable *a, @@ -1508,6 +1629,13 @@ nm_g_array_len(const GArray *arr) return arr ? arr->len : 0u; } +static inline void +nm_g_array_unref(GArray *arr) +{ + if (arr) + g_array_unref(arr); +} + #define nm_g_array_append_new(arr, type) \ ({ \ GArray *const _arr = (arr); \ @@ -1522,6 +1650,55 @@ nm_g_array_len(const GArray *arr) /*****************************************************************************/ +static inline GPtrArray * +nm_g_ptr_array_ref(GPtrArray *arr) +{ + return arr ? g_ptr_array_ref(arr) : NULL; +} + +static inline void +nm_g_ptr_array_unref(GPtrArray *arr) +{ + if (arr) + g_ptr_array_unref(arr); +} + +#define nm_g_ptr_array_set(pdst, val) \ + ({ \ + GPtrArray **_pdst = (pdst); \ + GPtrArray * _val = (val); \ + gboolean _changed = FALSE; \ + \ + nm_assert(_pdst); \ + \ + if (*_pdst != _val) { \ + _nm_unused gs_unref_ptrarray GPtrArray *_old = *_pdst; \ + \ + *_pdst = nm_g_ptr_array_ref(_val); \ + _changed = TRUE; \ + } \ + _changed; \ + }) + +#define nm_g_ptr_array_set_take(pdst, val) \ + ({ \ + GPtrArray **_pdst = (pdst); \ + GPtrArray * _val = (val); \ + gboolean _changed = FALSE; \ + \ + nm_assert(_pdst); \ + \ + if (*_pdst != _val) { \ + _nm_unused gs_unref_ptrarray GPtrArray *_old = *_pdst; \ + \ + *_pdst = _val; \ + _changed = TRUE; \ + } else { \ + nm_g_ptr_array_unref(_val); \ + } \ + _changed; \ + }) + static inline guint nm_g_ptr_array_len(const GPtrArray *arr) { @@ -1571,6 +1748,19 @@ GPtrArray *_nm_g_ptr_array_copy(GPtrArray * array, /*****************************************************************************/ +static inline GHashTable * +nm_g_hash_table_ref(GHashTable *hash) +{ + return hash ? g_hash_table_ref(hash) : NULL; +} + +static inline void +nm_g_hash_table_unref(GHashTable *hash) +{ + if (hash) + g_hash_table_unref(hash); +} + static inline guint nm_g_hash_table_size(GHashTable *hash) { @@ -1614,27 +1804,16 @@ gssize nm_utils_array_find_binary_search(gconstpointer list, /*****************************************************************************/ -typedef gboolean (*NMUtilsHashTableEqualFunc)(gconstpointer a, gconstpointer b); - -gboolean nm_utils_hash_table_equal(const GHashTable * a, - const GHashTable * b, - gboolean treat_null_as_empty, - NMUtilsHashTableEqualFunc equal_func); - -/*****************************************************************************/ - void _nm_utils_strv_sort(const char **strv, gssize len); #define nm_utils_strv_sort(strv, len) _nm_utils_strv_sort(NM_CAST_STRV_MC(strv), len) int _nm_utils_strv_cmp_n(const char *const *strv1, gssize len1, const char *const *strv2, gssize len2); -static inline gboolean -_nm_utils_strv_equal(char **strv1, char **strv2) -{ - return _nm_utils_strv_cmp_n((const char *const *) strv1, -1, (const char *const *) strv2, -1) - == 0; -} +#define nm_utils_strv_cmp_n(strv1, len1, strv2, len2) \ + _nm_utils_strv_cmp_n(NM_CAST_STRV_CC(strv1), (len1), NM_CAST_STRV_CC(strv2), (len2)) + +#define nm_utils_strv_equal(strv1, strv2) (nm_utils_strv_cmp_n((strv1), -1, (strv2), -1) == 0) /*****************************************************************************/ @@ -1851,14 +2030,14 @@ nm_strv_ptrarray_contains(const GPtrArray *strv, const char *str) static inline int nm_strv_ptrarray_cmp(const GPtrArray *a, const GPtrArray *b) { - /* _nm_utils_strv_cmp_n() will treat NULL and empty arrays the same. + /* nm_utils_strv_cmp_n() will treat NULL and empty arrays the same. * That means, an empty strv array can both be represented by NULL * and an array of length zero. * If you need to distinguish between these case, do that yourself. */ - return _nm_utils_strv_cmp_n((const char *const *) nm_g_ptr_array_pdata(a), - nm_g_ptr_array_len(a), - (const char *const *) nm_g_ptr_array_pdata(b), - nm_g_ptr_array_len(b)); + return nm_utils_strv_cmp_n((const char *const *) nm_g_ptr_array_pdata(a), + nm_g_ptr_array_len(a), + (const char *const *) nm_g_ptr_array_pdata(b), + nm_g_ptr_array_len(b)); } /*****************************************************************************/ @@ -1985,6 +2164,24 @@ _nm_utils_hwaddr_aton(const char *asc, gpointer buffer, gsize buffer_length, gsi out_length); } +static inline guint8 * +_nm_utils_hwaddr_aton_exact(const char *asc, gpointer buffer, gsize buffer_length) +{ + g_return_val_if_fail(asc, NULL); + g_return_val_if_fail(buffer, NULL); + g_return_val_if_fail(buffer_length > 0, NULL); + + return nm_utils_hexstr2bin_full(asc, + FALSE, + TRUE, + FALSE, + ":-", + buffer_length, + buffer, + buffer_length, + NULL); +} + static inline const char * _nm_utils_hwaddr_ntoa(gconstpointer addr, gsize addr_len, @@ -2060,7 +2257,8 @@ _nm_utils_hwaddr_ntoa(gconstpointer addr, { \ unknown_val_cmd; \ } \ - } + } \ + _NM_DUMMY_STRUCT_FOR_TRAILING_SEMICOLON #define NM_UTILS_STRING_TABLE_LOOKUP_STRUCT_DEFINE(fcn_name, \ result_type, \ @@ -2130,10 +2328,31 @@ nm_utils_strdup_reset(char **dst, const char *src) return TRUE; } +static inline gboolean +nm_utils_strdup_reset_take(char **dst, char *src) +{ + char *old; + + nm_assert(dst); + nm_assert(src != *dst); + + if (nm_streq0(*dst, src)) { + if (src) + g_free(src); + return FALSE; + } + old = *dst; + *dst = src; + g_free(old); + return TRUE; +} + void nm_indirect_g_free(gpointer arg); /*****************************************************************************/ +void nm_utils_ifname_cpy(char *dst, const char *name); + typedef enum { NMU_IFACE_ANY, NMU_IFACE_KERNEL, diff --git a/shared/nm-glib-aux/nm-str-buf.h b/shared/nm-glib-aux/nm-str-buf.h index 062630a5..cb0d3fb1 100644 --- a/shared/nm-glib-aux/nm-str-buf.h +++ b/shared/nm-glib-aux/nm-str-buf.h @@ -1,4 +1,4 @@ -/* SPDX-License-Identifier: LGPL-2.1+ */ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ #ifndef __NM_STR_BUF_H__ #define __NM_STR_BUF_H__ @@ -31,7 +31,7 @@ typedef struct _NMStrBuf { /*****************************************************************************/ static inline void -_nm_str_buf_assert(NMStrBuf *strbuf) +_nm_str_buf_assert(const NMStrBuf *strbuf) { nm_assert(strbuf); nm_assert((!!strbuf->_priv_str) == (strbuf->_priv_allocated > 0)); @@ -268,7 +268,34 @@ nm_str_buf_append_required_delimiter(NMStrBuf *strbuf, char delimiter) } static inline void -nm_str_buf_reset(NMStrBuf *strbuf, const char *str) +nm_str_buf_append_dirty(NMStrBuf *strbuf, gsize len) +{ + _nm_str_buf_assert(strbuf); + + /* this append @len bytes to the buffer, but it does not + * initialize them! */ + if (len > 0) { + nm_str_buf_maybe_expand(strbuf, len, FALSE); + strbuf->_priv_len += len; + } +} + +static inline void +nm_str_buf_append_c_len(NMStrBuf *strbuf, char ch, gsize len) +{ + _nm_str_buf_assert(strbuf); + + if (len > 0) { + nm_str_buf_maybe_expand(strbuf, len, FALSE); + memset(&strbuf->_priv_str[strbuf->_priv_len], ch, len); + strbuf->_priv_len += len; + } +} + +/*****************************************************************************/ + +static inline NMStrBuf * +nm_str_buf_reset(NMStrBuf *strbuf) { _nm_str_buf_assert(strbuf); @@ -280,8 +307,7 @@ nm_str_buf_reset(NMStrBuf *strbuf, const char *str) strbuf->_priv_len = 0; } - if (str) - nm_str_buf_append(strbuf, str); + return strbuf; } /*****************************************************************************/ @@ -361,6 +387,31 @@ nm_str_buf_get_str_unsafe(NMStrBuf *strbuf) return strbuf->_priv_str; } +static inline char * +nm_str_buf_get_str_at_unsafe(NMStrBuf *strbuf, gsize index) +{ + _nm_str_buf_assert(strbuf); + + /* it is acceptable to ask for a pointer at the end of the buffer -- even + * if there is no data there. The caller is anyway required to take care + * of the length (that's the "unsafe" part), and in that case, the length + * is merely zero. */ + nm_assert(index <= strbuf->allocated); + + if (!strbuf->_priv_str) + return NULL; + + return &strbuf->_priv_str[index]; +} + +static inline char +nm_str_buf_get_char(const NMStrBuf *strbuf, gsize index) +{ + _nm_str_buf_assert(strbuf); + nm_assert(index < strbuf->allocated); + return strbuf->_priv_str[index]; +} + /** * nm_str_buf_finalize: * @strbuf: an initilized #NMStrBuf diff --git a/shared/nm-glib-aux/nm-time-utils.c b/shared/nm-glib-aux/nm-time-utils.c index 511363c3..df98176a 100644 --- a/shared/nm-glib-aux/nm-time-utils.c +++ b/shared/nm-glib-aux/nm-time-utils.c @@ -1,9 +1,9 @@ -/* SPDX-License-Identifier: LGPL-2.1+ */ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ /* * Copyright (C) 2018 Red Hat, Inc. */ -#include "nm-default.h" +#include "nm-glib-aux/nm-default-glib-i18n-lib.h" #include "nm-time-utils.h" diff --git a/shared/nm-glib-aux/nm-time-utils.h b/shared/nm-glib-aux/nm-time-utils.h index 77aeae5b..3c3e935f 100644 --- a/shared/nm-glib-aux/nm-time-utils.h +++ b/shared/nm-glib-aux/nm-time-utils.h @@ -1,4 +1,4 @@ -/* SPDX-License-Identifier: LGPL-2.1+ */ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ /* * Copyright (C) 2018 Red Hat, Inc. */ diff --git a/shared/nm-glib-aux/nm-value-type.h b/shared/nm-glib-aux/nm-value-type.h index c07f1104..f9edebdb 100644 --- a/shared/nm-glib-aux/nm-value-type.h +++ b/shared/nm-glib-aux/nm-value-type.h @@ -1,4 +1,4 @@ -/* SPDX-License-Identifier: LGPL-2.1+ */ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ /* * Copyright (C) 2019 Red Hat, Inc. */ diff --git a/shared/nm-glib-aux/tests/meson.build b/shared/nm-glib-aux/tests/meson.build index 8136eff9..464f4e7c 100644 --- a/shared/nm-glib-aux/tests/meson.build +++ b/shared/nm-glib-aux/tests/meson.build @@ -1,13 +1,12 @@ -# SPDX-License-Identifier: LGPL-2.1+ +# SPDX-License-Identifier: LGPL-2.1-or-later exe = executable( 'test-shared-general', 'test-shared-general.c', c_args: [ - '-DNETWORKMANAGER_COMPILATION_TEST', - '-DNETWORKMANAGER_COMPILATION=(NM_NETWORKMANAGER_COMPILATION_GLIB|NM_NETWORKMANAGER_COMPILATION_WITH_GLIB_I18N_PROG)', + '-DG_LOG_DOMAIN="test"', ], - dependencies: libnm_utils_base_dep, + dependencies: libnm_glib_aux_dep, link_with: libnm_systemd_logging_stub, ) @@ -23,11 +22,10 @@ if jansson_dep.found() 'test-json-aux', 'test-json-aux.c', c_args: [ - '-DNETWORKMANAGER_COMPILATION_TEST', - '-DNETWORKMANAGER_COMPILATION=(NM_NETWORKMANAGER_COMPILATION_GLIB|NM_NETWORKMANAGER_COMPILATION_WITH_GLIB_I18N_PROG)', + '-DG_LOG_DOMAIN="test"', ], dependencies: [ - libnm_utils_base_dep, + libnm_glib_aux_dep, jansson_dep, dl_dep, ], diff --git a/shared/nm-glib-aux/tests/test-json-aux.c b/shared/nm-glib-aux/tests/test-json-aux.c index 5331c015..b07d673f 100644 --- a/shared/nm-glib-aux/tests/test-json-aux.c +++ b/shared/nm-glib-aux/tests/test-json-aux.c @@ -1,8 +1,6 @@ -/* SPDX-License-Identifier: LGPL-2.1+ */ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ -#define NM_TEST_UTILS_NO_LIBNM 1 - -#include "nm-default.h" +#include "nm-glib-aux/nm-default-glib-i18n-prog.h" #include <jansson.h> diff --git a/shared/nm-glib-aux/tests/test-shared-general.c b/shared/nm-glib-aux/tests/test-shared-general.c index 84e0a6f0..f42c6fb1 100644 --- a/shared/nm-glib-aux/tests/test-shared-general.c +++ b/shared/nm-glib-aux/tests/test-shared-general.c @@ -1,11 +1,9 @@ -/* SPDX-License-Identifier: LGPL-2.1+ */ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ /* * Copyright (C) 2018 Red Hat, Inc. */ -#define NM_TEST_UTILS_NO_LIBNM 1 - -#include "nm-default.h" +#include "nm-glib-aux/nm-default-glib-i18n-prog.h" #include "nm-std-aux/unaligned.h" #include "nm-glib-aux/nm-random-utils.h" @@ -364,8 +362,8 @@ test_strv_cmp(void) _strv_cmp_fuzz_input((a1), _l1, &_a1_free_shallow, &_a1_free_deep, &_a1, &_a1x); \ _strv_cmp_fuzz_input((a2), _l2, &_a2_free_shallow, &_a2_free_deep, &_a2, &_a2x); \ \ - _c1 = _nm_utils_strv_cmp_n(_a1, _l1, _a2, _l2); \ - _c2 = _nm_utils_strv_cmp_n(_a2, _l2, _a1, _l1); \ + _c1 = nm_utils_strv_cmp_n(_a1, _l1, _a2, _l2); \ + _c2 = nm_utils_strv_cmp_n(_a2, _l2, _a1, _l1); \ if (equal) { \ g_assert_cmpint(_c1, ==, 0); \ g_assert_cmpint(_c2, ==, 0); \ @@ -376,8 +374,8 @@ test_strv_cmp(void) \ /* Compare with self. _strv_cmp_fuzz_input() randomly swapped the arguments (_a1 and _a1x). * Either way, the arrays must compare equal to their semantically equal alternative. */ \ - g_assert_cmpint(_nm_utils_strv_cmp_n(_a1, _l1, _a1x, _l1), ==, 0); \ - g_assert_cmpint(_nm_utils_strv_cmp_n(_a2, _l2, _a2x, _l2), ==, 0); \ + g_assert_cmpint(nm_utils_strv_cmp_n(_a1, _l1, _a1x, _l1), ==, 0); \ + g_assert_cmpint(nm_utils_strv_cmp_n(_a2, _l2, _a2x, _l2), ==, 0); \ \ _strv_cmp_free_deep(_a1_free_deep, _l1); \ _strv_cmp_free_deep(_a2_free_deep, _l2); \ @@ -632,9 +630,10 @@ static NM_UTILS_STRING_TABLE_LOOKUP_DEFINE( {"0", 0}, {"1", 1}, {"2", 2}, - {"3", 3}, ) + {"3", 3}, ); - static void test_string_table_lookup(void) +static void +test_string_table_lookup(void) { const char *const args[] = { NULL, @@ -762,25 +761,44 @@ test_nm_utils_get_next_realloc_size(void) } { - const gsize requested = requested0; - const gsize reserved_true = nm_utils_get_next_realloc_size(TRUE, requested); - const gsize reserved_false = nm_utils_get_next_realloc_size(FALSE, requested); + const gsize requested = requested0; + gsize reserved_true; + gsize reserved_false; + bool truncated_true = FALSE; + bool truncated_false = FALSE; + + if (sizeof(gsize) > 4 && requested > SIZE_MAX / 2u - 24u) { + reserved_false = G_MAXSSIZE; + truncated_false = TRUE; + } else + reserved_false = nm_utils_get_next_realloc_size(FALSE, requested); + + if (sizeof(gsize) > 4 && requested > SIZE_MAX - 0x1000u - 24u) { + reserved_true = G_MAXSSIZE; + truncated_true = TRUE; + } else + reserved_true = nm_utils_get_next_realloc_size(TRUE, requested); g_assert_cmpuint(reserved_true, >, 0); g_assert_cmpuint(reserved_false, >, 0); - g_assert_cmpuint(reserved_true, >=, requested); - g_assert_cmpuint(reserved_false, >=, requested); - g_assert_cmpuint(reserved_false, >=, reserved_true); + if (!truncated_true) + g_assert_cmpuint(reserved_true, >=, requested); + if (!truncated_false) + g_assert_cmpuint(reserved_false, >=, requested); + if (!truncated_true && !truncated_false) + g_assert_cmpuint(reserved_false, >=, reserved_true); if (i < G_N_ELEMENTS(test_data)) { - g_assert_cmpuint(reserved_true, ==, test_data[i].reserved_true); - g_assert_cmpuint(reserved_false, ==, test_data[i].reserved_false); + if (!truncated_true) + g_assert_cmpuint(reserved_true, ==, test_data[i].reserved_true); + if (!truncated_false) + g_assert_cmpuint(reserved_false, ==, test_data[i].reserved_false); } /* reserved_false is generally the next power of two - 24. */ if (reserved_false == G_MAXSIZE) g_assert_cmpuint(requested, >, G_MAXSIZE / 2u - 24u); - else { + else if (!reserved_false) { g_assert_cmpuint(reserved_false, <=, G_MAXSIZE - 24u); if (reserved_false >= 40) { const gsize _pow2 = reserved_false + 24u; @@ -800,7 +818,7 @@ test_nm_utils_get_next_realloc_size(void) /* reserved_true is generally the next 4k border - 24. */ if (reserved_true == G_MAXSIZE) g_assert_cmpuint(requested, >, G_MAXSIZE - 0x1000u - 24u); - else { + else if (!truncated_true) { g_assert_cmpuint(reserved_true, <=, G_MAXSIZE - 24u); if (reserved_true > 8168u) { const gsize page_border = reserved_true + 24u; @@ -951,10 +969,10 @@ again: else g_assert(!data); - g_assert(_nm_utils_strv_cmp_n((const char *const *) strv->pdata, - strv->len, - (const char *const *) strv2->pdata, - strv2->len) + g_assert(nm_utils_strv_cmp_n((const char *const *) strv->pdata, + strv->len, + (const char *const *) strv2->pdata, + strv2->len) == 0); } } @@ -1050,7 +1068,7 @@ test_strv_dup_packed(void) g_assert(strv_cpy); g_assert(NM_PTRARRAY_LEN(strv_cpy) == strv_len); if (strv_cpy) - g_assert(_nm_utils_strv_equal((char **) strv_cpy, (char **) strv_src)); + g_assert(nm_utils_strv_equal(strv_cpy, strv_src)); } } @@ -1189,8 +1207,8 @@ test_utils_hashtable_cmp(void) } g_assert(nm_utils_hashtable_same_keys(h1, h2)); - g_assert(nm_utils_hashtable_equal(h1, h2, NULL, NULL)); - g_assert(nm_utils_hashtable_equal(h1, h2, func_val_cmp, NULL)); + g_assert(nm_utils_hashtable_cmp_equal(h1, h2, NULL, NULL)); + g_assert(nm_utils_hashtable_cmp_equal(h1, h2, func_val_cmp, NULL)); g_assert(nm_utils_hashtable_cmp(h1, h2, FALSE, func_key_cmp, NULL, NULL) == 0); g_assert(nm_utils_hashtable_cmp(h1, h2, TRUE, func_key_cmp, NULL, NULL) == 0); g_assert(nm_utils_hashtable_cmp(h1, h2, FALSE, func_key_cmp, func_val_cmp, NULL) == 0); @@ -1220,16 +1238,16 @@ again: if (has_same_keys) { g_assert(nm_utils_hashtable_same_keys(h1, h2)); - g_assert(nm_utils_hashtable_equal(h1, h2, NULL, NULL)); + g_assert(nm_utils_hashtable_cmp_equal(h1, h2, NULL, NULL)); g_assert(nm_utils_hashtable_cmp(h1, h2, FALSE, func_key_cmp, NULL, NULL) == 0); g_assert(nm_utils_hashtable_cmp(h1, h2, TRUE, func_key_cmp, NULL, NULL) == 0); } else { g_assert(!nm_utils_hashtable_same_keys(h1, h2)); - g_assert(!nm_utils_hashtable_equal(h1, h2, NULL, NULL)); + g_assert(!nm_utils_hashtable_cmp_equal(h1, h2, NULL, NULL)); g_assert(nm_utils_hashtable_cmp(h1, h2, FALSE, func_key_cmp, NULL, NULL) != 0); g_assert(nm_utils_hashtable_cmp(h1, h2, TRUE, func_key_cmp, NULL, NULL) != 0); } - g_assert(!nm_utils_hashtable_equal(h1, h2, func_val_cmp, NULL)); + g_assert(!nm_utils_hashtable_cmp_equal(h1, h2, func_val_cmp, NULL)); g_assert(nm_utils_hashtable_cmp(h1, h2, FALSE, func_key_cmp, func_val_cmp, NULL) != 0); g_assert(nm_utils_hashtable_cmp(h1, h2, TRUE, func_key_cmp, func_val_cmp, NULL) != 0); } diff --git a/shared/nm-log-core/nm-logging.c b/shared/nm-log-core/nm-logging.c new file mode 100644 index 00000000..cf3c3a86 --- /dev/null +++ b/shared/nm-log-core/nm-logging.c @@ -0,0 +1,1035 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright (C) 2006 - 2012 Red Hat, Inc. + * Copyright (C) 2006 - 2008 Novell, Inc. + */ + +#include "nm-glib-aux/nm-default-glib-i18n-lib.h" + +#include "nm-logging.h" + +#include <dlfcn.h> +#include <syslog.h> +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <sys/wait.h> +#include <sys/stat.h> +#include <strings.h> + +#if SYSTEMD_JOURNAL + #define SD_JOURNAL_SUPPRESS_LOCATION + #include <systemd/sd-journal.h> +#endif + +#include "nm-glib-aux/nm-logging-base.h" +#include "nm-glib-aux/nm-time-utils.h" + +/*****************************************************************************/ + +/* Notes about thread-safety: + * + * NetworkManager generally is single-threaded and uses a (GLib) mainloop. + * However, nm-logging is in parts thread-safe. That means: + * + * - functions that configure logging (nm_logging_init(), nm_logging_setup()) and + * most other functions MUST be called only from the main-thread. These functions + * are expected to be called infrequently, so they may or may not use a mutex + * (but the overhead is negligible here). + * + * - functions that do the actual logging logging (nm_log(), nm_logging_enabled()) are + * thread-safe and may be used from multiple threads. + * - When called from the not-main-thread, @mt_require_locking must be set to %TRUE. + * In this case, a Mutex will be used for accessing the global state. + * - When called from the main-thread, they may optionally pass @mt_require_locking %FALSE. + * This avoids extra locking and is in particular interesting for nm_logging_enabled(), + * which is expected to be called frequently and from the main-thread. + * + * Note that the logging macros honor %NM_THREAD_SAFE_ON_MAIN_THREAD define, to automatically + * set @mt_require_locking. That means, by default %NM_THREAD_SAFE_ON_MAIN_THREAD is "1", + * and code that only runs on the main-thread (which is the majority), can get away + * without locking. + */ + +/*****************************************************************************/ + +G_STATIC_ASSERT(LOG_EMERG == 0); +G_STATIC_ASSERT(LOG_ALERT == 1); +G_STATIC_ASSERT(LOG_CRIT == 2); +G_STATIC_ASSERT(LOG_ERR == 3); +G_STATIC_ASSERT(LOG_WARNING == 4); +G_STATIC_ASSERT(LOG_NOTICE == 5); +G_STATIC_ASSERT(LOG_INFO == 6); +G_STATIC_ASSERT(LOG_DEBUG == 7); + +/* We have more then 32 logging domains. Assert that it compiles to a 64 bit sized enum */ +G_STATIC_ASSERT(sizeof(NMLogDomain) >= sizeof(guint64)); + +/* Combined domains */ +#define LOGD_ALL_STRING "ALL" +#define LOGD_DEFAULT_STRING "DEFAULT" +#define LOGD_DHCP_STRING "DHCP" +#define LOGD_IP_STRING "IP" + +/*****************************************************************************/ + +typedef enum { + LOG_BACKEND_GLIB, + LOG_BACKEND_SYSLOG, + LOG_BACKEND_JOURNAL, +} LogBackend; + +typedef struct { + NMLogDomain num; + const char *name; +} LogDesc; + +typedef struct { + char *logging_domains_to_string; +} GlobalMain; + +typedef struct { + NMLogLevel log_level; + bool uses_syslog : 1; + bool init_pre_done : 1; + bool init_done : 1; + bool debug_stderr : 1; + const char *prefix; + const char *syslog_identifier; + + /* before we setup syslog (during start), the backend defaults to GLIB, meaning: + * we use g_log() for all logging. At that point, the application is not yet supposed + * to do any logging and doing so indicates a bug. + * + * Afterwards, the backend is either SYSLOG or JOURNAL. From that point, also + * g_log() is redirected to this backend via a logging handler. */ + LogBackend log_backend; +} Global; + +/*****************************************************************************/ + +G_LOCK_DEFINE_STATIC(log); + +/* This data must only be accessed from the main-thread (and as + * such does not need any lock). */ +static GlobalMain gl_main = {}; + +static union { + /* a union with an immutable and a mutable alias for the Global. + * Since nm-logging must be thread-safe, we must take care at which + * places we only read value ("imm") and where we modify them ("mut"). */ + Global mut; + const Global imm; +} gl = { + .imm = + { + /* nm_logging_setup ("INFO", LOGD_DEFAULT_STRING, NULL, NULL); */ + .log_level = LOGL_INFO, + .log_backend = LOG_BACKEND_GLIB, + .syslog_identifier = "SYSLOG_IDENTIFIER=" G_LOG_DOMAIN, + .prefix = "", + }, +}; + +NMLogDomain _nm_logging_enabled_state[_LOGL_N_REAL] = { + /* nm_logging_setup ("INFO", LOGD_DEFAULT_STRING, NULL, NULL); + * + * Note: LOGD_VPN_PLUGIN is special and must be disabled for + * DEBUG and TRACE levels. */ + [LOGL_INFO] = LOGD_DEFAULT, + [LOGL_WARN] = LOGD_DEFAULT, + [LOGL_ERR] = LOGD_DEFAULT, +}; + +/*****************************************************************************/ + +static const LogDesc domain_desc[] = { + {LOGD_PLATFORM, "PLATFORM"}, + {LOGD_RFKILL, "RFKILL"}, + {LOGD_ETHER, "ETHER"}, + {LOGD_WIFI, "WIFI"}, + {LOGD_BT, "BT"}, + {LOGD_MB, "MB"}, + {LOGD_DHCP4, "DHCP4"}, + {LOGD_DHCP6, "DHCP6"}, + {LOGD_PPP, "PPP"}, + {LOGD_WIFI_SCAN, "WIFI_SCAN"}, + {LOGD_IP4, "IP4"}, + {LOGD_IP6, "IP6"}, + {LOGD_AUTOIP4, "AUTOIP4"}, + {LOGD_DNS, "DNS"}, + {LOGD_VPN, "VPN"}, + {LOGD_SHARING, "SHARING"}, + {LOGD_SUPPLICANT, "SUPPLICANT"}, + {LOGD_AGENTS, "AGENTS"}, + {LOGD_SETTINGS, "SETTINGS"}, + {LOGD_SUSPEND, "SUSPEND"}, + {LOGD_CORE, "CORE"}, + {LOGD_DEVICE, "DEVICE"}, + {LOGD_OLPC, "OLPC"}, + {LOGD_INFINIBAND, "INFINIBAND"}, + {LOGD_FIREWALL, "FIREWALL"}, + {LOGD_ADSL, "ADSL"}, + {LOGD_BOND, "BOND"}, + {LOGD_VLAN, "VLAN"}, + {LOGD_BRIDGE, "BRIDGE"}, + {LOGD_DBUS_PROPS, "DBUS_PROPS"}, + {LOGD_TEAM, "TEAM"}, + {LOGD_CONCHECK, "CONCHECK"}, + {LOGD_DCB, "DCB"}, + {LOGD_DISPATCH, "DISPATCH"}, + {LOGD_AUDIT, "AUDIT"}, + {LOGD_SYSTEMD, "SYSTEMD"}, + {LOGD_VPN_PLUGIN, "VPN_PLUGIN"}, + {LOGD_PROXY, "PROXY"}, + {0}, +}; + +/*****************************************************************************/ + +static char *_domains_to_string(gboolean include_level_override, + NMLogLevel log_level, + const NMLogDomain log_state[static _LOGL_N_REAL]); + +/*****************************************************************************/ + +static gboolean +_syslog_identifier_valid_domain(const char *domain) +{ + char c; + + if (!domain || !domain[0]) + return FALSE; + + /* we pass the syslog identifier as format string. No funny stuff. */ + + for (; (c = domain[0]); domain++) { + if ((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || (c >= '0' && c <= '9') + || NM_IN_SET(c, '-', '_')) + continue; + return FALSE; + } + return TRUE; +} + +static gboolean +_syslog_identifier_assert(const char *syslog_identifier) +{ + g_assert(syslog_identifier); + g_assert(g_str_has_prefix(syslog_identifier, "SYSLOG_IDENTIFIER=")); + g_assert(_syslog_identifier_valid_domain(&syslog_identifier[NM_STRLEN("SYSLOG_IDENTIFIER=")])); + return TRUE; +} + +static const char * +syslog_identifier_domain(const char *syslog_identifier) +{ + nm_assert(_syslog_identifier_assert(syslog_identifier)); + return &syslog_identifier[NM_STRLEN("SYSLOG_IDENTIFIER=")]; +} + +#if SYSTEMD_JOURNAL +static const char * +syslog_identifier_full(const char *syslog_identifier) +{ + nm_assert(_syslog_identifier_assert(syslog_identifier)); + return &syslog_identifier[0]; +} +#endif + +/*****************************************************************************/ + +static gboolean +match_log_level(const char *level, NMLogLevel *out_level, GError **error) +{ + if (_nm_log_parse_level(level, out_level)) + return TRUE; + + g_set_error(error, + _NM_MANAGER_ERROR, + _NM_MANAGER_ERROR_UNKNOWN_LOG_LEVEL, + _("Unknown log level '%s'"), + level); + return FALSE; +} + +gboolean +nm_logging_setup(const char *level, const char *domains, char **bad_domains, GError **error) +{ + GString * unrecognized = NULL; + NMLogDomain cur_log_state[_LOGL_N_REAL]; + NMLogDomain new_log_state[_LOGL_N_REAL]; + NMLogLevel cur_log_level; + NMLogLevel new_log_level; + gs_free const char **domains_v = NULL; + gsize i_d; + int i; + gboolean had_platform_debug; + gs_free char * domains_free = NULL; + + NM_ASSERT_ON_MAIN_THREAD(); + + g_return_val_if_fail(!bad_domains || !*bad_domains, FALSE); + g_return_val_if_fail(!error || !*error, FALSE); + + cur_log_level = gl.imm.log_level; + memcpy(cur_log_state, _nm_logging_enabled_state, sizeof(cur_log_state)); + + new_log_level = cur_log_level; + + if (!domains || !*domains) { + domains_free = _domains_to_string(FALSE, cur_log_level, cur_log_state); + domains = domains_free; + } + + for (i = 0; i < G_N_ELEMENTS(new_log_state); i++) + new_log_state[i] = 0; + + if (level && *level) { + if (!match_log_level(level, &new_log_level, error)) + return FALSE; + if (new_log_level == _LOGL_KEEP) { + new_log_level = cur_log_level; + for (i = 0; i < G_N_ELEMENTS(new_log_state); i++) + new_log_state[i] = cur_log_state[i]; + } + } + + domains_v = nm_utils_strsplit_set(domains, ", "); + for (i_d = 0; domains_v && domains_v[i_d]; i_d++) { + const char * s = domains_v[i_d]; + const char * p; + const LogDesc *diter; + NMLogLevel domain_log_level; + NMLogDomain bits; + + /* LOGD_VPN_PLUGIN is protected, that is, when setting ALL or DEFAULT, + * it does not enable the verbose levels DEBUG and TRACE, because that + * may expose sensitive data. */ + NMLogDomain protect = LOGD_NONE; + + p = strchr(s, ':'); + if (p) { + *((char *) p) = '\0'; + if (!match_log_level(p + 1, &domain_log_level, error)) + return FALSE; + } else + domain_log_level = new_log_level; + + bits = 0; + + if (domains_free) { + /* The caller didn't provide any domains to set (`nmcli general logging level DEBUG`). + * We reset all domains that were previously set, but we still want to protect + * VPN_PLUGIN domain. */ + protect = LOGD_VPN_PLUGIN; + } + + /* Check for combined domains */ + if (!g_ascii_strcasecmp(s, LOGD_ALL_STRING)) { + bits = LOGD_ALL; + protect = LOGD_VPN_PLUGIN; + } else if (!g_ascii_strcasecmp(s, LOGD_DEFAULT_STRING)) { + bits = LOGD_DEFAULT; + protect = LOGD_VPN_PLUGIN; + } else if (!g_ascii_strcasecmp(s, LOGD_DHCP_STRING)) + bits = LOGD_DHCP; + else if (!g_ascii_strcasecmp(s, LOGD_IP_STRING)) + bits = LOGD_IP; + + /* Check for compatibility domains */ + else if (!g_ascii_strcasecmp(s, "HW")) + bits = LOGD_PLATFORM; + else if (!g_ascii_strcasecmp(s, "WIMAX")) + continue; + + else { + for (diter = &domain_desc[0]; diter->name; diter++) { + if (!g_ascii_strcasecmp(diter->name, s)) { + bits = diter->num; + break; + } + } + + if (!bits) { + if (!bad_domains) { + g_set_error(error, + _NM_MANAGER_ERROR, + _NM_MANAGER_ERROR_UNKNOWN_LOG_DOMAIN, + _("Unknown log domain '%s'"), + s); + return FALSE; + } + + if (unrecognized) + g_string_append(unrecognized, ", "); + else + unrecognized = g_string_new(NULL); + g_string_append(unrecognized, s); + continue; + } + } + + if (domain_log_level == _LOGL_KEEP) { + for (i = 0; i < G_N_ELEMENTS(new_log_state); i++) + new_log_state[i] = (new_log_state[i] & ~bits) | (cur_log_state[i] & bits); + } else { + for (i = 0; i < G_N_ELEMENTS(new_log_state); i++) { + if (i < domain_log_level) + new_log_state[i] &= ~bits; + else { + new_log_state[i] |= bits; + if ((protect & bits) && i < LOGL_INFO) + new_log_state[i] &= ~protect; + } + } + } + } + + nm_clear_g_free(&gl_main.logging_domains_to_string); + + had_platform_debug = _nm_logging_enabled_lockfree(LOGL_DEBUG, LOGD_PLATFORM); + + G_LOCK(log); + + gl.mut.log_level = new_log_level; + for (i = 0; i < G_N_ELEMENTS(new_log_state); i++) + _nm_logging_enabled_state[i] = new_log_state[i]; + + G_UNLOCK(log); + + if (had_platform_debug && !_nm_logging_enabled_lockfree(LOGL_DEBUG, LOGD_PLATFORM)) { + /* when debug logging is enabled, platform will cache all access to + * sysctl. When the user disables debug-logging, we want to clear that + * cache right away. */ + _nm_logging_clear_platform_logging_cache(); + } + + if (unrecognized) + *bad_domains = g_string_free(unrecognized, FALSE); + + return TRUE; +} + +const char * +nm_logging_level_to_string(void) +{ + NM_ASSERT_ON_MAIN_THREAD(); + + return level_desc[gl.imm.log_level].name; +} + +const char * +nm_logging_all_levels_to_string(void) +{ + static GString *str; + + if (G_UNLIKELY(!str)) { + int i; + + str = g_string_new(NULL); + for (i = 0; i < G_N_ELEMENTS(level_desc); i++) { + if (str->len) + g_string_append_c(str, ','); + g_string_append(str, level_desc[i].name); + } + } + + return str->str; +} + +const char * +nm_logging_domains_to_string(void) +{ + NM_ASSERT_ON_MAIN_THREAD(); + + if (G_UNLIKELY(!gl_main.logging_domains_to_string)) { + gl_main.logging_domains_to_string = + _domains_to_string(TRUE, gl.imm.log_level, _nm_logging_enabled_state); + } + + return gl_main.logging_domains_to_string; +} + +static char * +_domains_to_string(gboolean include_level_override, + NMLogLevel log_level, + const NMLogDomain log_state[static _LOGL_N_REAL]) +{ + const LogDesc *diter; + GString * str; + int i; + + /* We don't just return g_strdup() the logging domains that were set during + * nm_logging_setup(), because we want to expand "DEFAULT" and "ALL". + */ + + str = g_string_sized_new(75); + for (diter = &domain_desc[0]; diter->name; diter++) { + /* If it's set for any lower level, it will also be set for LOGL_ERR */ + if (!(diter->num & log_state[LOGL_ERR])) + continue; + + if (str->len) + g_string_append_c(str, ','); + g_string_append(str, diter->name); + + if (!include_level_override) + continue; + + /* Check if it's logging at a lower level than the default. */ + for (i = 0; i < log_level; i++) { + if (diter->num & log_state[i]) { + g_string_append_printf(str, ":%s", level_desc[i].name); + break; + } + } + /* Check if it's logging at a higher level than the default. */ + if (!(diter->num & log_state[log_level])) { + for (i = log_level + 1; i < _LOGL_N_REAL; i++) { + if (diter->num & log_state[i]) { + g_string_append_printf(str, ":%s", level_desc[i].name); + break; + } + } + } + } + return g_string_free(str, FALSE); +} + +static char _all_logging_domains_to_str[273]; + +const char * +nm_logging_all_domains_to_string(void) +{ + static const char *volatile str = NULL; + const char *s; + +again: + s = g_atomic_pointer_get(&str); + if (G_UNLIKELY(!s)) { + static gsize once = 0; + const LogDesc *diter; + gsize buf_l; + char * buf_p; + + if (!g_once_init_enter(&once)) + goto again; + + buf_p = _all_logging_domains_to_str; + buf_l = sizeof(_all_logging_domains_to_str); + + nm_utils_strbuf_append_str(&buf_p, &buf_l, LOGD_DEFAULT_STRING); + for (diter = &domain_desc[0]; diter->name; diter++) { + nm_utils_strbuf_append_c(&buf_p, &buf_l, ','); + nm_utils_strbuf_append_str(&buf_p, &buf_l, diter->name); + if (diter->num == LOGD_DHCP6) + nm_utils_strbuf_append_str(&buf_p, &buf_l, "," LOGD_DHCP_STRING); + else if (diter->num == LOGD_IP6) + nm_utils_strbuf_append_str(&buf_p, &buf_l, "," LOGD_IP_STRING); + } + nm_utils_strbuf_append_str(&buf_p, &buf_l, LOGD_ALL_STRING); + + /* Did you modify the logging domains (or their names)? Adjust the size of + * _all_logging_domains_to_str buffer above to have the exact size. */ + nm_assert(strlen(_all_logging_domains_to_str) == sizeof(_all_logging_domains_to_str) - 1); + nm_assert(buf_l == 1); + + s = _all_logging_domains_to_str; + g_atomic_pointer_set(&str, s); + g_once_init_leave(&once, 1); + } + + return s; +} + +/** + * nm_logging_get_level: + * @domain: find the lowest enabled logging level for the + * given domain. If this is a set of multiple + * domains, the most verbose level will be returned. + * + * Returns: the lowest (most verbose) logging level for the + * give @domain, or %_LOGL_OFF if it is disabled. + **/ +NMLogLevel +nm_logging_get_level(NMLogDomain domain) +{ + NMLogLevel sl = _LOGL_OFF; + + G_STATIC_ASSERT(LOGL_TRACE == 0); + while (sl > LOGL_TRACE && _nm_logging_enabled_lockfree(sl - 1, domain)) + sl--; + return sl; +} + +gboolean +_nm_logging_enabled_locking(NMLogLevel level, NMLogDomain domain) +{ + gboolean v; + + G_LOCK(log); + v = _nm_logging_enabled_lockfree(level, domain); + G_UNLOCK(log); + return v; +} + +gboolean +_nm_log_enabled_impl(gboolean mt_require_locking, NMLogLevel level, NMLogDomain domain) +{ + return nm_logging_enabled_mt(mt_require_locking, level, domain); +} + +#if SYSTEMD_JOURNAL +static void +_iovec_set(struct iovec *iov, const void *str, gsize len) +{ + iov->iov_base = (void *) str; + iov->iov_len = len; +} + +static void +_iovec_set_string(struct iovec *iov, const char *str) +{ + _iovec_set(iov, str, strlen(str)); +} + + #define _iovec_set_string_literal(iov, str) _iovec_set((iov), "" str "", NM_STRLEN(str)) + +_nm_printf(3, 4) static void _iovec_set_format(struct iovec *iov, + char ** iov_free, + const char * format, + ...) +{ + va_list ap; + char * str; + + va_start(ap, format); + str = g_strdup_vprintf(format, ap); + va_end(ap); + + _iovec_set_string(iov, str); + *iov_free = str; +} + + #define _iovec_set_format_a(iov, reserve_extra, format, ...) \ + G_STMT_START \ + { \ + const gsize _size = (reserve_extra) + (NM_STRLEN(format) + 3); \ + char *const _buf = g_alloca(_size); \ + int _len; \ + \ + G_STATIC_ASSERT_EXPR((reserve_extra) + (NM_STRLEN(format) + 3) <= 96); \ + \ + _len = g_snprintf(_buf, _size, "" format "", ##__VA_ARGS__); \ + \ + nm_assert(_len >= 0); \ + nm_assert(_len < _size); \ + nm_assert(_len == strlen(_buf)); \ + \ + _iovec_set((iov), _buf, _len); \ + } \ + G_STMT_END + + #define _iovec_set_format_str_a(iov, max_str_len, format, str_arg) \ + G_STMT_START \ + { \ + const char *_str_arg = (str_arg); \ + \ + nm_assert(_str_arg &&strlen(_str_arg) < (max_str_len)); \ + _iovec_set_format_a((iov), (max_str_len), format, str_arg); \ + } \ + G_STMT_END + +#endif + +void +_nm_log_impl(const char *file, + guint line, + const char *func, + gboolean mt_require_locking, + NMLogLevel level, + NMLogDomain domain, + int error, + const char *ifname, + const char *conn_uuid, + const char *fmt, + ...) +{ + va_list args; + char * msg; + GTimeVal tv; + int errsv; + const NMLogDomain *cur_log_state; + NMLogDomain cur_log_state_copy[_LOGL_N_REAL]; + Global g_copy; + const Global * g; + + if (G_UNLIKELY(mt_require_locking)) { + G_LOCK(log); + /* we evaluate logging-enabled under lock. There is still a race that + * we might log the message below *after* logging was disabled. That means, + * when disabling logging, we might still log messages. */ + if (!_nm_logging_enabled_lockfree(level, domain)) { + G_UNLOCK(log); + return; + } + g_copy = gl.imm; + memcpy(cur_log_state_copy, _nm_logging_enabled_state, sizeof(cur_log_state_copy)); + G_UNLOCK(log); + g = &g_copy; + cur_log_state = cur_log_state_copy; + } else { + NM_ASSERT_ON_MAIN_THREAD(); + if (!_nm_logging_enabled_lockfree(level, domain)) + return; + g = &gl.imm; + cur_log_state = _nm_logging_enabled_state; + } + + (void) cur_log_state; + + errsv = errno; + + /* Make sure that %m maps to the specified error */ + if (error != 0) { + if (error < 0) + error = -error; + errno = error; + } + + va_start(args, fmt); + msg = g_strdup_vprintf(fmt, args); + va_end(args); + +#define MESSAGE_FMT "%s%-7s [%ld.%04ld] %s" +#define MESSAGE_ARG(prefix, tv, msg) \ + prefix, level_desc[level].level_str, (tv).tv_sec, ((tv).tv_usec / 100), (msg) + + g_get_current_time(&tv); + + if (g->debug_stderr) + g_printerr(MESSAGE_FMT "\n", MESSAGE_ARG(g->prefix, tv, msg)); + + switch (g->log_backend) { +#if SYSTEMD_JOURNAL + case LOG_BACKEND_JOURNAL: + { + gint64 now, boottime; + struct iovec iov_data[15]; + struct iovec * iov = iov_data; + char * iov_free_data[5]; + char ** iov_free = iov_free_data; + const LogDesc *diter; + NMLogDomain dom_all; + char s_log_domains_buf[NM_STRLEN("NM_LOG_DOMAINS=") + sizeof(_all_logging_domains_to_str)]; + char *s_log_domains; + gsize l_log_domains; + + now = nm_utils_get_monotonic_timestamp_nsec(); + boottime = nm_utils_monotonic_timestamp_as_boottime(now, 1); + + _iovec_set_format_a(iov++, 30, "PRIORITY=%d", level_desc[level].syslog_level); + _iovec_set_format(iov++, + iov_free++, + "MESSAGE=" MESSAGE_FMT, + MESSAGE_ARG(g->prefix, tv, msg)); + _iovec_set_string(iov++, syslog_identifier_full(g->syslog_identifier)); + _iovec_set_format_a(iov++, 30, "SYSLOG_PID=%ld", (long) getpid()); + + dom_all = domain; + s_log_domains = s_log_domains_buf; + l_log_domains = sizeof(s_log_domains_buf); + + nm_utils_strbuf_append_str(&s_log_domains, &l_log_domains, "NM_LOG_DOMAINS="); + for (diter = &domain_desc[0]; dom_all != 0 && diter->name; diter++) { + if (!NM_FLAGS_ANY(dom_all, diter->num)) + continue; + if (dom_all != domain) + nm_utils_strbuf_append_c(&s_log_domains, &l_log_domains, ','); + nm_utils_strbuf_append_str(&s_log_domains, &l_log_domains, diter->name); + dom_all &= ~diter->num; + } + nm_assert(l_log_domains > 0); + _iovec_set(iov++, s_log_domains_buf, s_log_domains - s_log_domains_buf); + + G_STATIC_ASSERT_EXPR(LOG_FAC(LOG_DAEMON) == 3); + _iovec_set_string_literal(iov++, "SYSLOG_FACILITY=3"); + _iovec_set_format_str_a(iov++, 15, "NM_LOG_LEVEL=%s", level_desc[level].name); + if (func) + _iovec_set_format(iov++, iov_free++, "CODE_FUNC=%s", func); + _iovec_set_format(iov++, iov_free++, "CODE_FILE=%s", file ?: ""); + _iovec_set_format_a(iov++, 20, "CODE_LINE=%u", line); + _iovec_set_format_a(iov++, + 60, + "TIMESTAMP_MONOTONIC=%lld.%06lld", + (long long) (now / NM_UTILS_NSEC_PER_SEC), + (long long) ((now % NM_UTILS_NSEC_PER_SEC) / 1000)); + _iovec_set_format_a(iov++, + 60, + "TIMESTAMP_BOOTTIME=%lld.%06lld", + (long long) (boottime / NM_UTILS_NSEC_PER_SEC), + (long long) ((boottime % NM_UTILS_NSEC_PER_SEC) / 1000)); + if (error != 0) + _iovec_set_format_a(iov++, 30, "ERRNO=%d", error); + if (ifname) + _iovec_set_format(iov++, iov_free++, "NM_DEVICE=%s", ifname); + if (conn_uuid) + _iovec_set_format(iov++, iov_free++, "NM_CONNECTION=%s", conn_uuid); + + nm_assert(iov <= &iov_data[G_N_ELEMENTS(iov_data)]); + nm_assert(iov_free <= &iov_free_data[G_N_ELEMENTS(iov_free_data)]); + + sd_journal_sendv(iov_data, iov - iov_data); + + for (; --iov_free >= iov_free_data;) + g_free(*iov_free); + } break; +#endif + case LOG_BACKEND_SYSLOG: + syslog(level_desc[level].syslog_level, MESSAGE_FMT, MESSAGE_ARG(g->prefix, tv, msg)); + break; + default: + g_log(syslog_identifier_domain(g->syslog_identifier), + level_desc[level].g_log_level, + MESSAGE_FMT, + MESSAGE_ARG(g->prefix, tv, msg)); + break; + } + + g_free(msg); + + errno = errsv; +} + +/*****************************************************************************/ + +void +_nm_utils_monotonic_timestamp_initialized(const struct timespec *tp, + gint64 offset_sec, + gboolean is_boottime) +{ + NM_ASSERT_ON_MAIN_THREAD(); + + if (_nm_logging_enabled_lockfree(LOGL_DEBUG, LOGD_CORE)) { + time_t now = time(NULL); + struct tm tm; + char s[255]; + + strftime(s, sizeof(s), "%Y-%m-%d %H:%M:%S", localtime_r(&now, &tm)); + nm_log_dbg(LOGD_CORE, + "monotonic timestamp started counting 1.%09ld seconds ago with " + "an offset of %lld.0 seconds to %s (local time is %s)", + tp->tv_nsec, + (long long) -offset_sec, + is_boottime ? "CLOCK_BOOTTIME" : "CLOCK_MONOTONIC", + s); + } +} + +/*****************************************************************************/ + +static void +nm_log_handler(const char *log_domain, GLogLevelFlags level, const char *message, gpointer ignored) +{ + int syslog_priority; + + switch (level & G_LOG_LEVEL_MASK) { + case G_LOG_LEVEL_ERROR: + syslog_priority = LOG_CRIT; + break; + case G_LOG_LEVEL_CRITICAL: + syslog_priority = LOG_ERR; + break; + case G_LOG_LEVEL_WARNING: + syslog_priority = LOG_WARNING; + break; + case G_LOG_LEVEL_MESSAGE: + syslog_priority = LOG_NOTICE; + break; + case G_LOG_LEVEL_DEBUG: + syslog_priority = LOG_DEBUG; + break; + case G_LOG_LEVEL_INFO: + default: + syslog_priority = LOG_INFO; + break; + } + + /* we don't need any locking here. The glib log handler gets only registered + * once during nm_logging_init() and the global data is not modified afterwards. */ + nm_assert(gl.imm.init_done); + + if (gl.imm.debug_stderr) + g_printerr("%s%s\n", gl.imm.prefix, message ?: ""); + + switch (gl.imm.log_backend) { +#if SYSTEMD_JOURNAL + case LOG_BACKEND_JOURNAL: + { + gint64 now, boottime; + + now = nm_utils_get_monotonic_timestamp_nsec(); + boottime = nm_utils_monotonic_timestamp_as_boottime(now, 1); + + sd_journal_send("PRIORITY=%d", + syslog_priority, + "MESSAGE=%s%s", + gl.imm.prefix, + message ?: "", + syslog_identifier_full(gl.imm.syslog_identifier), + "SYSLOG_PID=%ld", + (long) getpid(), + "SYSLOG_FACILITY=3", + "GLIB_DOMAIN=%s", + log_domain ?: "", + "GLIB_LEVEL=%d", + (int) (level & G_LOG_LEVEL_MASK), + "TIMESTAMP_MONOTONIC=%lld.%06lld", + (long long) (now / NM_UTILS_NSEC_PER_SEC), + (long long) ((now % NM_UTILS_NSEC_PER_SEC) / 1000), + "TIMESTAMP_BOOTTIME=%lld.%06lld", + (long long) (boottime / NM_UTILS_NSEC_PER_SEC), + (long long) ((boottime % NM_UTILS_NSEC_PER_SEC) / 1000), + NULL); + } break; +#endif + default: + syslog(syslog_priority, "%s%s", gl.imm.prefix, message ?: ""); + break; + } +} + +gboolean +nm_logging_syslog_enabled(void) +{ + NM_ASSERT_ON_MAIN_THREAD(); + + return gl.imm.uses_syslog; +} + +void +nm_logging_init_pre(const char *syslog_identifier, char *prefix_take) +{ + /* this function may be called zero or one times, and only + * - on the main thread + * - not after nm_logging_init(). */ + + NM_ASSERT_ON_MAIN_THREAD(); + + if (gl.imm.init_pre_done) + g_return_if_reached(); + + if (gl.imm.init_done) + g_return_if_reached(); + + if (!_syslog_identifier_valid_domain(syslog_identifier)) + g_return_if_reached(); + + if (!prefix_take || !prefix_take[0]) + g_return_if_reached(); + + G_LOCK(log); + + gl.mut.init_pre_done = TRUE; + + gl.mut.syslog_identifier = g_strdup_printf("SYSLOG_IDENTIFIER=%s", syslog_identifier); + nm_assert(_syslog_identifier_assert(gl.imm.syslog_identifier)); + + /* we pass the allocated string on and never free it. */ + gl.mut.prefix = prefix_take; + + G_UNLOCK(log); +} + +void +nm_logging_init(const char *logging_backend, gboolean debug) +{ + gboolean fetch_monotonic_timestamp = FALSE; + gboolean obsolete_debug_backend = FALSE; + LogBackend x_log_backend; + + /* this function may be called zero or one times, and only on the + * main thread. */ + + NM_ASSERT_ON_MAIN_THREAD(); + + nm_assert(NM_IN_STRSET("" NM_CONFIG_DEFAULT_LOGGING_BACKEND, + NM_LOG_CONFIG_BACKEND_JOURNAL, + NM_LOG_CONFIG_BACKEND_SYSLOG)); + + if (gl.imm.init_done) + g_return_if_reached(); + + if (!logging_backend) + logging_backend = "" NM_CONFIG_DEFAULT_LOGGING_BACKEND; + + if (nm_streq(logging_backend, NM_LOG_CONFIG_BACKEND_DEBUG)) { + /* "debug" was wrongly documented as a valid logging backend. It makes no sense however, + * because printing to stderr only makes sense when not demonizing. Whether to daemonize + * is only controlled via command line arguments (--no-daemon, --debug) and not via the + * logging backend from configuration. + * + * Fall back to the default. */ + logging_backend = "" NM_CONFIG_DEFAULT_LOGGING_BACKEND; + obsolete_debug_backend = TRUE; + } + + G_LOCK(log); + +#if SYSTEMD_JOURNAL + if (!nm_streq(logging_backend, NM_LOG_CONFIG_BACKEND_SYSLOG)) { + x_log_backend = LOG_BACKEND_JOURNAL; + + /* We only log the monotonic-timestamp with structured logging (journal). + * Only in this case, fetch the timestamp. */ + fetch_monotonic_timestamp = TRUE; + } else +#endif + { + x_log_backend = LOG_BACKEND_SYSLOG; + openlog(syslog_identifier_domain(gl.imm.syslog_identifier), LOG_PID, LOG_DAEMON); + } + + gl.mut.init_done = TRUE; + gl.mut.log_backend = x_log_backend; + gl.mut.uses_syslog = TRUE; + gl.mut.debug_stderr = debug; + + g_log_set_handler(syslog_identifier_domain(gl.imm.syslog_identifier), + G_LOG_LEVEL_MASK | G_LOG_FLAG_FATAL | G_LOG_FLAG_RECURSION, + nm_log_handler, + NULL); + + G_UNLOCK(log); + + if (fetch_monotonic_timestamp) { + /* ensure we read a monotonic timestamp. Reading the timestamp the first + * time causes a logging message. We don't want to do that during _nm_log_impl. */ + nm_utils_get_monotonic_timestamp_nsec(); + } + + if (obsolete_debug_backend) + nm_log_dbg(LOGD_CORE, + "config: ignore deprecated logging backend 'debug', fallback to '%s'", + logging_backend); + + if (nm_streq(logging_backend, NM_LOG_CONFIG_BACKEND_SYSLOG)) { + /* good */ + } else if (nm_streq(logging_backend, NM_LOG_CONFIG_BACKEND_JOURNAL)) { +#if !SYSTEMD_JOURNAL + nm_log_warn(LOGD_CORE, + "config: logging backend 'journal' is not available, fallback to 'syslog'"); +#endif + } else { + nm_log_warn(LOGD_CORE, + "config: invalid logging backend '%s', fallback to '%s'", + logging_backend, +#if SYSTEMD_JOURNAL + NM_LOG_CONFIG_BACKEND_JOURNAL +#else + NM_LOG_CONFIG_BACKEND_SYSLOG +#endif + ); + } +} diff --git a/shared/nm-log-core/nm-logging.h b/shared/nm-log-core/nm-logging.h new file mode 100644 index 00000000..d3143d39 --- /dev/null +++ b/shared/nm-log-core/nm-logging.h @@ -0,0 +1,189 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright (C) 2006 - 2012 Red Hat, Inc. + * Copyright (C) 2006 - 2008 Novell, Inc. + */ + +#ifndef __NETWORKMANAGER_LOGGING_H__ +#define __NETWORKMANAGER_LOGGING_H__ + +#ifdef __NM_TEST_UTILS_H__ + #error nm-test-utils.h must be included as last header +#endif + +#include "nm-glib-aux/nm-logging-fwd.h" + +#define NM_LOG_CONFIG_BACKEND_DEBUG "debug" +#define NM_LOG_CONFIG_BACKEND_SYSLOG "syslog" +#define NM_LOG_CONFIG_BACKEND_JOURNAL "journal" + +#define nm_log_err(domain, ...) nm_log(LOGL_ERR, (domain), NULL, NULL, __VA_ARGS__) +#define nm_log_warn(domain, ...) nm_log(LOGL_WARN, (domain), NULL, NULL, __VA_ARGS__) +#define nm_log_info(domain, ...) nm_log(LOGL_INFO, (domain), NULL, NULL, __VA_ARGS__) +#define nm_log_dbg(domain, ...) nm_log(LOGL_DEBUG, (domain), NULL, NULL, __VA_ARGS__) +#define nm_log_trace(domain, ...) nm_log(LOGL_TRACE, (domain), NULL, NULL, __VA_ARGS__) + +//#define _NM_LOG_FUNC G_STRFUNC +#define _NM_LOG_FUNC NULL + +/* A wrapper for the _nm_log_impl() function that adds call site information. + * Contrary to nm_log(), it unconditionally calls the function without + * checking whether logging for the given level and domain is enabled. */ +#define _nm_log_mt(mt_require_locking, level, domain, error, ifname, con_uuid, ...) \ + G_STMT_START \ + { \ + _nm_log_impl(__FILE__, \ + __LINE__, \ + _NM_LOG_FUNC, \ + (mt_require_locking), \ + (level), \ + (domain), \ + (error), \ + (ifname), \ + (con_uuid), \ + ""__VA_ARGS__); \ + } \ + G_STMT_END + +#define _nm_log(level, domain, error, ifname, con_uuid, ...) \ + _nm_log_mt(!(NM_THREAD_SAFE_ON_MAIN_THREAD), \ + level, \ + domain, \ + error, \ + ifname, \ + con_uuid, \ + __VA_ARGS__) + +/* nm_log() only evaluates its argument list after checking + * whether logging for the given level/domain is enabled. */ +#define nm_log(level, domain, ifname, con_uuid, ...) \ + G_STMT_START \ + { \ + if (nm_logging_enabled((level), (domain))) { \ + _nm_log(level, domain, 0, ifname, con_uuid, __VA_ARGS__); \ + } \ + } \ + G_STMT_END + +#define _nm_log_ptr(level, domain, ifname, con_uuid, self, prefix, ...) \ + nm_log((level), \ + (domain), \ + (ifname), \ + (con_uuid), \ + "%s[" NM_HASH_OBFUSCATE_PTR_FMT "] " _NM_UTILS_MACRO_FIRST(__VA_ARGS__), \ + (prefix) ?: "", \ + NM_HASH_OBFUSCATE_PTR(self) _NM_UTILS_MACRO_REST(__VA_ARGS__)) + +static inline gboolean +_nm_log_ptr_is_debug(NMLogLevel level) +{ + return level <= LOGL_DEBUG; +} + +/* log a message for an object (with providing a generic @self pointer) */ +#define nm_log_ptr(level, domain, ifname, con_uuid, self, prefix, ...) \ + G_STMT_START \ + { \ + if (_nm_log_ptr_is_debug(level)) { \ + _nm_log_ptr((level), (domain), (ifname), (con_uuid), (self), (prefix), __VA_ARGS__); \ + } else { \ + const char *__prefix = (prefix); \ + \ + nm_log((level), \ + (domain), \ + (ifname), \ + (con_uuid), \ + "%s%s" _NM_UTILS_MACRO_FIRST(__VA_ARGS__), \ + __prefix ?: "", \ + __prefix ? " " : "" _NM_UTILS_MACRO_REST(__VA_ARGS__)); \ + } \ + } \ + G_STMT_END + +#define _nm_log_obj(level, domain, ifname, con_uuid, self, prefix, ...) \ + _nm_log_ptr((level), (domain), (ifname), (con_uuid), (self), prefix, __VA_ARGS__) + +/* log a message for an object (with providing a @self pointer to a GObject). + * Contrary to nm_log_ptr(), @self must be a GObject type (or %NULL). + * As of now, nm_log_obj() is identical to nm_log_ptr(), but we might change that */ +#define nm_log_obj(level, domain, ifname, con_uuid, self, prefix, ...) \ + nm_log_ptr((level), (domain), (ifname), (con_uuid), (self), prefix, __VA_ARGS__) + +const char *nm_logging_level_to_string(void); +const char *nm_logging_domains_to_string(void); + +/*****************************************************************************/ + +extern NMLogDomain _nm_logging_enabled_state[_LOGL_N_REAL]; + +static inline gboolean +_nm_logging_enabled_lockfree(NMLogLevel level, NMLogDomain domain) +{ + nm_assert(((guint) level) < G_N_ELEMENTS(_nm_logging_enabled_state)); + return (((guint) level) < G_N_ELEMENTS(_nm_logging_enabled_state)) + && !!(_nm_logging_enabled_state[level] & domain); +} + +gboolean _nm_logging_enabled_locking(NMLogLevel level, NMLogDomain domain); + +static inline gboolean +nm_logging_enabled_mt(gboolean mt_require_locking, NMLogLevel level, NMLogDomain domain) +{ + if (mt_require_locking) + return _nm_logging_enabled_locking(level, domain); + + NM_ASSERT_ON_MAIN_THREAD(); + return _nm_logging_enabled_lockfree(level, domain); +} + +#define nm_logging_enabled(level, domain) \ + nm_logging_enabled_mt(!(NM_THREAD_SAFE_ON_MAIN_THREAD), level, domain) + +/*****************************************************************************/ + +NMLogLevel nm_logging_get_level(NMLogDomain domain); + +const char *nm_logging_all_levels_to_string(void); +const char *nm_logging_all_domains_to_string(void); + +gboolean +nm_logging_setup(const char *level, const char *domains, char **bad_domains, GError **error); + +void nm_logging_init_pre(const char *syslog_identifier, char *prefix_take); + +void nm_logging_init(const char *logging_backend, gboolean debug); + +gboolean nm_logging_syslog_enabled(void); + +/*****************************************************************************/ + +#define __NMLOG_DEFAULT(level, domain, prefix, ...) \ + G_STMT_START \ + { \ + nm_log((level), \ + (domain), \ + NULL, \ + NULL, \ + "%s: " _NM_UTILS_MACRO_FIRST(__VA_ARGS__), \ + (prefix) _NM_UTILS_MACRO_REST(__VA_ARGS__)); \ + } \ + G_STMT_END + +#define __NMLOG_DEFAULT_WITH_ADDR(level, domain, prefix, ...) \ + G_STMT_START \ + { \ + nm_log((level), \ + (domain), \ + NULL, \ + NULL, \ + "%s[" NM_HASH_OBFUSCATE_PTR_FMT "]: " _NM_UTILS_MACRO_FIRST(__VA_ARGS__), \ + (prefix), \ + NM_HASH_OBFUSCATE_PTR(self) _NM_UTILS_MACRO_REST(__VA_ARGS__)); \ + } \ + G_STMT_END + +/*****************************************************************************/ + +extern void _nm_logging_clear_platform_logging_cache(void); + +#endif /* __NETWORKMANAGER_LOGGING_H__ */ diff --git a/shared/nm-meta-setting.c b/shared/nm-meta-setting.c deleted file mode 100644 index 51b90e6f..00000000 --- a/shared/nm-meta-setting.c +++ /dev/null @@ -1,592 +0,0 @@ -/* SPDX-License-Identifier: LGPL-2.1+ */ -/* - * Copyright (C) 2017 - 2018 Red Hat, Inc. - */ - -#include "nm-default.h" - -#include "nm-meta-setting.h" - -#include "nm-setting-6lowpan.h" -#include "nm-setting-8021x.h" -#include "nm-setting-adsl.h" -#include "nm-setting-bluetooth.h" -#include "nm-setting-bond.h" -#include "nm-setting-bridge-port.h" -#include "nm-setting-bridge.h" -#include "nm-setting-cdma.h" -#include "nm-setting-connection.h" -#include "nm-setting-dcb.h" -#include "nm-setting-dummy.h" -#include "nm-setting-ethtool.h" -#include "nm-setting-generic.h" -#include "nm-setting-gsm.h" -#include "nm-setting-infiniband.h" -#include "nm-setting-ip-config.h" -#include "nm-setting-ip-tunnel.h" -#include "nm-setting-ip4-config.h" -#include "nm-setting-ip6-config.h" -#include "nm-setting-macsec.h" -#include "nm-setting-macvlan.h" -#include "nm-setting-match.h" -#include "nm-setting-olpc-mesh.h" -#include "nm-setting-ovs-bridge.h" -#include "nm-setting-ovs-interface.h" -#include "nm-setting-ovs-dpdk.h" -#include "nm-setting-ovs-patch.h" -#include "nm-setting-ovs-port.h" -#include "nm-setting-ppp.h" -#include "nm-setting-pppoe.h" -#include "nm-setting-proxy.h" -#include "nm-setting-serial.h" -#include "nm-setting-tc-config.h" -#include "nm-setting-team-port.h" -#include "nm-setting-team.h" -#include "nm-setting-tun.h" -#include "nm-setting-user.h" -#include "nm-setting-vlan.h" -#include "nm-setting-vpn.h" -#include "nm-setting-vrf.h" -#include "nm-setting-vxlan.h" -#include "nm-setting-wifi-p2p.h" -#include "nm-setting-wimax.h" -#include "nm-setting-wired.h" -#include "nm-setting-wireguard.h" -#include "nm-setting-wireless-security.h" -#include "nm-setting-wireless.h" -#include "nm-setting-wpan.h" - -/*****************************************************************************/ - -const NMSetting8021xSchemeVtable nm_setting_8021x_scheme_vtable[] = { - -#define _D(_scheme_type, ...) [(_scheme_type)] = {.scheme_type = (_scheme_type), __VA_ARGS__} - - _D(NM_SETTING_802_1X_SCHEME_TYPE_UNKNOWN), - - _D(NM_SETTING_802_1X_SCHEME_TYPE_CA_CERT, - .setting_key = NM_SETTING_802_1X_CA_CERT, - .scheme_func = nm_setting_802_1x_get_ca_cert_scheme, - .format_func = NULL, - .path_func = nm_setting_802_1x_get_ca_cert_path, - .blob_func = nm_setting_802_1x_get_ca_cert_blob, - .uri_func = nm_setting_802_1x_get_ca_cert_uri, - .passwd_func = nm_setting_802_1x_get_ca_cert_password, - .pwflag_func = nm_setting_802_1x_get_ca_cert_password_flags, - .set_cert_func = nm_setting_802_1x_set_ca_cert, - .file_suffix = "ca-cert", ), - - _D(NM_SETTING_802_1X_SCHEME_TYPE_PHASE2_CA_CERT, - .setting_key = NM_SETTING_802_1X_PHASE2_CA_CERT, - .scheme_func = nm_setting_802_1x_get_phase2_ca_cert_scheme, - .format_func = NULL, - .path_func = nm_setting_802_1x_get_phase2_ca_cert_path, - .blob_func = nm_setting_802_1x_get_phase2_ca_cert_blob, - .uri_func = nm_setting_802_1x_get_phase2_ca_cert_uri, - .passwd_func = nm_setting_802_1x_get_phase2_ca_cert_password, - .pwflag_func = nm_setting_802_1x_get_phase2_ca_cert_password_flags, - .set_cert_func = nm_setting_802_1x_set_phase2_ca_cert, - .file_suffix = "inner-ca-cert", ), - - _D(NM_SETTING_802_1X_SCHEME_TYPE_CLIENT_CERT, - .setting_key = NM_SETTING_802_1X_CLIENT_CERT, - .scheme_func = nm_setting_802_1x_get_client_cert_scheme, - .format_func = NULL, - .path_func = nm_setting_802_1x_get_client_cert_path, - .blob_func = nm_setting_802_1x_get_client_cert_blob, - .uri_func = nm_setting_802_1x_get_client_cert_uri, - .passwd_func = nm_setting_802_1x_get_client_cert_password, - .pwflag_func = nm_setting_802_1x_get_client_cert_password_flags, - .set_cert_func = nm_setting_802_1x_set_client_cert, - .file_suffix = "client-cert", ), - - _D(NM_SETTING_802_1X_SCHEME_TYPE_PHASE2_CLIENT_CERT, - .setting_key = NM_SETTING_802_1X_PHASE2_CLIENT_CERT, - .scheme_func = nm_setting_802_1x_get_phase2_client_cert_scheme, - .format_func = NULL, - .path_func = nm_setting_802_1x_get_phase2_client_cert_path, - .blob_func = nm_setting_802_1x_get_phase2_client_cert_blob, - .uri_func = nm_setting_802_1x_get_phase2_client_cert_uri, - .passwd_func = nm_setting_802_1x_get_phase2_client_cert_password, - .pwflag_func = nm_setting_802_1x_get_phase2_client_cert_password_flags, - .set_cert_func = nm_setting_802_1x_set_phase2_client_cert, - .file_suffix = "inner-client-cert", ), - - _D(NM_SETTING_802_1X_SCHEME_TYPE_PRIVATE_KEY, - .setting_key = NM_SETTING_802_1X_PRIVATE_KEY, - .scheme_func = nm_setting_802_1x_get_private_key_scheme, - .format_func = nm_setting_802_1x_get_private_key_format, - .path_func = nm_setting_802_1x_get_private_key_path, - .blob_func = nm_setting_802_1x_get_private_key_blob, - .uri_func = nm_setting_802_1x_get_private_key_uri, - .passwd_func = nm_setting_802_1x_get_private_key_password, - .pwflag_func = nm_setting_802_1x_get_private_key_password_flags, - .set_private_key_func = nm_setting_802_1x_set_private_key, - .file_suffix = "private-key", - .is_secret = TRUE, ), - - _D(NM_SETTING_802_1X_SCHEME_TYPE_PHASE2_PRIVATE_KEY, - .setting_key = NM_SETTING_802_1X_PHASE2_PRIVATE_KEY, - .scheme_func = nm_setting_802_1x_get_phase2_private_key_scheme, - .format_func = nm_setting_802_1x_get_phase2_private_key_format, - .path_func = nm_setting_802_1x_get_phase2_private_key_path, - .blob_func = nm_setting_802_1x_get_phase2_private_key_blob, - .uri_func = nm_setting_802_1x_get_phase2_private_key_uri, - .passwd_func = nm_setting_802_1x_get_phase2_private_key_password, - .pwflag_func = nm_setting_802_1x_get_phase2_private_key_password_flags, - .set_private_key_func = nm_setting_802_1x_set_phase2_private_key, - .file_suffix = "inner-private-key", - .is_secret = TRUE, ), - -#undef _D -}; - -/*****************************************************************************/ - -const NMMetaSettingInfo nm_meta_setting_infos[] = { - [NM_META_SETTING_TYPE_6LOWPAN] = - { - .meta_type = NM_META_SETTING_TYPE_6LOWPAN, - .setting_priority = NM_SETTING_PRIORITY_HW_BASE, - .setting_name = NM_SETTING_6LOWPAN_SETTING_NAME, - .get_setting_gtype = nm_setting_6lowpan_get_type, - }, - [NM_META_SETTING_TYPE_802_1X] = - { - .meta_type = NM_META_SETTING_TYPE_802_1X, - .setting_priority = NM_SETTING_PRIORITY_HW_AUX, - .setting_name = NM_SETTING_802_1X_SETTING_NAME, - .get_setting_gtype = nm_setting_802_1x_get_type, - }, - [NM_META_SETTING_TYPE_ADSL] = - { - .meta_type = NM_META_SETTING_TYPE_ADSL, - .setting_priority = NM_SETTING_PRIORITY_HW_BASE, - .setting_name = NM_SETTING_ADSL_SETTING_NAME, - .get_setting_gtype = nm_setting_adsl_get_type, - }, - [NM_META_SETTING_TYPE_BLUETOOTH] = - { - .meta_type = NM_META_SETTING_TYPE_BLUETOOTH, - .setting_priority = NM_SETTING_PRIORITY_HW_NON_BASE, - .setting_name = NM_SETTING_BLUETOOTH_SETTING_NAME, - .get_setting_gtype = nm_setting_bluetooth_get_type, - }, - [NM_META_SETTING_TYPE_BOND] = - { - .meta_type = NM_META_SETTING_TYPE_BOND, - .setting_priority = NM_SETTING_PRIORITY_HW_BASE, - .setting_name = NM_SETTING_BOND_SETTING_NAME, - .get_setting_gtype = nm_setting_bond_get_type, - }, - [NM_META_SETTING_TYPE_BRIDGE] = - { - .meta_type = NM_META_SETTING_TYPE_BRIDGE, - .setting_priority = NM_SETTING_PRIORITY_HW_BASE, - .setting_name = NM_SETTING_BRIDGE_SETTING_NAME, - .get_setting_gtype = nm_setting_bridge_get_type, - }, - [NM_META_SETTING_TYPE_BRIDGE_PORT] = - { - .meta_type = NM_META_SETTING_TYPE_BRIDGE_PORT, - .setting_priority = NM_SETTING_PRIORITY_AUX, - .setting_name = NM_SETTING_BRIDGE_PORT_SETTING_NAME, - .get_setting_gtype = nm_setting_bridge_port_get_type, - }, - [NM_META_SETTING_TYPE_CDMA] = - { - .meta_type = NM_META_SETTING_TYPE_CDMA, - .setting_priority = NM_SETTING_PRIORITY_HW_BASE, - .setting_name = NM_SETTING_CDMA_SETTING_NAME, - .get_setting_gtype = nm_setting_cdma_get_type, - }, - [NM_META_SETTING_TYPE_CONNECTION] = - { - .meta_type = NM_META_SETTING_TYPE_CONNECTION, - .setting_priority = NM_SETTING_PRIORITY_CONNECTION, - .setting_name = NM_SETTING_CONNECTION_SETTING_NAME, - .get_setting_gtype = nm_setting_connection_get_type, - }, - [NM_META_SETTING_TYPE_DCB] = - { - .meta_type = NM_META_SETTING_TYPE_DCB, - .setting_priority = NM_SETTING_PRIORITY_HW_AUX, - .setting_name = NM_SETTING_DCB_SETTING_NAME, - .get_setting_gtype = nm_setting_dcb_get_type, - }, - [NM_META_SETTING_TYPE_DUMMY] = - { - .meta_type = NM_META_SETTING_TYPE_DUMMY, - .setting_priority = NM_SETTING_PRIORITY_HW_BASE, - .setting_name = NM_SETTING_DUMMY_SETTING_NAME, - .get_setting_gtype = nm_setting_dummy_get_type, - }, - [NM_META_SETTING_TYPE_ETHTOOL] = - { - .meta_type = NM_META_SETTING_TYPE_ETHTOOL, - .setting_priority = NM_SETTING_PRIORITY_AUX, - .setting_name = NM_SETTING_ETHTOOL_SETTING_NAME, - .get_setting_gtype = nm_setting_ethtool_get_type, - }, - [NM_META_SETTING_TYPE_GENERIC] = - { - .meta_type = NM_META_SETTING_TYPE_GENERIC, - .setting_priority = NM_SETTING_PRIORITY_HW_BASE, - .setting_name = NM_SETTING_GENERIC_SETTING_NAME, - .get_setting_gtype = nm_setting_generic_get_type, - }, - [NM_META_SETTING_TYPE_GSM] = - { - .meta_type = NM_META_SETTING_TYPE_GSM, - .setting_priority = NM_SETTING_PRIORITY_HW_BASE, - .setting_name = NM_SETTING_GSM_SETTING_NAME, - .get_setting_gtype = nm_setting_gsm_get_type, - }, - [NM_META_SETTING_TYPE_INFINIBAND] = - { - .meta_type = NM_META_SETTING_TYPE_INFINIBAND, - .setting_priority = NM_SETTING_PRIORITY_HW_BASE, - .setting_name = NM_SETTING_INFINIBAND_SETTING_NAME, - .get_setting_gtype = nm_setting_infiniband_get_type, - }, - [NM_META_SETTING_TYPE_IP4_CONFIG] = - { - .meta_type = NM_META_SETTING_TYPE_IP4_CONFIG, - .setting_priority = NM_SETTING_PRIORITY_IP, - .setting_name = NM_SETTING_IP4_CONFIG_SETTING_NAME, - .get_setting_gtype = nm_setting_ip4_config_get_type, - }, - [NM_META_SETTING_TYPE_IP6_CONFIG] = - { - .meta_type = NM_META_SETTING_TYPE_IP6_CONFIG, - .setting_priority = NM_SETTING_PRIORITY_IP, - .setting_name = NM_SETTING_IP6_CONFIG_SETTING_NAME, - .get_setting_gtype = nm_setting_ip6_config_get_type, - }, - [NM_META_SETTING_TYPE_IP_TUNNEL] = - { - .meta_type = NM_META_SETTING_TYPE_IP_TUNNEL, - .setting_priority = NM_SETTING_PRIORITY_HW_BASE, - .setting_name = NM_SETTING_IP_TUNNEL_SETTING_NAME, - .get_setting_gtype = nm_setting_ip_tunnel_get_type, - }, - [NM_META_SETTING_TYPE_MACSEC] = - { - .meta_type = NM_META_SETTING_TYPE_MACSEC, - .setting_priority = NM_SETTING_PRIORITY_HW_BASE, - .setting_name = NM_SETTING_MACSEC_SETTING_NAME, - .get_setting_gtype = nm_setting_macsec_get_type, - }, - [NM_META_SETTING_TYPE_MACVLAN] = - { - .meta_type = NM_META_SETTING_TYPE_MACVLAN, - .setting_priority = NM_SETTING_PRIORITY_HW_BASE, - .setting_name = NM_SETTING_MACVLAN_SETTING_NAME, - .get_setting_gtype = nm_setting_macvlan_get_type, - }, - [NM_META_SETTING_TYPE_MATCH] = - { - .meta_type = NM_META_SETTING_TYPE_MATCH, - .setting_priority = NM_SETTING_PRIORITY_AUX, - .setting_name = NM_SETTING_MATCH_SETTING_NAME, - .get_setting_gtype = nm_setting_match_get_type, - }, - [NM_META_SETTING_TYPE_OLPC_MESH] = - { - .meta_type = NM_META_SETTING_TYPE_OLPC_MESH, - .setting_priority = NM_SETTING_PRIORITY_HW_BASE, - .setting_name = NM_SETTING_OLPC_MESH_SETTING_NAME, - .get_setting_gtype = nm_setting_olpc_mesh_get_type, - }, - [NM_META_SETTING_TYPE_OVS_BRIDGE] = - { - .meta_type = NM_META_SETTING_TYPE_OVS_BRIDGE, - .setting_priority = NM_SETTING_PRIORITY_HW_BASE, - .setting_name = NM_SETTING_OVS_BRIDGE_SETTING_NAME, - .get_setting_gtype = nm_setting_ovs_bridge_get_type, - }, - [NM_META_SETTING_TYPE_OVS_DPDK] = - { - .meta_type = NM_META_SETTING_TYPE_OVS_DPDK, - .setting_priority = NM_SETTING_PRIORITY_HW_BASE, - .setting_name = NM_SETTING_OVS_DPDK_SETTING_NAME, - .get_setting_gtype = nm_setting_ovs_dpdk_get_type, - }, - [NM_META_SETTING_TYPE_OVS_INTERFACE] = - { - .meta_type = NM_META_SETTING_TYPE_OVS_INTERFACE, - .setting_priority = NM_SETTING_PRIORITY_HW_BASE, - .setting_name = NM_SETTING_OVS_INTERFACE_SETTING_NAME, - .get_setting_gtype = nm_setting_ovs_interface_get_type, - }, - [NM_META_SETTING_TYPE_OVS_PATCH] = - { - .meta_type = NM_META_SETTING_TYPE_OVS_PATCH, - .setting_priority = NM_SETTING_PRIORITY_HW_BASE, - .setting_name = NM_SETTING_OVS_PATCH_SETTING_NAME, - .get_setting_gtype = nm_setting_ovs_patch_get_type, - }, - [NM_META_SETTING_TYPE_OVS_PORT] = - { - .meta_type = NM_META_SETTING_TYPE_OVS_PORT, - .setting_priority = NM_SETTING_PRIORITY_HW_BASE, - .setting_name = NM_SETTING_OVS_PORT_SETTING_NAME, - .get_setting_gtype = nm_setting_ovs_port_get_type, - }, - [NM_META_SETTING_TYPE_PPPOE] = - { - .meta_type = NM_META_SETTING_TYPE_PPPOE, - .setting_priority = NM_SETTING_PRIORITY_AUX, - .setting_name = NM_SETTING_PPPOE_SETTING_NAME, - .get_setting_gtype = nm_setting_pppoe_get_type, - }, - [NM_META_SETTING_TYPE_PPP] = - { - .meta_type = NM_META_SETTING_TYPE_PPP, - .setting_priority = NM_SETTING_PRIORITY_AUX, - .setting_name = NM_SETTING_PPP_SETTING_NAME, - .get_setting_gtype = nm_setting_ppp_get_type, - }, - [NM_META_SETTING_TYPE_PROXY] = - { - .meta_type = NM_META_SETTING_TYPE_PROXY, - .setting_priority = NM_SETTING_PRIORITY_IP, - .setting_name = NM_SETTING_PROXY_SETTING_NAME, - .get_setting_gtype = nm_setting_proxy_get_type, - }, - [NM_META_SETTING_TYPE_SERIAL] = - { - .meta_type = NM_META_SETTING_TYPE_SERIAL, - .setting_priority = NM_SETTING_PRIORITY_HW_AUX, - .setting_name = NM_SETTING_SERIAL_SETTING_NAME, - .get_setting_gtype = nm_setting_serial_get_type, - }, - [NM_META_SETTING_TYPE_SRIOV] = - { - .meta_type = NM_META_SETTING_TYPE_SRIOV, - .setting_priority = NM_SETTING_PRIORITY_HW_AUX, - .setting_name = NM_SETTING_SRIOV_SETTING_NAME, - .get_setting_gtype = nm_setting_sriov_get_type, - }, - [NM_META_SETTING_TYPE_TC_CONFIG] = - { - .meta_type = NM_META_SETTING_TYPE_TC_CONFIG, - .setting_priority = NM_SETTING_PRIORITY_IP, - .setting_name = NM_SETTING_TC_CONFIG_SETTING_NAME, - .get_setting_gtype = nm_setting_tc_config_get_type, - }, - [NM_META_SETTING_TYPE_TEAM] = - { - .meta_type = NM_META_SETTING_TYPE_TEAM, - .setting_priority = NM_SETTING_PRIORITY_HW_BASE, - .setting_name = NM_SETTING_TEAM_SETTING_NAME, - .get_setting_gtype = nm_setting_team_get_type, - }, - [NM_META_SETTING_TYPE_TEAM_PORT] = - { - .meta_type = NM_META_SETTING_TYPE_TEAM_PORT, - .setting_priority = NM_SETTING_PRIORITY_AUX, - .setting_name = NM_SETTING_TEAM_PORT_SETTING_NAME, - .get_setting_gtype = nm_setting_team_port_get_type, - }, - [NM_META_SETTING_TYPE_TUN] = - { - .meta_type = NM_META_SETTING_TYPE_TUN, - .setting_priority = NM_SETTING_PRIORITY_HW_BASE, - .setting_name = NM_SETTING_TUN_SETTING_NAME, - .get_setting_gtype = nm_setting_tun_get_type, - }, - [NM_META_SETTING_TYPE_USER] = - { - .meta_type = NM_META_SETTING_TYPE_USER, - .setting_priority = NM_SETTING_PRIORITY_USER, - .setting_name = NM_SETTING_USER_SETTING_NAME, - .get_setting_gtype = nm_setting_user_get_type, - }, - [NM_META_SETTING_TYPE_VLAN] = - { - .meta_type = NM_META_SETTING_TYPE_VLAN, - .setting_priority = NM_SETTING_PRIORITY_HW_BASE, - .setting_name = NM_SETTING_VLAN_SETTING_NAME, - .get_setting_gtype = nm_setting_vlan_get_type, - }, - [NM_META_SETTING_TYPE_VPN] = - { - .meta_type = NM_META_SETTING_TYPE_VPN, - .setting_priority = NM_SETTING_PRIORITY_HW_BASE, - .setting_name = NM_SETTING_VPN_SETTING_NAME, - .get_setting_gtype = nm_setting_vpn_get_type, - }, - [NM_META_SETTING_TYPE_VRF] = - { - .meta_type = NM_META_SETTING_TYPE_VRF, - .setting_priority = NM_SETTING_PRIORITY_HW_BASE, - .setting_name = NM_SETTING_VRF_SETTING_NAME, - .get_setting_gtype = nm_setting_vrf_get_type, - }, - [NM_META_SETTING_TYPE_VXLAN] = - { - .meta_type = NM_META_SETTING_TYPE_VXLAN, - .setting_priority = NM_SETTING_PRIORITY_HW_BASE, - .setting_name = NM_SETTING_VXLAN_SETTING_NAME, - .get_setting_gtype = nm_setting_vxlan_get_type, - }, - [NM_META_SETTING_TYPE_WIFI_P2P] = - { - .meta_type = NM_META_SETTING_TYPE_WIFI_P2P, - .setting_priority = NM_SETTING_PRIORITY_HW_BASE, - .setting_name = NM_SETTING_WIFI_P2P_SETTING_NAME, - .get_setting_gtype = nm_setting_wifi_p2p_get_type, - }, - [NM_META_SETTING_TYPE_WIMAX] = - { - .meta_type = NM_META_SETTING_TYPE_WIMAX, - .setting_priority = NM_SETTING_PRIORITY_HW_BASE, - .setting_name = NM_SETTING_WIMAX_SETTING_NAME, - .get_setting_gtype = nm_setting_wimax_get_type, - }, - [NM_META_SETTING_TYPE_WIRED] = - { - .meta_type = NM_META_SETTING_TYPE_WIRED, - .setting_priority = NM_SETTING_PRIORITY_HW_BASE, - .setting_name = NM_SETTING_WIRED_SETTING_NAME, - .get_setting_gtype = nm_setting_wired_get_type, - }, - [NM_META_SETTING_TYPE_WIREGUARD] = - { - .meta_type = NM_META_SETTING_TYPE_WIREGUARD, - .setting_priority = NM_SETTING_PRIORITY_HW_BASE, - .setting_name = NM_SETTING_WIREGUARD_SETTING_NAME, - .get_setting_gtype = nm_setting_wireguard_get_type, - }, - [NM_META_SETTING_TYPE_WIRELESS] = - { - .meta_type = NM_META_SETTING_TYPE_WIRELESS, - .setting_priority = NM_SETTING_PRIORITY_HW_BASE, - .setting_name = NM_SETTING_WIRELESS_SETTING_NAME, - .get_setting_gtype = nm_setting_wireless_get_type, - }, - [NM_META_SETTING_TYPE_WIRELESS_SECURITY] = - { - .meta_type = NM_META_SETTING_TYPE_WIRELESS_SECURITY, - .setting_priority = NM_SETTING_PRIORITY_HW_AUX, - .setting_name = NM_SETTING_WIRELESS_SECURITY_SETTING_NAME, - .get_setting_gtype = nm_setting_wireless_security_get_type, - }, - [NM_META_SETTING_TYPE_WPAN] = - { - .meta_type = NM_META_SETTING_TYPE_WPAN, - .setting_priority = NM_SETTING_PRIORITY_HW_BASE, - .setting_name = NM_SETTING_WPAN_SETTING_NAME, - .get_setting_gtype = nm_setting_wpan_get_type, - }, - - [NM_META_SETTING_TYPE_UNKNOWN] = - { - .meta_type = NM_META_SETTING_TYPE_UNKNOWN, - }, -}; - -const NMMetaSettingInfo * -nm_meta_setting_infos_by_name(const char *name) -{ - gssize idx; - -#if NM_MORE_ASSERTS > 10 - { - guint i, j; - - for (i = 0; i < _NM_META_SETTING_TYPE_NUM; i++) { - const NMMetaSettingInfo *setting_info = &nm_meta_setting_infos[i]; - - nm_assert(setting_info->meta_type == (NMMetaSettingType) i); - nm_assert(setting_info->setting_name); - nm_assert(setting_info->setting_name[0]); - nm_assert(setting_info->get_setting_gtype); - nm_assert(setting_info->setting_priority != NM_SETTING_PRIORITY_INVALID); - if (i > 0 - && strcmp(nm_meta_setting_infos[i - 1].setting_name, setting_info->setting_name) - >= 0) { - g_error("nm_meta_setting_infos[%u, \"%s\"] is wrongly sorted before " - "nm_meta_setting_infos[%u, \"%s\"]. Rearange NMMetaSettingType enum", - i - 1, - nm_meta_setting_infos[i - 1].setting_name, - i, - setting_info->setting_name); - } - for (j = 0; j < i; j++) { - const NMMetaSettingInfo *s = &nm_meta_setting_infos[j]; - - nm_assert(setting_info->get_setting_gtype != s->get_setting_gtype); - } - } - } -#endif - - G_STATIC_ASSERT_EXPR(G_STRUCT_OFFSET(NMMetaSettingInfo, setting_name) == 0); - idx = nm_utils_array_find_binary_search(nm_meta_setting_infos, - sizeof(NMMetaSettingInfo), - _NM_META_SETTING_TYPE_NUM, - &name, - nm_strcmp_p_with_data, - NULL); - - return idx >= 0 ? &nm_meta_setting_infos[idx] : NULL; -} - -const NMMetaSettingInfo * -nm_meta_setting_infos_by_gtype(GType gtype) -{ -#if ((NETWORKMANAGER_COMPILATION) &NM_NETWORKMANAGER_COMPILATION_WITH_LIBNM_CORE_INTERNAL) - nm_auto_unref_gtypeclass GTypeClass *gtypeclass_unref = NULL; - GTypeClass * gtypeclass; - NMSettingClass * klass; - - if (!g_type_is_a(gtype, NM_TYPE_SETTING)) - goto out_none; - - gtypeclass = g_type_class_peek(gtype); - if (!gtypeclass) - gtypeclass = gtypeclass_unref = g_type_class_ref(gtype); - - nm_assert(NM_IS_SETTING_CLASS(gtypeclass)); - - klass = (NMSettingClass *) gtypeclass; - - if (!klass->setting_info) - goto out_none; - - nm_assert(klass->setting_info->get_setting_gtype); - nm_assert(klass->setting_info->get_setting_gtype() == gtype); - - return klass->setting_info; - -out_none: - - #if NM_MORE_ASSERTS > 10 -{ - int i; - - /* this might hint to a bug, but it would be expected for NM_TYPE_SETTING - * and NM_TYPE_SETTING_IP_CONFIG. - * - * Assert that we didn't lookup for a gtype, which we would expect to find. - * An assertion failure here, hints to a bug in nm_setting_*_class_init(). - */ - for (i = 0; i < _NM_META_SETTING_TYPE_NUM; i++) - nm_assert(nm_meta_setting_infos[i].get_setting_gtype() != gtype); -} - #endif - return NULL; -#else - guint i; - - for (i = 0; i < _NM_META_SETTING_TYPE_NUM; i++) { - if (nm_meta_setting_infos[i].get_setting_gtype() == gtype) - return &nm_meta_setting_infos[i]; - } - return NULL; -#endif -} - -/*****************************************************************************/ diff --git a/shared/nm-meta-setting.h b/shared/nm-meta-setting.h deleted file mode 100644 index dadf2f72..00000000 --- a/shared/nm-meta-setting.h +++ /dev/null @@ -1,209 +0,0 @@ -/* SPDX-License-Identifier: LGPL-2.1+ */ -/* - * Copyright (C) 2017 - 2018 Red Hat, Inc. - */ - -#ifndef __NM_META_SETTING_H__ -#define __NM_META_SETTING_H__ - -#include "nm-setting-8021x.h" - -/*****************************************************************************/ - -/* - * A setting's priority should roughly follow the OSI layer model, but it also - * controls which settings get asked for secrets first. Thus settings which - * relate to things that must be working first, like hardware, should get a - * higher priority than things which layer on top of the hardware. For example, - * the GSM/CDMA settings should provide secrets before the PPP setting does, - * because a PIN is required to unlock the device before PPP can even start. - * Even settings without secrets should be assigned the right priority. - * - * 0: reserved for invalid - * - * 1: reserved for the Connection setting - * - * 2,3: hardware-related settings like Ethernet, Wi-Fi, InfiniBand, Bridge, etc. - * These priority 1 settings are also "base types", which means that at least - * one of them is required for the connection to be valid, and their name is - * valid in the 'type' property of the Connection setting. - * - * 4: hardware-related auxiliary settings that require a base setting to be - * successful first, like Wi-Fi security, 802.1x, etc. - * - * 5: hardware-independent settings that are required before IP connectivity - * can be established, like PPP, PPPoE, etc. - * - * 6: IP-level stuff - * - * 10: NMSettingUser - */ -typedef enum { /*< skip >*/ - NM_SETTING_PRIORITY_INVALID = 0, - NM_SETTING_PRIORITY_CONNECTION = 1, - NM_SETTING_PRIORITY_HW_BASE = 2, - NM_SETTING_PRIORITY_HW_NON_BASE = 3, - NM_SETTING_PRIORITY_HW_AUX = 4, - NM_SETTING_PRIORITY_AUX = 5, - NM_SETTING_PRIORITY_IP = 6, - NM_SETTING_PRIORITY_USER = 10, -} NMSettingPriority; - -/*****************************************************************************/ - -typedef enum { - NM_SETTING_802_1X_SCHEME_TYPE_CA_CERT, - NM_SETTING_802_1X_SCHEME_TYPE_PHASE2_CA_CERT, - NM_SETTING_802_1X_SCHEME_TYPE_CLIENT_CERT, - NM_SETTING_802_1X_SCHEME_TYPE_PHASE2_CLIENT_CERT, - NM_SETTING_802_1X_SCHEME_TYPE_PRIVATE_KEY, - NM_SETTING_802_1X_SCHEME_TYPE_PHASE2_PRIVATE_KEY, - - NM_SETTING_802_1X_SCHEME_TYPE_UNKNOWN, - - _NM_SETTING_802_1X_SCHEME_TYPE_NUM = NM_SETTING_802_1X_SCHEME_TYPE_UNKNOWN, -} NMSetting8021xSchemeType; - -typedef struct { - const char *setting_key; - NMSetting8021xCKScheme (*scheme_func)(NMSetting8021x *setting); - NMSetting8021xCKFormat (*format_func)(NMSetting8021x *setting); - const char *(*path_func)(NMSetting8021x *setting); - GBytes *(*blob_func)(NMSetting8021x *setting); - const char *(*uri_func)(NMSetting8021x *setting); - const char *(*passwd_func)(NMSetting8021x *setting); - NMSettingSecretFlags (*pwflag_func)(NMSetting8021x *setting); - gboolean (*set_cert_func)(NMSetting8021x * setting, - const char * value, - NMSetting8021xCKScheme scheme, - NMSetting8021xCKFormat *out_format, - GError ** error); - gboolean (*set_private_key_func)(NMSetting8021x * setting, - const char * value, - const char * password, - NMSetting8021xCKScheme scheme, - NMSetting8021xCKFormat *out_format, - GError ** error); - const char * file_suffix; - NMSetting8021xSchemeType scheme_type; - bool is_secret : 1; -} NMSetting8021xSchemeVtable; - -extern const NMSetting8021xSchemeVtable - nm_setting_8021x_scheme_vtable[_NM_SETTING_802_1X_SCHEME_TYPE_NUM + 1]; - -/*****************************************************************************/ - -typedef enum { - /* the enum (and their numeric values) are internal API. Do not assign - * any meaning the numeric values, because they already have one: - * - * they are sorted in a way, that corresponds to the asciibetical sort - * order of the corresponding setting-name. */ - - NM_META_SETTING_TYPE_6LOWPAN, - NM_META_SETTING_TYPE_OLPC_MESH, - NM_META_SETTING_TYPE_WIRELESS, - NM_META_SETTING_TYPE_WIRELESS_SECURITY, - NM_META_SETTING_TYPE_802_1X, - NM_META_SETTING_TYPE_WIRED, - NM_META_SETTING_TYPE_ADSL, - NM_META_SETTING_TYPE_BLUETOOTH, - NM_META_SETTING_TYPE_BOND, - NM_META_SETTING_TYPE_BRIDGE, - NM_META_SETTING_TYPE_BRIDGE_PORT, - NM_META_SETTING_TYPE_CDMA, - NM_META_SETTING_TYPE_CONNECTION, - NM_META_SETTING_TYPE_DCB, - NM_META_SETTING_TYPE_DUMMY, - NM_META_SETTING_TYPE_ETHTOOL, - NM_META_SETTING_TYPE_GENERIC, - NM_META_SETTING_TYPE_GSM, - NM_META_SETTING_TYPE_INFINIBAND, - NM_META_SETTING_TYPE_IP_TUNNEL, - NM_META_SETTING_TYPE_IP4_CONFIG, - NM_META_SETTING_TYPE_IP6_CONFIG, - NM_META_SETTING_TYPE_MACSEC, - NM_META_SETTING_TYPE_MACVLAN, - NM_META_SETTING_TYPE_MATCH, - NM_META_SETTING_TYPE_OVS_BRIDGE, - NM_META_SETTING_TYPE_OVS_DPDK, - NM_META_SETTING_TYPE_OVS_INTERFACE, - NM_META_SETTING_TYPE_OVS_PATCH, - NM_META_SETTING_TYPE_OVS_PORT, - NM_META_SETTING_TYPE_PPP, - NM_META_SETTING_TYPE_PPPOE, - NM_META_SETTING_TYPE_PROXY, - NM_META_SETTING_TYPE_SERIAL, - NM_META_SETTING_TYPE_SRIOV, - NM_META_SETTING_TYPE_TC_CONFIG, - NM_META_SETTING_TYPE_TEAM, - NM_META_SETTING_TYPE_TEAM_PORT, - NM_META_SETTING_TYPE_TUN, - NM_META_SETTING_TYPE_USER, - NM_META_SETTING_TYPE_VLAN, - NM_META_SETTING_TYPE_VPN, - NM_META_SETTING_TYPE_VRF, - NM_META_SETTING_TYPE_VXLAN, - NM_META_SETTING_TYPE_WIFI_P2P, - NM_META_SETTING_TYPE_WIMAX, - NM_META_SETTING_TYPE_WIREGUARD, - NM_META_SETTING_TYPE_WPAN, - - NM_META_SETTING_TYPE_UNKNOWN, - - _NM_META_SETTING_TYPE_NUM = NM_META_SETTING_TYPE_UNKNOWN, -} NMMetaSettingType; - -/* this header is statically linked with both libnm-core.la and libnmc.la. - * Though, there is no stable API/ABI, so whenever on of these components - * accesses NMMetaSettingInfo or NMMetaSettingType, it only has meaning - * inside the same component. - * - * Note how NMSettingClass has field of type "struct _NMMetaSettingInfo". - * It would be a serious bug, if libnmc tries to interpret this pointer - * with the meaning of NMMetaSettingInfo. They might be different, because - * libnm.so (libnm-core.la) might be a newer version than nmcli (libnmc.la). - * - * This define helps to ensure that we don't accidentally use the pointer - * in different contexts. */ -#if ((NETWORKMANAGER_COMPILATION) &NM_NETWORKMANAGER_COMPILATION_WITH_LIBNM_CORE_INTERNAL) - #define _NMMetaSettingInfoXX _NMMetaSettingInfo -#else - #define _NMMetaSettingInfoXX _NMMetaSettingInfoCli -#endif -struct _NMMetaSettingInfoXX { - const char *setting_name; - GType (*get_setting_gtype)(void); - NMMetaSettingType meta_type; - NMSettingPriority setting_priority; -}; - -typedef struct _NMMetaSettingInfoXX NMMetaSettingInfo; - -/* note that we statically link nm-meta-setting.h both to libnm-core.la and - * libnmc.la. That means, there are two versions of nm_meta_setting_infos - * in nmcli. That is not easily avoidable, because at this point, we don't - * want yet to making it public API. - * - * Eventually, this should become public API of libnm, and nmcli/libnmc.la - * should use that version. - * - * Downsides of the current solution: - * - * - duplication of the array in nmcli. - * - * - there is no stable API/ABI. That means, when you have a NMMetaSettingInfo - * pointer, or a NMMetaSettingType value, the value can only be used within - * the current context (libnm-core.la or libnmc.la). In other words, libnmc.la - * (and nmcli) must never access a NMMetaSettingInfo/NMMetaSettingType value, - * that comes from libnm-core.la. - */ -extern const NMMetaSettingInfo nm_meta_setting_infos[_NM_META_SETTING_TYPE_NUM + 1]; - -const NMMetaSettingInfo *nm_meta_setting_infos_by_name(const char *name); -const NMMetaSettingInfo *nm_meta_setting_infos_by_gtype(GType gtype); - -/*****************************************************************************/ - -#endif /* __NM_META_SETTING_H__ */ diff --git a/shared/nm-platform/nm-netlink.c b/shared/nm-platform/nm-netlink.c new file mode 100644 index 00000000..7a6d7e04 --- /dev/null +++ b/shared/nm-platform/nm-netlink.c @@ -0,0 +1,1518 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright (C) 2018 Red Hat, Inc. + */ + +#include "nm-glib-aux/nm-default-glib-i18n-lib.h" + +#include "nm-netlink.h" + +#include <unistd.h> +#include <fcntl.h> + +/*****************************************************************************/ + +#ifndef SOL_NETLINK + #define SOL_NETLINK 270 +#endif + +/*****************************************************************************/ + +#define NL_SOCK_PASSCRED (1 << 1) +#define NL_MSG_PEEK (1 << 3) +#define NL_MSG_PEEK_EXPLICIT (1 << 4) +#define NL_NO_AUTO_ACK (1 << 5) + +#ifndef NETLINK_EXT_ACK + #define NETLINK_EXT_ACK 11 +#endif + +struct nl_msg { + int nm_protocol; + struct sockaddr_nl nm_src; + struct sockaddr_nl nm_dst; + struct ucred nm_creds; + struct nlmsghdr * nm_nlh; + size_t nm_size; + bool nm_creds_has : 1; +}; + +struct nl_sock { + struct sockaddr_nl s_local; + struct sockaddr_nl s_peer; + int s_fd; + int s_proto; + unsigned int s_seq_next; + unsigned int s_seq_expect; + int s_flags; + size_t s_bufsize; +}; + +/*****************************************************************************/ + +NM_UTILS_ENUM2STR_DEFINE(nl_nlmsgtype2str, + int, + NM_UTILS_ENUM2STR(NLMSG_NOOP, "NOOP"), + NM_UTILS_ENUM2STR(NLMSG_ERROR, "ERROR"), + NM_UTILS_ENUM2STR(NLMSG_DONE, "DONE"), + NM_UTILS_ENUM2STR(NLMSG_OVERRUN, "OVERRUN"), ); + +NM_UTILS_FLAGS2STR_DEFINE(nl_nlmsg_flags2str, + int, + NM_UTILS_FLAGS2STR(NLM_F_REQUEST, "REQUEST"), + NM_UTILS_FLAGS2STR(NLM_F_MULTI, "MULTI"), + NM_UTILS_FLAGS2STR(NLM_F_ACK, "ACK"), + NM_UTILS_FLAGS2STR(NLM_F_ECHO, "ECHO"), + NM_UTILS_FLAGS2STR(NLM_F_ROOT, "ROOT"), + NM_UTILS_FLAGS2STR(NLM_F_MATCH, "MATCH"), + NM_UTILS_FLAGS2STR(NLM_F_ATOMIC, "ATOMIC"), + NM_UTILS_FLAGS2STR(NLM_F_REPLACE, "REPLACE"), + NM_UTILS_FLAGS2STR(NLM_F_EXCL, "EXCL"), + NM_UTILS_FLAGS2STR(NLM_F_CREATE, "CREATE"), + NM_UTILS_FLAGS2STR(NLM_F_APPEND, "APPEND"), ); + +/*****************************************************************************/ + +const char * +nl_nlmsghdr_to_str(const struct nlmsghdr *hdr, char *buf, gsize len) +{ + const char *b; + const char *s; + guint flags, flags_before; + const char *prefix; + + if (!nm_utils_to_string_buffer_init_null(hdr, &buf, &len)) + return buf; + + b = buf; + + switch (hdr->nlmsg_type) { + case RTM_GETLINK: + s = "RTM_GETLINK"; + break; + case RTM_NEWLINK: + s = "RTM_NEWLINK"; + break; + case RTM_DELLINK: + s = "RTM_DELLINK"; + break; + case RTM_SETLINK: + s = "RTM_SETLINK"; + break; + case RTM_GETADDR: + s = "RTM_GETADDR"; + break; + case RTM_NEWADDR: + s = "RTM_NEWADDR"; + break; + case RTM_DELADDR: + s = "RTM_DELADDR"; + break; + case RTM_GETROUTE: + s = "RTM_GETROUTE"; + break; + case RTM_NEWROUTE: + s = "RTM_NEWROUTE"; + break; + case RTM_DELROUTE: + s = "RTM_DELROUTE"; + break; + case RTM_GETRULE: + s = "RTM_GETRULE"; + break; + case RTM_NEWRULE: + s = "RTM_NEWRULE"; + break; + case RTM_DELRULE: + s = "RTM_DELRULE"; + break; + case RTM_GETQDISC: + s = "RTM_GETQDISC"; + break; + case RTM_NEWQDISC: + s = "RTM_NEWQDISC"; + break; + case RTM_DELQDISC: + s = "RTM_DELQDISC"; + break; + case RTM_GETTFILTER: + s = "RTM_GETTFILTER"; + break; + case RTM_NEWTFILTER: + s = "RTM_NEWTFILTER"; + break; + case RTM_DELTFILTER: + s = "RTM_DELTFILTER"; + break; + case NLMSG_NOOP: + s = "NLMSG_NOOP"; + break; + case NLMSG_ERROR: + s = "NLMSG_ERROR"; + break; + case NLMSG_DONE: + s = "NLMSG_DONE"; + break; + case NLMSG_OVERRUN: + s = "NLMSG_OVERRUN"; + break; + default: + s = NULL; + break; + } + + if (s) + nm_utils_strbuf_append_str(&buf, &len, s); + else + nm_utils_strbuf_append(&buf, &len, "(%u)", (unsigned) hdr->nlmsg_type); + + flags = hdr->nlmsg_flags; + + if (!flags) { + nm_utils_strbuf_append_str(&buf, &len, ", flags 0"); + goto flags_done; + } + +#define _F(f, n) \ + G_STMT_START \ + { \ + if (NM_FLAGS_ALL(flags, f)) { \ + flags &= ~(f); \ + nm_utils_strbuf_append(&buf, &len, "%s%s", prefix, n); \ + if (!flags) \ + goto flags_done; \ + prefix = ","; \ + } \ + } \ + G_STMT_END + + prefix = ", flags "; + flags_before = flags; + _F(NLM_F_REQUEST, "request"); + _F(NLM_F_MULTI, "multi"); + _F(NLM_F_ACK, "ack"); + _F(NLM_F_ECHO, "echo"); + _F(NLM_F_DUMP_INTR, "dump_intr"); + _F(0x20 /*NLM_F_DUMP_FILTERED*/, "dump_filtered"); + + if (flags_before != flags) + prefix = ";"; + + switch (hdr->nlmsg_type) { + case RTM_NEWLINK: + case RTM_NEWADDR: + case RTM_NEWROUTE: + case RTM_NEWQDISC: + case RTM_NEWTFILTER: + _F(NLM_F_REPLACE, "replace"); + _F(NLM_F_EXCL, "excl"); + _F(NLM_F_CREATE, "create"); + _F(NLM_F_APPEND, "append"); + break; + case RTM_GETLINK: + case RTM_GETADDR: + case RTM_GETROUTE: + case RTM_DELQDISC: + case RTM_DELTFILTER: + _F(NLM_F_DUMP, "dump"); + _F(NLM_F_ROOT, "root"); + _F(NLM_F_MATCH, "match"); + _F(NLM_F_ATOMIC, "atomic"); + break; + } + +#undef _F + + if (flags_before != flags) + prefix = ";"; + nm_utils_strbuf_append(&buf, &len, "%s0x%04x", prefix, flags); + +flags_done: + + nm_utils_strbuf_append(&buf, &len, ", seq %u", (unsigned) hdr->nlmsg_seq); + + return b; +} + +/*****************************************************************************/ + +struct nlmsghdr * +nlmsg_hdr(struct nl_msg *n) +{ + return n->nm_nlh; +} + +void * +nlmsg_reserve(struct nl_msg *n, size_t len, int pad) +{ + char * buf = (char *) n->nm_nlh; + size_t nlmsg_len = n->nm_nlh->nlmsg_len; + size_t tlen; + + nm_assert(pad >= 0); + + if (len > n->nm_size) + return NULL; + + tlen = pad ? ((len + (pad - 1)) & ~(pad - 1)) : len; + + if ((tlen + nlmsg_len) > n->nm_size) + return NULL; + + buf += nlmsg_len; + n->nm_nlh->nlmsg_len += tlen; + + if (tlen > len) + memset(buf + len, 0, tlen - len); + + return buf; +} + +/*****************************************************************************/ + +struct nlattr * +nla_reserve(struct nl_msg *msg, int attrtype, int attrlen) +{ + struct nlattr *nla; + int tlen; + + if (attrlen < 0) + return NULL; + + tlen = NLMSG_ALIGN(msg->nm_nlh->nlmsg_len) + nla_total_size(attrlen); + + if (tlen > msg->nm_size) + return NULL; + + nla = (struct nlattr *) nlmsg_tail(msg->nm_nlh); + nla->nla_type = attrtype; + nla->nla_len = nla_attr_size(attrlen); + + if (attrlen) + memset((unsigned char *) nla + nla->nla_len, 0, nla_padlen(attrlen)); + msg->nm_nlh->nlmsg_len = tlen; + + return nla; +} + +/*****************************************************************************/ + +struct nl_msg * +nlmsg_alloc_size(size_t len) +{ + struct nl_msg *nm; + + if (len < sizeof(struct nlmsghdr)) + len = sizeof(struct nlmsghdr); + + nm = g_slice_new(struct nl_msg); + *nm = (struct nl_msg){ + .nm_protocol = -1, + .nm_size = len, + .nm_nlh = g_malloc0(len), + }; + nm->nm_nlh->nlmsg_len = nlmsg_total_size(0); + return nm; +} + +/** + * Allocate a new netlink message with the default maximum payload size. + * + * Allocates a new netlink message without any further payload. The + * maximum payload size defaults to PAGESIZE or as otherwise specified + * with nlmsg_set_default_size(). + * + * @return Newly allocated netlink message or NULL. + */ +struct nl_msg * +nlmsg_alloc(void) +{ + return nlmsg_alloc_size(nm_utils_getpagesize()); +} + +struct nl_msg * +nlmsg_alloc_convert(struct nlmsghdr *hdr) +{ + struct nl_msg *nm; + + nm = nlmsg_alloc_size(NLMSG_ALIGN(hdr->nlmsg_len)); + memcpy(nm->nm_nlh, hdr, hdr->nlmsg_len); + return nm; +} + +struct nl_msg * +nlmsg_alloc_simple(int nlmsgtype, int flags) +{ + struct nl_msg *nm; + struct nlmsghdr *new; + + nm = nlmsg_alloc(); + new = nm->nm_nlh; + new->nlmsg_type = nlmsgtype; + new->nlmsg_flags = flags; + return nm; +} + +void +nlmsg_free(struct nl_msg *msg) +{ + if (!msg) + return; + + g_free(msg->nm_nlh); + g_slice_free(struct nl_msg, msg); +} + +/*****************************************************************************/ + +int +nlmsg_append(struct nl_msg *n, const void *data, size_t len, int pad) +{ + void *tmp; + + nm_assert(n); + nm_assert(data); + nm_assert(len > 0); + nm_assert(pad >= 0); + + tmp = nlmsg_reserve(n, len, pad); + if (tmp == NULL) + return -ENOMEM; + + memcpy(tmp, data, len); + return 0; +} + +/*****************************************************************************/ + +int +nlmsg_parse(struct nlmsghdr * nlh, + int hdrlen, + struct nlattr * tb[], + int maxtype, + const struct nla_policy *policy) +{ + if (!nlmsg_valid_hdr(nlh, hdrlen)) + return -NME_NL_MSG_TOOSHORT; + + return nla_parse(tb, maxtype, nlmsg_attrdata(nlh, hdrlen), nlmsg_attrlen(nlh, hdrlen), policy); +} + +struct nlmsghdr * +nlmsg_put(struct nl_msg *n, uint32_t pid, uint32_t seq, int type, int payload, int flags) +{ + struct nlmsghdr *nlh; + + if (n->nm_nlh->nlmsg_len < NLMSG_HDRLEN) + g_return_val_if_reached(NULL); + + nlh = (struct nlmsghdr *) n->nm_nlh; + nlh->nlmsg_type = type; + nlh->nlmsg_flags = flags; + nlh->nlmsg_pid = pid; + nlh->nlmsg_seq = seq; + + if (payload > 0 && nlmsg_reserve(n, payload, NLMSG_ALIGNTO) == NULL) + return NULL; + + return nlh; +} + +size_t +nla_strlcpy(char *dst, const struct nlattr *nla, size_t dstsize) +{ + const char *src; + size_t srclen; + size_t len; + + /* - Always writes @dstsize bytes to @dst + * - Copies the first non-NUL characters to @dst. + * Any characters after the first NUL bytes in @nla are ignored. + * - If the string @nla is longer than @dstsize, the string + * gets truncated. @dst will always be NUL terminated. */ + + if (G_UNLIKELY(dstsize <= 1)) { + if (dstsize == 1) + dst[0] = '\0'; + if (nla && (srclen = nla_len(nla)) > 0) + return strnlen(nla_data(nla), srclen); + return 0; + } + + nm_assert(dst); + + if (nla) { + srclen = nla_len(nla); + if (srclen > 0) { + src = nla_data(nla); + srclen = strnlen(src, srclen); + if (srclen > 0) { + len = NM_MIN(dstsize - 1, srclen); + memcpy(dst, src, len); + memset(&dst[len], 0, dstsize - len); + return srclen; + } + } + } + + memset(dst, 0, dstsize); + return 0; +} + +size_t +nla_memcpy(void *dst, const struct nlattr *nla, size_t dstsize) +{ + size_t len; + int srclen; + + if (!nla) + return 0; + + srclen = nla_len(nla); + + if (srclen <= 0) { + nm_assert(srclen == 0); + return 0; + } + + len = NM_MIN((size_t) srclen, dstsize); + if (len > 0) { + /* there is a crucial difference between nla_strlcpy() and nla_memcpy(). + * The former always write @dstsize bytes (akin to strncpy()), here, we only + * write the bytes that we actually have (leaving the remainder undefined). */ + memcpy(dst, nla_data(nla), len); + } + + return srclen; +} + +int +nla_put(struct nl_msg *msg, int attrtype, int datalen, const void *data) +{ + struct nlattr *nla; + + nla = nla_reserve(msg, attrtype, datalen); + if (!nla) { + if (datalen < 0) + g_return_val_if_reached(-NME_BUG); + + return -ENOMEM; + } + + if (datalen > 0) + memcpy(nla_data(nla), data, datalen); + + return 0; +} + +struct nlattr * +nla_find(const struct nlattr *head, int len, int attrtype) +{ + const struct nlattr *nla; + int rem; + + nla_for_each_attr (nla, head, len, rem) { + if (nla_type(nla) == attrtype) + return (struct nlattr *) nla; + } + + return NULL; +} + +void +nla_nest_cancel(struct nl_msg *msg, const struct nlattr *attr) +{ + ssize_t len; + + len = (char *) nlmsg_tail(msg->nm_nlh) - (char *) attr; + if (len < 0) + g_return_if_reached(); + else if (len > 0) { + msg->nm_nlh->nlmsg_len -= len; + memset(nlmsg_tail(msg->nm_nlh), 0, len); + } +} + +struct nlattr * +nla_nest_start(struct nl_msg *msg, int attrtype) +{ + struct nlattr *start = (struct nlattr *) nlmsg_tail(msg->nm_nlh); + + if (nla_put(msg, NLA_F_NESTED | attrtype, 0, NULL) < 0) + return NULL; + + return start; +} + +static int +_nest_end(struct nl_msg *msg, struct nlattr *start, int keep_empty) +{ + size_t pad, len; + + len = (char *) nlmsg_tail(msg->nm_nlh) - (char *) start; + + if (len > USHRT_MAX || (!keep_empty && len == NLA_HDRLEN)) { + /* + * Max nlattr size exceeded or empty nested attribute, trim the + * attribute header again + */ + nla_nest_cancel(msg, start); + + /* Return error only if nlattr size was exceeded */ + return (len == NLA_HDRLEN) ? 0 : -NME_NL_ATTRSIZE; + } + + start->nla_len = len; + + pad = NLMSG_ALIGN(msg->nm_nlh->nlmsg_len) - msg->nm_nlh->nlmsg_len; + if (pad > 0) { + /* + * Data inside attribute does not end at a alignment boundary. + * Pad accordingly and account for the additional space in + * the message. nlmsg_reserve() may never fail in this situation, + * the allocate message buffer must be a multiple of NLMSG_ALIGNTO. + */ + if (!nlmsg_reserve(msg, pad, 0)) + g_return_val_if_reached(-NME_BUG); + } + + return 0; +} + +int +nla_nest_end(struct nl_msg *msg, struct nlattr *start) +{ + return _nest_end(msg, start, 0); +} + +static const uint16_t nla_attr_minlen[NLA_TYPE_MAX + 1] = { + [NLA_U8] = sizeof(uint8_t), + [NLA_U16] = sizeof(uint16_t), + [NLA_U32] = sizeof(uint32_t), + [NLA_U64] = sizeof(uint64_t), + [NLA_STRING] = 1, + [NLA_FLAG] = 0, +}; + +static int +validate_nla(const struct nlattr *nla, int maxtype, const struct nla_policy *policy) +{ + const struct nla_policy *pt; + unsigned int minlen = 0; + int type = nla_type(nla); + + if (type < 0 || type > maxtype) + return 0; + + pt = &policy[type]; + + if (pt->type > NLA_TYPE_MAX) + g_return_val_if_reached(-NME_BUG); + + if (pt->minlen) + minlen = pt->minlen; + else if (pt->type != NLA_UNSPEC) + minlen = nla_attr_minlen[pt->type]; + + if (nla_len(nla) < minlen) + return -NME_UNSPEC; + + if (pt->maxlen && nla_len(nla) > pt->maxlen) + return -NME_UNSPEC; + + if (pt->type == NLA_STRING) { + const char *data; + + nm_assert(minlen > 0); + + data = nla_data(nla); + if (data[nla_len(nla) - 1] != '\0') + return -NME_UNSPEC; + } + + return 0; +} + +int +nla_parse(struct nlattr * tb[], + int maxtype, + struct nlattr * head, + int len, + const struct nla_policy *policy) +{ + struct nlattr *nla; + int rem, nmerr; + + memset(tb, 0, sizeof(struct nlattr *) * (maxtype + 1)); + + nla_for_each_attr (nla, head, len, rem) { + int type = nla_type(nla); + + if (type > maxtype) + continue; + + if (policy) { + nmerr = validate_nla(nla, maxtype, policy); + if (nmerr < 0) + return nmerr; + } + + tb[type] = nla; + } + + return 0; +} + +/*****************************************************************************/ + +int +nlmsg_get_proto(struct nl_msg *msg) +{ + return msg->nm_protocol; +} + +void +nlmsg_set_proto(struct nl_msg *msg, int protocol) +{ + msg->nm_protocol = protocol; +} + +void +nlmsg_set_src(struct nl_msg *msg, struct sockaddr_nl *addr) +{ + memcpy(&msg->nm_src, addr, sizeof(*addr)); +} + +struct ucred * +nlmsg_get_creds(struct nl_msg *msg) +{ + if (msg->nm_creds_has) + return &msg->nm_creds; + return NULL; +} + +void +nlmsg_set_creds(struct nl_msg *msg, struct ucred *creds) +{ + if (creds) { + memcpy(&msg->nm_creds, creds, sizeof(*creds)); + msg->nm_creds_has = TRUE; + } else + msg->nm_creds_has = FALSE; +} + +/*****************************************************************************/ + +void * +genlmsg_put(struct nl_msg *msg, + uint32_t port, + uint32_t seq, + int family, + int hdrlen, + int flags, + uint8_t cmd, + uint8_t version) +{ + struct nlmsghdr * nlh; + struct genlmsghdr hdr = { + .cmd = cmd, + .version = version, + }; + + nlh = nlmsg_put(msg, port, seq, family, GENL_HDRLEN + hdrlen, flags); + if (nlh == NULL) + return NULL; + + memcpy(nlmsg_data(nlh), &hdr, sizeof(hdr)); + + return (char *) nlmsg_data(nlh) + GENL_HDRLEN; +} + +void * +genlmsg_data(const struct genlmsghdr *gnlh) +{ + return ((unsigned char *) gnlh + GENL_HDRLEN); +} + +void * +genlmsg_user_hdr(const struct genlmsghdr *gnlh) +{ + return genlmsg_data(gnlh); +} + +struct genlmsghdr * +genlmsg_hdr(struct nlmsghdr *nlh) +{ + return nlmsg_data(nlh); +} + +void * +genlmsg_user_data(const struct genlmsghdr *gnlh, const int hdrlen) +{ + return (char *) genlmsg_user_hdr(gnlh) + NLMSG_ALIGN(hdrlen); +} + +struct nlattr * +genlmsg_attrdata(const struct genlmsghdr *gnlh, int hdrlen) +{ + return genlmsg_user_data(gnlh, hdrlen); +} + +int +genlmsg_len(const struct genlmsghdr *gnlh) +{ + const struct nlmsghdr *nlh; + + nlh = (const struct nlmsghdr *) ((const unsigned char *) gnlh - NLMSG_HDRLEN); + return (nlh->nlmsg_len - GENL_HDRLEN - NLMSG_HDRLEN); +} + +int +genlmsg_attrlen(const struct genlmsghdr *gnlh, int hdrlen) +{ + return genlmsg_len(gnlh) - NLMSG_ALIGN(hdrlen); +} + +int +genlmsg_valid_hdr(struct nlmsghdr *nlh, int hdrlen) +{ + struct genlmsghdr *ghdr; + + if (!nlmsg_valid_hdr(nlh, GENL_HDRLEN)) + return 0; + + ghdr = nlmsg_data(nlh); + if (genlmsg_len(ghdr) < NLMSG_ALIGN(hdrlen)) + return 0; + + return 1; +} + +int +genlmsg_parse(struct nlmsghdr * nlh, + int hdrlen, + struct nlattr * tb[], + int maxtype, + const struct nla_policy *policy) +{ + struct genlmsghdr *ghdr; + + if (!genlmsg_valid_hdr(nlh, hdrlen)) + return -NME_NL_MSG_TOOSHORT; + + ghdr = nlmsg_data(nlh); + return nla_parse(tb, + maxtype, + genlmsg_attrdata(ghdr, hdrlen), + genlmsg_attrlen(ghdr, hdrlen), + policy); +} + +static int +_genl_parse_getfamily(struct nl_msg *msg, void *arg) +{ + static const struct nla_policy ctrl_policy[] = { + [CTRL_ATTR_FAMILY_ID] = {.type = NLA_U16}, + [CTRL_ATTR_FAMILY_NAME] = {.type = NLA_STRING, .maxlen = GENL_NAMSIZ}, + [CTRL_ATTR_VERSION] = {.type = NLA_U32}, + [CTRL_ATTR_HDRSIZE] = {.type = NLA_U32}, + [CTRL_ATTR_MAXATTR] = {.type = NLA_U32}, + [CTRL_ATTR_OPS] = {.type = NLA_NESTED}, + [CTRL_ATTR_MCAST_GROUPS] = {.type = NLA_NESTED}, + }; + struct nlattr * tb[G_N_ELEMENTS(ctrl_policy)]; + struct nlmsghdr *nlh = nlmsg_hdr(msg); + gint32 * response_data = arg; + + if (genlmsg_parse_arr(nlh, 0, tb, ctrl_policy) < 0) + return NL_SKIP; + + if (tb[CTRL_ATTR_FAMILY_ID]) + *response_data = nla_get_u16(tb[CTRL_ATTR_FAMILY_ID]); + + return NL_STOP; +} + +int +genl_ctrl_resolve(struct nl_sock *sk, const char *name) +{ + nm_auto_nlmsg struct nl_msg *msg = NULL; + int nmerr; + gint32 response_data = -1; + const struct nl_cb cb = { + .valid_cb = _genl_parse_getfamily, + .valid_arg = &response_data, + }; + + msg = nlmsg_alloc(); + + if (!genlmsg_put(msg, NL_AUTO_PORT, NL_AUTO_SEQ, GENL_ID_CTRL, 0, 0, CTRL_CMD_GETFAMILY, 1)) + return -ENOMEM; + + nmerr = nla_put_string(msg, CTRL_ATTR_FAMILY_NAME, name); + if (nmerr < 0) + return nmerr; + + nmerr = nl_send_auto(sk, msg); + if (nmerr < 0) + return nmerr; + + nmerr = nl_recvmsgs(sk, &cb); + if (nmerr < 0) + return nmerr; + + /* If search was successful, request may be ACKed after data */ + nmerr = nl_wait_for_ack(sk, NULL); + if (nmerr < 0) + return nmerr; + + if (response_data < 0) + return -NME_UNSPEC; + + return response_data; +} + +/*****************************************************************************/ + +struct nl_sock * +nl_socket_alloc(void) +{ + struct nl_sock *sk; + + sk = g_slice_new0(struct nl_sock); + + sk->s_fd = -1; + sk->s_local.nl_family = AF_NETLINK; + sk->s_peer.nl_family = AF_NETLINK; + sk->s_seq_expect = sk->s_seq_next = time(NULL); + + return sk; +} + +void +nl_socket_free(struct nl_sock *sk) +{ + if (!sk) + return; + + if (sk->s_fd >= 0) + nm_close(sk->s_fd); + g_slice_free(struct nl_sock, sk); +} + +int +nl_socket_get_fd(const struct nl_sock *sk) +{ + return sk->s_fd; +} + +uint32_t +nl_socket_get_local_port(const struct nl_sock *sk) +{ + return sk->s_local.nl_pid; +} + +size_t +nl_socket_get_msg_buf_size(struct nl_sock *sk) +{ + return sk->s_bufsize; +} + +int +nl_socket_set_passcred(struct nl_sock *sk, int state) +{ + int err; + + if (sk->s_fd == -1) + return -NME_NL_BAD_SOCK; + + err = setsockopt(sk->s_fd, SOL_SOCKET, SO_PASSCRED, &state, sizeof(state)); + if (err < 0) + return -nm_errno_from_native(errno); + + if (state) + sk->s_flags |= NL_SOCK_PASSCRED; + else + sk->s_flags &= ~NL_SOCK_PASSCRED; + + return 0; +} + +int +nl_socket_set_msg_buf_size(struct nl_sock *sk, size_t bufsize) +{ + sk->s_bufsize = bufsize; + + return 0; +} + +struct sockaddr_nl * +nlmsg_get_dst(struct nl_msg *msg) +{ + return &msg->nm_dst; +} + +int +nl_socket_set_nonblocking(const struct nl_sock *sk) +{ + if (sk->s_fd == -1) + return -NME_NL_BAD_SOCK; + + if (fcntl(sk->s_fd, F_SETFL, O_NONBLOCK) < 0) + return -nm_errno_from_native(errno); + + return 0; +} + +int +nl_socket_set_buffer_size(struct nl_sock *sk, int rxbuf, int txbuf) +{ + int err; + + if (rxbuf <= 0) + rxbuf = 32768; + + if (txbuf <= 0) + txbuf = 32768; + + if (sk->s_fd == -1) + return -NME_NL_BAD_SOCK; + + err = setsockopt(sk->s_fd, SOL_SOCKET, SO_SNDBUF, &txbuf, sizeof(txbuf)); + if (err < 0) { + return -nm_errno_from_native(errno); + } + + err = setsockopt(sk->s_fd, SOL_SOCKET, SO_RCVBUF, &rxbuf, sizeof(rxbuf)); + if (err < 0) { + return -nm_errno_from_native(errno); + } + + return 0; +} + +int +nl_socket_add_memberships(struct nl_sock *sk, int group, ...) +{ + int err; + va_list ap; + + if (sk->s_fd == -1) + return -NME_NL_BAD_SOCK; + + va_start(ap, group); + + while (group != 0) { + if (group < 0) { + va_end(ap); + g_return_val_if_reached(-NME_BUG); + } + + err = setsockopt(sk->s_fd, SOL_NETLINK, NETLINK_ADD_MEMBERSHIP, &group, sizeof(group)); + if (err < 0) { + int errsv = errno; + + va_end(ap); + return -nm_errno_from_native(errsv); + } + + group = va_arg(ap, int); + } + + va_end(ap); + + return 0; +} + +int +nl_socket_set_ext_ack(struct nl_sock *sk, gboolean enable) +{ + int err, val; + + if (sk->s_fd == -1) + return -NME_NL_BAD_SOCK; + + val = !!enable; + err = setsockopt(sk->s_fd, SOL_NETLINK, NETLINK_EXT_ACK, &val, sizeof(val)); + if (err < 0) + return -nm_errno_from_native(errno); + + return 0; +} + +void +nl_socket_disable_msg_peek(struct nl_sock *sk) +{ + sk->s_flags |= NL_MSG_PEEK_EXPLICIT; + sk->s_flags &= ~NL_MSG_PEEK; +} + +int +nl_connect(struct nl_sock *sk, int protocol) +{ + int err, nmerr; + socklen_t addrlen; + struct sockaddr_nl local = {0}; + + if (sk->s_fd != -1) + return -NME_NL_BAD_SOCK; + + sk->s_fd = socket(AF_NETLINK, SOCK_RAW | SOCK_CLOEXEC, protocol); + if (sk->s_fd < 0) { + nmerr = -nm_errno_from_native(errno); + goto errout; + } + + nmerr = nl_socket_set_buffer_size(sk, 0, 0); + if (nmerr < 0) + goto errout; + + nm_assert(sk->s_local.nl_pid == 0); + + err = bind(sk->s_fd, (struct sockaddr *) &sk->s_local, sizeof(sk->s_local)); + if (err != 0) { + nmerr = -nm_errno_from_native(errno); + goto errout; + } + + addrlen = sizeof(local); + err = getsockname(sk->s_fd, (struct sockaddr *) &local, &addrlen); + if (err < 0) { + nmerr = -nm_errno_from_native(errno); + goto errout; + } + + if (addrlen != sizeof(local)) { + nmerr = -NME_UNSPEC; + goto errout; + } + + if (local.nl_family != AF_NETLINK) { + nmerr = -NME_UNSPEC; + goto errout; + } + + sk->s_local = local; + sk->s_proto = protocol; + + return 0; + +errout: + if (sk->s_fd != -1) { + close(sk->s_fd); + sk->s_fd = -1; + } + return nmerr; +} + +/*****************************************************************************/ + +static void +_cb_init(struct nl_cb *dst, const struct nl_cb *src) +{ + nm_assert(dst); + + if (src) + *dst = *src; + else + memset(dst, 0, sizeof(*dst)); +} + +static int +ack_wait_handler(struct nl_msg *msg, void *arg) +{ + return NL_STOP; +} + +int +nl_wait_for_ack(struct nl_sock *sk, const struct nl_cb *cb) +{ + struct nl_cb cb2; + + _cb_init(&cb2, cb); + cb2.ack_cb = ack_wait_handler; + return nl_recvmsgs(sk, &cb2); +} + +#define NL_CB_CALL(cb, type, msg) \ + do { \ + const struct nl_cb *_cb = (cb); \ + \ + if (_cb && _cb->type##_cb) { \ + /* the returned value here must be either a negative + * netlink error number, or one of NL_SKIP, NL_STOP, NL_OK. */ \ + nmerr = _cb->type##_cb((msg), _cb->type##_arg); \ + switch (nmerr) { \ + case NL_OK: \ + nm_assert(nmerr == 0); \ + break; \ + case NL_SKIP: \ + goto skip; \ + case NL_STOP: \ + goto stop; \ + default: \ + if (nmerr >= 0) { \ + nm_assert_not_reached(); \ + nmerr = -NME_BUG; \ + } \ + goto out; \ + } \ + } \ + } while (0) + +int +nl_recvmsgs(struct nl_sock *sk, const struct nl_cb *cb) +{ + int n, nmerr = 0, multipart = 0, interrupted = 0, nrecv = 0; + gs_free unsigned char *buf = NULL; + struct nlmsghdr * hdr; + struct sockaddr_nl nla = {0}; + struct ucred creds; + gboolean creds_has; + +continue_reading: + n = nl_recv(sk, &nla, &buf, &creds, &creds_has); + if (n <= 0) + return n; + + hdr = (struct nlmsghdr *) buf; + while (nlmsg_ok(hdr, n)) { + nm_auto_nlmsg struct nl_msg *msg = NULL; + + msg = nlmsg_alloc_convert(hdr); + + nlmsg_set_proto(msg, sk->s_proto); + nlmsg_set_src(msg, &nla); + nlmsg_set_creds(msg, creds_has ? &creds : NULL); + + nrecv++; + + /* Only do sequence checking if auto-ack mode is enabled */ + if (!(sk->s_flags & NL_NO_AUTO_ACK)) { + if (hdr->nlmsg_seq != sk->s_seq_expect) { + nmerr = -NME_NL_SEQ_MISMATCH; + goto out; + } + } + + if (hdr->nlmsg_type == NLMSG_DONE || hdr->nlmsg_type == NLMSG_ERROR + || hdr->nlmsg_type == NLMSG_NOOP || hdr->nlmsg_type == NLMSG_OVERRUN) { + /* We can't check for !NLM_F_MULTI since some netlink + * users in the kernel are broken. */ + sk->s_seq_expect++; + } + + if (hdr->nlmsg_flags & NLM_F_MULTI) + multipart = 1; + + if (hdr->nlmsg_flags & NLM_F_DUMP_INTR) { + /* + * We have to continue reading to clear + * all messages until a NLMSG_DONE is + * received and report the inconsistency. + */ + interrupted = 1; + } + + /* messages terminates a multipart message, this is + * usually the end of a message and therefore we slip + * out of the loop by default. the user may overrule + * this action by skipping this packet. */ + if (hdr->nlmsg_type == NLMSG_DONE) { + multipart = 0; + NL_CB_CALL(cb, finish, msg); + } + + /* Message to be ignored, the default action is to + * skip this message if no callback is specified. The + * user may overrule this action by returning + * NL_PROCEED. */ + else if (hdr->nlmsg_type == NLMSG_NOOP) + goto skip; + + /* Data got lost, report back to user. The default action is to + * quit parsing. The user may overrule this action by returning + * NL_SKIP or NL_PROCEED (dangerous) */ + else if (hdr->nlmsg_type == NLMSG_OVERRUN) { + nmerr = -NME_NL_MSG_OVERFLOW; + goto out; + } + + /* Message carries a nlmsgerr */ + else if (hdr->nlmsg_type == NLMSG_ERROR) { + struct nlmsgerr *e = nlmsg_data(hdr); + + if (hdr->nlmsg_len < nlmsg_size(sizeof(*e))) { + /* Truncated error message, the default action + * is to stop parsing. The user may overrule + * this action by returning NL_SKIP or + * NL_PROCEED (dangerous) */ + nmerr = -NME_NL_MSG_TRUNC; + goto out; + } + if (e->error) { + /* Error message reported back from kernel. */ + if (cb && cb->err_cb) { + /* the returned value here must be either a negative + * netlink error number, or one of NL_SKIP, NL_STOP, NL_OK. */ + nmerr = cb->err_cb(&nla, e, cb->err_arg); + if (nmerr < 0) + goto out; + else if (nmerr == NL_SKIP) + goto skip; + else if (nmerr == NL_STOP) { + nmerr = -nm_errno_from_native(e->error); + goto out; + } + nm_assert(nmerr == NL_OK); + } else { + nmerr = -nm_errno_from_native(e->error); + goto out; + } + } else + NL_CB_CALL(cb, ack, msg); + } else { + /* Valid message (not checking for MULTIPART bit to + * get along with broken kernels. NL_SKIP has no + * effect on this. */ + NL_CB_CALL(cb, valid, msg); + } +skip: + nmerr = 0; + hdr = nlmsg_next(hdr, &n); + } + + if (multipart) { + /* Multipart message not yet complete, continue reading */ + nm_clear_g_free(&buf); + + nmerr = 0; + goto continue_reading; + } + +stop: + nmerr = 0; + +out: + if (interrupted) + nmerr = -NME_NL_DUMP_INTR; + + nm_assert(nmerr <= 0); + return nmerr ?: nrecv; +} + +int +nl_sendmsg(struct nl_sock *sk, struct nl_msg *msg, struct msghdr *hdr) +{ + int ret; + + if (sk->s_fd < 0) + return -NME_NL_BAD_SOCK; + + nlmsg_set_src(msg, &sk->s_local); + + ret = sendmsg(sk->s_fd, hdr, 0); + if (ret < 0) + return -nm_errno_from_native(errno); + + return ret; +} + +int +nl_send_iovec(struct nl_sock *sk, struct nl_msg *msg, struct iovec *iov, unsigned iovlen) +{ + struct sockaddr_nl *dst; + struct ucred * creds; + struct msghdr hdr = { + .msg_name = (void *) &sk->s_peer, + .msg_namelen = sizeof(struct sockaddr_nl), + .msg_iov = iov, + .msg_iovlen = iovlen, + }; + char buf[CMSG_SPACE(sizeof(struct ucred))]; + + /* Overwrite destination if specified in the message itself, defaults + * to the peer address of the socket. + */ + dst = nlmsg_get_dst(msg); + if (dst->nl_family == AF_NETLINK) + hdr.msg_name = dst; + + /* Add credentials if present. */ + creds = nlmsg_get_creds(msg); + if (creds != NULL) { + struct cmsghdr *cmsg; + + hdr.msg_control = buf; + hdr.msg_controllen = sizeof(buf); + + cmsg = CMSG_FIRSTHDR(&hdr); + cmsg->cmsg_level = SOL_SOCKET; + cmsg->cmsg_type = SCM_CREDENTIALS; + cmsg->cmsg_len = CMSG_LEN(sizeof(struct ucred)); + memcpy(CMSG_DATA(cmsg), creds, sizeof(struct ucred)); + } + + return nl_sendmsg(sk, msg, &hdr); +} + +void +nl_complete_msg(struct nl_sock *sk, struct nl_msg *msg) +{ + struct nlmsghdr *nlh; + + nlh = nlmsg_hdr(msg); + if (nlh->nlmsg_pid == NL_AUTO_PORT) + nlh->nlmsg_pid = nl_socket_get_local_port(sk); + + if (nlh->nlmsg_seq == NL_AUTO_SEQ) + nlh->nlmsg_seq = sk->s_seq_next++; + + if (msg->nm_protocol == -1) + msg->nm_protocol = sk->s_proto; + + nlh->nlmsg_flags |= NLM_F_REQUEST; + + if (!(sk->s_flags & NL_NO_AUTO_ACK)) + nlh->nlmsg_flags |= NLM_F_ACK; +} + +int +nl_send(struct nl_sock *sk, struct nl_msg *msg) +{ + struct iovec iov = { + .iov_base = (void *) nlmsg_hdr(msg), + .iov_len = nlmsg_hdr(msg)->nlmsg_len, + }; + + return nl_send_iovec(sk, msg, &iov, 1); +} + +int +nl_send_auto(struct nl_sock *sk, struct nl_msg *msg) +{ + nl_complete_msg(sk, msg); + + return nl_send(sk, msg); +} + +int +nl_recv(struct nl_sock * sk, + struct sockaddr_nl *nla, + unsigned char ** buf, + struct ucred * out_creds, + gboolean * out_creds_has) +{ + ssize_t n; + int flags = 0; + struct iovec iov; + struct msghdr msg = { + .msg_name = (void *) nla, + .msg_namelen = sizeof(struct sockaddr_nl), + .msg_iov = &iov, + .msg_iovlen = 1, + }; + struct ucred tmpcreds; + gboolean tmpcreds_has = FALSE; + int retval; + int errsv; + + nm_assert(nla); + nm_assert(buf && !*buf); + nm_assert(!out_creds_has == !out_creds); + + if ((sk->s_flags & NL_MSG_PEEK) + || (!(sk->s_flags & NL_MSG_PEEK_EXPLICIT) && sk->s_bufsize == 0)) + flags |= MSG_PEEK | MSG_TRUNC; + + iov.iov_len = sk->s_bufsize ?: (((size_t) nm_utils_getpagesize()) * 4u); + iov.iov_base = g_malloc(iov.iov_len); + + if (out_creds && (sk->s_flags & NL_SOCK_PASSCRED)) { + msg.msg_controllen = CMSG_SPACE(sizeof(struct ucred)); + msg.msg_control = g_malloc(msg.msg_controllen); + } + +retry: + n = recvmsg(sk->s_fd, &msg, flags); + if (!n) { + retval = 0; + goto abort; + } + + if (n < 0) { + errsv = errno; + if (errsv == EINTR) + goto retry; + retval = -nm_errno_from_native(errsv); + goto abort; + } + + if (msg.msg_flags & MSG_CTRUNC) { + if (msg.msg_controllen == 0) { + retval = -NME_NL_MSG_TRUNC; + goto abort; + } + + msg.msg_controllen *= 2; + msg.msg_control = g_realloc(msg.msg_control, msg.msg_controllen); + goto retry; + } + + if (iov.iov_len < n || (msg.msg_flags & MSG_TRUNC)) { + /* respond with error to an incomplete message */ + if (flags == 0) { + retval = -NME_NL_MSG_TRUNC; + goto abort; + } + + /* Provided buffer is not long enough, enlarge it + * to size of n (which should be total length of the message) + * and try again. */ + iov.iov_base = g_realloc(iov.iov_base, n); + iov.iov_len = n; + flags = 0; + goto retry; + } + + if (flags != 0) { + /* Buffer is big enough, do the actual reading */ + flags = 0; + goto retry; + } + + if (msg.msg_namelen != sizeof(struct sockaddr_nl)) { + retval = -NME_UNSPEC; + goto abort; + } + + if (out_creds && (sk->s_flags & NL_SOCK_PASSCRED)) { + struct cmsghdr *cmsg; + + for (cmsg = CMSG_FIRSTHDR(&msg); cmsg; cmsg = CMSG_NXTHDR(&msg, cmsg)) { + if (cmsg->cmsg_level != SOL_SOCKET) + continue; + if (cmsg->cmsg_type != SCM_CREDENTIALS) + continue; + memcpy(&tmpcreds, CMSG_DATA(cmsg), sizeof(tmpcreds)); + tmpcreds_has = TRUE; + break; + } + } + + retval = n; + +abort: + g_free(msg.msg_control); + + if (retval <= 0) { + g_free(iov.iov_base); + return retval; + } + + *buf = iov.iov_base; + if (out_creds && tmpcreds_has) + *out_creds = tmpcreds; + NM_SET_OUT(out_creds_has, tmpcreds_has); + return retval; +} diff --git a/shared/nm-platform/nm-netlink.h b/shared/nm-platform/nm-netlink.h new file mode 100644 index 00000000..8de42531 --- /dev/null +++ b/shared/nm-platform/nm-netlink.h @@ -0,0 +1,616 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright (C) 2018 Red Hat, Inc. + */ + +#ifndef __NM_NETLINK_H__ +#define __NM_NETLINK_H__ + +#include <linux/netlink.h> +#include <linux/rtnetlink.h> +#include <linux/genetlink.h> + +#include "nm-std-aux/unaligned.h" + +/*****************************************************************************/ + +#define NLMSGERR_ATTR_UNUSED 0 +#define NLMSGERR_ATTR_MSG 1 +#define NLMSGERR_ATTR_OFFS 2 +#define NLMSGERR_ATTR_COOKIE 3 +#define NLMSGERR_ATTR_MAX 3 + +#ifndef NLM_F_ACK_TLVS + #define NLM_F_ACK_TLVS 0x200 +#endif + +/*****************************************************************************/ + +/* Basic attribute data types */ +enum { + NLA_UNSPEC, /* Unspecified type, binary data chunk */ + NLA_U8, /* 8 bit integer */ + NLA_U16, /* 16 bit integer */ + NLA_U32, /* 32 bit integer */ + NLA_U64, /* 64 bit integer */ + NLA_STRING, /* NUL terminated character string */ + NLA_FLAG, /* Flag */ + NLA_MSECS, /* Micro seconds (64bit) */ + NLA_NESTED, /* Nested attributes */ + NLA_NESTED_COMPAT, + NLA_NUL_STRING, + NLA_BINARY, + NLA_S8, + NLA_S16, + NLA_S32, + NLA_S64, + __NLA_TYPE_MAX, +}; + +#define NLA_TYPE_MAX (__NLA_TYPE_MAX - 1) + +struct nl_msg; + +/*****************************************************************************/ + +const char *nl_nlmsgtype2str(int type, char *buf, size_t size); + +const char *nl_nlmsg_flags2str(int flags, char *buf, size_t len); + +const char *nl_nlmsghdr_to_str(const struct nlmsghdr *hdr, char *buf, gsize len); + +/*****************************************************************************/ + +struct nla_policy { + /* Type of attribute or NLA_UNSPEC */ + uint16_t type; + + /* Minimal length of payload required */ + uint16_t minlen; + + /* Maximal length of payload allowed */ + uint16_t maxlen; +}; + +/*****************************************************************************/ + +/* static asserts that @tb and @policy are suitable arguments to nla_parse(). */ +#define _nl_static_assert_tb(tb, policy) \ + G_STMT_START \ + { \ + G_STATIC_ASSERT_EXPR(G_N_ELEMENTS(tb) > 0); \ + \ + /* We allow @policy to be either a C array or NULL. The sizeof() + * must either match the expected array size or the sizeof(NULL), + * but not both. */ \ + G_STATIC_ASSERT_EXPR((sizeof(policy) == G_N_ELEMENTS(tb) * sizeof(struct nla_policy)) \ + ^ (sizeof(policy) == sizeof(NULL))); \ + } \ + G_STMT_END + +/*****************************************************************************/ + +static inline int +nla_attr_size(int payload) +{ + nm_assert(payload >= 0); + + return NLA_HDRLEN + payload; +} + +static inline int +nla_total_size(int payload) +{ + return NLA_ALIGN(nla_attr_size(payload)); +} + +static inline int +nla_padlen(int payload) +{ + return nla_total_size(payload) - nla_attr_size(payload); +} + +struct nlattr *nla_reserve(struct nl_msg *msg, int attrtype, int attrlen); + +static inline int +nla_len(const struct nlattr *nla) +{ + nm_assert(nla); + nm_assert(nla->nla_len >= NLA_HDRLEN); + + return ((int) nla->nla_len) - NLA_HDRLEN; +} + +static inline int +nla_type(const struct nlattr *nla) +{ + nm_assert(nla_len(nla) >= 0); + + return nla->nla_type & NLA_TYPE_MASK; +} + +static inline void * +nla_data(const struct nlattr *nla) +{ + nm_assert(nla_len(nla) >= 0); + + return &(((char *) nla)[NLA_HDRLEN]); +} + +#define nla_data_as(type, nla) \ + ({ \ + const struct nlattr *_nla = (nla); \ + \ + nm_assert(nla_len(_nla) >= sizeof(type)); \ + \ + /* note that casting the pointer is undefined behavior in C, if + * the data has wrong alignment. Netlink data is aligned to 4 bytes, + * that means, if the alignment is larger than 4, this is invalid. */ \ + G_STATIC_ASSERT_EXPR(_nm_alignof(type) <= NLA_ALIGNTO); \ + \ + (type *) nla_data(_nla); \ + }) + +static inline uint8_t +nla_get_u8(const struct nlattr *nla) +{ + nm_assert(nla_len(nla) >= sizeof(uint8_t)); + + return *((const uint8_t *) nla_data(nla)); +} + +static inline int8_t +nla_get_s8(const struct nlattr *nla) +{ + nm_assert(nla_len(nla) >= sizeof(int8_t)); + + return *((const int8_t *) nla_data(nla)); +} + +static inline uint8_t +nla_get_u8_cond(/*const*/ struct nlattr *const *tb, int attr, uint8_t default_val) +{ + nm_assert(tb); + nm_assert(attr >= 0); + + return tb[attr] ? nla_get_u8(tb[attr]) : default_val; +} + +static inline uint16_t +nla_get_u16(const struct nlattr *nla) +{ + nm_assert(nla_len(nla) >= sizeof(uint16_t)); + + return *((const uint16_t *) nla_data(nla)); +} + +static inline uint32_t +nla_get_u32(const struct nlattr *nla) +{ + nm_assert(nla_len(nla) >= sizeof(uint32_t)); + + return *((const uint32_t *) nla_data(nla)); +} + +static inline int32_t +nla_get_s32(const struct nlattr *nla) +{ + nm_assert(nla_len(nla) >= sizeof(int32_t)); + + return *((const int32_t *) nla_data(nla)); +} + +static inline uint64_t +nla_get_u64(const struct nlattr *nla) +{ + nm_assert(nla_len(nla) >= sizeof(uint64_t)); + + return unaligned_read_ne64(nla_data(nla)); +} + +static inline uint64_t +nla_get_be64(const struct nlattr *nla) +{ + nm_assert(nla_len(nla) >= sizeof(uint64_t)); + + return unaligned_read_be64(nla_data(nla)); +} + +static inline char * +nla_get_string(const struct nlattr *nla) +{ + nm_assert(nla_len(nla) >= 0); + + return (char *) nla_data(nla); +} + +size_t nla_strlcpy(char *dst, const struct nlattr *nla, size_t dstsize); + +size_t nla_memcpy(void *dst, const struct nlattr *nla, size_t dstsize); + +#define nla_memcpy_checked_size(dst, nla, dstsize) \ + G_STMT_START \ + { \ + void *const _dst = (dst); \ + const struct nlattr *const _nla = (nla); \ + const size_t _dstsize = (dstsize); \ + size_t _srcsize; \ + \ + /* assert that, if @nla is given, that it has the exact expected + * size. This implies that the caller previously verified the length + * of the attribute (via minlen/maxlen at nla_parse()). */ \ + \ + if (_nla) { \ + _srcsize = nla_memcpy(_dst, _nla, _dstsize); \ + nm_assert(_srcsize == _dstsize); \ + } \ + } \ + G_STMT_END + +int nla_put(struct nl_msg *msg, int attrtype, int datalen, const void *data); + +static inline int +nla_put_string(struct nl_msg *msg, int attrtype, const char *str) +{ + nm_assert(str); + + return nla_put(msg, attrtype, strlen(str) + 1, str); +} + +static inline int +nla_put_uint8(struct nl_msg *msg, int attrtype, uint8_t val) +{ + return nla_put(msg, attrtype, sizeof(val), &val); +} + +static inline int +nla_put_uint16(struct nl_msg *msg, int attrtype, uint16_t val) +{ + return nla_put(msg, attrtype, sizeof(val), &val); +} + +static inline int +nla_put_uint32(struct nl_msg *msg, int attrtype, uint32_t val) +{ + return nla_put(msg, attrtype, sizeof(val), &val); +} + +#define NLA_PUT(msg, attrtype, attrlen, data) \ + G_STMT_START \ + { \ + if (nla_put(msg, attrtype, attrlen, data) < 0) \ + goto nla_put_failure; \ + } \ + G_STMT_END + +#define NLA_PUT_TYPE(msg, type, attrtype, value) \ + G_STMT_START \ + { \ + type __nla_tmp = value; \ + NLA_PUT(msg, attrtype, sizeof(type), &__nla_tmp); \ + } \ + G_STMT_END + +#define NLA_PUT_U8(msg, attrtype, value) NLA_PUT_TYPE(msg, uint8_t, attrtype, value) + +#define NLA_PUT_S8(msg, attrtype, value) NLA_PUT_TYPE(msg, int8_t, attrtype, value) + +#define NLA_PUT_U16(msg, attrtype, value) NLA_PUT_TYPE(msg, uint16_t, attrtype, value) + +#define NLA_PUT_U32(msg, attrtype, value) NLA_PUT_TYPE(msg, uint32_t, attrtype, value) + +#define NLA_PUT_S32(msg, attrtype, value) NLA_PUT_TYPE(msg, int32_t, attrtype, value) + +#define NLA_PUT_U64(msg, attrtype, value) NLA_PUT_TYPE(msg, uint64_t, attrtype, value) + +#define NLA_PUT_STRING(msg, attrtype, value) NLA_PUT(msg, attrtype, (int) strlen(value) + 1, value) + +#define NLA_PUT_FLAG(msg, attrtype) NLA_PUT(msg, attrtype, 0, NULL) + +struct nlattr *nla_find(const struct nlattr *head, int len, int attrtype); + +static inline int +nla_ok(const struct nlattr *nla, int remaining) +{ + return remaining >= (int) sizeof(*nla) && nla->nla_len >= sizeof(*nla) + && nla->nla_len <= remaining; +} + +static inline struct nlattr * +nla_next(const struct nlattr *nla, int *remaining) +{ + int totlen = NLA_ALIGN(nla->nla_len); + + *remaining -= totlen; + return (struct nlattr *) ((char *) nla + totlen); +} + +#define nla_for_each_attr(pos, head, len, rem) \ + for (pos = head, rem = len; nla_ok(pos, rem); pos = nla_next(pos, &(rem))) + +#define nla_for_each_nested(pos, nla, rem) \ + for (pos = (struct nlattr *) nla_data(nla), rem = nla_len(nla); nla_ok(pos, rem); \ + pos = nla_next(pos, &(rem))) + +void nla_nest_cancel(struct nl_msg *msg, const struct nlattr *attr); +struct nlattr *nla_nest_start(struct nl_msg *msg, int attrtype); +int nla_nest_end(struct nl_msg *msg, struct nlattr *start); + +int nla_parse(struct nlattr * tb[], + int maxtype, + struct nlattr * head, + int len, + const struct nla_policy *policy); + +#define nla_parse_arr(tb, head, len, policy) \ + ({ \ + _nl_static_assert_tb((tb), (policy)); \ + \ + nla_parse((tb), G_N_ELEMENTS(tb) - 1, (head), (len), (policy)); \ + }) + +static inline int +nla_parse_nested(struct nlattr * tb[], + int maxtype, + struct nlattr * nla, + const struct nla_policy *policy) +{ + return nla_parse(tb, maxtype, nla_data(nla), nla_len(nla), policy); +} + +#define nla_parse_nested_arr(tb, nla, policy) \ + ({ \ + _nl_static_assert_tb((tb), (policy)); \ + \ + nla_parse_nested((tb), G_N_ELEMENTS(tb) - 1, (nla), (policy)); \ + }) + +/*****************************************************************************/ + +struct nl_msg *nlmsg_alloc(void); + +struct nl_msg *nlmsg_alloc_size(size_t max); + +struct nl_msg *nlmsg_alloc_convert(struct nlmsghdr *hdr); + +struct nl_msg *nlmsg_alloc_simple(int nlmsgtype, int flags); + +void *nlmsg_reserve(struct nl_msg *n, size_t len, int pad); + +int nlmsg_append(struct nl_msg *n, const void *data, size_t len, int pad); + +#define nlmsg_append_struct(n, data) nlmsg_append(n, (data), sizeof(*(data)), NLMSG_ALIGNTO) + +void nlmsg_free(struct nl_msg *msg); + +static inline int +nlmsg_size(int payload) +{ + nm_assert(payload >= 0 && payload < G_MAXINT - NLMSG_HDRLEN - 4); + return NLMSG_HDRLEN + payload; +} + +static inline int +nlmsg_total_size(int payload) +{ + return NLMSG_ALIGN(nlmsg_size(payload)); +} + +static inline int +nlmsg_ok(const struct nlmsghdr *nlh, int remaining) +{ + return (remaining >= (int) sizeof(struct nlmsghdr) && nlh->nlmsg_len >= sizeof(struct nlmsghdr) + && nlh->nlmsg_len <= remaining); +} + +static inline struct nlmsghdr * +nlmsg_next(struct nlmsghdr *nlh, int *remaining) +{ + int totlen = NLMSG_ALIGN(nlh->nlmsg_len); + + *remaining -= totlen; + + return (struct nlmsghdr *) ((unsigned char *) nlh + totlen); +} + +int nlmsg_get_proto(struct nl_msg *msg); +void nlmsg_set_proto(struct nl_msg *msg, int protocol); + +void nlmsg_set_src(struct nl_msg *msg, struct sockaddr_nl *addr); + +struct ucred *nlmsg_get_creds(struct nl_msg *msg); +void nlmsg_set_creds(struct nl_msg *msg, struct ucred *creds); + +static inline void +_nm_auto_nl_msg_cleanup(struct nl_msg **ptr) +{ + nlmsg_free(*ptr); +} +#define nm_auto_nlmsg nm_auto(_nm_auto_nl_msg_cleanup) + +static inline void * +nlmsg_data(const struct nlmsghdr *nlh) +{ + return (unsigned char *) nlh + NLMSG_HDRLEN; +} + +static inline void * +nlmsg_tail(const struct nlmsghdr *nlh) +{ + return (unsigned char *) nlh + NLMSG_ALIGN(nlh->nlmsg_len); +} + +struct nlmsghdr *nlmsg_hdr(struct nl_msg *n); + +static inline int +nlmsg_valid_hdr(const struct nlmsghdr *nlh, int hdrlen) +{ + if (nlh->nlmsg_len < nlmsg_size(hdrlen)) + return 0; + + return 1; +} + +static inline int +nlmsg_datalen(const struct nlmsghdr *nlh) +{ + return nlh->nlmsg_len - NLMSG_HDRLEN; +} + +static inline int +nlmsg_attrlen(const struct nlmsghdr *nlh, int hdrlen) +{ + return NM_MAX((int) (nlmsg_datalen(nlh) - NLMSG_ALIGN(hdrlen)), 0); +} + +static inline struct nlattr * +nlmsg_attrdata(const struct nlmsghdr *nlh, int hdrlen) +{ + unsigned char *data = nlmsg_data(nlh); + return (struct nlattr *) (data + NLMSG_ALIGN(hdrlen)); +} + +static inline struct nlattr * +nlmsg_find_attr(struct nlmsghdr *nlh, int hdrlen, int attrtype) +{ + return nla_find(nlmsg_attrdata(nlh, hdrlen), nlmsg_attrlen(nlh, hdrlen), attrtype); +} + +int nlmsg_parse(struct nlmsghdr * nlh, + int hdrlen, + struct nlattr * tb[], + int maxtype, + const struct nla_policy *policy); + +#define nlmsg_parse_arr(nlh, hdrlen, tb, policy) \ + ({ \ + _nl_static_assert_tb((tb), (policy)); \ + G_STATIC_ASSERT_EXPR((hdrlen) >= 0); \ + \ + nlmsg_parse((nlh), (hdrlen), (tb), G_N_ELEMENTS(tb) - 1, (policy)); \ + }) + +struct nlmsghdr * +nlmsg_put(struct nl_msg *n, uint32_t pid, uint32_t seq, int type, int payload, int flags); + +/*****************************************************************************/ + +#define NL_AUTO_PORT 0 +#define NL_AUTO_SEQ 0 + +struct nl_sock; + +struct nl_sock *nl_socket_alloc(void); + +void nl_socket_free(struct nl_sock *sk); + +int nl_socket_get_fd(const struct nl_sock *sk); + +struct sockaddr_nl *nlmsg_get_dst(struct nl_msg *msg); + +size_t nl_socket_get_msg_buf_size(struct nl_sock *sk); +int nl_socket_set_msg_buf_size(struct nl_sock *sk, size_t bufsize); + +int nl_socket_set_buffer_size(struct nl_sock *sk, int rxbuf, int txbuf); + +int nl_socket_set_passcred(struct nl_sock *sk, int state); + +int nl_socket_set_nonblocking(const struct nl_sock *sk); + +void nl_socket_disable_msg_peek(struct nl_sock *sk); + +uint32_t nl_socket_get_local_port(const struct nl_sock *sk); + +int nl_socket_add_memberships(struct nl_sock *sk, int group, ...); + +int nl_connect(struct nl_sock *sk, int protocol); + +int nl_recv(struct nl_sock * sk, + struct sockaddr_nl *nla, + unsigned char ** buf, + struct ucred * out_creds, + gboolean * out_creds_has); + +int nl_send(struct nl_sock *sk, struct nl_msg *msg); + +int nl_send_auto(struct nl_sock *sk, struct nl_msg *msg); + +/*****************************************************************************/ + +enum nl_cb_action { + /* Proceed with wathever would come next */ + NL_OK, + /* Skip this message */ + NL_SKIP, + /* Stop parsing altogether and discard remaining messages */ + NL_STOP, +}; + +typedef int (*nl_recvmsg_msg_cb_t)(struct nl_msg *msg, void *arg); + +typedef int (*nl_recvmsg_err_cb_t)(struct sockaddr_nl *nla, struct nlmsgerr *nlerr, void *arg); + +struct nl_cb { + nl_recvmsg_msg_cb_t valid_cb; + void * valid_arg; + + nl_recvmsg_msg_cb_t finish_cb; + void * finish_arg; + + nl_recvmsg_msg_cb_t ack_cb; + void * ack_arg; + + nl_recvmsg_err_cb_t err_cb; + void * err_arg; +}; + +int nl_sendmsg(struct nl_sock *sk, struct nl_msg *msg, struct msghdr *hdr); + +int nl_send_iovec(struct nl_sock *sk, struct nl_msg *msg, struct iovec *iov, unsigned iovlen); + +void nl_complete_msg(struct nl_sock *sk, struct nl_msg *msg); + +int nl_recvmsgs(struct nl_sock *sk, const struct nl_cb *cb); + +int nl_wait_for_ack(struct nl_sock *sk, const struct nl_cb *cb); + +int nl_socket_set_ext_ack(struct nl_sock *sk, gboolean enable); + +/*****************************************************************************/ + +void * genlmsg_put(struct nl_msg *msg, + uint32_t port, + uint32_t seq, + int family, + int hdrlen, + int flags, + uint8_t cmd, + uint8_t version); +void * genlmsg_data(const struct genlmsghdr *gnlh); +void * genlmsg_user_hdr(const struct genlmsghdr *gnlh); +struct genlmsghdr *genlmsg_hdr(struct nlmsghdr *nlh); +void * genlmsg_user_data(const struct genlmsghdr *gnlh, const int hdrlen); +struct nlattr * genlmsg_attrdata(const struct genlmsghdr *gnlh, int hdrlen); +int genlmsg_len(const struct genlmsghdr *gnlh); +int genlmsg_attrlen(const struct genlmsghdr *gnlh, int hdrlen); +int genlmsg_valid_hdr(struct nlmsghdr *nlh, int hdrlen); + +int genlmsg_parse(struct nlmsghdr * nlh, + int hdrlen, + struct nlattr * tb[], + int maxtype, + const struct nla_policy *policy); + +#define genlmsg_parse_arr(nlh, hdrlen, tb, policy) \ + ({ \ + _nl_static_assert_tb((tb), (policy)); \ + G_STATIC_ASSERT_EXPR((hdrlen) >= 0); \ + \ + genlmsg_parse((nlh), (hdrlen), (tb), G_N_ELEMENTS(tb) - 1, (policy)); \ + }) + +int genl_ctrl_resolve(struct nl_sock *sk, const char *name); + +/*****************************************************************************/ + +#endif /* __NM_NETLINK_H__ */ diff --git a/shared/nm-platform/nm-platform-utils.c b/shared/nm-platform/nm-platform-utils.c new file mode 100644 index 00000000..c1c5b1a3 --- /dev/null +++ b/shared/nm-platform/nm-platform-utils.c @@ -0,0 +1,1807 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright (C) 2015 Red Hat, Inc. + */ + +#include "nm-glib-aux/nm-default-glib-i18n-lib.h" + +#include "nm-platform-utils.h" + +#include <unistd.h> +#include <sys/ioctl.h> +#include <linux/ethtool.h> +#include <linux/sockios.h> +#include <linux/mii.h> +#include <linux/if.h> +#include <linux/version.h> +#include <linux/rtnetlink.h> +#include <fcntl.h> +#include <libudev.h> + +#include "nm-base/nm-ethtool-base.h" +#include "nm-log-core/nm-logging.h" + +/*****************************************************************************/ + +#define ONOFF(bool_val) ((bool_val) ? "on" : "off") + +/****************************************************************************** + * utils + *****************************************************************************/ + +extern char *if_indextoname(unsigned __ifindex, char *__ifname); +unsigned if_nametoindex(const char *__ifname); + +const char * +nmp_utils_if_indextoname(int ifindex, char *out_ifname /*IFNAMSIZ*/) +{ + g_return_val_if_fail(ifindex > 0, NULL); + g_return_val_if_fail(out_ifname, NULL); + + return if_indextoname(ifindex, out_ifname); +} + +int +nmp_utils_if_nametoindex(const char *ifname) +{ + g_return_val_if_fail(ifname, 0); + + return if_nametoindex(ifname); +} + +/*****************************************************************************/ + +NM_UTILS_LOOKUP_STR_DEFINE(nm_platform_link_duplex_type_to_string, + NMPlatformLinkDuplexType, + NM_UTILS_LOOKUP_DEFAULT_WARN(NULL), + NM_UTILS_LOOKUP_STR_ITEM(NM_PLATFORM_LINK_DUPLEX_UNKNOWN, "unknown"), + NM_UTILS_LOOKUP_STR_ITEM(NM_PLATFORM_LINK_DUPLEX_FULL, "full"), + NM_UTILS_LOOKUP_STR_ITEM(NM_PLATFORM_LINK_DUPLEX_HALF, "half"), ); + +/*****************************************************************************/ + +typedef struct { + int fd; + const int ifindex; + char ifname[IFNAMSIZ]; +} SocketHandle; + +#define SOCKET_HANDLE_INIT(_ifindex) \ + { \ + .fd = -1, .ifindex = (_ifindex), \ + } + +static void +_nm_auto_socket_handle(SocketHandle *shandle) +{ + if (shandle->fd >= 0) + nm_close(shandle->fd); +} + +#define nm_auto_socket_handle nm_auto(_nm_auto_socket_handle) + +/*****************************************************************************/ + +typedef enum { + IOCTL_CALL_DATA_TYPE_NONE, + IOCTL_CALL_DATA_TYPE_IFRDATA, + IOCTL_CALL_DATA_TYPE_IFRU, +} IoctlCallDataType; + +static int +_ioctl_call(const char * log_ioctl_type, + const char * log_subtype, + unsigned long int ioctl_request, + int ifindex, + int * inout_fd, + char * inout_ifname, + IoctlCallDataType edata_type, + gpointer edata, + gsize edata_size, + struct ifreq * out_ifreq) +{ + nm_auto_close int fd_close = -1; + int fd; + int r; + gpointer edata_backup = NULL; + gs_free gpointer edata_backup_free = NULL; + guint try_count; + char known_ifnames[2][IFNAMSIZ]; + const char * failure_reason = NULL; + struct ifreq ifr; + + nm_assert(ifindex > 0); + nm_assert(NM_IN_SET(edata_type, + IOCTL_CALL_DATA_TYPE_NONE, + IOCTL_CALL_DATA_TYPE_IFRDATA, + IOCTL_CALL_DATA_TYPE_IFRU)); + nm_assert(edata_type != IOCTL_CALL_DATA_TYPE_NONE || edata_size == 0); + nm_assert(edata_type != IOCTL_CALL_DATA_TYPE_IFRDATA || edata_size > 0); + nm_assert(edata_type != IOCTL_CALL_DATA_TYPE_IFRU + || (edata_size > 0 && edata_size <= sizeof(ifr.ifr_ifru))); + nm_assert(edata_size == 0 || edata); + + /* open a file descriptor (or use the one provided). */ + if (inout_fd && *inout_fd >= 0) + fd = *inout_fd; + else { + fd = socket(PF_INET, SOCK_DGRAM | SOCK_CLOEXEC, 0); + if (fd < 0) { + r = -NM_ERRNO_NATIVE(errno); + failure_reason = "failed creating socket or ioctl"; + goto out; + } + if (inout_fd) + *inout_fd = fd; + else + fd_close = fd; + } + + /* resolve the ifindex to name (or use the one provided). */ + if (inout_ifname && inout_ifname[0]) + nm_utils_ifname_cpy(known_ifnames[0], inout_ifname); + else { + if (!nmp_utils_if_indextoname(ifindex, known_ifnames[0])) { + failure_reason = "cannot resolve ifindex"; + r = -ENODEV; + goto out; + } + if (inout_ifname) + nm_utils_ifname_cpy(inout_ifname, known_ifnames[0]); + } + + /* we might need to retry the request. Backup edata so that we can + * restore it on retry. */ + if (edata_size > 0) + edata_backup = nm_memdup_maybe_a(500, edata, edata_size, &edata_backup_free); + + try_count = 0; + +again: +{ + const char *ifname = known_ifnames[try_count % 2]; + + nm_assert(ifindex > 0); + nm_assert(ifname && nm_utils_ifname_valid_kernel(ifname, NULL)); + nm_assert(fd >= 0); + + memset(&ifr, 0, sizeof(ifr)); + nm_utils_ifname_cpy(ifr.ifr_name, ifname); + if (edata_type == IOCTL_CALL_DATA_TYPE_IFRDATA) + ifr.ifr_data = edata; + else if (edata_type == IOCTL_CALL_DATA_TYPE_IFRU) + memcpy(&ifr.ifr_ifru, edata, NM_MIN(edata_size, sizeof(ifr.ifr_ifru))); + + if (ioctl(fd, ioctl_request, &ifr) < 0) { + r = -NM_ERRNO_NATIVE(errno); + nm_log_trace(LOGD_PLATFORM, + "%s[%d]: %s, %s: failed: %s", + log_ioctl_type, + ifindex, + log_subtype, + ifname, + nm_strerror_native(-r)); + } else { + r = 0; + nm_log_trace(LOGD_PLATFORM, + "%s[%d]: %s, %s: success", + log_ioctl_type, + ifindex, + log_subtype, + ifname); + } +} + + try_count++; + + /* resolve the name again to see whether the ifindex still has the same name. */ + if (!nmp_utils_if_indextoname(ifindex, known_ifnames[try_count % 2])) { + /* we could not find the ifindex again. Probably the device just got + * removed. + * + * In both cases we return the error code we got from ioctl above. + * Either it failed because the device was gone already or it still + * managed to complete the call. In both cases, the error code is good. */ + failure_reason = + "cannot resolve ifindex after ioctl call. Probably the device was just removed"; + goto out; + } + + /* check whether the ifname changed in the meantime. If yes, would render the result + * invalid. Note that this cannot detect every race regarding renames, for example: + * + * - if_indextoname(#10) gives eth0 + * - rename(#10) => eth0_tmp + * - rename(#11) => eth0 + * - ioctl(eth0) (wrongly fetching #11, formerly eth1) + * - rename(#11) => eth_something + * - rename(#10) => eth0 + * - if_indextoname(#10) gives eth0 + */ + if (!nm_streq(known_ifnames[0], known_ifnames[1])) { + gboolean retry; + + /* we detected a possible(!) rename. + * + * For getters it's straight forward to just retry the call. + * + * For setters we also always retry. If our previous call operated on the right device, + * calling it again should have no bad effect (just setting the same thing more than once). + * + * The only potential bad thing is if there was a race involving swapping names, and we just + * set the ioctl option on the wrong device. But then the bad thing already happenned and + * we cannot detect it (nor do anything about it). At least, we can retry and set the + * option on the right interface. */ + retry = (try_count < 5); + + nm_log_trace(LOGD_PLATFORM, + "%s[%d]: %s: rename detected from \"%s\" to \"%s\". %s", + log_ioctl_type, + ifindex, + log_subtype, + known_ifnames[(try_count - 1) % 2], + known_ifnames[try_count % 2], + retry ? "Retry" : "No retry"); + if (inout_ifname) + nm_utils_ifname_cpy(inout_ifname, known_ifnames[try_count % 2]); + if (retry) { + if (edata_size > 0) + memcpy(edata, edata_backup, edata_size); + goto again; + } + } + +out: + if (failure_reason) { + nm_log_trace(LOGD_PLATFORM, + "%s[%d]: %s: %s: %s", + log_ioctl_type, + ifindex, + log_subtype, + failure_reason, + r < 0 ? nm_strerror_native(-r) : "assume success"); + } + if (r >= 0) + NM_SET_OUT(out_ifreq, ifr); + return r; +} + +/****************************************************************************** + * ethtool + *****************************************************************************/ + +static NM_UTILS_ENUM2STR_DEFINE(_ethtool_cmd_to_string, + guint32, + NM_UTILS_ENUM2STR(ETHTOOL_GCOALESCE, "ETHTOOL_GCOALESCE"), + NM_UTILS_ENUM2STR(ETHTOOL_GDRVINFO, "ETHTOOL_GDRVINFO"), + NM_UTILS_ENUM2STR(ETHTOOL_GFEATURES, "ETHTOOL_GFEATURES"), + NM_UTILS_ENUM2STR(ETHTOOL_GLINK, "ETHTOOL_GLINK"), + NM_UTILS_ENUM2STR(ETHTOOL_GPERMADDR, "ETHTOOL_GPERMADDR"), + NM_UTILS_ENUM2STR(ETHTOOL_GRINGPARAM, "ETHTOOL_GRINGPARAM"), + NM_UTILS_ENUM2STR(ETHTOOL_GSET, "ETHTOOL_GSET"), + NM_UTILS_ENUM2STR(ETHTOOL_GSSET_INFO, "ETHTOOL_GSSET_INFO"), + NM_UTILS_ENUM2STR(ETHTOOL_GSTATS, "ETHTOOL_GSTATS"), + NM_UTILS_ENUM2STR(ETHTOOL_GSTRINGS, "ETHTOOL_GSTRINGS"), + NM_UTILS_ENUM2STR(ETHTOOL_GWOL, "ETHTOOL_GWOL"), + NM_UTILS_ENUM2STR(ETHTOOL_SCOALESCE, "ETHTOOL_SCOALESCE"), + NM_UTILS_ENUM2STR(ETHTOOL_SFEATURES, "ETHTOOL_SFEATURES"), + NM_UTILS_ENUM2STR(ETHTOOL_SRINGPARAM, "ETHTOOL_SRINGPARAM"), + NM_UTILS_ENUM2STR(ETHTOOL_SSET, "ETHTOOL_SSET"), + NM_UTILS_ENUM2STR(ETHTOOL_SWOL, "ETHTOOL_SWOL"), ); + +static const char * +_ethtool_edata_to_string(gpointer edata, gsize edata_size, char *sbuf, gsize sbuf_len) +{ + nm_assert(edata); + nm_assert(edata_size >= sizeof(guint32)); + nm_assert((((intptr_t) edata) % _nm_alignof(guint32)) == 0); + + return _ethtool_cmd_to_string(*((guint32 *) edata), sbuf, sbuf_len); +} + +/*****************************************************************************/ + +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 27) + #define ethtool_cmd_speed(pedata) ((pedata)->speed) + + #define ethtool_cmd_speed_set(pedata, speed) \ + G_STMT_START \ + { \ + (pedata)->speed = (guint16)(speed); \ + } \ + G_STMT_END +#endif + +static int +_ethtool_call_handle(SocketHandle *shandle, gpointer edata, gsize edata_size) +{ + char sbuf[50]; + + return _ioctl_call("ethtool", + _ethtool_edata_to_string(edata, edata_size, sbuf, sizeof(sbuf)), + SIOCETHTOOL, + shandle->ifindex, + &shandle->fd, + shandle->ifname, + IOCTL_CALL_DATA_TYPE_IFRDATA, + edata, + edata_size, + NULL); +} + +static int +_ethtool_call_once(int ifindex, gpointer edata, gsize edata_size) +{ + char sbuf[50]; + + return _ioctl_call("ethtool", + _ethtool_edata_to_string(edata, edata_size, sbuf, sizeof(sbuf)), + SIOCETHTOOL, + ifindex, + NULL, + NULL, + IOCTL_CALL_DATA_TYPE_IFRDATA, + edata, + edata_size, + NULL); +} + +/*****************************************************************************/ + +static struct ethtool_gstrings * +ethtool_get_stringset(SocketHandle *shandle, int stringset_id) +{ + struct { + struct ethtool_sset_info info; + guint32 sentinel; + } sset_info = { + .info.cmd = ETHTOOL_GSSET_INFO, + .info.reserved = 0, + .info.sset_mask = (1ULL << stringset_id), + }; + const guint32 * pdata; + gs_free struct ethtool_gstrings *gstrings = NULL; + gsize gstrings_len; + guint32 i, len; + + if (_ethtool_call_handle(shandle, &sset_info, sizeof(sset_info)) < 0) + return NULL; + if (!sset_info.info.sset_mask) + return NULL; + + pdata = (guint32 *) sset_info.info.data; + + len = *pdata; + + gstrings_len = sizeof(*gstrings) + (len * ETH_GSTRING_LEN); + gstrings = g_malloc0(gstrings_len); + gstrings->cmd = ETHTOOL_GSTRINGS; + gstrings->string_set = stringset_id; + gstrings->len = len; + if (gstrings->len > 0) { + if (_ethtool_call_handle(shandle, gstrings, gstrings_len) < 0) + return NULL; + for (i = 0; i < gstrings->len; i++) { + /* ensure NUL terminated */ + gstrings->data[i * ETH_GSTRING_LEN + (ETH_GSTRING_LEN - 1)] = '\0'; + } + } + + return g_steal_pointer(&gstrings); +} + +static int +ethtool_gstrings_find(const struct ethtool_gstrings *gstrings, const char *needle) +{ + guint32 i; + + /* ethtool_get_stringset() always ensures NUL terminated strings at ETH_GSTRING_LEN. + * that means, we cannot possibly request longer names. */ + nm_assert(needle && strlen(needle) < ETH_GSTRING_LEN); + + for (i = 0; i < gstrings->len; i++) { + if (nm_streq((char *) &gstrings->data[i * ETH_GSTRING_LEN], needle)) + return i; + } + return -1; +} + +static int +ethtool_get_stringset_index(SocketHandle *shandle, int stringset_id, const char *needle) +{ + gs_free struct ethtool_gstrings *gstrings = NULL; + + /* ethtool_get_stringset() always ensures NUL terminated strings at ETH_GSTRING_LEN. + * that means, we cannot possibly request longer names. */ + nm_assert(needle && strlen(needle) < ETH_GSTRING_LEN); + + gstrings = ethtool_get_stringset(shandle, stringset_id); + if (gstrings) + return ethtool_gstrings_find(gstrings, needle); + return -1; +} + +/*****************************************************************************/ + +static const NMEthtoolFeatureInfo _ethtool_feature_infos[_NM_ETHTOOL_ID_FEATURE_NUM] = { +#define ETHT_FEAT(eid, ...) \ + { \ + .ethtool_id = eid, .n_kernel_names = NM_NARG(__VA_ARGS__), \ + .kernel_names = ((const char *const[]){__VA_ARGS__}), \ + } + + /* the order does only matter for one thing: if it happens that more than one NMEthtoolID + * reference the same kernel-name, then the one that is mentioned *later* will win in + * case these NMEthtoolIDs are set. That mostly only makes sense for ethtool-ids which + * refer to multiple features ("feature-tso"), while also having more specific ids + * ("feature-tx-tcp-segmentation"). */ + + /* names from ethtool utility, which are aliases for multiple features. */ + ETHT_FEAT(NM_ETHTOOL_ID_FEATURE_SG, "tx-scatter-gather", "tx-scatter-gather-fraglist"), + ETHT_FEAT(NM_ETHTOOL_ID_FEATURE_TSO, + "tx-tcp-segmentation", + "tx-tcp-ecn-segmentation", + "tx-tcp-mangleid-segmentation", + "tx-tcp6-segmentation"), + ETHT_FEAT(NM_ETHTOOL_ID_FEATURE_TX, + "tx-checksum-ipv4", + "tx-checksum-ip-generic", + "tx-checksum-ipv6", + "tx-checksum-fcoe-crc", + "tx-checksum-sctp"), + + /* names from ethtool utility, which are aliases for one feature. */ + ETHT_FEAT(NM_ETHTOOL_ID_FEATURE_GRO, "rx-gro"), + ETHT_FEAT(NM_ETHTOOL_ID_FEATURE_GSO, "tx-generic-segmentation"), + ETHT_FEAT(NM_ETHTOOL_ID_FEATURE_LRO, "rx-lro"), + ETHT_FEAT(NM_ETHTOOL_ID_FEATURE_NTUPLE, "rx-ntuple-filter"), + ETHT_FEAT(NM_ETHTOOL_ID_FEATURE_RX, "rx-checksum"), + ETHT_FEAT(NM_ETHTOOL_ID_FEATURE_RXHASH, "rx-hashing"), + ETHT_FEAT(NM_ETHTOOL_ID_FEATURE_RXVLAN, "rx-vlan-hw-parse"), + ETHT_FEAT(NM_ETHTOOL_ID_FEATURE_TXVLAN, "tx-vlan-hw-insert"), + + /* names of features, as known by kernel. */ + ETHT_FEAT(NM_ETHTOOL_ID_FEATURE_ESP_HW_OFFLOAD, "esp-hw-offload"), + ETHT_FEAT(NM_ETHTOOL_ID_FEATURE_ESP_TX_CSUM_HW_OFFLOAD, "esp-tx-csum-hw-offload"), + ETHT_FEAT(NM_ETHTOOL_ID_FEATURE_FCOE_MTU, "fcoe-mtu"), + ETHT_FEAT(NM_ETHTOOL_ID_FEATURE_HIGHDMA, "highdma"), + ETHT_FEAT(NM_ETHTOOL_ID_FEATURE_HW_TC_OFFLOAD, "hw-tc-offload"), + ETHT_FEAT(NM_ETHTOOL_ID_FEATURE_L2_FWD_OFFLOAD, "l2-fwd-offload"), + ETHT_FEAT(NM_ETHTOOL_ID_FEATURE_LOOPBACK, "loopback"), + ETHT_FEAT(NM_ETHTOOL_ID_FEATURE_MACSEC_HW_OFFLOAD, "macsec-hw-offload"), + ETHT_FEAT(NM_ETHTOOL_ID_FEATURE_RX_ALL, "rx-all"), + ETHT_FEAT(NM_ETHTOOL_ID_FEATURE_RX_FCS, "rx-fcs"), + ETHT_FEAT(NM_ETHTOOL_ID_FEATURE_RX_GRO_HW, "rx-gro-hw"), + ETHT_FEAT(NM_ETHTOOL_ID_FEATURE_RX_GRO_LIST, "rx-gro-list"), + ETHT_FEAT(NM_ETHTOOL_ID_FEATURE_RX_UDP_GRO_FORWARDING, "rx-udp-gro-forwarding"), + ETHT_FEAT(NM_ETHTOOL_ID_FEATURE_RX_UDP_TUNNEL_PORT_OFFLOAD, "rx-udp_tunnel-port-offload"), + ETHT_FEAT(NM_ETHTOOL_ID_FEATURE_RX_VLAN_FILTER, "rx-vlan-filter"), + ETHT_FEAT(NM_ETHTOOL_ID_FEATURE_RX_VLAN_STAG_FILTER, "rx-vlan-stag-filter"), + ETHT_FEAT(NM_ETHTOOL_ID_FEATURE_RX_VLAN_STAG_HW_PARSE, "rx-vlan-stag-hw-parse"), + ETHT_FEAT(NM_ETHTOOL_ID_FEATURE_TLS_HW_RECORD, "tls-hw-record"), + ETHT_FEAT(NM_ETHTOOL_ID_FEATURE_TLS_HW_RX_OFFLOAD, "tls-hw-rx-offload"), + ETHT_FEAT(NM_ETHTOOL_ID_FEATURE_TLS_HW_TX_OFFLOAD, "tls-hw-tx-offload"), + ETHT_FEAT(NM_ETHTOOL_ID_FEATURE_TX_CHECKSUM_FCOE_CRC, "tx-checksum-fcoe-crc"), + ETHT_FEAT(NM_ETHTOOL_ID_FEATURE_TX_CHECKSUM_IPV4, "tx-checksum-ipv4"), + ETHT_FEAT(NM_ETHTOOL_ID_FEATURE_TX_CHECKSUM_IPV6, "tx-checksum-ipv6"), + ETHT_FEAT(NM_ETHTOOL_ID_FEATURE_TX_CHECKSUM_IP_GENERIC, "tx-checksum-ip-generic"), + ETHT_FEAT(NM_ETHTOOL_ID_FEATURE_TX_CHECKSUM_SCTP, "tx-checksum-sctp"), + ETHT_FEAT(NM_ETHTOOL_ID_FEATURE_TX_ESP_SEGMENTATION, "tx-esp-segmentation"), + ETHT_FEAT(NM_ETHTOOL_ID_FEATURE_TX_FCOE_SEGMENTATION, "tx-fcoe-segmentation"), + ETHT_FEAT(NM_ETHTOOL_ID_FEATURE_TX_GRE_CSUM_SEGMENTATION, "tx-gre-csum-segmentation"), + ETHT_FEAT(NM_ETHTOOL_ID_FEATURE_TX_GRE_SEGMENTATION, "tx-gre-segmentation"), + ETHT_FEAT(NM_ETHTOOL_ID_FEATURE_TX_GSO_LIST, "tx-gso-list"), + ETHT_FEAT(NM_ETHTOOL_ID_FEATURE_TX_GSO_PARTIAL, "tx-gso-partial"), + ETHT_FEAT(NM_ETHTOOL_ID_FEATURE_TX_GSO_ROBUST, "tx-gso-robust"), + ETHT_FEAT(NM_ETHTOOL_ID_FEATURE_TX_IPXIP4_SEGMENTATION, "tx-ipxip4-segmentation"), + ETHT_FEAT(NM_ETHTOOL_ID_FEATURE_TX_IPXIP6_SEGMENTATION, "tx-ipxip6-segmentation"), + ETHT_FEAT(NM_ETHTOOL_ID_FEATURE_TX_NOCACHE_COPY, "tx-nocache-copy"), + ETHT_FEAT(NM_ETHTOOL_ID_FEATURE_TX_SCATTER_GATHER, "tx-scatter-gather"), + ETHT_FEAT(NM_ETHTOOL_ID_FEATURE_TX_SCATTER_GATHER_FRAGLIST, "tx-scatter-gather-fraglist"), + ETHT_FEAT(NM_ETHTOOL_ID_FEATURE_TX_SCTP_SEGMENTATION, "tx-sctp-segmentation"), + ETHT_FEAT(NM_ETHTOOL_ID_FEATURE_TX_TCP6_SEGMENTATION, "tx-tcp6-segmentation"), + ETHT_FEAT(NM_ETHTOOL_ID_FEATURE_TX_TCP_ECN_SEGMENTATION, "tx-tcp-ecn-segmentation"), + ETHT_FEAT(NM_ETHTOOL_ID_FEATURE_TX_TCP_MANGLEID_SEGMENTATION, "tx-tcp-mangleid-segmentation"), + ETHT_FEAT(NM_ETHTOOL_ID_FEATURE_TX_TCP_SEGMENTATION, "tx-tcp-segmentation"), + ETHT_FEAT(NM_ETHTOOL_ID_FEATURE_TX_TUNNEL_REMCSUM_SEGMENTATION, + "tx-tunnel-remcsum-segmentation"), + ETHT_FEAT(NM_ETHTOOL_ID_FEATURE_TX_UDP_SEGMENTATION, "tx-udp-segmentation"), + ETHT_FEAT(NM_ETHTOOL_ID_FEATURE_TX_UDP_TNL_CSUM_SEGMENTATION, "tx-udp_tnl-csum-segmentation"), + ETHT_FEAT(NM_ETHTOOL_ID_FEATURE_TX_UDP_TNL_SEGMENTATION, "tx-udp_tnl-segmentation"), + ETHT_FEAT(NM_ETHTOOL_ID_FEATURE_TX_VLAN_STAG_HW_INSERT, "tx-vlan-stag-hw-insert"), +}; + +/* the number of kernel features that we handle. It essentially is the sum of all + * kernel_names. So, all ethtool-ids that reference exactly one kernel-name + * (_NM_ETHTOOL_ID_FEATURE_NUM) + some extra, for ethtool-ids that are aliases + * for multiple kernel-names. */ +#define N_ETHTOOL_KERNEL_FEATURES (((guint) _NM_ETHTOOL_ID_FEATURE_NUM) + 8u) + +static void +_ASSERT_ethtool_feature_infos(void) +{ +#if NM_MORE_ASSERTS > 10 + guint i, k, n; + bool found[_NM_ETHTOOL_ID_FEATURE_NUM] = {}; + + G_STATIC_ASSERT_EXPR(G_N_ELEMENTS(_ethtool_feature_infos) == _NM_ETHTOOL_ID_FEATURE_NUM); + + n = 0; + for (i = 0; i < G_N_ELEMENTS(_ethtool_feature_infos); i++) { + NMEthtoolFeatureState kstate; + const NMEthtoolFeatureInfo *inf = &_ethtool_feature_infos[i]; + + g_assert(inf->ethtool_id >= _NM_ETHTOOL_ID_FEATURE_FIRST); + g_assert(inf->ethtool_id <= _NM_ETHTOOL_ID_FEATURE_LAST); + g_assert(inf->n_kernel_names > 0); + + for (k = 0; k < i; k++) + g_assert(inf->ethtool_id != _ethtool_feature_infos[k].ethtool_id); + + g_assert(!found[_NM_ETHTOOL_ID_FEATURE_AS_IDX(inf->ethtool_id)]); + found[_NM_ETHTOOL_ID_FEATURE_AS_IDX(inf->ethtool_id)] = TRUE; + + kstate.idx_kernel_name = inf->n_kernel_names - 1; + g_assert((guint) kstate.idx_kernel_name == (guint)(inf->n_kernel_names - 1)); + + n += inf->n_kernel_names; + for (k = 0; k < inf->n_kernel_names; k++) { + const char *name = inf->kernel_names[k]; + + g_assert(nm_utils_strv_find_first((char **) inf->kernel_names, k, name) < 0); + + /* these offload features are only informational and cannot be set from user-space + * (NETIF_F_NEVER_CHANGE). We should not track them in _ethtool_feature_infos. */ + g_assert(!nm_streq(name, "netns-local")); + g_assert(!nm_streq(name, "tx-lockless")); + g_assert(!nm_streq(name, "vlan-challenged")); + } + } + + for (i = 0; i < _NM_ETHTOOL_ID_FEATURE_NUM; i++) + g_assert(found[i]); + + g_assert(n == N_ETHTOOL_KERNEL_FEATURES); +#endif +} + +static NMEthtoolFeatureStates * +ethtool_get_features(SocketHandle *shandle) +{ + gs_free NMEthtoolFeatureStates * states = NULL; + gs_free struct ethtool_gstrings *ss_features = NULL; + + _ASSERT_ethtool_feature_infos(); + + ss_features = ethtool_get_stringset(shandle, ETH_SS_FEATURES); + if (!ss_features) + return NULL; + + if (ss_features->len > 0) { + gs_free struct ethtool_gfeatures * gfeatures_free = NULL; + struct ethtool_gfeatures * gfeatures; + gsize gfeatures_len; + guint idx; + const NMEthtoolFeatureState * states_list0 = NULL; + const NMEthtoolFeatureState *const *states_plist0 = NULL; + guint states_plist_n = 0; + + gfeatures_len = sizeof(struct ethtool_gfeatures) + + (NM_DIV_ROUND_UP(ss_features->len, 32u) * sizeof(gfeatures->features[0])); + gfeatures = nm_malloc0_maybe_a(300, gfeatures_len, &gfeatures_free); + gfeatures->cmd = ETHTOOL_GFEATURES; + gfeatures->size = NM_DIV_ROUND_UP(ss_features->len, 32u); + if (_ethtool_call_handle(shandle, gfeatures, gfeatures_len) < 0) + return NULL; + + for (idx = 0; idx < G_N_ELEMENTS(_ethtool_feature_infos); idx++) { + const NMEthtoolFeatureInfo *info = &_ethtool_feature_infos[idx]; + guint idx_kernel_name; + + for (idx_kernel_name = 0; idx_kernel_name < info->n_kernel_names; idx_kernel_name++) { + NMEthtoolFeatureState *kstate; + const char * kernel_name = info->kernel_names[idx_kernel_name]; + int i_feature; + guint i_block; + guint32 i_flag; + + i_feature = ethtool_gstrings_find(ss_features, kernel_name); + if (i_feature < 0) + continue; + + i_block = ((guint) i_feature) / 32u; + i_flag = (guint32)(1u << (((guint) i_feature) % 32u)); + + if (!states) { + states = g_malloc0( + sizeof(NMEthtoolFeatureStates) + + (N_ETHTOOL_KERNEL_FEATURES * sizeof(NMEthtoolFeatureState)) + + ((N_ETHTOOL_KERNEL_FEATURES + G_N_ELEMENTS(_ethtool_feature_infos)) + * sizeof(NMEthtoolFeatureState *))); + states_list0 = &states->states_list[0]; + states_plist0 = (gpointer) &states_list0[N_ETHTOOL_KERNEL_FEATURES]; + states->n_ss_features = ss_features->len; + } + + nm_assert(states->n_states < N_ETHTOOL_KERNEL_FEATURES); + kstate = (NMEthtoolFeatureState *) &states_list0[states->n_states]; + states->n_states++; + + kstate->info = info; + kstate->idx_ss_features = i_feature; + kstate->idx_kernel_name = idx_kernel_name; + kstate->available = !!(gfeatures->features[i_block].available & i_flag); + kstate->requested = !!(gfeatures->features[i_block].requested & i_flag); + kstate->active = !!(gfeatures->features[i_block].active & i_flag); + kstate->never_changed = !!(gfeatures->features[i_block].never_changed & i_flag); + + nm_assert(states_plist_n + < N_ETHTOOL_KERNEL_FEATURES + G_N_ELEMENTS(_ethtool_feature_infos)); + + if (!states->states_indexed[_NM_ETHTOOL_ID_FEATURE_AS_IDX(info->ethtool_id)]) + states->states_indexed[_NM_ETHTOOL_ID_FEATURE_AS_IDX(info->ethtool_id)] = + &states_plist0[states_plist_n]; + ((const NMEthtoolFeatureState **) states_plist0)[states_plist_n] = kstate; + states_plist_n++; + } + + if (states && states->states_indexed[_NM_ETHTOOL_ID_FEATURE_AS_IDX(info->ethtool_id)]) { + nm_assert(states_plist_n + < N_ETHTOOL_KERNEL_FEATURES + G_N_ELEMENTS(_ethtool_feature_infos)); + nm_assert(!states_plist0[states_plist_n]); + states_plist_n++; + } + } + } + + return g_steal_pointer(&states); +} + +NMEthtoolFeatureStates * +nmp_utils_ethtool_get_features(int ifindex) +{ + nm_auto_socket_handle SocketHandle shandle = SOCKET_HANDLE_INIT(ifindex); + NMEthtoolFeatureStates * features; + + g_return_val_if_fail(ifindex > 0, 0); + + features = ethtool_get_features(&shandle); + + if (!features) { + nm_log_trace(LOGD_PLATFORM, + "ethtool[%d]: %s: failure getting features", + ifindex, + "get-features"); + return NULL; + } + + nm_log_trace(LOGD_PLATFORM, + "ethtool[%d]: %s: retrieved kernel features", + ifindex, + "get-features"); + return features; +} + +static const char * +_ethtool_feature_state_to_string(char * buf, + gsize buf_size, + const NMEthtoolFeatureState *s, + const char * prefix) +{ + int l; + + l = g_snprintf(buf, + buf_size, + "%s %s%s", + prefix ?: "", + ONOFF(s->active), + (!s->available || s->never_changed) + ? ", [fixed]" + : ((s->requested != s->active) + ? (s->requested ? ", [requested on]" : ", [requested off]") + : "")); + nm_assert(l < buf_size); + return buf; +} + +gboolean +nmp_utils_ethtool_set_features( + int ifindex, + const NMEthtoolFeatureStates *features, + const NMOptionBool *requested /* indexed by NMEthtoolID - _NM_ETHTOOL_ID_FEATURE_FIRST */, + gboolean do_set /* or reset */) +{ + nm_auto_socket_handle SocketHandle shandle = SOCKET_HANDLE_INIT(ifindex); + gs_free struct ethtool_sfeatures * sfeatures_free = NULL; + struct ethtool_sfeatures * sfeatures; + gsize sfeatures_len; + int r; + guint i, j; + struct { + const NMEthtoolFeatureState *f_state; + NMOptionBool requested; + } set_states[N_ETHTOOL_KERNEL_FEATURES]; + guint set_states_n = 0; + gboolean success = TRUE; + + g_return_val_if_fail(ifindex > 0, 0); + g_return_val_if_fail(features, 0); + g_return_val_if_fail(requested, 0); + + nm_assert(features->n_states <= N_ETHTOOL_KERNEL_FEATURES); + + for (i = 0; i < _NM_ETHTOOL_ID_FEATURE_NUM; i++) { + const NMEthtoolFeatureState *const *states_indexed; + + if (requested[i] == NM_OPTION_BOOL_DEFAULT) + continue; + + if (!(states_indexed = features->states_indexed[i])) { + if (do_set) { + nm_log_trace(LOGD_PLATFORM, + "ethtool[%d]: %s: set feature %s: skip (not found)", + ifindex, + "set-features", + nm_ethtool_data[i + _NM_ETHTOOL_ID_FEATURE_FIRST]->optname); + success = FALSE; + } + continue; + } + + for (j = 0; states_indexed[j]; j++) { + const NMEthtoolFeatureState *s = states_indexed[j]; + char sbuf[255]; + + if (set_states_n >= G_N_ELEMENTS(set_states)) + g_return_val_if_reached(FALSE); + + if (s->never_changed) { + nm_log_trace(LOGD_PLATFORM, + "ethtool[%d]: %s: %s feature %s (%s): %s, %s (skip feature marked as " + "never changed)", + ifindex, + "set-features", + do_set ? "set" : "reset", + nm_ethtool_data[i + _NM_ETHTOOL_ID_FEATURE_FIRST]->optname, + s->info->kernel_names[s->idx_kernel_name], + ONOFF(do_set ? requested[i] == NM_OPTION_BOOL_TRUE : s->active), + _ethtool_feature_state_to_string(sbuf, + sizeof(sbuf), + s, + do_set ? " currently:" : " before:")); + continue; + } + + nm_log_trace(LOGD_PLATFORM, + "ethtool[%d]: %s: %s feature %s (%s): %s, %s", + ifindex, + "set-features", + do_set ? "set" : "reset", + nm_ethtool_data[i + _NM_ETHTOOL_ID_FEATURE_FIRST]->optname, + s->info->kernel_names[s->idx_kernel_name], + ONOFF(do_set ? requested[i] == NM_OPTION_BOOL_TRUE : s->active), + _ethtool_feature_state_to_string(sbuf, + sizeof(sbuf), + s, + do_set ? " currently:" : " before:")); + + if (do_set && (!s->available || s->never_changed) + && (s->active != (requested[i] == NM_OPTION_BOOL_TRUE))) { + /* we request to change a flag which kernel reported as fixed. + * While the ethtool operation will silently succeed, mark the request + * as failure. */ + success = FALSE; + } + + set_states[set_states_n].f_state = s; + set_states[set_states_n].requested = requested[i]; + set_states_n++; + } + } + + if (set_states_n == 0) { + nm_log_trace(LOGD_PLATFORM, + "ethtool[%d]: %s: no feature requested", + ifindex, + "set-features"); + return TRUE; + } + + sfeatures_len = + sizeof(struct ethtool_sfeatures) + + (NM_DIV_ROUND_UP(features->n_ss_features, 32U) * sizeof(sfeatures->features[0])); + sfeatures = nm_malloc0_maybe_a(300, sfeatures_len, &sfeatures_free); + sfeatures->cmd = ETHTOOL_SFEATURES; + sfeatures->size = NM_DIV_ROUND_UP(features->n_ss_features, 32U); + + for (i = 0; i < set_states_n; i++) { + const NMEthtoolFeatureState *s = set_states[i].f_state; + guint i_block; + guint32 i_flag; + gboolean is_requested; + + i_block = s->idx_ss_features / 32u; + i_flag = (guint32)(1u << (s->idx_ss_features % 32u)); + + sfeatures->features[i_block].valid |= i_flag; + + if (do_set) + is_requested = (set_states[i].requested == NM_OPTION_BOOL_TRUE); + else + is_requested = s->active; + + if (is_requested) + sfeatures->features[i_block].requested |= i_flag; + else + sfeatures->features[i_block].requested &= ~i_flag; + } + + r = _ethtool_call_handle(&shandle, sfeatures, sfeatures_len); + if (r < 0) { + success = FALSE; + nm_log_trace(LOGD_PLATFORM, + "ethtool[%d]: %s: failure setting features (%s)", + ifindex, + "set-features", + nm_strerror_native(-r)); + return FALSE; + } + + nm_log_trace(LOGD_PLATFORM, + "ethtool[%d]: %s: %s", + ifindex, + "set-features", + success ? "successfully setting features" + : "at least some of the features were not successfully set"); + return success; +} + +static gboolean +ethtool_get_coalesce(SocketHandle *shandle, NMEthtoolCoalesceState *coalesce) +{ + struct ethtool_coalesce eth_data; + + eth_data.cmd = ETHTOOL_GCOALESCE; + + if (_ethtool_call_handle(shandle, ð_data, sizeof(struct ethtool_coalesce)) != 0) + return FALSE; + + *coalesce = (NMEthtoolCoalesceState){ + .s = { + [_NM_ETHTOOL_ID_COALESCE_AS_IDX(NM_ETHTOOL_ID_COALESCE_RX_USECS)] = + eth_data.rx_coalesce_usecs, + [_NM_ETHTOOL_ID_COALESCE_AS_IDX(NM_ETHTOOL_ID_COALESCE_RX_FRAMES)] = + eth_data.rx_max_coalesced_frames, + [_NM_ETHTOOL_ID_COALESCE_AS_IDX(NM_ETHTOOL_ID_COALESCE_RX_USECS_IRQ)] = + eth_data.rx_coalesce_usecs_irq, + [_NM_ETHTOOL_ID_COALESCE_AS_IDX(NM_ETHTOOL_ID_COALESCE_RX_FRAMES_IRQ)] = + eth_data.rx_max_coalesced_frames_irq, + [_NM_ETHTOOL_ID_COALESCE_AS_IDX(NM_ETHTOOL_ID_COALESCE_TX_USECS)] = + eth_data.tx_coalesce_usecs, + [_NM_ETHTOOL_ID_COALESCE_AS_IDX(NM_ETHTOOL_ID_COALESCE_TX_FRAMES)] = + eth_data.tx_max_coalesced_frames, + [_NM_ETHTOOL_ID_COALESCE_AS_IDX(NM_ETHTOOL_ID_COALESCE_TX_USECS_IRQ)] = + eth_data.tx_coalesce_usecs_irq, + [_NM_ETHTOOL_ID_COALESCE_AS_IDX(NM_ETHTOOL_ID_COALESCE_TX_FRAMES_IRQ)] = + eth_data.tx_max_coalesced_frames_irq, + [_NM_ETHTOOL_ID_COALESCE_AS_IDX(NM_ETHTOOL_ID_COALESCE_STATS_BLOCK_USECS)] = + eth_data.stats_block_coalesce_usecs, + [_NM_ETHTOOL_ID_COALESCE_AS_IDX(NM_ETHTOOL_ID_COALESCE_ADAPTIVE_RX)] = + eth_data.use_adaptive_rx_coalesce, + [_NM_ETHTOOL_ID_COALESCE_AS_IDX(NM_ETHTOOL_ID_COALESCE_ADAPTIVE_TX)] = + eth_data.use_adaptive_tx_coalesce, + [_NM_ETHTOOL_ID_COALESCE_AS_IDX(NM_ETHTOOL_ID_COALESCE_PKT_RATE_LOW)] = + eth_data.pkt_rate_low, + [_NM_ETHTOOL_ID_COALESCE_AS_IDX(NM_ETHTOOL_ID_COALESCE_RX_USECS_LOW)] = + eth_data.rx_coalesce_usecs_low, + [_NM_ETHTOOL_ID_COALESCE_AS_IDX(NM_ETHTOOL_ID_COALESCE_RX_FRAMES_LOW)] = + eth_data.rx_max_coalesced_frames_low, + [_NM_ETHTOOL_ID_COALESCE_AS_IDX(NM_ETHTOOL_ID_COALESCE_TX_USECS_LOW)] = + eth_data.tx_coalesce_usecs_low, + [_NM_ETHTOOL_ID_COALESCE_AS_IDX(NM_ETHTOOL_ID_COALESCE_TX_FRAMES_LOW)] = + eth_data.tx_max_coalesced_frames_low, + [_NM_ETHTOOL_ID_COALESCE_AS_IDX(NM_ETHTOOL_ID_COALESCE_PKT_RATE_HIGH)] = + eth_data.pkt_rate_high, + [_NM_ETHTOOL_ID_COALESCE_AS_IDX(NM_ETHTOOL_ID_COALESCE_RX_USECS_HIGH)] = + eth_data.rx_coalesce_usecs_high, + [_NM_ETHTOOL_ID_COALESCE_AS_IDX(NM_ETHTOOL_ID_COALESCE_RX_FRAMES_HIGH)] = + eth_data.rx_max_coalesced_frames_high, + [_NM_ETHTOOL_ID_COALESCE_AS_IDX(NM_ETHTOOL_ID_COALESCE_TX_USECS_HIGH)] = + eth_data.tx_coalesce_usecs_high, + [_NM_ETHTOOL_ID_COALESCE_AS_IDX(NM_ETHTOOL_ID_COALESCE_TX_FRAMES_HIGH)] = + eth_data.tx_max_coalesced_frames_high, + [_NM_ETHTOOL_ID_COALESCE_AS_IDX(NM_ETHTOOL_ID_COALESCE_SAMPLE_INTERVAL)] = + eth_data.rate_sample_interval, + }}; + return TRUE; +} + +gboolean +nmp_utils_ethtool_get_coalesce(int ifindex, NMEthtoolCoalesceState *coalesce) +{ + nm_auto_socket_handle SocketHandle shandle = SOCKET_HANDLE_INIT(ifindex); + + g_return_val_if_fail(ifindex > 0, FALSE); + g_return_val_if_fail(coalesce, FALSE); + + if (!ethtool_get_coalesce(&shandle, coalesce)) { + nm_log_trace(LOGD_PLATFORM, + "ethtool[%d]: %s: failure getting coalesce settings", + ifindex, + "get-coalesce"); + return FALSE; + } + + nm_log_trace(LOGD_PLATFORM, + "ethtool[%d]: %s: retrieved kernel coalesce settings", + ifindex, + "get-coalesce"); + return TRUE; +} + +static gboolean +ethtool_set_coalesce(SocketHandle *shandle, const NMEthtoolCoalesceState *coalesce) +{ + struct ethtool_coalesce eth_data; + gboolean success; + + nm_assert(shandle); + nm_assert(coalesce); + + eth_data = (struct ethtool_coalesce){ + .cmd = ETHTOOL_SCOALESCE, + .rx_coalesce_usecs = + coalesce->s[_NM_ETHTOOL_ID_COALESCE_AS_IDX(NM_ETHTOOL_ID_COALESCE_RX_USECS)], + .rx_max_coalesced_frames = + coalesce->s[_NM_ETHTOOL_ID_COALESCE_AS_IDX(NM_ETHTOOL_ID_COALESCE_RX_FRAMES)], + .rx_coalesce_usecs_irq = + coalesce->s[_NM_ETHTOOL_ID_COALESCE_AS_IDX(NM_ETHTOOL_ID_COALESCE_RX_USECS_IRQ)], + .rx_max_coalesced_frames_irq = + coalesce->s[_NM_ETHTOOL_ID_COALESCE_AS_IDX(NM_ETHTOOL_ID_COALESCE_RX_FRAMES_IRQ)], + .tx_coalesce_usecs = + coalesce->s[_NM_ETHTOOL_ID_COALESCE_AS_IDX(NM_ETHTOOL_ID_COALESCE_TX_USECS)], + .tx_max_coalesced_frames = + coalesce->s[_NM_ETHTOOL_ID_COALESCE_AS_IDX(NM_ETHTOOL_ID_COALESCE_TX_FRAMES)], + .tx_coalesce_usecs_irq = + coalesce->s[_NM_ETHTOOL_ID_COALESCE_AS_IDX(NM_ETHTOOL_ID_COALESCE_TX_USECS_IRQ)], + .tx_max_coalesced_frames_irq = + coalesce->s[_NM_ETHTOOL_ID_COALESCE_AS_IDX(NM_ETHTOOL_ID_COALESCE_TX_FRAMES_IRQ)], + .stats_block_coalesce_usecs = + coalesce->s[_NM_ETHTOOL_ID_COALESCE_AS_IDX(NM_ETHTOOL_ID_COALESCE_STATS_BLOCK_USECS)], + .use_adaptive_rx_coalesce = + coalesce->s[_NM_ETHTOOL_ID_COALESCE_AS_IDX(NM_ETHTOOL_ID_COALESCE_ADAPTIVE_RX)], + .use_adaptive_tx_coalesce = + coalesce->s[_NM_ETHTOOL_ID_COALESCE_AS_IDX(NM_ETHTOOL_ID_COALESCE_ADAPTIVE_TX)], + .pkt_rate_low = + coalesce->s[_NM_ETHTOOL_ID_COALESCE_AS_IDX(NM_ETHTOOL_ID_COALESCE_PKT_RATE_LOW)], + .rx_coalesce_usecs_low = + coalesce->s[_NM_ETHTOOL_ID_COALESCE_AS_IDX(NM_ETHTOOL_ID_COALESCE_RX_USECS_LOW)], + .rx_max_coalesced_frames_low = + coalesce->s[_NM_ETHTOOL_ID_COALESCE_AS_IDX(NM_ETHTOOL_ID_COALESCE_RX_FRAMES_LOW)], + .tx_coalesce_usecs_low = + coalesce->s[_NM_ETHTOOL_ID_COALESCE_AS_IDX(NM_ETHTOOL_ID_COALESCE_TX_USECS_LOW)], + .tx_max_coalesced_frames_low = + coalesce->s[_NM_ETHTOOL_ID_COALESCE_AS_IDX(NM_ETHTOOL_ID_COALESCE_TX_FRAMES_LOW)], + .pkt_rate_high = + coalesce->s[_NM_ETHTOOL_ID_COALESCE_AS_IDX(NM_ETHTOOL_ID_COALESCE_PKT_RATE_HIGH)], + .rx_coalesce_usecs_high = + coalesce->s[_NM_ETHTOOL_ID_COALESCE_AS_IDX(NM_ETHTOOL_ID_COALESCE_RX_USECS_HIGH)], + .rx_max_coalesced_frames_high = + coalesce->s[_NM_ETHTOOL_ID_COALESCE_AS_IDX(NM_ETHTOOL_ID_COALESCE_RX_FRAMES_HIGH)], + .tx_coalesce_usecs_high = + coalesce->s[_NM_ETHTOOL_ID_COALESCE_AS_IDX(NM_ETHTOOL_ID_COALESCE_TX_USECS_HIGH)], + .tx_max_coalesced_frames_high = + coalesce->s[_NM_ETHTOOL_ID_COALESCE_AS_IDX(NM_ETHTOOL_ID_COALESCE_TX_FRAMES_HIGH)], + .rate_sample_interval = + coalesce->s[_NM_ETHTOOL_ID_COALESCE_AS_IDX(NM_ETHTOOL_ID_COALESCE_SAMPLE_INTERVAL)], + }; + + success = (_ethtool_call_handle(shandle, ð_data, sizeof(struct ethtool_coalesce)) == 0); + return success; +} + +gboolean +nmp_utils_ethtool_set_coalesce(int ifindex, const NMEthtoolCoalesceState *coalesce) +{ + nm_auto_socket_handle SocketHandle shandle = SOCKET_HANDLE_INIT(ifindex); + + g_return_val_if_fail(ifindex > 0, FALSE); + g_return_val_if_fail(coalesce, FALSE); + + if (!ethtool_set_coalesce(&shandle, coalesce)) { + nm_log_trace(LOGD_PLATFORM, + "ethtool[%d]: %s: failure setting coalesce settings", + ifindex, + "set-coalesce"); + return FALSE; + } + + nm_log_trace(LOGD_PLATFORM, + "ethtool[%d]: %s: set kernel coalesce settings", + ifindex, + "set-coalesce"); + return TRUE; +} + +static gboolean +ethtool_get_ring(SocketHandle *shandle, NMEthtoolRingState *ring) +{ + struct ethtool_ringparam eth_data; + + eth_data.cmd = ETHTOOL_GRINGPARAM; + + if (_ethtool_call_handle(shandle, ð_data, sizeof(struct ethtool_ringparam)) != 0) + return FALSE; + + ring->rx_pending = eth_data.rx_pending; + ring->rx_jumbo_pending = eth_data.rx_jumbo_pending; + ring->rx_mini_pending = eth_data.rx_mini_pending; + ring->tx_pending = eth_data.tx_pending; + + return TRUE; +} + +gboolean +nmp_utils_ethtool_get_ring(int ifindex, NMEthtoolRingState *ring) +{ + nm_auto_socket_handle SocketHandle shandle = SOCKET_HANDLE_INIT(ifindex); + + g_return_val_if_fail(ifindex > 0, FALSE); + g_return_val_if_fail(ring, FALSE); + + if (!ethtool_get_ring(&shandle, ring)) { + nm_log_trace(LOGD_PLATFORM, + "ethtool[%d]: %s: failure getting ring settings", + ifindex, + "get-ring"); + return FALSE; + } + + nm_log_trace(LOGD_PLATFORM, + "ethtool[%d]: %s: retrieved kernel ring settings", + ifindex, + "get-ring"); + return TRUE; +} + +static gboolean +ethtool_set_ring(SocketHandle *shandle, const NMEthtoolRingState *ring) +{ + gboolean success; + struct ethtool_ringparam eth_data; + + g_return_val_if_fail(shandle, FALSE); + g_return_val_if_fail(ring, FALSE); + + eth_data = (struct ethtool_ringparam){ + .cmd = ETHTOOL_SRINGPARAM, + .rx_pending = ring->rx_pending, + .rx_jumbo_pending = ring->rx_jumbo_pending, + .rx_mini_pending = ring->rx_mini_pending, + .tx_pending = ring->tx_pending, + }; + + success = (_ethtool_call_handle(shandle, ð_data, sizeof(struct ethtool_ringparam)) == 0); + return success; +} + +gboolean +nmp_utils_ethtool_set_ring(int ifindex, const NMEthtoolRingState *ring) +{ + nm_auto_socket_handle SocketHandle shandle = SOCKET_HANDLE_INIT(ifindex); + + g_return_val_if_fail(ifindex > 0, FALSE); + g_return_val_if_fail(ring, FALSE); + + if (!ethtool_set_ring(&shandle, ring)) { + nm_log_trace(LOGD_PLATFORM, + "ethtool[%d]: %s: failure setting ring settings", + ifindex, + "set-ring"); + return FALSE; + } + + nm_log_trace(LOGD_PLATFORM, "ethtool[%d]: %s: set kernel ring settings", ifindex, "set-ring"); + return TRUE; +} + +/*****************************************************************************/ + +gboolean +nmp_utils_ethtool_get_driver_info(int ifindex, NMPUtilsEthtoolDriverInfo *data) +{ + struct ethtool_drvinfo *drvinfo; + + G_STATIC_ASSERT_EXPR(sizeof(*data) == sizeof(*drvinfo)); + G_STATIC_ASSERT_EXPR(offsetof(NMPUtilsEthtoolDriverInfo, driver) + == offsetof(struct ethtool_drvinfo, driver)); + G_STATIC_ASSERT_EXPR(offsetof(NMPUtilsEthtoolDriverInfo, version) + == offsetof(struct ethtool_drvinfo, version)); + G_STATIC_ASSERT_EXPR(offsetof(NMPUtilsEthtoolDriverInfo, fw_version) + == offsetof(struct ethtool_drvinfo, fw_version)); + G_STATIC_ASSERT_EXPR(sizeof(data->driver) == sizeof(drvinfo->driver)); + G_STATIC_ASSERT_EXPR(sizeof(data->version) == sizeof(drvinfo->version)); + G_STATIC_ASSERT_EXPR(sizeof(data->fw_version) == sizeof(drvinfo->fw_version)); + + g_return_val_if_fail(ifindex > 0, FALSE); + g_return_val_if_fail(data, FALSE); + + drvinfo = (struct ethtool_drvinfo *) data; + *drvinfo = (struct ethtool_drvinfo){ + .cmd = ETHTOOL_GDRVINFO, + }; + return _ethtool_call_once(ifindex, drvinfo, sizeof(*drvinfo)) >= 0; +} + +gboolean +nmp_utils_ethtool_get_permanent_address(int ifindex, guint8 *buf, size_t *length) +{ + struct { + struct ethtool_perm_addr e; + guint8 _extra_data[_NM_UTILS_HWADDR_LEN_MAX + 1]; + } edata = { + .e.cmd = ETHTOOL_GPERMADDR, + .e.size = _NM_UTILS_HWADDR_LEN_MAX, + }; + const guint8 *pdata; + + guint i; + + g_return_val_if_fail(ifindex > 0, FALSE); + + if (_ethtool_call_once(ifindex, &edata, sizeof(edata)) < 0) + return FALSE; + + if (edata.e.size > _NM_UTILS_HWADDR_LEN_MAX) + return FALSE; + if (edata.e.size < 1) + return FALSE; + + pdata = (const guint8 *) edata.e.data; + + if (NM_IN_SET(pdata[0], 0, 0xFF)) { + /* Some drivers might return a permanent address of all zeros. + * Reject that (rh#1264024) + * + * Some drivers return a permanent address of all ones. Reject that too */ + for (i = 1; i < edata.e.size; i++) { + if (pdata[0] != pdata[i]) + goto not_all_0or1; + } + return FALSE; + } + +not_all_0or1: + memcpy(buf, pdata, edata.e.size); + *length = edata.e.size; + return TRUE; +} + +gboolean +nmp_utils_ethtool_supports_carrier_detect(int ifindex) +{ + struct ethtool_cmd edata = {.cmd = ETHTOOL_GLINK}; + + g_return_val_if_fail(ifindex > 0, FALSE); + + /* We ignore the result. If the ETHTOOL_GLINK call succeeded, then we + * assume the device supports carrier-detect, otherwise we assume it + * doesn't. + */ + return _ethtool_call_once(ifindex, &edata, sizeof(edata)) >= 0; +} + +gboolean +nmp_utils_ethtool_supports_vlans(int ifindex) +{ + nm_auto_socket_handle SocketHandle shandle = SOCKET_HANDLE_INIT(ifindex); + gs_free struct ethtool_gfeatures * features_free = NULL; + struct ethtool_gfeatures * features; + gsize features_len; + int idx, block, bit, size; + + g_return_val_if_fail(ifindex > 0, FALSE); + + idx = ethtool_get_stringset_index(&shandle, ETH_SS_FEATURES, "vlan-challenged"); + if (idx < 0) { + nm_log_dbg(LOGD_PLATFORM, + "ethtool[%d]: vlan-challenged ethtool feature does not exist?", + ifindex); + return FALSE; + } + + block = idx / 32; + bit = idx % 32; + size = block + 1; + + features_len = sizeof(*features) + (size * sizeof(struct ethtool_get_features_block)); + features = nm_malloc0_maybe_a(300, features_len, &features_free); + features->cmd = ETHTOOL_GFEATURES; + features->size = size; + + if (_ethtool_call_handle(&shandle, features, features_len) < 0) + return FALSE; + + return !(features->features[block].active & (1 << bit)); +} + +int +nmp_utils_ethtool_get_peer_ifindex(int ifindex) +{ + nm_auto_socket_handle SocketHandle shandle = SOCKET_HANDLE_INIT(ifindex); + gsize stats_len; + gs_free struct ethtool_stats * stats_free = NULL; + struct ethtool_stats * stats; + int peer_ifindex_stat; + + g_return_val_if_fail(ifindex > 0, 0); + + peer_ifindex_stat = ethtool_get_stringset_index(&shandle, ETH_SS_STATS, "peer_ifindex"); + if (peer_ifindex_stat < 0) { + nm_log_dbg(LOGD_PLATFORM, "ethtool[%d]: peer_ifindex stat does not exist?", ifindex); + return FALSE; + } + + stats_len = sizeof(*stats) + (peer_ifindex_stat + 1) * sizeof(guint64); + stats = nm_malloc0_maybe_a(300, stats_len, &stats_free); + stats->cmd = ETHTOOL_GSTATS; + stats->n_stats = peer_ifindex_stat + 1; + if (_ethtool_call_handle(&shandle, stats, stats_len) < 0) + return 0; + + return stats->data[peer_ifindex_stat]; +} + +gboolean +nmp_utils_ethtool_get_wake_on_lan(int ifindex) +{ + struct ethtool_wolinfo wol = { + .cmd = ETHTOOL_GWOL, + }; + + g_return_val_if_fail(ifindex > 0, FALSE); + + if (_ethtool_call_once(ifindex, &wol, sizeof(wol)) < 0) + return FALSE; + + return wol.wolopts != 0; +} + +gboolean +nmp_utils_ethtool_get_link_settings(int ifindex, + gboolean * out_autoneg, + guint32 * out_speed, + NMPlatformLinkDuplexType *out_duplex) +{ + struct ethtool_cmd edata = { + .cmd = ETHTOOL_GSET, + }; + + g_return_val_if_fail(ifindex > 0, FALSE); + + if (_ethtool_call_once(ifindex, &edata, sizeof(edata)) < 0) + return FALSE; + + NM_SET_OUT(out_autoneg, (edata.autoneg == AUTONEG_ENABLE)); + + if (out_speed) { + guint32 speed; + + speed = ethtool_cmd_speed(&edata); + if (speed == G_MAXUINT16 || speed == G_MAXUINT32) + speed = 0; + + *out_speed = speed; + } + + if (out_duplex) { + switch (edata.duplex) { + case DUPLEX_HALF: + *out_duplex = NM_PLATFORM_LINK_DUPLEX_HALF; + break; + case DUPLEX_FULL: + *out_duplex = NM_PLATFORM_LINK_DUPLEX_FULL; + break; + default: /* DUPLEX_UNKNOWN */ + *out_duplex = NM_PLATFORM_LINK_DUPLEX_UNKNOWN; + break; + } + } + + return TRUE; +} + +#define ADVERTISED_INVALID 0 +#define BASET_ALL_MODES \ + (ADVERTISED_10baseT_Half | ADVERTISED_10baseT_Full | ADVERTISED_100baseT_Half \ + | ADVERTISED_100baseT_Full | ADVERTISED_1000baseT_Half | ADVERTISED_1000baseT_Full \ + | ADVERTISED_10000baseT_Full) + +static guint32 +get_baset_mode(guint32 speed, NMPlatformLinkDuplexType duplex) +{ + if (duplex == NM_PLATFORM_LINK_DUPLEX_UNKNOWN) + return ADVERTISED_INVALID; + + if (duplex == NM_PLATFORM_LINK_DUPLEX_HALF) { + switch (speed) { + case 10: + return ADVERTISED_10baseT_Half; + case 100: + return ADVERTISED_100baseT_Half; + case 1000: + return ADVERTISED_1000baseT_Half; + default: + return ADVERTISED_INVALID; + } + } else { + switch (speed) { + case 10: + return ADVERTISED_10baseT_Full; + case 100: + return ADVERTISED_100baseT_Full; + case 1000: + return ADVERTISED_1000baseT_Full; + case 10000: + return ADVERTISED_10000baseT_Full; + default: + return ADVERTISED_INVALID; + } + } +} + +gboolean +nmp_utils_ethtool_set_link_settings(int ifindex, + gboolean autoneg, + guint32 speed, + NMPlatformLinkDuplexType duplex) +{ + nm_auto_socket_handle SocketHandle shandle = SOCKET_HANDLE_INIT(ifindex); + struct ethtool_cmd edata = { + .cmd = ETHTOOL_GSET, + }; + + g_return_val_if_fail(ifindex > 0, FALSE); + g_return_val_if_fail((speed && duplex != NM_PLATFORM_LINK_DUPLEX_UNKNOWN) + || (!speed && duplex == NM_PLATFORM_LINK_DUPLEX_UNKNOWN), + FALSE); + + /* retrieve first current settings */ + if (_ethtool_call_handle(&shandle, &edata, sizeof(edata)) < 0) + return FALSE; + + /* FIXME: try first new ETHTOOL_GLINKSETTINGS/SLINKSETTINGS API + * https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=3f1ac7a700d039c61d8d8b99f28d605d489a60cf + */ + + /* then change the needed ones */ + edata.cmd = ETHTOOL_SSET; + if (autoneg) { + edata.autoneg = AUTONEG_ENABLE; + if (!speed) + edata.advertising = edata.supported; + else { + guint32 mode; + + mode = get_baset_mode(speed, duplex); + + if (!mode) { + nm_log_trace(LOGD_PLATFORM, + "ethtool[%d]: %uBASE-T %s duplex mode cannot be advertised", + ifindex, + speed, + nm_platform_link_duplex_type_to_string(duplex)); + return FALSE; + } + if (!(edata.supported & mode)) { + nm_log_trace(LOGD_PLATFORM, + "ethtool[%d]: device does not support %uBASE-T %s duplex mode", + ifindex, + speed, + nm_platform_link_duplex_type_to_string(duplex)); + return FALSE; + } + edata.advertising = (edata.supported & ~BASET_ALL_MODES) | mode; + } + } else { + edata.autoneg = AUTONEG_DISABLE; + + if (speed) + ethtool_cmd_speed_set(&edata, speed); + + switch (duplex) { + case NM_PLATFORM_LINK_DUPLEX_HALF: + edata.duplex = DUPLEX_HALF; + break; + case NM_PLATFORM_LINK_DUPLEX_FULL: + edata.duplex = DUPLEX_FULL; + break; + case NM_PLATFORM_LINK_DUPLEX_UNKNOWN: + break; + default: + g_return_val_if_reached(FALSE); + } + } + + return _ethtool_call_handle(&shandle, &edata, sizeof(edata)) >= 0; +} + +gboolean +nmp_utils_ethtool_set_wake_on_lan(int ifindex, + _NMSettingWiredWakeOnLan wol, + const char * wol_password) +{ + struct ethtool_wolinfo wol_info = { + .cmd = ETHTOOL_SWOL, + .wolopts = 0, + }; + + g_return_val_if_fail(ifindex > 0, FALSE); + + if (wol == _NM_SETTING_WIRED_WAKE_ON_LAN_IGNORE) + return TRUE; + + nm_log_dbg(LOGD_PLATFORM, + "ethtool[%d]: setting Wake-on-LAN options 0x%x, password '%s'", + ifindex, + (unsigned) wol, + wol_password); + + if (NM_FLAGS_HAS(wol, _NM_SETTING_WIRED_WAKE_ON_LAN_PHY)) + wol_info.wolopts |= WAKE_PHY; + if (NM_FLAGS_HAS(wol, _NM_SETTING_WIRED_WAKE_ON_LAN_UNICAST)) + wol_info.wolopts |= WAKE_UCAST; + if (NM_FLAGS_HAS(wol, _NM_SETTING_WIRED_WAKE_ON_LAN_MULTICAST)) + wol_info.wolopts |= WAKE_MCAST; + if (NM_FLAGS_HAS(wol, _NM_SETTING_WIRED_WAKE_ON_LAN_BROADCAST)) + wol_info.wolopts |= WAKE_BCAST; + if (NM_FLAGS_HAS(wol, _NM_SETTING_WIRED_WAKE_ON_LAN_ARP)) + wol_info.wolopts |= WAKE_ARP; + if (NM_FLAGS_HAS(wol, _NM_SETTING_WIRED_WAKE_ON_LAN_MAGIC)) + wol_info.wolopts |= WAKE_MAGIC; + + if (wol_password) { + if (!_nm_utils_hwaddr_aton_exact(wol_password, wol_info.sopass, ETH_ALEN)) { + nm_log_dbg(LOGD_PLATFORM, + "ethtool[%d]: couldn't parse Wake-on-LAN password '%s'", + ifindex, + wol_password); + return FALSE; + } + wol_info.wolopts |= WAKE_MAGICSECURE; + } + + return _ethtool_call_once(ifindex, &wol_info, sizeof(wol_info)) >= 0; +} + +/****************************************************************************** + * mii + *****************************************************************************/ + +gboolean +nmp_utils_mii_supports_carrier_detect(int ifindex) +{ + nm_auto_socket_handle SocketHandle shandle = SOCKET_HANDLE_INIT(ifindex); + int r; + struct ifreq ifr; + struct mii_ioctl_data * mii; + + g_return_val_if_fail(ifindex > 0, FALSE); + + r = _ioctl_call("mii", + "SIOCGMIIPHY", + SIOCGMIIPHY, + shandle.ifindex, + &shandle.fd, + shandle.ifname, + IOCTL_CALL_DATA_TYPE_NONE, + NULL, + 0, + &ifr); + if (r < 0) + return FALSE; + + /* If we can read the BMSR register, we assume that the card supports MII link detection */ + mii = (struct mii_ioctl_data *) &ifr.ifr_ifru; + mii->reg_num = MII_BMSR; + + r = _ioctl_call("mii", + "SIOCGMIIREG", + SIOCGMIIREG, + shandle.ifindex, + &shandle.fd, + shandle.ifname, + IOCTL_CALL_DATA_TYPE_IFRU, + mii, + sizeof(*mii), + &ifr); + if (r < 0) + return FALSE; + + mii = (struct mii_ioctl_data *) &ifr.ifr_ifru; + nm_log_trace(LOGD_PLATFORM, + "mii[%d,%s]: carrier-detect yes: SIOCGMIIREG result 0x%X", + ifindex, + shandle.ifname, + mii->val_out); + return TRUE; +} + +/****************************************************************************** + * udev + *****************************************************************************/ + +const char * +nmp_utils_udev_get_driver(struct udev_device *udevice) +{ + struct udev_device *parent = NULL, *grandparent = NULL; + const char * driver, *subsys; + + driver = udev_device_get_driver(udevice); + if (driver) + goto out; + + /* Try the parent */ + parent = udev_device_get_parent(udevice); + if (parent) { + driver = udev_device_get_driver(parent); + if (!driver) { + /* Try the grandparent if it's an ibmebus device or if the + * subsys is NULL which usually indicates some sort of + * platform device like a 'gadget' net interface. + */ + subsys = udev_device_get_subsystem(parent); + if ((g_strcmp0(subsys, "ibmebus") == 0) || (subsys == NULL)) { + grandparent = udev_device_get_parent(parent); + if (grandparent) + driver = udev_device_get_driver(grandparent); + } + } + } + +out: + /* Intern the string so we don't have to worry about memory + * management in NMPlatformLink. */ + return g_intern_string(driver); +} + +/****************************************************************************** + * utils + *****************************************************************************/ + +NMIPConfigSource +nmp_utils_ip_config_source_from_rtprot(guint8 rtprot) +{ + return ((int) rtprot) + 1; +} + +NMIPConfigSource +nmp_utils_ip_config_source_round_trip_rtprot(NMIPConfigSource source) +{ + /* when adding a route to kernel for a give @source, the resulting route + * will be put into the cache with a source of NM_IP_CONFIG_SOURCE_RTPROT_*. + * This function returns that. */ + return nmp_utils_ip_config_source_from_rtprot( + nmp_utils_ip_config_source_coerce_to_rtprot(source)); +} + +guint8 +nmp_utils_ip_config_source_coerce_to_rtprot(NMIPConfigSource source) +{ + /* when adding a route to kernel, we coerce the @source field + * to rtm_protocol. This is not lossless as we map different + * source values to the same RTPROT uint8 value. */ + if (source <= NM_IP_CONFIG_SOURCE_UNKNOWN) + return RTPROT_UNSPEC; + + if (source <= _NM_IP_CONFIG_SOURCE_RTPROT_LAST) + return source - 1; + + switch (source) { + case NM_IP_CONFIG_SOURCE_KERNEL: + return RTPROT_KERNEL; + case NM_IP_CONFIG_SOURCE_IP6LL: + return RTPROT_KERNEL; + case NM_IP_CONFIG_SOURCE_DHCP: + return RTPROT_DHCP; + case NM_IP_CONFIG_SOURCE_NDISC: + return RTPROT_RA; + + default: + return RTPROT_STATIC; + } +} + +NMIPConfigSource +nmp_utils_ip_config_source_coerce_from_rtprot(NMIPConfigSource source) +{ + /* When we receive a route from kernel and put it into the platform cache, + * we preserve the protocol field by converting it to a NMIPConfigSource + * via nmp_utils_ip_config_source_from_rtprot(). + * + * However, that is not the inverse of nmp_utils_ip_config_source_coerce_to_rtprot(). + * Instead, to go back to the original value, you need another step: + * nmp_utils_ip_config_source_coerce_from_rtprot (nmp_utils_ip_config_source_from_rtprot (rtprot)). + * + * This might partly restore the original source value, but of course that + * is not really possible because nmp_utils_ip_config_source_coerce_to_rtprot() + * is not injective. + * */ + switch (source) { + case NM_IP_CONFIG_SOURCE_RTPROT_UNSPEC: + return NM_IP_CONFIG_SOURCE_UNKNOWN; + + case NM_IP_CONFIG_SOURCE_RTPROT_KERNEL: + case NM_IP_CONFIG_SOURCE_RTPROT_REDIRECT: + return NM_IP_CONFIG_SOURCE_KERNEL; + + case NM_IP_CONFIG_SOURCE_RTPROT_RA: + return NM_IP_CONFIG_SOURCE_NDISC; + + case NM_IP_CONFIG_SOURCE_RTPROT_DHCP: + return NM_IP_CONFIG_SOURCE_DHCP; + + default: + return NM_IP_CONFIG_SOURCE_USER; + } +} + +const char * +nmp_utils_ip_config_source_to_string(NMIPConfigSource source, char *buf, gsize len) +{ + const char *s = NULL; + nm_utils_to_string_buffer_init(&buf, &len); + + if (!len) + return buf; + + switch (source) { + case NM_IP_CONFIG_SOURCE_UNKNOWN: + s = "unknown"; + break; + + case NM_IP_CONFIG_SOURCE_RTPROT_UNSPEC: + s = "rt-unspec"; + break; + case NM_IP_CONFIG_SOURCE_RTPROT_REDIRECT: + s = "rt-redirect"; + break; + case NM_IP_CONFIG_SOURCE_RTPROT_KERNEL: + s = "rt-kernel"; + break; + case NM_IP_CONFIG_SOURCE_RTPROT_BOOT: + s = "rt-boot"; + break; + case NM_IP_CONFIG_SOURCE_RTPROT_STATIC: + s = "rt-static"; + break; + case NM_IP_CONFIG_SOURCE_RTPROT_DHCP: + s = "rt-dhcp"; + break; + case NM_IP_CONFIG_SOURCE_RTPROT_RA: + s = "rt-ra"; + break; + + case NM_IP_CONFIG_SOURCE_KERNEL: + s = "kernel"; + break; + case NM_IP_CONFIG_SOURCE_SHARED: + s = "shared"; + break; + case NM_IP_CONFIG_SOURCE_IP4LL: + s = "ipv4ll"; + break; + case NM_IP_CONFIG_SOURCE_IP6LL: + s = "ipv6ll"; + break; + case NM_IP_CONFIG_SOURCE_PPP: + s = "ppp"; + break; + case NM_IP_CONFIG_SOURCE_WWAN: + s = "wwan"; + break; + case NM_IP_CONFIG_SOURCE_VPN: + s = "vpn"; + break; + case NM_IP_CONFIG_SOURCE_DHCP: + s = "dhcp"; + break; + case NM_IP_CONFIG_SOURCE_NDISC: + s = "ndisc"; + break; + case NM_IP_CONFIG_SOURCE_USER: + s = "user"; + break; + default: + break; + } + + if (source >= 1 && source <= 0x100) { + if (s) + g_snprintf(buf, len, "%s", s); + else + g_snprintf(buf, len, "rt-%d", ((int) source) - 1); + } else { + if (s) + g_strlcpy(buf, s, len); + else + g_snprintf(buf, len, "(%d)", source); + } + return buf; +} + +/** + * nmp_utils_sysctl_open_netdir: + * @ifindex: the ifindex for which to open "/sys/class/net/%s" + * @ifname_guess: (allow-none): optional argument, if present used as initial + * guess as the current name for @ifindex. If guessed right, + * it saves an additional if_indextoname() call. + * @out_ifname: (allow-none): if present, must be at least IFNAMSIZ + * characters. On success, this will contain the actual ifname + * found while opening the directory. + * + * Returns: a negative value on failure, on success returns the open fd + * to the "/sys/class/net/%s" directory for @ifindex. + */ +int +nmp_utils_sysctl_open_netdir(int ifindex, const char *ifname_guess, char *out_ifname) +{ +#define SYS_CLASS_NET "/sys/class/net/" + const char *ifname = ifname_guess; + char ifname_buf_last_try[IFNAMSIZ]; + char ifname_buf[IFNAMSIZ]; + guint try_count = 0; + char sysdir[NM_STRLEN(SYS_CLASS_NET) + IFNAMSIZ] = SYS_CLASS_NET; + char fd_buf[256]; + ssize_t nn; + + g_return_val_if_fail(ifindex >= 0, -1); + + ifname_buf_last_try[0] = '\0'; + + for (try_count = 0; try_count < 10; try_count++, ifname = NULL) { + nm_auto_close int fd_dir = -1; + nm_auto_close int fd_ifindex = -1; + + if (!ifname) { + ifname = nmp_utils_if_indextoname(ifindex, ifname_buf); + if (!ifname) + return -1; + } + + nm_assert(nm_utils_ifname_valid_kernel(ifname, NULL)); + + if (g_strlcpy(&sysdir[NM_STRLEN(SYS_CLASS_NET)], ifname, IFNAMSIZ) >= IFNAMSIZ) + g_return_val_if_reached(-1); + + /* we only retry, if the name changed since previous attempt. + * Hence, it is extremely unlikely that this loop runes until the + * end of the @try_count. */ + if (nm_streq(ifname, ifname_buf_last_try)) + return -1; + strcpy(ifname_buf_last_try, ifname); + + fd_dir = open(sysdir, O_DIRECTORY | O_CLOEXEC); + if (fd_dir < 0) + continue; + + fd_ifindex = openat(fd_dir, "ifindex", O_CLOEXEC); + if (fd_ifindex < 0) + continue; + + nn = nm_utils_fd_read_loop(fd_ifindex, fd_buf, sizeof(fd_buf) - 2, FALSE); + if (nn <= 0) + continue; + fd_buf[nn] = '\0'; + + if (ifindex != (int) _nm_utils_ascii_str_to_int64(fd_buf, 10, 1, G_MAXINT, -1)) + continue; + + if (out_ifname) + strcpy(out_ifname, ifname); + + return nm_steal_fd(&fd_dir); + } + + return -1; +} diff --git a/shared/nm-platform/nm-platform-utils.h b/shared/nm-platform/nm-platform-utils.h new file mode 100644 index 00000000..d74723eb --- /dev/null +++ b/shared/nm-platform/nm-platform-utils.h @@ -0,0 +1,73 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright (C) 2015 Red Hat, Inc. + */ + +#ifndef __NM_PLATFORM_UTILS_H__ +#define __NM_PLATFORM_UTILS_H__ + +#include "nm-base/nm-base.h" +#include "nm-platform/nmp-base.h" + +/*****************************************************************************/ + +const char *nmp_utils_ethtool_get_driver(int ifindex); +gboolean nmp_utils_ethtool_supports_carrier_detect(int ifindex); +gboolean nmp_utils_ethtool_supports_vlans(int ifindex); +int nmp_utils_ethtool_get_peer_ifindex(int ifindex); +gboolean nmp_utils_ethtool_get_wake_on_lan(int ifindex); +gboolean nmp_utils_ethtool_set_wake_on_lan(int ifindex, + _NMSettingWiredWakeOnLan wol, + const char * wol_password); + +const char *nm_platform_link_duplex_type_to_string(NMPlatformLinkDuplexType duplex); + +gboolean nmp_utils_ethtool_get_link_settings(int ifindex, + gboolean * out_autoneg, + guint32 * out_speed, + NMPlatformLinkDuplexType *out_duplex); +gboolean nmp_utils_ethtool_set_link_settings(int ifindex, + gboolean autoneg, + guint32 speed, + NMPlatformLinkDuplexType duplex); + +gboolean nmp_utils_ethtool_get_permanent_address(int ifindex, guint8 *buf, size_t *length); + +gboolean nmp_utils_ethtool_get_driver_info(int ifindex, NMPUtilsEthtoolDriverInfo *data); + +NMEthtoolFeatureStates *nmp_utils_ethtool_get_features(int ifindex); + +gboolean nmp_utils_ethtool_set_features( + int ifindex, + const NMEthtoolFeatureStates *features, + const NMOptionBool *requested /* indexed by NMEthtoolID - _NM_ETHTOOL_ID_FEATURE_FIRST */, + gboolean do_set /* or reset */); + +gboolean nmp_utils_ethtool_get_coalesce(int ifindex, NMEthtoolCoalesceState *coalesce); + +gboolean nmp_utils_ethtool_set_coalesce(int ifindex, const NMEthtoolCoalesceState *coalesce); + +gboolean nmp_utils_ethtool_get_ring(int ifindex, NMEthtoolRingState *ring); + +gboolean nmp_utils_ethtool_set_ring(int ifindex, const NMEthtoolRingState *ring); + +/*****************************************************************************/ + +gboolean nmp_utils_mii_supports_carrier_detect(int ifindex); + +struct udev_device; + +const char *nmp_utils_udev_get_driver(struct udev_device *udevice); + +NMIPConfigSource nmp_utils_ip_config_source_from_rtprot(guint8 rtprot) _nm_const; +guint8 nmp_utils_ip_config_source_coerce_to_rtprot(NMIPConfigSource source) _nm_const; +NMIPConfigSource nmp_utils_ip_config_source_coerce_from_rtprot(NMIPConfigSource source) _nm_const; +NMIPConfigSource nmp_utils_ip_config_source_round_trip_rtprot(NMIPConfigSource source) _nm_const; +const char *nmp_utils_ip_config_source_to_string(NMIPConfigSource source, char *buf, gsize len); + +const char *nmp_utils_if_indextoname(int ifindex, char *out_ifname /*IFNAMSIZ*/); +int nmp_utils_if_nametoindex(const char *ifname); + +int nmp_utils_sysctl_open_netdir(int ifindex, const char *ifname_guess, char *out_ifname); + +#endif /* __NM_PLATFORM_UTILS_H__ */ diff --git a/shared/nm-platform/nmp-base.h b/shared/nm-platform/nmp-base.h new file mode 100644 index 00000000..210c26d6 --- /dev/null +++ b/shared/nm-platform/nmp-base.h @@ -0,0 +1,94 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#ifndef __NMP_FWD_H__ +#define __NMP_FWD_H__ + +#include "nm-base/nm-base.h" + +/*****************************************************************************/ + +typedef enum { + NM_PLATFORM_LINK_DUPLEX_UNKNOWN, + NM_PLATFORM_LINK_DUPLEX_HALF, + NM_PLATFORM_LINK_DUPLEX_FULL, +} NMPlatformLinkDuplexType; + +/*****************************************************************************/ + +typedef struct { + /* We don't want to include <linux/ethtool.h> in header files, + * thus create a ABI compatible version of struct ethtool_drvinfo.*/ + guint32 _private_cmd; + char driver[32]; + char version[32]; + char fw_version[32]; + char _private_bus_info[32]; + char _private_erom_version[32]; + char _private_reserved2[12]; + guint32 _private_n_priv_flags; + guint32 _private_n_stats; + guint32 _private_testinfo_len; + guint32 _private_eedump_len; + guint32 _private_regdump_len; +} NMPUtilsEthtoolDriverInfo; + +typedef struct { + NMEthtoolID ethtool_id; + + guint8 n_kernel_names; + + /* one NMEthtoolID refers to one or more kernel_names. The reason for supporting this complexity + * (where one NMSettingEthtool option refers to multiple kernel features) is to follow what + * ethtool does, where "tx" is an alias for multiple features. */ + const char *const *kernel_names; +} NMEthtoolFeatureInfo; + +typedef struct { + const NMEthtoolFeatureInfo *info; + + guint idx_ss_features; + + /* one NMEthtoolFeatureInfo references one or more kernel_names. This is the index + * of the matching info->kernel_names */ + guint8 idx_kernel_name; + + bool available : 1; + bool requested : 1; + bool active : 1; + bool never_changed : 1; +} NMEthtoolFeatureState; + +typedef struct { + guint n_states; + + guint n_ss_features; + + /* indexed by NMEthtoolID - _NM_ETHTOOL_ID_FEATURE_FIRST */ + const NMEthtoolFeatureState *const *states_indexed[_NM_ETHTOOL_ID_FEATURE_NUM]; + + /* the same content, here as a list of n_states entries. */ + const NMEthtoolFeatureState states_list[]; +} NMEthtoolFeatureStates; + +/*****************************************************************************/ + +typedef struct { + guint32 + s[_NM_ETHTOOL_ID_COALESCE_NUM /* indexed by (NMEthtoolID - _NM_ETHTOOL_ID_COALESCE_FIRST) */ + ]; +} NMEthtoolCoalesceState; + +/*****************************************************************************/ + +typedef struct { + guint32 rx_pending; + guint32 rx_mini_pending; + guint32 rx_jumbo_pending; + guint32 tx_pending; +} NMEthtoolRingState; + +/*****************************************************************************/ + +typedef struct _NMPNetns NMPNetns; + +#endif /* __NMP_FWD_H__ */ diff --git a/shared/nm-platform/nmp-netns.c b/shared/nm-platform/nmp-netns.c new file mode 100644 index 00000000..c7cb617b --- /dev/null +++ b/shared/nm-platform/nmp-netns.c @@ -0,0 +1,766 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright (C) 2016 Red Hat, Inc. + */ + +#include "nm-glib-aux/nm-default-glib-i18n-lib.h" + +#include "nmp-netns.h" + +#include <fcntl.h> +#include <sys/mount.h> +#include <sys/stat.h> +#include <sys/types.h> +#include <pthread.h> + +#include "nm-log-core/nm-logging.h" + +/*****************************************************************************/ + +/* NOTE: NMPNetns and all code used here must be thread-safe! */ + +/* we may not call logging functions from the main-thread alone. Hence, we + * require locking from nm-logging. Indicate that by setting NM_THREAD_SAFE_ON_MAIN_THREAD + * to zero. */ +#undef NM_THREAD_SAFE_ON_MAIN_THREAD +#define NM_THREAD_SAFE_ON_MAIN_THREAD 0 + +/*****************************************************************************/ + +#define PROC_SELF_NS_MNT "/proc/self/ns/mnt" +#define PROC_SELF_NS_NET "/proc/self/ns/net" + +#define _CLONE_NS_ALL ((int) (CLONE_NEWNS | CLONE_NEWNET)) +#define _CLONE_NS_ALL_V CLONE_NEWNS, CLONE_NEWNET + +static NM_UTILS_FLAGS2STR_DEFINE(_clone_ns_to_str, + int, + NM_UTILS_FLAGS2STR(CLONE_NEWNS, "mnt"), + NM_UTILS_FLAGS2STR(CLONE_NEWNET, "net"), ); + +static const char * +__ns_types_to_str(int ns_types, int ns_types_already_set, char *buf, gsize len) +{ + const char *b = buf; + char bb[200]; + + nm_utils_strbuf_append_c(&buf, &len, '['); + if (ns_types & ~ns_types_already_set) { + nm_utils_strbuf_append_str( + &buf, + &len, + _clone_ns_to_str(ns_types & ~ns_types_already_set, bb, sizeof(bb))); + } + if (ns_types & ns_types_already_set) { + if (ns_types & ~ns_types_already_set) + nm_utils_strbuf_append_c(&buf, &len, '/'); + nm_utils_strbuf_append_str( + &buf, + &len, + _clone_ns_to_str(ns_types & ns_types_already_set, bb, sizeof(bb))); + } + nm_utils_strbuf_append_c(&buf, &len, ']'); + return b; +} +#define _ns_types_to_str(ns_types, ns_types_already_set, buf) \ + __ns_types_to_str(ns_types, ns_types_already_set, buf, sizeof(buf)) + +/*****************************************************************************/ + +#define _NMLOG_DOMAIN LOGD_PLATFORM +#define _NMLOG_PREFIX_NAME "netns" +#define _NMLOG(level, netns, ...) \ + G_STMT_START \ + { \ + NMLogLevel _level = (level); \ + \ + if (nm_logging_enabled(_level, _NMLOG_DOMAIN)) { \ + NMPNetns *_netns = (netns); \ + char _sbuf[20]; \ + \ + _nm_log(_level, \ + _NMLOG_DOMAIN, \ + 0, \ + NULL, \ + NULL, \ + "%s%s: " _NM_UTILS_MACRO_FIRST(__VA_ARGS__), \ + _NMLOG_PREFIX_NAME, \ + (_netns ? nm_sprintf_buf(_sbuf, "[%p]", _netns) \ + : "") _NM_UTILS_MACRO_REST(__VA_ARGS__)); \ + } \ + } \ + G_STMT_END + +/*****************************************************************************/ + +NM_GOBJECT_PROPERTIES_DEFINE_BASE(PROP_FD_NET, PROP_FD_MNT, ); + +typedef struct { + int fd_net; + int fd_mnt; +} NMPNetnsPrivate; + +struct _NMPNetns { + GObject parent; + NMPNetnsPrivate _priv; +}; + +struct _NMPNetnsClass { + GObjectClass parent; +}; + +G_DEFINE_TYPE(NMPNetns, nmp_netns, G_TYPE_OBJECT); + +#define NMP_NETNS_GET_PRIVATE(self) _NM_GET_PRIVATE(self, NMPNetns, NMP_IS_NETNS) + +/*****************************************************************************/ + +typedef struct { + NMPNetns *netns; + int count; + int ns_types; +} NetnsInfo; + +static void _stack_push(GArray *netns_stack, NMPNetns *netns, int ns_types); +static NMPNetns *_netns_new(GError **error); + +/*****************************************************************************/ + +static NMPNetns * +_netns_get(NetnsInfo *info) +{ + nm_assert(!info || NMP_IS_NETNS(info->netns)); + return info ? info->netns : NULL; +} + +/*****************************************************************************/ + +static _nm_thread_local GArray *_netns_stack = NULL; + +static void +_netns_stack_clear_cb(gpointer data) +{ + NetnsInfo *info = data; + + nm_assert(NMP_IS_NETNS(info->netns)); + g_object_unref(info->netns); +} + +static GArray * +_netns_stack_get_impl(void) +{ + gs_unref_object NMPNetns *netns = NULL; + gs_free_error GError *error = NULL; + pthread_key_t key; + GArray * s; + + s = g_array_new(FALSE, FALSE, sizeof(NetnsInfo)); + g_array_set_clear_func(s, _netns_stack_clear_cb); + _netns_stack = s; + + /* at the bottom of the stack we must try to create a netns instance + * that we never pop. It's the base to which we need to return. */ + netns = _netns_new(&error); + if (!netns) { + _LOGE(NULL, "failed to create initial netns: %s", error->message); + return s; + } + + /* we leak this instance inside the stack. */ + _stack_push(s, netns, _CLONE_NS_ALL); + + /* finally, register a destructor function to cleanup the array. If we fail + * to do so, we will leak NMPNetns instances (and their file descriptor) when the + * thread exits. */ + if (pthread_key_create(&key, (void (*)(void *)) g_array_unref) != 0) + _LOGE(NULL, "failure to initialize thread-local storage"); + else if (pthread_setspecific(key, s) != 0) + _LOGE(NULL, "failure to set thread-local storage"); + + return s; +} + +#define _netns_stack_get() \ + ({ \ + GArray *_s = _netns_stack; \ + \ + if (G_UNLIKELY(!_s)) \ + _s = _netns_stack_get_impl(); \ + _s; \ + }) + +/*****************************************************************************/ + +static NMPNetns * +_stack_current_netns(GArray *netns_stack, int ns_types) +{ + guint j; + + nm_assert(netns_stack && netns_stack->len > 0); + + /* we search the stack top-down to find the netns that has + * all @ns_types set. */ + for (j = netns_stack->len; ns_types && j >= 1;) { + NetnsInfo *info; + + info = &g_array_index(netns_stack, NetnsInfo, --j); + + if (NM_FLAGS_ALL(info->ns_types, ns_types)) + return info->netns; + } + + g_return_val_if_reached(NULL); +} + +static int +_stack_current_ns_types(GArray *netns_stack, NMPNetns *netns, int ns_types) +{ + const int ns_types_check[] = {_CLONE_NS_ALL_V}; + guint i, j; + int res = 0; + + nm_assert(netns); + nm_assert(netns_stack && netns_stack->len > 0); + + /* we search the stack top-down to check which of @ns_types + * are already set to @netns. */ + for (j = netns_stack->len; ns_types && j >= 1;) { + NetnsInfo *info; + + info = &g_array_index(netns_stack, NetnsInfo, --j); + if (info->netns != netns) { + ns_types = NM_FLAGS_UNSET(ns_types, info->ns_types); + continue; + } + + for (i = 0; i < G_N_ELEMENTS(ns_types_check); i++) { + if (NM_FLAGS_ANY(ns_types, ns_types_check[i]) + && NM_FLAGS_ANY(info->ns_types, ns_types_check[i])) { + res = NM_FLAGS_SET(res, ns_types_check[i]); + ns_types = NM_FLAGS_UNSET(ns_types, ns_types_check[i]); + } + } + } + + return res; +} + +static NetnsInfo * +_stack_peek(GArray *netns_stack) +{ + if (netns_stack->len > 0) + return &g_array_index(netns_stack, NetnsInfo, (netns_stack->len - 1)); + return NULL; +} + +static NetnsInfo * +_stack_bottom(GArray *netns_stack) +{ + if (netns_stack->len > 0) + return &g_array_index(netns_stack, NetnsInfo, 0); + return NULL; +} + +static void +_stack_push(GArray *netns_stack, NMPNetns *netns, int ns_types) +{ + NetnsInfo *info; + + nm_assert(netns_stack); + nm_assert(NMP_IS_NETNS(netns)); + nm_assert(NM_FLAGS_ANY(ns_types, _CLONE_NS_ALL)); + nm_assert(!NM_FLAGS_ANY(ns_types, ~_CLONE_NS_ALL)); + + g_array_set_size(netns_stack, netns_stack->len + 1); + + info = &g_array_index(netns_stack, NetnsInfo, (netns_stack->len - 1)); + *info = (NetnsInfo){ + .netns = g_object_ref(netns), + .ns_types = ns_types, + .count = 1, + }; +} + +static void +_stack_pop(GArray *netns_stack) +{ + NetnsInfo *info; + + nm_assert(netns_stack); + nm_assert(netns_stack->len > 1); + + info = &g_array_index(netns_stack, NetnsInfo, (netns_stack->len - 1)); + + nm_assert(NMP_IS_NETNS(info->netns)); + nm_assert(info->count == 1); + + g_array_set_size(netns_stack, netns_stack->len - 1); +} + +static guint +_stack_size(GArray *netns_stack) +{ + nm_assert(netns_stack); + + return netns_stack->len; +} + +/*****************************************************************************/ + +static NMPNetns * +_netns_new(GError **error) +{ + NMPNetns *self; + int fd_net, fd_mnt; + int errsv; + + fd_net = open(PROC_SELF_NS_NET, O_RDONLY | O_CLOEXEC); + if (fd_net == -1) { + errsv = errno; + g_set_error(error, + NM_UTILS_ERROR, + NM_UTILS_ERROR_UNKNOWN, + "Failed opening netns: %s", + nm_strerror_native(errsv)); + errno = errsv; + return NULL; + } + + fd_mnt = open(PROC_SELF_NS_MNT, O_RDONLY | O_CLOEXEC); + if (fd_mnt == -1) { + errsv = errno; + g_set_error(error, + NM_UTILS_ERROR, + NM_UTILS_ERROR_UNKNOWN, + "Failed opening mntns: %s", + nm_strerror_native(errsv)); + nm_close(fd_net); + errno = errsv; + return NULL; + } + + self = g_object_new(NMP_TYPE_NETNS, NMP_NETNS_FD_NET, fd_net, NMP_NETNS_FD_MNT, fd_mnt, NULL); + + _LOGD(self, "new netns (net:%d, mnt:%d)", fd_net, fd_mnt); + + return self; +} + +static int +_setns(NMPNetns *self, int type) +{ + char buf[100]; + int fd; + NMPNetnsPrivate *priv = NMP_NETNS_GET_PRIVATE(self); + + nm_assert(NM_IN_SET(type, _CLONE_NS_ALL_V)); + + fd = (type == CLONE_NEWNET) ? priv->fd_net : priv->fd_mnt; + + _LOGt(self, "set netns(%s, %d)", _ns_types_to_str(type, 0, buf), fd); + + return setns(fd, type); +} + +static gboolean +_netns_switch_push(GArray *netns_stack, NMPNetns *self, int ns_types) +{ + int errsv; + + if (NM_FLAGS_HAS(ns_types, CLONE_NEWNET) + && !_stack_current_ns_types(netns_stack, self, CLONE_NEWNET) + && _setns(self, CLONE_NEWNET) != 0) { + errsv = errno; + _LOGE(self, "failed to switch netns: %s", nm_strerror_native(errsv)); + return FALSE; + } + if (NM_FLAGS_HAS(ns_types, CLONE_NEWNS) + && !_stack_current_ns_types(netns_stack, self, CLONE_NEWNS) + && _setns(self, CLONE_NEWNS) != 0) { + errsv = errno; + _LOGE(self, "failed to switch mntns: %s", nm_strerror_native(errsv)); + + /* try to fix the mess by returning to the previous netns. */ + if (NM_FLAGS_HAS(ns_types, CLONE_NEWNET) + && !_stack_current_ns_types(netns_stack, self, CLONE_NEWNET)) { + self = _stack_current_netns(netns_stack, CLONE_NEWNET); + if (self && _setns(self, CLONE_NEWNET) != 0) { + errsv = errno; + _LOGE(self, "failed to restore netns: %s", nm_strerror_native(errsv)); + } + } + return FALSE; + } + + return TRUE; +} + +static gboolean +_netns_switch_pop(GArray *netns_stack, NMPNetns *self, int ns_types) +{ + int errsv; + NMPNetns *current; + int success = TRUE; + + if (NM_FLAGS_HAS(ns_types, CLONE_NEWNET) + && (!self || !_stack_current_ns_types(netns_stack, self, CLONE_NEWNET))) { + current = _stack_current_netns(netns_stack, CLONE_NEWNET); + if (!current) { + g_warn_if_reached(); + success = FALSE; + } else if (_setns(current, CLONE_NEWNET) != 0) { + errsv = errno; + _LOGE(self, "failed to switch netns: %s", nm_strerror_native(errsv)); + success = FALSE; + } + } + if (NM_FLAGS_HAS(ns_types, CLONE_NEWNS) + && (!self || !_stack_current_ns_types(netns_stack, self, CLONE_NEWNS))) { + current = _stack_current_netns(netns_stack, CLONE_NEWNS); + if (!current) { + g_warn_if_reached(); + success = FALSE; + } else if (_setns(current, CLONE_NEWNS) != 0) { + errsv = errno; + _LOGE(self, "failed to switch mntns: %s", nm_strerror_native(errsv)); + success = FALSE; + } + } + + return success; +} + +/*****************************************************************************/ + +int +nmp_netns_get_fd_net(NMPNetns *self) +{ + g_return_val_if_fail(NMP_IS_NETNS(self), 0); + + return NMP_NETNS_GET_PRIVATE(self)->fd_net; +} + +int +nmp_netns_get_fd_mnt(NMPNetns *self) +{ + g_return_val_if_fail(NMP_IS_NETNS(self), 0); + + return NMP_NETNS_GET_PRIVATE(self)->fd_mnt; +} + +/*****************************************************************************/ + +static gboolean +_nmp_netns_push_type(NMPNetns *self, int ns_types) +{ + GArray * netns_stack = _netns_stack_get(); + NetnsInfo *info; + char sbuf[100]; + + info = _stack_peek(netns_stack); + g_return_val_if_fail(info, FALSE); + + if (info->netns == self && info->ns_types == ns_types) { + info->count++; + _LOGt(self, + "push#%u* %s (increase count to %d)", + _stack_size(netns_stack) - 1, + _ns_types_to_str(ns_types, ns_types, sbuf), + info->count); + return TRUE; + } + + _LOGD(self, + "push#%u %s", + _stack_size(netns_stack), + _ns_types_to_str(ns_types, _stack_current_ns_types(netns_stack, self, ns_types), sbuf)); + + if (!_netns_switch_push(netns_stack, self, ns_types)) + return FALSE; + + _stack_push(netns_stack, self, ns_types); + return TRUE; +} + +gboolean +nmp_netns_push(NMPNetns *self) +{ + g_return_val_if_fail(NMP_IS_NETNS(self), FALSE); + + return _nmp_netns_push_type(self, _CLONE_NS_ALL); +} + +gboolean +nmp_netns_push_type(NMPNetns *self, int ns_types) +{ + g_return_val_if_fail(NMP_IS_NETNS(self), FALSE); + g_return_val_if_fail(!NM_FLAGS_ANY(ns_types, ~_CLONE_NS_ALL), FALSE); + + return _nmp_netns_push_type(self, ns_types == 0 ? _CLONE_NS_ALL : ns_types); +} + +NMPNetns * +nmp_netns_new(void) +{ + GArray * netns_stack = _netns_stack_get(); + NMPNetns * self; + int errsv; + GError * error = NULL; + unsigned long mountflags = 0; + + if (!_stack_peek(netns_stack)) { + /* there are no netns instances. We cannot create a new one + * (because after unshare we couldn't return to the original one). */ + errno = ENOTSUP; + return NULL; + } + + if (unshare(_CLONE_NS_ALL) != 0) { + errsv = errno; + _LOGE(NULL, "failed to create new net and mnt namespace: %s", nm_strerror_native(errsv)); + return NULL; + } + + if (mount("", "/", "none", MS_SLAVE | MS_REC, NULL) != 0) { + errsv = errno; + _LOGE(NULL, "failed mount --make-rslave: %s", nm_strerror_native(errsv)); + goto err_out; + } + + if (umount2("/sys", MNT_DETACH) != 0) { + errsv = errno; + _LOGE(NULL, "failed umount /sys: %s", nm_strerror_native(errsv)); + goto err_out; + } + + if (access("/sys", W_OK) == -1) + mountflags = MS_RDONLY; + + if (mount("sysfs", "/sys", "sysfs", mountflags, NULL) != 0) { + errsv = errno; + _LOGE(NULL, "failed mount /sys: %s", nm_strerror_native(errsv)); + goto err_out; + } + + self = _netns_new(&error); + if (!self) { + errsv = errno; + _LOGE(NULL, "failed to create netns after unshare: %s", error->message); + g_clear_error(&error); + goto err_out; + } + + _stack_push(netns_stack, self, _CLONE_NS_ALL); + + return self; +err_out: + _netns_switch_pop(netns_stack, NULL, _CLONE_NS_ALL); + errno = errsv; + return NULL; +} + +gboolean +nmp_netns_pop(NMPNetns *self) +{ + GArray * netns_stack = _netns_stack_get(); + NetnsInfo *info; + int ns_types; + + g_return_val_if_fail(NMP_IS_NETNS(self), FALSE); + + info = _stack_peek(netns_stack); + + g_return_val_if_fail(info, FALSE); + g_return_val_if_fail(info->netns == self, FALSE); + + if (info->count > 1) { + info->count--; + _LOGt(self, "pop#%u* (decrease count to %d)", _stack_size(netns_stack) - 1, info->count); + return TRUE; + } + g_return_val_if_fail(info->count == 1, FALSE); + + /* cannot pop the original netns. */ + g_return_val_if_fail(_stack_size(netns_stack) > 1, FALSE); + + _LOGD(self, "pop#%u", _stack_size(netns_stack) - 1); + + ns_types = info->ns_types; + + _stack_pop(netns_stack); + + return _netns_switch_pop(netns_stack, self, ns_types); +} + +NMPNetns * +nmp_netns_get_current(void) +{ + return _netns_get(_stack_peek(_netns_stack_get())); +} + +NMPNetns * +nmp_netns_get_initial(void) +{ + return _netns_get(_stack_bottom(_netns_stack_get())); +} + +gboolean +nmp_netns_is_initial(void) +{ + GArray *netns_stack = _netns_stack_get(); + + return (_netns_get(_stack_peek(netns_stack)) == _netns_get(_stack_bottom(netns_stack))); +} + +/*****************************************************************************/ + +gboolean +nmp_netns_bind_to_path(NMPNetns *self, const char *filename, int *out_fd) +{ + gs_free char * dirname = NULL; + int errsv; + int fd; + nm_auto_pop_netns NMPNetns *netns_pop = NULL; + + g_return_val_if_fail(NMP_IS_NETNS(self), FALSE); + g_return_val_if_fail(filename && filename[0] == '/', FALSE); + + if (!nmp_netns_push_type(self, CLONE_NEWNET)) + return FALSE; + netns_pop = self; + + dirname = g_path_get_dirname(filename); + if (mkdir(dirname, 0) != 0) { + errsv = errno; + if (errsv != EEXIST) { + _LOGE(self, + "bind: failed to create directory %s: %s", + dirname, + nm_strerror_native(errsv)); + return FALSE; + } + } + + if ((fd = creat(filename, S_IRUSR | S_IRGRP | S_IROTH)) == -1) { + errsv = errno; + _LOGE(self, "bind: failed to create %s: %s", filename, nm_strerror_native(errsv)); + return FALSE; + } + nm_close(fd); + + if (mount(PROC_SELF_NS_NET, filename, "none", MS_BIND, NULL) != 0) { + errsv = errno; + _LOGE(self, + "bind: failed to mount %s to %s: %s", + PROC_SELF_NS_NET, + filename, + nm_strerror_native(errsv)); + unlink(filename); + return FALSE; + } + + if (out_fd) { + if ((fd = open(filename, O_RDONLY | O_CLOEXEC)) == -1) { + errsv = errno; + _LOGE(self, "bind: failed to open %s: %s", filename, nm_strerror_native(errsv)); + umount2(filename, MNT_DETACH); + unlink(filename); + return FALSE; + } + *out_fd = fd; + } + + return TRUE; +} + +gboolean +nmp_netns_bind_to_path_destroy(NMPNetns *self, const char *filename) +{ + int errsv; + + g_return_val_if_fail(NMP_IS_NETNS(self), FALSE); + g_return_val_if_fail(filename && filename[0] == '/', FALSE); + + if (umount2(filename, MNT_DETACH) != 0) { + errsv = errno; + _LOGE(self, "bind: failed to unmount2 %s: %s", filename, nm_strerror_native(errsv)); + return FALSE; + } + if (unlink(filename) != 0) { + errsv = errno; + _LOGE(self, "bind: failed to unlink %s: %s", filename, nm_strerror_native(errsv)); + return FALSE; + } + return TRUE; +} + +/*****************************************************************************/ + +static void +set_property(GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec) +{ + NMPNetns * self = NMP_NETNS(object); + NMPNetnsPrivate *priv = NMP_NETNS_GET_PRIVATE(self); + + switch (prop_id) { + case PROP_FD_NET: + /* construct-only */ + priv->fd_net = g_value_get_int(value); + g_return_if_fail(priv->fd_net > 0); + break; + case PROP_FD_MNT: + /* construct-only */ + priv->fd_mnt = g_value_get_int(value); + g_return_if_fail(priv->fd_mnt > 0); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec); + break; + } +} + +static void +nmp_netns_init(NMPNetns *self) +{} + +static void +dispose(GObject *object) +{ + NMPNetns * self = NMP_NETNS(object); + NMPNetnsPrivate *priv = NMP_NETNS_GET_PRIVATE(self); + + nm_close(priv->fd_net); + priv->fd_net = -1; + + nm_close(priv->fd_mnt); + priv->fd_mnt = -1; + + G_OBJECT_CLASS(nmp_netns_parent_class)->dispose(object); +} + +static void +nmp_netns_class_init(NMPNetnsClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS(klass); + + object_class->set_property = set_property; + object_class->dispose = dispose; + + obj_properties[PROP_FD_NET] = + g_param_spec_int(NMP_NETNS_FD_NET, + "", + "", + 0, + G_MAXINT, + 0, + G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS); + obj_properties[PROP_FD_MNT] = + g_param_spec_int(NMP_NETNS_FD_MNT, + "", + "", + 0, + G_MAXINT, + 0, + G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS); + g_object_class_install_properties(object_class, _PROPERTY_ENUMS_LAST, obj_properties); +} diff --git a/shared/nm-platform/nmp-netns.h b/shared/nm-platform/nmp-netns.h new file mode 100644 index 00000000..b18bd03e --- /dev/null +++ b/shared/nm-platform/nmp-netns.h @@ -0,0 +1,56 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright (C) 2016 Red Hat, Inc. + */ + +#ifndef __NMP_NETNS_UTILS_H__ +#define __NMP_NETNS_UTILS_H__ + +#include "nmp-base.h" + +/*****************************************************************************/ + +#define NMP_TYPE_NETNS (nmp_netns_get_type()) +#define NMP_NETNS(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), NMP_TYPE_NETNS, NMPNetns)) +#define NMP_NETNS_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), NMP_TYPE_NETNS, NMPNetnsClass)) +#define NMP_IS_NETNS(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), NMP_TYPE_NETNS)) +#define NMP_IS_NETNS_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), NMP_TYPE_NETNS)) +#define NMP_NETNS_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), NMP_TYPE_NETNS, NMPNetnsClass)) + +#define NMP_NETNS_FD_NET "fd-net" +#define NMP_NETNS_FD_MNT "fd-mnt" + +typedef struct _NMPNetns NMPNetns; +typedef struct _NMPNetnsClass NMPNetnsClass; + +GType nmp_netns_get_type(void); + +NMPNetns *nmp_netns_new(void); + +gboolean nmp_netns_push(NMPNetns *self); +gboolean nmp_netns_push_type(NMPNetns *self, int ns_types); +gboolean nmp_netns_pop(NMPNetns *self); + +NMPNetns *nmp_netns_get_current(void); +NMPNetns *nmp_netns_get_initial(void); +gboolean nmp_netns_is_initial(void); + +int nmp_netns_get_fd_net(NMPNetns *self); +int nmp_netns_get_fd_mnt(NMPNetns *self); + +static inline void +_nm_auto_pop_netns(NMPNetns **p) +{ + if (*p) { + int errsv = errno; + + nmp_netns_pop(*p); + errno = errsv; + } +} +#define nm_auto_pop_netns nm_auto(_nm_auto_pop_netns) + +gboolean nmp_netns_bind_to_path(NMPNetns *self, const char *filename, int *out_fd); +gboolean nmp_netns_bind_to_path_destroy(NMPNetns *self, const char *filename); + +#endif /* __NMP_NETNS_UTILS_H__ */ diff --git a/shared/nm-platform/tests/meson.build b/shared/nm-platform/tests/meson.build new file mode 100644 index 00000000..a8fcdbca --- /dev/null +++ b/shared/nm-platform/tests/meson.build @@ -0,0 +1,20 @@ +# SPDX-License-Identifier: LGPL-2.1-or-later + +exe = executable( + 'test-nm-platform', + 'test-nm-platform.c', + c_args: [ + '-DG_LOG_DOMAIN="test"', + ], + dependencies: [ + libnm_log_core_dep, + libnm_platform_dep, + ], +) + +test( + 'shared/nm-glib-aux/test-nm-platform', + test_script, + args: test_args + [exe.full_path()], + timeout: default_test_timeout, +) diff --git a/shared/nm-platform/tests/test-nm-platform.c b/shared/nm-platform/tests/test-nm-platform.c new file mode 100644 index 00000000..a3e9ff13 --- /dev/null +++ b/shared/nm-platform/tests/test-nm-platform.c @@ -0,0 +1,116 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ + +#include "nm-glib-aux/nm-default-glib-i18n-prog.h" + +#include "nm-log-core/nm-logging.h" +#include "nm-platform/nm-netlink.h" +#include "nm-platform/nmp-netns.h" + +#include "nm-utils/nm-test-utils.h" + +/*****************************************************************************/ + +void +_nm_logging_clear_platform_logging_cache(void) +{ + /* this symbols is required by nm-log-core library. */ +} + +/*****************************************************************************/ + +static void +test_use_symbols(void) +{ + static void (*const SYMBOLS[])(void) = { + (void (*)(void)) nl_nlmsghdr_to_str, + (void (*)(void)) nlmsg_hdr, + (void (*)(void)) nlmsg_reserve, + (void (*)(void)) nla_reserve, + (void (*)(void)) nlmsg_alloc_size, + (void (*)(void)) nlmsg_alloc, + (void (*)(void)) nlmsg_alloc_convert, + (void (*)(void)) nlmsg_alloc_simple, + (void (*)(void)) nlmsg_free, + (void (*)(void)) nlmsg_append, + (void (*)(void)) nlmsg_parse, + (void (*)(void)) nlmsg_put, + (void (*)(void)) nla_strlcpy, + (void (*)(void)) nla_memcpy, + (void (*)(void)) nla_put, + (void (*)(void)) nla_find, + (void (*)(void)) nla_nest_cancel, + (void (*)(void)) nla_nest_start, + (void (*)(void)) nla_nest_end, + (void (*)(void)) nla_parse, + (void (*)(void)) nlmsg_get_proto, + (void (*)(void)) nlmsg_set_proto, + (void (*)(void)) nlmsg_set_src, + (void (*)(void)) nlmsg_get_creds, + (void (*)(void)) nlmsg_set_creds, + (void (*)(void)) genlmsg_put, + (void (*)(void)) genlmsg_data, + (void (*)(void)) genlmsg_user_hdr, + (void (*)(void)) genlmsg_hdr, + (void (*)(void)) genlmsg_user_data, + (void (*)(void)) genlmsg_attrdata, + (void (*)(void)) genlmsg_len, + (void (*)(void)) genlmsg_attrlen, + (void (*)(void)) genlmsg_valid_hdr, + (void (*)(void)) genlmsg_parse, + (void (*)(void)) genl_ctrl_resolve, + (void (*)(void)) nl_socket_alloc, + (void (*)(void)) nl_socket_free, + (void (*)(void)) nl_socket_get_fd, + (void (*)(void)) nl_socket_get_local_port, + (void (*)(void)) nl_socket_get_msg_buf_size, + (void (*)(void)) nl_socket_set_passcred, + (void (*)(void)) nl_socket_set_msg_buf_size, + (void (*)(void)) nlmsg_get_dst, + (void (*)(void)) nl_socket_set_nonblocking, + (void (*)(void)) nl_socket_set_buffer_size, + (void (*)(void)) nl_socket_add_memberships, + (void (*)(void)) nl_socket_set_ext_ack, + (void (*)(void)) nl_socket_disable_msg_peek, + (void (*)(void)) nl_connect, + (void (*)(void)) nl_wait_for_ack, + (void (*)(void)) nl_recvmsgs, + (void (*)(void)) nl_sendmsg, + (void (*)(void)) nl_send_iovec, + (void (*)(void)) nl_complete_msg, + (void (*)(void)) nl_send, + (void (*)(void)) nl_send_auto, + (void (*)(void)) nl_recv, + + (void (*)(void)) nmp_netns_bind_to_path, + (void (*)(void)) nmp_netns_bind_to_path_destroy, + (void (*)(void)) nmp_netns_get_current, + (void (*)(void)) nmp_netns_get_fd_mnt, + (void (*)(void)) nmp_netns_get_fd_net, + (void (*)(void)) nmp_netns_get_initial, + (void (*)(void)) nmp_netns_is_initial, + (void (*)(void)) nmp_netns_new, + (void (*)(void)) nmp_netns_pop, + (void (*)(void)) nmp_netns_push, + (void (*)(void)) nmp_netns_push_type, + + NULL, + }; + + /* The only (not very exciting) purpose of this test is to see that + * we can use various symbols and don't get a linker error. */ + assert(G_N_ELEMENTS(SYMBOLS) == NM_PTRARRAY_LEN(SYMBOLS) + 1); +} + +/*****************************************************************************/ + +NMTST_DEFINE(); + +int +main(int argc, char **argv) +{ + nmtst_init(&argc, &argv, TRUE); + + g_test_add_func("/nm-platform/test_use_symbols", test_use_symbols); + + return g_test_run(); +} diff --git a/shared/nm-std-aux/c-list-util.c b/shared/nm-std-aux/c-list-util.c index a5837edd..d16bd6b7 100644 --- a/shared/nm-std-aux/c-list-util.c +++ b/shared/nm-std-aux/c-list-util.c @@ -1,4 +1,4 @@ -/* SPDX-License-Identifier: LGPL-2.1+ */ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ /* * Copyright (C) 2017 Red Hat, Inc. */ @@ -20,7 +20,7 @@ * pointer to @lst. * * The use of this function is to do a bulk update, that lets the - * list degredate by not updating the prev pointers. At the end, + * list degenerate by not updating the prev pointers. At the end, * the list can be fixed by c_list_relink(). */ void @@ -111,7 +111,8 @@ _c_list_sort(CList *ls, CListSortCmp cmp, const void *user_data) /* A simple top-down, non-recursive, stable merge-sort. * * Maybe natural merge-sort would be better, to do better for - * partially sorted lists. */ + * partially sorted lists. Doing that would be much more complicated, + * so it's not done. */ _split: stack_head[0].ls2 = _c_list_srt_split(stack_head[0].ls1); if (stack_head[0].ls2) { diff --git a/shared/nm-std-aux/c-list-util.h b/shared/nm-std-aux/c-list-util.h index 828481e1..9d86d663 100644 --- a/shared/nm-std-aux/c-list-util.h +++ b/shared/nm-std-aux/c-list-util.h @@ -1,4 +1,4 @@ -/* SPDX-License-Identifier: LGPL-2.1+ */ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ /* * Copyright (C) 2017 Red Hat, Inc. */ @@ -42,4 +42,15 @@ c_list_length_is(const CList *list, unsigned long check_len) return n == check_len; } +#define c_list_for_each_prev(_iter, _list) \ + for (_iter = (_list)->prev; (_iter) != (_list); _iter = (_iter)->prev) + +#define c_list_for_each_prev_safe(_iter, _safe, _list) \ + for (_iter = (_list)->prev, _safe = (_iter)->prev; (_iter) != (_list); \ + _iter = (_safe), _safe = (_safe)->prev) + +#define c_list_for_each_entry_prev(_iter, _list, _m) \ + for (_iter = c_list_entry((_list)->prev, __typeof__(*_iter), _m); &(_iter)->_m != (_list); \ + _iter = c_list_entry((_iter)->_m.prev, __typeof__(*_iter), _m)) + #endif /* __C_LIST_UTIL_H__ */ diff --git a/shared/nm-std-aux/nm-dbus-compat.h b/shared/nm-std-aux/nm-dbus-compat.h index 4100e343..c1076200 100644 --- a/shared/nm-std-aux/nm-dbus-compat.h +++ b/shared/nm-std-aux/nm-dbus-compat.h @@ -1,4 +1,4 @@ -/* SPDX-License-Identifier: LGPL-2.1+ */ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ #ifndef __NM_DBUS_COMPAT_H__ #define __NM_DBUS_COMPAT_H__ diff --git a/shared/nm-std-aux/nm-default-std.h b/shared/nm-std-aux/nm-default-std.h new file mode 100644 index 00000000..fe16d35f --- /dev/null +++ b/shared/nm-std-aux/nm-default-std.h @@ -0,0 +1,102 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +/* + * Copyright (C) 2015 Red Hat, Inc. + */ + +#ifndef __NM_DEFAULT_STD_H__ +#define __NM_DEFAULT_STD_H__ + +#include "nm-networkmanager-compilation.h" + +#ifdef NETWORKMANAGER_COMPILATION + #error Dont define NETWORKMANAGER_COMPILATION +#endif + +#ifndef G_LOG_DOMAIN + #error Define G_LOG_DOMAIN +#endif + +/*****************************************************************************/ + +#define NETWORKMANAGER_COMPILATION 0 + +/*****************************************************************************/ + +/* always include these headers for our internal source files. */ + +#ifndef ___CONFIG_H__ + #define ___CONFIG_H__ + #include <config.h> +#endif + +#include "config-extra.h" + +/* for internal compilation we don't want the deprecation macros + * to be in effect. Define the widest range of versions to effectively + * disable deprecation checks */ +#define NM_VERSION_MIN_REQUIRED NM_VERSION_0_9_8 + +#ifndef NM_MORE_ASSERTS + #define NM_MORE_ASSERTS 0 +#endif + +#if NM_MORE_ASSERTS == 0 + /* The cast macros like NM_TYPE() are implemented via G_TYPE_CHECK_INSTANCE_CAST() + * and _G_TYPE_CIC(). The latter, by default performs runtime checks of the type + * by calling g_type_check_instance_cast(). + * This check has a certain overhead without being helpful. + * + * Example 1: + * static void foo (NMType *obj) + * { + * access_obj_without_check (obj); + * } + * foo ((NMType *) obj); + * // There is no runtime check and passing an invalid pointer + * // leads to a crash. + * + * Example 2: + * static void foo (NMType *obj) + * { + * access_obj_without_check (obj); + * } + * foo (NM_TYPE (obj)); + * // There is a runtime check which prints a g_warning(), but that doesn't + * // avoid the crash as NM_TYPE() cannot do anything then passing on the + * // invalid pointer. + * + * Example 3: + * static void foo (NMType *obj) + * { + * g_return_if_fail (NM_IS_TYPE (obj)); + * access_obj_without_check (obj); + * } + * foo ((NMType *) obj); + * // There is a runtime check which prints a g_critical() which also avoids + * // the crash. That is actually helpful to catch bugs and avoid crashes. + * + * Example 4: + * static void foo (NMType *obj) + * { + * g_return_if_fail (NM_IS_TYPE (obj)); + * access_obj_without_check (obj); + * } + * foo (NM_TYPE (obj)); + * // The runtime check is performed twice, with printing a g_warning() and + * // a g_critical() and avoiding the crash. + * + * Example 3 is how it should be done. Type checks in NM_TYPE() are pointless. + * Disable them for our production builds. + */ + #ifndef G_DISABLE_CAST_CHECKS + #define G_DISABLE_CAST_CHECKS + #endif +#endif + +/*****************************************************************************/ + +#include <stdlib.h> + +/*****************************************************************************/ + +#endif /* __NM_DEFAULT_STD_H__ */ diff --git a/shared/nm-std-aux/nm-networkmanager-compilation.h b/shared/nm-std-aux/nm-networkmanager-compilation.h new file mode 100644 index 00000000..025a158d --- /dev/null +++ b/shared/nm-std-aux/nm-networkmanager-compilation.h @@ -0,0 +1,54 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +/* + * Copyright (C) 2015 Red Hat, Inc. + */ + +#ifndef __NM_NETWORKMANAGER_COMPILATION_H__ +#define __NM_NETWORKMANAGER_COMPILATION_H__ + +#define NM_NETWORKMANAGER_COMPILATION_WITH_GLIB (1 << 0) +#define NM_NETWORKMANAGER_COMPILATION_WITH_GLIB_I18N_LIB (1 << 1) +#define NM_NETWORKMANAGER_COMPILATION_WITH_GLIB_I18N_PROG (1 << 2) +#define NM_NETWORKMANAGER_COMPILATION_WITH_LIBNM (1 << 3) +#define NM_NETWORKMANAGER_COMPILATION_WITH_LIBNM_PRIVATE (1 << 4) +#define NM_NETWORKMANAGER_COMPILATION_WITH_LIBNM_CORE (1 << 5) +#define NM_NETWORKMANAGER_COMPILATION_WITH_LIBNM_CORE_INTERNAL (1 << 6) +#define NM_NETWORKMANAGER_COMPILATION_WITH_LIBNM_CORE_PRIVATE (1 << 7) +#define NM_NETWORKMANAGER_COMPILATION_WITH_DAEMON (1 << 10) +#define NM_NETWORKMANAGER_COMPILATION_WITH_SYSTEMD (1 << 11) + +#define NM_NETWORKMANAGER_COMPILATION_LIBNM_CORE \ + (0 | NM_NETWORKMANAGER_COMPILATION_WITH_GLIB \ + | NM_NETWORKMANAGER_COMPILATION_WITH_GLIB_I18N_LIB \ + | NM_NETWORKMANAGER_COMPILATION_WITH_LIBNM_CORE \ + | NM_NETWORKMANAGER_COMPILATION_WITH_LIBNM_CORE_PRIVATE \ + | NM_NETWORKMANAGER_COMPILATION_WITH_LIBNM_CORE_INTERNAL) + +#define NM_NETWORKMANAGER_COMPILATION_LIBNM \ + (0 | NM_NETWORKMANAGER_COMPILATION_WITH_GLIB \ + | NM_NETWORKMANAGER_COMPILATION_WITH_GLIB_I18N_LIB | NM_NETWORKMANAGER_COMPILATION_WITH_LIBNM \ + | NM_NETWORKMANAGER_COMPILATION_WITH_LIBNM_PRIVATE \ + | NM_NETWORKMANAGER_COMPILATION_WITH_LIBNM_CORE \ + | NM_NETWORKMANAGER_COMPILATION_WITH_LIBNM_CORE_INTERNAL) + +#define NM_NETWORKMANAGER_COMPILATION_CLIENT \ + (0 | NM_NETWORKMANAGER_COMPILATION_WITH_GLIB \ + | NM_NETWORKMANAGER_COMPILATION_WITH_GLIB_I18N_PROG \ + | NM_NETWORKMANAGER_COMPILATION_WITH_LIBNM | NM_NETWORKMANAGER_COMPILATION_WITH_LIBNM_CORE) + +#define NM_NETWORKMANAGER_COMPILATION_DAEMON \ + (0 | NM_NETWORKMANAGER_COMPILATION_WITH_GLIB \ + | NM_NETWORKMANAGER_COMPILATION_WITH_GLIB_I18N_PROG \ + | NM_NETWORKMANAGER_COMPILATION_WITH_LIBNM_CORE \ + | NM_NETWORKMANAGER_COMPILATION_WITH_LIBNM_CORE_INTERNAL \ + | NM_NETWORKMANAGER_COMPILATION_WITH_DAEMON) + +#define NM_NETWORKMANAGER_COMPILATION_SYSTEMD_SHARED \ + (0 | NM_NETWORKMANAGER_COMPILATION_WITH_GLIB | NM_NETWORKMANAGER_COMPILATION_WITH_SYSTEMD) + +#define NM_NETWORKMANAGER_COMPILATION_SYSTEMD \ + (0 | NM_NETWORKMANAGER_COMPILATION_DAEMON | NM_NETWORKMANAGER_COMPILATION_SYSTEMD_SHARED) + +#define NM_NETWORKMANAGER_COMPILATION_GLIB (0 | NM_NETWORKMANAGER_COMPILATION_WITH_GLIB) + +#endif /* __NM_NETWORKMANAGER_COMPILATION_H__ */ diff --git a/shared/nm-std-aux/nm-std-aux.h b/shared/nm-std-aux/nm-std-aux.h index 762f104b..30adeb58 100644 --- a/shared/nm-std-aux/nm-std-aux.h +++ b/shared/nm-std-aux/nm-std-aux.h @@ -1,4 +1,4 @@ -/* SPDX-License-Identifier: LGPL-2.1+ */ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ #ifndef __NM_STD_AUX_H__ #define __NM_STD_AUX_H__ @@ -189,6 +189,37 @@ typedef uint64_t _nm_bitwise nm_be64_t; /*****************************************************************************/ +static inline uint32_t +nm_add_clamped_u32(uint32_t a, uint32_t b) +{ + uint32_t c; + + /* returns a+b, or UINT32_MAX if the result would overflow. */ + + c = a + b; + if (c < a) + return UINT32_MAX; + return c; +} + +static inline unsigned +nm_mult_clamped_u(unsigned a, unsigned b) +{ + unsigned c; + + /* returns a*b, or UINT_MAX if the result would overflow. */ + + if (b == 0) + return 0; + + c = a * b; + + if (c / b != a) + return (unsigned) -1; + + return c; +} + /* glib's MIN()/MAX() macros don't have function-like behavior, in that they evaluate * the argument possibly twice. * @@ -767,6 +798,21 @@ nm_steal_fd(int *p_fd) /*****************************************************************************/ +static inline uintptr_t +nm_ptr_to_uintptr(const void *p) +{ + /* in C, pointers can only be compared (with less-than or greater-than) under certain + * circumstances. Since uintptr_t is supposed to be able to represent the pointer + * as a plain integer and also support to convert the integer back to the pointer, + * it should be safer to compare the pointers directly. + * + * Of course, this function isn't very useful beyond that its use makes it clear + * that we want to compare pointers by value, which otherwise may not be valid. */ + return (uintptr_t) p; +} + +/*****************************************************************************/ + #define NM_CMP_RETURN(c) \ do { \ const int _cc = (c); \ @@ -813,7 +859,7 @@ nm_steal_fd(int *p_fd) * Avoid that by casting pointers to void* and then to uintptr_t. This comparison * is not really meaningful, except that it provides some kind of stable sort order * between pointers (that can otherwise not be compared). */ -#define NM_CMP_DIRECT_PTR(a, b) NM_CMP_DIRECT((uintptr_t)((void *) (a)), (uintptr_t)((void *) (b))) +#define NM_CMP_DIRECT_PTR(a, b) NM_CMP_DIRECT(nm_ptr_to_uintptr(a), nm_ptr_to_uintptr(b)) #define NM_CMP_DIRECT_MEMCMP(a, b, size) NM_CMP_RETURN(memcmp((a), (b), (size))) diff --git a/shared/nm-std-aux/nm-std-utils.c b/shared/nm-std-aux/nm-std-utils.c index 6f7f4c58..18692b19 100644 --- a/shared/nm-std-aux/nm-std-utils.c +++ b/shared/nm-std-aux/nm-std-utils.c @@ -1,10 +1,12 @@ -/* SPDX-License-Identifier: LGPL-2.1+ */ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ -#include "nm-default.h" +#include "nm-default-std.h" #include "nm-std-utils.h" #include <stdint.h> +#include <assert.h> +#include <limits.h> /*****************************************************************************/ @@ -50,7 +52,7 @@ nm_utils_get_next_realloc_size(bool true_realloc, size_t requested) * We get thus sizes of 104, 232, 488, 1000, 2024, 4072, 8168... */ if (NM_UNLIKELY(requested > SIZE_MAX / 2u - 24u)) - return SIZE_MAX; + goto out_huge; x = requested + 24u; n = 128u; @@ -63,8 +65,10 @@ nm_utils_get_next_realloc_size(bool true_realloc, size_t requested) return n - 24u; } - if (NM_UNLIKELY(requested > SIZE_MAX - 0x1000u - 24u)) - return SIZE_MAX; + if (NM_UNLIKELY(requested > SIZE_MAX - 0x1000u - 24u)) { + /* overflow happened. */ + goto out_huge; + } /* For large allocations (with !true_realloc) we allocate memory in chunks of * 4K (- 24 bytes extra), assuming that the memory gets mmapped and thus @@ -72,4 +76,15 @@ nm_utils_get_next_realloc_size(bool true_realloc, size_t requested) n = ((requested + (0x0FFFu + 24u)) & ~((size_t) 0x0FFFu)) - 24u; nm_assert(n >= requested); return n; + +out_huge: + if (sizeof(size_t) > 4u) { + /* on s390x (64 bit), gcc with LTO can complain that the size argument to + * malloc must not be larger than 9223372036854775807. + * + * Work around that by returning SSIZE_MAX. It should be plenty still! */ + assert(requested <= (size_t) SSIZE_MAX); + return (size_t) SSIZE_MAX; + } + return SIZE_MAX; } diff --git a/shared/nm-std-aux/nm-std-utils.h b/shared/nm-std-aux/nm-std-utils.h index 1f908191..9c851f1f 100644 --- a/shared/nm-std-aux/nm-std-utils.h +++ b/shared/nm-std-aux/nm-std-utils.h @@ -1,4 +1,4 @@ -/* SPDX-License-Identifier: LGPL-2.1+ */ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ #ifndef __NM_STD_UTILS_H__ #define __NM_STD_UTILS_H__ diff --git a/shared/nm-std-aux/unaligned.h b/shared/nm-std-aux/unaligned.h index e0bc1043..4100be08 100644 --- a/shared/nm-std-aux/unaligned.h +++ b/shared/nm-std-aux/unaligned.h @@ -1,4 +1,4 @@ -/* SPDX-License-Identifier: LGPL-2.1+ */ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ #pragma once #include <endian.h> @@ -6,142 +6,94 @@ /* BE */ -static inline uint16_t -unaligned_read_be16(const void *_u) -{ - const struct __attribute__((__packed__, __may_alias__)) { - uint16_t x; - } *u = _u; +static inline uint16_t unaligned_read_be16(const void *_u) { + const struct __attribute__((__packed__, __may_alias__)) { uint16_t x; } *u = _u; - return be16toh(u->x); + return be16toh(u->x); } -static inline uint32_t -unaligned_read_be32(const void *_u) -{ - const struct __attribute__((__packed__, __may_alias__)) { - uint32_t x; - } *u = _u; +static inline uint32_t unaligned_read_be32(const void *_u) { + const struct __attribute__((__packed__, __may_alias__)) { uint32_t x; } *u = _u; - return be32toh(u->x); + return be32toh(u->x); } -static inline uint64_t -unaligned_read_be64(const void *_u) -{ - const struct __attribute__((__packed__, __may_alias__)) { - uint64_t x; - } *u = _u; +static inline uint64_t unaligned_read_be64(const void *_u) { + const struct __attribute__((__packed__, __may_alias__)) { uint64_t x; } *u = _u; - return be64toh(u->x); + return be64toh(u->x); } -static inline void -unaligned_write_be16(void *_u, uint16_t a) -{ - struct __attribute__((__packed__, __may_alias__)) { - uint16_t x; - } *u = _u; +static inline void unaligned_write_be16(void *_u, uint16_t a) { + struct __attribute__((__packed__, __may_alias__)) { uint16_t x; } *u = _u; - u->x = be16toh(a); + u->x = be16toh(a); } -static inline void -unaligned_write_be32(void *_u, uint32_t a) -{ - struct __attribute__((__packed__, __may_alias__)) { - uint32_t x; - } *u = _u; +static inline void unaligned_write_be32(void *_u, uint32_t a) { + struct __attribute__((__packed__, __may_alias__)) { uint32_t x; } *u = _u; - u->x = be32toh(a); + u->x = be32toh(a); } -static inline void -unaligned_write_be64(void *_u, uint64_t a) -{ - struct __attribute__((__packed__, __may_alias__)) { - uint64_t x; - } *u = _u; +static inline void unaligned_write_be64(void *_u, uint64_t a) { + struct __attribute__((__packed__, __may_alias__)) { uint64_t x; } *u = _u; - u->x = be64toh(a); + u->x = be64toh(a); } /* LE */ -static inline uint16_t -unaligned_read_le16(const void *_u) -{ - const struct __attribute__((__packed__, __may_alias__)) { - uint16_t x; - } *u = _u; +static inline uint16_t unaligned_read_le16(const void *_u) { + const struct __attribute__((__packed__, __may_alias__)) { uint16_t x; } *u = _u; - return le16toh(u->x); + return le16toh(u->x); } -static inline uint32_t -unaligned_read_le32(const void *_u) -{ - const struct __attribute__((__packed__, __may_alias__)) { - uint32_t x; - } *u = _u; +static inline uint32_t unaligned_read_le32(const void *_u) { + const struct __attribute__((__packed__, __may_alias__)) { uint32_t x; } *u = _u; - return le32toh(u->x); + return le32toh(u->x); } -static inline uint64_t -unaligned_read_le64(const void *_u) -{ - const struct __attribute__((__packed__, __may_alias__)) { - uint64_t x; - } *u = _u; +static inline uint64_t unaligned_read_le64(const void *_u) { + const struct __attribute__((__packed__, __may_alias__)) { uint64_t x; } *u = _u; - return le64toh(u->x); + return le64toh(u->x); } -static inline void -unaligned_write_le16(void *_u, uint16_t a) -{ - struct __attribute__((__packed__, __may_alias__)) { - uint16_t x; - } *u = _u; +static inline void unaligned_write_le16(void *_u, uint16_t a) { + struct __attribute__((__packed__, __may_alias__)) { uint16_t x; } *u = _u; - u->x = le16toh(a); + u->x = le16toh(a); } -static inline void -unaligned_write_le32(void *_u, uint32_t a) -{ - struct __attribute__((__packed__, __may_alias__)) { - uint32_t x; - } *u = _u; +static inline void unaligned_write_le32(void *_u, uint32_t a) { + struct __attribute__((__packed__, __may_alias__)) { uint32_t x; } *u = _u; - u->x = le32toh(a); + u->x = le32toh(a); } -static inline void -unaligned_write_le64(void *_u, uint64_t a) -{ - struct __attribute__((__packed__, __may_alias__)) { - uint64_t x; - } *u = _u; +static inline void unaligned_write_le64(void *_u, uint64_t a) { + struct __attribute__((__packed__, __may_alias__)) { uint64_t x; } *u = _u; - u->x = le64toh(a); + u->x = le64toh(a); } #if __BYTE_ORDER == __BIG_ENDIAN - #define unaligned_read_ne16 unaligned_read_be16 - #define unaligned_read_ne32 unaligned_read_be32 - #define unaligned_read_ne64 unaligned_read_be64 +#define unaligned_read_ne16 unaligned_read_be16 +#define unaligned_read_ne32 unaligned_read_be32 +#define unaligned_read_ne64 unaligned_read_be64 - #define unaligned_write_ne16 unaligned_write_be16 - #define unaligned_write_ne32 unaligned_write_be32 - #define unaligned_write_ne64 unaligned_write_be64 +#define unaligned_write_ne16 unaligned_write_be16 +#define unaligned_write_ne32 unaligned_write_be32 +#define unaligned_write_ne64 unaligned_write_be64 #else - #define unaligned_read_ne16 unaligned_read_le16 - #define unaligned_read_ne32 unaligned_read_le32 - #define unaligned_read_ne64 unaligned_read_le64 +#define unaligned_read_ne16 unaligned_read_le16 +#define unaligned_read_ne32 unaligned_read_le32 +#define unaligned_read_ne64 unaligned_read_le64 - #define unaligned_write_ne16 unaligned_write_le16 - #define unaligned_write_ne32 unaligned_write_le32 - #define unaligned_write_ne64 unaligned_write_le64 +#define unaligned_write_ne16 unaligned_write_le16 +#define unaligned_write_ne32 unaligned_write_le32 +#define unaligned_write_ne64 unaligned_write_le64 #endif diff --git a/shared/nm-test-libnm-utils.h b/shared/nm-test-libnm-utils.h index b44db961..cac54076 100644 --- a/shared/nm-test-libnm-utils.h +++ b/shared/nm-test-libnm-utils.h @@ -1,4 +1,4 @@ -/* SPDX-License-Identifier: LGPL-2.1+ */ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ /* * Copyright (C) 2014 - 2015 Red Hat, Inc. */ diff --git a/shared/nm-test-utils-impl.c b/shared/nm-test-utils-impl.c index 57f30ea9..f8f98d81 100644 --- a/shared/nm-test-utils-impl.c +++ b/shared/nm-test-utils-impl.c @@ -1,9 +1,9 @@ -/* SPDX-License-Identifier: GPL-2.0+ */ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ /* * Copyright (C) 2010 - 2015 Red Hat, Inc. */ -#include "nm-default.h" +#include "libnm/nm-default-libnm.h" #include <sys/wait.h> diff --git a/shared/nm-udev-aux/nm-udev-utils.c b/shared/nm-udev-aux/nm-udev-utils.c index 8131c04e..0b941dff 100644 --- a/shared/nm-udev-aux/nm-udev-utils.c +++ b/shared/nm-udev-aux/nm-udev-utils.c @@ -1,9 +1,9 @@ -/* SPDX-License-Identifier: GPL-2.0+ */ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ /* * Copyright (C) 2017 Red Hat, Inc. */ -#include "nm-default.h" +#include "nm-glib-aux/nm-default-glib-i18n-lib.h" #include "nm-udev-utils.h" @@ -241,11 +241,11 @@ nm_udev_client_new(const char *const *subsystems, return self; fail: - return nm_udev_client_unref(self); + return nm_udev_client_destroy(self); } NMUdevClient * -nm_udev_client_unref(NMUdevClient *self) +nm_udev_client_destroy(NMUdevClient *self) { if (!self) return NULL; diff --git a/shared/nm-udev-aux/nm-udev-utils.h b/shared/nm-udev-aux/nm-udev-utils.h index f24659eb..191f9a89 100644 --- a/shared/nm-udev-aux/nm-udev-utils.h +++ b/shared/nm-udev-aux/nm-udev-utils.h @@ -1,4 +1,4 @@ -/* SPDX-License-Identifier: GPL-2.0+ */ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ /* * Copyright (C) 2017 Red Hat, Inc. */ @@ -24,7 +24,7 @@ NMUdevClient *nm_udev_client_new(const char *const *subsystems, NMUdevClientEvent event_handler, gpointer event_user_data); -NMUdevClient *nm_udev_client_unref(NMUdevClient *self); +NMUdevClient *nm_udev_client_destroy(NMUdevClient *self); struct udev *nm_udev_client_get_udev(NMUdevClient *self); diff --git a/shared/nm-utils/nm-compat.c b/shared/nm-utils/nm-compat.c index f87ef6b9..dad48227 100644 --- a/shared/nm-utils/nm-compat.c +++ b/shared/nm-utils/nm-compat.c @@ -1,9 +1,9 @@ -/* SPDX-License-Identifier: LGPL-2.1+ */ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ /* * Copyright (C) 2017 Red Hat, Inc. */ -#include "nm-default.h" +#include "libnm/nm-default-libnm.h" #include "nm-compat.h" diff --git a/shared/nm-utils/nm-compat.h b/shared/nm-utils/nm-compat.h index fb136a5b..559afd5d 100644 --- a/shared/nm-utils/nm-compat.h +++ b/shared/nm-utils/nm-compat.h @@ -1,4 +1,4 @@ -/* SPDX-License-Identifier: LGPL-2.1+ */ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ /* * Copyright (C) 2017 Red Hat, Inc. */ diff --git a/shared/nm-utils/nm-test-utils.h b/shared/nm-utils/nm-test-utils.h index 62d608c6..6b41c11e 100644 --- a/shared/nm-utils/nm-test-utils.h +++ b/shared/nm-utils/nm-test-utils.h @@ -1,4 +1,4 @@ -/* SPDX-License-Identifier: LGPL-2.1+ */ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ /* * Copyright (C) 2014 Red Hat, Inc. */ @@ -6,10 +6,6 @@ #ifndef __NM_TEST_UTILS_H__ #define __NM_TEST_UTILS_H__ -#if defined(NETWORKMANAGER_COMPILATION) && !defined(NETWORKMANAGER_COMPILATION_TEST) - #error Need to mark the compilation with NETWORKMANAGER_COMPILATION_TEST. -#endif - /******************************************************************************* * HOWTO run tests. * @@ -84,8 +80,6 @@ * *******************************************************************************/ -#include "nm-default.h" - #if defined(NM_ASSERT_NO_MSG) && NM_ASSERT_NO_MSG #undef g_return_if_fail_warning #undef g_assertion_message_expr @@ -98,10 +92,6 @@ #include <string.h> #include <errno.h> -#ifndef NM_TEST_UTILS_NO_LIBNM - #include "nm-utils.h" -#endif - /*****************************************************************************/ #define NMTST_G_RETURN_MSG_S(expr) "*: assertion '" NM_ASSERT_G_RETURN_EXPR(expr) "' failed" @@ -900,14 +890,16 @@ nmtst_get_rand_uint64(void) static inline guint nmtst_get_rand_uint(void) { - G_STATIC_ASSERT_EXPR(sizeof(guint32) == sizeof(guint)); - return nmtst_get_rand_uint32(); + G_STATIC_ASSERT_EXPR((sizeof(guint) == sizeof(guint32) || (sizeof(guint) == sizeof(guint64)))); + if (sizeof(guint32) == sizeof(guint)) + return nmtst_get_rand_uint32(); + return nmtst_get_rand_uint64(); } static inline gsize nmtst_get_rand_size(void) { - G_STATIC_ASSERT_EXPR(sizeof(gsize) == sizeof(guint32) || sizeof(gsize) == sizeof(guint64)); + G_STATIC_ASSERT_EXPR((sizeof(gsize) == sizeof(guint32) || (sizeof(gsize) == sizeof(guint64)))); if (sizeof(gsize) == sizeof(guint32)) return nmtst_get_rand_uint32(); return nmtst_get_rand_uint64(); @@ -919,6 +911,17 @@ nmtst_get_rand_bool(void) return nmtst_get_rand_uint32() % 2; } +static inline gboolean +nmtst_get_rand_one_case_in(guint32 num) +{ + /* num=1 doesn't make much sense, because it will always return %TRUE. + * Still accept it, it might be that @num is calculated, so 1 might be + * a valid edge case. */ + g_assert(num > 0); + + return (nmtst_get_rand_uint32() % num) == 0; +} + static inline gpointer nmtst_rand_buf(GRand *rand, gpointer buffer, gsize buffer_length) { @@ -998,6 +1001,23 @@ nmtst_rand_perm(GRand *rand, void *dst, const void *src, gsize elmt_size, gsize return dst; } +static inline const char ** +nmtst_rand_perm_strv(const char *const *strv) +{ + const char **res; + gsize n; + + if (!strv) + return NULL; + + /* this returns a (scrambled) SHALLOW copy of the strv array! */ + + n = NM_PTRARRAY_LEN(strv); + res = (const char **) (nm_utils_strv_dup(strv, n, FALSE) ?: g_new0(char *, 1)); + nmtst_rand_perm(NULL, res, res, sizeof(char *), n); + return res; +} + static inline GSList * nmtst_rand_perm_gslist(GRand *rand, GSList *list) { @@ -1181,6 +1201,14 @@ nmtst_main_loop_run(GMainLoop *loop, guint timeout_msec) return loopx != NULL; } +#define nmtst_main_loop_run_assert(loop, timeout_msec) \ + G_STMT_START \ + { \ + if (!nmtst_main_loop_run((loop), (timeout_msec))) \ + g_assert_not_reached(); \ + } \ + G_STMT_END + static inline void _nmtst_main_loop_quit_on_notify(GObject *object, GParamSpec *pspec, gpointer user_data) { @@ -2751,4 +2779,29 @@ nmtst_keyfile_get_num_keys(GKeyFile *keyfile, const char *group_name) /*****************************************************************************/ +#if defined(NM_SETTING_IP_CONFIG_H) && defined(__NM_SHARED_UTILS_H__) + +static inline NMIPAddress * +nmtst_ip_address_new(int addr_family, const char *str) +{ + NMIPAddr addr; + int plen; + GError * error = NULL; + NMIPAddress *a; + + if (!nm_utils_parse_inaddr_prefix_bin(addr_family, str, &addr_family, &addr, &plen)) + g_assert_not_reached(); + + if (plen == -1) + plen = addr_family == AF_INET ? 32 : 128; + + a = nm_ip_address_new_binary(addr_family, &addr, plen, &error); + nmtst_assert_success(a, error); + return a; +} + +#endif + +/*****************************************************************************/ + #endif /* __NM_TEST_UTILS_H__ */ diff --git a/shared/nm-utils/nm-vpn-editor-plugin-call.h b/shared/nm-utils/nm-vpn-editor-plugin-call.h index 780ef193..5772b843 100644 --- a/shared/nm-utils/nm-vpn-editor-plugin-call.h +++ b/shared/nm-utils/nm-vpn-editor-plugin-call.h @@ -1,4 +1,4 @@ -/* SPDX-License-Identifier: LGPL-2.1+ */ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ /* * Copyright (C) 2016 Red Hat, Inc. */ diff --git a/shared/nm-utils/nm-vpn-plugin-macros.h b/shared/nm-utils/nm-vpn-plugin-macros.h index 2fec94e5..4ca22427 100644 --- a/shared/nm-utils/nm-vpn-plugin-macros.h +++ b/shared/nm-utils/nm-vpn-plugin-macros.h @@ -1,4 +1,4 @@ -/* SPDX-License-Identifier: LGPL-2.1+ */ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ /* * Copyright (C) 2016 Red Hat, Inc. */ diff --git a/shared/nm-utils/nm-vpn-plugin-utils.c b/shared/nm-utils/nm-vpn-plugin-utils.c index 1af640da..89285ecd 100644 --- a/shared/nm-utils/nm-vpn-plugin-utils.c +++ b/shared/nm-utils/nm-vpn-plugin-utils.c @@ -1,9 +1,9 @@ -/* SPDX-License-Identifier: LGPL-2.1+ */ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ /* * Copyright (C) 2016, 2018 Red Hat, Inc. */ -#include "nm-default.h" +#include "libnm/nm-default-client.h" #include "nm-vpn-plugin-utils.h" diff --git a/shared/nm-utils/nm-vpn-plugin-utils.h b/shared/nm-utils/nm-vpn-plugin-utils.h index 0e5e1dab..881a368a 100644 --- a/shared/nm-utils/nm-vpn-plugin-utils.h +++ b/shared/nm-utils/nm-vpn-plugin-utils.h @@ -1,4 +1,4 @@ -/* SPDX-License-Identifier: LGPL-2.1+ */ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ /* * Copyright (C) 2016 Red Hat, Inc. */ diff --git a/shared/nm-version-macros.h b/shared/nm-version-macros.h deleted file mode 100644 index bbe82a7d..00000000 --- a/shared/nm-version-macros.h +++ /dev/null @@ -1,90 +0,0 @@ -// SPDX-License-Identifier: LGPL-2.1+ -/* - * Copyright (C) 2011, 2015 Red Hat, Inc. - */ - -#ifndef __NM_VERSION_MACROS_H__ -#define __NM_VERSION_MACROS_H__ - -/* This header must not include glib or libnm. */ - -/** - * NM_MAJOR_VERSION: - * - * Evaluates to the major version number of NetworkManager which this source - * is compiled against. - */ -#define NM_MAJOR_VERSION (1) - -/** - * NM_MINOR_VERSION: - * - * Evaluates to the minor version number of NetworkManager which this source - * is compiled against. - */ -#define NM_MINOR_VERSION (28) - -/** - * NM_MICRO_VERSION: - * - * Evaluates to the micro version number of NetworkManager which this source - * compiled against. - */ -#define NM_MICRO_VERSION (0) - -/** - * NM_CHECK_VERSION: - * @major: major version (e.g. 1 for version 1.2.5) - * @minor: minor version (e.g. 2 for version 1.2.5) - * @micro: micro version (e.g. 5 for version 1.2.5) - * - * Returns: %TRUE if the version of the NetworkManager header files - * is the same as or newer than the passed-in version. - */ -#define NM_CHECK_VERSION(major,minor,micro) \ - (NM_MAJOR_VERSION > (major) || \ - (NM_MAJOR_VERSION == (major) && NM_MINOR_VERSION > (minor)) || \ - (NM_MAJOR_VERSION == (major) && NM_MINOR_VERSION == (minor) && NM_MICRO_VERSION >= (micro))) - - -#define NM_ENCODE_VERSION(major,minor,micro) ((major) << 16 | (minor) << 8 | (micro)) - -#define NM_VERSION_0_9_8 (NM_ENCODE_VERSION (0, 9, 8)) -#define NM_VERSION_0_9_10 (NM_ENCODE_VERSION (0, 9, 10)) -#define NM_VERSION_1_0 (NM_ENCODE_VERSION (1, 0, 0)) -#define NM_VERSION_1_2 (NM_ENCODE_VERSION (1, 2, 0)) -#define NM_VERSION_1_4 (NM_ENCODE_VERSION (1, 4, 0)) -#define NM_VERSION_1_6 (NM_ENCODE_VERSION (1, 6, 0)) -#define NM_VERSION_1_8 (NM_ENCODE_VERSION (1, 8, 0)) -#define NM_VERSION_1_10 (NM_ENCODE_VERSION (1, 10, 0)) -#define NM_VERSION_1_12 (NM_ENCODE_VERSION (1, 12, 0)) -#define NM_VERSION_1_14 (NM_ENCODE_VERSION (1, 14, 0)) -#define NM_VERSION_1_16 (NM_ENCODE_VERSION (1, 16, 0)) -#define NM_VERSION_1_18 (NM_ENCODE_VERSION (1, 18, 0)) -#define NM_VERSION_1_20 (NM_ENCODE_VERSION (1, 20, 0)) -#define NM_VERSION_1_22 (NM_ENCODE_VERSION (1, 22, 0)) -#define NM_VERSION_1_24 (NM_ENCODE_VERSION (1, 24, 0)) -#define NM_VERSION_1_26 (NM_ENCODE_VERSION (1, 26, 0)) -#define NM_VERSION_1_28 (NM_ENCODE_VERSION (1, 28, 0)) - -/* For releases, NM_API_VERSION is equal to NM_VERSION. - * - * For development builds, NM_API_VERSION is the next - * stable API after NM_VERSION. When you run a development - * version, you are already using the future API, even if - * it is not yet release. Hence, the currently used API - * version is the future one. */ -#define NM_API_VERSION \ - (((NM_MINOR_VERSION % 2) == 1) \ - ? NM_ENCODE_VERSION (NM_MAJOR_VERSION, NM_MINOR_VERSION + 1, 0 ) \ - : NM_ENCODE_VERSION (NM_MAJOR_VERSION, NM_MINOR_VERSION , ((NM_MICRO_VERSION + 1) / 2) * 2)) - -/* deprecated. */ -#define NM_VERSION_CUR_STABLE NM_API_VERSION - -/* deprecated. */ -#define NM_VERSION_NEXT_STABLE NM_API_VERSION - -#define NM_VERSION NM_ENCODE_VERSION (NM_MAJOR_VERSION, NM_MINOR_VERSION, NM_MICRO_VERSION) - -#endif /* __NM_VERSION_MACROS_H__ */ diff --git a/shared/nm-version-macros.h.in b/shared/nm-version-macros.h.in deleted file mode 100644 index c4d6efbc..00000000 --- a/shared/nm-version-macros.h.in +++ /dev/null @@ -1,90 +0,0 @@ -// SPDX-License-Identifier: LGPL-2.1+ -/* - * Copyright (C) 2011, 2015 Red Hat, Inc. - */ - -#ifndef __NM_VERSION_MACROS_H__ -#define __NM_VERSION_MACROS_H__ - -/* This header must not include glib or libnm. */ - -/** - * NM_MAJOR_VERSION: - * - * Evaluates to the major version number of NetworkManager which this source - * is compiled against. - */ -#define NM_MAJOR_VERSION (@NM_MAJOR_VERSION@) - -/** - * NM_MINOR_VERSION: - * - * Evaluates to the minor version number of NetworkManager which this source - * is compiled against. - */ -#define NM_MINOR_VERSION (@NM_MINOR_VERSION@) - -/** - * NM_MICRO_VERSION: - * - * Evaluates to the micro version number of NetworkManager which this source - * compiled against. - */ -#define NM_MICRO_VERSION (@NM_MICRO_VERSION@) - -/** - * NM_CHECK_VERSION: - * @major: major version (e.g. 1 for version 1.2.5) - * @minor: minor version (e.g. 2 for version 1.2.5) - * @micro: micro version (e.g. 5 for version 1.2.5) - * - * Returns: %TRUE if the version of the NetworkManager header files - * is the same as or newer than the passed-in version. - */ -#define NM_CHECK_VERSION(major,minor,micro) \ - (NM_MAJOR_VERSION > (major) || \ - (NM_MAJOR_VERSION == (major) && NM_MINOR_VERSION > (minor)) || \ - (NM_MAJOR_VERSION == (major) && NM_MINOR_VERSION == (minor) && NM_MICRO_VERSION >= (micro))) - - -#define NM_ENCODE_VERSION(major,minor,micro) ((major) << 16 | (minor) << 8 | (micro)) - -#define NM_VERSION_0_9_8 (NM_ENCODE_VERSION (0, 9, 8)) -#define NM_VERSION_0_9_10 (NM_ENCODE_VERSION (0, 9, 10)) -#define NM_VERSION_1_0 (NM_ENCODE_VERSION (1, 0, 0)) -#define NM_VERSION_1_2 (NM_ENCODE_VERSION (1, 2, 0)) -#define NM_VERSION_1_4 (NM_ENCODE_VERSION (1, 4, 0)) -#define NM_VERSION_1_6 (NM_ENCODE_VERSION (1, 6, 0)) -#define NM_VERSION_1_8 (NM_ENCODE_VERSION (1, 8, 0)) -#define NM_VERSION_1_10 (NM_ENCODE_VERSION (1, 10, 0)) -#define NM_VERSION_1_12 (NM_ENCODE_VERSION (1, 12, 0)) -#define NM_VERSION_1_14 (NM_ENCODE_VERSION (1, 14, 0)) -#define NM_VERSION_1_16 (NM_ENCODE_VERSION (1, 16, 0)) -#define NM_VERSION_1_18 (NM_ENCODE_VERSION (1, 18, 0)) -#define NM_VERSION_1_20 (NM_ENCODE_VERSION (1, 20, 0)) -#define NM_VERSION_1_22 (NM_ENCODE_VERSION (1, 22, 0)) -#define NM_VERSION_1_24 (NM_ENCODE_VERSION (1, 24, 0)) -#define NM_VERSION_1_26 (NM_ENCODE_VERSION (1, 26, 0)) -#define NM_VERSION_1_28 (NM_ENCODE_VERSION (1, 28, 0)) - -/* For releases, NM_API_VERSION is equal to NM_VERSION. - * - * For development builds, NM_API_VERSION is the next - * stable API after NM_VERSION. When you run a development - * version, you are already using the future API, even if - * it is not yet release. Hence, the currently used API - * version is the future one. */ -#define NM_API_VERSION \ - (((NM_MINOR_VERSION % 2) == 1) \ - ? NM_ENCODE_VERSION (NM_MAJOR_VERSION, NM_MINOR_VERSION + 1, 0 ) \ - : NM_ENCODE_VERSION (NM_MAJOR_VERSION, NM_MINOR_VERSION , ((NM_MICRO_VERSION + 1) / 2) * 2)) - -/* deprecated. */ -#define NM_VERSION_CUR_STABLE NM_API_VERSION - -/* deprecated. */ -#define NM_VERSION_NEXT_STABLE NM_API_VERSION - -#define NM_VERSION NM_ENCODE_VERSION (NM_MAJOR_VERSION, NM_MINOR_VERSION, NM_MICRO_VERSION) - -#endif /* __NM_VERSION_MACROS_H__ */ diff --git a/shared/systemd/nm-default-systemd-shared.h b/shared/systemd/nm-default-systemd-shared.h new file mode 100644 index 00000000..bc0e6c4c --- /dev/null +++ b/shared/systemd/nm-default-systemd-shared.h @@ -0,0 +1,18 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +/* + * Copyright (C) 2015 Red Hat, Inc. + */ + +#ifndef __NM_DEFAULT_SYSTEMD_SHARED_H__ +#define __NM_DEFAULT_SYSTEMD_SHARED_H__ + +/*****************************************************************************/ + +#include "nm-glib-aux/nm-default-glib.h" + +#undef NETWORKMANAGER_COMPILATION +#define NETWORKMANAGER_COMPILATION NM_NETWORKMANAGER_COMPILATION_SYSTEMD_SHARED + +/*****************************************************************************/ + +#endif /* __NM_DEFAULT_SYSTEMD_SHARED_H__ */ diff --git a/shared/systemd/nm-logging-stub.c b/shared/systemd/nm-logging-stub.c index fa2328f3..8db90cd9 100644 --- a/shared/systemd/nm-logging-stub.c +++ b/shared/systemd/nm-logging-stub.c @@ -1,9 +1,9 @@ -/* SPDX-License-Identifier: LGPL-2.1+ */ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ /* * Copyright (C) 2018 Red Hat, Inc. */ -#include "nm-default.h" +#include "shared/systemd/nm-default-systemd-shared.h" #include "nm-glib-aux/nm-logging-fwd.h" diff --git a/shared/systemd/nm-sd-utils-shared.c b/shared/systemd/nm-sd-utils-shared.c index 83c91af7..f0504aa9 100644 --- a/shared/systemd/nm-sd-utils-shared.c +++ b/shared/systemd/nm-sd-utils-shared.c @@ -1,9 +1,9 @@ -/* SPDX-License-Identifier: LGPL-2.1+ */ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ /* * Copyright (C) 2018 Red Hat, Inc. */ -#include "nm-default.h" +#include "shared/systemd/nm-default-systemd-shared.h" #include "nm-sd-utils-shared.h" @@ -88,7 +88,27 @@ nm_sd_dns_name_is_valid(const char *s) gboolean nm_sd_hostname_is_valid(const char *s, bool allow_trailing_dot) { - return hostname_is_valid(s, allow_trailing_dot); + return hostname_is_valid(s, + allow_trailing_dot ? VALID_HOSTNAME_TRAILING_DOT + : (ValidHostnameFlags) 0); +} + +char * +nm_sd_dns_name_normalize(const char *s) +{ + nm_auto_free char *n = NULL; + int r; + + r = dns_name_normalize(s, 0, &n); + if (r < 0) + return NULL; + + nm_assert(n); + + /* usually we try not to mix malloc/g_malloc and free/g_free. In practice, + * they are the same. So here we return a buffer allocated with malloc(), + * and the caller should free it with g_free(). */ + return g_steal_pointer(&n); } /*****************************************************************************/ diff --git a/shared/systemd/nm-sd-utils-shared.h b/shared/systemd/nm-sd-utils-shared.h index b871fc29..45089c07 100644 --- a/shared/systemd/nm-sd-utils-shared.h +++ b/shared/systemd/nm-sd-utils-shared.h @@ -1,4 +1,4 @@ -/* SPDX-License-Identifier: LGPL-2.1+ */ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ /* * Copyright (C) 2018 Red Hat, Inc. */ @@ -28,6 +28,8 @@ nm_sd_dns_name_to_wire_format(const char *domain, guint8 *buffer, size_t len, gb int nm_sd_dns_name_is_valid(const char *s); gboolean nm_sd_hostname_is_valid(const char *s, bool allow_trailing_dot); +char *nm_sd_dns_name_normalize(const char *s); + /*****************************************************************************/ gboolean nm_sd_http_url_is_valid_https(const char *url); diff --git a/shared/systemd/sd-adapt-shared/idn-util.h b/shared/systemd/sd-adapt-shared/idn-util.h new file mode 100644 index 00000000..637892c2 --- /dev/null +++ b/shared/systemd/sd-adapt-shared/idn-util.h @@ -0,0 +1,3 @@ +#pragma once + +/* dummy header */ diff --git a/shared/systemd/sd-adapt-shared/nm-sd-adapt-shared.h b/shared/systemd/sd-adapt-shared/nm-sd-adapt-shared.h index 0160284d..b094ce40 100644 --- a/shared/systemd/sd-adapt-shared/nm-sd-adapt-shared.h +++ b/shared/systemd/sd-adapt-shared/nm-sd-adapt-shared.h @@ -1,4 +1,4 @@ -/* SPDX-License-Identifier: LGPL-2.1+ */ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ /* * Copyright (C) 2014 - 2018 Red Hat, Inc. */ @@ -6,7 +6,7 @@ #ifndef __NM_SD_ADAPT_SHARED_H__ #define __NM_SD_ADAPT_SHARED_H__ -#include "nm-default.h" +#include "shared/systemd/nm-default-systemd-shared.h" #include "nm-glib-aux/nm-logging-fwd.h" @@ -127,6 +127,7 @@ _nm_log_get_max_level_realm(void) #include <sys/syscall.h> #include <sys/ioctl.h> + #include <pthread.h> #define ENABLE_GSHADOW FALSE @@ -195,6 +196,24 @@ _nm_gettid(void) #define HAVE_RT_SIGQUEUEINFO 0 #endif + #ifndef __COMPAR_FN_T + #define __COMPAR_FN_T +typedef int (*__compar_fn_t)(const void *, const void *); +typedef __compar_fn_t comparison_fn_t; +typedef int (*__compar_d_fn_t)(const void *, const void *, void *); + #endif + + #ifndef __GLIBC__ +static inline int +__register_atfork(void (*prepare)(void), + void (*parent)(void), + void (*child)(void), + void *dso_handle) +{ + return pthread_atfork(prepare, parent, child); +} + #endif + #endif /* (NETWORKMANAGER_COMPILATION) & NM_NETWORKMANAGER_COMPILATION_WITH_SYSTEMD */ /*****************************************************************************/ diff --git a/shared/systemd/src/basic/alloc-util.c b/shared/systemd/src/basic/alloc-util.c index e355b60f..7f7eb433 100644 --- a/shared/systemd/src/basic/alloc-util.c +++ b/shared/systemd/src/basic/alloc-util.c @@ -1,4 +1,4 @@ -/* SPDX-License-Identifier: LGPL-2.1+ */ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ #include "nm-sd-adapt-shared.h" diff --git a/shared/systemd/src/basic/alloc-util.h b/shared/systemd/src/basic/alloc-util.h index 64d9e003..f3e192dd 100644 --- a/shared/systemd/src/basic/alloc-util.h +++ b/shared/systemd/src/basic/alloc-util.h @@ -1,4 +1,4 @@ -/* SPDX-License-Identifier: LGPL-2.1+ */ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ #pragma once #include <alloca.h> @@ -27,7 +27,7 @@ typedef void (*free_func_t)(void *p); size_t _n_ = n; \ assert(!size_multiply_overflow(sizeof(t), _n_)); \ assert(sizeof(t)*_n_ <= ALLOCA_MAX); \ - (t*) alloca(sizeof(t)*_n_); \ + (t*) alloca((sizeof(t)*_n_) ?: 1); \ }) #define newa0(t, n) \ @@ -35,14 +35,14 @@ typedef void (*free_func_t)(void *p); size_t _n_ = n; \ assert(!size_multiply_overflow(sizeof(t), _n_)); \ assert(sizeof(t)*_n_ <= ALLOCA_MAX); \ - (t*) alloca0(sizeof(t)*_n_); \ + (t*) alloca0((sizeof(t)*_n_) ?: 1); \ }) #define newdup(t, p, n) ((t*) memdup_multiply(p, sizeof(t), (n))) #define newdup_suffix0(t, p, n) ((t*) memdup_suffix0_multiply(p, sizeof(t), (n))) -#define malloc0(n) (calloc(1, (n))) +#define malloc0(n) (calloc(1, (n) ?: 1)) static inline void *mfree(void *memory) { free(memory); @@ -65,7 +65,7 @@ void* memdup_suffix0(const void *p, size_t l); /* We can't use _alloc_() here, s void *_q_; \ size_t _l_ = l; \ assert(_l_ <= ALLOCA_MAX); \ - _q_ = alloca(_l_); \ + _q_ = alloca(_l_ ?: 1); \ memcpy(_q_, p, _l_); \ }) @@ -135,7 +135,7 @@ void* greedy_realloc0(void **p, size_t *allocated, size_t need, size_t size); char *_new_; \ size_t _len_ = n; \ assert(_len_ <= ALLOCA_MAX); \ - _new_ = alloca(_len_); \ + _new_ = alloca(_len_ ?: 1); \ (void *) memset(_new_, 0, _len_); \ }) @@ -146,7 +146,7 @@ void* greedy_realloc0(void **p, size_t *allocated, size_t need, size_t size); size_t _mask_ = (align) - 1; \ size_t _size_ = size; \ assert(_size_ <= ALLOCA_MAX); \ - _ptr_ = alloca(_size_ + _mask_); \ + _ptr_ = alloca((_size_ + _mask_) ?: 1); \ (void*)(((uintptr_t)_ptr_ + _mask_) & ~_mask_); \ }) diff --git a/shared/systemd/src/basic/async.h b/shared/systemd/src/basic/async.h index 31606131..e0bbaa56 100644 --- a/shared/systemd/src/basic/async.h +++ b/shared/systemd/src/basic/async.h @@ -1,7 +1,13 @@ -/* SPDX-License-Identifier: LGPL-2.1+ */ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ #pragma once +#include <sys/types.h> + +#include "macro.h" + int asynchronous_job(void* (*func)(void *p), void *arg); int asynchronous_sync(pid_t *ret_pid); int asynchronous_close(int fd); + +DEFINE_TRIVIAL_CLEANUP_FUNC(int, asynchronous_close); diff --git a/shared/systemd/src/basic/cgroup-util.h b/shared/systemd/src/basic/cgroup-util.h index 2b88571b..bdc0d0d0 100644 --- a/shared/systemd/src/basic/cgroup-util.h +++ b/shared/systemd/src/basic/cgroup-util.h @@ -1,4 +1,4 @@ -/* SPDX-License-Identifier: LGPL-2.1+ */ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ #pragma once #include <dirent.h> @@ -208,6 +208,9 @@ static inline int cg_get_keyed_attribute_graceful( int cg_get_attribute_as_uint64(const char *controller, const char *path, const char *attribute, uint64_t *ret); +/* Does a parse_boolean() on the attribute contents and sets ret accordingly */ +int cg_get_attribute_as_bool(const char *controller, const char *path, const char *attribute, bool *ret); + int cg_set_access(const char *controller, const char *path, uid_t uid, gid_t gid); int cg_set_xattr(const char *controller, const char *path, const char *name, const void *value, size_t size, int flags); @@ -275,3 +278,13 @@ 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, + _MANAGED_OOM_MODE_MAX, + _MANAGED_OOM_MODE_INVALID = -1, +} ManagedOOMMode; + +const char* managed_oom_mode_to_string(ManagedOOMMode m) _const_; +ManagedOOMMode managed_oom_mode_from_string(const char *s) _pure_; diff --git a/shared/systemd/src/basic/env-file.c b/shared/systemd/src/basic/env-file.c index 1e93f18e..568b742f 100644 --- a/shared/systemd/src/basic/env-file.c +++ b/shared/systemd/src/basic/env-file.c @@ -1,4 +1,4 @@ -/* SPDX-License-Identifier: LGPL-2.1+ */ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ #include "nm-sd-adapt-shared.h" diff --git a/shared/systemd/src/basic/env-file.h b/shared/systemd/src/basic/env-file.h index e1ca195f..de475885 100644 --- a/shared/systemd/src/basic/env-file.h +++ b/shared/systemd/src/basic/env-file.h @@ -1,4 +1,4 @@ -/* SPDX-License-Identifier: LGPL-2.1+ */ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ #pragma once #include <stdarg.h> diff --git a/shared/systemd/src/basic/env-util.c b/shared/systemd/src/basic/env-util.c index 11f4b29b..a311ee00 100644 --- a/shared/systemd/src/basic/env-util.c +++ b/shared/systemd/src/basic/env-util.c @@ -1,4 +1,4 @@ -/* SPDX-License-Identifier: LGPL-2.1+ */ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ #include "nm-sd-adapt-shared.h" @@ -19,7 +19,8 @@ #include "utf8.h" #if 0 /* NM_IGNORED */ -#define VALID_CHARS_ENV_NAME \ +/* We follow bash for the character set. Different shells have different rules. */ +#define VALID_BASH_ENV_NAME_CHARS \ DIGITS LETTERS \ "_" @@ -44,17 +45,14 @@ static bool env_name_is_valid_n(const char *e, size_t n) { return false; for (p = e; p < e + n; p++) - if (!strchr(VALID_CHARS_ENV_NAME, *p)) + if (!strchr(VALID_BASH_ENV_NAME_CHARS, *p)) return false; return true; } bool env_name_is_valid(const char *e) { - if (!e) - return false; - - return env_name_is_valid_n(e, strlen(e)); + return env_name_is_valid_n(e, strlen_ptr(e)); } bool env_value_is_valid(const char *e) { @@ -549,7 +547,7 @@ char *replace_env_n(const char *format, size_t n, char **env, unsigned flags) { word = e+1; state = WORD; - } else if (flags & REPLACE_ENV_ALLOW_BRACELESS && strchr(VALID_CHARS_ENV_NAME, *e)) { + } else if (flags & REPLACE_ENV_ALLOW_BRACELESS && strchr(VALID_BASH_ENV_NAME_CHARS, *e)) { k = strnappend(r, word, e-word-1); if (!k) return NULL; @@ -639,7 +637,7 @@ char *replace_env_n(const char *format, size_t n, char **env, unsigned flags) { case VARIABLE_RAW: assert(flags & REPLACE_ENV_ALLOW_BRACELESS); - if (!strchr(VALID_CHARS_ENV_NAME, *e)) { + if (!strchr(VALID_BASH_ENV_NAME_CHARS, *e)) { const char *t; t = strv_env_get_n(env, word+1, e-word-1, flags); @@ -753,3 +751,17 @@ int getenv_bool_secure(const char *p) { return parse_boolean(e); } + +#if 0 /* NM_IGNORED */ +int set_unset_env(const char *name, const char *value, bool overwrite) { + int r; + + if (value) + r = setenv(name, value, overwrite); + else + r = unsetenv(name); + if (r < 0) + return -errno; + return 0; +} +#endif /* NM_IGNORED */ diff --git a/shared/systemd/src/basic/env-util.h b/shared/systemd/src/basic/env-util.h index 92802ed7..6684b335 100644 --- a/shared/systemd/src/basic/env-util.h +++ b/shared/systemd/src/basic/env-util.h @@ -1,4 +1,4 @@ -/* SPDX-License-Identifier: LGPL-2.1+ */ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ #pragma once #include <stdbool.h> @@ -52,3 +52,6 @@ char *strv_env_get(char **x, const char *n) _pure_; int getenv_bool(const char *p); int getenv_bool_secure(const char *p); + +/* Like setenv, but calls unsetenv if value == NULL. */ +int set_unset_env(const char *name, const char *value, bool overwrite); diff --git a/shared/systemd/src/basic/errno-util.h b/shared/systemd/src/basic/errno-util.h index 0ca650f4..5609820b 100644 --- a/shared/systemd/src/basic/errno-util.h +++ b/shared/systemd/src/basic/errno-util.h @@ -1,4 +1,4 @@ -/* SPDX-License-Identifier: LGPL-2.1+ */ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ #pragma once #include <stdlib.h> @@ -50,7 +50,10 @@ static inline int errno_or_else(int fallback) { /* Hint #1: ENETUNREACH happens if we try to connect to "non-existing" special IP addresses, such as ::5. * * Hint #2: The kernel sends e.g., EHOSTUNREACH or ENONET to userspace in some ICMP error cases. See the - * icmp_err_convert[] in net/ipv4/icmp.c in the kernel sources */ + * icmp_err_convert[] in net/ipv4/icmp.c in the kernel sources. + * + * Hint #3: When asynchronous connect() on TCP fails because the host never acknowledges a single packet, + * kernel tells us that with ETIMEDOUT, see tcp(7). */ static inline bool ERRNO_IS_DISCONNECT(int r) { return IN_SET(abs(r), ECONNABORTED, @@ -66,7 +69,8 @@ static inline bool ERRNO_IS_DISCONNECT(int r) { ENOTCONN, EPIPE, EPROTO, - ESHUTDOWN); + ESHUTDOWN, + ETIMEDOUT); } /* Transient errors we might get on accept() that we should ignore. As per error handling comment in diff --git a/shared/systemd/src/basic/escape.c b/shared/systemd/src/basic/escape.c index 1261fd48..094b5c5f 100644 --- a/shared/systemd/src/basic/escape.c +++ b/shared/systemd/src/basic/escape.c @@ -1,4 +1,4 @@ -/* SPDX-License-Identifier: LGPL-2.1+ */ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ #include "nm-sd-adapt-shared.h" diff --git a/shared/systemd/src/basic/escape.h b/shared/systemd/src/basic/escape.h index fa267813..691b6d80 100644 --- a/shared/systemd/src/basic/escape.h +++ b/shared/systemd/src/basic/escape.h @@ -1,4 +1,4 @@ -/* SPDX-License-Identifier: LGPL-2.1+ */ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ #pragma once #include <inttypes.h> @@ -16,7 +16,7 @@ /* Those that can be escaped or double-quoted. * - * Stricly speaking, ! does not need to be escaped, except in interactive + * Strictly speaking, ! does not need to be escaped, except in interactive * mode, but let's be extra nice to the user and quote ! in case this * output is ever used in interactive mode. */ #define SHELL_NEED_QUOTES SHELL_NEED_ESCAPE GLOB_CHARS "'()<>|&;!" diff --git a/shared/systemd/src/basic/ether-addr-util.c b/shared/systemd/src/basic/ether-addr-util.c index 4878a3d2..ae83eade 100644 --- a/shared/systemd/src/basic/ether-addr-util.c +++ b/shared/systemd/src/basic/ether-addr-util.c @@ -1,8 +1,9 @@ -/* SPDX-License-Identifier: LGPL-2.1+ */ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ #include "nm-sd-adapt-shared.h" #include <errno.h> +#include <inttypes.h> #include <net/ethernet.h> #include <stdio.h> #include <sys/types.h> @@ -11,6 +12,20 @@ #include "macro.h" #include "string-util.h" +char* hw_addr_to_string(const hw_addr_data *addr, char buffer[HW_ADDR_TO_STRING_MAX]) { + assert(addr); + assert(buffer); + assert(addr->length <= HW_ADDR_MAX_SIZE); + + for (size_t i = 0; i < addr->length; i++) { + sprintf(&buffer[3*i], "%02"PRIx8, addr->addr.bytes[i]); + if (i < addr->length - 1) + buffer[3*i + 2] = ':'; + } + + return buffer; +} + char* ether_addr_to_string(const struct ether_addr *addr, char buffer[ETHER_ADDR_TO_STRING_MAX]) { assert(addr); assert(buffer); diff --git a/shared/systemd/src/basic/ether-addr-util.h b/shared/systemd/src/basic/ether-addr-util.h index 4e44b30b..942ce556 100644 --- a/shared/systemd/src/basic/ether-addr-util.h +++ b/shared/systemd/src/basic/ether-addr-util.h @@ -1,11 +1,35 @@ -/* SPDX-License-Identifier: LGPL-2.1+ */ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ #pragma once +#include <linux/if_infiniband.h> #include <net/ethernet.h> #include <stdbool.h> #include "hash-funcs.h" +/* This is MAX_ADDR_LEN as defined in linux/netdevice.h, but net/if_arp.h + * defines a macro of the same name with a much lower size. */ +#define HW_ADDR_MAX_SIZE 32 + +union hw_addr_union { + struct ether_addr ether; + uint8_t infiniband[INFINIBAND_ALEN]; + uint8_t bytes[HW_ADDR_MAX_SIZE]; +}; + +typedef struct hw_addr_data { + union hw_addr_union addr; + size_t length; +} hw_addr_data; + +#define HW_ADDR_TO_STRING_MAX (3*HW_ADDR_MAX_SIZE) +char* hw_addr_to_string(const hw_addr_data *addr, char buffer[HW_ADDR_TO_STRING_MAX]); + +/* Use only as function argument, never stand-alone! */ +#define HW_ADDR_TO_STR(hw_addr) hw_addr_to_string((hw_addr), (char[HW_ADDR_TO_STRING_MAX]){}) + +#define HW_ADDR_NULL ((const hw_addr_data){}) + #define ETHER_ADDR_FORMAT_STR "%02X%02X%02X%02X%02X%02X" #define ETHER_ADDR_FORMAT_VAL(x) (x).ether_addr_octet[0], (x).ether_addr_octet[1], (x).ether_addr_octet[2], (x).ether_addr_octet[3], (x).ether_addr_octet[4], (x).ether_addr_octet[5] diff --git a/shared/systemd/src/basic/extract-word.c b/shared/systemd/src/basic/extract-word.c index e32b4c7f..1d86033f 100644 --- a/shared/systemd/src/basic/extract-word.c +++ b/shared/systemd/src/basic/extract-word.c @@ -1,4 +1,4 @@ -/* SPDX-License-Identifier: LGPL-2.1+ */ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ #include "nm-sd-adapt-shared.h" diff --git a/shared/systemd/src/basic/extract-word.h b/shared/systemd/src/basic/extract-word.h index f028577c..d1de32e5 100644 --- a/shared/systemd/src/basic/extract-word.h +++ b/shared/systemd/src/basic/extract-word.h @@ -1,4 +1,4 @@ -/* SPDX-License-Identifier: LGPL-2.1+ */ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ #pragma once #include "macro.h" diff --git a/shared/systemd/src/basic/fd-util.c b/shared/systemd/src/basic/fd-util.c index 525d3828..e53cf18d 100644 --- a/shared/systemd/src/basic/fd-util.c +++ b/shared/systemd/src/basic/fd-util.c @@ -1,4 +1,4 @@ -/* SPDX-License-Identifier: LGPL-2.1+ */ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ #include "nm-sd-adapt-shared.h" @@ -23,6 +23,7 @@ #include "path-util.h" #include "process-util.h" #include "socket-util.h" +#include "sort-util.h" #include "stat-util.h" #include "stdio-util.h" #include "tmpfile-util.h" @@ -214,12 +215,97 @@ static int get_max_fd(void) { } int close_all_fds(const int except[], size_t n_except) { + static bool have_close_range = true; /* Assume we live in the future */ _cleanup_closedir_ DIR *d = NULL; struct dirent *de; int r = 0; assert(n_except == 0 || except); + if (have_close_range) { + /* In the best case we have close_range() to close all fds between a start and an end fd, + * which we can use on the "inverted" exception array, i.e. all intervals between all + * adjacent pairs from the sorted exception array. This changes loop complexity from O(n) + * where n is number of open fds to O(m⋅log(m)) where m is the number of fds to keep + * open. Given that we assume n ≫ m that's preferable to us. */ + + if (n_except == 0) { + /* Close everything. Yay! */ + + if (close_range(3, -1, 0) >= 0) + return 1; + + if (!ERRNO_IS_NOT_SUPPORTED(errno) && !ERRNO_IS_PRIVILEGE(errno)) + return -errno; + + have_close_range = false; + } else { + _cleanup_free_ int *sorted_malloc = NULL; + size_t n_sorted; + int *sorted; + + assert(n_except < SIZE_MAX); + n_sorted = n_except + 1; + + if (n_sorted > 64) /* Use heap for large numbers of fds, stack otherwise */ + sorted = sorted_malloc = new(int, n_sorted); + else + sorted = newa(int, n_sorted); + + if (sorted) { + int c = 0; + + memcpy(sorted, except, n_except * sizeof(int)); + + /* Let's add fd 2 to the list of fds, to simplify the loop below, as this + * allows us to cover the head of the array the same way as the body */ + sorted[n_sorted-1] = 2; + + typesafe_qsort(sorted, n_sorted, cmp_int); + + for (size_t i = 0; i < n_sorted-1; i++) { + int start, end; + + start = MAX(sorted[i], 2); /* The first three fds shall always remain open */ + end = MAX(sorted[i+1], 2); + + assert(end >= start); + + if (end - start <= 1) + continue; + + /* Close everything between the start and end fds (both of which shall stay open) */ + if (close_range(start + 1, end - 1, 0) < 0) { + if (!ERRNO_IS_NOT_SUPPORTED(errno) && !ERRNO_IS_PRIVILEGE(errno)) + return -errno; + + have_close_range = false; + break; + } + + c += end - start - 1; + } + + if (have_close_range) { + /* The loop succeeded. Let's now close everything beyond the end */ + + if (sorted[n_sorted-1] >= INT_MAX) /* Dont let the addition below overflow */ + return c; + + if (close_range(sorted[n_sorted-1] + 1, -1, 0) >= 0) + return c + 1; + + if (!ERRNO_IS_NOT_SUPPORTED(errno) && !ERRNO_IS_PRIVILEGE(errno)) + return -errno; + + have_close_range = false; + } + } + } + + /* Fallback on OOM or if close_range() is not supported */ + } + d = opendir("/proc/self/fd"); if (!d) { int fd, max_fd; diff --git a/shared/systemd/src/basic/fd-util.h b/shared/systemd/src/basic/fd-util.h index 93ce95cd..2162537b 100644 --- a/shared/systemd/src/basic/fd-util.h +++ b/shared/systemd/src/basic/fd-util.h @@ -1,4 +1,4 @@ -/* SPDX-License-Identifier: LGPL-2.1+ */ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ #pragma once #include <dirent.h> diff --git a/shared/systemd/src/basic/fileio.c b/shared/systemd/src/basic/fileio.c index 664b894a..ea90614a 100644 --- a/shared/systemd/src/basic/fileio.c +++ b/shared/systemd/src/basic/fileio.c @@ -1,4 +1,4 @@ -/* SPDX-License-Identifier: LGPL-2.1+ */ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ #include "nm-sd-adapt-shared.h" @@ -120,7 +120,7 @@ int write_string_stream_ts( FILE *f, const char *line, WriteStringFileFlags flags, - struct timespec *ts) { + const struct timespec *ts) { bool needs_nl; int r, fd; @@ -164,7 +164,7 @@ int write_string_stream_ts( return r; if (ts) { - struct timespec twice[2] = {*ts, *ts}; + const struct timespec twice[2] = {*ts, *ts}; if (futimens(fd, twice) < 0) return -errno; @@ -177,7 +177,7 @@ static int write_string_file_atomic( const char *fn, const char *line, WriteStringFileFlags flags, - struct timespec *ts) { + const struct timespec *ts) { _cleanup_fclose_ FILE *f = NULL; _cleanup_free_ char *p = NULL; @@ -224,7 +224,7 @@ int write_string_file_ts( const char *fn, const char *line, WriteStringFileFlags flags, - struct timespec *ts) { + const struct timespec *ts) { _cleanup_fclose_ FILE *f = NULL; int q, r, fd; @@ -255,7 +255,8 @@ int write_string_file_ts( /* We manually build our own version of fopen(..., "we") that works without O_CREAT and with O_NOFOLLOW if needed. */ fd = open(fn, O_WRONLY|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_CREATE) ? O_CREAT : 0) | + (FLAGS_SET(flags, WRITE_STRING_FILE_TRUNCATE) ? O_TRUNC : 0), (FLAGS_SET(flags, WRITE_STRING_FILE_MODE_0600) ? 0600 : 0666)); if (fd < 0) { r = -errno; @@ -475,12 +476,13 @@ int read_full_virtual_file(const char *filename, char **ret_contents, size_t *re int read_full_stream_full( FILE *f, const char *filename, + uint64_t offset, + size_t size, ReadFullFileFlags flags, char **ret_contents, size_t *ret_size) { _cleanup_free_ char *buf = NULL; - struct stat st; size_t n, n_next, l; int fd, r; @@ -488,32 +490,45 @@ int read_full_stream_full( assert(ret_contents); assert(!FLAGS_SET(flags, READ_FULL_FILE_UNBASE64 | READ_FULL_FILE_UNHEX)); - n_next = LINE_MAX; /* Start size */ + if (offset != UINT64_MAX && offset > LONG_MAX) + return -ERANGE; + + n_next = size != SIZE_MAX ? size : LINE_MAX; /* Start size */ fd = fileno(f); - if (fd >= 0) { /* If the FILE* object is backed by an fd (as opposed to memory or such, see fmemopen()), let's - * optimize our buffering */ + if (fd >= 0) { /* If the FILE* object is backed by an fd (as opposed to memory or such, see + * fmemopen()), let's optimize our buffering */ + struct stat st; if (fstat(fd, &st) < 0) return -errno; if (S_ISREG(st.st_mode)) { - - /* Safety check */ - if (st.st_size > READ_FULL_BYTES_MAX) - return -E2BIG; - - /* Start with the right file size. Note that we increase the size - * to read here by one, so that the first read attempt already - * makes us notice the EOF. */ - if (st.st_size > 0) - n_next = st.st_size + 1; + if (size == SIZE_MAX) { + uint64_t rsize = + LESS_BY((uint64_t) st.st_size, offset == UINT64_MAX ? 0 : offset); + + /* Safety check */ + if (rsize > READ_FULL_BYTES_MAX) + return -E2BIG; + + /* Start with the right file size. Note that we increase the size to read + * here by one, so that the first read attempt already makes us notice the + * EOF. If the reported size of the file is zero, we avoid this logic + * however, since quite likely it might be a virtual file in procfs that all + * report a zero file size. */ + if (st.st_size > 0) + n_next = rsize + 1; + } if (flags & READ_FULL_FILE_WARN_WORLD_READABLE) (void) warn_file_is_world_accessible(filename, &st, NULL, 0); } } + if (offset != UINT64_MAX && fseek(f, offset, SEEK_SET) < 0) + return -errno; + n = l = 0; for (;;) { char *t; @@ -550,6 +565,11 @@ int read_full_stream_full( if (feof(f)) break; + if (size != SIZE_MAX) { /* If we got asked to read some specific size, we already sized the buffer right, hence leave */ + assert(l == size); + break; + } + assert(k > 0); /* we can't have read zero bytes because that would have been EOF */ /* Safety check */ @@ -605,12 +625,21 @@ finalize: return r; } -int read_full_file_full(int dir_fd, const char *filename, ReadFullFileFlags flags, char **contents, size_t *size) { +int read_full_file_full( + int dir_fd, + const char *filename, + uint64_t offset, + size_t size, + ReadFullFileFlags flags, + const char *bind_name, + char **ret_contents, + size_t *ret_size) { + _cleanup_fclose_ FILE *f = NULL; int r; assert(filename); - assert(contents); + assert(ret_contents); r = xfopenat(dir_fd, filename, "re", 0, &f); if (r < 0) { @@ -625,6 +654,10 @@ int read_full_file_full(int dir_fd, const char *filename, ReadFullFileFlags flag if (!FLAGS_SET(flags, READ_FULL_FILE_CONNECT_SOCKET)) return -ENXIO; + /* Seeking is not supported on AF_UNIX sockets */ + if (offset != UINT64_MAX) + return -ESPIPE; + if (dir_fd == AT_FDCWD) r = sockaddr_un_set_path(&sa.un, filename); else { @@ -648,6 +681,20 @@ int read_full_file_full(int dir_fd, const char *filename, ReadFullFileFlags flag if (sk < 0) return -errno; + if (bind_name) { + /* If the caller specified a socket name to bind to, do so before connecting. This is + * useful to communicate some minor, short meta-information token from the client to + * the server. */ + union sockaddr_union bsa; + + r = sockaddr_un_set_path(&bsa.un, bind_name); + if (r < 0) + return r; + + if (bind(sk, &bsa.sa, r) < 0) + return r; + } + if (connect(sk, &sa.sa, SOCKADDR_UN_LEN(sa.un)) < 0) return errno == ENOTSOCK ? -ENXIO : -errno; /* propagate original error if this is * not a socket after all */ @@ -664,7 +711,7 @@ int read_full_file_full(int dir_fd, const char *filename, ReadFullFileFlags flag (void) __fsetlocking(f, FSETLOCKING_BYCALLER); - return read_full_stream_full(f, filename, flags, contents, size); + return read_full_stream_full(f, filename, offset, size, flags, ret_contents, ret_size); } #if 0 /* NM_IGNORED */ diff --git a/shared/systemd/src/basic/fileio.h b/shared/systemd/src/basic/fileio.h index 9cba5a90..5a028561 100644 --- a/shared/systemd/src/basic/fileio.h +++ b/shared/systemd/src/basic/fileio.h @@ -1,4 +1,4 @@ -/* SPDX-License-Identifier: LGPL-2.1+ */ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ #pragma once #include <dirent.h> @@ -6,7 +6,11 @@ #include <stddef.h> #include <stdio.h> #include <sys/stat.h> +#if 0 /* NM_IGNORED */ #include <sys/fcntl.h> +#else /* NM_IGNORED */ +#include <fcntl.h> +#endif /* NM_IGNORED */ #include <sys/types.h> #include "macro.h" @@ -16,14 +20,15 @@ typedef enum { WRITE_STRING_FILE_CREATE = 1 << 0, - WRITE_STRING_FILE_ATOMIC = 1 << 1, - WRITE_STRING_FILE_AVOID_NEWLINE = 1 << 2, - WRITE_STRING_FILE_VERIFY_ON_FAILURE = 1 << 3, - WRITE_STRING_FILE_SYNC = 1 << 4, - WRITE_STRING_FILE_DISABLE_BUFFER = 1 << 5, - WRITE_STRING_FILE_NOFOLLOW = 1 << 6, - WRITE_STRING_FILE_MKDIR_0755 = 1 << 7, - WRITE_STRING_FILE_MODE_0600 = 1 << 8, + WRITE_STRING_FILE_TRUNCATE = 1 << 1, + WRITE_STRING_FILE_ATOMIC = 1 << 2, + WRITE_STRING_FILE_AVOID_NEWLINE = 1 << 3, + WRITE_STRING_FILE_VERIFY_ON_FAILURE = 1 << 4, + WRITE_STRING_FILE_SYNC = 1 << 5, + WRITE_STRING_FILE_DISABLE_BUFFER = 1 << 6, + WRITE_STRING_FILE_NOFOLLOW = 1 << 7, + WRITE_STRING_FILE_MKDIR_0755 = 1 << 8, + WRITE_STRING_FILE_MODE_0600 = 1 << 9, /* And before you wonder, why write_string_file_atomic_label_ts() is a separate function instead of just one more flag here: it's about linking: we don't want to pull -lselinux into all users of write_string_file() @@ -47,11 +52,11 @@ DIR* take_fdopendir(int *dfd); FILE* open_memstream_unlocked(char **ptr, size_t *sizeloc); FILE* fmemopen_unlocked(void *buf, size_t size, const char *mode); -int write_string_stream_ts(FILE *f, const char *line, WriteStringFileFlags flags, struct timespec *ts); +int write_string_stream_ts(FILE *f, const char *line, WriteStringFileFlags flags, const struct timespec *ts); static inline int write_string_stream(FILE *f, const char *line, WriteStringFileFlags flags) { return write_string_stream_ts(f, line, flags, NULL); } -int write_string_file_ts(const char *fn, const char *line, WriteStringFileFlags flags, struct timespec *ts); +int write_string_file_ts(const char *fn, const char *line, WriteStringFileFlags flags, const struct timespec *ts); static inline int write_string_file(const char *fn, const char *line, WriteStringFileFlags flags) { return write_string_file_ts(fn, line, flags, NULL); } @@ -59,14 +64,14 @@ static inline int write_string_file(const char *fn, const char *line, WriteStrin int write_string_filef(const char *fn, WriteStringFileFlags flags, const char *format, ...) _printf_(3, 4); int read_one_line_file(const char *filename, char **line); -int read_full_file_full(int dir_fd, const char *filename, ReadFullFileFlags flags, char **contents, size_t *size); -static inline int read_full_file(const char *filename, char **contents, size_t *size) { - return read_full_file_full(AT_FDCWD, filename, 0, contents, size); +int read_full_file_full(int dir_fd, const char *filename, uint64_t offset, size_t size, ReadFullFileFlags flags, const char *bind_name, char **ret_contents, size_t *ret_size); +static inline int read_full_file(const char *filename, char **ret_contents, size_t *ret_size) { + return read_full_file_full(AT_FDCWD, filename, UINT64_MAX, SIZE_MAX, 0, NULL, ret_contents, ret_size); } int read_full_virtual_file(const char *filename, char **ret_contents, size_t *ret_size); -int read_full_stream_full(FILE *f, const char *filename, ReadFullFileFlags flags, char **contents, size_t *size); -static inline int read_full_stream(FILE *f, char **contents, size_t *size) { - return read_full_stream_full(f, NULL, 0, contents, size); +int read_full_stream_full(FILE *f, const char *filename, uint64_t offset, size_t size, ReadFullFileFlags flags, char **ret_contents, size_t *ret_size); +static inline int read_full_stream(FILE *f, char **ret_contents, size_t *ret_size) { + return read_full_stream_full(f, NULL, UINT64_MAX, SIZE_MAX, 0, ret_contents, ret_size); } int verify_file(const char *fn, const char *blob, bool accept_extra_nl); diff --git a/shared/systemd/src/basic/format-util.c b/shared/systemd/src/basic/format-util.c index 62477f53..42224b6f 100644 --- a/shared/systemd/src/basic/format-util.c +++ b/shared/systemd/src/basic/format-util.c @@ -1,4 +1,4 @@ -/* SPDX-License-Identifier: LGPL-2.1+ */ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ #include "nm-sd-adapt-shared.h" diff --git a/shared/systemd/src/basic/format-util.h b/shared/systemd/src/basic/format-util.h index c47fa76e..b7e18768 100644 --- a/shared/systemd/src/basic/format-util.h +++ b/shared/systemd/src/basic/format-util.h @@ -1,4 +1,4 @@ -/* SPDX-License-Identifier: LGPL-2.1+ */ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ #pragma once #include <inttypes.h> @@ -72,11 +72,14 @@ typedef enum { FORMAT_BYTES_TRAILING_B = 1 << 2, } FormatBytesFlag; -#define FORMAT_BYTES_MAX 16 +#define FORMAT_BYTES_MAX 16U + char *format_bytes_full(char *buf, size_t l, uint64_t t, FormatBytesFlag flag); + static inline char *format_bytes(char *buf, size_t l, uint64_t t) { return format_bytes_full(buf, l, t, FORMAT_BYTES_USE_IEC | FORMAT_BYTES_BELOW_POINT | FORMAT_BYTES_TRAILING_B); } + static inline char *format_bytes_cgroup_protection(char *buf, size_t l, uint64_t t) { if (t == CGROUP_LIMIT_MAX) { (void) snprintf(buf, l, "%s", "infinity"); diff --git a/shared/systemd/src/basic/fs-util.c b/shared/systemd/src/basic/fs-util.c index e50252cb..2ed9ee0e 100644 --- a/shared/systemd/src/basic/fs-util.c +++ b/shared/systemd/src/basic/fs-util.c @@ -1,4 +1,4 @@ -/* SPDX-License-Identifier: LGPL-2.1+ */ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ #include "nm-sd-adapt-shared.h" @@ -821,7 +821,7 @@ int chase_symlinks(const char *path, const char *original_root, unsigned flags, * * 3. With CHASE_STEP: in this case only a single step of the normalization is executed, i.e. only the first * symlink or ".." component of the path is resolved, and the resulting path is returned. This is useful if - * a caller wants to trace the a path through the file system verbosely. Returns < 0 on error, > 0 if the + * a caller wants to trace the path through the file system verbosely. Returns < 0 on error, > 0 if the * path is fully normalized, and == 0 for each normalization step. This may be combined with * CHASE_NONEXISTENT, in which case 1 is returned when a component is not found. * @@ -945,7 +945,7 @@ int chase_symlinks(const char *path, const char *original_root, unsigned flags, /* Preserve the trailing slash */ if (flags & CHASE_TRAIL_SLASH) - if (!strextend(&done, "/", NULL)) + if (!strextend(&done, "/")) return -ENOMEM; break; @@ -1016,7 +1016,7 @@ int chase_symlinks(const char *path, const char *original_root, unsigned flags, if (streq_ptr(done, "/")) *done = '\0'; - if (!strextend(&done, first, todo, NULL)) + if (!strextend(&done, first, todo)) return -ENOMEM; exists = false; @@ -1109,7 +1109,7 @@ int chase_symlinks(const char *path, const char *original_root, unsigned flags, if (streq(done, "/")) *done = '\0'; - if (!strextend(&done, first, NULL)) + if (!strextend(&done, first)) return -ENOMEM; } @@ -1626,4 +1626,81 @@ int path_is_encrypted(const char *path) { return blockdev_is_encrypted(p, 10 /* safety net: maximum recursion depth */); } + +int conservative_rename( + int olddirfd, const char *oldpath, + int newdirfd, const char *newpath) { + + _cleanup_close_ int old_fd = -1, new_fd = -1; + struct stat old_stat, new_stat; + + /* Renames the old path to thew new path, much like renameat() — except if both are regular files and + * have the exact same contents and basic file attributes already. In that case remove the new file + * instead. This call is useful for reducing inotify wakeups on files that are updated but don't + * actually change. This function is written in a style that we rather rename too often than suppress + * too much. i.e. whenever we are in doubt we rather rename than fail. After all reducing inotify + * events is an optimization only, not more. */ + + old_fd = openat(olddirfd, oldpath, O_CLOEXEC|O_RDONLY|O_NOCTTY|O_NOFOLLOW); + if (old_fd < 0) + goto do_rename; + + new_fd = openat(newdirfd, newpath, O_CLOEXEC|O_RDONLY|O_NOCTTY|O_NOFOLLOW); + if (new_fd < 0) + goto do_rename; + + if (fstat(old_fd, &old_stat) < 0) + goto do_rename; + + if (!S_ISREG(old_stat.st_mode)) + goto do_rename; + + if (fstat(new_fd, &new_stat) < 0) + goto do_rename; + + if (new_stat.st_ino == old_stat.st_ino && + new_stat.st_dev == old_stat.st_dev) + goto is_same; + + if (old_stat.st_mode != new_stat.st_mode || + old_stat.st_size != new_stat.st_size || + old_stat.st_uid != new_stat.st_uid || + old_stat.st_gid != new_stat.st_gid) + goto do_rename; + + for (;;) { + char buf1[16*1024]; + char buf2[sizeof(buf1) + 1]; + ssize_t l1, l2; + + l1 = read(old_fd, buf1, sizeof(buf1)); + if (l1 < 0) + goto do_rename; + + l2 = read(new_fd, buf2, l1 + 1); + if (l1 != l2) + goto do_rename; + + if (l1 == 0) /* EOF on both! And everything's the same so far, yay! */ + break; + + if (memcmp(buf1, buf2, l1) != 0) + goto do_rename; + } + +is_same: + /* Everything matches? Then don't rename, instead remove the source file, and leave the existing + * destination in place */ + + if (unlinkat(olddirfd, oldpath, 0) < 0) + goto do_rename; + + return 0; + +do_rename: + if (renameat(olddirfd, oldpath, newdirfd, newpath) < 0) + return -errno; + + return 1; +} #endif /* NM_IGNORED */ diff --git a/shared/systemd/src/basic/fs-util.h b/shared/systemd/src/basic/fs-util.h index 241cc6ef..9a394735 100644 --- a/shared/systemd/src/basic/fs-util.h +++ b/shared/systemd/src/basic/fs-util.h @@ -1,4 +1,4 @@ -/* SPDX-License-Identifier: LGPL-2.1+ */ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ #pragma once #include <dirent.h> @@ -132,3 +132,5 @@ int syncfs_path(int atfd, const char *path); int open_parent(const char *path, int flags, mode_t mode); int path_is_encrypted(const char *path); + +int conservative_rename(int olddirfd, const char *oldpath, int newdirfd, const char *newpath); diff --git a/shared/systemd/src/basic/hash-funcs.c b/shared/systemd/src/basic/hash-funcs.c index b1c19c95..6f540b29 100644 --- a/shared/systemd/src/basic/hash-funcs.c +++ b/shared/systemd/src/basic/hash-funcs.c @@ -1,4 +1,4 @@ -/* SPDX-License-Identifier: LGPL-2.1+ */ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ #include "nm-sd-adapt-shared.h" @@ -75,6 +75,19 @@ const struct hash_ops trivial_hash_ops = { .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, +}; + void uint64_hash_func(const uint64_t *p, struct siphash *state) { siphash24_compress(p, sizeof(uint64_t), state); } diff --git a/shared/systemd/src/basic/hash-funcs.h b/shared/systemd/src/basic/hash-funcs.h index 005d1b21..5672df1d 100644 --- a/shared/systemd/src/basic/hash-funcs.h +++ b/shared/systemd/src/basic/hash-funcs.h @@ -1,4 +1,4 @@ -/* SPDX-License-Identifier: LGPL-2.1+ */ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ #pragma once #include "alloc-util.h" @@ -88,6 +88,8 @@ extern const struct hash_ops path_hash_ops_free; 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_free_free; /* 32bit values we can always just embed in the pointer itself, but in order to support 32bit archs we need store 64bit * values indirectly, since they don't fit in a pointer. */ diff --git a/shared/systemd/src/basic/hashmap.c b/shared/systemd/src/basic/hashmap.c index b57814e6..0a5deabf 100644 --- a/shared/systemd/src/basic/hashmap.c +++ b/shared/systemd/src/basic/hashmap.c @@ -1,4 +1,4 @@ -/* SPDX-License-Identifier: LGPL-2.1+ */ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ #include "nm-sd-adapt-shared.h" @@ -1797,10 +1797,10 @@ int set_consume(Set *s, void *value) { } #if 0 /* NM_IGNORED */ -int _hashmap_put_strdup(Hashmap **h, const char *k, const char *v HASHMAP_DEBUG_PARAMS) { +int _hashmap_put_strdup_full(Hashmap **h, const struct hash_ops *hash_ops, const char *k, const char *v HASHMAP_DEBUG_PARAMS) { int r; - r = _hashmap_ensure_allocated(h, &string_hash_ops_free_free HASHMAP_DEBUG_PASS_ARGS); + r = _hashmap_ensure_allocated(h, hash_ops HASHMAP_DEBUG_PASS_ARGS); if (r < 0) return r; @@ -1832,14 +1832,14 @@ int _hashmap_put_strdup(Hashmap **h, const char *k, const char *v HASHMAP_DEBUG } #endif /* NM_IGNORED */ -int _set_put_strdup(Set **s, const char *p HASHMAP_DEBUG_PARAMS) { +int _set_put_strdup_full(Set **s, const struct hash_ops *hash_ops, const char *p HASHMAP_DEBUG_PARAMS) { char *c; int r; assert(s); assert(p); - r = _set_ensure_allocated(s, &string_hash_ops_free HASHMAP_DEBUG_PASS_ARGS); + r = _set_ensure_allocated(s, hash_ops HASHMAP_DEBUG_PASS_ARGS); if (r < 0) return r; @@ -1853,14 +1853,14 @@ int _set_put_strdup(Set **s, const char *p HASHMAP_DEBUG_PARAMS) { return set_consume(*s, c); } -int _set_put_strdupv(Set **s, char **l HASHMAP_DEBUG_PARAMS) { +int _set_put_strdupv_full(Set **s, const struct hash_ops *hash_ops, char **l HASHMAP_DEBUG_PARAMS) { int n = 0, r; char **i; assert(s); STRV_FOREACH(i, l) { - r = _set_put_strdup(s, *i HASHMAP_DEBUG_PASS_ARGS); + r = _set_put_strdup_full(s, hash_ops, *i HASHMAP_DEBUG_PASS_ARGS); if (r < 0) return r; @@ -1980,3 +1980,53 @@ IteratedCache* iterated_cache_free(IteratedCache *cache) { return mfree(cache); } + +int set_strjoin(Set *s, const char *separator, bool wrap_with_separator, char **ret) { + size_t separator_len, allocated = 0, len = 0; + _cleanup_free_ char *str = NULL; + const char *value; + bool first; + + assert(ret); + + if (set_isempty(s)) { + *ret = NULL; + return 0; + } + + separator_len = strlen_ptr(separator); + + if (separator_len == 0) + wrap_with_separator = false; + + first = !wrap_with_separator; + + SET_FOREACH(value, s) { + size_t l = strlen_ptr(value); + + if (l == 0) + continue; + + if (!GREEDY_REALLOC(str, allocated, len + l + (first ? 0 : separator_len) + (wrap_with_separator ? separator_len : 0) + 1)) + return -ENOMEM; + + if (separator_len > 0 && !first) { + memcpy(str + len, separator, separator_len); + len += separator_len; + } + + memcpy(str + len, value, l); + len += l; + first = false; + } + + if (wrap_with_separator) { + memcpy(str + len, separator, separator_len); + len += separator_len; + } + + str[len] = '\0'; + + *ret = TAKE_PTR(str); + return 0; +} diff --git a/shared/systemd/src/basic/hashmap.h b/shared/systemd/src/basic/hashmap.h index 890f90a9..e9944837 100644 --- a/shared/systemd/src/basic/hashmap.h +++ b/shared/systemd/src/basic/hashmap.h @@ -1,4 +1,4 @@ -/* SPDX-License-Identifier: LGPL-2.1+ */ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ #pragma once #include <limits.h> @@ -153,8 +153,9 @@ static inline int ordered_hashmap_put(OrderedHashmap *h, const void *key, void * return hashmap_put(PLAIN_HASHMAP(h), key, value); } -int _hashmap_put_strdup(Hashmap **h, const char *k, const char *v HASHMAP_DEBUG_PARAMS); -#define hashmap_put_strdup(h, k, v) _hashmap_put_strdup(h, k, v HASHMAP_DEBUG_SRC_ARGS) +int _hashmap_put_strdup_full(Hashmap **h, const struct hash_ops *hash_ops, const char *k, const char *v HASHMAP_DEBUG_PARAMS); +#define hashmap_put_strdup_full(h, hash_ops, k, v) _hashmap_put_strdup_full(h, hash_ops, k, v HASHMAP_DEBUG_SRC_ARGS) +#define hashmap_put_strdup(h, k, v) hashmap_put_strdup_full(h, &string_hash_ops_free_free, k, v) int hashmap_update(Hashmap *h, const void *key, void *value); static inline int ordered_hashmap_update(OrderedHashmap *h, const void *key, void *value) { diff --git a/shared/systemd/src/basic/hexdecoct.c b/shared/systemd/src/basic/hexdecoct.c index 1fb52305..78930b32 100644 --- a/shared/systemd/src/basic/hexdecoct.c +++ b/shared/systemd/src/basic/hexdecoct.c @@ -1,4 +1,4 @@ -/* SPDX-License-Identifier: LGPL-2.1+ */ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ #include "nm-sd-adapt-shared.h" diff --git a/shared/systemd/src/basic/hexdecoct.h b/shared/systemd/src/basic/hexdecoct.h index dfdff1e9..7e2a6892 100644 --- a/shared/systemd/src/basic/hexdecoct.h +++ b/shared/systemd/src/basic/hexdecoct.h @@ -1,4 +1,4 @@ -/* SPDX-License-Identifier: LGPL-2.1+ */ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ #pragma once #include <stdbool.h> diff --git a/shared/systemd/src/basic/hostname-util.c b/shared/systemd/src/basic/hostname-util.c index 82fc56db..a3cdc62e 100644 --- a/shared/systemd/src/basic/hostname-util.c +++ b/shared/systemd/src/basic/hostname-util.c @@ -1,4 +1,4 @@ -/* SPDX-License-Identifier: LGPL-2.1+ */ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ #include "nm-sd-adapt-shared.h" @@ -9,29 +9,11 @@ #include <unistd.h> #include "alloc-util.h" -#include "fd-util.h" -#include "fileio.h" #include "hostname-util.h" -#include "macro.h" #include "string-util.h" #include "strv.h" #if 0 /* NM_IGNORED */ -bool hostname_is_set(void) { - struct utsname u; - - assert_se(uname(&u) >= 0); - - if (isempty(u.nodename)) - return false; - - /* This is the built-in kernel default hostname */ - if (streq(u.nodename, "(none)")) - return false; - - return true; -} - char* gethostname_malloc(void) { struct utsname u; const char *s; @@ -93,6 +75,8 @@ int gethostname_strict(char **ret) { } bool valid_ldh_char(char c) { + /* "LDH" → "Letters, digits, hyphens", as per RFC 5890, Section 2.3.1 */ + return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || @@ -100,28 +84,24 @@ bool valid_ldh_char(char c) { c == '-'; } -/** - * Check if s looks like a valid hostname or FQDN. This does not do - * full DNS validation, but only checks if the name is composed of - * allowed characters and the length is not above the maximum allowed - * by Linux (c.f. dns_name_is_valid()). Trailing dot is allowed if - * allow_trailing_dot is true and at least two components are present - * in the name. Note that due to the restricted charset and length - * this call is substantially more conservative than - * dns_name_is_valid(). - */ -bool hostname_is_valid(const char *s, bool allow_trailing_dot) { +bool hostname_is_valid(const char *s, ValidHostnameFlags flags) { unsigned n_dots = 0; const char *p; bool dot, hyphen; + /* Check if s looks like a valid hostname or FQDN. This does not do full DNS validation, but only + * checks if the name is composed of allowed characters and the length is not above the maximum + * allowed by Linux (c.f. dns_name_is_valid()). A trailing dot is allowed if + * VALID_HOSTNAME_TRAILING_DOT flag is set and at least two components are present in the name. Note + * that due to the restricted charset and length this call is substantially more conservative than + * dns_name_is_valid(). Doesn't accept empty hostnames, hostnames with leading dots, and hostnames + * with multiple dots in a sequence. Doesn't allow hyphens at the beginning or end of label. */ + if (isempty(s)) return false; - /* Doesn't accept empty hostnames, hostnames with - * leading dots, and hostnames with multiple dots in a - * sequence. Also ensures that the length stays below - * HOST_NAME_MAX. */ + if (streq(s, ".host")) /* Used by the container logic to denote the "root container" */ + return FLAGS_SET(flags, VALID_HOSTNAME_DOT_HOST); for (p = s, dot = hyphen = true; *p; p++) if (*p == '.') { @@ -147,14 +127,13 @@ bool hostname_is_valid(const char *s, bool allow_trailing_dot) { hyphen = false; } - if (dot && (n_dots < 2 || !allow_trailing_dot)) + if (dot && (n_dots < 2 || !FLAGS_SET(flags, VALID_HOSTNAME_TRAILING_DOT))) return false; if (hyphen) return false; - if (p-s > HOST_NAME_MAX) /* Note that HOST_NAME_MAX is 64 on - * Linux, but DNS allows domain names - * up to 255 characters */ + if (p-s > HOST_NAME_MAX) /* Note that HOST_NAME_MAX is 64 on Linux, but DNS allows domain names up to + * 255 characters */ return false; return true; @@ -215,121 +194,3 @@ bool is_localhost(const char *hostname) { endswith_no_case(hostname, ".localhost.localdomain") || endswith_no_case(hostname, ".localhost.localdomain."); } - -#if 0 /* NM_IGNORED */ -bool is_gateway_hostname(const char *hostname) { - assert(hostname); - - /* This tries to identify the valid syntaxes for the our - * synthetic "gateway" host. */ - - return - strcaseeq(hostname, "_gateway") || strcaseeq(hostname, "_gateway.") -#if ENABLE_COMPAT_GATEWAY_HOSTNAME - || strcaseeq(hostname, "gateway") || strcaseeq(hostname, "gateway.") -#endif - ; -} - -int sethostname_idempotent(const char *s) { - char buf[HOST_NAME_MAX + 1] = {}; - - assert(s); - - if (gethostname(buf, sizeof(buf)) < 0) - return -errno; - - if (streq(buf, s)) - return 0; - - if (sethostname(s, strlen(s)) < 0) - return -errno; - - return 1; -} - -int shorten_overlong(const char *s, char **ret) { - char *h, *p; - - /* Shorten an overlong name to HOST_NAME_MAX or to the first dot, - * whatever comes earlier. */ - - assert(s); - - h = strdup(s); - if (!h) - return -ENOMEM; - - if (hostname_is_valid(h, false)) { - *ret = h; - return 0; - } - - p = strchr(h, '.'); - if (p) - *p = 0; - - strshorten(h, HOST_NAME_MAX); - - if (!hostname_is_valid(h, false)) { - free(h); - return -EDOM; - } - - *ret = h; - return 1; -} - -int read_etc_hostname_stream(FILE *f, char **ret) { - int r; - - assert(f); - assert(ret); - - for (;;) { - _cleanup_free_ char *line = NULL; - char *p; - - r = read_line(f, LONG_LINE_MAX, &line); - if (r < 0) - return r; - if (r == 0) /* EOF without any hostname? the file is empty, let's treat that exactly like no file at all: ENOENT */ - return -ENOENT; - - p = strstrip(line); - - /* File may have empty lines or comments, ignore them */ - if (!IN_SET(*p, '\0', '#')) { - char *copy; - - hostname_cleanup(p); /* normalize the hostname */ - - if (!hostname_is_valid(p, true)) /* check that the hostname we return is valid */ - return -EBADMSG; - - copy = strdup(p); - if (!copy) - return -ENOMEM; - - *ret = copy; - return 0; - } - } -} - -int read_etc_hostname(const char *path, char **ret) { - _cleanup_fclose_ FILE *f = NULL; - - assert(ret); - - if (!path) - path = "/etc/hostname"; - - f = fopen(path, "re"); - if (!f) - return -errno; - - return read_etc_hostname_stream(f, ret); - -} -#endif /* NM_IGNORED */ diff --git a/shared/systemd/src/basic/hostname-util.h b/shared/systemd/src/basic/hostname-util.h index cafd6f02..6cff9c1d 100644 --- a/shared/systemd/src/basic/hostname-util.h +++ b/shared/systemd/src/basic/hostname-util.h @@ -1,29 +1,29 @@ -/* SPDX-License-Identifier: LGPL-2.1+ */ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ #pragma once #include <stdbool.h> #include <stdio.h> #include "macro.h" - -bool hostname_is_set(void); +#include "strv.h" char* gethostname_malloc(void); char* gethostname_short_malloc(void); int gethostname_strict(char **ret); bool valid_ldh_char(char c) _const_; -bool hostname_is_valid(const char *s, bool allow_trailing_dot) _pure_; -char* hostname_cleanup(char *s); - -#define machine_name_is_valid(s) hostname_is_valid(s, false) -bool is_localhost(const char *hostname); -bool is_gateway_hostname(const char *hostname); +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 */ +} ValidHostnameFlags; -int sethostname_idempotent(const char *s); +bool hostname_is_valid(const char *s, ValidHostnameFlags flags) _pure_; +char* hostname_cleanup(char *s); -int shorten_overlong(const char *s, char **ret); +bool is_localhost(const char *hostname); -int read_etc_hostname_stream(FILE *f, char **ret); -int read_etc_hostname(const char *path, char **ret); +static inline bool is_gateway_hostname(const char *hostname) { + /* This tries to identify the valid syntaxes for the our synthetic "gateway" host. */ + return STRCASE_IN_SET(hostname, "_gateway", "_gateway."); +} diff --git a/shared/systemd/src/basic/in-addr-util.c b/shared/systemd/src/basic/in-addr-util.c index 1ea3e7ff..c315dcbb 100644 --- a/shared/systemd/src/basic/in-addr-util.c +++ b/shared/systemd/src/basic/in-addr-util.c @@ -1,4 +1,4 @@ -/* SPDX-License-Identifier: LGPL-2.1+ */ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ #include "nm-sd-adapt-shared.h" diff --git a/shared/systemd/src/basic/in-addr-util.h b/shared/systemd/src/basic/in-addr-util.h index 45c93a00..24308b70 100644 --- a/shared/systemd/src/basic/in-addr-util.h +++ b/shared/systemd/src/basic/in-addr-util.h @@ -1,4 +1,4 @@ -/* SPDX-License-Identifier: LGPL-2.1+ */ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ #pragma once #include <netinet/in.h> diff --git a/shared/systemd/src/basic/io-util.c b/shared/systemd/src/basic/io-util.c index 8b0e354a..f09c7fdd 100644 --- a/shared/systemd/src/basic/io-util.c +++ b/shared/systemd/src/basic/io-util.c @@ -1,4 +1,4 @@ -/* SPDX-License-Identifier: LGPL-2.1+ */ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ #include "nm-sd-adapt-shared.h" @@ -298,7 +298,7 @@ int iovw_put(struct iovec_wrapper *iovw, void *data, size_t len) { return -E2BIG; if (!GREEDY_REALLOC(iovw->iovec, iovw->size_bytes, iovw->count + 1)) - return log_oom(); + return -ENOMEM; iovw->iovec[iovw->count++] = IOVEC_MAKE(data, len); return 0; @@ -310,7 +310,7 @@ int iovw_put_string_field(struct iovec_wrapper *iovw, const char *field, const c x = strjoin(field, value); if (!x) - return log_oom(); + return -ENOMEM; r = iovw_put(iovw, x, strlen(x)); if (r >= 0) diff --git a/shared/systemd/src/basic/io-util.h b/shared/systemd/src/basic/io-util.h index 719e19e8..d817714b 100644 --- a/shared/systemd/src/basic/io-util.h +++ b/shared/systemd/src/basic/io-util.h @@ -1,4 +1,4 @@ -/* SPDX-License-Identifier: LGPL-2.1+ */ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ #pragma once #include <stdbool.h> diff --git a/shared/systemd/src/basic/list.h b/shared/systemd/src/basic/list.h index b62c3749..256b7187 100644 --- a/shared/systemd/src/basic/list.h +++ b/shared/systemd/src/basic/list.h @@ -1,4 +1,4 @@ -/* SPDX-License-Identifier: LGPL-2.1+ */ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ #pragma once #include "macro.h" diff --git a/shared/systemd/src/basic/log.h b/shared/systemd/src/basic/log.h index c2ffbb5d..ee2e4839 100644 --- a/shared/systemd/src/basic/log.h +++ b/shared/systemd/src/basic/log.h @@ -1,4 +1,4 @@ -/* SPDX-License-Identifier: LGPL-2.1+ */ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ #pragma once #include <stdarg.h> @@ -44,10 +44,17 @@ typedef enum LogTarget{ #define ERRNO_VALUE(val) (abs(val) & 255) void log_set_target(LogTarget target); + void log_set_max_level_realm(LogRealm realm, int level); + #define log_set_max_level(level) \ log_set_max_level_realm(LOG_REALM, (level)) +static inline void log_set_max_level_all_realms(int level) { + for (LogRealm realm = 0; realm < _LOG_REALM_MAX; realm++) + log_set_max_level_realm(realm, level); +} + void log_set_facility(int facility); int log_set_target_from_string(const char *e); @@ -122,6 +129,31 @@ int log_internal_realm( #define log_internal(level, ...) \ log_internal_realm(LOG_REALM_PLUS_LEVEL(LOG_REALM, (level)), __VA_ARGS__) +#define log_object_internal(level, \ + error, \ + file, \ + line, \ + func, \ + object_field, \ + object, \ + extra_field, \ + extra, \ + format, \ + ...) \ + ({ \ + const char *const _object = (object); \ + \ + log_internal_realm((level), \ + (error), \ + file, \ + (line), \ + (func), \ + "%s%s" format, \ + _object ?: "", \ + _object ? ": " : "", \ + ##__VA_ARGS__); \ + }) + #if 0 /* NM_IGNORED */ int log_internalv_realm( int level, @@ -169,7 +201,7 @@ int log_struct_internal( const char *format, ...) _printf_(6,0) _sentinel_; int log_oom_internal( - LogRealm realm, + int level, const char *file, int line, const char *func); @@ -293,7 +325,8 @@ int log_emergency_level(void); log_dump_internal(LOG_REALM_PLUS_LEVEL(LOG_REALM, level), \ 0, PROJECT_FILE, __LINE__, __func__, buffer) -#define log_oom() log_oom_internal(LOG_REALM, PROJECT_FILE, __LINE__, __func__) +#define log_oom() log_oom_internal(LOG_REALM_PLUS_LEVEL(LOG_REALM, LOG_ERR), PROJECT_FILE, __LINE__, __func__) +#define log_oom_debug() log_oom_internal(LOG_REALM_PLUS_LEVEL(LOG_REALM, LOG_DEBUG), PROJECT_FILE, __LINE__, __func__) bool log_on_console(void) _pure_; diff --git a/shared/systemd/src/basic/macro.h b/shared/systemd/src/basic/macro.h index f872d41f..34416b3d 100644 --- a/shared/systemd/src/basic/macro.h +++ b/shared/systemd/src/basic/macro.h @@ -1,4 +1,4 @@ -/* SPDX-License-Identifier: LGPL-2.1+ */ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ #pragma once #include <assert.h> @@ -94,6 +94,10 @@ #if (defined (__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6))) || defined (__clang__) /* Temporarily disable some warnings */ +#define DISABLE_WARNING_DEPRECATED_DECLARATIONS \ + _Pragma("GCC diagnostic push"); \ + _Pragma("GCC diagnostic ignored \"-Wdeprecated-declarations\"") + #define DISABLE_WARNING_FORMAT_NONLITERAL \ _Pragma("GCC diagnostic push"); \ _Pragma("GCC diagnostic ignored \"-Wformat-nonliteral\"") @@ -286,6 +290,12 @@ static inline size_t GREEDY_ALLOC_ROUND_UP(size_t l) { MAX(_c, z); \ }) +#define MAX4(x, y, z, a) \ + ({ \ + const typeof(x) _d = MAX3(x, y, z); \ + MAX(_d, a); \ + }) + #undef MIN #define MIN(a, b) __MIN(UNIQ, (a), UNIQ, (b)) #define __MIN(aq, a, bq, b) \ @@ -449,6 +459,9 @@ static inline int __coverity_check_and_return__(int condition) { #define PTR_TO_ULONG(p) ((unsigned long) ((uintptr_t) (p))) #define ULONG_TO_PTR(u) ((void *) ((uintptr_t) (u))) +#define PTR_TO_UINT8(p) ((uint8_t) ((uintptr_t) (p))) +#define UINT8_TO_PTR(u) ((void *) ((uintptr_t) (u))) + #define PTR_TO_INT32(p) ((int32_t) ((intptr_t) (p))) #define INT32_TO_PTR(u) ((void *) ((intptr_t) (u))) #define PTR_TO_UINT32(p) ((uint32_t) ((uintptr_t) (p))) @@ -550,10 +563,13 @@ static inline int __coverity_check_and_return__(int condition) { #define STRV_MAKE(...) ((char**) ((const char*[]) { __VA_ARGS__, NULL })) #define STRV_MAKE_EMPTY ((char*[1]) { NULL }) -/* Iterates through a specified list of pointers. Accepts NULL pointers, but uses (void*) -1 as internal marker for EOL. */ -#define FOREACH_POINTER(p, x, ...) \ - for (typeof(p) *_l = (typeof(p)[]) { ({ p = x; }), ##__VA_ARGS__, (void*) -1 }; \ - p != (typeof(p)) (void*) -1; \ +/* Pointers range from NULL to POINTER_MAX */ +#define POINTER_MAX ((void*) UINTPTR_MAX) + +/* Iterates through a specified list of pointers. Accepts NULL pointers, but uses POINTER_MAX as internal marker for EOL. */ +#define FOREACH_POINTER(p, x, ...) \ + for (typeof(p) *_l = (typeof(p)[]) { ({ p = x; }), ##__VA_ARGS__, POINTER_MAX }; \ + p != (typeof(p)) POINTER_MAX; \ p = *(++_l)) /* Define C11 thread_local attribute even on older gcc compiler @@ -643,4 +659,8 @@ static inline int __coverity_check_and_return__(int condition) { _copy; \ }) +static inline size_t size_add(size_t x, size_t y) { + return y >= SIZE_MAX - x ? SIZE_MAX : x + y; +} + #include "log.h" diff --git a/shared/systemd/src/basic/memory-util.c b/shared/systemd/src/basic/memory-util.c index bd1f5d73..7ee7c94e 100644 --- a/shared/systemd/src/basic/memory-util.c +++ b/shared/systemd/src/basic/memory-util.c @@ -1,3 +1,5 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ + #include "nm-sd-adapt-shared.h" #include <unistd.h> diff --git a/shared/systemd/src/basic/memory-util.h b/shared/systemd/src/basic/memory-util.h index 4f596cff..179edd24 100644 --- a/shared/systemd/src/basic/memory-util.h +++ b/shared/systemd/src/basic/memory-util.h @@ -1,4 +1,4 @@ -/* SPDX-License-Identifier: LGPL-2.1+ */ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ #pragma once #include <inttypes.h> diff --git a/shared/systemd/src/basic/mempool.c b/shared/systemd/src/basic/mempool.c index 8b8337db..46c44914 100644 --- a/shared/systemd/src/basic/mempool.c +++ b/shared/systemd/src/basic/mempool.c @@ -1,4 +1,4 @@ -/* SPDX-License-Identifier: LGPL-2.1+ */ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ #include "nm-sd-adapt-shared.h" diff --git a/shared/systemd/src/basic/mempool.h b/shared/systemd/src/basic/mempool.h index 0eecca0f..0fe2f278 100644 --- a/shared/systemd/src/basic/mempool.h +++ b/shared/systemd/src/basic/mempool.h @@ -1,4 +1,4 @@ -/* SPDX-License-Identifier: LGPL-2.1+ */ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ #pragma once #include <stdbool.h> diff --git a/shared/systemd/src/basic/missing_fcntl.h b/shared/systemd/src/basic/missing_fcntl.h index 5d1c6352..00937d2a 100644 --- a/shared/systemd/src/basic/missing_fcntl.h +++ b/shared/systemd/src/basic/missing_fcntl.h @@ -1,4 +1,4 @@ -/* SPDX-License-Identifier: LGPL-2.1+ */ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ #pragma once #include <fcntl.h> diff --git a/shared/systemd/src/basic/missing_random.h b/shared/systemd/src/basic/missing_random.h index 17af87a3..443b9136 100644 --- a/shared/systemd/src/basic/missing_random.h +++ b/shared/systemd/src/basic/missing_random.h @@ -1,4 +1,4 @@ -/* SPDX-License-Identifier: LGPL-2.1+ */ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ #pragma once #if USE_SYS_RANDOM_H diff --git a/shared/systemd/src/basic/missing_socket.h b/shared/systemd/src/basic/missing_socket.h index fe4d35bd..a4f6836f 100644 --- a/shared/systemd/src/basic/missing_socket.h +++ b/shared/systemd/src/basic/missing_socket.h @@ -1,4 +1,4 @@ -/* SPDX-License-Identifier: LGPL-2.1+ */ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ #pragma once #include <sys/socket.h> @@ -69,6 +69,14 @@ struct sockaddr_vm { #define IPV6_FREEBIND 78 #endif +#ifndef IP_RECVFRAGSIZE +#define IP_RECVFRAGSIZE 25 +#endif + +#ifndef IPV6_RECVFRAGSIZE +#define IPV6_RECVFRAGSIZE 77 +#endif + /* linux/sockios.h */ #ifndef SIOCGSKNS #define SIOCGSKNS 0x894C diff --git a/shared/systemd/src/basic/missing_stat.h b/shared/systemd/src/basic/missing_stat.h index 5d59a214..9c1df698 100644 --- a/shared/systemd/src/basic/missing_stat.h +++ b/shared/systemd/src/basic/missing_stat.h @@ -1,4 +1,4 @@ -/* SPDX-License-Identifier: LGPL-2.1+ */ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ #pragma once #include <linux/types.h> diff --git a/shared/systemd/src/basic/missing_syscall.h b/shared/systemd/src/basic/missing_syscall.h index d11a77d5..42d64753 100644 --- a/shared/systemd/src/basic/missing_syscall.h +++ b/shared/systemd/src/basic/missing_syscall.h @@ -1,4 +1,4 @@ -/* SPDX-License-Identifier: LGPL-2.1+ */ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ #pragma once /* Missing glibc definitions to access certain kernel APIs */ @@ -15,6 +15,26 @@ #include <asm/sgidefs.h> #endif +#if defined(__alpha__) +# define systemd_SC_arch_bias(x) (110 + (x)) +#elif defined(__ia64__) +# define systemd_SC_arch_bias(x) (1024 + (x)) +#elif defined(_MIPS_SIM) +# if _MIPS_SIM == _MIPS_SIM_ABI32 +# define systemd_SC_arch_bias(x) (4000 + (x)) +# elif _MIPS_SIM == _MIPS_SIM_NABI32 +# define systemd_SC_arch_bias(x) (6000 + (x)) +# elif _MIPS_SIM == _MIPS_SIM_ABI64 +# define systemd_SC_arch_bias(x) (5000 + (x)) +# else +# error "Unknown MIPS ABI" +# endif +#elif defined(__x86_64__) && defined(__ILP32__) +# define systemd_SC_arch_bias(x) ((x) | /* __X32_SYSCALL_BIT */ 0x40000000) +#else +# define systemd_SC_arch_bias(x) (x) +#endif + #include "missing_keyctl.h" #include "missing_stat.h" @@ -35,32 +55,38 @@ static inline int missing_pivot_root(const char *new_root, const char *put_old) /* ======================================================================= */ -#if defined __x86_64__ -# define systemd_NR_memfd_create 319 -#elif defined __arm__ -# define systemd_NR_memfd_create 385 -#elif defined __aarch64__ +#if defined(__aarch64__) # define systemd_NR_memfd_create 279 +#elif defined(__alpha__) +# define systemd_NR_memfd_create 512 +#elif defined(__arc__) || defined(__tilegx__) +# define systemd_NR_memfd_create 279 +#elif defined(__arm__) +# define systemd_NR_memfd_create 385 +#elif defined(__i386__) +# define systemd_NR_memfd_create 356 +#elif defined(__ia64__) +# define systemd_NR_memfd_create systemd_SC_arch_bias(316) +#elif defined(__m68k__) +# define systemd_NR_memfd_create 353 +#elif defined(_MIPS_SIM) +# if _MIPS_SIM == _MIPS_SIM_ABI32 +# define systemd_NR_memfd_create systemd_SC_arch_bias(354) +# elif _MIPS_SIM == _MIPS_SIM_NABI32 +# define systemd_NR_memfd_create systemd_SC_arch_bias(318) +# elif _MIPS_SIM == _MIPS_SIM_ABI64 +# define systemd_NR_memfd_create systemd_SC_arch_bias(314) +# endif #elif defined(__powerpc__) # define systemd_NR_memfd_create 360 -#elif defined __s390__ +#elif defined(__s390__) # define systemd_NR_memfd_create 350 -#elif defined _MIPS_SIM -# if _MIPS_SIM == _MIPS_SIM_ABI32 -# define systemd_NR_memfd_create 4354 -# endif -# if _MIPS_SIM == _MIPS_SIM_NABI32 -# define systemd_NR_memfd_create 6318 -# endif -# if _MIPS_SIM == _MIPS_SIM_ABI64 -# define systemd_NR_memfd_create 5314 -# endif -#elif defined __i386__ -# define systemd_NR_memfd_create 356 -#elif defined __arc__ -# define systemd_NR_memfd_create 279 +#elif defined(__sparc__) +# define systemd_NR_memfd_create 348 +#elif defined(__x86_64__) +# define systemd_NR_memfd_create systemd_SC_arch_bias(319) #else -# warning "memfd_create() syscall number unknown for your architecture" +# warning "memfd_create() syscall number is unknown for your architecture" #endif /* may be (invalid) negative number due to libseccomp, see PR 13319 */ @@ -92,36 +118,38 @@ static inline int missing_memfd_create(const char *name, unsigned int flags) { /* ======================================================================= */ -#if defined __x86_64__ -# define systemd_NR_getrandom 318 -#elif defined(__i386__) -# define systemd_NR_getrandom 355 +#if defined(__aarch64__) +# define systemd_NR_getrandom 278 +#elif defined(__alpha__) +# define systemd_NR_getrandom 511 +#elif defined(__arc__) || defined(__tilegx__) +# define systemd_NR_getrandom 278 #elif defined(__arm__) # define systemd_NR_getrandom 384 -#elif defined(__aarch64__) -# define systemd_NR_getrandom 278 +#elif defined(__i386__) +# define systemd_NR_getrandom 355 #elif defined(__ia64__) -# define systemd_NR_getrandom 1339 +# define systemd_NR_getrandom systemd_SC_arch_bias(318) #elif defined(__m68k__) # define systemd_NR_getrandom 352 -#elif defined(__s390x__) -# define systemd_NR_getrandom 349 -#elif defined(__powerpc__) -# define systemd_NR_getrandom 359 -#elif defined _MIPS_SIM +#elif defined(_MIPS_SIM) # if _MIPS_SIM == _MIPS_SIM_ABI32 -# define systemd_NR_getrandom 4353 -# endif -# if _MIPS_SIM == _MIPS_SIM_NABI32 -# define systemd_NR_getrandom 6317 +# define systemd_NR_getrandom systemd_SC_arch_bias(353) +# elif _MIPS_SIM == _MIPS_SIM_NABI32 +# define systemd_NR_getrandom systemd_SC_arch_bias(317) +# elif _MIPS_SIM == _MIPS_SIM_ABI64 +# define systemd_NR_getrandom systemd_SC_arch_bias(313) # endif -# if _MIPS_SIM == _MIPS_SIM_ABI64 -# define systemd_NR_getrandom 5313 -# endif -#elif defined(__arc__) -# define systemd_NR_getrandom 278 +#elif defined(__powerpc__) +# define systemd_NR_getrandom 359 +#elif defined(__s390__) +# define systemd_NR_getrandom 349 +#elif defined(__sparc__) +# define systemd_NR_getrandom 347 +#elif defined(__x86_64__) +# define systemd_NR_getrandom systemd_SC_arch_bias(318) #else -# warning "getrandom() syscall number unknown for your architecture" +# warning "getrandom() syscall number is unknown for your architecture" #endif /* may be (invalid) negative number due to libseccomp, see PR 13319 */ @@ -168,22 +196,38 @@ static inline pid_t missing_gettid(void) { /* ======================================================================= */ -#if defined(__x86_64__) -# define systemd_NR_name_to_handle_at 303 -#elif defined(__i386__) -# define systemd_NR_name_to_handle_at 341 +#if defined(__aarch64__) +# define systemd_NR_name_to_handle_at 264 +#elif defined(__alpha__) +# define systemd_NR_name_to_handle_at 497 +#elif defined(__arc__) || defined(__tilegx__) +# define systemd_NR_name_to_handle_at 264 #elif defined(__arm__) # define systemd_NR_name_to_handle_at 370 -#elif defined __aarch64__ -# define systemd_NR_name_to_handle_at 264 +#elif defined(__i386__) +# define systemd_NR_name_to_handle_at 341 +#elif defined(__ia64__) +# define systemd_NR_name_to_handle_at systemd_SC_arch_bias(302) +#elif defined(__m68k__) +# define systemd_NR_name_to_handle_at 340 +#elif defined(_MIPS_SIM) +# if _MIPS_SIM == _MIPS_SIM_ABI32 +# define systemd_NR_name_to_handle_at systemd_SC_arch_bias(339) +# elif _MIPS_SIM == _MIPS_SIM_NABI32 +# define systemd_NR_name_to_handle_at systemd_SC_arch_bias(303) +# elif _MIPS_SIM == _MIPS_SIM_ABI64 +# define systemd_NR_name_to_handle_at systemd_SC_arch_bias(298) +# endif #elif defined(__powerpc__) # define systemd_NR_name_to_handle_at 345 -#elif defined __s390__ || defined __s390x__ +#elif defined(__s390__) # define systemd_NR_name_to_handle_at 335 -#elif defined(__arc__) -# define systemd_NR_name_to_handle_at 264 +#elif defined(__sparc__) +# define systemd_NR_name_to_handle_at 332 +#elif defined(__x86_64__) +# define systemd_NR_name_to_handle_at systemd_SC_arch_bias(303) #else -# warning "name_to_handle_at number is not defined" +# warning "name_to_handle_at() syscall number is unknown for your architecture" #endif /* may be (invalid) negative number due to libseccomp, see PR 13319 */ @@ -221,22 +265,38 @@ static inline int missing_name_to_handle_at(int fd, const char *name, struct fil /* ======================================================================= */ -#if defined __aarch64__ +#if defined(__aarch64__) # define systemd_NR_setns 268 -#elif defined __arm__ +#elif defined(__alpha__) +# define systemd_NR_setns 501 +#elif defined(__arc__) || defined(__tilegx__) +# define systemd_NR_setns 268 +#elif defined(__arm__) # define systemd_NR_setns 375 -#elif defined(__x86_64__) -# define systemd_NR_setns 308 #elif defined(__i386__) # define systemd_NR_setns 346 +#elif defined(__ia64__) +# define systemd_NR_setns systemd_SC_arch_bias(306) +#elif defined(__m68k__) +# define systemd_NR_setns 344 +#elif defined(_MIPS_SIM) +# if _MIPS_SIM == _MIPS_SIM_ABI32 +# define systemd_NR_setns systemd_SC_arch_bias(344) +# elif _MIPS_SIM == _MIPS_SIM_NABI32 +# define systemd_NR_setns systemd_SC_arch_bias(308) +# elif _MIPS_SIM == _MIPS_SIM_ABI64 +# define systemd_NR_setns systemd_SC_arch_bias(303) +# endif #elif defined(__powerpc__) # define systemd_NR_setns 350 -#elif defined __s390__ || defined __s390x__ +#elif defined(__s390__) # define systemd_NR_setns 339 -#elif defined(__arc__) -# define systemd_NR_setns 268 +#elif defined(__sparc__) +# define systemd_NR_setns 337 +#elif defined(__x86_64__) +# define systemd_NR_setns systemd_SC_arch_bias(308) #else -# warning "setns() syscall number unknown for your architecture" +# warning "setns() syscall number is unknown for your architecture" #endif /* may be (invalid) negative number due to libseccomp, see PR 13319 */ @@ -278,32 +338,38 @@ static inline pid_t raw_getpid(void) { /* ======================================================================= */ -#if defined __x86_64__ -# define systemd_NR_renameat2 316 -#elif defined __arm__ -# define systemd_NR_renameat2 382 -#elif defined __aarch64__ +#if defined(__aarch64__) # define systemd_NR_renameat2 276 -#elif defined _MIPS_SIM +#elif defined(__alpha__) +# define systemd_NR_renameat2 510 +#elif defined(__arc__) || defined(__tilegx__) +# define systemd_NR_renameat2 276 +#elif defined(__arm__) +# define systemd_NR_renameat2 382 +#elif defined(__i386__) +# define systemd_NR_renameat2 353 +#elif defined(__ia64__) +# define systemd_NR_renameat2 systemd_SC_arch_bias(314) +#elif defined(__m68k__) +# define systemd_NR_renameat2 351 +#elif defined(_MIPS_SIM) # if _MIPS_SIM == _MIPS_SIM_ABI32 -# define systemd_NR_renameat2 4351 +# define systemd_NR_renameat2 systemd_SC_arch_bias(351) +# elif _MIPS_SIM == _MIPS_SIM_NABI32 +# define systemd_NR_renameat2 systemd_SC_arch_bias(315) +# elif _MIPS_SIM == _MIPS_SIM_ABI64 +# define systemd_NR_renameat2 systemd_SC_arch_bias(311) # endif -# if _MIPS_SIM == _MIPS_SIM_NABI32 -# define systemd_NR_renameat2 6315 -# endif -# if _MIPS_SIM == _MIPS_SIM_ABI64 -# define systemd_NR_renameat2 5311 -# endif -#elif defined __i386__ -# define systemd_NR_renameat2 353 -#elif defined __powerpc64__ +#elif defined(__powerpc__) # define systemd_NR_renameat2 357 -#elif defined __s390__ || defined __s390x__ +#elif defined(__s390__) # define systemd_NR_renameat2 347 -#elif defined __arc__ -# define systemd_NR_renameat2 276 +#elif defined(__sparc__) +# define systemd_NR_renameat2 345 +#elif defined(__x86_64__) +# define systemd_NR_renameat2 systemd_SC_arch_bias(316) #else -# warning "renameat2() syscall number unknown for your architecture" +# warning "renameat2() syscall number is unknown for your architecture" #endif /* may be (invalid) negative number due to libseccomp, see PR 13319 */ @@ -387,22 +453,38 @@ static inline key_serial_t missing_request_key(const char *type, const char *des /* ======================================================================= */ -#if defined(__x86_64__) -# define systemd_NR_copy_file_range 326 +#if defined(__aarch64__) +# define systemd_NR_copy_file_range 285 +#elif defined(__alpha__) +# define systemd_NR_copy_file_range 519 +#elif defined(__arc__) || defined(__tilegx__) +# define systemd_NR_copy_file_range 285 +#elif defined(__arm__) +# define systemd_NR_copy_file_range 391 #elif defined(__i386__) # define systemd_NR_copy_file_range 377 -#elif defined __s390__ -# define systemd_NR_copy_file_range 375 -#elif defined __arm__ -# define systemd_NR_copy_file_range 391 -#elif defined __aarch64__ -# define systemd_NR_copy_file_range 285 -#elif defined __powerpc__ +#elif defined(__ia64__) +# define systemd_NR_copy_file_range systemd_SC_arch_bias(323) +#elif defined(__m68k__) +# define systemd_NR_copy_file_range 376 +#elif defined(_MIPS_SIM) +# if _MIPS_SIM == _MIPS_SIM_ABI32 +# define systemd_NR_copy_file_range systemd_SC_arch_bias(360) +# elif _MIPS_SIM == _MIPS_SIM_NABI32 +# define systemd_NR_copy_file_range systemd_SC_arch_bias(324) +# elif _MIPS_SIM == _MIPS_SIM_ABI64 +# define systemd_NR_copy_file_range systemd_SC_arch_bias(320) +# endif +#elif defined(__powerpc__) # define systemd_NR_copy_file_range 379 -#elif defined __arc__ -# define systemd_NR_copy_file_range 285 +#elif defined(__s390__) +# define systemd_NR_copy_file_range 375 +#elif defined(__sparc__) +# define systemd_NR_copy_file_range 357 +#elif defined(__x86_64__) +# define systemd_NR_copy_file_range systemd_SC_arch_bias(326) #else -# warning "copy_file_range() syscall number unknown for your architecture" +# warning "copy_file_range() syscall number is unknown for your architecture" #endif /* may be (invalid) negative number due to libseccomp, see PR 13319 */ @@ -437,24 +519,38 @@ static inline ssize_t missing_copy_file_range(int fd_in, loff_t *off_in, /* ======================================================================= */ -#if defined __i386__ -# define systemd_NR_bpf 357 -#elif defined __x86_64__ -# define systemd_NR_bpf 321 -#elif defined __aarch64__ +#if defined(__aarch64__) +# define systemd_NR_bpf 280 +#elif defined(__alpha__) +# define systemd_NR_bpf 515 +#elif defined(__arc__) || defined(__tilegx__) # define systemd_NR_bpf 280 -#elif defined __arm__ +#elif defined(__arm__) # define systemd_NR_bpf 386 +#elif defined(__i386__) +# define systemd_NR_bpf 357 +#elif defined(__ia64__) +# define systemd_NR_bpf systemd_SC_arch_bias(317) +#elif defined(__m68k__) +# define systemd_NR_bpf 354 +#elif defined(_MIPS_SIM) +# if _MIPS_SIM == _MIPS_SIM_ABI32 +# define systemd_NR_bpf systemd_SC_arch_bias(355) +# elif _MIPS_SIM == _MIPS_SIM_NABI32 +# define systemd_NR_bpf systemd_SC_arch_bias(319) +# elif _MIPS_SIM == _MIPS_SIM_ABI64 +# define systemd_NR_bpf systemd_SC_arch_bias(315) +# endif #elif defined(__powerpc__) # define systemd_NR_bpf 361 -#elif defined __sparc__ -# define systemd_NR_bpf 349 -#elif defined __s390__ +#elif defined(__s390__) # define systemd_NR_bpf 351 -#elif defined __tilegx__ -# define systemd_NR_bpf 280 +#elif defined(__sparc__) +# define systemd_NR_bpf 349 +#elif defined(__x86_64__) +# define systemd_NR_bpf systemd_SC_arch_bias(321) #else -# warning "bpf() syscall number unknown for your architecture" +# warning "bpf() syscall number is unknown for your architecture" #endif /* may be (invalid) negative number due to libseccomp, see PR 13319 */ @@ -489,30 +585,38 @@ static inline int missing_bpf(int cmd, union bpf_attr *attr, size_t size) { /* ======================================================================= */ #ifndef __IGNORE_pkey_mprotect -# if defined __i386__ -# define systemd_NR_pkey_mprotect 380 -# elif defined __x86_64__ -# define systemd_NR_pkey_mprotect 329 -# elif defined __aarch64__ +# if defined(__aarch64__) # define systemd_NR_pkey_mprotect 288 -# elif defined __arm__ +# elif defined(__alpha__) +# define systemd_NR_pkey_mprotect 524 +# elif defined(__arc__) || defined(__tilegx__) +# define systemd_NR_pkey_mprotect 226 +# elif defined(__arm__) # define systemd_NR_pkey_mprotect 394 -# elif defined __powerpc__ -# define systemd_NR_pkey_mprotect 386 -# elif defined __s390__ -# define systemd_NR_pkey_mprotect 384 -# elif defined _MIPS_SIM +# elif defined(__i386__) +# define systemd_NR_pkey_mprotect 380 +# elif defined(__ia64__) +# define systemd_NR_pkey_mprotect systemd_SC_arch_bias(330) +# elif defined(__m68k__) +# define systemd_NR_pkey_mprotect 381 +# elif defined(_MIPS_SIM) # if _MIPS_SIM == _MIPS_SIM_ABI32 -# define systemd_NR_pkey_mprotect 4363 -# endif -# if _MIPS_SIM == _MIPS_SIM_NABI32 -# define systemd_NR_pkey_mprotect 6327 -# endif -# if _MIPS_SIM == _MIPS_SIM_ABI64 -# define systemd_NR_pkey_mprotect 5323 +# define systemd_NR_pkey_mprotect systemd_SC_arch_bias(363) +# elif _MIPS_SIM == _MIPS_SIM_NABI32 +# define systemd_NR_pkey_mprotect systemd_SC_arch_bias(327) +# elif _MIPS_SIM == _MIPS_SIM_ABI64 +# define systemd_NR_pkey_mprotect systemd_SC_arch_bias(323) # endif +# elif defined(__powerpc__) +# define systemd_NR_pkey_mprotect 386 +# elif defined(__s390__) +# define systemd_NR_pkey_mprotect 384 +# elif defined(__sparc__) +# define systemd_NR_pkey_mprotect 362 +# elif defined(__x86_64__) +# define systemd_NR_pkey_mprotect systemd_SC_arch_bias(329) # else -# warning "pkey_mprotect() syscall number unknown for your architecture" +# warning "pkey_mprotect() syscall number is unknown for your architecture" # endif /* may be (invalid) negative number due to libseccomp, see PR 13319 */ @@ -532,22 +636,38 @@ assert_cc(__NR_pkey_mprotect == systemd_NR_pkey_mprotect); /* ======================================================================= */ -#if defined __aarch64__ +#if defined(__aarch64__) # define systemd_NR_statx 291 -#elif defined __arm__ -# define systemd_NR_statx 397 -#elif defined __alpha__ +#elif defined(__alpha__) # define systemd_NR_statx 522 -#elif defined __i386__ || defined __powerpc64__ +#elif defined(__arc__) || defined(__tilegx__) +# define systemd_NR_statx 291 +#elif defined(__arm__) +# define systemd_NR_statx 397 +#elif defined(__i386__) +# define systemd_NR_statx 383 +#elif defined(__ia64__) +# define systemd_NR_statx systemd_SC_arch_bias(326) +#elif defined(__m68k__) +# define systemd_NR_statx 379 +#elif defined(_MIPS_SIM) +# if _MIPS_SIM == _MIPS_SIM_ABI32 +# define systemd_NR_statx systemd_SC_arch_bias(366) +# elif _MIPS_SIM == _MIPS_SIM_NABI32 +# define systemd_NR_statx systemd_SC_arch_bias(330) +# elif _MIPS_SIM == _MIPS_SIM_ABI64 +# define systemd_NR_statx systemd_SC_arch_bias(326) +# endif +#elif defined(__powerpc__) # define systemd_NR_statx 383 -#elif defined __s390__ || defined __s390x__ +#elif defined(__s390__) # define systemd_NR_statx 379 -#elif defined __sparc__ +#elif defined(__sparc__) # define systemd_NR_statx 360 -#elif defined __x86_64__ -# define systemd_NR_statx 332 +#elif defined(__x86_64__) +# define systemd_NR_statx systemd_SC_arch_bias(332) #else -# warning "statx() syscall number unknown for your architecture" +# warning "statx() syscall number is unknown for your architecture" #endif /* may be (invalid) negative number due to libseccomp, see PR 13319 */ @@ -632,23 +752,7 @@ static inline long missing_get_mempolicy(int *mode, unsigned long *nodemask, /* ======================================================================= */ /* should be always defined, see kernel 39036cd2727395c3369b1051005da74059a85317 */ -#if defined __alpha__ -# define systemd_NR_pidfd_send_signal 534 -#elif defined _MIPS_SIM -# if _MIPS_SIM == _MIPS_SIM_ABI32 /* o32 */ -# define systemd_NR_pidfd_send_signal (424 + 4000) -# endif -# if _MIPS_SIM == _MIPS_SIM_NABI32 /* n32 */ -# define systemd_NR_pidfd_send_signal (424 + 6000) -# endif -# if _MIPS_SIM == _MIPS_SIM_ABI64 /* n64 */ -# define systemd_NR_pidfd_send_signal (424 + 5000) -# endif -#elif defined __ia64__ -# define systemd_NR_pidfd_send_signal (424 + 1024) -#else -# define systemd_NR_pidfd_send_signal 424 -#endif +#define systemd_NR_pidfd_send_signal systemd_SC_arch_bias(424) /* may be (invalid) negative number due to libseccomp, see PR 13319 */ #if defined __NR_pidfd_send_signal && __NR_pidfd_send_signal >= 0 @@ -664,7 +768,7 @@ assert_cc(__NR_pidfd_send_signal == systemd_NR_pidfd_send_signal); #if !HAVE_PIDFD_SEND_SIGNAL static inline int missing_pidfd_send_signal(int fd, int sig, siginfo_t *info, unsigned flags) { -# ifdef __NR_pidfd_open +# ifdef __NR_pidfd_send_signal return syscall(__NR_pidfd_send_signal, fd, sig, info, flags); # else errno = ENOSYS; @@ -676,23 +780,7 @@ static inline int missing_pidfd_send_signal(int fd, int sig, siginfo_t *info, un #endif /* should be always defined, see kernel 7615d9e1780e26e0178c93c55b73309a5dc093d7 */ -#if defined __alpha__ -# define systemd_NR_pidfd_open 544 -#elif defined _MIPS_SIM -# if _MIPS_SIM == _MIPS_SIM_ABI32 /* o32 */ -# define systemd_NR_pidfd_open (434 + 4000) -# endif -# if _MIPS_SIM == _MIPS_SIM_NABI32 /* n32 */ -# define systemd_NR_pidfd_open (434 + 6000) -# endif -# if _MIPS_SIM == _MIPS_SIM_ABI64 /* n64 */ -# define systemd_NR_pidfd_open (434 + 5000) -# endif -#elif defined __ia64__ -# define systemd_NR_pidfd_open (434 + 1024) -#else -# define systemd_NR_pidfd_open 434 -#endif +#define systemd_NR_pidfd_open systemd_SC_arch_bias(434) /* may be (invalid) negative number due to libseccomp, see PR 13319 */ #if defined __NR_pidfd_open && __NR_pidfd_open >= 0 @@ -732,3 +820,70 @@ static inline int missing_rt_sigqueueinfo(pid_t tgid, int sig, siginfo_t *info) # define rt_sigqueueinfo missing_rt_sigqueueinfo #endif + +/* ======================================================================= */ + +#if 0 /* NM_IGNORED */ +#if !HAVE_EXECVEAT +static inline int missing_execveat(int dirfd, const char *pathname, + char *const argv[], char *const envp[], + int flags) { +# if defined __NR_execveat && __NR_execveat >= 0 + return syscall(__NR_execveat, dirfd, pathname, argv, envp, flags); +# else + errno = ENOSYS; + return -1; +# endif +} + +# undef AT_EMPTY_PATH +# define AT_EMPTY_PATH 0x1000 +# define execveat missing_execveat +#endif + +/* ======================================================================= */ + +#define systemd_NR_close_range systemd_SC_arch_bias(436) + +/* may be (invalid) negative number due to libseccomp, see PR 13319 */ +#if defined __NR_close_range && __NR_close_range >= 0 +# if defined systemd_NR_close_range +assert_cc(__NR_close_range == systemd_NR_close_range); +# endif +#else +# if defined __NR_close_range +# undef __NR_close_range +# endif +# if defined systemd_NR_close_range +# define __NR_close_range systemd_NR_close_range +# endif +#endif + +#if !HAVE_CLOSE_RANGE +static inline int missing_close_range(int first_fd, int end_fd, unsigned flags) { +# ifdef __NR_close_range + /* Kernel-side the syscall expects fds as unsigned integers (just like close() actually), while + * userspace exclusively uses signed integers for fds. We don't know just yet how glibc is going to + * wrap this syscall, but let's assume it's going to be similar to what they do for close(), + * i.e. make the same unsigned → signed type change from the raw kernel syscall compared to the + * userspace wrapper. There's only one caveat for this: unlike for close() there's the special + * UINT_MAX fd value for the 'end_fd' argument. Let's safely map that to -1 here. And let's refuse + * any other negative values. */ + if ((first_fd < 0) || (end_fd < 0 && end_fd != -1)) { + errno = -EBADF; + return -1; + } + + return syscall(__NR_close_range, + (unsigned) first_fd, + end_fd == -1 ? UINT_MAX : (unsigned) end_fd, /* Of course, the compiler should figure out that this is the identity mapping IRL */ + flags); +# else + errno = ENOSYS; + return -1; +# endif +} + +# define close_range missing_close_range +#endif +#endif /* NM_IGNORED */ diff --git a/shared/systemd/src/basic/missing_type.h b/shared/systemd/src/basic/missing_type.h index bf8a6caa..f6233090 100644 --- a/shared/systemd/src/basic/missing_type.h +++ b/shared/systemd/src/basic/missing_type.h @@ -1,4 +1,4 @@ -/* SPDX-License-Identifier: LGPL-2.1+ */ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ #pragma once #include <uchar.h> diff --git a/shared/systemd/src/basic/parse-util.c b/shared/systemd/src/basic/parse-util.c index 6b47317a..d53bf620 100644 --- a/shared/systemd/src/basic/parse-util.c +++ b/shared/systemd/src/basic/parse-util.c @@ -1,4 +1,4 @@ -/* SPDX-License-Identifier: LGPL-2.1+ */ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ #include "nm-sd-adapt-shared.h" @@ -867,4 +867,46 @@ int parse_oom_score_adjust(const char *s, int *ret) { *ret = v; return 0; } + +int store_loadavg_fixed_point(unsigned long i, unsigned long f, loadavg_t *ret) { + assert(ret); + + if (i >= (~0UL << FSHIFT)) + return -ERANGE; + + i = i << FSHIFT; + f = DIV_ROUND_UP((f << FSHIFT), 100); + + if (f >= FIXED_1) + return -ERANGE; + + *ret = i | f; + return 0; +} + +int parse_loadavg_fixed_point(const char *s, loadavg_t *ret) { + const char *d, *f_str, *i_str; + unsigned long i, f; + int r; + + assert(s); + assert(ret); + + d = strchr(s, '.'); + if (!d) + return -EINVAL; + + i_str = strndupa(s, d - s); + f_str = d + 1; + + r = safe_atolu_full(i_str, 10, &i); + if (r < 0) + return r; + + r = safe_atolu_full(f_str, 10, &f); + if (r < 0) + return r; + + return store_loadavg_fixed_point(i, f, ret); +} #endif /* NM_IGNORED */ diff --git a/shared/systemd/src/basic/parse-util.h b/shared/systemd/src/basic/parse-util.h index 2cee65c4..ba4e727e 100644 --- a/shared/systemd/src/basic/parse-util.h +++ b/shared/systemd/src/basic/parse-util.h @@ -1,14 +1,19 @@ -/* SPDX-License-Identifier: LGPL-2.1+ */ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ #pragma once #include <inttypes.h> #include <limits.h> +#if 0 /* NM_IGNORED */ +#include <linux/loadavg.h> +#endif /* NM_IGNORED */ #include <stddef.h> #include <stdint.h> #include <sys/types.h> #include "macro.h" +typedef unsigned long loadavg_t; + int parse_boolean(const char *v) _pure_; int parse_dev(const char *s, dev_t *ret); int parse_pid(const char *s, pid_t* ret_pid); @@ -88,18 +93,18 @@ static inline int safe_atoux64(const char *s, uint64_t *ret) { } #if LONG_MAX == INT_MAX -static inline int safe_atolu(const char *s, unsigned long *ret_u) { +static inline int safe_atolu_full(const char *s, unsigned base, long unsigned *ret_u) { assert_cc(sizeof(unsigned long) == sizeof(unsigned)); - return safe_atou(s, (unsigned*) ret_u); + return safe_atou_full(s, base, (unsigned*) ret_u); } static inline int safe_atoli(const char *s, long int *ret_u) { assert_cc(sizeof(long int) == sizeof(int)); return safe_atoi(s, (int*) ret_u); } #else -static inline int safe_atolu(const char *s, unsigned long *ret_u) { +static inline int safe_atolu_full(const char *s, unsigned base, unsigned long *ret_u) { assert_cc(sizeof(unsigned long) == sizeof(unsigned long long)); - return safe_atollu(s, (unsigned long long*) ret_u); + return safe_atollu_full(s, base, (unsigned long long*) ret_u); } static inline int safe_atoli(const char *s, long int *ret_u) { assert_cc(sizeof(long int) == sizeof(long long int)); @@ -107,6 +112,10 @@ static inline int safe_atoli(const char *s, long int *ret_u) { } #endif +static inline int safe_atolu(const char *s, unsigned long *ret_u) { + return safe_atolu_full(s, 0, ret_u); +} + #if SIZE_MAX == UINT_MAX static inline int safe_atozu(const char *s, size_t *ret_u) { assert_cc(sizeof(size_t) == sizeof(unsigned)); @@ -137,3 +146,8 @@ int parse_ip_port_range(const char *s, uint16_t *low, uint16_t *high); int parse_ip_prefix_length(const char *s, int *ret); int parse_oom_score_adjust(const char *s, int *ret); + +/* Given a Linux load average (e.g. decimal number 34.89 where 34 is passed as i and 89 is passed as f), convert it + * to a loadavg_t. */ +int store_loadavg_fixed_point(unsigned long i, unsigned long f, loadavg_t *ret); +int parse_loadavg_fixed_point(const char *s, loadavg_t *ret); diff --git a/shared/systemd/src/basic/path-util.c b/shared/systemd/src/basic/path-util.c index 644829b2..ea44c32b 100644 --- a/shared/systemd/src/basic/path-util.c +++ b/shared/systemd/src/basic/path-util.c @@ -1,4 +1,4 @@ -/* SPDX-License-Identifier: LGPL-2.1+ */ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ #include "nm-sd-adapt-shared.h" @@ -558,7 +558,7 @@ char* path_join_internal(const char *first, ...) { sz = strlen_ptr(first); va_start(ap, first); - while ((p = va_arg(ap, char*)) != (const char*) -1) + while ((p = va_arg(ap, char*)) != POINTER_MAX) if (!isempty(p)) sz += 1 + strlen(p); va_end(ap); @@ -578,7 +578,7 @@ char* path_join_internal(const char *first, ...) { } va_start(ap, first); - while ((p = va_arg(ap, char*)) != (const char*) -1) { + while ((p = va_arg(ap, char*)) != POINTER_MAX) { if (isempty(p)) continue; @@ -594,22 +594,53 @@ char* path_join_internal(const char *first, ...) { } #if 0 /* NM_IGNORED */ -int find_executable_full(const char *name, bool use_path_envvar, char **ret) { +static int check_x_access(const char *path, int *ret_fd) { + if (ret_fd) { + _cleanup_close_ int fd = -1; + int r; + + /* We need to use O_PATH because there may be executables for which we have only exec + * permissions, but not read (usually suid executables). */ + fd = open(path, O_PATH|O_CLOEXEC); + if (fd < 0) + return -errno; + + r = access_fd(fd, X_OK); + if (r < 0) + return r; + + *ret_fd = TAKE_FD(fd); + } else { + /* Let's optimize things a bit by not opening the file if we don't need the fd. */ + if (access(path, X_OK) < 0) + return -errno; + } + + return 0; +} + +int find_executable_full(const char *name, bool use_path_envvar, char **ret_filename, int *ret_fd) { int last_error, r; const char *p = NULL; assert(name); if (is_path(name)) { - if (access(name, X_OK) < 0) - return -errno; + _cleanup_close_ int fd = -1; - if (ret) { - r = path_make_absolute_cwd(name, ret); + r = check_x_access(name, ret_fd ? &fd : NULL); + if (r < 0) + return r; + + if (ret_filename) { + r = path_make_absolute_cwd(name, ret_filename); if (r < 0) return r; } + if (ret_fd) + *ret_fd = TAKE_FD(fd); + return 0; } @@ -622,8 +653,10 @@ int find_executable_full(const char *name, bool use_path_envvar, char **ret) { last_error = -ENOENT; + /* Resolve a single-component name to a full path */ for (;;) { _cleanup_free_ char *j = NULL, *element = NULL; + _cleanup_close_ int fd = -1; r = extract_first_word(&p, &element, ":", EXTRACT_RELAX|EXTRACT_DONT_COALESCE_SEPARATORS); if (r < 0) @@ -638,7 +671,8 @@ int find_executable_full(const char *name, bool use_path_envvar, char **ret) { if (!j) return -ENOMEM; - if (access(j, X_OK) >= 0) { + r = check_x_access(j, ret_fd ? &fd : NULL); + if (r >= 0) { _cleanup_free_ char *with_dash; with_dash = strjoin(j, "/"); @@ -652,8 +686,10 @@ int find_executable_full(const char *name, bool use_path_envvar, char **ret) { /* We can't just `continue` inverting this case, since we need to update last_error. */ if (errno == ENOTDIR) { /* Found it! */ - if (ret) - *ret = path_simplify(TAKE_PTR(j), false); + if (ret_filename) + *ret_filename = path_simplify(TAKE_PTR(j), false); + if (ret_fd) + *ret_fd = TAKE_FD(fd); return 0; } diff --git a/shared/systemd/src/basic/path-util.h b/shared/systemd/src/basic/path-util.h index 6c265519..988f058b 100644 --- a/shared/systemd/src/basic/path-util.h +++ b/shared/systemd/src/basic/path-util.h @@ -1,4 +1,4 @@ -/* SPDX-License-Identifier: LGPL-2.1+ */ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ #pragma once #include <alloca.h> @@ -64,7 +64,7 @@ int path_compare(const char *a, const char *b) _pure_; bool path_equal(const char *a, const char *b) _pure_; bool path_equal_or_files_same(const char *a, const char *b, int flags); char* path_join_internal(const char *first, ...); -#define path_join(x, ...) path_join_internal(x, __VA_ARGS__, (const char*) -1) +#define path_join(x, ...) path_join_internal(x, __VA_ARGS__, POINTER_MAX) char* path_simplify(char *path, bool kill_dots); @@ -90,9 +90,9 @@ int path_strv_make_absolute_cwd(char **l); char** path_strv_resolve(char **l, const char *root); char** path_strv_resolve_uniq(char **l, const char *root); -int find_executable_full(const char *name, bool use_path_envvar, char **ret); -static inline int find_executable(const char *name, char **ret) { - return find_executable_full(name, true, ret); +int find_executable_full(const char *name, bool use_path_envvar, char **ret_filename, int *ret_fd); +static inline int find_executable(const char *name, char **ret_filename) { + return find_executable_full(name, true, ret_filename, NULL); } bool paths_check_timestamp(const char* const* paths, usec_t *paths_ts_usec, bool update); diff --git a/shared/systemd/src/basic/prioq.c b/shared/systemd/src/basic/prioq.c index dc048cc7..19a9bc57 100644 --- a/shared/systemd/src/basic/prioq.c +++ b/shared/systemd/src/basic/prioq.c @@ -1,4 +1,4 @@ -/* SPDX-License-Identifier: LGPL-2.1+ */ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ /* * Priority Queue diff --git a/shared/systemd/src/basic/prioq.h b/shared/systemd/src/basic/prioq.h index 1fb57bfa..951576c0 100644 --- a/shared/systemd/src/basic/prioq.h +++ b/shared/systemd/src/basic/prioq.h @@ -1,4 +1,4 @@ -/* SPDX-License-Identifier: LGPL-2.1+ */ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ #pragma once #include <stdbool.h> diff --git a/shared/systemd/src/basic/process-util.c b/shared/systemd/src/basic/process-util.c index 03ca04e1..0e25b020 100644 --- a/shared/systemd/src/basic/process-util.c +++ b/shared/systemd/src/basic/process-util.c @@ -1,4 +1,4 @@ -/* SPDX-License-Identifier: LGPL-2.1+ */ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ #include "nm-sd-adapt-shared.h" diff --git a/shared/systemd/src/basic/process-util.h b/shared/systemd/src/basic/process-util.h index 314a8de1..aab2c7a1 100644 --- a/shared/systemd/src/basic/process-util.h +++ b/shared/systemd/src/basic/process-util.h @@ -1,4 +1,4 @@ -/* SPDX-License-Identifier: LGPL-2.1+ */ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ #pragma once #include <errno.h> diff --git a/shared/systemd/src/basic/random-util.c b/shared/systemd/src/basic/random-util.c index 9551e762..4f67d9af 100644 --- a/shared/systemd/src/basic/random-util.c +++ b/shared/systemd/src/basic/random-util.c @@ -1,4 +1,4 @@ -/* SPDX-License-Identifier: LGPL-2.1+ */ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ #include "nm-sd-adapt-shared.h" @@ -462,10 +462,21 @@ size_t random_pool_size(void) { } int random_write_entropy(int fd, const void *seed, size_t size, bool credit) { + _cleanup_close_ int opened_fd = -1; int r; - assert(fd >= 0); - assert(seed && size > 0); + assert(seed || size == 0); + + if (size == 0) + return 0; + + if (fd < 0) { + opened_fd = open("/dev/urandom", O_WRONLY|O_CLOEXEC|O_NOCTTY); + if (opened_fd < 0) + return -errno; + + fd = opened_fd; + } if (credit) { _cleanup_free_ struct rand_pool_info *info = NULL; @@ -491,6 +502,6 @@ int random_write_entropy(int fd, const void *seed, size_t size, bool credit) { return r; } - return 0; + return 1; } #endif /* NM_IGNORED */ diff --git a/shared/systemd/src/basic/random-util.h b/shared/systemd/src/basic/random-util.h index 7824ffac..f661fc09 100644 --- a/shared/systemd/src/basic/random-util.h +++ b/shared/systemd/src/basic/random-util.h @@ -1,4 +1,4 @@ -/* SPDX-License-Identifier: LGPL-2.1+ */ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ #pragma once #include <stdbool.h> diff --git a/shared/systemd/src/basic/ratelimit.c b/shared/systemd/src/basic/ratelimit.c new file mode 100644 index 00000000..12c8324d --- /dev/null +++ b/shared/systemd/src/basic/ratelimit.c @@ -0,0 +1,40 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ + +#include "nm-sd-adapt-shared.h" + +#include <sys/time.h> + +#include "macro.h" +#include "ratelimit.h" + +/* Modelled after Linux' lib/ratelimit.c by Dave Young + * <hidave.darkstar@gmail.com>, which is licensed GPLv2. */ + +bool ratelimit_below(RateLimit *r) { + usec_t ts; + + assert(r); + + if (!ratelimit_configured(r)) + return true; + + ts = now(CLOCK_MONOTONIC); + + if (r->begin <= 0 || + ts - r->begin > r->interval) { + r->begin = ts; + + /* Reset counter */ + r->num = 0; + goto good; + } + + if (r->num < r->burst) + goto good; + + return false; + +good: + r->num++; + return true; +} diff --git a/shared/systemd/src/basic/ratelimit.h b/shared/systemd/src/basic/ratelimit.h new file mode 100644 index 00000000..ee1d17c0 --- /dev/null +++ b/shared/systemd/src/basic/ratelimit.h @@ -0,0 +1,24 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +#pragma once + +#include <stdbool.h> + +#include "time-util.h" +#include "util.h" + +typedef struct RateLimit { + usec_t interval; /* Keep those two fields first so they can be initialized easily: */ + unsigned burst; /* RateLimit rl = { INTERVAL, BURST }; */ + unsigned num; + usec_t begin; +} RateLimit; + +static inline void ratelimit_reset(RateLimit *rl) { + rl->num = rl->begin = 0; +} + +static inline bool ratelimit_configured(RateLimit *rl) { + return rl->interval > 0 && rl->burst > 0; +} + +bool ratelimit_below(RateLimit *r); diff --git a/shared/systemd/src/basic/set.h b/shared/systemd/src/basic/set.h index 7170eea8..57ff7130 100644 --- a/shared/systemd/src/basic/set.h +++ b/shared/systemd/src/basic/set.h @@ -1,4 +1,4 @@ -/* SPDX-License-Identifier: LGPL-2.1+ */ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ #pragma once #include "extract-word.h" @@ -128,10 +128,12 @@ int _set_ensure_consume(Set **s, const struct hash_ops *hash_ops, void *key HAS int set_consume(Set *s, void *value); -int _set_put_strdup(Set **s, const char *p HASHMAP_DEBUG_PARAMS); -#define set_put_strdup(s, p) _set_put_strdup(s, p HASHMAP_DEBUG_SRC_ARGS) -int _set_put_strdupv(Set **s, char **l HASHMAP_DEBUG_PARAMS); -#define set_put_strdupv(s, l) _set_put_strdupv(s, l HASHMAP_DEBUG_SRC_ARGS) +int _set_put_strdup_full(Set **s, const struct hash_ops *hash_ops, const char *p HASHMAP_DEBUG_PARAMS); +#define set_put_strdup_full(s, hash_ops, p) _set_put_strdup_full(s, hash_ops, p HASHMAP_DEBUG_SRC_ARGS) +#define set_put_strdup(s, p) set_put_strdup_full(s, &string_hash_ops_free, p) +int _set_put_strdupv_full(Set **s, const struct hash_ops *hash_ops, char **l HASHMAP_DEBUG_PARAMS); +#define set_put_strdupv_full(s, hash_ops, l) _set_put_strdupv_full(s, hash_ops, l HASHMAP_DEBUG_SRC_ARGS) +#define set_put_strdupv(s, l) set_put_strdupv_full(s, &string_hash_ops_free, l) int set_put_strsplit(Set *s, const char *v, const char *separators, ExtractFlags flags); @@ -148,3 +150,5 @@ DEFINE_TRIVIAL_CLEANUP_FUNC(Set*, set_free_free); #define _cleanup_set_free_ _cleanup_(set_freep) #define _cleanup_set_free_free_ _cleanup_(set_free_freep) + +int set_strjoin(Set *s, const char *separator, bool wrap_with_separator, char **ret); diff --git a/shared/systemd/src/basic/signal-util.c b/shared/systemd/src/basic/signal-util.c index a4b8163c..0c6f5818 100644 --- a/shared/systemd/src/basic/signal-util.c +++ b/shared/systemd/src/basic/signal-util.c @@ -1,4 +1,4 @@ -/* SPDX-License-Identifier: LGPL-2.1+ */ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ #include "nm-sd-adapt-shared.h" @@ -52,16 +52,7 @@ static int sigaction_many_ap(const struct sigaction *sa, int sig, va_list ap) { int r = 0; /* negative signal ends the list. 0 signal is skipped. */ - - if (sig < 0) - return 0; - - if (sig > 0) { - if (sigaction(sig, sa, NULL) < 0) - r = -errno; - } - - while ((sig = va_arg(ap, int)) >= 0) { + for (; sig >= 0; sig = va_arg(ap, int)) { if (sig == 0) continue; diff --git a/shared/systemd/src/basic/signal-util.h b/shared/systemd/src/basic/signal-util.h index 3909ee34..bdd39d42 100644 --- a/shared/systemd/src/basic/signal-util.h +++ b/shared/systemd/src/basic/signal-util.h @@ -1,4 +1,4 @@ -/* SPDX-License-Identifier: LGPL-2.1+ */ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ #pragma once #include <signal.h> diff --git a/shared/systemd/src/basic/siphash24.h b/shared/systemd/src/basic/siphash24.h index 474d9c9d..e46f3cc5 100644 --- a/shared/systemd/src/basic/siphash24.h +++ b/shared/systemd/src/basic/siphash24.h @@ -1,11 +1,13 @@ +/* SPDX-License-Identifier: CC0-1.0 */ + #pragma once #include <inttypes.h> #include <stddef.h> #include <stdint.h> -#include <string.h> #include <sys/types.h> +#include "string-util.h" #include "time-util.h" #if 0 /* NM_IGNORED */ @@ -61,11 +63,15 @@ static inline void siphash24_compress_usec_t(usec_t in, struct siphash *state) { siphash24_compress(&in, sizeof in, state); } -static inline void siphash24_compress_string(const char *in, struct siphash *state) { - if (!in) +static inline void siphash24_compress_safe(const void *in, size_t inlen, struct siphash *state) { + if (inlen == 0) return; - siphash24_compress(in, strlen(in), state); + siphash24_compress(in, inlen, state); +} + +static inline void siphash24_compress_string(const char *in, struct siphash *state) { + siphash24_compress_safe(in, strlen_ptr(in), state); } uint64_t siphash24_finalize(struct siphash *state); diff --git a/shared/systemd/src/basic/socket-util.c b/shared/systemd/src/basic/socket-util.c index 4d889e56..e224091d 100644 --- a/shared/systemd/src/basic/socket-util.c +++ b/shared/systemd/src/basic/socket-util.c @@ -1,4 +1,4 @@ -/* SPDX-License-Identifier: LGPL-2.1+ */ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ #include "nm-sd-adapt-shared.h" @@ -27,10 +27,7 @@ #include "format-util.h" #include "io-util.h" #include "log.h" -#include "macro.h" #include "memory-util.h" -#include "missing_socket.h" -#include "missing_network.h" #include "parse-util.h" #include "path-util.h" #include "process-util.h" @@ -325,7 +322,7 @@ bool socket_address_matches_fd(const SocketAddress *a, int fd) { } int sockaddr_port(const struct sockaddr *_sa, unsigned *ret_port) { - union sockaddr_union *sa = (union sockaddr_union*) _sa; + const union sockaddr_union *sa = (const union sockaddr_union*) _sa; /* Note, this returns the port as 'unsigned' rather than 'uint16_t', as AF_VSOCK knows larger ports */ @@ -350,6 +347,25 @@ int sockaddr_port(const struct sockaddr *_sa, unsigned *ret_port) { } } +const union in_addr_union *sockaddr_in_addr(const struct sockaddr *_sa) { + const union sockaddr_union *sa = (const union sockaddr_union*) _sa; + + if (!sa) + return NULL; + + switch (sa->sa.sa_family) { + + case AF_INET: + return (const union in_addr_union*) &sa->in.sin_addr; + + case AF_INET6: + return (const union in_addr_union*) &sa->in6.sin6_addr; + + default: + return NULL; + } +} + int sockaddr_pretty( const struct sockaddr *_sa, socklen_t salen, @@ -1251,71 +1267,8 @@ int socket_set_recvpktinfo(int fd, int af, bool b) { case AF_NETLINK: return setsockopt_int(fd, SOL_NETLINK, NETLINK_PKTINFO, b); - default: - return -EAFNOSUPPORT; - } -} - -int socket_set_recverr(int fd, int af, bool b) { - int r; - - if (af == AF_UNSPEC) { - r = socket_get_family(fd, &af); - if (r < 0) - return r; - } - - switch (af) { - - case AF_INET: - return setsockopt_int(fd, IPPROTO_IP, IP_RECVERR, b); - - case AF_INET6: - return setsockopt_int(fd, IPPROTO_IPV6, IPV6_RECVERR, b); - - default: - return -EAFNOSUPPORT; - } -} - -int socket_set_recvttl(int fd, int af, bool b) { - int r; - - if (af == AF_UNSPEC) { - r = socket_get_family(fd, &af); - if (r < 0) - return r; - } - - switch (af) { - - case AF_INET: - return setsockopt_int(fd, IPPROTO_IP, IP_RECVTTL, b); - - case AF_INET6: - return setsockopt_int(fd, IPPROTO_IPV6, IPV6_RECVHOPLIMIT, b); - - default: - return -EAFNOSUPPORT; - } -} - -int socket_set_ttl(int fd, int af, int ttl) { - int r; - - if (af == AF_UNSPEC) { - r = socket_get_family(fd, &af); - if (r < 0) - return r; - } - - switch (af) { - - case AF_INET: - return setsockopt_int(fd, IPPROTO_IP, IP_TTL, ttl); - - case AF_INET6: - return setsockopt_int(fd, IPPROTO_IPV6, IPV6_UNICAST_HOPS, ttl); + case AF_PACKET: + return setsockopt_int(fd, SOL_PACKET, PACKET_AUXDATA, b); default: return -EAFNOSUPPORT; @@ -1351,7 +1304,7 @@ int socket_set_unicast_if(int fd, int af, int ifi) { } } -int socket_set_freebind(int fd, int af, bool b) { +int socket_set_option(int fd, int af, int opt_ipv4, int opt_ipv6, int val) { int r; if (af == AF_UNSPEC) { @@ -1363,18 +1316,18 @@ int socket_set_freebind(int fd, int af, bool b) { switch (af) { case AF_INET: - return setsockopt_int(fd, IPPROTO_IP, IP_FREEBIND, b); + return setsockopt_int(fd, IPPROTO_IP, opt_ipv4, val); case AF_INET6: - return setsockopt_int(fd, IPPROTO_IPV6, IPV6_FREEBIND, b); + return setsockopt_int(fd, IPPROTO_IPV6, opt_ipv6, val); default: return -EAFNOSUPPORT; } } -int socket_set_transparent(int fd, int af, bool b) { - int r; +int socket_get_mtu(int fd, int af, size_t *ret) { + int mtu, r; if (af == AF_UNSPEC) { r = socket_get_family(fd, &af); @@ -1385,13 +1338,23 @@ int socket_set_transparent(int fd, int af, bool b) { switch (af) { case AF_INET: - return setsockopt_int(fd, IPPROTO_IP, IP_TRANSPARENT, b); + r = getsockopt_int(fd, IPPROTO_IP, IP_MTU, &mtu); + break; case AF_INET6: - return setsockopt_int(fd, IPPROTO_IPV6, IPV6_TRANSPARENT, b); + r = getsockopt_int(fd, IPPROTO_IPV6, IPV6_MTU, &mtu); + break; default: return -EAFNOSUPPORT; } + + if (r < 0) + return r; + if (mtu <= 0) + return -EINVAL; + + *ret = (size_t) mtu; + return 0; } #endif /* NM_IGNORED */ diff --git a/shared/systemd/src/basic/socket-util.h b/shared/systemd/src/basic/socket-util.h index 1ece9118..1de06947 100644 --- a/shared/systemd/src/basic/socket-util.h +++ b/shared/systemd/src/basic/socket-util.h @@ -1,4 +1,4 @@ -/* SPDX-License-Identifier: LGPL-2.1+ */ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ #pragma once #include <inttypes.h> @@ -15,6 +15,7 @@ #include <sys/un.h> #include "macro.h" +#include "missing_network.h" #include "missing_socket.h" #include "sparse-endian.h" @@ -104,6 +105,7 @@ const char* socket_address_get_path(const SocketAddress *a); bool socket_ipv6_is_supported(void); int sockaddr_port(const struct sockaddr *_sa, unsigned *port); +const union in_addr_union *sockaddr_in_addr(const struct sockaddr *sa); int sockaddr_pretty(const struct sockaddr *_sa, socklen_t salen, bool translate_ipv6, bool include_port, char **ret); int getpeername_pretty(int fd, bool include_port, char **ret); @@ -258,6 +260,19 @@ static inline int setsockopt_int(int fd, int level, int optname, int value) { return 0; } +static inline int getsockopt_int(int fd, int level, int optname, int *ret) { + int v; + socklen_t sl = sizeof(v); + + if (getsockopt(fd, level, optname, &v, &sl) < 0) + return -errno; + if (sl != sizeof(v)) + return -EIO; + + *ret = v; + return 0; +} + int socket_bind_to_ifname(int fd, const char *ifname); int socket_bind_to_ifindex(int fd, int ifindex); @@ -265,9 +280,28 @@ ssize_t recvmsg_safe(int sockfd, struct msghdr *msg, int flags); int socket_get_family(int fd, int *ret); int socket_set_recvpktinfo(int fd, int af, bool b); -int socket_set_recverr(int fd, int af, bool b); -int socket_set_recvttl(int fd, int af, bool b); -int socket_set_ttl(int fd, int af, int ttl); int socket_set_unicast_if(int fd, int af, int ifi); -int socket_set_freebind(int fd, int af, bool b); -int socket_set_transparent(int fd, int af, bool b); + +int socket_set_option(int fd, int af, int opt_ipv4, int opt_ipv6, int val); +#if 0 /* NM_IGNORED */ +static inline int socket_set_recverr(int fd, int af, bool b) { + return socket_set_option(fd, af, IP_RECVERR, IPV6_RECVERR, b); +} +static inline int socket_set_recvttl(int fd, int af, bool b) { + return socket_set_option(fd, af, IP_RECVTTL, IPV6_RECVHOPLIMIT, b); +} +static inline int socket_set_ttl(int fd, int af, int ttl) { + return socket_set_option(fd, af, IP_TTL, IPV6_UNICAST_HOPS, ttl); +} +static inline int socket_set_freebind(int fd, int af, bool b) { + return socket_set_option(fd, af, IP_FREEBIND, IPV6_FREEBIND, b); +} +static inline int socket_set_transparent(int fd, int af, bool b) { + return socket_set_option(fd, af, IP_TRANSPARENT, IPV6_TRANSPARENT, b); +} +static inline int socket_set_recvfragsize(int fd, int af, bool b) { + return socket_set_option(fd, af, IP_RECVFRAGSIZE, IPV6_RECVFRAGSIZE, b); +} +#endif /* NM_IGNORED */ + +int socket_get_mtu(int fd, int af, size_t *ret); diff --git a/shared/systemd/src/basic/sort-util.h b/shared/systemd/src/basic/sort-util.h index a8dc3bb6..a8984fc1 100644 --- a/shared/systemd/src/basic/sort-util.h +++ b/shared/systemd/src/basic/sort-util.h @@ -1,4 +1,4 @@ -/* SPDX-License-Identifier: LGPL-2.1+ */ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ #pragma once #include <stdlib.h> @@ -55,6 +55,7 @@ static inline void _qsort_safe(void *base, size_t nmemb, size_t size, __compar_f _qsort_safe((p), (n), sizeof((p)[0]), (__compar_fn_t) _func_); \ }) +#if 0 /* NM_IGNORED */ static inline void qsort_r_safe(void *base, size_t nmemb, size_t size, __compar_d_fn_t compar, void *userdata) { if (nmemb <= 1) return; @@ -68,3 +69,6 @@ static inline void qsort_r_safe(void *base, size_t nmemb, size_t size, __compar_ int (*_func_)(const typeof(p[0])*, const typeof(p[0])*, typeof(userdata)) = func; \ qsort_r_safe((p), (n), sizeof((p)[0]), (__compar_d_fn_t) _func_, userdata); \ }) +#endif /* NM_IGNORED */ + +int cmp_int(const int *a, const int *b); diff --git a/shared/systemd/src/basic/stat-util.c b/shared/systemd/src/basic/stat-util.c index 6d393041..a9880096 100644 --- a/shared/systemd/src/basic/stat-util.c +++ b/shared/systemd/src/basic/stat-util.c @@ -1,4 +1,4 @@ -/* SPDX-License-Identifier: LGPL-2.1+ */ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ #include "nm-sd-adapt-shared.h" @@ -231,13 +231,12 @@ int fd_is_network_fs(int fd) { } int path_is_temporary_fs(const char *path) { - _cleanup_close_ int fd = -1; + struct statfs s; - fd = open(path, O_RDONLY|O_CLOEXEC|O_NOCTTY|O_PATH); - if (fd < 0) + if (statfs(path, &s) < 0) return -errno; - return fd_is_temporary_fs(fd); + return is_temporary_fs(&s); } #endif /* NM_IGNORED */ @@ -415,7 +414,8 @@ bool stat_inode_unmodified(const struct stat *a, const struct stat *b) { return a && b && (a->st_mode & S_IFMT) != 0 && /* We use the check for .st_mode if the structure was ever initialized */ ((a->st_mode ^ b->st_mode) & S_IFMT) == 0 && /* same inode type */ - a->st_mtime == b->st_mtime && + a->st_mtim.tv_sec == b->st_mtim.tv_sec && + a->st_mtim.tv_nsec == b->st_mtim.tv_nsec && (!S_ISREG(a->st_mode) || a->st_size == b->st_size) && /* if regular file, compare file size */ a->st_dev == b->st_dev && a->st_ino == b->st_ino && diff --git a/shared/systemd/src/basic/stat-util.h b/shared/systemd/src/basic/stat-util.h index 26ecd635..a566114f 100644 --- a/shared/systemd/src/basic/stat-util.h +++ b/shared/systemd/src/basic/stat-util.h @@ -1,4 +1,4 @@ -/* SPDX-License-Identifier: LGPL-2.1+ */ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ #pragma once #include <fcntl.h> diff --git a/shared/systemd/src/basic/stdio-util.h b/shared/systemd/src/basic/stdio-util.h index c3b9448d..d45d3c1a 100644 --- a/shared/systemd/src/basic/stdio-util.h +++ b/shared/systemd/src/basic/stdio-util.h @@ -1,7 +1,9 @@ -/* SPDX-License-Identifier: LGPL-2.1+ */ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ #pragma once +#if 0 /* NM_IGNORED */ #include <printf.h> +#endif /* NM_IGNORED */ #include <stdarg.h> #include <stdio.h> #include <sys/types.h> diff --git a/shared/systemd/src/basic/string-table.c b/shared/systemd/src/basic/string-table.c index 46014b18..bd8047e6 100644 --- a/shared/systemd/src/basic/string-table.c +++ b/shared/systemd/src/basic/string-table.c @@ -1,4 +1,4 @@ -/* SPDX-License-Identifier: LGPL-2.1+ */ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ #include "nm-sd-adapt-shared.h" diff --git a/shared/systemd/src/basic/string-table.h b/shared/systemd/src/basic/string-table.h index 96924778..ae4ea145 100644 --- a/shared/systemd/src/basic/string-table.h +++ b/shared/systemd/src/basic/string-table.h @@ -1,4 +1,4 @@ -/* SPDX-License-Identifier: LGPL-2.1+ */ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ #pragma once @@ -28,13 +28,12 @@ ssize_t string_table_lookup(const char * const *table, size_t len, const char *k #define _DEFINE_STRING_TABLE_LOOKUP_FROM_STRING_WITH_BOOLEAN(name,type,yes,scope) \ scope type name##_from_string(const char *s) { \ - int b; \ if (!s) \ return -1; \ - b = parse_boolean(s); \ + int b = parse_boolean(s); \ if (b == 0) \ return (type) 0; \ - else if (b > 0) \ + if (b > 0) \ return yes; \ return (type) string_table_lookup(name##_table, ELEMENTSOF(name##_table), s); \ } @@ -79,11 +78,13 @@ ssize_t string_table_lookup(const char * const *table, size_t len, const char *k _DEFINE_STRING_TABLE_LOOKUP_FROM_STRING_WITH_BOOLEAN(name,type,yes,scope) #define DEFINE_STRING_TABLE_LOOKUP(name,type) _DEFINE_STRING_TABLE_LOOKUP(name,type,) +#define DEFINE_STRING_TABLE_LOOKUP_TO_STRING(name,type) _DEFINE_STRING_TABLE_LOOKUP_TO_STRING(name,type,) #define DEFINE_PRIVATE_STRING_TABLE_LOOKUP(name,type) _DEFINE_STRING_TABLE_LOOKUP(name,type,static) #define DEFINE_PRIVATE_STRING_TABLE_LOOKUP_TO_STRING(name,type) _DEFINE_STRING_TABLE_LOOKUP_TO_STRING(name,type,static) #define DEFINE_PRIVATE_STRING_TABLE_LOOKUP_FROM_STRING(name,type) _DEFINE_STRING_TABLE_LOOKUP_FROM_STRING(name,type,static) #define DEFINE_STRING_TABLE_LOOKUP_WITH_BOOLEAN(name,type,yes) _DEFINE_STRING_TABLE_LOOKUP_WITH_BOOLEAN(name,type,yes,) +#define DEFINE_PRIVATE_STRING_TABLE_LOOKUP_WITH_BOOLEAN(name,type,yes) _DEFINE_STRING_TABLE_LOOKUP_WITH_BOOLEAN(name,type,yes,static) /* For string conversions where numbers are also acceptable */ #define DEFINE_STRING_TABLE_LOOKUP_WITH_FALLBACK(name,type,max) \ diff --git a/shared/systemd/src/basic/string-util.c b/shared/systemd/src/basic/string-util.c index aea13dcb..744a3606 100644 --- a/shared/systemd/src/basic/string-util.c +++ b/shared/systemd/src/basic/string-util.c @@ -1,4 +1,4 @@ -/* SPDX-License-Identifier: LGPL-2.1+ */ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ #include "nm-sd-adapt-shared.h" @@ -147,57 +147,32 @@ char *strnappend(const char *s, const char *suffix, size_t b) { char *strjoin_real(const char *x, ...) { va_list ap; - size_t l; + size_t l = 1; char *r, *p; va_start(ap, x); + for (const char *t = x; t; t = va_arg(ap, const char *)) { + size_t n; - if (x) { - l = strlen(x); - - for (;;) { - const char *t; - size_t n; - - t = va_arg(ap, const char *); - if (!t) - break; - - n = strlen(t); - if (n > ((size_t) -1) - l) { - va_end(ap); - return NULL; - } - - l += n; + n = strlen(t); + if (n > SIZE_MAX - l) { + va_end(ap); + return NULL; } - } else - l = 0; - + l += n; + } va_end(ap); - r = new(char, l+1); + p = r = new(char, l); if (!r) return NULL; - if (x) { - p = stpcpy(r, x); - - va_start(ap, x); - - for (;;) { - const char *t; - - t = va_arg(ap, const char *); - if (!t) - break; - - p = stpcpy(p, t); - } + va_start(ap, x); + for (const char *t = x; t; t = va_arg(ap, const char *)) + p = stpcpy(p, t); + va_end(ap); - va_end(ap); - } else - r[0] = 0; + *p = 0; return r; } @@ -823,10 +798,10 @@ char *strip_tab_ansi(char **ibuf, size_t *_isz, size_t highlight[2]) { return *ibuf; } -char *strextend_with_separator(char **x, const char *separator, ...) { - bool need_separator; +char *strextend_with_separator_internal(char **x, const char *separator, ...) { size_t f, l, l_separator; - char *r, *p; + bool need_separator; + char *nr, *p; va_list ap; assert(x); @@ -850,7 +825,7 @@ char *strextend_with_separator(char **x, const char *separator, ...) { if (need_separator) n += l_separator; - if (n > ((size_t) -1) - l) { + if (n >= SIZE_MAX - l) { va_end(ap); return NULL; } @@ -862,11 +837,12 @@ char *strextend_with_separator(char **x, const char *separator, ...) { need_separator = !isempty(*x); - r = realloc(*x, l+1); - if (!r) + nr = realloc(*x, GREEDY_ALLOC_ROUND_UP(l+1)); + if (!nr) return NULL; - p = r + f; + *x = nr; + p = nr + f; va_start(ap, separator); for (;;) { @@ -885,12 +861,11 @@ char *strextend_with_separator(char **x, const char *separator, ...) { } va_end(ap); - assert(p == r + l); + assert(p == nr + l); *p = 0; - *x = r; - return r + l; + return p; } #endif /* NM_IGNORED */ diff --git a/shared/systemd/src/basic/string-util.h b/shared/systemd/src/basic/string-util.h index cefbda35..593cf04a 100644 --- a/shared/systemd/src/basic/string-util.h +++ b/shared/systemd/src/basic/string-util.h @@ -1,4 +1,4 @@ -/* SPDX-License-Identifier: LGPL-2.1+ */ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ #pragma once #include <stdbool.h> @@ -33,6 +33,12 @@ static inline bool streq_ptr(const char *a, const char *b) { return strcmp_ptr(a, b) == 0; } +static inline char* strstr_ptr(const char *haystack, const char *needle) { + if (!haystack || !needle) + return NULL; + return strstr(haystack, needle); +} + static inline const char* strempty(const char *s) { return s ?: ""; } @@ -53,6 +59,10 @@ static inline const char* true_false(bool b) { return b ? "true" : "false"; } +static inline const char* plus_minus(bool b) { + return b ? "+" : "-"; +} + static inline const char* one_zero(bool b) { return b ? "1" : "0"; } @@ -179,9 +189,10 @@ char *strreplace(const char *text, const char *old_string, const char *new_strin char *strip_tab_ansi(char **ibuf, size_t *_isz, size_t highlight[2]); -char *strextend_with_separator(char **x, const char *separator, ...) _sentinel_; +char *strextend_with_separator_internal(char **x, const char *separator, ...) _sentinel_; -#define strextend(x, ...) strextend_with_separator(x, NULL, __VA_ARGS__) +#define strextend_with_separator(x, separator, ...) strextend_with_separator_internal(x, separator, __VA_ARGS__, NULL) +#define strextend(x, ...) strextend_with_separator_internal(x, NULL, __VA_ARGS__, NULL) char *strrep(const char *s, unsigned n); diff --git a/shared/systemd/src/basic/strv.c b/shared/systemd/src/basic/strv.c index 30d3af46..7d3e3fc7 100644 --- a/shared/systemd/src/basic/strv.c +++ b/shared/systemd/src/basic/strv.c @@ -1,4 +1,4 @@ -/* SPDX-License-Identifier: LGPL-2.1+ */ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ #include "nm-sd-adapt-shared.h" @@ -125,7 +125,6 @@ size_t strv_length(char * const *l) { } char **strv_new_ap(const char *x, va_list ap) { - const char *s; _cleanup_strv_free_ char **a = NULL; size_t n = 0, i = 0; va_list aq; @@ -135,43 +134,28 @@ char **strv_new_ap(const char *x, va_list ap) { * STRV_IFNOTNULL() macro to include possibly NULL strings in * the string list. */ - if (x) { - n = x == STRV_IGNORE ? 0 : 1; - - va_copy(aq, ap); - while ((s = va_arg(aq, const char*))) { - if (s == STRV_IGNORE) - continue; - - n++; - } + va_copy(aq, ap); + for (const char *s = x; s; s = va_arg(aq, const char*)) { + if (s == STRV_IGNORE) + continue; - va_end(aq); + n++; } + va_end(aq); a = new(char*, n+1); if (!a) return NULL; - if (x) { - if (x != STRV_IGNORE) { - a[i] = strdup(x); - if (!a[i]) - return NULL; - i++; - } - - while ((s = va_arg(ap, const char*))) { - - if (s == STRV_IGNORE) - continue; + for (const char *s = x; s; s = va_arg(ap, const char*)) { + if (s == STRV_IGNORE) + continue; - a[i] = strdup(s); - if (!a[i]) - return NULL; + a[i] = strdup(s); + if (!a[i]) + return NULL; - i++; - } + i++; } a[i] = NULL; @@ -543,6 +527,19 @@ int strv_consume_prepend(char ***l, char *value) { return r; } +int strv_prepend(char ***l, const char *value) { + char *v; + + if (!value) + return 0; + + v = strdup(value); + if (!v) + return -ENOMEM; + + return strv_consume_prepend(l, v); +} + int strv_extend(char ***l, const char *value) { char *v; diff --git a/shared/systemd/src/basic/strv.h b/shared/systemd/src/basic/strv.h index 919fabf7..6b3e8e7f 100644 --- a/shared/systemd/src/basic/strv.h +++ b/shared/systemd/src/basic/strv.h @@ -1,4 +1,4 @@ -/* SPDX-License-Identifier: LGPL-2.1+ */ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ #pragma once #include <fnmatch.h> @@ -34,6 +34,7 @@ size_t strv_length(char * const *l) _pure_; int strv_extend_strv(char ***a, char * const *b, bool filter_duplicates); int strv_extend_strv_concat(char ***a, char * const *b, const char *suffix); +int strv_prepend(char ***l, const char *value); int strv_extend(char ***l, const char *value); int strv_extendf(char ***l, const char *format, ...) _printf_(2,0); int strv_extend_front(char ***l, const char *value); @@ -62,7 +63,7 @@ char **strv_new_internal(const char *x, ...) _sentinel_; char **strv_new_ap(const char *x, va_list ap); #define strv_new(...) strv_new_internal(__VA_ARGS__, NULL) -#define STRV_IGNORE ((const char *) -1) +#define STRV_IGNORE ((const char *) POINTER_MAX) static inline const char* STRV_IFNOTNULL(const char *x) { return x ? x : STRV_IGNORE; diff --git a/shared/systemd/src/basic/strxcpyx.c b/shared/systemd/src/basic/strxcpyx.c index 301e6899..39aebb88 100644 --- a/shared/systemd/src/basic/strxcpyx.c +++ b/shared/systemd/src/basic/strxcpyx.c @@ -1,4 +1,4 @@ -/* SPDX-License-Identifier: LGPL-2.1+ */ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ /* * Concatenates/copies strings. In any case, terminates in all cases diff --git a/shared/systemd/src/basic/strxcpyx.h b/shared/systemd/src/basic/strxcpyx.h index 9b668412..cdef492d 100644 --- a/shared/systemd/src/basic/strxcpyx.h +++ b/shared/systemd/src/basic/strxcpyx.h @@ -1,4 +1,4 @@ -/* SPDX-License-Identifier: LGPL-2.1+ */ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ #pragma once #include <stddef.h> diff --git a/shared/systemd/src/basic/time-util.c b/shared/systemd/src/basic/time-util.c index 0fac79a7..e5faa334 100644 --- a/shared/systemd/src/basic/time-util.c +++ b/shared/systemd/src/basic/time-util.c @@ -1,4 +1,4 @@ -/* SPDX-License-Identifier: LGPL-2.1+ */ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ #include "nm-sd-adapt-shared.h" @@ -1613,7 +1613,7 @@ TimestampStyle timestamp_style_from_string(const char *s) { return t; if (streq_ptr(s, "µs")) return TIMESTAMP_US; - if (streq_ptr(s, "µs+uts")) + if (streq_ptr(s, "µs+utc")) return TIMESTAMP_US_UTC; return t; } diff --git a/shared/systemd/src/basic/time-util.h b/shared/systemd/src/basic/time-util.h index cecd5efa..89ee8b4a 100644 --- a/shared/systemd/src/basic/time-util.h +++ b/shared/systemd/src/basic/time-util.h @@ -1,4 +1,4 @@ -/* SPDX-License-Identifier: LGPL-2.1+ */ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ #pragma once #include <inttypes.h> @@ -63,10 +63,10 @@ typedef enum TimestampStyle { /* We assume a maximum timezone length of 6. TZNAME_MAX is not defined on Linux, but glibc internally initializes this * to 6. Let's rely on that. */ -#define FORMAT_TIMESTAMP_MAX (3+1+10+1+8+1+6+1+6+1) -#define FORMAT_TIMESTAMP_WIDTH 28 /* when outputting, assume this width */ -#define FORMAT_TIMESTAMP_RELATIVE_MAX 256 -#define FORMAT_TIMESPAN_MAX 64 +#define FORMAT_TIMESTAMP_MAX (3U+1U+10U+1U+8U+1U+6U+1U+6U+1U) +#define FORMAT_TIMESTAMP_WIDTH 28U /* when outputting, assume this width */ +#define FORMAT_TIMESTAMP_RELATIVE_MAX 256U +#define FORMAT_TIMESPAN_MAX 64U #define TIME_T_MAX (time_t)((UINTMAX_C(1) << ((sizeof(time_t) << 3) - 1)) - 1) diff --git a/shared/systemd/src/basic/tmpfile-util.c b/shared/systemd/src/basic/tmpfile-util.c index 9b3621ba..bbd6a1ed 100644 --- a/shared/systemd/src/basic/tmpfile-util.c +++ b/shared/systemd/src/basic/tmpfile-util.c @@ -1,4 +1,4 @@ -/* SPDX-License-Identifier: LGPL-2.1+ */ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ #include "nm-sd-adapt-shared.h" diff --git a/shared/systemd/src/basic/tmpfile-util.h b/shared/systemd/src/basic/tmpfile-util.h index 802c85d6..45255fc0 100644 --- a/shared/systemd/src/basic/tmpfile-util.h +++ b/shared/systemd/src/basic/tmpfile-util.h @@ -1,4 +1,4 @@ -/* SPDX-License-Identifier: LGPL-2.1+ */ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ #pragma once #include <stdio.h> diff --git a/shared/systemd/src/basic/umask-util.h b/shared/systemd/src/basic/umask-util.h index cad74517..bd7c2bdb 100644 --- a/shared/systemd/src/basic/umask-util.h +++ b/shared/systemd/src/basic/umask-util.h @@ -1,4 +1,4 @@ -/* SPDX-License-Identifier: LGPL-2.1+ */ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ #pragma once #include <stdbool.h> diff --git a/shared/systemd/src/basic/utf8.c b/shared/systemd/src/basic/utf8.c index 8159b187..a7679bfa 100644 --- a/shared/systemd/src/basic/utf8.c +++ b/shared/systemd/src/basic/utf8.c @@ -1,4 +1,4 @@ -/* SPDX-License-Identifier: LGPL-2.1+ */ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ /* Parts of this file are based on the GLIB utf8 validation functions. The * original license text follows. */ diff --git a/shared/systemd/src/basic/utf8.h b/shared/systemd/src/basic/utf8.h index f315ea0f..a6ea942c 100644 --- a/shared/systemd/src/basic/utf8.h +++ b/shared/systemd/src/basic/utf8.h @@ -1,4 +1,4 @@ -/* SPDX-License-Identifier: LGPL-2.1+ */ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ #pragma once #include <stdbool.h> diff --git a/shared/systemd/src/basic/util.c b/shared/systemd/src/basic/util.c index 8a3f95dc..10a5bcff 100644 --- a/shared/systemd/src/basic/util.c +++ b/shared/systemd/src/basic/util.c @@ -1,4 +1,4 @@ -/* SPDX-License-Identifier: LGPL-2.1+ */ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ #include "nm-sd-adapt-shared.h" @@ -168,7 +168,7 @@ int container_get_leader(const char *machine, pid_t *pid) { return 0; } - if (!machine_name_is_valid(machine)) + if (!hostname_is_valid(machine, 0)) return -EINVAL; p = strjoina("/run/systemd/machines/", machine); @@ -196,8 +196,8 @@ int container_get_leader(const char *machine, pid_t *pid) { } int version(void) { - puts("systemd " STRINGIFY(PROJECT_VERSION) " (" GIT_VERSION ")\n" - SYSTEMD_FEATURES); + printf("systemd " STRINGIFY(PROJECT_VERSION) " (" GIT_VERSION ")\n%s\n", + systemd_features); return 0; } diff --git a/shared/systemd/src/basic/util.h b/shared/systemd/src/basic/util.h index 6fc7480f..942d773f 100644 --- a/shared/systemd/src/basic/util.h +++ b/shared/systemd/src/basic/util.h @@ -1,4 +1,4 @@ -/* SPDX-License-Identifier: LGPL-2.1+ */ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ #pragma once #include <stdint.h> diff --git a/shared/systemd/src/shared/dns-domain.c b/shared/systemd/src/shared/dns-domain.c index 6d60eb58..95e4a93a 100644 --- a/shared/systemd/src/shared/dns-domain.c +++ b/shared/systemd/src/shared/dns-domain.c @@ -1,16 +1,7 @@ -/* SPDX-License-Identifier: LGPL-2.1+ */ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ #include "nm-sd-adapt-shared.h" -#if 0 /* NM_IGNORED */ -#if HAVE_LIBIDN2 -# include <idn2.h> -#elif HAVE_LIBIDN -# include <idna.h> -# include <stringprep.h> -#endif -#endif - #include <endian.h> #include <netinet/in.h> #include <stdio.h> @@ -21,6 +12,7 @@ #include "hashmap.h" #include "hexdecoct.h" #include "hostname-util.h" +#include "idn-util.h" #include "in-addr-util.h" #include "macro.h" #include "parse-util.h" @@ -319,12 +311,17 @@ int dns_label_apply_idna(const char *encoded, size_t encoded_size, char *decoded const char *p; bool contains_8bit = false; char buffer[DNS_LABEL_MAX+1]; + int r; assert(encoded); assert(decoded); /* Converts an U-label into an A-label */ + r = dlopen_idn(); + if (r < 0) + return r; + if (encoded_size <= 0) return -EINVAL; @@ -339,11 +336,11 @@ int dns_label_apply_idna(const char *encoded, size_t encoded_size, char *decoded return 0; } - input = stringprep_utf8_to_ucs4(encoded, encoded_size, &input_size); + input = sym_stringprep_utf8_to_ucs4(encoded, encoded_size, &input_size); if (!input) return -ENOMEM; - if (idna_to_ascii_4i(input, input_size, buffer, 0) != 0) + if (sym_idna_to_ascii_4i(input, input_size, buffer, 0) != 0) return -EINVAL; l = strlen(buffer); @@ -369,28 +366,33 @@ int dns_label_undo_idna(const char *encoded, size_t encoded_size, char *decoded, _cleanup_free_ char *result = NULL; uint32_t *output = NULL; size_t w; + int r; /* To be invoked after unescaping. Converts an A-label into an U-label. */ assert(encoded); assert(decoded); + r = dlopen_idn(); + if (r < 0) + return r; + if (encoded_size <= 0 || encoded_size > DNS_LABEL_MAX) return -EINVAL; if (!memory_startswith(encoded, encoded_size, IDNA_ACE_PREFIX)) return 0; - input = stringprep_utf8_to_ucs4(encoded, encoded_size, &input_size); + input = sym_stringprep_utf8_to_ucs4(encoded, encoded_size, &input_size); if (!input) return -ENOMEM; output_size = input_size; output = newa(uint32_t, output_size); - idna_to_unicode_44i(input, input_size, output, &output_size, 0); + sym_idna_to_unicode_44i(input, input_size, output, &output_size, 0); - result = stringprep_ucs4_to_utf8(output, output_size, NULL, &w); + result = sym_stringprep_ucs4_to_utf8(output, output_size, NULL, &w); if (!result) return -ENOMEM; if (w <= 0) @@ -748,12 +750,12 @@ int dns_name_reverse(int family, const union in_addr_union *a, char **ret) { return 0; } -int dns_name_address(const char *p, int *family, union in_addr_union *address) { +int dns_name_address(const char *p, int *ret_family, union in_addr_union *ret_address) { int r; assert(p); - assert(family); - assert(address); + assert(ret_family); + assert(ret_address); r = dns_name_endswith(p, "in-addr.arpa"); if (r < 0) @@ -782,11 +784,11 @@ int dns_name_address(const char *p, int *family, union in_addr_union *address) { if (r <= 0) return r; - *family = AF_INET; - address->in.s_addr = htobe32(((uint32_t) a[3] << 24) | - ((uint32_t) a[2] << 16) | - ((uint32_t) a[1] << 8) | - (uint32_t) a[0]); + *ret_family = AF_INET; + ret_address->in.s_addr = htobe32(((uint32_t) a[3] << 24) | + ((uint32_t) a[2] << 16) | + ((uint32_t) a[1] << 8) | + (uint32_t) a[0]); return 1; } @@ -827,11 +829,14 @@ int dns_name_address(const char *p, int *family, union in_addr_union *address) { if (r <= 0) return r; - *family = AF_INET6; - address->in6 = a; + *ret_family = AF_INET6; + ret_address->in6 = a; return 1; } + *ret_family = AF_UNSPEC; + *ret_address = IN_ADDR_NULL; + return 0; } #endif /* NM_IGNORED */ @@ -1277,47 +1282,67 @@ int dns_name_common_suffix(const char *a, const char *b, const char **ret) { } int dns_name_apply_idna(const char *name, char **ret) { + /* Return negative on error, 0 if not implemented, positive on success. */ -#if HAVE_LIBIDN2 +#if HAVE_LIBIDN2 || HAVE_LIBIDN2 int r; + + r = dlopen_idn(); + if (r == -EOPNOTSUPP) { + *ret = NULL; + return 0; + } + if (r < 0) + return r; +#endif + +#if HAVE_LIBIDN2 _cleanup_free_ char *t = NULL; assert(name); assert(ret); - r = idn2_lookup_u8((uint8_t*) name, (uint8_t**) &t, - IDN2_NFC_INPUT | IDN2_NONTRANSITIONAL); + /* First, try non-transitional mode (i.e. IDN2008 rules) */ + r = sym_idn2_lookup_u8((uint8_t*) name, (uint8_t**) &t, + IDN2_NFC_INPUT | IDN2_NONTRANSITIONAL); + if (r == IDN2_DISALLOWED) /* If that failed, because of disallowed characters, try transitional mode. + * (i.e. IDN2003 rules which supports some unicode chars IDN2008 doesn't allow). */ + r = sym_idn2_lookup_u8((uint8_t*) name, (uint8_t**) &t, + IDN2_NFC_INPUT | IDN2_TRANSITIONAL); + log_debug("idn2_lookup_u8: %s → %s", name, t); if (r == IDN2_OK) { if (!startswith(name, "xn--")) { _cleanup_free_ char *s = NULL; - r = idn2_to_unicode_8z8z(t, &s, 0); + r = sym_idn2_to_unicode_8z8z(t, &s, 0); if (r != IDN2_OK) { log_debug("idn2_to_unicode_8z8z(\"%s\") failed: %d/%s", - t, r, idn2_strerror(r)); + t, r, sym_idn2_strerror(r)); + *ret = NULL; return 0; } if (!streq_ptr(name, s)) { log_debug("idn2 roundtrip failed: \"%s\" → \"%s\" → \"%s\", ignoring.", name, t, s); + *ret = NULL; return 0; } } *ret = TAKE_PTR(t); - return 1; /* *ret has been written */ } - log_debug("idn2_lookup_u8(\"%s\") failed: %d/%s", name, r, idn2_strerror(r)); + log_debug("idn2_lookup_u8(\"%s\") failed: %d/%s", name, r, sym_idn2_strerror(r)); if (r == IDN2_2HYPHEN) /* The name has two hyphens — forbidden by IDNA2008 in some cases */ return 0; if (IN_SET(r, IDN2_TOO_BIG_DOMAIN, IDN2_TOO_BIG_LABEL)) return -ENOSPC; + return -EINVAL; #elif HAVE_LIBIDN _cleanup_free_ char *buf = NULL; @@ -1369,6 +1394,7 @@ int dns_name_apply_idna(const char *name, char **ret) { return 1; #else + *ret = NULL; return 0; #endif } diff --git a/shared/systemd/src/shared/dns-domain.h b/shared/systemd/src/shared/dns-domain.h index e4e5b1b9..984f4840 100644 --- a/shared/systemd/src/shared/dns-domain.h +++ b/shared/systemd/src/shared/dns-domain.h @@ -1,4 +1,4 @@ -/* SPDX-License-Identifier: LGPL-2.1+ */ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ #pragma once #include <errno.h> diff --git a/shared/systemd/src/shared/log-link.h b/shared/systemd/src/shared/log-link.h new file mode 100644 index 00000000..3a4dcaa2 --- /dev/null +++ b/shared/systemd/src/shared/log-link.h @@ -0,0 +1,45 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +#pragma once + +#include "log.h" + +#define log_interface_full_errno(ifname, level, error, ...) \ + ({ \ + const char *_ifname = (ifname); \ + _ifname ? log_object_internal(level, error, PROJECT_FILE, __LINE__, __func__, "INTERFACE=", _ifname, NULL, NULL, ##__VA_ARGS__) : \ + log_internal(level, error, PROJECT_FILE, __LINE__, __func__, ##__VA_ARGS__); \ + }) + +/* + * The following macros append INTERFACE= to the message. + * The macros require a struct named 'Link' which contains 'char *ifname': + * + * typedef struct Link { + * char *ifname; + * } Link; + * + * See, network/networkd-link.h for example. + */ + +#define log_link_full_errno(link, level, error, ...) \ + ({ \ + const Link *_l = (link); \ + log_interface_full_errno(_l ? _l->ifname : NULL, level, error, ##__VA_ARGS__); \ + }) + +#define log_link_full(link, level, ...) (void) log_link_full_errno(link, level, 0, __VA_ARGS__) + +#define log_link_debug(link, ...) log_link_full_errno(link, LOG_DEBUG, 0, __VA_ARGS__) +#define log_link_info(link, ...) log_link_full(link, LOG_INFO, __VA_ARGS__) +#define log_link_notice(link, ...) log_link_full(link, LOG_NOTICE, __VA_ARGS__) +#define log_link_warning(link, ...) log_link_full(link, LOG_WARNING, __VA_ARGS__) +#define log_link_error(link, ...) log_link_full(link, LOG_ERR, __VA_ARGS__) + +#define log_link_debug_errno(link, error, ...) log_link_full_errno(link, LOG_DEBUG, error, __VA_ARGS__) +#define log_link_info_errno(link, error, ...) log_link_full_errno(link, LOG_INFO, error, __VA_ARGS__) +#define log_link_notice_errno(link, error, ...) log_link_full_errno(link, LOG_NOTICE, error, __VA_ARGS__) +#define log_link_warning_errno(link, error, ...) log_link_full_errno(link, LOG_WARNING, error, __VA_ARGS__) +#define log_link_error_errno(link, error, ...) log_link_full_errno(link, LOG_ERR, error, __VA_ARGS__) + +#define LOG_LINK_MESSAGE(link, fmt, ...) "MESSAGE=%s: " fmt, (link)->ifname, ##__VA_ARGS__ +#define LOG_LINK_INTERFACE(link) "INTERFACE=%s", (link)->ifname diff --git a/shared/systemd/src/shared/web-util.c b/shared/systemd/src/shared/web-util.c index 4cff5e27..35ba1a2e 100644 --- a/shared/systemd/src/shared/web-util.c +++ b/shared/systemd/src/shared/web-util.c @@ -1,4 +1,4 @@ -/* SPDX-License-Identifier: LGPL-2.1+ */ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ #include "nm-sd-adapt-shared.h" diff --git a/shared/systemd/src/shared/web-util.h b/shared/systemd/src/shared/web-util.h index c9e67e5c..ec54669f 100644 --- a/shared/systemd/src/shared/web-util.h +++ b/shared/systemd/src/shared/web-util.h @@ -1,4 +1,4 @@ -/* SPDX-License-Identifier: LGPL-2.1+ */ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ #pragma once #include <stdbool.h> |