diff options
author | Thomas Haller <thaller@redhat.com> | 2023-07-28 15:04:42 (GMT) |
---|---|---|
committer | Thomas Haller <thaller@redhat.com> | 2023-07-31 13:15:56 (GMT) |
commit | 2da8481b505509aab05bb37f8fc54536a3f5e99f (patch) | |
tree | b44af41eb7816603ed2150ad61635c351d99879c /include/base | |
parent | d3e9b5134663c08165f33ea38d2de7143eeb570d (diff) | |
download | libnl-2da8481b505509aab05bb37f8fc54536a3f5e99f.zip libnl-2da8481b505509aab05bb37f8fc54536a3f5e99f.tar.gz libnl-2da8481b505509aab05bb37f8fc54536a3f5e99f.tar.bz2 |
base: move "netlink-private/utils.h" to "base/nl-base-utils.h"
"base/nl-base-utils.h" (formerly "netlink-private/utils.h") contains
no libnl3 specific references, just a bunch of C helpers.
It's also a header-only "library", so it can be freely used by all our
C-code.
Move it to a separate directory, to make that clear.
Diffstat (limited to 'include/base')
-rw-r--r-- | include/base/README.md | 13 | ||||
-rw-r--r-- | include/base/nl-base-utils.h | 716 |
2 files changed, 729 insertions, 0 deletions
diff --git a/include/base/README.md b/include/base/README.md new file mode 100644 index 0000000..3da1621 --- /dev/null +++ b/include/base/README.md @@ -0,0 +1,13 @@ +include/base +============ + +This directory contains libnl-independent helper. + +It's an internal "library" of helpers, that purely depend +on C and external dependencies. + +Currently, it's also header-only, so there is no need to link +with anything special. + +This can be used freely by all our internal code, but it's private API, +so it cannot be used in public headers. diff --git a/include/base/nl-base-utils.h b/include/base/nl-base-utils.h new file mode 100644 index 0000000..e02b5f4 --- /dev/null +++ b/include/base/nl-base-utils.h @@ -0,0 +1,716 @@ +/* SPDX-License-Identifier: LGPL-2.1-only */ +/* + * Copyright (c) 2003-2012 Thomas Graf <tgraf@suug.ch> + */ + +#ifndef __NETLINK_BASE_NL_BASE_UTILS_H__ +#define __NETLINK_BASE_NL_BASE_UTILS_H__ + +#include <byteswap.h> +#include <stdlib.h> +#include <assert.h> +#include <unistd.h> +#include <errno.h> +#include <string.h> +#include <stdbool.h> +#include <stdint.h> +#include <netinet/in.h> +#include <arpa/inet.h> + +#if __BYTE_ORDER == __BIG_ENDIAN +#define ntohll(x) (x) +#elif __BYTE_ORDER == __LITTLE_ENDIAN +#define ntohll(x) bswap_64((x)) +#endif +#define htonll(x) ntohll(x) + +/*****************************************************************************/ + +#define _NL_STRINGIFY_ARG(contents) #contents +#define _NL_STRINGIFY(macro_or_string) _NL_STRINGIFY_ARG(macro_or_string) + +/*****************************************************************************/ + +#if defined(__GNUC__) +#define _NL_PRAGMA_WARNING_DO(warning) \ + _NL_STRINGIFY(GCC diagnostic ignored warning) +#elif defined(__clang__) +#define _NL_PRAGMA_WARNING_DO(warning) \ + _NL_STRINGIFY(clang diagnostic ignored warning) +#endif + +/* you can only suppress a specific warning that the compiler + * understands. Otherwise you will get another compiler warning + * about invalid pragma option. + * It's not that bad however, because gcc and clang often have the + * same name for the same warning. */ + +#if defined(__GNUC__) && \ + (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6)) +#define _NL_PRAGMA_WARNING_DISABLE(warning) \ + _Pragma("GCC diagnostic push") \ + _Pragma(_NL_PRAGMA_WARNING_DO("-Wpragmas")) \ + _Pragma(_NL_PRAGMA_WARNING_DO(warning)) +#elif defined(__clang__) +#define _NL_PRAGMA_WARNING_DISABLE(warning) \ + _Pragma("clang diagnostic push") \ + _Pragma(_NL_PRAGMA_WARNING_DO("-Wunknown-warning-option")) \ + _Pragma(_NL_PRAGMA_WARNING_DO(warning)) +#else +#define _NL_PRAGMA_WARNING_DISABLE(warning) +#endif + +#if defined(__GNUC__) && \ + (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6)) +#define _NL_PRAGMA_WARNING_REENABLE _Pragma("GCC diagnostic pop") +#elif defined(__clang__) +#define _NL_PRAGMA_WARNING_REENABLE _Pragma("clang diagnostic pop") +#else +#define _NL_PRAGMA_WARNING_REENABLE +#endif + +/*****************************************************************************/ + +#define _nl_packed __attribute__((__packed__)) +#define _nl_unused __attribute__((__unused__)) +#define _nl_always_inline __attribute__((__always_inline__)) +#define _nl_used __attribute__((__used__)) +#define _nl_pure __attribute__((__pure__)) +#define _nl_const __attribute__((__const__)) +#define _nl_noreturn __attribute__((__noreturn__)) +#define _nl_warn_unused_result __attribute__((__warn_unused_result__)) +#define _nl_printf(a, b) __attribute__((__format__(__printf__, a, b))) +#define _nl_align(s) __attribute__((__aligned__(s))) +#define _nl_section(s) __attribute__((__section__(s))) +#define _nl_alignof(type) __alignof(type) +#define _nl_alignas(type) _nl_align(_nl_alignof(type)) +#define _nl_deprecated(msg) __attribute__((__deprecated__(msg))) +#define _nl_init __attribute__((constructor)) +#define _nl_exit __attribute__((destructor)) +#define _nl_auto(fcn) __attribute__((__cleanup__(fcn))) + +/*****************************************************************************/ + +#ifdef thread_local +#define _nl_thread_local thread_local +/* + * Don't break on glibc < 2.16 that doesn't define __STDC_NO_THREADS__ + * see http://gcc.gnu.org/bugzilla/show_bug.cgi?id=53769 + */ +#elif __STDC_VERSION__ >= 201112L && \ + !(defined(__STDC_NO_THREADS__) || \ + (defined(__GNU_LIBRARY__) && __GLIBC__ == 2 && \ + __GLIBC_MINOR__ < 16)) +#define _nl_thread_local _Thread_local +#else +#define _nl_thread_local __thread +#endif + +/*****************************************************************************/ + +#define _NL_STATIC_ASSERT(cond) ((void)sizeof(char[(cond) ? 1 : -1])) + +/*****************************************************************************/ + +#if defined(NL_MORE_ASSERTS) && NL_MORE_ASSERTS > 0 +#define _nl_assert(cond) assert(cond) +#else +#define _nl_assert(cond) \ + do { \ + if (0) { \ + assert(cond); \ + } \ + } while (0) +#endif + +#define _nl_assert_not_reached() assert(0) + +/*****************************************************************************/ + +#define _NL_BIT(n) (1ull << (n)) + +/*****************************************************************************/ + +#define _NL_PASTE_ARGS(identifier1, identifier2) identifier1##identifier2 +#define _NL_PASTE(identifier1, identifier2) \ + _NL_PASTE_ARGS(identifier1, identifier2) + +/* Taken from systemd's UNIQ_T and UNIQ macros. */ + +#define _NL_UNIQ_T(x, uniq) _NL_PASTE(__unique_prefix_, _NL_PASTE(x, uniq)) +#define _NL_UNIQ __COUNTER__ + +/*****************************************************************************/ + +#define _nl_assert_addr_family_or_unspec(addr_family) \ + do { \ + typeof(addr_family) _addr_family = (addr_family); \ + \ + _nl_assert(_addr_family == AF_UNSPEC || \ + _addr_family == AF_INET || \ + _addr_family == AF_INET6); \ + } while (0) + +#define _nl_assert_addr_family(addr_family) \ + do { \ + typeof(addr_family) _addr_family = (addr_family); \ + \ + _nl_assert(_addr_family == AF_INET || \ + _addr_family == AF_INET6); \ + } while (0) + +/*****************************************************************************/ + +#define _NL_SWAP(pa, pb) \ + do { \ + typeof(*(pa)) *_pa = (pa); \ + typeof(*(pb)) *_pb = (pb); \ + typeof(*_pa) _tmp; \ + \ + _nl_assert(_pa); \ + _nl_assert(_pb); \ + _tmp = *_pa; \ + *_pa = *_pb; \ + *_pb = _tmp; \ + } while (0) + +/*****************************************************************************/ + +#define _NL_N_ELEMENTS(arr) (sizeof(arr) / sizeof((arr)[0])) + +/*****************************************************************************/ + +extern const char *nl_strerror_l(int err); + +/*****************************************************************************/ + +static inline uintptr_t _nl_ptr_to_uintptr(const void *p) +{ + /* in C, pointers can only be compared (with less-than or greater-than) under certain + * circumstances. Since uintptr_t is supposed to be able to represent the pointer + * as a plain integer and also support to convert the integer back to the pointer, + * it should be safer to compare the pointers directly. + * + * Of course, this function isn't very useful beyond that its use makes it clear + * that we want to compare pointers by value, which otherwise may not be valid. */ + return (uintptr_t)p; +} + +/*****************************************************************************/ + +static inline int _nl_strcmp0(const char *s1, const char *s2) +{ + int c; + + /* like g_strcmp0(), but this is inlinable. + * + * Also, it is guaranteed to return either -1, 0, or 1. */ + if (s1 == s2) + return 0; + if (!s1) + return -1; + if (!s2) + return 1; + c = strcmp(s1, s2); + if (c < 0) + return -1; + if (c > 0) + return 1; + return 0; +} + +static inline bool _nl_streq(const char *a, const char *b) +{ + return !strcmp(a, b); +} + +static inline bool _nl_streq0(const char *a, const char *b) +{ + return a == b || (a && b && _nl_streq(a, b)); +} + +static inline int _nl_memcmp(const void *s1, const void *s2, size_t n) +{ + /* Workaround undefined behavior in memcmp() with NULL pointers. */ + if (n == 0) + return 0; + _nl_assert(s1); + _nl_assert(s2); + return memcmp(s1, s2, n); +} + +static inline bool _nl_memeq(const void *s1, const void *s2, size_t len) +{ + return _nl_memcmp(s1, s2, len) == 0; +} + +static inline void *_nl_memcpy(void *restrict dest, const void *restrict src, + size_t n) +{ + /* Workaround undefined behavior in memcpy() with NULL pointers. */ + if (n == 0) + return dest; + + _nl_assert(src); + return memcpy(dest, src, n); +} + +/*****************************************************************************/ + +#define _NL_INT_IS_SIGNED(arg) (!(((typeof(arg))-1) > 0)) + +#define _NL_INT_SAME_SIGNEDNESS(arg1, arg2) \ + (_NL_INT_IS_SIGNED(arg1) == _NL_INT_IS_SIGNED(arg2)) + +/*****************************************************************************/ + +/* glib's MIN()/MAX() macros don't have function-like behavior, in that they evaluate + * the argument possibly twice. + * + * Taken from systemd's MIN()/MAX() macros. */ + +#define _NL_MIN(a, b) __NL_MIN(_NL_UNIQ, a, _NL_UNIQ, b) +#define __NL_MIN(aq, a, bq, b) \ + ({ \ + typeof(a) _NL_UNIQ_T(A, aq) = (a); \ + typeof(b) _NL_UNIQ_T(B, bq) = (b); \ + \ + _NL_STATIC_ASSERT(_NL_INT_SAME_SIGNEDNESS(_NL_UNIQ_T(A, aq), \ + _NL_UNIQ_T(B, bq))); \ + \ + ((_NL_UNIQ_T(A, aq) < _NL_UNIQ_T(B, bq)) ? _NL_UNIQ_T(A, aq) : \ + _NL_UNIQ_T(B, bq)); \ + }) + +#define _NL_MAX(a, b) __NL_MAX(_NL_UNIQ, a, _NL_UNIQ, b) +#define __NL_MAX(aq, a, bq, b) \ + ({ \ + typeof(a) _NL_UNIQ_T(A, aq) = (a); \ + typeof(b) _NL_UNIQ_T(B, bq) = (b); \ + \ + _NL_STATIC_ASSERT(_NL_INT_SAME_SIGNEDNESS(_NL_UNIQ_T(A, aq), \ + _NL_UNIQ_T(B, bq))); \ + \ + ((_NL_UNIQ_T(A, aq) > _NL_UNIQ_T(B, bq)) ? _NL_UNIQ_T(A, aq) : \ + _NL_UNIQ_T(B, bq)); \ + }) + +#define _NL_CLAMP(x, low, high) \ + __NL_CLAMP(_NL_UNIQ, x, _NL_UNIQ, low, _NL_UNIQ, high) +#define __NL_CLAMP(xq, x, lowq, low, highq, high) \ + ({ \ + typeof(x) _NL_UNIQ_T(X, xq) = (x); \ + typeof(low) _NL_UNIQ_T(LOW, lowq) = (low); \ + typeof(high) _NL_UNIQ_T(HIGH, highq) = (high); \ + \ + _NL_STATIC_ASSERT(_NL_INT_SAME_SIGNEDNESS( \ + _NL_UNIQ_T(X, xq), _NL_UNIQ_T(LOW, lowq))); \ + _NL_STATIC_ASSERT(_NL_INT_SAME_SIGNEDNESS( \ + _NL_UNIQ_T(X, xq), _NL_UNIQ_T(HIGH, highq))); \ + \ + ((_NL_UNIQ_T(X, xq) > _NL_UNIQ_T(HIGH, highq)) ? \ + _NL_UNIQ_T(HIGH, highq) : \ + (_NL_UNIQ_T(X, xq) < _NL_UNIQ_T(LOW, lowq)) ? \ + _NL_UNIQ_T(LOW, lowq) : \ + _NL_UNIQ_T(X, xq)); \ + }) + +#define _NL_MAX_WITH_CMP(cmp, a, b) \ + ({ \ + typeof(a) _a = (a); \ + typeof(b) _b = (b); \ + \ + (((cmp(_a, _b)) >= 0) ? _a : _b); \ + }) + +/* evaluates to (void) if _A or _B are not constant or of different types */ +#define _NL_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))) + +/*****************************************************************************/ + +#define _NL_CMP_RETURN(c) \ + do { \ + const int _cc = (c); \ + \ + if (_cc) \ + return _cc < 0 ? -1 : 1; \ + } while (0) + +#define _NL_CMP_RETURN_DIRECT(c) \ + /* Usually we want that our CMP functions return strictly + * -1, 0, or 1. _NL_CMP_RETURN_DIRECT() is like _NL_CMP_RETURN(), + * except, it does not clamp the integer value. */ \ + do { \ + const int _cc = (c); \ + \ + if (_cc) \ + return _cc; \ + } while (0) + +#define _NL_CMP_SELF(a, b) \ + do { \ + typeof(a) _a = (a); \ + typeof(b) _b = (b); \ + \ + if (_a == _b) \ + return 0; \ + if (!_a) \ + return -1; \ + if (!_b) \ + return 1; \ + } while (0) + +/*****************************************************************************/ + +#define _NL_CMP_DIRECT(a, b) \ + do { \ + typeof(a) _a = (a); \ + typeof(b) _b = (b); \ + \ + _NL_STATIC_ASSERT(_NL_INT_SAME_SIGNEDNESS(_a, _b)); \ + \ + if (_a != _b) \ + return (_a < _b) ? -1 : 1; \ + } while (0) + +#define _NL_CMP_DIRECT_UNSAFE(a, b) \ + /* This variant is "unsafe", because it evaluates the arguments more then once. + * This is only useful for bitfields, for which typeof() doesn't work. + * Don't use otherwise. */ \ + do { \ + if ((a) != (b)) \ + return ((a) < (b)) ? -1 : 1; \ + } while (0) + +/* In the general case, direct pointer comparison is undefined behavior in C. + * Avoid that by casting pointers to void* and then to uintptr_t. This comparison + * is not really meaningful, except that it provides some kind of stable sort order + * between pointers (that can otherwise not be compared). */ +#define _NL_CMP_DIRECT_PTR(a, b) \ + _NL_CMP_DIRECT(_nl_ptr_to_uintptr(a), _nl_ptr_to_uintptr(b)) + +#define _NL_CMP_DIRECT_BOOL(a, b) _NL_CMP_DIRECT(!!(a), !!(b)) + +#define _NL_CMP_DIRECT_MEMCMP(a, b, size) \ + _NL_CMP_RETURN(_nl_memcmp((a), (b), (size))) + +#define _NL_CMP_DIRECT_STRCMP(a, b) _NL_CMP_RETURN_DIRECT(strcmp((a), (b))) + +#define _NL_CMP_DIRECT_STRCMP0(a, b) \ + _NL_CMP_RETURN_DIRECT(_nl_strcmp0((a), (b))) + +#define _NL_CMP_DIRECT_STR_INTERNED(a, b) \ + /* This is interned strings, which are first checked for equality only using pointer + * comparison. Only in case of differences, the sort order is still determined by strcmp(). */ \ + do { \ + const char *const _a = (a); \ + const char *const _b = (b); \ + \ + if (_a != _b) \ + _NL_CMP_RETURN_DIRECT(_nl_strcmp0(_a, _b)); \ + } while (0) + +#define _NL_CMP_DIRECT_IN6ADDR(a, b) \ + do { \ + const struct in6_addr *const _a = (a); \ + const struct in6_addr *const _b = (b); \ + \ + _NL_CMP_RETURN(memcmp(_a, _b, sizeof(struct in6_addr))); \ + } while (0) + +/*****************************************************************************/ + +#define _NL_CMP_FIELD(a, b, field) _NL_CMP_DIRECT(((a)->field), ((b)->field)) + +#define _NL_CMP_FIELD_UNSAFE(a, b, field) \ + /* This variant is "unsafe", because it evaluates the arguments more then once. + * This is only useful for bitfields, for which typeof() doesn't work. + * Don't use otherwise. */ \ + _NL_CMP_DIRECT_UNSAFE(((a)->field), ((b)->field)) + +#define _NL_CMP_FIELD_BOOL(a, b, field) \ + _NL_CMP_DIRECT_BOOL(((a)->field), ((b)->field)) + +#define _NL_CMP_FIELD_STR(a, b, field) \ + _NL_CMP_DIRECT_STRCMP(((a)->field), ((b)->field)) + +#define _NL_CMP_FIELD_STR0(a, b, field) \ + _NL_CMP_DIRECT_STRCMP0(((a)->field), ((b)->field)) + +#define _NL_CMP_FIELD_STR_INTERNED(a, b, field) \ + _NL_CMP_DIRECT_STR_INTERNED(((a)->field), ((b)->field)) + +#define _NL_CMP_FIELD_MEMCMP_LEN(a, b, field, len) \ + _NL_CMP_DIRECT_MEMCMP(&((a)->field), &((b)->field), \ + _NL_MIN(len, sizeof((a)->field))) + +#define _NL_CMP_FIELD_MEMCMP(a, b, field) \ + _NL_CMP_DIRECT_MEMCMP(&((a)->field), &((b)->field), sizeof((a)->field)) + +#define _NL_CMP_FIELD_IN6ADDR(a, b, field) \ + _NL_CMP_DIRECT_IN6ADDR(&((a)->field), &((b)->field)) + +/*****************************************************************************/ + +/* internal macro to calculate the size of a struct @type up to (and including) @field. + * this will be used for .minlen policy fields, so that we require only a field of up + * to the given size. */ +#define _nl_offsetofend(type, field) \ + (offsetof(type, field) + sizeof(((type *)NULL)->field)) + +/*****************************************************************************/ + +#define _nl_clear_pointer(pp, destroy) \ + ({ \ + __typeof__(*(pp)) *_pp = (pp); \ + __typeof__(*_pp) _p; \ + int _changed = 0; \ + \ + if (_pp && (_p = *_pp)) { \ + _nl_unused const void *const _p_check_is_pointer = _p; \ + \ + *_pp = NULL; \ + \ + (destroy)(_p); \ + \ + _changed = 1; \ + } \ + _changed; \ + }) + +#define _nl_clear_free(pp) _nl_clear_pointer(pp, free) + +#define _nl_steal_pointer(pp) \ + ({ \ + __typeof__(*(pp)) *const _pp = (pp); \ + __typeof__(*_pp) _p = NULL; \ + \ + if (_pp && (_p = *_pp)) { \ + *_pp = NULL; \ + } \ + \ + _p; \ + }) + +/*****************************************************************************/ + +#define _nl_malloc_maybe_a(alloca_maxlen, bytes, to_free) \ + ({ \ + const size_t _bytes = (bytes); \ + __typeof__(to_free) _to_free = (to_free); \ + __typeof__(*_to_free) _ptr; \ + \ + _NL_STATIC_ASSERT((alloca_maxlen) <= 500); \ + _nl_assert(_to_free && !*_to_free); \ + \ + if (_bytes <= (alloca_maxlen)) { \ + _ptr = alloca(_bytes); \ + } else { \ + _ptr = malloc(_bytes); \ + *_to_free = _ptr; \ + }; \ + \ + _ptr; \ + }) + +/*****************************************************************************/ + +static inline char *_nl_strncpy_trunc(char *dst, const char *src, size_t len) +{ + /* we don't use/reimplement strlcpy(), because we want the fill-all-with-NUL + * behavior of strncpy(). This is just strncpy() with gracefully handling truncation + * (and disabling the "-Wstringop-truncation" warning). + * + * Note that truncation is silently accepted. + */ + + _NL_PRAGMA_WARNING_DISABLE("-Wstringop-truncation"); + _NL_PRAGMA_WARNING_DISABLE("-Wstringop-overflow"); + + if (len > 0) { + _nl_assert(dst); + _nl_assert(src); + + strncpy(dst, src, len); + + dst[len - 1] = '\0'; + } + + _NL_PRAGMA_WARNING_REENABLE; + _NL_PRAGMA_WARNING_REENABLE; + + return dst; +} + +static inline char *_nl_strncpy_assert(char *dst, const char *src, size_t len) +{ + /* we don't use/reimplement strlcpy(), because we want the fill-all-with-NUL + * behavior of strncpy(). This is just strncpy() with assertion against truncation + * (and disabling the "-Wstringop-truncation" warning). + * + * Note that truncation is still a bug and there is an _nl_assert() + * against that. + */ + + _NL_PRAGMA_WARNING_DISABLE("-Wstringop-truncation"); + _NL_PRAGMA_WARNING_DISABLE("-Wstringop-overflow"); + + if (len > 0) { + _nl_assert(dst); + _nl_assert(src); + + strncpy(dst, src, len); + + _nl_assert(dst[len - 1] == '\0'); + + dst[len - 1] = '\0'; + } + + _NL_PRAGMA_WARNING_REENABLE; + _NL_PRAGMA_WARNING_REENABLE; + + return dst; +} + +#define _NL_RETURN_ON_ERR(cmd) \ + do { \ + int _err; \ + \ + _err = (cmd); \ + if (_err < 0) \ + return _err; \ + } while (0) + +#define _NL_RETURN_E_ON_ERR(e, cmd) \ + do { \ + int _err; \ + \ + _err = (cmd); \ + if (_err < 0) { \ + _NL_STATIC_ASSERT((e) > 0); \ + return -(e); \ + } \ + } while (0) + +/* _NL_RETURN_ON_PUT_ERR() shall only be used with a put command (nla_put or nlmsg_append). + * These commands can either fail with a regular error code (which gets propagated) + * or with -NLE_NOMEM. However, they don't really try to allocate memory, so we don't + * want to propagate -NLE_NOMEM. Instead, we coerce such failure to -NLE_MSGSIZE. */ +#define _NL_RETURN_ON_PUT_ERR(put_cmd) \ + do { \ + int _err; \ + \ + _err = (put_cmd); \ + if (_err < 0) { \ + if (_err == -NLE_NOMEM) { \ + /* nla_put() returns -NLE_NOMEM in case of out of buffer size. We don't + * want to propagate that error and map it to -NLE_MSGSIZE. */ \ + return -NLE_MSGSIZE; \ + } \ + /* any other error can only be due to invalid parameters. Propagate the + * error, however also assert that it cannot be reached. */ \ + _nl_assert_not_reached(); \ + return _err; \ + } else \ + _nl_assert(_err == 0); \ + } while (0) + +static inline int _nl_close(int fd) +{ + int r; + + r = close(fd); + _nl_assert(r == 0 || fd < 0 || errno != EBADF); + return r; +} + +static inline void *_nl_memdup(const void *ptr, size_t len) +{ + void *p; + + if (len == 0) { + /* malloc() leaves it implementation defined whether to return NULL. + * Callers rely on returning NULL if len is zero. */ + return NULL; + } + + p = malloc(len); + if (!p) + return NULL; + memcpy(p, ptr, len); + return p; +} + +#define _nl_memdup_ptr(ptr) ((__typeof__(ptr))_nl_memdup((ptr), sizeof(*(ptr)))) + +/*****************************************************************************/ + +typedef union { + in_addr_t addr4; + struct in_addr a4; + struct in6_addr a6; +} _NLIPAddr; + +static inline char *_nl_inet_ntop(int addr_family, const void *addr, + char buf[static INET_ADDRSTRLEN]) +{ + char *r; + + _nl_assert_addr_family(addr_family); + _nl_assert(addr); + + /* inet_ntop() is documented to fail, but if we pass a known address family + * and a suitably large buffer, it cannot. Assert for that. */ + + r = (char *)inet_ntop(addr_family, addr, buf, + (addr_family == AF_INET) ? INET_ADDRSTRLEN : + INET6_ADDRSTRLEN); + _nl_assert(r == buf); + _nl_assert(strlen(r) < ((addr_family == AF_INET) ? INET_ADDRSTRLEN : + INET6_ADDRSTRLEN)); + + return r; +} + +static inline char *_nl_inet_ntop_dup(int addr_family, const void *addr) +{ + return (char *)_nl_inet_ntop(addr_family, addr, + malloc((addr_family == AF_INET) ? + INET_ADDRSTRLEN : + INET6_ADDRSTRLEN)); +} + +/*****************************************************************************/ + +#define _NL_AUTO_DEFINE_FCN_VOID0(CastType, name, func) \ + static inline void name(void *v) \ + { \ + if (*((CastType *)v)) \ + func(*((CastType *)v)); \ + } \ + struct _nl_dummy_for_tailing_semicolon + +#define _NL_AUTO_DEFINE_FCN_STRUCT(CastType, name, func) \ + static inline void name(CastType *v) \ + { \ + if (v) \ + func(v); \ + } \ + struct _nl_dummy_for_tailing_semicolon + +#define _NL_AUTO_DEFINE_FCN_TYPED0(CastType, name, func) \ + static inline void name(CastType *v) \ + { \ + if (*v) \ + func(*v); \ + } \ + struct _nl_dummy_for_tailing_semicolon + +#define _nl_auto_free _nl_auto(_nl_auto_free_fcn) +_NL_AUTO_DEFINE_FCN_VOID0(void *, _nl_auto_free_fcn, free); + +#endif /* __NETLINK_BASE_NL_BASE_UTILS_H__ */ |