mirror of
https://gitlab.freedesktop.org/NetworkManager/NetworkManager.git
synced 2025-12-28 12:10:10 +01:00
We want to parse "/proc/cmdline". That is space separated with support for quoting and escaping. Our implementation becomes part of stable behavior, and we should interpret the kernel command line the same way as the system does. That means, our implementation should match systemd's.
2080 lines
73 KiB
C
2080 lines
73 KiB
C
// SPDX-License-Identifier: LGPL-2.1+
|
|
/*
|
|
* Copyright (C) 2016 Red Hat, Inc.
|
|
*/
|
|
|
|
#ifndef __NM_SHARED_UTILS_H__
|
|
#define __NM_SHARED_UTILS_H__
|
|
|
|
#include <netinet/in.h>
|
|
|
|
/*****************************************************************************/
|
|
|
|
pid_t nm_utils_gettid (void);
|
|
|
|
gboolean _nm_assert_on_main_thread (void);
|
|
|
|
#if NM_MORE_ASSERTS > 5
|
|
#define NM_ASSERT_ON_MAIN_THREAD() G_STMT_START { nm_assert (_nm_assert_on_main_thread ()); } G_STMT_END
|
|
#else
|
|
#define NM_ASSERT_ON_MAIN_THREAD() G_STMT_START { ; } G_STMT_END
|
|
#endif
|
|
|
|
/*****************************************************************************/
|
|
|
|
static inline gboolean
|
|
_NM_INT_NOT_NEGATIVE (gssize val)
|
|
{
|
|
/* whether an enum (without negative values) is a signed int, depends on compiler options
|
|
* and compiler implementation.
|
|
*
|
|
* When using such an enum for accessing an array, one naturally wants to check
|
|
* that the enum is not negative. However, the compiler doesn't like a plain
|
|
* comparison "enum_val >= 0", because (if the enum is unsigned), it will warn
|
|
* that the expression is always true *duh*. Not even a cast to a signed
|
|
* type helps to avoid the compiler warning in any case.
|
|
*
|
|
* The sole purpose of this function is to avoid a compiler warning, when checking
|
|
* that an enum is not negative. */
|
|
return val >= 0;
|
|
}
|
|
|
|
/* check whether the integer value is smaller than G_MAXINT32. This macro exists
|
|
* for the sole purpose, that a plain "((int) value <= G_MAXINT32)" comparison
|
|
* may cause the compiler or coverity that this check is always TRUE. But the
|
|
* check depends on compile time and the size of C type "int". Of course, most
|
|
* of the time in is gint32 and an int value is always <= G_MAXINT32. The check
|
|
* exists to catch cases where that is not true.
|
|
*
|
|
* Together with the G_STATIC_ASSERT(), we make sure that this is always satisfied. */
|
|
G_STATIC_ASSERT (sizeof (int) == sizeof (gint32));
|
|
#if _NM_CC_SUPPORT_GENERIC
|
|
#define _NM_INT_LE_MAXINT32(value) \
|
|
({ \
|
|
_nm_unused typeof (value) _value = (value); \
|
|
\
|
|
_Generic((value), \
|
|
int: TRUE \
|
|
); \
|
|
})
|
|
#else
|
|
#define _NM_INT_LE_MAXINT32(value) ({ \
|
|
_nm_unused typeof (value) _value = (value); \
|
|
_nm_unused const int *_p_value = &_value; \
|
|
\
|
|
TRUE; \
|
|
})
|
|
#endif
|
|
|
|
/*****************************************************************************/
|
|
|
|
static inline char
|
|
nm_utils_addr_family_to_char (int addr_family)
|
|
{
|
|
switch (addr_family) {
|
|
case AF_UNSPEC: return 'X';
|
|
case AF_INET: return '4';
|
|
case AF_INET6: return '6';
|
|
}
|
|
g_return_val_if_reached ('?');
|
|
}
|
|
|
|
static inline gsize
|
|
nm_utils_addr_family_to_size (int addr_family)
|
|
{
|
|
switch (addr_family) {
|
|
case AF_INET: return sizeof (in_addr_t);
|
|
case AF_INET6: return sizeof (struct in6_addr);
|
|
}
|
|
g_return_val_if_reached (0);
|
|
}
|
|
|
|
static inline int
|
|
nm_utils_addr_family_from_size (gsize len)
|
|
{
|
|
switch (len) {
|
|
case sizeof (in_addr_t): return AF_INET;
|
|
case sizeof (struct in6_addr): return AF_INET6;
|
|
}
|
|
return AF_UNSPEC;
|
|
}
|
|
|
|
#define nm_assert_addr_family(addr_family) \
|
|
nm_assert (NM_IN_SET ((addr_family), AF_INET, AF_INET6))
|
|
|
|
/*****************************************************************************/
|
|
|
|
typedef struct {
|
|
union {
|
|
guint8 addr_ptr[1];
|
|
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 addr_eth[6 /*ETH_ALEN*/];
|
|
|
|
guint8 array[sizeof (struct in6_addr)];
|
|
};
|
|
} NMIPAddr;
|
|
|
|
#define NM_IP_ADDR_INIT { .array = { 0 } }
|
|
|
|
extern const NMIPAddr nm_ip_addr_zero;
|
|
|
|
static inline int
|
|
nm_ip_addr_cmp (int addr_family, gconstpointer a, gconstpointer b)
|
|
{
|
|
nm_assert_addr_family (addr_family);
|
|
nm_assert (a);
|
|
nm_assert (b);
|
|
|
|
return memcmp (a, b, nm_utils_addr_family_to_size (addr_family));
|
|
}
|
|
|
|
static inline gboolean
|
|
nm_ip_addr_equal (int addr_family, gconstpointer a, gconstpointer b)
|
|
{
|
|
return nm_ip_addr_cmp (addr_family, a, b) == 0;
|
|
}
|
|
|
|
static inline gboolean
|
|
nm_ip_addr_is_null (int addr_family, gconstpointer addr)
|
|
{
|
|
nm_assert (addr);
|
|
if (addr_family == AF_INET6)
|
|
return IN6_IS_ADDR_UNSPECIFIED ((const struct in6_addr *) addr);
|
|
nm_assert (addr_family == AF_INET);
|
|
return ((const struct in_addr *) addr)->s_addr == 0;
|
|
}
|
|
|
|
static inline void
|
|
nm_ip_addr_set (int addr_family, gpointer dst, gconstpointer src)
|
|
{
|
|
nm_assert_addr_family (addr_family);
|
|
nm_assert (dst);
|
|
nm_assert (src);
|
|
|
|
memcpy (dst,
|
|
src,
|
|
(addr_family != AF_INET6)
|
|
? sizeof (in_addr_t)
|
|
: sizeof (struct in6_addr));
|
|
}
|
|
|
|
gboolean nm_ip_addr_set_from_untrusted (int addr_family,
|
|
gpointer dst,
|
|
gconstpointer src,
|
|
gsize src_len,
|
|
int *out_addr_family);
|
|
|
|
static inline gboolean
|
|
nm_ip4_addr_is_localhost (in_addr_t addr4)
|
|
{
|
|
return (addr4 & htonl (0xFF000000u)) == htonl (0x7F000000u);
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
|
|
struct ether_addr;
|
|
|
|
static inline int
|
|
nm_utils_ether_addr_cmp (const struct ether_addr *a1, const struct ether_addr *a2)
|
|
{
|
|
nm_assert (a1);
|
|
nm_assert (a2);
|
|
return memcmp (a1, a2, 6 /*ETH_ALEN*/);
|
|
}
|
|
|
|
static inline gboolean
|
|
nm_utils_ether_addr_equal (const struct ether_addr *a1, const struct ether_addr *a2)
|
|
{
|
|
return nm_utils_ether_addr_cmp (a1, a2) == 0;
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
|
|
#define NM_UTILS_INET_ADDRSTRLEN INET6_ADDRSTRLEN
|
|
|
|
static inline const char *
|
|
nm_utils_inet_ntop (int addr_family, gconstpointer addr, char *dst)
|
|
{
|
|
const char *s;
|
|
|
|
const char *inet_ntop (int af,
|
|
const void *src,
|
|
char *dst,
|
|
socklen_t size);
|
|
|
|
nm_assert_addr_family (addr_family);
|
|
nm_assert (addr);
|
|
nm_assert (dst);
|
|
|
|
s = inet_ntop (addr_family,
|
|
addr,
|
|
dst,
|
|
addr_family == AF_INET6 ? INET6_ADDRSTRLEN : INET_ADDRSTRLEN);
|
|
nm_assert (s);
|
|
return s;
|
|
}
|
|
|
|
static inline const char *
|
|
_nm_utils_inet4_ntop (in_addr_t addr, char dst[static INET_ADDRSTRLEN])
|
|
{
|
|
return nm_utils_inet_ntop (AF_INET, &addr, dst);
|
|
}
|
|
|
|
static inline const char *
|
|
_nm_utils_inet6_ntop (const struct in6_addr *addr, char dst[static INET6_ADDRSTRLEN])
|
|
{
|
|
return nm_utils_inet_ntop (AF_INET6, addr, dst);
|
|
}
|
|
|
|
static inline char *
|
|
nm_utils_inet_ntop_dup (int addr_family, gconstpointer addr)
|
|
{
|
|
char buf[NM_UTILS_INET_ADDRSTRLEN];
|
|
|
|
return g_strdup (nm_utils_inet_ntop (addr_family, addr, buf));
|
|
}
|
|
|
|
static inline char *
|
|
nm_utils_inet4_ntop_dup (in_addr_t addr)
|
|
{
|
|
return nm_utils_inet_ntop_dup (AF_INET, &addr);
|
|
}
|
|
|
|
static inline char *
|
|
nm_utils_inet6_ntop_dup (const struct in6_addr *addr)
|
|
{
|
|
return nm_utils_inet_ntop_dup (AF_INET6, addr);
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
|
|
gboolean nm_utils_ipaddr_is_valid (int addr_family,
|
|
const char *str_addr);
|
|
|
|
gboolean nm_utils_ipaddr_is_normalized (int addr_family,
|
|
const char *str_addr);
|
|
|
|
/*****************************************************************************/
|
|
|
|
#define NM_CMP_RETURN(c) \
|
|
G_STMT_START { \
|
|
const int _cc = (c); \
|
|
if (_cc) \
|
|
return _cc < 0 ? -1 : 1; \
|
|
} G_STMT_END
|
|
|
|
#define NM_CMP_RETURN_DIRECT(c) \
|
|
G_STMT_START { \
|
|
const int _cc = (c); \
|
|
if (_cc) \
|
|
return _cc; \
|
|
} G_STMT_END
|
|
|
|
#define NM_CMP_SELF(a, b) \
|
|
G_STMT_START { \
|
|
typeof (a) _a = (a); \
|
|
typeof (b) _b = (b); \
|
|
\
|
|
if (_a == _b) \
|
|
return 0; \
|
|
if (!_a) \
|
|
return -1; \
|
|
if (!_b) \
|
|
return 1; \
|
|
} G_STMT_END
|
|
|
|
#define NM_CMP_DIRECT(a, b) \
|
|
G_STMT_START { \
|
|
typeof (a) _a = (a); \
|
|
typeof (b) _b = (b); \
|
|
\
|
|
if (_a != _b) \
|
|
return (_a < _b) ? -1 : 1; \
|
|
} G_STMT_END
|
|
|
|
/* 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 NM_CMP_DIRECT_PTR(a, b) \
|
|
NM_CMP_DIRECT ((uintptr_t) ((void *) (a)), \
|
|
(uintptr_t) ((void *) (b)))
|
|
|
|
#define NM_CMP_DIRECT_MEMCMP(a, b, size) \
|
|
NM_CMP_RETURN (memcmp ((a), (b), (size)))
|
|
|
|
#define NM_CMP_DIRECT_STRCMP(a, b) \
|
|
NM_CMP_RETURN_DIRECT (strcmp ((a), (b)))
|
|
|
|
#define NM_CMP_DIRECT_STRCMP0(a, b) \
|
|
NM_CMP_RETURN_DIRECT (nm_strcmp0 ((a), (b)))
|
|
|
|
#define NM_CMP_DIRECT_IN6ADDR(a, b) \
|
|
G_STMT_START { \
|
|
const struct in6_addr *const _a = (a); \
|
|
const struct in6_addr *const _b = (b); \
|
|
NM_CMP_RETURN (memcmp (_a, _b, sizeof (struct in6_addr))); \
|
|
} G_STMT_END
|
|
|
|
#define NM_CMP_FIELD(a, b, field) \
|
|
NM_CMP_DIRECT (((a)->field), ((b)->field))
|
|
|
|
#define NM_CMP_FIELD_UNSAFE(a, b, field) \
|
|
G_STMT_START { \
|
|
/* it's unsafe, because it evaluates the arguments more then once.
|
|
* This is necessary for bitfields, for which typeof() doesn't work. */ \
|
|
if (((a)->field) != ((b)->field)) \
|
|
return ((a)->field < ((b)->field)) ? -1 : 1; \
|
|
} G_STMT_END
|
|
|
|
#define NM_CMP_FIELD_BOOL(a, b, field) \
|
|
NM_CMP_DIRECT (!!((a)->field), !!((b)->field))
|
|
|
|
#define NM_CMP_FIELD_STR(a, b, field) \
|
|
NM_CMP_RETURN (strcmp (((a)->field), ((b)->field)))
|
|
|
|
#define NM_CMP_FIELD_STR_INTERNED(a, b, field) \
|
|
G_STMT_START { \
|
|
const char *_a = ((a)->field); \
|
|
const char *_b = ((b)->field); \
|
|
\
|
|
if (_a != _b) { \
|
|
NM_CMP_RETURN_DIRECT (nm_strcmp0 (_a, _b)); \
|
|
} \
|
|
} G_STMT_END
|
|
|
|
#define NM_CMP_FIELD_STR0(a, b, field) \
|
|
NM_CMP_RETURN_DIRECT (nm_strcmp0 (((a)->field), ((b)->field)))
|
|
|
|
#define NM_CMP_FIELD_MEMCMP_LEN(a, b, field, len) \
|
|
NM_CMP_RETURN (memcmp (&((a)->field), &((b)->field), \
|
|
NM_MIN (len, sizeof ((a)->field))))
|
|
|
|
#define NM_CMP_FIELD_MEMCMP(a, b, field) \
|
|
NM_CMP_RETURN (memcmp (&((a)->field), \
|
|
&((b)->field), \
|
|
sizeof ((a)->field)))
|
|
|
|
#define NM_CMP_FIELD_IN6ADDR(a, b, field) \
|
|
G_STMT_START { \
|
|
const struct in6_addr *const _a = &((a)->field); \
|
|
const struct in6_addr *const _b = &((b)->field); \
|
|
NM_CMP_RETURN (memcmp (_a, _b, sizeof (struct in6_addr))); \
|
|
} G_STMT_END
|
|
|
|
/*****************************************************************************/
|
|
|
|
gboolean nm_utils_memeqzero (gconstpointer data, gsize length);
|
|
|
|
/*****************************************************************************/
|
|
|
|
extern const void *const _NM_PTRARRAY_EMPTY[1];
|
|
|
|
#define NM_PTRARRAY_EMPTY(type) ((type const*) _NM_PTRARRAY_EMPTY)
|
|
|
|
static inline void
|
|
_nm_utils_strbuf_init (char *buf, gsize len, char **p_buf_ptr, gsize *p_buf_len)
|
|
{
|
|
NM_SET_OUT (p_buf_len, len);
|
|
NM_SET_OUT (p_buf_ptr, buf);
|
|
buf[0] = '\0';
|
|
}
|
|
|
|
#define nm_utils_strbuf_init(buf, p_buf_ptr, p_buf_len) \
|
|
G_STMT_START { \
|
|
G_STATIC_ASSERT (G_N_ELEMENTS (buf) == sizeof (buf) && sizeof (buf) > sizeof (char *)); \
|
|
_nm_utils_strbuf_init ((buf), sizeof (buf), (p_buf_ptr), (p_buf_len)); \
|
|
} G_STMT_END
|
|
void nm_utils_strbuf_append (char **buf, gsize *len, const char *format, ...) _nm_printf (3, 4);
|
|
void nm_utils_strbuf_append_c (char **buf, gsize *len, char c);
|
|
void nm_utils_strbuf_append_str (char **buf, gsize *len, const char *str);
|
|
void nm_utils_strbuf_append_bin (char **buf, gsize *len, gconstpointer str, gsize str_len);
|
|
void nm_utils_strbuf_seek_end (char **buf, gsize *len);
|
|
|
|
const char *nm_strquote (char *buf, gsize buf_len, const char *str);
|
|
|
|
static inline gboolean
|
|
nm_utils_is_separator (const char c)
|
|
{
|
|
return NM_IN_SET (c, ' ', '\t');
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
|
|
GBytes *nm_gbytes_get_empty (void);
|
|
|
|
static inline gboolean
|
|
nm_gbytes_equal0 (GBytes *a, GBytes *b)
|
|
{
|
|
return a == b || (a && b && g_bytes_equal (a, b));
|
|
}
|
|
|
|
gboolean nm_utils_gbytes_equal_mem (GBytes *bytes,
|
|
gconstpointer mem_data,
|
|
gsize mem_len);
|
|
|
|
GVariant *nm_utils_gbytes_to_variant_ay (GBytes *bytes);
|
|
|
|
GVariant *nm_utils_strdict_to_variant_ass (GHashTable *strdict);
|
|
GVariant *nm_utils_strdict_to_variant_asv (GHashTable *strdict);
|
|
|
|
/*****************************************************************************/
|
|
|
|
GVariant *nm_utils_gvariant_vardict_filter (GVariant *src,
|
|
gboolean (*filter_fcn) (const char *key,
|
|
GVariant *val,
|
|
char **out_key,
|
|
GVariant **out_val,
|
|
gpointer user_data),
|
|
gpointer user_data);
|
|
|
|
GVariant *
|
|
nm_utils_gvariant_vardict_filter_drop_one (GVariant *src,
|
|
const char *key);
|
|
|
|
/*****************************************************************************/
|
|
|
|
static inline int
|
|
nm_utils_hexchar_to_int (char ch)
|
|
{
|
|
G_STATIC_ASSERT_EXPR ('0' < 'A');
|
|
G_STATIC_ASSERT_EXPR ('A' < 'a');
|
|
|
|
if (ch >= '0') {
|
|
if (ch <= '9')
|
|
return ch - '0';
|
|
if (ch >= 'A') {
|
|
if (ch <= 'F')
|
|
return ((int) ch) + (10 - (int) 'A');
|
|
if (ch >= 'a' && ch <= 'f')
|
|
return ((int) ch) + (10 - (int) 'a');
|
|
}
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
|
|
const char *nm_utils_dbus_path_get_last_component (const char *dbus_path);
|
|
|
|
int nm_utils_dbus_path_cmp (const char *dbus_path_a, const char *dbus_path_b);
|
|
|
|
/*****************************************************************************/
|
|
|
|
typedef enum {
|
|
NM_UTILS_STRSPLIT_SET_FLAGS_NONE = 0,
|
|
|
|
/* by default, strsplit will coalesce consecutive delimiters and remove
|
|
* them from the result. If this flag is present, empty values are preserved
|
|
* and returned.
|
|
*
|
|
* When combined with %NM_UTILS_STRSPLIT_SET_FLAGS_STRSTRIP, if a value gets
|
|
* empty after strstrip(), it also gets removed. */
|
|
NM_UTILS_STRSPLIT_SET_FLAGS_PRESERVE_EMPTY = (1u << 0),
|
|
|
|
/* %NM_UTILS_STRSPLIT_SET_FLAGS_ALLOW_ESCAPING means that delimiters prefixed
|
|
* by a backslash are not treated as a separator. Such delimiters and their escape
|
|
* character are copied to the current word without unescaping them. In general,
|
|
* nm_utils_strsplit_set_full() does not remove any backslash escape characters
|
|
* and does no unescaping. It only considers them for skipping to split at
|
|
* an escaped delimiter.
|
|
*
|
|
* If this is combined with (or implied by %NM_UTILS_STRSPLIT_SET_FLAGS_ESCAPED), then
|
|
* the backslash escapes are removed from the result.
|
|
*/
|
|
NM_UTILS_STRSPLIT_SET_FLAGS_ALLOW_ESCAPING = (1u << 1),
|
|
|
|
/* If flag is set, does the same as g_strstrip() on the returned tokens.
|
|
* This will remove leading and trailing ascii whitespaces (g_ascii_isspace()
|
|
* and NM_ASCII_SPACES).
|
|
*
|
|
* - when combined with !%NM_UTILS_STRSPLIT_SET_FLAGS_PRESERVE_EMPTY,
|
|
* empty tokens will be removed (and %NULL will be returned if that
|
|
* results in an empty string array).
|
|
* - when combined with %NM_UTILS_STRSPLIT_SET_FLAGS_ALLOW_ESCAPING,
|
|
* trailing whitespace escaped by backslash are not stripped. */
|
|
NM_UTILS_STRSPLIT_SET_FLAGS_STRSTRIP = (1u << 2),
|
|
|
|
/* This implies %NM_UTILS_STRSPLIT_SET_FLAGS_ALLOW_ESCAPING.
|
|
*
|
|
* This will do a final run over all tokens and remove all backslash
|
|
* escape characters that
|
|
* - precede a delimiter.
|
|
* - precede a backslash.
|
|
* - preceed a whitespace (with %NM_UTILS_STRSPLIT_SET_FLAGS_STRSTRIP).
|
|
*
|
|
* Note that with %NM_UTILS_STRSPLIT_SET_FLAGS_STRSTRIP, it is only
|
|
* necessary to escape the very last whitespace (if the delimiters
|
|
* are not whitespace themself). So, technically, it would be sufficient
|
|
* to only unescape a backslash before the last whitespace and the user
|
|
* still could express everything. However, such a rule would be complicated
|
|
* to understand, so when using backslash escaping with nm_utils_strsplit_set_full(),
|
|
* then all characters (including backslash) are treated verbatim, except:
|
|
*
|
|
* - "\\$DELIMITER" (escaped delimiter)
|
|
* - "\\\\" (escaped backslash)
|
|
* - "\\$SPACE" (escaped space) (with %NM_UTILS_STRSPLIT_SET_FLAGS_STRSTRIP).
|
|
*
|
|
* Note that all other escapes like "\\n" or "\\001" are left alone.
|
|
* That makes the escaping/unescaping rules simple. Also, for the most part
|
|
* a text is just taken as-is, with little additional rules. Only backslashes
|
|
* need extra care, and then only if they proceed one of the relevant characters.
|
|
*/
|
|
NM_UTILS_STRSPLIT_SET_FLAGS_ESCAPED = (1u << 3),
|
|
|
|
} NMUtilsStrsplitSetFlags;
|
|
|
|
const char **nm_utils_strsplit_set_full (const char *str,
|
|
const char *delimiter,
|
|
NMUtilsStrsplitSetFlags flags);
|
|
|
|
static inline const char **
|
|
nm_utils_strsplit_set_with_empty (const char *str,
|
|
const char *delimiters)
|
|
{
|
|
/* this returns the same result as g_strsplit_set(str, delimiters, -1), except
|
|
* it does not deep-clone the strv array.
|
|
* Also, for @str == "", this returns %NULL while g_strsplit_set() would return
|
|
* an empty strv array. */
|
|
return nm_utils_strsplit_set_full (str, delimiters, NM_UTILS_STRSPLIT_SET_FLAGS_PRESERVE_EMPTY);
|
|
}
|
|
|
|
static inline const char **
|
|
nm_utils_strsplit_set (const char *str,
|
|
const char *delimiters)
|
|
{
|
|
return nm_utils_strsplit_set_full (str, delimiters, NM_UTILS_STRSPLIT_SET_FLAGS_NONE);
|
|
}
|
|
|
|
gssize nm_utils_strv_find_first (char **list, gssize len, const char *needle);
|
|
|
|
char **_nm_utils_strv_cleanup (char **strv,
|
|
gboolean strip_whitespace,
|
|
gboolean skip_empty,
|
|
gboolean skip_repeated);
|
|
|
|
/*****************************************************************************/
|
|
|
|
static inline const char **
|
|
nm_utils_escaped_tokens_split (const char *str,
|
|
const char *delimiters)
|
|
{
|
|
return nm_utils_strsplit_set_full (str,
|
|
delimiters,
|
|
NM_UTILS_STRSPLIT_SET_FLAGS_ESCAPED
|
|
| NM_UTILS_STRSPLIT_SET_FLAGS_STRSTRIP);
|
|
}
|
|
|
|
typedef enum {
|
|
NM_UTILS_ESCAPED_TOKENS_ESCAPE_FLAGS_NONE = 0,
|
|
NM_UTILS_ESCAPED_TOKENS_ESCAPE_FLAGS_ESCAPE_SPACES = (1ull << 0),
|
|
NM_UTILS_ESCAPED_TOKENS_ESCAPE_FLAGS_ESCAPE_LEADING_SPACE = (1ull << 1),
|
|
NM_UTILS_ESCAPED_TOKENS_ESCAPE_FLAGS_ESCAPE_TRAILING_SPACE = (1ull << 2),
|
|
|
|
/* Backslash characters will be escaped as "\\\\" if they precede another
|
|
* character that makes it necessary. Such characters are:
|
|
*
|
|
* 1) before another '\\' backslash.
|
|
* 2) before any delimiter in @delimiters.
|
|
* 3) before any delimiter in @delimiters_as_needed.
|
|
* 4) before a white space, if ESCAPE_LEADING_SPACE or ESCAPE_TRAILING_SPACE is set.
|
|
* 5) before the end of the word
|
|
*
|
|
* Rule 4) is an extension. It's not immediately clear why with ESCAPE_LEADING_SPACE
|
|
* and ESCAPE_TRAILING_SPACE we want *all* backslashes before a white space escaped.
|
|
* The reason is, that we obviously want to use ESCAPE_LEADING_SPACE and ESCAPE_TRAILING_SPACE
|
|
* in cases, where we later parse the backslash escaped strings back, but allowing to strip
|
|
* unescaped white spaces. That means, we want that " a " gets escaped as "\\ a\\ ".
|
|
* On the other hand, we also want that " a\\ b " gets escaped as "\\ a\\\\ b\\ ",
|
|
* and not "\\ a\\ b\\ ". Because otherwise, the parser would need to treat "\\ "
|
|
* differently depending on whether the sequence is at the beginning, end or middle
|
|
* of the word.
|
|
*
|
|
* Rule 5) is also not immediately obvious. When used with ESCAPE_TRAILING_SPACE,
|
|
* we clearly want to allow that an escaped word can have arbitrary
|
|
* whitespace suffixes. That's why this mode exists. So we must escape "a\\" as
|
|
* "a\\\\", so that appending " " does not change the meaning.
|
|
* Also without ESCAPE_TRAILING_SPACE, we want in general that we can concatenate
|
|
* two escaped words without changing their meaning. If the words would be "a\\"
|
|
* and "," (with ',' being a delimiter), then the result must be "a\\\\" and "\\,"
|
|
* so that the concatenated word ("a\\\\\\,") is still the same. If we would escape
|
|
* them instead as "a\\" + "\\,", then the concatenated word would be "a\\\\," and
|
|
* different.
|
|
* */
|
|
NM_UTILS_ESCAPED_TOKENS_ESCAPE_FLAGS_ESCAPE_BACKSLASH_AS_NEEDED = (1ull << 3),
|
|
|
|
NM_UTILS_ESCAPED_TOKENS_ESCAPE_FLAGS_ESCAPE_BACKSLASH_ALWAYS = (1ull << 4),
|
|
} NMUtilsEscapedTokensEscapeFlags;
|
|
|
|
const char *nm_utils_escaped_tokens_escape_full (const char *str,
|
|
const char *delimiters,
|
|
const char *delimiters_as_needed,
|
|
NMUtilsEscapedTokensEscapeFlags flags,
|
|
char **out_to_free);
|
|
|
|
static inline const char *
|
|
nm_utils_escaped_tokens_escape (const char *str,
|
|
const char *delimiters,
|
|
char **out_to_free)
|
|
{
|
|
return nm_utils_escaped_tokens_escape_full (str,
|
|
delimiters,
|
|
NULL,
|
|
NM_UTILS_ESCAPED_TOKENS_ESCAPE_FLAGS_ESCAPE_BACKSLASH_ALWAYS
|
|
| NM_UTILS_ESCAPED_TOKENS_ESCAPE_FLAGS_ESCAPE_TRAILING_SPACE,
|
|
out_to_free);
|
|
}
|
|
|
|
static inline GString *
|
|
nm_utils_escaped_tokens_escape_gstr_assert (const char *str,
|
|
const char *delimiters,
|
|
GString *gstring)
|
|
{
|
|
#if NM_MORE_ASSERTS > 0
|
|
|
|
/* Just appends @str to @gstring, but also assert that
|
|
* no escaping is necessary.
|
|
*
|
|
* Use nm_utils_escaped_tokens_escape_gstr_assert() instead
|
|
* of nm_utils_escaped_tokens_escape_gstr(), if you *know* that
|
|
* @str contains no delimiters, no backslashes, and no trailing
|
|
* whitespace that requires escaping. */
|
|
|
|
nm_assert (str);
|
|
nm_assert (gstring);
|
|
nm_assert (delimiters);
|
|
|
|
{
|
|
gs_free char *str_to_free = NULL;
|
|
const char *str0;
|
|
|
|
str0 = nm_utils_escaped_tokens_escape (str, delimiters, &str_to_free);
|
|
nm_assert (str0 == str);
|
|
nm_assert (!str_to_free);
|
|
}
|
|
#endif
|
|
|
|
g_string_append (gstring, str);
|
|
return gstring;
|
|
}
|
|
|
|
static inline GString *
|
|
nm_utils_escaped_tokens_escape_gstr (const char *str,
|
|
const char *delimiters,
|
|
GString *gstring)
|
|
{
|
|
gs_free char *str_to_free = NULL;
|
|
|
|
nm_assert (str);
|
|
nm_assert (gstring);
|
|
|
|
g_string_append (gstring,
|
|
nm_utils_escaped_tokens_escape (str, delimiters, &str_to_free));
|
|
return gstring;
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
|
|
char **nm_utils_strsplit_quoted (const char *str);
|
|
|
|
/*****************************************************************************/
|
|
|
|
static inline const char **
|
|
nm_utils_escaped_tokens_options_split_list (const char *str)
|
|
{
|
|
return nm_utils_strsplit_set_full (str,
|
|
",",
|
|
NM_UTILS_STRSPLIT_SET_FLAGS_STRSTRIP
|
|
| NM_UTILS_STRSPLIT_SET_FLAGS_ALLOW_ESCAPING);
|
|
}
|
|
|
|
void nm_utils_escaped_tokens_options_split (char *str,
|
|
const char **out_key,
|
|
const char **out_val);
|
|
|
|
static inline const char *
|
|
nm_utils_escaped_tokens_options_escape_key (const char *key,
|
|
char **out_to_free)
|
|
{
|
|
return nm_utils_escaped_tokens_escape_full (key,
|
|
",=",
|
|
NULL,
|
|
NM_UTILS_ESCAPED_TOKENS_ESCAPE_FLAGS_ESCAPE_BACKSLASH_AS_NEEDED
|
|
| NM_UTILS_ESCAPED_TOKENS_ESCAPE_FLAGS_ESCAPE_LEADING_SPACE
|
|
| NM_UTILS_ESCAPED_TOKENS_ESCAPE_FLAGS_ESCAPE_TRAILING_SPACE,
|
|
out_to_free);
|
|
}
|
|
|
|
static inline const char *
|
|
nm_utils_escaped_tokens_options_escape_val (const char *val,
|
|
char **out_to_free)
|
|
{
|
|
return nm_utils_escaped_tokens_escape_full (val,
|
|
",",
|
|
"=",
|
|
NM_UTILS_ESCAPED_TOKENS_ESCAPE_FLAGS_ESCAPE_BACKSLASH_AS_NEEDED
|
|
| NM_UTILS_ESCAPED_TOKENS_ESCAPE_FLAGS_ESCAPE_LEADING_SPACE
|
|
| NM_UTILS_ESCAPED_TOKENS_ESCAPE_FLAGS_ESCAPE_TRAILING_SPACE,
|
|
out_to_free);
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
|
|
#define NM_UTILS_CHECKSUM_LENGTH_MD5 16
|
|
#define NM_UTILS_CHECKSUM_LENGTH_SHA1 20
|
|
#define NM_UTILS_CHECKSUM_LENGTH_SHA256 32
|
|
|
|
#define nm_utils_checksum_get_digest(sum, arr) \
|
|
G_STMT_START { \
|
|
GChecksum *const _sum = (sum); \
|
|
gsize _len; \
|
|
\
|
|
G_STATIC_ASSERT_EXPR ( sizeof (arr) == NM_UTILS_CHECKSUM_LENGTH_MD5 \
|
|
|| sizeof (arr) == NM_UTILS_CHECKSUM_LENGTH_SHA1 \
|
|
|| sizeof (arr) == NM_UTILS_CHECKSUM_LENGTH_SHA256); \
|
|
G_STATIC_ASSERT_EXPR (sizeof (arr) == G_N_ELEMENTS (arr)); \
|
|
\
|
|
nm_assert (_sum); \
|
|
\
|
|
_len = G_N_ELEMENTS (arr); \
|
|
\
|
|
g_checksum_get_digest (_sum, (arr), &_len); \
|
|
nm_assert (_len == G_N_ELEMENTS (arr)); \
|
|
} G_STMT_END
|
|
|
|
#define nm_utils_checksum_get_digest_len(sum, buf, len) \
|
|
G_STMT_START { \
|
|
GChecksum *const _sum = (sum); \
|
|
const gsize _len0 = (len); \
|
|
gsize _len; \
|
|
\
|
|
nm_assert (NM_IN_SET (_len0, NM_UTILS_CHECKSUM_LENGTH_MD5, \
|
|
NM_UTILS_CHECKSUM_LENGTH_SHA1, \
|
|
NM_UTILS_CHECKSUM_LENGTH_SHA256)); \
|
|
nm_assert (_sum); \
|
|
\
|
|
_len = _len0; \
|
|
g_checksum_get_digest (_sum, (buf), &_len); \
|
|
nm_assert (_len == _len0); \
|
|
} G_STMT_END
|
|
|
|
/*****************************************************************************/
|
|
|
|
guint32 _nm_utils_ip4_prefix_to_netmask (guint32 prefix);
|
|
guint32 _nm_utils_ip4_get_default_prefix (guint32 ip);
|
|
|
|
gconstpointer nm_utils_ipx_address_clear_host_address (int family, gpointer dst, gconstpointer src, guint8 plen);
|
|
in_addr_t nm_utils_ip4_address_clear_host_address (in_addr_t addr, guint8 plen);
|
|
const struct in6_addr *nm_utils_ip6_address_clear_host_address (struct in6_addr *dst, const struct in6_addr *src, guint8 plen);
|
|
int nm_utils_ip6_address_same_prefix_cmp (const struct in6_addr *addr_a, const struct in6_addr *addr_b, guint8 plen);
|
|
|
|
gboolean nm_utils_ip_is_site_local (int addr_family,
|
|
const void *address);
|
|
|
|
/*****************************************************************************/
|
|
|
|
gboolean nm_utils_parse_inaddr_bin_full (int addr_family,
|
|
gboolean accept_legacy,
|
|
const char *text,
|
|
int *out_addr_family,
|
|
gpointer out_addr);
|
|
static inline gboolean
|
|
nm_utils_parse_inaddr_bin (int addr_family,
|
|
const char *text,
|
|
int *out_addr_family,
|
|
gpointer out_addr)
|
|
{
|
|
return nm_utils_parse_inaddr_bin_full (addr_family, FALSE, text, out_addr_family, out_addr);
|
|
}
|
|
|
|
gboolean nm_utils_parse_inaddr (int addr_family,
|
|
const char *text,
|
|
char **out_addr);
|
|
|
|
gboolean nm_utils_parse_inaddr_prefix_bin (int addr_family,
|
|
const char *text,
|
|
int *out_addr_family,
|
|
gpointer out_addr,
|
|
int *out_prefix);
|
|
|
|
gboolean nm_utils_parse_inaddr_prefix (int addr_family,
|
|
const char *text,
|
|
char **out_addr,
|
|
int *out_prefix);
|
|
|
|
gint64 nm_g_ascii_strtoll (const char *nptr,
|
|
char **endptr,
|
|
guint base);
|
|
|
|
guint64 nm_g_ascii_strtoull (const char *nptr,
|
|
char **endptr,
|
|
guint base);
|
|
|
|
double nm_g_ascii_strtod (const char *nptr,
|
|
char **endptr);
|
|
|
|
gint64 _nm_utils_ascii_str_to_int64 (const char *str, guint base, gint64 min, gint64 max, gint64 fallback);
|
|
guint64 _nm_utils_ascii_str_to_uint64 (const char *str, guint base, guint64 min, guint64 max, guint64 fallback);
|
|
|
|
int _nm_utils_ascii_str_to_bool (const char *str,
|
|
int default_value);
|
|
|
|
/*****************************************************************************/
|
|
|
|
extern char _nm_utils_to_string_buffer[2096];
|
|
|
|
void nm_utils_to_string_buffer_init (char **buf, gsize *len);
|
|
gboolean nm_utils_to_string_buffer_init_null (gconstpointer obj, char **buf, gsize *len);
|
|
|
|
/*****************************************************************************/
|
|
|
|
typedef struct {
|
|
unsigned flag;
|
|
const char *name;
|
|
} NMUtilsFlags2StrDesc;
|
|
|
|
#define NM_UTILS_FLAGS2STR(f, n) { .flag = f, .name = ""n, }
|
|
|
|
#define NM_UTILS_FLAGS2STR_DEFINE(fcn_name, flags_type, ...) \
|
|
const char * \
|
|
fcn_name (flags_type flags, char *buf, gsize len) \
|
|
{ \
|
|
static const NMUtilsFlags2StrDesc descs[] = { \
|
|
__VA_ARGS__ \
|
|
}; \
|
|
G_STATIC_ASSERT (sizeof (flags_type) <= sizeof (unsigned)); \
|
|
return nm_utils_flags2str (descs, G_N_ELEMENTS (descs), flags, buf, len); \
|
|
};
|
|
|
|
const char *nm_utils_flags2str (const NMUtilsFlags2StrDesc *descs,
|
|
gsize n_descs,
|
|
unsigned flags,
|
|
char *buf,
|
|
gsize len);
|
|
|
|
/*****************************************************************************/
|
|
|
|
#define NM_UTILS_ENUM2STR(v, n) (void) 0; case v: s = ""n""; break; (void) 0
|
|
#define NM_UTILS_ENUM2STR_IGNORE(v) (void) 0; case v: break; (void) 0
|
|
|
|
#define NM_UTILS_ENUM2STR_DEFINE_FULL(fcn_name, lookup_type, int_fmt, ...) \
|
|
const char * \
|
|
fcn_name (lookup_type val, char *buf, gsize len) \
|
|
{ \
|
|
nm_utils_to_string_buffer_init (&buf, &len); \
|
|
if (len) { \
|
|
const char *s = NULL; \
|
|
switch (val) { \
|
|
(void) 0, \
|
|
__VA_ARGS__ \
|
|
(void) 0; \
|
|
}; \
|
|
if (s) \
|
|
g_strlcpy (buf, s, len); \
|
|
else \
|
|
g_snprintf (buf, len, "(%"int_fmt")", val); \
|
|
} \
|
|
return buf; \
|
|
}
|
|
|
|
#define NM_UTILS_ENUM2STR_DEFINE(fcn_name, lookup_type, ...) \
|
|
NM_UTILS_ENUM2STR_DEFINE_FULL (fcn_name, lookup_type, "d", __VA_ARGS__)
|
|
|
|
/*****************************************************************************/
|
|
|
|
#define _nm_g_slice_free_fcn_define(mem_size) \
|
|
static inline void \
|
|
_nm_g_slice_free_fcn_##mem_size (gpointer mem_block) \
|
|
{ \
|
|
g_slice_free1 (mem_size, mem_block); \
|
|
}
|
|
|
|
_nm_g_slice_free_fcn_define (1)
|
|
_nm_g_slice_free_fcn_define (2)
|
|
_nm_g_slice_free_fcn_define (4)
|
|
_nm_g_slice_free_fcn_define (8)
|
|
_nm_g_slice_free_fcn_define (10)
|
|
_nm_g_slice_free_fcn_define (12)
|
|
_nm_g_slice_free_fcn_define (16)
|
|
_nm_g_slice_free_fcn_define (32)
|
|
|
|
#define nm_g_slice_free_fcn1(mem_size) \
|
|
({ \
|
|
void (*_fcn) (gpointer); \
|
|
\
|
|
/* If mem_size is a compile time constant, the compiler
|
|
* will be able to optimize this. Hence, you don't want
|
|
* to call this with a non-constant size argument. */ \
|
|
G_STATIC_ASSERT_EXPR ( ((mem_size) == 1) \
|
|
|| ((mem_size) == 2) \
|
|
|| ((mem_size) == 4) \
|
|
|| ((mem_size) == 8) \
|
|
|| ((mem_size) == 10) \
|
|
|| ((mem_size) == 12) \
|
|
|| ((mem_size) == 16) \
|
|
|| ((mem_size) == 32)); \
|
|
switch ((mem_size)) { \
|
|
case 1: _fcn = _nm_g_slice_free_fcn_1; break; \
|
|
case 2: _fcn = _nm_g_slice_free_fcn_2; break; \
|
|
case 4: _fcn = _nm_g_slice_free_fcn_4; break; \
|
|
case 8: _fcn = _nm_g_slice_free_fcn_8; break; \
|
|
case 10: _fcn = _nm_g_slice_free_fcn_10; break; \
|
|
case 12: _fcn = _nm_g_slice_free_fcn_12; break; \
|
|
case 16: _fcn = _nm_g_slice_free_fcn_16; break; \
|
|
case 32: _fcn = _nm_g_slice_free_fcn_32; break; \
|
|
default: g_assert_not_reached (); _fcn = NULL; break; \
|
|
} \
|
|
_fcn; \
|
|
})
|
|
|
|
/**
|
|
* nm_g_slice_free_fcn:
|
|
* @type: type argument for sizeof() operator that you would
|
|
* pass to g_slice_new().
|
|
*
|
|
* Returns: a function pointer with GDestroyNotify signature
|
|
* for g_slice_free(type,*).
|
|
*
|
|
* Only certain types are implemented. You'll get a compile time
|
|
* error for the wrong types. */
|
|
#define nm_g_slice_free_fcn(type) (nm_g_slice_free_fcn1 (sizeof (type)))
|
|
|
|
#define nm_g_slice_free_fcn_gint64 (nm_g_slice_free_fcn (gint64))
|
|
|
|
/*****************************************************************************/
|
|
|
|
/* Like g_error_matches() however:
|
|
* - as macro it is always inlined.
|
|
* - the @domain is usually a error quark getter function that cannot
|
|
* be inlined. This macro calls the getter only if there is an error (lazy).
|
|
* - accept a list of allowed codes, instead of only one.
|
|
*/
|
|
#define nm_g_error_matches(error, err_domain, ...) \
|
|
({ \
|
|
const GError *const _error = (error); \
|
|
\
|
|
_error \
|
|
&& _error->domain == (err_domain) \
|
|
&& NM_IN_SET (_error->code, __VA_ARGS__); \
|
|
})
|
|
|
|
static inline void
|
|
nm_g_set_error_take (GError **error, GError *error_take)
|
|
{
|
|
if (!error_take)
|
|
g_return_if_reached ();
|
|
if (!error) {
|
|
g_error_free (error_take);
|
|
return;
|
|
}
|
|
if (*error) {
|
|
g_error_free (error_take);
|
|
g_return_if_reached ();
|
|
}
|
|
*error = error_take;
|
|
}
|
|
|
|
#define nm_g_set_error_take_lazy(error, error_take_lazy) \
|
|
G_STMT_START { \
|
|
GError **_error = (error); \
|
|
\
|
|
if (_error) \
|
|
nm_g_set_error_take (_error, (error_take_lazy)); \
|
|
} G_STMT_END
|
|
|
|
/**
|
|
* NMUtilsError:
|
|
* @NM_UTILS_ERROR_UNKNOWN: unknown or unclassified error
|
|
* @NM_UTILS_ERROR_CANCELLED_DISPOSING: when disposing an object that has
|
|
* pending aynchronous operations, the operation is cancelled with this
|
|
* error reason. Depending on the usage, this might indicate a bug because
|
|
* usually the target object should stay alive as long as there are pending
|
|
* operations.
|
|
*
|
|
* @NM_UTILS_ERROR_CONNECTION_AVAILABLE_INCOMPATIBLE: used for a very particular
|
|
* purpose during nm_device_check_connection_compatible() to indicate that
|
|
* the profile does not match the device already because their type differs.
|
|
* That is, there is a fundamental reason of trying to check a profile that
|
|
* cannot possibly match on this device.
|
|
* @NM_UTILS_ERROR_CONNECTION_AVAILABLE_UNMANAGED_DEVICE: used for a very particular
|
|
* purpose during nm_device_check_connection_available(), to indicate that the
|
|
* device is not available because it is unmanaged.
|
|
* @NM_UTILS_ERROR_CONNECTION_AVAILABLE_TEMPORARY: the profile is currently not
|
|
* available/compatible with the device, but this may be only temporary.
|
|
*
|
|
* @NM_UTILS_ERROR_SETTING_MISSING: the setting is missing
|
|
*
|
|
* @NM_UTILS_ERROR_INVALID_ARGUMENT: invalid argument.
|
|
*/
|
|
typedef enum {
|
|
NM_UTILS_ERROR_UNKNOWN = 0, /*< nick=Unknown >*/
|
|
NM_UTILS_ERROR_CANCELLED_DISPOSING, /*< nick=CancelledDisposing >*/
|
|
NM_UTILS_ERROR_INVALID_ARGUMENT, /*< nick=InvalidArgument >*/
|
|
|
|
/* the following codes have a special meaning and are exactly used for
|
|
* nm_device_check_connection_compatible() and nm_device_check_connection_available().
|
|
*
|
|
* Actually, their meaning is not very important (so, don't think too
|
|
* hard about the name of these error codes). What is important, is their
|
|
* relative order (i.e. the integer value of the codes). When manager
|
|
* searches for a suitable device, it will check all devices whether
|
|
* a profile can be activated. If they all fail, it will pick the error
|
|
* message from the device that returned the *highest* error code,
|
|
* in the hope that this message makes the most sense for the caller.
|
|
* */
|
|
NM_UTILS_ERROR_CONNECTION_AVAILABLE_INCOMPATIBLE,
|
|
NM_UTILS_ERROR_CONNECTION_AVAILABLE_UNMANAGED_DEVICE,
|
|
NM_UTILS_ERROR_CONNECTION_AVAILABLE_TEMPORARY,
|
|
|
|
NM_UTILS_ERROR_SETTING_MISSING,
|
|
|
|
} NMUtilsError;
|
|
|
|
#define NM_UTILS_ERROR (nm_utils_error_quark ())
|
|
GQuark nm_utils_error_quark (void);
|
|
|
|
void nm_utils_error_set_cancelled (GError **error,
|
|
gboolean is_disposing,
|
|
const char *instance_name);
|
|
|
|
static inline GError *
|
|
nm_utils_error_new_cancelled (gboolean is_disposing,
|
|
const char *instance_name)
|
|
{
|
|
GError *error = NULL;
|
|
|
|
nm_utils_error_set_cancelled (&error, is_disposing, instance_name);
|
|
return error;
|
|
}
|
|
|
|
gboolean nm_utils_error_is_cancelled_or_disposing (GError *error);
|
|
|
|
static inline gboolean
|
|
nm_utils_error_is_cancelled (GError *error)
|
|
{
|
|
return error
|
|
&& error->code == G_IO_ERROR_CANCELLED
|
|
&& error->domain == G_IO_ERROR;
|
|
}
|
|
|
|
gboolean nm_utils_error_is_notfound (GError *error);
|
|
|
|
static inline void
|
|
nm_utils_error_set_literal (GError **error, int error_code, const char *literal)
|
|
{
|
|
g_set_error_literal (error, NM_UTILS_ERROR, error_code, literal);
|
|
}
|
|
|
|
#define nm_utils_error_set(error, error_code, ...) \
|
|
G_STMT_START { \
|
|
if (NM_NARG (__VA_ARGS__) == 1) { \
|
|
g_set_error_literal ((error), NM_UTILS_ERROR, (error_code), _NM_UTILS_MACRO_FIRST (__VA_ARGS__)); \
|
|
} else { \
|
|
g_set_error ((error), NM_UTILS_ERROR, (error_code), __VA_ARGS__); \
|
|
} \
|
|
} G_STMT_END
|
|
|
|
#define nm_utils_error_set_errno(error, errsv, fmt, ...) \
|
|
G_STMT_START { \
|
|
char _bstrerr[NM_STRERROR_BUFSIZE]; \
|
|
\
|
|
g_set_error ((error), \
|
|
NM_UTILS_ERROR, \
|
|
NM_UTILS_ERROR_UNKNOWN, \
|
|
fmt, \
|
|
##__VA_ARGS__, \
|
|
nm_strerror_native_r (({ \
|
|
const int _errsv = (errsv); \
|
|
\
|
|
( _errsv >= 0 \
|
|
? _errsv \
|
|
: ( G_UNLIKELY (_errsv == G_MININT) \
|
|
? G_MAXINT \
|
|
: -errsv)); \
|
|
}), \
|
|
_bstrerr, \
|
|
sizeof (_bstrerr))); \
|
|
} G_STMT_END
|
|
|
|
#define nm_utils_error_new(error_code, ...) \
|
|
( (NM_NARG (__VA_ARGS__) == 1) \
|
|
? g_error_new_literal (NM_UTILS_ERROR, (error_code), _NM_UTILS_MACRO_FIRST (__VA_ARGS__)) \
|
|
: g_error_new (NM_UTILS_ERROR, (error_code), __VA_ARGS__))
|
|
|
|
/*****************************************************************************/
|
|
|
|
gboolean nm_g_object_set_property (GObject *object,
|
|
const char *property_name,
|
|
const GValue *value,
|
|
GError **error);
|
|
|
|
gboolean nm_g_object_set_property_string (GObject *object,
|
|
const char *property_name,
|
|
const char *value,
|
|
GError **error);
|
|
|
|
gboolean nm_g_object_set_property_string_static (GObject *object,
|
|
const char *property_name,
|
|
const char *value,
|
|
GError **error);
|
|
|
|
gboolean nm_g_object_set_property_string_take (GObject *object,
|
|
const char *property_name,
|
|
char *value,
|
|
GError **error);
|
|
|
|
gboolean nm_g_object_set_property_boolean (GObject *object,
|
|
const char *property_name,
|
|
gboolean value,
|
|
GError **error);
|
|
|
|
gboolean nm_g_object_set_property_char (GObject *object,
|
|
const char *property_name,
|
|
gint8 value,
|
|
GError **error);
|
|
|
|
gboolean nm_g_object_set_property_uchar (GObject *object,
|
|
const char *property_name,
|
|
guint8 value,
|
|
GError **error);
|
|
|
|
gboolean nm_g_object_set_property_int (GObject *object,
|
|
const char *property_name,
|
|
int value,
|
|
GError **error);
|
|
|
|
gboolean nm_g_object_set_property_int64 (GObject *object,
|
|
const char *property_name,
|
|
gint64 value,
|
|
GError **error);
|
|
|
|
gboolean nm_g_object_set_property_uint (GObject *object,
|
|
const char *property_name,
|
|
guint value,
|
|
GError **error);
|
|
|
|
gboolean nm_g_object_set_property_uint64 (GObject *object,
|
|
const char *property_name,
|
|
guint64 value,
|
|
GError **error);
|
|
|
|
gboolean nm_g_object_set_property_flags (GObject *object,
|
|
const char *property_name,
|
|
GType gtype,
|
|
guint value,
|
|
GError **error);
|
|
|
|
gboolean nm_g_object_set_property_enum (GObject *object,
|
|
const char *property_name,
|
|
GType gtype,
|
|
int value,
|
|
GError **error);
|
|
|
|
GParamSpec *nm_g_object_class_find_property_from_gtype (GType gtype,
|
|
const char *property_name);
|
|
|
|
/*****************************************************************************/
|
|
|
|
#define _NM_G_PARAM_SPEC_CAST(param_spec, _value_type, _c_type) \
|
|
({ \
|
|
const GParamSpec *const _param_spec = (param_spec); \
|
|
\
|
|
nm_assert ( !_param_spec \
|
|
|| _param_spec->value_type == (_value_type)); \
|
|
((const _c_type *) _param_spec); \
|
|
})
|
|
|
|
#define NM_G_PARAM_SPEC_CAST_BOOLEAN(param_spec) _NM_G_PARAM_SPEC_CAST (param_spec, G_TYPE_BOOLEAN, GParamSpecBoolean)
|
|
#define NM_G_PARAM_SPEC_CAST_UINT(param_spec) _NM_G_PARAM_SPEC_CAST (param_spec, G_TYPE_UINT, GParamSpecUInt)
|
|
#define NM_G_PARAM_SPEC_CAST_UINT64(param_spec) _NM_G_PARAM_SPEC_CAST (param_spec, G_TYPE_UINT64, GParamSpecUInt64)
|
|
|
|
#define NM_G_PARAM_SPEC_GET_DEFAULT_BOOLEAN(param_spec) (NM_G_PARAM_SPEC_CAST_BOOLEAN (NM_ENSURE_NOT_NULL (param_spec))->default_value)
|
|
#define NM_G_PARAM_SPEC_GET_DEFAULT_UINT(param_spec) (NM_G_PARAM_SPEC_CAST_UINT (NM_ENSURE_NOT_NULL (param_spec))->default_value)
|
|
#define NM_G_PARAM_SPEC_GET_DEFAULT_UINT64(param_spec) (NM_G_PARAM_SPEC_CAST_UINT64 (NM_ENSURE_NOT_NULL (param_spec))->default_value)
|
|
|
|
/*****************************************************************************/
|
|
|
|
GType nm_g_type_find_implementing_class_for_property (GType gtype,
|
|
const char *pname);
|
|
|
|
/*****************************************************************************/
|
|
|
|
typedef enum {
|
|
NM_UTILS_STR_UTF8_SAFE_FLAG_NONE = 0,
|
|
|
|
/* This flag only has an effect during escaping. */
|
|
NM_UTILS_STR_UTF8_SAFE_FLAG_ESCAPE_CTRL = 0x0001,
|
|
|
|
/* This flag only has an effect during escaping. */
|
|
NM_UTILS_STR_UTF8_SAFE_FLAG_ESCAPE_NON_ASCII = 0x0002,
|
|
|
|
/* 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,
|
|
|
|
/* This flag only has an effect during unescaping. It means
|
|
* that non-escaped whitespaces (g_ascii_isspace()) will be
|
|
* stripped from the front and end of the string. Note that
|
|
* 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,
|
|
} NMUtilsStrUtf8SafeFlags;
|
|
|
|
const char *nm_utils_buf_utf8safe_escape (gconstpointer buf, gssize buflen, NMUtilsStrUtf8SafeFlags flags, char **to_free);
|
|
char *nm_utils_buf_utf8safe_escape_cp (gconstpointer buf, gssize buflen, NMUtilsStrUtf8SafeFlags flags);
|
|
const char *nm_utils_buf_utf8safe_escape_bytes (GBytes *bytes, NMUtilsStrUtf8SafeFlags flags, char **to_free);
|
|
gconstpointer nm_utils_buf_utf8safe_unescape (const char *str, NMUtilsStrUtf8SafeFlags flags, gsize *out_len, gpointer *to_free);
|
|
|
|
const char *nm_utils_str_utf8safe_escape (const char *str, NMUtilsStrUtf8SafeFlags flags, char **to_free);
|
|
const char *nm_utils_str_utf8safe_unescape (const char *str, NMUtilsStrUtf8SafeFlags flags, char **to_free);
|
|
|
|
char *nm_utils_str_utf8safe_escape_cp (const char *str, NMUtilsStrUtf8SafeFlags flags);
|
|
char *nm_utils_str_utf8safe_unescape_cp (const char *str, NMUtilsStrUtf8SafeFlags flags);
|
|
|
|
char *nm_utils_str_utf8safe_escape_take (char *str, NMUtilsStrUtf8SafeFlags flags);
|
|
|
|
static inline void
|
|
nm_g_variant_unref_floating (GVariant *var)
|
|
{
|
|
/* often a function wants to keep a reference to an input variant.
|
|
* It uses g_variant_ref_sink() to either increase the ref-count,
|
|
* or take ownership of a possibly floating reference.
|
|
*
|
|
* If the function doesn't actually want to do anything with the
|
|
* input variant, it still must make sure that a passed in floating
|
|
* reference is consumed. Hence, this helper which:
|
|
*
|
|
* - does nothing if @var is not floating
|
|
* - unrefs (consumes) @var if it is floating. */
|
|
if (g_variant_is_floating (var))
|
|
g_variant_unref (var);
|
|
}
|
|
|
|
#define nm_g_variant_lookup(dictionary, ...) \
|
|
({ \
|
|
GVariant *const _dictionary = (dictionary); \
|
|
\
|
|
( _dictionary \
|
|
&& g_variant_lookup (_dictionary, __VA_ARGS__)); \
|
|
})
|
|
|
|
static inline GVariant *
|
|
nm_g_variant_lookup_value (GVariant *dictionary,
|
|
const char *key,
|
|
const GVariantType *expected_type)
|
|
{
|
|
return dictionary
|
|
? g_variant_lookup_value (dictionary, key, expected_type)
|
|
: NULL;
|
|
}
|
|
|
|
static inline gboolean
|
|
nm_g_variant_is_of_type (GVariant *value,
|
|
const GVariantType *type)
|
|
{
|
|
return value
|
|
&& g_variant_is_of_type (value, type);
|
|
}
|
|
|
|
static inline void
|
|
nm_g_variant_builder_add_sv (GVariantBuilder *builder, const char *key, GVariant *val)
|
|
{
|
|
g_variant_builder_add (builder, "{sv}", key, val);
|
|
}
|
|
|
|
static inline void
|
|
nm_g_variant_builder_add_sv_bytearray (GVariantBuilder *builder, const char *key, const guint8 *arr, gsize len)
|
|
{
|
|
g_variant_builder_add (builder, "{sv}", key, g_variant_new_fixed_array (G_VARIANT_TYPE_BYTE, arr, len, 1));
|
|
}
|
|
|
|
static inline void
|
|
nm_g_variant_builder_add_sv_uint32 (GVariantBuilder *builder, const char *key, guint32 val)
|
|
{
|
|
nm_g_variant_builder_add_sv (builder, key, g_variant_new_uint32 (val));
|
|
}
|
|
|
|
static inline void
|
|
nm_g_variant_builder_add_sv_str (GVariantBuilder *builder, const char *key, const char *str)
|
|
{
|
|
nm_g_variant_builder_add_sv (builder, key, g_variant_new_string (str));
|
|
}
|
|
|
|
static inline void
|
|
nm_g_source_destroy_and_unref (GSource *source)
|
|
{
|
|
g_source_destroy (source);
|
|
g_source_unref (source);
|
|
}
|
|
|
|
#define nm_clear_g_source_inst(ptr) (nm_clear_pointer ((ptr), nm_g_source_destroy_and_unref))
|
|
|
|
NM_AUTO_DEFINE_FCN0 (GSource *, _nm_auto_destroy_and_unref_gsource, nm_g_source_destroy_and_unref);
|
|
#define nm_auto_destroy_and_unref_gsource nm_auto(_nm_auto_destroy_and_unref_gsource)
|
|
|
|
NM_AUTO_DEFINE_FCN0 (GMainContext *, _nm_auto_pop_gmaincontext, g_main_context_pop_thread_default)
|
|
#define nm_auto_pop_gmaincontext nm_auto (_nm_auto_pop_gmaincontext)
|
|
|
|
static inline gboolean
|
|
nm_source_func_unref_gobject (gpointer user_data)
|
|
{
|
|
nm_assert (G_IS_OBJECT (user_data));
|
|
g_object_unref (user_data);
|
|
return G_SOURCE_REMOVE;
|
|
}
|
|
|
|
GSource *nm_g_idle_source_new (int priority,
|
|
GSourceFunc func,
|
|
gpointer user_data,
|
|
GDestroyNotify destroy_notify);
|
|
|
|
GSource *nm_g_timeout_source_new (guint timeout_msec,
|
|
int priority,
|
|
GSourceFunc func,
|
|
gpointer user_data,
|
|
GDestroyNotify destroy_notify);
|
|
GSource *nm_g_unix_fd_source_new (int fd,
|
|
GIOCondition io_condition,
|
|
int priority,
|
|
gboolean (*source_func) (int fd,
|
|
GIOCondition condition,
|
|
gpointer user_data),
|
|
gpointer user_data,
|
|
GDestroyNotify destroy_notify);
|
|
GSource *nm_g_unix_signal_source_new (int signum,
|
|
int priority,
|
|
GSourceFunc handler,
|
|
gpointer user_data,
|
|
GDestroyNotify notify);
|
|
|
|
static inline GSource *
|
|
nm_g_source_attach (GSource *source,
|
|
GMainContext *context)
|
|
{
|
|
g_source_attach (source, context);
|
|
return source;
|
|
}
|
|
|
|
NM_AUTO_DEFINE_FCN0 (GMainContext *, _nm_auto_unref_gmaincontext, g_main_context_unref)
|
|
#define nm_auto_unref_gmaincontext nm_auto (_nm_auto_unref_gmaincontext)
|
|
|
|
static inline GMainContext *
|
|
nm_g_main_context_push_thread_default (GMainContext *context)
|
|
{
|
|
/* This function is to work together with nm_auto_pop_gmaincontext. */
|
|
if (G_UNLIKELY (!context))
|
|
context = g_main_context_default ();
|
|
g_main_context_push_thread_default (context);
|
|
return context;
|
|
}
|
|
|
|
static inline gboolean
|
|
nm_g_main_context_is_thread_default (GMainContext *context)
|
|
{
|
|
GMainContext *cur_context;
|
|
|
|
cur_context = g_main_context_get_thread_default ();
|
|
if (cur_context == context)
|
|
return TRUE;
|
|
|
|
if (G_UNLIKELY (!cur_context))
|
|
cur_context = g_main_context_default ();
|
|
else if (G_UNLIKELY (!context))
|
|
context = g_main_context_default ();
|
|
else
|
|
return FALSE;
|
|
|
|
return (cur_context == context);
|
|
}
|
|
|
|
static inline GMainContext *
|
|
nm_g_main_context_push_thread_default_if_necessary (GMainContext *context)
|
|
{
|
|
GMainContext *cur_context;
|
|
|
|
cur_context = g_main_context_get_thread_default ();
|
|
if (cur_context == context)
|
|
return NULL;
|
|
|
|
if (G_UNLIKELY (!cur_context)) {
|
|
cur_context = g_main_context_default ();
|
|
if (cur_context == context)
|
|
return NULL;
|
|
} else if (G_UNLIKELY (!context)) {
|
|
context = g_main_context_default ();
|
|
if (cur_context == context)
|
|
return NULL;
|
|
}
|
|
|
|
g_main_context_push_thread_default (context);
|
|
return context;
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
|
|
static inline int
|
|
nm_utf8_collate0 (const char *a, const char *b)
|
|
{
|
|
if (!a)
|
|
return !b ? 0 : -1;
|
|
if (!b)
|
|
return 1;
|
|
return g_utf8_collate (a, b);
|
|
}
|
|
|
|
int nm_strcmp_with_data (gconstpointer a, gconstpointer b, gpointer user_data);
|
|
int nm_strcmp_p_with_data (gconstpointer a, gconstpointer b, gpointer user_data);
|
|
int nm_strcmp0_p_with_data (gconstpointer a, gconstpointer b, gpointer user_data);
|
|
int nm_cmp_uint32_p_with_data (gconstpointer p_a, gconstpointer p_b, gpointer user_data);
|
|
int nm_cmp_int2ptr_p_with_data (gconstpointer p_a, gconstpointer p_b, gpointer user_data);
|
|
|
|
/*****************************************************************************/
|
|
|
|
typedef struct {
|
|
const char *name;
|
|
} NMUtilsNamedEntry;
|
|
|
|
typedef struct {
|
|
union {
|
|
NMUtilsNamedEntry named_entry;
|
|
const char *name;
|
|
};
|
|
union {
|
|
const char *value_str;
|
|
gpointer value_ptr;
|
|
};
|
|
} NMUtilsNamedValue;
|
|
|
|
#define NM_UTILS_NAMED_VALUE_INIT(n, v) { .name = (n), .value_ptr = (v) }
|
|
|
|
NMUtilsNamedValue *nm_utils_named_values_from_strdict_full (GHashTable *hash,
|
|
guint *out_len,
|
|
GCompareDataFunc compare_func,
|
|
gpointer user_data,
|
|
NMUtilsNamedValue *provided_buffer,
|
|
guint provided_buffer_len,
|
|
NMUtilsNamedValue **out_allocated_buffer);
|
|
|
|
#define nm_utils_named_values_from_strdict(hash, out_len, array, out_allocated_buffer) \
|
|
nm_utils_named_values_from_strdict_full ((hash), (out_len), nm_strcmp_p_with_data, NULL, (array), G_N_ELEMENTS (array), (out_allocated_buffer))
|
|
|
|
gssize nm_utils_named_value_list_find (const NMUtilsNamedValue *arr,
|
|
gsize len,
|
|
const char *name,
|
|
gboolean sorted);
|
|
|
|
gboolean nm_utils_named_value_list_is_sorted (const NMUtilsNamedValue *arr,
|
|
gsize len,
|
|
gboolean accept_duplicates,
|
|
GCompareDataFunc compare_func,
|
|
gpointer user_data);
|
|
|
|
void nm_utils_named_value_list_sort (NMUtilsNamedValue *arr,
|
|
gsize len,
|
|
GCompareDataFunc compare_func,
|
|
gpointer user_data);
|
|
|
|
/*****************************************************************************/
|
|
|
|
gpointer *nm_utils_hash_keys_to_array (GHashTable *hash,
|
|
GCompareDataFunc compare_func,
|
|
gpointer user_data,
|
|
guint *out_len);
|
|
|
|
gpointer *nm_utils_hash_values_to_array (GHashTable *hash,
|
|
GCompareDataFunc compare_func,
|
|
gpointer user_data,
|
|
guint *out_len);
|
|
|
|
static inline const char **
|
|
nm_utils_strdict_get_keys (const GHashTable *hash,
|
|
gboolean sorted,
|
|
guint *out_length)
|
|
{
|
|
return (const char **) nm_utils_hash_keys_to_array ((GHashTable *) hash,
|
|
sorted ? nm_strcmp_p_with_data : NULL,
|
|
NULL,
|
|
out_length);
|
|
}
|
|
|
|
gboolean nm_utils_hashtable_same_keys (const GHashTable *a,
|
|
const GHashTable *b);
|
|
|
|
char **nm_utils_strv_make_deep_copied (const char **strv);
|
|
|
|
char **nm_utils_strv_make_deep_copied_n (const char **strv, gsize len);
|
|
|
|
static inline char **
|
|
nm_utils_strv_make_deep_copied_nonnull (const char **strv)
|
|
{
|
|
return nm_utils_strv_make_deep_copied (strv) ?: g_new0 (char *, 1);
|
|
}
|
|
|
|
char **nm_utils_strv_dup (gpointer strv,
|
|
gssize len,
|
|
gboolean deep_copied);
|
|
|
|
/*****************************************************************************/
|
|
|
|
GSList *nm_utils_g_slist_find_str (const GSList *list,
|
|
const char *needle);
|
|
|
|
int nm_utils_g_slist_strlist_cmp (const GSList *a, const GSList *b);
|
|
|
|
char *nm_utils_g_slist_strlist_join (const GSList *a, const char *separator);
|
|
|
|
/*****************************************************************************/
|
|
|
|
static inline guint
|
|
nm_g_array_len (const GArray *arr)
|
|
{
|
|
return arr ? arr->len : 0u;
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
|
|
static inline guint
|
|
nm_g_ptr_array_len (const GPtrArray *arr)
|
|
{
|
|
return arr ? arr->len : 0u;
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
|
|
static inline guint
|
|
nm_g_hash_table_size (GHashTable *hash)
|
|
{
|
|
return hash ? g_hash_table_size (hash) : 0u;
|
|
}
|
|
|
|
static inline gpointer
|
|
nm_g_hash_table_lookup (GHashTable *hash, gconstpointer key)
|
|
{
|
|
return hash ? g_hash_table_lookup (hash, key) : NULL;
|
|
}
|
|
|
|
static inline gboolean
|
|
nm_g_hash_table_remove (GHashTable *hash, gconstpointer key)
|
|
{
|
|
return hash ? g_hash_table_remove (hash, key) : FALSE;
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
|
|
gssize nm_utils_ptrarray_find_binary_search (gconstpointer *list,
|
|
gsize len,
|
|
gconstpointer needle,
|
|
GCompareDataFunc cmpfcn,
|
|
gpointer user_data,
|
|
gssize *out_idx_first,
|
|
gssize *out_idx_last);
|
|
|
|
gssize nm_utils_array_find_binary_search (gconstpointer list,
|
|
gsize elem_size,
|
|
gsize len,
|
|
gconstpointer needle,
|
|
GCompareDataFunc cmpfcn,
|
|
gpointer user_data);
|
|
|
|
/*****************************************************************************/
|
|
|
|
typedef gboolean (*NMUtilsHashTableEqualFunc) (gconstpointer a,
|
|
gconstpointer b);
|
|
|
|
gboolean nm_utils_hash_table_equal (const GHashTable *a,
|
|
const GHashTable *b,
|
|
gboolean treat_null_as_empty,
|
|
NMUtilsHashTableEqualFunc equal_func);
|
|
|
|
/*****************************************************************************/
|
|
|
|
void _nm_utils_strv_sort (const char **strv, gssize len);
|
|
#define nm_utils_strv_sort(strv, len) _nm_utils_strv_sort (NM_CAST_STRV_MC (strv), len)
|
|
|
|
int _nm_utils_strv_cmp_n (const char *const*strv1,
|
|
gssize len1,
|
|
const char *const*strv2,
|
|
gssize len2);
|
|
|
|
static inline gboolean
|
|
_nm_utils_strv_equal (char **strv1, char **strv2)
|
|
{
|
|
return _nm_utils_strv_cmp_n ((const char *const*) strv1, -1,
|
|
(const char *const*) strv2, -1) == 0;
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
|
|
#define NM_UTILS_NSEC_PER_SEC ((gint64) 1000000000)
|
|
#define NM_UTILS_USEC_PER_SEC ((gint64) 1000000)
|
|
#define NM_UTILS_MSEC_PER_SEC ((gint64) 1000)
|
|
#define NM_UTILS_NSEC_PER_MSEC ((gint64) 1000000)
|
|
|
|
static inline gint64
|
|
NM_UTILS_NSEC_TO_MSEC_CEIL (gint64 nsec)
|
|
{
|
|
return (nsec + (NM_UTILS_NSEC_PER_MSEC - 1)) / NM_UTILS_NSEC_PER_MSEC;
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
|
|
int nm_utils_fd_wait_for_event (int fd, int event, gint64 timeout_nsec);
|
|
ssize_t nm_utils_fd_read_loop (int fd, void *buf, size_t nbytes, bool do_poll);
|
|
int nm_utils_fd_read_loop_exact (int fd, void *buf, size_t nbytes, bool do_poll);
|
|
|
|
/*****************************************************************************/
|
|
|
|
#define NM_DEFINE_GDBUS_ARG_INFO_FULL(name_, ...) \
|
|
((GDBusArgInfo *) (&((const GDBusArgInfo) { \
|
|
.ref_count = -1, \
|
|
.name = name_, \
|
|
__VA_ARGS__ \
|
|
})))
|
|
|
|
#define NM_DEFINE_GDBUS_ARG_INFO(name_, a_signature) \
|
|
NM_DEFINE_GDBUS_ARG_INFO_FULL ( \
|
|
name_, \
|
|
.signature = a_signature, \
|
|
)
|
|
|
|
#define NM_DEFINE_GDBUS_ARG_INFOS(...) \
|
|
((GDBusArgInfo **) ((const GDBusArgInfo *[]) { \
|
|
__VA_ARGS__ \
|
|
NULL, \
|
|
}))
|
|
|
|
#define NM_DEFINE_GDBUS_PROPERTY_INFO(name_, ...) \
|
|
((GDBusPropertyInfo *) (&((const GDBusPropertyInfo) { \
|
|
.ref_count = -1, \
|
|
.name = name_, \
|
|
__VA_ARGS__ \
|
|
})))
|
|
|
|
#define NM_DEFINE_GDBUS_PROPERTY_INFO_READABLE(name_, m_signature) \
|
|
NM_DEFINE_GDBUS_PROPERTY_INFO ( \
|
|
name_, \
|
|
.signature = m_signature, \
|
|
.flags = G_DBUS_PROPERTY_INFO_FLAGS_READABLE, \
|
|
)
|
|
|
|
#define NM_DEFINE_GDBUS_PROPERTY_INFOS(...) \
|
|
((GDBusPropertyInfo **) ((const GDBusPropertyInfo *[]) { \
|
|
__VA_ARGS__ \
|
|
NULL, \
|
|
}))
|
|
|
|
#define NM_DEFINE_GDBUS_SIGNAL_INFO_INIT(name_, ...) \
|
|
{ \
|
|
.ref_count = -1, \
|
|
.name = name_, \
|
|
__VA_ARGS__ \
|
|
}
|
|
|
|
#define NM_DEFINE_GDBUS_SIGNAL_INFO(name_, ...) \
|
|
((GDBusSignalInfo *) (&((const GDBusSignalInfo) NM_DEFINE_GDBUS_SIGNAL_INFO_INIT (name_, __VA_ARGS__))))
|
|
|
|
#define NM_DEFINE_GDBUS_SIGNAL_INFOS(...) \
|
|
((GDBusSignalInfo **) ((const GDBusSignalInfo *[]) { \
|
|
__VA_ARGS__ \
|
|
NULL, \
|
|
}))
|
|
|
|
#define NM_DEFINE_GDBUS_METHOD_INFO_INIT(name_, ...) \
|
|
{ \
|
|
.ref_count = -1, \
|
|
.name = name_, \
|
|
__VA_ARGS__ \
|
|
}
|
|
|
|
#define NM_DEFINE_GDBUS_METHOD_INFO(name_, ...) \
|
|
((GDBusMethodInfo *) (&((const GDBusMethodInfo) NM_DEFINE_GDBUS_METHOD_INFO_INIT (name_, __VA_ARGS__))))
|
|
|
|
#define NM_DEFINE_GDBUS_METHOD_INFOS(...) \
|
|
((GDBusMethodInfo **) ((const GDBusMethodInfo *[]) { \
|
|
__VA_ARGS__ \
|
|
NULL, \
|
|
}))
|
|
|
|
#define NM_DEFINE_GDBUS_INTERFACE_INFO_INIT(name_, ...) \
|
|
{ \
|
|
.ref_count = -1, \
|
|
.name = name_, \
|
|
__VA_ARGS__ \
|
|
}
|
|
|
|
#define NM_DEFINE_GDBUS_INTERFACE_INFO(name_, ...) \
|
|
((GDBusInterfaceInfo *) (&((const GDBusInterfaceInfo) NM_DEFINE_GDBUS_INTERFACE_INFO_INIT (name_, __VA_ARGS__))))
|
|
|
|
#define NM_DEFINE_GDBUS_INTERFACE_VTABLE(...) \
|
|
((GDBusInterfaceVTable *) (&((const GDBusInterfaceVTable) { \
|
|
__VA_ARGS__ \
|
|
})))
|
|
|
|
/*****************************************************************************/
|
|
|
|
guint64 nm_utils_get_start_time_for_pid (pid_t pid, char *out_state, pid_t *out_ppid);
|
|
|
|
static inline gboolean
|
|
nm_utils_process_state_is_dead (char pstate)
|
|
{
|
|
/* "/proc/[pid]/stat" returns a state as the 3rd fields (see `man 5 proc`).
|
|
* Some of these states indicate the the process is effectively dead (or a zombie).
|
|
*/
|
|
return NM_IN_SET (pstate, 'Z', 'x', 'X');
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
|
|
gpointer _nm_utils_user_data_pack (int nargs, gconstpointer *args);
|
|
|
|
#define nm_utils_user_data_pack(...) \
|
|
_nm_utils_user_data_pack(NM_NARG (__VA_ARGS__), (gconstpointer[]) { __VA_ARGS__ })
|
|
|
|
void _nm_utils_user_data_unpack (gpointer user_data, int nargs, ...);
|
|
|
|
#define nm_utils_user_data_unpack(user_data, ...) \
|
|
_nm_utils_user_data_unpack(user_data, NM_NARG (__VA_ARGS__), __VA_ARGS__)
|
|
|
|
/*****************************************************************************/
|
|
|
|
typedef void (*NMUtilsInvokeOnIdleCallback) (gpointer user_data,
|
|
GCancellable *cancellable);
|
|
|
|
void nm_utils_invoke_on_idle (GCancellable *cancellable,
|
|
NMUtilsInvokeOnIdleCallback callback,
|
|
gpointer callback_user_data);
|
|
|
|
void nm_utils_invoke_on_timeout (guint timeout_msec,
|
|
GCancellable *cancellable,
|
|
NMUtilsInvokeOnIdleCallback callback,
|
|
gpointer callback_user_data);
|
|
|
|
/*****************************************************************************/
|
|
|
|
GSource *nm_utils_g_main_context_create_integrate_source (GMainContext *internal);
|
|
|
|
/*****************************************************************************/
|
|
|
|
static inline void
|
|
nm_strv_ptrarray_add_string_take (GPtrArray *cmd,
|
|
char *str)
|
|
{
|
|
nm_assert (cmd);
|
|
nm_assert (str);
|
|
|
|
g_ptr_array_add (cmd, str);
|
|
}
|
|
|
|
static inline void
|
|
nm_strv_ptrarray_add_string_dup (GPtrArray *cmd,
|
|
const char *str)
|
|
{
|
|
nm_strv_ptrarray_add_string_take (cmd,
|
|
g_strdup (str));
|
|
}
|
|
|
|
#define nm_strv_ptrarray_add_string_concat(cmd, ...) \
|
|
nm_strv_ptrarray_add_string_take ((cmd), g_strconcat (__VA_ARGS__, NULL))
|
|
|
|
#define nm_strv_ptrarray_add_string_printf(cmd, ...) \
|
|
nm_strv_ptrarray_add_string_take ((cmd), g_strdup_printf (__VA_ARGS__))
|
|
|
|
#define nm_strv_ptrarray_add_int(cmd, val) \
|
|
nm_strv_ptrarray_add_string_take ((cmd), nm_strdup_int (val))
|
|
|
|
static inline void
|
|
nm_strv_ptrarray_take_gstring (GPtrArray *cmd,
|
|
GString **gstr)
|
|
{
|
|
nm_assert (gstr && *gstr);
|
|
|
|
nm_strv_ptrarray_add_string_take (cmd,
|
|
g_string_free (g_steal_pointer (gstr),
|
|
FALSE));
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
|
|
int nm_utils_getpagesize (void);
|
|
|
|
/*****************************************************************************/
|
|
|
|
char *nm_utils_bin2hexstr_full (gconstpointer addr,
|
|
gsize length,
|
|
char delimiter,
|
|
gboolean upper_case,
|
|
char *out);
|
|
|
|
guint8 *nm_utils_hexstr2bin_full (const char *hexstr,
|
|
gboolean allow_0x_prefix,
|
|
gboolean delimiter_required,
|
|
const char *delimiter_candidates,
|
|
gsize required_len,
|
|
guint8 *buffer,
|
|
gsize buffer_len,
|
|
gsize *out_len);
|
|
|
|
#define nm_utils_hexstr2bin_buf(hexstr, allow_0x_prefix, delimiter_required, delimiter_candidates, buffer) \
|
|
nm_utils_hexstr2bin_full ((hexstr), (allow_0x_prefix), (delimiter_required), (delimiter_candidates), G_N_ELEMENTS (buffer), (buffer), G_N_ELEMENTS (buffer), NULL)
|
|
|
|
guint8 *nm_utils_hexstr2bin_alloc (const char *hexstr,
|
|
gboolean allow_0x_prefix,
|
|
gboolean delimiter_required,
|
|
const char *delimiter_candidates,
|
|
gsize required_len,
|
|
gsize *out_len);
|
|
|
|
/*****************************************************************************/
|
|
|
|
#define _NM_UTILS_STRING_TABLE_LOOKUP_DEFINE(fcn_name, \
|
|
value_type, \
|
|
value_type_result, \
|
|
entry_cmd, \
|
|
unknown_val_cmd, \
|
|
get_operator, \
|
|
...) \
|
|
value_type_result \
|
|
fcn_name (const char *name) \
|
|
{ \
|
|
static const struct { \
|
|
const char *name; \
|
|
value_type value; \
|
|
} LIST[] = { \
|
|
__VA_ARGS__ \
|
|
}; \
|
|
\
|
|
if (NM_MORE_ASSERT_ONCE (5)) { \
|
|
int i; \
|
|
\
|
|
for (i = 0; i < G_N_ELEMENTS (LIST); i++) { \
|
|
nm_assert (LIST[i].name); \
|
|
if (i > 0) \
|
|
nm_assert (strcmp (LIST[i - 1].name, LIST[i].name) < 0); \
|
|
} \
|
|
} \
|
|
\
|
|
{ entry_cmd; } \
|
|
\
|
|
if (G_LIKELY (name)) { \
|
|
G_STATIC_ASSERT (G_N_ELEMENTS (LIST) > 1); \
|
|
G_STATIC_ASSERT (G_N_ELEMENTS (LIST) < G_MAXINT / 2 - 10); \
|
|
int imin = 0; \
|
|
int imax = (G_N_ELEMENTS (LIST) - 1); \
|
|
int imid = (G_N_ELEMENTS (LIST) - 1) / 2; \
|
|
\
|
|
for (;;) { \
|
|
const int cmp = strcmp (LIST[imid].name, name); \
|
|
\
|
|
if (G_UNLIKELY (cmp == 0)) \
|
|
return get_operator (LIST[imid].value); \
|
|
\
|
|
if (cmp < 0) \
|
|
imin = imid + 1; \
|
|
else \
|
|
imax = imid - 1; \
|
|
\
|
|
if (G_UNLIKELY (imin > imax)) \
|
|
break; \
|
|
\
|
|
/* integer overflow cannot happen, because LIST is shorter than G_MAXINT/2. */ \
|
|
imid = (imin + imax) / 2;\
|
|
} \
|
|
} \
|
|
\
|
|
{ unknown_val_cmd; } \
|
|
}
|
|
|
|
#define NM_UTILS_STRING_TABLE_LOOKUP_STRUCT_DEFINE(fcn_name, \
|
|
result_type, \
|
|
entry_cmd, \
|
|
unknown_val_cmd, \
|
|
...) \
|
|
_NM_UTILS_STRING_TABLE_LOOKUP_DEFINE (fcn_name, \
|
|
result_type, \
|
|
const result_type *, \
|
|
entry_cmd, \
|
|
unknown_val_cmd, \
|
|
&, \
|
|
__VA_ARGS__)
|
|
|
|
#define NM_UTILS_STRING_TABLE_LOOKUP_DEFINE(fcn_name, \
|
|
result_type, \
|
|
entry_cmd, \
|
|
unknown_val_cmd, \
|
|
...) \
|
|
_NM_UTILS_STRING_TABLE_LOOKUP_DEFINE (fcn_name, \
|
|
result_type, \
|
|
result_type, \
|
|
entry_cmd, \
|
|
unknown_val_cmd, \
|
|
, \
|
|
__VA_ARGS__)
|
|
|
|
/*****************************************************************************/
|
|
|
|
static inline GTask *
|
|
nm_g_task_new (gpointer source_object,
|
|
GCancellable *cancellable,
|
|
gpointer source_tag,
|
|
GAsyncReadyCallback callback,
|
|
gpointer callback_data)
|
|
{
|
|
GTask *task;
|
|
|
|
task = g_task_new (source_object, cancellable, callback, callback_data);
|
|
if (source_tag)
|
|
g_task_set_source_tag (task, source_tag);
|
|
return task;
|
|
}
|
|
|
|
static inline gboolean
|
|
nm_g_task_is_valid (gpointer task,
|
|
gpointer source_object,
|
|
gpointer source_tag)
|
|
{
|
|
return g_task_is_valid (task, source_object)
|
|
&& g_task_get_source_tag (task) == source_tag;
|
|
}
|
|
|
|
guint nm_utils_parse_debug_string (const char *string,
|
|
const GDebugKey *keys,
|
|
guint nkeys);
|
|
|
|
/*****************************************************************************/
|
|
|
|
static inline gboolean
|
|
nm_utils_strdup_reset (char **dst, const char *src)
|
|
{
|
|
nm_assert (dst);
|
|
|
|
if (nm_streq0 (*dst, src))
|
|
return FALSE;
|
|
g_free (*dst);
|
|
*dst = g_strdup (src);
|
|
return TRUE;
|
|
}
|
|
|
|
void nm_indirect_g_free (gpointer arg);
|
|
|
|
/*****************************************************************************/
|
|
|
|
/* nm_utils_get_next_realloc_size() is used to grow buffers exponentially, when
|
|
* the final size is unknown. As such, it has borders for which it allocates
|
|
* certain buffer sizes.
|
|
*
|
|
* The use of these defines is to get favorable allocation sequences.
|
|
* For example, nm_str_buf_init() asks for an initial allocation size. Note that
|
|
* it reserves the exactly requested amount, under the assumption that the
|
|
* user may know how many bytes will be required. However, often the caller
|
|
* doesn't know in advance, and NMStrBuf grows exponentially by calling
|
|
* nm_utils_get_next_realloc_size().
|
|
* Imagine you call nm_str_buf_init() with an initial buffer size 100, and you
|
|
* add one character at a time. Then the first reallocation will increase the
|
|
* buffer size only from 100 to 104.
|
|
* If you however start with an initial buffer size of 104, then the next reallocation
|
|
* via nm_utils_get_next_realloc_size() gives you 232, and so on. By using
|
|
* these sizes, it results in one less allocation, if you anyway don't know the
|
|
* exact size in advance. */
|
|
#define NM_UTILS_GET_NEXT_REALLOC_SIZE_104 ((gsize) 104)
|
|
#define NM_UTILS_GET_NEXT_REALLOC_SIZE_1000 ((gsize) 1000)
|
|
|
|
gsize nm_utils_get_next_realloc_size (gboolean true_realloc, gsize requested);
|
|
|
|
/*****************************************************************************/
|
|
|
|
typedef enum {
|
|
NMU_IFACE_ANY,
|
|
NMU_IFACE_KERNEL,
|
|
NMU_IFACE_OVS,
|
|
NMU_IFACE_OVS_AND_KERNEL,
|
|
} NMUtilsIfaceType;
|
|
|
|
gboolean nm_utils_ifname_valid_kernel (const char *name, GError **error);
|
|
|
|
gboolean nm_utils_ifname_valid (const char* name,
|
|
NMUtilsIfaceType type,
|
|
GError **error);
|
|
|
|
/*****************************************************************************/
|
|
|
|
static inline GArray *
|
|
nm_strvarray_ensure (GArray **p)
|
|
{
|
|
if (!*p) {
|
|
*p = g_array_new (TRUE, FALSE, sizeof (char *));
|
|
g_array_set_clear_func (*p, nm_indirect_g_free);
|
|
}
|
|
return *p;
|
|
}
|
|
|
|
static inline void
|
|
nm_strvarray_add (GArray *array, const char *str)
|
|
{
|
|
char *s;
|
|
|
|
s = g_strdup (str);
|
|
g_array_append_val (array, s);
|
|
}
|
|
|
|
static inline const char *const*
|
|
nm_strvarray_get_strv_non_empty (GArray *arr, guint *length)
|
|
{
|
|
if (!arr || arr->len == 0) {
|
|
NM_SET_OUT (length, 0);
|
|
return NULL;
|
|
}
|
|
|
|
NM_SET_OUT (length, arr->len);
|
|
return &g_array_index (arr, const char *, 0);
|
|
}
|
|
|
|
static inline const char *const*
|
|
nm_strvarray_get_strv (GArray **arr, guint *length)
|
|
{
|
|
if (!*arr) {
|
|
NM_SET_OUT (length, 0);
|
|
return (const char *const*) arr;
|
|
}
|
|
|
|
NM_SET_OUT (length, (*arr)->len);
|
|
return &g_array_index (*arr, const char *, 0);
|
|
}
|
|
|
|
static inline void
|
|
nm_strvarray_set_strv (GArray **array, const char *const*strv)
|
|
{
|
|
gs_unref_array GArray *array_old = NULL;
|
|
|
|
array_old = g_steal_pointer (array);
|
|
|
|
if (!strv || !strv[0])
|
|
return;
|
|
|
|
nm_strvarray_ensure (array);
|
|
for (; strv[0]; strv++)
|
|
nm_strvarray_add (*array, strv[0]);
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
|
|
void _nm_utils_format_variant_attributes_full (GString *str,
|
|
const NMUtilsNamedValue *values,
|
|
guint num_values,
|
|
char attr_separator,
|
|
char key_value_separator);
|
|
|
|
char *_nm_utils_format_variant_attributes (GHashTable *attributes,
|
|
char attr_separator,
|
|
char key_value_separator);
|
|
|
|
#endif /* __NM_SHARED_UTILS_H__ */
|