diff options
author | Thomas Haller <thaller@redhat.com> | 2019-08-08 16:26:23 (GMT) |
---|---|---|
committer | Thomas Haller <thaller@redhat.com> | 2019-08-09 14:48:55 (GMT) |
commit | 6c574429d900d75cb8d77e65bf1d3ae2a87bf44a (patch) | |
tree | e6002062a6fa88b0d3665f65b22afb1de18206b2 | |
parent | 325d30403ad30ea6cd54d651ede9e11f14ae03cf (diff) | |
download | libnl-6c574429d900d75cb8d77e65bf1d3ae2a87bf44a.zip libnl-6c574429d900d75cb8d77e65bf1d3ae2a87bf44a.tar.gz libnl-6c574429d900d75cb8d77e65bf1d3ae2a87bf44a.tar.bz2 |
utils: add internal helper macros for cleanup
Yes, these use gcc-isms like typeof(), __attribute__((__unused__)),
__attribute__((__cleanup__(fcn))) and expression statements.
First of all, this is now only required when building libnl3 itself.
The public headers still should to be conservative and only use C89
features.
Also, clang supports these too, so you can at least build libnl3 with
gcc and clang. Since libnl3 uses internally linux headers, and the linux
kernel also can only be compiled with gcc (and maybe clang), it seems
clear that on the target platform a suitable compiler is available.
If there is a reasonable request of a real-world compiler that is not
able to compile this, we can revisit some choices. But not having
__attribute__((__cleanup__(fcn))) is like programming C from a decade
ago. Especially during parsing (which libnl3 does obviously a lot), this
allows to return-early while cleanup up memory. While this sounds simple
to get right manually, in practice the resulting code is either
unnecessary complex or simply buggy.
To make implementing libnl3 more convenient, these helpers are
introduced.
-rw-r--r-- | include/netlink-private/utils.h | 195 |
1 files changed, 194 insertions, 1 deletions
diff --git a/include/netlink-private/utils.h b/include/netlink-private/utils.h index f038a80..a71dc63 100644 --- a/include/netlink-private/utils.h +++ b/include/netlink-private/utils.h @@ -13,6 +13,7 @@ #define NETLINK_UTILS_PRIV_H_ #include <byteswap.h> + #if __BYTE_ORDER == __BIG_ENDIAN #define ntohll(x) (x) #elif __BYTE_ORDER == __LITTLE_ENDIAN @@ -20,11 +21,203 @@ #endif #define htonll(x) ntohll(x) -extern const char * nl_strerror_l(int err); +/*****************************************************************************/ + +#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_unused __attribute__ ((__unused__)) +#define _nl_auto(fcn) __attribute__ ((__cleanup__(fcn))) + +/*****************************************************************************/ + +#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_AUTO_DEFINE_FCN_VOID0(CastType, name, func) \ +static inline void name (void *v) \ +{ \ + if (*((CastType *) v)) \ + func (*((CastType *) v)); \ +} + +#define _nl_auto_free _nl_auto(_nl_auto_free_fcn) +_NL_AUTO_DEFINE_FCN_VOID0 (void *, _nl_auto_free_fcn, free) + +/*****************************************************************************/ + +extern const char *nl_strerror_l(int err); + +/*****************************************************************************/ /* 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 trunction + * (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(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 trunction + * (and disabling the "-Wstringop-truncation" warning). + * + * Note that truncation is still a bug and there is an _nl_assert() + * against that. + */ + + if (len > 0) { + _nl_assert(dst); + _nl_assert(src); + + strncpy(dst, src, len); + + /* Truncation is a bug and we assert against it. But note that this + * assertion is disabled by default because we cannot be sure that + * there are not wrong uses of _nl_strncpy() where truncation might + * happen (wrongly!!). */ + _nl_assert (memchr(dst, '\0', len)); + + dst[len - 1] = '\0'; + } + + return dst; +} + #endif |