diff options
| author | Michael Biebl <biebl@debian.org> | 2023-08-09 21:55:35 +0200 |
|---|---|---|
| committer | Michael Biebl <biebl@debian.org> | 2023-08-09 21:55:35 +0200 |
| commit | 05e4a733f2141995181a551854d5df929f084adf (patch) | |
| tree | 83bb937740a6667525ba0df046748ecaa829c269 /src/libnm-glib-aux | |
| parent | 14b0f3a9dc9ea90d60a3b057350fd4d637dc021a (diff) | |
New upstream version 1.44.0 upstream/1.44.0
Diffstat (limited to 'src/libnm-glib-aux')
| -rw-r--r-- | src/libnm-glib-aux/nm-dbus-aux.c | 12 | ||||
| -rw-r--r-- | src/libnm-glib-aux/nm-dbus-aux.h | 2 | ||||
| -rw-r--r-- | src/libnm-glib-aux/nm-dedup-multi.c | 6 | ||||
| -rw-r--r-- | src/libnm-glib-aux/nm-glib.h | 2 | ||||
| -rw-r--r-- | src/libnm-glib-aux/nm-hash-utils.h | 8 | ||||
| -rw-r--r-- | src/libnm-glib-aux/nm-inet-utils.c | 151 | ||||
| -rw-r--r-- | src/libnm-glib-aux/nm-inet-utils.h | 75 | ||||
| -rw-r--r-- | src/libnm-glib-aux/nm-io-utils.c | 100 | ||||
| -rw-r--r-- | src/libnm-glib-aux/nm-io-utils.h | 10 | ||||
| -rw-r--r-- | src/libnm-glib-aux/nm-logging-fwd.h | 18 | ||||
| -rw-r--r-- | src/libnm-glib-aux/nm-macros-internal.h | 212 | ||||
| -rw-r--r-- | src/libnm-glib-aux/nm-prioq.c | 192 | ||||
| -rw-r--r-- | src/libnm-glib-aux/nm-prioq.h | 4 | ||||
| -rw-r--r-- | src/libnm-glib-aux/nm-random-utils.c | 2 | ||||
| -rw-r--r-- | src/libnm-glib-aux/nm-ref-string.h | 2 | ||||
| -rw-r--r-- | src/libnm-glib-aux/nm-shared-utils.c | 363 | ||||
| -rw-r--r-- | src/libnm-glib-aux/nm-shared-utils.h | 56 | ||||
| -rw-r--r-- | src/libnm-glib-aux/nm-str-buf.h | 4 | ||||
| -rw-r--r-- | src/libnm-glib-aux/nm-test-utils.h | 53 | ||||
| -rw-r--r-- | src/libnm-glib-aux/nm-uuid.c | 2 | ||||
| -rw-r--r-- | src/libnm-glib-aux/tests/test-shared-general.c | 180 |
21 files changed, 1150 insertions, 304 deletions
diff --git a/src/libnm-glib-aux/nm-dbus-aux.c b/src/libnm-glib-aux/nm-dbus-aux.c index 3925da55..5c4dbc49 100644 --- a/src/libnm-glib-aux/nm-dbus-aux.c +++ b/src/libnm-glib-aux/nm-dbus-aux.c @@ -454,8 +454,8 @@ _bus_get_cb(GObject *source, GAsyncResult *result, gpointer user_data) /** * nm_g_bus_get_blocking: - * @cancellable: (allow-none): a #GCancellable to abort the operation. - * @error: (allow-none): the error. + * @cancellable: a #GCancellable to abort the operation. + * @error: the error. * * This calls g_bus_get(), but iterates the current (thread-default) GMainContext * until the response is ready. As such, it's similar to g_bus_get_sync(), @@ -605,10 +605,10 @@ dbus_signal_meta_marshal(GClosure *closure, * _nm_dbus_proxy_signal_connect_data: * @proxy: a #GDBusProxy * @signal_name: the D-Bus signal to connect to - * @signature: (allow-none): the signal's type signature (must be a tuple) + * @signature: (nullable): the signal's type signature (must be a tuple) * @c_handler: the signal handler function - * @data: (allow-none): data to pass to @c_handler - * @destroy_data: (allow-none): closure destroy notify for @data + * @data: (nullable): data to pass to @c_handler + * @destroy_data: (nullable): closure destroy notify for @data * @connect_flags: connection flags * * Connects to the D-Bus signal @signal_name on @proxy. @c_handler must be a @@ -690,7 +690,7 @@ _nm_dbus_typecheck_response(GVariant *response, const GVariantType *reply_type, * @proxy: A #GDBusProxy. * @res: A #GAsyncResult obtained from the #GAsyncReadyCallback passed to * g_dbus_proxy_call(). - * @reply_type: (allow-none): the expected type of the reply, or %NULL + * @reply_type: (nullable): the expected type of the reply, or %NULL * @error: Return location for error or %NULL. * * Finishes an operation started with g_dbus_proxy_call(), as with diff --git a/src/libnm-glib-aux/nm-dbus-aux.h b/src/libnm-glib-aux/nm-dbus-aux.h index 5db79c08..5bbb01e9 100644 --- a/src/libnm-glib-aux/nm-dbus-aux.h +++ b/src/libnm-glib-aux/nm-dbus-aux.h @@ -290,7 +290,7 @@ gulong _nm_dbus_proxy_signal_connect_data(GDBusProxy *proxy, * @signal_name: the D-Bus signal to connect to * @signature: the signal's type signature (must be a tuple) * @c_handler: the signal handler function - * @data: (allow-none): data to pass to @c_handler + * @data: (nullable): data to pass to @c_handler * * Simplified version of _nm_dbus_proxy_signal_connect_data() with fewer arguments. * diff --git a/src/libnm-glib-aux/nm-dedup-multi.c b/src/libnm-glib-aux/nm-dedup-multi.c index 625c4ef3..cf2dba90 100644 --- a/src/libnm-glib-aux/nm-dedup-multi.c +++ b/src/libnm-glib-aux/nm-dedup-multi.c @@ -97,7 +97,7 @@ _entry_lookup_head(const NMDedupMultiIndex *self, if (c_list_is_empty(&idx_type->lst_idx_head)) head_entry = NULL; else { - nm_assert(c_list_length(&idx_type->lst_idx_head) == 1); + nm_assert(c_list_length_is(&idx_type->lst_idx_head, 1)); head_entry = c_list_entry(idx_type->lst_idx_head.next, NMDedupMultiHeadEntry, lst_idx); } nm_assert(head_entry == g_hash_table_lookup(self->idx_entries, &stack_entry)); @@ -420,7 +420,7 @@ nm_dedup_multi_index_add(NMDedupMultiIndex *self, * * Adds and object to the index. * - * Return: %TRUE if anything changed, %FALSE if nothing changed. + * Returns: %TRUE if anything changed, %FALSE if nothing changed. */ gboolean nm_dedup_multi_index_add_full(NMDedupMultiIndex *self, @@ -951,7 +951,7 @@ nm_dedup_multi_objs_to_ptr_array_head(const NMDedupMultiHeadEntry *head_entry, /** * nm_dedup_multi_entry_reorder: * @entry: the entry to reorder. It must not be NULL (and tracked in an index). - * @entry_order: (allow-none): an optional other entry. It MUST be in the same + * @entry_order: (nullable): an optional other entry. It MUST be in the same * list as entry. If given, @entry will be ordered after/before @entry_order. * If left at %NULL, @entry will be moved to the front/end of the list. * @order_after: if @entry_order is given, %TRUE means to move @entry after diff --git a/src/libnm-glib-aux/nm-glib.h b/src/libnm-glib-aux/nm-glib.h index 0436d135..9c90d429 100644 --- a/src/libnm-glib-aux/nm-glib.h +++ b/src/libnm-glib-aux/nm-glib.h @@ -500,7 +500,7 @@ _nm_g_variant_new_take_string(char *string) return value; #elif !GLIB_CHECK_VERSION(2, 38, 0) GVariant *value; - GBytes *bytes; + GBytes *bytes; g_return_val_if_fail(string != NULL, NULL); g_return_val_if_fail(g_utf8_validate(string, -1, NULL), NULL); diff --git a/src/libnm-glib-aux/nm-hash-utils.h b/src/libnm-glib-aux/nm-hash-utils.h index e2707fc2..c1306200 100644 --- a/src/libnm-glib-aux/nm-hash-utils.h +++ b/src/libnm-glib-aux/nm-hash-utils.h @@ -185,10 +185,10 @@ nm_hash_update_str(NMHashState *state, const char *str) /* Like nm_hash_update_str(), but restricted to arrays only. nm_hash_update_str() only works * with a @str argument that cannot be NULL. If you have a string pointer, that is never NULL, use * nm_hash_update() instead. */ -#define nm_hash_update_strarr(state, str) \ - (_Generic(&(str), const char(*)[sizeof(str)] \ - : nm_hash_update_str((state), (str)), char(*)[sizeof(str)] \ - : nm_hash_update_str((state), (str)))) +#define nm_hash_update_strarr(state, str) \ + (_Generic(&(str), \ + const char(*)[sizeof(str)]: nm_hash_update_str((state), (str)), \ + char(*)[sizeof(str)]: nm_hash_update_str((state), (str)))) #else #define nm_hash_update_strarr(state, str) nm_hash_update_str((state), (str)) #endif diff --git a/src/libnm-glib-aux/nm-inet-utils.c b/src/libnm-glib-aux/nm-inet-utils.c index 7f710f35..2ee73ad5 100644 --- a/src/libnm-glib-aux/nm-inet-utils.c +++ b/src/libnm-glib-aux/nm-inet-utils.c @@ -6,6 +6,7 @@ #include <netinet/in.h> #include <arpa/inet.h> +#include <dlfcn.h> /*****************************************************************************/ @@ -266,34 +267,118 @@ nm_ip6_addr_same_prefix_cmp(const struct in6_addr *addr_a, /*****************************************************************************/ -static gboolean -_parse_legacy_addr4(const char *text, in_addr_t *out_addr, GError **error) +static int +_inet_aton(const char *text, in_addr_t *out_addr) { - gs_free char *s_free = NULL; - struct in_addr a1; - guint8 bin[sizeof(a1)]; - char *s; - int i; + /* Call inet_aton() via dlopen. + * + * The inet_aton() API is discouraged, and ABI checkers warn when we call + * it. + * + * We want to use this function, but only for testing/asserting. To avoid + * the ABI checker's complain, dlopen() the symbol. This is not used for + * production. + */ + static gpointer fcn_sym = NULL; + static gsize initialized = 0; + int (*fcn)(const char *text, struct in_addr *out_addr); + int r; + in_addr_t a; + + if (g_once_init_enter(&initialized)) { + GModule *module; + + module = g_module_open(NULL, G_MODULE_BIND_LAZY); + if (module) { + if (!g_module_symbol(module, "inet_aton", &fcn_sym)) + g_module_close(module); + else + g_module_make_resident(module); + } + g_once_init_leave(&initialized, 1); + } - if (inet_aton(text, &a1) != 1) { - g_set_error_literal(error, - NM_UTILS_ERROR, - NM_UTILS_ERROR_INVALID_ARGUMENT, - "address invalid according to inet_aton()"); - return FALSE; + if (!fcn_sym) + return -ENOSYS; + + fcn = fcn_sym; + r = fcn(text, (gpointer) &a); + + if (r != 1) + return -EINVAL; + + NM_SET_OUT(out_addr, a); + return 0; +} + +int +nmtst_inet_aton(const char *text, in_addr_t *out_addr) +{ + return _inet_aton(text, out_addr); +} + +static void +_nm_assert_legacy_addr4(const char *text, in_addr_t addr) +{ +#if NM_MORE_ASSERTS > 20 + char buf1[NM_INET_ADDRSTRLEN]; + char buf2[NM_INET_ADDRSTRLEN]; + int r; + in_addr_t a; + + /* Our legacy parser accepted "text" as "addr". + * + * However, we want to ensure that whatever we parse is also parsed by old + * inet_aton(). So we want to be strictly more strict than inet_aton() in + * what we accept. + */ + + r = _inet_aton(text, &a); + + if (r != 0) { + if (r == -ENOSYS) + return; + g_error("inet_aton(\"%s\") failed with \"%s\", but we expected %s", + text, + nm_strerror_native(-r), + nm_inet4_ntop(addr, buf2)); } - /* OK, inet_aton() accepted the format. That's good, because we want - * to accept IPv4 addresses in octal format, like 255.255.000.000. - * That's what "legacy" means here. inet_pton() doesn't accept those. + if (a != addr) { + g_error("inet_aton(\"%s\") parsed %s, but we expected %s", + text, + nm_inet4_ntop(a, buf1), + nm_inet4_ntop(addr, buf2)); + } +#endif +} + +static gboolean +_parse_legacy_addr4(const char *text, in_addr_t *out_addr, GError **error) +{ + gs_free char *s_free = NULL; + union { + guint8 b[sizeof(in_addr_t)]; + in_addr_t a; + } addr; + char *s; + int i; + + /* inet_pton() does strict parsing of IPv4 address. Good. * - * But inet_aton() also ignores trailing garbage and formats with fewer than - * 4 digits. That is just too crazy and we don't do that. Perform additional checks - * and reject some forms that inet_aton() accepted. + * However, inet_aton() used to accept much more relaxed forms (e.g. octal + * and hex numbers, not having 4 components but fewer, ignore any trailing + * garbage). + * + * Some places where we accept input, we want to be slightly more forgiving + * than inet_pton() and accept some (not all!) forms of what inet_aton() + * would accept. For example, we want to accept 255.000.000.000. + * + * We reimplement that below. * * Note that we still should (of course) accept everything that inet_pton() - * accepts. However this code never gets called if inet_pton() succeeds - * (see below, aside the assertion code). */ + * accepts. This is ensured because the caller only calls this function + * after inet_pton() failed. */ if (NM_STRCHAR_ANY(text, ch, (!(ch >= '0' && ch <= '9') && !NM_IN_SET(ch, '.', 'x')))) { /* We only accepts '.', digits, and 'x' for "0x". */ @@ -306,7 +391,7 @@ _parse_legacy_addr4(const char *text, in_addr_t *out_addr, GError **error) s = nm_memdup_maybe_a(300, text, strlen(text) + 1, &s_free); - for (i = 0; i < G_N_ELEMENTS(bin); i++) { + for (i = 0; i < G_N_ELEMENTS(addr.b); i++) { char *current_token = s; gint32 v; @@ -316,7 +401,7 @@ _parse_legacy_addr4(const char *text, in_addr_t *out_addr, GError **error) s++; } - if ((i == G_N_ELEMENTS(bin) - 1) != (s == NULL)) { + if ((i == G_N_ELEMENTS(addr.b) - 1) != (s == NULL)) { /* Exactly for the last digit, we expect to have no more following token. * But this isn't the case. Abort. */ g_set_error(error, @@ -344,26 +429,12 @@ _parse_legacy_addr4(const char *text, in_addr_t *out_addr, GError **error) return FALSE; } - bin[i] = v; + addr.b[i] = v; } - if (memcmp(bin, &a1, sizeof(bin)) != 0) { - /* our parsing did not agree with what inet_aton() gave. Something - * is wrong. Abort. */ - g_set_error( - error, - NM_UTILS_ERROR, - NM_UTILS_ERROR_INVALID_ARGUMENT, - "inet_aton() result 0x%08x differs from computed value 0x%02hhx%02hhx%02hhx%02hhx", - a1.s_addr, - bin[0], - bin[1], - bin[2], - bin[3]); - return FALSE; - } + _nm_assert_legacy_addr4(text, addr.a); - *out_addr = a1.s_addr; + *out_addr = addr.a; return TRUE; } diff --git a/src/libnm-glib-aux/nm-inet-utils.h b/src/libnm-glib-aux/nm-inet-utils.h index 8421929e..65ceeb2e 100644 --- a/src/libnm-glib-aux/nm-inet-utils.h +++ b/src/libnm-glib-aux/nm-inet-utils.h @@ -3,26 +3,28 @@ #ifndef __NM_INET_UTILS_H__ #define __NM_INET_UTILS_H__ -typedef struct _NMIPAddr { - union { - guint8 addr_ptr[sizeof(struct in6_addr)]; - in_addr_t addr4; - struct in_addr addr4_struct; - struct in6_addr addr6; - - /* NMIPAddr is really a union for IP addresses. - * However, as ethernet addresses fit in here nicely, use - * it also for an ethernet MAC address. */ - guint8 ether_addr_octet[6 /*ETH_ALEN*/]; - NMEtherAddr ether_addr; - - guint8 array[sizeof(struct in6_addr)]; - }; +#include "libnm-std-aux/unaligned-fundamental.h" + +typedef union _NMIPAddr { + guint8 addr_ptr[sizeof(struct in6_addr)]; + in_addr_t addr4; + struct in_addr addr4_struct; + struct in6_addr addr6; + + /* This union field only exists, so that it's guaranteed that NMIPAddr has + * a suitable alignment. We use that with nm_ether_addr_zero macro, that + * aliases nm_ip_addr_zero. */ + NMEtherAddr _ether_addr; } NMIPAddr; -#define NM_IP_ADDR_INIT \ - { \ - .array = { 0 } \ +typedef struct _NMIPAddrTyped { + NMIPAddr addr; + gint8 addr_family; +} NMIPAddrTyped; + +#define NM_IP_ADDR_INIT \ + { \ + .addr_ptr = { 0 } \ } #define _NM_IN6ADDR_INIT(a0, a1, a2, a3, a4, a5, a6, a7, a8, a9, aa, ab, ac, ad, ae, af) \ @@ -90,14 +92,15 @@ nm_ip_addr_set(int addr_family, gpointer dst, gconstpointer src) static inline gboolean nm_ip_addr_is_null(int addr_family, gconstpointer addr) { - NMIPAddr a; + struct in6_addr a6; - nm_ip_addr_set(addr_family, &a, addr); + nm_assert(addr); if (NM_IS_IPv4(addr_family)) - return a.addr4 == 0; + return unaligned_read_ne32(addr) == 0; - return IN6_IS_ADDR_UNSPECIFIED(&a.addr6); + memcpy(&a6, addr, sizeof(struct in6_addr)); + return IN6_IS_ADDR_UNSPECIFIED(&a6); } static inline NMIPAddr @@ -117,7 +120,7 @@ nm_ip_addr_init(int addr_family, gconstpointer src) /* ensure all bytes of the union are initialized. If only to make * valgrind happy. */ - memset(&a.array[sizeof(in_addr_t)], 0, sizeof(a) - sizeof(in_addr_t)); + memset(&a.addr_ptr[sizeof(in_addr_t)], 0, sizeof(a) - sizeof(in_addr_t)); } else memcpy(&a, src, sizeof(struct in6_addr)); @@ -143,6 +146,30 @@ nm_ip_addr_from_packed_array(int addr_family, gconstpointer ipaddr_arr, gsize id /*****************************************************************************/ +static inline int +nm_ip_addr_typed_cmp(const NMIPAddrTyped *a, const NMIPAddrTyped *b) +{ + NM_CMP_SELF(a, b); + NM_CMP_FIELD(a, b, addr_family); + NM_CMP_DIRECT_MEMCMP(&a->addr, &b->addr, nm_utils_addr_family_to_size(a->addr_family)); + return 0; +} + +static inline gboolean +nm_ip_addr_typed_equal(const NMIPAddrTyped *a, const NMIPAddrTyped *b) +{ + return nm_ip_addr_typed_cmp(a, b) == 0; +} + +static inline void +nm_ip_addr_typed_hash_update(NMHashState *h, const NMIPAddrTyped *addr) +{ + nm_hash_update_vals(h, addr->addr_family); + nm_hash_update_mem(h, &addr->addr, nm_utils_addr_family_to_size(addr->addr_family)); +} + +/*****************************************************************************/ + static inline guint32 nm_ip4_addr_netmask_to_prefix(in_addr_t subnetmask) { @@ -342,6 +369,8 @@ nm_inet6_ntop_dup(const struct in6_addr *addr) /*****************************************************************************/ +int nmtst_inet_aton(const char *text, in_addr_t *out_addr); + gboolean nm_inet_parse_bin_full(int addr_family, gboolean accept_legacy, const char *text, diff --git a/src/libnm-glib-aux/nm-io-utils.c b/src/libnm-glib-aux/nm-io-utils.c index d1430595..ec016ed8 100644 --- a/src/libnm-glib-aux/nm-io-utils.c +++ b/src/libnm-glib-aux/nm-io-utils.c @@ -21,6 +21,86 @@ /*****************************************************************************/ +int +nm_io_fcntl_getfl(int fd) +{ + int f; + + nm_assert(fd >= 0); + + f = fcntl(fd, F_GETFL, 0); + + /* The caller really must provide a valid FD. For a valid FD, there is not + * reason why this call could fail (or how we could handle the failure). + * + * Unlike plain fcntl(), nm_io_fcntl_getfl() cannot fail. */ + nm_assert(f != -1); + + /* We not only assert that the return value is "!= -1", but that it's not + * negative. Negative flags would be very odd, and not something we would + * expect for a successful call. */ + nm_assert(f >= 0); + + return f; +} + +int +nm_io_fcntl_setfl(int fd, int flags) +{ + int f; + int errsv; + + nm_assert(fd >= 0); + nm_assert(flags >= 0); + + f = fcntl(fd, F_SETFL, flags); + if (f != 0) { + errsv = errno; + + nm_assert(errsv != EBADF); + + return -NM_ERRNO_NATIVE(errsv); + } + + return 0; +} + +int +nm_io_fcntl_setfl_update(int fd, int flags_mask, int flags_value) +{ + int flags_current; + + nm_assert(fd >= 0); + nm_assert(flags_mask > 0); + nm_assert(flags_value >= 0); + nm_assert(((~flags_mask) & flags_value) == 0); + + flags_current = nm_io_fcntl_getfl(fd); + return nm_io_fcntl_setfl(fd, (flags_current & ~flags_mask) | (flags_mask & flags_value)); +} + +void +nm_io_fcntl_setfl_update_nonblock(int fd) +{ + int r; + + nm_assert(fd >= 0); + + r = nm_io_fcntl_setfl_update(fd, O_NONBLOCK, O_NONBLOCK); + + /* nm_io_fcntl_setfl_update() already asserts that it cannot fail with + * EBADF. + * + * In nm_io_fcntl_setfl_update_nonblock() only sts O_NONBLOCK, where we + * don't expect any other error. Kernel should never reject setting this + * flags, and if it did, we have to find out how to handle that. Currently + * we don't handle it and assert against failure. */ + + nm_assert(r == 0); +} + +/*****************************************************************************/ + _nm_printf(4, 5) static int _get_contents_error(GError **error, int errsv, int *out_errsv, @@ -79,7 +159,7 @@ _nm_printf(4, 5) static int _get_contents_error(GError **error, * 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. - * @out_errsv: (allow-none) (out): on error, a positive errno. or zero. + * @out_errsv: (out) (optional): on error, a positive errno. or zero. * @error: * * @@ -276,7 +356,7 @@ nm_utils_fd_get_contents(int fd, * 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. - * @out_errsv: (allow-none) (out): on error, a positive errno. or zero. + * @out_errsv: (out) (optional): on error, a positive errno. or zero. * @error: * * A reimplementation of g_file_get_contents() with a few differences: @@ -444,7 +524,7 @@ nm_utils_file_set_contents(const char *filename, /** * nm_utils_file_stat: * @filename: the filename to stat. - * @out_st: (allow-none) (out): if given, this will be passed to stat(). + * @out_st: (out) (nullable): if given, this will be passed to stat(). * * Just wraps stat() and gives the errno number as function result instead * of setting the errno (though, errno is also set). It's only for convenience @@ -482,13 +562,17 @@ nm_utils_fd_read(int fd, NMStrBuf *out_string) g_return_val_if_fail(fd >= 0, -1); g_return_val_if_fail(out_string, -1); - /* If the buffer size is 0, we allocate NM_UTILS_GET_NEXT_REALLOC_SIZE_1000 (1000 bytes) - * the first time. Afterwards, the buffer grows exponentially. + /* Reserve at least 488+1 bytes of buffer size. That is probably a suitable + * compromise between not wasting too much buffer space and not reading too much. * - * Note that with @buf_available, we always would read as much buffer as we actually - * have reserved. */ - nm_str_buf_maybe_expand(out_string, NM_UTILS_GET_NEXT_REALLOC_SIZE_1000, FALSE); + * Note that when we start with an empty buffer, the first allocation of + * 488+1 bytes will actually allocate 1000 bytes. So if we were to receive + * one byte at a time, we don't need a reallocation for the first 1000-(488+1) + * bytes. Afterwards grows the buffer exponentially. + */ + nm_str_buf_maybe_expand(out_string, NM_UTILS_GET_NEXT_REALLOC_SIZE_488 + 1, FALSE); + /* We always use all the available buffer size. */ buf_available = out_string->allocated - out_string->len; n_read = read(fd, &((nm_str_buf_get_str_unsafe(out_string))[out_string->len]), buf_available); diff --git a/src/libnm-glib-aux/nm-io-utils.h b/src/libnm-glib-aux/nm-io-utils.h index 54018140..0021138f 100644 --- a/src/libnm-glib-aux/nm-io-utils.h +++ b/src/libnm-glib-aux/nm-io-utils.h @@ -10,6 +10,16 @@ /*****************************************************************************/ +int nm_io_fcntl_getfl(int fd); + +int nm_io_fcntl_setfl(int fd, int flags); + +int nm_io_fcntl_setfl_update(int fd, int flags_mask, int flags_value); + +void nm_io_fcntl_setfl_update_nonblock(int fd); + +/*****************************************************************************/ + /** * NMUtilsFileGetContentsFlags: * @NM_UTILS_FILE_GET_CONTENTS_FLAG_NONE: no flag diff --git a/src/libnm-glib-aux/nm-logging-fwd.h b/src/libnm-glib-aux/nm-logging-fwd.h index 72e5723c..f7ab9098 100644 --- a/src/libnm-glib-aux/nm-logging-fwd.h +++ b/src/libnm-glib-aux/nm-logging-fwd.h @@ -177,8 +177,8 @@ extern void _nm_utils_monotonic_timestamp_initialized(const struct timespec *tp, #define _LOGW(...) _NMLOG(_LOGL_WARN, __VA_ARGS__) #define _LOGE(...) _NMLOG(_LOGL_ERR, __VA_ARGS__) -#define _LOGT_ENABLED(...) _NMLOG_ENABLED(_LOGL_TRACE, ##__VA_ARGS__) -#define _LOGD_ENABLED(...) _NMLOG_ENABLED(_LOGL_DEBUG, ##__VA_ARGS__) +#define _LOGT_ENABLED(...) G_UNLIKELY(_NMLOG_ENABLED(_LOGL_TRACE, ##__VA_ARGS__)) +#define _LOGD_ENABLED(...) G_UNLIKELY(_NMLOG_ENABLED(_LOGL_DEBUG, ##__VA_ARGS__)) #define _LOGI_ENABLED(...) _NMLOG_ENABLED(_LOGL_INFO, ##__VA_ARGS__) #define _LOGW_ENABLED(...) _NMLOG_ENABLED(_LOGL_WARN, ##__VA_ARGS__) #define _LOGE_ENABLED(...) _NMLOG_ENABLED(_LOGL_ERR, ##__VA_ARGS__) @@ -192,7 +192,7 @@ extern void _nm_utils_monotonic_timestamp_initialized(const struct timespec *tp, /* _LOGT() and _LOGt() both log with level TRACE, but the latter is disabled by default, * unless building with --with-more-logging. */ #if NM_MORE_LOGGING -#define _LOGt_ENABLED(...) _NMLOG_ENABLED(_LOGL_TRACE, ##__VA_ARGS__) +#define _LOGt_ENABLED(...) G_UNLIKELY(_NMLOG_ENABLED(_LOGL_TRACE, ##__VA_ARGS__)) #define _LOGt(...) _NMLOG(_LOGL_TRACE, __VA_ARGS__) #define _LOGt_err(errsv, ...) _NMLOG_err(errsv, _LOGL_TRACE, __VA_ARGS__) #else @@ -233,8 +233,8 @@ extern void _nm_utils_monotonic_timestamp_initialized(const struct timespec *tp, #define _LOG2W(...) _NMLOG2(_LOGL_WARN, __VA_ARGS__) #define _LOG2E(...) _NMLOG2(_LOGL_ERR, __VA_ARGS__) -#define _LOG2T_ENABLED(...) _NMLOG2_ENABLED(_LOGL_TRACE, ##__VA_ARGS__) -#define _LOG2D_ENABLED(...) _NMLOG2_ENABLED(_LOGL_DEBUG, ##__VA_ARGS__) +#define _LOG2T_ENABLED(...) G_UNLIKELY(_NMLOG2_ENABLED(_LOGL_TRACE, ##__VA_ARGS__)) +#define _LOG2D_ENABLED(...) G_UNLIKELY(_NMLOG2_ENABLED(_LOGL_DEBUG, ##__VA_ARGS__)) #define _LOG2I_ENABLED(...) _NMLOG2_ENABLED(_LOGL_INFO, ##__VA_ARGS__) #define _LOG2W_ENABLED(...) _NMLOG2_ENABLED(_LOGL_WARN, ##__VA_ARGS__) #define _LOG2E_ENABLED(...) _NMLOG2_ENABLED(_LOGL_ERR, ##__VA_ARGS__) @@ -246,7 +246,7 @@ extern void _nm_utils_monotonic_timestamp_initialized(const struct timespec *tp, #define _LOG2E_err(errsv, ...) _NMLOG2_err(errsv, _LOGL_ERR, __VA_ARGS__) #if NM_MORE_LOGGING -#define _LOG2t_ENABLED(...) _NMLOG2_ENABLED(_LOGL_TRACE, ##__VA_ARGS__) +#define _LOG2t_ENABLED(...) G_UNLIKELY(_NMLOG2_ENABLED(_LOGL_TRACE, ##__VA_ARGS__)) #define _LOG2t(...) _NMLOG2(_LOGL_TRACE, __VA_ARGS__) #define _LOG2t_err(errsv, ...) _NMLOG2_err(errsv, _LOGL_TRACE, __VA_ARGS__) #else @@ -278,8 +278,8 @@ extern void _nm_utils_monotonic_timestamp_initialized(const struct timespec *tp, #define _LOG3W(...) _NMLOG3(_LOGL_WARN, __VA_ARGS__) #define _LOG3E(...) _NMLOG3(_LOGL_ERR, __VA_ARGS__) -#define _LOG3T_ENABLED(...) _NMLOG3_ENABLED(_LOGL_TRACE, ##__VA_ARGS__) -#define _LOG3D_ENABLED(...) _NMLOG3_ENABLED(_LOGL_DEBUG, ##__VA_ARGS__) +#define _LOG3T_ENABLED(...) G_UNLIKELY(_NMLOG3_ENABLED(_LOGL_TRACE, ##__VA_ARGS__)) +#define _LOG3D_ENABLED(...) G_UNLIKELY(_NMLOG3_ENABLED(_LOGL_DEBUG, ##__VA_ARGS__)) #define _LOG3I_ENABLED(...) _NMLOG3_ENABLED(_LOGL_INFO, ##__VA_ARGS__) #define _LOG3W_ENABLED(...) _NMLOG3_ENABLED(_LOGL_WARN, ##__VA_ARGS__) #define _LOG3E_ENABLED(...) _NMLOG3_ENABLED(_LOGL_ERR, ##__VA_ARGS__) @@ -291,7 +291,7 @@ extern void _nm_utils_monotonic_timestamp_initialized(const struct timespec *tp, #define _LOG3E_err(errsv, ...) _NMLOG3_err(errsv, _LOGL_ERR, __VA_ARGS__) #if NM_MORE_LOGGING -#define _LOG3t_ENABLED(...) _NMLOG3_ENABLED(_LOGL_TRACE, ##__VA_ARGS__) +#define _LOG3t_ENABLED(...) G_UNLIKELY(_NMLOG3_ENABLED(_LOGL_TRACE, ##__VA_ARGS__)) #define _LOG3t(...) _NMLOG3(_LOGL_TRACE, __VA_ARGS__) #define _LOG3t_err(errsv, ...) _NMLOG3_err(errsv, _LOGL_TRACE, __VA_ARGS__) #else diff --git a/src/libnm-glib-aux/nm-macros-internal.h b/src/libnm-glib-aux/nm-macros-internal.h index 0534ee5d..9972dc44 100644 --- a/src/libnm-glib-aux/nm-macros-internal.h +++ b/src/libnm-glib-aux/nm-macros-internal.h @@ -239,7 +239,7 @@ _nm_auto_freev(gpointer ptr) /** * NM_G_ERROR_MSG: - * @error: (allow-none): the #GError instance + * @error: (nullable): the #GError instance * * All functions must follow the convention that when they * return a failure, they must also set the GError to a valid @@ -259,41 +259,41 @@ NM_G_ERROR_MSG(GError *error) #if _NM_CC_SUPPORT_GENERIC #define _NM_CONSTCAST_FULL_1(type, obj_expr, obj) \ - (_Generic ((obj_expr), \ - const void *: ((const type *) (obj)), \ - void *: (( type *) (obj)), \ - const type *: ((const type *) (obj)), \ - type *: (( type *) (obj)))) + (_Generic((obj_expr), \ + const void *: ((const type *) (obj)), \ + void *: ((type *) (obj)), \ + const type *: ((const type *) (obj)), \ + type *: ((type *) (obj)))) #define _NM_CONSTCAST_FULL_2(type, obj_expr, obj, alias_type2) \ - (_Generic ((obj_expr), \ - const void *: ((const type *) (obj)), \ - void *: (( type *) (obj)), \ - const alias_type2 *: ((const type *) (obj)), \ - alias_type2 *: (( type *) (obj)), \ - const type *: ((const type *) (obj)), \ - type *: (( type *) (obj)))) + (_Generic((obj_expr), \ + const void *: ((const type *) (obj)), \ + void *: ((type *) (obj)), \ + const alias_type2 *: ((const type *) (obj)), \ + alias_type2 *: ((type *) (obj)), \ + const type *: ((const type *) (obj)), \ + type *: ((type *) (obj)))) #define _NM_CONSTCAST_FULL_3(type, obj_expr, obj, alias_type2, alias_type3) \ - (_Generic ((obj_expr), \ - const void *: ((const type *) (obj)), \ - void *: (( type *) (obj)), \ - const alias_type2 *: ((const type *) (obj)), \ - alias_type2 *: (( type *) (obj)), \ - const alias_type3 *: ((const type *) (obj)), \ - alias_type3 *: (( type *) (obj)), \ - const type *: ((const type *) (obj)), \ - type *: (( type *) (obj)))) + (_Generic((obj_expr), \ + const void *: ((const type *) (obj)), \ + void *: ((type *) (obj)), \ + const alias_type2 *: ((const type *) (obj)), \ + alias_type2 *: ((type *) (obj)), \ + const alias_type3 *: ((const type *) (obj)), \ + alias_type3 *: ((type *) (obj)), \ + const type *: ((const type *) (obj)), \ + type *: ((type *) (obj)))) #define _NM_CONSTCAST_FULL_4(type, obj_expr, obj, alias_type2, alias_type3, alias_type4) \ - (_Generic ((obj_expr), \ - const void *: ((const type *) (obj)), \ - void *: (( type *) (obj)), \ - const alias_type2 *: ((const type *) (obj)), \ - alias_type2 *: (( type *) (obj)), \ - const alias_type3 *: ((const type *) (obj)), \ - alias_type3 *: (( type *) (obj)), \ - const alias_type4 *: ((const type *) (obj)), \ - alias_type4 *: (( type *) (obj)), \ - const type *: ((const type *) (obj)), \ - type *: (( type *) (obj)))) + (_Generic((obj_expr), \ + const void *: ((const type *) (obj)), \ + void *: ((type *) (obj)), \ + const alias_type2 *: ((const type *) (obj)), \ + alias_type2 *: ((type *) (obj)), \ + const alias_type3 *: ((const type *) (obj)), \ + alias_type3 *: ((type *) (obj)), \ + const alias_type4 *: ((const type *) (obj)), \ + alias_type4 *: ((type *) (obj)), \ + const type *: ((const type *) (obj)), \ + type *: ((type *) (obj)))) #define _NM_CONSTCAST_FULL_x(type, obj_expr, obj, n, ...) \ (_NM_CONSTCAST_FULL_##n(type, obj_expr, obj, ##__VA_ARGS__)) #define _NM_CONSTCAST_FULL_y(type, obj_expr, obj, n, ...) \ @@ -308,18 +308,18 @@ NM_G_ERROR_MSG(GError *error) #if _NM_CC_SUPPORT_GENERIC #define NM_UNCONST_PTR(type, arg) \ - _Generic((arg), const type * : ((type *) (arg)), type * : ((type *) (arg))) + _Generic((arg), const type *: ((type *) (arg)), type *: ((type *) (arg))) #else #define NM_UNCONST_PTR(type, arg) ((type *) (arg)) #endif #if _NM_CC_SUPPORT_GENERIC -#define NM_UNCONST_PPTR(type, arg) \ - _Generic ((arg), \ - const type * *: ((type **) (arg)), \ - type * *: ((type **) (arg)), \ - const type *const*: ((type **) (arg)), \ - type *const*: ((type **) (arg))) +#define NM_UNCONST_PPTR(type, arg) \ + _Generic((arg), \ + const type **: ((type **) (arg)), \ + type **: ((type **) (arg)), \ + const type *const *: ((type **) (arg)), \ + type *const *: ((type **) (arg))) #else #define NM_UNCONST_PPTR(type, arg) ((type **) (arg)) #endif @@ -377,30 +377,29 @@ NM_G_ERROR_MSG(GError *error) * These macros do the cast, but they only accept a compatible input * type, otherwise they will fail compilation. */ -#define NM_CAST_STRV_MC(value) \ - (_Generic ((value), \ - const char * *: (const char * *) (value), \ - char * *: (const char * *) (value), \ - void *: (const char * *) (value))) -#define NM_CAST_STRV_CC(value) \ - (_Generic ((value), \ - const char *const*: (const char *const*) (value), \ - const char * *: (const char *const*) (value), \ - char *const*: (const char *const*) (value), \ - char * *: (const char *const*) (value), \ - const void *: (const char *const*) (value), \ - void *: (const char *const*) (value))) +#define NM_CAST_STRV_MC(value) \ + (_Generic((value), \ + const char **: (const char **) (value), \ + char **: (const char **) (value), \ + void *: (const char **) (value))) +#define NM_CAST_STRV_CC(value) \ + (_Generic((value), \ + const char *const *: (const char *const *) (value), \ + const char **: (const char *const *) (value), \ + char *const *: (const char *const *) (value), \ + char **: (const char *const *) (value), \ + const void *: (const char *const *) (value), \ + void *: (const char *const *) (value))) #else #define NM_CAST_STRV_MC(value) ((const char **) (value)) #define NM_CAST_STRV_CC(value) ((const char *const *) (value)) #endif #if _NM_CC_SUPPORT_GENERIC -#define NM_PROPAGATE_CONST(test_expr, ptr) \ - (_Generic ((test_expr), \ - const typeof (*(test_expr)) *: ((const typeof (*(ptr)) *) (ptr)), \ - default: (_Generic ((test_expr), \ - typeof (*(test_expr)) *: (ptr))))) +#define NM_PROPAGATE_CONST(test_expr, ptr) \ + (_Generic((test_expr), \ + const typeof(*(test_expr)) *: ((const typeof(*(ptr)) *) (ptr)), \ + default: (_Generic((test_expr), typeof(*(test_expr)) *: (ptr))))) #else #define NM_PROPAGATE_CONST(test_expr, ptr) (ptr) #endif @@ -1023,34 +1022,43 @@ nm_g_variant_equal(GVariant *a, GVariant *b) #define NM_FLAGS_SET(flags, val) \ ({ \ const typeof(flags) _flags = (flags); \ - const typeof(flags) _val = (val); \ + typeof(_flags) _val = (val); \ \ - _flags | _val; \ + (void) (FALSE && (flags) == (val)); \ + \ + (_flags | _val); \ }) #define NM_FLAGS_UNSET(flags, val) \ ({ \ const typeof(flags) _flags = (flags); \ - const typeof(flags) _val = (val); \ + typeof(_flags) _val = (val); \ + \ + (void) (FALSE && (flags) == (val)); \ \ - _flags & (~_val); \ + (_flags & (~_val)); \ }) -#define NM_FLAGS_ASSIGN(flags, val, assign) \ - ({ \ - const typeof(flags) _flags = (flags); \ - const typeof(flags) _val = (val); \ - \ - (assign) ? _flags | (_val) : _flags & (~_val); \ +#define NM_FLAGS_ASSIGN(flags, val, assign) \ + ({ \ + const typeof(flags) _flags = (flags); \ + typeof(_flags) _val = (val); \ + \ + (void) (FALSE && (flags) == (val)); \ + \ + (assign) ? (_flags | _val) : (_flags & (~_val)); \ }) -#define NM_FLAGS_ASSIGN_MASK(flags, mask, val) \ - ({ \ - const typeof(flags) _flags = (flags); \ - const typeof(flags) _mask = (mask); \ - const typeof(flags) _val = (val); \ - \ - ((_flags & ~_mask) | (_mask & _val)); \ +#define NM_FLAGS_ASSIGN_MASK(flags, mask, val) \ + ({ \ + const typeof(flags) _flags = (flags); \ + typeof(_flags) _mask = (mask); \ + typeof(_flags) _val = (val); \ + \ + (void) (FALSE && (flags) == (val)); \ + (void) (FALSE && (flags) == (mask)); \ + \ + ((_flags & (~_mask)) | (_val & _mask)); \ }) /*****************************************************************************/ @@ -1078,11 +1086,20 @@ nm_g_variant_equal(GVariant *a, GVariant *b) /* mirrors g_ascii_isspace() and what we consider spaces in general. */ #define NM_ASCII_SPACES " \n\t\r\f" -/* Like NM_ASCII_SPACES, but without "\f" (0x0c, Formfeed Page Break). - * This is what for example systemd calls WHITESPACE and what it uses to tokenize - * the kernel command line. */ +/* Like NM_ASCII_SPACES, but without "\f" (0x0c, Formfeed Page Break). This is + * what for example systemd calls WHITESPACE and what it uses to tokenize the + * kernel command line. */ #define NM_ASCII_WHITESPACES " \n\t\r" +/* mirrors <ctype.h>'s isspace() with C locale. It's like NM_ASCII_SPACES but + * additionally also considers '\v' (vertical tab). */ +#define NM_ASCII_SPACES_CTYPE NM_ASCII_SPACES "\v" + +/* mirrors kernel's isspace() from "include/linux/ctype.h", which treats as + * space the common ASCII spaces, including '\v' (vertical tab), but also + * '\240' (non-breaking space, NBSP in Latin-1). */ +#define NM_ASCII_SPACES_KERNEL NM_ASCII_SPACES_CTYPE "\240" + static inline gboolean nm_ascii_is_whitespace(char ch) { @@ -1092,6 +1109,13 @@ nm_ascii_is_whitespace(char ch) return NM_IN_SET(ch, ' ', '\n', '\t', '\r'); } +static inline gboolean +nm_ascii_is_space_kernel(char ch) +{ + /* Checks whether @ch is in NM_ASCII_SPACES_KERNEL. */ + return NM_IN_SET(ch, ' ', '\n', '\t', '\r', '\f', '\v', '\240'); +} + #define NM_ASCII_NEWLINE "\n\r" static inline gboolean @@ -1490,23 +1514,21 @@ _nm_strndup_a_step(char *s, const char *str, gsize len) * * Instead, this generic macro is supposed to handle all integers correctly. */ #if _NM_CC_SUPPORT_GENERIC -#define nm_strdup_int(val) \ - _Generic((val), char \ - : g_strdup_printf("%d", (int) (val)), \ - \ - signed char \ - : g_strdup_printf("%d", (signed) (val)), signed short \ - : g_strdup_printf("%d", (signed) (val)), signed \ - : g_strdup_printf("%d", (signed) (val)), signed long \ - : g_strdup_printf("%ld", (signed long) (val)), signed long long \ - : g_strdup_printf("%lld", (signed long long) (val)), \ - \ - unsigned char \ - : g_strdup_printf("%u", (unsigned) (val)), unsigned short \ - : g_strdup_printf("%u", (unsigned) (val)), unsigned \ - : g_strdup_printf("%u", (unsigned) (val)), unsigned long \ - : g_strdup_printf("%lu", (unsigned long) (val)), unsigned long long \ - : g_strdup_printf("%llu", (unsigned long long) (val))) +#define nm_strdup_int(val) \ + _Generic((val), \ + char: g_strdup_printf("%d", (int) (val)), \ + \ + signed char: g_strdup_printf("%d", (signed) (val)), \ + signed short: g_strdup_printf("%d", (signed) (val)), \ + signed: g_strdup_printf("%d", (signed) (val)), \ + signed long: g_strdup_printf("%ld", (signed long) (val)), \ + signed long long: g_strdup_printf("%lld", (signed long long) (val)), \ + \ + unsigned char: g_strdup_printf("%u", (unsigned) (val)), \ + unsigned short: g_strdup_printf("%u", (unsigned) (val)), \ + unsigned: g_strdup_printf("%u", (unsigned) (val)), \ + unsigned long: g_strdup_printf("%lu", (unsigned long) (val)), \ + unsigned long long: g_strdup_printf("%llu", (unsigned long long) (val))) #else #define nm_strdup_int(val) \ ((sizeof(val) == sizeof(guint64) && ((typeof(val)) -1) > 0) \ diff --git a/src/libnm-glib-aux/nm-prioq.c b/src/libnm-glib-aux/nm-prioq.c index 3448dcd9..e74b2b99 100644 --- a/src/libnm-glib-aux/nm-prioq.c +++ b/src/libnm-glib-aux/nm-prioq.c @@ -16,15 +16,38 @@ #include "nm-prioq.h" -#include <errno.h> -#include <stdlib.h> - /*****************************************************************************/ -struct _NMPrioqItem { +typedef struct _NMPrioqItem { void *data; unsigned *idx; -}; +} PrioqItem; + +/*****************************************************************************/ + +#define _nm_assert_q(q) \ + G_STMT_START \ + { \ + const NMPrioq *const _q2 = (q); \ + \ + nm_assert(_q2); \ + nm_assert(_q2->_priv.n_items == 0 || _q2->_priv.items); \ + nm_assert(_q2->_priv.compare_func); \ + } \ + G_STMT_END + +#define _nm_assert_item(q, item) \ + G_STMT_START \ + { \ + const NMPrioq *const _q = (q); \ + const PrioqItem *const _item = (item); \ + \ + _nm_assert_q(_q); \ + \ + nm_assert(_item >= _q->_priv.items); \ + nm_assert(_item < &_q->_priv.items[_q->_priv.n_items]); \ + } \ + G_STMT_END /*****************************************************************************/ @@ -72,6 +95,15 @@ nm_prioq_destroy(NMPrioq *q) if (!q || !q->_priv.compare_func) return; + _nm_assert_q(q); + + while (q->_priv.n_items > 0) { + PrioqItem *i = &q->_priv.items[--q->_priv.n_items]; + + if (i->idx) + *i->idx = NM_PRIOQ_IDX_NULL; + } + free(q->_priv.items); q->_priv.compare_func = NULL; } @@ -81,8 +113,7 @@ nm_prioq_destroy(NMPrioq *q) static int compare(NMPrioq *q, unsigned a, unsigned b) { - nm_assert(q); - nm_assert(q->_priv.compare_func); + _nm_assert_q(q); nm_assert(a != b); nm_assert(a < q->_priv.n_items); nm_assert(b < q->_priv.n_items); @@ -99,15 +130,13 @@ compare(NMPrioq *q, unsigned a, unsigned b) static void swap(NMPrioq *q, unsigned j, unsigned k) { - nm_assert(q); + _nm_assert_q(q); nm_assert(j < q->_priv.n_items); nm_assert(k < q->_priv.n_items); - nm_assert(!q->_priv.items[j].idx || *(q->_priv.items[j].idx) == j); nm_assert(!q->_priv.items[k].idx || *(q->_priv.items[k].idx) == k); - NM_SWAP(&q->_priv.items[j].data, &q->_priv.items[k].data); - NM_SWAP(&q->_priv.items[j].idx, &q->_priv.items[k].idx); + NM_SWAP(&q->_priv.items[j], &q->_priv.items[k]); if (q->_priv.items[j].idx) *q->_priv.items[j].idx = j; @@ -119,7 +148,7 @@ swap(NMPrioq *q, unsigned j, unsigned k) static unsigned shuffle_up(NMPrioq *q, unsigned idx) { - nm_assert(q); + _nm_assert_q(q); nm_assert(idx < q->_priv.n_items); while (idx > 0) { @@ -140,7 +169,7 @@ shuffle_up(NMPrioq *q, unsigned idx) static unsigned shuffle_down(NMPrioq *q, unsigned idx) { - nm_assert(q); + _nm_assert_q(q); for (;;) { unsigned j; @@ -184,19 +213,21 @@ nm_prioq_put(NMPrioq *q, void *data, unsigned *idx) { unsigned k; - nm_assert(q); + _nm_assert_q(q); + nm_assert(q->_priv.n_items < G_MAXUINT); - if (q->_priv.n_items >= q->_priv.n_allocated) { + if (G_UNLIKELY(q->_priv.n_items >= q->_priv.n_allocated)) { q->_priv.n_allocated = NM_MAX((q->_priv.n_items + 1u) * 2u, 16u); - q->_priv.items = g_renew(struct _NMPrioqItem, q->_priv.items, q->_priv.n_allocated); + q->_priv.items = g_renew(PrioqItem, q->_priv.items, q->_priv.n_allocated); } k = q->_priv.n_items++; - q->_priv.items[k] = (struct _NMPrioqItem){ + q->_priv.items[k] = (PrioqItem){ .data = data, .idx = idx, }; + if (idx) *idx = k; @@ -204,71 +235,88 @@ nm_prioq_put(NMPrioq *q, void *data, unsigned *idx) } static void -remove_item(NMPrioq *q, struct _NMPrioqItem *i) +remove_item(NMPrioq *q, PrioqItem *i) { - struct _NMPrioqItem *l; - unsigned k; + PrioqItem *l; + unsigned k; - nm_assert(q); - nm_assert(i); - nm_assert(q->_priv.n_items > 0); - nm_assert(i >= q->_priv.items); - nm_assert(i < &q->_priv.items[q->_priv.n_items]); + _nm_assert_item(q, i); - l = &q->_priv.items[q->_priv.n_items - 1u]; + if (i->idx) + *i->idx = NM_PRIOQ_IDX_NULL; + + q->_priv.n_items--; + + l = &q->_priv.items[q->_priv.n_items]; if (i == l) { - /* Last entry, let's just remove it */ - q->_priv.n_items--; + /* Last entry, nothing to do. */ return; } - /* Not last entry, let's replace the last entry with - * this one, and reshuffle */ + /* Not last entry, let's replace this entry with the last one, and + * reshuffle */ + k = i - q->_priv.items; *i = *l; + if (i->idx) *i->idx = k; - q->_priv.n_items--; k = shuffle_down(q, k); shuffle_up(q, k); } -_nm_pure static struct _NMPrioqItem * +static PrioqItem * find_item(NMPrioq *q, void *data, unsigned *idx) { - struct _NMPrioqItem *i; - - nm_assert(q); + PrioqItem *i; - if (q->_priv.n_items <= 0) - return NULL; + _nm_assert_q(q); - if (idx) { - if (*idx == NM_PRIOQ_IDX_NULL || *idx >= q->_priv.n_items) - return NULL; - - i = &q->_priv.items[*idx]; - if (i->data == data) - return i; - } else { + if (G_UNLIKELY(!idx)) { + /* We allow using NMPrioq without "idx". In that case, it does a linear + * search for the data. */ for (i = q->_priv.items; i < &q->_priv.items[q->_priv.n_items]; i++) { if (i->data == data) return i; } + return NULL; } - return NULL; + /* If the user however provides an "idx" pointer, then we assert that it is + * consistent. That is, if data is not in the queue, then we require that + * "*idx" is NM_PRIOQ_IDX_NULL, and otherwise we require that we really + * find "data" at index "*idx". + * + * This means, when the user calls nm_prioq_{remove,update,reshuffle}() + * with an "idx", then they must make sure that the index is consistent. + * Usually this means they are required to initialize the index to + * NM_PRIOQ_IDX_NULL while the data is not in the heap. + * + * This is done to assert more, and requires a stricter usage of the API + * (in the hope to find misuses of the index). */ + + if (*idx >= q->_priv.n_items) { + nm_assert(*idx == NM_PRIOQ_IDX_NULL); + return NULL; + } + + i = &q->_priv.items[*idx]; + + if (i->data != data) + return nm_assert_unreachable_val(NULL); + + return i; } gboolean nm_prioq_remove(NMPrioq *q, void *data, unsigned *idx) { - struct _NMPrioqItem *i; + PrioqItem *i; - nm_assert(q); + _nm_assert_q(q); i = find_item(q, data, idx); if (!i) @@ -278,28 +326,60 @@ nm_prioq_remove(NMPrioq *q, void *data, unsigned *idx) return TRUE; } +static void +reshuffle_item(NMPrioq *q, PrioqItem *i) +{ + unsigned k; + + _nm_assert_item(q, i); + + k = i - q->_priv.items; + k = shuffle_down(q, k); + shuffle_up(q, k); +} + gboolean nm_prioq_reshuffle(NMPrioq *q, void *data, unsigned *idx) { - struct _NMPrioqItem *i; - unsigned k; + PrioqItem *i; - nm_assert(q); + _nm_assert_q(q); i = find_item(q, data, idx); if (!i) return FALSE; - k = i - q->_priv.items; - k = shuffle_down(q, k); - shuffle_up(q, k); + reshuffle_item(q, i); return TRUE; } +void +nm_prioq_update(NMPrioq *q, void *data, unsigned *idx, bool queued /* or else remove */) +{ + PrioqItem *i; + + _nm_assert_q(q); + + i = find_item(q, data, idx); + + if (!i) { + if (queued) + nm_prioq_put(q, data, idx); + return; + } + + if (!queued) { + remove_item(q, i); + return; + } + + reshuffle_item(q, i); +} + void * nm_prioq_peek_by_index(NMPrioq *q, unsigned idx) { - nm_assert(q); + _nm_assert_q(q); if (idx >= q->_priv.n_items) return NULL; @@ -312,7 +392,7 @@ nm_prioq_pop(NMPrioq *q) { void *data; - nm_assert(q); + _nm_assert_q(q); if (q->_priv.n_items <= 0) return NULL; diff --git a/src/libnm-glib-aux/nm-prioq.h b/src/libnm-glib-aux/nm-prioq.h index 918c6447..51e5b059 100644 --- a/src/libnm-glib-aux/nm-prioq.h +++ b/src/libnm-glib-aux/nm-prioq.h @@ -43,6 +43,8 @@ void nm_prioq_put(NMPrioq *q, void *data, unsigned *idx); gboolean nm_prioq_remove(NMPrioq *q, void *data, unsigned *idx); gboolean nm_prioq_reshuffle(NMPrioq *q, void *data, unsigned *idx); +void nm_prioq_update(NMPrioq *q, void *data, unsigned *idx, bool queued /* or else remove */); + void *nm_prioq_peek_by_index(NMPrioq *q, unsigned idx) _nm_pure; static inline void * @@ -53,7 +55,7 @@ nm_prioq_peek(NMPrioq *q) void *nm_prioq_pop(NMPrioq *q); -#define NM_PRIOQ_FOREACH_ITEM(q, p) for (unsigned _i = 0; (p = nm_prioq_peek_by_index(q, _i)); _i++) +#define nm_prioq_for_each(q, p) for (unsigned _i = 0; (p = nm_prioq_peek_by_index((q), _i)); _i++) _nm_pure static inline unsigned nm_prioq_size(NMPrioq *q) diff --git a/src/libnm-glib-aux/nm-random-utils.c b/src/libnm-glib-aux/nm-random-utils.c index 2050d2f9..bbc5536a 100644 --- a/src/libnm-glib-aux/nm-random-utils.c +++ b/src/libnm-glib-aux/nm-random-utils.c @@ -301,7 +301,7 @@ _bad_random_bytes(guint8 *buf, gsize n) * nm_random_get_bytes_full: * @p: the buffer to fill * @n: the number of bytes to write to @p. - * @out_high_quality: (allow-none) (out): whether the returned + * @out_high_quality: (out) (optional): whether the returned * random bytes are of high quality. * * - will never block diff --git a/src/libnm-glib-aux/nm-ref-string.h b/src/libnm-glib-aux/nm-ref-string.h index 1b0cabf2..c7cfe87f 100644 --- a/src/libnm-glib-aux/nm-ref-string.h +++ b/src/libnm-glib-aux/nm-ref-string.h @@ -53,7 +53,7 @@ nmtst_ref_string_find(const char *cstr) { /* WARNING: only use for testing. See nmtst_ref_string_find_len() why. */ if (!cstr) - return FALSE; + return NULL; return nmtst_ref_string_find_len(cstr, strlen(cstr)); } diff --git a/src/libnm-glib-aux/nm-shared-utils.c b/src/libnm-glib-aux/nm-shared-utils.c index 702a63e9..34a3af20 100644 --- a/src/libnm-glib-aux/nm-shared-utils.c +++ b/src/libnm-glib-aux/nm-shared-utils.c @@ -20,7 +20,9 @@ #include "c-list/src/c-list.h" #include "nm-errno.h" +#include "nm-time-utils.h" #include "nm-str-buf.h" +#include "nm-time-utils.h" G_STATIC_ASSERT(sizeof(NMEtherAddr) == 6); G_STATIC_ASSERT(_nm_alignof(NMEtherAddr) == 1); @@ -64,7 +66,7 @@ nm_ether_addr_from_string(NMEtherAddr *addr, const char *str) * * Checks if only the bottom 64bits of the address are set. * - * Return value: %TRUE or %FALSE + * Returns: %TRUE or %FALSE */ gboolean _nm_utils_inet6_is_token(const struct in6_addr *in6addr) @@ -437,7 +439,7 @@ nm_g_bytes_new_from_variant_ay(GVariant *var) /** * nm_g_bytes_equal_mem: - * @bytes: (allow-none): a #GBytes array to compare. Note that + * @bytes: (nullable): a #GBytes array to compare. Note that * %NULL is treated like an #GBytes array of length zero. * @mem_data: the data pointer with @mem_len bytes * @mem_len: the length of the data pointer @@ -692,7 +694,7 @@ nm_strdict_to_variant_asv(GHashTable *strdict) * nm_strquote: * @buf: the output buffer of where to write the quoted @str argument. * @buf_len: the size of @buf. - * @str: (allow-none): the string to quote. + * @str: (nullable): the string to quote. * * Writes @str to @buf with quoting. The resulting buffer * is always NUL terminated, unless @buf_len is zero. @@ -1764,11 +1766,11 @@ nm_utils_escaped_tokens_escape_full(const char *str, * nm_utils_escaped_tokens_options_split: * @str: the src string. This string will be modified in-place. * The output values will point into @str. - * @out_key: (allow-none): the returned output key. This will always be set to @str - * itself. @str will be modified to contain only the unescaped, truncated - * key name. - * @out_val: returns the parsed (and unescaped) value or %NULL, if @str contains - * no '=' delimiter. + * @out_key: (out) (nullable): the returned output key. This will always be set + * to @str itself. @str will be modified to contain only the unescaped, + * truncated key name. + * @out_val: (out) (nullable): returns the parsed (and unescaped) value or + * %NULL, if @str contains no '=' delimiter. * * Honors backslash escaping to parse @str as "key=value" pairs. Optionally, if no '=' * is present, @out_val will be returned as %NULL. Backslash can be used to escape @@ -2224,7 +2226,7 @@ nm_utils_error_is_notfound(GError *error) * @object: the target object * @property_name: the property name * @value: the #GValue to set - * @error: (allow-none): optional error argument + * @error: optional error argument * * A reimplementation of g_object_set_property(), but instead * returning an error instead of logging a warning. All g_object_set*() @@ -2524,7 +2526,7 @@ _str_buf_append_c_escape_octal(NMStrBuf *strbuf, char ch) /** * nm_utils_buf_utf8safe_unescape: - * @str: (allow-none): the string to unescape. The string itself is a NUL terminated + * @str: (nullable): the string to unescape. The string itself is a NUL terminated * ASCII string, that can have C-style backslash escape sequences (which * are to be unescaped). Non-ASCII characters (e.g. UTF-8) are taken verbatim, so * it doesn't care that this string is UTF-8. However, usually this is a UTF-8 encoded @@ -2755,13 +2757,16 @@ nm_utils_buf_utf8safe_escape(gconstpointer buf, if (g_utf8_validate(str, buflen, &p) && nul_terminated) { /* note that g_utf8_validate() does not allow NUL character inside @str. Good. * We can treat @str like a NUL terminated string. */ - if (!NM_STRCHAR_ANY(str, - ch, - (ch == '\\' - || (NM_FLAGS_HAS(flags, NM_UTILS_STR_UTF8_SAFE_FLAG_ESCAPE_CTRL) - && nm_ascii_is_ctrl_or_del(ch)) - || (NM_FLAGS_HAS(flags, NM_UTILS_STR_UTF8_SAFE_FLAG_ESCAPE_NON_ASCII) - && nm_ascii_is_non_ascii(ch))))) + if (!NM_STRCHAR_ANY( + str, + ch, + (ch == '\\' + || (NM_FLAGS_HAS(flags, NM_UTILS_STR_UTF8_SAFE_FLAG_ESCAPE_CTRL) + && nm_ascii_is_ctrl_or_del(ch)) + || (NM_FLAGS_HAS(flags, NM_UTILS_STR_UTF8_SAFE_FLAG_ESCAPE_NON_ASCII) + && nm_ascii_is_non_ascii(ch)) + || (NM_FLAGS_HAS(flags, NM_UTILS_STR_UTF8_SAFE_FLAG_ESCAPE_DOUBLE_QUOTE) + && ch == '"')))) return str; } @@ -2781,7 +2786,9 @@ nm_utils_buf_utf8safe_escape(gconstpointer buf, else if ((NM_FLAGS_HAS(flags, NM_UTILS_STR_UTF8_SAFE_FLAG_ESCAPE_CTRL) && nm_ascii_is_ctrl_or_del(ch)) || (NM_FLAGS_HAS(flags, NM_UTILS_STR_UTF8_SAFE_FLAG_ESCAPE_NON_ASCII) - && nm_ascii_is_non_ascii(ch))) + && nm_ascii_is_non_ascii(ch)) + || (NM_FLAGS_HAS(flags, NM_UTILS_STR_UTF8_SAFE_FLAG_ESCAPE_DOUBLE_QUOTE) + && ch == '"')) _str_buf_append_c_escape_octal(&strbuf, ch); else nm_str_buf_append_c(&strbuf, ch); @@ -3332,9 +3339,9 @@ _utils_hashtable_equal(GHashTable *hash_a, /** * nm_utils_hashtable_cmp_equal: - * @a: (allow-none): the hash table or %NULL - * @b: (allow-none): the other hash table or %NULL - * @cmp_values: (allow-none): if %NULL, only the keys + * @a: (nullable): the hash table or %NULL + * @b: (nullable): the other hash table or %NULL + * @cmp_values: (nullable): if %NULL, only the keys * will be compared. Otherwise, this function is used to * check whether all keys are equal. * @user_data: the argument for @cmp_values. @@ -3401,8 +3408,8 @@ _hashtable_cmp_func(gconstpointer a, gconstpointer b, gpointer user_data) /** * nm_utils_hashtable_cmp: - * @a: (allow-none): the hash to compare. May be %NULL. - * @b: (allow-none): the other hash to compare. May be %NULL. + * @a: (nullable): the hash to compare. May be %NULL. + * @b: (nullable): the other hash to compare. May be %NULL. * @do_fast_precheck: if %TRUE, assume that the hashes are equal * and that it is worth calling nm_utils_hashtable_cmp_equal() first. * That requires, that both hashes have the same equals function @@ -3410,7 +3417,7 @@ _hashtable_cmp_func(gconstpointer a, gconstpointer b, gpointer user_data) * @cmp_keys: the compare function for keys. Usually, the hash/equal function * of both hashes corresponds to this function. If you set @do_fast_precheck * to false, then this is not a requirement. - * @cmp_values: (allow-none): if %NULL, only the keys are compared. + * @cmp_values: (nullable): if %NULL, only the keys are compared. * Otherwise, the values must are also compared with this function. * * Both hashes must have keys/values of the same domain, so that @@ -4839,6 +4846,50 @@ nm_g_child_watch_source_new(GPid pid, return source; } +gboolean +nm_g_timeout_reschedule(GSource **src, + gint64 *p_expiry_msec, + gint64 expiry_msec, + GSourceFunc func, + gpointer user_data) +{ + gint64 now_msec; + gint64 timeout_msec; + + /* (Re-)Schedules a timeout at "expiry_msec" (in + * nm_utils_get_monotonic_timestamp_msec() scale). + * + * If a source is already scheduled in "*src" and "*p_expiry_msec" is + * identical to "expiry_msec", then we assume the timer is already ticking, + * and nothing is rescheduled. + * + * Otherwise, "*src" gets cancelled (if any), a new timer is scheduled + * (assigned to "*src") and the new expiry is written to "*p_expiry_msec". + */ + + nm_assert(src); + nm_assert(p_expiry_msec); + + if (*src) { + if (*p_expiry_msec == expiry_msec) { + /* already scheduled with same expiry. */ + return FALSE; + } + nm_clear_g_source_inst(src); + } + + now_msec = nm_utils_get_monotonic_timestamp_msec(); + + if (expiry_msec <= now_msec) + timeout_msec = 0; + else + timeout_msec = NM_MIN(expiry_msec - now_msec, (gint64) G_MAXUINT); + + *p_expiry_msec = expiry_msec; + *src = nm_g_timeout_add_source(timeout_msec, func, user_data); + return TRUE; +} + /*****************************************************************************/ #define _CTX_LOG(fmt, ...) \ @@ -5301,7 +5352,7 @@ nm_utils_ifname_valid_kernel(const char *name, GError **error) if (ch == '\0') return TRUE; - if (NM_IN_SET(ch, '/', ':') || g_ascii_isspace(ch)) { + if (NM_IN_SET(ch, '/', ':') || nm_ascii_is_space_kernel(ch)) { g_set_error_literal(error, NM_UTILS_ERROR, NM_UTILS_ERROR_UNKNOWN, @@ -6729,7 +6780,7 @@ valid_ldh_char(char c) * @s: the hostname to check. * @trailing_dot: Accept trailing dot on multi-label names. * - * Return: %TRUE if valid. + * Returns: %TRUE if valid. */ gboolean nm_hostname_is_valid(const char *s, gboolean trailing_dot) @@ -6789,3 +6840,263 @@ nm_hostname_is_valid(const char *s, gboolean trailing_dot) return TRUE; } + +/*****************************************************************************/ + +typedef struct { + GTask *task; + GSource *source_timeout; + GSource *source_next_poll; + GMainContext *context; + GCancellable *internal_cancellable; + NMUtilsPollProbeStartFcn probe_start_fcn; + NMUtilsPollProbeFinishFcn probe_finish_fcn; + gpointer probe_user_data; + gulong cancellable_id; + gint64 last_poll_start_ms; + int sleep_timeout_ms; + int ratelimit_timeout_ms; + bool completed : 1; +} PollTaskData; + +static void +_poll_task_data_free(gpointer data) +{ + PollTaskData *poll_task_data = data; + + nm_assert(G_IS_TASK(poll_task_data->task)); + nm_assert(!poll_task_data->source_next_poll); + nm_assert(!poll_task_data->source_timeout); + nm_assert(poll_task_data->cancellable_id == 0); + + g_main_context_unref(poll_task_data->context); + + nm_g_slice_free(poll_task_data); +} + +static void +_poll_return(PollTaskData *poll_task_data, GError *error_take) +{ + nm_clear_g_source_inst(&poll_task_data->source_next_poll); + nm_clear_g_source_inst(&poll_task_data->source_timeout); + nm_clear_g_cancellable_disconnect(g_task_get_cancellable(poll_task_data->task), + &poll_task_data->cancellable_id); + + nm_clear_g_cancellable(&poll_task_data->internal_cancellable); + + if (error_take) + g_task_return_error(poll_task_data->task, g_steal_pointer(&error_take)); + else + g_task_return_boolean(poll_task_data->task, TRUE); + + g_object_unref(poll_task_data->task); +} + +static gboolean _poll_start_cb(gpointer user_data); + +static void +_poll_done_cb(GObject *source, GAsyncResult *result, gpointer user_data) +{ + PollTaskData *poll_task_data = user_data; + _nm_unused gs_unref_object GTask *task = + poll_task_data->task; /* balance ref from _poll_start_cb() */ + gs_free_error GError *error = NULL; + gint64 now_ms; + gint64 wait_ms; + gboolean is_finished; + + is_finished = + poll_task_data->probe_finish_fcn(source, result, poll_task_data->probe_user_data, &error); + + if (nm_utils_error_is_cancelled(error)) { + /* we already handle this differently. Nothing to do. */ + return; + } + + if (error || is_finished) { + _poll_return(poll_task_data, g_steal_pointer(&error)); + return; + } + + now_ms = nm_utils_get_monotonic_timestamp_msec(); + if (poll_task_data->ratelimit_timeout_ms > 0) + wait_ms = + (poll_task_data->last_poll_start_ms + poll_task_data->ratelimit_timeout_ms) - now_ms; + else + wait_ms = 0; + if (poll_task_data->sleep_timeout_ms > 0) + wait_ms = MAX(wait_ms, poll_task_data->sleep_timeout_ms); + + poll_task_data->source_next_poll = + nm_g_source_attach(nm_g_timeout_source_new(MAX(1, wait_ms), + G_PRIORITY_DEFAULT, + _poll_start_cb, + poll_task_data, + NULL), + poll_task_data->context); +} + +static gboolean +_poll_start_cb(gpointer user_data) +{ + PollTaskData *poll_task_data = user_data; + + nm_clear_g_source_inst(&poll_task_data->source_next_poll); + + poll_task_data->last_poll_start_ms = nm_utils_get_monotonic_timestamp_msec(); + + g_object_ref(poll_task_data->task); /* balanced by _poll_done_cb() */ + + poll_task_data->probe_start_fcn(poll_task_data->internal_cancellable, + poll_task_data->probe_user_data, + _poll_done_cb, + poll_task_data); + + return G_SOURCE_CONTINUE; +} + +static gboolean +_poll_timeout_cb(gpointer user_data) +{ + PollTaskData *poll_task_data = user_data; + + _poll_return(poll_task_data, nm_utils_error_new(NM_UTILS_ERROR_UNKNOWN, "timeout expired")); + return G_SOURCE_CONTINUE; +} + +static void +_poll_cancelled_cb(GObject *object, gpointer user_data) +{ + PollTaskData *poll_task_data = user_data; + GError *error = NULL; + + nm_clear_g_signal_handler(g_task_get_cancellable(poll_task_data->task), + &poll_task_data->cancellable_id); + nm_utils_error_set_cancelled(&error, FALSE, NULL); + _poll_return(poll_task_data, error); +} + +/** + * nm_utils_poll: + * @poll_timeout_ms: if >= 0, then this is the overall timeout for how long we poll. + * When this timeout expires, the request completes with failure (and error set). + * @ratelimit_timeout_ms: if > 0, we ratelimit the starts from one prope_start_fcn + * call to the next. We will wait at least this time between two consecutive polls. + * @sleep_timeout_ms: if > 0, then we wait after a probe finished this timeout + * before the next. Together with @ratelimit_timeout_ms this determines how + * frequently we probe. We will wait at least this time between the end of the + * previous poll and the next one. + * @probe_register_object_fcn: (allow-none): called by nm_utils_poll() + * synchronously, with the new, internal GTask instance. The purpose of this + * callback is a bit obscure, you may want to pass NULL here. It's used by some + * caller to register a weak pointer on the internal GTask instance to track + * the lifetime of the operation. + * @probe_start_fcn: used to start a (asynchronous) probe. A probe must be + * completed by calling the provided callback. While a probe is in progress, we + * will not start another. The function is called the first time on an idle + * handler, afterwards it gets called again on each timeout for polling. + * @probe_finish_fcn: will be called from the callback of @probe_start_fcn. If the + * function returns %TRUE (polling done) or an error, polling stops. Otherwise, + * another poll will be started. + * @probe_user_data: user_data for the probe functions. + * @cancellable: cancellable for polling. + * @callback: when polling completes. + * @user_data: for @callback. + * + * This uses the current g_main_context_get_thread_default() for scheduling + * actions. + */ +void +nm_utils_poll(int poll_timeout_ms, + int ratelimit_timeout_ms, + int sleep_timeout_ms, + NMUtilsPollProbeRegisterObjectFcn probe_register_object_fcn, + NMUtilsPollProbeStartFcn probe_start_fcn, + NMUtilsPollProbeFinishFcn probe_finish_fcn, + gpointer probe_user_data, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data) +{ + PollTaskData *poll_task_data; + + poll_task_data = g_slice_new(PollTaskData); + *poll_task_data = (PollTaskData){ + .task = nm_g_task_new(NULL, cancellable, nm_utils_poll, callback, user_data), + .probe_start_fcn = probe_start_fcn, + .probe_finish_fcn = probe_finish_fcn, + .probe_user_data = probe_user_data, + .completed = FALSE, + .context = g_main_context_ref_thread_default(), + .sleep_timeout_ms = sleep_timeout_ms, + .ratelimit_timeout_ms = ratelimit_timeout_ms, + .internal_cancellable = g_cancellable_new(), + }; + + g_task_set_task_data(poll_task_data->task, poll_task_data, _poll_task_data_free); + + if (probe_register_object_fcn) + probe_register_object_fcn((GObject *) poll_task_data->task, probe_user_data); + + if (poll_timeout_ms >= 0) { + poll_task_data->source_timeout = + nm_g_source_attach(nm_g_timeout_source_new(poll_timeout_ms, + G_PRIORITY_DEFAULT, + _poll_timeout_cb, + poll_task_data, + NULL), + poll_task_data->context); + } + + poll_task_data->source_next_poll = nm_g_source_attach( + nm_g_idle_source_new(G_PRIORITY_DEFAULT_IDLE, _poll_start_cb, poll_task_data, NULL), + poll_task_data->context); + + if (cancellable) { + gulong signal_id; + + signal_id = g_cancellable_connect(cancellable, + G_CALLBACK(_poll_cancelled_cb), + poll_task_data, + NULL); + if (signal_id == 0) { + /* the request is already cancelled. Return. */ + return; + } + poll_task_data->cancellable_id = signal_id; + } +} + +/** + * nm_utils_poll_finish: + * @result: the GAsyncResult from the GAsyncReadyCallback callback. + * @probe_user_data: the user data provided to nm_utils_poll(). + * @error: the failure code. + * + * Returns: %TRUE if the polling completed with success. In that case, + * the error won't be set. + * If the request was cancelled, this is indicated by @error and + * %FALSE will be returned. + * If the probe returned a failure, this returns %FALSE and the error + * provided by @probe_finish_fcn. + * If the request times out, this returns %FALSE with error set. + * Error is always set if (and only if) the function returns %FALSE. + */ +gboolean +nm_utils_poll_finish(GAsyncResult *result, gpointer *probe_user_data, GError **error) +{ + GTask *task; + PollTaskData *poll_task_data; + + g_return_val_if_fail(nm_g_task_is_valid(result, NULL, nm_utils_poll), FALSE); + g_return_val_if_fail(!error || !*error, FALSE); + + task = G_TASK(result); + + if (probe_user_data) { + poll_task_data = g_task_get_task_data(task); + NM_SET_OUT(probe_user_data, poll_task_data->probe_user_data); + } + + return g_task_propagate_boolean(task, error); +} diff --git a/src/libnm-glib-aux/nm-shared-utils.h b/src/libnm-glib-aux/nm-shared-utils.h index d2e30504..e37cf5e6 100644 --- a/src/libnm-glib-aux/nm-shared-utils.h +++ b/src/libnm-glib-aux/nm-shared-utils.h @@ -79,7 +79,7 @@ G_STATIC_ASSERT(sizeof(int) == sizeof(gint32)); ({ \ _nm_unused typeof(value) _value = (value); \ \ - _Generic((value), int : TRUE); \ + _Generic((value), int: TRUE); \ }) #else #define _NM_INT_LE_MAXINT32(value) \ @@ -208,12 +208,13 @@ typedef struct { #define NM_ETHER_ADDR_INIT(...) ((NMEtherAddr) _NM_ETHER_ADDR_INIT(__VA_ARGS__)) -struct _NMIPAddr; -extern const struct _NMIPAddr nm_ip_addr_zero; +union _NMIPAddr; + +extern const union _NMIPAddr nm_ip_addr_zero; /* Let's reuse nm_ip_addr_zero also for nm_ether_addr_zero. It's a union that * also contains a NMEtherAddr field. */ -#define nm_ether_addr_zero (*((const NMEtherAddr *) ((gconstpointer) &nm_ip_addr_zero))) +#define nm_ether_addr_zero (*((const NMEtherAddr *) &nm_ip_addr_zero)) static inline int nm_ether_addr_cmp(const NMEtherAddr *a, const NMEtherAddr *b) @@ -1251,12 +1252,16 @@ typedef enum { * It will backslash escape ascii characters according to nm_ascii_is_non_ascii(). */ NM_UTILS_STR_UTF8_SAFE_FLAG_ESCAPE_NON_ASCII = 0x0002, + /* Escape '"' as ASCII "\\042". This is useful when escaping a string so that + * it can be unescaped with `echo -e $PASTE_TEXT`. */ + NM_UTILS_STR_UTF8_SAFE_FLAG_ESCAPE_DOUBLE_QUOTE = 0x0004, + /* This flag only has an effect during escaping to ensure we * don't leak secrets in memory. Note that during unescape we * know the maximum result size from the beginning, and no * reallocation happens. Thus, unescape always avoids leaking * secrets already. */ - NM_UTILS_STR_UTF8_SAFE_FLAG_SECRET = 0x0004, + NM_UTILS_STR_UTF8_SAFE_FLAG_SECRET = 0x0008, /* This flag only has an effect during unescaping. It means * that non-escaped whitespaces (g_ascii_isspace()) will be @@ -1264,7 +1269,7 @@ typedef enum { * this flag is only useful for gracefully accepting user input * with spaces. With this flag, escape and unescape may no longer * yield the original input. */ - NM_UTILS_STR_UTF8_SAFE_UNESCAPE_STRIP_SPACES = 0x0008, + NM_UTILS_STR_UTF8_SAFE_UNESCAPE_STRIP_SPACES = 0x0010, } NMUtilsStrUtf8SafeFlags; const char *nm_utils_buf_utf8safe_escape(gconstpointer buf, @@ -1352,10 +1357,6 @@ nm_g_variant_new_au(const guint32 *data, gsize len) return g_variant_new_fixed_array(G_VARIANT_TYPE_UINT32, data, len, sizeof(guint32)); } -struct _NMIPAddr; - -extern const struct _NMIPAddr nm_ip_addr_zero; - static inline GVariant * nm_g_variant_new_ay_inaddr(int addr_family, gconstpointer addr) { @@ -1554,6 +1555,12 @@ nm_g_timeout_add_source(guint timeout_msec, GSourceFunc func, gpointer user_data NULL); } +gboolean nm_g_timeout_reschedule(GSource **src, + gint64 *p_expiry_msec, + gint64 expiry_msec, + GSourceFunc func, + gpointer user_data); + static inline GSource * nm_g_timeout_add_seconds_source(guint timeout_sec, GSourceFunc func, gpointer user_data) { @@ -2724,7 +2731,7 @@ guint8 *nm_utils_hexstr2bin_alloc(const char *hexstr, * can parse addresses of any length. That is, you don't need * to know the length before-hand. * - * Return value: @buffer, or %NULL if @asc couldn't be parsed. + * Returns: @buffer, or %NULL if @asc couldn't be parsed. */ static inline guint8 * _nm_utils_hwaddr_aton(const char *asc, gpointer buffer, gsize buffer_length, gsize *out_length) @@ -3250,4 +3257,31 @@ nm_path_startswith(const char *path, const char *prefix) gboolean nm_hostname_is_valid(const char *s, gboolean trailing_dot); +/*****************************************************************************/ + +typedef void (*NMUtilsPollProbeRegisterObjectFcn)(GObject *object, gpointer user_data); + +typedef void (*NMUtilsPollProbeStartFcn)(GCancellable *cancellable, + gpointer probe_user_data, + GAsyncReadyCallback callback, + gpointer user_data); + +typedef gboolean (*NMUtilsPollProbeFinishFcn)(GObject *source, + GAsyncResult *result, + gpointer probe_user_data, + GError **error); + +void nm_utils_poll(int poll_timeout_ms, + int ratelimit_timeout_ms, + int sleep_timeout_ms, + NMUtilsPollProbeRegisterObjectFcn probe_register_object_fcn, + NMUtilsPollProbeStartFcn probe_start_fcn, + NMUtilsPollProbeFinishFcn probe_finish_fcn, + gpointer probe_user_data, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data); + +gboolean nm_utils_poll_finish(GAsyncResult *result, gpointer *probe_user_data, GError **error); + #endif /* __NM_SHARED_UTILS_H__ */ diff --git a/src/libnm-glib-aux/nm-str-buf.h b/src/libnm-glib-aux/nm-str-buf.h index 32ab8da8..c62381e5 100644 --- a/src/libnm-glib-aux/nm-str-buf.h +++ b/src/libnm-glib-aux/nm-str-buf.h @@ -522,8 +522,8 @@ nm_str_buf_get_char(const NMStrBuf *strbuf, gsize index) /** * nm_str_buf_finalize: - * @strbuf: an initilized #NMStrBuf - * @out_len: (out): (allow-none): optional output + * @strbuf: an initialized #NMStrBuf + * @out_len: (out): (optional): optional output * argument with the length of the returned string. * * Returns: (transfer full): the string of the buffer diff --git a/src/libnm-glib-aux/nm-test-utils.h b/src/libnm-glib-aux/nm-test-utils.h index b65818e0..de6fd0f1 100644 --- a/src/libnm-glib-aux/nm-test-utils.h +++ b/src/libnm-glib-aux/nm-test-utils.h @@ -203,6 +203,54 @@ } \ G_STMT_END +#define nmtst_assert_cmpmem(m1, l1, m2, l2) \ + G_STMT_START \ + { \ + const guint8 *const _m1 = (gpointer) (m1); \ + const guint8 *const _m2 = (gpointer) (m2); \ + const gsize _l1 = (l1); \ + const gsize _l2 = (l2); \ + \ + /* This is like g_assert_cmpmem(), however on failure it actually + * prints the compared buffer contents, which is useful for debugging + * the test failure. */ \ + \ + g_assert(_l1 == 0 || _m1); \ + g_assert(_l2 == 0 || _m2); \ + \ + if (_l1 != _l2 || (_l1 > 0 && memcmp(_m1, _m2, _l1) != 0)) { \ + gs_free char *_s1 = NULL; \ + gs_free char *_s2 = NULL; \ + \ + g_error( \ + "ERROR: %s:%d : buffer [\"%s\" (%s, %zu bytes)] differs from [\"%s\" (%s, %zu " \ + "bytes)]:\n" \ + " a=[ \"%s\" ]\n" \ + " b=[ \"%s\" ]\n", \ + __FILE__, \ + (int) __LINE__, \ + #m1, \ + #l1, \ + _l1, \ + #m2, \ + #l2, \ + _l2, \ + (_s1 = nm_utils_buf_utf8safe_escape_cp( \ + _m1, \ + _l1, \ + NM_UTILS_STR_UTF8_SAFE_FLAG_ESCAPE_CTRL \ + | NM_UTILS_STR_UTF8_SAFE_FLAG_ESCAPE_DOUBLE_QUOTE)) \ + ?: "", \ + (_s2 = nm_utils_buf_utf8safe_escape_cp( \ + _m2, \ + _l2, \ + NM_UTILS_STR_UTF8_SAFE_FLAG_ESCAPE_CTRL \ + | NM_UTILS_STR_UTF8_SAFE_FLAG_ESCAPE_DOUBLE_QUOTE)) \ + ?: ""); \ + } \ + } \ + G_STMT_END + /*****************************************************************************/ /* Our nm-error error numbers use negative values to signal failure. @@ -1249,7 +1297,7 @@ nmtst_stable_rand(guint64 seed, gpointer buf, gsize len) /** * nmtst_get_rand_word_length: - * @rand: (allow-none): #GRand instance or %NULL to use the singleton. + * @rand: (nullable): #GRand instance or %NULL to use the singleton. * * Returns: a random integer >= 0, that most frequently is somewhere between * 0 and 16, but (with decreasing) probability, it can be larger. This can @@ -2839,8 +2887,7 @@ _nmtst_variant_new_vardict(int dummy, ...) G_STMT_END #else #define _nmtst_assert_variant_bytestring_cmp_str(_ptr, _ptr2, _len) \ - G_STMT_START \ - {} \ + G_STMT_START {} \ G_STMT_END #endif diff --git a/src/libnm-glib-aux/nm-uuid.c b/src/libnm-glib-aux/nm-uuid.c index cdfa5f62..df1b10c0 100644 --- a/src/libnm-glib-aux/nm-uuid.c +++ b/src/libnm-glib-aux/nm-uuid.c @@ -411,7 +411,7 @@ nm_uuid_generate_from_string_str(const char *s, * @uuid_type: the UUID type to use. Prefer version 5 unless you have * good reasons. * @type_args: the namespace UUID. - * @strv: (allow-none): the strv list to hash. Can be NULL, in which + * @strv: (nullable): the strv list to hash. Can be NULL, in which * case the result is different from an empty array. * @len: if negative, @strv is a NULL terminated array. Otherwise, * it is the length of the strv array. In the latter case it may diff --git a/src/libnm-glib-aux/tests/test-shared-general.c b/src/libnm-glib-aux/tests/test-shared-general.c index 3eaca547..84d87d48 100644 --- a/src/libnm-glib-aux/tests/test-shared-general.c +++ b/src/libnm-glib-aux/tests/test-shared-general.c @@ -2313,6 +2313,130 @@ test_inet_utils(void) /*****************************************************************************/ +static gboolean +_inet_parse(int addr_family, const char *str, gboolean accept_legacy, gpointer out_addr) +{ + int addr_family2 = -1; + int *const p_addr_family2 = nmtst_get_rand_bool() ? &addr_family2 : NULL; + NMIPAddr addr; + gboolean success; + + g_assert(NM_IN_SET(addr_family, AF_INET, AF_INET6)); + + success = + nm_inet_parse_bin_full((p_addr_family2 && nmtst_get_rand_bool()) ? AF_UNSPEC : addr_family, + accept_legacy, + str, + p_addr_family2, + &addr); + + if (success) { + g_assert(!p_addr_family2 || NM_IN_SET(*p_addr_family2, AF_INET, AF_INET6)); + if (p_addr_family2 && *p_addr_family2 != addr_family) { + success = FALSE; + } else + g_assert(!p_addr_family2 || *p_addr_family2 == addr_family); + } else + g_assert(addr_family2 == -1); + + if (out_addr && success) + nm_ip_addr_set(addr_family, out_addr, &addr); + + return success; +} + +#define _inet_parse_fail(check, accept_legacy) \ + G_STMT_START \ + { \ + NMIPAddr _addr; \ + gboolean _success; \ + \ + _success = _inet_parse(nmtst_get_rand_bool() ? AF_INET : AF_INET6, \ + "" check "", \ + (accept_legacy), \ + nmtst_get_rand_bool() ? &_addr : NULL); \ + g_assert(!_success); \ + } \ + G_STMT_END + +#define _inet_parse_good(check, expected, accept_legacy) \ + G_STMT_START \ + { \ + int _accept_legacy = (accept_legacy); \ + const char *const _check = "" check ""; \ + const char *const _expected = expected ?: _check; \ + NMIPAddr _addr[2]; \ + gboolean _success[2]; \ + \ + if (_accept_legacy == -1) \ + _accept_legacy = nmtst_get_rand_bool(); \ + \ + _success[0] = _inet_parse(AF_INET6, _check, _accept_legacy, &_addr[0]); \ + _success[1] = _inet_parse(AF_INET, _check, _accept_legacy, &_addr[1]); \ + \ + g_assert(NM_IN_SET(_success[0], FALSE, TRUE)); \ + g_assert(NM_IN_SET(_success[1], FALSE, TRUE)); \ + g_assert(_success[0] != _success[1]); \ + \ + if (_success[0]) \ + nmtst_assert_ip6_address(&_addr[0].addr6, _expected); \ + else \ + nmtst_assert_ip4_address(_addr[1].addr4, _expected); \ + \ + if (_success[1]) { \ + in_addr_t _a4; \ + int _r; \ + \ + _r = nmtst_inet_aton(_check, &_a4); \ + g_assert_cmpint(_r, ==, 0); \ + nmtst_assert_ip4_address(_a4, _expected); \ + } \ + } \ + G_STMT_END + +static void +test_inet_parse_ip4_legacy(void) +{ + _inet_parse_fail("", -1); + _inet_parse_fail(" ", -1); + _inet_parse_fail("a", -1); + _inet_parse_fail("0", -1); + _inet_parse_fail("0.1", -1); + _inet_parse_fail("0.4.1", -1); + _inet_parse_fail("1.2.3.05", FALSE); + _inet_parse_fail("192.000.002.010", FALSE); + _inet_parse_fail("1.2.3..5", -1); + _inet_parse_fail("1.2.3.0x", -1); + _inet_parse_fail("0xC0000234", -1); + _inet_parse_fail("192.0.2.2X", -1); + _inet_parse_fail("192.0.2.3 Y", -1); + _inet_parse_fail("192.0.2.4\nZ", -1); + _inet_parse_fail("192.0.2.5\tT", -1); + _inet_parse_fail("192.0.2.6 Y", -1); + _inet_parse_fail("192.0.2.7\n", -1); + _inet_parse_fail("192.0.2.7\t", -1); + _inet_parse_fail("192.0.2.7 ", -1); + _inet_parse_fail("00x0019.0000001.000000.0x1", -1); + _inet_parse_fail("192.0.2.7.", -1); + _inet_parse_fail("192.0.2.7.0", -1); + + _inet_parse_good("192.0.2.1", NULL, -1); + _inet_parse_good("1.2.3.4", NULL, -1); + _inet_parse_good("192.167.3.4", NULL, -1); + + _inet_parse_good("192.000.002.010", "192.0.2.8", TRUE); + _inet_parse_good("255.000.000.000", "255.0.0.0", TRUE); + _inet_parse_good("1.2.3.05", "1.2.3.5", TRUE); + _inet_parse_good("01.2.3.05", "1.2.3.5", TRUE); + _inet_parse_good("192.00167.0003.4", "192.119.3.4", TRUE); + _inet_parse_good("0x19.00167.0003.4", "25.119.3.4", TRUE); + _inet_parse_good("0x19.000000167.0000003.4", "25.119.3.4", TRUE); + _inet_parse_good("0x0019.000000167.0000003.04", "25.119.3.4", TRUE); + _inet_parse_good("0x0019.0000001.000000.0x1", "25.1.0.1", TRUE); +} + +/*****************************************************************************/ + static void test_garray(void) { @@ -2348,7 +2472,7 @@ test_garray(void) static int _prioq_cmp(gconstpointer a, gconstpointer b) { - NM_CMP_DIRECT(GPOINTER_TO_UINT(a), GPOINTER_TO_UINT(b)); + NM_CMP_DIRECT(*((const guint32 *) a), *((const guint32 *) b)); return 0; } @@ -2362,11 +2486,12 @@ static void test_nm_prioq(void) { nm_auto_prioq NMPrioq q = NM_PRIOQ_ZERO; - gpointer data[200]; - gpointer data_pop[200]; + guint32 data[200]; + const guint32 *data_pop[200]; guint data_idx[G_N_ELEMENTS(data)]; guint i; guint n; + guint m; gpointer p; if (nmtst_get_rand_one_case_in(10)) @@ -2379,37 +2504,67 @@ test_nm_prioq(void) g_assert(nm_prioq_size(&q) == 0); - if (nmtst_get_rand_one_case_in(10)) + if (nmtst_get_rand_one_case_in(100)) return; for (i = 0; i < G_N_ELEMENTS(data); i++) { - data[i] = GUINT_TO_POINTER((nmtst_get_rand_uint32() % G_N_ELEMENTS(data)) + 1u); + data[i] = nmtst_get_rand_uint32() % G_N_ELEMENTS(data); data_idx[i] = NM_PRIOQ_IDX_NULL; } - nm_prioq_put(&q, data[0], NULL); + nm_prioq_put(&q, &data[0], NULL); g_assert(nm_prioq_size(&q) == 1); p = nm_prioq_pop(&q); - g_assert(p == data[0]); + g_assert(p == &data[0]); g_assert(nm_prioq_size(&q) == 0); g_assert(!nm_prioq_pop(&q)); n = nmtst_get_rand_uint32() % G_N_ELEMENTS(data); for (i = 0; i < n; i++) - nm_prioq_put(&q, data[i], &data_idx[i]); + nm_prioq_put(&q, &data[i], &data_idx[i]); - g_assert_cmpint(nm_prioq_size(&q), ==, n); + m = n; + for (i = 0; i < n; i++) { + if (!nmtst_get_rand_bool()) + continue; - if (nmtst_get_rand_one_case_in(10)) + data[i] = nmtst_get_rand_uint32() % G_N_ELEMENTS(data); + switch (nmtst_get_rand_uint32() % 4) { + case 0: + nm_prioq_reshuffle(&q, &data[i], &data_idx[i]); + break; + case 1: + nm_prioq_remove(&q, &data[i], nmtst_get_rand_bool() ? &data_idx[i] : NULL); + m--; + break; + case 2: + nm_prioq_update(&q, &data[i], &data_idx[i], TRUE); + break; + case 3: + nm_prioq_update(&q, &data[i], nmtst_get_rand_bool() ? &data_idx[i] : NULL, FALSE); + m--; + break; + } + } + + g_assert_cmpint(nm_prioq_size(&q), ==, m); + + if (nmtst_get_rand_one_case_in(50)) return; - for (i = 0; i < n; i++) { + for (i = 0; i < m; i++) { data_pop[i] = nm_prioq_pop(&q); g_assert(data_pop[i]); - if (i > 0) + g_assert_cmpint(*data_pop[i], >=, 0); + g_assert_cmpint(*data_pop[i], <, G_N_ELEMENTS(data)); + g_assert(data_pop[i] >= &data[0]); + g_assert(data_pop[i] < &data[n]); + if (i > 0) { g_assert(_prioq_cmp(data_pop[i - 1], data_pop[i]) <= 0); + g_assert_cmpint(*data_pop[i - 1], <=, *data_pop[i]); + } } g_assert(!nm_prioq_pop(&q)); @@ -2464,6 +2619,7 @@ main(int argc, char **argv) g_test_add_func("/general/test_path_simplify", test_path_simplify); g_test_add_func("/general/test_hostname_is_valid", test_hostname_is_valid); g_test_add_func("/general/test_inet_utils", test_inet_utils); + g_test_add_func("/general/test_inet_parse_ip4_legacy", test_inet_parse_ip4_legacy); g_test_add_func("/general/test_garray", test_garray); g_test_add_func("/general/test_nm_prioq", test_nm_prioq); g_test_add_func("/general/test_nm_random", test_nm_random); |