diff options
Diffstat (limited to 'shared')
56 files changed, 1419 insertions, 430 deletions
diff --git a/shared/meson.build b/shared/meson.build index 54ef3a2e..a6e94d6b 100644 --- a/shared/meson.build +++ b/shared/meson.build @@ -81,37 +81,37 @@ shared_nm_test_utils_impl_c = files('nm-test-utils-impl.c') shared_nm_utils_nm_vpn_plugin_utils_c = files('nm-utils/nm-vpn-plugin-utils.c') -shared_files_time_utils = files('nm-utils/nm-time-utils.c') - -shared_files_libnm_core = 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-udev-utils.c -'''.split()) - -shared_files_clients_common = files(''' - nm-utils/nm-enum-utils.c - nm-utils/nm-hash-utils.c - nm-utils/nm-random-utils.c - nm-utils/nm-shared-utils.c -'''.split()) - -shared_files_libnm_util = files(''' - nm-utils/nm-shared-utils.c -'''.split()) - -shared_files_libnm_glib = files(''' - nm-utils/nm-udev-utils.c -'''.split()) - -shared_dep = declare_dependency( +############################################################################### + +shared_nm_utils_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( + '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, + include_directories: [ + top_inc, + shared_inc, + ], + dependencies: [ + glib_dep, + ], +) + +shared_nm_utils_base_dep = declare_dependency( + link_with: shared_nm_utils_base, include_directories: [ top_inc, shared_inc, @@ -119,26 +119,44 @@ shared_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, + include_directories: [ + top_inc, + shared_inc, + ], + dependencies: [ + glib_dep, + shared_nm_utils_base_dep, + libudev_dep, + ], +) + +shared_nm_utils_udev_dep = declare_dependency( + link_with: shared_nm_utils_udev, + include_directories: [ + top_inc, + shared_inc, + ], + dependencies: [ + glib_dep, + shared_nm_utils_base_dep, + libudev_dep, + ], +) + ############################################################################### test_shared_general = executable( 'nm-utils/tests/test-shared-general', - [ 'nm-utils/tests/test-shared-general.c', - 'nm-utils/c-list-util.c', - 'nm-utils/nm-dedup-multi.c', - 'nm-utils/nm-enum-utils.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', - ], + [ '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_dep, + dependencies: shared_nm_utils_base_dep, link_with: shared_c_siphash, ) test( @@ -188,7 +206,7 @@ libnm_systemd_shared = static_library( 'systemd/sd-adapt-shared', 'systemd/src/basic', ), - dependencies: shared_dep, + dependencies: shared_nm_utils_base_dep, c_args: [ '-DNETWORKMANAGER_COMPILATION=NM_NETWORKMANAGER_COMPILATION_SYSTEMD_SHARED', '-DG_LOG_DOMAIN="libnm"', @@ -201,7 +219,7 @@ libnm_systemd_shared_dep = declare_dependency( 'systemd/src/basic', ), dependencies: [ - shared_dep, + shared_nm_utils_base_dep, ], link_with: [ libnm_systemd_shared, @@ -217,7 +235,7 @@ libnm_systemd_logging_stub = static_library( 'systemd/sd-adapt-shared', 'systemd/src/basic', ), - dependencies: shared_dep, + dependencies: shared_nm_utils_base_dep, c_args: [ '-DNETWORKMANAGER_COMPILATION=NM_NETWORKMANAGER_COMPILATION_SYSTEMD_SHARED', '-DG_LOG_DOMAIN="libnm"', diff --git a/shared/nm-common-macros.h b/shared/nm-common-macros.h index 2edb9728..f5aa3a1e 100644 --- a/shared/nm-common-macros.h +++ b/shared/nm-common-macros.h @@ -40,6 +40,7 @@ #define NM_AUTH_PERMISSION_CHECKPOINT_ROLLBACK "org.freedesktop.NetworkManager.checkpoint-rollback" #define NM_AUTH_PERMISSION_ENABLE_DISABLE_STATISTICS "org.freedesktop.NetworkManager.enable-disable-statistics" #define NM_AUTH_PERMISSION_ENABLE_DISABLE_CONNECTIVITY_CHECK "org.freedesktop.NetworkManager.enable-disable-connectivity-check" +#define NM_AUTH_PERMISSION_WIFI_SCAN "org.freedesktop.NetworkManager.wifi.scan" #define NM_CLONED_MAC_PRESERVE "preserve" #define NM_CLONED_MAC_PERMANENT "permanent" diff --git a/shared/nm-default.h b/shared/nm-default.h index c006f33d..26d6476a 100644 --- a/shared/nm-default.h +++ b/shared/nm-default.h @@ -292,6 +292,7 @@ _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" #if (NETWORKMANAGER_COMPILATION) & NM_NETWORKMANAGER_COMPILATION_WITH_LIBNM_UTIL /* no hash-utils in legacy code. */ diff --git a/shared/nm-ethtool-utils.c b/shared/nm-ethtool-utils.c index d50695ae..3313274a 100644 --- a/shared/nm-ethtool-utils.c +++ b/shared/nm-ethtool-utils.c @@ -90,7 +90,7 @@ const NMEthtoolData *const nm_ethtool_data[_NM_ETHTOOL_ID_NUM + 1] = { [_NM_ETHTOOL_ID_NUM] = NULL, }; -const guint8 const _by_name[_NM_ETHTOOL_ID_NUM] = { +static const guint8 _by_name[_NM_ETHTOOL_ID_NUM] = { /* sorted by optname. */ NM_ETHTOOL_ID_FEATURE_ESP_HW_OFFLOAD, NM_ETHTOOL_ID_FEATURE_ESP_TX_CSUM_HW_OFFLOAD, diff --git a/shared/nm-meta-setting.c b/shared/nm-meta-setting.c index 3e79747f..e666e0b2 100644 --- a/shared/nm-meta-setting.c +++ b/shared/nm-meta-setting.c @@ -28,8 +28,8 @@ #include "nm-setting-adsl.h" #include "nm-setting-bluetooth.h" #include "nm-setting-bond.h" -#include "nm-setting-bridge.h" #include "nm-setting-bridge-port.h" +#include "nm-setting-bridge.h" #include "nm-setting-cdma.h" #include "nm-setting-connection.h" #include "nm-setting-dcb.h" @@ -38,10 +38,10 @@ #include "nm-setting-generic.h" #include "nm-setting-gsm.h" #include "nm-setting-infiniband.h" -#include "nm-setting-ip4-config.h" -#include "nm-setting-ip6-config.h" #include "nm-setting-ip-config.h" #include "nm-setting-ip-tunnel.h" +#include "nm-setting-ip4-config.h" +#include "nm-setting-ip6-config.h" #include "nm-setting-macsec.h" #include "nm-setting-macvlan.h" #include "nm-setting-match.h" @@ -55,17 +55,19 @@ #include "nm-setting-proxy.h" #include "nm-setting-serial.h" #include "nm-setting-tc-config.h" -#include "nm-setting-team.h" #include "nm-setting-team-port.h" +#include "nm-setting-team.h" #include "nm-setting-tun.h" #include "nm-setting-user.h" #include "nm-setting-vlan.h" #include "nm-setting-vpn.h" #include "nm-setting-vxlan.h" +#include "nm-setting-wifi-p2p.h" #include "nm-setting-wimax.h" #include "nm-setting-wired.h" -#include "nm-setting-wireless.h" +#include "nm-setting-wireguard.h" #include "nm-setting-wireless-security.h" +#include "nm-setting-wireless.h" #include "nm-setting-wpan.h" /*****************************************************************************/ @@ -383,6 +385,12 @@ const NMMetaSettingInfo nm_meta_setting_infos[] = { .setting_name = NM_SETTING_VXLAN_SETTING_NAME, .get_setting_gtype = nm_setting_vxlan_get_type, }, + [NM_META_SETTING_TYPE_WIFI_P2P] = { + .meta_type = NM_META_SETTING_TYPE_WIFI_P2P, + .setting_priority = NM_SETTING_PRIORITY_HW_BASE, + .setting_name = NM_SETTING_WIFI_P2P_SETTING_NAME, + .get_setting_gtype = nm_setting_wifi_p2p_get_type, + }, [NM_META_SETTING_TYPE_WIMAX] = { .meta_type = NM_META_SETTING_TYPE_WIMAX, .setting_priority = NM_SETTING_PRIORITY_HW_BASE, @@ -395,6 +403,12 @@ const NMMetaSettingInfo nm_meta_setting_infos[] = { .setting_name = NM_SETTING_WIRED_SETTING_NAME, .get_setting_gtype = nm_setting_wired_get_type, }, + [NM_META_SETTING_TYPE_WIREGUARD] = { + .meta_type = NM_META_SETTING_TYPE_WIREGUARD, + .setting_priority = NM_SETTING_PRIORITY_HW_BASE, + .setting_name = NM_SETTING_WIREGUARD_SETTING_NAME, + .get_setting_gtype = nm_setting_wireguard_get_type, + }, [NM_META_SETTING_TYPE_WIRELESS] = { .meta_type = NM_META_SETTING_TYPE_WIRELESS, .setting_priority = NM_SETTING_PRIORITY_HW_BASE, diff --git a/shared/nm-meta-setting.h b/shared/nm-meta-setting.h index 26c29bea..18727a16 100644 --- a/shared/nm-meta-setting.h +++ b/shared/nm-meta-setting.h @@ -145,7 +145,9 @@ typedef enum { NM_META_SETTING_TYPE_VLAN, NM_META_SETTING_TYPE_VPN, NM_META_SETTING_TYPE_VXLAN, + NM_META_SETTING_TYPE_WIFI_P2P, NM_META_SETTING_TYPE_WIMAX, + NM_META_SETTING_TYPE_WIREGUARD, NM_META_SETTING_TYPE_WPAN, NM_META_SETTING_TYPE_UNKNOWN, diff --git a/shared/nm-test-utils-impl.c b/shared/nm-test-utils-impl.c index 1da9014e..509b235a 100644 --- a/shared/nm-test-utils-impl.c +++ b/shared/nm-test-utils-impl.c @@ -20,7 +20,6 @@ #include "nm-default.h" -#include <string.h> #include <sys/wait.h> #include "NetworkManager.h" diff --git a/shared/nm-utils/nm-c-list.h b/shared/nm-utils/nm-c-list.h index b43d1441..5c73f574 100644 --- a/shared/nm-utils/nm-c-list.h +++ b/shared/nm-utils/nm-c-list.h @@ -78,4 +78,40 @@ nm_c_list_elem_free_all (CList *head, GDestroyNotify free_fcn) } } +/*****************************************************************************/ + +static inline gboolean +nm_c_list_move_before (CList *lst, CList *elem) +{ + nm_assert (lst); + nm_assert (elem); + nm_assert (c_list_contains (lst, elem)); + + if ( lst != elem + && lst->prev != elem) { + c_list_unlink_stale (elem); + c_list_link_before (lst, elem); + return TRUE; + } + return FALSE; +} +#define nm_c_list_move_tail(lst, elem) nm_c_list_move_before (lst, elem) + +static inline gboolean +nm_c_list_move_after (CList *lst, CList *elem) +{ + nm_assert (lst); + nm_assert (elem); + nm_assert (c_list_contains (lst, elem)); + + if ( lst != elem + && lst->next != elem) { + c_list_unlink_stale (elem); + c_list_link_after (lst, elem); + return TRUE; + } + return FALSE; +} +#define nm_c_list_move_front(lst, elem) nm_c_list_move_after (lst, elem) + #endif /* __NM_C_LIST_H__ */ diff --git a/shared/nm-utils/nm-dedup-multi.c b/shared/nm-utils/nm-dedup-multi.c index 852c207c..5bdc3e3c 100644 --- a/shared/nm-utils/nm-dedup-multi.c +++ b/shared/nm-utils/nm-dedup-multi.c @@ -24,6 +24,7 @@ #include "nm-dedup-multi.h" #include "nm-hash-utils.h" +#include "nm-c-list.h" /*****************************************************************************/ @@ -260,44 +261,27 @@ _add (NMDedupMultiIndex *self, nm_dedup_multi_entry_set_dirty (entry, FALSE); nm_assert (!head_existing || entry->head == head_existing); - - if (entry_order) { - nm_assert (entry_order->head == entry->head); - nm_assert (c_list_contains (&entry->lst_entries, &entry_order->lst_entries)); - nm_assert (c_list_contains (&entry_order->lst_entries, &entry->lst_entries)); - } + nm_assert (!entry_order || entry_order->head == entry->head); + nm_assert (!entry_order || c_list_contains (&entry->lst_entries, &entry_order->lst_entries)); + nm_assert (!entry_order || c_list_contains (&entry_order->lst_entries, &entry->lst_entries)); switch (mode) { case NM_DEDUP_MULTI_IDX_MODE_PREPEND_FORCE: if (entry_order) { - if ( entry_order != entry - && entry->lst_entries.next != &entry_order->lst_entries) { - c_list_unlink_stale (&entry->lst_entries); - c_list_link_before ((CList *) &entry_order->lst_entries, &entry->lst_entries); + if (nm_c_list_move_before ((CList *) &entry_order->lst_entries, &entry->lst_entries)) changed = TRUE; - } } else { - if (entry->lst_entries.prev != &entry->head->lst_entries_head) { - c_list_unlink_stale (&entry->lst_entries); - c_list_link_front ((CList *) &entry->head->lst_entries_head, &entry->lst_entries); + if (nm_c_list_move_front ((CList *) &entry->head->lst_entries_head, &entry->lst_entries)) changed = TRUE; - } } break; case NM_DEDUP_MULTI_IDX_MODE_APPEND_FORCE: if (entry_order) { - if ( entry_order != entry - && entry->lst_entries.prev != &entry_order->lst_entries) { - c_list_unlink_stale (&entry->lst_entries); - c_list_link_after ((CList *) &entry_order->lst_entries, &entry->lst_entries); + if (nm_c_list_move_after ((CList *) &entry_order->lst_entries, &entry->lst_entries)) changed = TRUE; - } } else { - if (entry->lst_entries.next != &entry->head->lst_entries_head) { - c_list_unlink_stale (&entry->lst_entries); - c_list_link_tail ((CList *) &entry->head->lst_entries_head, &entry->lst_entries); + if (nm_c_list_move_tail ((CList *) &entry->head->lst_entries_head, &entry->lst_entries)) changed = TRUE; - } } break; case NM_DEDUP_MULTI_IDX_MODE_PREPEND: @@ -1022,33 +1006,20 @@ nm_dedup_multi_entry_reorder (const NMDedupMultiEntry *entry, if (!entry_order) { const NMDedupMultiHeadEntry *head_entry = entry->head; - nm_assert (c_list_contains (&head_entry->lst_entries_head, &entry->lst_entries)); if (order_after) { - if (head_entry->lst_entries_head.prev != &entry->lst_entries) { - c_list_unlink_stale ((CList *) &entry->lst_entries); - c_list_link_tail ((CList *) &head_entry->lst_entries_head, (CList *) &entry->lst_entries); + if (nm_c_list_move_tail ((CList *) &head_entry->lst_entries_head, (CList *) &entry->lst_entries)) return TRUE; - } } else { - if (head_entry->lst_entries_head.next != &entry->lst_entries) { - c_list_unlink_stale ((CList *) &entry->lst_entries); - c_list_link_front ((CList *) &head_entry->lst_entries_head, (CList *) &entry->lst_entries); + if (nm_c_list_move_front ((CList *) &head_entry->lst_entries_head, (CList *) &entry->lst_entries)) return TRUE; - } } - } else if (entry != entry_order) { + } else { if (order_after) { - if (entry_order->lst_entries.next != &entry->lst_entries) { - c_list_unlink_stale ((CList *) &entry->lst_entries); - c_list_link_after ((CList *) &entry_order->lst_entries, (CList *) &entry->lst_entries); + if (nm_c_list_move_after ((CList *) &entry_order->lst_entries, (CList *) &entry->lst_entries)) return TRUE; - } } else { - if (entry_order->lst_entries.prev != &entry->lst_entries) { - c_list_unlink_stale ((CList *) &entry->lst_entries); - c_list_link_before ((CList *) &entry_order->lst_entries, (CList *) &entry->lst_entries); + if (nm_c_list_move_before ((CList *) &entry_order->lst_entries, (CList *) &entry->lst_entries)) return TRUE; - } } } diff --git a/shared/nm-utils/nm-errno.c b/shared/nm-utils/nm-errno.c index c87f0b48..30eb9a8e 100644 --- a/shared/nm-utils/nm-errno.c +++ b/shared/nm-utils/nm-errno.c @@ -22,33 +22,61 @@ #include "nm-errno.h" +#include <pthread.h> + /*****************************************************************************/ -NM_UTILS_LOOKUP_STR_DEFINE_STATIC (_geterror, int, +NM_UTILS_LOOKUP_STR_DEFINE_STATIC (_geterror, +#if 0 + enum _NMErrno, +#else + int, +#endif NM_UTILS_LOOKUP_DEFAULT (NULL), - NM_UTILS_LOOKUP_STR_ITEM (NME_UNSPEC, "NME_UNSPEC"), - NM_UTILS_LOOKUP_STR_ITEM (NME_BUG, "NME_BUG"), - NM_UTILS_LOOKUP_STR_ITEM (NME_NATIVE_ERRNO, "NME_NATIVE_ERRNO"), - - NM_UTILS_LOOKUP_STR_ITEM (NME_NL_ATTRSIZE, "NME_NL_ATTRSIZE"), - NM_UTILS_LOOKUP_STR_ITEM (NME_NL_BAD_SOCK, "NME_NL_BAD_SOCK"), - NM_UTILS_LOOKUP_STR_ITEM (NME_NL_DUMP_INTR, "NME_NL_DUMP_INTR"), - NM_UTILS_LOOKUP_STR_ITEM (NME_NL_MSG_OVERFLOW, "NME_NL_MSG_OVERFLOW"), - NM_UTILS_LOOKUP_STR_ITEM (NME_NL_MSG_TOOSHORT, "NME_NL_MSG_TOOSHORT"), - NM_UTILS_LOOKUP_STR_ITEM (NME_NL_MSG_TRUNC, "NME_NL_MSG_TRUNC"), - NM_UTILS_LOOKUP_STR_ITEM (NME_NL_SEQ_MISMATCH, "NME_NL_SEQ_MISMATCH"), - - NM_UTILS_LOOKUP_STR_ITEM (NME_PL_NOT_FOUND, "not-found"), - NM_UTILS_LOOKUP_STR_ITEM (NME_PL_EXISTS, "exists"), - NM_UTILS_LOOKUP_STR_ITEM (NME_PL_WRONG_TYPE, "wrong-type"), - NM_UTILS_LOOKUP_STR_ITEM (NME_PL_NOT_SLAVE, "not-slave"), - NM_UTILS_LOOKUP_STR_ITEM (NME_PL_NO_FIRMWARE, "no-firmware"), - NM_UTILS_LOOKUP_STR_ITEM (NME_PL_OPNOTSUPP, "not-supported"), - NM_UTILS_LOOKUP_STR_ITEM (NME_PL_NETLINK, "netlink"), - NM_UTILS_LOOKUP_STR_ITEM (NME_PL_CANT_SET_MTU, "cant-set-mtu"), + NM_UTILS_LOOKUP_STR_ITEM (NME_ERRNO_SUCCESS, "NME_ERRNO_SUCCESS"), + NM_UTILS_LOOKUP_STR_ITEM (NME_ERRNO_OUT_OF_RANGE, "NME_ERRNO_OUT_OF_RANGE"), + + NM_UTILS_LOOKUP_STR_ITEM (NME_UNSPEC, "NME_UNSPEC"), + NM_UTILS_LOOKUP_STR_ITEM (NME_BUG, "NME_BUG"), + NM_UTILS_LOOKUP_STR_ITEM (NME_NATIVE_ERRNO, "NME_NATIVE_ERRNO"), + + NM_UTILS_LOOKUP_STR_ITEM (NME_NL_ATTRSIZE, "NME_NL_ATTRSIZE"), + NM_UTILS_LOOKUP_STR_ITEM (NME_NL_BAD_SOCK, "NME_NL_BAD_SOCK"), + NM_UTILS_LOOKUP_STR_ITEM (NME_NL_DUMP_INTR, "NME_NL_DUMP_INTR"), + NM_UTILS_LOOKUP_STR_ITEM (NME_NL_MSG_OVERFLOW, "NME_NL_MSG_OVERFLOW"), + NM_UTILS_LOOKUP_STR_ITEM (NME_NL_MSG_TOOSHORT, "NME_NL_MSG_TOOSHORT"), + NM_UTILS_LOOKUP_STR_ITEM (NME_NL_MSG_TRUNC, "NME_NL_MSG_TRUNC"), + NM_UTILS_LOOKUP_STR_ITEM (NME_NL_SEQ_MISMATCH, "NME_NL_SEQ_MISMATCH"), + NM_UTILS_LOOKUP_STR_ITEM (NME_NL_NOADDR, "NME_NL_NOADDR"), + + NM_UTILS_LOOKUP_STR_ITEM (NME_PL_NOT_FOUND, "not-found"), + NM_UTILS_LOOKUP_STR_ITEM (NME_PL_EXISTS, "exists"), + NM_UTILS_LOOKUP_STR_ITEM (NME_PL_WRONG_TYPE, "wrong-type"), + NM_UTILS_LOOKUP_STR_ITEM (NME_PL_NOT_SLAVE, "not-slave"), + NM_UTILS_LOOKUP_STR_ITEM (NME_PL_NO_FIRMWARE, "no-firmware"), + NM_UTILS_LOOKUP_STR_ITEM (NME_PL_OPNOTSUPP, "not-supported"), + NM_UTILS_LOOKUP_STR_ITEM (NME_PL_NETLINK, "netlink"), + NM_UTILS_LOOKUP_STR_ITEM (NME_PL_CANT_SET_MTU, "cant-set-mtu"), + + NM_UTILS_LOOKUP_ITEM_IGNORE (_NM_ERRNO_MININT), + NM_UTILS_LOOKUP_ITEM_IGNORE (_NM_ERRNO_RESERVED_LAST_PLUS_1), ); +/** + * nm_strerror(): + * @nmerr: the NetworkManager specific errno to be converted + * to string. + * + * NetworkManager specific error numbers reserve a range in "errno.h" with + * our own defines. For numbers that don't fall into this range, the numbers + * are identical to the common error numbers. + * + * Idential to strerror(), g_strerror(), nm_strerror_native() for error numbers + * that are not in the reserved range of NetworkManager specific errors. + * + * Returns: (transfer none): the string representation of the error number. + */ const char * nm_strerror (int nmerr) { @@ -61,5 +89,110 @@ nm_strerror (int nmerr) if (s) return s; } - return g_strerror (nmerr); + return nm_strerror_native (nmerr); +} + +/*****************************************************************************/ + +/** + * nm_strerror_native_r: + * @errsv: the errno to convert to string. + * @buf: the output buffer where to write the string to. + * @buf_size: the length of buffer. + * + * This is like strerror_r(), with one difference: depending on the + * locale, the returned string is guaranteed to be valid UTF-8. + * Also, there is some confusion as to whether to use glibc's + * strerror_r() or the POXIX/XSI variant. This is abstracted + * by the function. + * + * Note that the returned buffer may also be a statically allocated + * buffer, and not the input buffer @buf. Consequently, the returned + * string may be longer than @buf_size. + * + * Returns: (transfer none): a NUL terminated error message. This is either a static + * string (that is never freed), or the provided @buf argumnt. + */ +const char * +nm_strerror_native_r (int errsv, char *buf, gsize buf_size) +{ + char *buf2; + + nm_assert (buf); + nm_assert (buf_size > 0); + +#if (_POSIX_C_SOURCE >= 200112L) && ! _GNU_SOURCE + /* XSI-compliant */ + { + int errno_saved = errno; + + if (strerror_r (errsv, buf, buf_size) != 0) { + g_snprintf (buf, buf_size, "Unspecified errno %d", errsv); + errno = errno_saved; + } + buf2 = buf; + } +#else + /* GNU-specific */ + buf2 = strerror_r (errsv, buf, buf_size); +#endif + + /* like g_strerror(), ensure that the error message is UTF-8. */ + if ( !g_get_charset (NULL) + && !g_utf8_validate (buf2, -1, NULL)) { + gs_free char *msg = NULL; + + msg = g_locale_to_utf8 (buf2, -1, NULL, NULL, NULL); + if (msg) { + g_strlcpy (buf, msg, buf_size); + buf2 = buf; + } + } + + return buf2; +} + +/** + * nm_strerror_native: + * @errsv: the errno integer from <errno.h> + * + * Like strerror(), but strerror() is not thread-safe and not guaranteed + * to be UTF-8. + * + * g_strerror() is a thread-safe variant of strerror(), however it caches + * all returned strings in a dictionary. That means, using this on untrusted + * error numbers can result in this cache to grow without limits. + * + * Instead, return a tread-local buffer. This way, it's thread-safe. + * + * There is a downside to this: subsequent calls of nm_strerror_native() + * overwrite the error message. + * + * Returns: (transfer none): the text representation of the error number. + */ +const char * +nm_strerror_native (int errsv) +{ + static _nm_thread_local char *buf_static = NULL; + char *buf; + + buf = buf_static; + if (G_UNLIKELY (!buf)) { + int errno_saved = errno; + pthread_key_t key; + + buf = g_malloc (NM_STRERROR_BUFSIZE); + buf_static = buf; + + if ( pthread_key_create (&key, g_free) != 0 + || pthread_setspecific (key, buf) != 0) { + /* Failure. We will leak the buffer when the thread exits. + * + * Nothing we can do about it really. For Debug builds we fail with an assertion. */ + nm_assert_not_reached (); + } + errno = errno_saved; + } + + return nm_strerror_native_r (errsv, buf, NM_STRERROR_BUFSIZE); } diff --git a/shared/nm-utils/nm-errno.h b/shared/nm-utils/nm-errno.h index c3008f1f..d77735a7 100644 --- a/shared/nm-utils/nm-errno.h +++ b/shared/nm-utils/nm-errno.h @@ -25,11 +25,23 @@ /*****************************************************************************/ -enum { +enum _NMErrno { _NM_ERRNO_MININT = G_MININT, _NM_ERRNO_MAXINT = G_MAXINT, _NM_ERRNO_RESERVED_FIRST = 100000, + + /* when we cannot represent a number as positive number, we resort to this + * number. Basically, the values G_MININT, -NME_ERRNO_SUCCESS, NME_ERRNO_SUCCESS + * and G_MAXINT all map to the same value. */ + NME_ERRNO_OUT_OF_RANGE = G_MAXINT, + + /* Indicate that the original errno was zero. Zero denotes *no error*, but we know something + * went wrong and we want to report some error. This is a placeholder to mean, something + * was wrong, but errno was zero. */ + NME_ERRNO_SUCCESS = G_MAXINT - 1, + + /* an unspecified error. */ NME_UNSPEC = _NM_ERRNO_RESERVED_FIRST, @@ -68,68 +80,106 @@ enum { /*****************************************************************************/ +/* When we receive an errno from a system function, we can safely assume + * that the error number is not negative. We rely on that, and possibly just + * "return -errsv;" to signal an error. We also rely on that, because libc + * is our trusted base: meaning, if it cannot even succeed at setting errno + * according to specification, all bets are off. + * + * This macro returns the input argument, and asserts that the error variable + * is positive. + * + * In a sense, the macro is related to nm_errno_native() function, but the difference + * is that this macro asserts that @errsv is positive, while nm_errno_native() coerces + * negative values to be non-negative. */ +#define NM_ERRNO_NATIVE(errsv) \ + ({ \ + const int _errsv_x = (errsv); \ + \ + nm_assert (_errsv_x > 0); \ + _errsv_x; \ + }) + +/* Normalize native errno. + * + * Our API may return native error codes (<errno.h>) as negative values. This function + * takes such an errno, and normalizes it to their positive value. + * + * The special values G_MININT and zero are coerced to NME_ERRNO_OUT_OF_RANGE and NME_ERRNO_SUCCESS + * respectively. + * Other values are coerced to their inverse. + * Other positive values are returned unchanged. + * + * Basically, this normalizes errsv to be positive (taking care of two pathological cases). + */ static inline int nm_errno_native (int errsv) { - /* several API returns negative errno values as errors. Normalize - * negative values to positive values. - * - * As a special case, map G_MININT to G_MAXINT. If you care about the - * distinction, then check for G_MININT before. - * - * Basically, this normalizes a plain errno to be non-negative. */ - return errsv >= 0 - ? errsv - : ((errsv == G_MININT) ? G_MAXINT : -errsv); + switch (errsv) { + case 0: return NME_ERRNO_SUCCESS; + case G_MININT: return NME_ERRNO_OUT_OF_RANGE; + default: + return errsv >= 0 ? errsv : -errsv; + } } +/* Normalizes an nm-error to be positive. + * + * Various API returns negative error codes, and this function converts the negative + * value to its positive. + * + * Note that @nmerr is on the domain of NetworkManager specific error numbers, + * which is not the same as the native error numbers (errsv from <errno.h>). But + * as far as normalizing goes, nm_errno() does exactly the same remapping as + * nm_errno_native(). */ static inline int nm_errno (int nmerr) { - /* Normalizes an nm-error to be positive. Various API returns negative - * error codes, and this function converts the negative value to its - * positive. - * - * It's very similar to nm_errno_native(), but not exactly. The difference is that - * nm_errno_native() is for plain errno, while nm_errno() is for nm-error numbers. - * Yes, nm-error number are ~almost~ the same as errno, except that a particular - * range (_NM_ERRNO_RESERVED_FIRST, _NM_ERRNO_RESERVED_LAST) is reserved. The difference - * between the two functions is only how G_MININT is mapped. - * - * See also nm_errno_from_native() below. */ - return nmerr >= 0 - ? nmerr - : ((nmerr == G_MININT) ? NME_BUG : -nmerr); + return nm_errno_native (nmerr); } +/* this maps a native errno to a (always non-negative) nm-error number. + * + * Note that nm-error numbers are embedded into the range of regular + * errno. The only difference is, that nm-error numbers reserve a + * range (_NM_ERRNO_RESERVED_FIRST, _NM_ERRNO_RESERVED_LAST) for their + * own purpose. + * + * That means, converting an errno to nm-error number means in + * most cases just returning itself. + * Only pathological cases need special handling: + * + * - 0 is mapped to NME_ERRNO_SUCCESS; + * - G_MININT is mapped to NME_ERRNO_OUT_OF_RANGE; + * - values in the range of (+/-) [_NM_ERRNO_RESERVED_FIRST, _NM_ERRNO_RESERVED_LAST] + * are mapped to NME_NATIVE_ERRNO + * - all other values are their (positive) absolute value. + */ static inline int nm_errno_from_native (int errsv) { - /* this maps a native errno to a (always non-negative) nm-error number. - * - * Note that nm-error numbers are embedded into the range of regular - * errno. The only difference is, that nm-error numbers reserve a - * range (_NM_ERRNO_RESERVED_FIRST, _NM_ERRNO_RESERVED_LAST) for their - * own purpose. - * - * That means, converting an errno to nm-error number means in - * most cases just returning itself (negative values are normalized - * to be positive). Only values G_MININT and [_NM_ERRNO_RESERVED_FIRST, _NM_ERRNO_RESERVED_LAST] - * are coerced to the special value NME_NATIVE_ERRNO, as they cannot - * otherwise be represented in nm-error number domain. */ - if (errsv < 0) { - return G_UNLIKELY (errsv == G_MININT) + switch (errsv) { + case 0: return NME_ERRNO_SUCCESS; + case G_MININT: return NME_ERRNO_OUT_OF_RANGE; + default: + if (errsv < 0) + errsv = -errsv; + return G_UNLIKELY ( errsv >= _NM_ERRNO_RESERVED_FIRST + && errsv <= _NM_ERRNO_RESERVED_LAST) ? NME_NATIVE_ERRNO - : -errsv; + : errsv; } - return G_UNLIKELY ( errsv >= _NM_ERRNO_RESERVED_FIRST - && errsv <= _NM_ERRNO_RESERVED_LAST) - ? NME_NATIVE_ERRNO - : errsv; } const char *nm_strerror (int nmerr); /*****************************************************************************/ +#define NM_STRERROR_BUFSIZE 1024 + +const char *nm_strerror_native_r (int errsv, char *buf, gsize buf_size); +const char *nm_strerror_native (int errsv); + +/*****************************************************************************/ + #endif /* __NM_ERRNO_H__ */ diff --git a/shared/nm-utils/nm-glib.h b/shared/nm-utils/nm-glib.h index b7534edd..e941e067 100644 --- a/shared/nm-utils/nm-glib.h +++ b/shared/nm-utils/nm-glib.h @@ -424,11 +424,13 @@ g_steal_pointer (gpointer pp) return ref; } +#endif -/* type safety */ -#define g_steal_pointer(pp) \ - (0 ? (*(pp)) : (g_steal_pointer) (pp)) +#ifdef g_steal_pointer +#undef g_steal_pointer #endif +#define g_steal_pointer(pp) \ + ((typeof (*(pp))) g_steal_pointer (pp)) /*****************************************************************************/ diff --git a/shared/nm-utils/nm-hash-utils.c b/shared/nm-utils/nm-hash-utils.c index 80387c71..6e728e6b 100644 --- a/shared/nm-utils/nm-hash-utils.c +++ b/shared/nm-utils/nm-hash-utils.c @@ -71,7 +71,7 @@ again: * the first guint has only the entropy that nm_utils_random_bytes() * generated for the first 4 bytes and relies on a good random generator. * - * The first int is especially intersting for nm_hash_static() below, and we + * The first int is especially interesting for nm_hash_static() below, and we * want to have it all the entropy of t_arr. */ c_siphash_init (&siph_state, t_arr.v8); c_siphash_append (&siph_state, (const guint8 *) &t_arr, sizeof (t_arr)); diff --git a/shared/nm-utils/nm-hash-utils.h b/shared/nm-utils/nm-hash-utils.h index cf71a7e9..1a1e44f5 100644 --- a/shared/nm-utils/nm-hash-utils.h +++ b/shared/nm-utils/nm-hash-utils.h @@ -122,6 +122,9 @@ nm_hash_update (NMHashState *state, const void *ptr, gsize n) nm_hash_update ((state), &_val, sizeof (_val)); \ } G_STMT_END +#define nm_hash_update_valp(state, val) \ + nm_hash_update ((state), (val), sizeof (*(val))) \ + static inline void nm_hash_update_bool (NMHashState *state, bool val) { diff --git a/shared/nm-utils/nm-io-utils.c b/shared/nm-utils/nm-io-utils.c index 06f756c4..51312748 100644 --- a/shared/nm-utils/nm-io-utils.c +++ b/shared/nm-utils/nm-io-utils.c @@ -29,6 +29,7 @@ #include "nm-shared-utils.h" #include "nm-secret-utils.h" +#include "nm-errno.h" /*****************************************************************************/ @@ -36,14 +37,12 @@ _nm_printf (3, 4) static int _get_contents_error (GError **error, int errsv, const char *format, ...) { - if (errsv < 0) - errsv = -errsv; - else if (!errsv) - errsv = errno; + nm_assert (NM_ERRNO_NATIVE (errsv)); if (error) { - char *msg; + gs_free char *msg = NULL; va_list args; + char bstrerr[NM_STRERROR_BUFSIZE]; va_start (args, format); msg = g_strdup_vprintf (format, args); @@ -52,11 +51,17 @@ _get_contents_error (GError **error, int errsv, const char *format, ...) G_FILE_ERROR, g_file_error_from_errno (errsv), "%s: %s", - msg, g_strerror (errsv)); - g_free (msg); + msg, + nm_strerror_native_r (errsv, bstrerr, sizeof (bstrerr))); } return -errsv; } +#define _get_contents_error_errno(error, ...) \ + ({ \ + int _errsv = (errno); \ + \ + _get_contents_error (error, _errsv, __VA_ARGS__); \ + }) static char * _mem_realloc (char *old, gboolean do_bzero_mem, gsize cur_len, gsize new_len) @@ -127,13 +132,14 @@ nm_utils_fd_get_contents (int fd, struct stat stat_buf; gs_free char *str = NULL; const bool do_bzero_mem = NM_FLAGS_HAS (flags, NM_UTILS_FILE_GET_CONTENTS_FLAG_SECRET); + int errsv; g_return_val_if_fail (fd >= 0, -EINVAL); g_return_val_if_fail (contents, -EINVAL); g_return_val_if_fail (!error || !*error, -EINVAL); if (fstat (fd, &stat_buf) < 0) - return _get_contents_error (error, 0, "failure during fstat"); + return _get_contents_error_errno (error, "failure during fstat"); if (!max_length) { /* default to a very large size, but not extreme */ @@ -156,7 +162,7 @@ nm_utils_fd_get_contents (int fd, if (n_read < 0) { if (do_bzero_mem) nm_explicit_bzero (str, n_stat); - return _get_contents_error (error, n_read, "error reading %zu bytes from file descriptor", n_stat); + return _get_contents_error (error, -n_read, "error reading %zu bytes from file descriptor", n_stat); } str[n_read] = '\0'; @@ -176,19 +182,19 @@ nm_utils_fd_get_contents (int fd, else { fd2 = fcntl (fd, F_DUPFD_CLOEXEC, 0); if (fd2 < 0) - return _get_contents_error (error, 0, "error during dup"); + return _get_contents_error_errno (error, "error during dup"); } if (!(f = fdopen (fd2, "r"))) { + errsv = errno; nm_close (fd2); - return _get_contents_error (error, 0, "failure during fdopen"); + return _get_contents_error (error, errsv, "failure during fdopen"); } n_have = 0; n_alloc = 0; while (!feof (f)) { - int errsv; gsize n_read; n_read = fread (buf, 1, sizeof (buf), f); @@ -262,8 +268,8 @@ nm_utils_fd_get_contents (int fd, * @flags: %NMUtilsFileGetContentsFlags for reading the file. * @contents: the output buffer with the file read. It is always * NUL terminated. The buffer is at most @max_length long, including - * the NUL byte. That is, it reads only files up to a length of - * @max_length - 1 bytes. + * the NUL byte. That is, it reads only files up to a length of + * @max_length - 1 bytes. * @length: optional output argument of the read file size. * * A reimplementation of g_file_get_contents() with a few differences: @@ -284,6 +290,7 @@ nm_utils_file_get_contents (int dirfd, { int fd; int errsv; + char bstrerr[NM_STRERROR_BUFSIZE]; g_return_val_if_fail (filename && filename[0], -EINVAL); @@ -297,8 +304,8 @@ nm_utils_file_get_contents (int dirfd, g_file_error_from_errno (errsv), "Failed to open file \"%s\" with openat: %s", filename, - g_strerror (errsv)); - return -errsv; + nm_strerror_native_r (errsv, bstrerr, sizeof (bstrerr))); + return -NM_ERRNO_NATIVE (errsv); } } else { fd = open (filename, O_RDONLY | O_CLOEXEC); @@ -310,8 +317,8 @@ nm_utils_file_get_contents (int dirfd, g_file_error_from_errno (errsv), "Failed to open file \"%s\": %s", filename, - g_strerror (errsv)); - return -errsv; + nm_strerror_native_r (errsv, bstrerr, sizeof (bstrerr))); + return -NM_ERRNO_NATIVE (errsv); } } return nm_utils_fd_get_contents (fd, @@ -341,6 +348,7 @@ nm_utils_file_set_contents (const char *filename, int errsv; gssize s; int fd; + char bstrerr[NM_STRERROR_BUFSIZE]; g_return_val_if_fail (filename, FALSE); g_return_val_if_fail (contents || !length, FALSE); @@ -359,7 +367,7 @@ nm_utils_file_set_contents (const char *filename, g_file_error_from_errno (errsv), "failed to create file %s: %s", tmp_name, - g_strerror (errsv)); + nm_strerror_native_r (errsv, bstrerr, sizeof (bstrerr))); return FALSE; } @@ -378,7 +386,7 @@ nm_utils_file_set_contents (const char *filename, g_file_error_from_errno (errsv), "failed to write to file %s: %s", tmp_name, - g_strerror (errsv)); + nm_strerror_native_r (errsv, bstrerr, sizeof (bstrerr))); return FALSE; } @@ -395,20 +403,21 @@ nm_utils_file_set_contents (const char *filename, * guarantee the data is written to the disk before the metadata.) */ if ( lstat (filename, &statbuf) == 0 - && statbuf.st_size > 0 - && fsync (fd) != 0) { - errsv = errno; + && statbuf.st_size > 0) { + if (fsync (fd) != 0) { + errsv = errno; - nm_close (fd); - unlink (tmp_name); + nm_close (fd); + unlink (tmp_name); - g_set_error (error, - G_FILE_ERROR, - g_file_error_from_errno (errsv), - "failed to fsync %s: %s", - tmp_name, - g_strerror (errsv)); - return FALSE; + g_set_error (error, + G_FILE_ERROR, + g_file_error_from_errno (errsv), + "failed to fsync %s: %s", + tmp_name, + nm_strerror_native_r (errsv, bstrerr, sizeof (bstrerr))); + return FALSE; + } } nm_close (fd); @@ -422,7 +431,7 @@ nm_utils_file_set_contents (const char *filename, "failed to rename %s to %s: %s", tmp_name, filename, - g_strerror (errsv)); + nm_strerror_native_r (errsv, bstrerr, sizeof (bstrerr))); return FALSE; } diff --git a/shared/nm-utils/nm-jansson.h b/shared/nm-utils/nm-jansson.h index b00c75c6..5a73231f 100644 --- a/shared/nm-utils/nm-jansson.h +++ b/shared/nm-utils/nm-jansson.h @@ -34,13 +34,16 @@ /* Added in Jansson v2.8 */ #ifndef json_object_foreach_safe #define json_object_foreach_safe(object, n, key, value) \ - for(key = json_object_iter_key(json_object_iter(object)), \ - n = json_object_iter_next(object, json_object_key_to_iter(key)); \ - key && (value = json_object_iter_value(json_object_key_to_iter(key))); \ - key = json_object_iter_key(n), \ - n = json_object_iter_next(object, json_object_key_to_iter(key))) + for (key = json_object_iter_key(json_object_iter(object)), \ + n = json_object_iter_next(object, json_object_key_to_iter(key)); \ + key && (value = json_object_iter_value(json_object_key_to_iter(key))); \ + key = json_object_iter_key(n), \ + n = json_object_iter_next(object, json_object_key_to_iter(key))) #endif +NM_AUTO_DEFINE_FCN0 (json_t *, _nm_auto_decref_json, json_decref) +#define nm_auto_decref_json nm_auto(_nm_auto_decref_json) + #endif /* WITH_JANSON */ #endif /* __NM_JANSSON_H__ */ diff --git a/shared/nm-utils/nm-logging-fwd.h b/shared/nm-utils/nm-logging-fwd.h index 303d5951..900dfff8 100644 --- a/shared/nm-utils/nm-logging-fwd.h +++ b/shared/nm-utils/nm-logging-fwd.h @@ -94,18 +94,20 @@ typedef enum { /*< skip >*/ _LOGL_N, /* the number of logging levels including "OFF" */ } NMLogLevel; -gboolean _nm_log_enabled (NMLogLevel level, - NMLogDomain domain); +gboolean _nm_log_enabled_impl (gboolean mt_require_locking, + NMLogLevel level, + NMLogDomain domain); void _nm_log_impl (const char *file, guint line, const char *func, + gboolean mt_require_locking, NMLogLevel level, NMLogDomain domain, int error, const char *ifname, const char *con_uuid, const char *fmt, - ...) _nm_printf (9, 10); + ...) _nm_printf (10, 11); #endif /* __NM_LOGGING_DEFINES_H__ */ diff --git a/shared/nm-utils/nm-macros-internal.h b/shared/nm-utils/nm-macros-internal.h index 1f3970fe..42299c96 100644 --- a/shared/nm-utils/nm-macros-internal.h +++ b/shared/nm-utils/nm-macros-internal.h @@ -45,6 +45,18 @@ #define nm_auto(fcn) __attribute__ ((__cleanup__(fcn))) +/* This is required to make LTO working. + * + * See https://gitlab.freedesktop.org/NetworkManager/NetworkManager/merge_requests/76#note_112694 + * https://gcc.gnu.org/bugzilla/show_bug.cgi?id=48200#c28 + */ +#ifndef __clang__ +#define _nm_externally_visible __attribute__ ((__externally_visible__)) +#else +#define _nm_externally_visible +#endif + + #if __GNUC__ >= 7 #define _nm_fallthrough __attribute__ ((__fallthrough__)) #else @@ -67,6 +79,28 @@ /*****************************************************************************/ +/* most of our code is single-threaded with a mainloop. Hence, we usually don't need + * any thread-safety. Sometimes, we do need thread-safety (nm-logging), but we can + * avoid locking if we are on the main-thread by: + * + * - modifications of shared data is done infrequently and only from the + * main-thread (nm_logging_setup()) + * - read-only access is done frequently (nm_logging_enabled()) + * - from the main-thread, we can do that without locking (because + * all modifications are also done on the main thread. + * - from other threads, we need locking. But this is expected to be + * done infrequently too. Important is the lock-free fast-path on the + * main-thread. + * + * By defining NM_THREAD_SAFE_ON_MAIN_THREAD you indicate that this code runs + * on the main-thread. It is by default defined to "1". If you have code that + * is also used on another thread, redefine the define to 0 (to opt in into + * the slow-path). + */ +#define NM_THREAD_SAFE_ON_MAIN_THREAD 1 + +/*****************************************************************************/ + #define NM_AUTO_DEFINE_FCN_VOID(CastType, name, func) \ static inline void name (void *v) \ { \ @@ -415,7 +449,7 @@ NM_G_ERROR_MSG (GError *error) /*****************************************************************************/ /* macro to return strlen() of a compile time string. */ -#define NM_STRLEN(str) ( sizeof ("" str) - 1 ) +#define NM_STRLEN(str) ( sizeof (""str"") - 1 ) /* returns the length of a NULL terminated array of pointers, * like g_strv_length() does. The difference is: @@ -826,12 +860,33 @@ fcn (void) \ /*****************************************************************************/ -#define nm_streq(s1, s2) (strcmp (s1, s2) == 0) -#define nm_streq0(s1, s2) (g_strcmp0 (s1, s2) == 0) +static inline gboolean +nm_streq (const char *s1, const char *s2) +{ + return strcmp (s1, s2) == 0; +} + +static inline gboolean +nm_streq0 (const char *s1, const char *s2) +{ + return (s1 == s2) + || (s1 && s2 && strcmp (s1, s2) == 0); +} #define NM_STR_HAS_PREFIX(str, prefix) \ (strncmp ((str), ""prefix"", NM_STRLEN (prefix)) == 0) +#define NM_STR_HAS_SUFFIX(str, suffix) \ + ({ \ + const char *_str = (str); \ + gsize _l = strlen (_str); \ + \ + ( (_l >= NM_STRLEN (suffix)) \ + && (memcmp (&_str[_l - NM_STRLEN (suffix)], \ + ""suffix"", \ + NM_STRLEN (suffix)) == 0)); \ + }) + /*****************************************************************************/ static inline GString * @@ -1127,6 +1182,28 @@ nm_clear_g_cancellable (GCancellable **cancellable) return FALSE; } +/* If @cancellable_id is not 0, clear it and call g_cancellable_disconnect(). + * @cancellable may be %NULL, if there is nothing to disconnect. + * + * It's like nm_clear_g_signal_handler(), except that it uses g_cancellable_disconnect() + * instead of g_signal_handler_disconnect(). + * + * Note the warning in glib documentation about dead-lock and what g_cancellable_disconnect() + * actually does. */ +static inline gboolean +nm_clear_g_cancellable_disconnect (GCancellable *cancellable, gulong *cancellable_id) +{ + gulong id; + + if ( cancellable_id + && (id = *cancellable_id) != 0) { + *cancellable_id = 0; + g_cancellable_disconnect (cancellable, id); + return TRUE; + } + return FALSE; +} + /*****************************************************************************/ static inline GVariant * @@ -1243,17 +1320,17 @@ fcn_name (lookup_type val) \ /*****************************************************************************/ -#define _NM_BACKPORT_SYMBOL_IMPL(VERSION, RETURN_TYPE, ORIG_FUNC, VERSIONED_FUNC, ARGS_TYPED, ARGS) \ -RETURN_TYPE VERSIONED_FUNC ARGS_TYPED; \ -RETURN_TYPE VERSIONED_FUNC ARGS_TYPED \ +#define _NM_BACKPORT_SYMBOL_IMPL(version, return_type, orig_func, versioned_func, args_typed, args) \ +return_type versioned_func args_typed; \ +_nm_externally_visible return_type versioned_func args_typed \ { \ - return ORIG_FUNC ARGS; \ + return orig_func args; \ } \ -RETURN_TYPE ORIG_FUNC ARGS_TYPED; \ -__asm__(".symver "G_STRINGIFY(VERSIONED_FUNC)", "G_STRINGIFY(ORIG_FUNC)"@"G_STRINGIFY(VERSION)) +return_type orig_func args_typed; \ +__asm__(".symver "G_STRINGIFY(versioned_func)", "G_STRINGIFY(orig_func)"@"G_STRINGIFY(version)) -#define NM_BACKPORT_SYMBOL(VERSION, RETURN_TYPE, FUNC, ARGS_TYPED, ARGS) \ -_NM_BACKPORT_SYMBOL_IMPL(VERSION, RETURN_TYPE, FUNC, _##FUNC##_##VERSION, ARGS_TYPED, ARGS) +#define NM_BACKPORT_SYMBOL(version, return_type, func, args_typed, args) \ +_NM_BACKPORT_SYMBOL_IMPL(version, return_type, func, _##func##_##version, args_typed, args) /*****************************************************************************/ @@ -1371,6 +1448,14 @@ nm_strcmp_p (gconstpointer a, gconstpointer b) : _b); \ }) +/* evaluates to (void) if _A or _B are not constant or of different types */ +#define NM_CONST_MAX(_A, _B) \ + (__builtin_choose_expr (( __builtin_constant_p (_A) \ + && __builtin_constant_p (_B) \ + && __builtin_types_compatible_p (typeof (_A), typeof (_B))), \ + ((_A) > (_B)) ? (_A) : (_B), \ + ((void) 0))) + /*****************************************************************************/ static inline guint diff --git a/shared/nm-utils/nm-secret-utils.c b/shared/nm-utils/nm-secret-utils.c index 65f99c65..ec5cc6b1 100644 --- a/shared/nm-utils/nm-secret-utils.c +++ b/shared/nm-utils/nm-secret-utils.c @@ -17,6 +17,7 @@ * Boston, MA 02110-1301 USA. * * (C) Copyright 2018 Red Hat, Inc. + * (C) Copyright 2015 - 2019 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved. */ #include "nm-default.h" @@ -132,3 +133,29 @@ nm_secret_buf_to_gbytes_take (NMSecretBuf *secret, gssize actual_len) _secret_buf_free, secret); } + +/*****************************************************************************/ + +/** + * nm_utils_memeqzero_secret: + * @data: the data pointer to check (may be %NULL if @length is zero). + * @length: the number of bytes to check. + * + * Checks that all bytes are zero. This always takes the same amount + * of time to prevent timing attacks. + * + * Returns: whether all bytes are zero. + */ +gboolean +nm_utils_memeqzero_secret (gconstpointer data, gsize length) +{ + const guint8 *const key = data; + volatile guint8 acc = 0; + gsize i; + + for (i = 0; i < length; i++) { + acc |= key[i]; + asm volatile("" : "=r"(acc) : "0"(acc)); + } + return 1 & ((acc - 1) >> 8); +} diff --git a/shared/nm-utils/nm-secret-utils.h b/shared/nm-utils/nm-secret-utils.h index 9df31afe..034ef7bd 100644 --- a/shared/nm-utils/nm-secret-utils.h +++ b/shared/nm-utils/nm-secret-utils.h @@ -76,6 +76,19 @@ typedef struct { } NMSecretPtr; static inline void +nm_secret_ptr_bzero (NMSecretPtr *secret) +{ + if (secret) { + if (secret->len > 0) { + if (secret->ptr) + nm_explicit_bzero (secret->ptr, secret->len); + } + } +} + +#define nm_auto_bzero_secret_ptr nm_auto(nm_secret_ptr_bzero) + +static inline void nm_secret_ptr_clear (NMSecretPtr *secret) { if (secret) { @@ -90,12 +103,24 @@ nm_secret_ptr_clear (NMSecretPtr *secret) #define nm_auto_clear_secret_ptr nm_auto(nm_secret_ptr_clear) +#define NM_SECRET_PTR_INIT() \ + ((const NMSecretPtr) { \ + .len = 0, \ + .ptr = NULL, \ + }) + #define NM_SECRET_PTR_STATIC(_len) \ ((const NMSecretPtr) { \ .len = _len, \ .ptr = ((guint8 [_len]) { }), \ }) +#define NM_SECRET_PTR_ARRAY(_arr) \ + ((const NMSecretPtr) { \ + .len = G_N_ELEMENTS (_arr) * sizeof ((_arr)[0]), \ + .ptr = &((_arr)[0]), \ + }) + static inline void nm_secret_ptr_clear_static (const NMSecretPtr *secret) { @@ -148,4 +173,6 @@ GBytes *nm_secret_buf_to_gbytes_take (NMSecretBuf *secret, gssize actual_len); /*****************************************************************************/ +gboolean nm_utils_memeqzero_secret (gconstpointer data, gsize length); + #endif /* __NM_SECRET_UTILS_H__ */ diff --git a/shared/nm-utils/nm-shared-utils.c b/shared/nm-utils/nm-shared-utils.c index c9ef89e9..6a43c670 100644 --- a/shared/nm-utils/nm-shared-utils.c +++ b/shared/nm-utils/nm-shared-utils.c @@ -23,10 +23,12 @@ #include "nm-shared-utils.h" -#include <errno.h> #include <arpa/inet.h> #include <poll.h> #include <fcntl.h> +#include <sys/syscall.h> + +#include "nm-errno.h" /*****************************************************************************/ @@ -34,7 +36,116 @@ const void *const _NM_PTRARRAY_EMPTY[1] = { NULL }; /*****************************************************************************/ -const NMIPAddr nm_ip_addr_zero = { 0 }; +const NMIPAddr nm_ip_addr_zero = { }; + +/* this initializes a struct in_addr/in6_addr and allows for untrusted + * arguments (like unsuitable @addr_family or @src_len). It's almost safe + * in the sense that it verifies input arguments strictly. Also, it + * uses memcpy() to access @src, so alignment is not an issue. + * + * Only potential pitfalls: + * + * - it allows for @addr_family to be AF_UNSPEC. If that is the case (and the + * caller allows for that), the caller MUST provide @out_addr_family. + * - when setting @dst to an IPv4 address, the trailing bytes are not touched. + * Meaning, if @dst is an NMIPAddr union, only the first bytes will be set. + * If that matter to you, clear @dst before. */ +gboolean +nm_ip_addr_set_from_untrusted (int addr_family, + gpointer dst, + gconstpointer src, + gsize src_len, + int *out_addr_family) +{ + nm_assert (dst); + + switch (addr_family) { + case AF_UNSPEC: + if (!out_addr_family) { + /* when the callers allow undefined @addr_family, they must provide + * an @out_addr_family argument. */ + nm_assert_not_reached (); + return FALSE; + } + switch (src_len) { + case sizeof (struct in_addr): addr_family = AF_INET; break; + case sizeof (struct in6_addr): addr_family = AF_INET6; break; + default: + return FALSE; + } + break; + case AF_INET: + if (src_len != sizeof (struct in_addr)) + return FALSE; + break; + case AF_INET6: + if (src_len != sizeof (struct in6_addr)) + return FALSE; + break; + default: + /* when the callers allow undefined @addr_family, they must provide + * an @out_addr_family argument. */ + nm_assert (out_addr_family); + return FALSE; + } + + nm_assert (src); + + memcpy (dst, src, src_len); + NM_SET_OUT (out_addr_family, addr_family); + return TRUE; +} + +/*****************************************************************************/ + +pid_t +nm_utils_gettid (void) +{ + return (pid_t) syscall (SYS_gettid); +} + +/* Used for asserting that this function is called on the main-thread. + * The main-thread is determined by remembering the thread-id + * of when the function was called the first time. + * + * When forking, the thread-id is again reset upon first call. */ +gboolean +_nm_assert_on_main_thread (void) +{ + G_LOCK_DEFINE_STATIC (lock); + static pid_t seen_tid; + static pid_t seen_pid; + pid_t tid; + pid_t pid; + gboolean success = FALSE; + + tid = nm_utils_gettid (); + nm_assert (tid != 0); + + G_LOCK (lock); + + if (G_LIKELY (tid == seen_tid)) { + /* we don't care about false positives (when the process forked, and the thread-id + * is accidentally re-used) . It's for assertions only. */ + success = TRUE; + } else { + pid = getpid (); + nm_assert (pid != 0); + + if ( seen_tid == 0 + || seen_pid != pid) { + /* either this is the first time we call the function, or the process + * forked. In both cases, remember the thread-id. */ + seen_tid = tid; + seen_pid = pid; + success = TRUE; + } + } + + G_UNLOCK (lock); + + return success; +} /*****************************************************************************/ @@ -561,6 +672,8 @@ nm_utils_parse_inaddr_prefix_bin (int addr_family, return FALSE; if (slash) { + /* For IPv4, `ip addr add` supports the prefix-length as a netmask. We don't + * do that. */ prefix = _nm_utils_ascii_str_to_int64 (slash + 1, 10, 0, addr_family == AF_INET ? 32 : 128, @@ -1360,6 +1473,53 @@ nm_g_object_class_find_property_from_gtype (GType gtype, /*****************************************************************************/ +/** + * nm_g_type_find_implementing_class_for_property: + * @gtype: the GObject type which has a property @pname + * @pname: the name of the property to look up + * + * This is only a helper function for printf debugging. It's not + * used in actual code. Hence, the function just asserts that + * @pname and @gtype arguments are suitable. It cannot fail. + * + * Returns: the most ancestor type of @gtype, that + * implements the property @pname. It means, it + * searches the type hierarchy to find the type + * that added @pname. + */ +GType +nm_g_type_find_implementing_class_for_property (GType gtype, + const char *pname) +{ + nm_auto_unref_gtypeclass GObjectClass *klass = NULL; + GParamSpec *pspec; + + g_return_val_if_fail (pname, G_TYPE_INVALID); + + klass = g_type_class_ref (gtype); + g_return_val_if_fail (G_IS_OBJECT_CLASS (klass), G_TYPE_INVALID); + + pspec = g_object_class_find_property (klass, pname); + g_return_val_if_fail (pspec, G_TYPE_INVALID); + + gtype = G_TYPE_FROM_CLASS (klass); + + while (TRUE) { + nm_auto_unref_gtypeclass GObjectClass *k = NULL; + + k = g_type_class_ref (g_type_parent (gtype)); + + g_return_val_if_fail (G_IS_OBJECT_CLASS (k), G_TYPE_INVALID); + + if (g_object_class_find_property (k, pname) != pspec) + return gtype; + + gtype = G_TYPE_FROM_CLASS (k); + } +} + +/*****************************************************************************/ + static void _str_append_escape (GString *s, char ch) { @@ -1691,7 +1851,7 @@ nm_utils_fd_wait_for_event (int fd, int event, gint64 timeout_ns) r = ppoll (&pollfd, 1, pts, NULL); if (r < 0) - return -errno; + return -NM_ERRNO_NATIVE (errno); if (r == 0) return 0; return pollfd.revents; @@ -1718,10 +1878,12 @@ nm_utils_fd_read_loop (int fd, void *buf, size_t nbytes, bool do_poll) k = read (fd, p, nbytes); if (k < 0) { - if (errno == EINTR) + int errsv = errno; + + if (errsv == EINTR) continue; - if (errno == EAGAIN && do_poll) { + if (errsv == EAGAIN && do_poll) { /* We knowingly ignore any return value here, * and expect that any error/EOF is reported @@ -1731,7 +1893,7 @@ nm_utils_fd_read_loop (int fd, void *buf, size_t nbytes, bool do_poll) continue; } - return n > 0 ? n : -errno; + return n > 0 ? n : -NM_ERRNO_NATIVE (errsv); } if (k == 0) @@ -2375,3 +2537,205 @@ nm_utils_memeqzero (gconstpointer data, gsize length) /* Now we know that's zero, memcmp with self. */ return memcmp (data, p, length) == 0; } + +/** + * nm_utils_bin2hexstr_full: + * @addr: pointer of @length bytes. If @length is zero, this may + * also be %NULL. + * @length: number of bytes in @addr. May also be zero, in which + * case this will return an empty string. + * @delimiter: either '\0', otherwise the output string will have the + * given delimiter character between each two hex numbers. + * @upper_case: if TRUE, use upper case ASCII characters for hex. + * @out: if %NULL, the function will allocate a new buffer of + * either (@length*2+1) or (@length*3) bytes, depending on whether + * a @delimiter is specified. In that case, the allocated buffer will + * be returned and must be freed by the caller. + * If not %NULL, the buffer must already be preallocated and contain + * at least (@length*2+1) or (@length*3) bytes, depending on the delimiter. + * + * Returns: the binary value converted to a hex string. If @out is given, + * this always returns @out. If @out is %NULL, a newly allocated string + * is returned. + */ +char * +nm_utils_bin2hexstr_full (gconstpointer addr, + gsize length, + char delimiter, + gboolean upper_case, + char *out) +{ + const guint8 *in = addr; + const char *LOOKUP = upper_case ? "0123456789ABCDEF" : "0123456789abcdef"; + char *out0; + + if (out) + out0 = out; + else { + out0 = out = g_new (char, delimiter == '\0' + ? length * 2 + 1 + : length * 3); + } + + /* @out must contain at least @length*3 bytes if @delimiter is set, + * otherwise, @length*2+1. */ + + if (length > 0) { + nm_assert (in); + for (;;) { + const guint8 v = *in++; + + *out++ = LOOKUP[v >> 4]; + *out++ = LOOKUP[v & 0x0F]; + length--; + if (!length) + break; + if (delimiter) + *out++ = delimiter; + } + } + + *out = '\0'; + return out0; +} + +guint8 * +nm_utils_hexstr2bin_full (const char *hexstr, + gboolean allow_0x_prefix, + gboolean delimiter_required, + const char *delimiter_candidates, + gsize required_len, + guint8 *buffer, + gsize buffer_len, + gsize *out_len) +{ + const char *in = hexstr; + guint8 *out = buffer; + gboolean delimiter_has = TRUE; + guint8 delimiter = '\0'; + gsize len; + + nm_assert (hexstr); + nm_assert (buffer); + nm_assert (required_len > 0 || out_len); + + if ( allow_0x_prefix + && in[0] == '0' + && in[1] == 'x') + in += 2; + + while (TRUE) { + const guint8 d1 = in[0]; + guint8 d2; + int i1, i2; + + i1 = nm_utils_hexchar_to_int (d1); + if (i1 < 0) + goto fail; + + /* If there's no leading zero (ie "aa:b:cc") then fake it */ + d2 = in[1]; + if ( d2 + && (i2 = nm_utils_hexchar_to_int (d2)) >= 0) { + *out++ = (i1 << 4) + i2; + d2 = in[2]; + if (!d2) + break; + in += 2; + } else { + /* Fake leading zero */ + *out++ = i1; + if (!d2) { + if (!delimiter_has) { + /* when using no delimiter, there must be pairs of hex chars */ + goto fail; + } + break; + } + in += 1; + } + + if (--buffer_len == 0) + goto fail; + + if (delimiter_has) { + if (d2 != delimiter) { + if (delimiter) + goto fail; + if (delimiter_candidates) { + while (delimiter_candidates[0]) { + if (delimiter_candidates++[0] == d2) + delimiter = d2; + } + } + if (!delimiter) { + if (delimiter_required) + goto fail; + delimiter_has = FALSE; + continue; + } + } + in++; + } + } + + len = out - buffer; + if ( required_len == 0 + || len == required_len) { + NM_SET_OUT (out_len, len); + return buffer; + } + +fail: + NM_SET_OUT (out_len, 0); + return NULL; +} + +guint8 * +nm_utils_hexstr2bin_alloc (const char *hexstr, + gboolean allow_0x_prefix, + gboolean delimiter_required, + const char *delimiter_candidates, + gsize required_len, + gsize *out_len) +{ + guint8 *buffer; + gsize buffer_len, len; + + g_return_val_if_fail (hexstr, NULL); + + nm_assert (required_len > 0 || out_len); + + if ( allow_0x_prefix + && hexstr[0] == '0' + && hexstr[1] == 'x') + hexstr += 2; + + if (!hexstr[0]) + goto fail; + + if (required_len > 0) + buffer_len = required_len; + else + buffer_len = strlen (hexstr) / 2 + 3; + + buffer = g_malloc (buffer_len); + + if (nm_utils_hexstr2bin_full (hexstr, + FALSE, + delimiter_required, + delimiter_candidates, + required_len, + buffer, + buffer_len, + &len)) { + NM_SET_OUT (out_len, len); + return buffer; + } + + g_free (buffer); + +fail: + NM_SET_OUT (out_len, 0); + return NULL; +} diff --git a/shared/nm-utils/nm-shared-utils.h b/shared/nm-utils/nm-shared-utils.h index e28a5fb2..65e34959 100644 --- a/shared/nm-utils/nm-shared-utils.h +++ b/shared/nm-utils/nm-shared-utils.h @@ -26,6 +26,18 @@ /*****************************************************************************/ +pid_t nm_utils_gettid (void); + +gboolean _nm_assert_on_main_thread (void); + +#if NM_MORE_ASSERTS > 5 +#define NM_ASSERT_ON_MAIN_THREAD() G_STMT_START { nm_assert (_nm_assert_on_main_thread ()); } G_STMT_END +#else +#define NM_ASSERT_ON_MAIN_THREAD() G_STMT_START { ; } G_STMT_END +#endif + +/*****************************************************************************/ + static inline gboolean _NM_INT_NOT_NEGATIVE (gssize val) { @@ -128,6 +140,18 @@ nm_ip_addr_set (int addr_family, gpointer dst, gconstpointer src) : sizeof (struct in6_addr)); } +gboolean nm_ip_addr_set_from_untrusted (int addr_family, + gpointer dst, + gconstpointer src, + gsize src_len, + int *out_addr_family); + +static inline gboolean +nm_ip4_addr_is_localhost (in_addr_t addr4) +{ + return (addr4 & htonl (0xFF000000u)) == htonl (0x7F000000u); +} + /*****************************************************************************/ #define NM_CMP_RETURN(c) \ @@ -305,7 +329,7 @@ _nm_strndup_a_step (char *s, const char *str, gsize len) * * 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 suble error potential, because the caller needs to ensure to + * 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. */ @@ -688,20 +712,26 @@ nm_utils_error_set_literal (GError **error, int error_code, const char *literal) g_set_error ((error), NM_UTILS_ERROR, error_code, __VA_ARGS__) #define nm_utils_error_set_errno(error, errsv, fmt, ...) \ - g_set_error ((error), \ - NM_UTILS_ERROR, \ - NM_UTILS_ERROR_UNKNOWN, \ - fmt, \ - ##__VA_ARGS__, \ - g_strerror (({ \ - const int _errsv = (errsv); \ - \ - ( _errsv >= 0 \ - ? _errsv \ - : ( (_errsv == G_MININT) \ - ? G_MAXINT \ - : -errsv)); \ - }))) + G_STMT_START { \ + char _bstrerr[NM_STRERROR_BUFSIZE]; \ + \ + g_set_error ((error), \ + NM_UTILS_ERROR, \ + NM_UTILS_ERROR_UNKNOWN, \ + fmt, \ + ##__VA_ARGS__, \ + nm_strerror_native_r (({ \ + const int _errsv = (errsv); \ + \ + ( _errsv >= 0 \ + ? _errsv \ + : ( G_UNLIKELY (_errsv == G_MININT) \ + ? G_MAXINT \ + : -errsv)); \ + }), \ + _bstrerr, \ + sizeof (_bstrerr))); \ + } G_STMT_END /*****************************************************************************/ @@ -777,6 +807,11 @@ GParamSpec *nm_g_object_class_find_property_from_gtype (GType gtype, /*****************************************************************************/ +GType nm_g_type_find_implementing_class_for_property (GType gtype, + const char *pname); + +/*****************************************************************************/ + typedef enum { NM_UTILS_STR_UTF8_SAFE_FLAG_NONE = 0, NM_UTILS_STR_UTF8_SAFE_FLAG_ESCAPE_CTRL = 0x0001, @@ -1093,4 +1128,31 @@ nm_strv_ptrarray_take_gstring (GPtrArray *cmd, int nm_utils_getpagesize (void); +/*****************************************************************************/ + +char *nm_utils_bin2hexstr_full (gconstpointer addr, + gsize length, + char delimiter, + gboolean upper_case, + char *out); + +guint8 *nm_utils_hexstr2bin_full (const char *hexstr, + gboolean allow_0x_prefix, + gboolean delimiter_required, + const char *delimiter_candidates, + gsize required_len, + guint8 *buffer, + gsize buffer_len, + gsize *out_len); + +#define nm_utils_hexstr2bin_buf(hexstr, allow_0x_prefix, delimiter_required, delimiter_candidates, buffer) \ + nm_utils_hexstr2bin_full ((hexstr), (allow_0x_prefix), (delimiter_required), (delimiter_candidates), G_N_ELEMENTS (buffer), (buffer), G_N_ELEMENTS (buffer), NULL) + +guint8 *nm_utils_hexstr2bin_alloc (const char *hexstr, + gboolean allow_0x_prefix, + gboolean delimiter_required, + const char *delimiter_candidates, + gsize required_len, + gsize *out_len); + #endif /* __NM_SHARED_UTILS_H__ */ diff --git a/shared/nm-utils/nm-test-utils.h b/shared/nm-utils/nm-test-utils.h index c235d93d..c5ea5e3f 100644 --- a/shared/nm-utils/nm-test-utils.h +++ b/shared/nm-utils/nm-test-utils.h @@ -1060,7 +1060,7 @@ nmtst_reexec_sudo (void) execvp (__nmtst_internal.sudo_cmd, argv); errsv = errno; - g_error (">> exec %s failed: %d - %s", __nmtst_internal.sudo_cmd, errsv, strerror (errsv)); + g_error (">> exec %s failed: %d - %s", __nmtst_internal.sudo_cmd, errsv, nm_strerror_native (errsv)); } /*****************************************************************************/ @@ -1214,7 +1214,7 @@ nmtst_inet_from_string (int addr_family, const char *str) static inline const char * nmtst_inet_to_string (int addr_family, gconstpointer addr) { - static char buf[MAX (INET6_ADDRSTRLEN, INET_ADDRSTRLEN)]; + static char buf[NM_CONST_MAX (INET6_ADDRSTRLEN, INET_ADDRSTRLEN)]; g_assert (NM_IN_SET (addr_family, AF_INET, AF_INET6)); g_assert (addr); @@ -1370,7 +1370,7 @@ nmtst_file_unlink_if_exists (const char *name) if (unlink (name) != 0) { errsv = errno; if (errsv != ENOENT) - g_error ("nmtst_file_unlink_if_exists(%s): failed with %s", name, strerror (errsv)); + g_error ("nmtst_file_unlink_if_exists(%s): failed with %s", name, nm_strerror_native (errsv)); } } @@ -1383,7 +1383,7 @@ nmtst_file_unlink (const char *name) if (unlink (name) != 0) { errsv = errno; - g_error ("nmtst_file_unlink(%s): failed with %s", name, strerror (errsv)); + g_error ("nmtst_file_unlink(%s): failed with %s", name, nm_strerror_native (errsv)); } } diff --git a/shared/nm-utils/tests/test-shared-general.c b/shared/nm-utils/tests/test-shared-general.c index 7d22e56d..d53b21d9 100644 --- a/shared/nm-utils/tests/test-shared-general.c +++ b/shared/nm-utils/tests/test-shared-general.c @@ -23,6 +23,7 @@ #include "nm-utils/nm-time-utils.h" #include "nm-utils/nm-random-utils.h" +#include "nm-utils/unaligned.h" #include "nm-utils/nm-test-utils.h" @@ -204,6 +205,49 @@ test_nm_strndup_a (void) /*****************************************************************************/ +static void +test_nm_ip4_addr_is_localhost (void) +{ + g_assert ( nm_ip4_addr_is_localhost (nmtst_inet4_from_string ("127.0.0.0"))); + g_assert ( nm_ip4_addr_is_localhost (nmtst_inet4_from_string ("127.0.0.1"))); + g_assert ( nm_ip4_addr_is_localhost (nmtst_inet4_from_string ("127.5.0.1"))); + g_assert (!nm_ip4_addr_is_localhost (nmtst_inet4_from_string ("126.5.0.1"))); + g_assert (!nm_ip4_addr_is_localhost (nmtst_inet4_from_string ("128.5.0.1"))); + g_assert (!nm_ip4_addr_is_localhost (nmtst_inet4_from_string ("129.5.0.1"))); +} + +/*****************************************************************************/ + +static void +test_unaligned (void) +{ + int shift; + + for (shift = 0; shift <= 32; shift++) { + guint8 buf[100] = { }; + guint8 val = 0; + + while (val == 0) + val = nmtst_get_rand_int () % 256; + + buf[shift] = val; + + g_assert_cmpint (unaligned_read_le64 (&buf[shift]), ==, (guint64) val); + g_assert_cmpint (unaligned_read_be64 (&buf[shift]), ==, ((guint64) val) << 56); + g_assert_cmpint (unaligned_read_ne64 (&buf[shift]), !=, 0); + + g_assert_cmpint (unaligned_read_le32 (&buf[shift]), ==, (guint32) val); + g_assert_cmpint (unaligned_read_be32 (&buf[shift]), ==, ((guint32) val) << 24); + g_assert_cmpint (unaligned_read_ne32 (&buf[shift]), !=, 0); + + g_assert_cmpint (unaligned_read_le16 (&buf[shift]), ==, (guint16) val); + g_assert_cmpint (unaligned_read_be16 (&buf[shift]), ==, ((guint16) val) << 8); + g_assert_cmpint (unaligned_read_ne16 (&buf[shift]), !=, 0); + } +} + +/*****************************************************************************/ + NMTST_DEFINE (); int main (int argc, char **argv) @@ -215,6 +259,8 @@ int main (int argc, char **argv) g_test_add_func ("/general/test_nm_make_strv", test_make_strv); g_test_add_func ("/general/test_nm_strdup_int", test_nm_strdup_int); 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); return g_test_run (); } diff --git a/shared/nm-version-macros.h b/shared/nm-version-macros.h index b04baabb..ffe13c4e 100644 --- a/shared/nm-version-macros.h +++ b/shared/nm-version-macros.h @@ -45,7 +45,7 @@ * Evaluates to the micro version number of NetworkManager which this source * compiled against. */ -#define NM_MICRO_VERSION (2) +#define NM_MICRO_VERSION (91) /** * NM_CHECK_VERSION: diff --git a/shared/systemd/nm-logging-stub.c b/shared/systemd/nm-logging-stub.c index 80d43607..5be69b4b 100644 --- a/shared/systemd/nm-logging-stub.c +++ b/shared/systemd/nm-logging-stub.c @@ -24,8 +24,9 @@ /*****************************************************************************/ gboolean -_nm_log_enabled (NMLogLevel level, - NMLogDomain domain) +_nm_log_enabled_impl (gboolean mt_require_locking, + NMLogLevel level, + NMLogDomain domain) { return FALSE; } @@ -34,6 +35,7 @@ void _nm_log_impl (const char *file, guint line, const char *func, + gboolean mt_require_locking, NMLogLevel level, NMLogDomain domain, int error, diff --git a/shared/systemd/sd-adapt-shared/missing.h b/shared/systemd/sd-adapt-shared/missing.h index 637892c2..2ee34b6a 100644 --- a/shared/systemd/sd-adapt-shared/missing.h +++ b/shared/systemd/sd-adapt-shared/missing.h @@ -1,3 +1,6 @@ #pragma once /* dummy header */ + +#include "missing_fcntl.h" +#include "missing_type.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 905e45c0..06008ce8 100644 --- a/shared/systemd/sd-adapt-shared/nm-sd-adapt-shared.h +++ b/shared/systemd/sd-adapt-shared/nm-sd-adapt-shared.h @@ -16,8 +16,8 @@ * Copyright (C) 2014 - 2018 Red Hat, Inc. */ -#ifndef __NM_SD_ADAPT_BASIC_H__ -#define __NM_SD_ADAPT_BASIC_H__ +#ifndef __NM_SD_ADAPT_SHARED_H__ +#define __NM_SD_ADAPT_SHARED_H__ #include "nm-default.h" @@ -27,11 +27,16 @@ /*****************************************************************************/ +/* strerror() is not thread-safe. Patch systemd-sources via a define. */ +#define strerror(errsv) nm_strerror_native (errsv) + +/*****************************************************************************/ + static inline NMLogLevel _slog_level_to_nm (int slevel) { - switch (LOG_PRI (slevel)) { - case LOG_DEBUG: return LOGL_DEBUG; + switch (LOG_PRI (slevel)) { + case LOG_DEBUG: return LOGL_DEBUG; case LOG_WARNING: return LOGL_WARN; case LOG_CRIT: case LOG_ERR: return LOGL_ERR; @@ -54,10 +59,10 @@ _nm_log_get_max_level_realm (void) const int _nm_e = (error); \ const NMLogLevel _nm_l = _slog_level_to_nm ((level)); \ \ - if (_nm_log_enabled (_nm_l, LOGD_SYSTEMD)) { \ + if (_nm_log_enabled_impl (!(NM_THREAD_SAFE_ON_MAIN_THREAD), _nm_l, LOGD_SYSTEMD)) { \ const char *_nm_location = strrchr ((""file), '/'); \ \ - _nm_log_impl (_nm_location ? _nm_location + 1 : (""file), (line), (func), _nm_l, LOGD_SYSTEMD, _nm_e, NULL, NULL, ("%s"format), "libsystemd: ", ## __VA_ARGS__); \ + _nm_log_impl (_nm_location ? _nm_location + 1 : (""file), (line), (func), !(NM_THREAD_SAFE_ON_MAIN_THREAD), _nm_l, LOGD_SYSTEMD, _nm_e, NULL, NULL, ("%s"format), "libsystemd: ", ## __VA_ARGS__); \ } \ (_nm_e > 0 ? -_nm_e : _nm_e); \ }) @@ -130,4 +135,4 @@ static inline pid_t gettid(void) { /*****************************************************************************/ -#endif /* __NM_SD_ADAPT_BASIC_H__ */ +#endif /* __NM_SD_ADAPT_SHARED_H__ */ diff --git a/shared/systemd/src/basic/alloc-util.h b/shared/systemd/src/basic/alloc-util.h index ff7a4679..893a1238 100644 --- a/shared/systemd/src/basic/alloc-util.h +++ b/shared/systemd/src/basic/alloc-util.h @@ -10,20 +10,28 @@ 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 + * proceeding and smashing the stack limits. Note that by default RLIMIT_STACK is 8M on Linux. */ +#define ALLOCA_MAX (4U*1024U*1024U) + #define new(t, n) ((t*) malloc_multiply(sizeof(t), (n))) #define new0(t, n) ((t*) calloc((n) ?: 1, sizeof(t))) -#define newa(t, n) \ - ({ \ - assert(!size_multiply_overflow(sizeof(t), n)); \ - (t*) alloca(sizeof(t)*(n)); \ +#define newa(t, n) \ + ({ \ + size_t _n_ = n; \ + assert(!size_multiply_overflow(sizeof(t), _n_)); \ + assert(sizeof(t)*_n_ <= ALLOCA_MAX); \ + (t*) alloca(sizeof(t)*_n_); \ }) -#define newa0(t, n) \ - ({ \ - assert(!size_multiply_overflow(sizeof(t), n)); \ - (t*) alloca0(sizeof(t)*(n)); \ +#define newa0(t, n) \ + ({ \ + size_t _n_ = n; \ + assert(!size_multiply_overflow(sizeof(t), _n_)); \ + assert(sizeof(t)*_n_ <= ALLOCA_MAX); \ + (t*) alloca0(sizeof(t)*_n_); \ }) #define newdup(t, p, n) ((t*) memdup_multiply(p, sizeof(t), (n))) @@ -51,16 +59,20 @@ void* memdup_suffix0(const void *p, size_t l) _alloc_(2); #define memdupa(p, l) \ ({ \ void *_q_; \ - _q_ = alloca(l); \ - memcpy(_q_, p, l); \ + size_t _l_ = l; \ + assert(_l_ <= ALLOCA_MAX); \ + _q_ = alloca(_l_); \ + memcpy(_q_, p, _l_); \ }) #define memdupa_suffix0(p, l) \ ({ \ void *_q_; \ - _q_ = alloca(l + 1); \ - ((uint8_t*) _q_)[l] = 0; \ - memcpy(_q_, p, l); \ + size_t _l_ = l; \ + assert(_l_ <= ALLOCA_MAX); \ + _q_ = alloca(_l_ + 1); \ + ((uint8_t*) _q_)[_l_] = 0; \ + memcpy(_q_, p, _l_); \ }) static inline void freep(void *p) { @@ -116,6 +128,7 @@ void* greedy_realloc0(void **p, size_t *allocated, size_t need, size_t size); ({ \ char *_new_; \ size_t _len_ = n; \ + assert(_len_ <= ALLOCA_MAX); \ _new_ = alloca(_len_); \ (void *) memset(_new_, 0, _len_); \ }) @@ -125,16 +138,18 @@ void* greedy_realloc0(void **p, size_t *allocated, size_t need, size_t size); ({ \ void *_ptr_; \ size_t _mask_ = (align) - 1; \ - _ptr_ = alloca((size) + _mask_); \ + size_t _size_ = size; \ + assert(_size_ <= ALLOCA_MAX); \ + _ptr_ = alloca(_size_ + _mask_); \ (void*)(((uintptr_t)_ptr_ + _mask_) & ~_mask_); \ }) #define alloca0_align(size, align) \ ({ \ void *_new_; \ - size_t _size_ = (size); \ - _new_ = alloca_align(_size_, (align)); \ - (void*)memset(_new_, 0, _size_); \ + size_t _xsize_ = (size); \ + _new_ = alloca_align(_xsize_, (align)); \ + (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 diff --git a/shared/systemd/src/basic/env-file.c b/shared/systemd/src/basic/env-file.c index beafd8c2..4babe753 100644 --- a/shared/systemd/src/basic/env-file.c +++ b/shared/systemd/src/basic/env-file.c @@ -37,7 +37,6 @@ static int parse_env_file_internal( VALUE, VALUE_ESCAPE, SINGLE_QUOTE_VALUE, - SINGLE_QUOTE_VALUE_ESCAPE, DOUBLE_QUOTE_VALUE, DOUBLE_QUOTE_VALUE_ESCAPE, COMMENT, @@ -115,7 +114,7 @@ static int parse_env_file_internal( } else if (c == '\'') state = SINGLE_QUOTE_VALUE; - else if (c == '\"') + else if (c == '"') state = DOUBLE_QUOTE_VALUE; else if (c == '\\') state = VALUE_ESCAPE; @@ -188,8 +187,6 @@ static int parse_env_file_internal( case SINGLE_QUOTE_VALUE: if (c == '\'') state = PRE_VALUE; - else if (c == '\\') - state = SINGLE_QUOTE_VALUE_ESCAPE; else { if (!GREEDY_REALLOC(value, value_alloc, n_value+2)) return -ENOMEM; @@ -199,19 +196,8 @@ static int parse_env_file_internal( break; - case SINGLE_QUOTE_VALUE_ESCAPE: - state = SINGLE_QUOTE_VALUE; - - if (!strchr(NEWLINE, c)) { - if (!GREEDY_REALLOC(value, value_alloc, n_value+2)) - return -ENOMEM; - - value[n_value++] = c; - } - break; - case DOUBLE_QUOTE_VALUE: - if (c == '\"') + if (c == '"') state = PRE_VALUE; else if (c == '\\') state = DOUBLE_QUOTE_VALUE_ESCAPE; @@ -227,12 +213,17 @@ static int parse_env_file_internal( case DOUBLE_QUOTE_VALUE_ESCAPE: state = DOUBLE_QUOTE_VALUE; - if (!strchr(NEWLINE, c)) { + if (c == '"') { if (!GREEDY_REALLOC(value, value_alloc, n_value+2)) return -ENOMEM; - + value[n_value++] = '"'; + } else if (!strchr(NEWLINE, c)) { + if (!GREEDY_REALLOC(value, value_alloc, n_value+3)) + return -ENOMEM; + value[n_value++] = '\\'; value[n_value++] = c; } + break; case COMMENT: @@ -255,7 +246,6 @@ static int parse_env_file_internal( VALUE, VALUE_ESCAPE, SINGLE_QUOTE_VALUE, - SINGLE_QUOTE_VALUE_ESCAPE, DOUBLE_QUOTE_VALUE, DOUBLE_QUOTE_VALUE_ESCAPE)) { @@ -530,7 +520,7 @@ static void write_env_var(FILE *f, const char *v) { fwrite_unlocked(v, 1, p-v, f); if (string_has_cc(p, NULL) || chars_intersect(p, WHITESPACE SHELL_NEED_QUOTES)) { - fputc_unlocked('\"', f); + fputc_unlocked('"', f); for (; *p; p++) { if (strchr(SHELL_NEED_ESCAPE, *p)) @@ -539,7 +529,7 @@ static void write_env_var(FILE *f, const char *v) { fputc_unlocked(*p, f); } - fputc_unlocked('\"', f); + fputc_unlocked('"', f); } else fputs_unlocked(p, f); diff --git a/shared/systemd/src/basic/env-util.c b/shared/systemd/src/basic/env-util.c index fd9eedc5..dc10362d 100644 --- a/shared/systemd/src/basic/env-util.c +++ b/shared/systemd/src/basic/env-util.c @@ -342,7 +342,6 @@ char **strv_env_unset(char **l, const char *p) { } char **strv_env_unset_many(char **l, ...) { - char **f, **t; if (!l) @@ -411,7 +410,6 @@ int strv_env_replace(char ***l, char *p) { } char **strv_env_set(char **x, const char *p) { - _cleanup_strv_free_ char **ret = NULL; size_t n, m; char **k; diff --git a/shared/systemd/src/basic/env-util.h b/shared/systemd/src/basic/env-util.h index 4d21ea6b..d54f9965 100644 --- a/shared/systemd/src/basic/env-util.h +++ b/shared/systemd/src/basic/env-util.h @@ -13,9 +13,9 @@ bool env_value_is_valid(const char *e); bool env_assignment_is_valid(const char *e); enum { - REPLACE_ENV_USE_ENVIRONMENT = 1u, - REPLACE_ENV_ALLOW_BRACELESS = 2u, - REPLACE_ENV_ALLOW_EXTENDED = 4u, + REPLACE_ENV_USE_ENVIRONMENT = 1 << 0, + REPLACE_ENV_ALLOW_BRACELESS = 1 << 1, + REPLACE_ENV_ALLOW_EXTENDED = 1 << 2, }; char *replace_env_n(const char *format, size_t n, char **env, unsigned flags); diff --git a/shared/systemd/src/basic/extract-word.h b/shared/systemd/src/basic/extract-word.h index 8c63b7c3..705ebbe9 100644 --- a/shared/systemd/src/basic/extract-word.h +++ b/shared/systemd/src/basic/extract-word.h @@ -4,12 +4,12 @@ #include "macro.h" typedef enum ExtractFlags { - EXTRACT_RELAX = 1, - EXTRACT_CUNESCAPE = 2, - EXTRACT_CUNESCAPE_RELAX = 4, - EXTRACT_QUOTES = 8, - EXTRACT_DONT_COALESCE_SEPARATORS = 16, - EXTRACT_RETAIN_ESCAPE = 32, + EXTRACT_RELAX = 1 << 0, + EXTRACT_CUNESCAPE = 1 << 1, + EXTRACT_CUNESCAPE_RELAX = 1 << 2, + EXTRACT_QUOTES = 1 << 3, + EXTRACT_DONT_COALESCE_SEPARATORS = 1 << 4, + EXTRACT_RETAIN_ESCAPE = 1 << 5, } ExtractFlags; int extract_first_word(const char **p, char **ret, const char *separators, ExtractFlags flags); diff --git a/shared/systemd/src/basic/fd-util.c b/shared/systemd/src/basic/fd-util.c index 8ca1711b..0cc0c6b5 100644 --- a/shared/systemd/src/basic/fd-util.c +++ b/shared/systemd/src/basic/fd-util.c @@ -192,6 +192,27 @@ _pure_ static bool fd_in_set(int fd, const int fdset[], size_t n_fdset) { return false; } +static int get_max_fd(void) { + struct rlimit rl; + rlim_t m; + + /* Return the highest possible fd, based RLIMIT_NOFILE, but enforcing FD_SETSIZE-1 as lower boundary + * and INT_MAX as upper boundary. */ + + if (getrlimit(RLIMIT_NOFILE, &rl) < 0) + return -errno; + + m = MAX(rl.rlim_cur, rl.rlim_max); + if (m < FD_SETSIZE) /* Let's always cover at least 1024 fds */ + return FD_SETSIZE-1; + + if (m == RLIM_INFINITY || m > INT_MAX) /* Saturate on overflow. After all fds are "int", hence can + * never be above INT_MAX */ + return INT_MAX; + + return (int) (m - 1); +} + int close_all_fds(const int except[], size_t n_except) { _cleanup_closedir_ DIR *d = NULL; struct dirent *de; @@ -201,20 +222,14 @@ int close_all_fds(const int except[], size_t n_except) { d = opendir("/proc/self/fd"); if (!d) { - struct rlimit rl; int fd, max_fd; - /* When /proc isn't available (for example in chroots) the fallback is brute forcing through the fd - * table */ - - assert_se(getrlimit(RLIMIT_NOFILE, &rl) >= 0); - - if (rl.rlim_max == 0) - return -EINVAL; + /* When /proc isn't available (for example in chroots) the fallback is brute forcing through + * the fd table */ - /* Let's take special care if the resource limit is set to unlimited, or actually larger than the range - * of 'int'. Let's avoid implicit overflows. */ - max_fd = (rl.rlim_max == RLIM_INFINITY || rl.rlim_max > INT_MAX) ? INT_MAX : (int) (rl.rlim_max - 1); + max_fd = get_max_fd(); + if (max_fd < 0) + return max_fd; for (fd = 3; fd >= 0; fd = fd < max_fd ? fd + 1 : -1) { int q; @@ -365,7 +380,7 @@ int fd_get_path(int fd, char **ret) { r = readlink_malloc(procfs_path, ret); if (r == -ENOENT) { /* ENOENT can mean two things: that the fd does not exist or that /proc is not mounted. Let's make - * things debuggable and distuingish the two. */ + * things debuggable and distinguish the two. */ if (access("/proc/self/fd/", F_OK) < 0) /* /proc is not available or not set up properly, we're most likely in some chroot diff --git a/shared/systemd/src/basic/fileio.c b/shared/systemd/src/basic/fileio.c index b172507b..ee66190f 100644 --- a/shared/systemd/src/basic/fileio.c +++ b/shared/systemd/src/basic/fileio.c @@ -215,7 +215,6 @@ int write_string_filef( int read_one_line_file(const char *fn, char **line) { _cleanup_fclose_ FILE *f = NULL; - int r; assert(fn); assert(line); @@ -226,8 +225,7 @@ int read_one_line_file(const char *fn, char **line) { (void) __fsetlocking(f, FSETLOCKING_BYCALLER); - r = read_line(f, LONG_LINE_MAX, line); - return r < 0 ? r : 0; + return read_line(f, LONG_LINE_MAX, line); } int verify_file(const char *fn, const char *blob, bool accept_extra_nl) { diff --git a/shared/systemd/src/basic/hashmap.c b/shared/systemd/src/basic/hashmap.c index 09303c6f..c0655831 100644 --- a/shared/systemd/src/basic/hashmap.c +++ b/shared/systemd/src/basic/hashmap.c @@ -347,7 +347,7 @@ static unsigned base_bucket_hash(HashmapBase *h, const void *p) { } #define bucket_hash(h, p) base_bucket_hash(HASHMAP_BASE(h), p) -static inline void base_set_dirty(HashmapBase *h) { +static void base_set_dirty(HashmapBase *h) { h->dirty = true; } #define hashmap_set_dirty(h) base_set_dirty(HASHMAP_BASE(h)) @@ -890,7 +890,8 @@ void internal_hashmap_clear(HashmapBase *h, free_func_t default_free_key, free_f * themselves from our hash table a second time, the entry is already gone. */ while (internal_hashmap_size(h) > 0) { - void *v, *k; + void *k = NULL; + void *v; v = internal_hashmap_first_key_and_value(h, true, &k); @@ -1517,8 +1518,11 @@ void *internal_hashmap_first_key_and_value(HashmapBase *h, bool remove, void **r unsigned idx; idx = find_first_entry(h); - if (idx == IDX_NIL) + if (idx == IDX_NIL) { + if (ret_key) + *ret_key = NULL; return NULL; + } e = bucket_at(h, idx); key = (void*) e->key; diff --git a/shared/systemd/src/basic/hashmap.h b/shared/systemd/src/basic/hashmap.h index 5bf807a7..e16a9f9e 100644 --- a/shared/systemd/src/basic/hashmap.h +++ b/shared/systemd/src/basic/hashmap.h @@ -326,7 +326,6 @@ static inline void *ordered_hashmap_first_key_and_value(OrderedHashmap *h, void return internal_hashmap_first_key_and_value(HASHMAP_BASE(h), false, ret); } - static inline void *hashmap_steal_first(Hashmap *h) { return internal_hashmap_first_key_and_value(HASHMAP_BASE(h), true, NULL); } diff --git a/shared/systemd/src/basic/hexdecoct.c b/shared/systemd/src/basic/hexdecoct.c index ffd9a8f6..7c66cc62 100644 --- a/shared/systemd/src/basic/hexdecoct.c +++ b/shared/systemd/src/basic/hexdecoct.c @@ -606,10 +606,11 @@ static int base64_append_width( lines = DIV_ROUND_UP(len, width); slen = strlen_ptr(sep); - if (lines > (SSIZE_MAX - plen - 1 - slen) / (indent + width + 1)) + if (plen >= SSIZE_MAX - 1 - slen || + lines > (SSIZE_MAX - plen - 1 - slen) / (indent + width + 1)) return -ENOMEM; - t = realloc(*prefix, plen + 1 + slen + (indent + width + 1) * lines); + t = realloc(*prefix, (ssize_t) plen + 1 + slen + (indent + width + 1) * lines); if (!t) return -ENOMEM; @@ -644,7 +645,7 @@ int base64_append( return base64_append_width(prefix, plen, "\n", indent, p, l, width - indent - 1); else /* leave plen on the left, keep last column free */ - return base64_append_width(prefix, plen, NULL, plen, p, l, width - plen - 1); + return base64_append_width(prefix, plen, " ", plen, p, l, width - plen - 1); } #endif /* NM_IGNORED */ diff --git a/shared/systemd/src/basic/in-addr-util.c b/shared/systemd/src/basic/in-addr-util.c index d2cc5606..5ced3501 100644 --- a/shared/systemd/src/basic/in-addr-util.c +++ b/shared/systemd/src/basic/in-addr-util.c @@ -70,6 +70,14 @@ bool in4_addr_is_localhost(const struct in_addr *a) { return (be32toh(a->s_addr) & UINT32_C(0xFF000000)) == UINT32_C(127) << 24; } +bool in4_addr_is_non_local(const struct in_addr *a) { + /* Whether the address is not null and not localhost. + * + * As such, it is suitable to configure as DNS/NTP server from DHCP. */ + return !in4_addr_is_null(a) && + !in4_addr_is_localhost(a); +} + int in_addr_is_localhost(int family, const union in_addr_union *u) { assert(u); @@ -500,9 +508,8 @@ int in_addr_parse_prefixlen(int family, const char *p, unsigned char *ret) { return 0; } -int in_addr_prefix_from_string_internal( +int in_addr_prefix_from_string( const char *p, - bool use_default_prefixlen, int family, union in_addr_union *ret_prefix, unsigned char *ret_prefixlen) { @@ -536,13 +543,6 @@ int in_addr_prefix_from_string_internal( r = in_addr_parse_prefixlen(family, e+1, &k); if (r < 0) return r; - } else if (use_default_prefixlen) { - if (family == AF_INET) { - r = in4_addr_default_prefixlen(&buffer.in, &k); - if (r < 0) - return r; - } else - k = 0; } else k = FAMILY_ADDRESS_SIZE(family) * 8; @@ -556,7 +556,7 @@ int in_addr_prefix_from_string_internal( int in_addr_prefix_from_string_auto_internal( const char *p, - bool use_default_prefixlen, + InAddrPrefixLenMode mode, int *ret_family, union in_addr_union *ret_prefix, unsigned char *ret_prefixlen) { @@ -587,15 +587,24 @@ int in_addr_prefix_from_string_auto_internal( r = in_addr_parse_prefixlen(family, e+1, &k); if (r < 0) return r; - } else if (use_default_prefixlen) { - if (family == AF_INET) { - r = in4_addr_default_prefixlen(&buffer.in, &k); - if (r < 0) - return r; - } else - k = 0; } else - k = FAMILY_ADDRESS_SIZE(family) * 8; + switch (mode) { + case PREFIXLEN_FULL: + k = FAMILY_ADDRESS_SIZE(family) * 8; + break; + case PREFIXLEN_REFUSE: + return -ENOANO; /* To distinguish this error from others. */ + case PREFIXLEN_LEGACY: + if (family == AF_INET) { + r = in4_addr_default_prefixlen(&buffer.in, &k); + if (r < 0) + return r; + } else + k = 0; + break; + default: + assert_not_reached("Invalid prefixlen mode"); + } if (ret_family) *ret_family = family; diff --git a/shared/systemd/src/basic/in-addr-util.h b/shared/systemd/src/basic/in-addr-util.h index 5de85cc4..c2156712 100644 --- a/shared/systemd/src/basic/in-addr-util.h +++ b/shared/systemd/src/basic/in-addr-util.h @@ -30,6 +30,8 @@ int in_addr_is_link_local(int family, const union in_addr_union *u); bool in4_addr_is_localhost(const struct in_addr *a); int in_addr_is_localhost(int family, const union in_addr_union *u); +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); @@ -45,19 +47,17 @@ int in4_addr_default_subnet_mask(const struct in_addr *addr, struct in_addr *mas int in_addr_mask(int family, union in_addr_union *addr, unsigned char prefixlen); int in_addr_prefix_covers(int family, const union in_addr_union *prefix, unsigned char prefixlen, const union in_addr_union *address); int in_addr_parse_prefixlen(int family, const char *p, unsigned char *ret); -int in_addr_prefix_from_string_internal(const char *p, bool use_default_prefixlen, int family, union in_addr_union *ret_prefix, unsigned char *ret_prefixlen); -int in_addr_prefix_from_string_auto_internal(const char *p, bool use_default_prefixlen, int *ret_family, union in_addr_union *ret_prefix, unsigned char *ret_prefixlen); -static inline int in_addr_prefix_from_string(const char *p, int family, union in_addr_union *ret_prefix, unsigned char *ret_prefixlen) { - return in_addr_prefix_from_string_internal(p, false, family, ret_prefix, ret_prefixlen); -} +int in_addr_prefix_from_string(const char *p, int family, union in_addr_union *ret_prefix, unsigned char *ret_prefixlen); + +typedef enum InAddrPrefixLenMode { + PREFIXLEN_FULL, /* Default to prefixlen of address size, 32 for IPv4 or 128 for IPv6, if not specified. */ + PREFIXLEN_REFUSE, /* Fail with -ENOANO if prefixlen is not specified. */ + PREFIXLEN_LEGACY, /* Default to legacy default prefixlen calculation from address if not specified. */ +} InAddrPrefixLenMode; + +int in_addr_prefix_from_string_auto_internal(const char *p, InAddrPrefixLenMode mode, int *ret_family, union in_addr_union *ret_prefix, unsigned char *ret_prefixlen); static inline int in_addr_prefix_from_string_auto(const char *p, int *ret_family, union in_addr_union *ret_prefix, unsigned char *ret_prefixlen) { - return in_addr_prefix_from_string_auto_internal(p, false, ret_family, ret_prefix, ret_prefixlen); -} -static inline int in_addr_default_prefix_from_string(const char *p, int family, union in_addr_union *ret_prefix, unsigned char *ret_prefixlen) { - return in_addr_prefix_from_string_internal(p, true, family, ret_prefix, ret_prefixlen); -} -static inline int in_addr_default_prefix_from_string_auto(const char *p, int *ret_family, union in_addr_union *ret_prefix, unsigned char *ret_prefixlen) { - return in_addr_prefix_from_string_auto_internal(p, true, ret_family, ret_prefix, ret_prefixlen); + return in_addr_prefix_from_string_auto_internal(p, PREFIXLEN_FULL, ret_family, ret_prefix, ret_prefixlen); } static inline size_t FAMILY_ADDRESS_SIZE(int family) { diff --git a/shared/systemd/src/basic/io-util.c b/shared/systemd/src/basic/io-util.c index f7c25915..3f47eff5 100644 --- a/shared/systemd/src/basic/io-util.c +++ b/shared/systemd/src/basic/io-util.c @@ -10,8 +10,10 @@ #include <unistd.h> #include "io-util.h" +#include "string-util.h" #include "time-util.h" +#if 0 /* NM_IGNORED */ int flush_fd(int fd) { struct pollfd pollfd = { .fd = fd, @@ -55,6 +57,7 @@ int flush_fd(int fd) { count += (int) l; } } +#endif /* NM_IGNORED */ ssize_t loop_read(int fd, void *buf, size_t nbytes, bool do_poll) { uint8_t *p = buf; @@ -115,6 +118,7 @@ int loop_read_exact(int fd, void *buf, size_t nbytes, bool do_poll) { return 0; } +#if 0 /* NM_IGNORED */ int loop_write(int fd, const void *buf, size_t nbytes, bool do_poll) { const uint8_t *p = buf; @@ -173,6 +177,7 @@ int pipe_eof(int fd) { return pollfd.revents & POLLHUP; } +#endif /* NM_IGNORED */ int fd_wait_for_event(int fd, int event, usec_t t) { @@ -193,6 +198,7 @@ int fd_wait_for_event(int fd, int event, usec_t t) { return pollfd.revents; } +#if 0 /* NM_IGNORED */ static size_t nul_length(const uint8_t *p, size_t sz) { size_t n = 0; @@ -254,3 +260,13 @@ ssize_t sparse_write(int fd, const void *p, size_t sz, size_t run_length) { return q - (const uint8_t*) p; } + +char* set_iovec_string_field(struct iovec *iovec, size_t *n_iovec, const char *field, const char *value) { + char *x; + + x = strappend(field, value); + if (x) + iovec[(*n_iovec)++] = IOVEC_MAKE_STRING(x); + return x; +} +#endif /* NM_IGNORED */ diff --git a/shared/systemd/src/basic/io-util.h b/shared/systemd/src/basic/io-util.h index ed189b58..792a64ad 100644 --- a/shared/systemd/src/basic/io-util.h +++ b/shared/systemd/src/basic/io-util.h @@ -71,3 +71,5 @@ static inline bool FILE_SIZE_VALID_OR_INFINITY(uint64_t l) { #define IOVEC_MAKE(base, len) (struct iovec) IOVEC_INIT(base, len) #define IOVEC_INIT_STRING(string) IOVEC_INIT((char*) string, strlen(string)) #define IOVEC_MAKE_STRING(string) (struct iovec) IOVEC_INIT_STRING(string) + +char* set_iovec_string_field(struct iovec *iovec, size_t *n_iovec, const char *field, const char *value); diff --git a/shared/systemd/src/basic/missing_fcntl.h b/shared/systemd/src/basic/missing_fcntl.h new file mode 100644 index 00000000..5d1c6352 --- /dev/null +++ b/shared/systemd/src/basic/missing_fcntl.h @@ -0,0 +1,60 @@ +/* SPDX-License-Identifier: LGPL-2.1+ */ +#pragma once + +#include <fcntl.h> + +#ifndef F_LINUX_SPECIFIC_BASE +#define F_LINUX_SPECIFIC_BASE 1024 +#endif + +#ifndef F_SETPIPE_SZ +#define F_SETPIPE_SZ (F_LINUX_SPECIFIC_BASE + 7) +#endif + +#ifndef F_GETPIPE_SZ +#define F_GETPIPE_SZ (F_LINUX_SPECIFIC_BASE + 8) +#endif + +#ifndef F_ADD_SEALS +#define F_ADD_SEALS (F_LINUX_SPECIFIC_BASE + 9) +#define F_GET_SEALS (F_LINUX_SPECIFIC_BASE + 10) + +#define F_SEAL_SEAL 0x0001 /* prevent further seals from being set */ +#define F_SEAL_SHRINK 0x0002 /* prevent file from shrinking */ +#define F_SEAL_GROW 0x0004 /* prevent file from growing */ +#define F_SEAL_WRITE 0x0008 /* prevent writes */ +#endif + +#ifndef F_OFD_GETLK +#define F_OFD_GETLK 36 +#define F_OFD_SETLK 37 +#define F_OFD_SETLKW 38 +#endif + +#ifndef MAX_HANDLE_SZ +#define MAX_HANDLE_SZ 128 +#endif + +/* The precise definition of __O_TMPFILE is arch specific; use the + * values defined by the kernel (note: some are hexa, some are octal, + * duplicated as-is from the kernel definitions): + * - alpha, parisc, sparc: each has a specific value; + * - others: they use the "generic" value. + */ + +#ifndef __O_TMPFILE +#if defined(__alpha__) +#define __O_TMPFILE 0100000000 +#elif defined(__parisc__) || defined(__hppa__) +#define __O_TMPFILE 0400000000 +#elif defined(__sparc__) || defined(__sparc64__) +#define __O_TMPFILE 0x2000000 +#else +#define __O_TMPFILE 020000000 +#endif +#endif + +/* a horrid kludge trying to make sure that this will fail on old kernels */ +#ifndef O_TMPFILE +#define O_TMPFILE (__O_TMPFILE | O_DIRECTORY) +#endif diff --git a/shared/systemd/src/basic/path-util.h b/shared/systemd/src/basic/path-util.h index 56ebd59c..5204adaa 100644 --- a/shared/systemd/src/basic/path-util.h +++ b/shared/systemd/src/basic/path-util.h @@ -133,7 +133,7 @@ char *prefix_root(const char *root, const char *path); _ret = _path; \ else { \ _l = strlen(_root) + 1 + strlen(_path) + 1; \ - _n = alloca(_l); \ + _n = newa(char, _l); \ _p = stpcpy(_n, _root); \ while (_p > _n && _p[-1] == '/') \ _p--; \ diff --git a/shared/systemd/src/basic/prioq.c b/shared/systemd/src/basic/prioq.c index b218f37d..dc048cc7 100644 --- a/shared/systemd/src/basic/prioq.c +++ b/shared/systemd/src/basic/prioq.c @@ -261,15 +261,14 @@ int prioq_reshuffle(Prioq *q, void *data, unsigned *idx) { return 1; } -void *prioq_peek(Prioq *q) { - +void *prioq_peek_by_index(Prioq *q, unsigned idx) { if (!q) return NULL; - if (q->n_items <= 0) + if (idx >= q->n_items) return NULL; - return q->items[0].data; + return q->items[idx].data; } void *prioq_pop(Prioq *q) { diff --git a/shared/systemd/src/basic/prioq.h b/shared/systemd/src/basic/prioq.h index bba5c7ca..1fb57bfa 100644 --- a/shared/systemd/src/basic/prioq.h +++ b/shared/systemd/src/basic/prioq.h @@ -19,8 +19,14 @@ int prioq_put(Prioq *q, void *data, unsigned *idx); int prioq_remove(Prioq *q, void *data, unsigned *idx); int prioq_reshuffle(Prioq *q, void *data, unsigned *idx); -void *prioq_peek(Prioq *q) _pure_; +void *prioq_peek_by_index(Prioq *q, unsigned idx) _pure_; +static inline void *prioq_peek(Prioq *q) { + return prioq_peek_by_index(q, 0); +} void *prioq_pop(Prioq *q); +#define PRIOQ_FOREACH_ITEM(q, p) \ + for (unsigned _i = 0; (p = prioq_peek_by_index(q, _i)); _i++) + unsigned prioq_size(Prioq *q) _pure_; bool prioq_isempty(Prioq *q) _pure_; diff --git a/shared/systemd/src/basic/process-util.c b/shared/systemd/src/basic/process-util.c index be7911a3..b0afb5c8 100644 --- a/shared/systemd/src/basic/process-util.c +++ b/shared/systemd/src/basic/process-util.c @@ -107,7 +107,8 @@ int get_process_comm(pid_t pid, char **ret) { int get_process_cmdline(pid_t pid, size_t max_length, bool comm_fallback, char **line) { _cleanup_fclose_ FILE *f = NULL; bool space = false; - char *k, *ans = NULL; + char *k; + _cleanup_free_ char *ans = NULL; const char *p; int c; @@ -134,6 +135,13 @@ int get_process_cmdline(pid_t pid, size_t max_length, bool comm_fallback, char * (void) __fsetlocking(f, FSETLOCKING_BYCALLER); + if (max_length == 0) { + /* This is supposed to be a safety guard against runaway command lines. */ + long l = sysconf(_SC_ARG_MAX); + assert(l > 0); + max_length = l; + } + if (max_length == 1) { /* If there's only room for one byte, return the empty string */ @@ -141,35 +149,9 @@ int get_process_cmdline(pid_t pid, size_t max_length, bool comm_fallback, char * if (!ans) return -ENOMEM; - *line = ans; + *line = TAKE_PTR(ans); return 0; - } else if (max_length == 0) { - size_t len = 0, allocated = 0; - - while ((c = getc(f)) != EOF) { - - if (!GREEDY_REALLOC(ans, allocated, len+3)) { - free(ans); - return -ENOMEM; - } - - if (isprint(c)) { - if (space) { - ans[len++] = ' '; - space = false; - } - - ans[len++] = c; - } else if (len > 0) - space = true; - } - - if (len > 0) - ans[len] = '\0'; - else - ans = mfree(ans); - } else { bool dotdotdot = false; size_t left; @@ -232,7 +214,7 @@ int get_process_cmdline(pid_t pid, size_t max_length, bool comm_fallback, char * _cleanup_free_ char *t = NULL; int h; - free(ans); + ans = mfree(ans); if (!comm_fallback) return -ENOENT; @@ -241,37 +223,42 @@ int get_process_cmdline(pid_t pid, size_t max_length, bool comm_fallback, char * if (h < 0) return h; - if (max_length == 0) - ans = strjoin("[", t, "]"); - else { - size_t l; - - l = strlen(t); + size_t l = strlen(t); - if (l + 3 <= max_length) - ans = strjoin("[", t, "]"); - else if (max_length <= 6) { + if (l + 3 <= max_length) { + ans = strjoin("[", t, "]"); + if (!ans) + return -ENOMEM; - ans = new(char, max_length); - if (!ans) - return -ENOMEM; + } else if (max_length <= 6) { + ans = new(char, max_length); + if (!ans) + return -ENOMEM; - memcpy(ans, "[...]", max_length-1); - ans[max_length-1] = 0; - } else { - t[max_length - 6] = 0; + memcpy(ans, "[...]", max_length-1); + ans[max_length-1] = 0; + } else { + t[max_length - 6] = 0; - /* Chop off final spaces */ - delete_trailing_chars(t, WHITESPACE); + /* Chop off final spaces */ + delete_trailing_chars(t, WHITESPACE); - ans = strjoin("[", t, "...]"); - } + ans = strjoin("[", t, "...]"); + if (!ans) + return -ENOMEM; } - if (!ans) - return -ENOMEM; + + *line = TAKE_PTR(ans); + return 0; } - *line = ans; + k = realloc(ans, strlen(ans) + 1); + if (!k) + return -ENOMEM; + + ans = NULL; + *line = k; + return 0; } diff --git a/shared/systemd/src/basic/process-util.h b/shared/systemd/src/basic/process-util.h index 0fad4e1e..0425042f 100644 --- a/shared/systemd/src/basic/process-util.h +++ b/shared/systemd/src/basic/process-util.h @@ -24,8 +24,8 @@ if (_pid_ == 0) { \ _r_ = ("/proc/self/" field); \ } else { \ - _r_ = alloca(STRLEN("/proc/") + DECIMAL_STR_MAX(pid_t) + 1 + sizeof(field)); \ - sprintf((char*) _r_, "/proc/"PID_FMT"/" field, _pid_); \ + _r_ = newa(char, STRLEN("/proc/") + DECIMAL_STR_MAX(pid_t) + 1 + sizeof(field)); \ + sprintf((char*) _r_, "/proc/"PID_FMT"/" field, _pid_); \ } \ _r_; \ }) diff --git a/shared/systemd/src/basic/stat-util.c b/shared/systemd/src/basic/stat-util.c index 8843aba8..686adaf1 100644 --- a/shared/systemd/src/basic/stat-util.c +++ b/shared/systemd/src/basic/stat-util.c @@ -70,13 +70,22 @@ int is_device_node(const char *path) { return !!(S_ISBLK(info.st_mode) || S_ISCHR(info.st_mode)); } -int dir_is_empty(const char *path) { - _cleanup_closedir_ DIR *d; +int dir_is_empty_at(int dir_fd, const char *path) { + _cleanup_close_ int fd = -1; + _cleanup_closedir_ DIR *d = NULL; struct dirent *de; - d = opendir(path); + if (path) + fd = openat(dir_fd, path, O_RDONLY|O_DIRECTORY|O_CLOEXEC); + else + fd = fcntl(fd, F_DUPFD_CLOEXEC, 3); + if (fd < 0) + return -errno; + + d = fdopendir(fd); if (!d) return -errno; + fd = -1; FOREACH_DIRENT(de, d, return -errno) return 0; @@ -343,7 +352,6 @@ int device_path_make_major_minor(mode_t mode, dev_t devno, char **ret) { return -ENOMEM; return 0; - } int device_path_make_canonical(mode_t mode, dev_t devno, char **ret) { diff --git a/shared/systemd/src/basic/stat-util.h b/shared/systemd/src/basic/stat-util.h index 0a08e642..74fb7251 100644 --- a/shared/systemd/src/basic/stat-util.h +++ b/shared/systemd/src/basic/stat-util.h @@ -15,7 +15,10 @@ int is_dir(const char *path, bool follow); int is_dir_fd(int fd); int is_device_node(const char *path); -int dir_is_empty(const char *path); +int dir_is_empty_at(int dir_fd, const char *path); +static inline int dir_is_empty(const char *path) { + return dir_is_empty_at(AT_FDCWD, path); +} static inline int dir_is_populated(const char *path) { int r; diff --git a/shared/systemd/src/basic/string-util.h b/shared/systemd/src/basic/string-util.h index a5b5a16a..38070abb 100644 --- a/shared/systemd/src/basic/string-util.h +++ b/shared/systemd/src/basic/string-util.h @@ -6,6 +6,7 @@ #include <stddef.h> #include <string.h> +#include "alloc-util.h" #include "macro.h" /* What is interpreted as whitespace? */ @@ -111,7 +112,7 @@ char *strjoin_real(const char *x, ...) _sentinel_; size_t _i_; \ for (_i_ = 0; _i_ < ELEMENTSOF(_appendees_) && _appendees_[_i_]; _i_++) \ _len_ += strlen(_appendees_[_i_]); \ - _p_ = _d_ = alloca(_len_ + 1); \ + _p_ = _d_ = newa(char, _len_ + 1); \ for (_i_ = 0; _i_ < ELEMENTSOF(_appendees_) && _appendees_[_i_]; _i_++) \ _p_ = stpcpy(_p_, _appendees_[_i_]); \ *_p_ = 0; \ diff --git a/shared/systemd/src/basic/strv.h b/shared/systemd/src/basic/strv.h index aa4cd4aa..392cab65 100644 --- a/shared/systemd/src/basic/strv.h +++ b/shared/systemd/src/basic/strv.h @@ -156,17 +156,10 @@ void strv_print(char **l); _found; \ }) -#define FOREACH_STRING(x, ...) \ - for (char **_l = ({ \ - char **_ll = STRV_MAKE(__VA_ARGS__); \ - x = _ll ? _ll[0] : NULL; \ - _ll; \ - }); \ - _l && *_l; \ - x = ({ \ - _l ++; \ - _l[0]; \ - })) +#define FOREACH_STRING(x, y, ...) \ + for (char **_l = STRV_MAKE(({ x = y; }), ##__VA_ARGS__); \ + x; \ + x = *(++_l)) char **strv_reverse(char **l); char **strv_shell_escape(char **l, const char *bad); diff --git a/shared/systemd/src/basic/time-util.c b/shared/systemd/src/basic/time-util.c index 4e609a64..9ea0380a 100644 --- a/shared/systemd/src/basic/time-util.c +++ b/shared/systemd/src/basic/time-util.c @@ -1036,6 +1036,15 @@ int parse_sec_fix_0(const char *t, usec_t *ret) { return r; } +int parse_sec_def_infinity(const char *t, usec_t *ret) { + t += strspn(t, WHITESPACE); + if (isempty(t)) { + *ret = USEC_INFINITY; + return 0; + } + return parse_sec(t, ret); +} + static const char* extract_nsec_multiplier(const char *p, nsec_t *multiplier) { static const struct { const char *suffix; diff --git a/shared/systemd/src/basic/time-util.h b/shared/systemd/src/basic/time-util.h index 53163050..a238f691 100644 --- a/shared/systemd/src/basic/time-util.h +++ b/shared/systemd/src/basic/time-util.h @@ -112,6 +112,7 @@ int parse_timestamp(const char *t, usec_t *usec); int parse_sec(const char *t, usec_t *usec); int parse_sec_fix_0(const char *t, usec_t *usec); +int parse_sec_def_infinity(const char *t, usec_t *usec); int parse_time(const char *t, usec_t *usec, usec_t default_unit); int parse_nsec(const char *t, nsec_t *nsec); diff --git a/shared/systemd/src/basic/tmpfile-util.c b/shared/systemd/src/basic/tmpfile-util.c index 06e325f0..019121cb 100644 --- a/shared/systemd/src/basic/tmpfile-util.c +++ b/shared/systemd/src/basic/tmpfile-util.c @@ -10,6 +10,7 @@ #include "hexdecoct.h" #include "macro.h" #include "memfd-util.h" +#include "missing_fcntl.h" #include "missing_syscall.h" #include "path-util.h" #include "process-util.h" diff --git a/shared/systemd/src/basic/util.h b/shared/systemd/src/basic/util.h index f009d37d..dc33d660 100644 --- a/shared/systemd/src/basic/util.h +++ b/shared/systemd/src/basic/util.h @@ -174,12 +174,21 @@ static inline void *mempset(void *s, int c, size_t 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();" |