From 85563b7fc7ec2cd21e38debb9b28db342e2e8e7c Mon Sep 17 00:00:00 2001 From: Michael Biebl Date: Sun, 21 Apr 2019 21:09:51 +0200 Subject: New upstream version 1.18.0 --- shared/c-rbtree/src/c-rbtree.c | 1 + shared/meson.build | 125 +- shared/n-acd/src/n-acd-probe.c | 1 + shared/n-acd/src/util/timer.c | 2 + shared/nm-common-macros.h | 62 - shared/nm-dbus-compat.h | 74 - shared/nm-default.h | 8 +- shared/nm-dispatcher-api.h | 61 - shared/nm-ethtool-utils.c | 225 -- shared/nm-ethtool-utils.h | 120 - shared/nm-glib-aux/nm-c-list.h | 117 + shared/nm-glib-aux/nm-dedup-multi.c | 1092 ++++++++ shared/nm-glib-aux/nm-dedup-multi.h | 437 +++ shared/nm-glib-aux/nm-enum-utils.c | 372 +++ shared/nm-glib-aux/nm-enum-utils.h | 48 + shared/nm-glib-aux/nm-errno.c | 198 ++ shared/nm-glib-aux/nm-errno.h | 185 ++ shared/nm-glib-aux/nm-glib.h | 567 ++++ shared/nm-glib-aux/nm-hash-utils.c | 196 ++ shared/nm-glib-aux/nm-hash-utils.h | 315 +++ shared/nm-glib-aux/nm-io-utils.c | 439 +++ shared/nm-glib-aux/nm-io-utils.h | 63 + shared/nm-glib-aux/nm-jansson.h | 49 + shared/nm-glib-aux/nm-logging-fwd.h | 113 + shared/nm-glib-aux/nm-macros-internal.h | 1855 ++++++++++++ shared/nm-glib-aux/nm-obj.h | 82 + shared/nm-glib-aux/nm-random-utils.c | 165 ++ shared/nm-glib-aux/nm-random-utils.h | 27 + shared/nm-glib-aux/nm-secret-utils.c | 168 ++ shared/nm-glib-aux/nm-secret-utils.h | 178 ++ shared/nm-glib-aux/nm-shared-utils.c | 2941 ++++++++++++++++++++ shared/nm-glib-aux/nm-shared-utils.h | 1191 ++++++++ shared/nm-glib-aux/nm-time-utils.c | 273 ++ shared/nm-glib-aux/nm-time-utils.h | 45 + shared/nm-libnm-core-aux/nm-dispatcher-api.h | 65 + shared/nm-libnm-core-intern/nm-common-macros.h | 62 + shared/nm-libnm-core-intern/nm-ethtool-utils.c | 225 ++ shared/nm-libnm-core-intern/nm-ethtool-utils.h | 120 + shared/nm-libnm-core-intern/nm-libnm-core-utils.c | 76 + shared/nm-libnm-core-intern/nm-libnm-core-utils.h | 113 + shared/nm-meta-setting.c | 8 + shared/nm-meta-setting.h | 12 + shared/nm-std-aux/c-list-util.c | 209 ++ shared/nm-std-aux/c-list-util.h | 66 + shared/nm-std-aux/nm-dbus-compat.h | 74 + shared/nm-std-aux/unaligned.h | 99 + shared/nm-test-utils-impl.c | 2 +- shared/nm-udev-aux/nm-udev-utils.c | 291 ++ shared/nm-udev-aux/nm-udev-utils.h | 48 + shared/nm-utils/c-list-util.c | 209 -- shared/nm-utils/c-list-util.h | 43 - shared/nm-utils/nm-c-list.h | 117 - shared/nm-utils/nm-dedup-multi.c | 1092 -------- shared/nm-utils/nm-dedup-multi.h | 437 --- shared/nm-utils/nm-enum-utils.c | 372 --- shared/nm-utils/nm-enum-utils.h | 48 - shared/nm-utils/nm-errno.c | 198 -- shared/nm-utils/nm-errno.h | 185 -- shared/nm-utils/nm-glib.h | 567 ---- shared/nm-utils/nm-hash-utils.c | 196 -- shared/nm-utils/nm-hash-utils.h | 290 -- shared/nm-utils/nm-io-utils.c | 439 --- shared/nm-utils/nm-io-utils.h | 63 - shared/nm-utils/nm-jansson.h | 49 - shared/nm-utils/nm-logging-fwd.h | 113 - shared/nm-utils/nm-macros-internal.h | 1707 ------------ shared/nm-utils/nm-obj.h | 82 - shared/nm-utils/nm-random-utils.c | 165 -- shared/nm-utils/nm-random-utils.h | 27 - shared/nm-utils/nm-secret-utils.c | 161 -- shared/nm-utils/nm-secret-utils.h | 178 -- shared/nm-utils/nm-shared-utils.c | 2741 ------------------ shared/nm-utils/nm-shared-utils.h | 1158 -------- shared/nm-utils/nm-test-utils.h | 11 +- shared/nm-utils/nm-time-utils.c | 273 -- shared/nm-utils/nm-time-utils.h | 45 - shared/nm-utils/nm-udev-utils.c | 291 -- shared/nm-utils/nm-udev-utils.h | 48 - shared/nm-utils/nm-vpn-editor-plugin-call.h | 2 +- shared/nm-utils/tests/test-shared-general.c | 203 +- shared/nm-utils/unaligned.h | 99 - shared/nm-version-macros.h | 3 +- shared/nm-version-macros.h.in | 1 + shared/systemd/nm-logging-stub.c | 2 +- shared/systemd/nm-sd-utils-shared.c | 5 +- shared/systemd/nm-sd-utils-shared.h | 6 +- shared/systemd/sd-adapt-shared/missing.h | 2 + shared/systemd/sd-adapt-shared/missing_socket.h | 3 - shared/systemd/sd-adapt-shared/namespace-util.h | 3 + .../systemd/sd-adapt-shared/nm-sd-adapt-shared.h | 2 +- shared/systemd/sd-adapt-shared/nulstr-util.h | 3 + shared/systemd/sd-adapt-shared/strxcpyx.h | 3 + shared/systemd/sd-adapt-shared/unaligned.h | 2 +- shared/systemd/src/basic/alloc-util.c | 20 +- shared/systemd/src/basic/alloc-util.h | 14 +- shared/systemd/src/basic/env-file.c | 2 +- shared/systemd/src/basic/errno-util.h | 69 + shared/systemd/src/basic/fd-util.c | 11 + shared/systemd/src/basic/fd-util.h | 12 - shared/systemd/src/basic/fileio.c | 104 +- shared/systemd/src/basic/fileio.h | 20 +- shared/systemd/src/basic/fs-util.c | 104 +- shared/systemd/src/basic/fs-util.h | 4 +- shared/systemd/src/basic/hashmap.c | 7 +- shared/systemd/src/basic/hashmap.h | 2 + shared/systemd/src/basic/hexdecoct.c | 77 +- shared/systemd/src/basic/hexdecoct.h | 5 +- shared/systemd/src/basic/in-addr-util.c | 134 +- shared/systemd/src/basic/in-addr-util.h | 2 + shared/systemd/src/basic/log.h | 22 +- shared/systemd/src/basic/memory-util.c | 59 + shared/systemd/src/basic/memory-util.h | 84 + shared/systemd/src/basic/mempool.c | 1 + shared/systemd/src/basic/missing_socket.h | 66 + shared/systemd/src/basic/missing_stat.h | 53 + shared/systemd/src/basic/path-util.c | 51 +- shared/systemd/src/basic/process-util.c | 53 +- shared/systemd/src/basic/process-util.h | 4 + shared/systemd/src/basic/random-util.c | 11 +- shared/systemd/src/basic/refcnt.h | 54 - shared/systemd/src/basic/socket-util.c | 72 +- shared/systemd/src/basic/socket-util.h | 3 + shared/systemd/src/basic/sort-util.h | 70 + shared/systemd/src/basic/stat-util.c | 46 - shared/systemd/src/basic/stat-util.h | 3 +- shared/systemd/src/basic/stdio-util.h | 2 +- shared/systemd/src/basic/string-table.h | 8 +- shared/systemd/src/basic/string-util.c | 35 +- shared/systemd/src/basic/string-util.h | 43 +- shared/systemd/src/basic/strv.c | 7 +- shared/systemd/src/basic/strv.h | 2 +- shared/systemd/src/basic/time-util.c | 2 +- shared/systemd/src/basic/utf8.c | 38 +- shared/systemd/src/basic/utf8.h | 2 +- shared/systemd/src/basic/util.c | 339 --- shared/systemd/src/basic/util.h | 181 +- 136 files changed, 13939 insertions(+), 12868 deletions(-) delete mode 100644 shared/nm-common-macros.h delete mode 100644 shared/nm-dbus-compat.h delete mode 100644 shared/nm-dispatcher-api.h delete mode 100644 shared/nm-ethtool-utils.c delete mode 100644 shared/nm-ethtool-utils.h create mode 100644 shared/nm-glib-aux/nm-c-list.h create mode 100644 shared/nm-glib-aux/nm-dedup-multi.c create mode 100644 shared/nm-glib-aux/nm-dedup-multi.h create mode 100644 shared/nm-glib-aux/nm-enum-utils.c create mode 100644 shared/nm-glib-aux/nm-enum-utils.h create mode 100644 shared/nm-glib-aux/nm-errno.c create mode 100644 shared/nm-glib-aux/nm-errno.h create mode 100644 shared/nm-glib-aux/nm-glib.h create mode 100644 shared/nm-glib-aux/nm-hash-utils.c create mode 100644 shared/nm-glib-aux/nm-hash-utils.h create mode 100644 shared/nm-glib-aux/nm-io-utils.c create mode 100644 shared/nm-glib-aux/nm-io-utils.h create mode 100644 shared/nm-glib-aux/nm-jansson.h create mode 100644 shared/nm-glib-aux/nm-logging-fwd.h create mode 100644 shared/nm-glib-aux/nm-macros-internal.h create mode 100644 shared/nm-glib-aux/nm-obj.h create mode 100644 shared/nm-glib-aux/nm-random-utils.c create mode 100644 shared/nm-glib-aux/nm-random-utils.h create mode 100644 shared/nm-glib-aux/nm-secret-utils.c create mode 100644 shared/nm-glib-aux/nm-secret-utils.h create mode 100644 shared/nm-glib-aux/nm-shared-utils.c create mode 100644 shared/nm-glib-aux/nm-shared-utils.h create mode 100644 shared/nm-glib-aux/nm-time-utils.c create mode 100644 shared/nm-glib-aux/nm-time-utils.h create mode 100644 shared/nm-libnm-core-aux/nm-dispatcher-api.h create mode 100644 shared/nm-libnm-core-intern/nm-common-macros.h create mode 100644 shared/nm-libnm-core-intern/nm-ethtool-utils.c create mode 100644 shared/nm-libnm-core-intern/nm-ethtool-utils.h create mode 100644 shared/nm-libnm-core-intern/nm-libnm-core-utils.c create mode 100644 shared/nm-libnm-core-intern/nm-libnm-core-utils.h create mode 100644 shared/nm-std-aux/c-list-util.c create mode 100644 shared/nm-std-aux/c-list-util.h create mode 100644 shared/nm-std-aux/nm-dbus-compat.h create mode 100644 shared/nm-std-aux/unaligned.h create mode 100644 shared/nm-udev-aux/nm-udev-utils.c create mode 100644 shared/nm-udev-aux/nm-udev-utils.h delete mode 100644 shared/nm-utils/c-list-util.c delete mode 100644 shared/nm-utils/c-list-util.h delete mode 100644 shared/nm-utils/nm-c-list.h delete mode 100644 shared/nm-utils/nm-dedup-multi.c delete mode 100644 shared/nm-utils/nm-dedup-multi.h delete mode 100644 shared/nm-utils/nm-enum-utils.c delete mode 100644 shared/nm-utils/nm-enum-utils.h delete mode 100644 shared/nm-utils/nm-errno.c delete mode 100644 shared/nm-utils/nm-errno.h delete mode 100644 shared/nm-utils/nm-glib.h delete mode 100644 shared/nm-utils/nm-hash-utils.c delete mode 100644 shared/nm-utils/nm-hash-utils.h delete mode 100644 shared/nm-utils/nm-io-utils.c delete mode 100644 shared/nm-utils/nm-io-utils.h delete mode 100644 shared/nm-utils/nm-jansson.h delete mode 100644 shared/nm-utils/nm-logging-fwd.h delete mode 100644 shared/nm-utils/nm-macros-internal.h delete mode 100644 shared/nm-utils/nm-obj.h delete mode 100644 shared/nm-utils/nm-random-utils.c delete mode 100644 shared/nm-utils/nm-random-utils.h delete mode 100644 shared/nm-utils/nm-secret-utils.c delete mode 100644 shared/nm-utils/nm-secret-utils.h delete mode 100644 shared/nm-utils/nm-shared-utils.c delete mode 100644 shared/nm-utils/nm-shared-utils.h delete mode 100644 shared/nm-utils/nm-time-utils.c delete mode 100644 shared/nm-utils/nm-time-utils.h delete mode 100644 shared/nm-utils/nm-udev-utils.c delete mode 100644 shared/nm-utils/nm-udev-utils.h delete mode 100644 shared/nm-utils/unaligned.h delete mode 100644 shared/systemd/sd-adapt-shared/missing_socket.h create mode 100644 shared/systemd/sd-adapt-shared/namespace-util.h create mode 100644 shared/systemd/sd-adapt-shared/nulstr-util.h create mode 100644 shared/systemd/sd-adapt-shared/strxcpyx.h create mode 100644 shared/systemd/src/basic/errno-util.h create mode 100644 shared/systemd/src/basic/memory-util.c create mode 100644 shared/systemd/src/basic/memory-util.h create mode 100644 shared/systemd/src/basic/missing_socket.h create mode 100644 shared/systemd/src/basic/missing_stat.h delete mode 100644 shared/systemd/src/basic/refcnt.h create mode 100644 shared/systemd/src/basic/sort-util.h (limited to 'shared') diff --git a/shared/c-rbtree/src/c-rbtree.c b/shared/c-rbtree/src/c-rbtree.c index f58db849..31d74300 100644 --- a/shared/c-rbtree/src/c-rbtree.c +++ b/shared/c-rbtree/src/c-rbtree.c @@ -460,6 +460,7 @@ _public_ void c_rbtree_move(CRBTree *to, CRBTree *from) { if (from->root) { t = c_rbnode_pop_root(from->root); assert(t == from); + (void)t; to->root = from->root; from->root = NULL; diff --git a/shared/meson.build b/shared/meson.build index a6e94d6b..ed9bf03f 100644 --- a/shared/meson.build +++ b/shared/meson.build @@ -1,5 +1,7 @@ shared_inc = include_directories('.') +############################################################################### + shared_c_siphash = static_library( 'c-siphash', sources: 'c-siphash/src/c-siphash.c', @@ -10,6 +12,8 @@ shared_c_siphash_dep = declare_dependency( link_with: shared_c_siphash, ) +############################################################################### + shared_c_rbtree = static_library( 'c-rbtree', c_args: '-std=c11', @@ -23,6 +27,7 @@ shared_c_rbtree_dep = declare_dependency( link_with: shared_c_rbtree, ) +############################################################################### if enable_ebpf shared_n_acd_bpf_files = files('n-acd/src/n-acd-bpf.c') @@ -62,6 +67,8 @@ shared_n_acd_dep = declare_dependency( link_with: shared_n_acd, ) +############################################################################### + version_conf = configuration_data() version_conf.set('NM_MAJOR_VERSION', nm_major_version) version_conf.set('NM_MINOR_VERSION', nm_minor_version) @@ -73,8 +80,6 @@ version_header = configure_file( configuration: version_conf, ) -shared_nm_ethtool_utils_c = files('nm-ethtool-utils.c') - shared_nm_meta_setting_c = files('nm-meta-setting.c') shared_nm_test_utils_impl_c = files('nm-test-utils-impl.c') @@ -83,35 +88,60 @@ shared_nm_utils_nm_vpn_plugin_utils_c = files('nm-utils/nm-vpn-plugin-utils.c') ############################################################################### -shared_nm_utils_c_args = [ +shared_nm_std_aux = static_library( + 'nm-std-aux', + sources: files('nm-std-aux/c-list-util.c'), + c_args: [ + '-DG_LOG_DOMAIN="@0@"'.format(libnm_name), + '-DNETWORKMANAGER_COMPILATION=0', + ], + include_directories: [ + top_inc, + shared_inc, + ], + dependencies: [ + ], +) + +shared_nm_std_aux_dep = declare_dependency( + link_with: shared_nm_std_aux, + include_directories: [ + top_inc, + shared_inc, + ], +) + +############################################################################### + +shared_nm_glib_aux_c_args = [ '-DG_LOG_DOMAIN="@0@"'.format(libnm_name), '-DNETWORKMANAGER_COMPILATION=(NM_NETWORKMANAGER_COMPILATION_GLIB|NM_NETWORKMANAGER_COMPILATION_WITH_GLIB_I18N_LIB)', ] -shared_nm_utils_base = static_library( +shared_nm_glib_aux = static_library( 'nm-utils-base', - sources: files('nm-utils/c-list-util.c', - 'nm-utils/nm-dedup-multi.c', - 'nm-utils/nm-enum-utils.c', - 'nm-utils/nm-errno.c', - 'nm-utils/nm-hash-utils.c', - 'nm-utils/nm-io-utils.c', - 'nm-utils/nm-random-utils.c', - 'nm-utils/nm-secret-utils.c', - 'nm-utils/nm-shared-utils.c', - 'nm-utils/nm-time-utils.c'), - c_args: shared_nm_utils_c_args, + sources: files('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-random-utils.c', + 'nm-glib-aux/nm-secret-utils.c', + 'nm-glib-aux/nm-shared-utils.c', + 'nm-glib-aux/nm-time-utils.c'), + c_args: shared_nm_glib_aux_c_args, include_directories: [ top_inc, shared_inc, ], dependencies: [ glib_dep, + shared_nm_std_aux_dep, ], ) -shared_nm_utils_base_dep = declare_dependency( - link_with: shared_nm_utils_base, +shared_nm_glib_aux_dep = declare_dependency( + link_with: shared_nm_glib_aux, include_directories: [ top_inc, shared_inc, @@ -119,54 +149,38 @@ shared_nm_utils_base_dep = declare_dependency( dependencies: glib_dep, ) -shared_nm_utils_udev = static_library( - 'nm-utils-udev', - sources: files('nm-utils/nm-udev-utils.c'), - c_args: shared_nm_utils_c_args, +############################################################################### + +shared_nm_udev_aux = static_library( + 'nm-udev-aux', + sources: files('nm-udev-aux/nm-udev-utils.c'), + c_args: shared_nm_glib_aux_c_args, include_directories: [ top_inc, shared_inc, ], dependencies: [ glib_dep, - shared_nm_utils_base_dep, + shared_nm_glib_aux_dep, libudev_dep, ], ) -shared_nm_utils_udev_dep = declare_dependency( - link_with: shared_nm_utils_udev, +shared_nm_udev_aux_dep = declare_dependency( + link_with: shared_nm_udev_aux, include_directories: [ top_inc, shared_inc, ], dependencies: [ glib_dep, - shared_nm_utils_base_dep, + shared_nm_glib_aux_dep, libudev_dep, ], ) ############################################################################### -test_shared_general = executable( - 'nm-utils/tests/test-shared-general', - [ 'nm-utils/tests/test-shared-general.c', ], - c_args: [ - '-DNETWORKMANAGER_COMPILATION_TEST', - '-DNETWORKMANAGER_COMPILATION=(NM_NETWORKMANAGER_COMPILATION_GLIB|NM_NETWORKMANAGER_COMPILATION_WITH_GLIB_I18N_PROG)', - ], - dependencies: shared_nm_utils_base_dep, - link_with: shared_c_siphash, -) -test( - 'shared/nm-utils/test-shared-general', - test_script, - args: test_args + [test_shared_general.full_path()] -) - -############################################################################### - libnm_systemd_shared = static_library( 'nm-systemd-shared', sources: files( @@ -185,6 +199,7 @@ libnm_systemd_shared = static_library( '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', @@ -206,7 +221,7 @@ libnm_systemd_shared = static_library( 'systemd/sd-adapt-shared', 'systemd/src/basic', ), - dependencies: shared_nm_utils_base_dep, + dependencies: shared_nm_glib_aux_dep, c_args: [ '-DNETWORKMANAGER_COMPILATION=NM_NETWORKMANAGER_COMPILATION_SYSTEMD_SHARED', '-DG_LOG_DOMAIN="libnm"', @@ -219,7 +234,7 @@ libnm_systemd_shared_dep = declare_dependency( 'systemd/src/basic', ), dependencies: [ - shared_nm_utils_base_dep, + shared_nm_glib_aux_dep, ], link_with: [ libnm_systemd_shared, @@ -235,7 +250,7 @@ libnm_systemd_logging_stub = static_library( 'systemd/sd-adapt-shared', 'systemd/src/basic', ), - dependencies: shared_nm_utils_base_dep, + dependencies: shared_nm_glib_aux_dep, c_args: [ '-DNETWORKMANAGER_COMPILATION=NM_NETWORKMANAGER_COMPILATION_SYSTEMD_SHARED', '-DG_LOG_DOMAIN="libnm"', @@ -250,3 +265,21 @@ libnm_systemd_shared_no_logging_dep = declare_dependency( libnm_systemd_logging_stub, ], ) + +############################################################################### + +test_shared_general = executable( + 'nm-utils/tests/test-shared-general', + [ 'nm-utils/tests/test-shared-general.c', ], + c_args: [ + '-DNETWORKMANAGER_COMPILATION_TEST', + '-DNETWORKMANAGER_COMPILATION=(NM_NETWORKMANAGER_COMPILATION_GLIB|NM_NETWORKMANAGER_COMPILATION_WITH_GLIB_I18N_PROG)', + ], + dependencies: shared_nm_glib_aux_dep, + link_with: shared_c_siphash, +) +test( + 'shared/nm-utils/test-shared-general', + test_script, + args: test_args + [test_shared_general.full_path()] +) diff --git a/shared/n-acd/src/n-acd-probe.c b/shared/n-acd/src/n-acd-probe.c index 8c233b56..d4da0fd5 100644 --- a/shared/n-acd/src/n-acd-probe.c +++ b/shared/n-acd/src/n-acd-probe.c @@ -215,6 +215,7 @@ static void n_acd_probe_unlink(NAcdProbe *probe) { if (n_acd_probe_is_unique(probe)) { r = n_acd_bpf_map_remove(probe->acd->fd_bpf_map, &probe->ip); assert(r >= 0); + (void)r; --probe->acd->n_bpf_map; } c_rbnode_unlink(&probe->ip_node); diff --git a/shared/n-acd/src/util/timer.c b/shared/n-acd/src/util/timer.c index 29627af7..07dbf34e 100644 --- a/shared/n-acd/src/util/timer.c +++ b/shared/n-acd/src/util/timer.c @@ -44,6 +44,7 @@ void timer_now(Timer *timer, uint64_t *nowp) { r = clock_gettime(timer->clock, &ts); assert(r >= 0); + (void)r; *nowp = ts.tv_sec * UINT64_C(1000000000) + ts.tv_nsec; } @@ -74,6 +75,7 @@ void timer_rearm(Timer *timer) { }, NULL); assert(r >= 0); + (void)r; timer->scheduled_timeout = time; } diff --git a/shared/nm-common-macros.h b/shared/nm-common-macros.h deleted file mode 100644 index f5aa3a1e..00000000 --- a/shared/nm-common-macros.h +++ /dev/null @@ -1,62 +0,0 @@ -/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ -/* NetworkManager -- Network link manager - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the - * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, - * Boston, MA 02110-1301 USA. - * - * (C) Copyright 2016 Red Hat, Inc. - */ - -#ifndef __NM_COMMON_MACROS_H__ -#define __NM_COMMON_MACROS_H__ - -/*****************************************************************************/ - -#define NM_AUTH_PERMISSION_ENABLE_DISABLE_NETWORK "org.freedesktop.NetworkManager.enable-disable-network" -#define NM_AUTH_PERMISSION_SLEEP_WAKE "org.freedesktop.NetworkManager.sleep-wake" -#define NM_AUTH_PERMISSION_ENABLE_DISABLE_WIFI "org.freedesktop.NetworkManager.enable-disable-wifi" -#define NM_AUTH_PERMISSION_ENABLE_DISABLE_WWAN "org.freedesktop.NetworkManager.enable-disable-wwan" -#define NM_AUTH_PERMISSION_ENABLE_DISABLE_WIMAX "org.freedesktop.NetworkManager.enable-disable-wimax" -#define NM_AUTH_PERMISSION_NETWORK_CONTROL "org.freedesktop.NetworkManager.network-control" -#define NM_AUTH_PERMISSION_WIFI_SHARE_PROTECTED "org.freedesktop.NetworkManager.wifi.share.protected" -#define NM_AUTH_PERMISSION_WIFI_SHARE_OPEN "org.freedesktop.NetworkManager.wifi.share.open" -#define NM_AUTH_PERMISSION_SETTINGS_MODIFY_SYSTEM "org.freedesktop.NetworkManager.settings.modify.system" -#define NM_AUTH_PERMISSION_SETTINGS_MODIFY_OWN "org.freedesktop.NetworkManager.settings.modify.own" -#define NM_AUTH_PERMISSION_SETTINGS_MODIFY_HOSTNAME "org.freedesktop.NetworkManager.settings.modify.hostname" -#define NM_AUTH_PERMISSION_SETTINGS_MODIFY_GLOBAL_DNS "org.freedesktop.NetworkManager.settings.modify.global-dns" -#define NM_AUTH_PERMISSION_RELOAD "org.freedesktop.NetworkManager.reload" -#define NM_AUTH_PERMISSION_CHECKPOINT_ROLLBACK "org.freedesktop.NetworkManager.checkpoint-rollback" -#define NM_AUTH_PERMISSION_ENABLE_DISABLE_STATISTICS "org.freedesktop.NetworkManager.enable-disable-statistics" -#define NM_AUTH_PERMISSION_ENABLE_DISABLE_CONNECTIVITY_CHECK "org.freedesktop.NetworkManager.enable-disable-connectivity-check" -#define NM_AUTH_PERMISSION_WIFI_SCAN "org.freedesktop.NetworkManager.wifi.scan" - -#define NM_CLONED_MAC_PRESERVE "preserve" -#define NM_CLONED_MAC_PERMANENT "permanent" -#define NM_CLONED_MAC_RANDOM "random" -#define NM_CLONED_MAC_STABLE "stable" - -static inline gboolean -NM_CLONED_MAC_IS_SPECIAL (const char *str) -{ - return NM_IN_STRSET (str, - NM_CLONED_MAC_PRESERVE, - NM_CLONED_MAC_PERMANENT, - NM_CLONED_MAC_RANDOM, - NM_CLONED_MAC_STABLE); -} - -/*****************************************************************************/ - -#endif /* __NM_COMMON_MACROS_H__ */ diff --git a/shared/nm-dbus-compat.h b/shared/nm-dbus-compat.h deleted file mode 100644 index dd97b5fd..00000000 --- a/shared/nm-dbus-compat.h +++ /dev/null @@ -1,74 +0,0 @@ -/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ -/* - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Copyright 2015 Red Hat, Inc. - */ - -#ifndef __NM_DBUS_COMPAT_H__ -#define __NM_DBUS_COMPAT_H__ - -/* Copied from */ - -/* Bus names */ - -/** The bus name used to talk to the bus itself. */ -#define DBUS_SERVICE_DBUS "org.freedesktop.DBus" - -/* Paths */ -/** The object path used to talk to the bus itself. */ -#define DBUS_PATH_DBUS "/org/freedesktop/DBus" -/** The object path used in local/in-process-generated messages. */ -#define DBUS_PATH_LOCAL "/org/freedesktop/DBus/Local" - -/* Interfaces, these #define don't do much other than - * catch typos at compile time - */ -/** The interface exported by the object with #DBUS_SERVICE_DBUS and #DBUS_PATH_DBUS */ -#define DBUS_INTERFACE_DBUS "org.freedesktop.DBus" -/** The interface supported by introspectable objects */ -#define DBUS_INTERFACE_INTROSPECTABLE "org.freedesktop.DBus.Introspectable" -/** The interface supported by objects with properties */ -#define DBUS_INTERFACE_PROPERTIES "org.freedesktop.DBus.Properties" -/** The interface supported by most dbus peers */ -#define DBUS_INTERFACE_PEER "org.freedesktop.DBus.Peer" - -/** This is a special interface whose methods can only be invoked - * by the local implementation (messages from remote apps aren't - * allowed to specify this interface). - */ -#define DBUS_INTERFACE_LOCAL "org.freedesktop.DBus.Local" - -/* Owner flags */ -#define DBUS_NAME_FLAG_ALLOW_REPLACEMENT 0x1 /**< Allow another service to become the primary owner if requested */ -#define DBUS_NAME_FLAG_REPLACE_EXISTING 0x2 /**< Request to replace the current primary owner */ -#define DBUS_NAME_FLAG_DO_NOT_QUEUE 0x4 /**< If we can not become the primary owner do not place us in the queue */ - -/* Replies to request for a name */ -#define DBUS_REQUEST_NAME_REPLY_PRIMARY_OWNER 1 /**< Service has become the primary owner of the requested name */ -#define DBUS_REQUEST_NAME_REPLY_IN_QUEUE 2 /**< Service could not become the primary owner and has been placed in the queue */ -#define DBUS_REQUEST_NAME_REPLY_EXISTS 3 /**< Service is already in the queue */ -#define DBUS_REQUEST_NAME_REPLY_ALREADY_OWNER 4 /**< Service is already the primary owner */ - -/* Replies to releasing a name */ -#define DBUS_RELEASE_NAME_REPLY_RELEASED 1 /**< Service was released from the given name */ -#define DBUS_RELEASE_NAME_REPLY_NON_EXISTENT 2 /**< The given name does not exist on the bus */ -#define DBUS_RELEASE_NAME_REPLY_NOT_OWNER 3 /**< Service is not an owner of the given name */ - -/* Replies to service starts */ -#define DBUS_START_REPLY_SUCCESS 1 /**< Service was auto started */ -#define DBUS_START_REPLY_ALREADY_RUNNING 2 /**< Service was already running */ - -#endif /* __NM_DBUS_COMPAT_H__ */ diff --git a/shared/nm-default.h b/shared/nm-default.h index 26d6476a..54e99167 100644 --- a/shared/nm-default.h +++ b/shared/nm-default.h @@ -290,14 +290,14 @@ _nm_g_return_if_fail_warning (const char *log_domain, /*****************************************************************************/ -#include "nm-utils/nm-macros-internal.h" -#include "nm-utils/nm-shared-utils.h" -#include "nm-utils/nm-errno.h" +#include "nm-glib-aux/nm-macros-internal.h" +#include "nm-glib-aux/nm-shared-utils.h" +#include "nm-glib-aux/nm-errno.h" #if (NETWORKMANAGER_COMPILATION) & NM_NETWORKMANAGER_COMPILATION_WITH_LIBNM_UTIL /* no hash-utils in legacy code. */ #else -#include "nm-utils/nm-hash-utils.h" +#include "nm-glib-aux/nm-hash-utils.h" #endif /*****************************************************************************/ diff --git a/shared/nm-dispatcher-api.h b/shared/nm-dispatcher-api.h deleted file mode 100644 index b1f28e71..00000000 --- a/shared/nm-dispatcher-api.h +++ /dev/null @@ -1,61 +0,0 @@ -/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ -/* NetworkManager -- Network link manager - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Copyright (C) 2008 - 2012 Red Hat, Inc. - */ - -#define NMD_SCRIPT_DIR_DEFAULT NMCONFDIR "/dispatcher.d" -#define NMD_SCRIPT_DIR_PRE_UP NMD_SCRIPT_DIR_DEFAULT "/pre-up.d" -#define NMD_SCRIPT_DIR_PRE_DOWN NMD_SCRIPT_DIR_DEFAULT "/pre-down.d" -#define NMD_SCRIPT_DIR_NO_WAIT NMD_SCRIPT_DIR_DEFAULT "/no-wait.d" - -#define NM_DISPATCHER_DBUS_SERVICE "org.freedesktop.nm_dispatcher" -#define NM_DISPATCHER_DBUS_INTERFACE "org.freedesktop.nm_dispatcher" -#define NM_DISPATCHER_DBUS_PATH "/org/freedesktop/nm_dispatcher" - -#define NMD_CONNECTION_PROPS_PATH "path" -#define NMD_CONNECTION_PROPS_FILENAME "filename" -#define NMD_CONNECTION_PROPS_EXTERNAL "external" - -#define NMD_DEVICE_PROPS_INTERFACE "interface" -#define NMD_DEVICE_PROPS_IP_INTERFACE "ip-interface" -#define NMD_DEVICE_PROPS_TYPE "type" -#define NMD_DEVICE_PROPS_STATE "state" -#define NMD_DEVICE_PROPS_PATH "path" - -/* Actions */ -#define NMD_ACTION_HOSTNAME "hostname" -#define NMD_ACTION_PRE_UP "pre-up" -#define NMD_ACTION_UP "up" -#define NMD_ACTION_PRE_DOWN "pre-down" -#define NMD_ACTION_DOWN "down" -#define NMD_ACTION_VPN_PRE_UP "vpn-pre-up" -#define NMD_ACTION_VPN_UP "vpn-up" -#define NMD_ACTION_VPN_PRE_DOWN "vpn-pre-down" -#define NMD_ACTION_VPN_DOWN "vpn-down" -#define NMD_ACTION_DHCP4_CHANGE "dhcp4-change" -#define NMD_ACTION_DHCP6_CHANGE "dhcp6-change" -#define NMD_ACTION_CONNECTIVITY_CHANGE "connectivity-change" - -typedef enum { - DISPATCH_RESULT_UNKNOWN = 0, - DISPATCH_RESULT_SUCCESS = 1, - DISPATCH_RESULT_EXEC_FAILED = 2, - DISPATCH_RESULT_FAILED = 3, - DISPATCH_RESULT_TIMEOUT = 4, -} DispatchResult; - diff --git a/shared/nm-ethtool-utils.c b/shared/nm-ethtool-utils.c deleted file mode 100644 index 3313274a..00000000 --- a/shared/nm-ethtool-utils.c +++ /dev/null @@ -1,225 +0,0 @@ -/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ - -/* - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the - * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, - * Boston, MA 02110-1301 USA. - * - * Copyright 2018 Red Hat, Inc. - */ - -#include "nm-default.h" - -#include "nm-ethtool-utils.h" - -#include "nm-setting-ethtool.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 (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_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_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_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_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_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), - [_NM_ETHTOOL_ID_NUM] = NULL, -}; - -static const guint8 _by_name[_NM_ETHTOOL_ID_NUM] = { - /* sorted by optname. */ - 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_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_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_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_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_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, -}; - -/*****************************************************************************/ - -static void -_ASSERT_data (void) -{ -#if NM_MORE_ASSERTS > 10 - int i; - - 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); - } - } - } -#endif -} - -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; - - nm_assert (optname); - - _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]]; -} diff --git a/shared/nm-ethtool-utils.h b/shared/nm-ethtool-utils.h deleted file mode 100644 index 5f22a9a0..00000000 --- a/shared/nm-ethtool-utils.h +++ /dev/null @@ -1,120 +0,0 @@ -/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ -/* - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the - * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, - * Boston, MA 02110-1301 USA. - * - * Copyright 2018 Red Hat, Inc. - */ - -#ifndef __NM_ETHTOOL_UTILS_H__ -#define __NM_ETHTOOL_UTILS_H__ - -/*****************************************************************************/ - -typedef enum { - NM_ETHTOOL_ID_UNKNOWN = -1, - - _NM_ETHTOOL_ID_FIRST = 0, - - _NM_ETHTOOL_ID_FEATURE_FIRST = _NM_ETHTOOL_ID_FIRST, - 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_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_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_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_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_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_FEATURE_NUM = (_NM_ETHTOOL_ID_FEATURE_LAST - _NM_ETHTOOL_ID_FEATURE_FIRST + 1), - - _NM_ETHTOOL_ID_LAST = _NM_ETHTOOL_ID_FEATURE_LAST, - - _NM_ETHTOOL_ID_NUM = (_NM_ETHTOOL_ID_LAST - _NM_ETHTOOL_ID_FIRST + 1), -} NMEthtoolID; - -typedef struct { - const char *optname; - NMEthtoolID id; -} NMEthtoolData; - -extern const NMEthtoolData *const nm_ethtool_data[/*_NM_ETHTOOL_ID_NUM + NULL-terminated*/]; - -const NMEthtoolData *nm_ethtool_data_get_by_optname (const char *optname); - -/****************************************************************************/ - -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; -} - -static inline gboolean -nm_ethtool_id_is_feature (NMEthtoolID id) -{ - return id >= _NM_ETHTOOL_ID_FEATURE_FIRST && id <= _NM_ETHTOOL_ID_FEATURE_LAST; -} - -/****************************************************************************/ - -#endif /* __NM_ETHTOOL_UTILS_H__ */ diff --git a/shared/nm-glib-aux/nm-c-list.h b/shared/nm-glib-aux/nm-c-list.h new file mode 100644 index 00000000..5c73f574 --- /dev/null +++ b/shared/nm-glib-aux/nm-c-list.h @@ -0,0 +1,117 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* NetworkManager -- Network link manager + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA. + * + * (C) Copyright 2014 Red Hat, Inc. + */ + +#ifndef __NM_C_LIST_H__ +#define __NM_C_LIST_H__ + +#include "c-list/src/c-list.h" + +/*****************************************************************************/ + +#define nm_c_list_contains_entry(list, what, member) \ + ({ \ + typeof (what) _what = (what); \ + \ + _what && c_list_contains (list, &_what->member); \ + }) + +typedef struct { + CList lst; + void *data; +} NMCListElem; + +static inline NMCListElem * +nm_c_list_elem_new_stale (void *data) +{ + NMCListElem *elem; + + elem = g_slice_new (NMCListElem); + elem->data = data; + return elem; +} + +static inline void * +nm_c_list_elem_get (CList *lst) +{ + if (!lst) + return NULL; + return c_list_entry (lst, NMCListElem, lst)->data; +} + +static inline void +nm_c_list_elem_free (NMCListElem *elem) +{ + if (elem) { + c_list_unlink_stale (&elem->lst); + g_slice_free (NMCListElem, elem); + } +} + +static inline void +nm_c_list_elem_free_all (CList *head, GDestroyNotify free_fcn) +{ + NMCListElem *elem; + + while ((elem = c_list_first_entry (head, NMCListElem, lst))) { + if (free_fcn) + free_fcn (elem->data); + c_list_unlink_stale (&elem->lst); + g_slice_free (NMCListElem, elem); + } +} + +/*****************************************************************************/ + +static inline gboolean +nm_c_list_move_before (CList *lst, CList *elem) +{ + nm_assert (lst); + nm_assert (elem); + nm_assert (c_list_contains (lst, elem)); + + if ( lst != elem + && lst->prev != elem) { + c_list_unlink_stale (elem); + c_list_link_before (lst, elem); + return TRUE; + } + return FALSE; +} +#define nm_c_list_move_tail(lst, elem) nm_c_list_move_before (lst, elem) + +static inline gboolean +nm_c_list_move_after (CList *lst, CList *elem) +{ + nm_assert (lst); + nm_assert (elem); + nm_assert (c_list_contains (lst, elem)); + + if ( lst != elem + && lst->next != elem) { + c_list_unlink_stale (elem); + c_list_link_after (lst, elem); + return TRUE; + } + return FALSE; +} +#define nm_c_list_move_front(lst, elem) nm_c_list_move_after (lst, elem) + +#endif /* __NM_C_LIST_H__ */ diff --git a/shared/nm-glib-aux/nm-dedup-multi.c b/shared/nm-glib-aux/nm-dedup-multi.c new file mode 100644 index 00000000..5bdc3e3c --- /dev/null +++ b/shared/nm-glib-aux/nm-dedup-multi.c @@ -0,0 +1,1092 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* NetworkManager -- Network link manager + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA. + * + * (C) Copyright 2017 Red Hat, Inc. + */ + +#include "nm-default.h" + +#include "nm-dedup-multi.h" + +#include "nm-hash-utils.h" +#include "nm-c-list.h" + +/*****************************************************************************/ + +typedef struct { + /* the stack-allocated lookup entry. It has a compatible + * memory layout with NMDedupMultiEntry and NMDedupMultiHeadEntry. + * + * It is recognizable by having lst_entries_sentinel.next set to NULL. + * Contrary to the other entries, which have lst_entries.next + * always non-NULL. + * */ + CList lst_entries_sentinel; + const NMDedupMultiObj *obj; + const NMDedupMultiIdxType *idx_type; + bool lookup_head; +} LookupEntry; + +struct _NMDedupMultiIndex { + int ref_count; + GHashTable *idx_entries; + GHashTable *idx_objs; +}; + +/*****************************************************************************/ + +static void +ASSERT_idx_type (const NMDedupMultiIdxType *idx_type) +{ + nm_assert (idx_type); +#if NM_MORE_ASSERTS > 10 + nm_assert (idx_type->klass); + nm_assert (idx_type->klass->idx_obj_id_hash_update); + nm_assert (idx_type->klass->idx_obj_id_equal); + nm_assert (!!idx_type->klass->idx_obj_partition_hash_update == !!idx_type->klass->idx_obj_partition_equal); + nm_assert (idx_type->lst_idx_head.next); +#endif +} + +void +nm_dedup_multi_idx_type_init (NMDedupMultiIdxType *idx_type, + const NMDedupMultiIdxTypeClass *klass) +{ + nm_assert (idx_type); + nm_assert (klass); + + memset (idx_type, 0, sizeof (*idx_type)); + idx_type->klass = klass; + c_list_init (&idx_type->lst_idx_head); + + ASSERT_idx_type (idx_type); +} + +/*****************************************************************************/ + +static NMDedupMultiEntry * +_entry_lookup_obj (const NMDedupMultiIndex *self, + const NMDedupMultiIdxType *idx_type, + const NMDedupMultiObj *obj) +{ + const LookupEntry stack_entry = { + .obj = obj, + .idx_type = idx_type, + .lookup_head = FALSE, + }; + + ASSERT_idx_type (idx_type); + return g_hash_table_lookup (self->idx_entries, &stack_entry); +} + +static NMDedupMultiHeadEntry * +_entry_lookup_head (const NMDedupMultiIndex *self, + const NMDedupMultiIdxType *idx_type, + const NMDedupMultiObj *obj) +{ + NMDedupMultiHeadEntry *head_entry; + const LookupEntry stack_entry = { + .obj = obj, + .idx_type = idx_type, + .lookup_head = TRUE, + }; + + ASSERT_idx_type (idx_type); + + if (!idx_type->klass->idx_obj_partition_equal) { + if (c_list_is_empty (&idx_type->lst_idx_head)) + head_entry = NULL; + else { + nm_assert (c_list_length (&idx_type->lst_idx_head) == 1); + head_entry = c_list_entry (idx_type->lst_idx_head.next, NMDedupMultiHeadEntry, lst_idx); + } + nm_assert (head_entry == g_hash_table_lookup (self->idx_entries, &stack_entry)); + return head_entry; + } + + return g_hash_table_lookup (self->idx_entries, &stack_entry); +} + +static void +_entry_unpack (const NMDedupMultiEntry *entry, + const NMDedupMultiIdxType **out_idx_type, + const NMDedupMultiObj **out_obj, + gboolean *out_lookup_head) +{ + const NMDedupMultiHeadEntry *head_entry; + const LookupEntry *lookup_entry; + + nm_assert (entry); + + G_STATIC_ASSERT_EXPR (G_STRUCT_OFFSET (LookupEntry, lst_entries_sentinel) == G_STRUCT_OFFSET (NMDedupMultiEntry, lst_entries)); + G_STATIC_ASSERT_EXPR (G_STRUCT_OFFSET (NMDedupMultiEntry, lst_entries) == G_STRUCT_OFFSET (NMDedupMultiHeadEntry, lst_entries_head)); + G_STATIC_ASSERT_EXPR (G_STRUCT_OFFSET (NMDedupMultiEntry, obj) == G_STRUCT_OFFSET (NMDedupMultiHeadEntry, idx_type)); + G_STATIC_ASSERT_EXPR (G_STRUCT_OFFSET (NMDedupMultiEntry, is_head) == G_STRUCT_OFFSET (NMDedupMultiHeadEntry, is_head)); + + if (!entry->lst_entries.next) { + /* the entry is stack-allocated by _entry_lookup(). */ + lookup_entry = (LookupEntry *) entry; + *out_obj = lookup_entry->obj; + *out_idx_type = lookup_entry->idx_type; + *out_lookup_head = lookup_entry->lookup_head; + } else if (entry->is_head) { + head_entry = (NMDedupMultiHeadEntry *) entry; + nm_assert (!c_list_is_empty (&head_entry->lst_entries_head)); + *out_obj = c_list_entry (head_entry->lst_entries_head.next, NMDedupMultiEntry, lst_entries)->obj; + *out_idx_type = head_entry->idx_type; + *out_lookup_head = TRUE; + } else { + *out_obj = entry->obj; + *out_idx_type = entry->head->idx_type; + *out_lookup_head = FALSE; + } + + nm_assert (NM_IN_SET (*out_lookup_head, FALSE, TRUE)); + ASSERT_idx_type (*out_idx_type); + + /* for lookup of the head, we allow to omit object, but only + * if the idx_type does not partition the objects. Otherwise, we + * require a obj to compare. */ + nm_assert ( !*out_lookup_head + || ( *out_obj + || !(*out_idx_type)->klass->idx_obj_partition_equal)); + + /* lookup of the object requires always an object. */ + nm_assert ( *out_lookup_head + || *out_obj); +} + +static guint +_dict_idx_entries_hash (const NMDedupMultiEntry *entry) +{ + const NMDedupMultiIdxType *idx_type; + const NMDedupMultiObj *obj; + gboolean lookup_head; + NMHashState h; + + _entry_unpack (entry, &idx_type, &obj, &lookup_head); + + nm_hash_init (&h, 1914869417u); + if (idx_type->klass->idx_obj_partition_hash_update) { + nm_assert (obj); + idx_type->klass->idx_obj_partition_hash_update (idx_type, obj, &h); + } + + if (!lookup_head) + idx_type->klass->idx_obj_id_hash_update (idx_type, obj, &h); + + nm_hash_update_val (&h, idx_type); + return nm_hash_complete (&h); +} + +static gboolean +_dict_idx_entries_equal (const NMDedupMultiEntry *entry_a, + const NMDedupMultiEntry *entry_b) +{ + const NMDedupMultiIdxType *idx_type_a, *idx_type_b; + const NMDedupMultiObj *obj_a, *obj_b; + gboolean lookup_head_a, lookup_head_b; + + _entry_unpack (entry_a, &idx_type_a, &obj_a, &lookup_head_a); + _entry_unpack (entry_b, &idx_type_b, &obj_b, &lookup_head_b); + + if ( idx_type_a != idx_type_b + || lookup_head_a != lookup_head_b) + return FALSE; + if (!nm_dedup_multi_idx_type_partition_equal (idx_type_a, obj_a, obj_b)) + return FALSE; + if ( !lookup_head_a + && !nm_dedup_multi_idx_type_id_equal (idx_type_a, obj_a, obj_b)) + return FALSE; + return TRUE; +} + +/*****************************************************************************/ + +static gboolean +_add (NMDedupMultiIndex *self, + NMDedupMultiIdxType *idx_type, + const NMDedupMultiObj *obj, + NMDedupMultiEntry *entry, + NMDedupMultiIdxMode mode, + const NMDedupMultiEntry *entry_order, + NMDedupMultiHeadEntry *head_existing, + const NMDedupMultiEntry **out_entry, + const NMDedupMultiObj **out_obj_old) +{ + NMDedupMultiHeadEntry *head_entry; + const NMDedupMultiObj *obj_new, *obj_old; + gboolean add_head_entry = FALSE; + + nm_assert (self); + ASSERT_idx_type (idx_type); + nm_assert (obj); + nm_assert (NM_IN_SET (mode, + NM_DEDUP_MULTI_IDX_MODE_PREPEND, + NM_DEDUP_MULTI_IDX_MODE_PREPEND_FORCE, + NM_DEDUP_MULTI_IDX_MODE_APPEND, + NM_DEDUP_MULTI_IDX_MODE_APPEND_FORCE)); + nm_assert (!head_existing || head_existing->idx_type == idx_type); + nm_assert (({ + const NMDedupMultiHeadEntry *_h; + gboolean _ok = TRUE; + if (head_existing) { + _h = nm_dedup_multi_index_lookup_head (self, idx_type, obj); + if (head_existing == NM_DEDUP_MULTI_HEAD_ENTRY_MISSING) + _ok = (_h == NULL); + else + _ok = (_h == head_existing); + } + _ok; + })); + + if (entry) { + gboolean changed = FALSE; + + nm_dedup_multi_entry_set_dirty (entry, FALSE); + + nm_assert (!head_existing || entry->head == head_existing); + nm_assert (!entry_order || entry_order->head == entry->head); + nm_assert (!entry_order || c_list_contains (&entry->lst_entries, &entry_order->lst_entries)); + nm_assert (!entry_order || c_list_contains (&entry_order->lst_entries, &entry->lst_entries)); + + switch (mode) { + case NM_DEDUP_MULTI_IDX_MODE_PREPEND_FORCE: + if (entry_order) { + if (nm_c_list_move_before ((CList *) &entry_order->lst_entries, &entry->lst_entries)) + changed = TRUE; + } else { + if (nm_c_list_move_front ((CList *) &entry->head->lst_entries_head, &entry->lst_entries)) + changed = TRUE; + } + break; + case NM_DEDUP_MULTI_IDX_MODE_APPEND_FORCE: + if (entry_order) { + if (nm_c_list_move_after ((CList *) &entry_order->lst_entries, &entry->lst_entries)) + changed = TRUE; + } else { + if (nm_c_list_move_tail ((CList *) &entry->head->lst_entries_head, &entry->lst_entries)) + changed = TRUE; + } + break; + case NM_DEDUP_MULTI_IDX_MODE_PREPEND: + case NM_DEDUP_MULTI_IDX_MODE_APPEND: + break; + }; + + nm_assert (obj->klass == ((const NMDedupMultiObj *) entry->obj)->klass); + if ( obj == entry->obj + || obj->klass->obj_full_equal (obj, + entry->obj)) { + NM_SET_OUT (out_entry, entry); + NM_SET_OUT (out_obj_old, nm_dedup_multi_obj_ref (entry->obj)); + return changed; + } + + obj_new = nm_dedup_multi_index_obj_intern (self, obj); + + obj_old = entry->obj; + entry->obj = obj_new; + + NM_SET_OUT (out_entry, entry); + if (out_obj_old) + *out_obj_old = obj_old; + else + nm_dedup_multi_obj_unref (obj_old); + return TRUE; + } + + if ( idx_type->klass->idx_obj_partitionable + && !idx_type->klass->idx_obj_partitionable (idx_type, obj)) { + /* this object cannot be partitioned by this idx_type. */ + nm_assert (!head_existing || head_existing == NM_DEDUP_MULTI_HEAD_ENTRY_MISSING); + NM_SET_OUT (out_entry, NULL); + NM_SET_OUT (out_obj_old, NULL); + return FALSE; + } + + obj_new = nm_dedup_multi_index_obj_intern (self, obj); + + if (!head_existing) + head_entry = _entry_lookup_head (self, idx_type, obj_new); + else if (head_existing == NM_DEDUP_MULTI_HEAD_ENTRY_MISSING) + head_entry = NULL; + else + head_entry = head_existing; + + if (!head_entry) { + head_entry = g_slice_new0 (NMDedupMultiHeadEntry); + head_entry->is_head = TRUE; + head_entry->idx_type = idx_type; + c_list_init (&head_entry->lst_entries_head); + c_list_link_tail (&idx_type->lst_idx_head, &head_entry->lst_idx); + add_head_entry = TRUE; + } else + nm_assert (c_list_contains (&idx_type->lst_idx_head, &head_entry->lst_idx)); + + if (entry_order) { + nm_assert (!add_head_entry); + nm_assert (entry_order->head == head_entry); + nm_assert (c_list_contains (&head_entry->lst_entries_head, &entry_order->lst_entries)); + nm_assert (c_list_contains (&entry_order->lst_entries, &head_entry->lst_entries_head)); + } + + entry = g_slice_new0 (NMDedupMultiEntry); + entry->obj = obj_new; + entry->head = head_entry; + + switch (mode) { + case NM_DEDUP_MULTI_IDX_MODE_PREPEND: + case NM_DEDUP_MULTI_IDX_MODE_PREPEND_FORCE: + if (entry_order) + c_list_link_before ((CList *) &entry_order->lst_entries, &entry->lst_entries); + else + c_list_link_front (&head_entry->lst_entries_head, &entry->lst_entries); + break; + default: + if (entry_order) + c_list_link_after ((CList *) &entry_order->lst_entries, &entry->lst_entries); + else + c_list_link_tail (&head_entry->lst_entries_head, &entry->lst_entries); + break; + }; + + idx_type->len++; + head_entry->len++; + + if ( add_head_entry + && !g_hash_table_add (self->idx_entries, head_entry)) + nm_assert_not_reached (); + + if (!g_hash_table_add (self->idx_entries, entry)) + nm_assert_not_reached (); + + NM_SET_OUT (out_entry, entry); + NM_SET_OUT (out_obj_old, NULL); + return TRUE; +} + +gboolean +nm_dedup_multi_index_add (NMDedupMultiIndex *self, + NMDedupMultiIdxType *idx_type, + /*const NMDedupMultiObj * */ gconstpointer obj, + NMDedupMultiIdxMode mode, + const NMDedupMultiEntry **out_entry, + /* const NMDedupMultiObj ** */ gpointer out_obj_old) +{ + NMDedupMultiEntry *entry; + + g_return_val_if_fail (self, FALSE); + g_return_val_if_fail (idx_type, FALSE); + g_return_val_if_fail (obj, FALSE); + g_return_val_if_fail (NM_IN_SET (mode, + NM_DEDUP_MULTI_IDX_MODE_PREPEND, + NM_DEDUP_MULTI_IDX_MODE_PREPEND_FORCE, + NM_DEDUP_MULTI_IDX_MODE_APPEND, + NM_DEDUP_MULTI_IDX_MODE_APPEND_FORCE), + FALSE); + + entry = _entry_lookup_obj (self, idx_type, obj); + return _add (self, idx_type, obj, + entry, mode, + NULL, NULL, + out_entry, out_obj_old); +} + +/* nm_dedup_multi_index_add_full: + * @self: the index instance. + * @idx_type: the index handle for storing @obj. + * @obj: the NMDedupMultiObj instance to add. + * @mode: whether to append or prepend the new item. If @entry_order is given, + * the entry will be sorted after/before, instead of appending/prepending to + * the entire list. If a comparable object is already tracked, then it may + * still be resorted by specifying one of the "FORCE" modes. + * @entry_order: if not NULL, the new entry will be sorted before or after @entry_order. + * If given, @entry_order MUST be tracked by @self, and the object it points to MUST + * be in the same partition tracked by @idx_type. That is, they must have the same + * head_entry and it means, you must ensure that @entry_order and the created/modified + * entry will share the same head. + * @entry_existing: if not NULL, it safes a hash lookup of the entry where the + * object will be placed in. You can omit this, and it will be automatically + * detected (at the expense of an additional hash lookup). + * Basically, this is the result of nm_dedup_multi_index_lookup_obj(), + * with the peculiarity that if you know that @obj is not yet tracked, + * you may specify %NM_DEDUP_MULTI_ENTRY_MISSING. + * @head_existing: an optional argument to safe a lookup for the head. If specified, + * it must be identical to nm_dedup_multi_index_lookup_head(), with the peculiarity + * that if the head is not yet tracked, you may specify %NM_DEDUP_MULTI_HEAD_ENTRY_MISSING + * @out_entry: if give, return the added entry. This entry may have already exists (update) + * or be newly created. If @obj is not partitionable according to @idx_type, @obj + * is not to be added and it returns %NULL. + * @out_obj_old: if given, return the previously contained object. It only + * returns a object, if a matching entry was tracked previously, not if a + * new entry was created. Note that when passing @out_obj_old you obtain a reference + * to the boxed object and MUST return it with nm_dedup_multi_obj_unref(). + * + * Adds and object to the index. + * + * Return: %TRUE if anything changed, %FALSE if nothing changed. + */ +gboolean +nm_dedup_multi_index_add_full (NMDedupMultiIndex *self, + NMDedupMultiIdxType *idx_type, + /*const NMDedupMultiObj * */ gconstpointer obj, + NMDedupMultiIdxMode mode, + const NMDedupMultiEntry *entry_order, + const NMDedupMultiEntry *entry_existing, + const NMDedupMultiHeadEntry *head_existing, + const NMDedupMultiEntry **out_entry, + /* const NMDedupMultiObj ** */ gpointer out_obj_old) +{ + NMDedupMultiEntry *entry; + + g_return_val_if_fail (self, FALSE); + g_return_val_if_fail (idx_type, FALSE); + g_return_val_if_fail (obj, FALSE); + g_return_val_if_fail (NM_IN_SET (mode, + NM_DEDUP_MULTI_IDX_MODE_PREPEND, + NM_DEDUP_MULTI_IDX_MODE_PREPEND_FORCE, + NM_DEDUP_MULTI_IDX_MODE_APPEND, + NM_DEDUP_MULTI_IDX_MODE_APPEND_FORCE), + FALSE); + + if (entry_existing == NULL) + entry = _entry_lookup_obj (self, idx_type, obj); + else if (entry_existing == NM_DEDUP_MULTI_ENTRY_MISSING) { + nm_assert (!_entry_lookup_obj (self, idx_type, obj)); + entry = NULL; + } else { + nm_assert (entry_existing == _entry_lookup_obj (self, idx_type, obj)); + entry = (NMDedupMultiEntry *) entry_existing; + } + return _add (self, idx_type, obj, + entry, + mode, entry_order, + (NMDedupMultiHeadEntry *) head_existing, + out_entry, out_obj_old); +} + +/*****************************************************************************/ + +static void +_remove_entry (NMDedupMultiIndex *self, + NMDedupMultiEntry *entry, + gboolean *out_head_entry_removed) +{ + const NMDedupMultiObj *obj; + NMDedupMultiHeadEntry *head_entry; + NMDedupMultiIdxType *idx_type; + + nm_assert (self); + nm_assert (entry); + nm_assert (entry->obj); + nm_assert (entry->head); + nm_assert (!c_list_is_empty (&entry->lst_entries)); + nm_assert (g_hash_table_lookup (self->idx_entries, entry) == entry); + + head_entry = (NMDedupMultiHeadEntry *) entry->head; + obj = entry->obj; + + nm_assert (head_entry); + nm_assert (head_entry->len > 0); + nm_assert (g_hash_table_lookup (self->idx_entries, head_entry) == head_entry); + + idx_type = (NMDedupMultiIdxType *) head_entry->idx_type; + ASSERT_idx_type (idx_type); + + nm_assert (idx_type->len >= head_entry->len); + if (--head_entry->len > 0) { + nm_assert (idx_type->len > 1); + idx_type->len--; + head_entry = NULL; + } + + NM_SET_OUT (out_head_entry_removed, head_entry != NULL); + + if (!g_hash_table_remove (self->idx_entries, entry)) + nm_assert_not_reached (); + + if ( head_entry + && !g_hash_table_remove (self->idx_entries, head_entry)) + nm_assert_not_reached (); + + c_list_unlink_stale (&entry->lst_entries); + g_slice_free (NMDedupMultiEntry, entry); + + if (head_entry) { + nm_assert (c_list_is_empty (&head_entry->lst_entries_head)); + c_list_unlink_stale (&head_entry->lst_idx); + g_slice_free (NMDedupMultiHeadEntry, head_entry); + } + + nm_dedup_multi_obj_unref (obj); +} + +static guint +_remove_head (NMDedupMultiIndex *self, + NMDedupMultiHeadEntry *head_entry, + gboolean remove_all /* otherwise just dirty ones */, + gboolean mark_survivors_dirty) +{ + guint n; + gboolean head_entry_removed; + CList *iter_entry, *iter_entry_safe; + + nm_assert (self); + nm_assert (head_entry); + nm_assert (head_entry->len > 0); + nm_assert (head_entry->len == c_list_length (&head_entry->lst_entries_head)); + nm_assert (g_hash_table_lookup (self->idx_entries, head_entry) == head_entry); + + n = 0; + c_list_for_each_safe (iter_entry, iter_entry_safe, &head_entry->lst_entries_head) { + NMDedupMultiEntry *entry; + + entry = c_list_entry (iter_entry, NMDedupMultiEntry, lst_entries); + if ( remove_all + || entry->dirty) { + _remove_entry (self, + entry, + &head_entry_removed); + n++; + if (head_entry_removed) + break; + } else if (mark_survivors_dirty) + nm_dedup_multi_entry_set_dirty (entry, TRUE); + } + + return n; +} + +static guint +_remove_idx_entry (NMDedupMultiIndex *self, + NMDedupMultiIdxType *idx_type, + gboolean remove_all /* otherwise just dirty ones */, + gboolean mark_survivors_dirty) +{ + guint n; + CList *iter_idx, *iter_idx_safe; + + nm_assert (self); + ASSERT_idx_type (idx_type); + + n = 0; + c_list_for_each_safe (iter_idx, iter_idx_safe, &idx_type->lst_idx_head) { + n += _remove_head (self, + c_list_entry (iter_idx, NMDedupMultiHeadEntry, lst_idx), + remove_all, mark_survivors_dirty); + } + return n; +} + +guint +nm_dedup_multi_index_remove_entry (NMDedupMultiIndex *self, + gconstpointer entry) +{ + g_return_val_if_fail (self, 0); + + nm_assert (entry); + + if (!((NMDedupMultiEntry *) entry)->is_head) { + _remove_entry (self, (NMDedupMultiEntry *) entry, NULL); + return 1; + } + return _remove_head (self, (NMDedupMultiHeadEntry *) entry, TRUE, FALSE); +} + +guint +nm_dedup_multi_index_remove_obj (NMDedupMultiIndex *self, + NMDedupMultiIdxType *idx_type, + /*const NMDedupMultiObj * */ gconstpointer obj, + /*const NMDedupMultiObj ** */ gconstpointer *out_obj) +{ + const NMDedupMultiEntry *entry; + + entry = nm_dedup_multi_index_lookup_obj (self, idx_type, obj); + if (!entry) { + NM_SET_OUT (out_obj, NULL); + return 0; + } + + /* since we are about to remove the object, we obviously pass + * a reference to @out_obj, the caller MUST unref the object, + * if he chooses to provide @out_obj. */ + NM_SET_OUT (out_obj, nm_dedup_multi_obj_ref (entry->obj)); + + _remove_entry (self, (NMDedupMultiEntry *) entry, NULL); + return 1; +} + +guint +nm_dedup_multi_index_remove_head (NMDedupMultiIndex *self, + NMDedupMultiIdxType *idx_type, + /*const NMDedupMultiObj * */ gconstpointer obj) +{ + const NMDedupMultiHeadEntry *entry; + + entry = nm_dedup_multi_index_lookup_head (self, idx_type, obj); + return entry + ? _remove_head (self, (NMDedupMultiHeadEntry *) entry, TRUE, FALSE) + : 0; +} + +guint +nm_dedup_multi_index_remove_idx (NMDedupMultiIndex *self, + NMDedupMultiIdxType *idx_type) +{ + g_return_val_if_fail (self, 0); + g_return_val_if_fail (idx_type, 0); + + return _remove_idx_entry (self, idx_type, TRUE, FALSE); +} + +/*****************************************************************************/ + +/** + * nm_dedup_multi_index_lookup_obj: + * @self: the index cache + * @idx_type: the lookup index type + * @obj: the object to lookup. This means the match is performed + * according to NMDedupMultiIdxTypeClass's idx_obj_id_equal() + * of @idx_type. + * + * Returns: the cache entry or %NULL if the entry wasn't found. + */ +const NMDedupMultiEntry * +nm_dedup_multi_index_lookup_obj (const NMDedupMultiIndex *self, + const NMDedupMultiIdxType *idx_type, + /*const NMDedupMultiObj * */ gconstpointer obj) +{ + g_return_val_if_fail (self, FALSE); + g_return_val_if_fail (idx_type, FALSE); + g_return_val_if_fail (obj, FALSE); + + nm_assert (idx_type && idx_type->klass); + return _entry_lookup_obj (self, idx_type, obj); +} + +/** + * nm_dedup_multi_index_lookup_head: + * @self: the index cache + * @idx_type: the lookup index type + * @obj: the object to lookup, of type "const NMDedupMultiObj *". + * Depending on the idx_type, you *must* also provide a selector + * object, even when looking up the list head. That is, because + * the idx_type implementation may choose to partition the objects + * in distinct list, so you need a selector object to know which + * list head to lookup. + * + * Returns: the cache entry or %NULL if the entry wasn't found. + */ +const NMDedupMultiHeadEntry * +nm_dedup_multi_index_lookup_head (const NMDedupMultiIndex *self, + const NMDedupMultiIdxType *idx_type, + /*const NMDedupMultiObj * */ gconstpointer obj) +{ + g_return_val_if_fail (self, FALSE); + g_return_val_if_fail (idx_type, FALSE); + + return _entry_lookup_head (self, idx_type, obj); +} + +/*****************************************************************************/ + +void +nm_dedup_multi_index_dirty_set_head (NMDedupMultiIndex *self, + const NMDedupMultiIdxType *idx_type, + /*const NMDedupMultiObj * */ gconstpointer obj) +{ + NMDedupMultiHeadEntry *head_entry; + CList *iter_entry; + + g_return_if_fail (self); + g_return_if_fail (idx_type); + + head_entry = _entry_lookup_head (self, idx_type, obj); + if (!head_entry) + return; + + c_list_for_each (iter_entry, &head_entry->lst_entries_head) { + NMDedupMultiEntry *entry; + + entry = c_list_entry (iter_entry, NMDedupMultiEntry, lst_entries); + nm_dedup_multi_entry_set_dirty (entry, TRUE); + } +} + +void +nm_dedup_multi_index_dirty_set_idx (NMDedupMultiIndex *self, + const NMDedupMultiIdxType *idx_type) +{ + CList *iter_idx, *iter_entry; + + g_return_if_fail (self); + g_return_if_fail (idx_type); + + c_list_for_each (iter_idx, &idx_type->lst_idx_head) { + NMDedupMultiHeadEntry *head_entry; + + head_entry = c_list_entry (iter_idx, NMDedupMultiHeadEntry, lst_idx); + c_list_for_each (iter_entry, &head_entry->lst_entries_head) { + NMDedupMultiEntry *entry; + + entry = c_list_entry (iter_entry, NMDedupMultiEntry, lst_entries); + nm_dedup_multi_entry_set_dirty (entry, TRUE); + } + } +} + +/** + * nm_dedup_multi_index_dirty_remove_idx: + * @self: the index instance + * @idx_type: the index-type to select the objects. + * @mark_survivors_dirty: while the function removes all entries that are + * marked as dirty, if @set_dirty is true, the surviving objects + * will be marked dirty right away. + * + * Deletes all entries for @idx_type that are marked dirty. Only + * non-dirty objects survive. If @mark_survivors_dirty is set to TRUE, the survivors + * are marked as dirty right away. + * + * Returns: number of deleted entries. + */ +guint +nm_dedup_multi_index_dirty_remove_idx (NMDedupMultiIndex *self, + NMDedupMultiIdxType *idx_type, + gboolean mark_survivors_dirty) +{ + g_return_val_if_fail (self, 0); + g_return_val_if_fail (idx_type, 0); + + return _remove_idx_entry (self, idx_type, FALSE, mark_survivors_dirty); +} + +/*****************************************************************************/ + +static guint +_dict_idx_objs_hash (const NMDedupMultiObj *obj) +{ + NMHashState h; + + nm_hash_init (&h, 1748638583u); + obj->klass->obj_full_hash_update (obj, &h); + return nm_hash_complete (&h); +} + +static gboolean +_dict_idx_objs_equal (const NMDedupMultiObj *obj_a, + const NMDedupMultiObj *obj_b) +{ + return obj_a == obj_b + || ( obj_a->klass == obj_b->klass + && obj_a->klass->obj_full_equal (obj_a, obj_b)); +} + +void +nm_dedup_multi_index_obj_release (NMDedupMultiIndex *self, + /* const NMDedupMultiObj * */ gconstpointer obj) +{ + nm_assert (self); + nm_assert (obj); + nm_assert (g_hash_table_lookup (self->idx_objs, obj) == obj); + nm_assert (((const NMDedupMultiObj *) obj)->_multi_idx == self); + + ((NMDedupMultiObj *) obj)->_multi_idx = NULL; + if (!g_hash_table_remove (self->idx_objs, obj)) + nm_assert_not_reached (); +} + +gconstpointer +nm_dedup_multi_index_obj_find (NMDedupMultiIndex *self, + /* const NMDedupMultiObj * */ gconstpointer obj) +{ + g_return_val_if_fail (self, NULL); + g_return_val_if_fail (obj, NULL); + + return g_hash_table_lookup (self->idx_objs, obj); +} + +gconstpointer +nm_dedup_multi_index_obj_intern (NMDedupMultiIndex *self, + /* const NMDedupMultiObj * */ gconstpointer obj) +{ + const NMDedupMultiObj *obj_new = obj; + const NMDedupMultiObj *obj_old; + + nm_assert (self); + nm_assert (obj_new); + + if (obj_new->_multi_idx == self) { + nm_assert (g_hash_table_lookup (self->idx_objs, obj_new) == obj_new); + nm_dedup_multi_obj_ref (obj_new); + return obj_new; + } + + obj_old = g_hash_table_lookup (self->idx_objs, obj_new); + nm_assert (obj_old != obj_new); + + if (obj_old) { + nm_assert (obj_old->_multi_idx == self); + nm_dedup_multi_obj_ref (obj_old); + return obj_old; + } + + if (nm_dedup_multi_obj_needs_clone (obj_new)) + obj_new = nm_dedup_multi_obj_clone (obj_new); + else + obj_new = nm_dedup_multi_obj_ref (obj_new); + + nm_assert (obj_new); + nm_assert (!obj_new->_multi_idx); + + if (!g_hash_table_add (self->idx_objs, (gpointer) obj_new)) + nm_assert_not_reached (); + + ((NMDedupMultiObj *) obj_new)->_multi_idx = self; + return obj_new; +} + +void +nm_dedup_multi_obj_unref (const NMDedupMultiObj *obj) +{ + if (obj) { + nm_assert (obj->_ref_count > 0); + nm_assert (obj->_ref_count != NM_OBJ_REF_COUNT_STACKINIT); + +again: + if (--(((NMDedupMultiObj *) obj)->_ref_count) <= 0) { + if (obj->_multi_idx) { + /* restore the ref-count to 1 and release the object first + * from the index. Then, retry again to unref. */ + ((NMDedupMultiObj *) obj)->_ref_count++; + nm_dedup_multi_index_obj_release (obj->_multi_idx, obj); + nm_assert (obj->_ref_count == 1); + nm_assert (!obj->_multi_idx); + goto again; + } + + obj->klass->obj_destroy ((NMDedupMultiObj *) obj); + } + } +} + +gboolean +nm_dedup_multi_obj_needs_clone (const NMDedupMultiObj *obj) +{ + nm_assert (obj); + + if ( obj->_multi_idx + || obj->_ref_count == NM_OBJ_REF_COUNT_STACKINIT) + return TRUE; + + if ( obj->klass->obj_needs_clone + && obj->klass->obj_needs_clone (obj)) + return TRUE; + + return FALSE; +} + +const NMDedupMultiObj * +nm_dedup_multi_obj_clone (const NMDedupMultiObj *obj) +{ + const NMDedupMultiObj *o; + + nm_assert (obj); + + o = obj->klass->obj_clone (obj); + nm_assert (o); + nm_assert (o->_ref_count == 1); + return o; +} + +gconstpointer * +nm_dedup_multi_objs_to_array_head (const NMDedupMultiHeadEntry *head_entry, + NMDedupMultiFcnSelectPredicate predicate, + gpointer user_data, + guint *out_len) +{ + gconstpointer *result; + CList *iter; + guint i; + + if (!head_entry) { + NM_SET_OUT (out_len, 0); + return NULL; + } + + result = g_new (gconstpointer, head_entry->len + 1); + i = 0; + c_list_for_each (iter, &head_entry->lst_entries_head) { + const NMDedupMultiObj *obj = c_list_entry (iter, NMDedupMultiEntry, lst_entries)->obj; + + if ( !predicate + || predicate (obj, user_data)) { + nm_assert (i < head_entry->len); + result[i++] = obj; + } + } + + if (i == 0) { + g_free (result); + NM_SET_OUT (out_len, 0); + return NULL; + } + + nm_assert (i <= head_entry->len); + NM_SET_OUT (out_len, i); + result[i++] = NULL; + return result; +} + +GPtrArray * +nm_dedup_multi_objs_to_ptr_array_head (const NMDedupMultiHeadEntry *head_entry, + NMDedupMultiFcnSelectPredicate predicate, + gpointer user_data) +{ + GPtrArray *result; + CList *iter; + + if (!head_entry) + return NULL; + + result = g_ptr_array_new_full (head_entry->len, + (GDestroyNotify) nm_dedup_multi_obj_unref); + c_list_for_each (iter, &head_entry->lst_entries_head) { + const NMDedupMultiObj *obj = c_list_entry (iter, NMDedupMultiEntry, lst_entries)->obj; + + if ( !predicate + || predicate (obj, user_data)) + g_ptr_array_add (result, (gpointer) nm_dedup_multi_obj_ref (obj)); + } + + if (result->len == 0) { + g_ptr_array_unref (result); + return NULL; + } + return result; +} + +/** + * nm_dedup_multi_entry_reorder: + * @entry: the entry to reorder. It must not be NULL (and tracked in an index). + * @entry_order: (allow-none): an optional other entry. It MUST be in the same + * list as entry. If given, @entry will be ordered after/before @entry_order. + * If left at %NULL, @entry will be moved to the front/end of the list. + * @order_after: if @entry_order is given, %TRUE means to move @entry after + * @entry_order (otherwise before). + * If @entry_order is %NULL, %TRUE means to move @entry to the tail of the list + * (otherwise the beginning). Note that "tail of the list" here means that @entry + * will be linked before the head of the circular list. + * + * Returns: %TRUE, if anything was changed. Otherwise, @entry was already at the + * right place and nothing was done. + */ +gboolean +nm_dedup_multi_entry_reorder (const NMDedupMultiEntry *entry, + const NMDedupMultiEntry *entry_order, + gboolean order_after) +{ + nm_assert (entry); + + if (!entry_order) { + const NMDedupMultiHeadEntry *head_entry = entry->head; + + if (order_after) { + if (nm_c_list_move_tail ((CList *) &head_entry->lst_entries_head, (CList *) &entry->lst_entries)) + return TRUE; + } else { + if (nm_c_list_move_front ((CList *) &head_entry->lst_entries_head, (CList *) &entry->lst_entries)) + return TRUE; + } + } else { + if (order_after) { + if (nm_c_list_move_after ((CList *) &entry_order->lst_entries, (CList *) &entry->lst_entries)) + return TRUE; + } else { + if (nm_c_list_move_before ((CList *) &entry_order->lst_entries, (CList *) &entry->lst_entries)) + return TRUE; + } + } + + return FALSE; +} + +/*****************************************************************************/ + +NMDedupMultiIndex * +nm_dedup_multi_index_new (void) +{ + NMDedupMultiIndex *self; + + self = g_slice_new0 (NMDedupMultiIndex); + self->ref_count = 1; + self->idx_entries = g_hash_table_new ((GHashFunc) _dict_idx_entries_hash, (GEqualFunc) _dict_idx_entries_equal); + self->idx_objs = g_hash_table_new ((GHashFunc) _dict_idx_objs_hash, (GEqualFunc) _dict_idx_objs_equal); + return self; +} + +NMDedupMultiIndex * +nm_dedup_multi_index_ref (NMDedupMultiIndex *self) +{ + g_return_val_if_fail (self, NULL); + g_return_val_if_fail (self->ref_count > 0, NULL); + + self->ref_count++; + return self; +} + +NMDedupMultiIndex * +nm_dedup_multi_index_unref (NMDedupMultiIndex *self) +{ + GHashTableIter iter; + const NMDedupMultiIdxType *idx_type; + NMDedupMultiEntry *entry; + const NMDedupMultiObj *obj; + + g_return_val_if_fail (self, NULL); + g_return_val_if_fail (self->ref_count > 0, NULL); + + if (--self->ref_count > 0) + return NULL; + +more: + g_hash_table_iter_init (&iter, self->idx_entries); + while (g_hash_table_iter_next (&iter, (gpointer *) &entry, NULL)) { + if (entry->is_head) + idx_type = ((NMDedupMultiHeadEntry *) entry)->idx_type; + else + idx_type = entry->head->idx_type; + _remove_idx_entry (self, (NMDedupMultiIdxType *) idx_type, TRUE, FALSE); + goto more; + } + + nm_assert (g_hash_table_size (self->idx_entries) == 0); + + g_hash_table_iter_init (&iter, self->idx_objs); + while (g_hash_table_iter_next (&iter, (gpointer *) &obj, NULL)) { + nm_assert (obj->_multi_idx == self); + ((NMDedupMultiObj * )obj)->_multi_idx = NULL; + } + g_hash_table_remove_all (self->idx_objs); + + g_hash_table_unref (self->idx_entries); + g_hash_table_unref (self->idx_objs); + + g_slice_free (NMDedupMultiIndex, self); + return NULL; +} diff --git a/shared/nm-glib-aux/nm-dedup-multi.h b/shared/nm-glib-aux/nm-dedup-multi.h new file mode 100644 index 00000000..82c6f1e9 --- /dev/null +++ b/shared/nm-glib-aux/nm-dedup-multi.h @@ -0,0 +1,437 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* NetworkManager -- Network link manager + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA. + * + * (C) Copyright 2017 Red Hat, Inc. + */ + +#ifndef __NM_DEDUP_MULTI_H__ +#define __NM_DEDUP_MULTI_H__ + +#include "nm-obj.h" +#include "nm-std-aux/c-list-util.h" + +/*****************************************************************************/ + +struct _NMHashState; + +typedef struct _NMDedupMultiObj NMDedupMultiObj; +typedef struct _NMDedupMultiObjClass NMDedupMultiObjClass; +typedef struct _NMDedupMultiIdxType NMDedupMultiIdxType; +typedef struct _NMDedupMultiIdxTypeClass NMDedupMultiIdxTypeClass; +typedef struct _NMDedupMultiEntry NMDedupMultiEntry; +typedef struct _NMDedupMultiHeadEntry NMDedupMultiHeadEntry; +typedef struct _NMDedupMultiIndex NMDedupMultiIndex; + +typedef enum _NMDedupMultiIdxMode { + NM_DEDUP_MULTI_IDX_MODE_PREPEND, + + NM_DEDUP_MULTI_IDX_MODE_PREPEND_FORCE, + + /* append new objects to the end of the list. + * If the object is already in the cache, don't move it. */ + NM_DEDUP_MULTI_IDX_MODE_APPEND, + + /* like NM_DEDUP_MULTI_IDX_MODE_APPEND, but if the object + * is already in the cache, move it to the end. */ + NM_DEDUP_MULTI_IDX_MODE_APPEND_FORCE, +} NMDedupMultiIdxMode; + +/*****************************************************************************/ + +struct _NMDedupMultiObj { + union { + NMObjBaseInst parent; + const NMDedupMultiObjClass *klass; + }; + NMDedupMultiIndex *_multi_idx; + guint _ref_count; +}; + +struct _NMDedupMultiObjClass { + NMObjBaseClass parent; + + const NMDedupMultiObj *(*obj_clone) (const NMDedupMultiObj *obj); + + gboolean (*obj_needs_clone) (const NMDedupMultiObj *obj); + + void (*obj_destroy) (NMDedupMultiObj *obj); + + /* the NMDedupMultiObj can be deduplicated. For that the obj_full_hash_update() + * and obj_full_equal() compare *all* fields of the object, even minor ones. */ + void (*obj_full_hash_update) (const NMDedupMultiObj *obj, + struct _NMHashState *h); + gboolean (*obj_full_equal) (const NMDedupMultiObj *obj_a, + const NMDedupMultiObj *obj_b); +}; + +/*****************************************************************************/ + +static inline const NMDedupMultiObj * +nm_dedup_multi_obj_ref (const NMDedupMultiObj *obj) +{ + /* ref and unref accept const pointers. Objects is supposed to be shared + * and kept immutable. Disallowing to take/return a reference to a const + * NMPObject is cumbersome, because callers are precisely expected to + * keep a ref on the otherwise immutable object. */ + + nm_assert (obj); + nm_assert (obj->_ref_count != NM_OBJ_REF_COUNT_STACKINIT); + nm_assert (obj->_ref_count > 0); + + ((NMDedupMultiObj *) obj)->_ref_count++; + return obj; +} + +void nm_dedup_multi_obj_unref (const NMDedupMultiObj *obj); +const NMDedupMultiObj *nm_dedup_multi_obj_clone (const NMDedupMultiObj *obj); +gboolean nm_dedup_multi_obj_needs_clone (const NMDedupMultiObj *obj); + +gconstpointer nm_dedup_multi_index_obj_intern (NMDedupMultiIndex *self, + /* const NMDedupMultiObj * */ gconstpointer obj); + +void nm_dedup_multi_index_obj_release (NMDedupMultiIndex *self, + /* const NMDedupMultiObj * */ gconstpointer obj); + +/* const NMDedupMultiObj * */ gconstpointer nm_dedup_multi_index_obj_find (NMDedupMultiIndex *self, + /* const NMDedupMultiObj * */ gconstpointer obj); + +/*****************************************************************************/ + +/* the NMDedupMultiIdxType is an access handle under which you can store and + * retrieve NMDedupMultiObj instances in NMDedupMultiIndex. + * + * The NMDedupMultiIdxTypeClass determines its behavior, but you can have + * multiple instances (of the same class). + * + * For example, NMIP4Config can have idx-type to put there all IPv4 Routes. + * This idx-type instance is private to the NMIP4Config instance. Basically, + * the NMIP4Config instance uses the idx-type to maintain an ordered list + * of routes in NMDedupMultiIndex. + * + * However, a NMDedupMultiIdxType may also partition the set of objects + * in multiple distinct lists. NMIP4Config doesn't do that (because instead + * of creating one idx-type for IPv4 and IPv6 routes, it just cretaes + * to distinct idx-types, one for each address family. + * This partitioning is used by NMPlatform to maintain a lookup index for + * routes by ifindex. As the ifindex is dynamic, it does not create an + * idx-type instance for each ifindex. Instead, it has one idx-type for + * all routes. But whenever accessing NMDedupMultiIndex with an NMDedupMultiObj, + * the partitioning NMDedupMultiIdxType takes into account the NMDedupMultiObj + * instance to associate it with the right list. + * + * Hence, a NMDedupMultiIdxEntry has a list of possibly multiple NMDedupMultiHeadEntry + * instances, which each is the head for a list of NMDedupMultiEntry instances. + * In the platform example, the NMDedupMultiHeadEntry partition the indexed objects + * by their ifindex. */ +struct _NMDedupMultiIdxType { + union { + NMObjBaseInst parent; + const NMDedupMultiIdxTypeClass *klass; + }; + + CList lst_idx_head; + + guint len; +}; + +void nm_dedup_multi_idx_type_init (NMDedupMultiIdxType *idx_type, + const NMDedupMultiIdxTypeClass *klass); + +struct _NMDedupMultiIdxTypeClass { + NMObjBaseClass parent; + + void (*idx_obj_id_hash_update) (const NMDedupMultiIdxType *idx_type, + const NMDedupMultiObj *obj, + struct _NMHashState *h); + gboolean (*idx_obj_id_equal) (const NMDedupMultiIdxType *idx_type, + const NMDedupMultiObj *obj_a, + const NMDedupMultiObj *obj_b); + + /* an NMDedupMultiIdxTypeClass which implements partitioning of the + * tracked objects, must implement the idx_obj_partition*() functions. + * + * idx_obj_partitionable() may return NULL if the object cannot be tracked. + * For example, a index for routes by ifindex, may not want to track any + * routes that don't have a valid ifindex. If the idx-type says that the + * object is not partitionable, it is never added to the NMDedupMultiIndex. */ + gboolean (*idx_obj_partitionable) (const NMDedupMultiIdxType *idx_type, + const NMDedupMultiObj *obj); + void (*idx_obj_partition_hash_update) (const NMDedupMultiIdxType *idx_type, + const NMDedupMultiObj *obj, + struct _NMHashState *h); + gboolean (*idx_obj_partition_equal) (const NMDedupMultiIdxType *idx_type, + const NMDedupMultiObj *obj_a, + const NMDedupMultiObj *obj_b); +}; + +static inline gboolean +nm_dedup_multi_idx_type_id_equal (const NMDedupMultiIdxType *idx_type, + /* const NMDedupMultiObj * */ gconstpointer obj_a, + /* const NMDedupMultiObj * */ gconstpointer obj_b) +{ + nm_assert (idx_type); + return obj_a == obj_b + || idx_type->klass->idx_obj_id_equal (idx_type, + obj_a, + obj_b); +} + +static inline gboolean +nm_dedup_multi_idx_type_partition_equal (const NMDedupMultiIdxType *idx_type, + /* const NMDedupMultiObj * */ gconstpointer obj_a, + /* const NMDedupMultiObj * */ gconstpointer obj_b) +{ + nm_assert (idx_type); + if (idx_type->klass->idx_obj_partition_equal) { + nm_assert (obj_a); + nm_assert (obj_b); + return obj_a == obj_b + || idx_type->klass->idx_obj_partition_equal (idx_type, + obj_a, + obj_b); + } + return TRUE; +} + +/*****************************************************************************/ + +struct _NMDedupMultiEntry { + + /* this is the list of all entries that share the same head entry. + * All entries compare equal according to idx_obj_partition_equal(). */ + CList lst_entries; + + /* const NMDedupMultiObj * */ gconstpointer obj; + + bool is_head; + bool dirty; + + const NMDedupMultiHeadEntry *head; +}; + +struct _NMDedupMultiHeadEntry { + + /* this is the list of all entries that share the same head entry. + * All entries compare equal according to idx_obj_partition_equal(). */ + CList lst_entries_head; + + const NMDedupMultiIdxType *idx_type; + + bool is_head; + + guint len; + + CList lst_idx; +}; + +/*****************************************************************************/ + +static inline gconstpointer +nm_dedup_multi_entry_get_obj (const NMDedupMultiEntry *entry) +{ + /* convenience method that allows to skip the %NULL check on + * @entry. Think of the NULL-conditional operator ?. of C# */ + return entry ? entry->obj : NULL; +} + +/*****************************************************************************/ + +static inline void +nm_dedup_multi_entry_set_dirty (const NMDedupMultiEntry *entry, + gboolean dirty) +{ + /* NMDedupMultiEntry is always exposed as a const object, because it is not + * supposed to be modified outside NMDedupMultiIndex API. Except the "dirty" + * flag. In C++ speak, it is a mutable field. + * + * Add this inline function, to cast-away constness and set the dirty flag. */ + nm_assert (entry); + ((NMDedupMultiEntry *) entry)->dirty = dirty; +} + +/*****************************************************************************/ + +NMDedupMultiIndex *nm_dedup_multi_index_new (void); +NMDedupMultiIndex *nm_dedup_multi_index_ref (NMDedupMultiIndex *self); +NMDedupMultiIndex *nm_dedup_multi_index_unref (NMDedupMultiIndex *self); + +static inline void +_nm_auto_unref_dedup_multi_index (NMDedupMultiIndex **v) +{ + if (*v) + nm_dedup_multi_index_unref (*v); +} +#define nm_auto_unref_dedup_multi_index nm_auto(_nm_auto_unref_dedup_multi_index) + +#define NM_DEDUP_MULTI_ENTRY_MISSING ((const NMDedupMultiEntry *) GUINT_TO_POINTER (1)) +#define NM_DEDUP_MULTI_HEAD_ENTRY_MISSING ((const NMDedupMultiHeadEntry *) GUINT_TO_POINTER (1)) + +gboolean nm_dedup_multi_index_add_full (NMDedupMultiIndex *self, + NMDedupMultiIdxType *idx_type, + /*const NMDedupMultiObj * */ gconstpointer obj, + NMDedupMultiIdxMode mode, + const NMDedupMultiEntry *entry_order, + const NMDedupMultiEntry *entry_existing, + const NMDedupMultiHeadEntry *head_existing, + const NMDedupMultiEntry **out_entry, + /* const NMDedupMultiObj ** */ gpointer out_obj_old); + +gboolean nm_dedup_multi_index_add (NMDedupMultiIndex *self, + NMDedupMultiIdxType *idx_type, + /*const NMDedupMultiObj * */ gconstpointer obj, + NMDedupMultiIdxMode mode, + const NMDedupMultiEntry **out_entry, + /* const NMDedupMultiObj ** */ gpointer out_obj_old); + +const NMDedupMultiEntry *nm_dedup_multi_index_lookup_obj (const NMDedupMultiIndex *self, + const NMDedupMultiIdxType *idx_type, + /*const NMDedupMultiObj * */ gconstpointer obj); + +const NMDedupMultiHeadEntry *nm_dedup_multi_index_lookup_head (const NMDedupMultiIndex *self, + const NMDedupMultiIdxType *idx_type, + /*const NMDedupMultiObj * */ gconstpointer obj); + +guint nm_dedup_multi_index_remove_entry (NMDedupMultiIndex *self, + gconstpointer entry); + +guint nm_dedup_multi_index_remove_obj (NMDedupMultiIndex *self, + NMDedupMultiIdxType *idx_type, + /*const NMDedupMultiObj * */ gconstpointer obj, + /*const NMDedupMultiObj ** */ gconstpointer *out_obj); + +guint nm_dedup_multi_index_remove_head (NMDedupMultiIndex *self, + NMDedupMultiIdxType *idx_type, + /*const NMDedupMultiObj * */ gconstpointer obj); + +guint nm_dedup_multi_index_remove_idx (NMDedupMultiIndex *self, + NMDedupMultiIdxType *idx_type); + +void nm_dedup_multi_index_dirty_set_head (NMDedupMultiIndex *self, + const NMDedupMultiIdxType *idx_type, + /*const NMDedupMultiObj * */ gconstpointer obj); + +void nm_dedup_multi_index_dirty_set_idx (NMDedupMultiIndex *self, + const NMDedupMultiIdxType *idx_type); + +guint nm_dedup_multi_index_dirty_remove_idx (NMDedupMultiIndex *self, + NMDedupMultiIdxType *idx_type, + gboolean mark_survivors_dirty); + +/*****************************************************************************/ + +typedef struct _NMDedupMultiIter { + const CList *_head; + const CList *_next; + const NMDedupMultiEntry *current; +} NMDedupMultiIter; + +static inline void +nm_dedup_multi_iter_init (NMDedupMultiIter *iter, const NMDedupMultiHeadEntry *head) +{ + g_return_if_fail (iter); + + if (head && !c_list_is_empty (&head->lst_entries_head)) { + iter->_head = &head->lst_entries_head; + iter->_next = head->lst_entries_head.next; + } else { + iter->_head = NULL; + iter->_next = NULL; + } + iter->current = NULL; +} + +static inline gboolean +nm_dedup_multi_iter_next (NMDedupMultiIter *iter) +{ + g_return_val_if_fail (iter, FALSE); + + if (!iter->_next) + return FALSE; + + /* we always look ahead for the next. This way, the user + * may delete the current entry (but no other entries). */ + iter->current = c_list_entry (iter->_next, NMDedupMultiEntry, lst_entries); + if (iter->_next->next == iter->_head) + iter->_next = NULL; + else + iter->_next = iter->_next->next; + return TRUE; +} + +#define nm_dedup_multi_iter_for_each(iter, head_entry) \ + for (nm_dedup_multi_iter_init ((iter), (head_entry)); \ + nm_dedup_multi_iter_next ((iter)); \ + ) + +/*****************************************************************************/ + +typedef gboolean (*NMDedupMultiFcnSelectPredicate) (/* const NMDedupMultiObj * */ gconstpointer obj, + gpointer user_data); + +gconstpointer *nm_dedup_multi_objs_to_array_head (const NMDedupMultiHeadEntry *head_entry, + NMDedupMultiFcnSelectPredicate predicate, + gpointer user_data, + guint *out_len); +GPtrArray *nm_dedup_multi_objs_to_ptr_array_head (const NMDedupMultiHeadEntry *head_entry, + NMDedupMultiFcnSelectPredicate predicate, + gpointer user_data); + +static inline const NMDedupMultiEntry * +nm_dedup_multi_head_entry_get_idx (const NMDedupMultiHeadEntry *head_entry, + int idx) +{ + CList *iter; + + if (head_entry) { + if (idx >= 0) { + c_list_for_each (iter, &head_entry->lst_entries_head) { + if (idx-- == 0) + return c_list_entry (iter, NMDedupMultiEntry, lst_entries); + } + } else { + for (iter = head_entry->lst_entries_head.prev; + iter != &head_entry->lst_entries_head; + iter = iter->prev) { + if (++idx == 0) + return c_list_entry (iter, NMDedupMultiEntry, lst_entries); + } + } + } + return NULL; +} + +static inline void +nm_dedup_multi_head_entry_sort (const NMDedupMultiHeadEntry *head_entry, + CListSortCmp cmp, + gconstpointer user_data) +{ + if (head_entry) { + /* the head entry can be sorted directly without messing up the + * index to which it belongs. Of course, this does mess up any + * NMDedupMultiIter instances. */ + c_list_sort ((CList *) &head_entry->lst_entries_head, cmp, user_data); + } +} + +gboolean nm_dedup_multi_entry_reorder (const NMDedupMultiEntry *entry, + const NMDedupMultiEntry *entry_order, + gboolean order_after); + +/*****************************************************************************/ + +#endif /* __NM_DEDUP_MULTI_H__ */ diff --git a/shared/nm-glib-aux/nm-enum-utils.c b/shared/nm-glib-aux/nm-enum-utils.c new file mode 100644 index 00000000..a4f6e809 --- /dev/null +++ b/shared/nm-glib-aux/nm-enum-utils.c @@ -0,0 +1,372 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* NetworkManager -- Network link manager + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA. + * + * (C) Copyright 2017 Red Hat, Inc. + */ + +#include "nm-default.h" + +#include "nm-enum-utils.h" + +/*****************************************************************************/ + +#define IS_FLAGS_SEPARATOR(ch) (NM_IN_SET ((ch), ' ', '\t', ',', '\n', '\r')) + +static void +_ASSERT_enum_values_info (GType type, + const NMUtilsEnumValueInfo *value_infos) +{ +#if NM_MORE_ASSERTS > 5 + nm_auto_unref_gtypeclass GTypeClass *klass = NULL; + gs_unref_hashtable GHashTable *ht = NULL; + + klass = g_type_class_ref (type); + + g_assert (G_IS_ENUM_CLASS (klass) || G_IS_FLAGS_CLASS (klass)); + + if (!value_infos) + return; + + ht = g_hash_table_new (g_str_hash, g_str_equal); + + for (; value_infos->nick; value_infos++) { + + g_assert (value_infos->nick[0]); + + /* duplicate nicks make no sense!! */ + g_assert (!g_hash_table_contains (ht, value_infos->nick)); + g_hash_table_add (ht, (gpointer) value_infos->nick); + + if (G_IS_ENUM_CLASS (klass)) { + GEnumValue *enum_value; + + enum_value = g_enum_get_value_by_nick (G_ENUM_CLASS (klass), value_infos->nick); + if (enum_value) { + /* we do allow specifying the same name via @value_infos and @type. + * That might make sense, if @type comes from a library where older versions + * of the library don't yet support the value. In this case, the caller can + * provide the nick via @value_infos, to support the older library version. + * And then, when actually running against a newer library version where + * @type knows the nick, we have this situation. + * + * Another reason for specifying a nick both in @value_infos and @type, + * is to specify an alias which is not used with highest preference. For + * example, if you add an alias "disabled" for "none" (both numerically + * equal), then the first alias in @value_infos will be preferred over + * the name from @type. So, to still use "none" as preferred name, you may + * explicitly specify the "none" alias in @value_infos before "disabled". + * + * However, what never is allowed, is to use a name (nick) to re-number + * the value. That is, if both @value_infos and @type contain a particular + * nick, their numeric values must agree as well. + * Allowing this, would be very confusing, because the name would have a different + * value from the regular GLib GEnum API. + */ + g_assert (enum_value->value == value_infos->value); + } + } else { + GFlagsValue *flags_value; + + flags_value = g_flags_get_value_by_nick (G_FLAGS_CLASS (klass), value_infos->nick); + if (flags_value) { + /* see ENUM case above. */ + g_assert (flags_value->value == (guint) value_infos->value); + } + } + } +#endif +} + +static gboolean +_is_hex_string (const char *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) +{ + 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); +} + +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); +} + +char * +_nm_utils_enum_to_str_full (GType type, + int value, + const char *flags_separator, + const NMUtilsEnumValueInfo *value_infos) +{ + nm_auto_unref_gtypeclass GTypeClass *klass = NULL; + + _ASSERT_enum_values_info (type, value_infos); + + if ( flags_separator + && ( !flags_separator[0] + || NM_STRCHAR_ANY (flags_separator, ch, !IS_FLAGS_SEPARATOR (ch)))) + g_return_val_if_reached (NULL); + + klass = g_type_class_ref (type); + + if (G_IS_ENUM_CLASS (klass)) { + GEnumValue *enum_value; + + for ( ; value_infos && value_infos->nick; value_infos++) { + if (value_infos->value == value) + return g_strdup (value_infos->nick); + } + + enum_value = g_enum_get_value (G_ENUM_CLASS (klass), value); + if ( !enum_value + || !_enum_is_valid_enum_nick (enum_value->value_nick)) + return g_strdup_printf ("%d", value); + else + return g_strdup (enum_value->value_nick); + } else if (G_IS_FLAGS_CLASS (klass)) { + GFlagsValue *flags_value; + GString *str = g_string_new (""); + unsigned uvalue = (unsigned) value; + + flags_separator = flags_separator ?: " "; + + for ( ; value_infos && value_infos->nick; value_infos++) { + + nm_assert (_enum_is_valid_flags_nick (value_infos->nick)); + + if (uvalue == 0) { + if (value_infos->value != 0) + continue; + } else { + if (!NM_FLAGS_ALL (uvalue, (unsigned) value_infos->value)) + continue; + } + + if (str->len) + g_string_append (str, flags_separator); + g_string_append (str, value_infos->nick); + uvalue &= ~((unsigned) value_infos->value); + if (uvalue == 0) { + /* we printed all flags. Done. */ + goto flags_done; + } + } + + do { + flags_value = g_flags_get_first_value (G_FLAGS_CLASS (klass), uvalue); + if (str->len) + g_string_append (str, flags_separator); + if ( !flags_value + || !_enum_is_valid_flags_nick (flags_value->value_nick)) { + if (uvalue) + g_string_append_printf (str, "0x%x", uvalue); + break; + } + g_string_append (str, flags_value->value_nick); + uvalue &= ~flags_value->value; + } while (uvalue); + +flags_done: + return g_string_free (str, FALSE); + } + + g_return_val_if_reached (NULL); +} + +static const NMUtilsEnumValueInfo * +_find_value_info (const NMUtilsEnumValueInfo *value_infos, const char *needle) +{ + if (value_infos) { + for (; value_infos->nick; value_infos++) { + if (nm_streq (needle, value_infos->nick)) + return value_infos; + } + } + return NULL; +} + +gboolean +_nm_utils_enum_from_str_full (GType type, + const char *str, + int *out_value, + char **err_token, + const NMUtilsEnumValueInfo *value_infos) +{ + GTypeClass *klass; + gboolean ret = FALSE; + int value = 0; + gs_free char *str_clone = NULL; + char *s; + gint64 v64; + const NMUtilsEnumValueInfo *nick; + + g_return_val_if_fail (str, FALSE); + + _ASSERT_enum_values_info (type, value_infos); + + str_clone = strdup (str); + s = nm_str_skip_leading_spaces (str_clone); + g_strchomp (s); + + klass = g_type_class_ref (type); + + if (G_IS_ENUM_CLASS (klass)) { + GEnumValue *enum_value; + + 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; + } + } else if (_is_dec_string (s)) { + v64 = _nm_utils_ascii_str_to_int64 (s, 10, 0, G_MAXUINT, -1); + if (v64 != -1) { + value = (int) v64; + ret = TRUE; + } + } else if ((nick = _find_value_info (value_infos, s))) { + value = nick->value; + ret = TRUE; + } else if ((enum_value = g_enum_get_value_by_nick (G_ENUM_CLASS (klass), s))) { + value = enum_value->value; + ret = TRUE; + } + } + } else if (G_IS_FLAGS_CLASS (klass)) { + GFlagsValue *flags_value; + unsigned uvalue = 0; + + ret = TRUE; + while (s[0]) { + char *s_end; + + for (s_end = s; s_end[0]; s_end++) { + if (IS_FLAGS_SEPARATOR (s_end[0])) { + s_end[0] = '\0'; + s_end++; + break; + } + } + + if (s[0]) { + if (_is_hex_string (s)) { + 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)) { + v64 = _nm_utils_ascii_str_to_int64 (s, 10, 0, G_MAXUINT, -1); + if (v64 == -1) { + ret = FALSE; + break; + } + uvalue |= (unsigned) v64; + } else if ((nick = _find_value_info (value_infos, s))) + uvalue |= (unsigned) nick->value; + else if ((flags_value = g_flags_get_value_by_nick (G_FLAGS_CLASS (klass), s))) + uvalue |= flags_value->value; + else { + ret = FALSE; + break; + } + } + + s = s_end; + } + + value = (int) uvalue; + } else + g_return_val_if_reached (FALSE); + + NM_SET_OUT (err_token, !ret && s[0] ? g_strdup (s) : NULL); + NM_SET_OUT (out_value, ret ? value : 0); + g_type_class_unref (klass); + return ret; +} + +const char ** +_nm_utils_enum_get_values (GType type, int from, int to) +{ + GTypeClass *klass; + GPtrArray *array; + int i; + char sbuf[64]; + + klass = g_type_class_ref (type); + array = g_ptr_array_new (); + + if (G_IS_ENUM_CLASS (klass)) { + GEnumClass *enum_class = G_ENUM_CLASS (klass); + GEnumValue *enum_value; + + for (i = 0; i < enum_class->n_values; i++) { + enum_value = &enum_class->values[i]; + if (enum_value->value >= from && enum_value->value <= to) { + if (_enum_is_valid_enum_nick (enum_value->value_nick)) + g_ptr_array_add (array, (gpointer) enum_value->value_nick); + else + g_ptr_array_add (array, (gpointer) g_intern_string (nm_sprintf_buf (sbuf, "%d", enum_value->value))); + } + } + } else if (G_IS_FLAGS_CLASS (klass)) { + GFlagsClass *flags_class = G_FLAGS_CLASS (klass); + GFlagsValue *flags_value; + + for (i = 0; i < flags_class->n_values; i++) { + flags_value = &flags_class->values[i]; + if (flags_value->value >= (guint) from && flags_value->value <= (guint) to) { + if (_enum_is_valid_flags_nick (flags_value->value_nick)) + g_ptr_array_add (array, (gpointer) flags_value->value_nick); + else + g_ptr_array_add (array, (gpointer) g_intern_string (nm_sprintf_buf (sbuf, "0x%x", (unsigned) flags_value->value))); + } + } + } else { + g_type_class_unref (klass); + g_ptr_array_free (array, TRUE); + g_return_val_if_reached (NULL); + } + + g_type_class_unref (klass); + g_ptr_array_add (array, NULL); + + return (const char **) g_ptr_array_free (array, FALSE); +} diff --git a/shared/nm-glib-aux/nm-enum-utils.h b/shared/nm-glib-aux/nm-enum-utils.h new file mode 100644 index 00000000..1827fdf4 --- /dev/null +++ b/shared/nm-glib-aux/nm-enum-utils.h @@ -0,0 +1,48 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* NetworkManager -- Network link manager + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA. + * + * (C) Copyright 2017 Red Hat, Inc. + */ + +#ifndef __NM_ENUM_UTILS_H__ +#define __NM_ENUM_UTILS_H__ + +/*****************************************************************************/ + +typedef struct _NMUtilsEnumValueInfo { + /* currently, this is only used for _nm_utils_enum_from_str_full() to + * declare additional aliases for values. */ + const char *nick; + int value; +} NMUtilsEnumValueInfo; + +char *_nm_utils_enum_to_str_full (GType type, + int value, + const char *sep, + const NMUtilsEnumValueInfo *value_infos); +gboolean _nm_utils_enum_from_str_full (GType type, + const char *str, + int *out_value, + char **err_token, + const NMUtilsEnumValueInfo *value_infos); + +const char **_nm_utils_enum_get_values (GType type, int from, int to); + +/*****************************************************************************/ + +#endif /* __NM_ENUM_UTILS_H__ */ diff --git a/shared/nm-glib-aux/nm-errno.c b/shared/nm-glib-aux/nm-errno.c new file mode 100644 index 00000000..30eb9a8e --- /dev/null +++ b/shared/nm-glib-aux/nm-errno.c @@ -0,0 +1,198 @@ +/* NetworkManager -- Network link manager + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA. + * + * Copyright 2018 Red Hat, Inc. + */ + +#include "nm-default.h" + +#include "nm-errno.h" + +#include + +/*****************************************************************************/ + +NM_UTILS_LOOKUP_STR_DEFINE_STATIC (_geterror, +#if 0 + enum _NMErrno, +#else + int, +#endif + NM_UTILS_LOOKUP_DEFAULT (NULL), + + NM_UTILS_LOOKUP_STR_ITEM (NME_ERRNO_SUCCESS, "NME_ERRNO_SUCCESS"), + NM_UTILS_LOOKUP_STR_ITEM (NME_ERRNO_OUT_OF_RANGE, "NME_ERRNO_OUT_OF_RANGE"), + + NM_UTILS_LOOKUP_STR_ITEM (NME_UNSPEC, "NME_UNSPEC"), + NM_UTILS_LOOKUP_STR_ITEM (NME_BUG, "NME_BUG"), + NM_UTILS_LOOKUP_STR_ITEM (NME_NATIVE_ERRNO, "NME_NATIVE_ERRNO"), + + NM_UTILS_LOOKUP_STR_ITEM (NME_NL_ATTRSIZE, "NME_NL_ATTRSIZE"), + NM_UTILS_LOOKUP_STR_ITEM (NME_NL_BAD_SOCK, "NME_NL_BAD_SOCK"), + NM_UTILS_LOOKUP_STR_ITEM (NME_NL_DUMP_INTR, "NME_NL_DUMP_INTR"), + NM_UTILS_LOOKUP_STR_ITEM (NME_NL_MSG_OVERFLOW, "NME_NL_MSG_OVERFLOW"), + NM_UTILS_LOOKUP_STR_ITEM (NME_NL_MSG_TOOSHORT, "NME_NL_MSG_TOOSHORT"), + NM_UTILS_LOOKUP_STR_ITEM (NME_NL_MSG_TRUNC, "NME_NL_MSG_TRUNC"), + NM_UTILS_LOOKUP_STR_ITEM (NME_NL_SEQ_MISMATCH, "NME_NL_SEQ_MISMATCH"), + NM_UTILS_LOOKUP_STR_ITEM (NME_NL_NOADDR, "NME_NL_NOADDR"), + + NM_UTILS_LOOKUP_STR_ITEM (NME_PL_NOT_FOUND, "not-found"), + NM_UTILS_LOOKUP_STR_ITEM (NME_PL_EXISTS, "exists"), + NM_UTILS_LOOKUP_STR_ITEM (NME_PL_WRONG_TYPE, "wrong-type"), + NM_UTILS_LOOKUP_STR_ITEM (NME_PL_NOT_SLAVE, "not-slave"), + NM_UTILS_LOOKUP_STR_ITEM (NME_PL_NO_FIRMWARE, "no-firmware"), + NM_UTILS_LOOKUP_STR_ITEM (NME_PL_OPNOTSUPP, "not-supported"), + NM_UTILS_LOOKUP_STR_ITEM (NME_PL_NETLINK, "netlink"), + NM_UTILS_LOOKUP_STR_ITEM (NME_PL_CANT_SET_MTU, "cant-set-mtu"), + + NM_UTILS_LOOKUP_ITEM_IGNORE (_NM_ERRNO_MININT), + NM_UTILS_LOOKUP_ITEM_IGNORE (_NM_ERRNO_RESERVED_LAST_PLUS_1), +); + +/** + * nm_strerror(): + * @nmerr: the NetworkManager specific errno to be converted + * to string. + * + * NetworkManager specific error numbers reserve a range in "errno.h" with + * our own defines. For numbers that don't fall into this range, the numbers + * are identical to the common error numbers. + * + * Idential to strerror(), g_strerror(), nm_strerror_native() for error numbers + * that are not in the reserved range of NetworkManager specific errors. + * + * Returns: (transfer none): the string representation of the error number. + */ +const char * +nm_strerror (int nmerr) +{ + const char *s; + + nmerr = nm_errno (nmerr); + + if (nmerr >= _NM_ERRNO_RESERVED_FIRST) { + s = _geterror (nmerr); + if (s) + return s; + } + return nm_strerror_native (nmerr); +} + +/*****************************************************************************/ + +/** + * nm_strerror_native_r: + * @errsv: the errno to convert to string. + * @buf: the output buffer where to write the string to. + * @buf_size: the length of buffer. + * + * This is like strerror_r(), with one difference: depending on the + * locale, the returned string is guaranteed to be valid UTF-8. + * Also, there is some confusion as to whether to use glibc's + * strerror_r() or the POXIX/XSI variant. This is abstracted + * by the function. + * + * Note that the returned buffer may also be a statically allocated + * buffer, and not the input buffer @buf. Consequently, the returned + * string may be longer than @buf_size. + * + * Returns: (transfer none): a NUL terminated error message. This is either a static + * string (that is never freed), or the provided @buf argumnt. + */ +const char * +nm_strerror_native_r (int errsv, char *buf, gsize buf_size) +{ + char *buf2; + + nm_assert (buf); + nm_assert (buf_size > 0); + +#if (_POSIX_C_SOURCE >= 200112L) && ! _GNU_SOURCE + /* XSI-compliant */ + { + int errno_saved = errno; + + if (strerror_r (errsv, buf, buf_size) != 0) { + g_snprintf (buf, buf_size, "Unspecified errno %d", errsv); + errno = errno_saved; + } + buf2 = buf; + } +#else + /* GNU-specific */ + buf2 = strerror_r (errsv, buf, buf_size); +#endif + + /* like g_strerror(), ensure that the error message is UTF-8. */ + if ( !g_get_charset (NULL) + && !g_utf8_validate (buf2, -1, NULL)) { + gs_free char *msg = NULL; + + msg = g_locale_to_utf8 (buf2, -1, NULL, NULL, NULL); + if (msg) { + g_strlcpy (buf, msg, buf_size); + buf2 = buf; + } + } + + return buf2; +} + +/** + * nm_strerror_native: + * @errsv: the errno integer from + * + * Like strerror(), but strerror() is not thread-safe and not guaranteed + * to be UTF-8. + * + * g_strerror() is a thread-safe variant of strerror(), however it caches + * all returned strings in a dictionary. That means, using this on untrusted + * error numbers can result in this cache to grow without limits. + * + * Instead, return a tread-local buffer. This way, it's thread-safe. + * + * There is a downside to this: subsequent calls of nm_strerror_native() + * overwrite the error message. + * + * Returns: (transfer none): the text representation of the error number. + */ +const char * +nm_strerror_native (int errsv) +{ + static _nm_thread_local char *buf_static = NULL; + char *buf; + + buf = buf_static; + if (G_UNLIKELY (!buf)) { + int errno_saved = errno; + pthread_key_t key; + + buf = g_malloc (NM_STRERROR_BUFSIZE); + buf_static = buf; + + if ( pthread_key_create (&key, g_free) != 0 + || pthread_setspecific (key, buf) != 0) { + /* Failure. We will leak the buffer when the thread exits. + * + * Nothing we can do about it really. For Debug builds we fail with an assertion. */ + nm_assert_not_reached (); + } + errno = errno_saved; + } + + return nm_strerror_native_r (errsv, buf, NM_STRERROR_BUFSIZE); +} diff --git a/shared/nm-glib-aux/nm-errno.h b/shared/nm-glib-aux/nm-errno.h new file mode 100644 index 00000000..d77735a7 --- /dev/null +++ b/shared/nm-glib-aux/nm-errno.h @@ -0,0 +1,185 @@ +/* NetworkManager -- Network link manager + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA. + * + * Copyright 2018 Red Hat, Inc. + */ + +#ifndef __NM_ERRNO_H__ +#define __NM_ERRNO_H__ + +#include + +/*****************************************************************************/ + +enum _NMErrno { + _NM_ERRNO_MININT = G_MININT, + _NM_ERRNO_MAXINT = G_MAXINT, + _NM_ERRNO_RESERVED_FIRST = 100000, + + + /* when we cannot represent a number as positive number, we resort to this + * number. Basically, the values G_MININT, -NME_ERRNO_SUCCESS, NME_ERRNO_SUCCESS + * and G_MAXINT all map to the same value. */ + NME_ERRNO_OUT_OF_RANGE = G_MAXINT, + + /* Indicate that the original errno was zero. Zero denotes *no error*, but we know something + * went wrong and we want to report some error. This is a placeholder to mean, something + * was wrong, but errno was zero. */ + NME_ERRNO_SUCCESS = G_MAXINT - 1, + + + /* an unspecified error. */ + NME_UNSPEC = _NM_ERRNO_RESERVED_FIRST, + + /* A bug, for example when an assertion failed. + * Should never happen. */ + NME_BUG, + + /* a native error number (from ) cannot be mapped as + * an nm-error, because it is in the range [_NM_ERRNO_RESERVED_FIRST, + * _NM_ERRNO_RESERVED_LAST]. */ + NME_NATIVE_ERRNO, + + /* netlink errors. */ + NME_NL_SEQ_MISMATCH, + NME_NL_MSG_TRUNC, + NME_NL_MSG_TOOSHORT, + NME_NL_DUMP_INTR, + NME_NL_ATTRSIZE, + NME_NL_BAD_SOCK, + NME_NL_NOADDR, + NME_NL_MSG_OVERFLOW, + + /* platform errors. */ + NME_PL_NOT_FOUND, + NME_PL_EXISTS, + NME_PL_WRONG_TYPE, + NME_PL_NOT_SLAVE, + NME_PL_NO_FIRMWARE, + NME_PL_OPNOTSUPP, + NME_PL_NETLINK, + NME_PL_CANT_SET_MTU, + + _NM_ERRNO_RESERVED_LAST_PLUS_1, + _NM_ERRNO_RESERVED_LAST = _NM_ERRNO_RESERVED_LAST_PLUS_1 - 1, +}; + +/*****************************************************************************/ + +/* When we receive an errno from a system function, we can safely assume + * that the error number is not negative. We rely on that, and possibly just + * "return -errsv;" to signal an error. We also rely on that, because libc + * is our trusted base: meaning, if it cannot even succeed at setting errno + * according to specification, all bets are off. + * + * This macro returns the input argument, and asserts that the error variable + * is positive. + * + * In a sense, the macro is related to nm_errno_native() function, but the difference + * is that this macro asserts that @errsv is positive, while nm_errno_native() coerces + * negative values to be non-negative. */ +#define NM_ERRNO_NATIVE(errsv) \ + ({ \ + const int _errsv_x = (errsv); \ + \ + nm_assert (_errsv_x > 0); \ + _errsv_x; \ + }) + +/* Normalize native errno. + * + * Our API may return native error codes () as negative values. This function + * takes such an errno, and normalizes it to their positive value. + * + * The special values G_MININT and zero are coerced to NME_ERRNO_OUT_OF_RANGE and NME_ERRNO_SUCCESS + * respectively. + * Other values are coerced to their inverse. + * Other positive values are returned unchanged. + * + * Basically, this normalizes errsv to be positive (taking care of two pathological cases). + */ +static inline int +nm_errno_native (int errsv) +{ + switch (errsv) { + case 0: return NME_ERRNO_SUCCESS; + case G_MININT: return NME_ERRNO_OUT_OF_RANGE; + default: + return errsv >= 0 ? errsv : -errsv; + } +} + +/* Normalizes an nm-error to be positive. + * + * Various API returns negative error codes, and this function converts the negative + * value to its positive. + * + * Note that @nmerr is on the domain of NetworkManager specific error numbers, + * which is not the same as the native error numbers (errsv from ). But + * as far as normalizing goes, nm_errno() does exactly the same remapping as + * nm_errno_native(). */ +static inline int +nm_errno (int nmerr) +{ + return nm_errno_native (nmerr); +} + +/* this maps a native errno to a (always non-negative) nm-error number. + * + * Note that nm-error numbers are embedded into the range of regular + * errno. The only difference is, that nm-error numbers reserve a + * range (_NM_ERRNO_RESERVED_FIRST, _NM_ERRNO_RESERVED_LAST) for their + * own purpose. + * + * That means, converting an errno to nm-error number means in + * most cases just returning itself. + * Only pathological cases need special handling: + * + * - 0 is mapped to NME_ERRNO_SUCCESS; + * - G_MININT is mapped to NME_ERRNO_OUT_OF_RANGE; + * - values in the range of (+/-) [_NM_ERRNO_RESERVED_FIRST, _NM_ERRNO_RESERVED_LAST] + * are mapped to NME_NATIVE_ERRNO + * - all other values are their (positive) absolute value. + */ +static inline int +nm_errno_from_native (int errsv) +{ + switch (errsv) { + case 0: return NME_ERRNO_SUCCESS; + case G_MININT: return NME_ERRNO_OUT_OF_RANGE; + default: + if (errsv < 0) + errsv = -errsv; + return G_UNLIKELY ( errsv >= _NM_ERRNO_RESERVED_FIRST + && errsv <= _NM_ERRNO_RESERVED_LAST) + ? NME_NATIVE_ERRNO + : errsv; + } +} + +const char *nm_strerror (int nmerr); + +/*****************************************************************************/ + +#define NM_STRERROR_BUFSIZE 1024 + +const char *nm_strerror_native_r (int errsv, char *buf, gsize buf_size); +const char *nm_strerror_native (int errsv); + +/*****************************************************************************/ + +#endif /* __NM_ERRNO_H__ */ diff --git a/shared/nm-glib-aux/nm-glib.h b/shared/nm-glib-aux/nm-glib.h new file mode 100644 index 00000000..e941e067 --- /dev/null +++ b/shared/nm-glib-aux/nm-glib.h @@ -0,0 +1,567 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Copyright 2008 - 2018 Red Hat, Inc. + */ + +#ifndef __NM_GLIB_H__ +#define __NM_GLIB_H__ + +/*****************************************************************************/ + +#ifndef __NM_MACROS_INTERNAL_H__ +#error "nm-glib.h requires nm-macros-internal.h. Do not include this directly" +#endif + +/*****************************************************************************/ + +#ifdef __clang__ + +#undef G_GNUC_BEGIN_IGNORE_DEPRECATIONS +#undef G_GNUC_END_IGNORE_DEPRECATIONS + +#define G_GNUC_BEGIN_IGNORE_DEPRECATIONS \ + _Pragma("clang diagnostic push") \ + _Pragma("clang diagnostic ignored \"-Wdeprecated-declarations\"") + +#define G_GNUC_END_IGNORE_DEPRECATIONS \ + _Pragma("clang diagnostic pop") + +#endif + +/*****************************************************************************/ + +static inline void +__g_type_ensure (GType type) +{ +#if !GLIB_CHECK_VERSION(2,34,0) + if (G_UNLIKELY (type == (GType)-1)) + g_error ("can't happen"); +#else + G_GNUC_BEGIN_IGNORE_DEPRECATIONS; + g_type_ensure (type); + G_GNUC_END_IGNORE_DEPRECATIONS; +#endif +} +#define g_type_ensure __g_type_ensure + +/*****************************************************************************/ + +#if !GLIB_CHECK_VERSION(2,34,0) + +#define g_clear_pointer(pp, destroy) \ + G_STMT_START { \ + G_STATIC_ASSERT (sizeof *(pp) == sizeof (gpointer)); \ + /* Only one access, please */ \ + gpointer *_pp = (gpointer *) (pp); \ + gpointer _p; \ + /* This assignment is needed to avoid a gcc warning */ \ + GDestroyNotify _destroy = (GDestroyNotify) (destroy); \ + \ + _p = *_pp; \ + if (_p) \ + { \ + *_pp = NULL; \ + _destroy (_p); \ + } \ + } G_STMT_END + +#endif + +/*****************************************************************************/ + +#if !GLIB_CHECK_VERSION(2,34,0) + +/* These are used to clean up the output of test programs; we can just let + * them no-op in older glib. + */ +#define g_test_expect_message(log_domain, log_level, pattern) +#define g_test_assert_expected_messages() + +#else + +/* We build with -DGLIB_MAX_ALLOWED_VERSION set to 2.32 to make sure we don't + * accidentally use new API that we shouldn't. But we don't want warnings for + * the APIs that we emulate above. + */ + +#define g_test_expect_message(domain, level, format...) \ + G_STMT_START { \ + G_GNUC_BEGIN_IGNORE_DEPRECATIONS \ + g_test_expect_message (domain, level, format); \ + G_GNUC_END_IGNORE_DEPRECATIONS \ + } G_STMT_END + +#define g_test_assert_expected_messages_internal(domain, file, line, func) \ + G_STMT_START { \ + G_GNUC_BEGIN_IGNORE_DEPRECATIONS \ + g_test_assert_expected_messages_internal (domain, file, line, func); \ + G_GNUC_END_IGNORE_DEPRECATIONS \ + } G_STMT_END + +#endif + +/*****************************************************************************/ + +#if GLIB_CHECK_VERSION (2, 35, 0) +/* For glib >= 2.36, g_type_init() is deprecated. + * But since 2.35.1 (7c42ab23b55c43ab96d0ac2124b550bf1f49c1ec) this function + * does nothing. Replace the call with empty statement. */ +#define nm_g_type_init() G_STMT_START { (void) 0; } G_STMT_END +#else +#define nm_g_type_init() G_STMT_START { g_type_init (); } G_STMT_END +#endif + +/*****************************************************************************/ + +/* g_test_initialized() is only available since glib 2.36. */ +#if !GLIB_CHECK_VERSION (2, 36, 0) +#define g_test_initialized() (g_test_config_vars->test_initialized) +#endif + +/*****************************************************************************/ + +/* g_assert_cmpmem() is only available since glib 2.46. */ +#if !GLIB_CHECK_VERSION (2, 45, 7) +#define g_assert_cmpmem(m1, l1, m2, l2) G_STMT_START {\ + gconstpointer __m1 = m1, __m2 = m2; \ + int __l1 = l1, __l2 = l2; \ + if (__l1 != __l2) \ + g_assertion_message_cmpnum (G_LOG_DOMAIN, __FILE__, __LINE__, G_STRFUNC, \ + #l1 " (len(" #m1 ")) == " #l2 " (len(" #m2 "))", __l1, "==", __l2, 'i'); \ + else if (memcmp (__m1, __m2, __l1) != 0) \ + g_assertion_message (G_LOG_DOMAIN, __FILE__, __LINE__, G_STRFUNC, \ + "assertion failed (" #m1 " == " #m2 ")"); \ + } G_STMT_END +#endif + +/*****************************************************************************/ + +/* Rumtime check for glib version. First do a compile time check which + * (if satisfied) shortcuts the runtime check. */ +static inline gboolean +nm_glib_check_version (guint major, guint minor, guint micro) +{ + return GLIB_CHECK_VERSION (major, minor, micro) + || ( ( glib_major_version > major) + || ( glib_major_version == major + && glib_minor_version > minor) + || ( glib_major_version == major + && glib_minor_version == minor + && glib_micro_version < micro)); +} + +/*****************************************************************************/ + +/* g_test_skip() is only available since glib 2.38. Add a compatibility wrapper. */ +static inline void +__nmtst_g_test_skip (const char *msg) +{ +#if GLIB_CHECK_VERSION (2, 38, 0) + G_GNUC_BEGIN_IGNORE_DEPRECATIONS + g_test_skip (msg); + G_GNUC_END_IGNORE_DEPRECATIONS +#else + g_debug ("%s", msg); +#endif +} +#define g_test_skip __nmtst_g_test_skip + +/*****************************************************************************/ + +/* g_test_add_data_func_full() is only available since glib 2.34. Add a compatibility wrapper. */ +static inline void +__g_test_add_data_func_full (const char *testpath, + gpointer test_data, + GTestDataFunc test_func, + GDestroyNotify data_free_func) +{ +#if GLIB_CHECK_VERSION (2, 34, 0) + G_GNUC_BEGIN_IGNORE_DEPRECATIONS + g_test_add_data_func_full (testpath, test_data, test_func, data_free_func); + G_GNUC_END_IGNORE_DEPRECATIONS +#else + g_return_if_fail (testpath != NULL); + g_return_if_fail (testpath[0] == '/'); + g_return_if_fail (test_func != NULL); + + g_test_add_vtable (testpath, 0, test_data, NULL, + (GTestFixtureFunc) test_func, + (GTestFixtureFunc) data_free_func); +#endif +} +#define g_test_add_data_func_full __g_test_add_data_func_full + +/*****************************************************************************/ + +#if !GLIB_CHECK_VERSION (2, 34, 0) +#define G_DEFINE_QUARK(QN, q_n) \ +GQuark \ +q_n##_quark (void) \ +{ \ + static GQuark q; \ + \ + if G_UNLIKELY (q == 0) \ + q = g_quark_from_static_string (#QN); \ + \ + return q; \ +} +#endif + +/*****************************************************************************/ + +static inline gboolean +nm_g_hash_table_replace (GHashTable *hash, gpointer key, gpointer value) +{ + /* glib 2.40 added a return value indicating whether the key already existed + * (910191597a6c2e5d5d460e9ce9efb4f47d9cc63c). */ +#if GLIB_CHECK_VERSION(2, 40, 0) + return g_hash_table_replace (hash, key, value); +#else + gboolean contained = g_hash_table_contains (hash, key); + + g_hash_table_replace (hash, key, value); + return !contained; +#endif +} + +static inline gboolean +nm_g_hash_table_insert (GHashTable *hash, gpointer key, gpointer value) +{ + /* glib 2.40 added a return value indicating whether the key already existed + * (910191597a6c2e5d5d460e9ce9efb4f47d9cc63c). */ +#if GLIB_CHECK_VERSION(2, 40, 0) + return g_hash_table_insert (hash, key, value); +#else + gboolean contained = g_hash_table_contains (hash, key); + + g_hash_table_insert (hash, key, value); + return !contained; +#endif +} + +static inline gboolean +nm_g_hash_table_add (GHashTable *hash, gpointer key) +{ + /* glib 2.40 added a return value indicating whether the key already existed + * (910191597a6c2e5d5d460e9ce9efb4f47d9cc63c). */ +#if GLIB_CHECK_VERSION(2, 40, 0) + return g_hash_table_add (hash, key); +#else + gboolean contained = g_hash_table_contains (hash, key); + + g_hash_table_add (hash, key); + return !contained; +#endif +} + +/*****************************************************************************/ + +#if !GLIB_CHECK_VERSION(2, 40, 0) || defined (NM_GLIB_COMPAT_H_TEST) +static inline void +_nm_g_ptr_array_insert (GPtrArray *array, + int index_, + gpointer data) +{ + g_return_if_fail (array); + g_return_if_fail (index_ >= -1); + g_return_if_fail (index_ <= (int) array->len); + + g_ptr_array_add (array, data); + + if (index_ != -1 && index_ != (int) (array->len - 1)) { + memmove (&(array->pdata[index_ + 1]), + &(array->pdata[index_]), + (array->len - index_ - 1) * sizeof (gpointer)); + array->pdata[index_] = data; + } +} +#endif + +#if !GLIB_CHECK_VERSION(2, 40, 0) +#define g_ptr_array_insert(array, index, data) G_STMT_START { _nm_g_ptr_array_insert (array, index, data); } G_STMT_END +#else +#define g_ptr_array_insert(array, index, data) \ + G_STMT_START { \ + G_GNUC_BEGIN_IGNORE_DEPRECATIONS \ + g_ptr_array_insert (array, index, data); \ + G_GNUC_END_IGNORE_DEPRECATIONS \ + } G_STMT_END +#endif + +/*****************************************************************************/ + +#if !GLIB_CHECK_VERSION (2, 40, 0) +static inline gboolean +_g_key_file_save_to_file (GKeyFile *key_file, + const char *filename, + GError **error) +{ + char *contents; + gboolean success; + gsize length; + + g_return_val_if_fail (key_file != NULL, FALSE); + g_return_val_if_fail (filename != NULL, FALSE); + g_return_val_if_fail (error == NULL || *error == NULL, FALSE); + + contents = g_key_file_to_data (key_file, &length, NULL); + g_assert (contents != NULL); + + success = g_file_set_contents (filename, contents, length, error); + g_free (contents); + + return success; +} +#define g_key_file_save_to_file(key_file, filename, error) \ + _g_key_file_save_to_file (key_file, filename, error) +#else +#define g_key_file_save_to_file(key_file, filename, error) \ + ({ \ + gboolean _success; \ + \ + G_GNUC_BEGIN_IGNORE_DEPRECATIONS \ + _success = g_key_file_save_to_file (key_file, filename, error); \ + G_GNUC_END_IGNORE_DEPRECATIONS \ + _success; \ + }) +#endif + +/*****************************************************************************/ + +#if GLIB_CHECK_VERSION (2, 36, 0) +#define g_credentials_get_unix_pid(creds, error) \ + ({ \ + G_GNUC_BEGIN_IGNORE_DEPRECATIONS \ + (g_credentials_get_unix_pid) ((creds), (error)); \ + G_GNUC_END_IGNORE_DEPRECATIONS \ + }) +#else +#define g_credentials_get_unix_pid(creds, error) \ + ({ \ + struct ucred *native_creds; \ + \ + native_creds = g_credentials_get_native ((creds), G_CREDENTIALS_TYPE_LINUX_UCRED); \ + g_assert (native_creds); \ + native_creds->pid; \ + }) +#endif + +/*****************************************************************************/ + +#if !GLIB_CHECK_VERSION(2, 40, 0) || defined (NM_GLIB_COMPAT_H_TEST) +static inline gpointer * +_nm_g_hash_table_get_keys_as_array (GHashTable *hash_table, + guint *length) +{ + GHashTableIter iter; + gpointer key, *ret; + guint i = 0; + + g_return_val_if_fail (hash_table, NULL); + + ret = g_new0 (gpointer, g_hash_table_size (hash_table) + 1); + g_hash_table_iter_init (&iter, hash_table); + + while (g_hash_table_iter_next (&iter, &key, NULL)) + ret[i++] = key; + + ret[i] = NULL; + + if (length) + *length = i; + + return ret; +} +#endif +#if !GLIB_CHECK_VERSION(2, 40, 0) +#define g_hash_table_get_keys_as_array(hash_table, length) \ + ({ \ + _nm_g_hash_table_get_keys_as_array (hash_table, length); \ + }) +#else +#define g_hash_table_get_keys_as_array(hash_table, length) \ + ({ \ + G_GNUC_BEGIN_IGNORE_DEPRECATIONS \ + (g_hash_table_get_keys_as_array) ((hash_table), (length)); \ + G_GNUC_END_IGNORE_DEPRECATIONS \ + }) +#endif + +/*****************************************************************************/ + +#ifndef g_info +/* g_info was only added with 2.39.2 */ +#define g_info(...) g_log (G_LOG_DOMAIN, \ + G_LOG_LEVEL_INFO, \ + __VA_ARGS__) +#endif + +/*****************************************************************************/ + +#if !GLIB_CHECK_VERSION(2, 44, 0) +static inline gpointer +g_steal_pointer (gpointer pp) +{ + gpointer *ptr = (gpointer *) pp; + gpointer ref; + + ref = *ptr; + *ptr = NULL; + + return ref; +} +#endif + +#ifdef g_steal_pointer +#undef g_steal_pointer +#endif +#define g_steal_pointer(pp) \ + ((typeof (*(pp))) g_steal_pointer (pp)) + +/*****************************************************************************/ + +static inline gboolean +_nm_g_strv_contains (const char * const *strv, + const char *str) +{ +#if !GLIB_CHECK_VERSION(2, 44, 0) + g_return_val_if_fail (strv != NULL, FALSE); + g_return_val_if_fail (str != NULL, FALSE); + + for (; *strv != NULL; strv++) { + if (g_str_equal (str, *strv)) + return TRUE; + } + + return FALSE; +#else + G_GNUC_BEGIN_IGNORE_DEPRECATIONS + return g_strv_contains (strv, str); + G_GNUC_END_IGNORE_DEPRECATIONS +#endif +} +#define g_strv_contains _nm_g_strv_contains + +/*****************************************************************************/ + +static inline GVariant * +_nm_g_variant_new_take_string (char *string) +{ +#if !GLIB_CHECK_VERSION(2, 36, 0) + GVariant *value; + + g_return_val_if_fail (string != NULL, NULL); + g_return_val_if_fail (g_utf8_validate (string, -1, NULL), NULL); + + value = g_variant_new_string (string); + g_free (string); + return value; +#elif !GLIB_CHECK_VERSION(2, 38, 0) + GVariant *value; + GBytes *bytes; + + g_return_val_if_fail (string != NULL, NULL); + g_return_val_if_fail (g_utf8_validate (string, -1, NULL), NULL); + + bytes = g_bytes_new_take (string, strlen (string) + 1); + value = g_variant_new_from_bytes (G_VARIANT_TYPE_STRING, bytes, TRUE); + g_bytes_unref (bytes); + + return value; +#else + G_GNUC_BEGIN_IGNORE_DEPRECATIONS + return g_variant_new_take_string (string); + G_GNUC_END_IGNORE_DEPRECATIONS +#endif +} +#define g_variant_new_take_string _nm_g_variant_new_take_string + +/*****************************************************************************/ + +#if !GLIB_CHECK_VERSION(2, 38, 0) +_nm_printf (1, 2) +static inline GVariant * +_nm_g_variant_new_printf (const char *format_string, ...) +{ + char *string; + va_list ap; + + g_return_val_if_fail (format_string, NULL); + + va_start (ap, format_string); + string = g_strdup_vprintf (format_string, ap); + va_end (ap); + + return g_variant_new_take_string (string); +} +#define g_variant_new_printf(...) _nm_g_variant_new_printf(__VA_ARGS__) +#else +#define g_variant_new_printf(...) \ + ({ \ + GVariant *_v; \ + \ + G_GNUC_BEGIN_IGNORE_DEPRECATIONS \ + _v = g_variant_new_printf (__VA_ARGS__); \ + G_GNUC_END_IGNORE_DEPRECATIONS \ + _v; \ + }) +#endif + +/*****************************************************************************/ + +#if !GLIB_CHECK_VERSION (2, 56, 0) +#define g_object_ref(Obj) ((typeof(Obj)) g_object_ref (Obj)) +#define g_object_ref_sink(Obj) ((typeof(Obj)) g_object_ref_sink (Obj)) +#endif + +/*****************************************************************************/ + +#ifndef g_autofree +/* we still don't rely on recent glib to provide g_autofree. Hence, we continue + * to use our gs_* free macros that we took from libgsystem. + * + * To ease migration towards g_auto*, add a compat define for g_autofree. */ +#define g_autofree gs_free +#endif + +/*****************************************************************************/ + +#if !GLIB_CHECK_VERSION (2, 47, 1) +/* Older versions of g_value_unset() only allowed to unset a GValue which + * was initialized previously. This was relaxed ([1], [2], [3]). + * + * Our nm_auto_unset_gvalue macro requires to be able to call g_value_unset(). + * Also, it is our general practice to allow for that. Add a compat implementation. + * + * [1] https://gitlab.gnome.org/GNOME/glib/commit/4b2d92a864f1505f1b08eb639d74293fa32681da + * [2] commit "Allow passing unset GValues to g_value_unset()" + * [3] https://bugzilla.gnome.org/show_bug.cgi?id=755766 + */ +static inline void +_nm_g_value_unset (GValue *value) +{ + g_return_if_fail (value); + + if (value->g_type != 0) + g_value_unset (value); +} +#define g_value_unset _nm_g_value_unset +#endif + +/*****************************************************************************/ + +#endif /* __NM_GLIB_H__ */ diff --git a/shared/nm-glib-aux/nm-hash-utils.c b/shared/nm-glib-aux/nm-hash-utils.c new file mode 100644 index 00000000..6e728e6b --- /dev/null +++ b/shared/nm-glib-aux/nm-hash-utils.c @@ -0,0 +1,196 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* NetworkManager -- Network link manager + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA. + * + * (C) Copyright 2017 Red Hat, Inc. + */ + +#include "nm-default.h" + +#include "nm-hash-utils.h" + +#include + +#include "nm-shared-utils.h" +#include "nm-random-utils.h" + +/*****************************************************************************/ + +#define HASH_KEY_SIZE 16u +#define HASH_KEY_SIZE_GUINT ((HASH_KEY_SIZE + sizeof (guint) - 1) / sizeof (guint)) + +G_STATIC_ASSERT (sizeof (guint) * HASH_KEY_SIZE_GUINT >= HASH_KEY_SIZE); + +static const guint8 *volatile global_seed = NULL; + +static const guint8 * +_get_hash_key_init (void) +{ + static gsize g_lock; + /* the returned hash is aligned to guin64, hence, it is safe + * to use it as guint* or guint64* pointer. */ + static union { + guint8 v8[HASH_KEY_SIZE]; + } g_arr _nm_alignas (guint64); + const guint8 *g; + union { + guint8 v8[HASH_KEY_SIZE]; + guint vuint; + } t_arr; + +again: + g = g_atomic_pointer_get (&global_seed); + if (G_LIKELY (g != NULL)) { + nm_assert (g == g_arr.v8); + return g; + } + + { + CSipHash siph_state; + uint64_t h; + + /* initialize a random key in t_arr. */ + + nm_utils_random_bytes (&t_arr, sizeof (t_arr)); + + /* use siphash() of the key-size, to mangle the first guint. Otherwise, + * the first guint has only the entropy that nm_utils_random_bytes() + * generated for the first 4 bytes and relies on a good random generator. + * + * The first int is especially interesting for nm_hash_static() below, and we + * want to have it all the entropy of t_arr. */ + c_siphash_init (&siph_state, t_arr.v8); + c_siphash_append (&siph_state, (const guint8 *) &t_arr, sizeof (t_arr)); + h = c_siphash_finalize (&siph_state); + if (sizeof (guint) < sizeof (h)) + t_arr.vuint = t_arr.vuint ^ ((guint) (h & 0xFFFFFFFFu)) ^ ((guint) (h >> 32)); + else + t_arr.vuint = t_arr.vuint ^ ((guint) (h & 0xFFFFFFFFu)); + } + + if (!g_once_init_enter (&g_lock)) { + /* lost a race. The random key is already initialized. */ + goto again; + } + + memcpy (g_arr.v8, t_arr.v8, HASH_KEY_SIZE); + g = g_arr.v8; + g_atomic_pointer_set (&global_seed, g); + g_once_init_leave (&g_lock, 1); + return g; +} + +#define _get_hash_key() \ + ({ \ + const guint8 *_g; \ + \ + _g = g_atomic_pointer_get (&global_seed); \ + if (G_UNLIKELY (!_g)) \ + _g = _get_hash_key_init (); \ + _g; \ + }) + +guint +nm_hash_static (guint static_seed) +{ + /* note that we only xor the static_seed with the key. + * We don't use siphash, which would mix the bits better. + * Note that this doesn't matter, because static_seed is not + * supposed to be a value that you are hashing (for that, use + * full siphash). + * Instead, different callers may set a different static_seed + * so that nm_hash_str(NULL) != nm_hash_ptr(NULL). + * + * Also, ensure that we don't return zero. + */ + return ((*((const guint *) _get_hash_key ())) ^ static_seed) + ?: static_seed ?: 3679500967u; +} + +void +nm_hash_siphash42_init (CSipHash *h, guint static_seed) +{ + const guint8 *g; + guint seed[HASH_KEY_SIZE_GUINT]; + + nm_assert (h); + + g = _get_hash_key (); + memcpy (seed, g, HASH_KEY_SIZE); + seed[0] ^= static_seed; + c_siphash_init (h, (const guint8 *) seed); +} + +guint +nm_hash_str (const char *str) +{ + NMHashState h; + + if (!str) + return nm_hash_static (1867854211u); + nm_hash_init (&h, 1867854211u); + nm_hash_update_str (&h, str); + return nm_hash_complete (&h); +} + +guint +nm_str_hash (gconstpointer str) +{ + return nm_hash_str (str); +} + +guint +nm_hash_ptr (gconstpointer ptr) +{ + NMHashState h; + + if (!ptr) + return nm_hash_static (2907677551u); + nm_hash_init (&h, 2907677551u); + nm_hash_update (&h, &ptr, sizeof (ptr)); + return nm_hash_complete (&h); +} + +guint +nm_direct_hash (gconstpointer ptr) +{ + return nm_hash_ptr (ptr); +} + +/*****************************************************************************/ + +guint +nm_pstr_hash (gconstpointer p) +{ + const char *const*s = p; + + if (!s) + return nm_hash_static (101061439u); + return nm_hash_str (*s); +} + +gboolean +nm_pstr_equal (gconstpointer a, gconstpointer b) +{ + const char *const*s1 = a; + const char *const*s2 = b; + + return (s1 == s2) + || ( s1 + && s2 + && nm_streq0 (*s1, *s2)); +} diff --git a/shared/nm-glib-aux/nm-hash-utils.h b/shared/nm-glib-aux/nm-hash-utils.h new file mode 100644 index 00000000..3f622f99 --- /dev/null +++ b/shared/nm-glib-aux/nm-hash-utils.h @@ -0,0 +1,315 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* NetworkManager -- Network link manager + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA. + * + * (C) Copyright 2017 Red Hat, Inc. + */ + +#ifndef __NM_HASH_UTILS_H__ +#define __NM_HASH_UTILS_H__ + +#include "c-siphash/src/c-siphash.h" +#include "nm-macros-internal.h" + +/*****************************************************************************/ + +void nm_hash_siphash42_init (CSipHash *h, guint static_seed); + +/* Siphash24 of binary buffer @arr and @len, using the randomized seed from + * other NMHash functions. + * + * Note, that this is guaranteed to use siphash42 under the hood (contrary to + * all other NMHash API, which leave this undefined). That matters at the point, + * where the caller needs to be sure that a reasonably strong hasing algorithm + * is used. (Yes, NMHash is all about siphash24, but otherwise that is not promised + * anywhere). + * + * Another difference is, that this returns guint64 (not guint like other NMHash functions). + * + * Another difference is, that this may also return zero (not like nm_hash_complete()). + * + * Then, why not use c_siphash_hash() directly? Because this also uses the randomized, + * per-run hash-seed like nm_hash_init(). So, you get siphash24 with a random + * seed (which is cached for the current run of the program). + */ +static inline guint64 +nm_hash_siphash42 (guint static_seed, const void *ptr, gsize n) +{ + CSipHash h; + + nm_hash_siphash42_init (&h, static_seed); + c_siphash_append (&h, ptr, n); + return c_siphash_finalize (&h); +} + +/*****************************************************************************/ + +struct _NMHashState { + CSipHash _state; +}; + +typedef struct _NMHashState NMHashState; + +guint nm_hash_static (guint static_seed); + +static inline void +nm_hash_init (NMHashState *state, guint static_seed) +{ + nm_assert (state); + + nm_hash_siphash42_init (&state->_state, static_seed); +} + +static inline guint64 +nm_hash_complete_u64 (NMHashState *state) +{ + nm_assert (state); + + /* this returns the native u64 hash value. Note that this differs + * from nm_hash_complete() in two ways: + * + * - the type, guint64 vs. guint. + * - nm_hash_complete() never returns zero. + * + * In practice, nm_hash*() API is implemented via siphash24, so this returns + * the siphash24 value. But that is not guaranteed by the API, and if you need + * siphash24 directly, use c_siphash_*() and nm_hash_siphash42*() API. */ + return c_siphash_finalize (&state->_state); +} + +static inline guint +nm_hash_complete (NMHashState *state) +{ + guint64 h; + + h = nm_hash_complete_u64 (state); + + /* we don't ever want to return a zero hash. + * + * NMPObject requires that in _idx_obj_part(), and it's just a good idea. */ + return (((guint) (h >> 32)) ^ ((guint) h)) ?: 1396707757u; +} + +static inline void +nm_hash_update (NMHashState *state, const void *ptr, gsize n) +{ + nm_assert (state); + nm_assert (ptr); + nm_assert (n > 0); + + /* Note: the data passed in here might be sensitive data (secrets), + * that we should nm_explicty_zero() afterwards. However, since + * we are using siphash24 with a random key, that is not really + * necessary. Something to keep in mind, if we ever move away from + * this hash implementation. */ + c_siphash_append (&state->_state, ptr, n); +} + +#define nm_hash_update_val(state, val) \ + G_STMT_START { \ + typeof (val) _val = (val); \ + \ + nm_hash_update ((state), &_val, sizeof (_val)); \ + } G_STMT_END + +#define nm_hash_update_valp(state, val) \ + nm_hash_update ((state), (val), sizeof (*(val))) \ + +static inline void +nm_hash_update_bool (NMHashState *state, bool val) +{ + nm_hash_update (state, &val, sizeof (val)); +} + +#define _NM_HASH_COMBINE_BOOLS_x_1( t, y) ((y) ? ((t) (1ull << 0)) : ((t) 0ull)) +#define _NM_HASH_COMBINE_BOOLS_x_2( t, y, ...) ((y) ? ((t) (1ull << 1)) : ((t) 0ull)) | _NM_HASH_COMBINE_BOOLS_x_1 (t, __VA_ARGS__) +#define _NM_HASH_COMBINE_BOOLS_x_3( t, y, ...) ((y) ? ((t) (1ull << 2)) : ((t) 0ull)) | _NM_HASH_COMBINE_BOOLS_x_2 (t, __VA_ARGS__) +#define _NM_HASH_COMBINE_BOOLS_x_4( t, y, ...) ((y) ? ((t) (1ull << 3)) : ((t) 0ull)) | _NM_HASH_COMBINE_BOOLS_x_3 (t, __VA_ARGS__) +#define _NM_HASH_COMBINE_BOOLS_x_5( t, y, ...) ((y) ? ((t) (1ull << 4)) : ((t) 0ull)) | _NM_HASH_COMBINE_BOOLS_x_4 (t, __VA_ARGS__) +#define _NM_HASH_COMBINE_BOOLS_x_6( t, y, ...) ((y) ? ((t) (1ull << 5)) : ((t) 0ull)) | _NM_HASH_COMBINE_BOOLS_x_5 (t, __VA_ARGS__) +#define _NM_HASH_COMBINE_BOOLS_x_7( t, y, ...) ((y) ? ((t) (1ull << 6)) : ((t) 0ull)) | _NM_HASH_COMBINE_BOOLS_x_6 (t, __VA_ARGS__) +#define _NM_HASH_COMBINE_BOOLS_x_8( t, y, ...) ((y) ? ((t) (1ull << 7)) : ((t) 0ull)) | _NM_HASH_COMBINE_BOOLS_x_7 (t, __VA_ARGS__) +#define _NM_HASH_COMBINE_BOOLS_x_9( t, y, ...) ((y) ? ((t) (1ull << 8)) : ((t) 0ull)) | (G_STATIC_ASSERT_EXPR (sizeof (t) >= 2), (_NM_HASH_COMBINE_BOOLS_x_8 (t, __VA_ARGS__))) +#define _NM_HASH_COMBINE_BOOLS_x_10(t, y, ...) ((y) ? ((t) (1ull << 9)) : ((t) 0ull)) | _NM_HASH_COMBINE_BOOLS_x_9 (t, __VA_ARGS__) +#define _NM_HASH_COMBINE_BOOLS_x_11(t, y, ...) ((y) ? ((t) (1ull << 10)) : ((t) 0ull)) | _NM_HASH_COMBINE_BOOLS_x_10 (t, __VA_ARGS__) +#define _NM_HASH_COMBINE_BOOLS_n2(t, n, ...) _NM_HASH_COMBINE_BOOLS_x_##n (t, __VA_ARGS__) +#define _NM_HASH_COMBINE_BOOLS_n(t, n, ...) _NM_HASH_COMBINE_BOOLS_n2(t, n, __VA_ARGS__) + +#define NM_HASH_COMBINE_BOOLS(type, ...) ((type) (_NM_HASH_COMBINE_BOOLS_n(type, NM_NARG (__VA_ARGS__), __VA_ARGS__))) + +#define nm_hash_update_bools(state, ...) \ + nm_hash_update_val (state, NM_HASH_COMBINE_BOOLS (guint8, __VA_ARGS__)) + +#define _NM_HASH_COMBINE_VALS_typ_x_1( y) typeof (y) _v1; +#define _NM_HASH_COMBINE_VALS_typ_x_2( y, ...) typeof (y) _v2; _NM_HASH_COMBINE_VALS_typ_x_1 (__VA_ARGS__) +#define _NM_HASH_COMBINE_VALS_typ_x_3( y, ...) typeof (y) _v3; _NM_HASH_COMBINE_VALS_typ_x_2 (__VA_ARGS__) +#define _NM_HASH_COMBINE_VALS_typ_x_4( y, ...) typeof (y) _v4; _NM_HASH_COMBINE_VALS_typ_x_3 (__VA_ARGS__) +#define _NM_HASH_COMBINE_VALS_typ_x_5( y, ...) typeof (y) _v5; _NM_HASH_COMBINE_VALS_typ_x_4 (__VA_ARGS__) +#define _NM_HASH_COMBINE_VALS_typ_x_6( y, ...) typeof (y) _v6; _NM_HASH_COMBINE_VALS_typ_x_5 (__VA_ARGS__) +#define _NM_HASH_COMBINE_VALS_typ_x_7( y, ...) typeof (y) _v7; _NM_HASH_COMBINE_VALS_typ_x_6 (__VA_ARGS__) +#define _NM_HASH_COMBINE_VALS_typ_x_8( y, ...) typeof (y) _v8; _NM_HASH_COMBINE_VALS_typ_x_7 (__VA_ARGS__) +#define _NM_HASH_COMBINE_VALS_typ_x_9( y, ...) typeof (y) _v9; _NM_HASH_COMBINE_VALS_typ_x_8 (__VA_ARGS__) +#define _NM_HASH_COMBINE_VALS_typ_x_10(y, ...) typeof (y) _v10; _NM_HASH_COMBINE_VALS_typ_x_9 (__VA_ARGS__) +#define _NM_HASH_COMBINE_VALS_typ_x_11(y, ...) typeof (y) _v11; _NM_HASH_COMBINE_VALS_typ_x_10 (__VA_ARGS__) +#define _NM_HASH_COMBINE_VALS_typ_x_12(y, ...) typeof (y) _v12; _NM_HASH_COMBINE_VALS_typ_x_11 (__VA_ARGS__) +#define _NM_HASH_COMBINE_VALS_typ_x_13(y, ...) typeof (y) _v13; _NM_HASH_COMBINE_VALS_typ_x_12 (__VA_ARGS__) +#define _NM_HASH_COMBINE_VALS_typ_x_14(y, ...) typeof (y) _v14; _NM_HASH_COMBINE_VALS_typ_x_13 (__VA_ARGS__) +#define _NM_HASH_COMBINE_VALS_typ_x_15(y, ...) typeof (y) _v15; _NM_HASH_COMBINE_VALS_typ_x_14 (__VA_ARGS__) +#define _NM_HASH_COMBINE_VALS_typ_x_16(y, ...) typeof (y) _v16; _NM_HASH_COMBINE_VALS_typ_x_15 (__VA_ARGS__) +#define _NM_HASH_COMBINE_VALS_typ_x_17(y, ...) typeof (y) _v17; _NM_HASH_COMBINE_VALS_typ_x_16 (__VA_ARGS__) +#define _NM_HASH_COMBINE_VALS_typ_x_18(y, ...) typeof (y) _v18; _NM_HASH_COMBINE_VALS_typ_x_17 (__VA_ARGS__) +#define _NM_HASH_COMBINE_VALS_typ_x_19(y, ...) typeof (y) _v19; _NM_HASH_COMBINE_VALS_typ_x_18 (__VA_ARGS__) +#define _NM_HASH_COMBINE_VALS_typ_x_20(y, ...) typeof (y) _v20; _NM_HASH_COMBINE_VALS_typ_x_19 (__VA_ARGS__) +#define _NM_HASH_COMBINE_VALS_typ_n2(n, ...) _NM_HASH_COMBINE_VALS_typ_x_##n (__VA_ARGS__) +#define _NM_HASH_COMBINE_VALS_typ_n(n, ...) _NM_HASH_COMBINE_VALS_typ_n2(n, __VA_ARGS__) + +#define _NM_HASH_COMBINE_VALS_val_x_1( y) ._v1 = (y), +#define _NM_HASH_COMBINE_VALS_val_x_2( y, ...) ._v2 = (y), _NM_HASH_COMBINE_VALS_val_x_1 (__VA_ARGS__) +#define _NM_HASH_COMBINE_VALS_val_x_3( y, ...) ._v3 = (y), _NM_HASH_COMBINE_VALS_val_x_2 (__VA_ARGS__) +#define _NM_HASH_COMBINE_VALS_val_x_4( y, ...) ._v4 = (y), _NM_HASH_COMBINE_VALS_val_x_3 (__VA_ARGS__) +#define _NM_HASH_COMBINE_VALS_val_x_5( y, ...) ._v5 = (y), _NM_HASH_COMBINE_VALS_val_x_4 (__VA_ARGS__) +#define _NM_HASH_COMBINE_VALS_val_x_6( y, ...) ._v6 = (y), _NM_HASH_COMBINE_VALS_val_x_5 (__VA_ARGS__) +#define _NM_HASH_COMBINE_VALS_val_x_7( y, ...) ._v7 = (y), _NM_HASH_COMBINE_VALS_val_x_6 (__VA_ARGS__) +#define _NM_HASH_COMBINE_VALS_val_x_8( y, ...) ._v8 = (y), _NM_HASH_COMBINE_VALS_val_x_7 (__VA_ARGS__) +#define _NM_HASH_COMBINE_VALS_val_x_9( y, ...) ._v9 = (y), _NM_HASH_COMBINE_VALS_val_x_8 (__VA_ARGS__) +#define _NM_HASH_COMBINE_VALS_val_x_10(y, ...) ._v10 = (y), _NM_HASH_COMBINE_VALS_val_x_9 (__VA_ARGS__) +#define _NM_HASH_COMBINE_VALS_val_x_11(y, ...) ._v11 = (y), _NM_HASH_COMBINE_VALS_val_x_10 (__VA_ARGS__) +#define _NM_HASH_COMBINE_VALS_val_x_12(y, ...) ._v12 = (y), _NM_HASH_COMBINE_VALS_val_x_11 (__VA_ARGS__) +#define _NM_HASH_COMBINE_VALS_val_x_13(y, ...) ._v13 = (y), _NM_HASH_COMBINE_VALS_val_x_12 (__VA_ARGS__) +#define _NM_HASH_COMBINE_VALS_val_x_14(y, ...) ._v14 = (y), _NM_HASH_COMBINE_VALS_val_x_13 (__VA_ARGS__) +#define _NM_HASH_COMBINE_VALS_val_x_15(y, ...) ._v15 = (y), _NM_HASH_COMBINE_VALS_val_x_14 (__VA_ARGS__) +#define _NM_HASH_COMBINE_VALS_val_x_16(y, ...) ._v16 = (y), _NM_HASH_COMBINE_VALS_val_x_15 (__VA_ARGS__) +#define _NM_HASH_COMBINE_VALS_val_x_17(y, ...) ._v17 = (y), _NM_HASH_COMBINE_VALS_val_x_16 (__VA_ARGS__) +#define _NM_HASH_COMBINE_VALS_val_x_18(y, ...) ._v18 = (y), _NM_HASH_COMBINE_VALS_val_x_17 (__VA_ARGS__) +#define _NM_HASH_COMBINE_VALS_val_x_19(y, ...) ._v19 = (y), _NM_HASH_COMBINE_VALS_val_x_18 (__VA_ARGS__) +#define _NM_HASH_COMBINE_VALS_val_x_20(y, ...) ._v20 = (y), _NM_HASH_COMBINE_VALS_val_x_19 (__VA_ARGS__) +#define _NM_HASH_COMBINE_VALS_val_n2(n, ...) _NM_HASH_COMBINE_VALS_val_x_##n (__VA_ARGS__) +#define _NM_HASH_COMBINE_VALS_val_n(n, ...) _NM_HASH_COMBINE_VALS_val_n2(n, __VA_ARGS__) + +/* NM_HASH_COMBINE_VALS() is faster then nm_hash_update_val() as it combines multiple + * calls to nm_hash_update() using a packed structure. */ +#define NM_HASH_COMBINE_VALS(var, ...) \ + const struct _nm_packed { \ + _NM_HASH_COMBINE_VALS_typ_n (NM_NARG (__VA_ARGS__), __VA_ARGS__) \ + } var _nm_alignas (guint64) = { \ + _NM_HASH_COMBINE_VALS_val_n (NM_NARG (__VA_ARGS__), __VA_ARGS__) \ + } + +/* nm_hash_update_vals() is faster then nm_hash_update_val() as it combines multiple + * calls to nm_hash_update() using a packed structure. */ +#define nm_hash_update_vals(state, ...) \ + G_STMT_START { \ + NM_HASH_COMBINE_VALS (_val, __VA_ARGS__); \ + \ + nm_hash_update ((state), &_val, sizeof (_val)); \ + } G_STMT_END + +static inline void +nm_hash_update_mem (NMHashState *state, const void *ptr, gsize n) +{ + /* This also hashes the length of the data. That means, + * hashing two consecutive binary fields (of arbitrary + * length), will hash differently. That is, + * [[1,1], []] differs from [[1],[1]]. + * + * If you have a constant length (sizeof), use nm_hash_update() + * instead. */ + nm_hash_update (state, &n, sizeof (n)); + if (n > 0) + nm_hash_update (state, ptr, n); +} + +static inline void +nm_hash_update_str0 (NMHashState *state, const char *str) +{ + if (str) + nm_hash_update_mem (state, str, strlen (str)); + else { + gsize n = G_MAXSIZE; + + nm_hash_update (state, &n, sizeof (n)); + } +} + +static inline void +nm_hash_update_str (NMHashState *state, const char *str) +{ + nm_assert (str); + nm_hash_update (state, str, strlen (str) + 1); +} + +#if _NM_CC_SUPPORT_GENERIC +/* Like nm_hash_update_str(), but restricted to arrays only. nm_hash_update_str() only works + * with a @str argument that cannot be NULL. If you have a string pointer, that is never NULL, use + * nm_hash_update() instead. */ +#define nm_hash_update_strarr(state, str) \ + (_Generic (&(str), \ + const char (*) [sizeof (str)]: nm_hash_update_str ((state), (str)), \ + char (*) [sizeof (str)]: nm_hash_update_str ((state), (str))) \ + ) +#else +#define nm_hash_update_strarr(state, str) nm_hash_update_str ((state), (str)) +#endif + +guint nm_hash_ptr (gconstpointer ptr); +guint nm_direct_hash (gconstpointer str); + +guint nm_hash_str (const char *str); +guint nm_str_hash (gconstpointer str); + +#define nm_hash_val(static_seed, val) \ + ({ \ + NMHashState _h; \ + \ + nm_hash_init (&_h, (static_seed)); \ + nm_hash_update_val (&_h, (val)); \ + nm_hash_complete (&_h); \ + }) + +/*****************************************************************************/ + +/* nm_pstr_*() are for hashing keys that are pointers to strings, + * that is, "const char *const*" types, using strcmp(). */ + +guint nm_pstr_hash (gconstpointer p); + +gboolean nm_pstr_equal (gconstpointer a, gconstpointer b); + +/*****************************************************************************/ + +#define NM_HASH_OBFUSCATE_PTR_FMT "%016llx" + +/* sometimes we want to log a pointer directly, for providing context/information about + * the message that get logged. Logging pointer values directly defeats ASLR, so we should + * not do that. This returns a "unsigned long long" value that can be used + * instead. + * + * Note that there is a chance that two different pointer values hash to the same obfuscated + * value. So beware of that when reviewing logs. However, such a collision is very unlikely. */ +#define nm_hash_obfuscate_ptr(static_seed, val) \ + ({ \ + NMHashState _h; \ + const void *_val_obf_ptr = (val); \ + \ + nm_hash_init (&_h, (static_seed)); \ + nm_hash_update_val (&_h, _val_obf_ptr); \ + (unsigned long long) nm_hash_complete_u64 (&_h); \ + }) + +/*****************************************************************************/ + +#endif /* __NM_HASH_UTILS_H__ */ diff --git a/shared/nm-glib-aux/nm-io-utils.c b/shared/nm-glib-aux/nm-io-utils.c new file mode 100644 index 00000000..51312748 --- /dev/null +++ b/shared/nm-glib-aux/nm-io-utils.c @@ -0,0 +1,439 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* NetworkManager -- Network link manager + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA. + * + * (C) Copyright 2018 Red Hat, Inc. + */ + +#include "nm-default.h" + +#include "nm-io-utils.h" + +#include +#include +#include + +#include "nm-shared-utils.h" +#include "nm-secret-utils.h" +#include "nm-errno.h" + +/*****************************************************************************/ + +_nm_printf (3, 4) +static int +_get_contents_error (GError **error, int errsv, const char *format, ...) +{ + nm_assert (NM_ERRNO_NATIVE (errsv)); + + if (error) { + gs_free char *msg = NULL; + va_list args; + char bstrerr[NM_STRERROR_BUFSIZE]; + + va_start (args, format); + msg = g_strdup_vprintf (format, args); + va_end (args); + g_set_error (error, + G_FILE_ERROR, + g_file_error_from_errno (errsv), + "%s: %s", + msg, + nm_strerror_native_r (errsv, bstrerr, sizeof (bstrerr))); + } + return -errsv; +} +#define _get_contents_error_errno(error, ...) \ + ({ \ + int _errsv = (errno); \ + \ + _get_contents_error (error, _errsv, __VA_ARGS__); \ + }) + +static char * +_mem_realloc (char *old, gboolean do_bzero_mem, gsize cur_len, gsize new_len) +{ + char *new; + + /* re-allocating to zero bytes is an odd case. We don't need it + * and it's not supported. */ + nm_assert (new_len > 0); + + /* regardless of success/failure, @old will always be freed/consumed. */ + + if (do_bzero_mem && cur_len > 0) { + new = g_try_malloc (new_len); + if (new) + memcpy (new, old, NM_MIN (cur_len, new_len)); + nm_explicit_bzero (old, cur_len); + g_free (old); + } else { + new = g_try_realloc (old, new_len); + if (!new) + g_free (old); + } + + return new; +} + +/** + * nm_utils_fd_get_contents: + * @fd: open file descriptor to read. The fd will not be closed, + * but don't rely on its state afterwards. + * @close_fd: if %TRUE, @fd will be closed by the function. + * Passing %TRUE here might safe a syscall for dup(). + * @max_length: allocate at most @max_length bytes. If the + * file is larger, reading will fail. Set to zero to use + * a very large default. + * WARNING: @max_length is here to avoid a crash for huge/unlimited files. + * For example, stat(/sys/class/net/enp0s25/ifindex) gives a filesize of + * 4K, although the actual real is small. @max_length is the memory + * allocated in the process of reading the file, thus it must be at least + * the size reported by fstat. + * If you set it to 1K, read will fail because fstat() claims the + * file is larger. + * @flags: %NMUtilsFileGetContentsFlags for reading the file. + * @contents: the output buffer with the file read. It is always + * NUL terminated. The buffer is at most @max_length long, including + * the NUL byte. That is, it reads only files up to a length of + * @max_length - 1 bytes. + * @length: optional output argument of the read file size. + * + * A reimplementation of g_file_get_contents() with a few differences: + * - accepts an open fd, instead of a path name. This allows you to + * use openat(). + * - limits the maximum filesize to max_length. + * + * Returns: a negative error code on failure. + */ +int +nm_utils_fd_get_contents (int fd, + gboolean close_fd, + gsize max_length, + NMUtilsFileGetContentsFlags flags, + char **contents, + gsize *length, + GError **error) +{ + nm_auto_close int fd_keeper = close_fd ? fd : -1; + struct stat stat_buf; + gs_free char *str = NULL; + const bool do_bzero_mem = NM_FLAGS_HAS (flags, NM_UTILS_FILE_GET_CONTENTS_FLAG_SECRET); + int errsv; + + g_return_val_if_fail (fd >= 0, -EINVAL); + g_return_val_if_fail (contents, -EINVAL); + g_return_val_if_fail (!error || !*error, -EINVAL); + + if (fstat (fd, &stat_buf) < 0) + return _get_contents_error_errno (error, "failure during fstat"); + + if (!max_length) { + /* default to a very large size, but not extreme */ + max_length = 2 * 1024 * 1024; + } + + if ( stat_buf.st_size > 0 + && S_ISREG (stat_buf.st_mode)) { + const gsize n_stat = stat_buf.st_size; + ssize_t n_read; + + if (n_stat > max_length - 1) + return _get_contents_error (error, EMSGSIZE, "file too large (%zu+1 bytes with maximum %zu bytes)", n_stat, max_length); + + str = g_try_malloc (n_stat + 1); + if (!str) + return _get_contents_error (error, ENOMEM, "failure to allocate buffer of %zu+1 bytes", n_stat); + + n_read = nm_utils_fd_read_loop (fd, str, n_stat, TRUE); + if (n_read < 0) { + if (do_bzero_mem) + nm_explicit_bzero (str, n_stat); + return _get_contents_error (error, -n_read, "error reading %zu bytes from file descriptor", n_stat); + } + str[n_read] = '\0'; + + if (n_read < n_stat) { + if (!(str = _mem_realloc (str, do_bzero_mem, n_stat + 1, n_read + 1))) + return _get_contents_error (error, ENOMEM, "failure to reallocate buffer with %zu bytes", n_read + 1); + } + NM_SET_OUT (length, n_read); + } else { + nm_auto_fclose FILE *f = NULL; + char buf[4096]; + gsize n_have, n_alloc; + int fd2; + + if (fd_keeper >= 0) + fd2 = nm_steal_fd (&fd_keeper); + else { + fd2 = fcntl (fd, F_DUPFD_CLOEXEC, 0); + if (fd2 < 0) + return _get_contents_error_errno (error, "error during dup"); + } + + if (!(f = fdopen (fd2, "r"))) { + errsv = errno; + nm_close (fd2); + return _get_contents_error (error, errsv, "failure during fdopen"); + } + + n_have = 0; + n_alloc = 0; + + while (!feof (f)) { + gsize n_read; + + n_read = fread (buf, 1, sizeof (buf), f); + errsv = errno; + if (ferror (f)) { + if (do_bzero_mem) + nm_explicit_bzero (buf, sizeof (buf)); + return _get_contents_error (error, errsv, "error during fread"); + } + + if ( n_have > G_MAXSIZE - 1 - n_read + || n_have + n_read + 1 > max_length) { + if (do_bzero_mem) + nm_explicit_bzero (buf, sizeof (buf)); + return _get_contents_error (error, EMSGSIZE, "file stream too large (%zu+1 bytes with maximum %zu bytes)", + (n_have > G_MAXSIZE - 1 - n_read) ? G_MAXSIZE : n_have + n_read, + max_length); + } + + if (n_have + n_read + 1 >= n_alloc) { + gsize old_n_alloc = n_alloc; + + if (n_alloc != 0) { + nm_assert (str); + if (n_alloc >= max_length / 2) + n_alloc = max_length; + else + n_alloc *= 2; + } else { + nm_assert (!str); + n_alloc = NM_MIN (n_read + 1, sizeof (buf)); + } + + if (!(str = _mem_realloc (str, do_bzero_mem, old_n_alloc, n_alloc))) { + if (do_bzero_mem) + nm_explicit_bzero (buf, sizeof (buf)); + return _get_contents_error (error, ENOMEM, "failure to allocate buffer of %zu bytes", n_alloc); + } + } + + memcpy (str + n_have, buf, n_read); + n_have += n_read; + } + + if (do_bzero_mem) + nm_explicit_bzero (buf, sizeof (buf)); + + if (n_alloc == 0) + str = g_new0 (char, 1); + else { + str[n_have] = '\0'; + if (n_have + 1 < n_alloc) { + if (!(str = _mem_realloc (str, do_bzero_mem, n_alloc, n_have + 1))) + return _get_contents_error (error, ENOMEM, "failure to truncate buffer to %zu bytes", n_have + 1); + } + } + + NM_SET_OUT (length, n_have); + } + + *contents = g_steal_pointer (&str); + return 0; +} + +/** + * nm_utils_file_get_contents: + * @dirfd: optional file descriptor to use openat(). If negative, use plain open(). + * @filename: the filename to open. Possibly relative to @dirfd. + * @max_length: allocate at most @max_length bytes. + * WARNING: see nm_utils_fd_get_contents() hint about @max_length. + * @flags: %NMUtilsFileGetContentsFlags for reading the file. + * @contents: the output buffer with the file read. It is always + * NUL terminated. The buffer is at most @max_length long, including + * the NUL byte. That is, it reads only files up to a length of + * @max_length - 1 bytes. + * @length: optional output argument of the read file size. + * + * A reimplementation of g_file_get_contents() with a few differences: + * - accepts an @dirfd to open @filename relative to that path via openat(). + * - limits the maximum filesize to max_length. + * - uses O_CLOEXEC on internal file descriptor + * + * Returns: a negative error code on failure. + */ +int +nm_utils_file_get_contents (int dirfd, + const char *filename, + gsize max_length, + NMUtilsFileGetContentsFlags flags, + char **contents, + gsize *length, + GError **error) +{ + int fd; + int errsv; + char bstrerr[NM_STRERROR_BUFSIZE]; + + g_return_val_if_fail (filename && filename[0], -EINVAL); + + if (dirfd >= 0) { + fd = openat (dirfd, filename, O_RDONLY | O_CLOEXEC); + if (fd < 0) { + errsv = errno; + + g_set_error (error, + G_FILE_ERROR, + g_file_error_from_errno (errsv), + "Failed to open file \"%s\" with openat: %s", + filename, + nm_strerror_native_r (errsv, bstrerr, sizeof (bstrerr))); + return -NM_ERRNO_NATIVE (errsv); + } + } else { + fd = open (filename, O_RDONLY | O_CLOEXEC); + if (fd < 0) { + errsv = errno; + + g_set_error (error, + G_FILE_ERROR, + g_file_error_from_errno (errsv), + "Failed to open file \"%s\": %s", + filename, + nm_strerror_native_r (errsv, bstrerr, sizeof (bstrerr))); + return -NM_ERRNO_NATIVE (errsv); + } + } + return nm_utils_fd_get_contents (fd, + TRUE, + max_length, + flags, + contents, + length, + error); +} + +/*****************************************************************************/ + +/* + * Copied from GLib's g_file_set_contents() et al., but allows + * specifying a mode for the new file. + */ +gboolean +nm_utils_file_set_contents (const char *filename, + const char *contents, + gssize length, + mode_t mode, + GError **error) +{ + gs_free char *tmp_name = NULL; + struct stat statbuf; + int errsv; + gssize s; + int fd; + char bstrerr[NM_STRERROR_BUFSIZE]; + + g_return_val_if_fail (filename, FALSE); + g_return_val_if_fail (contents || !length, FALSE); + g_return_val_if_fail (!error || !*error, FALSE); + g_return_val_if_fail (length >= -1, FALSE); + + if (length == -1) + length = strlen (contents); + + tmp_name = g_strdup_printf ("%s.XXXXXX", filename); + fd = g_mkstemp_full (tmp_name, O_RDWR | O_CLOEXEC, mode); + if (fd < 0) { + errsv = errno; + g_set_error (error, + G_FILE_ERROR, + g_file_error_from_errno (errsv), + "failed to create file %s: %s", + tmp_name, + nm_strerror_native_r (errsv, bstrerr, sizeof (bstrerr))); + return FALSE; + } + + while (length > 0) { + s = write (fd, contents, length); + if (s < 0) { + errsv = errno; + if (errsv == EINTR) + continue; + + nm_close (fd); + unlink (tmp_name); + + g_set_error (error, + G_FILE_ERROR, + g_file_error_from_errno (errsv), + "failed to write to file %s: %s", + tmp_name, + nm_strerror_native_r (errsv, bstrerr, sizeof (bstrerr))); + return FALSE; + } + + g_assert (s <= length); + + contents += s; + length -= s; + } + + /* If the final destination exists and is > 0 bytes, we want to sync the + * newly written file to ensure the data is on disk when we rename over + * the destination. Otherwise if we get a system crash we can lose both + * the new and the old file on some filesystems. (I.E. those that don't + * guarantee the data is written to the disk before the metadata.) + */ + if ( lstat (filename, &statbuf) == 0 + && statbuf.st_size > 0) { + if (fsync (fd) != 0) { + errsv = errno; + + nm_close (fd); + unlink (tmp_name); + + g_set_error (error, + G_FILE_ERROR, + g_file_error_from_errno (errsv), + "failed to fsync %s: %s", + tmp_name, + nm_strerror_native_r (errsv, bstrerr, sizeof (bstrerr))); + return FALSE; + } + } + + nm_close (fd); + + if (rename (tmp_name, filename)) { + errsv = errno; + unlink (tmp_name); + g_set_error (error, + G_FILE_ERROR, + g_file_error_from_errno (errsv), + "failed to rename %s to %s: %s", + tmp_name, + filename, + nm_strerror_native_r (errsv, bstrerr, sizeof (bstrerr))); + return FALSE; + } + + return TRUE; +} diff --git a/shared/nm-glib-aux/nm-io-utils.h b/shared/nm-glib-aux/nm-io-utils.h new file mode 100644 index 00000000..dc72a2a6 --- /dev/null +++ b/shared/nm-glib-aux/nm-io-utils.h @@ -0,0 +1,63 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* NetworkManager -- Network link manager + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA. + * + * (C) Copyright 2018 Red Hat, Inc. + */ + +#ifndef __NM_IO_UTILS_H__ +#define __NM_IO_UTILS_H__ + +#include "nm-macros-internal.h" + +/*****************************************************************************/ + +/** + * NMUtilsFileGetContentsFlags: + * @NM_UTILS_FILE_GET_CONTENTS_FLAG_NONE: no flag + * @NM_UTILS_FILE_GET_CONTENTS_FLAG_SECRET: if present, ensure that no + * data is left in memory. Essentially, it means to call explicity_bzero() + * to not leave key material on the heap (when reading secrets). + */ +typedef enum { + NM_UTILS_FILE_GET_CONTENTS_FLAG_NONE = 0, + NM_UTILS_FILE_GET_CONTENTS_FLAG_SECRET = (1 << 0), +} NMUtilsFileGetContentsFlags; + +int nm_utils_fd_get_contents (int fd, + gboolean close_fd, + gsize max_length, + NMUtilsFileGetContentsFlags flags, + char **contents, + gsize *length, + GError **error); + +int nm_utils_file_get_contents (int dirfd, + const char *filename, + gsize max_length, + NMUtilsFileGetContentsFlags flags, + char **contents, + gsize *length, + GError **error); + +gboolean nm_utils_file_set_contents (const char *filename, + const char *contents, + gssize length, + mode_t mode, + GError **error); + +#endif /* __NM_IO_UTILS_H__ */ diff --git a/shared/nm-glib-aux/nm-jansson.h b/shared/nm-glib-aux/nm-jansson.h new file mode 100644 index 00000000..5a73231f --- /dev/null +++ b/shared/nm-glib-aux/nm-jansson.h @@ -0,0 +1,49 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Copyright 2018 Red Hat, Inc. + */ + +#ifndef __NM_JANSSON_H__ +#define __NM_JANSSON_H__ + +/* you need to include at least "config.h" first, possibly "nm-default.h". */ + +#if WITH_JANSSON + +#include + +/* Added in Jansson v2.7 */ +#ifndef json_boolean_value +#define json_boolean_value json_is_true +#endif + +/* Added in Jansson v2.8 */ +#ifndef json_object_foreach_safe +#define json_object_foreach_safe(object, n, key, value) \ + for (key = json_object_iter_key(json_object_iter(object)), \ + n = json_object_iter_next(object, json_object_key_to_iter(key)); \ + key && (value = json_object_iter_value(json_object_key_to_iter(key))); \ + key = json_object_iter_key(n), \ + n = json_object_iter_next(object, json_object_key_to_iter(key))) +#endif + +NM_AUTO_DEFINE_FCN0 (json_t *, _nm_auto_decref_json, json_decref) +#define nm_auto_decref_json nm_auto(_nm_auto_decref_json) + +#endif /* WITH_JANSON */ + +#endif /* __NM_JANSSON_H__ */ diff --git a/shared/nm-glib-aux/nm-logging-fwd.h b/shared/nm-glib-aux/nm-logging-fwd.h new file mode 100644 index 00000000..900dfff8 --- /dev/null +++ b/shared/nm-glib-aux/nm-logging-fwd.h @@ -0,0 +1,113 @@ +/* NetworkManager -- Network link manager + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA. + * + * Copyright (C) 2006 - 2018 Red Hat, Inc. + * Copyright (C) 2006 - 2008 Novell, Inc. + */ + +#ifndef __NM_LOGGING_DEFINES_H__ +#define __NM_LOGGING_DEFINES_H__ + +/* Log domains */ + +typedef enum { /*< skip >*/ + LOGD_NONE = 0LL, + LOGD_PLATFORM = (1LL << 0), /* Platform services */ + LOGD_RFKILL = (1LL << 1), + LOGD_ETHER = (1LL << 2), + LOGD_WIFI = (1LL << 3), + LOGD_BT = (1LL << 4), + LOGD_MB = (1LL << 5), /* mobile broadband */ + LOGD_DHCP4 = (1LL << 6), + LOGD_DHCP6 = (1LL << 7), + LOGD_PPP = (1LL << 8), + LOGD_WIFI_SCAN = (1LL << 9), + LOGD_IP4 = (1LL << 10), + LOGD_IP6 = (1LL << 11), + LOGD_AUTOIP4 = (1LL << 12), + LOGD_DNS = (1LL << 13), + LOGD_VPN = (1LL << 14), + LOGD_SHARING = (1LL << 15), /* Connection sharing/dnsmasq */ + LOGD_SUPPLICANT = (1LL << 16), /* Wi-Fi and 802.1x */ + LOGD_AGENTS = (1LL << 17), /* Secret agents */ + LOGD_SETTINGS = (1LL << 18), /* Settings */ + LOGD_SUSPEND = (1LL << 19), /* Suspend/Resume */ + LOGD_CORE = (1LL << 20), /* Core daemon and policy stuff */ + LOGD_DEVICE = (1LL << 21), /* Device state and activation */ + LOGD_OLPC = (1LL << 22), + LOGD_INFINIBAND = (1LL << 23), + LOGD_FIREWALL = (1LL << 24), + LOGD_ADSL = (1LL << 25), + LOGD_BOND = (1LL << 26), + LOGD_VLAN = (1LL << 27), + LOGD_BRIDGE = (1LL << 28), + LOGD_DBUS_PROPS = (1LL << 29), + LOGD_TEAM = (1LL << 30), + LOGD_CONCHECK = (1LL << 31), + LOGD_DCB = (1LL << 32), /* Data Center Bridging */ + LOGD_DISPATCH = (1LL << 33), + LOGD_AUDIT = (1LL << 34), + LOGD_SYSTEMD = (1LL << 35), + LOGD_VPN_PLUGIN = (1LL << 36), + LOGD_PROXY = (1LL << 37), + + __LOGD_MAX, + LOGD_ALL = (((__LOGD_MAX - 1LL) << 1) - 1LL), + LOGD_DEFAULT = LOGD_ALL & ~( + LOGD_DBUS_PROPS | + LOGD_WIFI_SCAN | + LOGD_VPN_PLUGIN | + 0), + + /* aliases: */ + LOGD_DHCP = LOGD_DHCP4 | LOGD_DHCP6, + LOGD_IP = LOGD_IP4 | LOGD_IP6, +} NMLogDomain; + +/* Log levels */ +typedef enum { /*< skip >*/ + LOGL_TRACE, + LOGL_DEBUG, + LOGL_INFO, + LOGL_WARN, + LOGL_ERR, + + _LOGL_N_REAL, /* the number of actual logging levels */ + + _LOGL_OFF = _LOGL_N_REAL, /* special logging level that is always disabled. */ + _LOGL_KEEP, /* special logging level to indicate that the logging level should not be changed. */ + + _LOGL_N, /* the number of logging levels including "OFF" */ +} NMLogLevel; + +gboolean _nm_log_enabled_impl (gboolean mt_require_locking, + NMLogLevel level, + NMLogDomain domain); + +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 *con_uuid, + const char *fmt, + ...) _nm_printf (10, 11); + +#endif /* __NM_LOGGING_DEFINES_H__ */ diff --git a/shared/nm-glib-aux/nm-macros-internal.h b/shared/nm-glib-aux/nm-macros-internal.h new file mode 100644 index 00000000..2e46cd2d --- /dev/null +++ b/shared/nm-glib-aux/nm-macros-internal.h @@ -0,0 +1,1855 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* NetworkManager -- Network link manager + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA. + * + * (C) Copyright 2012 Colin Walters . + * (C) Copyright 2014 Red Hat, Inc. + */ + +#ifndef __NM_MACROS_INTERNAL_H__ +#define __NM_MACROS_INTERNAL_H__ + +#include +#include +#include +#include + +#include + +/*****************************************************************************/ + +#define _nm_packed __attribute__ ((__packed__)) +#define _nm_unused __attribute__ ((__unused__)) +#define _nm_used __attribute__ ((__used__)) +#define _nm_pure __attribute__ ((__pure__)) +#define _nm_const __attribute__ ((__const__)) +#define _nm_printf(a,b) __attribute__ ((__format__ (__printf__, a, b))) +#define _nm_align(s) __attribute__ ((__aligned__ (s))) +#define _nm_section(s) __attribute__ ((__section__ (s))) +#define _nm_alignof(type) __alignof (type) +#define _nm_alignas(type) _nm_align (_nm_alignof (type)) +#define nm_auto(fcn) __attribute__ ((__cleanup__(fcn))) + + +/* This is required to make LTO working. + * + * See https://gitlab.freedesktop.org/NetworkManager/NetworkManager/merge_requests/76#note_112694 + * https://gcc.gnu.org/bugzilla/show_bug.cgi?id=48200#c28 + */ +#ifndef __clang__ +#define _nm_externally_visible __attribute__ ((__externally_visible__)) +#else +#define _nm_externally_visible +#endif + + +#if __GNUC__ >= 7 +#define _nm_fallthrough __attribute__ ((__fallthrough__)) +#else +#define _nm_fallthrough +#endif + +/*****************************************************************************/ + +#ifdef thread_local +#define _nm_thread_local thread_local +/* + * Don't break on glibc < 2.16 that doesn't define __STDC_NO_THREADS__ + * see http://gcc.gnu.org/bugzilla/show_bug.cgi?id=53769 + */ +#elif __STDC_VERSION__ >= 201112L && !(defined(__STDC_NO_THREADS__) || (defined(__GNU_LIBRARY__) && __GLIBC__ == 2 && __GLIBC_MINOR__ < 16)) +#define _nm_thread_local _Thread_local +#else +#define _nm_thread_local __thread +#endif + +/*****************************************************************************/ + +/* most of our code is single-threaded with a mainloop. Hence, we usually don't need + * any thread-safety. Sometimes, we do need thread-safety (nm-logging), but we can + * avoid locking if we are on the main-thread by: + * + * - modifications of shared data is done infrequently and only from the + * main-thread (nm_logging_setup()) + * - read-only access is done frequently (nm_logging_enabled()) + * - from the main-thread, we can do that without locking (because + * all modifications are also done on the main thread. + * - from other threads, we need locking. But this is expected to be + * done infrequently too. Important is the lock-free fast-path on the + * main-thread. + * + * By defining NM_THREAD_SAFE_ON_MAIN_THREAD you indicate that this code runs + * on the main-thread. It is by default defined to "1". If you have code that + * is also used on another thread, redefine the define to 0 (to opt in into + * the slow-path). + */ +#define NM_THREAD_SAFE_ON_MAIN_THREAD 1 + +/*****************************************************************************/ + +#define NM_AUTO_DEFINE_FCN_VOID(CastType, name, func) \ +static inline void name (void *v) \ +{ \ + func (*((CastType *) v)); \ +} + +#define NM_AUTO_DEFINE_FCN_VOID0(CastType, name, func) \ +static inline void name (void *v) \ +{ \ + if (*((CastType *) v)) \ + func (*((CastType *) v)); \ +} + +#define NM_AUTO_DEFINE_FCN(Type, name, func) \ +static inline void name (Type *v) \ +{ \ + func (*v); \ +} + +#define NM_AUTO_DEFINE_FCN0(Type, name, func) \ +static inline void name (Type *v) \ +{ \ + if (*v) \ + func (*v); \ +} + +/*****************************************************************************/ + +/** + * gs_free: + * + * Call g_free() on a variable location when it goes out of scope. + */ +#define gs_free nm_auto(gs_local_free) +NM_AUTO_DEFINE_FCN_VOID0 (void *, gs_local_free, g_free) + +/** + * gs_unref_object: + * + * Call g_object_unref() on a variable location when it goes out of + * scope. Note that unlike g_object_unref(), the variable may be + * %NULL. + */ +#define gs_unref_object nm_auto(gs_local_obj_unref) +NM_AUTO_DEFINE_FCN_VOID0 (GObject *, gs_local_obj_unref, g_object_unref) + +/** + * gs_unref_variant: + * + * Call g_variant_unref() on a variable location when it goes out of + * scope. Note that unlike g_variant_unref(), the variable may be + * %NULL. + */ +#define gs_unref_variant nm_auto(gs_local_variant_unref) +NM_AUTO_DEFINE_FCN0 (GVariant *, gs_local_variant_unref, g_variant_unref) + +/** + * gs_unref_array: + * + * Call g_array_unref() on a variable location when it goes out of + * scope. Note that unlike g_array_unref(), the variable may be + * %NULL. + + */ +#define gs_unref_array nm_auto(gs_local_array_unref) +NM_AUTO_DEFINE_FCN0 (GArray *, gs_local_array_unref, g_array_unref) + +/** + * gs_unref_ptrarray: + * + * Call g_ptr_array_unref() on a variable location when it goes out of + * scope. Note that unlike g_ptr_array_unref(), the variable may be + * %NULL. + + */ +#define gs_unref_ptrarray nm_auto(gs_local_ptrarray_unref) +NM_AUTO_DEFINE_FCN0 (GPtrArray *, gs_local_ptrarray_unref, g_ptr_array_unref) + +/** + * gs_unref_hashtable: + * + * Call g_hash_table_unref() on a variable location when it goes out + * of scope. Note that unlike g_hash_table_unref(), the variable may + * be %NULL. + */ +#define gs_unref_hashtable nm_auto(gs_local_hashtable_unref) +NM_AUTO_DEFINE_FCN0 (GHashTable *, gs_local_hashtable_unref, g_hash_table_unref) + +/** + * gs_free_slist: + * + * Call g_slist_free() on a variable location when it goes out + * of scope. + */ +#define gs_free_slist nm_auto(gs_local_free_slist) +NM_AUTO_DEFINE_FCN0 (GSList *, gs_local_free_slist, g_slist_free) + +/** + * gs_unref_bytes: + * + * Call g_bytes_unref() on a variable location when it goes out + * of scope. Note that unlike g_bytes_unref(), the variable may + * be %NULL. + */ +#define gs_unref_bytes nm_auto(gs_local_bytes_unref) +NM_AUTO_DEFINE_FCN0 (GBytes *, gs_local_bytes_unref, g_bytes_unref) + +/** + * gs_strfreev: + * + * Call g_strfreev() on a variable location when it goes out of scope. + */ +#define gs_strfreev nm_auto(gs_local_strfreev) +NM_AUTO_DEFINE_FCN0 (char **, gs_local_strfreev, g_strfreev) + +/** + * gs_free_error: + * + * Call g_error_free() on a variable location when it goes out of scope. + */ +#define gs_free_error nm_auto(gs_local_free_error) +NM_AUTO_DEFINE_FCN0 (GError *, gs_local_free_error, g_error_free) + +/** + * gs_unref_keyfile: + * + * Call g_key_file_unref() on a variable location when it goes out of scope. + */ +#define gs_unref_keyfile nm_auto(gs_local_keyfile_unref) +NM_AUTO_DEFINE_FCN0 (GKeyFile *, gs_local_keyfile_unref, g_key_file_unref) + +/*****************************************************************************/ + +#include "nm-glib.h" + +/*****************************************************************************/ + +#define nm_offsetofend(t,m) (G_STRUCT_OFFSET (t,m) + sizeof (((t *) NULL)->m)) + +/*****************************************************************************/ + +static inline int nm_close (int fd); + +/** + * nm_auto_free: + * + * Call free() on a variable location when it goes out of scope. + * This is for pointers that are allocated with malloc() instead of + * g_malloc(). + * + * In practice, since glib 2.45, g_malloc()/g_free() always wraps malloc()/free(). + * See bgo#751592. In that case, it would be safe to free pointers allocated with + * malloc() with gs_free or g_free(). + * + * However, let's never mix them. To free malloc'ed memory, always use + * free() or nm_auto_free. + */ +NM_AUTO_DEFINE_FCN_VOID0 (void *, _nm_auto_free_impl, free) +#define nm_auto_free nm_auto(_nm_auto_free_impl) + +NM_AUTO_DEFINE_FCN0 (GVariantIter *, _nm_auto_free_variant_iter, g_variant_iter_free) +#define nm_auto_free_variant_iter nm_auto(_nm_auto_free_variant_iter) + +NM_AUTO_DEFINE_FCN0 (GVariantBuilder *, _nm_auto_unref_variant_builder, g_variant_builder_unref) +#define nm_auto_unref_variant_builder nm_auto(_nm_auto_unref_variant_builder) + +#define nm_auto_clear_variant_builder nm_auto(g_variant_builder_clear) + +NM_AUTO_DEFINE_FCN0 (GList *, _nm_auto_free_list, g_list_free) +#define nm_auto_free_list nm_auto(_nm_auto_free_list) + +NM_AUTO_DEFINE_FCN0 (GChecksum *, _nm_auto_checksum_free, g_checksum_free) +#define nm_auto_free_checksum nm_auto(_nm_auto_checksum_free) + +#define nm_auto_unset_gvalue nm_auto(g_value_unset) + +NM_AUTO_DEFINE_FCN_VOID0 (void *, _nm_auto_unref_gtypeclass, g_type_class_unref) +#define nm_auto_unref_gtypeclass nm_auto(_nm_auto_unref_gtypeclass) + +NM_AUTO_DEFINE_FCN0 (GByteArray *, _nm_auto_unref_bytearray, g_byte_array_unref) +#define nm_auto_unref_bytearray nm_auto(_nm_auto_unref_bytearray) + +static inline void +_nm_auto_free_gstring (GString **str) +{ + if (*str) + g_string_free (*str, TRUE); +} +#define nm_auto_free_gstring nm_auto(_nm_auto_free_gstring) + +static inline void +_nm_auto_close (int *pfd) +{ + if (*pfd >= 0) { + int errsv = errno; + + (void) nm_close (*pfd); + errno = errsv; + } +} +#define nm_auto_close nm_auto(_nm_auto_close) + +static inline void +_nm_auto_fclose (FILE **pfd) +{ + if (*pfd) { + int errsv = errno; + + (void) fclose (*pfd); + errno = errsv; + } +} +#define nm_auto_fclose nm_auto(_nm_auto_fclose) + +static inline void +_nm_auto_protect_errno (int *p_saved_errno) +{ + errno = *p_saved_errno; +} +#define NM_AUTO_PROTECT_ERRNO(errsv_saved) nm_auto(_nm_auto_protect_errno) _nm_unused const int errsv_saved = (errno) + +NM_AUTO_DEFINE_FCN0 (GSource *, _nm_auto_unref_gsource, g_source_unref); +#define nm_auto_unref_gsource nm_auto(_nm_auto_unref_gsource) + +NM_AUTO_DEFINE_FCN0 (GMainLoop *, _nm_auto_unref_gmainloop, g_main_loop_unref); +#define nm_auto_unref_gmainloop nm_auto(_nm_auto_unref_gmainloop) + +static inline void +_nm_auto_freev (gpointer ptr) +{ + gpointer **p = ptr; + gpointer *_ptr; + + if (*p) { + for (_ptr = *p; *_ptr; _ptr++) + g_free (*_ptr); + g_free (*p); + } +} +/* g_free a NULL terminated array of pointers, with also freeing each + * pointer with g_free(). It essentially does the same as + * gs_strfreev / g_strfreev(), but not restricted to strv arrays. */ +#define nm_auto_freev nm_auto(_nm_auto_freev) + +/*****************************************************************************/ + +/* http://stackoverflow.com/a/11172679 */ +#define _NM_UTILS_MACRO_FIRST(...) __NM_UTILS_MACRO_FIRST_HELPER(__VA_ARGS__, throwaway) +#define __NM_UTILS_MACRO_FIRST_HELPER(first, ...) first + +#define _NM_UTILS_MACRO_REST(...) __NM_UTILS_MACRO_REST_HELPER(__NM_UTILS_MACRO_REST_NUM(__VA_ARGS__), __VA_ARGS__) +#define __NM_UTILS_MACRO_REST_HELPER(qty, ...) __NM_UTILS_MACRO_REST_HELPER2(qty, __VA_ARGS__) +#define __NM_UTILS_MACRO_REST_HELPER2(qty, ...) __NM_UTILS_MACRO_REST_HELPER_##qty(__VA_ARGS__) +#define __NM_UTILS_MACRO_REST_HELPER_ONE(first) +#define __NM_UTILS_MACRO_REST_HELPER_TWOORMORE(first, ...) , __VA_ARGS__ +#define __NM_UTILS_MACRO_REST_NUM(...) \ + __NM_UTILS_MACRO_REST_SELECT_30TH(__VA_ARGS__, \ + TWOORMORE, TWOORMORE, TWOORMORE, TWOORMORE, TWOORMORE,\ + TWOORMORE, TWOORMORE, TWOORMORE, TWOORMORE, TWOORMORE,\ + TWOORMORE, TWOORMORE, TWOORMORE, TWOORMORE, TWOORMORE,\ + TWOORMORE, TWOORMORE, TWOORMORE, TWOORMORE, TWOORMORE,\ + TWOORMORE, TWOORMORE, TWOORMORE, TWOORMORE, TWOORMORE,\ + TWOORMORE, TWOORMORE, TWOORMORE, ONE, throwaway) +#define __NM_UTILS_MACRO_REST_SELECT_30TH(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20, a21, a22, a23, a24, a25, a26, a27, a28, a29, a30, ...) a30 + +/*****************************************************************************/ + +/* http://stackoverflow.com/a/2124385/354393 + * https://stackoverflow.com/questions/11317474/macro-to-count-number-of-arguments + */ + +#define NM_NARG(...) \ + _NM_NARG(, ##__VA_ARGS__, _NM_NARG_RSEQ_N()) +#define _NM_NARG(...) \ + _NM_NARG_ARG_N(__VA_ARGS__) +#define _NM_NARG_ARG_N( \ + _0, \ + _1, _2, _3, _4, _5, _6, _7, _8, _9,_10, \ + _11,_12,_13,_14,_15,_16,_17,_18,_19,_20, \ + _21,_22,_23,_24,_25,_26,_27,_28,_29,_30, \ + _31,_32,_33,_34,_35,_36,_37,_38,_39,_40, \ + _41,_42,_43,_44,_45,_46,_47,_48,_49,_50, \ + _51,_52,_53,_54,_55,_56,_57,_58,_59,_60, \ + _61,_62,_63,N,...) N +#define _NM_NARG_RSEQ_N() \ + 63,62,61,60, \ + 59,58,57,56,55,54,53,52,51,50, \ + 49,48,47,46,45,44,43,42,41,40, \ + 39,38,37,36,35,34,33,32,31,30, \ + 29,28,27,26,25,24,23,22,21,20, \ + 19,18,17,16,15,14,13,12,11,10, \ + 9,8,7,6,5,4,3,2,1,0 + +/*****************************************************************************/ + +#if defined (__GNUC__) +#define _NM_PRAGMA_WARNING_DO(warning) G_STRINGIFY(GCC diagnostic ignored warning) +#elif defined (__clang__) +#define _NM_PRAGMA_WARNING_DO(warning) G_STRINGIFY(clang diagnostic ignored warning) +#endif + +/* you can only suppress a specific warning that the compiler + * understands. Otherwise you will get another compiler warning + * about invalid pragma option. + * It's not that bad however, because gcc and clang often have the + * same name for the same warning. */ + +#if defined (__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6)) +#define NM_PRAGMA_WARNING_DISABLE(warning) \ + _Pragma("GCC diagnostic push") \ + _Pragma(_NM_PRAGMA_WARNING_DO(warning)) +#elif defined (__clang__) +#define NM_PRAGMA_WARNING_DISABLE(warning) \ + _Pragma("clang diagnostic push") \ + _Pragma(_NM_PRAGMA_WARNING_DO("-Wunknown-warning-option")) \ + _Pragma(_NM_PRAGMA_WARNING_DO(warning)) +#else +#define NM_PRAGMA_WARNING_DISABLE(warning) +#endif + +#if defined (__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6)) +#define NM_PRAGMA_WARNING_REENABLE \ + _Pragma("GCC diagnostic pop") +#elif defined (__clang__) +#define NM_PRAGMA_WARNING_REENABLE \ + _Pragma("clang diagnostic pop") +#else +#define NM_PRAGMA_WARNING_REENABLE +#endif + +/*****************************************************************************/ + +/** + * NM_G_ERROR_MSG: + * @error: (allow-none): the #GError instance + * + * All functions must follow the convention that when they + * return a failure, they must also set the GError to a valid + * message. For external API however, we want to be extra + * careful before accessing the error instance. Use NM_G_ERROR_MSG() + * which is safe to use on NULL. + * + * Returns: the error message. + **/ +static inline const char * +NM_G_ERROR_MSG (GError *error) +{ + return error ? (error->message ?: "(null)") : "(no-error)"; \ +} + +/*****************************************************************************/ + +/* macro to return strlen() of a compile time string. */ +#define NM_STRLEN(str) ( sizeof (""str"") - 1 ) + +/* returns the length of a NULL terminated array of pointers, + * like g_strv_length() does. The difference is: + * - it operats on arrays of pointers (of any kind, requiring no cast). + * - it accepts NULL to return zero. */ +#define NM_PTRARRAY_LEN(array) \ + ({ \ + typeof (*(array)) *const _array = (array); \ + gsize _n = 0; \ + \ + if (_array) { \ + _nm_unused gconstpointer _type_check_is_pointer = _array[0]; \ + \ + while (_array[_n]) \ + _n++; \ + } \ + _n; \ + }) + +/* Note: @value is only evaluated when *out_val is present. + * Thus, + * NM_SET_OUT (out_str, g_strdup ("hallo")); + * does the right thing. + */ +#define NM_SET_OUT(out_val, value) \ + G_STMT_START { \ + typeof(*(out_val)) *_out_val = (out_val); \ + \ + if (_out_val) { \ + *_out_val = (value); \ + } \ + } G_STMT_END + +/*****************************************************************************/ + +#ifndef _NM_CC_SUPPORT_AUTO_TYPE +#if (defined (__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 9 ))) +#define _NM_CC_SUPPORT_AUTO_TYPE 1 +#else +#define _NM_CC_SUPPORT_AUTO_TYPE 0 +#endif +#endif + +#ifndef _NM_CC_SUPPORT_GENERIC +/* In the meantime, NetworkManager requires C11 and _Generic() should always be available. + * However, shared/nm-utils may also be used in VPN/applet, which possibly did not yet + * bump the C standard requirement. Leave this for the moment, but eventually we can + * drop it. */ +#if (defined (__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 9 ))) || (defined (__clang__)) +#define _NM_CC_SUPPORT_GENERIC 1 +#else +#define _NM_CC_SUPPORT_GENERIC 0 +#endif +#endif + +#if _NM_CC_SUPPORT_AUTO_TYPE +#define _nm_auto_type __auto_type +#endif + +#if _NM_CC_SUPPORT_GENERIC +#define _NM_CONSTCAST_FULL_1(type, obj_expr, obj) \ + (_Generic ((obj_expr), \ + const void *const: ((const type *) (obj)), \ + const void * : ((const type *) (obj)), \ + void *const: (( type *) (obj)), \ + void * : (( type *) (obj)), \ + const type *const: ((const type *) (obj)), \ + const type * : ((const type *) (obj)), \ + type *const: (( type *) (obj)), \ + type * : (( type *) (obj)))) +#define _NM_CONSTCAST_FULL_2(type, obj_expr, obj, alias_type2) \ + (_Generic ((obj_expr), \ + const void *const: ((const type *) (obj)), \ + const void * : ((const type *) (obj)), \ + void *const: (( type *) (obj)), \ + void * : (( type *) (obj)), \ + const alias_type2 *const: ((const type *) (obj)), \ + const alias_type2 * : ((const type *) (obj)), \ + alias_type2 *const: (( type *) (obj)), \ + alias_type2 * : (( type *) (obj)), \ + const type *const: ((const type *) (obj)), \ + const type * : ((const type *) (obj)), \ + type *const: (( type *) (obj)), \ + type * : (( type *) (obj)))) +#define _NM_CONSTCAST_FULL_3(type, obj_expr, obj, alias_type2, alias_type3) \ + (_Generic ((obj_expr), \ + const void *const: ((const type *) (obj)), \ + const void * : ((const type *) (obj)), \ + void *const: (( type *) (obj)), \ + void * : (( type *) (obj)), \ + const alias_type2 *const: ((const type *) (obj)), \ + const alias_type2 * : ((const type *) (obj)), \ + alias_type2 *const: (( type *) (obj)), \ + alias_type2 * : (( type *) (obj)), \ + const alias_type3 *const: ((const type *) (obj)), \ + const alias_type3 * : ((const type *) (obj)), \ + alias_type3 *const: (( type *) (obj)), \ + alias_type3 * : (( type *) (obj)), \ + const type *const: ((const type *) (obj)), \ + const type * : ((const type *) (obj)), \ + type *const: (( type *) (obj)), \ + type * : (( type *) (obj)))) +#define _NM_CONSTCAST_FULL_4(type, obj_expr, obj, alias_type2, alias_type3, alias_type4) \ + (_Generic ((obj_expr), \ + const void *const: ((const type *) (obj)), \ + const void * : ((const type *) (obj)), \ + void *const: (( type *) (obj)), \ + void * : (( type *) (obj)), \ + const alias_type2 *const: ((const type *) (obj)), \ + const alias_type2 * : ((const type *) (obj)), \ + alias_type2 *const: (( type *) (obj)), \ + alias_type2 * : (( type *) (obj)), \ + const alias_type3 *const: ((const type *) (obj)), \ + const alias_type3 * : ((const type *) (obj)), \ + alias_type3 *const: (( type *) (obj)), \ + alias_type3 * : (( type *) (obj)), \ + const alias_type4 *const: ((const type *) (obj)), \ + const alias_type4 * : ((const type *) (obj)), \ + alias_type4 *const: (( type *) (obj)), \ + alias_type4 * : (( type *) (obj)), \ + const type *const: ((const type *) (obj)), \ + const type * : ((const type *) (obj)), \ + type *const: (( type *) (obj)), \ + type * : (( type *) (obj)))) +#define _NM_CONSTCAST_FULL_x(type, obj_expr, obj, n, ...) (_NM_CONSTCAST_FULL_##n (type, obj_expr, obj, ##__VA_ARGS__)) +#define _NM_CONSTCAST_FULL_y(type, obj_expr, obj, n, ...) (_NM_CONSTCAST_FULL_x (type, obj_expr, obj, n, ##__VA_ARGS__)) +#define NM_CONSTCAST_FULL( type, obj_expr, obj, ...) (_NM_CONSTCAST_FULL_y (type, obj_expr, obj, NM_NARG (dummy, ##__VA_ARGS__), ##__VA_ARGS__)) +#else +#define NM_CONSTCAST_FULL( type, obj_expr, obj, ...) ((type *) (obj)) +#endif + +#define NM_CONSTCAST(type, obj, ...) \ + NM_CONSTCAST_FULL(type, (obj), (obj), ##__VA_ARGS__) + +#if _NM_CC_SUPPORT_GENERIC +#define NM_UNCONST_PTR(type, arg) \ + _Generic ((arg), \ + const type *: ((type *) (arg)), \ + type *: ((type *) (arg))) +#else +#define NM_UNCONST_PTR(type, arg) \ + ((type *) (arg)) +#endif + +#if _NM_CC_SUPPORT_GENERIC +#define NM_UNCONST_PPTR(type, arg) \ + _Generic ((arg), \ + const type * *: ((type **) (arg)), \ + type * *: ((type **) (arg)), \ + const type *const*: ((type **) (arg)), \ + type *const*: ((type **) (arg))) +#else +#define NM_UNCONST_PPTR(type, arg) \ + ((type **) (arg)) +#endif + +#define NM_GOBJECT_CAST(type, obj, is_check, ...) \ + ({ \ + const void *_obj = (obj); \ + \ + nm_assert (_obj || (is_check (_obj))); \ + NM_CONSTCAST_FULL (type, (obj), _obj, GObject, ##__VA_ARGS__); \ + }) + +#define NM_GOBJECT_CAST_NON_NULL(type, obj, is_check, ...) \ + ({ \ + const void *_obj = (obj); \ + \ + nm_assert (is_check (_obj)); \ + NM_CONSTCAST_FULL (type, (obj), _obj, GObject, ##__VA_ARGS__); \ + }) + +#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. */ +#define _NM_ENSURE_TYPE(type, value) (_Generic ((value), type: (value))) +#else +#define _NM_ENSURE_TYPE(type, value) (value) +#endif + +#if _NM_CC_SUPPORT_GENERIC +/* these macros cast (value) to + * - "const char **" (for "MC", mutable-const) + * - "const char *const*" (for "CC", const-const) + * The point is to do this cast, but only accepting pointers + * that are compatible already. + * + * The problem is, if you add a function like g_strdupv(), the input + * argument is not modified (CC), but you want to make it work also + * for "char **". C doesn't allow this form of casting (for good reasons), + * so the function makes a choice like g_strdupv(char**). That means, + * every time you want to call it with a const argument, you need to + * explicitly cast it. + * + * These macros do the cast, but they only accept a compatible input + * type, otherwise they will fail compilation. + */ +#define NM_CAST_STRV_MC(value) \ + (_Generic ((value), \ + const char * *: (const char * *) (value), \ + char * *: (const char * *) (value), \ + void *: (const char * *) (value))) +#define NM_CAST_STRV_CC(value) \ + (_Generic ((value), \ + const char *const*: (const char *const*) (value), \ + const char * *: (const char *const*) (value), \ + char *const*: (const char *const*) (value), \ + char * *: (const char *const*) (value), \ + const void *: (const char *const*) (value), \ + void *: (const char *const*) (value))) +#else +#define NM_CAST_STRV_MC(value) ((const char * *) (value)) +#define NM_CAST_STRV_CC(value) ((const char *const*) (value)) +#endif + +#if _NM_CC_SUPPORT_GENERIC +#define NM_PROPAGATE_CONST(test_expr, ptr) \ + (_Generic ((test_expr), \ + const typeof (*(test_expr)) *: ((const typeof (*(ptr)) *) (ptr)), \ + default: (_Generic ((test_expr), \ + typeof (*(test_expr)) *: (ptr))))) +#else +#define NM_PROPAGATE_CONST(test_expr, ptr) (ptr) +#endif + +/* with the way it is implemented, the caller may or may not pass a trailing + * ',' and it will work. However, this makes the macro unsuitable for initializing + * an array. */ +#define NM_MAKE_STRV(...) \ + ((const char *const[(sizeof (((const char *const[]) { __VA_ARGS__ })) / sizeof (const char *)) + 1]) { __VA_ARGS__ }) + +/*****************************************************************************/ + +#define _NM_IN_SET_EVAL_1( op, _x, y) (_x == (y)) +#define _NM_IN_SET_EVAL_2( op, _x, y, ...) (_x == (y)) op _NM_IN_SET_EVAL_1 (op, _x, __VA_ARGS__) +#define _NM_IN_SET_EVAL_3( op, _x, y, ...) (_x == (y)) op _NM_IN_SET_EVAL_2 (op, _x, __VA_ARGS__) +#define _NM_IN_SET_EVAL_4( op, _x, y, ...) (_x == (y)) op _NM_IN_SET_EVAL_3 (op, _x, __VA_ARGS__) +#define _NM_IN_SET_EVAL_5( op, _x, y, ...) (_x == (y)) op _NM_IN_SET_EVAL_4 (op, _x, __VA_ARGS__) +#define _NM_IN_SET_EVAL_6( op, _x, y, ...) (_x == (y)) op _NM_IN_SET_EVAL_5 (op, _x, __VA_ARGS__) +#define _NM_IN_SET_EVAL_7( op, _x, y, ...) (_x == (y)) op _NM_IN_SET_EVAL_6 (op, _x, __VA_ARGS__) +#define _NM_IN_SET_EVAL_8( op, _x, y, ...) (_x == (y)) op _NM_IN_SET_EVAL_7 (op, _x, __VA_ARGS__) +#define _NM_IN_SET_EVAL_9( op, _x, y, ...) (_x == (y)) op _NM_IN_SET_EVAL_8 (op, _x, __VA_ARGS__) +#define _NM_IN_SET_EVAL_10(op, _x, y, ...) (_x == (y)) op _NM_IN_SET_EVAL_9 (op, _x, __VA_ARGS__) +#define _NM_IN_SET_EVAL_11(op, _x, y, ...) (_x == (y)) op _NM_IN_SET_EVAL_10 (op, _x, __VA_ARGS__) +#define _NM_IN_SET_EVAL_12(op, _x, y, ...) (_x == (y)) op _NM_IN_SET_EVAL_11 (op, _x, __VA_ARGS__) +#define _NM_IN_SET_EVAL_13(op, _x, y, ...) (_x == (y)) op _NM_IN_SET_EVAL_12 (op, _x, __VA_ARGS__) +#define _NM_IN_SET_EVAL_14(op, _x, y, ...) (_x == (y)) op _NM_IN_SET_EVAL_13 (op, _x, __VA_ARGS__) +#define _NM_IN_SET_EVAL_15(op, _x, y, ...) (_x == (y)) op _NM_IN_SET_EVAL_14 (op, _x, __VA_ARGS__) +#define _NM_IN_SET_EVAL_16(op, _x, y, ...) (_x == (y)) op _NM_IN_SET_EVAL_15 (op, _x, __VA_ARGS__) + +#define _NM_IN_SET_EVAL_N2(op, _x, n, ...) (_NM_IN_SET_EVAL_##n(op, _x, __VA_ARGS__)) +#define _NM_IN_SET_EVAL_N(op, type, x, n, ...) \ + ({ \ + type _x = (x); \ + \ + /* trigger a -Wenum-compare warning */ \ + nm_assert (TRUE || _x == (x)); \ + \ + !!_NM_IN_SET_EVAL_N2(op, _x, n, __VA_ARGS__); \ + }) + +#define _NM_IN_SET(op, type, x, ...) _NM_IN_SET_EVAL_N(op, type, x, NM_NARG (__VA_ARGS__), __VA_ARGS__) + +/* Beware that this does short-circuit evaluation (use "||" instead of "|") + * which has a possibly unexpected non-function-like behavior. + * Use NM_IN_SET_SE if you need all arguments to be evaluated. */ +#define NM_IN_SET(x, ...) _NM_IN_SET(||, typeof (x), x, __VA_ARGS__) + +/* "SE" stands for "side-effect". Contrary to NM_IN_SET(), this does not do + * short-circuit evaluation, which can make a difference if the arguments have + * side-effects. */ +#define NM_IN_SET_SE(x, ...) _NM_IN_SET(|, typeof (x), x, __VA_ARGS__) + +/* the *_TYPED forms allow to explicitly select the type of "x". This is useful + * if "x" doesn't support typeof (bitfields) or you want to gracefully convert + * a type using automatic type conversion rules (but not forcing the conversion + * with a cast). */ +#define NM_IN_SET_TYPED(type, x, ...) _NM_IN_SET(||, type, x, __VA_ARGS__) +#define NM_IN_SET_SE_TYPED(type, x, ...) _NM_IN_SET(|, type, x, __VA_ARGS__) + +/*****************************************************************************/ + +static inline gboolean +_NM_IN_STRSET_streq (const char *x, const char *s) +{ + return s && strcmp (x, s) == 0; +} + +#define _NM_IN_STRSET_EVAL_1( op, _x, y) _NM_IN_STRSET_streq (_x, y) +#define _NM_IN_STRSET_EVAL_2( op, _x, y, ...) _NM_IN_STRSET_streq (_x, y) op _NM_IN_STRSET_EVAL_1 (op, _x, __VA_ARGS__) +#define _NM_IN_STRSET_EVAL_3( op, _x, y, ...) _NM_IN_STRSET_streq (_x, y) op _NM_IN_STRSET_EVAL_2 (op, _x, __VA_ARGS__) +#define _NM_IN_STRSET_EVAL_4( op, _x, y, ...) _NM_IN_STRSET_streq (_x, y) op _NM_IN_STRSET_EVAL_3 (op, _x, __VA_ARGS__) +#define _NM_IN_STRSET_EVAL_5( op, _x, y, ...) _NM_IN_STRSET_streq (_x, y) op _NM_IN_STRSET_EVAL_4 (op, _x, __VA_ARGS__) +#define _NM_IN_STRSET_EVAL_6( op, _x, y, ...) _NM_IN_STRSET_streq (_x, y) op _NM_IN_STRSET_EVAL_5 (op, _x, __VA_ARGS__) +#define _NM_IN_STRSET_EVAL_7( op, _x, y, ...) _NM_IN_STRSET_streq (_x, y) op _NM_IN_STRSET_EVAL_6 (op, _x, __VA_ARGS__) +#define _NM_IN_STRSET_EVAL_8( op, _x, y, ...) _NM_IN_STRSET_streq (_x, y) op _NM_IN_STRSET_EVAL_7 (op, _x, __VA_ARGS__) +#define _NM_IN_STRSET_EVAL_9( op, _x, y, ...) _NM_IN_STRSET_streq (_x, y) op _NM_IN_STRSET_EVAL_8 (op, _x, __VA_ARGS__) +#define _NM_IN_STRSET_EVAL_10(op, _x, y, ...) _NM_IN_STRSET_streq (_x, y) op _NM_IN_STRSET_EVAL_9 (op, _x, __VA_ARGS__) +#define _NM_IN_STRSET_EVAL_11(op, _x, y, ...) _NM_IN_STRSET_streq (_x, y) op _NM_IN_STRSET_EVAL_10 (op, _x, __VA_ARGS__) +#define _NM_IN_STRSET_EVAL_12(op, _x, y, ...) _NM_IN_STRSET_streq (_x, y) op _NM_IN_STRSET_EVAL_11 (op, _x, __VA_ARGS__) +#define _NM_IN_STRSET_EVAL_13(op, _x, y, ...) _NM_IN_STRSET_streq (_x, y) op _NM_IN_STRSET_EVAL_12 (op, _x, __VA_ARGS__) +#define _NM_IN_STRSET_EVAL_14(op, _x, y, ...) _NM_IN_STRSET_streq (_x, y) op _NM_IN_STRSET_EVAL_13 (op, _x, __VA_ARGS__) +#define _NM_IN_STRSET_EVAL_15(op, _x, y, ...) _NM_IN_STRSET_streq (_x, y) op _NM_IN_STRSET_EVAL_14 (op, _x, __VA_ARGS__) +#define _NM_IN_STRSET_EVAL_16(op, _x, y, ...) _NM_IN_STRSET_streq (_x, y) op _NM_IN_STRSET_EVAL_15 (op, _x, __VA_ARGS__) + +#define _NM_IN_STRSET_EVAL_N2(op, _x, n, ...) (_NM_IN_STRSET_EVAL_##n(op, _x, __VA_ARGS__)) +#define _NM_IN_STRSET_EVAL_N(op, x, n, ...) \ + ({ \ + const char *_x = (x); \ + ( ((_x == NULL) && _NM_IN_SET_EVAL_N2 (op, ((const char *) NULL), n, __VA_ARGS__)) \ + || ((_x != NULL) && _NM_IN_STRSET_EVAL_N2 (op, _x, n, __VA_ARGS__)) \ + ); \ + }) + +/* Beware that this does short-circuit evaluation (use "||" instead of "|") + * which has a possibly unexpected non-function-like behavior. + * Use NM_IN_STRSET_SE if you need all arguments to be evaluated. */ +#define NM_IN_STRSET(x, ...) _NM_IN_STRSET_EVAL_N(||, x, NM_NARG (__VA_ARGS__), __VA_ARGS__) + +/* "SE" stands for "side-effect". Contrary to NM_IN_STRSET(), this does not do + * short-circuit evaluation, which can make a difference if the arguments have + * side-effects. */ +#define NM_IN_STRSET_SE(x, ...) _NM_IN_STRSET_EVAL_N(|, x, NM_NARG (__VA_ARGS__), __VA_ARGS__) + +#define NM_STRCHAR_ALL(str, ch_iter, predicate) \ + ({ \ + gboolean _val = TRUE; \ + const char *_str = (str); \ + \ + if (_str) { \ + for (;;) { \ + const char ch_iter = _str[0]; \ + \ + if (ch_iter != '\0') { \ + if (predicate) {\ + _str++; \ + continue; \ + } \ + _val = FALSE; \ + } \ + break; \ + } \ + } \ + _val; \ + }) + +#define NM_STRCHAR_ANY(str, ch_iter, predicate) \ + ({ \ + gboolean _val = FALSE; \ + const char *_str = (str); \ + \ + if (_str) { \ + for (;;) { \ + const char ch_iter = _str[0]; \ + \ + if (ch_iter != '\0') { \ + if (predicate) { \ + ; \ + } else { \ + _str++; \ + continue; \ + } \ + _val = TRUE; \ + } \ + break; \ + } \ + } \ + _val; \ + }) + +/*****************************************************************************/ + +/* NM_CACHED_QUARK() returns the GQuark for @string, but caches + * it in a static variable to speed up future lookups. + * + * @string must be a string literal. + */ +#define NM_CACHED_QUARK(string) \ + ({ \ + static GQuark _nm_cached_quark = 0; \ + \ + (G_LIKELY (_nm_cached_quark != 0) \ + ? _nm_cached_quark \ + : (_nm_cached_quark = g_quark_from_static_string (""string""))); \ + }) + +/* NM_CACHED_QUARK_FCN() is essentially the same as G_DEFINE_QUARK + * with two differences: + * - @string must be a quoted string-literal + * - @fcn must be the full function name, while G_DEFINE_QUARK() appends + * "_quark" to the function name. + * Both properties of G_DEFINE_QUARK() are non favorable, because you can no + * longer grep for string/fcn -- unless you are aware that you are searching + * for G_DEFINE_QUARK() and omit quotes / append _quark(). With NM_CACHED_QUARK_FCN(), + * ctags/cscope can locate the use of @fcn (though it doesn't recognize that + * NM_CACHED_QUARK_FCN() defines it). + */ +#define NM_CACHED_QUARK_FCN(string, fcn) \ +GQuark \ +fcn (void) \ +{ \ + return NM_CACHED_QUARK (string); \ +} + +/*****************************************************************************/ + +static inline gboolean +nm_streq (const char *s1, const char *s2) +{ + return strcmp (s1, s2) == 0; +} + +static inline gboolean +nm_streq0 (const char *s1, const char *s2) +{ + return (s1 == s2) + || (s1 && s2 && strcmp (s1, s2) == 0); +} + +#define NM_STR_HAS_PREFIX(str, prefix) \ + (strncmp ((str), ""prefix"", NM_STRLEN (prefix)) == 0) + +#define NM_STR_HAS_SUFFIX(str, suffix) \ + ({ \ + const char *_str = (str); \ + gsize _l = strlen (_str); \ + \ + ( (_l >= NM_STRLEN (suffix)) \ + && (memcmp (&_str[_l - NM_STRLEN (suffix)], \ + ""suffix"", \ + NM_STRLEN (suffix)) == 0)); \ + }) + +/*****************************************************************************/ + +static inline GString * +nm_gstring_prepare (GString **l) +{ + if (*l) + g_string_set_size (*l, 0); + else + *l = g_string_sized_new (30); + return *l; +} + +static inline GString * +nm_gstring_add_space_delimiter (GString *str) +{ + if (str->len > 0) + g_string_append_c (str, ' '); + return str; +} + +static inline const char * +nm_str_not_empty (const char *str) +{ + return str && str[0] ? str : NULL; +} + +static inline char * +nm_strdup_not_empty (const char *str) +{ + return str && str[0] ? g_strdup (str) : NULL; +} + +static inline char * +nm_str_realloc (char *str) +{ + gs_free char *s = str; + + /* Returns a new clone of @str and frees @str. The point is that @str + * possibly points to a larger chunck of memory. We want to freshly allocate + * a buffer. + * + * We could use realloc(), but that might not do anything or leave + * @str in its memory pool for chunks of a different size (bad for + * fragmentation). + * + * This is only useful when we want to keep the buffer around for a long + * time and want to re-allocate a more optimal buffer. */ + + return g_strdup (s); +} + +/*****************************************************************************/ + +#define NM_PRINT_FMT_QUOTED(cond, prefix, str, suffix, str_else) \ + (cond) ? (prefix) : "", \ + (cond) ? (str) : (str_else), \ + (cond) ? (suffix) : "" +#define NM_PRINT_FMT_QUOTE_STRING(arg) NM_PRINT_FMT_QUOTED((arg), "\"", (arg), "\"", "(null)") + +/*****************************************************************************/ + +/* glib/C provides the following kind of assertions: + * - assert() -- disable with NDEBUG + * - g_return_if_fail() -- disable with G_DISABLE_CHECKS + * - g_assert() -- disable with G_DISABLE_ASSERT + * but they are all enabled by default and usually even production builds have + * these kind of assertions enabled. It also means, that disabling assertions + * is an untested configuration, and might have bugs. + * + * Add our own assertion macro nm_assert(), which is disabled by default and must + * be explicitly enabled. They are useful for more expensive checks or checks that + * depend less on runtime conditions (that is, are generally expected to be true). */ + +#ifndef NM_MORE_ASSERTS +#define NM_MORE_ASSERTS 0 +#endif + +#if NM_MORE_ASSERTS +#define nm_assert(cond) G_STMT_START { g_assert (cond); } G_STMT_END +#define nm_assert_se(cond) G_STMT_START { if (G_LIKELY (cond)) { ; } else { g_assert (FALSE && (cond)); } } G_STMT_END +#define nm_assert_not_reached() G_STMT_START { g_assert_not_reached (); } G_STMT_END +#else +#define nm_assert(cond) G_STMT_START { if (FALSE) { if (cond) { } } } G_STMT_END +#define nm_assert_se(cond) G_STMT_START { if (G_LIKELY (cond)) { ; } } G_STMT_END +#define nm_assert_not_reached() G_STMT_START { ; } G_STMT_END +#endif + +/*****************************************************************************/ + +#define NM_GOBJECT_PROPERTIES_DEFINE_BASE(...) \ +typedef enum { \ + PROP_0, \ + __VA_ARGS__ \ + _PROPERTY_ENUMS_LAST, \ +} _PropertyEnums; \ +static GParamSpec *obj_properties[_PROPERTY_ENUMS_LAST] = { NULL, } + +#define NM_GOBJECT_PROPERTIES_DEFINE(obj_type, ...) \ +NM_GOBJECT_PROPERTIES_DEFINE_BASE (__VA_ARGS__); \ +static inline void \ +_nm_gobject_notify_together_impl (obj_type *obj, guint n, const _PropertyEnums *props) \ +{ \ + const gboolean freeze_thaw = (n > 1); \ + \ + nm_assert (G_IS_OBJECT (obj)); \ + nm_assert (n > 0); \ + \ + if (freeze_thaw) \ + g_object_freeze_notify ((GObject *) obj); \ + while (n-- > 0) { \ + const _PropertyEnums prop = *props++; \ + \ + if (prop != PROP_0) { \ + nm_assert ((gsize) prop < G_N_ELEMENTS (obj_properties)); \ + nm_assert (obj_properties[prop]); \ + g_object_notify_by_pspec ((GObject *) obj, obj_properties[prop]); \ + } \ + } \ + if (freeze_thaw) \ + g_object_thaw_notify ((GObject *) obj); \ +} \ +\ +static inline void \ +_notify (obj_type *obj, _PropertyEnums prop) \ +{ \ + _nm_gobject_notify_together_impl (obj, 1, &prop); \ +} \ + +/* invokes _notify() for all arguments (of type _PropertyEnums). Note, that if + * there are more than one prop arguments, this will involve a freeze/thaw + * of GObject property notifications. */ +#define nm_gobject_notify_together(obj, ...) \ + _nm_gobject_notify_together_impl (obj, NM_NARG (__VA_ARGS__), (const _PropertyEnums[]) { __VA_ARGS__ }) + +/*****************************************************************************/ + +#define _NM_GET_PRIVATE(self, type, is_check, ...) (&(NM_GOBJECT_CAST_NON_NULL (type, (self), is_check, ##__VA_ARGS__)->_priv)) +#if _NM_CC_SUPPORT_AUTO_TYPE +#define _NM_GET_PRIVATE_PTR(self, type, is_check, ...) \ + ({ \ + _nm_auto_type _self = NM_GOBJECT_CAST_NON_NULL (type, (self), is_check, ##__VA_ARGS__); \ + \ + NM_PROPAGATE_CONST (_self, _self->_priv); \ + }) +#else +#define _NM_GET_PRIVATE_PTR(self, type, is_check, ...) (NM_GOBJECT_CAST_NON_NULL (type, (self), is_check, ##__VA_ARGS__)->_priv) +#endif + +/*****************************************************************************/ + +static inline gpointer +nm_g_object_ref (gpointer obj) +{ + /* g_object_ref() doesn't accept NULL. */ + if (obj) + g_object_ref (obj); + return obj; +} +#define nm_g_object_ref(obj) ((typeof (obj)) nm_g_object_ref (obj)) + +static inline void +nm_g_object_unref (gpointer obj) +{ + /* g_object_unref() doesn't accept NULL. Usully, we workaround that + * by using g_clear_object(), but sometimes that is not convenient + * (for example as as destroy function for a hash table that can contain + * NULL values). */ + if (obj) + g_object_unref (obj); +} + +/* Assigns GObject @obj to destination @pp, and takes an additional ref. + * The previous value of @pp is unrefed. + * + * It makes sure to first increase the ref-count of @obj, and handles %NULL + * @obj correctly. + * */ +#define nm_g_object_ref_set(pp, obj) \ + ({ \ + typeof (*(pp)) *const _pp = (pp); \ + typeof (*_pp) const _obj = (obj); \ + typeof (*_pp) _p; \ + gboolean _changed = FALSE; \ + \ + nm_assert (!_pp || !*_pp || G_IS_OBJECT (*_pp)); \ + nm_assert (!_obj || G_IS_OBJECT (_obj)); \ + \ + if ( _pp \ + && ((_p = *_pp) != _obj)) { \ + nm_g_object_ref (_obj); \ + *_pp = _obj; \ + nm_g_object_unref (_p); \ + _changed = TRUE; \ + } \ + _changed; \ + }) + +#define nm_clear_pointer(pp, destroy) \ + ({ \ + typeof (*(pp)) *_pp = (pp); \ + typeof (*_pp) _p; \ + gboolean _changed = FALSE; \ + \ + if ( _pp \ + && (_p = *_pp)) { \ + _nm_unused gconstpointer _p_check_is_pointer = _p; \ + \ + *_pp = NULL; \ + /* g_clear_pointer() assigns @destroy first to a local variable, so that + * you can call "g_clear_pointer (pp, (GDestroyNotify) destroy);" without + * gcc emitting a warning. We don't do that, hence, you cannot cast + * "destroy" first. + * + * On the upside: you are not supposed to cast fcn, because the pointer + * types are preserved. If you really need a cast, you should cast @pp. + * But that is hardly ever necessary. */ \ + (destroy) (_p); \ + \ + _changed = TRUE; \ + } \ + _changed; \ + }) + +/* basically, replaces + * g_clear_pointer (&location, g_free) + * with + * nm_clear_g_free (&location) + * + * Another advantage is that by using a macro and typeof(), it is more + * typesafe and gives you for example a compiler warning when pp is a const + * pointer or points to a const-pointer. + */ +#define nm_clear_g_free(pp) \ + nm_clear_pointer (pp, g_free) + +#define nm_clear_g_object(pp) \ + nm_clear_pointer (pp, g_object_unref) + +static inline gboolean +nm_clear_g_source (guint *id) +{ + guint v; + + if ( id + && (v = *id)) { + *id = 0; + g_source_remove (v); + return TRUE; + } + return FALSE; +} + +static inline gboolean +nm_clear_g_signal_handler (gpointer self, gulong *id) +{ + gulong v; + + if ( id + && (v = *id)) { + *id = 0; + g_signal_handler_disconnect (self, v); + return TRUE; + } + return FALSE; +} + +static inline gboolean +nm_clear_g_variant (GVariant **variant) +{ + GVariant *v; + + if ( variant + && (v = *variant)) { + *variant = NULL; + g_variant_unref (v); + return TRUE; + } + return FALSE; +} + +static inline gboolean +nm_clear_g_cancellable (GCancellable **cancellable) +{ + GCancellable *v; + + if ( cancellable + && (v = *cancellable)) { + *cancellable = NULL; + g_cancellable_cancel (v); + g_object_unref (v); + return TRUE; + } + return FALSE; +} + +/* If @cancellable_id is not 0, clear it and call g_cancellable_disconnect(). + * @cancellable may be %NULL, if there is nothing to disconnect. + * + * It's like nm_clear_g_signal_handler(), except that it uses g_cancellable_disconnect() + * instead of g_signal_handler_disconnect(). + * + * Note the warning in glib documentation about dead-lock and what g_cancellable_disconnect() + * actually does. */ +static inline gboolean +nm_clear_g_cancellable_disconnect (GCancellable *cancellable, gulong *cancellable_id) +{ + gulong id; + + if ( cancellable_id + && (id = *cancellable_id) != 0) { + *cancellable_id = 0; + g_cancellable_disconnect (cancellable, id); + return TRUE; + } + return FALSE; +} + +/*****************************************************************************/ + +static inline GVariant * +nm_g_variant_ref (GVariant *v) +{ + if (v) + g_variant_ref (v); + return v; +} + +static inline void +nm_g_variant_unref (GVariant *v) +{ + if (v) + g_variant_unref (v); +} + +/*****************************************************************************/ + +/* Determine whether @x is a power of two (@x being an integer type). + * Basically, this returns TRUE, if @x has exactly one bit set. + * For negative values and zero, this always returns FALSE. */ +#define nm_utils_is_power_of_two(x) ({ \ + typeof(x) __x = (x); \ + \ + ( (__x > ((typeof(__x)) 0)) \ + && ((__x & (__x - (((typeof(__x)) 1)))) == ((typeof(__x)) 0))); \ + }) + +#define NM_DIV_ROUND_UP(x, y) \ + ({ \ + const typeof(x) _x = (x); \ + const typeof(y) _y = (y); \ + \ + (_x / _y + !!(_x % _y)); \ + }) + +/*****************************************************************************/ + +#define NM_UTILS_LOOKUP_DEFAULT(v) return (v) +#define NM_UTILS_LOOKUP_DEFAULT_WARN(v) g_return_val_if_reached (v) +#define NM_UTILS_LOOKUP_DEFAULT_NM_ASSERT(v) { nm_assert_not_reached (); return (v); } +#define NM_UTILS_LOOKUP_ITEM(v, n) (void) 0; case v: return (n); (void) 0 +#define NM_UTILS_LOOKUP_STR_ITEM(v, n) NM_UTILS_LOOKUP_ITEM(v, ""n"") +#define NM_UTILS_LOOKUP_ITEM_IGNORE(v) (void) 0; case v: break; (void) 0 +#define NM_UTILS_LOOKUP_ITEM_IGNORE_OTHER() (void) 0; default: break; (void) 0 + +#define _NM_UTILS_LOOKUP_DEFINE(scope, fcn_name, lookup_type, result_type, unknown_val, ...) \ +scope result_type \ +fcn_name (lookup_type val) \ +{ \ + switch (val) { \ + (void) 0, \ + __VA_ARGS__ \ + (void) 0; \ + }; \ + { unknown_val; } \ +} + +#define NM_UTILS_LOOKUP_STR_DEFINE(fcn_name, lookup_type, unknown_val, ...) \ + _NM_UTILS_LOOKUP_DEFINE (, fcn_name, lookup_type, const char *, unknown_val, __VA_ARGS__) +#define NM_UTILS_LOOKUP_STR_DEFINE_STATIC(fcn_name, lookup_type, unknown_val, ...) \ + _NM_UTILS_LOOKUP_DEFINE (static, fcn_name, lookup_type, const char *, unknown_val, __VA_ARGS__) + +/* Call the string-lookup-table function @fcn_name. If the function returns + * %NULL, the numeric index is converted to string using a alloca() buffer. + * Beware: this macro uses alloca(). */ +#define NM_UTILS_LOOKUP_STR_A(fcn_name, idx) \ + ({ \ + typeof (idx) _idx = (idx); \ + const char *_s; \ + \ + _s = fcn_name (_idx); \ + if (!_s) { \ + _s = g_alloca (30); \ + \ + g_snprintf ((char *) _s, 30, "(%lld)", (long long) _idx); \ + } \ + _s; \ + }) + +/*****************************************************************************/ + +/* check if @flags has exactly one flag (@check) set. You should call this + * only with @check being a compile time constant and a power of two. */ +#define NM_FLAGS_HAS(flags, check) \ + ( G_STATIC_ASSERT_EXPR ((check) > 0 && ((check) & ((check) - 1)) == 0), NM_FLAGS_ANY ((flags), (check)) ) + +#define NM_FLAGS_ANY(flags, check) ( ( ((flags) & (check)) != 0 ) ? TRUE : FALSE ) +#define NM_FLAGS_ALL(flags, check) ( ( ((flags) & (check)) == (check) ) ? TRUE : FALSE ) + +#define NM_FLAGS_SET(flags, val) ({ \ + const typeof(flags) _flags = (flags); \ + const typeof(flags) _val = (val); \ + \ + _flags | _val; \ + }) + +#define NM_FLAGS_UNSET(flags, val) ({ \ + const typeof(flags) _flags = (flags); \ + const typeof(flags) _val = (val); \ + \ + _flags & (~_val); \ + }) + +#define NM_FLAGS_ASSIGN(flags, val, assign) ({ \ + const typeof(flags) _flags = (flags); \ + const typeof(flags) _val = (val); \ + \ + (assign) \ + ? _flags | (_val) \ + : _flags & (~_val); \ + }) + +/*****************************************************************************/ + +#define _NM_BACKPORT_SYMBOL_IMPL(version, return_type, orig_func, versioned_func, args_typed, args) \ +return_type versioned_func args_typed; \ +_nm_externally_visible return_type versioned_func args_typed \ +{ \ + return orig_func args; \ +} \ +return_type orig_func args_typed; \ +__asm__(".symver "G_STRINGIFY(versioned_func)", "G_STRINGIFY(orig_func)"@"G_STRINGIFY(version)) + +#define NM_BACKPORT_SYMBOL(version, return_type, func, args_typed, args) \ +_NM_BACKPORT_SYMBOL_IMPL(version, return_type, func, _##func##_##version, args_typed, args) + +/*****************************************************************************/ + +/* mirrors g_ascii_isspace() and what we consider spaces in general. */ +#define NM_ASCII_SPACES "\t\n\f\r " + +#define nm_str_skip_leading_spaces(str) \ + ({ \ + typeof (*(str)) *_str_sls = (str); \ + _nm_unused const char *const _str_type_check = _str_sls; \ + \ + if (_str_sls) { \ + while (g_ascii_isspace (_str_sls[0])) \ + _str_sls++; \ + } \ + _str_sls; \ + }) + +static inline char * +nm_strstrip (char *str) +{ + /* g_strstrip doesn't like NULL. */ + return str ? g_strstrip (str) : NULL; +} + +static inline const char * +nm_strstrip_avoid_copy (const char *str, char **str_free) +{ + gsize l; + char *s; + + nm_assert (str_free && !*str_free); + + if (!str) + return NULL; + + str = nm_str_skip_leading_spaces (str); + l = strlen (str); + if ( l == 0 + || !g_ascii_isspace (str[l - 1])) + return str; + while ( l > 0 + && g_ascii_isspace (str[l - 1])) + l--; + + s = g_new (char, l + 1); + memcpy (s, str, l); + s[l] = '\0'; + *str_free = s; + return s; +} + +#define nm_strstrip_avoid_copy_a(alloca_maxlen, str, out_str_free) \ + ({ \ + const char *_str_ssac = (str); \ + char **_out_str_free_ssac = (out_str_free); \ + \ + G_STATIC_ASSERT_EXPR ((alloca_maxlen) > 0); \ + \ + nm_assert ( _out_str_free_ssac || ((alloca_maxlen) > (str ? strlen (str) : 0u))); \ + nm_assert (!_out_str_free_ssac || !*_out_str_free_ssac); \ + \ + if (_str_ssac) { \ + _str_ssac = nm_str_skip_leading_spaces (_str_ssac); \ + if (_str_ssac[0] != '\0') { \ + gsize _l = strlen (_str_ssac); \ + \ + if (g_ascii_isspace (_str_ssac[--_l])) { \ + while ( _l > 0 \ + && g_ascii_isspace (_str_ssac[_l - 1])) { \ + _l--; \ + } \ + _str_ssac = nm_strndup_a ((alloca_maxlen), _str_ssac, _l, _out_str_free_ssac); \ + } \ + } \ + } \ + \ + _str_ssac; \ + }) + +/* g_ptr_array_sort()'s compare function takes pointers to the + * value. Thus, you cannot use strcmp directly. You can use + * nm_strcmp_p(). + * + * Like strcmp(), this function is not forgiving to accept %NULL. */ +static inline int +nm_strcmp_p (gconstpointer a, gconstpointer b) +{ + const char *s1 = *((const char **) a); + const char *s2 = *((const char **) b); + + return strcmp (s1, s2); +} + +/*****************************************************************************/ + +/* Taken from systemd's UNIQ_T and UNIQ macros. */ + +#define NM_UNIQ_T(x, uniq) G_PASTE(__unique_prefix_, G_PASTE(x, uniq)) +#define NM_UNIQ __COUNTER__ + +/*****************************************************************************/ + +/* glib's MIN()/MAX() macros don't have function-like behavior, in that they evaluate + * the argument possibly twice. + * + * Taken from systemd's MIN()/MAX() macros. */ + +#define NM_MIN(a, b) __NM_MIN(NM_UNIQ, a, NM_UNIQ, b) +#define __NM_MIN(aq, a, bq, b) \ + ({ \ + typeof (a) NM_UNIQ_T(A, aq) = (a); \ + typeof (b) NM_UNIQ_T(B, bq) = (b); \ + ((NM_UNIQ_T(A, aq) < NM_UNIQ_T(B, bq)) ? NM_UNIQ_T(A, aq) : NM_UNIQ_T(B, bq)); \ + }) + +#define NM_MAX(a, b) __NM_MAX(NM_UNIQ, a, NM_UNIQ, b) +#define __NM_MAX(aq, a, bq, b) \ + ({ \ + typeof (a) NM_UNIQ_T(A, aq) = (a); \ + typeof (b) NM_UNIQ_T(B, bq) = (b); \ + ((NM_UNIQ_T(A, aq) > NM_UNIQ_T(B, bq)) ? NM_UNIQ_T(A, aq) : NM_UNIQ_T(B, bq)); \ + }) + +#define NM_CLAMP(x, low, high) __NM_CLAMP(NM_UNIQ, x, NM_UNIQ, low, NM_UNIQ, high) +#define __NM_CLAMP(xq, x, lowq, low, highq, high) \ + ({ \ + typeof(x)NM_UNIQ_T(X,xq) = (x); \ + typeof(low) NM_UNIQ_T(LOW,lowq) = (low); \ + typeof(high) NM_UNIQ_T(HIGH,highq) = (high); \ + \ + ( (NM_UNIQ_T(X,xq) > NM_UNIQ_T(HIGH,highq)) \ + ? NM_UNIQ_T(HIGH,highq) \ + : (NM_UNIQ_T(X,xq) < NM_UNIQ_T(LOW,lowq)) \ + ? NM_UNIQ_T(LOW,lowq) \ + : NM_UNIQ_T(X,xq)); \ + }) + +#define NM_MAX_WITH_CMP(cmp, a, b) \ + ({ \ + typeof (a) _a = (a); \ + typeof (b) _b = (b); \ + \ + ( ((cmp (_a, _b)) >= 0) \ + ? _a \ + : _b); \ + }) + +/* evaluates to (void) if _A or _B are not constant or of different types */ +#define NM_CONST_MAX(_A, _B) \ + (__builtin_choose_expr (( __builtin_constant_p (_A) \ + && __builtin_constant_p (_B) \ + && __builtin_types_compatible_p (typeof (_A), typeof (_B))), \ + ((_A) > (_B)) ? (_A) : (_B), \ + ((void) 0))) + +/*****************************************************************************/ + +/* like g_memdup(). The difference is that the @size argument is of type + * gsize, while g_memdup() has type guint. Since, the size of container types + * like GArray is guint as well, this means trying to g_memdup() an + * array, + * g_memdup (array->data, array->len * sizeof (ElementType)) + * will lead to integer overflow, if there are more than G_MAXUINT/sizeof(ElementType) + * bytes. That seems unnecessarily dangerous to me. + * nm_memdup() avoids that, because its size argument is always large enough + * to contain all data that a GArray can hold. + * + * Another minor difference to g_memdup() is that the glib version also + * returns %NULL if @data is %NULL. E.g. g_memdup(NULL, 1) + * gives %NULL, but nm_memdup(NULL, 1) crashes. I think that + * is desirable, because @size MUST be correct at all times. @size + * may be zero, but one must not claim to have non-zero bytes when + * passing a %NULL @data pointer. + */ +static inline gpointer +nm_memdup (gconstpointer data, gsize size) +{ + gpointer p; + + if (size == 0) + return NULL; + p = g_malloc (size); + memcpy (p, data, size); + return p; +} + +static inline char * +_nm_strndup_a_step (char *s, const char *str, gsize len) +{ + NM_PRAGMA_WARNING_DISABLE ("-Wstringop-truncation"); + if (len > 0) + strncpy (s, str, len); + s[len] = '\0'; + return s; + NM_PRAGMA_WARNING_REENABLE; +} + +/* Similar to g_strndup(), however, if the string (including the terminating + * NUL char) fits into alloca_maxlen, this will alloca() the memory. + * + * It's a mix of strndup() and strndupa(), but deciding based on @alloca_maxlen + * which one to use. + * + * In case malloc() is necessary, @out_str_free will be set (this string + * must be freed afterwards). It is permissible to pass %NULL as @out_str_free, + * if you ensure that len < alloca_maxlen. + * + * Note that just like g_strndup(), this always returns a buffer with @len + 1 + * bytes, even if strlen(@str) is shorter than that (NUL terminated early). We fill + * the buffer with strncpy(), which means, that @str is copied up to the first + * NUL character and then filled with NUL characters. */ +#define nm_strndup_a(alloca_maxlen, str, len, out_str_free) \ + ({ \ + const gsize _alloca_maxlen_snd = (alloca_maxlen); \ + const char *const _str_snd = (str); \ + const gsize _len_snd = (len); \ + char **const _out_str_free_snd = (out_str_free); \ + char *_s_snd; \ + \ + G_STATIC_ASSERT_EXPR ((alloca_maxlen) <= 300); \ + \ + if ( _out_str_free_snd \ + && _len_snd >= _alloca_maxlen_snd) { \ + _s_snd = g_malloc (_len_snd + 1); \ + *_out_str_free_snd = _s_snd; \ + } else { \ + g_assert (_len_snd < _alloca_maxlen_snd); \ + _s_snd = g_alloca (_len_snd + 1); \ + } \ + _nm_strndup_a_step (_s_snd, _str_snd, _len_snd); \ + }) + +/*****************************************************************************/ + +/* generic macro to convert an int to a (heap allocated) string. + * + * Usually, an inline function nm_strdup_int64() would be enough. However, + * that cannot be used for guint64. So, we would also need nm_strdup_uint64(). + * This causes subtle error potential, because the caller needs to ensure to + * use the right one (and compiler isn't going to help as it silently casts). + * + * Instead, this generic macro is supposed to handle all integers correctly. */ +#if _NM_CC_SUPPORT_GENERIC +#define nm_strdup_int(val) \ + _Generic ((val), \ + char: g_strdup_printf ("%d", (int) (val)), \ + \ + signed char: g_strdup_printf ("%d", (signed) (val)), \ + signed short: g_strdup_printf ("%d", (signed) (val)), \ + signed: g_strdup_printf ("%d", (signed) (val)), \ + signed long: g_strdup_printf ("%ld", (signed long) (val)), \ + signed long long: g_strdup_printf ("%lld", (signed long long) (val)), \ + \ + unsigned char: g_strdup_printf ("%u", (unsigned) (val)), \ + unsigned short: g_strdup_printf ("%u", (unsigned) (val)), \ + unsigned: g_strdup_printf ("%u", (unsigned) (val)), \ + unsigned long: g_strdup_printf ("%lu", (unsigned long) (val)), \ + unsigned long long: g_strdup_printf ("%llu", (unsigned long long) (val)) \ + ) +#else +#define nm_strdup_int(val) \ + ( ( sizeof (val) == sizeof (guint64) \ + && ((typeof (val)) -1) > 0) \ + ? g_strdup_printf ("%"G_GUINT64_FORMAT, (guint64) (val)) \ + : g_strdup_printf ("%"G_GINT64_FORMAT, (gint64) (val))) +#endif + +/*****************************************************************************/ + +static inline guint +nm_encode_version (guint major, guint minor, guint micro) +{ + /* analog to the preprocessor macro NM_ENCODE_VERSION(). */ + return (major << 16) | (minor << 8) | micro; +} + +static inline void +nm_decode_version (guint version, guint *major, guint *minor, guint *micro) +{ + *major = (version & 0xFFFF0000u) >> 16; + *minor = (version & 0x0000FF00u) >> 8; + *micro = (version & 0x000000FFu); +} + +/*****************************************************************************/ + +/* taken from systemd's DECIMAL_STR_MAX() + * + * Returns the number of chars needed to format variables of the + * specified type as a decimal string. Adds in extra space for a + * negative '-' prefix (hence works correctly on signed + * types). Includes space for the trailing NUL. */ +#define NM_DECIMAL_STR_MAX(type) \ + (2+(sizeof(type) <= 1 ? 3 : \ + sizeof(type) <= 2 ? 5 : \ + sizeof(type) <= 4 ? 10 : \ + sizeof(type) <= 8 ? 20 : sizeof(int[-2*(sizeof(type) > 8)]))) + +/*****************************************************************************/ + +/* if @str is NULL, return "(null)". Otherwise, allocate a buffer using + * alloca() of and fill it with @str. @str will be quoted with double quote. + * If @str is longer then @trunc_at, the string is truncated and the closing + * quote is instead '^' to indicate truncation. + * + * Thus, the maximum stack allocated buffer will be @trunc_at+3. The maximum + * buffer size must be a constant and not larger than 300. */ +#define nm_strquote_a(trunc_at, str) \ + ({ \ + const char *const _str = (str); \ + \ + (_str \ + ? ({ \ + const gsize _trunc_at = (trunc_at); \ + const gsize _strlen_trunc = NM_MIN (strlen (_str), _trunc_at); \ + char *_buf; \ + \ + G_STATIC_ASSERT_EXPR ((trunc_at) <= 300); \ + \ + _buf = g_alloca (_strlen_trunc + 3); \ + _buf[0] = '"'; \ + memcpy (&_buf[1], _str, _strlen_trunc); \ + _buf[_strlen_trunc + 1] = _str[_strlen_trunc] ? '^' : '"'; \ + _buf[_strlen_trunc + 2] = '\0'; \ + _buf; \ + }) \ + : "(null)"); \ + }) + +#define nm_sprintf_buf(buf, format, ...) \ + ({ \ + char * _buf = (buf); \ + int _buf_len; \ + \ + /* some static assert trying to ensure that the buffer is statically allocated. + * It disallows a buffer size of sizeof(gpointer) to catch that. */ \ + G_STATIC_ASSERT (G_N_ELEMENTS (buf) == sizeof (buf) && sizeof (buf) != sizeof (char *)); \ + _buf_len = g_snprintf (_buf, sizeof (buf), \ + ""format"", ##__VA_ARGS__); \ + nm_assert (_buf_len < sizeof (buf)); \ + _buf; \ + }) + +/* it is "unsafe" because @bufsize must not be a constant expression and + * there is no check at compiletime. Regardless of that, the buffer size + * must not be larger than 300 bytes, as this gets stack allocated. */ +#define nm_sprintf_buf_unsafe_a(bufsize, format, ...) \ + ({ \ + char *_buf; \ + int _buf_len; \ + typeof (bufsize) _bufsize = (bufsize); \ + \ + nm_assert (_bufsize <= 300); \ + \ + _buf = g_alloca (_bufsize); \ + _buf_len = g_snprintf (_buf, _bufsize, \ + ""format"", ##__VA_ARGS__); \ + nm_assert (_buf_len >= 0 && _buf_len < _bufsize); \ + _buf; \ + }) + +#define nm_sprintf_bufa(bufsize, format, ...) \ + ({ \ + G_STATIC_ASSERT_EXPR ((bufsize) <= 300); \ + nm_sprintf_buf_unsafe_a ((bufsize), format, ##__VA_ARGS__); \ + }) + +/* aims to alloca() a buffer and fill it with printf(format, name). + * Note that format must not contain any format specifier except + * "%s". + * If the resulting string would be too large for stack allocation, + * it allocates a buffer with g_malloc() and assigns it to *p_val_to_free. */ +#define nm_construct_name_a(format, name, p_val_to_free) \ + ({ \ + const char *const _name = (name); \ + char **const _p_val_to_free = (p_val_to_free); \ + const gsize _name_len = strlen (_name); \ + char *_buf2; \ + \ + nm_assert (_p_val_to_free && !*_p_val_to_free); \ + if ( NM_STRLEN (format) <= 290 \ + && _name_len < (gsize) (290 - NM_STRLEN (format))) \ + _buf2 = nm_sprintf_buf_unsafe_a (NM_STRLEN (format) + _name_len, format, _name); \ + else { \ + _buf2 = g_strdup_printf (format, _name); \ + *_p_val_to_free = _buf2; \ + } \ + (const char *) _buf2; \ + }) + +/*****************************************************************************/ + +/** + * The boolean type _Bool is C99 while we mostly stick to C89. However, _Bool is too + * convenient to miss and is effectively available in gcc and clang. So, just use it. + * + * Usually, one would include "stdbool.h" to get the "bool" define which aliases + * _Bool. We provide this define here, because we want to make use of it anywhere. + * (also, stdbool.h is again C99). + * + * Using _Bool has advantages over gboolean: + * + * - commonly _Bool is one byte large, instead of gboolean's 4 bytes (because gboolean + * is a typedef for int). Especially when having boolean fields in a struct, we can + * thereby easily save some space. + * + * - _Bool type guarantees that two "true" expressions compare equal. E.g. the following + * will not work: + * gboolean v1 = 1; + * gboolean v2 = 2; + * g_assert_cmpint (v1, ==, v2); // will fail + * For that, we often to use !! to coerce gboolean values to 0 or 1: + * g_assert_cmpint (!!v2, ==, TRUE); + * With _Bool type, this will be handled properly by the compiler. + * + * - For structs, we might want to safe even more space and use bitfields: + * struct s1 { + * gboolean v1:1; + * }; + * But the problem here is that gboolean is signed, so that + * v1 will be either 0 or -1 (not 1, TRUE). Thus, the following + * fails: + * struct s1 s = { .v1 = TRUE, }; + * g_assert_cmpint (s1.v1, ==, TRUE); + * It will however work just fine with bool/_Bool while retaining the + * notion of having a boolean value. + * + * Also, add the defines for "true" and "false". Those are nicely highlighted by the editor + * as special types, contrary to glib's "TRUE"/"FALSE". + */ + +#ifndef bool +#define bool _Bool +#define true 1 +#define false 0 +#endif + +#ifdef _G_BOOLEAN_EXPR +/* g_assert() uses G_LIKELY(), which in turn uses _G_BOOLEAN_EXPR(). + * As glib's implementation uses a local variable _g_boolean_var_, + * we cannot do + * g_assert (some_macro ()); + * where some_macro() itself expands to ({g_assert(); ...}). + * In other words, you cannot have a g_assert() inside a g_assert() + * without getting a -Werror=shadow failure. + * + * Workaround that by re-defining _G_BOOLEAN_EXPR() + **/ +#undef _G_BOOLEAN_EXPR +#define __NM_G_BOOLEAN_EXPR_IMPL(v, expr) \ + ({ \ + int NM_UNIQ_T(V, v); \ + \ + if (expr) \ + NM_UNIQ_T(V, v) = 1; \ + else \ + NM_UNIQ_T(V, v) = 0; \ + NM_UNIQ_T(V, v); \ + }) +#define _G_BOOLEAN_EXPR(expr) __NM_G_BOOLEAN_EXPR_IMPL (NM_UNIQ, expr) +#endif + +/*****************************************************************************/ + +/** + * nm_steal_int: + * @p_val: pointer to an int type. + * + * Returns: *p_val and sets *p_val to zero the same time. + * Accepts %NULL, in which case also numeric 0 will be returned. + */ +#define nm_steal_int(p_val) \ + ({ \ + typeof (p_val) const _p_val = (p_val); \ + typeof (*_p_val) _val = 0; \ + \ + if ( _p_val \ + && (_val = *_p_val)) { \ + *_p_val = 0; \ + } \ + _val; \ + }) + +static inline int +nm_steal_fd (int *p_fd) +{ + int fd; + + if ( p_fd + && ((fd = *p_fd) >= 0)) { + *p_fd = -1; + return fd; + } + return -1; +} + +/** + * nm_close: + * + * Like close() but throws an assertion if the input fd is + * invalid. Closing an invalid fd is a programming error, so + * it's better to catch it early. + */ +static inline int +nm_close (int fd) +{ + int r; + + r = close (fd); + nm_assert (r != -1 || fd < 0 || errno != EBADF); + return r; +} + +#define NM_PID_T_INVAL ((pid_t) -1) + +#endif /* __NM_MACROS_INTERNAL_H__ */ diff --git a/shared/nm-glib-aux/nm-obj.h b/shared/nm-glib-aux/nm-obj.h new file mode 100644 index 00000000..4edd1f3e --- /dev/null +++ b/shared/nm-glib-aux/nm-obj.h @@ -0,0 +1,82 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* NetworkManager -- Network link manager + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA. + * + * (C) Copyright 2017 Red Hat, Inc. + */ + +#ifndef __NM_OBJ_H__ +#define __NM_OBJ_H__ + +/*****************************************************************************/ + +#define NM_OBJ_REF_COUNT_STACKINIT (G_MAXINT) + +typedef struct _NMObjBaseInst NMObjBaseInst; +typedef struct _NMObjBaseClass NMObjBaseClass; + +struct _NMObjBaseInst { + /* The first field of NMObjBaseInst is compatible with GObject. + * Basically, NMObjBaseInst is an abstract base type of GTypeInstance. + * + * If you do it right, you may derive a type of NMObjBaseInst as a proper GTypeInstance. + * That involves allocating a GType for it, which can be inconvenient because + * a GType is dynamically created (and the class can no longer be immutable + * memory). + * + * Even if your implementation of NMObjBaseInst is not a full fledged GType(Instance), + * you still can use GTypeInstances in the same context as you can decide based on the + * NMObjBaseClass with what kind of object you are dealing with. + * + * Basically, the only thing NMObjBaseInst gives you is access to an + * NMObjBaseClass instance. + */ + union { + const NMObjBaseClass *klass; + GTypeInstance g_type_instance; + }; +}; + +struct _NMObjBaseClass { + /* NMObjBaseClass is the base class of all NMObjBaseInst implementations. + * Note that it is also an abstract super class of GTypeInstance, that means + * you may implement a NMObjBaseClass as a subtype of GTypeClass. + * + * For that to work, you must properly set the GTypeClass instance (and its + * GType). + * + * Note that to implement a NMObjBaseClass that is *not* a GTypeClass, you wouldn't + * set the GType. Hence, this field is only useful for type implementations that actually + * extend GTypeClass. + * + * In a way it is wrong that NMObjBaseClass has the GType member, because it is + * a base class of GTypeClass and doesn't necessarily use the GType. However, + * it is here so that G_TYPE_CHECK_INSTANCE_TYPE() and friends work correctly + * on any NMObjectClass. That means, while not necessary, it is convenient that + * a NMObjBaseClass has all members of GTypeClass. + * Also note that usually you have only one instance of a certain type, so this + * wastes just a few bytes for the unneeded GType. + */ + union { + GType g_type; + GTypeClass g_type_class; + }; +}; + +/*****************************************************************************/ + +#endif /* __NM_OBJ_H__ */ diff --git a/shared/nm-glib-aux/nm-random-utils.c b/shared/nm-glib-aux/nm-random-utils.c new file mode 100644 index 00000000..d7c7da42 --- /dev/null +++ b/shared/nm-glib-aux/nm-random-utils.c @@ -0,0 +1,165 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* NetworkManager -- Network link manager + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA. + * + * (C) Copyright 2017 Red Hat, Inc. + */ + +#include "nm-default.h" + +#include "nm-random-utils.h" + +#include + +#if USE_SYS_RANDOM_H +#include +#else +#include +#endif + +#include "nm-shared-utils.h" + +/*****************************************************************************/ + +/** + * nm_utils_random_bytes: + * @p: the buffer to fill + * @n: the number of bytes to write to @p. + * + * Uses getrandom() or reads /dev/urandom to fill the buffer + * with random data. If all fails, as last fallback it uses + * GRand to fill the buffer with pseudo random numbers. + * The function always succeeds in writing some random numbers + * to the buffer. The return value of FALSE indicates that the + * obtained bytes are probably not of good randomness. + * + * Returns: whether the written bytes are good. If you + * don't require good randomness, you can ignore the return + * value. + * + * Note that if calling getrandom() fails because there is not enough + * entropy (at early boot), the function will read /dev/urandom. + * Which of course, still has low entropy, and cause kernel to log + * a warning. + */ +gboolean +nm_utils_random_bytes (void *p, size_t n) +{ + int fd; + int r; + gboolean has_high_quality = TRUE; + gboolean urandom_success; + guint8 *buf = p; + gboolean avoid_urandom = FALSE; + + g_return_val_if_fail (p, FALSE); + g_return_val_if_fail (n > 0, FALSE); + +#if HAVE_GETRANDOM + { + static gboolean have_syscall = TRUE; + + if (have_syscall) { + r = getrandom (buf, n, GRND_NONBLOCK); + if (r > 0) { + if ((size_t) r == n) + return TRUE; + + /* no or partial read. There is not enough entropy. + * Fill the rest reading from urandom, and remember that + * some bits are not high quality. */ + nm_assert (r < n); + buf += r; + n -= r; + has_high_quality = FALSE; + + /* At this point, we don't want to read /dev/urandom, because + * the entropy pool is low (early boot?), and asking for more + * entropy causes kernel messages to be logged. + * + * We use our fallback via GRand. Note that g_rand_new() also + * tries to seed itself with data from /dev/urandom, but since + * we reuse the instance, it shouldn't matter. */ + avoid_urandom = TRUE; + } else { + if (errno == ENOSYS) { + /* no support for getrandom(). We don't know whether + * we urandom will give us good quality. Assume yes. */ + have_syscall = FALSE; + } else { + /* unknown error. We'll read urandom below, but we don't have + * high-quality randomness. */ + has_high_quality = FALSE; + } + } + } + } +#endif + + urandom_success = FALSE; + if (!avoid_urandom) { +fd_open: + fd = open ("/dev/urandom", O_RDONLY | O_CLOEXEC | O_NOCTTY); + if (fd < 0) { + r = errno; + if (r == EINTR) + goto fd_open; + } else { + r = nm_utils_fd_read_loop_exact (fd, buf, n, TRUE); + nm_close (fd); + if (r >= 0) + urandom_success = TRUE; + } + } + + if (!urandom_success) { + static _nm_thread_local GRand *rand = NULL; + gsize i; + int j; + + /* we failed to fill the bytes reading from urandom. + * Fill the bits using GRand pseudo random numbers. + * + * We don't have good quality. + */ + has_high_quality = FALSE; + + if (G_UNLIKELY (!rand)) + rand = g_rand_new (); + + nm_assert (n > 0); + i = 0; + for (;;) { + const union { + guint32 v32; + guint8 v8[4]; + } v = { + .v32 = g_rand_int (rand), + }; + + for (j = 0; j < 4; ) { + buf[i++] = v.v8[j++]; + if (i >= n) + goto done; + } + } +done: + ; + } + + return has_high_quality; +} diff --git a/shared/nm-glib-aux/nm-random-utils.h b/shared/nm-glib-aux/nm-random-utils.h new file mode 100644 index 00000000..15a118d3 --- /dev/null +++ b/shared/nm-glib-aux/nm-random-utils.h @@ -0,0 +1,27 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* NetworkManager -- Network link manager + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA. + * + * (C) Copyright 2017 Red Hat, Inc. + */ + +#ifndef __NM_RANDOM_UTILS_H__ +#define __NM_RANDOM_UTILS_H__ + +gboolean nm_utils_random_bytes (void *p, size_t n); + +#endif /* __NM_RANDOM_UTILS_H__ */ diff --git a/shared/nm-glib-aux/nm-secret-utils.c b/shared/nm-glib-aux/nm-secret-utils.c new file mode 100644 index 00000000..81f8b5ae --- /dev/null +++ b/shared/nm-glib-aux/nm-secret-utils.c @@ -0,0 +1,168 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* NetworkManager -- Network link manager + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA. + * + * (C) Copyright 2018 Red Hat, Inc. + * (C) Copyright 2015 - 2019 Jason A. Donenfeld . All Rights Reserved. + */ + +#include "nm-default.h" + +#include "nm-secret-utils.h" + +/*****************************************************************************/ + +void +nm_explicit_bzero (void *s, gsize n) +{ + /* gracefully handle n == 0. This is important, callers rely on it. */ + if (n == 0) + return; + + nm_assert (s); + +#if defined (HAVE_DECL_EXPLICIT_BZERO) && HAVE_DECL_EXPLICIT_BZERO + explicit_bzero (s, n); +#else + { + volatile guint8 *p = s; + + memset (s, '\0', n); + while (n-- > 0) + *(p++) = '\0'; + } +#endif +} + +/*****************************************************************************/ + +char * +nm_secret_strchomp (char *secret) +{ + gsize len; + + g_return_val_if_fail (secret, NULL); + + /* it's actually identical to g_strchomp(). However, + * the glib function does not document, that it clears the + * memory. For @secret, we don't only want to truncate trailing + * spaces, we want to overwrite them with NUL. */ + + len = strlen (secret); + while (len--) { + if (g_ascii_isspace ((guchar) secret[len])) + secret[len] = '\0'; + else + break; + } + + return secret; +} + +/*****************************************************************************/ + +GBytes * +nm_secret_copy_to_gbytes (gconstpointer mem, gsize mem_len) +{ + NMSecretBuf *b; + + if (mem_len == 0) + return g_bytes_new_static ("", 0); + + nm_assert (mem); + + /* NUL terminate the buffer. + * + * The entire buffer is already malloc'ed and likely has some room for padding. + * Thus, in many situations, this additional byte will cause no overhead in + * practice. + * + * Even if it causes an overhead, do it just for safety. Yes, the returned + * bytes is not a NUL terminated string and no user must rely on this. Do + * not treat binary data as NUL terminated strings, unless you know what + * you are doing. Anyway, defensive FTW. + */ + + b = nm_secret_buf_new (mem_len + 1); + memcpy (b->bin, mem, mem_len); + b->bin[mem_len] = 0; + return nm_secret_buf_to_gbytes_take (b, mem_len); +} + +/*****************************************************************************/ + +NMSecretBuf * +nm_secret_buf_new (gsize len) +{ + NMSecretBuf *secret; + + nm_assert (len > 0); + + secret = g_malloc (sizeof (NMSecretBuf) + len); + *((gsize *) &(secret->len)) = len; + return secret; +} + +static void +_secret_buf_free (gpointer user_data) +{ + NMSecretBuf *secret = user_data; + + nm_assert (secret); + nm_assert (secret->len > 0); + + nm_explicit_bzero (secret->bin, secret->len); + g_free (user_data); +} + +GBytes * +nm_secret_buf_to_gbytes_take (NMSecretBuf *secret, gssize actual_len) +{ + nm_assert (secret); + nm_assert (secret->len > 0); + nm_assert (actual_len == -1 || (actual_len >= 0 && actual_len <= secret->len)); + return g_bytes_new_with_free_func (secret->bin, + actual_len >= 0 ? (gsize) actual_len : secret->len, + _secret_buf_free, + secret); +} + +/*****************************************************************************/ + +/** + * nm_utils_memeqzero_secret: + * @data: the data pointer to check (may be %NULL if @length is zero). + * @length: the number of bytes to check. + * + * Checks that all bytes are zero. This always takes the same amount + * of time to prevent timing attacks. + * + * Returns: whether all bytes are zero. + */ +gboolean +nm_utils_memeqzero_secret (gconstpointer data, gsize length) +{ + const guint8 *const key = data; + volatile guint8 acc = 0; + gsize i; + + for (i = 0; i < length; i++) { + acc |= key[i]; + asm volatile("" : "=r"(acc) : "0"(acc)); + } + return 1 & ((acc - 1) >> 8); +} diff --git a/shared/nm-glib-aux/nm-secret-utils.h b/shared/nm-glib-aux/nm-secret-utils.h new file mode 100644 index 00000000..034ef7bd --- /dev/null +++ b/shared/nm-glib-aux/nm-secret-utils.h @@ -0,0 +1,178 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* NetworkManager -- Network link manager + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA. + * + * (C) Copyright 2018 Red Hat, Inc. + */ + +#ifndef __NM_SECRET_UTILS_H__ +#define __NM_SECRET_UTILS_H__ + +#include "nm-macros-internal.h" + +/*****************************************************************************/ + +void nm_explicit_bzero (void *s, gsize n); + +/*****************************************************************************/ + +char *nm_secret_strchomp (char *secret); + +/*****************************************************************************/ + +static inline void +nm_free_secret (char *secret) +{ + if (secret) { + nm_explicit_bzero (secret, strlen (secret)); + g_free (secret); + } +} + +NM_AUTO_DEFINE_FCN0 (char *, _nm_auto_free_secret, nm_free_secret) +/** + * nm_auto_free_secret: + * + * Call g_free() on a variable location when it goes out of scope. + * Also, previously, calls memset(loc, 0, strlen(loc)) to clear out + * the secret. + */ +#define nm_auto_free_secret nm_auto(_nm_auto_free_secret) + +/*****************************************************************************/ + +GBytes *nm_secret_copy_to_gbytes (gconstpointer mem, gsize mem_len); + +/*****************************************************************************/ + +/* NMSecretPtr is a pair of malloc'ed data pointer and the length of the + * data. The purpose is to use it in combination with nm_auto_clear_secret_ptr + * which ensures that the data pointer (with all len bytes) is cleared upon + * cleanup. */ +typedef struct { + gsize len; + + /* the data pointer. This pointer must be allocated with malloc (at least + * when used with nm_secret_ptr_clear()). */ + union { + char *str; + void *ptr; + guint8 *bin; + }; +} NMSecretPtr; + +static inline void +nm_secret_ptr_bzero (NMSecretPtr *secret) +{ + if (secret) { + if (secret->len > 0) { + if (secret->ptr) + nm_explicit_bzero (secret->ptr, secret->len); + } + } +} + +#define nm_auto_bzero_secret_ptr nm_auto(nm_secret_ptr_bzero) + +static inline void +nm_secret_ptr_clear (NMSecretPtr *secret) +{ + if (secret) { + if (secret->len > 0) { + if (secret->ptr) + nm_explicit_bzero (secret->ptr, secret->len); + secret->len = 0; + } + nm_clear_g_free (&secret->ptr); + } +} + +#define nm_auto_clear_secret_ptr nm_auto(nm_secret_ptr_clear) + +#define NM_SECRET_PTR_INIT() \ + ((const NMSecretPtr) { \ + .len = 0, \ + .ptr = NULL, \ + }) + +#define NM_SECRET_PTR_STATIC(_len) \ + ((const NMSecretPtr) { \ + .len = _len, \ + .ptr = ((guint8 [_len]) { }), \ + }) + +#define NM_SECRET_PTR_ARRAY(_arr) \ + ((const NMSecretPtr) { \ + .len = G_N_ELEMENTS (_arr) * sizeof ((_arr)[0]), \ + .ptr = &((_arr)[0]), \ + }) + +static inline void +nm_secret_ptr_clear_static (const NMSecretPtr *secret) +{ + if (secret) { + if (secret->len > 0) { + nm_assert (secret->ptr); + nm_explicit_bzero (secret->ptr, secret->len); + } + } +} + +#define nm_auto_clear_static_secret_ptr nm_auto(nm_secret_ptr_clear_static) + +static inline void +nm_secret_ptr_move (NMSecretPtr *dst, NMSecretPtr *src) +{ + if (dst && dst != src) { + *dst = *src; + src->len = 0; + src->ptr = NULL; + } +} + +/*****************************************************************************/ + +typedef struct { + const gsize len; + union { + char str[0]; + guint8 bin[0]; + }; +} NMSecretBuf; + +static inline void +_nm_auto_free_secret_buf (NMSecretBuf **ptr) +{ + NMSecretBuf *b = *ptr; + + if (b) { + nm_assert (b->len > 0); + nm_explicit_bzero (b->bin, b->len); + g_free (b); + } +} +#define nm_auto_free_secret_buf nm_auto(_nm_auto_free_secret_buf) + +NMSecretBuf *nm_secret_buf_new (gsize len); + +GBytes *nm_secret_buf_to_gbytes_take (NMSecretBuf *secret, gssize actual_len); + +/*****************************************************************************/ + +gboolean nm_utils_memeqzero_secret (gconstpointer data, gsize length); + +#endif /* __NM_SECRET_UTILS_H__ */ diff --git a/shared/nm-glib-aux/nm-shared-utils.c b/shared/nm-glib-aux/nm-shared-utils.c new file mode 100644 index 00000000..cf08a77f --- /dev/null +++ b/shared/nm-glib-aux/nm-shared-utils.c @@ -0,0 +1,2941 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* NetworkManager -- Network link manager + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA. + * + * (C) Copyright 2016 Red Hat, Inc. + */ + +#include "nm-default.h" + +#include "nm-shared-utils.h" + +#include +#include +#include +#include + +#include "nm-errno.h" + +/*****************************************************************************/ + +const void *const _NM_PTRARRAY_EMPTY[1] = { NULL }; + +/*****************************************************************************/ + +const NMIPAddr nm_ip_addr_zero = { }; + +/* this initializes a struct in_addr/in6_addr and allows for untrusted + * arguments (like unsuitable @addr_family or @src_len). It's almost safe + * in the sense that it verifies input arguments strictly. Also, it + * uses memcpy() to access @src, so alignment is not an issue. + * + * Only potential pitfalls: + * + * - it allows for @addr_family to be AF_UNSPEC. If that is the case (and the + * caller allows for that), the caller MUST provide @out_addr_family. + * - when setting @dst to an IPv4 address, the trailing bytes are not touched. + * Meaning, if @dst is an NMIPAddr union, only the first bytes will be set. + * If that matter to you, clear @dst before. */ +gboolean +nm_ip_addr_set_from_untrusted (int addr_family, + gpointer dst, + gconstpointer src, + gsize src_len, + int *out_addr_family) +{ + nm_assert (dst); + + switch (addr_family) { + case AF_UNSPEC: + if (!out_addr_family) { + /* when the callers allow undefined @addr_family, they must provide + * an @out_addr_family argument. */ + nm_assert_not_reached (); + return FALSE; + } + switch (src_len) { + case sizeof (struct in_addr): addr_family = AF_INET; break; + case sizeof (struct in6_addr): addr_family = AF_INET6; break; + default: + return FALSE; + } + break; + case AF_INET: + if (src_len != sizeof (struct in_addr)) + return FALSE; + break; + case AF_INET6: + if (src_len != sizeof (struct in6_addr)) + return FALSE; + break; + default: + /* when the callers allow undefined @addr_family, they must provide + * an @out_addr_family argument. */ + nm_assert (out_addr_family); + return FALSE; + } + + nm_assert (src); + + memcpy (dst, src, src_len); + NM_SET_OUT (out_addr_family, addr_family); + return TRUE; +} + +/*****************************************************************************/ + +pid_t +nm_utils_gettid (void) +{ + return (pid_t) syscall (SYS_gettid); +} + +/* Used for asserting that this function is called on the main-thread. + * The main-thread is determined by remembering the thread-id + * of when the function was called the first time. + * + * When forking, the thread-id is again reset upon first call. */ +gboolean +_nm_assert_on_main_thread (void) +{ + G_LOCK_DEFINE_STATIC (lock); + static pid_t seen_tid; + static pid_t seen_pid; + pid_t tid; + pid_t pid; + gboolean success = FALSE; + + tid = nm_utils_gettid (); + nm_assert (tid != 0); + + G_LOCK (lock); + + if (G_LIKELY (tid == seen_tid)) { + /* we don't care about false positives (when the process forked, and the thread-id + * is accidentally re-used) . It's for assertions only. */ + success = TRUE; + } else { + pid = getpid (); + nm_assert (pid != 0); + + if ( seen_tid == 0 + || seen_pid != pid) { + /* either this is the first time we call the function, or the process + * forked. In both cases, remember the thread-id. */ + seen_tid = tid; + seen_pid = pid; + success = TRUE; + } + } + + G_UNLOCK (lock); + + return success; +} + +/*****************************************************************************/ + +void +nm_utils_strbuf_append_c (char **buf, gsize *len, char c) +{ + switch (*len) { + case 0: + return; + case 1: + (*buf)[0] = '\0'; + *len = 0; + (*buf)++; + return; + default: + (*buf)[0] = c; + (*buf)[1] = '\0'; + (*len)--; + (*buf)++; + return; + } +} + +void +nm_utils_strbuf_append_bin (char **buf, gsize *len, gconstpointer str, gsize str_len) +{ + switch (*len) { + case 0: + return; + case 1: + if (str_len == 0) { + (*buf)[0] = '\0'; + return; + } + (*buf)[0] = '\0'; + *len = 0; + (*buf)++; + return; + default: + if (str_len == 0) { + (*buf)[0] = '\0'; + return; + } + if (str_len >= *len) { + memcpy (*buf, str, *len - 1); + (*buf)[*len - 1] = '\0'; + *buf = &(*buf)[*len]; + *len = 0; + } else { + memcpy (*buf, str, str_len); + *buf = &(*buf)[str_len]; + (*buf)[0] = '\0'; + *len -= str_len; + } + return; + } +} + +void +nm_utils_strbuf_append_str (char **buf, gsize *len, const char *str) +{ + gsize src_len; + + switch (*len) { + case 0: + return; + case 1: + if (!str || !*str) { + (*buf)[0] = '\0'; + return; + } + (*buf)[0] = '\0'; + *len = 0; + (*buf)++; + return; + default: + if (!str || !*str) { + (*buf)[0] = '\0'; + return; + } + src_len = g_strlcpy (*buf, str, *len); + if (src_len >= *len) { + *buf = &(*buf)[*len]; + *len = 0; + } else { + *buf = &(*buf)[src_len]; + *len -= src_len; + } + return; + } +} + +void +nm_utils_strbuf_append (char **buf, gsize *len, const char *format, ...) +{ + char *p = *buf; + va_list args; + int retval; + + if (*len == 0) + return; + + va_start (args, format); + retval = g_vsnprintf (p, *len, format, args); + va_end (args); + + if ((gsize) retval >= *len) { + *buf = &p[*len]; + *len = 0; + } else { + *buf = &p[retval]; + *len -= retval; + } +} + +/** + * nm_utils_strbuf_seek_end: + * @buf: the input/output buffer + * @len: the input/output length of the buffer. + * + * Commonly, one uses nm_utils_strbuf_append*(), to incrementally + * append strings to the buffer. However, sometimes we need to use + * existing API to write to the buffer. + * After doing so, we want to adjust the buffer counter. + * Essentially, + * + * g_snprintf (buf, len, ...); + * nm_utils_strbuf_seek_end (&buf, &len); + * + * is almost the same as + * + * nm_utils_strbuf_append (&buf, &len, ...); + * + * The only difference is the behavior when the string got truncated: + * nm_utils_strbuf_append() will recognize that and set the remaining + * length to zero. + * + * In general, the behavior is: + * + * - if *len is zero, do nothing + * - if the buffer contains a NUL byte within the first *len characters, + * the buffer is pointed to the NUL byte and len is adjusted. In this + * case, the remaining *len is always >= 1. + * In particular, that is also the case if the NUL byte is at the very last + * position ((*buf)[*len -1]). That happens, when the previous operation + * either fit the string exactly into the buffer or the string was truncated + * by g_snprintf(). The difference cannot be determined. + * - if the buffer contains no NUL bytes within the first *len characters, + * write NUL at the last position, set *len to zero, and point *buf past + * the NUL byte. This would happen with + * + * strncpy (buf, long_str, len); + * nm_utils_strbuf_seek_end (&buf, &len). + * + * where strncpy() does truncate the string and not NUL terminate it. + * nm_utils_strbuf_seek_end() would then NUL terminate it. + */ +void +nm_utils_strbuf_seek_end (char **buf, gsize *len) +{ + gsize l; + char *end; + + nm_assert (len); + nm_assert (buf && *buf); + + if (*len <= 1) { + if ( *len == 1 + && (*buf)[0]) + goto truncate; + return; + } + + end = memchr (*buf, 0, *len); + if (end) { + l = end - *buf; + nm_assert (l < *len); + + *buf = end; + *len -= l; + return; + } + +truncate: + /* hm, no NUL character within len bytes. + * Just NUL terminate the array and consume them + * all. */ + *buf += *len; + (*buf)[-1] = '\0'; + *len = 0; + return; +} + +/*****************************************************************************/ + +/** + * nm_utils_gbytes_equals: + * @bytes: (allow-none): a #GBytes array to compare. Note that + * %NULL is treated like an #GBytes array of length zero. + * @mem_data: the data pointer with @mem_len bytes + * @mem_len: the length of the data pointer + * + * Returns: %TRUE if @bytes contains the same data as @mem_data. As a + * special case, a %NULL @bytes is treated like an empty array. + */ +gboolean +nm_utils_gbytes_equal_mem (GBytes *bytes, + gconstpointer mem_data, + gsize mem_len) +{ + gconstpointer p; + gsize l; + + if (!bytes) { + /* as a special case, let %NULL GBytes compare idential + * to an empty array. */ + return (mem_len == 0); + } + + p = g_bytes_get_data (bytes, &l); + return l == mem_len + && ( mem_len == 0 /* allow @mem_data to be %NULL */ + || memcmp (p, mem_data, mem_len) == 0); +} + +GVariant * +nm_utils_gbytes_to_variant_ay (GBytes *bytes) +{ + const guint8 *p; + gsize l; + + if (!bytes) { + /* for convenience, accept NULL to return an empty variant */ + return g_variant_new_array (G_VARIANT_TYPE_BYTE, NULL, 0); + } + + p = g_bytes_get_data (bytes, &l); + return g_variant_new_fixed_array (G_VARIANT_TYPE_BYTE, p, l, 1); +} + +/*****************************************************************************/ + +/** + * nm_strquote: + * @buf: the output buffer of where to write the quoted @str argument. + * @buf_len: the size of @buf. + * @str: (allow-none): the string to quote. + * + * Writes @str to @buf with quoting. The resulting buffer + * is always NUL terminated, unless @buf_len is zero. + * If @str is %NULL, it writes "(null)". + * + * If @str needs to be truncated, the closing quote is '^' instead + * of '"'. + * + * This is similar to nm_strquote_a(), which however uses alloca() + * to allocate a new buffer. Also, here @buf_len is the size of @buf, + * while nm_strquote_a() has the number of characters to print. The latter + * doesn't include the quoting. + * + * Returns: the input buffer with the quoted string. + */ +const char * +nm_strquote (char *buf, gsize buf_len, const char *str) +{ + const char *const buf0 = buf; + + if (!str) { + nm_utils_strbuf_append_str (&buf, &buf_len, "(null)"); + goto out; + } + + if (G_UNLIKELY (buf_len <= 2)) { + switch (buf_len) { + case 2: + *(buf++) = '^'; + /* fall-through */ + case 1: + *(buf++) = '\0'; + break; + } + goto out; + } + + *(buf++) = '"'; + buf_len--; + + nm_utils_strbuf_append_str (&buf, &buf_len, str); + + /* if the string was too long we indicate truncation with a + * '^' instead of a closing quote. */ + if (G_UNLIKELY (buf_len <= 1)) { + switch (buf_len) { + case 1: + buf[-1] = '^'; + break; + case 0: + buf[-2] = '^'; + break; + default: + nm_assert_not_reached (); + break; + } + } else { + nm_assert (buf_len >= 2); + *(buf++) = '"'; + *(buf++) = '\0'; + } + +out: + return buf0; +} + +/*****************************************************************************/ + +char _nm_utils_to_string_buffer[]; + +void +nm_utils_to_string_buffer_init (char **buf, gsize *len) +{ + if (!*buf) { + *buf = _nm_utils_to_string_buffer; + *len = sizeof (_nm_utils_to_string_buffer); + } +} + +gboolean +nm_utils_to_string_buffer_init_null (gconstpointer obj, char **buf, gsize *len) +{ + nm_utils_to_string_buffer_init (buf, len); + if (!obj) { + g_strlcpy (*buf, "(null)", *len); + return FALSE; + } + return TRUE; +} + +/*****************************************************************************/ + +const char * +nm_utils_flags2str (const NMUtilsFlags2StrDesc *descs, + gsize n_descs, + unsigned flags, + char *buf, + gsize len) +{ + gsize i; + char *p; + +#if NM_MORE_ASSERTS > 10 + nm_assert (descs); + nm_assert (n_descs > 0); + for (i = 0; i < n_descs; i++) { + gsize j; + + nm_assert (descs[i].name && descs[i].name[0]); + for (j = 0; j < i; j++) + nm_assert (descs[j].flag != descs[i].flag); + } +#endif + + nm_utils_to_string_buffer_init (&buf, &len); + + if (!len) + return buf; + + buf[0] = '\0'; + p = buf; + if (!flags) { + for (i = 0; i < n_descs; i++) { + if (!descs[i].flag) { + nm_utils_strbuf_append_str (&p, &len, descs[i].name); + break; + } + } + return buf; + } + + for (i = 0; flags && i < n_descs; i++) { + if ( descs[i].flag + && NM_FLAGS_ALL (flags, descs[i].flag)) { + flags &= ~descs[i].flag; + + if (buf[0] != '\0') + nm_utils_strbuf_append_c (&p, &len, ','); + nm_utils_strbuf_append_str (&p, &len, descs[i].name); + } + } + if (flags) { + if (buf[0] != '\0') + nm_utils_strbuf_append_c (&p, &len, ','); + nm_utils_strbuf_append (&p, &len, "0x%x", flags); + } + return buf; +}; + +/*****************************************************************************/ + +/** + * _nm_utils_ip4_prefix_to_netmask: + * @prefix: a CIDR prefix + * + * Returns: the netmask represented by the prefix, in network byte order + **/ +guint32 +_nm_utils_ip4_prefix_to_netmask (guint32 prefix) +{ + return prefix < 32 ? ~htonl(0xFFFFFFFF >> prefix) : 0xFFFFFFFF; +} + +/** + * _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) +{ + 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 */ + + return 24; /* Class C - 255.255.255.0 */ +} + +gboolean +nm_utils_ip_is_site_local (int addr_family, + const void *address) +{ + in_addr_t addr4; + + switch (addr_family) { + case AF_INET: + /* RFC1918 private addresses + * 10.0.0.0/8, 172.16.0.0/12, 192.168.0.0/16 */ + addr4 = ntohl (*((const in_addr_t *) address)); + return (addr4 & 0xff000000) == 0x0a000000 + || (addr4 & 0xfff00000) == 0xac100000 + || (addr4 & 0xffff0000) == 0xc0a80000; + case AF_INET6: + return IN6_IS_ADDR_SITELOCAL (address); + default: + g_return_val_if_reached (FALSE); + } +} + +/*****************************************************************************/ + +gboolean +nm_utils_parse_inaddr_bin (int addr_family, + const char *text, + int *out_addr_family, + gpointer out_addr) +{ + NMIPAddr addrbin; + + g_return_val_if_fail (text, FALSE); + + if (addr_family == AF_UNSPEC) { + g_return_val_if_fail (!out_addr || out_addr_family, FALSE); + addr_family = strchr (text, ':') ? AF_INET6 : AF_INET; + } else + g_return_val_if_fail (NM_IN_SET (addr_family, AF_INET, AF_INET6), FALSE); + + if (inet_pton (addr_family, text, &addrbin) != 1) + return FALSE; + + NM_SET_OUT (out_addr_family, addr_family); + if (out_addr) + nm_ip_addr_set (addr_family, out_addr, &addrbin); + return TRUE; +} + +gboolean +nm_utils_parse_inaddr (int addr_family, + const char *text, + char **out_addr) +{ + NMIPAddr addrbin; + char addrstr_buf[MAX (INET_ADDRSTRLEN, INET6_ADDRSTRLEN)]; + + g_return_val_if_fail (text, FALSE); + + if (addr_family == AF_UNSPEC) + addr_family = strchr (text, ':') ? AF_INET6 : AF_INET; + else + g_return_val_if_fail (NM_IN_SET (addr_family, AF_INET, AF_INET6), FALSE); + + if (inet_pton (addr_family, text, &addrbin) != 1) + return FALSE; + + NM_SET_OUT (out_addr, g_strdup (inet_ntop (addr_family, &addrbin, addrstr_buf, sizeof (addrstr_buf)))); + return TRUE; +} + +gboolean +nm_utils_parse_inaddr_prefix_bin (int addr_family, + const char *text, + int *out_addr_family, + gpointer out_addr, + int *out_prefix) +{ + gs_free char *addrstr_free = NULL; + int prefix = -1; + const char *slash; + const char *addrstr; + NMIPAddr addrbin; + + g_return_val_if_fail (text, FALSE); + + if (addr_family == AF_UNSPEC) { + g_return_val_if_fail (!out_addr || out_addr_family, FALSE); + addr_family = strchr (text, ':') ? AF_INET6 : AF_INET; + } else + g_return_val_if_fail (NM_IN_SET (addr_family, AF_INET, AF_INET6), FALSE); + + slash = strchr (text, '/'); + if (slash) + addrstr = addrstr_free = g_strndup (text, slash - text); + else + addrstr = text; + + if (inet_pton (addr_family, addrstr, &addrbin) != 1) + return FALSE; + + if (slash) { + /* For IPv4, `ip addr add` supports the prefix-length as a netmask. We don't + * do that. */ + prefix = _nm_utils_ascii_str_to_int64 (slash + 1, 10, + 0, + addr_family == AF_INET ? 32 : 128, + -1); + if (prefix == -1) + return FALSE; + } + + NM_SET_OUT (out_addr_family, addr_family); + if (out_addr) + nm_ip_addr_set (addr_family, out_addr, &addrbin); + NM_SET_OUT (out_prefix, prefix); + return TRUE; +} + +gboolean +nm_utils_parse_inaddr_prefix (int addr_family, + const char *text, + char **out_addr, + int *out_prefix) +{ + NMIPAddr addrbin; + char addrstr_buf[MAX (INET_ADDRSTRLEN, INET6_ADDRSTRLEN)]; + + if (!nm_utils_parse_inaddr_prefix_bin (addr_family, text, &addr_family, &addrbin, out_prefix)) + return FALSE; + NM_SET_OUT (out_addr, g_strdup (inet_ntop (addr_family, &addrbin, addrstr_buf, sizeof (addrstr_buf)))); + return TRUE; +} + +/*****************************************************************************/ + +/* _nm_utils_ascii_str_to_int64: + * + * A wrapper for g_ascii_strtoll, that checks whether the whole string + * can be successfully converted to a number and is within a given + * range. On any error, @fallback will be returned and %errno will be set + * to a non-zero value. On success, %errno will be set to zero, check %errno + * for errors. Any trailing or leading (ascii) white space is ignored and the + * functions is locale independent. + * + * The function is guaranteed to return a value between @min and @max + * (inclusive) or @fallback. Also, the parsing is rather strict, it does + * not allow for any unrecognized characters, except leading and trailing + * white space. + **/ +gint64 +_nm_utils_ascii_str_to_int64 (const char *str, guint base, gint64 min, gint64 max, gint64 fallback) +{ + gint64 v; + const char *s = NULL; + + if (str) { + while (g_ascii_isspace (str[0])) + str++; + } + if (!str || !str[0]) { + errno = EINVAL; + return fallback; + } + + errno = 0; + v = g_ascii_strtoll (str, (char **) &s, base); + + if (errno != 0) + return fallback; + if (s[0] != '\0') { + while (g_ascii_isspace (s[0])) + s++; + if (s[0] != '\0') { + errno = EINVAL; + return fallback; + } + } + if (v > max || v < min) { + errno = ERANGE; + return fallback; + } + + return v; +} + +guint64 +_nm_utils_ascii_str_to_uint64 (const char *str, guint base, guint64 min, guint64 max, guint64 fallback) +{ + guint64 v; + const char *s = NULL; + + if (str) { + while (g_ascii_isspace (str[0])) + str++; + } + if (!str || !str[0]) { + errno = EINVAL; + return fallback; + } + + errno = 0; + v = g_ascii_strtoull (str, (char **) &s, base); + + if (errno != 0) + return fallback; + if (s[0] != '\0') { + while (g_ascii_isspace (s[0])) + s++; + if (s[0] != '\0') { + errno = EINVAL; + return fallback; + } + } + if (v > max || v < min) { + errno = ERANGE; + return fallback; + } + + if ( v != 0 + && str[0] == '-') { + /* I don't know why, but g_ascii_strtoull() accepts minus signs ("-2" gives 18446744073709551614). + * For "-0" that is OK, but otherwise not. */ + errno = ERANGE; + return fallback; + } + + return v; +} + +/*****************************************************************************/ + +/* like nm_strcmp_p(), suitable for g_ptr_array_sort_with_data(). + * g_ptr_array_sort() just casts nm_strcmp_p() to a function of different + * signature. I guess, in glib there are knowledgeable people that ensure + * that this additional argument doesn't cause problems due to different ABI + * for every architecture that glib supports. + * For NetworkManager, we'd rather avoid such stunts. + **/ +int +nm_strcmp_p_with_data (gconstpointer a, gconstpointer b, gpointer user_data) +{ + const char *s1 = *((const char **) a); + const char *s2 = *((const char **) b); + + return strcmp (s1, s2); +} + +int +nm_cmp_uint32_p_with_data (gconstpointer p_a, gconstpointer p_b, gpointer user_data) +{ + const guint32 a = *((const guint32 *) p_a); + const guint32 b = *((const guint32 *) p_b); + + if (a < b) + return -1; + if (a > b) + return 1; + return 0; +} + +int +nm_cmp_int2ptr_p_with_data (gconstpointer p_a, gconstpointer p_b, gpointer user_data) +{ + /* p_a and p_b are two pointers to a pointer, where the pointer is + * interpreted as a integer using GPOINTER_TO_INT(). + * + * That is the case of a hash-table that uses GINT_TO_POINTER() to + * convert integers as pointers, and the resulting keys-as-array + * array. */ + const int a = GPOINTER_TO_INT (*((gconstpointer *) p_a)); + const int b = GPOINTER_TO_INT (*((gconstpointer *) p_b)); + + if (a < b) + return -1; + if (a > b) + return 1; + return 0; +} + +/*****************************************************************************/ + +const char * +nm_utils_dbus_path_get_last_component (const char *dbus_path) +{ + if (dbus_path) { + dbus_path = strrchr (dbus_path, '/'); + if (dbus_path) + return dbus_path + 1; + } + return NULL; +} + +static gint64 +_dbus_path_component_as_num (const char *p) +{ + gint64 n; + + /* no odd stuff. No leading zeros, only a non-negative, decimal integer. + * + * Otherwise, there would be multiple ways to encode the same number "10" + * and "010". That is just confusing. A number has no leading zeros, + * if it has, it's not a number (as far as we are concerned here). */ + if (p[0] == '0') { + if (p[1] != '\0') + return -1; + else + return 0; + } + if (!(p[0] >= '1' && p[0] <= '9')) + return -1; + if (!NM_STRCHAR_ALL (&p[1], ch, (ch >= '0' && ch <= '9'))) + return -1; + n = _nm_utils_ascii_str_to_int64 (p, 10, 0, G_MAXINT64, -1); + nm_assert (n == -1 || nm_streq0 (p, nm_sprintf_bufa (100, "%"G_GINT64_FORMAT, n))); + return n; +} + +int +nm_utils_dbus_path_cmp (const char *dbus_path_a, const char *dbus_path_b) +{ + const char *l_a, *l_b; + gsize plen; + gint64 n_a, n_b; + + /* compare function for two D-Bus paths. It behaves like + * strcmp(), except, if both paths have the same prefix, + * and both end in a (positive) number, then the paths + * will be sorted by number. */ + + NM_CMP_SELF (dbus_path_a, dbus_path_b); + + /* if one or both paths have no slash (and no last component) + * compare the full paths directly. */ + if ( !(l_a = nm_utils_dbus_path_get_last_component (dbus_path_a)) + || !(l_b = nm_utils_dbus_path_get_last_component (dbus_path_b))) + goto comp_full; + + /* check if both paths have the same prefix (up to the last-component). */ + plen = l_a - dbus_path_a; + if (plen != (l_b - dbus_path_b)) + goto comp_full; + NM_CMP_RETURN (strncmp (dbus_path_a, dbus_path_b, plen)); + + n_a = _dbus_path_component_as_num (l_a); + n_b = _dbus_path_component_as_num (l_b); + if (n_a == -1 && n_b == -1) + goto comp_l; + + /* both components must be convertiable to a number. If they are not, + * (and only one of them is), then we must always strictly sort numeric parts + * after non-numeric components. If we wouldn't, we wouldn't have + * a total order. + * + * An example of a not total ordering would be: + * "8" < "010" (numeric) + * "0x" < "8" (lexical) + * "0x" > "010" (lexical) + * We avoid this, by forcing that a non-numeric entry "0x" always sorts + * before numeric entries. + * + * Additionally, _dbus_path_component_as_num() would also reject "010" as + * not a valid number. + */ + if (n_a == -1) + return -1; + if (n_b == -1) + return 1; + + NM_CMP_DIRECT (n_a, n_b); + nm_assert (nm_streq (dbus_path_a, dbus_path_b)); + return 0; + +comp_full: + NM_CMP_DIRECT_STRCMP0 (dbus_path_a, dbus_path_b); + return 0; +comp_l: + NM_CMP_DIRECT_STRCMP0 (l_a, l_b); + nm_assert (nm_streq (dbus_path_a, dbus_path_b)); + return 0; +} + +/*****************************************************************************/ + +static void +_char_lookup_table_init (guint8 lookup[static 256], + const char *candidates) +{ + memset (lookup, 0, 256); + while (candidates[0] != '\0') + lookup[(guint8) ((candidates++)[0])] = 1; +} + +static gboolean +_char_lookup_has (const guint8 lookup[static 256], + char ch) +{ + nm_assert (lookup[(guint8) '\0'] == 0); + return lookup[(guint8) ch] != 0; +} + +/** + * nm_utils_strsplit_set_full: + * @str: the string to split. + * @delimiters: the set of delimiters. + * @flags: additional flags for controlling the operation. + * + * This is a replacement for g_strsplit_set() which avoids copying + * each word once (the entire strv array), but instead copies it once + * and all words point into that internal copy. + * + * Note that for @str %NULL and "", this always returns %NULL too. That differs + * from g_strsplit_set(), which would return an empty strv array for "". + * + * Note that g_strsplit_set() returns empty words as well. By default, + * nm_utils_strsplit_set_full() strips all empty tokens (that is, repeated + * delimiters. With %NM_UTILS_STRSPLIT_SET_FLAGS_PRESERVE_EMPTY, empty tokens + * are not removed. + * + * If @flags has %NM_UTILS_STRSPLIT_SET_FLAGS_ALLOW_ESCAPING, delimiters prefixed + * by a backslash are not treated as a separator. Such delimiters and their escape + * character are copied to the current word without unescaping them. In general, + * nm_utils_strsplit_set_full() does not remove any backslash escape characters + * and does not unescaping. It only considers them for skipping to split at + * an escaped delimiter. + * + * Returns: %NULL if @str is %NULL or "". + * If @str only contains delimiters and %NM_UTILS_STRSPLIT_SET_FLAGS_PRESERVE_EMPTY + * is not set, it also returns %NULL. + * Otherwise, a %NULL terminated strv array containing the split words. + * (delimiter characters are removed). + * The strings to which the result strv array points to are allocated + * after the returned result itself. Don't free the strings themself, + * but free everything with g_free(). + * It is however safe and allowed to modify the indiviual strings, + * like "g_strstrip((char *) iter[0])". + */ +const char ** +nm_utils_strsplit_set_full (const char *str, + const char *delimiters, + NMUtilsStrsplitSetFlags flags) +{ + const char **ptr; + gsize num_tokens; + gsize i_token; + gsize str_len_p1; + const char *c_str; + char *s; + guint8 ch_lookup[256]; + const gboolean f_escaped = NM_FLAGS_HAS (flags, NM_UTILS_STRSPLIT_SET_FLAGS_ESCAPED); + const gboolean f_allow_escaping = f_escaped || NM_FLAGS_HAS (flags, NM_UTILS_STRSPLIT_SET_FLAGS_ALLOW_ESCAPING); + const gboolean f_preserve_empty = NM_FLAGS_HAS (flags, NM_UTILS_STRSPLIT_SET_FLAGS_PRESERVE_EMPTY); + const gboolean f_strstrip = NM_FLAGS_HAS (flags, NM_UTILS_STRSPLIT_SET_FLAGS_STRSTRIP); + + if (!str) + return NULL; + + if (!delimiters) { + nm_assert_not_reached (); + delimiters = " \t\n"; + } + _char_lookup_table_init (ch_lookup, delimiters); + + nm_assert ( !f_allow_escaping + || !_char_lookup_has (ch_lookup, '\\')); + + if (!f_preserve_empty) { + while (_char_lookup_has (ch_lookup, str[0])) + str++; + } + + if (!str[0]) { + /* We return %NULL here, also with NM_UTILS_STRSPLIT_SET_FLAGS_PRESERVE_EMPTY. + * That makes nm_utils_strsplit_set_full() with NM_UTILS_STRSPLIT_SET_FLAGS_PRESERVE_EMPTY + * different from g_strsplit_set(), which would in this case return an empty array. + * If you need to handle %NULL, and "" specially, then check the input string first. */ + return NULL; + } + +#define _char_is_escaped(str_start, str_cur) \ + ({ \ + const char *const _str_start = (str_start); \ + const char *const _str_cur = (str_cur); \ + const char *_str_i = (_str_cur); \ + \ + while ( _str_i > _str_start \ + && _str_i[-1] == '\\') \ + _str_i--; \ + (((_str_cur - _str_i) % 2) != 0); \ + }) + + num_tokens = 1; + c_str = str; + while (TRUE) { + + while (G_LIKELY (!_char_lookup_has (ch_lookup, c_str[0]))) { + if (c_str[0] == '\0') + goto done1; + c_str++; + } + + /* we assume escapings are not frequent. After we found + * this delimiter, check whether it was escaped by counting + * the backslashed before. */ + if ( f_allow_escaping + && _char_is_escaped (str, c_str)) { + /* the delimiter is escaped. This was not an accepted delimiter. */ + c_str++; + continue; + } + + c_str++; + + /* if we drop empty tokens, then we now skip over all consecutive delimiters. */ + if (!f_preserve_empty) { + while (_char_lookup_has (ch_lookup, c_str[0])) + c_str++; + if (c_str[0] == '\0') + break; + } + + num_tokens++; + } + +done1: + + nm_assert (c_str[0] == '\0'); + + str_len_p1 = (c_str - str) + 1; + + nm_assert (str[str_len_p1 - 1] == '\0'); + + ptr = g_malloc ((sizeof (const char *) * (num_tokens + 1)) + str_len_p1); + s = (char *) &ptr[num_tokens + 1]; + memcpy (s, str, str_len_p1); + + i_token = 0; + + while (TRUE) { + + nm_assert (i_token < num_tokens); + ptr[i_token++] = s; + + if (s[0] == '\0') { + nm_assert (f_preserve_empty); + goto done2; + } + nm_assert ( f_preserve_empty + || !_char_lookup_has (ch_lookup, s[0])); + + while (!_char_lookup_has (ch_lookup, s[0])) { + if (G_UNLIKELY ( s[0] == '\\' + && f_allow_escaping)) { + s++; + if (s[0] == '\0') + goto done2; + s++; + } else if (s[0] == '\0') + goto done2; + else + s++; + } + + nm_assert (_char_lookup_has (ch_lookup, s[0])); + s[0] = '\0'; + s++; + + if (!f_preserve_empty) { + while (_char_lookup_has (ch_lookup, s[0])) + s++; + if (s[0] == '\0') + goto done2; + } + } + +done2: + nm_assert (i_token == num_tokens); + ptr[i_token] = NULL; + + if (f_strstrip) { + gsize i; + + i_token = 0; + for (i = 0; ptr[i]; i++) { + + s = (char *) nm_str_skip_leading_spaces (ptr[i]); + if (s[0] != '\0') { + char *s_last; + + s_last = &s[strlen (s) - 1]; + while ( s_last > s + && g_ascii_isspace (s_last[0]) + && ( ! f_allow_escaping + || !_char_is_escaped (s, s_last))) + (s_last--)[0] = '\0'; + } + + if ( !f_preserve_empty + && s[0] == '\0') + continue; + + ptr[i_token++] = s; + } + + if (i_token == 0) { + g_free (ptr); + return NULL; + } + ptr[i_token] = NULL; + } + + if (f_escaped) { + gsize i, j; + + /* We no longer need ch_lookup for its original purpose. Modify it, so it + * can detect the delimiters, '\\', and (optionally) whitespaces. */ + ch_lookup[((guint8) '\\')] = 1; + if (f_strstrip) { + for (i = 0; NM_ASCII_SPACES[i]; i++) + ch_lookup[((guint8) (NM_ASCII_SPACES[i]))] = 1; + } + + for (i_token = 0; ptr[i_token]; i_token++) { + s = (char *) ptr[i_token]; + j = 0; + for (i = 0; s[i] != '\0'; ) { + if ( s[i] == '\\' + && _char_lookup_has (ch_lookup, s[i + 1])) + i++; + s[j++] = s[i++]; + } + s[j] = '\0'; + } + } + + return ptr; +} + +/*****************************************************************************/ + +const char * +nm_utils_escaped_tokens_escape (const char *str, + const char *delimiters, + char **out_to_free) +{ + guint8 ch_lookup[256]; + char *ret; + gsize str_len; + gsize alloc_len; + gsize n_escapes; + gsize i, j; + gboolean escape_trailing_space; + + if (!delimiters) { + nm_assert (delimiters); + delimiters = NM_ASCII_SPACES; + } + + if (!str || str[0] == '\0') { + *out_to_free = NULL; + return str; + } + + _char_lookup_table_init (ch_lookup, delimiters); + + /* also mark '\\' as requiring escaping. */ + ch_lookup[((guint8) '\\')] = 1; + + n_escapes = 0; + for (i = 0; str[i] != '\0'; i++) { + if (_char_lookup_has (ch_lookup, str[i])) + n_escapes++; + } + + str_len = i; + nm_assert (str_len > 0 && strlen (str) == str_len); + + escape_trailing_space = !_char_lookup_has (ch_lookup, str[str_len - 1]) + && g_ascii_isspace (str[str_len - 1]); + + if ( n_escapes == 0 + && !escape_trailing_space) { + *out_to_free = NULL; + return str; + } + + alloc_len = str_len + n_escapes + ((gsize) escape_trailing_space) + 1; + ret = g_new (char, alloc_len); + + j = 0; + for (i = 0; str[i] != '\0'; i++) { + if (_char_lookup_has (ch_lookup, str[i])) { + nm_assert (j < alloc_len); + ret[j++] = '\\'; + } + nm_assert (j < alloc_len); + ret[j++] = str[i]; + } + if (escape_trailing_space) { + nm_assert (!_char_lookup_has (ch_lookup, ret[j - 1]) && g_ascii_isspace (ret[j - 1])); + ret[j] = ret[j - 1]; + ret[j - 1] = '\\'; + j++; + } + + nm_assert (j == alloc_len - 1); + ret[j] = '\0'; + + *out_to_free = ret; + return ret; +} + +/*****************************************************************************/ + +/** + * nm_utils_strv_find_first: + * @list: the strv list to search + * @len: the length of the list, or a negative value if @list is %NULL terminated. + * @needle: the value to search for. The search is done using strcmp(). + * + * Searches @list for @needle and returns the index of the first match (based + * on strcmp()). + * + * For convenience, @list has type 'char**' instead of 'const char **'. + * + * Returns: index of first occurrence or -1 if @needle is not found in @list. + */ +gssize +nm_utils_strv_find_first (char **list, gssize len, const char *needle) +{ + gssize i; + + if (len > 0) { + g_return_val_if_fail (list, -1); + + if (!needle) { + /* if we search a list with known length, %NULL is a valid @needle. */ + for (i = 0; i < len; i++) { + if (!list[i]) + return i; + } + } else { + for (i = 0; i < len; i++) { + if (list[i] && !strcmp (needle, list[i])) + return i; + } + } + } else if (len < 0) { + g_return_val_if_fail (needle, -1); + + if (list) { + for (i = 0; list[i]; i++) { + if (strcmp (needle, list[i]) == 0) + return i; + } + } + } + return -1; +} + +char ** +_nm_utils_strv_cleanup (char **strv, + gboolean strip_whitespace, + gboolean skip_empty, + gboolean skip_repeated) +{ + guint i, j; + + if (!strv || !*strv) + return strv; + + if (strip_whitespace) { + for (i = 0; strv[i]; i++) + g_strstrip (strv[i]); + } + if (!skip_empty && !skip_repeated) + return strv; + j = 0; + for (i = 0; strv[i]; i++) { + if ( (skip_empty && !*strv[i]) + || (skip_repeated && nm_utils_strv_find_first (strv, j, strv[i]) >= 0)) + g_free (strv[i]); + else + strv[j++] = strv[i]; + } + strv[j] = NULL; + return strv; +} + +/*****************************************************************************/ + +int +_nm_utils_ascii_str_to_bool (const char *str, + int default_value) +{ + gs_free char *str_free = NULL; + + if (!str) + return default_value; + + str = nm_strstrip_avoid_copy_a (300, str, &str_free); + if (str[0] == '\0') + return default_value; + + if ( !g_ascii_strcasecmp (str, "true") + || !g_ascii_strcasecmp (str, "yes") + || !g_ascii_strcasecmp (str, "on") + || !g_ascii_strcasecmp (str, "1")) + return TRUE; + + if ( !g_ascii_strcasecmp (str, "false") + || !g_ascii_strcasecmp (str, "no") + || !g_ascii_strcasecmp (str, "off") + || !g_ascii_strcasecmp (str, "0")) + return FALSE; + + return default_value; +} + +/*****************************************************************************/ + +NM_CACHED_QUARK_FCN ("nm-utils-error-quark", nm_utils_error_quark) + +void +nm_utils_error_set_cancelled (GError **error, + gboolean is_disposing, + const char *instance_name) +{ + if (is_disposing) { + g_set_error (error, NM_UTILS_ERROR, NM_UTILS_ERROR_CANCELLED_DISPOSING, + "Disposing %s instance", + instance_name && *instance_name ? instance_name : "source"); + } else { + g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_CANCELLED, + "Request cancelled"); + } +} + +gboolean +nm_utils_error_is_cancelled (GError *error, + gboolean consider_is_disposing) +{ + if (error) { + if (error->domain == G_IO_ERROR) + return NM_IN_SET (error->code, G_IO_ERROR_CANCELLED); + if (consider_is_disposing) { + if (error->domain == NM_UTILS_ERROR) + return NM_IN_SET (error->code, NM_UTILS_ERROR_CANCELLED_DISPOSING); + } + } + return FALSE; +} + +gboolean +nm_utils_error_is_notfound (GError *error) +{ + if (error) { + if (error->domain == G_IO_ERROR) + return NM_IN_SET (error->code, G_IO_ERROR_NOT_FOUND); + if (error->domain == G_FILE_ERROR) + return NM_IN_SET (error->code, G_FILE_ERROR_NOENT); + } + return FALSE; +} + +/*****************************************************************************/ + +/** + * nm_g_object_set_property: + * @object: the target object + * @property_name: the property name + * @value: the #GValue to set + * @error: (allow-none): optional error argument + * + * A reimplementation of g_object_set_property(), but instead + * returning an error instead of logging a warning. All g_object_set*() + * versions in glib require you to not pass invalid types or they will + * log a g_warning() -- without reporting an error. We don't want that, + * so we need to hack error checking around it. + * + * Returns: whether the value was successfully set. + */ +gboolean +nm_g_object_set_property (GObject *object, + const char *property_name, + const GValue *value, + GError **error) +{ + GParamSpec *pspec; + nm_auto_unset_gvalue GValue tmp_value = G_VALUE_INIT; + GObjectClass *klass; + + g_return_val_if_fail (G_IS_OBJECT (object), FALSE); + g_return_val_if_fail (property_name != NULL, FALSE); + g_return_val_if_fail (G_IS_VALUE (value), FALSE); + g_return_val_if_fail (!error || !*error, FALSE); + + /* g_object_class_find_property() does g_param_spec_get_redirect_target(), + * where we differ from a plain g_object_set_property(). */ + pspec = g_object_class_find_property (G_OBJECT_GET_CLASS (object), property_name); + + if (!pspec) { + g_set_error (error, NM_UTILS_ERROR, NM_UTILS_ERROR_UNKNOWN, + _("object class '%s' has no property named '%s'"), + G_OBJECT_TYPE_NAME (object), + property_name); + return FALSE; + } + if (!(pspec->flags & G_PARAM_WRITABLE)) { + g_set_error (error, NM_UTILS_ERROR, NM_UTILS_ERROR_UNKNOWN, + _("property '%s' of object class '%s' is not writable"), + pspec->name, + G_OBJECT_TYPE_NAME (object)); + return FALSE; + } + if ((pspec->flags & G_PARAM_CONSTRUCT_ONLY)) { + g_set_error (error, NM_UTILS_ERROR, NM_UTILS_ERROR_UNKNOWN, + _("construct property \"%s\" for object '%s' can't be set after construction"), + pspec->name, G_OBJECT_TYPE_NAME (object)); + return FALSE; + } + + klass = g_type_class_peek (pspec->owner_type); + if (klass == NULL) { + g_set_error (error, NM_UTILS_ERROR, NM_UTILS_ERROR_UNKNOWN, + _("'%s::%s' is not a valid property name; '%s' is not a GObject subtype"), + g_type_name (pspec->owner_type), pspec->name, g_type_name (pspec->owner_type)); + return FALSE; + } + + /* provide a copy to work from, convert (if necessary) and validate */ + g_value_init (&tmp_value, pspec->value_type); + if (!g_value_transform (value, &tmp_value)) { + g_set_error (error, NM_UTILS_ERROR, NM_UTILS_ERROR_UNKNOWN, + _("unable to set property '%s' of type '%s' from value of type '%s'"), + pspec->name, + g_type_name (pspec->value_type), + G_VALUE_TYPE_NAME (value)); + return FALSE; + } + if ( g_param_value_validate (pspec, &tmp_value) + && !(pspec->flags & G_PARAM_LAX_VALIDATION)) { + gs_free char *contents = g_strdup_value_contents (value); + + g_set_error (error, NM_UTILS_ERROR, NM_UTILS_ERROR_UNKNOWN, + _("value \"%s\" of type '%s' is invalid or out of range for property '%s' of type '%s'"), + contents, + G_VALUE_TYPE_NAME (value), + pspec->name, + g_type_name (pspec->value_type)); + return FALSE; + } + + g_object_set_property (object, property_name, &tmp_value); + return TRUE; +} + +#define _set_property(object, property_name, gtype, gtype_set, value, error) \ + G_STMT_START { \ + nm_auto_unset_gvalue GValue gvalue = { 0 }; \ + \ + g_value_init (&gvalue, gtype); \ + gtype_set (&gvalue, (value)); \ + return nm_g_object_set_property ((object), (property_name), &gvalue, (error)); \ + } G_STMT_END + +gboolean +nm_g_object_set_property_string (GObject *object, + const char *property_name, + const char *value, + GError **error) +{ + _set_property (object, property_name, G_TYPE_STRING, g_value_set_string, value, error); +} + +gboolean +nm_g_object_set_property_string_static (GObject *object, + const char *property_name, + const char *value, + GError **error) +{ + _set_property (object, property_name, G_TYPE_STRING, g_value_set_static_string, value, error); +} + +gboolean +nm_g_object_set_property_string_take (GObject *object, + const char *property_name, + char *value, + GError **error) +{ + _set_property (object, property_name, G_TYPE_STRING, g_value_take_string, value, error); +} + +gboolean +nm_g_object_set_property_boolean (GObject *object, + const char *property_name, + gboolean value, + GError **error) +{ + _set_property (object, property_name, G_TYPE_BOOLEAN, g_value_set_boolean, !!value, error); +} + +gboolean +nm_g_object_set_property_char (GObject *object, + const char *property_name, + gint8 value, + GError **error) +{ + /* glib says about G_TYPE_CHAR: + * + * The type designated by G_TYPE_CHAR is unconditionally an 8-bit signed integer. + * + * This is always a (signed!) char. */ + _set_property (object, property_name, G_TYPE_CHAR, g_value_set_schar, value, error); +} + +gboolean +nm_g_object_set_property_uchar (GObject *object, + const char *property_name, + guint8 value, + GError **error) +{ + _set_property (object, property_name, G_TYPE_UCHAR, g_value_set_uchar, value, error); +} + +gboolean +nm_g_object_set_property_int (GObject *object, + const char *property_name, + int value, + GError **error) +{ + _set_property (object, property_name, G_TYPE_INT, g_value_set_int, value, error); +} + +gboolean +nm_g_object_set_property_int64 (GObject *object, + const char *property_name, + gint64 value, + GError **error) +{ + _set_property (object, property_name, G_TYPE_INT64, g_value_set_int64, value, error); +} + +gboolean +nm_g_object_set_property_uint (GObject *object, + const char *property_name, + guint value, + GError **error) +{ + _set_property (object, property_name, G_TYPE_UINT, g_value_set_uint, value, error); +} + +gboolean +nm_g_object_set_property_uint64 (GObject *object, + const char *property_name, + guint64 value, + GError **error) +{ + _set_property (object, property_name, G_TYPE_UINT64, g_value_set_uint64, value, error); +} + +gboolean +nm_g_object_set_property_flags (GObject *object, + const char *property_name, + GType gtype, + guint value, + GError **error) +{ + nm_assert (({ + nm_auto_unref_gtypeclass GTypeClass *gtypeclass = g_type_class_ref (gtype); + G_IS_FLAGS_CLASS (gtypeclass); + })); + _set_property (object, property_name, gtype, g_value_set_flags, value, error); +} + +gboolean +nm_g_object_set_property_enum (GObject *object, + const char *property_name, + GType gtype, + int value, + GError **error) +{ + nm_assert (({ + nm_auto_unref_gtypeclass GTypeClass *gtypeclass = g_type_class_ref (gtype); + G_IS_ENUM_CLASS (gtypeclass); + })); + _set_property (object, property_name, gtype, g_value_set_enum, value, error); +} + +GParamSpec * +nm_g_object_class_find_property_from_gtype (GType gtype, + const char *property_name) +{ + nm_auto_unref_gtypeclass GObjectClass *gclass = NULL; + + gclass = g_type_class_ref (gtype); + return g_object_class_find_property (gclass, property_name); +} + +/*****************************************************************************/ + +/** + * nm_g_type_find_implementing_class_for_property: + * @gtype: the GObject type which has a property @pname + * @pname: the name of the property to look up + * + * This is only a helper function for printf debugging. It's not + * used in actual code. Hence, the function just asserts that + * @pname and @gtype arguments are suitable. It cannot fail. + * + * Returns: the most ancestor type of @gtype, that + * implements the property @pname. It means, it + * searches the type hierarchy to find the type + * that added @pname. + */ +GType +nm_g_type_find_implementing_class_for_property (GType gtype, + const char *pname) +{ + nm_auto_unref_gtypeclass GObjectClass *klass = NULL; + GParamSpec *pspec; + + g_return_val_if_fail (pname, G_TYPE_INVALID); + + klass = g_type_class_ref (gtype); + g_return_val_if_fail (G_IS_OBJECT_CLASS (klass), G_TYPE_INVALID); + + pspec = g_object_class_find_property (klass, pname); + g_return_val_if_fail (pspec, G_TYPE_INVALID); + + gtype = G_TYPE_FROM_CLASS (klass); + + while (TRUE) { + nm_auto_unref_gtypeclass GObjectClass *k = NULL; + + k = g_type_class_ref (g_type_parent (gtype)); + + g_return_val_if_fail (G_IS_OBJECT_CLASS (k), G_TYPE_INVALID); + + if (g_object_class_find_property (k, pname) != pspec) + return gtype; + + gtype = G_TYPE_FROM_CLASS (k); + } +} + +/*****************************************************************************/ + +static void +_str_append_escape (GString *s, char ch) +{ + g_string_append_c (s, '\\'); + g_string_append_c (s, '0' + ((((guchar) ch) >> 6) & 07)); + g_string_append_c (s, '0' + ((((guchar) ch) >> 3) & 07)); + g_string_append_c (s, '0' + ( ((guchar) ch) & 07)); +} + +gconstpointer +nm_utils_buf_utf8safe_unescape (const char *str, gsize *out_len, gpointer *to_free) +{ + GString *gstr; + gsize len; + const char *s; + + g_return_val_if_fail (to_free, NULL); + g_return_val_if_fail (out_len, NULL); + + if (!str) { + *out_len = 0; + *to_free = NULL; + return NULL; + } + + len = strlen (str); + + s = memchr (str, '\\', len); + if (!s) { + *out_len = len; + *to_free = NULL; + return str; + } + + gstr = g_string_new_len (NULL, len); + + g_string_append_len (gstr, str, s - str); + str = s; + + for (;;) { + char ch; + guint v; + + nm_assert (str[0] == '\\'); + + ch = (++str)[0]; + + if (ch == '\0') { + // error. Trailing '\\' + break; + } + + if (ch >= '0' && ch <= '9') { + v = ch - '0'; + ch = (++str)[0]; + if (ch >= '0' && ch <= '7') { + v = v * 8 + (ch - '0'); + ch = (++str)[0]; + if (ch >= '0' && ch <= '7') { + v = v * 8 + (ch - '0'); + ++str; + } + } + ch = v; + } else { + switch (ch) { + case 'b': ch = '\b'; break; + case 'f': ch = '\f'; break; + case 'n': ch = '\n'; break; + case 'r': ch = '\r'; break; + case 't': ch = '\t'; break; + case 'v': ch = '\v'; break; + default: + /* Here we handle "\\\\", but all other unexpected escape sequences are really a bug. + * Take them literally, after removing the escape character */ + break; + } + str++; + } + + g_string_append_c (gstr, ch); + + s = strchr (str, '\\'); + if (!s) { + g_string_append (gstr, str); + break; + } + + g_string_append_len (gstr, str, s - str); + str = s; + } + + *out_len = gstr->len; + *to_free = gstr->str; + return g_string_free (gstr, FALSE); +} + +/** + * nm_utils_buf_utf8safe_escape: + * @buf: byte array, possibly in utf-8 encoding, may have NUL characters. + * @buflen: the length of @buf in bytes, or -1 if @buf is a NUL terminated + * string. + * @flags: #NMUtilsStrUtf8SafeFlags flags + * @to_free: (out): return the pointer location of the string + * if a copying was necessary. + * + * Based on the assumption, that @buf contains UTF-8 encoded bytes, + * this will return valid UTF-8 sequence, and invalid sequences + * will be escaped with backslash (C escaping, like g_strescape()). + * This is sanitize non UTF-8 characters. The result is valid + * UTF-8. + * + * The operation can be reverted with nm_utils_buf_utf8safe_unescape(). + * Note that if, and only if @buf contains no NUL bytes, the operation + * can also be reverted with g_strcompress(). + * + * Depending on @flags, valid UTF-8 characters are not escaped at all + * (except the escape character '\\'). This is the difference to g_strescape(), + * which escapes all non-ASCII characters. This allows to pass on + * valid UTF-8 characters as-is and can be directly shown to the user + * as UTF-8 -- with exception of the backslash escape character, + * invalid UTF-8 sequences, and other (depending on @flags). + * + * Returns: the escaped input buffer, as valid UTF-8. If no escaping + * is necessary, it returns the input @buf. Otherwise, an allocated + * string @to_free is returned which must be freed by the caller + * with g_free. The escaping can be reverted by g_strcompress(). + **/ +const char * +nm_utils_buf_utf8safe_escape (gconstpointer buf, gssize buflen, NMUtilsStrUtf8SafeFlags flags, char **to_free) +{ + const char *const str = buf; + const char *p = NULL; + const char *s; + gboolean nul_terminated = FALSE; + GString *gstr; + + g_return_val_if_fail (to_free, NULL); + + *to_free = NULL; + + if (buflen == 0) + return NULL; + + if (buflen < 0) { + if (!str) + return NULL; + buflen = strlen (str); + if (buflen == 0) + return str; + nul_terminated = TRUE; + } + + if ( g_utf8_validate (str, buflen, &p) + && nul_terminated) { + /* note that g_utf8_validate() does not allow NUL character inside @str. Good. + * We can treat @str like a NUL terminated string. */ + if (!NM_STRCHAR_ANY (str, ch, + ( ch == '\\' \ + || ( NM_FLAGS_HAS (flags, NM_UTILS_STR_UTF8_SAFE_FLAG_ESCAPE_CTRL) \ + && ch < ' ') \ + || ( NM_FLAGS_HAS (flags, NM_UTILS_STR_UTF8_SAFE_FLAG_ESCAPE_NON_ASCII) \ + && ((guchar) ch) >= 127)))) + return str; + } + + gstr = g_string_sized_new (buflen + 5); + + s = str; + do { + buflen -= p - s; + nm_assert (buflen >= 0); + + for (; s < p; s++) { + char ch = s[0]; + + if (ch == '\\') + g_string_append (gstr, "\\\\"); + else if ( ( NM_FLAGS_HAS (flags, NM_UTILS_STR_UTF8_SAFE_FLAG_ESCAPE_CTRL) \ + && ch < ' ') \ + || ( NM_FLAGS_HAS (flags, NM_UTILS_STR_UTF8_SAFE_FLAG_ESCAPE_NON_ASCII) \ + && ((guchar) ch) >= 127)) + _str_append_escape (gstr, ch); + else + g_string_append_c (gstr, ch); + } + + if (buflen <= 0) + break; + + _str_append_escape (gstr, p[0]); + + buflen--; + if (buflen == 0) + break; + + s = &p[1]; + g_utf8_validate (s, buflen, &p); + } while (TRUE); + + *to_free = g_string_free (gstr, FALSE); + return *to_free; +} + +const char * +nm_utils_buf_utf8safe_escape_bytes (GBytes *bytes, NMUtilsStrUtf8SafeFlags flags, char **to_free) +{ + gconstpointer p; + gsize l; + + if (bytes) + p = g_bytes_get_data (bytes, &l); + else { + p = NULL; + l = 0; + } + + return nm_utils_buf_utf8safe_escape (p, l, flags, to_free); +} + +/*****************************************************************************/ + +const char * +nm_utils_str_utf8safe_unescape (const char *str, char **to_free) +{ + g_return_val_if_fail (to_free, NULL); + + if (!str || !strchr (str, '\\')) { + *to_free = NULL; + return str; + } + return (*to_free = g_strcompress (str)); +} + +/** + * nm_utils_str_utf8safe_escape: + * @str: NUL terminated input string, possibly in utf-8 encoding + * @flags: #NMUtilsStrUtf8SafeFlags flags + * @to_free: (out): return the pointer location of the string + * if a copying was necessary. + * + * Returns the possible non-UTF-8 NUL terminated string @str + * and uses backslash escaping (C escaping, like g_strescape()) + * to sanitize non UTF-8 characters. The result is valid + * UTF-8. + * + * The operation can be reverted with g_strcompress() or + * nm_utils_str_utf8safe_unescape(). + * + * Depending on @flags, valid UTF-8 characters are not escaped at all + * (except the escape character '\\'). This is the difference to g_strescape(), + * which escapes all non-ASCII characters. This allows to pass on + * valid UTF-8 characters as-is and can be directly shown to the user + * as UTF-8 -- with exception of the backslash escape character, + * invalid UTF-8 sequences, and other (depending on @flags). + * + * Returns: the escaped input string, as valid UTF-8. If no escaping + * is necessary, it returns the input @str. Otherwise, an allocated + * string @to_free is returned which must be freed by the caller + * with g_free. The escaping can be reverted by g_strcompress(). + **/ +const char * +nm_utils_str_utf8safe_escape (const char *str, NMUtilsStrUtf8SafeFlags flags, char **to_free) +{ + return nm_utils_buf_utf8safe_escape (str, -1, flags, to_free); +} + +/** + * nm_utils_str_utf8safe_escape_cp: + * @str: NUL terminated input string, possibly in utf-8 encoding + * @flags: #NMUtilsStrUtf8SafeFlags flags + * + * Like nm_utils_str_utf8safe_escape(), except the returned value + * is always a copy of the input and must be freed by the caller. + * + * Returns: the escaped input string in UTF-8 encoding. The returned + * value should be freed with g_free(). + * The escaping can be reverted by g_strcompress(). + **/ +char * +nm_utils_str_utf8safe_escape_cp (const char *str, NMUtilsStrUtf8SafeFlags flags) +{ + char *s; + + nm_utils_str_utf8safe_escape (str, flags, &s); + return s ?: g_strdup (str); +} + +char * +nm_utils_str_utf8safe_unescape_cp (const char *str) +{ + return str ? g_strcompress (str) : NULL; +} + +char * +nm_utils_str_utf8safe_escape_take (char *str, NMUtilsStrUtf8SafeFlags flags) +{ + char *str_to_free; + + nm_utils_str_utf8safe_escape (str, flags, &str_to_free); + if (str_to_free) { + g_free (str); + return str_to_free; + } + return str; +} + +/*****************************************************************************/ + +/* taken from systemd's fd_wait_for_event(). Note that the timeout + * is here in nano-seconds, not micro-seconds. */ +int +nm_utils_fd_wait_for_event (int fd, int event, gint64 timeout_ns) +{ + struct pollfd pollfd = { + .fd = fd, + .events = event, + }; + struct timespec ts, *pts; + int r; + + if (timeout_ns < 0) + pts = NULL; + else { + ts.tv_sec = (time_t) (timeout_ns / NM_UTILS_NS_PER_SECOND); + ts.tv_nsec = (long int) (timeout_ns % NM_UTILS_NS_PER_SECOND); + pts = &ts; + } + + r = ppoll (&pollfd, 1, pts, NULL); + if (r < 0) + return -NM_ERRNO_NATIVE (errno); + if (r == 0) + return 0; + return pollfd.revents; +} + +/* taken from systemd's loop_read() */ +ssize_t +nm_utils_fd_read_loop (int fd, void *buf, size_t nbytes, bool do_poll) +{ + uint8_t *p = buf; + ssize_t n = 0; + + g_return_val_if_fail (fd >= 0, -EINVAL); + g_return_val_if_fail (buf, -EINVAL); + + /* If called with nbytes == 0, let's call read() at least + * once, to validate the operation */ + + if (nbytes > (size_t) SSIZE_MAX) + return -EINVAL; + + do { + ssize_t k; + + k = read (fd, p, nbytes); + if (k < 0) { + int errsv = errno; + + if (errsv == EINTR) + continue; + + if (errsv == EAGAIN && do_poll) { + + /* We knowingly ignore any return value here, + * and expect that any error/EOF is reported + * via read() */ + + (void) nm_utils_fd_wait_for_event (fd, POLLIN, -1); + continue; + } + + return n > 0 ? n : -NM_ERRNO_NATIVE (errsv); + } + + if (k == 0) + return n; + + g_assert ((size_t) k <= nbytes); + + p += k; + nbytes -= k; + n += k; + } while (nbytes > 0); + + return n; +} + +/* taken from systemd's loop_read_exact() */ +int +nm_utils_fd_read_loop_exact (int fd, void *buf, size_t nbytes, bool do_poll) +{ + ssize_t n; + + n = nm_utils_fd_read_loop (fd, buf, nbytes, do_poll); + if (n < 0) + return (int) n; + if ((size_t) n != nbytes) + return -EIO; + + return 0; +} + +NMUtilsNamedValue * +nm_utils_named_values_from_str_dict (GHashTable *hash, guint *out_len) +{ + GHashTableIter iter; + NMUtilsNamedValue *values; + guint i, len; + + if ( !hash + || !(len = g_hash_table_size (hash))) { + NM_SET_OUT (out_len, 0); + return NULL; + } + + i = 0; + values = g_new (NMUtilsNamedValue, len + 1); + g_hash_table_iter_init (&iter, hash); + while (g_hash_table_iter_next (&iter, + (gpointer *) &values[i].name, + (gpointer *) &values[i].value_ptr)) + i++; + nm_assert (i == len); + values[i].name = NULL; + values[i].value_ptr = NULL; + + if (len > 1) { + g_qsort_with_data (values, len, sizeof (values[0]), + nm_utils_named_entry_cmp_with_data, NULL); + } + + NM_SET_OUT (out_len, len); + return values; +} + +gpointer * +nm_utils_hash_keys_to_array (GHashTable *hash, + GCompareDataFunc compare_func, + gpointer user_data, + guint *out_len) +{ + guint len; + gpointer *keys; + + /* by convention, we never return an empty array. In that + * case, always %NULL. */ + if ( !hash + || g_hash_table_size (hash) == 0) { + NM_SET_OUT (out_len, 0); + return NULL; + } + + keys = g_hash_table_get_keys_as_array (hash, &len); + if ( len > 1 + && compare_func) { + g_qsort_with_data (keys, + len, + sizeof (gpointer), + compare_func, + user_data); + } + NM_SET_OUT (out_len, len); + return keys; +} + +char ** +nm_utils_strv_make_deep_copied (const char **strv) +{ + gsize i; + + /* it takes a strv dictionary, and copies each + * strings. Note that this updates @strv *in-place* + * and returns it. */ + + if (!strv) + return NULL; + for (i = 0; strv[i]; i++) + strv[i] = g_strdup (strv[i]); + + return (char **) strv; +} + +/*****************************************************************************/ + +gssize +nm_utils_ptrarray_find_binary_search (gconstpointer *list, + gsize len, + gconstpointer needle, + GCompareDataFunc cmpfcn, + gpointer user_data, + gssize *out_idx_first, + gssize *out_idx_last) +{ + gssize imin, imax, imid, i2min, i2max, i2mid; + int cmp; + + g_return_val_if_fail (list || !len, ~((gssize) 0)); + g_return_val_if_fail (cmpfcn, ~((gssize) 0)); + + imin = 0; + if (len > 0) { + imax = len - 1; + + while (imin <= imax) { + imid = imin + (imax - imin) / 2; + + cmp = cmpfcn (list[imid], needle, user_data); + if (cmp == 0) { + /* we found a matching entry at index imid. + * + * Does the caller request the first/last index as well (in case that + * there are multiple entries which compare equal). */ + + if (out_idx_first) { + i2min = imin; + i2max = imid + 1; + while (i2min <= i2max) { + i2mid = i2min + (i2max - i2min) / 2; + + cmp = cmpfcn (list[i2mid], needle, user_data); + if (cmp == 0) + i2max = i2mid -1; + else { + nm_assert (cmp < 0); + i2min = i2mid + 1; + } + } + *out_idx_first = i2min; + } + if (out_idx_last) { + i2min = imid + 1; + i2max = imax; + while (i2min <= i2max) { + i2mid = i2min + (i2max - i2min) / 2; + + cmp = cmpfcn (list[i2mid], needle, user_data); + if (cmp == 0) + i2min = i2mid + 1; + else { + nm_assert (cmp > 0); + i2max = i2mid - 1; + } + } + *out_idx_last = i2min - 1; + } + return imid; + } + + if (cmp < 0) + imin = imid + 1; + else + imax = imid - 1; + } + } + + /* return the inverse of @imin. This is a negative number, but + * also is ~imin the position where the value should be inserted. */ + imin = ~imin; + NM_SET_OUT (out_idx_first, imin); + NM_SET_OUT (out_idx_last, imin); + return imin; +} + +/*****************************************************************************/ + +/** + * nm_utils_array_find_binary_search: + * @list: the list to search. It must be sorted according to @cmpfcn ordering. + * @elem_size: the size in bytes of each element in the list + * @len: the number of elements in @list + * @needle: the value that is searched + * @cmpfcn: the compare function. The elements @list are passed as first + * argument to @cmpfcn, while @needle is passed as second. Usually, the + * needle is the same data type as inside the list, however, that is + * not necessary, as long as @cmpfcn takes care to cast the two arguments + * accordingly. + * @user_data: optional argument passed to @cmpfcn + * + * Performs binary search for @needle in @list. On success, returns the + * (non-negative) index where the compare function found the searched element. + * On success, it returns a negative value. Note that the return negative value + * is the bitwise inverse of the position where the element should be inserted. + * + * If the list contains multiple matching elements, an arbitrary index is + * returned. + * + * Returns: the index to the element in the list, or the (negative, bitwise inverted) + * position where it should be. + */ +gssize +nm_utils_array_find_binary_search (gconstpointer list, + gsize elem_size, + gsize len, + gconstpointer needle, + GCompareDataFunc cmpfcn, + gpointer user_data) +{ + gssize imin, imax, imid; + int cmp; + + g_return_val_if_fail (list || !len, ~((gssize) 0)); + g_return_val_if_fail (cmpfcn, ~((gssize) 0)); + g_return_val_if_fail (elem_size > 0, ~((gssize) 0)); + + imin = 0; + if (len == 0) + return ~imin; + + imax = len - 1; + + while (imin <= imax) { + imid = imin + (imax - imin) / 2; + + cmp = cmpfcn (&((const char *) list)[elem_size * imid], needle, user_data); + if (cmp == 0) + return imid; + + if (cmp < 0) + imin = imid + 1; + else + imax = imid - 1; + } + + /* return the inverse of @imin. This is a negative number, but + * also is ~imin the position where the value should be inserted. */ + return ~imin; +} + +/*****************************************************************************/ + +/** + * 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`. + * @out_ppid: parent process id + * + * Originally copied from polkit source (src/polkit/polkitunixprocess.c) + * and adjusted. + * + * Returns: the timestamp when the process started (by parsing /proc/$PID/stat). + * If an error occurs (e.g. the process does not exist), 0 is returned. + * + * The returned start time counts since boot, in the unit HZ (with HZ usually being (1/100) seconds) + **/ +guint64 +nm_utils_get_start_time_for_pid (pid_t pid, char *out_state, pid_t *out_ppid) +{ + guint64 start_time; + char filename[256]; + gs_free char *contents = NULL; + size_t length; + gs_free const char **tokens = NULL; + char *p; + char state = ' '; + gint64 ppid = 0; + + start_time = 0; + contents = NULL; + + g_return_val_if_fail (pid > 0, 0); + + nm_sprintf_buf (filename, "/proc/%"G_GUINT64_FORMAT"/stat", (guint64) pid); + + if (!g_file_get_contents (filename, &contents, &length, NULL)) + goto fail; + + /* start time is the token at index 19 after the '(process name)' entry - since only this + * field can contain the ')' character, search backwards for this to avoid malicious + * processes trying to fool us + */ + p = strrchr (contents, ')'); + if (!p) + goto fail; + p += 2; /* skip ') ' */ + if (p - contents >= (int) length) + goto fail; + + state = p[0]; + + tokens = nm_utils_strsplit_set (p, " "); + + if (NM_PTRARRAY_LEN (tokens) < 20) + goto fail; + + if (out_ppid) { + ppid = _nm_utils_ascii_str_to_int64 (tokens[1], 10, 1, G_MAXINT, 0); + if (ppid == 0) + goto fail; + } + + start_time = _nm_utils_ascii_str_to_int64 (tokens[19], 10, 1, G_MAXINT64, 0); + if (start_time == 0) + goto fail; + + NM_SET_OUT (out_state, state); + NM_SET_OUT (out_ppid, ppid); + return start_time; + +fail: + NM_SET_OUT (out_state, ' '); + NM_SET_OUT (out_ppid, 0); + return 0; +} + +/*****************************************************************************/ + +/** + * _nm_utils_strv_sort: + * @strv: pointer containing strings that will be sorted + * in-place, %NULL is allowed, unless @len indicates + * that there are more elements. + * @len: the number of elements in strv. If negative, + * strv must be a NULL terminated array and the length + * will be calculated first. If @len is a positive + * number, all first @len elements in @strv must be + * non-NULL, valid strings. + * + * Ascending sort of the array @strv inplace, using plain strcmp() string + * comparison. + */ +void +_nm_utils_strv_sort (const char **strv, gssize len) +{ + gsize l; + + l = len < 0 ? (gsize) NM_PTRARRAY_LEN (strv) : (gsize) len; + + if (l <= 1) + return; + + nm_assert (l <= (gsize) G_MAXINT); + + g_qsort_with_data (strv, + l, + sizeof (const char *), + nm_strcmp_p_with_data, + NULL); +} + +/** + * _nm_utils_strv_cmp_n: + * @strv1: a string array + * @len1: the length of @strv1, or -1 for NULL terminated array. + * @strv2: a string array + * @len2: the length of @strv2, or -1 for NULL terminated array. + * + * Note that + * - len == -1 && strv == NULL + * is treated like a %NULL argument and compares differently from + * other arrays. + * + * Note that an empty array can be represented as + * - len == -1 && strv && !strv[0] + * - len == 0 && !strv + * - len == 0 && strv + * These 3 forms all compare equal. + * It also means, if length is 0, then it is permissible for strv to be %NULL. + * + * The strv arrays may contain %NULL strings (if len is positive). + * + * Returns: 0 if the arrays are equal (using strcmp). + **/ +int +_nm_utils_strv_cmp_n (const char *const*strv1, + gssize len1, + const char *const*strv2, + gssize len2) +{ + gsize n, n2; + + if (len1 < 0) { + if (!strv1) + return (len2 < 0 && !strv2) ? 0 : -1; + n = NM_PTRARRAY_LEN (strv1); + } else + n = len1; + + if (len2 < 0) { + if (!strv2) + return 1; + n2 = NM_PTRARRAY_LEN (strv2); + } else + n2 = len2; + + NM_CMP_DIRECT (n, n2); + for (; n > 0; n--, strv1++, strv2++) + NM_CMP_DIRECT_STRCMP0 (*strv1, *strv2); + return 0; +} + +/*****************************************************************************/ + +gpointer +_nm_utils_user_data_pack (int nargs, gconstpointer *args) +{ + int i; + gpointer *data; + + nm_assert (nargs > 0); + nm_assert (args); + + data = g_slice_alloc (((gsize) nargs) * sizeof (gconstpointer)); + for (i = 0; i < nargs; i++) + data[i] = (gpointer) args[i]; + return data; +} + +void +_nm_utils_user_data_unpack (gpointer user_data, int nargs, ...) +{ + gpointer *data = user_data; + va_list ap; + int i; + + nm_assert (data); + nm_assert (nargs > 0); + + va_start (ap, nargs); + for (i = 0; i < nargs; i++) { + gpointer *dst; + + dst = va_arg (ap, gpointer *); + nm_assert (dst); + + *dst = data[i]; + } + va_end (ap); + + g_slice_free1 (((gsize) nargs) * sizeof (gconstpointer), user_data); +} + +/*****************************************************************************/ + +typedef struct { + gpointer callback_user_data; + GCancellable *cancellable; + NMUtilsInvokeOnIdleCallback callback; + gulong cancelled_id; + guint idle_id; +} InvokeOnIdleData; + +static gboolean +_nm_utils_invoke_on_idle_cb_idle (gpointer user_data) +{ + InvokeOnIdleData *data = user_data; + + data->idle_id = 0; + nm_clear_g_signal_handler (data->cancellable, &data->cancelled_id); + + data->callback (data->callback_user_data, data->cancellable); + nm_g_object_unref (data->cancellable); + g_slice_free (InvokeOnIdleData, data); + return G_SOURCE_REMOVE; +} + +static void +_nm_utils_invoke_on_idle_cb_cancelled (GCancellable *cancellable, + InvokeOnIdleData *data) +{ + /* on cancellation, we invoke the callback synchronously. */ + nm_clear_g_signal_handler (data->cancellable, &data->cancelled_id); + nm_clear_g_source (&data->idle_id); + data->callback (data->callback_user_data, data->cancellable); + nm_g_object_unref (data->cancellable); + g_slice_free (InvokeOnIdleData, data); +} + +void +nm_utils_invoke_on_idle (NMUtilsInvokeOnIdleCallback callback, + gpointer callback_user_data, + GCancellable *cancellable) +{ + InvokeOnIdleData *data; + + g_return_if_fail (callback); + + data = g_slice_new (InvokeOnIdleData); + data->callback = callback; + data->callback_user_data = callback_user_data; + data->cancellable = nm_g_object_ref (cancellable); + if ( cancellable + && !g_cancellable_is_cancelled (cancellable)) { + /* if we are passed a non-cancelled cancellable, we register to the "cancelled" + * signal an invoke the callback synchronously (from the signal handler). + * + * We don't do that, + * - if the cancellable is already cancelled (because we don't want to invoke + * the callback synchronously from the caller). + * - if we have no cancellable at hand. */ + data->cancelled_id = g_signal_connect (cancellable, + "cancelled", + G_CALLBACK (_nm_utils_invoke_on_idle_cb_cancelled), + data); + } else + data->cancelled_id = 0; + data->idle_id = g_idle_add (_nm_utils_invoke_on_idle_cb_idle, data); +} + +/*****************************************************************************/ + +int +nm_utils_getpagesize (void) +{ + static volatile int val = 0; + long l; + int v; + + v = g_atomic_int_get (&val); + + if (G_UNLIKELY (v == 0)) { + l = sysconf (_SC_PAGESIZE); + + g_return_val_if_fail (l > 0 && l < G_MAXINT, 4*1024); + + v = (int) l; + if (!g_atomic_int_compare_and_exchange (&val, 0, v)) { + v = g_atomic_int_get (&val); + g_return_val_if_fail (v > 0, 4*1024); + } + } + + nm_assert (v > 0); +#if NM_MORE_ASSERTS > 5 + nm_assert (v == getpagesize ()); + nm_assert (v == sysconf (_SC_PAGESIZE)); +#endif + + return v; +} + +gboolean +nm_utils_memeqzero (gconstpointer data, gsize length) +{ + const unsigned char *p = data; + int len; + + /* Taken from https://github.com/rustyrussell/ccan/blob/9d2d2c49f053018724bcc6e37029da10b7c3d60d/ccan/mem/mem.c#L92, + * CC-0 licensed. */ + + /* Check first 16 bytes manually */ + for (len = 0; len < 16; len++) { + if (!length) + return TRUE; + if (*p) + return FALSE; + p++; + length--; + } + + /* Now we know that's zero, memcmp with self. */ + return memcmp (data, p, length) == 0; +} + +/** + * nm_utils_bin2hexstr_full: + * @addr: pointer of @length bytes. If @length is zero, this may + * also be %NULL. + * @length: number of bytes in @addr. May also be zero, in which + * case this will return an empty string. + * @delimiter: either '\0', otherwise the output string will have the + * given delimiter character between each two hex numbers. + * @upper_case: if TRUE, use upper case ASCII characters for hex. + * @out: if %NULL, the function will allocate a new buffer of + * either (@length*2+1) or (@length*3) bytes, depending on whether + * a @delimiter is specified. In that case, the allocated buffer will + * be returned and must be freed by the caller. + * If not %NULL, the buffer must already be preallocated and contain + * at least (@length*2+1) or (@length*3) bytes, depending on the delimiter. + * + * Returns: the binary value converted to a hex string. If @out is given, + * this always returns @out. If @out is %NULL, a newly allocated string + * is returned. + */ +char * +nm_utils_bin2hexstr_full (gconstpointer addr, + gsize length, + char delimiter, + gboolean upper_case, + char *out) +{ + const guint8 *in = addr; + const char *LOOKUP = upper_case ? "0123456789ABCDEF" : "0123456789abcdef"; + char *out0; + + if (out) + out0 = out; + else { + out0 = out = g_new (char, delimiter == '\0' + ? length * 2 + 1 + : length * 3); + } + + /* @out must contain at least @length*3 bytes if @delimiter is set, + * otherwise, @length*2+1. */ + + if (length > 0) { + nm_assert (in); + for (;;) { + const guint8 v = *in++; + + *out++ = LOOKUP[v >> 4]; + *out++ = LOOKUP[v & 0x0F]; + length--; + if (!length) + break; + if (delimiter) + *out++ = delimiter; + } + } + + *out = '\0'; + return out0; +} + +guint8 * +nm_utils_hexstr2bin_full (const char *hexstr, + gboolean allow_0x_prefix, + gboolean delimiter_required, + const char *delimiter_candidates, + gsize required_len, + guint8 *buffer, + gsize buffer_len, + gsize *out_len) +{ + const char *in = hexstr; + guint8 *out = buffer; + gboolean delimiter_has = TRUE; + guint8 delimiter = '\0'; + gsize len; + + nm_assert (hexstr); + nm_assert (buffer); + nm_assert (required_len > 0 || out_len); + + if ( allow_0x_prefix + && in[0] == '0' + && in[1] == 'x') + in += 2; + + while (TRUE) { + const guint8 d1 = in[0]; + guint8 d2; + int i1, i2; + + i1 = nm_utils_hexchar_to_int (d1); + if (i1 < 0) + goto fail; + + /* If there's no leading zero (ie "aa:b:cc") then fake it */ + d2 = in[1]; + if ( d2 + && (i2 = nm_utils_hexchar_to_int (d2)) >= 0) { + *out++ = (i1 << 4) + i2; + d2 = in[2]; + if (!d2) + break; + in += 2; + } else { + /* Fake leading zero */ + *out++ = i1; + if (!d2) { + if (!delimiter_has) { + /* when using no delimiter, there must be pairs of hex chars */ + goto fail; + } + break; + } + in += 1; + } + + if (--buffer_len == 0) + goto fail; + + if (delimiter_has) { + if (d2 != delimiter) { + if (delimiter) + goto fail; + if (delimiter_candidates) { + while (delimiter_candidates[0]) { + if (delimiter_candidates++[0] == d2) + delimiter = d2; + } + } + if (!delimiter) { + if (delimiter_required) + goto fail; + delimiter_has = FALSE; + continue; + } + } + in++; + } + } + + len = out - buffer; + if ( required_len == 0 + || len == required_len) { + NM_SET_OUT (out_len, len); + return buffer; + } + +fail: + NM_SET_OUT (out_len, 0); + return NULL; +} + +guint8 * +nm_utils_hexstr2bin_alloc (const char *hexstr, + gboolean allow_0x_prefix, + gboolean delimiter_required, + const char *delimiter_candidates, + gsize required_len, + gsize *out_len) +{ + guint8 *buffer; + gsize buffer_len, len; + + g_return_val_if_fail (hexstr, NULL); + + nm_assert (required_len > 0 || out_len); + + if ( allow_0x_prefix + && hexstr[0] == '0' + && hexstr[1] == 'x') + hexstr += 2; + + if (!hexstr[0]) + goto fail; + + if (required_len > 0) + buffer_len = required_len; + else + buffer_len = strlen (hexstr) / 2 + 3; + + buffer = g_malloc (buffer_len); + + if (nm_utils_hexstr2bin_full (hexstr, + FALSE, + delimiter_required, + delimiter_candidates, + required_len, + buffer, + buffer_len, + &len)) { + NM_SET_OUT (out_len, len); + return buffer; + } + + g_free (buffer); + +fail: + NM_SET_OUT (out_len, 0); + return NULL; +} diff --git a/shared/nm-glib-aux/nm-shared-utils.h b/shared/nm-glib-aux/nm-shared-utils.h new file mode 100644 index 00000000..af3c2f83 --- /dev/null +++ b/shared/nm-glib-aux/nm-shared-utils.h @@ -0,0 +1,1191 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* NetworkManager -- Network link manager + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA. + * + * (C) Copyright 2016 Red Hat, Inc. + */ + +#ifndef __NM_SHARED_UTILS_H__ +#define __NM_SHARED_UTILS_H__ + +#include + +/*****************************************************************************/ + +pid_t nm_utils_gettid (void); + +gboolean _nm_assert_on_main_thread (void); + +#if NM_MORE_ASSERTS > 5 +#define NM_ASSERT_ON_MAIN_THREAD() G_STMT_START { nm_assert (_nm_assert_on_main_thread ()); } G_STMT_END +#else +#define NM_ASSERT_ON_MAIN_THREAD() G_STMT_START { ; } G_STMT_END +#endif + +/*****************************************************************************/ + +static inline gboolean +_NM_INT_NOT_NEGATIVE (gssize val) +{ + /* whether an enum (without negative values) is a signed int, depends on compiler options + * and compiler implementation. + * + * When using such an enum for accessing an array, one naturally wants to check + * that the enum is not negative. However, the compiler doesn't like a plain + * comparison "enum_val >= 0", because (if the enum is unsigned), it will warn + * that the expression is always true *duh*. Not even a cast to a signed + * type helps to avoid the compiler warning in any case. + * + * The sole purpose of this function is to avoid a compiler warning, when checking + * that an enum is not negative. */ + return val >= 0; +} + +/* check whether the integer value is smaller than G_MAXINT32. This macro exists + * for the sole purpose, that a plain "((int) value <= G_MAXINT32)" comparison + * may cause the compiler or coverity that this check is always TRUE. But the + * check depends on compile time and the size of C type "int". Of course, most + * of the time in is gint32 and an int value is always <= G_MAXINT32. The check + * exists to catch cases where that is not true. + * + * Together with the G_STATIC_ASSERT(), we make sure that this is always satisfied. */ +G_STATIC_ASSERT (sizeof (int) == sizeof (gint32)); +#if _NM_CC_SUPPORT_GENERIC +#define _NM_INT_LE_MAXINT32(value) \ + ({ \ + _nm_unused typeof (value) _value = (value); \ + \ + _Generic((value), \ + int: TRUE \ + ); \ + }) +#else +#define _NM_INT_LE_MAXINT32(value) ({ \ + _nm_unused typeof (value) _value = (value); \ + _nm_unused const int *_p_value = &_value; \ + \ + TRUE; \ + }) +#endif + +/*****************************************************************************/ + +static inline char +nm_utils_addr_family_to_char (int addr_family) +{ + switch (addr_family) { + case AF_UNSPEC: return 'X'; + case AF_INET: return '4'; + case AF_INET6: return '6'; + } + g_return_val_if_reached ('?'); +} + +static inline gsize +nm_utils_addr_family_to_size (int addr_family) +{ + switch (addr_family) { + case AF_INET: return sizeof (in_addr_t); + case AF_INET6: return sizeof (struct in6_addr); + } + g_return_val_if_reached (0); +} + +#define nm_assert_addr_family(addr_family) \ + nm_assert (NM_IN_SET ((addr_family), AF_INET, AF_INET6)) + +/*****************************************************************************/ + +typedef struct { + union { + guint8 addr_ptr[1]; + in_addr_t addr4; + struct in_addr addr4_struct; + struct in6_addr addr6; + + /* NMIPAddr is really a union for IP addresses. + * However, as ethernet addresses fit in here nicely, use + * it also for an ethernet MAC address. */ + guint8 addr_eth[6 /*ETH_ALEN*/]; + }; +} NMIPAddr; + +extern const NMIPAddr nm_ip_addr_zero; + +static inline gboolean +nm_ip_addr_is_null (int addr_family, gconstpointer addr) +{ + nm_assert (addr); + if (addr_family == AF_INET6) + return IN6_IS_ADDR_UNSPECIFIED ((const struct in6_addr *) addr); + nm_assert (addr_family == AF_INET); + return ((const struct in_addr *) addr)->s_addr == 0; +} + +static inline void +nm_ip_addr_set (int addr_family, gpointer dst, gconstpointer src) +{ + nm_assert_addr_family (addr_family); + nm_assert (dst); + nm_assert (src); + + memcpy (dst, + src, + (addr_family != AF_INET6) + ? sizeof (in_addr_t) + : sizeof (struct in6_addr)); +} + +gboolean nm_ip_addr_set_from_untrusted (int addr_family, + gpointer dst, + gconstpointer src, + gsize src_len, + int *out_addr_family); + +static inline gboolean +nm_ip4_addr_is_localhost (in_addr_t addr4) +{ + return (addr4 & htonl (0xFF000000u)) == htonl (0x7F000000u); +} + +/*****************************************************************************/ + +#define NM_CMP_RETURN(c) \ + G_STMT_START { \ + const int _cc = (c); \ + if (_cc) \ + return _cc < 0 ? -1 : 1; \ + } G_STMT_END + +#define NM_CMP_SELF(a, b) \ + G_STMT_START { \ + typeof (a) _a = (a); \ + typeof (b) _b = (b); \ + \ + if (_a == _b) \ + return 0; \ + if (!_a) \ + return -1; \ + if (!_b) \ + return 1; \ + } G_STMT_END + +#define NM_CMP_DIRECT(a, b) \ + G_STMT_START { \ + typeof (a) _a = (a); \ + typeof (b) _b = (b); \ + \ + if (_a != _b) \ + return (_a < _b) ? -1 : 1; \ + } G_STMT_END + +#define NM_CMP_DIRECT_MEMCMP(a, b, size) \ + NM_CMP_RETURN (memcmp ((a), (b), (size))) + +#define NM_CMP_DIRECT_STRCMP0(a, b) \ + NM_CMP_RETURN (g_strcmp0 ((a), (b))) + +#define NM_CMP_DIRECT_IN6ADDR(a, b) \ + G_STMT_START { \ + const struct in6_addr *const _a = (a); \ + const struct in6_addr *const _b = (b); \ + NM_CMP_RETURN (memcmp (_a, _b, sizeof (struct in6_addr))); \ + } G_STMT_END + +#define NM_CMP_FIELD(a, b, field) \ + NM_CMP_DIRECT (((a)->field), ((b)->field)) + +#define NM_CMP_FIELD_UNSAFE(a, b, field) \ + G_STMT_START { \ + /* it's unsafe, because it evaluates the arguments more then once. + * This is necessary for bitfields, for which typeof() doesn't work. */ \ + if (((a)->field) != ((b)->field)) \ + return ((a)->field < ((b)->field)) ? -1 : 1; \ + } G_STMT_END + +#define NM_CMP_FIELD_BOOL(a, b, field) \ + NM_CMP_DIRECT (!!((a)->field), !!((b)->field)) + +#define NM_CMP_FIELD_STR(a, b, field) \ + NM_CMP_RETURN (strcmp (((a)->field), ((b)->field))) + +#define NM_CMP_FIELD_STR_INTERNED(a, b, field) \ + G_STMT_START { \ + const char *_a = ((a)->field); \ + const char *_b = ((b)->field); \ + \ + if (_a != _b) { \ + NM_CMP_RETURN (g_strcmp0 (_a, _b)); \ + } \ + } G_STMT_END + +#define NM_CMP_FIELD_STR0(a, b, field) \ + NM_CMP_RETURN (g_strcmp0 (((a)->field), ((b)->field))) + +#define NM_CMP_FIELD_MEMCMP_LEN(a, b, field, len) \ + NM_CMP_RETURN (memcmp (&((a)->field), &((b)->field), \ + MIN (len, sizeof ((a)->field)))) + +#define NM_CMP_FIELD_MEMCMP(a, b, field) \ + NM_CMP_RETURN (memcmp (&((a)->field), \ + &((b)->field), \ + sizeof ((a)->field))) + +#define NM_CMP_FIELD_IN6ADDR(a, b, field) \ + G_STMT_START { \ + const struct in6_addr *const _a = &((a)->field); \ + const struct in6_addr *const _b = &((b)->field); \ + NM_CMP_RETURN (memcmp (_a, _b, sizeof (struct in6_addr))); \ + } G_STMT_END + +/*****************************************************************************/ + +gboolean nm_utils_memeqzero (gconstpointer data, gsize length); + +/*****************************************************************************/ + +extern const void *const _NM_PTRARRAY_EMPTY[1]; + +#define NM_PTRARRAY_EMPTY(type) ((type const*) _NM_PTRARRAY_EMPTY) + +static inline void +_nm_utils_strbuf_init (char *buf, gsize len, char **p_buf_ptr, gsize *p_buf_len) +{ + NM_SET_OUT (p_buf_len, len); + NM_SET_OUT (p_buf_ptr, buf); + buf[0] = '\0'; +} + +#define nm_utils_strbuf_init(buf, p_buf_ptr, p_buf_len) \ + G_STMT_START { \ + G_STATIC_ASSERT (G_N_ELEMENTS (buf) == sizeof (buf) && sizeof (buf) > sizeof (char *)); \ + _nm_utils_strbuf_init ((buf), sizeof (buf), (p_buf_ptr), (p_buf_len)); \ + } G_STMT_END +void nm_utils_strbuf_append (char **buf, gsize *len, const char *format, ...) _nm_printf (3, 4); +void nm_utils_strbuf_append_c (char **buf, gsize *len, char c); +void nm_utils_strbuf_append_str (char **buf, gsize *len, const char *str); +void nm_utils_strbuf_append_bin (char **buf, gsize *len, gconstpointer str, gsize str_len); +void nm_utils_strbuf_seek_end (char **buf, gsize *len); + +const char *nm_strquote (char *buf, gsize buf_len, const char *str); + +static inline gboolean +nm_utils_is_separator (const char c) +{ + return NM_IN_SET (c, ' ', '\t'); +} + +/*****************************************************************************/ + +static inline gboolean +nm_gbytes_equal0 (GBytes *a, GBytes *b) +{ + return a == b || (a && b && g_bytes_equal (a, b)); +} + +gboolean nm_utils_gbytes_equal_mem (GBytes *bytes, + gconstpointer mem_data, + gsize mem_len); + +GVariant *nm_utils_gbytes_to_variant_ay (GBytes *bytes); + +/*****************************************************************************/ + +static inline int +nm_utils_hexchar_to_int (char ch) +{ + G_STATIC_ASSERT_EXPR ('0' < 'A'); + G_STATIC_ASSERT_EXPR ('A' < 'a'); + + if (ch >= '0') { + if (ch <= '9') + return ch - '0'; + if (ch >= 'A') { + if (ch <= 'F') + return ((int) ch) + (10 - (int) 'A'); + if (ch >= 'a' && ch <= 'f') + return ((int) ch) + (10 - (int) 'a'); + } + } + return -1; +} + +/*****************************************************************************/ + +const char *nm_utils_dbus_path_get_last_component (const char *dbus_path); + +int nm_utils_dbus_path_cmp (const char *dbus_path_a, const char *dbus_path_b); + +/*****************************************************************************/ + +typedef enum { + NM_UTILS_STRSPLIT_SET_FLAGS_NONE = 0, + NM_UTILS_STRSPLIT_SET_FLAGS_PRESERVE_EMPTY = (1u << 0), + NM_UTILS_STRSPLIT_SET_FLAGS_ALLOW_ESCAPING = (1u << 1), + + /* If flag is set, does the same as g_strstrip() on the returned tokens. + * This will remove leading and trailing ascii whitespaces (g_ascii_isspace() + * and NM_ASCII_SPACES). + * + * - when combined with !%NM_UTILS_STRSPLIT_SET_FLAGS_PRESERVE_EMPTY, + * empty tokens will be removed (and %NULL will be returned if that + * results in an empty string array). + * - when combined with %NM_UTILS_STRSPLIT_SET_FLAGS_ALLOW_ESCAPING, + * trailing whitespace escaped by backslash are not stripped. */ + NM_UTILS_STRSPLIT_SET_FLAGS_STRSTRIP = (1u << 2), + + /* This implies %NM_UTILS_STRSPLIT_SET_FLAGS_ALLOW_ESCAPING. + * + * This will do a final run over all tokens and remove all backslash + * escape characters that + * - precede a delimiter. + * - precede a backslash. + * - preceed a whitespace (with %NM_UTILS_STRSPLIT_SET_FLAGS_STRSTRIP). + * + * Note that with %NM_UTILS_STRSPLIT_SET_FLAGS_STRSTRIP, it is only + * necessary to escape the very last whitespace (if the delimiters + * are not whitespace themself). So, technically, it would be sufficient + * to only unescape a backslash before the last whitespace and the user + * still could express everything. However, such a rule would be complicated + * to understand, so when using backslash escaping with nm_utils_strsplit_set_full(), + * then all characters (including backslash) are treated verbatim, except: + * + * - "\\$DELIMITER" (escaped delimiter) + * - "\\\\" (escaped backslash) + * - "\\$SPACE" (escaped space) (with %NM_UTILS_STRSPLIT_SET_FLAGS_STRSTRIP). + * + * Note that all other escapes like "\\n" or "\\001" are left alone. + * That makes the escaping/unescaping rules simple. Also, for the most part + * a text is just taken as-is, with little additional rules. Only backslashes + * need extra care, and then only if they proceed one of the relevant characters. + */ + NM_UTILS_STRSPLIT_SET_FLAGS_ESCAPED = (1u << 3), +} NMUtilsStrsplitSetFlags; + +const char **nm_utils_strsplit_set_full (const char *str, + const char *delimiter, + NMUtilsStrsplitSetFlags flags); + +static inline const char ** +nm_utils_strsplit_set_with_empty (const char *str, + const char *delimiters) +{ + /* this returns the same result as g_strsplit_set(str, delimiters, -1), except + * it does not deep-clone the strv array. + * Also, for @str == "", this returns %NULL while g_strsplit_set() would return + * an empty strv array. */ + return nm_utils_strsplit_set_full (str, delimiters, NM_UTILS_STRSPLIT_SET_FLAGS_PRESERVE_EMPTY); +} + +static inline const char ** +nm_utils_strsplit_set (const char *str, + const char *delimiters) +{ + return nm_utils_strsplit_set_full (str, delimiters, NM_UTILS_STRSPLIT_SET_FLAGS_NONE); +} + +gssize nm_utils_strv_find_first (char **list, gssize len, const char *needle); + +char **_nm_utils_strv_cleanup (char **strv, + gboolean strip_whitespace, + gboolean skip_empty, + gboolean skip_repeated); + +/*****************************************************************************/ + +static inline const char ** +nm_utils_escaped_tokens_split (const char *str, + const char *delimiters) +{ + return nm_utils_strsplit_set_full (str, + delimiters, + NM_UTILS_STRSPLIT_SET_FLAGS_ESCAPED + | NM_UTILS_STRSPLIT_SET_FLAGS_STRSTRIP); +} + +const char *nm_utils_escaped_tokens_escape (const char *str, + const char *delimiters, + char **out_to_free); + +static inline GString * +nm_utils_escaped_tokens_escape_gstr_assert (const char *str, + const char *delimiters, + GString *gstring) +{ +#if NM_MORE_ASSERTS > 0 + + /* Just appends @str to @gstring, but also assert that + * no escaping is necessary. + * + * Use nm_utils_escaped_tokens_escape_gstr_assert() instead + * of nm_utils_escaped_tokens_escape_gstr(), if you *know* that + * @str contains no delimiters, no backslashes, and no trailing + * whitespace that requires escaping. */ + + nm_assert (str); + nm_assert (gstring); + nm_assert (delimiters); + + { + gs_free char *str_to_free = NULL; + const char *str0; + + str0 = nm_utils_escaped_tokens_escape (str, delimiters, &str_to_free); + nm_assert (str0 == str); + nm_assert (!str_to_free); + } +#endif + + g_string_append (gstring, str); + return gstring; +} + +static inline GString * +nm_utils_escaped_tokens_escape_gstr (const char *str, + const char *delimiters, + GString *gstring) +{ + gs_free char *str_to_free = NULL; + + nm_assert (str); + nm_assert (gstring); + + g_string_append (gstring, + nm_utils_escaped_tokens_escape (str, delimiters, &str_to_free)); + return gstring; +} + +/*****************************************************************************/ + +#define NM_UTILS_CHECKSUM_LENGTH_MD5 16 +#define NM_UTILS_CHECKSUM_LENGTH_SHA1 20 +#define NM_UTILS_CHECKSUM_LENGTH_SHA256 32 + +#define nm_utils_checksum_get_digest(sum, arr) \ + G_STMT_START { \ + GChecksum *const _sum = (sum); \ + gsize _len; \ + \ + G_STATIC_ASSERT_EXPR ( sizeof (arr) == NM_UTILS_CHECKSUM_LENGTH_MD5 \ + || sizeof (arr) == NM_UTILS_CHECKSUM_LENGTH_SHA1 \ + || sizeof (arr) == NM_UTILS_CHECKSUM_LENGTH_SHA256); \ + G_STATIC_ASSERT_EXPR (sizeof (arr) == G_N_ELEMENTS (arr)); \ + \ + nm_assert (_sum); \ + \ + _len = G_N_ELEMENTS (arr); \ + \ + g_checksum_get_digest (_sum, (arr), &_len); \ + nm_assert (_len == G_N_ELEMENTS (arr)); \ + } G_STMT_END + +#define nm_utils_checksum_get_digest_len(sum, buf, len) \ + G_STMT_START { \ + GChecksum *const _sum = (sum); \ + const gsize _len0 = (len); \ + gsize _len; \ + \ + nm_assert (NM_IN_SET (_len0, NM_UTILS_CHECKSUM_LENGTH_MD5, \ + NM_UTILS_CHECKSUM_LENGTH_SHA1, \ + NM_UTILS_CHECKSUM_LENGTH_SHA256)); \ + nm_assert (_sum); \ + \ + _len = _len0; \ + g_checksum_get_digest (_sum, (buf), &_len); \ + nm_assert (_len == _len0); \ + } G_STMT_END + +/*****************************************************************************/ + +guint32 _nm_utils_ip4_prefix_to_netmask (guint32 prefix); +guint32 _nm_utils_ip4_get_default_prefix (guint32 ip); + +gboolean nm_utils_ip_is_site_local (int addr_family, + const void *address); + +/*****************************************************************************/ + +gboolean nm_utils_parse_inaddr_bin (int addr_family, + const char *text, + int *out_addr_family, + gpointer out_addr); + +gboolean nm_utils_parse_inaddr (int addr_family, + const char *text, + char **out_addr); + +gboolean nm_utils_parse_inaddr_prefix_bin (int addr_family, + const char *text, + int *out_addr_family, + gpointer out_addr, + int *out_prefix); + +gboolean nm_utils_parse_inaddr_prefix (int addr_family, + const char *text, + char **out_addr, + int *out_prefix); + +gint64 _nm_utils_ascii_str_to_int64 (const char *str, guint base, gint64 min, gint64 max, gint64 fallback); +guint64 _nm_utils_ascii_str_to_uint64 (const char *str, guint base, guint64 min, guint64 max, guint64 fallback); + +int _nm_utils_ascii_str_to_bool (const char *str, + int default_value); + +/*****************************************************************************/ + +extern char _nm_utils_to_string_buffer[2096]; + +void nm_utils_to_string_buffer_init (char **buf, gsize *len); +gboolean nm_utils_to_string_buffer_init_null (gconstpointer obj, char **buf, gsize *len); + +/*****************************************************************************/ + +typedef struct { + unsigned flag; + const char *name; +} NMUtilsFlags2StrDesc; + +#define NM_UTILS_FLAGS2STR(f, n) { .flag = f, .name = ""n, } + +#define _NM_UTILS_FLAGS2STR_DEFINE(scope, fcn_name, flags_type, ...) \ +scope const char * \ +fcn_name (flags_type flags, char *buf, gsize len) \ +{ \ + 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); \ +}; + +#define NM_UTILS_FLAGS2STR_DEFINE(fcn_name, flags_type, ...) \ + _NM_UTILS_FLAGS2STR_DEFINE (, fcn_name, flags_type, __VA_ARGS__) +#define NM_UTILS_FLAGS2STR_DEFINE_STATIC(fcn_name, flags_type, ...) \ + _NM_UTILS_FLAGS2STR_DEFINE (static, fcn_name, flags_type, __VA_ARGS__) + +const char *nm_utils_flags2str (const NMUtilsFlags2StrDesc *descs, + gsize n_descs, + unsigned flags, + char *buf, + gsize len); + +/*****************************************************************************/ + +#define NM_UTILS_ENUM2STR(v, n) (void) 0; case v: s = ""n""; break; (void) 0 +#define NM_UTILS_ENUM2STR_IGNORE(v) (void) 0; case v: break; (void) 0 + +#define _NM_UTILS_ENUM2STR_DEFINE(scope, fcn_name, lookup_type, int_fmt, ...) \ +scope const char * \ +fcn_name (lookup_type val, char *buf, gsize len) \ +{ \ + nm_utils_to_string_buffer_init (&buf, &len); \ + if (len) { \ + const char *s = NULL; \ + switch (val) { \ + (void) 0, \ + __VA_ARGS__ \ + (void) 0; \ + }; \ + if (s) \ + g_strlcpy (buf, s, len); \ + else \ + g_snprintf (buf, len, "(%"int_fmt")", val); \ + } \ + return buf; \ +} + +#define NM_UTILS_ENUM2STR_DEFINE(fcn_name, lookup_type, ...) \ + _NM_UTILS_ENUM2STR_DEFINE (, fcn_name, lookup_type, "d", __VA_ARGS__) +#define NM_UTILS_ENUM2STR_DEFINE_STATIC(fcn_name, lookup_type, ...) \ + _NM_UTILS_ENUM2STR_DEFINE (static, fcn_name, lookup_type, "d", __VA_ARGS__) + +/*****************************************************************************/ + +#define _nm_g_slice_free_fcn_define(mem_size) \ +static inline void \ +_nm_g_slice_free_fcn_##mem_size (gpointer mem_block) \ +{ \ + g_slice_free1 (mem_size, mem_block); \ +} + +_nm_g_slice_free_fcn_define (1) +_nm_g_slice_free_fcn_define (2) +_nm_g_slice_free_fcn_define (4) +_nm_g_slice_free_fcn_define (8) +_nm_g_slice_free_fcn_define (10) +_nm_g_slice_free_fcn_define (12) +_nm_g_slice_free_fcn_define (16) + +#define _nm_g_slice_free_fcn1(mem_size) \ + ({ \ + void (*_fcn) (gpointer); \ + \ + /* If mem_size is a compile time constant, the compiler + * will be able to optimize this. Hence, you don't want + * to call this with a non-constant size argument. */ \ + G_STATIC_ASSERT_EXPR ( ((mem_size) == 1) \ + || ((mem_size) == 2) \ + || ((mem_size) == 4) \ + || ((mem_size) == 8) \ + || ((mem_size) == 10) \ + || ((mem_size) == 12) \ + || ((mem_size) == 16)); \ + switch ((mem_size)) { \ + case 1: _fcn = _nm_g_slice_free_fcn_1; break; \ + case 2: _fcn = _nm_g_slice_free_fcn_2; break; \ + case 4: _fcn = _nm_g_slice_free_fcn_4; break; \ + case 8: _fcn = _nm_g_slice_free_fcn_8; break; \ + case 10: _fcn = _nm_g_slice_free_fcn_10; break; \ + case 12: _fcn = _nm_g_slice_free_fcn_12; break; \ + case 16: _fcn = _nm_g_slice_free_fcn_16; break; \ + default: g_assert_not_reached (); _fcn = NULL; break; \ + } \ + _fcn; \ + }) + +/** + * nm_g_slice_free_fcn: + * @type: type argument for sizeof() operator that you would + * pass to g_slice_new(). + * + * Returns: a function pointer with GDestroyNotify signature + * for g_slice_free(type,*). + * + * Only certain types are implemented. You'll get an assertion + * using the wrong type. */ +#define nm_g_slice_free_fcn(type) (_nm_g_slice_free_fcn1 (sizeof (type))) + +#define nm_g_slice_free_fcn_gint64 (nm_g_slice_free_fcn (gint64)) + +/*****************************************************************************/ + +/** + * NMUtilsError: + * @NM_UTILS_ERROR_UNKNOWN: unknown or unclassified error + * @NM_UTILS_ERROR_CANCELLED_DISPOSING: when disposing an object that has + * pending aynchronous operations, the operation is cancelled with this + * 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_CONNECTION_AVAILABLE_INCOMPATIBLE: used for a very particular + * purpose during nm_device_check_connection_compatible() to indicate that + * the profile does not match the device already because their type differs. + * That is, there is a fundamental reason of trying to check a profile that + * cannot possibly match on this device. + * @NM_UTILS_ERROR_CONNECTION_AVAILABLE_UNMANAGED_DEVICE: used for a very particular + * purpose during nm_device_check_connection_available(), to indicate that the + * device is not available because it is unmanaged. + * @NM_UTILS_ERROR_CONNECTION_AVAILABLE_TEMPORARY: the profile is currently not + * available/compatible with the device, but this may be only temporary. + * + * @NM_UTILS_ERROR_INVALID_ARGUMENT: invalid argument. + */ +typedef enum { + NM_UTILS_ERROR_UNKNOWN = 0, /*< nick=Unknown >*/ + NM_UTILS_ERROR_CANCELLED_DISPOSING, /*< nick=CancelledDisposing >*/ + NM_UTILS_ERROR_INVALID_ARGUMENT, /*< nick=InvalidArgument >*/ + + /* the following codes have a special meaning and are exactly used for + * nm_device_check_connection_compatible() and nm_device_check_connection_available(). + * + * Actually, their meaning is not very important (so, don't think too + * hard about the name of these error codes). What is important, is their + * relative order (i.e. the integer value of the codes). When manager + * searches for a suitable device, it will check all devices whether + * a profile can be activated. If they all fail, it will pick the error + * message from the device that returned the *highest* error code, + * in the hope that this message makes the most sense for the caller. + * */ + NM_UTILS_ERROR_CONNECTION_AVAILABLE_INCOMPATIBLE, + NM_UTILS_ERROR_CONNECTION_AVAILABLE_UNMANAGED_DEVICE, + NM_UTILS_ERROR_CONNECTION_AVAILABLE_TEMPORARY, + +} NMUtilsError; + +#define NM_UTILS_ERROR (nm_utils_error_quark ()) +GQuark nm_utils_error_quark (void); + +void nm_utils_error_set_cancelled (GError **error, + gboolean is_disposing, + const char *instance_name); +gboolean nm_utils_error_is_cancelled (GError *error, + gboolean consider_is_disposing); + +gboolean nm_utils_error_is_notfound (GError *error); + +static inline void +nm_utils_error_set_literal (GError **error, int error_code, const char *literal) +{ + g_set_error_literal (error, NM_UTILS_ERROR, error_code, literal); +} + +#define nm_utils_error_set(error, error_code, ...) \ + g_set_error ((error), NM_UTILS_ERROR, error_code, __VA_ARGS__) + +#define nm_utils_error_set_errno(error, errsv, fmt, ...) \ + G_STMT_START { \ + char _bstrerr[NM_STRERROR_BUFSIZE]; \ + \ + g_set_error ((error), \ + NM_UTILS_ERROR, \ + NM_UTILS_ERROR_UNKNOWN, \ + fmt, \ + ##__VA_ARGS__, \ + nm_strerror_native_r (({ \ + const int _errsv = (errsv); \ + \ + ( _errsv >= 0 \ + ? _errsv \ + : ( G_UNLIKELY (_errsv == G_MININT) \ + ? G_MAXINT \ + : -errsv)); \ + }), \ + _bstrerr, \ + sizeof (_bstrerr))); \ + } G_STMT_END + +/*****************************************************************************/ + +gboolean nm_g_object_set_property (GObject *object, + const char *property_name, + const GValue *value, + GError **error); + +gboolean nm_g_object_set_property_string (GObject *object, + const char *property_name, + const char *value, + GError **error); + +gboolean nm_g_object_set_property_string_static (GObject *object, + const char *property_name, + const char *value, + GError **error); + +gboolean nm_g_object_set_property_string_take (GObject *object, + const char *property_name, + char *value, + GError **error); + +gboolean nm_g_object_set_property_boolean (GObject *object, + const char *property_name, + gboolean value, + GError **error); + +gboolean nm_g_object_set_property_char (GObject *object, + const char *property_name, + gint8 value, + GError **error); + +gboolean nm_g_object_set_property_uchar (GObject *object, + const char *property_name, + guint8 value, + GError **error); + +gboolean nm_g_object_set_property_int (GObject *object, + const char *property_name, + int value, + GError **error); + +gboolean nm_g_object_set_property_int64 (GObject *object, + const char *property_name, + gint64 value, + GError **error); + +gboolean nm_g_object_set_property_uint (GObject *object, + const char *property_name, + guint value, + GError **error); + +gboolean nm_g_object_set_property_uint64 (GObject *object, + const char *property_name, + guint64 value, + GError **error); + +gboolean nm_g_object_set_property_flags (GObject *object, + const char *property_name, + GType gtype, + guint value, + GError **error); + +gboolean nm_g_object_set_property_enum (GObject *object, + const char *property_name, + GType gtype, + int value, + GError **error); + +GParamSpec *nm_g_object_class_find_property_from_gtype (GType gtype, + const char *property_name); + +/*****************************************************************************/ + +GType nm_g_type_find_implementing_class_for_property (GType gtype, + const char *pname); + +/*****************************************************************************/ + +typedef enum { + NM_UTILS_STR_UTF8_SAFE_FLAG_NONE = 0, + NM_UTILS_STR_UTF8_SAFE_FLAG_ESCAPE_CTRL = 0x0001, + NM_UTILS_STR_UTF8_SAFE_FLAG_ESCAPE_NON_ASCII = 0x0002, +} NMUtilsStrUtf8SafeFlags; + +const char *nm_utils_buf_utf8safe_escape (gconstpointer buf, gssize buflen, NMUtilsStrUtf8SafeFlags flags, char **to_free); +const char *nm_utils_buf_utf8safe_escape_bytes (GBytes *bytes, NMUtilsStrUtf8SafeFlags flags, char **to_free); +gconstpointer nm_utils_buf_utf8safe_unescape (const char *str, gsize *out_len, gpointer *to_free); + +const char *nm_utils_str_utf8safe_escape (const char *str, NMUtilsStrUtf8SafeFlags flags, char **to_free); +const char *nm_utils_str_utf8safe_unescape (const char *str, char **to_free); + +char *nm_utils_str_utf8safe_escape_cp (const char *str, NMUtilsStrUtf8SafeFlags flags); +char *nm_utils_str_utf8safe_unescape_cp (const char *str); + +char *nm_utils_str_utf8safe_escape_take (char *str, NMUtilsStrUtf8SafeFlags flags); + +static inline void +nm_g_variant_unref_floating (GVariant *var) +{ + /* often a function wants to keep a reference to an input variant. + * It uses g_variant_ref_sink() to either increase the ref-count, + * or take ownership of a possibly floating reference. + * + * If the function doesn't actually want to do anything with the + * input variant, it still must make sure that a passed in floating + * reference is consumed. Hence, this helper which: + * + * - does nothing if @var is not floating + * - unrefs (consumes) @var if it is floating. */ + if (g_variant_is_floating (var)) + g_variant_unref (var); +} + +/*****************************************************************************/ + +static inline int +nm_utf8_collate0 (const char *a, const char *b) +{ + if (!a) + return !b ? 0 : -1; + if (!b) + return 1; + return g_utf8_collate (a, b); +} + +int nm_strcmp_p_with_data (gconstpointer a, gconstpointer b, gpointer user_data); +int nm_cmp_uint32_p_with_data (gconstpointer p_a, gconstpointer p_b, gpointer user_data); +int nm_cmp_int2ptr_p_with_data (gconstpointer p_a, gconstpointer p_b, gpointer user_data); + +/*****************************************************************************/ + +typedef struct { + const char *name; +} NMUtilsNamedEntry; + +typedef struct { + union { + NMUtilsNamedEntry named_entry; + const char *name; + }; + union { + const char *value_str; + gconstpointer value_ptr; + }; +} NMUtilsNamedValue; + +#define nm_utils_named_entry_cmp nm_strcmp_p +#define nm_utils_named_entry_cmp_with_data nm_strcmp_p_with_data + +NMUtilsNamedValue *nm_utils_named_values_from_str_dict (GHashTable *hash, guint *out_len); + +gpointer *nm_utils_hash_keys_to_array (GHashTable *hash, + GCompareDataFunc compare_func, + gpointer user_data, + guint *out_len); + +static inline const char ** +nm_utils_strdict_get_keys (const GHashTable *hash, + gboolean sorted, + guint *out_length) +{ + return (const char **) nm_utils_hash_keys_to_array ((GHashTable *) hash, + sorted ? nm_strcmp_p_with_data : NULL, + NULL, + out_length); +} + +char **nm_utils_strv_make_deep_copied (const char **strv); + +static inline char ** +nm_utils_strv_make_deep_copied_nonnull (const char **strv) +{ + return nm_utils_strv_make_deep_copied (strv) ?: g_new0 (char *, 1); +} + +/*****************************************************************************/ + +gssize nm_utils_ptrarray_find_binary_search (gconstpointer *list, + gsize len, + gconstpointer needle, + GCompareDataFunc cmpfcn, + gpointer user_data, + gssize *out_idx_first, + gssize *out_idx_last); + +gssize nm_utils_array_find_binary_search (gconstpointer list, + gsize elem_size, + gsize len, + gconstpointer needle, + GCompareDataFunc cmpfcn, + gpointer user_data); + +/*****************************************************************************/ + +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_NS_PER_SECOND ((gint64) 1000000000) +#define NM_UTILS_NS_PER_MSEC ((gint64) 1000000) +#define NM_UTILS_MSEC_PER_SECOND ((gint64) 1000) +#define NM_UTILS_NS_TO_MSEC_CEIL(nsec) (((nsec) + (NM_UTILS_NS_PER_MSEC - 1)) / NM_UTILS_NS_PER_MSEC) + +/*****************************************************************************/ + +int nm_utils_fd_wait_for_event (int fd, int event, gint64 timeout_ns); +ssize_t nm_utils_fd_read_loop (int fd, void *buf, size_t nbytes, bool do_poll); +int nm_utils_fd_read_loop_exact (int fd, void *buf, size_t nbytes, bool do_poll); + +/*****************************************************************************/ + +static inline const char * +nm_utils_dbus_normalize_object_path (const char *path) +{ + /* D-Bus does not allow an empty object path. Hence, whenever we mean NULL / no-object + * on D-Bus, it's path is actually "/". + * + * Normalize that away, and return %NULL in that case. */ + if (path && path[0] == '/' && path[1] == '\0') + return NULL; + return path; +} + +#define NM_DEFINE_GDBUS_ARG_INFO_FULL(name_, ...) \ + ((GDBusArgInfo *) (&((const GDBusArgInfo) { \ + .ref_count = -1, \ + .name = name_, \ + __VA_ARGS__ \ + }))) + +#define NM_DEFINE_GDBUS_ARG_INFO(name_, a_signature) \ + NM_DEFINE_GDBUS_ARG_INFO_FULL ( \ + name_, \ + .signature = a_signature, \ + ) + +#define NM_DEFINE_GDBUS_ARG_INFOS(...) \ + ((GDBusArgInfo **) ((const GDBusArgInfo *[]) { \ + __VA_ARGS__ \ + NULL, \ + })) + +#define NM_DEFINE_GDBUS_PROPERTY_INFO(name_, ...) \ + ((GDBusPropertyInfo *) (&((const GDBusPropertyInfo) { \ + .ref_count = -1, \ + .name = name_, \ + __VA_ARGS__ \ + }))) + +#define NM_DEFINE_GDBUS_PROPERTY_INFO_READABLE(name_, m_signature) \ + NM_DEFINE_GDBUS_PROPERTY_INFO ( \ + name_, \ + .signature = m_signature, \ + .flags = G_DBUS_PROPERTY_INFO_FLAGS_READABLE, \ + ) + +#define NM_DEFINE_GDBUS_PROPERTY_INFOS(...) \ + ((GDBusPropertyInfo **) ((const GDBusPropertyInfo *[]) { \ + __VA_ARGS__ \ + NULL, \ + })) + +#define NM_DEFINE_GDBUS_SIGNAL_INFO_INIT(name_, ...) \ + { \ + .ref_count = -1, \ + .name = name_, \ + __VA_ARGS__ \ + } + +#define NM_DEFINE_GDBUS_SIGNAL_INFO(name_, ...) \ + ((GDBusSignalInfo *) (&((const GDBusSignalInfo) NM_DEFINE_GDBUS_SIGNAL_INFO_INIT (name_, __VA_ARGS__)))) + +#define NM_DEFINE_GDBUS_SIGNAL_INFOS(...) \ + ((GDBusSignalInfo **) ((const GDBusSignalInfo *[]) { \ + __VA_ARGS__ \ + NULL, \ + })) + +#define NM_DEFINE_GDBUS_METHOD_INFO_INIT(name_, ...) \ + { \ + .ref_count = -1, \ + .name = name_, \ + __VA_ARGS__ \ + } + +#define NM_DEFINE_GDBUS_METHOD_INFO(name_, ...) \ + ((GDBusMethodInfo *) (&((const GDBusMethodInfo) NM_DEFINE_GDBUS_METHOD_INFO_INIT (name_, __VA_ARGS__)))) + +#define NM_DEFINE_GDBUS_METHOD_INFOS(...) \ + ((GDBusMethodInfo **) ((const GDBusMethodInfo *[]) { \ + __VA_ARGS__ \ + NULL, \ + })) + +#define NM_DEFINE_GDBUS_INTERFACE_INFO_INIT(name_, ...) \ + { \ + .ref_count = -1, \ + .name = name_, \ + __VA_ARGS__ \ + } + +#define NM_DEFINE_GDBUS_INTERFACE_INFO(name_, ...) \ + ((GDBusInterfaceInfo *) (&((const GDBusInterfaceInfo) NM_DEFINE_GDBUS_INTERFACE_INFO_INIT (name_, __VA_ARGS__)))) + +#define NM_DEFINE_GDBUS_INTERFACE_VTABLE(...) \ + ((GDBusInterfaceVTable *) (&((const GDBusInterfaceVTable) { \ + __VA_ARGS__ \ + }))) + +/*****************************************************************************/ + +guint64 nm_utils_get_start_time_for_pid (pid_t pid, char *out_state, pid_t *out_ppid); + +/*****************************************************************************/ + +gpointer _nm_utils_user_data_pack (int nargs, gconstpointer *args); + +#define nm_utils_user_data_pack(...) \ + _nm_utils_user_data_pack(NM_NARG (__VA_ARGS__), (gconstpointer[]) { __VA_ARGS__ }) + +void _nm_utils_user_data_unpack (gpointer user_data, int nargs, ...); + +#define nm_utils_user_data_unpack(user_data, ...) \ + _nm_utils_user_data_unpack(user_data, NM_NARG (__VA_ARGS__), __VA_ARGS__) + +/*****************************************************************************/ + +typedef void (*NMUtilsInvokeOnIdleCallback) (gpointer callback_user_data, + GCancellable *cancellable); + +void nm_utils_invoke_on_idle (NMUtilsInvokeOnIdleCallback callback, + gpointer callback_user_data, + GCancellable *cancellable); + +/*****************************************************************************/ + +static inline void +nm_strv_ptrarray_add_string_take (GPtrArray *cmd, + char *str) +{ + nm_assert (cmd); + nm_assert (str); + + g_ptr_array_add (cmd, str); +} + +static inline void +nm_strv_ptrarray_add_string_dup (GPtrArray *cmd, + const char *str) +{ + nm_strv_ptrarray_add_string_take (cmd, + g_strdup (str)); +} + +#define nm_strv_ptrarray_add_string_concat(cmd, ...) \ + nm_strv_ptrarray_add_string_take ((cmd), g_strconcat (__VA_ARGS__, NULL)) + +#define nm_strv_ptrarray_add_string_printf(cmd, ...) \ + nm_strv_ptrarray_add_string_take ((cmd), g_strdup_printf (__VA_ARGS__)) + +#define nm_strv_ptrarray_add_int(cmd, val) \ + nm_strv_ptrarray_add_string_take ((cmd), nm_strdup_int (val)) + +static inline void +nm_strv_ptrarray_take_gstring (GPtrArray *cmd, + GString **gstr) +{ + nm_assert (gstr && *gstr); + + nm_strv_ptrarray_add_string_take (cmd, + g_string_free (g_steal_pointer (gstr), + FALSE)); +} + +/*****************************************************************************/ + +int nm_utils_getpagesize (void); + +/*****************************************************************************/ + +char *nm_utils_bin2hexstr_full (gconstpointer addr, + gsize length, + char delimiter, + gboolean upper_case, + char *out); + +guint8 *nm_utils_hexstr2bin_full (const char *hexstr, + gboolean allow_0x_prefix, + gboolean delimiter_required, + const char *delimiter_candidates, + gsize required_len, + guint8 *buffer, + gsize buffer_len, + gsize *out_len); + +#define nm_utils_hexstr2bin_buf(hexstr, allow_0x_prefix, delimiter_required, delimiter_candidates, buffer) \ + nm_utils_hexstr2bin_full ((hexstr), (allow_0x_prefix), (delimiter_required), (delimiter_candidates), G_N_ELEMENTS (buffer), (buffer), G_N_ELEMENTS (buffer), NULL) + +guint8 *nm_utils_hexstr2bin_alloc (const char *hexstr, + gboolean allow_0x_prefix, + gboolean delimiter_required, + const char *delimiter_candidates, + gsize required_len, + gsize *out_len); + +#endif /* __NM_SHARED_UTILS_H__ */ diff --git a/shared/nm-glib-aux/nm-time-utils.c b/shared/nm-glib-aux/nm-time-utils.c new file mode 100644 index 00000000..ae526c34 --- /dev/null +++ b/shared/nm-glib-aux/nm-time-utils.c @@ -0,0 +1,273 @@ +/* NetworkManager -- Network link manager + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA. + * + * (C) Copyright 2018 Red Hat, Inc. + */ + +#include "nm-default.h" + +#include "nm-time-utils.h" + +/*****************************************************************************/ + +typedef struct { + /* the offset to the native clock, in seconds. */ + gint64 offset_sec; + clockid_t clk_id; +} GlobalState; + +static const GlobalState *volatile p_global_state; + +static const GlobalState * +_t_init_global_state (void) +{ + static GlobalState global_state = { }; + static gsize init_once = 0; + const GlobalState *p; + clockid_t clk_id; + struct timespec tp; + gint64 offset_sec; + int r; + + clk_id = CLOCK_BOOTTIME; + r = clock_gettime (clk_id, &tp); + if (r == -1 && errno == EINVAL) { + clk_id = CLOCK_MONOTONIC; + r = clock_gettime (clk_id, &tp); + } + + /* The only failure we tolerate is that CLOCK_BOOTTIME is not supported. + * Other than that, we rely on kernel to not fail on this. */ + g_assert (r == 0); + g_assert (tp.tv_nsec >= 0 && tp.tv_nsec < NM_UTILS_NS_PER_SECOND); + + /* Calculate an offset for the time stamp. + * + * We always want positive values, because then we can initialize + * a timestamp with 0 and be sure, that it will be less then any + * value nm_utils_get_monotonic_timestamp_*() might return. + * For this to be true also for nm_utils_get_monotonic_timestamp_s() at + * early boot, we have to shift the timestamp to start counting at + * least from 1 second onward. + * + * Another advantage of shifting is, that this way we make use of the whole 31 bit + * range of signed int, before the time stamp for nm_utils_get_monotonic_timestamp_s() + * wraps (~68 years). + **/ + offset_sec = (- ((gint64) tp.tv_sec)) + 1; + + if (!g_once_init_enter (&init_once)) { + /* there was a race. We expect the pointer to be fully initialized now. */ + p = g_atomic_pointer_get (&p_global_state); + g_assert (p); + return p; + } + + global_state.offset_sec = offset_sec; + global_state.clk_id = clk_id; + p = &global_state; + g_atomic_pointer_set (&p_global_state, p); + g_once_init_leave (&init_once, 1); + + _nm_utils_monotonic_timestamp_initialized (&tp, + p->offset_sec, + p->clk_id == CLOCK_BOOTTIME); + + return p; +} + +#define _t_get_global_state() \ + ({ \ + const GlobalState *_p; \ + \ + _p = g_atomic_pointer_get (&p_global_state); \ + (G_LIKELY (_p) ? _p : _t_init_global_state ()); \ + }) + +#define _t_clock_gettime_eval(p, tp) \ + ({ \ + struct timespec *const _tp = (tp); \ + const GlobalState *const _p2 = (p); \ + int _r; \ + \ + nm_assert (_tp); \ + \ + _r = clock_gettime (_p2->clk_id, _tp); \ + \ + nm_assert (_r == 0); \ + nm_assert (_tp->tv_nsec >= 0 && _tp->tv_nsec < NM_UTILS_NS_PER_SECOND); \ + \ + _p2; \ + }) + +#define _t_clock_gettime(tp) \ + _t_clock_gettime_eval (_t_get_global_state (), tp); + +/*****************************************************************************/ + +/** + * nm_utils_get_monotonic_timestamp_ns: + * + * Returns: a monotonically increasing time stamp in nanoseconds, + * starting at an unspecified offset. See clock_gettime(), %CLOCK_BOOTTIME. + * + * The returned value will start counting at an undefined point + * in the past and will always be positive. + * + * All the nm_utils_get_monotonic_timestamp_*s functions return the same + * timestamp but in different scales (nsec, usec, msec, sec). + **/ +gint64 +nm_utils_get_monotonic_timestamp_ns (void) +{ + const GlobalState *p; + struct timespec tp; + + p = _t_clock_gettime (&tp); + + /* Although the result will always be positive, we return a signed + * integer, which makes it easier to calculate time differences (when + * you want to subtract signed values). + **/ + return (((gint64) tp.tv_sec) + p->offset_sec) * NM_UTILS_NS_PER_SECOND + + tp.tv_nsec; +} + +/** + * nm_utils_get_monotonic_timestamp_us: + * + * Returns: a monotonically increasing time stamp in microseconds, + * starting at an unspecified offset. See clock_gettime(), %CLOCK_BOOTTIME. + * + * The returned value will start counting at an undefined point + * in the past and will always be positive. + * + * All the nm_utils_get_monotonic_timestamp_*s functions return the same + * timestamp but in different scales (nsec, usec, msec, sec). + **/ +gint64 +nm_utils_get_monotonic_timestamp_us (void) +{ + const GlobalState *p; + struct timespec tp; + + p = _t_clock_gettime (&tp); + + /* Although the result will always be positive, we return a signed + * integer, which makes it easier to calculate time differences (when + * you want to subtract signed values). + **/ + return (((gint64) tp.tv_sec) + p->offset_sec) * ((gint64) G_USEC_PER_SEC) + + (tp.tv_nsec / (NM_UTILS_NS_PER_SECOND/G_USEC_PER_SEC)); +} + +/** + * nm_utils_get_monotonic_timestamp_ms: + * + * Returns: a monotonically increasing time stamp in milliseconds, + * starting at an unspecified offset. See clock_gettime(), %CLOCK_BOOTTIME. + * + * The returned value will start counting at an undefined point + * in the past and will always be positive. + * + * All the nm_utils_get_monotonic_timestamp_*s functions return the same + * timestamp but in different scales (nsec, usec, msec, sec). + **/ +gint64 +nm_utils_get_monotonic_timestamp_ms (void) +{ + const GlobalState *p; + struct timespec tp; + + p = _t_clock_gettime (&tp); + + /* Although the result will always be positive, we return a signed + * integer, which makes it easier to calculate time differences (when + * you want to subtract signed values). + **/ + return (((gint64) tp.tv_sec) + p->offset_sec) * ((gint64) 1000) + + (tp.tv_nsec / (NM_UTILS_NS_PER_SECOND/1000)); +} + +/** + * nm_utils_get_monotonic_timestamp_s: + * + * Returns: nm_utils_get_monotonic_timestamp_ms() in seconds (throwing + * away sub second parts). The returned value will always be positive. + * + * This value wraps after roughly 68 years which should be fine for any + * practical purpose. + * + * All the nm_utils_get_monotonic_timestamp_*s functions return the same + * timestamp but in different scales (nsec, usec, msec, sec). + **/ +gint32 +nm_utils_get_monotonic_timestamp_s (void) +{ + const GlobalState *p; + struct timespec tp; + + p = _t_clock_gettime (&tp); + + return (((gint64) tp.tv_sec) + p->offset_sec); +} + +/** + * nm_utils_monotonic_timestamp_as_boottime: + * @timestamp: the monotonic-timestamp that should be converted into CLOCK_BOOTTIME. + * @timestamp_ns_per_tick: How many nano seconds make one unit of @timestamp? E.g. if + * @timestamp is in unit seconds, pass %NM_UTILS_NS_PER_SECOND; @timestamp in nano + * seconds, pass 1; @timestamp in milli seconds, pass %NM_UTILS_NS_PER_SECOND/1000; etc. + * + * Returns: the monotonic-timestamp as CLOCK_BOOTTIME, as returned by clock_gettime(). + * The unit is the same as the passed in @timestamp basd on @timestamp_ns_per_tick. + * E.g. if you passed @timestamp in as seconds, it will return boottime in seconds. + * If @timestamp is a non-positive, it returns -1. Note that a (valid) monotonic-timestamp + * is always positive. + * + * On older kernels that don't support CLOCK_BOOTTIME, the returned time is instead CLOCK_MONOTONIC. + **/ +gint64 +nm_utils_monotonic_timestamp_as_boottime (gint64 timestamp, gint64 timestamp_ns_per_tick) +{ + const GlobalState *p; + gint64 offset; + + /* only support ns-per-tick being a multiple of 10. */ + g_return_val_if_fail (timestamp_ns_per_tick == 1 + || (timestamp_ns_per_tick > 0 && + timestamp_ns_per_tick <= NM_UTILS_NS_PER_SECOND && + timestamp_ns_per_tick % 10 == 0), + -1); + + /* Check that the timestamp is in a valid range. */ + g_return_val_if_fail (timestamp >= 0, -1); + + /* if the caller didn't yet ever fetch a monotonic-timestamp, he cannot pass any meaningful + * value (because he has no idea what these timestamps would be). That would be a bug. */ + nm_assert (g_atomic_pointer_get (&p_global_state)); + + p = _t_get_global_state (); + + /* calculate the offset of monotonic-timestamp to boottime. offset_s is <= 1. */ + offset = p->offset_sec * (NM_UTILS_NS_PER_SECOND / timestamp_ns_per_tick); + + /* check for overflow. */ + g_return_val_if_fail (offset > 0 || timestamp < G_MAXINT64 + offset, G_MAXINT64); + + return timestamp - offset; +} diff --git a/shared/nm-glib-aux/nm-time-utils.h b/shared/nm-glib-aux/nm-time-utils.h new file mode 100644 index 00000000..7e4f4f25 --- /dev/null +++ b/shared/nm-glib-aux/nm-time-utils.h @@ -0,0 +1,45 @@ +/* NetworkManager -- Network link manager + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA. + * + * (C) Copyright 2018 Red Hat, Inc. + */ + +#ifndef __NM_TIME_UTILS_H__ +#define __NM_TIME_UTILS_H__ + +gint64 nm_utils_get_monotonic_timestamp_ns (void); +gint64 nm_utils_get_monotonic_timestamp_us (void); +gint64 nm_utils_get_monotonic_timestamp_ms (void); +gint32 nm_utils_get_monotonic_timestamp_s (void); +gint64 nm_utils_monotonic_timestamp_as_boottime (gint64 timestamp, gint64 timestamp_ticks_per_ns); + +static inline gint64 +nm_utils_get_monotonic_timestamp_ns_cached (gint64 *cache_now) +{ + return (*cache_now) + ?: (*cache_now = nm_utils_get_monotonic_timestamp_ns ()); +} + +struct timespec; + +/* this function must be implemented to handle the notification when + * the first monotonic-timestamp is fetched. */ +extern void _nm_utils_monotonic_timestamp_initialized (const struct timespec *tp, + gint64 offset_sec, + gboolean is_boottime); + +#endif /* __NM_TIME_UTILS_H__ */ diff --git a/shared/nm-libnm-core-aux/nm-dispatcher-api.h b/shared/nm-libnm-core-aux/nm-dispatcher-api.h new file mode 100644 index 00000000..e6d0d92f --- /dev/null +++ b/shared/nm-libnm-core-aux/nm-dispatcher-api.h @@ -0,0 +1,65 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* NetworkManager -- Network link manager + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Copyright (C) 2008 - 2012 Red Hat, Inc. + */ + +#ifndef __NM_DISPACHER_API_H__ +#define __NM_DISPACHER_API_H__ + +#define NMD_SCRIPT_DIR_DEFAULT NMCONFDIR "/dispatcher.d" +#define NMD_SCRIPT_DIR_PRE_UP NMD_SCRIPT_DIR_DEFAULT "/pre-up.d" +#define NMD_SCRIPT_DIR_PRE_DOWN NMD_SCRIPT_DIR_DEFAULT "/pre-down.d" +#define NMD_SCRIPT_DIR_NO_WAIT NMD_SCRIPT_DIR_DEFAULT "/no-wait.d" + +#define NM_DISPATCHER_DBUS_SERVICE "org.freedesktop.nm_dispatcher" +#define NM_DISPATCHER_DBUS_INTERFACE "org.freedesktop.nm_dispatcher" +#define NM_DISPATCHER_DBUS_PATH "/org/freedesktop/nm_dispatcher" + +#define NMD_CONNECTION_PROPS_PATH "path" +#define NMD_CONNECTION_PROPS_FILENAME "filename" +#define NMD_CONNECTION_PROPS_EXTERNAL "external" + +#define NMD_DEVICE_PROPS_INTERFACE "interface" +#define NMD_DEVICE_PROPS_IP_INTERFACE "ip-interface" +#define NMD_DEVICE_PROPS_TYPE "type" +#define NMD_DEVICE_PROPS_STATE "state" +#define NMD_DEVICE_PROPS_PATH "path" + +/* Actions */ +#define NMD_ACTION_HOSTNAME "hostname" +#define NMD_ACTION_PRE_UP "pre-up" +#define NMD_ACTION_UP "up" +#define NMD_ACTION_PRE_DOWN "pre-down" +#define NMD_ACTION_DOWN "down" +#define NMD_ACTION_VPN_PRE_UP "vpn-pre-up" +#define NMD_ACTION_VPN_UP "vpn-up" +#define NMD_ACTION_VPN_PRE_DOWN "vpn-pre-down" +#define NMD_ACTION_VPN_DOWN "vpn-down" +#define NMD_ACTION_DHCP4_CHANGE "dhcp4-change" +#define NMD_ACTION_DHCP6_CHANGE "dhcp6-change" +#define NMD_ACTION_CONNECTIVITY_CHANGE "connectivity-change" + +typedef enum { + DISPATCH_RESULT_UNKNOWN = 0, + DISPATCH_RESULT_SUCCESS = 1, + DISPATCH_RESULT_EXEC_FAILED = 2, + DISPATCH_RESULT_FAILED = 3, + DISPATCH_RESULT_TIMEOUT = 4, +} DispatchResult; + +#endif /* __NM_DISPACHER_API_H__ */ diff --git a/shared/nm-libnm-core-intern/nm-common-macros.h b/shared/nm-libnm-core-intern/nm-common-macros.h new file mode 100644 index 00000000..f5aa3a1e --- /dev/null +++ b/shared/nm-libnm-core-intern/nm-common-macros.h @@ -0,0 +1,62 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* NetworkManager -- Network link manager + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA. + * + * (C) Copyright 2016 Red Hat, Inc. + */ + +#ifndef __NM_COMMON_MACROS_H__ +#define __NM_COMMON_MACROS_H__ + +/*****************************************************************************/ + +#define NM_AUTH_PERMISSION_ENABLE_DISABLE_NETWORK "org.freedesktop.NetworkManager.enable-disable-network" +#define NM_AUTH_PERMISSION_SLEEP_WAKE "org.freedesktop.NetworkManager.sleep-wake" +#define NM_AUTH_PERMISSION_ENABLE_DISABLE_WIFI "org.freedesktop.NetworkManager.enable-disable-wifi" +#define NM_AUTH_PERMISSION_ENABLE_DISABLE_WWAN "org.freedesktop.NetworkManager.enable-disable-wwan" +#define NM_AUTH_PERMISSION_ENABLE_DISABLE_WIMAX "org.freedesktop.NetworkManager.enable-disable-wimax" +#define NM_AUTH_PERMISSION_NETWORK_CONTROL "org.freedesktop.NetworkManager.network-control" +#define NM_AUTH_PERMISSION_WIFI_SHARE_PROTECTED "org.freedesktop.NetworkManager.wifi.share.protected" +#define NM_AUTH_PERMISSION_WIFI_SHARE_OPEN "org.freedesktop.NetworkManager.wifi.share.open" +#define NM_AUTH_PERMISSION_SETTINGS_MODIFY_SYSTEM "org.freedesktop.NetworkManager.settings.modify.system" +#define NM_AUTH_PERMISSION_SETTINGS_MODIFY_OWN "org.freedesktop.NetworkManager.settings.modify.own" +#define NM_AUTH_PERMISSION_SETTINGS_MODIFY_HOSTNAME "org.freedesktop.NetworkManager.settings.modify.hostname" +#define NM_AUTH_PERMISSION_SETTINGS_MODIFY_GLOBAL_DNS "org.freedesktop.NetworkManager.settings.modify.global-dns" +#define NM_AUTH_PERMISSION_RELOAD "org.freedesktop.NetworkManager.reload" +#define NM_AUTH_PERMISSION_CHECKPOINT_ROLLBACK "org.freedesktop.NetworkManager.checkpoint-rollback" +#define NM_AUTH_PERMISSION_ENABLE_DISABLE_STATISTICS "org.freedesktop.NetworkManager.enable-disable-statistics" +#define NM_AUTH_PERMISSION_ENABLE_DISABLE_CONNECTIVITY_CHECK "org.freedesktop.NetworkManager.enable-disable-connectivity-check" +#define NM_AUTH_PERMISSION_WIFI_SCAN "org.freedesktop.NetworkManager.wifi.scan" + +#define NM_CLONED_MAC_PRESERVE "preserve" +#define NM_CLONED_MAC_PERMANENT "permanent" +#define NM_CLONED_MAC_RANDOM "random" +#define NM_CLONED_MAC_STABLE "stable" + +static inline gboolean +NM_CLONED_MAC_IS_SPECIAL (const char *str) +{ + return NM_IN_STRSET (str, + NM_CLONED_MAC_PRESERVE, + NM_CLONED_MAC_PERMANENT, + NM_CLONED_MAC_RANDOM, + NM_CLONED_MAC_STABLE); +} + +/*****************************************************************************/ + +#endif /* __NM_COMMON_MACROS_H__ */ diff --git a/shared/nm-libnm-core-intern/nm-ethtool-utils.c b/shared/nm-libnm-core-intern/nm-ethtool-utils.c new file mode 100644 index 00000000..3313274a --- /dev/null +++ b/shared/nm-libnm-core-intern/nm-ethtool-utils.c @@ -0,0 +1,225 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ + +/* + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA. + * + * Copyright 2018 Red Hat, Inc. + */ + +#include "nm-default.h" + +#include "nm-ethtool-utils.h" + +#include "nm-setting-ethtool.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 (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_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_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_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_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_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), + [_NM_ETHTOOL_ID_NUM] = NULL, +}; + +static const guint8 _by_name[_NM_ETHTOOL_ID_NUM] = { + /* sorted by optname. */ + 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_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_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_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_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_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, +}; + +/*****************************************************************************/ + +static void +_ASSERT_data (void) +{ +#if NM_MORE_ASSERTS > 10 + int i; + + 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); + } + } + } +#endif +} + +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; + + nm_assert (optname); + + _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]]; +} diff --git a/shared/nm-libnm-core-intern/nm-ethtool-utils.h b/shared/nm-libnm-core-intern/nm-ethtool-utils.h new file mode 100644 index 00000000..5f22a9a0 --- /dev/null +++ b/shared/nm-libnm-core-intern/nm-ethtool-utils.h @@ -0,0 +1,120 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA. + * + * Copyright 2018 Red Hat, Inc. + */ + +#ifndef __NM_ETHTOOL_UTILS_H__ +#define __NM_ETHTOOL_UTILS_H__ + +/*****************************************************************************/ + +typedef enum { + NM_ETHTOOL_ID_UNKNOWN = -1, + + _NM_ETHTOOL_ID_FIRST = 0, + + _NM_ETHTOOL_ID_FEATURE_FIRST = _NM_ETHTOOL_ID_FIRST, + 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_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_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_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_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_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_FEATURE_NUM = (_NM_ETHTOOL_ID_FEATURE_LAST - _NM_ETHTOOL_ID_FEATURE_FIRST + 1), + + _NM_ETHTOOL_ID_LAST = _NM_ETHTOOL_ID_FEATURE_LAST, + + _NM_ETHTOOL_ID_NUM = (_NM_ETHTOOL_ID_LAST - _NM_ETHTOOL_ID_FIRST + 1), +} NMEthtoolID; + +typedef struct { + const char *optname; + NMEthtoolID id; +} NMEthtoolData; + +extern const NMEthtoolData *const nm_ethtool_data[/*_NM_ETHTOOL_ID_NUM + NULL-terminated*/]; + +const NMEthtoolData *nm_ethtool_data_get_by_optname (const char *optname); + +/****************************************************************************/ + +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; +} + +static inline gboolean +nm_ethtool_id_is_feature (NMEthtoolID id) +{ + return id >= _NM_ETHTOOL_ID_FEATURE_FIRST && id <= _NM_ETHTOOL_ID_FEATURE_LAST; +} + +/****************************************************************************/ + +#endif /* __NM_ETHTOOL_UTILS_H__ */ diff --git a/shared/nm-libnm-core-intern/nm-libnm-core-utils.c b/shared/nm-libnm-core-intern/nm-libnm-core-utils.c new file mode 100644 index 00000000..d1e5f754 --- /dev/null +++ b/shared/nm-libnm-core-intern/nm-libnm-core-utils.c @@ -0,0 +1,76 @@ +/* + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA. + */ + +#include "nm-default.h" + +#include "nm-libnm-core-utils.h" + +/*****************************************************************************/ + +gboolean +nm_utils_vlan_priority_map_parse_str (NMVlanPriorityMap map_type, + const char *str, + gboolean allow_wildcard_to, + guint32 *out_from, + guint32 *out_to, + gboolean *out_has_wildcard_to) +{ + const char *s2; + gint64 v1, v2; + + nm_assert (str); + + s2 = strchr (str, ':'); + + if (!s2) { + if (!allow_wildcard_to) + return FALSE; + v1 = _nm_utils_ascii_str_to_int64 (str, 10, 0, G_MAXUINT32, -1); + v2 = -1; + } else { + gs_free char *s1_free = NULL; + gsize s1_len = (s2 - str); + + s2 = nm_str_skip_leading_spaces (&s2[1]); + if ( s2[0] == '\0' + || ( s2[0] == '*' + && NM_STRCHAR_ALL (&s2[1], ch, g_ascii_isspace (ch)))) { + if (!allow_wildcard_to) + return FALSE; + v2 = -1; + } else { + v2 = _nm_utils_ascii_str_to_int64 (s2, 10, 0, G_MAXUINT32, -1); + if ( v2 < 0 + || (guint32) v2 > nm_utils_vlan_priority_map_get_max_prio (map_type, FALSE)) + return FALSE; + } + + v1 = _nm_utils_ascii_str_to_int64 (nm_strndup_a (100, str, s1_len, &s1_free), + 10, 0, G_MAXUINT32, -1); + } + + if ( v1 < 0 + || (guint32) v1 > nm_utils_vlan_priority_map_get_max_prio (map_type, TRUE)) + return FALSE; + + NM_SET_OUT (out_from, v1); + NM_SET_OUT (out_to, v2 < 0 + ? 0u + : (guint) v2); + NM_SET_OUT (out_has_wildcard_to, v2 < 0); + return TRUE; +} diff --git a/shared/nm-libnm-core-intern/nm-libnm-core-utils.h b/shared/nm-libnm-core-intern/nm-libnm-core-utils.h new file mode 100644 index 00000000..35d6c5ad --- /dev/null +++ b/shared/nm-libnm-core-intern/nm-libnm-core-utils.h @@ -0,0 +1,113 @@ +/* + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA. + */ + +#ifndef __NM_LIBNM_SHARED_UTILS_H__ +#define __NM_LIBNM_SHARED_UTILS_H__ + +/****************************************************************************/ + +#include "nm-setting-bridge.h" +#include "nm-setting-connection.h" +#include "nm-setting-ip-config.h" +#include "nm-setting-ip4-config.h" +#include "nm-setting-ip6-config.h" +#include "nm-setting-sriov.h" +#include "nm-setting-team.h" +#include "nm-setting-vlan.h" +#include "nm-setting-wireguard.h" + +/****************************************************************************/ + +#define nm_auto_unref_ip_address nm_auto (_nm_ip_address_unref) +NM_AUTO_DEFINE_FCN0 (NMIPAddress *, _nm_ip_address_unref, nm_ip_address_unref) + +#define nm_auto_unref_ip_route nm_auto (_nm_auto_unref_ip_route) +NM_AUTO_DEFINE_FCN0 (NMIPRoute *, _nm_auto_unref_ip_route, nm_ip_route_unref) + +#define nm_auto_unref_ip_routing_rule nm_auto(_nm_auto_unref_ip_routing_rule) +NM_AUTO_DEFINE_FCN0 (NMIPRoutingRule *, _nm_auto_unref_ip_routing_rule, nm_ip_routing_rule_unref) + +#define nm_auto_unref_sriov_vf nm_auto (_nm_auto_unref_sriov_vf) +NM_AUTO_DEFINE_FCN0 (NMSriovVF *, _nm_auto_unref_sriov_vf, nm_sriov_vf_unref) + +#define nm_auto_unref_tc_qdisc nm_auto (_nm_auto_unref_tc_qdisc) +NM_AUTO_DEFINE_FCN0 (NMTCQdisc *, _nm_auto_unref_tc_qdisc, nm_tc_qdisc_unref) + +#define nm_auto_unref_tc_tfilter nm_auto (_nm_auto_unref_tc_tfilter) +NM_AUTO_DEFINE_FCN0 (NMTCTfilter *, _nm_auto_unref_tc_tfilter, nm_tc_tfilter_unref) + +#define nm_auto_unref_bridge_vlan nm_auto (_nm_auto_unref_bridge_vlan) +NM_AUTO_DEFINE_FCN0 (NMBridgeVlan *, _nm_auto_unref_bridge_vlan, nm_bridge_vlan_unref) + +#define nm_auto_unref_team_link_watcher nm_auto (_nm_auto_unref_team_link_watcher) +NM_AUTO_DEFINE_FCN0 (NMTeamLinkWatcher *, _nm_auto_unref_team_link_watcher, nm_team_link_watcher_unref) + +#define nm_auto_unref_wgpeer nm_auto (_nm_auto_unref_wgpeer) +NM_AUTO_DEFINE_FCN0 (NMWireGuardPeer *, _nm_auto_unref_wgpeer, nm_wireguard_peer_unref) + +/****************************************************************************/ + +static inline guint32 +nm_utils_vlan_priority_map_get_max_prio (NMVlanPriorityMap map, gboolean from) +{ + if (map == NM_VLAN_INGRESS_MAP) { + return from + ? 7u /* MAX_8021P_PRIO */ + : (guint32) G_MAXUINT32 /* MAX_SKB_PRIO */; + } + nm_assert (map == NM_VLAN_EGRESS_MAP); + return from + ? (guint32) G_MAXUINT32 /* MAX_SKB_PRIO */ + : 7u /* MAX_8021P_PRIO */; +} + +gboolean nm_utils_vlan_priority_map_parse_str (NMVlanPriorityMap map_type, + const char *str, + gboolean allow_wildcard_to, + guint32 *out_from, + guint32 *out_to, + gboolean *out_has_wildcard_to); + +/*****************************************************************************/ + +static inline int +nm_setting_ip_config_get_addr_family (NMSettingIPConfig *s_ip) +{ + if (NM_IS_SETTING_IP4_CONFIG (s_ip)) + return AF_INET; + if (NM_IS_SETTING_IP6_CONFIG (s_ip)) + return AF_INET6; + g_return_val_if_reached (AF_UNSPEC); +} + +/*****************************************************************************/ + +/* The maximum MTU for infiniband. + * + * This is both in transport-mode "datagram" and "connected" + * and they both have the same maximum define. + * + * Note that in the past, MTU in "datagram" mode was restricted + * to 2044 bytes. That is no longer the case and we accept large + * MTUs. + * + * This define is the maxiumum for the MTU in a connection profile (the + * setting). Whether large MTUs can be configured later (at activation time) + * depends on other factors. */ +#define NM_INFINIBAND_MAX_MTU ((guint) 65520) + +#endif /* __NM_LIBNM_SHARED_UTILS_H__ */ diff --git a/shared/nm-meta-setting.c b/shared/nm-meta-setting.c index e666e0b2..8d1d4ecd 100644 --- a/shared/nm-meta-setting.c +++ b/shared/nm-meta-setting.c @@ -82,6 +82,7 @@ const NMSetting8021xSchemeVtable nm_setting_8021x_scheme_vtable[] = { .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", }, @@ -94,6 +95,7 @@ const NMSetting8021xSchemeVtable nm_setting_8021x_scheme_vtable[] = { .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", }, @@ -106,6 +108,7 @@ const NMSetting8021xSchemeVtable nm_setting_8021x_scheme_vtable[] = { .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", }, @@ -118,6 +121,7 @@ const NMSetting8021xSchemeVtable nm_setting_8021x_scheme_vtable[] = { .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", }, @@ -130,7 +134,9 @@ const NMSetting8021xSchemeVtable nm_setting_8021x_scheme_vtable[] = { .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, }, [NM_SETTING_802_1X_SCHEME_TYPE_PHASE2_PRIVATE_KEY] = { @@ -142,7 +148,9 @@ const NMSetting8021xSchemeVtable nm_setting_8021x_scheme_vtable[] = { .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, }, [NM_SETTING_802_1X_SCHEME_TYPE_UNKNOWN] = { NULL }, diff --git a/shared/nm-meta-setting.h b/shared/nm-meta-setting.h index 18727a16..73ee103e 100644 --- a/shared/nm-meta-setting.h +++ b/shared/nm-meta-setting.h @@ -89,7 +89,19 @@ typedef struct { 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; + bool is_secret:1; } NMSetting8021xSchemeVtable; extern const NMSetting8021xSchemeVtable nm_setting_8021x_scheme_vtable[_NM_SETTING_802_1X_SCHEME_TYPE_NUM + 1]; diff --git a/shared/nm-std-aux/c-list-util.c b/shared/nm-std-aux/c-list-util.c new file mode 100644 index 00000000..44ca26a5 --- /dev/null +++ b/shared/nm-std-aux/c-list-util.c @@ -0,0 +1,209 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* NetworkManager -- Network link manager + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA. + * + * (C) Copyright 2017 Red Hat, Inc. + */ + +#include "c-list-util.h" + +/*****************************************************************************/ + +/** + * c_list_relink: + * @lst: the head list entry + * + * Takes an invalid list, that has undefined prev pointers. + * Only the next pointers are valid, and the tail's next + * pointer points to %NULL instead of the head. + * + * c_list_relink() fixes the list by updating all prev pointers + * and close the circular linking by pointing the tails' next + * 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, + * the list can be fixed by c_list_relink(). + */ +void +c_list_relink (CList *lst) +{ + CList *ls, *ls_prev; + + ls_prev = lst; + ls = lst->next; + do { + ls->prev = ls_prev; + ls_prev = ls; + ls = ls->next; + } while (ls); + ls_prev->next = lst; + lst->prev = ls_prev; +} + +/*****************************************************************************/ + +static CList * +_c_list_srt_split (CList *ls) +{ + CList *ls2; + + ls2 = ls; + ls = ls->next; + if (!ls) + return NULL; + do { + ls = ls->next; + if (!ls) + break; + ls = ls->next; + ls2 = ls2->next; + } while (ls); + ls = ls2->next; + ls2->next = NULL; + return ls; +} + +static CList * +_c_list_srt_merge (CList *ls1, + CList *ls2, + CListSortCmp cmp, + const void *user_data) +{ + CList *ls; + CList head; + + ls = &head; + for (;;) { + /* while invoking the @cmp function, the list + * elements are not properly linked. Don't try to access + * their next/prev pointers. */ + if (cmp (ls1, ls2, user_data) <= 0) { + ls->next = ls1; + ls = ls1; + ls1 = ls1->next; + if (!ls1) + break; + } else { + ls->next = ls2; + ls = ls2; + ls2 = ls2->next; + if (!ls2) + break; + } + } + ls->next = ls1 ?: ls2; + + return head.next; +} + +typedef struct { + CList *ls1; + CList *ls2; + char ls1_sorted; +} SortStack; + +static CList * +_c_list_sort (CList *ls, + CListSortCmp cmp, + const void *user_data) +{ + /* reserve a huge stack-size. We need roughly log2(n) entries, hence this + * is much more we will ever need. We don't guard for stack-overflow either. */ + SortStack stack_arr[70]; + SortStack *stack_head = stack_arr; + + stack_arr[0].ls1 = ls; + + /* A simple top-down, non-recursive, stable merge-sort. + * + * Maybe natural merge-sort would be better, to do better for + * partially sorted lists. */ +_split: + stack_head[0].ls2 = _c_list_srt_split (stack_head[0].ls1); + if (stack_head[0].ls2) { + stack_head[0].ls1_sorted = 0; + stack_head[1].ls1 = stack_head[0].ls1; + stack_head++; + goto _split; + } + +_backtrack: + if (stack_head == stack_arr) + return stack_arr[0].ls1; + + stack_head--; + if (!stack_head[0].ls1_sorted) { + stack_head[0].ls1 = stack_head[1].ls1; + stack_head[0].ls1_sorted = 1; + stack_head[1].ls1 = stack_head[0].ls2; + stack_head++; + goto _split; + } + + stack_head[0].ls1 = _c_list_srt_merge (stack_head[0].ls1, stack_head[1].ls1, cmp, user_data); + goto _backtrack; +} + +/** + * c_list_sort_headless: + * @lst: the list. + * @cmp: compare function for sorting. While comparing two + * CList elements, their next/prev pointers are in undefined + * state. + * @user_data: user data for @cmp. + * + * Sorts the list @lst according to @cmp. Contrary to + * c_list_sort(), @lst is not the list head but a + * valid entry as well. This function returns the new + * list head. + */ +CList * +c_list_sort_headless (CList *lst, + CListSortCmp cmp, + const void *user_data) +{ + if (!c_list_is_empty (lst)) { + lst->prev->next = NULL; + lst = _c_list_sort (lst, cmp, user_data); + c_list_relink (lst); + } + return lst; +} + +/** + * c_list_sort: + * @head: the list head. + * @cmp: compare function for sorting. While comparing two + * CList elements, their next/prev pointers are in undefined + * state. + * @user_data: user data for @cmp. + * + * Sorts the list @head according to @cmp. + */ +void +c_list_sort (CList *head, + CListSortCmp cmp, + const void *user_data) +{ + if ( !c_list_is_empty (head) + && head->next->next != head) { + head->prev->next = NULL; + head->next = _c_list_sort (head->next, cmp, user_data); + c_list_relink (head); + } +} diff --git a/shared/nm-std-aux/c-list-util.h b/shared/nm-std-aux/c-list-util.h new file mode 100644 index 00000000..648bacc7 --- /dev/null +++ b/shared/nm-std-aux/c-list-util.h @@ -0,0 +1,66 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* NetworkManager -- Network link manager + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA. + * + * (C) Copyright 2017 Red Hat, Inc. + */ + +#ifndef __C_LIST_UTIL_H__ +#define __C_LIST_UTIL_H__ + +#include "c-list/src/c-list.h" + +/*****************************************************************************/ + +void c_list_relink (CList *lst); + +typedef int (*CListSortCmp) (const CList *a, + const CList *b, + const void *user_data); + +CList *c_list_sort_headless (CList *lst, + CListSortCmp cmp, + const void *user_data); + +void c_list_sort (CList *head, + CListSortCmp cmp, + const void *user_data); + +/* c_list_length_is: + * @list: the #CList list head + * @check_len: the length to compare + * + * Returns: basically the same as (c_list_length (@list) == @check_len), + * but does not require to iterate the entire list first. There is only + * one real use: to find out whether there is exactly one element in the + * list, by passing @check_len as 1. + */ +static inline int +c_list_length_is (const CList *list, unsigned long check_len) { + unsigned long n = 0; + const CList *iter; + + c_list_for_each (iter, list) { + ++n; + if (n > check_len) + return 0; + } + + return n == check_len; +} + +#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 new file mode 100644 index 00000000..dd97b5fd --- /dev/null +++ b/shared/nm-std-aux/nm-dbus-compat.h @@ -0,0 +1,74 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Copyright 2015 Red Hat, Inc. + */ + +#ifndef __NM_DBUS_COMPAT_H__ +#define __NM_DBUS_COMPAT_H__ + +/* Copied from */ + +/* Bus names */ + +/** The bus name used to talk to the bus itself. */ +#define DBUS_SERVICE_DBUS "org.freedesktop.DBus" + +/* Paths */ +/** The object path used to talk to the bus itself. */ +#define DBUS_PATH_DBUS "/org/freedesktop/DBus" +/** The object path used in local/in-process-generated messages. */ +#define DBUS_PATH_LOCAL "/org/freedesktop/DBus/Local" + +/* Interfaces, these #define don't do much other than + * catch typos at compile time + */ +/** The interface exported by the object with #DBUS_SERVICE_DBUS and #DBUS_PATH_DBUS */ +#define DBUS_INTERFACE_DBUS "org.freedesktop.DBus" +/** The interface supported by introspectable objects */ +#define DBUS_INTERFACE_INTROSPECTABLE "org.freedesktop.DBus.Introspectable" +/** The interface supported by objects with properties */ +#define DBUS_INTERFACE_PROPERTIES "org.freedesktop.DBus.Properties" +/** The interface supported by most dbus peers */ +#define DBUS_INTERFACE_PEER "org.freedesktop.DBus.Peer" + +/** This is a special interface whose methods can only be invoked + * by the local implementation (messages from remote apps aren't + * allowed to specify this interface). + */ +#define DBUS_INTERFACE_LOCAL "org.freedesktop.DBus.Local" + +/* Owner flags */ +#define DBUS_NAME_FLAG_ALLOW_REPLACEMENT 0x1 /**< Allow another service to become the primary owner if requested */ +#define DBUS_NAME_FLAG_REPLACE_EXISTING 0x2 /**< Request to replace the current primary owner */ +#define DBUS_NAME_FLAG_DO_NOT_QUEUE 0x4 /**< If we can not become the primary owner do not place us in the queue */ + +/* Replies to request for a name */ +#define DBUS_REQUEST_NAME_REPLY_PRIMARY_OWNER 1 /**< Service has become the primary owner of the requested name */ +#define DBUS_REQUEST_NAME_REPLY_IN_QUEUE 2 /**< Service could not become the primary owner and has been placed in the queue */ +#define DBUS_REQUEST_NAME_REPLY_EXISTS 3 /**< Service is already in the queue */ +#define DBUS_REQUEST_NAME_REPLY_ALREADY_OWNER 4 /**< Service is already the primary owner */ + +/* Replies to releasing a name */ +#define DBUS_RELEASE_NAME_REPLY_RELEASED 1 /**< Service was released from the given name */ +#define DBUS_RELEASE_NAME_REPLY_NON_EXISTENT 2 /**< The given name does not exist on the bus */ +#define DBUS_RELEASE_NAME_REPLY_NOT_OWNER 3 /**< Service is not an owner of the given name */ + +/* Replies to service starts */ +#define DBUS_START_REPLY_SUCCESS 1 /**< Service was auto started */ +#define DBUS_START_REPLY_ALREADY_RUNNING 2 /**< Service was already running */ + +#endif /* __NM_DBUS_COMPAT_H__ */ diff --git a/shared/nm-std-aux/unaligned.h b/shared/nm-std-aux/unaligned.h new file mode 100644 index 00000000..00c17f87 --- /dev/null +++ b/shared/nm-std-aux/unaligned.h @@ -0,0 +1,99 @@ +/* SPDX-License-Identifier: LGPL-2.1+ */ +#pragma once + +#include +#include + +/* BE */ + +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); +} + +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); +} + +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); +} + +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); +} + +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); +} + +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); +} + +/* LE */ + +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); +} + +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); +} + +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); +} + +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); +} + +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); +} + +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); +} + +#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_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_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-utils-impl.c b/shared/nm-test-utils-impl.c index 509b235a..02d71593 100644 --- a/shared/nm-test-utils-impl.c +++ b/shared/nm-test-utils-impl.c @@ -23,7 +23,7 @@ #include #include "NetworkManager.h" -#include "nm-dbus-compat.h" +#include "nm-std-aux/nm-dbus-compat.h" #include "nm-test-libnm-utils.h" diff --git a/shared/nm-udev-aux/nm-udev-utils.c b/shared/nm-udev-aux/nm-udev-utils.c new file mode 100644 index 00000000..5d0919b3 --- /dev/null +++ b/shared/nm-udev-aux/nm-udev-utils.c @@ -0,0 +1,291 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* nm-udev-utils.c - udev utils functions + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Copyright (C) 2017 Red Hat, Inc. + */ + +#include "nm-default.h" + +#include "nm-udev-utils.h" + +#include + +struct _NMPUdevClient { + char **subsystems; + GSource *watch_source; + struct udev *udev; + struct udev_monitor *monitor; + NMUdevClientEvent event_handler; + gpointer event_user_data; +}; + +/*****************************************************************************/ + +gboolean +nm_udev_utils_property_as_boolean (const char *uproperty) +{ + /* taken from g_udev_device_get_property_as_boolean() */ + + if (uproperty) { + if ( strcmp (uproperty, "1") == 0 + || g_ascii_strcasecmp (uproperty, "true") == 0) + return TRUE; + } + return FALSE; +} + +const char * +nm_udev_utils_property_decode (const char *uproperty, char **to_free) +{ + const char *p; + char *unescaped = NULL; + char *n = NULL; + + if (!uproperty) { + *to_free = NULL; + return NULL; + } + + p = uproperty; + while (*p) { + int a, b; + + if ( p[0] == '\\' + && p[1] == 'x' + && (a = g_ascii_xdigit_value (p[2])) >= 0 + && (b = g_ascii_xdigit_value (p[3])) >= 0 + && (a || b)) { + if (!n) { + gssize l = p - uproperty; + + unescaped = g_malloc (l + strlen (p) + 1 - 3); + memcpy (unescaped, uproperty, l); + n = &unescaped[l]; + } + *n++ = (a << 4) | b; + p += 4; + } else { + if (n) + *n++ = *p; + p++; + } + } + + if (!n) { + *to_free = NULL; + return uproperty; + } + + *n++ = '\0'; + return (*to_free = unescaped); +} + +char * +nm_udev_utils_property_decode_cp (const char *uproperty) +{ + char *cpy; + + uproperty = nm_udev_utils_property_decode (uproperty, &cpy); + return cpy ?: g_strdup (uproperty); +} + +/*****************************************************************************/ + +static void +_subsystem_split (const char *subsystem_full, + const char **out_subsystem, + const char **out_devtype, + char **to_free) +{ + char *tmp, *s; + + nm_assert (subsystem_full); + nm_assert (out_subsystem); + nm_assert (out_devtype); + nm_assert (to_free); + + s = strstr (subsystem_full, "/"); + if (s) { + tmp = g_strdup (subsystem_full); + s = &tmp[s - subsystem_full]; + *s = '\0'; + *out_subsystem = tmp; + *out_devtype = &s[1]; + *to_free = tmp; + } else { + *out_subsystem = subsystem_full; + *out_devtype = NULL; + *to_free = NULL; + } +} + +static struct udev_enumerate * +nm_udev_utils_enumerate (struct udev *uclient, + const char *const*subsystems) +{ + struct udev_enumerate *enumerate; + guint n; + + enumerate = udev_enumerate_new (uclient); + + if (subsystems) { + for (n = 0; subsystems[n]; n++) { + const char *subsystem; + const char *devtype; + gs_free char *to_free = NULL; + + _subsystem_split (subsystems[n], &subsystem, &devtype, &to_free); + + udev_enumerate_add_match_subsystem (enumerate, subsystem); + + if (devtype != NULL) + udev_enumerate_add_match_property (enumerate, "DEVTYPE", devtype); + } + } + + return enumerate; +} + +struct udev * +nm_udev_client_get_udev (NMUdevClient *self) +{ + g_return_val_if_fail (self, NULL); + + return self->udev; +} + +struct udev_enumerate * +nm_udev_client_enumerate_new (NMUdevClient *self) +{ + g_return_val_if_fail (self, NULL); + + return nm_udev_utils_enumerate (self->udev, (const char *const*) self->subsystems); +} + +/*****************************************************************************/ + +static gboolean +monitor_event (GIOChannel *source, + GIOCondition condition, + gpointer user_data) +{ + NMUdevClient *self = user_data; + struct udev_device *udevice; + + if (!self->monitor) + goto out; + + udevice = udev_monitor_receive_device (self->monitor); + if (udevice == NULL) + goto out; + + self->event_handler (self, + udevice, + self->event_user_data); + udev_device_unref (udevice); + +out: + return TRUE; +} + +/** + * nm_udev_client_new: + * @subsystems: the subsystems + * @event_handler: callback for events + * @event_user_data: user-data for @event_handler + * + * Basically, it is g_udev_client_new(), and most notably + * g_udev_client_constructed(). + * + * Returns: a new NMUdevClient instance. + */ +NMUdevClient * +nm_udev_client_new (const char *const*subsystems, + NMUdevClientEvent event_handler, + gpointer event_user_data) +{ + NMUdevClient *self; + GIOChannel *channel; + guint n; + + self = g_slice_new0 (NMUdevClient); + + self->event_handler = event_handler; + self->event_user_data = event_user_data; + self->subsystems = subsystems && subsystems[0] ? g_strdupv ((char **) subsystems) : NULL; + + self->udev = udev_new (); + if (!self->udev) + goto fail; + + /* connect to event source */ + if (self->event_handler) { + self->monitor = udev_monitor_new_from_netlink (self->udev, "udev"); + if (!self->monitor) + goto fail; + + if (self->subsystems) { + /* install subsystem filters to only wake up for certain events */ + for (n = 0; self->subsystems[n]; n++) { + gs_free char *to_free = NULL; + const char *subsystem; + const char *devtype; + + _subsystem_split (self->subsystems[n], &subsystem, &devtype, &to_free); + udev_monitor_filter_add_match_subsystem_devtype (self->monitor, subsystem, devtype); + } + + /* listen to events, and buffer them */ + udev_monitor_set_receive_buffer_size (self->monitor, 4*1024*1024); + udev_monitor_enable_receiving (self->monitor); + channel = g_io_channel_unix_new (udev_monitor_get_fd (self->monitor)); + self->watch_source = g_io_create_watch (channel, G_IO_IN); + g_io_channel_unref (channel); + g_source_set_callback (self->watch_source, (GSourceFunc)(void (*) (void)) monitor_event, self, NULL); + g_source_attach (self->watch_source, g_main_context_get_thread_default ()); + g_source_unref (self->watch_source); + } + } + + return self; + +fail: + return nm_udev_client_unref (self); +} + +NMUdevClient * +nm_udev_client_unref (NMUdevClient *self) +{ + if (!self) + return NULL; + + if (self->watch_source) { + g_source_destroy (self->watch_source); + self->watch_source = NULL; + } + + udev_monitor_unref (self->monitor); + self->monitor = NULL; + udev_unref (self->udev); + self->udev = NULL; + + g_strfreev (self->subsystems); + + g_slice_free (NMUdevClient, self); + + return NULL; +} diff --git a/shared/nm-udev-aux/nm-udev-utils.h b/shared/nm-udev-aux/nm-udev-utils.h new file mode 100644 index 00000000..911e8a27 --- /dev/null +++ b/shared/nm-udev-aux/nm-udev-utils.h @@ -0,0 +1,48 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* nm-udev-utils.h - udev utils functions + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Copyright (C) 2017 Red Hat, Inc. + */ + +#ifndef __NM_UDEV_UTILS_H__ +#define __NM_UDEV_UTILS_H__ + +struct udev; +struct udev_device; +struct udev_enumerate; + +gboolean nm_udev_utils_property_as_boolean (const char *uproperty); +const char *nm_udev_utils_property_decode (const char *uproperty, char **to_free); +char *nm_udev_utils_property_decode_cp (const char *uproperty); + +typedef struct _NMPUdevClient NMUdevClient; + +typedef void (*NMUdevClientEvent) (NMUdevClient *udev_client, + struct udev_device *udevice, + gpointer event_user_data); + +NMUdevClient *nm_udev_client_new (const char *const*subsystems, + NMUdevClientEvent event_handler, + gpointer event_user_data); + +NMUdevClient *nm_udev_client_unref (NMUdevClient *self); + +struct udev *nm_udev_client_get_udev (NMUdevClient *self); + +struct udev_enumerate *nm_udev_client_enumerate_new (NMUdevClient *self); + +#endif /* __NM_UDEV_UTILS_H__ */ diff --git a/shared/nm-utils/c-list-util.c b/shared/nm-utils/c-list-util.c deleted file mode 100644 index 44ca26a5..00000000 --- a/shared/nm-utils/c-list-util.c +++ /dev/null @@ -1,209 +0,0 @@ -/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ -/* NetworkManager -- Network link manager - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the - * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, - * Boston, MA 02110-1301 USA. - * - * (C) Copyright 2017 Red Hat, Inc. - */ - -#include "c-list-util.h" - -/*****************************************************************************/ - -/** - * c_list_relink: - * @lst: the head list entry - * - * Takes an invalid list, that has undefined prev pointers. - * Only the next pointers are valid, and the tail's next - * pointer points to %NULL instead of the head. - * - * c_list_relink() fixes the list by updating all prev pointers - * and close the circular linking by pointing the tails' next - * 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, - * the list can be fixed by c_list_relink(). - */ -void -c_list_relink (CList *lst) -{ - CList *ls, *ls_prev; - - ls_prev = lst; - ls = lst->next; - do { - ls->prev = ls_prev; - ls_prev = ls; - ls = ls->next; - } while (ls); - ls_prev->next = lst; - lst->prev = ls_prev; -} - -/*****************************************************************************/ - -static CList * -_c_list_srt_split (CList *ls) -{ - CList *ls2; - - ls2 = ls; - ls = ls->next; - if (!ls) - return NULL; - do { - ls = ls->next; - if (!ls) - break; - ls = ls->next; - ls2 = ls2->next; - } while (ls); - ls = ls2->next; - ls2->next = NULL; - return ls; -} - -static CList * -_c_list_srt_merge (CList *ls1, - CList *ls2, - CListSortCmp cmp, - const void *user_data) -{ - CList *ls; - CList head; - - ls = &head; - for (;;) { - /* while invoking the @cmp function, the list - * elements are not properly linked. Don't try to access - * their next/prev pointers. */ - if (cmp (ls1, ls2, user_data) <= 0) { - ls->next = ls1; - ls = ls1; - ls1 = ls1->next; - if (!ls1) - break; - } else { - ls->next = ls2; - ls = ls2; - ls2 = ls2->next; - if (!ls2) - break; - } - } - ls->next = ls1 ?: ls2; - - return head.next; -} - -typedef struct { - CList *ls1; - CList *ls2; - char ls1_sorted; -} SortStack; - -static CList * -_c_list_sort (CList *ls, - CListSortCmp cmp, - const void *user_data) -{ - /* reserve a huge stack-size. We need roughly log2(n) entries, hence this - * is much more we will ever need. We don't guard for stack-overflow either. */ - SortStack stack_arr[70]; - SortStack *stack_head = stack_arr; - - stack_arr[0].ls1 = ls; - - /* A simple top-down, non-recursive, stable merge-sort. - * - * Maybe natural merge-sort would be better, to do better for - * partially sorted lists. */ -_split: - stack_head[0].ls2 = _c_list_srt_split (stack_head[0].ls1); - if (stack_head[0].ls2) { - stack_head[0].ls1_sorted = 0; - stack_head[1].ls1 = stack_head[0].ls1; - stack_head++; - goto _split; - } - -_backtrack: - if (stack_head == stack_arr) - return stack_arr[0].ls1; - - stack_head--; - if (!stack_head[0].ls1_sorted) { - stack_head[0].ls1 = stack_head[1].ls1; - stack_head[0].ls1_sorted = 1; - stack_head[1].ls1 = stack_head[0].ls2; - stack_head++; - goto _split; - } - - stack_head[0].ls1 = _c_list_srt_merge (stack_head[0].ls1, stack_head[1].ls1, cmp, user_data); - goto _backtrack; -} - -/** - * c_list_sort_headless: - * @lst: the list. - * @cmp: compare function for sorting. While comparing two - * CList elements, their next/prev pointers are in undefined - * state. - * @user_data: user data for @cmp. - * - * Sorts the list @lst according to @cmp. Contrary to - * c_list_sort(), @lst is not the list head but a - * valid entry as well. This function returns the new - * list head. - */ -CList * -c_list_sort_headless (CList *lst, - CListSortCmp cmp, - const void *user_data) -{ - if (!c_list_is_empty (lst)) { - lst->prev->next = NULL; - lst = _c_list_sort (lst, cmp, user_data); - c_list_relink (lst); - } - return lst; -} - -/** - * c_list_sort: - * @head: the list head. - * @cmp: compare function for sorting. While comparing two - * CList elements, their next/prev pointers are in undefined - * state. - * @user_data: user data for @cmp. - * - * Sorts the list @head according to @cmp. - */ -void -c_list_sort (CList *head, - CListSortCmp cmp, - const void *user_data) -{ - if ( !c_list_is_empty (head) - && head->next->next != head) { - head->prev->next = NULL; - head->next = _c_list_sort (head->next, cmp, user_data); - c_list_relink (head); - } -} diff --git a/shared/nm-utils/c-list-util.h b/shared/nm-utils/c-list-util.h deleted file mode 100644 index e87f1c19..00000000 --- a/shared/nm-utils/c-list-util.h +++ /dev/null @@ -1,43 +0,0 @@ -/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ -/* NetworkManager -- Network link manager - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the - * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, - * Boston, MA 02110-1301 USA. - * - * (C) Copyright 2017 Red Hat, Inc. - */ - -#ifndef __C_LIST_UTIL_H__ -#define __C_LIST_UTIL_H__ - -#include "c-list/src/c-list.h" - -/*****************************************************************************/ - -void c_list_relink (CList *lst); - -typedef int (*CListSortCmp) (const CList *a, - const CList *b, - const void *user_data); - -CList *c_list_sort_headless (CList *lst, - CListSortCmp cmp, - const void *user_data); - -void c_list_sort (CList *head, - CListSortCmp cmp, - const void *user_data); - -#endif /* __C_LIST_UTIL_H__ */ diff --git a/shared/nm-utils/nm-c-list.h b/shared/nm-utils/nm-c-list.h deleted file mode 100644 index 5c73f574..00000000 --- a/shared/nm-utils/nm-c-list.h +++ /dev/null @@ -1,117 +0,0 @@ -/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ -/* NetworkManager -- Network link manager - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the - * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, - * Boston, MA 02110-1301 USA. - * - * (C) Copyright 2014 Red Hat, Inc. - */ - -#ifndef __NM_C_LIST_H__ -#define __NM_C_LIST_H__ - -#include "c-list/src/c-list.h" - -/*****************************************************************************/ - -#define nm_c_list_contains_entry(list, what, member) \ - ({ \ - typeof (what) _what = (what); \ - \ - _what && c_list_contains (list, &_what->member); \ - }) - -typedef struct { - CList lst; - void *data; -} NMCListElem; - -static inline NMCListElem * -nm_c_list_elem_new_stale (void *data) -{ - NMCListElem *elem; - - elem = g_slice_new (NMCListElem); - elem->data = data; - return elem; -} - -static inline void * -nm_c_list_elem_get (CList *lst) -{ - if (!lst) - return NULL; - return c_list_entry (lst, NMCListElem, lst)->data; -} - -static inline void -nm_c_list_elem_free (NMCListElem *elem) -{ - if (elem) { - c_list_unlink_stale (&elem->lst); - g_slice_free (NMCListElem, elem); - } -} - -static inline void -nm_c_list_elem_free_all (CList *head, GDestroyNotify free_fcn) -{ - NMCListElem *elem; - - while ((elem = c_list_first_entry (head, NMCListElem, lst))) { - if (free_fcn) - free_fcn (elem->data); - c_list_unlink_stale (&elem->lst); - g_slice_free (NMCListElem, elem); - } -} - -/*****************************************************************************/ - -static inline gboolean -nm_c_list_move_before (CList *lst, CList *elem) -{ - nm_assert (lst); - nm_assert (elem); - nm_assert (c_list_contains (lst, elem)); - - if ( lst != elem - && lst->prev != elem) { - c_list_unlink_stale (elem); - c_list_link_before (lst, elem); - return TRUE; - } - return FALSE; -} -#define nm_c_list_move_tail(lst, elem) nm_c_list_move_before (lst, elem) - -static inline gboolean -nm_c_list_move_after (CList *lst, CList *elem) -{ - nm_assert (lst); - nm_assert (elem); - nm_assert (c_list_contains (lst, elem)); - - if ( lst != elem - && lst->next != elem) { - c_list_unlink_stale (elem); - c_list_link_after (lst, elem); - return TRUE; - } - return FALSE; -} -#define nm_c_list_move_front(lst, elem) nm_c_list_move_after (lst, elem) - -#endif /* __NM_C_LIST_H__ */ diff --git a/shared/nm-utils/nm-dedup-multi.c b/shared/nm-utils/nm-dedup-multi.c deleted file mode 100644 index 5bdc3e3c..00000000 --- a/shared/nm-utils/nm-dedup-multi.c +++ /dev/null @@ -1,1092 +0,0 @@ -/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ -/* NetworkManager -- Network link manager - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the - * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, - * Boston, MA 02110-1301 USA. - * - * (C) Copyright 2017 Red Hat, Inc. - */ - -#include "nm-default.h" - -#include "nm-dedup-multi.h" - -#include "nm-hash-utils.h" -#include "nm-c-list.h" - -/*****************************************************************************/ - -typedef struct { - /* the stack-allocated lookup entry. It has a compatible - * memory layout with NMDedupMultiEntry and NMDedupMultiHeadEntry. - * - * It is recognizable by having lst_entries_sentinel.next set to NULL. - * Contrary to the other entries, which have lst_entries.next - * always non-NULL. - * */ - CList lst_entries_sentinel; - const NMDedupMultiObj *obj; - const NMDedupMultiIdxType *idx_type; - bool lookup_head; -} LookupEntry; - -struct _NMDedupMultiIndex { - int ref_count; - GHashTable *idx_entries; - GHashTable *idx_objs; -}; - -/*****************************************************************************/ - -static void -ASSERT_idx_type (const NMDedupMultiIdxType *idx_type) -{ - nm_assert (idx_type); -#if NM_MORE_ASSERTS > 10 - nm_assert (idx_type->klass); - nm_assert (idx_type->klass->idx_obj_id_hash_update); - nm_assert (idx_type->klass->idx_obj_id_equal); - nm_assert (!!idx_type->klass->idx_obj_partition_hash_update == !!idx_type->klass->idx_obj_partition_equal); - nm_assert (idx_type->lst_idx_head.next); -#endif -} - -void -nm_dedup_multi_idx_type_init (NMDedupMultiIdxType *idx_type, - const NMDedupMultiIdxTypeClass *klass) -{ - nm_assert (idx_type); - nm_assert (klass); - - memset (idx_type, 0, sizeof (*idx_type)); - idx_type->klass = klass; - c_list_init (&idx_type->lst_idx_head); - - ASSERT_idx_type (idx_type); -} - -/*****************************************************************************/ - -static NMDedupMultiEntry * -_entry_lookup_obj (const NMDedupMultiIndex *self, - const NMDedupMultiIdxType *idx_type, - const NMDedupMultiObj *obj) -{ - const LookupEntry stack_entry = { - .obj = obj, - .idx_type = idx_type, - .lookup_head = FALSE, - }; - - ASSERT_idx_type (idx_type); - return g_hash_table_lookup (self->idx_entries, &stack_entry); -} - -static NMDedupMultiHeadEntry * -_entry_lookup_head (const NMDedupMultiIndex *self, - const NMDedupMultiIdxType *idx_type, - const NMDedupMultiObj *obj) -{ - NMDedupMultiHeadEntry *head_entry; - const LookupEntry stack_entry = { - .obj = obj, - .idx_type = idx_type, - .lookup_head = TRUE, - }; - - ASSERT_idx_type (idx_type); - - if (!idx_type->klass->idx_obj_partition_equal) { - if (c_list_is_empty (&idx_type->lst_idx_head)) - head_entry = NULL; - else { - nm_assert (c_list_length (&idx_type->lst_idx_head) == 1); - head_entry = c_list_entry (idx_type->lst_idx_head.next, NMDedupMultiHeadEntry, lst_idx); - } - nm_assert (head_entry == g_hash_table_lookup (self->idx_entries, &stack_entry)); - return head_entry; - } - - return g_hash_table_lookup (self->idx_entries, &stack_entry); -} - -static void -_entry_unpack (const NMDedupMultiEntry *entry, - const NMDedupMultiIdxType **out_idx_type, - const NMDedupMultiObj **out_obj, - gboolean *out_lookup_head) -{ - const NMDedupMultiHeadEntry *head_entry; - const LookupEntry *lookup_entry; - - nm_assert (entry); - - G_STATIC_ASSERT_EXPR (G_STRUCT_OFFSET (LookupEntry, lst_entries_sentinel) == G_STRUCT_OFFSET (NMDedupMultiEntry, lst_entries)); - G_STATIC_ASSERT_EXPR (G_STRUCT_OFFSET (NMDedupMultiEntry, lst_entries) == G_STRUCT_OFFSET (NMDedupMultiHeadEntry, lst_entries_head)); - G_STATIC_ASSERT_EXPR (G_STRUCT_OFFSET (NMDedupMultiEntry, obj) == G_STRUCT_OFFSET (NMDedupMultiHeadEntry, idx_type)); - G_STATIC_ASSERT_EXPR (G_STRUCT_OFFSET (NMDedupMultiEntry, is_head) == G_STRUCT_OFFSET (NMDedupMultiHeadEntry, is_head)); - - if (!entry->lst_entries.next) { - /* the entry is stack-allocated by _entry_lookup(). */ - lookup_entry = (LookupEntry *) entry; - *out_obj = lookup_entry->obj; - *out_idx_type = lookup_entry->idx_type; - *out_lookup_head = lookup_entry->lookup_head; - } else if (entry->is_head) { - head_entry = (NMDedupMultiHeadEntry *) entry; - nm_assert (!c_list_is_empty (&head_entry->lst_entries_head)); - *out_obj = c_list_entry (head_entry->lst_entries_head.next, NMDedupMultiEntry, lst_entries)->obj; - *out_idx_type = head_entry->idx_type; - *out_lookup_head = TRUE; - } else { - *out_obj = entry->obj; - *out_idx_type = entry->head->idx_type; - *out_lookup_head = FALSE; - } - - nm_assert (NM_IN_SET (*out_lookup_head, FALSE, TRUE)); - ASSERT_idx_type (*out_idx_type); - - /* for lookup of the head, we allow to omit object, but only - * if the idx_type does not partition the objects. Otherwise, we - * require a obj to compare. */ - nm_assert ( !*out_lookup_head - || ( *out_obj - || !(*out_idx_type)->klass->idx_obj_partition_equal)); - - /* lookup of the object requires always an object. */ - nm_assert ( *out_lookup_head - || *out_obj); -} - -static guint -_dict_idx_entries_hash (const NMDedupMultiEntry *entry) -{ - const NMDedupMultiIdxType *idx_type; - const NMDedupMultiObj *obj; - gboolean lookup_head; - NMHashState h; - - _entry_unpack (entry, &idx_type, &obj, &lookup_head); - - nm_hash_init (&h, 1914869417u); - if (idx_type->klass->idx_obj_partition_hash_update) { - nm_assert (obj); - idx_type->klass->idx_obj_partition_hash_update (idx_type, obj, &h); - } - - if (!lookup_head) - idx_type->klass->idx_obj_id_hash_update (idx_type, obj, &h); - - nm_hash_update_val (&h, idx_type); - return nm_hash_complete (&h); -} - -static gboolean -_dict_idx_entries_equal (const NMDedupMultiEntry *entry_a, - const NMDedupMultiEntry *entry_b) -{ - const NMDedupMultiIdxType *idx_type_a, *idx_type_b; - const NMDedupMultiObj *obj_a, *obj_b; - gboolean lookup_head_a, lookup_head_b; - - _entry_unpack (entry_a, &idx_type_a, &obj_a, &lookup_head_a); - _entry_unpack (entry_b, &idx_type_b, &obj_b, &lookup_head_b); - - if ( idx_type_a != idx_type_b - || lookup_head_a != lookup_head_b) - return FALSE; - if (!nm_dedup_multi_idx_type_partition_equal (idx_type_a, obj_a, obj_b)) - return FALSE; - if ( !lookup_head_a - && !nm_dedup_multi_idx_type_id_equal (idx_type_a, obj_a, obj_b)) - return FALSE; - return TRUE; -} - -/*****************************************************************************/ - -static gboolean -_add (NMDedupMultiIndex *self, - NMDedupMultiIdxType *idx_type, - const NMDedupMultiObj *obj, - NMDedupMultiEntry *entry, - NMDedupMultiIdxMode mode, - const NMDedupMultiEntry *entry_order, - NMDedupMultiHeadEntry *head_existing, - const NMDedupMultiEntry **out_entry, - const NMDedupMultiObj **out_obj_old) -{ - NMDedupMultiHeadEntry *head_entry; - const NMDedupMultiObj *obj_new, *obj_old; - gboolean add_head_entry = FALSE; - - nm_assert (self); - ASSERT_idx_type (idx_type); - nm_assert (obj); - nm_assert (NM_IN_SET (mode, - NM_DEDUP_MULTI_IDX_MODE_PREPEND, - NM_DEDUP_MULTI_IDX_MODE_PREPEND_FORCE, - NM_DEDUP_MULTI_IDX_MODE_APPEND, - NM_DEDUP_MULTI_IDX_MODE_APPEND_FORCE)); - nm_assert (!head_existing || head_existing->idx_type == idx_type); - nm_assert (({ - const NMDedupMultiHeadEntry *_h; - gboolean _ok = TRUE; - if (head_existing) { - _h = nm_dedup_multi_index_lookup_head (self, idx_type, obj); - if (head_existing == NM_DEDUP_MULTI_HEAD_ENTRY_MISSING) - _ok = (_h == NULL); - else - _ok = (_h == head_existing); - } - _ok; - })); - - if (entry) { - gboolean changed = FALSE; - - nm_dedup_multi_entry_set_dirty (entry, FALSE); - - nm_assert (!head_existing || entry->head == head_existing); - nm_assert (!entry_order || entry_order->head == entry->head); - nm_assert (!entry_order || c_list_contains (&entry->lst_entries, &entry_order->lst_entries)); - nm_assert (!entry_order || c_list_contains (&entry_order->lst_entries, &entry->lst_entries)); - - switch (mode) { - case NM_DEDUP_MULTI_IDX_MODE_PREPEND_FORCE: - if (entry_order) { - if (nm_c_list_move_before ((CList *) &entry_order->lst_entries, &entry->lst_entries)) - changed = TRUE; - } else { - if (nm_c_list_move_front ((CList *) &entry->head->lst_entries_head, &entry->lst_entries)) - changed = TRUE; - } - break; - case NM_DEDUP_MULTI_IDX_MODE_APPEND_FORCE: - if (entry_order) { - if (nm_c_list_move_after ((CList *) &entry_order->lst_entries, &entry->lst_entries)) - changed = TRUE; - } else { - if (nm_c_list_move_tail ((CList *) &entry->head->lst_entries_head, &entry->lst_entries)) - changed = TRUE; - } - break; - case NM_DEDUP_MULTI_IDX_MODE_PREPEND: - case NM_DEDUP_MULTI_IDX_MODE_APPEND: - break; - }; - - nm_assert (obj->klass == ((const NMDedupMultiObj *) entry->obj)->klass); - if ( obj == entry->obj - || obj->klass->obj_full_equal (obj, - entry->obj)) { - NM_SET_OUT (out_entry, entry); - NM_SET_OUT (out_obj_old, nm_dedup_multi_obj_ref (entry->obj)); - return changed; - } - - obj_new = nm_dedup_multi_index_obj_intern (self, obj); - - obj_old = entry->obj; - entry->obj = obj_new; - - NM_SET_OUT (out_entry, entry); - if (out_obj_old) - *out_obj_old = obj_old; - else - nm_dedup_multi_obj_unref (obj_old); - return TRUE; - } - - if ( idx_type->klass->idx_obj_partitionable - && !idx_type->klass->idx_obj_partitionable (idx_type, obj)) { - /* this object cannot be partitioned by this idx_type. */ - nm_assert (!head_existing || head_existing == NM_DEDUP_MULTI_HEAD_ENTRY_MISSING); - NM_SET_OUT (out_entry, NULL); - NM_SET_OUT (out_obj_old, NULL); - return FALSE; - } - - obj_new = nm_dedup_multi_index_obj_intern (self, obj); - - if (!head_existing) - head_entry = _entry_lookup_head (self, idx_type, obj_new); - else if (head_existing == NM_DEDUP_MULTI_HEAD_ENTRY_MISSING) - head_entry = NULL; - else - head_entry = head_existing; - - if (!head_entry) { - head_entry = g_slice_new0 (NMDedupMultiHeadEntry); - head_entry->is_head = TRUE; - head_entry->idx_type = idx_type; - c_list_init (&head_entry->lst_entries_head); - c_list_link_tail (&idx_type->lst_idx_head, &head_entry->lst_idx); - add_head_entry = TRUE; - } else - nm_assert (c_list_contains (&idx_type->lst_idx_head, &head_entry->lst_idx)); - - if (entry_order) { - nm_assert (!add_head_entry); - nm_assert (entry_order->head == head_entry); - nm_assert (c_list_contains (&head_entry->lst_entries_head, &entry_order->lst_entries)); - nm_assert (c_list_contains (&entry_order->lst_entries, &head_entry->lst_entries_head)); - } - - entry = g_slice_new0 (NMDedupMultiEntry); - entry->obj = obj_new; - entry->head = head_entry; - - switch (mode) { - case NM_DEDUP_MULTI_IDX_MODE_PREPEND: - case NM_DEDUP_MULTI_IDX_MODE_PREPEND_FORCE: - if (entry_order) - c_list_link_before ((CList *) &entry_order->lst_entries, &entry->lst_entries); - else - c_list_link_front (&head_entry->lst_entries_head, &entry->lst_entries); - break; - default: - if (entry_order) - c_list_link_after ((CList *) &entry_order->lst_entries, &entry->lst_entries); - else - c_list_link_tail (&head_entry->lst_entries_head, &entry->lst_entries); - break; - }; - - idx_type->len++; - head_entry->len++; - - if ( add_head_entry - && !g_hash_table_add (self->idx_entries, head_entry)) - nm_assert_not_reached (); - - if (!g_hash_table_add (self->idx_entries, entry)) - nm_assert_not_reached (); - - NM_SET_OUT (out_entry, entry); - NM_SET_OUT (out_obj_old, NULL); - return TRUE; -} - -gboolean -nm_dedup_multi_index_add (NMDedupMultiIndex *self, - NMDedupMultiIdxType *idx_type, - /*const NMDedupMultiObj * */ gconstpointer obj, - NMDedupMultiIdxMode mode, - const NMDedupMultiEntry **out_entry, - /* const NMDedupMultiObj ** */ gpointer out_obj_old) -{ - NMDedupMultiEntry *entry; - - g_return_val_if_fail (self, FALSE); - g_return_val_if_fail (idx_type, FALSE); - g_return_val_if_fail (obj, FALSE); - g_return_val_if_fail (NM_IN_SET (mode, - NM_DEDUP_MULTI_IDX_MODE_PREPEND, - NM_DEDUP_MULTI_IDX_MODE_PREPEND_FORCE, - NM_DEDUP_MULTI_IDX_MODE_APPEND, - NM_DEDUP_MULTI_IDX_MODE_APPEND_FORCE), - FALSE); - - entry = _entry_lookup_obj (self, idx_type, obj); - return _add (self, idx_type, obj, - entry, mode, - NULL, NULL, - out_entry, out_obj_old); -} - -/* nm_dedup_multi_index_add_full: - * @self: the index instance. - * @idx_type: the index handle for storing @obj. - * @obj: the NMDedupMultiObj instance to add. - * @mode: whether to append or prepend the new item. If @entry_order is given, - * the entry will be sorted after/before, instead of appending/prepending to - * the entire list. If a comparable object is already tracked, then it may - * still be resorted by specifying one of the "FORCE" modes. - * @entry_order: if not NULL, the new entry will be sorted before or after @entry_order. - * If given, @entry_order MUST be tracked by @self, and the object it points to MUST - * be in the same partition tracked by @idx_type. That is, they must have the same - * head_entry and it means, you must ensure that @entry_order and the created/modified - * entry will share the same head. - * @entry_existing: if not NULL, it safes a hash lookup of the entry where the - * object will be placed in. You can omit this, and it will be automatically - * detected (at the expense of an additional hash lookup). - * Basically, this is the result of nm_dedup_multi_index_lookup_obj(), - * with the peculiarity that if you know that @obj is not yet tracked, - * you may specify %NM_DEDUP_MULTI_ENTRY_MISSING. - * @head_existing: an optional argument to safe a lookup for the head. If specified, - * it must be identical to nm_dedup_multi_index_lookup_head(), with the peculiarity - * that if the head is not yet tracked, you may specify %NM_DEDUP_MULTI_HEAD_ENTRY_MISSING - * @out_entry: if give, return the added entry. This entry may have already exists (update) - * or be newly created. If @obj is not partitionable according to @idx_type, @obj - * is not to be added and it returns %NULL. - * @out_obj_old: if given, return the previously contained object. It only - * returns a object, if a matching entry was tracked previously, not if a - * new entry was created. Note that when passing @out_obj_old you obtain a reference - * to the boxed object and MUST return it with nm_dedup_multi_obj_unref(). - * - * Adds and object to the index. - * - * Return: %TRUE if anything changed, %FALSE if nothing changed. - */ -gboolean -nm_dedup_multi_index_add_full (NMDedupMultiIndex *self, - NMDedupMultiIdxType *idx_type, - /*const NMDedupMultiObj * */ gconstpointer obj, - NMDedupMultiIdxMode mode, - const NMDedupMultiEntry *entry_order, - const NMDedupMultiEntry *entry_existing, - const NMDedupMultiHeadEntry *head_existing, - const NMDedupMultiEntry **out_entry, - /* const NMDedupMultiObj ** */ gpointer out_obj_old) -{ - NMDedupMultiEntry *entry; - - g_return_val_if_fail (self, FALSE); - g_return_val_if_fail (idx_type, FALSE); - g_return_val_if_fail (obj, FALSE); - g_return_val_if_fail (NM_IN_SET (mode, - NM_DEDUP_MULTI_IDX_MODE_PREPEND, - NM_DEDUP_MULTI_IDX_MODE_PREPEND_FORCE, - NM_DEDUP_MULTI_IDX_MODE_APPEND, - NM_DEDUP_MULTI_IDX_MODE_APPEND_FORCE), - FALSE); - - if (entry_existing == NULL) - entry = _entry_lookup_obj (self, idx_type, obj); - else if (entry_existing == NM_DEDUP_MULTI_ENTRY_MISSING) { - nm_assert (!_entry_lookup_obj (self, idx_type, obj)); - entry = NULL; - } else { - nm_assert (entry_existing == _entry_lookup_obj (self, idx_type, obj)); - entry = (NMDedupMultiEntry *) entry_existing; - } - return _add (self, idx_type, obj, - entry, - mode, entry_order, - (NMDedupMultiHeadEntry *) head_existing, - out_entry, out_obj_old); -} - -/*****************************************************************************/ - -static void -_remove_entry (NMDedupMultiIndex *self, - NMDedupMultiEntry *entry, - gboolean *out_head_entry_removed) -{ - const NMDedupMultiObj *obj; - NMDedupMultiHeadEntry *head_entry; - NMDedupMultiIdxType *idx_type; - - nm_assert (self); - nm_assert (entry); - nm_assert (entry->obj); - nm_assert (entry->head); - nm_assert (!c_list_is_empty (&entry->lst_entries)); - nm_assert (g_hash_table_lookup (self->idx_entries, entry) == entry); - - head_entry = (NMDedupMultiHeadEntry *) entry->head; - obj = entry->obj; - - nm_assert (head_entry); - nm_assert (head_entry->len > 0); - nm_assert (g_hash_table_lookup (self->idx_entries, head_entry) == head_entry); - - idx_type = (NMDedupMultiIdxType *) head_entry->idx_type; - ASSERT_idx_type (idx_type); - - nm_assert (idx_type->len >= head_entry->len); - if (--head_entry->len > 0) { - nm_assert (idx_type->len > 1); - idx_type->len--; - head_entry = NULL; - } - - NM_SET_OUT (out_head_entry_removed, head_entry != NULL); - - if (!g_hash_table_remove (self->idx_entries, entry)) - nm_assert_not_reached (); - - if ( head_entry - && !g_hash_table_remove (self->idx_entries, head_entry)) - nm_assert_not_reached (); - - c_list_unlink_stale (&entry->lst_entries); - g_slice_free (NMDedupMultiEntry, entry); - - if (head_entry) { - nm_assert (c_list_is_empty (&head_entry->lst_entries_head)); - c_list_unlink_stale (&head_entry->lst_idx); - g_slice_free (NMDedupMultiHeadEntry, head_entry); - } - - nm_dedup_multi_obj_unref (obj); -} - -static guint -_remove_head (NMDedupMultiIndex *self, - NMDedupMultiHeadEntry *head_entry, - gboolean remove_all /* otherwise just dirty ones */, - gboolean mark_survivors_dirty) -{ - guint n; - gboolean head_entry_removed; - CList *iter_entry, *iter_entry_safe; - - nm_assert (self); - nm_assert (head_entry); - nm_assert (head_entry->len > 0); - nm_assert (head_entry->len == c_list_length (&head_entry->lst_entries_head)); - nm_assert (g_hash_table_lookup (self->idx_entries, head_entry) == head_entry); - - n = 0; - c_list_for_each_safe (iter_entry, iter_entry_safe, &head_entry->lst_entries_head) { - NMDedupMultiEntry *entry; - - entry = c_list_entry (iter_entry, NMDedupMultiEntry, lst_entries); - if ( remove_all - || entry->dirty) { - _remove_entry (self, - entry, - &head_entry_removed); - n++; - if (head_entry_removed) - break; - } else if (mark_survivors_dirty) - nm_dedup_multi_entry_set_dirty (entry, TRUE); - } - - return n; -} - -static guint -_remove_idx_entry (NMDedupMultiIndex *self, - NMDedupMultiIdxType *idx_type, - gboolean remove_all /* otherwise just dirty ones */, - gboolean mark_survivors_dirty) -{ - guint n; - CList *iter_idx, *iter_idx_safe; - - nm_assert (self); - ASSERT_idx_type (idx_type); - - n = 0; - c_list_for_each_safe (iter_idx, iter_idx_safe, &idx_type->lst_idx_head) { - n += _remove_head (self, - c_list_entry (iter_idx, NMDedupMultiHeadEntry, lst_idx), - remove_all, mark_survivors_dirty); - } - return n; -} - -guint -nm_dedup_multi_index_remove_entry (NMDedupMultiIndex *self, - gconstpointer entry) -{ - g_return_val_if_fail (self, 0); - - nm_assert (entry); - - if (!((NMDedupMultiEntry *) entry)->is_head) { - _remove_entry (self, (NMDedupMultiEntry *) entry, NULL); - return 1; - } - return _remove_head (self, (NMDedupMultiHeadEntry *) entry, TRUE, FALSE); -} - -guint -nm_dedup_multi_index_remove_obj (NMDedupMultiIndex *self, - NMDedupMultiIdxType *idx_type, - /*const NMDedupMultiObj * */ gconstpointer obj, - /*const NMDedupMultiObj ** */ gconstpointer *out_obj) -{ - const NMDedupMultiEntry *entry; - - entry = nm_dedup_multi_index_lookup_obj (self, idx_type, obj); - if (!entry) { - NM_SET_OUT (out_obj, NULL); - return 0; - } - - /* since we are about to remove the object, we obviously pass - * a reference to @out_obj, the caller MUST unref the object, - * if he chooses to provide @out_obj. */ - NM_SET_OUT (out_obj, nm_dedup_multi_obj_ref (entry->obj)); - - _remove_entry (self, (NMDedupMultiEntry *) entry, NULL); - return 1; -} - -guint -nm_dedup_multi_index_remove_head (NMDedupMultiIndex *self, - NMDedupMultiIdxType *idx_type, - /*const NMDedupMultiObj * */ gconstpointer obj) -{ - const NMDedupMultiHeadEntry *entry; - - entry = nm_dedup_multi_index_lookup_head (self, idx_type, obj); - return entry - ? _remove_head (self, (NMDedupMultiHeadEntry *) entry, TRUE, FALSE) - : 0; -} - -guint -nm_dedup_multi_index_remove_idx (NMDedupMultiIndex *self, - NMDedupMultiIdxType *idx_type) -{ - g_return_val_if_fail (self, 0); - g_return_val_if_fail (idx_type, 0); - - return _remove_idx_entry (self, idx_type, TRUE, FALSE); -} - -/*****************************************************************************/ - -/** - * nm_dedup_multi_index_lookup_obj: - * @self: the index cache - * @idx_type: the lookup index type - * @obj: the object to lookup. This means the match is performed - * according to NMDedupMultiIdxTypeClass's idx_obj_id_equal() - * of @idx_type. - * - * Returns: the cache entry or %NULL if the entry wasn't found. - */ -const NMDedupMultiEntry * -nm_dedup_multi_index_lookup_obj (const NMDedupMultiIndex *self, - const NMDedupMultiIdxType *idx_type, - /*const NMDedupMultiObj * */ gconstpointer obj) -{ - g_return_val_if_fail (self, FALSE); - g_return_val_if_fail (idx_type, FALSE); - g_return_val_if_fail (obj, FALSE); - - nm_assert (idx_type && idx_type->klass); - return _entry_lookup_obj (self, idx_type, obj); -} - -/** - * nm_dedup_multi_index_lookup_head: - * @self: the index cache - * @idx_type: the lookup index type - * @obj: the object to lookup, of type "const NMDedupMultiObj *". - * Depending on the idx_type, you *must* also provide a selector - * object, even when looking up the list head. That is, because - * the idx_type implementation may choose to partition the objects - * in distinct list, so you need a selector object to know which - * list head to lookup. - * - * Returns: the cache entry or %NULL if the entry wasn't found. - */ -const NMDedupMultiHeadEntry * -nm_dedup_multi_index_lookup_head (const NMDedupMultiIndex *self, - const NMDedupMultiIdxType *idx_type, - /*const NMDedupMultiObj * */ gconstpointer obj) -{ - g_return_val_if_fail (self, FALSE); - g_return_val_if_fail (idx_type, FALSE); - - return _entry_lookup_head (self, idx_type, obj); -} - -/*****************************************************************************/ - -void -nm_dedup_multi_index_dirty_set_head (NMDedupMultiIndex *self, - const NMDedupMultiIdxType *idx_type, - /*const NMDedupMultiObj * */ gconstpointer obj) -{ - NMDedupMultiHeadEntry *head_entry; - CList *iter_entry; - - g_return_if_fail (self); - g_return_if_fail (idx_type); - - head_entry = _entry_lookup_head (self, idx_type, obj); - if (!head_entry) - return; - - c_list_for_each (iter_entry, &head_entry->lst_entries_head) { - NMDedupMultiEntry *entry; - - entry = c_list_entry (iter_entry, NMDedupMultiEntry, lst_entries); - nm_dedup_multi_entry_set_dirty (entry, TRUE); - } -} - -void -nm_dedup_multi_index_dirty_set_idx (NMDedupMultiIndex *self, - const NMDedupMultiIdxType *idx_type) -{ - CList *iter_idx, *iter_entry; - - g_return_if_fail (self); - g_return_if_fail (idx_type); - - c_list_for_each (iter_idx, &idx_type->lst_idx_head) { - NMDedupMultiHeadEntry *head_entry; - - head_entry = c_list_entry (iter_idx, NMDedupMultiHeadEntry, lst_idx); - c_list_for_each (iter_entry, &head_entry->lst_entries_head) { - NMDedupMultiEntry *entry; - - entry = c_list_entry (iter_entry, NMDedupMultiEntry, lst_entries); - nm_dedup_multi_entry_set_dirty (entry, TRUE); - } - } -} - -/** - * nm_dedup_multi_index_dirty_remove_idx: - * @self: the index instance - * @idx_type: the index-type to select the objects. - * @mark_survivors_dirty: while the function removes all entries that are - * marked as dirty, if @set_dirty is true, the surviving objects - * will be marked dirty right away. - * - * Deletes all entries for @idx_type that are marked dirty. Only - * non-dirty objects survive. If @mark_survivors_dirty is set to TRUE, the survivors - * are marked as dirty right away. - * - * Returns: number of deleted entries. - */ -guint -nm_dedup_multi_index_dirty_remove_idx (NMDedupMultiIndex *self, - NMDedupMultiIdxType *idx_type, - gboolean mark_survivors_dirty) -{ - g_return_val_if_fail (self, 0); - g_return_val_if_fail (idx_type, 0); - - return _remove_idx_entry (self, idx_type, FALSE, mark_survivors_dirty); -} - -/*****************************************************************************/ - -static guint -_dict_idx_objs_hash (const NMDedupMultiObj *obj) -{ - NMHashState h; - - nm_hash_init (&h, 1748638583u); - obj->klass->obj_full_hash_update (obj, &h); - return nm_hash_complete (&h); -} - -static gboolean -_dict_idx_objs_equal (const NMDedupMultiObj *obj_a, - const NMDedupMultiObj *obj_b) -{ - return obj_a == obj_b - || ( obj_a->klass == obj_b->klass - && obj_a->klass->obj_full_equal (obj_a, obj_b)); -} - -void -nm_dedup_multi_index_obj_release (NMDedupMultiIndex *self, - /* const NMDedupMultiObj * */ gconstpointer obj) -{ - nm_assert (self); - nm_assert (obj); - nm_assert (g_hash_table_lookup (self->idx_objs, obj) == obj); - nm_assert (((const NMDedupMultiObj *) obj)->_multi_idx == self); - - ((NMDedupMultiObj *) obj)->_multi_idx = NULL; - if (!g_hash_table_remove (self->idx_objs, obj)) - nm_assert_not_reached (); -} - -gconstpointer -nm_dedup_multi_index_obj_find (NMDedupMultiIndex *self, - /* const NMDedupMultiObj * */ gconstpointer obj) -{ - g_return_val_if_fail (self, NULL); - g_return_val_if_fail (obj, NULL); - - return g_hash_table_lookup (self->idx_objs, obj); -} - -gconstpointer -nm_dedup_multi_index_obj_intern (NMDedupMultiIndex *self, - /* const NMDedupMultiObj * */ gconstpointer obj) -{ - const NMDedupMultiObj *obj_new = obj; - const NMDedupMultiObj *obj_old; - - nm_assert (self); - nm_assert (obj_new); - - if (obj_new->_multi_idx == self) { - nm_assert (g_hash_table_lookup (self->idx_objs, obj_new) == obj_new); - nm_dedup_multi_obj_ref (obj_new); - return obj_new; - } - - obj_old = g_hash_table_lookup (self->idx_objs, obj_new); - nm_assert (obj_old != obj_new); - - if (obj_old) { - nm_assert (obj_old->_multi_idx == self); - nm_dedup_multi_obj_ref (obj_old); - return obj_old; - } - - if (nm_dedup_multi_obj_needs_clone (obj_new)) - obj_new = nm_dedup_multi_obj_clone (obj_new); - else - obj_new = nm_dedup_multi_obj_ref (obj_new); - - nm_assert (obj_new); - nm_assert (!obj_new->_multi_idx); - - if (!g_hash_table_add (self->idx_objs, (gpointer) obj_new)) - nm_assert_not_reached (); - - ((NMDedupMultiObj *) obj_new)->_multi_idx = self; - return obj_new; -} - -void -nm_dedup_multi_obj_unref (const NMDedupMultiObj *obj) -{ - if (obj) { - nm_assert (obj->_ref_count > 0); - nm_assert (obj->_ref_count != NM_OBJ_REF_COUNT_STACKINIT); - -again: - if (--(((NMDedupMultiObj *) obj)->_ref_count) <= 0) { - if (obj->_multi_idx) { - /* restore the ref-count to 1 and release the object first - * from the index. Then, retry again to unref. */ - ((NMDedupMultiObj *) obj)->_ref_count++; - nm_dedup_multi_index_obj_release (obj->_multi_idx, obj); - nm_assert (obj->_ref_count == 1); - nm_assert (!obj->_multi_idx); - goto again; - } - - obj->klass->obj_destroy ((NMDedupMultiObj *) obj); - } - } -} - -gboolean -nm_dedup_multi_obj_needs_clone (const NMDedupMultiObj *obj) -{ - nm_assert (obj); - - if ( obj->_multi_idx - || obj->_ref_count == NM_OBJ_REF_COUNT_STACKINIT) - return TRUE; - - if ( obj->klass->obj_needs_clone - && obj->klass->obj_needs_clone (obj)) - return TRUE; - - return FALSE; -} - -const NMDedupMultiObj * -nm_dedup_multi_obj_clone (const NMDedupMultiObj *obj) -{ - const NMDedupMultiObj *o; - - nm_assert (obj); - - o = obj->klass->obj_clone (obj); - nm_assert (o); - nm_assert (o->_ref_count == 1); - return o; -} - -gconstpointer * -nm_dedup_multi_objs_to_array_head (const NMDedupMultiHeadEntry *head_entry, - NMDedupMultiFcnSelectPredicate predicate, - gpointer user_data, - guint *out_len) -{ - gconstpointer *result; - CList *iter; - guint i; - - if (!head_entry) { - NM_SET_OUT (out_len, 0); - return NULL; - } - - result = g_new (gconstpointer, head_entry->len + 1); - i = 0; - c_list_for_each (iter, &head_entry->lst_entries_head) { - const NMDedupMultiObj *obj = c_list_entry (iter, NMDedupMultiEntry, lst_entries)->obj; - - if ( !predicate - || predicate (obj, user_data)) { - nm_assert (i < head_entry->len); - result[i++] = obj; - } - } - - if (i == 0) { - g_free (result); - NM_SET_OUT (out_len, 0); - return NULL; - } - - nm_assert (i <= head_entry->len); - NM_SET_OUT (out_len, i); - result[i++] = NULL; - return result; -} - -GPtrArray * -nm_dedup_multi_objs_to_ptr_array_head (const NMDedupMultiHeadEntry *head_entry, - NMDedupMultiFcnSelectPredicate predicate, - gpointer user_data) -{ - GPtrArray *result; - CList *iter; - - if (!head_entry) - return NULL; - - result = g_ptr_array_new_full (head_entry->len, - (GDestroyNotify) nm_dedup_multi_obj_unref); - c_list_for_each (iter, &head_entry->lst_entries_head) { - const NMDedupMultiObj *obj = c_list_entry (iter, NMDedupMultiEntry, lst_entries)->obj; - - if ( !predicate - || predicate (obj, user_data)) - g_ptr_array_add (result, (gpointer) nm_dedup_multi_obj_ref (obj)); - } - - if (result->len == 0) { - g_ptr_array_unref (result); - return NULL; - } - return result; -} - -/** - * nm_dedup_multi_entry_reorder: - * @entry: the entry to reorder. It must not be NULL (and tracked in an index). - * @entry_order: (allow-none): an optional other entry. It MUST be in the same - * list as entry. If given, @entry will be ordered after/before @entry_order. - * If left at %NULL, @entry will be moved to the front/end of the list. - * @order_after: if @entry_order is given, %TRUE means to move @entry after - * @entry_order (otherwise before). - * If @entry_order is %NULL, %TRUE means to move @entry to the tail of the list - * (otherwise the beginning). Note that "tail of the list" here means that @entry - * will be linked before the head of the circular list. - * - * Returns: %TRUE, if anything was changed. Otherwise, @entry was already at the - * right place and nothing was done. - */ -gboolean -nm_dedup_multi_entry_reorder (const NMDedupMultiEntry *entry, - const NMDedupMultiEntry *entry_order, - gboolean order_after) -{ - nm_assert (entry); - - if (!entry_order) { - const NMDedupMultiHeadEntry *head_entry = entry->head; - - if (order_after) { - if (nm_c_list_move_tail ((CList *) &head_entry->lst_entries_head, (CList *) &entry->lst_entries)) - return TRUE; - } else { - if (nm_c_list_move_front ((CList *) &head_entry->lst_entries_head, (CList *) &entry->lst_entries)) - return TRUE; - } - } else { - if (order_after) { - if (nm_c_list_move_after ((CList *) &entry_order->lst_entries, (CList *) &entry->lst_entries)) - return TRUE; - } else { - if (nm_c_list_move_before ((CList *) &entry_order->lst_entries, (CList *) &entry->lst_entries)) - return TRUE; - } - } - - return FALSE; -} - -/*****************************************************************************/ - -NMDedupMultiIndex * -nm_dedup_multi_index_new (void) -{ - NMDedupMultiIndex *self; - - self = g_slice_new0 (NMDedupMultiIndex); - self->ref_count = 1; - self->idx_entries = g_hash_table_new ((GHashFunc) _dict_idx_entries_hash, (GEqualFunc) _dict_idx_entries_equal); - self->idx_objs = g_hash_table_new ((GHashFunc) _dict_idx_objs_hash, (GEqualFunc) _dict_idx_objs_equal); - return self; -} - -NMDedupMultiIndex * -nm_dedup_multi_index_ref (NMDedupMultiIndex *self) -{ - g_return_val_if_fail (self, NULL); - g_return_val_if_fail (self->ref_count > 0, NULL); - - self->ref_count++; - return self; -} - -NMDedupMultiIndex * -nm_dedup_multi_index_unref (NMDedupMultiIndex *self) -{ - GHashTableIter iter; - const NMDedupMultiIdxType *idx_type; - NMDedupMultiEntry *entry; - const NMDedupMultiObj *obj; - - g_return_val_if_fail (self, NULL); - g_return_val_if_fail (self->ref_count > 0, NULL); - - if (--self->ref_count > 0) - return NULL; - -more: - g_hash_table_iter_init (&iter, self->idx_entries); - while (g_hash_table_iter_next (&iter, (gpointer *) &entry, NULL)) { - if (entry->is_head) - idx_type = ((NMDedupMultiHeadEntry *) entry)->idx_type; - else - idx_type = entry->head->idx_type; - _remove_idx_entry (self, (NMDedupMultiIdxType *) idx_type, TRUE, FALSE); - goto more; - } - - nm_assert (g_hash_table_size (self->idx_entries) == 0); - - g_hash_table_iter_init (&iter, self->idx_objs); - while (g_hash_table_iter_next (&iter, (gpointer *) &obj, NULL)) { - nm_assert (obj->_multi_idx == self); - ((NMDedupMultiObj * )obj)->_multi_idx = NULL; - } - g_hash_table_remove_all (self->idx_objs); - - g_hash_table_unref (self->idx_entries); - g_hash_table_unref (self->idx_objs); - - g_slice_free (NMDedupMultiIndex, self); - return NULL; -} diff --git a/shared/nm-utils/nm-dedup-multi.h b/shared/nm-utils/nm-dedup-multi.h deleted file mode 100644 index 845b4c3e..00000000 --- a/shared/nm-utils/nm-dedup-multi.h +++ /dev/null @@ -1,437 +0,0 @@ -/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ -/* NetworkManager -- Network link manager - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the - * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, - * Boston, MA 02110-1301 USA. - * - * (C) Copyright 2017 Red Hat, Inc. - */ - -#ifndef __NM_DEDUP_MULTI_H__ -#define __NM_DEDUP_MULTI_H__ - -#include "nm-obj.h" -#include "c-list-util.h" - -/*****************************************************************************/ - -struct _NMHashState; - -typedef struct _NMDedupMultiObj NMDedupMultiObj; -typedef struct _NMDedupMultiObjClass NMDedupMultiObjClass; -typedef struct _NMDedupMultiIdxType NMDedupMultiIdxType; -typedef struct _NMDedupMultiIdxTypeClass NMDedupMultiIdxTypeClass; -typedef struct _NMDedupMultiEntry NMDedupMultiEntry; -typedef struct _NMDedupMultiHeadEntry NMDedupMultiHeadEntry; -typedef struct _NMDedupMultiIndex NMDedupMultiIndex; - -typedef enum _NMDedupMultiIdxMode { - NM_DEDUP_MULTI_IDX_MODE_PREPEND, - - NM_DEDUP_MULTI_IDX_MODE_PREPEND_FORCE, - - /* append new objects to the end of the list. - * If the object is already in the cache, don't move it. */ - NM_DEDUP_MULTI_IDX_MODE_APPEND, - - /* like NM_DEDUP_MULTI_IDX_MODE_APPEND, but if the object - * is already in the cache, move it to the end. */ - NM_DEDUP_MULTI_IDX_MODE_APPEND_FORCE, -} NMDedupMultiIdxMode; - -/*****************************************************************************/ - -struct _NMDedupMultiObj { - union { - NMObjBaseInst parent; - const NMDedupMultiObjClass *klass; - }; - NMDedupMultiIndex *_multi_idx; - guint _ref_count; -}; - -struct _NMDedupMultiObjClass { - NMObjBaseClass parent; - - const NMDedupMultiObj *(*obj_clone) (const NMDedupMultiObj *obj); - - gboolean (*obj_needs_clone) (const NMDedupMultiObj *obj); - - void (*obj_destroy) (NMDedupMultiObj *obj); - - /* the NMDedupMultiObj can be deduplicated. For that the obj_full_hash_update() - * and obj_full_equal() compare *all* fields of the object, even minor ones. */ - void (*obj_full_hash_update) (const NMDedupMultiObj *obj, - struct _NMHashState *h); - gboolean (*obj_full_equal) (const NMDedupMultiObj *obj_a, - const NMDedupMultiObj *obj_b); -}; - -/*****************************************************************************/ - -static inline const NMDedupMultiObj * -nm_dedup_multi_obj_ref (const NMDedupMultiObj *obj) -{ - /* ref and unref accept const pointers. Objects is supposed to be shared - * and kept immutable. Disallowing to take/return a reference to a const - * NMPObject is cumbersome, because callers are precisely expected to - * keep a ref on the otherwise immutable object. */ - - nm_assert (obj); - nm_assert (obj->_ref_count != NM_OBJ_REF_COUNT_STACKINIT); - nm_assert (obj->_ref_count > 0); - - ((NMDedupMultiObj *) obj)->_ref_count++; - return obj; -} - -void nm_dedup_multi_obj_unref (const NMDedupMultiObj *obj); -const NMDedupMultiObj *nm_dedup_multi_obj_clone (const NMDedupMultiObj *obj); -gboolean nm_dedup_multi_obj_needs_clone (const NMDedupMultiObj *obj); - -gconstpointer nm_dedup_multi_index_obj_intern (NMDedupMultiIndex *self, - /* const NMDedupMultiObj * */ gconstpointer obj); - -void nm_dedup_multi_index_obj_release (NMDedupMultiIndex *self, - /* const NMDedupMultiObj * */ gconstpointer obj); - -/* const NMDedupMultiObj * */ gconstpointer nm_dedup_multi_index_obj_find (NMDedupMultiIndex *self, - /* const NMDedupMultiObj * */ gconstpointer obj); - -/*****************************************************************************/ - -/* the NMDedupMultiIdxType is an access handle under which you can store and - * retrieve NMDedupMultiObj instances in NMDedupMultiIndex. - * - * The NMDedupMultiIdxTypeClass determines its behavior, but you can have - * multiple instances (of the same class). - * - * For example, NMIP4Config can have idx-type to put there all IPv4 Routes. - * This idx-type instance is private to the NMIP4Config instance. Basically, - * the NMIP4Config instance uses the idx-type to maintain an ordered list - * of routes in NMDedupMultiIndex. - * - * However, a NMDedupMultiIdxType may also partition the set of objects - * in multiple distinct lists. NMIP4Config doesn't do that (because instead - * of creating one idx-type for IPv4 and IPv6 routes, it just cretaes - * to distinct idx-types, one for each address family. - * This partitioning is used by NMPlatform to maintain a lookup index for - * routes by ifindex. As the ifindex is dynamic, it does not create an - * idx-type instance for each ifindex. Instead, it has one idx-type for - * all routes. But whenever accessing NMDedupMultiIndex with an NMDedupMultiObj, - * the partitioning NMDedupMultiIdxType takes into account the NMDedupMultiObj - * instance to associate it with the right list. - * - * Hence, a NMDedupMultiIdxEntry has a list of possibly multiple NMDedupMultiHeadEntry - * instances, which each is the head for a list of NMDedupMultiEntry instances. - * In the platform example, the NMDedupMultiHeadEntry partition the indexed objects - * by their ifindex. */ -struct _NMDedupMultiIdxType { - union { - NMObjBaseInst parent; - const NMDedupMultiIdxTypeClass *klass; - }; - - CList lst_idx_head; - - guint len; -}; - -void nm_dedup_multi_idx_type_init (NMDedupMultiIdxType *idx_type, - const NMDedupMultiIdxTypeClass *klass); - -struct _NMDedupMultiIdxTypeClass { - NMObjBaseClass parent; - - void (*idx_obj_id_hash_update) (const NMDedupMultiIdxType *idx_type, - const NMDedupMultiObj *obj, - struct _NMHashState *h); - gboolean (*idx_obj_id_equal) (const NMDedupMultiIdxType *idx_type, - const NMDedupMultiObj *obj_a, - const NMDedupMultiObj *obj_b); - - /* an NMDedupMultiIdxTypeClass which implements partitioning of the - * tracked objects, must implement the idx_obj_partition*() functions. - * - * idx_obj_partitionable() may return NULL if the object cannot be tracked. - * For example, a index for routes by ifindex, may not want to track any - * routes that don't have a valid ifindex. If the idx-type says that the - * object is not partitionable, it is never added to the NMDedupMultiIndex. */ - gboolean (*idx_obj_partitionable) (const NMDedupMultiIdxType *idx_type, - const NMDedupMultiObj *obj); - void (*idx_obj_partition_hash_update) (const NMDedupMultiIdxType *idx_type, - const NMDedupMultiObj *obj, - struct _NMHashState *h); - gboolean (*idx_obj_partition_equal) (const NMDedupMultiIdxType *idx_type, - const NMDedupMultiObj *obj_a, - const NMDedupMultiObj *obj_b); -}; - -static inline gboolean -nm_dedup_multi_idx_type_id_equal (const NMDedupMultiIdxType *idx_type, - /* const NMDedupMultiObj * */ gconstpointer obj_a, - /* const NMDedupMultiObj * */ gconstpointer obj_b) -{ - nm_assert (idx_type); - return obj_a == obj_b - || idx_type->klass->idx_obj_id_equal (idx_type, - obj_a, - obj_b); -} - -static inline gboolean -nm_dedup_multi_idx_type_partition_equal (const NMDedupMultiIdxType *idx_type, - /* const NMDedupMultiObj * */ gconstpointer obj_a, - /* const NMDedupMultiObj * */ gconstpointer obj_b) -{ - nm_assert (idx_type); - if (idx_type->klass->idx_obj_partition_equal) { - nm_assert (obj_a); - nm_assert (obj_b); - return obj_a == obj_b - || idx_type->klass->idx_obj_partition_equal (idx_type, - obj_a, - obj_b); - } - return TRUE; -} - -/*****************************************************************************/ - -struct _NMDedupMultiEntry { - - /* this is the list of all entries that share the same head entry. - * All entries compare equal according to idx_obj_partition_equal(). */ - CList lst_entries; - - /* const NMDedupMultiObj * */ gconstpointer obj; - - bool is_head; - bool dirty; - - const NMDedupMultiHeadEntry *head; -}; - -struct _NMDedupMultiHeadEntry { - - /* this is the list of all entries that share the same head entry. - * All entries compare equal according to idx_obj_partition_equal(). */ - CList lst_entries_head; - - const NMDedupMultiIdxType *idx_type; - - bool is_head; - - guint len; - - CList lst_idx; -}; - -/*****************************************************************************/ - -static inline gconstpointer -nm_dedup_multi_entry_get_obj (const NMDedupMultiEntry *entry) -{ - /* convenience method that allows to skip the %NULL check on - * @entry. Think of the NULL-conditional operator ?. of C# */ - return entry ? entry->obj : NULL; -} - -/*****************************************************************************/ - -static inline void -nm_dedup_multi_entry_set_dirty (const NMDedupMultiEntry *entry, - gboolean dirty) -{ - /* NMDedupMultiEntry is always exposed as a const object, because it is not - * supposed to be modified outside NMDedupMultiIndex API. Except the "dirty" - * flag. In C++ speak, it is a mutable field. - * - * Add this inline function, to cast-away constness and set the dirty flag. */ - nm_assert (entry); - ((NMDedupMultiEntry *) entry)->dirty = dirty; -} - -/*****************************************************************************/ - -NMDedupMultiIndex *nm_dedup_multi_index_new (void); -NMDedupMultiIndex *nm_dedup_multi_index_ref (NMDedupMultiIndex *self); -NMDedupMultiIndex *nm_dedup_multi_index_unref (NMDedupMultiIndex *self); - -static inline void -_nm_auto_unref_dedup_multi_index (NMDedupMultiIndex **v) -{ - if (*v) - nm_dedup_multi_index_unref (*v); -} -#define nm_auto_unref_dedup_multi_index nm_auto(_nm_auto_unref_dedup_multi_index) - -#define NM_DEDUP_MULTI_ENTRY_MISSING ((const NMDedupMultiEntry *) GUINT_TO_POINTER (1)) -#define NM_DEDUP_MULTI_HEAD_ENTRY_MISSING ((const NMDedupMultiHeadEntry *) GUINT_TO_POINTER (1)) - -gboolean nm_dedup_multi_index_add_full (NMDedupMultiIndex *self, - NMDedupMultiIdxType *idx_type, - /*const NMDedupMultiObj * */ gconstpointer obj, - NMDedupMultiIdxMode mode, - const NMDedupMultiEntry *entry_order, - const NMDedupMultiEntry *entry_existing, - const NMDedupMultiHeadEntry *head_existing, - const NMDedupMultiEntry **out_entry, - /* const NMDedupMultiObj ** */ gpointer out_obj_old); - -gboolean nm_dedup_multi_index_add (NMDedupMultiIndex *self, - NMDedupMultiIdxType *idx_type, - /*const NMDedupMultiObj * */ gconstpointer obj, - NMDedupMultiIdxMode mode, - const NMDedupMultiEntry **out_entry, - /* const NMDedupMultiObj ** */ gpointer out_obj_old); - -const NMDedupMultiEntry *nm_dedup_multi_index_lookup_obj (const NMDedupMultiIndex *self, - const NMDedupMultiIdxType *idx_type, - /*const NMDedupMultiObj * */ gconstpointer obj); - -const NMDedupMultiHeadEntry *nm_dedup_multi_index_lookup_head (const NMDedupMultiIndex *self, - const NMDedupMultiIdxType *idx_type, - /*const NMDedupMultiObj * */ gconstpointer obj); - -guint nm_dedup_multi_index_remove_entry (NMDedupMultiIndex *self, - gconstpointer entry); - -guint nm_dedup_multi_index_remove_obj (NMDedupMultiIndex *self, - NMDedupMultiIdxType *idx_type, - /*const NMDedupMultiObj * */ gconstpointer obj, - /*const NMDedupMultiObj ** */ gconstpointer *out_obj); - -guint nm_dedup_multi_index_remove_head (NMDedupMultiIndex *self, - NMDedupMultiIdxType *idx_type, - /*const NMDedupMultiObj * */ gconstpointer obj); - -guint nm_dedup_multi_index_remove_idx (NMDedupMultiIndex *self, - NMDedupMultiIdxType *idx_type); - -void nm_dedup_multi_index_dirty_set_head (NMDedupMultiIndex *self, - const NMDedupMultiIdxType *idx_type, - /*const NMDedupMultiObj * */ gconstpointer obj); - -void nm_dedup_multi_index_dirty_set_idx (NMDedupMultiIndex *self, - const NMDedupMultiIdxType *idx_type); - -guint nm_dedup_multi_index_dirty_remove_idx (NMDedupMultiIndex *self, - NMDedupMultiIdxType *idx_type, - gboolean mark_survivors_dirty); - -/*****************************************************************************/ - -typedef struct _NMDedupMultiIter { - const CList *_head; - const CList *_next; - const NMDedupMultiEntry *current; -} NMDedupMultiIter; - -static inline void -nm_dedup_multi_iter_init (NMDedupMultiIter *iter, const NMDedupMultiHeadEntry *head) -{ - g_return_if_fail (iter); - - if (head && !c_list_is_empty (&head->lst_entries_head)) { - iter->_head = &head->lst_entries_head; - iter->_next = head->lst_entries_head.next; - } else { - iter->_head = NULL; - iter->_next = NULL; - } - iter->current = NULL; -} - -static inline gboolean -nm_dedup_multi_iter_next (NMDedupMultiIter *iter) -{ - g_return_val_if_fail (iter, FALSE); - - if (!iter->_next) - return FALSE; - - /* we always look ahead for the next. This way, the user - * may delete the current entry (but no other entries). */ - iter->current = c_list_entry (iter->_next, NMDedupMultiEntry, lst_entries); - if (iter->_next->next == iter->_head) - iter->_next = NULL; - else - iter->_next = iter->_next->next; - return TRUE; -} - -#define nm_dedup_multi_iter_for_each(iter, head_entry) \ - for (nm_dedup_multi_iter_init ((iter), (head_entry)); \ - nm_dedup_multi_iter_next ((iter)); \ - ) - -/*****************************************************************************/ - -typedef gboolean (*NMDedupMultiFcnSelectPredicate) (/* const NMDedupMultiObj * */ gconstpointer obj, - gpointer user_data); - -gconstpointer *nm_dedup_multi_objs_to_array_head (const NMDedupMultiHeadEntry *head_entry, - NMDedupMultiFcnSelectPredicate predicate, - gpointer user_data, - guint *out_len); -GPtrArray *nm_dedup_multi_objs_to_ptr_array_head (const NMDedupMultiHeadEntry *head_entry, - NMDedupMultiFcnSelectPredicate predicate, - gpointer user_data); - -static inline const NMDedupMultiEntry * -nm_dedup_multi_head_entry_get_idx (const NMDedupMultiHeadEntry *head_entry, - int idx) -{ - CList *iter; - - if (head_entry) { - if (idx >= 0) { - c_list_for_each (iter, &head_entry->lst_entries_head) { - if (idx-- == 0) - return c_list_entry (iter, NMDedupMultiEntry, lst_entries); - } - } else { - for (iter = head_entry->lst_entries_head.prev; - iter != &head_entry->lst_entries_head; - iter = iter->prev) { - if (++idx == 0) - return c_list_entry (iter, NMDedupMultiEntry, lst_entries); - } - } - } - return NULL; -} - -static inline void -nm_dedup_multi_head_entry_sort (const NMDedupMultiHeadEntry *head_entry, - CListSortCmp cmp, - gconstpointer user_data) -{ - if (head_entry) { - /* the head entry can be sorted directly without messing up the - * index to which it belongs. Of course, this does mess up any - * NMDedupMultiIter instances. */ - c_list_sort ((CList *) &head_entry->lst_entries_head, cmp, user_data); - } -} - -gboolean nm_dedup_multi_entry_reorder (const NMDedupMultiEntry *entry, - const NMDedupMultiEntry *entry_order, - gboolean order_after); - -/*****************************************************************************/ - -#endif /* __NM_DEDUP_MULTI_H__ */ diff --git a/shared/nm-utils/nm-enum-utils.c b/shared/nm-utils/nm-enum-utils.c deleted file mode 100644 index a4f6e809..00000000 --- a/shared/nm-utils/nm-enum-utils.c +++ /dev/null @@ -1,372 +0,0 @@ -/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ -/* NetworkManager -- Network link manager - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the - * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, - * Boston, MA 02110-1301 USA. - * - * (C) Copyright 2017 Red Hat, Inc. - */ - -#include "nm-default.h" - -#include "nm-enum-utils.h" - -/*****************************************************************************/ - -#define IS_FLAGS_SEPARATOR(ch) (NM_IN_SET ((ch), ' ', '\t', ',', '\n', '\r')) - -static void -_ASSERT_enum_values_info (GType type, - const NMUtilsEnumValueInfo *value_infos) -{ -#if NM_MORE_ASSERTS > 5 - nm_auto_unref_gtypeclass GTypeClass *klass = NULL; - gs_unref_hashtable GHashTable *ht = NULL; - - klass = g_type_class_ref (type); - - g_assert (G_IS_ENUM_CLASS (klass) || G_IS_FLAGS_CLASS (klass)); - - if (!value_infos) - return; - - ht = g_hash_table_new (g_str_hash, g_str_equal); - - for (; value_infos->nick; value_infos++) { - - g_assert (value_infos->nick[0]); - - /* duplicate nicks make no sense!! */ - g_assert (!g_hash_table_contains (ht, value_infos->nick)); - g_hash_table_add (ht, (gpointer) value_infos->nick); - - if (G_IS_ENUM_CLASS (klass)) { - GEnumValue *enum_value; - - enum_value = g_enum_get_value_by_nick (G_ENUM_CLASS (klass), value_infos->nick); - if (enum_value) { - /* we do allow specifying the same name via @value_infos and @type. - * That might make sense, if @type comes from a library where older versions - * of the library don't yet support the value. In this case, the caller can - * provide the nick via @value_infos, to support the older library version. - * And then, when actually running against a newer library version where - * @type knows the nick, we have this situation. - * - * Another reason for specifying a nick both in @value_infos and @type, - * is to specify an alias which is not used with highest preference. For - * example, if you add an alias "disabled" for "none" (both numerically - * equal), then the first alias in @value_infos will be preferred over - * the name from @type. So, to still use "none" as preferred name, you may - * explicitly specify the "none" alias in @value_infos before "disabled". - * - * However, what never is allowed, is to use a name (nick) to re-number - * the value. That is, if both @value_infos and @type contain a particular - * nick, their numeric values must agree as well. - * Allowing this, would be very confusing, because the name would have a different - * value from the regular GLib GEnum API. - */ - g_assert (enum_value->value == value_infos->value); - } - } else { - GFlagsValue *flags_value; - - flags_value = g_flags_get_value_by_nick (G_FLAGS_CLASS (klass), value_infos->nick); - if (flags_value) { - /* see ENUM case above. */ - g_assert (flags_value->value == (guint) value_infos->value); - } - } - } -#endif -} - -static gboolean -_is_hex_string (const char *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) -{ - 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); -} - -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); -} - -char * -_nm_utils_enum_to_str_full (GType type, - int value, - const char *flags_separator, - const NMUtilsEnumValueInfo *value_infos) -{ - nm_auto_unref_gtypeclass GTypeClass *klass = NULL; - - _ASSERT_enum_values_info (type, value_infos); - - if ( flags_separator - && ( !flags_separator[0] - || NM_STRCHAR_ANY (flags_separator, ch, !IS_FLAGS_SEPARATOR (ch)))) - g_return_val_if_reached (NULL); - - klass = g_type_class_ref (type); - - if (G_IS_ENUM_CLASS (klass)) { - GEnumValue *enum_value; - - for ( ; value_infos && value_infos->nick; value_infos++) { - if (value_infos->value == value) - return g_strdup (value_infos->nick); - } - - enum_value = g_enum_get_value (G_ENUM_CLASS (klass), value); - if ( !enum_value - || !_enum_is_valid_enum_nick (enum_value->value_nick)) - return g_strdup_printf ("%d", value); - else - return g_strdup (enum_value->value_nick); - } else if (G_IS_FLAGS_CLASS (klass)) { - GFlagsValue *flags_value; - GString *str = g_string_new (""); - unsigned uvalue = (unsigned) value; - - flags_separator = flags_separator ?: " "; - - for ( ; value_infos && value_infos->nick; value_infos++) { - - nm_assert (_enum_is_valid_flags_nick (value_infos->nick)); - - if (uvalue == 0) { - if (value_infos->value != 0) - continue; - } else { - if (!NM_FLAGS_ALL (uvalue, (unsigned) value_infos->value)) - continue; - } - - if (str->len) - g_string_append (str, flags_separator); - g_string_append (str, value_infos->nick); - uvalue &= ~((unsigned) value_infos->value); - if (uvalue == 0) { - /* we printed all flags. Done. */ - goto flags_done; - } - } - - do { - flags_value = g_flags_get_first_value (G_FLAGS_CLASS (klass), uvalue); - if (str->len) - g_string_append (str, flags_separator); - if ( !flags_value - || !_enum_is_valid_flags_nick (flags_value->value_nick)) { - if (uvalue) - g_string_append_printf (str, "0x%x", uvalue); - break; - } - g_string_append (str, flags_value->value_nick); - uvalue &= ~flags_value->value; - } while (uvalue); - -flags_done: - return g_string_free (str, FALSE); - } - - g_return_val_if_reached (NULL); -} - -static const NMUtilsEnumValueInfo * -_find_value_info (const NMUtilsEnumValueInfo *value_infos, const char *needle) -{ - if (value_infos) { - for (; value_infos->nick; value_infos++) { - if (nm_streq (needle, value_infos->nick)) - return value_infos; - } - } - return NULL; -} - -gboolean -_nm_utils_enum_from_str_full (GType type, - const char *str, - int *out_value, - char **err_token, - const NMUtilsEnumValueInfo *value_infos) -{ - GTypeClass *klass; - gboolean ret = FALSE; - int value = 0; - gs_free char *str_clone = NULL; - char *s; - gint64 v64; - const NMUtilsEnumValueInfo *nick; - - g_return_val_if_fail (str, FALSE); - - _ASSERT_enum_values_info (type, value_infos); - - str_clone = strdup (str); - s = nm_str_skip_leading_spaces (str_clone); - g_strchomp (s); - - klass = g_type_class_ref (type); - - if (G_IS_ENUM_CLASS (klass)) { - GEnumValue *enum_value; - - 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; - } - } else if (_is_dec_string (s)) { - v64 = _nm_utils_ascii_str_to_int64 (s, 10, 0, G_MAXUINT, -1); - if (v64 != -1) { - value = (int) v64; - ret = TRUE; - } - } else if ((nick = _find_value_info (value_infos, s))) { - value = nick->value; - ret = TRUE; - } else if ((enum_value = g_enum_get_value_by_nick (G_ENUM_CLASS (klass), s))) { - value = enum_value->value; - ret = TRUE; - } - } - } else if (G_IS_FLAGS_CLASS (klass)) { - GFlagsValue *flags_value; - unsigned uvalue = 0; - - ret = TRUE; - while (s[0]) { - char *s_end; - - for (s_end = s; s_end[0]; s_end++) { - if (IS_FLAGS_SEPARATOR (s_end[0])) { - s_end[0] = '\0'; - s_end++; - break; - } - } - - if (s[0]) { - if (_is_hex_string (s)) { - 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)) { - v64 = _nm_utils_ascii_str_to_int64 (s, 10, 0, G_MAXUINT, -1); - if (v64 == -1) { - ret = FALSE; - break; - } - uvalue |= (unsigned) v64; - } else if ((nick = _find_value_info (value_infos, s))) - uvalue |= (unsigned) nick->value; - else if ((flags_value = g_flags_get_value_by_nick (G_FLAGS_CLASS (klass), s))) - uvalue |= flags_value->value; - else { - ret = FALSE; - break; - } - } - - s = s_end; - } - - value = (int) uvalue; - } else - g_return_val_if_reached (FALSE); - - NM_SET_OUT (err_token, !ret && s[0] ? g_strdup (s) : NULL); - NM_SET_OUT (out_value, ret ? value : 0); - g_type_class_unref (klass); - return ret; -} - -const char ** -_nm_utils_enum_get_values (GType type, int from, int to) -{ - GTypeClass *klass; - GPtrArray *array; - int i; - char sbuf[64]; - - klass = g_type_class_ref (type); - array = g_ptr_array_new (); - - if (G_IS_ENUM_CLASS (klass)) { - GEnumClass *enum_class = G_ENUM_CLASS (klass); - GEnumValue *enum_value; - - for (i = 0; i < enum_class->n_values; i++) { - enum_value = &enum_class->values[i]; - if (enum_value->value >= from && enum_value->value <= to) { - if (_enum_is_valid_enum_nick (enum_value->value_nick)) - g_ptr_array_add (array, (gpointer) enum_value->value_nick); - else - g_ptr_array_add (array, (gpointer) g_intern_string (nm_sprintf_buf (sbuf, "%d", enum_value->value))); - } - } - } else if (G_IS_FLAGS_CLASS (klass)) { - GFlagsClass *flags_class = G_FLAGS_CLASS (klass); - GFlagsValue *flags_value; - - for (i = 0; i < flags_class->n_values; i++) { - flags_value = &flags_class->values[i]; - if (flags_value->value >= (guint) from && flags_value->value <= (guint) to) { - if (_enum_is_valid_flags_nick (flags_value->value_nick)) - g_ptr_array_add (array, (gpointer) flags_value->value_nick); - else - g_ptr_array_add (array, (gpointer) g_intern_string (nm_sprintf_buf (sbuf, "0x%x", (unsigned) flags_value->value))); - } - } - } else { - g_type_class_unref (klass); - g_ptr_array_free (array, TRUE); - g_return_val_if_reached (NULL); - } - - g_type_class_unref (klass); - g_ptr_array_add (array, NULL); - - return (const char **) g_ptr_array_free (array, FALSE); -} diff --git a/shared/nm-utils/nm-enum-utils.h b/shared/nm-utils/nm-enum-utils.h deleted file mode 100644 index 1827fdf4..00000000 --- a/shared/nm-utils/nm-enum-utils.h +++ /dev/null @@ -1,48 +0,0 @@ -/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ -/* NetworkManager -- Network link manager - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the - * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, - * Boston, MA 02110-1301 USA. - * - * (C) Copyright 2017 Red Hat, Inc. - */ - -#ifndef __NM_ENUM_UTILS_H__ -#define __NM_ENUM_UTILS_H__ - -/*****************************************************************************/ - -typedef struct _NMUtilsEnumValueInfo { - /* currently, this is only used for _nm_utils_enum_from_str_full() to - * declare additional aliases for values. */ - const char *nick; - int value; -} NMUtilsEnumValueInfo; - -char *_nm_utils_enum_to_str_full (GType type, - int value, - const char *sep, - const NMUtilsEnumValueInfo *value_infos); -gboolean _nm_utils_enum_from_str_full (GType type, - const char *str, - int *out_value, - char **err_token, - const NMUtilsEnumValueInfo *value_infos); - -const char **_nm_utils_enum_get_values (GType type, int from, int to); - -/*****************************************************************************/ - -#endif /* __NM_ENUM_UTILS_H__ */ diff --git a/shared/nm-utils/nm-errno.c b/shared/nm-utils/nm-errno.c deleted file mode 100644 index 30eb9a8e..00000000 --- a/shared/nm-utils/nm-errno.c +++ /dev/null @@ -1,198 +0,0 @@ -/* NetworkManager -- Network link manager - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the - * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, - * Boston, MA 02110-1301 USA. - * - * Copyright 2018 Red Hat, Inc. - */ - -#include "nm-default.h" - -#include "nm-errno.h" - -#include - -/*****************************************************************************/ - -NM_UTILS_LOOKUP_STR_DEFINE_STATIC (_geterror, -#if 0 - enum _NMErrno, -#else - int, -#endif - NM_UTILS_LOOKUP_DEFAULT (NULL), - - NM_UTILS_LOOKUP_STR_ITEM (NME_ERRNO_SUCCESS, "NME_ERRNO_SUCCESS"), - NM_UTILS_LOOKUP_STR_ITEM (NME_ERRNO_OUT_OF_RANGE, "NME_ERRNO_OUT_OF_RANGE"), - - NM_UTILS_LOOKUP_STR_ITEM (NME_UNSPEC, "NME_UNSPEC"), - NM_UTILS_LOOKUP_STR_ITEM (NME_BUG, "NME_BUG"), - NM_UTILS_LOOKUP_STR_ITEM (NME_NATIVE_ERRNO, "NME_NATIVE_ERRNO"), - - NM_UTILS_LOOKUP_STR_ITEM (NME_NL_ATTRSIZE, "NME_NL_ATTRSIZE"), - NM_UTILS_LOOKUP_STR_ITEM (NME_NL_BAD_SOCK, "NME_NL_BAD_SOCK"), - NM_UTILS_LOOKUP_STR_ITEM (NME_NL_DUMP_INTR, "NME_NL_DUMP_INTR"), - NM_UTILS_LOOKUP_STR_ITEM (NME_NL_MSG_OVERFLOW, "NME_NL_MSG_OVERFLOW"), - NM_UTILS_LOOKUP_STR_ITEM (NME_NL_MSG_TOOSHORT, "NME_NL_MSG_TOOSHORT"), - NM_UTILS_LOOKUP_STR_ITEM (NME_NL_MSG_TRUNC, "NME_NL_MSG_TRUNC"), - NM_UTILS_LOOKUP_STR_ITEM (NME_NL_SEQ_MISMATCH, "NME_NL_SEQ_MISMATCH"), - NM_UTILS_LOOKUP_STR_ITEM (NME_NL_NOADDR, "NME_NL_NOADDR"), - - NM_UTILS_LOOKUP_STR_ITEM (NME_PL_NOT_FOUND, "not-found"), - NM_UTILS_LOOKUP_STR_ITEM (NME_PL_EXISTS, "exists"), - NM_UTILS_LOOKUP_STR_ITEM (NME_PL_WRONG_TYPE, "wrong-type"), - NM_UTILS_LOOKUP_STR_ITEM (NME_PL_NOT_SLAVE, "not-slave"), - NM_UTILS_LOOKUP_STR_ITEM (NME_PL_NO_FIRMWARE, "no-firmware"), - NM_UTILS_LOOKUP_STR_ITEM (NME_PL_OPNOTSUPP, "not-supported"), - NM_UTILS_LOOKUP_STR_ITEM (NME_PL_NETLINK, "netlink"), - NM_UTILS_LOOKUP_STR_ITEM (NME_PL_CANT_SET_MTU, "cant-set-mtu"), - - NM_UTILS_LOOKUP_ITEM_IGNORE (_NM_ERRNO_MININT), - NM_UTILS_LOOKUP_ITEM_IGNORE (_NM_ERRNO_RESERVED_LAST_PLUS_1), -); - -/** - * nm_strerror(): - * @nmerr: the NetworkManager specific errno to be converted - * to string. - * - * NetworkManager specific error numbers reserve a range in "errno.h" with - * our own defines. For numbers that don't fall into this range, the numbers - * are identical to the common error numbers. - * - * Idential to strerror(), g_strerror(), nm_strerror_native() for error numbers - * that are not in the reserved range of NetworkManager specific errors. - * - * Returns: (transfer none): the string representation of the error number. - */ -const char * -nm_strerror (int nmerr) -{ - const char *s; - - nmerr = nm_errno (nmerr); - - if (nmerr >= _NM_ERRNO_RESERVED_FIRST) { - s = _geterror (nmerr); - if (s) - return s; - } - return nm_strerror_native (nmerr); -} - -/*****************************************************************************/ - -/** - * nm_strerror_native_r: - * @errsv: the errno to convert to string. - * @buf: the output buffer where to write the string to. - * @buf_size: the length of buffer. - * - * This is like strerror_r(), with one difference: depending on the - * locale, the returned string is guaranteed to be valid UTF-8. - * Also, there is some confusion as to whether to use glibc's - * strerror_r() or the POXIX/XSI variant. This is abstracted - * by the function. - * - * Note that the returned buffer may also be a statically allocated - * buffer, and not the input buffer @buf. Consequently, the returned - * string may be longer than @buf_size. - * - * Returns: (transfer none): a NUL terminated error message. This is either a static - * string (that is never freed), or the provided @buf argumnt. - */ -const char * -nm_strerror_native_r (int errsv, char *buf, gsize buf_size) -{ - char *buf2; - - nm_assert (buf); - nm_assert (buf_size > 0); - -#if (_POSIX_C_SOURCE >= 200112L) && ! _GNU_SOURCE - /* XSI-compliant */ - { - int errno_saved = errno; - - if (strerror_r (errsv, buf, buf_size) != 0) { - g_snprintf (buf, buf_size, "Unspecified errno %d", errsv); - errno = errno_saved; - } - buf2 = buf; - } -#else - /* GNU-specific */ - buf2 = strerror_r (errsv, buf, buf_size); -#endif - - /* like g_strerror(), ensure that the error message is UTF-8. */ - if ( !g_get_charset (NULL) - && !g_utf8_validate (buf2, -1, NULL)) { - gs_free char *msg = NULL; - - msg = g_locale_to_utf8 (buf2, -1, NULL, NULL, NULL); - if (msg) { - g_strlcpy (buf, msg, buf_size); - buf2 = buf; - } - } - - return buf2; -} - -/** - * nm_strerror_native: - * @errsv: the errno integer from - * - * Like strerror(), but strerror() is not thread-safe and not guaranteed - * to be UTF-8. - * - * g_strerror() is a thread-safe variant of strerror(), however it caches - * all returned strings in a dictionary. That means, using this on untrusted - * error numbers can result in this cache to grow without limits. - * - * Instead, return a tread-local buffer. This way, it's thread-safe. - * - * There is a downside to this: subsequent calls of nm_strerror_native() - * overwrite the error message. - * - * Returns: (transfer none): the text representation of the error number. - */ -const char * -nm_strerror_native (int errsv) -{ - static _nm_thread_local char *buf_static = NULL; - char *buf; - - buf = buf_static; - if (G_UNLIKELY (!buf)) { - int errno_saved = errno; - pthread_key_t key; - - buf = g_malloc (NM_STRERROR_BUFSIZE); - buf_static = buf; - - if ( pthread_key_create (&key, g_free) != 0 - || pthread_setspecific (key, buf) != 0) { - /* Failure. We will leak the buffer when the thread exits. - * - * Nothing we can do about it really. For Debug builds we fail with an assertion. */ - nm_assert_not_reached (); - } - errno = errno_saved; - } - - return nm_strerror_native_r (errsv, buf, NM_STRERROR_BUFSIZE); -} diff --git a/shared/nm-utils/nm-errno.h b/shared/nm-utils/nm-errno.h deleted file mode 100644 index d77735a7..00000000 --- a/shared/nm-utils/nm-errno.h +++ /dev/null @@ -1,185 +0,0 @@ -/* NetworkManager -- Network link manager - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the - * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, - * Boston, MA 02110-1301 USA. - * - * Copyright 2018 Red Hat, Inc. - */ - -#ifndef __NM_ERRNO_H__ -#define __NM_ERRNO_H__ - -#include - -/*****************************************************************************/ - -enum _NMErrno { - _NM_ERRNO_MININT = G_MININT, - _NM_ERRNO_MAXINT = G_MAXINT, - _NM_ERRNO_RESERVED_FIRST = 100000, - - - /* when we cannot represent a number as positive number, we resort to this - * number. Basically, the values G_MININT, -NME_ERRNO_SUCCESS, NME_ERRNO_SUCCESS - * and G_MAXINT all map to the same value. */ - NME_ERRNO_OUT_OF_RANGE = G_MAXINT, - - /* Indicate that the original errno was zero. Zero denotes *no error*, but we know something - * went wrong and we want to report some error. This is a placeholder to mean, something - * was wrong, but errno was zero. */ - NME_ERRNO_SUCCESS = G_MAXINT - 1, - - - /* an unspecified error. */ - NME_UNSPEC = _NM_ERRNO_RESERVED_FIRST, - - /* A bug, for example when an assertion failed. - * Should never happen. */ - NME_BUG, - - /* a native error number (from ) cannot be mapped as - * an nm-error, because it is in the range [_NM_ERRNO_RESERVED_FIRST, - * _NM_ERRNO_RESERVED_LAST]. */ - NME_NATIVE_ERRNO, - - /* netlink errors. */ - NME_NL_SEQ_MISMATCH, - NME_NL_MSG_TRUNC, - NME_NL_MSG_TOOSHORT, - NME_NL_DUMP_INTR, - NME_NL_ATTRSIZE, - NME_NL_BAD_SOCK, - NME_NL_NOADDR, - NME_NL_MSG_OVERFLOW, - - /* platform errors. */ - NME_PL_NOT_FOUND, - NME_PL_EXISTS, - NME_PL_WRONG_TYPE, - NME_PL_NOT_SLAVE, - NME_PL_NO_FIRMWARE, - NME_PL_OPNOTSUPP, - NME_PL_NETLINK, - NME_PL_CANT_SET_MTU, - - _NM_ERRNO_RESERVED_LAST_PLUS_1, - _NM_ERRNO_RESERVED_LAST = _NM_ERRNO_RESERVED_LAST_PLUS_1 - 1, -}; - -/*****************************************************************************/ - -/* When we receive an errno from a system function, we can safely assume - * that the error number is not negative. We rely on that, and possibly just - * "return -errsv;" to signal an error. We also rely on that, because libc - * is our trusted base: meaning, if it cannot even succeed at setting errno - * according to specification, all bets are off. - * - * This macro returns the input argument, and asserts that the error variable - * is positive. - * - * In a sense, the macro is related to nm_errno_native() function, but the difference - * is that this macro asserts that @errsv is positive, while nm_errno_native() coerces - * negative values to be non-negative. */ -#define NM_ERRNO_NATIVE(errsv) \ - ({ \ - const int _errsv_x = (errsv); \ - \ - nm_assert (_errsv_x > 0); \ - _errsv_x; \ - }) - -/* Normalize native errno. - * - * Our API may return native error codes () as negative values. This function - * takes such an errno, and normalizes it to their positive value. - * - * The special values G_MININT and zero are coerced to NME_ERRNO_OUT_OF_RANGE and NME_ERRNO_SUCCESS - * respectively. - * Other values are coerced to their inverse. - * Other positive values are returned unchanged. - * - * Basically, this normalizes errsv to be positive (taking care of two pathological cases). - */ -static inline int -nm_errno_native (int errsv) -{ - switch (errsv) { - case 0: return NME_ERRNO_SUCCESS; - case G_MININT: return NME_ERRNO_OUT_OF_RANGE; - default: - return errsv >= 0 ? errsv : -errsv; - } -} - -/* Normalizes an nm-error to be positive. - * - * Various API returns negative error codes, and this function converts the negative - * value to its positive. - * - * Note that @nmerr is on the domain of NetworkManager specific error numbers, - * which is not the same as the native error numbers (errsv from ). But - * as far as normalizing goes, nm_errno() does exactly the same remapping as - * nm_errno_native(). */ -static inline int -nm_errno (int nmerr) -{ - return nm_errno_native (nmerr); -} - -/* this maps a native errno to a (always non-negative) nm-error number. - * - * Note that nm-error numbers are embedded into the range of regular - * errno. The only difference is, that nm-error numbers reserve a - * range (_NM_ERRNO_RESERVED_FIRST, _NM_ERRNO_RESERVED_LAST) for their - * own purpose. - * - * That means, converting an errno to nm-error number means in - * most cases just returning itself. - * Only pathological cases need special handling: - * - * - 0 is mapped to NME_ERRNO_SUCCESS; - * - G_MININT is mapped to NME_ERRNO_OUT_OF_RANGE; - * - values in the range of (+/-) [_NM_ERRNO_RESERVED_FIRST, _NM_ERRNO_RESERVED_LAST] - * are mapped to NME_NATIVE_ERRNO - * - all other values are their (positive) absolute value. - */ -static inline int -nm_errno_from_native (int errsv) -{ - switch (errsv) { - case 0: return NME_ERRNO_SUCCESS; - case G_MININT: return NME_ERRNO_OUT_OF_RANGE; - default: - if (errsv < 0) - errsv = -errsv; - return G_UNLIKELY ( errsv >= _NM_ERRNO_RESERVED_FIRST - && errsv <= _NM_ERRNO_RESERVED_LAST) - ? NME_NATIVE_ERRNO - : errsv; - } -} - -const char *nm_strerror (int nmerr); - -/*****************************************************************************/ - -#define NM_STRERROR_BUFSIZE 1024 - -const char *nm_strerror_native_r (int errsv, char *buf, gsize buf_size); -const char *nm_strerror_native (int errsv); - -/*****************************************************************************/ - -#endif /* __NM_ERRNO_H__ */ diff --git a/shared/nm-utils/nm-glib.h b/shared/nm-utils/nm-glib.h deleted file mode 100644 index e941e067..00000000 --- a/shared/nm-utils/nm-glib.h +++ /dev/null @@ -1,567 +0,0 @@ -/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ -/* - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Copyright 2008 - 2018 Red Hat, Inc. - */ - -#ifndef __NM_GLIB_H__ -#define __NM_GLIB_H__ - -/*****************************************************************************/ - -#ifndef __NM_MACROS_INTERNAL_H__ -#error "nm-glib.h requires nm-macros-internal.h. Do not include this directly" -#endif - -/*****************************************************************************/ - -#ifdef __clang__ - -#undef G_GNUC_BEGIN_IGNORE_DEPRECATIONS -#undef G_GNUC_END_IGNORE_DEPRECATIONS - -#define G_GNUC_BEGIN_IGNORE_DEPRECATIONS \ - _Pragma("clang diagnostic push") \ - _Pragma("clang diagnostic ignored \"-Wdeprecated-declarations\"") - -#define G_GNUC_END_IGNORE_DEPRECATIONS \ - _Pragma("clang diagnostic pop") - -#endif - -/*****************************************************************************/ - -static inline void -__g_type_ensure (GType type) -{ -#if !GLIB_CHECK_VERSION(2,34,0) - if (G_UNLIKELY (type == (GType)-1)) - g_error ("can't happen"); -#else - G_GNUC_BEGIN_IGNORE_DEPRECATIONS; - g_type_ensure (type); - G_GNUC_END_IGNORE_DEPRECATIONS; -#endif -} -#define g_type_ensure __g_type_ensure - -/*****************************************************************************/ - -#if !GLIB_CHECK_VERSION(2,34,0) - -#define g_clear_pointer(pp, destroy) \ - G_STMT_START { \ - G_STATIC_ASSERT (sizeof *(pp) == sizeof (gpointer)); \ - /* Only one access, please */ \ - gpointer *_pp = (gpointer *) (pp); \ - gpointer _p; \ - /* This assignment is needed to avoid a gcc warning */ \ - GDestroyNotify _destroy = (GDestroyNotify) (destroy); \ - \ - _p = *_pp; \ - if (_p) \ - { \ - *_pp = NULL; \ - _destroy (_p); \ - } \ - } G_STMT_END - -#endif - -/*****************************************************************************/ - -#if !GLIB_CHECK_VERSION(2,34,0) - -/* These are used to clean up the output of test programs; we can just let - * them no-op in older glib. - */ -#define g_test_expect_message(log_domain, log_level, pattern) -#define g_test_assert_expected_messages() - -#else - -/* We build with -DGLIB_MAX_ALLOWED_VERSION set to 2.32 to make sure we don't - * accidentally use new API that we shouldn't. But we don't want warnings for - * the APIs that we emulate above. - */ - -#define g_test_expect_message(domain, level, format...) \ - G_STMT_START { \ - G_GNUC_BEGIN_IGNORE_DEPRECATIONS \ - g_test_expect_message (domain, level, format); \ - G_GNUC_END_IGNORE_DEPRECATIONS \ - } G_STMT_END - -#define g_test_assert_expected_messages_internal(domain, file, line, func) \ - G_STMT_START { \ - G_GNUC_BEGIN_IGNORE_DEPRECATIONS \ - g_test_assert_expected_messages_internal (domain, file, line, func); \ - G_GNUC_END_IGNORE_DEPRECATIONS \ - } G_STMT_END - -#endif - -/*****************************************************************************/ - -#if GLIB_CHECK_VERSION (2, 35, 0) -/* For glib >= 2.36, g_type_init() is deprecated. - * But since 2.35.1 (7c42ab23b55c43ab96d0ac2124b550bf1f49c1ec) this function - * does nothing. Replace the call with empty statement. */ -#define nm_g_type_init() G_STMT_START { (void) 0; } G_STMT_END -#else -#define nm_g_type_init() G_STMT_START { g_type_init (); } G_STMT_END -#endif - -/*****************************************************************************/ - -/* g_test_initialized() is only available since glib 2.36. */ -#if !GLIB_CHECK_VERSION (2, 36, 0) -#define g_test_initialized() (g_test_config_vars->test_initialized) -#endif - -/*****************************************************************************/ - -/* g_assert_cmpmem() is only available since glib 2.46. */ -#if !GLIB_CHECK_VERSION (2, 45, 7) -#define g_assert_cmpmem(m1, l1, m2, l2) G_STMT_START {\ - gconstpointer __m1 = m1, __m2 = m2; \ - int __l1 = l1, __l2 = l2; \ - if (__l1 != __l2) \ - g_assertion_message_cmpnum (G_LOG_DOMAIN, __FILE__, __LINE__, G_STRFUNC, \ - #l1 " (len(" #m1 ")) == " #l2 " (len(" #m2 "))", __l1, "==", __l2, 'i'); \ - else if (memcmp (__m1, __m2, __l1) != 0) \ - g_assertion_message (G_LOG_DOMAIN, __FILE__, __LINE__, G_STRFUNC, \ - "assertion failed (" #m1 " == " #m2 ")"); \ - } G_STMT_END -#endif - -/*****************************************************************************/ - -/* Rumtime check for glib version. First do a compile time check which - * (if satisfied) shortcuts the runtime check. */ -static inline gboolean -nm_glib_check_version (guint major, guint minor, guint micro) -{ - return GLIB_CHECK_VERSION (major, minor, micro) - || ( ( glib_major_version > major) - || ( glib_major_version == major - && glib_minor_version > minor) - || ( glib_major_version == major - && glib_minor_version == minor - && glib_micro_version < micro)); -} - -/*****************************************************************************/ - -/* g_test_skip() is only available since glib 2.38. Add a compatibility wrapper. */ -static inline void -__nmtst_g_test_skip (const char *msg) -{ -#if GLIB_CHECK_VERSION (2, 38, 0) - G_GNUC_BEGIN_IGNORE_DEPRECATIONS - g_test_skip (msg); - G_GNUC_END_IGNORE_DEPRECATIONS -#else - g_debug ("%s", msg); -#endif -} -#define g_test_skip __nmtst_g_test_skip - -/*****************************************************************************/ - -/* g_test_add_data_func_full() is only available since glib 2.34. Add a compatibility wrapper. */ -static inline void -__g_test_add_data_func_full (const char *testpath, - gpointer test_data, - GTestDataFunc test_func, - GDestroyNotify data_free_func) -{ -#if GLIB_CHECK_VERSION (2, 34, 0) - G_GNUC_BEGIN_IGNORE_DEPRECATIONS - g_test_add_data_func_full (testpath, test_data, test_func, data_free_func); - G_GNUC_END_IGNORE_DEPRECATIONS -#else - g_return_if_fail (testpath != NULL); - g_return_if_fail (testpath[0] == '/'); - g_return_if_fail (test_func != NULL); - - g_test_add_vtable (testpath, 0, test_data, NULL, - (GTestFixtureFunc) test_func, - (GTestFixtureFunc) data_free_func); -#endif -} -#define g_test_add_data_func_full __g_test_add_data_func_full - -/*****************************************************************************/ - -#if !GLIB_CHECK_VERSION (2, 34, 0) -#define G_DEFINE_QUARK(QN, q_n) \ -GQuark \ -q_n##_quark (void) \ -{ \ - static GQuark q; \ - \ - if G_UNLIKELY (q == 0) \ - q = g_quark_from_static_string (#QN); \ - \ - return q; \ -} -#endif - -/*****************************************************************************/ - -static inline gboolean -nm_g_hash_table_replace (GHashTable *hash, gpointer key, gpointer value) -{ - /* glib 2.40 added a return value indicating whether the key already existed - * (910191597a6c2e5d5d460e9ce9efb4f47d9cc63c). */ -#if GLIB_CHECK_VERSION(2, 40, 0) - return g_hash_table_replace (hash, key, value); -#else - gboolean contained = g_hash_table_contains (hash, key); - - g_hash_table_replace (hash, key, value); - return !contained; -#endif -} - -static inline gboolean -nm_g_hash_table_insert (GHashTable *hash, gpointer key, gpointer value) -{ - /* glib 2.40 added a return value indicating whether the key already existed - * (910191597a6c2e5d5d460e9ce9efb4f47d9cc63c). */ -#if GLIB_CHECK_VERSION(2, 40, 0) - return g_hash_table_insert (hash, key, value); -#else - gboolean contained = g_hash_table_contains (hash, key); - - g_hash_table_insert (hash, key, value); - return !contained; -#endif -} - -static inline gboolean -nm_g_hash_table_add (GHashTable *hash, gpointer key) -{ - /* glib 2.40 added a return value indicating whether the key already existed - * (910191597a6c2e5d5d460e9ce9efb4f47d9cc63c). */ -#if GLIB_CHECK_VERSION(2, 40, 0) - return g_hash_table_add (hash, key); -#else - gboolean contained = g_hash_table_contains (hash, key); - - g_hash_table_add (hash, key); - return !contained; -#endif -} - -/*****************************************************************************/ - -#if !GLIB_CHECK_VERSION(2, 40, 0) || defined (NM_GLIB_COMPAT_H_TEST) -static inline void -_nm_g_ptr_array_insert (GPtrArray *array, - int index_, - gpointer data) -{ - g_return_if_fail (array); - g_return_if_fail (index_ >= -1); - g_return_if_fail (index_ <= (int) array->len); - - g_ptr_array_add (array, data); - - if (index_ != -1 && index_ != (int) (array->len - 1)) { - memmove (&(array->pdata[index_ + 1]), - &(array->pdata[index_]), - (array->len - index_ - 1) * sizeof (gpointer)); - array->pdata[index_] = data; - } -} -#endif - -#if !GLIB_CHECK_VERSION(2, 40, 0) -#define g_ptr_array_insert(array, index, data) G_STMT_START { _nm_g_ptr_array_insert (array, index, data); } G_STMT_END -#else -#define g_ptr_array_insert(array, index, data) \ - G_STMT_START { \ - G_GNUC_BEGIN_IGNORE_DEPRECATIONS \ - g_ptr_array_insert (array, index, data); \ - G_GNUC_END_IGNORE_DEPRECATIONS \ - } G_STMT_END -#endif - -/*****************************************************************************/ - -#if !GLIB_CHECK_VERSION (2, 40, 0) -static inline gboolean -_g_key_file_save_to_file (GKeyFile *key_file, - const char *filename, - GError **error) -{ - char *contents; - gboolean success; - gsize length; - - g_return_val_if_fail (key_file != NULL, FALSE); - g_return_val_if_fail (filename != NULL, FALSE); - g_return_val_if_fail (error == NULL || *error == NULL, FALSE); - - contents = g_key_file_to_data (key_file, &length, NULL); - g_assert (contents != NULL); - - success = g_file_set_contents (filename, contents, length, error); - g_free (contents); - - return success; -} -#define g_key_file_save_to_file(key_file, filename, error) \ - _g_key_file_save_to_file (key_file, filename, error) -#else -#define g_key_file_save_to_file(key_file, filename, error) \ - ({ \ - gboolean _success; \ - \ - G_GNUC_BEGIN_IGNORE_DEPRECATIONS \ - _success = g_key_file_save_to_file (key_file, filename, error); \ - G_GNUC_END_IGNORE_DEPRECATIONS \ - _success; \ - }) -#endif - -/*****************************************************************************/ - -#if GLIB_CHECK_VERSION (2, 36, 0) -#define g_credentials_get_unix_pid(creds, error) \ - ({ \ - G_GNUC_BEGIN_IGNORE_DEPRECATIONS \ - (g_credentials_get_unix_pid) ((creds), (error)); \ - G_GNUC_END_IGNORE_DEPRECATIONS \ - }) -#else -#define g_credentials_get_unix_pid(creds, error) \ - ({ \ - struct ucred *native_creds; \ - \ - native_creds = g_credentials_get_native ((creds), G_CREDENTIALS_TYPE_LINUX_UCRED); \ - g_assert (native_creds); \ - native_creds->pid; \ - }) -#endif - -/*****************************************************************************/ - -#if !GLIB_CHECK_VERSION(2, 40, 0) || defined (NM_GLIB_COMPAT_H_TEST) -static inline gpointer * -_nm_g_hash_table_get_keys_as_array (GHashTable *hash_table, - guint *length) -{ - GHashTableIter iter; - gpointer key, *ret; - guint i = 0; - - g_return_val_if_fail (hash_table, NULL); - - ret = g_new0 (gpointer, g_hash_table_size (hash_table) + 1); - g_hash_table_iter_init (&iter, hash_table); - - while (g_hash_table_iter_next (&iter, &key, NULL)) - ret[i++] = key; - - ret[i] = NULL; - - if (length) - *length = i; - - return ret; -} -#endif -#if !GLIB_CHECK_VERSION(2, 40, 0) -#define g_hash_table_get_keys_as_array(hash_table, length) \ - ({ \ - _nm_g_hash_table_get_keys_as_array (hash_table, length); \ - }) -#else -#define g_hash_table_get_keys_as_array(hash_table, length) \ - ({ \ - G_GNUC_BEGIN_IGNORE_DEPRECATIONS \ - (g_hash_table_get_keys_as_array) ((hash_table), (length)); \ - G_GNUC_END_IGNORE_DEPRECATIONS \ - }) -#endif - -/*****************************************************************************/ - -#ifndef g_info -/* g_info was only added with 2.39.2 */ -#define g_info(...) g_log (G_LOG_DOMAIN, \ - G_LOG_LEVEL_INFO, \ - __VA_ARGS__) -#endif - -/*****************************************************************************/ - -#if !GLIB_CHECK_VERSION(2, 44, 0) -static inline gpointer -g_steal_pointer (gpointer pp) -{ - gpointer *ptr = (gpointer *) pp; - gpointer ref; - - ref = *ptr; - *ptr = NULL; - - return ref; -} -#endif - -#ifdef g_steal_pointer -#undef g_steal_pointer -#endif -#define g_steal_pointer(pp) \ - ((typeof (*(pp))) g_steal_pointer (pp)) - -/*****************************************************************************/ - -static inline gboolean -_nm_g_strv_contains (const char * const *strv, - const char *str) -{ -#if !GLIB_CHECK_VERSION(2, 44, 0) - g_return_val_if_fail (strv != NULL, FALSE); - g_return_val_if_fail (str != NULL, FALSE); - - for (; *strv != NULL; strv++) { - if (g_str_equal (str, *strv)) - return TRUE; - } - - return FALSE; -#else - G_GNUC_BEGIN_IGNORE_DEPRECATIONS - return g_strv_contains (strv, str); - G_GNUC_END_IGNORE_DEPRECATIONS -#endif -} -#define g_strv_contains _nm_g_strv_contains - -/*****************************************************************************/ - -static inline GVariant * -_nm_g_variant_new_take_string (char *string) -{ -#if !GLIB_CHECK_VERSION(2, 36, 0) - GVariant *value; - - g_return_val_if_fail (string != NULL, NULL); - g_return_val_if_fail (g_utf8_validate (string, -1, NULL), NULL); - - value = g_variant_new_string (string); - g_free (string); - return value; -#elif !GLIB_CHECK_VERSION(2, 38, 0) - GVariant *value; - GBytes *bytes; - - g_return_val_if_fail (string != NULL, NULL); - g_return_val_if_fail (g_utf8_validate (string, -1, NULL), NULL); - - bytes = g_bytes_new_take (string, strlen (string) + 1); - value = g_variant_new_from_bytes (G_VARIANT_TYPE_STRING, bytes, TRUE); - g_bytes_unref (bytes); - - return value; -#else - G_GNUC_BEGIN_IGNORE_DEPRECATIONS - return g_variant_new_take_string (string); - G_GNUC_END_IGNORE_DEPRECATIONS -#endif -} -#define g_variant_new_take_string _nm_g_variant_new_take_string - -/*****************************************************************************/ - -#if !GLIB_CHECK_VERSION(2, 38, 0) -_nm_printf (1, 2) -static inline GVariant * -_nm_g_variant_new_printf (const char *format_string, ...) -{ - char *string; - va_list ap; - - g_return_val_if_fail (format_string, NULL); - - va_start (ap, format_string); - string = g_strdup_vprintf (format_string, ap); - va_end (ap); - - return g_variant_new_take_string (string); -} -#define g_variant_new_printf(...) _nm_g_variant_new_printf(__VA_ARGS__) -#else -#define g_variant_new_printf(...) \ - ({ \ - GVariant *_v; \ - \ - G_GNUC_BEGIN_IGNORE_DEPRECATIONS \ - _v = g_variant_new_printf (__VA_ARGS__); \ - G_GNUC_END_IGNORE_DEPRECATIONS \ - _v; \ - }) -#endif - -/*****************************************************************************/ - -#if !GLIB_CHECK_VERSION (2, 56, 0) -#define g_object_ref(Obj) ((typeof(Obj)) g_object_ref (Obj)) -#define g_object_ref_sink(Obj) ((typeof(Obj)) g_object_ref_sink (Obj)) -#endif - -/*****************************************************************************/ - -#ifndef g_autofree -/* we still don't rely on recent glib to provide g_autofree. Hence, we continue - * to use our gs_* free macros that we took from libgsystem. - * - * To ease migration towards g_auto*, add a compat define for g_autofree. */ -#define g_autofree gs_free -#endif - -/*****************************************************************************/ - -#if !GLIB_CHECK_VERSION (2, 47, 1) -/* Older versions of g_value_unset() only allowed to unset a GValue which - * was initialized previously. This was relaxed ([1], [2], [3]). - * - * Our nm_auto_unset_gvalue macro requires to be able to call g_value_unset(). - * Also, it is our general practice to allow for that. Add a compat implementation. - * - * [1] https://gitlab.gnome.org/GNOME/glib/commit/4b2d92a864f1505f1b08eb639d74293fa32681da - * [2] commit "Allow passing unset GValues to g_value_unset()" - * [3] https://bugzilla.gnome.org/show_bug.cgi?id=755766 - */ -static inline void -_nm_g_value_unset (GValue *value) -{ - g_return_if_fail (value); - - if (value->g_type != 0) - g_value_unset (value); -} -#define g_value_unset _nm_g_value_unset -#endif - -/*****************************************************************************/ - -#endif /* __NM_GLIB_H__ */ diff --git a/shared/nm-utils/nm-hash-utils.c b/shared/nm-utils/nm-hash-utils.c deleted file mode 100644 index 6e728e6b..00000000 --- a/shared/nm-utils/nm-hash-utils.c +++ /dev/null @@ -1,196 +0,0 @@ -/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ -/* NetworkManager -- Network link manager - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the - * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, - * Boston, MA 02110-1301 USA. - * - * (C) Copyright 2017 Red Hat, Inc. - */ - -#include "nm-default.h" - -#include "nm-hash-utils.h" - -#include - -#include "nm-shared-utils.h" -#include "nm-random-utils.h" - -/*****************************************************************************/ - -#define HASH_KEY_SIZE 16u -#define HASH_KEY_SIZE_GUINT ((HASH_KEY_SIZE + sizeof (guint) - 1) / sizeof (guint)) - -G_STATIC_ASSERT (sizeof (guint) * HASH_KEY_SIZE_GUINT >= HASH_KEY_SIZE); - -static const guint8 *volatile global_seed = NULL; - -static const guint8 * -_get_hash_key_init (void) -{ - static gsize g_lock; - /* the returned hash is aligned to guin64, hence, it is safe - * to use it as guint* or guint64* pointer. */ - static union { - guint8 v8[HASH_KEY_SIZE]; - } g_arr _nm_alignas (guint64); - const guint8 *g; - union { - guint8 v8[HASH_KEY_SIZE]; - guint vuint; - } t_arr; - -again: - g = g_atomic_pointer_get (&global_seed); - if (G_LIKELY (g != NULL)) { - nm_assert (g == g_arr.v8); - return g; - } - - { - CSipHash siph_state; - uint64_t h; - - /* initialize a random key in t_arr. */ - - nm_utils_random_bytes (&t_arr, sizeof (t_arr)); - - /* use siphash() of the key-size, to mangle the first guint. Otherwise, - * the first guint has only the entropy that nm_utils_random_bytes() - * generated for the first 4 bytes and relies on a good random generator. - * - * The first int is especially interesting for nm_hash_static() below, and we - * want to have it all the entropy of t_arr. */ - c_siphash_init (&siph_state, t_arr.v8); - c_siphash_append (&siph_state, (const guint8 *) &t_arr, sizeof (t_arr)); - h = c_siphash_finalize (&siph_state); - if (sizeof (guint) < sizeof (h)) - t_arr.vuint = t_arr.vuint ^ ((guint) (h & 0xFFFFFFFFu)) ^ ((guint) (h >> 32)); - else - t_arr.vuint = t_arr.vuint ^ ((guint) (h & 0xFFFFFFFFu)); - } - - if (!g_once_init_enter (&g_lock)) { - /* lost a race. The random key is already initialized. */ - goto again; - } - - memcpy (g_arr.v8, t_arr.v8, HASH_KEY_SIZE); - g = g_arr.v8; - g_atomic_pointer_set (&global_seed, g); - g_once_init_leave (&g_lock, 1); - return g; -} - -#define _get_hash_key() \ - ({ \ - const guint8 *_g; \ - \ - _g = g_atomic_pointer_get (&global_seed); \ - if (G_UNLIKELY (!_g)) \ - _g = _get_hash_key_init (); \ - _g; \ - }) - -guint -nm_hash_static (guint static_seed) -{ - /* note that we only xor the static_seed with the key. - * We don't use siphash, which would mix the bits better. - * Note that this doesn't matter, because static_seed is not - * supposed to be a value that you are hashing (for that, use - * full siphash). - * Instead, different callers may set a different static_seed - * so that nm_hash_str(NULL) != nm_hash_ptr(NULL). - * - * Also, ensure that we don't return zero. - */ - return ((*((const guint *) _get_hash_key ())) ^ static_seed) - ?: static_seed ?: 3679500967u; -} - -void -nm_hash_siphash42_init (CSipHash *h, guint static_seed) -{ - const guint8 *g; - guint seed[HASH_KEY_SIZE_GUINT]; - - nm_assert (h); - - g = _get_hash_key (); - memcpy (seed, g, HASH_KEY_SIZE); - seed[0] ^= static_seed; - c_siphash_init (h, (const guint8 *) seed); -} - -guint -nm_hash_str (const char *str) -{ - NMHashState h; - - if (!str) - return nm_hash_static (1867854211u); - nm_hash_init (&h, 1867854211u); - nm_hash_update_str (&h, str); - return nm_hash_complete (&h); -} - -guint -nm_str_hash (gconstpointer str) -{ - return nm_hash_str (str); -} - -guint -nm_hash_ptr (gconstpointer ptr) -{ - NMHashState h; - - if (!ptr) - return nm_hash_static (2907677551u); - nm_hash_init (&h, 2907677551u); - nm_hash_update (&h, &ptr, sizeof (ptr)); - return nm_hash_complete (&h); -} - -guint -nm_direct_hash (gconstpointer ptr) -{ - return nm_hash_ptr (ptr); -} - -/*****************************************************************************/ - -guint -nm_pstr_hash (gconstpointer p) -{ - const char *const*s = p; - - if (!s) - return nm_hash_static (101061439u); - return nm_hash_str (*s); -} - -gboolean -nm_pstr_equal (gconstpointer a, gconstpointer b) -{ - const char *const*s1 = a; - const char *const*s2 = b; - - return (s1 == s2) - || ( s1 - && s2 - && nm_streq0 (*s1, *s2)); -} diff --git a/shared/nm-utils/nm-hash-utils.h b/shared/nm-utils/nm-hash-utils.h deleted file mode 100644 index 1a1e44f5..00000000 --- a/shared/nm-utils/nm-hash-utils.h +++ /dev/null @@ -1,290 +0,0 @@ -/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ -/* NetworkManager -- Network link manager - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the - * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, - * Boston, MA 02110-1301 USA. - * - * (C) Copyright 2017 Red Hat, Inc. - */ - -#ifndef __NM_HASH_UTILS_H__ -#define __NM_HASH_UTILS_H__ - -#include "c-siphash/src/c-siphash.h" -#include "nm-macros-internal.h" - -/*****************************************************************************/ - -void nm_hash_siphash42_init (CSipHash *h, guint static_seed); - -/* Siphash24 of binary buffer @arr and @len, using the randomized seed from - * other NMHash functions. - * - * Note, that this is guaranteed to use siphash42 under the hood (contrary to - * all other NMHash API, which leave this undefined). That matters at the point, - * where the caller needs to be sure that a reasonably strong hasing algorithm - * is used. (Yes, NMHash is all about siphash24, but otherwise that is not promised - * anywhere). - * - * Another difference is, that this returns guint64 (not guint like other NMHash functions). - * - * Another difference is, that this may also return zero (not like nm_hash_complete()). - * - * Then, why not use c_siphash_hash() directly? Because this also uses the randomized, - * per-run hash-seed like nm_hash_init(). So, you get siphash24 with a random - * seed (which is cached for the current run of the program). - */ -static inline guint64 -nm_hash_siphash42 (guint static_seed, const void *ptr, gsize n) -{ - CSipHash h; - - nm_hash_siphash42_init (&h, static_seed); - c_siphash_append (&h, ptr, n); - return c_siphash_finalize (&h); -} - -/*****************************************************************************/ - -struct _NMHashState { - CSipHash _state; -}; - -typedef struct _NMHashState NMHashState; - -guint nm_hash_static (guint static_seed); - -static inline void -nm_hash_init (NMHashState *state, guint static_seed) -{ - nm_assert (state); - - nm_hash_siphash42_init (&state->_state, static_seed); -} - -static inline guint64 -nm_hash_complete_u64 (NMHashState *state) -{ - nm_assert (state); - - /* this returns the native u64 hash value. Note that this differs - * from nm_hash_complete() in two ways: - * - * - the type, guint64 vs. guint. - * - nm_hash_complete() never returns zero. */ - return c_siphash_finalize (&state->_state); -} - -static inline guint -nm_hash_complete (NMHashState *state) -{ - guint64 h; - - h = nm_hash_complete_u64 (state); - - /* we don't ever want to return a zero hash. - * - * NMPObject requires that in _idx_obj_part(), and it's just a good idea. */ - return (((guint) (h >> 32)) ^ ((guint) h)) ?: 1396707757u; -} - -static inline void -nm_hash_update (NMHashState *state, const void *ptr, gsize n) -{ - nm_assert (state); - nm_assert (ptr); - nm_assert (n > 0); - - /* Note: the data passed in here might be sensitive data (secrets), - * that we should nm_explicty_zero() afterwards. However, since - * we are using siphash24 with a random key, that is not really - * necessary. Something to keep in mind, if we ever move away from - * this hash implementation. */ - c_siphash_append (&state->_state, ptr, n); -} - -#define nm_hash_update_val(state, val) \ - G_STMT_START { \ - typeof (val) _val = (val); \ - \ - nm_hash_update ((state), &_val, sizeof (_val)); \ - } G_STMT_END - -#define nm_hash_update_valp(state, val) \ - nm_hash_update ((state), (val), sizeof (*(val))) \ - -static inline void -nm_hash_update_bool (NMHashState *state, bool val) -{ - nm_hash_update (state, &val, sizeof (val)); -} - -#define _NM_HASH_COMBINE_BOOLS_x_1( t, y) ((y) ? ((t) (1ull << 0)) : ((t) 0ull)) -#define _NM_HASH_COMBINE_BOOLS_x_2( t, y, ...) ((y) ? ((t) (1ull << 1)) : ((t) 0ull)) | _NM_HASH_COMBINE_BOOLS_x_1 (t, __VA_ARGS__) -#define _NM_HASH_COMBINE_BOOLS_x_3( t, y, ...) ((y) ? ((t) (1ull << 2)) : ((t) 0ull)) | _NM_HASH_COMBINE_BOOLS_x_2 (t, __VA_ARGS__) -#define _NM_HASH_COMBINE_BOOLS_x_4( t, y, ...) ((y) ? ((t) (1ull << 3)) : ((t) 0ull)) | _NM_HASH_COMBINE_BOOLS_x_3 (t, __VA_ARGS__) -#define _NM_HASH_COMBINE_BOOLS_x_5( t, y, ...) ((y) ? ((t) (1ull << 4)) : ((t) 0ull)) | _NM_HASH_COMBINE_BOOLS_x_4 (t, __VA_ARGS__) -#define _NM_HASH_COMBINE_BOOLS_x_6( t, y, ...) ((y) ? ((t) (1ull << 5)) : ((t) 0ull)) | _NM_HASH_COMBINE_BOOLS_x_5 (t, __VA_ARGS__) -#define _NM_HASH_COMBINE_BOOLS_x_7( t, y, ...) ((y) ? ((t) (1ull << 6)) : ((t) 0ull)) | _NM_HASH_COMBINE_BOOLS_x_6 (t, __VA_ARGS__) -#define _NM_HASH_COMBINE_BOOLS_x_8( t, y, ...) ((y) ? ((t) (1ull << 7)) : ((t) 0ull)) | _NM_HASH_COMBINE_BOOLS_x_7 (t, __VA_ARGS__) -#define _NM_HASH_COMBINE_BOOLS_x_9( t, y, ...) ((y) ? ((t) (1ull << 8)) : ((t) 0ull)) | (G_STATIC_ASSERT_EXPR (sizeof (t) >= 2), (_NM_HASH_COMBINE_BOOLS_x_8 (t, __VA_ARGS__))) -#define _NM_HASH_COMBINE_BOOLS_x_10(t, y, ...) ((y) ? ((t) (1ull << 9)) : ((t) 0ull)) | _NM_HASH_COMBINE_BOOLS_x_9 (t, __VA_ARGS__) -#define _NM_HASH_COMBINE_BOOLS_x_11(t, y, ...) ((y) ? ((t) (1ull << 10)) : ((t) 0ull)) | _NM_HASH_COMBINE_BOOLS_x_10 (t, __VA_ARGS__) -#define _NM_HASH_COMBINE_BOOLS_n2(t, n, ...) _NM_HASH_COMBINE_BOOLS_x_##n (t, __VA_ARGS__) -#define _NM_HASH_COMBINE_BOOLS_n(t, n, ...) _NM_HASH_COMBINE_BOOLS_n2(t, n, __VA_ARGS__) - -#define NM_HASH_COMBINE_BOOLS(type, ...) ((type) (_NM_HASH_COMBINE_BOOLS_n(type, NM_NARG (__VA_ARGS__), __VA_ARGS__))) - -#define nm_hash_update_bools(state, ...) \ - nm_hash_update_val (state, NM_HASH_COMBINE_BOOLS (guint8, __VA_ARGS__)) - -#define _NM_HASH_COMBINE_VALS_typ_x_1( y) typeof (y) _v1; -#define _NM_HASH_COMBINE_VALS_typ_x_2( y, ...) typeof (y) _v2; _NM_HASH_COMBINE_VALS_typ_x_1 (__VA_ARGS__) -#define _NM_HASH_COMBINE_VALS_typ_x_3( y, ...) typeof (y) _v3; _NM_HASH_COMBINE_VALS_typ_x_2 (__VA_ARGS__) -#define _NM_HASH_COMBINE_VALS_typ_x_4( y, ...) typeof (y) _v4; _NM_HASH_COMBINE_VALS_typ_x_3 (__VA_ARGS__) -#define _NM_HASH_COMBINE_VALS_typ_x_5( y, ...) typeof (y) _v5; _NM_HASH_COMBINE_VALS_typ_x_4 (__VA_ARGS__) -#define _NM_HASH_COMBINE_VALS_typ_x_6( y, ...) typeof (y) _v6; _NM_HASH_COMBINE_VALS_typ_x_5 (__VA_ARGS__) -#define _NM_HASH_COMBINE_VALS_typ_x_7( y, ...) typeof (y) _v7; _NM_HASH_COMBINE_VALS_typ_x_6 (__VA_ARGS__) -#define _NM_HASH_COMBINE_VALS_typ_x_8( y, ...) typeof (y) _v8; _NM_HASH_COMBINE_VALS_typ_x_7 (__VA_ARGS__) -#define _NM_HASH_COMBINE_VALS_typ_x_9( y, ...) typeof (y) _v9; _NM_HASH_COMBINE_VALS_typ_x_8 (__VA_ARGS__) -#define _NM_HASH_COMBINE_VALS_typ_x_10(y, ...) typeof (y) _v10; _NM_HASH_COMBINE_VALS_typ_x_9 (__VA_ARGS__) -#define _NM_HASH_COMBINE_VALS_typ_x_11(y, ...) typeof (y) _v11; _NM_HASH_COMBINE_VALS_typ_x_10 (__VA_ARGS__) -#define _NM_HASH_COMBINE_VALS_typ_x_12(y, ...) typeof (y) _v12; _NM_HASH_COMBINE_VALS_typ_x_11 (__VA_ARGS__) -#define _NM_HASH_COMBINE_VALS_typ_x_13(y, ...) typeof (y) _v13; _NM_HASH_COMBINE_VALS_typ_x_12 (__VA_ARGS__) -#define _NM_HASH_COMBINE_VALS_typ_x_14(y, ...) typeof (y) _v14; _NM_HASH_COMBINE_VALS_typ_x_13 (__VA_ARGS__) -#define _NM_HASH_COMBINE_VALS_typ_x_15(y, ...) typeof (y) _v15; _NM_HASH_COMBINE_VALS_typ_x_14 (__VA_ARGS__) -#define _NM_HASH_COMBINE_VALS_typ_x_16(y, ...) typeof (y) _v16; _NM_HASH_COMBINE_VALS_typ_x_15 (__VA_ARGS__) -#define _NM_HASH_COMBINE_VALS_typ_x_17(y, ...) typeof (y) _v17; _NM_HASH_COMBINE_VALS_typ_x_16 (__VA_ARGS__) -#define _NM_HASH_COMBINE_VALS_typ_x_18(y, ...) typeof (y) _v18; _NM_HASH_COMBINE_VALS_typ_x_17 (__VA_ARGS__) -#define _NM_HASH_COMBINE_VALS_typ_x_19(y, ...) typeof (y) _v19; _NM_HASH_COMBINE_VALS_typ_x_18 (__VA_ARGS__) -#define _NM_HASH_COMBINE_VALS_typ_x_20(y, ...) typeof (y) _v20; _NM_HASH_COMBINE_VALS_typ_x_19 (__VA_ARGS__) -#define _NM_HASH_COMBINE_VALS_typ_n2(n, ...) _NM_HASH_COMBINE_VALS_typ_x_##n (__VA_ARGS__) -#define _NM_HASH_COMBINE_VALS_typ_n(n, ...) _NM_HASH_COMBINE_VALS_typ_n2(n, __VA_ARGS__) - -#define _NM_HASH_COMBINE_VALS_val_x_1( y) ._v1 = (y), -#define _NM_HASH_COMBINE_VALS_val_x_2( y, ...) ._v2 = (y), _NM_HASH_COMBINE_VALS_val_x_1 (__VA_ARGS__) -#define _NM_HASH_COMBINE_VALS_val_x_3( y, ...) ._v3 = (y), _NM_HASH_COMBINE_VALS_val_x_2 (__VA_ARGS__) -#define _NM_HASH_COMBINE_VALS_val_x_4( y, ...) ._v4 = (y), _NM_HASH_COMBINE_VALS_val_x_3 (__VA_ARGS__) -#define _NM_HASH_COMBINE_VALS_val_x_5( y, ...) ._v5 = (y), _NM_HASH_COMBINE_VALS_val_x_4 (__VA_ARGS__) -#define _NM_HASH_COMBINE_VALS_val_x_6( y, ...) ._v6 = (y), _NM_HASH_COMBINE_VALS_val_x_5 (__VA_ARGS__) -#define _NM_HASH_COMBINE_VALS_val_x_7( y, ...) ._v7 = (y), _NM_HASH_COMBINE_VALS_val_x_6 (__VA_ARGS__) -#define _NM_HASH_COMBINE_VALS_val_x_8( y, ...) ._v8 = (y), _NM_HASH_COMBINE_VALS_val_x_7 (__VA_ARGS__) -#define _NM_HASH_COMBINE_VALS_val_x_9( y, ...) ._v9 = (y), _NM_HASH_COMBINE_VALS_val_x_8 (__VA_ARGS__) -#define _NM_HASH_COMBINE_VALS_val_x_10(y, ...) ._v10 = (y), _NM_HASH_COMBINE_VALS_val_x_9 (__VA_ARGS__) -#define _NM_HASH_COMBINE_VALS_val_x_11(y, ...) ._v11 = (y), _NM_HASH_COMBINE_VALS_val_x_10 (__VA_ARGS__) -#define _NM_HASH_COMBINE_VALS_val_x_12(y, ...) ._v12 = (y), _NM_HASH_COMBINE_VALS_val_x_11 (__VA_ARGS__) -#define _NM_HASH_COMBINE_VALS_val_x_13(y, ...) ._v13 = (y), _NM_HASH_COMBINE_VALS_val_x_12 (__VA_ARGS__) -#define _NM_HASH_COMBINE_VALS_val_x_14(y, ...) ._v14 = (y), _NM_HASH_COMBINE_VALS_val_x_13 (__VA_ARGS__) -#define _NM_HASH_COMBINE_VALS_val_x_15(y, ...) ._v15 = (y), _NM_HASH_COMBINE_VALS_val_x_14 (__VA_ARGS__) -#define _NM_HASH_COMBINE_VALS_val_x_16(y, ...) ._v16 = (y), _NM_HASH_COMBINE_VALS_val_x_15 (__VA_ARGS__) -#define _NM_HASH_COMBINE_VALS_val_x_17(y, ...) ._v17 = (y), _NM_HASH_COMBINE_VALS_val_x_16 (__VA_ARGS__) -#define _NM_HASH_COMBINE_VALS_val_x_18(y, ...) ._v18 = (y), _NM_HASH_COMBINE_VALS_val_x_17 (__VA_ARGS__) -#define _NM_HASH_COMBINE_VALS_val_x_19(y, ...) ._v19 = (y), _NM_HASH_COMBINE_VALS_val_x_18 (__VA_ARGS__) -#define _NM_HASH_COMBINE_VALS_val_x_20(y, ...) ._v20 = (y), _NM_HASH_COMBINE_VALS_val_x_19 (__VA_ARGS__) -#define _NM_HASH_COMBINE_VALS_val_n2(n, ...) _NM_HASH_COMBINE_VALS_val_x_##n (__VA_ARGS__) -#define _NM_HASH_COMBINE_VALS_val_n(n, ...) _NM_HASH_COMBINE_VALS_val_n2(n, __VA_ARGS__) - -/* NM_HASH_COMBINE_VALS() is faster then nm_hash_update_val() as it combines multiple - * calls to nm_hash_update() using a packed structure. */ -#define NM_HASH_COMBINE_VALS(var, ...) \ - const struct _nm_packed { \ - _NM_HASH_COMBINE_VALS_typ_n (NM_NARG (__VA_ARGS__), __VA_ARGS__) \ - } var _nm_alignas (guint64) = { \ - _NM_HASH_COMBINE_VALS_val_n (NM_NARG (__VA_ARGS__), __VA_ARGS__) \ - } - -/* nm_hash_update_vals() is faster then nm_hash_update_val() as it combines multiple - * calls to nm_hash_update() using a packed structure. */ -#define nm_hash_update_vals(state, ...) \ - G_STMT_START { \ - NM_HASH_COMBINE_VALS (_val, __VA_ARGS__); \ - \ - nm_hash_update ((state), &_val, sizeof (_val)); \ - } G_STMT_END - -static inline void -nm_hash_update_mem (NMHashState *state, const void *ptr, gsize n) -{ - /* This also hashes the length of the data. That means, - * hashing two consecutive binary fields (of arbitrary - * length), will hash differently. That is, - * [[1,1], []] differs from [[1],[1]]. - * - * If you have a constant length (sizeof), use nm_hash_update() - * instead. */ - nm_hash_update (state, &n, sizeof (n)); - if (n > 0) - nm_hash_update (state, ptr, n); -} - -static inline void -nm_hash_update_str0 (NMHashState *state, const char *str) -{ - if (str) - nm_hash_update_mem (state, str, strlen (str)); - else { - gsize n = G_MAXSIZE; - - nm_hash_update (state, &n, sizeof (n)); - } -} - -static inline void -nm_hash_update_str (NMHashState *state, const char *str) -{ - nm_assert (str); - nm_hash_update (state, str, strlen (str) + 1); -} - -#if _NM_CC_SUPPORT_GENERIC -/* Like nm_hash_update_str(), but restricted to arrays only. nm_hash_update_str() only works - * with a @str argument that cannot be NULL. If you have a string pointer, that is never NULL, use - * nm_hash_update() instead. */ -#define nm_hash_update_strarr(state, str) \ - (_Generic (&(str), \ - const char (*) [sizeof (str)]: nm_hash_update_str ((state), (str)), \ - char (*) [sizeof (str)]: nm_hash_update_str ((state), (str))) \ - ) -#else -#define nm_hash_update_strarr(state, str) nm_hash_update_str ((state), (str)) -#endif - -guint nm_hash_ptr (gconstpointer ptr); -guint nm_direct_hash (gconstpointer str); - -guint nm_hash_str (const char *str); -guint nm_str_hash (gconstpointer str); - -#define nm_hash_val(static_seed, val) \ - ({ \ - NMHashState _h; \ - \ - nm_hash_init (&_h, (static_seed)); \ - nm_hash_update_val (&_h, (val)); \ - nm_hash_complete (&_h); \ - }) - -/*****************************************************************************/ - -/* nm_pstr_*() are for hashing keys that are pointers to strings, - * that is, "const char *const*" types, using strcmp(). */ - -guint nm_pstr_hash (gconstpointer p); - -gboolean nm_pstr_equal (gconstpointer a, gconstpointer b); - -/*****************************************************************************/ - -#endif /* __NM_HASH_UTILS_H__ */ diff --git a/shared/nm-utils/nm-io-utils.c b/shared/nm-utils/nm-io-utils.c deleted file mode 100644 index 51312748..00000000 --- a/shared/nm-utils/nm-io-utils.c +++ /dev/null @@ -1,439 +0,0 @@ -/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ -/* NetworkManager -- Network link manager - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the - * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, - * Boston, MA 02110-1301 USA. - * - * (C) Copyright 2018 Red Hat, Inc. - */ - -#include "nm-default.h" - -#include "nm-io-utils.h" - -#include -#include -#include - -#include "nm-shared-utils.h" -#include "nm-secret-utils.h" -#include "nm-errno.h" - -/*****************************************************************************/ - -_nm_printf (3, 4) -static int -_get_contents_error (GError **error, int errsv, const char *format, ...) -{ - nm_assert (NM_ERRNO_NATIVE (errsv)); - - if (error) { - gs_free char *msg = NULL; - va_list args; - char bstrerr[NM_STRERROR_BUFSIZE]; - - va_start (args, format); - msg = g_strdup_vprintf (format, args); - va_end (args); - g_set_error (error, - G_FILE_ERROR, - g_file_error_from_errno (errsv), - "%s: %s", - msg, - nm_strerror_native_r (errsv, bstrerr, sizeof (bstrerr))); - } - return -errsv; -} -#define _get_contents_error_errno(error, ...) \ - ({ \ - int _errsv = (errno); \ - \ - _get_contents_error (error, _errsv, __VA_ARGS__); \ - }) - -static char * -_mem_realloc (char *old, gboolean do_bzero_mem, gsize cur_len, gsize new_len) -{ - char *new; - - /* re-allocating to zero bytes is an odd case. We don't need it - * and it's not supported. */ - nm_assert (new_len > 0); - - /* regardless of success/failure, @old will always be freed/consumed. */ - - if (do_bzero_mem && cur_len > 0) { - new = g_try_malloc (new_len); - if (new) - memcpy (new, old, NM_MIN (cur_len, new_len)); - nm_explicit_bzero (old, cur_len); - g_free (old); - } else { - new = g_try_realloc (old, new_len); - if (!new) - g_free (old); - } - - return new; -} - -/** - * nm_utils_fd_get_contents: - * @fd: open file descriptor to read. The fd will not be closed, - * but don't rely on its state afterwards. - * @close_fd: if %TRUE, @fd will be closed by the function. - * Passing %TRUE here might safe a syscall for dup(). - * @max_length: allocate at most @max_length bytes. If the - * file is larger, reading will fail. Set to zero to use - * a very large default. - * WARNING: @max_length is here to avoid a crash for huge/unlimited files. - * For example, stat(/sys/class/net/enp0s25/ifindex) gives a filesize of - * 4K, although the actual real is small. @max_length is the memory - * allocated in the process of reading the file, thus it must be at least - * the size reported by fstat. - * If you set it to 1K, read will fail because fstat() claims the - * file is larger. - * @flags: %NMUtilsFileGetContentsFlags for reading the file. - * @contents: the output buffer with the file read. It is always - * NUL terminated. The buffer is at most @max_length long, including - * the NUL byte. That is, it reads only files up to a length of - * @max_length - 1 bytes. - * @length: optional output argument of the read file size. - * - * A reimplementation of g_file_get_contents() with a few differences: - * - accepts an open fd, instead of a path name. This allows you to - * use openat(). - * - limits the maximum filesize to max_length. - * - * Returns: a negative error code on failure. - */ -int -nm_utils_fd_get_contents (int fd, - gboolean close_fd, - gsize max_length, - NMUtilsFileGetContentsFlags flags, - char **contents, - gsize *length, - GError **error) -{ - nm_auto_close int fd_keeper = close_fd ? fd : -1; - struct stat stat_buf; - gs_free char *str = NULL; - const bool do_bzero_mem = NM_FLAGS_HAS (flags, NM_UTILS_FILE_GET_CONTENTS_FLAG_SECRET); - int errsv; - - g_return_val_if_fail (fd >= 0, -EINVAL); - g_return_val_if_fail (contents, -EINVAL); - g_return_val_if_fail (!error || !*error, -EINVAL); - - if (fstat (fd, &stat_buf) < 0) - return _get_contents_error_errno (error, "failure during fstat"); - - if (!max_length) { - /* default to a very large size, but not extreme */ - max_length = 2 * 1024 * 1024; - } - - if ( stat_buf.st_size > 0 - && S_ISREG (stat_buf.st_mode)) { - const gsize n_stat = stat_buf.st_size; - ssize_t n_read; - - if (n_stat > max_length - 1) - return _get_contents_error (error, EMSGSIZE, "file too large (%zu+1 bytes with maximum %zu bytes)", n_stat, max_length); - - str = g_try_malloc (n_stat + 1); - if (!str) - return _get_contents_error (error, ENOMEM, "failure to allocate buffer of %zu+1 bytes", n_stat); - - n_read = nm_utils_fd_read_loop (fd, str, n_stat, TRUE); - if (n_read < 0) { - if (do_bzero_mem) - nm_explicit_bzero (str, n_stat); - return _get_contents_error (error, -n_read, "error reading %zu bytes from file descriptor", n_stat); - } - str[n_read] = '\0'; - - if (n_read < n_stat) { - if (!(str = _mem_realloc (str, do_bzero_mem, n_stat + 1, n_read + 1))) - return _get_contents_error (error, ENOMEM, "failure to reallocate buffer with %zu bytes", n_read + 1); - } - NM_SET_OUT (length, n_read); - } else { - nm_auto_fclose FILE *f = NULL; - char buf[4096]; - gsize n_have, n_alloc; - int fd2; - - if (fd_keeper >= 0) - fd2 = nm_steal_fd (&fd_keeper); - else { - fd2 = fcntl (fd, F_DUPFD_CLOEXEC, 0); - if (fd2 < 0) - return _get_contents_error_errno (error, "error during dup"); - } - - if (!(f = fdopen (fd2, "r"))) { - errsv = errno; - nm_close (fd2); - return _get_contents_error (error, errsv, "failure during fdopen"); - } - - n_have = 0; - n_alloc = 0; - - while (!feof (f)) { - gsize n_read; - - n_read = fread (buf, 1, sizeof (buf), f); - errsv = errno; - if (ferror (f)) { - if (do_bzero_mem) - nm_explicit_bzero (buf, sizeof (buf)); - return _get_contents_error (error, errsv, "error during fread"); - } - - if ( n_have > G_MAXSIZE - 1 - n_read - || n_have + n_read + 1 > max_length) { - if (do_bzero_mem) - nm_explicit_bzero (buf, sizeof (buf)); - return _get_contents_error (error, EMSGSIZE, "file stream too large (%zu+1 bytes with maximum %zu bytes)", - (n_have > G_MAXSIZE - 1 - n_read) ? G_MAXSIZE : n_have + n_read, - max_length); - } - - if (n_have + n_read + 1 >= n_alloc) { - gsize old_n_alloc = n_alloc; - - if (n_alloc != 0) { - nm_assert (str); - if (n_alloc >= max_length / 2) - n_alloc = max_length; - else - n_alloc *= 2; - } else { - nm_assert (!str); - n_alloc = NM_MIN (n_read + 1, sizeof (buf)); - } - - if (!(str = _mem_realloc (str, do_bzero_mem, old_n_alloc, n_alloc))) { - if (do_bzero_mem) - nm_explicit_bzero (buf, sizeof (buf)); - return _get_contents_error (error, ENOMEM, "failure to allocate buffer of %zu bytes", n_alloc); - } - } - - memcpy (str + n_have, buf, n_read); - n_have += n_read; - } - - if (do_bzero_mem) - nm_explicit_bzero (buf, sizeof (buf)); - - if (n_alloc == 0) - str = g_new0 (char, 1); - else { - str[n_have] = '\0'; - if (n_have + 1 < n_alloc) { - if (!(str = _mem_realloc (str, do_bzero_mem, n_alloc, n_have + 1))) - return _get_contents_error (error, ENOMEM, "failure to truncate buffer to %zu bytes", n_have + 1); - } - } - - NM_SET_OUT (length, n_have); - } - - *contents = g_steal_pointer (&str); - return 0; -} - -/** - * nm_utils_file_get_contents: - * @dirfd: optional file descriptor to use openat(). If negative, use plain open(). - * @filename: the filename to open. Possibly relative to @dirfd. - * @max_length: allocate at most @max_length bytes. - * WARNING: see nm_utils_fd_get_contents() hint about @max_length. - * @flags: %NMUtilsFileGetContentsFlags for reading the file. - * @contents: the output buffer with the file read. It is always - * NUL terminated. The buffer is at most @max_length long, including - * the NUL byte. That is, it reads only files up to a length of - * @max_length - 1 bytes. - * @length: optional output argument of the read file size. - * - * A reimplementation of g_file_get_contents() with a few differences: - * - accepts an @dirfd to open @filename relative to that path via openat(). - * - limits the maximum filesize to max_length. - * - uses O_CLOEXEC on internal file descriptor - * - * Returns: a negative error code on failure. - */ -int -nm_utils_file_get_contents (int dirfd, - const char *filename, - gsize max_length, - NMUtilsFileGetContentsFlags flags, - char **contents, - gsize *length, - GError **error) -{ - int fd; - int errsv; - char bstrerr[NM_STRERROR_BUFSIZE]; - - g_return_val_if_fail (filename && filename[0], -EINVAL); - - if (dirfd >= 0) { - fd = openat (dirfd, filename, O_RDONLY | O_CLOEXEC); - if (fd < 0) { - errsv = errno; - - g_set_error (error, - G_FILE_ERROR, - g_file_error_from_errno (errsv), - "Failed to open file \"%s\" with openat: %s", - filename, - nm_strerror_native_r (errsv, bstrerr, sizeof (bstrerr))); - return -NM_ERRNO_NATIVE (errsv); - } - } else { - fd = open (filename, O_RDONLY | O_CLOEXEC); - if (fd < 0) { - errsv = errno; - - g_set_error (error, - G_FILE_ERROR, - g_file_error_from_errno (errsv), - "Failed to open file \"%s\": %s", - filename, - nm_strerror_native_r (errsv, bstrerr, sizeof (bstrerr))); - return -NM_ERRNO_NATIVE (errsv); - } - } - return nm_utils_fd_get_contents (fd, - TRUE, - max_length, - flags, - contents, - length, - error); -} - -/*****************************************************************************/ - -/* - * Copied from GLib's g_file_set_contents() et al., but allows - * specifying a mode for the new file. - */ -gboolean -nm_utils_file_set_contents (const char *filename, - const char *contents, - gssize length, - mode_t mode, - GError **error) -{ - gs_free char *tmp_name = NULL; - struct stat statbuf; - int errsv; - gssize s; - int fd; - char bstrerr[NM_STRERROR_BUFSIZE]; - - g_return_val_if_fail (filename, FALSE); - g_return_val_if_fail (contents || !length, FALSE); - g_return_val_if_fail (!error || !*error, FALSE); - g_return_val_if_fail (length >= -1, FALSE); - - if (length == -1) - length = strlen (contents); - - tmp_name = g_strdup_printf ("%s.XXXXXX", filename); - fd = g_mkstemp_full (tmp_name, O_RDWR | O_CLOEXEC, mode); - if (fd < 0) { - errsv = errno; - g_set_error (error, - G_FILE_ERROR, - g_file_error_from_errno (errsv), - "failed to create file %s: %s", - tmp_name, - nm_strerror_native_r (errsv, bstrerr, sizeof (bstrerr))); - return FALSE; - } - - while (length > 0) { - s = write (fd, contents, length); - if (s < 0) { - errsv = errno; - if (errsv == EINTR) - continue; - - nm_close (fd); - unlink (tmp_name); - - g_set_error (error, - G_FILE_ERROR, - g_file_error_from_errno (errsv), - "failed to write to file %s: %s", - tmp_name, - nm_strerror_native_r (errsv, bstrerr, sizeof (bstrerr))); - return FALSE; - } - - g_assert (s <= length); - - contents += s; - length -= s; - } - - /* If the final destination exists and is > 0 bytes, we want to sync the - * newly written file to ensure the data is on disk when we rename over - * the destination. Otherwise if we get a system crash we can lose both - * the new and the old file on some filesystems. (I.E. those that don't - * guarantee the data is written to the disk before the metadata.) - */ - if ( lstat (filename, &statbuf) == 0 - && statbuf.st_size > 0) { - if (fsync (fd) != 0) { - errsv = errno; - - nm_close (fd); - unlink (tmp_name); - - g_set_error (error, - G_FILE_ERROR, - g_file_error_from_errno (errsv), - "failed to fsync %s: %s", - tmp_name, - nm_strerror_native_r (errsv, bstrerr, sizeof (bstrerr))); - return FALSE; - } - } - - nm_close (fd); - - if (rename (tmp_name, filename)) { - errsv = errno; - unlink (tmp_name); - g_set_error (error, - G_FILE_ERROR, - g_file_error_from_errno (errsv), - "failed to rename %s to %s: %s", - tmp_name, - filename, - nm_strerror_native_r (errsv, bstrerr, sizeof (bstrerr))); - return FALSE; - } - - return TRUE; -} diff --git a/shared/nm-utils/nm-io-utils.h b/shared/nm-utils/nm-io-utils.h deleted file mode 100644 index dc72a2a6..00000000 --- a/shared/nm-utils/nm-io-utils.h +++ /dev/null @@ -1,63 +0,0 @@ -/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ -/* NetworkManager -- Network link manager - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the - * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, - * Boston, MA 02110-1301 USA. - * - * (C) Copyright 2018 Red Hat, Inc. - */ - -#ifndef __NM_IO_UTILS_H__ -#define __NM_IO_UTILS_H__ - -#include "nm-macros-internal.h" - -/*****************************************************************************/ - -/** - * NMUtilsFileGetContentsFlags: - * @NM_UTILS_FILE_GET_CONTENTS_FLAG_NONE: no flag - * @NM_UTILS_FILE_GET_CONTENTS_FLAG_SECRET: if present, ensure that no - * data is left in memory. Essentially, it means to call explicity_bzero() - * to not leave key material on the heap (when reading secrets). - */ -typedef enum { - NM_UTILS_FILE_GET_CONTENTS_FLAG_NONE = 0, - NM_UTILS_FILE_GET_CONTENTS_FLAG_SECRET = (1 << 0), -} NMUtilsFileGetContentsFlags; - -int nm_utils_fd_get_contents (int fd, - gboolean close_fd, - gsize max_length, - NMUtilsFileGetContentsFlags flags, - char **contents, - gsize *length, - GError **error); - -int nm_utils_file_get_contents (int dirfd, - const char *filename, - gsize max_length, - NMUtilsFileGetContentsFlags flags, - char **contents, - gsize *length, - GError **error); - -gboolean nm_utils_file_set_contents (const char *filename, - const char *contents, - gssize length, - mode_t mode, - GError **error); - -#endif /* __NM_IO_UTILS_H__ */ diff --git a/shared/nm-utils/nm-jansson.h b/shared/nm-utils/nm-jansson.h deleted file mode 100644 index 5a73231f..00000000 --- a/shared/nm-utils/nm-jansson.h +++ /dev/null @@ -1,49 +0,0 @@ -/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ -/* - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Copyright 2018 Red Hat, Inc. - */ - -#ifndef __NM_JANSSON_H__ -#define __NM_JANSSON_H__ - -/* you need to include at least "config.h" first, possibly "nm-default.h". */ - -#if WITH_JANSSON - -#include - -/* Added in Jansson v2.7 */ -#ifndef json_boolean_value -#define json_boolean_value json_is_true -#endif - -/* Added in Jansson v2.8 */ -#ifndef json_object_foreach_safe -#define json_object_foreach_safe(object, n, key, value) \ - for (key = json_object_iter_key(json_object_iter(object)), \ - n = json_object_iter_next(object, json_object_key_to_iter(key)); \ - key && (value = json_object_iter_value(json_object_key_to_iter(key))); \ - key = json_object_iter_key(n), \ - n = json_object_iter_next(object, json_object_key_to_iter(key))) -#endif - -NM_AUTO_DEFINE_FCN0 (json_t *, _nm_auto_decref_json, json_decref) -#define nm_auto_decref_json nm_auto(_nm_auto_decref_json) - -#endif /* WITH_JANSON */ - -#endif /* __NM_JANSSON_H__ */ diff --git a/shared/nm-utils/nm-logging-fwd.h b/shared/nm-utils/nm-logging-fwd.h deleted file mode 100644 index 900dfff8..00000000 --- a/shared/nm-utils/nm-logging-fwd.h +++ /dev/null @@ -1,113 +0,0 @@ -/* NetworkManager -- Network link manager - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the - * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, - * Boston, MA 02110-1301 USA. - * - * Copyright (C) 2006 - 2018 Red Hat, Inc. - * Copyright (C) 2006 - 2008 Novell, Inc. - */ - -#ifndef __NM_LOGGING_DEFINES_H__ -#define __NM_LOGGING_DEFINES_H__ - -/* Log domains */ - -typedef enum { /*< skip >*/ - LOGD_NONE = 0LL, - LOGD_PLATFORM = (1LL << 0), /* Platform services */ - LOGD_RFKILL = (1LL << 1), - LOGD_ETHER = (1LL << 2), - LOGD_WIFI = (1LL << 3), - LOGD_BT = (1LL << 4), - LOGD_MB = (1LL << 5), /* mobile broadband */ - LOGD_DHCP4 = (1LL << 6), - LOGD_DHCP6 = (1LL << 7), - LOGD_PPP = (1LL << 8), - LOGD_WIFI_SCAN = (1LL << 9), - LOGD_IP4 = (1LL << 10), - LOGD_IP6 = (1LL << 11), - LOGD_AUTOIP4 = (1LL << 12), - LOGD_DNS = (1LL << 13), - LOGD_VPN = (1LL << 14), - LOGD_SHARING = (1LL << 15), /* Connection sharing/dnsmasq */ - LOGD_SUPPLICANT = (1LL << 16), /* Wi-Fi and 802.1x */ - LOGD_AGENTS = (1LL << 17), /* Secret agents */ - LOGD_SETTINGS = (1LL << 18), /* Settings */ - LOGD_SUSPEND = (1LL << 19), /* Suspend/Resume */ - LOGD_CORE = (1LL << 20), /* Core daemon and policy stuff */ - LOGD_DEVICE = (1LL << 21), /* Device state and activation */ - LOGD_OLPC = (1LL << 22), - LOGD_INFINIBAND = (1LL << 23), - LOGD_FIREWALL = (1LL << 24), - LOGD_ADSL = (1LL << 25), - LOGD_BOND = (1LL << 26), - LOGD_VLAN = (1LL << 27), - LOGD_BRIDGE = (1LL << 28), - LOGD_DBUS_PROPS = (1LL << 29), - LOGD_TEAM = (1LL << 30), - LOGD_CONCHECK = (1LL << 31), - LOGD_DCB = (1LL << 32), /* Data Center Bridging */ - LOGD_DISPATCH = (1LL << 33), - LOGD_AUDIT = (1LL << 34), - LOGD_SYSTEMD = (1LL << 35), - LOGD_VPN_PLUGIN = (1LL << 36), - LOGD_PROXY = (1LL << 37), - - __LOGD_MAX, - LOGD_ALL = (((__LOGD_MAX - 1LL) << 1) - 1LL), - LOGD_DEFAULT = LOGD_ALL & ~( - LOGD_DBUS_PROPS | - LOGD_WIFI_SCAN | - LOGD_VPN_PLUGIN | - 0), - - /* aliases: */ - LOGD_DHCP = LOGD_DHCP4 | LOGD_DHCP6, - LOGD_IP = LOGD_IP4 | LOGD_IP6, -} NMLogDomain; - -/* Log levels */ -typedef enum { /*< skip >*/ - LOGL_TRACE, - LOGL_DEBUG, - LOGL_INFO, - LOGL_WARN, - LOGL_ERR, - - _LOGL_N_REAL, /* the number of actual logging levels */ - - _LOGL_OFF = _LOGL_N_REAL, /* special logging level that is always disabled. */ - _LOGL_KEEP, /* special logging level to indicate that the logging level should not be changed. */ - - _LOGL_N, /* the number of logging levels including "OFF" */ -} NMLogLevel; - -gboolean _nm_log_enabled_impl (gboolean mt_require_locking, - NMLogLevel level, - NMLogDomain domain); - -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 *con_uuid, - const char *fmt, - ...) _nm_printf (10, 11); - -#endif /* __NM_LOGGING_DEFINES_H__ */ diff --git a/shared/nm-utils/nm-macros-internal.h b/shared/nm-utils/nm-macros-internal.h deleted file mode 100644 index 42299c96..00000000 --- a/shared/nm-utils/nm-macros-internal.h +++ /dev/null @@ -1,1707 +0,0 @@ -/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ -/* NetworkManager -- Network link manager - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the - * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, - * Boston, MA 02110-1301 USA. - * - * (C) Copyright 2012 Colin Walters . - * (C) Copyright 2014 Red Hat, Inc. - */ - -#ifndef __NM_MACROS_INTERNAL_H__ -#define __NM_MACROS_INTERNAL_H__ - -#include -#include -#include -#include - -#include - -/*****************************************************************************/ - -#define _nm_packed __attribute__ ((__packed__)) -#define _nm_unused __attribute__ ((__unused__)) -#define _nm_used __attribute__ ((__used__)) -#define _nm_pure __attribute__ ((__pure__)) -#define _nm_const __attribute__ ((__const__)) -#define _nm_printf(a,b) __attribute__ ((__format__ (__printf__, a, b))) -#define _nm_align(s) __attribute__ ((__aligned__ (s))) -#define _nm_section(s) __attribute__ ((__section__ (s))) -#define _nm_alignof(type) __alignof (type) -#define _nm_alignas(type) _nm_align (_nm_alignof (type)) -#define nm_auto(fcn) __attribute__ ((__cleanup__(fcn))) - - -/* This is required to make LTO working. - * - * See https://gitlab.freedesktop.org/NetworkManager/NetworkManager/merge_requests/76#note_112694 - * https://gcc.gnu.org/bugzilla/show_bug.cgi?id=48200#c28 - */ -#ifndef __clang__ -#define _nm_externally_visible __attribute__ ((__externally_visible__)) -#else -#define _nm_externally_visible -#endif - - -#if __GNUC__ >= 7 -#define _nm_fallthrough __attribute__ ((__fallthrough__)) -#else -#define _nm_fallthrough -#endif - -/*****************************************************************************/ - -#ifdef thread_local -#define _nm_thread_local thread_local -/* - * Don't break on glibc < 2.16 that doesn't define __STDC_NO_THREADS__ - * see http://gcc.gnu.org/bugzilla/show_bug.cgi?id=53769 - */ -#elif __STDC_VERSION__ >= 201112L && !(defined(__STDC_NO_THREADS__) || (defined(__GNU_LIBRARY__) && __GLIBC__ == 2 && __GLIBC_MINOR__ < 16)) -#define _nm_thread_local _Thread_local -#else -#define _nm_thread_local __thread -#endif - -/*****************************************************************************/ - -/* most of our code is single-threaded with a mainloop. Hence, we usually don't need - * any thread-safety. Sometimes, we do need thread-safety (nm-logging), but we can - * avoid locking if we are on the main-thread by: - * - * - modifications of shared data is done infrequently and only from the - * main-thread (nm_logging_setup()) - * - read-only access is done frequently (nm_logging_enabled()) - * - from the main-thread, we can do that without locking (because - * all modifications are also done on the main thread. - * - from other threads, we need locking. But this is expected to be - * done infrequently too. Important is the lock-free fast-path on the - * main-thread. - * - * By defining NM_THREAD_SAFE_ON_MAIN_THREAD you indicate that this code runs - * on the main-thread. It is by default defined to "1". If you have code that - * is also used on another thread, redefine the define to 0 (to opt in into - * the slow-path). - */ -#define NM_THREAD_SAFE_ON_MAIN_THREAD 1 - -/*****************************************************************************/ - -#define NM_AUTO_DEFINE_FCN_VOID(CastType, name, func) \ -static inline void name (void *v) \ -{ \ - func (*((CastType *) v)); \ -} - -#define NM_AUTO_DEFINE_FCN_VOID0(CastType, name, func) \ -static inline void name (void *v) \ -{ \ - if (*((CastType *) v)) \ - func (*((CastType *) v)); \ -} - -#define NM_AUTO_DEFINE_FCN(Type, name, func) \ -static inline void name (Type *v) \ -{ \ - func (*v); \ -} - -#define NM_AUTO_DEFINE_FCN0(Type, name, func) \ -static inline void name (Type *v) \ -{ \ - if (*v) \ - func (*v); \ -} - -/*****************************************************************************/ - -/** - * gs_free: - * - * Call g_free() on a variable location when it goes out of scope. - */ -#define gs_free nm_auto(gs_local_free) -NM_AUTO_DEFINE_FCN_VOID0 (void *, gs_local_free, g_free) - -/** - * gs_unref_object: - * - * Call g_object_unref() on a variable location when it goes out of - * scope. Note that unlike g_object_unref(), the variable may be - * %NULL. - */ -#define gs_unref_object nm_auto(gs_local_obj_unref) -NM_AUTO_DEFINE_FCN_VOID0 (GObject *, gs_local_obj_unref, g_object_unref) - -/** - * gs_unref_variant: - * - * Call g_variant_unref() on a variable location when it goes out of - * scope. Note that unlike g_variant_unref(), the variable may be - * %NULL. - */ -#define gs_unref_variant nm_auto(gs_local_variant_unref) -NM_AUTO_DEFINE_FCN0 (GVariant *, gs_local_variant_unref, g_variant_unref) - -/** - * gs_unref_array: - * - * Call g_array_unref() on a variable location when it goes out of - * scope. Note that unlike g_array_unref(), the variable may be - * %NULL. - - */ -#define gs_unref_array nm_auto(gs_local_array_unref) -NM_AUTO_DEFINE_FCN0 (GArray *, gs_local_array_unref, g_array_unref) - -/** - * gs_unref_ptrarray: - * - * Call g_ptr_array_unref() on a variable location when it goes out of - * scope. Note that unlike g_ptr_array_unref(), the variable may be - * %NULL. - - */ -#define gs_unref_ptrarray nm_auto(gs_local_ptrarray_unref) -NM_AUTO_DEFINE_FCN0 (GPtrArray *, gs_local_ptrarray_unref, g_ptr_array_unref) - -/** - * gs_unref_hashtable: - * - * Call g_hash_table_unref() on a variable location when it goes out - * of scope. Note that unlike g_hash_table_unref(), the variable may - * be %NULL. - */ -#define gs_unref_hashtable nm_auto(gs_local_hashtable_unref) -NM_AUTO_DEFINE_FCN0 (GHashTable *, gs_local_hashtable_unref, g_hash_table_unref) - -/** - * gs_free_slist: - * - * Call g_slist_free() on a variable location when it goes out - * of scope. - */ -#define gs_free_slist nm_auto(gs_local_free_slist) -NM_AUTO_DEFINE_FCN0 (GSList *, gs_local_free_slist, g_slist_free) - -/** - * gs_unref_bytes: - * - * Call g_bytes_unref() on a variable location when it goes out - * of scope. Note that unlike g_bytes_unref(), the variable may - * be %NULL. - */ -#define gs_unref_bytes nm_auto(gs_local_bytes_unref) -NM_AUTO_DEFINE_FCN0 (GBytes *, gs_local_bytes_unref, g_bytes_unref) - -/** - * gs_strfreev: - * - * Call g_strfreev() on a variable location when it goes out of scope. - */ -#define gs_strfreev nm_auto(gs_local_strfreev) -NM_AUTO_DEFINE_FCN0 (char **, gs_local_strfreev, g_strfreev) - -/** - * gs_free_error: - * - * Call g_error_free() on a variable location when it goes out of scope. - */ -#define gs_free_error nm_auto(gs_local_free_error) -NM_AUTO_DEFINE_FCN0 (GError *, gs_local_free_error, g_error_free) - -/** - * gs_unref_keyfile: - * - * Call g_key_file_unref() on a variable location when it goes out of scope. - */ -#define gs_unref_keyfile nm_auto(gs_local_keyfile_unref) -NM_AUTO_DEFINE_FCN0 (GKeyFile *, gs_local_keyfile_unref, g_key_file_unref) - -/*****************************************************************************/ - -#include "nm-glib.h" - -/*****************************************************************************/ - -#define nm_offsetofend(t,m) (G_STRUCT_OFFSET (t,m) + sizeof (((t *) NULL)->m)) - -/*****************************************************************************/ - -static inline int nm_close (int fd); - -/** - * nm_auto_free: - * - * Call free() on a variable location when it goes out of scope. - * This is for pointers that are allocated with malloc() instead of - * g_malloc(). - * - * In practice, since glib 2.45, g_malloc()/g_free() always wraps malloc()/free(). - * See bgo#751592. In that case, it would be safe to free pointers allocated with - * malloc() with gs_free or g_free(). - * - * However, let's never mix them. To free malloc'ed memory, always use - * free() or nm_auto_free. - */ -NM_AUTO_DEFINE_FCN_VOID0 (void *, _nm_auto_free_impl, free) -#define nm_auto_free nm_auto(_nm_auto_free_impl) - -NM_AUTO_DEFINE_FCN0 (GVariantIter *, _nm_auto_free_variant_iter, g_variant_iter_free) -#define nm_auto_free_variant_iter nm_auto(_nm_auto_free_variant_iter) - -NM_AUTO_DEFINE_FCN0 (GVariantBuilder *, _nm_auto_unref_variant_builder, g_variant_builder_unref) -#define nm_auto_unref_variant_builder nm_auto(_nm_auto_unref_variant_builder) - -NM_AUTO_DEFINE_FCN0 (GList *, _nm_auto_free_list, g_list_free) -#define nm_auto_free_list nm_auto(_nm_auto_free_list) - -NM_AUTO_DEFINE_FCN0 (GChecksum *, _nm_auto_checksum_free, g_checksum_free) -#define nm_auto_free_checksum nm_auto(_nm_auto_checksum_free) - -#define nm_auto_unset_gvalue nm_auto(g_value_unset) - -NM_AUTO_DEFINE_FCN_VOID0 (void *, _nm_auto_unref_gtypeclass, g_type_class_unref) -#define nm_auto_unref_gtypeclass nm_auto(_nm_auto_unref_gtypeclass) - -NM_AUTO_DEFINE_FCN0 (GByteArray *, _nm_auto_unref_bytearray, g_byte_array_unref) -#define nm_auto_unref_bytearray nm_auto(_nm_auto_unref_bytearray) - -static inline void -_nm_auto_free_gstring (GString **str) -{ - if (*str) - g_string_free (*str, TRUE); -} -#define nm_auto_free_gstring nm_auto(_nm_auto_free_gstring) - -static inline void -_nm_auto_close (int *pfd) -{ - if (*pfd >= 0) { - int errsv = errno; - - (void) nm_close (*pfd); - errno = errsv; - } -} -#define nm_auto_close nm_auto(_nm_auto_close) - -static inline void -_nm_auto_fclose (FILE **pfd) -{ - if (*pfd) { - int errsv = errno; - - (void) fclose (*pfd); - errno = errsv; - } -} -#define nm_auto_fclose nm_auto(_nm_auto_fclose) - -static inline void -_nm_auto_protect_errno (int *p_saved_errno) -{ - errno = *p_saved_errno; -} -#define NM_AUTO_PROTECT_ERRNO(errsv_saved) nm_auto(_nm_auto_protect_errno) _nm_unused const int errsv_saved = (errno) - -NM_AUTO_DEFINE_FCN0 (GSource *, _nm_auto_unref_gsource, g_source_unref); -#define nm_auto_unref_gsource nm_auto(_nm_auto_unref_gsource) - -static inline void -_nm_auto_freev (gpointer ptr) -{ - gpointer **p = ptr; - gpointer *_ptr; - - if (*p) { - for (_ptr = *p; *_ptr; _ptr++) - g_free (*_ptr); - g_free (*p); - } -} -/* g_free a NULL terminated array of pointers, with also freeing each - * pointer with g_free(). It essentially does the same as - * gs_strfreev / g_strfreev(), but not restricted to strv arrays. */ -#define nm_auto_freev nm_auto(_nm_auto_freev) - -/*****************************************************************************/ - -/* http://stackoverflow.com/a/11172679 */ -#define _NM_UTILS_MACRO_FIRST(...) __NM_UTILS_MACRO_FIRST_HELPER(__VA_ARGS__, throwaway) -#define __NM_UTILS_MACRO_FIRST_HELPER(first, ...) first - -#define _NM_UTILS_MACRO_REST(...) __NM_UTILS_MACRO_REST_HELPER(__NM_UTILS_MACRO_REST_NUM(__VA_ARGS__), __VA_ARGS__) -#define __NM_UTILS_MACRO_REST_HELPER(qty, ...) __NM_UTILS_MACRO_REST_HELPER2(qty, __VA_ARGS__) -#define __NM_UTILS_MACRO_REST_HELPER2(qty, ...) __NM_UTILS_MACRO_REST_HELPER_##qty(__VA_ARGS__) -#define __NM_UTILS_MACRO_REST_HELPER_ONE(first) -#define __NM_UTILS_MACRO_REST_HELPER_TWOORMORE(first, ...) , __VA_ARGS__ -#define __NM_UTILS_MACRO_REST_NUM(...) \ - __NM_UTILS_MACRO_REST_SELECT_30TH(__VA_ARGS__, \ - TWOORMORE, TWOORMORE, TWOORMORE, TWOORMORE, TWOORMORE,\ - TWOORMORE, TWOORMORE, TWOORMORE, TWOORMORE, TWOORMORE,\ - TWOORMORE, TWOORMORE, TWOORMORE, TWOORMORE, TWOORMORE,\ - TWOORMORE, TWOORMORE, TWOORMORE, TWOORMORE, TWOORMORE,\ - TWOORMORE, TWOORMORE, TWOORMORE, TWOORMORE, TWOORMORE,\ - TWOORMORE, TWOORMORE, TWOORMORE, ONE, throwaway) -#define __NM_UTILS_MACRO_REST_SELECT_30TH(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20, a21, a22, a23, a24, a25, a26, a27, a28, a29, a30, ...) a30 - -/*****************************************************************************/ - -/* http://stackoverflow.com/a/2124385/354393 - * https://stackoverflow.com/questions/11317474/macro-to-count-number-of-arguments - */ - -#define NM_NARG(...) \ - _NM_NARG(, ##__VA_ARGS__, _NM_NARG_RSEQ_N()) -#define _NM_NARG(...) \ - _NM_NARG_ARG_N(__VA_ARGS__) -#define _NM_NARG_ARG_N( \ - _0, \ - _1, _2, _3, _4, _5, _6, _7, _8, _9,_10, \ - _11,_12,_13,_14,_15,_16,_17,_18,_19,_20, \ - _21,_22,_23,_24,_25,_26,_27,_28,_29,_30, \ - _31,_32,_33,_34,_35,_36,_37,_38,_39,_40, \ - _41,_42,_43,_44,_45,_46,_47,_48,_49,_50, \ - _51,_52,_53,_54,_55,_56,_57,_58,_59,_60, \ - _61,_62,_63,N,...) N -#define _NM_NARG_RSEQ_N() \ - 63,62,61,60, \ - 59,58,57,56,55,54,53,52,51,50, \ - 49,48,47,46,45,44,43,42,41,40, \ - 39,38,37,36,35,34,33,32,31,30, \ - 29,28,27,26,25,24,23,22,21,20, \ - 19,18,17,16,15,14,13,12,11,10, \ - 9,8,7,6,5,4,3,2,1,0 - -/*****************************************************************************/ - -#if defined (__GNUC__) -#define _NM_PRAGMA_WARNING_DO(warning) G_STRINGIFY(GCC diagnostic ignored warning) -#elif defined (__clang__) -#define _NM_PRAGMA_WARNING_DO(warning) G_STRINGIFY(clang diagnostic ignored warning) -#endif - -/* you can only suppress a specific warning that the compiler - * understands. Otherwise you will get another compiler warning - * about invalid pragma option. - * It's not that bad however, because gcc and clang often have the - * same name for the same warning. */ - -#if defined (__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6)) -#define NM_PRAGMA_WARNING_DISABLE(warning) \ - _Pragma("GCC diagnostic push") \ - _Pragma(_NM_PRAGMA_WARNING_DO(warning)) -#elif defined (__clang__) -#define NM_PRAGMA_WARNING_DISABLE(warning) \ - _Pragma("clang diagnostic push") \ - _Pragma(_NM_PRAGMA_WARNING_DO("-Wunknown-warning-option")) \ - _Pragma(_NM_PRAGMA_WARNING_DO(warning)) -#else -#define NM_PRAGMA_WARNING_DISABLE(warning) -#endif - -#if defined (__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6)) -#define NM_PRAGMA_WARNING_REENABLE \ - _Pragma("GCC diagnostic pop") -#elif defined (__clang__) -#define NM_PRAGMA_WARNING_REENABLE \ - _Pragma("clang diagnostic pop") -#else -#define NM_PRAGMA_WARNING_REENABLE -#endif - -/*****************************************************************************/ - -/** - * NM_G_ERROR_MSG: - * @error: (allow-none): the #GError instance - * - * All functions must follow the convention that when they - * return a failure, they must also set the GError to a valid - * message. For external API however, we want to be extra - * careful before accessing the error instance. Use NM_G_ERROR_MSG() - * which is safe to use on NULL. - * - * Returns: the error message. - **/ -static inline const char * -NM_G_ERROR_MSG (GError *error) -{ - return error ? (error->message ?: "(null)") : "(no-error)"; \ -} - -/*****************************************************************************/ - -/* macro to return strlen() of a compile time string. */ -#define NM_STRLEN(str) ( sizeof (""str"") - 1 ) - -/* returns the length of a NULL terminated array of pointers, - * like g_strv_length() does. The difference is: - * - it operats on arrays of pointers (of any kind, requiring no cast). - * - it accepts NULL to return zero. */ -#define NM_PTRARRAY_LEN(array) \ - ({ \ - typeof (*(array)) *const _array = (array); \ - gsize _n = 0; \ - \ - if (_array) { \ - _nm_unused gconstpointer _type_check_is_pointer = _array[0]; \ - \ - while (_array[_n]) \ - _n++; \ - } \ - _n; \ - }) - -/* Note: @value is only evaluated when *out_val is present. - * Thus, - * NM_SET_OUT (out_str, g_strdup ("hallo")); - * does the right thing. - */ -#define NM_SET_OUT(out_val, value) \ - G_STMT_START { \ - typeof(*(out_val)) *_out_val = (out_val); \ - \ - if (_out_val) { \ - *_out_val = (value); \ - } \ - } G_STMT_END - -/*****************************************************************************/ - -#ifndef _NM_CC_SUPPORT_AUTO_TYPE -#if (defined (__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 9 ))) -#define _NM_CC_SUPPORT_AUTO_TYPE 1 -#else -#define _NM_CC_SUPPORT_AUTO_TYPE 0 -#endif -#endif - -#ifndef _NM_CC_SUPPORT_GENERIC -/* In the meantime, NetworkManager requires C11 and _Generic() should always be available. - * However, shared/nm-utils may also be used in VPN/applet, which possibly did not yet - * bump the C standard requirement. Leave this for the moment, but eventually we can - * drop it. */ -#if (defined (__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 9 ))) || (defined (__clang__)) -#define _NM_CC_SUPPORT_GENERIC 1 -#else -#define _NM_CC_SUPPORT_GENERIC 0 -#endif -#endif - -#if _NM_CC_SUPPORT_AUTO_TYPE -#define _nm_auto_type __auto_type -#endif - -#if _NM_CC_SUPPORT_GENERIC -#define _NM_CONSTCAST_FULL_1(type, obj_expr, obj) \ - (_Generic ((obj_expr), \ - const void *const: ((const type *) (obj)), \ - const void * : ((const type *) (obj)), \ - void *const: (( type *) (obj)), \ - void * : (( type *) (obj)), \ - const type *const: ((const type *) (obj)), \ - const type * : ((const type *) (obj)), \ - type *const: (( type *) (obj)), \ - type * : (( type *) (obj)))) -#define _NM_CONSTCAST_FULL_2(type, obj_expr, obj, alias_type2) \ - (_Generic ((obj_expr), \ - const void *const: ((const type *) (obj)), \ - const void * : ((const type *) (obj)), \ - void *const: (( type *) (obj)), \ - void * : (( type *) (obj)), \ - const alias_type2 *const: ((const type *) (obj)), \ - const alias_type2 * : ((const type *) (obj)), \ - alias_type2 *const: (( type *) (obj)), \ - alias_type2 * : (( type *) (obj)), \ - const type *const: ((const type *) (obj)), \ - const type * : ((const type *) (obj)), \ - type *const: (( type *) (obj)), \ - type * : (( type *) (obj)))) -#define _NM_CONSTCAST_FULL_3(type, obj_expr, obj, alias_type2, alias_type3) \ - (_Generic ((obj_expr), \ - const void *const: ((const type *) (obj)), \ - const void * : ((const type *) (obj)), \ - void *const: (( type *) (obj)), \ - void * : (( type *) (obj)), \ - const alias_type2 *const: ((const type *) (obj)), \ - const alias_type2 * : ((const type *) (obj)), \ - alias_type2 *const: (( type *) (obj)), \ - alias_type2 * : (( type *) (obj)), \ - const alias_type3 *const: ((const type *) (obj)), \ - const alias_type3 * : ((const type *) (obj)), \ - alias_type3 *const: (( type *) (obj)), \ - alias_type3 * : (( type *) (obj)), \ - const type *const: ((const type *) (obj)), \ - const type * : ((const type *) (obj)), \ - type *const: (( type *) (obj)), \ - type * : (( type *) (obj)))) -#define _NM_CONSTCAST_FULL_4(type, obj_expr, obj, alias_type2, alias_type3, alias_type4) \ - (_Generic ((obj_expr), \ - const void *const: ((const type *) (obj)), \ - const void * : ((const type *) (obj)), \ - void *const: (( type *) (obj)), \ - void * : (( type *) (obj)), \ - const alias_type2 *const: ((const type *) (obj)), \ - const alias_type2 * : ((const type *) (obj)), \ - alias_type2 *const: (( type *) (obj)), \ - alias_type2 * : (( type *) (obj)), \ - const alias_type3 *const: ((const type *) (obj)), \ - const alias_type3 * : ((const type *) (obj)), \ - alias_type3 *const: (( type *) (obj)), \ - alias_type3 * : (( type *) (obj)), \ - const alias_type4 *const: ((const type *) (obj)), \ - const alias_type4 * : ((const type *) (obj)), \ - alias_type4 *const: (( type *) (obj)), \ - alias_type4 * : (( type *) (obj)), \ - const type *const: ((const type *) (obj)), \ - const type * : ((const type *) (obj)), \ - type *const: (( type *) (obj)), \ - type * : (( type *) (obj)))) -#define _NM_CONSTCAST_FULL_x(type, obj_expr, obj, n, ...) (_NM_CONSTCAST_FULL_##n (type, obj_expr, obj, ##__VA_ARGS__)) -#define _NM_CONSTCAST_FULL_y(type, obj_expr, obj, n, ...) (_NM_CONSTCAST_FULL_x (type, obj_expr, obj, n, ##__VA_ARGS__)) -#define NM_CONSTCAST_FULL( type, obj_expr, obj, ...) (_NM_CONSTCAST_FULL_y (type, obj_expr, obj, NM_NARG (dummy, ##__VA_ARGS__), ##__VA_ARGS__)) -#else -#define NM_CONSTCAST_FULL( type, obj_expr, obj, ...) ((type *) (obj)) -#endif - -#define NM_CONSTCAST(type, obj, ...) \ - NM_CONSTCAST_FULL(type, (obj), (obj), ##__VA_ARGS__) - -#if _NM_CC_SUPPORT_GENERIC -#define NM_UNCONST_PTR(type, arg) \ - _Generic ((arg), \ - const type *: ((type *) (arg)), \ - type *: ((type *) (arg))) -#else -#define NM_UNCONST_PTR(type, arg) \ - ((type *) (arg)) -#endif - -#if _NM_CC_SUPPORT_GENERIC -#define NM_UNCONST_PPTR(type, arg) \ - _Generic ((arg), \ - const type * *: ((type **) (arg)), \ - type * *: ((type **) (arg)), \ - const type *const*: ((type **) (arg)), \ - type *const*: ((type **) (arg))) -#else -#define NM_UNCONST_PPTR(type, arg) \ - ((type **) (arg)) -#endif - -#define NM_GOBJECT_CAST(type, obj, is_check, ...) \ - ({ \ - const void *_obj = (obj); \ - \ - nm_assert (_obj || (is_check (_obj))); \ - NM_CONSTCAST_FULL (type, (obj), _obj, GObject, ##__VA_ARGS__); \ - }) - -#define NM_GOBJECT_CAST_NON_NULL(type, obj, is_check, ...) \ - ({ \ - const void *_obj = (obj); \ - \ - nm_assert (is_check (_obj)); \ - NM_CONSTCAST_FULL (type, (obj), _obj, GObject, ##__VA_ARGS__); \ - }) - -#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. */ -#define _NM_ENSURE_TYPE(type, value) (_Generic ((value), type: (value))) -#else -#define _NM_ENSURE_TYPE(type, value) (value) -#endif - -#if _NM_CC_SUPPORT_GENERIC -/* these macros cast (value) to - * - "const char **" (for "MC", mutable-const) - * - "const char *const*" (for "CC", const-const) - * The point is to do this cast, but only accepting pointers - * that are compatible already. - * - * The problem is, if you add a function like g_strdupv(), the input - * argument is not modified (CC), but you want to make it work also - * for "char **". C doesn't allow this form of casting (for good reasons), - * so the function makes a choice like g_strdupv(char**). That means, - * every time you want to call it with a const argument, you need to - * explicitly cast it. - * - * These macros do the cast, but they only accept a compatible input - * type, otherwise they will fail compilation. - */ -#define NM_CAST_STRV_MC(value) \ - (_Generic ((value), \ - const char * *: (const char * *) (value), \ - char * *: (const char * *) (value), \ - void *: (const char * *) (value))) -#define NM_CAST_STRV_CC(value) \ - (_Generic ((value), \ - const char *const*: (const char *const*) (value), \ - const char * *: (const char *const*) (value), \ - char *const*: (const char *const*) (value), \ - char * *: (const char *const*) (value), \ - const void *: (const char *const*) (value), \ - void *: (const char *const*) (value))) -#else -#define NM_CAST_STRV_MC(value) ((const char * *) (value)) -#define NM_CAST_STRV_CC(value) ((const char *const*) (value)) -#endif - -#if _NM_CC_SUPPORT_GENERIC -#define NM_PROPAGATE_CONST(test_expr, ptr) \ - (_Generic ((test_expr), \ - const typeof (*(test_expr)) *: ((const typeof (*(ptr)) *) (ptr)), \ - default: (_Generic ((test_expr), \ - typeof (*(test_expr)) *: (ptr))))) -#else -#define NM_PROPAGATE_CONST(test_expr, ptr) (ptr) -#endif - -/* with the way it is implemented, the caller may or may not pass a trailing - * ',' and it will work. However, this makes the macro unsuitable for initializing - * an array. */ -#define NM_MAKE_STRV(...) \ - ((const char *const[(sizeof (((const char *const[]) { __VA_ARGS__ })) / sizeof (const char *)) + 1]) { __VA_ARGS__ }) - -/*****************************************************************************/ - -#define _NM_IN_SET_EVAL_1( op, _x, y) (_x == (y)) -#define _NM_IN_SET_EVAL_2( op, _x, y, ...) (_x == (y)) op _NM_IN_SET_EVAL_1 (op, _x, __VA_ARGS__) -#define _NM_IN_SET_EVAL_3( op, _x, y, ...) (_x == (y)) op _NM_IN_SET_EVAL_2 (op, _x, __VA_ARGS__) -#define _NM_IN_SET_EVAL_4( op, _x, y, ...) (_x == (y)) op _NM_IN_SET_EVAL_3 (op, _x, __VA_ARGS__) -#define _NM_IN_SET_EVAL_5( op, _x, y, ...) (_x == (y)) op _NM_IN_SET_EVAL_4 (op, _x, __VA_ARGS__) -#define _NM_IN_SET_EVAL_6( op, _x, y, ...) (_x == (y)) op _NM_IN_SET_EVAL_5 (op, _x, __VA_ARGS__) -#define _NM_IN_SET_EVAL_7( op, _x, y, ...) (_x == (y)) op _NM_IN_SET_EVAL_6 (op, _x, __VA_ARGS__) -#define _NM_IN_SET_EVAL_8( op, _x, y, ...) (_x == (y)) op _NM_IN_SET_EVAL_7 (op, _x, __VA_ARGS__) -#define _NM_IN_SET_EVAL_9( op, _x, y, ...) (_x == (y)) op _NM_IN_SET_EVAL_8 (op, _x, __VA_ARGS__) -#define _NM_IN_SET_EVAL_10(op, _x, y, ...) (_x == (y)) op _NM_IN_SET_EVAL_9 (op, _x, __VA_ARGS__) -#define _NM_IN_SET_EVAL_11(op, _x, y, ...) (_x == (y)) op _NM_IN_SET_EVAL_10 (op, _x, __VA_ARGS__) -#define _NM_IN_SET_EVAL_12(op, _x, y, ...) (_x == (y)) op _NM_IN_SET_EVAL_11 (op, _x, __VA_ARGS__) -#define _NM_IN_SET_EVAL_13(op, _x, y, ...) (_x == (y)) op _NM_IN_SET_EVAL_12 (op, _x, __VA_ARGS__) -#define _NM_IN_SET_EVAL_14(op, _x, y, ...) (_x == (y)) op _NM_IN_SET_EVAL_13 (op, _x, __VA_ARGS__) -#define _NM_IN_SET_EVAL_15(op, _x, y, ...) (_x == (y)) op _NM_IN_SET_EVAL_14 (op, _x, __VA_ARGS__) -#define _NM_IN_SET_EVAL_16(op, _x, y, ...) (_x == (y)) op _NM_IN_SET_EVAL_15 (op, _x, __VA_ARGS__) - -#define _NM_IN_SET_EVAL_N2(op, _x, n, ...) (_NM_IN_SET_EVAL_##n(op, _x, __VA_ARGS__)) -#define _NM_IN_SET_EVAL_N(op, type, x, n, ...) \ - ({ \ - type _x = (x); \ - \ - /* trigger a -Wenum-compare warning */ \ - nm_assert (TRUE || _x == (x)); \ - \ - !!_NM_IN_SET_EVAL_N2(op, _x, n, __VA_ARGS__); \ - }) - -#define _NM_IN_SET(op, type, x, ...) _NM_IN_SET_EVAL_N(op, type, x, NM_NARG (__VA_ARGS__), __VA_ARGS__) - -/* Beware that this does short-circuit evaluation (use "||" instead of "|") - * which has a possibly unexpected non-function-like behavior. - * Use NM_IN_SET_SE if you need all arguments to be evaluated. */ -#define NM_IN_SET(x, ...) _NM_IN_SET(||, typeof (x), x, __VA_ARGS__) - -/* "SE" stands for "side-effect". Contrary to NM_IN_SET(), this does not do - * short-circuit evaluation, which can make a difference if the arguments have - * side-effects. */ -#define NM_IN_SET_SE(x, ...) _NM_IN_SET(|, typeof (x), x, __VA_ARGS__) - -/* the *_TYPED forms allow to explicitly select the type of "x". This is useful - * if "x" doesn't support typeof (bitfields) or you want to gracefully convert - * a type using automatic type conversion rules (but not forcing the conversion - * with a cast). */ -#define NM_IN_SET_TYPED(type, x, ...) _NM_IN_SET(||, type, x, __VA_ARGS__) -#define NM_IN_SET_SE_TYPED(type, x, ...) _NM_IN_SET(|, type, x, __VA_ARGS__) - -/*****************************************************************************/ - -static inline gboolean -_NM_IN_STRSET_streq (const char *x, const char *s) -{ - return s && strcmp (x, s) == 0; -} - -#define _NM_IN_STRSET_EVAL_1( op, _x, y) _NM_IN_STRSET_streq (_x, y) -#define _NM_IN_STRSET_EVAL_2( op, _x, y, ...) _NM_IN_STRSET_streq (_x, y) op _NM_IN_STRSET_EVAL_1 (op, _x, __VA_ARGS__) -#define _NM_IN_STRSET_EVAL_3( op, _x, y, ...) _NM_IN_STRSET_streq (_x, y) op _NM_IN_STRSET_EVAL_2 (op, _x, __VA_ARGS__) -#define _NM_IN_STRSET_EVAL_4( op, _x, y, ...) _NM_IN_STRSET_streq (_x, y) op _NM_IN_STRSET_EVAL_3 (op, _x, __VA_ARGS__) -#define _NM_IN_STRSET_EVAL_5( op, _x, y, ...) _NM_IN_STRSET_streq (_x, y) op _NM_IN_STRSET_EVAL_4 (op, _x, __VA_ARGS__) -#define _NM_IN_STRSET_EVAL_6( op, _x, y, ...) _NM_IN_STRSET_streq (_x, y) op _NM_IN_STRSET_EVAL_5 (op, _x, __VA_ARGS__) -#define _NM_IN_STRSET_EVAL_7( op, _x, y, ...) _NM_IN_STRSET_streq (_x, y) op _NM_IN_STRSET_EVAL_6 (op, _x, __VA_ARGS__) -#define _NM_IN_STRSET_EVAL_8( op, _x, y, ...) _NM_IN_STRSET_streq (_x, y) op _NM_IN_STRSET_EVAL_7 (op, _x, __VA_ARGS__) -#define _NM_IN_STRSET_EVAL_9( op, _x, y, ...) _NM_IN_STRSET_streq (_x, y) op _NM_IN_STRSET_EVAL_8 (op, _x, __VA_ARGS__) -#define _NM_IN_STRSET_EVAL_10(op, _x, y, ...) _NM_IN_STRSET_streq (_x, y) op _NM_IN_STRSET_EVAL_9 (op, _x, __VA_ARGS__) -#define _NM_IN_STRSET_EVAL_11(op, _x, y, ...) _NM_IN_STRSET_streq (_x, y) op _NM_IN_STRSET_EVAL_10 (op, _x, __VA_ARGS__) -#define _NM_IN_STRSET_EVAL_12(op, _x, y, ...) _NM_IN_STRSET_streq (_x, y) op _NM_IN_STRSET_EVAL_11 (op, _x, __VA_ARGS__) -#define _NM_IN_STRSET_EVAL_13(op, _x, y, ...) _NM_IN_STRSET_streq (_x, y) op _NM_IN_STRSET_EVAL_12 (op, _x, __VA_ARGS__) -#define _NM_IN_STRSET_EVAL_14(op, _x, y, ...) _NM_IN_STRSET_streq (_x, y) op _NM_IN_STRSET_EVAL_13 (op, _x, __VA_ARGS__) -#define _NM_IN_STRSET_EVAL_15(op, _x, y, ...) _NM_IN_STRSET_streq (_x, y) op _NM_IN_STRSET_EVAL_14 (op, _x, __VA_ARGS__) -#define _NM_IN_STRSET_EVAL_16(op, _x, y, ...) _NM_IN_STRSET_streq (_x, y) op _NM_IN_STRSET_EVAL_15 (op, _x, __VA_ARGS__) - -#define _NM_IN_STRSET_EVAL_N2(op, _x, n, ...) (_NM_IN_STRSET_EVAL_##n(op, _x, __VA_ARGS__)) -#define _NM_IN_STRSET_EVAL_N(op, x, n, ...) \ - ({ \ - const char *_x = (x); \ - ( ((_x == NULL) && _NM_IN_SET_EVAL_N2 (op, ((const char *) NULL), n, __VA_ARGS__)) \ - || ((_x != NULL) && _NM_IN_STRSET_EVAL_N2 (op, _x, n, __VA_ARGS__)) \ - ); \ - }) - -/* Beware that this does short-circuit evaluation (use "||" instead of "|") - * which has a possibly unexpected non-function-like behavior. - * Use NM_IN_STRSET_SE if you need all arguments to be evaluated. */ -#define NM_IN_STRSET(x, ...) _NM_IN_STRSET_EVAL_N(||, x, NM_NARG (__VA_ARGS__), __VA_ARGS__) - -/* "SE" stands for "side-effect". Contrary to NM_IN_STRSET(), this does not do - * short-circuit evaluation, which can make a difference if the arguments have - * side-effects. */ -#define NM_IN_STRSET_SE(x, ...) _NM_IN_STRSET_EVAL_N(|, x, NM_NARG (__VA_ARGS__), __VA_ARGS__) - -#define NM_STRCHAR_ALL(str, ch_iter, predicate) \ - ({ \ - gboolean _val = TRUE; \ - const char *_str = (str); \ - \ - if (_str) { \ - for (;;) { \ - const char ch_iter = _str[0]; \ - \ - if (ch_iter != '\0') { \ - if (predicate) {\ - _str++; \ - continue; \ - } \ - _val = FALSE; \ - } \ - break; \ - } \ - } \ - _val; \ - }) - -#define NM_STRCHAR_ANY(str, ch_iter, predicate) \ - ({ \ - gboolean _val = FALSE; \ - const char *_str = (str); \ - \ - if (_str) { \ - for (;;) { \ - const char ch_iter = _str[0]; \ - \ - if (ch_iter != '\0') { \ - if (predicate) { \ - ; \ - } else { \ - _str++; \ - continue; \ - } \ - _val = TRUE; \ - } \ - break; \ - } \ - } \ - _val; \ - }) - -/*****************************************************************************/ - -/* NM_CACHED_QUARK() returns the GQuark for @string, but caches - * it in a static variable to speed up future lookups. - * - * @string must be a string literal. - */ -#define NM_CACHED_QUARK(string) \ - ({ \ - static GQuark _nm_cached_quark = 0; \ - \ - (G_LIKELY (_nm_cached_quark != 0) \ - ? _nm_cached_quark \ - : (_nm_cached_quark = g_quark_from_static_string (""string""))); \ - }) - -/* NM_CACHED_QUARK_FCN() is essentially the same as G_DEFINE_QUARK - * with two differences: - * - @string must be a quoted string-literal - * - @fcn must be the full function name, while G_DEFINE_QUARK() appends - * "_quark" to the function name. - * Both properties of G_DEFINE_QUARK() are non favorable, because you can no - * longer grep for string/fcn -- unless you are aware that you are searching - * for G_DEFINE_QUARK() and omit quotes / append _quark(). With NM_CACHED_QUARK_FCN(), - * ctags/cscope can locate the use of @fcn (though it doesn't recognize that - * NM_CACHED_QUARK_FCN() defines it). - */ -#define NM_CACHED_QUARK_FCN(string, fcn) \ -GQuark \ -fcn (void) \ -{ \ - return NM_CACHED_QUARK (string); \ -} - -/*****************************************************************************/ - -static inline gboolean -nm_streq (const char *s1, const char *s2) -{ - return strcmp (s1, s2) == 0; -} - -static inline gboolean -nm_streq0 (const char *s1, const char *s2) -{ - return (s1 == s2) - || (s1 && s2 && strcmp (s1, s2) == 0); -} - -#define NM_STR_HAS_PREFIX(str, prefix) \ - (strncmp ((str), ""prefix"", NM_STRLEN (prefix)) == 0) - -#define NM_STR_HAS_SUFFIX(str, suffix) \ - ({ \ - const char *_str = (str); \ - gsize _l = strlen (_str); \ - \ - ( (_l >= NM_STRLEN (suffix)) \ - && (memcmp (&_str[_l - NM_STRLEN (suffix)], \ - ""suffix"", \ - NM_STRLEN (suffix)) == 0)); \ - }) - -/*****************************************************************************/ - -static inline GString * -nm_gstring_prepare (GString **l) -{ - if (*l) - g_string_set_size (*l, 0); - else - *l = g_string_sized_new (30); - return *l; -} - -static inline GString * -nm_gstring_add_space_delimiter (GString *str) -{ - if (str->len > 0) - g_string_append_c (str, ' '); - return str; -} - -static inline const char * -nm_str_not_empty (const char *str) -{ - return str && str[0] ? str : NULL; -} - -static inline char * -nm_strdup_not_empty (const char *str) -{ - return str && str[0] ? g_strdup (str) : NULL; -} - -static inline char * -nm_str_realloc (char *str) -{ - gs_free char *s = str; - - /* Returns a new clone of @str and frees @str. The point is that @str - * possibly points to a larger chunck of memory. We want to freshly allocate - * a buffer. - * - * We could use realloc(), but that might not do anything or leave - * @str in its memory pool for chunks of a different size (bad for - * fragmentation). - * - * This is only useful when we want to keep the buffer around for a long - * time and want to re-allocate a more optimal buffer. */ - - return g_strdup (s); -} - -/*****************************************************************************/ - -#define NM_PRINT_FMT_QUOTED(cond, prefix, str, suffix, str_else) \ - (cond) ? (prefix) : "", \ - (cond) ? (str) : (str_else), \ - (cond) ? (suffix) : "" -#define NM_PRINT_FMT_QUOTE_STRING(arg) NM_PRINT_FMT_QUOTED((arg), "\"", (arg), "\"", "(null)") - -/*****************************************************************************/ - -/* glib/C provides the following kind of assertions: - * - assert() -- disable with NDEBUG - * - g_return_if_fail() -- disable with G_DISABLE_CHECKS - * - g_assert() -- disable with G_DISABLE_ASSERT - * but they are all enabled by default and usually even production builds have - * these kind of assertions enabled. It also means, that disabling assertions - * is an untested configuration, and might have bugs. - * - * Add our own assertion macro nm_assert(), which is disabled by default and must - * be explicitly enabled. They are useful for more expensive checks or checks that - * depend less on runtime conditions (that is, are generally expected to be true). */ - -#ifndef NM_MORE_ASSERTS -#define NM_MORE_ASSERTS 0 -#endif - -#if NM_MORE_ASSERTS -#define nm_assert(cond) G_STMT_START { g_assert (cond); } G_STMT_END -#define nm_assert_se(cond) G_STMT_START { if (G_LIKELY (cond)) { ; } else { g_assert (FALSE && (cond)); } } G_STMT_END -#define nm_assert_not_reached() G_STMT_START { g_assert_not_reached (); } G_STMT_END -#else -#define nm_assert(cond) G_STMT_START { if (FALSE) { if (cond) { } } } G_STMT_END -#define nm_assert_se(cond) G_STMT_START { if (G_LIKELY (cond)) { ; } } G_STMT_END -#define nm_assert_not_reached() G_STMT_START { ; } G_STMT_END -#endif - -/*****************************************************************************/ - -#define NM_GOBJECT_PROPERTIES_DEFINE_BASE(...) \ -typedef enum { \ - PROP_0, \ - __VA_ARGS__ \ - _PROPERTY_ENUMS_LAST, \ -} _PropertyEnums; \ -static GParamSpec *obj_properties[_PROPERTY_ENUMS_LAST] = { NULL, } - -#define NM_GOBJECT_PROPERTIES_DEFINE(obj_type, ...) \ -NM_GOBJECT_PROPERTIES_DEFINE_BASE (__VA_ARGS__); \ -static inline void \ -_nm_gobject_notify_together_impl (obj_type *obj, guint n, const _PropertyEnums *props) \ -{ \ - const gboolean freeze_thaw = (n > 1); \ - \ - nm_assert (G_IS_OBJECT (obj)); \ - nm_assert (n > 0); \ - \ - if (freeze_thaw) \ - g_object_freeze_notify ((GObject *) obj); \ - while (n-- > 0) { \ - const _PropertyEnums prop = *props++; \ - \ - if (prop != PROP_0) { \ - nm_assert ((gsize) prop < G_N_ELEMENTS (obj_properties)); \ - nm_assert (obj_properties[prop]); \ - g_object_notify_by_pspec ((GObject *) obj, obj_properties[prop]); \ - } \ - } \ - if (freeze_thaw) \ - g_object_thaw_notify ((GObject *) obj); \ -} \ -\ -static inline void \ -_notify (obj_type *obj, _PropertyEnums prop) \ -{ \ - _nm_gobject_notify_together_impl (obj, 1, &prop); \ -} \ - -/* invokes _notify() for all arguments (of type _PropertyEnums). Note, that if - * there are more than one prop arguments, this will involve a freeze/thaw - * of GObject property notifications. */ -#define nm_gobject_notify_together(obj, ...) \ - _nm_gobject_notify_together_impl (obj, NM_NARG (__VA_ARGS__), (const _PropertyEnums[]) { __VA_ARGS__ }) - -/*****************************************************************************/ - -#define _NM_GET_PRIVATE(self, type, is_check, ...) (&(NM_GOBJECT_CAST_NON_NULL (type, (self), is_check, ##__VA_ARGS__)->_priv)) -#if _NM_CC_SUPPORT_AUTO_TYPE -#define _NM_GET_PRIVATE_PTR(self, type, is_check, ...) \ - ({ \ - _nm_auto_type _self = NM_GOBJECT_CAST_NON_NULL (type, (self), is_check, ##__VA_ARGS__); \ - \ - NM_PROPAGATE_CONST (_self, _self->_priv); \ - }) -#else -#define _NM_GET_PRIVATE_PTR(self, type, is_check, ...) (NM_GOBJECT_CAST_NON_NULL (type, (self), is_check, ##__VA_ARGS__)->_priv) -#endif - -/*****************************************************************************/ - -static inline gpointer -nm_g_object_ref (gpointer obj) -{ - /* g_object_ref() doesn't accept NULL. */ - if (obj) - g_object_ref (obj); - return obj; -} -#define nm_g_object_ref(obj) ((typeof (obj)) nm_g_object_ref (obj)) - -static inline void -nm_g_object_unref (gpointer obj) -{ - /* g_object_unref() doesn't accept NULL. Usully, we workaround that - * by using g_clear_object(), but sometimes that is not convenient - * (for example as as destroy function for a hash table that can contain - * NULL values). */ - if (obj) - g_object_unref (obj); -} - -/* Assigns GObject @obj to destination @pp, and takes an additional ref. - * The previous value of @pp is unrefed. - * - * It makes sure to first increase the ref-count of @obj, and handles %NULL - * @obj correctly. - * */ -#define nm_g_object_ref_set(pp, obj) \ - ({ \ - typeof (*(pp)) *const _pp = (pp); \ - typeof (*_pp) const _obj = (obj); \ - typeof (*_pp) _p; \ - gboolean _changed = FALSE; \ - \ - nm_assert (!_pp || !*_pp || G_IS_OBJECT (*_pp)); \ - nm_assert (!_obj || G_IS_OBJECT (_obj)); \ - \ - if ( _pp \ - && ((_p = *_pp) != _obj)) { \ - nm_g_object_ref (_obj); \ - *_pp = _obj; \ - nm_g_object_unref (_p); \ - _changed = TRUE; \ - } \ - _changed; \ - }) - -#define nm_clear_pointer(pp, destroy) \ - ({ \ - typeof (*(pp)) *_pp = (pp); \ - typeof (*_pp) _p; \ - gboolean _changed = FALSE; \ - \ - if ( _pp \ - && (_p = *_pp)) { \ - _nm_unused gconstpointer _p_check_is_pointer = _p; \ - \ - *_pp = NULL; \ - /* g_clear_pointer() assigns @destroy first to a local variable, so that - * you can call "g_clear_pointer (pp, (GDestroyNotify) destroy);" without - * gcc emitting a warning. We don't do that, hence, you cannot cast - * "destroy" first. - * - * On the upside: you are not supposed to cast fcn, because the pointer - * types are preserved. If you really need a cast, you should cast @pp. - * But that is hardly ever necessary. */ \ - (destroy) (_p); \ - \ - _changed = TRUE; \ - } \ - _changed; \ - }) - -/* basically, replaces - * g_clear_pointer (&location, g_free) - * with - * nm_clear_g_free (&location) - * - * Another advantage is that by using a macro and typeof(), it is more - * typesafe and gives you for example a compiler warning when pp is a const - * pointer or points to a const-pointer. - */ -#define nm_clear_g_free(pp) \ - nm_clear_pointer (pp, g_free) - -#define nm_clear_g_object(pp) \ - nm_clear_pointer (pp, g_object_unref) - -static inline gboolean -nm_clear_g_source (guint *id) -{ - guint v; - - if ( id - && (v = *id)) { - *id = 0; - g_source_remove (v); - return TRUE; - } - return FALSE; -} - -static inline gboolean -nm_clear_g_signal_handler (gpointer self, gulong *id) -{ - gulong v; - - if ( id - && (v = *id)) { - *id = 0; - g_signal_handler_disconnect (self, v); - return TRUE; - } - return FALSE; -} - -static inline gboolean -nm_clear_g_variant (GVariant **variant) -{ - GVariant *v; - - if ( variant - && (v = *variant)) { - *variant = NULL; - g_variant_unref (v); - return TRUE; - } - return FALSE; -} - -static inline gboolean -nm_clear_g_cancellable (GCancellable **cancellable) -{ - GCancellable *v; - - if ( cancellable - && (v = *cancellable)) { - *cancellable = NULL; - g_cancellable_cancel (v); - g_object_unref (v); - return TRUE; - } - return FALSE; -} - -/* If @cancellable_id is not 0, clear it and call g_cancellable_disconnect(). - * @cancellable may be %NULL, if there is nothing to disconnect. - * - * It's like nm_clear_g_signal_handler(), except that it uses g_cancellable_disconnect() - * instead of g_signal_handler_disconnect(). - * - * Note the warning in glib documentation about dead-lock and what g_cancellable_disconnect() - * actually does. */ -static inline gboolean -nm_clear_g_cancellable_disconnect (GCancellable *cancellable, gulong *cancellable_id) -{ - gulong id; - - if ( cancellable_id - && (id = *cancellable_id) != 0) { - *cancellable_id = 0; - g_cancellable_disconnect (cancellable, id); - return TRUE; - } - return FALSE; -} - -/*****************************************************************************/ - -static inline GVariant * -nm_g_variant_ref (GVariant *v) -{ - if (v) - g_variant_ref (v); - return v; -} - -static inline void -nm_g_variant_unref (GVariant *v) -{ - if (v) - g_variant_unref (v); -} - -/*****************************************************************************/ - -/* Determine whether @x is a power of two (@x being an integer type). - * Basically, this returns TRUE, if @x has exactly one bit set. - * For negative values and zero, this always returns FALSE. */ -#define nm_utils_is_power_of_two(x) ({ \ - typeof(x) __x = (x); \ - \ - ( (__x > ((typeof(__x)) 0)) \ - && ((__x & (__x - (((typeof(__x)) 1)))) == ((typeof(__x)) 0))); \ - }) - -#define NM_DIV_ROUND_UP(x, y) \ - ({ \ - const typeof(x) _x = (x); \ - const typeof(y) _y = (y); \ - \ - (_x / _y + !!(_x % _y)); \ - }) - -/*****************************************************************************/ - -#define NM_UTILS_LOOKUP_DEFAULT(v) return (v) -#define NM_UTILS_LOOKUP_DEFAULT_WARN(v) g_return_val_if_reached (v) -#define NM_UTILS_LOOKUP_DEFAULT_NM_ASSERT(v) { nm_assert_not_reached (); return (v); } -#define NM_UTILS_LOOKUP_ITEM(v, n) (void) 0; case v: return (n); (void) 0 -#define NM_UTILS_LOOKUP_STR_ITEM(v, n) NM_UTILS_LOOKUP_ITEM(v, ""n"") -#define NM_UTILS_LOOKUP_ITEM_IGNORE(v) (void) 0; case v: break; (void) 0 -#define NM_UTILS_LOOKUP_ITEM_IGNORE_OTHER() (void) 0; default: break; (void) 0 - -#define _NM_UTILS_LOOKUP_DEFINE(scope, fcn_name, lookup_type, result_type, unknown_val, ...) \ -scope result_type \ -fcn_name (lookup_type val) \ -{ \ - switch (val) { \ - (void) 0, \ - __VA_ARGS__ \ - (void) 0; \ - }; \ - { unknown_val; } \ -} - -#define NM_UTILS_LOOKUP_STR_DEFINE(fcn_name, lookup_type, unknown_val, ...) \ - _NM_UTILS_LOOKUP_DEFINE (, fcn_name, lookup_type, const char *, unknown_val, __VA_ARGS__) -#define NM_UTILS_LOOKUP_STR_DEFINE_STATIC(fcn_name, lookup_type, unknown_val, ...) \ - _NM_UTILS_LOOKUP_DEFINE (static, fcn_name, lookup_type, const char *, unknown_val, __VA_ARGS__) - -/* Call the string-lookup-table function @fcn_name. If the function returns - * %NULL, the numeric index is converted to string using a alloca() buffer. - * Beware: this macro uses alloca(). */ -#define NM_UTILS_LOOKUP_STR_A(fcn_name, idx) \ - ({ \ - typeof (idx) _idx = (idx); \ - const char *_s; \ - \ - _s = fcn_name (_idx); \ - if (!_s) { \ - _s = g_alloca (30); \ - \ - g_snprintf ((char *) _s, 30, "(%lld)", (long long) _idx); \ - } \ - _s; \ - }) - -/*****************************************************************************/ - -/* check if @flags has exactly one flag (@check) set. You should call this - * only with @check being a compile time constant and a power of two. */ -#define NM_FLAGS_HAS(flags, check) \ - ( G_STATIC_ASSERT_EXPR ((check) > 0 && ((check) & ((check) - 1)) == 0), NM_FLAGS_ANY ((flags), (check)) ) - -#define NM_FLAGS_ANY(flags, check) ( ( ((flags) & (check)) != 0 ) ? TRUE : FALSE ) -#define NM_FLAGS_ALL(flags, check) ( ( ((flags) & (check)) == (check) ) ? TRUE : FALSE ) - -#define NM_FLAGS_SET(flags, val) ({ \ - const typeof(flags) _flags = (flags); \ - const typeof(flags) _val = (val); \ - \ - _flags | _val; \ - }) - -#define NM_FLAGS_UNSET(flags, val) ({ \ - const typeof(flags) _flags = (flags); \ - const typeof(flags) _val = (val); \ - \ - _flags & (~_val); \ - }) - -#define NM_FLAGS_ASSIGN(flags, val, assign) ({ \ - const typeof(flags) _flags = (flags); \ - const typeof(flags) _val = (val); \ - \ - (assign) \ - ? _flags | (_val) \ - : _flags & (~_val); \ - }) - -/*****************************************************************************/ - -#define _NM_BACKPORT_SYMBOL_IMPL(version, return_type, orig_func, versioned_func, args_typed, args) \ -return_type versioned_func args_typed; \ -_nm_externally_visible return_type versioned_func args_typed \ -{ \ - return orig_func args; \ -} \ -return_type orig_func args_typed; \ -__asm__(".symver "G_STRINGIFY(versioned_func)", "G_STRINGIFY(orig_func)"@"G_STRINGIFY(version)) - -#define NM_BACKPORT_SYMBOL(version, return_type, func, args_typed, args) \ -_NM_BACKPORT_SYMBOL_IMPL(version, return_type, func, _##func##_##version, args_typed, args) - -/*****************************************************************************/ - -#define nm_str_skip_leading_spaces(str) \ - ({ \ - typeof (*(str)) *_str = (str); \ - _nm_unused const char *_str_type_check = _str; \ - \ - if (_str) { \ - while (g_ascii_isspace (_str[0])) \ - _str++; \ - } \ - _str; \ - }) - -static inline char * -nm_strstrip (char *str) -{ - /* g_strstrip doesn't like NULL. */ - return str ? g_strstrip (str) : NULL; -} - -static inline const char * -nm_strstrip_avoid_copy (const char *str, char **str_free) -{ - gsize l; - char *s; - - nm_assert (str_free && !*str_free); - - if (!str) - return NULL; - - str = nm_str_skip_leading_spaces (str); - l = strlen (str); - if ( l == 0 - || !g_ascii_isspace (str[l - 1])) - return str; - while ( l > 0 - && g_ascii_isspace (str[l - 1])) - l--; - - s = g_new (char, l + 1); - memcpy (s, str, l); - s[l] = '\0'; - *str_free = s; - return s; -} - -/* g_ptr_array_sort()'s compare function takes pointers to the - * value. Thus, you cannot use strcmp directly. You can use - * nm_strcmp_p(). - * - * Like strcmp(), this function is not forgiving to accept %NULL. */ -static inline int -nm_strcmp_p (gconstpointer a, gconstpointer b) -{ - const char *s1 = *((const char **) a); - const char *s2 = *((const char **) b); - - return strcmp (s1, s2); -} - -/*****************************************************************************/ - -/* Taken from systemd's UNIQ_T and UNIQ macros. */ - -#define NM_UNIQ_T(x, uniq) G_PASTE(__unique_prefix_, G_PASTE(x, uniq)) -#define NM_UNIQ __COUNTER__ - -/*****************************************************************************/ - -/* glib's MIN()/MAX() macros don't have function-like behavior, in that they evaluate - * the argument possibly twice. - * - * Taken from systemd's MIN()/MAX() macros. */ - -#define NM_MIN(a, b) __NM_MIN(NM_UNIQ, a, NM_UNIQ, b) -#define __NM_MIN(aq, a, bq, b) \ - ({ \ - typeof (a) NM_UNIQ_T(A, aq) = (a); \ - typeof (b) NM_UNIQ_T(B, bq) = (b); \ - ((NM_UNIQ_T(A, aq) < NM_UNIQ_T(B, bq)) ? NM_UNIQ_T(A, aq) : NM_UNIQ_T(B, bq)); \ - }) - -#define NM_MAX(a, b) __NM_MAX(NM_UNIQ, a, NM_UNIQ, b) -#define __NM_MAX(aq, a, bq, b) \ - ({ \ - typeof (a) NM_UNIQ_T(A, aq) = (a); \ - typeof (b) NM_UNIQ_T(B, bq) = (b); \ - ((NM_UNIQ_T(A, aq) > NM_UNIQ_T(B, bq)) ? NM_UNIQ_T(A, aq) : NM_UNIQ_T(B, bq)); \ - }) - -#define NM_CLAMP(x, low, high) __NM_CLAMP(NM_UNIQ, x, NM_UNIQ, low, NM_UNIQ, high) -#define __NM_CLAMP(xq, x, lowq, low, highq, high) \ - ({ \ - typeof(x)NM_UNIQ_T(X,xq) = (x); \ - typeof(low) NM_UNIQ_T(LOW,lowq) = (low); \ - typeof(high) NM_UNIQ_T(HIGH,highq) = (high); \ - \ - ( (NM_UNIQ_T(X,xq) > NM_UNIQ_T(HIGH,highq)) \ - ? NM_UNIQ_T(HIGH,highq) \ - : (NM_UNIQ_T(X,xq) < NM_UNIQ_T(LOW,lowq)) \ - ? NM_UNIQ_T(LOW,lowq) \ - : NM_UNIQ_T(X,xq)); \ - }) - -#define NM_MAX_WITH_CMP(cmp, a, b) \ - ({ \ - typeof (a) _a = (a); \ - typeof (b) _b = (b); \ - \ - ( ((cmp (_a, _b)) >= 0) \ - ? _a \ - : _b); \ - }) - -/* evaluates to (void) if _A or _B are not constant or of different types */ -#define NM_CONST_MAX(_A, _B) \ - (__builtin_choose_expr (( __builtin_constant_p (_A) \ - && __builtin_constant_p (_B) \ - && __builtin_types_compatible_p (typeof (_A), typeof (_B))), \ - ((_A) > (_B)) ? (_A) : (_B), \ - ((void) 0))) - -/*****************************************************************************/ - -static inline guint -nm_encode_version (guint major, guint minor, guint micro) -{ - /* analog to the preprocessor macro NM_ENCODE_VERSION(). */ - return (major << 16) | (minor << 8) | micro; -} - -static inline void -nm_decode_version (guint version, guint *major, guint *minor, guint *micro) -{ - *major = (version & 0xFFFF0000u) >> 16; - *minor = (version & 0x0000FF00u) >> 8; - *micro = (version & 0x000000FFu); -} - -/*****************************************************************************/ - -/* taken from systemd's DECIMAL_STR_MAX() - * - * Returns the number of chars needed to format variables of the - * specified type as a decimal string. Adds in extra space for a - * negative '-' prefix (hence works correctly on signed - * types). Includes space for the trailing NUL. */ -#define NM_DECIMAL_STR_MAX(type) \ - (2+(sizeof(type) <= 1 ? 3 : \ - sizeof(type) <= 2 ? 5 : \ - sizeof(type) <= 4 ? 10 : \ - sizeof(type) <= 8 ? 20 : sizeof(int[-2*(sizeof(type) > 8)]))) - -/*****************************************************************************/ - -/* if @str is NULL, return "(null)". Otherwise, allocate a buffer using - * alloca() of and fill it with @str. @str will be quoted with double quote. - * If @str is longer then @trunc_at, the string is truncated and the closing - * quote is instead '^' to indicate truncation. - * - * Thus, the maximum stack allocated buffer will be @trunc_at+3. The maximum - * buffer size must be a constant and not larger than 300. */ -#define nm_strquote_a(trunc_at, str) \ - ({ \ - const char *const _str = (str); \ - \ - (_str \ - ? ({ \ - const gsize _trunc_at = (trunc_at); \ - const gsize _strlen_trunc = NM_MIN (strlen (_str), _trunc_at); \ - char *_buf; \ - \ - G_STATIC_ASSERT_EXPR ((trunc_at) <= 300); \ - \ - _buf = g_alloca (_strlen_trunc + 3); \ - _buf[0] = '"'; \ - memcpy (&_buf[1], _str, _strlen_trunc); \ - _buf[_strlen_trunc + 1] = _str[_strlen_trunc] ? '^' : '"'; \ - _buf[_strlen_trunc + 2] = '\0'; \ - _buf; \ - }) \ - : "(null)"); \ - }) - -#define nm_sprintf_buf(buf, format, ...) \ - ({ \ - char * _buf = (buf); \ - int _buf_len; \ - \ - /* some static assert trying to ensure that the buffer is statically allocated. - * It disallows a buffer size of sizeof(gpointer) to catch that. */ \ - G_STATIC_ASSERT (G_N_ELEMENTS (buf) == sizeof (buf) && sizeof (buf) != sizeof (char *)); \ - _buf_len = g_snprintf (_buf, sizeof (buf), \ - ""format"", ##__VA_ARGS__); \ - nm_assert (_buf_len < sizeof (buf)); \ - _buf; \ - }) - -/* it is "unsafe" because @bufsize must not be a constant expression and - * there is no check at compiletime. Regardless of that, the buffer size - * must not be larger than 300 bytes, as this gets stack allocated. */ -#define nm_sprintf_buf_unsafe_a(bufsize, format, ...) \ - ({ \ - char *_buf; \ - int _buf_len; \ - typeof (bufsize) _bufsize = (bufsize); \ - \ - nm_assert (_bufsize <= 300); \ - \ - _buf = g_alloca (_bufsize); \ - _buf_len = g_snprintf (_buf, _bufsize, \ - ""format"", ##__VA_ARGS__); \ - nm_assert (_buf_len >= 0 && _buf_len < _bufsize); \ - _buf; \ - }) - -#define nm_sprintf_bufa(bufsize, format, ...) \ - ({ \ - G_STATIC_ASSERT_EXPR ((bufsize) <= 300); \ - nm_sprintf_buf_unsafe_a ((bufsize), format, ##__VA_ARGS__); \ - }) - -/* aims to alloca() a buffer and fill it with printf(format, name). - * Note that format must not contain any format specifier except - * "%s". - * If the resulting string would be too large for stack allocation, - * it allocates a buffer with g_malloc() and assigns it to *p_val_to_free. */ -#define nm_construct_name_a(format, name, p_val_to_free) \ - ({ \ - const char *const _name = (name); \ - char **const _p_val_to_free = (p_val_to_free); \ - const gsize _name_len = strlen (_name); \ - char *_buf2; \ - \ - nm_assert (_p_val_to_free && !*_p_val_to_free); \ - if ( NM_STRLEN (format) <= 290 \ - && _name_len < (gsize) (290 - NM_STRLEN (format))) \ - _buf2 = nm_sprintf_buf_unsafe_a (NM_STRLEN (format) + _name_len, format, _name); \ - else { \ - _buf2 = g_strdup_printf (format, _name); \ - *_p_val_to_free = _buf2; \ - } \ - (const char *) _buf2; \ - }) - -/*****************************************************************************/ - -/** - * The boolean type _Bool is C99 while we mostly stick to C89. However, _Bool is too - * convenient to miss and is effectively available in gcc and clang. So, just use it. - * - * Usually, one would include "stdbool.h" to get the "bool" define which aliases - * _Bool. We provide this define here, because we want to make use of it anywhere. - * (also, stdbool.h is again C99). - * - * Using _Bool has advantages over gboolean: - * - * - commonly _Bool is one byte large, instead of gboolean's 4 bytes (because gboolean - * is a typedef for int). Especially when having boolean fields in a struct, we can - * thereby easily save some space. - * - * - _Bool type guarantees that two "true" expressions compare equal. E.g. the following - * will not work: - * gboolean v1 = 1; - * gboolean v2 = 2; - * g_assert_cmpint (v1, ==, v2); // will fail - * For that, we often to use !! to coerce gboolean values to 0 or 1: - * g_assert_cmpint (!!v2, ==, TRUE); - * With _Bool type, this will be handled properly by the compiler. - * - * - For structs, we might want to safe even more space and use bitfields: - * struct s1 { - * gboolean v1:1; - * }; - * But the problem here is that gboolean is signed, so that - * v1 will be either 0 or -1 (not 1, TRUE). Thus, the following - * fails: - * struct s1 s = { .v1 = TRUE, }; - * g_assert_cmpint (s1.v1, ==, TRUE); - * It will however work just fine with bool/_Bool while retaining the - * notion of having a boolean value. - * - * Also, add the defines for "true" and "false". Those are nicely highlighted by the editor - * as special types, contrary to glib's "TRUE"/"FALSE". - */ - -#ifndef bool -#define bool _Bool -#define true 1 -#define false 0 -#endif - -#ifdef _G_BOOLEAN_EXPR -/* g_assert() uses G_LIKELY(), which in turn uses _G_BOOLEAN_EXPR(). - * As glib's implementation uses a local variable _g_boolean_var_, - * we cannot do - * g_assert (some_macro ()); - * where some_macro() itself expands to ({g_assert(); ...}). - * In other words, you cannot have a g_assert() inside a g_assert() - * without getting a -Werror=shadow failure. - * - * Workaround that by re-defining _G_BOOLEAN_EXPR() - **/ -#undef _G_BOOLEAN_EXPR -#define __NM_G_BOOLEAN_EXPR_IMPL(v, expr) \ - ({ \ - int NM_UNIQ_T(V, v); \ - \ - if (expr) \ - NM_UNIQ_T(V, v) = 1; \ - else \ - NM_UNIQ_T(V, v) = 0; \ - NM_UNIQ_T(V, v); \ - }) -#define _G_BOOLEAN_EXPR(expr) __NM_G_BOOLEAN_EXPR_IMPL (NM_UNIQ, expr) -#endif - -/*****************************************************************************/ - -/** - * nm_steal_int: - * @p_val: pointer to an int type. - * - * Returns: *p_val and sets *p_val to zero the same time. - * Accepts %NULL, in which case also numeric 0 will be returned. - */ -#define nm_steal_int(p_val) \ - ({ \ - typeof (p_val) const _p_val = (p_val); \ - typeof (*_p_val) _val = 0; \ - \ - if ( _p_val \ - && (_val = *_p_val)) { \ - *_p_val = 0; \ - } \ - _val; \ - }) - -static inline int -nm_steal_fd (int *p_fd) -{ - int fd; - - if ( p_fd - && ((fd = *p_fd) >= 0)) { - *p_fd = -1; - return fd; - } - return -1; -} - -/** - * nm_close: - * - * Like close() but throws an assertion if the input fd is - * invalid. Closing an invalid fd is a programming error, so - * it's better to catch it early. - */ -static inline int -nm_close (int fd) -{ - int r; - - r = close (fd); - nm_assert (r != -1 || fd < 0 || errno != EBADF); - return r; -} - -#define NM_PID_T_INVAL ((pid_t) -1) - -#endif /* __NM_MACROS_INTERNAL_H__ */ diff --git a/shared/nm-utils/nm-obj.h b/shared/nm-utils/nm-obj.h deleted file mode 100644 index 4edd1f3e..00000000 --- a/shared/nm-utils/nm-obj.h +++ /dev/null @@ -1,82 +0,0 @@ -/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ -/* NetworkManager -- Network link manager - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the - * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, - * Boston, MA 02110-1301 USA. - * - * (C) Copyright 2017 Red Hat, Inc. - */ - -#ifndef __NM_OBJ_H__ -#define __NM_OBJ_H__ - -/*****************************************************************************/ - -#define NM_OBJ_REF_COUNT_STACKINIT (G_MAXINT) - -typedef struct _NMObjBaseInst NMObjBaseInst; -typedef struct _NMObjBaseClass NMObjBaseClass; - -struct _NMObjBaseInst { - /* The first field of NMObjBaseInst is compatible with GObject. - * Basically, NMObjBaseInst is an abstract base type of GTypeInstance. - * - * If you do it right, you may derive a type of NMObjBaseInst as a proper GTypeInstance. - * That involves allocating a GType for it, which can be inconvenient because - * a GType is dynamically created (and the class can no longer be immutable - * memory). - * - * Even if your implementation of NMObjBaseInst is not a full fledged GType(Instance), - * you still can use GTypeInstances in the same context as you can decide based on the - * NMObjBaseClass with what kind of object you are dealing with. - * - * Basically, the only thing NMObjBaseInst gives you is access to an - * NMObjBaseClass instance. - */ - union { - const NMObjBaseClass *klass; - GTypeInstance g_type_instance; - }; -}; - -struct _NMObjBaseClass { - /* NMObjBaseClass is the base class of all NMObjBaseInst implementations. - * Note that it is also an abstract super class of GTypeInstance, that means - * you may implement a NMObjBaseClass as a subtype of GTypeClass. - * - * For that to work, you must properly set the GTypeClass instance (and its - * GType). - * - * Note that to implement a NMObjBaseClass that is *not* a GTypeClass, you wouldn't - * set the GType. Hence, this field is only useful for type implementations that actually - * extend GTypeClass. - * - * In a way it is wrong that NMObjBaseClass has the GType member, because it is - * a base class of GTypeClass and doesn't necessarily use the GType. However, - * it is here so that G_TYPE_CHECK_INSTANCE_TYPE() and friends work correctly - * on any NMObjectClass. That means, while not necessary, it is convenient that - * a NMObjBaseClass has all members of GTypeClass. - * Also note that usually you have only one instance of a certain type, so this - * wastes just a few bytes for the unneeded GType. - */ - union { - GType g_type; - GTypeClass g_type_class; - }; -}; - -/*****************************************************************************/ - -#endif /* __NM_OBJ_H__ */ diff --git a/shared/nm-utils/nm-random-utils.c b/shared/nm-utils/nm-random-utils.c deleted file mode 100644 index d7c7da42..00000000 --- a/shared/nm-utils/nm-random-utils.c +++ /dev/null @@ -1,165 +0,0 @@ -/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ -/* NetworkManager -- Network link manager - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the - * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, - * Boston, MA 02110-1301 USA. - * - * (C) Copyright 2017 Red Hat, Inc. - */ - -#include "nm-default.h" - -#include "nm-random-utils.h" - -#include - -#if USE_SYS_RANDOM_H -#include -#else -#include -#endif - -#include "nm-shared-utils.h" - -/*****************************************************************************/ - -/** - * nm_utils_random_bytes: - * @p: the buffer to fill - * @n: the number of bytes to write to @p. - * - * Uses getrandom() or reads /dev/urandom to fill the buffer - * with random data. If all fails, as last fallback it uses - * GRand to fill the buffer with pseudo random numbers. - * The function always succeeds in writing some random numbers - * to the buffer. The return value of FALSE indicates that the - * obtained bytes are probably not of good randomness. - * - * Returns: whether the written bytes are good. If you - * don't require good randomness, you can ignore the return - * value. - * - * Note that if calling getrandom() fails because there is not enough - * entropy (at early boot), the function will read /dev/urandom. - * Which of course, still has low entropy, and cause kernel to log - * a warning. - */ -gboolean -nm_utils_random_bytes (void *p, size_t n) -{ - int fd; - int r; - gboolean has_high_quality = TRUE; - gboolean urandom_success; - guint8 *buf = p; - gboolean avoid_urandom = FALSE; - - g_return_val_if_fail (p, FALSE); - g_return_val_if_fail (n > 0, FALSE); - -#if HAVE_GETRANDOM - { - static gboolean have_syscall = TRUE; - - if (have_syscall) { - r = getrandom (buf, n, GRND_NONBLOCK); - if (r > 0) { - if ((size_t) r == n) - return TRUE; - - /* no or partial read. There is not enough entropy. - * Fill the rest reading from urandom, and remember that - * some bits are not high quality. */ - nm_assert (r < n); - buf += r; - n -= r; - has_high_quality = FALSE; - - /* At this point, we don't want to read /dev/urandom, because - * the entropy pool is low (early boot?), and asking for more - * entropy causes kernel messages to be logged. - * - * We use our fallback via GRand. Note that g_rand_new() also - * tries to seed itself with data from /dev/urandom, but since - * we reuse the instance, it shouldn't matter. */ - avoid_urandom = TRUE; - } else { - if (errno == ENOSYS) { - /* no support for getrandom(). We don't know whether - * we urandom will give us good quality. Assume yes. */ - have_syscall = FALSE; - } else { - /* unknown error. We'll read urandom below, but we don't have - * high-quality randomness. */ - has_high_quality = FALSE; - } - } - } - } -#endif - - urandom_success = FALSE; - if (!avoid_urandom) { -fd_open: - fd = open ("/dev/urandom", O_RDONLY | O_CLOEXEC | O_NOCTTY); - if (fd < 0) { - r = errno; - if (r == EINTR) - goto fd_open; - } else { - r = nm_utils_fd_read_loop_exact (fd, buf, n, TRUE); - nm_close (fd); - if (r >= 0) - urandom_success = TRUE; - } - } - - if (!urandom_success) { - static _nm_thread_local GRand *rand = NULL; - gsize i; - int j; - - /* we failed to fill the bytes reading from urandom. - * Fill the bits using GRand pseudo random numbers. - * - * We don't have good quality. - */ - has_high_quality = FALSE; - - if (G_UNLIKELY (!rand)) - rand = g_rand_new (); - - nm_assert (n > 0); - i = 0; - for (;;) { - const union { - guint32 v32; - guint8 v8[4]; - } v = { - .v32 = g_rand_int (rand), - }; - - for (j = 0; j < 4; ) { - buf[i++] = v.v8[j++]; - if (i >= n) - goto done; - } - } -done: - ; - } - - return has_high_quality; -} diff --git a/shared/nm-utils/nm-random-utils.h b/shared/nm-utils/nm-random-utils.h deleted file mode 100644 index 15a118d3..00000000 --- a/shared/nm-utils/nm-random-utils.h +++ /dev/null @@ -1,27 +0,0 @@ -/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ -/* NetworkManager -- Network link manager - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the - * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, - * Boston, MA 02110-1301 USA. - * - * (C) Copyright 2017 Red Hat, Inc. - */ - -#ifndef __NM_RANDOM_UTILS_H__ -#define __NM_RANDOM_UTILS_H__ - -gboolean nm_utils_random_bytes (void *p, size_t n); - -#endif /* __NM_RANDOM_UTILS_H__ */ diff --git a/shared/nm-utils/nm-secret-utils.c b/shared/nm-utils/nm-secret-utils.c deleted file mode 100644 index ec5cc6b1..00000000 --- a/shared/nm-utils/nm-secret-utils.c +++ /dev/null @@ -1,161 +0,0 @@ -/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ -/* NetworkManager -- Network link manager - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the - * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, - * Boston, MA 02110-1301 USA. - * - * (C) Copyright 2018 Red Hat, Inc. - * (C) Copyright 2015 - 2019 Jason A. Donenfeld . All Rights Reserved. - */ - -#include "nm-default.h" - -#include "nm-secret-utils.h" - -/*****************************************************************************/ - -void -nm_explicit_bzero (void *s, gsize n) -{ - /* gracefully handle n == 0. This is important, callers rely on it. */ - if (n > 0) { - nm_assert (s); -#if defined (HAVE_DECL_EXPLICIT_BZERO) && HAVE_DECL_EXPLICIT_BZERO - explicit_bzero (s, n); -#else - /* don't bother with a workaround. Use a reasonable glibc. */ - memset (s, 0, n); -#endif - } -} - -/*****************************************************************************/ - -char * -nm_secret_strchomp (char *secret) -{ - gsize len; - - g_return_val_if_fail (secret, NULL); - - /* it's actually identical to g_strchomp(). However, - * the glib function does not document, that it clears the - * memory. For @secret, we don't only want to truncate trailing - * spaces, we want to overwrite them with NUL. */ - - len = strlen (secret); - while (len--) { - if (g_ascii_isspace ((guchar) secret[len])) - secret[len] = '\0'; - else - break; - } - - return secret; -} - -/*****************************************************************************/ - -GBytes * -nm_secret_copy_to_gbytes (gconstpointer mem, gsize mem_len) -{ - NMSecretBuf *b; - - if (mem_len == 0) - return g_bytes_new_static ("", 0); - - nm_assert (mem); - - /* NUL terminate the buffer. - * - * The entire buffer is already malloc'ed and likely has some room for padding. - * Thus, in many situations, this additional byte will cause no overhead in - * practice. - * - * Even if it causes an overhead, do it just for safety. Yes, the returned - * bytes is not a NUL terminated string and no user must rely on this. Do - * not treat binary data as NUL terminated strings, unless you know what - * you are doing. Anyway, defensive FTW. - */ - - b = nm_secret_buf_new (mem_len + 1); - memcpy (b->bin, mem, mem_len); - b->bin[mem_len] = 0; - return nm_secret_buf_to_gbytes_take (b, mem_len); -} - -/*****************************************************************************/ - -NMSecretBuf * -nm_secret_buf_new (gsize len) -{ - NMSecretBuf *secret; - - nm_assert (len > 0); - - secret = g_malloc (sizeof (NMSecretBuf) + len); - *((gsize *) &(secret->len)) = len; - return secret; -} - -static void -_secret_buf_free (gpointer user_data) -{ - NMSecretBuf *secret = user_data; - - nm_assert (secret); - nm_assert (secret->len > 0); - - nm_explicit_bzero (secret->bin, secret->len); - g_free (user_data); -} - -GBytes * -nm_secret_buf_to_gbytes_take (NMSecretBuf *secret, gssize actual_len) -{ - nm_assert (secret); - nm_assert (secret->len > 0); - nm_assert (actual_len == -1 || (actual_len >= 0 && actual_len <= secret->len)); - return g_bytes_new_with_free_func (secret->bin, - actual_len >= 0 ? (gsize) actual_len : secret->len, - _secret_buf_free, - secret); -} - -/*****************************************************************************/ - -/** - * nm_utils_memeqzero_secret: - * @data: the data pointer to check (may be %NULL if @length is zero). - * @length: the number of bytes to check. - * - * Checks that all bytes are zero. This always takes the same amount - * of time to prevent timing attacks. - * - * Returns: whether all bytes are zero. - */ -gboolean -nm_utils_memeqzero_secret (gconstpointer data, gsize length) -{ - const guint8 *const key = data; - volatile guint8 acc = 0; - gsize i; - - for (i = 0; i < length; i++) { - acc |= key[i]; - asm volatile("" : "=r"(acc) : "0"(acc)); - } - return 1 & ((acc - 1) >> 8); -} diff --git a/shared/nm-utils/nm-secret-utils.h b/shared/nm-utils/nm-secret-utils.h deleted file mode 100644 index 034ef7bd..00000000 --- a/shared/nm-utils/nm-secret-utils.h +++ /dev/null @@ -1,178 +0,0 @@ -/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ -/* NetworkManager -- Network link manager - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the - * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, - * Boston, MA 02110-1301 USA. - * - * (C) Copyright 2018 Red Hat, Inc. - */ - -#ifndef __NM_SECRET_UTILS_H__ -#define __NM_SECRET_UTILS_H__ - -#include "nm-macros-internal.h" - -/*****************************************************************************/ - -void nm_explicit_bzero (void *s, gsize n); - -/*****************************************************************************/ - -char *nm_secret_strchomp (char *secret); - -/*****************************************************************************/ - -static inline void -nm_free_secret (char *secret) -{ - if (secret) { - nm_explicit_bzero (secret, strlen (secret)); - g_free (secret); - } -} - -NM_AUTO_DEFINE_FCN0 (char *, _nm_auto_free_secret, nm_free_secret) -/** - * nm_auto_free_secret: - * - * Call g_free() on a variable location when it goes out of scope. - * Also, previously, calls memset(loc, 0, strlen(loc)) to clear out - * the secret. - */ -#define nm_auto_free_secret nm_auto(_nm_auto_free_secret) - -/*****************************************************************************/ - -GBytes *nm_secret_copy_to_gbytes (gconstpointer mem, gsize mem_len); - -/*****************************************************************************/ - -/* NMSecretPtr is a pair of malloc'ed data pointer and the length of the - * data. The purpose is to use it in combination with nm_auto_clear_secret_ptr - * which ensures that the data pointer (with all len bytes) is cleared upon - * cleanup. */ -typedef struct { - gsize len; - - /* the data pointer. This pointer must be allocated with malloc (at least - * when used with nm_secret_ptr_clear()). */ - union { - char *str; - void *ptr; - guint8 *bin; - }; -} NMSecretPtr; - -static inline void -nm_secret_ptr_bzero (NMSecretPtr *secret) -{ - if (secret) { - if (secret->len > 0) { - if (secret->ptr) - nm_explicit_bzero (secret->ptr, secret->len); - } - } -} - -#define nm_auto_bzero_secret_ptr nm_auto(nm_secret_ptr_bzero) - -static inline void -nm_secret_ptr_clear (NMSecretPtr *secret) -{ - if (secret) { - if (secret->len > 0) { - if (secret->ptr) - nm_explicit_bzero (secret->ptr, secret->len); - secret->len = 0; - } - nm_clear_g_free (&secret->ptr); - } -} - -#define nm_auto_clear_secret_ptr nm_auto(nm_secret_ptr_clear) - -#define NM_SECRET_PTR_INIT() \ - ((const NMSecretPtr) { \ - .len = 0, \ - .ptr = NULL, \ - }) - -#define NM_SECRET_PTR_STATIC(_len) \ - ((const NMSecretPtr) { \ - .len = _len, \ - .ptr = ((guint8 [_len]) { }), \ - }) - -#define NM_SECRET_PTR_ARRAY(_arr) \ - ((const NMSecretPtr) { \ - .len = G_N_ELEMENTS (_arr) * sizeof ((_arr)[0]), \ - .ptr = &((_arr)[0]), \ - }) - -static inline void -nm_secret_ptr_clear_static (const NMSecretPtr *secret) -{ - if (secret) { - if (secret->len > 0) { - nm_assert (secret->ptr); - nm_explicit_bzero (secret->ptr, secret->len); - } - } -} - -#define nm_auto_clear_static_secret_ptr nm_auto(nm_secret_ptr_clear_static) - -static inline void -nm_secret_ptr_move (NMSecretPtr *dst, NMSecretPtr *src) -{ - if (dst && dst != src) { - *dst = *src; - src->len = 0; - src->ptr = NULL; - } -} - -/*****************************************************************************/ - -typedef struct { - const gsize len; - union { - char str[0]; - guint8 bin[0]; - }; -} NMSecretBuf; - -static inline void -_nm_auto_free_secret_buf (NMSecretBuf **ptr) -{ - NMSecretBuf *b = *ptr; - - if (b) { - nm_assert (b->len > 0); - nm_explicit_bzero (b->bin, b->len); - g_free (b); - } -} -#define nm_auto_free_secret_buf nm_auto(_nm_auto_free_secret_buf) - -NMSecretBuf *nm_secret_buf_new (gsize len); - -GBytes *nm_secret_buf_to_gbytes_take (NMSecretBuf *secret, gssize actual_len); - -/*****************************************************************************/ - -gboolean nm_utils_memeqzero_secret (gconstpointer data, gsize length); - -#endif /* __NM_SECRET_UTILS_H__ */ diff --git a/shared/nm-utils/nm-shared-utils.c b/shared/nm-utils/nm-shared-utils.c deleted file mode 100644 index 6a43c670..00000000 --- a/shared/nm-utils/nm-shared-utils.c +++ /dev/null @@ -1,2741 +0,0 @@ -/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ -/* NetworkManager -- Network link manager - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the - * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, - * Boston, MA 02110-1301 USA. - * - * (C) Copyright 2016 Red Hat, Inc. - */ - -#include "nm-default.h" - -#include "nm-shared-utils.h" - -#include -#include -#include -#include - -#include "nm-errno.h" - -/*****************************************************************************/ - -const void *const _NM_PTRARRAY_EMPTY[1] = { NULL }; - -/*****************************************************************************/ - -const NMIPAddr nm_ip_addr_zero = { }; - -/* this initializes a struct in_addr/in6_addr and allows for untrusted - * arguments (like unsuitable @addr_family or @src_len). It's almost safe - * in the sense that it verifies input arguments strictly. Also, it - * uses memcpy() to access @src, so alignment is not an issue. - * - * Only potential pitfalls: - * - * - it allows for @addr_family to be AF_UNSPEC. If that is the case (and the - * caller allows for that), the caller MUST provide @out_addr_family. - * - when setting @dst to an IPv4 address, the trailing bytes are not touched. - * Meaning, if @dst is an NMIPAddr union, only the first bytes will be set. - * If that matter to you, clear @dst before. */ -gboolean -nm_ip_addr_set_from_untrusted (int addr_family, - gpointer dst, - gconstpointer src, - gsize src_len, - int *out_addr_family) -{ - nm_assert (dst); - - switch (addr_family) { - case AF_UNSPEC: - if (!out_addr_family) { - /* when the callers allow undefined @addr_family, they must provide - * an @out_addr_family argument. */ - nm_assert_not_reached (); - return FALSE; - } - switch (src_len) { - case sizeof (struct in_addr): addr_family = AF_INET; break; - case sizeof (struct in6_addr): addr_family = AF_INET6; break; - default: - return FALSE; - } - break; - case AF_INET: - if (src_len != sizeof (struct in_addr)) - return FALSE; - break; - case AF_INET6: - if (src_len != sizeof (struct in6_addr)) - return FALSE; - break; - default: - /* when the callers allow undefined @addr_family, they must provide - * an @out_addr_family argument. */ - nm_assert (out_addr_family); - return FALSE; - } - - nm_assert (src); - - memcpy (dst, src, src_len); - NM_SET_OUT (out_addr_family, addr_family); - return TRUE; -} - -/*****************************************************************************/ - -pid_t -nm_utils_gettid (void) -{ - return (pid_t) syscall (SYS_gettid); -} - -/* Used for asserting that this function is called on the main-thread. - * The main-thread is determined by remembering the thread-id - * of when the function was called the first time. - * - * When forking, the thread-id is again reset upon first call. */ -gboolean -_nm_assert_on_main_thread (void) -{ - G_LOCK_DEFINE_STATIC (lock); - static pid_t seen_tid; - static pid_t seen_pid; - pid_t tid; - pid_t pid; - gboolean success = FALSE; - - tid = nm_utils_gettid (); - nm_assert (tid != 0); - - G_LOCK (lock); - - if (G_LIKELY (tid == seen_tid)) { - /* we don't care about false positives (when the process forked, and the thread-id - * is accidentally re-used) . It's for assertions only. */ - success = TRUE; - } else { - pid = getpid (); - nm_assert (pid != 0); - - if ( seen_tid == 0 - || seen_pid != pid) { - /* either this is the first time we call the function, or the process - * forked. In both cases, remember the thread-id. */ - seen_tid = tid; - seen_pid = pid; - success = TRUE; - } - } - - G_UNLOCK (lock); - - return success; -} - -/*****************************************************************************/ - -void -nm_utils_strbuf_append_c (char **buf, gsize *len, char c) -{ - switch (*len) { - case 0: - return; - case 1: - (*buf)[0] = '\0'; - *len = 0; - (*buf)++; - return; - default: - (*buf)[0] = c; - (*buf)[1] = '\0'; - (*len)--; - (*buf)++; - return; - } -} - -void -nm_utils_strbuf_append_bin (char **buf, gsize *len, gconstpointer str, gsize str_len) -{ - switch (*len) { - case 0: - return; - case 1: - if (str_len == 0) { - (*buf)[0] = '\0'; - return; - } - (*buf)[0] = '\0'; - *len = 0; - (*buf)++; - return; - default: - if (str_len == 0) { - (*buf)[0] = '\0'; - return; - } - if (str_len >= *len) { - memcpy (*buf, str, *len - 1); - (*buf)[*len - 1] = '\0'; - *buf = &(*buf)[*len]; - *len = 0; - } else { - memcpy (*buf, str, str_len); - *buf = &(*buf)[str_len]; - (*buf)[0] = '\0'; - *len -= str_len; - } - return; - } -} - -void -nm_utils_strbuf_append_str (char **buf, gsize *len, const char *str) -{ - gsize src_len; - - switch (*len) { - case 0: - return; - case 1: - if (!str || !*str) { - (*buf)[0] = '\0'; - return; - } - (*buf)[0] = '\0'; - *len = 0; - (*buf)++; - return; - default: - if (!str || !*str) { - (*buf)[0] = '\0'; - return; - } - src_len = g_strlcpy (*buf, str, *len); - if (src_len >= *len) { - *buf = &(*buf)[*len]; - *len = 0; - } else { - *buf = &(*buf)[src_len]; - *len -= src_len; - } - return; - } -} - -void -nm_utils_strbuf_append (char **buf, gsize *len, const char *format, ...) -{ - char *p = *buf; - va_list args; - int retval; - - if (*len == 0) - return; - - va_start (args, format); - retval = g_vsnprintf (p, *len, format, args); - va_end (args); - - if ((gsize) retval >= *len) { - *buf = &p[*len]; - *len = 0; - } else { - *buf = &p[retval]; - *len -= retval; - } -} - -/** - * nm_utils_strbuf_seek_end: - * @buf: the input/output buffer - * @len: the input/output length of the buffer. - * - * Commonly, one uses nm_utils_strbuf_append*(), to incrementally - * append strings to the buffer. However, sometimes we need to use - * existing API to write to the buffer. - * After doing so, we want to adjust the buffer counter. - * Essentially, - * - * g_snprintf (buf, len, ...); - * nm_utils_strbuf_seek_end (&buf, &len); - * - * is almost the same as - * - * nm_utils_strbuf_append (&buf, &len, ...); - * - * The only difference is the behavior when the string got truncated: - * nm_utils_strbuf_append() will recognize that and set the remaining - * length to zero. - * - * In general, the behavior is: - * - * - if *len is zero, do nothing - * - if the buffer contains a NUL byte within the first *len characters, - * the buffer is pointed to the NUL byte and len is adjusted. In this - * case, the remaining *len is always >= 1. - * In particular, that is also the case if the NUL byte is at the very last - * position ((*buf)[*len -1]). That happens, when the previous operation - * either fit the string exactly into the buffer or the string was truncated - * by g_snprintf(). The difference cannot be determined. - * - if the buffer contains no NUL bytes within the first *len characters, - * write NUL at the last position, set *len to zero, and point *buf past - * the NUL byte. This would happen with - * - * strncpy (buf, long_str, len); - * nm_utils_strbuf_seek_end (&buf, &len). - * - * where strncpy() does truncate the string and not NUL terminate it. - * nm_utils_strbuf_seek_end() would then NUL terminate it. - */ -void -nm_utils_strbuf_seek_end (char **buf, gsize *len) -{ - gsize l; - char *end; - - nm_assert (len); - nm_assert (buf && *buf); - - if (*len <= 1) { - if ( *len == 1 - && (*buf)[0]) - goto truncate; - return; - } - - end = memchr (*buf, 0, *len); - if (end) { - l = end - *buf; - nm_assert (l < *len); - - *buf = end; - *len -= l; - return; - } - -truncate: - /* hm, no NUL character within len bytes. - * Just NUL terminate the array and consume them - * all. */ - *buf += *len; - (*buf)[-1] = '\0'; - *len = 0; - return; -} - -/*****************************************************************************/ - -/** - * nm_utils_gbytes_equals: - * @bytes: (allow-none): a #GBytes array to compare. Note that - * %NULL is treated like an #GBytes array of length zero. - * @mem_data: the data pointer with @mem_len bytes - * @mem_len: the length of the data pointer - * - * Returns: %TRUE if @bytes contains the same data as @mem_data. As a - * special case, a %NULL @bytes is treated like an empty array. - */ -gboolean -nm_utils_gbytes_equal_mem (GBytes *bytes, - gconstpointer mem_data, - gsize mem_len) -{ - gconstpointer p; - gsize l; - - if (!bytes) { - /* as a special case, let %NULL GBytes compare idential - * to an empty array. */ - return (mem_len == 0); - } - - p = g_bytes_get_data (bytes, &l); - return l == mem_len - && ( mem_len == 0 /* allow @mem_data to be %NULL */ - || memcmp (p, mem_data, mem_len) == 0); -} - -GVariant * -nm_utils_gbytes_to_variant_ay (GBytes *bytes) -{ - const guint8 *p; - gsize l; - - if (!bytes) { - /* for convenience, accept NULL to return an empty variant */ - return g_variant_new_array (G_VARIANT_TYPE_BYTE, NULL, 0); - } - - p = g_bytes_get_data (bytes, &l); - return g_variant_new_fixed_array (G_VARIANT_TYPE_BYTE, p, l, 1); -} - -/*****************************************************************************/ - -/** - * nm_strquote: - * @buf: the output buffer of where to write the quoted @str argument. - * @buf_len: the size of @buf. - * @str: (allow-none): the string to quote. - * - * Writes @str to @buf with quoting. The resulting buffer - * is always NUL terminated, unless @buf_len is zero. - * If @str is %NULL, it writes "(null)". - * - * If @str needs to be truncated, the closing quote is '^' instead - * of '"'. - * - * This is similar to nm_strquote_a(), which however uses alloca() - * to allocate a new buffer. Also, here @buf_len is the size of @buf, - * while nm_strquote_a() has the number of characters to print. The latter - * doesn't include the quoting. - * - * Returns: the input buffer with the quoted string. - */ -const char * -nm_strquote (char *buf, gsize buf_len, const char *str) -{ - const char *const buf0 = buf; - - if (!str) { - nm_utils_strbuf_append_str (&buf, &buf_len, "(null)"); - goto out; - } - - if (G_UNLIKELY (buf_len <= 2)) { - switch (buf_len) { - case 2: - *(buf++) = '^'; - /* fall-through */ - case 1: - *(buf++) = '\0'; - break; - } - goto out; - } - - *(buf++) = '"'; - buf_len--; - - nm_utils_strbuf_append_str (&buf, &buf_len, str); - - /* if the string was too long we indicate truncation with a - * '^' instead of a closing quote. */ - if (G_UNLIKELY (buf_len <= 1)) { - switch (buf_len) { - case 1: - buf[-1] = '^'; - break; - case 0: - buf[-2] = '^'; - break; - default: - nm_assert_not_reached (); - break; - } - } else { - nm_assert (buf_len >= 2); - *(buf++) = '"'; - *(buf++) = '\0'; - } - -out: - return buf0; -} - -/*****************************************************************************/ - -char _nm_utils_to_string_buffer[]; - -void -nm_utils_to_string_buffer_init (char **buf, gsize *len) -{ - if (!*buf) { - *buf = _nm_utils_to_string_buffer; - *len = sizeof (_nm_utils_to_string_buffer); - } -} - -gboolean -nm_utils_to_string_buffer_init_null (gconstpointer obj, char **buf, gsize *len) -{ - nm_utils_to_string_buffer_init (buf, len); - if (!obj) { - g_strlcpy (*buf, "(null)", *len); - return FALSE; - } - return TRUE; -} - -/*****************************************************************************/ - -const char * -nm_utils_flags2str (const NMUtilsFlags2StrDesc *descs, - gsize n_descs, - unsigned flags, - char *buf, - gsize len) -{ - gsize i; - char *p; - -#if NM_MORE_ASSERTS > 10 - nm_assert (descs); - nm_assert (n_descs > 0); - for (i = 0; i < n_descs; i++) { - gsize j; - - nm_assert (descs[i].name && descs[i].name[0]); - for (j = 0; j < i; j++) - nm_assert (descs[j].flag != descs[i].flag); - } -#endif - - nm_utils_to_string_buffer_init (&buf, &len); - - if (!len) - return buf; - - buf[0] = '\0'; - p = buf; - if (!flags) { - for (i = 0; i < n_descs; i++) { - if (!descs[i].flag) { - nm_utils_strbuf_append_str (&p, &len, descs[i].name); - break; - } - } - return buf; - } - - for (i = 0; flags && i < n_descs; i++) { - if ( descs[i].flag - && NM_FLAGS_ALL (flags, descs[i].flag)) { - flags &= ~descs[i].flag; - - if (buf[0] != '\0') - nm_utils_strbuf_append_c (&p, &len, ','); - nm_utils_strbuf_append_str (&p, &len, descs[i].name); - } - } - if (flags) { - if (buf[0] != '\0') - nm_utils_strbuf_append_c (&p, &len, ','); - nm_utils_strbuf_append (&p, &len, "0x%x", flags); - } - return buf; -}; - -/*****************************************************************************/ - -/** - * _nm_utils_ip4_prefix_to_netmask: - * @prefix: a CIDR prefix - * - * Returns: the netmask represented by the prefix, in network byte order - **/ -guint32 -_nm_utils_ip4_prefix_to_netmask (guint32 prefix) -{ - return prefix < 32 ? ~htonl(0xFFFFFFFF >> prefix) : 0xFFFFFFFF; -} - -/** - * _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) -{ - 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 */ - - return 24; /* Class C - 255.255.255.0 */ -} - -gboolean -nm_utils_ip_is_site_local (int addr_family, - const void *address) -{ - in_addr_t addr4; - - switch (addr_family) { - case AF_INET: - /* RFC1918 private addresses - * 10.0.0.0/8, 172.16.0.0/12, 192.168.0.0/16 */ - addr4 = ntohl (*((const in_addr_t *) address)); - return (addr4 & 0xff000000) == 0x0a000000 - || (addr4 & 0xfff00000) == 0xac100000 - || (addr4 & 0xffff0000) == 0xc0a80000; - case AF_INET6: - return IN6_IS_ADDR_SITELOCAL (address); - default: - g_return_val_if_reached (FALSE); - } -} - -/*****************************************************************************/ - -gboolean -nm_utils_parse_inaddr_bin (int addr_family, - const char *text, - int *out_addr_family, - gpointer out_addr) -{ - NMIPAddr addrbin; - - g_return_val_if_fail (text, FALSE); - - if (addr_family == AF_UNSPEC) { - g_return_val_if_fail (!out_addr || out_addr_family, FALSE); - addr_family = strchr (text, ':') ? AF_INET6 : AF_INET; - } else - g_return_val_if_fail (NM_IN_SET (addr_family, AF_INET, AF_INET6), FALSE); - - if (inet_pton (addr_family, text, &addrbin) != 1) - return FALSE; - - NM_SET_OUT (out_addr_family, addr_family); - if (out_addr) - nm_ip_addr_set (addr_family, out_addr, &addrbin); - return TRUE; -} - -gboolean -nm_utils_parse_inaddr (int addr_family, - const char *text, - char **out_addr) -{ - NMIPAddr addrbin; - char addrstr_buf[MAX (INET_ADDRSTRLEN, INET6_ADDRSTRLEN)]; - - if (!nm_utils_parse_inaddr_bin (addr_family, text, &addr_family, &addrbin)) - return FALSE; - NM_SET_OUT (out_addr, g_strdup (inet_ntop (addr_family, &addrbin, addrstr_buf, sizeof (addrstr_buf)))); - return TRUE; -} - -gboolean -nm_utils_parse_inaddr_prefix_bin (int addr_family, - const char *text, - int *out_addr_family, - gpointer out_addr, - int *out_prefix) -{ - gs_free char *addrstr_free = NULL; - int prefix = -1; - const char *slash; - const char *addrstr; - NMIPAddr addrbin; - - g_return_val_if_fail (text, FALSE); - - if (addr_family == AF_UNSPEC) { - g_return_val_if_fail (!out_addr || out_addr_family, FALSE); - addr_family = strchr (text, ':') ? AF_INET6 : AF_INET; - } else - g_return_val_if_fail (NM_IN_SET (addr_family, AF_INET, AF_INET6), FALSE); - - slash = strchr (text, '/'); - if (slash) - addrstr = addrstr_free = g_strndup (text, slash - text); - else - addrstr = text; - - if (inet_pton (addr_family, addrstr, &addrbin) != 1) - return FALSE; - - if (slash) { - /* For IPv4, `ip addr add` supports the prefix-length as a netmask. We don't - * do that. */ - prefix = _nm_utils_ascii_str_to_int64 (slash + 1, 10, - 0, - addr_family == AF_INET ? 32 : 128, - -1); - if (prefix == -1) - return FALSE; - } - - NM_SET_OUT (out_addr_family, addr_family); - if (out_addr) - nm_ip_addr_set (addr_family, out_addr, &addrbin); - NM_SET_OUT (out_prefix, prefix); - return TRUE; -} - -gboolean -nm_utils_parse_inaddr_prefix (int addr_family, - const char *text, - char **out_addr, - int *out_prefix) -{ - NMIPAddr addrbin; - char addrstr_buf[MAX (INET_ADDRSTRLEN, INET6_ADDRSTRLEN)]; - - if (!nm_utils_parse_inaddr_prefix_bin (addr_family, text, &addr_family, &addrbin, out_prefix)) - return FALSE; - NM_SET_OUT (out_addr, g_strdup (inet_ntop (addr_family, &addrbin, addrstr_buf, sizeof (addrstr_buf)))); - return TRUE; -} - -/*****************************************************************************/ - -/* _nm_utils_ascii_str_to_int64: - * - * A wrapper for g_ascii_strtoll, that checks whether the whole string - * can be successfully converted to a number and is within a given - * range. On any error, @fallback will be returned and %errno will be set - * to a non-zero value. On success, %errno will be set to zero, check %errno - * for errors. Any trailing or leading (ascii) white space is ignored and the - * functions is locale independent. - * - * The function is guaranteed to return a value between @min and @max - * (inclusive) or @fallback. Also, the parsing is rather strict, it does - * not allow for any unrecognized characters, except leading and trailing - * white space. - **/ -gint64 -_nm_utils_ascii_str_to_int64 (const char *str, guint base, gint64 min, gint64 max, gint64 fallback) -{ - gint64 v; - const char *s = NULL; - - if (str) { - while (g_ascii_isspace (str[0])) - str++; - } - if (!str || !str[0]) { - errno = EINVAL; - return fallback; - } - - errno = 0; - v = g_ascii_strtoll (str, (char **) &s, base); - - if (errno != 0) - return fallback; - if (s[0] != '\0') { - while (g_ascii_isspace (s[0])) - s++; - if (s[0] != '\0') { - errno = EINVAL; - return fallback; - } - } - if (v > max || v < min) { - errno = ERANGE; - return fallback; - } - - return v; -} - -guint64 -_nm_utils_ascii_str_to_uint64 (const char *str, guint base, guint64 min, guint64 max, guint64 fallback) -{ - guint64 v; - const char *s = NULL; - - if (str) { - while (g_ascii_isspace (str[0])) - str++; - } - if (!str || !str[0]) { - errno = EINVAL; - return fallback; - } - - errno = 0; - v = g_ascii_strtoull (str, (char **) &s, base); - - if (errno != 0) - return fallback; - if (s[0] != '\0') { - while (g_ascii_isspace (s[0])) - s++; - if (s[0] != '\0') { - errno = EINVAL; - return fallback; - } - } - if (v > max || v < min) { - errno = ERANGE; - return fallback; - } - - if ( v != 0 - && str[0] == '-') { - /* I don't know why, but g_ascii_strtoull() accepts minus signs ("-2" gives 18446744073709551614). - * For "-0" that is OK, but otherwise not. */ - errno = ERANGE; - return fallback; - } - - return v; -} - -/*****************************************************************************/ - -/* like nm_strcmp_p(), suitable for g_ptr_array_sort_with_data(). - * g_ptr_array_sort() just casts nm_strcmp_p() to a function of different - * signature. I guess, in glib there are knowledgeable people that ensure - * that this additional argument doesn't cause problems due to different ABI - * for every architecture that glib supports. - * For NetworkManager, we'd rather avoid such stunts. - **/ -int -nm_strcmp_p_with_data (gconstpointer a, gconstpointer b, gpointer user_data) -{ - const char *s1 = *((const char **) a); - const char *s2 = *((const char **) b); - - return strcmp (s1, s2); -} - -int -nm_cmp_uint32_p_with_data (gconstpointer p_a, gconstpointer p_b, gpointer user_data) -{ - const guint32 a = *((const guint32 *) p_a); - const guint32 b = *((const guint32 *) p_b); - - if (a < b) - return -1; - if (a > b) - return 1; - return 0; -} - -int -nm_cmp_int2ptr_p_with_data (gconstpointer p_a, gconstpointer p_b, gpointer user_data) -{ - /* p_a and p_b are two pointers to a pointer, where the pointer is - * interpreted as a integer using GPOINTER_TO_INT(). - * - * That is the case of a hash-table that uses GINT_TO_POINTER() to - * convert integers as pointers, and the resulting keys-as-array - * array. */ - const int a = GPOINTER_TO_INT (*((gconstpointer *) p_a)); - const int b = GPOINTER_TO_INT (*((gconstpointer *) p_b)); - - if (a < b) - return -1; - if (a > b) - return 1; - return 0; -} - -/*****************************************************************************/ - -const char * -nm_utils_dbus_path_get_last_component (const char *dbus_path) -{ - if (dbus_path) { - dbus_path = strrchr (dbus_path, '/'); - if (dbus_path) - return dbus_path + 1; - } - return NULL; -} - -static gint64 -_dbus_path_component_as_num (const char *p) -{ - gint64 n; - - /* no odd stuff. No leading zeros, only a non-negative, decimal integer. - * - * Otherwise, there would be multiple ways to encode the same number "10" - * and "010". That is just confusing. A number has no leading zeros, - * if it has, it's not a number (as far as we are concerned here). */ - if (p[0] == '0') { - if (p[1] != '\0') - return -1; - else - return 0; - } - if (!(p[0] >= '1' && p[0] <= '9')) - return -1; - if (!NM_STRCHAR_ALL (&p[1], ch, (ch >= '0' && ch <= '9'))) - return -1; - n = _nm_utils_ascii_str_to_int64 (p, 10, 0, G_MAXINT64, -1); - nm_assert (n == -1 || nm_streq0 (p, nm_sprintf_bufa (100, "%"G_GINT64_FORMAT, n))); - return n; -} - -int -nm_utils_dbus_path_cmp (const char *dbus_path_a, const char *dbus_path_b) -{ - const char *l_a, *l_b; - gsize plen; - gint64 n_a, n_b; - - /* compare function for two D-Bus paths. It behaves like - * strcmp(), except, if both paths have the same prefix, - * and both end in a (positive) number, then the paths - * will be sorted by number. */ - - NM_CMP_SELF (dbus_path_a, dbus_path_b); - - /* if one or both paths have no slash (and no last component) - * compare the full paths directly. */ - if ( !(l_a = nm_utils_dbus_path_get_last_component (dbus_path_a)) - || !(l_b = nm_utils_dbus_path_get_last_component (dbus_path_b))) - goto comp_full; - - /* check if both paths have the same prefix (up to the last-component). */ - plen = l_a - dbus_path_a; - if (plen != (l_b - dbus_path_b)) - goto comp_full; - NM_CMP_RETURN (strncmp (dbus_path_a, dbus_path_b, plen)); - - n_a = _dbus_path_component_as_num (l_a); - n_b = _dbus_path_component_as_num (l_b); - if (n_a == -1 && n_b == -1) - goto comp_l; - - /* both components must be convertiable to a number. If they are not, - * (and only one of them is), then we must always strictly sort numeric parts - * after non-numeric components. If we wouldn't, we wouldn't have - * a total order. - * - * An example of a not total ordering would be: - * "8" < "010" (numeric) - * "0x" < "8" (lexical) - * "0x" > "010" (lexical) - * We avoid this, by forcing that a non-numeric entry "0x" always sorts - * before numeric entries. - * - * Additionally, _dbus_path_component_as_num() would also reject "010" as - * not a valid number. - */ - if (n_a == -1) - return -1; - if (n_b == -1) - return 1; - - NM_CMP_DIRECT (n_a, n_b); - nm_assert (nm_streq (dbus_path_a, dbus_path_b)); - return 0; - -comp_full: - NM_CMP_DIRECT_STRCMP0 (dbus_path_a, dbus_path_b); - return 0; -comp_l: - NM_CMP_DIRECT_STRCMP0 (l_a, l_b); - nm_assert (nm_streq (dbus_path_a, dbus_path_b)); - return 0; -} - -/*****************************************************************************/ - -/** - * nm_utils_strsplit_set: - * @str: the string to split. - * @delimiters: the set of delimiters. If %NULL, defaults to " \t\n", - * like bash's $IFS. - * @allow_escaping: whether delimiters can be escaped by a backslash - * - * This is a replacement for g_strsplit_set() which avoids copying - * each word once (the entire strv array), but instead copies it once - * and all words point into that internal copy. - * - * Another difference from g_strsplit_set() is that this never returns - * empty words. Multiple delimiters are combined and treated as one. - * - * If @allow_escaping is %TRUE, delimiters prefixed by a backslash are - * not treated as a separator. Such delimiters and their escape - * character are copied to the current word without unescaping them. - * - * Returns: %NULL if @str is %NULL or contains only delimiters. - * Otherwise, a %NULL terminated strv array containing non-empty - * words, split at the delimiter characters (delimiter characters - * are removed). - * The strings to which the result strv array points to are allocated - * after the returned result itself. Don't free the strings themself, - * but free everything with g_free(). - */ -const char ** -nm_utils_strsplit_set (const char *str, const char *delimiters, gboolean allow_escaping) -{ - const char **ptr, **ptr0; - gsize alloc_size, plen, i; - gsize str_len; - char *s0; - char *s; - guint8 delimiters_table[256]; - gboolean escaped = FALSE; - - if (!str) - return NULL; - - /* initialize lookup table for delimiter */ - if (!delimiters) - delimiters = " \t\n"; - memset (delimiters_table, 0, sizeof (delimiters_table)); - for (i = 0; delimiters[i]; i++) - delimiters_table[(guint8) delimiters[i]] = 1; - -#define _is_delimiter(ch, delimiters_table, allow_esc, esc) \ - ((delimiters_table)[(guint8) (ch)] != 0 && (!allow_esc || !esc)) - -#define next_char(p, esc) \ - G_STMT_START { \ - if (esc) \ - esc = FALSE; \ - else \ - esc = p[0] == '\\'; \ - p++; \ - } G_STMT_END - - /* skip initial delimiters, and return of the remaining string is - * empty. */ - while (_is_delimiter (str[0], delimiters_table, allow_escaping, escaped)) - next_char (str, escaped); - - if (!str[0]) - return NULL; - - str_len = strlen (str) + 1; - alloc_size = 8; - - /* we allocate the buffer larger, so to copy @str at the - * end of it as @s0. */ - ptr0 = g_malloc ((sizeof (const char *) * (alloc_size + 1)) + str_len); - s0 = (char *) &ptr0[alloc_size + 1]; - memcpy (s0, str, str_len); - - plen = 0; - s = s0; - ptr = ptr0; - - while (TRUE) { - if (plen >= alloc_size) { - const char **ptr_old = ptr; - - /* reallocate the buffer. Note that for now the string - * continues to be in ptr0/s0. We fix that at the end. */ - alloc_size *= 2; - ptr = g_malloc ((sizeof (const char *) * (alloc_size + 1)) + str_len); - memcpy (ptr, ptr_old, sizeof (const char *) * plen); - if (ptr_old != ptr0) - g_free (ptr_old); - } - - ptr[plen++] = s; - - nm_assert (s[0] && !_is_delimiter (s[0], delimiters_table, allow_escaping, escaped)); - - while (TRUE) { - next_char (s, escaped); - if (_is_delimiter (s[0], delimiters_table, allow_escaping, escaped)) - break; - if (s[0] == '\0') - goto done; - } - - s[0] = '\0'; - next_char (s, escaped); - while (_is_delimiter (s[0], delimiters_table, allow_escaping, escaped)) - next_char (s, escaped); - if (s[0] == '\0') - break; - } -done: - ptr[plen] = NULL; - - if (ptr != ptr0) { - /* we reallocated the buffer. We must copy over the - * string @s0 and adjust the pointers. */ - s = (char *) &ptr[alloc_size + 1]; - memcpy (s, s0, str_len); - for (i = 0; i < plen; i++) - ptr[i] = &s[ptr[i] - s0]; - g_free (ptr0); - } - - return ptr; -} - -/** - * nm_utils_strv_find_first: - * @list: the strv list to search - * @len: the length of the list, or a negative value if @list is %NULL terminated. - * @needle: the value to search for. The search is done using strcmp(). - * - * Searches @list for @needle and returns the index of the first match (based - * on strcmp()). - * - * For convenience, @list has type 'char**' instead of 'const char **'. - * - * Returns: index of first occurrence or -1 if @needle is not found in @list. - */ -gssize -nm_utils_strv_find_first (char **list, gssize len, const char *needle) -{ - gssize i; - - if (len > 0) { - g_return_val_if_fail (list, -1); - - if (!needle) { - /* if we search a list with known length, %NULL is a valid @needle. */ - for (i = 0; i < len; i++) { - if (!list[i]) - return i; - } - } else { - for (i = 0; i < len; i++) { - if (list[i] && !strcmp (needle, list[i])) - return i; - } - } - } else if (len < 0) { - g_return_val_if_fail (needle, -1); - - if (list) { - for (i = 0; list[i]; i++) { - if (strcmp (needle, list[i]) == 0) - return i; - } - } - } - return -1; -} - -char ** -_nm_utils_strv_cleanup (char **strv, - gboolean strip_whitespace, - gboolean skip_empty, - gboolean skip_repeated) -{ - guint i, j; - - if (!strv || !*strv) - return strv; - - if (strip_whitespace) { - for (i = 0; strv[i]; i++) - g_strstrip (strv[i]); - } - if (!skip_empty && !skip_repeated) - return strv; - j = 0; - for (i = 0; strv[i]; i++) { - if ( (skip_empty && !*strv[i]) - || (skip_repeated && nm_utils_strv_find_first (strv, j, strv[i]) >= 0)) - g_free (strv[i]); - else - strv[j++] = strv[i]; - } - strv[j] = NULL; - return strv; -} - -/*****************************************************************************/ - -int -_nm_utils_ascii_str_to_bool (const char *str, - int default_value) -{ - gsize len; - char *s = NULL; - - if (!str) - return default_value; - - while (str[0] && g_ascii_isspace (str[0])) - str++; - - if (!str[0]) - return default_value; - - len = strlen (str); - if (g_ascii_isspace (str[len - 1])) { - s = g_strdup (str); - g_strchomp (s); - str = s; - } - - if (!g_ascii_strcasecmp (str, "true") || !g_ascii_strcasecmp (str, "yes") || !g_ascii_strcasecmp (str, "on") || !g_ascii_strcasecmp (str, "1")) - default_value = TRUE; - else if (!g_ascii_strcasecmp (str, "false") || !g_ascii_strcasecmp (str, "no") || !g_ascii_strcasecmp (str, "off") || !g_ascii_strcasecmp (str, "0")) - default_value = FALSE; - if (s) - g_free (s); - return default_value; -} - -/*****************************************************************************/ - -NM_CACHED_QUARK_FCN ("nm-utils-error-quark", nm_utils_error_quark) - -void -nm_utils_error_set_cancelled (GError **error, - gboolean is_disposing, - const char *instance_name) -{ - if (is_disposing) { - g_set_error (error, NM_UTILS_ERROR, NM_UTILS_ERROR_CANCELLED_DISPOSING, - "Disposing %s instance", - instance_name && *instance_name ? instance_name : "source"); - } else { - g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_CANCELLED, - "Request cancelled"); - } -} - -gboolean -nm_utils_error_is_cancelled (GError *error, - gboolean consider_is_disposing) -{ - if (error) { - if (error->domain == G_IO_ERROR) - return NM_IN_SET (error->code, G_IO_ERROR_CANCELLED); - if (consider_is_disposing) { - if (error->domain == NM_UTILS_ERROR) - return NM_IN_SET (error->code, NM_UTILS_ERROR_CANCELLED_DISPOSING); - } - } - return FALSE; -} - -gboolean -nm_utils_error_is_notfound (GError *error) -{ - if (error) { - if (error->domain == G_IO_ERROR) - return NM_IN_SET (error->code, G_IO_ERROR_NOT_FOUND); - if (error->domain == G_FILE_ERROR) - return NM_IN_SET (error->code, G_FILE_ERROR_NOENT); - } - return FALSE; -} - -/*****************************************************************************/ - -/** - * nm_g_object_set_property: - * @object: the target object - * @property_name: the property name - * @value: the #GValue to set - * @error: (allow-none): optional error argument - * - * A reimplementation of g_object_set_property(), but instead - * returning an error instead of logging a warning. All g_object_set*() - * versions in glib require you to not pass invalid types or they will - * log a g_warning() -- without reporting an error. We don't want that, - * so we need to hack error checking around it. - * - * Returns: whether the value was successfully set. - */ -gboolean -nm_g_object_set_property (GObject *object, - const char *property_name, - const GValue *value, - GError **error) -{ - GParamSpec *pspec; - nm_auto_unset_gvalue GValue tmp_value = G_VALUE_INIT; - GObjectClass *klass; - - g_return_val_if_fail (G_IS_OBJECT (object), FALSE); - g_return_val_if_fail (property_name != NULL, FALSE); - g_return_val_if_fail (G_IS_VALUE (value), FALSE); - g_return_val_if_fail (!error || !*error, FALSE); - - /* g_object_class_find_property() does g_param_spec_get_redirect_target(), - * where we differ from a plain g_object_set_property(). */ - pspec = g_object_class_find_property (G_OBJECT_GET_CLASS (object), property_name); - - if (!pspec) { - g_set_error (error, NM_UTILS_ERROR, NM_UTILS_ERROR_UNKNOWN, - _("object class '%s' has no property named '%s'"), - G_OBJECT_TYPE_NAME (object), - property_name); - return FALSE; - } - if (!(pspec->flags & G_PARAM_WRITABLE)) { - g_set_error (error, NM_UTILS_ERROR, NM_UTILS_ERROR_UNKNOWN, - _("property '%s' of object class '%s' is not writable"), - pspec->name, - G_OBJECT_TYPE_NAME (object)); - return FALSE; - } - if ((pspec->flags & G_PARAM_CONSTRUCT_ONLY)) { - g_set_error (error, NM_UTILS_ERROR, NM_UTILS_ERROR_UNKNOWN, - _("construct property \"%s\" for object '%s' can't be set after construction"), - pspec->name, G_OBJECT_TYPE_NAME (object)); - return FALSE; - } - - klass = g_type_class_peek (pspec->owner_type); - if (klass == NULL) { - g_set_error (error, NM_UTILS_ERROR, NM_UTILS_ERROR_UNKNOWN, - _("'%s::%s' is not a valid property name; '%s' is not a GObject subtype"), - g_type_name (pspec->owner_type), pspec->name, g_type_name (pspec->owner_type)); - return FALSE; - } - - /* provide a copy to work from, convert (if necessary) and validate */ - g_value_init (&tmp_value, pspec->value_type); - if (!g_value_transform (value, &tmp_value)) { - g_set_error (error, NM_UTILS_ERROR, NM_UTILS_ERROR_UNKNOWN, - _("unable to set property '%s' of type '%s' from value of type '%s'"), - pspec->name, - g_type_name (pspec->value_type), - G_VALUE_TYPE_NAME (value)); - return FALSE; - } - if ( g_param_value_validate (pspec, &tmp_value) - && !(pspec->flags & G_PARAM_LAX_VALIDATION)) { - gs_free char *contents = g_strdup_value_contents (value); - - g_set_error (error, NM_UTILS_ERROR, NM_UTILS_ERROR_UNKNOWN, - _("value \"%s\" of type '%s' is invalid or out of range for property '%s' of type '%s'"), - contents, - G_VALUE_TYPE_NAME (value), - pspec->name, - g_type_name (pspec->value_type)); - return FALSE; - } - - g_object_set_property (object, property_name, &tmp_value); - return TRUE; -} - -#define _set_property(object, property_name, gtype, gtype_set, value, error) \ - G_STMT_START { \ - nm_auto_unset_gvalue GValue gvalue = { 0 }; \ - \ - g_value_init (&gvalue, gtype); \ - gtype_set (&gvalue, (value)); \ - return nm_g_object_set_property ((object), (property_name), &gvalue, (error)); \ - } G_STMT_END - -gboolean -nm_g_object_set_property_string (GObject *object, - const char *property_name, - const char *value, - GError **error) -{ - _set_property (object, property_name, G_TYPE_STRING, g_value_set_string, value, error); -} - -gboolean -nm_g_object_set_property_string_static (GObject *object, - const char *property_name, - const char *value, - GError **error) -{ - _set_property (object, property_name, G_TYPE_STRING, g_value_set_static_string, value, error); -} - -gboolean -nm_g_object_set_property_string_take (GObject *object, - const char *property_name, - char *value, - GError **error) -{ - _set_property (object, property_name, G_TYPE_STRING, g_value_take_string, value, error); -} - -gboolean -nm_g_object_set_property_boolean (GObject *object, - const char *property_name, - gboolean value, - GError **error) -{ - _set_property (object, property_name, G_TYPE_BOOLEAN, g_value_set_boolean, !!value, error); -} - -gboolean -nm_g_object_set_property_char (GObject *object, - const char *property_name, - gint8 value, - GError **error) -{ - /* glib says about G_TYPE_CHAR: - * - * The type designated by G_TYPE_CHAR is unconditionally an 8-bit signed integer. - * - * This is always a (signed!) char. */ - _set_property (object, property_name, G_TYPE_CHAR, g_value_set_schar, value, error); -} - -gboolean -nm_g_object_set_property_uchar (GObject *object, - const char *property_name, - guint8 value, - GError **error) -{ - _set_property (object, property_name, G_TYPE_UCHAR, g_value_set_uchar, value, error); -} - -gboolean -nm_g_object_set_property_int (GObject *object, - const char *property_name, - int value, - GError **error) -{ - _set_property (object, property_name, G_TYPE_INT, g_value_set_int, value, error); -} - -gboolean -nm_g_object_set_property_int64 (GObject *object, - const char *property_name, - gint64 value, - GError **error) -{ - _set_property (object, property_name, G_TYPE_INT64, g_value_set_int64, value, error); -} - -gboolean -nm_g_object_set_property_uint (GObject *object, - const char *property_name, - guint value, - GError **error) -{ - _set_property (object, property_name, G_TYPE_UINT, g_value_set_uint, value, error); -} - -gboolean -nm_g_object_set_property_uint64 (GObject *object, - const char *property_name, - guint64 value, - GError **error) -{ - _set_property (object, property_name, G_TYPE_UINT64, g_value_set_uint64, value, error); -} - -gboolean -nm_g_object_set_property_flags (GObject *object, - const char *property_name, - GType gtype, - guint value, - GError **error) -{ - nm_assert (({ - nm_auto_unref_gtypeclass GTypeClass *gtypeclass = g_type_class_ref (gtype); - G_IS_FLAGS_CLASS (gtypeclass); - })); - _set_property (object, property_name, gtype, g_value_set_flags, value, error); -} - -gboolean -nm_g_object_set_property_enum (GObject *object, - const char *property_name, - GType gtype, - int value, - GError **error) -{ - nm_assert (({ - nm_auto_unref_gtypeclass GTypeClass *gtypeclass = g_type_class_ref (gtype); - G_IS_ENUM_CLASS (gtypeclass); - })); - _set_property (object, property_name, gtype, g_value_set_enum, value, error); -} - -GParamSpec * -nm_g_object_class_find_property_from_gtype (GType gtype, - const char *property_name) -{ - nm_auto_unref_gtypeclass GObjectClass *gclass = NULL; - - gclass = g_type_class_ref (gtype); - return g_object_class_find_property (gclass, property_name); -} - -/*****************************************************************************/ - -/** - * nm_g_type_find_implementing_class_for_property: - * @gtype: the GObject type which has a property @pname - * @pname: the name of the property to look up - * - * This is only a helper function for printf debugging. It's not - * used in actual code. Hence, the function just asserts that - * @pname and @gtype arguments are suitable. It cannot fail. - * - * Returns: the most ancestor type of @gtype, that - * implements the property @pname. It means, it - * searches the type hierarchy to find the type - * that added @pname. - */ -GType -nm_g_type_find_implementing_class_for_property (GType gtype, - const char *pname) -{ - nm_auto_unref_gtypeclass GObjectClass *klass = NULL; - GParamSpec *pspec; - - g_return_val_if_fail (pname, G_TYPE_INVALID); - - klass = g_type_class_ref (gtype); - g_return_val_if_fail (G_IS_OBJECT_CLASS (klass), G_TYPE_INVALID); - - pspec = g_object_class_find_property (klass, pname); - g_return_val_if_fail (pspec, G_TYPE_INVALID); - - gtype = G_TYPE_FROM_CLASS (klass); - - while (TRUE) { - nm_auto_unref_gtypeclass GObjectClass *k = NULL; - - k = g_type_class_ref (g_type_parent (gtype)); - - g_return_val_if_fail (G_IS_OBJECT_CLASS (k), G_TYPE_INVALID); - - if (g_object_class_find_property (k, pname) != pspec) - return gtype; - - gtype = G_TYPE_FROM_CLASS (k); - } -} - -/*****************************************************************************/ - -static void -_str_append_escape (GString *s, char ch) -{ - g_string_append_c (s, '\\'); - g_string_append_c (s, '0' + ((((guchar) ch) >> 6) & 07)); - g_string_append_c (s, '0' + ((((guchar) ch) >> 3) & 07)); - g_string_append_c (s, '0' + ( ((guchar) ch) & 07)); -} - -gconstpointer -nm_utils_buf_utf8safe_unescape (const char *str, gsize *out_len, gpointer *to_free) -{ - GString *gstr; - gsize len; - const char *s; - - g_return_val_if_fail (to_free, NULL); - g_return_val_if_fail (out_len, NULL); - - if (!str) { - *out_len = 0; - *to_free = NULL; - return NULL; - } - - len = strlen (str); - - s = memchr (str, '\\', len); - if (!s) { - *out_len = len; - *to_free = NULL; - return str; - } - - gstr = g_string_new_len (NULL, len); - - g_string_append_len (gstr, str, s - str); - str = s; - - for (;;) { - char ch; - guint v; - - nm_assert (str[0] == '\\'); - - ch = (++str)[0]; - - if (ch == '\0') { - // error. Trailing '\\' - break; - } - - if (ch >= '0' && ch <= '9') { - v = ch - '0'; - ch = (++str)[0]; - if (ch >= '0' && ch <= '7') { - v = v * 8 + (ch - '0'); - ch = (++str)[0]; - if (ch >= '0' && ch <= '7') { - v = v * 8 + (ch - '0'); - ++str; - } - } - ch = v; - } else { - switch (ch) { - case 'b': ch = '\b'; break; - case 'f': ch = '\f'; break; - case 'n': ch = '\n'; break; - case 'r': ch = '\r'; break; - case 't': ch = '\t'; break; - case 'v': ch = '\v'; break; - default: - /* Here we handle "\\\\", but all other unexpected escape sequences are really a bug. - * Take them literally, after removing the escape character */ - break; - } - str++; - } - - g_string_append_c (gstr, ch); - - s = strchr (str, '\\'); - if (!s) { - g_string_append (gstr, str); - break; - } - - g_string_append_len (gstr, str, s - str); - str = s; - } - - *out_len = gstr->len; - *to_free = gstr->str; - return g_string_free (gstr, FALSE); -} - -/** - * nm_utils_buf_utf8safe_escape: - * @buf: byte array, possibly in utf-8 encoding, may have NUL characters. - * @buflen: the length of @buf in bytes, or -1 if @buf is a NUL terminated - * string. - * @flags: #NMUtilsStrUtf8SafeFlags flags - * @to_free: (out): return the pointer location of the string - * if a copying was necessary. - * - * Based on the assumption, that @buf contains UTF-8 encoded bytes, - * this will return valid UTF-8 sequence, and invalid sequences - * will be escaped with backslash (C escaping, like g_strescape()). - * This is sanitize non UTF-8 characters. The result is valid - * UTF-8. - * - * The operation can be reverted with nm_utils_buf_utf8safe_unescape(). - * Note that if, and only if @buf contains no NUL bytes, the operation - * can also be reverted with g_strcompress(). - * - * Depending on @flags, valid UTF-8 characters are not escaped at all - * (except the escape character '\\'). This is the difference to g_strescape(), - * which escapes all non-ASCII characters. This allows to pass on - * valid UTF-8 characters as-is and can be directly shown to the user - * as UTF-8 -- with exception of the backslash escape character, - * invalid UTF-8 sequences, and other (depending on @flags). - * - * Returns: the escaped input buffer, as valid UTF-8. If no escaping - * is necessary, it returns the input @buf. Otherwise, an allocated - * string @to_free is returned which must be freed by the caller - * with g_free. The escaping can be reverted by g_strcompress(). - **/ -const char * -nm_utils_buf_utf8safe_escape (gconstpointer buf, gssize buflen, NMUtilsStrUtf8SafeFlags flags, char **to_free) -{ - const char *const str = buf; - const char *p = NULL; - const char *s; - gboolean nul_terminated = FALSE; - GString *gstr; - - g_return_val_if_fail (to_free, NULL); - - *to_free = NULL; - - if (buflen == 0) - return NULL; - - if (buflen < 0) { - if (!str) - return NULL; - buflen = strlen (str); - if (buflen == 0) - return str; - nul_terminated = TRUE; - } - - if ( g_utf8_validate (str, buflen, &p) - && nul_terminated) { - /* note that g_utf8_validate() does not allow NUL character inside @str. Good. - * We can treat @str like a NUL terminated string. */ - if (!NM_STRCHAR_ANY (str, ch, - ( ch == '\\' \ - || ( NM_FLAGS_HAS (flags, NM_UTILS_STR_UTF8_SAFE_FLAG_ESCAPE_CTRL) \ - && ch < ' ') \ - || ( NM_FLAGS_HAS (flags, NM_UTILS_STR_UTF8_SAFE_FLAG_ESCAPE_NON_ASCII) \ - && ((guchar) ch) >= 127)))) - return str; - } - - gstr = g_string_sized_new (buflen + 5); - - s = str; - do { - buflen -= p - s; - nm_assert (buflen >= 0); - - for (; s < p; s++) { - char ch = s[0]; - - if (ch == '\\') - g_string_append (gstr, "\\\\"); - else if ( ( NM_FLAGS_HAS (flags, NM_UTILS_STR_UTF8_SAFE_FLAG_ESCAPE_CTRL) \ - && ch < ' ') \ - || ( NM_FLAGS_HAS (flags, NM_UTILS_STR_UTF8_SAFE_FLAG_ESCAPE_NON_ASCII) \ - && ((guchar) ch) >= 127)) - _str_append_escape (gstr, ch); - else - g_string_append_c (gstr, ch); - } - - if (buflen <= 0) - break; - - _str_append_escape (gstr, p[0]); - - buflen--; - if (buflen == 0) - break; - - s = &p[1]; - g_utf8_validate (s, buflen, &p); - } while (TRUE); - - *to_free = g_string_free (gstr, FALSE); - return *to_free; -} - -const char * -nm_utils_buf_utf8safe_escape_bytes (GBytes *bytes, NMUtilsStrUtf8SafeFlags flags, char **to_free) -{ - gconstpointer p; - gsize l; - - if (bytes) - p = g_bytes_get_data (bytes, &l); - else { - p = NULL; - l = 0; - } - - return nm_utils_buf_utf8safe_escape (p, l, flags, to_free); -} - -/*****************************************************************************/ - -const char * -nm_utils_str_utf8safe_unescape (const char *str, char **to_free) -{ - g_return_val_if_fail (to_free, NULL); - - if (!str || !strchr (str, '\\')) { - *to_free = NULL; - return str; - } - return (*to_free = g_strcompress (str)); -} - -/** - * nm_utils_str_utf8safe_escape: - * @str: NUL terminated input string, possibly in utf-8 encoding - * @flags: #NMUtilsStrUtf8SafeFlags flags - * @to_free: (out): return the pointer location of the string - * if a copying was necessary. - * - * Returns the possible non-UTF-8 NUL terminated string @str - * and uses backslash escaping (C escaping, like g_strescape()) - * to sanitize non UTF-8 characters. The result is valid - * UTF-8. - * - * The operation can be reverted with g_strcompress() or - * nm_utils_str_utf8safe_unescape(). - * - * Depending on @flags, valid UTF-8 characters are not escaped at all - * (except the escape character '\\'). This is the difference to g_strescape(), - * which escapes all non-ASCII characters. This allows to pass on - * valid UTF-8 characters as-is and can be directly shown to the user - * as UTF-8 -- with exception of the backslash escape character, - * invalid UTF-8 sequences, and other (depending on @flags). - * - * Returns: the escaped input string, as valid UTF-8. If no escaping - * is necessary, it returns the input @str. Otherwise, an allocated - * string @to_free is returned which must be freed by the caller - * with g_free. The escaping can be reverted by g_strcompress(). - **/ -const char * -nm_utils_str_utf8safe_escape (const char *str, NMUtilsStrUtf8SafeFlags flags, char **to_free) -{ - return nm_utils_buf_utf8safe_escape (str, -1, flags, to_free); -} - -/** - * nm_utils_str_utf8safe_escape_cp: - * @str: NUL terminated input string, possibly in utf-8 encoding - * @flags: #NMUtilsStrUtf8SafeFlags flags - * - * Like nm_utils_str_utf8safe_escape(), except the returned value - * is always a copy of the input and must be freed by the caller. - * - * Returns: the escaped input string in UTF-8 encoding. The returned - * value should be freed with g_free(). - * The escaping can be reverted by g_strcompress(). - **/ -char * -nm_utils_str_utf8safe_escape_cp (const char *str, NMUtilsStrUtf8SafeFlags flags) -{ - char *s; - - nm_utils_str_utf8safe_escape (str, flags, &s); - return s ?: g_strdup (str); -} - -char * -nm_utils_str_utf8safe_unescape_cp (const char *str) -{ - return str ? g_strcompress (str) : NULL; -} - -char * -nm_utils_str_utf8safe_escape_take (char *str, NMUtilsStrUtf8SafeFlags flags) -{ - char *str_to_free; - - nm_utils_str_utf8safe_escape (str, flags, &str_to_free); - if (str_to_free) { - g_free (str); - return str_to_free; - } - return str; -} - -/*****************************************************************************/ - -/* taken from systemd's fd_wait_for_event(). Note that the timeout - * is here in nano-seconds, not micro-seconds. */ -int -nm_utils_fd_wait_for_event (int fd, int event, gint64 timeout_ns) -{ - struct pollfd pollfd = { - .fd = fd, - .events = event, - }; - struct timespec ts, *pts; - int r; - - if (timeout_ns < 0) - pts = NULL; - else { - ts.tv_sec = (time_t) (timeout_ns / NM_UTILS_NS_PER_SECOND); - ts.tv_nsec = (long int) (timeout_ns % NM_UTILS_NS_PER_SECOND); - pts = &ts; - } - - r = ppoll (&pollfd, 1, pts, NULL); - if (r < 0) - return -NM_ERRNO_NATIVE (errno); - if (r == 0) - return 0; - return pollfd.revents; -} - -/* taken from systemd's loop_read() */ -ssize_t -nm_utils_fd_read_loop (int fd, void *buf, size_t nbytes, bool do_poll) -{ - uint8_t *p = buf; - ssize_t n = 0; - - g_return_val_if_fail (fd >= 0, -EINVAL); - g_return_val_if_fail (buf, -EINVAL); - - /* If called with nbytes == 0, let's call read() at least - * once, to validate the operation */ - - if (nbytes > (size_t) SSIZE_MAX) - return -EINVAL; - - do { - ssize_t k; - - k = read (fd, p, nbytes); - if (k < 0) { - int errsv = errno; - - if (errsv == EINTR) - continue; - - if (errsv == EAGAIN && do_poll) { - - /* We knowingly ignore any return value here, - * and expect that any error/EOF is reported - * via read() */ - - (void) nm_utils_fd_wait_for_event (fd, POLLIN, -1); - continue; - } - - return n > 0 ? n : -NM_ERRNO_NATIVE (errsv); - } - - if (k == 0) - return n; - - g_assert ((size_t) k <= nbytes); - - p += k; - nbytes -= k; - n += k; - } while (nbytes > 0); - - return n; -} - -/* taken from systemd's loop_read_exact() */ -int -nm_utils_fd_read_loop_exact (int fd, void *buf, size_t nbytes, bool do_poll) -{ - ssize_t n; - - n = nm_utils_fd_read_loop (fd, buf, nbytes, do_poll); - if (n < 0) - return (int) n; - if ((size_t) n != nbytes) - return -EIO; - - return 0; -} - -NMUtilsNamedValue * -nm_utils_named_values_from_str_dict (GHashTable *hash, guint *out_len) -{ - GHashTableIter iter; - NMUtilsNamedValue *values; - guint i, len; - - if ( !hash - || !(len = g_hash_table_size (hash))) { - NM_SET_OUT (out_len, 0); - return NULL; - } - - i = 0; - values = g_new (NMUtilsNamedValue, len + 1); - g_hash_table_iter_init (&iter, hash); - while (g_hash_table_iter_next (&iter, - (gpointer *) &values[i].name, - (gpointer *) &values[i].value_ptr)) - i++; - nm_assert (i == len); - values[i].name = NULL; - values[i].value_ptr = NULL; - - if (len > 1) { - g_qsort_with_data (values, len, sizeof (values[0]), - nm_utils_named_entry_cmp_with_data, NULL); - } - - NM_SET_OUT (out_len, len); - return values; -} - -gpointer * -nm_utils_hash_keys_to_array (GHashTable *hash, - GCompareDataFunc compare_func, - gpointer user_data, - guint *out_len) -{ - guint len; - gpointer *keys; - - /* by convention, we never return an empty array. In that - * case, always %NULL. */ - if ( !hash - || g_hash_table_size (hash) == 0) { - NM_SET_OUT (out_len, 0); - return NULL; - } - - keys = g_hash_table_get_keys_as_array (hash, &len); - if ( len > 1 - && compare_func) { - g_qsort_with_data (keys, - len, - sizeof (gpointer), - compare_func, - user_data); - } - NM_SET_OUT (out_len, len); - return keys; -} - -char ** -nm_utils_strv_make_deep_copied (const char **strv) -{ - gsize i; - - /* it takes a strv dictionary, and copies each - * strings. Note that this updates @strv *in-place* - * and returns it. */ - - if (!strv) - return NULL; - for (i = 0; strv[i]; i++) - strv[i] = g_strdup (strv[i]); - - return (char **) strv; -} - -/*****************************************************************************/ - -gssize -nm_utils_ptrarray_find_binary_search (gconstpointer *list, - gsize len, - gconstpointer needle, - GCompareDataFunc cmpfcn, - gpointer user_data, - gssize *out_idx_first, - gssize *out_idx_last) -{ - gssize imin, imax, imid, i2min, i2max, i2mid; - int cmp; - - g_return_val_if_fail (list || !len, ~((gssize) 0)); - g_return_val_if_fail (cmpfcn, ~((gssize) 0)); - - imin = 0; - if (len > 0) { - imax = len - 1; - - while (imin <= imax) { - imid = imin + (imax - imin) / 2; - - cmp = cmpfcn (list[imid], needle, user_data); - if (cmp == 0) { - /* we found a matching entry at index imid. - * - * Does the caller request the first/last index as well (in case that - * there are multiple entries which compare equal). */ - - if (out_idx_first) { - i2min = imin; - i2max = imid + 1; - while (i2min <= i2max) { - i2mid = i2min + (i2max - i2min) / 2; - - cmp = cmpfcn (list[i2mid], needle, user_data); - if (cmp == 0) - i2max = i2mid -1; - else { - nm_assert (cmp < 0); - i2min = i2mid + 1; - } - } - *out_idx_first = i2min; - } - if (out_idx_last) { - i2min = imid + 1; - i2max = imax; - while (i2min <= i2max) { - i2mid = i2min + (i2max - i2min) / 2; - - cmp = cmpfcn (list[i2mid], needle, user_data); - if (cmp == 0) - i2min = i2mid + 1; - else { - nm_assert (cmp > 0); - i2max = i2mid - 1; - } - } - *out_idx_last = i2min - 1; - } - return imid; - } - - if (cmp < 0) - imin = imid + 1; - else - imax = imid - 1; - } - } - - /* return the inverse of @imin. This is a negative number, but - * also is ~imin the position where the value should be inserted. */ - imin = ~imin; - NM_SET_OUT (out_idx_first, imin); - NM_SET_OUT (out_idx_last, imin); - return imin; -} - -/*****************************************************************************/ - -/** - * nm_utils_array_find_binary_search: - * @list: the list to search. It must be sorted according to @cmpfcn ordering. - * @elem_size: the size in bytes of each element in the list - * @len: the number of elements in @list - * @needle: the value that is searched - * @cmpfcn: the compare function. The elements @list are passed as first - * argument to @cmpfcn, while @needle is passed as second. Usually, the - * needle is the same data type as inside the list, however, that is - * not necessary, as long as @cmpfcn takes care to cast the two arguments - * accordingly. - * @user_data: optional argument passed to @cmpfcn - * - * Performs binary search for @needle in @list. On success, returns the - * (non-negative) index where the compare function found the searched element. - * On success, it returns a negative value. Note that the return negative value - * is the bitwise inverse of the position where the element should be inserted. - * - * If the list contains multiple matching elements, an arbitrary index is - * returned. - * - * Returns: the index to the element in the list, or the (negative, bitwise inverted) - * position where it should be. - */ -gssize -nm_utils_array_find_binary_search (gconstpointer list, - gsize elem_size, - gsize len, - gconstpointer needle, - GCompareDataFunc cmpfcn, - gpointer user_data) -{ - gssize imin, imax, imid; - int cmp; - - g_return_val_if_fail (list || !len, ~((gssize) 0)); - g_return_val_if_fail (cmpfcn, ~((gssize) 0)); - g_return_val_if_fail (elem_size > 0, ~((gssize) 0)); - - imin = 0; - if (len == 0) - return ~imin; - - imax = len - 1; - - while (imin <= imax) { - imid = imin + (imax - imin) / 2; - - cmp = cmpfcn (&((const char *) list)[elem_size * imid], needle, user_data); - if (cmp == 0) - return imid; - - if (cmp < 0) - imin = imid + 1; - else - imax = imid - 1; - } - - /* return the inverse of @imin. This is a negative number, but - * also is ~imin the position where the value should be inserted. */ - return ~imin; -} - -/*****************************************************************************/ - -/** - * 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`. - * @out_ppid: parent process id - * - * Originally copied from polkit source (src/polkit/polkitunixprocess.c) - * and adjusted. - * - * Returns: the timestamp when the process started (by parsing /proc/$PID/stat). - * If an error occurs (e.g. the process does not exist), 0 is returned. - * - * The returned start time counts since boot, in the unit HZ (with HZ usually being (1/100) seconds) - **/ -guint64 -nm_utils_get_start_time_for_pid (pid_t pid, char *out_state, pid_t *out_ppid) -{ - guint64 start_time; - char filename[256]; - gs_free char *contents = NULL; - size_t length; - gs_free const char **tokens = NULL; - char *p; - char state = ' '; - gint64 ppid = 0; - - start_time = 0; - contents = NULL; - - g_return_val_if_fail (pid > 0, 0); - - nm_sprintf_buf (filename, "/proc/%"G_GUINT64_FORMAT"/stat", (guint64) pid); - - if (!g_file_get_contents (filename, &contents, &length, NULL)) - goto fail; - - /* start time is the token at index 19 after the '(process name)' entry - since only this - * field can contain the ')' character, search backwards for this to avoid malicious - * processes trying to fool us - */ - p = strrchr (contents, ')'); - if (!p) - goto fail; - p += 2; /* skip ') ' */ - if (p - contents >= (int) length) - goto fail; - - state = p[0]; - - tokens = nm_utils_strsplit_set (p, " ", FALSE); - - if (NM_PTRARRAY_LEN (tokens) < 20) - goto fail; - - if (out_ppid) { - ppid = _nm_utils_ascii_str_to_int64 (tokens[1], 10, 1, G_MAXINT, 0); - if (ppid == 0) - goto fail; - } - - start_time = _nm_utils_ascii_str_to_int64 (tokens[19], 10, 1, G_MAXINT64, 0); - if (start_time == 0) - goto fail; - - NM_SET_OUT (out_state, state); - NM_SET_OUT (out_ppid, ppid); - return start_time; - -fail: - NM_SET_OUT (out_state, ' '); - NM_SET_OUT (out_ppid, 0); - return 0; -} - -/*****************************************************************************/ - -/** - * _nm_utils_strv_sort: - * @strv: pointer containing strings that will be sorted - * in-place, %NULL is allowed, unless @len indicates - * that there are more elements. - * @len: the number of elements in strv. If negative, - * strv must be a NULL terminated array and the length - * will be calculated first. If @len is a positive - * number, all first @len elements in @strv must be - * non-NULL, valid strings. - * - * Ascending sort of the array @strv inplace, using plain strcmp() string - * comparison. - */ -void -_nm_utils_strv_sort (const char **strv, gssize len) -{ - gsize l; - - l = len < 0 ? (gsize) NM_PTRARRAY_LEN (strv) : (gsize) len; - - if (l <= 1) - return; - - nm_assert (l <= (gsize) G_MAXINT); - - g_qsort_with_data (strv, - l, - sizeof (const char *), - nm_strcmp_p_with_data, - NULL); -} - -/*****************************************************************************/ - -gpointer -_nm_utils_user_data_pack (int nargs, gconstpointer *args) -{ - int i; - gpointer *data; - - nm_assert (nargs > 0); - nm_assert (args); - - data = g_slice_alloc (((gsize) nargs) * sizeof (gconstpointer)); - for (i = 0; i < nargs; i++) - data[i] = (gpointer) args[i]; - return data; -} - -void -_nm_utils_user_data_unpack (gpointer user_data, int nargs, ...) -{ - gpointer *data = user_data; - va_list ap; - int i; - - nm_assert (data); - nm_assert (nargs > 0); - - va_start (ap, nargs); - for (i = 0; i < nargs; i++) { - gpointer *dst; - - dst = va_arg (ap, gpointer *); - nm_assert (dst); - - *dst = data[i]; - } - va_end (ap); - - g_slice_free1 (((gsize) nargs) * sizeof (gconstpointer), user_data); -} - -/*****************************************************************************/ - -#define IS_SPACE(c) NM_IN_SET ((c), ' ', '\t') - -const char * -_nm_utils_escape_spaces (const char *str, char **to_free) -{ - const char *ptr = str; - char *ret, *r; - - *to_free = NULL; - - if (!str) - return NULL; - - while (TRUE) { - if (!*ptr) - return str; - if (IS_SPACE (*ptr)) - break; - ptr++; - } - - ptr = str; - ret = g_new (char, strlen (str) * 2 + 1); - r = ret; - *to_free = ret; - while (*ptr) { - if (IS_SPACE (*ptr)) - *r++ = '\\'; - *r++ = *ptr++; - } - *r = '\0'; - - return ret; -} - -char * -_nm_utils_unescape_spaces (char *str) -{ - guint i, j = 0; - - if (!str) - return NULL; - - for (i = 0; str[i]; i++) { - if (str[i] == '\\' && IS_SPACE (str[i+1])) - i++; - str[j++] = str[i]; - } - str[j] = '\0'; - - return str; -} - -#undef IS_SPACE - -/*****************************************************************************/ - -typedef struct { - gpointer callback_user_data; - GCancellable *cancellable; - NMUtilsInvokeOnIdleCallback callback; - gulong cancelled_id; - guint idle_id; -} InvokeOnIdleData; - -static gboolean -_nm_utils_invoke_on_idle_cb_idle (gpointer user_data) -{ - InvokeOnIdleData *data = user_data; - - data->idle_id = 0; - nm_clear_g_signal_handler (data->cancellable, &data->cancelled_id); - - data->callback (data->callback_user_data, data->cancellable); - nm_g_object_unref (data->cancellable); - g_slice_free (InvokeOnIdleData, data); - return G_SOURCE_REMOVE; -} - -static void -_nm_utils_invoke_on_idle_cb_cancelled (GCancellable *cancellable, - InvokeOnIdleData *data) -{ - /* on cancellation, we invoke the callback synchronously. */ - nm_clear_g_signal_handler (data->cancellable, &data->cancelled_id); - nm_clear_g_source (&data->idle_id); - data->callback (data->callback_user_data, data->cancellable); - nm_g_object_unref (data->cancellable); - g_slice_free (InvokeOnIdleData, data); -} - -void -nm_utils_invoke_on_idle (NMUtilsInvokeOnIdleCallback callback, - gpointer callback_user_data, - GCancellable *cancellable) -{ - InvokeOnIdleData *data; - - g_return_if_fail (callback); - - data = g_slice_new (InvokeOnIdleData); - data->callback = callback; - data->callback_user_data = callback_user_data; - data->cancellable = nm_g_object_ref (cancellable); - if ( cancellable - && !g_cancellable_is_cancelled (cancellable)) { - /* if we are passed a non-cancelled cancellable, we register to the "cancelled" - * signal an invoke the callback synchronously (from the signal handler). - * - * We don't do that, - * - if the cancellable is already cancelled (because we don't want to invoke - * the callback synchronously from the caller). - * - if we have no cancellable at hand. */ - data->cancelled_id = g_signal_connect (cancellable, - "cancelled", - G_CALLBACK (_nm_utils_invoke_on_idle_cb_cancelled), - data); - } else - data->cancelled_id = 0; - data->idle_id = g_idle_add (_nm_utils_invoke_on_idle_cb_idle, data); -} - -/*****************************************************************************/ - -int -nm_utils_getpagesize (void) -{ - static volatile int val = 0; - long l; - int v; - - v = g_atomic_int_get (&val); - - if (G_UNLIKELY (v == 0)) { - l = sysconf (_SC_PAGESIZE); - - g_return_val_if_fail (l > 0 && l < G_MAXINT, 4*1024); - - v = (int) l; - if (!g_atomic_int_compare_and_exchange (&val, 0, v)) { - v = g_atomic_int_get (&val); - g_return_val_if_fail (v > 0, 4*1024); - } - } - - nm_assert (v > 0); -#if NM_MORE_ASSERTS > 5 - nm_assert (v == getpagesize ()); - nm_assert (v == sysconf (_SC_PAGESIZE)); -#endif - - return v; -} - -gboolean -nm_utils_memeqzero (gconstpointer data, gsize length) -{ - const unsigned char *p = data; - int len; - - /* Taken from https://github.com/rustyrussell/ccan/blob/9d2d2c49f053018724bcc6e37029da10b7c3d60d/ccan/mem/mem.c#L92, - * CC-0 licensed. */ - - /* Check first 16 bytes manually */ - for (len = 0; len < 16; len++) { - if (!length) - return TRUE; - if (*p) - return FALSE; - p++; - length--; - } - - /* Now we know that's zero, memcmp with self. */ - return memcmp (data, p, length) == 0; -} - -/** - * nm_utils_bin2hexstr_full: - * @addr: pointer of @length bytes. If @length is zero, this may - * also be %NULL. - * @length: number of bytes in @addr. May also be zero, in which - * case this will return an empty string. - * @delimiter: either '\0', otherwise the output string will have the - * given delimiter character between each two hex numbers. - * @upper_case: if TRUE, use upper case ASCII characters for hex. - * @out: if %NULL, the function will allocate a new buffer of - * either (@length*2+1) or (@length*3) bytes, depending on whether - * a @delimiter is specified. In that case, the allocated buffer will - * be returned and must be freed by the caller. - * If not %NULL, the buffer must already be preallocated and contain - * at least (@length*2+1) or (@length*3) bytes, depending on the delimiter. - * - * Returns: the binary value converted to a hex string. If @out is given, - * this always returns @out. If @out is %NULL, a newly allocated string - * is returned. - */ -char * -nm_utils_bin2hexstr_full (gconstpointer addr, - gsize length, - char delimiter, - gboolean upper_case, - char *out) -{ - const guint8 *in = addr; - const char *LOOKUP = upper_case ? "0123456789ABCDEF" : "0123456789abcdef"; - char *out0; - - if (out) - out0 = out; - else { - out0 = out = g_new (char, delimiter == '\0' - ? length * 2 + 1 - : length * 3); - } - - /* @out must contain at least @length*3 bytes if @delimiter is set, - * otherwise, @length*2+1. */ - - if (length > 0) { - nm_assert (in); - for (;;) { - const guint8 v = *in++; - - *out++ = LOOKUP[v >> 4]; - *out++ = LOOKUP[v & 0x0F]; - length--; - if (!length) - break; - if (delimiter) - *out++ = delimiter; - } - } - - *out = '\0'; - return out0; -} - -guint8 * -nm_utils_hexstr2bin_full (const char *hexstr, - gboolean allow_0x_prefix, - gboolean delimiter_required, - const char *delimiter_candidates, - gsize required_len, - guint8 *buffer, - gsize buffer_len, - gsize *out_len) -{ - const char *in = hexstr; - guint8 *out = buffer; - gboolean delimiter_has = TRUE; - guint8 delimiter = '\0'; - gsize len; - - nm_assert (hexstr); - nm_assert (buffer); - nm_assert (required_len > 0 || out_len); - - if ( allow_0x_prefix - && in[0] == '0' - && in[1] == 'x') - in += 2; - - while (TRUE) { - const guint8 d1 = in[0]; - guint8 d2; - int i1, i2; - - i1 = nm_utils_hexchar_to_int (d1); - if (i1 < 0) - goto fail; - - /* If there's no leading zero (ie "aa:b:cc") then fake it */ - d2 = in[1]; - if ( d2 - && (i2 = nm_utils_hexchar_to_int (d2)) >= 0) { - *out++ = (i1 << 4) + i2; - d2 = in[2]; - if (!d2) - break; - in += 2; - } else { - /* Fake leading zero */ - *out++ = i1; - if (!d2) { - if (!delimiter_has) { - /* when using no delimiter, there must be pairs of hex chars */ - goto fail; - } - break; - } - in += 1; - } - - if (--buffer_len == 0) - goto fail; - - if (delimiter_has) { - if (d2 != delimiter) { - if (delimiter) - goto fail; - if (delimiter_candidates) { - while (delimiter_candidates[0]) { - if (delimiter_candidates++[0] == d2) - delimiter = d2; - } - } - if (!delimiter) { - if (delimiter_required) - goto fail; - delimiter_has = FALSE; - continue; - } - } - in++; - } - } - - len = out - buffer; - if ( required_len == 0 - || len == required_len) { - NM_SET_OUT (out_len, len); - return buffer; - } - -fail: - NM_SET_OUT (out_len, 0); - return NULL; -} - -guint8 * -nm_utils_hexstr2bin_alloc (const char *hexstr, - gboolean allow_0x_prefix, - gboolean delimiter_required, - const char *delimiter_candidates, - gsize required_len, - gsize *out_len) -{ - guint8 *buffer; - gsize buffer_len, len; - - g_return_val_if_fail (hexstr, NULL); - - nm_assert (required_len > 0 || out_len); - - if ( allow_0x_prefix - && hexstr[0] == '0' - && hexstr[1] == 'x') - hexstr += 2; - - if (!hexstr[0]) - goto fail; - - if (required_len > 0) - buffer_len = required_len; - else - buffer_len = strlen (hexstr) / 2 + 3; - - buffer = g_malloc (buffer_len); - - if (nm_utils_hexstr2bin_full (hexstr, - FALSE, - delimiter_required, - delimiter_candidates, - required_len, - buffer, - buffer_len, - &len)) { - NM_SET_OUT (out_len, len); - return buffer; - } - - g_free (buffer); - -fail: - NM_SET_OUT (out_len, 0); - return NULL; -} diff --git a/shared/nm-utils/nm-shared-utils.h b/shared/nm-utils/nm-shared-utils.h deleted file mode 100644 index 65e34959..00000000 --- a/shared/nm-utils/nm-shared-utils.h +++ /dev/null @@ -1,1158 +0,0 @@ -/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ -/* NetworkManager -- Network link manager - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the - * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, - * Boston, MA 02110-1301 USA. - * - * (C) Copyright 2016 Red Hat, Inc. - */ - -#ifndef __NM_SHARED_UTILS_H__ -#define __NM_SHARED_UTILS_H__ - -#include - -/*****************************************************************************/ - -pid_t nm_utils_gettid (void); - -gboolean _nm_assert_on_main_thread (void); - -#if NM_MORE_ASSERTS > 5 -#define NM_ASSERT_ON_MAIN_THREAD() G_STMT_START { nm_assert (_nm_assert_on_main_thread ()); } G_STMT_END -#else -#define NM_ASSERT_ON_MAIN_THREAD() G_STMT_START { ; } G_STMT_END -#endif - -/*****************************************************************************/ - -static inline gboolean -_NM_INT_NOT_NEGATIVE (gssize val) -{ - /* whether an enum (without negative values) is a signed int, depends on compiler options - * and compiler implementation. - * - * When using such an enum for accessing an array, one naturally wants to check - * that the enum is not negative. However, the compiler doesn't like a plain - * comparison "enum_val >= 0", because (if the enum is unsigned), it will warn - * that the expression is always true *duh*. Not even a cast to a signed - * type helps to avoid the compiler warning in any case. - * - * The sole purpose of this function is to avoid a compiler warning, when checking - * that an enum is not negative. */ - return val >= 0; -} - -/* check whether the integer value is smaller than G_MAXINT32. This macro exists - * for the sole purpose, that a plain "((int) value <= G_MAXINT32)" comparison - * may cause the compiler or coverity that this check is always TRUE. But the - * check depends on compile time and the size of C type "int". Of course, most - * of the time in is gint32 and an int value is always <= G_MAXINT32. The check - * exists to catch cases where that is not true. - * - * Together with the G_STATIC_ASSERT(), we make sure that this is always satisfied. */ -G_STATIC_ASSERT (sizeof (int) == sizeof (gint32)); -#if _NM_CC_SUPPORT_GENERIC -#define _NM_INT_LE_MAXINT32(value) \ - ({ \ - _nm_unused typeof (value) _value = (value); \ - \ - _Generic((value), \ - int: TRUE \ - ); \ - }) -#else -#define _NM_INT_LE_MAXINT32(value) ({ \ - _nm_unused typeof (value) _value = (value); \ - _nm_unused const int *_p_value = &_value; \ - \ - TRUE; \ - }) -#endif - -/*****************************************************************************/ - -static inline char -nm_utils_addr_family_to_char (int addr_family) -{ - switch (addr_family) { - case AF_UNSPEC: return 'X'; - case AF_INET: return '4'; - case AF_INET6: return '6'; - } - g_return_val_if_reached ('?'); -} - -static inline gsize -nm_utils_addr_family_to_size (int addr_family) -{ - switch (addr_family) { - case AF_INET: return sizeof (in_addr_t); - case AF_INET6: return sizeof (struct in6_addr); - } - g_return_val_if_reached (0); -} - -#define nm_assert_addr_family(addr_family) \ - nm_assert (NM_IN_SET ((addr_family), AF_INET, AF_INET6)) - -/*****************************************************************************/ - -typedef struct { - union { - guint8 addr_ptr[1]; - in_addr_t addr4; - struct in_addr addr4_struct; - struct in6_addr addr6; - - /* NMIPAddr is really a union for IP addresses. - * However, as ethernet addresses fit in here nicely, use - * it also for an ethernet MAC address. */ - guint8 addr_eth[6 /*ETH_ALEN*/]; - }; -} NMIPAddr; - -extern const NMIPAddr nm_ip_addr_zero; - -static inline void -nm_ip_addr_set (int addr_family, gpointer dst, gconstpointer src) -{ - nm_assert_addr_family (addr_family); - nm_assert (dst); - nm_assert (src); - - memcpy (dst, - src, - (addr_family != AF_INET6) - ? sizeof (in_addr_t) - : sizeof (struct in6_addr)); -} - -gboolean nm_ip_addr_set_from_untrusted (int addr_family, - gpointer dst, - gconstpointer src, - gsize src_len, - int *out_addr_family); - -static inline gboolean -nm_ip4_addr_is_localhost (in_addr_t addr4) -{ - return (addr4 & htonl (0xFF000000u)) == htonl (0x7F000000u); -} - -/*****************************************************************************/ - -#define NM_CMP_RETURN(c) \ - G_STMT_START { \ - const int _cc = (c); \ - if (_cc) \ - return _cc < 0 ? -1 : 1; \ - } G_STMT_END - -#define NM_CMP_SELF(a, b) \ - G_STMT_START { \ - typeof (a) _a = (a); \ - typeof (b) _b = (b); \ - \ - if (_a == _b) \ - return 0; \ - if (!_a) \ - return -1; \ - if (!_b) \ - return 1; \ - } G_STMT_END - -#define NM_CMP_DIRECT(a, b) \ - G_STMT_START { \ - typeof (a) _a = (a); \ - typeof (b) _b = (b); \ - \ - if (_a != _b) \ - return (_a < _b) ? -1 : 1; \ - } G_STMT_END - -#define NM_CMP_DIRECT_MEMCMP(a, b, size) \ - NM_CMP_RETURN (memcmp ((a), (b), (size))) - -#define NM_CMP_DIRECT_STRCMP0(a, b) \ - NM_CMP_RETURN (g_strcmp0 ((a), (b))) - -#define NM_CMP_DIRECT_IN6ADDR(a, b) \ - G_STMT_START { \ - const struct in6_addr *const _a = (a); \ - const struct in6_addr *const _b = (b); \ - NM_CMP_RETURN (memcmp (_a, _b, sizeof (struct in6_addr))); \ - } G_STMT_END - -#define NM_CMP_FIELD(a, b, field) \ - NM_CMP_DIRECT (((a)->field), ((b)->field)) - -#define NM_CMP_FIELD_UNSAFE(a, b, field) \ - G_STMT_START { \ - /* it's unsafe, because it evaluates the arguments more then once. - * This is necessary for bitfields, for which typeof() doesn't work. */ \ - if (((a)->field) != ((b)->field)) \ - return ((a)->field < ((b)->field)) ? -1 : 1; \ - } G_STMT_END - -#define NM_CMP_FIELD_BOOL(a, b, field) \ - NM_CMP_DIRECT (!!((a)->field), !!((b)->field)) - -#define NM_CMP_FIELD_STR(a, b, field) \ - NM_CMP_RETURN (strcmp (((a)->field), ((b)->field))) - -#define NM_CMP_FIELD_STR_INTERNED(a, b, field) \ - G_STMT_START { \ - const char *_a = ((a)->field); \ - const char *_b = ((b)->field); \ - \ - if (_a != _b) { \ - NM_CMP_RETURN (g_strcmp0 (_a, _b)); \ - } \ - } G_STMT_END - -#define NM_CMP_FIELD_STR0(a, b, field) \ - NM_CMP_RETURN (g_strcmp0 (((a)->field), ((b)->field))) - -#define NM_CMP_FIELD_MEMCMP_LEN(a, b, field, len) \ - NM_CMP_RETURN (memcmp (&((a)->field), &((b)->field), \ - MIN (len, sizeof ((a)->field)))) - -#define NM_CMP_FIELD_MEMCMP(a, b, field) \ - NM_CMP_RETURN (memcmp (&((a)->field), \ - &((b)->field), \ - sizeof ((a)->field))) - -#define NM_CMP_FIELD_IN6ADDR(a, b, field) \ - G_STMT_START { \ - const struct in6_addr *const _a = &((a)->field); \ - const struct in6_addr *const _b = &((b)->field); \ - NM_CMP_RETURN (memcmp (_a, _b, sizeof (struct in6_addr))); \ - } G_STMT_END - -/*****************************************************************************/ - -gboolean nm_utils_memeqzero (gconstpointer data, gsize length); - -/*****************************************************************************/ - -/* like g_memdup(). The difference is that the @size argument is of type - * gsize, while g_memdup() has type guint. Since, the size of container types - * like GArray is guint as well, this means trying to g_memdup() an - * array, - * g_memdup (array->data, array->len * sizeof (ElementType)) - * will lead to integer overflow, if there are more than G_MAXUINT/sizeof(ElementType) - * bytes. That seems unnecessarily dangerous to me. - * nm_memdup() avoids that, because its size argument is always large enough - * to contain all data that a GArray can hold. - * - * Another minor difference to g_memdup() is that the glib version also - * returns %NULL if @data is %NULL. E.g. g_memdup(NULL, 1) - * gives %NULL, but nm_memdup(NULL, 1) crashes. I think that - * is desirable, because @size MUST be correct at all times. @size - * may be zero, but one must not claim to have non-zero bytes when - * passing a %NULL @data pointer. - */ -static inline gpointer -nm_memdup (gconstpointer data, gsize size) -{ - gpointer p; - - if (size == 0) - return NULL; - p = g_malloc (size); - memcpy (p, data, size); - return p; -} - -static inline char * -_nm_strndup_a_step (char *s, const char *str, gsize len) -{ - NM_PRAGMA_WARNING_DISABLE ("-Wstringop-truncation"); - if (len > 0) - strncpy (s, str, len); - s[len] = '\0'; - return s; - NM_PRAGMA_WARNING_REENABLE; -} - -/* Similar to g_strndup(), however, if the string (including the terminating - * NUL char) fits into alloca_maxlen, this will alloca() the memory. - * - * It's a mix of strndup() and strndupa(), but deciding based on @alloca_maxlen - * which one to use. - * - * In case malloc() is necessary, @out_str_free will be set (this string - * must be freed afterwards). It is permissible to pass %NULL as @out_str_free, - * if you ensure that len < alloca_maxlen. - * - * Note that just like g_strndup(), this always returns a buffer with @len + 1 - * bytes, even if strlen(@str) is shorter than that (NUL terminated early). We fill - * the buffer with strncpy(), which means, that @str is copied up to the first - * NUL character and then filled with NUL characters. */ -#define nm_strndup_a(alloca_maxlen, str, len, out_str_free) \ - ({ \ - const gsize _alloca_maxlen = (alloca_maxlen); \ - const char *const _str = (str); \ - const gsize _len = (len); \ - char **const _out_str_free = (out_str_free); \ - char *_s; \ - \ - G_STATIC_ASSERT_EXPR ((alloca_maxlen) <= 300); \ - \ - if ( _out_str_free \ - && _len >= _alloca_maxlen) { \ - _s = g_malloc (_len + 1); \ - *_out_str_free = _s; \ - } else { \ - g_assert (_len < _alloca_maxlen); \ - _s = g_alloca (_len + 1); \ - } \ - _nm_strndup_a_step (_s, _str, _len); \ - }) - -/*****************************************************************************/ - -/* generic macro to convert an int to a (heap allocated) string. - * - * Usually, an inline function nm_strdup_int64() would be enough. However, - * that cannot be used for guint64. So, we would also need nm_strdup_uint64(). - * This causes subtle error potential, because the caller needs to ensure to - * use the right one (and compiler isn't going to help as it silently casts). - * - * Instead, this generic macro is supposed to handle all integers correctly. */ -#if _NM_CC_SUPPORT_GENERIC -#define nm_strdup_int(val) \ - _Generic ((val), \ - char: g_strdup_printf ("%d", (int) (val)), \ - \ - signed char: g_strdup_printf ("%d", (signed) (val)), \ - signed short: g_strdup_printf ("%d", (signed) (val)), \ - signed: g_strdup_printf ("%d", (signed) (val)), \ - signed long: g_strdup_printf ("%ld", (signed long) (val)), \ - signed long long: g_strdup_printf ("%lld", (signed long long) (val)), \ - \ - unsigned char: g_strdup_printf ("%u", (unsigned) (val)), \ - unsigned short: g_strdup_printf ("%u", (unsigned) (val)), \ - unsigned: g_strdup_printf ("%u", (unsigned) (val)), \ - unsigned long: g_strdup_printf ("%lu", (unsigned long) (val)), \ - unsigned long long: g_strdup_printf ("%llu", (unsigned long long) (val)) \ - ) -#else -#define nm_strdup_int(val) \ - ( ( sizeof (val) == sizeof (guint64) \ - && ((typeof (val)) -1) > 0) \ - ? g_strdup_printf ("%"G_GUINT64_FORMAT, (guint64) (val)) \ - : g_strdup_printf ("%"G_GINT64_FORMAT, (gint64) (val))) -#endif - -/*****************************************************************************/ - -extern const void *const _NM_PTRARRAY_EMPTY[1]; - -#define NM_PTRARRAY_EMPTY(type) ((type const*) _NM_PTRARRAY_EMPTY) - -static inline void -_nm_utils_strbuf_init (char *buf, gsize len, char **p_buf_ptr, gsize *p_buf_len) -{ - NM_SET_OUT (p_buf_len, len); - NM_SET_OUT (p_buf_ptr, buf); - buf[0] = '\0'; -} - -#define nm_utils_strbuf_init(buf, p_buf_ptr, p_buf_len) \ - G_STMT_START { \ - G_STATIC_ASSERT (G_N_ELEMENTS (buf) == sizeof (buf) && sizeof (buf) > sizeof (char *)); \ - _nm_utils_strbuf_init ((buf), sizeof (buf), (p_buf_ptr), (p_buf_len)); \ - } G_STMT_END -void nm_utils_strbuf_append (char **buf, gsize *len, const char *format, ...) _nm_printf (3, 4); -void nm_utils_strbuf_append_c (char **buf, gsize *len, char c); -void nm_utils_strbuf_append_str (char **buf, gsize *len, const char *str); -void nm_utils_strbuf_append_bin (char **buf, gsize *len, gconstpointer str, gsize str_len); -void nm_utils_strbuf_seek_end (char **buf, gsize *len); - -const char *nm_strquote (char *buf, gsize buf_len, const char *str); - -static inline gboolean -nm_utils_is_separator (const char c) -{ - return NM_IN_SET (c, ' ', '\t'); -} - -/*****************************************************************************/ - -static inline gboolean -nm_gbytes_equal0 (GBytes *a, GBytes *b) -{ - return a == b || (a && b && g_bytes_equal (a, b)); -} - -gboolean nm_utils_gbytes_equal_mem (GBytes *bytes, - gconstpointer mem_data, - gsize mem_len); - -GVariant *nm_utils_gbytes_to_variant_ay (GBytes *bytes); - -/*****************************************************************************/ - -static inline int -nm_utils_hexchar_to_int (char ch) -{ - G_STATIC_ASSERT_EXPR ('0' < 'A'); - G_STATIC_ASSERT_EXPR ('A' < 'a'); - - if (ch >= '0') { - if (ch <= '9') - return ch - '0'; - if (ch >= 'A') { - if (ch <= 'F') - return ((int) ch) + (10 - (int) 'A'); - if (ch >= 'a' && ch <= 'f') - return ((int) ch) + (10 - (int) 'a'); - } - } - return -1; -} - -/*****************************************************************************/ - -const char *nm_utils_dbus_path_get_last_component (const char *dbus_path); - -int nm_utils_dbus_path_cmp (const char *dbus_path_a, const char *dbus_path_b); - -/*****************************************************************************/ - -const char **nm_utils_strsplit_set (const char *str, const char *delimiters, gboolean allow_escaping); - -gssize nm_utils_strv_find_first (char **list, gssize len, const char *needle); - -char **_nm_utils_strv_cleanup (char **strv, - gboolean strip_whitespace, - gboolean skip_empty, - gboolean skip_repeated); - -/*****************************************************************************/ - -#define NM_UTILS_CHECKSUM_LENGTH_MD5 16 -#define NM_UTILS_CHECKSUM_LENGTH_SHA1 20 -#define NM_UTILS_CHECKSUM_LENGTH_SHA256 32 - -#define nm_utils_checksum_get_digest(sum, arr) \ - G_STMT_START { \ - GChecksum *const _sum = (sum); \ - gsize _len; \ - \ - G_STATIC_ASSERT_EXPR ( sizeof (arr) == NM_UTILS_CHECKSUM_LENGTH_MD5 \ - || sizeof (arr) == NM_UTILS_CHECKSUM_LENGTH_SHA1 \ - || sizeof (arr) == NM_UTILS_CHECKSUM_LENGTH_SHA256); \ - G_STATIC_ASSERT_EXPR (sizeof (arr) == G_N_ELEMENTS (arr)); \ - \ - nm_assert (_sum); \ - \ - _len = G_N_ELEMENTS (arr); \ - \ - g_checksum_get_digest (_sum, (arr), &_len); \ - nm_assert (_len == G_N_ELEMENTS (arr)); \ - } G_STMT_END - -#define nm_utils_checksum_get_digest_len(sum, buf, len) \ - G_STMT_START { \ - GChecksum *const _sum = (sum); \ - const gsize _len0 = (len); \ - gsize _len; \ - \ - nm_assert (NM_IN_SET (_len0, NM_UTILS_CHECKSUM_LENGTH_MD5, \ - NM_UTILS_CHECKSUM_LENGTH_SHA1, \ - NM_UTILS_CHECKSUM_LENGTH_SHA256)); \ - nm_assert (_sum); \ - \ - _len = _len0; \ - g_checksum_get_digest (_sum, (buf), &_len); \ - nm_assert (_len == _len0); \ - } G_STMT_END - -/*****************************************************************************/ - -guint32 _nm_utils_ip4_prefix_to_netmask (guint32 prefix); -guint32 _nm_utils_ip4_get_default_prefix (guint32 ip); - -gboolean nm_utils_ip_is_site_local (int addr_family, - const void *address); - -/*****************************************************************************/ - -gboolean nm_utils_parse_inaddr_bin (int addr_family, - const char *text, - int *out_addr_family, - gpointer out_addr); - -gboolean nm_utils_parse_inaddr (int addr_family, - const char *text, - char **out_addr); - -gboolean nm_utils_parse_inaddr_prefix_bin (int addr_family, - const char *text, - int *out_addr_family, - gpointer out_addr, - int *out_prefix); - -gboolean nm_utils_parse_inaddr_prefix (int addr_family, - const char *text, - char **out_addr, - int *out_prefix); - -gint64 _nm_utils_ascii_str_to_int64 (const char *str, guint base, gint64 min, gint64 max, gint64 fallback); -guint64 _nm_utils_ascii_str_to_uint64 (const char *str, guint base, guint64 min, guint64 max, guint64 fallback); - -int _nm_utils_ascii_str_to_bool (const char *str, - int default_value); - -/*****************************************************************************/ - -extern char _nm_utils_to_string_buffer[2096]; - -void nm_utils_to_string_buffer_init (char **buf, gsize *len); -gboolean nm_utils_to_string_buffer_init_null (gconstpointer obj, char **buf, gsize *len); - -/*****************************************************************************/ - -typedef struct { - unsigned flag; - const char *name; -} NMUtilsFlags2StrDesc; - -#define NM_UTILS_FLAGS2STR(f, n) { .flag = f, .name = ""n, } - -#define _NM_UTILS_FLAGS2STR_DEFINE(scope, fcn_name, flags_type, ...) \ -scope const char * \ -fcn_name (flags_type flags, char *buf, gsize len) \ -{ \ - 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); \ -}; - -#define NM_UTILS_FLAGS2STR_DEFINE(fcn_name, flags_type, ...) \ - _NM_UTILS_FLAGS2STR_DEFINE (, fcn_name, flags_type, __VA_ARGS__) -#define NM_UTILS_FLAGS2STR_DEFINE_STATIC(fcn_name, flags_type, ...) \ - _NM_UTILS_FLAGS2STR_DEFINE (static, fcn_name, flags_type, __VA_ARGS__) - -const char *nm_utils_flags2str (const NMUtilsFlags2StrDesc *descs, - gsize n_descs, - unsigned flags, - char *buf, - gsize len); - -/*****************************************************************************/ - -#define NM_UTILS_ENUM2STR(v, n) (void) 0; case v: s = ""n""; break; (void) 0 -#define NM_UTILS_ENUM2STR_IGNORE(v) (void) 0; case v: break; (void) 0 - -#define _NM_UTILS_ENUM2STR_DEFINE(scope, fcn_name, lookup_type, int_fmt, ...) \ -scope const char * \ -fcn_name (lookup_type val, char *buf, gsize len) \ -{ \ - nm_utils_to_string_buffer_init (&buf, &len); \ - if (len) { \ - const char *s = NULL; \ - switch (val) { \ - (void) 0, \ - __VA_ARGS__ \ - (void) 0; \ - }; \ - if (s) \ - g_strlcpy (buf, s, len); \ - else \ - g_snprintf (buf, len, "(%"int_fmt")", val); \ - } \ - return buf; \ -} - -#define NM_UTILS_ENUM2STR_DEFINE(fcn_name, lookup_type, ...) \ - _NM_UTILS_ENUM2STR_DEFINE (, fcn_name, lookup_type, "d", __VA_ARGS__) -#define NM_UTILS_ENUM2STR_DEFINE_STATIC(fcn_name, lookup_type, ...) \ - _NM_UTILS_ENUM2STR_DEFINE (static, fcn_name, lookup_type, "d", __VA_ARGS__) - -/*****************************************************************************/ - -#define _nm_g_slice_free_fcn_define(mem_size) \ -static inline void \ -_nm_g_slice_free_fcn_##mem_size (gpointer mem_block) \ -{ \ - g_slice_free1 (mem_size, mem_block); \ -} - -_nm_g_slice_free_fcn_define (1) -_nm_g_slice_free_fcn_define (2) -_nm_g_slice_free_fcn_define (4) -_nm_g_slice_free_fcn_define (8) -_nm_g_slice_free_fcn_define (10) -_nm_g_slice_free_fcn_define (12) -_nm_g_slice_free_fcn_define (16) - -#define _nm_g_slice_free_fcn1(mem_size) \ - ({ \ - void (*_fcn) (gpointer); \ - \ - /* If mem_size is a compile time constant, the compiler - * will be able to optimize this. Hence, you don't want - * to call this with a non-constant size argument. */ \ - G_STATIC_ASSERT_EXPR ( ((mem_size) == 1) \ - || ((mem_size) == 2) \ - || ((mem_size) == 4) \ - || ((mem_size) == 8) \ - || ((mem_size) == 10) \ - || ((mem_size) == 12) \ - || ((mem_size) == 16)); \ - switch ((mem_size)) { \ - case 1: _fcn = _nm_g_slice_free_fcn_1; break; \ - case 2: _fcn = _nm_g_slice_free_fcn_2; break; \ - case 4: _fcn = _nm_g_slice_free_fcn_4; break; \ - case 8: _fcn = _nm_g_slice_free_fcn_8; break; \ - case 10: _fcn = _nm_g_slice_free_fcn_10; break; \ - case 12: _fcn = _nm_g_slice_free_fcn_12; break; \ - case 16: _fcn = _nm_g_slice_free_fcn_16; break; \ - default: g_assert_not_reached (); _fcn = NULL; break; \ - } \ - _fcn; \ - }) - -/** - * nm_g_slice_free_fcn: - * @type: type argument for sizeof() operator that you would - * pass to g_slice_new(). - * - * Returns: a function pointer with GDestroyNotify signature - * for g_slice_free(type,*). - * - * Only certain types are implemented. You'll get an assertion - * using the wrong type. */ -#define nm_g_slice_free_fcn(type) (_nm_g_slice_free_fcn1 (sizeof (type))) - -#define nm_g_slice_free_fcn_gint64 (nm_g_slice_free_fcn (gint64)) - -/*****************************************************************************/ - -/** - * NMUtilsError: - * @NM_UTILS_ERROR_UNKNOWN: unknown or unclassified error - * @NM_UTILS_ERROR_CANCELLED_DISPOSING: when disposing an object that has - * pending aynchronous operations, the operation is cancelled with this - * 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_CONNECTION_AVAILABLE_INCOMPATIBLE: used for a very particular - * purpose during nm_device_check_connection_compatible() to indicate that - * the profile does not match the device already because their type differs. - * That is, there is a fundamental reason of trying to check a profile that - * cannot possibly match on this device. - * @NM_UTILS_ERROR_CONNECTION_AVAILABLE_UNMANAGED_DEVICE: used for a very particular - * purpose during nm_device_check_connection_available(), to indicate that the - * device is not available because it is unmanaged. - * @NM_UTILS_ERROR_CONNECTION_AVAILABLE_TEMPORARY: the profile is currently not - * available/compatible with the device, but this may be only temporary. - * - * @NM_UTILS_ERROR_INVALID_ARGUMENT: invalid argument. - */ -typedef enum { - NM_UTILS_ERROR_UNKNOWN = 0, /*< nick=Unknown >*/ - NM_UTILS_ERROR_CANCELLED_DISPOSING, /*< nick=CancelledDisposing >*/ - NM_UTILS_ERROR_INVALID_ARGUMENT, /*< nick=InvalidArgument >*/ - - /* the following codes have a special meaning and are exactly used for - * nm_device_check_connection_compatible() and nm_device_check_connection_available(). - * - * Actually, their meaning is not very important (so, don't think too - * hard about the name of these error codes). What is important, is their - * relative order (i.e. the integer value of the codes). When manager - * searches for a suitable device, it will check all devices whether - * a profile can be activated. If they all fail, it will pick the error - * message from the device that returned the *highest* error code, - * in the hope that this message makes the most sense for the caller. - * */ - NM_UTILS_ERROR_CONNECTION_AVAILABLE_INCOMPATIBLE, - NM_UTILS_ERROR_CONNECTION_AVAILABLE_UNMANAGED_DEVICE, - NM_UTILS_ERROR_CONNECTION_AVAILABLE_TEMPORARY, - -} NMUtilsError; - -#define NM_UTILS_ERROR (nm_utils_error_quark ()) -GQuark nm_utils_error_quark (void); - -void nm_utils_error_set_cancelled (GError **error, - gboolean is_disposing, - const char *instance_name); -gboolean nm_utils_error_is_cancelled (GError *error, - gboolean consider_is_disposing); - -gboolean nm_utils_error_is_notfound (GError *error); - -static inline void -nm_utils_error_set_literal (GError **error, int error_code, const char *literal) -{ - g_set_error_literal (error, NM_UTILS_ERROR, error_code, literal); -} - -#define nm_utils_error_set(error, error_code, ...) \ - g_set_error ((error), NM_UTILS_ERROR, error_code, __VA_ARGS__) - -#define nm_utils_error_set_errno(error, errsv, fmt, ...) \ - G_STMT_START { \ - char _bstrerr[NM_STRERROR_BUFSIZE]; \ - \ - g_set_error ((error), \ - NM_UTILS_ERROR, \ - NM_UTILS_ERROR_UNKNOWN, \ - fmt, \ - ##__VA_ARGS__, \ - nm_strerror_native_r (({ \ - const int _errsv = (errsv); \ - \ - ( _errsv >= 0 \ - ? _errsv \ - : ( G_UNLIKELY (_errsv == G_MININT) \ - ? G_MAXINT \ - : -errsv)); \ - }), \ - _bstrerr, \ - sizeof (_bstrerr))); \ - } G_STMT_END - -/*****************************************************************************/ - -gboolean nm_g_object_set_property (GObject *object, - const char *property_name, - const GValue *value, - GError **error); - -gboolean nm_g_object_set_property_string (GObject *object, - const char *property_name, - const char *value, - GError **error); - -gboolean nm_g_object_set_property_string_static (GObject *object, - const char *property_name, - const char *value, - GError **error); - -gboolean nm_g_object_set_property_string_take (GObject *object, - const char *property_name, - char *value, - GError **error); - -gboolean nm_g_object_set_property_boolean (GObject *object, - const char *property_name, - gboolean value, - GError **error); - -gboolean nm_g_object_set_property_char (GObject *object, - const char *property_name, - gint8 value, - GError **error); - -gboolean nm_g_object_set_property_uchar (GObject *object, - const char *property_name, - guint8 value, - GError **error); - -gboolean nm_g_object_set_property_int (GObject *object, - const char *property_name, - int value, - GError **error); - -gboolean nm_g_object_set_property_int64 (GObject *object, - const char *property_name, - gint64 value, - GError **error); - -gboolean nm_g_object_set_property_uint (GObject *object, - const char *property_name, - guint value, - GError **error); - -gboolean nm_g_object_set_property_uint64 (GObject *object, - const char *property_name, - guint64 value, - GError **error); - -gboolean nm_g_object_set_property_flags (GObject *object, - const char *property_name, - GType gtype, - guint value, - GError **error); - -gboolean nm_g_object_set_property_enum (GObject *object, - const char *property_name, - GType gtype, - int value, - GError **error); - -GParamSpec *nm_g_object_class_find_property_from_gtype (GType gtype, - const char *property_name); - -/*****************************************************************************/ - -GType nm_g_type_find_implementing_class_for_property (GType gtype, - const char *pname); - -/*****************************************************************************/ - -typedef enum { - NM_UTILS_STR_UTF8_SAFE_FLAG_NONE = 0, - NM_UTILS_STR_UTF8_SAFE_FLAG_ESCAPE_CTRL = 0x0001, - NM_UTILS_STR_UTF8_SAFE_FLAG_ESCAPE_NON_ASCII = 0x0002, -} NMUtilsStrUtf8SafeFlags; - -const char *nm_utils_buf_utf8safe_escape (gconstpointer buf, gssize buflen, NMUtilsStrUtf8SafeFlags flags, char **to_free); -const char *nm_utils_buf_utf8safe_escape_bytes (GBytes *bytes, NMUtilsStrUtf8SafeFlags flags, char **to_free); -gconstpointer nm_utils_buf_utf8safe_unescape (const char *str, gsize *out_len, gpointer *to_free); - -const char *nm_utils_str_utf8safe_escape (const char *str, NMUtilsStrUtf8SafeFlags flags, char **to_free); -const char *nm_utils_str_utf8safe_unescape (const char *str, char **to_free); - -char *nm_utils_str_utf8safe_escape_cp (const char *str, NMUtilsStrUtf8SafeFlags flags); -char *nm_utils_str_utf8safe_unescape_cp (const char *str); - -char *nm_utils_str_utf8safe_escape_take (char *str, NMUtilsStrUtf8SafeFlags flags); - -static inline void -nm_g_variant_unref_floating (GVariant *var) -{ - /* often a function wants to keep a reference to an input variant. - * It uses g_variant_ref_sink() to either increase the ref-count, - * or take ownership of a possibly floating reference. - * - * If the function doesn't actually want to do anything with the - * input variant, it still must make sure that a passed in floating - * reference is consumed. Hence, this helper which: - * - * - does nothing if @var is not floating - * - unrefs (consumes) @var if it is floating. */ - if (g_variant_is_floating (var)) - g_variant_unref (var); -} - -/*****************************************************************************/ - -static inline int -nm_utf8_collate0 (const char *a, const char *b) -{ - if (!a) - return !b ? 0 : -1; - if (!b) - return 1; - return g_utf8_collate (a, b); -} - -int nm_strcmp_p_with_data (gconstpointer a, gconstpointer b, gpointer user_data); -int nm_cmp_uint32_p_with_data (gconstpointer p_a, gconstpointer p_b, gpointer user_data); -int nm_cmp_int2ptr_p_with_data (gconstpointer p_a, gconstpointer p_b, gpointer user_data); - -/*****************************************************************************/ - -typedef struct { - const char *name; -} NMUtilsNamedEntry; - -typedef struct { - union { - NMUtilsNamedEntry named_entry; - const char *name; - }; - union { - const char *value_str; - gconstpointer value_ptr; - }; -} NMUtilsNamedValue; - -#define nm_utils_named_entry_cmp nm_strcmp_p -#define nm_utils_named_entry_cmp_with_data nm_strcmp_p_with_data - -NMUtilsNamedValue *nm_utils_named_values_from_str_dict (GHashTable *hash, guint *out_len); - -gpointer *nm_utils_hash_keys_to_array (GHashTable *hash, - GCompareDataFunc compare_func, - gpointer user_data, - guint *out_len); - -static inline const char ** -nm_utils_strdict_get_keys (const GHashTable *hash, - gboolean sorted, - guint *out_length) -{ - return (const char **) nm_utils_hash_keys_to_array ((GHashTable *) hash, - sorted ? nm_strcmp_p_with_data : NULL, - NULL, - out_length); -} - -char **nm_utils_strv_make_deep_copied (const char **strv); - -static inline char ** -nm_utils_strv_make_deep_copied_nonnull (const char **strv) -{ - return nm_utils_strv_make_deep_copied (strv) ?: g_new0 (char *, 1); -} - -/*****************************************************************************/ - -gssize nm_utils_ptrarray_find_binary_search (gconstpointer *list, - gsize len, - gconstpointer needle, - GCompareDataFunc cmpfcn, - gpointer user_data, - gssize *out_idx_first, - gssize *out_idx_last); - -gssize nm_utils_array_find_binary_search (gconstpointer list, - gsize elem_size, - gsize len, - gconstpointer needle, - GCompareDataFunc cmpfcn, - gpointer user_data); - -/*****************************************************************************/ - -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) - -/*****************************************************************************/ - -#define NM_UTILS_NS_PER_SECOND ((gint64) 1000000000) -#define NM_UTILS_NS_PER_MSEC ((gint64) 1000000) -#define NM_UTILS_MSEC_PER_SECOND ((gint64) 1000) -#define NM_UTILS_NS_TO_MSEC_CEIL(nsec) (((nsec) + (NM_UTILS_NS_PER_MSEC - 1)) / NM_UTILS_NS_PER_MSEC) - -/*****************************************************************************/ - -int nm_utils_fd_wait_for_event (int fd, int event, gint64 timeout_ns); -ssize_t nm_utils_fd_read_loop (int fd, void *buf, size_t nbytes, bool do_poll); -int nm_utils_fd_read_loop_exact (int fd, void *buf, size_t nbytes, bool do_poll); - -/*****************************************************************************/ - -static inline const char * -nm_utils_dbus_normalize_object_path (const char *path) -{ - /* D-Bus does not allow an empty object path. Hence, whenever we mean NULL / no-object - * on D-Bus, it's path is actually "/". - * - * Normalize that away, and return %NULL in that case. */ - if (path && path[0] == '/' && path[1] == '\0') - return NULL; - return path; -} - -#define NM_DEFINE_GDBUS_ARG_INFO_FULL(name_, ...) \ - ((GDBusArgInfo *) (&((const GDBusArgInfo) { \ - .ref_count = -1, \ - .name = name_, \ - __VA_ARGS__ \ - }))) - -#define NM_DEFINE_GDBUS_ARG_INFO(name_, a_signature) \ - NM_DEFINE_GDBUS_ARG_INFO_FULL ( \ - name_, \ - .signature = a_signature, \ - ) - -#define NM_DEFINE_GDBUS_ARG_INFOS(...) \ - ((GDBusArgInfo **) ((const GDBusArgInfo *[]) { \ - __VA_ARGS__ \ - NULL, \ - })) - -#define NM_DEFINE_GDBUS_PROPERTY_INFO(name_, ...) \ - ((GDBusPropertyInfo *) (&((const GDBusPropertyInfo) { \ - .ref_count = -1, \ - .name = name_, \ - __VA_ARGS__ \ - }))) - -#define NM_DEFINE_GDBUS_PROPERTY_INFO_READABLE(name_, m_signature) \ - NM_DEFINE_GDBUS_PROPERTY_INFO ( \ - name_, \ - .signature = m_signature, \ - .flags = G_DBUS_PROPERTY_INFO_FLAGS_READABLE, \ - ) - -#define NM_DEFINE_GDBUS_PROPERTY_INFOS(...) \ - ((GDBusPropertyInfo **) ((const GDBusPropertyInfo *[]) { \ - __VA_ARGS__ \ - NULL, \ - })) - -#define NM_DEFINE_GDBUS_SIGNAL_INFO_INIT(name_, ...) \ - { \ - .ref_count = -1, \ - .name = name_, \ - __VA_ARGS__ \ - } - -#define NM_DEFINE_GDBUS_SIGNAL_INFO(name_, ...) \ - ((GDBusSignalInfo *) (&((const GDBusSignalInfo) NM_DEFINE_GDBUS_SIGNAL_INFO_INIT (name_, __VA_ARGS__)))) - -#define NM_DEFINE_GDBUS_SIGNAL_INFOS(...) \ - ((GDBusSignalInfo **) ((const GDBusSignalInfo *[]) { \ - __VA_ARGS__ \ - NULL, \ - })) - -#define NM_DEFINE_GDBUS_METHOD_INFO_INIT(name_, ...) \ - { \ - .ref_count = -1, \ - .name = name_, \ - __VA_ARGS__ \ - } - -#define NM_DEFINE_GDBUS_METHOD_INFO(name_, ...) \ - ((GDBusMethodInfo *) (&((const GDBusMethodInfo) NM_DEFINE_GDBUS_METHOD_INFO_INIT (name_, __VA_ARGS__)))) - -#define NM_DEFINE_GDBUS_METHOD_INFOS(...) \ - ((GDBusMethodInfo **) ((const GDBusMethodInfo *[]) { \ - __VA_ARGS__ \ - NULL, \ - })) - -#define NM_DEFINE_GDBUS_INTERFACE_INFO_INIT(name_, ...) \ - { \ - .ref_count = -1, \ - .name = name_, \ - __VA_ARGS__ \ - } - -#define NM_DEFINE_GDBUS_INTERFACE_INFO(name_, ...) \ - ((GDBusInterfaceInfo *) (&((const GDBusInterfaceInfo) NM_DEFINE_GDBUS_INTERFACE_INFO_INIT (name_, __VA_ARGS__)))) - -#define NM_DEFINE_GDBUS_INTERFACE_VTABLE(...) \ - ((GDBusInterfaceVTable *) (&((const GDBusInterfaceVTable) { \ - __VA_ARGS__ \ - }))) - -/*****************************************************************************/ - -guint64 nm_utils_get_start_time_for_pid (pid_t pid, char *out_state, pid_t *out_ppid); - -/*****************************************************************************/ - -gpointer _nm_utils_user_data_pack (int nargs, gconstpointer *args); - -#define nm_utils_user_data_pack(...) \ - _nm_utils_user_data_pack(NM_NARG (__VA_ARGS__), (gconstpointer[]) { __VA_ARGS__ }) - -void _nm_utils_user_data_unpack (gpointer user_data, int nargs, ...); - -#define nm_utils_user_data_unpack(user_data, ...) \ - _nm_utils_user_data_unpack(user_data, NM_NARG (__VA_ARGS__), __VA_ARGS__) - -/*****************************************************************************/ - -const char *_nm_utils_escape_spaces (const char *str, char **to_free); -char *_nm_utils_unescape_spaces (char *str); - -/*****************************************************************************/ - -typedef void (*NMUtilsInvokeOnIdleCallback) (gpointer callback_user_data, - GCancellable *cancellable); - -void nm_utils_invoke_on_idle (NMUtilsInvokeOnIdleCallback callback, - gpointer callback_user_data, - GCancellable *cancellable); - -/*****************************************************************************/ - -static inline void -nm_strv_ptrarray_add_string_take (GPtrArray *cmd, - char *str) -{ - nm_assert (cmd); - nm_assert (str); - - g_ptr_array_add (cmd, str); -} - -static inline void -nm_strv_ptrarray_add_string_dup (GPtrArray *cmd, - const char *str) -{ - nm_strv_ptrarray_add_string_take (cmd, - g_strdup (str)); -} - -#define nm_strv_ptrarray_add_string_concat(cmd, ...) \ - nm_strv_ptrarray_add_string_take ((cmd), g_strconcat (__VA_ARGS__, NULL)) - -#define nm_strv_ptrarray_add_string_printf(cmd, ...) \ - nm_strv_ptrarray_add_string_take ((cmd), g_strdup_printf (__VA_ARGS__)) - -#define nm_strv_ptrarray_add_int(cmd, val) \ - nm_strv_ptrarray_add_string_take ((cmd), nm_strdup_int (val)) - -static inline void -nm_strv_ptrarray_take_gstring (GPtrArray *cmd, - GString **gstr) -{ - nm_assert (gstr && *gstr); - - nm_strv_ptrarray_add_string_take (cmd, - g_string_free (g_steal_pointer (gstr), - FALSE)); -} - -/*****************************************************************************/ - -int nm_utils_getpagesize (void); - -/*****************************************************************************/ - -char *nm_utils_bin2hexstr_full (gconstpointer addr, - gsize length, - char delimiter, - gboolean upper_case, - char *out); - -guint8 *nm_utils_hexstr2bin_full (const char *hexstr, - gboolean allow_0x_prefix, - gboolean delimiter_required, - const char *delimiter_candidates, - gsize required_len, - guint8 *buffer, - gsize buffer_len, - gsize *out_len); - -#define nm_utils_hexstr2bin_buf(hexstr, allow_0x_prefix, delimiter_required, delimiter_candidates, buffer) \ - nm_utils_hexstr2bin_full ((hexstr), (allow_0x_prefix), (delimiter_required), (delimiter_candidates), G_N_ELEMENTS (buffer), (buffer), G_N_ELEMENTS (buffer), NULL) - -guint8 *nm_utils_hexstr2bin_alloc (const char *hexstr, - gboolean allow_0x_prefix, - gboolean delimiter_required, - const char *delimiter_candidates, - gsize required_len, - gsize *out_len); - -#endif /* __NM_SHARED_UTILS_H__ */ diff --git a/shared/nm-utils/nm-test-utils.h b/shared/nm-utils/nm-test-utils.h index c5ea5e3f..d0ec7d9f 100644 --- a/shared/nm-utils/nm-test-utils.h +++ b/shared/nm-utils/nm-test-utils.h @@ -1144,12 +1144,13 @@ nmtst_uuid_generate (void) #endif -#define NMTST_SWAP(x,y) \ +#define NMTST_SWAP(x, y) \ G_STMT_START { \ - char __nmtst_swap_temp[sizeof(x) == sizeof(y) ? (signed) sizeof(x) : -1]; \ - memcpy(__nmtst_swap_temp, &y, sizeof(x)); \ - memcpy(&y, &x, sizeof(x)); \ - memcpy(&x, __nmtst_swap_temp, sizeof(x)); \ + char __nmtst_swap_temp[sizeof((x)) == sizeof((y)) ? (signed) sizeof((x)) : -1]; \ + \ + memcpy(__nmtst_swap_temp, &(y), sizeof (__nmtst_swap_temp)); \ + memcpy(&(y), &(x), sizeof (__nmtst_swap_temp)); \ + memcpy(&(x), __nmtst_swap_temp, sizeof (__nmtst_swap_temp)); \ } G_STMT_END #define nmtst_assert_str_has_substr(str, substr) \ diff --git a/shared/nm-utils/nm-time-utils.c b/shared/nm-utils/nm-time-utils.c deleted file mode 100644 index ae526c34..00000000 --- a/shared/nm-utils/nm-time-utils.c +++ /dev/null @@ -1,273 +0,0 @@ -/* NetworkManager -- Network link manager - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the - * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, - * Boston, MA 02110-1301 USA. - * - * (C) Copyright 2018 Red Hat, Inc. - */ - -#include "nm-default.h" - -#include "nm-time-utils.h" - -/*****************************************************************************/ - -typedef struct { - /* the offset to the native clock, in seconds. */ - gint64 offset_sec; - clockid_t clk_id; -} GlobalState; - -static const GlobalState *volatile p_global_state; - -static const GlobalState * -_t_init_global_state (void) -{ - static GlobalState global_state = { }; - static gsize init_once = 0; - const GlobalState *p; - clockid_t clk_id; - struct timespec tp; - gint64 offset_sec; - int r; - - clk_id = CLOCK_BOOTTIME; - r = clock_gettime (clk_id, &tp); - if (r == -1 && errno == EINVAL) { - clk_id = CLOCK_MONOTONIC; - r = clock_gettime (clk_id, &tp); - } - - /* The only failure we tolerate is that CLOCK_BOOTTIME is not supported. - * Other than that, we rely on kernel to not fail on this. */ - g_assert (r == 0); - g_assert (tp.tv_nsec >= 0 && tp.tv_nsec < NM_UTILS_NS_PER_SECOND); - - /* Calculate an offset for the time stamp. - * - * We always want positive values, because then we can initialize - * a timestamp with 0 and be sure, that it will be less then any - * value nm_utils_get_monotonic_timestamp_*() might return. - * For this to be true also for nm_utils_get_monotonic_timestamp_s() at - * early boot, we have to shift the timestamp to start counting at - * least from 1 second onward. - * - * Another advantage of shifting is, that this way we make use of the whole 31 bit - * range of signed int, before the time stamp for nm_utils_get_monotonic_timestamp_s() - * wraps (~68 years). - **/ - offset_sec = (- ((gint64) tp.tv_sec)) + 1; - - if (!g_once_init_enter (&init_once)) { - /* there was a race. We expect the pointer to be fully initialized now. */ - p = g_atomic_pointer_get (&p_global_state); - g_assert (p); - return p; - } - - global_state.offset_sec = offset_sec; - global_state.clk_id = clk_id; - p = &global_state; - g_atomic_pointer_set (&p_global_state, p); - g_once_init_leave (&init_once, 1); - - _nm_utils_monotonic_timestamp_initialized (&tp, - p->offset_sec, - p->clk_id == CLOCK_BOOTTIME); - - return p; -} - -#define _t_get_global_state() \ - ({ \ - const GlobalState *_p; \ - \ - _p = g_atomic_pointer_get (&p_global_state); \ - (G_LIKELY (_p) ? _p : _t_init_global_state ()); \ - }) - -#define _t_clock_gettime_eval(p, tp) \ - ({ \ - struct timespec *const _tp = (tp); \ - const GlobalState *const _p2 = (p); \ - int _r; \ - \ - nm_assert (_tp); \ - \ - _r = clock_gettime (_p2->clk_id, _tp); \ - \ - nm_assert (_r == 0); \ - nm_assert (_tp->tv_nsec >= 0 && _tp->tv_nsec < NM_UTILS_NS_PER_SECOND); \ - \ - _p2; \ - }) - -#define _t_clock_gettime(tp) \ - _t_clock_gettime_eval (_t_get_global_state (), tp); - -/*****************************************************************************/ - -/** - * nm_utils_get_monotonic_timestamp_ns: - * - * Returns: a monotonically increasing time stamp in nanoseconds, - * starting at an unspecified offset. See clock_gettime(), %CLOCK_BOOTTIME. - * - * The returned value will start counting at an undefined point - * in the past and will always be positive. - * - * All the nm_utils_get_monotonic_timestamp_*s functions return the same - * timestamp but in different scales (nsec, usec, msec, sec). - **/ -gint64 -nm_utils_get_monotonic_timestamp_ns (void) -{ - const GlobalState *p; - struct timespec tp; - - p = _t_clock_gettime (&tp); - - /* Although the result will always be positive, we return a signed - * integer, which makes it easier to calculate time differences (when - * you want to subtract signed values). - **/ - return (((gint64) tp.tv_sec) + p->offset_sec) * NM_UTILS_NS_PER_SECOND + - tp.tv_nsec; -} - -/** - * nm_utils_get_monotonic_timestamp_us: - * - * Returns: a monotonically increasing time stamp in microseconds, - * starting at an unspecified offset. See clock_gettime(), %CLOCK_BOOTTIME. - * - * The returned value will start counting at an undefined point - * in the past and will always be positive. - * - * All the nm_utils_get_monotonic_timestamp_*s functions return the same - * timestamp but in different scales (nsec, usec, msec, sec). - **/ -gint64 -nm_utils_get_monotonic_timestamp_us (void) -{ - const GlobalState *p; - struct timespec tp; - - p = _t_clock_gettime (&tp); - - /* Although the result will always be positive, we return a signed - * integer, which makes it easier to calculate time differences (when - * you want to subtract signed values). - **/ - return (((gint64) tp.tv_sec) + p->offset_sec) * ((gint64) G_USEC_PER_SEC) + - (tp.tv_nsec / (NM_UTILS_NS_PER_SECOND/G_USEC_PER_SEC)); -} - -/** - * nm_utils_get_monotonic_timestamp_ms: - * - * Returns: a monotonically increasing time stamp in milliseconds, - * starting at an unspecified offset. See clock_gettime(), %CLOCK_BOOTTIME. - * - * The returned value will start counting at an undefined point - * in the past and will always be positive. - * - * All the nm_utils_get_monotonic_timestamp_*s functions return the same - * timestamp but in different scales (nsec, usec, msec, sec). - **/ -gint64 -nm_utils_get_monotonic_timestamp_ms (void) -{ - const GlobalState *p; - struct timespec tp; - - p = _t_clock_gettime (&tp); - - /* Although the result will always be positive, we return a signed - * integer, which makes it easier to calculate time differences (when - * you want to subtract signed values). - **/ - return (((gint64) tp.tv_sec) + p->offset_sec) * ((gint64) 1000) + - (tp.tv_nsec / (NM_UTILS_NS_PER_SECOND/1000)); -} - -/** - * nm_utils_get_monotonic_timestamp_s: - * - * Returns: nm_utils_get_monotonic_timestamp_ms() in seconds (throwing - * away sub second parts). The returned value will always be positive. - * - * This value wraps after roughly 68 years which should be fine for any - * practical purpose. - * - * All the nm_utils_get_monotonic_timestamp_*s functions return the same - * timestamp but in different scales (nsec, usec, msec, sec). - **/ -gint32 -nm_utils_get_monotonic_timestamp_s (void) -{ - const GlobalState *p; - struct timespec tp; - - p = _t_clock_gettime (&tp); - - return (((gint64) tp.tv_sec) + p->offset_sec); -} - -/** - * nm_utils_monotonic_timestamp_as_boottime: - * @timestamp: the monotonic-timestamp that should be converted into CLOCK_BOOTTIME. - * @timestamp_ns_per_tick: How many nano seconds make one unit of @timestamp? E.g. if - * @timestamp is in unit seconds, pass %NM_UTILS_NS_PER_SECOND; @timestamp in nano - * seconds, pass 1; @timestamp in milli seconds, pass %NM_UTILS_NS_PER_SECOND/1000; etc. - * - * Returns: the monotonic-timestamp as CLOCK_BOOTTIME, as returned by clock_gettime(). - * The unit is the same as the passed in @timestamp basd on @timestamp_ns_per_tick. - * E.g. if you passed @timestamp in as seconds, it will return boottime in seconds. - * If @timestamp is a non-positive, it returns -1. Note that a (valid) monotonic-timestamp - * is always positive. - * - * On older kernels that don't support CLOCK_BOOTTIME, the returned time is instead CLOCK_MONOTONIC. - **/ -gint64 -nm_utils_monotonic_timestamp_as_boottime (gint64 timestamp, gint64 timestamp_ns_per_tick) -{ - const GlobalState *p; - gint64 offset; - - /* only support ns-per-tick being a multiple of 10. */ - g_return_val_if_fail (timestamp_ns_per_tick == 1 - || (timestamp_ns_per_tick > 0 && - timestamp_ns_per_tick <= NM_UTILS_NS_PER_SECOND && - timestamp_ns_per_tick % 10 == 0), - -1); - - /* Check that the timestamp is in a valid range. */ - g_return_val_if_fail (timestamp >= 0, -1); - - /* if the caller didn't yet ever fetch a monotonic-timestamp, he cannot pass any meaningful - * value (because he has no idea what these timestamps would be). That would be a bug. */ - nm_assert (g_atomic_pointer_get (&p_global_state)); - - p = _t_get_global_state (); - - /* calculate the offset of monotonic-timestamp to boottime. offset_s is <= 1. */ - offset = p->offset_sec * (NM_UTILS_NS_PER_SECOND / timestamp_ns_per_tick); - - /* check for overflow. */ - g_return_val_if_fail (offset > 0 || timestamp < G_MAXINT64 + offset, G_MAXINT64); - - return timestamp - offset; -} diff --git a/shared/nm-utils/nm-time-utils.h b/shared/nm-utils/nm-time-utils.h deleted file mode 100644 index 7e4f4f25..00000000 --- a/shared/nm-utils/nm-time-utils.h +++ /dev/null @@ -1,45 +0,0 @@ -/* NetworkManager -- Network link manager - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the - * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, - * Boston, MA 02110-1301 USA. - * - * (C) Copyright 2018 Red Hat, Inc. - */ - -#ifndef __NM_TIME_UTILS_H__ -#define __NM_TIME_UTILS_H__ - -gint64 nm_utils_get_monotonic_timestamp_ns (void); -gint64 nm_utils_get_monotonic_timestamp_us (void); -gint64 nm_utils_get_monotonic_timestamp_ms (void); -gint32 nm_utils_get_monotonic_timestamp_s (void); -gint64 nm_utils_monotonic_timestamp_as_boottime (gint64 timestamp, gint64 timestamp_ticks_per_ns); - -static inline gint64 -nm_utils_get_monotonic_timestamp_ns_cached (gint64 *cache_now) -{ - return (*cache_now) - ?: (*cache_now = nm_utils_get_monotonic_timestamp_ns ()); -} - -struct timespec; - -/* this function must be implemented to handle the notification when - * the first monotonic-timestamp is fetched. */ -extern void _nm_utils_monotonic_timestamp_initialized (const struct timespec *tp, - gint64 offset_sec, - gboolean is_boottime); - -#endif /* __NM_TIME_UTILS_H__ */ diff --git a/shared/nm-utils/nm-udev-utils.c b/shared/nm-utils/nm-udev-utils.c deleted file mode 100644 index 5d0919b3..00000000 --- a/shared/nm-utils/nm-udev-utils.c +++ /dev/null @@ -1,291 +0,0 @@ -/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ -/* nm-udev-utils.c - udev utils functions - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2, or (at your option) - * any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Copyright (C) 2017 Red Hat, Inc. - */ - -#include "nm-default.h" - -#include "nm-udev-utils.h" - -#include - -struct _NMPUdevClient { - char **subsystems; - GSource *watch_source; - struct udev *udev; - struct udev_monitor *monitor; - NMUdevClientEvent event_handler; - gpointer event_user_data; -}; - -/*****************************************************************************/ - -gboolean -nm_udev_utils_property_as_boolean (const char *uproperty) -{ - /* taken from g_udev_device_get_property_as_boolean() */ - - if (uproperty) { - if ( strcmp (uproperty, "1") == 0 - || g_ascii_strcasecmp (uproperty, "true") == 0) - return TRUE; - } - return FALSE; -} - -const char * -nm_udev_utils_property_decode (const char *uproperty, char **to_free) -{ - const char *p; - char *unescaped = NULL; - char *n = NULL; - - if (!uproperty) { - *to_free = NULL; - return NULL; - } - - p = uproperty; - while (*p) { - int a, b; - - if ( p[0] == '\\' - && p[1] == 'x' - && (a = g_ascii_xdigit_value (p[2])) >= 0 - && (b = g_ascii_xdigit_value (p[3])) >= 0 - && (a || b)) { - if (!n) { - gssize l = p - uproperty; - - unescaped = g_malloc (l + strlen (p) + 1 - 3); - memcpy (unescaped, uproperty, l); - n = &unescaped[l]; - } - *n++ = (a << 4) | b; - p += 4; - } else { - if (n) - *n++ = *p; - p++; - } - } - - if (!n) { - *to_free = NULL; - return uproperty; - } - - *n++ = '\0'; - return (*to_free = unescaped); -} - -char * -nm_udev_utils_property_decode_cp (const char *uproperty) -{ - char *cpy; - - uproperty = nm_udev_utils_property_decode (uproperty, &cpy); - return cpy ?: g_strdup (uproperty); -} - -/*****************************************************************************/ - -static void -_subsystem_split (const char *subsystem_full, - const char **out_subsystem, - const char **out_devtype, - char **to_free) -{ - char *tmp, *s; - - nm_assert (subsystem_full); - nm_assert (out_subsystem); - nm_assert (out_devtype); - nm_assert (to_free); - - s = strstr (subsystem_full, "/"); - if (s) { - tmp = g_strdup (subsystem_full); - s = &tmp[s - subsystem_full]; - *s = '\0'; - *out_subsystem = tmp; - *out_devtype = &s[1]; - *to_free = tmp; - } else { - *out_subsystem = subsystem_full; - *out_devtype = NULL; - *to_free = NULL; - } -} - -static struct udev_enumerate * -nm_udev_utils_enumerate (struct udev *uclient, - const char *const*subsystems) -{ - struct udev_enumerate *enumerate; - guint n; - - enumerate = udev_enumerate_new (uclient); - - if (subsystems) { - for (n = 0; subsystems[n]; n++) { - const char *subsystem; - const char *devtype; - gs_free char *to_free = NULL; - - _subsystem_split (subsystems[n], &subsystem, &devtype, &to_free); - - udev_enumerate_add_match_subsystem (enumerate, subsystem); - - if (devtype != NULL) - udev_enumerate_add_match_property (enumerate, "DEVTYPE", devtype); - } - } - - return enumerate; -} - -struct udev * -nm_udev_client_get_udev (NMUdevClient *self) -{ - g_return_val_if_fail (self, NULL); - - return self->udev; -} - -struct udev_enumerate * -nm_udev_client_enumerate_new (NMUdevClient *self) -{ - g_return_val_if_fail (self, NULL); - - return nm_udev_utils_enumerate (self->udev, (const char *const*) self->subsystems); -} - -/*****************************************************************************/ - -static gboolean -monitor_event (GIOChannel *source, - GIOCondition condition, - gpointer user_data) -{ - NMUdevClient *self = user_data; - struct udev_device *udevice; - - if (!self->monitor) - goto out; - - udevice = udev_monitor_receive_device (self->monitor); - if (udevice == NULL) - goto out; - - self->event_handler (self, - udevice, - self->event_user_data); - udev_device_unref (udevice); - -out: - return TRUE; -} - -/** - * nm_udev_client_new: - * @subsystems: the subsystems - * @event_handler: callback for events - * @event_user_data: user-data for @event_handler - * - * Basically, it is g_udev_client_new(), and most notably - * g_udev_client_constructed(). - * - * Returns: a new NMUdevClient instance. - */ -NMUdevClient * -nm_udev_client_new (const char *const*subsystems, - NMUdevClientEvent event_handler, - gpointer event_user_data) -{ - NMUdevClient *self; - GIOChannel *channel; - guint n; - - self = g_slice_new0 (NMUdevClient); - - self->event_handler = event_handler; - self->event_user_data = event_user_data; - self->subsystems = subsystems && subsystems[0] ? g_strdupv ((char **) subsystems) : NULL; - - self->udev = udev_new (); - if (!self->udev) - goto fail; - - /* connect to event source */ - if (self->event_handler) { - self->monitor = udev_monitor_new_from_netlink (self->udev, "udev"); - if (!self->monitor) - goto fail; - - if (self->subsystems) { - /* install subsystem filters to only wake up for certain events */ - for (n = 0; self->subsystems[n]; n++) { - gs_free char *to_free = NULL; - const char *subsystem; - const char *devtype; - - _subsystem_split (self->subsystems[n], &subsystem, &devtype, &to_free); - udev_monitor_filter_add_match_subsystem_devtype (self->monitor, subsystem, devtype); - } - - /* listen to events, and buffer them */ - udev_monitor_set_receive_buffer_size (self->monitor, 4*1024*1024); - udev_monitor_enable_receiving (self->monitor); - channel = g_io_channel_unix_new (udev_monitor_get_fd (self->monitor)); - self->watch_source = g_io_create_watch (channel, G_IO_IN); - g_io_channel_unref (channel); - g_source_set_callback (self->watch_source, (GSourceFunc)(void (*) (void)) monitor_event, self, NULL); - g_source_attach (self->watch_source, g_main_context_get_thread_default ()); - g_source_unref (self->watch_source); - } - } - - return self; - -fail: - return nm_udev_client_unref (self); -} - -NMUdevClient * -nm_udev_client_unref (NMUdevClient *self) -{ - if (!self) - return NULL; - - if (self->watch_source) { - g_source_destroy (self->watch_source); - self->watch_source = NULL; - } - - udev_monitor_unref (self->monitor); - self->monitor = NULL; - udev_unref (self->udev); - self->udev = NULL; - - g_strfreev (self->subsystems); - - g_slice_free (NMUdevClient, self); - - return NULL; -} diff --git a/shared/nm-utils/nm-udev-utils.h b/shared/nm-utils/nm-udev-utils.h deleted file mode 100644 index 911e8a27..00000000 --- a/shared/nm-utils/nm-udev-utils.h +++ /dev/null @@ -1,48 +0,0 @@ -/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ -/* nm-udev-utils.h - udev utils functions - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2, or (at your option) - * any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Copyright (C) 2017 Red Hat, Inc. - */ - -#ifndef __NM_UDEV_UTILS_H__ -#define __NM_UDEV_UTILS_H__ - -struct udev; -struct udev_device; -struct udev_enumerate; - -gboolean nm_udev_utils_property_as_boolean (const char *uproperty); -const char *nm_udev_utils_property_decode (const char *uproperty, char **to_free); -char *nm_udev_utils_property_decode_cp (const char *uproperty); - -typedef struct _NMPUdevClient NMUdevClient; - -typedef void (*NMUdevClientEvent) (NMUdevClient *udev_client, - struct udev_device *udevice, - gpointer event_user_data); - -NMUdevClient *nm_udev_client_new (const char *const*subsystems, - NMUdevClientEvent event_handler, - gpointer event_user_data); - -NMUdevClient *nm_udev_client_unref (NMUdevClient *self); - -struct udev *nm_udev_client_get_udev (NMUdevClient *self); - -struct udev_enumerate *nm_udev_client_enumerate_new (NMUdevClient *self); - -#endif /* __NM_UDEV_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 78d041df..fd982acf 100644 --- a/shared/nm-utils/nm-vpn-editor-plugin-call.h +++ b/shared/nm-utils/nm-vpn-editor-plugin-call.h @@ -32,7 +32,7 @@ #include /* we make use of other internal header files, you need those too. */ -#include "nm-macros-internal.h" +#include "nm-glib-aux/nm-macros-internal.h" /*****************************************************************************/ diff --git a/shared/nm-utils/tests/test-shared-general.c b/shared/nm-utils/tests/test-shared-general.c index d53b21d9..83cffd7f 100644 --- a/shared/nm-utils/tests/test-shared-general.c +++ b/shared/nm-utils/tests/test-shared-general.c @@ -21,9 +21,9 @@ #include "nm-default.h" -#include "nm-utils/nm-time-utils.h" -#include "nm-utils/nm-random-utils.h" -#include "nm-utils/unaligned.h" +#include "nm-std-aux/unaligned.h" +#include "nm-glib-aux/nm-random-utils.h" +#include "nm-glib-aux/nm-time-utils.h" #include "nm-utils/nm-test-utils.h" @@ -248,6 +248,201 @@ test_unaligned (void) /*****************************************************************************/ +static void +_strv_cmp_fuzz_input (const char *const*in, + gssize l, + const char ***out_strv_free_shallow, + char ***out_strv_free_deep, + const char *const* *out_s1, + const char *const* *out_s2) +{ + const char **strv; + gsize i; + + /* Fuzz the input argument. It will return two output arrays that are semantically + * equal the input. */ + + if (nmtst_get_rand_bool ()) { + char **ss; + + if (l < 0) + ss = g_strdupv ((char **) in); + else if (l == 0) { + ss = nmtst_get_rand_bool () + ? NULL + : g_new0 (char *, 1); + } else { + ss = nm_memdup (in, sizeof (const char *) * l); + for (i = 0; i < (gsize) l; i++) + ss[i] = g_strdup (ss[i]); + } + strv = (const char **) ss; + *out_strv_free_deep = ss; + } else { + if (l < 0) { + strv = in + ? nm_memdup (in, sizeof (const char *) * (NM_PTRARRAY_LEN (in) + 1)) + : NULL; + } else if (l == 0) { + strv = nmtst_get_rand_bool () + ? NULL + : g_new0 (const char *, 1); + } else + strv = nm_memdup (in, sizeof (const char *) * l); + *out_strv_free_shallow = strv; + } + + *out_s1 = in; + *out_s2 = strv; + + if (nmtst_get_rand_bool ()) { + /* randomly swap the original and the clone. That means, out_s1 is either + * the input argument (as-is) or the sementically equal clone. */ + NMTST_SWAP (*out_s1, *out_s2); + } + if (nmtst_get_rand_bool ()) { + /* randomly make s1 and s2 the same. This is for testing that + * comparing two identical pointers yields the same result. */ + *out_s2 = *out_s1; + } +} + +static void +_strv_cmp_free_deep (char **strv, + gssize len) +{ + gssize i; + + if (strv) { + if (len < 0) + g_strfreev (strv); + else { + for (i = 0; i < len; i++) + g_free (strv[i]); + g_free (strv); + } + } +} + +static void +test_strv_cmp (void) +{ + const char *const strv0[1] = { }; + const char *const strv1[2] = { "", }; + +#define _STRV_CMP(a1, l1, a2, l2, equal) \ + G_STMT_START { \ + gssize _l1 = (l1); \ + gssize _l2 = (l2); \ + const char *const*_a1; \ + const char *const*_a2; \ + const char *const*_a1x; \ + const char *const*_a2x; \ + char **_a1_free_deep = NULL; \ + char **_a2_free_deep = NULL; \ + gs_free const char **_a1_free_shallow = NULL; \ + gs_free const char **_a2_free_shallow = NULL; \ + int _c1, _c2; \ + \ + _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); \ + if (equal) { \ + g_assert_cmpint (_c1, ==, 0); \ + g_assert_cmpint (_c2, ==, 0); \ + } else { \ + g_assert_cmpint (_c1, ==, -1); \ + g_assert_cmpint (_c2, ==, 1); \ + } \ + \ + /* 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); \ + \ + _strv_cmp_free_deep (_a1_free_deep, _l1); \ + _strv_cmp_free_deep (_a2_free_deep, _l2); \ + } G_STMT_END + + _STRV_CMP (NULL, -1, NULL, -1, TRUE); + + _STRV_CMP (NULL, -1, NULL, 0, FALSE); + _STRV_CMP (NULL, -1, strv0, 0, FALSE); + _STRV_CMP (NULL, -1, strv0, -1, FALSE); + + _STRV_CMP (NULL, 0, NULL, 0, TRUE); + _STRV_CMP (NULL, 0, strv0, 0, TRUE); + _STRV_CMP (NULL, 0, strv0, -1, TRUE); + _STRV_CMP (strv0, 0, strv0, 0, TRUE); + _STRV_CMP (strv0, 0, strv0, -1, TRUE); + _STRV_CMP (strv0, -1, strv0, -1, TRUE); + + _STRV_CMP (NULL, 0, strv1, -1, FALSE); + _STRV_CMP (NULL, 0, strv1, 1, FALSE); + _STRV_CMP (strv0, 0, strv1, -1, FALSE); + _STRV_CMP (strv0, 0, strv1, 1, FALSE); + _STRV_CMP (strv0, -1, strv1, -1, FALSE); + _STRV_CMP (strv0, -1, strv1, 1, FALSE); + + _STRV_CMP (strv1, -1, strv1, 1, TRUE); + _STRV_CMP (strv1, 1, strv1, 1, TRUE); +} + +/*****************************************************************************/ + +static void +_do_strstrip_avoid_copy (const char *str) +{ + gs_free char *str1 = g_strdup (str); + gs_free char *str2 = g_strdup (str); + gs_free char *str3 = NULL; + gs_free char *str4 = NULL; + const char *s3; + const char *s4; + + if (str1) + g_strstrip (str1); + + nm_strstrip (str2); + + g_assert_cmpstr (str1, ==, str2); + + s3 = nm_strstrip_avoid_copy (str, &str3); + g_assert_cmpstr (str1, ==, s3); + + s4 = nm_strstrip_avoid_copy_a (10, str, &str4); + g_assert_cmpstr (str1, ==, s4); + g_assert (!str == !s4); + g_assert (!s4 || strlen (s4) <= strlen (str)); + if (s4 && s4 == &str[strlen (str) - strlen (s4)]) { + g_assert (!str4); + g_assert (s3 == s4); + } else if (s4 && strlen (s4) >= 10) { + g_assert (str4); + g_assert (s4 == str4); + } else + g_assert (!str4); + + if (!nm_streq0 (str1, str)) + _do_strstrip_avoid_copy (str1); +} + +static void +test_strstrip_avoid_copy (void) +{ + _do_strstrip_avoid_copy (NULL); + _do_strstrip_avoid_copy (""); + _do_strstrip_avoid_copy (" "); + _do_strstrip_avoid_copy (" a "); + _do_strstrip_avoid_copy (" 012345678 "); + _do_strstrip_avoid_copy (" 0123456789 "); + _do_strstrip_avoid_copy (" 01234567890 "); + _do_strstrip_avoid_copy (" 012345678901 "); +} +/*****************************************************************************/ + NMTST_DEFINE (); int main (int argc, char **argv) @@ -261,6 +456,8 @@ int main (int argc, char **argv) g_test_add_func ("/general/test_nm_strndup_a", test_nm_strndup_a); g_test_add_func ("/general/test_nm_ip4_addr_is_localhost", test_nm_ip4_addr_is_localhost); g_test_add_func ("/general/test_unaligned", test_unaligned); + g_test_add_func ("/general/test_strv_cmp", test_strv_cmp); + g_test_add_func ("/general/test_strstrip_avoid_copy", test_strstrip_avoid_copy); return g_test_run (); } diff --git a/shared/nm-utils/unaligned.h b/shared/nm-utils/unaligned.h deleted file mode 100644 index 00c17f87..00000000 --- a/shared/nm-utils/unaligned.h +++ /dev/null @@ -1,99 +0,0 @@ -/* SPDX-License-Identifier: LGPL-2.1+ */ -#pragma once - -#include -#include - -/* BE */ - -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); -} - -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); -} - -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); -} - -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); -} - -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); -} - -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); -} - -/* LE */ - -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); -} - -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); -} - -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); -} - -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); -} - -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); -} - -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); -} - -#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_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_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-version-macros.h b/shared/nm-version-macros.h index 3906e7c8..6c5e8557 100644 --- a/shared/nm-version-macros.h +++ b/shared/nm-version-macros.h @@ -37,7 +37,7 @@ * Evaluates to the minor version number of NetworkManager which this source * is compiled against. */ -#define NM_MINOR_VERSION (16) +#define NM_MINOR_VERSION (18) /** * NM_MICRO_VERSION: @@ -75,6 +75,7 @@ #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)) /* For releases, NM_API_VERSION is equal to NM_VERSION. * diff --git a/shared/nm-version-macros.h.in b/shared/nm-version-macros.h.in index 22af1428..4b57529a 100644 --- a/shared/nm-version-macros.h.in +++ b/shared/nm-version-macros.h.in @@ -75,6 +75,7 @@ #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)) /* For releases, NM_API_VERSION is equal to NM_VERSION. * diff --git a/shared/systemd/nm-logging-stub.c b/shared/systemd/nm-logging-stub.c index 5be69b4b..59699228 100644 --- a/shared/systemd/nm-logging-stub.c +++ b/shared/systemd/nm-logging-stub.c @@ -19,7 +19,7 @@ #include "nm-default.h" -#include "nm-utils/nm-logging-fwd.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 0e89fbb7..ecd27492 100644 --- a/shared/systemd/nm-sd-utils-shared.c +++ b/shared/systemd/nm-sd-utils-shared.c @@ -62,6 +62,8 @@ nm_sd_utils_unbase64char (char ch, gboolean accept_padding_equal) * will cause the function to fail. * @l: the length of @p. @p is not treated as NUL terminated string but * merely as a buffer of ascii characters. + * @secure: whether the temporary memory will be cleared to avoid leaving + * secrets in memory (see also nm_explict_bzero()). * @mem: (transfer full): the decoded buffer on success. * @len: the length of @mem on success. * @@ -75,8 +77,9 @@ nm_sd_utils_unbase64char (char ch, gboolean accept_padding_equal) int nm_sd_utils_unbase64mem (const char *p, size_t l, + gboolean secure, guint8 **mem, size_t *len) { - return unbase64mem (p, l, (void **) mem, len); + return unbase64mem_full (p, l, secure, (void **) mem, len); } diff --git a/shared/systemd/nm-sd-utils-shared.h b/shared/systemd/nm-sd-utils-shared.h index eddf0c28..b3b77c88 100644 --- a/shared/systemd/nm-sd-utils-shared.h +++ b/shared/systemd/nm-sd-utils-shared.h @@ -31,7 +31,11 @@ const char *nm_sd_utils_path_startswith (const char *path, const char *prefix); int nm_sd_utils_unbase64char (char ch, gboolean accept_padding_equal); -int nm_sd_utils_unbase64mem (const char *p, size_t l, guint8 **mem, size_t *len); +int nm_sd_utils_unbase64mem (const char *p, + size_t l, + gboolean secure, + guint8 **mem, + size_t *len); /*****************************************************************************/ diff --git a/shared/systemd/sd-adapt-shared/missing.h b/shared/systemd/sd-adapt-shared/missing.h index 2ee34b6a..d0b460a7 100644 --- a/shared/systemd/sd-adapt-shared/missing.h +++ b/shared/systemd/sd-adapt-shared/missing.h @@ -3,4 +3,6 @@ /* dummy header */ #include "missing_fcntl.h" +#include "missing_socket.h" +#include "missing_stat.h" #include "missing_type.h" diff --git a/shared/systemd/sd-adapt-shared/missing_socket.h b/shared/systemd/sd-adapt-shared/missing_socket.h deleted file mode 100644 index 637892c2..00000000 --- a/shared/systemd/sd-adapt-shared/missing_socket.h +++ /dev/null @@ -1,3 +0,0 @@ -#pragma once - -/* dummy header */ diff --git a/shared/systemd/sd-adapt-shared/namespace-util.h b/shared/systemd/sd-adapt-shared/namespace-util.h new file mode 100644 index 00000000..637892c2 --- /dev/null +++ b/shared/systemd/sd-adapt-shared/namespace-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 b10722d7..a285c3cd 100644 --- a/shared/systemd/sd-adapt-shared/nm-sd-adapt-shared.h +++ b/shared/systemd/sd-adapt-shared/nm-sd-adapt-shared.h @@ -23,7 +23,7 @@ #include -#include "nm-utils/nm-logging-fwd.h" +#include "nm-glib-aux/nm-logging-fwd.h" /*****************************************************************************/ diff --git a/shared/systemd/sd-adapt-shared/nulstr-util.h b/shared/systemd/sd-adapt-shared/nulstr-util.h new file mode 100644 index 00000000..637892c2 --- /dev/null +++ b/shared/systemd/sd-adapt-shared/nulstr-util.h @@ -0,0 +1,3 @@ +#pragma once + +/* dummy header */ diff --git a/shared/systemd/sd-adapt-shared/strxcpyx.h b/shared/systemd/sd-adapt-shared/strxcpyx.h new file mode 100644 index 00000000..637892c2 --- /dev/null +++ b/shared/systemd/sd-adapt-shared/strxcpyx.h @@ -0,0 +1,3 @@ +#pragma once + +/* dummy header */ diff --git a/shared/systemd/sd-adapt-shared/unaligned.h b/shared/systemd/sd-adapt-shared/unaligned.h index 17dc0444..ac1a6928 100644 --- a/shared/systemd/sd-adapt-shared/unaligned.h +++ b/shared/systemd/sd-adapt-shared/unaligned.h @@ -1,3 +1,3 @@ #pragma once -#include "nm-utils/unaligned.h" +#include "nm-std-aux/unaligned.h" diff --git a/shared/systemd/src/basic/alloc-util.c b/shared/systemd/src/basic/alloc-util.c index d23624d8..92b350bc 100644 --- a/shared/systemd/src/basic/alloc-util.c +++ b/shared/systemd/src/basic/alloc-util.c @@ -2,12 +2,13 @@ #include "nm-sd-adapt-shared.h" +#include #include #include #include "alloc-util.h" #include "macro.h" -#include "util.h" +#include "memory-util.h" void* memdup(const void *p, size_t l) { void *ret; @@ -29,6 +30,9 @@ void* memdup_suffix0(const void *p, size_t l) { /* The same as memdup() but place a safety NUL byte after the allocated memory */ + if (_unlikely_(l == SIZE_MAX)) /* prevent overflow */ + return NULL; + ret = malloc(l + 1); if (!ret) return NULL; @@ -47,19 +51,23 @@ void* greedy_realloc(void **p, size_t *allocated, size_t need, size_t size) { if (*allocated >= need) return *p; - newalloc = MAX(need * 2, 64u / size); - a = newalloc * size; + if (_unlikely_(need > SIZE_MAX/2)) /* Overflow check */ + return NULL; - /* check for overflows */ - if (a < size * need) + newalloc = need * 2; + if (size_multiply_overflow(newalloc, size)) return NULL; + a = newalloc * size; + if (a < 64) /* Allocate at least 64 bytes */ + a = 64; + q = realloc(*p, a); if (!q) return NULL; *p = q; - *allocated = newalloc; + *allocated = _unlikely_(size == 0) ? newalloc : malloc_usable_size(q) / size; return q; } diff --git a/shared/systemd/src/basic/alloc-util.h b/shared/systemd/src/basic/alloc-util.h index 893a1238..9b20be47 100644 --- a/shared/systemd/src/basic/alloc-util.h +++ b/shared/systemd/src/basic/alloc-util.h @@ -8,6 +8,10 @@ #include "macro.h" +#if HAS_FEATURE_MEMORY_SANITIZER +# include +#endif + typedef void (*free_func_t)(void *p); /* If for some reason more than 4M are allocated on the stack, let's abort immediately. It's better than @@ -152,11 +156,17 @@ void* greedy_realloc0(void **p, size_t *allocated, size_t need, size_t size); (void*)memset(_new_, 0, _xsize_); \ }) -/* Takes inspiration from Rusts's Option::take() method: reads and returns a pointer, but at the same time resets it to - * NULL. See: https://doc.rust-lang.org/std/option/enum.Option.html#method.take */ +/* Takes inspiration from Rust's Option::take() method: reads and returns a pointer, but at the same time + * resets it to NULL. See: https://doc.rust-lang.org/std/option/enum.Option.html#method.take */ #define TAKE_PTR(ptr) \ ({ \ typeof(ptr) _ptr_ = (ptr); \ (ptr) = NULL; \ _ptr_; \ }) + +#if HAS_FEATURE_MEMORY_SANITIZER +# define msan_unpoison(r, s) __msan_unpoison(r, s) +#else +# define msan_unpoison(r, s) +#endif diff --git a/shared/systemd/src/basic/env-file.c b/shared/systemd/src/basic/env-file.c index 4babe753..4a0f9c39 100644 --- a/shared/systemd/src/basic/env-file.c +++ b/shared/systemd/src/basic/env-file.c @@ -562,7 +562,7 @@ int write_env_file(const char *fname, char **l) { r = -errno; } - unlink(p); + (void) unlink(p); return r; } #endif /* NM_IGNORED */ diff --git a/shared/systemd/src/basic/errno-util.h b/shared/systemd/src/basic/errno-util.h new file mode 100644 index 00000000..d7a5ea77 --- /dev/null +++ b/shared/systemd/src/basic/errno-util.h @@ -0,0 +1,69 @@ +/* SPDX-License-Identifier: LGPL-2.1+ */ +#pragma once + +#include "macro.h" + +static inline void _reset_errno_(int *saved_errno) { + if (*saved_errno < 0) /* Invalidated by UNPROTECT_ERRNO? */ + return; + + errno = *saved_errno; +} + +#define PROTECT_ERRNO \ + _cleanup_(_reset_errno_) _unused_ int _saved_errno_ = errno + +#define UNPROTECT_ERRNO \ + do { \ + errno = _saved_errno_; \ + _saved_errno_ = -1; \ + } while (false) + +static inline int negative_errno(void) { + /* This helper should be used to shut up gcc if you know 'errno' is + * negative. Instead of "return -errno;", use "return negative_errno();" + * It will suppress bogus gcc warnings in case it assumes 'errno' might + * be 0 and thus the caller's error-handling might not be triggered. */ + assert_return(errno > 0, -EINVAL); + return -errno; +} + +/* 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 */ +static inline bool ERRNO_IS_DISCONNECT(int r) { + return IN_SET(abs(r), + ECONNABORTED, + ECONNREFUSED, + ECONNRESET, + EHOSTDOWN, + EHOSTUNREACH, + ENETDOWN, + ENETRESET, + ENETUNREACH, + ENONET, + ENOPROTOOPT, + ENOTCONN, + EPIPE, + EPROTO, + ESHUTDOWN); +} + +/* Transient errors we might get on accept() that we should ignore. As per error handling comment in + * the accept(2) man page. */ +static inline bool ERRNO_IS_ACCEPT_AGAIN(int r) { + return ERRNO_IS_DISCONNECT(r) || + IN_SET(abs(r), + EAGAIN, + EINTR, + EOPNOTSUPP); +} + +/* Resource exhaustion, could be our fault or general system trouble */ +static inline bool ERRNO_IS_RESOURCE(int r) { + return IN_SET(abs(r), + EMFILE, + ENFILE, + ENOMEM); +} diff --git a/shared/systemd/src/basic/fd-util.c b/shared/systemd/src/basic/fd-util.c index 0cc0c6b5..941053bf 100644 --- a/shared/systemd/src/basic/fd-util.c +++ b/shared/systemd/src/basic/fd-util.c @@ -27,6 +27,10 @@ #include "util.h" #include "tmpfile-util.h" +/* The maximum number of iterations in the loop to close descriptors in the fallback case + * when /proc/self/fd/ is inaccessible. */ +#define MAX_FD_LOOP_LIMIT (1024*1024) + int close_nointr(int fd) { assert(fd >= 0); @@ -231,6 +235,13 @@ int close_all_fds(const int except[], size_t n_except) { if (max_fd < 0) return max_fd; + /* Refuse to do the loop over more too many elements. It's better to fail immediately than to + * spin the CPU for a long time. */ + if (max_fd > MAX_FD_LOOP_LIMIT) + return log_debug_errno(SYNTHETIC_ERRNO(EPERM), + "/proc/self/fd is inaccessible. Refusing to loop over %d potential fds.", + max_fd); + for (fd = 3; fd >= 0; fd = fd < max_fd ? fd + 1 : -1) { int q; diff --git a/shared/systemd/src/basic/fd-util.h b/shared/systemd/src/basic/fd-util.h index 4085a244..e490753c 100644 --- a/shared/systemd/src/basic/fd-util.h +++ b/shared/systemd/src/basic/fd-util.h @@ -77,18 +77,6 @@ int acquire_data_fd(const void *data, size_t size, unsigned flags); int fd_duplicate_data_fd(int fd); -/* Hint: ENETUNREACH happens if we try to connect to "non-existing" special IP addresses, such as ::5 */ -/* 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 */ -#define ERRNO_IS_DISCONNECT(r) \ - IN_SET(r, \ - ENOTCONN, ECONNRESET, ECONNREFUSED, ECONNABORTED, EPIPE, \ - ENETUNREACH, EHOSTUNREACH, ENOPROTOOPT, EHOSTDOWN, ENONET) - -/* Resource exhaustion, could be our fault or general system trouble */ -#define ERRNO_IS_RESOURCE(r) \ - IN_SET(r, ENOMEM, EMFILE, ENFILE) - int fd_move_above_stdio(int fd); int rearrange_stdio(int original_input_fd, int original_output_fd, int original_error_fd); diff --git a/shared/systemd/src/basic/fileio.c b/shared/systemd/src/basic/fileio.c index ee66190f..0dfb4574 100644 --- a/shared/systemd/src/basic/fileio.c +++ b/shared/systemd/src/basic/fileio.c @@ -19,6 +19,7 @@ #include "fd-util.h" #include "fileio.h" #include "fs-util.h" +#include "hexdecoct.h" #include "log.h" #include "macro.h" #include "missing.h" @@ -268,26 +269,29 @@ int verify_file(const char *fn, const char *blob, bool accept_extra_nl) { } #endif /* NM_IGNORED */ -int read_full_stream( +int read_full_stream_full( FILE *f, + const char *filename, + ReadFullFileFlags flags, char **ret_contents, size_t *ret_size) { _cleanup_free_ char *buf = NULL; struct stat st; - size_t n, l; - int fd; + size_t n, n_next, l; + int fd, r; assert(f); assert(ret_contents); + assert(!(flags & READ_FULL_FILE_UNBASE64) || ret_size); - n = LINE_MAX; /* Start size */ + n_next = 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 (fstat(fileno(f), &st) < 0) + if (fstat(fd, &st) < 0) return -errno; if (S_ISREG(st.st_mode)) { @@ -300,27 +304,44 @@ int read_full_stream( * size of 0. 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 = st.st_size + 1; + n_next = st.st_size + 1; + + if (flags & READ_FULL_FILE_SECURE) + (void) warn_file_is_world_accessible(filename, &st, NULL, 0); } } - l = 0; + n = l = 0; for (;;) { char *t; size_t k; - t = realloc(buf, n + 1); - if (!t) - return -ENOMEM; + if (flags & READ_FULL_FILE_SECURE) { + t = malloc(n_next + 1); + if (!t) { + r = -ENOMEM; + goto finalize; + } + memcpy_safe(t, buf, n); + explicit_bzero_safe(buf, n); + } else { + t = realloc(buf, n_next + 1); + if (!t) + return -ENOMEM; + } buf = t; + n = n_next; + errno = 0; k = fread(buf + l, 1, n - l, f); if (k > 0) l += k; - if (ferror(f)) - return errno > 0 ? -errno : -EIO; + if (ferror(f)) { + r = errno > 0 ? -errno : -EIO; + goto finalize; + } if (feof(f)) break; @@ -331,10 +352,18 @@ int read_full_stream( assert(l == n); /* Safety check */ - if (n >= READ_FULL_BYTES_MAX) - return -E2BIG; + if (n >= READ_FULL_BYTES_MAX) { + r = -E2BIG; + goto finalize; + } + + n_next = MIN(n * 2, READ_FULL_BYTES_MAX); + } - n = MIN(n * 2, READ_FULL_BYTES_MAX); + if (flags & READ_FULL_FILE_UNBASE64) { + buf[l++] = 0; + r = unbase64mem_full(buf, l, flags & READ_FULL_FILE_SECURE, (void **) ret_contents, ret_size); + goto finalize; } if (!ret_size) { @@ -342,8 +371,10 @@ int read_full_stream( * trailing NUL byte. But if there's an embedded NUL byte, then we should refuse operation as otherwise * there'd be ambiguity about what we just read. */ - if (memchr(buf, 0, l)) - return -EBADMSG; + if (memchr(buf, 0, l)) { + r = -EBADMSG; + goto finalize; + } } buf[l] = 0; @@ -353,21 +384,27 @@ int read_full_stream( *ret_size = l; return 0; + +finalize: + if (flags & READ_FULL_FILE_SECURE) + explicit_bzero_safe(buf, n); + + return r; } -int read_full_file(const char *fn, char **contents, size_t *size) { +int read_full_file_full(const char *filename, ReadFullFileFlags flags, char **contents, size_t *size) { _cleanup_fclose_ FILE *f = NULL; - assert(fn); + assert(filename); assert(contents); - f = fopen(fn, "re"); + f = fopen(filename, "re"); if (!f) return -errno; (void) __fsetlocking(f, FSETLOCKING_BYCALLER); - return read_full_stream(f, contents, size); + return read_full_stream_full(f, filename, flags, contents, size); } #if 0 /* NM_IGNORED */ @@ -830,3 +867,28 @@ int safe_fgetc(FILE *f, char *ret) { return 1; } #endif /* NM_IGNORED */ + +int warn_file_is_world_accessible(const char *filename, struct stat *st, const char *unit, unsigned line) { + struct stat _st; + + if (!filename) + return 0; + + if (!st) { + if (stat(filename, &_st) < 0) + return -errno; + st = &_st; + } + + if ((st->st_mode & S_IRWXO) == 0) + return 0; + + if (unit) + log_syntax(unit, LOG_WARNING, filename, line, 0, + "%s has %04o mode that is too permissive, please adjust the access mode.", + filename, st->st_mode & 07777); + else + log_warning("%s has %04o mode that is too permissive, please adjust the access mode.", + filename, st->st_mode & 07777); + return 0; +} diff --git a/shared/systemd/src/basic/fileio.h b/shared/systemd/src/basic/fileio.h index 53e3f4ef..760e7386 100644 --- a/shared/systemd/src/basic/fileio.h +++ b/shared/systemd/src/basic/fileio.h @@ -5,6 +5,7 @@ #include #include #include +#include #include #include "macro.h" @@ -27,6 +28,11 @@ typedef enum { } WriteStringFileFlags; +typedef enum { + READ_FULL_FILE_SECURE = 1 << 0, + READ_FULL_FILE_UNBASE64 = 1 << 1, +} ReadFullFileFlags; + int write_string_stream_ts(FILE *f, const char *line, WriteStringFileFlags flags, 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); @@ -38,9 +44,15 @@ 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 *fn, char **line); -int read_full_file(const char *fn, char **contents, size_t *size); -int read_full_stream(FILE *f, char **contents, size_t *size); +int read_one_line_file(const char *filename, char **line); +int read_full_file_full(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(filename, 0, contents, 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 verify_file(const char *fn, const char *blob, bool accept_extra_nl); @@ -76,3 +88,5 @@ static inline int read_nul_string(FILE *f, size_t limit, char **ret) { } int safe_fgetc(FILE *f, char *ret); + +int warn_file_is_world_accessible(const char *filename, struct stat *st, const char *unit, unsigned line); diff --git a/shared/systemd/src/basic/fs-util.c b/shared/systemd/src/basic/fs-util.c index 4b234139..be85eef1 100644 --- a/shared/systemd/src/basic/fs-util.c +++ b/shared/systemd/src/basic/fs-util.c @@ -8,6 +8,7 @@ #include #include #include +#include #include #include #include @@ -219,64 +220,109 @@ int readlink_and_make_absolute(const char *p, char **r) { int chmod_and_chown(const char *path, mode_t mode, uid_t uid, gid_t gid) { char fd_path[STRLEN("/proc/self/fd/") + DECIMAL_STR_MAX(int) + 1]; _cleanup_close_ int fd = -1; + bool st_valid = false; + struct stat st; + int r; + assert(path); - /* Under the assumption that we are running privileged we first change the access mode and only then hand out - * ownership to avoid a window where access is too open. */ + /* Under the assumption that we are running privileged we first change the access mode and only then + * hand out ownership to avoid a window where access is too open. */ - fd = open(path, O_PATH|O_CLOEXEC|O_NOFOLLOW); /* Let's acquire an O_PATH fd, as precaution to change mode/owner - * on the same file */ + fd = open(path, O_PATH|O_CLOEXEC|O_NOFOLLOW); /* Let's acquire an O_PATH fd, as precaution to change + * mode/owner on the same file */ if (fd < 0) return -errno; xsprintf(fd_path, "/proc/self/fd/%i", fd); if (mode != MODE_INVALID) { - if ((mode & S_IFMT) != 0) { - struct stat st; if (stat(fd_path, &st) < 0) return -errno; if ((mode & S_IFMT) != (st.st_mode & S_IFMT)) return -EINVAL; + + st_valid = true; } - if (chmod(fd_path, mode & 07777) < 0) - return -errno; + if (chmod(fd_path, mode & 07777) < 0) { + r = -errno; + + if (!st_valid && stat(fd_path, &st) < 0) + return -errno; + + if ((mode & 07777) != (st.st_mode & 07777)) + return r; + + st_valid = true; + } } - if (uid != UID_INVALID || gid != GID_INVALID) - if (chown(fd_path, uid, gid) < 0) - return -errno; + if (uid != UID_INVALID || gid != GID_INVALID) { + if (chown(fd_path, uid, gid) < 0) { + r = -errno; + + if (!st_valid && stat(fd_path, &st) < 0) + return -errno; + + if (uid != UID_INVALID && st.st_uid != uid) + return r; + if (gid != GID_INVALID && st.st_gid != gid) + return r; + } + } return 0; } int fchmod_and_chown(int fd, mode_t mode, uid_t uid, gid_t gid) { + bool st_valid = false; + struct stat st; + int r; + /* Under the assumption that we are running privileged we first change the access mode and only then hand out * ownership to avoid a window where access is too open. */ if (mode != MODE_INVALID) { - if ((mode & S_IFMT) != 0) { - struct stat st; if (fstat(fd, &st) < 0) return -errno; if ((mode & S_IFMT) != (st.st_mode & S_IFMT)) return -EINVAL; + + st_valid = true; } - if (fchmod(fd, mode & 0777) < 0) - return -errno; + if (fchmod(fd, mode & 07777) < 0) { + r = -errno; + + if (!st_valid && fstat(fd, &st) < 0) + return -errno; + + if ((mode & 07777) != (st.st_mode & 07777)) + return r; + + st_valid = true; + } } if (uid != UID_INVALID || gid != GID_INVALID) - if (fchown(fd, uid, gid) < 0) - return -errno; + if (fchown(fd, uid, gid) < 0) { + r = -errno; + + if (!st_valid && fstat(fd, &st) < 0) + return -errno; + + if (uid != UID_INVALID && st.st_uid != uid) + return r; + if (gid != GID_INVALID && st.st_gid != gid) + return r; + } return 0; } @@ -314,6 +360,10 @@ int fd_warn_permissions(const char *path, int fd) { if (fstat(fd, &st) < 0) return -errno; + /* Don't complain if we are reading something that is not a file, for example /dev/null */ + if (!S_ISREG(st.st_mode)) + return 0; + if (st.st_mode & 0111) log_warning("Configuration file %s is marked executable. Please remove executable permission bits. Proceeding anyway.", path); @@ -934,6 +984,7 @@ int chase_symlinks(const char *path, const char *original_root, unsigned flags, if (fstat(child, &st) < 0) return -errno; if ((flags & CHASE_SAFE) && + (empty_or_root(root) || (size_t)(todo - buffer) > strlen(root)) && unsafe_transition(&previous_stat, &st)) return log_unsafe_transition(fd, child, path, flags); @@ -1338,6 +1389,21 @@ int fsync_path_at(int at_fd, const char *path) { return 0; } +int syncfs_path(int atfd, const char *path) { + _cleanup_close_ int fd = -1; + + assert(path); + + fd = openat(atfd, path, O_CLOEXEC|O_RDONLY|O_NONBLOCK); + if (fd < 0) + return -errno; + + if (syncfs(fd) < 0) + return -errno; + + return 0; +} + int open_parent(const char *path, int flags, mode_t mode) { _cleanup_free_ char *parent = NULL; int fd; @@ -1354,9 +1420,9 @@ int open_parent(const char *path, int flags, mode_t mode) { /* Let's insist on O_DIRECTORY since the parent of a file or directory is a directory. Except if we open an * O_TMPFILE file, because in that case we are actually create a regular file below the parent directory. */ - if ((flags & O_PATH) == O_PATH) + if (FLAGS_SET(flags, O_PATH)) flags |= O_DIRECTORY; - else if ((flags & O_TMPFILE) != O_TMPFILE) + else if (!FLAGS_SET(flags, O_TMPFILE)) flags |= O_DIRECTORY|O_RDONLY; fd = open(parent, flags, mode); diff --git a/shared/systemd/src/basic/fs-util.h b/shared/systemd/src/basic/fs-util.h index 7ad030be..b9651205 100644 --- a/shared/systemd/src/basic/fs-util.h +++ b/shared/systemd/src/basic/fs-util.h @@ -10,8 +10,8 @@ #include #include +#include "errno-util.h" #include "time-util.h" -#include "util.h" int unlink_noerrno(const char *path); @@ -108,4 +108,6 @@ int unlinkat_deallocate(int fd, const char *name, int flags); int fsync_directory_of_file(int fd); int fsync_path_at(int at_fd, const char *path); +int syncfs_path(int atfd, const char *path); + int open_parent(const char *path, int flags, mode_t mode); diff --git a/shared/systemd/src/basic/hashmap.c b/shared/systemd/src/basic/hashmap.c index c0655831..9418dbd8 100644 --- a/shared/systemd/src/basic/hashmap.c +++ b/shared/systemd/src/basic/hashmap.c @@ -11,6 +11,7 @@ #include "fileio.h" #include "hashmap.h" #include "macro.h" +#include "memory-util.h" #include "mempool.h" #include "process-util.h" #include "random-util.h" @@ -18,7 +19,6 @@ #include "siphash24.h" #include "string-util.h" #include "strv.h" -#include "util.h" #if ENABLE_DEBUG_HASHMAP #include @@ -1538,7 +1538,6 @@ void *internal_hashmap_first_key_and_value(HashmapBase *h, bool remove, void **r } unsigned internal_hashmap_size(HashmapBase *h) { - if (!h) return 0; @@ -1546,7 +1545,6 @@ unsigned internal_hashmap_size(HashmapBase *h) { } unsigned internal_hashmap_buckets(HashmapBase *h) { - if (!h) return 0; @@ -1906,8 +1904,7 @@ IteratedCache *iterated_cache_free(IteratedCache *cache) { if (cache) { free(cache->keys.ptr); free(cache->values.ptr); - free(cache); } - return NULL; + return mfree(cache); } diff --git a/shared/systemd/src/basic/hashmap.h b/shared/systemd/src/basic/hashmap.h index e16a9f9e..41c8adb1 100644 --- a/shared/systemd/src/basic/hashmap.h +++ b/shared/systemd/src/basic/hashmap.h @@ -412,9 +412,11 @@ static inline char **ordered_hashmap_get_strv(OrderedHashmap *h) { DEFINE_TRIVIAL_CLEANUP_FUNC(Hashmap*, hashmap_free); DEFINE_TRIVIAL_CLEANUP_FUNC(Hashmap*, hashmap_free_free); +DEFINE_TRIVIAL_CLEANUP_FUNC(Hashmap*, hashmap_free_free_key); DEFINE_TRIVIAL_CLEANUP_FUNC(Hashmap*, hashmap_free_free_free); DEFINE_TRIVIAL_CLEANUP_FUNC(OrderedHashmap*, ordered_hashmap_free); DEFINE_TRIVIAL_CLEANUP_FUNC(OrderedHashmap*, ordered_hashmap_free_free); +DEFINE_TRIVIAL_CLEANUP_FUNC(OrderedHashmap*, ordered_hashmap_free_free_key); DEFINE_TRIVIAL_CLEANUP_FUNC(OrderedHashmap*, ordered_hashmap_free_free_free); #define _cleanup_hashmap_free_ _cleanup_(hashmap_freep) diff --git a/shared/systemd/src/basic/hexdecoct.c b/shared/systemd/src/basic/hexdecoct.c index 7c66cc62..c81c09e8 100644 --- a/shared/systemd/src/basic/hexdecoct.c +++ b/shared/systemd/src/basic/hexdecoct.c @@ -10,8 +10,8 @@ #include "alloc-util.h" #include "hexdecoct.h" #include "macro.h" +#include "memory-util.h" #include "string-util.h" -#include "util.h" char octchar(int x) { return '0' + (x & 7); @@ -691,11 +691,12 @@ static int unbase64_next(const char **p, size_t *l) { return ret; } -int unbase64mem(const char *p, size_t l, void **ret, size_t *ret_size) { +int unbase64mem_full(const char *p, size_t l, bool secure, void **ret, size_t *ret_size) { _cleanup_free_ uint8_t *buf = NULL; const char *x; uint8_t *z; size_t len; + int r; assert(p || l == 0); assert(ret); @@ -718,36 +719,54 @@ int unbase64mem(const char *p, size_t l, void **ret, size_t *ret_size) { a = unbase64_next(&x, &l); if (a == -EPIPE) /* End of string */ break; - if (a < 0) - return a; - if (a == INT_MAX) /* Padding is not allowed at the beginning of a 4ch block */ - return -EINVAL; + if (a < 0) { + r = a; + goto on_failure; + } + if (a == INT_MAX) { /* Padding is not allowed at the beginning of a 4ch block */ + r = -EINVAL; + goto on_failure; + } b = unbase64_next(&x, &l); - if (b < 0) - return b; - if (b == INT_MAX) /* Padding is not allowed at the second character of a 4ch block either */ - return -EINVAL; + if (b < 0) { + r = b; + goto on_failure; + } + if (b == INT_MAX) { /* Padding is not allowed at the second character of a 4ch block either */ + r = -EINVAL; + goto on_failure; + } c = unbase64_next(&x, &l); - if (c < 0) - return c; + if (c < 0) { + r = c; + goto on_failure; + } d = unbase64_next(&x, &l); - if (d < 0) - return d; + if (d < 0) { + r = d; + goto on_failure; + } if (c == INT_MAX) { /* Padding at the third character */ - if (d != INT_MAX) /* If the third character is padding, the fourth must be too */ - return -EINVAL; + if (d != INT_MAX) { /* If the third character is padding, the fourth must be too */ + r = -EINVAL; + goto on_failure; + } /* b == 00YY0000 */ - if (b & 15) - return -EINVAL; + if (b & 15) { + r = -EINVAL; + goto on_failure; + } - if (l > 0) /* Trailing rubbish? */ - return -ENAMETOOLONG; + if (l > 0) { /* Trailing rubbish? */ + r = -ENAMETOOLONG; + goto on_failure; + } *(z++) = (uint8_t) a << 2 | (uint8_t) (b >> 4); /* XXXXXXYY */ break; @@ -755,11 +774,15 @@ int unbase64mem(const char *p, size_t l, void **ret, size_t *ret_size) { if (d == INT_MAX) { /* c == 00ZZZZ00 */ - if (c & 3) - return -EINVAL; + if (c & 3) { + r = -EINVAL; + goto on_failure; + } - if (l > 0) /* Trailing rubbish? */ - return -ENAMETOOLONG; + if (l > 0) { /* Trailing rubbish? */ + r = -ENAMETOOLONG; + goto on_failure; + } *(z++) = (uint8_t) a << 2 | (uint8_t) b >> 4; /* XXXXXXYY */ *(z++) = (uint8_t) b << 4 | (uint8_t) c >> 2; /* YYYYZZZZ */ @@ -777,6 +800,12 @@ int unbase64mem(const char *p, size_t l, void **ret, size_t *ret_size) { *ret = TAKE_PTR(buf); return 0; + +on_failure: + if (secure) + explicit_bzero_safe(buf, len); + + return r; } #if 0 /* NM_IGNORED */ diff --git a/shared/systemd/src/basic/hexdecoct.h b/shared/systemd/src/basic/hexdecoct.h index 9477d16e..fa6013ee 100644 --- a/shared/systemd/src/basic/hexdecoct.h +++ b/shared/systemd/src/basic/hexdecoct.h @@ -33,6 +33,9 @@ ssize_t base64mem(const void *p, size_t l, char **out); int base64_append(char **prefix, int plen, const void *p, size_t l, int margin, int width); -int unbase64mem(const char *p, size_t l, void **mem, size_t *len); +int unbase64mem_full(const char *p, size_t l, bool secure, void **mem, size_t *len); +static inline int unbase64mem(const char *p, size_t l, void **mem, size_t *len) { + return unbase64mem_full(p, l, false, mem, len); +} void hexdump(FILE *f, const void *p, size_t s); diff --git a/shared/systemd/src/basic/in-addr-util.c b/shared/systemd/src/basic/in-addr-util.c index 5ced3501..5899f62f 100644 --- a/shared/systemd/src/basic/in-addr-util.c +++ b/shared/systemd/src/basic/in-addr-util.c @@ -7,12 +7,15 @@ #include #include #include +#include #include #include "alloc-util.h" #include "in-addr-util.h" #include "macro.h" #include "parse-util.h" +#include "random-util.h" +#include "strxcpyx.h" #include "util.h" bool in4_addr_is_null(const struct in_addr *a) { @@ -107,6 +110,7 @@ int in_addr_equal(int family, const union in_addr_union *a, const union in_addr_ return -EAFNOSUPPORT; } +#if 0 /* NM_IGNORED */ int in_addr_prefix_intersect( int family, const union in_addr_union *a, @@ -217,8 +221,86 @@ int in_addr_prefix_next(int family, union in_addr_union *u, unsigned prefixlen) return -EAFNOSUPPORT; } +int in_addr_random_prefix( + int family, + union in_addr_union *u, + unsigned prefixlen_fixed_part, + unsigned prefixlen) { + + assert(u); + + /* Random network part of an address by one. */ + + if (prefixlen <= 0) + return 0; + + if (family == AF_INET) { + uint32_t c, n; + + if (prefixlen_fixed_part > 32) + prefixlen_fixed_part = 32; + if (prefixlen > 32) + prefixlen = 32; + if (prefixlen_fixed_part >= prefixlen) + return -EINVAL; + + c = be32toh(u->in.s_addr); + c &= ((UINT32_C(1) << prefixlen_fixed_part) - 1) << (32 - prefixlen_fixed_part); + + random_bytes(&n, sizeof(n)); + n &= ((UINT32_C(1) << (prefixlen - prefixlen_fixed_part)) - 1) << (32 - prefixlen); + + u->in.s_addr = htobe32(n | c); + return 1; + } + + if (family == AF_INET6) { + struct in6_addr n; + unsigned i, j; + + if (prefixlen_fixed_part > 128) + prefixlen_fixed_part = 128; + if (prefixlen > 128) + prefixlen = 128; + if (prefixlen_fixed_part >= prefixlen) + return -EINVAL; + + random_bytes(&n, sizeof(n)); + + for (i = 0; i < 16; i++) { + uint8_t mask_fixed_part = 0, mask = 0; + + if (i < (prefixlen_fixed_part + 7) / 8) { + if (i < prefixlen_fixed_part / 8) + mask_fixed_part = 0xffu; + else { + j = prefixlen_fixed_part % 8; + mask_fixed_part = ((UINT8_C(1) << (j + 1)) - 1) << (8 - j); + } + } + + if (i < (prefixlen + 7) / 8) { + if (i < prefixlen / 8) + mask = 0xffu ^ mask_fixed_part; + else { + j = prefixlen % 8; + mask = (((UINT8_C(1) << (j + 1)) - 1) << (8 - j)) ^ mask_fixed_part; + } + } + + u->in6.s6_addr[i] &= mask_fixed_part; + u->in6.s6_addr[i] |= n.s6_addr[i] & mask; + } + + return 1; + } + + return -EAFNOSUPPORT; +} +#endif /* NM_IGNORED */ + int in_addr_to_string(int family, const union in_addr_union *u, char **ret) { - char *x; + _cleanup_free_ char *x = NULL; size_t l; assert(u); @@ -236,18 +318,52 @@ int in_addr_to_string(int family, const union in_addr_union *u, char **ret) { return -ENOMEM; errno = 0; - if (!inet_ntop(family, u, x, l)) { - free(x); + if (!inet_ntop(family, u, x, l)) return errno > 0 ? -errno : -EINVAL; - } - *ret = x; + *ret = TAKE_PTR(x); + return 0; +} + +#if 0 /* NM_IGNORED */ +int in_addr_prefix_to_string(int family, const union in_addr_union *u, unsigned prefixlen, char **ret) { + _cleanup_free_ char *x = NULL; + char *p; + size_t l; + + assert(u); + assert(ret); + + if (family == AF_INET) + l = INET_ADDRSTRLEN + 3; + else if (family == AF_INET6) + l = INET6_ADDRSTRLEN + 4; + else + return -EAFNOSUPPORT; + + if (prefixlen > FAMILY_ADDRESS_SIZE(family) * 8) + return -EINVAL; + + x = new(char, l); + if (!x) + return -ENOMEM; + + errno = 0; + if (!inet_ntop(family, u, x, l)) + return errno > 0 ? -errno : -EINVAL; + + p = x + strlen(x); + l -= strlen(x); + (void) strpcpyf(&p, l, "/%u", prefixlen); + + *ret = TAKE_PTR(x); return 0; } +#endif /* NM_IGNORED */ int in_addr_ifindex_to_string(int family, const union in_addr_union *u, int ifindex, char **ret) { + _cleanup_free_ char *x = NULL; size_t l; - char *x; int r; assert(u); @@ -273,14 +389,12 @@ int in_addr_ifindex_to_string(int family, const union in_addr_union *u, int ifin return -ENOMEM; errno = 0; - if (!inet_ntop(family, u, x, l)) { - free(x); + if (!inet_ntop(family, u, x, l)) return errno > 0 ? -errno : -EINVAL; - } sprintf(strchr(x, 0), "%%%i", ifindex); - *ret = x; + *ret = TAKE_PTR(x); return 0; fallback: diff --git a/shared/systemd/src/basic/in-addr-util.h b/shared/systemd/src/basic/in-addr-util.h index c2156712..a6a685b9 100644 --- a/shared/systemd/src/basic/in-addr-util.h +++ b/shared/systemd/src/basic/in-addr-util.h @@ -35,7 +35,9 @@ bool in4_addr_is_non_local(const struct in_addr *a); int in_addr_equal(int family, const union in_addr_union *a, const union in_addr_union *b); int in_addr_prefix_intersect(int family, const union in_addr_union *a, unsigned aprefixlen, const union in_addr_union *b, unsigned bprefixlen); int in_addr_prefix_next(int family, union in_addr_union *u, unsigned prefixlen); +int in_addr_random_prefix(int family, union in_addr_union *u, unsigned prefixlen_fixed_part, unsigned prefixlen); int in_addr_to_string(int family, const union in_addr_union *u, char **ret); +int in_addr_prefix_to_string(int family, const union in_addr_union *u, unsigned prefixlen, char **ret); int in_addr_ifindex_to_string(int family, const union in_addr_union *u, int ifindex, char **ret); int in_addr_from_string(int family, const char *s, union in_addr_union *ret); int in_addr_from_string_auto(const char *s, int *ret_family, union in_addr_union *ret); diff --git a/shared/systemd/src/basic/log.h b/shared/systemd/src/basic/log.h index 364b8a49..98c5f4d7 100644 --- a/shared/systemd/src/basic/log.h +++ b/shared/systemd/src/basic/log.h @@ -120,6 +120,19 @@ int log_internalv_realm( log_internalv_realm(LOG_REALM_PLUS_LEVEL(LOG_REALM, (level)), __VA_ARGS__) /* Realm is fixed to LOG_REALM_SYSTEMD for those */ +int log_object_internalv( + int level, + int error, + const char *file, + int line, + const char *func, + const char *object_field, + const char *object, + const char *extra_field, + const char *extra, + const char *format, + va_list ap) _printf_(10,0); + int log_object_internal( int level, int error, @@ -145,7 +158,12 @@ int log_oom_internal( const char *file, int line, const char *func); +#endif /* NM_IGNORED */ +#define log_oom_internal(realm, file, line, func) \ + log_internal_realm (LOG_REALM_PLUS_LEVEL (realm, LOG_ERR), \ + ENOMEM, file, line, func, "Out of memory.") +#if 0 /* NM_IGNORED */ int log_format_iovec( struct iovec *iovec, size_t iovec_len, @@ -314,8 +332,8 @@ int log_syntax_invalid_utf8_internal( ({ \ int _level = (level), _e = (error); \ (log_get_max_level() >= LOG_PRI(_level)) \ - ? log_syntax_internal(unit, _level, config_file, config_line, _e, __FILE__, __LINE__, __func__, __VA_ARGS__) \ - : -abs(_e); \ + ? log_internal_realm(_level, _e, __FILE__, __LINE__, __func__, __VA_ARGS__) \ + : -ERRNO_VALUE(_e); \ }) #define log_syntax_invalid_utf8(unit, level, config_file, config_line, rvalue) \ diff --git a/shared/systemd/src/basic/memory-util.c b/shared/systemd/src/basic/memory-util.c new file mode 100644 index 00000000..bd1f5d73 --- /dev/null +++ b/shared/systemd/src/basic/memory-util.c @@ -0,0 +1,59 @@ +#include "nm-sd-adapt-shared.h" + +#include + +#include "memory-util.h" + +size_t page_size(void) { + static thread_local size_t pgsz = 0; + long r; + + if (_likely_(pgsz > 0)) + return pgsz; + + r = sysconf(_SC_PAGESIZE); + assert(r > 0); + + pgsz = (size_t) r; + return pgsz; +} + +bool memeqzero(const void *data, size_t length) { + /* Does the buffer consist entirely of NULs? + * Copied from https://github.com/systemd/casync/, copied in turn from + * https://github.com/rustyrussell/ccan/blob/master/ccan/mem/mem.c#L92, + * which is licensed CC-0. + */ + + const uint8_t *p = data; + size_t i; + + /* Check first 16 bytes manually */ + for (i = 0; i < 16; i++, length--) { + if (length == 0) + return true; + if (p[i]) + return false; + } + + /* Now we know first 16 bytes are NUL, memcmp with self. */ + return memcmp(data, p + i, length) == 0; +} + +#if !HAVE_EXPLICIT_BZERO +/* + * The pointer to memset() is volatile so that compiler must de-reference the pointer and can't assume that + * it points to any function in particular (such as memset(), which it then might further "optimize"). This + * approach is inspired by openssl's crypto/mem_clr.c. + */ +typedef void *(*memset_t)(void *,int,size_t); + +static volatile memset_t memset_func = memset; + +void* explicit_bzero_safe(void *p, size_t l) { + if (l > 0) + memset_func(p, '\0', l); + + return p; +} +#endif diff --git a/shared/systemd/src/basic/memory-util.h b/shared/systemd/src/basic/memory-util.h new file mode 100644 index 00000000..915c24a5 --- /dev/null +++ b/shared/systemd/src/basic/memory-util.h @@ -0,0 +1,84 @@ +/* SPDX-License-Identifier: LGPL-2.1+ */ +#pragma once + +#include +#include +#include +#include + +#include "macro.h" + +size_t page_size(void) _pure_; +#define PAGE_ALIGN(l) ALIGN_TO((l), page_size()) + +/* Normal memcpy requires src to be nonnull. We do nothing if n is 0. */ +static inline void memcpy_safe(void *dst, const void *src, size_t n) { + if (n == 0) + return; + assert(src); + memcpy(dst, src, n); +} + +/* Normal memcmp requires s1 and s2 to be nonnull. We do nothing if n is 0. */ +static inline int memcmp_safe(const void *s1, const void *s2, size_t n) { + if (n == 0) + return 0; + assert(s1); + assert(s2); + return memcmp(s1, s2, n); +} + +/* Compare s1 (length n1) with s2 (length n2) in lexicographic order. */ +static inline int memcmp_nn(const void *s1, size_t n1, const void *s2, size_t n2) { + return memcmp_safe(s1, s2, MIN(n1, n2)) + ?: CMP(n1, n2); +} + +#define memzero(x,l) \ + ({ \ + size_t _l_ = (l); \ + void *_x_ = (x); \ + _l_ == 0 ? _x_ : memset(_x_, 0, _l_); \ + }) + +#define zero(x) (memzero(&(x), sizeof(x))) + +bool memeqzero(const void *data, size_t length); + +#define eqzero(x) memeqzero(x, sizeof(x)) + +static inline void *mempset(void *s, int c, size_t n) { + memset(s, c, n); + return (uint8_t*)s + n; +} + +/* Normal memmem() requires haystack to be nonnull, which is annoying for zero-length buffers */ +static inline void *memmem_safe(const void *haystack, size_t haystacklen, const void *needle, size_t needlelen) { + + if (needlelen <= 0) + return (void*) haystack; + + if (haystacklen < needlelen) + return NULL; + + assert(haystack); + assert(needle); + + return memmem(haystack, haystacklen, needle, needlelen); +} + +#if HAVE_EXPLICIT_BZERO +static inline void* explicit_bzero_safe(void *p, size_t l) { + if (l > 0) + explicit_bzero(p, l); + + return p; +} +#else +void *explicit_bzero_safe(void *p, size_t l); +#endif + +/* Use with _cleanup_ to erase a single 'char' when leaving scope */ +static inline void erase_char(char *p) { + explicit_bzero_safe(p, sizeof(char)); +} diff --git a/shared/systemd/src/basic/mempool.c b/shared/systemd/src/basic/mempool.c index 0fa51fba..8b8337db 100644 --- a/shared/systemd/src/basic/mempool.c +++ b/shared/systemd/src/basic/mempool.c @@ -7,6 +7,7 @@ #include "env-util.h" #include "macro.h" +#include "memory-util.h" #include "mempool.h" #include "process-util.h" #include "util.h" diff --git a/shared/systemd/src/basic/missing_socket.h b/shared/systemd/src/basic/missing_socket.h new file mode 100644 index 00000000..29828dba --- /dev/null +++ b/shared/systemd/src/basic/missing_socket.h @@ -0,0 +1,66 @@ +/* SPDX-License-Identifier: LGPL-2.1+ */ +#pragma once + +#include + +#if 0 /* NM_IGNORED */ +#if HAVE_LINUX_VM_SOCKETS_H +#include +#else +#define VMADDR_CID_ANY -1U +struct sockaddr_vm { + unsigned short svm_family; + unsigned short svm_reserved1; + unsigned int svm_port; + unsigned int svm_cid; + unsigned char svm_zero[sizeof(struct sockaddr) - + sizeof(unsigned short) - + sizeof(unsigned short) - + sizeof(unsigned int) - + sizeof(unsigned int)]; +}; +#endif /* !HAVE_LINUX_VM_SOCKETS_H */ +#endif /* NM_IGNORED */ + +#ifndef AF_VSOCK +#define AF_VSOCK 40 +#endif + +#ifndef SO_REUSEPORT +#define SO_REUSEPORT 15 +#endif + +#ifndef SO_PEERGROUPS +#define SO_PEERGROUPS 59 +#endif + +#ifndef SO_BINDTOIFINDEX +#define SO_BINDTOIFINDEX 62 +#endif + +#ifndef SOL_NETLINK +#define SOL_NETLINK 270 +#endif + +#ifndef SOL_ALG +#define SOL_ALG 279 +#endif + +/* Not exposed yet. Defined in include/linux/socket.h. */ +#ifndef SOL_SCTP +#define SOL_SCTP 132 +#endif + +/* Not exposed yet. Defined in include/linux/socket.h */ +#ifndef SCM_SECURITY +#define SCM_SECURITY 0x03 +#endif + +/* netinet/in.h */ +#ifndef IP_FREEBIND +#define IP_FREEBIND 15 +#endif + +#ifndef IP_TRANSPARENT +#define IP_TRANSPARENT 19 +#endif diff --git a/shared/systemd/src/basic/missing_stat.h b/shared/systemd/src/basic/missing_stat.h new file mode 100644 index 00000000..7d89a7bb --- /dev/null +++ b/shared/systemd/src/basic/missing_stat.h @@ -0,0 +1,53 @@ +/* SPDX-License-Identifier: LGPL-2.1+ */ +#pragma once + +#include +#include + +#if 0 /* NM_IGNORED */ +#if WANT_LINUX_STAT_H +#include +#endif + +/* a528d35e8bfcc521d7cb70aaf03e1bd296c8493f (4.11) */ +#if !HAVE_STRUCT_STATX +struct statx_timestamp { + __s64 tv_sec; + __u32 tv_nsec; + __s32 __reserved; +}; +struct statx { + __u32 stx_mask; + __u32 stx_blksize; + __u64 stx_attributes; + __u32 stx_nlink; + __u32 stx_uid; + __u32 stx_gid; + __u16 stx_mode; + __u16 __spare0[1]; + __u64 stx_ino; + __u64 stx_size; + __u64 stx_blocks; + __u64 stx_attributes_mask; + struct statx_timestamp stx_atime; + struct statx_timestamp stx_btime; + struct statx_timestamp stx_ctime; + struct statx_timestamp stx_mtime; + __u32 stx_rdev_major; + __u32 stx_rdev_minor; + __u32 stx_dev_major; + __u32 stx_dev_minor; + __u64 __spare2[14]; +}; +#endif +#endif /* NM_IGNORED */ + +/* a528d35e8bfcc521d7cb70aaf03e1bd296c8493f (4.11) */ +#ifndef STATX_BTIME +#define STATX_BTIME 0x00000800U +#endif + +/* a528d35e8bfcc521d7cb70aaf03e1bd296c8493f (4.11) */ +#ifndef AT_STATX_DONT_SYNC +#define AT_STATX_DONT_SYNC 0x4000 +#endif diff --git a/shared/systemd/src/basic/path-util.c b/shared/systemd/src/basic/path-util.c index 6c5e725d..29955a36 100644 --- a/shared/systemd/src/basic/path-util.c +++ b/shared/systemd/src/basic/path-util.c @@ -23,6 +23,7 @@ #include "log.h" #include "macro.h" #include "missing.h" +#include "nulstr-util.h" #include "parse-util.h" #include "path-util.h" #include "stat-util.h" @@ -1112,48 +1113,40 @@ int path_simplify_and_warn( unsigned line, const char *lvalue) { - bool absolute, fatal = flag & PATH_CHECK_FATAL; + bool fatal = flag & PATH_CHECK_FATAL; assert(!FLAGS_SET(flag, PATH_CHECK_ABSOLUTE | PATH_CHECK_RELATIVE)); - if (!utf8_is_valid(path)) { - log_syntax_invalid_utf8(unit, LOG_ERR, filename, line, path); - return -EINVAL; - } + if (!utf8_is_valid(path)) + return log_syntax_invalid_utf8(unit, LOG_ERR, filename, line, path); if (flag & (PATH_CHECK_ABSOLUTE | PATH_CHECK_RELATIVE)) { + bool absolute; + absolute = path_is_absolute(path); - if (!absolute && (flag & PATH_CHECK_ABSOLUTE)) { - log_syntax(unit, LOG_ERR, filename, line, 0, - "%s= path is not absolute%s: %s", - lvalue, fatal ? "" : ", ignoring", path); - return -EINVAL; - } + if (!absolute && (flag & PATH_CHECK_ABSOLUTE)) + return log_syntax(unit, LOG_ERR, filename, line, SYNTHETIC_ERRNO(EINVAL), + "%s= path is not absolute%s: %s", + lvalue, fatal ? "" : ", ignoring", path); - if (absolute && (flag & PATH_CHECK_RELATIVE)) { - log_syntax(unit, LOG_ERR, filename, line, 0, - "%s= path is absolute%s: %s", - lvalue, fatal ? "" : ", ignoring", path); - return -EINVAL; - } + if (absolute && (flag & PATH_CHECK_RELATIVE)) + return log_syntax(unit, LOG_ERR, filename, line, SYNTHETIC_ERRNO(EINVAL), + "%s= path is absolute%s: %s", + lvalue, fatal ? "" : ", ignoring", path); } path_simplify(path, true); - if (!path_is_normalized(path)) { - log_syntax(unit, LOG_ERR, filename, line, 0, - "%s= path is not normalized%s: %s", - lvalue, fatal ? "" : ", ignoring", path); - return -EINVAL; - } + if (!path_is_valid(path)) + return log_syntax(unit, LOG_ERR, filename, line, SYNTHETIC_ERRNO(EINVAL), + "%s= path has invalid length (%zu bytes)%s.", + lvalue, strlen(path), fatal ? "" : ", ignoring"); - if (!path_is_valid(path)) { - log_syntax(unit, LOG_ERR, filename, line, 0, - "%s= path has invalid length (%zu bytes)%s.", - lvalue, strlen(path), fatal ? "" : ", ignoring"); - return -EINVAL; - } + if (!path_is_normalized(path)) + return log_syntax(unit, LOG_ERR, filename, line, SYNTHETIC_ERRNO(EINVAL), + "%s= path is not normalized%s: %s", + lvalue, fatal ? "" : ", ignoring", path); return 0; } diff --git a/shared/systemd/src/basic/process-util.c b/shared/systemd/src/basic/process-util.c index b0afb5c8..7431be3e 100644 --- a/shared/systemd/src/basic/process-util.c +++ b/shared/systemd/src/basic/process-util.c @@ -36,7 +36,9 @@ #include "ioprio.h" #include "log.h" #include "macro.h" +#include "memory-util.h" #include "missing.h" +#include "namespace-util.h" #include "process-util.h" #include "raw-clone.h" #include "rlimit-util.h" @@ -46,7 +48,6 @@ #include "string-util.h" #include "terminal-util.h" #include "user-util.h" -#include "util.h" #if 0 /* NM_IGNORED */ int get_process_state(pid_t pid) { @@ -938,6 +939,20 @@ int getenv_for_pid(pid_t pid, const char *field, char **ret) { return 0; } +int pid_is_my_child(pid_t pid) { + pid_t ppid; + int r; + + if (pid <= 1) + return false; + + r = get_process_ppid(pid, &ppid); + if (r < 0) + return r; + + return ppid == getpid_cached(); +} + bool pid_is_unwaited(pid_t pid) { /* Checks whether a PID is still valid at all, including a zombie */ @@ -1007,7 +1022,7 @@ _noreturn_ void freeze(void) { log_close(); /* Make sure nobody waits for us on a socket anymore */ - close_all_fds(NULL, 0); + (void) close_all_fds(NULL, 0); sync(); @@ -1543,6 +1558,40 @@ int set_oom_score_adjust(int value) { WRITE_STRING_FILE_VERIFY_ON_FAILURE|WRITE_STRING_FILE_DISABLE_BUFFER); } +int cpus_in_affinity_mask(void) { + size_t n = 16; + int r; + + for (;;) { + cpu_set_t *c; + + c = CPU_ALLOC(n); + if (!c) + return -ENOMEM; + + if (sched_getaffinity(0, CPU_ALLOC_SIZE(n), c) >= 0) { + int k; + + k = CPU_COUNT_S(CPU_ALLOC_SIZE(n), c); + CPU_FREE(c); + + if (k <= 0) + return -EINVAL; + + return k; + } + + r = -errno; + CPU_FREE(c); + + if (r != -EINVAL) + return r; + if (n > SIZE_MAX/2) + return -ENOMEM; + n *= 2; + } +} + static const char *const ioprio_class_table[] = { [IOPRIO_CLASS_NONE] = "none", [IOPRIO_CLASS_RT] = "realtime", diff --git a/shared/systemd/src/basic/process-util.h b/shared/systemd/src/basic/process-util.h index 0425042f..3933bee6 100644 --- a/shared/systemd/src/basic/process-util.h +++ b/shared/systemd/src/basic/process-util.h @@ -12,6 +12,7 @@ #include #include +#include "alloc-util.h" #include "format-util.h" #include "ioprio.h" #include "macro.h" @@ -68,6 +69,7 @@ int getenv_for_pid(pid_t pid, const char *field, char **_value); bool pid_is_alive(pid_t pid); bool pid_is_unwaited(pid_t pid); +int pid_is_my_child(pid_t pid); int pid_from_same_root_fs(pid_t pid); bool is_main_thread(void); @@ -194,3 +196,5 @@ assert_cc(TASKS_MAX <= (unsigned long) PID_T_MAX) (pid) = 0; \ _pid_; \ }) + +int cpus_in_affinity_mask(void); diff --git a/shared/systemd/src/basic/random-util.c b/shared/systemd/src/basic/random-util.c index 7c670e59..b8b45958 100644 --- a/shared/systemd/src/basic/random-util.c +++ b/shared/systemd/src/basic/random-util.c @@ -25,16 +25,13 @@ # include #endif +#include "alloc-util.h" #include "fd-util.h" #include "io-util.h" #include "missing.h" #include "random-util.h" #include "time-util.h" -#if HAS_FEATURE_MEMORY_SANITIZER -#include -#endif - int rdrand(unsigned long *ret) { #if defined(__i386__) || defined(__x86_64__) @@ -60,11 +57,7 @@ int rdrand(unsigned long *ret) { "setc %1" : "=r" (*ret), "=qm" (err)); - -#if HAS_FEATURE_MEMORY_SANITIZER - __msan_unpoison(&err, sizeof(err)); -#endif - + msan_unpoison(&err, sizeof(err)); if (!err) return -EAGAIN; diff --git a/shared/systemd/src/basic/refcnt.h b/shared/systemd/src/basic/refcnt.h deleted file mode 100644 index 40f9a84a..00000000 --- a/shared/systemd/src/basic/refcnt.h +++ /dev/null @@ -1,54 +0,0 @@ -/* SPDX-License-Identifier: LGPL-2.1+ */ -#pragma once - -/* A type-safe atomic refcounter. - * - * DO NOT USE THIS UNLESS YOU ACTUALLY CARE ABOUT THREAD SAFETY! */ - -typedef struct { - volatile unsigned _value; -} RefCount; - -#define REFCNT_GET(r) ((r)._value) -#define REFCNT_INC(r) (__sync_add_and_fetch(&(r)._value, 1)) -#define REFCNT_DEC(r) (__sync_sub_and_fetch(&(r)._value, 1)) - -#define REFCNT_INIT ((RefCount) { ._value = 1 }) - -#define _DEFINE_ATOMIC_REF_FUNC(type, name, scope) \ - scope type *name##_ref(type *p) { \ - if (!p) \ - return NULL; \ - \ - assert_se(REFCNT_INC(p->n_ref) >= 2); \ - return p; \ - } - -#define _DEFINE_ATOMIC_UNREF_FUNC(type, name, free_func, scope) \ - scope type *name##_unref(type *p) { \ - if (!p) \ - return NULL; \ - \ - if (REFCNT_DEC(p->n_ref) > 0) \ - return NULL; \ - \ - return free_func(p); \ - } - -#define DEFINE_ATOMIC_REF_FUNC(type, name) \ - _DEFINE_ATOMIC_REF_FUNC(type, name,) -#define DEFINE_PUBLIC_ATOMIC_REF_FUNC(type, name) \ - _DEFINE_ATOMIC_REF_FUNC(type, name, _public_) - -#define DEFINE_ATOMIC_UNREF_FUNC(type, name, free_func) \ - _DEFINE_ATOMIC_UNREF_FUNC(type, name, free_func,) -#define DEFINE_PUBLIC_ATOMIC_UNREF_FUNC(type, name, free_func) \ - _DEFINE_ATOMIC_UNREF_FUNC(type, name, free_func, _public_) - -#define DEFINE_ATOMIC_REF_UNREF_FUNC(type, name, free_func) \ - DEFINE_ATOMIC_REF_FUNC(type, name); \ - DEFINE_ATOMIC_UNREF_FUNC(type, name, free_func); - -#define DEFINE_PUBLIC_ATOMIC_REF_UNREF_FUNC(type, name, free_func) \ - DEFINE_PUBLIC_ATOMIC_REF_FUNC(type, name); \ - DEFINE_PUBLIC_ATOMIC_UNREF_FUNC(type, name, free_func); diff --git a/shared/systemd/src/basic/socket-util.c b/shared/systemd/src/basic/socket-util.c index 68a62f85..b98b0461 100644 --- a/shared/systemd/src/basic/socket-util.c +++ b/shared/systemd/src/basic/socket-util.c @@ -17,12 +17,14 @@ #include #include "alloc-util.h" +#include "errno-util.h" #include "escape.h" #include "fd-util.h" #include "fileio.h" #include "format-util.h" #include "log.h" #include "macro.h" +#include "memory-util.h" #include "missing.h" #include "parse-util.h" #include "path-util.h" @@ -33,7 +35,6 @@ #include "strv.h" #include "user-util.h" #include "utf8.h" -#include "util.h" #if 0 /* NM_IGNORED */ #if ENABLE_IDN @@ -238,23 +239,32 @@ int socket_address_parse_and_warn(SocketAddress *a, const char *s) { } int socket_address_parse_netlink(SocketAddress *a, const char *s) { - int family; + _cleanup_free_ char *word = NULL; unsigned group = 0; - _cleanup_free_ char *sfamily = NULL; + int family, r; + assert(a); assert(s); zero(*a); a->type = SOCK_RAW; - errno = 0; - if (sscanf(s, "%ms %u", &sfamily, &group) < 1) - return errno > 0 ? -errno : -EINVAL; + r = extract_first_word(&s, &word, NULL, 0); + if (r < 0) + return r; + if (r == 0) + return -EINVAL; - family = netlink_family_from_string(sfamily); + family = netlink_family_from_string(word); if (family < 0) return -EINVAL; + if (!isempty(s)) { + r = safe_atou(s, &group); + if (r < 0) + return r; + } + a->sockaddr.nl.nl_family = AF_NETLINK; a->sockaddr.nl.nl_groups = group; @@ -1233,22 +1243,22 @@ int flush_accept(int fd) { continue; return -errno; - - } else if (r == 0) + } + if (r == 0) return 0; cfd = accept4(fd, NULL, NULL, SOCK_NONBLOCK|SOCK_CLOEXEC); if (cfd < 0) { - if (errno == EINTR) - continue; - if (errno == EAGAIN) return 0; + if (ERRNO_IS_ACCEPT_AGAIN(errno)) + continue; + return -errno; } - close(cfd); + safe_close(cfd); } } @@ -1351,3 +1361,39 @@ int sockaddr_un_set_path(struct sockaddr_un *ret, const char *path) { } } #endif /* NM_IGNORED */ + +int socket_bind_to_ifname(int fd, const char *ifname) { + assert(fd >= 0); + + /* Call with NULL to drop binding */ + + if (setsockopt(fd, SOL_SOCKET, SO_BINDTODEVICE, ifname, strlen_ptr(ifname)) < 0) + return -errno; + + return 0; +} + +int socket_bind_to_ifindex(int fd, int ifindex) { + char ifname[IFNAMSIZ] = ""; + + assert(fd >= 0); + + if (ifindex <= 0) { + /* Drop binding */ + if (setsockopt(fd, SOL_SOCKET, SO_BINDTODEVICE, NULL, 0) < 0) + return -errno; + + return 0; + } + + if (setsockopt(fd, SOL_SOCKET, SO_BINDTOIFINDEX, &ifindex, sizeof(ifindex)) >= 0) + return 0; + if (errno != ENOPROTOOPT) + return -errno; + + /* Fall back to SO_BINDTODEVICE on kernels < 5.0 which didn't have SO_BINDTOIFINDEX */ + if (!if_indextoname(ifindex, ifname)) + return -errno; + + return socket_bind_to_ifname(fd, ifname); +} diff --git a/shared/systemd/src/basic/socket-util.h b/shared/systemd/src/basic/socket-util.h index d2246a8e..15443f1e 100644 --- a/shared/systemd/src/basic/socket-util.h +++ b/shared/systemd/src/basic/socket-util.h @@ -200,3 +200,6 @@ static inline int setsockopt_int(int fd, int level, int optname, int value) { return 0; } + +int socket_bind_to_ifname(int fd, const char *ifname); +int socket_bind_to_ifindex(int fd, int ifindex); diff --git a/shared/systemd/src/basic/sort-util.h b/shared/systemd/src/basic/sort-util.h new file mode 100644 index 00000000..e029f864 --- /dev/null +++ b/shared/systemd/src/basic/sort-util.h @@ -0,0 +1,70 @@ +/* SPDX-License-Identifier: LGPL-2.1+ */ +#pragma once + +#include + +#include "macro.h" + +void *xbsearch_r(const void *key, const void *base, size_t nmemb, size_t size, + __compar_d_fn_t compar, void *arg); + +#define typesafe_bsearch_r(k, b, n, func, userdata) \ + ({ \ + const typeof(b[0]) *_k = k; \ + int (*_func_)(const typeof(b[0])*, const typeof(b[0])*, typeof(userdata)) = func; \ + xbsearch_r((const void*) _k, (b), (n), sizeof((b)[0]), (__compar_d_fn_t) _func_, userdata); \ + }) + +/** + * Normal bsearch requires base to be nonnull. Here were require + * that only if nmemb > 0. + */ +static inline void* bsearch_safe(const void *key, const void *base, + size_t nmemb, size_t size, __compar_fn_t compar) { + if (nmemb <= 0) + return NULL; + + assert(base); + return bsearch(key, base, nmemb, size, compar); +} + +#define typesafe_bsearch(k, b, n, func) \ + ({ \ + const typeof(b[0]) *_k = k; \ + int (*_func_)(const typeof(b[0])*, const typeof(b[0])*) = func; \ + bsearch_safe((const void*) _k, (b), (n), sizeof((b)[0]), (__compar_fn_t) _func_); \ + }) + +/** + * Normal qsort requires base to be nonnull. Here were require + * that only if nmemb > 0. + */ +static inline void qsort_safe(void *base, size_t nmemb, size_t size, __compar_fn_t compar) { + if (nmemb <= 1) + return; + + assert(base); + qsort(base, nmemb, size, compar); +} + +/* A wrapper around the above, but that adds typesafety: the element size is automatically derived from the type and so + * is the prototype for the comparison function */ +#define typesafe_qsort(p, n, func) \ + ({ \ + int (*_func_)(const typeof(p[0])*, const typeof(p[0])*) = func; \ + qsort_safe((p), (n), sizeof((p)[0]), (__compar_fn_t) _func_); \ + }) + +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; + + assert(base); + qsort_r(base, nmemb, size, compar, userdata); +} + +#define typesafe_qsort_r(p, n, func, userdata) \ + ({ \ + 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); \ + }) diff --git a/shared/systemd/src/basic/stat-util.c b/shared/systemd/src/basic/stat-util.c index 686adaf1..c9837fa1 100644 --- a/shared/systemd/src/basic/stat-util.c +++ b/shared/systemd/src/basic/stat-util.c @@ -226,52 +226,6 @@ int fd_is_network_fs(int fd) { return is_network_fs(&s); } -int fd_is_network_ns(int fd) { - struct statfs s; - int r; - - /* Checks whether the specified file descriptor refers to a network namespace. On old kernels there's no nice - * way to detect that, hence on those we'll return a recognizable error (EUCLEAN), so that callers can handle - * this somewhat nicely. - * - * This function returns > 0 if the fd definitely refers to a network namespace, 0 if it definitely does not - * refer to a network namespace, -EUCLEAN if we can't determine, and other negative error codes on error. */ - - if (fstatfs(fd, &s) < 0) - return -errno; - - if (!is_fs_type(&s, NSFS_MAGIC)) { - /* On really old kernels, there was no "nsfs", and network namespace sockets belonged to procfs - * instead. Handle that in a somewhat smart way. */ - - if (is_fs_type(&s, PROC_SUPER_MAGIC)) { - struct statfs t; - - /* OK, so it is procfs. Let's see if our own network namespace is procfs, too. If so, then the - * passed fd might refer to a network namespace, but we can't know for sure. In that case, - * return a recognizable error. */ - - if (statfs("/proc/self/ns/net", &t) < 0) - return -errno; - - if (s.f_type == t.f_type) - return -EUCLEAN; /* It's possible, we simply don't know */ - } - - return 0; /* No! */ - } - - r = ioctl(fd, NS_GET_NSTYPE); - if (r < 0) { - if (errno == ENOTTY) /* Old kernels didn't know this ioctl, let's also return a recognizable error in that case */ - return -EUCLEAN; - - return -errno; - } - - return r == CLONE_NEWNET; -} - int path_is_temporary_fs(const char *path) { _cleanup_close_ int fd = -1; diff --git a/shared/systemd/src/basic/stat-util.h b/shared/systemd/src/basic/stat-util.h index 74fb7251..7824af35 100644 --- a/shared/systemd/src/basic/stat-util.h +++ b/shared/systemd/src/basic/stat-util.h @@ -1,6 +1,7 @@ /* SPDX-License-Identifier: LGPL-2.1+ */ #pragma once +#include #include #include #include @@ -50,8 +51,6 @@ bool is_network_fs(const struct statfs *s) _pure_; int fd_is_temporary_fs(int fd); int fd_is_network_fs(int fd); -int fd_is_network_ns(int fd); - int path_is_temporary_fs(const char *path); /* Because statfs.t_type can be int on some architectures, we have to cast diff --git a/shared/systemd/src/basic/stdio-util.h b/shared/systemd/src/basic/stdio-util.h index dc67b6e7..c3b9448d 100644 --- a/shared/systemd/src/basic/stdio-util.h +++ b/shared/systemd/src/basic/stdio-util.h @@ -7,7 +7,7 @@ #include #include "macro.h" -#include "util.h" +#include "memory-util.h" #define snprintf_ok(buf, len, fmt, ...) \ ((size_t) snprintf(buf, len, fmt, __VA_ARGS__) < (len)) diff --git a/shared/systemd/src/basic/string-table.h b/shared/systemd/src/basic/string-table.h index 228c12ad..42fe4f43 100644 --- a/shared/systemd/src/basic/string-table.h +++ b/shared/systemd/src/basic/string-table.h @@ -59,13 +59,13 @@ ssize_t string_table_lookup(const char * const *table, size_t len, const char *k #define _DEFINE_STRING_TABLE_LOOKUP_FROM_STRING_FALLBACK(name,type,max,scope) \ scope type name##_from_string(const char *s) { \ - type i; \ unsigned u = 0; \ + type i; \ if (!s) \ return (type) -1; \ - for (i = 0; i < (type) ELEMENTSOF(name##_table); i++) \ - if (streq_ptr(name##_table[i], s)) \ - return i; \ + i = (type) string_table_lookup(name##_table, ELEMENTSOF(name##_table), s); \ + if (i >= 0) \ + return i; \ if (safe_atou(s, &u) >= 0 && u <= max) \ return (type) u; \ return (type) -1; \ diff --git a/shared/systemd/src/basic/string-util.c b/shared/systemd/src/basic/string-util.c index 0e961927..df519976 100644 --- a/shared/systemd/src/basic/string-util.c +++ b/shared/systemd/src/basic/string-util.c @@ -12,14 +12,15 @@ #include "alloc-util.h" #include "escape.h" +#include "fileio.h" #include "gunicode.h" #include "locale-util.h" #include "macro.h" +#include "memory-util.h" #include "string-util.h" #include "terminal-util.h" #include "utf8.h" #include "util.h" -#include "fileio.h" int strcmp_ptr(const char *a, const char *b) { @@ -681,19 +682,6 @@ char *cellescape(char *buf, size_t len, const char *s) { } #endif /* NM_IGNORED */ -bool nulstr_contains(const char *nulstr, const char *needle) { - const char *i; - - if (!nulstr) - return false; - - NULSTR_FOREACH(i, nulstr) - if (streq(i, needle)) - return true; - - return false; -} - char* strshorten(char *s, size_t l) { assert(s); @@ -1056,25 +1044,6 @@ int free_and_strndup(char **p, const char *s, size_t l) { return 1; } -#if !HAVE_EXPLICIT_BZERO -/* - * Pointer to memset is volatile so that compiler must de-reference - * the pointer and can't assume that it points to any function in - * particular (such as memset, which it then might further "optimize") - * This approach is inspired by openssl's crypto/mem_clr.c. - */ -typedef void *(*memset_t)(void *,int,size_t); - -static volatile memset_t memset_func = memset; - -void* explicit_bzero_safe(void *p, size_t l) { - if (l > 0) - memset_func(p, '\0', l); - - return p; -} -#endif - char* string_erase(char *x) { if (!x) return NULL; diff --git a/shared/systemd/src/basic/string-util.h b/shared/systemd/src/basic/string-util.h index 38070abb..b23f4c83 100644 --- a/shared/systemd/src/basic/string-util.h +++ b/shared/systemd/src/basic/string-util.h @@ -57,6 +57,16 @@ static inline const char *empty_to_dash(const char *str) { return isempty(str) ? "-" : str; } +static inline bool empty_or_dash(const char *str) { + return !str || + str[0] == 0 || + (str[0] == '-' && str[1] == 0); +} + +static inline const char *empty_or_dash_to_null(const char *p) { + return empty_or_dash(p) ? NULL : p; +} + static inline char *startswith(const char *s, const char *prefix) { size_t l; @@ -165,8 +175,6 @@ char *cellescape(char *buf, size_t len, const char *s); /* This limit is arbitrary, enough to give some idea what the string contains */ #define CELLESCAPE_DEFAULT_LENGTH 64 -bool nulstr_contains(const char *nulstr, const char *needle); - char* strshorten(char *s, size_t l); char *strreplace(const char *text, const char *old_string, const char *new_string); @@ -182,33 +190,12 @@ char *strrep(const char *s, unsigned n); int split_pair(const char *s, const char *sep, char **l, char **r); int free_and_strdup(char **p, const char *s); -int free_and_strndup(char **p, const char *s, size_t l); - -/* Normal memmem() requires haystack to be nonnull, which is annoying for zero-length buffers */ -static inline void *memmem_safe(const void *haystack, size_t haystacklen, const void *needle, size_t needlelen) { - - if (needlelen <= 0) - return (void*) haystack; - - if (haystacklen < needlelen) - return NULL; - - assert(haystack); - assert(needle); - - return memmem(haystack, haystacklen, needle, needlelen); +static inline int free_and_strdup_warn(char **p, const char *s) { + if (free_and_strdup(p, s) < 0) + return log_oom(); + return 0; } - -#if HAVE_EXPLICIT_BZERO -static inline void* explicit_bzero_safe(void *p, size_t l) { - if (l > 0) - explicit_bzero(p, l); - - return p; -} -#else -void *explicit_bzero_safe(void *p, size_t l); -#endif +int free_and_strndup(char **p, const char *s, size_t l); char *string_erase(char *x); diff --git a/shared/systemd/src/basic/strv.c b/shared/systemd/src/basic/strv.c index 1615c9ba..8de40b9d 100644 --- a/shared/systemd/src/basic/strv.c +++ b/shared/systemd/src/basic/strv.c @@ -13,9 +13,10 @@ #include "escape.h" #include "extract-word.h" #include "fileio.h" +#include "nulstr-util.h" +#include "sort-util.h" #include "string-util.h" #include "strv.h" -#include "util.h" char *strv_find(char **l, const char *name) { char **i; @@ -651,6 +652,7 @@ char **strv_parse_nulstr(const char *s, size_t l) { return v; } +#if 0 /* NM_IGNORED */ char **strv_split_nulstr(const char *s) { const char *i; char **r = NULL; @@ -666,6 +668,7 @@ char **strv_split_nulstr(const char *s) { return r; } +#endif /* NM_IGNORED */ int strv_make_nulstr(char **l, char **p, size_t *q) { /* A valid nulstr with two NULs at the end will be created, but @@ -722,6 +725,7 @@ bool strv_overlap(char **a, char **b) { return false; } +#if 0 /* NM_IGNORED */ static int str_compare(char * const *a, char * const *b) { return strcmp(*a, *b); } @@ -730,6 +734,7 @@ char **strv_sort(char **l) { typesafe_qsort(l, strv_length(l), str_compare); return l; } +#endif /* NM_IGNORED */ bool strv_equal(char **a, char **b) { diff --git a/shared/systemd/src/basic/strv.h b/shared/systemd/src/basic/strv.h index 392cab65..aa5f95ab 100644 --- a/shared/systemd/src/basic/strv.h +++ b/shared/systemd/src/basic/strv.h @@ -5,12 +5,12 @@ #include #include #include +#include #include "alloc-util.h" #include "extract-word.h" #include "macro.h" #include "string-util.h" -#include "util.h" char *strv_find(char **l, const char *name) _pure_; char *strv_find_prefix(char **l, const char *name) _pure_; diff --git a/shared/systemd/src/basic/time-util.c b/shared/systemd/src/basic/time-util.c index 9ea0380a..14b17bfc 100644 --- a/shared/systemd/src/basic/time-util.c +++ b/shared/systemd/src/basic/time-util.c @@ -1219,7 +1219,7 @@ int get_timezones(char ***ret) { n_allocated = 2; n_zones = 1; - f = fopen("/usr/share/zoneinfo/zone.tab", "re"); + f = fopen("/usr/share/zoneinfo/zone1970.tab", "re"); if (f) { for (;;) { _cleanup_free_ char *line = NULL; diff --git a/shared/systemd/src/basic/utf8.c b/shared/systemd/src/basic/utf8.c index e9958c91..f1c6ac1f 100644 --- a/shared/systemd/src/basic/utf8.c +++ b/shared/systemd/src/basic/utf8.c @@ -65,12 +65,7 @@ static bool unichar_is_control(char32_t ch) { #endif /* NM_IGNORED */ /* count of characters used to encode one unicode char */ -static size_t utf8_encoded_expected_len(const char *str) { - uint8_t c; - - assert(str); - - c = (uint8_t) str[0]; +static size_t utf8_encoded_expected_len(uint8_t c) { if (c < 0x80) return 1; if ((c & 0xe0) == 0xc0) @@ -94,7 +89,7 @@ int utf8_encoded_to_unichar(const char *str, char32_t *ret_unichar) { assert(str); - len = utf8_encoded_expected_len(str); + len = utf8_encoded_expected_len(str[0]); switch (len) { case 1: @@ -138,14 +133,14 @@ bool utf8_is_printable_newline(const char* str, size_t length, bool newline) { assert(str); - for (p = str; length;) { + for (p = str; length > 0;) { int encoded_len, r; char32_t val; - encoded_len = utf8_encoded_valid_unichar(p); - if (encoded_len < 0 || - (size_t) encoded_len > length) + encoded_len = utf8_encoded_valid_unichar(p, length); + if (encoded_len < 0) return false; + assert(encoded_len > 0 && (size_t) encoded_len <= length); r = utf8_encoded_to_unichar(p, &val); if (r < 0 || @@ -170,7 +165,7 @@ char *utf8_is_valid(const char *str) { while (*p) { int len; - len = utf8_encoded_valid_unichar(p); + len = utf8_encoded_valid_unichar(p, (size_t) -1); if (len < 0) return NULL; @@ -192,7 +187,7 @@ char *utf8_escape_invalid(const char *str) { while (*str) { int len; - len = utf8_encoded_valid_unichar(str); + len = utf8_encoded_valid_unichar(str, (size_t) -1); if (len > 0) { s = mempcpy(s, str, len); str += len; @@ -220,7 +215,7 @@ char *utf8_escape_non_printable(const char *str) { while (*str) { int len; - len = utf8_encoded_valid_unichar(str); + len = utf8_encoded_valid_unichar(str, (size_t) -1); if (len > 0) { if (utf8_is_printable(str, len)) { s = mempcpy(s, str, len); @@ -416,7 +411,7 @@ char16_t *utf8_to_utf16(const char *s, size_t length) { char32_t unichar; size_t e; - e = utf8_encoded_expected_len(s + i); + e = utf8_encoded_expected_len(s[i]); if (e <= 1) /* Invalid and single byte characters are copied as they are */ goto copy; @@ -469,17 +464,24 @@ static int utf8_unichar_to_encoded_len(char32_t unichar) { } /* validate one encoded unicode char and return its length */ -int utf8_encoded_valid_unichar(const char *str) { +int utf8_encoded_valid_unichar(const char *str, size_t length /* bytes */) { char32_t unichar; size_t len, i; int r; assert(str); + assert(length > 0); - len = utf8_encoded_expected_len(str); + /* We read until NUL, at most length bytes. (size_t) -1 may be used to disable the length check. */ + + len = utf8_encoded_expected_len(str[0]); if (len == 0) return -EINVAL; + /* Do we have a truncated multi-byte character? */ + if (len > length) + return -EINVAL; + /* ascii is valid */ if (len == 1) return 1; @@ -513,7 +515,7 @@ size_t utf8_n_codepoints(const char *str) { while (*str != 0) { int k; - k = utf8_encoded_valid_unichar(str); + k = utf8_encoded_valid_unichar(str, (size_t) -1); if (k < 0) return (size_t) -1; diff --git a/shared/systemd/src/basic/utf8.h b/shared/systemd/src/basic/utf8.h index 62845693..6df70921 100644 --- a/shared/systemd/src/basic/utf8.h +++ b/shared/systemd/src/basic/utf8.h @@ -32,7 +32,7 @@ char16_t *utf8_to_utf16(const char *s, size_t length); size_t char16_strlen(const char16_t *s); /* returns the number of 16bit words in the string (not bytes!) */ -int utf8_encoded_valid_unichar(const char *str); +int utf8_encoded_valid_unichar(const char *str, size_t length); int utf8_encoded_to_unichar(const char *str, char32_t *ret_unichar); static inline bool utf16_is_surrogate(char16_t c) { diff --git a/shared/systemd/src/basic/util.c b/shared/systemd/src/basic/util.c index 7686ecd2..23aa6b26 100644 --- a/shared/systemd/src/basic/util.c +++ b/shared/systemd/src/basic/util.c @@ -21,7 +21,6 @@ #include "alloc-util.h" #include "btrfs-util.h" #include "build.h" -#include "cgroup-util.h" #include "def.h" #include "device-nodes.h" #include "dirent-util.h" @@ -54,35 +53,6 @@ int saved_argc = 0; char **saved_argv = NULL; static int saved_in_initrd = -1; -#endif /* NM_IGNORED */ - -size_t page_size(void) { - static thread_local size_t pgsz = 0; - long r; - - if (_likely_(pgsz > 0)) - return pgsz; - - r = sysconf(_SC_PAGESIZE); - assert(r > 0); - - pgsz = (size_t) r; - return pgsz; -} - -#if 0 /* NM_IGNORED */ -bool plymouth_running(void) { - return access("/run/plymouth/pid", F_OK) >= 0; -} - -bool display_is_local(const char *display) { - assert(display); - - return - display[0] == ':' && - display[1] >= '0' && - display[1] <= '9'; -} bool kexec_loaded(void) { _cleanup_free_ char *s = NULL; @@ -146,53 +116,6 @@ void in_initrd_force(bool value) { saved_in_initrd = value; } -/* hey glibc, APIs with callbacks without a user pointer are so useless */ -void *xbsearch_r(const void *key, const void *base, size_t nmemb, size_t size, - __compar_d_fn_t compar, void *arg) { - size_t l, u, idx; - const void *p; - int comparison; - - assert(!size_multiply_overflow(nmemb, size)); - - l = 0; - u = nmemb; - while (l < u) { - idx = (l + u) / 2; - p = (const uint8_t*) base + idx * size; - comparison = compar(key, p, arg); - if (comparison < 0) - u = idx; - else if (comparison > 0) - l = idx + 1; - else - return (void *)p; - } - return NULL; -} - -bool memeqzero(const void *data, size_t length) { - /* Does the buffer consist entirely of NULs? - * Copied from https://github.com/systemd/casync/, copied in turn from - * https://github.com/rustyrussell/ccan/blob/master/ccan/mem/mem.c#L92, - * which is licensed CC-0. - */ - - const uint8_t *p = data; - size_t i; - - /* Check first 16 bytes manually */ - for (i = 0; i < 16; i++, length--) { - if (length == 0) - return true; - if (p[i]) - return false; - } - - /* Now we know first 16 bytes are NUL, memcmp with self. */ - return memcmp(data, p + i, length) == 0; -} - int on_ac_power(void) { bool found_offline = false, found_online = false; _cleanup_closedir_ DIR *d = NULL; @@ -299,268 +222,6 @@ int container_get_leader(const char *machine, pid_t *pid) { return 0; } -int namespace_open(pid_t pid, int *pidns_fd, int *mntns_fd, int *netns_fd, int *userns_fd, int *root_fd) { - _cleanup_close_ int pidnsfd = -1, mntnsfd = -1, netnsfd = -1, usernsfd = -1; - int rfd = -1; - - assert(pid >= 0); - - if (mntns_fd) { - const char *mntns; - - mntns = procfs_file_alloca(pid, "ns/mnt"); - mntnsfd = open(mntns, O_RDONLY|O_NOCTTY|O_CLOEXEC); - if (mntnsfd < 0) - return -errno; - } - - if (pidns_fd) { - const char *pidns; - - pidns = procfs_file_alloca(pid, "ns/pid"); - pidnsfd = open(pidns, O_RDONLY|O_NOCTTY|O_CLOEXEC); - if (pidnsfd < 0) - return -errno; - } - - if (netns_fd) { - const char *netns; - - netns = procfs_file_alloca(pid, "ns/net"); - netnsfd = open(netns, O_RDONLY|O_NOCTTY|O_CLOEXEC); - if (netnsfd < 0) - return -errno; - } - - if (userns_fd) { - const char *userns; - - userns = procfs_file_alloca(pid, "ns/user"); - usernsfd = open(userns, O_RDONLY|O_NOCTTY|O_CLOEXEC); - if (usernsfd < 0 && errno != ENOENT) - return -errno; - } - - if (root_fd) { - const char *root; - - root = procfs_file_alloca(pid, "root"); - rfd = open(root, O_RDONLY|O_NOCTTY|O_CLOEXEC|O_DIRECTORY); - if (rfd < 0) - return -errno; - } - - if (pidns_fd) - *pidns_fd = pidnsfd; - - if (mntns_fd) - *mntns_fd = mntnsfd; - - if (netns_fd) - *netns_fd = netnsfd; - - if (userns_fd) - *userns_fd = usernsfd; - - if (root_fd) - *root_fd = rfd; - - pidnsfd = mntnsfd = netnsfd = usernsfd = -1; - - return 0; -} - -int namespace_enter(int pidns_fd, int mntns_fd, int netns_fd, int userns_fd, int root_fd) { - if (userns_fd >= 0) { - /* Can't setns to your own userns, since then you could - * escalate from non-root to root in your own namespace, so - * check if namespaces equal before attempting to enter. */ - _cleanup_free_ char *userns_fd_path = NULL; - int r; - if (asprintf(&userns_fd_path, "/proc/self/fd/%d", userns_fd) < 0) - return -ENOMEM; - - r = files_same(userns_fd_path, "/proc/self/ns/user", 0); - if (r < 0) - return r; - if (r) - userns_fd = -1; - } - - if (pidns_fd >= 0) - if (setns(pidns_fd, CLONE_NEWPID) < 0) - return -errno; - - if (mntns_fd >= 0) - if (setns(mntns_fd, CLONE_NEWNS) < 0) - return -errno; - - if (netns_fd >= 0) - if (setns(netns_fd, CLONE_NEWNET) < 0) - return -errno; - - if (userns_fd >= 0) - if (setns(userns_fd, CLONE_NEWUSER) < 0) - return -errno; - - if (root_fd >= 0) { - if (fchdir(root_fd) < 0) - return -errno; - - if (chroot(".") < 0) - return -errno; - } - - return reset_uid_gid(); -} - -uint64_t physical_memory(void) { - _cleanup_free_ char *root = NULL, *value = NULL; - uint64_t mem, lim; - size_t ps; - long sc; - int r; - - /* We return this as uint64_t in case we are running as 32bit process on a 64bit kernel with huge amounts of - * memory. - * - * In order to support containers nicely that have a configured memory limit we'll take the minimum of the - * physically reported amount of memory and the limit configured for the root cgroup, if there is any. */ - - sc = sysconf(_SC_PHYS_PAGES); - assert(sc > 0); - - ps = page_size(); - mem = (uint64_t) sc * (uint64_t) ps; - - r = cg_get_root_path(&root); - if (r < 0) { - log_debug_errno(r, "Failed to determine root cgroup, ignoring cgroup memory limit: %m"); - return mem; - } - - r = cg_all_unified(); - if (r < 0) { - log_debug_errno(r, "Failed to determine root unified mode, ignoring cgroup memory limit: %m"); - return mem; - } - if (r > 0) { - r = cg_get_attribute("memory", root, "memory.max", &value); - if (r < 0) { - log_debug_errno(r, "Failed to read memory.max cgroup attribute, ignoring cgroup memory limit: %m"); - return mem; - } - - if (streq(value, "max")) - return mem; - } else { - r = cg_get_attribute("memory", root, "memory.limit_in_bytes", &value); - if (r < 0) { - log_debug_errno(r, "Failed to read memory.limit_in_bytes cgroup attribute, ignoring cgroup memory limit: %m"); - return mem; - } - } - - r = safe_atou64(value, &lim); - if (r < 0) { - log_debug_errno(r, "Failed to parse cgroup memory limit '%s', ignoring: %m", value); - return mem; - } - if (lim == UINT64_MAX) - return mem; - - /* Make sure the limit is a multiple of our own page size */ - lim /= ps; - lim *= ps; - - return MIN(mem, lim); -} - -uint64_t physical_memory_scale(uint64_t v, uint64_t max) { - uint64_t p, m, ps, r; - - assert(max > 0); - - /* Returns the physical memory size, multiplied by v divided by max. Returns UINT64_MAX on overflow. On success - * the result is a multiple of the page size (rounds down). */ - - ps = page_size(); - assert(ps > 0); - - p = physical_memory() / ps; - assert(p > 0); - - m = p * v; - if (m / p != v) - return UINT64_MAX; - - m /= max; - - r = m * ps; - if (r / ps != m) - return UINT64_MAX; - - return r; -} - -uint64_t system_tasks_max(void) { - - uint64_t a = TASKS_MAX, b = TASKS_MAX; - _cleanup_free_ char *root = NULL; - int r; - - /* Determine the maximum number of tasks that may run on this system. We check three sources to determine this - * limit: - * - * a) the maximum tasks value the kernel allows on this architecture - * b) the cgroups pids_max attribute for the system - * c) the kernel's configured maximum PID value - * - * And then pick the smallest of the three */ - - r = procfs_tasks_get_limit(&a); - if (r < 0) - log_debug_errno(r, "Failed to read maximum number of tasks from /proc, ignoring: %m"); - - r = cg_get_root_path(&root); - if (r < 0) - log_debug_errno(r, "Failed to determine cgroup root path, ignoring: %m"); - else { - _cleanup_free_ char *value = NULL; - - r = cg_get_attribute("pids", root, "pids.max", &value); - if (r < 0) - log_debug_errno(r, "Failed to read pids.max attribute of cgroup root, ignoring: %m"); - else if (!streq(value, "max")) { - r = safe_atou64(value, &b); - if (r < 0) - log_debug_errno(r, "Failed to parse pids.max attribute of cgroup root, ignoring: %m"); - } - } - - return MIN3(TASKS_MAX, - a <= 0 ? TASKS_MAX : a, - b <= 0 ? TASKS_MAX : b); -} - -uint64_t system_tasks_max_scale(uint64_t v, uint64_t max) { - uint64_t t, m; - - assert(max > 0); - - /* Multiply the system's task value by the fraction v/max. Hence, if max==100 this calculates percentages - * relative to the system's maximum number of tasks. Returns UINT64_MAX on overflow. */ - - t = system_tasks_max(); - assert(t > 0); - - m = t * v; - if (m / t != v) /* overflow? */ - return UINT64_MAX; - - return m / max; -} - int version(void) { puts("systemd " STRINGIFY(PROJECT_VERSION) " (" GIT_VERSION ")\n" SYSTEMD_FEATURES); diff --git a/shared/systemd/src/basic/util.h b/shared/systemd/src/basic/util.h index dc33d660..25e6ab81 100644 --- a/shared/systemd/src/basic/util.h +++ b/shared/systemd/src/basic/util.h @@ -1,34 +1,9 @@ /* SPDX-License-Identifier: LGPL-2.1+ */ #pragma once -#include -#include -#include -#include -#include -#include -#include -#include -#include #include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "format-util.h" -#include "macro.h" -#include "time-util.h" -size_t page_size(void) _pure_; -#define PAGE_ALIGN(l) ALIGN_TO((l), page_size()) +#include "macro.h" static inline const char* yes_no(bool b) { return b ? "yes" : "no"; @@ -46,19 +21,14 @@ static inline const char* enable_disable(bool b) { return b ? "enable" : "disable"; } -bool plymouth_running(void); - -bool display_is_local(const char *display) _pure_; - -#define NULSTR_FOREACH(i, l) \ - for ((i) = (l); (i) && *(i); (i) = strchr((i), 0)+1) - -#define NULSTR_FOREACH_PAIR(i, j, l) \ - for ((i) = (l), (j) = strchr((i), 0)+1; (i) && *(i); (i) = strchr((j), 0)+1, (j) = *(i) ? strchr((i), 0)+1 : (i)) - extern int saved_argc; extern char **saved_argv; +static inline void save_argc_argv(int argc, char **argv) { + saved_argc = argc; + saved_argv = argv; +} + bool kexec_loaded(void); int prot_from_flags(int flags) _const_; @@ -66,138 +36,8 @@ int prot_from_flags(int flags) _const_; bool in_initrd(void); void in_initrd_force(bool value); -void *xbsearch_r(const void *key, const void *base, size_t nmemb, size_t size, - __compar_d_fn_t compar, void *arg); - -#define typesafe_bsearch_r(k, b, n, func, userdata) \ - ({ \ - const typeof(b[0]) *_k = k; \ - int (*_func_)(const typeof(b[0])*, const typeof(b[0])*, typeof(userdata)) = func; \ - xbsearch_r((const void*) _k, (b), (n), sizeof((b)[0]), (__compar_d_fn_t) _func_, userdata); \ - }) - -/** - * Normal bsearch requires base to be nonnull. Here were require - * that only if nmemb > 0. - */ -static inline void* bsearch_safe(const void *key, const void *base, - size_t nmemb, size_t size, __compar_fn_t compar) { - if (nmemb <= 0) - return NULL; - - assert(base); - return bsearch(key, base, nmemb, size, compar); -} - -#define typesafe_bsearch(k, b, n, func) \ - ({ \ - const typeof(b[0]) *_k = k; \ - int (*_func_)(const typeof(b[0])*, const typeof(b[0])*) = func; \ - bsearch_safe((const void*) _k, (b), (n), sizeof((b)[0]), (__compar_fn_t) _func_); \ - }) - -/** - * Normal qsort requires base to be nonnull. Here were require - * that only if nmemb > 0. - */ -static inline void qsort_safe(void *base, size_t nmemb, size_t size, __compar_fn_t compar) { - if (nmemb <= 1) - return; - - assert(base); - qsort(base, nmemb, size, compar); -} - -/* A wrapper around the above, but that adds typesafety: the element size is automatically derived from the type and so - * is the prototype for the comparison function */ -#define typesafe_qsort(p, n, func) \ - ({ \ - int (*_func_)(const typeof(p[0])*, const typeof(p[0])*) = func; \ - qsort_safe((p), (n), sizeof((p)[0]), (__compar_fn_t) _func_); \ - }) - -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; - - assert(base); - qsort_r(base, nmemb, size, compar, userdata); -} - -#define typesafe_qsort_r(p, n, func, userdata) \ - ({ \ - 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); \ - }) - -/* Normal memcpy requires src to be nonnull. We do nothing if n is 0. */ -static inline void memcpy_safe(void *dst, const void *src, size_t n) { - if (n == 0) - return; - assert(src); - memcpy(dst, src, n); -} - -/* Normal memcmp requires s1 and s2 to be nonnull. We do nothing if n is 0. */ -static inline int memcmp_safe(const void *s1, const void *s2, size_t n) { - if (n == 0) - return 0; - assert(s1); - assert(s2); - return memcmp(s1, s2, n); -} - -/* Compare s1 (length n1) with s2 (length n2) in lexicographic order. */ -static inline int memcmp_nn(const void *s1, size_t n1, const void *s2, size_t n2) { - return memcmp_safe(s1, s2, MIN(n1, n2)) - ?: CMP(n1, n2); -} - int on_ac_power(void); -#define memzero(x,l) \ - ({ \ - size_t _l_ = (l); \ - void *_x_ = (x); \ - _l_ == 0 ? _x_ : memset(_x_, 0, _l_); \ - }) - -#define zero(x) (memzero(&(x), sizeof(x))) - -bool memeqzero(const void *data, size_t length); - -#define eqzero(x) memeqzero(x, sizeof(x)) - -static inline void *mempset(void *s, int c, size_t n) { - memset(s, c, n); - return (uint8_t*)s + n; -} - -static inline void _reset_errno_(int *saved_errno) { - if (*saved_errno < 0) /* Invalidated by UNPROTECT_ERRNO? */ - return; - - errno = *saved_errno; -} - -#define PROTECT_ERRNO \ - _cleanup_(_reset_errno_) _unused_ int _saved_errno_ = errno - -#define UNPROTECT_ERRNO \ - do { \ - errno = _saved_errno_; \ - _saved_errno_ = -1; \ - } while (false) - -static inline int negative_errno(void) { - /* This helper should be used to shut up gcc if you know 'errno' is - * negative. Instead of "return -errno;", use "return negative_errno();" - * It will suppress bogus gcc warnings in case it assumes 'errno' might - * be 0 and thus the caller's error-handling might not be triggered. */ - assert_return(errno > 0, -EINVAL); - return -errno; -} - static inline unsigned u64log2(uint64_t n) { #if __SIZEOF_LONG_LONG__ == 8 return (n > 1) ? (unsigned) __builtin_clzll(n) ^ 63U : 0; @@ -237,15 +77,6 @@ static inline unsigned log2u_round_up(unsigned x) { int container_get_leader(const char *machine, pid_t *pid); -int namespace_open(pid_t pid, int *pidns_fd, int *mntns_fd, int *netns_fd, int *userns_fd, int *root_fd); -int namespace_enter(int pidns_fd, int mntns_fd, int netns_fd, int userns_fd, int root_fd); - -uint64_t physical_memory(void); -uint64_t physical_memory_scale(uint64_t v, uint64_t max); - -uint64_t system_tasks_max(void); -uint64_t system_tasks_max_scale(uint64_t v, uint64_t max); - int version(void); int str_verscmp(const char *s1, const char *s2); -- cgit 1.3.0-6-gf8a5