diff options
| author | Michael Biebl <biebl@debian.org> | 2019-04-21 21:09:51 +0200 |
|---|---|---|
| committer | Michael Biebl <biebl@debian.org> | 2019-04-21 21:09:51 +0200 |
| commit | 85563b7fc7ec2cd21e38debb9b28db342e2e8e7c (patch) | |
| tree | cce7b0b02d28fae2df9fdf2c1804cacd1500f2d7 /shared | |
| parent | 9a6dcbf895f9da01768e64b73cec88c16157d91e (diff) | |
New upstream version 1.18.0 upstream/1.18.0
Diffstat (limited to 'shared')
101 files changed, 2296 insertions, 1225 deletions
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-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-utils/nm-c-list.h b/shared/nm-glib-aux/nm-c-list.h index 5c73f574..5c73f574 100644 --- a/shared/nm-utils/nm-c-list.h +++ b/shared/nm-glib-aux/nm-c-list.h diff --git a/shared/nm-utils/nm-dedup-multi.c b/shared/nm-glib-aux/nm-dedup-multi.c index 5bdc3e3c..5bdc3e3c 100644 --- a/shared/nm-utils/nm-dedup-multi.c +++ b/shared/nm-glib-aux/nm-dedup-multi.c diff --git a/shared/nm-utils/nm-dedup-multi.h b/shared/nm-glib-aux/nm-dedup-multi.h index 845b4c3e..82c6f1e9 100644 --- a/shared/nm-utils/nm-dedup-multi.h +++ b/shared/nm-glib-aux/nm-dedup-multi.h @@ -23,7 +23,7 @@ #define __NM_DEDUP_MULTI_H__ #include "nm-obj.h" -#include "c-list-util.h" +#include "nm-std-aux/c-list-util.h" /*****************************************************************************/ diff --git a/shared/nm-utils/nm-enum-utils.c b/shared/nm-glib-aux/nm-enum-utils.c index a4f6e809..a4f6e809 100644 --- a/shared/nm-utils/nm-enum-utils.c +++ b/shared/nm-glib-aux/nm-enum-utils.c diff --git a/shared/nm-utils/nm-enum-utils.h b/shared/nm-glib-aux/nm-enum-utils.h index 1827fdf4..1827fdf4 100644 --- a/shared/nm-utils/nm-enum-utils.h +++ b/shared/nm-glib-aux/nm-enum-utils.h diff --git a/shared/nm-utils/nm-errno.c b/shared/nm-glib-aux/nm-errno.c index 30eb9a8e..30eb9a8e 100644 --- a/shared/nm-utils/nm-errno.c +++ b/shared/nm-glib-aux/nm-errno.c diff --git a/shared/nm-utils/nm-errno.h b/shared/nm-glib-aux/nm-errno.h index d77735a7..d77735a7 100644 --- a/shared/nm-utils/nm-errno.h +++ b/shared/nm-glib-aux/nm-errno.h diff --git a/shared/nm-utils/nm-glib.h b/shared/nm-glib-aux/nm-glib.h index e941e067..e941e067 100644 --- a/shared/nm-utils/nm-glib.h +++ b/shared/nm-glib-aux/nm-glib.h diff --git a/shared/nm-utils/nm-hash-utils.c b/shared/nm-glib-aux/nm-hash-utils.c index 6e728e6b..6e728e6b 100644 --- a/shared/nm-utils/nm-hash-utils.c +++ b/shared/nm-glib-aux/nm-hash-utils.c diff --git a/shared/nm-utils/nm-hash-utils.h b/shared/nm-glib-aux/nm-hash-utils.h index 1a1e44f5..3f622f99 100644 --- a/shared/nm-utils/nm-hash-utils.h +++ b/shared/nm-glib-aux/nm-hash-utils.h @@ -83,7 +83,11 @@ nm_hash_complete_u64 (NMHashState *state) * from nm_hash_complete() in two ways: * * - the type, guint64 vs. guint. - * - nm_hash_complete() never returns zero. */ + * - 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); } @@ -287,4 +291,25 @@ 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-utils/nm-io-utils.c b/shared/nm-glib-aux/nm-io-utils.c index 51312748..51312748 100644 --- a/shared/nm-utils/nm-io-utils.c +++ b/shared/nm-glib-aux/nm-io-utils.c diff --git a/shared/nm-utils/nm-io-utils.h b/shared/nm-glib-aux/nm-io-utils.h index dc72a2a6..dc72a2a6 100644 --- a/shared/nm-utils/nm-io-utils.h +++ b/shared/nm-glib-aux/nm-io-utils.h diff --git a/shared/nm-utils/nm-jansson.h b/shared/nm-glib-aux/nm-jansson.h index 5a73231f..5a73231f 100644 --- a/shared/nm-utils/nm-jansson.h +++ b/shared/nm-glib-aux/nm-jansson.h diff --git a/shared/nm-utils/nm-logging-fwd.h b/shared/nm-glib-aux/nm-logging-fwd.h index 900dfff8..900dfff8 100644 --- a/shared/nm-utils/nm-logging-fwd.h +++ b/shared/nm-glib-aux/nm-logging-fwd.h diff --git a/shared/nm-utils/nm-macros-internal.h b/shared/nm-glib-aux/nm-macros-internal.h index 42299c96..2e46cd2d 100644 --- a/shared/nm-utils/nm-macros-internal.h +++ b/shared/nm-glib-aux/nm-macros-internal.h @@ -267,6 +267,8 @@ NM_AUTO_DEFINE_FCN0 (GVariantIter *, _nm_auto_free_variant_iter, g_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) @@ -323,6 +325,9 @@ _nm_auto_protect_errno (int *p_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) { @@ -1334,16 +1339,19 @@ _NM_BACKPORT_SYMBOL_IMPL(version, return_type, func, _##func##_##version, args_t /*****************************************************************************/ +/* 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 = (str); \ - _nm_unused const char *_str_type_check = _str; \ + typeof (*(str)) *_str_sls = (str); \ + _nm_unused const char *const _str_type_check = _str_sls; \ \ - if (_str) { \ - while (g_ascii_isspace (_str[0])) \ - _str++; \ + if (_str_sls) { \ + while (g_ascii_isspace (_str_sls[0])) \ + _str_sls++; \ } \ - _str; \ + _str_sls; \ }) static inline char * @@ -1380,6 +1388,34 @@ nm_strstrip_avoid_copy (const char *str, char **str_free) 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(). @@ -1458,6 +1494,118 @@ nm_strcmp_p (gconstpointer a, gconstpointer b) /*****************************************************************************/ +/* 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) { diff --git a/shared/nm-utils/nm-obj.h b/shared/nm-glib-aux/nm-obj.h index 4edd1f3e..4edd1f3e 100644 --- a/shared/nm-utils/nm-obj.h +++ b/shared/nm-glib-aux/nm-obj.h diff --git a/shared/nm-utils/nm-random-utils.c b/shared/nm-glib-aux/nm-random-utils.c index d7c7da42..d7c7da42 100644 --- a/shared/nm-utils/nm-random-utils.c +++ b/shared/nm-glib-aux/nm-random-utils.c diff --git a/shared/nm-utils/nm-random-utils.h b/shared/nm-glib-aux/nm-random-utils.h index 15a118d3..15a118d3 100644 --- a/shared/nm-utils/nm-random-utils.h +++ b/shared/nm-glib-aux/nm-random-utils.h diff --git a/shared/nm-utils/nm-secret-utils.c b/shared/nm-glib-aux/nm-secret-utils.c index ec5cc6b1..81f8b5ae 100644 --- a/shared/nm-utils/nm-secret-utils.c +++ b/shared/nm-glib-aux/nm-secret-utils.c @@ -30,15 +30,22 @@ 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 (n == 0) + return; + + nm_assert (s); + #if defined (HAVE_DECL_EXPLICIT_BZERO) && HAVE_DECL_EXPLICIT_BZERO - explicit_bzero (s, n); + explicit_bzero (s, n); #else - /* don't bother with a workaround. Use a reasonable glibc. */ - memset (s, 0, n); -#endif + { + volatile guint8 *p = s; + + memset (s, '\0', n); + while (n-- > 0) + *(p++) = '\0'; } +#endif } /*****************************************************************************/ diff --git a/shared/nm-utils/nm-secret-utils.h b/shared/nm-glib-aux/nm-secret-utils.h index 034ef7bd..034ef7bd 100644 --- a/shared/nm-utils/nm-secret-utils.h +++ b/shared/nm-glib-aux/nm-secret-utils.h diff --git a/shared/nm-utils/nm-shared-utils.c b/shared/nm-glib-aux/nm-shared-utils.c index 6a43c670..cf08a77f 100644 --- a/shared/nm-utils/nm-shared-utils.c +++ b/shared/nm-glib-aux/nm-shared-utils.c @@ -635,8 +635,16 @@ nm_utils_parse_inaddr (int addr_family, NMIPAddr addrbin; char addrstr_buf[MAX (INET_ADDRSTRLEN, INET6_ADDRSTRLEN)]; - if (!nm_utils_parse_inaddr_bin (addr_family, text, &addr_family, &addrbin)) + 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; } @@ -954,134 +962,336 @@ comp_l: /*****************************************************************************/ +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: + * nm_utils_strsplit_set_full: * @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 + * @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. * - * Another difference from g_strsplit_set() is that this never returns - * empty words. Multiple delimiters are combined and treated as one. + * 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 @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. + * 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 contains only delimiters. - * Otherwise, a %NULL terminated strv array containing non-empty - * words, split at the delimiter characters (delimiter characters - * are removed). + * 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 (const char *str, const char *delimiters, gboolean allow_escaping) -{ - const char **ptr, **ptr0; - gsize alloc_size, plen, i; - gsize str_len; - char *s0; +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 delimiters_table[256]; - gboolean escaped = FALSE; + 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; - /* initialize lookup table for delimiter */ - if (!delimiters) + if (!delimiters) { + nm_assert_not_reached (); 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)) + } + _char_lookup_table_init (ch_lookup, delimiters); -#define next_char(p, esc) \ - G_STMT_START { \ - if (esc) \ - esc = FALSE; \ - else \ - esc = p[0] == '\\'; \ - p++; \ - } G_STMT_END + nm_assert ( !f_allow_escaping + || !_char_lookup_has (ch_lookup, '\\')); - /* 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 (!f_preserve_empty) { + while (_char_lookup_has (ch_lookup, str[0])) + str++; + } - if (!str[0]) + 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; + } - str_len = strlen (str) + 1; - alloc_size = 8; +#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 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); + /* 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; + } - plen = 0; - s = s0; - ptr = ptr0; + c_str++; - 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); + /* 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; } - ptr[plen++] = s; + num_tokens++; + } - nm_assert (s[0] && !_is_delimiter (s[0], delimiters_table, allow_escaping, escaped)); +done1: - while (TRUE) { - next_char (s, escaped); - if (_is_delimiter (s[0], delimiters_table, allow_escaping, escaped)) - break; - if (s[0] == '\0') - goto done; + 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'; - next_char (s, escaped); - while (_is_delimiter (s[0], delimiters_table, allow_escaping, escaped)) - next_char (s, escaped); - if (s[0] == '\0') - break; + s++; + + if (!f_preserve_empty) { + while (_char_lookup_has (ch_lookup, s[0])) + s++; + if (s[0] == '\0') + goto done2; + } } -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); +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 @@ -1163,31 +1373,27 @@ int _nm_utils_ascii_str_to_bool (const char *str, int default_value) { - gsize len; - char *s = NULL; + gs_free char *str_free = NULL; if (!str) return default_value; - while (str[0] && g_ascii_isspace (str[0])) - str++; - - if (!str[0]) + str = nm_strstrip_avoid_copy_a (300, str, &str_free); + if (str[0] == '\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")) + 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; - 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; } @@ -2258,7 +2464,7 @@ nm_utils_get_start_time_for_pid (pid_t pid, char *out_state, pid_t *out_ppid) state = p[0]; - tokens = nm_utils_strsplit_set (p, " ", FALSE); + tokens = nm_utils_strsplit_set (p, " "); if (NM_PTRARRAY_LEN (tokens) < 20) goto fail; @@ -2318,6 +2524,57 @@ _nm_utils_strv_sort (const char **strv, gssize len) 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 @@ -2361,63 +2618,6 @@ _nm_utils_user_data_unpack (gpointer user_data, int nargs, ...) /*****************************************************************************/ -#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; diff --git a/shared/nm-utils/nm-shared-utils.h b/shared/nm-glib-aux/nm-shared-utils.h index 65e34959..af3c2f83 100644 --- a/shared/nm-utils/nm-shared-utils.h +++ b/shared/nm-glib-aux/nm-shared-utils.h @@ -126,6 +126,16 @@ typedef struct { 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) { @@ -248,118 +258,6 @@ 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) @@ -434,7 +332,71 @@ 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); +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); @@ -445,6 +407,70 @@ char **_nm_utils_strv_cleanup (char **strv, /*****************************************************************************/ +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 @@ -941,6 +967,18 @@ gboolean nm_utils_hash_table_equal (const GHashTable *a, 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) @@ -1072,11 +1110,6 @@ void _nm_utils_user_data_unpack (gpointer user_data, int nargs, ...); /*****************************************************************************/ -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); diff --git a/shared/nm-utils/nm-time-utils.c b/shared/nm-glib-aux/nm-time-utils.c index ae526c34..ae526c34 100644 --- a/shared/nm-utils/nm-time-utils.c +++ b/shared/nm-glib-aux/nm-time-utils.c diff --git a/shared/nm-utils/nm-time-utils.h b/shared/nm-glib-aux/nm-time-utils.h index 7e4f4f25..7e4f4f25 100644 --- a/shared/nm-utils/nm-time-utils.h +++ b/shared/nm-glib-aux/nm-time-utils.h diff --git a/shared/nm-dispatcher-api.h b/shared/nm-libnm-core-aux/nm-dispatcher-api.h index b1f28e71..e6d0d92f 100644 --- a/shared/nm-dispatcher-api.h +++ b/shared/nm-libnm-core-aux/nm-dispatcher-api.h @@ -18,6 +18,9 @@ * 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" @@ -59,3 +62,4 @@ typedef enum { DISPATCH_RESULT_TIMEOUT = 4, } DispatchResult; +#endif /* __NM_DISPACHER_API_H__ */ diff --git a/shared/nm-common-macros.h b/shared/nm-libnm-core-intern/nm-common-macros.h index f5aa3a1e..f5aa3a1e 100644 --- a/shared/nm-common-macros.h +++ b/shared/nm-libnm-core-intern/nm-common-macros.h diff --git a/shared/nm-ethtool-utils.c b/shared/nm-libnm-core-intern/nm-ethtool-utils.c index 3313274a..3313274a 100644 --- a/shared/nm-ethtool-utils.c +++ b/shared/nm-libnm-core-intern/nm-ethtool-utils.c diff --git a/shared/nm-ethtool-utils.h b/shared/nm-libnm-core-intern/nm-ethtool-utils.h index 5f22a9a0..5f22a9a0 100644 --- a/shared/nm-ethtool-utils.h +++ b/shared/nm-libnm-core-intern/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-utils/c-list-util.c b/shared/nm-std-aux/c-list-util.c index 44ca26a5..44ca26a5 100644 --- a/shared/nm-utils/c-list-util.c +++ b/shared/nm-std-aux/c-list-util.c diff --git a/shared/nm-utils/c-list-util.h b/shared/nm-std-aux/c-list-util.h index e87f1c19..648bacc7 100644 --- a/shared/nm-utils/c-list-util.h +++ b/shared/nm-std-aux/c-list-util.h @@ -40,4 +40,27 @@ 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-dbus-compat.h b/shared/nm-std-aux/nm-dbus-compat.h index dd97b5fd..dd97b5fd 100644 --- a/shared/nm-dbus-compat.h +++ b/shared/nm-std-aux/nm-dbus-compat.h diff --git a/shared/nm-utils/unaligned.h b/shared/nm-std-aux/unaligned.h index 00c17f87..00c17f87 100644 --- a/shared/nm-utils/unaligned.h +++ b/shared/nm-std-aux/unaligned.h 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 <sys/wait.h> #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-utils/nm-udev-utils.c b/shared/nm-udev-aux/nm-udev-utils.c index 5d0919b3..5d0919b3 100644 --- a/shared/nm-utils/nm-udev-utils.c +++ b/shared/nm-udev-aux/nm-udev-utils.c diff --git a/shared/nm-utils/nm-udev-utils.h b/shared/nm-udev-aux/nm-udev-utils.h index 911e8a27..911e8a27 100644 --- a/shared/nm-utils/nm-udev-utils.h +++ b/shared/nm-udev-aux/nm-udev-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-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 <NetworkManager.h> /* 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-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/namespace-util.h index 637892c2..637892c2 100644 --- a/shared/systemd/sd-adapt-shared/missing_socket.h +++ b/shared/systemd/sd-adapt-shared/namespace-util.h 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 <syslog.h> -#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 <malloc.h> #include <stdint.h> #include <string.h> #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 <sanitizer/msan_interface.h> +#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 <stdbool.h> #include <stddef.h> #include <stdio.h> +#include <sys/stat.h> #include <sys/types.h> #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 <stdlib.h> #include <string.h> #include <sys/stat.h> +#include <linux/falloc.h> #include <linux/magic.h> #include <time.h> #include <unistd.h> @@ -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 <sys/types.h> #include <unistd.h> +#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 <pthread.h> @@ -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 <errno.h> #include <net/if.h> #include <stdint.h> +#include <stdio.h> #include <stdlib.h> #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 <unistd.h> + +#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 <inttypes.h> +#include <stdbool.h> +#include <string.h> +#include <sys/types.h> + +#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 <sys/socket.h> + +#if 0 /* NM_IGNORED */ +#if HAVE_LINUX_VM_SOCKETS_H +#include <linux/vm_sockets.h> +#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 <linux/types.h> +#include <sys/stat.h> + +#if 0 /* NM_IGNORED */ +#if WANT_LINUX_STAT_H +#include <linux/stat.h> +#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 <sys/resource.h> #include <sys/types.h> +#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 <linux/random.h> #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 <sanitizer/msan_interface.h> -#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 <unistd.h> #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 <stdlib.h> + +#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 <fcntl.h> #include <stdbool.h> #include <stddef.h> #include <sys/stat.h> @@ -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 <sys/types.h> #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 <stdarg.h> #include <stdbool.h> #include <stddef.h> +#include <stdio.h> #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 <alloca.h> -#include <errno.h> -#include <fcntl.h> -#include <inttypes.h> -#include <limits.h> -#include <locale.h> -#include <stdarg.h> -#include <stdbool.h> -#include <stddef.h> #include <stdint.h> -#include <stdio.h> -#include <stdlib.h> -#include <string.h> -#include <sys/inotify.h> -#include <sys/socket.h> -#include <sys/stat.h> -#include <sys/statfs.h> -#include <sys/sysmacros.h> -#include <sys/types.h> -#include <time.h> -#include <unistd.h> - -#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); |