diff options
| author | Michael Biebl <biebl@debian.org> | 2021-10-01 23:05:04 +0200 |
|---|---|---|
| committer | Michael Biebl <biebl@debian.org> | 2021-10-01 23:05:04 +0200 |
| commit | e74c568b07b50b97873fb4ee1d776dedefbd54d6 (patch) | |
| tree | 3469f17ea9af91f7ff169b890633bda68b0cf76e /shared | |
| parent | bfe522304da217296e2a61040f58e35ec5d6f3f2 (diff) | |
New upstream version 1.32.12 upstream/1.32.12
Diffstat (limited to 'shared')
236 files changed, 0 insertions, 78797 deletions
diff --git a/shared/README.md b/shared/README.md deleted file mode 100644 index ed950fce..00000000 --- a/shared/README.md +++ /dev/null @@ -1,108 +0,0 @@ -The "shared/" Directory -======================= - -For NetworkManager we place helper/utility code under "shared/" -in static libraries. The idea is to avoid code duplication but also -provide high quality helper functions that simplify the higher layers. -In NetworkManager there are complicated parts, for example "src/nm-manager.c" -is huge. On the other hand, this helper code should be simple and easy -to understand, so that we can build more complex code on top of it. - -As we statically link them into our binaries, they are all inherently -internal API, that means they cannot be part of libnm's (libnm-core's) public API. -It also means that their API/ABI is not stable. - -We don't care these libraries to be minimal and contain only symbols that are -used by all users. Instead, we expect the linker to throw away unused symbols. -We achieve this by having a symbol versioning file to hide internal symbols -(which gives the linker a possibility to remove them if they are unused) and -compiling with LTO or `"-Wl,--gc-sections"`. Let the tool solve this and not -manual organization. - -Hence these libraries (and their content) are structured this way to satisfy -the following questions: - -1) which dependencies (libraries) do they have? That determines which - other libraries can use it. For example: - - - "shared/nm-std-aux" and "shared/nm-glib-aux" both provide general - purpose helpers, the difference is that the former has no dependency - on glib2 library. Both these libraries are a basic dependency for - many other parts of the code. - - - "shared/nm-udev-aux" has a dependency on libudev, it thus cannot - be in "shared/nm-glib-aux". - - - client code also has a glib2 dependency. That means it can link with - "shared/nm-std-aux" and "shared/nm-glib-aux", but must not link - with "shared/nm-udev-aux" (as it has no direct udev dependenct -- - although clients get it indirectly because libnm already requires - it). - -2) what is their overall purpose? As said, we rely on the linker to - prune unused symbols. But in a few cases we avoid to merge different - code in the same library. For example: - - - "shared/nm-glib-aux" and "shared/nm-base" both only have a - glib2 dependency. Hence, they could be merged. However we - don't do that because "shared/nm-base" is more about NetworkManager - specific code, while "shared/nm-glib-aux" is about general - purpose helpers. - -3) some of these libraries are forked from an upstream. They are kept - separate so that we can re-import future upstream versions. - -Detail -====== - -- `shared/c-list` -- `shared/c-rbtree` -- `shared/c-siphash` -- `shared/c-stdoux` -- `shared/n-acd` -- `shared/n-dhcp4` - - These are forked from upstream and imported with git-subtree. They - in general only have a libc dependency (or dependencies between each - other). - -- `shared/nm-std-aux` - - This contains helper code with only a libc dependency. - Almost all C code depends on this library. - -- `shared/nm-glib-aux` - - Like "shared/nm-std-aux" but also has a glib2 dependency. - Almost all glib2 code depends on this library. - -- `shared/nm-udev/aux` - - Like "shared/nm-glib-aux" but also has a libudev dependency. It - has code related to libudev. - -- `shared/systemd` - - These are forked from upstream systemd and imported with a script. - Under "shared/systemd/src" we try to keep the sources as close to - the original as possible. There is also some adapter code to make - it useable for us. It has a dependency on "shared/nm-glib-aux" - and will need a logging implementation for "shared/nm-glib-aux/nm-logging-fwd.h". - -- `shared/nm-base` - - Depends on "shared/nm-glib-aux" and glib2 but it provides helper code - that more about NetworkManager specifc things. - -- `shared/nm-log-core` - - This is the logging implementation as used by NetworkManager core ("src/"). - It is also a dependency for "shared/nm-platform". - -- `shared/nm-platform` - - Platform implementation. It depends on "shared/nm-log-core", "shared/nm-base" - and "shared/nm-glib-aux". - -- Other than that, there are still a few unorganized files/directories here. - These should be cleaned up. diff --git a/shared/c-list/src/c-list.h b/shared/c-list/src/c-list.h deleted file mode 100644 index c92aa6f3..00000000 --- a/shared/c-list/src/c-list.h +++ /dev/null @@ -1,443 +0,0 @@ -#pragma once - -/* - * Circular Intrusive Double Linked List Collection in ISO-C11 - * - * This implements a generic circular double linked list. List entries must - * embed the CList object, which provides pointers to the next and previous - * element. Insertion and removal can be done in O(1) due to the double links. - * Furthermore, the list is circular, thus allows access to front/tail in O(1) - * as well, even if you only have a single head pointer (which is not how the - * list is usually operated, though). - * - * Note that you are free to use the list implementation without a head - * pointer. However, usual operation uses a single CList object as head, which - * is itself linked in the list and as such must be identified as list head. - * This allows very simply list operations and avoids a lot of special cases. - * Most importantly, you can unlink entries without requiring a head pointer. - */ - -#ifdef __cplusplus -extern "C" { -#endif - -#include <stddef.h> - -typedef struct CList CList; - -/** - * struct CList - Entry of a circular double linked list - * @next: next entry - * @prev: previous entry - * - * Each entry in a list must embed a CList object. This object contains - * pointers to its next and previous elements, which can be freely accessed by - * the API user at any time. Note that the list is circular, and the list head - * is linked in the list as well. - * - * The list head must be initialized via C_LIST_INIT before use. There is no - * reason to initialize entry objects before linking them. However, if you need - * a boolean state that tells you whether the entry is linked or not, you should - * initialize the entry via C_LIST_INIT as well. - */ -struct CList { - CList *next; - CList *prev; -}; - -#define C_LIST_INIT(_var) { .next = &(_var), .prev = &(_var) } - -/** - * c_list_init() - initialize list entry - * @what: list entry to initialize - */ -static inline void c_list_init(CList *what) { - *what = (CList)C_LIST_INIT(*what); -} - -/** - * c_list_entry() - get parent container of list entry - * @_what: list entry, or NULL - * @_t: type of parent container - * @_m: member name of list entry in @_t - * - * If the list entry @_what is embedded into a surrounding structure, this will - * turn the list entry pointer @_what into a pointer to the parent container - * (using offsetof(3), or sometimes called container_of(3)). - * - * If @_what is NULL, this will also return NULL. - * - * Return: Pointer to parent container, or NULL. - */ -#define c_list_entry(_what, _t, _m) \ - ((_t *)(void *)(((unsigned long)(void *)(_what) ?: \ - offsetof(_t, _m)) - offsetof(_t, _m))) - -/** - * c_list_is_linked() - check whether an entry is linked - * @what: entry to check, or NULL - * - * Return: True if @what is linked in a list, false if not. - */ -static inline _Bool c_list_is_linked(const CList *what) { - return what && what->next != what; -} - -/** - * c_list_is_empty() - check whether a list is empty - * @list: list to check, or NULL - * - * This is the same as !c_list_is_linked(). - * - * Return: True if @list is empty, false if not. - */ -static inline _Bool c_list_is_empty(const CList *list) { - return !c_list_is_linked(list); -} - -/** - * c_list_link_before() - link entry into list - * @where: linked list entry used as anchor - * @what: entry to link - * - * This links @what directly in front of @where. @where can either be a list - * head or any entry in the list. - * - * If @where points to the list head, this effectively links @what as new tail - * element. Hence, the macro c_list_link_tail() is an alias to this. - * - * @what is not inspected prior to being linked. Hence, it better not be linked - * into another list, or the other list will be corrupted. - */ -static inline void c_list_link_before(CList *where, CList *what) { - CList *prev = where->prev, *next = where; - - next->prev = what; - what->next = next; - what->prev = prev; - prev->next = what; -} -#define c_list_link_tail(_list, _what) c_list_link_before((_list), (_what)) - -/** - * c_list_link_after() - link entry into list - * @where: linked list entry used as anchor - * @what: entry to link - * - * This links @what directly after @where. @where can either be a list head or - * any entry in the list. - * - * If @where points to the list head, this effectively links @what as new front - * element. Hence, the macro c_list_link_front() is an alias to this. - * - * @what is not inspected prior to being linked. Hence, it better not be linked - * into another list, or the other list will be corrupted. - */ -static inline void c_list_link_after(CList *where, CList *what) { - CList *prev = where, *next = where->next; - - next->prev = what; - what->next = next; - what->prev = prev; - prev->next = what; -} -#define c_list_link_front(_list, _what) c_list_link_after((_list), (_what)) - -/** - * c_list_unlink_stale() - unlink element from list - * @what: element to unlink - * - * This unlinks @what. If @what was initialized via C_LIST_INIT(), it has no - * effect. If @what was never linked, nor initialized, behavior is undefined. - * - * Note that this does not modify @what. It just modifies the previous and next - * elements in the list to no longer reference @what. If you want to make sure - * @what is re-initialized after removal, use c_list_unlink(). - */ -static inline void c_list_unlink_stale(CList *what) { - CList *prev = what->prev, *next = what->next; - - next->prev = prev; - prev->next = next; -} - -/** - * c_list_unlink() - unlink element from list and re-initialize - * @what: element to unlink - * - * This is like c_list_unlink_stale() but re-initializes @what after removal. - */ -static inline void c_list_unlink(CList *what) { - /* condition is not needed, but avoids STOREs in fast-path */ - if (c_list_is_linked(what)) { - c_list_unlink_stale(what); - *what = (CList)C_LIST_INIT(*what); - } -} - -/** - * c_list_swap() - exchange the contents of two lists - * @list1: the list to operate on - * @list2: the list to operate on - * - * This replaces the contents of the list @list1 with the contents - * of @list2, and vice versa. - */ -static inline void c_list_swap(CList *list1, CList *list2) { - CList t; - - /* make neighbors of list1 point to list2, and vice versa */ - t = *list1; - t.next->prev = list2; - t.prev->next = list2; - t = *list2; - t.next->prev = list1; - t.prev->next = list1; - - /* swap list1 and list2 now that their neighbors were fixed up */ - t = *list1; - *list1 = *list2; - *list2 = t; -} - -/** - * c_list_splice() - splice one list into another - * @target: the list to splice into - * @source: the list to splice - * - * This removes all the entries from @source and splice them into @target. - * The order of the two lists is preserved and the source is appended - * to the end of target. - * - * On return, the source list will be empty. - */ -static inline void c_list_splice(CList *target, CList *source) { - if (!c_list_is_empty(source)) { - /* attach the front of @source to the tail of @target */ - source->next->prev = target->prev; - target->prev->next = source->next; - - /* attach the tail of @source to the front of @target */ - source->prev->next = target; - target->prev = source->prev; - - /* clear source */ - *source = (CList)C_LIST_INIT(*source); - } -} - -/** - * c_list_first() - return pointer to first element, or NULL if empty - * @list: list to operate on, or NULL - * - * This returns a pointer to the first element, or NULL if empty. This never - * returns a pointer to the list head. - * - * Return: Pointer to first list element, or NULL if empty. - */ -static inline CList *c_list_first(CList *list) { - return c_list_is_empty(list) ? NULL : list->next; -} - -/** - * c_list_last() - return pointer to last element, or NULL if empty - * @list: list to operate on, or NULL - * - * This returns a pointer to the last element, or NULL if empty. This never - * returns a pointer to the list head. - * - * Return: Pointer to last list element, or NULL if empty. - */ -static inline CList *c_list_last(CList *list) { - return c_list_is_empty(list) ? NULL : list->prev; -} - -/** - * c_list_first_entry() - return pointer to first entry, or NULL if empty - * @_list: list to operate on, or NULL - * @_t: type of list entries - * @_m: name of CList member in @_t - * - * This is like c_list_first(), but also applies c_list_entry() on the result. - * - * Return: Pointer to first list entry, or NULL if empty. - */ -#define c_list_first_entry(_list, _t, _m) \ - c_list_entry(c_list_first(_list), _t, _m) - -/** - * c_list_last_entry() - return pointer to last entry, or NULL if empty - * @_list: list to operate on, or NULL - * @_t: type of list entries - * @_m: name of CList member in @_t - * - * This is like c_list_last(), but also applies c_list_entry() on the result. - * - * Return: Pointer to last list entry, or NULL if empty. - */ -#define c_list_last_entry(_list, _t, _m) \ - c_list_entry(c_list_last(_list), _t, _m) - -/** - * c_list_for_each*() - iterators - * - * The c_list_for_each*() macros provide simple for-loop wrappers to iterate - * a linked list. They come in a set of flavours: - * - * - "entry": This combines c_list_entry() with the loop iterator, so the - * iterator always has the type of the surrounding object, rather - * than CList. - * - * - "safe": The loop iterator always keeps track of the next element to - * visit. This means, you can safely modify the current element, - * while retaining loop-integrity. - * You still must not touch any other entry of the list. Otherwise, - * the loop-iterator will be corrupted. - * - * - "continue": Rather than starting the iteration at the front of the list, - * use the current value of the iterator as starting position. - * Note that the first loop iteration will be the following - * element, not the given element. - * - * - "unlink": This unlinks the current element from the list before the loop - * code is run. Note that this only does a partial unlink, since - * it assumes the entire list will be unlinked. You must not - * break out of the loop, or the list will be in an inconsistent - * state. - */ - -#define c_list_for_each(_iter, _list) \ - for (_iter = (_list)->next; \ - (_iter) != (_list); \ - _iter = (_iter)->next) - -#define c_list_for_each_entry(_iter, _list, _m) \ - for (_iter = c_list_entry((_list)->next, __typeof__(*_iter), _m); \ - &(_iter)->_m != (_list); \ - _iter = c_list_entry((_iter)->_m.next, __typeof__(*_iter), _m)) - -#define c_list_for_each_safe(_iter, _safe, _list) \ - for (_iter = (_list)->next, _safe = (_iter)->next; \ - (_iter) != (_list); \ - _iter = (_safe), _safe = (_safe)->next) - -#define c_list_for_each_entry_safe(_iter, _safe, _list, _m) \ - for (_iter = c_list_entry((_list)->next, __typeof__(*_iter), _m), \ - _safe = c_list_entry((_iter)->_m.next, __typeof__(*_iter), _m); \ - &(_iter)->_m != (_list); \ - _iter = (_safe), \ - _safe = c_list_entry((_safe)->_m.next, __typeof__(*_iter), _m)) \ - -#define c_list_for_each_continue(_iter, _list) \ - for (_iter = (_iter) ? (_iter)->next : (_list)->next; \ - (_iter) != (_list); \ - _iter = (_iter)->next) - -#define c_list_for_each_entry_continue(_iter, _list, _m) \ - for (_iter = c_list_entry((_iter) ? (_iter)->_m.next : (_list)->next, \ - __typeof__(*_iter), \ - _m); \ - &(_iter)->_m != (_list); \ - _iter = c_list_entry((_iter)->_m.next, __typeof__(*_iter), _m)) - -#define c_list_for_each_safe_continue(_iter, _safe, _list) \ - for (_iter = (_iter) ? (_iter)->next : (_list)->next, \ - _safe = (_iter)->next; \ - (_iter) != (_list); \ - _iter = (_safe), _safe = (_safe)->next) - -#define c_list_for_each_entry_safe_continue(_iter, _safe, _list, _m) \ - for (_iter = c_list_entry((_iter) ? (_iter)->_m.next : (_list)->next, \ - __typeof__(*_iter), \ - _m), \ - _safe = c_list_entry((_iter)->_m.next, __typeof__(*_iter), _m); \ - &(_iter)->_m != (_list); \ - _iter = (_safe), \ - _safe = c_list_entry((_safe)->_m.next, __typeof__(*_iter), _m)) \ - -#define c_list_for_each_safe_unlink(_iter, _safe, _list) \ - for (_iter = (_list)->next, _safe = (_iter)->next; \ - ((*_iter = (CList)C_LIST_INIT(*_iter)), (_iter) != (_list)); \ - _iter = (_safe), _safe = (_safe)->next) - -#define c_list_for_each_entry_safe_unlink(_iter, _safe, _list, _m) \ - for (_iter = c_list_entry((_list)->next, __typeof__(*_iter), _m), \ - _safe = c_list_entry((_iter)->_m.next, __typeof__(*_iter), _m); \ - (((_iter)->_m = (CList)C_LIST_INIT((_iter)->_m)), \ - &(_iter)->_m != (_list)); \ - _iter = (_safe), \ - _safe = c_list_entry((_safe)->_m.next, __typeof__(*_iter), _m)) \ - -/** - * c_list_flush() - flush all entries from a list - * @list: list to flush - * - * This unlinks all entries from the given list @list and reinitializes their - * link-nodes via C_LIST_INIT(). - * - * Note that the entries are not modified in any other way, nor is their memory - * released. This function just unlinks them and resets all the list nodes. It - * is particularly useful with temporary lists on the stack in combination with - * the GCC-extension __attribute__((__cleanup__(arg))). - */ -static inline void c_list_flush(CList *list) { - CList *iter, *safe; - - c_list_for_each_safe_unlink(iter, safe, list) - /* empty */ ; -} - -/** - * c_list_length() - return number of linked entries, excluding the head - * @list: list to operate on - * - * Returns the number of entries in the list, excluding the list head @list. - * That is, for a list that is empty according to c_list_is_empty(), the - * returned length is 0. This requires to iterate the list and has thus O(n) - * runtime. - * - * Note that this function is meant for debugging purposes only. If you need - * the list size during normal operation, you should maintain a counter - * separately. - * - * Return: Number of items in @list. - */ -static inline unsigned long c_list_length(const CList *list) { - unsigned long n = 0; - const CList *iter; - - c_list_for_each(iter, list) - ++n; - - return n; -} - -/** - * c_list_contains() - check whether an entry is linked in a certain list - * @list: list to operate on - * @what: entry to look for - * - * This checks whether @what is linked into @list. This requires a linear - * search through the list, as such runs in O(n). Note that the list-head is - * considered part of the list, and hence this returns true if @what equals - * @list. - * - * Note that this function is meant for debugging purposes, and consistency - * checks. You should always be aware whether your objects are linked in a - * specific list. - * - * Return: True if @what is in @list, false otherwise. - */ -static inline _Bool c_list_contains(const CList *list, const CList *what) { - const CList *iter; - - c_list_for_each(iter, list) - if (what == iter) - return 1; - - return what == list; -} - -#ifdef __cplusplus -} -#endif diff --git a/shared/c-rbtree/src/c-rbtree-private.h b/shared/c-rbtree/src/c-rbtree-private.h deleted file mode 100644 index c9befbf3..00000000 --- a/shared/c-rbtree/src/c-rbtree-private.h +++ /dev/null @@ -1,35 +0,0 @@ -#pragma once - -/* - * Private definitions - * This file contains private definitions for the RB-Tree implementation, but - * which are used by our test-suite. - */ - -#include <c-stdaux.h> -#include <stddef.h> -#include "c-rbtree.h" - -/* - * Nodes - */ - -static inline void *c_rbnode_raw(CRBNode *n) { - return (void *)(n->__parent_and_flags & ~C_RBNODE_FLAG_MASK); -} - -static inline unsigned long c_rbnode_flags(CRBNode *n) { - return n->__parent_and_flags & C_RBNODE_FLAG_MASK; -} - -static inline _Bool c_rbnode_is_red(CRBNode *n) { - return c_rbnode_flags(n) & C_RBNODE_RED; -} - -static inline _Bool c_rbnode_is_black(CRBNode *n) { - return !(c_rbnode_flags(n) & C_RBNODE_RED); -} - -static inline _Bool c_rbnode_is_root(CRBNode *n) { - return c_rbnode_flags(n) & C_RBNODE_ROOT; -} diff --git a/shared/c-rbtree/src/c-rbtree.c b/shared/c-rbtree/src/c-rbtree.c deleted file mode 100644 index 2f0e608f..00000000 --- a/shared/c-rbtree/src/c-rbtree.c +++ /dev/null @@ -1,1120 +0,0 @@ -/* - * RB-Tree Implementation - * This implements the insertion/removal of elements in RB-Trees. You're highly - * recommended to have an RB-Tree documentation at hand when reading this. Both - * insertion and removal can be split into a handful of situations that can - * occur. Those situations are enumerated as "Case 1" to "Case n" here, and - * follow closely the cases described in most RB-Tree documentations. This file - * does not explain why it is enough to handle just those cases, nor does it - * provide a proof of correctness. Dig out your algorithm 101 handbook if - * you're interested. - * - * This implementation is *not* straightforward. Usually, a handful of - * rotation, reparent, swap and link helpers can be used to implement the - * rebalance operations. However, those often perform unnecessary writes. - * Therefore, this implementation hard-codes all the operations. You're highly - * recommended to look at the two basic helpers before reading the code: - * c_rbnode_swap_child() - * c_rbnode_set_parent_and_flags() - * Those are the only helpers used, hence, you should really know what they do - * before digging into the code. - * - * For a highlevel documentation of the API, see the header file and docbook - * comments. - */ - -#include <assert.h> -#include <c-stdaux.h> -#include <stdalign.h> -#include <stddef.h> -#include "c-rbtree.h" -#include "c-rbtree-private.h" - -/* - * We use the lower 2 bits of CRBNode pointers to store flags. Make sure - * CRBNode is 4-byte aligned, so the lower 2 bits are actually unused. We also - * sometimes store a pointer to the root-node, so make sure this one is also 4 - * byte aligned. - * Note that there are actually some architectures where `max_align_t` is 4, so - * we do not have much wiggle-room to extend this flag-set. - */ -static_assert(alignof(CRBNode) <= alignof(max_align_t), "Invalid RBNode alignment"); -static_assert(alignof(CRBNode) >= 4, "Invalid CRBNode alignment"); -static_assert(alignof(CRBTree) <= alignof(max_align_t), "Invalid RBTree alignment"); -static_assert(alignof(CRBTree) >= 4, "Invalid CRBTree alignment"); - -/** - * c_rbnode_leftmost() - return leftmost child - * @n: current node, or NULL - * - * This returns the leftmost child of @n. If @n is NULL, this will return NULL. - * In all other cases, this function returns a valid pointer. That is, if @n - * does not have any left children, this returns @n. - * - * Worst case runtime (n: number of elements in tree): O(log(n)) - * - * Return: Pointer to leftmost child, or NULL. - */ -_c_public_ CRBNode *c_rbnode_leftmost(CRBNode *n) { - if (n) - while (n->left) - n = n->left; - return n; -} - -/** - * c_rbnode_rightmost() - return rightmost child - * @n: current node, or NULL - * - * This returns the rightmost child of @n. If @n is NULL, this will return - * NULL. In all other cases, this function returns a valid pointer. That is, if - * @n does not have any right children, this returns @n. - * - * Worst case runtime (n: number of elements in tree): O(log(n)) - * - * Return: Pointer to rightmost child, or NULL. - */ -_c_public_ CRBNode *c_rbnode_rightmost(CRBNode *n) { - if (n) - while (n->right) - n = n->right; - return n; -} - -/** - * c_rbnode_leftdeepest() - return left-deepest child - * @n: current node, or NULL - * - * This returns the left-deepest child of @n. If @n is NULL, this will return - * NULL. In all other cases, this function returns a valid pointer. That is, if - * @n does not have any children, this returns @n. - * - * The left-deepest child is defined as the deepest child without any left - * (grand-...)siblings. - * - * Worst case runtime (n: number of elements in tree): O(log(n)) - * - * Return: Pointer to left-deepest child, or NULL. - */ -_c_public_ CRBNode *c_rbnode_leftdeepest(CRBNode *n) { - if (n) { - for (;;) { - if (n->left) - n = n->left; - else if (n->right) - n = n->right; - else - break; - } - } - return n; -} - -/** - * c_rbnode_rightdeepest() - return right-deepest child - * @n: current node, or NULL - * - * This returns the right-deepest child of @n. If @n is NULL, this will return - * NULL. In all other cases, this function returns a valid pointer. That is, if - * @n does not have any children, this returns @n. - * - * The right-deepest child is defined as the deepest child without any right - * (grand-...)siblings. - * - * Worst case runtime (n: number of elements in tree): O(log(n)) - * - * Return: Pointer to right-deepest child, or NULL. - */ -_c_public_ CRBNode *c_rbnode_rightdeepest(CRBNode *n) { - if (n) { - for (;;) { - if (n->right) - n = n->right; - else if (n->left) - n = n->left; - else - break; - } - } - return n; -} - -/** - * c_rbnode_next() - return next node - * @n: current node, or NULL - * - * An RB-Tree always defines a linear order of its elements. This function - * returns the logically next node to @n. If @n is NULL, the last node or - * unlinked, this returns NULL. - * - * Worst case runtime (n: number of elements in tree): O(log(n)) - * - * Return: Pointer to next node, or NULL. - */ -_c_public_ CRBNode *c_rbnode_next(CRBNode *n) { - CRBNode *p; - - if (!c_rbnode_is_linked(n)) - return NULL; - if (n->right) - return c_rbnode_leftmost(n->right); - - while ((p = c_rbnode_parent(n)) && n == p->right) - n = p; - - return p; -} - -/** - * c_rbnode_prev() - return previous node - * @n: current node, or NULL - * - * An RB-Tree always defines a linear order of its elements. This function - * returns the logically previous node to @n. If @n is NULL, the first node or - * unlinked, this returns NULL. - * - * Worst case runtime (n: number of elements in tree): O(log(n)) - * - * Return: Pointer to previous node, or NULL. - */ -_c_public_ CRBNode *c_rbnode_prev(CRBNode *n) { - CRBNode *p; - - if (!c_rbnode_is_linked(n)) - return NULL; - if (n->left) - return c_rbnode_rightmost(n->left); - - while ((p = c_rbnode_parent(n)) && n == p->left) - n = p; - - return p; -} - -/** - * c_rbnode_next_postorder() - return next node in post-order - * @n: current node, or NULL - * - * This returns the next node to @n, based on a left-to-right post-order - * traversal. If @n is NULL, the root node, or unlinked, this returns NULL. - * - * This implements a left-to-right post-order traversal: First visit the left - * child of a node, then the right, and lastly the node itself. Children are - * traversed recursively. - * - * This function can be used to implement a left-to-right post-order traversal: - * - * for (n = c_rbtree_first_postorder(t); n; n = c_rbnode_next_postorder(n)) - * visit(n); - * - * Worst case runtime (n: number of elements in tree): O(log(n)) - * - * Return: Pointer to next node, or NULL. - */ -_c_public_ CRBNode *c_rbnode_next_postorder(CRBNode *n) { - CRBNode *p; - - if (!c_rbnode_is_linked(n)) - return NULL; - - p = c_rbnode_parent(n); - if (p && n == p->left && p->right) - return c_rbnode_leftdeepest(p->right); - - return p; -} - -/** - * c_rbnode_prev_postorder() - return previous node in post-order - * @n: current node, or NULL - * - * This returns the previous node to @n, based on a left-to-right post-order - * traversal. That is, it is the inverse operation to c_rbnode_next_postorder(). - * If @n is NULL, the left-deepest node, or unlinked, this returns NULL. - * - * This function returns the logical previous node in a directed post-order - * traversal. That is, it effectively does a pre-order traversal (since a - * reverse post-order traversal is a pre-order traversal). This function does - * NOT do a right-to-left post-order traversal! In other words, the following - * invariant is guaranteed, if c_rbnode_next_postorder(n) is non-NULL: - * - * n == c_rbnode_prev_postorder(c_rbnode_next_postorder(n)) - * - * This function can be used to implement a right-to-left pre-order traversal, - * using the fact that a reverse post-order traversal is also a valid pre-order - * traversal: - * - * for (n = c_rbtree_last_postorder(t); n; n = c_rbnode_prev_postorder(n)) - * visit(n); - * - * This would effectively perform a right-to-left pre-order traversal: first - * visit a parent, then its right child, then its left child. Both children are - * traversed recursively. - * - * Worst case runtime (n: number of elements in tree): O(log(n)) - * - * Return: Pointer to previous node in post-order, or NULL. - */ -_c_public_ CRBNode *c_rbnode_prev_postorder(CRBNode *n) { - CRBNode *p; - - if (!c_rbnode_is_linked(n)) - return NULL; - if (n->right) - return n->right; - if (n->left) - return n->left; - - while ((p = c_rbnode_parent(n))) { - if (p->left && n != p->left) - return p->left; - n = p; - } - - return NULL; -} - -/** - * c_rbtree_first() - return first node - * @t: tree to operate on - * - * An RB-Tree always defines a linear order of its elements. This function - * returns the logically first node in @t. If @t is empty, NULL is returned. - * - * Fixed runtime (n: number of elements in tree): O(log(n)) - * - * Return: Pointer to first node, or NULL. - */ -_c_public_ CRBNode *c_rbtree_first(CRBTree *t) { - c_assert(t); - return c_rbnode_leftmost(t->root); -} - -/** - * c_rbtree_last() - return last node - * @t: tree to operate on - * - * An RB-Tree always defines a linear order of its elements. This function - * returns the logically last node in @t. If @t is empty, NULL is returned. - * - * Fixed runtime (n: number of elements in tree): O(log(n)) - * - * Return: Pointer to last node, or NULL. - */ -_c_public_ CRBNode *c_rbtree_last(CRBTree *t) { - c_assert(t); - return c_rbnode_rightmost(t->root); -} - -/** - * c_rbtree_first_postorder() - return first node in post-order - * @t: tree to operate on - * - * This returns the first node of a left-to-right post-order traversal. That - * is, it returns the left-deepest leaf. If the tree is empty, this returns - * NULL. - * - * This can also be interpreted as the last node of a right-to-left pre-order - * traversal. - * - * Fixed runtime (n: number of elements in tree): O(log(n)) - * - * Return: Pointer to first node in post-order, or NULL. - */ -_c_public_ CRBNode *c_rbtree_first_postorder(CRBTree *t) { - c_assert(t); - return c_rbnode_leftdeepest(t->root); -} - -/** - * c_rbtree_last_postorder() - return last node in post-order - * @t: tree to operate on - * - * This returns the last node of a left-to-right post-order traversal. That is, - * it always returns the root node, or NULL if the tree is empty. - * - * This can also be interpreted as the first node of a right-to-left pre-order - * traversal. - * - * Fixed runtime (n: number of elements in tree): O(1) - * - * Return: Pointer to last node in post-order, or NULL. - */ -_c_public_ CRBNode *c_rbtree_last_postorder(CRBTree *t) { - c_assert(t); - return t->root; -} - -static inline void c_rbtree_store(CRBNode **ptr, CRBNode *addr) { - /* - * We use volatile accesses whenever we STORE @left or @right members - * of a node. This guarantees that any parallel, lockless lookup gets - * to see those stores in the correct order, which itself guarantees - * that there're no temporary loops during tree rotation. - * Note that you still need to properly synchronize your accesses via - * seqlocks, rcu, whatever. We just guarantee that you get *some* - * result on a lockless traversal and never run into endless loops, or - * undefined behavior. - */ - *(volatile CRBNode **)ptr = addr; -} - -/* - * Set the flags and parent of a node. This should be treated as a simple - * assignment of the 'flags' and 'parent' fields of the node. No other magic is - * applied. But since both fields share its backing memory, this helper - * function is provided. - */ -static inline void c_rbnode_set_parent_and_flags(CRBNode *n, CRBNode *p, unsigned long flags) { - n->__parent_and_flags = (unsigned long)p | flags; -} - -/* - * Nodes in the tree do not separately store a point to the tree root. That is, - * there is no way to access the tree-root in O(1) given an arbitrary node. - * Fortunately, this is usually not required. The only situation where this is - * needed is when rotating the root-node itself. - * - * In case of the root node, c_rbnode_parent() returns NULL. We use this fact - * to re-use the parent-pointer storage of the root node to point to the - * CRBTree root. This way, we can rotate the root-node (or add/remove it) - * without requiring a separate tree-root pointer. - * - * However, to keep the tree-modification functions simple, we hide this detail - * whenever possible. This means, c_rbnode_parent() will continue to return - * NULL, and tree modifications will boldly reset the pointer to NULL on - * rotation. Hence, the only way to retain this pointer is to call - * c_rbnode_pop_root() on a possible root-node before rotating. This returns - * NULL if the node in question is not the root node. Otherwise, it returns the - * tree-root, and clears the pointer/flag from the node in question. This way, - * you can perform tree operations as usual. Afterwards, use - * c_rbnode_push_root() to restore the root-pointer on any possible new root. - */ -static inline CRBTree *c_rbnode_pop_root(CRBNode *n) { - CRBTree *t = NULL; - - if (c_rbnode_is_root(n)) { - t = c_rbnode_raw(n); - n->__parent_and_flags = c_rbnode_flags(n) & ~C_RBNODE_ROOT; - } - - return t; -} - -/* counter-part to c_rbnode_pop_root() */ -static inline CRBTree *c_rbnode_push_root(CRBNode *n, CRBTree *t) { - if (t) { - if (n) - n->__parent_and_flags = (unsigned long)t - | c_rbnode_flags(n) - | C_RBNODE_ROOT; - c_rbtree_store(&t->root, n); - } - - return NULL; -} - -/* - * This function partially swaps a child node with another one. That is, this - * function changes the parent of @old to point to @new. That is, you use it - * when swapping @old with @new, to update the parent's left/right pointer. - * This function does *NOT* perform a full swap, nor does it touch any 'parent' - * pointer. - * - * The sole purpose of this function is to shortcut left/right conditionals - * like this: - * - * if (old == old->parent->left) - * old->parent->left = new; - * else - * old->parent->right = new; - * - * That's it! If @old is the root node, this will do nothing. The caller must - * employ c_rbnode_pop_root() and c_rbnode_push_root(). - */ -static inline void c_rbnode_swap_child(CRBNode *old, CRBNode *new) { - CRBNode *p = c_rbnode_parent(old); - - if (p) { - if (p->left == old) - c_rbtree_store(&p->left, new); - else - c_rbtree_store(&p->right, new); - } -} - -/** - * c_rbtree_move() - move tree - * @to: destination tree - * @from: source tree - * - * This imports the entire tree from @from into @to. @to must be empty! @from - * will be empty afterwards. - * - * Note that this operates in O(1) time. Only the root-entry is updated to - * point to the new tree-root. - */ -_c_public_ void c_rbtree_move(CRBTree *to, CRBTree *from) { - CRBTree *t; - - c_assert(!to->root); - - if (from->root) { - t = c_rbnode_pop_root(from->root); - c_assert(t == from); - - to->root = from->root; - from->root = NULL; - - c_rbnode_push_root(to->root, to); - } -} - -static inline void c_rbtree_paint_terminal(CRBNode *n) { - CRBNode *p, *g, *gg, *x; - CRBTree *t; - - /* - * Case 4: - * This path assumes @n is red, @p is red, but the uncle is unset or - * black. This implies @g exists and is black. - * - * This case requires up to 2 rotations to restore the tree invariants. - * That is, it runs in O(1) time and fully restores the RB-Tree - * invariants, all at the cost of performing at mots 2 rotations. - */ - - p = c_rbnode_parent(n); - g = c_rbnode_parent(p); - gg = c_rbnode_parent(g); - - c_assert(c_rbnode_is_red(p)); - c_assert(c_rbnode_is_black(g)); - c_assert(p == g->left || !g->left || c_rbnode_is_black(g->left)); - c_assert(p == g->right || !g->right || c_rbnode_is_black(g->right)); - - if (p == g->left) { - if (n == p->right) { - /* - * We're the right red child of a red parent, which is - * a left child. Rotate on parent and consider us to be - * the old parent and the old parent to be us, making us - * the left child instead of the right child so we can - * handle it the same as below. Rotating two red nodes - * changes none of the invariants. - */ - x = n->left; - c_rbtree_store(&p->right, x); - c_rbtree_store(&n->left, p); - if (x) - c_rbnode_set_parent_and_flags(x, p, c_rbnode_flags(x)); - c_rbnode_set_parent_and_flags(p, n, c_rbnode_flags(p)); - p = n; - } - - /* 'n' is invalid from here on! */ - - /* - * We're the red left child of a red parent, black grandparent - * and uncle. Rotate parent on grandparent and switch their - * colors, making the parent black and the grandparent red. The - * root of this subtree was changed from the grandparent to the - * parent, but the color remained black, so the number of black - * nodes on each path stays the same. However, we got rid of - * the double red path as we are still the (red) child of the - * parent, which has now turned black. Note that had we been - * the right child, rather than the left child, we would now be - * the left child of the old grandparent, and we would still - * have a double red path. As the new grandparent remains - * black, we're done. - */ - x = p->right; - t = c_rbnode_pop_root(g); - c_rbtree_store(&g->left, x); - c_rbtree_store(&p->right, g); - c_rbnode_swap_child(g, p); - if (x) - c_rbnode_set_parent_and_flags(x, g, c_rbnode_flags(x) & ~C_RBNODE_RED); - c_rbnode_set_parent_and_flags(p, gg, c_rbnode_flags(p) & ~C_RBNODE_RED); - c_rbnode_set_parent_and_flags(g, p, c_rbnode_flags(g) | C_RBNODE_RED); - c_rbnode_push_root(p, t); - } else /* if (p == g->right) */ { /* same as above, but mirrored */ - if (n == p->left) { - x = n->right; - c_rbtree_store(&p->left, n->right); - c_rbtree_store(&n->right, p); - if (x) - c_rbnode_set_parent_and_flags(x, p, c_rbnode_flags(x)); - c_rbnode_set_parent_and_flags(p, n, c_rbnode_flags(p)); - p = n; - } - - x = p->left; - t = c_rbnode_pop_root(g); - c_rbtree_store(&g->right, x); - c_rbtree_store(&p->left, g); - c_rbnode_swap_child(g, p); - if (x) - c_rbnode_set_parent_and_flags(x, g, c_rbnode_flags(x) & ~C_RBNODE_RED); - c_rbnode_set_parent_and_flags(p, gg, c_rbnode_flags(p) & ~C_RBNODE_RED); - c_rbnode_set_parent_and_flags(g, p, c_rbnode_flags(g) | C_RBNODE_RED); - c_rbnode_push_root(p, t); - } -} - -static inline CRBNode *c_rbtree_paint_path(CRBNode *n) { - CRBNode *p, *g, *u; - - for (;;) { - p = c_rbnode_parent(n); - if (!p) { - /* - * Case 1: - * We reached the root. Mark it black and be done. As - * all leaf-paths share the root, the ratio of black - * nodes on each path stays the same. - */ - c_rbnode_set_parent_and_flags(n, c_rbnode_raw(n), c_rbnode_flags(n) & ~C_RBNODE_RED); - return NULL; - } else if (c_rbnode_is_black(p)) { - /* - * Case 2: - * The parent is already black. As our node is red, we - * did not change the number of black nodes on any - * path, nor do we have multiple consecutive red nodes. - * There is nothing to be done. - */ - return NULL; - } - - g = c_rbnode_parent(p); - u = (p == g->left) ? g->right : g->left; - if (!u || !c_rbnode_is_red(u)) { - /* - * Case 4: - * The parent is red, but its uncle is black. By - * rotating the parent above the uncle, we distribute - * the red nodes and thus restore the tree invariants. - * No recursive fixup will be needed afterwards. Hence, - * just let the caller know about @n and make them do - * the rotations. - */ - return n; - } - - /* - * Case 3: - * Parent and uncle are both red, and grandparent is black. - * Repaint parent and uncle black, the grandparent red and - * recurse into the grandparent. Note that this is the only - * recursive case. That is, this step restores the tree - * invariants for the sub-tree below @p (including @n), but - * needs to continue the re-coloring two levels up. - */ - c_rbnode_set_parent_and_flags(p, g, c_rbnode_flags(p) & ~C_RBNODE_RED); - c_rbnode_set_parent_and_flags(u, g, c_rbnode_flags(u) & ~C_RBNODE_RED); - c_rbnode_set_parent_and_flags(g, c_rbnode_raw(g), c_rbnode_flags(g) | C_RBNODE_RED); - n = g; - } -} - -static inline void c_rbtree_paint(CRBNode *n) { - /* - * When a new node is inserted into an RB-Tree, we always link it as a - * tail-node and paint it red. This way, the node will not violate the - * rb-tree invariants regarding the number of black nodes on all paths. - * - * However, a red node must never have another bordering red-node (ie., - * child or parent). Since the node is newly linked, it does not have - * any children. Therefore, all we need to do is fix the path upwards - * through all parents until we hit a black parent or can otherwise fix - * the coloring. - * - * This function first walks up the path from @n towards the tree root - * (done in c_rbtree_paint_path()). This recolors its parent/uncle, if - * possible, until it hits a sub-tree that cannot be fixed via - * re-coloring. After c_rbtree_paint_path() returns, there are two - * possible outcomes: - * - * 1) @n is NULL, in which case the tree invariants were - * restored by mere recoloring. Nothing is to be done. - * - * 2) @n is non-NULL, but points to a red ancestor of the - * original node. In this case we need to restore the tree - * invariants via a simple left or right rotation. This will - * be done by c_rbtree_paint_terminal(). - * - * As a summary, this function runs O(log(n)) re-coloring operations in - * the worst case, followed by O(1) rotations as final restoration. The - * amortized cost, however, is O(1), since re-coloring only recurses - * upwards if it hits a red uncle (which can only happen if a previous - * operation terminated its operation on that layer). - * While amortized painting of inserted nodes is O(1), finding the - * correct spot to link the node (before painting it) still requires a - * search in the binary tree in O(log(n)). - */ - n = c_rbtree_paint_path(n); - if (n) - c_rbtree_paint_terminal(n); -} - -/** - * c_rbnode_link() - link node into tree - * @p: parent node to link under - * @l: left/right slot of @p to link at - * @n: node to add - * - * This links @n into an tree underneath another node. The caller must provide - * the exact spot where to link the node. That is, the caller must traverse the - * tree based on their search order. Once they hit a leaf where to insert the - * node, call this function to link it and rebalance the tree. - * - * For this to work, the caller must provide a pointer to the parent node. If - * the tree might be empty, you must resort to c_rbtree_add(). - * - * In most cases you are better off using c_rbtree_add(). See there for details - * how tree-insertion works. - */ -_c_public_ void c_rbnode_link(CRBNode *p, CRBNode **l, CRBNode *n) { - c_assert(p); - c_assert(l); - c_assert(n); - c_assert(l == &p->left || l == &p->right); - - c_rbnode_set_parent_and_flags(n, p, C_RBNODE_RED); - c_rbtree_store(&n->left, NULL); - c_rbtree_store(&n->right, NULL); - c_rbtree_store(l, n); - - c_rbtree_paint(n); -} - -/** - * c_rbtree_add() - add node to tree - * @t: tree to operate one - * @p: parent node to link under, or NULL - * @l: left/right slot of @p (or root) to link at - * @n: node to add - * - * This links @n into the tree given as @t. The caller must provide the exact - * spot where to link the node. That is, the caller must traverse the tree - * based on their search order. Once they hit a leaf where to insert the node, - * call this function to link it and rebalance the tree. - * - * A typical insertion would look like this (@t is your tree, @n is your node): - * - * CRBNode **i, *p; - * - * i = &t->root; - * p = NULL; - * while (*i) { - * p = *i; - * if (compare(n, *i) < 0) - * i = &(*i)->left; - * else - * i = &(*i)->right; - * } - * - * c_rbtree_add(t, p, i, n); - * - * Once the node is linked into the tree, a simple lookup on the same tree can - * be coded like this: - * - * CRBNode *i; - * - * i = t->root; - * while (i) { - * int v = compare(n, i); - * if (v < 0) - * i = (*i)->left; - * else if (v > 0) - * i = (*i)->right; - * else - * break; - * } - * - * When you add nodes to a tree, the memory contents of the node do not matter. - * That is, there is no need to initialize the node via c_rbnode_init(). - * However, if you relink nodes multiple times during their lifetime, it is - * usually very convenient to use c_rbnode_init() and c_rbnode_unlink() (rather - * than c_rbnode_unlink_stale()). In those cases, you should validate that a - * node is unlinked before you call c_rbtree_add(). - */ -_c_public_ void c_rbtree_add(CRBTree *t, CRBNode *p, CRBNode **l, CRBNode *n) { - c_assert(t); - c_assert(l); - c_assert(n); - c_assert(!p || l == &p->left || l == &p->right); - c_assert(p || l == &t->root); - - c_rbnode_set_parent_and_flags(n, p, C_RBNODE_RED); - c_rbtree_store(&n->left, NULL); - c_rbtree_store(&n->right, NULL); - - if (p) - c_rbtree_store(l, n); - else - c_rbnode_push_root(n, t); - - c_rbtree_paint(n); -} - -static inline void c_rbnode_rebalance_terminal(CRBNode *p, CRBNode *previous) { - CRBNode *s, *x, *y, *g; - CRBTree *t; - - if (previous == p->left) { - s = p->right; - if (c_rbnode_is_red(s)) { - /* - * Case 2: - * We have a red node as sibling. Rotate it onto our - * side so we can later on turn it black. This way, we - * gain the additional black node in our path. - */ - t = c_rbnode_pop_root(p); - g = c_rbnode_parent(p); - x = s->left; - c_rbtree_store(&p->right, x); - c_rbtree_store(&s->left, p); - c_rbnode_swap_child(p, s); - c_rbnode_set_parent_and_flags(x, p, c_rbnode_flags(x) & ~C_RBNODE_RED); - c_rbnode_set_parent_and_flags(s, g, c_rbnode_flags(s) & ~C_RBNODE_RED); - c_rbnode_set_parent_and_flags(p, s, c_rbnode_flags(p) | C_RBNODE_RED); - c_rbnode_push_root(s, t); - s = x; - } - - x = s->right; - if (!x || c_rbnode_is_black(x)) { - y = s->left; - if (!y || c_rbnode_is_black(y)) { - /* - * Case 3+4: - * Our sibling is black and has only black - * children. Flip it red and turn parent black. - * This way we gained a black node in our path. - * Note that the parent must be red, otherwise - * it must have been handled by our caller. - */ - c_assert(c_rbnode_is_red(p)); - c_rbnode_set_parent_and_flags(s, p, c_rbnode_flags(s) | C_RBNODE_RED); - c_rbnode_set_parent_and_flags(p, c_rbnode_parent(p), c_rbnode_flags(p) & ~C_RBNODE_RED); - return; - } - - /* - * Case 5: - * Left child of our sibling is red, right one is black. - * Rotate on parent so the right child of our sibling is - * now red, and we can fall through to case 6. - */ - x = y->right; - c_rbtree_store(&s->left, y->right); - c_rbtree_store(&y->right, s); - c_rbtree_store(&p->right, y); - if (x) - c_rbnode_set_parent_and_flags(x, s, c_rbnode_flags(x) & ~C_RBNODE_RED); - x = s; - s = y; - } - - /* - * Case 6: - * The right child of our sibling is red. Rotate left and flip - * colors, which gains us an additional black node in our path, - * that was previously on our sibling. - */ - t = c_rbnode_pop_root(p); - g = c_rbnode_parent(p); - y = s->left; - c_rbtree_store(&p->right, y); - c_rbtree_store(&s->left, p); - c_rbnode_swap_child(p, s); - c_rbnode_set_parent_and_flags(x, s, c_rbnode_flags(x) & ~C_RBNODE_RED); - if (y) - c_rbnode_set_parent_and_flags(y, p, c_rbnode_flags(y)); - c_rbnode_set_parent_and_flags(s, g, c_rbnode_flags(p)); - c_rbnode_set_parent_and_flags(p, s, c_rbnode_flags(p) & ~C_RBNODE_RED); - c_rbnode_push_root(s, t); - } else /* if (previous == p->right) */ { /* same as above, but mirrored */ - s = p->left; - if (c_rbnode_is_red(s)) { - t = c_rbnode_pop_root(p); - g = c_rbnode_parent(p); - x = s->right; - c_rbtree_store(&p->left, x); - c_rbtree_store(&s->right, p); - c_rbnode_swap_child(p, s); - c_rbnode_set_parent_and_flags(x, p, c_rbnode_flags(x) & ~C_RBNODE_RED); - c_rbnode_set_parent_and_flags(s, g, c_rbnode_flags(s) & ~C_RBNODE_RED); - c_rbnode_set_parent_and_flags(p, s, c_rbnode_flags(p) | C_RBNODE_RED); - c_rbnode_push_root(s, t); - s = x; - } - - x = s->left; - if (!x || c_rbnode_is_black(x)) { - y = s->right; - if (!y || c_rbnode_is_black(y)) { - c_assert(c_rbnode_is_red(p)); - c_rbnode_set_parent_and_flags(s, p, c_rbnode_flags(s) | C_RBNODE_RED); - c_rbnode_set_parent_and_flags(p, c_rbnode_parent(p), c_rbnode_flags(p) & ~C_RBNODE_RED); - return; - } - - x = y->left; - c_rbtree_store(&s->right, y->left); - c_rbtree_store(&y->left, s); - c_rbtree_store(&p->left, y); - if (x) - c_rbnode_set_parent_and_flags(x, s, c_rbnode_flags(x) & ~C_RBNODE_RED); - x = s; - s = y; - } - - t = c_rbnode_pop_root(p); - g = c_rbnode_parent(p); - y = s->right; - c_rbtree_store(&p->left, y); - c_rbtree_store(&s->right, p); - c_rbnode_swap_child(p, s); - c_rbnode_set_parent_and_flags(x, s, c_rbnode_flags(x) & ~C_RBNODE_RED); - if (y) - c_rbnode_set_parent_and_flags(y, p, c_rbnode_flags(y)); - c_rbnode_set_parent_and_flags(s, g, c_rbnode_flags(p)); - c_rbnode_set_parent_and_flags(p, s, c_rbnode_flags(p) & ~C_RBNODE_RED); - c_rbnode_push_root(s, t); - } -} - -static inline CRBNode *c_rbnode_rebalance_path(CRBNode *p, CRBNode **previous) { - CRBNode *s, *nl, *nr; - - while (p) { - s = (*previous == p->left) ? p->right : p->left; - nl = s->left; - nr = s->right; - - /* - * If the sibling under @p is black and exclusively has black - * children itself (i.e., nephews/nieces in @nl/@nr), then we - * can easily re-color to fix this sub-tree, and continue one - * layer up. However, if that's not the case, we have tree - * rotations at our hands to move one of the black nodes into - * our path, then turning the red node black to fully restore - * the RB-Tree invariants again. This fixup will be done by the - * caller, so we just let them know where to do that. - */ - if (c_rbnode_is_red(s) || - (nl && c_rbnode_is_red(nl)) || - (nr && c_rbnode_is_red(nr))) - return p; - - /* - * Case 3+4: - * Sibling is black, and all nephews/nieces are black. Flip - * sibling red. This way the sibling lost a black node in its - * path, thus getting even with our path. However, paths not - * going through @p haven't been fixed up, hence we proceed - * recursively one layer up. - * Before we continue one layer up, there are two possible - * terminations: If the parent is red, we can turn it black. - * This terminates the rebalancing, since the entire point of - * rebalancing is that everything below @p has one black node - * less than everything else. Lastly, if there is no layer - * above, we hit the tree root and nothing is left to be done. - */ - c_rbnode_set_parent_and_flags(s, p, c_rbnode_flags(s) | C_RBNODE_RED); - if (c_rbnode_is_red(p)) { - c_rbnode_set_parent_and_flags(p, c_rbnode_parent(p), c_rbnode_flags(p) & ~C_RBNODE_RED); - return NULL; - } - - *previous = p; - p = c_rbnode_parent(p); - } - - return NULL; -} - -static inline void c_rbnode_rebalance(CRBNode *n) { - CRBNode *previous = NULL; - - /* - * Rebalance a tree after a node was removed. This function must be - * called on the parent of the leaf that was removed. It will first - * perform a recursive re-coloring on the parents of @n, until it - * either hits the tree-root, or a condition where a tree-rotation is - * needed to restore the RB-Tree invariants. - */ - - n = c_rbnode_rebalance_path(n, &previous); - if (n) - c_rbnode_rebalance_terminal(n, previous); -} - -/** - * c_rbnode_unlink_stale() - remove node from tree - * @n: node to remove - * - * This removes the given node from its tree. Once unlinked, the tree is - * rebalanced. - * - * This does *NOT* reset @n to being unlinked. If you need this, use - * c_rbtree_unlink(). - */ -_c_public_ void c_rbnode_unlink_stale(CRBNode *n) { - CRBTree *t; - - c_assert(n); - c_assert(c_rbnode_is_linked(n)); - - /* - * There are three distinct cases during node removal of a tree: - * * The node has no children, in which case it can simply be removed. - * * The node has exactly one child, in which case the child displaces - * its parent. - * * The node has two children, in which case there is guaranteed to - * be a successor to the node (successor being the node ordered - * directly after it). This successor is the leftmost descendant of - * the node's right child, so it cannot have a left child of its own. - * Therefore, we can simply swap the node with its successor (including - * color) and remove the node from its new place, which will be one of - * the first two cases. - * - * Whenever the node we removed was black, we have to rebalance the - * tree. Note that this affects the actual node we _remove_, not @n (in - * case we swap it). - */ - - if (!n->left && !n->right) { - /* - * Case 1.0 - * The node has no children, it is a leaf-node and we - * can simply unlink it. If it was also black, we have - * to rebalance. - */ - t = c_rbnode_pop_root(n); - c_rbnode_swap_child(n, NULL); - c_rbnode_push_root(NULL, t); - - if (c_rbnode_is_black(n)) - c_rbnode_rebalance(c_rbnode_parent(n)); - } else if (!n->left && n->right) { - /* - * Case 1.1: - * The node has exactly one child, and it is on the - * right. The child *must* be red (otherwise, the right - * path has more black nodes than the non-existing left - * path), and the node to be removed must hence be - * black. We simply replace the node with its child, - * turning the red child black, and thus no rebalancing - * is required. - */ - t = c_rbnode_pop_root(n); - c_rbnode_swap_child(n, n->right); - c_rbnode_set_parent_and_flags(n->right, c_rbnode_parent(n), c_rbnode_flags(n->right) & ~C_RBNODE_RED); - c_rbnode_push_root(n->right, t); - } else if (n->left && !n->right) { - /* - * Case 1.2: - * The node has exactly one child, and it is on the left. Treat - * it as mirrored case of Case 1.1 (i.e., replace the node by - * its child). - */ - t = c_rbnode_pop_root(n); - c_rbnode_swap_child(n, n->left); - c_rbnode_set_parent_and_flags(n->left, c_rbnode_parent(n), c_rbnode_flags(n->left) & ~C_RBNODE_RED); - c_rbnode_push_root(n->left, t); - } else /* if (n->left && n->right) */ { - CRBNode *s, *p, *c, *next = NULL; - - /* Cache possible tree-root during tree-rotations. */ - t = c_rbnode_pop_root(n); - - /* - * Case 1.3: - * We are dealing with a full interior node with a child on - * both sides. We want to find its successor and swap it, - * then remove the node similar to Case 1. For performance - * reasons we don't perform the full swap, but skip links - * that are about to be removed, anyway. - * - * First locate the successor, remember its child and the - * parent the original node should have been linked on, - * before being removed. Then link up both the successor's - * new children and old child. - * - * s: successor - * p: parent - * c: right (and only potential) child of successor - * next: next node to rebalance on - */ - s = n->right; - if (!s->left) { - /* - * The immediate right child is the successor, - * the successor's right child remains linked - * as before. - */ - p = s; - c = s->right; - } else { - s = c_rbnode_leftmost(s); - p = c_rbnode_parent(s); - c = s->right; - - /* - * The new parent pointer of the successor's - * child is set below. - */ - c_rbtree_store(&p->left, c); - - c_rbtree_store(&s->right, n->right); - c_rbnode_set_parent_and_flags(n->right, s, c_rbnode_flags(n->right)); - } - - /* - * In both the above cases, the successor's left child - * needs to be replaced with the left child of the node - * that is being removed. - */ - c_rbtree_store(&s->left, n->left); - c_rbnode_set_parent_and_flags(n->left, s, c_rbnode_flags(n->left)); - - /* - * As in cases 1.1 and 1.0 above, if successor was a - * black leaf, we need to rebalance the tree, otherwise - * it must have a red child, so simply recolor that black - * and continue. Note that @next must be stored here, as - * the original color of the successor is forgotten below. - */ - if (c) - c_rbnode_set_parent_and_flags(c, p, c_rbnode_flags(c) & ~C_RBNODE_RED); - else - next = c_rbnode_is_black(s) ? p : NULL; - - /* - * Update the successor, to inherit the parent and color - * from the node being removed. - */ - if (c_rbnode_is_red(n)) - c_rbnode_set_parent_and_flags(s, c_rbnode_parent(n), c_rbnode_flags(s) | C_RBNODE_RED); - else - c_rbnode_set_parent_and_flags(s, c_rbnode_parent(n), c_rbnode_flags(s) & ~C_RBNODE_RED); - - /* - * Update the parent of the node being removed. Note that this - * needs to happen after the parent of the successor is set - * above, as that call would clear the root pointer, if set. - */ - c_rbnode_swap_child(n, s); - - /* Possibly restore saved tree-root. */ - c_rbnode_push_root(s, t); - - if (next) - c_rbnode_rebalance(next); - } -} diff --git a/shared/c-rbtree/src/c-rbtree.h b/shared/c-rbtree/src/c-rbtree.h deleted file mode 100644 index d4d0fe45..00000000 --- a/shared/c-rbtree/src/c-rbtree.h +++ /dev/null @@ -1,437 +0,0 @@ -#pragma once - -/** - * Standalone Red-Black-Tree Implementation in Standard ISO-C11 - * - * This library provides an RB-Tree API, that is fully implemented in ISO-C11 - * and has no external dependencies. Furthermore, tree traversal, memory - * allocations, and key comparisons are completely controlled by the API user. - * The implementation only provides the RB-Tree specific rebalancing and - * coloring. - * - * A tree is represented by the "CRBTree" structure. It contains a *single* - * field, which is a pointer to the root node. If NULL, the tree is empty. If - * non-NULL, there is at least a single element in the tree. - * - * Each node of the tree is represented by the "CRBNode" structure. It has - * three fields. The @left and @right members can be accessed by the API user - * directly to traverse the tree. The third member is a combination of the - * parent pointer and a set of flags. - * API users are required to embed the CRBNode object into their own objects - * and then use offsetof() (i.e., container_of() and friends) to turn CRBNode - * pointers into pointers to their own structure. - */ - -#ifdef __cplusplus -extern "C" { -#endif - -#include <assert.h> -#include <stdalign.h> -#include <stddef.h> - -typedef struct CRBNode CRBNode; -typedef struct CRBTree CRBTree; - -/* implementation detail */ -#define C_RBNODE_RED (0x1UL) -#define C_RBNODE_ROOT (0x2UL) -#define C_RBNODE_FLAG_MASK (0x3UL) - -/** - * struct CRBNode - Node of a Red-Black Tree - * @__parent_and_flags: internal state - * @left: left child, or NULL - * @right: right child, or NULL - * - * Each node in an RB-Tree must embed a CRBNode object. This object contains - * pointers to its left and right child, which can be freely accessed by the - * API user at any time. They are NULL, if the node does not have a left/right - * child. - * - * The @__parent_and_flags field must never be accessed directly. It encodes - * the pointer to the parent node, and the color of the node. Use the accessor - * functions instead. - * - * There is no reason to initialize a CRBNode object before linking it. - * However, if you need a boolean state that tells you whether the node is - * linked or not, you should initialize the node via c_rbnode_init() or - * C_RBNODE_INIT. - */ -struct CRBNode { - union { - unsigned long __parent_and_flags; - /* enforce >=4-byte alignment for @__parent_and_flags */ - alignas(4) unsigned char __align_dummy; - }; - CRBNode *left; - CRBNode *right; -}; - -#define C_RBNODE_INIT(_var) { .__parent_and_flags = (unsigned long)&(_var) } - -CRBNode *c_rbnode_leftmost(CRBNode *n); -CRBNode *c_rbnode_rightmost(CRBNode *n); -CRBNode *c_rbnode_leftdeepest(CRBNode *n); -CRBNode *c_rbnode_rightdeepest(CRBNode *n); -CRBNode *c_rbnode_next(CRBNode *n); -CRBNode *c_rbnode_prev(CRBNode *n); -CRBNode *c_rbnode_next_postorder(CRBNode *n); -CRBNode *c_rbnode_prev_postorder(CRBNode *n); - -void c_rbnode_link(CRBNode *p, CRBNode **l, CRBNode *n); -void c_rbnode_unlink_stale(CRBNode *n); - -/** - * struct CRBTree - Red-Black Tree - * @root: pointer to the root node, or NULL - * - * Each Red-Black Tree is rooted in an CRBTree object. This object contains a - * pointer to the root node of the tree. The API user is free to access the - * @root member at any time, and use it to traverse the tree. - * - * To initialize an RB-Tree, set it to NULL / all zero. - */ -struct CRBTree { - union { - CRBNode *root; - /* enforce >=4-byte alignment for @root */ - alignas(4) unsigned char __align_dummy; - }; -}; - -#define C_RBTREE_INIT {} - -CRBNode *c_rbtree_first(CRBTree *t); -CRBNode *c_rbtree_last(CRBTree *t); -CRBNode *c_rbtree_first_postorder(CRBTree *t); -CRBNode *c_rbtree_last_postorder(CRBTree *t); - -void c_rbtree_move(CRBTree *to, CRBTree *from); -void c_rbtree_add(CRBTree *t, CRBNode *p, CRBNode **l, CRBNode *n); - -/** - * c_rbnode_init() - mark a node as unlinked - * @n: node to operate on - * - * This marks the node @n as unlinked. The node will be set to a valid state - * that can never happen if the node is linked in a tree. Furthermore, this - * state is fully known to the implementation, and as such handled gracefully - * in all cases. - * - * You are *NOT* required to call this on your node. c_rbtree_add() can handle - * uninitialized nodes just fine. However, calling this allows to use - * c_rbnode_is_linked() to check for the state of a node. Furthermore, - * iterators and accessors can be called on initialized (yet unlinked) nodes. - * - * Use the C_RBNODE_INIT macro if you want to initialize static variables. - */ -static inline void c_rbnode_init(CRBNode *n) { - *n = (CRBNode)C_RBNODE_INIT(*n); -} - -/** - * c_rbnode_entry() - get parent container of tree node - * @_what: tree node, or NULL - * @_t: type of parent container - * @_m: member name of tree node in @_t - * - * If the tree node @_what is embedded into a surrounding structure, this will - * turn the tree node pointer @_what into a pointer to the parent container - * (using offsetof(3), or sometimes called container_of(3)). - * - * If @_what is NULL, this will also return NULL. - * - * Return: Pointer to parent container, or NULL. - */ -#define c_rbnode_entry(_what, _t, _m) \ - ((_t *)(void *)(((unsigned long)(void *)(_what) ?: \ - offsetof(_t, _m)) - offsetof(_t, _m))) - -/** - * c_rbnode_parent() - return parent pointer - * @n node to access - * - * This returns a pointer to the parent of the given node @n. If @n does not - * have a parent, NULL is returned. If @n is not linked, @n itself is returned. - * - * You should not call this on unlinked or uninitialized nodes! If you do, you - * better know its semantics. - * - * Return: Pointer to parent. - */ -static inline CRBNode *c_rbnode_parent(CRBNode *n) { - return (n->__parent_and_flags & C_RBNODE_ROOT) ? - NULL : - (void *)(n->__parent_and_flags & ~C_RBNODE_FLAG_MASK); -} - -/** - * c_rbnode_is_linked() - check whether a node is linked - * @n: node to check, or NULL - * - * This checks whether the passed node is linked. If you pass NULL, or if the - * node is not linked into a tree, this will return false. Otherwise, this - * returns true. - * - * Note that you must have either linked the node or initialized it, before - * calling this function. Never call this function on uninitialized nodes. - * Furthermore, removing a node via c_rbnode_unlink_stale() does *NOT* mark the - * node as unlinked. You have to call c_rbnode_init() yourself after removal, or - * use the c_rbnode_unlink() helper. - * - * Return: true if the node is linked, false if not. - */ -static inline _Bool c_rbnode_is_linked(CRBNode *n) { - return n && c_rbnode_parent(n) != n; -} - -/** - * c_rbnode_unlink() - safely remove node from tree and reinitialize it - * @n: node to remove, or NULL - * - * This is almost the same as c_rbnode_unlink_stale(), but extends it slightly, to be - * more convenient to use in many cases: - * - if @n is unlinked or NULL, this is a no-op - * - @n is reinitialized after being removed - */ -static inline void c_rbnode_unlink(CRBNode *n) { - if (c_rbnode_is_linked(n)) { - c_rbnode_unlink_stale(n); - c_rbnode_init(n); - } -} - -/** - * c_rbtree_init() - initialize a new RB-Tree - * @t: tree to operate on - * - * This initializes a new, empty RB-Tree. An RB-Tree must be initialized before - * any other functions are called on it. Alternatively, you can zero its memory - * or assign C_RBTREE_INIT. - */ -static inline void c_rbtree_init(CRBTree *t) { - *t = (CRBTree)C_RBTREE_INIT; -} - -/** - * c_rbtree_is_empty() - check whether an RB-tree is empty - * @t: tree to operate on - * - * This checks whether the passed RB-Tree is empty. - * - * Return: True if tree is empty, false otherwise. - */ -static inline _Bool c_rbtree_is_empty(CRBTree *t) { - return !t->root; -} - -/** - * CRBCompareFunc - compare a node to a key - * @t: tree where the node is linked to - * @k: key to compare - * @n: node to compare - * - * If you use the tree-traversal helpers (which are optional), you need to - * provide this callback so they can compare nodes in a tree to the key you - * look for. - * - * The tree @t is provided as optional context to this callback. The key you - * look for is provided as @k, the current node that should be compared to is - * provided as @n. This function should work like strcmp(), that is, return <0 - * if @key orders before @n, 0 if both compare equal, and >0 if it orders after - * @n. - */ -typedef int (*CRBCompareFunc) (CRBTree *t, void *k, CRBNode *n); - -/** - * c_rbtree_find_node() - find node - * @t: tree to search through - * @f: comparison function - * @k: key to search for - * - * This searches through @t for a node that compares equal to @k. The function - * @f must be provided by the caller, which is used to compare nodes to @k. See - * the documentation of CRBCompareFunc for details. - * - * If there are multiple entries that compare equal to @k, this will return a - * pseudo-randomly picked node. If you need stable lookup functions for trees - * where duplicate entries are allowed, you better code your own lookup. - * - * Return: Pointer to matching node, or NULL. - */ -static inline CRBNode *c_rbtree_find_node(CRBTree *t, CRBCompareFunc f, const void *k) { - CRBNode *i; - - assert(t); - assert(f); - - i = t->root; - while (i) { - int v = f(t, (void *)k, i); - if (v < 0) - i = i->left; - else if (v > 0) - i = i->right; - else - return i; - } - - return NULL; -} - -/** - * c_rbtree_find_entry() - find entry - * @_t: tree to search through - * @_f: comparison function - * @_k: key to search for - * @_s: type of the structure that embeds the nodes - * @_m: name of the node-member in type @_t - * - * This is very similar to c_rbtree_find_node(), but instead of returning a - * pointer to the CRBNode, it returns a pointer to the surrounding object. This - * object must embed the CRBNode object. The type of the surrounding object - * must be given as @_s, and the name of the embedded CRBNode member as @_m. - * - * See c_rbtree_find_node() and c_rbnode_entry() for more details. - * - * Return: Pointer to found entry, NULL if not found. - */ -#define c_rbtree_find_entry(_t, _f, _k, _s, _m) \ - c_rbnode_entry(c_rbtree_find_node((_t), (_f), (_k)), _s, _m) - -/** - * c_rbtree_find_slot() - find slot to insert new node - * @t: tree to search through - * @f: comparison function - * @k: key to search for - * @p: output storage for parent pointer - * - * This searches through @t just like c_rbtree_find_node() does. However, - * instead of returning a pointer to a node that compares equal to @k, this - * searches for a slot to insert a node with key @k. A pointer to the slot is - * returned, and a pointer to the parent of the slot is stored in @p. Both - * can be passed directly to c_rbtree_add(), together with your node to insert. - * - * If there already is a node in the tree, that compares equal to @k, this will - * return NULL and store the conflicting node in @p. In all other cases, - * this will return a pointer (non-NULL) to the empty slot to insert the node - * at. @p will point to the parent node of that slot. - * - * If you want trees that allow duplicate nodes, you better code your own - * insertion function. - * - * Return: Pointer to slot to insert node, or NULL on conflicts. - */ -static inline CRBNode **c_rbtree_find_slot(CRBTree *t, CRBCompareFunc f, const void *k, CRBNode **p) { - CRBNode **i; - - assert(t); - assert(f); - assert(p); - - i = &t->root; - *p = NULL; - while (*i) { - int v = f(t, (void *)k, *i); - *p = *i; - if (v < 0) - i = &(*i)->left; - else if (v > 0) - i = &(*i)->right; - else - return NULL; - } - - return i; -} - -/** - * c_rbtree_for_each*() - iterators - * - * The c_rbtree_for_each*() macros provide simple for-loop wrappers to iterate - * an RB-Tree. They come in a set of flavours: - * - * - "entry": This combines c_rbnode_entry() with the loop iterator, so the - * iterator always has the type of the surrounding object, rather - * than CRBNode. - * - * - "safe": The loop iterator always keeps track of the next element to - * visit. This means, you can safely modify the current element, - * while retaining loop-integrity. - * You still must not touch any other entry of the tree. Otherwise, - * the loop-iterator will be corrupted. Also remember to only - * modify the tree in a way compatible with your iterator-order. - * That is, if you use in-order iteration (default), you can unlink - * your current object, including re-balancing the tree. However, - * if you use post-order, you must not trigger a tree rebalance - * operation, since it is not an invariant of post-order iteration. - * - * - "postorder": Rather than the default in-order iteration, this iterates - * the tree in post-order. - * - * - "unlink": This unlinks the current element from the tree before the loop - * code is run. Note that the tree is not rebalanced. That is, - * you must never break out of the loop. If you do so, the tree - * is corrupted. - */ - -#define c_rbtree_for_each(_iter, _tree) \ - for (_iter = c_rbtree_first(_tree); \ - _iter; \ - _iter = c_rbnode_next(_iter)) - -#define c_rbtree_for_each_entry(_iter, _tree, _m) \ - for (_iter = c_rbnode_entry(c_rbtree_first(_tree), __typeof__(*_iter), _m); \ - _iter; \ - _iter = c_rbnode_entry(c_rbnode_next(&_iter->_m), __typeof__(*_iter), _m)) - -#define c_rbtree_for_each_safe(_iter, _safe, _tree) \ - for (_iter = c_rbtree_first(_tree), _safe = c_rbnode_next(_iter); \ - _iter; \ - _iter = _safe, _safe = c_rbnode_next(_safe)) - -#define c_rbtree_for_each_entry_safe(_iter, _safe, _tree, _m) \ - for (_iter = c_rbnode_entry(c_rbtree_first(_tree), __typeof__(*_iter), _m), \ - _safe = _iter ? c_rbnode_entry(c_rbnode_next(&_iter->_m), __typeof__(*_iter), _m) : NULL; \ - _iter; \ - _iter = _safe, \ - _safe = _safe ? c_rbnode_entry(c_rbnode_next(&_safe->_m), __typeof__(*_iter), _m) : NULL) - -#define c_rbtree_for_each_postorder(_iter, _tree) \ - for (_iter = c_rbtree_first_postorder(_tree); \ - _iter; \ - _iter = c_rbnode_next_postorder(_iter)) \ - -#define c_rbtree_for_each_entry_postorder(_iter, _tree, _m) \ - for (_iter = c_rbnode_entry(c_rbtree_first_postorder(_tree), __typeof__(*_iter), _m); \ - _iter; \ - _iter = c_rbnode_entry(c_rbnode_next_postorder(&_iter->_m), __typeof__(*_iter), _m)) - -#define c_rbtree_for_each_safe_postorder(_iter, _safe, _tree) \ - for (_iter = c_rbtree_first_postorder(_tree), _safe = c_rbnode_next_postorder(_iter); \ - _iter; \ - _iter = _safe, _safe = c_rbnode_next_postorder(_safe)) - -#define c_rbtree_for_each_entry_safe_postorder(_iter, _safe, _tree, _m) \ - for (_iter = c_rbnode_entry(c_rbtree_first_postorder(_tree), __typeof__(*_iter), _m), \ - _safe = _iter ? c_rbnode_entry(c_rbnode_next_postorder(&_iter->_m), __typeof__(*_iter), _m) : NULL; \ - _iter; \ - _iter = _safe, \ - _safe = _safe ? c_rbnode_entry(c_rbnode_next_postorder(&_safe->_m), __typeof__(*_iter), _m) : NULL) - -#define c_rbtree_for_each_safe_postorder_unlink(_iter, _safe, _tree) \ - for (_iter = c_rbtree_first_postorder(_tree), _safe = c_rbnode_next_postorder(_iter); \ - _iter ? ((*_iter = (CRBNode)C_RBNODE_INIT(*_iter)), 1) : (((_tree)->root = NULL), 0); \ - _iter = _safe, _safe = c_rbnode_next_postorder(_safe)) \ - -#define c_rbtree_for_each_entry_safe_postorder_unlink(_iter, _safe, _tree, _m) \ - for (_iter = c_rbnode_entry(c_rbtree_first_postorder(_tree), __typeof__(*_iter), _m), \ - _safe = _iter ? c_rbnode_entry(c_rbnode_next_postorder(&_iter->_m), __typeof__(*_iter), _m) : NULL; \ - _iter ? ((_iter->_m = (CRBNode)C_RBNODE_INIT(_iter->_m)), 1) : (((_tree)->root = NULL), 0); \ - _iter = _safe, \ - _safe = _safe ? c_rbnode_entry(c_rbnode_next_postorder(&_safe->_m), __typeof__(*_iter), _m) : NULL) - -#ifdef __cplusplus -} -#endif diff --git a/shared/c-siphash/src/c-siphash.c b/shared/c-siphash/src/c-siphash.c deleted file mode 100644 index 720e403a..00000000 --- a/shared/c-siphash/src/c-siphash.c +++ /dev/null @@ -1,245 +0,0 @@ -/* - * SipHash Implementation - * - * For highlevel documentation of the API see the header file and the docbook - * comments. This implementation is based on the reference implementation of - * SipHash, written by Jean-Philippe Aumasson and Daniel J. Bernstein, and - * released to the Public Domain. - * - * So far, only SipHash24 is implemented, since there was no need for other - * parameters. However, adjusted c_siphash_append_X() and - * C_siphash_finalize_Y() can be easily provided, if required. - */ - -#include <c-stdaux.h> -#include <stddef.h> -#include <stdint.h> -#include "c-siphash.h" - -static inline uint64_t c_siphash_read_le64(const uint8_t bytes[8]) { - return ((uint64_t) bytes[0]) | - (((uint64_t) bytes[1]) << 8) | - (((uint64_t) bytes[2]) << 16) | - (((uint64_t) bytes[3]) << 24) | - (((uint64_t) bytes[4]) << 32) | - (((uint64_t) bytes[5]) << 40) | - (((uint64_t) bytes[6]) << 48) | - (((uint64_t) bytes[7]) << 56); -} - -static inline uint64_t c_siphash_rotate_left(uint64_t x, uint8_t b) { - return (x << b) | (x >> (64 - b)); -} - -static inline void c_siphash_sipround(CSipHash *state) { - state->v0 += state->v1; - state->v1 = c_siphash_rotate_left(state->v1, 13); - state->v1 ^= state->v0; - state->v0 = c_siphash_rotate_left(state->v0, 32); - state->v2 += state->v3; - state->v3 = c_siphash_rotate_left(state->v3, 16); - state->v3 ^= state->v2; - state->v0 += state->v3; - state->v3 = c_siphash_rotate_left(state->v3, 21); - state->v3 ^= state->v0; - state->v2 += state->v1; - state->v1 = c_siphash_rotate_left(state->v1, 17); - state->v1 ^= state->v2; - state->v2 = c_siphash_rotate_left(state->v2, 32); -} - -/** - * c_siphash_init() - initialize siphash context - * @state: context object - * @seed: 128bit seed - * - * This initializes the siphash state context. Once initialized, it can be used - * to hash arbitrary input. To feed data into it, use c_siphash_append(). To get - * the final hash, use c_siphash_finalize(). - * - * Note that the siphash context does not allocate state. There is no need to - * deserialize it before releasing its backing memory. - * - * The hashes generated by this context change depending on the seed. Every - * user is highly inclined to provide their unique seed. If no stable hashes - * are needed, a random seed will do fine. - * - * Right now, only SipHash24 is supported. Other SipHash parameters can be - * easily added if required. - */ -_c_public_ void c_siphash_init(CSipHash *state, const uint8_t seed[16]) { - uint64_t k0, k1; - - k0 = c_siphash_read_le64(seed); - k1 = c_siphash_read_le64(seed + 8); - - *state = (CSipHash) { - /* - * Default seed is taken from the reference implementation - * of SipHash24 ("somepseudorandomlygeneratedbytes"). Callers - * are still recommended to provide proper seeds themselves. - */ - .v0 = 0x736f6d6570736575ULL ^ k0, - .v1 = 0x646f72616e646f6dULL ^ k1, - .v2 = 0x6c7967656e657261ULL ^ k0, - .v3 = 0x7465646279746573ULL ^ k1, - .padding = 0, - .n_bytes = 0, - }; -} - -/** - * c_siphash_append() - hash stream of data - * @state: context object - * @bytes: array of input bytes - * @n_bytes: number of input bytes - * - * This feeds an array of bytes into the SipHash state machine. This is a - * streaming-capable API. That is, the resulting hash is the same, regardless - * of the way you chunk the input. - * This function simply feeds the given bytes into the SipHash state machine. - * It does not produce a final hash. You can call this function many times to - * append more data. To retrieve the final hash, call c_siphash_finalize(). - * - * Note that this implementation works best when used with chunk-sizes of - * multiples of 64bit (8-bytes). This is not a requirement, though. - */ -_c_public_ void c_siphash_append(CSipHash *state, const uint8_t *bytes, size_t n_bytes) { - const uint8_t *end = bytes + n_bytes; - size_t left = state->n_bytes & 7; - uint64_t m; - - state->n_bytes += n_bytes; - - /* - * SipHash operates on 64bit chunks. If the previous blob was not a - * multiple of 64bit in length, we must operate on single bytes. - */ - if (left > 0) { - for ( ; bytes < end && left < 8; ++bytes, ++left) - state->padding |= ((uint64_t) *bytes) << (left * 8); - - if (bytes == end && left < 8) - return; - - state->v3 ^= state->padding; - c_siphash_sipround(state); - c_siphash_sipround(state); - state->v0 ^= state->padding; - - state->padding = 0; - } - - end -= (state->n_bytes % sizeof(uint64_t)); - - /* - * We are now guaranteed to be at a 64bit state boundary. Hence, we can - * operate in 64bit chunks on all input. This is much faster than the - * one-byte-at-a-time loop. - */ - for ( ; bytes < end; bytes += 8) { - m = c_siphash_read_le64(bytes); - - state->v3 ^= m; - c_siphash_sipround(state); - c_siphash_sipround(state); - state->v0 ^= m; - } - - /* - * Now that we hashed as much 64bit chunks as possible, we need to - * remember the remaining trailing bytes. Keep them in @padding so the - * next round (or the finalizer) get access to them. - */ - left = state->n_bytes & 7; - switch (left) { - case 7: - state->padding |= ((uint64_t) bytes[6]) << 48; - /* fallthrough */ - case 6: - state->padding |= ((uint64_t) bytes[5]) << 40; - /* fallthrough */ - case 5: - state->padding |= ((uint64_t) bytes[4]) << 32; - /* fallthrough */ - case 4: - state->padding |= ((uint64_t) bytes[3]) << 24; - /* fallthrough */ - case 3: - state->padding |= ((uint64_t) bytes[2]) << 16; - /* fallthrough */ - case 2: - state->padding |= ((uint64_t) bytes[1]) << 8; - /* fallthrough */ - case 1: - state->padding |= ((uint64_t) bytes[0]); - /* fallthrough */ - case 0: - break; - } -} - -/** - * c_siphash_finalize() - finalize hash - * @state: context object - * - * This produces the final SipHash24 hash value for the given SipHash state. - * That is, it produces a hash value corresponding to the SipHash24 hash value - * of the concatenated byte-array passed into @state via c_siphash_append(). - * - * Note that @state has an invalid state after this function returns. To reuse - * it for another hash, you must call c_siphash_init() again. If you don't need - * the object, anymore, you can release it any time. There is no need to - * destroy the object explicitly. - * - * Return: 64bit hash value - */ -_c_public_ uint64_t c_siphash_finalize(CSipHash *state) { - uint64_t b; - - b = state->padding | (((uint64_t) state->n_bytes) << 56); - - state->v3 ^= b; - c_siphash_sipround(state); - c_siphash_sipround(state); - state->v0 ^= b; - - state->v2 ^= 0xff; - - c_siphash_sipround(state); - c_siphash_sipround(state); - c_siphash_sipround(state); - c_siphash_sipround(state); - - return state->v0 ^ state->v1 ^ state->v2 ^ state->v3; -} - -/** - * c_siphash_hash() - hash data blob - * @seed: 128bit seed - * @bytes: byte array to hash - * @n_bytes: number of bytes to hash - * - * This produces the SipHash24 hash value for the input @bytes / @n_bytes, - * using the seed provided as @seed. - * - * This is functionally equivalent to: - * - * CSipHash state; - * c_siphash_init(&state, seed); - * c_siphash_append(&state, bytes, n_bytes); - * return c_siphash_finalize(&state); - * - * Unlike the streaming API, this is a one-shot call suitable for any data that - * is available in-memory at the same time. - * - * Return: 64bit hash value - */ -_c_public_ uint64_t c_siphash_hash(const uint8_t seed[16], const uint8_t *bytes, size_t n_bytes) { - CSipHash state; - - c_siphash_init(&state, seed); - c_siphash_append(&state, bytes, n_bytes); - - return c_siphash_finalize(&state); -} diff --git a/shared/c-siphash/src/c-siphash.h b/shared/c-siphash/src/c-siphash.h deleted file mode 100644 index c0cfc1e0..00000000 --- a/shared/c-siphash/src/c-siphash.h +++ /dev/null @@ -1,60 +0,0 @@ -#pragma once - -/** - * Streaming-capable SipHash Implementation - * - * This library provides a SipHash API, that is fully implemented in ISO-C11 - * and has no external dependencies. The library performs no memory allocation, - * and provides a streaming API where data to be hashed can be appended - * piecemeal. - * - * A streaming-capable hash state is represented by the "CSipHash" structure, - * which should be initialized with a unique seed before use. If streaming - * capabilities are not required, c_siphash_hash() provides a simple one-shot - * API. - */ - -#ifdef __cplusplus -extern "C" { -#endif - -#include <stddef.h> -#include <stdint.h> - -typedef struct CSipHash CSipHash; - -/** - * struct CSipHash - SipHash state object - * @v0-@v3: internal state - * @padding: pending bytes that were not a multiple of 8 - * @n_bytes: number of hashed bytes - * - * The state of an inflight hash is represenetd by a CSipHash object. Before - * hashing, it must be initialized with c_siphash_init(), providing a unique - * random hash seed. Data is hashed by appending it to the state object, using - * c_siphash_append(). Finally, the hash is read out by calling - * c_siphash_finalize(). - * - * This state object has no allocated resources. It is safe to release its - * backing memory without any further action. - */ -struct CSipHash { - uint64_t v0; - uint64_t v1; - uint64_t v2; - uint64_t v3; - uint64_t padding; - size_t n_bytes; -}; - -#define C_SIPHASH_NULL {} - -void c_siphash_init(CSipHash *state, const uint8_t seed[16]); -void c_siphash_append(CSipHash *state, const uint8_t *bytes, size_t n_bytes); -uint64_t c_siphash_finalize(CSipHash *state); - -uint64_t c_siphash_hash(const uint8_t seed[16], const uint8_t *bytes, size_t n_bytes); - -#ifdef __cplusplus -} -#endif diff --git a/shared/c-stdaux/src/c-stdaux.h b/shared/c-stdaux/src/c-stdaux.h deleted file mode 100644 index 1cdbbbcf..00000000 --- a/shared/c-stdaux/src/c-stdaux.h +++ /dev/null @@ -1,550 +0,0 @@ -#pragma once - -/* - * Auxiliary macros and functions for the C standard library - * - * The `c-stdaux.h` header contains a collection of auxiliary macros and helper - * functions around the functionality provided by the different C standard - * library implementations, as well as other specifications implemented by - * them. - * - * Most of the helpers provided here provide aliases for common library and - * compiler features. Furthermore, several helpers simply provide other calling - * conventions than their standard counterparts (e.g., they allow for NULL to - * be passed with an object length of 0 where it makes sense to accept empty - * input). - * - * The namespace used by this project is: - * - * * `c_*` for all common C symbols or definitions that behave like proper C - * entities (e.g., macros that protect against double-evaluation would use - * lower-case names) - * - * * `C_*` for all constants, as well as macros that may not be safe against - * double evaluation. - */ - -#ifdef __cplusplus -extern "C" { -#endif - -#include <assert.h> -#include <dirent.h> -#include <errno.h> -#include <fcntl.h> -#include <inttypes.h> -#include <limits.h> -#include <stdalign.h> -#include <stdarg.h> -#if 0 /* NM_IGNORED */ -#include <stdatomic.h> -#endif /* NM_IGNORED */ -#include <stdbool.h> -#include <stddef.h> -#include <stdio.h> -#include <stdlib.h> -#include <stdnoreturn.h> -#include <string.h> -#include <sys/time.h> -#include <sys/types.h> -#include <time.h> -#include <unistd.h> - -/* - * Shortcuts for gcc attributes. See GCC manual for details. They're 1-to-1 - * mappings to the GCC equivalents. No additional magic here. They are - * supported by other compilers as well. - */ -#define _c_cleanup_(_x) __attribute__((__cleanup__(_x))) -#define _c_const_ __attribute__((__const__)) -#define _c_deprecated_ __attribute__((__deprecated__)) -#define _c_hidden_ __attribute__((__visibility__("hidden"))) -#define _c_likely_(_x) (__builtin_expect(!!(_x), 1)) -#define _c_packed_ __attribute__((__packed__)) -#define _c_printf_(_a, _b) __attribute__((__format__(printf, _a, _b))) -#define _c_public_ __attribute__((__visibility__("default"))) -#define _c_pure_ __attribute__((__pure__)) -#define _c_sentinel_ __attribute__((__sentinel__)) -#define _c_unlikely_(_x) (__builtin_expect(!!(_x), 0)) -#define _c_unused_ __attribute__((__unused__)) - -/** - * C_EXPR_ASSERT() - create expression with assertion - * @_expr: expression to evaluate to - * @_assertion: arbitrary assertion - * @_message: message associated with the assertion - * - * This macro simply evaluates to @_expr. That is, it can be used in any - * context that expects an expression like @_expr. Additionally, it takes an - * assertion as @_assertion and evaluates it through _Static_assert(), using - * @_message as debug message. - * - * The _Static_assert() builtin of C11 is defined as statement and thus cannot - * be used in expressions. This macro circumvents this restriction. - * - * Return: Evaluates to @_expr. - */ -#if defined(__COVERITY__) // Coverity cannot const-fold __builtin_choose_expr() -# define C_EXPR_ASSERT(_expr, _assertion, _message) (_expr) -#else -# define C_EXPR_ASSERT(_expr, _assertion, _message) \ - /* indentation and line-split to get better diagnostics */ \ - (__builtin_choose_expr( \ - !!(1 + 0 * sizeof( \ - struct { \ -_Static_assert(_assertion, _message); \ - } \ - )), \ - (_expr), \ - ((void)0) \ - )) -#endif - -/** - * C_STRINGIFY() - stringify a token, but evaluate it first - * @_x: token to evaluate and stringify - * - * Return: Evaluates to a constant string literal - */ -#define C_STRINGIFY(_x) C_INTERNAL_STRINGIFY(_x) -#define C_INTERNAL_STRINGIFY(_x) #_x - -/** - * C_CONCATENATE() - concatenate two tokens, but evaluate them first - * @_x: first token - * @_y: second token - * - * Return: Evaluates to a constant identifier - */ -#define C_CONCATENATE(_x, _y) C_INTERNAL_CONCATENATE(_x, _y) -#define C_INTERNAL_CONCATENATE(_x, _y) _x ## _y - -/** - * C_EXPAND() - expand a tuple to a series of its values - * @_x: tuple to expand - * - * Return: Evaluates to the expanded tuple - */ -#define C_EXPAND(_x) C_INTERNAL_EXPAND _x -#define C_INTERNAL_EXPAND(...) __VA_ARGS__ - -/** - * C_VAR() - generate unique variable name - * @_x: name of variable, optional - * @_uniq: unique prefix, usually provided by __COUNTER__, optional - * - * This macro shall be used to generate unique variable names, that will not be - * shadowed by recursive macro invocations. It is effectively a - * C_CONCATENATE of both arguments, but also provides a globally separated - * prefix and makes the code better readable. - * - * The second argument is optional. If not given, __LINE__ is implied, and as - * such the macro will generate the same identifier if used multiple times on - * the same code-line (or within a macro). This should be used if recursive - * calls into the macro are not expected. In fact, no argument is necessary in - * this case, as a mere `C_VAR` will evaluate to a valid variable name. - * - * This helper may be used by macro implementations that might reasonable well - * be called in a stacked fasion, like: - * - * c_max(foo, c_max(bar, baz)) - * - * Such a stacked call of c_max() might cause compiler warnings of shadowed - * variables in the definition of c_max(). By using C_VAR(), such warnings - * can be silenced as each evaluation of c_max() uses unique variable names. - * - * Return: This evaluates to a constant identifier. - */ -#define C_VAR(...) C_INTERNAL_VAR(__VA_ARGS__, 2, 1) -#define C_INTERNAL_VAR(_x, _uniq, _num, ...) C_VAR ## _num (_x, _uniq) -#define C_VAR1(_x, _unused) C_VAR2(_x, C_CONCATENATE(line, __LINE__)) -#define C_VAR2(_x, _uniq) C_CONCATENATE(c_internal_var_unique_, C_CONCATENATE(_uniq, _x)) - -/** - * C_CC_MACRO1() - provide safe environment to a macro - * @_call: macro to call - * @_x1: first argument - * @...: further arguments to forward unmodified to @_call - * - * This function simplifies the implementation of macros. Whenever you - * implement a macro, provide the internal macro name as @_call and its - * argument as @_x1. Inside of your internal macro, you... - * - * - ...are safe against multiple evaluation errors, since C_CC_MACRO1 will - * store the initial parameters in temporary variables. - * - * - ...support constant folding, as C_CC_MACRO1 takes care to invoke your - * macro with the original values, if they are compile-time constant. - * - * - ...have unique variable names for recursive callers and will not run into - * variable-shadowing-warnings accidentally. - * - * - ...have properly typed arguments as C_CC_MACRO1 stores the original - * arguments in an `__auto_type` temporary variable. - * - * Return: Result of @_call is returned. - */ -#define C_CC_MACRO1(_call, _x1, ...) C_INTERNAL_CC_MACRO1(_call, __COUNTER__, (_x1), ## __VA_ARGS__) -#define C_INTERNAL_CC_MACRO1(_call, _x1q, _x1, ...) \ - __builtin_choose_expr( \ - __builtin_constant_p(_x1), \ - _call(_x1, ## __VA_ARGS__), \ - __extension__ ({ \ - const __auto_type C_VAR(X1, _x1q) = (_x1); \ - _call(C_VAR(X1, _x1q), ## __VA_ARGS__); \ - })) - -/** - * C_CC_MACRO2() - provide safe environment to a macro - * @_call: macro to call - * @_x1: first argument - * @_x2: second argument - * @...: further arguments to forward unmodified to @_call - * - * This is the 2-argument equivalent of C_CC_MACRO1(). - * - * Return: Result of @_call is returned. - */ -#define C_CC_MACRO2(_call, _x1, _x2, ...) C_INTERNAL_CC_MACRO2(_call, __COUNTER__, (_x1), __COUNTER__, (_x2), ## __VA_ARGS__) -#define C_INTERNAL_CC_MACRO2(_call, _x1q, _x1, _x2q, _x2, ...) \ - __builtin_choose_expr( \ - (__builtin_constant_p(_x1) && __builtin_constant_p(_x2)), \ - _call((_x1), (_x2), ## __VA_ARGS__), \ - __extension__ ({ \ - const __auto_type C_VAR(X1, _x1q) = (_x1); \ - const __auto_type C_VAR(X2, _x2q) = (_x2); \ - _call(C_VAR(X1, _x1q), C_VAR(X2, _x2q), ## __VA_ARGS__); \ - })) - -/** - * C_CC_MACRO3() - provide safe environment to a macro - * @_call: macro to call - * @_x1: first argument - * @_x2: second argument - * @_x3: third argument - * @...: further arguments to forward unmodified to @_call - * - * This is the 3-argument equivalent of C_CC_MACRO1(). - * - * Return: Result of @_call is returned. - */ -#define C_CC_MACRO3(_call, _x1, _x2, _x3, ...) C_INTERNAL_CC_MACRO3(_call, __COUNTER__, (_x1), __COUNTER__, (_x2), __COUNTER__, (_x3), ## __VA_ARGS__) -#define C_INTERNAL_CC_MACRO3(_call, _x1q, _x1, _x2q, _x2, _x3q, _x3, ...) \ - __builtin_choose_expr( \ - (__builtin_constant_p(_x1) && __builtin_constant_p(_x2) && __builtin_constant_p(_x3)), \ - _call((_x1), (_x2), (_x3), ## __VA_ARGS__), \ - __extension__ ({ \ - const __auto_type C_VAR(X1, _x1q) = (_x1); \ - const __auto_type C_VAR(X2, _x2q) = (_x2); \ - const __auto_type C_VAR(X3, _x3q) = (_x3); \ - _call(C_VAR(X1, _x1q), C_VAR(X2, _x2q), C_VAR(X3, _x3q), ## __VA_ARGS__); \ - })) - -/** - * C_ARRAY_SIZE() - calculate number of array elements at compile time - * @_x: array to calculate size of - * - * Return: Evaluates to a constant integer expression. - */ -#define C_ARRAY_SIZE(_x) \ - C_EXPR_ASSERT(sizeof(_x) / sizeof((_x)[0]), \ - /* \ - * Verify that `_x' is an array, not a pointer. Rely on \ - * `&_x[0]' degrading arrays to pointers. \ - */ \ - !__builtin_types_compatible_p( \ - __typeof__(_x), \ - __typeof__(&(*(__typeof__(_x)*)0)[0]) \ - ), \ - "C_ARRAY_SIZE() called with non-array argument" \ - ) - -/** - * C_DECIMAL_MAX() - calculate maximum length of the decimal - * representation of an integer - * @_type: integer variable/type - * - * This calculates the bytes required for the decimal representation of an - * integer of the given type. It accounts for a possible +/- prefix, but it - * does *NOT* include the trailing terminating zero byte. - * - * Return: Evaluates to a constant integer expression - */ -#define C_DECIMAL_MAX(_arg) \ - (_Generic((__typeof__(_arg)){ 0 }, \ - char: C_INTERNAL_DECIMAL_MAX(sizeof(char)), \ - signed char: C_INTERNAL_DECIMAL_MAX(sizeof(signed char)), \ - unsigned char: C_INTERNAL_DECIMAL_MAX(sizeof(unsigned char)), \ - signed short: C_INTERNAL_DECIMAL_MAX(sizeof(signed short)), \ - unsigned short: C_INTERNAL_DECIMAL_MAX(sizeof(unsigned short)), \ - signed int: C_INTERNAL_DECIMAL_MAX(sizeof(signed int)), \ - unsigned int: C_INTERNAL_DECIMAL_MAX(sizeof(unsigned int)), \ - signed long: C_INTERNAL_DECIMAL_MAX(sizeof(signed long)), \ - unsigned long: C_INTERNAL_DECIMAL_MAX(sizeof(unsigned long)), \ - signed long long: C_INTERNAL_DECIMAL_MAX(sizeof(signed long long)), \ - unsigned long long: C_INTERNAL_DECIMAL_MAX(sizeof(unsigned long long)))) -#define C_INTERNAL_DECIMAL_MAX(_bytes) \ - C_EXPR_ASSERT( \ - 1 + ((_bytes) <= 1 ? 3 : \ - (_bytes) <= 2 ? 5 : \ - (_bytes) <= 4 ? 10 : \ - 20), \ - (_bytes) <= 8, \ - "Invalid use of C_INTERNAL_DECIMAL_MAX()" \ - ) - -/** - * c_container_of() - cast a member of a structure out to the containing structure - * @_ptr: pointer to the member or NULL - * @_type: type of the container struct this is embedded in - * @_member: name of the member within the struct - * - * This uses `offsetof(3)` to turn a pointer to a structure-member into a - * pointer to the surrounding structure. - * - * Return: Pointer to the surrounding object. - */ -#define c_container_of(_ptr, _type, _member) C_CC_MACRO1(C_CONTAINER_OF, (_ptr), _type, _member) -#define C_CONTAINER_OF(_ptr, _type, _member) \ - __extension__ ({ \ - /* trigger warning if types do not match */ \ - (void)(&((_type *)0)->_member == (_ptr)); \ - _ptr ? (_type*)( (char*)_ptr - offsetof(_type, _member) ) : NULL; \ - }) - -/** - * c_max() - compute maximum of two values - * @_a: value A - * @_b: value B - * - * Calculate the maximum of both passed values. Both arguments are evaluated - * exactly once, under all circumstances. Furthermore, if both values are - * constant expressions, the result will be constant as well. - * - * The comparison of their values is performed with the types given by the - * caller. It is the caller's responsibility to convert them to suitable types - * if necessary. - * - * Return: Maximum of both values is returned. - */ -#define c_max(_a, _b) C_CC_MACRO2(C_MAX, (_a), (_b)) -#define C_MAX(_a, _b) ((_a) > (_b) ? (_a) : (_b)) - -/** - * c_min() - compute minimum of two values - * @_a: value A - * @_b: value B - * - * Calculate the minimum of both passed values. Both arguments are evaluated - * exactly once, under all circumstances. Furthermore, if both values are - * constant expressions, the result will be constant as well. - * - * The comparison of their values is performed with the types given by the - * caller. It is the caller's responsibility to convert them to suitable types - * if necessary. - * - * Return: Minimum of both values is returned. - */ -#define c_min(_a, _b) C_CC_MACRO2(C_MIN, (_a), (_b)) -#define C_MIN(_a, _b) ((_a) < (_b) ? (_a) : (_b)) - -/** - * c_less_by() - calculate clamped difference of two values - * @_a: minuend - * @_b: subtrahend - * - * Calculate [_a - _b], but clamp the result to 0. Both arguments are evaluated - * exactly once, under all circumstances. Furthermore, if both values are - * constant expressions, the result will be constant as well. - * - * The comparison of their values is performed with the types given by the - * caller. It is the caller's responsibility to convert them to suitable types - * if necessary. - * - * Return: This computes [_a - _b], if [_a > _b]. Otherwise, 0 is returned. - */ -#define c_less_by(_a, _b) C_CC_MACRO2(C_LESS_BY, (_a), (_b)) -#define C_LESS_BY(_a, _b) ((_a) > (_b) ? (_a) - (_b) : 0) - -/** - * c_clamp() - clamp value to lower and upper boundary - * @_x: value to clamp - * @_low: lower boundary - * @_high: higher boundary - * - * This clamps @_x to the lower and higher bounds given as @_low and @_high. - * All arguments are evaluated exactly once, and yield a constant expression if - * all arguments are constant as well. - * - * The comparison of their values is performed with the types given by the - * caller. It is the caller's responsibility to convert them to suitable types - * if necessary. - * - * Return: Clamped integer value. - */ -#define c_clamp(_x, _low, _high) C_CC_MACRO3(C_CLAMP, (_x), (_low), (_high)) -#define C_CLAMP(_x, _low, _high) ((_x) > (_high) ? (_high) : (_x) < (_low) ? (_low) : (_x)) - -/** - * c_div_round_up() - calculate integer quotient but round up - * @_x: dividend - * @_y: divisor - * - * Calculates [x / y] but rounds up the result to the next integer. All - * arguments are evaluated exactly once, and yield a constant expression if all - * arguments are constant. - * - * Note: - * [(x + y - 1) / y] suffers from an integer overflow, even though the - * computation should be possible in the given type. Therefore, we use - * [x / y + !!(x % y)]. Note that on most CPUs a division returns both the - * quotient and the remainder, so both should be equally fast. Furthermore, if - * the divisor is a power of two, the compiler will optimize it, anyway. - * - * The operationsare performed with the types given by the caller. It is the - * caller's responsibility to convert the arguments to suitable types if - * necessary. - * - * Return: The quotient is returned. - */ -#define c_div_round_up(_x, _y) C_CC_MACRO2(C_DIV_ROUND_UP, (_x), (_y)) -#define C_DIV_ROUND_UP(_x, _y) ((_x) / (_y) + !!((_x) % (_y))) - -/** - * c_align_to() - align value to a multiple - * @_val: value to align - * @_to: align to multiple of this - * - * This aligns @_val to a multiple of @_to. If @_val is already a multiple of - * @_to, @_val is returned unchanged. This function operates within the - * boundaries of the type of @_val and @_to. Make sure to cast them if needed. - * - * The arguments of this macro are evaluated exactly once. If both arguments - * are a constant expression, this also yields a constant return value. - * - * Note that @_to must be a power of 2, otherwise the behavior will not match - * expectations. - * - * Return: @_val aligned to a multiple of @_to - */ -#define c_align_to(_val, _to) C_CC_MACRO2(C_ALIGN_TO, (_val), (_to)) -#define C_ALIGN_TO(_val, _to) (((_val) + (_to) - 1) & ~((_to) - 1)) - -/** - * c_assert() - runtime assertions - * @expr_result: result of an expression - * - * This function behaves like the standard `assert(3)` macro. That is, if - * `NDEBUG` is defined, it is a no-op. In all other cases it will assert that - * the result of the passed expression is true. - * - * Unlike the standard `assert(3)` macro, this function always evaluates its - * argument. This means side-effects will always be evaluated! However, if the - * macro is used with constant expressions, the compiler will be able to - * optimize it away. - */ -#define c_assert(_x) ({ \ - const _c_unused_ bool c_assert_result = (_x); \ - assert(c_assert_result && #_x); \ - }) - -/** - * c_errno() - return valid errno - * - * This helper should be used to shut up gcc if you know 'errno' is valid (ie., - * errno is > 0). Instead of "return -errno;", use - * "return -c_errno();" It will suppress bogus gcc warnings in case it assumes - * 'errno' might be 0 (or <0) and thus the caller's error-handling might not be - * triggered. - * - * This helper should be avoided whenever possible. However, occasionally we - * really want to shut up gcc (especially with static/inline functions). In - * those cases, gcc usually cannot deduce that some error paths are guaranteed - * to be taken. Hence, making the return value explicit allows gcc to better - * optimize the code. - * - * Note that you really should never use this helper to work around broken libc - * calls or syscalls, not setting 'errno' correctly. - * - * Return: Positive error code is returned. - */ -static inline int c_errno(void) { - return _c_likely_(errno > 0) ? errno : ENOTRECOVERABLE; -} - -/* - * Common Destructors - * - * Followingly, there're a bunch of common 'static inline' destructors, which - * simply call the function that they're named after, but return "INVALID" - * instead of "void". This allows direct assignment to any member-field and/or - * variable they're defined in, like: - * - * foo = c_free(foo); - * - * or - * - * foo->bar = c_close(foo->bar); - * - * Furthermore, all those destructors can be safely called with the "INVALID" - * value as argument, and they will be a no-op. - */ - -static inline void *c_free(void *p) { - free(p); - return NULL; -} - -static inline int c_close(int fd) { - if (fd >= 0) - close(fd); - return -1; -} - -static inline FILE *c_fclose(FILE *f) { - if (f) - fclose(f); - return NULL; -} - -static inline DIR *c_closedir(DIR *d) { - if (d) - closedir(d); - return NULL; -} - -/* - * Common Cleanup Helpers - * - * A bunch of _c_cleanup_(foobarp) helpers that are used all over the place. - * Note that all of those have the "if (IS_INVALID(foobar))" check inline, so - * compilers can optimize most of the cleanup-paths in a function. However, if - * the function they call already does this _inline_, then it might be skipped. - */ - -#define C_DEFINE_CLEANUP(_type, _func) \ - static inline void _func ## p(_type *p) { \ - if (*p) \ - _func(*p); \ - } struct c_internal_trailing_semicolon - -#define C_DEFINE_DIRECT_CLEANUP(_type, _func) \ - static inline void _func ## p(_type *p) { \ - _func(*p); \ - } struct c_internal_trailing_semicolon - -static inline void c_freep(void *p) { - /* - * `foobar **` does not coerce to `void **`, so we need `void *` as - * argument type, and then we dereference manually. - */ - c_free(*(void **)p); -} - -C_DEFINE_DIRECT_CLEANUP(int, c_close); -C_DEFINE_CLEANUP(FILE *, c_fclose); -C_DEFINE_CLEANUP(DIR *, c_closedir); - -#ifdef __cplusplus -} -#endif diff --git a/shared/meson.build b/shared/meson.build deleted file mode 100644 index 52eb6b9b..00000000 --- a/shared/meson.build +++ /dev/null @@ -1,298 +0,0 @@ -# SPDX-License-Identifier: LGPL-2.1-or-later - -shared_inc = include_directories('.') - -glib_nm_default_dep = declare_dependency( - include_directories: [ - top_inc, - shared_inc, - ], - dependencies: [ - glib_dep, - ], -) - -libc_siphash = static_library( - 'c-siphash', - sources: 'c-siphash/src/c-siphash.c', - include_directories: include_directories('c-stdaux/src'), -) - -libc_rbtree = static_library( - 'c-rbtree', - sources: 'c-rbtree/src/c-rbtree.c', - include_directories: include_directories('c-stdaux/src'), - c_args: '-std=c11', -) - -if enable_ebpf - n_acd_bpf_source = 'n-acd/src/n-acd-bpf.c' -else - n_acd_bpf_source = 'n-acd/src/n-acd-bpf-fallback.c' -endif - -libn_acd = static_library( - 'n-acd', - sources: files( - 'n-acd/src/n-acd.c', - 'n-acd/src/n-acd-probe.c', - 'n-acd/src/util/timer.c', - n_acd_bpf_source, - ), - include_directories: include_directories( - 'c-list/src', - 'c-rbtree/src', - 'c-siphash/src', - 'c-stdaux/src', - ), - c_args: [ - '-D_GNU_SOURCE', - '-DSO_ATTACH_BPF=50', - '-std=c11', - '-Wno-pointer-arith', - '-Wno-vla', - ], - link_with: [ - libc_rbtree, - libc_siphash, - ], -) - -libn_acd_dep = declare_dependency( - include_directories: shared_inc, - link_with: libn_acd, -) - -libn_dhcp4 = static_library( - 'n-dhcp4', - sources: files( - 'n-dhcp4/src/n-dhcp4-c-connection.c', - 'n-dhcp4/src/n-dhcp4-c-lease.c', - 'n-dhcp4/src/n-dhcp4-client.c', - 'n-dhcp4/src/n-dhcp4-c-probe.c', - 'n-dhcp4/src/n-dhcp4-incoming.c', - 'n-dhcp4/src/n-dhcp4-outgoing.c', - 'n-dhcp4/src/n-dhcp4-socket.c', - 'n-dhcp4/src/util/packet.c', - 'n-dhcp4/src/util/socket.c', - ), - c_args: [ - '-D_GNU_SOURCE', - '-Wno-declaration-after-statement', - '-Wno-pointer-arith', - ], - include_directories: include_directories( - 'c-list/src', - 'c-siphash/src', - 'c-stdaux/src', - ), - link_with: libc_siphash, -) - -libn_dhcp4_dep = declare_dependency( - include_directories: shared_inc, - link_with: libn_dhcp4, -) - -nm_test_utils_impl_source = files('nm-test-utils-impl.c') - -nm_vpn_plugin_utils_source = files('nm-utils/nm-vpn-plugin-utils.c') - -libnm_std_aux = static_library( - 'nm-std-aux', - sources: [ - 'nm-std-aux/c-list-util.c', - 'nm-std-aux/nm-std-utils.c', - ], - include_directories: top_inc, - c_args: [ - '-DG_LOG_DOMAIN="libnm"', - ], -) - -libnm_glib_aux = static_library( - 'nm-glib-aux', - sources: files( - 'nm-glib-aux/nm-dbus-aux.c', - 'nm-glib-aux/nm-dedup-multi.c', - 'nm-glib-aux/nm-enum-utils.c', - 'nm-glib-aux/nm-errno.c', - 'nm-glib-aux/nm-hash-utils.c', - 'nm-glib-aux/nm-io-utils.c', - 'nm-glib-aux/nm-json-aux.c', - 'nm-glib-aux/nm-keyfile-aux.c', - 'nm-glib-aux/nm-logging-base.c', - 'nm-glib-aux/nm-random-utils.c', - 'nm-glib-aux/nm-ref-string.c', - 'nm-glib-aux/nm-secret-utils.c', - 'nm-glib-aux/nm-shared-utils.c', - 'nm-glib-aux/nm-time-utils.c', - ), - dependencies: glib_nm_default_dep, - c_args: [ - '-DG_LOG_DOMAIN="libnm"', - ], - link_with: [ - libc_siphash, - libnm_std_aux, - ], -) - -libnm_glib_aux_dep = declare_dependency( - dependencies: glib_nm_default_dep, - link_with: libnm_glib_aux, -) - -libnm_udev_aux = static_library( - 'nm-udev-aux', - sources: 'nm-udev-aux/nm-udev-utils.c', - dependencies: [ - glib_nm_default_dep, - libudev_dep, - ], - c_args: [ - '-DG_LOG_DOMAIN="libnm"', - ], -) - -libnm_udev_aux_dep = declare_dependency( - include_directories: shared_inc, - link_with: libnm_udev_aux, -) - -libnm_base = static_library( - 'nm-base', - sources: files( - 'nm-base/nm-ethtool-base.c', - ), - dependencies: libnm_glib_aux_dep, - c_args: [ - '-DG_LOG_DOMAIN="libnm"', - ], -) - -libnm_base_dep = declare_dependency( - include_directories: shared_inc, - dependencies: libnm_glib_aux_dep, - link_with: libnm_base, -) - -libnm_log_core = static_library( - 'nm-log-core', - sources: 'nm-log-core/nm-logging.c', - dependencies: [ - glib_nm_default_dep, - libsystemd_dep, - ], - c_args: [ - '-DG_LOG_DOMAIN="NetworkManager"', - ], -) - -libnm_log_core_dep = declare_dependency( - include_directories: shared_inc, - dependencies: [ - libnm_glib_aux_dep, - ], - link_with: libnm_log_core, -) - -libnm_platform = static_library( - 'nm-platform', - sources: [ - 'nm-platform/nm-netlink.c', - 'nm-platform/nm-platform-utils.c', - 'nm-platform/nmp-netns.c', - ], - dependencies: [ - glib_nm_default_dep, - ], - c_args: [ - '-DG_LOG_DOMAIN="NetworkManager"', - ], -) - -libnm_platform_dep = declare_dependency( - include_directories: shared_inc, - dependencies: [ - libnm_glib_aux_dep, - ], - link_with: libnm_platform, -) - -libnm_systemd_shared = static_library( - 'nm-systemd-shared', - sources: files( - 'systemd/nm-sd-utils-shared.c', - 'systemd/src/basic/alloc-util.c', - 'systemd/src/basic/env-file.c', - 'systemd/src/basic/env-util.c', - 'systemd/src/basic/escape.c', - 'systemd/src/basic/ether-addr-util.c', - 'systemd/src/basic/extract-word.c', - 'systemd/src/basic/fd-util.c', - 'systemd/src/basic/fileio.c', - 'systemd/src/basic/format-util.c', - 'systemd/src/basic/fs-util.c', - 'systemd/src/basic/hash-funcs.c', - 'systemd/src/basic/hashmap.c', - 'systemd/src/basic/hexdecoct.c', - 'systemd/src/basic/hostname-util.c', - 'systemd/src/basic/in-addr-util.c', - 'systemd/src/basic/io-util.c', - 'systemd/src/basic/memory-util.c', - 'systemd/src/basic/mempool.c', - 'systemd/src/basic/parse-util.c', - 'systemd/src/basic/path-util.c', - 'systemd/src/basic/prioq.c', - 'systemd/src/basic/process-util.c', - 'systemd/src/basic/random-util.c', - 'systemd/src/basic/ratelimit.c', - 'systemd/src/basic/signal-util.c', - 'systemd/src/basic/socket-util.c', - 'systemd/src/basic/stat-util.c', - 'systemd/src/basic/string-table.c', - 'systemd/src/basic/string-util.c', - 'systemd/src/basic/strv.c', - 'systemd/src/basic/strxcpyx.c', - 'systemd/src/basic/time-util.c', - 'systemd/src/basic/tmpfile-util.c', - 'systemd/src/basic/utf8.c', - 'systemd/src/basic/util.c', - 'systemd/src/shared/dns-domain.c', - 'systemd/src/shared/web-util.c', - ), - include_directories: include_directories( - 'systemd/sd-adapt-shared', - 'systemd/src/basic', - 'systemd/src/shared', - ), - dependencies: glib_nm_default_dep, - c_args: [ - '-DG_LOG_DOMAIN="libnm"', - ], -) - -libnm_systemd_shared_dep = declare_dependency( - include_directories: include_directories( - 'systemd/sd-adapt-shared', - 'systemd/src/basic', - 'systemd/src/shared', - ), - dependencies: glib_dep, - link_with: libnm_systemd_shared, -) - -libnm_systemd_logging_stub = static_library( - 'nm-systemd-logging-stub', - sources: 'systemd/nm-logging-stub.c', - dependencies: glib_nm_default_dep, - c_args: [ - '-DG_LOG_DOMAIN="libnm"', - ], -) - -if enable_tests - subdir('nm-glib-aux/tests') - subdir('nm-platform/tests') -endif diff --git a/shared/n-acd/src/n-acd-bpf-fallback.c b/shared/n-acd/src/n-acd-bpf-fallback.c deleted file mode 100644 index 3cf4eb06..00000000 --- a/shared/n-acd/src/n-acd-bpf-fallback.c +++ /dev/null @@ -1,30 +0,0 @@ -/* - * A noop implementation of eBPF filter for IPv4 Address Conflict Detection - * - * These are a collection of dummy functions that have no effect, but allows - * n-acd to compile without eBPF support. - * - * See n-acd-bpf.c for documentation. - */ - -#include <c-stdaux.h> -#include <stddef.h> -#include "n-acd-private.h" - -int n_acd_bpf_map_create(int *mapfdp, size_t max_entries) { - *mapfdp = -1; - return 0; -} - -int n_acd_bpf_map_add(int mapfd, struct in_addr *addrp) { - return 0; -} - -int n_acd_bpf_map_remove(int mapfd, struct in_addr *addrp) { - return 0; -} - -int n_acd_bpf_compile(int *progfdp, int mapfd, struct ether_addr *macp) { - *progfdp = -1; - return 0; -} diff --git a/shared/n-acd/src/n-acd-bpf.c b/shared/n-acd/src/n-acd-bpf.c deleted file mode 100644 index 57b29ddf..00000000 --- a/shared/n-acd/src/n-acd-bpf.c +++ /dev/null @@ -1,317 +0,0 @@ -/* - * eBPF filter for IPv4 Address Conflict Detection - * - * An eBPF map and an eBPF program are provided. The map contains all the - * addresses address conflict detection is performed on, and the program - * filters out all packets except exactly the packets relevant to the ACD - * protocol on the addresses currently in the map. - * - * Note that userspace still has to filter the incoming packets, as filter - * are applied when packets are queued on the socket, not when userspace - * receives them. It is therefore possible to receive packets about addresses - * that have already been removed. - */ - -#include <c-stdaux.h> -#include <errno.h> -#include <inttypes.h> -#include <linux/bpf.h> -#include <netinet/if_ether.h> -#include <netinet/in.h> -#include <stdlib.h> -#include <string.h> -#include <sys/resource.h> -#include <sys/syscall.h> -#include <unistd.h> -#include "n-acd-private.h" - -#define BPF_LD_ABS(SIZE, IMM) \ - ((struct bpf_insn) { \ - .code = BPF_LD | BPF_SIZE(SIZE) | BPF_ABS, \ - .dst_reg = 0, \ - .src_reg = 0, \ - .off = 0, \ - .imm = IMM, \ - }) - -#define BPF_LDX_MEM(SIZE, DST, SRC, OFF) \ - ((struct bpf_insn) { \ - .code = BPF_LDX | BPF_SIZE(SIZE) | BPF_MEM, \ - .dst_reg = DST, \ - .src_reg = SRC, \ - .off = OFF, \ - .imm = 0, \ - }) - -#define BPF_LD_MAP_FD(DST, MAP_FD) \ - ((struct bpf_insn) { \ - .code = BPF_LD | BPF_DW | BPF_IMM, \ - .dst_reg = DST, \ - .src_reg = BPF_PSEUDO_MAP_FD, \ - .off = 0, \ - .imm = (__u32) (MAP_FD), \ - }), \ - ((struct bpf_insn) { \ - .code = 0, /* zero is reserved opcode */ \ - .dst_reg = 0, \ - .src_reg = 0, \ - .off = 0, \ - .imm = ((__u64) (MAP_FD)) >> 32, \ - }) - -#define BPF_ALU_REG(OP, DST, SRC) \ - ((struct bpf_insn) { \ - .code = BPF_ALU64 | BPF_OP(OP) | BPF_X, \ - .dst_reg = DST, \ - .src_reg = SRC, \ - .off = 0, \ - .imm = 0, \ - }) - -#define BPF_ALU_IMM(OP, DST, IMM) \ - ((struct bpf_insn) { \ - .code = BPF_ALU64 | BPF_OP(OP) | BPF_K, \ - .dst_reg = DST, \ - .src_reg = 0, \ - .off = 0, \ - .imm = IMM, \ - }) - -#define BPF_MOV_REG(DST, SRC) \ - ((struct bpf_insn) { \ - .code = BPF_ALU64 | BPF_MOV | BPF_X, \ - .dst_reg = DST, \ - .src_reg = SRC, \ - .off = 0, \ - .imm = 0, \ - }) - -#define BPF_MOV_IMM(DST, IMM) \ - ((struct bpf_insn) { \ - .code = BPF_ALU64 | BPF_MOV | BPF_K, \ - .dst_reg = DST, \ - .src_reg = 0, \ - .off = 0, \ - .imm = IMM, \ - }) - -#define BPF_STX_MEM(SIZE, DST, SRC, OFF) \ - ((struct bpf_insn) { \ - .code = BPF_STX | BPF_SIZE(SIZE) | BPF_MEM, \ - .dst_reg = DST, \ - .src_reg = SRC, \ - .off = OFF, \ - .imm = 0, \ - }) - -#define BPF_JMP_REG(OP, DST, SRC, OFF) \ - ((struct bpf_insn) { \ - .code = BPF_JMP | BPF_OP(OP) | BPF_X, \ - .dst_reg = DST, \ - .src_reg = SRC, \ - .off = OFF, \ - .imm = 0, \ - }) - -#define BPF_JMP_IMM(OP, DST, IMM, OFF) \ - ((struct bpf_insn) { \ - .code = BPF_JMP | BPF_OP(OP) | BPF_K, \ - .dst_reg = DST, \ - .src_reg = 0, \ - .off = OFF, \ - .imm = IMM, \ - }) - -#define BPF_EMIT_CALL(FUNC) \ - ((struct bpf_insn) { \ - .code = BPF_JMP | BPF_CALL, \ - .dst_reg = 0, \ - .src_reg = 0, \ - .off = 0, \ - .imm = FUNC, \ - }) - -#define BPF_EXIT_INSN() \ - ((struct bpf_insn) { \ - .code = BPF_JMP | BPF_EXIT, \ - .dst_reg = 0, \ - .src_reg = 0, \ - .off = 0, \ - .imm = 0, \ - }) - -static int n_acd_syscall_bpf(int cmd, union bpf_attr *attr, unsigned int size) { - return (int)syscall(__NR_bpf, cmd, attr, size); -} - -int n_acd_bpf_map_create(int *mapfdp, size_t max_entries) { - union bpf_attr attr; - int mapfd; - - memset(&attr, 0, sizeof(attr)); - attr = (union bpf_attr){ - .map_type = BPF_MAP_TYPE_HASH, - .key_size = sizeof(uint32_t), - .value_size = sizeof(uint8_t), /* values are never used, but must be set */ - .max_entries = max_entries, - }; - - mapfd = n_acd_syscall_bpf(BPF_MAP_CREATE, &attr, sizeof(attr)); - if (mapfd < 0) - return -errno; - - *mapfdp = mapfd; - return 0; -} - -int n_acd_bpf_map_add(int mapfd, struct in_addr *addrp) { - union bpf_attr attr; - uint32_t addr = be32toh(addrp->s_addr); - uint8_t _dummy = 0; - int r; - - memset(&attr, 0, sizeof(attr)); - attr = (union bpf_attr){ - .map_fd = mapfd, - .key = (uint64_t)(unsigned long)&addr, - .value = (uint64_t)(unsigned long)&_dummy, - .flags = BPF_NOEXIST, - }; - - r = n_acd_syscall_bpf(BPF_MAP_UPDATE_ELEM, &attr, sizeof(attr)); - if (r < 0) - return -errno; - - return 0; -} - -int n_acd_bpf_map_remove(int mapfd, struct in_addr *addrp) { - uint32_t addr = be32toh(addrp->s_addr); - union bpf_attr attr; - int r; - - memset(&attr, 0, sizeof(attr)); - attr = (union bpf_attr){ - .map_fd = mapfd, - .key = (uint64_t)(unsigned long)&addr, - }; - - r = n_acd_syscall_bpf(BPF_MAP_DELETE_ELEM, &attr, sizeof(attr)); - if (r < 0) - return -errno; - - return 0; -} - -int n_acd_bpf_compile(int *progfdp, int mapfd, struct ether_addr *macp) { - const union { - uint8_t u8[6]; - uint16_t u16[3]; - uint32_t u32[1]; - } mac = { - .u8 = { - macp->ether_addr_octet[0], - macp->ether_addr_octet[1], - macp->ether_addr_octet[2], - macp->ether_addr_octet[3], - macp->ether_addr_octet[4], - macp->ether_addr_octet[5], - }, - }; - struct bpf_insn prog[] = { - /* for using BPF_LD_ABS r6 must point to the skb, currently in r1 */ - BPF_MOV_REG(6, 1), /* r6 = r1 */ - - /* drop the packet if it is too short */ - BPF_LDX_MEM(BPF_W, 0, 6, offsetof(struct __sk_buff, len)), /* r0 = skb->len */ - BPF_JMP_IMM(BPF_JGE, 0, sizeof(struct ether_arp), 2), /* if (r0 >= sizeof(ether_arp)) skip 2 */ - BPF_MOV_IMM(0, 0), /* r0 = 0 */ - BPF_EXIT_INSN(), /* return */ - - /* drop the packet if the header is not as expected */ - BPF_LD_ABS(BPF_H, offsetof(struct ether_arp, arp_hrd)), /* r0 = header type */ - BPF_JMP_IMM(BPF_JEQ, 0, ARPHRD_ETHER, 2), /* if (r0 == ethernet) skip 2 */ - BPF_MOV_IMM(0, 0), /* r0 = 0 */ - BPF_EXIT_INSN(), /* return */ - - BPF_LD_ABS(BPF_H, offsetof(struct ether_arp, arp_pro)), /* r0 = protocol */ - BPF_JMP_IMM(BPF_JEQ, 0, ETHERTYPE_IP, 2), /* if (r0 == IP) skip 2 */ - BPF_MOV_IMM(0, 0), /* r0 = 0 */ - BPF_EXIT_INSN(), /* return */ - - BPF_LD_ABS(BPF_B, offsetof(struct ether_arp, arp_hln)), /* r0 = hw addr length */ - BPF_JMP_IMM(BPF_JEQ, 0, sizeof(struct ether_addr), 2), /* if (r0 == sizeof(ether_addr)) skip 2 */ - BPF_MOV_IMM(0, 0), /* r0 = 0 */ - BPF_EXIT_INSN(), /* return */ - - BPF_LD_ABS(BPF_B, offsetof(struct ether_arp, arp_pln)), /* r0 = protocol addr length */ - BPF_JMP_IMM(BPF_JEQ, 0, sizeof(struct in_addr), 2), /* if (r0 == sizeof(in_addr)) skip 2 */ - BPF_MOV_IMM(0, 0), /* r0 = 0 */ - BPF_EXIT_INSN(), /* return */ - - /* drop packets from our own mac address */ - BPF_LD_ABS(BPF_W, offsetof(struct ether_arp, arp_sha)), /* r0 = first four bytes of packet mac address */ - BPF_JMP_IMM(BPF_JNE, 0, be32toh(mac.u32[0]), 4), /* if (r0 != first four bytes of our mac address) skip 4 */ - BPF_LD_ABS(BPF_H, offsetof(struct ether_arp, arp_sha) + 4), /* r0 = last two bytes of packet mac address */ - BPF_JMP_IMM(BPF_JNE, 0, be16toh(mac.u16[2]), 2), /* if (r0 != last two bytes of our mac address) skip 2 */ - BPF_MOV_IMM(0, 0), /* r0 = 0 */ - BPF_EXIT_INSN(), /* return */ - - /* - * We listen for two kinds of packets: - * Conflicts) - * These are requests or replies with the sender address not set to INADDR_ANY. The - * conflicted address is the sender address, remember this in r7. - * Probes) - * These are requests with the sender address set to INADDR_ANY. The probed address - * is the target address, remember this in r7. - * Any other packets are dropped. - */ - BPF_LD_ABS(BPF_W, offsetof(struct ether_arp, arp_spa)), /* r0 = sender ip address */ - BPF_JMP_IMM(BPF_JEQ, 0, 0, 7), /* if (r0 == 0) skip 7 */ - BPF_MOV_REG(7, 0), /* r7 = r0 */ - BPF_LD_ABS(BPF_H, offsetof(struct ether_arp, arp_op)), /* r0 = operation */ - BPF_JMP_IMM(BPF_JEQ, 0, ARPOP_REQUEST, 3), /* if (r0 == request) skip 3 */ - BPF_JMP_IMM(BPF_JEQ, 0, ARPOP_REPLY, 2), /* if (r0 == reply) skip 2 */ - BPF_MOV_IMM(0, 0), /* r0 = 0 */ - BPF_EXIT_INSN(), /* return */ - BPF_JMP_IMM(BPF_JA, 0, 0, 6), /* skip 6 */ - BPF_LD_ABS(BPF_W, offsetof(struct ether_arp, arp_tpa)), /* r0 = target ip address */ - BPF_MOV_REG(7, 0), /* r7 = r0 */ - BPF_LD_ABS(BPF_H, offsetof(struct ether_arp, arp_op)), /* r0 = operation */ - BPF_JMP_IMM(BPF_JEQ, 0, ARPOP_REQUEST, 2), /* if (r0 == request) skip 2 */ - BPF_MOV_IMM(0, 0), /* r0 = 0 */ - BPF_EXIT_INSN(), /* return */ - - /* check if the probe or conflict is for an address we are monitoring */ - BPF_STX_MEM(BPF_W, 10, 7, -4), /* *(uint32_t*)fp - 4 = r7 */ - BPF_MOV_REG(2, 10), /* r2 = fp */ - BPF_ALU_IMM(BPF_ADD, 2, -4), /* r2 -= 4 */ - BPF_LD_MAP_FD(1, mapfd), /* r1 = mapfd */ - BPF_EMIT_CALL(BPF_FUNC_map_lookup_elem), /* r0 = map_lookup_elem(r1, r2) */ - BPF_JMP_IMM(BPF_JNE, 0, 0, 2), /* if (r0 != NULL) skip 2 */ - BPF_MOV_IMM(0, 0), /* r0 = 0 */ - BPF_EXIT_INSN(), /* return */ - - /* return exactly the packet length*/ - BPF_MOV_IMM(0, sizeof(struct ether_arp)), /* r0 = sizeof(struct ether_arp) */ - BPF_EXIT_INSN(), /* return */ - }; - union bpf_attr attr; - int progfd; - - memset(&attr, 0, sizeof(attr)); - attr = (union bpf_attr){ - .prog_type = BPF_PROG_TYPE_SOCKET_FILTER, - .insns = (uint64_t)(unsigned long)prog, - .insn_cnt = sizeof(prog) / sizeof(*prog), - .license = (uint64_t)(unsigned long)"ASL", - }; - - progfd = n_acd_syscall_bpf(BPF_PROG_LOAD, &attr, sizeof(attr)); - if (progfd < 0) - return -errno; - - *progfdp = progfd; - return 0; -} diff --git a/shared/n-acd/src/n-acd-private.h b/shared/n-acd/src/n-acd-private.h deleted file mode 100644 index 4583c018..00000000 --- a/shared/n-acd/src/n-acd-private.h +++ /dev/null @@ -1,154 +0,0 @@ -#pragma once - -#include <c-list.h> -#include <c-rbtree.h> -#include <c-stdaux.h> -#include <errno.h> -#include <inttypes.h> -#include <netinet/if_ether.h> -#include <netinet/in.h> -#include <stdbool.h> -#include <stdlib.h> -#include "util/timer.h" -#include "n-acd.h" - -typedef struct NAcdEventNode NAcdEventNode; - -/* This augments the error-codes with internal ones that are never exposed. */ -enum { - _N_ACD_INTERNAL = _N_ACD_E_N, - - N_ACD_E_DROPPED, -}; - -enum { - N_ACD_PROBE_STATE_PROBING, - N_ACD_PROBE_STATE_CONFIGURING, - N_ACD_PROBE_STATE_ANNOUNCING, - N_ACD_PROBE_STATE_FAILED, -}; - -struct NAcdConfig { - int ifindex; - unsigned int transport; - uint8_t mac[ETH_ALEN]; - size_t n_mac; -}; - -#define N_ACD_CONFIG_NULL(_x) { \ - .transport = _N_ACD_TRANSPORT_N, \ - } - -struct NAcdProbeConfig { - struct in_addr ip; - uint64_t timeout_msecs; -}; - -#define N_ACD_PROBE_CONFIG_NULL(_x) { \ - .timeout_msecs = N_ACD_TIMEOUT_RFC5227, \ - } - -struct NAcdEventNode { - CList acd_link; - CList probe_link; - NAcdEvent event; - uint8_t sender[ETH_ALEN]; - bool is_public : 1; -}; - -#define N_ACD_EVENT_NODE_NULL(_x) { \ - .acd_link = C_LIST_INIT((_x).acd_link), \ - .probe_link = C_LIST_INIT((_x).probe_link), \ - } - -struct NAcd { - unsigned long n_refs; - unsigned int seed; - int fd_epoll; - int fd_socket; - CRBTree ip_tree; - CList event_list; - Timer timer; - - /* BPF map */ - int fd_bpf_map; - size_t n_bpf_map; - size_t max_bpf_map; - - /* configuration */ - int ifindex; - uint8_t mac[ETH_ALEN]; - - /* flags */ - bool preempted : 1; -}; - -#define N_ACD_NULL(_x) { \ - .n_refs = 1, \ - .fd_epoll = -1, \ - .fd_socket = -1, \ - .ip_tree = C_RBTREE_INIT, \ - .event_list = C_LIST_INIT((_x).event_list), \ - .timer = TIMER_NULL((_x).timer), \ - .fd_bpf_map = -1, \ - } - -struct NAcdProbe { - NAcd *acd; - CRBNode ip_node; - CList event_list; - Timeout timeout; - - /* configuration */ - struct in_addr ip; - uint64_t timeout_multiplier; - void *userdata; - - /* state */ - unsigned int state; - unsigned int n_iteration; - unsigned int defend; - uint64_t last_defend; -}; - -#define N_ACD_PROBE_NULL(_x) { \ - .ip_node = C_RBNODE_INIT((_x).ip_node), \ - .event_list = C_LIST_INIT((_x).event_list), \ - .timeout = TIMEOUT_INIT((_x).timeout), \ - .state = N_ACD_PROBE_STATE_PROBING, \ - .defend = N_ACD_DEFEND_NEVER, \ - } - -/* events */ - -int n_acd_event_node_new(NAcdEventNode **nodep); -NAcdEventNode *n_acd_event_node_free(NAcdEventNode *node); - -/* contexts */ - -void n_acd_remember(NAcd *acd, uint64_t now, bool success); -int n_acd_raise(NAcd *acd, NAcdEventNode **nodep, unsigned int event); -int n_acd_send(NAcd *acd, const struct in_addr *tpa, const struct in_addr *spa); -int n_acd_ensure_bpf_map_space(NAcd *acd); - -/* probes */ - -int n_acd_probe_new(NAcdProbe **probep, NAcd *acd, NAcdProbeConfig *config); -int n_acd_probe_raise(NAcdProbe *probe, NAcdEventNode **nodep, unsigned int event); -int n_acd_probe_handle_timeout(NAcdProbe *probe); -int n_acd_probe_handle_packet(NAcdProbe *probe, struct ether_arp *packet, bool hard_conflict); - -/* eBPF */ - -int n_acd_bpf_map_create(int *mapfdp, size_t max_elements); -int n_acd_bpf_map_add(int mapfd, struct in_addr *addr); -int n_acd_bpf_map_remove(int mapfd, struct in_addr *addr); - -int n_acd_bpf_compile(int *progfdp, int mapfd, struct ether_addr *mac); - -/* inline helpers */ - -static inline void n_acd_event_node_freep(NAcdEventNode **node) { - if (*node) - n_acd_event_node_free(*node); -} diff --git a/shared/n-acd/src/n-acd-probe.c b/shared/n-acd/src/n-acd-probe.c deleted file mode 100644 index 2a5c6451..00000000 --- a/shared/n-acd/src/n-acd-probe.c +++ /dev/null @@ -1,712 +0,0 @@ -/* - * IPv4 Address Conflict Detection - * - * This file implements the probe object. A probe is basically the - * state-machine of a single ACD run. It takes an address to probe for, checks - * for conflicts and then defends it once configured. - */ - -#include <assert.h> -#include <c-rbtree.h> -#include <c-stdaux.h> -#include <endian.h> -#include <errno.h> -#include <inttypes.h> -#include <limits.h> -#include <netinet/if_ether.h> -#include <netinet/in.h> -#include <stdio.h> -#include <stdlib.h> -#include <string.h> -#include <sys/types.h> -#include <unistd.h> -#include "n-acd.h" -#include "n-acd-private.h" - -/* - * These parameters and timing intervals are specified in RFC-5227. The - * original values are: - * - * PROBE_NUM 3 - * PROBE_WAIT 1s - * PROBE_MIN 1s - * PROBE_MAX 3s - * ANNOUNCE_NUM 3 - * ANNOUNCE_WAIT 2s - * ANNOUNCE_INTERVAL 2s - * MAX_CONFLICTS 10 - * RATE_LIMIT_INTERVAL 60s - * DEFEND_INTERVAL 10s - * - * If we assume a best-case and worst-case scenario for non-conflicted runs, we - * end up with a runtime between 4s and 9s to finish the probe. Then it still - * takes a fixed 4s to finish the announcements. - * - * RFC 5227 section 1.1: - * [...] (Note that the values listed here are fixed constants; they are - * not intended to be modifiable by implementers, operators, or end users. - * These constants are given symbolic names here to facilitate the writing - * of future standards that may want to reference this document with - * different values for these named constants; however, at the present time - * no such future standards exist.) [...] - * - * Unfortunately, no-one ever stepped up to write a "future standard" to revise - * the timings. A 9s timeout for successful link setups is not acceptable today. - * Hence, we will just go forward and ignore the proposed values. On both - * wired and wireless local links round-trip latencies of below 3ms are common. - * We require the caller to set a timeout multiplier, where 1 corresponds to a - * total probe time between 0.5 ms and 1.0 ms. On modern networks a multiplier - * of about 100 should be a reasonable default. To comply with the RFC select a - * multiplier of 9000. - */ -#define N_ACD_RFC_PROBE_NUM (3) -#define N_ACD_RFC_PROBE_WAIT_NSEC (UINT64_C(111111)) /* 1/9 ms */ -#define N_ACD_RFC_PROBE_MIN_NSEC (UINT64_C(111111)) /* 1/9 ms */ -#define N_ACD_RFC_PROBE_MAX_NSEC (UINT64_C(333333)) /* 3/9 ms */ -#define N_ACD_RFC_ANNOUNCE_NUM (3) -#define N_ACD_RFC_ANNOUNCE_WAIT_NSEC (UINT64_C(222222)) /* 2/9 ms */ -#define N_ACD_RFC_ANNOUNCE_INTERVAL_NSEC (UINT64_C(222222)) /* 2/9 ms */ -#define N_ACD_RFC_MAX_CONFLICTS (10) -#define N_ACD_RFC_RATE_LIMIT_INTERVAL_NSEC (UINT64_C(60000000000)) /* 60s */ -#define N_ACD_RFC_DEFEND_INTERVAL_NSEC (UINT64_C(10000000000)) /* 10s */ - -/** - * n_acd_probe_config_new() - create probe configuration - * @configp: output argument for new probe configuration - * - * This creates a new probe configuration. It will be returned in @configp to - * the caller, which upon return fully owns the object. - * - * A probe configuration collects parameters for probes. It never validates the - * input, but this is left to the consumer of the configuration to do. - * - * Return: 0 on success, negative error code on failure. - */ -_c_public_ int n_acd_probe_config_new(NAcdProbeConfig **configp) { - _c_cleanup_(n_acd_probe_config_freep) NAcdProbeConfig *config = NULL; - - config = malloc(sizeof(*config)); - if (!config) - return -ENOMEM; - - *config = (NAcdProbeConfig)N_ACD_PROBE_CONFIG_NULL(*config); - - *configp = config; - config = NULL; - return 0; -} - -/** - * n_acd_probe_config_free() - destroy probe configuration - * @config: configuration to operate on, or NULL - * - * This destroys the probe configuration and all associated objects. If @config - * is NULL, this is a no-op. - * - * Return: NULL is returned. - */ -_c_public_ NAcdProbeConfig *n_acd_probe_config_free(NAcdProbeConfig *config) { - if (!config) - return NULL; - - free(config); - - return NULL; -} - -/** - * n_acd_probe_config_set_ip() - set ip property - * @config: configuration to operate on - * @ip: ip to set - * - * This sets the IP property to the value `ip`. The address is copied into the - * configuration object. No validation is performed. - * - * The IP property selects the IP address that a probe checks for. It is the - * caller's responsibility to guarantee the address is valid and can be used. - */ -_c_public_ void n_acd_probe_config_set_ip(NAcdProbeConfig *config, struct in_addr ip) { - config->ip = ip; -} - -/** - * n_acd_probe_config_set_timeout() - set timeout property - * @config: configuration to operate on - * @msecs: timeout to set, in milliseconds - * - * This sets the timeout to use for a conflict detection probe. The - * specification default is provided as `N_ACD_TIMEOUT_RFC5227` and corresponds - * to 9 seconds. - * - * If set to 0, conflict detection is skipped and the address is immediately - * advertised and defended. - * - * Depending on the transport used, the API user should select a suitable - * timeout. Since `ACD` only operates on the link layer, timeouts in the - * hundreds of milliseconds range should be more than enough for any modern - * network. Note that increasing this value directly affects the time it takes - * to connect to a network, since an address should not be used unless conflict - * detection finishes. - * - * Using the specification default is **discouraged**. It is way too slow and - * not appropriate for modern networks. - * - * Default value is `N_ACD_TIMEOUT_RFC5227`. - */ -_c_public_ void n_acd_probe_config_set_timeout(NAcdProbeConfig *config, uint64_t msecs) { - config->timeout_msecs = msecs; -} - -static void n_acd_probe_schedule(NAcdProbe *probe, uint64_t n_timeout, unsigned int n_jitter) { - uint64_t n_time; - - timer_now(&probe->acd->timer, &n_time); - n_time += n_timeout; - - /* - * ACD specifies jitter values to reduce packet storms on the local - * link. This call accepts the maximum relative jitter value in - * nanoseconds as @n_jitter. We then use rand_r(3p) to get a - * pseudo-random jitter on top of the real timeout given as @n_timeout. - */ - if (n_jitter) { - uint64_t random; - - random = ((uint64_t)rand_r(&probe->acd->seed) << 32) | (uint64_t)rand_r(&probe->acd->seed); - n_time += random % n_jitter; - } - - timeout_schedule(&probe->timeout, &probe->acd->timer, n_time); -} - -static void n_acd_probe_unschedule(NAcdProbe *probe) { - timeout_unschedule(&probe->timeout); -} - -static bool n_acd_probe_is_unique(NAcdProbe *probe) { - NAcdProbe *sibling; - - if (!c_rbnode_is_linked(&probe->ip_node)) - return false; - - sibling = c_rbnode_entry(c_rbnode_next(&probe->ip_node), NAcdProbe, ip_node); - if (sibling && sibling->ip.s_addr == probe->ip.s_addr) - return false; - - sibling = c_rbnode_entry(c_rbnode_prev(&probe->ip_node), NAcdProbe, ip_node); - if (sibling && sibling->ip.s_addr == probe->ip.s_addr) - return false; - - return true; -} - -static int n_acd_probe_link(NAcdProbe *probe) { - int r; - - /* - * Make sure the kernel bpf map has space for at least one more - * entry. - */ - r = n_acd_ensure_bpf_map_space(probe->acd); - if (r) - return r; - - /* - * Link entry into context, indexed by its IP. Note that we allow - * duplicates just fine. It is up to you to decide whether to avoid - * duplicates, if you don't want them. Duplicates on the same context - * do not conflict with each other, though. - */ - { - CRBNode **slot, *parent; - NAcdProbe *other; - - slot = &probe->acd->ip_tree.root; - parent = NULL; - while (*slot) { - other = c_rbnode_entry(*slot, NAcdProbe, ip_node); - parent = *slot; - if (probe->ip.s_addr < other->ip.s_addr) - slot = &(*slot)->left; - else - slot = &(*slot)->right; - } - - c_rbtree_add(&probe->acd->ip_tree, parent, slot, &probe->ip_node); - } - - /* - * Add the ip address to the map, if it is not already there. - */ - if (n_acd_probe_is_unique(probe)) { - r = n_acd_bpf_map_add(probe->acd->fd_bpf_map, &probe->ip); - if (r) { - /* - * Make sure the IP address is linked in userspace iff - * it is linked in the kernel. - */ - c_rbnode_unlink(&probe->ip_node); - return r; - } - ++probe->acd->n_bpf_map; - } - - return 0; -} - -static void n_acd_probe_unlink(NAcdProbe *probe) { - int r; - - /* - * If this is the only probe for a given IP, remove the IP from the - * kernel BPF map. - */ - if (n_acd_probe_is_unique(probe)) { - r = n_acd_bpf_map_remove(probe->acd->fd_bpf_map, &probe->ip); - c_assert(r >= 0); - --probe->acd->n_bpf_map; - } - c_rbnode_unlink(&probe->ip_node); -} - -int n_acd_probe_new(NAcdProbe **probep, NAcd *acd, NAcdProbeConfig *config) { - _c_cleanup_(n_acd_probe_freep) NAcdProbe *probe = NULL; - int r; - - if (!config->ip.s_addr) - return N_ACD_E_INVALID_ARGUMENT; - - probe = malloc(sizeof(*probe)); - if (!probe) - return -ENOMEM; - - *probe = (NAcdProbe)N_ACD_PROBE_NULL(*probe); - probe->acd = n_acd_ref(acd); - probe->ip = config->ip; - - /* - * We use the provided timeout-length as multiplier for all our - * timeouts. The provided timeout defines the maximum length of an - * entire probe-interval until the first announcement. Given the - * spec-provided parameters, this ends up as: - * - * PROBE_WAIT + PROBE_MAX + PROBE_MAX + ANNOUNCE_WAIT - * = 1s + 3s + 3s + 2s - * = 9s - * - * Hence, the default value for this timeout is 9000ms, which just - * ends up matching the spec-provided values. - * - * What we now semantically do is divide this timeout by 1ns/1000000. - * This first turns it into nanoseconds, then strips the unit by - * turning it into a multiplier. However, rather than performing the - * division here, we multiplier all our timeouts by 1000000 statically - * at compile time. Therefore, we can use the user-provided timeout as - * unmodified multiplier. No conversion necessary. - */ - probe->timeout_multiplier = config->timeout_msecs; - - r = n_acd_probe_link(probe); - if (r) - return r; - - /* - * Now that everything is set up, we have to send the first probe. This - * is done after ~PROBE_WAIT seconds, hence we schedule our timer. - * In case no timeout-multiplier is set, we pretend we already sent all - * probes successfully and schedule the timer so we proceed with the - * announcements. We must schedule a fake timer there, since we are not - * allowed to advance the state machine outside of n_acd_dispatch(). - */ - if (probe->timeout_multiplier) { - probe->n_iteration = 0; - n_acd_probe_schedule(probe, - 0, - probe->timeout_multiplier * N_ACD_RFC_PROBE_WAIT_NSEC); - } else { - probe->n_iteration = N_ACD_RFC_PROBE_NUM; - n_acd_probe_schedule(probe, 0, 0); - } - - *probep = probe; - probe = NULL; - return 0; -} - -/** - * n_acd_probe_free() - destroy a probe - * @probe: probe to operate on, or NULL - * - * This destroys the probe specified by @probe. All operations are immediately - * ceded and all associated objects are released. - * - * If @probe is NULL, this is a no-op. - * - * This function will flush all events associated with @probe from the event - * queue. That is, no events will be returned for this @probe anymore. - * - * Return: NULL is returned. - */ -_c_public_ NAcdProbe *n_acd_probe_free(NAcdProbe *probe) { - NAcdEventNode *node, *t_node; - - if (!probe) - return NULL; - - c_list_for_each_entry_safe(node, t_node, &probe->event_list, probe_link) - n_acd_event_node_free(node); - - n_acd_probe_unschedule(probe); - n_acd_probe_unlink(probe); - probe->acd = n_acd_unref(probe->acd); - free(probe); - - return NULL; -} - -int n_acd_probe_raise(NAcdProbe *probe, NAcdEventNode **nodep, unsigned int event) { - _c_cleanup_(n_acd_event_node_freep) NAcdEventNode *node = NULL; - int r; - - r = n_acd_raise(probe->acd, &node, event); - if (r) - return r; - - switch (event) { - case N_ACD_EVENT_READY: - node->event.ready.probe = probe; - break; - case N_ACD_EVENT_USED: - node->event.used.probe = probe; - break; - case N_ACD_EVENT_DEFENDED: - node->event.defended.probe = probe; - break; - case N_ACD_EVENT_CONFLICT: - node->event.conflict.probe = probe; - break; - default: - c_assert(0); - return -ENOTRECOVERABLE; - } - - c_list_link_tail(&probe->event_list, &node->probe_link); - - if (nodep) - *nodep = node; - node = NULL; - return 0; -} - -int n_acd_probe_handle_timeout(NAcdProbe *probe) { - int r; - - switch (probe->state) { - case N_ACD_PROBE_STATE_PROBING: - /* - * We are still PROBING. We send 3 probes with a random timeout - * scheduled between each. If, after a fixed timeout, we did - * not receive any conflict we consider the probing successful. - */ - if (probe->n_iteration < N_ACD_RFC_PROBE_NUM) { - /* - * We have not sent all 3 probes, yet. A timer fired, - * so we are ready to send the next probe. If this is - * the third probe, schedule a timer for ANNOUNCE_WAIT - * to give other peers a chance to answer. If this is - * not the third probe, wait between PROBE_MIN and - * PROBE_MAX for the next probe. - */ - - r = n_acd_send(probe->acd, &probe->ip, NULL); - if (r) { - if (r != -N_ACD_E_DROPPED) - return r; - - /* - * Packet was dropped, and we know about it. It - * never reached the network. Reasons are - * manifold, and n_acd_send() raises events if - * necessary. - * From a probe-perspective, we simply pretend - * we never sent the probe and schedule a - * timeout for the next probe, effectively - * doubling a single probe-interval. - */ - } else { - /* Successfully sent, so advance counter. */ - ++probe->n_iteration; - } - - if (probe->n_iteration < N_ACD_RFC_PROBE_NUM) - n_acd_probe_schedule(probe, - probe->timeout_multiplier * N_ACD_RFC_PROBE_MIN_NSEC, - probe->timeout_multiplier * (N_ACD_RFC_PROBE_MAX_NSEC - N_ACD_RFC_PROBE_MIN_NSEC)); - else - n_acd_probe_schedule(probe, - probe->timeout_multiplier * N_ACD_RFC_ANNOUNCE_WAIT_NSEC, - 0); - } else { - /* - * All 3 probes succeeded and we waited enough to - * consider this address usable by now. Do not announce - * the address, yet. We must first give the caller a - * chance to configure the address (so they can answer - * ARP requests), before announcing it. - */ - r = n_acd_probe_raise(probe, NULL, N_ACD_EVENT_READY); - if (r) - return r; - - probe->state = N_ACD_PROBE_STATE_CONFIGURING; - } - - break; - - case N_ACD_PROBE_STATE_ANNOUNCING: - /* - * We are ANNOUNCING, meaning the caller configured the address - * on the interface and is actively using it. We send 3 - * announcements out, in a short interval, and then just - * perform passive conflict detection. - * Note that once all 3 announcements are sent, we no longer - * schedule a timer, so this part should not trigger, anymore. - */ - - r = n_acd_send(probe->acd, &probe->ip, &probe->ip); - if (r) { - if (r != -N_ACD_E_DROPPED) - return r; - - /* - * See above in STATE_PROBING for details. We know the - * packet was never sent, so we simply try again after - * extending the timer. - */ - } else { - /* Successfully sent, so advance counter. */ - ++probe->n_iteration; - } - - if (probe->n_iteration < N_ACD_RFC_ANNOUNCE_NUM) { - /* - * Announcements are always scheduled according to the - * time-intervals specified in the spec. We always use - * the RFC5227-mandated multiplier. - * If you reconsider this, note that timeout_multiplier - * might be 0 here. - */ - n_acd_probe_schedule(probe, - N_ACD_TIMEOUT_RFC5227 * N_ACD_RFC_ANNOUNCE_INTERVAL_NSEC, - 0); - } - - break; - - case N_ACD_PROBE_STATE_CONFIGURING: - case N_ACD_PROBE_STATE_FAILED: - default: - /* - * There are no timeouts in these states. If we trigger one, - * something is fishy. - */ - c_assert(0); - return -ENOTRECOVERABLE; - } - - return 0; -} - -int n_acd_probe_handle_packet(NAcdProbe *probe, struct ether_arp *packet, bool hard_conflict) { - NAcdEventNode *node; - uint64_t now; - int r; - - timer_now(&probe->acd->timer, &now); - - switch (probe->state) { - case N_ACD_PROBE_STATE_PROBING: - /* - * Regardless whether this is a hard or soft conflict, we must - * treat this as a probe failure. That is, notify the caller of - * the conflict and wait for further instructions. We do not - * react to this, until the caller tells us what to do, but we - * do stop sending further probes. - */ - r = n_acd_probe_raise(probe, &node, N_ACD_EVENT_USED); - if (r) - return r; - - node->event.used.sender = node->sender; - node->event.used.n_sender = ETH_ALEN; - memcpy(node->sender, packet->arp_sha, ETH_ALEN); - - n_acd_probe_unschedule(probe); - n_acd_probe_unlink(probe); - probe->state = N_ACD_PROBE_STATE_FAILED; - - break; - - case N_ACD_PROBE_STATE_CONFIGURING: - /* - * We are waiting for the caller to configure the interface and - * start ANNOUNCING. In this state, we cannot defend the - * address as that would indicate that it is ready to be used, - * and we cannot signal CONFLICT or USED as the caller may - * already have started to use the address (and may have - * configured the engine to always defend it, which means they - * should be able to rely on never losing it after READY). - * Simply drop the event, and rely on the anticipated ANNOUNCE - * to trigger it again. - */ - - break; - - case N_ACD_PROBE_STATE_ANNOUNCING: { - /* - * We were already instructed to announce the address, which - * means the address is configured and in use. Hence, the - * caller is responsible to serve regular ARP queries. Meaning, - * we can ignore any soft conflicts (other peers doing ACD). - * - * But if we see a hard-conflict, we either defend the address - * according to the caller's instructions, or we report the - * conflict and bail out. - */ - bool conflict = false, rate_limited = false; - - if (!hard_conflict) - break; - - rate_limited = now < probe->last_defend + N_ACD_RFC_DEFEND_INTERVAL_NSEC; - - switch (probe->defend) { - case N_ACD_DEFEND_NEVER: - conflict = true; - break; - case N_ACD_DEFEND_ONCE: - if (rate_limited) { - conflict = true; - break; - } - - /* fallthrough */ - case N_ACD_DEFEND_ALWAYS: - if (!rate_limited) { - r = n_acd_send(probe->acd, &probe->ip, &probe->ip); - if (r) { - if (r != -N_ACD_E_DROPPED) - return r; - - if (probe->defend == N_ACD_DEFEND_ONCE) { - conflict = true; - break; - } - } - - if (r != -N_ACD_E_DROPPED) - probe->last_defend = now; - } - - r = n_acd_probe_raise(probe, &node, N_ACD_EVENT_DEFENDED); - if (r) - return r; - - node->event.defended.sender = node->sender; - node->event.defended.n_sender = ETH_ALEN; - memcpy(node->sender, packet->arp_sha, ETH_ALEN); - - break; - } - - if (conflict) { - r = n_acd_probe_raise(probe, &node, N_ACD_EVENT_CONFLICT); - if (r) - return r; - - node->event.conflict.sender = node->sender; - node->event.conflict.n_sender = ETH_ALEN; - memcpy(node->sender, packet->arp_sha, ETH_ALEN); - - n_acd_probe_unschedule(probe); - n_acd_probe_unlink(probe); - probe->state = N_ACD_PROBE_STATE_FAILED; - } - - break; - } - - case N_ACD_PROBE_STATE_FAILED: - default: - /* - * We are not listening for packets in these states. If we receive one, - * something is fishy. - */ - c_assert(0); - return -ENOTRECOVERABLE; - } - - return 0; -} - -/** - * n_acd_probe_set_userdata - set userdata - * @probe: probe to operate on - * @userdata: userdata pointer - * - * This can be used to set a caller-controlled user-data pointer on @probe. The - * value of the pointer is never inspected or used by `n-acd` and is fully - * under control of the caller. - * - * The default value is NULL. - */ -_c_public_ void n_acd_probe_set_userdata(NAcdProbe *probe, void *userdata) { - probe->userdata = userdata; -} - -/** - * n_acd_probe_get_userdata - get userdata - * @probe: probe to operate on - * - * This queries the userdata pointer that was previously set through - * n_acd_probe_set_userdata(). - * - * The default value is NULL. - * - * Return: The stored userdata pointer is returned. - */ -_c_public_ void n_acd_probe_get_userdata(NAcdProbe *probe, void **userdatap) { - *userdatap = probe->userdata; -} - -/** - * n_acd_probe_announce() - announce the configured IP address - * @probe: probe to operate on - * @defend: defense policy - * - * Announce the IP address on the local link, and start defending it according - * to the given policy, which mut be one of N_ACD_DEFEND_ONCE, - * N_ACD_DEFEND_NEVER, or N_ACD_DEFEND_ALWAYS. - * - * This must be called in response to an N_ACD_EVENT_READY event, and only - * after the given address has been configured on the given network interface. - * - * Return: 0 on success, N_ACD_E_INVALID_ARGUMENT in case the defense policy - * is invalid, negative error code on failure. - */ -_c_public_ int n_acd_probe_announce(NAcdProbe *probe, unsigned int defend) { - if (defend >= _N_ACD_DEFEND_N) - return N_ACD_E_INVALID_ARGUMENT; - - probe->state = N_ACD_PROBE_STATE_ANNOUNCING; - probe->defend = defend; - probe->n_iteration = 0; - - /* - * We must schedule a fake-timeout, since we are not allowed to - * advance the state-machine outside of n_acd_dispatch(). - */ - n_acd_probe_schedule(probe, 0, 0); - - return 0; -} diff --git a/shared/n-acd/src/n-acd.c b/shared/n-acd/src/n-acd.c deleted file mode 100644 index c1d92865..00000000 --- a/shared/n-acd/src/n-acd.c +++ /dev/null @@ -1,1027 +0,0 @@ -/* - * IPv4 Address Conflict Detection - * - * This file contains the main context initialization and management functions, - * as well as a bunch of utilities used through the n-acd modules. - */ - -/** - * DOC: IPv4 Address Conflict Detection - * - * The `n-acd` project implements the IPv4 Address Conflict Detection protocol - * as defined in RFC-5227. The protocol originates in the IPv4 Link Local - * Address selection but was later on generalized and resulted in `ACD`. The - * idea is to use `ARP` to query a link for an address to see whether it - * already exists on the network, as well as defending an address that is in - * use on a network interface. Furthermore, `ACD` provides passive diagnostics - * for administrators, as it will detect address conflicts automatically, which - * then can be logged or shown to a user. - * - * The main context object of `n-acd` is the `NAcd` structure. It is a passive - * ref-counted context object which drives `ACD` probes running on it. A - * context is specific to a linux network device and transport. If multiple - * network devices are used, then separate `NAcd` contexts must be deployed. - * - * The `NAcdProbe` object drives a single `ACD` state-machine. A probe is - * created on an `NAcd` context by providing an address to probe for. The probe - * will then raise notifications whether the address conflict detection found - * something, or whether the address is ready to be used. Optionally, the probe - * will then enter into passive mode and defend the address as long as it is - * kept active. - * - * Note that the `n-acd` project only implements the networking protocol. It - * never queries or modifies network interfaces. It completely relies on the - * API user to react to notifications and update network interfaces - * respectively. `n-acd` uses an event-mechanism on every context object. All - * events raise by any probe or operation on a given context will queue all - * events on that context object. The event-queue can then be drained by the - * API user. All events are properly asynchronous and designed in a way that no - * synchronous reaction to any event is required. That is, the events are - * carefully designed to allow forwarding via IPC (or even networks) to a - * controller that handles them and specifies how to react. Furthermore, none - * of the function calls of `n-acd` require synchronous error handling. - * Instead, functions only ever return values on fatal errors. Everything else - * is queued as events, thus guaranteeing that synchronous handling of return - * values is not required. Exceptions are functions that do not affect internal - * state or do not have an associated context object. - */ - -#include <assert.h> -#include <c-list.h> -#include <c-rbtree.h> -#include <c-siphash.h> -#include <c-stdaux.h> -#include <endian.h> -#include <errno.h> -#include <inttypes.h> -#include <limits.h> -#include <linux/if_packet.h> -#include <netinet/if_ether.h> -#include <netinet/in.h> -#include <stdlib.h> -#include <string.h> -#include <sys/auxv.h> -#include <sys/epoll.h> -#include <sys/socket.h> -#include <sys/types.h> -#include <unistd.h> -#include "n-acd.h" -#include "n-acd-private.h" - -enum { - N_ACD_EPOLL_TIMER, - N_ACD_EPOLL_SOCKET, -}; - -static int n_acd_get_random(unsigned int *random) { - uint8_t hash_seed[] = { - 0x3a, 0x0c, 0xa6, 0xdd, 0x44, 0xef, 0x5f, 0x7a, - 0x5e, 0xd7, 0x25, 0x37, 0xbf, 0x4e, 0x80, 0xa1, - }; - CSipHash hash = C_SIPHASH_NULL; - struct timespec ts; - const uint8_t *p; - int r; - - /* - * We need random jitter for all timeouts when handling ARP probes. Use - * AT_RANDOM to get a seed for rand_r(3p), if available (should always - * be available on linux). See the time-out scheduler for details. - * Additionally, we include the current time in the seed. This avoids - * using the same jitter in case you run multiple ACD engines in the - * same process. Lastly, the seed is hashed with SipHash24 to avoid - * exposing the value of AT_RANDOM on the network. - */ - c_siphash_init(&hash, hash_seed); - - p = (const uint8_t *)getauxval(AT_RANDOM); - if (p) - c_siphash_append(&hash, p, 16); - - r = clock_gettime(CLOCK_MONOTONIC, &ts); - if (r < 0) - return -c_errno(); - - c_siphash_append(&hash, (const uint8_t *)&ts.tv_sec, sizeof(ts.tv_sec)); - c_siphash_append(&hash, (const uint8_t *)&ts.tv_nsec, sizeof(ts.tv_nsec)); - - *random = c_siphash_finalize(&hash); - return 0; -} - -static int n_acd_socket_new(int *fdp, int fd_bpf_prog, NAcdConfig *config) { - const struct sockaddr_ll address = { - .sll_family = AF_PACKET, - .sll_protocol = htobe16(ETH_P_ARP), - .sll_ifindex = config->ifindex, - .sll_halen = ETH_ALEN, - .sll_addr = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff }, - }; - int r, s = -1; - - s = socket(PF_PACKET, SOCK_DGRAM | SOCK_CLOEXEC | SOCK_NONBLOCK, 0); - if (s < 0) { - r = -c_errno(); - goto error; - } - - if (fd_bpf_prog >= 0) { - r = setsockopt(s, SOL_SOCKET, SO_ATTACH_BPF, &fd_bpf_prog, sizeof(fd_bpf_prog)); - if (r < 0) { - r = -c_errno(); - goto error; - } - } - - r = bind(s, (struct sockaddr *)&address, sizeof(address)); - if (r < 0) { - r = -c_errno(); - goto error; - } - - *fdp = s; - s = -1; - return 0; - -error: - if (s >= 0) - close(s); - return r; -} - -/** - * n_acd_config_new() - create configuration object - * @configp: output argument for new configuration - * - * This creates a new configuration object and provides it to the caller. The - * object is fully owned by the caller upon function return. - * - * A configuration object is a passive structure that is used to collect - * information that is then passed to a constructor or other function. A - * configuration never validates the data, but it is up to the consumer of a - * configuration to do that. - * - * Return: 0 on success, negative error code on failure. - */ -_c_public_ int n_acd_config_new(NAcdConfig **configp) { - _c_cleanup_(n_acd_config_freep) NAcdConfig *config = NULL; - - config = malloc(sizeof(*config)); - if (!config) - return -ENOMEM; - - *config = (NAcdConfig)N_ACD_CONFIG_NULL(*config); - - *configp = config; - config = NULL; - return 0; -} - -/** - * n_acd_config_free() - destroy configuration object - * @config: configuration to operate on, or NULL - * - * This destroys the configuration object @config. If @config is NULL, this is - * a no-op. - * - * Return: NULL is returned. - */ -_c_public_ NAcdConfig *n_acd_config_free(NAcdConfig *config) { - if (!config) - return NULL; - - free(config); - - return NULL; -} - -/** - * n_acd_config_set_ifindex() - set ifindex property - * @config: configuration to operate on - * @ifindex: ifindex to set - * - * This sets the @ifindex property of the configuration object. Any previous - * value is overwritten. - * - * A valid ifindex is a 32bit integer greater than 0. Any other value is - * treated as unspecified. - * - * The ifindex corresponds to the interface index provided by the linux kernel. - * It specifies the network device to be used. - */ -_c_public_ void n_acd_config_set_ifindex(NAcdConfig *config, int ifindex) { - config->ifindex = ifindex; -} - -/** - * n_acd_config_set_transport() - set transport property - * @config: configuration to operate on - * @transport: transport to set - * - * This specifies the transport to use. A transport must be one of the - * `N_ACD_TRANSPORT_*` identifiers. It selects which transport protocol `n-acd` - * will run on. - */ -_c_public_ void n_acd_config_set_transport(NAcdConfig *config, unsigned int transport) { - config->transport = transport; -} - -/** - * n_acd_config_set_mac() - set mac property - * @config: configuration to operate on - * @mac: mac to set - * - * This specifies the hardware address (also referred to as `MAC Address`) to - * use. Any hardware address can be specified. It is the caller's - * responsibility to make sure the address can actually be used. - * - * The address in @mac is copied into @config. It does not have to be retained - * by the caller. - */ -_c_public_ void n_acd_config_set_mac(NAcdConfig *config, const uint8_t *mac, size_t n_mac) { - /* - * We truncate the address at the maximum we support. We still remember - * the original length, so any consumer of this configuration can then - * complain about an unsupported address length. This allows us to - * avoid a memory allocation here and having to return `int`. - */ - config->n_mac = n_mac; - memcpy(config->mac, mac, n_mac > ETH_ALEN ? ETH_ALEN : n_mac); -} - -int n_acd_event_node_new(NAcdEventNode **nodep) { - NAcdEventNode *node; - - node = malloc(sizeof(*node)); - if (!node) - return -ENOMEM; - - *node = (NAcdEventNode)N_ACD_EVENT_NODE_NULL(*node); - - *nodep = node; - return 0; -} - -NAcdEventNode *n_acd_event_node_free(NAcdEventNode *node) { - if (!node) - return NULL; - - c_list_unlink(&node->probe_link); - c_list_unlink(&node->acd_link); - free(node); - - return NULL; -} - -int n_acd_ensure_bpf_map_space(NAcd *acd) { - NAcdProbe *probe; - _c_cleanup_(c_closep) int fd_map = -1, fd_prog = -1; - size_t max_map; - int r; - - if (acd->n_bpf_map < acd->max_bpf_map) - return 0; - - max_map = 2 * acd->max_bpf_map; - - r = n_acd_bpf_map_create(&fd_map, max_map); - if (r) - return r; - - c_rbtree_for_each_entry(probe, &acd->ip_tree, ip_node) { - r = n_acd_bpf_map_add(fd_map, &probe->ip); - if (r) - return r; - } - - r = n_acd_bpf_compile(&fd_prog, fd_map, (struct ether_addr*) acd->mac); - if (r) - return r; - - if (fd_prog >= 0) { - r = setsockopt(acd->fd_socket, SOL_SOCKET, SO_ATTACH_BPF, &fd_prog, sizeof(fd_prog)); - if (r) - return -c_errno(); - } - - if (acd->fd_bpf_map >= 0) - close(acd->fd_bpf_map); - acd->fd_bpf_map = fd_map; - fd_map = -1; - acd->max_bpf_map = max_map; - return 0; -} - -/** - * n_acd_new() - create a new ACD context - * @acdp: output argument for new context object - * @config: configuration parameters - * - * Create a new ACD context and return it in @acdp. The configuration @config - * must be initialized by the caller and must specify a valid network - * interface, transport mechanism, as well as hardware address compatible with - * the selected transport. The configuration is copied into the context. The - * @config object thus does not have to be retained by the caller. - * - * Return: 0 on success, negative error code on failure. - */ -_c_public_ int n_acd_new(NAcd **acdp, NAcdConfig *config) { - _c_cleanup_(n_acd_unrefp) NAcd *acd = NULL; - _c_cleanup_(c_closep) int fd_bpf_prog = -1; - struct epoll_event eevent; - int r; - - if (config->ifindex <= 0 || - config->transport != N_ACD_TRANSPORT_ETHERNET || - config->n_mac != ETH_ALEN || - !memcmp(config->mac, (uint8_t[ETH_ALEN]){ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff }, ETH_ALEN)) - return N_ACD_E_INVALID_ARGUMENT; - - acd = malloc(sizeof(*acd)); - if (!acd) - return -ENOMEM; - - *acd = (NAcd)N_ACD_NULL(*acd); - acd->ifindex = config->ifindex; - memcpy(acd->mac, config->mac, ETH_ALEN); - - r = n_acd_get_random(&acd->seed); - if (r) - return r; - - acd->fd_epoll = epoll_create1(EPOLL_CLOEXEC); - if (acd->fd_epoll < 0) - return -c_errno(); - - r = timer_init(&acd->timer); - if (r < 0) - return r; - - acd->max_bpf_map = 8; - - r = n_acd_bpf_map_create(&acd->fd_bpf_map, acd->max_bpf_map); - if (r) - return r; - - r = n_acd_bpf_compile(&fd_bpf_prog, acd->fd_bpf_map, (struct ether_addr*) acd->mac); - if (r) - return r; - - r = n_acd_socket_new(&acd->fd_socket, fd_bpf_prog, config); - if (r) - return r; - - eevent = (struct epoll_event){ - .events = EPOLLIN, - .data.u32 = N_ACD_EPOLL_TIMER, - }; - r = epoll_ctl(acd->fd_epoll, EPOLL_CTL_ADD, acd->timer.fd, &eevent); - if (r < 0) - return -c_errno(); - - eevent = (struct epoll_event){ - .events = EPOLLIN, - .data.u32 = N_ACD_EPOLL_SOCKET, - }; - r = epoll_ctl(acd->fd_epoll, EPOLL_CTL_ADD, acd->fd_socket, &eevent); - if (r < 0) - return -c_errno(); - - *acdp = acd; - acd = NULL; - return 0; -} - -static void n_acd_free_internal(NAcd *acd) { - NAcdEventNode *node, *t_node; - - if (!acd) - return; - - c_list_for_each_entry_safe(node, t_node, &acd->event_list, acd_link) - n_acd_event_node_free(node); - - c_assert(c_rbtree_is_empty(&acd->ip_tree)); - - if (acd->fd_socket >= 0) { - c_assert(acd->fd_epoll >= 0); - epoll_ctl(acd->fd_epoll, EPOLL_CTL_DEL, acd->fd_socket, NULL); - close(acd->fd_socket); - acd->fd_socket = -1; - } - - if (acd->fd_bpf_map >= 0) { - close(acd->fd_bpf_map); - acd->fd_bpf_map = -1; - } - - if (acd->timer.fd >= 0) { - c_assert(acd->fd_epoll >= 0); - epoll_ctl(acd->fd_epoll, EPOLL_CTL_DEL, acd->timer.fd, NULL); - timer_deinit(&acd->timer); - } - - if (acd->fd_epoll >= 0) { - close(acd->fd_epoll); - acd->fd_epoll = -1; - } - - free(acd); -} - -/** - * n_acd_ref() - acquire reference - * @acd: context to operate on, or NULL - * - * This acquires a single reference to the context specified as @acd. If @acd - * is NULL, this is a no-op. - * - * Return: @acd is returned. - */ -_c_public_ NAcd *n_acd_ref(NAcd *acd) { - if (acd) - ++acd->n_refs; - return acd; -} - -/** - * n_acd_unref() - release reference - * @acd: context to operate on, or NULL - * - * This releases a single reference to the context @acd. If this is the last - * reference, the context is torn down and deallocated. - * - * Return: NULL is returned. - */ -_c_public_ NAcd *n_acd_unref(NAcd *acd) { - if (acd && !--acd->n_refs) - n_acd_free_internal(acd); - return NULL; -} - -int n_acd_raise(NAcd *acd, NAcdEventNode **nodep, unsigned int event) { - NAcdEventNode *node; - int r; - - r = n_acd_event_node_new(&node); - if (r) - return r; - - node->event.event = event; - c_list_link_tail(&acd->event_list, &node->acd_link); - - if (nodep) - *nodep = node; - return 0; -} - -int n_acd_send(NAcd *acd, const struct in_addr *tpa, const struct in_addr *spa) { - struct sockaddr_ll address = { - .sll_family = AF_PACKET, - .sll_protocol = htobe16(ETH_P_ARP), - .sll_ifindex = acd->ifindex, - .sll_halen = ETH_ALEN, - .sll_addr = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff }, - }; - struct ether_arp arp = { - .ea_hdr = { - .ar_hrd = htobe16(ARPHRD_ETHER), - .ar_pro = htobe16(ETHERTYPE_IP), - .ar_hln = sizeof(acd->mac), - .ar_pln = sizeof(uint32_t), - .ar_op = htobe16(ARPOP_REQUEST), - }, - }; - ssize_t l; - int r; - - memcpy(arp.arp_sha, acd->mac, sizeof(acd->mac)); - memcpy(arp.arp_tpa, &tpa->s_addr, sizeof(uint32_t)); - - if (spa) - memcpy(arp.arp_spa, &spa->s_addr, sizeof(spa->s_addr)); - - l = sendto(acd->fd_socket, - &arp, - sizeof(arp), - MSG_NOSIGNAL, - (struct sockaddr *)&address, - sizeof(address)); - if (l < 0) { - if (errno == EAGAIN || errno == ENOBUFS) { - /* - * We never maintain outgoing queues. We rely on the - * network device to do that for us. In case the queues - * are full, or the kernel refuses to queue the packet - * for other reasons, we must tell our caller that the - * packet was dropped. - */ - return N_ACD_E_DROPPED; - } else if (errno == ENETDOWN || errno == ENXIO) { - /* - * These errors happen if the network device went down - * or was actually removed. We always propagate this as - * event, so the user can react accordingly (similarly - * to the recvmmsg(2) handler). In case the user does - * not immediately react, we also tell our caller that - * the packet was dropped, so we don't erroneously - * treat this as success. - */ - - r = n_acd_raise(acd, NULL, N_ACD_EVENT_DOWN); - if (r) - return r; - - return N_ACD_E_DROPPED; - } - - /* - * Random network error. We treat this as fatal and propagate - * the error, so it is noticed and can be investigated. - */ - return -c_errno(); - } else if (l != (ssize_t)sizeof(arp)) { - /* - * Ugh, the kernel modified the packet. This is unexpected. We - * consider the packet lost. - */ - return N_ACD_E_DROPPED; - } - - return 0; -} - -/** - * n_acd_get_fd() - get pollable file descriptor - * @acd: context object to operate on - * @fdp: output argument for file descriptor - * - * This returns the backing file-descriptor of the context object @acd. The - * file-descriptor is owned by @acd and valid as long as @acd is. The - * file-descriptor never changes, so it can be cached by the caller as long as - * they hold a reference to @acd. - * - * The file-descriptor is internal to the @acd context and should not be - * modified by the caller. It is only exposed to allow the caller to poll on - * it. Whenever the file-descriptor polls readable, n_acd_dispatch() should be - * called. - * - * Currently, the file-descriptor is an epoll-fd. - */ -_c_public_ void n_acd_get_fd(NAcd *acd, int *fdp) { - *fdp = acd->fd_epoll; -} - -static int n_acd_handle_timeout(NAcd *acd) { - NAcdProbe *probe; - uint64_t now; - int r; - - /* - * Read the current time once, and handle all timeouts that triggered - * before the current time. Rereading the current time in each loop - * might risk creating a live-lock, and the fact that we read the - * time after reading the timer guarantees that the timeout which - * woke us up is handled. - * - * When there are no more timeouts to handle at the given time, we - * rearm the timer to potentially wake us up again in the future. - */ - timer_now(&acd->timer, &now); - - for (;;) { - Timeout *timeout; - - r = timer_pop_timeout(&acd->timer, now, &timeout); - if (r < 0) { - return r; - } else if (!timeout) { - /* - * There are no more timeouts pending before @now. Rearm - * the timer to fire again at the next timeout. - */ - timer_rearm(&acd->timer); - break; - } - - probe = (void *)timeout - offsetof(NAcdProbe, timeout); - r = n_acd_probe_handle_timeout(probe); - if (r) - return r; - } - - return 0; -} - -static int n_acd_handle_packet(NAcd *acd, struct ether_arp *packet) { - bool hard_conflict; - NAcdProbe *probe; - uint32_t addr; - CRBNode *node; - int r; - - /* - * We are interested in 2 kinds of ARP messages: - * - * 1) Someone who is *NOT* us sends *ANY* ARP message with our IP - * address as sender. This is never good, because it implies an - * address conflict. - * We call this a hard-conflict. - * - * 2) Someone who is *NOT* us sends an ARP REQUEST without any sender - * IP, but our IP as target. This implies someone else performs an - * ARP Probe with our address. This also implies a conflict, but - * one that can be resolved by responding to the probe. - * We call this a soft-conflict. - * - * We are never interested in any other ARP message. The kernel already - * deals with everything else, hence, we can silently ignore those. - * - * Now, we simply check whether a sender-address is set. This allows us - * to distinguish both cases. We then check further conditions, so we - * can bail out early if neither is the case. - * - * Lastly, we perform a lookup in our probe-set to check whether the - * address actually matches, so we can let these probes dispatch the - * message. Note that we allow duplicate probes, so we need to dispatch - * each matching probe, not just one. - */ - - if (memcmp(packet->arp_spa, (uint8_t[4]){ }, sizeof(packet->arp_spa))) { - memcpy(&addr, packet->arp_spa, sizeof(addr)); - hard_conflict = true; - } else if (packet->ea_hdr.ar_op == htobe16(ARPOP_REQUEST)) { - memcpy(&addr, packet->arp_tpa, sizeof(addr)); - hard_conflict = false; - } else { - /* - * The BPF filter will not let through any other packet. - */ - return -EIO; - } - - /* Find top-most node that matches @addr. */ - node = acd->ip_tree.root; - while (node) { - probe = c_rbnode_entry(node, NAcdProbe, ip_node); - if (addr < probe->ip.s_addr) - node = node->left; - else if (addr > probe->ip.s_addr) - node = node->right; - else - break; - } - - /* - * If the address is unknown, we drop the package. This might happen if - * the kernel queued the packet and passed the BPF filter, but we - * modified the set before dequeuing the message. - */ - if (!node) - return 0; - - /* Forward to left-most child that still matches @addr. */ - while (node->left && addr == c_rbnode_entry(node->left, - NAcdProbe, - ip_node)->ip.s_addr) - node = node->left; - - /* Iterate all matching entries in-order. */ - do { - probe = c_rbnode_entry(node, NAcdProbe, ip_node); - - r = n_acd_probe_handle_packet(probe, packet, hard_conflict); - if (r) - return r; - - node = c_rbnode_next(node); - } while (node && addr == c_rbnode_entry(node, - NAcdProbe, - ip_node)->ip.s_addr); - - return 0; -} - -static int n_acd_dispatch_timer(NAcd *acd, struct epoll_event *event) { - int r; - - if (event->events & (EPOLLHUP | EPOLLERR)) { - /* - * There is no way to handle either gracefully. If we ignored - * them, we would busy-loop, so lets rather forward the error - * to the caller. - */ - return -EIO; - } - - if (event->events & EPOLLIN) { - r = timer_read(&acd->timer); - if (r <= 0) - return r; - - c_assert(r == TIMER_E_TRIGGERED); - - /* - * A timer triggered, handle all pending timeouts at a given - * point in time. There can only be a finite number of pending - * timeouts, any new ones will be in the future, so not handled - * now, but guaranteed to wake us up again when they do trigger. - */ - r = n_acd_handle_timeout(acd); - if (r) - return r; - } - - return 0; -} - -static bool n_acd_packet_is_valid(NAcd *acd, void *packet, size_t n_packet) { - struct ether_arp *arp; - - /* - * The eBPF filter will ensure that this function always returns true, however, - * this allows the eBPF filter to be an optional optimization which is necessary - * on older kernels. - * - * See comments in n-acd-bpf.c for details. - */ - - if (n_packet != sizeof(*arp)) - return false; - - arp = packet; - - if (arp->arp_hrd != htobe16(ARPHRD_ETHER)) - return false; - - if (arp->arp_pro != htobe16(ETHERTYPE_IP)) - return false; - - if (arp->arp_hln != sizeof(struct ether_addr)) - return false; - - if (arp->arp_pln != sizeof(struct in_addr)) - return false; - - if (!memcmp(arp->arp_sha, acd->mac, sizeof(struct ether_addr))) - return false; - - if (memcmp(arp->arp_spa, &((struct in_addr) { INADDR_ANY }), sizeof(struct in_addr))) { - if (arp->arp_op != htobe16(ARPOP_REQUEST) && arp->arp_op != htobe16(ARPOP_REPLY)) - return false; - } else if (arp->arp_op != htobe16(ARPOP_REQUEST)) { - return false; - } - - return true; -} - -static int n_acd_dispatch_socket(NAcd *acd, struct epoll_event *event) { - const size_t n_batch = 8; - struct mmsghdr msgs[n_batch]; - struct iovec iovecs[n_batch]; - struct ether_arp data[n_batch]; - size_t i; - int r, n; - - for (i = 0; i < n_batch; ++i) { - iovecs[i].iov_base = data + i; - iovecs[i].iov_len = sizeof(data[i]); - msgs[i].msg_hdr = (struct msghdr){ - .msg_iov = iovecs + i, - .msg_iovlen = 1, - }; - } - - /* - * We always directly call into recvmmsg(2), regardless which EPOLL* - * event is signalled. On sockets, the recv(2)-family of syscalls does - * a suitable job of handling all possible scenarios and telling us - * about it. Hence, lets take the easy route and always ask the kernel - * about the current state. - */ - n = recvmmsg(acd->fd_socket, msgs, n_batch, 0, NULL); - if (n < 0) { - if (errno == ENETDOWN) { - /* - * We get ENETDOWN if the network-device goes down or - * is removed. This error is temporary and only queued - * once. Subsequent reads will simply return EAGAIN - * until the device is up again and has data queued. - * Usually, the caller should tear down all probes when - * an interface goes down, but we leave it up to the - * caller to decide what to do. We propagate the code - * and continue. - */ - return n_acd_raise(acd, NULL, N_ACD_EVENT_DOWN); - } else if (errno == EAGAIN) { - /* - * There is no more data queued and we did not get - * preempted. Everything is good to go. - * As a safety-net against busy-looping, we do check - * for HUP/ERR. Neither should be set, since they imply - * error-dequeue behavior on all socket calls. Lets - * fail hard if we trigger it, so we can investigate. - */ - if (event->events & (EPOLLHUP | EPOLLERR)) - return -EIO; - - return 0; - } else { - /* - * Something went wrong. Propagate the error-code, so - * this can be investigated. - */ - return -c_errno(); - } - } else if (n >= (ssize_t)n_batch) { - /* - * If all buffers were filled with data, we cannot be sure that - * there is nothing left to read. But to avoid starvation, we - * cannot loop on this condition. Instead, we mark the context - * as preempted so the caller can call us again. - * Note that in level-triggered event-loops this condition can - * be neglected, but in edge-triggered event-loops it is - * crucial to forward this information. - * - * On the other hand, there are several conditions where the - * kernel might return less batches than requested, but was - * still preempted. However, all of those cases require the - * preemption to have triggered a wakeup *after* we entered - * recvmmsg(). Hence, even if we did not recognize the - * preemption, an edge must have triggered and as such we will - * handle the event on the next turn. - */ - acd->preempted = true; - } - - for (i = 0; (ssize_t)i < n; ++i) { - if (!n_acd_packet_is_valid(acd, data + i, msgs[i].msg_len)) - continue; - /* - * Handle the packet. Bail out if something went wrong. Note - * that this must be fatal errors, since we discard all other - * packets that follow. - */ - r = n_acd_handle_packet(acd, data + i); - if (r) - return r; - } - - return 0; -} - -/** - * n_acd_dispatch() - dispatch context - * @acd: context object to operate on - * - * This dispatches the internal state-machine of all probes and operations - * running on the context @acd. - * - * Any outside effect or event triggered by this dispatcher will be queued on - * the event-queue of @acd. Whenever the dispatcher returns, the caller is - * required to drain the event-queue via n_acd_pop_event() until it is empty. - * - * This function dispatches as many events as possible up to a static limit to - * prevent stalling execution. If the static limit is reached, this function - * will return with N_ACD_E_PREEMPTED, otherwise 0 is returned. In most cases - * preemption can be ignored, because level-triggered event notification - * handles it automatically. However, in case of edge-triggered event - * mechanisms, the caller must make sure to call the dispatcher again. - * - * Return: 0 on success, N_ACD_E_PREEMPTED on preemption, negative error code - * on failure. - */ -_c_public_ int n_acd_dispatch(NAcd *acd) { - struct epoll_event events[2]; - int n, i, r = 0; - - n = epoll_wait(acd->fd_epoll, events, sizeof(events) / sizeof(*events), 0); - if (n < 0) { - /* Linux never returns EINTR if `timeout == 0'. */ - return -c_errno(); - } - - acd->preempted = false; - - for (i = 0; i < n; ++i) { - switch (events[i].data.u32) { - case N_ACD_EPOLL_TIMER: - r = n_acd_dispatch_timer(acd, events + i); - break; - case N_ACD_EPOLL_SOCKET: - r = n_acd_dispatch_socket(acd, events + i); - break; - default: - c_assert(0); - r = 0; - break; - } - - if (r) - return r; - } - - return acd->preempted ? N_ACD_E_PREEMPTED : 0; -} - -/** - * n_acd_pop_event() - get the next pending event - * @acd: context object to operate on - * @eventp: output argument for the event - * - * Returns a pointer to the next pending event. The event is still owend by - * the context, and is only valid until the next call to n_acd_pop_event() - * or until the owning object is freed (either the ACD context or the indicated - * probe object). - * - * An event either originates on the ACD context, or one of the configured - * probes. If the event-type has a 'probe' pointer, it originated on the - * indicated probe (which is *never* NULL), otherwise it originated on the - * context. - * - * Users must call this function repeatedly until either an error is returned, - * or the event-pointer is NULL. Wakeups on the epoll-fd are only guaranteed - * for each batch of events. Hence, it is the callers responsibility to drain - * the event-queue somehow after each call to n_acd_dispatch(). Note that - * events can only be added by n_acd_dispatch(), hence, you cannot live-lock - * when draining the event queue. - * - * The possible events are: - * * N_ACD_EVENT_READY: A configured IP address was probed successfully - * and is ready to be used. Once configured on the - * interface, the caller must call n_acd_announce() - * to announce and start defending the address. - * * N_ACD_EVENT_USED: Someone is already using the IP address being - * probed. The probe is put into stopped state and - * should be freed by the caller. - * * N_ACD_EVENT_DEFENDED: A conflict was detected for an announced IP - * address, and the engine attempted to defend it. - * This is purely informational, and no action is - * required by the caller. - * * N_ACD_EVENT_CONFLICT: A conflict was detected for an announced IP - * address, and the probe was not able to defend - * it (according to the configured policy). The - * probe halted, the caller must stop using - * the address immediately, and should free the probe. - * * N_ACD_EVENT_DOWN: The specified network interface was put down. The - * user is recommended to free *ALL* probes and - * recreate them as soon as the interface is up again. - * Note that this event is purely informational. The - * probes will continue running, but all packets will - * be blackholed, and no network packets are received, - * until the network is back up again. Hence, from an - * operational perspective, the legitimacy of the ACD - * probes is lost and the user better re-probes all - * addresses. - * - * Returns: 0 on success, negative error code on failure. The popped event is - * returned in @eventp. If no event is pending, NULL is placed in - * @eventp and 0 is returned. If an error is returned, @eventp is left - * untouched. - */ -_c_public_ int n_acd_pop_event(NAcd *acd, NAcdEvent **eventp) { - NAcdEventNode *node, *t_node; - - c_list_for_each_entry_safe(node, t_node, &acd->event_list, acd_link) { - if (node->is_public) { - n_acd_event_node_free(node); - continue; - } - - node->is_public = true; - *eventp = &node->event; - return 0; - } - - *eventp = NULL; - return 0; -} - -/** - * n_acd_probe() - start new probe - * @acd: context object to operate on - * @probep: output argument for new probe - * @config: probe configuration - * - * This creates a new probe on the context @acd and returns the probe in - * @probep. The configuration @config must provide valid probe parameters. At - * least a valid IP address must be provided through the configuration. - * - * This function does not reject duplicate probes for the same address. It is - * the caller's decision whether duplicates are allowed or not. But note that - * duplicate probes on the same context will not conflict each other. That is, - * running a probe for the same address twice on the same context will not - * cause them to consider each other a duplicate. - * - * Probes are rather lightweight objects. They do not create any - * file-descriptors or other kernel objects. Probes always re-use the - * infrastructure provided by the context object @acd. This allows running many - * probes simultaneously without exhausting resources. - * - * Return: 0 on success, N_ACD_E_INVALID_ARGUMENT on invalid configuration - * parameters, negative error code on failure. - */ -_c_public_ int n_acd_probe(NAcd *acd, NAcdProbe **probep, NAcdProbeConfig *config) { - return n_acd_probe_new(probep, acd, config); -} diff --git a/shared/n-acd/src/n-acd.h b/shared/n-acd/src/n-acd.h deleted file mode 100644 index e2b01270..00000000 --- a/shared/n-acd/src/n-acd.h +++ /dev/null @@ -1,150 +0,0 @@ -#pragma once - -/* - * IPv4 Address Conflict Detection - * - * This is the public header of the n-acd library, implementing IPv4 Address - * Conflict Detection as described in RFC-5227. This header defines the public - * API and all entry points of n-acd. - */ - -#ifdef __cplusplus -extern "C" { -#endif - -#include <netinet/in.h> -#include <inttypes.h> -#include <stdbool.h> -#include <stdlib.h> - -typedef struct NAcd NAcd; -typedef struct NAcdConfig NAcdConfig; -typedef struct NAcdEvent NAcdEvent; -typedef struct NAcdProbe NAcdProbe; -typedef struct NAcdProbeConfig NAcdProbeConfig; - -#define N_ACD_TIMEOUT_RFC5227 (UINT64_C(9000)) - -enum { - _N_ACD_E_SUCCESS, - - N_ACD_E_PREEMPTED, - N_ACD_E_INVALID_ARGUMENT, - - _N_ACD_E_N, -}; - -enum { - N_ACD_TRANSPORT_ETHERNET, - _N_ACD_TRANSPORT_N, -}; - -enum { - N_ACD_EVENT_READY, - N_ACD_EVENT_USED, - N_ACD_EVENT_DEFENDED, - N_ACD_EVENT_CONFLICT, - N_ACD_EVENT_DOWN, - _N_ACD_EVENT_N, -}; - -enum { - N_ACD_DEFEND_NEVER, - N_ACD_DEFEND_ONCE, - N_ACD_DEFEND_ALWAYS, - _N_ACD_DEFEND_N, -}; - -struct NAcdEvent { - unsigned int event; - union { - struct { - NAcdProbe *probe; - } ready; - struct { - } down; - struct { - NAcdProbe *probe; - uint8_t *sender; - size_t n_sender; - } used, defended, conflict; - }; -}; - -/* configs */ - -int n_acd_config_new(NAcdConfig **configp); -NAcdConfig *n_acd_config_free(NAcdConfig *config); - -void n_acd_config_set_ifindex(NAcdConfig *config, int ifindex); -void n_acd_config_set_transport(NAcdConfig *config, unsigned int transport); -void n_acd_config_set_mac(NAcdConfig *config, const uint8_t *mac, size_t n_mac); - -int n_acd_probe_config_new(NAcdProbeConfig **configp); -NAcdProbeConfig *n_acd_probe_config_free(NAcdProbeConfig *config); - -void n_acd_probe_config_set_ip(NAcdProbeConfig *config, struct in_addr ip); -void n_acd_probe_config_set_timeout(NAcdProbeConfig *config, uint64_t msecs); - -/* contexts */ - -int n_acd_new(NAcd **acdp, NAcdConfig *config); -NAcd *n_acd_ref(NAcd *acd); -NAcd *n_acd_unref(NAcd *acd); - -void n_acd_get_fd(NAcd *acd, int *fdp); -int n_acd_dispatch(NAcd *acd); -int n_acd_pop_event(NAcd *acd, NAcdEvent **eventp); - -int n_acd_probe(NAcd *acd, NAcdProbe **probep, NAcdProbeConfig *config); - -/* probes */ - -NAcdProbe *n_acd_probe_free(NAcdProbe *probe); - -void n_acd_probe_set_userdata(NAcdProbe *probe, void *userdata); -void n_acd_probe_get_userdata(NAcdProbe *probe, void **userdatap); - -int n_acd_probe_announce(NAcdProbe *probe, unsigned int defend); - -/* inline helpers */ - -static inline void n_acd_config_freep(NAcdConfig **config) { - if (*config) - n_acd_config_free(*config); -} - -static inline void n_acd_config_freev(NAcdConfig *config) { - n_acd_config_free(config); -} - -static inline void n_acd_probe_config_freep(NAcdProbeConfig **config) { - if (*config) - n_acd_probe_config_free(*config); -} - -static inline void n_acd_probe_config_freev(NAcdProbeConfig *config) { - n_acd_probe_config_free(config); -} - -static inline void n_acd_unrefp(NAcd **acd) { - if (*acd) - n_acd_unref(*acd); -} - -static inline void n_acd_unrefv(NAcd *acd) { - n_acd_unref(acd); -} - -static inline void n_acd_probe_freep(NAcdProbe **probe) { - if (*probe) - n_acd_probe_free(*probe); -} - -static inline void n_acd_probe_freev(NAcdProbe *probe) { - n_acd_probe_free(probe); -} - -#ifdef __cplusplus -} -#endif diff --git a/shared/n-acd/src/util/timer.c b/shared/n-acd/src/util/timer.c deleted file mode 100644 index af2a887c..00000000 --- a/shared/n-acd/src/util/timer.c +++ /dev/null @@ -1,189 +0,0 @@ -/* - * Timer Utility Library - */ - -#include <assert.h> -#include <c-rbtree.h> -#include <c-stdaux.h> -#include <errno.h> -#include <stdlib.h> -#include <sys/timerfd.h> -#include <time.h> -#include "timer.h" - -int timer_init(Timer *timer) { - clockid_t clock = CLOCK_BOOTTIME; - int r; - - r = timerfd_create(clock, TFD_CLOEXEC | TFD_NONBLOCK); - if (r < 0 && errno == EINVAL) { - clock = CLOCK_MONOTONIC; - r = timerfd_create(clock, TFD_CLOEXEC | TFD_NONBLOCK); - } - if (r < 0) - return -errno; - - *timer = (Timer)TIMER_NULL(*timer); - timer->fd = r; - timer->clock = clock; - - return 0; -} - -void timer_deinit(Timer *timer) { - c_assert(c_rbtree_is_empty(&timer->tree)); - - if (timer->fd >= 0) { - close(timer->fd); - timer->fd = -1; - } -} - -void timer_now(Timer *timer, uint64_t *nowp) { - struct timespec ts; - int r; - - r = clock_gettime(timer->clock, &ts); - c_assert(r >= 0); - - *nowp = ts.tv_sec * UINT64_C(1000000000) + ts.tv_nsec; -} - -void timer_rearm(Timer *timer) { - uint64_t time; - Timeout *timeout; - int r; - - /* - * A timeout value of 0 clears the timer, we should only set that if - * no timeout exists in the tree. - */ - - timeout = c_rbnode_entry(c_rbtree_first(&timer->tree), Timeout, node); - c_assert(!timeout || timeout->timeout); - - time = timeout ? timeout->timeout : 0; - - if (time != timer->scheduled_timeout) { - r = timerfd_settime(timer->fd, - TFD_TIMER_ABSTIME, - &(struct itimerspec){ - .it_value = { - .tv_sec = time / UINT64_C(1000000000), - .tv_nsec = time % UINT64_C(1000000000), - }, - }, - NULL); - c_assert(r >= 0); - - timer->scheduled_timeout = time; - } -} - -int timer_read(Timer *timer) { - uint64_t v; - int r; - - r = read(timer->fd, &v, sizeof(v)); - if (r < 0) { - if (errno == EAGAIN) { - /* - * No more pending events. - */ - return 0; - } else { - /* - * Something failed. We use CLOCK_BOOTTIME/MONOTONIC, - * so ECANCELED cannot happen. Hence, there is no - * error that we could gracefully handle. Fail hard - * and let the caller deal with it. - */ - return -errno; - } - } else if (r != sizeof(v) || v == 0) { - /* - * Kernel guarantees 8-byte reads, and only to return - * data if at least one timer triggered; fail hard if - * it suddenly starts doing weird shit. - */ - return -EIO; - } - - return TIMER_E_TRIGGERED; -} - - -int timer_pop_timeout(Timer *timer, uint64_t until, Timeout **timeoutp) { - Timeout *timeout; - - /* - * If the first timeout is scheduled before @until, then unlink - * it and return it. Otherwise, return NULL. - */ - timeout = c_rbnode_entry(c_rbtree_first(&timer->tree), Timeout, node); - if (timeout && timeout->timeout <= until) { - c_rbnode_unlink(&timeout->node); - timeout->timeout = 0; - *timeoutp = timeout; - } else { - *timeoutp = NULL; - } - - return 0; -} - -void timeout_schedule(Timeout *timeout, Timer *timer, uint64_t time) { - c_assert(time); - - /* - * In case @timeout was already scheduled, remove it from the - * tree. If we are moving it to a new timer, rearm the old one. - */ - if (timeout->timer) { - c_rbnode_unlink(&timeout->node); - if (timeout->timer != timer) - timer_rearm(timeout->timer); - } - timeout->timer = timer; - timeout->timeout = time; - - /* - * Now insert it back into the tree in the correct new position. - * We allow duplicates in the tree, so this insertion is open-coded. - */ - { - Timeout *other; - CRBNode **slot, *parent; - - slot = &timer->tree.root; - parent = NULL; - while (*slot) { - other = c_rbnode_entry(*slot, Timeout, node); - parent = *slot; - if (timeout->timeout < other->timeout) - slot = &(*slot)->left; - else - slot = &(*slot)->right; - } - - c_rbtree_add(&timer->tree, parent, slot, &timeout->node); - } - - /* - * Rearm the timer as we updated the timeout tree. - */ - timer_rearm(timer); -} - -void timeout_unschedule(Timeout *timeout) { - Timer *timer = timeout->timer; - - if (!timer) - return; - - c_rbnode_unlink(&timeout->node); - timeout->timeout = 0; - timeout->timer = NULL; - - timer_rearm(timer); -} diff --git a/shared/n-acd/src/util/timer.h b/shared/n-acd/src/util/timer.h deleted file mode 100644 index d01b2741..00000000 --- a/shared/n-acd/src/util/timer.h +++ /dev/null @@ -1,54 +0,0 @@ -#pragma once - -#include <c-rbtree.h> -#include <c-stdaux.h> -#include <inttypes.h> -#include <stdlib.h> -#include <time.h> -#include <unistd.h> - -typedef struct Timer Timer; -typedef struct Timeout Timeout; - -enum { - _TIMER_E_SUCCESS, - - TIMER_E_TRIGGERED, - - _TIMER_E_N, -}; - -struct Timer { - int fd; - clockid_t clock; - CRBTree tree; - uint64_t scheduled_timeout; -}; - -#define TIMER_NULL(_x) { \ - .fd = -1, \ - .tree = C_RBTREE_INIT, \ - } - -struct Timeout { - Timer *timer; - CRBNode node; - uint64_t timeout; -}; - -#define TIMEOUT_INIT(_x) { \ - .node = C_RBNODE_INIT((_x).node), \ - } - -int timer_init(Timer *timer); -void timer_deinit(Timer *timer); - -void timer_now(Timer *timer, uint64_t *nowp); - -int timer_pop_timeout(Timer *timer, uint64_t now, Timeout **timerp); -void timer_rearm(Timer *timer); -int timer_read(Timer *timer); - -void timeout_schedule(Timeout *timeout, Timer *timer, uint64_t time); -void timeout_unschedule(Timeout *timeout); - diff --git a/shared/n-dhcp4/src/n-dhcp4-c-connection.c b/shared/n-dhcp4/src/n-dhcp4-c-connection.c deleted file mode 100644 index 31235253..00000000 --- a/shared/n-dhcp4/src/n-dhcp4-c-connection.c +++ /dev/null @@ -1,1256 +0,0 @@ -/* - * DHCPv4 Client Connection - * - * XXX - */ - -#include <assert.h> -#include <c-stdaux.h> -#include <errno.h> -#include <limits.h> -#include <sys/socket.h> /* needed by linux/netdevice.h */ -#include <linux/netdevice.h> -#include <net/if_arp.h> -#include <stdbool.h> -#include <stdlib.h> -#include <string.h> -#include <sys/epoll.h> -#include "n-dhcp4-private.h" -#include "util/packet.h" - -/** - * n_dhcp4_c_connection_init() - initialize client connection - * @connection: connection to operate on - * @client_config: client configuration to use - * @probe_config: client probe configuration to use - * @log_queue: the log queue for logging events - * @fd_epoll: epoll context to attach to, or -1 - * - * This initializes a new client connection using the configuration given in - * @client_config and @probe_config. - * - * The client-configuration given as @client_config must survive the lifetime - * of @connection. It is pinned in the connection and used all over the place. - * The caller must guarantee that the configuration is not deallocated in the - * meantime. Same is true for @probe_config. - * - * The new connection automatically attaches to the epoll context given as - * @fd_epoll. The epoll FD is retained in the connection and the caller must - * guarantee that it lives as long as the connection. - * The caller is explicitly allowed to pass -1 as @fd_epoll, in which case the - * connection will initialize correctly, but will not be in a usable state. - * That is, any call to n_dhcp4_c_connection_listen() will fail, since it will - * be unable to attach to the epoll context. Such a connection can be used to - * get a detached object that behaves sound, but provides no runtime. - * - * Return: 0 on success, negative error code on failure. - */ -int n_dhcp4_c_connection_init(NDhcp4CConnection *connection, - NDhcp4ClientConfig *client_config, - NDhcp4ClientProbeConfig *probe_config, - NDhcp4LogQueue *log_queue, - int fd_epoll) { - *connection = (NDhcp4CConnection)N_DHCP4_C_CONNECTION_NULL(*connection); - connection->client_config = client_config; - connection->probe_config = probe_config; - connection->fd_epoll = fd_epoll; - connection->log_queue = log_queue; - - /* - * We explicitly allow initializing connections with an invalid - * epoll-fd. The resulting connection immediately transitions into the - * CLOSED state. This allows the caller to create dummy connections - * useful to provide asynchronous constructor-feedback in the API. - * - * The effect of this is as if you immediately call - * n_dhcp4_c_connection_close() on the new connection. However, by - * directly passing -1 in the constructor, you are guaranteed not even - * the constructor can ever mess with your epoll-set. - */ - if (connection->fd_epoll < 0) - connection->state = N_DHCP4_C_CONNECTION_STATE_CLOSED; - - return 0; -} - -/** - * n_dhcp4_c_connection_deinit() - deinitialize client connection - * @connection: connection to operate on - * - * This deinitializes a connection that was previously initialized via - * n_dhcp4_c_connection_init(). It will tear down all allocated state and - * release it. - * - * Once this function returns, @connection is re-initialized to - * N_DHCP4_C_CONNECTION_NULL. If this function is called on a deinitialized - * connection, it is a no-op. - */ -void n_dhcp4_c_connection_deinit(NDhcp4CConnection *connection) { - n_dhcp4_c_connection_close(connection); - n_dhcp4_outgoing_free(connection->request); - *connection = (NDhcp4CConnection)N_DHCP4_C_CONNECTION_NULL(*connection); -} - -static void n_dhcp4_c_connection_outgoing_set_secs(NDhcp4Outgoing *message) { - uint64_t secs; - - /* - * This function sets the `secs` field for outgoing messages. It - * expects the base-time and start-time to be already set by the - * caller. - * For a given outgoing message, its `secs` field describes the time - * (in seconds) between the start of the transaction this message is - * part of and the start of the operational process (also called the - * base time here). - * - * The operational process in the DHCP sense describes the entire - * process of requesting a lease and acquiring it. That is, it starts - * with the caller's intent to request a lease, and it ends when we - * got granted a lease. The act of refreshing a lease is, in itself, a - * new operational process. The base-time describes the start-time - * recorded when such a process as initiated. - * - * A transaction in the DHCP sense describes a request+reply - * combination, in most cases. That is, the time a request is sent is - * the start-time of a transaction. In the ideal case, the start-time - * of the first transaction in an operational process matches the - * base-time. However, transactions are often delayed with a randomized - * offset to reduce traffic during network bursts. - * In some cases, however, transactions are composed out of multiple - * requests+reply combinations. This includes, for instance, the SELECT - * message following an OFFER. The specification clearly says that - * those must be considered a single transaction and thus share the - * transaction start-time. - * - * The `secs` field, thus, describes how long a client has been busy - * requesting a lease. DHCP servers and proxies do use it to prioritize - * clients. - * - * Note: Some DHCP relays reject a `secs` value of 0 (which might look - * like it is uninitialized). Hence, we always clamp the value to - * the range `[1, 65535]`. - */ - - secs = message->userdata.base_time - message->userdata.start_time; - secs /= 1000ULL * 1000ULL * 1000ULL; /* nsecs to secs */ - secs = C_CLAMP(secs, 1, UINT16_MAX); - - n_dhcp4_outgoing_set_secs(message, secs); -} - -int n_dhcp4_c_connection_listen(NDhcp4CConnection *connection) { - _c_cleanup_(c_closep) int fd_packet = -1; - int r; - - if (connection->state == N_DHCP4_C_CONNECTION_STATE_PACKET) - return 0; - - c_assert(connection->state == N_DHCP4_C_CONNECTION_STATE_INIT || - connection->state == N_DHCP4_C_CONNECTION_STATE_DRAINING || - connection->state == N_DHCP4_C_CONNECTION_STATE_UDP); - - if (connection->fd_packet >= 0) { - epoll_ctl(connection->fd_epoll, EPOLL_CTL_DEL, connection->fd_packet, NULL); - connection->fd_packet = c_close(connection->fd_packet); - } - - if (connection->fd_udp >= 0) { - epoll_ctl(connection->fd_epoll, EPOLL_CTL_DEL, connection->fd_udp, NULL); - connection->fd_udp = c_close(connection->fd_udp); - } - - r = n_dhcp4_c_socket_packet_new(&fd_packet, connection->client_config->ifindex); - if (r) - return r; - - r = epoll_ctl(connection->fd_epoll, - EPOLL_CTL_ADD, - fd_packet, - &(struct epoll_event){ - .events = EPOLLIN, - .data = { .u32 = N_DHCP4_CLIENT_EPOLL_IO }, - }); - if (r < 0) - return -errno; - - connection->state = N_DHCP4_C_CONNECTION_STATE_PACKET; - connection->fd_packet = fd_packet; - fd_packet = -1; - return 0; -} - -int n_dhcp4_c_connection_connect(NDhcp4CConnection *connection, - const struct in_addr *client, - const struct in_addr *server) { - _c_cleanup_(c_closep) int fd_udp = -1; - int r; - - c_assert(connection->state == N_DHCP4_C_CONNECTION_STATE_PACKET); - - r = n_dhcp4_c_socket_udp_new(&fd_udp, - connection->client_config->ifindex, - client, - server); - if (r) - return r; - - r = epoll_ctl(connection->fd_epoll, - EPOLL_CTL_ADD, - fd_udp, - &(struct epoll_event){ - .events = EPOLLIN, - .data = { .u32 = N_DHCP4_CLIENT_EPOLL_IO }, - }); - if (r < 0) - return -errno; - - r = packet_shutdown(connection->fd_packet); - if (r < 0) { - epoll_ctl(connection->fd_epoll, EPOLL_CTL_DEL, fd_udp, NULL); - return r; - } - - connection->state = N_DHCP4_C_CONNECTION_STATE_DRAINING; - connection->fd_udp = fd_udp; - fd_udp = -1; - connection->client_ip = client->s_addr; - connection->server_ip = server->s_addr; - return 0; -} - -void n_dhcp4_c_connection_close(NDhcp4CConnection *connection) { - if (connection->fd_udp >= 0) { - epoll_ctl(connection->fd_epoll, EPOLL_CTL_DEL, connection->fd_udp, NULL); - connection->fd_udp = c_close(connection->fd_udp); - } - - if (connection->fd_packet >= 0) { - epoll_ctl(connection->fd_epoll, EPOLL_CTL_DEL, connection->fd_packet, NULL); - connection->fd_packet = c_close(connection->fd_packet); - } - - connection->fd_epoll = -1; - connection->state = N_DHCP4_C_CONNECTION_STATE_CLOSED; -} - -static int n_dhcp4_c_connection_verify_incoming(NDhcp4CConnection *connection, - NDhcp4Incoming *message, - uint8_t *typep) { - NDhcp4Header *header = n_dhcp4_incoming_get_header(message); - uint8_t type; - uint32_t request_xid; - uint8_t *id; - size_t n_id; - int r; - - r = n_dhcp4_incoming_query_message_type(message, &type); - if (r) { - if (r == N_DHCP4_E_UNSET) - return N_DHCP4_E_MALFORMED; - else - return r; - } - - switch (type) { - case N_DHCP4_MESSAGE_OFFER: - case N_DHCP4_MESSAGE_ACK: - case N_DHCP4_MESSAGE_NAK: - /* - * Only accept replies if there is a pending request, and it - * has a matching transaction id. - */ - if (!connection->request) - return N_DHCP4_E_UNEXPECTED; - - n_dhcp4_outgoing_get_xid(connection->request, &request_xid); - if (header->xid != request_xid) - return N_DHCP4_E_UNEXPECTED; - - break; - case N_DHCP4_MESSAGE_FORCERENEW: - /* - * Force renew messages are triggered by a server, and do not - * match a pending request. - */ - break; - default: - return N_DHCP4_E_UNEXPECTED; - } - - /* - * In case our transport makes use of the 'chaddr' field, make sure it - * matches exactly our address. - */ - switch (connection->client_config->transport) { - case N_DHCP4_TRANSPORT_ETHERNET: - c_assert(connection->client_config->n_mac == ETH_ALEN); - - if (header->hlen != ETH_ALEN) - return N_DHCP4_E_UNEXPECTED; - if (memcmp(header->chaddr, connection->client_config->mac, ETH_ALEN) != 0) - return N_DHCP4_E_UNEXPECTED; - - break; - case N_DHCP4_TRANSPORT_INFINIBAND: - if (header->hlen != 0) - return N_DHCP4_E_UNEXPECTED; - - break; - } - - /* - * If a server passes us back a client ID, it must be the one we - * provided. We ignore any packets that have mismatching client-ids. - */ - id = NULL; - n_id = 0; - r = n_dhcp4_incoming_query(message, N_DHCP4_OPTION_CLIENT_IDENTIFIER, &id, &n_id); - if (r) { - if (r != N_DHCP4_E_UNSET) - return r; - } else { - if (n_id != connection->client_config->n_client_id) - return N_DHCP4_E_UNEXPECTED; - if (memcmp(id, connection->client_config->client_id, n_id) != 0) - return N_DHCP4_E_UNEXPECTED; - } - - *typep = type; - return 0; -} - -void n_dhcp4_c_connection_get_timeout(NDhcp4CConnection *connection, - uint64_t *timeoutp) { - uint64_t timeout; - size_t n_send; - - if (!connection->request) { - *timeoutp = 0; - return; - } - - switch (connection->request->userdata.type) { - case N_DHCP4_C_MESSAGE_DISCOVER: - case N_DHCP4_C_MESSAGE_SELECT: - case N_DHCP4_C_MESSAGE_INFORM: - /* - * Resend with an exponential backoff and a one second random - * slack, from a minimum of two seconds to a maximum of sixty - * four. - * - * Note that the RFC says to start at four rather than two - * seconds, and use [-1,1] slack, rather than [0,1]. - */ - n_send = connection->request->userdata.n_send; - if (n_send >= 6) - n_send = 6; - - timeout = connection->request->userdata.send_time + ((1ULL << n_send) * 1000000000ULL) + connection->request->userdata.send_jitter; - - break; - case N_DHCP4_C_MESSAGE_REBIND: - case N_DHCP4_C_MESSAGE_RENEW: - case N_DHCP4_C_MESSAGE_REBOOT: - /* - * Resend every sixty seconds with a one second random slack. - * - * Note that the RFC says to do this at most once, but we do - * it until we are cancelled. - */ - timeout = connection->request->userdata.send_time + (60ULL * 1000000000ULL) + connection->request->userdata.send_jitter; - - break; - case N_DHCP4_C_MESSAGE_DECLINE: - case N_DHCP4_C_MESSAGE_RELEASE: - /* XXX make sure these message types are never pinned? */ - timeout = 0; - break; - default: - c_assert(0); - } - - *timeoutp = timeout; -} - -static int n_dhcp4_c_connection_packet_broadcast(NDhcp4CConnection *connection, - NDhcp4Outgoing *message) { - int r; - - c_assert(connection->state == N_DHCP4_C_CONNECTION_STATE_PACKET); - - r = n_dhcp4_c_socket_packet_send(connection->fd_packet, - connection->client_config->ifindex, - connection->client_config->broadcast_mac, - connection->client_config->n_broadcast_mac, - message); - if (r) - return r; - - return 0; -} - -static int n_dhcp4_c_connection_udp_broadcast(NDhcp4CConnection *connection, - NDhcp4Outgoing *message) { - int r; - - c_assert(connection->state == N_DHCP4_C_CONNECTION_STATE_DRAINING || - connection->state == N_DHCP4_C_CONNECTION_STATE_UDP); - - r = n_dhcp4_c_socket_udp_broadcast(connection->fd_udp, message); - if (r) - return r; - - return 0; -} - -static int n_dhcp4_c_connection_udp_send(NDhcp4CConnection *connection, - NDhcp4Outgoing *message) { - int r; - - c_assert(connection->state == N_DHCP4_C_CONNECTION_STATE_DRAINING || - connection->state == N_DHCP4_C_CONNECTION_STATE_UDP); - - r = n_dhcp4_c_socket_udp_send(connection->fd_udp, message); - if (r) - return r; - - return 0; -} - -static void n_dhcp4_c_connection_init_header(NDhcp4CConnection *connection, - NDhcp4Header *header) { - bool broadcast = connection->client_config->request_broadcast; - - header->op = N_DHCP4_OP_BOOTREQUEST; - - switch (connection->client_config->transport) { - case N_DHCP4_TRANSPORT_ETHERNET: - c_assert(connection->client_config->n_mac == ETH_ALEN); - - header->htype = ARPHRD_ETHER; - header->hlen = ETH_ALEN; - memcpy(header->chaddr, connection->client_config->mac, ETH_ALEN); - break; - case N_DHCP4_TRANSPORT_INFINIBAND: - header->htype = ARPHRD_INFINIBAND; - header->hlen = 0; - - /* infiniband mandates to request broadcasts */ - broadcast = true; - break; - default: - abort(); - break; - } - - if (connection->client_ip != INADDR_ANY) { - header->ciaddr = connection->client_ip; - } else { - /* - * When the IP stack has not been configured, we may - * not be able to receive unicast packets, depending - * on the hardware. If that is the case we must request - * replies from the server to be broadcast. - * - * Once the IP stack has been configured, receiving - * unicast packets is never a problem, so the broadcast - * flag should not be set. - */ - if (broadcast) - header->flags |= N_DHCP4_MESSAGE_FLAG_BROADCAST; - } -} - -static int n_dhcp4_c_connection_new_message(NDhcp4CConnection *connection, - NDhcp4Outgoing **messagep, - uint8_t type) { - _c_cleanup_(n_dhcp4_outgoing_freep) NDhcp4Outgoing *message = NULL; - NDhcp4Header *header; - uint8_t message_type; - bool via_packet_socket = false; - int r; - - switch (type) { - case N_DHCP4_C_MESSAGE_DISCOVER: - message_type = N_DHCP4_MESSAGE_DISCOVER; - via_packet_socket = true; - break; - case N_DHCP4_C_MESSAGE_INFORM: - message_type = N_DHCP4_MESSAGE_INFORM; - break; - case N_DHCP4_C_MESSAGE_SELECT: - message_type = N_DHCP4_MESSAGE_REQUEST; - via_packet_socket = true; - break; - case N_DHCP4_C_MESSAGE_RENEW: - message_type = N_DHCP4_MESSAGE_REQUEST; - break; - case N_DHCP4_C_MESSAGE_REBIND: - message_type = N_DHCP4_MESSAGE_REQUEST; - break; - case N_DHCP4_C_MESSAGE_REBOOT: - message_type = N_DHCP4_MESSAGE_REQUEST; - via_packet_socket = true; - break; - case N_DHCP4_C_MESSAGE_RELEASE: - message_type = N_DHCP4_MESSAGE_RELEASE; - break; - case N_DHCP4_C_MESSAGE_DECLINE: - message_type = N_DHCP4_MESSAGE_DECLINE; - via_packet_socket = true; - break; - default: - abort(); - return -ENOTRECOVERABLE; - } - - /* - * We explicitly pass 0 as maximum message size, which makes - * NDhcp4Outgoing use the mandated default value from the spec (see its - * implementation). While the transport and like layers might support - * bigger MTUs (and we very likely know about them through - * n_dhcp4_client_update_mtu()), we cannot assume the target DHCP - * server supports parsing packets bigger than the minimum (and it is - * allowed to refuse bigger IP packets, even if the network supports - * transmission of them). - * - * We could theoretically increase this for packets other than the - * initial discovery. However, clients are unlikely to ever send large - * packets, so we just keep the same default for all outgoing packets. - */ - r = n_dhcp4_outgoing_new(&message, 0, N_DHCP4_OVERLOAD_FILE | N_DHCP4_OVERLOAD_SNAME); - if (r) - return r; - - header = n_dhcp4_outgoing_get_header(message); - n_dhcp4_c_connection_init_header(connection, header); - - message->userdata.type = type; - message->userdata.message_type = message_type; - - /* - * Note that some implementations expect the MESSAGE_TYPE option to be - * the first option, and possibly even hard-code access to it. Hence, - * we really should make sure to pass it first as well. - */ - r = n_dhcp4_outgoing_append(message, N_DHCP4_OPTION_MESSAGE_TYPE, &message_type, sizeof(message_type)); - if (r) - return r; - - r = n_dhcp4_outgoing_append(message, - N_DHCP4_OPTION_CLIENT_IDENTIFIER, - connection->client_config->client_id, - connection->client_config->n_client_id); - if (r) - return r; - - switch (message_type) { - case N_DHCP4_MESSAGE_DISCOVER: - case N_DHCP4_MESSAGE_REQUEST: - case N_DHCP4_MESSAGE_INFORM: { - uint16_t mtu; - - if (connection->probe_config->n_request_parameters > 0) { - r = n_dhcp4_outgoing_append(message, - N_DHCP4_OPTION_PARAMETER_REQUEST_LIST, - connection->probe_config->request_parameters, - connection->probe_config->n_request_parameters); - if (r) - return r; - } - - if (via_packet_socket) { - /* - * In case of packet sockets, we do not support - * fragmentation. Hence, our maximum message size - * equals the transport MTU. In case no mtu is given, - * we use the minimum size mandated by the IP spec. If - * we omit the field, some implementations will - * interpret this to mean any packet size is supported, - * which we rather not want as default behavior (we can - * always support suppressing this field, if that is - * what the caller wants). - */ - mtu = htons(connection->mtu ?: N_DHCP4_NETWORK_IP_MINIMUM_MAX_SIZE); - r = n_dhcp4_outgoing_append(message, N_DHCP4_OPTION_MAXIMUM_MESSAGE_SIZE, &mtu, sizeof(mtu)); - if (r) - return r; - } else { - /* - * Once we use UDP sockets, we support fragmentation - * through the kernel IP stack. This means, the biggest - * message we can receive is the maximum UDP size plus - * the possible IP header. This would sum up to - * 2^16-1 + 20 (or even 2^16-1 + 60 if pedantic) and - * thus exceed the option field. Hence, we simply set - * the option to the maximum possible value. - */ - mtu = htons(UINT16_MAX); - r = n_dhcp4_outgoing_append(message, N_DHCP4_OPTION_MAXIMUM_MESSAGE_SIZE, &mtu, sizeof(mtu)); - if (r) - return r; - } - - break; - } - default: - break; - } - - *messagep = message; - message = NULL; - return 0; -} - -/* - * RFC2131 3.1 - * - * The client broadcasts a DHCPDISCOVER message on its local physical - * subnet. The DHCPDISCOVER message MAY include options that suggest - * values for the network address and lease duration. BOOTP relay - * agents may pass the message on to DHCP servers not on the same - * physical subnet. - * - * RFC2131 3.5 - * - * [...] in its initial DHCPDISCOVER or DHCPREQUEST message, a client - * may provide the server with a list of specific parameters the - * client is interested in. If the client includes a list of - * parameters in a DHCPDISCOVER message, it MUST include that list in - * any subsequent DHCPREQUEST messages. - * - * [...] - * - * In addition, the client may suggest values for the network address - * and lease time in the DHCPDISCOVER message. The client may include - * the 'requested IP address' option to suggest that a particular IP - * address be assigned, and may include the 'IP address lease time' - * option to suggest the lease time it would like. Other options - * representing "hints" at configuration parameters are allowed in a - * DHCPDISCOVER or DHCPREQUEST message. - * - * RFC2131 4.4.1 - * - * The client generates and records a random transaction identifier and - * inserts that identifier into the 'xid' field. The client records its - * own local time for later use in computing the lease expiration. The - * client then broadcasts the DHCPDISCOVER on the local hardware - * broadcast address to the 0xffffffff IP broadcast address and 'DHCP - * server' UDP port. - * - * If the 'xid' of an arriving DHCPOFFER message does not match the - * 'xid' of the most recent DHCPDISCOVER message, the DHCPOFFER message - * must be silently discarded. Any arriving DHCPACK messages must be - * silently discarded. - */ -int n_dhcp4_c_connection_discover_new(NDhcp4CConnection *connection, - NDhcp4Outgoing **requestp) { - _c_cleanup_(n_dhcp4_outgoing_freep) NDhcp4Outgoing *message = NULL; - int r; - - r = n_dhcp4_c_connection_new_message(connection, &message, N_DHCP4_C_MESSAGE_DISCOVER); - if (r) - return r; - - *requestp = message; - message = NULL; - return 0; -} - -/* - * - * RFC2131 4.1.1 - * - * The DHCPREQUEST message contains the same 'xid' as the DHCPOFFER - * message. - * - * RFC2131 4.3.2 - * - * Client inserts the address of the selected server in 'server - * identifier', 'ciaddr' MUST be zero, 'requested IP address' MUST be - * filled in with the yiaddr value from the chosen DHCPOFFER. - */ -int n_dhcp4_c_connection_select_new(NDhcp4CConnection *connection, - NDhcp4Outgoing **requestp, - NDhcp4Incoming *offer) { - _c_cleanup_(n_dhcp4_outgoing_freep) NDhcp4Outgoing *message = NULL; - struct in_addr client; - struct in_addr server; - uint32_t xid; - int r; - - n_dhcp4_incoming_get_yiaddr(offer, &client); - - r = n_dhcp4_incoming_query_server_identifier(offer, &server); - if (r) - return r; - - r = n_dhcp4_c_connection_new_message(connection, &message, N_DHCP4_C_MESSAGE_SELECT); - if (r) - return r; - - r = n_dhcp4_outgoing_append(message, N_DHCP4_OPTION_REQUESTED_IP_ADDRESS, &client, sizeof(client)); - if (r) - return r; - - r = n_dhcp4_outgoing_append(message, N_DHCP4_OPTION_SERVER_IDENTIFIER, &server, sizeof(server)); - if (r) - return r; - - /* - * SELECT continues the transaction started by DISCOVER, and as such - * keeps the same start time. We also have to preserve the base time - * of the selected lease as well as the transaction ID. - */ - message->userdata.start_time = offer->userdata.start_time; - message->userdata.base_time = offer->userdata.base_time; - message->userdata.client_addr = client.s_addr; - n_dhcp4_incoming_get_xid(offer, &xid); - n_dhcp4_outgoing_set_xid(message, xid); - - *requestp = message; - message = NULL; - return 0; -} - -/* - * RFC2131 4.3.2 - * - * 'server identifier' MUST NOT be filled in, 'requested IP address' - * option MUST be filled in with client's notion of its previously - * assigned address. 'ciaddr' MUST be zero. The client is seeking to - * verify a previously allocated, cached configuration. Server SHOULD - * send a DHCPNAK message to the client if the 'requested IP address' - * is incorrect, or is on the wrong network. - */ -int n_dhcp4_c_connection_reboot_new(NDhcp4CConnection *connection, - NDhcp4Outgoing **requestp, - const struct in_addr *client) { - _c_cleanup_(n_dhcp4_outgoing_freep) NDhcp4Outgoing *message = NULL; - int r; - - r = n_dhcp4_c_connection_new_message(connection, &message, N_DHCP4_C_MESSAGE_REBOOT); - if (r) - return r; - - r = n_dhcp4_outgoing_append(message, N_DHCP4_OPTION_REQUESTED_IP_ADDRESS, client, sizeof(*client)); - if (r) - return r; - - *requestp = message; - message = NULL; - return 0; -} - -/* - * RFC2131 4.3.2 - * - * 'server identifier' MUST NOT be filled in, 'requested IP address' - * option MUST NOT be filled in, 'ciaddr' MUST be filled in with - * client's IP address. In this situation, the client is completely - * configured, and is trying to extend its lease. This message will - * be unicast, so no relay agents will be involved in its - * transmission. Because 'giaddr' is therefore not filled in, the - * DHCP server will trust the value in 'ciaddr', and use it when - * replying to the client. - * - * A client MAY choose to renew or extend its lease prior to T1. The - * server may choose not to extend the lease (as a policy decision by - * the network administrator), but should return a DHCPACK message - * regardless. - * - * RFC2131 4.4.5 - * - * At time T1 the client moves to RENEWING state and sends (via unicast) - * a DHCPREQUEST message to the server to extend its lease. The client - * sets the 'ciaddr' field in the DHCPREQUEST to its current network - * address. The client records the local time at which the DHCPREQUEST - * message is sent for computation of the lease expiration time. The - * client MUST NOT include a 'server identifier' in the DHCPREQUEST - * message. - */ -int n_dhcp4_c_connection_renew_new(NDhcp4CConnection *connection, - NDhcp4Outgoing **requestp) { - _c_cleanup_(n_dhcp4_outgoing_freep) NDhcp4Outgoing *message = NULL; - int r; - - r = n_dhcp4_c_connection_new_message(connection, &message, N_DHCP4_C_MESSAGE_RENEW); - if (r) - return r; - - message->userdata.client_addr = connection->client_ip; - *requestp = message; - message = NULL; - return 0; -} - -/* - * RFC2131 4.3.2 - * - * 'server identifier' MUST NOT be filled in, 'requested IP address' - * option MUST NOT be filled in, 'ciaddr' MUST be filled in with - * client's IP address. In this situation, the client is completely - * configured, and is trying to extend its lease. This message MUST - * be broadcast to the 0xffffffff IP broadcast address. The DHCP - * server SHOULD check 'ciaddr' for correctness before replying to - * the DHCPREQUEST. - * - * RFC2131 4.4.5 - * - * If no DHCPACK arrives before time T2, the client moves to REBINDING - * state and sends (via broadcast) a DHCPREQUEST message to extend its - * lease. The client sets the 'ciaddr' field in the DHCPREQUEST to its - * current network address. The client MUST NOT include a 'server - * identifier' in the DHCPREQUEST message. - */ -int n_dhcp4_c_connection_rebind_new(NDhcp4CConnection *connection, - NDhcp4Outgoing **requestp) { - _c_cleanup_(n_dhcp4_outgoing_freep) NDhcp4Outgoing *message = NULL; - int r; - - r = n_dhcp4_c_connection_new_message(connection, &message, N_DHCP4_C_MESSAGE_REBIND); - if (r) - return r; - - message->userdata.client_addr = connection->client_ip; - *requestp = message; - message = NULL; - return 0; -} - -/* - * RFC2131 3.2 - * - * If the client detects that the IP address in the DHCPACK message - * is already in use, the client MUST send a DHCPDECLINE message to the - * server and restarts the configuration process by requesting a - * new network address. - * - * RFC2131 4.4.4 - * - * Because the client is declining the use of the IP address supplied by - * the server, the client broadcasts DHCPDECLINE messages. - */ -int n_dhcp4_c_connection_decline_new(NDhcp4CConnection *connection, - NDhcp4Outgoing **requestp, - NDhcp4Incoming *ack, - const char *error) { - _c_cleanup_(n_dhcp4_outgoing_freep) NDhcp4Outgoing *message = NULL; - struct in_addr client; - struct in_addr server; - int r; - - n_dhcp4_incoming_get_yiaddr(ack, &client); - - r = n_dhcp4_incoming_query_server_identifier(ack, &server); - if (r) - return r; - - r = n_dhcp4_c_connection_new_message(connection, &message, N_DHCP4_C_MESSAGE_DECLINE); - if (r) - return r; - - r = n_dhcp4_outgoing_append(message, N_DHCP4_OPTION_REQUESTED_IP_ADDRESS, &client, sizeof(client)); - if (r) - return r; - - r = n_dhcp4_outgoing_append(message, N_DHCP4_OPTION_SERVER_IDENTIFIER, &server, sizeof(server)); - if (r) - return r; - - if (error) { - r = n_dhcp4_outgoing_append(message, N_DHCP4_OPTION_ERROR_MESSAGE, error, strlen(error) + 1); - if (r) - return r; - } - - message->userdata.client_addr = client.s_addr; - *requestp = message; - message = NULL; - return 0; -} - -/* - * RFC2131 3.4 - * - * If a client has obtained a network address through some other means - * (e.g., manual configuration), it may use a DHCPINFORM request message - * to obtain other local configuration parameters. - * - * RFC2131 4.4 - * - * The DHCPINFORM message is not shown in figure 5. A client simply - * sends the DHCPINFORM and waits for DHCPACK messages. Once the client - * has selected its parameters, it has completed the configuration - * process. - * - * RFC2131 4.4.3 - * - * The client sends a DHCPINFORM message. The client may request - * specific configuration parameters by including the 'parameter request - * list' option. The client generates and records a random transaction - * identifier and inserts that identifier into the 'xid' field. The - * client places its own network address in the 'ciaddr' field. The - * client SHOULD NOT request lease time parameters. - * - * The client then unicasts the DHCPINFORM to the DHCP server if it - * knows the server's address, otherwise it broadcasts the message to - * the limited (all 1s) broadcast address. DHCPINFORM messages MUST be - * directed to the 'DHCP server' UDP port. - */ -int n_dhcp4_c_connection_inform_new(NDhcp4CConnection *connection, - NDhcp4Outgoing **requestp) { - _c_cleanup_(n_dhcp4_outgoing_freep) NDhcp4Outgoing *message = NULL; - int r; - - r = n_dhcp4_c_connection_new_message(connection, &message, N_DHCP4_C_MESSAGE_INFORM); - if (r) - return r; - - message->userdata.client_addr = connection->client_ip; - *requestp = message; - message = NULL; - return 0; -} - -/* - * RFC2131 3.1 - * - * The client may choose to relinquish its lease on a network address - * by sending a DHCPRELEASE message to the server. The client - * identifies the lease to be released with its 'client identifier', - * or 'chaddr' and network address in the DHCPRELEASE message. If the - * client used a 'client identifier' when it obtained the lease, it - * MUST use the same 'client identifier' in the DHCPRELEASE message. - * - * RFC2131 3.2 - * - * The client may choose to relinquish its lease on a network - * address by sending a DHCPRELEASE message to the server. The - * client identifies the lease to be released with its - * 'client identifier', or 'chaddr' and network address in the - * DHCPRELEASE message. - * - * Note that in this case, where the client retains its network - * address locally, the client will not normally relinquish its - * lease during a graceful shutdown. Only in the case where the - * client explicitly needs to relinquish its lease, e.g., the client - * is about to be moved to a different subnet, will the client send - * a DHCPRELEASE message. - * - * RFC2131 4.4.4 - * - * The client unicasts DHCPRELEASE messages to the server. - * - * RFC2131 4.4.6 - * - * If the client no longer requires use of its assigned network address - * (e.g., the client is gracefully shut down), the client sends a - * DHCPRELEASE message to the server. Note that the correct operation - * of DHCP does not depend on the transmission of DHCPRELEASE messages. - */ -int n_dhcp4_c_connection_release_new(NDhcp4CConnection *connection, - NDhcp4Outgoing **requestp, - const char *error) { - _c_cleanup_(n_dhcp4_outgoing_freep) NDhcp4Outgoing *message = NULL; - int r; - - r = n_dhcp4_c_connection_new_message(connection, &message, N_DHCP4_C_MESSAGE_RELEASE); - if (r) - return r; - - r = n_dhcp4_outgoing_append(message, N_DHCP4_OPTION_SERVER_IDENTIFIER, &connection->server_ip, sizeof(connection->server_ip)); - if (r) - return r; - - if (error) { - r = n_dhcp4_outgoing_append(message, N_DHCP4_OPTION_ERROR_MESSAGE, error, strlen(error) + 1); - if (r) - return r; - } - - *requestp = message; - message = NULL; - return 0; -} - -static const char *message_type_to_str(uint8_t type) { - switch (type) { - case N_DHCP4_MESSAGE_DISCOVER: - return "DISCOVER"; - case N_DHCP4_MESSAGE_OFFER: - return "OFFER"; - case N_DHCP4_MESSAGE_REQUEST: - return "REQUEST"; - case N_DHCP4_MESSAGE_DECLINE: - return "DECLINE"; - case N_DHCP4_MESSAGE_ACK: - return "ACK"; - case N_DHCP4_MESSAGE_NAK: - return "NACK"; - case N_DHCP4_MESSAGE_RELEASE: - return "RELEASE"; - case N_DHCP4_MESSAGE_INFORM: - return "INFORM"; - case N_DHCP4_MESSAGE_FORCERENEW: - return "FORCERENEW"; - default: - return "UNKNOWN"; - } -} - -static int n_dhcp4_c_connection_send_request(NDhcp4CConnection *connection, - NDhcp4Outgoing *request, - uint64_t timestamp) { - char server_addr[INET_ADDRSTRLEN]; - char client_addr[INET_ADDRSTRLEN]; - char error_msg[128]; - int r; - bool broadcast = false; - - /* - * Increment the base time and reset the xid field, - * where applicable. We never alter the header on - * resends of SELECT, as it must always match the - * OFFER message they are in reply to. - */ - switch (request->userdata.type) { - case N_DHCP4_C_MESSAGE_DISCOVER: - case N_DHCP4_C_MESSAGE_INFORM: - case N_DHCP4_C_MESSAGE_REBOOT: - case N_DHCP4_C_MESSAGE_REBIND: - case N_DHCP4_C_MESSAGE_RENEW: - request->userdata.base_time = timestamp; - n_dhcp4_outgoing_set_xid(request, n_dhcp4_client_probe_config_get_random(connection->probe_config)); - - break; - case N_DHCP4_C_MESSAGE_SELECT: - case N_DHCP4_C_MESSAGE_DECLINE: - case N_DHCP4_C_MESSAGE_RELEASE: - break; - default: - c_assert(0); - } - - request->userdata.send_time = timestamp; - request->userdata.send_jitter = (n_dhcp4_client_probe_config_get_random(connection->probe_config) % 1000000000ULL); - n_dhcp4_c_connection_outgoing_set_secs(request); - - switch (request->userdata.type) { - case N_DHCP4_C_MESSAGE_DISCOVER: - case N_DHCP4_C_MESSAGE_SELECT: - case N_DHCP4_C_MESSAGE_REBOOT: - case N_DHCP4_C_MESSAGE_DECLINE: - case N_DHCP4_C_MESSAGE_REBIND: - broadcast = true; - r = n_dhcp4_c_connection_packet_broadcast(connection, request); - break; - case N_DHCP4_C_MESSAGE_INFORM: - broadcast = true; - r = n_dhcp4_c_connection_udp_broadcast(connection, request); - break; - case N_DHCP4_C_MESSAGE_RENEW: - case N_DHCP4_C_MESSAGE_RELEASE: - r = n_dhcp4_c_connection_udp_send(connection, request); - break; - default: - c_assert(0); - } - - if (r) { - snprintf(error_msg, sizeof(error_msg), ": error %d", r); - } else { - error_msg[0] = '\0'; - } - - if (request->userdata.client_addr == INADDR_ANY) { - n_dhcp4_log(connection->log_queue, - LOG_INFO, - "send %s to %s%s", - message_type_to_str(request->userdata.message_type), - broadcast ? - "255.255.255.255" : - inet_ntop(AF_INET, &connection->server_ip, - server_addr, sizeof(server_addr)), - error_msg); - } else { - n_dhcp4_log(connection->log_queue, - LOG_INFO, - "send %s of %s to %s%s", - message_type_to_str(request->userdata.message_type), - inet_ntop(AF_INET, &request->userdata.client_addr, - client_addr, sizeof(client_addr)), - broadcast ? - "255.255.255.255" : - inet_ntop(AF_INET, &connection->server_ip, - server_addr, sizeof(server_addr)), - error_msg); - } - - ++request->userdata.n_send; - return 0; -} - -int n_dhcp4_c_connection_start_request(NDhcp4CConnection *connection, - NDhcp4Outgoing *request, - uint64_t timestamp) { - int r; - - /* - * This function starts a request, but in the case of SELECT it - * continues a previous transaction, so we do not want to reset - * the start time. Only set the start time if it was not already - * set. - */ - if (request->userdata.start_time == 0) - request->userdata.start_time = timestamp; - - connection->request = n_dhcp4_outgoing_free(connection->request); - - r = n_dhcp4_c_connection_send_request(connection, request, timestamp); - if (r) - return r; - - connection->request = request; - - return 0; -} - -int n_dhcp4_c_connection_dispatch_timer(NDhcp4CConnection *connection, - uint64_t timestamp) { - uint64_t timeout; - int r; - - if (!connection->request) - return 0; - - n_dhcp4_c_connection_get_timeout(connection, &timeout); - - if (timeout > timestamp) - return 0; - - r = n_dhcp4_c_connection_send_request(connection, connection->request, timestamp); - if (r) - return r; - - return 0; -} - -/* - * Returns: - * 0 on success - * N_DHCP4_E_MALFORMED if a malformed packet was received - * N_DHCP4_E_UNEXPECTED if the packet received contains unexpected data - * N_DHCP4_E_AGAIN if there was another error (non fatal for the client) - */ -int n_dhcp4_c_connection_dispatch_io(NDhcp4CConnection *connection, - NDhcp4Incoming **messagep) { - _c_cleanup_(n_dhcp4_incoming_freep) NDhcp4Incoming *message = NULL; - char serv_addr[INET_ADDRSTRLEN]; - char client_addr[INET_ADDRSTRLEN]; - uint8_t type; - int r; - - switch (connection->state) { - case N_DHCP4_C_CONNECTION_STATE_PACKET: - r = n_dhcp4_c_socket_packet_recv(connection->fd_packet, - connection->scratch_buffer, - sizeof(connection->scratch_buffer), - &message); - if (!r) - break; - else if (r == N_DHCP4_E_MALFORMED) - return r; - return N_DHCP4_E_AGAIN; - case N_DHCP4_C_CONNECTION_STATE_DRAINING: - r = n_dhcp4_c_socket_packet_recv(connection->fd_packet, - connection->scratch_buffer, - sizeof(connection->scratch_buffer), - &message); - if (!r) - break; - else if (r == N_DHCP4_E_MALFORMED) - return r; - else if (r != N_DHCP4_E_AGAIN) - return N_DHCP4_E_AGAIN; - - /* - * The UDP socket is open and the packet socket has been shut down - * and drained, clean up the packet socket and fall through to - * dispatching the UDP socket. - */ - r = epoll_ctl(connection->fd_epoll, EPOLL_CTL_DEL, connection->fd_packet, NULL); - c_assert(!r); - connection->fd_packet = c_close(connection->fd_packet); - connection->state = N_DHCP4_C_CONNECTION_STATE_UDP; - - /* fall-through */ - case N_DHCP4_C_CONNECTION_STATE_UDP: - r = n_dhcp4_c_socket_udp_recv(connection->fd_udp, - connection->scratch_buffer, - sizeof(connection->scratch_buffer), - &message); - if (!r) - break; - else if (r == N_DHCP4_E_MALFORMED) - return r; - return N_DHCP4_E_AGAIN; - default: - abort(); - return -ENOTRECOVERABLE; - } - - r = n_dhcp4_c_connection_verify_incoming(connection, message, &type); - if (r == N_DHCP4_E_MALFORMED || r == N_DHCP4_E_UNEXPECTED) - return r; - else if (r != 0) - return N_DHCP4_E_AGAIN; - - if (type == N_DHCP4_MESSAGE_OFFER || type == N_DHCP4_MESSAGE_ACK) { - n_dhcp4_log(connection->log_queue, - LOG_INFO, - "received %s of %s from %s", - message_type_to_str(type), - inet_ntop(AF_INET, &message->message.header.yiaddr, - client_addr, sizeof(client_addr)), - inet_ntop(AF_INET, &message->message.header.siaddr, - serv_addr, sizeof(serv_addr))); - } else { - n_dhcp4_log(connection->log_queue, - LOG_INFO, - "received %s from %s", - message_type_to_str(type), - inet_ntop(AF_INET, &message->message.header.siaddr, - serv_addr, sizeof(serv_addr))); - } - - switch (type) { - case N_DHCP4_MESSAGE_OFFER: - case N_DHCP4_MESSAGE_ACK: - case N_DHCP4_MESSAGE_NAK: - /* - * Remember the start time of the transaction, and the base - * time of any relative timestamps from the pending request. - * The same time applies to the response, and should be - * copied over. - */ - message->userdata.start_time = connection->request->userdata.start_time; - message->userdata.base_time = connection->request->userdata.base_time; - - if (type != N_DHCP4_MESSAGE_OFFER) { - /* - * We only allow one reply to ACK or NAK, but for OFFER we must - * accept several, so we do not free the pinned request. - */ - connection->request = n_dhcp4_outgoing_free(connection->request); - } - - break; - default: - break; - } - - *messagep = message; - message = NULL; - return 0; -} diff --git a/shared/n-dhcp4/src/n-dhcp4-c-lease.c b/shared/n-dhcp4/src/n-dhcp4-c-lease.c deleted file mode 100644 index 515814d4..00000000 --- a/shared/n-dhcp4/src/n-dhcp4-c-lease.c +++ /dev/null @@ -1,405 +0,0 @@ -/* - * DHCP4 Client Leases - * - * This implements the public API wrapping DHCP4 client leases. A lease object - * consists of the information given to us from the server, together with the - * timestamp recording the start of the validity of the lease. - * - * A probe may yield many OFFERS, each of which contains a lease object. One of - * these offers may be SELECTED, which implicitly rejects all the others. - * The server may then ACK or NAK the lease which tells us whether or not we - * are permitted to start using it. Once an ACK has been received, we can - * configure the address, and only then can we SELECT the lease. If we - * determine that the offered lease was not appropriate after all we - * may DECLINE it instead. - */ - -#include <assert.h> -#include <c-list.h> -#include <c-stdaux.h> -#include <errno.h> -#include <stdlib.h> -#include <string.h> -#include "n-dhcp4.h" -#include "n-dhcp4-private.h" - -/* - * Compute the absolute timeouts from an incoming message. A message contains relative timeouts and the userdata - * of the incoming message is set to the offset we must apply to get the absolute values. - * - * The special value UINT64_MAX is returned to indicate no or infinite timeouts. In case the given timeouts - * are invalid relative to each other, we recompute T1 and/or T2 to take their default values. Later timeouts - * take predecende above earlier ones (T1 is adjusted if it conflicts with T2, etc). - */ -static int n_dhcp4_incoming_get_timeouts(NDhcp4Incoming *message, uint64_t *t1p, uint64_t *t2p, uint64_t *lifetimep) { - uint64_t lifetime, t2, t1; - uint32_t u32; - int r; - - r = n_dhcp4_incoming_query_lifetime(message, &u32); - if (r == N_DHCP4_E_UNSET) { - lifetime = UINT64_MAX; - } else if (r) { - return r; - } else if (u32 == UINT32_MAX) { - lifetime = UINT64_MAX; - } else { - lifetime = u32 * (1000000000ULL); - } - - r = n_dhcp4_incoming_query_t2(message, &u32); - if (r == N_DHCP4_E_UNSET) { - if (lifetime == UINT64_MAX) - t2 = UINT64_MAX; - else - t2 = (lifetime * 7) / 8; - } else if (r) { - return r; - } else { - if (u32 == UINT32_MAX) - t2 = UINT64_MAX; - else - t2 = u32 * (1000000000ULL); - - if (t2 > lifetime) - t2 = (lifetime * 7) / 8; - } - - r = n_dhcp4_incoming_query_t1(message, &u32); - if (r == N_DHCP4_E_UNSET) { - if (t2 == UINT64_MAX) - t1 = UINT64_MAX; - else - t1 = (t2 * 4) / 7; - } else if (r) { - return r; - } else { - if (u32 == UINT32_MAX) - t1 = UINT64_MAX; - else - t1 = u32 * (1000000000ULL); - - if (t1 > t2) - t1 = (t2 * 4) / 7; - } - - if (lifetime != UINT64_MAX) - lifetime += message->userdata.base_time; - if (t2 != UINT64_MAX) - t2 += message->userdata.base_time; - if (t1 != UINT64_MAX) - t1 += message->userdata.base_time; - - *lifetimep = lifetime; - *t2p = t2; - *t1p = t1; - return 0; -} - -/** - * n_dhcp4_client_lease_new() - allocate new client lease object - * @leasep: output argument for new client lease object - * @message: incoming message representing the lease - * - * This creates a new client lease object. Client lease objects are simple - * wrappers around an incoming message representing a lease. - * - * Return: 0 on success, negative error code on failure. - */ -int n_dhcp4_client_lease_new(NDhcp4ClientLease **leasep, NDhcp4Incoming *message) { - _c_cleanup_(n_dhcp4_client_lease_unrefp) NDhcp4ClientLease *lease = NULL; - int r; - - c_assert(leasep); - - lease = malloc(sizeof(*lease)); - if (!lease) - return -ENOMEM; - - *lease = (NDhcp4ClientLease)N_DHCP4_CLIENT_LEASE_NULL(*lease); - - r = n_dhcp4_incoming_get_timeouts(message, &lease->t1, &lease->t2, &lease->lifetime); - if (r) - return r; - - lease->message = message; - *leasep = lease; - lease = NULL; - return 0; -} - -static void n_dhcp4_client_lease_free(NDhcp4ClientLease *lease) { - n_dhcp4_client_lease_unlink(lease); - n_dhcp4_incoming_free(lease->message); - free(lease); -} - -/** - * n_dhcp4_client_lease_ref() - reference client lease - * @lease: the client lease object to reference - * - * Take a new reference to a client lease. - * - * Return: the lease. - */ -_c_public_ NDhcp4ClientLease *n_dhcp4_client_lease_ref(NDhcp4ClientLease *lease) { - if (lease) - ++lease->n_refs; - return lease; -} - -/** - * n_dhcp4_client_lease_unref() - dereference client lease - * @lease: the client lease object to dereference - * - * Relase a reference to a client lease. - * - * Return: NULL. - */ -_c_public_ NDhcp4ClientLease *n_dhcp4_client_lease_unref(NDhcp4ClientLease *lease) { - if (lease && !--lease->n_refs) - n_dhcp4_client_lease_free(lease); - return NULL; -} - -/** - * n_dhcp4_client_lease_link() - link lease into probe - * @lease: the lease to operate on - * @probe: the probe to link the lease into - * - * Associate a lease with a probe. The lease may not already be linked. - */ -void n_dhcp4_client_lease_link(NDhcp4ClientLease *lease, NDhcp4ClientProbe *probe) { - c_assert(!lease->probe); - c_assert(!c_list_is_linked(&lease->probe_link)); - - lease->probe = probe; - c_list_link_tail(&probe->lease_list, &lease->probe_link); -} - -/** - * n_dhcp4_client_lease_unlink() - unlinke lease from its probe - * @lease: the lease to operate on - * - * Dissassociate a lease from a probe if it is associated with one. Otherwise, - * this is a noop. - */ -void n_dhcp4_client_lease_unlink(NDhcp4ClientLease *lease) { - lease->probe = NULL; - c_list_unlink(&lease->probe_link); -} - -/** - * n_dhcp4_client_lease_get_yiaddr() - get the IP address - * @lease: the lease to operate on - * @yiaddr: return argument for the IP address - * - * Gets the IP address contained in the lease. Or INADDR_ANY if the lease - * does not contain an IP address. - */ -_c_public_ void n_dhcp4_client_lease_get_yiaddr(NDhcp4ClientLease *lease, struct in_addr *yiaddr) { - NDhcp4Header *header = n_dhcp4_incoming_get_header(lease->message); - - yiaddr->s_addr = header->yiaddr; -} - -/** - * n_dhcp4_client_lease_get_siaddr() - get the server IP address - * @lease: the lease to operate on - * @siaddr: return argument for the IP address - * - * Gets the server IP address contained in the lease. Or INADDR_ANY if the - * lease does not contain an IP address. - */ -_c_public_ void n_dhcp4_client_lease_get_siaddr(NDhcp4ClientLease *lease, struct in_addr *siaddr) { - NDhcp4Header *header = n_dhcp4_incoming_get_header(lease->message); - - siaddr->s_addr = header->siaddr; -} - -/** - * n_dhcp4_client_lease_get_basetime() - get the timestamp when the lease was received. - * @lease: the lease to operate on - * @ns_basetimep: return argument for the base time in nano seconds - * - * Gets the timestamp when the lease was received in CLOCK_BOOTTIME. This - * is also the base timestamp for the expiration of the lifetime and t1/t2. - */ -_c_public_ void n_dhcp4_client_lease_get_basetime(NDhcp4ClientLease *lease, uint64_t *ns_basetimep) { - *ns_basetimep = lease->message->userdata.base_time; -} - -/** - * n_dhcp4_client_lease_get_lifetime() - get the lifetime - * @lease: the lease to operate on - * @ns_lifetimep: return argument for the lifetime in nano seconds - * - * Gets the end of the lease's lifetime in nanoseconds according to CLOCK_BOOTTIME, - * or (uint64_t)-1 for permanent leases. - */ -_c_public_ void n_dhcp4_client_lease_get_lifetime(NDhcp4ClientLease *lease, uint64_t *ns_lifetimep) { - *ns_lifetimep = lease->lifetime; -} - -/** - * n_dhcp4_client_lease_get_server_identifier() - get the server identifier - * @lease: the lease to operate on - * @addr: return argument for the server identifier - * - * Gets the address contained in the server-identifier DHCP option, in network - * byte order. - * - * Return: 0 on success, negative error code on failure. - */ -_c_public_ int n_dhcp4_client_lease_get_server_identifier (NDhcp4ClientLease *lease, struct in_addr *addr) { - uint8_t *data; - size_t n_data; - int r; - - r = n_dhcp4_incoming_query(lease->message, N_DHCP4_OPTION_SERVER_IDENTIFIER, &data, &n_data); - if (r) - return r; - if (n_data < sizeof(struct in_addr)) - return N_DHCP4_E_MALFORMED; - - memcpy(addr, data, sizeof(struct in_addr)); - - return 0; -} - -/** - * n_dhcp4_client_lease_query() - query the lease for an option - * @lease: the lease to operate on - * @option: the DHCP4 option code - * @datap: return argument of the data pointer - * @n_datap: return argument of data length in bytes - * - * Query the lease for a given option. Options internal to the DHCP protocol cannot - * be queried, and only options that were explicitly requested can be queried. - * - * Return: 0 on success, - * N_DCHP4_E_INTERNAL if an invalid option is queried, - * N_DHCP4_E_UNSET if the lease did not contain the option, or - * a negative error code on failure. - */ -_c_public_ int n_dhcp4_client_lease_query(NDhcp4ClientLease *lease, uint8_t option, uint8_t **datap, size_t *n_datap) { - switch (option) { - case N_DHCP4_OPTION_PAD: - case N_DHCP4_OPTION_REQUESTED_IP_ADDRESS: - case N_DHCP4_OPTION_IP_ADDRESS_LEASE_TIME: - case N_DHCP4_OPTION_OVERLOAD: - case N_DHCP4_OPTION_MESSAGE_TYPE: - case N_DHCP4_OPTION_SERVER_IDENTIFIER: - case N_DHCP4_OPTION_PARAMETER_REQUEST_LIST: - case N_DHCP4_OPTION_ERROR_MESSAGE: - case N_DHCP4_OPTION_MAXIMUM_MESSAGE_SIZE: - case N_DHCP4_OPTION_RENEWAL_T1_TIME: - case N_DHCP4_OPTION_REBINDING_T2_TIME: - case N_DHCP4_OPTION_END: - return N_DHCP4_E_INTERNAL; - } - - /* XXX: refuse to return options that were not requested */ - - return n_dhcp4_incoming_query(lease->message, option, datap, n_datap); -} - -/** - * n_dhcp4_client_lease_select() - select an offered lease - * @lease: lease to operate on - * - * Select a lease. This must be a lease that was offered, once - * one of the leases that were offered in response to a probe was - * selected none of the others can be. - * - * Return: 0 on success, or a negative error code on failure. - */ -_c_public_ int n_dhcp4_client_lease_select(NDhcp4ClientLease *lease) { - NDhcp4ClientLease *l, *t_l; - NDhcp4ClientProbe *probe; - int r; - - /* XXX error handling, this must be an OFFER */ - - if (!lease->probe) - return -ENOTRECOVERABLE; - if (lease->probe->current_lease) - return -ENOTRECOVERABLE; - - r = n_dhcp4_client_probe_transition_select(lease->probe, lease->message, n_dhcp4_gettime(CLOCK_BOOTTIME)); - if (r) - return r; - - /* - * Only one of the offered leases can be selected, so flush the list. - * All offered lease, including this one are now dead. - */ - probe = lease->probe; - c_list_for_each_entry_safe(l, t_l, &probe->lease_list, probe_link) - n_dhcp4_client_lease_unlink(l); - - return 0; -} - -/** - * n_dhcp4_client_lease_accept() - accept an ack'ed lease - * @lease: lease to operate on - * - * Accept a lease. This must be a lease that was ack'ed by the - * server. - * - * The offered IP address must be fully configured before the lease - * can be accepted. - * - * Return: 0 on success, or a negative error code on failure. - */ -_c_public_ int n_dhcp4_client_lease_accept(NDhcp4ClientLease *lease) { - int r; - - /* XXX error handling, this must be an ACK */ - - if (!lease->probe) - return -ENOTRECOVERABLE; - if (lease->probe->current_lease != lease) - return -ENOTRECOVERABLE; - - r = n_dhcp4_client_probe_transition_accept(lease->probe, lease->message); - if (r) - return r; - - n_dhcp4_client_lease_unlink(lease); - - return 0; -} - -/** - * n_dhcp4_client_lease_decline() - decline an ack'ed lease - * @lease: lease to operate on - * - * Decline a lease. This must be a lease that was ack'ed by the - * server. - * - * The offered IP address must not be used once the lease has been - * decline. - * - * Return: 0 on success, or a negative error code on failure. - */ -_c_public_ int n_dhcp4_client_lease_decline(NDhcp4ClientLease *lease, const char *error) { - int r; - - /* XXX: error handling, this must be an ACK */ - - if (!lease->probe) - return -ENOTRECOVERABLE; - if (lease->probe->current_lease != lease) - return -ENOTRECOVERABLE; - - r = n_dhcp4_client_probe_transition_decline(lease->probe, lease->message, error, n_dhcp4_gettime(CLOCK_BOOTTIME)); - if (r) - return r; - - lease->probe->current_lease = n_dhcp4_client_lease_unref(lease->probe->current_lease); - n_dhcp4_client_lease_unlink(lease); - - return 0; -} diff --git a/shared/n-dhcp4/src/n-dhcp4-c-probe.c b/shared/n-dhcp4/src/n-dhcp4-c-probe.c deleted file mode 100644 index 7f20ac05..00000000 --- a/shared/n-dhcp4/src/n-dhcp4-c-probe.c +++ /dev/null @@ -1,1294 +0,0 @@ -/* - * DHCPv4 Client Probes - * - * The probe object is used to represent the lifetime of a DHCP client session. - * A running probe discovers DHCP servers, requests a lease, and maintains that - * lease. - */ - -#include <assert.h> -#include <c-list.h> -#include <c-siphash.h> -#include <c-stdaux.h> -#include <errno.h> -#include <inttypes.h> -#include <stdbool.h> -#include <stdlib.h> -#include <string.h> -#include <sys/auxv.h> -#include "n-dhcp4.h" -#include "n-dhcp4-private.h" - - -static int n_dhcp4_client_probe_option_new(NDhcp4ClientProbeOption **optionp, - uint8_t option, - const void *data, - uint8_t n_data) { - NDhcp4ClientProbeOption *op; - - op = malloc(sizeof(*op) + n_data); - if (!op) - return -ENOMEM; - - op->option = option; - op->n_data = n_data; - memcpy(op->data, data, n_data); - - *optionp = op; - return 0; -} - -static void n_dhcp4_client_probe_option_free(NDhcp4ClientProbeOption *option) { - if (option) - free(option); -} - -/** - * n_dhcp4_client_probe_config_new() - create new probe configuration - * @configp: output argument to store new configuration - * - * This creates a new probe configuration object. The object is a collection of - * parameters for probes. No data verification is done by the configuration - * object. Instead, when passing the configuration to the constructor of a - * probe, this constructor will perform parameter validation. - * - * A probe configuration is an unlinked object only used to pass information to - * a probe constructor. The caller fully owns the returned configuration object - * and is responsible to free it when no longer needed. - * - * Return: 0 on success, negative error code on failure. - */ -_c_public_ int n_dhcp4_client_probe_config_new(NDhcp4ClientProbeConfig **configp) { - _c_cleanup_(n_dhcp4_client_probe_config_freep) NDhcp4ClientProbeConfig *config = NULL; - - config = calloc(1, sizeof(*config)); - if (!config) - return -ENOMEM; - - *config = (NDhcp4ClientProbeConfig)N_DHCP4_CLIENT_PROBE_CONFIG_NULL(*config); - - *configp = config; - config = NULL; - return 0; -} - -/** - * n_dhcp4_client_probe_config_free() - destroy probe configuration - * @config: configuration to operate on, or NULL - * - * This destroys a probe configuration object and deallocates all its - * resources. - * - * If @config is NULL, this is a no-op. - * - * Return: NULL is returned. - */ -_c_public_ NDhcp4ClientProbeConfig *n_dhcp4_client_probe_config_free(NDhcp4ClientProbeConfig *config) { - if (!config) - return NULL; - - for (unsigned int i = 0; i <= UINT8_MAX; ++i) - n_dhcp4_client_probe_option_free(config->options[i]); - - free(config); - - return NULL; -} - -/** - * n_dhcp4_client_probe_config_dup() - duplicate probe configuration - * @config: configuration to operate on - * @dupp: output argument for duplicate - * - * This duplicates the probe configuration given as @config and returns it in - * @dupp to the caller. - * - * Return: 0 on success, negative error code on failure. - */ -int n_dhcp4_client_probe_config_dup(NDhcp4ClientProbeConfig *config, - NDhcp4ClientProbeConfig **dupp) { - _c_cleanup_(n_dhcp4_client_probe_config_freep) NDhcp4ClientProbeConfig *dup = NULL; - int r; - - r = n_dhcp4_client_probe_config_new(&dup); - if (r) - return r; - - dup->inform_only = config->inform_only; - dup->init_reboot = config->init_reboot; - dup->requested_ip = config->requested_ip; - dup->ms_start_delay = config->ms_start_delay; - - for (unsigned int i = 0; i < config->n_request_parameters; ++i) - dup->request_parameters[dup->n_request_parameters++] = config->request_parameters[i]; - - for (unsigned int i = 0; i <= UINT8_MAX; ++i) { - if (!config->options[i]) - break; - - r = n_dhcp4_client_probe_option_new(&dup->options[i], - config->options[i]->option, - config->options[i]->data, - config->options[i]->n_data); - if (r) - return r; - } - - *dupp = dup; - dup = NULL; - return 0; -} - -/** - * n_dhcp4_client_probe_config_set_inform_only() - set inform-only property - * @config: configuration to operate on - * @inform_only: value to set - * - * This sets the inform-only property of the given configuration object. This - * property controls whether the client probe should request a full lease, or - * whether it should just ask for auxiliary information without requesting an - * address. - * - * The default is to request a full lease and address. If inform-only is set to - * true, only auxiliary information will be requested. - * - * XXX: This is currently not implemented, and setting the property has no effect. - */ -_c_public_ void n_dhcp4_client_probe_config_set_inform_only(NDhcp4ClientProbeConfig *config, bool inform_only) { - config->inform_only = inform_only; -} - -/** - * n_dhcp4_client_probe_config_set_init_reboot() - set init-reboot property - * @config: configuration to operate on - * @init_reboot: value to set - * - * This sets the init-reboot property of the given configuration object. If this - * is enabled, a requested IP address must also be set. - * - * The default is false. If set to true, a probe will make use of the - * INIT-REBOOT path, as described by the DHCP specification. In most cases, you - * do not want this. - * - * Background: The INIT-REBOOT path allows a DHCP client to skip - * server-discovery when rebooting/resuming their machine. The DHCP - * client simply re-requests the lease it had acquired before. This - * saves one roundtrip in the success-case, since the DISCOVER step - * is skipped. However, there are little to no timeouts involved, - * so the roundtrip should be barely noticeable. In contrast, if - * the INIT-REBOOT fails (because the lease is no longer valid, or - * not valid on this network), the client has to wait for a - * possible answer to the request before actually starting the DHCP - * process all over. This significantly increases the time needed - * to switch networks. - * The INIT-REBOOT state might have been a real improvements with - * the old resend-timeouts mandated by the DHCP specification. - * However, on modern networks with improved timeout values we - * recommend against using it. - */ -_c_public_ void n_dhcp4_client_probe_config_set_init_reboot(NDhcp4ClientProbeConfig *config, bool init_reboot) { - config->init_reboot = init_reboot; -} - -/** - * n_dhcp4_client_probe_config_set_requested_ip() - set requested-ip property - * @config: configuration to operate on - * @ip: value to set - * - * This sets the requested-ip property of the given configuration object. - * - * The default is all 0. If set to something else, the DHCP discovery will - * include this IP in its requests to tell DHCP servers which address to pick. - * Servers are not required to honor this, nor does this have any effect on - * servers not serving this address. - * - * This field should always be set if the caller knows of an address that was - * previously acquired on this network. It serves as hint to servers and will - * allow them to provide the same address again. - */ -_c_public_ void n_dhcp4_client_probe_config_set_requested_ip(NDhcp4ClientProbeConfig *config, struct in_addr ip) { - config->requested_ip = ip; -} - -/** - * n_dhcp4_client_probe_config_set_start_delay() - set start delay - * @config: configuration to operate on - * @msecs: value to set - * - * This sets the start delay property of the given configuration object. - * - * The default is 9000 ms, which is based on RFC2131. In the RFC the start - * delay is specified to be a random value in the range 1000 to 10.000 ms. - * However, there does not appear to be any particular reason to - * unconditionally wait at least one second, so we move the range down to - * start at 0 ms. The reason for the random delay is to avoid network-wide - * events causing too much simultaneous network traffic. However, on modern - * networks, a more reasonable value may be in the 10 ms range. - */ -_c_public_ void n_dhcp4_client_probe_config_set_start_delay(NDhcp4ClientProbeConfig *config, uint64_t msecs) { - config->ms_start_delay = msecs; -} - -/** - * n_dhpc4_client_probe_config_request_option() - append option to request from the server - * @config: configuration to operate on - * @option: option to request - * - * This adds an option to the list of options to request from the server. - * - * A server may send options that we do not request, and it may omit options - * that we do request. However, to increase the likelyhood of uniform behavior - * between server implementations, we do not expose options that were not - * explicitly requested. - * - * When called multiple times, the order matters. Earlier requests are - * considered higher priority than later requests, in case the server must omit - * some, due to a lack of space. If the same option is requested more than once, - * only the first call has an effect. - */ -_c_public_ void n_dhcp4_client_probe_config_request_option(NDhcp4ClientProbeConfig *config, uint8_t option) { - for (unsigned int i = 0; i < config->n_request_parameters; ++i) { - if (config->request_parameters[i] == option) - return; - } - - c_assert(config->n_request_parameters <= UINT8_MAX); - - config->request_parameters[config->n_request_parameters++] = option; -} - -/** - * n_dhcp4_client_probe_config_append_option() - append option to outgoing messages - * @config: configuration to operate on - * @option: DHCP option number - * @data: payload - * @n_data: number of bytes in payload - * - * This sets extra options on a given configuration object. - * - * These options are appended verbatim to outgoing messages where - * that is supported by the specification. The same options are - * appended to all messages. - * - * No option may be appended more than once. Options considered internal - * to the DHCP protocol may not be appended. - * - * Return: 0 on success, N_DHCP4_E_DUPLICATE_OPTION if an option has already been - * appended, N_DHCP4_E_INTERNAL if the option is not configurable, or - * a negative error code on failure. - */ -_c_public_ int n_dhcp4_client_probe_config_append_option(NDhcp4ClientProbeConfig *config, - uint8_t option, - const void *data, - uint8_t n_data) { - int r; - - /* XXX: filter internal options */ - - for (unsigned int i = 0; i <= UINT8_MAX; ++i) { - if (config->options[i]) { - if (config->options[i]->option == option) - return N_DHCP4_E_DUPLICATE_OPTION; - - continue; - } - - r = n_dhcp4_client_probe_option_new(&config->options[i], - option, - data, - n_data); - if (r) - return r; - - return 0; - } - - c_assert(0); - return -ENOTRECOVERABLE; -} - -static void n_dhcp4_client_probe_config_initialize_random_seed(NDhcp4ClientProbeConfig *config) { - uint8_t hash_seed[] = { - 0x25, 0x3f, 0x02, 0x75, 0x3a, 0xb8, 0x4f, 0x91, - 0x9d, 0x0a, 0xd6, 0x15, 0x9d, 0x72, 0x7b, 0xcb, - }; - CSipHash hash = C_SIPHASH_NULL; - unsigned short int seed16v[3]; - const uint8_t *p; - uint64_t u64; - - /* - * Initialize config's entropy buffer for successive jrand48(3) calls. - * - * We need random jitter for all timeouts and delays, used to reduce - * network traffic during bursts. This is not meant as security measure - * but only meant to improve network utilization during bursts. The - * random source is thus negligible. However, we want, under all - * circumstances, avoid two instances running with the same seed. Thus - * we source the seed from AT_RANDOM, which grants us a per-process - * unique seed. We then add the current time to make sure consequetive - * instances use different seeds (to avoid clashes if processes are - * duplicated, or similar), and lastly we add the config memory address - * to avoid clashes of multiple parallel instances. - * - * Again, none of these are meant as security measure, but only to - * avoid *ACCIDENTAL* seed clashes. That is, in the case that many - * transactions are started in parallel, we delay the individual - * messages (as described in the spec), to reduce the traffic on the - * network and the chance of packets being dropped (and thus triggering - * timeouts and resends). - * - * We hash everything through SipHash, to avoid exposing AT_RANDOM and - * other sources to the network. We use a static salt to distinguish it - * from other implementations using the same random source. - */ - c_siphash_init(&hash, hash_seed); - - p = (const uint8_t *)getauxval(AT_RANDOM); - if (p) - c_siphash_append(&hash, p, 16); - - u64 = n_dhcp4_gettime(CLOCK_MONOTONIC); - c_siphash_append(&hash, (const uint8_t *)&u64, sizeof(u64)); - - c_siphash_append(&hash, (const uint8_t *)&config, sizeof(config)); - - u64 = c_siphash_finalize(&hash); - - seed16v[0] = (u64 >> 0) ^ (u64 >> 48); - seed16v[1] = (u64 >> 16) ^ (u64 >> 0); - seed16v[2] = (u64 >> 32) ^ (u64 >> 16); - - memcpy(config->entropy, seed16v, sizeof(seed16v)); -} - -/** - * n_dhcp4_client_probe_config_get_random() - get random data - * @config: config object to operate on - * - * Fetch the next 32bit random number from the entropy pool in @config. - * Note that this is in no way suitable for security purposes. - * - * Return: the random data. - */ -uint32_t n_dhcp4_client_probe_config_get_random(NDhcp4ClientProbeConfig *config) { - return jrand48(config->entropy); -}; - -/** - * n_dhcp4_client_probe_new() - create new client probe - * @probep: output argument for new client probe - * @config: probe configuration - * @client: client to probe on behalf of - * @ns_now: the current time - * - * This creates a new client probe object. - * - * If one is already running, the new one will be immediately (but asynchronously) - * cancelled. Otherwise, a DISCOVER event is scheduled after a randomized delay. - * - * Return: 0 on success, or a negative error code on failure. - */ -int n_dhcp4_client_probe_new(NDhcp4ClientProbe **probep, - NDhcp4ClientProbeConfig *config, - NDhcp4Client *client, - uint64_t ns_now) { - _c_cleanup_(n_dhcp4_client_probe_freep) NDhcp4ClientProbe *probe = NULL; - bool active; - int r; - - /* - * If there is already a probe attached, we create the new probe in - * detached state. It will not be linked into the epoll context and not - * be useful in any way. We immediately raise the CANCELLED event to - * notify the caller about it. - */ - active = !client->current_probe; - - probe = calloc(1, sizeof(*probe)); - if (!probe) - return -ENOMEM; - - *probe = (NDhcp4ClientProbe)N_DHCP4_CLIENT_PROBE_NULL(*probe); - probe->client = n_dhcp4_client_ref(client); - - r = n_dhcp4_client_probe_config_dup(config, &probe->config); - if (r) - return r; - - /* - * XXX: make seed initialization optional, so the entropy can be reused. - */ - n_dhcp4_client_probe_config_initialize_random_seed(probe->config); - - /* The new probe keeps a reference on @client. So we are sure that &client->log_queue - * stays alive as long as we need it. */ - - r = n_dhcp4_c_connection_init(&probe->connection, - client->config, - probe->config, - &client->log_queue, - active ? client->fd_epoll : -1); - if (r) - return r; - - if (probe->config->requested_ip.s_addr != INADDR_ANY) - probe->last_address = probe->config->requested_ip; - - if (probe->config->init_reboot && probe->last_address.s_addr != INADDR_ANY) - probe->state = N_DHCP4_CLIENT_PROBE_STATE_INIT_REBOOT; - else - probe->state = N_DHCP4_CLIENT_PROBE_STATE_INIT; - - if (active) { - /* - * Defer the sending of DISCOVER by a random amount (by default up to 9 seconds). - */ - if (probe->state == N_DHCP4_CLIENT_PROBE_STATE_INIT) - probe->ns_deferred = ns_now + (n_dhcp4_client_probe_config_get_random(probe->config) % (probe->config->ms_start_delay * 1000000ULL)); - probe->client->current_probe = probe; - } else { - r = n_dhcp4_client_probe_raise(probe, - NULL, - N_DHCP4_CLIENT_EVENT_CANCELLED); - if (r) - return r; - } - - *probep = probe; - probe = NULL; - return 0; -} - -/** - * n_dhcp4_client_probe_free() - destroy a probe - * @probe: probe to operate on, or NULL - * - * This destroys a probe object and deallocates all its resources. - * - * If @probe is NULL, this is a no-op. - * - * Return: NULL is returned. - */ -_c_public_ NDhcp4ClientProbe *n_dhcp4_client_probe_free(NDhcp4ClientProbe *probe) { - NDhcp4CEventNode *node, *t_node; - NDhcp4ClientLease *lease, *t_lease; - - if (!probe) - return NULL; - - c_list_for_each_entry_safe(lease, t_lease, &probe->lease_list, probe_link) - n_dhcp4_client_lease_unlink(lease); - - c_list_for_each_entry_safe(node, t_node, &probe->event_list, probe_link) - n_dhcp4_c_event_node_free(node); - - if (probe == probe->client->current_probe) - probe->client->current_probe = NULL; - - n_dhcp4_client_lease_unref(probe->current_lease); - n_dhcp4_c_connection_deinit(&probe->connection); - n_dhcp4_client_unref(probe->client); - n_dhcp4_client_probe_config_free(probe->config); - - c_assert(c_list_is_empty(&probe->lease_list)); - c_assert(c_list_is_empty(&probe->event_list)); - free(probe); - - return NULL; -} - -/** - * n_dhcp4_client_probe_set_userdata() - set userdata pointer - * @probe: the probe to operate on - * @userdata: pointer to userdata - * - * Set a userdata pointer. The pointed to data is still owned by the caller, and - * is completely opaque to the probe. - */ -_c_public_ void n_dhcp4_client_probe_set_userdata(NDhcp4ClientProbe *probe, void *userdata) { - probe->userdata = userdata; -} - -/** - * n_dhcp4_client_probe_get_userdata() - get userdata pointer - * @probe: the probe to operate on - * @userdatap: return pointer for userdata pointer - * - * Get the userdata pointer. The lifetime of the userdata and making sure it is - * still valid when accessed via the probe is the responsibility of the caller. - */ -_c_public_ void n_dhcp4_client_probe_get_userdata(NDhcp4ClientProbe *probe, void **userdatap) { - *userdatap = probe->userdata; -} - -/** - * n_dhcp4_client_probe_raise() - XXX - */ -int n_dhcp4_client_probe_raise(NDhcp4ClientProbe *probe, NDhcp4CEventNode **nodep, unsigned int event) { - NDhcp4CEventNode *node; - int r; - - r = n_dhcp4_client_raise(probe->client, &node, event); - if (r) - return r; - - switch (event) { - case N_DHCP4_CLIENT_EVENT_OFFER: - node->event.offer.probe = probe; - break; - case N_DHCP4_CLIENT_EVENT_GRANTED: - node->event.granted.probe = probe; - break; - case N_DHCP4_CLIENT_EVENT_RETRACTED: - node->event.retracted.probe = probe; - break; - case N_DHCP4_CLIENT_EVENT_EXTENDED: - node->event.extended.probe = probe; - break; - case N_DHCP4_CLIENT_EVENT_EXPIRED: - node->event.expired.probe = probe; - break; - case N_DHCP4_CLIENT_EVENT_CANCELLED: - node->event.cancelled.probe = probe; - break; - default: - c_assert(0); - n_dhcp4_c_event_node_free(node); - return -ENOTRECOVERABLE; - } - - if (nodep) - *nodep = node; - return 0; -} - -void n_dhcp4_client_probe_get_timeout(NDhcp4ClientProbe *probe, uint64_t *timeoutp) { - uint64_t t1 = 0; - uint64_t t2 = 0; - uint64_t lifetime = 0; - uint64_t timeout = 0; - - if (probe->current_lease) { - t1 = probe->current_lease->t1; - t2 = probe->current_lease->t2; - lifetime = probe->current_lease->lifetime; - } - - n_dhcp4_c_connection_get_timeout(&probe->connection, &timeout); - - switch (probe->state) { - case N_DHCP4_CLIENT_PROBE_STATE_INIT_REBOOT: - /* send DHCP request immediately */ - timeout = 1; - break; - case N_DHCP4_CLIENT_PROBE_STATE_REBOOTING: - if (probe->ns_reinit && (!timeout || probe->ns_reinit < timeout)) - timeout = probe->ns_reinit; - break; - case N_DHCP4_CLIENT_PROBE_STATE_INIT: - if (probe->ns_deferred && (!timeout || probe->ns_deferred < timeout)) - timeout = probe->ns_deferred; - - break; - case N_DHCP4_CLIENT_PROBE_STATE_BOUND: - if (t1 && (!timeout || t1 < timeout)) - timeout = t1; - - /* fall-through */ - case N_DHCP4_CLIENT_PROBE_STATE_RENEWING: - if (t2 && (!timeout || t2 < timeout)) - timeout = t2; - - /* fall-through */ - case N_DHCP4_CLIENT_PROBE_STATE_REBINDING: - case N_DHCP4_CLIENT_PROBE_STATE_GRANTED: - if (lifetime && (!timeout || lifetime < timeout)) - timeout = lifetime; - break; - default: - /* ignore */ - break; - } - - *timeoutp = timeout; -} - -static int n_dhcp4_client_probe_outgoing_append_options(NDhcp4ClientProbe *probe, NDhcp4Outgoing *outgoing) { - int r; - - for (unsigned int i = 0; i <= UINT8_MAX; ++i) { - if (!probe->config->options[i]) - break; - - r = n_dhcp4_outgoing_append(outgoing, - probe->config->options[i]->option, - probe->config->options[i]->data, - probe->config->options[i]->n_data); - if (r) { - if (r == N_DHCP4_E_NO_SPACE) - /* XXX */ - break; - - return r; - } - } - - return 0; -} - -static int n_dhcp4_client_probe_transition_reboot(NDhcp4ClientProbe *probe, uint64_t ns_now) { - _c_cleanup_(n_dhcp4_outgoing_freep) NDhcp4Outgoing *request = NULL; - int r; - - switch (probe->state) { - case N_DHCP4_CLIENT_PROBE_STATE_INIT_REBOOT: - r = n_dhcp4_c_connection_listen(&probe->connection); - if (r) - return r; - - r = n_dhcp4_c_connection_reboot_new(&probe->connection, &request, &probe->last_address); - if (r) - return r; - - r = n_dhcp4_client_probe_outgoing_append_options(probe, request); - if (r) - return r; - - r = n_dhcp4_c_connection_start_request(&probe->connection, request, ns_now); - if (r) - return r; - else - request = NULL; /* consumed */ - - probe->state = N_DHCP4_CLIENT_PROBE_STATE_REBOOTING; - probe->ns_reinit = ns_now + 2000000000ULL; - - break; - - case N_DHCP4_CLIENT_PROBE_STATE_SELECTING: - case N_DHCP4_CLIENT_PROBE_STATE_INIT: - case N_DHCP4_CLIENT_PROBE_STATE_REBOOTING: - case N_DHCP4_CLIENT_PROBE_STATE_REQUESTING: - case N_DHCP4_CLIENT_PROBE_STATE_GRANTED: - case N_DHCP4_CLIENT_PROBE_STATE_BOUND: - case N_DHCP4_CLIENT_PROBE_STATE_RENEWING: - case N_DHCP4_CLIENT_PROBE_STATE_REBINDING: - case N_DHCP4_CLIENT_PROBE_STATE_EXPIRED: - default: - abort(); - break; - } - - return 0; -} - -static int n_dhcp4_client_probe_transition_deferred(NDhcp4ClientProbe *probe, uint64_t ns_now) { - _c_cleanup_(n_dhcp4_outgoing_freep) NDhcp4Outgoing *request = NULL; - int r; - - switch (probe->state) { - case N_DHCP4_CLIENT_PROBE_STATE_INIT: - r = n_dhcp4_c_connection_listen(&probe->connection); - if (r) - return r; - /* fall-through */ - - case N_DHCP4_CLIENT_PROBE_STATE_REBOOTING: - r = n_dhcp4_c_connection_discover_new(&probe->connection, &request); - if (r) - return r; - - if (probe->last_address.s_addr != INADDR_ANY) { - r = n_dhcp4_outgoing_append_requested_ip(request, probe->last_address); - if (r) - return r; - } - - r = n_dhcp4_client_probe_outgoing_append_options(probe, request); - if (r) - return r; - - r = n_dhcp4_c_connection_start_request(&probe->connection, request, ns_now); - if (r) - return r; - else - request = NULL; /* consumed */ - - probe->state = N_DHCP4_CLIENT_PROBE_STATE_SELECTING; - probe->ns_deferred = 0; - - break; - - case N_DHCP4_CLIENT_PROBE_STATE_SELECTING: - case N_DHCP4_CLIENT_PROBE_STATE_INIT_REBOOT: - case N_DHCP4_CLIENT_PROBE_STATE_REQUESTING: - case N_DHCP4_CLIENT_PROBE_STATE_GRANTED: - case N_DHCP4_CLIENT_PROBE_STATE_BOUND: - case N_DHCP4_CLIENT_PROBE_STATE_RENEWING: - case N_DHCP4_CLIENT_PROBE_STATE_REBINDING: - case N_DHCP4_CLIENT_PROBE_STATE_EXPIRED: - default: - abort(); - break; - } - - return 0; -} - -static int n_dhcp4_client_probe_transition_t1(NDhcp4ClientProbe *probe, uint64_t ns_now) { - _c_cleanup_(n_dhcp4_outgoing_freep) NDhcp4Outgoing *request = NULL; - int r; - - switch (probe->state) { - case N_DHCP4_CLIENT_PROBE_STATE_BOUND: - r = n_dhcp4_c_connection_renew_new(&probe->connection, &request); - if (r) - return r; - - r = n_dhcp4_client_probe_outgoing_append_options(probe, request); - if (r) - return r; - - r = n_dhcp4_c_connection_start_request(&probe->connection, request, ns_now); - if (r) - return r; - else - request = NULL; /* consumed */ - - probe->state = N_DHCP4_CLIENT_PROBE_STATE_RENEWING; - - break; - - case N_DHCP4_CLIENT_PROBE_STATE_INIT: - case N_DHCP4_CLIENT_PROBE_STATE_SELECTING: - case N_DHCP4_CLIENT_PROBE_STATE_INIT_REBOOT: - case N_DHCP4_CLIENT_PROBE_STATE_REBOOTING: - case N_DHCP4_CLIENT_PROBE_STATE_REQUESTING: - case N_DHCP4_CLIENT_PROBE_STATE_GRANTED: - case N_DHCP4_CLIENT_PROBE_STATE_RENEWING: - case N_DHCP4_CLIENT_PROBE_STATE_REBINDING: - case N_DHCP4_CLIENT_PROBE_STATE_EXPIRED: - default: - abort(); - break; - } - - return 0; -} - -static int n_dhcp4_client_probe_transition_t2(NDhcp4ClientProbe *probe, uint64_t ns_now) { - _c_cleanup_(n_dhcp4_outgoing_freep) NDhcp4Outgoing *request = NULL; - int r; - - switch (probe->state) { - case N_DHCP4_CLIENT_PROBE_STATE_BOUND: - case N_DHCP4_CLIENT_PROBE_STATE_RENEWING: - r = n_dhcp4_c_connection_listen(&probe->connection); - if (r) - return r; - - r = n_dhcp4_c_connection_rebind_new(&probe->connection, &request); - if (r) - return r; - - r = n_dhcp4_client_probe_outgoing_append_options(probe, request); - if (r) - return r; - - r = n_dhcp4_c_connection_start_request(&probe->connection, request, ns_now); - if (r) - return r; - else - request = NULL; /* consumed */ - - probe->state = N_DHCP4_CLIENT_PROBE_STATE_REBINDING; - - break; - - case N_DHCP4_CLIENT_PROBE_STATE_INIT: - case N_DHCP4_CLIENT_PROBE_STATE_SELECTING: - case N_DHCP4_CLIENT_PROBE_STATE_INIT_REBOOT: - case N_DHCP4_CLIENT_PROBE_STATE_REBOOTING: - case N_DHCP4_CLIENT_PROBE_STATE_REQUESTING: - case N_DHCP4_CLIENT_PROBE_STATE_GRANTED: - case N_DHCP4_CLIENT_PROBE_STATE_REBINDING: - case N_DHCP4_CLIENT_PROBE_STATE_EXPIRED: - default: - abort(); - break; - } - - return 0; -} - -static int n_dhcp4_client_probe_transition_lifetime(NDhcp4ClientProbe *probe) { - int r; - - switch (probe->state) { - case N_DHCP4_CLIENT_PROBE_STATE_BOUND: - case N_DHCP4_CLIENT_PROBE_STATE_GRANTED: - case N_DHCP4_CLIENT_PROBE_STATE_RENEWING: - case N_DHCP4_CLIENT_PROBE_STATE_REBINDING: - - /* XXX */ - - r = n_dhcp4_client_probe_raise(probe, - NULL, - N_DHCP4_CLIENT_EVENT_EXPIRED); - if (r) - return r; - - c_assert(probe->client->current_probe == probe); - - probe->current_lease = n_dhcp4_client_lease_unref(probe->current_lease); - - probe->state = N_DHCP4_CLIENT_PROBE_STATE_INIT; - probe->ns_deferred = n_dhcp4_gettime(CLOCK_BOOTTIME) + UINT64_C(1); - - break; - - case N_DHCP4_CLIENT_PROBE_STATE_INIT: - case N_DHCP4_CLIENT_PROBE_STATE_SELECTING: - case N_DHCP4_CLIENT_PROBE_STATE_INIT_REBOOT: - case N_DHCP4_CLIENT_PROBE_STATE_REBOOTING: - case N_DHCP4_CLIENT_PROBE_STATE_REQUESTING: - case N_DHCP4_CLIENT_PROBE_STATE_EXPIRED: - default: - abort(); - break; - } - - return 0; -} - -static int n_dhcp4_client_probe_transition_offer(NDhcp4ClientProbe *probe, NDhcp4Incoming *message_take) { - _c_cleanup_(n_dhcp4_incoming_freep) NDhcp4Incoming *message = message_take; - _c_cleanup_(n_dhcp4_client_lease_unrefp) NDhcp4ClientLease *lease = NULL; - NDhcp4CEventNode *node; - int r; - - switch (probe->state) { - case N_DHCP4_CLIENT_PROBE_STATE_SELECTING: - - r = n_dhcp4_client_probe_raise(probe, - &node, - N_DHCP4_CLIENT_EVENT_OFFER); - if (r) - return r; - - r = n_dhcp4_client_lease_new(&lease, message); - if (r) - return r; - - message = NULL; /* consumed */ - - n_dhcp4_client_lease_link(lease, probe); - - node->event.offer.lease = n_dhcp4_client_lease_ref(lease); - - break; - - case N_DHCP4_CLIENT_PROBE_STATE_INIT: - case N_DHCP4_CLIENT_PROBE_STATE_INIT_REBOOT: - case N_DHCP4_CLIENT_PROBE_STATE_REBOOTING: - case N_DHCP4_CLIENT_PROBE_STATE_REQUESTING: - case N_DHCP4_CLIENT_PROBE_STATE_BOUND: - case N_DHCP4_CLIENT_PROBE_STATE_GRANTED: - case N_DHCP4_CLIENT_PROBE_STATE_RENEWING: - case N_DHCP4_CLIENT_PROBE_STATE_REBINDING: - case N_DHCP4_CLIENT_PROBE_STATE_EXPIRED: - default: - /* ignore */ - break; - } - - return 0; -} - -static int n_dhcp4_client_probe_transition_ack(NDhcp4ClientProbe *probe, NDhcp4Incoming *message_take) { - _c_cleanup_(n_dhcp4_incoming_freep) NDhcp4Incoming *message = message_take; - _c_cleanup_(n_dhcp4_client_lease_unrefp) NDhcp4ClientLease *lease = NULL; - NDhcp4CEventNode *node; - struct in_addr client = {}; - struct in_addr server = {}; - int r; - - switch (probe->state) { - case N_DHCP4_CLIENT_PROBE_STATE_REBINDING: - n_dhcp4_incoming_get_yiaddr(message, &client); - - r = n_dhcp4_incoming_query_server_identifier(message, &server); - if (r) - return r; - r = n_dhcp4_c_connection_connect(&probe->connection, &client, &server); - if (r) - return r; - /* fall-through */ - case N_DHCP4_CLIENT_PROBE_STATE_RENEWING: - - r = n_dhcp4_client_probe_raise(probe, - &node, - N_DHCP4_CLIENT_EVENT_EXTENDED); - if (r) - return r; - - r = n_dhcp4_client_lease_new(&lease, message); - if (r) - return r; - - message = NULL; /* consumed */ - - n_dhcp4_client_lease_link(lease, probe); - - node->event.extended.lease = n_dhcp4_client_lease_ref(lease); - n_dhcp4_client_lease_unref(probe->current_lease); - probe->current_lease = n_dhcp4_client_lease_ref(lease); - probe->state = N_DHCP4_CLIENT_PROBE_STATE_BOUND; - n_dhcp4_client_lease_get_yiaddr(lease, &probe->last_address); - probe->ns_nak_restart_delay = 0; - break; - - case N_DHCP4_CLIENT_PROBE_STATE_REQUESTING: - case N_DHCP4_CLIENT_PROBE_STATE_REBOOTING: - - r = n_dhcp4_client_probe_raise(probe, - &node, - N_DHCP4_CLIENT_EVENT_GRANTED); - if (r) - return r; - - r = n_dhcp4_client_lease_new(&lease, message); - if (r) - return r; - - message = NULL; /* consumed */ - - n_dhcp4_client_lease_link(lease, probe); - - node->event.granted.lease = n_dhcp4_client_lease_ref(lease); - probe->current_lease = n_dhcp4_client_lease_ref(lease); - probe->state = N_DHCP4_CLIENT_PROBE_STATE_GRANTED; - probe->ns_nak_restart_delay = 0; - break; - - case N_DHCP4_CLIENT_PROBE_STATE_INIT: - case N_DHCP4_CLIENT_PROBE_STATE_SELECTING: - case N_DHCP4_CLIENT_PROBE_STATE_INIT_REBOOT: - case N_DHCP4_CLIENT_PROBE_STATE_BOUND: - case N_DHCP4_CLIENT_PROBE_STATE_GRANTED: - case N_DHCP4_CLIENT_PROBE_STATE_EXPIRED: - default: - /* ignore */ - break; - } - - return 0; -} - -static int n_dhcp4_client_probe_transition_nak(NDhcp4ClientProbe *probe) { - int r; - - switch (probe->state) { - case N_DHCP4_CLIENT_PROBE_STATE_REBOOTING: - case N_DHCP4_CLIENT_PROBE_STATE_REQUESTING: - case N_DHCP4_CLIENT_PROBE_STATE_RENEWING: - case N_DHCP4_CLIENT_PROBE_STATE_REBINDING: - - /* XXX */ - - r = n_dhcp4_client_probe_raise(probe, - NULL, - N_DHCP4_CLIENT_EVENT_RETRACTED); - if (r) - return r; - - probe->state = N_DHCP4_CLIENT_PROBE_STATE_INIT; - probe->ns_deferred = n_dhcp4_gettime(CLOCK_BOOTTIME) + probe->ns_nak_restart_delay; - probe->ns_nak_restart_delay = C_CLAMP(probe->ns_nak_restart_delay * 2u, - UINT64_C(2) * UINT64_C(1000000000), - UINT64_C(300) * UINT64_C(1000000000)); - break; - case N_DHCP4_CLIENT_PROBE_STATE_SELECTING: - case N_DHCP4_CLIENT_PROBE_STATE_INIT_REBOOT: - case N_DHCP4_CLIENT_PROBE_STATE_INIT: - case N_DHCP4_CLIENT_PROBE_STATE_BOUND: - case N_DHCP4_CLIENT_PROBE_STATE_GRANTED: - case N_DHCP4_CLIENT_PROBE_STATE_EXPIRED: - default: - /* ignore */ - break; - } - - return 0; -} - -int n_dhcp4_client_probe_transition_select(NDhcp4ClientProbe *probe, NDhcp4Incoming *offer, uint64_t ns_now) { - _c_cleanup_(n_dhcp4_outgoing_freep) NDhcp4Outgoing *request = NULL; - int r; - - switch (probe->state) { - case N_DHCP4_CLIENT_PROBE_STATE_SELECTING: - r = n_dhcp4_c_connection_select_new(&probe->connection, &request, offer); - if (r) - return r; - - r = n_dhcp4_client_probe_outgoing_append_options(probe, request); - if (r) - return r; - - r = n_dhcp4_c_connection_start_request(&probe->connection, request, ns_now); - if (r) - return r; - else - request = NULL; /* consumed */ - - /* XXX: ignore other offers */ - - probe->state = N_DHCP4_CLIENT_PROBE_STATE_REQUESTING; - - break; - case N_DHCP4_CLIENT_PROBE_STATE_INIT: - case N_DHCP4_CLIENT_PROBE_STATE_INIT_REBOOT: - case N_DHCP4_CLIENT_PROBE_STATE_REBOOTING: - case N_DHCP4_CLIENT_PROBE_STATE_REQUESTING: - case N_DHCP4_CLIENT_PROBE_STATE_BOUND: - case N_DHCP4_CLIENT_PROBE_STATE_GRANTED: - case N_DHCP4_CLIENT_PROBE_STATE_RENEWING: - case N_DHCP4_CLIENT_PROBE_STATE_REBINDING: - case N_DHCP4_CLIENT_PROBE_STATE_EXPIRED: - default: - /* ignore */ - break; - } - - return 0; -} - -/** - * n_dhcp4_client_probe_transition_accept() - XXX - */ -int n_dhcp4_client_probe_transition_accept(NDhcp4ClientProbe *probe, NDhcp4Incoming *ack) { - struct in_addr client = {}; - struct in_addr server = {}; - int r; - - switch (probe->state) { - case N_DHCP4_CLIENT_PROBE_STATE_GRANTED: - n_dhcp4_incoming_get_yiaddr(ack, &client); - - r = n_dhcp4_incoming_query_server_identifier(ack, &server); - if (r) - return r; - - r = n_dhcp4_c_connection_connect(&probe->connection, &client, &server); - if (r) - return r; - - probe->state = N_DHCP4_CLIENT_PROBE_STATE_BOUND; - - n_dhcp4_client_arm_timer(probe->client); - - break; - - case N_DHCP4_CLIENT_PROBE_STATE_INIT: - case N_DHCP4_CLIENT_PROBE_STATE_INIT_REBOOT: - case N_DHCP4_CLIENT_PROBE_STATE_REBOOTING: - case N_DHCP4_CLIENT_PROBE_STATE_SELECTING: - case N_DHCP4_CLIENT_PROBE_STATE_REQUESTING: - case N_DHCP4_CLIENT_PROBE_STATE_BOUND: - case N_DHCP4_CLIENT_PROBE_STATE_RENEWING: - case N_DHCP4_CLIENT_PROBE_STATE_REBINDING: - case N_DHCP4_CLIENT_PROBE_STATE_EXPIRED: - default: - /* ignore */ - break; - } - - return 0; -} - -/** - * n_dhc4_client_probe_transition_decline() - XXX - */ -int n_dhcp4_client_probe_transition_decline(NDhcp4ClientProbe *probe, NDhcp4Incoming *offer, const char *error, uint64_t ns_now) { - _c_cleanup_(n_dhcp4_outgoing_freep) NDhcp4Outgoing *request = NULL; - int r; - - switch (probe->state) { - case N_DHCP4_CLIENT_PROBE_STATE_GRANTED: - r = n_dhcp4_c_connection_decline_new(&probe->connection, &request, offer, error); - if (r) - return r; - - r = n_dhcp4_c_connection_start_request(&probe->connection, request, ns_now); - if (r) - return r; - else - request = NULL; /* consumed */ - - /* XXX: what state to transition to? */ - - break; - - case N_DHCP4_CLIENT_PROBE_STATE_INIT: - case N_DHCP4_CLIENT_PROBE_STATE_INIT_REBOOT: - case N_DHCP4_CLIENT_PROBE_STATE_REBOOTING: - case N_DHCP4_CLIENT_PROBE_STATE_SELECTING: - case N_DHCP4_CLIENT_PROBE_STATE_REQUESTING: - case N_DHCP4_CLIENT_PROBE_STATE_BOUND: - case N_DHCP4_CLIENT_PROBE_STATE_RENEWING: - case N_DHCP4_CLIENT_PROBE_STATE_REBINDING: - case N_DHCP4_CLIENT_PROBE_STATE_EXPIRED: - default: - /* ignore */ - break; - } - - return 0; -} - -/** - * n_dhcp4_client_probe_dispatch_timer() - XXX - */ -int n_dhcp4_client_probe_dispatch_timer(NDhcp4ClientProbe *probe, uint64_t ns_now) { - int r; - - switch (probe->state) { - case N_DHCP4_CLIENT_PROBE_STATE_INIT_REBOOT: - r = n_dhcp4_client_probe_transition_reboot(probe, ns_now); - if (r) - return r; - break; - case N_DHCP4_CLIENT_PROBE_STATE_REBOOTING: - if (ns_now >= probe->ns_reinit) { - r = n_dhcp4_client_probe_transition_deferred(probe, ns_now); - if (r) - return r; - } - - break; - case N_DHCP4_CLIENT_PROBE_STATE_INIT: - if (ns_now >= probe->ns_deferred) { - r = n_dhcp4_client_probe_transition_deferred(probe, ns_now); - if (r) - return r; - } - - break; - case N_DHCP4_CLIENT_PROBE_STATE_GRANTED: - if (ns_now >= probe->current_lease->lifetime) { - r = n_dhcp4_client_probe_transition_lifetime(probe); - if (r) - return r; - } - - break; - case N_DHCP4_CLIENT_PROBE_STATE_BOUND: - case N_DHCP4_CLIENT_PROBE_STATE_RENEWING: - case N_DHCP4_CLIENT_PROBE_STATE_REBINDING: - if (ns_now >= probe->current_lease->lifetime) { - r = n_dhcp4_client_probe_transition_lifetime(probe); - if (r) - return r; - } else if (ns_now >= probe->current_lease->t2 && - probe->state != N_DHCP4_CLIENT_PROBE_STATE_REBINDING) { - r = n_dhcp4_client_probe_transition_t2(probe, ns_now); - if (r) - return r; - } else if (ns_now >= probe->current_lease->t1 && - probe->state == N_DHCP4_CLIENT_PROBE_STATE_BOUND) { - r = n_dhcp4_client_probe_transition_t1(probe, ns_now); - if (r) - return r; - } - break; - default: - /* ignore */ - break; - } - - r = n_dhcp4_c_connection_dispatch_timer(&probe->connection, ns_now); - if (r) - return r; - - return 0; -} - -/** - * n_dhcp4_client_probe_dispatch_connection() - XXX - */ -int n_dhcp4_client_probe_dispatch_io(NDhcp4ClientProbe *probe, uint32_t events) { - _c_cleanup_(n_dhcp4_incoming_freep) NDhcp4Incoming *message = NULL; - uint8_t type; - int r; - - r = n_dhcp4_c_connection_dispatch_io(&probe->connection, &message); - if (r) { - if (r == N_DHCP4_E_AGAIN) - return 0; - else if (r == N_DHCP4_E_MALFORMED || r == N_DHCP4_E_UNEXPECTED) { - /* - * We fetched something from the sockets, which we - * discarded. We don't know whether there is more data - * to fetch, so we set the preempted flag to notify the - * caller we want to be called again. - */ - probe->client->preempted = true; - return 0; - } - - abort(); - return r; - } - - /* - * We fetched something from the sockets, which we will handle below. - * We don't know whether there is more data to fetch, so we set the - * preempted flag to notify the caller we want to be called again. - */ - probe->client->preempted = true; - - r = n_dhcp4_incoming_query_message_type(message, &type); - if (r == N_DHCP4_E_UNSET || r == N_DHCP4_E_MALFORMED) - /* - * XXX: this can never happen as we already queried the message - * type. - */ - return 0; - - switch (type) { - case N_DHCP4_MESSAGE_OFFER: - r = n_dhcp4_client_probe_transition_offer(probe, message); - message = NULL; /* consumed */ - if (r) - return r; - break; - case N_DHCP4_MESSAGE_ACK: - r = n_dhcp4_client_probe_transition_ack(probe, message); - message = NULL; /* consumed */ - if (r) - return r; - break; - case N_DHCP4_MESSAGE_NAK: - r = n_dhcp4_client_probe_transition_nak(probe); - if (r) - return r; - break; - default: - /* - * We receiveda message type we do not support, simply discard - * it. - */ - break; - } - - return 0; -} - -/** - * n_dhcp4_client_probe_update_mtu() - XXX - */ -int n_dhcp4_client_probe_update_mtu(NDhcp4ClientProbe *probe, uint16_t mtu) { - return 0; -} diff --git a/shared/n-dhcp4/src/n-dhcp4-client.c b/shared/n-dhcp4/src/n-dhcp4-client.c deleted file mode 100644 index 1dedbf30..00000000 --- a/shared/n-dhcp4/src/n-dhcp4-client.c +++ /dev/null @@ -1,990 +0,0 @@ -/* - * Client Side of the Dynamic Host Configuration Protocol for IPv4 - * - * This implements the public API around the NDhcp4Client object. The client - * object is simply a context to track running probes. It manages pending - * events of all probes, as well as forwards the dispatching requests whenever - * the dispatcher is run. - */ - -#include <assert.h> -#include <c-list.h> -#include <c-stdaux.h> -#include <errno.h> -#include <linux/if_ether.h> -#include <linux/if_infiniband.h> -#include <stdlib.h> -#include <string.h> -#include <sys/epoll.h> -#include <sys/time.h> -#include <sys/timerfd.h> -#include <time.h> -#include <unistd.h> -#include "n-dhcp4.h" -#include "n-dhcp4-private.h" - -/** - * n_dhcp4_client_config_new() - allocate new client configuration - * @configp: output argument for new client config - * - * This creates a new client configuration object. Client configurations are - * unlinked objects that merely serve as collection of parameters. They do not - * perform validity checks. - * - * The new client configuration is fully owned by the caller. They are - * responsible to free the object if no longer needed. - * - * Return: 0 on success, negative error code on failure. - */ -_c_public_ int n_dhcp4_client_config_new(NDhcp4ClientConfig **configp) { - _c_cleanup_(n_dhcp4_client_config_freep) NDhcp4ClientConfig *config = NULL; - - config = calloc(1, sizeof(*config)); - if (!config) - return -ENOMEM; - - *config = (NDhcp4ClientConfig)N_DHCP4_CLIENT_CONFIG_NULL(*config); - - *configp = config; - config = NULL; - return 0; -} - -/** - * n_dhcp4_client_config_free() - destroy client configuration - * @config: client configuration to operate on, or NULL - * - * This destroys a client configuration and deallocates all its resources. If - * NULL is passed, this is a no-op. - * - * Return: NULL is returned. - */ -_c_public_ NDhcp4ClientConfig *n_dhcp4_client_config_free(NDhcp4ClientConfig *config) { - if (!config) - return NULL; - - free(config->client_id); - free(config); - - return NULL; -} - -/** - * n_dhcp4_client_config_dup() - duplicate client configuration - * @config: client configuration to operate on - * @dupp: output argument for duplicate - * - * This duplicates the client configuration given as @config and returns it in - * @dupp to the caller. - * - * Return: 0 on success, negative error code on failure. - */ -int n_dhcp4_client_config_dup(NDhcp4ClientConfig *config, NDhcp4ClientConfig **dupp) { - _c_cleanup_(n_dhcp4_client_config_freep) NDhcp4ClientConfig *dup = NULL; - int r; - - r = n_dhcp4_client_config_new(&dup); - if (r) - return r; - - dup->ifindex = config->ifindex; - dup->transport = config->transport; - dup->request_broadcast = config->request_broadcast; - memcpy(dup->mac, config->mac, sizeof(dup->mac)); - dup->n_mac = config->n_mac; - memcpy(dup->broadcast_mac, config->broadcast_mac, sizeof(dup->broadcast_mac)); - dup->n_broadcast_mac = config->n_broadcast_mac; - - r = n_dhcp4_client_config_set_client_id(dup, - config->client_id, - config->n_client_id); - if (r) - return r; - - *dupp = dup; - dup = NULL; - return 0; -} - -/** - * n_dhcp4_client_config_set_ifindex() - set ifindex property - * @config: client configuration to operate on - * @ifindex: ifindex to set - * - * This sets the ifindex property of the client configuration. The ifindex - * specifies the network device that a DHCP client will run on. - */ -_c_public_ void n_dhcp4_client_config_set_ifindex(NDhcp4ClientConfig *config, int ifindex) { - config->ifindex = ifindex; -} - -/** - * n_dhcp4_client_config_set_transport() - set transport property - * @config: client configuration to operate on - * @transport: transport to set - * - * This sets the transport property of the client configuration. The transport - * defines the hardware transport of the network device that a DHCP client - * runs on. - * - * This takes one of the N_DHCP4_TRANSPORT_* identifiers as argument. - */ -_c_public_ void n_dhcp4_client_config_set_transport(NDhcp4ClientConfig *config, unsigned int transport) { - config->transport = transport; -} - -/** - * n_dhcp4_client_config_set_request_broadcast() - set request-broadcast property - * @config: configuration to operate on - * @request_broadcast: value to set - * - * This sets the request_broadcast property of the given configuration object. - * - * The default is false. If set to true, a the server will be told to not unicast - * replies to the client's IP address before it has been configured, but broadcast - * to INADDR_ANY instead. In most cases, you do not want this. - * - * Background: OFFER and ACK messages from DHCP servers to clients are unicast - * to the IP address handed out, even before the IP address has - * been configured on the target interface. This usually works - * because the correct destination hardware address is explicitly - * set on the outgoing packets, rather than being resolved (which - * would not work). However, some hardware does not accept incoming - * IP packets destined for addresses they do not own, even if the - * hardware address is correct. In this case, the server must - * broadcast the replies in order for the client to receive them. - * In general, unnecessary broadcasting is something one wants to - * avoid, and some networks will not deliver broadcasts to the - * client at all, in which case this flag must not be set. - */ -_c_public_ void n_dhcp4_client_config_set_request_broadcast(NDhcp4ClientConfig *config, bool request_broadcast) { - config->request_broadcast = request_broadcast; -} - -/** - * n_dhcp4_client_config_set_mac() - set mac property - * @config: client configuration to operate on - * @mac: hardware address to set - * @n_mac: length of the hardware address - * - * This sets the mac property of the client configuration. It specifies the - * hardware address of the local interface that the DHCP client runs on. - * - * This function copies the specified hardware address into @config. Any - * hardware address is supported. It is up to the consumer of the client - * configuration to verify the validity of the hardware address. - * - * Note: This function may truncate the hardware address internally, but - * retains the original length. The consumer of this configuration can - * thus tell whether the data was truncated and will refuse it. - * The internal buffer is big enough to hold any hardware address of all - * supported transports. Thus, truncation only happens if you use - * unsupported transports, and those will be rejected, anyway. - */ -_c_public_ void n_dhcp4_client_config_set_mac(NDhcp4ClientConfig *config, const uint8_t *mac, size_t n_mac) { - config->n_mac = n_mac; - - if (n_mac > sizeof(config->mac)) - n_mac = sizeof(config->mac); - - memcpy(config->mac, mac, n_mac); -} - -/** - * n_dhcp4_client_config_set_broadcast_mac() - set broadcast-mac property - * @config: client configuration to operate on - * @mac: hardware address to set - * @n_mac: length of the hardware address - * - * This sets the broadcast-mac property of the client configuration. It - * specifies the destination hardware address to use for broadcasts on the - * local interface that the DHCP client runs on. - * - * This function copies the specified hardware address into @config. Any - * hardware address is supported. It is up to the consumer of the client - * configuration to verify the validity of the hardware address. - * - * Note: This function may truncate the hardware address internally, but - * retains the original length. The consumer of this configuration can - * thus tell whether the data was truncated and will refuse it. - * The internal buffer is big enough to hold any hardware address of all - * supported transports. Thus, truncation only happens if you use - * unsupported transports, and those will be rejected, anyway. - */ -_c_public_ void n_dhcp4_client_config_set_broadcast_mac(NDhcp4ClientConfig *config, const uint8_t *mac, size_t n_mac) { - config->n_broadcast_mac = n_mac; - - if (n_mac > sizeof(config->mac)) - n_mac = sizeof(config->mac); - - memcpy(config->broadcast_mac, mac, n_mac); -} - -/** - * n_dhcp4_client_config_set_client_id() - set client-id property - * @config: client configuration to operate on - * @id: client id - * @n_id: length of the client id in bytes. The length - * must be from 2 up to 255 bytes. Set it to 0 - * to unset the client-id. - * - * This sets the client-id property of @config. It copies the entire client-id - * buffer into the configuration. - * See RFC 2132 (section 9.14) for the format of the Client Identifier. - * - * Return: 0 on success, negative error code on failure. - */ -_c_public_ int n_dhcp4_client_config_set_client_id(NDhcp4ClientConfig *config, const uint8_t *id, size_t n_id) { - uint8_t *t; - - if (n_id == 0) { - config->client_id = c_free(config->client_id); - config->n_client_id = 0; - return 0; - } - - if (n_id < 2 || n_id > 255) - return -EINVAL; - - t = malloc(n_id + 1); - if (!t) - return -ENOMEM; - - memcpy(t, id, n_id); - t[n_id] = 0; /* safety 0 for debugging */ - - free(config->client_id); - config->client_id = t; - config->n_client_id = n_id; - return 0; -} - -/** - * n_dhcp4_client_set_log_level() - set the logging level of the client - * @client: the client to operate on - * @level: the minimum syslog logging level that is - * still logged. For example, set to LOG_NOTICE - * to receive logging events with level LOG_NOTICE - * and higher. Set to -1 to disable generating - * logging events (which is also the default). - * - * By enabling logging, you can get N_DHCP4_CLIENT_EVENT_LOG events. - * - * From the logging event you may steal the message if (and only if) "allow_steal_message" - * is true. In that case, clear the message field and free the message yourself. - * - * If a logging event cannot be logged due to out of memory, one message - * gets logged that messages are missing. Until the event with that message - * gets dropped, no further logging events will be queued. - * - * You may change the logging level at any time, but it does not affect - * logging events that are already queued. - */ -_c_public_ void n_dhcp4_client_set_log_level(NDhcp4Client *client, int level) { - client->log_queue.log_level = level; -} - -/** - * n_dhcp4_c_event_node_new() - allocate new event - * @nodep: output argument for new event - * - * This allocates a new event node and returns it to the caller. The caller - * fully owns the event-node and is reposonsible to either link it somewhere, - * or release it. - * - * Event nodes can be linked on a client object, as well as optionally on a - * probe object. As long as an event-node is linked, it will be retrievable by - * the API user through n_dhcp4_client_pop_event(). Furthermore, destruction of - * the client, or probe respectively, will clean-up all pending events. - * - * Return: 0 on success, negative error code on failure. - */ -int n_dhcp4_c_event_node_new(NDhcp4CEventNode **nodep) { - NDhcp4CEventNode *node; - - node = calloc(1, sizeof(*node)); - if (!node) - return -ENOMEM; - - *node = (NDhcp4CEventNode)N_DHCP4_C_EVENT_NODE_NULL(*node); - - *nodep = node; - return 0; -} - -/** - * n_dhcp4_c_event_node_free() - deallocate event - * @node: node to operate on, or NULL - * - * This deallocates the node given as @node. If the node is linked on a client - * or probe, it is unlinked automatically. - * - * If @probe is NULL, this is a no-op. - * - * Return: NULL is returned. - */ -NDhcp4CEventNode *n_dhcp4_c_event_node_free(NDhcp4CEventNode *node) { - if (!node) - return NULL; - - switch (node->event.event) { - case N_DHCP4_CLIENT_EVENT_OFFER: - node->event.offer.lease = n_dhcp4_client_lease_unref(node->event.offer.lease); - break; - case N_DHCP4_CLIENT_EVENT_GRANTED: - node->event.granted.lease = n_dhcp4_client_lease_unref(node->event.granted.lease); - break; - case N_DHCP4_CLIENT_EVENT_EXTENDED: - node->event.extended.lease = n_dhcp4_client_lease_unref(node->event.extended.lease); - break; - case N_DHCP4_CLIENT_EVENT_LOG: - if (_c_unlikely_(!node->event.log.allow_steal_message)) { - /* @node is the static node "nomem_node". It must not be - * freed. */ - c_list_unlink(&node->client_link); - node->is_public = false; - return NULL; - } - node->event.log.message = c_free((char *)node->event.log.message); - break; - default: - break; - } - - c_list_unlink(&node->probe_link); - c_list_unlink(&node->client_link); - free(node); - - return NULL; -} - -/** - * n_dhcp4_client_new() - allocate new client - * @clientp: output argument for new client - * @config: configuration to use - * - * This allocates a new DHCP4 client object and returns it in @clientp to the - * caller. The caller then owns a single ref-count to the object and is - * responsible to drop it, when no longer needed. - * - * The configuration given as @config is used to initialize the client. The - * caller is free to destroy the configuration once this function returns. - * - * Return: 0 on success, negative error code on failure. - */ -_c_public_ int n_dhcp4_client_new(NDhcp4Client **clientp, NDhcp4ClientConfig *config) { - _c_cleanup_(n_dhcp4_client_unrefp) NDhcp4Client *client = NULL; - struct epoll_event ev = { - .events = EPOLLIN, - }; - int r; - - c_assert(clientp); - - /* verify configuration */ - { - if (config->ifindex < 1) - return N_DHCP4_E_INVALID_IFINDEX; - - switch (config->transport) { - case N_DHCP4_TRANSPORT_ETHERNET: - if (config->n_mac != ETH_ALEN || - config->n_broadcast_mac != ETH_ALEN) - return N_DHCP4_E_INVALID_ADDRESS; - - break; - case N_DHCP4_TRANSPORT_INFINIBAND: - if (config->n_mac != INFINIBAND_ALEN || - config->n_broadcast_mac != INFINIBAND_ALEN) - return N_DHCP4_E_INVALID_ADDRESS; - - break; - default: - return N_DHCP4_E_INVALID_TRANSPORT; - } - - if (config->n_client_id < 1) - return N_DHCP4_E_INVALID_CLIENT_ID; - } - - client = malloc(sizeof(*client)); - if (!client) - return -ENOMEM; - - *client = (NDhcp4Client)N_DHCP4_CLIENT_NULL(*client); - - r = n_dhcp4_client_config_dup(config, &client->config); - if (r) - return r; - - client->fd_epoll = epoll_create1(EPOLL_CLOEXEC); - if (client->fd_epoll < 0) - return -errno; - - client->fd_timer = timerfd_create(CLOCK_BOOTTIME, TFD_CLOEXEC | TFD_NONBLOCK); - if (client->fd_timer < 0 && errno == EINVAL) - client->fd_timer = timerfd_create(CLOCK_MONOTONIC, TFD_CLOEXEC | TFD_NONBLOCK); - if (client->fd_timer < 0) - return -errno; - - ev.data.u32 = N_DHCP4_CLIENT_EPOLL_TIMER; - r = epoll_ctl(client->fd_epoll, EPOLL_CTL_ADD, client->fd_timer, &ev); - if (r < 0) { - close(client->fd_timer); - client->fd_timer = -1; - return -errno; - } - - *clientp = client; - client = NULL; - return 0; -} - -static void n_dhcp4_client_free(NDhcp4Client *client) { - NDhcp4CEventNode *node, *t_node; - - c_assert(!client->current_probe); - - c_list_for_each_entry_safe(node, t_node, &client->event_list, client_link) - n_dhcp4_c_event_node_free(node); - - if (client->fd_timer >= 0) { - epoll_ctl(client->fd_epoll, EPOLL_CTL_DEL, client->fd_timer, NULL); - close(client->fd_timer); - } - - if (client->fd_epoll >= 0) - close(client->fd_epoll); - - n_dhcp4_client_config_free(client->config); - free(client); -} - -/** - * n_dhcp4_client_ref() - acquire client reference - * @client: client to operate on, or NULL - * - * This acquires a reference to the client given as @client. If @client is - * NULL, this function is a no-op. - * - * Return: @client is returned. - */ -_c_public_ NDhcp4Client *n_dhcp4_client_ref(NDhcp4Client *client) { - if (client) - ++client->n_refs; - return client; -} - -/** - * n_dhcp4_client_unref() - release client reference - * @client: client to operate on, or NULL - * - * This releases a reference to the client given as @client. If @client is - * NULL, this is a no-op. - * - * Once the last reference is dropped, the client object will get destroyed and - * deallocated. - * - * Return: NULL is returned. - */ -_c_public_ NDhcp4Client *n_dhcp4_client_unref(NDhcp4Client *client) { - if (client && !--client->n_refs) - n_dhcp4_client_free(client); - return NULL; -} - -/** - * n_dhcp4_client_raise() - raise event - * @client: client to operate on - * @nodep: output argument for new event, or NULL - * @event: event type to use - * - * This creates a new event-node on @client, setting the event-type to @event. - * The newly created event-node is returned to the caller in @nodep (unless - * @nodep is NULL). - * - * The event-node is automatically linked on @client. - * - * Return: 0 on success, negative error code on failure. - */ -int n_dhcp4_client_raise(NDhcp4Client *client, NDhcp4CEventNode **nodep, unsigned int event) { - NDhcp4CEventNode *node; - int r; - - r = n_dhcp4_c_event_node_new(&node); - if (r) - return r; - - node->event.event = event; - c_list_link_tail(&client->event_list, &node->client_link); - - if (nodep) - *nodep = node; - return 0; -} - -/** - * n_dhcp4_log_queue_fmt() - add a logging event. - * @client: the NDhcp4LogQueue to operate on - * @level: the syslog logging level - * @fmt: the format string for the message - * @... printf arguments for logging - * - * Appends a logging event to the event queue if logging is - * enabled and the logging level sufficiently high. - * - * Queuing a logging event might fail with out of memory. - * In that case, a static event will be queued that informs - * about lost messages. - */ -void n_dhcp4_log_queue_fmt(NDhcp4LogQueue *log_queue, - int level, - const char *fmt, - ...) { - NDhcp4CEventNode *node; - char *message; - va_list ap; - int r; - - if (level > log_queue->log_level) - return; - - /* Currently, the logging queue is only implemented for - * the client. Nobody would enable logging except a - * client instance. */ - c_assert(log_queue->is_client); - - if (!c_list_is_empty (&log_queue->nomem_node.client_link)) { - /* we have the nomem_node queued after a recent out - * of memory. This disables all logging messages until - * the event gets popped. - * - * The reason is that we can only queue the nomem_node once, - * so if we now try to append another event and succeed, the - * user wouldn't know which messages got dropped. Instead, - * just drop them all!! */ - return; - } - - r = n_dhcp4_c_event_node_new(&node); - if (r < 0) - goto handle_nomem; - - va_start(ap, fmt); - r = vasprintf(&message, fmt, ap); - va_end(ap); - - if (r < 0) { - n_dhcp4_c_event_node_free(node); - goto handle_nomem; - } - - node->event = (NDhcp4ClientEvent) { - .event = N_DHCP4_CLIENT_EVENT_LOG, - .log = { - .level = level, - .message = message, - .allow_steal_message = true, - }, - }; - - c_list_link_tail(log_queue->event_list, &node->client_link); - return; - -handle_nomem: - c_list_link_tail(log_queue->event_list, &log_queue->nomem_node.client_link); -} - -/** - * n_dhcp4_client_arm_timer() - update timer - * @client: client to operate on - * - * This updates the timer on @client to fire on the next pending timeout. This - * must be called whenever a timeout on @client might have changed. - */ -void n_dhcp4_client_arm_timer(NDhcp4Client *client) { - uint64_t now, offset, timeout = 0; - int r; - - if (client->current_probe) - n_dhcp4_client_probe_get_timeout(client->current_probe, &timeout); - - if (timeout != client->scheduled_timeout) { - /* - * Across our codebase, timeouts are specified as absolute - * timestamps on CLOCK_BOOTTIME. Unfortunately, there are - * systems with CLOCK_BOOTTIME support, but timerfd lacks it - * (in particular RHEL). Therefore, our timerfd might be on - * CLOCK_MONOTONIC. - * To account for this, we always schedule a relative timeout. - * We fetch the current time and then calculate the offset - * which we then schedule as relative timeout on the timerfd. - * This works regardless which clock the timerfd runs on. - * Once we no longer support CLOCK_MONOTONIC as fallback, we - * can simply switch to TFD_TIMER_ABSTIME here and specify - * `timeout` directly as value. - */ - now = n_dhcp4_gettime(CLOCK_BOOTTIME); - if (now >= timeout) - offset = 1; /* 0 would disarm the timerfd */ - else - offset = timeout - now; - - r = timerfd_settime(client->fd_timer, - 0, - &(struct itimerspec){ - .it_value = { - .tv_sec = offset / UINT64_C(1000000000), - .tv_nsec = offset % UINT64_C(1000000000), - }, - }, - NULL); - c_assert(r >= 0); - - client->scheduled_timeout = timeout; - } -} - -/** - * n_dhcp4_client_get_fd() - retrieve event FD - * @client: client to operate on - * @fdp: output argument to store FD - * - * This retrieves the FD used by the client object given as @client. The FD is - * always valid, and returned in @fdp. - * - * The caller is expected to poll this FD for readable events and call - * n_dhcp4_client_dispatch() whenever the FD is readable. - */ -_c_public_ void n_dhcp4_client_get_fd(NDhcp4Client *client, int *fdp) { - *fdp = client->fd_epoll; -} - -static int n_dhcp4_client_dispatch_timer(NDhcp4Client *client, struct epoll_event *event) { - uint64_t v, ns_now; - int r; - - if (event->events & (EPOLLHUP | EPOLLERR)) { - /* - * There is no way to handle either gracefully. If we ignored - * them, we would busy-loop, so lets rather forward the error - * to the caller. - */ - return -ENOTRECOVERABLE; - } - - if (event->events & EPOLLIN) { - r = read(client->fd_timer, &v, sizeof(v)); - if (r < 0) { - if (errno == EAGAIN) { - /* - * There are no more pending events, so nothing - * to be done. Return to the caller. - */ - return 0; - } - - /* - * Something failed. We use CLOCK_BOOTTIME/MONOTONIC, - * so ECANCELED cannot happen. Hence, there is no error - * that we could gracefully handle. Fail hard and let - * the caller deal with it. - */ - return -errno; - } else if (r != sizeof(v) || v == 0) { - /* - * Kernel guarantees 8-byte reads, and only to return - * data if at least one timer triggered; fail hard if - * it suddenly starts exposing unexpected behavior. - */ - return -ENOTRECOVERABLE; - } - - /* - * Forward the timer-event to the active probe. Timers should - * not fire if there is no probe running, but lets ignore them - * for now, so probe-internals are not leaked to this generic - * client dispatcher. - */ - if (client->current_probe) { - /* - * Read the current time *after* dispatching the timer, - * to make sure we do not miss wakeups. - */ - ns_now = n_dhcp4_gettime(CLOCK_BOOTTIME); - - r = n_dhcp4_client_probe_dispatch_timer(client->current_probe, - ns_now); - if (r) - return r; - } - } - - return 0; -} - -static int n_dhcp4_client_dispatch_io(NDhcp4Client *client, struct epoll_event *event) { - int r; - - if (client->current_probe) - r = n_dhcp4_client_probe_dispatch_io(client->current_probe, - event->events); - else - return -ENOTRECOVERABLE; - - return r; -} - -/** - * n_dhcp4_client_dispatch() - dispatch client - * @client: client to operate on - * - * This dispatches pending operations on @client. It will read incoming - * messages, write pending data, and handle any timeouts. - * - * This function never blocks. - * - * If there are more events to dispatch, than would be reasonable to do in a - * single dispatch, this will return N_DHCP4_E_PREEMPTED. In this case the - * caller is expected to call into this function again when it is ready to - * dispatch more events. - * If your event loop is level-triggered (it very likely is), you can - * optionally ignore this return code and treat it as success. - * - * Return: 0 on success, negative error code on failure, N_DHCP4_E_PREEMPTED if - * there is more data to dispatch. - */ -_c_public_ int n_dhcp4_client_dispatch(NDhcp4Client *client) { - struct epoll_event events[2]; - int n, i, r = 0; - - n = epoll_wait(client->fd_epoll, events, sizeof(events) / sizeof(*events), 0); - if (n < 0) { - /* Linux never returns EINTR if `timeout == 0'. */ - return -errno; - } - - client->preempted = false; - - for (i = 0; i < n; ++i) { - switch (events[i].data.u32) { - case N_DHCP4_CLIENT_EPOLL_TIMER: - r = n_dhcp4_client_dispatch_timer(client, events + i); - break; - case N_DHCP4_CLIENT_EPOLL_IO: - r = n_dhcp4_client_dispatch_io(client, events + i); - break; - default: - c_assert(0); - r = 0; - break; - } - - if (r) { - if (r == N_DHCP4_E_DOWN) { - r = n_dhcp4_client_raise(client, - NULL, - N_DHCP4_CLIENT_EVENT_DOWN); - if (r) - return r; - - /* continue normally */ - } else if (r) { - if (r >= _N_DHCP4_E_INTERNAL) { - n_dhcp4_log(&client->log_queue, - LOG_ERR, - "invalid internal error code %d after dispatch", - r); - return N_DHCP4_E_INTERNAL; - } - return r; - } - } - } - - n_dhcp4_client_arm_timer(client); - - return client->preempted ? N_DHCP4_E_PREEMPTED : 0; -} - -/** - * n_dhcp4_client_pop_event() - fetch pending event - * @client: client to operate on - * @eventp: output argument to store next event - * - * This fetches the next pending event from the event-queue and returns it to a - * caller. A pointer to the event is stored in @eventp. If there is no more - * event queued, NULL is returned. - * - * If a valid event is returned, it is accessible until the next call to this - * function, or the destruction of the context object (this might be either the - * client object or the probe object, pointed to by the event), whichever - * happens first. - * That is, the caller should not pin the returned event object, but copy - * required information into their own state tracking contexts. - * - * The possible events are: - * * N_DHCP4_CLIENT_EVENT_OFFER: A lease offered from a server in response - * to a probe. Several such offers may be - * received until one of them is selected by - * the caller. Only one lease may be selected. - * The attached lease object may be queried - * for information in order to decide which - * lease to select, though the information is - * not guaranteed to stay the same in the - * final lease. - * * N_DHCP4_CLIENT_EVENT_GRANTED: A selected lease was granted by the server. - * The information in the attached lease - * object should be used to configure the - * client. Once the client has been - * configured, the lease should be accepted. - * * N_DHCP4_CLIENT_EVENT_RETRACTED: A selected lease offer was retracted by the - * server. This can happen in case the server - * offers the same lease to several clients, - * or the server discovers that the IP address - * in the lease is already in use. - * * N_DHCP4_CLIENT_EVENT_EXTENDED: An active lease is extended, if applicable - * the kernel should be updated with the new - * lifetime information for addresses and/or - * routes. - * * N_DHCP4_CLIENT_EVENT_EXPIRED: An active lease failed to be extended by - * the end of its lifetime. The client should - * immediately stop using the information - * contained in the lease. - * * N_DHCP4_CLIENT_EVENT_DOWN: The network interface was put down down. - * The user is recommended to reestablish the - * lease at the first opportunity when the - * network comes back up. Note that this is - * purely informational, the probe will keep - * running, and if the network topology does - * not change any lease we have will still be - * valid. - * * N_DHCP4_CLIENT_EVENT_CANCELLED: The probe was cancelled. This can happen if - * the client attempted several incompatible - * probes in parallel, then the most recent - * ones will be cancelled asynchronously. - * * N_DHCP4_CLIENT_EVENT_LOG: A logging event if n_dhcp4_client_set_log_level() - * is enabled. - * - * Return: 0 on success, negative error code on failure. - */ -_c_public_ int n_dhcp4_client_pop_event(NDhcp4Client *client, NDhcp4ClientEvent **eventp) { - NDhcp4CEventNode *node, *t_node; - - c_list_for_each_entry_safe(node, t_node, &client->event_list, client_link) { - if (node->is_public) { - n_dhcp4_c_event_node_free(node); - continue; - } - - node->is_public = true; - *eventp = &node->event; - return 0; - } - - *eventp = NULL; - return 0; -} - -/** - * n_dhcp4_client_update_mtu() - update link mtu - * @client: client to operate on - * @mtu: new mtu - * - * This updates the link MTU used by the client object. By default, the minimum - * requirement given by the IP specification is assumed, which means 576 - * bytes. The caller is advised to update this to the actual MTU used by the - * link layer. - * - * This value reflects the MTU of the link layer. That is, it is the maximum - * packet size that you can send on that link, excluding the link-header but - * including the IP-header. On ethernet-v2 this would be 1500. - * - * If unsure, it is safe to leave this unset. However, in this case a DHCP - * server will be required to omit information if it does not fit into the - * default MTU. - * - * Unless you keep the default MTU, you should update the MTU whenever the link - * MTU changes. That is, when it is increased *and* when it is decreased. - * However, you must be aware that decreasing the MTU on a link might cause - * temporary data loss. - * - * Background: Knowing the link MTU guarantees that we can possibly transmit - * packets bigger than the IP minimum (i.e., 576 bytes). However, - * it does not guarantee that a possible target supports parsing - * packets bigger than the IP minimum. Hence, the MTU is used by a - * client to send a hint to a server that it can receive replies - * bigger than the minimum. As such, a server can reply with more - * information than otherwise possible. - * Since this DHCP client does not support fragmented packets, we - * simply set the allowed packet-size to the local link MTU. - * Note that DHCP relays might cause DHCP packets to be routed. - * However, such relays are required to always reassemble any - * fragments they receive into full DHCP packets, before they - * forward them either way. This guarantees that incoming packets - * are never fragmented, unless they exceed the local link MTU - * (this would otherwise not neccessarily be true, if some other - * part of the routed network had a lower MTU). - * - * Return: 0 on success, negative error code on failure. - */ -_c_public_ int n_dhcp4_client_update_mtu(NDhcp4Client *client, uint16_t mtu) { - int r; - - if (mtu == client->mtu) - return 0; - - if (client->current_probe) { - r = n_dhcp4_client_probe_update_mtu(client->current_probe, mtu); - if (r) - return r; - } - - client->mtu = mtu; - return 0; -} - -/** - * n_dhcp4_client_probe() - create a new probe - * @client: client to operate on - * @probep: output argument to store new probe - * @config: probe configuration to use - * - * This creates a new probe on @client. Probes represent DHCP requests and - * track the state over the entire lifetime of a lease. Once a probe is created - * it will start looking for DHCP servers, request a lease from them, and renew - * the lease continuously whenever it expires. Furthermore, if a lease cannot be - * renewed, a new lease will be requested. - * - * The API allows for many probes to be run at the same time. However, the DHCP - * specification forbids many of those cases (e.g., you must not reuse a client - * id, otherwise it will be impossible to track who to forward received packets - * to). Hence, so far only a single probe can run at a time. If you create a - * new probe, all older probes that conflict with that probe will be canceled - * (their state machine is halted and a N_DHCP4_CLIENT_EVENT_CANCELLED event is - * raised. - * This might change in the future, though. There might be cases where multiple - * probes can be run in parallel (e.g., with different client-ids, or an INFORM - * in parallel to a REQUEST, ...). - * - * Return: 0 on success, negative error code on failure. - */ -_c_public_ int n_dhcp4_client_probe(NDhcp4Client *client, - NDhcp4ClientProbe **probep, - NDhcp4ClientProbeConfig *config) { - _c_cleanup_(n_dhcp4_client_probe_freep) NDhcp4ClientProbe *probe = NULL; - uint64_t ns_now; - int r; - - ns_now = n_dhcp4_gettime(CLOCK_BOOTTIME); - - r = n_dhcp4_client_probe_new(&probe, config, client, ns_now); - if (r) - return r; - - n_dhcp4_client_arm_timer(client); - - *probep = probe; - probe = NULL; - return 0; -} diff --git a/shared/n-dhcp4/src/n-dhcp4-incoming.c b/shared/n-dhcp4/src/n-dhcp4-incoming.c deleted file mode 100644 index f739413b..00000000 --- a/shared/n-dhcp4/src/n-dhcp4-incoming.c +++ /dev/null @@ -1,428 +0,0 @@ -/* - * DHCPv4 Incoming Messages - * - * This file implements the message parser object for incoming DHCP4 messages. - * It takes a linear data blob as input, and provides accessors for the message - * content. - * - * This wrapper mainly deals with the OPTIONs array. That is, in hides the - * different overload-sections the DHCP4 spec defines, it concatenates - * duplicate option fields (as described by the spec), and provides a - * consistent view to the caller. - * - * Internally, for every incoming message we linearize its OPTIONs. This means, - * we create a copy of the contents, and merge all duplicate options into a - * single option entry. We then provide accessors to the caller to easily get - * O(1) access to individual fields. - */ - -#include <assert.h> -#include <c-stdaux.h> -#include <endian.h> -#include <errno.h> -#include <inttypes.h> -#include <netinet/ip.h> -#include <netinet/udp.h> -#include <stdbool.h> -#include <stddef.h> -#include <stdlib.h> -#include <string.h> -#include "n-dhcp4.h" -#include "n-dhcp4-private.h" - -static void n_dhcp4_incoming_prefetch(NDhcp4Incoming *incoming, size_t *offset, uint8_t option, const uint8_t *raw, size_t n_raw) { - uint8_t o, l; - size_t pos; - - for (pos = 0; pos < n_raw; ) { - o = raw[pos++]; - if (o == N_DHCP4_OPTION_PAD) - continue; - if (o == N_DHCP4_OPTION_END) - return; - - /* bail out if no remaining space for length field */ - if (pos >= n_raw) - return; - - /* bail out if length exceeds the available space */ - l = raw[pos++]; - if (l > n_raw || pos > n_raw - l) - return; - - /* prefetch content if it matches @option */ - if (o == option) { - memcpy((uint8_t *)&incoming->message + *offset, raw + pos, l); - *offset += l; - } - - pos += l; - } -} - -static void n_dhcp4_incoming_merge(NDhcp4Incoming *incoming, size_t *offset, uint8_t overload, uint8_t option) { - uint8_t *m = (uint8_t *)&incoming->message; - size_t pos; - - /* - * Prefetch all options matching @option from the 3 sections, - * concatenating their content. Remember the offset and size of the - * option in our message state. - */ - - pos = *offset; - - /* prefetch option from OPTIONS */ - n_dhcp4_incoming_prefetch(incoming, offset, option, - m + offsetof(NDhcp4Message, options), - incoming->n_message - offsetof(NDhcp4Message, options)); - - /* prefetch option from FILE */ - if (overload & N_DHCP4_OVERLOAD_FILE) - n_dhcp4_incoming_prefetch(incoming, offset, option, - m + offsetof(NDhcp4Message, file), - sizeof(incoming->message.file)); - - /* prefetch option from SNAME */ - if (overload & N_DHCP4_OVERLOAD_SNAME) - n_dhcp4_incoming_prefetch(incoming, offset, option, - m + offsetof(NDhcp4Message, sname), - sizeof(incoming->message.sname)); - - incoming->options[option].value = m + pos; - incoming->options[option].size = *offset - pos; -} - -static void n_dhcp4_incoming_linearize(NDhcp4Incoming *incoming) { - uint8_t *m, o, l, overload; - size_t i, pos, end, offset; - - /* - * Linearize all OPTIONs of the incoming message. We know that - * @incoming->message is preallocated to be big enough to hold the - * entire linearized message _trailing_ the original copy. All we have - * to do is walk the raw message in @incoming->message and for each - * option we find, copy it into the trailing space, concatenating all - * instances we find. - * - * Before we can copy the individual options, we must scan for the - * OVERLOAD option. This is required so our prefetcher knows which data - * arrays to scan for prefetching. - * - * So far, we require the OVERLOAD option to be present in the - * options-array (which is obvious and a given). However, if the option - * occurs multiple times outside of the options-array (i.e., SNAME or - * FILE), we silently ignore them. The specification does not allow - * multiple OVERLOAD options, anyway. Hence, this behavior only defines - * what we do when we see broken implementations, and we currently seem - * to support all styles we saw in the wild so far. - */ - - m = (uint8_t *)&incoming->message; - offset = incoming->n_message; - - n_dhcp4_incoming_merge(incoming, &offset, 0, N_DHCP4_OPTION_OVERLOAD); - if (incoming->options[N_DHCP4_OPTION_OVERLOAD].size >= 1) - overload = *incoming->options[N_DHCP4_OPTION_OVERLOAD].value; - else - overload = 0; - - for (i = 0; i < 3; ++i) { - if (i == 0) { /* walk OPTIONS */ - pos = offsetof(NDhcp4Message, options); - end = incoming->n_message; - } else if (i == 1) { /* walk FILE */ - if (!(overload & N_DHCP4_OVERLOAD_FILE)) - continue; - - pos = offsetof(NDhcp4Message, file); - end = pos + sizeof(incoming->message.file); - } else { /* walk SNAME */ - if (!(overload & N_DHCP4_OVERLOAD_SNAME)) - continue; - - pos = offsetof(NDhcp4Message, sname); - end = pos + sizeof(incoming->message.sname); - } - - while (pos < end) { - o = m[pos++]; - if (o == N_DHCP4_OPTION_PAD) - continue; - if (o == N_DHCP4_OPTION_END) - break; - if (pos >= end) - break; - - l = m[pos++]; - if (l > end || pos > end - l) - break; - - if (!incoming->options[o].value) - n_dhcp4_incoming_merge(incoming, &offset, overload, o); - - pos += l; - } - } -} - -/** - * n_dhcp4_incoming_new() - Allocate new incoming message object - * @incomingp: output argument for new object - * @raw: raw message blob - * @n_raw: length of the raw message blob - * - * This function allocates a new incoming-message object to wrap a received - * message blob. It performs basic verification of the message length and - * header, and then linearizes the DHCP4 options. - * - * The incoming-message object mainly provides accessors for the option-array. - * It handles all the different quirks around parsing and concatenating the - * options array, and provides the assembled data to the caller. It does not, - * however, in any way interpret the data of the individual options. This is up - * to the caller to do. - * - * Return: 0 on success, negative error code on failure, N_DHCP4_E_MALFORMED if - * the message is not a valid DHCP4 message. - */ -int n_dhcp4_incoming_new(NDhcp4Incoming **incomingp, const void *raw, size_t n_raw) { - _c_cleanup_(n_dhcp4_incoming_freep) NDhcp4Incoming *incoming = NULL; - size_t size; - - if (n_raw < sizeof(NDhcp4Message) || n_raw > UINT16_MAX) - return N_DHCP4_E_MALFORMED; - - /* - * Allocate enough space for book-keeping, a copy of @raw and trailing - * space for linearized options. The trailing space must be big enough - * to hold the entire options array unmodified (linearizing can only - * make it smaller). Hence, just allocate enough space to hold the raw - * message without the header. - */ - size = sizeof(*incoming) + n_raw - sizeof(NDhcp4Message); - size += n_raw - sizeof(NDhcp4Header); - - incoming = calloc(1, size); - if (!incoming) - return -ENOMEM; - - *incoming = (NDhcp4Incoming)N_DHCP4_INCOMING_NULL(*incoming); - incoming->n_message = n_raw; - memcpy(&incoming->message, raw, n_raw); - - if (incoming->message.magic != htobe32(N_DHCP4_MESSAGE_MAGIC)) - return N_DHCP4_E_MALFORMED; - - /* linearize options */ - n_dhcp4_incoming_linearize(incoming); - - *incomingp = incoming; - incoming = NULL; - return 0; -} - -/** - * n_dhcp4_incoming_free() - Deallocate message object - * @incoming: object to operate on, or NULL - * - * This deallocates and frees the given incoming-message object. If NULL is - * passed, this is a no-op. - * - * Return: NULL is returned. - */ -NDhcp4Incoming *n_dhcp4_incoming_free(NDhcp4Incoming *incoming) { - if (!incoming) - return NULL; - - free(incoming); - - return NULL; -} - -/** - * n_dhcp4_incoming_get_header() - Return message header - * @incoming: message to operate on - * - * This returns a pointer to the message header. Note that modifications to - * this header are permanent and will affect the original message. - * - * Return: A pointer to the message header is returned. - */ -NDhcp4Header *n_dhcp4_incoming_get_header(NDhcp4Incoming *incoming) { - return &incoming->message.header; -} - -/** - * n_dhcp4_incoming_get_raw() - Get access to the raw original message - * @incoming: message to operate on - * @rawp: output argument for the raw blob, or NULL - * - * This hands out a pointer to the raw message blob to the caller. This will - * point to the original message content, rather than the linearized version. - * - * Note that if the caller queried the contents of the message before, any - * modifications done by the caller will not affect the original message. It - * only affects the linearized content (which is a duplicate trailing the - * original message). However, modifications to the message header *DO* also - * appear in the original, since the message header is not duplicated. - * - * In either case, it is better to never modify the message, if you intend to - * forward it further. - * - * Return: The length of the raw message blob is returned. - */ -size_t n_dhcp4_incoming_get_raw(NDhcp4Incoming *incoming, const void **rawp) { - if (rawp) - *rawp = &incoming->message; - return incoming->n_message; -} - -/** - * n_dhcp4_incoming_query() - Query the contents of a specific option - * @incoming: message to query - * @option: option to look for - * @datap: output argument for the option-data, or NULL - * @n_datap: output argument for the length of the option, or NULL - * - * This returns a pointer to the requested option blob in the message. It - * points to a linearized version of all respective option-fields of the same - * type. Hence, the caller is not required to deal with multiple occurrences of - * the same option. - * - * If an option was not present in the incoming message, N_DHCP4_E_UNSET is - * returned. Note that this is different from an empty option! And empty option - * will return a valid pointer and size 0. - * - * Note that the pointer to the option-blob does not point to the original - * message, but a duplicated version. Modifications to the blob will not be - * reflected in the original message, but they will be permanent regarding - * further queries through this function. - * - * Note that the original message alignment might no longer be reflected in the - * returned blob. You must not alias the content of the blob, but always copy - * it out, or consume piecemeal. - * - * This function runs in O(1). - * - * Return: 0 on success, negative error code on failure, N_DHCP4_E_UNSET if the - * option was not found, - */ -int n_dhcp4_incoming_query(NDhcp4Incoming *incoming, uint8_t option, uint8_t **datap, size_t *n_datap) { - if (!incoming->options[option].value) - return N_DHCP4_E_UNSET; - - if (datap) - *datap = incoming->options[option].value; - if (n_datap) - *n_datap = incoming->options[option].size; - return 0; -} - -static int n_dhcp4_incoming_query_u8(NDhcp4Incoming *message, uint8_t option, uint8_t *u8p) { - uint8_t *data; - size_t n_data; - int r; - - r = n_dhcp4_incoming_query(message, option, &data, &n_data); - if (r) - return r; - else if (n_data < sizeof(*data)) - return N_DHCP4_E_MALFORMED; - - *u8p = *data; - return 0; -} - -static int n_dhcp4_incoming_query_u16(NDhcp4Incoming *message, uint8_t option, uint16_t *u16p) { - uint8_t *data; - size_t n_data; - uint16_t be16; - int r; - - r = n_dhcp4_incoming_query(message, option, &data, &n_data); - if (r) - return r; - else if (n_data < sizeof(be16)) - return N_DHCP4_E_MALFORMED; - - memcpy(&be16, data, sizeof(be16)); - - *u16p = ntohs(be16); - return 0; -} - -static int n_dhcp4_incoming_query_u32(NDhcp4Incoming *message, uint8_t option, uint32_t *u32p) { - uint8_t *data; - size_t n_data; - uint32_t be32; - int r; - - r = n_dhcp4_incoming_query(message, option, &data, &n_data); - if (r) - return r; - else if (n_data < sizeof(be32)) - return N_DHCP4_E_MALFORMED; - - memcpy(&be32, data, sizeof(be32)); - - *u32p = ntohl(be32); - return 0; -} - -static int n_dhcp4_incoming_query_in_addr(NDhcp4Incoming *message, uint8_t option, struct in_addr *addrp) { - uint8_t *data; - size_t n_data; - uint32_t be32; - int r; - - r = n_dhcp4_incoming_query(message, option, &data, &n_data); - if (r) - return r; - else if (n_data < sizeof(be32)) - return N_DHCP4_E_MALFORMED; - - memcpy(&be32, data, sizeof(be32)); - - addrp->s_addr = be32; - return 0; -} - -int n_dhcp4_incoming_query_message_type(NDhcp4Incoming *message, uint8_t *typep) { - return n_dhcp4_incoming_query_u8(message, N_DHCP4_OPTION_MESSAGE_TYPE, typep); -} - -int n_dhcp4_incoming_query_lifetime(NDhcp4Incoming *message, uint32_t *lifetimep) { - return n_dhcp4_incoming_query_u32(message, N_DHCP4_OPTION_IP_ADDRESS_LEASE_TIME, lifetimep); -} - -int n_dhcp4_incoming_query_t2(NDhcp4Incoming *message, uint32_t *t2p) { - return n_dhcp4_incoming_query_u32(message, N_DHCP4_OPTION_REBINDING_T2_TIME, t2p); -} - -int n_dhcp4_incoming_query_t1(NDhcp4Incoming *message, uint32_t *t1p) { - return n_dhcp4_incoming_query_u32(message, N_DHCP4_OPTION_RENEWAL_T1_TIME, t1p); -} - -int n_dhcp4_incoming_query_server_identifier(NDhcp4Incoming *message, struct in_addr *idp) { - return n_dhcp4_incoming_query_in_addr(message, N_DHCP4_OPTION_SERVER_IDENTIFIER, idp); -} - -int n_dhcp4_incoming_query_max_message_size(NDhcp4Incoming *message, uint16_t *max_message_sizep) { - return n_dhcp4_incoming_query_u16(message, N_DHCP4_OPTION_MAXIMUM_MESSAGE_SIZE, max_message_sizep); -} - -int n_dhcp4_incoming_query_requested_ip(NDhcp4Incoming *message, struct in_addr *requested_ipp) { - return n_dhcp4_incoming_query_in_addr(message, N_DHCP4_OPTION_REQUESTED_IP_ADDRESS, requested_ipp); -} - -void n_dhcp4_incoming_get_xid(NDhcp4Incoming *message, uint32_t *xidp) { - NDhcp4Header *header = n_dhcp4_incoming_get_header(message); - - *xidp = header->xid; -} - -void n_dhcp4_incoming_get_yiaddr(NDhcp4Incoming *message, struct in_addr *yiaddr) { - NDhcp4Header *header = n_dhcp4_incoming_get_header(message); - - yiaddr->s_addr = header->yiaddr; -} diff --git a/shared/n-dhcp4/src/n-dhcp4-outgoing.c b/shared/n-dhcp4/src/n-dhcp4-outgoing.c deleted file mode 100644 index bcab407f..00000000 --- a/shared/n-dhcp4/src/n-dhcp4-outgoing.c +++ /dev/null @@ -1,373 +0,0 @@ -/* - * DHCPv4 Outgoing Messages - * - * XXX - */ - -#include <assert.h> -#include <c-stdaux.h> -#include <endian.h> -#include <errno.h> -#include <inttypes.h> -#include <netinet/ip.h> -#include <netinet/udp.h> -#include <stdbool.h> -#include <stddef.h> -#include <stdlib.h> -#include <string.h> -#include "n-dhcp4.h" -#include "n-dhcp4-private.h" - -/** - * N_DHCP4_OUTGOING_MAX_PHDR - maximum protocol header size - * - * All DHCP4 messages-limits specify the size of the entire packet including - * the protocol layer (i.e., including the IP headers and UDP headers). To - * calculate the size we have remaining for the actual DHCP message, we need to - * substract the maximum possible header-length the linux-kernel might prepend - * to our messages. This turns out to be the maximum IP-header size (including - * optional IP headers, hence 60 bytes) plus the UDP header size (i.e., 8 - * bytes). - */ -#define N_DHCP4_OUTGOING_MAX_PHDR (N_DHCP4_NETWORK_IP_MAXIMUM_HEADER_SIZE + sizeof(struct udphdr)) - -/** - * n_dhcp4_outgoing_new() - Allocate new outgoing message - * @outgoingp: output argument to return allocate object through - * @max_size: maximum transmission size to use - * @overload: select sections to overload - * - * This allocates a new outgoing message and returns it to the caller. The - * caller can then append data to it and send it over the wire. - * - * The @max_size parameter specifies the transport-layer MTU to consider. If 0, - * N_DHCP4_NETWORK_IP_MINIMUM_MAX_SIZE is used. Note that this argument - * specifies the maximum packet size *INCLUDING* the IP-headers and UDP-header. - * Internally, the allocator makes sure to never create packets bigger than the - * specified MTU. The append functions will return an error, if the packet size - * would exceed the MTU. - * If you use a full UDP stack that supports packet fragmentation, you can - * specify the maximum packet size here (e.g., UINT16_MAX). - * - * Return: 0 on success, error code on failure. - */ -int n_dhcp4_outgoing_new(NDhcp4Outgoing **outgoingp, size_t max_size, uint8_t overload) { - _c_cleanup_(n_dhcp4_outgoing_freep) NDhcp4Outgoing *outgoing = NULL; - - /* - * Make sure the minimum limit is bigger than the maximum protocol - * header plus the DHCP-message-header plus a single OPTION_END byte. - */ - static_assert(N_DHCP4_NETWORK_IP_MINIMUM_MAX_SIZE >= N_DHCP4_OUTGOING_MAX_PHDR + - sizeof(NDhcp4Message) + 1, - "Invalid minimum IP packet limit"); - - c_assert(!(overload & ~(N_DHCP4_OVERLOAD_FILE | N_DHCP4_OVERLOAD_SNAME))); - - outgoing = calloc(1, sizeof(*outgoing)); - if (!outgoing) - return -ENOMEM; - - *outgoing = (NDhcp4Outgoing)N_DHCP4_OUTGOING_NULL(*outgoing); - outgoing->n_message = N_DHCP4_NETWORK_IP_MINIMUM_MAX_SIZE - N_DHCP4_OUTGOING_MAX_PHDR; - outgoing->i_message = offsetof(NDhcp4Message, options); - outgoing->max_size = outgoing->n_message; - outgoing->overload = overload; - - if (max_size > N_DHCP4_NETWORK_IP_MINIMUM_MAX_SIZE) - outgoing->max_size = max_size - N_DHCP4_OUTGOING_MAX_PHDR; - - outgoing->message = calloc(1, outgoing->n_message); - if (!outgoing->message) - return -ENOMEM; - - outgoing->message->magic = htonl(N_DHCP4_MESSAGE_MAGIC); - outgoing->message->options[0] = N_DHCP4_OPTION_END; - - *outgoingp = outgoing; - outgoing = NULL; - return 0; -} - -/** - * n_dhcp4_outgoing_free() - Deallocate outgoing message - * @outgoing: message to deallocate, or NULL - * - * This is the opposite to n_dhcp4_outgoing_new(). It deallocates and frees the - * passed object. If @outgoing is NULL, this is a no-op. - * - * Return: NULL is returned. - */ -NDhcp4Outgoing *n_dhcp4_outgoing_free(NDhcp4Outgoing *outgoing) { - if (!outgoing) - return NULL; - - free(outgoing->message); - free(outgoing); - - return NULL; -} - -/** - * n_dhcp4_outgoing_get_header() - Get pointer to the message header - * @outgoing: message to operate on - * - * This returns a pointer to the DHCP4 message header to the caller. The caller - * can use this to fill-in the header-fields. Note that all fields are - * initialized to their default values. Hence, you only need to override the - * fields where the default is not sufficient. - * - * Return: A pointer to the message header is returned. - */ -NDhcp4Header *n_dhcp4_outgoing_get_header(NDhcp4Outgoing *outgoing) { - return &outgoing->message->header; -} - -/** - * n_dhcp4_outgoing_get_raw() - Get the raw message blob - * @outgoing: message to operat on - * @rawp: output argument for the message-blob - * - * This function gives the caller access to the raw message-blob. That is, once - * message-marshaling is complete, use this to get the raw blob for sending. - * Note that this blob is only valid as long as you no longer append any - * further options to the message, nor modify it in any other way. - * - * Return: The size of the raw message blob is returned. - */ -size_t n_dhcp4_outgoing_get_raw(NDhcp4Outgoing *outgoing, const void **rawp) { - if (rawp) - *rawp = outgoing->message; - - /* - * Return the DHCP message until the END option, excluding any - * trailing padding. We overallocate during append, so the - * allocated message might be bigger than what we want to - * send on the wire. - */ - return outgoing->i_message + 1; -} - -static void n_dhcp4_outgoing_append_option(NDhcp4Outgoing *outgoing, - uint8_t option, - const void *data, - uint8_t n_data) { - uint8_t *blob = (void *)outgoing->message; - - blob[outgoing->i_message++] = option; - blob[outgoing->i_message++] = n_data; - memcpy(blob + outgoing->i_message, data, n_data); - outgoing->i_message += n_data; - blob[outgoing->i_message] = N_DHCP4_OPTION_END; -} - -/** - * n_dhcp4_outgoing_append() - Append option to outgoing message - * @outgoing: message to operate on - * @option: option code to append - * @data: data to append in the option - * @n_data: length of the data blob - * - * This appends another option to the given outgoing message. The data is taken - * verbatim and copied into the message. Note that no validation is done. If - * you provide an option multiple times, it will be added multiple times (spec - * then requires them to be interpreted as concatenated option, in case the - * option is marked as such). - * - * The size of a message is limited, based on the restriction passed to the - * outgoing-message constructor. If there is not enough free space to copy in - * the new option, N_DHCP4_E_NO_SPACE is returned. - * - * The order in which you append options might matter to some implementations. - * For example, the message-type is often expected to be the first option. We - * do not place such restrictions, but for compatibility with external - * implementations, you should follow these recommendations. - * Furthermore, we do not implement any kind of smart allocators. That is, all - * options are simply appended when you call this. But due to the overloading - * feature, fragmentation might matter. Hence, if you use overloading, overly - * big options might cause padding, and as such waste space. - * - * Return: 0 on success, negative error code on failure, N_DHCP4_E_NO_SPACE - * when there is not sufficient free space in the message. - */ -int n_dhcp4_outgoing_append(NDhcp4Outgoing *outgoing, - uint8_t option, - const void *data, - uint8_t n_data) { - NDhcp4Message *m; - uint8_t overload; - size_t rem, n; - - c_assert(option != N_DHCP4_OPTION_PAD); - c_assert(option != N_DHCP4_OPTION_END); - c_assert(option != N_DHCP4_OPTION_OVERLOAD); - - /* - * If the iterator is on the OPTIONs field, try appending the new blob. - * We need 2 header-bytes plus @n_data bytes. Additionally, we always - * reserve 3 trailing bytes for a possible OVERLOAD option, and 1 byte - * for the END marker. - */ - if (outgoing->i_message >= offsetof(NDhcp4Message, options)) { - rem = outgoing->n_message - outgoing->i_message; - - /* try fitting into remaining OPTIONs space */ - if (rem >= n_data + 2U + 3U + 1U) { - n_dhcp4_outgoing_append_option(outgoing, option, data, n_data); - return 0; - } - - /* try fitting into allowed OPTIONs space */ - if (outgoing->max_size - outgoing->i_message >= n_data + 2U + 3U + 1U) { - /* try over-allocation to reduce allocation pressure */ - n = outgoing->n_message + n_data + 128; - if (n > outgoing->max_size) - n = outgoing->max_size; - m = realloc(outgoing->message, n); - if (!m) - return -ENOMEM; - - memset((void *)m + outgoing->i_message, 0, n - outgoing->i_message); - outgoing->message = m; - outgoing->n_message = n; - n_dhcp4_outgoing_append_option(outgoing, option, data, n_data); - return 0; - } - - /* not enough remaining space, try OVERLOAD */ - if (!outgoing->overload) - return N_DHCP4_E_NO_SPACE; - - /* - * We ran out of space in the OPTIONs array, but overloading - * was enabled. This means, we can insert an OVERLOAD option - * and then use SNAME/FILE to store more options. - * Note that the three different sections cannot overlap and - * all must have an END marker. So as soon as we add the - * OVERLOAD option, we must make sure the other sections have - * the valid END marker. From then on, our *_append_option() - * helper makes sure to move the END marker with every - * insertion. - */ - overload = outgoing->overload; - n_dhcp4_outgoing_append_option(outgoing, N_DHCP4_OPTION_OVERLOAD, &overload, 1); - - if (overload & N_DHCP4_OVERLOAD_FILE) - outgoing->message->file[0] = N_DHCP4_OPTION_END; - if (overload & N_DHCP4_OVERLOAD_SNAME) - outgoing->message->sname[0] = N_DHCP4_OPTION_END; - - if (overload & N_DHCP4_OVERLOAD_FILE) - outgoing->i_message = offsetof(NDhcp4Message, file); - else if (overload & N_DHCP4_OVERLOAD_SNAME) - outgoing->i_message = offsetof(NDhcp4Message, sname); - } - - /* - * The OPTIONs section is full and OVERLOAD was enabled. Try writing - * into the FILE section. Always reserve 1 byte for the trailing END - * marker. - */ - if (outgoing->i_message >= offsetof(NDhcp4Message, file)) { - rem = sizeof(outgoing->message->file); - rem -= outgoing->i_message - offsetof(NDhcp4Message, file); - - if (rem >= n_data + 2U + 1U) { - n_dhcp4_outgoing_append_option(outgoing, option, data, n_data); - return 0; - } - - overload = outgoing->overload; - if (overload & N_DHCP4_OVERLOAD_SNAME) - outgoing->i_message = offsetof(NDhcp4Message, sname); - else - return N_DHCP4_E_NO_SPACE; - } - - /* - * OPTIONs and FILE are full, try putting data into the SNAME section - * as a last resort. - */ - if (outgoing->i_message >= offsetof(NDhcp4Message, sname)) { - rem = sizeof(outgoing->message->sname); - rem -= outgoing->i_message - offsetof(NDhcp4Message, sname); - - if (rem >= n_data + 2U + 1U) { - n_dhcp4_outgoing_append_option(outgoing, option, data, n_data); - return 0; - } - } - - return N_DHCP4_E_NO_SPACE; -} - -static int n_dhcp4_outgoing_append_u32(NDhcp4Outgoing *message, uint8_t option, uint32_t u32) { - uint32_t be32 = htonl(u32); - int r; - - r = n_dhcp4_outgoing_append(message, option, &be32, sizeof(be32)); - if (r) - return r; - - return 0; -} - -static int n_dhcp4_outgoing_append_in_addr(NDhcp4Outgoing *message, uint8_t option, struct in_addr addr) { - int r; - - r = n_dhcp4_outgoing_append(message, option, &addr.s_addr, sizeof(addr.s_addr)); - if (r) - return r; - - return 0; -} - -int n_dhcp4_outgoing_append_t1(NDhcp4Outgoing *message, uint32_t t1) { - return n_dhcp4_outgoing_append_u32(message, N_DHCP4_OPTION_RENEWAL_T1_TIME, t1); -} - -int n_dhcp4_outgoing_append_t2(NDhcp4Outgoing *message, uint32_t t2) { - return n_dhcp4_outgoing_append_u32(message, N_DHCP4_OPTION_REBINDING_T2_TIME, t2); -} - -int n_dhcp4_outgoing_append_lifetime(NDhcp4Outgoing *message, uint32_t lifetime) { - return n_dhcp4_outgoing_append_u32(message, N_DHCP4_OPTION_IP_ADDRESS_LEASE_TIME, lifetime); -} - -int n_dhcp4_outgoing_append_server_identifier(NDhcp4Outgoing *message, struct in_addr addr) { - return n_dhcp4_outgoing_append_in_addr(message, N_DHCP4_OPTION_SERVER_IDENTIFIER, addr); -} - -int n_dhcp4_outgoing_append_requested_ip(NDhcp4Outgoing *message, struct in_addr addr) { - return n_dhcp4_outgoing_append_in_addr(message, N_DHCP4_OPTION_REQUESTED_IP_ADDRESS, addr); -} - -void n_dhcp4_outgoing_set_secs(NDhcp4Outgoing *message, uint16_t secs) { - NDhcp4Header *header = n_dhcp4_outgoing_get_header(message); - - /* - * Some DHCP servers will reject DISCOVER or REQUEST messages if 'secs' - * is not set (i.e., set to 0), even though the spec allows it. - */ - c_assert(secs); - - header->secs = htons(secs); -} - -void n_dhcp4_outgoing_set_xid(NDhcp4Outgoing *message, uint32_t xid) { - NDhcp4Header *header = n_dhcp4_outgoing_get_header(message); - - header->xid = xid; -} - -void n_dhcp4_outgoing_get_xid(NDhcp4Outgoing *message, uint32_t *xidp) { - NDhcp4Header *header = n_dhcp4_outgoing_get_header(message); - - *xidp = header->xid; -} - -void n_dhcp4_outgoing_set_yiaddr(NDhcp4Outgoing *message, struct in_addr yiaddr) { - NDhcp4Header *header = n_dhcp4_outgoing_get_header(message); - - header->yiaddr = yiaddr.s_addr; -} diff --git a/shared/n-dhcp4/src/n-dhcp4-private.h b/shared/n-dhcp4/src/n-dhcp4-private.h deleted file mode 100644 index db7b24ff..00000000 --- a/shared/n-dhcp4/src/n-dhcp4-private.h +++ /dev/null @@ -1,759 +0,0 @@ -#pragma once - -#include <arpa/inet.h> -#include <assert.h> -#include <c-list.h> -#include <c-stdaux.h> -#include <endian.h> -#include <inttypes.h> -#include <limits.h> -#include <stdbool.h> -#include <stdlib.h> -#include <time.h> -#include <unistd.h> -#include <syslog.h> -#include "n-dhcp4.h" - -typedef struct NDhcp4CConnection NDhcp4CConnection; -typedef struct NDhcp4CEventNode NDhcp4CEventNode; -typedef struct NDhcp4ClientProbeOption NDhcp4ClientProbeOption; -typedef struct NDhcp4Header NDhcp4Header; -typedef struct NDhcp4Incoming NDhcp4Incoming; -typedef struct NDhcp4Message NDhcp4Message; -typedef struct NDhcp4Outgoing NDhcp4Outgoing; -typedef struct NDhcp4SConnection NDhcp4SConnection; -typedef struct NDhcp4SConnectionIp NDhcp4SConnectionIp; -typedef struct NDhcp4SEventNode NDhcp4SEventNode; -typedef struct NDhcp4LogQueue NDhcp4LogQueue; - -/* specs */ - -#define N_DHCP4_NETWORK_IP_MAXIMUM_HEADER_SIZE (60) /* See RFC791 */ -#define N_DHCP4_NETWORK_IP_MINIMUM_MAX_SIZE (576) /* See RFC791 */ -#define N_DHCP4_NETWORK_SERVER_PORT (67) -#define N_DHCP4_NETWORK_CLIENT_PORT (68) -#define N_DHCP4_MESSAGE_MAGIC ((uint32_t)(0x63825363)) -#define N_DHCP4_MESSAGE_FLAG_BROADCAST (htons(0x8000)) - -enum { - N_DHCP4_OP_BOOTREQUEST = 1, - N_DHCP4_OP_BOOTREPLY = 2, -}; - -enum { - N_DHCP4_OPTION_PAD = 0, - N_DHCP4_OPTION_SUBNET_MASK = 1, - N_DHCP4_OPTION_TIME_OFFSET = 2, - N_DHCP4_OPTION_ROUTER = 3, - N_DHCP4_OPTION_DOMAIN_NAME_SERVER = 6, - N_DHCP4_OPTION_HOST_NAME = 12, - N_DHCP4_OPTION_BOOT_FILE_SIZE = 13, - N_DHCP4_OPTION_DOMAIN_NAME = 15, - N_DHCP4_OPTION_ROOT_PATH = 17, - N_DHCP4_OPTION_ENABLE_IP_FORWARDING = 19, - N_DHCP4_OPTION_ENABLE_IP_FORWARDING_NL = 20, - N_DHCP4_OPTION_POLICY_FILTER = 21, - N_DHCP4_OPTION_INTERFACE_MDR = 22, - N_DHCP4_OPTION_INTERFACE_TTL = 23, - N_DHCP4_OPTION_INTERFACE_MTU_AGING_TIMEOUT = 24, - N_DHCP4_OPTION_INTERFACE_MTU = 26, - N_DHCP4_OPTION_BROADCAST = 28, - N_DHCP4_OPTION_STATIC_ROUTE = 33, - N_DHCP4_OPTION_NTP_SERVER = 42, - N_DHCP4_OPTION_VENDOR_SPECIFIC = 43, - N_DHCP4_OPTION_REQUESTED_IP_ADDRESS = 50, - N_DHCP4_OPTION_IP_ADDRESS_LEASE_TIME = 51, - N_DHCP4_OPTION_OVERLOAD = 52, - N_DHCP4_OPTION_MESSAGE_TYPE = 53, - N_DHCP4_OPTION_SERVER_IDENTIFIER = 54, - N_DHCP4_OPTION_PARAMETER_REQUEST_LIST = 55, - N_DHCP4_OPTION_ERROR_MESSAGE = 56, - N_DHCP4_OPTION_MAXIMUM_MESSAGE_SIZE = 57, - N_DHCP4_OPTION_RENEWAL_T1_TIME = 58, - N_DHCP4_OPTION_REBINDING_T2_TIME = 59, - N_DHCP4_OPTION_VENDOR_CLASS_IDENTIFIER = 60, - N_DHCP4_OPTION_CLIENT_IDENTIFIER = 61, - N_DHCP4_OPTION_FQDN = 81, - N_DHCP4_OPTION_NEW_POSIX_TIMEZONE = 100, - N_DHCP4_OPTION_NEW_TZDB_TIMEZONE = 101, - N_DHCP4_OPTION_CLASSLESS_STATIC_ROUTE = 121, - N_DHCP4_OPTION_PRIVATE_BASE = 224, - N_DHCP4_OPTION_PRIVATE_LAST = 254, - N_DHCP4_OPTION_END = 255, - _N_DHCP4_OPTION_N = 256, -}; - -enum { - N_DHCP4_OVERLOAD_FILE = 1, - N_DHCP4_OVERLOAD_SNAME = 2, -}; - -enum { - N_DHCP4_MESSAGE_DISCOVER = 1, - N_DHCP4_MESSAGE_OFFER = 2, - N_DHCP4_MESSAGE_REQUEST = 3, - N_DHCP4_MESSAGE_DECLINE = 4, - N_DHCP4_MESSAGE_ACK = 5, - N_DHCP4_MESSAGE_NAK = 6, - N_DHCP4_MESSAGE_RELEASE = 7, - N_DHCP4_MESSAGE_INFORM = 8, - N_DHCP4_MESSAGE_FORCERENEW = 9, -}; - -struct NDhcp4Header { - uint8_t op; - uint8_t htype; - uint8_t hlen; - uint8_t hops; - uint32_t xid; - uint16_t secs; - uint16_t flags; - uint32_t ciaddr; - uint32_t yiaddr; - uint32_t siaddr; - uint32_t giaddr; - uint8_t chaddr[16]; -} _c_packed_; - -struct NDhcp4Message { - NDhcp4Header header; - uint8_t sname[64]; - uint8_t file[128]; - uint32_t magic; - uint8_t options[]; -} _c_packed_; - -/* objects */ - -enum { - _N_DHCP4_E_INTERNAL = _N_DHCP4_E_N, - - N_DHCP4_E_UNEXPECTED, - - N_DHCP4_E_NO_SPACE, - N_DHCP4_E_MALFORMED, - - N_DHCP4_E_DROPPED, - N_DHCP4_E_DOWN, - N_DHCP4_E_AGAIN, -}; - -enum { - N_DHCP4_C_CONNECTION_STATE_INIT, - N_DHCP4_C_CONNECTION_STATE_PACKET, - N_DHCP4_C_CONNECTION_STATE_DRAINING, - N_DHCP4_C_CONNECTION_STATE_UDP, - N_DHCP4_C_CONNECTION_STATE_CLOSED, -}; - -enum { - N_DHCP4_CLIENT_EPOLL_TIMER, - N_DHCP4_CLIENT_EPOLL_IO, -}; - -enum { - N_DHCP4_CLIENT_PROBE_STATE_INIT, - N_DHCP4_CLIENT_PROBE_STATE_INIT_REBOOT, - N_DHCP4_CLIENT_PROBE_STATE_SELECTING, - N_DHCP4_CLIENT_PROBE_STATE_REBOOTING, - N_DHCP4_CLIENT_PROBE_STATE_REQUESTING, - N_DHCP4_CLIENT_PROBE_STATE_GRANTED, - N_DHCP4_CLIENT_PROBE_STATE_BOUND, - N_DHCP4_CLIENT_PROBE_STATE_RENEWING, - N_DHCP4_CLIENT_PROBE_STATE_REBINDING, - N_DHCP4_CLIENT_PROBE_STATE_EXPIRED, -}; - -enum { - N_DHCP4_CLIENT_LEASE_STATE_INIT, - N_DHCP4_CLIENT_LEASE_STATE_OFFERED, - N_DHCP4_CLIENT_LEASE_STATE_SELECTED, - N_DHCP4_CLIENT_LEASE_STATE_DECLINED, - N_DHCP4_CLIENT_LEASE_STATE_ACKED, -}; - -enum { - N_DHCP4_SERVER_EPOLL_TIMER, - N_DHCP4_SERVER_EPOLL_IO, -}; - -enum { - _N_DHCP4_C_MESSAGE_INVALID = 0, - N_DHCP4_C_MESSAGE_DISCOVER, - N_DHCP4_C_MESSAGE_INFORM, - N_DHCP4_C_MESSAGE_SELECT, - N_DHCP4_C_MESSAGE_IGNORE, - N_DHCP4_C_MESSAGE_RENEW, - N_DHCP4_C_MESSAGE_REBIND, - N_DHCP4_C_MESSAGE_REBOOT, - N_DHCP4_C_MESSAGE_RELEASE, - N_DHCP4_C_MESSAGE_DECLINE, -}; - -struct NDhcp4Outgoing { - NDhcp4Message *message; - size_t n_message; - size_t i_message; - size_t max_size; - - uint8_t overload : 2; - - struct { - uint8_t type; - uint8_t message_type; - uint32_t client_addr; - uint64_t start_time; - uint64_t base_time; - uint64_t send_time; - uint64_t send_jitter; - size_t n_send; - } userdata; -}; - -#define N_DHCP4_OUTGOING_NULL(_x) { \ - } - -struct NDhcp4Incoming { - struct { - uint8_t *value; - size_t size; - } options[_N_DHCP4_OPTION_N]; - - struct { - uint8_t type; - uint64_t start_time; - uint64_t base_time; - } userdata; - - size_t n_message; - NDhcp4Message message; - /* @message must be the last member */ -}; - -#define N_DHCP4_INCOMING_NULL(_x) { \ - } - -struct NDhcp4ClientConfig { - int ifindex; - unsigned int transport; - bool request_broadcast; - uint8_t mac[32]; /* MAX_ADDR_LEN */ - size_t n_mac; - uint8_t broadcast_mac[32]; /* MAX_ADDR_LEN */ - size_t n_broadcast_mac; - uint8_t *client_id; - size_t n_client_id; -}; - -#define N_DHCP4_CLIENT_CONFIG_NULL(_x) { \ - .transport = _N_DHCP4_TRANSPORT_N, \ - } - -struct NDhcp4ClientProbeOption { - uint8_t option; - uint8_t n_data; - uint8_t data[]; -}; - -#define N_DHCP4_CLIENT_PROBE_OPTION_NULL(_x) { \ - .option = N_DHCP4_OPTION_PAD, \ - } - -struct NDhcp4ClientProbeConfig { - bool inform_only; - bool init_reboot; - struct in_addr requested_ip; - unsigned short int entropy[3]; - uint64_t ms_start_delay; /* max ms to wait before starting probe */ - NDhcp4ClientProbeOption *options[UINT8_MAX + 1]; - int8_t request_parameters[UINT8_MAX + 1]; - size_t n_request_parameters; -}; - -#define N_DHCP4_CLIENT_PROBE_CONFIG_NULL(_x) { \ - .ms_start_delay = N_DHCP4_CLIENT_START_DELAY_RFC2131, \ - } - -struct NDhcp4CEventNode { - CList client_link; - CList probe_link; - NDhcp4ClientEvent event; - bool is_public : 1; -}; - -#define N_DHCP4_C_EVENT_NODE_NULL(_x) { \ - .client_link = C_LIST_INIT((_x).client_link), \ - .probe_link = C_LIST_INIT((_x).probe_link), \ - } - -struct NDhcp4LogQueue { - CList *event_list; - NDhcp4CEventNode nomem_node; - int log_level; - bool is_client : 1; -}; - -#define N_DHCP4_LOG_QUEUE_NULL_DEFUNCT() { \ - .log_level = -1, \ - .is_client = false, \ - } - -#define N_DHCP4_LOG_QUEUE_NULL_CLIENT(client) { \ - .event_list = &((client).event_list), \ - .log_level = -1, \ - .is_client = true, \ - .nomem_node = { \ - .client_link = C_LIST_INIT((client).log_queue.nomem_node.client_link), \ - .probe_link = C_LIST_INIT((client).log_queue.nomem_node.probe_link), \ - .event = { \ - .event = N_DHCP4_CLIENT_EVENT_LOG, \ - .log = { \ - .level = LOG_CRIT, \ - .message = "one or more logging messages dropped due to out of memory", \ - .allow_steal_message = false, \ - }, \ - }, \ - .is_public = false, \ - }, \ - } - -struct NDhcp4CConnection { - NDhcp4ClientConfig *client_config; - NDhcp4ClientProbeConfig *probe_config; - NDhcp4LogQueue *log_queue; - - int fd_epoll; - - unsigned int state; /* current connection state */ - int fd_packet; /* packet socket */ - int fd_udp; /* udp socket */ - - NDhcp4Outgoing *request; /* current request */ - - uint32_t client_ip; /* client IP address, or 0 */ - uint32_t server_ip; /* server IP address, or 0 */ - uint16_t mtu; /* client mtu, or 0 */ - - /* - * When we get DHCP packets from the kernel, we need a buffer to read - * the data into. Since UDP packets can be up to 2^16 bytes in size, we - * avoid placing it on the stack and instead read into this scratch - * buffer. It is purely meant as stack replacement, no data is returned - * through this buffer. - */ - uint8_t scratch_buffer[UINT16_MAX]; -}; - -#define N_DHCP4_C_CONNECTION_NULL(_x) { \ - .fd_packet = -1, \ - .fd_udp = -1, \ - } - -struct NDhcp4Client { - unsigned long n_refs; - NDhcp4ClientConfig *config; - CList event_list; - - NDhcp4LogQueue log_queue; - - int fd_epoll; - int fd_timer; - - uint16_t mtu; - NDhcp4ClientProbe *current_probe; - uint64_t scheduled_timeout; - - bool preempted : 1; -}; - -#define N_DHCP4_CLIENT_NULL(_x) { \ - .n_refs = 1, \ - .event_list = C_LIST_INIT((_x).event_list), \ - .fd_epoll = -1, \ - .fd_timer = -1, \ - .log_queue = N_DHCP4_LOG_QUEUE_NULL_CLIENT(_x), \ - } - -struct NDhcp4ClientProbe { - NDhcp4ClientProbeConfig *config; - NDhcp4Client *client; - CList event_list; - CList lease_list; - void *userdata; - - unsigned int state; /* current probe state */ - struct in_addr last_address; /* last address obtained */ - uint64_t ns_deferred; /* timeout for deferred action */ - uint64_t ns_reinit; - uint64_t ns_nak_restart_delay; /* restart delay after a nak */ - NDhcp4ClientLease *current_lease; /* current lease */ - - NDhcp4CConnection connection; /* client connection wrapper */ -}; - -#define N_DHCP4_CLIENT_PROBE_NULL(_x) { \ - .event_list = C_LIST_INIT((_x).event_list), \ - .lease_list = C_LIST_INIT((_x).lease_list), \ - .connection = N_DHCP4_C_CONNECTION_NULL((_x).connection), \ - } - -struct NDhcp4ClientLease { - unsigned long n_refs; - - NDhcp4ClientProbe *probe; - CList probe_link; - - NDhcp4Incoming *message; - - uint64_t t1; - uint64_t t2; - uint64_t lifetime; -}; - -#define N_DHCP4_CLIENT_LEASE_NULL(_x) { \ - .n_refs = 1, \ - .probe_link = C_LIST_INIT((_x).probe_link), \ - } - -struct NDhcp4ServerConfig { - int ifindex; -}; - -#define N_DHCP4_SERVER_CONFIG_NULL(_x) { \ - } - -struct NDhcp4SEventNode { - CList server_link; - NDhcp4ServerEvent event; - bool is_public : 1; -}; - -#define N_DHCP4_S_EVENT_NODE_NULL(_x) { \ - .server_link = C_LIST_INIT((_x).server_link), \ - } - -struct NDhcp4SConnection { - int ifindex; /* interface index */ - int fd_packet; /* packet socket */ - int fd_udp; /* udp socket */ - uint8_t buf[UINT16_MAX]; /* scratch recevie buffer */ - - /* XXX: support a set of server addresses */ - NDhcp4SConnectionIp *ip; /* server IP address, or NULL */ -}; - -#define N_DHCP4_S_CONNECTION_NULL(_x) { \ - .fd_packet = -1, \ - .fd_udp = -1, \ - } - -struct NDhcp4SConnectionIp { - NDhcp4SConnection *connection; - struct in_addr ip; -}; - -#define N_DHCP4_S_CONNECTION_IP_NULL(_x) { \ -} - -struct NDhcp4Server { - unsigned long n_refs; - CList event_list; - CList lease_list; - - bool preempted : 1; - - NDhcp4SConnection connection; -}; - -#define N_DHCP4_SERVER_NULL(_x) { \ - .n_refs = 1, \ - .event_list = C_LIST_INIT((_x).event_list), \ - .lease_list = C_LIST_INIT((_x).lease_list), \ - .connection = N_DHCP4_S_CONNECTION_NULL((_x).connection), \ - } - -struct NDhcp4ServerIp { - NDhcp4SConnectionIp ip; -}; - -#define N_DHCP4_SERVER_IP_NULL(_x) { \ - .ip = N_DHCP4_S_CONNECTION_IP_NULL((_x).ip), \ - } - -struct NDhcp4ServerLease { - unsigned long n_refs; - - NDhcp4Server *server; - CList server_link; - - NDhcp4Incoming *request; - NDhcp4Incoming *reply; -}; - -#define N_DHCP4_SERVER_LEASE_NULL(_x) { \ - .n_refs = 1, \ - .server_link = C_LIST_INIT((_x).server_link), \ - } - -/* outgoing messages */ - -int n_dhcp4_outgoing_new(NDhcp4Outgoing **outgoingp, size_t max_size, uint8_t overload); -NDhcp4Outgoing *n_dhcp4_outgoing_free(NDhcp4Outgoing *outgoing); - -NDhcp4Header *n_dhcp4_outgoing_get_header(NDhcp4Outgoing *outgoing); -size_t n_dhcp4_outgoing_get_raw(NDhcp4Outgoing *outgoing, const void **rawp); -int n_dhcp4_outgoing_append(NDhcp4Outgoing *outgoing, uint8_t option, const void *data, uint8_t n_data); - -int n_dhcp4_outgoing_append_t1(NDhcp4Outgoing *message, uint32_t t1); -int n_dhcp4_outgoing_append_t2(NDhcp4Outgoing *message, uint32_t t2); -int n_dhcp4_outgoing_append_lifetime(NDhcp4Outgoing *message, uint32_t lifetime); -int n_dhcp4_outgoing_append_server_identifier(NDhcp4Outgoing *message, struct in_addr addr); -int n_dhcp4_outgoing_append_requested_ip(NDhcp4Outgoing *message, struct in_addr addr); - -void n_dhcp4_outgoing_set_secs(NDhcp4Outgoing *message, uint16_t secs); -void n_dhcp4_outgoing_set_xid(NDhcp4Outgoing *message, uint32_t xid); -void n_dhcp4_outgoing_set_yiaddr(NDhcp4Outgoing *message, struct in_addr yiaddr); - -void n_dhcp4_outgoing_get_xid(NDhcp4Outgoing *message, uint32_t *xidp); - -/* incoming messages */ - -int n_dhcp4_incoming_new(NDhcp4Incoming **incomingp, const void *raw, size_t n_raw); -NDhcp4Incoming *n_dhcp4_incoming_free(NDhcp4Incoming *incoming); - -NDhcp4Header *n_dhcp4_incoming_get_header(NDhcp4Incoming *incoming); -size_t n_dhcp4_incoming_get_raw(NDhcp4Incoming *incoming, const void **rawp); -int n_dhcp4_incoming_query(NDhcp4Incoming *incoming, uint8_t option, uint8_t **datap, size_t *n_datap); - -int n_dhcp4_incoming_query_message_type(NDhcp4Incoming *message, uint8_t *typep); -int n_dhcp4_incoming_query_lifetime(NDhcp4Incoming *message, uint32_t *lifetimep); -int n_dhcp4_incoming_query_t2(NDhcp4Incoming *message, uint32_t *t2p); -int n_dhcp4_incoming_query_t1(NDhcp4Incoming *message, uint32_t *t1p); -int n_dhcp4_incoming_query_server_identifier(NDhcp4Incoming *message, struct in_addr *idp); -int n_dhcp4_incoming_query_max_message_size(NDhcp4Incoming *message, uint16_t *max_message_sizep); -int n_dhcp4_incoming_query_requested_ip(NDhcp4Incoming *message, struct in_addr *requested_ipp); - -void n_dhcp4_incoming_get_xid(NDhcp4Incoming *message, uint32_t *xidp); -void n_dhcp4_incoming_get_yiaddr(NDhcp4Incoming *message, struct in_addr *yiaddr); - -/* sockets */ - -int n_dhcp4_c_socket_packet_new(int *sockfdp, int ifindex); -int n_dhcp4_c_socket_udp_new(int *sockfdp, - int ifindex, - const struct in_addr *client_addr, - const struct in_addr *server_addr); -int n_dhcp4_s_socket_packet_new(int *sockfdp); -int n_dhcp4_s_socket_udp_new(int *sockfdp, int ifindex); - -int n_dhcp4_c_socket_packet_send(int sockfd, - int ifindex, - const unsigned char *dest_haddr, - unsigned char halen, - NDhcp4Outgoing *message); -int n_dhcp4_c_socket_udp_send(int sockfd, NDhcp4Outgoing *message); -int n_dhcp4_c_socket_udp_broadcast(int sockfd, NDhcp4Outgoing *message); -int n_dhcp4_s_socket_packet_send(int sockfd, - int ifindex, - const struct in_addr *src_inaddr, - const unsigned char *dest_haddr, - unsigned char halen, - const struct in_addr *dest_inaddr, - NDhcp4Outgoing *message); -int n_dhcp4_s_socket_udp_send(int sockfd, - const struct in_addr *inaddr_src, - const struct in_addr *inaddr_dest, - NDhcp4Outgoing *message); -int n_dhcp4_s_socket_udp_broadcast(int sockfd, - const struct in_addr *inaddr_src, - NDhcp4Outgoing *message); - -int n_dhcp4_c_socket_packet_recv(int sockfd, - uint8_t *buf, - size_t n_buf, - NDhcp4Incoming **messagep); -int n_dhcp4_c_socket_udp_recv(int sockfd, - uint8_t *buf, - size_t n_buf, - NDhcp4Incoming **messagep); -int n_dhcp4_s_socket_udp_recv(int sockfd, - uint8_t *buf, - size_t n_buf, - NDhcp4Incoming **messagep, - struct sockaddr_in *dest); - -/* client configs */ - -int n_dhcp4_client_config_dup(NDhcp4ClientConfig *config, - NDhcp4ClientConfig **dupp); - -/* client probe configs */ - -int n_dhcp4_client_probe_config_dup(NDhcp4ClientProbeConfig *config, - NDhcp4ClientProbeConfig **dupp); -uint32_t n_dhcp4_client_probe_config_get_random(NDhcp4ClientProbeConfig *config); - -/* client events */ - -int n_dhcp4_c_event_node_new(NDhcp4CEventNode **nodep); -NDhcp4CEventNode *n_dhcp4_c_event_node_free(NDhcp4CEventNode *node); - -/* client connections */ - -int n_dhcp4_c_connection_init(NDhcp4CConnection *connection, - NDhcp4ClientConfig *client_config, - NDhcp4ClientProbeConfig *probe_config, - NDhcp4LogQueue *log_queue, - int fd_epoll); -void n_dhcp4_c_connection_deinit(NDhcp4CConnection *connection); - -int n_dhcp4_c_connection_listen(NDhcp4CConnection *connection); -int n_dhcp4_c_connection_connect(NDhcp4CConnection *connection, - const struct in_addr *client, - const struct in_addr *server); -void n_dhcp4_c_connection_close(NDhcp4CConnection *connection); - -void n_dhcp4_c_connection_get_timeout(NDhcp4CConnection *connection, - uint64_t *timeoutp); - -int n_dhcp4_c_connection_discover_new(NDhcp4CConnection *connection, - NDhcp4Outgoing **request); -int n_dhcp4_c_connection_select_new(NDhcp4CConnection *connection, - NDhcp4Outgoing **request, - NDhcp4Incoming *offer); -int n_dhcp4_c_connection_reboot_new(NDhcp4CConnection *connection, - NDhcp4Outgoing **request, - const struct in_addr *client); -int n_dhcp4_c_connection_renew_new(NDhcp4CConnection *connection, - NDhcp4Outgoing **request); -int n_dhcp4_c_connection_rebind_new(NDhcp4CConnection *connection, - NDhcp4Outgoing **request); -int n_dhcp4_c_connection_decline_new(NDhcp4CConnection *connection, - NDhcp4Outgoing **request, - NDhcp4Incoming *ack, - const char *error); -int n_dhcp4_c_connection_inform_new(NDhcp4CConnection *connection, - NDhcp4Outgoing **request); -int n_dhcp4_c_connection_release_new(NDhcp4CConnection *connection, - NDhcp4Outgoing **request, - const char *error); - -int n_dhcp4_c_connection_start_request(NDhcp4CConnection *connection, - NDhcp4Outgoing *request, - uint64_t timestamp); -int n_dhcp4_c_connection_dispatch_timer(NDhcp4CConnection *connection, - uint64_t timestamp); -int n_dhcp4_c_connection_dispatch_io(NDhcp4CConnection *connection, - NDhcp4Incoming **messagep); - -/* clients */ - -int n_dhcp4_client_raise(NDhcp4Client *client, NDhcp4CEventNode **nodep, unsigned int event); -void n_dhcp4_client_arm_timer(NDhcp4Client *client); - -/* client probes */ - -int n_dhcp4_client_probe_new(NDhcp4ClientProbe **probep, - NDhcp4ClientProbeConfig *config, - NDhcp4Client *client, - uint64_t ns_now); - -int n_dhcp4_client_probe_raise(NDhcp4ClientProbe *probe, NDhcp4CEventNode **nodep, unsigned int event); -void n_dhcp4_client_probe_get_timeout(NDhcp4ClientProbe *probe, uint64_t *timeoutp); -int n_dhcp4_client_probe_dispatch_timer(NDhcp4ClientProbe *probe, uint64_t ns_now); -int n_dhcp4_client_probe_dispatch_io(NDhcp4ClientProbe *probe, uint32_t events); -int n_dhcp4_client_probe_transition_select(NDhcp4ClientProbe *probe, NDhcp4Incoming *offer, uint64_t ns_now); -int n_dhcp4_client_probe_transition_accept(NDhcp4ClientProbe *probe, NDhcp4Incoming *ack); -int n_dhcp4_client_probe_transition_decline(NDhcp4ClientProbe *probe, NDhcp4Incoming *offer, const char *error, uint64_t ns_now); -int n_dhcp4_client_probe_update_mtu(NDhcp4ClientProbe *probe, uint16_t mtu); - -/* client leases */ - -int n_dhcp4_client_lease_new(NDhcp4ClientLease **leasep, NDhcp4Incoming *message); -void n_dhcp4_client_lease_link(NDhcp4ClientLease *lease, NDhcp4ClientProbe *probe); -void n_dhcp4_client_lease_unlink(NDhcp4ClientLease *lease); - -/* server connections */ - -int n_dhcp4_s_connection_init(NDhcp4SConnection *connection, int ifindex); -void n_dhcp4_s_connection_deinit(NDhcp4SConnection *connection); - -void n_dhcp4_s_connection_get_fd(NDhcp4SConnection *connection, int *fdp); -int n_dhcp4_s_connection_dispatch_io(NDhcp4SConnection *connection, NDhcp4Incoming **messagep); - -int n_dhcp4_s_connection_offer_new(NDhcp4SConnection *connection, - NDhcp4Outgoing **replyp, - NDhcp4Incoming *request, - const struct in_addr *server_address, - const struct in_addr *client_address, - uint32_t lifetime); -int n_dhcp4_s_connection_ack_new(NDhcp4SConnection *connection, - NDhcp4Outgoing **replyp, - NDhcp4Incoming *request, - const struct in_addr *server_address, - const struct in_addr *client_address, - uint32_t lifetime); -int n_dhcp4_s_connection_nak_new(NDhcp4SConnection *connection, - NDhcp4Outgoing **replyp, - NDhcp4Incoming *request, - const struct in_addr *server_address); - -int n_dhcp4_s_connection_send_reply(NDhcp4SConnection *connection, - const struct in_addr *server_addr, - NDhcp4Outgoing *reply); - -/* server connection ips */ - -void n_dhcp4_s_connection_ip_init(NDhcp4SConnectionIp *ip, struct in_addr addr); -void n_dhcp4_s_connection_ip_deinit(NDhcp4SConnectionIp *ip); - -void n_dhcp4_s_connection_ip_link(NDhcp4SConnectionIp *ip, NDhcp4SConnection *connection); -void n_dhcp4_s_connection_ip_unlink(NDhcp4SConnectionIp *ip); - -/* inline helpers */ - -static inline void n_dhcp4_outgoing_freep(NDhcp4Outgoing **outgoing) { - if (*outgoing) - n_dhcp4_outgoing_free(*outgoing); -} - -static inline void n_dhcp4_incoming_freep(NDhcp4Incoming **incoming) { - if (*incoming) - n_dhcp4_incoming_free(*incoming); -} - -static inline uint64_t n_dhcp4_gettime(clockid_t clock) { - struct timespec ts; - int r; - - r = clock_gettime(clock, &ts); - c_assert(r >= 0); - - return ts.tv_sec * 1000ULL * 1000ULL * 1000ULL + ts.tv_nsec; -} - -void n_dhcp4_log_queue_fmt(NDhcp4LogQueue *log_queue, - int level, - const char *fmt, - ...) _c_printf_(3, 4); - -/** - * n_dhcp4_log() - append a logging event - * @x_log_queue: the logging event queue - * @x_level: the syslog logging level for the message. - * @...: the format string and arguments. - * - * Warning: this macro only evaluates the format arguments if the logging - * level is enabled. - */ -#define n_dhcp4_log(x_log_queue, x_level, ...) \ - do { \ - NDhcp4LogQueue *const _log_queue = (x_log_queue); \ - const int _level = (x_level); \ - \ - if (_level <= _log_queue->log_level) { \ - n_dhcp4_log_queue_fmt(_log_queue, \ - _level, \ - __VA_ARGS__); \ - } \ - } while (0) diff --git a/shared/n-dhcp4/src/n-dhcp4-socket.c b/shared/n-dhcp4/src/n-dhcp4-socket.c deleted file mode 100644 index 3e703b4f..00000000 --- a/shared/n-dhcp4/src/n-dhcp4-socket.c +++ /dev/null @@ -1,661 +0,0 @@ -/* - * DHCP specific low-level socket helpers - */ - -#include <c-stdaux.h> -#include <errno.h> -#include <linux/filter.h> -#include <sys/socket.h> /* needed by linux/if.h */ -#include <linux/if.h> -#include <linux/if_packet.h> -#include <linux/netdevice.h> -#include <linux/udp.h> -#include <netinet/ip.h> -#include <stddef.h> -#include <stdlib.h> -#include <stdint.h> -#include <string.h> -#include <sys/types.h> -#include "n-dhcp4-private.h" -#include "util/packet.h" -#include "util/socket.h" - -/** - * n_dhcp4_c_socket_packet_new() - create a new DHCP4 client packet socket - * @sockfdp: return argument for the new socket - * @ifindex: interface index to bind to - * - * Create a new AF_PACKET/SOCK_DGRAM socket usable to listen to and send DHCP client - * packets before an IP address has been configured. - * - * Only unfragmented DHCP packets from a server to a client destined for the given - * ifindex is returned. - * - * Return: 0 on success, or a negative error code on failure. - */ -int n_dhcp4_c_socket_packet_new(int *sockfdp, int ifindex) { - _c_cleanup_(c_closep) int sockfd = -1; - struct sock_filter filter[] = { - /* - * IP - * - * Check - * - UDP - * - Unfragmented - * - Large enough to fit the DHCP header - * - * Leave X the size of the IP header, for future indirect reads. - */ - BPF_STMT(BPF_LD + BPF_B + BPF_ABS, offsetof(struct iphdr, protocol)), /* A <- IP protocol */ - BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, IPPROTO_UDP, 1, 0), /* IP protocol == UDP ? */ - BPF_STMT(BPF_RET + BPF_K, 0), /* ignore */ - - BPF_STMT(BPF_LD + BPF_H + BPF_ABS, offsetof(struct iphdr, frag_off)), /* A <- Flags + Fragment offset */ - BPF_STMT(BPF_ALU + BPF_AND + BPF_K, IP_MF | IP_OFFMASK), /* A <- A & (IP_MF | IP_OFFMASK) */ - BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, 0, 1, 0), /* fragmented packet ? */ - BPF_STMT(BPF_RET + BPF_K, 0), /* ignore */ - - BPF_STMT(BPF_LDX + BPF_B + BPF_MSH, 0), /* X <- IP header length */ - BPF_STMT(BPF_LD + BPF_W + BPF_LEN, 0), /* A <- packet length */ - BPF_STMT(BPF_ALU + BPF_SUB + BPF_X, 0), /* A -= X */ - BPF_JUMP(BPF_JMP + BPF_JGE + BPF_K, sizeof(struct udphdr) + sizeof(NDhcp4Message), 1, 0), /* packet >= DHCPPacket ? */ - BPF_STMT(BPF_RET + BPF_K, 0), /* ignore */ - - /* - * UDP - * - * Check - * - DHCP client port - * - * Leave X the size of IP and UDP headers, for future indirect reads. - */ - BPF_STMT(BPF_LD + BPF_H + BPF_IND, offsetof(struct udphdr, dest)), /* A <- UDP destination port */ - BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, N_DHCP4_NETWORK_CLIENT_PORT, 1, 0), /* UDP destination port == DHCP client port ? */ - BPF_STMT(BPF_RET + BPF_K, 0), /* ignore */ - - BPF_STMT(BPF_LD + BPF_W + BPF_K, sizeof(struct udphdr)), /* A <- size of UDP header */ - BPF_STMT(BPF_ALU + BPF_ADD + BPF_X, 0), /* A += X */ - BPF_STMT(BPF_MISC + BPF_TAX, 0), /* X <- A */ - - /* - * DHCP - * - * Check - * - BOOTREPLY (from server to client) - * - DHCP magic cookie - */ - BPF_STMT(BPF_LD + BPF_B + BPF_IND, offsetof(NDhcp4Header, op)), /* A <- DHCP op */ - BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, N_DHCP4_OP_BOOTREPLY, 1, 0), /* op == BOOTREPLY ? */ - BPF_STMT(BPF_RET + BPF_K, 0), /* ignore */ - - BPF_STMT(BPF_LD + BPF_W + BPF_IND, offsetof(NDhcp4Message, magic)), /* A <- DHCP magic cookie */ - BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, N_DHCP4_MESSAGE_MAGIC, 1, 0), /* cookie == DHCP magic cookie ? */ - BPF_STMT(BPF_RET + BPF_K, 0), /* ignore */ - - BPF_STMT(BPF_RET + BPF_K, 65535), /* return all */ - }; - struct sock_fprog fprog = { - .filter = filter, - .len = sizeof(filter) / sizeof(filter[0]), - }; - struct sockaddr_ll addr = { - .sll_family = AF_PACKET, - .sll_protocol = htons(ETH_P_IP), - .sll_ifindex = ifindex, - }; - int r, on = 1; - - sockfd = socket(AF_PACKET, SOCK_DGRAM | SOCK_CLOEXEC | SOCK_NONBLOCK, 0); - if (sockfd < 0) - return -errno; - - r = setsockopt(sockfd, SOL_SOCKET, SO_ATTACH_FILTER, &fprog, sizeof(fprog)); - if (r < 0) - return -errno; - - /* We need the flag that tells us if the checksum is correct. */ - r = setsockopt(sockfd, SOL_PACKET, PACKET_AUXDATA, &on, sizeof(on)); - if (r < 0) - return -errno; - - r = bind(sockfd, (struct sockaddr*)&addr, sizeof(addr)); - if (r < 0) - return -errno; - - *sockfdp = sockfd; - sockfd = -1; - return 0; -} - -/** - * n_dhcp4_c_socket_udp_new() - create a new DHCP4 client UDP socket - * @sockfdp: return argument for the new socket - * @ifindex: interface index to bind to - * @client_addr: client address to bind to - * @server_addr: server address to connect to - * - * Create a new AF_INET/SOCK_DGRAM socket usable to listen to and send DHCP client - * packets. - * - * The client address given in @addr must be configured on the interface @ifindex - * before the socket is created. - * - * Return: 0 on success, or a negative error code on failure. - */ -int n_dhcp4_c_socket_udp_new(int *sockfdp, - int ifindex, - const struct in_addr *client_addr, - const struct in_addr *server_addr) { - _c_cleanup_(c_closep) int sockfd = -1; - struct sock_filter filter[] = { - /* - * IP/UDP - * - * Set X to the size of IP and UDP headers, for future indirect reads. - */ - BPF_STMT(BPF_LDX + BPF_B + BPF_MSH, 0), /* X <- IP header length */ - BPF_STMT(BPF_LD + BPF_W + BPF_K, sizeof(struct udphdr)), /* A <- size of UDP header */ - BPF_STMT(BPF_ALU + BPF_ADD + BPF_X, 0), /* A += X */ - BPF_STMT(BPF_MISC + BPF_TAX, 0), /* X <- A */ - - /* - * DHCP - * - * Check - * - BOOTREPLY (from server to client) - * - DHCP magic cookie - */ - BPF_STMT(BPF_LD + BPF_B + BPF_IND, offsetof(NDhcp4Header, op)), /* A <- DHCP op */ - BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, N_DHCP4_OP_BOOTREPLY, 1, 0), /* op == BOOTREPLY ? */ - BPF_STMT(BPF_RET + BPF_K, 0), /* ignore */ - - BPF_STMT(BPF_LD + BPF_W + BPF_IND, offsetof(NDhcp4Message, magic)), /* A <- DHCP magic cookie */ - BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, N_DHCP4_MESSAGE_MAGIC, 1, 0), /* cookie == DHCP magic cookie ? */ - BPF_STMT(BPF_RET + BPF_K, 0), /* ignore */ - - BPF_STMT(BPF_RET + BPF_K, 65535), /* return all */ - }; - struct sock_fprog fprog = { - .filter = filter, - .len = sizeof(filter) / sizeof(filter[0]), - }; - struct sockaddr_in saddr = { - .sin_family = AF_INET, - .sin_addr = *client_addr, - .sin_port = htons(N_DHCP4_NETWORK_CLIENT_PORT), - }; - struct sockaddr_in daddr = { - .sin_family = AF_INET, - .sin_addr = *server_addr, - .sin_port = htons(N_DHCP4_NETWORK_SERVER_PORT), - }; - int r, tos = IPTOS_CLASS_CS6, on = 1; - - sockfd = socket(AF_INET, SOCK_DGRAM | SOCK_CLOEXEC | SOCK_NONBLOCK, 0); - if (sockfd < 0) - return -errno; - - r = setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)); - if (r < 0) - return -errno; - - r = setsockopt(sockfd, SOL_SOCKET, SO_ATTACH_FILTER, &fprog, sizeof(fprog)); - if (r < 0) - return -errno; - - r = socket_bind_if(sockfd, ifindex); - if (r) - return r; - - r = setsockopt(sockfd, SOL_SOCKET, SO_BROADCAST, &on, sizeof(on)); - if (r < 0) - return -errno; - - r = setsockopt(sockfd, IPPROTO_IP, IP_TOS, &tos, sizeof(tos)); - if (r < 0) - return -errno; - - r = bind(sockfd, (struct sockaddr*)&saddr, sizeof(saddr)); - if (r < 0) - return -errno; - - r = connect(sockfd, (struct sockaddr*)&daddr, sizeof(daddr)); - if (r < 0) - return -errno; - - *sockfdp = sockfd; - sockfd = -1; - return 0; -} - -/** - * n_dhcp4_s_socket_packet_new() - create a new DHCP4 server packet socket - * @sockfdp: return argument for the new socket - * - * Create a new AF_PACKET/SOCK_DGRAM socket usable to send DHCP packets to clients - * before they have an IP address configured, on the given interface. - * - * Return: 0 on success, or a negative error code on failure. - */ -int n_dhcp4_s_socket_packet_new(int *sockfdp) { - _c_cleanup_(c_closep) int sockfd = -1; - - sockfd = socket(AF_PACKET, SOCK_DGRAM | SOCK_CLOEXEC | SOCK_NONBLOCK, 0); - if (sockfd < 0) - return -errno; - - *sockfdp = sockfd; - sockfd = -1; - return 0; -} - -/** - * n_dhcp4_s_socket_udp_new() - create a new DHCP4 server UDP socket - * @sockfdp: return argument for the new socket - * @ifindex: intercafe index to bind to - * - * Create a new AF_INET/SOCK_DGRAM socket usable to listen to DHCP server packets, - * on the given interface. - * - * Return: 0 on success, or a negative error code on failure. - */ -int n_dhcp4_s_socket_udp_new(int *sockfdp, int ifindex) { - _c_cleanup_(c_closep) int sockfd = -1; - struct sock_filter filter[] = { - /* - * IP/UDP - * - * Set X to the size of IP and UDP headers, for future indirect reads. - */ - BPF_STMT(BPF_LDX + BPF_B + BPF_MSH, 0), /* X <- IP header length */ - BPF_STMT(BPF_LD + BPF_W + BPF_K, sizeof(struct udphdr)), /* A <- size of UDP header */ - BPF_STMT(BPF_ALU + BPF_ADD + BPF_X, 0), /* A += X */ - BPF_STMT(BPF_MISC + BPF_TAX, 0), /* X <- A */ - - /* - * DHCP - * - * Check - * - BOOTREQUEST (from client to server) - * - DHCP magic cookie - */ - - BPF_STMT(BPF_LD + BPF_B + BPF_IND, offsetof(NDhcp4Header, op)), /* A <- DHCP op */ - BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, N_DHCP4_OP_BOOTREQUEST, 1, 0), /* op == BOOTREQUEST ? */ - BPF_STMT(BPF_RET + BPF_K, 0), /* ignore */ - - BPF_STMT(BPF_LD + BPF_W + BPF_IND, offsetof(NDhcp4Message, magic)), /* A <- DHCP magic cookie */ - BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, N_DHCP4_MESSAGE_MAGIC, 1, 0), /* cookie == DHCP magic cookie ? */ - BPF_STMT(BPF_RET + BPF_K, 0), /* ignore */ - - BPF_STMT(BPF_RET + BPF_K, 65535), /* return all */ - }; - struct sock_fprog fprog = { - .filter = filter, - .len = sizeof(filter) / sizeof(filter[0]), - }; - struct sockaddr_in addr = { - .sin_family = AF_INET, - .sin_addr = { INADDR_ANY }, - .sin_port = htons(N_DHCP4_NETWORK_SERVER_PORT), - }; - int r, tos = IPTOS_CLASS_CS6, on = 1; - - sockfd = socket(AF_INET, SOCK_DGRAM | SOCK_CLOEXEC | SOCK_NONBLOCK, 0); - if (sockfd < 0) - return -errno; - - r = setsockopt(sockfd, SOL_SOCKET, SO_ATTACH_FILTER, &fprog, sizeof(fprog)); - if (r < 0) - return -errno; - - r = socket_bind_if(sockfd, ifindex); - if (r) - return r; - - r = setsockopt(sockfd, SOL_SOCKET, SO_BROADCAST, &on, sizeof(on)); - if (r < 0) - return -errno; - - r = setsockopt(sockfd, IPPROTO_IP, IP_TOS, &tos, sizeof(tos)); - if (r < 0) - return -errno; - - r = setsockopt(sockfd, IPPROTO_IP, IP_PKTINFO, &on, sizeof(on)); - if (r < 0) - return -errno; - - r = bind(sockfd, (struct sockaddr*)&addr, sizeof(addr)); - if (r < 0) - return -errno; - - *sockfdp = sockfd; - sockfd = -1; - return 0; -} - -static int n_dhcp4_socket_packet_send(int sockfd, - int ifindex, - const struct sockaddr_in *src_paddr, - const unsigned char *dest_haddr, - unsigned char halen, - const struct sockaddr_in *dest_paddr, - NDhcp4Outgoing *message) { - struct packet_sockaddr_ll haddr = { - .sll_family = AF_PACKET, - .sll_protocol = htons(ETH_P_IP), - .sll_ifindex = ifindex, - .sll_halen = halen, - }; - const void *buf; - size_t n_buf, len; - int r; - - c_assert(halen <= sizeof(haddr.sll_addr)); - - memcpy(haddr.sll_addr, dest_haddr, halen); - - n_buf = n_dhcp4_outgoing_get_raw(message, &buf); - - r = packet_sendto_udp(sockfd, buf, n_buf, &len, src_paddr, &haddr, dest_paddr); - if (r < 0) { - if (r == -EAGAIN || r == -ENOBUFS) - return N_DHCP4_E_DROPPED; - else if (r == -ENETDOWN || r == -ENXIO) - return N_DHCP4_E_DOWN; - else - return r; - } else if (len != n_buf) { - return N_DHCP4_E_DROPPED; - } - - return 0; -} - -/** - * n_dhcp4_c_socket_packet_send() - XXX - */ -int n_dhcp4_c_socket_packet_send(int sockfd, - int ifindex, - const unsigned char *dest_haddr, - unsigned char halen, - NDhcp4Outgoing *message) { - struct sockaddr_in src_paddr = { - .sin_family = AF_INET, - .sin_port = htons(N_DHCP4_NETWORK_CLIENT_PORT), - .sin_addr = { INADDR_ANY }, - }; - struct sockaddr_in dest_paddr = { - .sin_family = AF_INET, - .sin_port = htons(N_DHCP4_NETWORK_SERVER_PORT), - .sin_addr = { INADDR_BROADCAST } - }; - - return n_dhcp4_socket_packet_send(sockfd, - ifindex, - &src_paddr, - dest_haddr, - halen, - &dest_paddr, - message); -} - -/** - * n_dhcp4_c_socket_udp_send() - XXX - */ -int n_dhcp4_c_socket_udp_send(int sockfd, - NDhcp4Outgoing *message) { - const void *buf; - size_t n_buf; - ssize_t len; - - n_buf = n_dhcp4_outgoing_get_raw(message, &buf); - - len = send(sockfd, buf, n_buf, 0); - if (len < 0) { - if (errno == EAGAIN || errno == ENOBUFS) - return N_DHCP4_E_DROPPED; - else if (errno == ENETDOWN || errno == ENXIO) - return N_DHCP4_E_DOWN; - else - return -errno; - } else if ((size_t)len != n_buf) - return N_DHCP4_E_DROPPED; - - return 0; -} - -/** - * n_dhcp4_c_socket_udp_broadcast() - XXX - */ -int n_dhcp4_c_socket_udp_broadcast(int sockfd, NDhcp4Outgoing *message) { - struct sockaddr_in sockaddr_dest = { - .sin_family = AF_INET, - .sin_port = htons(N_DHCP4_NETWORK_SERVER_PORT), - .sin_addr = { INADDR_BROADCAST }, - }; - const void *buf; - size_t n_buf; - ssize_t len; - - n_buf = n_dhcp4_outgoing_get_raw(message, &buf); - - len = sendto(sockfd, - buf, - n_buf, - 0, - (struct sockaddr*)&sockaddr_dest, - sizeof(sockaddr_dest)); - if (len < 0) { - if (errno == EAGAIN || errno == ENOBUFS) - return N_DHCP4_E_DROPPED; - else if (errno == ENETDOWN || errno == ENXIO) - return N_DHCP4_E_DOWN; - else - return -errno; - } else if ((size_t)len != n_buf) - return N_DHCP4_E_DROPPED; - - return 0; -} - -/** - * n_dhcp4_s_socket_packet_send() - XXX - */ -int n_dhcp4_s_socket_packet_send(int sockfd, - int ifindex, - const struct in_addr *src_inaddr, - const unsigned char *dest_haddr, - unsigned char halen, - const struct in_addr *dest_inaddr, - NDhcp4Outgoing *message) { - struct sockaddr_in src_paddr = { - .sin_family = AF_INET, - .sin_port = htons(N_DHCP4_NETWORK_SERVER_PORT), - .sin_addr = *src_inaddr, - }; - struct sockaddr_in dest_paddr = { - .sin_family = AF_INET, - .sin_port = htons(N_DHCP4_NETWORK_CLIENT_PORT), - .sin_addr = *dest_inaddr, - }; - - return n_dhcp4_socket_packet_send(sockfd, - ifindex, - &src_paddr, - dest_haddr, - halen, - &dest_paddr, - message); -} - -/** - * n_dhcp4_s_socket_udp_send() - XXX - */ -int n_dhcp4_s_socket_udp_send(int sockfd, - const struct in_addr *inaddr_src, - const struct in_addr *inaddr_dest, - NDhcp4Outgoing *message) { - struct sockaddr_in sockaddr_dest = { - .sin_family = AF_INET, - .sin_port = htons(N_DHCP4_NETWORK_CLIENT_PORT), - .sin_addr = *inaddr_dest, - }; - struct iovec iov = {}; - union { - struct cmsghdr align; /* ensure correct stack alignment */ - char buf[CMSG_SPACE(sizeof(struct in_pktinfo))]; - } control = {}; - struct in_pktinfo pktinfo = { - .ipi_spec_dst = *inaddr_src, - }; - struct msghdr msg = { - .msg_name = (void*)&sockaddr_dest, - .msg_namelen = sizeof(sockaddr_dest), - .msg_iov = &iov, - .msg_iovlen = 1, - .msg_control = &control.buf, - .msg_controllen = sizeof(control.buf), - }; - struct cmsghdr *cmsg = CMSG_FIRSTHDR(&msg); - ssize_t len; - - cmsg->cmsg_level = IPPROTO_IP; - cmsg->cmsg_type = IP_PKTINFO; - cmsg->cmsg_len = CMSG_LEN(sizeof(struct in_pktinfo)); - memcpy(CMSG_DATA(cmsg), &pktinfo, sizeof(pktinfo)); - - iov.iov_len = n_dhcp4_outgoing_get_raw(message, (const void **)&iov.iov_base); - - len = sendmsg(sockfd, &msg, 0); - if (len < 0) { - if (errno == EAGAIN || errno == ENOBUFS) - return N_DHCP4_E_DROPPED; - else if (errno == ENETDOWN || errno == ENXIO) - return N_DHCP4_E_DOWN; - else - return -errno; - } else if ((size_t)len != iov.iov_len) - return N_DHCP4_E_DROPPED; - - return 0; -} - -int n_dhcp4_s_socket_udp_broadcast(int sockfd, - const struct in_addr *inaddr_src, - NDhcp4Outgoing *message) { - return n_dhcp4_s_socket_udp_send(sockfd, - inaddr_src, - &(const struct in_addr){INADDR_BROADCAST}, - message); -} - -int n_dhcp4_c_socket_packet_recv(int sockfd, - uint8_t *buf, - size_t n_buf, - NDhcp4Incoming **messagep) { - _c_cleanup_(n_dhcp4_incoming_freep) NDhcp4Incoming *message = NULL; - size_t len; - int r; - - r = packet_recv_udp(sockfd, buf, n_buf, &len); - if (r < 0) { - if (r == -ENETDOWN) - return N_DHCP4_E_DOWN; - else if (r == -EAGAIN) - return N_DHCP4_E_AGAIN; - else - return -errno; - } else if (len == 0) { - return N_DHCP4_E_MALFORMED; - } - - r = n_dhcp4_incoming_new(&message, buf, len); - if (r) - return r; - - *messagep = message; - message = NULL; - return 0; -} - -static int n_dhcp4_socket_udp_recv(int sockfd, - uint8_t *buf, - size_t n_buf, - NDhcp4Incoming **messagep, - struct in_pktinfo *pktinfo) { - _c_cleanup_(n_dhcp4_incoming_freep) NDhcp4Incoming *message = NULL; - struct iovec iov = { - .iov_base = buf, - .iov_len = n_buf, - }; - uint8_t cmsgbuf[CMSG_LEN(sizeof(struct in_pktinfo))]; - struct msghdr msg = { - .msg_iov = &iov, - .msg_iovlen = 1, - .msg_control = cmsgbuf, - .msg_controllen = sizeof(cmsgbuf), - }; - ssize_t len; - int r; - - len = recvmsg(sockfd, &msg, MSG_TRUNC); - if (len < 0) { - if (errno == ENETDOWN) - return N_DHCP4_E_DOWN; - else if (errno == EAGAIN) - return N_DHCP4_E_AGAIN; - else - return -errno; - } else if (len == 0 || (size_t)len > n_buf) { - return N_DHCP4_E_MALFORMED; - } - - r = n_dhcp4_incoming_new(&message, buf, len); - if (r) - return r; - - if (pktinfo) { - struct cmsghdr *cmsg; - - cmsg = CMSG_FIRSTHDR(&msg); - c_assert(cmsg); - c_assert(cmsg->cmsg_level == IPPROTO_IP); - c_assert(cmsg->cmsg_type == IP_PKTINFO); - c_assert(cmsg->cmsg_len == CMSG_LEN(sizeof(struct in_pktinfo))); - - memcpy(pktinfo, (void*)CMSG_DATA(cmsg), sizeof(struct in_pktinfo)); - } - - *messagep = message; - message = NULL; - return 0; -} - -int n_dhcp4_c_socket_udp_recv(int sockfd, - uint8_t *buf, - size_t n_buf, - NDhcp4Incoming **messagep) { - return n_dhcp4_socket_udp_recv(sockfd, buf, n_buf, messagep, NULL); -} - -int n_dhcp4_s_socket_udp_recv(int sockfd, - uint8_t *buf, - size_t n_buf, - NDhcp4Incoming **messagep, - struct sockaddr_in *dest) { - struct in_pktinfo pktinfo = {}; - int r; - - r = n_dhcp4_socket_udp_recv(sockfd, buf, n_buf, messagep, &pktinfo); - if (r) - return r; - - if (dest) { - dest->sin_family = AF_INET; - dest->sin_port = htons(N_DHCP4_NETWORK_SERVER_PORT); - dest->sin_addr.s_addr = pktinfo.ipi_addr.s_addr; - } - - return 0; -} diff --git a/shared/n-dhcp4/src/n-dhcp4.h b/shared/n-dhcp4/src/n-dhcp4.h deleted file mode 100644 index 435c5600..00000000 --- a/shared/n-dhcp4/src/n-dhcp4.h +++ /dev/null @@ -1,300 +0,0 @@ -#pragma once - -/* - * Dynamic Host Configuration Protocol for IPv4 - * - * This is the public header of the n-dhcp4 library, implementing IPv4 Dynamic - * Host Configuration Protocol as described in RFC-2132. This header defines - * the public API and all entry points of n-dhcp4. - */ - -#ifdef __cplusplus -extern "C" { -#endif - -#include <inttypes.h> -#include <netinet/in.h> -#include <stdbool.h> -#include <stdlib.h> - -typedef struct NDhcp4Client NDhcp4Client; -typedef struct NDhcp4ClientConfig NDhcp4ClientConfig; -typedef struct NDhcp4ClientEvent NDhcp4ClientEvent; -typedef struct NDhcp4ClientLease NDhcp4ClientLease; -typedef struct NDhcp4ClientProbe NDhcp4ClientProbe; -typedef struct NDhcp4ClientProbeConfig NDhcp4ClientProbeConfig; -typedef struct NDhcp4Server NDhcp4Server; -typedef struct NDhcp4ServerConfig NDhcp4ServerConfig; -typedef struct NDhcp4ServerEvent NDhcp4ServerEvent; -typedef struct NDhcp4ServerIp NDhcp4ServerIp; -typedef struct NDhcp4ServerLease NDhcp4ServerLease; - -#define N_DHCP4_CLIENT_START_DELAY_RFC2131 (UINT64_C(9000)) - -enum { - _N_DHCP4_E_SUCCESS, - - N_DHCP4_E_PREEMPTED, - N_DHCP4_E_INTERNAL, - - N_DHCP4_E_INVALID_IFINDEX, - N_DHCP4_E_INVALID_TRANSPORT, - N_DHCP4_E_INVALID_ADDRESS, - N_DHCP4_E_INVALID_CLIENT_ID, - N_DHCP4_E_DUPLICATE_OPTION, - N_DHCP4_E_UNSET, - - _N_DHCP4_E_N, -}; - -enum { - N_DHCP4_TRANSPORT_ETHERNET, - N_DHCP4_TRANSPORT_INFINIBAND, - _N_DHCP4_TRANSPORT_N, -}; - -enum { - N_DHCP4_CLIENT_EVENT_DOWN, - N_DHCP4_CLIENT_EVENT_OFFER, - N_DHCP4_CLIENT_EVENT_GRANTED, - N_DHCP4_CLIENT_EVENT_RETRACTED, - N_DHCP4_CLIENT_EVENT_EXTENDED, - N_DHCP4_CLIENT_EVENT_EXPIRED, - N_DHCP4_CLIENT_EVENT_CANCELLED, - N_DHCP4_CLIENT_EVENT_LOG, - _N_DHCP4_CLIENT_EVENT_N, -}; - -enum { - N_DHCP4_SERVER_EVENT_DOWN, - N_DHCP4_SERVER_EVENT_DISCOVER, - N_DHCP4_SERVER_EVENT_REQUEST, - N_DHCP4_SERVER_EVENT_RENEW, - N_DHCP4_SERVER_EVENT_DECLINE, - N_DHCP4_SERVER_EVENT_RELEASE, - _N_DHCP4_SERVER_EVENT_N, -}; - -struct NDhcp4ClientEvent { - unsigned int event; - union { - struct { - } down; - struct { - NDhcp4ClientProbe *probe; - NDhcp4ClientLease *lease; - } offer, granted, extended; - struct { - NDhcp4ClientProbe *probe; - } retracted, expired, cancelled; - struct { - /* If allow_steal_message is true, then the user may steal the message when handling - * the event. In that case, set the message field to %NULL and free it yourself - * with free(). */ - const char *message; - int level; - bool allow_steal_message; - } log; - }; -}; - -struct NDhcp4ServerEvent { - unsigned int event; - union { - struct { - } down; - struct { - NDhcp4ServerLease *lease; - } discover, request, decline, release; - }; -}; - -/* client configs */ - -int n_dhcp4_client_config_new(NDhcp4ClientConfig **configp); -NDhcp4ClientConfig *n_dhcp4_client_config_free(NDhcp4ClientConfig *config); - -void n_dhcp4_client_config_set_ifindex(NDhcp4ClientConfig *config, int ifindex); -void n_dhcp4_client_config_set_transport(NDhcp4ClientConfig *config, unsigned int transport); -void n_dhcp4_client_config_set_request_broadcast(NDhcp4ClientConfig *config, bool request_broadcast); -void n_dhcp4_client_config_set_mac(NDhcp4ClientConfig *config, const uint8_t *mac, size_t n_mac); -void n_dhcp4_client_config_set_broadcast_mac(NDhcp4ClientConfig *config, const uint8_t *mac, size_t n_mac); -int n_dhcp4_client_config_set_client_id(NDhcp4ClientConfig *config, const uint8_t *id, size_t n_id); - -/* client-probe configs */ - -int n_dhcp4_client_probe_config_new(NDhcp4ClientProbeConfig **configp); -NDhcp4ClientProbeConfig *n_dhcp4_client_probe_config_free(NDhcp4ClientProbeConfig *config); - -void n_dhcp4_client_probe_config_set_inform_only(NDhcp4ClientProbeConfig *config, bool inform_only); -void n_dhcp4_client_probe_config_set_init_reboot(NDhcp4ClientProbeConfig *config, bool init_reboot); -void n_dhcp4_client_probe_config_set_requested_ip(NDhcp4ClientProbeConfig *config, struct in_addr ip); -void n_dhcp4_client_probe_config_set_start_delay(NDhcp4ClientProbeConfig *config, uint64_t msecs); -void n_dhcp4_client_probe_config_request_option(NDhcp4ClientProbeConfig *config, uint8_t option); -int n_dhcp4_client_probe_config_append_option(NDhcp4ClientProbeConfig *config, - uint8_t option, - const void *data, - uint8_t n_data); - -/* clients */ - -int n_dhcp4_client_new(NDhcp4Client **clientp, NDhcp4ClientConfig *config); -NDhcp4Client *n_dhcp4_client_ref(NDhcp4Client *client); -NDhcp4Client *n_dhcp4_client_unref(NDhcp4Client *client); - -void n_dhcp4_client_get_fd(NDhcp4Client *client, int *fdp); -int n_dhcp4_client_dispatch(NDhcp4Client *client); -int n_dhcp4_client_pop_event(NDhcp4Client *client, NDhcp4ClientEvent **eventp); - -void n_dhcp4_client_set_log_level(NDhcp4Client *client, int level); - -int n_dhcp4_client_update_mtu(NDhcp4Client *client, uint16_t mtu); - -int n_dhcp4_client_probe(NDhcp4Client *client, - NDhcp4ClientProbe **probep, - NDhcp4ClientProbeConfig *config); - -/* client probes */ - -NDhcp4ClientProbe *n_dhcp4_client_probe_free(NDhcp4ClientProbe *probe); - -void n_dhcp4_client_probe_set_userdata(NDhcp4ClientProbe *probe, void *userdata); -void n_dhcp4_client_probe_get_userdata(NDhcp4ClientProbe *probe, void **userdatap); - -/* client leases */ - -NDhcp4ClientLease *n_dhcp4_client_lease_ref(NDhcp4ClientLease *lease); -NDhcp4ClientLease *n_dhcp4_client_lease_unref(NDhcp4ClientLease *lease); - -void n_dhcp4_client_lease_get_yiaddr(NDhcp4ClientLease *lease, struct in_addr *yiaddr); -void n_dhcp4_client_lease_get_siaddr(NDhcp4ClientLease *lease, struct in_addr *siaddr); -void n_dhcp4_client_lease_get_basetime(NDhcp4ClientLease *lease, uint64_t *ns_basetimep); -void n_dhcp4_client_lease_get_lifetime(NDhcp4ClientLease *lease, uint64_t *ns_lifetimep); -int n_dhcp4_client_lease_query(NDhcp4ClientLease *lease, uint8_t option, uint8_t **datap, size_t *n_datap); -int n_dhcp4_client_lease_get_server_identifier (NDhcp4ClientLease *lease, struct in_addr *addr); - -int n_dhcp4_client_lease_select(NDhcp4ClientLease *lease); -int n_dhcp4_client_lease_accept(NDhcp4ClientLease *lease); -int n_dhcp4_client_lease_decline(NDhcp4ClientLease *lease, const char *error); - -/* server configs */ - -int n_dhcp4_server_config_new(NDhcp4ServerConfig **configp); -NDhcp4ServerConfig *n_dhcp4_server_config_free(NDhcp4ServerConfig *config); - -void n_dhcp4_server_config_set_ifindex(NDhcp4ServerConfig *config, int ifindex); - -/* servers */ - -int n_dhcp4_server_new(NDhcp4Server **serverp, NDhcp4ServerConfig *config); -NDhcp4Server *n_dhcp4_server_ref(NDhcp4Server *server); -NDhcp4Server *n_dhcp4_server_unref(NDhcp4Server *server); - -void n_dhcp4_server_get_fd(NDhcp4Server *server, int *fdp); -int n_dhcp4_server_dispatch(NDhcp4Server *server); -int n_dhcp4_server_pop_event(NDhcp4Server *server, NDhcp4ServerEvent **eventp); - -int n_dhcp4_server_add_ip(NDhcp4Server *server, NDhcp4ServerIp **ipp, struct in_addr ip); - -/* server ip addresses */ - -NDhcp4ServerIp *n_dhcp4_server_ip_free(NDhcp4ServerIp *ip); - -/* server leases */ - -NDhcp4ServerLease *n_dhcp4_server_lease_ref(NDhcp4ServerLease *lease); -NDhcp4ServerLease *n_dhcp4_server_lease_unref(NDhcp4ServerLease *lease); - -int n_dhcp4_server_lease_query(NDhcp4ServerLease *lease, uint8_t option, uint8_t **datap, size_t *n_datap); -int n_dhcp4_server_lease_append(NDhcp4ServerLease *lease, uint8_t option, uint8_t *data, size_t n_data); - -int n_dhcp4_server_lease_offer(NDhcp4ServerLease *lease); -int n_dhcp4_server_lease_ack(NDhcp4ServerLease *lease); -int n_dhcp4_server_lease_nack(NDhcp4ServerLease *lease); - -/* inline helpers */ - -static inline void n_dhcp4_client_config_freep(NDhcp4ClientConfig **p) { - if (*p) - n_dhcp4_client_config_free(*p); -} - -static inline void n_dhcp4_client_config_freev(NDhcp4ClientConfig *p) { - n_dhcp4_client_config_free(p); -} - -static inline void n_dhcp4_client_probe_config_freep(NDhcp4ClientProbeConfig **p) { - if (*p) - n_dhcp4_client_probe_config_free(*p); -} - -static inline void n_dhcp4_client_probe_config_freev(NDhcp4ClientProbeConfig *p) { - n_dhcp4_client_probe_config_free(p); -} - -static inline void n_dhcp4_client_unrefp(NDhcp4Client **p) { - if (*p) - n_dhcp4_client_unref(*p); -} - -static inline void n_dhcp4_client_unrefv(NDhcp4Client *p) { - n_dhcp4_client_unref(p); -} - -static inline void n_dhcp4_client_probe_freep(NDhcp4ClientProbe **p) { - if (*p) - n_dhcp4_client_probe_free(*p); -} - -static inline void n_dhcp4_client_probe_freev(NDhcp4ClientProbe *p) { - n_dhcp4_client_probe_free(p); -} - -static inline void n_dhcp4_client_lease_unrefp(NDhcp4ClientLease **p) { - if (*p) - n_dhcp4_client_lease_unref(*p); -} - -static inline void n_dhcp4_client_lease_unrefv(NDhcp4ClientLease *p) { - n_dhcp4_client_lease_unref(p); -} - -static inline void n_dhcp4_server_config_freep(NDhcp4ServerConfig **p) { - if (*p) - n_dhcp4_server_config_free(*p); -} - -static inline void n_dhcp4_server_config_freev(NDhcp4ServerConfig *p) { - n_dhcp4_server_config_free(p); -} - -static inline void n_dhcp4_server_unrefp(NDhcp4Server **p) { - if (*p) - n_dhcp4_server_unref(*p); -} - -static inline void n_dhcp4_server_unrefv(NDhcp4Server *p) { - n_dhcp4_server_unref(p); -} - -static inline void n_dhcp4_server_ip_freep(NDhcp4ServerIp **p) { - if (*p) - n_dhcp4_server_ip_free(*p); -} - -static inline void n_dhcp4_server_ip_freev(NDhcp4ServerIp *p) { - n_dhcp4_server_ip_free(p); -} - -static inline void n_dhcp4_server_lease_unrefp(NDhcp4ServerLease **p) { - if (*p) - n_dhcp4_server_lease_unref(*p); -} - -static inline void n_dhcp4_server_lease_unrefv(NDhcp4ServerLease *p) { - n_dhcp4_server_lease_unref(p); -} - -#ifdef __cplusplus -} -#endif diff --git a/shared/n-dhcp4/src/util/packet.c b/shared/n-dhcp4/src/util/packet.c deleted file mode 100644 index 48f2c85e..00000000 --- a/shared/n-dhcp4/src/util/packet.c +++ /dev/null @@ -1,448 +0,0 @@ -/* - * Packet Sockets - */ - -#include <assert.h> -#include <c-stdaux.h> -#include <endian.h> -#include <errno.h> -#include <linux/filter.h> -#include <linux/if_ether.h> -#include <linux/if_packet.h> -#include <linux/udp.h> -#include <netinet/in.h> -#include <netinet/ip.h> -#include <stdbool.h> -#include <stdlib.h> -#include <string.h> -#include <sys/types.h> -#include <sys/socket.h> -#include "packet.h" - -/** - * packet_internet_checksum() - compute the internet checksum - * @data: the data to checksum - * @size: the length of @data in bytes - * - * Computes the internet checksum for a given blob according to RFC1071. - * - * The internet checksum is the one's complement of the one's complement sum of - * the 16-bit words of the data, padded with zero-bytes if the data does not - * end on a 16-bit boundary. - * - * Return: Checksum is returned. - */ -uint16_t packet_internet_checksum(const uint8_t *data, size_t size) { - uint64_t acc = 0; - uint32_t local; - - while (size >= sizeof(local)) { - memcpy(&local, data, sizeof(local)); - acc += local; - - data += sizeof(local); - size -= sizeof(local); - } - - if (size) { - local = 0; - memcpy(&local, data, size); - acc += local; - } - - while (acc >> 16) - acc = (acc & 0xffff) + (acc >> 16); - - return ~acc; -} - -/** - * packet_internet_checksum_udp() - compute the internet checkum for UDP packets - * @src_addr: source IP address - * @dst_addr: destination IP address - * @src_port: source port - * @dst_port: destination port - * @data: payload - * @size: length of payload in bytes - * @checksum: current checksum, or 0 - * - * Computes the internet checksum for a UDP packet, given the relevant IP and - * UDP header fields. - * - * Note that since a UDP packet contains the checksum itself, the resulting - * checksum will always be 0 (this fact is used to verify that a UDP packet is - * valid). - * Inversely, when calculating the checksum for outgoing packets, you have to - * specify 0 as @checksum, and this function will return the checksum for the - * caller to use for the packet. In this case, though, the caller must check - * whether the returned checksum might coincidentally be 0, in which case it - * must be flipped to -1 (0xffff), since 0 is not allowed as checksum in UDP - * packets, and -1 is arithmetically equivalent in the checksum calculation. - * - * Return: Checksum is returned. - */ -uint16_t packet_internet_checksum_udp(const struct in_addr *src_addr, - const struct in_addr *dst_addr, - uint16_t src_port, - uint16_t dst_port, - const uint8_t *data, - size_t size, - uint16_t checksum) { - struct { - uint32_t src; - uint32_t dst; - uint8_t _zeros; - uint8_t protocol; - uint16_t length; - struct udphdr udp; - } _c_packed_ udp_phdr = { - .src = src_addr->s_addr, - .dst = dst_addr->s_addr, - .protocol = IPPROTO_UDP, - .length = htons(sizeof(struct udphdr) + size), - .udp = { - .source = htons(src_port), - .dest = htons(dst_port), - .len = htons(sizeof(struct udphdr) + size), - .check = checksum, - }, - }; - const uint8_t *iter; - uint64_t acc = 0; - uint32_t local; - - _Static_assert(!(sizeof(udp_phdr) % sizeof(local)), - "UDP header structure size is not a multiple of 4"); - - for (iter = (const uint8_t *)&udp_phdr; - iter < (const uint8_t *)(&udp_phdr + 1); - iter += sizeof(local)) { - memcpy(&local, iter, sizeof(local)); - acc += local; - } - - while (size >= sizeof(local)) { - memcpy(&local, data, sizeof(local)); - acc += local; - - data += sizeof(local); - size -= sizeof(local); - } - - if (size) { - local = 0; - memcpy(&local, data, size); - acc += local; - } - - while (acc >> 16) - acc = (acc & 0xffff) + (acc >> 16); - - return ~acc; -} - -/** - * packet_sendto_udp() - send UDP packet on AF_PACKET socket - * @sockfd: AF_PACKET/SOCK_DGRAM socket - * @buf: payload - * @n_buf: length of payload in bytes - * @n_transmittedp: output argument for number of transmitted bytes - * @src_paddr: source protocol address, see ip(7) - * @dest_haddr: destination hardware address, see packet(7) - * @dest_paddr: destination protocol address, see ip(7) - * - * Sends an UDP packet on a AF_PACKET socket directly to a hardware - * address. The difference between this and sendto() on an AF_INET - * socket is that no routing is performed, so the packet is delivered - * even if the destination IP is not yet configured on the destination - * host. - * - * Return: 0 on success, negative error code on failure. - */ -int packet_sendto_udp(int sockfd, - const void *buf, - size_t n_buf, - size_t *n_transmittedp, - const struct sockaddr_in *src_paddr, - const struct packet_sockaddr_ll *dest_haddr, - const struct sockaddr_in *dest_paddr) { - struct iphdr ip_hdr = { - .version = IPVERSION, - .ihl = sizeof(ip_hdr) / 4, /* Length of header in multiples of four bytes */ - .tos = IPTOS_CLASS_CS6, /* Class Selector for network control */ - .tot_len = htons(sizeof(struct iphdr) + sizeof(struct udphdr) + n_buf), - .frag_off = htons(IP_DF), /* Do not fragment */ - .ttl = IPDEFTTL, - .protocol = IPPROTO_UDP, - .saddr = src_paddr->sin_addr.s_addr, - .daddr = dest_paddr->sin_addr.s_addr, - }; - struct udphdr udp_hdr = { - .source = src_paddr->sin_port, - .dest = dest_paddr->sin_port, - .len = htons(sizeof(udp_hdr) + n_buf), - }; - struct iovec iov[3] = { - { - .iov_base = &ip_hdr, - .iov_len = sizeof(ip_hdr), - }, - { - .iov_base = &udp_hdr, - .iov_len = sizeof(udp_hdr), - }, - { - .iov_base = (void *)buf, - .iov_len = n_buf, - }, - }; - struct msghdr msg = { - .msg_name = (void*)dest_haddr, - .msg_namelen = sizeof(*dest_haddr), - .msg_iov = iov, - .msg_iovlen = sizeof(iov) / sizeof(iov[0]), - }; - ssize_t pktlen; - - ip_hdr.check = packet_internet_checksum((void*)&ip_hdr, sizeof(ip_hdr)); - udp_hdr.check = packet_internet_checksum_udp(&src_paddr->sin_addr, - &dest_paddr->sin_addr, - ntohs(src_paddr->sin_port), - ntohs(dest_paddr->sin_port), - buf, - n_buf, - 0); - - /* - * 0x0000 and 0xffff are equivalent for computing the UDP checksum, - * but 0x0000 is reserved in UDP headers, to mean that the checksum is - * not set and should be ignored by the receiver. Hence, flip it to - * 0xffff in that case. - */ - udp_hdr.check = udp_hdr.check ?: 0xffff; - - pktlen = sendmsg(sockfd, &msg, 0); - if (pktlen < 0) - return -c_errno(); - - /* - * Kernel never truncates. Worst case, we get -EMSGSIZE. Kernel *might* - * prepend VNET headers, in which case a bigger length than sent is - * returned. - * Lets assert on this, and then return to the caller the proportion of - * its own buffer that we sent (which is always exactly the requested - * size). - */ - c_assert((size_t)pktlen >= sizeof(ip_hdr) + sizeof(udp_hdr) + n_buf); - *n_transmittedp = n_buf; - return 0; -} - -/** - * packet_recvfrom_upd() - receive UDP packet from AF_PACKET socket - * @sockfd: AF_PACKET/SOCK_DGRAM socket - * @buf: buffor for payload - * @n_buf: max length of payload in bytes - * @n_transmittedp: output argument for number transmitted bytes - * @src: return argument for source address, or NULL, see ip(7) - * - * Receives an UDP packet on a AF_PACKET socket. The difference between - * this and recvfrom() on an AF_INET socket is that the packet will be - * received even if the destination IP address has not been configured - * on the interface. - * - * Return: 0 on success, negative error code on failure. - */ -int packet_recvfrom_udp(int sockfd, - void *buf, - size_t n_buf, - size_t *n_transmittedp, - struct sockaddr_in *src) { - union { - struct iphdr hdr; - /* - * Maximum IP-header length is 15 * 4, since it is specified in - * the `ihl` field, which is four bits and interpreted as - * factor of 4. So maximum `ihl` value is `(2^4 - 1) * 4`. - */ - uint8_t data[15 * 4]; - } ip_hdr; - struct udphdr udp_hdr; - struct iovec iov[3] = { - { - .iov_base = &ip_hdr, - }, - { - .iov_base = &udp_hdr, - .iov_len = sizeof(udp_hdr), - }, - { - .iov_base = buf, - .iov_len = n_buf, - }, - }; - uint8_t cmsgbuf[CMSG_LEN(sizeof(struct tpacket_auxdata))]; - struct msghdr msg = { - .msg_iov = iov, - .msg_iovlen = sizeof(iov) / sizeof(iov[0]), - .msg_control = cmsgbuf, - .msg_controllen = sizeof(cmsgbuf), - }; - struct cmsghdr *cmsg; - bool checksum = true; - ssize_t pktlen; - size_t hdrlen; - - *n_transmittedp = 0; - - /* Peek packet to obtain the real IP header length */ - pktlen = recv(sockfd, &ip_hdr.hdr, sizeof(ip_hdr.hdr), MSG_PEEK); - if (pktlen < 0) - return -errno; - - if ((size_t)pktlen < sizeof(ip_hdr.hdr)) { - /* - * Received packet is smaller than the minimal IP header length, - * discard it. - */ - recv(sockfd, NULL, 0, 0); - return 0; - } - - if (ip_hdr.hdr.version != IPVERSION) { - /* - * This is not an IPv4 packet, discard it. - */ - recv(sockfd, NULL, 0, 0); - return 0; - } - - hdrlen = ip_hdr.hdr.ihl * 4; - if (hdrlen < sizeof(ip_hdr.hdr)) { - /* - * The length given in the header is smaller than the minimum - * header length, discard the packet. - */ - recv(sockfd, NULL, 0, 0); - return 0; - } - - /* - * Now that we know the ip-header length, we can prepare the iovec to - * read the entire packet into the correct buffers. - */ - iov[0].iov_len = hdrlen; - pktlen = recvmsg(sockfd, &msg, 0); - if (pktlen < 0) - return -errno; - - cmsg = CMSG_FIRSTHDR(&msg); - if (cmsg) { - if (cmsg->cmsg_level == SOL_PACKET && - cmsg->cmsg_type == PACKET_AUXDATA && - cmsg->cmsg_len == CMSG_LEN(sizeof(struct tpacket_auxdata))) { - struct tpacket_auxdata *aux = (void *)CMSG_DATA(cmsg); - checksum = !(aux->tp_status & TP_STATUS_CSUMNOTREADY); - } - } - - if (ntohs(ip_hdr.hdr.tot_len) > pktlen) { - /* - * The IP-packet is bigger than the chunk returned by the - * kernel. So either the packet is corrupt, or our caller - * provided too small a buffer. In both cases, we simply drop - * the packet. - */ - return 0; - } - - /* Truncate trailing garbage. */ - pktlen = ntohs(ip_hdr.hdr.tot_len); - - if ((size_t)pktlen < hdrlen + sizeof(udp_hdr)) { - /* - * The packet is too small to even contain an entire UDP - * header, so discard it entirely. - */ - return 0; - } else if ((size_t)pktlen < hdrlen + ntohs(udp_hdr.len)) { - /* - * The UDP header specified a longer length than the returned - * packet, so discard it entirely. - */ - return 0; - } - - /* - * Make @pktlen the length of the packet payload, without IP/UDP - * headers, since that is what the caller is interested in. - */ - pktlen = ntohs(udp_hdr.len) - sizeof(struct udphdr); - - /* IP */ - - if (ip_hdr.hdr.protocol != IPPROTO_UDP) { - return 0; /* not a UDP packet, discard it */ - } else if (ip_hdr.hdr.frag_off & htons(IP_MF | IP_OFFMASK)) { - return 0; /* fragmented packet, discard it */ - } else if (checksum && packet_internet_checksum(ip_hdr.data, hdrlen)) { - return 0; /* invalid checksum, discard it */ - } - - /* UDP */ - - if (checksum && udp_hdr.check) { - /* - * Computing the checksum of a packet that has the checksum set - * must yield 0. If it does not yield 0, the packet is invalid, - * in which case we discard it. - */ - if (packet_internet_checksum_udp(&(struct in_addr){ ip_hdr.hdr.saddr }, - &(struct in_addr){ ip_hdr.hdr.daddr }, - ntohs(udp_hdr.source), - ntohs(udp_hdr.dest), - buf, - pktlen, - udp_hdr.check)) { - return 0; - } - } - - if (src) { - src->sin_family = AF_INET; - src->sin_addr.s_addr = ip_hdr.hdr.saddr; - src->sin_port = udp_hdr.source; - } - - /* Return length of UDP payload (i.e., data written to @buf). */ - *n_transmittedp = pktlen; - return 0; -} - -/** - * packet_shutdown() - shutdown socket for future receive operations - * @sockfd: socket - * - * Partially emulates `shutdown(sockfd, SHUT_RD)`, in the sense that no - * further packets may be queued on the socket. All packets that are - * already queued will still be delivered, but once -EAGAIN is returned - * we are guaranteed never to be able to read more packets in the future. - * - * Return: 0 on success, or a negative error code on failure. - */ -int packet_shutdown(int sockfd) { - struct sock_filter filter[] = { - BPF_STMT(BPF_RET + BPF_K, 0), /* discard all packets */ - }; - struct sock_fprog fprog = { - .filter = filter, - .len = sizeof(filter) / sizeof(filter[0]), - }; - int r; - - r = setsockopt(sockfd, SOL_SOCKET, SO_ATTACH_FILTER, &fprog, sizeof(fprog)); - if (r < 0) - return -errno; - - return 0; -} diff --git a/shared/n-dhcp4/src/util/packet.h b/shared/n-dhcp4/src/util/packet.h deleted file mode 100644 index 98dabf7f..00000000 --- a/shared/n-dhcp4/src/util/packet.h +++ /dev/null @@ -1,61 +0,0 @@ -#pragma once - -/* - * Packet Sockets - */ - -#include <c-stdaux.h> -#include <inttypes.h> -#include <linux/if_packet.h> -#include <netinet/in.h> -#include <stdlib.h> -#include <unistd.h> - -/* - * `struct sockaddr_ll` is too small to fit the Infiniband hardware address. - * Introduce `struct packet_sockaddr_ll` which is the same as the original, - * except the `sl_addr` field is extended to fit all the supported hardware - * addresses. - */ -struct packet_sockaddr_ll { - unsigned short sll_family; - __be16 sll_protocol; - int sll_ifindex; - unsigned short sll_hatype; - unsigned char sll_pkttype; - unsigned char sll_halen; - unsigned char sll_addr[32]; /* MAX_ADDR_LEN */ -}; - -uint16_t packet_internet_checksum(const uint8_t *data, size_t len); -uint16_t packet_internet_checksum_udp(const struct in_addr *src_addr, - const struct in_addr *dst_addr, - uint16_t src_port, - uint16_t dst_port, - const uint8_t *data, - size_t size, - uint16_t checksum); - -int packet_sendto_udp(int sockfd, - const void *buf, - size_t n_buf, - size_t *n_transmittedp, - const struct sockaddr_in *src_paddr, - const struct packet_sockaddr_ll *dest_haddr, - const struct sockaddr_in *dest_paddr); -int packet_recvfrom_udp(int sockfd, - void *buf, - size_t n_buf, - size_t *n_transmittedp, - struct sockaddr_in *src); - -int packet_shutdown(int sockfd); - -/* inline helpers */ - -static inline int packet_recv_udp(int sockfd, - void *buf, - size_t n_buf, - size_t *n_transmittedp) { - return packet_recvfrom_udp(sockfd, buf, n_buf, n_transmittedp, NULL); -} diff --git a/shared/n-dhcp4/src/util/socket.c b/shared/n-dhcp4/src/util/socket.c deleted file mode 100644 index c25f76fd..00000000 --- a/shared/n-dhcp4/src/util/socket.c +++ /dev/null @@ -1,121 +0,0 @@ -/* - * Socket Utilities - */ - -#include <assert.h> -#include <c-stdaux.h> -#include <errno.h> -#include <net/if.h> -#include <stdlib.h> -#include <string.h> -#include <sys/ioctl.h> -#include <sys/socket.h> -#include "socket.h" - -/** - * socket_SIOCGIFNAME() - resolve an ifindex to an ifname - * @socket: socket to operate on - * @ifindex: index of network interface to resolve - * @ifname: buffer to store resolved name - * - * This uses the SIOCGIFNAME ioctl to resolve an ifindex to an ifname. The - * buffer provided in @ifnamep must be at least IFNAMSIZ bytes in size. The - * maximum ifname length is IFNAMSIZ-1, and this function always - * zero-terminates the result. - * - * This function is similar to if_indextoname(3) provided by glibc, but it - * allows to specify the target socket explicitly. This allows the caller to - * control the target network-namespace, rather than relying on the network - * namespace of the running process. - * - * Return: 0 on success, negative kernel error code on failure. - */ -int socket_SIOCGIFNAME(int socket, int ifindex, char (*ifnamep)[IFNAMSIZ]) { - struct ifreq req = { .ifr_ifindex = ifindex }; - int r; - - r = ioctl(socket, SIOCGIFNAME, &req); - if (r < 0) - return -errno; - - /* - * The linux kernel guarantees that an interface name is always - * zero-terminated, and it always fully fits into IFNAMSIZ bytes, - * including the zero-terminator. - */ - memcpy(ifnamep, req.ifr_name, IFNAMSIZ); - return 0; -} - -/** - * socket_bind_if() - bind socket to a network interface - * @socket: socket to operate on - * @ifindex: index of network interface to bind to, or 0 - * - * This binds the socket given via @socket to the network interface specified - * via @ifindex. It uses the underlying SO_BINDTODEVICE ioctl of the linux - * kernel. However, if available, if prefers the newer SO_BINDTOIFINDEX ioctl, - * which avoids resolving the interface name temporarily, and thus does not - * suffer from a race-condition. - * - * Return: 0 on success, negative error code on failure. - */ -int socket_bind_if(int socket, int ifindex) { - char ifname[IFNAMSIZ] = {}; - int r; - - c_assert(ifindex >= 0); - - /* - * We first try the newer SO_BINDTOIFINDEX. If it is not available on - * the running kernel, we fall back to SO_BINDTODEVICE. This, however, - * requires us to first resolve the ifindex to an ifname. Note that - * this is racy, since the device name might theoretically change - * asynchronously. - * - * Using 0 as ifindex will remove the device-binding. For - * SO_BINDTOIFINDEX we simply pass-through the 0 to the kernel, which - * recognizes this correctly. For SO_BINDTODEVICE we pass the empty - * string, which the kernel recognizes as a request to remove the - * binding. - * - * The commit introducing SO_BINDTOIFINDEX first appeared in linux-5.1: - * - * commit f5dd3d0c9638a9d9a02b5964c4ad636f06cf7e2c - * Author: David Herrmann <dh.herrmann@gmail.com> - * Date: Tue Jan 15 14:42:14 2019 +0100 - * - * net: introduce SO_BINDTOIFINDEX sockopt - * - * In older kernels, setsockopt(2) is guaranteed to return ENOPROTOOPT - * for this ioctl. - */ - -#ifdef SO_BINDTOIFINDEX - r = setsockopt(socket, - SOL_SOCKET, - SO_BINDTOIFINDEX, - &ifindex, - sizeof(ifindex)); - if (r >= 0) - return 0; - else if (errno != ENOPROTOOPT) - return -errno; -#endif /* SO_BINDTOIFINDEX */ - - if (ifindex > 0) { - r = socket_SIOCGIFNAME(socket, ifindex, &ifname); - if (r) - return r; - } - - r = setsockopt(socket, - SOL_SOCKET, - SO_BINDTODEVICE, - ifname, - strlen(ifname)); - if (r < 0) - return -errno; - - return 0; -} diff --git a/shared/n-dhcp4/src/util/socket.h b/shared/n-dhcp4/src/util/socket.h deleted file mode 100644 index b5ecf2c2..00000000 --- a/shared/n-dhcp4/src/util/socket.h +++ /dev/null @@ -1,11 +0,0 @@ -#pragma once - -/* - * Socket Utilities - */ - -#include <c-stdaux.h> -#include <stdlib.h> - -int socket_SIOCGIFNAME(int socket, int ifindex, char (*ifnamep)[IFNAMSIZ]); -int socket_bind_if(int socket, int ifindex); diff --git a/shared/nm-base/nm-base.h b/shared/nm-base/nm-base.h deleted file mode 100644 index 105d1783..00000000 --- a/shared/nm-base/nm-base.h +++ /dev/null @@ -1,210 +0,0 @@ -/* SPDX-License-Identifier: LGPL-2.1-or-later */ -/* - * Copyright (C) 2018 Red Hat, Inc. - */ - -#ifndef __NM_LIBNM_BASE_H__ -#define __NM_LIBNM_BASE_H__ - -/*****************************************************************************/ - -/* this must be the same as NM_UTILS_HWADDR_LEN_MAX from libnm. */ -#define _NM_UTILS_HWADDR_LEN_MAX 20 - -/*****************************************************************************/ - -typedef enum { - NM_ETHTOOL_ID_UNKNOWN = -1, - - _NM_ETHTOOL_ID_FIRST = 0, - - _NM_ETHTOOL_ID_COALESCE_FIRST = _NM_ETHTOOL_ID_FIRST, - NM_ETHTOOL_ID_COALESCE_ADAPTIVE_RX = _NM_ETHTOOL_ID_COALESCE_FIRST, - NM_ETHTOOL_ID_COALESCE_ADAPTIVE_TX, - NM_ETHTOOL_ID_COALESCE_PKT_RATE_HIGH, - NM_ETHTOOL_ID_COALESCE_PKT_RATE_LOW, - NM_ETHTOOL_ID_COALESCE_RX_FRAMES, - NM_ETHTOOL_ID_COALESCE_RX_FRAMES_HIGH, - NM_ETHTOOL_ID_COALESCE_RX_FRAMES_IRQ, - NM_ETHTOOL_ID_COALESCE_RX_FRAMES_LOW, - NM_ETHTOOL_ID_COALESCE_RX_USECS, - NM_ETHTOOL_ID_COALESCE_RX_USECS_HIGH, - NM_ETHTOOL_ID_COALESCE_RX_USECS_IRQ, - NM_ETHTOOL_ID_COALESCE_RX_USECS_LOW, - NM_ETHTOOL_ID_COALESCE_SAMPLE_INTERVAL, - NM_ETHTOOL_ID_COALESCE_STATS_BLOCK_USECS, - NM_ETHTOOL_ID_COALESCE_TX_FRAMES, - NM_ETHTOOL_ID_COALESCE_TX_FRAMES_HIGH, - NM_ETHTOOL_ID_COALESCE_TX_FRAMES_IRQ, - NM_ETHTOOL_ID_COALESCE_TX_FRAMES_LOW, - NM_ETHTOOL_ID_COALESCE_TX_USECS, - NM_ETHTOOL_ID_COALESCE_TX_USECS_HIGH, - NM_ETHTOOL_ID_COALESCE_TX_USECS_IRQ, - NM_ETHTOOL_ID_COALESCE_TX_USECS_LOW, - _NM_ETHTOOL_ID_COALESCE_LAST = NM_ETHTOOL_ID_COALESCE_TX_USECS_LOW, - - _NM_ETHTOOL_ID_FEATURE_FIRST = _NM_ETHTOOL_ID_COALESCE_LAST + 1, - NM_ETHTOOL_ID_FEATURE_ESP_HW_OFFLOAD = _NM_ETHTOOL_ID_FEATURE_FIRST, - NM_ETHTOOL_ID_FEATURE_ESP_TX_CSUM_HW_OFFLOAD, - NM_ETHTOOL_ID_FEATURE_FCOE_MTU, - NM_ETHTOOL_ID_FEATURE_GRO, - NM_ETHTOOL_ID_FEATURE_GSO, - NM_ETHTOOL_ID_FEATURE_HIGHDMA, - NM_ETHTOOL_ID_FEATURE_HW_TC_OFFLOAD, - NM_ETHTOOL_ID_FEATURE_L2_FWD_OFFLOAD, - NM_ETHTOOL_ID_FEATURE_LOOPBACK, - NM_ETHTOOL_ID_FEATURE_LRO, - NM_ETHTOOL_ID_FEATURE_MACSEC_HW_OFFLOAD, - NM_ETHTOOL_ID_FEATURE_NTUPLE, - NM_ETHTOOL_ID_FEATURE_RX, - NM_ETHTOOL_ID_FEATURE_RXHASH, - NM_ETHTOOL_ID_FEATURE_RXVLAN, - NM_ETHTOOL_ID_FEATURE_RX_ALL, - NM_ETHTOOL_ID_FEATURE_RX_FCS, - NM_ETHTOOL_ID_FEATURE_RX_GRO_HW, - NM_ETHTOOL_ID_FEATURE_RX_GRO_LIST, - NM_ETHTOOL_ID_FEATURE_RX_UDP_GRO_FORWARDING, - NM_ETHTOOL_ID_FEATURE_RX_UDP_TUNNEL_PORT_OFFLOAD, - NM_ETHTOOL_ID_FEATURE_RX_VLAN_FILTER, - NM_ETHTOOL_ID_FEATURE_RX_VLAN_STAG_FILTER, - NM_ETHTOOL_ID_FEATURE_RX_VLAN_STAG_HW_PARSE, - NM_ETHTOOL_ID_FEATURE_SG, - NM_ETHTOOL_ID_FEATURE_TLS_HW_RECORD, - NM_ETHTOOL_ID_FEATURE_TLS_HW_RX_OFFLOAD, - NM_ETHTOOL_ID_FEATURE_TLS_HW_TX_OFFLOAD, - NM_ETHTOOL_ID_FEATURE_TSO, - NM_ETHTOOL_ID_FEATURE_TX, - NM_ETHTOOL_ID_FEATURE_TXVLAN, - NM_ETHTOOL_ID_FEATURE_TX_CHECKSUM_FCOE_CRC, - NM_ETHTOOL_ID_FEATURE_TX_CHECKSUM_IPV4, - NM_ETHTOOL_ID_FEATURE_TX_CHECKSUM_IPV6, - NM_ETHTOOL_ID_FEATURE_TX_CHECKSUM_IP_GENERIC, - NM_ETHTOOL_ID_FEATURE_TX_CHECKSUM_SCTP, - NM_ETHTOOL_ID_FEATURE_TX_ESP_SEGMENTATION, - NM_ETHTOOL_ID_FEATURE_TX_FCOE_SEGMENTATION, - NM_ETHTOOL_ID_FEATURE_TX_GRE_CSUM_SEGMENTATION, - NM_ETHTOOL_ID_FEATURE_TX_GRE_SEGMENTATION, - NM_ETHTOOL_ID_FEATURE_TX_GSO_LIST, - NM_ETHTOOL_ID_FEATURE_TX_GSO_PARTIAL, - NM_ETHTOOL_ID_FEATURE_TX_GSO_ROBUST, - NM_ETHTOOL_ID_FEATURE_TX_IPXIP4_SEGMENTATION, - NM_ETHTOOL_ID_FEATURE_TX_IPXIP6_SEGMENTATION, - NM_ETHTOOL_ID_FEATURE_TX_NOCACHE_COPY, - NM_ETHTOOL_ID_FEATURE_TX_SCATTER_GATHER, - NM_ETHTOOL_ID_FEATURE_TX_SCATTER_GATHER_FRAGLIST, - NM_ETHTOOL_ID_FEATURE_TX_SCTP_SEGMENTATION, - NM_ETHTOOL_ID_FEATURE_TX_TCP6_SEGMENTATION, - NM_ETHTOOL_ID_FEATURE_TX_TCP_ECN_SEGMENTATION, - NM_ETHTOOL_ID_FEATURE_TX_TCP_MANGLEID_SEGMENTATION, - NM_ETHTOOL_ID_FEATURE_TX_TCP_SEGMENTATION, - NM_ETHTOOL_ID_FEATURE_TX_TUNNEL_REMCSUM_SEGMENTATION, - NM_ETHTOOL_ID_FEATURE_TX_UDP_SEGMENTATION, - NM_ETHTOOL_ID_FEATURE_TX_UDP_TNL_CSUM_SEGMENTATION, - NM_ETHTOOL_ID_FEATURE_TX_UDP_TNL_SEGMENTATION, - NM_ETHTOOL_ID_FEATURE_TX_VLAN_STAG_HW_INSERT, - _NM_ETHTOOL_ID_FEATURE_LAST = NM_ETHTOOL_ID_FEATURE_TX_VLAN_STAG_HW_INSERT, - - _NM_ETHTOOL_ID_RING_FIRST = _NM_ETHTOOL_ID_FEATURE_LAST + 1, - NM_ETHTOOL_ID_RING_RX = _NM_ETHTOOL_ID_RING_FIRST, - NM_ETHTOOL_ID_RING_RX_JUMBO, - NM_ETHTOOL_ID_RING_RX_MINI, - NM_ETHTOOL_ID_RING_TX, - _NM_ETHTOOL_ID_RING_LAST = NM_ETHTOOL_ID_RING_TX, - - _NM_ETHTOOL_ID_LAST = _NM_ETHTOOL_ID_RING_LAST, - - _NM_ETHTOOL_ID_COALESCE_NUM = - (_NM_ETHTOOL_ID_COALESCE_LAST - _NM_ETHTOOL_ID_COALESCE_FIRST + 1), - _NM_ETHTOOL_ID_FEATURE_NUM = (_NM_ETHTOOL_ID_FEATURE_LAST - _NM_ETHTOOL_ID_FEATURE_FIRST + 1), - _NM_ETHTOOL_ID_RING_NUM = (_NM_ETHTOOL_ID_RING_LAST - _NM_ETHTOOL_ID_RING_FIRST + 1), - _NM_ETHTOOL_ID_NUM = (_NM_ETHTOOL_ID_LAST - _NM_ETHTOOL_ID_FIRST + 1), -} NMEthtoolID; - -#define _NM_ETHTOOL_ID_FEATURE_AS_IDX(ethtool_id) ((ethtool_id) -_NM_ETHTOOL_ID_FEATURE_FIRST) -#define _NM_ETHTOOL_ID_COALESCE_AS_IDX(ethtool_id) ((ethtool_id) -_NM_ETHTOOL_ID_COALESCE_FIRST) - -typedef enum { - NM_ETHTOOL_TYPE_UNKNOWN, - NM_ETHTOOL_TYPE_COALESCE, - NM_ETHTOOL_TYPE_FEATURE, - NM_ETHTOOL_TYPE_RING, -} NMEthtoolType; - -/****************************************************************************/ - -static inline gboolean -nm_ethtool_id_is_feature(NMEthtoolID id) -{ - return id >= _NM_ETHTOOL_ID_FEATURE_FIRST && id <= _NM_ETHTOOL_ID_FEATURE_LAST; -} - -static inline gboolean -nm_ethtool_id_is_coalesce(NMEthtoolID id) -{ - return id >= _NM_ETHTOOL_ID_COALESCE_FIRST && id <= _NM_ETHTOOL_ID_COALESCE_LAST; -} - -static inline gboolean -nm_ethtool_id_is_ring(NMEthtoolID id) -{ - return id >= _NM_ETHTOOL_ID_RING_FIRST && id <= _NM_ETHTOOL_ID_RING_LAST; -} - -/*****************************************************************************/ - -typedef enum { - _NM_SETTING_WIRED_WAKE_ON_LAN_NONE = 0, - _NM_SETTING_WIRED_WAKE_ON_LAN_PHY = 0x2, - _NM_SETTING_WIRED_WAKE_ON_LAN_UNICAST = 0x4, - _NM_SETTING_WIRED_WAKE_ON_LAN_MULTICAST = 0x8, - _NM_SETTING_WIRED_WAKE_ON_LAN_BROADCAST = 0x10, - _NM_SETTING_WIRED_WAKE_ON_LAN_ARP = 0x20, - _NM_SETTING_WIRED_WAKE_ON_LAN_MAGIC = 0x40, - - _NM_SETTING_WIRED_WAKE_ON_LAN_ALL = 0x7E, - - _NM_SETTING_WIRED_WAKE_ON_LAN_DEFAULT = 0x1, - _NM_SETTING_WIRED_WAKE_ON_LAN_IGNORE = 0x8000, - _NM_SETTING_WIRED_WAKE_ON_LAN_EXCLUSIVE_FLAGS = 0x8001, -} _NMSettingWiredWakeOnLan; - -/*****************************************************************************/ - -typedef enum { - /* In priority order; higher number == higher priority */ - - NM_IP_CONFIG_SOURCE_UNKNOWN = 0, - - /* for routes, the source is mapped to the uint8 field rtm_protocol. - * Reserve the range [1,0x100] for native RTPROT values. */ - - NM_IP_CONFIG_SOURCE_RTPROT_UNSPEC = 1 + 0, - NM_IP_CONFIG_SOURCE_RTPROT_REDIRECT = 1 + 1, - NM_IP_CONFIG_SOURCE_RTPROT_KERNEL = 1 + 2, - NM_IP_CONFIG_SOURCE_RTPROT_BOOT = 1 + 3, - NM_IP_CONFIG_SOURCE_RTPROT_STATIC = 1 + 4, - NM_IP_CONFIG_SOURCE_RTPROT_RA = 1 + 9, - NM_IP_CONFIG_SOURCE_RTPROT_DHCP = 1 + 16, - _NM_IP_CONFIG_SOURCE_RTPROT_LAST = 1 + 0xFF, - - NM_IP_CONFIG_SOURCE_KERNEL, - NM_IP_CONFIG_SOURCE_SHARED, - NM_IP_CONFIG_SOURCE_IP4LL, - NM_IP_CONFIG_SOURCE_IP6LL, - NM_IP_CONFIG_SOURCE_PPP, - NM_IP_CONFIG_SOURCE_WWAN, - NM_IP_CONFIG_SOURCE_VPN, - NM_IP_CONFIG_SOURCE_DHCP, - NM_IP_CONFIG_SOURCE_NDISC, - NM_IP_CONFIG_SOURCE_USER, -} NMIPConfigSource; - -static inline gboolean -NM_IS_IP_CONFIG_SOURCE_RTPROT(NMIPConfigSource source) -{ - return source > NM_IP_CONFIG_SOURCE_UNKNOWN && source <= _NM_IP_CONFIG_SOURCE_RTPROT_LAST; -} - -/****************************************************************************/ - -#endif /* __NM_LIBNM_BASE_H__ */ diff --git a/shared/nm-base/nm-ethtool-base.c b/shared/nm-base/nm-ethtool-base.c deleted file mode 100644 index 52ff2871..00000000 --- a/shared/nm-base/nm-ethtool-base.c +++ /dev/null @@ -1,288 +0,0 @@ -/* SPDX-License-Identifier: LGPL-2.1-or-later */ -/* - * Copyright (C) 2018 Red Hat, Inc. - */ - -#include "nm-glib-aux/nm-default-glib-i18n-lib.h" - -#include "nm-ethtool-base.h" - -#include "nm-ethtool-utils-base.h" - -/*****************************************************************************/ - -#define ETHT_DATA(xname) \ - [NM_ETHTOOL_ID_##xname] = (&((const NMEthtoolData){ \ - .optname = NM_ETHTOOL_OPTNAME_##xname, \ - .id = NM_ETHTOOL_ID_##xname, \ - })) - -const NMEthtoolData *const nm_ethtool_data[_NM_ETHTOOL_ID_NUM + 1] = { - /* indexed by NMEthtoolID */ - ETHT_DATA(COALESCE_ADAPTIVE_RX), - ETHT_DATA(COALESCE_ADAPTIVE_TX), - ETHT_DATA(COALESCE_PKT_RATE_HIGH), - ETHT_DATA(COALESCE_PKT_RATE_LOW), - ETHT_DATA(COALESCE_RX_FRAMES), - ETHT_DATA(COALESCE_RX_FRAMES_HIGH), - ETHT_DATA(COALESCE_RX_FRAMES_IRQ), - ETHT_DATA(COALESCE_RX_FRAMES_LOW), - ETHT_DATA(COALESCE_RX_USECS), - ETHT_DATA(COALESCE_RX_USECS_HIGH), - ETHT_DATA(COALESCE_RX_USECS_IRQ), - ETHT_DATA(COALESCE_RX_USECS_LOW), - ETHT_DATA(COALESCE_SAMPLE_INTERVAL), - ETHT_DATA(COALESCE_STATS_BLOCK_USECS), - ETHT_DATA(COALESCE_TX_FRAMES), - ETHT_DATA(COALESCE_TX_FRAMES_HIGH), - ETHT_DATA(COALESCE_TX_FRAMES_IRQ), - ETHT_DATA(COALESCE_TX_FRAMES_LOW), - ETHT_DATA(COALESCE_TX_USECS), - ETHT_DATA(COALESCE_TX_USECS_HIGH), - ETHT_DATA(COALESCE_TX_USECS_IRQ), - ETHT_DATA(COALESCE_TX_USECS_LOW), - ETHT_DATA(FEATURE_ESP_HW_OFFLOAD), - ETHT_DATA(FEATURE_ESP_TX_CSUM_HW_OFFLOAD), - ETHT_DATA(FEATURE_FCOE_MTU), - ETHT_DATA(FEATURE_GRO), - ETHT_DATA(FEATURE_GSO), - ETHT_DATA(FEATURE_HIGHDMA), - ETHT_DATA(FEATURE_HW_TC_OFFLOAD), - ETHT_DATA(FEATURE_L2_FWD_OFFLOAD), - ETHT_DATA(FEATURE_LOOPBACK), - ETHT_DATA(FEATURE_LRO), - ETHT_DATA(FEATURE_MACSEC_HW_OFFLOAD), - ETHT_DATA(FEATURE_NTUPLE), - ETHT_DATA(FEATURE_RX), - ETHT_DATA(FEATURE_RXHASH), - ETHT_DATA(FEATURE_RXVLAN), - ETHT_DATA(FEATURE_RX_ALL), - ETHT_DATA(FEATURE_RX_FCS), - ETHT_DATA(FEATURE_RX_GRO_HW), - ETHT_DATA(FEATURE_RX_GRO_LIST), - ETHT_DATA(FEATURE_RX_UDP_GRO_FORWARDING), - ETHT_DATA(FEATURE_RX_UDP_TUNNEL_PORT_OFFLOAD), - ETHT_DATA(FEATURE_RX_VLAN_FILTER), - ETHT_DATA(FEATURE_RX_VLAN_STAG_FILTER), - ETHT_DATA(FEATURE_RX_VLAN_STAG_HW_PARSE), - ETHT_DATA(FEATURE_SG), - ETHT_DATA(FEATURE_TLS_HW_RECORD), - ETHT_DATA(FEATURE_TLS_HW_RX_OFFLOAD), - ETHT_DATA(FEATURE_TLS_HW_TX_OFFLOAD), - ETHT_DATA(FEATURE_TSO), - ETHT_DATA(FEATURE_TX), - ETHT_DATA(FEATURE_TXVLAN), - ETHT_DATA(FEATURE_TX_CHECKSUM_FCOE_CRC), - ETHT_DATA(FEATURE_TX_CHECKSUM_IPV4), - ETHT_DATA(FEATURE_TX_CHECKSUM_IPV6), - ETHT_DATA(FEATURE_TX_CHECKSUM_IP_GENERIC), - ETHT_DATA(FEATURE_TX_CHECKSUM_SCTP), - ETHT_DATA(FEATURE_TX_ESP_SEGMENTATION), - ETHT_DATA(FEATURE_TX_FCOE_SEGMENTATION), - ETHT_DATA(FEATURE_TX_GRE_CSUM_SEGMENTATION), - ETHT_DATA(FEATURE_TX_GRE_SEGMENTATION), - ETHT_DATA(FEATURE_TX_GSO_LIST), - ETHT_DATA(FEATURE_TX_GSO_PARTIAL), - ETHT_DATA(FEATURE_TX_GSO_ROBUST), - ETHT_DATA(FEATURE_TX_IPXIP4_SEGMENTATION), - ETHT_DATA(FEATURE_TX_IPXIP6_SEGMENTATION), - ETHT_DATA(FEATURE_TX_NOCACHE_COPY), - ETHT_DATA(FEATURE_TX_SCATTER_GATHER), - ETHT_DATA(FEATURE_TX_SCATTER_GATHER_FRAGLIST), - ETHT_DATA(FEATURE_TX_SCTP_SEGMENTATION), - ETHT_DATA(FEATURE_TX_TCP6_SEGMENTATION), - ETHT_DATA(FEATURE_TX_TCP_ECN_SEGMENTATION), - ETHT_DATA(FEATURE_TX_TCP_MANGLEID_SEGMENTATION), - ETHT_DATA(FEATURE_TX_TCP_SEGMENTATION), - ETHT_DATA(FEATURE_TX_TUNNEL_REMCSUM_SEGMENTATION), - ETHT_DATA(FEATURE_TX_UDP_SEGMENTATION), - ETHT_DATA(FEATURE_TX_UDP_TNL_CSUM_SEGMENTATION), - ETHT_DATA(FEATURE_TX_UDP_TNL_SEGMENTATION), - ETHT_DATA(FEATURE_TX_VLAN_STAG_HW_INSERT), - ETHT_DATA(RING_RX), - ETHT_DATA(RING_RX_JUMBO), - ETHT_DATA(RING_RX_MINI), - ETHT_DATA(RING_TX), - [_NM_ETHTOOL_ID_NUM] = NULL, -}; - -static const guint8 _by_name[_NM_ETHTOOL_ID_NUM] = { - /* sorted by optname. */ - NM_ETHTOOL_ID_COALESCE_ADAPTIVE_RX, - NM_ETHTOOL_ID_COALESCE_ADAPTIVE_TX, - NM_ETHTOOL_ID_COALESCE_PKT_RATE_HIGH, - NM_ETHTOOL_ID_COALESCE_PKT_RATE_LOW, - NM_ETHTOOL_ID_COALESCE_RX_FRAMES, - NM_ETHTOOL_ID_COALESCE_RX_FRAMES_HIGH, - NM_ETHTOOL_ID_COALESCE_RX_FRAMES_IRQ, - NM_ETHTOOL_ID_COALESCE_RX_FRAMES_LOW, - NM_ETHTOOL_ID_COALESCE_RX_USECS, - NM_ETHTOOL_ID_COALESCE_RX_USECS_HIGH, - NM_ETHTOOL_ID_COALESCE_RX_USECS_IRQ, - NM_ETHTOOL_ID_COALESCE_RX_USECS_LOW, - NM_ETHTOOL_ID_COALESCE_SAMPLE_INTERVAL, - NM_ETHTOOL_ID_COALESCE_STATS_BLOCK_USECS, - NM_ETHTOOL_ID_COALESCE_TX_FRAMES, - NM_ETHTOOL_ID_COALESCE_TX_FRAMES_HIGH, - NM_ETHTOOL_ID_COALESCE_TX_FRAMES_IRQ, - NM_ETHTOOL_ID_COALESCE_TX_FRAMES_LOW, - NM_ETHTOOL_ID_COALESCE_TX_USECS, - NM_ETHTOOL_ID_COALESCE_TX_USECS_HIGH, - NM_ETHTOOL_ID_COALESCE_TX_USECS_IRQ, - NM_ETHTOOL_ID_COALESCE_TX_USECS_LOW, - NM_ETHTOOL_ID_FEATURE_ESP_HW_OFFLOAD, - NM_ETHTOOL_ID_FEATURE_ESP_TX_CSUM_HW_OFFLOAD, - NM_ETHTOOL_ID_FEATURE_FCOE_MTU, - NM_ETHTOOL_ID_FEATURE_GRO, - NM_ETHTOOL_ID_FEATURE_GSO, - NM_ETHTOOL_ID_FEATURE_HIGHDMA, - NM_ETHTOOL_ID_FEATURE_HW_TC_OFFLOAD, - NM_ETHTOOL_ID_FEATURE_L2_FWD_OFFLOAD, - NM_ETHTOOL_ID_FEATURE_LOOPBACK, - NM_ETHTOOL_ID_FEATURE_LRO, - NM_ETHTOOL_ID_FEATURE_MACSEC_HW_OFFLOAD, - NM_ETHTOOL_ID_FEATURE_NTUPLE, - NM_ETHTOOL_ID_FEATURE_RX, - NM_ETHTOOL_ID_FEATURE_RX_ALL, - NM_ETHTOOL_ID_FEATURE_RX_FCS, - NM_ETHTOOL_ID_FEATURE_RX_GRO_HW, - NM_ETHTOOL_ID_FEATURE_RX_GRO_LIST, - NM_ETHTOOL_ID_FEATURE_RX_UDP_GRO_FORWARDING, - NM_ETHTOOL_ID_FEATURE_RX_UDP_TUNNEL_PORT_OFFLOAD, - NM_ETHTOOL_ID_FEATURE_RX_VLAN_FILTER, - NM_ETHTOOL_ID_FEATURE_RX_VLAN_STAG_FILTER, - NM_ETHTOOL_ID_FEATURE_RX_VLAN_STAG_HW_PARSE, - NM_ETHTOOL_ID_FEATURE_RXHASH, - NM_ETHTOOL_ID_FEATURE_RXVLAN, - NM_ETHTOOL_ID_FEATURE_SG, - NM_ETHTOOL_ID_FEATURE_TLS_HW_RECORD, - NM_ETHTOOL_ID_FEATURE_TLS_HW_RX_OFFLOAD, - NM_ETHTOOL_ID_FEATURE_TLS_HW_TX_OFFLOAD, - NM_ETHTOOL_ID_FEATURE_TSO, - NM_ETHTOOL_ID_FEATURE_TX, - NM_ETHTOOL_ID_FEATURE_TX_CHECKSUM_FCOE_CRC, - NM_ETHTOOL_ID_FEATURE_TX_CHECKSUM_IP_GENERIC, - NM_ETHTOOL_ID_FEATURE_TX_CHECKSUM_IPV4, - NM_ETHTOOL_ID_FEATURE_TX_CHECKSUM_IPV6, - NM_ETHTOOL_ID_FEATURE_TX_CHECKSUM_SCTP, - NM_ETHTOOL_ID_FEATURE_TX_ESP_SEGMENTATION, - NM_ETHTOOL_ID_FEATURE_TX_FCOE_SEGMENTATION, - NM_ETHTOOL_ID_FEATURE_TX_GRE_CSUM_SEGMENTATION, - NM_ETHTOOL_ID_FEATURE_TX_GRE_SEGMENTATION, - NM_ETHTOOL_ID_FEATURE_TX_GSO_LIST, - NM_ETHTOOL_ID_FEATURE_TX_GSO_PARTIAL, - NM_ETHTOOL_ID_FEATURE_TX_GSO_ROBUST, - NM_ETHTOOL_ID_FEATURE_TX_IPXIP4_SEGMENTATION, - NM_ETHTOOL_ID_FEATURE_TX_IPXIP6_SEGMENTATION, - NM_ETHTOOL_ID_FEATURE_TX_NOCACHE_COPY, - NM_ETHTOOL_ID_FEATURE_TX_SCATTER_GATHER, - NM_ETHTOOL_ID_FEATURE_TX_SCATTER_GATHER_FRAGLIST, - NM_ETHTOOL_ID_FEATURE_TX_SCTP_SEGMENTATION, - NM_ETHTOOL_ID_FEATURE_TX_TCP_ECN_SEGMENTATION, - NM_ETHTOOL_ID_FEATURE_TX_TCP_MANGLEID_SEGMENTATION, - NM_ETHTOOL_ID_FEATURE_TX_TCP_SEGMENTATION, - NM_ETHTOOL_ID_FEATURE_TX_TCP6_SEGMENTATION, - NM_ETHTOOL_ID_FEATURE_TX_TUNNEL_REMCSUM_SEGMENTATION, - NM_ETHTOOL_ID_FEATURE_TX_UDP_SEGMENTATION, - NM_ETHTOOL_ID_FEATURE_TX_UDP_TNL_CSUM_SEGMENTATION, - NM_ETHTOOL_ID_FEATURE_TX_UDP_TNL_SEGMENTATION, - NM_ETHTOOL_ID_FEATURE_TX_VLAN_STAG_HW_INSERT, - NM_ETHTOOL_ID_FEATURE_TXVLAN, - NM_ETHTOOL_ID_RING_RX, - NM_ETHTOOL_ID_RING_RX_JUMBO, - NM_ETHTOOL_ID_RING_RX_MINI, - NM_ETHTOOL_ID_RING_TX, -}; - -/*****************************************************************************/ - -static void -_ASSERT_data(void) -{ - int i; - - if (!NM_MORE_ASSERT_ONCE(10)) - return; - - G_STATIC_ASSERT_EXPR(_NM_ETHTOOL_ID_FIRST == 0); - G_STATIC_ASSERT_EXPR(_NM_ETHTOOL_ID_LAST == _NM_ETHTOOL_ID_NUM - 1); - G_STATIC_ASSERT_EXPR(_NM_ETHTOOL_ID_NUM > 0); - - nm_assert(NM_PTRARRAY_LEN(nm_ethtool_data) == _NM_ETHTOOL_ID_NUM); - nm_assert(G_N_ELEMENTS(_by_name) == _NM_ETHTOOL_ID_NUM); - nm_assert(G_N_ELEMENTS(nm_ethtool_data) == _NM_ETHTOOL_ID_NUM + 1); - - for (i = 0; i < _NM_ETHTOOL_ID_NUM; i++) { - const NMEthtoolData *d = nm_ethtool_data[i]; - - nm_assert(d); - nm_assert(d->id == (NMEthtoolID) i); - nm_assert(d->optname && d->optname[0]); - } - - for (i = 0; i < _NM_ETHTOOL_ID_NUM; i++) { - NMEthtoolID id = _by_name[i]; - const NMEthtoolData *d; - - nm_assert(id >= 0); - nm_assert(id < _NM_ETHTOOL_ID_NUM); - - d = nm_ethtool_data[id]; - if (i > 0) { - /* since we assert that all optnames are sorted strictly monotonically increasing, - * it also follows that there are no duplicates in the _by_name. - * It also follows, that all names in nm_ethtool_data are unique. */ - if (strcmp(nm_ethtool_data[_by_name[i - 1]]->optname, d->optname) >= 0) { - g_error("nm_ethtool_data is not sorted asciibetically: %u/%s should be after %u/%s", - i - 1, - nm_ethtool_data[_by_name[i - 1]]->optname, - i, - d->optname); - } - } - } -} - -static int -_by_name_cmp(gconstpointer a, gconstpointer b, gpointer user_data) -{ - const guint8 *p_id = a; - const char * optname = b; - - nm_assert(p_id && p_id >= _by_name && p_id <= &_by_name[_NM_ETHTOOL_ID_NUM]); - nm_assert(*p_id < _NM_ETHTOOL_ID_NUM); - - return strcmp(nm_ethtool_data[*p_id]->optname, optname); -} - -const NMEthtoolData * -nm_ethtool_data_get_by_optname(const char *optname) -{ - gssize idx; - - if (!optname) - return NULL; - - _ASSERT_data(); - - idx = nm_utils_array_find_binary_search((gconstpointer *) _by_name, - sizeof(_by_name[0]), - _NM_ETHTOOL_ID_NUM, - optname, - _by_name_cmp, - NULL); - return (idx < 0) ? NULL : nm_ethtool_data[_by_name[idx]]; -} - -NMEthtoolType -nm_ethtool_id_to_type(NMEthtoolID id) -{ - if (nm_ethtool_id_is_coalesce(id)) - return NM_ETHTOOL_TYPE_COALESCE; - if (nm_ethtool_id_is_feature(id)) - return NM_ETHTOOL_TYPE_FEATURE; - if (nm_ethtool_id_is_ring(id)) - return NM_ETHTOOL_TYPE_RING; - - return NM_ETHTOOL_TYPE_UNKNOWN; -} diff --git a/shared/nm-base/nm-ethtool-base.h b/shared/nm-base/nm-ethtool-base.h deleted file mode 100644 index be90ce75..00000000 --- a/shared/nm-base/nm-ethtool-base.h +++ /dev/null @@ -1,37 +0,0 @@ -/* SPDX-License-Identifier: LGPL-2.1-or-later */ -/* - * Copyright (C) 2018 Red Hat, Inc. - */ - -#ifndef __NM_ETHTOOL_BASE_H__ -#define __NM_ETHTOOL_BASE_H__ - -#include "nm-base/nm-base.h" - -/*****************************************************************************/ - -typedef struct { - const char *optname; - NMEthtoolID id; -} NMEthtoolData; - -extern const NMEthtoolData *const nm_ethtool_data[_NM_ETHTOOL_ID_NUM + 1]; - -const NMEthtoolData *nm_ethtool_data_get_by_optname(const char *optname); - -NMEthtoolType nm_ethtool_id_to_type(NMEthtoolID id); - -/****************************************************************************/ - -static inline NMEthtoolID -nm_ethtool_id_get_by_name(const char *optname) -{ - const NMEthtoolData *d; - - d = nm_ethtool_data_get_by_optname(optname); - return d ? d->id : NM_ETHTOOL_ID_UNKNOWN; -} - -/****************************************************************************/ - -#endif /* __NM_ETHTOOL_BASE_H__ */ diff --git a/shared/nm-base/nm-ethtool-utils-base.h b/shared/nm-base/nm-ethtool-utils-base.h deleted file mode 100644 index d422724b..00000000 --- a/shared/nm-base/nm-ethtool-utils-base.h +++ /dev/null @@ -1,107 +0,0 @@ -/* SPDX-License-Identifier: LGPL-2.1-or-later */ -/* - * Copyright (C) 2018 Red Hat, Inc. - */ - -#ifndef __NM_ETHTOOL_UTILS_H__ -#define __NM_ETHTOOL_UTILS_H__ - -G_BEGIN_DECLS - -/*****************************************************************************/ - -#define NM_ETHTOOL_OPTNAME_FEATURE_ESP_HW_OFFLOAD "feature-esp-hw-offload" -#define NM_ETHTOOL_OPTNAME_FEATURE_ESP_TX_CSUM_HW_OFFLOAD "feature-esp-tx-csum-hw-offload" -#define NM_ETHTOOL_OPTNAME_FEATURE_FCOE_MTU "feature-fcoe-mtu" -#define NM_ETHTOOL_OPTNAME_FEATURE_GRO "feature-gro" -#define NM_ETHTOOL_OPTNAME_FEATURE_GSO "feature-gso" -#define NM_ETHTOOL_OPTNAME_FEATURE_HIGHDMA "feature-highdma" -#define NM_ETHTOOL_OPTNAME_FEATURE_HW_TC_OFFLOAD "feature-hw-tc-offload" -#define NM_ETHTOOL_OPTNAME_FEATURE_L2_FWD_OFFLOAD "feature-l2-fwd-offload" -#define NM_ETHTOOL_OPTNAME_FEATURE_LOOPBACK "feature-loopback" -#define NM_ETHTOOL_OPTNAME_FEATURE_LRO "feature-lro" -#define NM_ETHTOOL_OPTNAME_FEATURE_MACSEC_HW_OFFLOAD "feature-macsec-hw-offload" -#define NM_ETHTOOL_OPTNAME_FEATURE_NTUPLE "feature-ntuple" -#define NM_ETHTOOL_OPTNAME_FEATURE_RX "feature-rx" -#define NM_ETHTOOL_OPTNAME_FEATURE_RXHASH "feature-rxhash" -#define NM_ETHTOOL_OPTNAME_FEATURE_RXVLAN "feature-rxvlan" -#define NM_ETHTOOL_OPTNAME_FEATURE_RX_ALL "feature-rx-all" -#define NM_ETHTOOL_OPTNAME_FEATURE_RX_FCS "feature-rx-fcs" -#define NM_ETHTOOL_OPTNAME_FEATURE_RX_GRO_HW "feature-rx-gro-hw" -#define NM_ETHTOOL_OPTNAME_FEATURE_RX_GRO_LIST "feature-rx-gro-list" -#define NM_ETHTOOL_OPTNAME_FEATURE_RX_UDP_GRO_FORWARDING "feature-rx-udp-gro-forwarding" -#define NM_ETHTOOL_OPTNAME_FEATURE_RX_UDP_TUNNEL_PORT_OFFLOAD "feature-rx-udp_tunnel-port-offload" -#define NM_ETHTOOL_OPTNAME_FEATURE_RX_VLAN_FILTER "feature-rx-vlan-filter" -#define NM_ETHTOOL_OPTNAME_FEATURE_RX_VLAN_STAG_FILTER "feature-rx-vlan-stag-filter" -#define NM_ETHTOOL_OPTNAME_FEATURE_RX_VLAN_STAG_HW_PARSE "feature-rx-vlan-stag-hw-parse" -#define NM_ETHTOOL_OPTNAME_FEATURE_SG "feature-sg" -#define NM_ETHTOOL_OPTNAME_FEATURE_TLS_HW_RECORD "feature-tls-hw-record" -#define NM_ETHTOOL_OPTNAME_FEATURE_TLS_HW_RX_OFFLOAD "feature-tls-hw-rx-offload" -#define NM_ETHTOOL_OPTNAME_FEATURE_TLS_HW_TX_OFFLOAD "feature-tls-hw-tx-offload" -#define NM_ETHTOOL_OPTNAME_FEATURE_TSO "feature-tso" -#define NM_ETHTOOL_OPTNAME_FEATURE_TX "feature-tx" -#define NM_ETHTOOL_OPTNAME_FEATURE_TXVLAN "feature-txvlan" -#define NM_ETHTOOL_OPTNAME_FEATURE_TX_CHECKSUM_FCOE_CRC "feature-tx-checksum-fcoe-crc" -#define NM_ETHTOOL_OPTNAME_FEATURE_TX_CHECKSUM_IPV4 "feature-tx-checksum-ipv4" -#define NM_ETHTOOL_OPTNAME_FEATURE_TX_CHECKSUM_IPV6 "feature-tx-checksum-ipv6" -#define NM_ETHTOOL_OPTNAME_FEATURE_TX_CHECKSUM_IP_GENERIC "feature-tx-checksum-ip-generic" -#define NM_ETHTOOL_OPTNAME_FEATURE_TX_CHECKSUM_SCTP "feature-tx-checksum-sctp" -#define NM_ETHTOOL_OPTNAME_FEATURE_TX_ESP_SEGMENTATION "feature-tx-esp-segmentation" -#define NM_ETHTOOL_OPTNAME_FEATURE_TX_FCOE_SEGMENTATION "feature-tx-fcoe-segmentation" -#define NM_ETHTOOL_OPTNAME_FEATURE_TX_GRE_CSUM_SEGMENTATION "feature-tx-gre-csum-segmentation" -#define NM_ETHTOOL_OPTNAME_FEATURE_TX_GRE_SEGMENTATION "feature-tx-gre-segmentation" -#define NM_ETHTOOL_OPTNAME_FEATURE_TX_GSO_LIST "feature-tx-gso-list" -#define NM_ETHTOOL_OPTNAME_FEATURE_TX_GSO_PARTIAL "feature-tx-gso-partial" -#define NM_ETHTOOL_OPTNAME_FEATURE_TX_GSO_ROBUST "feature-tx-gso-robust" -#define NM_ETHTOOL_OPTNAME_FEATURE_TX_IPXIP4_SEGMENTATION "feature-tx-ipxip4-segmentation" -#define NM_ETHTOOL_OPTNAME_FEATURE_TX_IPXIP6_SEGMENTATION "feature-tx-ipxip6-segmentation" -#define NM_ETHTOOL_OPTNAME_FEATURE_TX_NOCACHE_COPY "feature-tx-nocache-copy" -#define NM_ETHTOOL_OPTNAME_FEATURE_TX_SCATTER_GATHER "feature-tx-scatter-gather" -#define NM_ETHTOOL_OPTNAME_FEATURE_TX_SCATTER_GATHER_FRAGLIST "feature-tx-scatter-gather-fraglist" -#define NM_ETHTOOL_OPTNAME_FEATURE_TX_SCTP_SEGMENTATION "feature-tx-sctp-segmentation" -#define NM_ETHTOOL_OPTNAME_FEATURE_TX_TCP6_SEGMENTATION "feature-tx-tcp6-segmentation" -#define NM_ETHTOOL_OPTNAME_FEATURE_TX_TCP_ECN_SEGMENTATION "feature-tx-tcp-ecn-segmentation" -#define NM_ETHTOOL_OPTNAME_FEATURE_TX_TCP_MANGLEID_SEGMENTATION \ - "feature-tx-tcp-mangleid-segmentation" -#define NM_ETHTOOL_OPTNAME_FEATURE_TX_TCP_SEGMENTATION "feature-tx-tcp-segmentation" -#define NM_ETHTOOL_OPTNAME_FEATURE_TX_TUNNEL_REMCSUM_SEGMENTATION \ - "feature-tx-tunnel-remcsum-segmentation" -#define NM_ETHTOOL_OPTNAME_FEATURE_TX_UDP_SEGMENTATION "feature-tx-udp-segmentation" -#define NM_ETHTOOL_OPTNAME_FEATURE_TX_UDP_TNL_CSUM_SEGMENTATION \ - "feature-tx-udp_tnl-csum-segmentation" -#define NM_ETHTOOL_OPTNAME_FEATURE_TX_UDP_TNL_SEGMENTATION "feature-tx-udp_tnl-segmentation" -#define NM_ETHTOOL_OPTNAME_FEATURE_TX_VLAN_STAG_HW_INSERT "feature-tx-vlan-stag-hw-insert" - -#define NM_ETHTOOL_OPTNAME_COALESCE_ADAPTIVE_RX "coalesce-adaptive-rx" -#define NM_ETHTOOL_OPTNAME_COALESCE_ADAPTIVE_TX "coalesce-adaptive-tx" -#define NM_ETHTOOL_OPTNAME_COALESCE_PKT_RATE_HIGH "coalesce-pkt-rate-high" -#define NM_ETHTOOL_OPTNAME_COALESCE_PKT_RATE_LOW "coalesce-pkt-rate-low" -#define NM_ETHTOOL_OPTNAME_COALESCE_RX_FRAMES "coalesce-rx-frames" -#define NM_ETHTOOL_OPTNAME_COALESCE_RX_FRAMES_HIGH "coalesce-rx-frames-high" -#define NM_ETHTOOL_OPTNAME_COALESCE_RX_FRAMES_IRQ "coalesce-rx-frames-irq" -#define NM_ETHTOOL_OPTNAME_COALESCE_RX_FRAMES_LOW "coalesce-rx-frames-low" -#define NM_ETHTOOL_OPTNAME_COALESCE_RX_USECS "coalesce-rx-usecs" -#define NM_ETHTOOL_OPTNAME_COALESCE_RX_USECS_HIGH "coalesce-rx-usecs-high" -#define NM_ETHTOOL_OPTNAME_COALESCE_RX_USECS_IRQ "coalesce-rx-usecs-irq" -#define NM_ETHTOOL_OPTNAME_COALESCE_RX_USECS_LOW "coalesce-rx-usecs-low" -#define NM_ETHTOOL_OPTNAME_COALESCE_SAMPLE_INTERVAL "coalesce-sample-interval" -#define NM_ETHTOOL_OPTNAME_COALESCE_STATS_BLOCK_USECS "coalesce-stats-block-usecs" -#define NM_ETHTOOL_OPTNAME_COALESCE_TX_FRAMES "coalesce-tx-frames" -#define NM_ETHTOOL_OPTNAME_COALESCE_TX_FRAMES_HIGH "coalesce-tx-frames-high" -#define NM_ETHTOOL_OPTNAME_COALESCE_TX_FRAMES_IRQ "coalesce-tx-frames-irq" -#define NM_ETHTOOL_OPTNAME_COALESCE_TX_FRAMES_LOW "coalesce-tx-frames-low" -#define NM_ETHTOOL_OPTNAME_COALESCE_TX_USECS "coalesce-tx-usecs" -#define NM_ETHTOOL_OPTNAME_COALESCE_TX_USECS_HIGH "coalesce-tx-usecs-high" -#define NM_ETHTOOL_OPTNAME_COALESCE_TX_USECS_IRQ "coalesce-tx-usecs-irq" -#define NM_ETHTOOL_OPTNAME_COALESCE_TX_USECS_LOW "coalesce-tx-usecs-low" - -#define NM_ETHTOOL_OPTNAME_RING_RX "ring-rx" -#define NM_ETHTOOL_OPTNAME_RING_RX_JUMBO "ring-rx-jumbo" -#define NM_ETHTOOL_OPTNAME_RING_RX_MINI "ring-rx-mini" -#define NM_ETHTOOL_OPTNAME_RING_TX "ring-tx" - -/*****************************************************************************/ - -G_END_DECLS - -#endif /* __NM_ETHTOOL_UTILS_H__ */ diff --git a/shared/nm-glib-aux/nm-c-list.h b/shared/nm-glib-aux/nm-c-list.h deleted file mode 100644 index 6dd3ac72..00000000 --- a/shared/nm-glib-aux/nm-c-list.h +++ /dev/null @@ -1,180 +0,0 @@ -/* SPDX-License-Identifier: LGPL-2.1-or-later */ -/* - * Copyright (C) 2014 Red Hat, Inc. - */ - -#ifndef __NM_C_LIST_H__ -#define __NM_C_LIST_H__ - -#include "c-list/src/c-list.h" - -/*****************************************************************************/ - -#define nm_c_list_contains_entry(list, what, member) \ - ({ \ - typeof(what) _what = (what); \ - \ - _what &&c_list_contains(list, &_what->member); \ - }) - -/*****************************************************************************/ - -typedef struct { - CList lst; - void *data; -} NMCListElem; - -static inline NMCListElem * -nm_c_list_elem_new_stale(void *data) -{ - NMCListElem *elem; - - elem = g_slice_new(NMCListElem); - elem->data = data; - return elem; -} - -static inline gboolean -nm_c_list_elem_free_full(NMCListElem *elem, GDestroyNotify free_fcn) -{ - if (!elem) - return FALSE; - c_list_unlink_stale(&elem->lst); - if (free_fcn) - free_fcn(elem->data); - g_slice_free(NMCListElem, elem); - return TRUE; -} - -static inline gboolean -nm_c_list_elem_free(NMCListElem *elem) -{ - return nm_c_list_elem_free_full(elem, NULL); -} - -static inline void * -nm_c_list_elem_free_steal(NMCListElem *elem) -{ - gpointer data; - - if (!elem) - return NULL; - data = elem->data; - nm_c_list_elem_free_full(elem, NULL); - return data; -} - -static inline void -nm_c_list_elem_free_all(CList *head, GDestroyNotify free_fcn) -{ - NMCListElem *elem; - - while ((elem = c_list_first_entry(head, NMCListElem, lst))) - nm_c_list_elem_free_full(elem, free_fcn); -} - -#define nm_c_list_elem_find_first(head, arg, predicate) \ - ({ \ - CList *const _head = (head); \ - NMCListElem *_result = NULL; \ - NMCListElem *_elem; \ - \ - c_list_for_each_entry (_elem, _head, lst) { \ - void *const arg = _elem->data; \ - \ - if (predicate) { \ - _result = _elem; \ - break; \ - } \ - } \ - _result; \ - }) - -/** - * nm_c_list_elem_find_first_ptr: - * @head: the @CList head of a list containing #NMCListElem elements. - * Note that the head is not itself part of the list. - * @needle: the needle pointer. - * - * Iterates the list and returns the first #NMCListElem with the matching @needle, - * using pointer equality. - * - * Returns: the found list element or %NULL if not found. - */ -static inline NMCListElem * -nm_c_list_elem_find_first_ptr(CList *head, gconstpointer needle) -{ - return nm_c_list_elem_find_first(head, x, x == needle); -} - -/*****************************************************************************/ - -/** - * nm_c_list_move_before: - * @lst: the list element to which @elem will be prepended. - * @elem: the list element to move. - * - * This unlinks @elem from the current list and linkes it before - * @lst. This is like c_list_link_before(), except that @elem must - * be initialized and linked. Note that @elem may be linked in @lst - * or in another list. In both cases it gets moved. - * - * Returns: %TRUE if there were any changes. %FALSE if elem was already - * linked at the right place. - */ -static inline gboolean -nm_c_list_move_before(CList *lst, CList *elem) -{ - nm_assert(lst); - nm_assert(elem); - - if (lst != elem && lst->prev != elem) { - c_list_unlink_stale(elem); - c_list_link_before(lst, elem); - return TRUE; - } - return FALSE; -} -#define nm_c_list_move_tail(lst, elem) nm_c_list_move_before(lst, elem) - -/** - * nm_c_list_move_after: - * @lst: the list element to which @elem will be prepended. - * @elem: the list element to move. - * - * This unlinks @elem from the current list and linkes it after - * @lst. This is like c_list_link_after(), except that @elem must - * be initialized and linked. Note that @elem may be linked in @lst - * or in another list. In both cases it gets moved. - * - * Returns: %TRUE if there were any changes. %FALSE if elem was already - * linked at the right place. - */ -static inline gboolean -nm_c_list_move_after(CList *lst, CList *elem) -{ - nm_assert(lst); - nm_assert(elem); - - if (lst != elem && lst->next != elem) { - c_list_unlink_stale(elem); - c_list_link_after(lst, elem); - return TRUE; - } - return FALSE; -} -#define nm_c_list_move_front(lst, elem) nm_c_list_move_after(lst, elem) - -#define nm_c_list_free_all(lst, type, member, destroy_fcn) \ - G_STMT_START \ - { \ - CList *const _lst = (lst); \ - type * _elem; \ - \ - while ((_elem = c_list_first_entry(_lst, type, member))) { \ - destroy_fcn(_elem); \ - } \ - } \ - G_STMT_END - -#endif /* __NM_C_LIST_H__ */ diff --git a/shared/nm-glib-aux/nm-dbus-aux.c b/shared/nm-glib-aux/nm-dbus-aux.c deleted file mode 100644 index 9fb02a3f..00000000 --- a/shared/nm-glib-aux/nm-dbus-aux.c +++ /dev/null @@ -1,405 +0,0 @@ -/* SPDX-License-Identifier: LGPL-2.1-or-later */ -/* - * Copyright (C) 2019 Red Hat, Inc. - */ - -#include "nm-glib-aux/nm-default-glib-i18n-lib.h" - -#include "nm-dbus-aux.h" - -/*****************************************************************************/ - -static void -_nm_dbus_connection_call_get_name_owner_cb(GObject *source, GAsyncResult *res, gpointer user_data) -{ - gs_unref_variant GVariant *ret = NULL; - gs_free_error GError * error = NULL; - const char * owner = NULL; - gpointer orig_user_data; - NMDBusConnectionCallGetNameOwnerCb callback; - - nm_utils_user_data_unpack(user_data, &orig_user_data, &callback); - - ret = g_dbus_connection_call_finish(G_DBUS_CONNECTION(source), res, &error); - if (ret) - g_variant_get(ret, "(&s)", &owner); - - callback(owner, error, orig_user_data); -} - -void -nm_dbus_connection_call_get_name_owner(GDBusConnection * dbus_connection, - const char * service_name, - int timeout_msec, - GCancellable * cancellable, - NMDBusConnectionCallGetNameOwnerCb callback, - gpointer user_data) -{ - nm_assert(callback); - - g_dbus_connection_call(dbus_connection, - DBUS_SERVICE_DBUS, - DBUS_PATH_DBUS, - DBUS_INTERFACE_DBUS, - "GetNameOwner", - g_variant_new("(s)", service_name), - G_VARIANT_TYPE("(s)"), - G_DBUS_CALL_FLAGS_NONE, - timeout_msec, - cancellable, - _nm_dbus_connection_call_get_name_owner_cb, - nm_utils_user_data_pack(user_data, callback)); -} - -/*****************************************************************************/ - -static void -_nm_dbus_connection_call_default_cb(GObject *source, GAsyncResult *res, gpointer user_data) -{ - gs_unref_variant GVariant *ret = NULL; - gs_free_error GError * error = NULL; - gpointer orig_user_data; - NMDBusConnectionCallDefaultCb callback; - - nm_utils_user_data_unpack(user_data, &orig_user_data, &callback); - - ret = g_dbus_connection_call_finish(G_DBUS_CONNECTION(source), res, &error); - - nm_assert((!!ret) != (!!error)); - - callback(ret, error, orig_user_data); -} - -void -nm_dbus_connection_call_get_all(GDBusConnection * dbus_connection, - const char * bus_name, - const char * object_path, - const char * interface_name, - int timeout_msec, - GCancellable * cancellable, - NMDBusConnectionCallDefaultCb callback, - gpointer user_data) -{ - nm_assert(callback); - - g_dbus_connection_call(dbus_connection, - bus_name, - object_path, - DBUS_INTERFACE_PROPERTIES, - "GetAll", - g_variant_new("(s)", interface_name), - G_VARIANT_TYPE("(a{sv})"), - G_DBUS_CALL_FLAGS_NONE, - timeout_msec, - cancellable, - _nm_dbus_connection_call_default_cb, - nm_utils_user_data_pack(user_data, callback)); -} - -void -nm_dbus_connection_call_set(GDBusConnection * dbus_connection, - const char * bus_name, - const char * object_path, - const char * interface_name, - const char * property_name, - GVariant * value, - int timeout_msec, - GCancellable * cancellable, - NMDBusConnectionCallDefaultCb callback, - gpointer user_data) -{ - g_dbus_connection_call(dbus_connection, - bus_name, - object_path, - DBUS_INTERFACE_PROPERTIES, - "Set", - g_variant_new("(ssv)", interface_name, property_name, value), - G_VARIANT_TYPE("()"), - G_DBUS_CALL_FLAGS_NONE, - timeout_msec, - cancellable, - callback ? _nm_dbus_connection_call_default_cb : NULL, - callback ? nm_utils_user_data_pack(user_data, callback) : NULL); -} - -/*****************************************************************************/ - -static void -_nm_dbus_connection_call_get_managed_objects_cb(GObject * source, - GAsyncResult *res, - gpointer user_data) -{ - gs_unref_variant GVariant *ret = NULL; - gs_unref_variant GVariant *arg = NULL; - gs_free_error GError * error = NULL; - gpointer orig_user_data; - NMDBusConnectionCallDefaultCb callback; - - nm_utils_user_data_unpack(user_data, &orig_user_data, &callback); - - ret = g_dbus_connection_call_finish(G_DBUS_CONNECTION(source), res, &error); - - nm_assert((!!ret) != (!!error)); - - if (ret) { - nm_assert(g_variant_is_of_type(ret, G_VARIANT_TYPE("(a{oa{sa{sv}}})"))); - arg = g_variant_get_child_value(ret, 0); - } - - callback(arg, error, orig_user_data); -} - -void -nm_dbus_connection_call_get_managed_objects(GDBusConnection * dbus_connection, - const char * bus_name, - const char * object_path, - GDBusCallFlags flags, - int timeout_msec, - GCancellable * cancellable, - NMDBusConnectionCallDefaultCb callback, - gpointer user_data) -{ - nm_assert(callback); - - g_dbus_connection_call(dbus_connection, - bus_name, - object_path, - DBUS_INTERFACE_OBJECT_MANAGER, - "GetManagedObjects", - NULL, - G_VARIANT_TYPE("(a{oa{sa{sv}}})"), - flags, - timeout_msec, - cancellable, - _nm_dbus_connection_call_get_managed_objects_cb, - nm_utils_user_data_pack(user_data, callback)); -} - -/*****************************************************************************/ - -static void -_call_finish_cb(GObject * source, - GAsyncResult *result, - gpointer user_data, - gboolean return_void, - gboolean strip_dbus_error) -{ - gs_unref_object GTask *task = user_data; - gs_unref_variant GVariant *ret = NULL; - GError * error = NULL; - - nm_assert(G_IS_DBUS_CONNECTION(source)); - nm_assert(G_IS_TASK(user_data)); - - ret = g_dbus_connection_call_finish(G_DBUS_CONNECTION(source), result, &error); - if (!ret) { - if (strip_dbus_error) - g_dbus_error_strip_remote_error(error); - g_task_return_error(task, error); - return; - } - - if (!return_void) - g_task_return_pointer(task, g_steal_pointer(&ret), (GDestroyNotify) g_variant_unref); - else { - nm_assert(g_variant_is_of_type(ret, G_VARIANT_TYPE("()"))); - g_task_return_boolean(task, TRUE); - } -} - -/** - * nm_dbus_connection_call_finish_void_cb: - * - * A default callback to pass as callback to g_dbus_connection_call(). - * - * - user_data must be a GTask, whose reference will be consumed by the - * callback. - * - the return GVariant must be a empty tuple "()". - * - the GTask is returned either with error or TRUE boolean. - */ -void -nm_dbus_connection_call_finish_void_cb(GObject *source, GAsyncResult *result, gpointer user_data) -{ - _call_finish_cb(source, result, user_data, TRUE, FALSE); -} - -/** - * nm_dbus_connection_call_finish_void_strip_dbus_error_cb: - * - * Like nm_dbus_connection_call_finish_void_cb(). The difference - * is that on error this will first call g_dbus_error_strip_remote_error() on the error. - */ -void -nm_dbus_connection_call_finish_void_strip_dbus_error_cb(GObject * source, - GAsyncResult *result, - gpointer user_data) -{ - _call_finish_cb(source, result, user_data, TRUE, TRUE); -} - -/** - * nm_dbus_connection_call_finish_variant_cb: - * - * A default callback to pass as callback to g_dbus_connection_call(). - * - * - user_data must be a GTask, whose reference will be consumed by the - * callback. - * - the GTask is returned either with error or with a pointer containing the GVariant. - */ -void -nm_dbus_connection_call_finish_variant_cb(GObject *source, GAsyncResult *result, gpointer user_data) -{ - _call_finish_cb(source, result, user_data, FALSE, FALSE); -} - -/** - * nm_dbus_connection_call_finish_variant_strip_dbus_error_cb: - * - * Like nm_dbus_connection_call_finish_variant_strip_dbus_error_cb(). The difference - * is that on error this will first call g_dbus_error_strip_remote_error() on the error. - */ -void -nm_dbus_connection_call_finish_variant_strip_dbus_error_cb(GObject * source, - GAsyncResult *result, - gpointer user_data) -{ - _call_finish_cb(source, result, user_data, FALSE, TRUE); -} - -/*****************************************************************************/ - -typedef struct { - char * bus_name; - char * object_path; - char * interface_name; - char * method_name; - GVariant * parameters; - GDBusConnection * connection; - const GVariantType *reply_type; - int timeout_msec; -} CallAsyncInfo; - -static void -call_async_info_destroy(CallAsyncInfo *info) -{ - g_free(info->bus_name); - g_free(info->object_path); - g_free(info->interface_name); - g_free(info->method_name); - g_variant_unref(info->parameters); - nm_g_object_unref(info->connection); - g_free(info); -} - -static void -call_cb(GObject *source, GAsyncResult *result, gpointer user_data) -{ - gs_unref_object GTask *task = user_data; - GError * error = NULL; - GVariant * ret; - - ret = g_dbus_connection_call_finish(G_DBUS_CONNECTION(source), result, &error); - if (!ret) { - g_task_return_error(task, error); - return; - } - - g_task_return_pointer(task, ret, (GDestroyNotify) g_variant_unref); -} - -static void -call_bus_get_cb(GObject *source, GAsyncResult *result, gpointer user_data) -{ - gs_unref_object GTask *task = user_data; - GCancellable * cancellable; - CallAsyncInfo * info; - GError * error = NULL; - - info = g_task_get_task_data(task); - info->connection = g_bus_get_finish(result, &error); - cancellable = g_task_get_cancellable(task); - - if (!info->connection) { - g_task_return_error(task, g_steal_pointer(&error)); - return; - } - - g_dbus_connection_call(info->connection, - info->bus_name, - info->object_path, - info->interface_name, - info->method_name, - info->parameters, - info->reply_type, - G_DBUS_CALL_FLAGS_NONE, - info->timeout_msec, - cancellable, - call_cb, - g_steal_pointer(&task)); -} - -void -nm_dbus_call(GBusType bus_type, - const char * bus_name, - const char * object_path, - const char * interface_name, - const char * method_name, - GVariant * parameters, - const GVariantType *reply_type, - GCancellable * cancellable, - int timeout_msec, - GAsyncReadyCallback callback, - gpointer user_data) -{ - GTask * task; - CallAsyncInfo *info; - - info = g_new(CallAsyncInfo, 1); - *info = (CallAsyncInfo){ - .bus_name = g_strdup(bus_name), - .object_path = g_strdup(object_path), - .interface_name = g_strdup(interface_name), - .method_name = g_strdup(method_name), - .parameters = g_variant_ref_sink(parameters), - .reply_type = reply_type, - .timeout_msec = timeout_msec, - }; - - task = nm_g_task_new(NULL, cancellable, nm_dbus_call, callback, user_data); - g_task_set_task_data(task, info, (GDestroyNotify) call_async_info_destroy); - - g_bus_get(bus_type, cancellable, call_bus_get_cb, task); -} - -GVariant * -nm_dbus_call_finish(GAsyncResult *result, GError **error) -{ - nm_assert(nm_g_task_is_valid(result, NULL, nm_dbus_call)); - - return g_task_propagate_pointer(G_TASK(result), error); -} - -/*****************************************************************************/ - -gboolean -_nm_dbus_error_is(GError *error, ...) -{ - gs_free char *dbus_error = NULL; - const char * name; - va_list ap; - - dbus_error = g_dbus_error_get_remote_error(error); - if (!dbus_error) - return FALSE; - - va_start(ap, error); - while ((name = va_arg(ap, const char *))) { - if (nm_streq(dbus_error, name)) { - va_end(ap); - return TRUE; - } - } - va_end(ap); - - return FALSE; -} diff --git a/shared/nm-glib-aux/nm-dbus-aux.h b/shared/nm-glib-aux/nm-dbus-aux.h deleted file mode 100644 index c8107082..00000000 --- a/shared/nm-glib-aux/nm-dbus-aux.h +++ /dev/null @@ -1,218 +0,0 @@ -/* SPDX-License-Identifier: LGPL-2.1-or-later */ -/* - * Copyright (C) 2019 Red Hat, Inc. - */ - -#ifndef __NM_DBUS_AUX_H__ -#define __NM_DBUS_AUX_H__ - -#include "nm-std-aux/nm-dbus-compat.h" - -/*****************************************************************************/ - -static inline gboolean -nm_clear_g_dbus_connection_signal(GDBusConnection *dbus_connection, guint *id) -{ - guint v; - - if (id && (v = *id)) { - *id = 0; - g_dbus_connection_signal_unsubscribe(dbus_connection, v); - return TRUE; - } - return FALSE; -} - -/*****************************************************************************/ - -typedef void (*NMDBusConnectionCallDefaultCb)(GVariant *result, GError *error, gpointer user_data); - -/*****************************************************************************/ - -static inline void -nm_dbus_connection_call_start_service_by_name(GDBusConnection * dbus_connection, - const char * name, - int timeout_msec, - GCancellable * cancellable, - GAsyncReadyCallback callback, - gpointer user_data) -{ - g_dbus_connection_call(dbus_connection, - DBUS_SERVICE_DBUS, - DBUS_PATH_DBUS, - DBUS_INTERFACE_DBUS, - "StartServiceByName", - g_variant_new("(su)", name, 0u), - G_VARIANT_TYPE("(u)"), - G_DBUS_CALL_FLAGS_NONE, - timeout_msec, - cancellable, - callback, - user_data); -} - -/*****************************************************************************/ - -static inline guint -nm_dbus_connection_signal_subscribe_name_owner_changed(GDBusConnection * dbus_connection, - const char * service_name, - GDBusSignalCallback callback, - gpointer user_data, - GDestroyNotify user_data_free_func) - -{ - return g_dbus_connection_signal_subscribe(dbus_connection, - DBUS_SERVICE_DBUS, - DBUS_INTERFACE_DBUS, - "NameOwnerChanged", - DBUS_PATH_DBUS, - service_name, - G_DBUS_SIGNAL_FLAGS_NONE, - callback, - user_data, - user_data_free_func); -} - -typedef void (*NMDBusConnectionCallGetNameOwnerCb)(const char *name_owner, - GError * error, - gpointer user_data); - -void nm_dbus_connection_call_get_name_owner(GDBusConnection * dbus_connection, - const char * service_name, - int timeout_msec, - GCancellable * cancellable, - NMDBusConnectionCallGetNameOwnerCb callback, - gpointer user_data); - -static inline guint -nm_dbus_connection_signal_subscribe_properties_changed(GDBusConnection * dbus_connection, - const char * bus_name, - const char * object_path, - const char * interface_name, - GDBusSignalCallback callback, - gpointer user_data, - GDestroyNotify user_data_free_func) - -{ - nm_assert(bus_name); - - /* it seems that using a non-unique name causes problems that we get signals - * also from unrelated senders. Usually, you are anyway monitoring the name-owner, - * so you should have the unique name at hand. - * - * If not, investigate this, ensure that it works, and lift this restriction. */ - nm_assert(g_dbus_is_unique_name(bus_name)); - - return g_dbus_connection_signal_subscribe(dbus_connection, - bus_name, - DBUS_INTERFACE_PROPERTIES, - "PropertiesChanged", - object_path, - interface_name, - G_DBUS_SIGNAL_FLAGS_NONE, - callback, - user_data, - user_data_free_func); -} - -void nm_dbus_connection_call_get_all(GDBusConnection * dbus_connection, - const char * bus_name, - const char * object_path, - const char * interface_name, - int timeout_msec, - GCancellable * cancellable, - NMDBusConnectionCallDefaultCb callback, - gpointer user_data); - -void nm_dbus_connection_call_set(GDBusConnection * dbus_connection, - const char * bus_name, - const char * object_path, - const char * interface_name, - const char * property_name, - GVariant * value, - int timeout_msec, - GCancellable * cancellable, - NMDBusConnectionCallDefaultCb callback, - gpointer user_data); - -/*****************************************************************************/ - -static inline guint -nm_dbus_connection_signal_subscribe_object_manager(GDBusConnection * dbus_connection, - const char * service_name, - const char * object_path, - const char * signal_name, - GDBusSignalCallback callback, - gpointer user_data, - GDestroyNotify user_data_free_func) -{ - return g_dbus_connection_signal_subscribe(dbus_connection, - service_name, - DBUS_INTERFACE_OBJECT_MANAGER, - signal_name, - object_path, - NULL, - G_DBUS_SIGNAL_FLAGS_NONE, - callback, - user_data, - user_data_free_func); -} - -void nm_dbus_connection_call_get_managed_objects(GDBusConnection * dbus_connection, - const char * bus_name, - const char * object_path, - GDBusCallFlags flags, - int timeout_msec, - GCancellable * cancellable, - NMDBusConnectionCallDefaultCb callback, - gpointer user_data); - -/*****************************************************************************/ - -void -nm_dbus_connection_call_finish_void_cb(GObject *source, GAsyncResult *result, gpointer user_data); - -void nm_dbus_connection_call_finish_void_strip_dbus_error_cb(GObject * source, - GAsyncResult *result, - gpointer user_data); - -void nm_dbus_connection_call_finish_variant_cb(GObject * source, - GAsyncResult *result, - gpointer user_data); - -void nm_dbus_connection_call_finish_variant_strip_dbus_error_cb(GObject * source, - GAsyncResult *result, - gpointer user_data); - -/*****************************************************************************/ - -void nm_dbus_call(GBusType bus_type, - const char * bus_name, - const char * object_path, - const char * interface_name, - const char * method_name, - GVariant * parameters, - const GVariantType *reply_type, - GCancellable * cancellable, - int timeout_msec, - GAsyncReadyCallback callback, - gpointer user_data); - -GVariant *nm_dbus_call_finish(GAsyncResult *result, GError **error); - -/*****************************************************************************/ - -gboolean _nm_dbus_error_is(GError *error, ...) G_GNUC_NULL_TERMINATED; - -#define nm_dbus_error_is(error, ...) \ - ({ \ - GError *const _error = (error); \ - \ - _error &&_nm_dbus_error_is(_error, __VA_ARGS__, NULL); \ - }) - -#define NM_DBUS_ERROR_NAME_UNKNOWN_METHOD "org.freedesktop.DBus.Error.UnknownMethod" - -/*****************************************************************************/ - -#endif /* __NM_DBUS_AUX_H__ */ diff --git a/shared/nm-glib-aux/nm-dedup-multi.c b/shared/nm-glib-aux/nm-dedup-multi.c deleted file mode 100644 index 99da3b35..00000000 --- a/shared/nm-glib-aux/nm-dedup-multi.c +++ /dev/null @@ -1,1065 +0,0 @@ -/* SPDX-License-Identifier: LGPL-2.1-or-later */ -/* - * Copyright (C) 2017 Red Hat, Inc. - */ - -#include "nm-glib-aux/nm-default-glib-i18n-lib.h" - -#include "nm-dedup-multi.h" - -#include "nm-hash-utils.h" -#include "nm-c-list.h" - -/*****************************************************************************/ - -typedef struct { - /* the stack-allocated lookup entry. It has a compatible - * memory layout with NMDedupMultiEntry and NMDedupMultiHeadEntry. - * - * It is recognizable by having lst_entries_sentinel.next set to NULL. - * Contrary to the other entries, which have lst_entries.next - * always non-NULL. - * */ - CList lst_entries_sentinel; - const NMDedupMultiObj * obj; - const NMDedupMultiIdxType *idx_type; - bool lookup_head; -} LookupEntry; - -struct _NMDedupMultiIndex { - int ref_count; - GHashTable *idx_entries; - GHashTable *idx_objs; -}; - -/*****************************************************************************/ - -static void -ASSERT_idx_type(const NMDedupMultiIdxType *idx_type) -{ - nm_assert(idx_type); -#if NM_MORE_ASSERTS > 10 - nm_assert(idx_type->klass); - nm_assert(idx_type->klass->idx_obj_id_hash_update); - nm_assert(idx_type->klass->idx_obj_id_equal); - nm_assert(!!idx_type->klass->idx_obj_partition_hash_update - == !!idx_type->klass->idx_obj_partition_equal); - nm_assert(idx_type->lst_idx_head.next); -#endif -} - -void -nm_dedup_multi_idx_type_init(NMDedupMultiIdxType *idx_type, const NMDedupMultiIdxTypeClass *klass) -{ - nm_assert(idx_type); - nm_assert(klass); - - *idx_type = (NMDedupMultiIdxType){ - .klass = klass, - .lst_idx_head = C_LIST_INIT(idx_type->lst_idx_head), - }; - - ASSERT_idx_type(idx_type); -} - -/*****************************************************************************/ - -static NMDedupMultiEntry * -_entry_lookup_obj(const NMDedupMultiIndex * self, - const NMDedupMultiIdxType *idx_type, - const NMDedupMultiObj * obj) -{ - const LookupEntry stack_entry = { - .obj = obj, - .idx_type = idx_type, - .lookup_head = FALSE, - }; - - ASSERT_idx_type(idx_type); - return g_hash_table_lookup(self->idx_entries, &stack_entry); -} - -static NMDedupMultiHeadEntry * -_entry_lookup_head(const NMDedupMultiIndex * self, - const NMDedupMultiIdxType *idx_type, - const NMDedupMultiObj * obj) -{ - NMDedupMultiHeadEntry *head_entry; - const LookupEntry stack_entry = { - .obj = obj, - .idx_type = idx_type, - .lookup_head = TRUE, - }; - - ASSERT_idx_type(idx_type); - - if (!idx_type->klass->idx_obj_partition_equal) { - if (c_list_is_empty(&idx_type->lst_idx_head)) - head_entry = NULL; - else { - nm_assert(c_list_length(&idx_type->lst_idx_head) == 1); - head_entry = c_list_entry(idx_type->lst_idx_head.next, NMDedupMultiHeadEntry, lst_idx); - } - nm_assert(head_entry == g_hash_table_lookup(self->idx_entries, &stack_entry)); - return head_entry; - } - - return g_hash_table_lookup(self->idx_entries, &stack_entry); -} - -static void -_entry_unpack(const NMDedupMultiEntry * entry, - const NMDedupMultiIdxType **out_idx_type, - const NMDedupMultiObj ** out_obj, - gboolean * out_lookup_head) -{ - const NMDedupMultiHeadEntry *head_entry; - const LookupEntry * lookup_entry; - - nm_assert(entry); - - G_STATIC_ASSERT_EXPR(G_STRUCT_OFFSET(LookupEntry, lst_entries_sentinel) - == G_STRUCT_OFFSET(NMDedupMultiEntry, lst_entries)); - G_STATIC_ASSERT_EXPR(G_STRUCT_OFFSET(NMDedupMultiEntry, lst_entries) - == G_STRUCT_OFFSET(NMDedupMultiHeadEntry, lst_entries_head)); - G_STATIC_ASSERT_EXPR(G_STRUCT_OFFSET(NMDedupMultiEntry, obj) - == G_STRUCT_OFFSET(NMDedupMultiHeadEntry, idx_type)); - G_STATIC_ASSERT_EXPR(G_STRUCT_OFFSET(NMDedupMultiEntry, is_head) - == G_STRUCT_OFFSET(NMDedupMultiHeadEntry, is_head)); - - if (!entry->lst_entries.next) { - /* the entry is stack-allocated by _entry_lookup(). */ - lookup_entry = (LookupEntry *) entry; - *out_obj = lookup_entry->obj; - *out_idx_type = lookup_entry->idx_type; - *out_lookup_head = lookup_entry->lookup_head; - } else if (entry->is_head) { - head_entry = (NMDedupMultiHeadEntry *) entry; - nm_assert(!c_list_is_empty(&head_entry->lst_entries_head)); - *out_obj = - c_list_entry(head_entry->lst_entries_head.next, NMDedupMultiEntry, lst_entries)->obj; - *out_idx_type = head_entry->idx_type; - *out_lookup_head = TRUE; - } else { - *out_obj = entry->obj; - *out_idx_type = entry->head->idx_type; - *out_lookup_head = FALSE; - } - - nm_assert(NM_IN_SET(*out_lookup_head, FALSE, TRUE)); - ASSERT_idx_type(*out_idx_type); - - /* for lookup of the head, we allow to omit object, but only - * if the idx_type does not partition the objects. Otherwise, we - * require a obj to compare. */ - nm_assert(!*out_lookup_head || (*out_obj || !(*out_idx_type)->klass->idx_obj_partition_equal)); - - /* lookup of the object requires always an object. */ - nm_assert(*out_lookup_head || *out_obj); -} - -static guint -_dict_idx_entries_hash(const NMDedupMultiEntry *entry) -{ - const NMDedupMultiIdxType *idx_type; - const NMDedupMultiObj * obj; - gboolean lookup_head; - NMHashState h; - - _entry_unpack(entry, &idx_type, &obj, &lookup_head); - - nm_hash_init(&h, 1914869417u); - if (idx_type->klass->idx_obj_partition_hash_update) { - nm_assert(obj); - idx_type->klass->idx_obj_partition_hash_update(idx_type, obj, &h); - } - - if (!lookup_head) - idx_type->klass->idx_obj_id_hash_update(idx_type, obj, &h); - - nm_hash_update_val(&h, idx_type); - return nm_hash_complete(&h); -} - -static gboolean -_dict_idx_entries_equal(const NMDedupMultiEntry *entry_a, const NMDedupMultiEntry *entry_b) -{ - const NMDedupMultiIdxType *idx_type_a, *idx_type_b; - const NMDedupMultiObj * obj_a, *obj_b; - gboolean lookup_head_a, lookup_head_b; - - _entry_unpack(entry_a, &idx_type_a, &obj_a, &lookup_head_a); - _entry_unpack(entry_b, &idx_type_b, &obj_b, &lookup_head_b); - - if (idx_type_a != idx_type_b || lookup_head_a != lookup_head_b) - return FALSE; - if (!nm_dedup_multi_idx_type_partition_equal(idx_type_a, obj_a, obj_b)) - return FALSE; - if (!lookup_head_a && !nm_dedup_multi_idx_type_id_equal(idx_type_a, obj_a, obj_b)) - return FALSE; - return TRUE; -} - -/*****************************************************************************/ - -static gboolean -_add(NMDedupMultiIndex * self, - NMDedupMultiIdxType * idx_type, - const NMDedupMultiObj * obj, - NMDedupMultiEntry * entry, - NMDedupMultiIdxMode mode, - const NMDedupMultiEntry * entry_order, - NMDedupMultiHeadEntry * head_existing, - const NMDedupMultiEntry **out_entry, - const NMDedupMultiObj ** out_obj_old) -{ - NMDedupMultiHeadEntry *head_entry; - const NMDedupMultiObj *obj_new, *obj_old; - gboolean add_head_entry = FALSE; - - nm_assert(self); - ASSERT_idx_type(idx_type); - nm_assert(obj); - nm_assert(NM_IN_SET(mode, - NM_DEDUP_MULTI_IDX_MODE_PREPEND, - NM_DEDUP_MULTI_IDX_MODE_PREPEND_FORCE, - NM_DEDUP_MULTI_IDX_MODE_APPEND, - NM_DEDUP_MULTI_IDX_MODE_APPEND_FORCE)); - nm_assert(!head_existing || head_existing->idx_type == idx_type); - nm_assert(({ - const NMDedupMultiHeadEntry *_h; - gboolean _ok = TRUE; - if (head_existing) { - _h = nm_dedup_multi_index_lookup_head(self, idx_type, obj); - if (head_existing == NM_DEDUP_MULTI_HEAD_ENTRY_MISSING) - _ok = (_h == NULL); - else - _ok = (_h == head_existing); - } - _ok; - })); - - if (entry) { - gboolean changed = FALSE; - - nm_dedup_multi_entry_set_dirty(entry, FALSE); - - nm_assert(!head_existing || entry->head == head_existing); - nm_assert(!entry_order || entry_order->head == entry->head); - nm_assert(!entry_order || c_list_contains(&entry->lst_entries, &entry_order->lst_entries)); - nm_assert(!entry_order || c_list_contains(&entry_order->lst_entries, &entry->lst_entries)); - - switch (mode) { - case NM_DEDUP_MULTI_IDX_MODE_PREPEND_FORCE: - if (entry_order) { - if (nm_c_list_move_before((CList *) &entry_order->lst_entries, &entry->lst_entries)) - changed = TRUE; - } else { - if (nm_c_list_move_front((CList *) &entry->head->lst_entries_head, - &entry->lst_entries)) - changed = TRUE; - } - break; - case NM_DEDUP_MULTI_IDX_MODE_APPEND_FORCE: - if (entry_order) { - if (nm_c_list_move_after((CList *) &entry_order->lst_entries, &entry->lst_entries)) - changed = TRUE; - } else { - if (nm_c_list_move_tail((CList *) &entry->head->lst_entries_head, - &entry->lst_entries)) - changed = TRUE; - } - break; - case NM_DEDUP_MULTI_IDX_MODE_PREPEND: - case NM_DEDUP_MULTI_IDX_MODE_APPEND: - break; - }; - - nm_assert(obj->klass == ((const NMDedupMultiObj *) entry->obj)->klass); - if (obj == entry->obj || obj->klass->obj_full_equal(obj, entry->obj)) { - NM_SET_OUT(out_entry, entry); - NM_SET_OUT(out_obj_old, nm_dedup_multi_obj_ref(entry->obj)); - return changed; - } - - obj_new = nm_dedup_multi_index_obj_intern(self, obj); - - obj_old = entry->obj; - entry->obj = obj_new; - - NM_SET_OUT(out_entry, entry); - if (out_obj_old) - *out_obj_old = obj_old; - else - nm_dedup_multi_obj_unref(obj_old); - return TRUE; - } - - if (idx_type->klass->idx_obj_partitionable - && !idx_type->klass->idx_obj_partitionable(idx_type, obj)) { - /* this object cannot be partitioned by this idx_type. */ - nm_assert(!head_existing || head_existing == NM_DEDUP_MULTI_HEAD_ENTRY_MISSING); - NM_SET_OUT(out_entry, NULL); - NM_SET_OUT(out_obj_old, NULL); - return FALSE; - } - - obj_new = nm_dedup_multi_index_obj_intern(self, obj); - - if (!head_existing) - head_entry = _entry_lookup_head(self, idx_type, obj_new); - else if (head_existing == NM_DEDUP_MULTI_HEAD_ENTRY_MISSING) - head_entry = NULL; - else - head_entry = head_existing; - - if (!head_entry) { - head_entry = g_slice_new0(NMDedupMultiHeadEntry); - head_entry->is_head = TRUE; - head_entry->idx_type = idx_type; - c_list_init(&head_entry->lst_entries_head); - c_list_link_tail(&idx_type->lst_idx_head, &head_entry->lst_idx); - add_head_entry = TRUE; - } else - nm_assert(c_list_contains(&idx_type->lst_idx_head, &head_entry->lst_idx)); - - if (entry_order) { - nm_assert(!add_head_entry); - nm_assert(entry_order->head == head_entry); - nm_assert(c_list_contains(&head_entry->lst_entries_head, &entry_order->lst_entries)); - nm_assert(c_list_contains(&entry_order->lst_entries, &head_entry->lst_entries_head)); - } - - entry = g_slice_new0(NMDedupMultiEntry); - entry->obj = obj_new; - entry->head = head_entry; - - switch (mode) { - case NM_DEDUP_MULTI_IDX_MODE_PREPEND: - case NM_DEDUP_MULTI_IDX_MODE_PREPEND_FORCE: - if (entry_order) - c_list_link_before((CList *) &entry_order->lst_entries, &entry->lst_entries); - else - c_list_link_front(&head_entry->lst_entries_head, &entry->lst_entries); - break; - default: - if (entry_order) - c_list_link_after((CList *) &entry_order->lst_entries, &entry->lst_entries); - else - c_list_link_tail(&head_entry->lst_entries_head, &entry->lst_entries); - break; - }; - - idx_type->len++; - head_entry->len++; - - if (add_head_entry && !g_hash_table_add(self->idx_entries, head_entry)) - nm_assert_not_reached(); - - if (!g_hash_table_add(self->idx_entries, entry)) - nm_assert_not_reached(); - - NM_SET_OUT(out_entry, entry); - NM_SET_OUT(out_obj_old, NULL); - return TRUE; -} - -gboolean -nm_dedup_multi_index_add(NMDedupMultiIndex * self, - NMDedupMultiIdxType * idx_type, - /*const NMDedupMultiObj * */ gconstpointer obj, - NMDedupMultiIdxMode mode, - const NMDedupMultiEntry ** out_entry, - /* const NMDedupMultiObj ** */ gpointer out_obj_old) -{ - NMDedupMultiEntry *entry; - - g_return_val_if_fail(self, FALSE); - g_return_val_if_fail(idx_type, FALSE); - g_return_val_if_fail(obj, FALSE); - g_return_val_if_fail(NM_IN_SET(mode, - NM_DEDUP_MULTI_IDX_MODE_PREPEND, - NM_DEDUP_MULTI_IDX_MODE_PREPEND_FORCE, - NM_DEDUP_MULTI_IDX_MODE_APPEND, - NM_DEDUP_MULTI_IDX_MODE_APPEND_FORCE), - FALSE); - - entry = _entry_lookup_obj(self, idx_type, obj); - return _add(self, idx_type, obj, entry, mode, NULL, NULL, out_entry, out_obj_old); -} - -/* nm_dedup_multi_index_add_full: - * @self: the index instance. - * @idx_type: the index handle for storing @obj. - * @obj: the NMDedupMultiObj instance to add. - * @mode: whether to append or prepend the new item. If @entry_order is given, - * the entry will be sorted after/before, instead of appending/prepending to - * the entire list. If a comparable object is already tracked, then it may - * still be resorted by specifying one of the "FORCE" modes. - * @entry_order: if not NULL, the new entry will be sorted before or after @entry_order. - * If given, @entry_order MUST be tracked by @self, and the object it points to MUST - * be in the same partition tracked by @idx_type. That is, they must have the same - * head_entry and it means, you must ensure that @entry_order and the created/modified - * entry will share the same head. - * @entry_existing: if not NULL, it safes a hash lookup of the entry where the - * object will be placed in. You can omit this, and it will be automatically - * detected (at the expense of an additional hash lookup). - * Basically, this is the result of nm_dedup_multi_index_lookup_obj(), - * with the peculiarity that if you know that @obj is not yet tracked, - * you may specify %NM_DEDUP_MULTI_ENTRY_MISSING. - * @head_existing: an optional argument to safe a lookup for the head. If specified, - * it must be identical to nm_dedup_multi_index_lookup_head(), with the peculiarity - * that if the head is not yet tracked, you may specify %NM_DEDUP_MULTI_HEAD_ENTRY_MISSING - * @out_entry: if give, return the added entry. This entry may have already exists (update) - * or be newly created. If @obj is not partitionable according to @idx_type, @obj - * is not to be added and it returns %NULL. - * @out_obj_old: if given, return the previously contained object. It only - * returns a object, if a matching entry was tracked previously, not if a - * new entry was created. Note that when passing @out_obj_old you obtain a reference - * to the boxed object and MUST return it with nm_dedup_multi_obj_unref(). - * - * Adds and object to the index. - * - * Return: %TRUE if anything changed, %FALSE if nothing changed. - */ -gboolean -nm_dedup_multi_index_add_full(NMDedupMultiIndex * self, - NMDedupMultiIdxType * idx_type, - /*const NMDedupMultiObj * */ gconstpointer obj, - NMDedupMultiIdxMode mode, - const NMDedupMultiEntry * entry_order, - const NMDedupMultiEntry * entry_existing, - const NMDedupMultiHeadEntry * head_existing, - const NMDedupMultiEntry ** out_entry, - /* const NMDedupMultiObj ** */ gpointer out_obj_old) -{ - NMDedupMultiEntry *entry; - - g_return_val_if_fail(self, FALSE); - g_return_val_if_fail(idx_type, FALSE); - g_return_val_if_fail(obj, FALSE); - g_return_val_if_fail(NM_IN_SET(mode, - NM_DEDUP_MULTI_IDX_MODE_PREPEND, - NM_DEDUP_MULTI_IDX_MODE_PREPEND_FORCE, - NM_DEDUP_MULTI_IDX_MODE_APPEND, - NM_DEDUP_MULTI_IDX_MODE_APPEND_FORCE), - FALSE); - - if (entry_existing == NULL) - entry = _entry_lookup_obj(self, idx_type, obj); - else if (entry_existing == NM_DEDUP_MULTI_ENTRY_MISSING) { - nm_assert(!_entry_lookup_obj(self, idx_type, obj)); - entry = NULL; - } else { - nm_assert(entry_existing == _entry_lookup_obj(self, idx_type, obj)); - entry = (NMDedupMultiEntry *) entry_existing; - } - return _add(self, - idx_type, - obj, - entry, - mode, - entry_order, - (NMDedupMultiHeadEntry *) head_existing, - out_entry, - out_obj_old); -} - -/*****************************************************************************/ - -static void -_remove_entry(NMDedupMultiIndex *self, NMDedupMultiEntry *entry, gboolean *out_head_entry_removed) -{ - const NMDedupMultiObj *obj; - NMDedupMultiHeadEntry *head_entry; - NMDedupMultiIdxType * idx_type; - - nm_assert(self); - nm_assert(entry); - nm_assert(entry->obj); - nm_assert(entry->head); - nm_assert(!c_list_is_empty(&entry->lst_entries)); - nm_assert(g_hash_table_lookup(self->idx_entries, entry) == entry); - - head_entry = (NMDedupMultiHeadEntry *) entry->head; - obj = entry->obj; - - nm_assert(head_entry); - nm_assert(head_entry->len > 0); - nm_assert(g_hash_table_lookup(self->idx_entries, head_entry) == head_entry); - - idx_type = (NMDedupMultiIdxType *) head_entry->idx_type; - ASSERT_idx_type(idx_type); - - nm_assert(idx_type->len >= head_entry->len); - if (--head_entry->len > 0) { - nm_assert(idx_type->len > 1); - idx_type->len--; - head_entry = NULL; - } - - NM_SET_OUT(out_head_entry_removed, head_entry != NULL); - - if (!g_hash_table_remove(self->idx_entries, entry)) - nm_assert_not_reached(); - - if (head_entry && !g_hash_table_remove(self->idx_entries, head_entry)) - nm_assert_not_reached(); - - c_list_unlink_stale(&entry->lst_entries); - g_slice_free(NMDedupMultiEntry, entry); - - if (head_entry) { - nm_assert(c_list_is_empty(&head_entry->lst_entries_head)); - c_list_unlink_stale(&head_entry->lst_idx); - g_slice_free(NMDedupMultiHeadEntry, head_entry); - } - - nm_dedup_multi_obj_unref(obj); -} - -static guint -_remove_head(NMDedupMultiIndex * self, - NMDedupMultiHeadEntry *head_entry, - gboolean remove_all /* otherwise just dirty ones */, - gboolean mark_survivors_dirty) -{ - guint n; - gboolean head_entry_removed; - CList * iter_entry, *iter_entry_safe; - - nm_assert(self); - nm_assert(head_entry); - nm_assert(head_entry->len > 0); - nm_assert(head_entry->len == c_list_length(&head_entry->lst_entries_head)); - nm_assert(g_hash_table_lookup(self->idx_entries, head_entry) == head_entry); - - n = 0; - c_list_for_each_safe (iter_entry, iter_entry_safe, &head_entry->lst_entries_head) { - NMDedupMultiEntry *entry; - - entry = c_list_entry(iter_entry, NMDedupMultiEntry, lst_entries); - if (remove_all || entry->dirty) { - _remove_entry(self, entry, &head_entry_removed); - n++; - if (head_entry_removed) - break; - } else if (mark_survivors_dirty) - nm_dedup_multi_entry_set_dirty(entry, TRUE); - } - - return n; -} - -static guint -_remove_idx_entry(NMDedupMultiIndex * self, - NMDedupMultiIdxType *idx_type, - gboolean remove_all /* otherwise just dirty ones */, - gboolean mark_survivors_dirty) -{ - guint n; - CList *iter_idx, *iter_idx_safe; - - nm_assert(self); - ASSERT_idx_type(idx_type); - - n = 0; - c_list_for_each_safe (iter_idx, iter_idx_safe, &idx_type->lst_idx_head) { - n += _remove_head(self, - c_list_entry(iter_idx, NMDedupMultiHeadEntry, lst_idx), - remove_all, - mark_survivors_dirty); - } - return n; -} - -guint -nm_dedup_multi_index_remove_entry(NMDedupMultiIndex *self, gconstpointer entry) -{ - g_return_val_if_fail(self, 0); - - nm_assert(entry); - - if (!((NMDedupMultiEntry *) entry)->is_head) { - _remove_entry(self, (NMDedupMultiEntry *) entry, NULL); - return 1; - } - return _remove_head(self, (NMDedupMultiHeadEntry *) entry, TRUE, FALSE); -} - -guint -nm_dedup_multi_index_remove_obj(NMDedupMultiIndex * self, - NMDedupMultiIdxType * idx_type, - /*const NMDedupMultiObj * */ gconstpointer obj, - /*const NMDedupMultiObj ** */ gconstpointer *out_obj) -{ - const NMDedupMultiEntry *entry; - - entry = nm_dedup_multi_index_lookup_obj(self, idx_type, obj); - if (!entry) { - NM_SET_OUT(out_obj, NULL); - return 0; - } - - /* since we are about to remove the object, we obviously pass - * a reference to @out_obj, the caller MUST unref the object, - * if he chooses to provide @out_obj. */ - NM_SET_OUT(out_obj, nm_dedup_multi_obj_ref(entry->obj)); - - _remove_entry(self, (NMDedupMultiEntry *) entry, NULL); - return 1; -} - -guint -nm_dedup_multi_index_remove_head(NMDedupMultiIndex * self, - NMDedupMultiIdxType * idx_type, - /*const NMDedupMultiObj * */ gconstpointer obj) -{ - const NMDedupMultiHeadEntry *entry; - - entry = nm_dedup_multi_index_lookup_head(self, idx_type, obj); - return entry ? _remove_head(self, (NMDedupMultiHeadEntry *) entry, TRUE, FALSE) : 0; -} - -guint -nm_dedup_multi_index_remove_idx(NMDedupMultiIndex *self, NMDedupMultiIdxType *idx_type) -{ - g_return_val_if_fail(self, 0); - g_return_val_if_fail(idx_type, 0); - - return _remove_idx_entry(self, idx_type, TRUE, FALSE); -} - -/*****************************************************************************/ - -/** - * nm_dedup_multi_index_lookup_obj: - * @self: the index cache - * @idx_type: the lookup index type - * @obj: the object to lookup. This means the match is performed - * according to NMDedupMultiIdxTypeClass's idx_obj_id_equal() - * of @idx_type. - * - * Returns: the cache entry or %NULL if the entry wasn't found. - */ -const NMDedupMultiEntry * -nm_dedup_multi_index_lookup_obj(const NMDedupMultiIndex * self, - const NMDedupMultiIdxType * idx_type, - /*const NMDedupMultiObj * */ gconstpointer obj) -{ - g_return_val_if_fail(self, FALSE); - g_return_val_if_fail(idx_type, FALSE); - g_return_val_if_fail(obj, FALSE); - - nm_assert(idx_type && idx_type->klass); - return _entry_lookup_obj(self, idx_type, obj); -} - -/** - * nm_dedup_multi_index_lookup_head: - * @self: the index cache - * @idx_type: the lookup index type - * @obj: the object to lookup, of type "const NMDedupMultiObj *". - * Depending on the idx_type, you *must* also provide a selector - * object, even when looking up the list head. That is, because - * the idx_type implementation may choose to partition the objects - * in distinct list, so you need a selector object to know which - * list head to lookup. - * - * Returns: the cache entry or %NULL if the entry wasn't found. - */ -const NMDedupMultiHeadEntry * -nm_dedup_multi_index_lookup_head(const NMDedupMultiIndex * self, - const NMDedupMultiIdxType * idx_type, - /*const NMDedupMultiObj * */ gconstpointer obj) -{ - g_return_val_if_fail(self, FALSE); - g_return_val_if_fail(idx_type, FALSE); - - return _entry_lookup_head(self, idx_type, obj); -} - -/*****************************************************************************/ - -void -nm_dedup_multi_index_dirty_set_head(NMDedupMultiIndex * self, - const NMDedupMultiIdxType * idx_type, - /*const NMDedupMultiObj * */ gconstpointer obj) -{ - NMDedupMultiHeadEntry *head_entry; - CList * iter_entry; - - g_return_if_fail(self); - g_return_if_fail(idx_type); - - head_entry = _entry_lookup_head(self, idx_type, obj); - if (!head_entry) - return; - - c_list_for_each (iter_entry, &head_entry->lst_entries_head) { - NMDedupMultiEntry *entry; - - entry = c_list_entry(iter_entry, NMDedupMultiEntry, lst_entries); - nm_dedup_multi_entry_set_dirty(entry, TRUE); - } -} - -void -nm_dedup_multi_index_dirty_set_idx(NMDedupMultiIndex *self, const NMDedupMultiIdxType *idx_type) -{ - CList *iter_idx, *iter_entry; - - g_return_if_fail(self); - g_return_if_fail(idx_type); - - c_list_for_each (iter_idx, &idx_type->lst_idx_head) { - NMDedupMultiHeadEntry *head_entry; - - head_entry = c_list_entry(iter_idx, NMDedupMultiHeadEntry, lst_idx); - c_list_for_each (iter_entry, &head_entry->lst_entries_head) { - NMDedupMultiEntry *entry; - - entry = c_list_entry(iter_entry, NMDedupMultiEntry, lst_entries); - nm_dedup_multi_entry_set_dirty(entry, TRUE); - } - } -} - -/** - * nm_dedup_multi_index_dirty_remove_idx: - * @self: the index instance - * @idx_type: the index-type to select the objects. - * @mark_survivors_dirty: while the function removes all entries that are - * marked as dirty, if @set_dirty is true, the surviving objects - * will be marked dirty right away. - * - * Deletes all entries for @idx_type that are marked dirty. Only - * non-dirty objects survive. If @mark_survivors_dirty is set to TRUE, the survivors - * are marked as dirty right away. - * - * Returns: number of deleted entries. - */ -guint -nm_dedup_multi_index_dirty_remove_idx(NMDedupMultiIndex * self, - NMDedupMultiIdxType *idx_type, - gboolean mark_survivors_dirty) -{ - g_return_val_if_fail(self, 0); - g_return_val_if_fail(idx_type, 0); - - return _remove_idx_entry(self, idx_type, FALSE, mark_survivors_dirty); -} - -/*****************************************************************************/ - -static guint -_dict_idx_objs_hash(const NMDedupMultiObj *obj) -{ - NMHashState h; - - nm_hash_init(&h, 1748638583u); - obj->klass->obj_full_hash_update(obj, &h); - return nm_hash_complete(&h); -} - -static gboolean -_dict_idx_objs_equal(const NMDedupMultiObj *obj_a, const NMDedupMultiObj *obj_b) -{ - return obj_a == obj_b - || (obj_a->klass == obj_b->klass && obj_a->klass->obj_full_equal(obj_a, obj_b)); -} - -void -nm_dedup_multi_index_obj_release(NMDedupMultiIndex * self, - /* const NMDedupMultiObj * */ gconstpointer obj) -{ - nm_assert(self); - nm_assert(obj); - nm_assert(g_hash_table_lookup(self->idx_objs, obj) == obj); - nm_assert(((const NMDedupMultiObj *) obj)->_multi_idx == self); - - ((NMDedupMultiObj *) obj)->_multi_idx = NULL; - if (!g_hash_table_remove(self->idx_objs, obj)) - nm_assert_not_reached(); -} - -gconstpointer -nm_dedup_multi_index_obj_find(NMDedupMultiIndex * self, - /* const NMDedupMultiObj * */ gconstpointer obj) -{ - g_return_val_if_fail(self, NULL); - g_return_val_if_fail(obj, NULL); - - return g_hash_table_lookup(self->idx_objs, obj); -} - -gconstpointer -nm_dedup_multi_index_obj_intern(NMDedupMultiIndex * self, - /* const NMDedupMultiObj * */ gconstpointer obj) -{ - const NMDedupMultiObj *obj_new = obj; - const NMDedupMultiObj *obj_old; - - nm_assert(self); - nm_assert(obj_new); - - if (obj_new->_multi_idx == self) { - nm_assert(g_hash_table_lookup(self->idx_objs, obj_new) == obj_new); - nm_dedup_multi_obj_ref(obj_new); - return obj_new; - } - - obj_old = g_hash_table_lookup(self->idx_objs, obj_new); - nm_assert(obj_old != obj_new); - - if (obj_old) { - nm_assert(obj_old->_multi_idx == self); - nm_dedup_multi_obj_ref(obj_old); - return obj_old; - } - - if (nm_dedup_multi_obj_needs_clone(obj_new)) - obj_new = nm_dedup_multi_obj_clone(obj_new); - else - obj_new = nm_dedup_multi_obj_ref(obj_new); - - nm_assert(obj_new); - nm_assert(!obj_new->_multi_idx); - - if (!g_hash_table_add(self->idx_objs, (gpointer) obj_new)) - nm_assert_not_reached(); - - ((NMDedupMultiObj *) obj_new)->_multi_idx = self; - return obj_new; -} - -void -nm_dedup_multi_obj_unref(const NMDedupMultiObj *obj) -{ - if (obj) { - nm_assert(obj->_ref_count > 0); - nm_assert(obj->_ref_count != NM_OBJ_REF_COUNT_STACKINIT); - -again: - if (--(((NMDedupMultiObj *) obj)->_ref_count) <= 0) { - if (obj->_multi_idx) { - /* restore the ref-count to 1 and release the object first - * from the index. Then, retry again to unref. */ - ((NMDedupMultiObj *) obj)->_ref_count++; - nm_dedup_multi_index_obj_release(obj->_multi_idx, obj); - nm_assert(obj->_ref_count == 1); - nm_assert(!obj->_multi_idx); - goto again; - } - - obj->klass->obj_destroy((NMDedupMultiObj *) obj); - } - } -} - -gboolean -nm_dedup_multi_obj_needs_clone(const NMDedupMultiObj *obj) -{ - nm_assert(obj); - - if (obj->_multi_idx || obj->_ref_count == NM_OBJ_REF_COUNT_STACKINIT) - return TRUE; - - if (obj->klass->obj_needs_clone && obj->klass->obj_needs_clone(obj)) - return TRUE; - - return FALSE; -} - -const NMDedupMultiObj * -nm_dedup_multi_obj_clone(const NMDedupMultiObj *obj) -{ - const NMDedupMultiObj *o; - - nm_assert(obj); - - o = obj->klass->obj_clone(obj); - nm_assert(o); - nm_assert(o->_ref_count == 1); - return o; -} - -gconstpointer * -nm_dedup_multi_objs_to_array_head(const NMDedupMultiHeadEntry * head_entry, - NMDedupMultiFcnSelectPredicate predicate, - gpointer user_data, - guint * out_len) -{ - gconstpointer *result; - CList * iter; - guint i; - - if (!head_entry) { - NM_SET_OUT(out_len, 0); - return NULL; - } - - result = g_new(gconstpointer, head_entry->len + 1); - i = 0; - c_list_for_each (iter, &head_entry->lst_entries_head) { - const NMDedupMultiObj *obj = c_list_entry(iter, NMDedupMultiEntry, lst_entries)->obj; - - if (!predicate || predicate(obj, user_data)) { - nm_assert(i < head_entry->len); - result[i++] = obj; - } - } - - if (i == 0) { - g_free(result); - NM_SET_OUT(out_len, 0); - return NULL; - } - - nm_assert(i <= head_entry->len); - NM_SET_OUT(out_len, i); - result[i++] = NULL; - return result; -} - -GPtrArray * -nm_dedup_multi_objs_to_ptr_array_head(const NMDedupMultiHeadEntry * head_entry, - NMDedupMultiFcnSelectPredicate predicate, - gpointer user_data) -{ - GPtrArray *result; - CList * iter; - - if (!head_entry) - return NULL; - - result = g_ptr_array_new_full(head_entry->len, (GDestroyNotify) nm_dedup_multi_obj_unref); - c_list_for_each (iter, &head_entry->lst_entries_head) { - const NMDedupMultiObj *obj = c_list_entry(iter, NMDedupMultiEntry, lst_entries)->obj; - - if (!predicate || predicate(obj, user_data)) - g_ptr_array_add(result, (gpointer) nm_dedup_multi_obj_ref(obj)); - } - - if (result->len == 0) { - g_ptr_array_unref(result); - return NULL; - } - return result; -} - -/** - * nm_dedup_multi_entry_reorder: - * @entry: the entry to reorder. It must not be NULL (and tracked in an index). - * @entry_order: (allow-none): an optional other entry. It MUST be in the same - * list as entry. If given, @entry will be ordered after/before @entry_order. - * If left at %NULL, @entry will be moved to the front/end of the list. - * @order_after: if @entry_order is given, %TRUE means to move @entry after - * @entry_order (otherwise before). - * If @entry_order is %NULL, %TRUE means to move @entry to the tail of the list - * (otherwise the beginning). Note that "tail of the list" here means that @entry - * will be linked before the head of the circular list. - * - * Returns: %TRUE, if anything was changed. Otherwise, @entry was already at the - * right place and nothing was done. - */ -gboolean -nm_dedup_multi_entry_reorder(const NMDedupMultiEntry *entry, - const NMDedupMultiEntry *entry_order, - gboolean order_after) -{ - nm_assert(entry); - - if (!entry_order) { - const NMDedupMultiHeadEntry *head_entry = entry->head; - - if (order_after) { - if (nm_c_list_move_tail((CList *) &head_entry->lst_entries_head, - (CList *) &entry->lst_entries)) - return TRUE; - } else { - if (nm_c_list_move_front((CList *) &head_entry->lst_entries_head, - (CList *) &entry->lst_entries)) - return TRUE; - } - } else { - if (order_after) { - if (nm_c_list_move_after((CList *) &entry_order->lst_entries, - (CList *) &entry->lst_entries)) - return TRUE; - } else { - if (nm_c_list_move_before((CList *) &entry_order->lst_entries, - (CList *) &entry->lst_entries)) - return TRUE; - } - } - - return FALSE; -} - -/*****************************************************************************/ - -NMDedupMultiIndex * -nm_dedup_multi_index_new(void) -{ - NMDedupMultiIndex *self; - - self = g_slice_new0(NMDedupMultiIndex); - self->ref_count = 1; - self->idx_entries = - g_hash_table_new((GHashFunc) _dict_idx_entries_hash, (GEqualFunc) _dict_idx_entries_equal); - self->idx_objs = - g_hash_table_new((GHashFunc) _dict_idx_objs_hash, (GEqualFunc) _dict_idx_objs_equal); - return self; -} - -NMDedupMultiIndex * -nm_dedup_multi_index_ref(NMDedupMultiIndex *self) -{ - g_return_val_if_fail(self, NULL); - g_return_val_if_fail(self->ref_count > 0, NULL); - - self->ref_count++; - return self; -} - -NMDedupMultiIndex * -nm_dedup_multi_index_unref(NMDedupMultiIndex *self) -{ - GHashTableIter iter; - const NMDedupMultiIdxType *idx_type; - NMDedupMultiEntry * entry; - const NMDedupMultiObj * obj; - - g_return_val_if_fail(self, NULL); - g_return_val_if_fail(self->ref_count > 0, NULL); - - if (--self->ref_count > 0) - return NULL; - -more: - g_hash_table_iter_init(&iter, self->idx_entries); - while (g_hash_table_iter_next(&iter, (gpointer *) &entry, NULL)) { - if (entry->is_head) - idx_type = ((NMDedupMultiHeadEntry *) entry)->idx_type; - else - idx_type = entry->head->idx_type; - _remove_idx_entry(self, (NMDedupMultiIdxType *) idx_type, TRUE, FALSE); - goto more; - } - - nm_assert(g_hash_table_size(self->idx_entries) == 0); - - g_hash_table_iter_init(&iter, self->idx_objs); - while (g_hash_table_iter_next(&iter, (gpointer *) &obj, NULL)) { - nm_assert(obj->_multi_idx == self); - ((NMDedupMultiObj *) obj)->_multi_idx = NULL; - } - g_hash_table_remove_all(self->idx_objs); - - g_hash_table_unref(self->idx_entries); - g_hash_table_unref(self->idx_objs); - - g_slice_free(NMDedupMultiIndex, self); - return NULL; -} diff --git a/shared/nm-glib-aux/nm-dedup-multi.h b/shared/nm-glib-aux/nm-dedup-multi.h deleted file mode 100644 index 1c0761bf..00000000 --- a/shared/nm-glib-aux/nm-dedup-multi.h +++ /dev/null @@ -1,407 +0,0 @@ -/* SPDX-License-Identifier: LGPL-2.1-or-later */ -/* - * Copyright (C) 2017 Red Hat, Inc. - */ - -#ifndef __NM_DEDUP_MULTI_H__ -#define __NM_DEDUP_MULTI_H__ - -#include "nm-obj.h" -#include "nm-std-aux/c-list-util.h" - -/*****************************************************************************/ - -struct _NMHashState; - -typedef struct _NMDedupMultiObj NMDedupMultiObj; -typedef struct _NMDedupMultiObjClass NMDedupMultiObjClass; -typedef struct _NMDedupMultiIdxType NMDedupMultiIdxType; -typedef struct _NMDedupMultiIdxTypeClass NMDedupMultiIdxTypeClass; -typedef struct _NMDedupMultiEntry NMDedupMultiEntry; -typedef struct _NMDedupMultiHeadEntry NMDedupMultiHeadEntry; -typedef struct _NMDedupMultiIndex NMDedupMultiIndex; - -typedef enum _NMDedupMultiIdxMode { - NM_DEDUP_MULTI_IDX_MODE_PREPEND, - - NM_DEDUP_MULTI_IDX_MODE_PREPEND_FORCE, - - /* append new objects to the end of the list. - * If the object is already in the cache, don't move it. */ - NM_DEDUP_MULTI_IDX_MODE_APPEND, - - /* like NM_DEDUP_MULTI_IDX_MODE_APPEND, but if the object - * is already in the cache, move it to the end. */ - NM_DEDUP_MULTI_IDX_MODE_APPEND_FORCE, -} NMDedupMultiIdxMode; - -/*****************************************************************************/ - -struct _NMDedupMultiObj { - union { - NMObjBaseInst parent; - const NMDedupMultiObjClass *klass; - }; - NMDedupMultiIndex *_multi_idx; - guint _ref_count; -}; - -struct _NMDedupMultiObjClass { - NMObjBaseClass parent; - - const NMDedupMultiObj *(*obj_clone)(const NMDedupMultiObj *obj); - - gboolean (*obj_needs_clone)(const NMDedupMultiObj *obj); - - void (*obj_destroy)(NMDedupMultiObj *obj); - - /* the NMDedupMultiObj can be deduplicated. For that the obj_full_hash_update() - * and obj_full_equal() compare *all* fields of the object, even minor ones. */ - void (*obj_full_hash_update)(const NMDedupMultiObj *obj, struct _NMHashState *h); - gboolean (*obj_full_equal)(const NMDedupMultiObj *obj_a, const NMDedupMultiObj *obj_b); -}; - -/*****************************************************************************/ - -static inline const NMDedupMultiObj * -nm_dedup_multi_obj_ref(const NMDedupMultiObj *obj) -{ - /* ref and unref accept const pointers. Objects is supposed to be shared - * and kept immutable. Disallowing to take/return a reference to a const - * NMPObject is cumbersome, because callers are precisely expected to - * keep a ref on the otherwise immutable object. */ - - nm_assert(obj); - nm_assert(obj->_ref_count != NM_OBJ_REF_COUNT_STACKINIT); - nm_assert(obj->_ref_count > 0); - - ((NMDedupMultiObj *) obj)->_ref_count++; - return obj; -} - -void nm_dedup_multi_obj_unref(const NMDedupMultiObj *obj); -const NMDedupMultiObj *nm_dedup_multi_obj_clone(const NMDedupMultiObj *obj); -gboolean nm_dedup_multi_obj_needs_clone(const NMDedupMultiObj *obj); - -gconstpointer nm_dedup_multi_index_obj_intern(NMDedupMultiIndex * self, - /* const NMDedupMultiObj * */ gconstpointer obj); - -void nm_dedup_multi_index_obj_release(NMDedupMultiIndex * self, - /* const NMDedupMultiObj * */ gconstpointer obj); - -/* const NMDedupMultiObj * */ gconstpointer -nm_dedup_multi_index_obj_find(NMDedupMultiIndex * self, - /* const NMDedupMultiObj * */ gconstpointer obj); - -/*****************************************************************************/ - -/* the NMDedupMultiIdxType is an access handle under which you can store and - * retrieve NMDedupMultiObj instances in NMDedupMultiIndex. - * - * The NMDedupMultiIdxTypeClass determines its behavior, but you can have - * multiple instances (of the same class). - * - * For example, NMIP4Config can have idx-type to put there all IPv4 Routes. - * This idx-type instance is private to the NMIP4Config instance. Basically, - * the NMIP4Config instance uses the idx-type to maintain an ordered list - * of routes in NMDedupMultiIndex. - * - * However, a NMDedupMultiIdxType may also partition the set of objects - * in multiple distinct lists. NMIP4Config doesn't do that (because instead - * of creating one idx-type for IPv4 and IPv6 routes, it just cretaes - * to distinct idx-types, one for each address family. - * This partitioning is used by NMPlatform to maintain a lookup index for - * routes by ifindex. As the ifindex is dynamic, it does not create an - * idx-type instance for each ifindex. Instead, it has one idx-type for - * all routes. But whenever accessing NMDedupMultiIndex with an NMDedupMultiObj, - * the partitioning NMDedupMultiIdxType takes into account the NMDedupMultiObj - * instance to associate it with the right list. - * - * Hence, a NMDedupMultiIdxEntry has a list of possibly multiple NMDedupMultiHeadEntry - * instances, which each is the head for a list of NMDedupMultiEntry instances. - * In the platform example, the NMDedupMultiHeadEntry partition the indexed objects - * by their ifindex. */ -struct _NMDedupMultiIdxType { - union { - NMObjBaseInst parent; - const NMDedupMultiIdxTypeClass *klass; - }; - - CList lst_idx_head; - - guint len; -}; - -void nm_dedup_multi_idx_type_init(NMDedupMultiIdxType * idx_type, - const NMDedupMultiIdxTypeClass *klass); - -struct _NMDedupMultiIdxTypeClass { - NMObjBaseClass parent; - - void (*idx_obj_id_hash_update)(const NMDedupMultiIdxType *idx_type, - const NMDedupMultiObj * obj, - struct _NMHashState * h); - gboolean (*idx_obj_id_equal)(const NMDedupMultiIdxType *idx_type, - const NMDedupMultiObj * obj_a, - const NMDedupMultiObj * obj_b); - - /* an NMDedupMultiIdxTypeClass which implements partitioning of the - * tracked objects, must implement the idx_obj_partition*() functions. - * - * idx_obj_partitionable() may return NULL if the object cannot be tracked. - * For example, a index for routes by ifindex, may not want to track any - * routes that don't have a valid ifindex. If the idx-type says that the - * object is not partitionable, it is never added to the NMDedupMultiIndex. */ - gboolean (*idx_obj_partitionable)(const NMDedupMultiIdxType *idx_type, - const NMDedupMultiObj * obj); - void (*idx_obj_partition_hash_update)(const NMDedupMultiIdxType *idx_type, - const NMDedupMultiObj * obj, - struct _NMHashState * h); - gboolean (*idx_obj_partition_equal)(const NMDedupMultiIdxType *idx_type, - const NMDedupMultiObj * obj_a, - const NMDedupMultiObj * obj_b); -}; - -static inline gboolean -nm_dedup_multi_idx_type_id_equal(const NMDedupMultiIdxType * idx_type, - /* const NMDedupMultiObj * */ gconstpointer obj_a, - /* const NMDedupMultiObj * */ gconstpointer obj_b) -{ - nm_assert(idx_type); - return obj_a == obj_b || idx_type->klass->idx_obj_id_equal(idx_type, obj_a, obj_b); -} - -static inline gboolean -nm_dedup_multi_idx_type_partition_equal(const NMDedupMultiIdxType * idx_type, - /* const NMDedupMultiObj * */ gconstpointer obj_a, - /* const NMDedupMultiObj * */ gconstpointer obj_b) -{ - nm_assert(idx_type); - if (idx_type->klass->idx_obj_partition_equal) { - nm_assert(obj_a); - nm_assert(obj_b); - return obj_a == obj_b || idx_type->klass->idx_obj_partition_equal(idx_type, obj_a, obj_b); - } - return TRUE; -} - -/*****************************************************************************/ - -struct _NMDedupMultiEntry { - /* this is the list of all entries that share the same head entry. - * All entries compare equal according to idx_obj_partition_equal(). */ - CList lst_entries; - - /* const NMDedupMultiObj * */ gconstpointer obj; - - bool is_head; - bool dirty; - - const NMDedupMultiHeadEntry *head; -}; - -struct _NMDedupMultiHeadEntry { - /* this is the list of all entries that share the same head entry. - * All entries compare equal according to idx_obj_partition_equal(). */ - CList lst_entries_head; - - const NMDedupMultiIdxType *idx_type; - - bool is_head; - - guint len; - - CList lst_idx; -}; - -/*****************************************************************************/ - -static inline gconstpointer -nm_dedup_multi_entry_get_obj(const NMDedupMultiEntry *entry) -{ - /* convenience method that allows to skip the %NULL check on - * @entry. Think of the NULL-conditional operator ?. of C# */ - return entry ? entry->obj : NULL; -} - -/*****************************************************************************/ - -static inline void -nm_dedup_multi_entry_set_dirty(const NMDedupMultiEntry *entry, gboolean dirty) -{ - /* NMDedupMultiEntry is always exposed as a const object, because it is not - * supposed to be modified outside NMDedupMultiIndex API. Except the "dirty" - * flag. In C++ speak, it is a mutable field. - * - * Add this inline function, to cast-away constness and set the dirty flag. */ - nm_assert(entry); - ((NMDedupMultiEntry *) entry)->dirty = dirty; -} - -/*****************************************************************************/ - -NMDedupMultiIndex *nm_dedup_multi_index_new(void); -NMDedupMultiIndex *nm_dedup_multi_index_ref(NMDedupMultiIndex *self); -NMDedupMultiIndex *nm_dedup_multi_index_unref(NMDedupMultiIndex *self); - -static inline void -_nm_auto_unref_dedup_multi_index(NMDedupMultiIndex **v) -{ - if (*v) - nm_dedup_multi_index_unref(*v); -} -#define nm_auto_unref_dedup_multi_index nm_auto(_nm_auto_unref_dedup_multi_index) - -#define NM_DEDUP_MULTI_ENTRY_MISSING ((const NMDedupMultiEntry *) GUINT_TO_POINTER(1)) -#define NM_DEDUP_MULTI_HEAD_ENTRY_MISSING ((const NMDedupMultiHeadEntry *) GUINT_TO_POINTER(1)) - -gboolean nm_dedup_multi_index_add_full(NMDedupMultiIndex * self, - NMDedupMultiIdxType * idx_type, - /*const NMDedupMultiObj * */ gconstpointer obj, - NMDedupMultiIdxMode mode, - const NMDedupMultiEntry * entry_order, - const NMDedupMultiEntry * entry_existing, - const NMDedupMultiHeadEntry * head_existing, - const NMDedupMultiEntry ** out_entry, - /* const NMDedupMultiObj ** */ gpointer out_obj_old); - -gboolean nm_dedup_multi_index_add(NMDedupMultiIndex * self, - NMDedupMultiIdxType * idx_type, - /*const NMDedupMultiObj * */ gconstpointer obj, - NMDedupMultiIdxMode mode, - const NMDedupMultiEntry ** out_entry, - /* const NMDedupMultiObj ** */ gpointer out_obj_old); - -const NMDedupMultiEntry * -nm_dedup_multi_index_lookup_obj(const NMDedupMultiIndex * self, - const NMDedupMultiIdxType * idx_type, - /*const NMDedupMultiObj * */ gconstpointer obj); - -const NMDedupMultiHeadEntry * -nm_dedup_multi_index_lookup_head(const NMDedupMultiIndex * self, - const NMDedupMultiIdxType * idx_type, - /*const NMDedupMultiObj * */ gconstpointer obj); - -guint nm_dedup_multi_index_remove_entry(NMDedupMultiIndex *self, gconstpointer entry); - -guint nm_dedup_multi_index_remove_obj(NMDedupMultiIndex * self, - NMDedupMultiIdxType * idx_type, - /*const NMDedupMultiObj * */ gconstpointer obj, - /*const NMDedupMultiObj ** */ gconstpointer *out_obj); - -guint nm_dedup_multi_index_remove_head(NMDedupMultiIndex * self, - NMDedupMultiIdxType * idx_type, - /*const NMDedupMultiObj * */ gconstpointer obj); - -guint nm_dedup_multi_index_remove_idx(NMDedupMultiIndex *self, NMDedupMultiIdxType *idx_type); - -void nm_dedup_multi_index_dirty_set_head(NMDedupMultiIndex * self, - const NMDedupMultiIdxType * idx_type, - /*const NMDedupMultiObj * */ gconstpointer obj); - -void nm_dedup_multi_index_dirty_set_idx(NMDedupMultiIndex * self, - const NMDedupMultiIdxType *idx_type); - -guint nm_dedup_multi_index_dirty_remove_idx(NMDedupMultiIndex * self, - NMDedupMultiIdxType *idx_type, - gboolean mark_survivors_dirty); - -/*****************************************************************************/ - -typedef struct _NMDedupMultiIter { - const CList * _head; - const CList * _next; - const NMDedupMultiEntry *current; -} NMDedupMultiIter; - -static inline void -nm_dedup_multi_iter_init(NMDedupMultiIter *iter, const NMDedupMultiHeadEntry *head) -{ - g_return_if_fail(iter); - - if (head && !c_list_is_empty(&head->lst_entries_head)) { - iter->_head = &head->lst_entries_head; - iter->_next = head->lst_entries_head.next; - } else { - iter->_head = NULL; - iter->_next = NULL; - } - iter->current = NULL; -} - -static inline gboolean -nm_dedup_multi_iter_next(NMDedupMultiIter *iter) -{ - g_return_val_if_fail(iter, FALSE); - - if (!iter->_next) - return FALSE; - - /* we always look ahead for the next. This way, the user - * may delete the current entry (but no other entries). */ - iter->current = c_list_entry(iter->_next, NMDedupMultiEntry, lst_entries); - if (iter->_next->next == iter->_head) - iter->_next = NULL; - else - iter->_next = iter->_next->next; - return TRUE; -} - -#define nm_dedup_multi_iter_for_each(iter, head_entry) \ - for (nm_dedup_multi_iter_init((iter), (head_entry)); nm_dedup_multi_iter_next((iter));) - -/*****************************************************************************/ - -typedef gboolean (*NMDedupMultiFcnSelectPredicate)(/* const NMDedupMultiObj * */ gconstpointer obj, - gpointer user_data); - -gconstpointer *nm_dedup_multi_objs_to_array_head(const NMDedupMultiHeadEntry * head_entry, - NMDedupMultiFcnSelectPredicate predicate, - gpointer user_data, - guint * out_len); -GPtrArray * nm_dedup_multi_objs_to_ptr_array_head(const NMDedupMultiHeadEntry * head_entry, - NMDedupMultiFcnSelectPredicate predicate, - gpointer user_data); - -static inline const NMDedupMultiEntry * -nm_dedup_multi_head_entry_get_idx(const NMDedupMultiHeadEntry *head_entry, int idx) -{ - CList *iter; - - if (head_entry) { - if (idx >= 0) { - c_list_for_each (iter, &head_entry->lst_entries_head) { - if (idx-- == 0) - return c_list_entry(iter, NMDedupMultiEntry, lst_entries); - } - } else { - for (iter = head_entry->lst_entries_head.prev; iter != &head_entry->lst_entries_head; - iter = iter->prev) { - if (++idx == 0) - return c_list_entry(iter, NMDedupMultiEntry, lst_entries); - } - } - } - return NULL; -} - -static inline void -nm_dedup_multi_head_entry_sort(const NMDedupMultiHeadEntry *head_entry, - CListSortCmp cmp, - gconstpointer user_data) -{ - if (head_entry) { - /* the head entry can be sorted directly without messing up the - * index to which it belongs. Of course, this does mess up any - * NMDedupMultiIter instances. */ - c_list_sort((CList *) &head_entry->lst_entries_head, cmp, user_data); - } -} - -gboolean nm_dedup_multi_entry_reorder(const NMDedupMultiEntry *entry, - const NMDedupMultiEntry *entry_order, - gboolean order_after); - -/*****************************************************************************/ - -#endif /* __NM_DEDUP_MULTI_H__ */ diff --git a/shared/nm-glib-aux/nm-default-glib-i18n-lib.h b/shared/nm-glib-aux/nm-default-glib-i18n-lib.h deleted file mode 100644 index 9393d192..00000000 --- a/shared/nm-glib-aux/nm-default-glib-i18n-lib.h +++ /dev/null @@ -1,21 +0,0 @@ -/* SPDX-License-Identifier: LGPL-2.1-or-later */ -/* - * Copyright (C) 2015 Red Hat, Inc. - */ - -#ifndef __NM_DEFAULT_GLIB_I18N_LIB_H__ -#define __NM_DEFAULT_GLIB_I18N_LIB_H__ - -/*****************************************************************************/ - -#define _NETWORKMANAGER_COMPILATION_GLIB_I18N_LIB - -#include "nm-glib-aux/nm-default-glib.h" - -#undef NETWORKMANAGER_COMPILATION -#define NETWORKMANAGER_COMPILATION \ - (NM_NETWORKMANAGER_COMPILATION_GLIB | NM_NETWORKMANAGER_COMPILATION_WITH_GLIB_I18N_LIB) - -/*****************************************************************************/ - -#endif /* __NM_DEFAULT_GLIB_I18N_LIB_H__ */ diff --git a/shared/nm-glib-aux/nm-default-glib-i18n-prog.h b/shared/nm-glib-aux/nm-default-glib-i18n-prog.h deleted file mode 100644 index 0abe807b..00000000 --- a/shared/nm-glib-aux/nm-default-glib-i18n-prog.h +++ /dev/null @@ -1,21 +0,0 @@ -/* SPDX-License-Identifier: LGPL-2.1-or-later */ -/* - * Copyright (C) 2015 Red Hat, Inc. - */ - -#ifndef __NM_DEFAULT_GLIB_I18N_PROG_H__ -#define __NM_DEFAULT_GLIB_I18N_PROG_H__ - -/*****************************************************************************/ - -#define _NETWORKMANAGER_COMPILATION_GLIB_I18N_PROG - -#include "nm-glib-aux/nm-default-glib.h" - -#undef NETWORKMANAGER_COMPILATION -#define NETWORKMANAGER_COMPILATION \ - (NM_NETWORKMANAGER_COMPILATION_GLIB | NM_NETWORKMANAGER_COMPILATION_WITH_GLIB_I18N_PROG) - -/*****************************************************************************/ - -#endif /* __NM_DEFAULT_GLIB_I18N_PROG_H__ */ diff --git a/shared/nm-glib-aux/nm-default-glib.h b/shared/nm-glib-aux/nm-default-glib.h deleted file mode 100644 index 34b23f77..00000000 --- a/shared/nm-glib-aux/nm-default-glib.h +++ /dev/null @@ -1,75 +0,0 @@ -/* SPDX-License-Identifier: LGPL-2.1-or-later */ -/* - * Copyright (C) 2015 Red Hat, Inc. - */ - -#ifndef __NM_DEFAULT_GLIB_H__ -#define __NM_DEFAULT_GLIB_H__ - -/*****************************************************************************/ - -#include "nm-std-aux/nm-default-std.h" - -#undef NETWORKMANAGER_COMPILATION -#define NETWORKMANAGER_COMPILATION NM_NETWORKMANAGER_COMPILATION_WITH_GLIB - -/*****************************************************************************/ - -#include <glib.h> - -#if defined(_NETWORKMANAGER_COMPILATION_GLIB_I18N_PROG) - #if defined(_NETWORKMANAGER_COMPILATION_GLIB_I18N_LIB) - #error Cannot define _NETWORKMANAGER_COMPILATION_GLIB_I18N_LIB and _NETWORKMANAGER_COMPILATION_GLIB_I18N_PROG together - #endif - #undef _NETWORKMANAGER_COMPILATION_GLIB_I18N_PROG - #include <glib/gi18n.h> -#elif defined(_NETWORKMANAGER_COMPILATION_GLIB_I18N_LIB) - #undef _NETWORKMANAGER_COMPILATION_GLIB_I18N_LIB - #include <glib/gi18n-lib.h> -#endif - -/*****************************************************************************/ - -#if NM_MORE_ASSERTS == 0 - #ifndef G_DISABLE_CAST_CHECKS - /* Unless compiling with G_DISABLE_CAST_CHECKS, glib performs type checking - * during G_VARIANT_TYPE() via g_variant_type_checked_(). This is not necessary - * because commonly this cast is needed during something like - * - * g_variant_builder_init (&props, G_VARIANT_TYPE ("a{sv}")); - * - * Note that in if the variant type would be invalid, the check still - * wouldn't make the buggy code magically work. Instead of passing a - * bogus type string (bad), it would pass %NULL to g_variant_builder_init() - * (also bad). - * - * Also, a function like g_variant_builder_init() already validates - * the input type via something like - * - * g_return_if_fail (g_variant_type_is_container (type)); - * - * So, by having G_VARIANT_TYPE() also validate the type, we validate - * twice, whereas the first validation is rather pointless because it - * doesn't prevent the function to be called with invalid arguments. - * - * Just patch G_VARIANT_TYPE() to perform no check. - */ - #undef G_VARIANT_TYPE - #define G_VARIANT_TYPE(type_string) ((const GVariantType *) (type_string)) - #endif -#endif - -/*****************************************************************************/ - -#include "nm-gassert-patch.h" - -#include "nm-std-aux/nm-std-aux.h" -#include "nm-std-aux/nm-std-utils.h" -#include "nm-glib-aux/nm-macros-internal.h" -#include "nm-glib-aux/nm-shared-utils.h" -#include "nm-glib-aux/nm-errno.h" -#include "nm-glib-aux/nm-hash-utils.h" - -/*****************************************************************************/ - -#endif /* __NM_DEFAULT_GLIB_H__ */ diff --git a/shared/nm-glib-aux/nm-enum-utils.c b/shared/nm-glib-aux/nm-enum-utils.c deleted file mode 100644 index b06f2bb2..00000000 --- a/shared/nm-glib-aux/nm-enum-utils.c +++ /dev/null @@ -1,368 +0,0 @@ -/* SPDX-License-Identifier: LGPL-2.1-or-later */ -/* - * Copyright (C) 2017 Red Hat, Inc. - */ - -#include "nm-glib-aux/nm-default-glib-i18n-lib.h" - -#include "nm-enum-utils.h" -#include "nm-str-buf.h" - -/*****************************************************************************/ - -#define IS_FLAGS_SEPARATOR(ch) (NM_IN_SET((ch), ' ', '\t', ',', '\n', '\r')) - -static void -_ASSERT_enum_values_info(GType type, const NMUtilsEnumValueInfo *value_infos) -{ -#if NM_MORE_ASSERTS > 5 - nm_auto_unref_gtypeclass GTypeClass *klass = NULL; - gs_unref_hashtable GHashTable *ht = NULL; - - klass = g_type_class_ref(type); - - g_assert(G_IS_ENUM_CLASS(klass) || G_IS_FLAGS_CLASS(klass)); - - if (!value_infos) - return; - - ht = g_hash_table_new(g_str_hash, g_str_equal); - - for (; value_infos->nick; value_infos++) { - g_assert(value_infos->nick[0]); - - /* duplicate nicks make no sense!! */ - g_assert(!g_hash_table_contains(ht, value_infos->nick)); - g_hash_table_add(ht, (gpointer) value_infos->nick); - - if (G_IS_ENUM_CLASS(klass)) { - GEnumValue *enum_value; - - enum_value = g_enum_get_value_by_nick(G_ENUM_CLASS(klass), value_infos->nick); - if (enum_value) { - /* we do allow specifying the same name via @value_infos and @type. - * That might make sense, if @type comes from a library where older versions - * of the library don't yet support the value. In this case, the caller can - * provide the nick via @value_infos, to support the older library version. - * And then, when actually running against a newer library version where - * @type knows the nick, we have this situation. - * - * Another reason for specifying a nick both in @value_infos and @type, - * is to specify an alias which is not used with highest preference. For - * example, if you add an alias "disabled" for "none" (both numerically - * equal), then the first alias in @value_infos will be preferred over - * the name from @type. So, to still use "none" as preferred name, you may - * explicitly specify the "none" alias in @value_infos before "disabled". - * - * However, what never is allowed, is to use a name (nick) to re-number - * the value. That is, if both @value_infos and @type contain a particular - * nick, their numeric values must agree as well. - * Allowing this, would be very confusing, because the name would have a different - * value from the regular GLib GEnum API. - */ - g_assert(enum_value->value == value_infos->value); - } - } else { - GFlagsValue *flags_value; - - flags_value = g_flags_get_value_by_nick(G_FLAGS_CLASS(klass), value_infos->nick); - if (flags_value) { - /* see ENUM case above. */ - g_assert(flags_value->value == (guint) value_infos->value); - } - } - } -#endif -} - -static gboolean -_is_hex_string(const char *str, gboolean allow_sign) -{ - if (allow_sign && str[0] == '-') - str++; - return str[0] == '0' && str[1] == 'x' && str[2] - && NM_STRCHAR_ALL(&str[2], ch, g_ascii_isxdigit(ch)); -} - -static gboolean -_is_dec_string(const char *str, gboolean allow_sign) -{ - if (allow_sign && str[0] == '-') - str++; - return str[0] && NM_STRCHAR_ALL(&str[0], ch, g_ascii_isdigit(ch)); -} - -static gboolean -_enum_is_valid_enum_nick(const char *str) -{ - return str[0] && !NM_STRCHAR_ANY(str, ch, g_ascii_isspace(ch)) && !_is_dec_string(str, TRUE) - && !_is_hex_string(str, TRUE); -} - -static gboolean -_enum_is_valid_flags_nick(const char *str) -{ - return str[0] && !NM_STRCHAR_ANY(str, ch, IS_FLAGS_SEPARATOR(ch)) && !_is_dec_string(str, FALSE) - && !_is_hex_string(str, FALSE); -} - -char * -_nm_utils_enum_to_str_full(GType type, - int value, - const char * flags_separator, - const NMUtilsEnumValueInfo *value_infos) -{ - nm_auto_unref_gtypeclass GTypeClass *klass = NULL; - - _ASSERT_enum_values_info(type, value_infos); - - if (flags_separator - && (!flags_separator[0] || NM_STRCHAR_ANY(flags_separator, ch, !IS_FLAGS_SEPARATOR(ch)))) - g_return_val_if_reached(NULL); - - klass = g_type_class_ref(type); - - if (G_IS_ENUM_CLASS(klass)) { - GEnumValue *enum_value; - - for (; value_infos && value_infos->nick; value_infos++) { - if (value_infos->value == value) - return g_strdup(value_infos->nick); - } - - enum_value = g_enum_get_value(G_ENUM_CLASS(klass), value); - if (!enum_value || !_enum_is_valid_enum_nick(enum_value->value_nick)) - return g_strdup_printf("%d", value); - else - return g_strdup(enum_value->value_nick); - } else if (G_IS_FLAGS_CLASS(klass)) { - unsigned uvalue = (unsigned) value; - GFlagsValue *flags_value; - NMStrBuf strbuf; - - flags_separator = flags_separator ?: " "; - - nm_str_buf_init(&strbuf, 16, FALSE); - - for (; value_infos && value_infos->nick; value_infos++) { - nm_assert(_enum_is_valid_flags_nick(value_infos->nick)); - - if (uvalue == 0) { - if (value_infos->value != 0) - continue; - } else { - if (!NM_FLAGS_ALL(uvalue, (unsigned) value_infos->value)) - continue; - } - - if (strbuf.len) - nm_str_buf_append(&strbuf, flags_separator); - nm_str_buf_append(&strbuf, value_infos->nick); - uvalue &= ~((unsigned) value_infos->value); - if (uvalue == 0) { - /* we printed all flags. Done. */ - goto flags_done; - } - } - - do { - flags_value = g_flags_get_first_value(G_FLAGS_CLASS(klass), uvalue); - if (strbuf.len) - nm_str_buf_append(&strbuf, flags_separator); - if (!flags_value || !_enum_is_valid_flags_nick(flags_value->value_nick)) { - if (uvalue) - nm_str_buf_append_printf(&strbuf, "0x%x", uvalue); - break; - } - nm_str_buf_append(&strbuf, flags_value->value_nick); - uvalue &= ~flags_value->value; - } while (uvalue); - -flags_done: - return nm_str_buf_finalize(&strbuf, NULL); - } - - g_return_val_if_reached(NULL); -} - -static const NMUtilsEnumValueInfo * -_find_value_info(const NMUtilsEnumValueInfo *value_infos, const char *needle) -{ - if (value_infos) { - for (; value_infos->nick; value_infos++) { - if (nm_streq(needle, value_infos->nick)) - return value_infos; - } - } - return NULL; -} - -gboolean -_nm_utils_enum_from_str_full(GType type, - const char * str, - int * out_value, - char ** err_token, - const NMUtilsEnumValueInfo *value_infos) -{ - nm_auto_unref_gtypeclass GTypeClass *klass = NULL; - gboolean ret = FALSE; - int value = 0; - gs_free char * str_clone = NULL; - char * s; - gint64 v64; - const NMUtilsEnumValueInfo * nick; - - g_return_val_if_fail(str, FALSE); - - _ASSERT_enum_values_info(type, value_infos); - - s = nm_strdup_maybe_a(300, nm_str_skip_leading_spaces(str), &str_clone); - g_strchomp(s); - - klass = g_type_class_ref(type); - - if (G_IS_ENUM_CLASS(klass)) { - GEnumValue *enum_value; - - G_STATIC_ASSERT(G_MAXINT < G_MAXINT64); - G_STATIC_ASSERT(G_MININT > G_MININT64); - - if (s[0]) { - if (_is_hex_string(s, TRUE)) { - if (s[0] == '-') { - v64 = _nm_utils_ascii_str_to_int64(&s[3], - 16, - -((gint64) G_MAXINT), - -((gint64) G_MININT), - G_MAXINT64); - if (v64 != G_MAXINT64) { - value = (int) (-v64); - ret = TRUE; - } - } else { - v64 = _nm_utils_ascii_str_to_int64(&s[2], 16, G_MININT, G_MAXINT, G_MAXINT64); - if (v64 != G_MAXINT64) { - value = (int) v64; - ret = TRUE; - } - } - } else if (_is_dec_string(s, TRUE)) { - v64 = _nm_utils_ascii_str_to_int64(s, 10, G_MININT, G_MAXINT, G_MAXINT64); - if (v64 != G_MAXINT64) { - value = (int) v64; - ret = TRUE; - } - } else if ((nick = _find_value_info(value_infos, s))) { - value = nick->value; - ret = TRUE; - } else if ((enum_value = g_enum_get_value_by_nick(G_ENUM_CLASS(klass), s))) { - value = enum_value->value; - ret = TRUE; - } - } - } else if (G_IS_FLAGS_CLASS(klass)) { - GFlagsValue *flags_value; - unsigned uvalue = 0; - - ret = TRUE; - while (s[0]) { - char *s_end; - - for (s_end = s; s_end[0]; s_end++) { - if (IS_FLAGS_SEPARATOR(s_end[0])) { - s_end[0] = '\0'; - s_end++; - break; - } - } - - if (s[0]) { - if (_is_hex_string(s, FALSE)) { - v64 = _nm_utils_ascii_str_to_int64(&s[2], 16, 0, G_MAXUINT, -1); - if (v64 == -1) { - ret = FALSE; - break; - } - uvalue |= (unsigned) v64; - } else if (_is_dec_string(s, FALSE)) { - v64 = _nm_utils_ascii_str_to_int64(s, 10, 0, G_MAXUINT, -1); - if (v64 == -1) { - ret = FALSE; - break; - } - uvalue |= (unsigned) v64; - } else if ((nick = _find_value_info(value_infos, s))) - uvalue |= (unsigned) nick->value; - else if ((flags_value = g_flags_get_value_by_nick(G_FLAGS_CLASS(klass), s))) - uvalue |= flags_value->value; - else { - ret = FALSE; - break; - } - } - - s = s_end; - } - - value = (int) uvalue; - } else - g_return_val_if_reached(FALSE); - - NM_SET_OUT(err_token, !ret && s[0] ? g_strdup(s) : NULL); - NM_SET_OUT(out_value, ret ? value : 0); - return ret; -} - -const char ** -_nm_utils_enum_get_values(GType type, int from, int to) -{ - GTypeClass *klass; - GPtrArray * array; - int i; - char sbuf[64]; - - klass = g_type_class_ref(type); - array = g_ptr_array_new(); - - if (G_IS_ENUM_CLASS(klass)) { - GEnumClass *enum_class = G_ENUM_CLASS(klass); - GEnumValue *enum_value; - - for (i = 0; i < enum_class->n_values; i++) { - enum_value = &enum_class->values[i]; - if (enum_value->value >= from && enum_value->value <= to) { - if (_enum_is_valid_enum_nick(enum_value->value_nick)) - g_ptr_array_add(array, (gpointer) enum_value->value_nick); - else - g_ptr_array_add( - array, - (gpointer) g_intern_string(nm_sprintf_buf(sbuf, "%d", enum_value->value))); - } - } - } else if (G_IS_FLAGS_CLASS(klass)) { - GFlagsClass *flags_class = G_FLAGS_CLASS(klass); - GFlagsValue *flags_value; - - for (i = 0; i < flags_class->n_values; i++) { - flags_value = &flags_class->values[i]; - if (flags_value->value >= (guint) from && flags_value->value <= (guint) to) { - if (_enum_is_valid_flags_nick(flags_value->value_nick)) - g_ptr_array_add(array, (gpointer) flags_value->value_nick); - else - g_ptr_array_add( - array, - (gpointer) g_intern_string( - nm_sprintf_buf(sbuf, "0x%x", (unsigned) flags_value->value))); - } - } - } else { - g_type_class_unref(klass); - g_ptr_array_free(array, TRUE); - g_return_val_if_reached(NULL); - } - - g_type_class_unref(klass); - g_ptr_array_add(array, NULL); - - return (const char **) g_ptr_array_free(array, FALSE); -} diff --git a/shared/nm-glib-aux/nm-enum-utils.h b/shared/nm-glib-aux/nm-enum-utils.h deleted file mode 100644 index 89be54e7..00000000 --- a/shared/nm-glib-aux/nm-enum-utils.h +++ /dev/null @@ -1,32 +0,0 @@ -/* SPDX-License-Identifier: LGPL-2.1-or-later */ -/* - * Copyright (C) 2017 Red Hat, Inc. - */ - -#ifndef __NM_ENUM_UTILS_H__ -#define __NM_ENUM_UTILS_H__ - -/*****************************************************************************/ - -typedef struct _NMUtilsEnumValueInfo { - /* currently, this is only used for _nm_utils_enum_from_str_full() to - * declare additional aliases for values. */ - const char *nick; - int value; -} NMUtilsEnumValueInfo; - -char * _nm_utils_enum_to_str_full(GType type, - int value, - const char * sep, - const NMUtilsEnumValueInfo *value_infos); -gboolean _nm_utils_enum_from_str_full(GType type, - const char * str, - int * out_value, - char ** err_token, - const NMUtilsEnumValueInfo *value_infos); - -const char **_nm_utils_enum_get_values(GType type, int from, int to); - -/*****************************************************************************/ - -#endif /* __NM_ENUM_UTILS_H__ */ diff --git a/shared/nm-glib-aux/nm-errno.c b/shared/nm-glib-aux/nm-errno.c deleted file mode 100644 index c05379ec..00000000 --- a/shared/nm-glib-aux/nm-errno.c +++ /dev/null @@ -1,169 +0,0 @@ -/* SPDX-License-Identifier: LGPL-2.1-or-later */ -/* - * Copyright (C) 2018 Red Hat, Inc. - */ - -#include "nm-glib-aux/nm-default-glib-i18n-lib.h" - -#include "nm-errno.h" - -/*****************************************************************************/ - -static NM_UTILS_LOOKUP_STR_DEFINE( - _geterror, -#if 0 - enum _NMErrno, -#else - int, -#endif - NM_UTILS_LOOKUP_DEFAULT(NULL), - - NM_UTILS_LOOKUP_STR_ITEM(NME_ERRNO_SUCCESS, "NME_ERRNO_SUCCESS"), - NM_UTILS_LOOKUP_STR_ITEM(NME_ERRNO_OUT_OF_RANGE, "NME_ERRNO_OUT_OF_RANGE"), - - NM_UTILS_LOOKUP_STR_ITEM(NME_UNSPEC, "NME_UNSPEC"), - NM_UTILS_LOOKUP_STR_ITEM(NME_BUG, "NME_BUG"), - NM_UTILS_LOOKUP_STR_ITEM(NME_NATIVE_ERRNO, "NME_NATIVE_ERRNO"), - - NM_UTILS_LOOKUP_STR_ITEM(NME_NL_ATTRSIZE, "NME_NL_ATTRSIZE"), - NM_UTILS_LOOKUP_STR_ITEM(NME_NL_BAD_SOCK, "NME_NL_BAD_SOCK"), - NM_UTILS_LOOKUP_STR_ITEM(NME_NL_DUMP_INTR, "NME_NL_DUMP_INTR"), - NM_UTILS_LOOKUP_STR_ITEM(NME_NL_MSG_OVERFLOW, "NME_NL_MSG_OVERFLOW"), - NM_UTILS_LOOKUP_STR_ITEM(NME_NL_MSG_TOOSHORT, "NME_NL_MSG_TOOSHORT"), - NM_UTILS_LOOKUP_STR_ITEM(NME_NL_MSG_TRUNC, "NME_NL_MSG_TRUNC"), - NM_UTILS_LOOKUP_STR_ITEM(NME_NL_SEQ_MISMATCH, "NME_NL_SEQ_MISMATCH"), - NM_UTILS_LOOKUP_STR_ITEM(NME_NL_NOADDR, "NME_NL_NOADDR"), - - NM_UTILS_LOOKUP_STR_ITEM(NME_PL_NOT_FOUND, "not-found"), - NM_UTILS_LOOKUP_STR_ITEM(NME_PL_EXISTS, "exists"), - NM_UTILS_LOOKUP_STR_ITEM(NME_PL_WRONG_TYPE, "wrong-type"), - NM_UTILS_LOOKUP_STR_ITEM(NME_PL_NOT_SLAVE, "not-slave"), - NM_UTILS_LOOKUP_STR_ITEM(NME_PL_NO_FIRMWARE, "no-firmware"), - NM_UTILS_LOOKUP_STR_ITEM(NME_PL_OPNOTSUPP, "not-supported"), - NM_UTILS_LOOKUP_STR_ITEM(NME_PL_NETLINK, "netlink"), - NM_UTILS_LOOKUP_STR_ITEM(NME_PL_CANT_SET_MTU, "cant-set-mtu"), - - NM_UTILS_LOOKUP_ITEM_IGNORE(_NM_ERRNO_MININT), - NM_UTILS_LOOKUP_ITEM_IGNORE(_NM_ERRNO_RESERVED_LAST_PLUS_1), ); - -/** - * nm_strerror(): - * @nmerr: the NetworkManager specific errno to be converted - * to string. - * - * NetworkManager specific error numbers reserve a range in "errno.h" with - * our own defines. For numbers that don't fall into this range, the numbers - * are identical to the common error numbers. - * - * Identical to strerror(), g_strerror(), nm_strerror_native() for error numbers - * that are not in the reserved range of NetworkManager specific errors. - * - * Returns: (transfer none): the string representation of the error number. - */ -const char * -nm_strerror(int nmerr) -{ - const char *s; - - nmerr = nm_errno(nmerr); - - if (nmerr >= _NM_ERRNO_RESERVED_FIRST) { - s = _geterror(nmerr); - if (s) - return s; - } - return nm_strerror_native(nmerr); -} - -/*****************************************************************************/ - -/** - * nm_strerror_native_r: - * @errsv: the errno to convert to string. - * @buf: the output buffer where to write the string to. - * @buf_size: the length of buffer. - * - * This is like strerror_r(), with one difference: depending on the - * locale, the returned string is guaranteed to be valid UTF-8. - * Also, there is some confusion as to whether to use glibc's - * strerror_r() or the POXIX/XSI variant. This is abstracted - * by the function. - * - * Note that the returned buffer may also be a statically allocated - * buffer, and not the input buffer @buf. Consequently, the returned - * string may be longer than @buf_size. - * - * Returns: (transfer none): a NUL terminated error message. This is either a static - * string (that is never freed), or the provided @buf argument. - */ -const char * -nm_strerror_native_r(int errsv, char *buf, gsize buf_size) -{ - char *buf2; - - nm_assert(buf); - nm_assert(buf_size > 0); - -#if (!defined(__GLIBC__) && !defined(__UCLIBC__)) || ((_POSIX_C_SOURCE >= 200112L) && !_GNU_SOURCE) - /* XSI-compliant */ - { - int errno_saved = errno; - - if (strerror_r(errsv, buf, buf_size) != 0) { - g_snprintf(buf, buf_size, "Unspecified errno %d", errsv); - errno = errno_saved; - } - buf2 = buf; - } -#else - /* GNU-specific */ - buf2 = strerror_r(errsv, buf, buf_size); -#endif - - /* like g_strerror(), ensure that the error message is UTF-8. */ - if (!g_get_charset(NULL) && !g_utf8_validate(buf2, -1, NULL)) { - gs_free char *msg = NULL; - - msg = g_locale_to_utf8(buf2, -1, NULL, NULL, NULL); - if (msg) { - g_strlcpy(buf, msg, buf_size); - buf2 = buf; - } - } - - return buf2; -} - -/** - * nm_strerror_native: - * @errsv: the errno integer from <errno.h> - * - * Like strerror(), but strerror() is not thread-safe and not guaranteed - * to be UTF-8. - * - * g_strerror() is a thread-safe variant of strerror(), however it caches - * all returned strings in a dictionary. That means, using this on untrusted - * error numbers can result in this cache to grow without limits. - * - * Instead, return a tread-local buffer. This way, it's thread-safe. - * - * There is a downside to this: subsequent calls of nm_strerror_native() - * overwrite the error message. - * - * Returns: (transfer none): the text representation of the error number. - */ -const char * -nm_strerror_native(int errsv) -{ - static _nm_thread_local char *buf_static = NULL; - char * buf; - - buf = buf_static; - if (G_UNLIKELY(!buf)) { - buf = g_malloc(NM_STRERROR_BUFSIZE); - buf_static = buf; - nm_utils_thread_local_register_destroy(buf, g_free); - } - - return nm_strerror_native_r(errsv, buf, NM_STRERROR_BUFSIZE); -} diff --git a/shared/nm-glib-aux/nm-errno.h b/shared/nm-glib-aux/nm-errno.h deleted file mode 100644 index 62c8379f..00000000 --- a/shared/nm-glib-aux/nm-errno.h +++ /dev/null @@ -1,171 +0,0 @@ -/* SPDX-License-Identifier: LGPL-2.1-or-later */ -/* - * Copyright (C) 2018 Red Hat, Inc. - */ - -#ifndef __NM_ERRNO_H__ -#define __NM_ERRNO_H__ - -#include <errno.h> - -/*****************************************************************************/ - -enum _NMErrno { - _NM_ERRNO_MININT = G_MININT, - _NM_ERRNO_MAXINT = G_MAXINT, - _NM_ERRNO_RESERVED_FIRST = 100000, - - /* when we cannot represent a number as positive number, we resort to this - * number. Basically, the values G_MININT, -NME_ERRNO_SUCCESS, NME_ERRNO_SUCCESS - * and G_MAXINT all map to the same value. */ - NME_ERRNO_OUT_OF_RANGE = G_MAXINT, - - /* Indicate that the original errno was zero. Zero denotes *no error*, but we know something - * went wrong and we want to report some error. This is a placeholder to mean, something - * was wrong, but errno was zero. */ - NME_ERRNO_SUCCESS = G_MAXINT - 1, - - /* an unspecified error. */ - NME_UNSPEC = _NM_ERRNO_RESERVED_FIRST, - - /* A bug, for example when an assertion failed. - * Should never happen. */ - NME_BUG, - - /* a native error number (from <errno.h>) cannot be mapped as - * an nm-error, because it is in the range [_NM_ERRNO_RESERVED_FIRST, - * _NM_ERRNO_RESERVED_LAST]. */ - NME_NATIVE_ERRNO, - - /* netlink errors. */ - NME_NL_SEQ_MISMATCH, - NME_NL_MSG_TRUNC, - NME_NL_MSG_TOOSHORT, - NME_NL_DUMP_INTR, - NME_NL_ATTRSIZE, - NME_NL_BAD_SOCK, - NME_NL_NOADDR, - NME_NL_MSG_OVERFLOW, - - /* platform errors. */ - NME_PL_NOT_FOUND, - NME_PL_EXISTS, - NME_PL_WRONG_TYPE, - NME_PL_NOT_SLAVE, - NME_PL_NO_FIRMWARE, - NME_PL_OPNOTSUPP, - NME_PL_NETLINK, - NME_PL_CANT_SET_MTU, - - _NM_ERRNO_RESERVED_LAST_PLUS_1, - _NM_ERRNO_RESERVED_LAST = _NM_ERRNO_RESERVED_LAST_PLUS_1 - 1, -}; - -/*****************************************************************************/ - -/* When we receive an errno from a system function, we can safely assume - * that the error number is not negative. We rely on that, and possibly just - * "return -errsv;" to signal an error. We also rely on that, because libc - * is our trusted base: meaning, if it cannot even succeed at setting errno - * according to specification, all bets are off. - * - * This macro returns the input argument, and asserts that the error variable - * is positive. - * - * In a sense, the macro is related to nm_errno_native() function, but the difference - * is that this macro asserts that @errsv is positive, while nm_errno_native() coerces - * negative values to be non-negative. */ -#define NM_ERRNO_NATIVE(errsv) \ - ({ \ - const int _errsv_x = (errsv); \ - \ - nm_assert(_errsv_x > 0); \ - _errsv_x; \ - }) - -/* Normalize native errno. - * - * Our API may return native error codes (<errno.h>) as negative values. This function - * takes such an errno, and normalizes it to their positive value. - * - * The special values G_MININT and zero are coerced to NME_ERRNO_OUT_OF_RANGE and NME_ERRNO_SUCCESS - * respectively. - * Other values are coerced to their inverse. - * Other positive values are returned unchanged. - * - * Basically, this normalizes errsv to be positive (taking care of two pathological cases). - */ -static inline int -nm_errno_native(int errsv) -{ - switch (errsv) { - case 0: - return NME_ERRNO_SUCCESS; - case G_MININT: - return NME_ERRNO_OUT_OF_RANGE; - default: - return errsv >= 0 ? errsv : -errsv; - } -} - -/* Normalizes an nm-error to be positive. - * - * Various API returns negative error codes, and this function converts the negative - * value to its positive. - * - * Note that @nmerr is on the domain of NetworkManager specific error numbers, - * which is not the same as the native error numbers (errsv from <errno.h>). But - * as far as normalizing goes, nm_errno() does exactly the same remapping as - * nm_errno_native(). */ -static inline int -nm_errno(int nmerr) -{ - return nm_errno_native(nmerr); -} - -/* this maps a native errno to a (always non-negative) nm-error number. - * - * Note that nm-error numbers are embedded into the range of regular - * errno. The only difference is, that nm-error numbers reserve a - * range (_NM_ERRNO_RESERVED_FIRST, _NM_ERRNO_RESERVED_LAST) for their - * own purpose. - * - * That means, converting an errno to nm-error number means in - * most cases just returning itself. - * Only pathological cases need special handling: - * - * - 0 is mapped to NME_ERRNO_SUCCESS; - * - G_MININT is mapped to NME_ERRNO_OUT_OF_RANGE; - * - values in the range of (+/-) [_NM_ERRNO_RESERVED_FIRST, _NM_ERRNO_RESERVED_LAST] - * are mapped to NME_NATIVE_ERRNO - * - all other values are their (positive) absolute value. - */ -static inline int -nm_errno_from_native(int errsv) -{ - switch (errsv) { - case 0: - return NME_ERRNO_SUCCESS; - case G_MININT: - return NME_ERRNO_OUT_OF_RANGE; - default: - if (errsv < 0) - errsv = -errsv; - return G_UNLIKELY(errsv >= _NM_ERRNO_RESERVED_FIRST && errsv <= _NM_ERRNO_RESERVED_LAST) - ? NME_NATIVE_ERRNO - : errsv; - } -} - -const char *nm_strerror(int nmerr); - -/*****************************************************************************/ - -#define NM_STRERROR_BUFSIZE 1024 - -const char *nm_strerror_native_r(int errsv, char *buf, gsize buf_size); -const char *nm_strerror_native(int errsv); - -/*****************************************************************************/ - -#endif /* __NM_ERRNO_H__ */ diff --git a/shared/nm-glib-aux/nm-gassert-patch.h b/shared/nm-glib-aux/nm-gassert-patch.h deleted file mode 100644 index bac8697c..00000000 --- a/shared/nm-glib-aux/nm-gassert-patch.h +++ /dev/null @@ -1,76 +0,0 @@ -/* SPDX-License-Identifier: LGPL-2.1-or-later */ -/* - * Copyright (C) 2015 Red Hat, Inc. - */ - -#ifndef __NM_GASSERT_PATCH_H__ -#define __NM_GASSERT_PATCH_H__ - -/*****************************************************************************/ - -#if NM_MORE_ASSERTS == 0 - -/* glib assertions (g_return_*(), g_assert*()) contain a textual representation - * of the checked statement. This part of the assertion blows up the size of the - * binary. Unless we compile a debug-build with NM_MORE_ASSERTS, drop these - * parts. Note that the failed assertion still prints the file and line where the - * assertion fails. That shall suffice. */ - -static inline void -_nm_g_return_if_fail_warning(const char *log_domain, const char *file, int line) -{ - char file_buf[256 + 15]; - - g_snprintf(file_buf, sizeof(file_buf), "((%s:%d))", file, line); - g_return_if_fail_warning(log_domain, file_buf, "<dropped>"); -} - - #define g_return_if_fail_warning(log_domain, pretty_function, expression) \ - _nm_g_return_if_fail_warning(log_domain, __FILE__, __LINE__) - - #define g_assertion_message_expr(domain, file, line, func, expr) \ - g_assertion_message_expr(domain, file, line, "<unknown-fcn>", (expr) ? "<dropped>" : NULL) - - #undef g_return_val_if_reached - #define g_return_val_if_reached(val) \ - G_STMT_START \ - { \ - g_log(G_LOG_DOMAIN, \ - G_LOG_LEVEL_CRITICAL, \ - "file %s: line %d (%s): should not be reached", \ - __FILE__, \ - __LINE__, \ - "<dropped>"); \ - return (val); \ - } \ - G_STMT_END - - #undef g_return_if_reached - #define g_return_if_reached() \ - G_STMT_START \ - { \ - g_log(G_LOG_DOMAIN, \ - G_LOG_LEVEL_CRITICAL, \ - "file %s: line %d (%s): should not be reached", \ - __FILE__, \ - __LINE__, \ - "<dropped>"); \ - return; \ - } \ - G_STMT_END -#endif - -/*****************************************************************************/ - -#if NM_MORE_ASSERTS == 0 - #define NM_ASSERT_G_RETURN_EXPR(expr) "<dropped>" - #define NM_ASSERT_NO_MSG 1 - -#else - #define NM_ASSERT_G_RETURN_EXPR(expr) "" expr "" - #define NM_ASSERT_NO_MSG 0 -#endif - -/*****************************************************************************/ - -#endif /* __NM_GASSERT_PATCH_H__ */ diff --git a/shared/nm-glib-aux/nm-glib.h b/shared/nm-glib-aux/nm-glib.h deleted file mode 100644 index befb8d90..00000000 --- a/shared/nm-glib-aux/nm-glib.h +++ /dev/null @@ -1,707 +0,0 @@ -/* SPDX-License-Identifier: LGPL-2.1-or-later */ -/* - * Copyright (C) 2008 - 2018 Red Hat, Inc. - */ - -#ifndef __NM_GLIB_H__ -#define __NM_GLIB_H__ - -/*****************************************************************************/ - -#ifndef __NM_MACROS_INTERNAL_H__ - #error "nm-glib.h requires nm-macros-internal.h. Do not include this directly" -#endif - -/*****************************************************************************/ - -#ifdef __clang__ - - #undef G_GNUC_BEGIN_IGNORE_DEPRECATIONS - #undef G_GNUC_END_IGNORE_DEPRECATIONS - - #define G_GNUC_BEGIN_IGNORE_DEPRECATIONS \ - _Pragma("clang diagnostic push") \ - _Pragma("clang diagnostic ignored \"-Wdeprecated-declarations\"") - - #define G_GNUC_END_IGNORE_DEPRECATIONS _Pragma("clang diagnostic pop") - -#endif - -/*****************************************************************************/ - -static inline void -__g_type_ensure(GType type) -{ -#if !GLIB_CHECK_VERSION(2, 34, 0) - if (G_UNLIKELY(type == (GType) -1)) - g_error("can't happen"); -#else - G_GNUC_BEGIN_IGNORE_DEPRECATIONS; - g_type_ensure(type); - G_GNUC_END_IGNORE_DEPRECATIONS; -#endif -} -#define g_type_ensure __g_type_ensure - -/*****************************************************************************/ - -#if !GLIB_CHECK_VERSION(2, 34, 0) - - #define g_clear_pointer(pp, destroy) \ - G_STMT_START \ - { \ - G_STATIC_ASSERT(sizeof *(pp) == sizeof(gpointer)); \ - /* Only one access, please */ \ - gpointer *_pp = (gpointer *) (pp); \ - gpointer _p; \ - /* This assignment is needed to avoid a gcc warning */ \ - GDestroyNotify _destroy = (GDestroyNotify)(destroy); \ - \ - _p = *_pp; \ - if (_p) { \ - *_pp = NULL; \ - _destroy(_p); \ - } \ - } \ - G_STMT_END - -#endif - -/*****************************************************************************/ - -#if !GLIB_CHECK_VERSION(2, 34, 0) - - /* These are used to clean up the output of test programs; we can just let - * them no-op in older glib. - */ - #define g_test_expect_message(log_domain, log_level, pattern) - #define g_test_assert_expected_messages() - -#else - - /* We build with -DGLIB_MAX_ALLOWED_VERSION set to 2.32 to make sure we don't - * accidentally use new API that we shouldn't. But we don't want warnings for - * the APIs that we emulate above. - */ - - #define g_test_expect_message(domain, level, format...) \ - G_STMT_START \ - { \ - G_GNUC_BEGIN_IGNORE_DEPRECATIONS \ - g_test_expect_message(domain, level, format); \ - G_GNUC_END_IGNORE_DEPRECATIONS \ - } \ - G_STMT_END - - #define g_test_assert_expected_messages_internal(domain, file, line, func) \ - G_STMT_START \ - { \ - G_GNUC_BEGIN_IGNORE_DEPRECATIONS \ - g_test_assert_expected_messages_internal(domain, file, line, func); \ - G_GNUC_END_IGNORE_DEPRECATIONS \ - } \ - G_STMT_END - -#endif - -/*****************************************************************************/ - -#if GLIB_CHECK_VERSION(2, 35, 0) - /* For glib >= 2.36, g_type_init() is deprecated. - * But since 2.35.1 (7c42ab23b55c43ab96d0ac2124b550bf1f49c1ec) this function - * does nothing. Replace the call with empty statement. */ - #define nm_g_type_init() \ - G_STMT_START \ - { \ - (void) 0; \ - } \ - G_STMT_END -#else - #define nm_g_type_init() \ - G_STMT_START \ - { \ - g_type_init(); \ - } \ - G_STMT_END -#endif - -/*****************************************************************************/ - -/* g_test_initialized() is only available since glib 2.36. */ -#if !GLIB_CHECK_VERSION(2, 36, 0) - #define g_test_initialized() (g_test_config_vars->test_initialized) -#endif - -/*****************************************************************************/ - -/* g_assert_cmpmem() is only available since glib 2.46. */ -#if !GLIB_CHECK_VERSION(2, 45, 7) - #define g_assert_cmpmem(m1, l1, m2, l2) \ - G_STMT_START \ - { \ - gconstpointer __m1 = m1, __m2 = m2; \ - int __l1 = l1, __l2 = l2; \ - if (__l1 != __l2) \ - g_assertion_message_cmpnum(G_LOG_DOMAIN, \ - __FILE__, \ - __LINE__, \ - G_STRFUNC, \ - #l1 " (len(" #m1 ")) == " #l2 " (len(" #m2 "))", \ - __l1, \ - "==", \ - __l2, \ - 'i'); \ - else if (memcmp(__m1, __m2, __l1) != 0) \ - g_assertion_message(G_LOG_DOMAIN, \ - __FILE__, \ - __LINE__, \ - G_STRFUNC, \ - "assertion failed (" #m1 " == " #m2 ")"); \ - } \ - G_STMT_END -#endif - -/*****************************************************************************/ - -/* Rumtime check for glib version. First do a compile time check which - * (if satisfied) shortcuts the runtime check. */ -static inline gboolean -nm_glib_check_version(guint major, guint minor, guint micro) -{ - return GLIB_CHECK_VERSION(major, minor, micro) - || ((glib_major_version > major) - || (glib_major_version == major && glib_minor_version > minor) - || (glib_major_version == major && glib_minor_version == minor - && glib_micro_version < micro)); -} - -/*****************************************************************************/ - -/* g_test_skip() is only available since glib 2.38. Add a compatibility wrapper. */ -static inline void -__nmtst_g_test_skip(const char *msg) -{ -#if GLIB_CHECK_VERSION(2, 38, 0) - G_GNUC_BEGIN_IGNORE_DEPRECATIONS - g_test_skip(msg); - G_GNUC_END_IGNORE_DEPRECATIONS -#else - g_debug("%s", msg); -#endif -} -#define g_test_skip __nmtst_g_test_skip - -/*****************************************************************************/ - -/* g_test_add_data_func_full() is only available since glib 2.34. Add a compatibility wrapper. */ -static inline void -__g_test_add_data_func_full(const char * testpath, - gpointer test_data, - GTestDataFunc test_func, - GDestroyNotify data_free_func) -{ -#if GLIB_CHECK_VERSION(2, 34, 0) - G_GNUC_BEGIN_IGNORE_DEPRECATIONS - g_test_add_data_func_full(testpath, test_data, test_func, data_free_func); - G_GNUC_END_IGNORE_DEPRECATIONS -#else - g_return_if_fail(testpath != NULL); - g_return_if_fail(testpath[0] == '/'); - g_return_if_fail(test_func != NULL); - - g_test_add_vtable(testpath, - 0, - test_data, - NULL, - (GTestFixtureFunc) test_func, - (GTestFixtureFunc) data_free_func); -#endif -} -#define g_test_add_data_func_full __g_test_add_data_func_full - -/*****************************************************************************/ - -static inline gboolean -nm_g_hash_table_replace(GHashTable *hash, gpointer key, gpointer value) -{ - /* glib 2.40 added a return value indicating whether the key already existed - * (910191597a6c2e5d5d460e9ce9efb4f47d9cc63c). */ -#if GLIB_CHECK_VERSION(2, 40, 0) - return g_hash_table_replace(hash, key, value); -#else - gboolean contained = g_hash_table_contains(hash, key); - - g_hash_table_replace(hash, key, value); - return !contained; -#endif -} - -static inline gboolean -nm_g_hash_table_insert(GHashTable *hash, gpointer key, gpointer value) -{ - /* glib 2.40 added a return value indicating whether the key already existed - * (910191597a6c2e5d5d460e9ce9efb4f47d9cc63c). */ -#if GLIB_CHECK_VERSION(2, 40, 0) - return g_hash_table_insert(hash, key, value); -#else - gboolean contained = g_hash_table_contains(hash, key); - - g_hash_table_insert(hash, key, value); - return !contained; -#endif -} - -static inline gboolean -nm_g_hash_table_add(GHashTable *hash, gpointer key) -{ - /* glib 2.40 added a return value indicating whether the key already existed - * (910191597a6c2e5d5d460e9ce9efb4f47d9cc63c). */ -#if GLIB_CHECK_VERSION(2, 40, 0) - return g_hash_table_add(hash, key); -#else - gboolean contained = g_hash_table_contains(hash, key); - - g_hash_table_add(hash, key); - return !contained; -#endif -} - -/*****************************************************************************/ - -#if !GLIB_CHECK_VERSION(2, 40, 0) || defined(NM_GLIB_COMPAT_H_TEST) -static inline void -_nm_g_ptr_array_insert(GPtrArray *array, int index_, gpointer data) -{ - g_return_if_fail(array); - g_return_if_fail(index_ >= -1); - g_return_if_fail(index_ <= (int) array->len); - - g_ptr_array_add(array, data); - - if (index_ != -1 && index_ != (int) (array->len - 1)) { - memmove(&(array->pdata[index_ + 1]), - &(array->pdata[index_]), - (array->len - index_ - 1) * sizeof(gpointer)); - array->pdata[index_] = data; - } -} -#endif - -#if !GLIB_CHECK_VERSION(2, 40, 0) - #define g_ptr_array_insert(array, index, data) \ - G_STMT_START \ - { \ - _nm_g_ptr_array_insert(array, index, data); \ - } \ - G_STMT_END -#else - #define g_ptr_array_insert(array, index, data) \ - G_STMT_START \ - { \ - G_GNUC_BEGIN_IGNORE_DEPRECATIONS \ - g_ptr_array_insert(array, index, data); \ - G_GNUC_END_IGNORE_DEPRECATIONS \ - } \ - G_STMT_END -#endif - -/*****************************************************************************/ - -#if !GLIB_CHECK_VERSION(2, 40, 0) -static inline gboolean -_g_key_file_save_to_file(GKeyFile *key_file, const char *filename, GError **error) -{ - char * contents; - gboolean success; - gsize length; - - g_return_val_if_fail(key_file != NULL, FALSE); - g_return_val_if_fail(filename != NULL, FALSE); - g_return_val_if_fail(error == NULL || *error == NULL, FALSE); - - contents = g_key_file_to_data(key_file, &length, NULL); - g_assert(contents != NULL); - - success = g_file_set_contents(filename, contents, length, error); - g_free(contents); - - return success; -} - #define g_key_file_save_to_file(key_file, filename, error) \ - _g_key_file_save_to_file(key_file, filename, error) -#else - #define g_key_file_save_to_file(key_file, filename, error) \ - ({ \ - gboolean _success; \ - \ - G_GNUC_BEGIN_IGNORE_DEPRECATIONS \ - _success = g_key_file_save_to_file(key_file, filename, error); \ - G_GNUC_END_IGNORE_DEPRECATIONS \ - _success; \ - }) -#endif - -/*****************************************************************************/ - -#if GLIB_CHECK_VERSION(2, 36, 0) - #define g_credentials_get_unix_pid(creds, error) \ - ({ \ - G_GNUC_BEGIN_IGNORE_DEPRECATIONS(g_credentials_get_unix_pid)((creds), (error)); \ - G_GNUC_END_IGNORE_DEPRECATIONS \ - }) -#else - #define g_credentials_get_unix_pid(creds, error) \ - ({ \ - struct ucred *native_creds; \ - \ - native_creds = g_credentials_get_native((creds), G_CREDENTIALS_TYPE_LINUX_UCRED); \ - g_assert(native_creds); \ - native_creds->pid; \ - }) -#endif - -/*****************************************************************************/ - -#if !GLIB_CHECK_VERSION(2, 40, 0) || defined(NM_GLIB_COMPAT_H_TEST) -static inline gpointer * -_nm_g_hash_table_get_keys_as_array(GHashTable *hash_table, guint *length) -{ - GHashTableIter iter; - gpointer key, *ret; - guint i = 0; - - g_return_val_if_fail(hash_table, NULL); - - ret = g_new0(gpointer, g_hash_table_size(hash_table) + 1); - g_hash_table_iter_init(&iter, hash_table); - - while (g_hash_table_iter_next(&iter, &key, NULL)) - ret[i++] = key; - - ret[i] = NULL; - - if (length) - *length = i; - - return ret; -} -#endif -#if !GLIB_CHECK_VERSION(2, 40, 0) - #define g_hash_table_get_keys_as_array(hash_table, length) \ - ({ _nm_g_hash_table_get_keys_as_array(hash_table, length); }) -#else - #define g_hash_table_get_keys_as_array(hash_table, length) \ - ({ \ - G_GNUC_BEGIN_IGNORE_DEPRECATIONS(g_hash_table_get_keys_as_array) \ - ((hash_table), (length)); \ - G_GNUC_END_IGNORE_DEPRECATIONS \ - }) -#endif - -/*****************************************************************************/ - -#ifndef g_info - /* g_info was only added with 2.39.2 */ - #define g_info(...) g_log(G_LOG_DOMAIN, G_LOG_LEVEL_INFO, __VA_ARGS__) -#endif - -/*****************************************************************************/ - -static inline gpointer -_nm_g_steal_pointer(gpointer pp) -{ - gpointer *ptr = (gpointer *) pp; - gpointer ref; - - ref = *ptr; - *ptr = NULL; - - return ref; -} - -#if !GLIB_CHECK_VERSION(2, 44, 0) -static inline gpointer -g_steal_pointer(gpointer pp) -{ - return _nm_g_steal_pointer(pp); -} -#endif - -#ifdef g_steal_pointer - #undef g_steal_pointer -#endif -#define g_steal_pointer(pp) ((typeof(*(pp))) _nm_g_steal_pointer(pp)) - -/*****************************************************************************/ - -static inline gboolean -_nm_g_strv_contains(const char *const *strv, const char *str) -{ -#if !GLIB_CHECK_VERSION(2, 44, 0) - g_return_val_if_fail(strv != NULL, FALSE); - g_return_val_if_fail(str != NULL, FALSE); - - for (; *strv != NULL; strv++) { - if (g_str_equal(str, *strv)) - return TRUE; - } - - return FALSE; -#else - G_GNUC_BEGIN_IGNORE_DEPRECATIONS - return g_strv_contains(strv, str); - G_GNUC_END_IGNORE_DEPRECATIONS -#endif -} -#define g_strv_contains _nm_g_strv_contains - -/*****************************************************************************/ - -static inline GVariant * -_nm_g_variant_new_take_string(char *string) -{ -#if !GLIB_CHECK_VERSION(2, 36, 0) - GVariant *value; - - g_return_val_if_fail(string != NULL, NULL); - g_return_val_if_fail(g_utf8_validate(string, -1, NULL), NULL); - - value = g_variant_new_string(string); - g_free(string); - return value; -#elif !GLIB_CHECK_VERSION(2, 38, 0) - GVariant *value; - GBytes * bytes; - - g_return_val_if_fail(string != NULL, NULL); - g_return_val_if_fail(g_utf8_validate(string, -1, NULL), NULL); - - bytes = g_bytes_new_take(string, strlen(string) + 1); - value = g_variant_new_from_bytes(G_VARIANT_TYPE_STRING, bytes, TRUE); - g_bytes_unref(bytes); - - return value; -#else - G_GNUC_BEGIN_IGNORE_DEPRECATIONS - return g_variant_new_take_string(string); - G_GNUC_END_IGNORE_DEPRECATIONS -#endif -} -#define g_variant_new_take_string _nm_g_variant_new_take_string - -/*****************************************************************************/ - -#if !GLIB_CHECK_VERSION(2, 38, 0) -_nm_printf(1, 2) static inline GVariant *_nm_g_variant_new_printf(const char *format_string, ...) -{ - char * string; - va_list ap; - - g_return_val_if_fail(format_string, NULL); - - va_start(ap, format_string); - string = g_strdup_vprintf(format_string, ap); - va_end(ap); - - return g_variant_new_take_string(string); -} - #define g_variant_new_printf(...) _nm_g_variant_new_printf(__VA_ARGS__) -#else - #define g_variant_new_printf(...) \ - ({ \ - GVariant *_v; \ - \ - G_GNUC_BEGIN_IGNORE_DEPRECATIONS \ - _v = g_variant_new_printf(__VA_ARGS__); \ - G_GNUC_END_IGNORE_DEPRECATIONS \ - _v; \ - }) -#endif - -/*****************************************************************************/ - -/* Recent glib also casts the results to typeof(Obj), but only if - * - * ( defined(g_has_typeof) && GLIB_VERSION_MAX_ALLOWED >= GLIB_VERSION_2_56 ) - * - * Since we build NetworkManager with older GLIB_VERSION_MAX_ALLOWED, it's - * not taking effect. - * - * Override this. */ -#undef g_object_ref -#undef g_object_ref_sink -#define g_object_ref(Obj) ((typeof(Obj)) g_object_ref(Obj)) -#define g_object_ref_sink(Obj) ((typeof(Obj)) g_object_ref_sink(Obj)) - -/*****************************************************************************/ - -#ifndef g_autofree - /* we still don't rely on recent glib to provide g_autofree. Hence, we continue - * to use our gs_* free macros that we took from libgsystem. - * - * To ease migration towards g_auto*, add a compat define for g_autofree. */ - #define g_autofree gs_free -#endif - -/*****************************************************************************/ - -#if !GLIB_CHECK_VERSION(2, 47, 1) -/* Older versions of g_value_unset() only allowed to unset a GValue which - * was initialized previously. This was relaxed ([1], [2], [3]). - * - * Our nm_auto_unset_gvalue macro requires to be able to call g_value_unset(). - * Also, it is our general practice to allow for that. Add a compat implementation. - * - * [1] https://gitlab.gnome.org/GNOME/glib/commit/4b2d92a864f1505f1b08eb639d74293fa32681da - * [2] commit "Allow passing unset GValues to g_value_unset()" - * [3] https://bugzilla.gnome.org/show_bug.cgi?id=755766 - */ -static inline void -_nm_g_value_unset(GValue *value) -{ - g_return_if_fail(value); - - if (value->g_type != 0) - g_value_unset(value); -} - #define g_value_unset _nm_g_value_unset -#endif - -/* G_PID_FORMAT was added only in 2.53.5. Define it ourself. - * - * If this was about "pid_t", we would check SIZEOF_PID_T, and set - * PRIi32/PRIi16, like systemd does. But it's actually about - * GPid, which glib typedefs as an "int". - * - * There is a test_gpid() that check that GPid is really a typedef - * for int. */ -#undef G_PID_FORMAT -#define G_PID_FORMAT "i" - -/*****************************************************************************/ - -/* G_SOURCE_FUNC was added in 2.57.2. */ -#undef G_SOURCE_FUNC -#define G_SOURCE_FUNC(f) ((GSourceFunc)(void (*)(void))(f)) - -/*****************************************************************************/ - -/* g_atomic_pointer_get() is implemented as a macro, and it is also used for - * (gsize *) arguments. However, that leads to compiler warnings in certain - * configurations. Work around it, by redefining the macro. */ -static inline gpointer -_g_atomic_pointer_get(void **atomic) -{ - return g_atomic_pointer_get(atomic); -} -#undef g_atomic_pointer_get -#define g_atomic_pointer_get(atomic) \ - ({ \ - typeof(*atomic) *const _atomic = (atomic); \ - \ - /* g_atomic_pointer_get() is used by glib also for (gsize *) pointers, - * not only pointers to pointers. We thus don't enforce that (*atomic) - * is a pointer, but of suitable size/alignment. */ \ - \ - G_STATIC_ASSERT(sizeof(*_atomic) == sizeof(gpointer)); \ - G_STATIC_ASSERT(_nm_alignof(*_atomic) == _nm_alignof(gpointer)); \ - (void) (0 ? (gpointer) * (_atomic) : NULL); \ - \ - (typeof(*_atomic)) _g_atomic_pointer_get((void **) _atomic); \ - }) - -/* Reimplement g_atomic_pointer_set() macro too. Our variant does more type - * checks. */ -static inline void -_g_atomic_pointer_set(void **atomic, void *newval) -{ - return g_atomic_pointer_set(atomic, newval); -} -#undef g_atomic_pointer_set -#define g_atomic_pointer_set(atomic, newval) \ - ({ \ - typeof(*atomic) *const _atomic = (atomic); \ - typeof(*_atomic) const _newval = (newval); \ - _nm_unused gconstpointer const _val_type_check = _newval; \ - \ - (void) (0 ? (gpointer) * (_atomic) : NULL); \ - \ - _g_atomic_pointer_set((void **) _atomic, (void *) _newval); \ - }) - -/* Glib implements g_atomic_pointer_compare_and_exchange() as a macro. - * For one, to inline the atomic operation and also to perform some type checks - * on the arguments. - * Depending on compiler and glib version, glib passes the arguments as they - * are to __atomic_compare_exchange_n(). Some clang version don't accept const - * pointers there. Reimplement the macro to get that right, but with stronger - * type checks (as we use typeof()). Had one job. */ -static inline gboolean -_g_atomic_pointer_compare_and_exchange(void **atomic, void *oldval, void *newval) -{ - return g_atomic_pointer_compare_and_exchange(atomic, oldval, newval); -} -#undef g_atomic_pointer_compare_and_exchange -#define g_atomic_pointer_compare_and_exchange(atomic, oldval, newval) \ - ({ \ - typeof(*atomic) *const _atomic = (atomic); \ - typeof(*_atomic) const _oldval = (oldval); \ - typeof(*_atomic) const _newval = (newval); \ - _nm_unused gconstpointer const _val_type_check = _oldval; \ - \ - (void) (0 ? (gpointer) * (_atomic) : NULL); \ - \ - _g_atomic_pointer_compare_and_exchange((void **) _atomic, \ - (void *) _oldval, \ - (void *) _newval); \ - }) - -/*****************************************************************************/ - -#if !GLIB_CHECK_VERSION(2, 58, 0) -static inline gboolean -g_hash_table_steal_extended(GHashTable * hash_table, - gconstpointer lookup_key, - gpointer * stolen_key, - gpointer * stolen_value) -{ - g_assert(stolen_key); - g_assert(stolen_value); - - if (g_hash_table_lookup_extended(hash_table, lookup_key, stolen_key, stolen_value)) { - g_hash_table_steal(hash_table, lookup_key); - return TRUE; - } - *stolen_key = NULL; - *stolen_value = NULL; - return FALSE; -} -#else - #define g_hash_table_steal_extended(hash_table, lookup_key, stolen_key, stolen_value) \ - ({ \ - gpointer *_stolen_key = (stolen_key); \ - gpointer *_stolen_value = (stolen_value); \ - \ - /* we cannot allow NULL arguments, because then we would leak the values in - * the compat implementation. */ \ - g_assert(_stolen_key); \ - g_assert(_stolen_value); \ - \ - G_GNUC_BEGIN_IGNORE_DEPRECATIONS \ - g_hash_table_steal_extended(hash_table, lookup_key, _stolen_key, _stolen_value); \ - G_GNUC_END_IGNORE_DEPRECATIONS \ - }) -#endif - -/*****************************************************************************/ - -__attribute__(( - __deprecated__("Don't use g_cancellable_reset(). Create a new cancellable instead."))) void -_nm_g_cancellable_reset(GCancellable *cancellable); - -#undef g_cancellable_reset -#define g_cancellable_reset(cancellable) _nm_g_cancellable_reset(cancellable) - -/*****************************************************************************/ - -#endif /* __NM_GLIB_H__ */ diff --git a/shared/nm-glib-aux/nm-hash-utils.c b/shared/nm-glib-aux/nm-hash-utils.c deleted file mode 100644 index 29349b1d..00000000 --- a/shared/nm-glib-aux/nm-hash-utils.c +++ /dev/null @@ -1,286 +0,0 @@ -/* SPDX-License-Identifier: LGPL-2.1-or-later */ -/* - * Copyright (C) 2017 Red Hat, Inc. - */ - -#include "nm-glib-aux/nm-default-glib-i18n-lib.h" - -#include "nm-hash-utils.h" - -#include <stdint.h> - -#include "nm-shared-utils.h" -#include "nm-random-utils.h" - -/*****************************************************************************/ - -#define HASH_KEY_SIZE 16u -#define HASH_KEY_SIZE_GUINT ((HASH_KEY_SIZE + sizeof(guint) - 1) / sizeof(guint)) - -G_STATIC_ASSERT(sizeof(guint) * HASH_KEY_SIZE_GUINT >= HASH_KEY_SIZE); - -static const guint8 *volatile global_seed = NULL; - -static const guint8 * -_get_hash_key_init(void) -{ - /* the returned hash is aligned to guin64, hence, it is safe - * to use it as guint* or guint64* pointer. */ - static union { - guint8 v8[HASH_KEY_SIZE]; - guint _align_as_uint; - guint32 _align_as_uint32; - guint64 _align_as_uint64; - } g_arr; - const guint8 *g; - -again: - g = g_atomic_pointer_get(&global_seed); - if (!G_UNLIKELY(g)) { - static gsize g_lock; - uint64_t h; - union { - guint vuint; - guint8 v8[HASH_KEY_SIZE]; - guint8 _extra_entropy[3 * HASH_KEY_SIZE]; - } t_arr; - - nm_utils_random_bytes(&t_arr, sizeof(t_arr)); - - /* We only initialize one random hash key. So we can spend some effort - * of getting this right. For one, we collect more random bytes than - * necessary. - * - * Then, the first guint of the seed should have all the entropy that we could - * obtain in sizeof(t_arr). For that, siphash(t_arr) and xor the first guint - * with hash. - * The first guint is especially interesting for nm_hash_static() below that - * doesn't use siphash itself. */ - h = c_siphash_hash(t_arr.v8, (const guint8 *) &t_arr, sizeof(t_arr)); - if (sizeof(h) > sizeof(guint)) - t_arr.vuint = t_arr.vuint ^ ((guint)(h & G_MAXUINT)) ^ ((guint)(h >> 32)); - else - t_arr.vuint = t_arr.vuint ^ ((guint)(h & G_MAXUINT)); - - if (!g_once_init_enter(&g_lock)) { - /* lost a race. The random key is already initialized. */ - goto again; - } - - memcpy(g_arr.v8, t_arr.v8, HASH_KEY_SIZE); - g = g_arr.v8; - g_atomic_pointer_set(&global_seed, g); - g_once_init_leave(&g_lock, 1); - } - - nm_assert(g == g_arr.v8); - return g; -} - -#define _get_hash_key() \ - ({ \ - const guint8 *_g; \ - \ - _g = g_atomic_pointer_get(&global_seed); \ - if (G_UNLIKELY(!_g)) \ - _g = _get_hash_key_init(); \ - _g; \ - }) - -guint -nm_hash_static(guint static_seed) -{ - /* Note that we only xor the static_seed with the first guint of the key. - * - * We don't use siphash, which would mix the bits better with _get_hash_key(). - * Note that nm_hash_static() isn't used to hash the static_seed. Instead, it - * is used to get a unique hash value in a static context. That means, every - * caller is responsible to choose a static_seed that is sufficiently - * distinct from all other callers. In other words, static_seed should be a - * unique constant with good entropy. - * - * Note that _get_hash_key_init() already xored the first guint of the - * key with the siphash of the entire static key. That means, even if - * we got bad randomness for the first guint, the first guint is also - * mixed with the randomness of the entire random key. - * - * Also, ensure that we don't return zero (like for nm_hash_complete()). - */ - return ((*((const guint *) _get_hash_key())) ^ static_seed) ?: 3679500967u; -} - -void -nm_hash_siphash42_init(CSipHash *h, guint static_seed) -{ - const guint8 *g; - union { - guint64 _align_as_uint64; - guint arr[HASH_KEY_SIZE_GUINT]; - } seed; - - nm_assert(h); - - g = _get_hash_key(); - memcpy(&seed, g, HASH_KEY_SIZE); - seed.arr[0] ^= static_seed; - c_siphash_init(h, (const guint8 *) &seed); -} - -guint -nm_hash_str(const char *str) -{ - NMHashState h; - - if (!str) - return nm_hash_static(1867854211u); - nm_hash_init(&h, 1867854211u); - nm_hash_update_str(&h, str); - return nm_hash_complete(&h); -} - -guint -nm_str_hash(gconstpointer str) -{ - return nm_hash_str(str); -} - -guint -nm_hash_ptr(gconstpointer ptr) -{ - NMHashState h; - - if (!ptr) - return nm_hash_static(2907677551u); - nm_hash_init(&h, 2907677551u); - nm_hash_update(&h, &ptr, sizeof(ptr)); - return nm_hash_complete(&h); -} - -guint -nm_direct_hash(gconstpointer ptr) -{ - return nm_hash_ptr(ptr); -} - -/*****************************************************************************/ - -guint -nm_pstr_hash(gconstpointer p) -{ - const char *const *s = p; - - if (!s) - return nm_hash_static(101061439u); - return nm_hash_str(*s); -} - -gboolean -nm_pstr_equal(gconstpointer a, gconstpointer b) -{ - const char *const *s1 = a; - const char *const *s2 = b; - - return (s1 == s2) || (s1 && s2 && nm_streq0(*s1, *s2)); -} - -guint -nm_pint_hash(gconstpointer p) -{ - const int *s = p; - - if (!s) - return nm_hash_static(298377461u); - return nm_hash_val(1208815757u, *s); -} - -gboolean -nm_pint_equals(gconstpointer a, gconstpointer b) -{ - const int *s1 = a; - const int *s2 = a; - - return s1 == s2 || (s1 && s2 && *s1 == *s2); -} - -guint -nm_pdirect_hash(gconstpointer p) -{ - const void *const *s = p; - - if (!s) - return nm_hash_static(1852748873u); - return nm_direct_hash(*s); -} - -gboolean -nm_pdirect_equal(gconstpointer a, gconstpointer b) -{ - const void *const *s1 = a; - const void *const *s2 = b; - - return (s1 == s2) || (s1 && s2 && *s1 == *s2); -} - -guint -nm_ppdirect_hash(gconstpointer p) -{ - const void *const *const *s = p; - - if (!s) - return nm_hash_static(396534869u); - if (!*s) - return nm_hash_static(1476102263u); - return nm_direct_hash(**s); -} - -gboolean -nm_ppdirect_equal(gconstpointer a, gconstpointer b) -{ - const void *const *const *s1 = a; - const void *const *const *s2 = b; - - if (s1 == s2) - return TRUE; - if (!s1 || !s2) - return FALSE; - - if (*s1 == *s2) - return TRUE; - if (!*s1 || !*s2) - return FALSE; - - return **s1 == **s2; -} - -/*****************************************************************************/ - -guint -nm_gbytes_hash(gconstpointer p) -{ - GBytes * ptr = (GBytes *) p; - gconstpointer arr; - gsize len; - - arr = g_bytes_get_data(ptr, &len); - return nm_hash_mem(792701303u, arr, len); -} - -guint -nm_pgbytes_hash(gconstpointer p) -{ - GBytes *const *ptr = p; - gconstpointer arr; - gsize len; - - arr = g_bytes_get_data(*ptr, &len); - return nm_hash_mem(1470631313u, arr, len); -} - -gboolean -nm_pgbytes_equal(gconstpointer a, gconstpointer b) -{ - GBytes *const *ptr_a = a; - GBytes *const *ptr_b = b; - - return g_bytes_equal(*ptr_a, *ptr_b); -} diff --git a/shared/nm-glib-aux/nm-hash-utils.h b/shared/nm-glib-aux/nm-hash-utils.h deleted file mode 100644 index a7b8677b..00000000 --- a/shared/nm-glib-aux/nm-hash-utils.h +++ /dev/null @@ -1,442 +0,0 @@ -/* SPDX-License-Identifier: LGPL-2.1-or-later */ -/* - * Copyright (C) 2017 Red Hat, Inc. - */ - -#ifndef __NM_HASH_UTILS_H__ -#define __NM_HASH_UTILS_H__ - -#include "c-siphash/src/c-siphash.h" -#include "nm-macros-internal.h" - -/*****************************************************************************/ - -#define NM_HASH_SEED_16(a0, a1, a2, a3, a4, a5, a6, a7, a8, a9, aa, ab, ac, ad, ae, af) \ - ((const guint8[16]){a0, a1, a2, a3, a4, a5, a6, a7, a8, a9, aa, ab, ac, ad, ae, af}) - -void nm_hash_siphash42_init(CSipHash *h, guint static_seed); - -/* Siphash24 of binary buffer @arr and @len, using the randomized seed from - * other NMHash functions. - * - * Note, that this is guaranteed to use siphash42 under the hood (contrary to - * all other NMHash API, which leave this undefined). That matters at the point, - * where the caller needs to be sure that a reasonably strong hashing algorithm - * is used. (Yes, NMHash is all about siphash24, but otherwise that is not promised - * anywhere). - * - * Another difference is, that this returns guint64 (not guint like other NMHash functions). - * - * Another difference is, that this may also return zero (not like nm_hash_complete()). - * - * Then, why not use c_siphash_hash() directly? Because this also uses the randomized, - * per-run hash-seed like nm_hash_init(). So, you get siphash24 with a random - * seed (which is cached for the current run of the program). - */ -static inline guint64 -nm_hash_siphash42(guint static_seed, const void *ptr, gsize n) -{ - CSipHash h; - - nm_hash_siphash42_init(&h, static_seed); - c_siphash_append(&h, ptr, n); - return c_siphash_finalize(&h); -} - -/*****************************************************************************/ - -struct _NMHashState { - CSipHash _state; -}; - -typedef struct _NMHashState NMHashState; - -guint nm_hash_static(guint static_seed); - -static inline void -nm_hash_init(NMHashState *state, guint static_seed) -{ - nm_assert(state); - - nm_hash_siphash42_init(&state->_state, static_seed); -} - -static inline guint64 -nm_hash_complete_u64(NMHashState *state) -{ - nm_assert(state); - - /* this returns the native u64 hash value. Note that this differs - * from nm_hash_complete() in two ways: - * - * - the type, guint64 vs. guint. - * - nm_hash_complete() never returns zero. - * - * In practice, nm_hash*() API is implemented via siphash24, so this returns - * the siphash24 value. But that is not guaranteed by the API, and if you need - * siphash24 directly, use c_siphash_*() and nm_hash_siphash42*() API. */ - return c_siphash_finalize(&state->_state); -} - -static inline guint -nm_hash_complete(NMHashState *state) -{ - guint64 h; - - h = nm_hash_complete_u64(state); - - /* we don't ever want to return a zero hash. - * - * NMPObject requires that in _idx_obj_part(), and it's just a good idea. */ - return (((guint)(h >> 32)) ^ ((guint) h)) ?: 1396707757u; -} - -static inline void -nm_hash_update(NMHashState *state, const void *ptr, gsize n) -{ - nm_assert(state); - nm_assert(n == 0 || ptr); - - /* Note: the data passed in here might be sensitive data (secrets), - * that we should nm_explicit_bzero() afterwards. However, since - * we are using siphash24 with a random key, that is not really - * necessary. Something to keep in mind, if we ever move away from - * this hash implementation. */ - c_siphash_append(&state->_state, ptr, n); -} - -#define nm_hash_update_val(state, val) \ - G_STMT_START \ - { \ - typeof(val) _val = (val); \ - \ - nm_hash_update((state), &_val, sizeof(_val)); \ - } \ - G_STMT_END - -#define nm_hash_update_valp(state, val) nm_hash_update((state), (val), sizeof(*(val))) - -static inline void -nm_hash_update_bool(NMHashState *state, bool val) -{ - nm_hash_update(state, &val, sizeof(val)); -} - -#define _NM_HASH_COMBINE_BOOLS_x_1(t, y) ((y) ? ((t)(1ull << 0)) : ((t) 0ull)) -#define _NM_HASH_COMBINE_BOOLS_x_2(t, y, ...) \ - ((y) ? ((t)(1ull << 1)) : ((t) 0ull)) | _NM_HASH_COMBINE_BOOLS_x_1(t, __VA_ARGS__) -#define _NM_HASH_COMBINE_BOOLS_x_3(t, y, ...) \ - ((y) ? ((t)(1ull << 2)) : ((t) 0ull)) | _NM_HASH_COMBINE_BOOLS_x_2(t, __VA_ARGS__) -#define _NM_HASH_COMBINE_BOOLS_x_4(t, y, ...) \ - ((y) ? ((t)(1ull << 3)) : ((t) 0ull)) | _NM_HASH_COMBINE_BOOLS_x_3(t, __VA_ARGS__) -#define _NM_HASH_COMBINE_BOOLS_x_5(t, y, ...) \ - ((y) ? ((t)(1ull << 4)) : ((t) 0ull)) | _NM_HASH_COMBINE_BOOLS_x_4(t, __VA_ARGS__) -#define _NM_HASH_COMBINE_BOOLS_x_6(t, y, ...) \ - ((y) ? ((t)(1ull << 5)) : ((t) 0ull)) | _NM_HASH_COMBINE_BOOLS_x_5(t, __VA_ARGS__) -#define _NM_HASH_COMBINE_BOOLS_x_7(t, y, ...) \ - ((y) ? ((t)(1ull << 6)) : ((t) 0ull)) | _NM_HASH_COMBINE_BOOLS_x_6(t, __VA_ARGS__) -#define _NM_HASH_COMBINE_BOOLS_x_8(t, y, ...) \ - ((y) ? ((t)(1ull << 7)) : ((t) 0ull)) | _NM_HASH_COMBINE_BOOLS_x_7(t, __VA_ARGS__) -#define _NM_HASH_COMBINE_BOOLS_x_9(t, y, ...) \ - ((y) ? ((t)(1ull << 8)) : ((t) 0ull)) \ - | (G_STATIC_ASSERT_EXPR(sizeof(t) >= 2), (_NM_HASH_COMBINE_BOOLS_x_8(t, __VA_ARGS__))) -#define _NM_HASH_COMBINE_BOOLS_x_10(t, y, ...) \ - ((y) ? ((t)(1ull << 9)) : ((t) 0ull)) | _NM_HASH_COMBINE_BOOLS_x_9(t, __VA_ARGS__) -#define _NM_HASH_COMBINE_BOOLS_x_11(t, y, ...) \ - ((y) ? ((t)(1ull << 10)) : ((t) 0ull)) | _NM_HASH_COMBINE_BOOLS_x_10(t, __VA_ARGS__) -#define _NM_HASH_COMBINE_BOOLS_n2(t, n, ...) _NM_HASH_COMBINE_BOOLS_x_##n(t, __VA_ARGS__) -#define _NM_HASH_COMBINE_BOOLS_n(t, n, ...) _NM_HASH_COMBINE_BOOLS_n2(t, n, __VA_ARGS__) - -#define NM_HASH_COMBINE_BOOLS(type, ...) \ - ((type)(_NM_HASH_COMBINE_BOOLS_n(type, NM_NARG(__VA_ARGS__), __VA_ARGS__))) - -#define nm_hash_update_bools(state, ...) \ - nm_hash_update_val(state, NM_HASH_COMBINE_BOOLS(guint8, __VA_ARGS__)) - -#define _NM_HASH_COMBINE_VALS_typ_x_1(y) typeof(y) _v1; -#define _NM_HASH_COMBINE_VALS_typ_x_2(y, ...) \ - typeof(y) _v2; \ - _NM_HASH_COMBINE_VALS_typ_x_1(__VA_ARGS__) -#define _NM_HASH_COMBINE_VALS_typ_x_3(y, ...) \ - typeof(y) _v3; \ - _NM_HASH_COMBINE_VALS_typ_x_2(__VA_ARGS__) -#define _NM_HASH_COMBINE_VALS_typ_x_4(y, ...) \ - typeof(y) _v4; \ - _NM_HASH_COMBINE_VALS_typ_x_3(__VA_ARGS__) -#define _NM_HASH_COMBINE_VALS_typ_x_5(y, ...) \ - typeof(y) _v5; \ - _NM_HASH_COMBINE_VALS_typ_x_4(__VA_ARGS__) -#define _NM_HASH_COMBINE_VALS_typ_x_6(y, ...) \ - typeof(y) _v6; \ - _NM_HASH_COMBINE_VALS_typ_x_5(__VA_ARGS__) -#define _NM_HASH_COMBINE_VALS_typ_x_7(y, ...) \ - typeof(y) _v7; \ - _NM_HASH_COMBINE_VALS_typ_x_6(__VA_ARGS__) -#define _NM_HASH_COMBINE_VALS_typ_x_8(y, ...) \ - typeof(y) _v8; \ - _NM_HASH_COMBINE_VALS_typ_x_7(__VA_ARGS__) -#define _NM_HASH_COMBINE_VALS_typ_x_9(y, ...) \ - typeof(y) _v9; \ - _NM_HASH_COMBINE_VALS_typ_x_8(__VA_ARGS__) -#define _NM_HASH_COMBINE_VALS_typ_x_10(y, ...) \ - typeof(y) _v10; \ - _NM_HASH_COMBINE_VALS_typ_x_9(__VA_ARGS__) -#define _NM_HASH_COMBINE_VALS_typ_x_11(y, ...) \ - typeof(y) _v11; \ - _NM_HASH_COMBINE_VALS_typ_x_10(__VA_ARGS__) -#define _NM_HASH_COMBINE_VALS_typ_x_12(y, ...) \ - typeof(y) _v12; \ - _NM_HASH_COMBINE_VALS_typ_x_11(__VA_ARGS__) -#define _NM_HASH_COMBINE_VALS_typ_x_13(y, ...) \ - typeof(y) _v13; \ - _NM_HASH_COMBINE_VALS_typ_x_12(__VA_ARGS__) -#define _NM_HASH_COMBINE_VALS_typ_x_14(y, ...) \ - typeof(y) _v14; \ - _NM_HASH_COMBINE_VALS_typ_x_13(__VA_ARGS__) -#define _NM_HASH_COMBINE_VALS_typ_x_15(y, ...) \ - typeof(y) _v15; \ - _NM_HASH_COMBINE_VALS_typ_x_14(__VA_ARGS__) -#define _NM_HASH_COMBINE_VALS_typ_x_16(y, ...) \ - typeof(y) _v16; \ - _NM_HASH_COMBINE_VALS_typ_x_15(__VA_ARGS__) -#define _NM_HASH_COMBINE_VALS_typ_x_17(y, ...) \ - typeof(y) _v17; \ - _NM_HASH_COMBINE_VALS_typ_x_16(__VA_ARGS__) -#define _NM_HASH_COMBINE_VALS_typ_x_18(y, ...) \ - typeof(y) _v18; \ - _NM_HASH_COMBINE_VALS_typ_x_17(__VA_ARGS__) -#define _NM_HASH_COMBINE_VALS_typ_x_19(y, ...) \ - typeof(y) _v19; \ - _NM_HASH_COMBINE_VALS_typ_x_18(__VA_ARGS__) -#define _NM_HASH_COMBINE_VALS_typ_x_20(y, ...) \ - typeof(y) _v20; \ - _NM_HASH_COMBINE_VALS_typ_x_19(__VA_ARGS__) -#define _NM_HASH_COMBINE_VALS_typ_n2(n, ...) _NM_HASH_COMBINE_VALS_typ_x_##n(__VA_ARGS__) -#define _NM_HASH_COMBINE_VALS_typ_n(n, ...) _NM_HASH_COMBINE_VALS_typ_n2(n, __VA_ARGS__) - -#define _NM_HASH_COMBINE_VALS_val_x_1(y) ._v1 = (y), -#define _NM_HASH_COMBINE_VALS_val_x_2(y, ...) ._v2 = (y), _NM_HASH_COMBINE_VALS_val_x_1(__VA_ARGS__) -#define _NM_HASH_COMBINE_VALS_val_x_3(y, ...) ._v3 = (y), _NM_HASH_COMBINE_VALS_val_x_2(__VA_ARGS__) -#define _NM_HASH_COMBINE_VALS_val_x_4(y, ...) ._v4 = (y), _NM_HASH_COMBINE_VALS_val_x_3(__VA_ARGS__) -#define _NM_HASH_COMBINE_VALS_val_x_5(y, ...) ._v5 = (y), _NM_HASH_COMBINE_VALS_val_x_4(__VA_ARGS__) -#define _NM_HASH_COMBINE_VALS_val_x_6(y, ...) ._v6 = (y), _NM_HASH_COMBINE_VALS_val_x_5(__VA_ARGS__) -#define _NM_HASH_COMBINE_VALS_val_x_7(y, ...) ._v7 = (y), _NM_HASH_COMBINE_VALS_val_x_6(__VA_ARGS__) -#define _NM_HASH_COMBINE_VALS_val_x_8(y, ...) ._v8 = (y), _NM_HASH_COMBINE_VALS_val_x_7(__VA_ARGS__) -#define _NM_HASH_COMBINE_VALS_val_x_9(y, ...) ._v9 = (y), _NM_HASH_COMBINE_VALS_val_x_8(__VA_ARGS__) -#define _NM_HASH_COMBINE_VALS_val_x_10(y, ...) \ - ._v10 = (y), _NM_HASH_COMBINE_VALS_val_x_9(__VA_ARGS__) -#define _NM_HASH_COMBINE_VALS_val_x_11(y, ...) \ - ._v11 = (y), _NM_HASH_COMBINE_VALS_val_x_10(__VA_ARGS__) -#define _NM_HASH_COMBINE_VALS_val_x_12(y, ...) \ - ._v12 = (y), _NM_HASH_COMBINE_VALS_val_x_11(__VA_ARGS__) -#define _NM_HASH_COMBINE_VALS_val_x_13(y, ...) \ - ._v13 = (y), _NM_HASH_COMBINE_VALS_val_x_12(__VA_ARGS__) -#define _NM_HASH_COMBINE_VALS_val_x_14(y, ...) \ - ._v14 = (y), _NM_HASH_COMBINE_VALS_val_x_13(__VA_ARGS__) -#define _NM_HASH_COMBINE_VALS_val_x_15(y, ...) \ - ._v15 = (y), _NM_HASH_COMBINE_VALS_val_x_14(__VA_ARGS__) -#define _NM_HASH_COMBINE_VALS_val_x_16(y, ...) \ - ._v16 = (y), _NM_HASH_COMBINE_VALS_val_x_15(__VA_ARGS__) -#define _NM_HASH_COMBINE_VALS_val_x_17(y, ...) \ - ._v17 = (y), _NM_HASH_COMBINE_VALS_val_x_16(__VA_ARGS__) -#define _NM_HASH_COMBINE_VALS_val_x_18(y, ...) \ - ._v18 = (y), _NM_HASH_COMBINE_VALS_val_x_17(__VA_ARGS__) -#define _NM_HASH_COMBINE_VALS_val_x_19(y, ...) \ - ._v19 = (y), _NM_HASH_COMBINE_VALS_val_x_18(__VA_ARGS__) -#define _NM_HASH_COMBINE_VALS_val_x_20(y, ...) \ - ._v20 = (y), _NM_HASH_COMBINE_VALS_val_x_19(__VA_ARGS__) -#define _NM_HASH_COMBINE_VALS_val_n2(n, ...) _NM_HASH_COMBINE_VALS_val_x_##n(__VA_ARGS__) -#define _NM_HASH_COMBINE_VALS_val_n(n, ...) _NM_HASH_COMBINE_VALS_val_n2(n, __VA_ARGS__) - -/* NM_HASH_COMBINE_VALS() is faster then nm_hash_update_val() as it combines multiple - * calls to nm_hash_update() using a packed structure. */ -#define NM_HASH_COMBINE_VALS(var, ...) \ - const struct _nm_packed { \ - _NM_HASH_COMBINE_VALS_typ_n(NM_NARG(__VA_ARGS__), __VA_ARGS__) \ - } var _nm_alignas(guint64) = {_NM_HASH_COMBINE_VALS_val_n(NM_NARG(__VA_ARGS__), __VA_ARGS__)} - -/* nm_hash_update_vals() is faster then nm_hash_update_val() as it combines multiple - * calls to nm_hash_update() using a packed structure. */ -#define nm_hash_update_vals(state, ...) \ - G_STMT_START \ - { \ - NM_HASH_COMBINE_VALS(_val, __VA_ARGS__); \ - \ - nm_hash_update((state), &_val, sizeof(_val)); \ - } \ - G_STMT_END - -static inline void -nm_hash_update_mem(NMHashState *state, const void *ptr, gsize n) -{ - /* This also hashes the length of the data. That means, - * hashing two consecutive binary fields (of arbitrary - * length), will hash differently. That is, - * [[1,1], []] differs from [[1],[1]]. - * - * If you have a constant length (sizeof), use nm_hash_update() - * instead. */ - nm_hash_update(state, &n, sizeof(n)); - if (n > 0) - nm_hash_update(state, ptr, n); -} - -static inline void -nm_hash_update_str0(NMHashState *state, const char *str) -{ - if (str) - nm_hash_update_mem(state, str, strlen(str)); - else { - gsize n = G_MAXSIZE; - - nm_hash_update(state, &n, sizeof(n)); - } -} - -static inline void -nm_hash_update_str(NMHashState *state, const char *str) -{ - nm_assert(str); - nm_hash_update(state, str, strlen(str) + 1); -} - -#if _NM_CC_SUPPORT_GENERIC - /* Like nm_hash_update_str(), but restricted to arrays only. nm_hash_update_str() only works - * with a @str argument that cannot be NULL. If you have a string pointer, that is never NULL, use - * nm_hash_update() instead. */ - #define nm_hash_update_strarr(state, str) \ - (_Generic(&(str), const char(*)[sizeof(str)] \ - : nm_hash_update_str((state), (str)), char(*)[sizeof(str)] \ - : nm_hash_update_str((state), (str)))) -#else - #define nm_hash_update_strarr(state, str) nm_hash_update_str((state), (str)) -#endif - -guint nm_hash_ptr(gconstpointer ptr); -guint nm_direct_hash(gconstpointer str); - -guint nm_hash_str(const char *str); -guint nm_str_hash(gconstpointer str); - -#define nm_hash_val(static_seed, val) \ - ({ \ - NMHashState _h; \ - \ - nm_hash_init(&_h, (static_seed)); \ - nm_hash_update_val(&_h, (val)); \ - nm_hash_complete(&_h); \ - }) - -static inline guint -nm_hash_mem(guint static_seed, const void *ptr, gsize n) -{ - NMHashState h; - - if (n == 0) - return nm_hash_static(static_seed); - nm_hash_init(&h, static_seed); - nm_hash_update(&h, ptr, n); - return nm_hash_complete(&h); -} - -/*****************************************************************************/ - -/* nm_pstr_*() are for hashing keys that are pointers to strings, - * that is, "const char *const*" types, using strcmp(). */ - -guint nm_pstr_hash(gconstpointer p); - -gboolean nm_pstr_equal(gconstpointer a, gconstpointer b); - -/*****************************************************************************/ - -/* nm_pint_*() are for hashing keys that are pointers to int values, - * that is, "const int *" types. */ - -guint nm_pint_hash(gconstpointer p); -gboolean nm_pint_equals(gconstpointer a, gconstpointer b); - -G_STATIC_ASSERT(sizeof(int) == sizeof(guint32)); -#define nm_puint32_hash nm_pint_hash -#define nm_puint32_equals nm_pint_equals - -/*****************************************************************************/ - -/* this hashes/compares the pointer value that we point to. Basically, - * (*((const void *const*) a) == *((const void *const*) b)). */ - -guint nm_pdirect_hash(gconstpointer p); - -gboolean nm_pdirect_equal(gconstpointer a, gconstpointer b); - -/* this hashes/compares the direct pointer value by following pointers to - * pointers 2 times. - * (**((const void *const*const*) a) == **((const void *const*const*) b)). */ - -guint nm_ppdirect_hash(gconstpointer p); - -gboolean nm_ppdirect_equal(gconstpointer a, gconstpointer b); - -/*****************************************************************************/ - -guint nm_gbytes_hash(gconstpointer p); -#define nm_gbytes_equal g_bytes_equal - -guint nm_pgbytes_hash(gconstpointer p); -gboolean nm_pgbytes_equal(gconstpointer a, gconstpointer b); - -/*****************************************************************************/ - -#define NM_HASH_OBFUSCATE_PTR_FMT "%016" G_GINT64_MODIFIER "x" - -/* sometimes we want to log a pointer directly, for providing context/information about - * the message that get logged. Logging pointer values directly defeats ASLR, so we should - * not do that. This returns a "unsigned long long" value that can be used - * instead. - * - * Note that there is a chance that two different pointer values hash to the same obfuscated - * value. So beware of that when reviewing logs. However, such a collision is very unlikely. */ -static inline guint64 -nm_hash_obfuscate_ptr(guint static_seed, gconstpointer val) -{ - NMHashState h; - - nm_hash_init(&h, static_seed); - nm_hash_update_val(&h, val); - return nm_hash_complete_u64(&h); -} - -/* if you want to log obfuscated pointer for a certain context (like, NMPRuleManager - * logging user-tags), then you are advised to use nm_hash_obfuscate_ptr() with your - * own, unique static-seed. - * - * However, for example the singleton constructors log the obfuscated pointer values - * for all singletons, so they must all be obfuscated with the same seed. So, this - * macro uses a particular static seed that should be used by when comparing pointer - * values in a global context. */ -#define NM_HASH_OBFUSCATE_PTR(ptr) (nm_hash_obfuscate_ptr(1678382159u, ptr)) - -#define NM_HASH_OBFUSCATE_PTR_STR(ptr, buf) \ - ({ \ - gconstpointer _ptr = (ptr); \ - \ - _ptr ? nm_sprintf_buf(buf, "[" NM_HASH_OBFUSCATE_PTR_FMT "]", NM_HASH_OBFUSCATE_PTR(_ptr)) \ - : "(null)"; \ - }) - -static inline const char * -nm_hash_obfuscated_ptr_str(gconstpointer ptr, char buf[static 17]) -{ - int l; - - nm_assert(buf); - l = g_snprintf(buf, 17, NM_HASH_OBFUSCATE_PTR_FMT, NM_HASH_OBFUSCATE_PTR(ptr)); - nm_assert(l < 17); - return buf; -} - -#define nm_hash_obfuscated_ptr_str_a(ptr) (nm_hash_obfuscated_ptr_str((ptr), g_alloca(17))) - -/*****************************************************************************/ - -#endif /* __NM_HASH_UTILS_H__ */ diff --git a/shared/nm-glib-aux/nm-io-utils.c b/shared/nm-glib-aux/nm-io-utils.c deleted file mode 100644 index 429591ad..00000000 --- a/shared/nm-glib-aux/nm-io-utils.c +++ /dev/null @@ -1,480 +0,0 @@ -/* SPDX-License-Identifier: LGPL-2.1-or-later */ -/* - * Copyright (C) 2018 Red Hat, Inc. - */ - -#include "nm-glib-aux/nm-default-glib-i18n-lib.h" - -#include "nm-io-utils.h" - -#include <sys/types.h> -#include <sys/stat.h> -#include <fcntl.h> - -#include "nm-str-buf.h" -#include "nm-shared-utils.h" -#include "nm-secret-utils.h" -#include "nm-errno.h" - -/*****************************************************************************/ - -_nm_printf(4, 5) static int _get_contents_error(GError ** error, - int errsv, - int * out_errsv, - const char *format, - ...) -{ - nm_assert(NM_ERRNO_NATIVE(errsv)); - - if (error) { - gs_free char *msg = NULL; - va_list args; - char bstrerr[NM_STRERROR_BUFSIZE]; - - va_start(args, format); - msg = g_strdup_vprintf(format, args); - va_end(args); - g_set_error(error, - G_FILE_ERROR, - g_file_error_from_errno(errsv), - "%s: %s", - msg, - nm_strerror_native_r(errsv, bstrerr, sizeof(bstrerr))); - } - - nm_assert(errsv > 0); - NM_SET_OUT(out_errsv, errsv); - - return FALSE; -} -#define _get_contents_error_errno(error, out_errsv, ...) \ - ({ \ - int _errsv = (errno); \ - \ - _get_contents_error(error, _errsv, out_errsv, __VA_ARGS__); \ - }) - -/** - * nm_utils_fd_get_contents: - * @fd: open file descriptor to read. The fd will not be closed, - * but don't rely on its state afterwards. - * @close_fd: if %TRUE, @fd will be closed by the function. - * Passing %TRUE here might safe a syscall for dup(). - * @max_length: allocate at most @max_length bytes. If the - * file is larger, reading will fail. Set to zero to use - * a very large default. - * WARNING: @max_length is here to avoid a crash for huge/unlimited files. - * For example, stat(/sys/class/net/enp0s25/ifindex) gives a filesize of - * 4K, although the actual real is small. @max_length is the memory - * allocated in the process of reading the file, thus it must be at least - * the size reported by fstat. - * If you set it to 1K, read will fail because fstat() claims the - * file is larger. - * @flags: %NMUtilsFileGetContentsFlags for reading the file. - * @contents: the output buffer with the file read. It is always - * NUL terminated. The buffer is at most @max_length long, including - * the NUL byte. That is, it reads only files up to a length of - * @max_length - 1 bytes. - * @length: optional output argument of the read file size. - * @out_errsv: (allow-none) (out): on error, a positive errno. or zero. - * @error: - * - * - * A reimplementation of g_file_get_contents() with a few differences: - * - accepts an open fd, instead of a path name. This allows you to - * use openat(). - * - limits the maximum filesize to max_length. - * - * Returns: TRUE on success. - */ -gboolean -nm_utils_fd_get_contents(int fd, - gboolean close_fd, - gsize max_length, - NMUtilsFileGetContentsFlags flags, - char ** contents, - gsize * length, - int * out_errsv, - GError ** error) -{ - nm_auto_close int fd_keeper = close_fd ? fd : -1; - struct stat stat_buf; - gs_free char * str = NULL; - const bool do_bzero_mem = NM_FLAGS_HAS(flags, NM_UTILS_FILE_GET_CONTENTS_FLAG_SECRET); - int errsv; - - g_return_val_if_fail(fd >= 0, FALSE); - g_return_val_if_fail(contents && !*contents, FALSE); - g_return_val_if_fail(!error || !*error, FALSE); - - NM_SET_OUT(length, 0); - - if (fstat(fd, &stat_buf) < 0) - return _get_contents_error_errno(error, out_errsv, "failure during fstat"); - - if (!max_length) { - /* default to a very large size, but not extreme */ - max_length = 2 * 1024 * 1024; - } - - if (stat_buf.st_size > 0 && S_ISREG(stat_buf.st_mode)) { - const gsize n_stat = stat_buf.st_size; - ssize_t n_read; - - if (n_stat > max_length - 1) - return _get_contents_error(error, - EMSGSIZE, - out_errsv, - "file too large (%zu+1 bytes with maximum %zu bytes)", - n_stat, - max_length); - - str = g_try_malloc(n_stat + 1); - if (!str) - return _get_contents_error(error, - ENOMEM, - out_errsv, - "failure to allocate buffer of %zu+1 bytes", - n_stat); - - n_read = nm_utils_fd_read_loop(fd, str, n_stat, TRUE); - if (n_read < 0) { - if (do_bzero_mem) - nm_explicit_bzero(str, n_stat); - return _get_contents_error(error, - -n_read, - out_errsv, - "error reading %zu bytes from file descriptor", - n_stat); - } - str[n_read] = '\0'; - - if (n_read < n_stat) { - if (!(str = nm_secret_mem_try_realloc_take(str, do_bzero_mem, n_stat + 1, n_read + 1))) - return _get_contents_error(error, - ENOMEM, - out_errsv, - "failure to reallocate buffer with %zu bytes", - n_read + 1); - } - NM_SET_OUT(length, n_read); - } else { - nm_auto_fclose FILE *f = NULL; - char buf[4096]; - gsize n_have, n_alloc; - int fd2; - - if (fd_keeper >= 0) - fd2 = nm_steal_fd(&fd_keeper); - else { - fd2 = fcntl(fd, F_DUPFD_CLOEXEC, 0); - if (fd2 < 0) - return _get_contents_error_errno(error, out_errsv, "error during dup"); - } - - if (!(f = fdopen(fd2, "r"))) { - errsv = errno; - nm_close(fd2); - return _get_contents_error(error, errsv, out_errsv, "failure during fdopen"); - } - - n_have = 0; - n_alloc = 0; - - while (!feof(f)) { - gsize n_read; - - n_read = fread(buf, 1, sizeof(buf), f); - errsv = errno; - if (ferror(f)) { - if (do_bzero_mem) - nm_explicit_bzero(buf, sizeof(buf)); - return _get_contents_error(error, errsv, out_errsv, "error during fread"); - } - - if (n_have > G_MAXSIZE - 1 - n_read || n_have + n_read + 1 > max_length) { - if (do_bzero_mem) - nm_explicit_bzero(buf, sizeof(buf)); - return _get_contents_error( - error, - EMSGSIZE, - out_errsv, - "file stream too large (%zu+1 bytes with maximum %zu bytes)", - (n_have > G_MAXSIZE - 1 - n_read) ? G_MAXSIZE : n_have + n_read, - max_length); - } - - if (n_have + n_read + 1 >= n_alloc) { - gsize old_n_alloc = n_alloc; - - if (n_alloc != 0) { - nm_assert(str); - if (n_alloc >= max_length / 2) - n_alloc = max_length; - else - n_alloc *= 2; - } else { - nm_assert(!str); - n_alloc = NM_MIN(n_read + 1, sizeof(buf)); - } - - if (!(str = nm_secret_mem_try_realloc_take(str, - do_bzero_mem, - old_n_alloc, - n_alloc))) { - if (do_bzero_mem) - nm_explicit_bzero(buf, sizeof(buf)); - return _get_contents_error(error, - ENOMEM, - out_errsv, - "failure to allocate buffer of %zu bytes", - n_alloc); - } - } - - memcpy(str + n_have, buf, n_read); - n_have += n_read; - } - - if (do_bzero_mem) - nm_explicit_bzero(buf, sizeof(buf)); - - if (n_alloc == 0) - str = g_new0(char, 1); - else { - str[n_have] = '\0'; - if (n_have + 1 < n_alloc) { - if (!(str = nm_secret_mem_try_realloc_take(str, do_bzero_mem, n_alloc, n_have + 1))) - return _get_contents_error(error, - ENOMEM, - out_errsv, - "failure to truncate buffer to %zu bytes", - n_have + 1); - } - } - - NM_SET_OUT(length, n_have); - } - - *contents = g_steal_pointer(&str); - NM_SET_OUT(out_errsv, 0); - return TRUE; -} - -/** - * nm_utils_file_get_contents: - * @dirfd: optional file descriptor to use openat(). If negative, use plain open(). - * @filename: the filename to open. Possibly relative to @dirfd. - * @max_length: allocate at most @max_length bytes. - * WARNING: see nm_utils_fd_get_contents() hint about @max_length. - * @flags: %NMUtilsFileGetContentsFlags for reading the file. - * @contents: the output buffer with the file read. It is always - * NUL terminated. The buffer is at most @max_length long, including - * the NUL byte. That is, it reads only files up to a length of - * @max_length - 1 bytes. - * @length: optional output argument of the read file size. - * @out_errsv: (allow-none) (out): on error, a positive errno. or zero. - * @error: - * - * A reimplementation of g_file_get_contents() with a few differences: - * - accepts an @dirfd to open @filename relative to that path via openat(). - * - limits the maximum filesize to max_length. - * - uses O_CLOEXEC on internal file descriptor - * - optionally returns the native errno on failure. - * - * Returns: TRUE on success. - */ -gboolean -nm_utils_file_get_contents(int dirfd, - const char * filename, - gsize max_length, - NMUtilsFileGetContentsFlags flags, - char ** contents, - gsize * length, - int * out_errsv, - GError ** error) -{ - int fd; - - g_return_val_if_fail(filename && filename[0], FALSE); - g_return_val_if_fail(contents && !*contents, FALSE); - - NM_SET_OUT(length, 0); - - if (dirfd >= 0) { - fd = openat(dirfd, filename, O_RDONLY | O_CLOEXEC); - if (fd < 0) { - return _get_contents_error_errno(error, - out_errsv, - "Failed to open file \"%s\" with openat", - filename); - } - } else { - fd = open(filename, O_RDONLY | O_CLOEXEC); - if (fd < 0) { - return _get_contents_error_errno(error, - out_errsv, - "Failed to open file \"%s\"", - filename); - } - } - return nm_utils_fd_get_contents(fd, - TRUE, - max_length, - flags, - contents, - length, - out_errsv, - error); -} - -/*****************************************************************************/ - -/* - * Copied from GLib's g_file_set_contents() et al., but allows - * specifying a mode for the new file. - */ -gboolean -nm_utils_file_set_contents(const char *filename, - const char *contents, - gssize length, - mode_t mode, - int * out_errsv, - GError ** error) -{ - gs_free char *tmp_name = NULL; - struct stat statbuf; - int errsv; - gssize s; - int fd; - - g_return_val_if_fail(filename, FALSE); - g_return_val_if_fail(contents || !length, FALSE); - g_return_val_if_fail(!error || !*error, FALSE); - g_return_val_if_fail(length >= -1, FALSE); - - if (length == -1) - length = strlen(contents); - - tmp_name = g_strdup_printf("%s.XXXXXX", filename); - fd = g_mkstemp_full(tmp_name, O_RDWR | O_CLOEXEC, mode); - if (fd < 0) { - return _get_contents_error_errno(error, out_errsv, "failed to create file %s", tmp_name); - } - - while (length > 0) { - s = write(fd, contents, length); - if (s < 0) { - errsv = NM_ERRNO_NATIVE(errno); - if (errsv == EINTR) - continue; - - nm_close(fd); - unlink(tmp_name); - return _get_contents_error(error, - errsv, - out_errsv, - "failed to write to file %s", - tmp_name); - } - - g_assert(s <= length); - - contents += s; - length -= s; - } - - /* If the final destination exists and is > 0 bytes, we want to sync the - * newly written file to ensure the data is on disk when we rename over - * the destination. Otherwise, if we get a system crash we can lose both - * the new and the old file on some filesystems. (I.E. those that don't - * guarantee the data is written to the disk before the metadata.) - */ - if (lstat(filename, &statbuf) == 0 && statbuf.st_size > 0) { - if (fsync(fd) != 0) { - errsv = NM_ERRNO_NATIVE(errno); - nm_close(fd); - unlink(tmp_name); - return _get_contents_error(error, errsv, out_errsv, "failed to fsync %s", tmp_name); - } - } - - nm_close(fd); - - if (rename(tmp_name, filename)) { - errsv = NM_ERRNO_NATIVE(errno); - unlink(tmp_name); - return _get_contents_error(error, - errsv, - out_errsv, - "failed rename %s to %s", - tmp_name, - filename); - } - - return TRUE; -} - -/** - * nm_utils_file_stat: - * @filename: the filename to stat. - * @out_st: (allow-none) (out): if given, this will be passed to stat(). - * - * Just wraps stat() and gives the errno number as function result instead - * of setting the errno (though, errno is also set). It's only for convenience - * with - * - * if (nm_utils_file_stat (filename, NULL) == -ENOENT) { - * } - * - * Returns: 0 on success a negative errno on failure. */ -int -nm_utils_file_stat(const char *filename, struct stat *out_st) -{ - struct stat st; - - if (stat(filename, out_st ?: &st) != 0) - return -NM_ERRNO_NATIVE(errno); - return 0; -} - -/** - * nm_utils_fd_read: - * @fd: the fd to read from. - * @out_string: (out): output string where read bytes will be stored. - * - * Returns: <0 on failure, which is -(errno). - * 0 on EOF. - * >0 on success, which is the number of bytes read. */ -gssize -nm_utils_fd_read(int fd, NMStrBuf *out_string) -{ - gsize buf_available; - gssize n_read; - int errsv; - - g_return_val_if_fail(fd >= 0, -1); - g_return_val_if_fail(out_string, -1); - - /* If the buffer size is 0, we allocate NM_UTILS_GET_NEXT_REALLOC_SIZE_1000 (1000 bytes) - * the first time. Afterwards, the buffer grows exponentially. - * - * Note that with @buf_available, we always would read as much buffer as we actually - * have reserved. */ - nm_str_buf_maybe_expand(out_string, NM_UTILS_GET_NEXT_REALLOC_SIZE_1000, FALSE); - - buf_available = out_string->allocated - out_string->len; - - n_read = read(fd, &((nm_str_buf_get_str_unsafe(out_string))[out_string->len]), buf_available); - if (n_read < 0) { - errsv = errno; - return -NM_ERRNO_NATIVE(errsv); - } - - if (n_read > 0) { - nm_assert((gsize) n_read <= buf_available); - nm_str_buf_set_size(out_string, out_string->len + (gsize) n_read, TRUE, FALSE); - } - - return n_read; -} diff --git a/shared/nm-glib-aux/nm-io-utils.h b/shared/nm-glib-aux/nm-io-utils.h deleted file mode 100644 index 8182f5cf..00000000 --- a/shared/nm-glib-aux/nm-io-utils.h +++ /dev/null @@ -1,58 +0,0 @@ -/* SPDX-License-Identifier: LGPL-2.1-or-later */ -/* - * Copyright (C) 2018 Red Hat, Inc. - */ - -#ifndef __NM_IO_UTILS_H__ -#define __NM_IO_UTILS_H__ - -#include "nm-macros-internal.h" - -/*****************************************************************************/ - -/** - * NMUtilsFileGetContentsFlags: - * @NM_UTILS_FILE_GET_CONTENTS_FLAG_NONE: no flag - * @NM_UTILS_FILE_GET_CONTENTS_FLAG_SECRET: if present, ensure that no - * data is left in memory. Essentially, it means to call nm_explicit_bzero() - * to not leave key material on the heap (when reading secrets). - */ -typedef enum { - NM_UTILS_FILE_GET_CONTENTS_FLAG_NONE = 0, - NM_UTILS_FILE_GET_CONTENTS_FLAG_SECRET = (1 << 0), -} NMUtilsFileGetContentsFlags; - -gboolean nm_utils_fd_get_contents(int fd, - gboolean close_fd, - gsize max_length, - NMUtilsFileGetContentsFlags flags, - char ** contents, - gsize * length, - int * out_errsv, - GError ** error); - -gboolean nm_utils_file_get_contents(int dirfd, - const char * filename, - gsize max_length, - NMUtilsFileGetContentsFlags flags, - char ** contents, - gsize * length, - int * out_errsv, - GError ** error); - -gboolean nm_utils_file_set_contents(const char *filename, - const char *contents, - gssize length, - mode_t mode, - int * out_errsv, - GError ** error); - -struct _NMStrBuf; - -gssize nm_utils_fd_read(int fd, struct _NMStrBuf *out_string); - -struct stat; - -int nm_utils_file_stat(const char *filename, struct stat *out_st); - -#endif /* __NM_IO_UTILS_H__ */ diff --git a/shared/nm-glib-aux/nm-jansson.h b/shared/nm-glib-aux/nm-jansson.h deleted file mode 100644 index 6173a7ac..00000000 --- a/shared/nm-glib-aux/nm-jansson.h +++ /dev/null @@ -1,30 +0,0 @@ -/* SPDX-License-Identifier: LGPL-2.1-or-later */ -/* - * Copyright (C) 2018 Red Hat, Inc. - */ - -#ifndef __NM_JANSSON_H__ -#define __NM_JANSSON_H__ - -/* you need to include at least "config.h" first, possibly "nm-default.h". */ - -#if WITH_JANSSON - - #include <jansson.h> - - /* Added in Jansson v2.8 */ - #ifndef json_object_foreach_safe - #define json_object_foreach_safe(object, n, key, value) \ - for (key = json_object_iter_key(json_object_iter(object)), \ - n = json_object_iter_next(object, json_object_key_to_iter(key)); \ - key && (value = json_object_iter_value(json_object_key_to_iter(key))); \ - key = json_object_iter_key(n), \ - n = json_object_iter_next(object, json_object_key_to_iter(key))) - #endif - -NM_AUTO_DEFINE_FCN0(json_t *, _nm_auto_decref_json, json_decref); - #define nm_auto_decref_json nm_auto(_nm_auto_decref_json) - -#endif /* WITH_JANSON */ - -#endif /* __NM_JANSSON_H__ */ diff --git a/shared/nm-glib-aux/nm-json-aux.c b/shared/nm-glib-aux/nm-json-aux.c deleted file mode 100644 index 97ee606f..00000000 --- a/shared/nm-glib-aux/nm-json-aux.c +++ /dev/null @@ -1,286 +0,0 @@ -/* SPDX-License-Identifier: LGPL-2.1-or-later */ -/* - * Copyright (C) 2017 - 2019 Red Hat, Inc. - */ - -#include "nm-glib-aux/nm-default-glib-i18n-lib.h" - -#include "nm-json-aux.h" - -#include <dlfcn.h> - -/*****************************************************************************/ - -/* If RTLD_DEEPBIND isn't available just ignore it. This can cause problems - * with jansson, json-glib, and cjson symbols clashing (and as such crashing the - * program). But that needs to be fixed by the json libraries, and it is by adding - * symbol versioning in recent versions. */ -#ifndef RTLD_DEEPBIND - #define RTLD_DEEPBIND 0 -#endif - -/*****************************************************************************/ - -static void -_gstr_append_string_len(GString *gstr, const char *str, gsize len) -{ - g_string_append_c(gstr, '\"'); - - while (len > 0) { - gsize n; - const char *end; - gboolean valid; - - nm_assert(len > 0); - - valid = g_utf8_validate(str, len, &end); - - nm_assert(end && end >= str && end <= &str[len]); - - if (end > str) { - const char *s; - - for (s = str; s < end; s++) { - nm_assert(s[0] != '\0'); - - if (s[0] < 0x20) { - const char *text; - - switch (s[0]) { - case '\\': - text = "\\\\"; - break; - case '\"': - text = "\\\""; - break; - case '\b': - text = "\\b"; - break; - case '\f': - text = "\\f"; - break; - case '\n': - text = "\\n"; - break; - case '\r': - text = "\\r"; - break; - case '\t': - text = "\\t"; - break; - default: - g_string_append_printf(gstr, "\\u%04X", (guint) s[0]); - continue; - } - g_string_append(gstr, text); - continue; - } - - if (NM_IN_SET(s[0], '\\', '\"')) - g_string_append_c(gstr, '\\'); - g_string_append_c(gstr, s[0]); - } - } else - nm_assert(!valid); - - if (valid) { - nm_assert(end == &str[len]); - break; - } - - nm_assert(end < &str[len]); - - if (end[0] == '\0') { - /* there is a NUL byte in the string. Technically this is valid UTF-8, so we - * encode it there. However, this will likely result in a truncated string when - * parsing. */ - g_string_append(gstr, "\\u0000"); - } else { - /* the character is not valid UTF-8. There is nothing we can do about it, because - * JSON can only contain UTF-8 and even the escape sequences can only escape Unicode - * codepoints (but not binary). - * - * The argument is not a string (in any known encoding), hence we cannot represent - * it as a JSON string (which are unicode strings). - * - * Print an underscore instead of the invalid char :) */ - g_string_append_c(gstr, '_'); - } - - n = str - end; - nm_assert(n < len); - n++; - str += n; - len -= n; - } - - g_string_append_c(gstr, '\"'); -} - -void -nm_json_gstr_append_string_len(GString *gstr, const char *str, gsize n) -{ - g_return_if_fail(gstr); - - _gstr_append_string_len(gstr, str, n); -} - -void -nm_json_gstr_append_string(GString *gstr, const char *str) -{ - g_return_if_fail(gstr); - - if (!str) - g_string_append(gstr, "null"); - else - _gstr_append_string_len(gstr, str, strlen(str)); -} - -void -nm_json_gstr_append_obj_name(GString *gstr, const char *key, char start_container) -{ - g_return_if_fail(gstr); - g_return_if_fail(key); - - nm_json_gstr_append_string(gstr, key); - - if (start_container != '\0') { - nm_assert(NM_IN_SET(start_container, '[', '{')); - g_string_append_printf(gstr, ": %c ", start_container); - } else - g_string_append(gstr, ": "); -} - -/*****************************************************************************/ - -typedef struct { - NMJsonVt vt; - void * dl_handle; -} NMJsonVtInternal; - -static NMJsonVtInternal * -_nm_json_vt_internal_load(void) -{ - NMJsonVtInternal *v; - const char * soname; - void * handle; - - v = g_new0(NMJsonVtInternal, 1); - -#if WITH_JANSSON && defined(JANSSON_SONAME) - G_STATIC_ASSERT_EXPR(NM_STRLEN(JANSSON_SONAME) > 0); - nm_assert(strlen(JANSSON_SONAME) > 0); - soname = JANSSON_SONAME; -#elif !WITH_JANSSON && !defined(JANSSON_SONAME) - soname = NULL; -#else - #error "WITH_JANSON and JANSSON_SONAME are defined inconsistently." -#endif - - if (!soname) - return v; - - handle = dlopen(soname, - RTLD_LAZY | RTLD_LOCAL | RTLD_NODELETE -#if !defined(ASAN_BUILD) - | RTLD_DEEPBIND -#endif - | 0); - if (!handle) - return v; - -#define TRY_BIND_SYMBOL(symbol) \ - G_STMT_START \ - { \ - void *_sym = dlsym(handle, #symbol); \ - \ - if (!_sym) \ - goto fail_symbol; \ - v->vt.nm_##symbol = _sym; \ - } \ - G_STMT_END - - TRY_BIND_SYMBOL(json_array); - TRY_BIND_SYMBOL(json_array_append_new); - TRY_BIND_SYMBOL(json_array_get); - TRY_BIND_SYMBOL(json_array_size); - TRY_BIND_SYMBOL(json_delete); - TRY_BIND_SYMBOL(json_dumps); - TRY_BIND_SYMBOL(json_false); - TRY_BIND_SYMBOL(json_integer); - TRY_BIND_SYMBOL(json_integer_value); - TRY_BIND_SYMBOL(json_loads); - TRY_BIND_SYMBOL(json_object); - TRY_BIND_SYMBOL(json_object_del); - TRY_BIND_SYMBOL(json_object_get); - TRY_BIND_SYMBOL(json_object_iter); - TRY_BIND_SYMBOL(json_object_iter_key); - TRY_BIND_SYMBOL(json_object_iter_next); - TRY_BIND_SYMBOL(json_object_iter_value); - TRY_BIND_SYMBOL(json_object_key_to_iter); - TRY_BIND_SYMBOL(json_object_set_new); - TRY_BIND_SYMBOL(json_object_size); - TRY_BIND_SYMBOL(json_string); - TRY_BIND_SYMBOL(json_string_value); - TRY_BIND_SYMBOL(json_true); - - v->vt.loaded = TRUE; - v->dl_handle = handle; - return v; - -fail_symbol: - dlclose(&handle); - *v = (NMJsonVtInternal){}; - return v; -} - -const NMJsonVt *_nm_json_vt_ptr = NULL; - -const NMJsonVt * -_nm_json_vt_init(void) -{ - NMJsonVtInternal *v; - -again: - v = g_atomic_pointer_get((gpointer *) &_nm_json_vt_ptr); - if (G_UNLIKELY(!v)) { - v = _nm_json_vt_internal_load(); - if (!g_atomic_pointer_compare_and_exchange((gpointer *) &_nm_json_vt_ptr, NULL, v)) { - if (v->dl_handle) - dlclose(v->dl_handle); - g_free(v); - goto again; - } - - /* we transfer ownership. */ - } - - nm_assert(v && v == g_atomic_pointer_get((gpointer *) &_nm_json_vt_ptr)); - return &v->vt; -} - -const NMJsonVt * -nmtst_json_vt_reset(gboolean loaded) -{ - NMJsonVtInternal *v_old; - NMJsonVtInternal *v; - - v_old = g_atomic_pointer_get((gpointer *) &_nm_json_vt_ptr); - - if (!loaded) { - /* load a fake instance for testing. */ - v = g_new0(NMJsonVtInternal, 1); - } else - v = _nm_json_vt_internal_load(); - - if (!g_atomic_pointer_compare_and_exchange((gpointer *) &_nm_json_vt_ptr, v_old, v)) - g_assert_not_reached(); - - if (v_old) { - if (v_old->dl_handle) - dlclose(v_old->dl_handle); - g_free((gpointer *) v_old); - } - - return v->vt.loaded ? &v->vt : NULL; -} diff --git a/shared/nm-glib-aux/nm-json-aux.h b/shared/nm-glib-aux/nm-json-aux.h deleted file mode 100644 index 99759a8d..00000000 --- a/shared/nm-glib-aux/nm-json-aux.h +++ /dev/null @@ -1,312 +0,0 @@ -/* SPDX-License-Identifier: LGPL-2.1-or-later */ -/* - * Copyright (C) 2017 - 2019 Red Hat, Inc. - */ - -#ifndef __NM_JSON_AUX_H__ -#define __NM_JSON_AUX_H__ - -#include "nm-value-type.h" - -/*****************************************************************************/ - -static inline GString * -nm_json_gstr_append_delimiter(GString *gstr) -{ - g_string_append(gstr, ", "); - return gstr; -} - -void nm_json_gstr_append_string_len(GString *gstr, const char *str, gsize n); - -void nm_json_gstr_append_string(GString *gstr, const char *str); - -static inline void -nm_json_gstr_append_bool(GString *gstr, gboolean v) -{ - g_string_append(gstr, v ? "true" : "false"); -} - -static inline void -nm_json_gstr_append_int64(GString *gstr, gint64 v) -{ - g_string_append_printf(gstr, "%" G_GINT64_FORMAT, v); -} - -void nm_json_gstr_append_obj_name(GString *gstr, const char *key, char start_container); - -/*****************************************************************************/ - -#define NM_JSON_REJECT_DUPLICATES 0x1 - -typedef enum { - NM_JSON_OBJECT, - NM_JSON_ARRAY, - NM_JSON_STRING, - NM_JSON_INTEGER, - NM_JSON_REAL, - NM_JSON_TRUE, - NM_JSON_FALSE, - NM_JSON_NULL, -} nm_json_type; - -typedef struct nm_json_t { - nm_json_type type; - volatile size_t refcount; -} nm_json_t; - -typedef long long nm_json_int_t; - -#define NM_JSON_ERROR_TEXT_LENGTH 160 -#define NM_JSON_ERROR_SOURCE_LENGTH 80 - -typedef struct nm_json_error_t { - int line; - int column; - int position; - char source[NM_JSON_ERROR_SOURCE_LENGTH]; - char text[NM_JSON_ERROR_TEXT_LENGTH]; -} nm_json_error_t; - -typedef struct { - gboolean loaded; - char *(*nm_json_dumps)(const nm_json_t *json, size_t flags); - const char *(*nm_json_object_iter_key)(void *iter); - const char *(*nm_json_string_value)(const nm_json_t *json); - int (*nm_json_array_append_new)(nm_json_t *json, nm_json_t *value); - int (*nm_json_object_del)(nm_json_t *json, const char *key); - int (*nm_json_object_set_new)(nm_json_t *json, const char *key, nm_json_t *value); - nm_json_int_t (*nm_json_integer_value)(const nm_json_t *json); - nm_json_t *(*nm_json_array)(void); - nm_json_t *(*nm_json_array_get)(const nm_json_t *json, size_t index); - nm_json_t *(*nm_json_false)(void); - nm_json_t *(*nm_json_integer)(nm_json_int_t value); - nm_json_t *(*nm_json_loads)(const char *string, size_t flags, nm_json_error_t *error); - nm_json_t *(*nm_json_object)(void); - nm_json_t *(*nm_json_object_get)(const nm_json_t *json, const char *key); - nm_json_t *(*nm_json_object_iter_value)(void *); - nm_json_t *(*nm_json_string)(const char *value); - nm_json_t *(*nm_json_true)(void); - size_t (*nm_json_array_size)(const nm_json_t *json); - size_t (*nm_json_object_size)(const nm_json_t *json); - void (*nm_json_delete)(nm_json_t *json); - void *(*nm_json_object_iter)(nm_json_t *json); - void *(*nm_json_object_iter_next)(nm_json_t *json, void *iter); - void *(*nm_json_object_key_to_iter)(const char *key); -} NMJsonVt; - -extern const NMJsonVt *_nm_json_vt_ptr; - -const NMJsonVt *_nm_json_vt_init(void); - -static inline const NMJsonVt * -_nm_json_vt(void) -{ - const NMJsonVt *vt; - - vt = g_atomic_pointer_get((gpointer *) &_nm_json_vt_ptr); - if (G_UNLIKELY(!vt)) { - vt = _nm_json_vt_init(); - nm_assert(vt); - } - return vt; -} - -static inline const NMJsonVt * -nm_json_vt(void) -{ - const NMJsonVt *vt; - - vt = _nm_json_vt(); - return vt->loaded ? vt : NULL; -} - -static inline const NMJsonVt * -nm_json_vt_assert(void) -{ - const NMJsonVt *vt; - - vt = _nm_json_vt(); - nm_assert(vt->loaded); - return vt; -} - -const NMJsonVt *nmtst_json_vt_reset(gboolean loaded); - -/*****************************************************************************/ - -#define nm_json_boolean(vt, val) ((val) ? (vt)->nm_json_true() : (vt)->nm_json_false()) - -static inline void -nm_json_decref(const NMJsonVt *vt, nm_json_t *json) -{ - /* Our ref-counting is not threadsafe, unlike libjansson's. But we never - * share one json_t instance between threads, and if we would, we would very likely - * wrap a mutex around it. */ - if (json && json->refcount != (size_t) -1 && --json->refcount == 0) - vt->nm_json_delete(json); -} - -static inline void -_nm_auto_decref_json(nm_json_t **p_json) -{ - if (*p_json && (*p_json)->refcount != (size_t) -1 && --(*p_json)->refcount == 0) - nm_json_vt()->nm_json_delete(*p_json); -} - -#define nm_auto_decref_json nm_auto(_nm_auto_decref_json) - -/*****************************************************************************/ - -/* the following are implemented as pure macros in jansson.h. - * They can be used directly, however, add a nm_json* variant, - * to make it explict we don't accidentally use jansson ABI. */ - -#define nm_json_typeof(json) ((json)->type) -#define nm_json_is_object(json) ((json) && nm_json_typeof(json) == NM_JSON_OBJECT) -#define nm_json_is_array(json) ((json) && nm_json_typeof(json) == NM_JSON_ARRAY) -#define nm_json_is_string(json) ((json) && nm_json_typeof(json) == NM_JSON_STRING) -#define nm_json_is_integer(json) ((json) && nm_json_typeof(json) == NM_JSON_INTEGER) -#define nm_json_is_real(json) ((json) && nm_json_typeof(json) == NM_JSON_REAL) -#define nm_json_is_number(json) (nm_json_is_integer(json) || nm_json_is_real(json)) -#define nm_json_is_true(json) ((json) && nm_json_typeof(json) == NM_JSON_TRUE) -#define nm_json_is_false(json) ((json) && nm_json_typeof(json) == NM_JSON_FALSE) -#define nm_json_boolean_value nm_json_is_true -#define nm_json_is_boolean(json) (nm_json_is_true(json) || nm_json_is_false(json)) -#define nm_json_is_null(json) ((json) && nm_json_typeof(json) == NM_JSON_NULL) - -#define nm_json_array_foreach(vt, array, index, value) \ - for (index = 0; \ - index < vt->nm_json_array_size(array) && (value = vt->nm_json_array_get(array, index)); \ - index++) - -#define nm_json_object_foreach(vt, object, key, value) \ - for (key = vt->nm_json_object_iter_key(vt->nm_json_object_iter(object)); \ - key && (value = vt->nm_json_object_iter_value(vt->nm_json_object_key_to_iter(key))); \ - key = vt->nm_json_object_iter_key( \ - vt->nm_json_object_iter_next(object, vt->nm_json_object_key_to_iter(key)))) - -/*****************************************************************************/ - -static inline int -nm_jansson_json_as_bool(const nm_json_t *elem, bool *out_val) -{ - if (!elem) - return 0; - - if (!nm_json_is_boolean(elem)) - return -EINVAL; - - NM_SET_OUT(out_val, nm_json_boolean_value(elem)); - return 1; -} - -static inline int -nm_jansson_json_as_int32(const NMJsonVt *vt, const nm_json_t *elem, gint32 *out_val) -{ - nm_json_int_t v; - - if (!elem) - return 0; - - if (!nm_json_is_integer(elem)) - return -EINVAL; - - v = vt->nm_json_integer_value(elem); - if (v < (gint64) G_MININT32 || v > (gint64) G_MAXINT32) - return -ERANGE; - - NM_SET_OUT(out_val, v); - return 1; -} - -static inline int -nm_jansson_json_as_int(const NMJsonVt *vt, const nm_json_t *elem, int *out_val) -{ - nm_json_int_t v; - - if (!elem) - return 0; - - if (!nm_json_is_integer(elem)) - return -EINVAL; - - v = vt->nm_json_integer_value(elem); - if (v < (gint64) G_MININT || v > (gint64) G_MAXINT) - return -ERANGE; - - NM_SET_OUT(out_val, v); - return 1; -} - -static inline int -nm_jansson_json_as_string(const NMJsonVt *vt, const nm_json_t *elem, const char **out_val) -{ - if (!elem) - return 0; - - if (!nm_json_is_string(elem)) - return -EINVAL; - - NM_SET_OUT(out_val, vt->nm_json_string_value(elem)); - return 1; -} - -/*****************************************************************************/ - -#ifdef NM_VALUE_TYPE_DEFINE_FUNCTIONS - -static inline void -nm_value_type_to_json(NMValueType value_type, GString *gstr, gconstpointer p_field) -{ - nm_assert(p_field); - nm_assert(gstr); - - switch (value_type) { - case NM_VALUE_TYPE_BOOL: - nm_json_gstr_append_bool(gstr, *((const bool *) p_field)); - return; - case NM_VALUE_TYPE_INT32: - nm_json_gstr_append_int64(gstr, *((const gint32 *) p_field)); - return; - case NM_VALUE_TYPE_INT: - nm_json_gstr_append_int64(gstr, *((const int *) p_field)); - return; - case NM_VALUE_TYPE_STRING: - nm_json_gstr_append_string(gstr, *((const char *const *) p_field)); - return; - case NM_VALUE_TYPE_UNSPEC: - break; - } - nm_assert_not_reached(); -} - -static inline gboolean -nm_value_type_from_json(const NMJsonVt * vt, - NMValueType value_type, - const nm_json_t *elem, - gpointer out_val) -{ - switch (value_type) { - case NM_VALUE_TYPE_BOOL: - return (nm_jansson_json_as_bool(elem, out_val) > 0); - case NM_VALUE_TYPE_INT32: - return (nm_jansson_json_as_int32(vt, elem, out_val) > 0); - case NM_VALUE_TYPE_INT: - return (nm_jansson_json_as_int(vt, elem, out_val) > 0); - - /* warning: this overwrites/leaks the previous value. You better have *out_val - * point to uninitialized memory or NULL. */ - case NM_VALUE_TYPE_STRING: - return (nm_jansson_json_as_string(vt, elem, out_val) > 0); - - case NM_VALUE_TYPE_UNSPEC: - break; - } - nm_assert_not_reached(); - return FALSE; -} - -#endif /* NM_VALUE_TYPE_DEFINE_FUNCTIONS */ - -#endif /* __NM_JSON_AUX_H__ */ diff --git a/shared/nm-glib-aux/nm-keyfile-aux.c b/shared/nm-glib-aux/nm-keyfile-aux.c deleted file mode 100644 index b5962712..00000000 --- a/shared/nm-glib-aux/nm-keyfile-aux.c +++ /dev/null @@ -1,373 +0,0 @@ -/* SPDX-License-Identifier: LGPL-2.1-or-later */ -/* - * Copyright (C) 2019 Red Hat, Inc. - */ - -#include "nm-glib-aux/nm-default-glib-i18n-lib.h" - -#include "nm-keyfile-aux.h" - -#include <syslog.h> -#include <sys/stat.h> -#include <fcntl.h> - -#include "nm-io-utils.h" - -/*****************************************************************************/ - -struct _NMKeyFileDB { - NMKeyFileDBLogFcn log_fcn; - NMKeyFileDBGotDirtyFcn got_dirty_fcn; - gpointer user_data; - const char * group_name; - GKeyFile * kf; - guint ref_count; - - bool is_started : 1; - bool dirty : 1; - bool destroyed : 1; - - char filename[]; -}; - -#define _NMLOG(self, syslog_level, fmt, ...) \ - G_STMT_START \ - { \ - NMKeyFileDB *_self = (self); \ - \ - nm_assert(_self); \ - nm_assert(!_self->destroyed); \ - \ - if (_self->log_fcn) { \ - _self->log_fcn(_self, (syslog_level), _self->user_data, "" fmt "", ##__VA_ARGS__); \ - }; \ - } \ - G_STMT_END - -#define _LOGD(...) _NMLOG(self, LOG_DEBUG, __VA_ARGS__) - -static gboolean -_IS_KEY_FILE_DB(NMKeyFileDB *self, gboolean require_is_started, gboolean allow_destroyed) -{ - if (self == NULL) - return FALSE; - if (self->ref_count <= 0) { - nm_assert_not_reached(); - return FALSE; - } - if (require_is_started && !self->is_started) - return FALSE; - if (!allow_destroyed && self->destroyed) - return FALSE; - return TRUE; -} - -/*****************************************************************************/ - -NMKeyFileDB * -nm_key_file_db_new(const char * filename, - const char * group_name, - NMKeyFileDBLogFcn log_fcn, - NMKeyFileDBGotDirtyFcn got_dirty_fcn, - gpointer user_data) -{ - NMKeyFileDB *self; - gsize l_filename; - gsize l_group; - - g_return_val_if_fail(filename && filename[0], NULL); - g_return_val_if_fail(group_name && group_name[0], NULL); - - l_filename = strlen(filename); - l_group = strlen(group_name); - - self = g_malloc0(sizeof(NMKeyFileDB) + l_filename + 1 + l_group + 1); - self->ref_count = 1; - self->log_fcn = log_fcn; - self->got_dirty_fcn = got_dirty_fcn; - self->user_data = user_data; - self->kf = g_key_file_new(); - g_key_file_set_list_separator(self->kf, ','); - memcpy(self->filename, filename, l_filename + 1); - self->group_name = &self->filename[l_filename + 1]; - memcpy((char *) self->group_name, group_name, l_group + 1); - - return self; -} - -NMKeyFileDB * -nm_key_file_db_ref(NMKeyFileDB *self) -{ - if (!self) - return NULL; - - g_return_val_if_fail(_IS_KEY_FILE_DB(self, FALSE, TRUE), NULL); - - nm_assert(self->ref_count < G_MAXUINT); - self->ref_count++; - return self; -} - -void -nm_key_file_db_unref(NMKeyFileDB *self) -{ - if (!self) - return; - - g_return_if_fail(_IS_KEY_FILE_DB(self, FALSE, TRUE)); - - if (--self->ref_count > 0) - return; - - g_key_file_unref(self->kf); - - g_free(self); -} - -/* destroy() is like unref, but it also makes the instance unusable. - * All changes afterwards fail with an assertion. - * - * The point is that NMKeyFileDB is ref-counted in principle. But there - * is a primary owner who also provides the log_fcn(). - * - * When the primary owner goes out of scope and gives up the reference, it does - * not want to receive any log notifications anymore. - * - * The way NMKeyFileDB is intended to be used is in a very strict context: - * NMSettings owns the NMKeyFileDB instance and receives logging notifications. - * It's also the last one to persist the data to disk. Afterwards, no other user - * is supposed to be around and do anything with NMKeyFileDB. But since NMKeyFileDB - * is ref-counted it's hard to ensure that this is truly honored. So we start - * asserting at that point. - */ -void -nm_key_file_db_destroy(NMKeyFileDB *self) -{ - if (!self) - return; - - g_return_if_fail(_IS_KEY_FILE_DB(self, FALSE, FALSE)); - g_return_if_fail(!self->destroyed); - - self->destroyed = TRUE; - nm_key_file_db_unref(self); -} - -/*****************************************************************************/ - -/* nm_key_file_db_start() is supposed to be called right away, after creating the - * instance. - * - * It's not done as separate step after nm_key_file_db_new(), because we want to log, - * and the log_fcn returns the self pointer (which we should not expose before - * nm_key_file_db_new() returns. */ -void -nm_key_file_db_start(NMKeyFileDB *self) -{ - gs_free char *contents = NULL; - gsize contents_len; - gs_free_error GError *error = NULL; - - g_return_if_fail(_IS_KEY_FILE_DB(self, FALSE, FALSE)); - g_return_if_fail(!self->is_started); - - self->is_started = TRUE; - - if (!nm_utils_file_get_contents(-1, - self->filename, - 20 * 1024 * 1024, - NM_UTILS_FILE_GET_CONTENTS_FLAG_NONE, - &contents, - &contents_len, - NULL, - &error)) { - _LOGD("failed to read \"%s\": %s", self->filename, error->message); - return; - } - - if (!g_key_file_load_from_data(self->kf, - contents, - contents_len, - G_KEY_FILE_KEEP_COMMENTS, - &error)) { - _LOGD("failed to load keyfile \"%s\": %s", self->filename, error->message); - return; - } - - _LOGD("loaded keyfile-db for \"%s\"", self->filename); -} - -/*****************************************************************************/ - -const char * -nm_key_file_db_get_filename(NMKeyFileDB *self) -{ - g_return_val_if_fail(_IS_KEY_FILE_DB(self, FALSE, TRUE), NULL); - - return self->filename; -} - -gboolean -nm_key_file_db_is_dirty(NMKeyFileDB *self) -{ - g_return_val_if_fail(_IS_KEY_FILE_DB(self, FALSE, TRUE), FALSE); - - return self->dirty; -} - -/*****************************************************************************/ - -char * -nm_key_file_db_get_value(NMKeyFileDB *self, const char *key) -{ - g_return_val_if_fail(_IS_KEY_FILE_DB(self, TRUE, TRUE), NULL); - - return g_key_file_get_value(self->kf, self->group_name, key, NULL); -} - -char ** -nm_key_file_db_get_string_list(NMKeyFileDB *self, const char *key, gsize *out_len) -{ - g_return_val_if_fail(_IS_KEY_FILE_DB(self, TRUE, TRUE), NULL); - - return g_key_file_get_string_list(self->kf, self->group_name, key, out_len, NULL); -} - -/*****************************************************************************/ - -static void -_got_dirty(NMKeyFileDB *self, const char *key) -{ - nm_assert(_IS_KEY_FILE_DB(self, TRUE, FALSE)); - nm_assert(!self->dirty); - - _LOGD("updated entry for %s.%s", self->group_name, key); - - self->dirty = TRUE; - if (self->got_dirty_fcn) - self->got_dirty_fcn(self, self->user_data); -} - -/*****************************************************************************/ - -void -nm_key_file_db_remove_key(NMKeyFileDB *self, const char *key) -{ - gboolean got_dirty = FALSE; - - g_return_if_fail(_IS_KEY_FILE_DB(self, TRUE, FALSE)); - - if (!key) - return; - - if (!self->dirty) { - gs_free_error GError *error = NULL; - - g_key_file_has_key(self->kf, self->group_name, key, &error); - got_dirty = (error != NULL); - } - g_key_file_remove_key(self->kf, self->group_name, key, NULL); - - if (got_dirty) - _got_dirty(self, key); -} - -void -nm_key_file_db_set_value(NMKeyFileDB *self, const char *key, const char *value) -{ - gs_free char *old_value = NULL; - gboolean got_dirty = FALSE; - - g_return_if_fail(_IS_KEY_FILE_DB(self, TRUE, FALSE)); - g_return_if_fail(key); - - if (!value) { - nm_key_file_db_remove_key(self, key); - return; - } - - if (!self->dirty) { - gs_free_error GError *error = NULL; - - old_value = g_key_file_get_value(self->kf, self->group_name, key, &error); - if (error) - got_dirty = TRUE; - } - - g_key_file_set_value(self->kf, self->group_name, key, value); - - if (!self->dirty && !got_dirty) { - gs_free_error GError *error = NULL; - gs_free char * new_value = NULL; - - new_value = g_key_file_get_value(self->kf, self->group_name, key, &error); - if (error || !new_value || !nm_streq0(old_value, new_value)) - got_dirty = TRUE; - } - - if (got_dirty) - _got_dirty(self, key); -} - -void -nm_key_file_db_set_string_list(NMKeyFileDB * self, - const char * key, - const char *const *value, - gssize len) -{ - gs_free char *old_value = NULL; - gboolean got_dirty = FALSE; - - g_return_if_fail(_IS_KEY_FILE_DB(self, TRUE, FALSE)); - g_return_if_fail(key); - - if (!value) { - nm_key_file_db_remove_key(self, key); - return; - } - - if (!self->dirty) { - gs_free_error GError *error = NULL; - - old_value = g_key_file_get_value(self->kf, self->group_name, key, &error); - if (error) - got_dirty = TRUE; - } - - if (len < 0) - len = NM_PTRARRAY_LEN(value); - - g_key_file_set_string_list(self->kf, self->group_name, key, value, len); - - if (!self->dirty && !got_dirty) { - gs_free_error GError *error = NULL; - gs_free char * new_value = NULL; - - new_value = g_key_file_get_value(self->kf, self->group_name, key, &error); - if (error || !new_value || !nm_streq0(old_value, new_value)) - got_dirty = TRUE; - } - - if (got_dirty) - _got_dirty(self, key); -} - -/*****************************************************************************/ - -void -nm_key_file_db_to_file(NMKeyFileDB *self, gboolean force) -{ - gs_free_error GError *error = NULL; - - g_return_if_fail(_IS_KEY_FILE_DB(self, TRUE, FALSE)); - - if (!force && !self->dirty) - return; - - self->dirty = FALSE; - - if (!g_key_file_save_to_file(self->kf, self->filename, &error)) { - _LOGD("failure to write keyfile \"%s\": %s", self->filename, error->message); - } else - _LOGD("write keyfile: \"%s\"", self->filename); -} diff --git a/shared/nm-glib-aux/nm-keyfile-aux.h b/shared/nm-glib-aux/nm-keyfile-aux.h deleted file mode 100644 index 72d2f418..00000000 --- a/shared/nm-glib-aux/nm-keyfile-aux.h +++ /dev/null @@ -1,55 +0,0 @@ -/* SPDX-License-Identifier: LGPL-2.1-or-later */ -/* - * Copyright (C) 2019 Red Hat, Inc. - */ - -#ifndef __NM_KEYFILE_AUX_H__ -#define __NM_KEYFILE_AUX_H__ - -/*****************************************************************************/ - -typedef struct _NMKeyFileDB NMKeyFileDB; - -typedef void (*NMKeyFileDBLogFcn)(NMKeyFileDB *self, - int syslog_level, - gpointer user_data, - const char * fmt, - ...) G_GNUC_PRINTF(4, 5); - -typedef void (*NMKeyFileDBGotDirtyFcn)(NMKeyFileDB *self, gpointer user_data); - -NMKeyFileDB *nm_key_file_db_new(const char * filename, - const char * group, - NMKeyFileDBLogFcn log_fcn, - NMKeyFileDBGotDirtyFcn got_dirty_fcn, - gpointer user_data); - -void nm_key_file_db_start(NMKeyFileDB *self); - -NMKeyFileDB *nm_key_file_db_ref(NMKeyFileDB *self); -void nm_key_file_db_unref(NMKeyFileDB *self); - -void nm_key_file_db_destroy(NMKeyFileDB *self); - -const char *nm_key_file_db_get_filename(NMKeyFileDB *self); - -gboolean nm_key_file_db_is_dirty(NMKeyFileDB *self); - -char *nm_key_file_db_get_value(NMKeyFileDB *self, const char *key); - -char **nm_key_file_db_get_string_list(NMKeyFileDB *self, const char *key, gsize *out_len); - -void nm_key_file_db_remove_key(NMKeyFileDB *self, const char *key); - -void nm_key_file_db_set_value(NMKeyFileDB *self, const char *key, const char *value); - -void nm_key_file_db_set_string_list(NMKeyFileDB * self, - const char * key, - const char *const *value, - gssize len); - -void nm_key_file_db_to_file(NMKeyFileDB *self, gboolean force); - -/*****************************************************************************/ - -#endif /* __NM_KEYFILE_AUX_H__ */ diff --git a/shared/nm-glib-aux/nm-logging-base.c b/shared/nm-glib-aux/nm-logging-base.c deleted file mode 100644 index 66b591b2..00000000 --- a/shared/nm-glib-aux/nm-logging-base.c +++ /dev/null @@ -1,79 +0,0 @@ -/* SPDX-License-Identifier: LGPL-2.1-or-later */ - -#include "nm-glib-aux/nm-default-glib-i18n-lib.h" - -#include "nm-logging-base.h" - -#include <syslog.h> - -/*****************************************************************************/ - -const LogLevelDesc level_desc[_LOGL_N] = { - [LOGL_TRACE] = - { - "TRACE", - "<trace>", - LOG_DEBUG, - G_LOG_LEVEL_DEBUG, - }, - [LOGL_DEBUG] = - { - "DEBUG", - "<debug>", - LOG_DEBUG, - G_LOG_LEVEL_DEBUG, - }, - [LOGL_INFO] = - { - "INFO", - "<info>", - LOG_INFO, - G_LOG_LEVEL_INFO, - }, - [LOGL_WARN] = - { - "WARN", - "<warn>", - LOG_WARNING, - G_LOG_LEVEL_MESSAGE, - }, - [LOGL_ERR] = - { - "ERR", - "<error>", - LOG_ERR, - G_LOG_LEVEL_MESSAGE, - }, - [_LOGL_OFF] = - { - "OFF", - NULL, - 0, - 0, - }, - [_LOGL_KEEP] = - { - "KEEP", - NULL, - 0, - 0, - }, -}; - -gboolean -_nm_log_parse_level(const char *level, NMLogLevel *out_level) -{ - int i; - - if (!level) - return FALSE; - - for (i = 0; i < (int) G_N_ELEMENTS(level_desc); i++) { - if (!g_ascii_strcasecmp(level_desc[i].name, level)) { - NM_SET_OUT(out_level, i); - return TRUE; - } - } - - return FALSE; -} diff --git a/shared/nm-glib-aux/nm-logging-base.h b/shared/nm-glib-aux/nm-logging-base.h deleted file mode 100644 index d9ac03c7..00000000 --- a/shared/nm-glib-aux/nm-logging-base.h +++ /dev/null @@ -1,28 +0,0 @@ -/* SPDX-License-Identifier: LGPL-2.1-or-later */ - -#ifndef __NM_LOGGING_BASE_H__ -#define __NM_LOGGING_BASE_H__ - -#include "nm-logging-fwd.h" - -typedef struct { - const char *name; - const char *level_str; - - /* nm-logging uses syslog internally. Note that the three most-verbose syslog levels - * are LOG_DEBUG, LOG_INFO and LOG_NOTICE. Journal already highlights LOG_NOTICE - * as special. - * - * On the other hand, we have three levels LOGL_TRACE, LOGL_DEBUG and LOGL_INFO, - * which are regular messages not to be highlighted. For that reason, we must map - * LOGL_TRACE and LOGL_DEBUG both to syslog level LOG_DEBUG. */ - int syslog_level; - - GLogLevelFlags g_log_level; -} LogLevelDesc; - -extern const LogLevelDesc level_desc[_LOGL_N]; - -gboolean _nm_log_parse_level(const char *level, NMLogLevel *out_level); - -#endif /* __NM_LOGGING_BASE_H__ */ diff --git a/shared/nm-glib-aux/nm-logging-fwd.h b/shared/nm-glib-aux/nm-logging-fwd.h deleted file mode 100644 index df0bb161..00000000 --- a/shared/nm-glib-aux/nm-logging-fwd.h +++ /dev/null @@ -1,308 +0,0 @@ -/* SPDX-License-Identifier: LGPL-2.1-or-later */ -/* - * Copyright (C) 2006 - 2018 Red Hat, Inc. - * Copyright (C) 2006 - 2008 Novell, Inc. - */ - -#ifndef __NM_LOGGING_FWD_H__ -#define __NM_LOGGING_FWD_H__ - -/* Log domains */ - -typedef enum { /*< skip >*/ - LOGD_NONE = 0LL, - LOGD_PLATFORM = (1LL << 0), /* Platform services */ - LOGD_RFKILL = (1LL << 1), - LOGD_ETHER = (1LL << 2), - LOGD_WIFI = (1LL << 3), - LOGD_BT = (1LL << 4), - LOGD_MB = (1LL << 5), /* mobile broadband */ - LOGD_DHCP4 = (1LL << 6), - LOGD_DHCP6 = (1LL << 7), - LOGD_PPP = (1LL << 8), - LOGD_WIFI_SCAN = (1LL << 9), - LOGD_IP4 = (1LL << 10), - LOGD_IP6 = (1LL << 11), - LOGD_AUTOIP4 = (1LL << 12), - LOGD_DNS = (1LL << 13), - LOGD_VPN = (1LL << 14), - LOGD_SHARING = (1LL << 15), /* Connection sharing/dnsmasq */ - LOGD_SUPPLICANT = (1LL << 16), /* Wi-Fi and 802.1x */ - LOGD_AGENTS = (1LL << 17), /* Secret agents */ - LOGD_SETTINGS = (1LL << 18), /* Settings */ - LOGD_SUSPEND = (1LL << 19), /* Suspend/Resume */ - LOGD_CORE = (1LL << 20), /* Core daemon and policy stuff */ - LOGD_DEVICE = (1LL << 21), /* Device state and activation */ - LOGD_OLPC = (1LL << 22), - LOGD_INFINIBAND = (1LL << 23), - LOGD_FIREWALL = (1LL << 24), - LOGD_ADSL = (1LL << 25), - LOGD_BOND = (1LL << 26), - LOGD_VLAN = (1LL << 27), - LOGD_BRIDGE = (1LL << 28), - LOGD_DBUS_PROPS = (1LL << 29), - LOGD_TEAM = (1LL << 30), - LOGD_CONCHECK = (1LL << 31), - LOGD_DCB = (1LL << 32), /* Data Center Bridging */ - LOGD_DISPATCH = (1LL << 33), - LOGD_AUDIT = (1LL << 34), - LOGD_SYSTEMD = (1LL << 35), - LOGD_VPN_PLUGIN = (1LL << 36), - LOGD_PROXY = (1LL << 37), - - __LOGD_MAX, - LOGD_ALL = (((__LOGD_MAX - 1LL) << 1) - 1LL), - LOGD_DEFAULT = LOGD_ALL & ~(LOGD_DBUS_PROPS | LOGD_WIFI_SCAN | LOGD_VPN_PLUGIN | 0), - - /* aliases: */ - LOGD_DHCP = LOGD_DHCP4 | LOGD_DHCP6, - LOGD_IP = LOGD_IP4 | LOGD_IP6, - -#define LOGD_DHCPX(is_ipv4) ((is_ipv4) ? LOGD_DHCP4 : LOGD_DHCP6) -#define LOGD_IPX(is_ipv4) ((is_ipv4) ? LOGD_IP4 : LOGD_IP6) - -} NMLogDomain; - -/* Log levels */ -typedef enum { /*< skip >*/ - LOGL_TRACE, - LOGL_DEBUG, - LOGL_INFO, - LOGL_WARN, - LOGL_ERR, - - _LOGL_N_REAL, /* the number of actual logging levels */ - - _LOGL_OFF = _LOGL_N_REAL, /* special logging level that is always disabled. */ - _LOGL_KEEP, /* special logging level to indicate that the logging level should not be changed. */ - - _LOGL_N, /* the number of logging levels including "OFF" */ -} NMLogLevel; - -gboolean _nm_log_enabled_impl(gboolean mt_require_locking, NMLogLevel level, NMLogDomain domain); - -void _nm_log_impl(const char *file, - guint line, - const char *func, - gboolean mt_require_locking, - NMLogLevel level, - NMLogDomain domain, - int error, - const char *ifname, - const char *con_uuid, - const char *fmt, - ...) _nm_printf(10, 11); - -static inline NMLogLevel -nm_log_level_from_syslog(int syslog_level) -{ - switch (syslog_level) { - case 0 /* LOG_EMERG */: - return LOGL_ERR; - case 1 /* LOG_ALERT */: - return LOGL_ERR; - case 2 /* LOG_CRIT */: - return LOGL_ERR; - case 3 /* LOG_ERR */: - return LOGL_ERR; - case 4 /* LOG_WARNING */: - return LOGL_WARN; - case 5 /* LOG_NOTICE */: - return LOGL_INFO; - case 6 /* LOG_INFO */: - return LOGL_DEBUG; - case 7 /* LOG_DEBUG */: - return LOGL_TRACE; - default: - return syslog_level >= 0 ? LOGL_TRACE : LOGL_ERR; - } -} - -static inline int -nm_log_level_to_syslog(NMLogLevel nm_level) -{ - switch (nm_level) { - case LOGL_ERR: - return 3; /* LOG_ERR */ - case LOGL_WARN: - return 4; /* LOG_WARN */ - case LOGL_INFO: - return 5; /* LOG_NOTICE */ - case LOGL_DEBUG: - return 6; /* LOG_INFO */ - case LOGL_TRACE: - return 7; /* LOG_DEBUG */ - default: - return 0; /* LOG_EMERG */ - } -} - -/*****************************************************************************/ - -struct timespec; - -/* this function must be implemented to handle the notification when - * the first monotonic-timestamp is fetched. */ -extern void _nm_utils_monotonic_timestamp_initialized(const struct timespec *tp, - gint64 offset_sec, - gboolean is_boottime); - -/*****************************************************************************/ - -#define _LOGL_TRACE LOGL_TRACE -#define _LOGL_DEBUG LOGL_DEBUG -#define _LOGL_INFO LOGL_INFO -#define _LOGL_WARN LOGL_WARN -#define _LOGL_ERR LOGL_ERR - -/* This is the default definition of _NMLOG_ENABLED(). Special implementations - * might want to undef this and redefine it. */ -#define _NMLOG_ENABLED(level) (nm_logging_enabled((level), (_NMLOG_DOMAIN))) - -#define _LOGT(...) _NMLOG(_LOGL_TRACE, __VA_ARGS__) -#define _LOGD(...) _NMLOG(_LOGL_DEBUG, __VA_ARGS__) -#define _LOGI(...) _NMLOG(_LOGL_INFO, __VA_ARGS__) -#define _LOGW(...) _NMLOG(_LOGL_WARN, __VA_ARGS__) -#define _LOGE(...) _NMLOG(_LOGL_ERR, __VA_ARGS__) - -#define _LOGT_ENABLED(...) _NMLOG_ENABLED(_LOGL_TRACE, ##__VA_ARGS__) -#define _LOGD_ENABLED(...) _NMLOG_ENABLED(_LOGL_DEBUG, ##__VA_ARGS__) -#define _LOGI_ENABLED(...) _NMLOG_ENABLED(_LOGL_INFO, ##__VA_ARGS__) -#define _LOGW_ENABLED(...) _NMLOG_ENABLED(_LOGL_WARN, ##__VA_ARGS__) -#define _LOGE_ENABLED(...) _NMLOG_ENABLED(_LOGL_ERR, ##__VA_ARGS__) - -#define _LOGT_err(errsv, ...) _NMLOG_err(errsv, _LOGL_TRACE, __VA_ARGS__) -#define _LOGD_err(errsv, ...) _NMLOG_err(errsv, _LOGL_DEBUG, __VA_ARGS__) -#define _LOGI_err(errsv, ...) _NMLOG_err(errsv, _LOGL_INFO, __VA_ARGS__) -#define _LOGW_err(errsv, ...) _NMLOG_err(errsv, _LOGL_WARN, __VA_ARGS__) -#define _LOGE_err(errsv, ...) _NMLOG_err(errsv, _LOGL_ERR, __VA_ARGS__) - -/* _LOGT() and _LOGt() both log with level TRACE, but the latter is disabled by default, - * unless building with --with-more-logging. */ -#if NM_MORE_LOGGING - #define _LOGt_ENABLED(...) _NMLOG_ENABLED(_LOGL_TRACE, ##__VA_ARGS__) - #define _LOGt(...) _NMLOG(_LOGL_TRACE, __VA_ARGS__) - #define _LOGt_err(errsv, ...) _NMLOG_err(errsv, _LOGL_TRACE, __VA_ARGS__) -#else - /* still call the logging macros to get compile time checks, but they will be optimized out. */ - #define _LOGt_ENABLED(...) (FALSE && (_NMLOG_ENABLED(_LOGL_TRACE, ##__VA_ARGS__))) - #define _LOGt(...) \ - G_STMT_START \ - { \ - if (FALSE) { \ - _NMLOG(_LOGL_TRACE, __VA_ARGS__); \ - } \ - } \ - G_STMT_END - #define _LOGt_err(errsv, ...) \ - G_STMT_START \ - { \ - if (FALSE) { \ - _NMLOG_err(errsv, _LOGL_TRACE, __VA_ARGS__); \ - } \ - } \ - G_STMT_END -#endif - -/*****************************************************************************/ - -/* Some implementation define a second set of logging macros, for a separate - * use. As with the _LOGD() macro family above, the exact implementation - * depends on the file that uses them. - * Still, it encourages a common pattern to have the common set of macros - * like _LOG2D(), _LOG2I(), etc. and have _LOG2t() which by default - * is disabled at compile time. */ - -#define _NMLOG2_ENABLED(level) (nm_logging_enabled((level), (_NMLOG2_DOMAIN))) - -#define _LOG2T(...) _NMLOG2(_LOGL_TRACE, __VA_ARGS__) -#define _LOG2D(...) _NMLOG2(_LOGL_DEBUG, __VA_ARGS__) -#define _LOG2I(...) _NMLOG2(_LOGL_INFO, __VA_ARGS__) -#define _LOG2W(...) _NMLOG2(_LOGL_WARN, __VA_ARGS__) -#define _LOG2E(...) _NMLOG2(_LOGL_ERR, __VA_ARGS__) - -#define _LOG2T_ENABLED(...) _NMLOG2_ENABLED(_LOGL_TRACE, ##__VA_ARGS__) -#define _LOG2D_ENABLED(...) _NMLOG2_ENABLED(_LOGL_DEBUG, ##__VA_ARGS__) -#define _LOG2I_ENABLED(...) _NMLOG2_ENABLED(_LOGL_INFO, ##__VA_ARGS__) -#define _LOG2W_ENABLED(...) _NMLOG2_ENABLED(_LOGL_WARN, ##__VA_ARGS__) -#define _LOG2E_ENABLED(...) _NMLOG2_ENABLED(_LOGL_ERR, ##__VA_ARGS__) - -#define _LOG2T_err(errsv, ...) _NMLOG2_err(errsv, _LOGL_TRACE, __VA_ARGS__) -#define _LOG2D_err(errsv, ...) _NMLOG2_err(errsv, _LOGL_DEBUG, __VA_ARGS__) -#define _LOG2I_err(errsv, ...) _NMLOG2_err(errsv, _LOGL_INFO, __VA_ARGS__) -#define _LOG2W_err(errsv, ...) _NMLOG2_err(errsv, _LOGL_WARN, __VA_ARGS__) -#define _LOG2E_err(errsv, ...) _NMLOG2_err(errsv, _LOGL_ERR, __VA_ARGS__) - -#if NM_MORE_LOGGING - #define _LOG2t_ENABLED(...) _NMLOG2_ENABLED(_LOGL_TRACE, ##__VA_ARGS__) - #define _LOG2t(...) _NMLOG2(_LOGL_TRACE, __VA_ARGS__) - #define _LOG2t_err(errsv, ...) _NMLOG2_err(errsv, _LOGL_TRACE, __VA_ARGS__) -#else - /* still call the logging macros to get compile time checks, but they will be optimized out. */ - #define _LOG2t_ENABLED(...) (FALSE && (_NMLOG2_ENABLED(_LOGL_TRACE, ##__VA_ARGS__))) - #define _LOG2t(...) \ - G_STMT_START \ - { \ - if (FALSE) { \ - _NMLOG2(_LOGL_TRACE, __VA_ARGS__); \ - } \ - } \ - G_STMT_END - #define _LOG2t_err(errsv, ...) \ - G_STMT_START \ - { \ - if (FALSE) { \ - _NMLOG2_err(errsv, _LOGL_TRACE, __VA_ARGS__); \ - } \ - } \ - G_STMT_END -#endif - -#define _NMLOG3_ENABLED(level) (nm_logging_enabled((level), (_NMLOG3_DOMAIN))) - -#define _LOG3T(...) _NMLOG3(_LOGL_TRACE, __VA_ARGS__) -#define _LOG3D(...) _NMLOG3(_LOGL_DEBUG, __VA_ARGS__) -#define _LOG3I(...) _NMLOG3(_LOGL_INFO, __VA_ARGS__) -#define _LOG3W(...) _NMLOG3(_LOGL_WARN, __VA_ARGS__) -#define _LOG3E(...) _NMLOG3(_LOGL_ERR, __VA_ARGS__) - -#define _LOG3T_ENABLED(...) _NMLOG3_ENABLED(_LOGL_TRACE, ##__VA_ARGS__) -#define _LOG3D_ENABLED(...) _NMLOG3_ENABLED(_LOGL_DEBUG, ##__VA_ARGS__) -#define _LOG3I_ENABLED(...) _NMLOG3_ENABLED(_LOGL_INFO, ##__VA_ARGS__) -#define _LOG3W_ENABLED(...) _NMLOG3_ENABLED(_LOGL_WARN, ##__VA_ARGS__) -#define _LOG3E_ENABLED(...) _NMLOG3_ENABLED(_LOGL_ERR, ##__VA_ARGS__) - -#define _LOG3T_err(errsv, ...) _NMLOG3_err(errsv, _LOGL_TRACE, __VA_ARGS__) -#define _LOG3D_err(errsv, ...) _NMLOG3_err(errsv, _LOGL_DEBUG, __VA_ARGS__) -#define _LOG3I_err(errsv, ...) _NMLOG3_err(errsv, _LOGL_INFO, __VA_ARGS__) -#define _LOG3W_err(errsv, ...) _NMLOG3_err(errsv, _LOGL_WARN, __VA_ARGS__) -#define _LOG3E_err(errsv, ...) _NMLOG3_err(errsv, _LOGL_ERR, __VA_ARGS__) - -#if NM_MORE_LOGGING - #define _LOG3t_ENABLED(...) _NMLOG3_ENABLED(_LOGL_TRACE, ##__VA_ARGS__) - #define _LOG3t(...) _NMLOG3(_LOGL_TRACE, __VA_ARGS__) - #define _LOG3t_err(errsv, ...) _NMLOG3_err(errsv, _LOGL_TRACE, __VA_ARGS__) -#else - /* still call the logging macros to get compile time checks, but they will be optimized out. */ - #define _LOG3t_ENABLED(...) (FALSE && (_NMLOG3_ENABLED(_LOGL_TRACE, ##__VA_ARGS__))) - #define _LOG3t(...) \ - G_STMT_START \ - { \ - if (FALSE) { \ - _NMLOG3(_LOGL_TRACE, __VA_ARGS__); \ - } \ - } \ - G_STMT_END - #define _LOG3t_err(errsv, ...) \ - G_STMT_START \ - { \ - if (FALSE) { \ - _NMLOG3_err(errsv, _LOGL_TRACE, __VA_ARGS__); \ - } \ - } \ - G_STMT_END -#endif - -/*****************************************************************************/ - -#endif /* __NM_LOGGING_FWD_H__ */ diff --git a/shared/nm-glib-aux/nm-macros-internal.h b/shared/nm-glib-aux/nm-macros-internal.h deleted file mode 100644 index 6f63f204..00000000 --- a/shared/nm-glib-aux/nm-macros-internal.h +++ /dev/null @@ -1,1824 +0,0 @@ -/* SPDX-License-Identifier: LGPL-2.1-or-later */ -/* - * Copyright (C) 2012 Colin Walters <walters@verbum.org>. - * Copyright (C) 2014 Red Hat, Inc. - */ - -#ifndef __NM_MACROS_INTERNAL_H__ -#define __NM_MACROS_INTERNAL_H__ - -#include <stdio.h> -#include <stdlib.h> -#include <errno.h> -#include <string.h> - -#include <gio/gio.h> - -/*****************************************************************************/ - -/* most of our code is single-threaded with a mainloop. Hence, we usually don't need - * any thread-safety. Sometimes, we do need thread-safety (nm-logging), but we can - * avoid locking if we are on the main-thread by: - * - * - modifications of shared data is done infrequently and only from the - * main-thread (nm_logging_setup()) - * - read-only access is done frequently (nm_logging_enabled()) - * - from the main-thread, we can do that without locking (because - * all modifications are also done on the main thread. - * - from other threads, we need locking. But this is expected to be - * done infrequently too. Important is the lock-free fast-path on the - * main-thread. - * - * By defining NM_THREAD_SAFE_ON_MAIN_THREAD you indicate that this code runs - * on the main-thread. It is by default defined to "1". If you have code that - * is also used on another thread, redefine the define to 0 (to opt in into - * the slow-path). - */ -#define NM_THREAD_SAFE_ON_MAIN_THREAD 1 - -/*****************************************************************************/ - -#include "nm-glib.h" - -/*****************************************************************************/ - -#define nm_offsetofend(t, m) (G_STRUCT_OFFSET(t, m) + sizeof(((t *) NULL)->m)) - -/*****************************************************************************/ - -#define gs_free nm_auto_g_free -#define gs_unref_object nm_auto_unref_object -#define gs_unref_variant nm_auto_unref_variant -#define gs_unref_array nm_auto_unref_array -#define gs_unref_ptrarray nm_auto_unref_ptrarray -#define gs_unref_hashtable nm_auto_unref_hashtable -#define gs_unref_bytes nm_auto_unref_bytes -#define gs_strfreev nm_auto_strfreev -#define gs_free_error nm_auto_free_error - -/*****************************************************************************/ - -NM_AUTO_DEFINE_FCN_VOID0(void *, _nm_auto_g_free, g_free); -#define nm_auto_g_free nm_auto(_nm_auto_g_free) - -NM_AUTO_DEFINE_FCN_VOID0(GObject *, _nm_auto_unref_object, g_object_unref); -#define nm_auto_unref_object nm_auto(_nm_auto_unref_object) - -NM_AUTO_DEFINE_FCN0(GVariant *, _nm_auto_unref_variant, g_variant_unref); -#define nm_auto_unref_variant nm_auto(_nm_auto_unref_variant) - -NM_AUTO_DEFINE_FCN0(GArray *, _nm_auto_unref_array, g_array_unref); -#define nm_auto_unref_array nm_auto(_nm_auto_unref_array) - -NM_AUTO_DEFINE_FCN0(GPtrArray *, _nm_auto_unref_ptrarray, g_ptr_array_unref); -#define nm_auto_unref_ptrarray nm_auto(_nm_auto_unref_ptrarray) - -NM_AUTO_DEFINE_FCN0(GHashTable *, _nm_auto_unref_hashtable, g_hash_table_unref); -#define nm_auto_unref_hashtable nm_auto(_nm_auto_unref_hashtable) - -NM_AUTO_DEFINE_FCN0(GSList *, _nm_auto_free_slist, g_slist_free); -#define nm_auto_free_slist nm_auto(_nm_auto_free_slist) - -NM_AUTO_DEFINE_FCN0(GBytes *, _nm_auto_unref_bytes, g_bytes_unref); -#define nm_auto_unref_bytes nm_auto(_nm_auto_unref_bytes) - -NM_AUTO_DEFINE_FCN0(char **, _nm_auto_strfreev, g_strfreev); -#define nm_auto_strfreev nm_auto(_nm_auto_strfreev) - -NM_AUTO_DEFINE_FCN0(GError *, _nm_auto_free_error, g_error_free); -#define nm_auto_free_error nm_auto(_nm_auto_free_error) - -NM_AUTO_DEFINE_FCN0(GKeyFile *, _nm_auto_unref_keyfile, g_key_file_unref); -#define nm_auto_unref_keyfile nm_auto(_nm_auto_unref_keyfile) - -NM_AUTO_DEFINE_FCN0(GVariantIter *, _nm_auto_free_variant_iter, g_variant_iter_free); -#define nm_auto_free_variant_iter nm_auto(_nm_auto_free_variant_iter) - -NM_AUTO_DEFINE_FCN0(GVariantBuilder *, _nm_auto_unref_variant_builder, g_variant_builder_unref); -#define nm_auto_unref_variant_builder nm_auto(_nm_auto_unref_variant_builder) - -#define nm_auto_clear_variant_builder nm_auto(g_variant_builder_clear) - -NM_AUTO_DEFINE_FCN0(GList *, _nm_auto_free_list, g_list_free); -#define nm_auto_free_list nm_auto(_nm_auto_free_list) - -NM_AUTO_DEFINE_FCN0(GChecksum *, _nm_auto_checksum_free, g_checksum_free); -#define nm_auto_free_checksum nm_auto(_nm_auto_checksum_free) - -#define nm_auto_unset_gvalue nm_auto(g_value_unset) - -NM_AUTO_DEFINE_FCN_VOID0(void *, _nm_auto_unref_gtypeclass, g_type_class_unref); -#define nm_auto_unref_gtypeclass nm_auto(_nm_auto_unref_gtypeclass) - -NM_AUTO_DEFINE_FCN0(GByteArray *, _nm_auto_unref_bytearray, g_byte_array_unref); -#define nm_auto_unref_bytearray nm_auto(_nm_auto_unref_bytearray) - -static inline void -_nm_auto_free_gstring(GString **str) -{ - if (*str) - g_string_free(*str, TRUE); -} -#define nm_auto_free_gstring nm_auto(_nm_auto_free_gstring) - -NM_AUTO_DEFINE_FCN0(GSource *, _nm_auto_unref_gsource, g_source_unref); -#define nm_auto_unref_gsource nm_auto(_nm_auto_unref_gsource) - -NM_AUTO_DEFINE_FCN0(guint, _nm_auto_remove_source, g_source_remove); -#define nm_auto_remove_source nm_auto(_nm_auto_remove_source) - -NM_AUTO_DEFINE_FCN0(GIOChannel *, _nm_auto_unref_io_channel, g_io_channel_unref); -#define nm_auto_unref_io_channel nm_auto(_nm_auto_unref_io_channel) - -NM_AUTO_DEFINE_FCN0(GMainLoop *, _nm_auto_unref_gmainloop, g_main_loop_unref); -#define nm_auto_unref_gmainloop nm_auto(_nm_auto_unref_gmainloop) - -NM_AUTO_DEFINE_FCN0(GOptionContext *, _nm_auto_free_option_context, g_option_context_free); -#define nm_auto_free_option_context nm_auto(_nm_auto_free_option_context) - -static inline void -_nm_auto_freev(gpointer ptr) -{ - gpointer **p = ptr; - gpointer * _ptr; - - if (*p) { - for (_ptr = *p; *_ptr; _ptr++) - g_free(*_ptr); - g_free(*p); - } -} -/* g_free a NULL terminated array of pointers, with also freeing each - * pointer with g_free(). It essentially does the same as - * gs_strfreev / g_strfreev(), but not restricted to strv arrays. */ -#define nm_auto_freev nm_auto(_nm_auto_freev) - -/*****************************************************************************/ - -#define _NM_MACRO_SELECT_ARG_64(_1, \ - _2, \ - _3, \ - _4, \ - _5, \ - _6, \ - _7, \ - _8, \ - _9, \ - _10, \ - _11, \ - _12, \ - _13, \ - _14, \ - _15, \ - _16, \ - _17, \ - _18, \ - _19, \ - _20, \ - _21, \ - _22, \ - _23, \ - _24, \ - _25, \ - _26, \ - _27, \ - _28, \ - _29, \ - _30, \ - _31, \ - _32, \ - _33, \ - _34, \ - _35, \ - _36, \ - _37, \ - _38, \ - _39, \ - _40, \ - _41, \ - _42, \ - _43, \ - _44, \ - _45, \ - _46, \ - _47, \ - _48, \ - _49, \ - _50, \ - _51, \ - _52, \ - _53, \ - _54, \ - _55, \ - _56, \ - _57, \ - _58, \ - _59, \ - _60, \ - _61, \ - _62, \ - _63, \ - N, \ - ...) \ - N - -/* http://stackoverflow.com/a/2124385/354393 - * https://stackoverflow.com/questions/11317474/macro-to-count-number-of-arguments - */ - -#define NM_NARG(...) \ - _NM_MACRO_SELECT_ARG_64(, \ - ##__VA_ARGS__, \ - 62, \ - 61, \ - 60, \ - 59, \ - 58, \ - 57, \ - 56, \ - 55, \ - 54, \ - 53, \ - 52, \ - 51, \ - 50, \ - 49, \ - 48, \ - 47, \ - 46, \ - 45, \ - 44, \ - 43, \ - 42, \ - 41, \ - 40, \ - 39, \ - 38, \ - 37, \ - 36, \ - 35, \ - 34, \ - 33, \ - 32, \ - 31, \ - 30, \ - 29, \ - 28, \ - 27, \ - 26, \ - 25, \ - 24, \ - 23, \ - 22, \ - 21, \ - 20, \ - 19, \ - 18, \ - 17, \ - 16, \ - 15, \ - 14, \ - 13, \ - 12, \ - 11, \ - 10, \ - 9, \ - 8, \ - 7, \ - 6, \ - 5, \ - 4, \ - 3, \ - 2, \ - 1, \ - 0) -#define NM_NARG_MAX1(...) \ - _NM_MACRO_SELECT_ARG_64(, \ - ##__VA_ARGS__, \ - 1, \ - 1, \ - 1, \ - 1, \ - 1, \ - 1, \ - 1, \ - 1, \ - 1, \ - 1, \ - 1, \ - 1, \ - 1, \ - 1, \ - 1, \ - 1, \ - 1, \ - 1, \ - 1, \ - 1, \ - 1, \ - 1, \ - 1, \ - 1, \ - 1, \ - 1, \ - 1, \ - 1, \ - 1, \ - 1, \ - 1, \ - 1, \ - 1, \ - 1, \ - 1, \ - 1, \ - 1, \ - 1, \ - 1, \ - 1, \ - 1, \ - 1, \ - 1, \ - 1, \ - 1, \ - 1, \ - 1, \ - 1, \ - 1, \ - 1, \ - 1, \ - 1, \ - 1, \ - 1, \ - 1, \ - 1, \ - 1, \ - 1, \ - 1, \ - 1, \ - 1, \ - 1, \ - 0) -#define NM_NARG_MAX2(...) \ - _NM_MACRO_SELECT_ARG_64(, \ - ##__VA_ARGS__, \ - 2, \ - 2, \ - 2, \ - 2, \ - 2, \ - 2, \ - 2, \ - 2, \ - 2, \ - 2, \ - 2, \ - 2, \ - 2, \ - 2, \ - 2, \ - 2, \ - 2, \ - 2, \ - 2, \ - 2, \ - 2, \ - 2, \ - 2, \ - 2, \ - 2, \ - 2, \ - 2, \ - 2, \ - 2, \ - 2, \ - 2, \ - 2, \ - 2, \ - 2, \ - 2, \ - 2, \ - 2, \ - 2, \ - 2, \ - 2, \ - 2, \ - 2, \ - 2, \ - 2, \ - 2, \ - 2, \ - 2, \ - 2, \ - 2, \ - 2, \ - 2, \ - 2, \ - 2, \ - 2, \ - 2, \ - 2, \ - 2, \ - 2, \ - 2, \ - 2, \ - 2, \ - 1, \ - 0) - -#define _NM_MACRO_CALL(macro, ...) macro(__VA_ARGS__) - -/*****************************************************************************/ - -#define _NM_MACRO_COMMA_IF_ARGS(...) \ - _NM_MACRO_CALL(G_PASTE(__NM_MACRO_COMMA_IF_ARGS_, NM_NARG_MAX1(__VA_ARGS__)), __VA_ARGS__) -#define __NM_MACRO_COMMA_IF_ARGS_0() -#define __NM_MACRO_COMMA_IF_ARGS_1(...) , - -/*****************************************************************************/ - -/* http://stackoverflow.com/a/11172679 */ -#define _NM_UTILS_MACRO_FIRST(...) __NM_UTILS_MACRO_FIRST_HELPER(__VA_ARGS__, throwaway) -#define __NM_UTILS_MACRO_FIRST_HELPER(first, ...) first - -#define _NM_UTILS_MACRO_REST(...) \ - _NM_MACRO_CALL(G_PASTE(__NM_UTILS_MACRO_REST_, NM_NARG_MAX2(__VA_ARGS__)), __VA_ARGS__) -#define __NM_UTILS_MACRO_REST_0() -#define __NM_UTILS_MACRO_REST_1(first) -#define __NM_UTILS_MACRO_REST_2(first, ...) , __VA_ARGS__ - -/*****************************************************************************/ - -#if defined(__GNUC__) - #define _NM_PRAGMA_WARNING_DO(warning) G_STRINGIFY(GCC diagnostic ignored warning) -#elif defined(__clang__) - #define _NM_PRAGMA_WARNING_DO(warning) G_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 NM_PRAGMA_WARNING_DISABLE(warning) \ - _Pragma("GCC diagnostic push") _Pragma(_NM_PRAGMA_WARNING_DO(warning)) -#elif defined(__clang__) - #define NM_PRAGMA_WARNING_DISABLE(warning) \ - _Pragma("clang diagnostic push") \ - _Pragma(_NM_PRAGMA_WARNING_DO("-Wunknown-warning-option")) \ - _Pragma(_NM_PRAGMA_WARNING_DO(warning)) -#else - #define NM_PRAGMA_WARNING_DISABLE(warning) -#endif - -#if defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6)) - #define NM_PRAGMA_WARNING_REENABLE _Pragma("GCC diagnostic pop") -#elif defined(__clang__) - #define NM_PRAGMA_WARNING_REENABLE _Pragma("clang diagnostic pop") -#else - #define NM_PRAGMA_WARNING_REENABLE -#endif - -/*****************************************************************************/ - -/** - * NM_G_ERROR_MSG: - * @error: (allow-none): the #GError instance - * - * All functions must follow the convention that when they - * return a failure, they must also set the GError to a valid - * message. For external API however, we want to be extra - * careful before accessing the error instance. Use NM_G_ERROR_MSG() - * which is safe to use on NULL. - * - * Returns: the error message. - **/ -static inline const char * -NM_G_ERROR_MSG(GError *error) -{ - return error ? (error->message ?: "(null)") : "(no-error)"; -} - -/*****************************************************************************/ - -#ifndef _NM_CC_SUPPORT_AUTO_TYPE - #if (defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 9))) - #define _NM_CC_SUPPORT_AUTO_TYPE 1 - #else - #define _NM_CC_SUPPORT_AUTO_TYPE 0 - #endif -#endif - -#ifndef _NM_CC_SUPPORT_GENERIC - /* In the meantime, NetworkManager requires C11 and _Generic() should always be available. - * However, shared/nm-utils may also be used in VPN/applet, which possibly did not yet - * bump the C standard requirement. Leave this for the moment, but eventually we can - * drop it. */ - #if (defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 9))) \ - || (defined(__clang__)) - #define _NM_CC_SUPPORT_GENERIC 1 - #else - #define _NM_CC_SUPPORT_GENERIC 0 - #endif -#endif - -#if _NM_CC_SUPPORT_AUTO_TYPE - #define _nm_auto_type __auto_type -#endif - -#if _NM_CC_SUPPORT_GENERIC - #define _NM_CONSTCAST_FULL_1(type, obj_expr, obj) \ - (_Generic ((obj_expr), \ - const void *const: ((const type *) (obj)), \ - const void * : ((const type *) (obj)), \ - void *const: (( type *) (obj)), \ - void * : (( type *) (obj)), \ - const type *const: ((const type *) (obj)), \ - const type * : ((const type *) (obj)), \ - type *const: (( type *) (obj)), \ - type * : (( type *) (obj)))) - #define _NM_CONSTCAST_FULL_2(type, obj_expr, obj, alias_type2) \ - (_Generic ((obj_expr), \ - const void *const: ((const type *) (obj)), \ - const void * : ((const type *) (obj)), \ - void *const: (( type *) (obj)), \ - void * : (( type *) (obj)), \ - const alias_type2 *const: ((const type *) (obj)), \ - const alias_type2 * : ((const type *) (obj)), \ - alias_type2 *const: (( type *) (obj)), \ - alias_type2 * : (( type *) (obj)), \ - const type *const: ((const type *) (obj)), \ - const type * : ((const type *) (obj)), \ - type *const: (( type *) (obj)), \ - type * : (( type *) (obj)))) - #define _NM_CONSTCAST_FULL_3(type, obj_expr, obj, alias_type2, alias_type3) \ - (_Generic ((obj_expr), \ - const void *const: ((const type *) (obj)), \ - const void * : ((const type *) (obj)), \ - void *const: (( type *) (obj)), \ - void * : (( type *) (obj)), \ - const alias_type2 *const: ((const type *) (obj)), \ - const alias_type2 * : ((const type *) (obj)), \ - alias_type2 *const: (( type *) (obj)), \ - alias_type2 * : (( type *) (obj)), \ - const alias_type3 *const: ((const type *) (obj)), \ - const alias_type3 * : ((const type *) (obj)), \ - alias_type3 *const: (( type *) (obj)), \ - alias_type3 * : (( type *) (obj)), \ - const type *const: ((const type *) (obj)), \ - const type * : ((const type *) (obj)), \ - type *const: (( type *) (obj)), \ - type * : (( type *) (obj)))) - #define _NM_CONSTCAST_FULL_4(type, obj_expr, obj, alias_type2, alias_type3, alias_type4) \ - (_Generic ((obj_expr), \ - const void *const: ((const type *) (obj)), \ - const void * : ((const type *) (obj)), \ - void *const: (( type *) (obj)), \ - void * : (( type *) (obj)), \ - const alias_type2 *const: ((const type *) (obj)), \ - const alias_type2 * : ((const type *) (obj)), \ - alias_type2 *const: (( type *) (obj)), \ - alias_type2 * : (( type *) (obj)), \ - const alias_type3 *const: ((const type *) (obj)), \ - const alias_type3 * : ((const type *) (obj)), \ - alias_type3 *const: (( type *) (obj)), \ - alias_type3 * : (( type *) (obj)), \ - const alias_type4 *const: ((const type *) (obj)), \ - const alias_type4 * : ((const type *) (obj)), \ - alias_type4 *const: (( type *) (obj)), \ - alias_type4 * : (( type *) (obj)), \ - const type *const: ((const type *) (obj)), \ - const type * : ((const type *) (obj)), \ - type *const: (( type *) (obj)), \ - type * : (( type *) (obj)))) - #define _NM_CONSTCAST_FULL_x(type, obj_expr, obj, n, ...) \ - (_NM_CONSTCAST_FULL_##n(type, obj_expr, obj, ##__VA_ARGS__)) - #define _NM_CONSTCAST_FULL_y(type, obj_expr, obj, n, ...) \ - (_NM_CONSTCAST_FULL_x(type, obj_expr, obj, n, ##__VA_ARGS__)) - #define NM_CONSTCAST_FULL(type, obj_expr, obj, ...) \ - (_NM_CONSTCAST_FULL_y(type, obj_expr, obj, NM_NARG(dummy, ##__VA_ARGS__), ##__VA_ARGS__)) -#else - #define NM_CONSTCAST_FULL(type, obj_expr, obj, ...) ((type *) (obj)) -#endif - -#define NM_CONSTCAST(type, obj, ...) NM_CONSTCAST_FULL(type, (obj), (obj), ##__VA_ARGS__) - -#if _NM_CC_SUPPORT_GENERIC - #define NM_UNCONST_PTR(type, arg) \ - _Generic((arg), const type * : ((type *) (arg)), type * : ((type *) (arg))) -#else - #define NM_UNCONST_PTR(type, arg) ((type *) (arg)) -#endif - -#if _NM_CC_SUPPORT_GENERIC - #define NM_UNCONST_PPTR(type, arg) \ - _Generic ((arg), \ - const type * *: ((type **) (arg)), \ - type * *: ((type **) (arg)), \ - const type *const*: ((type **) (arg)), \ - type *const*: ((type **) (arg))) -#else - #define NM_UNCONST_PPTR(type, arg) ((type **) (arg)) -#endif - -#define NM_GOBJECT_CAST(type, obj, is_check, ...) \ - ({ \ - const void *_obj = (obj); \ - \ - nm_assert(_obj || (is_check(_obj))); \ - NM_CONSTCAST_FULL(type, (obj), _obj, GObject, ##__VA_ARGS__); \ - }) - -#define NM_GOBJECT_CAST_NON_NULL(type, obj, is_check, ...) \ - ({ \ - const void *_obj = (obj); \ - \ - nm_assert(is_check(_obj)); \ - NM_CONSTCAST_FULL(type, (obj), _obj, GObject, ##__VA_ARGS__); \ - }) - -#define NM_ENSURE_NOT_NULL(ptr) \ - ({ \ - typeof(ptr) _ptr = (ptr); \ - \ - nm_assert(_ptr != NULL); \ - _ptr; \ - }) - -#if _NM_CC_SUPPORT_GENERIC - /* returns @value, if the type of @value matches @type. - * This requires support for C11 _Generic(). If no support is - * present, this returns @value directly. - * - * It's useful to check the let the compiler ensure that @value is - * of a certain type. */ - #define _NM_ENSURE_TYPE(type, value) (_Generic((value), type : (value))) - #define _NM_ENSURE_TYPE_CONST(type, value) \ - (_Generic((value), const type \ - : ((const type)(value)), const type const \ - : ((const type)(value)), type \ - : ((const type)(value)), type const \ - : ((const type)(value)))) -#else - #define _NM_ENSURE_TYPE(type, value) (value) - #define _NM_ENSURE_TYPE_CONST(type, value) ((const type)(value)) -#endif - -#if _NM_CC_SUPPORT_GENERIC && (!defined(__clang__) || __clang_major__ > 3) - #define NM_STRUCT_OFFSET_ENSURE_TYPE(type, container, field) \ - (_Generic((&(((container *) NULL)->field))[0], type : G_STRUCT_OFFSET(container, field))) -#else - #define NM_STRUCT_OFFSET_ENSURE_TYPE(type, container, field) G_STRUCT_OFFSET(container, field) -#endif - -#if _NM_CC_SUPPORT_GENERIC - /* these macros cast (value) to - * - "const char **" (for "MC", mutable-const) - * - "const char *const*" (for "CC", const-const) - * The point is to do this cast, but only accepting pointers - * that are compatible already. - * - * The problem is, if you add a function like g_strdupv(), the input - * argument is not modified (CC), but you want to make it work also - * for "char **". C doesn't allow this form of casting (for good reasons), - * so the function makes a choice like g_strdupv(char**). That means, - * every time you want to call it with a const argument, you need to - * explicitly cast it. - * - * These macros do the cast, but they only accept a compatible input - * type, otherwise they will fail compilation. - */ - #define NM_CAST_STRV_MC(value) \ - (_Generic ((value), \ - const char * *: (const char * *) (value), \ - char * *: (const char * *) (value), \ - void *: (const char * *) (value))) - #define NM_CAST_STRV_CC(value) \ - (_Generic ((value), \ - const char *const*: (const char *const*) (value), \ - const char * *: (const char *const*) (value), \ - char *const*: (const char *const*) (value), \ - char * *: (const char *const*) (value), \ - const void *: (const char *const*) (value), \ - void *: (const char *const*) (value), \ - const char *const*const: (const char *const*) (value), \ - const char * *const: (const char *const*) (value), \ - char *const*const: (const char *const*) (value), \ - char * *const: (const char *const*) (value), \ - const void *const: (const char *const*) (value), \ - void *const: (const char *const*) (value))) -#else - #define NM_CAST_STRV_MC(value) ((const char **) (value)) - #define NM_CAST_STRV_CC(value) ((const char *const *) (value)) -#endif - -#if _NM_CC_SUPPORT_GENERIC - #define NM_PROPAGATE_CONST(test_expr, ptr) \ - (_Generic ((test_expr), \ - const typeof (*(test_expr)) *: ((const typeof (*(ptr)) *) (ptr)), \ - default: (_Generic ((test_expr), \ - typeof (*(test_expr)) *: (ptr))))) -#else - #define NM_PROPAGATE_CONST(test_expr, ptr) (ptr) -#endif - -/* with the way it is implemented, the caller may or may not pass a trailing - * ',' and it will work. However, this makes the macro unsuitable for initializing - * an array. */ -#define NM_MAKE_STRV(...) \ - ((const char *const[(sizeof(((const char *const[]){__VA_ARGS__})) / sizeof(const char *)) \ - + 1]){__VA_ARGS__}) - -/*****************************************************************************/ - -/* NM_CACHED_QUARK() returns the GQuark for @string, but caches - * it in a static variable to speed up future lookups. - * - * @string must be a string literal. - */ -#define NM_CACHED_QUARK(string) \ - ({ \ - static GQuark _nm_cached_quark = 0; \ - \ - (G_LIKELY(_nm_cached_quark != 0) \ - ? _nm_cached_quark \ - : (_nm_cached_quark = g_quark_from_static_string("" string ""))); \ - }) - -/* NM_CACHED_QUARK_FCN() is essentially the same as G_DEFINE_QUARK - * with two differences: - * - @string must be a quoted string-literal - * - @fcn must be the full function name, while G_DEFINE_QUARK() appends - * "_quark" to the function name. - * Both properties of G_DEFINE_QUARK() are non favorable, because you can no - * longer grep for string/fcn -- unless you are aware that you are searching - * for G_DEFINE_QUARK() and omit quotes / append _quark(). With NM_CACHED_QUARK_FCN(), - * ctags/cscope can locate the use of @fcn (though it doesn't recognize that - * NM_CACHED_QUARK_FCN() defines it). - */ -#define NM_CACHED_QUARK_FCN(string, fcn) \ - GQuark fcn(void) \ - { \ - return NM_CACHED_QUARK(string); \ - } \ - _NM_DUMMY_STRUCT_FOR_TRAILING_SEMICOLON - -/*****************************************************************************/ - -static inline GString * -nm_gstring_prepare(GString **l) -{ - if (*l) - g_string_set_size(*l, 0); - else - *l = g_string_sized_new(30); - return *l; -} - -static inline GString * -nm_gstring_add_space_delimiter(GString *str) -{ - if (str->len > 0) - g_string_append_c(str, ' '); - return str; -} - -static inline gboolean -nm_str_is_empty(const char *str) -{ - /* %NULL is also accepted, and also "empty". */ - return !str || !str[0]; -} - -static inline const char * -nm_str_not_empty(const char *str) -{ - return !nm_str_is_empty(str) ? str : NULL; -} - -static inline char * -nm_strdup_not_empty(const char *str) -{ - return !nm_str_is_empty(str) ? g_strdup(str) : NULL; -} - -static inline char * -nm_str_realloc(char *str) -{ - gs_free char *s = str; - - /* Returns a new clone of @str and frees @str. The point is that @str - * possibly points to a larger chunck of memory. We want to freshly allocate - * a buffer. - * - * We could use realloc(), but that might not do anything or leave - * @str in its memory pool for chunks of a different size (bad for - * fragmentation). - * - * This is only useful when we want to keep the buffer around for a long - * time and want to re-allocate a more optimal buffer. */ - - return g_strdup(s); -} - -/*****************************************************************************/ - -#define NM_PRINT_FMT_QUOTED2(cond, prefix, str, str_else) \ - (cond) ? (prefix) : "", (cond) ? (str) : (str_else) -#define NM_PRINT_FMT_QUOTED(cond, prefix, str, suffix, str_else) \ - (cond) ? (prefix) : "", (cond) ? (str) : (str_else), (cond) ? (suffix) : "" -#define NM_PRINT_FMT_QUOTE_STRING(arg) NM_PRINT_FMT_QUOTED((arg), "\"", (arg), "\"", "(null)") -#define NM_PRINT_FMT_QUOTE_REF_STRING(arg) \ - NM_PRINT_FMT_QUOTED((arg), "\"", (arg)->str, "\"", "(null)") - -/*****************************************************************************/ - -/* redefine assertions to use g_assert*() */ -#undef _nm_assert_call -#undef _nm_assert_call_not_reached -#define _nm_assert_call(cond) g_assert(cond) -#define _nm_assert_call_not_reached() g_assert_not_reached() - -/* Usage: - * - * if (NM_MORE_ASSERT_ONCE (5)) { extra_check (); } - * - * This will only run the check once, and only if NM_MORE_ASSERT is >= than - * more_assert_level. - */ -#define NM_MORE_ASSERT_ONCE(more_assert_level) \ - ((NM_MORE_ASSERTS >= (more_assert_level)) && ({ \ - static volatile int _assert_once = 0; \ - \ - G_STATIC_ASSERT_EXPR((more_assert_level) > 0); \ - \ - G_UNLIKELY(_assert_once == 0 && g_atomic_int_compare_and_exchange(&_assert_once, 0, 1)); \ - })) - -/*****************************************************************************/ - -#define NM_GOBJECT_PROPERTIES_DEFINE_BASE_FULL(suffix, ...) \ - typedef enum { \ - PROP_0##suffix, \ - __VA_ARGS__ _PROPERTY_ENUMS_LAST##suffix, \ - } _PropertyEnums##suffix; \ - static GParamSpec *obj_properties##suffix[_PROPERTY_ENUMS_LAST##suffix] = { \ - NULL, \ - } - -#define NM_GOBJECT_PROPERTIES_DEFINE_NOTIFY(suffix, obj_type) \ - static inline void _nm_gobject_notify_together_impl##suffix( \ - obj_type * obj, \ - guint n, \ - const _PropertyEnums##suffix *props) \ - { \ - GObject *const gobj = (GObject *) obj; \ - GParamSpec * pspec_first = NULL; \ - gboolean frozen = FALSE; \ - \ - nm_assert(G_IS_OBJECT(obj)); \ - nm_assert(n > 0); \ - \ - while (n-- > 0) { \ - const _PropertyEnums##suffix prop = *props++; \ - GParamSpec * pspec; \ - \ - if (prop == PROP_0##suffix) \ - continue; \ - \ - nm_assert((gsize) prop < G_N_ELEMENTS(obj_properties##suffix)); \ - pspec = obj_properties##suffix[prop]; \ - nm_assert(pspec); \ - \ - if (!frozen) { \ - if (!pspec_first) { \ - pspec_first = pspec; \ - continue; \ - } \ - frozen = TRUE; \ - g_object_freeze_notify(gobj); \ - g_object_notify_by_pspec(gobj, pspec_first); \ - } \ - g_object_notify_by_pspec(gobj, pspec); \ - } \ - \ - if (frozen) \ - g_object_thaw_notify(gobj); \ - else if (pspec_first) \ - g_object_notify_by_pspec(gobj, pspec_first); \ - } \ - \ - _nm_unused static inline void _notify##suffix(obj_type *obj, _PropertyEnums##suffix prop) \ - { \ - _nm_gobject_notify_together_impl##suffix(obj, 1, &prop); \ - } \ - _NM_DUMMY_STRUCT_FOR_TRAILING_SEMICOLON - -#define NM_GOBJECT_PROPERTIES_DEFINE_BASE(...) \ - NM_GOBJECT_PROPERTIES_DEFINE_BASE_FULL(, __VA_ARGS__); - -#define NM_GOBJECT_PROPERTIES_DEFINE_FULL(suffix, obj_type, ...) \ - NM_GOBJECT_PROPERTIES_DEFINE_BASE_FULL(suffix, __VA_ARGS__); \ - NM_GOBJECT_PROPERTIES_DEFINE_NOTIFY(suffix, obj_type) - -#define NM_GOBJECT_PROPERTIES_DEFINE(obj_type, ...) \ - NM_GOBJECT_PROPERTIES_DEFINE_FULL(, obj_type, __VA_ARGS__) - -/* invokes _notify() for all arguments (of type _PropertyEnums). Note, that if - * there are more than one prop arguments, this will involve a freeze/thaw - * of GObject property notifications. */ -#define nm_gobject_notify_together_full(suffix, obj, ...) \ - _nm_gobject_notify_together_impl##suffix(obj, \ - NM_NARG(__VA_ARGS__), \ - (const _PropertyEnums##suffix[]){__VA_ARGS__}) - -#define nm_gobject_notify_together(obj, ...) nm_gobject_notify_together_full(, obj, __VA_ARGS__) - -/*****************************************************************************/ - -#define _NM_GET_PRIVATE(self, type, is_check, ...) \ - (&(NM_GOBJECT_CAST_NON_NULL(type, (self), is_check, ##__VA_ARGS__)->_priv)) -#if _NM_CC_SUPPORT_AUTO_TYPE - #define _NM_GET_PRIVATE_PTR(self, type, is_check, ...) \ - ({ \ - _nm_auto_type _self_get_private = \ - NM_GOBJECT_CAST_NON_NULL(type, (self), is_check, ##__VA_ARGS__); \ - \ - NM_PROPAGATE_CONST(_self_get_private, _self_get_private->_priv); \ - }) -#else - #define _NM_GET_PRIVATE_PTR(self, type, is_check, ...) \ - (NM_GOBJECT_CAST_NON_NULL(type, (self), is_check, ##__VA_ARGS__)->_priv) -#endif - -/*****************************************************************************/ - -static inline gpointer -nm_g_object_ref(gpointer obj) -{ - /* g_object_ref() doesn't accept NULL. */ - if (obj) - g_object_ref(obj); - return obj; -} -#define nm_g_object_ref(obj) ((typeof(obj)) nm_g_object_ref(obj)) - -static inline void -nm_g_object_unref(gpointer obj) -{ - /* g_object_unref() doesn't accept NULL. Usually, we workaround that - * by using g_clear_object(), but sometimes that is not convenient - * (for example as destroy function for a hash table that can contain - * NULL values). */ - if (obj) - g_object_unref(obj); -} - -/* Assigns GObject @obj to destination @pp, and takes an additional ref. - * The previous value of @pp is unrefed. - * - * It makes sure to first increase the ref-count of @obj, and handles %NULL - * @obj correctly. - * */ -#define nm_g_object_ref_set(pp, obj) \ - ({ \ - typeof(*(pp)) *const _pp = (pp); \ - typeof(*_pp) const _obj = (obj); \ - typeof(*_pp) _p; \ - gboolean _changed = FALSE; \ - \ - nm_assert(!_pp || !*_pp || G_IS_OBJECT(*_pp)); \ - nm_assert(!_obj || G_IS_OBJECT(_obj)); \ - \ - if (_pp && ((_p = *_pp) != _obj)) { \ - nm_g_object_ref(_obj); \ - *_pp = _obj; \ - nm_g_object_unref(_p); \ - _changed = TRUE; \ - } \ - _changed; \ - }) - -#define nm_g_object_ref_set_take(pp, obj) \ - ({ \ - typeof(*(pp)) *const _pp = (pp); \ - typeof(*_pp) const _obj = (obj); \ - typeof(*_pp) _p; \ - gboolean _changed = FALSE; \ - \ - nm_assert(!_pp || !*_pp || G_IS_OBJECT(*_pp)); \ - nm_assert(!_obj || G_IS_OBJECT(_obj)); \ - \ - if (_pp && ((_p = *_pp) != _obj)) { \ - *_pp = _obj; \ - nm_g_object_unref(_p); \ - _changed = TRUE; \ - } else \ - nm_g_object_unref(_obj); \ - _changed; \ - }) - -/* basically, replaces - * g_clear_pointer (&location, g_free) - * with - * nm_clear_g_free (&location) - * - * Another advantage is that by using a macro and typeof(), it is more - * typesafe and gives you for example a compiler warning when pp is a const - * pointer or points to a const-pointer. - */ -#define nm_clear_g_free(pp) nm_clear_pointer(pp, g_free) - -/* Our nm_clear_pointer() is more typesafe than g_clear_pointer() and - * should be preferred. - * - * For g_clear_object() that is not the case (because g_object_unref() - * anyway takes a void pointer). So using g_clear_object() is fine. - * - * Still have a nm_clear_g_object() because that returns a boolean - * indication whether anything was cleared. */ -#define nm_clear_g_object(pp) nm_clear_pointer(pp, g_object_unref) - -/** - * nm_clear_error: - * @err: a pointer to pointer to a #GError. - * - * This is like g_clear_error(). The only difference is - * that this is an inline function. - */ -static inline void -nm_clear_error(GError **err) -{ - if (err && *err) { - g_error_free(*err); - *err = NULL; - } -} - -/* Patch g_clear_error() to use nm_clear_error(), which is inlineable - * and visible to the compiler. For example gs_free_error attribute only - * frees the error after checking that it's not %NULL. So, in many cases - * the compiler knows that gs_free_error has no effect and can optimize - * the call away. By making g_clear_error() inlineable, we give the compiler - * more chance to detect that the function actually has no effect. */ -#define g_clear_error(ptr) nm_clear_error(ptr) - -static inline gboolean -nm_clear_g_source(guint *id) -{ - guint v; - - if (id && (v = *id)) { - *id = 0; - g_source_remove(v); - return TRUE; - } - return FALSE; -} - -static inline gboolean -nm_clear_g_signal_handler(gpointer self, gulong *id) -{ - gulong v; - - if (id && (v = *id)) { - *id = 0; - g_signal_handler_disconnect(self, v); - return TRUE; - } - return FALSE; -} - -static inline gboolean -nm_clear_g_variant(GVariant **variant) -{ - GVariant *v; - - if (variant && (v = *variant)) { - *variant = NULL; - g_variant_unref(v); - return TRUE; - } - return FALSE; -} - -static inline gboolean -nm_clear_g_string(GString **ptr) -{ - GString *s; - - if (ptr && (s = *ptr)) { - *ptr = NULL; - g_string_free(s, TRUE); - }; - return FALSE; -} - -static inline gboolean -nm_clear_g_cancellable(GCancellable **cancellable) -{ - GCancellable *v; - - if (cancellable && (v = *cancellable)) { - *cancellable = NULL; - g_cancellable_cancel(v); - g_object_unref(v); - return TRUE; - } - return FALSE; -} - -/* If @cancellable_id is not 0, clear it and call g_cancellable_disconnect(). - * @cancellable may be %NULL, if there is nothing to disconnect. - * - * It's like nm_clear_g_signal_handler(), except that it uses g_cancellable_disconnect() - * instead of g_signal_handler_disconnect(). - * - * Note the warning in glib documentation about dead-lock and what g_cancellable_disconnect() - * actually does. */ -static inline gboolean -nm_clear_g_cancellable_disconnect(GCancellable *cancellable, gulong *cancellable_id) -{ - gulong id; - - if (cancellable_id && (id = *cancellable_id) != 0) { - *cancellable_id = 0; - g_cancellable_disconnect(cancellable, id); - return TRUE; - } - return FALSE; -} - -/*****************************************************************************/ - -static inline const char * -nm_dbus_path_not_empty(const char *str) -{ - nm_assert(!str || str[0] == '/'); - return !str || (str[0] == '/' && str[1] == '\0') ? NULL : str; -} - -/*****************************************************************************/ - -/* GVariantType is basically a C string. But G_VARIANT_TYPE() is not suitable - * to initialize a static variable (because it evaluates a function check that - * the string is valid). Add an alternative macro that does the plain cast. - * - * Here you loose the assertion check that G_VARIANT_TYPE() to ensure the - * string is valid. */ -#define NM_G_VARIANT_TYPE(fmt) ((const GVariantType *) ("" fmt "")) - -static inline GVariant * -nm_g_variant_ref(GVariant *v) -{ - if (v) - g_variant_ref(v); - return v; -} - -static inline GVariant * -nm_g_variant_ref_sink(GVariant *v) -{ - if (v) - g_variant_ref_sink(v); - return v; -} - -static inline void -nm_g_variant_unref(GVariant *v) -{ - if (v) - g_variant_unref(v); -} - -static inline GVariant * -nm_g_variant_take_ref(GVariant *v) -{ - if (v) - g_variant_take_ref(v); - return v; -} - -/*****************************************************************************/ - -#define NM_DIV_ROUND_UP(x, y) \ - ({ \ - const typeof(x) _x = (x); \ - const typeof(y) _y = (y); \ - \ - (_x / _y + !!(_x % _y)); \ - }) - -/*****************************************************************************/ - -#define NM_UTILS_LOOKUP_DEFAULT(v) return (v) -#define NM_UTILS_LOOKUP_DEFAULT_WARN(v) g_return_val_if_reached(v) -#define NM_UTILS_LOOKUP_DEFAULT_NM_ASSERT(v) \ - { \ - nm_assert_not_reached(); \ - return (v); \ - } -#define NM_UTILS_LOOKUP_ITEM(v, n) \ - (void) 0; \ -case v: \ - return (n); \ - (void) 0 -#define NM_UTILS_LOOKUP_STR_ITEM(v, n) NM_UTILS_LOOKUP_ITEM(v, "" n "") -#define NM_UTILS_LOOKUP_ITEM_IGNORE(v) \ - (void) 0; \ -case v: \ - break; \ - (void) 0 -#define NM_UTILS_LOOKUP_ITEM_IGNORE_OTHER() \ - (void) 0; \ -default: \ - break; \ - (void) 0 - -#define NM_UTILS_LOOKUP_DEFINE(fcn_name, lookup_type, result_type, unknown_val, ...) \ - result_type fcn_name(lookup_type val) \ - { \ - switch (val) { \ - (void) 0, __VA_ARGS__(void) 0; \ - }; \ - { \ - unknown_val; \ - } \ - } \ - _NM_DUMMY_STRUCT_FOR_TRAILING_SEMICOLON - -#define NM_UTILS_LOOKUP_STR_DEFINE(fcn_name, lookup_type, unknown_val, ...) \ - NM_UTILS_LOOKUP_DEFINE(fcn_name, lookup_type, const char *, unknown_val, __VA_ARGS__) - -/* Call the string-lookup-table function @fcn_name. If the function returns - * %NULL, the numeric index is converted to string using a alloca() buffer. - * Beware: this macro uses alloca(). */ -#define NM_UTILS_LOOKUP_STR_A(fcn_name, idx) \ - ({ \ - typeof(idx) _idx = (idx); \ - const char *_s; \ - \ - _s = fcn_name(_idx); \ - if (!_s) { \ - _s = g_alloca(30); \ - \ - g_snprintf((char *) _s, 30, "(%lld)", (long long) _idx); \ - } \ - _s; \ - }) - -/*****************************************************************************/ - -/* check if @flags has exactly one flag (@check) set. You should call this - * only with @check being a compile time constant and a power of two. */ -#define NM_FLAGS_HAS(flags, check) \ - (G_STATIC_ASSERT_EXPR((check) > 0 && ((check) & ((check) -1)) == 0), \ - NM_FLAGS_ANY((flags), (check))) - -#define NM_FLAGS_ANY(flags, check) ((((flags) & (check)) != 0) ? TRUE : FALSE) -#define NM_FLAGS_ALL(flags, check) ((((flags) & (check)) == (check)) ? TRUE : FALSE) - -#define NM_FLAGS_SET(flags, val) \ - ({ \ - const typeof(flags) _flags = (flags); \ - const typeof(flags) _val = (val); \ - \ - _flags | _val; \ - }) - -#define NM_FLAGS_UNSET(flags, val) \ - ({ \ - const typeof(flags) _flags = (flags); \ - const typeof(flags) _val = (val); \ - \ - _flags &(~_val); \ - }) - -#define NM_FLAGS_ASSIGN(flags, val, assign) \ - ({ \ - const typeof(flags) _flags = (flags); \ - const typeof(flags) _val = (val); \ - \ - (assign) ? _flags | (_val) : _flags &(~_val); \ - }) - -#define NM_FLAGS_ASSIGN_MASK(flags, mask, val) \ - ({ \ - const typeof(flags) _flags = (flags); \ - const typeof(flags) _mask = (mask); \ - const typeof(flags) _val = (val); \ - \ - ((_flags & ~_mask) | (_mask & _val)); \ - }) - -/*****************************************************************************/ - -#define _NM_BACKPORT_SYMBOL_IMPL(version, \ - return_type, \ - orig_func, \ - versioned_func, \ - args_typed, \ - args) \ - return_type versioned_func args_typed; \ - _nm_externally_visible return_type versioned_func args_typed \ - { \ - return orig_func args; \ - } \ - return_type orig_func args_typed; \ - __asm__(".symver " G_STRINGIFY(versioned_func) ", " G_STRINGIFY(orig_func) "@" G_STRINGIFY( \ - version)) - -#define NM_BACKPORT_SYMBOL(version, return_type, func, args_typed, args) \ - _NM_BACKPORT_SYMBOL_IMPL(version, return_type, func, _##func##_##version, args_typed, args) - -/*****************************************************************************/ - -/* mirrors g_ascii_isspace() and what we consider spaces in general. */ -#define NM_ASCII_SPACES " \n\t\r\f" - -/* Like NM_ASCII_SPACES, but without "\f" (0x0c, Formfeed Page Break). - * This is what for example systemd calls WHITESPACE and what it uses to tokenize - * the kernel command line. */ -#define NM_ASCII_WHITESPACES " \n\t\r" - -#define nm_str_skip_leading_spaces(str) \ - ({ \ - typeof(*(str)) * _str_sls = (str); \ - _nm_unused const char *const _str_type_check = _str_sls; \ - \ - if (_str_sls) { \ - while (g_ascii_isspace(_str_sls[0])) \ - _str_sls++; \ - } \ - _str_sls; \ - }) - -static inline char * -nm_strstrip(char *str) -{ - /* g_strstrip doesn't like NULL. */ - return str ? g_strstrip(str) : NULL; -} - -static inline const char * -nm_strstrip_avoid_copy(const char *str, char **str_free) -{ - gsize l; - char *s; - - nm_assert(str_free && !*str_free); - - if (!str) - return NULL; - - str = nm_str_skip_leading_spaces(str); - l = strlen(str); - if (l == 0 || !g_ascii_isspace(str[l - 1])) - return str; - while (l > 0 && g_ascii_isspace(str[l - 1])) - l--; - - s = g_new(char, l + 1); - memcpy(s, str, l); - s[l] = '\0'; - *str_free = s; - return s; -} - -#define nm_strstrip_avoid_copy_a(alloca_maxlen, str, out_str_free) \ - ({ \ - const char *_str_ssac = (str); \ - char ** _out_str_free_ssac = (out_str_free); \ - \ - G_STATIC_ASSERT_EXPR((alloca_maxlen) > 0); \ - \ - nm_assert(_out_str_free_ssac || ((alloca_maxlen) > (str ? strlen(str) : 0u))); \ - nm_assert(!_out_str_free_ssac || !*_out_str_free_ssac); \ - \ - if (_str_ssac) { \ - _str_ssac = nm_str_skip_leading_spaces(_str_ssac); \ - if (_str_ssac[0] != '\0') { \ - gsize _l = strlen(_str_ssac); \ - \ - if (g_ascii_isspace(_str_ssac[--_l])) { \ - while (_l > 0 && g_ascii_isspace(_str_ssac[_l - 1])) { \ - _l--; \ - } \ - _str_ssac = nm_strndup_a((alloca_maxlen), _str_ssac, _l, _out_str_free_ssac); \ - } \ - } \ - } \ - \ - _str_ssac; \ - }) - -static inline gboolean -nm_str_is_stripped(const char *str) -{ - if (str && str[0]) { - if (g_ascii_isspace(str[0]) || g_ascii_isspace(str[strlen(str) - 1])) - return FALSE; - } - return TRUE; -} - -/* g_ptr_array_sort()'s compare function takes pointers to the - * value. Thus, you cannot use strcmp directly. You can use - * nm_strcmp_p(). - * - * Like strcmp(), this function is not forgiving to accept %NULL. */ -static inline int -nm_strcmp_p(gconstpointer a, gconstpointer b) -{ - const char *s1 = *((const char **) a); - const char *s2 = *((const char **) b); - - return strcmp(s1, s2); -} - -/*****************************************************************************/ - -static inline int -_NM_IN_STRSET_ASCII_CASE_op_streq(const char *x, const char *s) -{ - return s && g_ascii_strcasecmp(x, s) == 0; -} - -#define NM_IN_STRSET_ASCII_CASE(x, ...) \ - _NM_IN_STRSET_EVAL_N(||, \ - _NM_IN_STRSET_ASCII_CASE_op_streq, \ - x, \ - NM_NARG(__VA_ARGS__), \ - __VA_ARGS__) - -#define NM_STR_HAS_SUFFIX_ASCII_CASE(str, suffix) \ - ({ \ - const char *const _str_has_suffix = (str); \ - size_t _l; \ - \ - nm_assert(strlen(suffix) == NM_STRLEN(suffix)); \ - \ - (_str_has_suffix && ((_l = strlen(_str_has_suffix)) >= NM_STRLEN(suffix)) \ - && (g_ascii_strcasecmp(&_str_has_suffix[_l - NM_STRLEN(suffix)], "" suffix "") == 0)); \ - }) - -#define NM_STR_HAS_SUFFIX_ASCII_CASE_WITH_MORE(str, suffix) \ - ({ \ - const char *const _str_has_suffix = (str); \ - size_t _l; \ - \ - nm_assert(strlen(suffix) == NM_STRLEN(suffix)); \ - \ - (_str_has_suffix && ((_l = strlen(_str_has_suffix)) > NM_STRLEN(suffix)) \ - && (g_ascii_strcasecmp(&_str_has_suffix[_l - NM_STRLEN(suffix)], "" suffix "") == 0)); \ - }) - -/*****************************************************************************/ - -#define nm_g_slice_free(ptr) g_slice_free(typeof(*(ptr)), ptr) - -/*****************************************************************************/ - -/* like g_memdup(). The difference is that the @size argument is of type - * gsize, while g_memdup() has type guint. Since, the size of container types - * like GArray is guint as well, this means trying to g_memdup() an - * array, - * g_memdup (array->data, array->len * sizeof (ElementType)) - * will lead to integer overflow, if there are more than G_MAXUINT/sizeof(ElementType) - * bytes. That seems unnecessarily dangerous to me. - * nm_memdup() avoids that, because its size argument is always large enough - * to contain all data that a GArray can hold. - * - * Another minor difference to g_memdup() is that the glib version also - * returns %NULL if @data is %NULL. E.g. g_memdup(NULL, 1) - * gives %NULL, but nm_memdup(NULL, 1) crashes. I think that - * is desirable, because @size MUST be correct at all times. @size - * may be zero, but one must not claim to have non-zero bytes when - * passing a %NULL @data pointer. - */ -static inline gpointer -nm_memdup(gconstpointer data, gsize size) -{ - gpointer p; - - if (size == 0) - return NULL; - p = g_malloc(size); - memcpy(p, data, size); - return p; -} - -#define nm_malloc_maybe_a(alloca_maxlen, bytes, to_free) \ - ({ \ - const gsize _bytes = (bytes); \ - typeof(to_free) _to_free = (to_free); \ - typeof(*_to_free) _ptr; \ - \ - G_STATIC_ASSERT_EXPR((alloca_maxlen) <= 500u); \ - G_STATIC_ASSERT_EXPR((alloca_maxlen) > 0u); \ - nm_assert(_to_free && !*_to_free); \ - \ - if (G_LIKELY(_bytes <= (alloca_maxlen))) { \ - _ptr = _bytes > 0u ? g_alloca(_bytes) : NULL; \ - } else { \ - _ptr = g_malloc(_bytes); \ - *_to_free = _ptr; \ - }; \ - \ - _ptr; \ - }) - -#define nm_malloc0_maybe_a(alloca_maxlen, bytes, to_free) \ - ({ \ - const gsize _bytes = (bytes); \ - typeof(to_free) _to_free = (to_free); \ - typeof(*_to_free) _ptr; \ - \ - G_STATIC_ASSERT_EXPR((alloca_maxlen) <= 500u); \ - G_STATIC_ASSERT_EXPR((alloca_maxlen) > 0u); \ - nm_assert(_to_free && !*_to_free); \ - \ - if (G_LIKELY(_bytes <= (alloca_maxlen))) { \ - if (_bytes > 0u) { \ - _ptr = g_alloca(_bytes); \ - memset(_ptr, 0, _bytes); \ - } else \ - _ptr = NULL; \ - } else { \ - _ptr = g_malloc0(_bytes); \ - *_to_free = _ptr; \ - }; \ - \ - _ptr; \ - }) - -#define nm_memdup_maybe_a(alloca_maxlen, data, size, to_free) \ - ({ \ - const gsize _size = (size); \ - typeof(to_free) _to_free_md = (to_free); \ - typeof(*_to_free_md) _ptr_md = NULL; \ - \ - nm_assert(_to_free_md && !*_to_free_md); \ - \ - if (_size > 0u) { \ - _ptr_md = nm_malloc_maybe_a((alloca_maxlen), _size, _to_free_md); \ - memcpy(_ptr_md, (data), _size); \ - } \ - \ - _ptr_md; \ - }) - -static inline char * -_nm_strndup_a_step(char *s, const char *str, gsize len) -{ - NM_PRAGMA_WARNING_DISABLE("-Wstringop-truncation"); - NM_PRAGMA_WARNING_DISABLE("-Wstringop-overflow"); - if (len > 0) - strncpy(s, str, len); - s[len] = '\0'; - return s; - NM_PRAGMA_WARNING_REENABLE; - NM_PRAGMA_WARNING_REENABLE; -} - -/* Similar to g_strndup(), however, if the string (including the terminating - * NUL char) fits into alloca_maxlen, this will alloca() the memory. - * - * It's a mix of strndup() and strndupa(), but deciding based on @alloca_maxlen - * which one to use. - * - * In case malloc() is necessary, @out_str_free will be set (this string - * must be freed afterwards). It is permissible to pass %NULL as @out_str_free, - * if you ensure that len < alloca_maxlen. - * - * Note that just like g_strndup(), this always returns a buffer with @len + 1 - * bytes, even if strlen(@str) is shorter than that (NUL terminated early). We fill - * the buffer with strncpy(), which means, that @str is copied up to the first - * NUL character and then filled with NUL characters. */ -#define nm_strndup_a(alloca_maxlen, str, len, out_str_free) \ - ({ \ - const gsize _alloca_maxlen_snd = (alloca_maxlen); \ - const char *const _str_snd = (str); \ - const gsize _len_snd = (len); \ - char **const _out_str_free_snd = (out_str_free); \ - char * _s_snd; \ - \ - G_STATIC_ASSERT_EXPR((alloca_maxlen) <= 300); \ - \ - if (_out_str_free_snd && _len_snd >= _alloca_maxlen_snd) { \ - _s_snd = g_malloc(_len_snd + 1); \ - *_out_str_free_snd = _s_snd; \ - } else { \ - g_assert(_len_snd < _alloca_maxlen_snd); \ - _s_snd = g_alloca(_len_snd + 1); \ - } \ - _nm_strndup_a_step(_s_snd, _str_snd, _len_snd); \ - }) - -#define nm_strdup_maybe_a(alloca_maxlen, str, out_str_free) \ - ({ \ - const char *const _str_snd = (str); \ - \ - (char *) nm_memdup_maybe_a(alloca_maxlen, \ - _str_snd, \ - _str_snd ? strlen(_str_snd) + 1u : 0u, \ - out_str_free); \ - }) - -/*****************************************************************************/ - -/* generic macro to convert an int to a (heap allocated) string. - * - * Usually, an inline function nm_strdup_int64() would be enough. However, - * that cannot be used for guint64. So, we would also need nm_strdup_uint64(). - * This causes subtle error potential, because the caller needs to ensure to - * use the right one (and compiler isn't going to help as it silently casts). - * - * Instead, this generic macro is supposed to handle all integers correctly. */ -#if _NM_CC_SUPPORT_GENERIC - #define nm_strdup_int(val) \ - _Generic((val), char \ - : g_strdup_printf("%d", (int) (val)), \ - \ - signed char \ - : g_strdup_printf("%d", (signed) (val)), signed short \ - : g_strdup_printf("%d", (signed) (val)), signed \ - : g_strdup_printf("%d", (signed) (val)), signed long \ - : g_strdup_printf("%ld", (signed long) (val)), signed long long \ - : g_strdup_printf("%lld", (signed long long) (val)), \ - \ - unsigned char \ - : g_strdup_printf("%u", (unsigned) (val)), unsigned short \ - : g_strdup_printf("%u", (unsigned) (val)), unsigned \ - : g_strdup_printf("%u", (unsigned) (val)), unsigned long \ - : g_strdup_printf("%lu", (unsigned long) (val)), unsigned long long \ - : g_strdup_printf("%llu", (unsigned long long) (val))) -#else - #define nm_strdup_int(val) \ - ((sizeof(val) == sizeof(guint64) && ((typeof(val)) - 1) > 0) \ - ? g_strdup_printf("%" G_GUINT64_FORMAT, (guint64)(val)) \ - : g_strdup_printf("%" G_GINT64_FORMAT, (gint64)(val))) -#endif - -/*****************************************************************************/ - -static inline guint -nm_encode_version(guint major, guint minor, guint micro) -{ - /* analog to the preprocessor macro NM_ENCODE_VERSION(). */ - return (major << 16) | (minor << 8) | micro; -} - -static inline void -nm_decode_version(guint version, guint *major, guint *minor, guint *micro) -{ - *major = (version & 0xFFFF0000u) >> 16; - *minor = (version & 0x0000FF00u) >> 8; - *micro = (version & 0x000000FFu); -} - -/*****************************************************************************/ - -/* taken from systemd's DECIMAL_STR_MAX() - * - * Returns the number of chars needed to format variables of the - * specified type as a decimal string. Adds in extra space for a - * negative '-' prefix (hence works correctly on signed - * types). Includes space for the trailing NUL. */ -#define NM_DECIMAL_STR_MAX(type) \ - (2 \ - + (sizeof(type) <= 1 ? 3 \ - : sizeof(type) <= 2 ? 5 \ - : sizeof(type) <= 4 ? 10 \ - : sizeof(type) <= 8 ? 20 \ - : sizeof(int[-2 * (sizeof(type) > 8)]))) - -/*****************************************************************************/ - -/* if @str is NULL, return "(null)". Otherwise, allocate a buffer using - * alloca() of and fill it with @str. @str will be quoted with double quote. - * If @str is longer then @trunc_at, the string is truncated and the closing - * quote is instead '^' to indicate truncation. - * - * Thus, the maximum stack allocated buffer will be @trunc_at+3. The maximum - * buffer size must be a constant and not larger than 300. */ -#define nm_strquote_a(trunc_at, str) \ - ({ \ - const char *const _str = (str); \ - \ - (_str ? ({ \ - const gsize _trunc_at = (trunc_at); \ - const gsize _strlen_trunc = NM_MIN(strlen(_str), _trunc_at); \ - char * _buf; \ - \ - G_STATIC_ASSERT_EXPR((trunc_at) <= 300); \ - \ - _buf = g_alloca(_strlen_trunc + 3); \ - _buf[0] = '"'; \ - memcpy(&_buf[1], _str, _strlen_trunc); \ - _buf[_strlen_trunc + 1] = _str[_strlen_trunc] ? '^' : '"'; \ - _buf[_strlen_trunc + 2] = '\0'; \ - _buf; \ - }) \ - : "(null)"); \ - }) - -#define nm_sprintf_buf(buf, format, ...) \ - ({ \ - char *_buf = (buf); \ - int _buf_len; \ - \ - /* some static assert trying to ensure that the buffer is statically allocated. - * It disallows a buffer size of sizeof(gpointer) to catch that. */ \ - G_STATIC_ASSERT(G_N_ELEMENTS(buf) == sizeof(buf) && sizeof(buf) != sizeof(char *)); \ - _buf_len = g_snprintf(_buf, sizeof(buf), "" format "", ##__VA_ARGS__); \ - nm_assert(_buf_len < sizeof(buf)); \ - _buf; \ - }) - -/* it is "unsafe" because @bufsize must not be a constant expression and - * there is no check at compiletime. Regardless of that, the buffer size - * must not be larger than 300 bytes, as this gets stack allocated. */ -#define nm_sprintf_buf_unsafe_a(bufsize, format, ...) \ - ({ \ - char *_buf; \ - int _buf_len; \ - typeof(bufsize) _bufsize = (bufsize); \ - \ - nm_assert(_bufsize <= 300); \ - \ - _buf = g_alloca(_bufsize); \ - _buf_len = g_snprintf(_buf, _bufsize, "" format "", ##__VA_ARGS__); \ - nm_assert(_buf_len >= 0 && _buf_len < _bufsize); \ - _buf; \ - }) - -#define nm_sprintf_bufa(bufsize, format, ...) \ - ({ \ - G_STATIC_ASSERT_EXPR((bufsize) <= 300); \ - nm_sprintf_buf_unsafe_a((bufsize), format, ##__VA_ARGS__); \ - }) - -/* aims to alloca() a buffer and fill it with printf(format, name). - * Note that format must not contain any format specifier except - * "%s". - * If the resulting string would be too large for stack allocation, - * it allocates a buffer with g_malloc() and assigns it to *p_val_to_free. */ -#define nm_construct_name_a(format, name, p_val_to_free) \ - ({ \ - const char *const _name = (name); \ - char **const _p_val_to_free = (p_val_to_free); \ - const gsize _name_len = strlen(_name); \ - char * _buf2; \ - \ - nm_assert(_p_val_to_free && !*_p_val_to_free); \ - if (NM_STRLEN(format) <= 290 && _name_len < (gsize)(290 - NM_STRLEN(format))) \ - _buf2 = nm_sprintf_buf_unsafe_a(NM_STRLEN(format) + _name_len, format, _name); \ - else { \ - _buf2 = g_strdup_printf(format, _name); \ - *_p_val_to_free = _buf2; \ - } \ - (const char *) _buf2; \ - }) - -/*****************************************************************************/ - -#ifdef _G_BOOLEAN_EXPR - /* g_assert() uses G_LIKELY(), which in turn uses _G_BOOLEAN_EXPR(). - * As glib's implementation uses a local variable _g_boolean_var_, - * we cannot do - * g_assert (some_macro ()); - * where some_macro() itself expands to ({g_assert(); ...}). - * In other words, you cannot have a g_assert() inside a g_assert() - * without getting a -Werror=shadow failure. - * - * Workaround that by re-defining _G_BOOLEAN_EXPR() - **/ - #undef _G_BOOLEAN_EXPR - #define _G_BOOLEAN_EXPR(expr) NM_BOOLEAN_EXPR(expr) -#endif - -/*****************************************************************************/ - -#define NM_PID_T_INVAL ((pid_t) -1) - -/*****************************************************************************/ - -NM_AUTO_DEFINE_FCN_VOID0(GMutex *, _nm_auto_unlock_g_mutex, g_mutex_unlock); - -#define nm_auto_unlock_g_mutex nm_auto(_nm_auto_unlock_g_mutex) - -#define _NM_G_MUTEX_LOCKED(lock, uniq) \ - _nm_unused nm_auto_unlock_g_mutex GMutex *NM_UNIQ_T(nm_lock, uniq) = ({ \ - GMutex *const _lock = (lock); \ - \ - g_mutex_lock(_lock); \ - _lock; \ - }) - -#define NM_G_MUTEX_LOCKED(lock) _NM_G_MUTEX_LOCKED(lock, NM_UNIQ) - -/*****************************************************************************/ - -#endif /* __NM_MACROS_INTERNAL_H__ */ diff --git a/shared/nm-glib-aux/nm-obj.h b/shared/nm-glib-aux/nm-obj.h deleted file mode 100644 index 2062f661..00000000 --- a/shared/nm-glib-aux/nm-obj.h +++ /dev/null @@ -1,66 +0,0 @@ -/* SPDX-License-Identifier: LGPL-2.1-or-later */ -/* - * Copyright (C) 2017 Red Hat, Inc. - */ - -#ifndef __NM_OBJ_H__ -#define __NM_OBJ_H__ - -/*****************************************************************************/ - -#define NM_OBJ_REF_COUNT_STACKINIT (G_MAXINT) - -typedef struct _NMObjBaseInst NMObjBaseInst; -typedef struct _NMObjBaseClass NMObjBaseClass; - -struct _NMObjBaseInst { - /* The first field of NMObjBaseInst is compatible with GObject. - * Basically, NMObjBaseInst is an abstract base type of GTypeInstance. - * - * If you do it right, you may derive a type of NMObjBaseInst as a proper GTypeInstance. - * That involves allocating a GType for it, which can be inconvenient because - * a GType is dynamically created (and the class can no longer be immutable - * memory). - * - * Even if your implementation of NMObjBaseInst is not a full fledged GType(Instance), - * you still can use GTypeInstances in the same context as you can decide based on the - * NMObjBaseClass with what kind of object you are dealing with. - * - * Basically, the only thing NMObjBaseInst gives you is access to an - * NMObjBaseClass instance. - */ - union { - const NMObjBaseClass *klass; - GTypeInstance g_type_instance; - }; -}; - -struct _NMObjBaseClass { - /* NMObjBaseClass is the base class of all NMObjBaseInst implementations. - * Note that it is also an abstract super class of GTypeInstance, that means - * you may implement a NMObjBaseClass as a subtype of GTypeClass. - * - * For that to work, you must properly set the GTypeClass instance (and its - * GType). - * - * Note that to implement a NMObjBaseClass that is *not* a GTypeClass, you wouldn't - * set the GType. Hence, this field is only useful for type implementations that actually - * extend GTypeClass. - * - * In a way it is wrong that NMObjBaseClass has the GType member, because it is - * a base class of GTypeClass and doesn't necessarily use the GType. However, - * it is here so that G_TYPE_CHECK_INSTANCE_TYPE() and friends work correctly - * on any NMObjectClass. That means, while not necessary, it is convenient that - * a NMObjBaseClass has all members of GTypeClass. - * Also note that usually you have only one instance of a certain type, so this - * wastes just a few bytes for the unneeded GType. - */ - union { - GType g_type; - GTypeClass g_type_class; - }; -}; - -/*****************************************************************************/ - -#endif /* __NM_OBJ_H__ */ diff --git a/shared/nm-glib-aux/nm-random-utils.c b/shared/nm-glib-aux/nm-random-utils.c deleted file mode 100644 index 97272ca3..00000000 --- a/shared/nm-glib-aux/nm-random-utils.c +++ /dev/null @@ -1,150 +0,0 @@ -/* SPDX-License-Identifier: LGPL-2.1-or-later */ -/* - * Copyright (C) 2017 Red Hat, Inc. - */ - -#include "nm-glib-aux/nm-default-glib-i18n-lib.h" - -#include "nm-random-utils.h" - -#include <fcntl.h> - -#if USE_SYS_RANDOM_H - #include <sys/random.h> -#else - #include <linux/random.h> -#endif - -#include "nm-shared-utils.h" - -/*****************************************************************************/ - -/** - * nm_utils_random_bytes: - * @p: the buffer to fill - * @n: the number of bytes to write to @p. - * - * Uses getrandom() or reads /dev/urandom to fill the buffer - * with random data. If all fails, as last fallback it uses - * GRand to fill the buffer with pseudo random numbers. - * The function always succeeds in writing some random numbers - * to the buffer. The return value of FALSE indicates that the - * obtained bytes are probably not of good randomness. - * - * Returns: whether the written bytes are good. If you - * don't require good randomness, you can ignore the return - * value. - * - * Note that if calling getrandom() fails because there is not enough - * entropy (at early boot), the function will read /dev/urandom. - * Which of course, still has low entropy, and cause kernel to log - * a warning. - */ -gboolean -nm_utils_random_bytes(void *p, size_t n) -{ - int fd; - int r; - gboolean has_high_quality = TRUE; - gboolean urandom_success; - guint8 * buf = p; - gboolean avoid_urandom = FALSE; - - g_return_val_if_fail(p, FALSE); - g_return_val_if_fail(n > 0, FALSE); - -#if HAVE_GETRANDOM - { - static gboolean have_syscall = TRUE; - - if (have_syscall) { - r = getrandom(buf, n, GRND_NONBLOCK); - if (r > 0) { - if ((size_t) r == n) - return TRUE; - - /* no or partial read. There is not enough entropy. - * Fill the rest reading from urandom, and remember that - * some bits are not high quality. */ - nm_assert(r < n); - buf += r; - n -= r; - has_high_quality = FALSE; - - /* At this point, we don't want to read /dev/urandom, because - * the entropy pool is low (early boot?), and asking for more - * entropy causes kernel messages to be logged. - * - * We use our fallback via GRand. Note that g_rand_new() also - * tries to seed itself with data from /dev/urandom, but since - * we reuse the instance, it shouldn't matter. */ - avoid_urandom = TRUE; - } else { - if (errno == ENOSYS) { - /* no support for getrandom(). We don't know whether - * we urandom will give us good quality. Assume yes. */ - have_syscall = FALSE; - } else { - /* unknown error. We'll read urandom below, but we don't have - * high-quality randomness. */ - has_high_quality = FALSE; - } - } - } - } -#endif - - urandom_success = FALSE; - if (!avoid_urandom) { -fd_open: - fd = open("/dev/urandom", O_RDONLY | O_CLOEXEC | O_NOCTTY); - if (fd < 0) { - r = errno; - if (r == EINTR) - goto fd_open; - } else { - r = nm_utils_fd_read_loop_exact(fd, buf, n, TRUE); - nm_close(fd); - if (r >= 0) - urandom_success = TRUE; - } - } - - if (!urandom_success) { - static _nm_thread_local GRand *rand = NULL; - gsize i; - int j; - - /* we failed to fill the bytes reading from urandom. - * Fill the bits using GRand pseudo random numbers. - * - * We don't have good quality. - */ - has_high_quality = FALSE; - - if (G_UNLIKELY(!rand)) { - rand = g_rand_new(); - nm_utils_thread_local_register_destroy(rand, (GDestroyNotify) g_rand_free); - } - - nm_assert(n > 0); - i = 0; - for (;;) { - const union { - guint32 v32; - guint8 v8[4]; - } v = { - .v32 = g_rand_int(rand), - }; - - for (j = 0; j < 4;) { - buf[i++] = v.v8[j++]; - if (i >= n) - goto done; - } - } -done:; - } - - return has_high_quality; -} diff --git a/shared/nm-glib-aux/nm-random-utils.h b/shared/nm-glib-aux/nm-random-utils.h deleted file mode 100644 index d0eae103..00000000 --- a/shared/nm-glib-aux/nm-random-utils.h +++ /dev/null @@ -1,11 +0,0 @@ -/* SPDX-License-Identifier: LGPL-2.1-or-later */ -/* - * Copyright (C) 2017 Red Hat, Inc. - */ - -#ifndef __NM_RANDOM_UTILS_H__ -#define __NM_RANDOM_UTILS_H__ - -gboolean nm_utils_random_bytes(void *p, size_t n); - -#endif /* __NM_RANDOM_UTILS_H__ */ diff --git a/shared/nm-glib-aux/nm-ref-string.c b/shared/nm-glib-aux/nm-ref-string.c deleted file mode 100644 index 1084c47f..00000000 --- a/shared/nm-glib-aux/nm-ref-string.c +++ /dev/null @@ -1,203 +0,0 @@ -/* SPDX-License-Identifier: LGPL-2.1-or-later */ - -#include "nm-glib-aux/nm-default-glib-i18n-lib.h" - -#include "nm-ref-string.h" - -/*****************************************************************************/ - -typedef struct { - NMRefString r; - volatile int ref_count; - char str_data[]; -} RefString; - -G_LOCK_DEFINE_STATIC(gl_lock); -static GHashTable *gl_hash; - -/* the first field of NMRefString is a pointer to the NUL terminated string. - * This also allows to compare strings with nm_pstr_equal(), although, pointer - * equality might be better. */ -G_STATIC_ASSERT(G_STRUCT_OFFSET(NMRefString, str) == 0); -G_STATIC_ASSERT(G_STRUCT_OFFSET(RefString, r) == 0); -G_STATIC_ASSERT(G_STRUCT_OFFSET(RefString, r.str) == 0); - -/*****************************************************************************/ - -static guint -_ref_string_hash(gconstpointer ptr) -{ - const RefString *a = ptr; - NMHashState h; - - nm_hash_init(&h, 1463435489u); - nm_hash_update(&h, a->r.str, a->r.len); - return nm_hash_complete(&h); -} - -static gboolean -_ref_string_equal(gconstpointer pa, gconstpointer pb) -{ - const RefString *a = pa; - const RefString *b = pb; - - return a->r.len == b->r.len && memcmp(a->r.str, b->r.str, a->r.len) == 0; -} - -/*****************************************************************************/ - -static void -_ASSERT(const RefString *rstr0) -{ - int r; - - nm_assert(rstr0); - - if (NM_MORE_ASSERTS > 0) { - r = g_atomic_int_get(&rstr0->ref_count); - nm_assert(r > 0); - nm_assert(r < G_MAXINT); - } - - nm_assert(rstr0->r.str == rstr0->str_data); - nm_assert(rstr0->r.str[rstr0->r.len] == '\0'); - - if (NM_MORE_ASSERTS > 10) { - G_LOCK(gl_lock); - r = g_atomic_int_get(&rstr0->ref_count); - nm_assert(r > 0); - nm_assert(r < G_MAXINT); - - nm_assert(rstr0 == g_hash_table_lookup(gl_hash, rstr0)); - G_UNLOCK(gl_lock); - } -} - -/** - * nm_ref_string_new_len: - * @cstr: the string to intern. Must contain @len bytes. - * If @len is zero, @cstr may be %NULL. Note that it is - * acceptable that the string contains a NUL character - * within the first @len bytes. That is, the string is - * not treated as a NUL terminated string, but as binary. - * Also, contrary to strncpy(), this will read all the - * first @len bytes. It won't stop at the first NUL. - * @len: the length of the string (usually there is no NUL character - * within the first @len bytes, but that would be acceptable as well - * to add binary data). - * - * Note that the resulting NMRefString instance will always be NUL terminated - * (at position @len). - * - * Note that NMRefString are always interned/deduplicated. If such a string - * already exists, the existing instance will be referred and returned. - * - * - * Since all NMRefString are shared and interned, you may use - * pointer equality to compare them. Note that if a NMRefString contains - * a NUL character (meaning, if - * - * strlen (nm_ref_string_get_str (str)) != nm_ref_string_get_len (str) - * - * ), then pointer in-equality does not mean that the NUL terminated strings - * are also unequal. In other words, for strings that contain NUL characters, - * - * if (str1 != str2) - * assert (!nm_streq0 (nm_ref_string_get_str (str1), nm_ref_string_get_str (str2))); - * - * might not hold! - * - * - * NMRefString is thread-safe. - * - * Returns: (transfer full): the interned string. This is - * never %NULL, but note that %NULL is also a valid NMRefString. - * The result must be unrefed with nm_ref_string_unref(). - */ -NMRefString * -nm_ref_string_new_len(const char *cstr, gsize len) -{ - RefString *rstr0; - - G_LOCK(gl_lock); - - if (G_UNLIKELY(!gl_hash)) { - gl_hash = g_hash_table_new_full(_ref_string_hash, _ref_string_equal, g_free, NULL); - rstr0 = NULL; - } else { - NMRefString rr_lookup = { - .len = len, - .str = cstr, - }; - - rstr0 = g_hash_table_lookup(gl_hash, &rr_lookup); - } - - if (rstr0) { - nm_assert(({ - int r = g_atomic_int_get(&rstr0->ref_count); - - (r >= 0 && r < G_MAXINT); - })); - g_atomic_int_inc(&rstr0->ref_count); - } else { - rstr0 = g_malloc(sizeof(RefString) + 1 + len); - rstr0->ref_count = 1; - *((gsize *) &rstr0->r.len) = len; - *((const char **) &rstr0->r.str) = rstr0->str_data; - if (len > 0) - memcpy(rstr0->str_data, cstr, len); - rstr0->str_data[len] = '\0'; - - if (!g_hash_table_add(gl_hash, rstr0)) - nm_assert_not_reached(); - } - - G_UNLOCK(gl_lock); - - return &rstr0->r; -} - -NMRefString * -nm_ref_string_ref(NMRefString *rstr) -{ - RefString *const rstr0 = (RefString *) rstr; - - if (!rstr) - return NULL; - - _ASSERT(rstr0); - - g_atomic_int_inc(&rstr0->ref_count); - return &rstr0->r; -} - -void -_nm_ref_string_unref_non_null(NMRefString *rstr) -{ - RefString *const rstr0 = (RefString *) rstr; - int r; - - _ASSERT(rstr0); - - /* fast-path: first try to decrement the ref-count without bringing it - * to zero. */ - r = rstr0->ref_count; - if (G_LIKELY(r > 1 && g_atomic_int_compare_and_exchange(&rstr0->ref_count, r, r - 1))) - return; - - /* We apparently are about to return the last reference. Take a lock. */ - - G_LOCK(gl_lock); - - nm_assert(g_hash_table_lookup(gl_hash, rstr0) == rstr0); - - if (G_LIKELY(g_atomic_int_dec_and_test(&rstr0->ref_count))) { - if (!g_hash_table_remove(gl_hash, rstr0)) - nm_assert_not_reached(); - } - - G_UNLOCK(gl_lock); -} - -/*****************************************************************************/ diff --git a/shared/nm-glib-aux/nm-ref-string.h b/shared/nm-glib-aux/nm-ref-string.h deleted file mode 100644 index 97c263b0..00000000 --- a/shared/nm-glib-aux/nm-ref-string.h +++ /dev/null @@ -1,79 +0,0 @@ -/* SPDX-License-Identifier: LGPL-2.1-or-later */ - -#ifndef __NM_REF_STRING_H__ -#define __NM_REF_STRING_H__ - -/*****************************************************************************/ - -typedef struct _NMRefString { - const char *const str; - const gsize len; -} NMRefString; - -/*****************************************************************************/ - -NMRefString *nm_ref_string_new_len(const char *cstr, gsize len); - -static inline NMRefString * -nm_ref_string_new(const char *cstr) -{ - return cstr ? nm_ref_string_new_len(cstr, strlen(cstr)) : NULL; -} - -NMRefString *nm_ref_string_ref(NMRefString *rstr); -void _nm_ref_string_unref_non_null(NMRefString *rstr); - -static inline void -nm_ref_string_unref(NMRefString *rstr) -{ - if (rstr) - _nm_ref_string_unref_non_null(rstr); -} - -NM_AUTO_DEFINE_FCN_VOID0(NMRefString *, _nm_auto_ref_string, _nm_ref_string_unref_non_null); -#define nm_auto_ref_string nm_auto(_nm_auto_ref_string) - -/*****************************************************************************/ - -static inline const char * -nm_ref_string_get_str(NMRefString *rstr) -{ - return rstr ? rstr->str : NULL; -} - -static inline gsize -nm_ref_string_get_len(NMRefString *rstr) -{ - return rstr ? rstr->len : 0u; -} - -static inline gboolean -nm_ref_string_equals_str(NMRefString *rstr, const char *s) -{ - /* Note that rstr->len might be greater than strlen(rstr->str). This function does - * not cover that and would ignore everything after the first NUL byte. If you need - * that distinction, this function is not for you. */ - - return rstr ? (s && nm_streq(rstr->str, s)) : (s == NULL); -} - -static inline gboolean -NM_IS_REF_STRING(const NMRefString *rstr) -{ -#if NM_MORE_ASSERTS > 10 - if (rstr) { - nm_auto_ref_string NMRefString *r2 = NULL; - - r2 = nm_ref_string_new_len(rstr->str, rstr->len); - nm_assert(rstr == r2); - } -#endif - - /* Technically, %NULL is also a valid NMRefString (according to nm_ref_string_new(), - * nm_ref_string_get_str() and nm_ref_string_unref()). However, NM_IS_REF_STRING() - * does not think so. If callers want to allow %NULL, they need to check - * separately. */ - return !!rstr; -} - -#endif /* __NM_REF_STRING_H__ */ diff --git a/shared/nm-glib-aux/nm-secret-utils.c b/shared/nm-glib-aux/nm-secret-utils.c deleted file mode 100644 index 8188b503..00000000 --- a/shared/nm-glib-aux/nm-secret-utils.c +++ /dev/null @@ -1,178 +0,0 @@ -/* SPDX-License-Identifier: LGPL-2.1-or-later */ -/* - * Copyright (C) 2018 Red Hat, Inc. - * Copyright (C) 2015 - 2019 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved. - */ - -#include "nm-glib-aux/nm-default-glib-i18n-lib.h" - -#include "nm-secret-utils.h" - -#include <malloc.h> - -/*****************************************************************************/ - -void -nm_explicit_bzero(void *s, gsize n) -{ - /* gracefully handle n == 0. This is important, callers rely on it. */ - if (G_UNLIKELY(n == 0)) - return; - - nm_assert(s); - -#if defined(HAVE_DECL_EXPLICIT_BZERO) && HAVE_DECL_EXPLICIT_BZERO - explicit_bzero(s, n); -#else - { - volatile guint8 *p = s; - - memset(s, '\0', n); - while (n-- > 0) - *(p++) = '\0'; - } -#endif -} - -void -nm_free_secret(char *secret) -{ - gsize len; - - if (!secret) - return; - -#if GLIB_CHECK_VERSION(2, 44, 0) - /* Here we mix malloc() and g_malloc() API. Usually we avoid this, - * however since glib 2.44.0 we are in fact guaranteed that g_malloc()/g_free() - * just wraps malloc()/free(), so this is actually fine. - * - * See https://gitlab.gnome.org/GNOME/glib/commit/3be6ed60aa58095691bd697344765e715a327fc1 - */ - len = malloc_usable_size(secret); -#else - len = strlen(secret); -#endif - - nm_explicit_bzero(secret, len); - g_free(secret); -} - -/*****************************************************************************/ - -char * -nm_secret_strchomp(char *secret) -{ - gsize len; - - g_return_val_if_fail(secret, NULL); - - /* it's actually identical to g_strchomp(). However, - * the glib function does not document, that it clears the - * memory. For @secret, we don't only want to truncate trailing - * spaces, we want to overwrite them with NUL. */ - - len = strlen(secret); - while (len--) { - if (g_ascii_isspace((guchar) secret[len])) - secret[len] = '\0'; - else - break; - } - - return secret; -} - -/*****************************************************************************/ - -GBytes * -nm_secret_copy_to_gbytes(gconstpointer mem, gsize mem_len) -{ - NMSecretBuf *b; - - if (mem_len == 0) - return g_bytes_new_static("", 0); - - nm_assert(mem); - - /* NUL terminate the buffer. - * - * The entire buffer is already malloc'ed and likely has some room for padding. - * Thus, in many situations, this additional byte will cause no overhead in - * practice. - * - * Even if it causes an overhead, do it just for safety. Yes, the returned - * bytes is not a NUL terminated string and no user must rely on this. Do - * not treat binary data as NUL terminated strings, unless you know what - * you are doing. Anyway, defensive FTW. - */ - - b = nm_secret_buf_new(mem_len + 1); - memcpy(b->bin, mem, mem_len); - b->bin[mem_len] = 0; - return nm_secret_buf_to_gbytes_take(b, mem_len); -} - -/*****************************************************************************/ - -NMSecretBuf * -nm_secret_buf_new(gsize len) -{ - NMSecretBuf *secret; - - nm_assert(len > 0); - - secret = g_malloc(sizeof(NMSecretBuf) + len); - *((gsize *) &(secret->len)) = len; - return secret; -} - -static void -_secret_buf_free(gpointer user_data) -{ - NMSecretBuf *secret = user_data; - - nm_assert(secret); - nm_assert(secret->len > 0); - - nm_explicit_bzero(secret->bin, secret->len); - g_free(user_data); -} - -GBytes * -nm_secret_buf_to_gbytes_take(NMSecretBuf *secret, gssize actual_len) -{ - nm_assert(secret); - nm_assert(secret->len > 0); - nm_assert(actual_len == -1 || (actual_len >= 0 && actual_len <= secret->len)); - return g_bytes_new_with_free_func(secret->bin, - actual_len >= 0 ? (gsize) actual_len : secret->len, - _secret_buf_free, - secret); -} - -/*****************************************************************************/ - -/** - * nm_utils_memeqzero_secret: - * @data: the data pointer to check (may be %NULL if @length is zero). - * @length: the number of bytes to check. - * - * Checks that all bytes are zero. This always takes the same amount - * of time to prevent timing attacks. - * - * Returns: whether all bytes are zero. - */ -gboolean -nm_utils_memeqzero_secret(gconstpointer data, gsize length) -{ - const guint8 *const key = data; - volatile guint8 acc = 0; - gsize i; - - for (i = 0; i < length; i++) { - acc |= key[i]; - asm volatile("" : "=r"(acc) : "0"(acc)); - } - return 1 & ((acc - 1) >> 8); -} diff --git a/shared/nm-glib-aux/nm-secret-utils.h b/shared/nm-glib-aux/nm-secret-utils.h deleted file mode 100644 index ac279635..00000000 --- a/shared/nm-glib-aux/nm-secret-utils.h +++ /dev/null @@ -1,273 +0,0 @@ -/* SPDX-License-Identifier: LGPL-2.1-or-later */ -/* - * Copyright (C) 2018 Red Hat, Inc. - */ - -#ifndef __NM_SECRET_UTILS_H__ -#define __NM_SECRET_UTILS_H__ - -#include "nm-macros-internal.h" - -/*****************************************************************************/ - -void nm_explicit_bzero(void *s, gsize n); - -/*****************************************************************************/ - -char *nm_secret_strchomp(char *secret); - -/*****************************************************************************/ - -void nm_free_secret(char *secret); - -NM_AUTO_DEFINE_FCN0(char *, _nm_auto_free_secret, nm_free_secret); -/** - * nm_auto_free_secret: - * - * Call g_free() on a variable location when it goes out of scope. - * Also, previously, calls memset(loc, 0, strlen(loc)) to clear out - * the secret. - */ -#define nm_auto_free_secret nm_auto(_nm_auto_free_secret) - -/*****************************************************************************/ - -GBytes *nm_secret_copy_to_gbytes(gconstpointer mem, gsize mem_len); - -/*****************************************************************************/ - -/* NMSecretPtr is a pair of malloc'ed data pointer and the length of the - * data. The purpose is to use it in combination with nm_auto_clear_secret_ptr - * which ensures that the data pointer (with all len bytes) is cleared upon - * cleanup. */ -typedef struct { - gsize len; - - /* the data pointer. This pointer must be allocated with malloc (at least - * when used with nm_secret_ptr_clear()). */ - union { - char * str; - void * ptr; - guint8 *bin; - }; -} NMSecretPtr; - -static inline void -nm_secret_ptr_bzero(NMSecretPtr *secret) -{ - if (secret) { - if (secret->len > 0) { - if (secret->ptr) - nm_explicit_bzero(secret->ptr, secret->len); - } - } -} - -#define nm_auto_bzero_secret_ptr nm_auto(nm_secret_ptr_bzero) - -static inline void -nm_secret_ptr_clear(NMSecretPtr *secret) -{ - if (secret) { - if (secret->len > 0) { - if (secret->ptr) - nm_explicit_bzero(secret->ptr, secret->len); - secret->len = 0; - } - nm_clear_g_free(&secret->ptr); - } -} - -#define nm_auto_clear_secret_ptr nm_auto(nm_secret_ptr_clear) - -#define NM_SECRET_PTR_INIT() \ - ((const NMSecretPtr){ \ - .len = 0, \ - .ptr = NULL, \ - }) - -#define NM_SECRET_PTR_STATIC(_len) \ - ((const NMSecretPtr){ \ - .len = _len, \ - .ptr = ((guint8[_len]){}), \ - }) - -#define NM_SECRET_PTR_ARRAY(_arr) \ - ((const NMSecretPtr){ \ - .len = G_N_ELEMENTS(_arr) * sizeof((_arr)[0]), \ - .ptr = &((_arr)[0]), \ - }) - -static inline void -nm_secret_ptr_clear_static(const NMSecretPtr *secret) -{ - if (secret) { - if (secret->len > 0) { - nm_assert(secret->ptr); - nm_explicit_bzero(secret->ptr, secret->len); - } - } -} - -#define nm_auto_clear_static_secret_ptr nm_auto(nm_secret_ptr_clear_static) - -static inline void -nm_secret_ptr_move(NMSecretPtr *dst, NMSecretPtr *src) -{ - if (dst && dst != src) { - *dst = *src; - src->len = 0; - src->ptr = NULL; - } -} - -/*****************************************************************************/ - -typedef struct { - const gsize len; - union { - char str[0]; - guint8 bin[0]; - }; -} NMSecretBuf; - -static inline void -_nm_auto_free_secret_buf(NMSecretBuf **ptr) -{ - NMSecretBuf *b = *ptr; - - if (b) { - nm_assert(b->len > 0); - nm_explicit_bzero(b->bin, b->len); - g_free(b); - } -} -#define nm_auto_free_secret_buf nm_auto(_nm_auto_free_secret_buf) - -NMSecretBuf *nm_secret_buf_new(gsize len); - -GBytes *nm_secret_buf_to_gbytes_take(NMSecretBuf *secret, gssize actual_len); - -/*****************************************************************************/ - -gboolean nm_utils_memeqzero_secret(gconstpointer data, gsize length); - -/*****************************************************************************/ - -/** - * nm_secret_mem_realloc: - * @m_old: the current buffer of length @cur_len. - * @do_bzero_mem: if %TRUE, bzero the old buffer - * @cur_len: the current buffer length of @m_old. It is necessary for bzero. - * @new_len: the desired new length - * - * If @do_bzero_mem is false, this is like g_realloc(). - * Otherwise, this will allocate a new buffer of the desired size, copy over the - * old data, and bzero the old buffer before freeing it. As such, it also behaves - * similar to g_realloc(), with the overhead of nm_explicit_bzero() and using - * malloc/free instead of realloc(). - * - * Returns: the new allocated buffer. Think of it behaving like g_realloc(). - */ -static inline gpointer -nm_secret_mem_realloc(gpointer m_old, gboolean do_bzero_mem, gsize cur_len, gsize new_len) -{ - gpointer m_new; - - nm_assert(m_old || cur_len == 0); - - if (do_bzero_mem && G_LIKELY(cur_len > 0)) { - m_new = g_malloc(new_len); - if (G_LIKELY(new_len > 0)) - memcpy(m_new, m_old, NM_MIN(cur_len, new_len)); - nm_explicit_bzero(m_old, cur_len); - g_free(m_old); - } else - m_new = g_realloc(m_old, new_len); - - return m_new; -} - -/** - * nm_secret_mem_try_realloc: - * @m_old: the current buffer of length @cur_len. - * @do_bzero_mem: if %TRUE, bzero the old buffer - * @cur_len: the current buffer length of @m_old. It is necessary for bzero. - * @new_len: the desired new length - * - * If @do_bzero_mem is false, this is like g_try_realloc(). - * Otherwise, this will try to allocate a new buffer of the desired size, copy over the - * old data, and bzero the old buffer before freeing it. As such, it also behaves - * similar to g_try_realloc(), with the overhead of nm_explicit_bzero() and using - * malloc/free instead of realloc(). - * - * Returns: the new allocated buffer or NULL. Think of it behaving like g_try_realloc(). - */ -static inline gpointer -nm_secret_mem_try_realloc(gpointer m_old, gboolean do_bzero_mem, gsize cur_len, gsize new_len) -{ - gpointer m_new; - - nm_assert(m_old || cur_len == 0); - - if (do_bzero_mem && G_LIKELY(cur_len > 0)) { - if (G_UNLIKELY(new_len == 0)) - m_new = NULL; - else { - m_new = g_try_malloc(new_len); - if (!m_new) - return NULL; - memcpy(m_new, m_old, NM_MIN(cur_len, new_len)); - } - nm_explicit_bzero(m_old, cur_len); - g_free(m_old); - return m_new; - } - - return g_try_realloc(m_old, new_len); -} - -/** - * nm_secret_mem_try_realloc_take: - * @m_old: the current buffer of length @cur_len. - * @do_bzero_mem: if %TRUE, bzero the old buffer - * @cur_len: the current buffer length of @m_old. It is necessary for bzero. - * @new_len: the desired new length - * - * This works like nm_secret_mem_try_realloc(), which is not unlike g_try_realloc(). - * The difference is, if we fail to allocate a new buffer, then @m_old will be - * freed (and possibly cleared). This differs from plain realloc(), where the - * old buffer is unchanged if the operation fails. - * - * Returns: the new allocated buffer or NULL. Think of it behaving like g_try_realloc() - * but it will always free @m_old. - */ -static inline gpointer -nm_secret_mem_try_realloc_take(gpointer m_old, gboolean do_bzero_mem, gsize cur_len, gsize new_len) -{ - gpointer m_new; - - nm_assert(m_old || cur_len == 0); - - if (do_bzero_mem && G_LIKELY(cur_len > 0)) { - if (G_UNLIKELY(new_len == 0)) - m_new = NULL; - else { - m_new = g_try_malloc(new_len); - if (G_LIKELY(m_new)) - memcpy(m_new, m_old, NM_MIN(cur_len, new_len)); - } - nm_explicit_bzero(m_old, cur_len); - g_free(m_old); - return m_new; - } - - m_new = g_try_realloc(m_old, new_len); - if (G_UNLIKELY(!m_new && new_len > 0)) - g_free(m_old); - return m_new; -} - -/*****************************************************************************/ - -#endif /* __NM_SECRET_UTILS_H__ */ diff --git a/shared/nm-glib-aux/nm-shared-utils.c b/shared/nm-glib-aux/nm-shared-utils.c deleted file mode 100644 index e7cea23a..00000000 --- a/shared/nm-glib-aux/nm-shared-utils.c +++ /dev/null @@ -1,5789 +0,0 @@ -/* SPDX-License-Identifier: LGPL-2.1-or-later */ -/* - * Copyright (C) 2016 Red Hat, Inc. - */ - -#include "nm-glib-aux/nm-default-glib-i18n-lib.h" - -#include "nm-shared-utils.h" - -#include <pwd.h> -#include <arpa/inet.h> -#include <poll.h> -#include <fcntl.h> -#include <sys/syscall.h> -#include <glib-unix.h> -#include <net/if.h> -#include <net/ethernet.h> -#include <pthread.h> - -#include "c-list/src/c-list.h" -#include "nm-errno.h" -#include "nm-str-buf.h" - -G_STATIC_ASSERT(sizeof(NMEtherAddr) == 6); -G_STATIC_ASSERT(_nm_alignof(NMEtherAddr) == 1); - -G_STATIC_ASSERT(sizeof(NMUtilsNamedEntry) == sizeof(const char *)); -G_STATIC_ASSERT(G_STRUCT_OFFSET(NMUtilsNamedValue, value_ptr) == sizeof(const char *)); - -/*****************************************************************************/ - -const char _nm_hexchar_table_lower[16] = "0123456789abcdef"; -const char _nm_hexchar_table_upper[16] = "0123456789ABCDEF"; - -const void *const _NM_PTRARRAY_EMPTY[1] = {NULL}; - -/*****************************************************************************/ - -const NMIPAddr nm_ip_addr_zero = {}; - -/* this initializes a struct in_addr/in6_addr and allows for untrusted - * arguments (like unsuitable @addr_family or @src_len). It's almost safe - * in the sense that it verifies input arguments strictly. Also, it - * uses memcpy() to access @src, so alignment is not an issue. - * - * Only potential pitfalls: - * - * - it allows for @addr_family to be AF_UNSPEC. If that is the case (and the - * caller allows for that), the caller MUST provide @out_addr_family. - * - when setting @dst to an IPv4 address, the trailing bytes are not touched. - * Meaning, if @dst is an NMIPAddr union, only the first bytes will be set. - * If that matter to you, clear @dst before. */ -gboolean -nm_ip_addr_set_from_untrusted(int addr_family, - gpointer dst, - gconstpointer src, - gsize src_len, - int * out_addr_family) -{ - nm_assert(dst); - - switch (addr_family) { - case AF_UNSPEC: - if (!out_addr_family) { - /* when the callers allow undefined @addr_family, they must provide - * an @out_addr_family argument. */ - nm_assert_not_reached(); - return FALSE; - } - switch (src_len) { - case sizeof(struct in_addr): - addr_family = AF_INET; - break; - case sizeof(struct in6_addr): - addr_family = AF_INET6; - break; - default: - return FALSE; - } - break; - case AF_INET: - if (src_len != sizeof(struct in_addr)) - return FALSE; - break; - case AF_INET6: - if (src_len != sizeof(struct in6_addr)) - return FALSE; - break; - default: - /* when the callers allow undefined @addr_family, they must provide - * an @out_addr_family argument. */ - nm_assert(out_addr_family); - return FALSE; - } - - nm_assert(src); - - memcpy(dst, src, src_len); - NM_SET_OUT(out_addr_family, addr_family); - return TRUE; -} - -/*****************************************************************************/ - -G_STATIC_ASSERT(ETH_ALEN == sizeof(struct ether_addr)); -G_STATIC_ASSERT(ETH_ALEN == 6); - -/*****************************************************************************/ - -pid_t -nm_utils_gettid(void) -{ - return (pid_t) syscall(SYS_gettid); -} - -/* Used for asserting that this function is called on the main-thread. - * The main-thread is determined by remembering the thread-id - * of when the function was called the first time. - * - * When forking, the thread-id is again reset upon first call. */ -gboolean -_nm_assert_on_main_thread(void) -{ - G_LOCK_DEFINE_STATIC(lock); - static pid_t seen_tid; - static pid_t seen_pid; - pid_t tid; - pid_t pid; - gboolean success = FALSE; - - tid = nm_utils_gettid(); - nm_assert(tid != 0); - - G_LOCK(lock); - - if (G_LIKELY(tid == seen_tid)) { - /* we don't care about false positives (when the process forked, and the thread-id - * is accidentally re-used) . It's for assertions only. */ - success = TRUE; - } else { - pid = getpid(); - nm_assert(pid != 0); - - if (seen_tid == 0 || seen_pid != pid) { - /* either this is the first time we call the function, or the process - * forked. In both cases, remember the thread-id. */ - seen_tid = tid; - seen_pid = pid; - success = TRUE; - } - } - - G_UNLOCK(lock); - - return success; -} - -/*****************************************************************************/ - -void -nm_utils_strbuf_append_c(char **buf, gsize *len, char c) -{ - switch (*len) { - case 0: - return; - case 1: - (*buf)[0] = '\0'; - *len = 0; - (*buf)++; - return; - default: - (*buf)[0] = c; - (*buf)[1] = '\0'; - (*len)--; - (*buf)++; - return; - } -} - -void -nm_utils_strbuf_append_bin(char **buf, gsize *len, gconstpointer str, gsize str_len) -{ - switch (*len) { - case 0: - return; - case 1: - if (str_len == 0) { - (*buf)[0] = '\0'; - return; - } - (*buf)[0] = '\0'; - *len = 0; - (*buf)++; - return; - default: - if (str_len == 0) { - (*buf)[0] = '\0'; - return; - } - if (str_len >= *len) { - memcpy(*buf, str, *len - 1); - (*buf)[*len - 1] = '\0'; - *buf = &(*buf)[*len]; - *len = 0; - } else { - memcpy(*buf, str, str_len); - *buf = &(*buf)[str_len]; - (*buf)[0] = '\0'; - *len -= str_len; - } - return; - } -} - -void -nm_utils_strbuf_append_str(char **buf, gsize *len, const char *str) -{ - gsize src_len; - - switch (*len) { - case 0: - return; - case 1: - if (!str || !*str) { - (*buf)[0] = '\0'; - return; - } - (*buf)[0] = '\0'; - *len = 0; - (*buf)++; - return; - default: - if (!str || !*str) { - (*buf)[0] = '\0'; - return; - } - src_len = g_strlcpy(*buf, str, *len); - if (src_len >= *len) { - *buf = &(*buf)[*len]; - *len = 0; - } else { - *buf = &(*buf)[src_len]; - *len -= src_len; - } - return; - } -} - -void -nm_utils_strbuf_append(char **buf, gsize *len, const char *format, ...) -{ - char * p = *buf; - va_list args; - int retval; - - if (*len == 0) - return; - - va_start(args, format); - retval = g_vsnprintf(p, *len, format, args); - va_end(args); - - if ((gsize) retval >= *len) { - *buf = &p[*len]; - *len = 0; - } else { - *buf = &p[retval]; - *len -= retval; - } -} - -/** - * nm_utils_strbuf_seek_end: - * @buf: the input/output buffer - * @len: the input/output length of the buffer. - * - * Commonly, one uses nm_utils_strbuf_append*(), to incrementally - * append strings to the buffer. However, sometimes we need to use - * existing API to write to the buffer. - * After doing so, we want to adjust the buffer counter. - * Essentially, - * - * g_snprintf (buf, len, ...); - * nm_utils_strbuf_seek_end (&buf, &len); - * - * is almost the same as - * - * nm_utils_strbuf_append (&buf, &len, ...); - * - * The only difference is the behavior when the string got truncated: - * nm_utils_strbuf_append() will recognize that and set the remaining - * length to zero. - * - * In general, the behavior is: - * - * - if *len is zero, do nothing - * - if the buffer contains a NUL byte within the first *len characters, - * the buffer is pointed to the NUL byte and len is adjusted. In this - * case, the remaining *len is always >= 1. - * In particular, that is also the case if the NUL byte is at the very last - * position ((*buf)[*len -1]). That happens, when the previous operation - * either fit the string exactly into the buffer or the string was truncated - * by g_snprintf(). The difference cannot be determined. - * - if the buffer contains no NUL bytes within the first *len characters, - * write NUL at the last position, set *len to zero, and point *buf past - * the NUL byte. This would happen with - * - * strncpy (buf, long_str, len); - * nm_utils_strbuf_seek_end (&buf, &len). - * - * where strncpy() does truncate the string and not NUL terminate it. - * nm_utils_strbuf_seek_end() would then NUL terminate it. - */ -void -nm_utils_strbuf_seek_end(char **buf, gsize *len) -{ - gsize l; - char *end; - - nm_assert(len); - nm_assert(buf && *buf); - - if (*len <= 1) { - if (*len == 1 && (*buf)[0]) - goto truncate; - return; - } - - end = memchr(*buf, 0, *len); - if (end) { - l = end - *buf; - nm_assert(l < *len); - - *buf = end; - *len -= l; - return; - } - -truncate: - /* hm, no NUL character within len bytes. - * Just NUL terminate the array and consume them - * all. */ - *buf += *len; - (*buf)[-1] = '\0'; - *len = 0; - return; -} - -/*****************************************************************************/ - -GBytes * -nm_gbytes_get_empty(void) -{ - static GBytes *bytes = NULL; - GBytes * b; - -again: - b = g_atomic_pointer_get(&bytes); - if (G_UNLIKELY(!b)) { - b = g_bytes_new_static("", 0); - if (!g_atomic_pointer_compare_and_exchange(&bytes, NULL, b)) { - g_bytes_unref(b); - goto again; - } - } - return b; -} - -GBytes * -nm_g_bytes_new_from_str(const char *str) -{ - gsize l; - - if (!str) - return NULL; - - /* the returned array is guaranteed to have a trailing '\0' - * character *after* the length. */ - - l = strlen(str); - return g_bytes_new_take(nm_memdup(str, l + 1u), l); -} - -/** - * nm_utils_gbytes_equals: - * @bytes: (allow-none): a #GBytes array to compare. Note that - * %NULL is treated like an #GBytes array of length zero. - * @mem_data: the data pointer with @mem_len bytes - * @mem_len: the length of the data pointer - * - * Returns: %TRUE if @bytes contains the same data as @mem_data. As a - * special case, a %NULL @bytes is treated like an empty array. - */ -gboolean -nm_utils_gbytes_equal_mem(GBytes *bytes, gconstpointer mem_data, gsize mem_len) -{ - gconstpointer p; - gsize l; - - if (!bytes) { - /* as a special case, let %NULL GBytes compare identical - * to an empty array. */ - return (mem_len == 0); - } - - p = g_bytes_get_data(bytes, &l); - return l == mem_len - && (mem_len == 0 /* allow @mem_data to be %NULL */ - || memcmp(p, mem_data, mem_len) == 0); -} - -GVariant * -nm_utils_gbytes_to_variant_ay(GBytes *bytes) -{ - const guint8 *p; - gsize l; - - if (!bytes) { - /* for convenience, accept NULL to return an empty variant */ - return g_variant_new_array(G_VARIANT_TYPE_BYTE, NULL, 0); - } - - p = g_bytes_get_data(bytes, &l); - return g_variant_new_fixed_array(G_VARIANT_TYPE_BYTE, p, l, 1); -} - -/*****************************************************************************/ - -#define _variant_singleton_get(create_variant) \ - ({ \ - static GVariant *_singleton = NULL; \ - GVariant * _v; \ - \ -again: \ - _v = g_atomic_pointer_get(&_singleton); \ - if (G_UNLIKELY(!_v)) { \ - _v = (create_variant); \ - nm_assert(_v); \ - nm_assert(g_variant_is_floating(_v)); \ - g_variant_ref_sink(_v); \ - if (!g_atomic_pointer_compare_and_exchange(&_singleton, NULL, _v)) { \ - g_variant_unref(_v); \ - goto again; \ - } \ - } \ - _v; \ - }) - -GVariant * -nm_g_variant_singleton_u_0(void) -{ - return _variant_singleton_get(g_variant_new_uint32(0)); -} - -/*****************************************************************************/ - -GHashTable * -nm_utils_strdict_clone(GHashTable *src) -{ - GHashTable * dst; - GHashTableIter iter; - const char * key; - const char * val; - - if (!src) - return NULL; - - dst = g_hash_table_new_full(nm_str_hash, g_str_equal, g_free, g_free); - g_hash_table_iter_init(&iter, src); - while (g_hash_table_iter_next(&iter, (gpointer *) &key, (gpointer *) &val)) - g_hash_table_insert(dst, g_strdup(key), g_strdup(val)); - return dst; -} - -/* Convert a hash table with "char *" keys and values to an "a{ss}" GVariant. - * The keys will be sorted asciibetically. - * Returns a floating reference. - */ -GVariant * -nm_utils_strdict_to_variant_ass(GHashTable *strdict) -{ - gs_free NMUtilsNamedValue *values_free = NULL; - NMUtilsNamedValue values_prepared[20]; - const NMUtilsNamedValue * values; - GVariantBuilder builder; - guint i; - guint n; - - values = nm_utils_named_values_from_strdict(strdict, &n, values_prepared, &values_free); - - g_variant_builder_init(&builder, G_VARIANT_TYPE("a{ss}")); - for (i = 0; i < n; i++) { - g_variant_builder_add(&builder, "{ss}", values[i].name, values[i].value_str); - } - return g_variant_builder_end(&builder); -} - -/*****************************************************************************/ - -GVariant * -nm_utils_strdict_to_variant_asv(GHashTable *strdict) -{ - gs_free NMUtilsNamedValue *values_free = NULL; - NMUtilsNamedValue values_prepared[20]; - const NMUtilsNamedValue * values; - GVariantBuilder builder; - guint i; - guint n; - - values = nm_utils_named_values_from_strdict(strdict, &n, values_prepared, &values_free); - - g_variant_builder_init(&builder, G_VARIANT_TYPE("a{sv}")); - for (i = 0; i < n; i++) { - g_variant_builder_add(&builder, - "{sv}", - values[i].name, - g_variant_new_string(values[i].value_str)); - } - return g_variant_builder_end(&builder); -} - -/*****************************************************************************/ - -/** - * nm_strquote: - * @buf: the output buffer of where to write the quoted @str argument. - * @buf_len: the size of @buf. - * @str: (allow-none): the string to quote. - * - * Writes @str to @buf with quoting. The resulting buffer - * is always NUL terminated, unless @buf_len is zero. - * If @str is %NULL, it writes "(null)". - * - * If @str needs to be truncated, the closing quote is '^' instead - * of '"'. - * - * This is similar to nm_strquote_a(), which however uses alloca() - * to allocate a new buffer. Also, here @buf_len is the size of @buf, - * while nm_strquote_a() has the number of characters to print. The latter - * doesn't include the quoting. - * - * Returns: the input buffer with the quoted string. - */ -const char * -nm_strquote(char *buf, gsize buf_len, const char *str) -{ - const char *const buf0 = buf; - - if (!str) { - nm_utils_strbuf_append_str(&buf, &buf_len, "(null)"); - goto out; - } - - if (G_UNLIKELY(buf_len <= 2)) { - switch (buf_len) { - case 2: - *(buf++) = '^'; - /* fall-through */ - case 1: - *(buf++) = '\0'; - break; - } - goto out; - } - - *(buf++) = '"'; - buf_len--; - - nm_utils_strbuf_append_str(&buf, &buf_len, str); - - /* if the string was too long we indicate truncation with a - * '^' instead of a closing quote. */ - if (G_UNLIKELY(buf_len <= 1)) { - switch (buf_len) { - case 1: - buf[-1] = '^'; - break; - case 0: - buf[-2] = '^'; - break; - default: - nm_assert_not_reached(); - break; - } - } else { - nm_assert(buf_len >= 2); - *(buf++) = '"'; - *(buf++) = '\0'; - } - -out: - return buf0; -} - -/*****************************************************************************/ - -char _nm_utils_to_string_buffer[]; - -void -nm_utils_to_string_buffer_init(char **buf, gsize *len) -{ - if (!*buf) { - *buf = _nm_utils_to_string_buffer; - *len = sizeof(_nm_utils_to_string_buffer); - } -} - -gboolean -nm_utils_to_string_buffer_init_null(gconstpointer obj, char **buf, gsize *len) -{ - nm_utils_to_string_buffer_init(buf, len); - if (!obj) { - g_strlcpy(*buf, "(null)", *len); - return FALSE; - } - return TRUE; -} - -/*****************************************************************************/ - -const char * -nm_utils_flags2str(const NMUtilsFlags2StrDesc *descs, - gsize n_descs, - unsigned flags, - char * buf, - gsize len) -{ - gsize i; - char *p; - -#if NM_MORE_ASSERTS > 10 - nm_assert(descs); - nm_assert(n_descs > 0); - for (i = 0; i < n_descs; i++) { - gsize j; - - nm_assert(descs[i].name && descs[i].name[0]); - for (j = 0; j < i; j++) - nm_assert(descs[j].flag != descs[i].flag); - } -#endif - - nm_utils_to_string_buffer_init(&buf, &len); - - if (!len) - return buf; - - buf[0] = '\0'; - p = buf; - if (!flags) { - for (i = 0; i < n_descs; i++) { - if (!descs[i].flag) { - nm_utils_strbuf_append_str(&p, &len, descs[i].name); - break; - } - } - return buf; - } - - for (i = 0; flags && i < n_descs; i++) { - if (descs[i].flag && NM_FLAGS_ALL(flags, descs[i].flag)) { - flags &= ~descs[i].flag; - - if (buf[0] != '\0') - nm_utils_strbuf_append_c(&p, &len, ','); - nm_utils_strbuf_append_str(&p, &len, descs[i].name); - } - } - if (flags) { - if (buf[0] != '\0') - nm_utils_strbuf_append_c(&p, &len, ','); - nm_utils_strbuf_append(&p, &len, "0x%x", flags); - } - return buf; -}; - -/*****************************************************************************/ - -/** - * _nm_utils_ip4_prefix_to_netmask: - * @prefix: a CIDR prefix - * - * Returns: the netmask represented by the prefix, in network byte order - **/ -guint32 -_nm_utils_ip4_prefix_to_netmask(guint32 prefix) -{ - return prefix < 32 ? ~htonl(0xFFFFFFFFu >> prefix) : 0xFFFFFFFFu; -} - -gconstpointer -nm_utils_ipx_address_clear_host_address(int family, gpointer dst, gconstpointer src, guint8 plen) -{ - g_return_val_if_fail(dst, NULL); - - switch (family) { - case AF_INET: - g_return_val_if_fail(plen <= 32, NULL); - - if (!src) { - /* allow "self-assignment", by specifying %NULL as source. */ - src = dst; - } - - *((guint32 *) dst) = nm_utils_ip4_address_clear_host_address(*((guint32 *) src), plen); - break; - case AF_INET6: - nm_utils_ip6_address_clear_host_address(dst, src, plen); - break; - default: - g_return_val_if_reached(NULL); - } - return dst; -} - -/* nm_utils_ip4_address_clear_host_address: - * @addr: source ip6 address - * @plen: prefix length of network - * - * returns: the input address, with the host address set to 0. - */ -in_addr_t -nm_utils_ip4_address_clear_host_address(in_addr_t addr, guint8 plen) -{ - return addr & _nm_utils_ip4_prefix_to_netmask(plen); -} - -/* nm_utils_ip6_address_clear_host_address: - * @dst: destination output buffer, will contain the network part of the @src address - * @src: source ip6 address - * @plen: prefix length of network - * - * Note: this function is self assignment safe, to update @src inplace, set both - * @dst and @src to the same destination or set @src NULL. - */ -const struct in6_addr * -nm_utils_ip6_address_clear_host_address(struct in6_addr * dst, - const struct in6_addr *src, - guint8 plen) -{ - g_return_val_if_fail(plen <= 128, NULL); - g_return_val_if_fail(dst, NULL); - - if (!src) - src = dst; - - if (plen < 128) { - guint nbytes = plen / 8; - guint nbits = plen % 8; - - if (nbytes && dst != src) - memcpy(dst, src, nbytes); - if (nbits) { - dst->s6_addr[nbytes] = (src->s6_addr[nbytes] & (0xFF << (8 - nbits))); - nbytes++; - } - if (nbytes <= 15) - memset(&dst->s6_addr[nbytes], 0, 16 - nbytes); - } else if (src != dst) - *dst = *src; - - return dst; -} - -int -nm_utils_ip6_address_same_prefix_cmp(const struct in6_addr *addr_a, - const struct in6_addr *addr_b, - guint8 plen) -{ - int nbytes; - guint8 va, vb, m; - - if (plen >= 128) - NM_CMP_DIRECT_MEMCMP(addr_a, addr_b, sizeof(struct in6_addr)); - else { - nbytes = plen / 8; - if (nbytes) - NM_CMP_DIRECT_MEMCMP(addr_a, addr_b, nbytes); - - plen = plen % 8; - if (plen != 0) { - m = ~((1 << (8 - plen)) - 1); - va = ((((const guint8 *) addr_a))[nbytes]) & m; - vb = ((((const guint8 *) addr_b))[nbytes]) & m; - NM_CMP_DIRECT(va, vb); - } - } - return 0; -} - -/*****************************************************************************/ - -guint32 -_nm_utils_ip4_get_default_prefix0(in_addr_t ip) -{ - /* The function is originally from ipcalc.c of Red Hat's initscripts. */ - switch (ntohl(ip) >> 24) { - case 0 ... 127: - return 8; /* Class A */ - case 128 ... 191: - return 16; /* Class B */ - case 192 ... 223: - return 24; /* Class C */ - } - return 0; -} - -guint32 -_nm_utils_ip4_get_default_prefix(in_addr_t ip) -{ - return _nm_utils_ip4_get_default_prefix0(ip) ?: 24; -} - -gboolean -nm_utils_ip_is_site_local(int addr_family, const void *address) -{ - in_addr_t addr4; - - switch (addr_family) { - case AF_INET: - /* RFC1918 private addresses - * 10.0.0.0/8, 172.16.0.0/12, 192.168.0.0/16 */ - addr4 = ntohl(*((const in_addr_t *) address)); - return (addr4 & 0xff000000) == 0x0a000000 || (addr4 & 0xfff00000) == 0xac100000 - || (addr4 & 0xffff0000) == 0xc0a80000; - case AF_INET6: - return IN6_IS_ADDR_SITELOCAL(address); - default: - g_return_val_if_reached(FALSE); - } -} - -/*****************************************************************************/ - -static gboolean -_parse_legacy_addr4(const char *text, in_addr_t *out_addr, GError **error) -{ - gs_free char * s_free = NULL; - struct in_addr a1; - guint8 bin[sizeof(a1)]; - char * s; - int i; - - if (inet_aton(text, &a1) != 1) { - g_set_error_literal(error, - NM_UTILS_ERROR, - NM_UTILS_ERROR_INVALID_ARGUMENT, - "address invalid according to inet_aton()"); - return FALSE; - } - - /* OK, inet_aton() accepted the format. That's good, because we want - * to accept IPv4 addresses in octal format, like 255.255.000.000. - * That's what "legacy" means here. inet_pton() doesn't accept those. - * - * But inet_aton() also ignores trailing garbage and formats with fewer than - * 4 digits. That is just too crazy and we don't do that. Perform additional checks - * and reject some forms that inet_aton() accepted. - * - * Note that we still should (of course) accept everything that inet_pton() - * accepts. However this code never gets called if inet_pton() succeeds - * (see below, aside the assertion code). */ - - if (NM_STRCHAR_ANY(text, ch, (!(ch >= '0' && ch <= '9') && !NM_IN_SET(ch, '.', 'x')))) { - /* We only accepts '.', digits, and 'x' for "0x". */ - g_set_error_literal(error, - NM_UTILS_ERROR, - NM_UTILS_ERROR_INVALID_ARGUMENT, - "contains an invalid character"); - return FALSE; - } - - s = nm_memdup_maybe_a(300, text, strlen(text) + 1, &s_free); - - for (i = 0; i < G_N_ELEMENTS(bin); i++) { - char * current_token = s; - gint32 v; - - s = strchr(s, '.'); - if (s) { - s[0] = '\0'; - s++; - } - - if ((i == G_N_ELEMENTS(bin) - 1) != (s == NULL)) { - /* Exactly for the last digit, we expect to have no more following token. - * But this isn't the case. Abort. */ - g_set_error(error, - NM_UTILS_ERROR, - NM_UTILS_ERROR_INVALID_ARGUMENT, - "wrong number of tokens (index %d, token '%s')", - i, - s); - return FALSE; - } - - v = _nm_utils_ascii_str_to_int64(current_token, 0, 0, 0xFF, -1); - if (v == -1) { - int errsv = errno; - - /* we do accept octal and hex (even with leading "0x"). But something - * about this token is wrong. */ - g_set_error(error, - NM_UTILS_ERROR, - NM_UTILS_ERROR_INVALID_ARGUMENT, - "invalid token '%s': %s (%d)", - current_token, - nm_strerror_native(errsv), - errsv); - return FALSE; - } - - bin[i] = v; - } - - if (memcmp(bin, &a1, sizeof(bin)) != 0) { - /* our parsing did not agree with what inet_aton() gave. Something - * is wrong. Abort. */ - g_set_error( - error, - NM_UTILS_ERROR, - NM_UTILS_ERROR_INVALID_ARGUMENT, - "inet_aton() result 0x%08x differs from computed value 0x%02hhx%02hhx%02hhx%02hhx", - a1.s_addr, - bin[0], - bin[1], - bin[2], - bin[3]); - return FALSE; - } - - *out_addr = a1.s_addr; - return TRUE; -} - -gboolean -nm_utils_parse_inaddr_bin_full(int addr_family, - gboolean accept_legacy, - const char *text, - int * out_addr_family, - gpointer out_addr) -{ - NMIPAddr addrbin; - - g_return_val_if_fail(text, FALSE); - - if (addr_family == AF_UNSPEC) { - g_return_val_if_fail(!out_addr || out_addr_family, FALSE); - addr_family = strchr(text, ':') ? AF_INET6 : AF_INET; - } else - g_return_val_if_fail(NM_IN_SET(addr_family, AF_INET, AF_INET6), FALSE); - - if (inet_pton(addr_family, text, &addrbin) != 1) { - if (accept_legacy && addr_family == AF_INET - && _parse_legacy_addr4(text, &addrbin.addr4, NULL)) { - /* The address is in some legacy format which inet_aton() accepts, but not inet_pton(). - * Most likely octal digits (leading zeros). We accept the address. */ - } else - return FALSE; - } - -#if NM_MORE_ASSERTS > 10 - if (addr_family == AF_INET) { - gs_free_error GError *error = NULL; - in_addr_t a; - - /* The legacy parser should accept everything that inet_pton() accepts too. Meaning, - * it should strictly parse *more* formats. And of course, parse it the same way. */ - if (!_parse_legacy_addr4(text, &a, &error)) { - char buf[INET_ADDRSTRLEN]; - - g_error("unexpected assertion failure: could parse \"%s\" as %s, but not accepted by " - "legacy parser: %s", - text, - _nm_utils_inet4_ntop(addrbin.addr4, buf), - error->message); - } - nm_assert(addrbin.addr4 == a); - } -#endif - - NM_SET_OUT(out_addr_family, addr_family); - if (out_addr) - nm_ip_addr_set(addr_family, out_addr, &addrbin); - return TRUE; -} - -gboolean -nm_utils_parse_inaddr(int addr_family, const char *text, char **out_addr) -{ - NMIPAddr addrbin; - char addrstr_buf[MAX(INET_ADDRSTRLEN, INET6_ADDRSTRLEN)]; - - g_return_val_if_fail(text, FALSE); - - if (addr_family == AF_UNSPEC) - addr_family = strchr(text, ':') ? AF_INET6 : AF_INET; - else - g_return_val_if_fail(NM_IN_SET(addr_family, AF_INET, AF_INET6), FALSE); - - if (inet_pton(addr_family, text, &addrbin) != 1) - return FALSE; - - NM_SET_OUT(out_addr, - g_strdup(inet_ntop(addr_family, &addrbin, addrstr_buf, sizeof(addrstr_buf)))); - return TRUE; -} - -gboolean -nm_utils_parse_inaddr_prefix_bin(int addr_family, - const char *text, - int * out_addr_family, - gpointer out_addr, - int * out_prefix) -{ - gs_free char *addrstr_free = NULL; - int prefix = -1; - const char * slash; - const char * addrstr; - NMIPAddr addrbin; - - g_return_val_if_fail(text, FALSE); - - if (addr_family == AF_UNSPEC) { - g_return_val_if_fail(!out_addr || out_addr_family, FALSE); - addr_family = strchr(text, ':') ? AF_INET6 : AF_INET; - } else - g_return_val_if_fail(NM_IN_SET(addr_family, AF_INET, AF_INET6), FALSE); - - slash = strchr(text, '/'); - if (slash) - addrstr = nm_strndup_a(300, text, slash - text, &addrstr_free); - else - addrstr = text; - - if (inet_pton(addr_family, addrstr, &addrbin) != 1) - return FALSE; - - if (slash) { - /* For IPv4, `ip addr add` supports the prefix-length as a netmask. We don't - * do that. */ - prefix = - _nm_utils_ascii_str_to_int64(&slash[1], 10, 0, addr_family == AF_INET ? 32 : 128, -1); - if (prefix == -1) - return FALSE; - } - - NM_SET_OUT(out_addr_family, addr_family); - if (out_addr) - nm_ip_addr_set(addr_family, out_addr, &addrbin); - NM_SET_OUT(out_prefix, prefix); - return TRUE; -} - -gboolean -nm_utils_parse_inaddr_prefix(int addr_family, const char *text, char **out_addr, int *out_prefix) -{ - NMIPAddr addrbin; - char addrstr_buf[MAX(INET_ADDRSTRLEN, INET6_ADDRSTRLEN)]; - - if (!nm_utils_parse_inaddr_prefix_bin(addr_family, text, &addr_family, &addrbin, out_prefix)) - return FALSE; - NM_SET_OUT(out_addr, - g_strdup(inet_ntop(addr_family, &addrbin, addrstr_buf, sizeof(addrstr_buf)))); - return TRUE; -} - -gboolean -nm_utils_parse_next_line(const char **inout_ptr, - gsize * inout_len, - const char **out_line, - gsize * out_line_len) -{ - gboolean eol_is_carriage_return; - const char *line_start; - gsize line_len; - - nm_assert(inout_ptr); - nm_assert(inout_len); - nm_assert(*inout_len == 0 || *inout_ptr); - nm_assert(out_line); - nm_assert(out_line_len); - - if (G_UNLIKELY(*inout_len == 0)) - return FALSE; - - line_start = *inout_ptr; - - eol_is_carriage_return = FALSE; - for (line_len = 0;; line_len++) { - if (line_len >= *inout_len) { - /* if we consumed the entire line, we place the pointer at - * one character after the end. */ - *inout_ptr = &line_start[line_len]; - *inout_len = 0; - goto done; - } - switch (line_start[line_len]) { - case '\r': - eol_is_carriage_return = TRUE; - /* fall-through*/ - case '\0': - case '\n': - *inout_ptr = &line_start[line_len + 1]; - *inout_len = *inout_len - line_len - 1u; - if (eol_is_carriage_return && *inout_len > 0 && (*inout_ptr)[0] == '\n') { - /* also consume "\r\n" as one. */ - (*inout_len)--; - (*inout_ptr)++; - } - goto done; - } - } - -done: - *out_line = line_start; - *out_line_len = line_len; - return TRUE; -} - -/*****************************************************************************/ - -gboolean -nm_utils_ipaddr_is_valid(int addr_family, const char *str_addr) -{ - nm_assert(NM_IN_SET(addr_family, AF_UNSPEC, AF_INET, AF_INET6)); - - return str_addr && nm_utils_parse_inaddr_bin(addr_family, str_addr, NULL, NULL); -} - -gboolean -nm_utils_ipaddr_is_normalized(int addr_family, const char *str_addr) -{ - NMIPAddr addr; - char sbuf[NM_UTILS_INET_ADDRSTRLEN]; - - nm_assert(NM_IN_SET(addr_family, AF_UNSPEC, AF_INET, AF_INET6)); - - if (!str_addr) - return FALSE; - - if (!nm_utils_parse_inaddr_bin(addr_family, str_addr, &addr_family, &addr)) - return FALSE; - - nm_utils_inet_ntop(addr_family, &addr, sbuf); - return nm_streq(sbuf, str_addr); -} - -/*****************************************************************************/ - -/** - * nm_g_ascii_strtoll() - * @nptr: the string to parse - * @endptr: the pointer on the first invalid chars - * @base: the base. - * - * This wraps g_ascii_strtoll() and should in almost all cases behave identical - * to it. - * - * However, it seems there are situations where g_ascii_strtoll() might set - * errno to some unexpected value EAGAIN. Possibly this is related to creating - * the C locale during - * - * #ifdef USE_XLOCALE - * return strtoll_l (nptr, endptr, base, get_C_locale ()); - * - * This wrapper tries to workaround that condition. - */ -gint64 -nm_g_ascii_strtoll(const char *nptr, char **endptr, guint base) -{ - int try_count = 2; - gint64 v; - const int errsv_orig = errno; - int errsv; - - nm_assert(nptr); - nm_assert(base == 0u || (base >= 2u && base <= 36u)); - -again: - errno = 0; - v = g_ascii_strtoll(nptr, endptr, base); - errsv = errno; - - if (errsv == 0) { - if (errsv_orig != 0) - errno = errsv_orig; - return v; - } - - if (errsv == ERANGE && NM_IN_SET(v, G_MININT64, G_MAXINT64)) - return v; - - if (errsv == EINVAL && v == 0 && nptr && nptr[0] == '\0') - return v; - - if (try_count-- > 0) - goto again; - -#if NM_MORE_ASSERTS - g_critical("g_ascii_strtoll() for \"%s\" failed with errno=%d (%s) and v=%" G_GINT64_FORMAT, - nptr, - errsv, - nm_strerror_native(errsv), - v); -#endif - - return v; -} - -/* See nm_g_ascii_strtoll() */ -guint64 -nm_g_ascii_strtoull(const char *nptr, char **endptr, guint base) -{ - int try_count = 2; - guint64 v; - const int errsv_orig = errno; - int errsv; - - nm_assert(nptr); - nm_assert(base == 0u || (base >= 2u && base <= 36u)); - -again: - errno = 0; - v = g_ascii_strtoull(nptr, endptr, base); - errsv = errno; - - if (errsv == 0) { - if (errsv_orig != 0) - errno = errsv_orig; - return v; - } - - if (errsv == ERANGE && NM_IN_SET(v, G_MAXUINT64)) - return v; - - if (errsv == EINVAL && v == 0 && nptr && nptr[0] == '\0') - return v; - - if (try_count-- > 0) - goto again; - -#if NM_MORE_ASSERTS - g_critical("g_ascii_strtoull() for \"%s\" failed with errno=%d (%s) and v=%" G_GUINT64_FORMAT, - nptr, - errsv, - nm_strerror_native(errsv), - v); -#endif - - return v; -} - -/* see nm_g_ascii_strtoll(). */ -double -nm_g_ascii_strtod(const char *nptr, char **endptr) -{ - int try_count = 2; - double v; - int errsv; - - nm_assert(nptr); - -again: - v = g_ascii_strtod(nptr, endptr); - errsv = errno; - - if (errsv == 0) - return v; - - if (errsv == ERANGE) - return v; - - if (try_count-- > 0) - goto again; - -#if NM_MORE_ASSERTS - g_critical("g_ascii_strtod() for \"%s\" failed with errno=%d (%s) and v=%f", - nptr, - errsv, - nm_strerror_native(errsv), - v); -#endif - - /* Not really much else to do. Return the parsed value and leave errno set - * to the unexpected value. */ - return v; -} - -/* _nm_utils_ascii_str_to_int64: - * - * A wrapper for g_ascii_strtoll, that checks whether the whole string - * can be successfully converted to a number and is within a given - * range. On any error, @fallback will be returned and %errno will be set - * to a non-zero value. On success, %errno will be set to zero, check %errno - * for errors. Any trailing or leading (ascii) white space is ignored and the - * functions is locale independent. - * - * The function is guaranteed to return a value between @min and @max - * (inclusive) or @fallback. Also, the parsing is rather strict, it does - * not allow for any unrecognized characters, except leading and trailing - * white space. - **/ -gint64 -_nm_utils_ascii_str_to_int64(const char *str, guint base, gint64 min, gint64 max, gint64 fallback) -{ - gint64 v; - const char *s = NULL; - - str = nm_str_skip_leading_spaces(str); - if (!str || !str[0]) { - errno = EINVAL; - return fallback; - } - - errno = 0; - v = nm_g_ascii_strtoll(str, (char **) &s, base); - - if (errno != 0) - return fallback; - - if (s[0] != '\0') { - s = nm_str_skip_leading_spaces(s); - if (s[0] != '\0') { - errno = EINVAL; - return fallback; - } - } - if (v > max || v < min) { - errno = ERANGE; - return fallback; - } - - return v; -} - -guint64 -_nm_utils_ascii_str_to_uint64(const char *str, - guint base, - guint64 min, - guint64 max, - guint64 fallback) -{ - guint64 v; - const char *s = NULL; - - if (str) { - while (g_ascii_isspace(str[0])) - str++; - } - if (!str || !str[0]) { - errno = EINVAL; - return fallback; - } - - errno = 0; - v = nm_g_ascii_strtoull(str, (char **) &s, base); - - if (errno != 0) - return fallback; - if (s[0] != '\0') { - while (g_ascii_isspace(s[0])) - s++; - if (s[0] != '\0') { - errno = EINVAL; - return fallback; - } - } - if (v > max || v < min) { - errno = ERANGE; - return fallback; - } - - if (v != 0 && str[0] == '-') { - /* As documented, g_ascii_strtoull() accepts negative values, and returns their - * absolute value. We don't. */ - errno = ERANGE; - return fallback; - } - - return v; -} - -/*****************************************************************************/ - -int -nm_strcmp_with_data(gconstpointer a, gconstpointer b, gpointer user_data) -{ - const char *s1 = a; - const char *s2 = b; - - return strcmp(s1, s2); -} - -/* like nm_strcmp_p(), suitable for g_ptr_array_sort_with_data(). - * g_ptr_array_sort() just casts nm_strcmp_p() to a function of different - * signature. I guess, in glib there are knowledgeable people that ensure - * that this additional argument doesn't cause problems due to different ABI - * for every architecture that glib supports. - * For NetworkManager, we'd rather avoid such stunts. - **/ -int -nm_strcmp_p_with_data(gconstpointer a, gconstpointer b, gpointer user_data) -{ - const char *s1 = *((const char **) a); - const char *s2 = *((const char **) b); - - return strcmp(s1, s2); -} - -int -nm_strcmp0_p_with_data(gconstpointer a, gconstpointer b, gpointer user_data) -{ - const char *s1 = *((const char **) a); - const char *s2 = *((const char **) b); - - return nm_strcmp0(s1, s2); -} - -int -nm_strcmp_ascii_case_with_data(gconstpointer a, gconstpointer b, gpointer user_data) -{ - const char *s1 = a; - const char *s2 = b; - - return g_ascii_strcasecmp(s1, s2); -} - -int -nm_cmp_uint32_p_with_data(gconstpointer p_a, gconstpointer p_b, gpointer user_data) -{ - const guint32 a = *((const guint32 *) p_a); - const guint32 b = *((const guint32 *) p_b); - - if (a < b) - return -1; - if (a > b) - return 1; - return 0; -} - -int -nm_cmp_int2ptr_p_with_data(gconstpointer p_a, gconstpointer p_b, gpointer user_data) -{ - /* p_a and p_b are two pointers to a pointer, where the pointer is - * interpreted as a integer using GPOINTER_TO_INT(). - * - * That is the case of a hash-table that uses GINT_TO_POINTER() to - * convert integers as pointers, and the resulting keys-as-array - * array. */ - const int a = GPOINTER_TO_INT(*((gconstpointer *) p_a)); - const int b = GPOINTER_TO_INT(*((gconstpointer *) p_b)); - - if (a < b) - return -1; - if (a > b) - return 1; - return 0; -} - -/*****************************************************************************/ - -const char * -nm_utils_dbus_path_get_last_component(const char *dbus_path) -{ - if (dbus_path) { - dbus_path = strrchr(dbus_path, '/'); - if (dbus_path) - return dbus_path + 1; - } - return NULL; -} - -static gint64 -_dbus_path_component_as_num(const char *p) -{ - gint64 n; - - /* no odd stuff. No leading zeros, only a non-negative, decimal integer. - * - * Otherwise, there would be multiple ways to encode the same number "10" - * and "010". That is just confusing. A number has no leading zeros, - * if it has, it's not a number (as far as we are concerned here). */ - if (p[0] == '0') { - if (p[1] != '\0') - return -1; - else - return 0; - } - if (!(p[0] >= '1' && p[0] <= '9')) - return -1; - if (!NM_STRCHAR_ALL(&p[1], ch, (ch >= '0' && ch <= '9'))) - return -1; - n = _nm_utils_ascii_str_to_int64(p, 10, 0, G_MAXINT64, -1); - nm_assert(n == -1 || nm_streq0(p, nm_sprintf_bufa(100, "%" G_GINT64_FORMAT, n))); - return n; -} - -int -nm_utils_dbus_path_cmp(const char *dbus_path_a, const char *dbus_path_b) -{ - const char *l_a, *l_b; - gsize plen; - gint64 n_a, n_b; - - /* compare function for two D-Bus paths. It behaves like - * strcmp(), except, if both paths have the same prefix, - * and both end in a (positive) number, then the paths - * will be sorted by number. */ - - NM_CMP_SELF(dbus_path_a, dbus_path_b); - - /* if one or both paths have no slash (and no last component) - * compare the full paths directly. */ - if (!(l_a = nm_utils_dbus_path_get_last_component(dbus_path_a)) - || !(l_b = nm_utils_dbus_path_get_last_component(dbus_path_b))) - goto comp_full; - - /* check if both paths have the same prefix (up to the last-component). */ - plen = l_a - dbus_path_a; - if (plen != (l_b - dbus_path_b)) - goto comp_full; - NM_CMP_RETURN(strncmp(dbus_path_a, dbus_path_b, plen)); - - n_a = _dbus_path_component_as_num(l_a); - n_b = _dbus_path_component_as_num(l_b); - if (n_a == -1 && n_b == -1) - goto comp_l; - - /* both components must be convertible to a number. If they are not, - * (and only one of them is), then we must always strictly sort numeric parts - * after non-numeric components. If we wouldn't, we wouldn't have - * a total order. - * - * An example of a not total ordering would be: - * "8" < "010" (numeric) - * "0x" < "8" (lexical) - * "0x" > "010" (lexical) - * We avoid this, by forcing that a non-numeric entry "0x" always sorts - * before numeric entries. - * - * Additionally, _dbus_path_component_as_num() would also reject "010" as - * not a valid number. - */ - if (n_a == -1) - return -1; - if (n_b == -1) - return 1; - - NM_CMP_DIRECT(n_a, n_b); - nm_assert(nm_streq(dbus_path_a, dbus_path_b)); - return 0; - -comp_full: - NM_CMP_DIRECT_STRCMP0(dbus_path_a, dbus_path_b); - return 0; -comp_l: - NM_CMP_DIRECT_STRCMP0(l_a, l_b); - nm_assert(nm_streq(dbus_path_a, dbus_path_b)); - return 0; -} - -/*****************************************************************************/ - -typedef struct { - union { - guint8 table[256]; - guint64 _dummy_for_alignment; - }; -} CharLookupTable; - -static void -_char_lookup_table_set_one(CharLookupTable *lookup, char ch) -{ - lookup->table[(guint8) ch] = 1; -} - -static void -_char_lookup_table_set_all(CharLookupTable *lookup, const char *candidates) -{ - while (candidates[0] != '\0') - _char_lookup_table_set_one(lookup, (candidates++)[0]); -} - -static void -_char_lookup_table_init(CharLookupTable *lookup, const char *candidates) -{ - *lookup = (CharLookupTable){ - .table = {0}, - }; - if (candidates) - _char_lookup_table_set_all(lookup, candidates); -} - -static gboolean -_char_lookup_has(const CharLookupTable *lookup, char ch) -{ - /* with some optimization levels, the compiler thinks this code - * might access uninitialized @lookup. It is not -- when you look at the - * callers of this function. */ - NM_PRAGMA_WARNING_DISABLE("-Wmaybe-uninitialized") - nm_assert(lookup->table[(guint8) '\0'] == 0); - return lookup->table[(guint8) ch] != 0; - NM_PRAGMA_WARNING_REENABLE -} - -static gboolean -_char_lookup_has_all(const CharLookupTable *lookup, const char *candidates) -{ - if (candidates) { - while (candidates[0] != '\0') { - if (!_char_lookup_has(lookup, (candidates++)[0])) - return FALSE; - } - } - return TRUE; -} - -/** - * nm_utils_strsplit_set_full: - * @str: the string to split. - * @delimiters: the set of delimiters. - * @flags: additional flags for controlling the operation. - * - * This is a replacement for g_strsplit_set() which avoids copying - * each word once (the entire strv array), but instead copies it once - * and all words point into that internal copy. - * - * Note that for @str %NULL and "", this always returns %NULL too. That differs - * from g_strsplit_set(), which would return an empty strv array for "". - * This never returns an empty array. - * - * Returns: %NULL if @str is %NULL or "". - * If @str only contains delimiters and %NM_UTILS_STRSPLIT_SET_FLAGS_PRESERVE_EMPTY - * is not set, it also returns %NULL. - * Otherwise, a %NULL terminated strv array containing the split words. - * (delimiter characters are removed). - * The strings to which the result strv array points to are allocated - * after the returned result itself. Don't free the strings themself, - * but free everything with g_free(). - * It is however safe and allowed to modify the individual strings in-place, - * like "g_strstrip((char *) iter[0])". - */ -const char ** -nm_utils_strsplit_set_full(const char *str, const char *delimiters, NMUtilsStrsplitSetFlags flags) -{ - const char ** ptr; - gsize num_tokens; - gsize i_token; - gsize str_len_p1; - const char * c_str; - char * s; - CharLookupTable ch_lookup; - const gboolean f_escaped = NM_FLAGS_HAS(flags, NM_UTILS_STRSPLIT_SET_FLAGS_ESCAPED); - const gboolean f_allow_escaping = - f_escaped || NM_FLAGS_HAS(flags, NM_UTILS_STRSPLIT_SET_FLAGS_ALLOW_ESCAPING); - const gboolean f_preserve_empty = - NM_FLAGS_HAS(flags, NM_UTILS_STRSPLIT_SET_FLAGS_PRESERVE_EMPTY); - const gboolean f_strstrip = NM_FLAGS_HAS(flags, NM_UTILS_STRSPLIT_SET_FLAGS_STRSTRIP); - - if (!str) - return NULL; - - if (!delimiters) { - nm_assert_not_reached(); - delimiters = " \t\n"; - } - _char_lookup_table_init(&ch_lookup, delimiters); - - nm_assert(!f_allow_escaping || !_char_lookup_has(&ch_lookup, '\\')); - - if (!f_preserve_empty) { - while (_char_lookup_has(&ch_lookup, str[0])) - str++; - } - - if (!str[0]) { - /* We return %NULL here, also with NM_UTILS_STRSPLIT_SET_FLAGS_PRESERVE_EMPTY. - * That makes nm_utils_strsplit_set_full() with NM_UTILS_STRSPLIT_SET_FLAGS_PRESERVE_EMPTY - * different from g_strsplit_set(), which would in this case return an empty array. - * If you need to handle %NULL, and "" specially, then check the input string first. */ - return NULL; - } - -#define _char_is_escaped(str_start, str_cur) \ - ({ \ - const char *const _str_start = (str_start); \ - const char *const _str_cur = (str_cur); \ - const char * _str_i = (_str_cur); \ - \ - while (_str_i > _str_start && _str_i[-1] == '\\') \ - _str_i--; \ - (((_str_cur - _str_i) % 2) != 0); \ - }) - - num_tokens = 1; - c_str = str; - while (TRUE) { - while (G_LIKELY(!_char_lookup_has(&ch_lookup, c_str[0]))) { - if (c_str[0] == '\0') - goto done1; - c_str++; - } - - /* we assume escapings are not frequent. After we found - * this delimiter, check whether it was escaped by counting - * the backslashed before. */ - if (f_allow_escaping && _char_is_escaped(str, c_str)) { - /* the delimiter is escaped. This was not an accepted delimiter. */ - c_str++; - continue; - } - - c_str++; - - /* if we drop empty tokens, then we now skip over all consecutive delimiters. */ - if (!f_preserve_empty) { - while (_char_lookup_has(&ch_lookup, c_str[0])) - c_str++; - if (c_str[0] == '\0') - break; - } - - num_tokens++; - } - -done1: - - nm_assert(c_str[0] == '\0'); - - str_len_p1 = (c_str - str) + 1; - - nm_assert(str[str_len_p1 - 1] == '\0'); - - ptr = g_malloc((sizeof(const char *) * (num_tokens + 1)) + str_len_p1); - s = (char *) &ptr[num_tokens + 1]; - memcpy(s, str, str_len_p1); - - i_token = 0; - - while (TRUE) { - nm_assert(i_token < num_tokens); - ptr[i_token++] = s; - - if (s[0] == '\0') { - nm_assert(f_preserve_empty); - goto done2; - } - nm_assert(f_preserve_empty || !_char_lookup_has(&ch_lookup, s[0])); - - while (!_char_lookup_has(&ch_lookup, s[0])) { - if (G_UNLIKELY(s[0] == '\\' && f_allow_escaping)) { - s++; - if (s[0] == '\0') - goto done2; - s++; - } else if (s[0] == '\0') - goto done2; - else - s++; - } - - nm_assert(_char_lookup_has(&ch_lookup, s[0])); - s[0] = '\0'; - s++; - - if (!f_preserve_empty) { - while (_char_lookup_has(&ch_lookup, s[0])) - s++; - if (s[0] == '\0') - goto done2; - } - } - -done2: - nm_assert(i_token == num_tokens); - ptr[i_token] = NULL; - - if (f_strstrip) { - gsize i; - - i_token = 0; - for (i = 0; ptr[i]; i++) { - s = (char *) nm_str_skip_leading_spaces(ptr[i]); - if (s[0] != '\0') { - char *s_last; - - s_last = &s[strlen(s) - 1]; - while (s_last > s && g_ascii_isspace(s_last[0]) - && (!f_allow_escaping || !_char_is_escaped(s, s_last))) - (s_last--)[0] = '\0'; - } - - if (!f_preserve_empty && s[0] == '\0') - continue; - - ptr[i_token++] = s; - } - - if (i_token == 0) { - g_free(ptr); - return NULL; - } - ptr[i_token] = NULL; - } - - if (f_escaped) { - gsize i, j; - - /* We no longer need ch_lookup for its original purpose. Modify it, so it - * can detect the delimiters, '\\', and (optionally) whitespaces. */ - _char_lookup_table_set_one(&ch_lookup, '\\'); - if (f_strstrip) - _char_lookup_table_set_all(&ch_lookup, NM_ASCII_SPACES); - - for (i_token = 0; ptr[i_token]; i_token++) { - s = (char *) ptr[i_token]; - j = 0; - for (i = 0; s[i] != '\0';) { - if (s[i] == '\\' && _char_lookup_has(&ch_lookup, s[i + 1])) - i++; - s[j++] = s[i++]; - } - s[j] = '\0'; - } - } - - nm_assert(ptr && ptr[0]); - return ptr; -} - -/*****************************************************************************/ - -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) -{ - CharLookupTable ch_lookup; - CharLookupTable ch_lookup_as_needed; - gboolean has_ch_lookup_as_needed = FALSE; - char * ret; - gsize str_len; - gsize alloc_len; - gsize n_escapes; - gsize i, j; - gboolean escape_leading_space; - gboolean escape_trailing_space; - gboolean escape_backslash_as_needed; - - nm_assert( - !delimiters_as_needed - || (delimiters_as_needed[0] - && NM_FLAGS_HAS(flags, - NM_UTILS_ESCAPED_TOKENS_ESCAPE_FLAGS_ESCAPE_BACKSLASH_AS_NEEDED))); - - if (!str || str[0] == '\0') { - *out_to_free = NULL; - return str; - } - - str_len = strlen(str); - - _char_lookup_table_init(&ch_lookup, delimiters); - if (!delimiters || NM_FLAGS_HAS(flags, NM_UTILS_ESCAPED_TOKENS_ESCAPE_FLAGS_ESCAPE_SPACES)) { - flags &= ~(NM_UTILS_ESCAPED_TOKENS_ESCAPE_FLAGS_ESCAPE_LEADING_SPACE - | NM_UTILS_ESCAPED_TOKENS_ESCAPE_FLAGS_ESCAPE_TRAILING_SPACE); - _char_lookup_table_set_all(&ch_lookup, NM_ASCII_SPACES); - } - - if (NM_FLAGS_HAS(flags, NM_UTILS_ESCAPED_TOKENS_ESCAPE_FLAGS_ESCAPE_BACKSLASH_ALWAYS)) { - _char_lookup_table_set_one(&ch_lookup, '\\'); - escape_backslash_as_needed = FALSE; - } else if (_char_lookup_has(&ch_lookup, '\\')) - escape_backslash_as_needed = FALSE; - else { - escape_backslash_as_needed = - NM_FLAGS_HAS(flags, NM_UTILS_ESCAPED_TOKENS_ESCAPE_FLAGS_ESCAPE_BACKSLASH_AS_NEEDED); - if (escape_backslash_as_needed) { - if (NM_FLAGS_ANY(flags, - NM_UTILS_ESCAPED_TOKENS_ESCAPE_FLAGS_ESCAPE_LEADING_SPACE - | NM_UTILS_ESCAPED_TOKENS_ESCAPE_FLAGS_ESCAPE_TRAILING_SPACE) - && !_char_lookup_has_all(&ch_lookup, NM_ASCII_SPACES)) { - /* ESCAPE_LEADING_SPACE and ESCAPE_TRAILING_SPACE implies that we escape backslash - * before whitespaces. */ - if (!has_ch_lookup_as_needed) { - has_ch_lookup_as_needed = TRUE; - _char_lookup_table_init(&ch_lookup_as_needed, NULL); - } - _char_lookup_table_set_all(&ch_lookup_as_needed, NM_ASCII_SPACES); - } - if (delimiters_as_needed && !_char_lookup_has_all(&ch_lookup, delimiters_as_needed)) { - if (!has_ch_lookup_as_needed) { - has_ch_lookup_as_needed = TRUE; - _char_lookup_table_init(&ch_lookup_as_needed, NULL); - } - _char_lookup_table_set_all(&ch_lookup_as_needed, delimiters_as_needed); - } - } - } - - escape_leading_space = - NM_FLAGS_HAS(flags, NM_UTILS_ESCAPED_TOKENS_ESCAPE_FLAGS_ESCAPE_LEADING_SPACE) - && g_ascii_isspace(str[0]) && !_char_lookup_has(&ch_lookup, str[0]); - if (str_len == 1) - escape_trailing_space = FALSE; - else { - escape_trailing_space = - NM_FLAGS_HAS(flags, NM_UTILS_ESCAPED_TOKENS_ESCAPE_FLAGS_ESCAPE_TRAILING_SPACE) - && g_ascii_isspace(str[str_len - 1]) && !_char_lookup_has(&ch_lookup, str[str_len - 1]); - } - - n_escapes = 0; - for (i = 0; str[i] != '\0'; i++) { - if (_char_lookup_has(&ch_lookup, str[i])) - n_escapes++; - else if (str[i] == '\\' && escape_backslash_as_needed - && (_char_lookup_has(&ch_lookup, str[i + 1]) || NM_IN_SET(str[i + 1], '\0', '\\') - || (has_ch_lookup_as_needed - && _char_lookup_has(&ch_lookup_as_needed, str[i + 1])))) - n_escapes++; - } - if (escape_leading_space) - n_escapes++; - if (escape_trailing_space) - n_escapes++; - - if (n_escapes == 0u) { - *out_to_free = NULL; - return str; - } - - alloc_len = str_len + n_escapes + 1u; - ret = g_new(char, alloc_len); - - j = 0; - i = 0; - - if (escape_leading_space) { - ret[j++] = '\\'; - ret[j++] = str[i++]; - } - for (; str[i] != '\0'; i++) { - if (_char_lookup_has(&ch_lookup, str[i])) - ret[j++] = '\\'; - else if (str[i] == '\\' && escape_backslash_as_needed - && (_char_lookup_has(&ch_lookup, str[i + 1]) || NM_IN_SET(str[i + 1], '\0', '\\') - || (has_ch_lookup_as_needed - && _char_lookup_has(&ch_lookup_as_needed, str[i + 1])))) - ret[j++] = '\\'; - ret[j++] = str[i]; - } - if (escape_trailing_space) { - nm_assert(!_char_lookup_has(&ch_lookup, ret[j - 1]) && g_ascii_isspace(ret[j - 1])); - ret[j] = ret[j - 1]; - ret[j - 1] = '\\'; - j++; - } - - nm_assert(j == alloc_len - 1); - ret[j] = '\0'; - nm_assert(strlen(ret) == j); - - *out_to_free = ret; - return ret; -} - -/** - * nm_utils_escaped_tokens_options_split: - * @str: the src string. This string will be modified in-place. - * The output values will point into @str. - * @out_key: (allow-none): the returned output key. This will always be set to @str - * itself. @str will be modified to contain only the unescaped, truncated - * key name. - * @out_val: returns the parsed (and unescaped) value or %NULL, if @str contains - * no '=' delimiter. - * - * Honors backslash escaping to parse @str as "key=value" pairs. Optionally, if no '=' - * is present, @out_val will be returned as %NULL. Backslash can be used to escape - * '=', ',', '\\', and ascii whitespace. Other backslash sequences are taken verbatim. - * - * For keys, '=' obviously must be escaped. For values, that is optional because an - * unescaped '=' is just taken verbatim. For example, in a key, the sequence "\\=" - * must be escaped as "\\\\\\=". For the value, that works too, but "\\\\=" is also - * accepted. - * - * Unescaped Space around the key and value are also removed. Space in general must - * not be escaped, unless they are at the beginning or the end of key/value. - */ -void -nm_utils_escaped_tokens_options_split(char *str, const char **out_key, const char **out_val) -{ - const char *val = NULL; - gsize i; - gsize j; - gsize last_space_idx; - gboolean last_space_has; - - nm_assert(str); - - i = 0; - while (g_ascii_isspace(str[i])) - i++; - - j = 0; - last_space_idx = 0; - last_space_has = FALSE; - while (str[i] != '\0') { - if (g_ascii_isspace(str[i])) { - if (!last_space_has) { - last_space_has = TRUE; - last_space_idx = j; - } - } else { - if (str[i] == '\\') { - if (NM_IN_SET(str[i + 1u], '\\', ',', '=') || g_ascii_isspace(str[i + 1u])) - i++; - } else if (str[i] == '=') { - /* Encounter an unescaped '=' character. When we still parse the key, this - * is the separator we were waiting for. If we are parsing the value, - * we take the character verbatim. */ - if (!val) { - if (last_space_has) { - str[last_space_idx] = '\0'; - j = last_space_idx + 1; - last_space_has = FALSE; - } else - str[j++] = '\0'; - val = &str[j]; - i++; - while (g_ascii_isspace(str[i])) - i++; - continue; - } - } - last_space_has = FALSE; - } - str[j++] = str[i++]; - } - - if (last_space_has) - str[last_space_idx] = '\0'; - else - str[j] = '\0'; - - *out_key = str; - *out_val = val; -} - -/*****************************************************************************/ - -/** - * nm_utils_strsplit_quoted: - * @str: the string to split (e.g. from /proc/cmdline). - * - * This basically does that systemd's extract_first_word() does - * with the flags "EXTRACT_UNQUOTE | EXTRACT_RELAX". This is what - * systemd uses to parse /proc/cmdline, and we do too. - * - * Splits the string. We have nm_utils_strsplit_set() which - * supports a variety of flags. However, extending that already - * complex code to also support quotation and escaping is hard. - * Instead, add a naive implementation. - * - * Returns: (transfer full): the split string. - */ -char ** -nm_utils_strsplit_quoted(const char *str) -{ - gs_unref_ptrarray GPtrArray *arr = NULL; - gs_free char * str_out = NULL; - CharLookupTable ch_lookup; - - nm_assert(str); - - _char_lookup_table_init(&ch_lookup, NM_ASCII_WHITESPACES); - - for (;;) { - char quote; - gsize j; - - while (_char_lookup_has(&ch_lookup, str[0])) - str++; - - if (str[0] == '\0') - break; - - if (!str_out) - str_out = g_new(char, strlen(str) + 1); - - quote = '\0'; - j = 0; - for (;;) { - if (str[0] == '\\') { - str++; - if (str[0] == '\0') - break; - str_out[j++] = str[0]; - str++; - continue; - } - if (quote) { - if (str[0] == '\0') - break; - if (str[0] == quote) { - quote = '\0'; - str++; - continue; - } - str_out[j++] = str[0]; - str++; - continue; - } - if (str[0] == '\0') - break; - if (NM_IN_SET(str[0], '\'', '"')) { - quote = str[0]; - str++; - continue; - } - if (_char_lookup_has(&ch_lookup, str[0])) { - str++; - break; - } - str_out[j++] = str[0]; - str++; - } - - if (!arr) - arr = g_ptr_array_new(); - g_ptr_array_add(arr, g_strndup(str_out, j)); - } - - if (!arr) - return g_new0(char *, 1); - - g_ptr_array_add(arr, NULL); - - /* We want to return an optimally sized strv array, with no excess - * memory allocated. Hence, clone once more. */ - return nm_memdup(arr->pdata, sizeof(char *) * arr->len); -} - -/*****************************************************************************/ - -/** - * nm_utils_strv_find_first: - * @list: the strv list to search - * @len: the length of the list, or a negative value if @list is %NULL terminated. - * @needle: the value to search for. The search is done using strcmp(). - * - * Searches @list for @needle and returns the index of the first match (based - * on strcmp()). - * - * For convenience, @list has type 'char**' instead of 'const char **'. - * - * Returns: index of first occurrence or -1 if @needle is not found in @list. - */ -gssize -nm_utils_strv_find_first(char **list, gssize len, const char *needle) -{ - gssize i; - - if (len > 0) { - g_return_val_if_fail(list, -1); - - if (!needle) { - /* if we search a list with known length, %NULL is a valid @needle. */ - for (i = 0; i < len; i++) { - if (!list[i]) - return i; - } - } else { - for (i = 0; i < len; i++) { - if (list[i] && !strcmp(needle, list[i])) - return i; - } - } - } else if (len < 0) { - g_return_val_if_fail(needle, -1); - - if (list) { - for (i = 0; list[i]; i++) { - if (strcmp(needle, list[i]) == 0) - return i; - } - } - } - return -1; -} - -char ** -_nm_utils_strv_cleanup(char ** strv, - gboolean strip_whitespace, - gboolean skip_empty, - gboolean skip_repeated) -{ - guint i, j; - - if (!strv || !*strv) - return strv; - - if (strip_whitespace) { - /* we only modify the strings pointed to by @strv if @strip_whitespace is - * requested. Otherwise, the strings themselves are untouched. */ - for (i = 0; strv[i]; i++) - g_strstrip(strv[i]); - } - if (!skip_empty && !skip_repeated) - return strv; - j = 0; - for (i = 0; strv[i]; i++) { - if ((skip_empty && !*strv[i]) - || (skip_repeated && nm_utils_strv_find_first(strv, j, strv[i]) >= 0)) - g_free(strv[i]); - else - strv[j++] = strv[i]; - } - strv[j] = NULL; - return strv; -} - -/*****************************************************************************/ - -GPtrArray * -_nm_g_ptr_array_copy(GPtrArray * array, - GCopyFunc func, - gpointer user_data, - GDestroyNotify element_free_func) -{ - GPtrArray *new_array; - guint i; - - g_return_val_if_fail(array, NULL); - - new_array = g_ptr_array_new_full(array->len, element_free_func); - for (i = 0; i < array->len; i++) { - g_ptr_array_add(new_array, func ? func(array->pdata[i], user_data) : array->pdata[i]); - } - return new_array; -} - -/*****************************************************************************/ - -int -_nm_utils_ascii_str_to_bool(const char *str, int default_value) -{ - gs_free char *str_free = NULL; - - if (!str) - return default_value; - - str = nm_strstrip_avoid_copy_a(300, str, &str_free); - if (str[0] == '\0') - return default_value; - - if (!g_ascii_strcasecmp(str, "true") || !g_ascii_strcasecmp(str, "yes") - || !g_ascii_strcasecmp(str, "on") || !g_ascii_strcasecmp(str, "1")) - return TRUE; - - if (!g_ascii_strcasecmp(str, "false") || !g_ascii_strcasecmp(str, "no") - || !g_ascii_strcasecmp(str, "off") || !g_ascii_strcasecmp(str, "0")) - return FALSE; - - return default_value; -} - -/*****************************************************************************/ - -NM_CACHED_QUARK_FCN("nm-manager-error-quark", nm_manager_error_quark); - -NM_CACHED_QUARK_FCN("nm-utils-error-quark", nm_utils_error_quark); - -void -nm_utils_error_set_cancelled(GError **error, gboolean is_disposing, const char *instance_name) -{ - if (is_disposing) { - g_set_error(error, - NM_UTILS_ERROR, - NM_UTILS_ERROR_CANCELLED_DISPOSING, - "Disposing %s instance", - instance_name && *instance_name ? instance_name : "source"); - } else { - g_set_error_literal(error, G_IO_ERROR, G_IO_ERROR_CANCELLED, "Request cancelled"); - } -} - -gboolean -nm_utils_error_is_cancelled_or_disposing(GError *error) -{ - if (error) { - if (error->domain == G_IO_ERROR) - return NM_IN_SET(error->code, G_IO_ERROR_CANCELLED); - if (error->domain == NM_UTILS_ERROR) - return NM_IN_SET(error->code, NM_UTILS_ERROR_CANCELLED_DISPOSING); - } - return FALSE; -} - -gboolean -nm_utils_error_is_notfound(GError *error) -{ - if (error) { - if (error->domain == G_IO_ERROR) - return NM_IN_SET(error->code, G_IO_ERROR_NOT_FOUND); - if (error->domain == G_FILE_ERROR) - return NM_IN_SET(error->code, G_FILE_ERROR_NOENT); - } - return FALSE; -} - -/*****************************************************************************/ - -/** - * nm_g_object_set_property: - * @object: the target object - * @property_name: the property name - * @value: the #GValue to set - * @error: (allow-none): optional error argument - * - * A reimplementation of g_object_set_property(), but instead - * returning an error instead of logging a warning. All g_object_set*() - * versions in glib require you to not pass invalid types or they will - * log a g_warning() -- without reporting an error. We don't want that, - * so we need to hack error checking around it. - * - * Returns: whether the value was successfully set. - */ -gboolean -nm_g_object_set_property(GObject * object, - const char * property_name, - const GValue *value, - GError ** error) -{ - GParamSpec * pspec; - nm_auto_unset_gvalue GValue tmp_value = G_VALUE_INIT; - GObjectClass * klass; - - g_return_val_if_fail(G_IS_OBJECT(object), FALSE); - g_return_val_if_fail(property_name != NULL, FALSE); - g_return_val_if_fail(G_IS_VALUE(value), FALSE); - g_return_val_if_fail(!error || !*error, FALSE); - - /* g_object_class_find_property() does g_param_spec_get_redirect_target(), - * where we differ from a plain g_object_set_property(). */ - pspec = g_object_class_find_property(G_OBJECT_GET_CLASS(object), property_name); - - if (!pspec) { - g_set_error(error, - NM_UTILS_ERROR, - NM_UTILS_ERROR_UNKNOWN, - _("object class '%s' has no property named '%s'"), - G_OBJECT_TYPE_NAME(object), - property_name); - return FALSE; - } - if (!(pspec->flags & G_PARAM_WRITABLE)) { - g_set_error(error, - NM_UTILS_ERROR, - NM_UTILS_ERROR_UNKNOWN, - _("property '%s' of object class '%s' is not writable"), - pspec->name, - G_OBJECT_TYPE_NAME(object)); - return FALSE; - } - if ((pspec->flags & G_PARAM_CONSTRUCT_ONLY)) { - g_set_error(error, - NM_UTILS_ERROR, - NM_UTILS_ERROR_UNKNOWN, - _("construct property \"%s\" for object '%s' can't be set after construction"), - pspec->name, - G_OBJECT_TYPE_NAME(object)); - return FALSE; - } - - klass = g_type_class_peek(pspec->owner_type); - if (klass == NULL) { - g_set_error(error, - NM_UTILS_ERROR, - NM_UTILS_ERROR_UNKNOWN, - _("'%s::%s' is not a valid property name; '%s' is not a GObject subtype"), - g_type_name(pspec->owner_type), - pspec->name, - g_type_name(pspec->owner_type)); - return FALSE; - } - - /* provide a copy to work from, convert (if necessary) and validate */ - g_value_init(&tmp_value, pspec->value_type); - if (!g_value_transform(value, &tmp_value)) { - g_set_error(error, - NM_UTILS_ERROR, - NM_UTILS_ERROR_UNKNOWN, - _("unable to set property '%s' of type '%s' from value of type '%s'"), - pspec->name, - g_type_name(pspec->value_type), - G_VALUE_TYPE_NAME(value)); - return FALSE; - } - if (g_param_value_validate(pspec, &tmp_value) && !(pspec->flags & G_PARAM_LAX_VALIDATION)) { - gs_free char *contents = g_strdup_value_contents(value); - - g_set_error(error, - NM_UTILS_ERROR, - NM_UTILS_ERROR_UNKNOWN, - _("value \"%s\" of type '%s' is invalid or out of range for property '%s' of " - "type '%s'"), - contents, - G_VALUE_TYPE_NAME(value), - pspec->name, - g_type_name(pspec->value_type)); - return FALSE; - } - - g_object_set_property(object, property_name, &tmp_value); - return TRUE; -} - -#define _set_property(object, property_name, gtype, gtype_set, value, error) \ - G_STMT_START \ - { \ - nm_auto_unset_gvalue GValue gvalue = {0}; \ - \ - g_value_init(&gvalue, gtype); \ - gtype_set(&gvalue, (value)); \ - return nm_g_object_set_property((object), (property_name), &gvalue, (error)); \ - } \ - G_STMT_END - -gboolean -nm_g_object_set_property_string(GObject * object, - const char *property_name, - const char *value, - GError ** error) -{ - _set_property(object, property_name, G_TYPE_STRING, g_value_set_string, value, error); -} - -gboolean -nm_g_object_set_property_string_static(GObject * object, - const char *property_name, - const char *value, - GError ** error) -{ - _set_property(object, property_name, G_TYPE_STRING, g_value_set_static_string, value, error); -} - -gboolean -nm_g_object_set_property_string_take(GObject * object, - const char *property_name, - char * value, - GError ** error) -{ - _set_property(object, property_name, G_TYPE_STRING, g_value_take_string, value, error); -} - -gboolean -nm_g_object_set_property_boolean(GObject * object, - const char *property_name, - gboolean value, - GError ** error) -{ - _set_property(object, property_name, G_TYPE_BOOLEAN, g_value_set_boolean, !!value, error); -} - -gboolean -nm_g_object_set_property_char(GObject * object, - const char *property_name, - gint8 value, - GError ** error) -{ - /* glib says about G_TYPE_CHAR: - * - * The type designated by G_TYPE_CHAR is unconditionally an 8-bit signed integer. - * - * This is always a (signed!) char. */ - _set_property(object, property_name, G_TYPE_CHAR, g_value_set_schar, value, error); -} - -gboolean -nm_g_object_set_property_uchar(GObject * object, - const char *property_name, - guint8 value, - GError ** error) -{ - _set_property(object, property_name, G_TYPE_UCHAR, g_value_set_uchar, value, error); -} - -gboolean -nm_g_object_set_property_int(GObject *object, const char *property_name, int value, GError **error) -{ - _set_property(object, property_name, G_TYPE_INT, g_value_set_int, value, error); -} - -gboolean -nm_g_object_set_property_int64(GObject * object, - const char *property_name, - gint64 value, - GError ** error) -{ - _set_property(object, property_name, G_TYPE_INT64, g_value_set_int64, value, error); -} - -gboolean -nm_g_object_set_property_uint(GObject * object, - const char *property_name, - guint value, - GError ** error) -{ - _set_property(object, property_name, G_TYPE_UINT, g_value_set_uint, value, error); -} - -gboolean -nm_g_object_set_property_uint64(GObject * object, - const char *property_name, - guint64 value, - GError ** error) -{ - _set_property(object, property_name, G_TYPE_UINT64, g_value_set_uint64, value, error); -} - -gboolean -nm_g_object_set_property_flags(GObject * object, - const char *property_name, - GType gtype, - guint value, - GError ** error) -{ - nm_assert(({ - nm_auto_unref_gtypeclass GTypeClass *gtypeclass = g_type_class_ref(gtype); - G_IS_FLAGS_CLASS(gtypeclass); - })); - _set_property(object, property_name, gtype, g_value_set_flags, value, error); -} - -gboolean -nm_g_object_set_property_enum(GObject * object, - const char *property_name, - GType gtype, - int value, - GError ** error) -{ - nm_assert(({ - nm_auto_unref_gtypeclass GTypeClass *gtypeclass = g_type_class_ref(gtype); - G_IS_ENUM_CLASS(gtypeclass); - })); - _set_property(object, property_name, gtype, g_value_set_enum, value, error); -} - -GParamSpec * -nm_g_object_class_find_property_from_gtype(GType gtype, const char *property_name) -{ - nm_auto_unref_gtypeclass GObjectClass *gclass = NULL; - - gclass = g_type_class_ref(gtype); - return g_object_class_find_property(gclass, property_name); -} - -/*****************************************************************************/ - -/** - * nm_g_type_find_implementing_class_for_property: - * @gtype: the GObject type which has a property @pname - * @pname: the name of the property to look up - * - * This is only a helper function for printf debugging. It's not - * used in actual code. Hence, the function just asserts that - * @pname and @gtype arguments are suitable. It cannot fail. - * - * Returns: the most ancestor type of @gtype, that - * implements the property @pname. It means, it - * searches the type hierarchy to find the type - * that added @pname. - */ -GType -nm_g_type_find_implementing_class_for_property(GType gtype, const char *pname) -{ - nm_auto_unref_gtypeclass GObjectClass *klass = NULL; - GParamSpec * pspec; - - g_return_val_if_fail(pname, G_TYPE_INVALID); - - klass = g_type_class_ref(gtype); - g_return_val_if_fail(G_IS_OBJECT_CLASS(klass), G_TYPE_INVALID); - - pspec = g_object_class_find_property(klass, pname); - g_return_val_if_fail(pspec, G_TYPE_INVALID); - - gtype = G_TYPE_FROM_CLASS(klass); - - while (TRUE) { - nm_auto_unref_gtypeclass GObjectClass *k = NULL; - - k = g_type_class_ref(g_type_parent(gtype)); - - g_return_val_if_fail(G_IS_OBJECT_CLASS(k), G_TYPE_INVALID); - - if (g_object_class_find_property(k, pname) != pspec) - return gtype; - - gtype = G_TYPE_FROM_CLASS(k); - } -} - -/*****************************************************************************/ - -static void -_str_buf_append_c_escape_octal(NMStrBuf *strbuf, char ch) -{ - nm_str_buf_append_c4(strbuf, - '\\', - '0' + ((char) ((((guchar) ch) >> 6) & 07)), - '0' + ((char) ((((guchar) ch) >> 3) & 07)), - '0' + ((char) ((((guchar) ch)) & 07))); -} - -/** - * nm_utils_buf_utf8safe_unescape: - * @str: (allow-none): the string to unescape. The string itself is a NUL terminated - * ASCII string, that can have C-style backslash escape sequences (which - * are to be unescaped). Non-ASCII characters (e.g. UTF-8) are taken verbatim, so - * it doesn't care that this string is UTF-8. However, usually this is a UTF-8 encoded - * string. - * @flags: flags for unescaping. The following flags are supported. - * %NM_UTILS_STR_UTF8_SAFE_UNESCAPE_STRIP_SPACES performs a g_strstrip() on the input string, - * but preserving escaped spaces. For example, "a\\t " gives "a\t" (that is, the escaped space does - * not get stripped). Likewise, the invalid escape sequence "a\\ " results in "a " (stripping - * the unescaped space, but preserving the escaped one). - * @out_len: (out): the length of the parsed string. - * @to_free: (out): if @str requires unescaping, the function will clone the string. In - * that case, the allocated buffer will be returned here. - * - * See C-style escapes at https://en.wikipedia.org/wiki/Escape_sequences_in_C#Table_of_escape_sequences. - * Note that hex escapes ("\\xhh") and unicode escapes ("\\uhhhh", "\\Uhhhhhhhh") are not supported. - * - * Also, this function is very similar to g_strcompress() but without issuing g_warning() - * assertions and proper handling of "\\000" escape sequences. - * - * Invalid escape sequences (or non-UTF-8 input) are gracefully accepted. For example "\\ " - * is an invalid escape sequence, in this case the backslash is removed and " " gets returned. - * - * The function never leaks secrets in memory. - * - * Returns: the unescaped buffer of length @out_len. If @str is %NULL, this returns %NULL - * and sets @out_len to 0. Otherwise, a non-%NULL binary buffer is returned with - * @out_len bytes. Note that the binary buffer is guaranteed to be NUL terminated. That - * is @result[@out_len] is NUL. - * Note that the result is binary, and may have embedded NUL characters and non-UTF-8. - * If the function can avoid cloning the input string, it will return a pointer inside - * the input @str. For example, if there is no backslash, no cloning is necessary. In that - * case, @to_free will be %NULL. Otherwise, @to_free is set to a newly allocated buffer - * containing the unescaped string and returned. - */ -gconstpointer -nm_utils_buf_utf8safe_unescape(const char * str, - NMUtilsStrUtf8SafeFlags flags, - gsize * out_len, - gpointer * to_free) -{ - gboolean strip_spaces = NM_FLAGS_HAS(flags, NM_UTILS_STR_UTF8_SAFE_UNESCAPE_STRIP_SPACES); - NMStrBuf strbuf; - const char *s; - gsize len; - - g_return_val_if_fail(to_free, NULL); - g_return_val_if_fail(out_len, NULL); - - if (!str) { - *out_len = 0; - *to_free = NULL; - return NULL; - } - - if (strip_spaces) - str = nm_str_skip_leading_spaces(str); - - len = strlen(str); - - s = memchr(str, '\\', len); - if (!s) { - if (strip_spaces && len > 0 && g_ascii_isspace(str[len - 1])) { - len--; - while (len > 0 && g_ascii_isspace(str[len - 1])) - len--; - *out_len = len; - return (*to_free = g_strndup(str, len)); - } - *out_len = len; - *to_free = NULL; - return str; - } - - nm_str_buf_init(&strbuf, len + 1u, FALSE); - - nm_str_buf_append_len(&strbuf, str, s - str); - str = s; - - for (;;) { - char ch; - guint v; - - nm_assert(str[0] == '\\'); - - ch = (++str)[0]; - - if (ch == '\0') { - /* error. Trailing '\\' */ - break; - } - - if (ch >= '0' && ch <= '9') { - v = ch - '0'; - ch = (++str)[0]; - if (ch >= '0' && ch <= '7') { - v = v * 8 + (ch - '0'); - ch = (++str)[0]; - if (ch >= '0' && ch <= '7') { - /* technically, escape sequences larger than \3FF are out of range - * and invalid. We don't check for that, and do the same as - * g_strcompress(): silently clip the value with & 0xFF. */ - v = v * 8 + (ch - '0'); - ++str; - } - } - ch = v; - } else { - switch (ch) { - case 'b': - ch = '\b'; - break; - case 'f': - ch = '\f'; - break; - case 'n': - ch = '\n'; - break; - case 'r': - ch = '\r'; - break; - case 't': - ch = '\t'; - break; - case 'v': - ch = '\v'; - break; - default: - /* Here we handle "\\\\", but all other unexpected escape sequences are really a bug. - * Take them literally, after removing the escape character */ - break; - } - str++; - } - - nm_str_buf_append_c(&strbuf, ch); - - s = strchr(str, '\\'); - if (!s) { - gsize l = strlen(str); - - if (strip_spaces) { - while (l > 0 && g_ascii_isspace(str[l - 1])) - l--; - } - nm_str_buf_append_len(&strbuf, str, l); - break; - } - - nm_str_buf_append_len(&strbuf, str, s - str); - str = s; - } - - /* assert that no reallocation was necessary. For one, unescaping should - * never result in a longer string than the input. Also, when unescaping - * secrets, we want to ensure that we don't leak secrets in memory. */ - nm_assert(strbuf.allocated == len + 1u); - - return (*to_free = nm_str_buf_finalize(&strbuf, out_len)); -} - -/** - * nm_utils_buf_utf8safe_escape: - * @buf: byte array, possibly in utf-8 encoding, may have NUL characters. - * @buflen: the length of @buf in bytes, or -1 if @buf is a NUL terminated - * string. - * @flags: #NMUtilsStrUtf8SafeFlags flags - * @to_free: (out): return the pointer location of the string - * if a copying was necessary. - * - * Based on the assumption, that @buf contains UTF-8 encoded bytes, - * this will return valid UTF-8 sequence, and invalid sequences - * will be escaped with backslash (C escaping, like g_strescape()). - * This is sanitize non UTF-8 characters. The result is valid - * UTF-8. - * - * The operation can be reverted with nm_utils_buf_utf8safe_unescape(). - * Note that if, and only if @buf contains no NUL bytes, the operation - * can also be reverted with g_strcompress(). - * - * Depending on @flags, valid UTF-8 characters are not escaped at all - * (except the escape character '\\'). This is the difference to g_strescape(), - * which escapes all non-ASCII characters. This allows to pass on - * valid UTF-8 characters as-is and can be directly shown to the user - * as UTF-8 -- with exception of the backslash escape character, - * invalid UTF-8 sequences, and other (depending on @flags). - * - * Returns: the escaped input buffer, as valid UTF-8. If no escaping - * is necessary, it returns the input @buf. Otherwise, an allocated - * string @to_free is returned which must be freed by the caller - * with g_free. The escaping can be reverted by g_strcompress(). - **/ -const char * -nm_utils_buf_utf8safe_escape(gconstpointer buf, - gssize buflen, - NMUtilsStrUtf8SafeFlags flags, - char ** to_free) -{ - const char *const str = buf; - const char * p = NULL; - const char * s; - gboolean nul_terminated = FALSE; - NMStrBuf strbuf; - - g_return_val_if_fail(to_free, NULL); - - *to_free = NULL; - - if (buflen == 0) - return NULL; - - if (buflen < 0) { - if (!str) - return NULL; - buflen = strlen(str); - if (buflen == 0) - return str; - nul_terminated = TRUE; - } - - if (g_utf8_validate(str, buflen, &p) && nul_terminated) { - /* note that g_utf8_validate() does not allow NUL character inside @str. Good. - * We can treat @str like a NUL terminated string. */ - if (!NM_STRCHAR_ANY( - str, - ch, - (ch == '\\' - || (NM_FLAGS_HAS(flags, NM_UTILS_STR_UTF8_SAFE_FLAG_ESCAPE_CTRL) && ch < ' ') - || (NM_FLAGS_HAS(flags, NM_UTILS_STR_UTF8_SAFE_FLAG_ESCAPE_NON_ASCII) - && ((guchar) ch) >= 127)))) - return str; - } - - nm_str_buf_init(&strbuf, buflen + 5, NM_FLAGS_HAS(flags, NM_UTILS_STR_UTF8_SAFE_FLAG_SECRET)); - - s = str; - do { - buflen -= p - s; - nm_assert(buflen >= 0); - - for (; s < p; s++) { - char ch = s[0]; - - nm_assert(ch); - if (ch == '\\') - nm_str_buf_append_c2(&strbuf, '\\', '\\'); - else if ((NM_FLAGS_HAS(flags, NM_UTILS_STR_UTF8_SAFE_FLAG_ESCAPE_CTRL) && ch < ' ') - || (NM_FLAGS_HAS(flags, NM_UTILS_STR_UTF8_SAFE_FLAG_ESCAPE_NON_ASCII) - && ((guchar) ch) >= 127)) - _str_buf_append_c_escape_octal(&strbuf, ch); - else - nm_str_buf_append_c(&strbuf, ch); - } - - if (buflen <= 0) - break; - - _str_buf_append_c_escape_octal(&strbuf, p[0]); - - buflen--; - if (buflen == 0) - break; - - s = &p[1]; - (void) g_utf8_validate(s, buflen, &p); - } while (TRUE); - - return (*to_free = nm_str_buf_finalize(&strbuf, NULL)); -} - -const char * -nm_utils_buf_utf8safe_escape_bytes(GBytes *bytes, NMUtilsStrUtf8SafeFlags flags, char **to_free) -{ - gconstpointer p; - gsize l; - - if (bytes) - p = g_bytes_get_data(bytes, &l); - else { - p = NULL; - l = 0; - } - - return nm_utils_buf_utf8safe_escape(p, l, flags, to_free); -} - -char * -nm_utils_buf_utf8safe_escape_cp(gconstpointer buf, gssize buflen, NMUtilsStrUtf8SafeFlags flags) -{ - const char *s_const; - char * s; - - s_const = nm_utils_buf_utf8safe_escape(buf, buflen, flags, &s); - nm_assert(!s || s == s_const); - return s ?: g_strdup(s_const); -} - -/*****************************************************************************/ - -const char * -nm_utils_str_utf8safe_unescape(const char *str, NMUtilsStrUtf8SafeFlags flags, char **to_free) -{ - const char *res; - gsize len; - - g_return_val_if_fail(to_free, NULL); - - res = nm_utils_buf_utf8safe_unescape(str, flags, &len, (gpointer *) to_free); - - nm_assert((!res && len == 0) || (strlen(res) <= len)); - - return res; -} - -/** - * nm_utils_str_utf8safe_escape: - * @str: NUL terminated input string, possibly in utf-8 encoding - * @flags: #NMUtilsStrUtf8SafeFlags flags - * @to_free: (out): return the pointer location of the string - * if a copying was necessary. - * - * Returns the possible non-UTF-8 NUL terminated string @str - * and uses backslash escaping (C escaping, like g_strescape()) - * to sanitize non UTF-8 characters. The result is valid - * UTF-8. - * - * The operation can be reverted with g_strcompress() or - * nm_utils_str_utf8safe_unescape(). - * - * Depending on @flags, valid UTF-8 characters are not escaped at all - * (except the escape character '\\'). This is the difference to g_strescape(), - * which escapes all non-ASCII characters. This allows to pass on - * valid UTF-8 characters as-is and can be directly shown to the user - * as UTF-8 -- with exception of the backslash escape character, - * invalid UTF-8 sequences, and other (depending on @flags). - * - * Returns: the escaped input string, as valid UTF-8. If no escaping - * is necessary, it returns the input @str. Otherwise, an allocated - * string @to_free is returned which must be freed by the caller - * with g_free. The escaping can be reverted by g_strcompress(). - **/ -const char * -nm_utils_str_utf8safe_escape(const char *str, NMUtilsStrUtf8SafeFlags flags, char **to_free) -{ - return nm_utils_buf_utf8safe_escape(str, -1, flags, to_free); -} - -/** - * nm_utils_str_utf8safe_escape_cp: - * @str: NUL terminated input string, possibly in utf-8 encoding - * @flags: #NMUtilsStrUtf8SafeFlags flags - * - * Like nm_utils_str_utf8safe_escape(), except the returned value - * is always a copy of the input and must be freed by the caller. - * - * Returns: the escaped input string in UTF-8 encoding. The returned - * value should be freed with g_free(). - * The escaping can be reverted by g_strcompress(). - **/ -char * -nm_utils_str_utf8safe_escape_cp(const char *str, NMUtilsStrUtf8SafeFlags flags) -{ - char *s; - - nm_utils_str_utf8safe_escape(str, flags, &s); - return s ?: g_strdup(str); -} - -char * -nm_utils_str_utf8safe_unescape_cp(const char *str, NMUtilsStrUtf8SafeFlags flags) -{ - char *s; - - str = nm_utils_str_utf8safe_unescape(str, flags, &s); - return s ?: g_strdup(str); -} - -char * -nm_utils_str_utf8safe_escape_take(char *str, NMUtilsStrUtf8SafeFlags flags) -{ - char *str_to_free; - - nm_utils_str_utf8safe_escape(str, flags, &str_to_free); - if (str_to_free) { - g_free(str); - return str_to_free; - } - return str; -} - -/*****************************************************************************/ - -/* taken from systemd's fd_wait_for_event(). Note that the timeout - * is here in nano-seconds, not micro-seconds. */ -int -nm_utils_fd_wait_for_event(int fd, int event, gint64 timeout_nsec) -{ - struct pollfd pollfd = { - .fd = fd, - .events = event, - }; - struct timespec ts, *pts; - int r; - - if (timeout_nsec < 0) - pts = NULL; - else { - ts.tv_sec = (time_t)(timeout_nsec / NM_UTILS_NSEC_PER_SEC); - ts.tv_nsec = (long int) (timeout_nsec % NM_UTILS_NSEC_PER_SEC); - pts = &ts; - } - - r = ppoll(&pollfd, 1, pts, NULL); - if (r < 0) - return -NM_ERRNO_NATIVE(errno); - if (r == 0) - return 0; - return pollfd.revents; -} - -/* taken from systemd's loop_read() */ -ssize_t -nm_utils_fd_read_loop(int fd, void *buf, size_t nbytes, bool do_poll) -{ - uint8_t *p = buf; - ssize_t n = 0; - - g_return_val_if_fail(fd >= 0, -EINVAL); - g_return_val_if_fail(buf, -EINVAL); - - /* If called with nbytes == 0, let's call read() at least - * once, to validate the operation */ - - if (nbytes > (size_t) SSIZE_MAX) - return -EINVAL; - - do { - ssize_t k; - - k = read(fd, p, nbytes); - if (k < 0) { - int errsv = errno; - - if (errsv == EINTR) - continue; - - if (errsv == EAGAIN && do_poll) { - /* We knowingly ignore any return value here, - * and expect that any error/EOF is reported - * via read() */ - - (void) nm_utils_fd_wait_for_event(fd, POLLIN, -1); - continue; - } - - return n > 0 ? n : -NM_ERRNO_NATIVE(errsv); - } - - if (k == 0) - return n; - - g_assert((size_t) k <= nbytes); - - p += k; - nbytes -= k; - n += k; - } while (nbytes > 0); - - return n; -} - -/* taken from systemd's loop_read_exact() */ -int -nm_utils_fd_read_loop_exact(int fd, void *buf, size_t nbytes, bool do_poll) -{ - ssize_t n; - - n = nm_utils_fd_read_loop(fd, buf, nbytes, do_poll); - if (n < 0) - return (int) n; - if ((size_t) n != nbytes) - return -EIO; - - return 0; -} - -/*****************************************************************************/ - -void -nm_utils_named_value_clear_with_g_free(NMUtilsNamedValue *val) -{ - if (val) { - gs_free gpointer x_name = NULL; - gs_free gpointer x_value = NULL; - - x_name = (gpointer) g_steal_pointer(&val->name); - x_value = g_steal_pointer(&val->value_ptr); - } -} - -G_STATIC_ASSERT(G_STRUCT_OFFSET(NMUtilsNamedValue, name) == 0); - -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) -{ - GHashTableIter iter; - NMUtilsNamedValue *values; - guint i, len; - - nm_assert(provided_buffer_len == 0 || provided_buffer); - nm_assert(!out_allocated_buffer || !*out_allocated_buffer); - - if (!hash || !(len = g_hash_table_size(hash))) { - NM_SET_OUT(out_len, 0); - return NULL; - } - - if (provided_buffer_len >= len + 1) { - /* the buffer provided by the caller is large enough. Use it. */ - values = provided_buffer; - } else { - /* allocate a new buffer. */ - values = g_new(NMUtilsNamedValue, len + 1); - NM_SET_OUT(out_allocated_buffer, values); - } - - i = 0; - g_hash_table_iter_init(&iter, hash); - while (g_hash_table_iter_next(&iter, (gpointer *) &values[i].name, &values[i].value_ptr)) - i++; - nm_assert(i == len); - values[i].name = NULL; - values[i].value_ptr = NULL; - - if (compare_func) - nm_utils_named_value_list_sort(values, len, compare_func, user_data); - - NM_SET_OUT(out_len, len); - return values; -} - -gssize -nm_utils_named_value_list_find(const NMUtilsNamedValue *arr, - gsize len, - const char * name, - gboolean sorted) -{ - gsize i; - - nm_assert(name); - -#if NM_MORE_ASSERTS > 5 - { - for (i = 0; i < len; i++) { - const NMUtilsNamedValue *v = &arr[i]; - - nm_assert(v->name); - if (sorted && i > 0) - nm_assert(strcmp(arr[i - 1].name, v->name) < 0); - } - } - - nm_assert(!sorted || nm_utils_named_value_list_is_sorted(arr, len, FALSE, NULL, NULL)); -#endif - - if (sorted) { - return nm_utils_array_find_binary_search(arr, - sizeof(NMUtilsNamedValue), - len, - &name, - nm_strcmp_p_with_data, - NULL); - } - for (i = 0; i < len; i++) { - if (nm_streq(arr[i].name, name)) - return i; - } - return ~((gssize) len); -} - -gboolean -nm_utils_named_value_list_is_sorted(const NMUtilsNamedValue *arr, - gsize len, - gboolean accept_duplicates, - GCompareDataFunc compare_func, - gpointer user_data) -{ - gsize i; - int c_limit; - - if (len == 0) - return TRUE; - - g_return_val_if_fail(arr, FALSE); - - if (!compare_func) - compare_func = nm_strcmp_p_with_data; - - c_limit = accept_duplicates ? 0 : -1; - - for (i = 1; i < len; i++) { - int c; - - c = compare_func(&arr[i - 1], &arr[i], user_data); - if (c > c_limit) - return FALSE; - } - return TRUE; -} - -void -nm_utils_named_value_list_sort(NMUtilsNamedValue *arr, - gsize len, - GCompareDataFunc compare_func, - gpointer user_data) -{ - if (len == 0) - return; - - g_return_if_fail(arr); - - if (len == 1) - return; - - g_qsort_with_data(arr, - len, - sizeof(NMUtilsNamedValue), - compare_func ?: nm_strcmp_p_with_data, - user_data); -} - -/*****************************************************************************/ - -gpointer * -nm_utils_hash_keys_to_array(GHashTable * hash, - GCompareDataFunc compare_func, - gpointer user_data, - guint * out_len) -{ - guint len; - gpointer *keys; - - /* by convention, we never return an empty array. In that - * case, always %NULL. */ - if (!hash || g_hash_table_size(hash) == 0) { - NM_SET_OUT(out_len, 0); - return NULL; - } - - keys = g_hash_table_get_keys_as_array(hash, &len); - if (len > 1 && compare_func) { - g_qsort_with_data(keys, len, sizeof(gpointer), compare_func, user_data); - } - NM_SET_OUT(out_len, len); - return keys; -} - -gpointer * -nm_utils_hash_values_to_array(GHashTable * hash, - GCompareDataFunc compare_func, - gpointer user_data, - guint * out_len) -{ - GHashTableIter iter; - gpointer value; - gpointer * arr; - guint i, len; - - if (!hash || (len = g_hash_table_size(hash)) == 0u) { - NM_SET_OUT(out_len, 0); - return NULL; - } - - arr = g_new(gpointer, ((gsize) len) + 1); - i = 0; - g_hash_table_iter_init(&iter, hash); - while (g_hash_table_iter_next(&iter, NULL, (gpointer *) &value)) - arr[i++] = value; - - nm_assert(i == len); - arr[len] = NULL; - - if (len > 1 && compare_func) { - g_qsort_with_data(arr, len, sizeof(gpointer), compare_func, user_data); - } - - NM_SET_OUT(out_len, len); - return arr; -} - -/*****************************************************************************/ - -/** - * nm_utils_hashtable_equal: - * @a: one #GHashTable - * @b: other #GHashTable - * @treat_null_as_empty: if %TRUE, when either @a or @b is %NULL, it is - * treated like an empty hash. It means, a %NULL hash will compare equal - * to an empty hash. - * @equal_func: the equality function, for comparing the values. - * If %NULL, the values are not compared. In that case, the function - * only checks, if both dictionaries have the same keys -- according - * to @b's key equality function. - * Note that the values of @a will be passed as first argument - * to @equal_func. - * - * Compares two hash tables, whether they have equal content. - * This only makes sense, if @a and @b have the same key types and - * the same key compare-function. - * - * Returns: %TRUE, if both dictionaries have the same content. - */ -gboolean -nm_utils_hashtable_equal(const GHashTable *a, - const GHashTable *b, - gboolean treat_null_as_empty, - GEqualFunc equal_func) -{ - guint n; - GHashTableIter iter; - gconstpointer key, v_a, v_b; - - if (a == b) - return TRUE; - if (!treat_null_as_empty) { - if (!a || !b) - return FALSE; - } - - n = a ? g_hash_table_size((GHashTable *) a) : 0; - if (n != (b ? g_hash_table_size((GHashTable *) b) : 0)) - return FALSE; - - if (n > 0) { - g_hash_table_iter_init(&iter, (GHashTable *) a); - while (g_hash_table_iter_next(&iter, (gpointer *) &key, (gpointer *) &v_a)) { - if (!g_hash_table_lookup_extended((GHashTable *) b, key, NULL, (gpointer *) &v_b)) - return FALSE; - if (equal_func && !equal_func(v_a, v_b)) - return FALSE; - } - } - - return TRUE; -} - -static gboolean -_utils_hashtable_equal(GHashTable * hash_a, - GHashTable * hash_b, - GCompareDataFunc cmp_values, - gpointer user_data) -{ - GHashTableIter h; - gpointer a_key; - gpointer a_val; - gpointer b_val; - - nm_assert(hash_a); - nm_assert(hash_b); - nm_assert(hash_a != hash_b); - nm_assert(g_hash_table_size(hash_a) == g_hash_table_size(hash_b)); - - /* We rely on both hashes to have the same hash/equal function. Otherwise, we would have to iterate - * both hashes and check whether all keys/values are present in the respective other hash (which - * would be O(n^2), since we couldn't use the plain lookup function. That is not a useful thing - * for this function. */ - - g_hash_table_iter_init(&h, hash_a); - while (g_hash_table_iter_next(&h, &a_key, &a_val)) { - if (!g_hash_table_lookup_extended(hash_b, a_key, NULL, &b_val)) - return FALSE; - - if (!cmp_values) { - /* we accept %NULL compare function to indicate that we don't care about the key. */ - continue; - } - - if (cmp_values(a_val, b_val, user_data) != 0) - return FALSE; - } - - return TRUE; -} - -/** - * nm_utils_hashtable_cmp_equal: - * @a: (allow-none): the hash table or %NULL - * @b: (allow-none): the other hash table or %NULL - * @cmp_values: (allow-none): if %NULL, only the keys - * will be compared. Otherwise, this function is used to - * check whether all keys are equal. - * @user_data: the argument for @cmp_values. - * - * It is required that both @a and @b have the same hash and equals - * function. - * - * Returns: %TRUE, if both keys have the same keys and (if - * @cmp_values is given) all values are the same. - */ -gboolean -nm_utils_hashtable_cmp_equal(const GHashTable *a, - const GHashTable *b, - GCompareDataFunc cmp_values, - gpointer user_data) -{ - GHashTable *hash_a = (GHashTable *) a; - GHashTable *hash_b = (GHashTable *) b; - gboolean same; - guint size; - - if (hash_a == hash_b) - return TRUE; - - if (!hash_a || !hash_b) - return FALSE; - - size = g_hash_table_size(hash_a); - if (size != g_hash_table_size(hash_b)) - return FALSE; - - if (size == 0) - return TRUE; - - same = _utils_hashtable_equal(hash_a, hash_b, cmp_values, user_data); - -#if NM_MORE_ASSERTS > 5 - nm_assert(same == _utils_hashtable_equal(hash_b, hash_a, cmp_values, user_data)); -#endif - - return same; -} - -typedef struct { - gpointer key; - gpointer val; -} HashTableCmpData; - -typedef struct { - GCompareDataFunc cmp_keys; - gpointer user_data; -} HashTableUserData; - -static int -_hashtable_cmp_func(gconstpointer a, gconstpointer b, gpointer user_data) -{ - const HashTableUserData *d = user_data; - const HashTableCmpData * d_a = *((const HashTableCmpData *const *) a); - const HashTableCmpData * d_b = *((const HashTableCmpData *const *) b); - - NM_CMP_RETURN(d->cmp_keys(d_a, d_b, d->user_data)); - return 0; -} - -/** - * nm_utils_hashtable_cmp: - * @a: (allow-none): the hash to compare. May be %NULL. - * @b: (allow-none): the other hash to compare. May be %NULL. - * @do_fast_precheck: if %TRUE, assume that the hashes are equal - * and that it is worth calling nm_utils_hashtable_cmp_equal() first. - * That requires, that both hashes have the same equals function - * which is compatible with the @cmp_keys function. - * @cmp_keys: the compare function for keys. Usually, the hash/equal function - * of both hashes corresponds to this function. If you set @do_fast_precheck - * to false, then this is not a requirement. - * @cmp_values: (allow-none): if %NULL, only the keys are compared. - * Otherwise, the values must are also compared with this function. - * - * Both hashes must have keys/values of the same domain, so that - * they can be effectively compared with @cmp_keys and @cmp_values. - * - * %NULL hashes compare equal to %NULL, but not to empty hashes. - * - * Returns: 0 if both hashes are equal, or -1 or 1 if one of the hashes - * sorts before/after. - */ -int -nm_utils_hashtable_cmp(const GHashTable *a, - const GHashTable *b, - gboolean do_fast_precheck, - GCompareDataFunc cmp_keys, - GCompareDataFunc cmp_values, - gpointer user_data) -{ - GHashTable *hash_a = (GHashTable *) a; - GHashTable *hash_b = (GHashTable *) b; - gs_free HashTableCmpData *cmp_array_free = NULL; - HashTableCmpData * cmp_array_a; - HashTableCmpData * cmp_array_b; - GHashTableIter h; - gpointer i_key; - gpointer i_val; - gsize size2; - guint size; - guint i; - - nm_assert(cmp_keys); - - NM_CMP_SELF(hash_a, hash_b); - - size = g_hash_table_size(hash_a); - - NM_CMP_DIRECT(size, g_hash_table_size(hash_b)); - - if (size == 0) - return 0; - - if (do_fast_precheck) { - gboolean same; - - /* we expect that the hashes are equal and the caller ensures us that they - * use the same hash/equal functions. Do a fast path check first... - * - * It's unclear whether this is worth it. The full comparison is O(n*ln(n)), - * while the fast check (using the hash lookup) is O(n). But then, the pre-check - * makes additional requirements on the hash's hash/equal functions -- the - * full comparison does not make such requirements. */ - same = _utils_hashtable_equal(hash_a, hash_b, cmp_values, user_data); -#if NM_MORE_ASSERTS > 5 - nm_assert(same == _utils_hashtable_equal(hash_b, hash_a, cmp_values, user_data)); -#endif - if (same) - return 0; - } - - size2 = ((gsize) size) * 2u; - if (size2 > 600u / sizeof(HashTableCmpData)) { - cmp_array_free = g_new(HashTableCmpData, size2); - cmp_array_a = cmp_array_free; - } else - cmp_array_a = g_newa(HashTableCmpData, size2); - cmp_array_b = &cmp_array_a[size]; - - i = 0; - g_hash_table_iter_init(&h, hash_a); - while (g_hash_table_iter_next(&h, &i_key, &i_val)) { - nm_assert(i < size); - cmp_array_a[i++] = (HashTableCmpData){ - .key = i_key, - .val = i_val, - }; - } - nm_assert(i == size); - - i = 0; - g_hash_table_iter_init(&h, hash_b); - while (g_hash_table_iter_next(&h, &i_key, &i_val)) { - nm_assert(i < size); - cmp_array_b[i++] = (HashTableCmpData){ - .key = i_key, - .val = i_val, - }; - } - nm_assert(i == size); - - g_qsort_with_data(cmp_array_a, - size, - sizeof(HashTableCmpData), - _hashtable_cmp_func, - &((HashTableUserData){ - .cmp_keys = cmp_keys, - .user_data = user_data, - })); - - g_qsort_with_data(cmp_array_b, - size, - sizeof(HashTableCmpData), - _hashtable_cmp_func, - &((HashTableUserData){ - .cmp_keys = cmp_keys, - .user_data = user_data, - })); - - for (i = 0; i < size; i++) { - NM_CMP_RETURN(cmp_keys(cmp_array_a[i].key, cmp_array_b[i].key, user_data)); - } - - if (cmp_values) { - for (i = 0; i < size; i++) { - NM_CMP_RETURN(cmp_values(cmp_array_a[i].val, cmp_array_b[i].val, user_data)); - } - } - - /* the fast-precheck should have already told that the arrays are equal. */ - nm_assert(!do_fast_precheck); - - return 0; -} - -char ** -nm_utils_strv_make_deep_copied(const char **strv) -{ - gsize i; - - /* it takes a strv list, and copies each - * strings. Note that this updates @strv *in-place* - * and returns it. */ - - if (!strv) - return NULL; - for (i = 0; strv[i]; i++) - strv[i] = g_strdup(strv[i]); - - return (char **) strv; -} - -char ** -nm_utils_strv_make_deep_copied_n(const char **strv, gsize len) -{ - gsize i; - - /* it takes a strv array with len elements, and copies each - * strings. Note that this updates @strv *in-place* - * and returns it. */ - - if (!strv) - return NULL; - for (i = 0; i < len; i++) - strv[i] = g_strdup(strv[i]); - - return (char **) strv; -} - -/** - * @strv: the strv array to copy. It may be %NULL if @len - * is negative or zero (in which case %NULL will be returned). - * @len: the length of strings in @str. If negative, strv is assumed - * to be a NULL terminated array. - * @deep_copied: if %TRUE, clones the individual strings. In that case, - * the returned array must be freed with g_strfreev(). Otherwise, the - * strings themself are not copied. You must take care of who owns the - * strings yourself. - * - * Like g_strdupv(), with two differences: - * - * - accepts a @len parameter for non-null terminated strv array. - * - * - this never returns an empty strv array, but always %NULL if - * there are no strings. - * - * Note that if @len is non-negative, then it still must not - * contain any %NULL pointers within the first @len elements. - * Otherwise, you would leak elements if you try to free the - * array with g_strfreev(). Allowing that would be error prone. - * - * Returns: (transfer full): a clone of the strv array. Always - * %NULL terminated. Depending on @deep_copied, the strings are - * cloned or not. - */ -char ** -_nm_utils_strv_dup(const char *const *strv, gssize len, gboolean deep_copied) -{ - gsize i, l; - char **v; - - if (len < 0) - l = NM_PTRARRAY_LEN(strv); - else - l = len; - if (l == 0) { - /* this function never returns an empty strv array. If you - * need that, handle it yourself. */ - return NULL; - } - - v = g_new(char *, l + 1); - for (i = 0; i < l; i++) { - if (G_UNLIKELY(!strv[i])) { - /* NULL strings are not allowed. Clear the remainder of the array - * and return it (with assertion failure). */ - l++; - for (; i < l; i++) - v[i] = NULL; - g_return_val_if_reached(v); - } - - if (deep_copied) - v[i] = g_strdup(strv[i]); - else - v[i] = (char *) strv[i]; - } - v[l] = NULL; - return v; -} - -const char ** -_nm_utils_strv_dup_packed(const char *const *strv, gssize len) - -{ - gs_free gsize *str_len_free = NULL; - gsize * str_len; - const char ** result; - gsize mem_len; - gsize pre_len; - gsize len2; - char * sbuf; - gsize i; - - nm_assert(len >= -1); - - if (G_LIKELY(len < 0)) { - if (!strv || !strv[0]) { - /* This function never returns an empty strv array. If you need that, handle it - * yourself. */ - return NULL; - } - len2 = NM_PTRARRAY_LEN(strv); - } else { - if (len == 0) - return NULL; - len2 = len; - } - - if (len2 > 300u / sizeof(gsize)) { - str_len_free = g_new(gsize, len2); - str_len = str_len_free; - } else - str_len = g_newa(gsize, len2); - - mem_len = 0; - for (i = 0; i < len2; i++) { - gsize l; - - if (G_LIKELY(strv[i])) - l = strlen(strv[i]) + 1u; - else - l = 0; - str_len[i] = l; - mem_len += l; - } - - pre_len = sizeof(const char *) * (len2 + 1u); - - result = g_malloc(pre_len + mem_len); - sbuf = &(((char *) result)[pre_len]); - for (i = 0; i < len2; i++) { - gsize l; - - if (G_UNLIKELY(!strv[i])) { - /* Technically there is no problem with accepting NULL strings. But that - * does not really result in a strv array, and likely this only happens due - * to a bug. We want to catch such bugs by asserting. - * - * We clear the remainder of the buffer and fail with an assertion. */ - len2++; - for (; i < len2; i++) - result[i] = NULL; - g_return_val_if_reached(result); - } - - result[i] = sbuf; - - l = str_len[i]; - memcpy(sbuf, strv[i], l); - sbuf += l; - } - result[i] = NULL; - nm_assert(i == len2); - nm_assert(sbuf == (&((const char *) result)[pre_len]) + mem_len); - - return result; -} - -/*****************************************************************************/ - -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 imin, imax, imid, i2min, i2max, i2mid; - int cmp; - - g_return_val_if_fail(list || !len, ~((gssize) 0)); - g_return_val_if_fail(cmpfcn, ~((gssize) 0)); - - imin = 0; - if (len > 0) { - imax = len - 1; - - while (imin <= imax) { - imid = imin + (imax - imin) / 2; - - cmp = cmpfcn(list[imid], needle, user_data); - if (cmp == 0) { - /* we found a matching entry at index imid. - * - * Does the caller request the first/last index as well (in case that - * there are multiple entries which compare equal). */ - - if (out_idx_first) { - i2min = imin; - i2max = imid + 1; - while (i2min <= i2max) { - i2mid = i2min + (i2max - i2min) / 2; - - cmp = cmpfcn(list[i2mid], needle, user_data); - if (cmp == 0) - i2max = i2mid - 1; - else { - nm_assert(cmp < 0); - i2min = i2mid + 1; - } - } - *out_idx_first = i2min; - } - if (out_idx_last) { - i2min = imid + 1; - i2max = imax; - while (i2min <= i2max) { - i2mid = i2min + (i2max - i2min) / 2; - - cmp = cmpfcn(list[i2mid], needle, user_data); - if (cmp == 0) - i2min = i2mid + 1; - else { - nm_assert(cmp > 0); - i2max = i2mid - 1; - } - } - *out_idx_last = i2min - 1; - } - return imid; - } - - if (cmp < 0) - imin = imid + 1; - else - imax = imid - 1; - } - } - - /* return the inverse of @imin. This is a negative number, but - * also is ~imin the position where the value should be inserted. */ - imin = ~imin; - NM_SET_OUT(out_idx_first, imin); - NM_SET_OUT(out_idx_last, imin); - return imin; -} - -/*****************************************************************************/ - -/** - * nm_utils_array_find_binary_search: - * @list: the list to search. It must be sorted according to @cmpfcn ordering. - * @elem_size: the size in bytes of each element in the list - * @len: the number of elements in @list - * @needle: the value that is searched - * @cmpfcn: the compare function. The elements @list are passed as first - * argument to @cmpfcn, while @needle is passed as second. Usually, the - * needle is the same data type as inside the list, however, that is - * not necessary, as long as @cmpfcn takes care to cast the two arguments - * accordingly. - * @user_data: optional argument passed to @cmpfcn - * - * Performs binary search for @needle in @list. On success, returns the - * (non-negative) index where the compare function found the searched element. - * On success, it returns a negative value. Note that the return negative value - * is the bitwise inverse of the position where the element should be inserted. - * - * If the list contains multiple matching elements, an arbitrary index is - * returned. - * - * Returns: the index to the element in the list, or the (negative, bitwise inverted) - * position where it should be. - */ -gssize -nm_utils_array_find_binary_search(gconstpointer list, - gsize elem_size, - gsize len, - gconstpointer needle, - GCompareDataFunc cmpfcn, - gpointer user_data) -{ - gssize imin, imax, imid; - int cmp; - - g_return_val_if_fail(list || !len, ~((gssize) 0)); - g_return_val_if_fail(cmpfcn, ~((gssize) 0)); - g_return_val_if_fail(elem_size > 0, ~((gssize) 0)); - - imin = 0; - if (len == 0) - return ~imin; - - imax = len - 1; - - while (imin <= imax) { - imid = imin + (imax - imin) / 2; - - cmp = cmpfcn(&((const char *) list)[elem_size * imid], needle, user_data); - if (cmp == 0) - return imid; - - if (cmp < 0) - imin = imid + 1; - else - imax = imid - 1; - } - - /* return the inverse of @imin. This is a negative number, but - * also is ~imin the position where the value should be inserted. */ - return ~imin; -} - -/*****************************************************************************/ - -/** - * nm_utils_get_start_time_for_pid: - * @pid: the process identifier - * @out_state: return the state character, like R, S, Z. See `man 5 proc`. - * @out_ppid: parent process id - * - * Originally copied from polkit source (src/polkit/polkitunixprocess.c) - * and adjusted. - * - * Returns: the timestamp when the process started (by parsing /proc/$PID/stat). - * If an error occurs (e.g. the process does not exist), 0 is returned. - * - * The returned start time counts since boot, in the unit HZ (with HZ usually being (1/100) seconds) - **/ -guint64 -nm_utils_get_start_time_for_pid(pid_t pid, char *out_state, pid_t *out_ppid) -{ - guint64 start_time; - char filename[256]; - gs_free char * contents = NULL; - size_t length; - gs_free const char **tokens = NULL; - char * p; - char state = ' '; - gint64 ppid = 0; - - start_time = 0; - contents = NULL; - - g_return_val_if_fail(pid > 0, 0); - - G_STATIC_ASSERT_EXPR(sizeof(GPid) >= sizeof(pid_t)); - - nm_sprintf_buf(filename, "/proc/%" G_PID_FORMAT "/stat", (GPid) pid); - - if (!g_file_get_contents(filename, &contents, &length, NULL)) - goto fail; - - /* start time is the token at index 19 after the '(process name)' entry - since only this - * field can contain the ')' character, search backwards for this to avoid malicious - * processes trying to fool us - */ - p = strrchr(contents, ')'); - if (!p) - goto fail; - p += 2; /* skip ') ' */ - if (p - contents >= (int) length) - goto fail; - - state = p[0]; - - tokens = nm_utils_strsplit_set(p, " "); - - if (NM_PTRARRAY_LEN(tokens) < 20) - goto fail; - - if (out_ppid) { - ppid = _nm_utils_ascii_str_to_int64(tokens[1], 10, 1, G_MAXINT, 0); - if (ppid == 0) - goto fail; - } - - start_time = _nm_utils_ascii_str_to_int64(tokens[19], 10, 1, G_MAXINT64, 0); - if (start_time == 0) - goto fail; - - NM_SET_OUT(out_state, state); - NM_SET_OUT(out_ppid, ppid); - return start_time; - -fail: - NM_SET_OUT(out_state, ' '); - NM_SET_OUT(out_ppid, 0); - return 0; -} - -/*****************************************************************************/ - -/** - * _nm_utils_strv_sort: - * @strv: pointer containing strings that will be sorted - * in-place, %NULL is allowed, unless @len indicates - * that there are more elements. - * @len: the number of elements in strv. If negative, - * strv must be a NULL terminated array and the length - * will be calculated first. If @len is a positive - * number, @strv is allowed to contain %NULL strings - * too. - * - * Ascending sort of the array @strv inplace, using plain strcmp() string - * comparison. - */ -void -_nm_utils_strv_sort(const char **strv, gssize len) -{ - GCompareDataFunc cmp; - gsize l; - - if (len < 0) { - l = NM_PTRARRAY_LEN(strv); - cmp = nm_strcmp_p_with_data; - } else { - l = len; - cmp = nm_strcmp0_p_with_data; - } - - if (l <= 1) - return; - - nm_assert(l <= (gsize) G_MAXINT); - - g_qsort_with_data(strv, l, sizeof(const char *), cmp, NULL); -} - -/** - * _nm_utils_strv_cmp_n: - * @strv1: a string array - * @len1: the length of @strv1, or -1 for NULL terminated array. - * @strv2: a string array - * @len2: the length of @strv2, or -1 for NULL terminated array. - * - * Note that - * - len == -1 && strv == NULL - * is treated like a %NULL argument and compares differently from - * other arrays. - * - * Note that an empty array can be represented as - * - len == -1 && strv && !strv[0] - * - len == 0 && !strv - * - len == 0 && strv - * These 3 forms all compare equal. - * It also means, if length is 0, then it is permissible for strv to be %NULL. - * - * The strv arrays may contain %NULL strings (if len is positive). - * - * Returns: 0 if the arrays are equal (using strcmp). - **/ -int -_nm_utils_strv_cmp_n(const char *const *strv1, gssize len1, const char *const *strv2, gssize len2) -{ - gsize n, n2; - - if (len1 < 0) { - if (!strv1) - return (len2 < 0 && !strv2) ? 0 : -1; - n = NM_PTRARRAY_LEN(strv1); - } else - n = len1; - - if (len2 < 0) { - if (!strv2) - return 1; - n2 = NM_PTRARRAY_LEN(strv2); - } else - n2 = len2; - - NM_CMP_DIRECT(n, n2); - for (; n > 0; n--, strv1++, strv2++) - NM_CMP_DIRECT_STRCMP0(*strv1, *strv2); - return 0; -} - -/*****************************************************************************/ - -/** - * nm_utils_g_slist_find_str: - * @list: the #GSList with NUL terminated strings to search - * @needle: the needle string to look for. - * - * Search the list for @needle and return the first found match - * (or %NULL if not found). Uses strcmp() for finding the first matching - * element. - * - * Returns: the #GSList element with @needle as string value or - * %NULL if not found. - */ -GSList * -nm_utils_g_slist_find_str(const GSList *list, const char *needle) -{ - nm_assert(needle); - - for (; list; list = list->next) { - nm_assert(list->data); - if (nm_streq(list->data, needle)) - return (GSList *) list; - } - return NULL; -} - -/** - * nm_utils_g_slist_strlist_cmp: - * @a: the left #GSList of strings - * @b: the right #GSList of strings to compare. - * - * Compares two string lists. The data elements are compared with - * strcmp(), allowing %NULL elements. - * - * Returns: 0, 1, or -1, depending on how the lists compare. - */ -int -nm_utils_g_slist_strlist_cmp(const GSList *a, const GSList *b) -{ - while (TRUE) { - if (!a) - return !b ? 0 : -1; - if (!b) - return 1; - NM_CMP_DIRECT_STRCMP0(a->data, b->data); - a = a->next; - b = b->next; - } -} - -char * -nm_utils_g_slist_strlist_join(const GSList *a, const char *separator) -{ - GString *str = NULL; - - if (!a) - return NULL; - - for (; a; a = a->next) { - if (!str) - str = g_string_new(NULL); - else - g_string_append(str, separator); - g_string_append(str, a->data); - } - return g_string_free(str, FALSE); -} - -/*****************************************************************************/ - -NMUtilsUserData * -_nm_utils_user_data_pack(int nargs, gconstpointer *args) -{ - int i; - gpointer *data; - - nm_assert(nargs > 0); - nm_assert(args); - - data = g_slice_alloc(((gsize) nargs) * sizeof(gconstpointer)); - for (i = 0; i < nargs; i++) - data[i] = (gpointer) args[i]; - return (NMUtilsUserData *) data; -} - -void -_nm_utils_user_data_unpack(NMUtilsUserData *user_data, int nargs, ...) -{ - gpointer *data = (gpointer *) user_data; - va_list ap; - int i; - - nm_assert(data); - nm_assert(nargs > 0); - - va_start(ap, nargs); - for (i = 0; i < nargs; i++) { - gpointer *dst; - - dst = va_arg(ap, gpointer *); - nm_assert(dst); - - *dst = data[i]; - } - va_end(ap); - - g_slice_free1(((gsize) nargs) * sizeof(gconstpointer), data); -} - -/*****************************************************************************/ - -typedef struct { - gpointer callback_user_data; - GCancellable * cancellable; - GSource * source; - NMUtilsInvokeOnIdleCallback callback; - gulong cancelled_id; -} InvokeOnIdleData; - -static gboolean -_nm_utils_invoke_on_idle_cb_idle(gpointer user_data) -{ - InvokeOnIdleData *data = user_data; - - nm_clear_g_signal_handler(data->cancellable, &data->cancelled_id); - - data->callback(data->callback_user_data, data->cancellable); - - nm_g_object_unref(data->cancellable); - g_source_destroy(data->source); - nm_g_slice_free(data); - return G_SOURCE_REMOVE; -} - -static void -_nm_utils_invoke_on_idle_cb_cancelled(GCancellable *cancellable, InvokeOnIdleData *data) -{ - /* on cancellation, we invoke the callback synchronously. */ - nm_clear_g_signal_handler(data->cancellable, &data->cancelled_id); - nm_clear_g_source_inst(&data->source); - data->callback(data->callback_user_data, data->cancellable); - nm_g_object_unref(data->cancellable); - nm_g_slice_free(data); -} - -static void -_nm_utils_invoke_on_idle_start(gboolean use_timeout, - guint timeout_msec, - GCancellable * cancellable, - NMUtilsInvokeOnIdleCallback callback, - gpointer callback_user_data) -{ - InvokeOnIdleData *data; - GSource * source; - - g_return_if_fail(callback); - - data = g_slice_new(InvokeOnIdleData); - *data = (InvokeOnIdleData){ - .callback = callback, - .callback_user_data = callback_user_data, - .cancellable = nm_g_object_ref(cancellable), - .cancelled_id = 0, - }; - - if (cancellable) { - if (g_cancellable_is_cancelled(cancellable)) { - /* the cancellable is already cancelled. We ignore the timeout - * and always schedule an idle action. */ - use_timeout = FALSE; - } else { - /* if we are passed a non-cancelled cancellable, we register to the "cancelled" - * signal an invoke the callback synchronously (from the signal handler). - * - * We don't do that, - * - if the cancellable is already cancelled (because we don't want to invoke - * the callback synchronously from the caller). - * - if we have no cancellable at hand. */ - data->cancelled_id = g_signal_connect(cancellable, - "cancelled", - G_CALLBACK(_nm_utils_invoke_on_idle_cb_cancelled), - data); - } - } - - if (use_timeout) { - source = nm_g_timeout_source_new(timeout_msec, - G_PRIORITY_DEFAULT, - _nm_utils_invoke_on_idle_cb_idle, - data, - NULL); - } else { - source = - nm_g_idle_source_new(G_PRIORITY_DEFAULT, _nm_utils_invoke_on_idle_cb_idle, data, NULL); - } - - /* use the current thread default context. */ - g_source_attach(source, g_main_context_get_thread_default()); - - data->source = source; -} - -void -nm_utils_invoke_on_idle(GCancellable * cancellable, - NMUtilsInvokeOnIdleCallback callback, - gpointer callback_user_data) -{ - _nm_utils_invoke_on_idle_start(FALSE, 0, cancellable, callback, callback_user_data); -} - -void -nm_utils_invoke_on_timeout(guint timeout_msec, - GCancellable * cancellable, - NMUtilsInvokeOnIdleCallback callback, - gpointer callback_user_data) -{ - _nm_utils_invoke_on_idle_start(TRUE, timeout_msec, cancellable, callback, callback_user_data); -} - -/*****************************************************************************/ - -int -nm_utils_getpagesize(void) -{ - static volatile int val = 0; - long l; - int v; - - v = g_atomic_int_get(&val); - - if (G_UNLIKELY(v == 0)) { - l = sysconf(_SC_PAGESIZE); - - g_return_val_if_fail(l > 0 && l < G_MAXINT, 4 * 1024); - - v = (int) l; - if (!g_atomic_int_compare_and_exchange(&val, 0, v)) { - v = g_atomic_int_get(&val); - g_return_val_if_fail(v > 0, 4 * 1024); - } - } - - nm_assert(v > 0); -#if NM_MORE_ASSERTS > 5 - nm_assert(v == getpagesize()); - nm_assert(v == sysconf(_SC_PAGESIZE)); -#endif - - return v; -} - -gboolean -nm_utils_memeqzero(gconstpointer data, gsize length) -{ - const unsigned char *p = data; - int len; - - /* Taken from https://github.com/rustyrussell/ccan/blob/9d2d2c49f053018724bcc6e37029da10b7c3d60d/ccan/mem/mem.c#L92, - * CC-0 licensed. */ - - /* Check first 16 bytes manually */ - for (len = 0; len < 16; len++) { - if (!length) - return TRUE; - if (*p) - return FALSE; - p++; - length--; - } - - /* Now we know that's zero, memcmp with self. */ - return memcmp(data, p, length) == 0; -} - -/** - * nm_utils_bin2hexstr_full: - * @addr: pointer of @length bytes. If @length is zero, this may - * also be %NULL. - * @length: number of bytes in @addr. May also be zero, in which - * case this will return an empty string. - * @delimiter: either '\0', otherwise the output string will have the - * given delimiter character between each two hex numbers. - * @upper_case: if TRUE, use upper case ASCII characters for hex. - * @out: if %NULL, the function will allocate a new buffer of - * either (@length*2+1) or (@length*3) bytes, depending on whether - * a @delimiter is specified. In that case, the allocated buffer will - * be returned and must be freed by the caller. - * If not %NULL, the buffer must already be preallocated and contain - * at least (@length*2+1) or (@length*3) bytes, depending on the delimiter. - * If @length is zero, then of course at least one byte will be allocated - * or @out (if given) must contain at least room for the trailing NUL byte. - * - * Returns: the binary value converted to a hex string. If @out is given, - * this always returns @out. If @out is %NULL, a newly allocated string - * is returned. This never returns %NULL, for buffers of length zero - * an empty string is returned. - */ -char * -nm_utils_bin2hexstr_full(gconstpointer addr, - gsize length, - char delimiter, - gboolean upper_case, - char * out) -{ - const guint8 *in = addr; - const char * LOOKUP = upper_case ? "0123456789ABCDEF" : "0123456789abcdef"; - char * out0; - - if (out) - out0 = out; - else { - out0 = out = - g_new(char, length == 0 ? 1u : (delimiter == '\0' ? length * 2u + 1u : length * 3u)); - } - - /* @out must contain at least @length*3 bytes if @delimiter is set, - * otherwise, @length*2+1. */ - - if (length > 0) { - nm_assert(in); - for (;;) { - const guint8 v = *in++; - - *out++ = LOOKUP[v >> 4]; - *out++ = LOOKUP[v & 0x0F]; - length--; - if (!length) - break; - if (delimiter) - *out++ = delimiter; - } - } - - *out = '\0'; - return out0; -} - -guint8 * -nm_utils_hexstr2bin_full(const char *hexstr, - gboolean allow_0x_prefix, - gboolean delimiter_required, - gboolean hexdigit_pairs_required, - const char *delimiter_candidates, - gsize required_len, - guint8 * buffer, - gsize buffer_len, - gsize * out_len) -{ - const char *in = hexstr; - guint8 * out = buffer; - gboolean delimiter_has = TRUE; - guint8 delimiter = '\0'; - gsize len; - - nm_assert(hexstr); - nm_assert(buffer); - nm_assert(required_len > 0 || out_len); - - if (allow_0x_prefix && in[0] == '0' && in[1] == 'x') - in += 2; - - while (TRUE) { - const guint8 d1 = in[0]; - guint8 d2; - int i1, i2; - - i1 = nm_utils_hexchar_to_int(d1); - if (i1 < 0) - goto fail; - - /* If there's no leading zero (ie "aa:b:cc") then fake it */ - d2 = in[1]; - if (d2 && (i2 = nm_utils_hexchar_to_int(d2)) >= 0) { - *out++ = (i1 << 4) + i2; - d2 = in[2]; - if (!d2) - break; - in += 2; - } else { - /* Fake leading zero */ - *out++ = i1; - if (!d2) { - if (!delimiter_has || hexdigit_pairs_required) { - /* when using no delimiter, there must be pairs of hex chars */ - goto fail; - } - break; - } else if (hexdigit_pairs_required) - goto fail; - in += 1; - } - - if (--buffer_len == 0) - goto fail; - - if (delimiter_has) { - if (d2 != delimiter) { - if (delimiter) - goto fail; - if (delimiter_candidates) { - while (delimiter_candidates[0]) { - if (delimiter_candidates++[0] == d2) - delimiter = d2; - } - } - if (!delimiter) { - if (delimiter_required) - goto fail; - delimiter_has = FALSE; - continue; - } - } - in++; - } - } - - len = out - buffer; - if (required_len == 0 || len == required_len) { - NM_SET_OUT(out_len, len); - return buffer; - } - -fail: - NM_SET_OUT(out_len, 0); - return NULL; -} - -guint8 * -nm_utils_hexstr2bin_alloc(const char *hexstr, - gboolean allow_0x_prefix, - gboolean delimiter_required, - const char *delimiter_candidates, - gsize required_len, - gsize * out_len) -{ - guint8 *buffer; - gsize buffer_len, len; - - if (G_UNLIKELY(!hexstr)) { - NM_SET_OUT(out_len, 0); - g_return_val_if_fail(hexstr, NULL); - } - - nm_assert(required_len > 0 || out_len); - - if (allow_0x_prefix && hexstr[0] == '0' && hexstr[1] == 'x') - hexstr += 2; - - if (!hexstr[0]) - goto fail; - - if (required_len > 0) - buffer_len = required_len; - else - buffer_len = strlen(hexstr) / 2 + 3; - - buffer = g_malloc(buffer_len); - - if (nm_utils_hexstr2bin_full(hexstr, - FALSE, - delimiter_required, - FALSE, - delimiter_candidates, - required_len, - buffer, - buffer_len, - &len)) { - NM_SET_OUT(out_len, len); - return buffer; - } - - g_free(buffer); - -fail: - NM_SET_OUT(out_len, 0); - return NULL; -} - -/*****************************************************************************/ - -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) -{ - GVariantIter iter; - GVariantBuilder builder; - const char * key; - GVariant * val; - - g_return_val_if_fail(src && g_variant_is_of_type(src, G_VARIANT_TYPE_VARDICT), NULL); - g_return_val_if_fail(filter_fcn, NULL); - - g_variant_builder_init(&builder, G_VARIANT_TYPE_VARDICT); - - g_variant_iter_init(&iter, src); - while (g_variant_iter_next(&iter, "{&sv}", &key, &val)) { - _nm_unused gs_unref_variant GVariant *val_free = val; - gs_free char * key2 = NULL; - gs_unref_variant GVariant *val2 = NULL; - - if (filter_fcn(key, val, &key2, &val2, user_data)) { - g_variant_builder_add(&builder, "{sv}", key2 ?: key, val2 ?: val); - } - } - - return g_variant_builder_end(&builder); -} - -static gboolean -_gvariant_vardict_filter_drop_one(const char *key, - GVariant * val, - char ** out_key, - GVariant ** out_val, - gpointer user_data) -{ - return !nm_streq(key, user_data); -} - -GVariant * -nm_utils_gvariant_vardict_filter_drop_one(GVariant *src, const char *key) -{ - return nm_utils_gvariant_vardict_filter(src, _gvariant_vardict_filter_drop_one, (gpointer) key); -} - -/*****************************************************************************/ - -static gboolean -debug_key_matches(const char *key, const char *token, guint length) -{ - /* may not call GLib functions: see note in g_parse_debug_string() */ - for (; length; length--, key++, token++) { - char k = (*key == '_') ? '-' : g_ascii_tolower(*key); - char t = (*token == '_') ? '-' : g_ascii_tolower(*token); - - if (k != t) - return FALSE; - } - - return *key == '\0'; -} - -/** - * nm_utils_parse_debug_string: - * @string: the string to parse - * @keys: the debug keys - * @nkeys: number of entries in @keys - * - * Similar to g_parse_debug_string(), but does not special - * case "help" or "all". - * - * Returns: the flags - */ -guint -nm_utils_parse_debug_string(const char *string, const GDebugKey *keys, guint nkeys) -{ - guint i; - guint result = 0; - const char *q; - - if (string == NULL) - return 0; - - while (*string) { - q = strpbrk(string, ":;, \t"); - if (!q) - q = string + strlen(string); - - for (i = 0; i < nkeys; i++) { - if (debug_key_matches(keys[i].key, string, q - string)) - result |= keys[i].value; - } - - string = q; - if (*string) - string++; - } - - return result; -} - -/*****************************************************************************/ - -GSource * -nm_g_idle_source_new(int priority, - GSourceFunc func, - gpointer user_data, - GDestroyNotify destroy_notify) -{ - GSource *source; - - source = g_idle_source_new(); - if (priority != G_PRIORITY_DEFAULT) - g_source_set_priority(source, priority); - g_source_set_callback(source, func, user_data, destroy_notify); - return source; -} - -GSource * -nm_g_timeout_source_new(guint timeout_msec, - int priority, - GSourceFunc func, - gpointer user_data, - GDestroyNotify destroy_notify) -{ - GSource *source; - - source = g_timeout_source_new(timeout_msec); - if (priority != G_PRIORITY_DEFAULT) - g_source_set_priority(source, priority); - g_source_set_callback(source, func, user_data, destroy_notify); - return source; -} - -GSource * -nm_g_timeout_source_new_seconds(guint timeout_sec, - int priority, - GSourceFunc func, - gpointer user_data, - GDestroyNotify destroy_notify) -{ - GSource *source; - - source = g_timeout_source_new_seconds(timeout_sec); - if (priority != G_PRIORITY_DEFAULT) - g_source_set_priority(source, priority); - g_source_set_callback(source, func, user_data, destroy_notify); - return source; -} - -GSource * -nm_g_unix_signal_source_new(int signum, - int priority, - GSourceFunc handler, - gpointer user_data, - GDestroyNotify notify) -{ - GSource *source; - - source = g_unix_signal_source_new(signum); - - if (priority != G_PRIORITY_DEFAULT) - g_source_set_priority(source, priority); - g_source_set_callback(source, handler, user_data, notify); - return source; -} - -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 *source; - - source = g_unix_fd_source_new(fd, io_condition); - - if (priority != G_PRIORITY_DEFAULT) - g_source_set_priority(source, priority); - g_source_set_callback(source, G_SOURCE_FUNC(source_func), user_data, destroy_notify); - return source; -} - -/*****************************************************************************/ - -#define _CTX_LOG(fmt, ...) \ - G_STMT_START \ - { \ - if (FALSE) { \ - gint64 _ts = g_get_monotonic_time() / 100; \ - \ - g_printerr(">>>> [%" G_GINT64_FORMAT ".%05" G_GINT64_FORMAT "] [src:%p]: " fmt "\n", \ - _ts / 10000, \ - _ts % 10000, \ - (ctx_src), \ - ##__VA_ARGS__); \ - } \ - } \ - G_STMT_END - -typedef struct { - int fd; - guint events; - guint registered_events; - union { - int one; - int *many; - } idx; - gpointer tag; - bool stale : 1; - bool has_many_idx : 1; -} PollData; - -typedef struct { - GSource source; - GMainContext *context; - GHashTable * fds; - GPollFD * fds_arr; - guint fds_len; - int max_priority; - bool acquired : 1; -} CtxIntegSource; - -static void -_poll_data_free(gpointer user_data) -{ - PollData *poll_data = user_data; - - if (poll_data->has_many_idx) - g_free(poll_data->idx.many); - nm_g_slice_free(poll_data); -} - -static void -_ctx_integ_source_reacquire(CtxIntegSource *ctx_src) -{ - if (G_LIKELY(ctx_src->acquired && g_main_context_is_owner(ctx_src->context))) - return; - - /* the parent context now iterates on a different thread. - * We need to release and reacquire the inner context. */ - - if (ctx_src->acquired) - g_main_context_release(ctx_src->context); - - if (G_UNLIKELY(!g_main_context_acquire(ctx_src->context))) { - /* Nobody is supposed to reacquire the context while we use it. This is a bug - * of the user. */ - ctx_src->acquired = FALSE; - g_return_if_reached(); - } - ctx_src->acquired = TRUE; -} - -static gboolean -_ctx_integ_source_prepare(GSource *source, int *out_timeout) -{ - CtxIntegSource *ctx_src = ((CtxIntegSource *) source); - int max_priority; - int timeout = -1; - gboolean any_ready; - GHashTableIter h_iter; - PollData * poll_data; - gboolean fds_changed; - GPollFD new_fds_stack[300u / sizeof(GPollFD)]; - gs_free GPollFD *new_fds_heap = NULL; - GPollFD * new_fds; - guint new_fds_len; - guint new_fds_alloc; - guint i; - - _CTX_LOG("prepare..."); - - _ctx_integ_source_reacquire(ctx_src); - - any_ready = g_main_context_prepare(ctx_src->context, &max_priority); - - new_fds_alloc = NM_MAX(G_N_ELEMENTS(new_fds_stack), ctx_src->fds_len); - - if (new_fds_alloc > G_N_ELEMENTS(new_fds_stack)) { - new_fds_heap = g_new(GPollFD, new_fds_alloc); - new_fds = new_fds_heap; - } else - new_fds = new_fds_stack; - - for (;;) { - int l; - - nm_assert(new_fds_alloc <= (guint) G_MAXINT); - - l = g_main_context_query(ctx_src->context, - max_priority, - &timeout, - new_fds, - (int) new_fds_alloc); - nm_assert(l >= 0); - - new_fds_len = (guint) l; - - if (G_LIKELY(new_fds_len <= new_fds_alloc)) - break; - - new_fds_alloc = new_fds_len; - g_free(new_fds_heap); - new_fds_heap = g_new(GPollFD, new_fds_alloc); - new_fds = new_fds_heap; - } - - fds_changed = FALSE; - if (new_fds_len != ctx_src->fds_len) - fds_changed = TRUE; - else { - for (i = 0; i < new_fds_len; i++) { - if (new_fds[i].fd != ctx_src->fds_arr[i].fd - || new_fds[i].events != ctx_src->fds_arr[i].events) { - fds_changed = TRUE; - break; - } - } - } - - if (G_UNLIKELY(fds_changed)) { - g_free(ctx_src->fds_arr); - ctx_src->fds_len = new_fds_len; - if (G_LIKELY(new_fds == new_fds_stack) || new_fds_alloc != new_fds_len) - ctx_src->fds_arr = nm_memdup(new_fds, sizeof(*new_fds) * new_fds_len); - else - ctx_src->fds_arr = g_steal_pointer(&new_fds_heap); - - g_hash_table_iter_init(&h_iter, ctx_src->fds); - while (g_hash_table_iter_next(&h_iter, (gpointer *) &poll_data, NULL)) - poll_data->stale = TRUE; - - for (i = 0; i < ctx_src->fds_len; i++) { - const GPollFD *fd = &ctx_src->fds_arr[i]; - - poll_data = g_hash_table_lookup(ctx_src->fds, &fd->fd); - - if (G_UNLIKELY(!poll_data)) { - poll_data = g_slice_new(PollData); - *poll_data = (PollData){ - .fd = fd->fd, - .idx.one = i, - .has_many_idx = FALSE, - .events = fd->events, - .registered_events = 0, - .tag = NULL, - .stale = FALSE, - }; - g_hash_table_add(ctx_src->fds, poll_data); - nm_assert(poll_data == g_hash_table_lookup(ctx_src->fds, &fd->fd)); - continue; - } - - if (G_LIKELY(poll_data->stale)) { - if (poll_data->has_many_idx) { - g_free(poll_data->idx.many); - poll_data->has_many_idx = FALSE; - } - poll_data->events = fd->events; - poll_data->idx.one = i; - poll_data->stale = FALSE; - continue; - } - - /* How odd. We have duplicate FDs. In fact, currently g_main_context_query() always - * coalesces the FDs and this cannot happen. However, that is not documented behavior, - * so we should not rely on that. So we need to keep a list of indexes... */ - poll_data->events |= fd->events; - if (!poll_data->has_many_idx) { - int idx0; - - idx0 = poll_data->idx.one; - poll_data->has_many_idx = TRUE; - poll_data->idx.many = g_new(int, 4); - poll_data->idx.many[0] = 2; /* number allocated */ - poll_data->idx.many[1] = 2; /* number used */ - poll_data->idx.many[2] = idx0; - poll_data->idx.many[3] = i; - } else { - if (poll_data->idx.many[0] == poll_data->idx.many[1]) { - poll_data->idx.many[0] *= 2; - poll_data->idx.many = - g_realloc(poll_data->idx.many, sizeof(int) * (2 + poll_data->idx.many[0])); - } - poll_data->idx.many[2 + poll_data->idx.many[1]] = i; - poll_data->idx.many[1]++; - } - } - - g_hash_table_iter_init(&h_iter, ctx_src->fds); - while (g_hash_table_iter_next(&h_iter, (gpointer *) &poll_data, NULL)) { - if (poll_data->stale) { - nm_assert(poll_data->tag); - nm_assert(poll_data->events == poll_data->registered_events); - _CTX_LOG("prepare: remove poll fd=%d, events=0x%x", - poll_data->fd, - poll_data->events); - g_source_remove_unix_fd(&ctx_src->source, poll_data->tag); - g_hash_table_iter_remove(&h_iter); - continue; - } - if (!poll_data->tag) { - _CTX_LOG("prepare: add poll fd=%d, events=0x%x", poll_data->fd, poll_data->events); - poll_data->registered_events = poll_data->events; - poll_data->tag = g_source_add_unix_fd(&ctx_src->source, - poll_data->fd, - poll_data->registered_events); - continue; - } - if (poll_data->registered_events != poll_data->events) { - _CTX_LOG("prepare: update poll fd=%d, events=0x%x", - poll_data->fd, - poll_data->events); - poll_data->registered_events = poll_data->events; - g_source_modify_unix_fd(&ctx_src->source, - poll_data->tag, - poll_data->registered_events); - } - } - } - - NM_SET_OUT(out_timeout, timeout); - ctx_src->max_priority = max_priority; - - _CTX_LOG("prepare: done, any-ready=%d, timeout=%d, max-priority=%d", - any_ready, - timeout, - max_priority); - - /* we always need to poll, because we have some file descriptors. */ - return FALSE; -} - -static gboolean -_ctx_integ_source_check(GSource *source) -{ - CtxIntegSource *ctx_src = ((CtxIntegSource *) source); - GHashTableIter h_iter; - gboolean some_ready; - PollData * poll_data; - - nm_assert(ctx_src->context); - - _CTX_LOG("check"); - - _ctx_integ_source_reacquire(ctx_src); - - g_hash_table_iter_init(&h_iter, ctx_src->fds); - while (g_hash_table_iter_next(&h_iter, (gpointer *) &poll_data, NULL)) { - guint revents; - - revents = g_source_query_unix_fd(&ctx_src->source, poll_data->tag); - if (G_UNLIKELY(poll_data->has_many_idx)) { - int num = poll_data->idx.many[1]; - int *p_idx = &poll_data->idx.many[2]; - - for (; num > 0; num--, p_idx++) - ctx_src->fds_arr[*p_idx].revents = revents; - } else - ctx_src->fds_arr[poll_data->idx.one].revents = revents; - } - - nm_assert(ctx_src->fds_len <= (guint) G_MAXINT); - - some_ready = g_main_context_check(ctx_src->context, - ctx_src->max_priority, - ctx_src->fds_arr, - (int) ctx_src->fds_len); - - _CTX_LOG("check (some-ready=%d)...", some_ready); - - return some_ready; -} - -static gboolean -_ctx_integ_source_dispatch(GSource *source, GSourceFunc callback, gpointer user_data) -{ - CtxIntegSource *ctx_src = ((CtxIntegSource *) source); - - nm_assert(ctx_src->context); - - _ctx_integ_source_reacquire(ctx_src); - - _CTX_LOG("dispatch"); - - g_main_context_dispatch(ctx_src->context); - - return G_SOURCE_CONTINUE; -} - -static void -_ctx_integ_source_finalize(GSource *source) -{ - CtxIntegSource *ctx_src = ((CtxIntegSource *) source); - GHashTableIter h_iter; - PollData * poll_data; - - g_return_if_fail(ctx_src->context); - - _CTX_LOG("finalize..."); - - g_hash_table_iter_init(&h_iter, ctx_src->fds); - while (g_hash_table_iter_next(&h_iter, (gpointer *) &poll_data, NULL)) { - nm_assert(poll_data->tag); - _CTX_LOG("prepare: remove poll fd=%d, events=0x%x", poll_data->fd, poll_data->events); - g_source_remove_unix_fd(&ctx_src->source, poll_data->tag); - g_hash_table_iter_remove(&h_iter); - } - - nm_clear_pointer(&ctx_src->fds, g_hash_table_unref); - nm_clear_g_free(&ctx_src->fds_arr); - ctx_src->fds_len = 0; - - if (ctx_src->acquired) { - ctx_src->acquired = FALSE; - g_main_context_release(ctx_src->context); - } - - nm_clear_pointer(&ctx_src->context, g_main_context_unref); -} - -static GSourceFuncs ctx_integ_source_funcs = { - .prepare = _ctx_integ_source_prepare, - .check = _ctx_integ_source_check, - .dispatch = _ctx_integ_source_dispatch, - .finalize = _ctx_integ_source_finalize, -}; - -/** - * nm_utils_g_main_context_create_integrate_source: - * @inner_context: the inner context that will be integrated to an - * outer #GMainContext. - * - * By integrating the inner context with an outer context, when iterating the outer - * context sources on the inner context will be dispatched. Note that while the - * created source exists, the @inner_context will be acquired. The user gets restricted - * what to do with the inner context. In particular while the inner context is integrated, - * the user should not acquire the inner context again or explicitly iterate it. What - * the user of course still can (and wants to) do is attaching new sources to the inner - * context. - * - * Note that GSource has a priority. While each context dispatches events based on - * their source's priorities, the outer context dispatches to the inner context - * only with one priority (the priority of the created source). That is, the sources - * from the two contexts are kept separate and are not sorted by their priorities. - * - * Returns: a newly created GSource that should be attached to the - * outer context. - */ -GSource * -nm_utils_g_main_context_create_integrate_source(GMainContext *inner_context) -{ - CtxIntegSource *ctx_src; - - g_return_val_if_fail(inner_context, NULL); - - if (!g_main_context_acquire(inner_context)) { - /* We require to acquire the context while it's integrated. We need to keep it acquired - * for the entire duration. - * - * This is also necessary because g_source_attach() only wakes up the context, if - * the context is currently acquired. */ - g_return_val_if_reached(NULL); - } - - ctx_src = (CtxIntegSource *) g_source_new(&ctx_integ_source_funcs, sizeof(CtxIntegSource)); - - g_source_set_name(&ctx_src->source, "ContextIntegrateSource"); - - ctx_src->context = g_main_context_ref(inner_context); - ctx_src->fds = g_hash_table_new_full(nm_pint_hash, nm_pint_equals, _poll_data_free, NULL); - ctx_src->fds_len = 0; - ctx_src->fds_arr = NULL; - ctx_src->acquired = TRUE; - ctx_src->max_priority = G_MAXINT; - - _CTX_LOG("create new integ-source for %p", inner_context); - - return &ctx_src->source; -} - -/*****************************************************************************/ - -void -nm_utils_ifname_cpy(char *dst, const char *name) -{ - int i; - - g_return_if_fail(dst); - g_return_if_fail(name && name[0]); - - nm_assert(nm_utils_ifname_valid_kernel(name, NULL)); - - /* ensures NUL padding of the entire IFNAMSIZ buffer. */ - - for (i = 0; i < (int) IFNAMSIZ && name[i] != '\0'; i++) - dst[i] = name[i]; - - nm_assert(name[i] == '\0'); - - for (; i < (int) IFNAMSIZ; i++) - dst[i] = '\0'; -} - -/*****************************************************************************/ - -gboolean -nm_utils_ifname_valid_kernel(const char *name, GError **error) -{ - int i; - - /* This function follows kernel's interface validation - * function dev_valid_name() in net/core/dev.c. - */ - - if (!name) { - g_set_error_literal(error, - NM_UTILS_ERROR, - NM_UTILS_ERROR_UNKNOWN, - _("interface name is missing")); - return FALSE; - } - - if (name[0] == '\0') { - g_set_error_literal(error, - NM_UTILS_ERROR, - NM_UTILS_ERROR_UNKNOWN, - _("interface name is too short")); - return FALSE; - } - - if (name[0] == '.' && (name[1] == '\0' || (name[1] == '.' && name[2] == '\0'))) { - g_set_error_literal(error, - NM_UTILS_ERROR, - NM_UTILS_ERROR_UNKNOWN, - _("interface name is reserved")); - return FALSE; - } - - for (i = 0; i < IFNAMSIZ; i++) { - char ch = name[i]; - - if (ch == '\0') - return TRUE; - if (NM_IN_SET(ch, '/', ':') || g_ascii_isspace(ch)) { - g_set_error_literal(error, - NM_UTILS_ERROR, - NM_UTILS_ERROR_UNKNOWN, - _("interface name contains an invalid character")); - return FALSE; - } - } - - g_set_error_literal(error, - NM_UTILS_ERROR, - NM_UTILS_ERROR_UNKNOWN, - _("interface name is longer than 15 characters")); - return FALSE; -} - -/*****************************************************************************/ - -static gboolean -_nm_utils_ifname_valid_kernel(const char *name, GError **error) -{ - if (!nm_utils_ifname_valid_kernel(name, error)) - return FALSE; - - if (strchr(name, '%')) { - /* Kernel's dev_valid_name() accepts (almost) any binary up to 15 chars. - * However, '%' is treated special as a format specifier. Try - * - * ip link add 'dummy%dx' type dummy - * - * Don't allow that for "connection.interface-name", which either - * matches an existing netdev name (thus, it cannot have a '%') or - * is used to configure a name (in which case we don't want kernel - * to replace the format specifier). */ - g_set_error_literal(error, - NM_UTILS_ERROR, - NM_UTILS_ERROR_UNKNOWN, - _("'%%' is not allowed in interface names")); - return FALSE; - } - - if (NM_IN_STRSET(name, "all", "default", "bonding_masters")) { - /* Certain names are not allowed. The "all" and "default" names are reserved - * due to their directories in "/proc/sys/net/ipv4/conf/" and "/proc/sys/net/ipv6/conf/". - * - * Also, there is "/sys/class/net/bonding_masters" file. - */ - nm_utils_error_set(error, - NM_UTILS_ERROR_UNKNOWN, - _("'%s' is not allowed as interface name"), - name); - return FALSE; - } - - return TRUE; -} - -static gboolean -_nm_utils_ifname_valid_ovs(const char *name, GError **error) -{ - const char *ch; - - /* OVS actually accepts a wider range of chars (all printable UTF-8 chars), - * NetworkManager restricts this to ASCII char as it's a safer option for - * now since OVS is not well documented on this matter. - **/ - for (ch = name; *ch; ++ch) { - if (*ch == '\\' || *ch == '/' || !g_ascii_isgraph(*ch)) { - g_set_error_literal(error, - NM_UTILS_ERROR, - NM_UTILS_ERROR_UNKNOWN, - _("interface name must be alphanumerical with " - "no forward or backward slashes")); - return FALSE; - } - }; - return TRUE; -} - -gboolean -nm_utils_ifname_valid(const char *name, NMUtilsIfaceType type, GError **error) -{ - g_return_val_if_fail(!error || !(*error), FALSE); - - if (!name || !(name[0])) { - g_set_error_literal(error, - NM_UTILS_ERROR, - NM_UTILS_ERROR_UNKNOWN, - _("interface name must not be empty")); - return FALSE; - } - - if (!g_utf8_validate(name, -1, NULL)) { - g_set_error_literal(error, - NM_UTILS_ERROR, - NM_UTILS_ERROR_UNKNOWN, - _("interface name must be UTF-8 encoded")); - return FALSE; - } - - switch (type) { - case NMU_IFACE_KERNEL: - return _nm_utils_ifname_valid_kernel(name, error); - case NMU_IFACE_OVS: - return _nm_utils_ifname_valid_ovs(name, error); - case NMU_IFACE_OVS_AND_KERNEL: - return _nm_utils_ifname_valid_kernel(name, error) - && _nm_utils_ifname_valid_ovs(name, error); - case NMU_IFACE_ANY: - { - gs_free_error GError *local = NULL; - - if (_nm_utils_ifname_valid_kernel(name, error ? &local : NULL)) - return TRUE; - if (_nm_utils_ifname_valid_ovs(name, NULL)) - return TRUE; - if (error) - g_propagate_error(error, g_steal_pointer(&local)); - return FALSE; - } - } - - g_return_val_if_reached(FALSE); -} - -/*****************************************************************************/ - -void -_nm_str_buf_ensure_size(NMStrBuf *strbuf, gsize new_size, gboolean reserve_exact) -{ - _nm_str_buf_assert(strbuf); - - /* Currently, this only supports strictly growing the buffer. */ - nm_assert(new_size > strbuf->_priv_allocated); - - if (!reserve_exact) { - new_size = nm_utils_get_next_realloc_size(!strbuf->_priv_do_bzero_mem, new_size); - } - - strbuf->_priv_str = nm_secret_mem_realloc(strbuf->_priv_str, - strbuf->_priv_do_bzero_mem, - strbuf->_priv_allocated, - new_size); - strbuf->_priv_allocated = new_size; -} - -void -nm_str_buf_append_printf(NMStrBuf *strbuf, const char *format, ...) -{ - va_list args; - gsize available; - int l; - - _nm_str_buf_assert(strbuf); - - available = strbuf->_priv_allocated - strbuf->_priv_len; - - nm_assert(available < G_MAXULONG); - - va_start(args, format); - l = g_vsnprintf(&strbuf->_priv_str[strbuf->_priv_len], available, format, args); - va_end(args); - - nm_assert(l >= 0); - nm_assert(l < G_MAXINT); - - if ((gsize) l >= available) { - gsize l2; - - if (l == 0) - return; - - l2 = ((gsize) l) + 1u; - - nm_str_buf_maybe_expand(strbuf, l2, FALSE); - - va_start(args, format); - l = g_vsnprintf(&strbuf->_priv_str[strbuf->_priv_len], l2, format, args); - va_end(args); - - nm_assert(l >= 0); - nm_assert((gsize) l == l2 - 1u); - } - - strbuf->_priv_len += (gsize) l; -} - -/*****************************************************************************/ - -/** - * nm_indirect_g_free: - * @arg: a pointer to a pointer that is to be freed. - * - * This does the same as nm_clear_g_free(arg) (g_clear_pointer (arg, g_free)). - * This is for example useful when you have a GArray with pointers and a - * clear function to free them. g_array_set_clear_func()'s destroy notify - * function gets a pointer to the array location, so we have to follow - * the first pointer. - */ -void -nm_indirect_g_free(gpointer arg) -{ - gpointer *p = arg; - - nm_clear_g_free(p); -} - -/*****************************************************************************/ - -static char * -attribute_escape(const char *src, char c1, char c2) -{ - char *ret, *dest; - - dest = ret = g_malloc(strlen(src) * 2 + 1); - - while (*src) { - if (*src == c1 || *src == c2 || *src == '\\') - *dest++ = '\\'; - *dest++ = *src++; - } - *dest++ = '\0'; - - return ret; -} - -void -_nm_utils_format_variant_attributes_full(GString * str, - const NMUtilsNamedValue * values, - guint num_values, - const NMVariantAttributeSpec *const *spec, - char attr_separator, - char key_value_separator) -{ - const NMVariantAttributeSpec *const *s; - const char * name, *value; - GVariant * variant; - char * escaped; - char buf[64]; - char sep = 0; - guint i; - - for (i = 0; i < num_values; i++) { - name = values[i].name; - variant = values[i].value_ptr; - value = NULL; - s = NULL; - - if (spec) { - for (s = spec; *s; s++) { - if (nm_streq0((*s)->name, name)) - break; - } - - if (!*s) - continue; - } - - if (g_variant_is_of_type(variant, G_VARIANT_TYPE_UINT32)) - value = nm_sprintf_buf(buf, "%u", g_variant_get_uint32(variant)); - else if (g_variant_is_of_type(variant, G_VARIANT_TYPE_INT32)) - value = nm_sprintf_buf(buf, "%d", (int) g_variant_get_int32(variant)); - else if (g_variant_is_of_type(variant, G_VARIANT_TYPE_UINT64)) - value = nm_sprintf_buf(buf, "%" G_GUINT64_FORMAT, g_variant_get_uint64(variant)); - else if (g_variant_is_of_type(variant, G_VARIANT_TYPE_BYTE)) - value = nm_sprintf_buf(buf, "%hhu", g_variant_get_byte(variant)); - else if (g_variant_is_of_type(variant, G_VARIANT_TYPE_BOOLEAN)) - value = g_variant_get_boolean(variant) ? "true" : "false"; - else if (g_variant_is_of_type(variant, G_VARIANT_TYPE_STRING)) - value = g_variant_get_string(variant, NULL); - else if (g_variant_is_of_type(variant, G_VARIANT_TYPE_BYTESTRING)) { - /* FIXME: there is no guarantee that the byte array - * is valid UTF-8.*/ - value = g_variant_get_bytestring(variant); - } else - continue; - - if (sep) - g_string_append_c(str, sep); - - escaped = attribute_escape(name, attr_separator, key_value_separator); - g_string_append(str, escaped); - g_free(escaped); - - if (!s || !*s || !(*s)->no_value) { - g_string_append_c(str, key_value_separator); - - escaped = attribute_escape(value, attr_separator, key_value_separator); - g_string_append(str, escaped); - g_free(escaped); - } - - sep = attr_separator; - } -} - -char * -_nm_utils_format_variant_attributes(GHashTable * attributes, - const NMVariantAttributeSpec *const *spec, - char attr_separator, - char key_value_separator) -{ - gs_free NMUtilsNamedValue *values_free = NULL; - NMUtilsNamedValue values_prepared[20]; - const NMUtilsNamedValue * values; - GString * str = NULL; - guint len; - - g_return_val_if_fail(attr_separator, NULL); - g_return_val_if_fail(key_value_separator, NULL); - - if (!attributes) - return NULL; - - values = nm_utils_named_values_from_strdict(attributes, &len, values_prepared, &values_free); - if (len == 0) - return NULL; - - str = g_string_new(""); - _nm_utils_format_variant_attributes_full(str, - values, - len, - spec, - attr_separator, - key_value_separator); - return g_string_free(str, FALSE); -} - -/*****************************************************************************/ - -gboolean -nm_utils_is_localhost(const char *name) -{ - static const char *const NAMES[] = { - "localhost", - "localhost4", - "localhost6", - "localhost.localdomain", - "localhost4.localdomain4", - "localhost6.localdomain6", - }; - gsize name_len; - int i; - - if (!name) - return FALSE; - - /* This tries to identify local host and domain names - * described in RFC6761 plus the redhatism of localdomain. - * - * Similar to systemd's is_localhost(). */ - - name_len = strlen(name); - - if (name_len == 0) - return FALSE; - - if (name[name_len - 1] == '.') { - /* one trailing dot is fine. Hide it. */ - name_len--; - } - - for (i = 0; i < (int) G_N_ELEMENTS(NAMES); i++) { - const char *n = NAMES[i]; - gsize l = strlen(n); - gsize s; - - if (name_len < l) - continue; - - s = name_len - l; - - if (g_ascii_strncasecmp(&name[s], n, l) != 0) - continue; - - /* we accept the name if it is equal to one of the well-known names, - * or if it is some prefix, a '.' and the well-known name. */ - if (s == 0) - return TRUE; - if (name[s - 1] == '.') - return TRUE; - } - - return FALSE; -} - -gboolean -nm_utils_is_specific_hostname(const char *name) -{ - if (nm_str_is_empty(name)) - return FALSE; - - if (nm_streq(name, "(none)")) { - /* This is not a special hostname. Probably an artefact by somebody wrongly - * printing NULL. */ - return FALSE; - } - - if (nm_utils_is_localhost(name)) - return FALSE; - - /* FIXME: properly validate the hostname, like systemd's hostname_is_valid() */ - - return TRUE; -} - -/*****************************************************************************/ - -/* taken from systemd's uid_to_name(). */ -char * -nm_utils_uid_to_name(uid_t uid) -{ - gs_free char *buf_heap = NULL; - char buf_stack[4096]; - gsize bufsize; - char * buf; - - bufsize = sizeof(buf_stack); - buf = buf_stack; - - for (;;) { - struct passwd pwbuf; - struct passwd *pw = NULL; - int r; - - r = getpwuid_r(uid, &pwbuf, buf, bufsize, &pw); - if (r == 0 && pw) - return nm_strdup_not_empty(pw->pw_name); - - if (r != ERANGE) - return NULL; - - if (bufsize > G_MAXSIZE / 2u) - return NULL; - - bufsize *= 2u; - g_free(buf_heap); - buf_heap = g_malloc(bufsize); - buf = buf_heap; - } -} - -/* taken from systemd's nss_user_record_by_name() */ -gboolean -nm_utils_name_to_uid(const char *name, uid_t *out_uid) -{ - gs_free char *buf_heap = NULL; - char buf_stack[4096]; - gsize bufsize; - char * buf; - - if (!name) - return nm_assert_unreachable_val(FALSE); - - bufsize = sizeof(buf_stack); - buf = buf_stack; - - for (;;) { - struct passwd *result; - struct passwd pwd; - int r; - - r = getpwnam_r(name, &pwd, buf, bufsize, &result); - if (r == 0) { - if (!result) - return FALSE; - NM_SET_OUT(out_uid, pwd.pw_uid); - return TRUE; - } - - if (r != ERANGE) - return FALSE; - - if (bufsize > G_MAXSIZE / 2u) - return FALSE; - - bufsize *= 2u; - g_free(buf_heap); - buf_heap = g_malloc(bufsize); - buf = buf_heap; - } -} - -/*****************************************************************************/ - -typedef struct { - CList lst; - gpointer tls_data; - GDestroyNotify destroy_notify; -} TlsRegData; - -static pthread_key_t _tls_reg_key; - -static void -_tls_reg_destroy(gpointer data) -{ - CList * lst_head = data; - TlsRegData *entry; - - if (!lst_head) - return; - - /* For no strong reason are we destroying the elements in reverse - * order than they were added. It seems a bit more sensible (but shouldn't - * matter nor should you rely on that). */ - while ((entry = c_list_last_entry(lst_head, TlsRegData, lst))) { - c_list_unlink_stale(&entry->lst); - entry->destroy_notify(entry->tls_data); - nm_g_slice_free(entry); - } - - nm_g_slice_free(lst_head); -} - -static void -_tls_reg_make_key(void) -{ - if (pthread_key_create(&_tls_reg_key, _tls_reg_destroy) != 0) - g_return_if_reached(); -} - -/** - * nm_utils_thread_local_register_destroy: - * @tls_data: the thread local storage data that should be destroyed when the thread - * exits. This pointer will be "owned" by the current thread. There is no way - * to un-register the destruction. - * @destroy_notify: the free function that will be called when the thread exits. - * - * If _nm_tread_local storage is heap allocated it requires freeing the pointer - * when the thread exits. Use this function to register the pointer to be - * released. - * - * This function does not change errno. - */ -void -nm_utils_thread_local_register_destroy(gpointer tls_data, GDestroyNotify destroy_notify) -{ - NM_AUTO_PROTECT_ERRNO(errsv); - static pthread_once_t key_once = PTHREAD_ONCE_INIT; - CList * lst_head; - TlsRegData * entry; - - nm_assert(destroy_notify); - - if (pthread_once(&key_once, _tls_reg_make_key) != 0) - g_return_if_reached(); - - if ((lst_head = pthread_getspecific(_tls_reg_key)) == NULL) { - lst_head = g_slice_new(CList); - c_list_init(lst_head); - if (pthread_setspecific(_tls_reg_key, lst_head) != 0) - g_return_if_reached(); - } - - entry = g_slice_new(TlsRegData); - entry->tls_data = tls_data; - entry->destroy_notify = destroy_notify; - c_list_link_tail(lst_head, &entry->lst); -} diff --git a/shared/nm-glib-aux/nm-shared-utils.h b/shared/nm-glib-aux/nm-shared-utils.h deleted file mode 100644 index f9c21a00..00000000 --- a/shared/nm-glib-aux/nm-shared-utils.h +++ /dev/null @@ -1,2484 +0,0 @@ -/* SPDX-License-Identifier: LGPL-2.1-or-later */ -/* - * Copyright (C) 2016 Red Hat, Inc. - */ - -#ifndef __NM_SHARED_UTILS_H__ -#define __NM_SHARED_UTILS_H__ - -#include <netinet/in.h> - -/*****************************************************************************/ - -/* An optional boolean (like NMTernary, with identical numerical - * enum values). Note that this enum type is _nm_packed! */ -typedef enum _nm_packed { - NM_OPTION_BOOL_DEFAULT = -1, - NM_OPTION_BOOL_FALSE = 0, - NM_OPTION_BOOL_TRUE = 1, -} NMOptionBool; - -/*****************************************************************************/ - -static inline gboolean -nm_is_ascii(char ch) -{ - return ((uint8_t) ch) < 128; -} - -/*****************************************************************************/ - -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 - -/*****************************************************************************/ - -typedef struct { - guint8 ether_addr_octet[6 /*ETH_ALEN*/]; -} NMEtherAddr; - -#define NM_ETHER_ADDR_FORMAT_STR "%02X:%02X:%02X:%02X:%02X:%02X" - -#define NM_ETHER_ADDR_FORMAT_VAL(x) \ - (x)->ether_addr_octet[0], (x)->ether_addr_octet[1], (x)->ether_addr_octet[2], \ - (x)->ether_addr_octet[3], (x)->ether_addr_octet[4], (x)->ether_addr_octet[5] - -#define _NM_ETHER_ADDR_INIT(a0, a1, a2, a3, a4, a5) \ - { \ - .ether_addr_octet = { \ - (a0), \ - (a1), \ - (a2), \ - (a3), \ - (a4), \ - (a5), \ - }, \ - } - -#define NM_ETHER_ADDR_INIT(...) ((NMEtherAddr) _NM_ETHER_ADDR_INIT(__VA_ARGS__)) - -static inline int -nm_ether_addr_cmp(const NMEtherAddr *a, const NMEtherAddr *b) -{ - NM_CMP_SELF(a, b); - NM_CMP_DIRECT_MEMCMP(a, b, sizeof(NMEtherAddr)); - return 0; -} - -static inline gboolean -nm_ether_addr_equal(const NMEtherAddr *a, const NMEtherAddr *b) -{ - return nm_ether_addr_cmp(a, b) == 0; -} - -/*****************************************************************************/ - -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 ether_addr_octet[6 /*ETH_ALEN*/]; - NMEtherAddr ether_addr; - - guint8 array[sizeof(struct in6_addr)]; - }; -} NMIPAddr; - -#define NM_IP_ADDR_INIT \ - { \ - .array = { 0 } \ - } - -extern const NMIPAddr nm_ip_addr_zero; - -#define nm_ether_addr_zero (nm_ip_addr_zero.ether_addr) - -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); - -/*****************************************************************************/ - -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); - -GBytes *nm_g_bytes_new_from_str(const char *str); - -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); - -GHashTable *nm_utils_strdict_clone(GHashTable *src); - -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 gpointer -nm_copy_func_g_strdup(gconstpointer arg, gpointer user_data) -{ - return g_strdup(arg); -} - -/*****************************************************************************/ - -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); -} - -/** - * nm_utils_escaped_tokens_escape_unnecessary: - * @str: the string to check for "escape" - * @delimiters: the delimiters - * - * This asserts that calling nm_utils_escaped_tokens_escape() - * on @str has no effect and returns @str directly. This is only - * for asserting that @str is safe to not require any escaping. - * - * Returns: @str - */ -static inline const char * -nm_utils_escaped_tokens_escape_unnecessary(const char *str, const char *delimiters) -{ -#if NM_MORE_ASSERTS > 0 - - nm_assert(str); - 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 - - return str; -} - -static inline void -nm_utils_escaped_tokens_escape_gstr_assert(const char *str, - const char *delimiters, - GString * gstring) -{ - g_string_append(gstring, nm_utils_escaped_tokens_escape_unnecessary(str, delimiters)); -} - -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_prefix0(in_addr_t ip); -guint32 _nm_utils_ip4_get_default_prefix(in_addr_t 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); - -gboolean nm_utils_parse_next_line(const char **inout_ptr, - gsize * inout_len, - const char **out_line, - gsize * out_line_len); - -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); \ - } \ - _NM_DUMMY_STRUCT_FOR_TRAILING_SEMICOLON - -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; \ - } \ - _NM_DUMMY_STRUCT_FOR_TRAILING_SEMICOLON - -#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 asynchronous 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_NOT_READY: the failure is related to being currently - * not ready to perform the operation. - * - * @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 >*/ - NM_UTILS_ERROR_NOT_READY, /*< nick=NotReady >*/ - - /* 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); - -GQuark nm_manager_error_quark(void); -#define _NM_MANAGER_ERROR (nm_manager_error_quark()) - -#define _NM_MANAGER_ERROR_UNKNOWN_LOG_LEVEL 10 -#define _NM_MANAGER_ERROR_UNKNOWN_LOG_DOMAIN 11 - -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); - -GVariant *nm_g_variant_singleton_u_0(void); - -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 GVariant * -nm_g_variant_new_ay_inaddr(int addr_family, gconstpointer addr) -{ - return g_variant_new_fixed_array(G_VARIANT_TYPE_BYTE, - addr ?: &nm_ip_addr_zero, - nm_utils_addr_family_to_size(addr_family), - 1); -} - -static inline GVariant * -nm_g_variant_new_ay_in4addr(in_addr_t addr) -{ - return nm_g_variant_new_ay_inaddr(AF_INET, &addr); -} - -static inline GVariant * -nm_g_variant_new_ay_in6addr(const struct in6_addr *addr) -{ - return nm_g_variant_new_ay_inaddr(AF_INET6, addr); -} - -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_timeout_source_new_seconds(guint timeout_sec, - 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; -} - -static inline GSource * -nm_g_idle_add_source(GSourceFunc func, gpointer user_data) -{ - /* G convenience function to attach a new timeout source to the default GMainContext. - * In that sense it's very similar to g_idle_add() except that it returns a - * reference to the new source. */ - return nm_g_source_attach(nm_g_idle_source_new(G_PRIORITY_DEFAULT, func, user_data, NULL), - NULL); -} - -static inline GSource * -nm_g_timeout_add_source(guint timeout_msec, GSourceFunc func, gpointer user_data) -{ - /* G convenience function to attach a new timeout source to the default GMainContext. - * In that sense it's very similar to g_timeout_add() except that it returns a - * reference to the new source. */ - return nm_g_source_attach( - nm_g_timeout_source_new(timeout_msec, G_PRIORITY_DEFAULT, func, user_data, NULL), - NULL); -} - -static inline GSource * -nm_g_timeout_add_source_seconds(guint timeout_sec, GSourceFunc func, gpointer user_data) -{ - /* G convenience function to attach a new timeout source to the default GMainContext. - * In that sense it's very similar to g_timeout_add_seconds() except that it returns a - * reference to the new source. */ - return nm_g_source_attach( - nm_g_timeout_source_new_seconds(timeout_sec, G_PRIORITY_DEFAULT, func, user_data, NULL), - NULL); -} - -static inline GSource * -nm_g_timeout_add_source_approx(guint timeout_msec, - guint timeout_sec_threshold, - GSourceFunc func, - gpointer user_data) -{ - GSource *source; - - /* If timeout_msec is larger or equal than a threshold, then we use g_timeout_source_new_seconds() - * instead. */ - if (timeout_msec / 1000u >= timeout_sec_threshold) - source = nm_g_timeout_source_new_seconds(timeout_msec / 1000u, - G_PRIORITY_DEFAULT, - func, - user_data, - NULL); - else - source = nm_g_timeout_source_new(timeout_msec, G_PRIORITY_DEFAULT, func, user_data, NULL); - return nm_g_source_attach(source, NULL); -} - -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_strcmp_ascii_case_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); - -void nm_utils_named_value_clear_with_g_free(NMUtilsNamedValue *val); - -/*****************************************************************************/ - -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_equal(const GHashTable *a, - const GHashTable *b, - gboolean treat_null_as_empty, - GEqualFunc equal_func); - -gboolean nm_utils_hashtable_cmp_equal(const GHashTable *a, - const GHashTable *b, - GCompareDataFunc cmp_values, - gpointer user_data); - -static inline gboolean -nm_utils_hashtable_same_keys(const GHashTable *a, const GHashTable *b) -{ - return nm_utils_hashtable_cmp_equal(a, b, NULL, NULL); -} - -int nm_utils_hashtable_cmp(const GHashTable *a, - const GHashTable *b, - gboolean do_fast_precheck, - GCompareDataFunc cmp_keys, - GCompareDataFunc cmp_values, - gpointer user_data); - -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(const char *const *strv, gssize len, gboolean deep_copied); - -#define nm_utils_strv_dup(strv, len, deep_copied) \ - _nm_utils_strv_dup(NM_CAST_STRV_CC(strv), (len), (deep_copied)) - -const char **_nm_utils_strv_dup_packed(const char *const *strv, gssize len); - -#define nm_utils_strv_dup_packed(strv, len) _nm_utils_strv_dup_packed(NM_CAST_STRV_CC(strv), (len)) - -/*****************************************************************************/ - -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 void -nm_g_array_unref(GArray *arr) -{ - if (arr) - g_array_unref(arr); -} - -#define nm_g_array_append_new(arr, type) \ - ({ \ - GArray *const _arr = (arr); \ - guint _len; \ - \ - nm_assert(_arr); \ - _len = _arr->len; \ - nm_assert(_len < G_MAXUINT); \ - g_array_set_size(_arr, _len + 1u); \ - &g_array_index(arr, type, _len); \ - }) - -/*****************************************************************************/ - -static inline GPtrArray * -nm_g_ptr_array_ref(GPtrArray *arr) -{ - return arr ? g_ptr_array_ref(arr) : NULL; -} - -static inline void -nm_g_ptr_array_unref(GPtrArray *arr) -{ - if (arr) - g_ptr_array_unref(arr); -} - -#define nm_g_ptr_array_set(pdst, val) \ - ({ \ - GPtrArray **_pdst = (pdst); \ - GPtrArray * _val = (val); \ - gboolean _changed = FALSE; \ - \ - nm_assert(_pdst); \ - \ - if (*_pdst != _val) { \ - _nm_unused gs_unref_ptrarray GPtrArray *_old = *_pdst; \ - \ - *_pdst = nm_g_ptr_array_ref(_val); \ - _changed = TRUE; \ - } \ - _changed; \ - }) - -#define nm_g_ptr_array_set_take(pdst, val) \ - ({ \ - GPtrArray **_pdst = (pdst); \ - GPtrArray * _val = (val); \ - gboolean _changed = FALSE; \ - \ - nm_assert(_pdst); \ - \ - if (*_pdst != _val) { \ - _nm_unused gs_unref_ptrarray GPtrArray *_old = *_pdst; \ - \ - *_pdst = _val; \ - _changed = TRUE; \ - } else { \ - nm_g_ptr_array_unref(_val); \ - } \ - _changed; \ - }) - -static inline guint -nm_g_ptr_array_len(const GPtrArray *arr) -{ - return arr ? arr->len : 0u; -} - -static inline gpointer * -nm_g_ptr_array_pdata(const GPtrArray *arr) -{ - return arr ? arr->pdata : NULL; -} - -GPtrArray *_nm_g_ptr_array_copy(GPtrArray * array, - GCopyFunc func, - gpointer user_data, - GDestroyNotify element_free_func); - -/** - * nm_g_ptr_array_copy: - * @array: the #GPtrArray to clone. - * @func: the copy function. - * @user_data: the user data for the copy function - * @element_free_func: the free function of the elements. @array MUST have - * the same element_free_func. This argument is only used on older - * glib, that doesn't support g_ptr_array_copy(). - * - * This is a replacement for g_ptr_array_copy(), which is not available - * before glib 2.62. Since GPtrArray does not allow to access the internal - * element_free_func, we cannot add a compatibility implementation of g_ptr_array_copy() - * and the user must provide a suitable destroy function. - * - * Note that the @element_free_func MUST correspond to free function set in @array. - */ -#if GLIB_CHECK_VERSION(2, 62, 0) - #define nm_g_ptr_array_copy(array, func, user_data, element_free_func) \ - ({ \ - _nm_unused GDestroyNotify const _element_free_func = (element_free_func); \ - \ - G_GNUC_BEGIN_IGNORE_DEPRECATIONS; \ - g_ptr_array_copy((array), (func), (user_data)); \ - G_GNUC_END_IGNORE_DEPRECATIONS; \ - }) -#else - #define nm_g_ptr_array_copy(array, func, user_data, element_free_func) \ - _nm_g_ptr_array_copy((array), (func), (user_data), (element_free_func)) -#endif - -/*****************************************************************************/ - -static inline GHashTable * -nm_g_hash_table_ref(GHashTable *hash) -{ - return hash ? g_hash_table_ref(hash) : NULL; -} - -static inline void -nm_g_hash_table_unref(GHashTable *hash) -{ - if (hash) - g_hash_table_unref(hash); -} - -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_contains(GHashTable *hash, gconstpointer key) -{ - return hash ? g_hash_table_contains(hash, key) : FALSE; -} - -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); - -/*****************************************************************************/ - -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); - -#define nm_utils_strv_cmp_n(strv1, len1, strv2, len2) \ - _nm_utils_strv_cmp_n(NM_CAST_STRV_CC(strv1), (len1), NM_CAST_STRV_CC(strv2), (len2)) - -#define nm_utils_strv_equal(strv1, strv2) (nm_utils_strv_cmp_n((strv1), -1, (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 process is effectively dead (or a zombie). - */ - return NM_IN_SET(pstate, 'Z', 'x', 'X'); -} - -/*****************************************************************************/ - -typedef struct _NMUtilsUserData NMUtilsUserData; - -NMUtilsUserData *_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(NMUtilsUserData *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 GPtrArray * -nm_strv_ptrarray_ensure(GPtrArray **p_arr) -{ - nm_assert(p_arr); - - if (G_UNLIKELY(!*p_arr)) - *p_arr = g_ptr_array_new_with_free_func(g_free); - - return *p_arr; -} - -static inline const char *const * -nm_strv_ptrarray_get_unsafe(GPtrArray *arr, guint *out_len) -{ - /* warning: the GPtrArray is not NULL terminated. So, it - * isn't really a strv array (sorry the misnomer). That's why - * the function is potentially "unsafe" and you must provide a - * out_len parameter. */ - if (!arr || arr->len == 0) { - *out_len = 0; - return NULL; - } - *out_len = arr->len; - return (const char *const *) arr->pdata; -} - -static inline GPtrArray * -nm_strv_ptrarray_clone(const GPtrArray *src, gboolean null_if_empty) -{ - if (!src || (null_if_empty && src->len == 0)) - return NULL; - return nm_g_ptr_array_copy((GPtrArray *) src, nm_copy_func_g_strdup, NULL, g_free); -} - -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)); -} - -static inline gssize -nm_strv_ptrarray_find_first(const GPtrArray *strv, const char *str) -{ - if (!strv) - return -1; - return nm_utils_strv_find_first((char **) strv->pdata, strv->len, str); -} - -static inline gboolean -nm_strv_ptrarray_contains(const GPtrArray *strv, const char *str) -{ - return nm_strv_ptrarray_find_first(strv, str) >= 0; -} - -static inline int -nm_strv_ptrarray_cmp(const GPtrArray *a, const GPtrArray *b) -{ - /* nm_utils_strv_cmp_n() will treat NULL and empty arrays the same. - * That means, an empty strv array can both be represented by NULL - * and an array of length zero. - * If you need to distinguish between these case, do that yourself. */ - return nm_utils_strv_cmp_n((const char *const *) nm_g_ptr_array_pdata(a), - nm_g_ptr_array_len(a), - (const char *const *) nm_g_ptr_array_pdata(b), - nm_g_ptr_array_len(b)); -} - -/*****************************************************************************/ - -int nm_utils_getpagesize(void); - -/*****************************************************************************/ - -extern const char _nm_hexchar_table_lower[16]; -extern const char _nm_hexchar_table_upper[16]; - -static inline char -nm_hexchar(int x, gboolean upper_case) -{ - return upper_case ? _nm_hexchar_table_upper[x & 15] : _nm_hexchar_table_lower[x & 15]; -} - -char *nm_utils_bin2hexstr_full(gconstpointer addr, - gsize length, - char delimiter, - gboolean upper_case, - char * out); - -#define nm_utils_bin2hexstr_a(addr, length, delimiter, upper_case, str_to_free) \ - ({ \ - gconstpointer _addr = (addr); \ - gsize _length = (length); \ - char _delimiter = (delimiter); \ - char ** _str_to_free = (str_to_free); \ - char * _s; \ - gsize _s_len; \ - \ - nm_assert(_str_to_free); \ - \ - _s_len = _length == 0 ? 1u : (_delimiter == '\0' ? _length * 2u + 1u : _length * 3u); \ - if (_s_len < 100) \ - _s = g_alloca(_s_len); \ - else { \ - _s = g_malloc(_s_len); \ - *_str_to_free = _s; \ - } \ - nm_utils_bin2hexstr_full(_addr, _length, _delimiter, (upper_case), _s); \ - }) - -static inline const char * -nm_ether_addr_to_string(const NMEtherAddr *ether_addr, char sbuf[static(sizeof(NMEtherAddr) * 3)]) -{ - nm_assert(ether_addr); - nm_assert(sbuf); - - return nm_utils_bin2hexstr_full(ether_addr, sizeof(NMEtherAddr), ':', TRUE, sbuf); -} - -#define nm_ether_addr_to_string_a(ether_addr) \ - nm_ether_addr_to_string((ether_addr), g_alloca(sizeof(NMEtherAddr) * 3)) - -guint8 *nm_utils_hexstr2bin_full(const char *hexstr, - gboolean allow_0x_prefix, - gboolean delimiter_required, - gboolean hexdigit_pairs_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), \ - FALSE, \ - (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); - -/** - * _nm_utils_hwaddr_aton: - * @asc: the ASCII representation of a hardware address - * @buffer: buffer to store the result into. Must have - * at least a size of @buffer_length. - * @buffer_length: the length of the input buffer @buffer. - * The result must fit into that buffer, otherwise - * the function fails and returns %NULL. - * @out_length: the output length in case of success. - * - * Parses @asc and converts it to binary form in @buffer. - * Bytes in @asc can be separated by colons (:), or hyphens (-), but not mixed. - * - * It is like nm_utils_hwaddr_aton(), but contrary to that it - * can parse addresses of any length. That is, you don't need - * to know the length before-hand. - * - * Return value: @buffer, or %NULL if @asc couldn't be parsed. - */ -static inline guint8 * -_nm_utils_hwaddr_aton(const char *asc, gpointer buffer, gsize buffer_length, gsize *out_length) -{ - g_return_val_if_fail(asc, NULL); - g_return_val_if_fail(buffer, NULL); - g_return_val_if_fail(buffer_length > 0, NULL); - g_return_val_if_fail(out_length, NULL); - - return nm_utils_hexstr2bin_full(asc, - FALSE, - TRUE, - FALSE, - ":-", - 0, - buffer, - buffer_length, - out_length); -} - -static inline guint8 * -_nm_utils_hwaddr_aton_exact(const char *asc, gpointer buffer, gsize buffer_length) -{ - g_return_val_if_fail(asc, NULL); - g_return_val_if_fail(buffer, NULL); - g_return_val_if_fail(buffer_length > 0, NULL); - - return nm_utils_hexstr2bin_full(asc, - FALSE, - TRUE, - FALSE, - ":-", - buffer_length, - buffer, - buffer_length, - NULL); -} - -static inline const char * -_nm_utils_hwaddr_ntoa(gconstpointer addr, - gsize addr_len, - gboolean upper_case, - char * buf, - gsize buf_len) -{ - g_return_val_if_fail(addr, NULL); - g_return_val_if_fail(addr_len > 0, NULL); - g_return_val_if_fail(buf, NULL); - if (buf_len < addr_len * 3) - g_return_val_if_reached(NULL); - - return nm_utils_bin2hexstr_full(addr, addr_len, ':', upper_case, buf); -} - -/*****************************************************************************/ - -#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; \ - } \ - } \ - _NM_DUMMY_STRUCT_FOR_TRAILING_SEMICOLON - -#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) -{ - char *old; - - nm_assert(dst); - - if (nm_streq0(*dst, src)) - return FALSE; - old = *dst; - *dst = g_strdup(src); - g_free(old); - return TRUE; -} - -static inline gboolean -nm_utils_strdup_reset_take(char **dst, char *src) -{ - char *old; - - nm_assert(dst); - nm_assert(src != *dst); - - if (nm_streq0(*dst, src)) { - if (src) - g_free(src); - return FALSE; - } - old = *dst; - *dst = src; - g_free(old); - return TRUE; -} - -void nm_indirect_g_free(gpointer arg); - -/*****************************************************************************/ - -void nm_utils_ifname_cpy(char *dst, const char *name); - -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]); -} - -static inline gboolean -nm_strvarray_remove_first(GArray *strv, const char *needle) -{ - guint i; - - nm_assert(needle); - - if (strv) { - for (i = 0; i < strv->len; i++) { - if (nm_streq(needle, g_array_index(strv, const char *, i))) { - g_array_remove_index(strv, i); - return TRUE; - } - } - } - return FALSE; -} - -/*****************************************************************************/ - -struct _NMVariantAttributeSpec { - char * name; - const GVariantType *type; - bool v4 : 1; - bool v6 : 1; - bool no_value : 1; - bool consumes_rest : 1; - char str_type; -}; - -typedef struct _NMVariantAttributeSpec NMVariantAttributeSpec; - -void _nm_utils_format_variant_attributes_full(GString * str, - const NMUtilsNamedValue * values, - guint num_values, - const NMVariantAttributeSpec *const *spec, - char attr_separator, - char key_value_separator); - -char *_nm_utils_format_variant_attributes(GHashTable * attributes, - const NMVariantAttributeSpec *const *spec, - char attr_separator, - char key_value_separator); - -/*****************************************************************************/ - -gboolean nm_utils_is_localhost(const char *name); - -gboolean nm_utils_is_specific_hostname(const char *name); - -char * nm_utils_uid_to_name(uid_t uid); -gboolean nm_utils_name_to_uid(const char *name, uid_t *out_uid); - -/*****************************************************************************/ - -void nm_utils_thread_local_register_destroy(gpointer tls_data, GDestroyNotify destroy_notify); - -#endif /* __NM_SHARED_UTILS_H__ */ diff --git a/shared/nm-glib-aux/nm-str-buf.h b/shared/nm-glib-aux/nm-str-buf.h deleted file mode 100644 index cb0d3fb1..00000000 --- a/shared/nm-glib-aux/nm-str-buf.h +++ /dev/null @@ -1,488 +0,0 @@ -/* SPDX-License-Identifier: LGPL-2.1-or-later */ - -#ifndef __NM_STR_BUF_H__ -#define __NM_STR_BUF_H__ - -#include "nm-shared-utils.h" -#include "nm-secret-utils.h" - -/*****************************************************************************/ - -/* NMStrBuf is not unlike GString. The main difference is that it can use - * nm_explicit_bzero() when growing the buffer. */ -typedef struct _NMStrBuf { - char *_priv_str; - - /* The unions only exist because we allow/encourage read-only access - * to the "len" and "allocated" fields, but modifying the fields is - * only allowed to the NMStrBuf implementation itself. */ - union { - /*const*/ gsize len; - gsize _priv_len; - }; - union { - /*const*/ gsize allocated; - gsize _priv_allocated; - }; - - bool _priv_do_bzero_mem; -} NMStrBuf; - -/*****************************************************************************/ - -static inline void -_nm_str_buf_assert(const NMStrBuf *strbuf) -{ - nm_assert(strbuf); - nm_assert((!!strbuf->_priv_str) == (strbuf->_priv_allocated > 0)); - nm_assert(strbuf->_priv_len <= strbuf->_priv_allocated); -} - -static inline NMStrBuf -NM_STR_BUF_INIT(gsize allocated, gboolean do_bzero_mem) -{ - NMStrBuf strbuf = { - ._priv_str = allocated ? g_malloc(allocated) : NULL, - ._priv_allocated = allocated, - ._priv_len = 0, - ._priv_do_bzero_mem = do_bzero_mem, - }; - - return strbuf; -} - -static inline void -nm_str_buf_init(NMStrBuf *strbuf, gsize len, bool do_bzero_mem) -{ - nm_assert(strbuf); - *strbuf = NM_STR_BUF_INIT(len, do_bzero_mem); - _nm_str_buf_assert(strbuf); -} - -void _nm_str_buf_ensure_size(NMStrBuf *strbuf, gsize new_size, gboolean reserve_exact); - -static inline void -nm_str_buf_maybe_expand(NMStrBuf *strbuf, gsize reserve, gboolean reserve_exact) -{ - _nm_str_buf_assert(strbuf); - nm_assert(strbuf->_priv_len < G_MAXSIZE - reserve); - - /* @reserve is the extra space that we require. */ - if (G_UNLIKELY(reserve > strbuf->_priv_allocated - strbuf->_priv_len)) - _nm_str_buf_ensure_size(strbuf, strbuf->_priv_len + reserve, reserve_exact); -} - -/*****************************************************************************/ - -/** - * nm_str_buf_set_size: - * @strbuf: the initialized #NMStrBuf - * @new_len: the new length - * @honor_do_bzero_mem: if %TRUE, the shrunk memory will be cleared, if - * do_bzero_mem is set. This should be usually set to %TRUE, unless - * you know that the shrunk memory does not contain data that requires to be - * cleared. When growing the size, this value has no effect. - * @reserve_exact: when growing the buffer, reserve the exact amount of bytes. - * If %FALSE, the buffer may allocate more memory than requested to grow - * exponentially. - * - * This is like g_string_set_size(). If new_len is smaller than the - * current length, the string gets truncated (excess memory will be cleared). - * - * When extending the length, the added bytes are undefined (like with - * g_string_set_size(). Likewise, if you first pre-allocate a buffer with - * nm_str_buf_maybe_expand(), then write to the bytes, and finally set - * the appropriate size, then that works as expected (by not clearing the - * pre-existing, grown buffer). - */ -static inline void -nm_str_buf_set_size(NMStrBuf *strbuf, - gsize new_len, - gboolean honor_do_bzero_mem, - gboolean reserve_exact) -{ - _nm_str_buf_assert(strbuf); - - if (new_len < strbuf->_priv_len) { - if (honor_do_bzero_mem && strbuf->_priv_do_bzero_mem) { - /* we only clear the memory that we wrote to. */ - nm_explicit_bzero(&strbuf->_priv_str[new_len], strbuf->_priv_len - new_len); - } - } else if (new_len > strbuf->_priv_len) { - nm_str_buf_maybe_expand(strbuf, - new_len - strbuf->_priv_len + (reserve_exact ? 0u : 1u), - reserve_exact); - } else - return; - - strbuf->_priv_len = new_len; -} - -/*****************************************************************************/ - -static inline void -nm_str_buf_erase(NMStrBuf *strbuf, gsize pos, gssize len, gboolean honor_do_bzero_mem) -{ - gsize new_len; - - _nm_str_buf_assert(strbuf); - - nm_assert(pos <= strbuf->_priv_len); - - if (len == 0) - return; - - if (len < 0) { - /* truncate the string before pos */ - nm_assert(len == -1); - new_len = pos; - } else { - gsize l = len; - - nm_assert(l <= strbuf->_priv_len - pos); - - new_len = strbuf->_priv_len - l; - if (pos + l < strbuf->_priv_len) { - memmove(&strbuf->_priv_str[pos], - &strbuf->_priv_str[pos + l], - strbuf->_priv_len - (pos + l)); - } - } - - nm_assert(new_len <= strbuf->_priv_len); - nm_str_buf_set_size(strbuf, new_len, honor_do_bzero_mem, TRUE); -} - -/*****************************************************************************/ - -static inline void -nm_str_buf_append_c_repeated(NMStrBuf *strbuf, char ch, guint len) -{ - if (len > 0) { - nm_str_buf_maybe_expand(strbuf, len + 1, FALSE); - do { - strbuf->_priv_str[strbuf->_priv_len++] = ch; - } while (--len > 0); - } -} - -static inline void -nm_str_buf_append_c(NMStrBuf *strbuf, char ch) -{ - nm_str_buf_maybe_expand(strbuf, 2, FALSE); - strbuf->_priv_str[strbuf->_priv_len++] = ch; -} - -static inline void -nm_str_buf_append_c2(NMStrBuf *strbuf, char ch0, char ch1) -{ - nm_str_buf_maybe_expand(strbuf, 3, FALSE); - strbuf->_priv_str[strbuf->_priv_len++] = ch0; - strbuf->_priv_str[strbuf->_priv_len++] = ch1; -} - -static inline void -nm_str_buf_append_c4(NMStrBuf *strbuf, char ch0, char ch1, char ch2, char ch3) -{ - nm_str_buf_maybe_expand(strbuf, 5, FALSE); - strbuf->_priv_str[strbuf->_priv_len++] = ch0; - strbuf->_priv_str[strbuf->_priv_len++] = ch1; - strbuf->_priv_str[strbuf->_priv_len++] = ch2; - strbuf->_priv_str[strbuf->_priv_len++] = ch3; -} - -static inline void -nm_str_buf_append_c_hex(NMStrBuf *strbuf, char ch, gboolean upper_case) -{ - nm_str_buf_maybe_expand(strbuf, 3, FALSE); - strbuf->_priv_str[strbuf->_priv_len++] = nm_hexchar(((guchar) ch) >> 4, upper_case); - strbuf->_priv_str[strbuf->_priv_len++] = nm_hexchar((guchar) ch, upper_case); -} - -static inline void -nm_str_buf_append_len(NMStrBuf *strbuf, const char *str, gsize len) -{ - _nm_str_buf_assert(strbuf); - - if (len > 0) { - nm_str_buf_maybe_expand(strbuf, len + 1, FALSE); - memcpy(&strbuf->_priv_str[strbuf->_priv_len], str, len); - strbuf->_priv_len += len; - } -} - -static inline char * -nm_str_buf_append_len0(NMStrBuf *strbuf, const char *str, gsize len) -{ - _nm_str_buf_assert(strbuf); - - /* this is basically like nm_str_buf_append_len() and - * nm_str_buf_get_str() in one. */ - - nm_str_buf_maybe_expand(strbuf, len + 1u, FALSE); - if (len > 0) { - memcpy(&strbuf->_priv_str[strbuf->_priv_len], str, len); - strbuf->_priv_len += len; - } - strbuf->_priv_str[strbuf->_priv_len] = '\0'; - return strbuf->_priv_str; -} - -static inline void -nm_str_buf_append(NMStrBuf *strbuf, const char *str) -{ - nm_assert(str); - - nm_str_buf_append_len(strbuf, str, strlen(str)); -} - -static inline char * -nm_str_buf_append0(NMStrBuf *strbuf, const char *str) -{ - nm_assert(str); - - return nm_str_buf_append_len0(strbuf, str, strlen(str)); -} - -void nm_str_buf_append_printf(NMStrBuf *strbuf, const char *format, ...) _nm_printf(2, 3); - -static inline void -nm_str_buf_ensure_trailing_c(NMStrBuf *strbuf, char ch) -{ - _nm_str_buf_assert(strbuf); - - if (strbuf->_priv_len == 0 || strbuf->_priv_str[strbuf->_priv_len - 1] != ch) - nm_str_buf_append_c(strbuf, ch); -} - -static inline NMStrBuf * -nm_str_buf_append_required_delimiter(NMStrBuf *strbuf, char delimiter) -{ - _nm_str_buf_assert(strbuf); - - /* appends the @delimiter if it is required (that is, if the - * string is not empty). */ - if (strbuf->len > 0) - nm_str_buf_append_c(strbuf, delimiter); - return strbuf; -} - -static inline void -nm_str_buf_append_dirty(NMStrBuf *strbuf, gsize len) -{ - _nm_str_buf_assert(strbuf); - - /* this append @len bytes to the buffer, but it does not - * initialize them! */ - if (len > 0) { - nm_str_buf_maybe_expand(strbuf, len, FALSE); - strbuf->_priv_len += len; - } -} - -static inline void -nm_str_buf_append_c_len(NMStrBuf *strbuf, char ch, gsize len) -{ - _nm_str_buf_assert(strbuf); - - if (len > 0) { - nm_str_buf_maybe_expand(strbuf, len, FALSE); - memset(&strbuf->_priv_str[strbuf->_priv_len], ch, len); - strbuf->_priv_len += len; - } -} - -/*****************************************************************************/ - -static inline NMStrBuf * -nm_str_buf_reset(NMStrBuf *strbuf) -{ - _nm_str_buf_assert(strbuf); - - if (strbuf->_priv_len > 0) { - if (strbuf->_priv_do_bzero_mem) { - /* we only clear the memory that we wrote to. */ - nm_explicit_bzero(strbuf->_priv_str, strbuf->_priv_len); - } - strbuf->_priv_len = 0; - } - - return strbuf; -} - -/*****************************************************************************/ - -/* Calls nm_utils_escaped_tokens_escape() on @str and appends the - * result to @strbuf. */ -static inline void -nm_utils_escaped_tokens_escape_strbuf(const char *str, const char *delimiters, NMStrBuf *strbuf) -{ - gs_free char *str_to_free = NULL; - - nm_assert(str); - - nm_str_buf_append(strbuf, nm_utils_escaped_tokens_escape(str, delimiters, &str_to_free)); -} - -/* Calls nm_utils_escaped_tokens_escape_unnecessary() on @str and appends the - * string to @strbuf. */ -static inline void -nm_utils_escaped_tokens_escape_strbuf_assert(const char *str, - const char *delimiters, - NMStrBuf * strbuf) -{ - nm_str_buf_append(strbuf, nm_utils_escaped_tokens_escape_unnecessary(str, delimiters)); -} - -/*****************************************************************************/ - -static inline gboolean -nm_str_buf_is_initalized(NMStrBuf *strbuf) -{ - nm_assert(strbuf); -#if NM_MORE_ASSERTS - if (strbuf->_priv_str) - _nm_str_buf_assert(strbuf); -#endif - return !!strbuf->_priv_str; -} - -/** - * nm_str_buf_get_str: - * @strbuf: the #NMStrBuf instance - * - * Returns the NUL terminated internal string. - * - * While constructing the string, the intermediate buffer - * is not NUL terminated (this makes it different from GString). - * Usually, one would build the string and retrieve it at the - * end with nm_str_buf_finalize(). This returns the NUL terminated - * buffer that was appended so far. Contrary to nm_str_buf_finalize(), you - * can still append more data to the buffer and this does not transfer ownership - * of the string. - * - * Returns: (transfer none): the internal string. The string - * is of length "strbuf->len", which may be larger if the - * returned string contains NUL characters (binary). The terminating - * NUL character is always present after "strbuf->len" characters. - * If currently no buffer is allocated, this will return %NULL. - */ -static inline char * -nm_str_buf_get_str(NMStrBuf *strbuf) -{ - _nm_str_buf_assert(strbuf); - - if (!strbuf->_priv_str) - return NULL; - - nm_str_buf_maybe_expand(strbuf, 1, FALSE); - strbuf->_priv_str[strbuf->_priv_len] = '\0'; - return strbuf->_priv_str; -} - -static inline char * -nm_str_buf_get_str_unsafe(NMStrBuf *strbuf) -{ - _nm_str_buf_assert(strbuf); - return strbuf->_priv_str; -} - -static inline char * -nm_str_buf_get_str_at_unsafe(NMStrBuf *strbuf, gsize index) -{ - _nm_str_buf_assert(strbuf); - - /* it is acceptable to ask for a pointer at the end of the buffer -- even - * if there is no data there. The caller is anyway required to take care - * of the length (that's the "unsafe" part), and in that case, the length - * is merely zero. */ - nm_assert(index <= strbuf->allocated); - - if (!strbuf->_priv_str) - return NULL; - - return &strbuf->_priv_str[index]; -} - -static inline char -nm_str_buf_get_char(const NMStrBuf *strbuf, gsize index) -{ - _nm_str_buf_assert(strbuf); - nm_assert(index < strbuf->allocated); - return strbuf->_priv_str[index]; -} - -/** - * nm_str_buf_finalize: - * @strbuf: an initilized #NMStrBuf - * @out_len: (out): (allow-none): optional output - * argument with the length of the returned string. - * - * Returns: (transfer full): the string of the buffer - * which must be freed by the caller. The @strbuf - * is afterwards in undefined state, though it can be - * reused after nm_str_buf_init(). - * Note that if no string is allocated yet (after nm_str_buf_init() with - * length zero), this will return %NULL. */ -static inline char * -nm_str_buf_finalize(NMStrBuf *strbuf, gsize *out_len) -{ - _nm_str_buf_assert(strbuf); - - NM_SET_OUT(out_len, strbuf->_priv_len); - - if (!strbuf->_priv_str) - return NULL; - - nm_str_buf_maybe_expand(strbuf, 1, TRUE); - strbuf->_priv_str[strbuf->_priv_len] = '\0'; - - /* the buffer is in invalid state afterwards, however, we clear it - * so far, that nm_auto_str_buf and nm_str_buf_destroy() is happy. */ - return g_steal_pointer(&strbuf->_priv_str); -} - -static inline GBytes * -nm_str_buf_finalize_to_gbytes(NMStrBuf *strbuf) -{ - char *s; - gsize l; - - /* this always returns a non-NULL, newly allocated GBytes instance. - * The data buffer always has an additional NUL character after - * the data, and the data is allocated with malloc. - * - * That means, the caller who takes ownership of the GBytes can - * safely modify the content of the buffer (including the additional - * NUL sentinel). */ - s = nm_str_buf_finalize(strbuf, &l); - return g_bytes_new_take(s ?: g_new0(char, 1), l); -} - -/** - * nm_str_buf_destroy: - * @strbuf: an initialized #NMStrBuf - * - * Frees the associated memory of @strbuf. The buffer - * afterwards is in undefined state, but can be re-initialized - * with nm_str_buf_init(). - */ -static inline void -nm_str_buf_destroy(NMStrBuf *strbuf) -{ - if (!strbuf->_priv_str) - return; - _nm_str_buf_assert(strbuf); - if (strbuf->_priv_do_bzero_mem) - nm_explicit_bzero(strbuf->_priv_str, strbuf->_priv_len); - g_free(strbuf->_priv_str); - - /* the buffer is in invalid state afterwards, however, we clear it - * so far, that nm_auto_str_buf is happy when calling - * nm_str_buf_destroy() again. */ - strbuf->_priv_str = NULL; -} - -#define nm_auto_str_buf nm_auto(nm_str_buf_destroy) - -#endif /* __NM_STR_BUF_H__ */ diff --git a/shared/nm-glib-aux/nm-time-utils.c b/shared/nm-glib-aux/nm-time-utils.c deleted file mode 100644 index df98176a..00000000 --- a/shared/nm-glib-aux/nm-time-utils.c +++ /dev/null @@ -1,328 +0,0 @@ -/* SPDX-License-Identifier: LGPL-2.1-or-later */ -/* - * Copyright (C) 2018 Red Hat, Inc. - */ - -#include "nm-glib-aux/nm-default-glib-i18n-lib.h" - -#include "nm-time-utils.h" - -#include "nm-logging-fwd.h" - -/*****************************************************************************/ - -typedef struct { - /* the offset to the native clock, in seconds. */ - gint64 offset_sec; - clockid_t clk_id; -} GlobalState; - -static const GlobalState *volatile p_global_state; - -static const GlobalState * -_t_init_global_state(void) -{ - static GlobalState global_state = {}; - static gsize init_once = 0; - const GlobalState *p; - clockid_t clk_id; - struct timespec tp; - gint64 offset_sec; - int r; - - clk_id = CLOCK_BOOTTIME; - r = clock_gettime(clk_id, &tp); - if (r == -1 && errno == EINVAL) { - clk_id = CLOCK_MONOTONIC; - r = clock_gettime(clk_id, &tp); - } - - /* The only failure we tolerate is that CLOCK_BOOTTIME is not supported. - * Other than that, we rely on kernel to not fail on this. */ - g_assert(r == 0); - g_assert(tp.tv_nsec >= 0 && tp.tv_nsec < NM_UTILS_NSEC_PER_SEC); - - /* Calculate an offset for the time stamp. - * - * We always want positive values, because then we can initialize - * a timestamp with 0 and be sure, that it will be less then any - * value nm_utils_get_monotonic_timestamp_*() might return. - * For this to be true also for nm_utils_get_monotonic_timestamp_sec() at - * early boot, we have to shift the timestamp to start counting at - * least from 1 second onward. - * - * Another advantage of shifting is, that this way we make use of the whole 31 bit - * range of signed int, before the time stamp for nm_utils_get_monotonic_timestamp_sec() - * wraps (~68 years). - **/ - offset_sec = (-((gint64) tp.tv_sec)) + 1; - - if (!g_once_init_enter(&init_once)) { - /* there was a race. We expect the pointer to be fully initialized now. */ - p = g_atomic_pointer_get(&p_global_state); - g_assert(p); - return p; - } - - global_state.offset_sec = offset_sec; - global_state.clk_id = clk_id; - p = &global_state; - g_atomic_pointer_set(&p_global_state, p); - g_once_init_leave(&init_once, 1); - - _nm_utils_monotonic_timestamp_initialized(&tp, p->offset_sec, p->clk_id == CLOCK_BOOTTIME); - - return p; -} - -#define _t_get_global_state() \ - ({ \ - const GlobalState *_p; \ - \ - _p = g_atomic_pointer_get(&p_global_state); \ - (G_LIKELY(_p) ? _p : _t_init_global_state()); \ - }) - -#define _t_clock_gettime_eval(p, tp) \ - ({ \ - struct timespec *const _tp = (tp); \ - const GlobalState *const _p2 = (p); \ - int _r; \ - \ - nm_assert(_tp); \ - \ - _r = clock_gettime(_p2->clk_id, _tp); \ - \ - nm_assert(_r == 0); \ - nm_assert(_tp->tv_nsec >= 0 && _tp->tv_nsec < NM_UTILS_NSEC_PER_SEC); \ - \ - _p2; \ - }) - -#define _t_clock_gettime(tp) _t_clock_gettime_eval(_t_get_global_state(), tp); - -/*****************************************************************************/ - -/** - * nm_utils_get_monotonic_timestamp_nsec: - * - * Returns: a monotonically increasing time stamp in nanoseconds, - * starting at an unspecified offset. See clock_gettime(), %CLOCK_BOOTTIME. - * - * The returned value will start counting at an undefined point - * in the past and will always be positive. - * - * All the nm_utils_get_monotonic_timestamp_*sec functions return the same - * timestamp but in different scales (nsec, usec, msec, sec). - **/ -gint64 -nm_utils_get_monotonic_timestamp_nsec(void) -{ - const GlobalState *p; - struct timespec tp; - - p = _t_clock_gettime(&tp); - - /* Although the result will always be positive, we return a signed - * integer, which makes it easier to calculate time differences (when - * you want to subtract signed values). - **/ - return (((gint64) tp.tv_sec) + p->offset_sec) * NM_UTILS_NSEC_PER_SEC + tp.tv_nsec; -} - -/** - * nm_utils_get_monotonic_timestamp_usec: - * - * Returns: a monotonically increasing time stamp in microseconds, - * starting at an unspecified offset. See clock_gettime(), %CLOCK_BOOTTIME. - * - * The returned value will start counting at an undefined point - * in the past and will always be positive. - * - * All the nm_utils_get_monotonic_timestamp_*sec functions return the same - * timestamp but in different scales (nsec, usec, msec, sec). - **/ -gint64 -nm_utils_get_monotonic_timestamp_usec(void) -{ - const GlobalState *p; - struct timespec tp; - - p = _t_clock_gettime(&tp); - - /* Although the result will always be positive, we return a signed - * integer, which makes it easier to calculate time differences (when - * you want to subtract signed values). - **/ - return (((gint64) tp.tv_sec) + p->offset_sec) * ((gint64) G_USEC_PER_SEC) - + (tp.tv_nsec / (NM_UTILS_NSEC_PER_SEC / G_USEC_PER_SEC)); -} - -/** - * nm_utils_get_monotonic_timestamp_msec: - * - * Returns: a monotonically increasing time stamp in milliseconds, - * starting at an unspecified offset. See clock_gettime(), %CLOCK_BOOTTIME. - * - * The returned value will start counting at an undefined point - * in the past and will always be positive. - * - * All the nm_utils_get_monotonic_timestamp_*sec functions return the same - * timestamp but in different scales (nsec, usec, msec, sec). - **/ -gint64 -nm_utils_get_monotonic_timestamp_msec(void) -{ - const GlobalState *p; - struct timespec tp; - - p = _t_clock_gettime(&tp); - - /* Although the result will always be positive, we return a signed - * integer, which makes it easier to calculate time differences (when - * you want to subtract signed values). - **/ - return (((gint64) tp.tv_sec) + p->offset_sec) * ((gint64) 1000) - + (tp.tv_nsec / (NM_UTILS_NSEC_PER_SEC / 1000)); -} - -/** - * nm_utils_get_monotonic_timestamp_sec: - * - * Returns: nm_utils_get_monotonic_timestamp_msec() in seconds (throwing - * away sub second parts). The returned value will always be positive. - * - * This value wraps after roughly 68 years which should be fine for any - * practical purpose. - * - * All the nm_utils_get_monotonic_timestamp_*sec functions return the same - * timestamp but in different scales (nsec, usec, msec, sec). - **/ -gint32 -nm_utils_get_monotonic_timestamp_sec(void) -{ - const GlobalState *p; - struct timespec tp; - - p = _t_clock_gettime(&tp); - - return (((gint64) tp.tv_sec) + p->offset_sec); -} - -/** - * nm_utils_monotonic_timestamp_as_boottime: - * @timestamp: the monotonic-timestamp that should be converted into CLOCK_BOOTTIME. - * @timestamp_nsec_per_tick: How many nanoseconds make one unit of @timestamp? E.g. if - * @timestamp is in unit seconds, pass %NM_UTILS_NSEC_PER_SEC; if @timestamp is - * in nanoseconds, pass 1; if @timestamp is in milliseconds, pass %NM_UTILS_NSEC_PER_SEC/1000. - * This must be a multiple of 10, and between 1 and %NM_UTILS_NSEC_PER_SEC. - * - * Returns: the monotonic-timestamp as CLOCK_BOOTTIME, as returned by clock_gettime(). - * The unit is the same as the passed in @timestamp based on @timestamp_nsec_per_tick. - * E.g. if you passed @timestamp in as seconds, it will return boottime in seconds. - * - * Note that valid monotonic-timestamps are always positive numbers (counting roughly since - * the application is running). However, it might make sense to calculate a timestamp from - * before the application was running, hence negative @timestamp is allowed. The result - * in that case might also be a negative timestamp (in CLOCK_BOOTTIME), which would indicate - * that the timestamp lies in the past before the machine was booted. - * - * On older kernels that don't support CLOCK_BOOTTIME, the returned time is instead CLOCK_MONOTONIC. - **/ -gint64 -nm_utils_monotonic_timestamp_as_boottime(gint64 timestamp, gint64 timestamp_nsec_per_tick) -{ - const GlobalState *p; - gint64 offset; - - /* only support nsec-per-tick being a multiple of 10. */ - g_return_val_if_fail(timestamp_nsec_per_tick == 1 - || (timestamp_nsec_per_tick > 0 - && timestamp_nsec_per_tick <= NM_UTILS_NSEC_PER_SEC - && timestamp_nsec_per_tick % 10 == 0), - -1); - - /* if the caller didn't yet ever fetch a monotonic-timestamp, he cannot pass any meaningful - * value (because he has no idea what these timestamps would be). That would be a bug. */ - nm_assert(g_atomic_pointer_get(&p_global_state)); - - p = _t_get_global_state(); - - nm_assert(p->offset_sec <= 0); - - /* calculate the offset of monotonic-timestamp to boottime. offset_s is <= 1. */ - offset = p->offset_sec * (NM_UTILS_NSEC_PER_SEC / timestamp_nsec_per_tick); - - nm_assert(offset <= 0 && offset > G_MININT64); - - /* check for overflow (note that offset is non-positive). */ - g_return_val_if_fail(timestamp < G_MAXINT64 + offset, G_MAXINT64); - - return timestamp - offset; -} - -/** - * nm_utils_monotonic_timestamp_from_boottime: - * @boottime: the timestamp from CLOCK_BOOTTIME (or CLOCK_MONOTONIC, if - * kernel does not support CLOCK_BOOTTIME and monotonic timestamps are based - * on CLOCK_MONOTONIC). - * @timestamp_nsec_per_tick: the scale in which @boottime is. If @boottime is in - * nano seconds, this should be 1. If it is in milli seconds, this should be - * %NM_UTILS_NSEC_PER_SEC/1000, etc. - * - * Returns: the same timestamp in monotonic timestamp scale. - * - * Note that commonly monotonic timestamps are positive. But they may not - * be positive in this case. That's when boottime is taken from a time before - * the monotonic timestamps started counting. So, that means a zero or negative - * value is still a valid timestamp. - * - * This is the inverse of nm_utils_monotonic_timestamp_as_boottime(). - */ -gint64 -nm_utils_monotonic_timestamp_from_boottime(guint64 boottime, gint64 timestamp_nsec_per_tick) -{ - const GlobalState *p; - gint64 offset; - - /* only support nsec-per-tick being a multiple of 10. */ - g_return_val_if_fail(timestamp_nsec_per_tick == 1 - || (timestamp_nsec_per_tick > 0 - && timestamp_nsec_per_tick <= NM_UTILS_NSEC_PER_SEC - && timestamp_nsec_per_tick % 10 == 0), - -1); - - p = _t_get_global_state(); - - nm_assert(p->offset_sec <= 0); - - /* calculate the offset of monotonic-timestamp to boottime. offset_s is <= 1. */ - offset = p->offset_sec * (NM_UTILS_NSEC_PER_SEC / timestamp_nsec_per_tick); - - nm_assert(offset <= 0 && offset > G_MININT64); - - /* check for overflow (note that offset is non-positive). */ - g_return_val_if_fail(boottime < G_MAXINT64, G_MAXINT64); - - return (gint64) boottime + offset; -} - -gint64 -nm_utils_clock_gettime_nsec(clockid_t clockid) -{ - struct timespec tp; - - if (clock_gettime(clockid, &tp) != 0) - return -NM_ERRNO_NATIVE(errno); - return nm_utils_timespec_to_nsec(&tp); -} - -gint64 -nm_utils_clock_gettime_msec(clockid_t clockid) -{ - struct timespec tp; - - if (clock_gettime(clockid, &tp) != 0) - return -NM_ERRNO_NATIVE(errno); - return nm_utils_timespec_to_msec(&tp); -} diff --git a/shared/nm-glib-aux/nm-time-utils.h b/shared/nm-glib-aux/nm-time-utils.h deleted file mode 100644 index 3c3e935f..00000000 --- a/shared/nm-glib-aux/nm-time-utils.h +++ /dev/null @@ -1,47 +0,0 @@ -/* SPDX-License-Identifier: LGPL-2.1-or-later */ -/* - * Copyright (C) 2018 Red Hat, Inc. - */ - -#ifndef __NM_TIME_UTILS_H__ -#define __NM_TIME_UTILS_H__ - -#include <time.h> - -static inline gint64 -nm_utils_timespec_to_nsec(const struct timespec *ts) -{ - return (((gint64) ts->tv_sec) * ((gint64) NM_UTILS_NSEC_PER_SEC)) + ((gint64) ts->tv_nsec); -} - -static inline gint64 -nm_utils_timespec_to_msec(const struct timespec *ts) -{ - return (((gint64) ts->tv_sec) * ((gint64) 1000)) - + (((gint64) ts->tv_nsec) / ((gint64) NM_UTILS_NSEC_PER_SEC / 1000)); -} - -gint64 nm_utils_get_monotonic_timestamp_nsec(void); -gint64 nm_utils_get_monotonic_timestamp_usec(void); -gint64 nm_utils_get_monotonic_timestamp_msec(void); -gint32 nm_utils_get_monotonic_timestamp_sec(void); - -gint64 nm_utils_monotonic_timestamp_as_boottime(gint64 timestamp, gint64 timestamp_ticks_per_nsec); -gint64 nm_utils_monotonic_timestamp_from_boottime(guint64 boottime, gint64 timestamp_nsec_per_tick); - -static inline gint64 -nm_utils_get_monotonic_timestamp_nsec_cached(gint64 *cache_now) -{ - return (*cache_now) ?: (*cache_now = nm_utils_get_monotonic_timestamp_nsec()); -} - -static inline gint64 -nm_utils_get_monotonic_timestamp_msec_cached(gint64 *cache_now) -{ - return (*cache_now) ?: (*cache_now = nm_utils_get_monotonic_timestamp_msec()); -} - -gint64 nm_utils_clock_gettime_nsec(clockid_t clockid); -gint64 nm_utils_clock_gettime_msec(clockid_t clockid); - -#endif /* __NM_TIME_UTILS_H__ */ diff --git a/shared/nm-glib-aux/nm-value-type.h b/shared/nm-glib-aux/nm-value-type.h deleted file mode 100644 index f9edebdb..00000000 --- a/shared/nm-glib-aux/nm-value-type.h +++ /dev/null @@ -1,209 +0,0 @@ -/* SPDX-License-Identifier: LGPL-2.1-or-later */ -/* - * Copyright (C) 2019 Red Hat, Inc. - */ - -#ifndef __NM_VALUE_TYPE_H__ -#define __NM_VALUE_TYPE_H__ - -typedef enum { - NM_VALUE_TYPE_UNSPEC = 1, - NM_VALUE_TYPE_BOOL = 2, - NM_VALUE_TYPE_INT32 = 3, - NM_VALUE_TYPE_INT = 4, - NM_VALUE_TYPE_STRING = 5, -} NMValueType; - -/*****************************************************************************/ - -#ifdef NM_VALUE_TYPE_DEFINE_FUNCTIONS - -typedef union { - bool v_bool; - gint32 v_int32; - int v_int; - const char *v_string; - - /* for convenience, also let the union contain other pointer types. These are - * for NM_VALUE_TYPE_UNSPEC. */ - gconstpointer * v_ptr; - const GPtrArray *v_ptrarray; - -} NMValueTypUnion; - - /* Set the NMValueTypUnion. You can also assign the member directly. - * The only purpose of this is that it also returns a pointer to the - * union. So, you can do - * - * ptr = NM_VALUE_TYP_UNION_SET (&value_typ_union_storage, v_bool, TRUE); - */ - #define NM_VALUE_TYP_UNION_SET(_arg, _type, _val) \ - ({ \ - NMValueTypUnion *const _arg2 = (_arg); \ - \ - *_arg2 = (NMValueTypUnion){ \ - ._type = (_val), \ - }; \ - _arg2; \ - }) - -typedef struct { - bool has; - NMValueTypUnion val; -} NMValueTypUnioMaybe; - - #define NM_VALUE_TYP_UNIO_MAYBE_SET(_arg, _type, _val) \ - ({ \ - NMValueTypUnioMaybe *const _arg2 = (_arg); \ - \ - *_arg2 = (NMValueTypUnioMaybe){ \ - .has = TRUE, \ - .val._type = (_val), \ - }; \ - _arg2; \ - }) - -/*****************************************************************************/ - -static inline int -nm_value_type_cmp(NMValueType value_type, gconstpointer p_a, gconstpointer p_b) -{ - switch (value_type) { - case NM_VALUE_TYPE_BOOL: - NM_CMP_DIRECT(*((const bool *) p_a), *((const bool *) p_b)); - return 0; - case NM_VALUE_TYPE_INT32: - NM_CMP_DIRECT(*((const gint32 *) p_a), *((const gint32 *) p_b)); - return 0; - case NM_VALUE_TYPE_INT: - NM_CMP_DIRECT(*((const int *) p_a), *((const int *) p_b)); - return 0; - case NM_VALUE_TYPE_STRING: - return nm_strcmp0(*((const char *const *) p_a), *((const char *const *) p_b)); - case NM_VALUE_TYPE_UNSPEC: - break; - } - nm_assert_not_reached(); - return 0; -} - -static inline gboolean -nm_value_type_equal(NMValueType value_type, gconstpointer p_a, gconstpointer p_b) -{ - return nm_value_type_cmp(value_type, p_a, p_b) == 0; -} - -static inline void -nm_value_type_copy(NMValueType value_type, gpointer dst, gconstpointer src) -{ - switch (value_type) { - case NM_VALUE_TYPE_BOOL: - (*((bool *) dst) = *((const bool *) src)); - return; - case NM_VALUE_TYPE_INT32: - (*((gint32 *) dst) = *((const gint32 *) src)); - return; - case NM_VALUE_TYPE_INT: - (*((int *) dst) = *((const int *) src)); - return; - case NM_VALUE_TYPE_STRING: - /* self assignment safe! */ - if (*((char **) dst) != *((const char *const *) src)) { - g_free(*((char **) dst)); - *((char **) dst) = g_strdup(*((const char *const *) src)); - } - return; - case NM_VALUE_TYPE_UNSPEC: - break; - } - nm_assert_not_reached(); -} - -static inline void -nm_value_type_get_from_variant(NMValueType value_type, - gpointer dst, - GVariant * variant, - gboolean clone) -{ - switch (value_type) { - case NM_VALUE_TYPE_BOOL: - *((bool *) dst) = g_variant_get_boolean(variant); - return; - case NM_VALUE_TYPE_INT32: - *((gint32 *) dst) = g_variant_get_int32(variant); - return; - case NM_VALUE_TYPE_STRING: - if (clone) { - g_free(*((char **) dst)); - *((char **) dst) = g_variant_dup_string(variant, NULL); - } else { - /* we don't clone the string, nor free the previous value. */ - *((const char **) dst) = g_variant_get_string(variant, NULL); - } - return; - - case NM_VALUE_TYPE_INT: - /* "int" also does not have a define variant type, because it's not - * clear how many bits we would need. */ - - /* fall-through */ - case NM_VALUE_TYPE_UNSPEC: - break; - } - nm_assert_not_reached(); -} - -static inline GVariant * -nm_value_type_to_variant(NMValueType value_type, gconstpointer src) -{ - const char *v_string; - - switch (value_type) { - case NM_VALUE_TYPE_BOOL: - return g_variant_new_boolean(*((const bool *) src)); - case NM_VALUE_TYPE_INT32: - return g_variant_new_int32(*((const gint32 *) src)); - case NM_VALUE_TYPE_STRING: - v_string = *((const char *const *) src); - return v_string ? g_variant_new_string(v_string) : NULL; - - case NM_VALUE_TYPE_INT: - /* "int" also does not have a define variant type, because it's not - * clear how many bits we would need. */ - - /* fall-through */ - case NM_VALUE_TYPE_UNSPEC: - break; - } - nm_assert_not_reached(); - return NULL; -} - -static inline const GVariantType * -nm_value_type_get_variant_type(NMValueType value_type) -{ - switch (value_type) { - case NM_VALUE_TYPE_BOOL: - return G_VARIANT_TYPE_BOOLEAN; - case NM_VALUE_TYPE_INT32: - return G_VARIANT_TYPE_INT32; - case NM_VALUE_TYPE_STRING: - return G_VARIANT_TYPE_STRING; - - case NM_VALUE_TYPE_INT: - /* "int" also does not have a define variant type, because it's not - * clear how many bits we would need. */ - - /* fall-through */ - case NM_VALUE_TYPE_UNSPEC: - break; - } - nm_assert_not_reached(); - return NULL; -} - - /*****************************************************************************/ - -#endif /* NM_VALUE_TYPE_DEFINE_FUNCTIONS */ - -#endif /* __NM_VALUE_TYPE_H__ */ diff --git a/shared/nm-glib-aux/tests/meson.build b/shared/nm-glib-aux/tests/meson.build deleted file mode 100644 index 464f4e7c..00000000 --- a/shared/nm-glib-aux/tests/meson.build +++ /dev/null @@ -1,41 +0,0 @@ -# SPDX-License-Identifier: LGPL-2.1-or-later - -exe = executable( - 'test-shared-general', - 'test-shared-general.c', - c_args: [ - '-DG_LOG_DOMAIN="test"', - ], - dependencies: libnm_glib_aux_dep, - link_with: libnm_systemd_logging_stub, -) - -test( - 'shared/nm-glib-aux/test-shared-general', - test_script, - args: test_args + [exe.full_path()], - timeout: default_test_timeout, -) - -if jansson_dep.found() - exe = executable( - 'test-json-aux', - 'test-json-aux.c', - c_args: [ - '-DG_LOG_DOMAIN="test"', - ], - dependencies: [ - libnm_glib_aux_dep, - jansson_dep, - dl_dep, - ], - link_with: libnm_systemd_logging_stub, - ) - - test( - 'shared/nm-glib-aux/test-json-aux', - test_script, - args: test_args + [exe.full_path()], - timeout: default_test_timeout, - ) -endif diff --git a/shared/nm-glib-aux/tests/test-json-aux.c b/shared/nm-glib-aux/tests/test-json-aux.c deleted file mode 100644 index b07d673f..00000000 --- a/shared/nm-glib-aux/tests/test-json-aux.c +++ /dev/null @@ -1,165 +0,0 @@ -/* SPDX-License-Identifier: LGPL-2.1-or-later */ - -#include "nm-glib-aux/nm-default-glib-i18n-prog.h" - -#include <jansson.h> - -#include "nm-glib-aux/nm-json-aux.h" - -#include "nm-utils/nm-test-utils.h" - -/*****************************************************************************/ - -static void -test_jansson(void) -{ - const NMJsonVt * vt; - nm_auto_decref_json nm_json_t *js1 = NULL; - nm_auto_decref_json nm_json_t *js2 = NULL; - -#define _ASSERT_FIELD(type1, type2, field) \ - G_STMT_START \ - { \ - G_STATIC_ASSERT_EXPR(sizeof(((type1 *) NULL)->field) == sizeof(((type2 *) NULL)->field)); \ - G_STATIC_ASSERT_EXPR(G_STRUCT_OFFSET(type1, field) == G_STRUCT_OFFSET(type2, field)); \ - } \ - G_STMT_END - - G_STATIC_ASSERT_EXPR(NM_JSON_REJECT_DUPLICATES == JSON_REJECT_DUPLICATES); - - G_STATIC_ASSERT_EXPR(sizeof(nm_json_type) == sizeof(json_type)); - - G_STATIC_ASSERT_EXPR((int) NM_JSON_OBJECT == JSON_OBJECT); - G_STATIC_ASSERT_EXPR((int) NM_JSON_ARRAY == JSON_ARRAY); - G_STATIC_ASSERT_EXPR((int) NM_JSON_STRING == JSON_STRING); - G_STATIC_ASSERT_EXPR((int) NM_JSON_INTEGER == JSON_INTEGER); - G_STATIC_ASSERT_EXPR((int) NM_JSON_REAL == JSON_REAL); - G_STATIC_ASSERT_EXPR((int) NM_JSON_TRUE == JSON_TRUE); - G_STATIC_ASSERT_EXPR((int) NM_JSON_FALSE == JSON_FALSE); - G_STATIC_ASSERT_EXPR((int) NM_JSON_NULL == JSON_NULL); - - G_STATIC_ASSERT_EXPR(sizeof(nm_json_int_t) == sizeof(json_int_t)); - - G_STATIC_ASSERT_EXPR(sizeof(nm_json_t) == sizeof(json_t)); - _ASSERT_FIELD(nm_json_t, json_t, refcount); - _ASSERT_FIELD(nm_json_t, json_t, type); - - G_STATIC_ASSERT_EXPR(NM_JSON_ERROR_TEXT_LENGTH == JSON_ERROR_TEXT_LENGTH); - G_STATIC_ASSERT_EXPR(NM_JSON_ERROR_SOURCE_LENGTH == JSON_ERROR_SOURCE_LENGTH); - - G_STATIC_ASSERT_EXPR(sizeof(nm_json_error_t) == sizeof(json_error_t)); - _ASSERT_FIELD(nm_json_error_t, json_error_t, line); - _ASSERT_FIELD(nm_json_error_t, json_error_t, column); - _ASSERT_FIELD(nm_json_error_t, json_error_t, position); - _ASSERT_FIELD(nm_json_error_t, json_error_t, source); - _ASSERT_FIELD(nm_json_error_t, json_error_t, text); - - vt = nm_json_vt(); - - g_assert(vt); - g_assert(vt->loaded); - - js1 = vt->nm_json_loads("{ \"a\": 5 }", 0, NULL); - g_assert(js1); - nm_json_decref(vt, g_steal_pointer(&js1)); - - js2 = vt->nm_json_loads("{ \"a\": 6 }", 0, NULL); - g_assert(js2); - -#define CHECK_FCN(vt, fcn, nm_type, js_type) \ - G_STMT_START \ - { \ - const NMJsonVt *const _vt = (vt); \ - _nm_unused nm_type = (_vt->nm_##fcn); \ - _nm_unused js_type = (fcn); \ - \ - g_assert(_vt->nm_##fcn); \ - g_assert(_f_nm); \ - g_assert(_f_js); \ - g_assert(_f_nm == _vt->nm_##fcn); \ - } \ - G_STMT_END - - CHECK_FCN(vt, json_array, nm_json_t * (*_f_nm)(void), json_t * (*_f_js)(void) ); - CHECK_FCN(vt, - json_array_append_new, - int (*_f_nm)(nm_json_t *, nm_json_t *), - int (*_f_js)(json_t *, json_t *)); - CHECK_FCN(vt, - json_array_get, - nm_json_t * (*_f_nm)(const nm_json_t *, gsize), - json_t * (*_f_js)(const json_t *, size_t)); - CHECK_FCN(vt, - json_array_size, - gsize(*_f_nm)(const nm_json_t *), - size_t(*_f_js)(const json_t *)); - CHECK_FCN(vt, json_delete, void (*_f_nm)(nm_json_t *), void (*_f_js)(json_t *)); - CHECK_FCN(vt, - json_dumps, - char *(*_f_nm)(const nm_json_t *, gsize), - char *(*_f_js)(const json_t *, size_t)); - CHECK_FCN(vt, json_false, nm_json_t * (*_f_nm)(void), json_t * (*_f_js)(void) ); - CHECK_FCN(vt, json_integer, nm_json_t * (*_f_nm)(nm_json_int_t), json_t * (*_f_js)(json_int_t)); - CHECK_FCN(vt, - json_integer_value, - nm_json_int_t(*_f_nm)(const nm_json_t *), - json_int_t(*_f_js)(const json_t *)); - CHECK_FCN(vt, - json_loads, - nm_json_t * (*_f_nm)(const char *, gsize, nm_json_error_t *), - json_t * (*_f_js)(const char *, size_t, json_error_t *) ); - CHECK_FCN(vt, json_object, nm_json_t * (*_f_nm)(void), json_t * (*_f_js)(void) ); - CHECK_FCN(vt, - json_object_del, - int (*_f_nm)(nm_json_t *, const char *), - int (*_f_js)(json_t *, const char *)); - CHECK_FCN(vt, - json_object_get, - nm_json_t * (*_f_nm)(const nm_json_t *, const char *), - json_t * (*_f_js)(const json_t *, const char *) ); - CHECK_FCN(vt, json_object_iter, void *(*_f_nm)(nm_json_t *), void *(*_f_js)(json_t *) ); - CHECK_FCN(vt, - json_object_iter_key, - const char *(*_f_nm)(void *), - const char *(*_f_js)(void *) ); - CHECK_FCN(vt, - json_object_iter_next, - void *(*_f_nm)(nm_json_t *, void *), - void *(*_f_js)(json_t *, void *) ); - CHECK_FCN(vt, json_object_iter_value, nm_json_t * (*_f_nm)(void *), json_t * (*_f_js)(void *) ); - CHECK_FCN(vt, - json_object_key_to_iter, - void *(*_f_nm)(const char *), - void *(*_f_js)(const char *) ); - CHECK_FCN(vt, - json_object_set_new, - int (*_f_nm)(nm_json_t *, const char *, nm_json_t *), - int (*_f_js)(json_t *, const char *, json_t *)); - CHECK_FCN(vt, - json_object_size, - gsize(*_f_nm)(const nm_json_t *), - size_t(*_f_js)(const json_t *)); - CHECK_FCN(vt, - json_string, - nm_json_t * (*_f_nm)(const char *), - json_t * (*_f_js)(const char *) ); - CHECK_FCN(vt, - json_string_value, - const char *(*_f_nm)(const nm_json_t *), - const char *(*_f_js)(const json_t *) ); - CHECK_FCN(vt, json_true, nm_json_t * (*_f_nm)(void), json_t * (*_f_js)(void) ); -} - -/*****************************************************************************/ - -NMTST_DEFINE(); - -int -main(int argc, char **argv) -{ - nmtst_init(&argc, &argv, TRUE); - - g_test_add_func("/general/test_jansson", test_jansson); - - return g_test_run(); -} diff --git a/shared/nm-glib-aux/tests/test-shared-general.c b/shared/nm-glib-aux/tests/test-shared-general.c deleted file mode 100644 index f42c6fb1..00000000 --- a/shared/nm-glib-aux/tests/test-shared-general.c +++ /dev/null @@ -1,1289 +0,0 @@ -/* SPDX-License-Identifier: LGPL-2.1-or-later */ -/* - * Copyright (C) 2018 Red Hat, Inc. - */ - -#include "nm-glib-aux/nm-default-glib-i18n-prog.h" - -#include "nm-std-aux/unaligned.h" -#include "nm-glib-aux/nm-random-utils.h" -#include "nm-glib-aux/nm-str-buf.h" -#include "nm-glib-aux/nm-time-utils.h" -#include "nm-glib-aux/nm-ref-string.h" - -#include "nm-utils/nm-test-utils.h" - -/*****************************************************************************/ - -G_STATIC_ASSERT(NM_AF_UNSPEC == AF_UNSPEC); -G_STATIC_ASSERT(NM_AF_INET == AF_INET); -G_STATIC_ASSERT(NM_AF_INET6 == AF_INET6); - -G_STATIC_ASSERT(NM_AF_INET_SIZE == sizeof(in_addr_t)); -G_STATIC_ASSERT(NM_AF_INET_SIZE == sizeof(struct in_addr)); -G_STATIC_ASSERT(NM_AF_INET6_SIZE == sizeof(struct in6_addr)); - -G_STATIC_ASSERT(4 == _nm_alignof(in_addr_t)); -G_STATIC_ASSERT(4 == _nm_alignof(struct in_addr)); -G_STATIC_ASSERT(4 == _nm_alignof(struct in6_addr)); -G_STATIC_ASSERT(4 == _nm_alignof(NMIPAddr)); - -/*****************************************************************************/ - -static void -test_gpid(void) -{ - const int *int_ptr; - GPid pid = 42; - - /* We redefine G_PID_FORMAT, because it's only available since glib 2.53.5. - * - * Also, this is the format for GPid, which for glib is always a typedef - * for "int". Add a check for that here. - * - * G_PID_FORMAT is not about pid_t, which might be a smaller int, and which we would - * check with SIZEOF_PID_T. */ - G_STATIC_ASSERT(sizeof(GPid) == sizeof(int)); - - g_assert_cmpstr("" G_PID_FORMAT, ==, "i"); - - /* check that it's really "int". We will get a compiler warning, if that's not - * the case. */ - int_ptr = &pid; - g_assert_cmpint(*int_ptr, ==, 42); -} - -/*****************************************************************************/ - -static void -test_monotonic_timestamp(void) -{ - g_assert(nm_utils_get_monotonic_timestamp_sec() > 0); -} - -/*****************************************************************************/ - -static void -test_nmhash(void) -{ - int rnd; - - nm_utils_random_bytes(&rnd, sizeof(rnd)); - - g_assert(nm_hash_val(555, 4) != 0); -} - -/*****************************************************************************/ - -static const char * -_make_strv_foo(void) -{ - return "foo"; -} - -static const char *const *const _tst_make_strv_1 = NM_MAKE_STRV("1", "2"); - -static void -test_make_strv(void) -{ - const char *const *v1a = NM_MAKE_STRV("a"); - const char *const *v1b = NM_MAKE_STRV("a", ); - const char *const *v2a = NM_MAKE_STRV("a", "b"); - const char *const *v2b = NM_MAKE_STRV("a", "b", ); - const char *const v3[] = { - "a", - "b", - }; - const char *const *v4b = NM_MAKE_STRV("a", _make_strv_foo(), ); - - g_assert(NM_PTRARRAY_LEN(v1a) == 1); - g_assert(NM_PTRARRAY_LEN(v1b) == 1); - g_assert(NM_PTRARRAY_LEN(v2a) == 2); - g_assert(NM_PTRARRAY_LEN(v2b) == 2); - - g_assert(NM_PTRARRAY_LEN(_tst_make_strv_1) == 2); - g_assert_cmpstr(_tst_make_strv_1[0], ==, "1"); - g_assert_cmpstr(_tst_make_strv_1[1], ==, "2"); - /* writing the static read-only variable leads to crash .*/ - //((char **) _tst_make_strv_1)[0] = NULL; - //((char **) _tst_make_strv_1)[2] = "c"; - - G_STATIC_ASSERT_EXPR(G_N_ELEMENTS(v3) == 2); - - g_assert(NM_PTRARRAY_LEN(v4b) == 2); - - G_STATIC_ASSERT_EXPR(G_N_ELEMENTS(NM_MAKE_STRV("a", "b")) == 3); - G_STATIC_ASSERT_EXPR(G_N_ELEMENTS(NM_MAKE_STRV("a", "b", )) == 3); - - nm_strquote_a(300, ""); -} - -/*****************************************************************************/ - -typedef enum { - TEST_NM_STRDUP_ENUM_m1 = -1, - TEST_NM_STRDUP_ENUM_3 = 3, -} TestNMStrdupIntEnum; - -static void -test_nm_strdup_int(void) -{ -#define _NM_STRDUP_INT_TEST(num, str) \ - G_STMT_START \ - { \ - gs_free char *_s1 = NULL; \ - \ - _s1 = nm_strdup_int((num)); \ - \ - g_assert(_s1); \ - g_assert_cmpstr(_s1, ==, str); \ - } \ - G_STMT_END - -#define _NM_STRDUP_INT_TEST_TYPED(type, num) \ - G_STMT_START \ - { \ - type _num = ((type) num); \ - \ - _NM_STRDUP_INT_TEST(_num, G_STRINGIFY(num)); \ - } \ - G_STMT_END - - _NM_STRDUP_INT_TEST_TYPED(char, 0); - _NM_STRDUP_INT_TEST_TYPED(char, 1); - _NM_STRDUP_INT_TEST_TYPED(guint8, 0); - _NM_STRDUP_INT_TEST_TYPED(gint8, 25); - _NM_STRDUP_INT_TEST_TYPED(char, 47); - _NM_STRDUP_INT_TEST_TYPED(short, 47); - _NM_STRDUP_INT_TEST_TYPED(int, 47); - _NM_STRDUP_INT_TEST_TYPED(long, 47); - _NM_STRDUP_INT_TEST_TYPED(unsigned char, 47); - _NM_STRDUP_INT_TEST_TYPED(unsigned short, 47); - _NM_STRDUP_INT_TEST_TYPED(unsigned, 47); - _NM_STRDUP_INT_TEST_TYPED(unsigned long, 47); - _NM_STRDUP_INT_TEST_TYPED(gint64, 9223372036854775807); - _NM_STRDUP_INT_TEST_TYPED(gint64, -9223372036854775807); - _NM_STRDUP_INT_TEST_TYPED(guint64, 0); - _NM_STRDUP_INT_TEST_TYPED(guint64, 9223372036854775807); - - _NM_STRDUP_INT_TEST(TEST_NM_STRDUP_ENUM_m1, "-1"); - _NM_STRDUP_INT_TEST(TEST_NM_STRDUP_ENUM_3, "3"); -} - -/*****************************************************************************/ - -static void -test_nm_strndup_a(void) -{ - int run; - - for (run = 0; run < 20; run++) { - gs_free char *input = NULL; - char ch; - gsize i, l; - - input = g_strnfill(nmtst_get_rand_uint32() % 20, 'x'); - - for (i = 0; input[i]; i++) { - while ((ch = ((char) nmtst_get_rand_uint32())) == '\0') { - /* repeat. */ - } - input[i] = ch; - } - - { - gs_free char *dup_free = NULL; - const char * dup; - - l = strlen(input) + 1; - dup = nm_strndup_a(10, input, l - 1, &dup_free); - g_assert_cmpstr(dup, ==, input); - if (strlen(dup) < 10) - g_assert(!dup_free); - else - g_assert(dup == dup_free); - } - - { - gs_free char *dup_free = NULL; - const char * dup; - - l = nmtst_get_rand_uint32() % 23; - dup = nm_strndup_a(10, input, l, &dup_free); - g_assert(strncmp(dup, input, l) == 0); - g_assert(strlen(dup) <= l); - if (l < 10) - g_assert(!dup_free); - else - g_assert(dup == dup_free); - if (strlen(input) < l) - g_assert(nm_utils_memeqzero(&dup[strlen(input)], l - strlen(input))); - } - } -} - -/*****************************************************************************/ - -static void -test_nm_ip4_addr_is_localhost(void) -{ - g_assert(nm_ip4_addr_is_localhost(nmtst_inet4_from_string("127.0.0.0"))); - g_assert(nm_ip4_addr_is_localhost(nmtst_inet4_from_string("127.0.0.1"))); - g_assert(nm_ip4_addr_is_localhost(nmtst_inet4_from_string("127.5.0.1"))); - g_assert(!nm_ip4_addr_is_localhost(nmtst_inet4_from_string("126.5.0.1"))); - g_assert(!nm_ip4_addr_is_localhost(nmtst_inet4_from_string("128.5.0.1"))); - g_assert(!nm_ip4_addr_is_localhost(nmtst_inet4_from_string("129.5.0.1"))); -} - -/*****************************************************************************/ - -static void -test_unaligned(void) -{ - int shift; - - for (shift = 0; shift <= 32; shift++) { - guint8 buf[100] = {}; - guint8 val = 0; - - while (val == 0) - val = nmtst_get_rand_uint32() % 256; - - buf[shift] = val; - - g_assert_cmpint(unaligned_read_le64(&buf[shift]), ==, (guint64) val); - g_assert_cmpint(unaligned_read_be64(&buf[shift]), ==, ((guint64) val) << 56); - g_assert_cmpint(unaligned_read_ne64(&buf[shift]), !=, 0); - - g_assert_cmpint(unaligned_read_le32(&buf[shift]), ==, (guint32) val); - g_assert_cmpint(unaligned_read_be32(&buf[shift]), ==, ((guint32) val) << 24); - g_assert_cmpint(unaligned_read_ne32(&buf[shift]), !=, 0); - - g_assert_cmpint(unaligned_read_le16(&buf[shift]), ==, (guint16) val); - g_assert_cmpint(unaligned_read_be16(&buf[shift]), ==, ((guint16) val) << 8); - g_assert_cmpint(unaligned_read_ne16(&buf[shift]), !=, 0); - } -} - -/*****************************************************************************/ - -static void -_strv_cmp_fuzz_input(const char *const * in, - gssize l, - const char *** out_strv_free_shallow, - char *** out_strv_free_deep, - const char *const **out_s1, - const char *const **out_s2) -{ - const char **strv; - gsize i; - - /* Fuzz the input argument. It will return two output arrays that are semantically - * equal the input. */ - - if (nmtst_get_rand_bool()) { - char **ss; - - if (l < 0) - ss = g_strdupv((char **) in); - else if (l == 0) { - ss = nmtst_get_rand_bool() ? NULL : g_new0(char *, 1); - } else { - ss = nm_memdup(in, sizeof(const char *) * l); - for (i = 0; i < (gsize) l; i++) - ss[i] = g_strdup(ss[i]); - } - strv = (const char **) ss; - *out_strv_free_deep = ss; - } else { - if (l < 0) { - strv = in ? nm_memdup(in, sizeof(const char *) * (NM_PTRARRAY_LEN(in) + 1)) : NULL; - } else if (l == 0) { - strv = nmtst_get_rand_bool() ? NULL : g_new0(const char *, 1); - } else - strv = nm_memdup(in, sizeof(const char *) * l); - *out_strv_free_shallow = strv; - } - - *out_s1 = in; - *out_s2 = strv; - - if (nmtst_get_rand_bool()) { - /* randomly swap the original and the clone. That means, out_s1 is either - * the input argument (as-is) or the sementically equal clone. */ - NM_SWAP(out_s1, out_s2); - } - if (nmtst_get_rand_bool()) { - /* randomly make s1 and s2 the same. This is for testing that - * comparing two identical pointers yields the same result. */ - *out_s2 = *out_s1; - } -} - -static void -_strv_cmp_free_deep(char **strv, gssize len) -{ - gssize i; - - if (strv) { - if (len < 0) - g_strfreev(strv); - else { - for (i = 0; i < len; i++) - g_free(strv[i]); - g_free(strv); - } - } -} - -static void -test_strv_cmp(void) -{ - const char *const strv0[1] = {}; - const char *const strv1[2] = { - "", - }; - -#define _STRV_CMP(a1, l1, a2, l2, equal) \ - G_STMT_START \ - { \ - gssize _l1 = (l1); \ - gssize _l2 = (l2); \ - const char *const * _a1; \ - const char *const * _a2; \ - const char *const * _a1x; \ - const char *const * _a2x; \ - char ** _a1_free_deep = NULL; \ - char ** _a2_free_deep = NULL; \ - gs_free const char **_a1_free_shallow = NULL; \ - gs_free const char **_a2_free_shallow = NULL; \ - int _c1, _c2; \ - \ - _strv_cmp_fuzz_input((a1), _l1, &_a1_free_shallow, &_a1_free_deep, &_a1, &_a1x); \ - _strv_cmp_fuzz_input((a2), _l2, &_a2_free_shallow, &_a2_free_deep, &_a2, &_a2x); \ - \ - _c1 = nm_utils_strv_cmp_n(_a1, _l1, _a2, _l2); \ - _c2 = nm_utils_strv_cmp_n(_a2, _l2, _a1, _l1); \ - if (equal) { \ - g_assert_cmpint(_c1, ==, 0); \ - g_assert_cmpint(_c2, ==, 0); \ - } else { \ - g_assert_cmpint(_c1, ==, -1); \ - g_assert_cmpint(_c2, ==, 1); \ - } \ - \ - /* Compare with self. _strv_cmp_fuzz_input() randomly swapped the arguments (_a1 and _a1x). - * Either way, the arrays must compare equal to their semantically equal alternative. */ \ - g_assert_cmpint(nm_utils_strv_cmp_n(_a1, _l1, _a1x, _l1), ==, 0); \ - g_assert_cmpint(nm_utils_strv_cmp_n(_a2, _l2, _a2x, _l2), ==, 0); \ - \ - _strv_cmp_free_deep(_a1_free_deep, _l1); \ - _strv_cmp_free_deep(_a2_free_deep, _l2); \ - } \ - G_STMT_END - - _STRV_CMP(NULL, -1, NULL, -1, TRUE); - - _STRV_CMP(NULL, -1, NULL, 0, FALSE); - _STRV_CMP(NULL, -1, strv0, 0, FALSE); - _STRV_CMP(NULL, -1, strv0, -1, FALSE); - - _STRV_CMP(NULL, 0, NULL, 0, TRUE); - _STRV_CMP(NULL, 0, strv0, 0, TRUE); - _STRV_CMP(NULL, 0, strv0, -1, TRUE); - _STRV_CMP(strv0, 0, strv0, 0, TRUE); - _STRV_CMP(strv0, 0, strv0, -1, TRUE); - _STRV_CMP(strv0, -1, strv0, -1, TRUE); - - _STRV_CMP(NULL, 0, strv1, -1, FALSE); - _STRV_CMP(NULL, 0, strv1, 1, FALSE); - _STRV_CMP(strv0, 0, strv1, -1, FALSE); - _STRV_CMP(strv0, 0, strv1, 1, FALSE); - _STRV_CMP(strv0, -1, strv1, -1, FALSE); - _STRV_CMP(strv0, -1, strv1, 1, FALSE); - - _STRV_CMP(strv1, -1, strv1, 1, TRUE); - _STRV_CMP(strv1, 1, strv1, 1, TRUE); -} - -/*****************************************************************************/ - -static void -_do_strstrip_avoid_copy(const char *str) -{ - gs_free char *str1 = g_strdup(str); - gs_free char *str2 = g_strdup(str); - gs_free char *str3 = NULL; - gs_free char *str4 = NULL; - const char * s3; - const char * s4; - - if (str1) - g_strstrip(str1); - - nm_strstrip(str2); - - g_assert_cmpstr(str1, ==, str2); - - s3 = nm_strstrip_avoid_copy(str, &str3); - g_assert_cmpstr(str1, ==, s3); - - s4 = nm_strstrip_avoid_copy_a(10, str, &str4); - g_assert_cmpstr(str1, ==, s4); - g_assert(!str == !s4); - g_assert(!s4 || strlen(s4) <= strlen(str)); - if (s4 && s4 == &str[strlen(str) - strlen(s4)]) { - g_assert(!str4); - g_assert(s3 == s4); - } else if (s4 && strlen(s4) >= 10) { - g_assert(str4); - g_assert(s4 == str4); - } else - g_assert(!str4); - - if (!nm_streq0(str1, str)) - _do_strstrip_avoid_copy(str1); -} - -static void -test_strstrip_avoid_copy(void) -{ - _do_strstrip_avoid_copy(NULL); - _do_strstrip_avoid_copy(""); - _do_strstrip_avoid_copy(" "); - _do_strstrip_avoid_copy(" a "); - _do_strstrip_avoid_copy(" 012345678 "); - _do_strstrip_avoid_copy(" 0123456789 "); - _do_strstrip_avoid_copy(" 01234567890 "); - _do_strstrip_avoid_copy(" 012345678901 "); -} - -/*****************************************************************************/ - -static void -test_nm_utils_bin2hexstr(void) -{ - int n_run; - - for (n_run = 0; n_run < 500; n_run++) { - guint8 buf[100]; - guint8 buf2[G_N_ELEMENTS(buf) + 1]; - gsize len = nmtst_get_rand_uint32() % (G_N_ELEMENTS(buf) + 1); - char strbuf1[G_N_ELEMENTS(buf) * 3]; - gboolean allocate = nmtst_get_rand_bool(); - char delimiter = nmtst_get_rand_bool() ? ':' : '\0'; - gboolean upper_case = nmtst_get_rand_bool(); - gboolean hexdigit_pairs_mangled; - gsize expected_strlen; - char * str_hex; - gsize required_len; - gboolean outlen_set; - gsize outlen; - guint8 * bin2; - guint i, j; - - nmtst_rand_buf(NULL, buf, len); - - if (len == 0) - expected_strlen = 0; - else if (delimiter != '\0') - expected_strlen = (len * 3u) - 1; - else - expected_strlen = len * 2u; - - g_assert_cmpint(expected_strlen, <, G_N_ELEMENTS(strbuf1)); - - str_hex = - nm_utils_bin2hexstr_full(buf, len, delimiter, upper_case, !allocate ? strbuf1 : NULL); - - g_assert(str_hex); - if (!allocate) - g_assert(str_hex == strbuf1); - g_assert_cmpint(strlen(str_hex), ==, expected_strlen); - - g_assert(NM_STRCHAR_ALL( - str_hex, - ch, - (ch >= '0' && ch <= '9') || ch == delimiter - || (upper_case ? (ch >= 'A' && ch <= 'F') : (ch >= 'a' && ch <= 'f')))); - - hexdigit_pairs_mangled = FALSE; - if (delimiter && len > 1 && nmtst_get_rand_bool()) { - /* randomly convert "0?" sequences to single digits, so we can get hexdigit_pairs_required - * parameter. */ - g_assert(strlen(str_hex) >= 5); - g_assert(str_hex[2] == delimiter); - i = 0; - j = 0; - for (;;) { - g_assert(g_ascii_isxdigit(str_hex[i])); - g_assert(g_ascii_isxdigit(str_hex[i + 1])); - g_assert(NM_IN_SET(str_hex[i + 2], delimiter, '\0')); - if (str_hex[i] == '0' && nmtst_get_rand_bool()) { - i++; - str_hex[j++] = str_hex[i++]; - hexdigit_pairs_mangled = TRUE; - } else { - str_hex[j++] = str_hex[i++]; - str_hex[j++] = str_hex[i++]; - } - if (str_hex[i] == '\0') { - str_hex[j] = '\0'; - break; - } - g_assert(str_hex[i] == delimiter); - str_hex[j++] = str_hex[i++]; - } - } - - required_len = nmtst_get_rand_bool() ? len : 0u; - - outlen_set = required_len == 0 || nmtst_get_rand_bool(); - - memset(buf2, 0, sizeof(buf2)); - - bin2 = nm_utils_hexstr2bin_full(str_hex, - nmtst_get_rand_bool(), - delimiter != '\0' && nmtst_get_rand_bool(), - !hexdigit_pairs_mangled && nmtst_get_rand_bool(), - delimiter != '\0' - ? nmtst_rand_select((const char *) ":", ":-") - : nmtst_rand_select((const char *) ":", ":-", "", NULL), - required_len, - buf2, - len, - outlen_set ? &outlen : NULL); - if (len > 0) { - g_assert(bin2); - g_assert(bin2 == buf2); - } else - g_assert(!bin2); - - if (outlen_set) - g_assert_cmpint(outlen, ==, len); - - g_assert_cmpmem(buf, len, buf2, len); - - g_assert(buf2[len] == '\0'); - - if (hexdigit_pairs_mangled) { - /* we mangled the hexstr to contain single digits. Trying to parse with - * hexdigit_pairs_required must now fail. */ - bin2 = nm_utils_hexstr2bin_full( - str_hex, - nmtst_get_rand_bool(), - delimiter != '\0' && nmtst_get_rand_bool(), - TRUE, - delimiter != '\0' ? nmtst_rand_select((const char *) ":", ":-") - : nmtst_rand_select((const char *) ":", ":-", "", NULL), - required_len, - buf2, - len, - outlen_set ? &outlen : NULL); - g_assert(!bin2); - } - - if (allocate) - g_free(str_hex); - } -} - -/*****************************************************************************/ - -static void -test_nm_ref_string(void) -{ - nm_auto_ref_string NMRefString *s1 = NULL; - NMRefString * s2; - - s1 = nm_ref_string_new("hallo"); - g_assert(s1); - g_assert_cmpstr(s1->str, ==, "hallo"); - g_assert_cmpint(s1->len, ==, strlen("hallo")); - - s2 = nm_ref_string_new("hallo"); - g_assert(s2 == s1); - nm_ref_string_unref(s2); - - s2 = nm_ref_string_new(NULL); - g_assert(!s2); - nm_ref_string_unref(s2); - -#define STR_WITH_NUL "hallo\0test\0" - s2 = nm_ref_string_new_len(STR_WITH_NUL, NM_STRLEN(STR_WITH_NUL)); - g_assert(s2); - g_assert_cmpstr(s2->str, ==, "hallo"); - g_assert_cmpint(s2->len, ==, NM_STRLEN(STR_WITH_NUL)); - g_assert_cmpint(s2->len, >, strlen(s2->str)); - g_assert_cmpmem(s2->str, s2->len, STR_WITH_NUL, NM_STRLEN(STR_WITH_NUL)); - g_assert(s2->str[s2->len] == '\0'); - nm_ref_string_unref(s2); -} - -/*****************************************************************************/ - -static NM_UTILS_STRING_TABLE_LOOKUP_DEFINE( - _do_string_table_lookup, - int, - { ; }, - { return -1; }, - {"0", 0}, - {"1", 1}, - {"2", 2}, - {"3", 3}, ); - -static void -test_string_table_lookup(void) -{ - const char *const args[] = { - NULL, - "0", - "1", - "2", - "3", - "x", - }; - int i; - - for (i = 0; i < G_N_ELEMENTS(args); i++) { - const char *needle = args[i]; - const int val2 = _nm_utils_ascii_str_to_int64(needle, 10, 0, 100, -1); - int val; - - val = _do_string_table_lookup(needle); - g_assert_cmpint(val, ==, val2); - } -} - -/*****************************************************************************/ - -static void -test_nm_utils_get_next_realloc_size(void) -{ - static const struct { - gsize requested; - gsize reserved_true; - gsize reserved_false; - } test_data[] = { - {0, 8, 8}, - {1, 8, 8}, - {8, 8, 8}, - {9, 16, 16}, - {16, 16, 16}, - {17, 32, 32}, - {32, 32, 32}, - {33, 40, 40}, - {40, 40, 40}, - {41, 104, 104}, - {104, 104, 104}, - {105, 232, 232}, - {232, 232, 232}, - {233, 488, 488}, - {488, 488, 488}, - {489, 1000, 1000}, - {1000, 1000, 1000}, - {1001, 2024, 2024}, - {2024, 2024, 2024}, - {2025, 4072, 4072}, - {4072, 4072, 4072}, - {4073, 8168, 8168}, - {8168, 8168, 8168}, - {8169, 12264, 16360}, - {12263, 12264, 16360}, - {12264, 12264, 16360}, - {12265, 16360, 16360}, - {16360, 16360, 16360}, - {16361, 20456, 32744}, - {20456, 20456, 32744}, - {20457, 24552, 32744}, - {24552, 24552, 32744}, - {24553, 28648, 32744}, - {28648, 28648, 32744}, - {28649, 32744, 32744}, - {32744, 32744, 32744}, - {32745, 36840, 65512}, - {36840, 36840, 65512}, - {G_MAXSIZE - 0x1000u, G_MAXSIZE, G_MAXSIZE}, - {G_MAXSIZE - 25u, G_MAXSIZE, G_MAXSIZE}, - {G_MAXSIZE - 24u, G_MAXSIZE, G_MAXSIZE}, - {G_MAXSIZE - 1u, G_MAXSIZE, G_MAXSIZE}, - {G_MAXSIZE, G_MAXSIZE, G_MAXSIZE}, - {NM_UTILS_GET_NEXT_REALLOC_SIZE_32, - NM_UTILS_GET_NEXT_REALLOC_SIZE_32, - NM_UTILS_GET_NEXT_REALLOC_SIZE_32}, - {NM_UTILS_GET_NEXT_REALLOC_SIZE_40, - NM_UTILS_GET_NEXT_REALLOC_SIZE_40, - NM_UTILS_GET_NEXT_REALLOC_SIZE_40}, - {NM_UTILS_GET_NEXT_REALLOC_SIZE_104, - NM_UTILS_GET_NEXT_REALLOC_SIZE_104, - NM_UTILS_GET_NEXT_REALLOC_SIZE_104}, - {NM_UTILS_GET_NEXT_REALLOC_SIZE_1000, - NM_UTILS_GET_NEXT_REALLOC_SIZE_1000, - NM_UTILS_GET_NEXT_REALLOC_SIZE_1000}, - }; - guint i; - - G_STATIC_ASSERT_EXPR(NM_UTILS_GET_NEXT_REALLOC_SIZE_104 == 104u); - G_STATIC_ASSERT_EXPR(NM_UTILS_GET_NEXT_REALLOC_SIZE_1000 == 1000u); - - for (i = 0; i < G_N_ELEMENTS(test_data) + 5000u; i++) { - gsize requested0; - - if (i < G_N_ELEMENTS(test_data)) - requested0 = test_data[i].requested; - else { - /* find some interesting random values for testing. */ - switch (nmtst_get_rand_uint32() % 5) { - case 0: - requested0 = nmtst_get_rand_size(); - break; - case 1: - /* values close to G_MAXSIZE. */ - requested0 = G_MAXSIZE - (nmtst_get_rand_uint32() % 12000u); - break; - case 2: - /* values around G_MAXSIZE/2. */ - requested0 = (G_MAXSIZE / 2u) + 6000u - (nmtst_get_rand_uint32() % 12000u); - break; - case 3: - /* values around powers of 2. */ - requested0 = (((gsize) 1) << (nmtst_get_rand_uint32() % (sizeof(gsize) * 8u))) - + 6000u - (nmtst_get_rand_uint32() % 12000u); - break; - case 4: - /* values around 4k borders. */ - requested0 = (nmtst_get_rand_size() & ~((gsize) 0xFFFu)) + 30u - - (nmtst_get_rand_uint32() % 60u); - break; - default: - g_assert_not_reached(); - } - } - - { - const gsize requested = requested0; - gsize reserved_true; - gsize reserved_false; - bool truncated_true = FALSE; - bool truncated_false = FALSE; - - if (sizeof(gsize) > 4 && requested > SIZE_MAX / 2u - 24u) { - reserved_false = G_MAXSSIZE; - truncated_false = TRUE; - } else - reserved_false = nm_utils_get_next_realloc_size(FALSE, requested); - - if (sizeof(gsize) > 4 && requested > SIZE_MAX - 0x1000u - 24u) { - reserved_true = G_MAXSSIZE; - truncated_true = TRUE; - } else - reserved_true = nm_utils_get_next_realloc_size(TRUE, requested); - - g_assert_cmpuint(reserved_true, >, 0); - g_assert_cmpuint(reserved_false, >, 0); - if (!truncated_true) - g_assert_cmpuint(reserved_true, >=, requested); - if (!truncated_false) - g_assert_cmpuint(reserved_false, >=, requested); - if (!truncated_true && !truncated_false) - g_assert_cmpuint(reserved_false, >=, reserved_true); - - if (i < G_N_ELEMENTS(test_data)) { - if (!truncated_true) - g_assert_cmpuint(reserved_true, ==, test_data[i].reserved_true); - if (!truncated_false) - g_assert_cmpuint(reserved_false, ==, test_data[i].reserved_false); - } - - /* reserved_false is generally the next power of two - 24. */ - if (reserved_false == G_MAXSIZE) - g_assert_cmpuint(requested, >, G_MAXSIZE / 2u - 24u); - else if (!reserved_false) { - g_assert_cmpuint(reserved_false, <=, G_MAXSIZE - 24u); - if (reserved_false >= 40) { - const gsize _pow2 = reserved_false + 24u; - - /* reserved_false must always be a power of two minus 24. */ - g_assert_cmpuint(_pow2, >=, 64u); - g_assert_cmpuint(_pow2, >, requested); - g_assert(nm_utils_is_power_of_two(_pow2)); - - /* but _pow2/2 must also be smaller than what we requested. */ - g_assert_cmpuint(_pow2 / 2u - 24u, <, requested); - } else { - /* smaller values are hard-coded. */ - } - } - - /* reserved_true is generally the next 4k border - 24. */ - if (reserved_true == G_MAXSIZE) - g_assert_cmpuint(requested, >, G_MAXSIZE - 0x1000u - 24u); - else if (!truncated_true) { - g_assert_cmpuint(reserved_true, <=, G_MAXSIZE - 24u); - if (reserved_true > 8168u) { - const gsize page_border = reserved_true + 24u; - - /* reserved_true must always be aligned to 4k (minus 24). */ - g_assert_cmpuint(page_border % 0x1000u, ==, 0); - if (requested > 0x1000u - 24u) { - /* page_border not be more than 4k above requested. */ - g_assert_cmpuint(page_border, >=, 0x1000u - 24u); - g_assert_cmpuint(page_border - 0x1000u - 24u, <, requested); - } - } else { - /* for smaller sizes, reserved_true and reserved_false are the same. */ - g_assert_cmpuint(reserved_true, ==, reserved_false); - } - } - } - } -} - -/*****************************************************************************/ - -static void -test_nm_str_buf(void) -{ - guint i_run; - - for (i_run = 0; TRUE; i_run++) { - nm_auto_str_buf NMStrBuf strbuf = {}; - nm_auto_free_gstring GString *gstr = NULL; - int i, j, k; - int c; - - nm_str_buf_init(&strbuf, nmtst_get_rand_uint32() % 200u + 1u, nmtst_get_rand_bool()); - - if (i_run < 1000) { - c = nmtst_get_rand_word_length(NULL); - for (i = 0; i < c; i++) - nm_str_buf_append_c(&strbuf, '0' + (i % 10)); - gstr = g_string_new(nm_str_buf_get_str(&strbuf)); - j = nmtst_get_rand_uint32() % (strbuf.len + 1); - k = nmtst_get_rand_uint32() % (strbuf.len - j + 2) - 1; - - nm_str_buf_erase(&strbuf, j, k, nmtst_get_rand_bool()); - g_string_erase(gstr, j, k); - g_assert_cmpstr(gstr->str, ==, nm_str_buf_get_str(&strbuf)); - } else - return; - } -} - -/*****************************************************************************/ - -static void -test_nm_utils_parse_next_line(void) -{ - const char *data; - const char *data0; - gsize data_len; - const char *line_start; - gsize line_len; - int i_run; - gsize j, k; - - data = NULL; - data_len = 0; - g_assert(!nm_utils_parse_next_line(&data, &data_len, &line_start, &line_len)); - - for (i_run = 0; i_run < 1000; i_run++) { - gs_unref_ptrarray GPtrArray *strv = g_ptr_array_new_with_free_func(g_free); - gs_unref_ptrarray GPtrArray *strv2 = g_ptr_array_new_with_free_func(g_free); - gsize strv_len = nmtst_get_rand_word_length(NULL); - nm_auto_str_buf NMStrBuf strbuf = NM_STR_BUF_INIT(0, nmtst_get_rand_bool()); - - /* create a list of random words. */ - for (j = 0; j < strv_len; j++) { - gsize w_len = nmtst_get_rand_word_length(NULL); - NMStrBuf w_buf = - NM_STR_BUF_INIT(nmtst_get_rand_uint32() % (w_len + 1), nmtst_get_rand_bool()); - - for (k = 0; k < w_len; k++) - nm_str_buf_append_c(&w_buf, '0' + (k % 10)); - nm_str_buf_maybe_expand(&w_buf, 1, TRUE); - g_ptr_array_add(strv, nm_str_buf_finalize(&w_buf, NULL)); - } - - /* join the list of random words with (random) line delimiters - * ("\0", "\n", "\r" or EOF). */ - for (j = 0; j < strv_len; j++) { - nm_str_buf_append(&strbuf, strv->pdata[j]); -again: - switch (nmtst_get_rand_uint32() % 5) { - case 0: - nm_str_buf_append_c(&strbuf, '\0'); - break; - case 1: - if (strbuf.len > 0 - && (nm_str_buf_get_str_unsafe(&strbuf))[strbuf.len - 1] == '\r') { - /* the previous line was empty and terminated by "\r". We - * must not join with "\n". Retry. */ - goto again; - } - nm_str_buf_append_c(&strbuf, '\n'); - break; - case 2: - nm_str_buf_append_c(&strbuf, '\r'); - break; - case 3: - nm_str_buf_append(&strbuf, "\r\n"); - break; - case 4: - /* the last word randomly is delimited or not, but not if the last - * word is "". */ - if (j + 1 < strv_len) { - /* it's not the last word. Retry. */ - goto again; - } - g_assert(j == strv_len - 1); - if (((const char *) strv->pdata[j])[0] == '\0') { - /* if the last word was "", we need a delimiter (to parse it back). - * Retry. */ - goto again; - } - /* The final delimiter gets omitted. It's EOF. */ - break; - } - } - - data0 = nm_str_buf_get_str_unsafe(&strbuf); - if (!data0 && nmtst_get_rand_bool()) { - nm_str_buf_maybe_expand(&strbuf, 1, TRUE); - data0 = nm_str_buf_get_str_unsafe(&strbuf); - g_assert(data0); - } - data_len = strbuf.len; - g_assert((data_len > 0 && data0) || data_len == 0); - data = data0; - while (nm_utils_parse_next_line(&data, &data_len, &line_start, &line_len)) { - g_assert(line_start); - g_assert(line_start >= data0); - g_assert(line_start < &data0[strbuf.len]); - g_assert(!memchr(line_start, '\0', line_len)); - g_ptr_array_add(strv2, g_strndup(line_start, line_len)); - } - g_assert(data_len == 0); - if (data0) - g_assert(data == &data0[strbuf.len]); - else - g_assert(!data); - - g_assert(nm_utils_strv_cmp_n((const char *const *) strv->pdata, - strv->len, - (const char *const *) strv2->pdata, - strv2->len) - == 0); - } -} - -/*****************************************************************************/ - -static void -test_in_strset_ascii_case(void) -{ - const char *x; - - x = NULL; - g_assert(NM_IN_STRSET_ASCII_CASE(x, NULL)); - g_assert(NM_IN_STRSET_ASCII_CASE(x, NULL, "b")); - g_assert(!NM_IN_STRSET_ASCII_CASE(x, "b")); - - x = "b"; - g_assert(NM_IN_STRSET(x, "b")); - g_assert(NM_IN_STRSET_ASCII_CASE(x, "b")); - g_assert(!NM_IN_STRSET(x, "B")); - g_assert(NM_IN_STRSET_ASCII_CASE(x, "B")); -} - -/*****************************************************************************/ - -static void -test_is_specific_hostname(void) -{ - g_assert(!nm_utils_is_specific_hostname(NULL)); - g_assert(!nm_utils_is_specific_hostname("")); - g_assert(!nm_utils_is_specific_hostname("(none)")); - g_assert(nm_utils_is_specific_hostname("(NONE)")); - - g_assert(!nm_utils_is_specific_hostname("localhost")); - g_assert(!nm_utils_is_specific_hostname("lOcalHost")); - g_assert(!nm_utils_is_specific_hostname("LOCALHOST")); - - g_assert(!nm_utils_is_specific_hostname("LOCALHOST.localdomain")); - - g_assert(nm_utils_is_specific_hostname("xlocalhost")); - g_assert(nm_utils_is_specific_hostname("lOcalHxost")); - g_assert(nm_utils_is_specific_hostname("LOCALxHOST")); - - g_assert(!nm_utils_is_specific_hostname("foo.LOCALHOST")); - g_assert(!nm_utils_is_specific_hostname("foo.LOCALHOsT6.")); - g_assert(!nm_utils_is_specific_hostname("foo.LOCALHOsT6.localdomain6")); - g_assert(!nm_utils_is_specific_hostname(".LOCALHOsT6.localdomain6")); - g_assert(!nm_utils_is_specific_hostname("LOCALHOsT6.localdomain6")); - g_assert(!nm_utils_is_specific_hostname("LOCALHOsT6.localdomain6.")); - g_assert(nm_utils_is_specific_hostname("LOCALHOsT6.localdomain.")); - - g_assert(nm_utils_is_specific_hostname(" ")); -} - -/*****************************************************************************/ - -static void -test_strv_dup_packed(void) -{ - gs_unref_ptrarray GPtrArray *src = NULL; - int i_run; - - src = g_ptr_array_new_with_free_func(g_free); - - for (i_run = 0; i_run < 500; i_run++) { - const int strv_len = nmtst_get_rand_word_length(NULL); - gs_free const char **strv_cpy = NULL; - const char *const * strv_src; - int i, j; - - g_ptr_array_set_size(src, 0); - for (i = 0; i < strv_len; i++) { - const int word_len = nmtst_get_rand_word_length(NULL); - NMStrBuf sbuf = NM_STR_BUF_INIT(0, nmtst_get_rand_bool()); - - for (j = 0; j < word_len; j++) - nm_str_buf_append_c(&sbuf, 'a' + (nmtst_get_rand_uint32() % 20)); - - g_ptr_array_add(src, nm_str_buf_finalize(&sbuf, NULL) ?: g_new0(char, 1)); - } - g_ptr_array_add(src, NULL); - - strv_src = (const char *const *) src->pdata; - g_assert(strv_src); - g_assert(NM_PTRARRAY_LEN(strv_src) == strv_len); - - strv_cpy = - nm_utils_strv_dup_packed(strv_src, - nmtst_get_rand_bool() ? (gssize) strv_len : (gssize) -1); - if (strv_len == 0) - g_assert(!strv_cpy); - else - g_assert(strv_cpy); - g_assert(NM_PTRARRAY_LEN(strv_cpy) == strv_len); - if (strv_cpy) - g_assert(nm_utils_strv_equal(strv_cpy, strv_src)); - } -} - -/*****************************************************************************/ - -static int -_hash_func_cmp_direct(gconstpointer a, gconstpointer b, gpointer user_data) -{ - NM_CMP_DIRECT(GPOINTER_TO_INT(a), GPOINTER_TO_INT(b)); - return 0; -} - -static void -test_utils_hashtable_cmp(void) -{ - static struct { - int val_i; - const char *val_s; - } vals[] = { - { - 0, - "0", - }, - { - 1, - "1", - }, - { - 2, - "2", - }, - { - 3, - "3", - }, - { - 4, - "4", - }, - { - 5, - "5", - }, - { - 6, - "6", - }, - { - 7, - "7", - }, - { - 8, - "8", - }, - { - 9, - "9", - }, - { - 0, - "a", - }, - { - 1, - "a", - }, - { - 2, - "a", - }, - { - 3, - "a", - }, - { - 4, - "a", - }, - { - 5, - "a", - }, - { - 0, - "0", - }, - { - 0, - "1", - }, - { - 0, - "2", - }, - { - 0, - "3", - }, - { - 0, - "4", - }, - { - 0, - "5", - }, - }; - guint test_run; - int is_num_key; - - for (test_run = 0; test_run < 30; test_run++) { - for (is_num_key = 0; is_num_key < 2; is_num_key++) { - GHashFunc func_key_hash = is_num_key ? nm_direct_hash : nm_str_hash; - GEqualFunc func_key_equal = is_num_key ? g_direct_equal : g_str_equal; - GCompareDataFunc func_key_cmp = - is_num_key ? _hash_func_cmp_direct : (GCompareDataFunc) nm_strcmp_with_data; - GCompareDataFunc func_val_cmp = - !is_num_key ? _hash_func_cmp_direct : (GCompareDataFunc) nm_strcmp_with_data; - gs_unref_hashtable GHashTable *h1 = NULL; - gs_unref_hashtable GHashTable *h2 = NULL; - gboolean has_same_keys; - guint i, n; - - h1 = g_hash_table_new(func_key_hash, func_key_equal); - h2 = g_hash_table_new(func_key_hash, func_key_equal); - - n = nmtst_get_rand_word_length(NULL); - for (i = 0; i < n; i++) { - typeof(vals[0]) *v = &vals[nmtst_get_rand_uint32() % G_N_ELEMENTS(vals)]; - gconstpointer v_key = is_num_key ? GINT_TO_POINTER(v->val_i) : v->val_s; - gconstpointer v_val = !is_num_key ? GINT_TO_POINTER(v->val_i) : v->val_s; - - g_hash_table_insert(h1, (gpointer) v_key, (gpointer) v_val); - g_hash_table_insert(h2, (gpointer) v_key, (gpointer) v_val); - } - - g_assert(nm_utils_hashtable_same_keys(h1, h2)); - g_assert(nm_utils_hashtable_cmp_equal(h1, h2, NULL, NULL)); - g_assert(nm_utils_hashtable_cmp_equal(h1, h2, func_val_cmp, NULL)); - g_assert(nm_utils_hashtable_cmp(h1, h2, FALSE, func_key_cmp, NULL, NULL) == 0); - g_assert(nm_utils_hashtable_cmp(h1, h2, TRUE, func_key_cmp, NULL, NULL) == 0); - g_assert(nm_utils_hashtable_cmp(h1, h2, FALSE, func_key_cmp, func_val_cmp, NULL) == 0); - g_assert(nm_utils_hashtable_cmp(h1, h2, TRUE, func_key_cmp, func_val_cmp, NULL) == 0); - - n = nmtst_get_rand_word_length(NULL) + 1; - has_same_keys = TRUE; - for (i = 0; i < n; i++) { -again: -{ - typeof(vals[0]) *v = &vals[nmtst_get_rand_uint32() % G_N_ELEMENTS(vals)]; - gconstpointer v_key = is_num_key ? GINT_TO_POINTER(v->val_i) : v->val_s; - gconstpointer v_val = !is_num_key ? GINT_TO_POINTER(v->val_i) : v->val_s; - gpointer v_key2; - gpointer v_val2; - - if (g_hash_table_lookup_extended(h1, v_key, &v_key2, &v_val2)) { - g_assert(func_key_cmp(v_key, v_key2, NULL) == 0); - if (func_val_cmp(v_val, v_val2, NULL) == 0) - goto again; - } else - has_same_keys = FALSE; - - g_hash_table_insert(h2, (gpointer) v_key, (gpointer) v_val); -} - } - - if (has_same_keys) { - g_assert(nm_utils_hashtable_same_keys(h1, h2)); - g_assert(nm_utils_hashtable_cmp_equal(h1, h2, NULL, NULL)); - g_assert(nm_utils_hashtable_cmp(h1, h2, FALSE, func_key_cmp, NULL, NULL) == 0); - g_assert(nm_utils_hashtable_cmp(h1, h2, TRUE, func_key_cmp, NULL, NULL) == 0); - } else { - g_assert(!nm_utils_hashtable_same_keys(h1, h2)); - g_assert(!nm_utils_hashtable_cmp_equal(h1, h2, NULL, NULL)); - g_assert(nm_utils_hashtable_cmp(h1, h2, FALSE, func_key_cmp, NULL, NULL) != 0); - g_assert(nm_utils_hashtable_cmp(h1, h2, TRUE, func_key_cmp, NULL, NULL) != 0); - } - g_assert(!nm_utils_hashtable_cmp_equal(h1, h2, func_val_cmp, NULL)); - g_assert(nm_utils_hashtable_cmp(h1, h2, FALSE, func_key_cmp, func_val_cmp, NULL) != 0); - g_assert(nm_utils_hashtable_cmp(h1, h2, TRUE, func_key_cmp, func_val_cmp, NULL) != 0); - } - } -} - -/*****************************************************************************/ - -NMTST_DEFINE(); - -int -main(int argc, char **argv) -{ - nmtst_init(&argc, &argv, TRUE); - - g_test_add_func("/general/test_gpid", test_gpid); - g_test_add_func("/general/test_monotonic_timestamp", test_monotonic_timestamp); - g_test_add_func("/general/test_nmhash", test_nmhash); - g_test_add_func("/general/test_nm_make_strv", test_make_strv); - g_test_add_func("/general/test_nm_strdup_int", test_nm_strdup_int); - g_test_add_func("/general/test_nm_strndup_a", test_nm_strndup_a); - g_test_add_func("/general/test_nm_ip4_addr_is_localhost", test_nm_ip4_addr_is_localhost); - g_test_add_func("/general/test_unaligned", test_unaligned); - g_test_add_func("/general/test_strv_cmp", test_strv_cmp); - g_test_add_func("/general/test_strstrip_avoid_copy", test_strstrip_avoid_copy); - g_test_add_func("/general/test_nm_utils_bin2hexstr", test_nm_utils_bin2hexstr); - g_test_add_func("/general/test_nm_ref_string", test_nm_ref_string); - g_test_add_func("/general/test_string_table_lookup", test_string_table_lookup); - g_test_add_func("/general/test_nm_utils_get_next_realloc_size", - test_nm_utils_get_next_realloc_size); - g_test_add_func("/general/test_nm_str_buf", test_nm_str_buf); - g_test_add_func("/general/test_nm_utils_parse_next_line", test_nm_utils_parse_next_line); - g_test_add_func("/general/test_in_strset_ascii_case", test_in_strset_ascii_case); - g_test_add_func("/general/test_is_specific_hostname", test_is_specific_hostname); - g_test_add_func("/general/test_strv_dup_packed", test_strv_dup_packed); - g_test_add_func("/general/test_utils_hashtable_cmp", test_utils_hashtable_cmp); - - return g_test_run(); -} diff --git a/shared/nm-log-core/nm-logging.c b/shared/nm-log-core/nm-logging.c deleted file mode 100644 index cf3c3a86..00000000 --- a/shared/nm-log-core/nm-logging.c +++ /dev/null @@ -1,1035 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-or-later */ -/* - * Copyright (C) 2006 - 2012 Red Hat, Inc. - * Copyright (C) 2006 - 2008 Novell, Inc. - */ - -#include "nm-glib-aux/nm-default-glib-i18n-lib.h" - -#include "nm-logging.h" - -#include <dlfcn.h> -#include <syslog.h> -#include <stdio.h> -#include <stdlib.h> -#include <unistd.h> -#include <sys/wait.h> -#include <sys/stat.h> -#include <strings.h> - -#if SYSTEMD_JOURNAL - #define SD_JOURNAL_SUPPRESS_LOCATION - #include <systemd/sd-journal.h> -#endif - -#include "nm-glib-aux/nm-logging-base.h" -#include "nm-glib-aux/nm-time-utils.h" - -/*****************************************************************************/ - -/* Notes about thread-safety: - * - * NetworkManager generally is single-threaded and uses a (GLib) mainloop. - * However, nm-logging is in parts thread-safe. That means: - * - * - functions that configure logging (nm_logging_init(), nm_logging_setup()) and - * most other functions MUST be called only from the main-thread. These functions - * are expected to be called infrequently, so they may or may not use a mutex - * (but the overhead is negligible here). - * - * - functions that do the actual logging logging (nm_log(), nm_logging_enabled()) are - * thread-safe and may be used from multiple threads. - * - When called from the not-main-thread, @mt_require_locking must be set to %TRUE. - * In this case, a Mutex will be used for accessing the global state. - * - When called from the main-thread, they may optionally pass @mt_require_locking %FALSE. - * This avoids extra locking and is in particular interesting for nm_logging_enabled(), - * which is expected to be called frequently and from the main-thread. - * - * Note that the logging macros honor %NM_THREAD_SAFE_ON_MAIN_THREAD define, to automatically - * set @mt_require_locking. That means, by default %NM_THREAD_SAFE_ON_MAIN_THREAD is "1", - * and code that only runs on the main-thread (which is the majority), can get away - * without locking. - */ - -/*****************************************************************************/ - -G_STATIC_ASSERT(LOG_EMERG == 0); -G_STATIC_ASSERT(LOG_ALERT == 1); -G_STATIC_ASSERT(LOG_CRIT == 2); -G_STATIC_ASSERT(LOG_ERR == 3); -G_STATIC_ASSERT(LOG_WARNING == 4); -G_STATIC_ASSERT(LOG_NOTICE == 5); -G_STATIC_ASSERT(LOG_INFO == 6); -G_STATIC_ASSERT(LOG_DEBUG == 7); - -/* We have more then 32 logging domains. Assert that it compiles to a 64 bit sized enum */ -G_STATIC_ASSERT(sizeof(NMLogDomain) >= sizeof(guint64)); - -/* Combined domains */ -#define LOGD_ALL_STRING "ALL" -#define LOGD_DEFAULT_STRING "DEFAULT" -#define LOGD_DHCP_STRING "DHCP" -#define LOGD_IP_STRING "IP" - -/*****************************************************************************/ - -typedef enum { - LOG_BACKEND_GLIB, - LOG_BACKEND_SYSLOG, - LOG_BACKEND_JOURNAL, -} LogBackend; - -typedef struct { - NMLogDomain num; - const char *name; -} LogDesc; - -typedef struct { - char *logging_domains_to_string; -} GlobalMain; - -typedef struct { - NMLogLevel log_level; - bool uses_syslog : 1; - bool init_pre_done : 1; - bool init_done : 1; - bool debug_stderr : 1; - const char *prefix; - const char *syslog_identifier; - - /* before we setup syslog (during start), the backend defaults to GLIB, meaning: - * we use g_log() for all logging. At that point, the application is not yet supposed - * to do any logging and doing so indicates a bug. - * - * Afterwards, the backend is either SYSLOG or JOURNAL. From that point, also - * g_log() is redirected to this backend via a logging handler. */ - LogBackend log_backend; -} Global; - -/*****************************************************************************/ - -G_LOCK_DEFINE_STATIC(log); - -/* This data must only be accessed from the main-thread (and as - * such does not need any lock). */ -static GlobalMain gl_main = {}; - -static union { - /* a union with an immutable and a mutable alias for the Global. - * Since nm-logging must be thread-safe, we must take care at which - * places we only read value ("imm") and where we modify them ("mut"). */ - Global mut; - const Global imm; -} gl = { - .imm = - { - /* nm_logging_setup ("INFO", LOGD_DEFAULT_STRING, NULL, NULL); */ - .log_level = LOGL_INFO, - .log_backend = LOG_BACKEND_GLIB, - .syslog_identifier = "SYSLOG_IDENTIFIER=" G_LOG_DOMAIN, - .prefix = "", - }, -}; - -NMLogDomain _nm_logging_enabled_state[_LOGL_N_REAL] = { - /* nm_logging_setup ("INFO", LOGD_DEFAULT_STRING, NULL, NULL); - * - * Note: LOGD_VPN_PLUGIN is special and must be disabled for - * DEBUG and TRACE levels. */ - [LOGL_INFO] = LOGD_DEFAULT, - [LOGL_WARN] = LOGD_DEFAULT, - [LOGL_ERR] = LOGD_DEFAULT, -}; - -/*****************************************************************************/ - -static const LogDesc domain_desc[] = { - {LOGD_PLATFORM, "PLATFORM"}, - {LOGD_RFKILL, "RFKILL"}, - {LOGD_ETHER, "ETHER"}, - {LOGD_WIFI, "WIFI"}, - {LOGD_BT, "BT"}, - {LOGD_MB, "MB"}, - {LOGD_DHCP4, "DHCP4"}, - {LOGD_DHCP6, "DHCP6"}, - {LOGD_PPP, "PPP"}, - {LOGD_WIFI_SCAN, "WIFI_SCAN"}, - {LOGD_IP4, "IP4"}, - {LOGD_IP6, "IP6"}, - {LOGD_AUTOIP4, "AUTOIP4"}, - {LOGD_DNS, "DNS"}, - {LOGD_VPN, "VPN"}, - {LOGD_SHARING, "SHARING"}, - {LOGD_SUPPLICANT, "SUPPLICANT"}, - {LOGD_AGENTS, "AGENTS"}, - {LOGD_SETTINGS, "SETTINGS"}, - {LOGD_SUSPEND, "SUSPEND"}, - {LOGD_CORE, "CORE"}, - {LOGD_DEVICE, "DEVICE"}, - {LOGD_OLPC, "OLPC"}, - {LOGD_INFINIBAND, "INFINIBAND"}, - {LOGD_FIREWALL, "FIREWALL"}, - {LOGD_ADSL, "ADSL"}, - {LOGD_BOND, "BOND"}, - {LOGD_VLAN, "VLAN"}, - {LOGD_BRIDGE, "BRIDGE"}, - {LOGD_DBUS_PROPS, "DBUS_PROPS"}, - {LOGD_TEAM, "TEAM"}, - {LOGD_CONCHECK, "CONCHECK"}, - {LOGD_DCB, "DCB"}, - {LOGD_DISPATCH, "DISPATCH"}, - {LOGD_AUDIT, "AUDIT"}, - {LOGD_SYSTEMD, "SYSTEMD"}, - {LOGD_VPN_PLUGIN, "VPN_PLUGIN"}, - {LOGD_PROXY, "PROXY"}, - {0}, -}; - -/*****************************************************************************/ - -static char *_domains_to_string(gboolean include_level_override, - NMLogLevel log_level, - const NMLogDomain log_state[static _LOGL_N_REAL]); - -/*****************************************************************************/ - -static gboolean -_syslog_identifier_valid_domain(const char *domain) -{ - char c; - - if (!domain || !domain[0]) - return FALSE; - - /* we pass the syslog identifier as format string. No funny stuff. */ - - for (; (c = domain[0]); domain++) { - if ((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || (c >= '0' && c <= '9') - || NM_IN_SET(c, '-', '_')) - continue; - return FALSE; - } - return TRUE; -} - -static gboolean -_syslog_identifier_assert(const char *syslog_identifier) -{ - g_assert(syslog_identifier); - g_assert(g_str_has_prefix(syslog_identifier, "SYSLOG_IDENTIFIER=")); - g_assert(_syslog_identifier_valid_domain(&syslog_identifier[NM_STRLEN("SYSLOG_IDENTIFIER=")])); - return TRUE; -} - -static const char * -syslog_identifier_domain(const char *syslog_identifier) -{ - nm_assert(_syslog_identifier_assert(syslog_identifier)); - return &syslog_identifier[NM_STRLEN("SYSLOG_IDENTIFIER=")]; -} - -#if SYSTEMD_JOURNAL -static const char * -syslog_identifier_full(const char *syslog_identifier) -{ - nm_assert(_syslog_identifier_assert(syslog_identifier)); - return &syslog_identifier[0]; -} -#endif - -/*****************************************************************************/ - -static gboolean -match_log_level(const char *level, NMLogLevel *out_level, GError **error) -{ - if (_nm_log_parse_level(level, out_level)) - return TRUE; - - g_set_error(error, - _NM_MANAGER_ERROR, - _NM_MANAGER_ERROR_UNKNOWN_LOG_LEVEL, - _("Unknown log level '%s'"), - level); - return FALSE; -} - -gboolean -nm_logging_setup(const char *level, const char *domains, char **bad_domains, GError **error) -{ - GString * unrecognized = NULL; - NMLogDomain cur_log_state[_LOGL_N_REAL]; - NMLogDomain new_log_state[_LOGL_N_REAL]; - NMLogLevel cur_log_level; - NMLogLevel new_log_level; - gs_free const char **domains_v = NULL; - gsize i_d; - int i; - gboolean had_platform_debug; - gs_free char * domains_free = NULL; - - NM_ASSERT_ON_MAIN_THREAD(); - - g_return_val_if_fail(!bad_domains || !*bad_domains, FALSE); - g_return_val_if_fail(!error || !*error, FALSE); - - cur_log_level = gl.imm.log_level; - memcpy(cur_log_state, _nm_logging_enabled_state, sizeof(cur_log_state)); - - new_log_level = cur_log_level; - - if (!domains || !*domains) { - domains_free = _domains_to_string(FALSE, cur_log_level, cur_log_state); - domains = domains_free; - } - - for (i = 0; i < G_N_ELEMENTS(new_log_state); i++) - new_log_state[i] = 0; - - if (level && *level) { - if (!match_log_level(level, &new_log_level, error)) - return FALSE; - if (new_log_level == _LOGL_KEEP) { - new_log_level = cur_log_level; - for (i = 0; i < G_N_ELEMENTS(new_log_state); i++) - new_log_state[i] = cur_log_state[i]; - } - } - - domains_v = nm_utils_strsplit_set(domains, ", "); - for (i_d = 0; domains_v && domains_v[i_d]; i_d++) { - const char * s = domains_v[i_d]; - const char * p; - const LogDesc *diter; - NMLogLevel domain_log_level; - NMLogDomain bits; - - /* LOGD_VPN_PLUGIN is protected, that is, when setting ALL or DEFAULT, - * it does not enable the verbose levels DEBUG and TRACE, because that - * may expose sensitive data. */ - NMLogDomain protect = LOGD_NONE; - - p = strchr(s, ':'); - if (p) { - *((char *) p) = '\0'; - if (!match_log_level(p + 1, &domain_log_level, error)) - return FALSE; - } else - domain_log_level = new_log_level; - - bits = 0; - - if (domains_free) { - /* The caller didn't provide any domains to set (`nmcli general logging level DEBUG`). - * We reset all domains that were previously set, but we still want to protect - * VPN_PLUGIN domain. */ - protect = LOGD_VPN_PLUGIN; - } - - /* Check for combined domains */ - if (!g_ascii_strcasecmp(s, LOGD_ALL_STRING)) { - bits = LOGD_ALL; - protect = LOGD_VPN_PLUGIN; - } else if (!g_ascii_strcasecmp(s, LOGD_DEFAULT_STRING)) { - bits = LOGD_DEFAULT; - protect = LOGD_VPN_PLUGIN; - } else if (!g_ascii_strcasecmp(s, LOGD_DHCP_STRING)) - bits = LOGD_DHCP; - else if (!g_ascii_strcasecmp(s, LOGD_IP_STRING)) - bits = LOGD_IP; - - /* Check for compatibility domains */ - else if (!g_ascii_strcasecmp(s, "HW")) - bits = LOGD_PLATFORM; - else if (!g_ascii_strcasecmp(s, "WIMAX")) - continue; - - else { - for (diter = &domain_desc[0]; diter->name; diter++) { - if (!g_ascii_strcasecmp(diter->name, s)) { - bits = diter->num; - break; - } - } - - if (!bits) { - if (!bad_domains) { - g_set_error(error, - _NM_MANAGER_ERROR, - _NM_MANAGER_ERROR_UNKNOWN_LOG_DOMAIN, - _("Unknown log domain '%s'"), - s); - return FALSE; - } - - if (unrecognized) - g_string_append(unrecognized, ", "); - else - unrecognized = g_string_new(NULL); - g_string_append(unrecognized, s); - continue; - } - } - - if (domain_log_level == _LOGL_KEEP) { - for (i = 0; i < G_N_ELEMENTS(new_log_state); i++) - new_log_state[i] = (new_log_state[i] & ~bits) | (cur_log_state[i] & bits); - } else { - for (i = 0; i < G_N_ELEMENTS(new_log_state); i++) { - if (i < domain_log_level) - new_log_state[i] &= ~bits; - else { - new_log_state[i] |= bits; - if ((protect & bits) && i < LOGL_INFO) - new_log_state[i] &= ~protect; - } - } - } - } - - nm_clear_g_free(&gl_main.logging_domains_to_string); - - had_platform_debug = _nm_logging_enabled_lockfree(LOGL_DEBUG, LOGD_PLATFORM); - - G_LOCK(log); - - gl.mut.log_level = new_log_level; - for (i = 0; i < G_N_ELEMENTS(new_log_state); i++) - _nm_logging_enabled_state[i] = new_log_state[i]; - - G_UNLOCK(log); - - if (had_platform_debug && !_nm_logging_enabled_lockfree(LOGL_DEBUG, LOGD_PLATFORM)) { - /* when debug logging is enabled, platform will cache all access to - * sysctl. When the user disables debug-logging, we want to clear that - * cache right away. */ - _nm_logging_clear_platform_logging_cache(); - } - - if (unrecognized) - *bad_domains = g_string_free(unrecognized, FALSE); - - return TRUE; -} - -const char * -nm_logging_level_to_string(void) -{ - NM_ASSERT_ON_MAIN_THREAD(); - - return level_desc[gl.imm.log_level].name; -} - -const char * -nm_logging_all_levels_to_string(void) -{ - static GString *str; - - if (G_UNLIKELY(!str)) { - int i; - - str = g_string_new(NULL); - for (i = 0; i < G_N_ELEMENTS(level_desc); i++) { - if (str->len) - g_string_append_c(str, ','); - g_string_append(str, level_desc[i].name); - } - } - - return str->str; -} - -const char * -nm_logging_domains_to_string(void) -{ - NM_ASSERT_ON_MAIN_THREAD(); - - if (G_UNLIKELY(!gl_main.logging_domains_to_string)) { - gl_main.logging_domains_to_string = - _domains_to_string(TRUE, gl.imm.log_level, _nm_logging_enabled_state); - } - - return gl_main.logging_domains_to_string; -} - -static char * -_domains_to_string(gboolean include_level_override, - NMLogLevel log_level, - const NMLogDomain log_state[static _LOGL_N_REAL]) -{ - const LogDesc *diter; - GString * str; - int i; - - /* We don't just return g_strdup() the logging domains that were set during - * nm_logging_setup(), because we want to expand "DEFAULT" and "ALL". - */ - - str = g_string_sized_new(75); - for (diter = &domain_desc[0]; diter->name; diter++) { - /* If it's set for any lower level, it will also be set for LOGL_ERR */ - if (!(diter->num & log_state[LOGL_ERR])) - continue; - - if (str->len) - g_string_append_c(str, ','); - g_string_append(str, diter->name); - - if (!include_level_override) - continue; - - /* Check if it's logging at a lower level than the default. */ - for (i = 0; i < log_level; i++) { - if (diter->num & log_state[i]) { - g_string_append_printf(str, ":%s", level_desc[i].name); - break; - } - } - /* Check if it's logging at a higher level than the default. */ - if (!(diter->num & log_state[log_level])) { - for (i = log_level + 1; i < _LOGL_N_REAL; i++) { - if (diter->num & log_state[i]) { - g_string_append_printf(str, ":%s", level_desc[i].name); - break; - } - } - } - } - return g_string_free(str, FALSE); -} - -static char _all_logging_domains_to_str[273]; - -const char * -nm_logging_all_domains_to_string(void) -{ - static const char *volatile str = NULL; - const char *s; - -again: - s = g_atomic_pointer_get(&str); - if (G_UNLIKELY(!s)) { - static gsize once = 0; - const LogDesc *diter; - gsize buf_l; - char * buf_p; - - if (!g_once_init_enter(&once)) - goto again; - - buf_p = _all_logging_domains_to_str; - buf_l = sizeof(_all_logging_domains_to_str); - - nm_utils_strbuf_append_str(&buf_p, &buf_l, LOGD_DEFAULT_STRING); - for (diter = &domain_desc[0]; diter->name; diter++) { - nm_utils_strbuf_append_c(&buf_p, &buf_l, ','); - nm_utils_strbuf_append_str(&buf_p, &buf_l, diter->name); - if (diter->num == LOGD_DHCP6) - nm_utils_strbuf_append_str(&buf_p, &buf_l, "," LOGD_DHCP_STRING); - else if (diter->num == LOGD_IP6) - nm_utils_strbuf_append_str(&buf_p, &buf_l, "," LOGD_IP_STRING); - } - nm_utils_strbuf_append_str(&buf_p, &buf_l, LOGD_ALL_STRING); - - /* Did you modify the logging domains (or their names)? Adjust the size of - * _all_logging_domains_to_str buffer above to have the exact size. */ - nm_assert(strlen(_all_logging_domains_to_str) == sizeof(_all_logging_domains_to_str) - 1); - nm_assert(buf_l == 1); - - s = _all_logging_domains_to_str; - g_atomic_pointer_set(&str, s); - g_once_init_leave(&once, 1); - } - - return s; -} - -/** - * nm_logging_get_level: - * @domain: find the lowest enabled logging level for the - * given domain. If this is a set of multiple - * domains, the most verbose level will be returned. - * - * Returns: the lowest (most verbose) logging level for the - * give @domain, or %_LOGL_OFF if it is disabled. - **/ -NMLogLevel -nm_logging_get_level(NMLogDomain domain) -{ - NMLogLevel sl = _LOGL_OFF; - - G_STATIC_ASSERT(LOGL_TRACE == 0); - while (sl > LOGL_TRACE && _nm_logging_enabled_lockfree(sl - 1, domain)) - sl--; - return sl; -} - -gboolean -_nm_logging_enabled_locking(NMLogLevel level, NMLogDomain domain) -{ - gboolean v; - - G_LOCK(log); - v = _nm_logging_enabled_lockfree(level, domain); - G_UNLOCK(log); - return v; -} - -gboolean -_nm_log_enabled_impl(gboolean mt_require_locking, NMLogLevel level, NMLogDomain domain) -{ - return nm_logging_enabled_mt(mt_require_locking, level, domain); -} - -#if SYSTEMD_JOURNAL -static void -_iovec_set(struct iovec *iov, const void *str, gsize len) -{ - iov->iov_base = (void *) str; - iov->iov_len = len; -} - -static void -_iovec_set_string(struct iovec *iov, const char *str) -{ - _iovec_set(iov, str, strlen(str)); -} - - #define _iovec_set_string_literal(iov, str) _iovec_set((iov), "" str "", NM_STRLEN(str)) - -_nm_printf(3, 4) static void _iovec_set_format(struct iovec *iov, - char ** iov_free, - const char * format, - ...) -{ - va_list ap; - char * str; - - va_start(ap, format); - str = g_strdup_vprintf(format, ap); - va_end(ap); - - _iovec_set_string(iov, str); - *iov_free = str; -} - - #define _iovec_set_format_a(iov, reserve_extra, format, ...) \ - G_STMT_START \ - { \ - const gsize _size = (reserve_extra) + (NM_STRLEN(format) + 3); \ - char *const _buf = g_alloca(_size); \ - int _len; \ - \ - G_STATIC_ASSERT_EXPR((reserve_extra) + (NM_STRLEN(format) + 3) <= 96); \ - \ - _len = g_snprintf(_buf, _size, "" format "", ##__VA_ARGS__); \ - \ - nm_assert(_len >= 0); \ - nm_assert(_len < _size); \ - nm_assert(_len == strlen(_buf)); \ - \ - _iovec_set((iov), _buf, _len); \ - } \ - G_STMT_END - - #define _iovec_set_format_str_a(iov, max_str_len, format, str_arg) \ - G_STMT_START \ - { \ - const char *_str_arg = (str_arg); \ - \ - nm_assert(_str_arg &&strlen(_str_arg) < (max_str_len)); \ - _iovec_set_format_a((iov), (max_str_len), format, str_arg); \ - } \ - G_STMT_END - -#endif - -void -_nm_log_impl(const char *file, - guint line, - const char *func, - gboolean mt_require_locking, - NMLogLevel level, - NMLogDomain domain, - int error, - const char *ifname, - const char *conn_uuid, - const char *fmt, - ...) -{ - va_list args; - char * msg; - GTimeVal tv; - int errsv; - const NMLogDomain *cur_log_state; - NMLogDomain cur_log_state_copy[_LOGL_N_REAL]; - Global g_copy; - const Global * g; - - if (G_UNLIKELY(mt_require_locking)) { - G_LOCK(log); - /* we evaluate logging-enabled under lock. There is still a race that - * we might log the message below *after* logging was disabled. That means, - * when disabling logging, we might still log messages. */ - if (!_nm_logging_enabled_lockfree(level, domain)) { - G_UNLOCK(log); - return; - } - g_copy = gl.imm; - memcpy(cur_log_state_copy, _nm_logging_enabled_state, sizeof(cur_log_state_copy)); - G_UNLOCK(log); - g = &g_copy; - cur_log_state = cur_log_state_copy; - } else { - NM_ASSERT_ON_MAIN_THREAD(); - if (!_nm_logging_enabled_lockfree(level, domain)) - return; - g = &gl.imm; - cur_log_state = _nm_logging_enabled_state; - } - - (void) cur_log_state; - - errsv = errno; - - /* Make sure that %m maps to the specified error */ - if (error != 0) { - if (error < 0) - error = -error; - errno = error; - } - - va_start(args, fmt); - msg = g_strdup_vprintf(fmt, args); - va_end(args); - -#define MESSAGE_FMT "%s%-7s [%ld.%04ld] %s" -#define MESSAGE_ARG(prefix, tv, msg) \ - prefix, level_desc[level].level_str, (tv).tv_sec, ((tv).tv_usec / 100), (msg) - - g_get_current_time(&tv); - - if (g->debug_stderr) - g_printerr(MESSAGE_FMT "\n", MESSAGE_ARG(g->prefix, tv, msg)); - - switch (g->log_backend) { -#if SYSTEMD_JOURNAL - case LOG_BACKEND_JOURNAL: - { - gint64 now, boottime; - struct iovec iov_data[15]; - struct iovec * iov = iov_data; - char * iov_free_data[5]; - char ** iov_free = iov_free_data; - const LogDesc *diter; - NMLogDomain dom_all; - char s_log_domains_buf[NM_STRLEN("NM_LOG_DOMAINS=") + sizeof(_all_logging_domains_to_str)]; - char *s_log_domains; - gsize l_log_domains; - - now = nm_utils_get_monotonic_timestamp_nsec(); - boottime = nm_utils_monotonic_timestamp_as_boottime(now, 1); - - _iovec_set_format_a(iov++, 30, "PRIORITY=%d", level_desc[level].syslog_level); - _iovec_set_format(iov++, - iov_free++, - "MESSAGE=" MESSAGE_FMT, - MESSAGE_ARG(g->prefix, tv, msg)); - _iovec_set_string(iov++, syslog_identifier_full(g->syslog_identifier)); - _iovec_set_format_a(iov++, 30, "SYSLOG_PID=%ld", (long) getpid()); - - dom_all = domain; - s_log_domains = s_log_domains_buf; - l_log_domains = sizeof(s_log_domains_buf); - - nm_utils_strbuf_append_str(&s_log_domains, &l_log_domains, "NM_LOG_DOMAINS="); - for (diter = &domain_desc[0]; dom_all != 0 && diter->name; diter++) { - if (!NM_FLAGS_ANY(dom_all, diter->num)) - continue; - if (dom_all != domain) - nm_utils_strbuf_append_c(&s_log_domains, &l_log_domains, ','); - nm_utils_strbuf_append_str(&s_log_domains, &l_log_domains, diter->name); - dom_all &= ~diter->num; - } - nm_assert(l_log_domains > 0); - _iovec_set(iov++, s_log_domains_buf, s_log_domains - s_log_domains_buf); - - G_STATIC_ASSERT_EXPR(LOG_FAC(LOG_DAEMON) == 3); - _iovec_set_string_literal(iov++, "SYSLOG_FACILITY=3"); - _iovec_set_format_str_a(iov++, 15, "NM_LOG_LEVEL=%s", level_desc[level].name); - if (func) - _iovec_set_format(iov++, iov_free++, "CODE_FUNC=%s", func); - _iovec_set_format(iov++, iov_free++, "CODE_FILE=%s", file ?: ""); - _iovec_set_format_a(iov++, 20, "CODE_LINE=%u", line); - _iovec_set_format_a(iov++, - 60, - "TIMESTAMP_MONOTONIC=%lld.%06lld", - (long long) (now / NM_UTILS_NSEC_PER_SEC), - (long long) ((now % NM_UTILS_NSEC_PER_SEC) / 1000)); - _iovec_set_format_a(iov++, - 60, - "TIMESTAMP_BOOTTIME=%lld.%06lld", - (long long) (boottime / NM_UTILS_NSEC_PER_SEC), - (long long) ((boottime % NM_UTILS_NSEC_PER_SEC) / 1000)); - if (error != 0) - _iovec_set_format_a(iov++, 30, "ERRNO=%d", error); - if (ifname) - _iovec_set_format(iov++, iov_free++, "NM_DEVICE=%s", ifname); - if (conn_uuid) - _iovec_set_format(iov++, iov_free++, "NM_CONNECTION=%s", conn_uuid); - - nm_assert(iov <= &iov_data[G_N_ELEMENTS(iov_data)]); - nm_assert(iov_free <= &iov_free_data[G_N_ELEMENTS(iov_free_data)]); - - sd_journal_sendv(iov_data, iov - iov_data); - - for (; --iov_free >= iov_free_data;) - g_free(*iov_free); - } break; -#endif - case LOG_BACKEND_SYSLOG: - syslog(level_desc[level].syslog_level, MESSAGE_FMT, MESSAGE_ARG(g->prefix, tv, msg)); - break; - default: - g_log(syslog_identifier_domain(g->syslog_identifier), - level_desc[level].g_log_level, - MESSAGE_FMT, - MESSAGE_ARG(g->prefix, tv, msg)); - break; - } - - g_free(msg); - - errno = errsv; -} - -/*****************************************************************************/ - -void -_nm_utils_monotonic_timestamp_initialized(const struct timespec *tp, - gint64 offset_sec, - gboolean is_boottime) -{ - NM_ASSERT_ON_MAIN_THREAD(); - - if (_nm_logging_enabled_lockfree(LOGL_DEBUG, LOGD_CORE)) { - time_t now = time(NULL); - struct tm tm; - char s[255]; - - strftime(s, sizeof(s), "%Y-%m-%d %H:%M:%S", localtime_r(&now, &tm)); - nm_log_dbg(LOGD_CORE, - "monotonic timestamp started counting 1.%09ld seconds ago with " - "an offset of %lld.0 seconds to %s (local time is %s)", - tp->tv_nsec, - (long long) -offset_sec, - is_boottime ? "CLOCK_BOOTTIME" : "CLOCK_MONOTONIC", - s); - } -} - -/*****************************************************************************/ - -static void -nm_log_handler(const char *log_domain, GLogLevelFlags level, const char *message, gpointer ignored) -{ - int syslog_priority; - - switch (level & G_LOG_LEVEL_MASK) { - case G_LOG_LEVEL_ERROR: - syslog_priority = LOG_CRIT; - break; - case G_LOG_LEVEL_CRITICAL: - syslog_priority = LOG_ERR; - break; - case G_LOG_LEVEL_WARNING: - syslog_priority = LOG_WARNING; - break; - case G_LOG_LEVEL_MESSAGE: - syslog_priority = LOG_NOTICE; - break; - case G_LOG_LEVEL_DEBUG: - syslog_priority = LOG_DEBUG; - break; - case G_LOG_LEVEL_INFO: - default: - syslog_priority = LOG_INFO; - break; - } - - /* we don't need any locking here. The glib log handler gets only registered - * once during nm_logging_init() and the global data is not modified afterwards. */ - nm_assert(gl.imm.init_done); - - if (gl.imm.debug_stderr) - g_printerr("%s%s\n", gl.imm.prefix, message ?: ""); - - switch (gl.imm.log_backend) { -#if SYSTEMD_JOURNAL - case LOG_BACKEND_JOURNAL: - { - gint64 now, boottime; - - now = nm_utils_get_monotonic_timestamp_nsec(); - boottime = nm_utils_monotonic_timestamp_as_boottime(now, 1); - - sd_journal_send("PRIORITY=%d", - syslog_priority, - "MESSAGE=%s%s", - gl.imm.prefix, - message ?: "", - syslog_identifier_full(gl.imm.syslog_identifier), - "SYSLOG_PID=%ld", - (long) getpid(), - "SYSLOG_FACILITY=3", - "GLIB_DOMAIN=%s", - log_domain ?: "", - "GLIB_LEVEL=%d", - (int) (level & G_LOG_LEVEL_MASK), - "TIMESTAMP_MONOTONIC=%lld.%06lld", - (long long) (now / NM_UTILS_NSEC_PER_SEC), - (long long) ((now % NM_UTILS_NSEC_PER_SEC) / 1000), - "TIMESTAMP_BOOTTIME=%lld.%06lld", - (long long) (boottime / NM_UTILS_NSEC_PER_SEC), - (long long) ((boottime % NM_UTILS_NSEC_PER_SEC) / 1000), - NULL); - } break; -#endif - default: - syslog(syslog_priority, "%s%s", gl.imm.prefix, message ?: ""); - break; - } -} - -gboolean -nm_logging_syslog_enabled(void) -{ - NM_ASSERT_ON_MAIN_THREAD(); - - return gl.imm.uses_syslog; -} - -void -nm_logging_init_pre(const char *syslog_identifier, char *prefix_take) -{ - /* this function may be called zero or one times, and only - * - on the main thread - * - not after nm_logging_init(). */ - - NM_ASSERT_ON_MAIN_THREAD(); - - if (gl.imm.init_pre_done) - g_return_if_reached(); - - if (gl.imm.init_done) - g_return_if_reached(); - - if (!_syslog_identifier_valid_domain(syslog_identifier)) - g_return_if_reached(); - - if (!prefix_take || !prefix_take[0]) - g_return_if_reached(); - - G_LOCK(log); - - gl.mut.init_pre_done = TRUE; - - gl.mut.syslog_identifier = g_strdup_printf("SYSLOG_IDENTIFIER=%s", syslog_identifier); - nm_assert(_syslog_identifier_assert(gl.imm.syslog_identifier)); - - /* we pass the allocated string on and never free it. */ - gl.mut.prefix = prefix_take; - - G_UNLOCK(log); -} - -void -nm_logging_init(const char *logging_backend, gboolean debug) -{ - gboolean fetch_monotonic_timestamp = FALSE; - gboolean obsolete_debug_backend = FALSE; - LogBackend x_log_backend; - - /* this function may be called zero or one times, and only on the - * main thread. */ - - NM_ASSERT_ON_MAIN_THREAD(); - - nm_assert(NM_IN_STRSET("" NM_CONFIG_DEFAULT_LOGGING_BACKEND, - NM_LOG_CONFIG_BACKEND_JOURNAL, - NM_LOG_CONFIG_BACKEND_SYSLOG)); - - if (gl.imm.init_done) - g_return_if_reached(); - - if (!logging_backend) - logging_backend = "" NM_CONFIG_DEFAULT_LOGGING_BACKEND; - - if (nm_streq(logging_backend, NM_LOG_CONFIG_BACKEND_DEBUG)) { - /* "debug" was wrongly documented as a valid logging backend. It makes no sense however, - * because printing to stderr only makes sense when not demonizing. Whether to daemonize - * is only controlled via command line arguments (--no-daemon, --debug) and not via the - * logging backend from configuration. - * - * Fall back to the default. */ - logging_backend = "" NM_CONFIG_DEFAULT_LOGGING_BACKEND; - obsolete_debug_backend = TRUE; - } - - G_LOCK(log); - -#if SYSTEMD_JOURNAL - if (!nm_streq(logging_backend, NM_LOG_CONFIG_BACKEND_SYSLOG)) { - x_log_backend = LOG_BACKEND_JOURNAL; - - /* We only log the monotonic-timestamp with structured logging (journal). - * Only in this case, fetch the timestamp. */ - fetch_monotonic_timestamp = TRUE; - } else -#endif - { - x_log_backend = LOG_BACKEND_SYSLOG; - openlog(syslog_identifier_domain(gl.imm.syslog_identifier), LOG_PID, LOG_DAEMON); - } - - gl.mut.init_done = TRUE; - gl.mut.log_backend = x_log_backend; - gl.mut.uses_syslog = TRUE; - gl.mut.debug_stderr = debug; - - g_log_set_handler(syslog_identifier_domain(gl.imm.syslog_identifier), - G_LOG_LEVEL_MASK | G_LOG_FLAG_FATAL | G_LOG_FLAG_RECURSION, - nm_log_handler, - NULL); - - G_UNLOCK(log); - - if (fetch_monotonic_timestamp) { - /* ensure we read a monotonic timestamp. Reading the timestamp the first - * time causes a logging message. We don't want to do that during _nm_log_impl. */ - nm_utils_get_monotonic_timestamp_nsec(); - } - - if (obsolete_debug_backend) - nm_log_dbg(LOGD_CORE, - "config: ignore deprecated logging backend 'debug', fallback to '%s'", - logging_backend); - - if (nm_streq(logging_backend, NM_LOG_CONFIG_BACKEND_SYSLOG)) { - /* good */ - } else if (nm_streq(logging_backend, NM_LOG_CONFIG_BACKEND_JOURNAL)) { -#if !SYSTEMD_JOURNAL - nm_log_warn(LOGD_CORE, - "config: logging backend 'journal' is not available, fallback to 'syslog'"); -#endif - } else { - nm_log_warn(LOGD_CORE, - "config: invalid logging backend '%s', fallback to '%s'", - logging_backend, -#if SYSTEMD_JOURNAL - NM_LOG_CONFIG_BACKEND_JOURNAL -#else - NM_LOG_CONFIG_BACKEND_SYSLOG -#endif - ); - } -} diff --git a/shared/nm-log-core/nm-logging.h b/shared/nm-log-core/nm-logging.h deleted file mode 100644 index d3143d39..00000000 --- a/shared/nm-log-core/nm-logging.h +++ /dev/null @@ -1,189 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-or-later */ -/* - * Copyright (C) 2006 - 2012 Red Hat, Inc. - * Copyright (C) 2006 - 2008 Novell, Inc. - */ - -#ifndef __NETWORKMANAGER_LOGGING_H__ -#define __NETWORKMANAGER_LOGGING_H__ - -#ifdef __NM_TEST_UTILS_H__ - #error nm-test-utils.h must be included as last header -#endif - -#include "nm-glib-aux/nm-logging-fwd.h" - -#define NM_LOG_CONFIG_BACKEND_DEBUG "debug" -#define NM_LOG_CONFIG_BACKEND_SYSLOG "syslog" -#define NM_LOG_CONFIG_BACKEND_JOURNAL "journal" - -#define nm_log_err(domain, ...) nm_log(LOGL_ERR, (domain), NULL, NULL, __VA_ARGS__) -#define nm_log_warn(domain, ...) nm_log(LOGL_WARN, (domain), NULL, NULL, __VA_ARGS__) -#define nm_log_info(domain, ...) nm_log(LOGL_INFO, (domain), NULL, NULL, __VA_ARGS__) -#define nm_log_dbg(domain, ...) nm_log(LOGL_DEBUG, (domain), NULL, NULL, __VA_ARGS__) -#define nm_log_trace(domain, ...) nm_log(LOGL_TRACE, (domain), NULL, NULL, __VA_ARGS__) - -//#define _NM_LOG_FUNC G_STRFUNC -#define _NM_LOG_FUNC NULL - -/* A wrapper for the _nm_log_impl() function that adds call site information. - * Contrary to nm_log(), it unconditionally calls the function without - * checking whether logging for the given level and domain is enabled. */ -#define _nm_log_mt(mt_require_locking, level, domain, error, ifname, con_uuid, ...) \ - G_STMT_START \ - { \ - _nm_log_impl(__FILE__, \ - __LINE__, \ - _NM_LOG_FUNC, \ - (mt_require_locking), \ - (level), \ - (domain), \ - (error), \ - (ifname), \ - (con_uuid), \ - ""__VA_ARGS__); \ - } \ - G_STMT_END - -#define _nm_log(level, domain, error, ifname, con_uuid, ...) \ - _nm_log_mt(!(NM_THREAD_SAFE_ON_MAIN_THREAD), \ - level, \ - domain, \ - error, \ - ifname, \ - con_uuid, \ - __VA_ARGS__) - -/* nm_log() only evaluates its argument list after checking - * whether logging for the given level/domain is enabled. */ -#define nm_log(level, domain, ifname, con_uuid, ...) \ - G_STMT_START \ - { \ - if (nm_logging_enabled((level), (domain))) { \ - _nm_log(level, domain, 0, ifname, con_uuid, __VA_ARGS__); \ - } \ - } \ - G_STMT_END - -#define _nm_log_ptr(level, domain, ifname, con_uuid, self, prefix, ...) \ - nm_log((level), \ - (domain), \ - (ifname), \ - (con_uuid), \ - "%s[" NM_HASH_OBFUSCATE_PTR_FMT "] " _NM_UTILS_MACRO_FIRST(__VA_ARGS__), \ - (prefix) ?: "", \ - NM_HASH_OBFUSCATE_PTR(self) _NM_UTILS_MACRO_REST(__VA_ARGS__)) - -static inline gboolean -_nm_log_ptr_is_debug(NMLogLevel level) -{ - return level <= LOGL_DEBUG; -} - -/* log a message for an object (with providing a generic @self pointer) */ -#define nm_log_ptr(level, domain, ifname, con_uuid, self, prefix, ...) \ - G_STMT_START \ - { \ - if (_nm_log_ptr_is_debug(level)) { \ - _nm_log_ptr((level), (domain), (ifname), (con_uuid), (self), (prefix), __VA_ARGS__); \ - } else { \ - const char *__prefix = (prefix); \ - \ - nm_log((level), \ - (domain), \ - (ifname), \ - (con_uuid), \ - "%s%s" _NM_UTILS_MACRO_FIRST(__VA_ARGS__), \ - __prefix ?: "", \ - __prefix ? " " : "" _NM_UTILS_MACRO_REST(__VA_ARGS__)); \ - } \ - } \ - G_STMT_END - -#define _nm_log_obj(level, domain, ifname, con_uuid, self, prefix, ...) \ - _nm_log_ptr((level), (domain), (ifname), (con_uuid), (self), prefix, __VA_ARGS__) - -/* log a message for an object (with providing a @self pointer to a GObject). - * Contrary to nm_log_ptr(), @self must be a GObject type (or %NULL). - * As of now, nm_log_obj() is identical to nm_log_ptr(), but we might change that */ -#define nm_log_obj(level, domain, ifname, con_uuid, self, prefix, ...) \ - nm_log_ptr((level), (domain), (ifname), (con_uuid), (self), prefix, __VA_ARGS__) - -const char *nm_logging_level_to_string(void); -const char *nm_logging_domains_to_string(void); - -/*****************************************************************************/ - -extern NMLogDomain _nm_logging_enabled_state[_LOGL_N_REAL]; - -static inline gboolean -_nm_logging_enabled_lockfree(NMLogLevel level, NMLogDomain domain) -{ - nm_assert(((guint) level) < G_N_ELEMENTS(_nm_logging_enabled_state)); - return (((guint) level) < G_N_ELEMENTS(_nm_logging_enabled_state)) - && !!(_nm_logging_enabled_state[level] & domain); -} - -gboolean _nm_logging_enabled_locking(NMLogLevel level, NMLogDomain domain); - -static inline gboolean -nm_logging_enabled_mt(gboolean mt_require_locking, NMLogLevel level, NMLogDomain domain) -{ - if (mt_require_locking) - return _nm_logging_enabled_locking(level, domain); - - NM_ASSERT_ON_MAIN_THREAD(); - return _nm_logging_enabled_lockfree(level, domain); -} - -#define nm_logging_enabled(level, domain) \ - nm_logging_enabled_mt(!(NM_THREAD_SAFE_ON_MAIN_THREAD), level, domain) - -/*****************************************************************************/ - -NMLogLevel nm_logging_get_level(NMLogDomain domain); - -const char *nm_logging_all_levels_to_string(void); -const char *nm_logging_all_domains_to_string(void); - -gboolean -nm_logging_setup(const char *level, const char *domains, char **bad_domains, GError **error); - -void nm_logging_init_pre(const char *syslog_identifier, char *prefix_take); - -void nm_logging_init(const char *logging_backend, gboolean debug); - -gboolean nm_logging_syslog_enabled(void); - -/*****************************************************************************/ - -#define __NMLOG_DEFAULT(level, domain, prefix, ...) \ - G_STMT_START \ - { \ - nm_log((level), \ - (domain), \ - NULL, \ - NULL, \ - "%s: " _NM_UTILS_MACRO_FIRST(__VA_ARGS__), \ - (prefix) _NM_UTILS_MACRO_REST(__VA_ARGS__)); \ - } \ - G_STMT_END - -#define __NMLOG_DEFAULT_WITH_ADDR(level, domain, prefix, ...) \ - G_STMT_START \ - { \ - nm_log((level), \ - (domain), \ - NULL, \ - NULL, \ - "%s[" NM_HASH_OBFUSCATE_PTR_FMT "]: " _NM_UTILS_MACRO_FIRST(__VA_ARGS__), \ - (prefix), \ - NM_HASH_OBFUSCATE_PTR(self) _NM_UTILS_MACRO_REST(__VA_ARGS__)); \ - } \ - G_STMT_END - -/*****************************************************************************/ - -extern void _nm_logging_clear_platform_logging_cache(void); - -#endif /* __NETWORKMANAGER_LOGGING_H__ */ diff --git a/shared/nm-platform/nm-netlink.c b/shared/nm-platform/nm-netlink.c deleted file mode 100644 index 7a6d7e04..00000000 --- a/shared/nm-platform/nm-netlink.c +++ /dev/null @@ -1,1518 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-or-later */ -/* - * Copyright (C) 2018 Red Hat, Inc. - */ - -#include "nm-glib-aux/nm-default-glib-i18n-lib.h" - -#include "nm-netlink.h" - -#include <unistd.h> -#include <fcntl.h> - -/*****************************************************************************/ - -#ifndef SOL_NETLINK - #define SOL_NETLINK 270 -#endif - -/*****************************************************************************/ - -#define NL_SOCK_PASSCRED (1 << 1) -#define NL_MSG_PEEK (1 << 3) -#define NL_MSG_PEEK_EXPLICIT (1 << 4) -#define NL_NO_AUTO_ACK (1 << 5) - -#ifndef NETLINK_EXT_ACK - #define NETLINK_EXT_ACK 11 -#endif - -struct nl_msg { - int nm_protocol; - struct sockaddr_nl nm_src; - struct sockaddr_nl nm_dst; - struct ucred nm_creds; - struct nlmsghdr * nm_nlh; - size_t nm_size; - bool nm_creds_has : 1; -}; - -struct nl_sock { - struct sockaddr_nl s_local; - struct sockaddr_nl s_peer; - int s_fd; - int s_proto; - unsigned int s_seq_next; - unsigned int s_seq_expect; - int s_flags; - size_t s_bufsize; -}; - -/*****************************************************************************/ - -NM_UTILS_ENUM2STR_DEFINE(nl_nlmsgtype2str, - int, - NM_UTILS_ENUM2STR(NLMSG_NOOP, "NOOP"), - NM_UTILS_ENUM2STR(NLMSG_ERROR, "ERROR"), - NM_UTILS_ENUM2STR(NLMSG_DONE, "DONE"), - NM_UTILS_ENUM2STR(NLMSG_OVERRUN, "OVERRUN"), ); - -NM_UTILS_FLAGS2STR_DEFINE(nl_nlmsg_flags2str, - int, - NM_UTILS_FLAGS2STR(NLM_F_REQUEST, "REQUEST"), - NM_UTILS_FLAGS2STR(NLM_F_MULTI, "MULTI"), - NM_UTILS_FLAGS2STR(NLM_F_ACK, "ACK"), - NM_UTILS_FLAGS2STR(NLM_F_ECHO, "ECHO"), - NM_UTILS_FLAGS2STR(NLM_F_ROOT, "ROOT"), - NM_UTILS_FLAGS2STR(NLM_F_MATCH, "MATCH"), - NM_UTILS_FLAGS2STR(NLM_F_ATOMIC, "ATOMIC"), - NM_UTILS_FLAGS2STR(NLM_F_REPLACE, "REPLACE"), - NM_UTILS_FLAGS2STR(NLM_F_EXCL, "EXCL"), - NM_UTILS_FLAGS2STR(NLM_F_CREATE, "CREATE"), - NM_UTILS_FLAGS2STR(NLM_F_APPEND, "APPEND"), ); - -/*****************************************************************************/ - -const char * -nl_nlmsghdr_to_str(const struct nlmsghdr *hdr, char *buf, gsize len) -{ - const char *b; - const char *s; - guint flags, flags_before; - const char *prefix; - - if (!nm_utils_to_string_buffer_init_null(hdr, &buf, &len)) - return buf; - - b = buf; - - switch (hdr->nlmsg_type) { - case RTM_GETLINK: - s = "RTM_GETLINK"; - break; - case RTM_NEWLINK: - s = "RTM_NEWLINK"; - break; - case RTM_DELLINK: - s = "RTM_DELLINK"; - break; - case RTM_SETLINK: - s = "RTM_SETLINK"; - break; - case RTM_GETADDR: - s = "RTM_GETADDR"; - break; - case RTM_NEWADDR: - s = "RTM_NEWADDR"; - break; - case RTM_DELADDR: - s = "RTM_DELADDR"; - break; - case RTM_GETROUTE: - s = "RTM_GETROUTE"; - break; - case RTM_NEWROUTE: - s = "RTM_NEWROUTE"; - break; - case RTM_DELROUTE: - s = "RTM_DELROUTE"; - break; - case RTM_GETRULE: - s = "RTM_GETRULE"; - break; - case RTM_NEWRULE: - s = "RTM_NEWRULE"; - break; - case RTM_DELRULE: - s = "RTM_DELRULE"; - break; - case RTM_GETQDISC: - s = "RTM_GETQDISC"; - break; - case RTM_NEWQDISC: - s = "RTM_NEWQDISC"; - break; - case RTM_DELQDISC: - s = "RTM_DELQDISC"; - break; - case RTM_GETTFILTER: - s = "RTM_GETTFILTER"; - break; - case RTM_NEWTFILTER: - s = "RTM_NEWTFILTER"; - break; - case RTM_DELTFILTER: - s = "RTM_DELTFILTER"; - break; - case NLMSG_NOOP: - s = "NLMSG_NOOP"; - break; - case NLMSG_ERROR: - s = "NLMSG_ERROR"; - break; - case NLMSG_DONE: - s = "NLMSG_DONE"; - break; - case NLMSG_OVERRUN: - s = "NLMSG_OVERRUN"; - break; - default: - s = NULL; - break; - } - - if (s) - nm_utils_strbuf_append_str(&buf, &len, s); - else - nm_utils_strbuf_append(&buf, &len, "(%u)", (unsigned) hdr->nlmsg_type); - - flags = hdr->nlmsg_flags; - - if (!flags) { - nm_utils_strbuf_append_str(&buf, &len, ", flags 0"); - goto flags_done; - } - -#define _F(f, n) \ - G_STMT_START \ - { \ - if (NM_FLAGS_ALL(flags, f)) { \ - flags &= ~(f); \ - nm_utils_strbuf_append(&buf, &len, "%s%s", prefix, n); \ - if (!flags) \ - goto flags_done; \ - prefix = ","; \ - } \ - } \ - G_STMT_END - - prefix = ", flags "; - flags_before = flags; - _F(NLM_F_REQUEST, "request"); - _F(NLM_F_MULTI, "multi"); - _F(NLM_F_ACK, "ack"); - _F(NLM_F_ECHO, "echo"); - _F(NLM_F_DUMP_INTR, "dump_intr"); - _F(0x20 /*NLM_F_DUMP_FILTERED*/, "dump_filtered"); - - if (flags_before != flags) - prefix = ";"; - - switch (hdr->nlmsg_type) { - case RTM_NEWLINK: - case RTM_NEWADDR: - case RTM_NEWROUTE: - case RTM_NEWQDISC: - case RTM_NEWTFILTER: - _F(NLM_F_REPLACE, "replace"); - _F(NLM_F_EXCL, "excl"); - _F(NLM_F_CREATE, "create"); - _F(NLM_F_APPEND, "append"); - break; - case RTM_GETLINK: - case RTM_GETADDR: - case RTM_GETROUTE: - case RTM_DELQDISC: - case RTM_DELTFILTER: - _F(NLM_F_DUMP, "dump"); - _F(NLM_F_ROOT, "root"); - _F(NLM_F_MATCH, "match"); - _F(NLM_F_ATOMIC, "atomic"); - break; - } - -#undef _F - - if (flags_before != flags) - prefix = ";"; - nm_utils_strbuf_append(&buf, &len, "%s0x%04x", prefix, flags); - -flags_done: - - nm_utils_strbuf_append(&buf, &len, ", seq %u", (unsigned) hdr->nlmsg_seq); - - return b; -} - -/*****************************************************************************/ - -struct nlmsghdr * -nlmsg_hdr(struct nl_msg *n) -{ - return n->nm_nlh; -} - -void * -nlmsg_reserve(struct nl_msg *n, size_t len, int pad) -{ - char * buf = (char *) n->nm_nlh; - size_t nlmsg_len = n->nm_nlh->nlmsg_len; - size_t tlen; - - nm_assert(pad >= 0); - - if (len > n->nm_size) - return NULL; - - tlen = pad ? ((len + (pad - 1)) & ~(pad - 1)) : len; - - if ((tlen + nlmsg_len) > n->nm_size) - return NULL; - - buf += nlmsg_len; - n->nm_nlh->nlmsg_len += tlen; - - if (tlen > len) - memset(buf + len, 0, tlen - len); - - return buf; -} - -/*****************************************************************************/ - -struct nlattr * -nla_reserve(struct nl_msg *msg, int attrtype, int attrlen) -{ - struct nlattr *nla; - int tlen; - - if (attrlen < 0) - return NULL; - - tlen = NLMSG_ALIGN(msg->nm_nlh->nlmsg_len) + nla_total_size(attrlen); - - if (tlen > msg->nm_size) - return NULL; - - nla = (struct nlattr *) nlmsg_tail(msg->nm_nlh); - nla->nla_type = attrtype; - nla->nla_len = nla_attr_size(attrlen); - - if (attrlen) - memset((unsigned char *) nla + nla->nla_len, 0, nla_padlen(attrlen)); - msg->nm_nlh->nlmsg_len = tlen; - - return nla; -} - -/*****************************************************************************/ - -struct nl_msg * -nlmsg_alloc_size(size_t len) -{ - struct nl_msg *nm; - - if (len < sizeof(struct nlmsghdr)) - len = sizeof(struct nlmsghdr); - - nm = g_slice_new(struct nl_msg); - *nm = (struct nl_msg){ - .nm_protocol = -1, - .nm_size = len, - .nm_nlh = g_malloc0(len), - }; - nm->nm_nlh->nlmsg_len = nlmsg_total_size(0); - return nm; -} - -/** - * Allocate a new netlink message with the default maximum payload size. - * - * Allocates a new netlink message without any further payload. The - * maximum payload size defaults to PAGESIZE or as otherwise specified - * with nlmsg_set_default_size(). - * - * @return Newly allocated netlink message or NULL. - */ -struct nl_msg * -nlmsg_alloc(void) -{ - return nlmsg_alloc_size(nm_utils_getpagesize()); -} - -struct nl_msg * -nlmsg_alloc_convert(struct nlmsghdr *hdr) -{ - struct nl_msg *nm; - - nm = nlmsg_alloc_size(NLMSG_ALIGN(hdr->nlmsg_len)); - memcpy(nm->nm_nlh, hdr, hdr->nlmsg_len); - return nm; -} - -struct nl_msg * -nlmsg_alloc_simple(int nlmsgtype, int flags) -{ - struct nl_msg *nm; - struct nlmsghdr *new; - - nm = nlmsg_alloc(); - new = nm->nm_nlh; - new->nlmsg_type = nlmsgtype; - new->nlmsg_flags = flags; - return nm; -} - -void -nlmsg_free(struct nl_msg *msg) -{ - if (!msg) - return; - - g_free(msg->nm_nlh); - g_slice_free(struct nl_msg, msg); -} - -/*****************************************************************************/ - -int -nlmsg_append(struct nl_msg *n, const void *data, size_t len, int pad) -{ - void *tmp; - - nm_assert(n); - nm_assert(data); - nm_assert(len > 0); - nm_assert(pad >= 0); - - tmp = nlmsg_reserve(n, len, pad); - if (tmp == NULL) - return -ENOMEM; - - memcpy(tmp, data, len); - return 0; -} - -/*****************************************************************************/ - -int -nlmsg_parse(struct nlmsghdr * nlh, - int hdrlen, - struct nlattr * tb[], - int maxtype, - const struct nla_policy *policy) -{ - if (!nlmsg_valid_hdr(nlh, hdrlen)) - return -NME_NL_MSG_TOOSHORT; - - return nla_parse(tb, maxtype, nlmsg_attrdata(nlh, hdrlen), nlmsg_attrlen(nlh, hdrlen), policy); -} - -struct nlmsghdr * -nlmsg_put(struct nl_msg *n, uint32_t pid, uint32_t seq, int type, int payload, int flags) -{ - struct nlmsghdr *nlh; - - if (n->nm_nlh->nlmsg_len < NLMSG_HDRLEN) - g_return_val_if_reached(NULL); - - nlh = (struct nlmsghdr *) n->nm_nlh; - nlh->nlmsg_type = type; - nlh->nlmsg_flags = flags; - nlh->nlmsg_pid = pid; - nlh->nlmsg_seq = seq; - - if (payload > 0 && nlmsg_reserve(n, payload, NLMSG_ALIGNTO) == NULL) - return NULL; - - return nlh; -} - -size_t -nla_strlcpy(char *dst, const struct nlattr *nla, size_t dstsize) -{ - const char *src; - size_t srclen; - size_t len; - - /* - Always writes @dstsize bytes to @dst - * - Copies the first non-NUL characters to @dst. - * Any characters after the first NUL bytes in @nla are ignored. - * - If the string @nla is longer than @dstsize, the string - * gets truncated. @dst will always be NUL terminated. */ - - if (G_UNLIKELY(dstsize <= 1)) { - if (dstsize == 1) - dst[0] = '\0'; - if (nla && (srclen = nla_len(nla)) > 0) - return strnlen(nla_data(nla), srclen); - return 0; - } - - nm_assert(dst); - - if (nla) { - srclen = nla_len(nla); - if (srclen > 0) { - src = nla_data(nla); - srclen = strnlen(src, srclen); - if (srclen > 0) { - len = NM_MIN(dstsize - 1, srclen); - memcpy(dst, src, len); - memset(&dst[len], 0, dstsize - len); - return srclen; - } - } - } - - memset(dst, 0, dstsize); - return 0; -} - -size_t -nla_memcpy(void *dst, const struct nlattr *nla, size_t dstsize) -{ - size_t len; - int srclen; - - if (!nla) - return 0; - - srclen = nla_len(nla); - - if (srclen <= 0) { - nm_assert(srclen == 0); - return 0; - } - - len = NM_MIN((size_t) srclen, dstsize); - if (len > 0) { - /* there is a crucial difference between nla_strlcpy() and nla_memcpy(). - * The former always write @dstsize bytes (akin to strncpy()), here, we only - * write the bytes that we actually have (leaving the remainder undefined). */ - memcpy(dst, nla_data(nla), len); - } - - return srclen; -} - -int -nla_put(struct nl_msg *msg, int attrtype, int datalen, const void *data) -{ - struct nlattr *nla; - - nla = nla_reserve(msg, attrtype, datalen); - if (!nla) { - if (datalen < 0) - g_return_val_if_reached(-NME_BUG); - - return -ENOMEM; - } - - if (datalen > 0) - memcpy(nla_data(nla), data, datalen); - - return 0; -} - -struct nlattr * -nla_find(const struct nlattr *head, int len, int attrtype) -{ - const struct nlattr *nla; - int rem; - - nla_for_each_attr (nla, head, len, rem) { - if (nla_type(nla) == attrtype) - return (struct nlattr *) nla; - } - - return NULL; -} - -void -nla_nest_cancel(struct nl_msg *msg, const struct nlattr *attr) -{ - ssize_t len; - - len = (char *) nlmsg_tail(msg->nm_nlh) - (char *) attr; - if (len < 0) - g_return_if_reached(); - else if (len > 0) { - msg->nm_nlh->nlmsg_len -= len; - memset(nlmsg_tail(msg->nm_nlh), 0, len); - } -} - -struct nlattr * -nla_nest_start(struct nl_msg *msg, int attrtype) -{ - struct nlattr *start = (struct nlattr *) nlmsg_tail(msg->nm_nlh); - - if (nla_put(msg, NLA_F_NESTED | attrtype, 0, NULL) < 0) - return NULL; - - return start; -} - -static int -_nest_end(struct nl_msg *msg, struct nlattr *start, int keep_empty) -{ - size_t pad, len; - - len = (char *) nlmsg_tail(msg->nm_nlh) - (char *) start; - - if (len > USHRT_MAX || (!keep_empty && len == NLA_HDRLEN)) { - /* - * Max nlattr size exceeded or empty nested attribute, trim the - * attribute header again - */ - nla_nest_cancel(msg, start); - - /* Return error only if nlattr size was exceeded */ - return (len == NLA_HDRLEN) ? 0 : -NME_NL_ATTRSIZE; - } - - start->nla_len = len; - - pad = NLMSG_ALIGN(msg->nm_nlh->nlmsg_len) - msg->nm_nlh->nlmsg_len; - if (pad > 0) { - /* - * Data inside attribute does not end at a alignment boundary. - * Pad accordingly and account for the additional space in - * the message. nlmsg_reserve() may never fail in this situation, - * the allocate message buffer must be a multiple of NLMSG_ALIGNTO. - */ - if (!nlmsg_reserve(msg, pad, 0)) - g_return_val_if_reached(-NME_BUG); - } - - return 0; -} - -int -nla_nest_end(struct nl_msg *msg, struct nlattr *start) -{ - return _nest_end(msg, start, 0); -} - -static const uint16_t nla_attr_minlen[NLA_TYPE_MAX + 1] = { - [NLA_U8] = sizeof(uint8_t), - [NLA_U16] = sizeof(uint16_t), - [NLA_U32] = sizeof(uint32_t), - [NLA_U64] = sizeof(uint64_t), - [NLA_STRING] = 1, - [NLA_FLAG] = 0, -}; - -static int -validate_nla(const struct nlattr *nla, int maxtype, const struct nla_policy *policy) -{ - const struct nla_policy *pt; - unsigned int minlen = 0; - int type = nla_type(nla); - - if (type < 0 || type > maxtype) - return 0; - - pt = &policy[type]; - - if (pt->type > NLA_TYPE_MAX) - g_return_val_if_reached(-NME_BUG); - - if (pt->minlen) - minlen = pt->minlen; - else if (pt->type != NLA_UNSPEC) - minlen = nla_attr_minlen[pt->type]; - - if (nla_len(nla) < minlen) - return -NME_UNSPEC; - - if (pt->maxlen && nla_len(nla) > pt->maxlen) - return -NME_UNSPEC; - - if (pt->type == NLA_STRING) { - const char *data; - - nm_assert(minlen > 0); - - data = nla_data(nla); - if (data[nla_len(nla) - 1] != '\0') - return -NME_UNSPEC; - } - - return 0; -} - -int -nla_parse(struct nlattr * tb[], - int maxtype, - struct nlattr * head, - int len, - const struct nla_policy *policy) -{ - struct nlattr *nla; - int rem, nmerr; - - memset(tb, 0, sizeof(struct nlattr *) * (maxtype + 1)); - - nla_for_each_attr (nla, head, len, rem) { - int type = nla_type(nla); - - if (type > maxtype) - continue; - - if (policy) { - nmerr = validate_nla(nla, maxtype, policy); - if (nmerr < 0) - return nmerr; - } - - tb[type] = nla; - } - - return 0; -} - -/*****************************************************************************/ - -int -nlmsg_get_proto(struct nl_msg *msg) -{ - return msg->nm_protocol; -} - -void -nlmsg_set_proto(struct nl_msg *msg, int protocol) -{ - msg->nm_protocol = protocol; -} - -void -nlmsg_set_src(struct nl_msg *msg, struct sockaddr_nl *addr) -{ - memcpy(&msg->nm_src, addr, sizeof(*addr)); -} - -struct ucred * -nlmsg_get_creds(struct nl_msg *msg) -{ - if (msg->nm_creds_has) - return &msg->nm_creds; - return NULL; -} - -void -nlmsg_set_creds(struct nl_msg *msg, struct ucred *creds) -{ - if (creds) { - memcpy(&msg->nm_creds, creds, sizeof(*creds)); - msg->nm_creds_has = TRUE; - } else - msg->nm_creds_has = FALSE; -} - -/*****************************************************************************/ - -void * -genlmsg_put(struct nl_msg *msg, - uint32_t port, - uint32_t seq, - int family, - int hdrlen, - int flags, - uint8_t cmd, - uint8_t version) -{ - struct nlmsghdr * nlh; - struct genlmsghdr hdr = { - .cmd = cmd, - .version = version, - }; - - nlh = nlmsg_put(msg, port, seq, family, GENL_HDRLEN + hdrlen, flags); - if (nlh == NULL) - return NULL; - - memcpy(nlmsg_data(nlh), &hdr, sizeof(hdr)); - - return (char *) nlmsg_data(nlh) + GENL_HDRLEN; -} - -void * -genlmsg_data(const struct genlmsghdr *gnlh) -{ - return ((unsigned char *) gnlh + GENL_HDRLEN); -} - -void * -genlmsg_user_hdr(const struct genlmsghdr *gnlh) -{ - return genlmsg_data(gnlh); -} - -struct genlmsghdr * -genlmsg_hdr(struct nlmsghdr *nlh) -{ - return nlmsg_data(nlh); -} - -void * -genlmsg_user_data(const struct genlmsghdr *gnlh, const int hdrlen) -{ - return (char *) genlmsg_user_hdr(gnlh) + NLMSG_ALIGN(hdrlen); -} - -struct nlattr * -genlmsg_attrdata(const struct genlmsghdr *gnlh, int hdrlen) -{ - return genlmsg_user_data(gnlh, hdrlen); -} - -int -genlmsg_len(const struct genlmsghdr *gnlh) -{ - const struct nlmsghdr *nlh; - - nlh = (const struct nlmsghdr *) ((const unsigned char *) gnlh - NLMSG_HDRLEN); - return (nlh->nlmsg_len - GENL_HDRLEN - NLMSG_HDRLEN); -} - -int -genlmsg_attrlen(const struct genlmsghdr *gnlh, int hdrlen) -{ - return genlmsg_len(gnlh) - NLMSG_ALIGN(hdrlen); -} - -int -genlmsg_valid_hdr(struct nlmsghdr *nlh, int hdrlen) -{ - struct genlmsghdr *ghdr; - - if (!nlmsg_valid_hdr(nlh, GENL_HDRLEN)) - return 0; - - ghdr = nlmsg_data(nlh); - if (genlmsg_len(ghdr) < NLMSG_ALIGN(hdrlen)) - return 0; - - return 1; -} - -int -genlmsg_parse(struct nlmsghdr * nlh, - int hdrlen, - struct nlattr * tb[], - int maxtype, - const struct nla_policy *policy) -{ - struct genlmsghdr *ghdr; - - if (!genlmsg_valid_hdr(nlh, hdrlen)) - return -NME_NL_MSG_TOOSHORT; - - ghdr = nlmsg_data(nlh); - return nla_parse(tb, - maxtype, - genlmsg_attrdata(ghdr, hdrlen), - genlmsg_attrlen(ghdr, hdrlen), - policy); -} - -static int -_genl_parse_getfamily(struct nl_msg *msg, void *arg) -{ - static const struct nla_policy ctrl_policy[] = { - [CTRL_ATTR_FAMILY_ID] = {.type = NLA_U16}, - [CTRL_ATTR_FAMILY_NAME] = {.type = NLA_STRING, .maxlen = GENL_NAMSIZ}, - [CTRL_ATTR_VERSION] = {.type = NLA_U32}, - [CTRL_ATTR_HDRSIZE] = {.type = NLA_U32}, - [CTRL_ATTR_MAXATTR] = {.type = NLA_U32}, - [CTRL_ATTR_OPS] = {.type = NLA_NESTED}, - [CTRL_ATTR_MCAST_GROUPS] = {.type = NLA_NESTED}, - }; - struct nlattr * tb[G_N_ELEMENTS(ctrl_policy)]; - struct nlmsghdr *nlh = nlmsg_hdr(msg); - gint32 * response_data = arg; - - if (genlmsg_parse_arr(nlh, 0, tb, ctrl_policy) < 0) - return NL_SKIP; - - if (tb[CTRL_ATTR_FAMILY_ID]) - *response_data = nla_get_u16(tb[CTRL_ATTR_FAMILY_ID]); - - return NL_STOP; -} - -int -genl_ctrl_resolve(struct nl_sock *sk, const char *name) -{ - nm_auto_nlmsg struct nl_msg *msg = NULL; - int nmerr; - gint32 response_data = -1; - const struct nl_cb cb = { - .valid_cb = _genl_parse_getfamily, - .valid_arg = &response_data, - }; - - msg = nlmsg_alloc(); - - if (!genlmsg_put(msg, NL_AUTO_PORT, NL_AUTO_SEQ, GENL_ID_CTRL, 0, 0, CTRL_CMD_GETFAMILY, 1)) - return -ENOMEM; - - nmerr = nla_put_string(msg, CTRL_ATTR_FAMILY_NAME, name); - if (nmerr < 0) - return nmerr; - - nmerr = nl_send_auto(sk, msg); - if (nmerr < 0) - return nmerr; - - nmerr = nl_recvmsgs(sk, &cb); - if (nmerr < 0) - return nmerr; - - /* If search was successful, request may be ACKed after data */ - nmerr = nl_wait_for_ack(sk, NULL); - if (nmerr < 0) - return nmerr; - - if (response_data < 0) - return -NME_UNSPEC; - - return response_data; -} - -/*****************************************************************************/ - -struct nl_sock * -nl_socket_alloc(void) -{ - struct nl_sock *sk; - - sk = g_slice_new0(struct nl_sock); - - sk->s_fd = -1; - sk->s_local.nl_family = AF_NETLINK; - sk->s_peer.nl_family = AF_NETLINK; - sk->s_seq_expect = sk->s_seq_next = time(NULL); - - return sk; -} - -void -nl_socket_free(struct nl_sock *sk) -{ - if (!sk) - return; - - if (sk->s_fd >= 0) - nm_close(sk->s_fd); - g_slice_free(struct nl_sock, sk); -} - -int -nl_socket_get_fd(const struct nl_sock *sk) -{ - return sk->s_fd; -} - -uint32_t -nl_socket_get_local_port(const struct nl_sock *sk) -{ - return sk->s_local.nl_pid; -} - -size_t -nl_socket_get_msg_buf_size(struct nl_sock *sk) -{ - return sk->s_bufsize; -} - -int -nl_socket_set_passcred(struct nl_sock *sk, int state) -{ - int err; - - if (sk->s_fd == -1) - return -NME_NL_BAD_SOCK; - - err = setsockopt(sk->s_fd, SOL_SOCKET, SO_PASSCRED, &state, sizeof(state)); - if (err < 0) - return -nm_errno_from_native(errno); - - if (state) - sk->s_flags |= NL_SOCK_PASSCRED; - else - sk->s_flags &= ~NL_SOCK_PASSCRED; - - return 0; -} - -int -nl_socket_set_msg_buf_size(struct nl_sock *sk, size_t bufsize) -{ - sk->s_bufsize = bufsize; - - return 0; -} - -struct sockaddr_nl * -nlmsg_get_dst(struct nl_msg *msg) -{ - return &msg->nm_dst; -} - -int -nl_socket_set_nonblocking(const struct nl_sock *sk) -{ - if (sk->s_fd == -1) - return -NME_NL_BAD_SOCK; - - if (fcntl(sk->s_fd, F_SETFL, O_NONBLOCK) < 0) - return -nm_errno_from_native(errno); - - return 0; -} - -int -nl_socket_set_buffer_size(struct nl_sock *sk, int rxbuf, int txbuf) -{ - int err; - - if (rxbuf <= 0) - rxbuf = 32768; - - if (txbuf <= 0) - txbuf = 32768; - - if (sk->s_fd == -1) - return -NME_NL_BAD_SOCK; - - err = setsockopt(sk->s_fd, SOL_SOCKET, SO_SNDBUF, &txbuf, sizeof(txbuf)); - if (err < 0) { - return -nm_errno_from_native(errno); - } - - err = setsockopt(sk->s_fd, SOL_SOCKET, SO_RCVBUF, &rxbuf, sizeof(rxbuf)); - if (err < 0) { - return -nm_errno_from_native(errno); - } - - return 0; -} - -int -nl_socket_add_memberships(struct nl_sock *sk, int group, ...) -{ - int err; - va_list ap; - - if (sk->s_fd == -1) - return -NME_NL_BAD_SOCK; - - va_start(ap, group); - - while (group != 0) { - if (group < 0) { - va_end(ap); - g_return_val_if_reached(-NME_BUG); - } - - err = setsockopt(sk->s_fd, SOL_NETLINK, NETLINK_ADD_MEMBERSHIP, &group, sizeof(group)); - if (err < 0) { - int errsv = errno; - - va_end(ap); - return -nm_errno_from_native(errsv); - } - - group = va_arg(ap, int); - } - - va_end(ap); - - return 0; -} - -int -nl_socket_set_ext_ack(struct nl_sock *sk, gboolean enable) -{ - int err, val; - - if (sk->s_fd == -1) - return -NME_NL_BAD_SOCK; - - val = !!enable; - err = setsockopt(sk->s_fd, SOL_NETLINK, NETLINK_EXT_ACK, &val, sizeof(val)); - if (err < 0) - return -nm_errno_from_native(errno); - - return 0; -} - -void -nl_socket_disable_msg_peek(struct nl_sock *sk) -{ - sk->s_flags |= NL_MSG_PEEK_EXPLICIT; - sk->s_flags &= ~NL_MSG_PEEK; -} - -int -nl_connect(struct nl_sock *sk, int protocol) -{ - int err, nmerr; - socklen_t addrlen; - struct sockaddr_nl local = {0}; - - if (sk->s_fd != -1) - return -NME_NL_BAD_SOCK; - - sk->s_fd = socket(AF_NETLINK, SOCK_RAW | SOCK_CLOEXEC, protocol); - if (sk->s_fd < 0) { - nmerr = -nm_errno_from_native(errno); - goto errout; - } - - nmerr = nl_socket_set_buffer_size(sk, 0, 0); - if (nmerr < 0) - goto errout; - - nm_assert(sk->s_local.nl_pid == 0); - - err = bind(sk->s_fd, (struct sockaddr *) &sk->s_local, sizeof(sk->s_local)); - if (err != 0) { - nmerr = -nm_errno_from_native(errno); - goto errout; - } - - addrlen = sizeof(local); - err = getsockname(sk->s_fd, (struct sockaddr *) &local, &addrlen); - if (err < 0) { - nmerr = -nm_errno_from_native(errno); - goto errout; - } - - if (addrlen != sizeof(local)) { - nmerr = -NME_UNSPEC; - goto errout; - } - - if (local.nl_family != AF_NETLINK) { - nmerr = -NME_UNSPEC; - goto errout; - } - - sk->s_local = local; - sk->s_proto = protocol; - - return 0; - -errout: - if (sk->s_fd != -1) { - close(sk->s_fd); - sk->s_fd = -1; - } - return nmerr; -} - -/*****************************************************************************/ - -static void -_cb_init(struct nl_cb *dst, const struct nl_cb *src) -{ - nm_assert(dst); - - if (src) - *dst = *src; - else - memset(dst, 0, sizeof(*dst)); -} - -static int -ack_wait_handler(struct nl_msg *msg, void *arg) -{ - return NL_STOP; -} - -int -nl_wait_for_ack(struct nl_sock *sk, const struct nl_cb *cb) -{ - struct nl_cb cb2; - - _cb_init(&cb2, cb); - cb2.ack_cb = ack_wait_handler; - return nl_recvmsgs(sk, &cb2); -} - -#define NL_CB_CALL(cb, type, msg) \ - do { \ - const struct nl_cb *_cb = (cb); \ - \ - if (_cb && _cb->type##_cb) { \ - /* the returned value here must be either a negative - * netlink error number, or one of NL_SKIP, NL_STOP, NL_OK. */ \ - nmerr = _cb->type##_cb((msg), _cb->type##_arg); \ - switch (nmerr) { \ - case NL_OK: \ - nm_assert(nmerr == 0); \ - break; \ - case NL_SKIP: \ - goto skip; \ - case NL_STOP: \ - goto stop; \ - default: \ - if (nmerr >= 0) { \ - nm_assert_not_reached(); \ - nmerr = -NME_BUG; \ - } \ - goto out; \ - } \ - } \ - } while (0) - -int -nl_recvmsgs(struct nl_sock *sk, const struct nl_cb *cb) -{ - int n, nmerr = 0, multipart = 0, interrupted = 0, nrecv = 0; - gs_free unsigned char *buf = NULL; - struct nlmsghdr * hdr; - struct sockaddr_nl nla = {0}; - struct ucred creds; - gboolean creds_has; - -continue_reading: - n = nl_recv(sk, &nla, &buf, &creds, &creds_has); - if (n <= 0) - return n; - - hdr = (struct nlmsghdr *) buf; - while (nlmsg_ok(hdr, n)) { - nm_auto_nlmsg struct nl_msg *msg = NULL; - - msg = nlmsg_alloc_convert(hdr); - - nlmsg_set_proto(msg, sk->s_proto); - nlmsg_set_src(msg, &nla); - nlmsg_set_creds(msg, creds_has ? &creds : NULL); - - nrecv++; - - /* Only do sequence checking if auto-ack mode is enabled */ - if (!(sk->s_flags & NL_NO_AUTO_ACK)) { - if (hdr->nlmsg_seq != sk->s_seq_expect) { - nmerr = -NME_NL_SEQ_MISMATCH; - goto out; - } - } - - if (hdr->nlmsg_type == NLMSG_DONE || hdr->nlmsg_type == NLMSG_ERROR - || hdr->nlmsg_type == NLMSG_NOOP || hdr->nlmsg_type == NLMSG_OVERRUN) { - /* We can't check for !NLM_F_MULTI since some netlink - * users in the kernel are broken. */ - sk->s_seq_expect++; - } - - if (hdr->nlmsg_flags & NLM_F_MULTI) - multipart = 1; - - if (hdr->nlmsg_flags & NLM_F_DUMP_INTR) { - /* - * We have to continue reading to clear - * all messages until a NLMSG_DONE is - * received and report the inconsistency. - */ - interrupted = 1; - } - - /* messages terminates a multipart message, this is - * usually the end of a message and therefore we slip - * out of the loop by default. the user may overrule - * this action by skipping this packet. */ - if (hdr->nlmsg_type == NLMSG_DONE) { - multipart = 0; - NL_CB_CALL(cb, finish, msg); - } - - /* Message to be ignored, the default action is to - * skip this message if no callback is specified. The - * user may overrule this action by returning - * NL_PROCEED. */ - else if (hdr->nlmsg_type == NLMSG_NOOP) - goto skip; - - /* Data got lost, report back to user. The default action is to - * quit parsing. The user may overrule this action by returning - * NL_SKIP or NL_PROCEED (dangerous) */ - else if (hdr->nlmsg_type == NLMSG_OVERRUN) { - nmerr = -NME_NL_MSG_OVERFLOW; - goto out; - } - - /* Message carries a nlmsgerr */ - else if (hdr->nlmsg_type == NLMSG_ERROR) { - struct nlmsgerr *e = nlmsg_data(hdr); - - if (hdr->nlmsg_len < nlmsg_size(sizeof(*e))) { - /* Truncated error message, the default action - * is to stop parsing. The user may overrule - * this action by returning NL_SKIP or - * NL_PROCEED (dangerous) */ - nmerr = -NME_NL_MSG_TRUNC; - goto out; - } - if (e->error) { - /* Error message reported back from kernel. */ - if (cb && cb->err_cb) { - /* the returned value here must be either a negative - * netlink error number, or one of NL_SKIP, NL_STOP, NL_OK. */ - nmerr = cb->err_cb(&nla, e, cb->err_arg); - if (nmerr < 0) - goto out; - else if (nmerr == NL_SKIP) - goto skip; - else if (nmerr == NL_STOP) { - nmerr = -nm_errno_from_native(e->error); - goto out; - } - nm_assert(nmerr == NL_OK); - } else { - nmerr = -nm_errno_from_native(e->error); - goto out; - } - } else - NL_CB_CALL(cb, ack, msg); - } else { - /* Valid message (not checking for MULTIPART bit to - * get along with broken kernels. NL_SKIP has no - * effect on this. */ - NL_CB_CALL(cb, valid, msg); - } -skip: - nmerr = 0; - hdr = nlmsg_next(hdr, &n); - } - - if (multipart) { - /* Multipart message not yet complete, continue reading */ - nm_clear_g_free(&buf); - - nmerr = 0; - goto continue_reading; - } - -stop: - nmerr = 0; - -out: - if (interrupted) - nmerr = -NME_NL_DUMP_INTR; - - nm_assert(nmerr <= 0); - return nmerr ?: nrecv; -} - -int -nl_sendmsg(struct nl_sock *sk, struct nl_msg *msg, struct msghdr *hdr) -{ - int ret; - - if (sk->s_fd < 0) - return -NME_NL_BAD_SOCK; - - nlmsg_set_src(msg, &sk->s_local); - - ret = sendmsg(sk->s_fd, hdr, 0); - if (ret < 0) - return -nm_errno_from_native(errno); - - return ret; -} - -int -nl_send_iovec(struct nl_sock *sk, struct nl_msg *msg, struct iovec *iov, unsigned iovlen) -{ - struct sockaddr_nl *dst; - struct ucred * creds; - struct msghdr hdr = { - .msg_name = (void *) &sk->s_peer, - .msg_namelen = sizeof(struct sockaddr_nl), - .msg_iov = iov, - .msg_iovlen = iovlen, - }; - char buf[CMSG_SPACE(sizeof(struct ucred))]; - - /* Overwrite destination if specified in the message itself, defaults - * to the peer address of the socket. - */ - dst = nlmsg_get_dst(msg); - if (dst->nl_family == AF_NETLINK) - hdr.msg_name = dst; - - /* Add credentials if present. */ - creds = nlmsg_get_creds(msg); - if (creds != NULL) { - struct cmsghdr *cmsg; - - hdr.msg_control = buf; - hdr.msg_controllen = sizeof(buf); - - cmsg = CMSG_FIRSTHDR(&hdr); - cmsg->cmsg_level = SOL_SOCKET; - cmsg->cmsg_type = SCM_CREDENTIALS; - cmsg->cmsg_len = CMSG_LEN(sizeof(struct ucred)); - memcpy(CMSG_DATA(cmsg), creds, sizeof(struct ucred)); - } - - return nl_sendmsg(sk, msg, &hdr); -} - -void -nl_complete_msg(struct nl_sock *sk, struct nl_msg *msg) -{ - struct nlmsghdr *nlh; - - nlh = nlmsg_hdr(msg); - if (nlh->nlmsg_pid == NL_AUTO_PORT) - nlh->nlmsg_pid = nl_socket_get_local_port(sk); - - if (nlh->nlmsg_seq == NL_AUTO_SEQ) - nlh->nlmsg_seq = sk->s_seq_next++; - - if (msg->nm_protocol == -1) - msg->nm_protocol = sk->s_proto; - - nlh->nlmsg_flags |= NLM_F_REQUEST; - - if (!(sk->s_flags & NL_NO_AUTO_ACK)) - nlh->nlmsg_flags |= NLM_F_ACK; -} - -int -nl_send(struct nl_sock *sk, struct nl_msg *msg) -{ - struct iovec iov = { - .iov_base = (void *) nlmsg_hdr(msg), - .iov_len = nlmsg_hdr(msg)->nlmsg_len, - }; - - return nl_send_iovec(sk, msg, &iov, 1); -} - -int -nl_send_auto(struct nl_sock *sk, struct nl_msg *msg) -{ - nl_complete_msg(sk, msg); - - return nl_send(sk, msg); -} - -int -nl_recv(struct nl_sock * sk, - struct sockaddr_nl *nla, - unsigned char ** buf, - struct ucred * out_creds, - gboolean * out_creds_has) -{ - ssize_t n; - int flags = 0; - struct iovec iov; - struct msghdr msg = { - .msg_name = (void *) nla, - .msg_namelen = sizeof(struct sockaddr_nl), - .msg_iov = &iov, - .msg_iovlen = 1, - }; - struct ucred tmpcreds; - gboolean tmpcreds_has = FALSE; - int retval; - int errsv; - - nm_assert(nla); - nm_assert(buf && !*buf); - nm_assert(!out_creds_has == !out_creds); - - if ((sk->s_flags & NL_MSG_PEEK) - || (!(sk->s_flags & NL_MSG_PEEK_EXPLICIT) && sk->s_bufsize == 0)) - flags |= MSG_PEEK | MSG_TRUNC; - - iov.iov_len = sk->s_bufsize ?: (((size_t) nm_utils_getpagesize()) * 4u); - iov.iov_base = g_malloc(iov.iov_len); - - if (out_creds && (sk->s_flags & NL_SOCK_PASSCRED)) { - msg.msg_controllen = CMSG_SPACE(sizeof(struct ucred)); - msg.msg_control = g_malloc(msg.msg_controllen); - } - -retry: - n = recvmsg(sk->s_fd, &msg, flags); - if (!n) { - retval = 0; - goto abort; - } - - if (n < 0) { - errsv = errno; - if (errsv == EINTR) - goto retry; - retval = -nm_errno_from_native(errsv); - goto abort; - } - - if (msg.msg_flags & MSG_CTRUNC) { - if (msg.msg_controllen == 0) { - retval = -NME_NL_MSG_TRUNC; - goto abort; - } - - msg.msg_controllen *= 2; - msg.msg_control = g_realloc(msg.msg_control, msg.msg_controllen); - goto retry; - } - - if (iov.iov_len < n || (msg.msg_flags & MSG_TRUNC)) { - /* respond with error to an incomplete message */ - if (flags == 0) { - retval = -NME_NL_MSG_TRUNC; - goto abort; - } - - /* Provided buffer is not long enough, enlarge it - * to size of n (which should be total length of the message) - * and try again. */ - iov.iov_base = g_realloc(iov.iov_base, n); - iov.iov_len = n; - flags = 0; - goto retry; - } - - if (flags != 0) { - /* Buffer is big enough, do the actual reading */ - flags = 0; - goto retry; - } - - if (msg.msg_namelen != sizeof(struct sockaddr_nl)) { - retval = -NME_UNSPEC; - goto abort; - } - - if (out_creds && (sk->s_flags & NL_SOCK_PASSCRED)) { - struct cmsghdr *cmsg; - - for (cmsg = CMSG_FIRSTHDR(&msg); cmsg; cmsg = CMSG_NXTHDR(&msg, cmsg)) { - if (cmsg->cmsg_level != SOL_SOCKET) - continue; - if (cmsg->cmsg_type != SCM_CREDENTIALS) - continue; - memcpy(&tmpcreds, CMSG_DATA(cmsg), sizeof(tmpcreds)); - tmpcreds_has = TRUE; - break; - } - } - - retval = n; - -abort: - g_free(msg.msg_control); - - if (retval <= 0) { - g_free(iov.iov_base); - return retval; - } - - *buf = iov.iov_base; - if (out_creds && tmpcreds_has) - *out_creds = tmpcreds; - NM_SET_OUT(out_creds_has, tmpcreds_has); - return retval; -} diff --git a/shared/nm-platform/nm-netlink.h b/shared/nm-platform/nm-netlink.h deleted file mode 100644 index 8de42531..00000000 --- a/shared/nm-platform/nm-netlink.h +++ /dev/null @@ -1,616 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-or-later */ -/* - * Copyright (C) 2018 Red Hat, Inc. - */ - -#ifndef __NM_NETLINK_H__ -#define __NM_NETLINK_H__ - -#include <linux/netlink.h> -#include <linux/rtnetlink.h> -#include <linux/genetlink.h> - -#include "nm-std-aux/unaligned.h" - -/*****************************************************************************/ - -#define NLMSGERR_ATTR_UNUSED 0 -#define NLMSGERR_ATTR_MSG 1 -#define NLMSGERR_ATTR_OFFS 2 -#define NLMSGERR_ATTR_COOKIE 3 -#define NLMSGERR_ATTR_MAX 3 - -#ifndef NLM_F_ACK_TLVS - #define NLM_F_ACK_TLVS 0x200 -#endif - -/*****************************************************************************/ - -/* Basic attribute data types */ -enum { - NLA_UNSPEC, /* Unspecified type, binary data chunk */ - NLA_U8, /* 8 bit integer */ - NLA_U16, /* 16 bit integer */ - NLA_U32, /* 32 bit integer */ - NLA_U64, /* 64 bit integer */ - NLA_STRING, /* NUL terminated character string */ - NLA_FLAG, /* Flag */ - NLA_MSECS, /* Micro seconds (64bit) */ - NLA_NESTED, /* Nested attributes */ - NLA_NESTED_COMPAT, - NLA_NUL_STRING, - NLA_BINARY, - NLA_S8, - NLA_S16, - NLA_S32, - NLA_S64, - __NLA_TYPE_MAX, -}; - -#define NLA_TYPE_MAX (__NLA_TYPE_MAX - 1) - -struct nl_msg; - -/*****************************************************************************/ - -const char *nl_nlmsgtype2str(int type, char *buf, size_t size); - -const char *nl_nlmsg_flags2str(int flags, char *buf, size_t len); - -const char *nl_nlmsghdr_to_str(const struct nlmsghdr *hdr, char *buf, gsize len); - -/*****************************************************************************/ - -struct nla_policy { - /* Type of attribute or NLA_UNSPEC */ - uint16_t type; - - /* Minimal length of payload required */ - uint16_t minlen; - - /* Maximal length of payload allowed */ - uint16_t maxlen; -}; - -/*****************************************************************************/ - -/* static asserts that @tb and @policy are suitable arguments to nla_parse(). */ -#define _nl_static_assert_tb(tb, policy) \ - G_STMT_START \ - { \ - G_STATIC_ASSERT_EXPR(G_N_ELEMENTS(tb) > 0); \ - \ - /* We allow @policy to be either a C array or NULL. The sizeof() - * must either match the expected array size or the sizeof(NULL), - * but not both. */ \ - G_STATIC_ASSERT_EXPR((sizeof(policy) == G_N_ELEMENTS(tb) * sizeof(struct nla_policy)) \ - ^ (sizeof(policy) == sizeof(NULL))); \ - } \ - G_STMT_END - -/*****************************************************************************/ - -static inline int -nla_attr_size(int payload) -{ - nm_assert(payload >= 0); - - return NLA_HDRLEN + payload; -} - -static inline int -nla_total_size(int payload) -{ - return NLA_ALIGN(nla_attr_size(payload)); -} - -static inline int -nla_padlen(int payload) -{ - return nla_total_size(payload) - nla_attr_size(payload); -} - -struct nlattr *nla_reserve(struct nl_msg *msg, int attrtype, int attrlen); - -static inline int -nla_len(const struct nlattr *nla) -{ - nm_assert(nla); - nm_assert(nla->nla_len >= NLA_HDRLEN); - - return ((int) nla->nla_len) - NLA_HDRLEN; -} - -static inline int -nla_type(const struct nlattr *nla) -{ - nm_assert(nla_len(nla) >= 0); - - return nla->nla_type & NLA_TYPE_MASK; -} - -static inline void * -nla_data(const struct nlattr *nla) -{ - nm_assert(nla_len(nla) >= 0); - - return &(((char *) nla)[NLA_HDRLEN]); -} - -#define nla_data_as(type, nla) \ - ({ \ - const struct nlattr *_nla = (nla); \ - \ - nm_assert(nla_len(_nla) >= sizeof(type)); \ - \ - /* note that casting the pointer is undefined behavior in C, if - * the data has wrong alignment. Netlink data is aligned to 4 bytes, - * that means, if the alignment is larger than 4, this is invalid. */ \ - G_STATIC_ASSERT_EXPR(_nm_alignof(type) <= NLA_ALIGNTO); \ - \ - (type *) nla_data(_nla); \ - }) - -static inline uint8_t -nla_get_u8(const struct nlattr *nla) -{ - nm_assert(nla_len(nla) >= sizeof(uint8_t)); - - return *((const uint8_t *) nla_data(nla)); -} - -static inline int8_t -nla_get_s8(const struct nlattr *nla) -{ - nm_assert(nla_len(nla) >= sizeof(int8_t)); - - return *((const int8_t *) nla_data(nla)); -} - -static inline uint8_t -nla_get_u8_cond(/*const*/ struct nlattr *const *tb, int attr, uint8_t default_val) -{ - nm_assert(tb); - nm_assert(attr >= 0); - - return tb[attr] ? nla_get_u8(tb[attr]) : default_val; -} - -static inline uint16_t -nla_get_u16(const struct nlattr *nla) -{ - nm_assert(nla_len(nla) >= sizeof(uint16_t)); - - return *((const uint16_t *) nla_data(nla)); -} - -static inline uint32_t -nla_get_u32(const struct nlattr *nla) -{ - nm_assert(nla_len(nla) >= sizeof(uint32_t)); - - return *((const uint32_t *) nla_data(nla)); -} - -static inline int32_t -nla_get_s32(const struct nlattr *nla) -{ - nm_assert(nla_len(nla) >= sizeof(int32_t)); - - return *((const int32_t *) nla_data(nla)); -} - -static inline uint64_t -nla_get_u64(const struct nlattr *nla) -{ - nm_assert(nla_len(nla) >= sizeof(uint64_t)); - - return unaligned_read_ne64(nla_data(nla)); -} - -static inline uint64_t -nla_get_be64(const struct nlattr *nla) -{ - nm_assert(nla_len(nla) >= sizeof(uint64_t)); - - return unaligned_read_be64(nla_data(nla)); -} - -static inline char * -nla_get_string(const struct nlattr *nla) -{ - nm_assert(nla_len(nla) >= 0); - - return (char *) nla_data(nla); -} - -size_t nla_strlcpy(char *dst, const struct nlattr *nla, size_t dstsize); - -size_t nla_memcpy(void *dst, const struct nlattr *nla, size_t dstsize); - -#define nla_memcpy_checked_size(dst, nla, dstsize) \ - G_STMT_START \ - { \ - void *const _dst = (dst); \ - const struct nlattr *const _nla = (nla); \ - const size_t _dstsize = (dstsize); \ - size_t _srcsize; \ - \ - /* assert that, if @nla is given, that it has the exact expected - * size. This implies that the caller previously verified the length - * of the attribute (via minlen/maxlen at nla_parse()). */ \ - \ - if (_nla) { \ - _srcsize = nla_memcpy(_dst, _nla, _dstsize); \ - nm_assert(_srcsize == _dstsize); \ - } \ - } \ - G_STMT_END - -int nla_put(struct nl_msg *msg, int attrtype, int datalen, const void *data); - -static inline int -nla_put_string(struct nl_msg *msg, int attrtype, const char *str) -{ - nm_assert(str); - - return nla_put(msg, attrtype, strlen(str) + 1, str); -} - -static inline int -nla_put_uint8(struct nl_msg *msg, int attrtype, uint8_t val) -{ - return nla_put(msg, attrtype, sizeof(val), &val); -} - -static inline int -nla_put_uint16(struct nl_msg *msg, int attrtype, uint16_t val) -{ - return nla_put(msg, attrtype, sizeof(val), &val); -} - -static inline int -nla_put_uint32(struct nl_msg *msg, int attrtype, uint32_t val) -{ - return nla_put(msg, attrtype, sizeof(val), &val); -} - -#define NLA_PUT(msg, attrtype, attrlen, data) \ - G_STMT_START \ - { \ - if (nla_put(msg, attrtype, attrlen, data) < 0) \ - goto nla_put_failure; \ - } \ - G_STMT_END - -#define NLA_PUT_TYPE(msg, type, attrtype, value) \ - G_STMT_START \ - { \ - type __nla_tmp = value; \ - NLA_PUT(msg, attrtype, sizeof(type), &__nla_tmp); \ - } \ - G_STMT_END - -#define NLA_PUT_U8(msg, attrtype, value) NLA_PUT_TYPE(msg, uint8_t, attrtype, value) - -#define NLA_PUT_S8(msg, attrtype, value) NLA_PUT_TYPE(msg, int8_t, attrtype, value) - -#define NLA_PUT_U16(msg, attrtype, value) NLA_PUT_TYPE(msg, uint16_t, attrtype, value) - -#define NLA_PUT_U32(msg, attrtype, value) NLA_PUT_TYPE(msg, uint32_t, attrtype, value) - -#define NLA_PUT_S32(msg, attrtype, value) NLA_PUT_TYPE(msg, int32_t, attrtype, value) - -#define NLA_PUT_U64(msg, attrtype, value) NLA_PUT_TYPE(msg, uint64_t, attrtype, value) - -#define NLA_PUT_STRING(msg, attrtype, value) NLA_PUT(msg, attrtype, (int) strlen(value) + 1, value) - -#define NLA_PUT_FLAG(msg, attrtype) NLA_PUT(msg, attrtype, 0, NULL) - -struct nlattr *nla_find(const struct nlattr *head, int len, int attrtype); - -static inline int -nla_ok(const struct nlattr *nla, int remaining) -{ - return remaining >= (int) sizeof(*nla) && nla->nla_len >= sizeof(*nla) - && nla->nla_len <= remaining; -} - -static inline struct nlattr * -nla_next(const struct nlattr *nla, int *remaining) -{ - int totlen = NLA_ALIGN(nla->nla_len); - - *remaining -= totlen; - return (struct nlattr *) ((char *) nla + totlen); -} - -#define nla_for_each_attr(pos, head, len, rem) \ - for (pos = head, rem = len; nla_ok(pos, rem); pos = nla_next(pos, &(rem))) - -#define nla_for_each_nested(pos, nla, rem) \ - for (pos = (struct nlattr *) nla_data(nla), rem = nla_len(nla); nla_ok(pos, rem); \ - pos = nla_next(pos, &(rem))) - -void nla_nest_cancel(struct nl_msg *msg, const struct nlattr *attr); -struct nlattr *nla_nest_start(struct nl_msg *msg, int attrtype); -int nla_nest_end(struct nl_msg *msg, struct nlattr *start); - -int nla_parse(struct nlattr * tb[], - int maxtype, - struct nlattr * head, - int len, - const struct nla_policy *policy); - -#define nla_parse_arr(tb, head, len, policy) \ - ({ \ - _nl_static_assert_tb((tb), (policy)); \ - \ - nla_parse((tb), G_N_ELEMENTS(tb) - 1, (head), (len), (policy)); \ - }) - -static inline int -nla_parse_nested(struct nlattr * tb[], - int maxtype, - struct nlattr * nla, - const struct nla_policy *policy) -{ - return nla_parse(tb, maxtype, nla_data(nla), nla_len(nla), policy); -} - -#define nla_parse_nested_arr(tb, nla, policy) \ - ({ \ - _nl_static_assert_tb((tb), (policy)); \ - \ - nla_parse_nested((tb), G_N_ELEMENTS(tb) - 1, (nla), (policy)); \ - }) - -/*****************************************************************************/ - -struct nl_msg *nlmsg_alloc(void); - -struct nl_msg *nlmsg_alloc_size(size_t max); - -struct nl_msg *nlmsg_alloc_convert(struct nlmsghdr *hdr); - -struct nl_msg *nlmsg_alloc_simple(int nlmsgtype, int flags); - -void *nlmsg_reserve(struct nl_msg *n, size_t len, int pad); - -int nlmsg_append(struct nl_msg *n, const void *data, size_t len, int pad); - -#define nlmsg_append_struct(n, data) nlmsg_append(n, (data), sizeof(*(data)), NLMSG_ALIGNTO) - -void nlmsg_free(struct nl_msg *msg); - -static inline int -nlmsg_size(int payload) -{ - nm_assert(payload >= 0 && payload < G_MAXINT - NLMSG_HDRLEN - 4); - return NLMSG_HDRLEN + payload; -} - -static inline int -nlmsg_total_size(int payload) -{ - return NLMSG_ALIGN(nlmsg_size(payload)); -} - -static inline int -nlmsg_ok(const struct nlmsghdr *nlh, int remaining) -{ - return (remaining >= (int) sizeof(struct nlmsghdr) && nlh->nlmsg_len >= sizeof(struct nlmsghdr) - && nlh->nlmsg_len <= remaining); -} - -static inline struct nlmsghdr * -nlmsg_next(struct nlmsghdr *nlh, int *remaining) -{ - int totlen = NLMSG_ALIGN(nlh->nlmsg_len); - - *remaining -= totlen; - - return (struct nlmsghdr *) ((unsigned char *) nlh + totlen); -} - -int nlmsg_get_proto(struct nl_msg *msg); -void nlmsg_set_proto(struct nl_msg *msg, int protocol); - -void nlmsg_set_src(struct nl_msg *msg, struct sockaddr_nl *addr); - -struct ucred *nlmsg_get_creds(struct nl_msg *msg); -void nlmsg_set_creds(struct nl_msg *msg, struct ucred *creds); - -static inline void -_nm_auto_nl_msg_cleanup(struct nl_msg **ptr) -{ - nlmsg_free(*ptr); -} -#define nm_auto_nlmsg nm_auto(_nm_auto_nl_msg_cleanup) - -static inline void * -nlmsg_data(const struct nlmsghdr *nlh) -{ - return (unsigned char *) nlh + NLMSG_HDRLEN; -} - -static inline void * -nlmsg_tail(const struct nlmsghdr *nlh) -{ - return (unsigned char *) nlh + NLMSG_ALIGN(nlh->nlmsg_len); -} - -struct nlmsghdr *nlmsg_hdr(struct nl_msg *n); - -static inline int -nlmsg_valid_hdr(const struct nlmsghdr *nlh, int hdrlen) -{ - if (nlh->nlmsg_len < nlmsg_size(hdrlen)) - return 0; - - return 1; -} - -static inline int -nlmsg_datalen(const struct nlmsghdr *nlh) -{ - return nlh->nlmsg_len - NLMSG_HDRLEN; -} - -static inline int -nlmsg_attrlen(const struct nlmsghdr *nlh, int hdrlen) -{ - return NM_MAX((int) (nlmsg_datalen(nlh) - NLMSG_ALIGN(hdrlen)), 0); -} - -static inline struct nlattr * -nlmsg_attrdata(const struct nlmsghdr *nlh, int hdrlen) -{ - unsigned char *data = nlmsg_data(nlh); - return (struct nlattr *) (data + NLMSG_ALIGN(hdrlen)); -} - -static inline struct nlattr * -nlmsg_find_attr(struct nlmsghdr *nlh, int hdrlen, int attrtype) -{ - return nla_find(nlmsg_attrdata(nlh, hdrlen), nlmsg_attrlen(nlh, hdrlen), attrtype); -} - -int nlmsg_parse(struct nlmsghdr * nlh, - int hdrlen, - struct nlattr * tb[], - int maxtype, - const struct nla_policy *policy); - -#define nlmsg_parse_arr(nlh, hdrlen, tb, policy) \ - ({ \ - _nl_static_assert_tb((tb), (policy)); \ - G_STATIC_ASSERT_EXPR((hdrlen) >= 0); \ - \ - nlmsg_parse((nlh), (hdrlen), (tb), G_N_ELEMENTS(tb) - 1, (policy)); \ - }) - -struct nlmsghdr * -nlmsg_put(struct nl_msg *n, uint32_t pid, uint32_t seq, int type, int payload, int flags); - -/*****************************************************************************/ - -#define NL_AUTO_PORT 0 -#define NL_AUTO_SEQ 0 - -struct nl_sock; - -struct nl_sock *nl_socket_alloc(void); - -void nl_socket_free(struct nl_sock *sk); - -int nl_socket_get_fd(const struct nl_sock *sk); - -struct sockaddr_nl *nlmsg_get_dst(struct nl_msg *msg); - -size_t nl_socket_get_msg_buf_size(struct nl_sock *sk); -int nl_socket_set_msg_buf_size(struct nl_sock *sk, size_t bufsize); - -int nl_socket_set_buffer_size(struct nl_sock *sk, int rxbuf, int txbuf); - -int nl_socket_set_passcred(struct nl_sock *sk, int state); - -int nl_socket_set_nonblocking(const struct nl_sock *sk); - -void nl_socket_disable_msg_peek(struct nl_sock *sk); - -uint32_t nl_socket_get_local_port(const struct nl_sock *sk); - -int nl_socket_add_memberships(struct nl_sock *sk, int group, ...); - -int nl_connect(struct nl_sock *sk, int protocol); - -int nl_recv(struct nl_sock * sk, - struct sockaddr_nl *nla, - unsigned char ** buf, - struct ucred * out_creds, - gboolean * out_creds_has); - -int nl_send(struct nl_sock *sk, struct nl_msg *msg); - -int nl_send_auto(struct nl_sock *sk, struct nl_msg *msg); - -/*****************************************************************************/ - -enum nl_cb_action { - /* Proceed with wathever would come next */ - NL_OK, - /* Skip this message */ - NL_SKIP, - /* Stop parsing altogether and discard remaining messages */ - NL_STOP, -}; - -typedef int (*nl_recvmsg_msg_cb_t)(struct nl_msg *msg, void *arg); - -typedef int (*nl_recvmsg_err_cb_t)(struct sockaddr_nl *nla, struct nlmsgerr *nlerr, void *arg); - -struct nl_cb { - nl_recvmsg_msg_cb_t valid_cb; - void * valid_arg; - - nl_recvmsg_msg_cb_t finish_cb; - void * finish_arg; - - nl_recvmsg_msg_cb_t ack_cb; - void * ack_arg; - - nl_recvmsg_err_cb_t err_cb; - void * err_arg; -}; - -int nl_sendmsg(struct nl_sock *sk, struct nl_msg *msg, struct msghdr *hdr); - -int nl_send_iovec(struct nl_sock *sk, struct nl_msg *msg, struct iovec *iov, unsigned iovlen); - -void nl_complete_msg(struct nl_sock *sk, struct nl_msg *msg); - -int nl_recvmsgs(struct nl_sock *sk, const struct nl_cb *cb); - -int nl_wait_for_ack(struct nl_sock *sk, const struct nl_cb *cb); - -int nl_socket_set_ext_ack(struct nl_sock *sk, gboolean enable); - -/*****************************************************************************/ - -void * genlmsg_put(struct nl_msg *msg, - uint32_t port, - uint32_t seq, - int family, - int hdrlen, - int flags, - uint8_t cmd, - uint8_t version); -void * genlmsg_data(const struct genlmsghdr *gnlh); -void * genlmsg_user_hdr(const struct genlmsghdr *gnlh); -struct genlmsghdr *genlmsg_hdr(struct nlmsghdr *nlh); -void * genlmsg_user_data(const struct genlmsghdr *gnlh, const int hdrlen); -struct nlattr * genlmsg_attrdata(const struct genlmsghdr *gnlh, int hdrlen); -int genlmsg_len(const struct genlmsghdr *gnlh); -int genlmsg_attrlen(const struct genlmsghdr *gnlh, int hdrlen); -int genlmsg_valid_hdr(struct nlmsghdr *nlh, int hdrlen); - -int genlmsg_parse(struct nlmsghdr * nlh, - int hdrlen, - struct nlattr * tb[], - int maxtype, - const struct nla_policy *policy); - -#define genlmsg_parse_arr(nlh, hdrlen, tb, policy) \ - ({ \ - _nl_static_assert_tb((tb), (policy)); \ - G_STATIC_ASSERT_EXPR((hdrlen) >= 0); \ - \ - genlmsg_parse((nlh), (hdrlen), (tb), G_N_ELEMENTS(tb) - 1, (policy)); \ - }) - -int genl_ctrl_resolve(struct nl_sock *sk, const char *name); - -/*****************************************************************************/ - -#endif /* __NM_NETLINK_H__ */ diff --git a/shared/nm-platform/nm-platform-utils.c b/shared/nm-platform/nm-platform-utils.c deleted file mode 100644 index c1c5b1a3..00000000 --- a/shared/nm-platform/nm-platform-utils.c +++ /dev/null @@ -1,1807 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-or-later */ -/* - * Copyright (C) 2015 Red Hat, Inc. - */ - -#include "nm-glib-aux/nm-default-glib-i18n-lib.h" - -#include "nm-platform-utils.h" - -#include <unistd.h> -#include <sys/ioctl.h> -#include <linux/ethtool.h> -#include <linux/sockios.h> -#include <linux/mii.h> -#include <linux/if.h> -#include <linux/version.h> -#include <linux/rtnetlink.h> -#include <fcntl.h> -#include <libudev.h> - -#include "nm-base/nm-ethtool-base.h" -#include "nm-log-core/nm-logging.h" - -/*****************************************************************************/ - -#define ONOFF(bool_val) ((bool_val) ? "on" : "off") - -/****************************************************************************** - * utils - *****************************************************************************/ - -extern char *if_indextoname(unsigned __ifindex, char *__ifname); -unsigned if_nametoindex(const char *__ifname); - -const char * -nmp_utils_if_indextoname(int ifindex, char *out_ifname /*IFNAMSIZ*/) -{ - g_return_val_if_fail(ifindex > 0, NULL); - g_return_val_if_fail(out_ifname, NULL); - - return if_indextoname(ifindex, out_ifname); -} - -int -nmp_utils_if_nametoindex(const char *ifname) -{ - g_return_val_if_fail(ifname, 0); - - return if_nametoindex(ifname); -} - -/*****************************************************************************/ - -NM_UTILS_LOOKUP_STR_DEFINE(nm_platform_link_duplex_type_to_string, - NMPlatformLinkDuplexType, - NM_UTILS_LOOKUP_DEFAULT_WARN(NULL), - NM_UTILS_LOOKUP_STR_ITEM(NM_PLATFORM_LINK_DUPLEX_UNKNOWN, "unknown"), - NM_UTILS_LOOKUP_STR_ITEM(NM_PLATFORM_LINK_DUPLEX_FULL, "full"), - NM_UTILS_LOOKUP_STR_ITEM(NM_PLATFORM_LINK_DUPLEX_HALF, "half"), ); - -/*****************************************************************************/ - -typedef struct { - int fd; - const int ifindex; - char ifname[IFNAMSIZ]; -} SocketHandle; - -#define SOCKET_HANDLE_INIT(_ifindex) \ - { \ - .fd = -1, .ifindex = (_ifindex), \ - } - -static void -_nm_auto_socket_handle(SocketHandle *shandle) -{ - if (shandle->fd >= 0) - nm_close(shandle->fd); -} - -#define nm_auto_socket_handle nm_auto(_nm_auto_socket_handle) - -/*****************************************************************************/ - -typedef enum { - IOCTL_CALL_DATA_TYPE_NONE, - IOCTL_CALL_DATA_TYPE_IFRDATA, - IOCTL_CALL_DATA_TYPE_IFRU, -} IoctlCallDataType; - -static int -_ioctl_call(const char * log_ioctl_type, - const char * log_subtype, - unsigned long int ioctl_request, - int ifindex, - int * inout_fd, - char * inout_ifname, - IoctlCallDataType edata_type, - gpointer edata, - gsize edata_size, - struct ifreq * out_ifreq) -{ - nm_auto_close int fd_close = -1; - int fd; - int r; - gpointer edata_backup = NULL; - gs_free gpointer edata_backup_free = NULL; - guint try_count; - char known_ifnames[2][IFNAMSIZ]; - const char * failure_reason = NULL; - struct ifreq ifr; - - nm_assert(ifindex > 0); - nm_assert(NM_IN_SET(edata_type, - IOCTL_CALL_DATA_TYPE_NONE, - IOCTL_CALL_DATA_TYPE_IFRDATA, - IOCTL_CALL_DATA_TYPE_IFRU)); - nm_assert(edata_type != IOCTL_CALL_DATA_TYPE_NONE || edata_size == 0); - nm_assert(edata_type != IOCTL_CALL_DATA_TYPE_IFRDATA || edata_size > 0); - nm_assert(edata_type != IOCTL_CALL_DATA_TYPE_IFRU - || (edata_size > 0 && edata_size <= sizeof(ifr.ifr_ifru))); - nm_assert(edata_size == 0 || edata); - - /* open a file descriptor (or use the one provided). */ - if (inout_fd && *inout_fd >= 0) - fd = *inout_fd; - else { - fd = socket(PF_INET, SOCK_DGRAM | SOCK_CLOEXEC, 0); - if (fd < 0) { - r = -NM_ERRNO_NATIVE(errno); - failure_reason = "failed creating socket or ioctl"; - goto out; - } - if (inout_fd) - *inout_fd = fd; - else - fd_close = fd; - } - - /* resolve the ifindex to name (or use the one provided). */ - if (inout_ifname && inout_ifname[0]) - nm_utils_ifname_cpy(known_ifnames[0], inout_ifname); - else { - if (!nmp_utils_if_indextoname(ifindex, known_ifnames[0])) { - failure_reason = "cannot resolve ifindex"; - r = -ENODEV; - goto out; - } - if (inout_ifname) - nm_utils_ifname_cpy(inout_ifname, known_ifnames[0]); - } - - /* we might need to retry the request. Backup edata so that we can - * restore it on retry. */ - if (edata_size > 0) - edata_backup = nm_memdup_maybe_a(500, edata, edata_size, &edata_backup_free); - - try_count = 0; - -again: -{ - const char *ifname = known_ifnames[try_count % 2]; - - nm_assert(ifindex > 0); - nm_assert(ifname && nm_utils_ifname_valid_kernel(ifname, NULL)); - nm_assert(fd >= 0); - - memset(&ifr, 0, sizeof(ifr)); - nm_utils_ifname_cpy(ifr.ifr_name, ifname); - if (edata_type == IOCTL_CALL_DATA_TYPE_IFRDATA) - ifr.ifr_data = edata; - else if (edata_type == IOCTL_CALL_DATA_TYPE_IFRU) - memcpy(&ifr.ifr_ifru, edata, NM_MIN(edata_size, sizeof(ifr.ifr_ifru))); - - if (ioctl(fd, ioctl_request, &ifr) < 0) { - r = -NM_ERRNO_NATIVE(errno); - nm_log_trace(LOGD_PLATFORM, - "%s[%d]: %s, %s: failed: %s", - log_ioctl_type, - ifindex, - log_subtype, - ifname, - nm_strerror_native(-r)); - } else { - r = 0; - nm_log_trace(LOGD_PLATFORM, - "%s[%d]: %s, %s: success", - log_ioctl_type, - ifindex, - log_subtype, - ifname); - } -} - - try_count++; - - /* resolve the name again to see whether the ifindex still has the same name. */ - if (!nmp_utils_if_indextoname(ifindex, known_ifnames[try_count % 2])) { - /* we could not find the ifindex again. Probably the device just got - * removed. - * - * In both cases we return the error code we got from ioctl above. - * Either it failed because the device was gone already or it still - * managed to complete the call. In both cases, the error code is good. */ - failure_reason = - "cannot resolve ifindex after ioctl call. Probably the device was just removed"; - goto out; - } - - /* check whether the ifname changed in the meantime. If yes, would render the result - * invalid. Note that this cannot detect every race regarding renames, for example: - * - * - if_indextoname(#10) gives eth0 - * - rename(#10) => eth0_tmp - * - rename(#11) => eth0 - * - ioctl(eth0) (wrongly fetching #11, formerly eth1) - * - rename(#11) => eth_something - * - rename(#10) => eth0 - * - if_indextoname(#10) gives eth0 - */ - if (!nm_streq(known_ifnames[0], known_ifnames[1])) { - gboolean retry; - - /* we detected a possible(!) rename. - * - * For getters it's straight forward to just retry the call. - * - * For setters we also always retry. If our previous call operated on the right device, - * calling it again should have no bad effect (just setting the same thing more than once). - * - * The only potential bad thing is if there was a race involving swapping names, and we just - * set the ioctl option on the wrong device. But then the bad thing already happenned and - * we cannot detect it (nor do anything about it). At least, we can retry and set the - * option on the right interface. */ - retry = (try_count < 5); - - nm_log_trace(LOGD_PLATFORM, - "%s[%d]: %s: rename detected from \"%s\" to \"%s\". %s", - log_ioctl_type, - ifindex, - log_subtype, - known_ifnames[(try_count - 1) % 2], - known_ifnames[try_count % 2], - retry ? "Retry" : "No retry"); - if (inout_ifname) - nm_utils_ifname_cpy(inout_ifname, known_ifnames[try_count % 2]); - if (retry) { - if (edata_size > 0) - memcpy(edata, edata_backup, edata_size); - goto again; - } - } - -out: - if (failure_reason) { - nm_log_trace(LOGD_PLATFORM, - "%s[%d]: %s: %s: %s", - log_ioctl_type, - ifindex, - log_subtype, - failure_reason, - r < 0 ? nm_strerror_native(-r) : "assume success"); - } - if (r >= 0) - NM_SET_OUT(out_ifreq, ifr); - return r; -} - -/****************************************************************************** - * ethtool - *****************************************************************************/ - -static NM_UTILS_ENUM2STR_DEFINE(_ethtool_cmd_to_string, - guint32, - NM_UTILS_ENUM2STR(ETHTOOL_GCOALESCE, "ETHTOOL_GCOALESCE"), - NM_UTILS_ENUM2STR(ETHTOOL_GDRVINFO, "ETHTOOL_GDRVINFO"), - NM_UTILS_ENUM2STR(ETHTOOL_GFEATURES, "ETHTOOL_GFEATURES"), - NM_UTILS_ENUM2STR(ETHTOOL_GLINK, "ETHTOOL_GLINK"), - NM_UTILS_ENUM2STR(ETHTOOL_GPERMADDR, "ETHTOOL_GPERMADDR"), - NM_UTILS_ENUM2STR(ETHTOOL_GRINGPARAM, "ETHTOOL_GRINGPARAM"), - NM_UTILS_ENUM2STR(ETHTOOL_GSET, "ETHTOOL_GSET"), - NM_UTILS_ENUM2STR(ETHTOOL_GSSET_INFO, "ETHTOOL_GSSET_INFO"), - NM_UTILS_ENUM2STR(ETHTOOL_GSTATS, "ETHTOOL_GSTATS"), - NM_UTILS_ENUM2STR(ETHTOOL_GSTRINGS, "ETHTOOL_GSTRINGS"), - NM_UTILS_ENUM2STR(ETHTOOL_GWOL, "ETHTOOL_GWOL"), - NM_UTILS_ENUM2STR(ETHTOOL_SCOALESCE, "ETHTOOL_SCOALESCE"), - NM_UTILS_ENUM2STR(ETHTOOL_SFEATURES, "ETHTOOL_SFEATURES"), - NM_UTILS_ENUM2STR(ETHTOOL_SRINGPARAM, "ETHTOOL_SRINGPARAM"), - NM_UTILS_ENUM2STR(ETHTOOL_SSET, "ETHTOOL_SSET"), - NM_UTILS_ENUM2STR(ETHTOOL_SWOL, "ETHTOOL_SWOL"), ); - -static const char * -_ethtool_edata_to_string(gpointer edata, gsize edata_size, char *sbuf, gsize sbuf_len) -{ - nm_assert(edata); - nm_assert(edata_size >= sizeof(guint32)); - nm_assert((((intptr_t) edata) % _nm_alignof(guint32)) == 0); - - return _ethtool_cmd_to_string(*((guint32 *) edata), sbuf, sbuf_len); -} - -/*****************************************************************************/ - -#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 27) - #define ethtool_cmd_speed(pedata) ((pedata)->speed) - - #define ethtool_cmd_speed_set(pedata, speed) \ - G_STMT_START \ - { \ - (pedata)->speed = (guint16)(speed); \ - } \ - G_STMT_END -#endif - -static int -_ethtool_call_handle(SocketHandle *shandle, gpointer edata, gsize edata_size) -{ - char sbuf[50]; - - return _ioctl_call("ethtool", - _ethtool_edata_to_string(edata, edata_size, sbuf, sizeof(sbuf)), - SIOCETHTOOL, - shandle->ifindex, - &shandle->fd, - shandle->ifname, - IOCTL_CALL_DATA_TYPE_IFRDATA, - edata, - edata_size, - NULL); -} - -static int -_ethtool_call_once(int ifindex, gpointer edata, gsize edata_size) -{ - char sbuf[50]; - - return _ioctl_call("ethtool", - _ethtool_edata_to_string(edata, edata_size, sbuf, sizeof(sbuf)), - SIOCETHTOOL, - ifindex, - NULL, - NULL, - IOCTL_CALL_DATA_TYPE_IFRDATA, - edata, - edata_size, - NULL); -} - -/*****************************************************************************/ - -static struct ethtool_gstrings * -ethtool_get_stringset(SocketHandle *shandle, int stringset_id) -{ - struct { - struct ethtool_sset_info info; - guint32 sentinel; - } sset_info = { - .info.cmd = ETHTOOL_GSSET_INFO, - .info.reserved = 0, - .info.sset_mask = (1ULL << stringset_id), - }; - const guint32 * pdata; - gs_free struct ethtool_gstrings *gstrings = NULL; - gsize gstrings_len; - guint32 i, len; - - if (_ethtool_call_handle(shandle, &sset_info, sizeof(sset_info)) < 0) - return NULL; - if (!sset_info.info.sset_mask) - return NULL; - - pdata = (guint32 *) sset_info.info.data; - - len = *pdata; - - gstrings_len = sizeof(*gstrings) + (len * ETH_GSTRING_LEN); - gstrings = g_malloc0(gstrings_len); - gstrings->cmd = ETHTOOL_GSTRINGS; - gstrings->string_set = stringset_id; - gstrings->len = len; - if (gstrings->len > 0) { - if (_ethtool_call_handle(shandle, gstrings, gstrings_len) < 0) - return NULL; - for (i = 0; i < gstrings->len; i++) { - /* ensure NUL terminated */ - gstrings->data[i * ETH_GSTRING_LEN + (ETH_GSTRING_LEN - 1)] = '\0'; - } - } - - return g_steal_pointer(&gstrings); -} - -static int -ethtool_gstrings_find(const struct ethtool_gstrings *gstrings, const char *needle) -{ - guint32 i; - - /* ethtool_get_stringset() always ensures NUL terminated strings at ETH_GSTRING_LEN. - * that means, we cannot possibly request longer names. */ - nm_assert(needle && strlen(needle) < ETH_GSTRING_LEN); - - for (i = 0; i < gstrings->len; i++) { - if (nm_streq((char *) &gstrings->data[i * ETH_GSTRING_LEN], needle)) - return i; - } - return -1; -} - -static int -ethtool_get_stringset_index(SocketHandle *shandle, int stringset_id, const char *needle) -{ - gs_free struct ethtool_gstrings *gstrings = NULL; - - /* ethtool_get_stringset() always ensures NUL terminated strings at ETH_GSTRING_LEN. - * that means, we cannot possibly request longer names. */ - nm_assert(needle && strlen(needle) < ETH_GSTRING_LEN); - - gstrings = ethtool_get_stringset(shandle, stringset_id); - if (gstrings) - return ethtool_gstrings_find(gstrings, needle); - return -1; -} - -/*****************************************************************************/ - -static const NMEthtoolFeatureInfo _ethtool_feature_infos[_NM_ETHTOOL_ID_FEATURE_NUM] = { -#define ETHT_FEAT(eid, ...) \ - { \ - .ethtool_id = eid, .n_kernel_names = NM_NARG(__VA_ARGS__), \ - .kernel_names = ((const char *const[]){__VA_ARGS__}), \ - } - - /* the order does only matter for one thing: if it happens that more than one NMEthtoolID - * reference the same kernel-name, then the one that is mentioned *later* will win in - * case these NMEthtoolIDs are set. That mostly only makes sense for ethtool-ids which - * refer to multiple features ("feature-tso"), while also having more specific ids - * ("feature-tx-tcp-segmentation"). */ - - /* names from ethtool utility, which are aliases for multiple features. */ - ETHT_FEAT(NM_ETHTOOL_ID_FEATURE_SG, "tx-scatter-gather", "tx-scatter-gather-fraglist"), - ETHT_FEAT(NM_ETHTOOL_ID_FEATURE_TSO, - "tx-tcp-segmentation", - "tx-tcp-ecn-segmentation", - "tx-tcp-mangleid-segmentation", - "tx-tcp6-segmentation"), - ETHT_FEAT(NM_ETHTOOL_ID_FEATURE_TX, - "tx-checksum-ipv4", - "tx-checksum-ip-generic", - "tx-checksum-ipv6", - "tx-checksum-fcoe-crc", - "tx-checksum-sctp"), - - /* names from ethtool utility, which are aliases for one feature. */ - ETHT_FEAT(NM_ETHTOOL_ID_FEATURE_GRO, "rx-gro"), - ETHT_FEAT(NM_ETHTOOL_ID_FEATURE_GSO, "tx-generic-segmentation"), - ETHT_FEAT(NM_ETHTOOL_ID_FEATURE_LRO, "rx-lro"), - ETHT_FEAT(NM_ETHTOOL_ID_FEATURE_NTUPLE, "rx-ntuple-filter"), - ETHT_FEAT(NM_ETHTOOL_ID_FEATURE_RX, "rx-checksum"), - ETHT_FEAT(NM_ETHTOOL_ID_FEATURE_RXHASH, "rx-hashing"), - ETHT_FEAT(NM_ETHTOOL_ID_FEATURE_RXVLAN, "rx-vlan-hw-parse"), - ETHT_FEAT(NM_ETHTOOL_ID_FEATURE_TXVLAN, "tx-vlan-hw-insert"), - - /* names of features, as known by kernel. */ - ETHT_FEAT(NM_ETHTOOL_ID_FEATURE_ESP_HW_OFFLOAD, "esp-hw-offload"), - ETHT_FEAT(NM_ETHTOOL_ID_FEATURE_ESP_TX_CSUM_HW_OFFLOAD, "esp-tx-csum-hw-offload"), - ETHT_FEAT(NM_ETHTOOL_ID_FEATURE_FCOE_MTU, "fcoe-mtu"), - ETHT_FEAT(NM_ETHTOOL_ID_FEATURE_HIGHDMA, "highdma"), - ETHT_FEAT(NM_ETHTOOL_ID_FEATURE_HW_TC_OFFLOAD, "hw-tc-offload"), - ETHT_FEAT(NM_ETHTOOL_ID_FEATURE_L2_FWD_OFFLOAD, "l2-fwd-offload"), - ETHT_FEAT(NM_ETHTOOL_ID_FEATURE_LOOPBACK, "loopback"), - ETHT_FEAT(NM_ETHTOOL_ID_FEATURE_MACSEC_HW_OFFLOAD, "macsec-hw-offload"), - ETHT_FEAT(NM_ETHTOOL_ID_FEATURE_RX_ALL, "rx-all"), - ETHT_FEAT(NM_ETHTOOL_ID_FEATURE_RX_FCS, "rx-fcs"), - ETHT_FEAT(NM_ETHTOOL_ID_FEATURE_RX_GRO_HW, "rx-gro-hw"), - ETHT_FEAT(NM_ETHTOOL_ID_FEATURE_RX_GRO_LIST, "rx-gro-list"), - ETHT_FEAT(NM_ETHTOOL_ID_FEATURE_RX_UDP_GRO_FORWARDING, "rx-udp-gro-forwarding"), - ETHT_FEAT(NM_ETHTOOL_ID_FEATURE_RX_UDP_TUNNEL_PORT_OFFLOAD, "rx-udp_tunnel-port-offload"), - ETHT_FEAT(NM_ETHTOOL_ID_FEATURE_RX_VLAN_FILTER, "rx-vlan-filter"), - ETHT_FEAT(NM_ETHTOOL_ID_FEATURE_RX_VLAN_STAG_FILTER, "rx-vlan-stag-filter"), - ETHT_FEAT(NM_ETHTOOL_ID_FEATURE_RX_VLAN_STAG_HW_PARSE, "rx-vlan-stag-hw-parse"), - ETHT_FEAT(NM_ETHTOOL_ID_FEATURE_TLS_HW_RECORD, "tls-hw-record"), - ETHT_FEAT(NM_ETHTOOL_ID_FEATURE_TLS_HW_RX_OFFLOAD, "tls-hw-rx-offload"), - ETHT_FEAT(NM_ETHTOOL_ID_FEATURE_TLS_HW_TX_OFFLOAD, "tls-hw-tx-offload"), - ETHT_FEAT(NM_ETHTOOL_ID_FEATURE_TX_CHECKSUM_FCOE_CRC, "tx-checksum-fcoe-crc"), - ETHT_FEAT(NM_ETHTOOL_ID_FEATURE_TX_CHECKSUM_IPV4, "tx-checksum-ipv4"), - ETHT_FEAT(NM_ETHTOOL_ID_FEATURE_TX_CHECKSUM_IPV6, "tx-checksum-ipv6"), - ETHT_FEAT(NM_ETHTOOL_ID_FEATURE_TX_CHECKSUM_IP_GENERIC, "tx-checksum-ip-generic"), - ETHT_FEAT(NM_ETHTOOL_ID_FEATURE_TX_CHECKSUM_SCTP, "tx-checksum-sctp"), - ETHT_FEAT(NM_ETHTOOL_ID_FEATURE_TX_ESP_SEGMENTATION, "tx-esp-segmentation"), - ETHT_FEAT(NM_ETHTOOL_ID_FEATURE_TX_FCOE_SEGMENTATION, "tx-fcoe-segmentation"), - ETHT_FEAT(NM_ETHTOOL_ID_FEATURE_TX_GRE_CSUM_SEGMENTATION, "tx-gre-csum-segmentation"), - ETHT_FEAT(NM_ETHTOOL_ID_FEATURE_TX_GRE_SEGMENTATION, "tx-gre-segmentation"), - ETHT_FEAT(NM_ETHTOOL_ID_FEATURE_TX_GSO_LIST, "tx-gso-list"), - ETHT_FEAT(NM_ETHTOOL_ID_FEATURE_TX_GSO_PARTIAL, "tx-gso-partial"), - ETHT_FEAT(NM_ETHTOOL_ID_FEATURE_TX_GSO_ROBUST, "tx-gso-robust"), - ETHT_FEAT(NM_ETHTOOL_ID_FEATURE_TX_IPXIP4_SEGMENTATION, "tx-ipxip4-segmentation"), - ETHT_FEAT(NM_ETHTOOL_ID_FEATURE_TX_IPXIP6_SEGMENTATION, "tx-ipxip6-segmentation"), - ETHT_FEAT(NM_ETHTOOL_ID_FEATURE_TX_NOCACHE_COPY, "tx-nocache-copy"), - ETHT_FEAT(NM_ETHTOOL_ID_FEATURE_TX_SCATTER_GATHER, "tx-scatter-gather"), - ETHT_FEAT(NM_ETHTOOL_ID_FEATURE_TX_SCATTER_GATHER_FRAGLIST, "tx-scatter-gather-fraglist"), - ETHT_FEAT(NM_ETHTOOL_ID_FEATURE_TX_SCTP_SEGMENTATION, "tx-sctp-segmentation"), - ETHT_FEAT(NM_ETHTOOL_ID_FEATURE_TX_TCP6_SEGMENTATION, "tx-tcp6-segmentation"), - ETHT_FEAT(NM_ETHTOOL_ID_FEATURE_TX_TCP_ECN_SEGMENTATION, "tx-tcp-ecn-segmentation"), - ETHT_FEAT(NM_ETHTOOL_ID_FEATURE_TX_TCP_MANGLEID_SEGMENTATION, "tx-tcp-mangleid-segmentation"), - ETHT_FEAT(NM_ETHTOOL_ID_FEATURE_TX_TCP_SEGMENTATION, "tx-tcp-segmentation"), - ETHT_FEAT(NM_ETHTOOL_ID_FEATURE_TX_TUNNEL_REMCSUM_SEGMENTATION, - "tx-tunnel-remcsum-segmentation"), - ETHT_FEAT(NM_ETHTOOL_ID_FEATURE_TX_UDP_SEGMENTATION, "tx-udp-segmentation"), - ETHT_FEAT(NM_ETHTOOL_ID_FEATURE_TX_UDP_TNL_CSUM_SEGMENTATION, "tx-udp_tnl-csum-segmentation"), - ETHT_FEAT(NM_ETHTOOL_ID_FEATURE_TX_UDP_TNL_SEGMENTATION, "tx-udp_tnl-segmentation"), - ETHT_FEAT(NM_ETHTOOL_ID_FEATURE_TX_VLAN_STAG_HW_INSERT, "tx-vlan-stag-hw-insert"), -}; - -/* the number of kernel features that we handle. It essentially is the sum of all - * kernel_names. So, all ethtool-ids that reference exactly one kernel-name - * (_NM_ETHTOOL_ID_FEATURE_NUM) + some extra, for ethtool-ids that are aliases - * for multiple kernel-names. */ -#define N_ETHTOOL_KERNEL_FEATURES (((guint) _NM_ETHTOOL_ID_FEATURE_NUM) + 8u) - -static void -_ASSERT_ethtool_feature_infos(void) -{ -#if NM_MORE_ASSERTS > 10 - guint i, k, n; - bool found[_NM_ETHTOOL_ID_FEATURE_NUM] = {}; - - G_STATIC_ASSERT_EXPR(G_N_ELEMENTS(_ethtool_feature_infos) == _NM_ETHTOOL_ID_FEATURE_NUM); - - n = 0; - for (i = 0; i < G_N_ELEMENTS(_ethtool_feature_infos); i++) { - NMEthtoolFeatureState kstate; - const NMEthtoolFeatureInfo *inf = &_ethtool_feature_infos[i]; - - g_assert(inf->ethtool_id >= _NM_ETHTOOL_ID_FEATURE_FIRST); - g_assert(inf->ethtool_id <= _NM_ETHTOOL_ID_FEATURE_LAST); - g_assert(inf->n_kernel_names > 0); - - for (k = 0; k < i; k++) - g_assert(inf->ethtool_id != _ethtool_feature_infos[k].ethtool_id); - - g_assert(!found[_NM_ETHTOOL_ID_FEATURE_AS_IDX(inf->ethtool_id)]); - found[_NM_ETHTOOL_ID_FEATURE_AS_IDX(inf->ethtool_id)] = TRUE; - - kstate.idx_kernel_name = inf->n_kernel_names - 1; - g_assert((guint) kstate.idx_kernel_name == (guint)(inf->n_kernel_names - 1)); - - n += inf->n_kernel_names; - for (k = 0; k < inf->n_kernel_names; k++) { - const char *name = inf->kernel_names[k]; - - g_assert(nm_utils_strv_find_first((char **) inf->kernel_names, k, name) < 0); - - /* these offload features are only informational and cannot be set from user-space - * (NETIF_F_NEVER_CHANGE). We should not track them in _ethtool_feature_infos. */ - g_assert(!nm_streq(name, "netns-local")); - g_assert(!nm_streq(name, "tx-lockless")); - g_assert(!nm_streq(name, "vlan-challenged")); - } - } - - for (i = 0; i < _NM_ETHTOOL_ID_FEATURE_NUM; i++) - g_assert(found[i]); - - g_assert(n == N_ETHTOOL_KERNEL_FEATURES); -#endif -} - -static NMEthtoolFeatureStates * -ethtool_get_features(SocketHandle *shandle) -{ - gs_free NMEthtoolFeatureStates * states = NULL; - gs_free struct ethtool_gstrings *ss_features = NULL; - - _ASSERT_ethtool_feature_infos(); - - ss_features = ethtool_get_stringset(shandle, ETH_SS_FEATURES); - if (!ss_features) - return NULL; - - if (ss_features->len > 0) { - gs_free struct ethtool_gfeatures * gfeatures_free = NULL; - struct ethtool_gfeatures * gfeatures; - gsize gfeatures_len; - guint idx; - const NMEthtoolFeatureState * states_list0 = NULL; - const NMEthtoolFeatureState *const *states_plist0 = NULL; - guint states_plist_n = 0; - - gfeatures_len = sizeof(struct ethtool_gfeatures) - + (NM_DIV_ROUND_UP(ss_features->len, 32u) * sizeof(gfeatures->features[0])); - gfeatures = nm_malloc0_maybe_a(300, gfeatures_len, &gfeatures_free); - gfeatures->cmd = ETHTOOL_GFEATURES; - gfeatures->size = NM_DIV_ROUND_UP(ss_features->len, 32u); - if (_ethtool_call_handle(shandle, gfeatures, gfeatures_len) < 0) - return NULL; - - for (idx = 0; idx < G_N_ELEMENTS(_ethtool_feature_infos); idx++) { - const NMEthtoolFeatureInfo *info = &_ethtool_feature_infos[idx]; - guint idx_kernel_name; - - for (idx_kernel_name = 0; idx_kernel_name < info->n_kernel_names; idx_kernel_name++) { - NMEthtoolFeatureState *kstate; - const char * kernel_name = info->kernel_names[idx_kernel_name]; - int i_feature; - guint i_block; - guint32 i_flag; - - i_feature = ethtool_gstrings_find(ss_features, kernel_name); - if (i_feature < 0) - continue; - - i_block = ((guint) i_feature) / 32u; - i_flag = (guint32)(1u << (((guint) i_feature) % 32u)); - - if (!states) { - states = g_malloc0( - sizeof(NMEthtoolFeatureStates) - + (N_ETHTOOL_KERNEL_FEATURES * sizeof(NMEthtoolFeatureState)) - + ((N_ETHTOOL_KERNEL_FEATURES + G_N_ELEMENTS(_ethtool_feature_infos)) - * sizeof(NMEthtoolFeatureState *))); - states_list0 = &states->states_list[0]; - states_plist0 = (gpointer) &states_list0[N_ETHTOOL_KERNEL_FEATURES]; - states->n_ss_features = ss_features->len; - } - - nm_assert(states->n_states < N_ETHTOOL_KERNEL_FEATURES); - kstate = (NMEthtoolFeatureState *) &states_list0[states->n_states]; - states->n_states++; - - kstate->info = info; - kstate->idx_ss_features = i_feature; - kstate->idx_kernel_name = idx_kernel_name; - kstate->available = !!(gfeatures->features[i_block].available & i_flag); - kstate->requested = !!(gfeatures->features[i_block].requested & i_flag); - kstate->active = !!(gfeatures->features[i_block].active & i_flag); - kstate->never_changed = !!(gfeatures->features[i_block].never_changed & i_flag); - - nm_assert(states_plist_n - < N_ETHTOOL_KERNEL_FEATURES + G_N_ELEMENTS(_ethtool_feature_infos)); - - if (!states->states_indexed[_NM_ETHTOOL_ID_FEATURE_AS_IDX(info->ethtool_id)]) - states->states_indexed[_NM_ETHTOOL_ID_FEATURE_AS_IDX(info->ethtool_id)] = - &states_plist0[states_plist_n]; - ((const NMEthtoolFeatureState **) states_plist0)[states_plist_n] = kstate; - states_plist_n++; - } - - if (states && states->states_indexed[_NM_ETHTOOL_ID_FEATURE_AS_IDX(info->ethtool_id)]) { - nm_assert(states_plist_n - < N_ETHTOOL_KERNEL_FEATURES + G_N_ELEMENTS(_ethtool_feature_infos)); - nm_assert(!states_plist0[states_plist_n]); - states_plist_n++; - } - } - } - - return g_steal_pointer(&states); -} - -NMEthtoolFeatureStates * -nmp_utils_ethtool_get_features(int ifindex) -{ - nm_auto_socket_handle SocketHandle shandle = SOCKET_HANDLE_INIT(ifindex); - NMEthtoolFeatureStates * features; - - g_return_val_if_fail(ifindex > 0, 0); - - features = ethtool_get_features(&shandle); - - if (!features) { - nm_log_trace(LOGD_PLATFORM, - "ethtool[%d]: %s: failure getting features", - ifindex, - "get-features"); - return NULL; - } - - nm_log_trace(LOGD_PLATFORM, - "ethtool[%d]: %s: retrieved kernel features", - ifindex, - "get-features"); - return features; -} - -static const char * -_ethtool_feature_state_to_string(char * buf, - gsize buf_size, - const NMEthtoolFeatureState *s, - const char * prefix) -{ - int l; - - l = g_snprintf(buf, - buf_size, - "%s %s%s", - prefix ?: "", - ONOFF(s->active), - (!s->available || s->never_changed) - ? ", [fixed]" - : ((s->requested != s->active) - ? (s->requested ? ", [requested on]" : ", [requested off]") - : "")); - nm_assert(l < buf_size); - return buf; -} - -gboolean -nmp_utils_ethtool_set_features( - int ifindex, - const NMEthtoolFeatureStates *features, - const NMOptionBool *requested /* indexed by NMEthtoolID - _NM_ETHTOOL_ID_FEATURE_FIRST */, - gboolean do_set /* or reset */) -{ - nm_auto_socket_handle SocketHandle shandle = SOCKET_HANDLE_INIT(ifindex); - gs_free struct ethtool_sfeatures * sfeatures_free = NULL; - struct ethtool_sfeatures * sfeatures; - gsize sfeatures_len; - int r; - guint i, j; - struct { - const NMEthtoolFeatureState *f_state; - NMOptionBool requested; - } set_states[N_ETHTOOL_KERNEL_FEATURES]; - guint set_states_n = 0; - gboolean success = TRUE; - - g_return_val_if_fail(ifindex > 0, 0); - g_return_val_if_fail(features, 0); - g_return_val_if_fail(requested, 0); - - nm_assert(features->n_states <= N_ETHTOOL_KERNEL_FEATURES); - - for (i = 0; i < _NM_ETHTOOL_ID_FEATURE_NUM; i++) { - const NMEthtoolFeatureState *const *states_indexed; - - if (requested[i] == NM_OPTION_BOOL_DEFAULT) - continue; - - if (!(states_indexed = features->states_indexed[i])) { - if (do_set) { - nm_log_trace(LOGD_PLATFORM, - "ethtool[%d]: %s: set feature %s: skip (not found)", - ifindex, - "set-features", - nm_ethtool_data[i + _NM_ETHTOOL_ID_FEATURE_FIRST]->optname); - success = FALSE; - } - continue; - } - - for (j = 0; states_indexed[j]; j++) { - const NMEthtoolFeatureState *s = states_indexed[j]; - char sbuf[255]; - - if (set_states_n >= G_N_ELEMENTS(set_states)) - g_return_val_if_reached(FALSE); - - if (s->never_changed) { - nm_log_trace(LOGD_PLATFORM, - "ethtool[%d]: %s: %s feature %s (%s): %s, %s (skip feature marked as " - "never changed)", - ifindex, - "set-features", - do_set ? "set" : "reset", - nm_ethtool_data[i + _NM_ETHTOOL_ID_FEATURE_FIRST]->optname, - s->info->kernel_names[s->idx_kernel_name], - ONOFF(do_set ? requested[i] == NM_OPTION_BOOL_TRUE : s->active), - _ethtool_feature_state_to_string(sbuf, - sizeof(sbuf), - s, - do_set ? " currently:" : " before:")); - continue; - } - - nm_log_trace(LOGD_PLATFORM, - "ethtool[%d]: %s: %s feature %s (%s): %s, %s", - ifindex, - "set-features", - do_set ? "set" : "reset", - nm_ethtool_data[i + _NM_ETHTOOL_ID_FEATURE_FIRST]->optname, - s->info->kernel_names[s->idx_kernel_name], - ONOFF(do_set ? requested[i] == NM_OPTION_BOOL_TRUE : s->active), - _ethtool_feature_state_to_string(sbuf, - sizeof(sbuf), - s, - do_set ? " currently:" : " before:")); - - if (do_set && (!s->available || s->never_changed) - && (s->active != (requested[i] == NM_OPTION_BOOL_TRUE))) { - /* we request to change a flag which kernel reported as fixed. - * While the ethtool operation will silently succeed, mark the request - * as failure. */ - success = FALSE; - } - - set_states[set_states_n].f_state = s; - set_states[set_states_n].requested = requested[i]; - set_states_n++; - } - } - - if (set_states_n == 0) { - nm_log_trace(LOGD_PLATFORM, - "ethtool[%d]: %s: no feature requested", - ifindex, - "set-features"); - return TRUE; - } - - sfeatures_len = - sizeof(struct ethtool_sfeatures) - + (NM_DIV_ROUND_UP(features->n_ss_features, 32U) * sizeof(sfeatures->features[0])); - sfeatures = nm_malloc0_maybe_a(300, sfeatures_len, &sfeatures_free); - sfeatures->cmd = ETHTOOL_SFEATURES; - sfeatures->size = NM_DIV_ROUND_UP(features->n_ss_features, 32U); - - for (i = 0; i < set_states_n; i++) { - const NMEthtoolFeatureState *s = set_states[i].f_state; - guint i_block; - guint32 i_flag; - gboolean is_requested; - - i_block = s->idx_ss_features / 32u; - i_flag = (guint32)(1u << (s->idx_ss_features % 32u)); - - sfeatures->features[i_block].valid |= i_flag; - - if (do_set) - is_requested = (set_states[i].requested == NM_OPTION_BOOL_TRUE); - else - is_requested = s->active; - - if (is_requested) - sfeatures->features[i_block].requested |= i_flag; - else - sfeatures->features[i_block].requested &= ~i_flag; - } - - r = _ethtool_call_handle(&shandle, sfeatures, sfeatures_len); - if (r < 0) { - success = FALSE; - nm_log_trace(LOGD_PLATFORM, - "ethtool[%d]: %s: failure setting features (%s)", - ifindex, - "set-features", - nm_strerror_native(-r)); - return FALSE; - } - - nm_log_trace(LOGD_PLATFORM, - "ethtool[%d]: %s: %s", - ifindex, - "set-features", - success ? "successfully setting features" - : "at least some of the features were not successfully set"); - return success; -} - -static gboolean -ethtool_get_coalesce(SocketHandle *shandle, NMEthtoolCoalesceState *coalesce) -{ - struct ethtool_coalesce eth_data; - - eth_data.cmd = ETHTOOL_GCOALESCE; - - if (_ethtool_call_handle(shandle, ð_data, sizeof(struct ethtool_coalesce)) != 0) - return FALSE; - - *coalesce = (NMEthtoolCoalesceState){ - .s = { - [_NM_ETHTOOL_ID_COALESCE_AS_IDX(NM_ETHTOOL_ID_COALESCE_RX_USECS)] = - eth_data.rx_coalesce_usecs, - [_NM_ETHTOOL_ID_COALESCE_AS_IDX(NM_ETHTOOL_ID_COALESCE_RX_FRAMES)] = - eth_data.rx_max_coalesced_frames, - [_NM_ETHTOOL_ID_COALESCE_AS_IDX(NM_ETHTOOL_ID_COALESCE_RX_USECS_IRQ)] = - eth_data.rx_coalesce_usecs_irq, - [_NM_ETHTOOL_ID_COALESCE_AS_IDX(NM_ETHTOOL_ID_COALESCE_RX_FRAMES_IRQ)] = - eth_data.rx_max_coalesced_frames_irq, - [_NM_ETHTOOL_ID_COALESCE_AS_IDX(NM_ETHTOOL_ID_COALESCE_TX_USECS)] = - eth_data.tx_coalesce_usecs, - [_NM_ETHTOOL_ID_COALESCE_AS_IDX(NM_ETHTOOL_ID_COALESCE_TX_FRAMES)] = - eth_data.tx_max_coalesced_frames, - [_NM_ETHTOOL_ID_COALESCE_AS_IDX(NM_ETHTOOL_ID_COALESCE_TX_USECS_IRQ)] = - eth_data.tx_coalesce_usecs_irq, - [_NM_ETHTOOL_ID_COALESCE_AS_IDX(NM_ETHTOOL_ID_COALESCE_TX_FRAMES_IRQ)] = - eth_data.tx_max_coalesced_frames_irq, - [_NM_ETHTOOL_ID_COALESCE_AS_IDX(NM_ETHTOOL_ID_COALESCE_STATS_BLOCK_USECS)] = - eth_data.stats_block_coalesce_usecs, - [_NM_ETHTOOL_ID_COALESCE_AS_IDX(NM_ETHTOOL_ID_COALESCE_ADAPTIVE_RX)] = - eth_data.use_adaptive_rx_coalesce, - [_NM_ETHTOOL_ID_COALESCE_AS_IDX(NM_ETHTOOL_ID_COALESCE_ADAPTIVE_TX)] = - eth_data.use_adaptive_tx_coalesce, - [_NM_ETHTOOL_ID_COALESCE_AS_IDX(NM_ETHTOOL_ID_COALESCE_PKT_RATE_LOW)] = - eth_data.pkt_rate_low, - [_NM_ETHTOOL_ID_COALESCE_AS_IDX(NM_ETHTOOL_ID_COALESCE_RX_USECS_LOW)] = - eth_data.rx_coalesce_usecs_low, - [_NM_ETHTOOL_ID_COALESCE_AS_IDX(NM_ETHTOOL_ID_COALESCE_RX_FRAMES_LOW)] = - eth_data.rx_max_coalesced_frames_low, - [_NM_ETHTOOL_ID_COALESCE_AS_IDX(NM_ETHTOOL_ID_COALESCE_TX_USECS_LOW)] = - eth_data.tx_coalesce_usecs_low, - [_NM_ETHTOOL_ID_COALESCE_AS_IDX(NM_ETHTOOL_ID_COALESCE_TX_FRAMES_LOW)] = - eth_data.tx_max_coalesced_frames_low, - [_NM_ETHTOOL_ID_COALESCE_AS_IDX(NM_ETHTOOL_ID_COALESCE_PKT_RATE_HIGH)] = - eth_data.pkt_rate_high, - [_NM_ETHTOOL_ID_COALESCE_AS_IDX(NM_ETHTOOL_ID_COALESCE_RX_USECS_HIGH)] = - eth_data.rx_coalesce_usecs_high, - [_NM_ETHTOOL_ID_COALESCE_AS_IDX(NM_ETHTOOL_ID_COALESCE_RX_FRAMES_HIGH)] = - eth_data.rx_max_coalesced_frames_high, - [_NM_ETHTOOL_ID_COALESCE_AS_IDX(NM_ETHTOOL_ID_COALESCE_TX_USECS_HIGH)] = - eth_data.tx_coalesce_usecs_high, - [_NM_ETHTOOL_ID_COALESCE_AS_IDX(NM_ETHTOOL_ID_COALESCE_TX_FRAMES_HIGH)] = - eth_data.tx_max_coalesced_frames_high, - [_NM_ETHTOOL_ID_COALESCE_AS_IDX(NM_ETHTOOL_ID_COALESCE_SAMPLE_INTERVAL)] = - eth_data.rate_sample_interval, - }}; - return TRUE; -} - -gboolean -nmp_utils_ethtool_get_coalesce(int ifindex, NMEthtoolCoalesceState *coalesce) -{ - nm_auto_socket_handle SocketHandle shandle = SOCKET_HANDLE_INIT(ifindex); - - g_return_val_if_fail(ifindex > 0, FALSE); - g_return_val_if_fail(coalesce, FALSE); - - if (!ethtool_get_coalesce(&shandle, coalesce)) { - nm_log_trace(LOGD_PLATFORM, - "ethtool[%d]: %s: failure getting coalesce settings", - ifindex, - "get-coalesce"); - return FALSE; - } - - nm_log_trace(LOGD_PLATFORM, - "ethtool[%d]: %s: retrieved kernel coalesce settings", - ifindex, - "get-coalesce"); - return TRUE; -} - -static gboolean -ethtool_set_coalesce(SocketHandle *shandle, const NMEthtoolCoalesceState *coalesce) -{ - struct ethtool_coalesce eth_data; - gboolean success; - - nm_assert(shandle); - nm_assert(coalesce); - - eth_data = (struct ethtool_coalesce){ - .cmd = ETHTOOL_SCOALESCE, - .rx_coalesce_usecs = - coalesce->s[_NM_ETHTOOL_ID_COALESCE_AS_IDX(NM_ETHTOOL_ID_COALESCE_RX_USECS)], - .rx_max_coalesced_frames = - coalesce->s[_NM_ETHTOOL_ID_COALESCE_AS_IDX(NM_ETHTOOL_ID_COALESCE_RX_FRAMES)], - .rx_coalesce_usecs_irq = - coalesce->s[_NM_ETHTOOL_ID_COALESCE_AS_IDX(NM_ETHTOOL_ID_COALESCE_RX_USECS_IRQ)], - .rx_max_coalesced_frames_irq = - coalesce->s[_NM_ETHTOOL_ID_COALESCE_AS_IDX(NM_ETHTOOL_ID_COALESCE_RX_FRAMES_IRQ)], - .tx_coalesce_usecs = - coalesce->s[_NM_ETHTOOL_ID_COALESCE_AS_IDX(NM_ETHTOOL_ID_COALESCE_TX_USECS)], - .tx_max_coalesced_frames = - coalesce->s[_NM_ETHTOOL_ID_COALESCE_AS_IDX(NM_ETHTOOL_ID_COALESCE_TX_FRAMES)], - .tx_coalesce_usecs_irq = - coalesce->s[_NM_ETHTOOL_ID_COALESCE_AS_IDX(NM_ETHTOOL_ID_COALESCE_TX_USECS_IRQ)], - .tx_max_coalesced_frames_irq = - coalesce->s[_NM_ETHTOOL_ID_COALESCE_AS_IDX(NM_ETHTOOL_ID_COALESCE_TX_FRAMES_IRQ)], - .stats_block_coalesce_usecs = - coalesce->s[_NM_ETHTOOL_ID_COALESCE_AS_IDX(NM_ETHTOOL_ID_COALESCE_STATS_BLOCK_USECS)], - .use_adaptive_rx_coalesce = - coalesce->s[_NM_ETHTOOL_ID_COALESCE_AS_IDX(NM_ETHTOOL_ID_COALESCE_ADAPTIVE_RX)], - .use_adaptive_tx_coalesce = - coalesce->s[_NM_ETHTOOL_ID_COALESCE_AS_IDX(NM_ETHTOOL_ID_COALESCE_ADAPTIVE_TX)], - .pkt_rate_low = - coalesce->s[_NM_ETHTOOL_ID_COALESCE_AS_IDX(NM_ETHTOOL_ID_COALESCE_PKT_RATE_LOW)], - .rx_coalesce_usecs_low = - coalesce->s[_NM_ETHTOOL_ID_COALESCE_AS_IDX(NM_ETHTOOL_ID_COALESCE_RX_USECS_LOW)], - .rx_max_coalesced_frames_low = - coalesce->s[_NM_ETHTOOL_ID_COALESCE_AS_IDX(NM_ETHTOOL_ID_COALESCE_RX_FRAMES_LOW)], - .tx_coalesce_usecs_low = - coalesce->s[_NM_ETHTOOL_ID_COALESCE_AS_IDX(NM_ETHTOOL_ID_COALESCE_TX_USECS_LOW)], - .tx_max_coalesced_frames_low = - coalesce->s[_NM_ETHTOOL_ID_COALESCE_AS_IDX(NM_ETHTOOL_ID_COALESCE_TX_FRAMES_LOW)], - .pkt_rate_high = - coalesce->s[_NM_ETHTOOL_ID_COALESCE_AS_IDX(NM_ETHTOOL_ID_COALESCE_PKT_RATE_HIGH)], - .rx_coalesce_usecs_high = - coalesce->s[_NM_ETHTOOL_ID_COALESCE_AS_IDX(NM_ETHTOOL_ID_COALESCE_RX_USECS_HIGH)], - .rx_max_coalesced_frames_high = - coalesce->s[_NM_ETHTOOL_ID_COALESCE_AS_IDX(NM_ETHTOOL_ID_COALESCE_RX_FRAMES_HIGH)], - .tx_coalesce_usecs_high = - coalesce->s[_NM_ETHTOOL_ID_COALESCE_AS_IDX(NM_ETHTOOL_ID_COALESCE_TX_USECS_HIGH)], - .tx_max_coalesced_frames_high = - coalesce->s[_NM_ETHTOOL_ID_COALESCE_AS_IDX(NM_ETHTOOL_ID_COALESCE_TX_FRAMES_HIGH)], - .rate_sample_interval = - coalesce->s[_NM_ETHTOOL_ID_COALESCE_AS_IDX(NM_ETHTOOL_ID_COALESCE_SAMPLE_INTERVAL)], - }; - - success = (_ethtool_call_handle(shandle, ð_data, sizeof(struct ethtool_coalesce)) == 0); - return success; -} - -gboolean -nmp_utils_ethtool_set_coalesce(int ifindex, const NMEthtoolCoalesceState *coalesce) -{ - nm_auto_socket_handle SocketHandle shandle = SOCKET_HANDLE_INIT(ifindex); - - g_return_val_if_fail(ifindex > 0, FALSE); - g_return_val_if_fail(coalesce, FALSE); - - if (!ethtool_set_coalesce(&shandle, coalesce)) { - nm_log_trace(LOGD_PLATFORM, - "ethtool[%d]: %s: failure setting coalesce settings", - ifindex, - "set-coalesce"); - return FALSE; - } - - nm_log_trace(LOGD_PLATFORM, - "ethtool[%d]: %s: set kernel coalesce settings", - ifindex, - "set-coalesce"); - return TRUE; -} - -static gboolean -ethtool_get_ring(SocketHandle *shandle, NMEthtoolRingState *ring) -{ - struct ethtool_ringparam eth_data; - - eth_data.cmd = ETHTOOL_GRINGPARAM; - - if (_ethtool_call_handle(shandle, ð_data, sizeof(struct ethtool_ringparam)) != 0) - return FALSE; - - ring->rx_pending = eth_data.rx_pending; - ring->rx_jumbo_pending = eth_data.rx_jumbo_pending; - ring->rx_mini_pending = eth_data.rx_mini_pending; - ring->tx_pending = eth_data.tx_pending; - - return TRUE; -} - -gboolean -nmp_utils_ethtool_get_ring(int ifindex, NMEthtoolRingState *ring) -{ - nm_auto_socket_handle SocketHandle shandle = SOCKET_HANDLE_INIT(ifindex); - - g_return_val_if_fail(ifindex > 0, FALSE); - g_return_val_if_fail(ring, FALSE); - - if (!ethtool_get_ring(&shandle, ring)) { - nm_log_trace(LOGD_PLATFORM, - "ethtool[%d]: %s: failure getting ring settings", - ifindex, - "get-ring"); - return FALSE; - } - - nm_log_trace(LOGD_PLATFORM, - "ethtool[%d]: %s: retrieved kernel ring settings", - ifindex, - "get-ring"); - return TRUE; -} - -static gboolean -ethtool_set_ring(SocketHandle *shandle, const NMEthtoolRingState *ring) -{ - gboolean success; - struct ethtool_ringparam eth_data; - - g_return_val_if_fail(shandle, FALSE); - g_return_val_if_fail(ring, FALSE); - - eth_data = (struct ethtool_ringparam){ - .cmd = ETHTOOL_SRINGPARAM, - .rx_pending = ring->rx_pending, - .rx_jumbo_pending = ring->rx_jumbo_pending, - .rx_mini_pending = ring->rx_mini_pending, - .tx_pending = ring->tx_pending, - }; - - success = (_ethtool_call_handle(shandle, ð_data, sizeof(struct ethtool_ringparam)) == 0); - return success; -} - -gboolean -nmp_utils_ethtool_set_ring(int ifindex, const NMEthtoolRingState *ring) -{ - nm_auto_socket_handle SocketHandle shandle = SOCKET_HANDLE_INIT(ifindex); - - g_return_val_if_fail(ifindex > 0, FALSE); - g_return_val_if_fail(ring, FALSE); - - if (!ethtool_set_ring(&shandle, ring)) { - nm_log_trace(LOGD_PLATFORM, - "ethtool[%d]: %s: failure setting ring settings", - ifindex, - "set-ring"); - return FALSE; - } - - nm_log_trace(LOGD_PLATFORM, "ethtool[%d]: %s: set kernel ring settings", ifindex, "set-ring"); - return TRUE; -} - -/*****************************************************************************/ - -gboolean -nmp_utils_ethtool_get_driver_info(int ifindex, NMPUtilsEthtoolDriverInfo *data) -{ - struct ethtool_drvinfo *drvinfo; - - G_STATIC_ASSERT_EXPR(sizeof(*data) == sizeof(*drvinfo)); - G_STATIC_ASSERT_EXPR(offsetof(NMPUtilsEthtoolDriverInfo, driver) - == offsetof(struct ethtool_drvinfo, driver)); - G_STATIC_ASSERT_EXPR(offsetof(NMPUtilsEthtoolDriverInfo, version) - == offsetof(struct ethtool_drvinfo, version)); - G_STATIC_ASSERT_EXPR(offsetof(NMPUtilsEthtoolDriverInfo, fw_version) - == offsetof(struct ethtool_drvinfo, fw_version)); - G_STATIC_ASSERT_EXPR(sizeof(data->driver) == sizeof(drvinfo->driver)); - G_STATIC_ASSERT_EXPR(sizeof(data->version) == sizeof(drvinfo->version)); - G_STATIC_ASSERT_EXPR(sizeof(data->fw_version) == sizeof(drvinfo->fw_version)); - - g_return_val_if_fail(ifindex > 0, FALSE); - g_return_val_if_fail(data, FALSE); - - drvinfo = (struct ethtool_drvinfo *) data; - *drvinfo = (struct ethtool_drvinfo){ - .cmd = ETHTOOL_GDRVINFO, - }; - return _ethtool_call_once(ifindex, drvinfo, sizeof(*drvinfo)) >= 0; -} - -gboolean -nmp_utils_ethtool_get_permanent_address(int ifindex, guint8 *buf, size_t *length) -{ - struct { - struct ethtool_perm_addr e; - guint8 _extra_data[_NM_UTILS_HWADDR_LEN_MAX + 1]; - } edata = { - .e.cmd = ETHTOOL_GPERMADDR, - .e.size = _NM_UTILS_HWADDR_LEN_MAX, - }; - const guint8 *pdata; - - guint i; - - g_return_val_if_fail(ifindex > 0, FALSE); - - if (_ethtool_call_once(ifindex, &edata, sizeof(edata)) < 0) - return FALSE; - - if (edata.e.size > _NM_UTILS_HWADDR_LEN_MAX) - return FALSE; - if (edata.e.size < 1) - return FALSE; - - pdata = (const guint8 *) edata.e.data; - - if (NM_IN_SET(pdata[0], 0, 0xFF)) { - /* Some drivers might return a permanent address of all zeros. - * Reject that (rh#1264024) - * - * Some drivers return a permanent address of all ones. Reject that too */ - for (i = 1; i < edata.e.size; i++) { - if (pdata[0] != pdata[i]) - goto not_all_0or1; - } - return FALSE; - } - -not_all_0or1: - memcpy(buf, pdata, edata.e.size); - *length = edata.e.size; - return TRUE; -} - -gboolean -nmp_utils_ethtool_supports_carrier_detect(int ifindex) -{ - struct ethtool_cmd edata = {.cmd = ETHTOOL_GLINK}; - - g_return_val_if_fail(ifindex > 0, FALSE); - - /* We ignore the result. If the ETHTOOL_GLINK call succeeded, then we - * assume the device supports carrier-detect, otherwise we assume it - * doesn't. - */ - return _ethtool_call_once(ifindex, &edata, sizeof(edata)) >= 0; -} - -gboolean -nmp_utils_ethtool_supports_vlans(int ifindex) -{ - nm_auto_socket_handle SocketHandle shandle = SOCKET_HANDLE_INIT(ifindex); - gs_free struct ethtool_gfeatures * features_free = NULL; - struct ethtool_gfeatures * features; - gsize features_len; - int idx, block, bit, size; - - g_return_val_if_fail(ifindex > 0, FALSE); - - idx = ethtool_get_stringset_index(&shandle, ETH_SS_FEATURES, "vlan-challenged"); - if (idx < 0) { - nm_log_dbg(LOGD_PLATFORM, - "ethtool[%d]: vlan-challenged ethtool feature does not exist?", - ifindex); - return FALSE; - } - - block = idx / 32; - bit = idx % 32; - size = block + 1; - - features_len = sizeof(*features) + (size * sizeof(struct ethtool_get_features_block)); - features = nm_malloc0_maybe_a(300, features_len, &features_free); - features->cmd = ETHTOOL_GFEATURES; - features->size = size; - - if (_ethtool_call_handle(&shandle, features, features_len) < 0) - return FALSE; - - return !(features->features[block].active & (1 << bit)); -} - -int -nmp_utils_ethtool_get_peer_ifindex(int ifindex) -{ - nm_auto_socket_handle SocketHandle shandle = SOCKET_HANDLE_INIT(ifindex); - gsize stats_len; - gs_free struct ethtool_stats * stats_free = NULL; - struct ethtool_stats * stats; - int peer_ifindex_stat; - - g_return_val_if_fail(ifindex > 0, 0); - - peer_ifindex_stat = ethtool_get_stringset_index(&shandle, ETH_SS_STATS, "peer_ifindex"); - if (peer_ifindex_stat < 0) { - nm_log_dbg(LOGD_PLATFORM, "ethtool[%d]: peer_ifindex stat does not exist?", ifindex); - return FALSE; - } - - stats_len = sizeof(*stats) + (peer_ifindex_stat + 1) * sizeof(guint64); - stats = nm_malloc0_maybe_a(300, stats_len, &stats_free); - stats->cmd = ETHTOOL_GSTATS; - stats->n_stats = peer_ifindex_stat + 1; - if (_ethtool_call_handle(&shandle, stats, stats_len) < 0) - return 0; - - return stats->data[peer_ifindex_stat]; -} - -gboolean -nmp_utils_ethtool_get_wake_on_lan(int ifindex) -{ - struct ethtool_wolinfo wol = { - .cmd = ETHTOOL_GWOL, - }; - - g_return_val_if_fail(ifindex > 0, FALSE); - - if (_ethtool_call_once(ifindex, &wol, sizeof(wol)) < 0) - return FALSE; - - return wol.wolopts != 0; -} - -gboolean -nmp_utils_ethtool_get_link_settings(int ifindex, - gboolean * out_autoneg, - guint32 * out_speed, - NMPlatformLinkDuplexType *out_duplex) -{ - struct ethtool_cmd edata = { - .cmd = ETHTOOL_GSET, - }; - - g_return_val_if_fail(ifindex > 0, FALSE); - - if (_ethtool_call_once(ifindex, &edata, sizeof(edata)) < 0) - return FALSE; - - NM_SET_OUT(out_autoneg, (edata.autoneg == AUTONEG_ENABLE)); - - if (out_speed) { - guint32 speed; - - speed = ethtool_cmd_speed(&edata); - if (speed == G_MAXUINT16 || speed == G_MAXUINT32) - speed = 0; - - *out_speed = speed; - } - - if (out_duplex) { - switch (edata.duplex) { - case DUPLEX_HALF: - *out_duplex = NM_PLATFORM_LINK_DUPLEX_HALF; - break; - case DUPLEX_FULL: - *out_duplex = NM_PLATFORM_LINK_DUPLEX_FULL; - break; - default: /* DUPLEX_UNKNOWN */ - *out_duplex = NM_PLATFORM_LINK_DUPLEX_UNKNOWN; - break; - } - } - - return TRUE; -} - -#define ADVERTISED_INVALID 0 -#define BASET_ALL_MODES \ - (ADVERTISED_10baseT_Half | ADVERTISED_10baseT_Full | ADVERTISED_100baseT_Half \ - | ADVERTISED_100baseT_Full | ADVERTISED_1000baseT_Half | ADVERTISED_1000baseT_Full \ - | ADVERTISED_10000baseT_Full) - -static guint32 -get_baset_mode(guint32 speed, NMPlatformLinkDuplexType duplex) -{ - if (duplex == NM_PLATFORM_LINK_DUPLEX_UNKNOWN) - return ADVERTISED_INVALID; - - if (duplex == NM_PLATFORM_LINK_DUPLEX_HALF) { - switch (speed) { - case 10: - return ADVERTISED_10baseT_Half; - case 100: - return ADVERTISED_100baseT_Half; - case 1000: - return ADVERTISED_1000baseT_Half; - default: - return ADVERTISED_INVALID; - } - } else { - switch (speed) { - case 10: - return ADVERTISED_10baseT_Full; - case 100: - return ADVERTISED_100baseT_Full; - case 1000: - return ADVERTISED_1000baseT_Full; - case 10000: - return ADVERTISED_10000baseT_Full; - default: - return ADVERTISED_INVALID; - } - } -} - -gboolean -nmp_utils_ethtool_set_link_settings(int ifindex, - gboolean autoneg, - guint32 speed, - NMPlatformLinkDuplexType duplex) -{ - nm_auto_socket_handle SocketHandle shandle = SOCKET_HANDLE_INIT(ifindex); - struct ethtool_cmd edata = { - .cmd = ETHTOOL_GSET, - }; - - g_return_val_if_fail(ifindex > 0, FALSE); - g_return_val_if_fail((speed && duplex != NM_PLATFORM_LINK_DUPLEX_UNKNOWN) - || (!speed && duplex == NM_PLATFORM_LINK_DUPLEX_UNKNOWN), - FALSE); - - /* retrieve first current settings */ - if (_ethtool_call_handle(&shandle, &edata, sizeof(edata)) < 0) - return FALSE; - - /* FIXME: try first new ETHTOOL_GLINKSETTINGS/SLINKSETTINGS API - * https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=3f1ac7a700d039c61d8d8b99f28d605d489a60cf - */ - - /* then change the needed ones */ - edata.cmd = ETHTOOL_SSET; - if (autoneg) { - edata.autoneg = AUTONEG_ENABLE; - if (!speed) - edata.advertising = edata.supported; - else { - guint32 mode; - - mode = get_baset_mode(speed, duplex); - - if (!mode) { - nm_log_trace(LOGD_PLATFORM, - "ethtool[%d]: %uBASE-T %s duplex mode cannot be advertised", - ifindex, - speed, - nm_platform_link_duplex_type_to_string(duplex)); - return FALSE; - } - if (!(edata.supported & mode)) { - nm_log_trace(LOGD_PLATFORM, - "ethtool[%d]: device does not support %uBASE-T %s duplex mode", - ifindex, - speed, - nm_platform_link_duplex_type_to_string(duplex)); - return FALSE; - } - edata.advertising = (edata.supported & ~BASET_ALL_MODES) | mode; - } - } else { - edata.autoneg = AUTONEG_DISABLE; - - if (speed) - ethtool_cmd_speed_set(&edata, speed); - - switch (duplex) { - case NM_PLATFORM_LINK_DUPLEX_HALF: - edata.duplex = DUPLEX_HALF; - break; - case NM_PLATFORM_LINK_DUPLEX_FULL: - edata.duplex = DUPLEX_FULL; - break; - case NM_PLATFORM_LINK_DUPLEX_UNKNOWN: - break; - default: - g_return_val_if_reached(FALSE); - } - } - - return _ethtool_call_handle(&shandle, &edata, sizeof(edata)) >= 0; -} - -gboolean -nmp_utils_ethtool_set_wake_on_lan(int ifindex, - _NMSettingWiredWakeOnLan wol, - const char * wol_password) -{ - struct ethtool_wolinfo wol_info = { - .cmd = ETHTOOL_SWOL, - .wolopts = 0, - }; - - g_return_val_if_fail(ifindex > 0, FALSE); - - if (wol == _NM_SETTING_WIRED_WAKE_ON_LAN_IGNORE) - return TRUE; - - nm_log_dbg(LOGD_PLATFORM, - "ethtool[%d]: setting Wake-on-LAN options 0x%x, password '%s'", - ifindex, - (unsigned) wol, - wol_password); - - if (NM_FLAGS_HAS(wol, _NM_SETTING_WIRED_WAKE_ON_LAN_PHY)) - wol_info.wolopts |= WAKE_PHY; - if (NM_FLAGS_HAS(wol, _NM_SETTING_WIRED_WAKE_ON_LAN_UNICAST)) - wol_info.wolopts |= WAKE_UCAST; - if (NM_FLAGS_HAS(wol, _NM_SETTING_WIRED_WAKE_ON_LAN_MULTICAST)) - wol_info.wolopts |= WAKE_MCAST; - if (NM_FLAGS_HAS(wol, _NM_SETTING_WIRED_WAKE_ON_LAN_BROADCAST)) - wol_info.wolopts |= WAKE_BCAST; - if (NM_FLAGS_HAS(wol, _NM_SETTING_WIRED_WAKE_ON_LAN_ARP)) - wol_info.wolopts |= WAKE_ARP; - if (NM_FLAGS_HAS(wol, _NM_SETTING_WIRED_WAKE_ON_LAN_MAGIC)) - wol_info.wolopts |= WAKE_MAGIC; - - if (wol_password) { - if (!_nm_utils_hwaddr_aton_exact(wol_password, wol_info.sopass, ETH_ALEN)) { - nm_log_dbg(LOGD_PLATFORM, - "ethtool[%d]: couldn't parse Wake-on-LAN password '%s'", - ifindex, - wol_password); - return FALSE; - } - wol_info.wolopts |= WAKE_MAGICSECURE; - } - - return _ethtool_call_once(ifindex, &wol_info, sizeof(wol_info)) >= 0; -} - -/****************************************************************************** - * mii - *****************************************************************************/ - -gboolean -nmp_utils_mii_supports_carrier_detect(int ifindex) -{ - nm_auto_socket_handle SocketHandle shandle = SOCKET_HANDLE_INIT(ifindex); - int r; - struct ifreq ifr; - struct mii_ioctl_data * mii; - - g_return_val_if_fail(ifindex > 0, FALSE); - - r = _ioctl_call("mii", - "SIOCGMIIPHY", - SIOCGMIIPHY, - shandle.ifindex, - &shandle.fd, - shandle.ifname, - IOCTL_CALL_DATA_TYPE_NONE, - NULL, - 0, - &ifr); - if (r < 0) - return FALSE; - - /* If we can read the BMSR register, we assume that the card supports MII link detection */ - mii = (struct mii_ioctl_data *) &ifr.ifr_ifru; - mii->reg_num = MII_BMSR; - - r = _ioctl_call("mii", - "SIOCGMIIREG", - SIOCGMIIREG, - shandle.ifindex, - &shandle.fd, - shandle.ifname, - IOCTL_CALL_DATA_TYPE_IFRU, - mii, - sizeof(*mii), - &ifr); - if (r < 0) - return FALSE; - - mii = (struct mii_ioctl_data *) &ifr.ifr_ifru; - nm_log_trace(LOGD_PLATFORM, - "mii[%d,%s]: carrier-detect yes: SIOCGMIIREG result 0x%X", - ifindex, - shandle.ifname, - mii->val_out); - return TRUE; -} - -/****************************************************************************** - * udev - *****************************************************************************/ - -const char * -nmp_utils_udev_get_driver(struct udev_device *udevice) -{ - struct udev_device *parent = NULL, *grandparent = NULL; - const char * driver, *subsys; - - driver = udev_device_get_driver(udevice); - if (driver) - goto out; - - /* Try the parent */ - parent = udev_device_get_parent(udevice); - if (parent) { - driver = udev_device_get_driver(parent); - if (!driver) { - /* Try the grandparent if it's an ibmebus device or if the - * subsys is NULL which usually indicates some sort of - * platform device like a 'gadget' net interface. - */ - subsys = udev_device_get_subsystem(parent); - if ((g_strcmp0(subsys, "ibmebus") == 0) || (subsys == NULL)) { - grandparent = udev_device_get_parent(parent); - if (grandparent) - driver = udev_device_get_driver(grandparent); - } - } - } - -out: - /* Intern the string so we don't have to worry about memory - * management in NMPlatformLink. */ - return g_intern_string(driver); -} - -/****************************************************************************** - * utils - *****************************************************************************/ - -NMIPConfigSource -nmp_utils_ip_config_source_from_rtprot(guint8 rtprot) -{ - return ((int) rtprot) + 1; -} - -NMIPConfigSource -nmp_utils_ip_config_source_round_trip_rtprot(NMIPConfigSource source) -{ - /* when adding a route to kernel for a give @source, the resulting route - * will be put into the cache with a source of NM_IP_CONFIG_SOURCE_RTPROT_*. - * This function returns that. */ - return nmp_utils_ip_config_source_from_rtprot( - nmp_utils_ip_config_source_coerce_to_rtprot(source)); -} - -guint8 -nmp_utils_ip_config_source_coerce_to_rtprot(NMIPConfigSource source) -{ - /* when adding a route to kernel, we coerce the @source field - * to rtm_protocol. This is not lossless as we map different - * source values to the same RTPROT uint8 value. */ - if (source <= NM_IP_CONFIG_SOURCE_UNKNOWN) - return RTPROT_UNSPEC; - - if (source <= _NM_IP_CONFIG_SOURCE_RTPROT_LAST) - return source - 1; - - switch (source) { - case NM_IP_CONFIG_SOURCE_KERNEL: - return RTPROT_KERNEL; - case NM_IP_CONFIG_SOURCE_IP6LL: - return RTPROT_KERNEL; - case NM_IP_CONFIG_SOURCE_DHCP: - return RTPROT_DHCP; - case NM_IP_CONFIG_SOURCE_NDISC: - return RTPROT_RA; - - default: - return RTPROT_STATIC; - } -} - -NMIPConfigSource -nmp_utils_ip_config_source_coerce_from_rtprot(NMIPConfigSource source) -{ - /* When we receive a route from kernel and put it into the platform cache, - * we preserve the protocol field by converting it to a NMIPConfigSource - * via nmp_utils_ip_config_source_from_rtprot(). - * - * However, that is not the inverse of nmp_utils_ip_config_source_coerce_to_rtprot(). - * Instead, to go back to the original value, you need another step: - * nmp_utils_ip_config_source_coerce_from_rtprot (nmp_utils_ip_config_source_from_rtprot (rtprot)). - * - * This might partly restore the original source value, but of course that - * is not really possible because nmp_utils_ip_config_source_coerce_to_rtprot() - * is not injective. - * */ - switch (source) { - case NM_IP_CONFIG_SOURCE_RTPROT_UNSPEC: - return NM_IP_CONFIG_SOURCE_UNKNOWN; - - case NM_IP_CONFIG_SOURCE_RTPROT_KERNEL: - case NM_IP_CONFIG_SOURCE_RTPROT_REDIRECT: - return NM_IP_CONFIG_SOURCE_KERNEL; - - case NM_IP_CONFIG_SOURCE_RTPROT_RA: - return NM_IP_CONFIG_SOURCE_NDISC; - - case NM_IP_CONFIG_SOURCE_RTPROT_DHCP: - return NM_IP_CONFIG_SOURCE_DHCP; - - default: - return NM_IP_CONFIG_SOURCE_USER; - } -} - -const char * -nmp_utils_ip_config_source_to_string(NMIPConfigSource source, char *buf, gsize len) -{ - const char *s = NULL; - nm_utils_to_string_buffer_init(&buf, &len); - - if (!len) - return buf; - - switch (source) { - case NM_IP_CONFIG_SOURCE_UNKNOWN: - s = "unknown"; - break; - - case NM_IP_CONFIG_SOURCE_RTPROT_UNSPEC: - s = "rt-unspec"; - break; - case NM_IP_CONFIG_SOURCE_RTPROT_REDIRECT: - s = "rt-redirect"; - break; - case NM_IP_CONFIG_SOURCE_RTPROT_KERNEL: - s = "rt-kernel"; - break; - case NM_IP_CONFIG_SOURCE_RTPROT_BOOT: - s = "rt-boot"; - break; - case NM_IP_CONFIG_SOURCE_RTPROT_STATIC: - s = "rt-static"; - break; - case NM_IP_CONFIG_SOURCE_RTPROT_DHCP: - s = "rt-dhcp"; - break; - case NM_IP_CONFIG_SOURCE_RTPROT_RA: - s = "rt-ra"; - break; - - case NM_IP_CONFIG_SOURCE_KERNEL: - s = "kernel"; - break; - case NM_IP_CONFIG_SOURCE_SHARED: - s = "shared"; - break; - case NM_IP_CONFIG_SOURCE_IP4LL: - s = "ipv4ll"; - break; - case NM_IP_CONFIG_SOURCE_IP6LL: - s = "ipv6ll"; - break; - case NM_IP_CONFIG_SOURCE_PPP: - s = "ppp"; - break; - case NM_IP_CONFIG_SOURCE_WWAN: - s = "wwan"; - break; - case NM_IP_CONFIG_SOURCE_VPN: - s = "vpn"; - break; - case NM_IP_CONFIG_SOURCE_DHCP: - s = "dhcp"; - break; - case NM_IP_CONFIG_SOURCE_NDISC: - s = "ndisc"; - break; - case NM_IP_CONFIG_SOURCE_USER: - s = "user"; - break; - default: - break; - } - - if (source >= 1 && source <= 0x100) { - if (s) - g_snprintf(buf, len, "%s", s); - else - g_snprintf(buf, len, "rt-%d", ((int) source) - 1); - } else { - if (s) - g_strlcpy(buf, s, len); - else - g_snprintf(buf, len, "(%d)", source); - } - return buf; -} - -/** - * nmp_utils_sysctl_open_netdir: - * @ifindex: the ifindex for which to open "/sys/class/net/%s" - * @ifname_guess: (allow-none): optional argument, if present used as initial - * guess as the current name for @ifindex. If guessed right, - * it saves an additional if_indextoname() call. - * @out_ifname: (allow-none): if present, must be at least IFNAMSIZ - * characters. On success, this will contain the actual ifname - * found while opening the directory. - * - * Returns: a negative value on failure, on success returns the open fd - * to the "/sys/class/net/%s" directory for @ifindex. - */ -int -nmp_utils_sysctl_open_netdir(int ifindex, const char *ifname_guess, char *out_ifname) -{ -#define SYS_CLASS_NET "/sys/class/net/" - const char *ifname = ifname_guess; - char ifname_buf_last_try[IFNAMSIZ]; - char ifname_buf[IFNAMSIZ]; - guint try_count = 0; - char sysdir[NM_STRLEN(SYS_CLASS_NET) + IFNAMSIZ] = SYS_CLASS_NET; - char fd_buf[256]; - ssize_t nn; - - g_return_val_if_fail(ifindex >= 0, -1); - - ifname_buf_last_try[0] = '\0'; - - for (try_count = 0; try_count < 10; try_count++, ifname = NULL) { - nm_auto_close int fd_dir = -1; - nm_auto_close int fd_ifindex = -1; - - if (!ifname) { - ifname = nmp_utils_if_indextoname(ifindex, ifname_buf); - if (!ifname) - return -1; - } - - nm_assert(nm_utils_ifname_valid_kernel(ifname, NULL)); - - if (g_strlcpy(&sysdir[NM_STRLEN(SYS_CLASS_NET)], ifname, IFNAMSIZ) >= IFNAMSIZ) - g_return_val_if_reached(-1); - - /* we only retry, if the name changed since previous attempt. - * Hence, it is extremely unlikely that this loop runes until the - * end of the @try_count. */ - if (nm_streq(ifname, ifname_buf_last_try)) - return -1; - strcpy(ifname_buf_last_try, ifname); - - fd_dir = open(sysdir, O_DIRECTORY | O_CLOEXEC); - if (fd_dir < 0) - continue; - - fd_ifindex = openat(fd_dir, "ifindex", O_CLOEXEC); - if (fd_ifindex < 0) - continue; - - nn = nm_utils_fd_read_loop(fd_ifindex, fd_buf, sizeof(fd_buf) - 2, FALSE); - if (nn <= 0) - continue; - fd_buf[nn] = '\0'; - - if (ifindex != (int) _nm_utils_ascii_str_to_int64(fd_buf, 10, 1, G_MAXINT, -1)) - continue; - - if (out_ifname) - strcpy(out_ifname, ifname); - - return nm_steal_fd(&fd_dir); - } - - return -1; -} diff --git a/shared/nm-platform/nm-platform-utils.h b/shared/nm-platform/nm-platform-utils.h deleted file mode 100644 index d74723eb..00000000 --- a/shared/nm-platform/nm-platform-utils.h +++ /dev/null @@ -1,73 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-or-later */ -/* - * Copyright (C) 2015 Red Hat, Inc. - */ - -#ifndef __NM_PLATFORM_UTILS_H__ -#define __NM_PLATFORM_UTILS_H__ - -#include "nm-base/nm-base.h" -#include "nm-platform/nmp-base.h" - -/*****************************************************************************/ - -const char *nmp_utils_ethtool_get_driver(int ifindex); -gboolean nmp_utils_ethtool_supports_carrier_detect(int ifindex); -gboolean nmp_utils_ethtool_supports_vlans(int ifindex); -int nmp_utils_ethtool_get_peer_ifindex(int ifindex); -gboolean nmp_utils_ethtool_get_wake_on_lan(int ifindex); -gboolean nmp_utils_ethtool_set_wake_on_lan(int ifindex, - _NMSettingWiredWakeOnLan wol, - const char * wol_password); - -const char *nm_platform_link_duplex_type_to_string(NMPlatformLinkDuplexType duplex); - -gboolean nmp_utils_ethtool_get_link_settings(int ifindex, - gboolean * out_autoneg, - guint32 * out_speed, - NMPlatformLinkDuplexType *out_duplex); -gboolean nmp_utils_ethtool_set_link_settings(int ifindex, - gboolean autoneg, - guint32 speed, - NMPlatformLinkDuplexType duplex); - -gboolean nmp_utils_ethtool_get_permanent_address(int ifindex, guint8 *buf, size_t *length); - -gboolean nmp_utils_ethtool_get_driver_info(int ifindex, NMPUtilsEthtoolDriverInfo *data); - -NMEthtoolFeatureStates *nmp_utils_ethtool_get_features(int ifindex); - -gboolean nmp_utils_ethtool_set_features( - int ifindex, - const NMEthtoolFeatureStates *features, - const NMOptionBool *requested /* indexed by NMEthtoolID - _NM_ETHTOOL_ID_FEATURE_FIRST */, - gboolean do_set /* or reset */); - -gboolean nmp_utils_ethtool_get_coalesce(int ifindex, NMEthtoolCoalesceState *coalesce); - -gboolean nmp_utils_ethtool_set_coalesce(int ifindex, const NMEthtoolCoalesceState *coalesce); - -gboolean nmp_utils_ethtool_get_ring(int ifindex, NMEthtoolRingState *ring); - -gboolean nmp_utils_ethtool_set_ring(int ifindex, const NMEthtoolRingState *ring); - -/*****************************************************************************/ - -gboolean nmp_utils_mii_supports_carrier_detect(int ifindex); - -struct udev_device; - -const char *nmp_utils_udev_get_driver(struct udev_device *udevice); - -NMIPConfigSource nmp_utils_ip_config_source_from_rtprot(guint8 rtprot) _nm_const; -guint8 nmp_utils_ip_config_source_coerce_to_rtprot(NMIPConfigSource source) _nm_const; -NMIPConfigSource nmp_utils_ip_config_source_coerce_from_rtprot(NMIPConfigSource source) _nm_const; -NMIPConfigSource nmp_utils_ip_config_source_round_trip_rtprot(NMIPConfigSource source) _nm_const; -const char *nmp_utils_ip_config_source_to_string(NMIPConfigSource source, char *buf, gsize len); - -const char *nmp_utils_if_indextoname(int ifindex, char *out_ifname /*IFNAMSIZ*/); -int nmp_utils_if_nametoindex(const char *ifname); - -int nmp_utils_sysctl_open_netdir(int ifindex, const char *ifname_guess, char *out_ifname); - -#endif /* __NM_PLATFORM_UTILS_H__ */ diff --git a/shared/nm-platform/nmp-base.h b/shared/nm-platform/nmp-base.h deleted file mode 100644 index 210c26d6..00000000 --- a/shared/nm-platform/nmp-base.h +++ /dev/null @@ -1,94 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-or-later */ - -#ifndef __NMP_FWD_H__ -#define __NMP_FWD_H__ - -#include "nm-base/nm-base.h" - -/*****************************************************************************/ - -typedef enum { - NM_PLATFORM_LINK_DUPLEX_UNKNOWN, - NM_PLATFORM_LINK_DUPLEX_HALF, - NM_PLATFORM_LINK_DUPLEX_FULL, -} NMPlatformLinkDuplexType; - -/*****************************************************************************/ - -typedef struct { - /* We don't want to include <linux/ethtool.h> in header files, - * thus create a ABI compatible version of struct ethtool_drvinfo.*/ - guint32 _private_cmd; - char driver[32]; - char version[32]; - char fw_version[32]; - char _private_bus_info[32]; - char _private_erom_version[32]; - char _private_reserved2[12]; - guint32 _private_n_priv_flags; - guint32 _private_n_stats; - guint32 _private_testinfo_len; - guint32 _private_eedump_len; - guint32 _private_regdump_len; -} NMPUtilsEthtoolDriverInfo; - -typedef struct { - NMEthtoolID ethtool_id; - - guint8 n_kernel_names; - - /* one NMEthtoolID refers to one or more kernel_names. The reason for supporting this complexity - * (where one NMSettingEthtool option refers to multiple kernel features) is to follow what - * ethtool does, where "tx" is an alias for multiple features. */ - const char *const *kernel_names; -} NMEthtoolFeatureInfo; - -typedef struct { - const NMEthtoolFeatureInfo *info; - - guint idx_ss_features; - - /* one NMEthtoolFeatureInfo references one or more kernel_names. This is the index - * of the matching info->kernel_names */ - guint8 idx_kernel_name; - - bool available : 1; - bool requested : 1; - bool active : 1; - bool never_changed : 1; -} NMEthtoolFeatureState; - -typedef struct { - guint n_states; - - guint n_ss_features; - - /* indexed by NMEthtoolID - _NM_ETHTOOL_ID_FEATURE_FIRST */ - const NMEthtoolFeatureState *const *states_indexed[_NM_ETHTOOL_ID_FEATURE_NUM]; - - /* the same content, here as a list of n_states entries. */ - const NMEthtoolFeatureState states_list[]; -} NMEthtoolFeatureStates; - -/*****************************************************************************/ - -typedef struct { - guint32 - s[_NM_ETHTOOL_ID_COALESCE_NUM /* indexed by (NMEthtoolID - _NM_ETHTOOL_ID_COALESCE_FIRST) */ - ]; -} NMEthtoolCoalesceState; - -/*****************************************************************************/ - -typedef struct { - guint32 rx_pending; - guint32 rx_mini_pending; - guint32 rx_jumbo_pending; - guint32 tx_pending; -} NMEthtoolRingState; - -/*****************************************************************************/ - -typedef struct _NMPNetns NMPNetns; - -#endif /* __NMP_FWD_H__ */ diff --git a/shared/nm-platform/nmp-netns.c b/shared/nm-platform/nmp-netns.c deleted file mode 100644 index 4dd9e848..00000000 --- a/shared/nm-platform/nmp-netns.c +++ /dev/null @@ -1,759 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-or-later */ -/* - * Copyright (C) 2016 Red Hat, Inc. - */ - -#include "nm-glib-aux/nm-default-glib-i18n-lib.h" - -#include "nmp-netns.h" - -#include <fcntl.h> -#include <sys/mount.h> -#include <sys/stat.h> -#include <sys/types.h> - -#include "nm-log-core/nm-logging.h" - -/*****************************************************************************/ - -/* NOTE: NMPNetns and all code used here must be thread-safe! */ - -/* we may not call logging functions from the main-thread alone. Hence, we - * require locking from nm-logging. Indicate that by setting NM_THREAD_SAFE_ON_MAIN_THREAD - * to zero. */ -#undef NM_THREAD_SAFE_ON_MAIN_THREAD -#define NM_THREAD_SAFE_ON_MAIN_THREAD 0 - -/*****************************************************************************/ - -#define PROC_SELF_NS_MNT "/proc/self/ns/mnt" -#define PROC_SELF_NS_NET "/proc/self/ns/net" - -#define _CLONE_NS_ALL ((int) (CLONE_NEWNS | CLONE_NEWNET)) -#define _CLONE_NS_ALL_V CLONE_NEWNS, CLONE_NEWNET - -static NM_UTILS_FLAGS2STR_DEFINE(_clone_ns_to_str, - int, - NM_UTILS_FLAGS2STR(CLONE_NEWNS, "mnt"), - NM_UTILS_FLAGS2STR(CLONE_NEWNET, "net"), ); - -static const char * -__ns_types_to_str(int ns_types, int ns_types_already_set, char *buf, gsize len) -{ - const char *b = buf; - char bb[200]; - - nm_utils_strbuf_append_c(&buf, &len, '['); - if (ns_types & ~ns_types_already_set) { - nm_utils_strbuf_append_str( - &buf, - &len, - _clone_ns_to_str(ns_types & ~ns_types_already_set, bb, sizeof(bb))); - } - if (ns_types & ns_types_already_set) { - if (ns_types & ~ns_types_already_set) - nm_utils_strbuf_append_c(&buf, &len, '/'); - nm_utils_strbuf_append_str( - &buf, - &len, - _clone_ns_to_str(ns_types & ns_types_already_set, bb, sizeof(bb))); - } - nm_utils_strbuf_append_c(&buf, &len, ']'); - return b; -} -#define _ns_types_to_str(ns_types, ns_types_already_set, buf) \ - __ns_types_to_str(ns_types, ns_types_already_set, buf, sizeof(buf)) - -/*****************************************************************************/ - -#define _NMLOG_DOMAIN LOGD_PLATFORM -#define _NMLOG_PREFIX_NAME "netns" -#define _NMLOG(level, netns, ...) \ - G_STMT_START \ - { \ - NMLogLevel _level = (level); \ - \ - if (nm_logging_enabled(_level, _NMLOG_DOMAIN)) { \ - NMPNetns *_netns = (netns); \ - char _sbuf[20]; \ - \ - _nm_log(_level, \ - _NMLOG_DOMAIN, \ - 0, \ - NULL, \ - NULL, \ - "%s%s: " _NM_UTILS_MACRO_FIRST(__VA_ARGS__), \ - _NMLOG_PREFIX_NAME, \ - (_netns ? nm_sprintf_buf(_sbuf, "[%p]", _netns) \ - : "") _NM_UTILS_MACRO_REST(__VA_ARGS__)); \ - } \ - } \ - G_STMT_END - -/*****************************************************************************/ - -NM_GOBJECT_PROPERTIES_DEFINE_BASE(PROP_FD_NET, PROP_FD_MNT, ); - -typedef struct { - int fd_net; - int fd_mnt; -} NMPNetnsPrivate; - -struct _NMPNetns { - GObject parent; - NMPNetnsPrivate _priv; -}; - -struct _NMPNetnsClass { - GObjectClass parent; -}; - -G_DEFINE_TYPE(NMPNetns, nmp_netns, G_TYPE_OBJECT); - -#define NMP_NETNS_GET_PRIVATE(self) _NM_GET_PRIVATE(self, NMPNetns, NMP_IS_NETNS) - -/*****************************************************************************/ - -typedef struct { - NMPNetns *netns; - int count; - int ns_types; -} NetnsInfo; - -static void _stack_push(GArray *netns_stack, NMPNetns *netns, int ns_types); -static NMPNetns *_netns_new(GError **error); - -/*****************************************************************************/ - -static NMPNetns * -_netns_get(NetnsInfo *info) -{ - nm_assert(!info || NMP_IS_NETNS(info->netns)); - return info ? info->netns : NULL; -} - -/*****************************************************************************/ - -static _nm_thread_local GArray *_netns_stack = NULL; - -static void -_netns_stack_clear_cb(gpointer data) -{ - NetnsInfo *info = data; - - nm_assert(NMP_IS_NETNS(info->netns)); - g_object_unref(info->netns); -} - -static GArray * -_netns_stack_get_impl(void) -{ - gs_unref_object NMPNetns *netns = NULL; - gs_free_error GError *error = NULL; - GArray * s; - - s = g_array_new(FALSE, FALSE, sizeof(NetnsInfo)); - g_array_set_clear_func(s, _netns_stack_clear_cb); - _netns_stack = s; - - nm_utils_thread_local_register_destroy(s, (GDestroyNotify) g_array_unref); - - /* at the bottom of the stack we must try to create a netns instance - * that we never pop. It's the base to which we need to return. */ - netns = _netns_new(&error); - - if (!netns) { - _LOGD(NULL, "failed to create initial netns: %s", error->message); - return s; - } - - /* we leak this instance inside the stack. */ - _stack_push(s, netns, _CLONE_NS_ALL); - - return s; -} - -#define _netns_stack_get() \ - ({ \ - GArray *_s = _netns_stack; \ - \ - if (G_UNLIKELY(!_s)) \ - _s = _netns_stack_get_impl(); \ - _s; \ - }) - -/*****************************************************************************/ - -static NMPNetns * -_stack_current_netns(GArray *netns_stack, int ns_types) -{ - guint j; - - nm_assert(netns_stack && netns_stack->len > 0); - - /* we search the stack top-down to find the netns that has - * all @ns_types set. */ - for (j = netns_stack->len; ns_types && j >= 1;) { - NetnsInfo *info; - - info = &g_array_index(netns_stack, NetnsInfo, --j); - - if (NM_FLAGS_ALL(info->ns_types, ns_types)) - return info->netns; - } - - g_return_val_if_reached(NULL); -} - -static int -_stack_current_ns_types(GArray *netns_stack, NMPNetns *netns, int ns_types) -{ - const int ns_types_check[] = {_CLONE_NS_ALL_V}; - guint i, j; - int res = 0; - - nm_assert(netns); - nm_assert(netns_stack && netns_stack->len > 0); - - /* we search the stack top-down to check which of @ns_types - * are already set to @netns. */ - for (j = netns_stack->len; ns_types && j >= 1;) { - NetnsInfo *info; - - info = &g_array_index(netns_stack, NetnsInfo, --j); - if (info->netns != netns) { - ns_types = NM_FLAGS_UNSET(ns_types, info->ns_types); - continue; - } - - for (i = 0; i < G_N_ELEMENTS(ns_types_check); i++) { - if (NM_FLAGS_ANY(ns_types, ns_types_check[i]) - && NM_FLAGS_ANY(info->ns_types, ns_types_check[i])) { - res = NM_FLAGS_SET(res, ns_types_check[i]); - ns_types = NM_FLAGS_UNSET(ns_types, ns_types_check[i]); - } - } - } - - return res; -} - -static NetnsInfo * -_stack_peek(GArray *netns_stack) -{ - if (netns_stack->len > 0) - return &g_array_index(netns_stack, NetnsInfo, (netns_stack->len - 1)); - return NULL; -} - -static NetnsInfo * -_stack_bottom(GArray *netns_stack) -{ - if (netns_stack->len > 0) - return &g_array_index(netns_stack, NetnsInfo, 0); - return NULL; -} - -static void -_stack_push(GArray *netns_stack, NMPNetns *netns, int ns_types) -{ - NetnsInfo *info; - - nm_assert(netns_stack); - nm_assert(NMP_IS_NETNS(netns)); - nm_assert(NM_FLAGS_ANY(ns_types, _CLONE_NS_ALL)); - nm_assert(!NM_FLAGS_ANY(ns_types, ~_CLONE_NS_ALL)); - - g_array_set_size(netns_stack, netns_stack->len + 1); - - info = &g_array_index(netns_stack, NetnsInfo, (netns_stack->len - 1)); - *info = (NetnsInfo){ - .netns = g_object_ref(netns), - .ns_types = ns_types, - .count = 1, - }; -} - -static void -_stack_pop(GArray *netns_stack) -{ - NetnsInfo *info; - - nm_assert(netns_stack); - nm_assert(netns_stack->len > 1); - - info = &g_array_index(netns_stack, NetnsInfo, (netns_stack->len - 1)); - - nm_assert(NMP_IS_NETNS(info->netns)); - nm_assert(info->count == 1); - - g_array_set_size(netns_stack, netns_stack->len - 1); -} - -static guint -_stack_size(GArray *netns_stack) -{ - nm_assert(netns_stack); - - return netns_stack->len; -} - -/*****************************************************************************/ - -static NMPNetns * -_netns_new(GError **error) -{ - NMPNetns *self; - int fd_net, fd_mnt; - int errsv; - - fd_net = open(PROC_SELF_NS_NET, O_RDONLY | O_CLOEXEC); - if (fd_net == -1) { - errsv = errno; - g_set_error(error, - NM_UTILS_ERROR, - NM_UTILS_ERROR_UNKNOWN, - "Failed opening netns: %s", - nm_strerror_native(errsv)); - errno = errsv; - return NULL; - } - - fd_mnt = open(PROC_SELF_NS_MNT, O_RDONLY | O_CLOEXEC); - if (fd_mnt == -1) { - errsv = errno; - g_set_error(error, - NM_UTILS_ERROR, - NM_UTILS_ERROR_UNKNOWN, - "Failed opening mntns: %s", - nm_strerror_native(errsv)); - nm_close(fd_net); - errno = errsv; - return NULL; - } - - self = g_object_new(NMP_TYPE_NETNS, NMP_NETNS_FD_NET, fd_net, NMP_NETNS_FD_MNT, fd_mnt, NULL); - - _LOGD(self, "new netns (net:%d, mnt:%d)", fd_net, fd_mnt); - - return self; -} - -static int -_setns(NMPNetns *self, int type) -{ - char buf[100]; - int fd; - NMPNetnsPrivate *priv = NMP_NETNS_GET_PRIVATE(self); - - nm_assert(NM_IN_SET(type, _CLONE_NS_ALL_V)); - - fd = (type == CLONE_NEWNET) ? priv->fd_net : priv->fd_mnt; - - _LOGt(self, "set netns(%s, %d)", _ns_types_to_str(type, 0, buf), fd); - - return setns(fd, type); -} - -static gboolean -_netns_switch_push(GArray *netns_stack, NMPNetns *self, int ns_types) -{ - int errsv; - - if (NM_FLAGS_HAS(ns_types, CLONE_NEWNET) - && !_stack_current_ns_types(netns_stack, self, CLONE_NEWNET) - && _setns(self, CLONE_NEWNET) != 0) { - errsv = errno; - _LOGE(self, "failed to switch netns: %s", nm_strerror_native(errsv)); - return FALSE; - } - if (NM_FLAGS_HAS(ns_types, CLONE_NEWNS) - && !_stack_current_ns_types(netns_stack, self, CLONE_NEWNS) - && _setns(self, CLONE_NEWNS) != 0) { - errsv = errno; - _LOGE(self, "failed to switch mntns: %s", nm_strerror_native(errsv)); - - /* try to fix the mess by returning to the previous netns. */ - if (NM_FLAGS_HAS(ns_types, CLONE_NEWNET) - && !_stack_current_ns_types(netns_stack, self, CLONE_NEWNET)) { - self = _stack_current_netns(netns_stack, CLONE_NEWNET); - if (self && _setns(self, CLONE_NEWNET) != 0) { - errsv = errno; - _LOGE(self, "failed to restore netns: %s", nm_strerror_native(errsv)); - } - } - return FALSE; - } - - return TRUE; -} - -static gboolean -_netns_switch_pop(GArray *netns_stack, NMPNetns *self, int ns_types) -{ - int errsv; - NMPNetns *current; - int success = TRUE; - - if (NM_FLAGS_HAS(ns_types, CLONE_NEWNET) - && (!self || !_stack_current_ns_types(netns_stack, self, CLONE_NEWNET))) { - current = _stack_current_netns(netns_stack, CLONE_NEWNET); - if (!current) { - g_warn_if_reached(); - success = FALSE; - } else if (_setns(current, CLONE_NEWNET) != 0) { - errsv = errno; - _LOGE(self, "failed to switch netns: %s", nm_strerror_native(errsv)); - success = FALSE; - } - } - if (NM_FLAGS_HAS(ns_types, CLONE_NEWNS) - && (!self || !_stack_current_ns_types(netns_stack, self, CLONE_NEWNS))) { - current = _stack_current_netns(netns_stack, CLONE_NEWNS); - if (!current) { - g_warn_if_reached(); - success = FALSE; - } else if (_setns(current, CLONE_NEWNS) != 0) { - errsv = errno; - _LOGE(self, "failed to switch mntns: %s", nm_strerror_native(errsv)); - success = FALSE; - } - } - - return success; -} - -/*****************************************************************************/ - -int -nmp_netns_get_fd_net(NMPNetns *self) -{ - g_return_val_if_fail(NMP_IS_NETNS(self), 0); - - return NMP_NETNS_GET_PRIVATE(self)->fd_net; -} - -int -nmp_netns_get_fd_mnt(NMPNetns *self) -{ - g_return_val_if_fail(NMP_IS_NETNS(self), 0); - - return NMP_NETNS_GET_PRIVATE(self)->fd_mnt; -} - -/*****************************************************************************/ - -static gboolean -_nmp_netns_push_type(NMPNetns *self, int ns_types) -{ - GArray * netns_stack = _netns_stack_get(); - NetnsInfo *info; - char sbuf[100]; - - info = _stack_peek(netns_stack); - g_return_val_if_fail(info, FALSE); - - if (info->netns == self && info->ns_types == ns_types) { - info->count++; - _LOGt(self, - "push#%u* %s (increase count to %d)", - _stack_size(netns_stack) - 1, - _ns_types_to_str(ns_types, ns_types, sbuf), - info->count); - return TRUE; - } - - _LOGD(self, - "push#%u %s", - _stack_size(netns_stack), - _ns_types_to_str(ns_types, _stack_current_ns_types(netns_stack, self, ns_types), sbuf)); - - if (!_netns_switch_push(netns_stack, self, ns_types)) - return FALSE; - - _stack_push(netns_stack, self, ns_types); - return TRUE; -} - -gboolean -nmp_netns_push(NMPNetns *self) -{ - g_return_val_if_fail(NMP_IS_NETNS(self), FALSE); - - return _nmp_netns_push_type(self, _CLONE_NS_ALL); -} - -gboolean -nmp_netns_push_type(NMPNetns *self, int ns_types) -{ - g_return_val_if_fail(NMP_IS_NETNS(self), FALSE); - g_return_val_if_fail(!NM_FLAGS_ANY(ns_types, ~_CLONE_NS_ALL), FALSE); - - return _nmp_netns_push_type(self, ns_types == 0 ? _CLONE_NS_ALL : ns_types); -} - -NMPNetns * -nmp_netns_new(void) -{ - GArray * netns_stack = _netns_stack_get(); - NMPNetns * self; - int errsv; - GError * error = NULL; - unsigned long mountflags = 0; - - if (!_stack_peek(netns_stack)) { - /* there are no netns instances. We cannot create a new one - * (because after unshare we couldn't return to the original one). */ - errno = ENOTSUP; - return NULL; - } - - if (unshare(_CLONE_NS_ALL) != 0) { - errsv = errno; - _LOGE(NULL, "failed to create new net and mnt namespace: %s", nm_strerror_native(errsv)); - return NULL; - } - - if (mount("", "/", "none", MS_SLAVE | MS_REC, NULL) != 0) { - errsv = errno; - _LOGE(NULL, "failed mount --make-rslave: %s", nm_strerror_native(errsv)); - goto err_out; - } - - if (umount2("/sys", MNT_DETACH) != 0) { - errsv = errno; - _LOGE(NULL, "failed umount /sys: %s", nm_strerror_native(errsv)); - goto err_out; - } - - if (access("/sys", W_OK) == -1) - mountflags = MS_RDONLY; - - if (mount("sysfs", "/sys", "sysfs", mountflags, NULL) != 0) { - errsv = errno; - _LOGE(NULL, "failed mount /sys: %s", nm_strerror_native(errsv)); - goto err_out; - } - - self = _netns_new(&error); - if (!self) { - errsv = errno; - _LOGE(NULL, "failed to create netns after unshare: %s", error->message); - g_clear_error(&error); - goto err_out; - } - - _stack_push(netns_stack, self, _CLONE_NS_ALL); - - return self; -err_out: - _netns_switch_pop(netns_stack, NULL, _CLONE_NS_ALL); - errno = errsv; - return NULL; -} - -gboolean -nmp_netns_pop(NMPNetns *self) -{ - GArray * netns_stack = _netns_stack_get(); - NetnsInfo *info; - int ns_types; - - g_return_val_if_fail(NMP_IS_NETNS(self), FALSE); - - info = _stack_peek(netns_stack); - - g_return_val_if_fail(info, FALSE); - g_return_val_if_fail(info->netns == self, FALSE); - - if (info->count > 1) { - info->count--; - _LOGt(self, "pop#%u* (decrease count to %d)", _stack_size(netns_stack) - 1, info->count); - return TRUE; - } - g_return_val_if_fail(info->count == 1, FALSE); - - /* cannot pop the original netns. */ - g_return_val_if_fail(_stack_size(netns_stack) > 1, FALSE); - - _LOGD(self, "pop#%u", _stack_size(netns_stack) - 1); - - ns_types = info->ns_types; - - _stack_pop(netns_stack); - - return _netns_switch_pop(netns_stack, self, ns_types); -} - -NMPNetns * -nmp_netns_get_current(void) -{ - return _netns_get(_stack_peek(_netns_stack_get())); -} - -NMPNetns * -nmp_netns_get_initial(void) -{ - return _netns_get(_stack_bottom(_netns_stack_get())); -} - -gboolean -nmp_netns_is_initial(void) -{ - GArray *netns_stack = _netns_stack_get(); - - return (_netns_get(_stack_peek(netns_stack)) == _netns_get(_stack_bottom(netns_stack))); -} - -/*****************************************************************************/ - -gboolean -nmp_netns_bind_to_path(NMPNetns *self, const char *filename, int *out_fd) -{ - gs_free char * dirname = NULL; - int errsv; - int fd; - nm_auto_pop_netns NMPNetns *netns_pop = NULL; - - g_return_val_if_fail(NMP_IS_NETNS(self), FALSE); - g_return_val_if_fail(filename && filename[0] == '/', FALSE); - - if (!nmp_netns_push_type(self, CLONE_NEWNET)) - return FALSE; - netns_pop = self; - - dirname = g_path_get_dirname(filename); - if (mkdir(dirname, 0) != 0) { - errsv = errno; - if (errsv != EEXIST) { - _LOGE(self, - "bind: failed to create directory %s: %s", - dirname, - nm_strerror_native(errsv)); - return FALSE; - } - } - - if ((fd = creat(filename, S_IRUSR | S_IRGRP | S_IROTH)) == -1) { - errsv = errno; - _LOGE(self, "bind: failed to create %s: %s", filename, nm_strerror_native(errsv)); - return FALSE; - } - nm_close(fd); - - if (mount(PROC_SELF_NS_NET, filename, "none", MS_BIND, NULL) != 0) { - errsv = errno; - _LOGE(self, - "bind: failed to mount %s to %s: %s", - PROC_SELF_NS_NET, - filename, - nm_strerror_native(errsv)); - unlink(filename); - return FALSE; - } - - if (out_fd) { - if ((fd = open(filename, O_RDONLY | O_CLOEXEC)) == -1) { - errsv = errno; - _LOGE(self, "bind: failed to open %s: %s", filename, nm_strerror_native(errsv)); - umount2(filename, MNT_DETACH); - unlink(filename); - return FALSE; - } - *out_fd = fd; - } - - return TRUE; -} - -gboolean -nmp_netns_bind_to_path_destroy(NMPNetns *self, const char *filename) -{ - int errsv; - - g_return_val_if_fail(NMP_IS_NETNS(self), FALSE); - g_return_val_if_fail(filename && filename[0] == '/', FALSE); - - if (umount2(filename, MNT_DETACH) != 0) { - errsv = errno; - _LOGE(self, "bind: failed to unmount2 %s: %s", filename, nm_strerror_native(errsv)); - return FALSE; - } - if (unlink(filename) != 0) { - errsv = errno; - _LOGE(self, "bind: failed to unlink %s: %s", filename, nm_strerror_native(errsv)); - return FALSE; - } - return TRUE; -} - -/*****************************************************************************/ - -static void -set_property(GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec) -{ - NMPNetns * self = NMP_NETNS(object); - NMPNetnsPrivate *priv = NMP_NETNS_GET_PRIVATE(self); - - switch (prop_id) { - case PROP_FD_NET: - /* construct-only */ - priv->fd_net = g_value_get_int(value); - g_return_if_fail(priv->fd_net > 0); - break; - case PROP_FD_MNT: - /* construct-only */ - priv->fd_mnt = g_value_get_int(value); - g_return_if_fail(priv->fd_mnt > 0); - break; - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec); - break; - } -} - -static void -nmp_netns_init(NMPNetns *self) -{} - -static void -dispose(GObject *object) -{ - NMPNetns * self = NMP_NETNS(object); - NMPNetnsPrivate *priv = NMP_NETNS_GET_PRIVATE(self); - - nm_close(priv->fd_net); - priv->fd_net = -1; - - nm_close(priv->fd_mnt); - priv->fd_mnt = -1; - - G_OBJECT_CLASS(nmp_netns_parent_class)->dispose(object); -} - -static void -nmp_netns_class_init(NMPNetnsClass *klass) -{ - GObjectClass *object_class = G_OBJECT_CLASS(klass); - - object_class->set_property = set_property; - object_class->dispose = dispose; - - obj_properties[PROP_FD_NET] = - g_param_spec_int(NMP_NETNS_FD_NET, - "", - "", - 0, - G_MAXINT, - 0, - G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS); - obj_properties[PROP_FD_MNT] = - g_param_spec_int(NMP_NETNS_FD_MNT, - "", - "", - 0, - G_MAXINT, - 0, - G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS); - g_object_class_install_properties(object_class, _PROPERTY_ENUMS_LAST, obj_properties); -} diff --git a/shared/nm-platform/nmp-netns.h b/shared/nm-platform/nmp-netns.h deleted file mode 100644 index b18bd03e..00000000 --- a/shared/nm-platform/nmp-netns.h +++ /dev/null @@ -1,56 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-or-later */ -/* - * Copyright (C) 2016 Red Hat, Inc. - */ - -#ifndef __NMP_NETNS_UTILS_H__ -#define __NMP_NETNS_UTILS_H__ - -#include "nmp-base.h" - -/*****************************************************************************/ - -#define NMP_TYPE_NETNS (nmp_netns_get_type()) -#define NMP_NETNS(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), NMP_TYPE_NETNS, NMPNetns)) -#define NMP_NETNS_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), NMP_TYPE_NETNS, NMPNetnsClass)) -#define NMP_IS_NETNS(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), NMP_TYPE_NETNS)) -#define NMP_IS_NETNS_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), NMP_TYPE_NETNS)) -#define NMP_NETNS_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), NMP_TYPE_NETNS, NMPNetnsClass)) - -#define NMP_NETNS_FD_NET "fd-net" -#define NMP_NETNS_FD_MNT "fd-mnt" - -typedef struct _NMPNetns NMPNetns; -typedef struct _NMPNetnsClass NMPNetnsClass; - -GType nmp_netns_get_type(void); - -NMPNetns *nmp_netns_new(void); - -gboolean nmp_netns_push(NMPNetns *self); -gboolean nmp_netns_push_type(NMPNetns *self, int ns_types); -gboolean nmp_netns_pop(NMPNetns *self); - -NMPNetns *nmp_netns_get_current(void); -NMPNetns *nmp_netns_get_initial(void); -gboolean nmp_netns_is_initial(void); - -int nmp_netns_get_fd_net(NMPNetns *self); -int nmp_netns_get_fd_mnt(NMPNetns *self); - -static inline void -_nm_auto_pop_netns(NMPNetns **p) -{ - if (*p) { - int errsv = errno; - - nmp_netns_pop(*p); - errno = errsv; - } -} -#define nm_auto_pop_netns nm_auto(_nm_auto_pop_netns) - -gboolean nmp_netns_bind_to_path(NMPNetns *self, const char *filename, int *out_fd); -gboolean nmp_netns_bind_to_path_destroy(NMPNetns *self, const char *filename); - -#endif /* __NMP_NETNS_UTILS_H__ */ diff --git a/shared/nm-platform/tests/meson.build b/shared/nm-platform/tests/meson.build deleted file mode 100644 index a8fcdbca..00000000 --- a/shared/nm-platform/tests/meson.build +++ /dev/null @@ -1,20 +0,0 @@ -# SPDX-License-Identifier: LGPL-2.1-or-later - -exe = executable( - 'test-nm-platform', - 'test-nm-platform.c', - c_args: [ - '-DG_LOG_DOMAIN="test"', - ], - dependencies: [ - libnm_log_core_dep, - libnm_platform_dep, - ], -) - -test( - 'shared/nm-glib-aux/test-nm-platform', - test_script, - args: test_args + [exe.full_path()], - timeout: default_test_timeout, -) diff --git a/shared/nm-platform/tests/test-nm-platform.c b/shared/nm-platform/tests/test-nm-platform.c deleted file mode 100644 index a3e9ff13..00000000 --- a/shared/nm-platform/tests/test-nm-platform.c +++ /dev/null @@ -1,116 +0,0 @@ -/* SPDX-License-Identifier: LGPL-2.1-or-later */ - -#include "nm-glib-aux/nm-default-glib-i18n-prog.h" - -#include "nm-log-core/nm-logging.h" -#include "nm-platform/nm-netlink.h" -#include "nm-platform/nmp-netns.h" - -#include "nm-utils/nm-test-utils.h" - -/*****************************************************************************/ - -void -_nm_logging_clear_platform_logging_cache(void) -{ - /* this symbols is required by nm-log-core library. */ -} - -/*****************************************************************************/ - -static void -test_use_symbols(void) -{ - static void (*const SYMBOLS[])(void) = { - (void (*)(void)) nl_nlmsghdr_to_str, - (void (*)(void)) nlmsg_hdr, - (void (*)(void)) nlmsg_reserve, - (void (*)(void)) nla_reserve, - (void (*)(void)) nlmsg_alloc_size, - (void (*)(void)) nlmsg_alloc, - (void (*)(void)) nlmsg_alloc_convert, - (void (*)(void)) nlmsg_alloc_simple, - (void (*)(void)) nlmsg_free, - (void (*)(void)) nlmsg_append, - (void (*)(void)) nlmsg_parse, - (void (*)(void)) nlmsg_put, - (void (*)(void)) nla_strlcpy, - (void (*)(void)) nla_memcpy, - (void (*)(void)) nla_put, - (void (*)(void)) nla_find, - (void (*)(void)) nla_nest_cancel, - (void (*)(void)) nla_nest_start, - (void (*)(void)) nla_nest_end, - (void (*)(void)) nla_parse, - (void (*)(void)) nlmsg_get_proto, - (void (*)(void)) nlmsg_set_proto, - (void (*)(void)) nlmsg_set_src, - (void (*)(void)) nlmsg_get_creds, - (void (*)(void)) nlmsg_set_creds, - (void (*)(void)) genlmsg_put, - (void (*)(void)) genlmsg_data, - (void (*)(void)) genlmsg_user_hdr, - (void (*)(void)) genlmsg_hdr, - (void (*)(void)) genlmsg_user_data, - (void (*)(void)) genlmsg_attrdata, - (void (*)(void)) genlmsg_len, - (void (*)(void)) genlmsg_attrlen, - (void (*)(void)) genlmsg_valid_hdr, - (void (*)(void)) genlmsg_parse, - (void (*)(void)) genl_ctrl_resolve, - (void (*)(void)) nl_socket_alloc, - (void (*)(void)) nl_socket_free, - (void (*)(void)) nl_socket_get_fd, - (void (*)(void)) nl_socket_get_local_port, - (void (*)(void)) nl_socket_get_msg_buf_size, - (void (*)(void)) nl_socket_set_passcred, - (void (*)(void)) nl_socket_set_msg_buf_size, - (void (*)(void)) nlmsg_get_dst, - (void (*)(void)) nl_socket_set_nonblocking, - (void (*)(void)) nl_socket_set_buffer_size, - (void (*)(void)) nl_socket_add_memberships, - (void (*)(void)) nl_socket_set_ext_ack, - (void (*)(void)) nl_socket_disable_msg_peek, - (void (*)(void)) nl_connect, - (void (*)(void)) nl_wait_for_ack, - (void (*)(void)) nl_recvmsgs, - (void (*)(void)) nl_sendmsg, - (void (*)(void)) nl_send_iovec, - (void (*)(void)) nl_complete_msg, - (void (*)(void)) nl_send, - (void (*)(void)) nl_send_auto, - (void (*)(void)) nl_recv, - - (void (*)(void)) nmp_netns_bind_to_path, - (void (*)(void)) nmp_netns_bind_to_path_destroy, - (void (*)(void)) nmp_netns_get_current, - (void (*)(void)) nmp_netns_get_fd_mnt, - (void (*)(void)) nmp_netns_get_fd_net, - (void (*)(void)) nmp_netns_get_initial, - (void (*)(void)) nmp_netns_is_initial, - (void (*)(void)) nmp_netns_new, - (void (*)(void)) nmp_netns_pop, - (void (*)(void)) nmp_netns_push, - (void (*)(void)) nmp_netns_push_type, - - NULL, - }; - - /* The only (not very exciting) purpose of this test is to see that - * we can use various symbols and don't get a linker error. */ - assert(G_N_ELEMENTS(SYMBOLS) == NM_PTRARRAY_LEN(SYMBOLS) + 1); -} - -/*****************************************************************************/ - -NMTST_DEFINE(); - -int -main(int argc, char **argv) -{ - nmtst_init(&argc, &argv, TRUE); - - g_test_add_func("/nm-platform/test_use_symbols", test_use_symbols); - - return g_test_run(); -} diff --git a/shared/nm-std-aux/c-list-util.c b/shared/nm-std-aux/c-list-util.c deleted file mode 100644 index d16bd6b7..00000000 --- a/shared/nm-std-aux/c-list-util.c +++ /dev/null @@ -1,184 +0,0 @@ -/* SPDX-License-Identifier: LGPL-2.1-or-later */ -/* - * Copyright (C) 2017 Red Hat, Inc. - */ - -#include "c-list-util.h" - -/*****************************************************************************/ - -/** - * c_list_relink: - * @lst: the head list entry - * - * Takes an invalid list, that has undefined prev pointers. - * Only the next pointers are valid, and the tail's next - * pointer points to %NULL instead of the head. - * - * c_list_relink() fixes the list by updating all prev pointers - * and close the circular linking by pointing the tails' next - * pointer to @lst. - * - * The use of this function is to do a bulk update, that lets the - * list degenerate by not updating the prev pointers. At the end, - * the list can be fixed by c_list_relink(). - */ -void -c_list_relink(CList *lst) -{ - CList *ls, *ls_prev; - - ls_prev = lst; - ls = lst->next; - do { - ls->prev = ls_prev; - ls_prev = ls; - ls = ls->next; - } while (ls); - ls_prev->next = lst; - lst->prev = ls_prev; -} - -/*****************************************************************************/ - -static CList * -_c_list_srt_split(CList *ls) -{ - CList *ls2; - - ls2 = ls; - ls = ls->next; - if (!ls) - return NULL; - do { - ls = ls->next; - if (!ls) - break; - ls = ls->next; - ls2 = ls2->next; - } while (ls); - ls = ls2->next; - ls2->next = NULL; - return ls; -} - -static CList * -_c_list_srt_merge(CList *ls1, CList *ls2, CListSortCmp cmp, const void *user_data) -{ - CList *ls; - CList head; - - ls = &head; - for (;;) { - /* while invoking the @cmp function, the list - * elements are not properly linked. Don't try to access - * their next/prev pointers. */ - if (cmp(ls1, ls2, user_data) <= 0) { - ls->next = ls1; - ls = ls1; - ls1 = ls1->next; - if (!ls1) - break; - } else { - ls->next = ls2; - ls = ls2; - ls2 = ls2->next; - if (!ls2) - break; - } - } - ls->next = ls1 ?: ls2; - - return head.next; -} - -typedef struct { - CList *ls1; - CList *ls2; - char ls1_sorted; -} SortStack; - -static CList * -_c_list_sort(CList *ls, CListSortCmp cmp, const void *user_data) -{ - /* reserve a huge stack-size. We need roughly log2(n) entries, hence this - * is much more we will ever need. We don't guard for stack-overflow either. */ - SortStack stack_arr[70]; - SortStack *stack_head = stack_arr; - - stack_arr[0].ls1 = ls; - - /* A simple top-down, non-recursive, stable merge-sort. - * - * Maybe natural merge-sort would be better, to do better for - * partially sorted lists. Doing that would be much more complicated, - * so it's not done. */ -_split: - stack_head[0].ls2 = _c_list_srt_split(stack_head[0].ls1); - if (stack_head[0].ls2) { - stack_head[0].ls1_sorted = 0; - stack_head[1].ls1 = stack_head[0].ls1; - stack_head++; - goto _split; - } - -_backtrack: - if (stack_head == stack_arr) - return stack_arr[0].ls1; - - stack_head--; - if (!stack_head[0].ls1_sorted) { - stack_head[0].ls1 = stack_head[1].ls1; - stack_head[0].ls1_sorted = 1; - stack_head[1].ls1 = stack_head[0].ls2; - stack_head++; - goto _split; - } - - stack_head[0].ls1 = _c_list_srt_merge(stack_head[0].ls1, stack_head[1].ls1, cmp, user_data); - goto _backtrack; -} - -/** - * c_list_sort_headless: - * @lst: the list. - * @cmp: compare function for sorting. While comparing two - * CList elements, their next/prev pointers are in undefined - * state. - * @user_data: user data for @cmp. - * - * Sorts the list @lst according to @cmp. Contrary to - * c_list_sort(), @lst is not the list head but a - * valid entry as well. This function returns the new - * list head. - */ -CList * -c_list_sort_headless(CList *lst, CListSortCmp cmp, const void *user_data) -{ - if (!c_list_is_empty(lst)) { - lst->prev->next = NULL; - lst = _c_list_sort(lst, cmp, user_data); - c_list_relink(lst); - } - return lst; -} - -/** - * c_list_sort: - * @head: the list head. - * @cmp: compare function for sorting. While comparing two - * CList elements, their next/prev pointers are in undefined - * state. - * @user_data: user data for @cmp. - * - * Sorts the list @head according to @cmp. - */ -void -c_list_sort(CList *head, CListSortCmp cmp, const void *user_data) -{ - if (!c_list_is_empty(head) && head->next->next != head) { - head->prev->next = NULL; - head->next = _c_list_sort(head->next, cmp, user_data); - c_list_relink(head); - } -} diff --git a/shared/nm-std-aux/c-list-util.h b/shared/nm-std-aux/c-list-util.h deleted file mode 100644 index 9d86d663..00000000 --- a/shared/nm-std-aux/c-list-util.h +++ /dev/null @@ -1,56 +0,0 @@ -/* SPDX-License-Identifier: LGPL-2.1-or-later */ -/* - * Copyright (C) 2017 Red Hat, Inc. - */ - -#ifndef __C_LIST_UTIL_H__ -#define __C_LIST_UTIL_H__ - -#include "c-list/src/c-list.h" - -/*****************************************************************************/ - -void c_list_relink(CList *lst); - -typedef int (*CListSortCmp)(const CList *a, const CList *b, const void *user_data); - -CList *c_list_sort_headless(CList *lst, CListSortCmp cmp, const void *user_data); - -void c_list_sort(CList *head, CListSortCmp cmp, const void *user_data); - -/* c_list_length_is: - * @list: the #CList list head - * @check_len: the length to compare - * - * Returns: basically the same as (c_list_length (@list) == @check_len), - * but does not require to iterate the entire list first. There is only - * one real use: to find out whether there is exactly one element in the - * list, by passing @check_len as 1. - */ -static inline int -c_list_length_is(const CList *list, unsigned long check_len) -{ - unsigned long n = 0; - const CList * iter; - - c_list_for_each (iter, list) { - ++n; - if (n > check_len) - return 0; - } - - return n == check_len; -} - -#define c_list_for_each_prev(_iter, _list) \ - for (_iter = (_list)->prev; (_iter) != (_list); _iter = (_iter)->prev) - -#define c_list_for_each_prev_safe(_iter, _safe, _list) \ - for (_iter = (_list)->prev, _safe = (_iter)->prev; (_iter) != (_list); \ - _iter = (_safe), _safe = (_safe)->prev) - -#define c_list_for_each_entry_prev(_iter, _list, _m) \ - for (_iter = c_list_entry((_list)->prev, __typeof__(*_iter), _m); &(_iter)->_m != (_list); \ - _iter = c_list_entry((_iter)->_m.prev, __typeof__(*_iter), _m)) - -#endif /* __C_LIST_UTIL_H__ */ diff --git a/shared/nm-std-aux/nm-dbus-compat.h b/shared/nm-std-aux/nm-dbus-compat.h deleted file mode 100644 index c1076200..00000000 --- a/shared/nm-std-aux/nm-dbus-compat.h +++ /dev/null @@ -1,25 +0,0 @@ -/* SPDX-License-Identifier: LGPL-2.1-or-later */ - -#ifndef __NM_DBUS_COMPAT_H__ -#define __NM_DBUS_COMPAT_H__ - -#define DBUS_SERVICE_DBUS "org.freedesktop.DBus" - -#define DBUS_PATH_DBUS "/org/freedesktop/DBus" - -#define DBUS_INTERFACE_DBUS "org.freedesktop.DBus" -#define DBUS_INTERFACE_INTROSPECTABLE "org.freedesktop.DBus.Introspectable" -#define DBUS_INTERFACE_OBJECT_MANAGER "org.freedesktop.DBus.ObjectManager" -#define DBUS_INTERFACE_PEER "org.freedesktop.DBus.Peer" -#define DBUS_INTERFACE_PROPERTIES "org.freedesktop.DBus.Properties" - -#define DBUS_NAME_FLAG_ALLOW_REPLACEMENT 0x1 -#define DBUS_NAME_FLAG_REPLACE_EXISTING 0x2 -#define DBUS_NAME_FLAG_DO_NOT_QUEUE 0x4 - -#define DBUS_REQUEST_NAME_REPLY_PRIMARY_OWNER 1 -#define DBUS_REQUEST_NAME_REPLY_IN_QUEUE 2 -#define DBUS_REQUEST_NAME_REPLY_EXISTS 3 -#define DBUS_REQUEST_NAME_REPLY_ALREADY_OWNER 4 - -#endif /* __NM_DBUS_COMPAT_H__ */ diff --git a/shared/nm-std-aux/nm-default-std.h b/shared/nm-std-aux/nm-default-std.h deleted file mode 100644 index fe16d35f..00000000 --- a/shared/nm-std-aux/nm-default-std.h +++ /dev/null @@ -1,102 +0,0 @@ -/* SPDX-License-Identifier: LGPL-2.1-or-later */ -/* - * Copyright (C) 2015 Red Hat, Inc. - */ - -#ifndef __NM_DEFAULT_STD_H__ -#define __NM_DEFAULT_STD_H__ - -#include "nm-networkmanager-compilation.h" - -#ifdef NETWORKMANAGER_COMPILATION - #error Dont define NETWORKMANAGER_COMPILATION -#endif - -#ifndef G_LOG_DOMAIN - #error Define G_LOG_DOMAIN -#endif - -/*****************************************************************************/ - -#define NETWORKMANAGER_COMPILATION 0 - -/*****************************************************************************/ - -/* always include these headers for our internal source files. */ - -#ifndef ___CONFIG_H__ - #define ___CONFIG_H__ - #include <config.h> -#endif - -#include "config-extra.h" - -/* for internal compilation we don't want the deprecation macros - * to be in effect. Define the widest range of versions to effectively - * disable deprecation checks */ -#define NM_VERSION_MIN_REQUIRED NM_VERSION_0_9_8 - -#ifndef NM_MORE_ASSERTS - #define NM_MORE_ASSERTS 0 -#endif - -#if NM_MORE_ASSERTS == 0 - /* The cast macros like NM_TYPE() are implemented via G_TYPE_CHECK_INSTANCE_CAST() - * and _G_TYPE_CIC(). The latter, by default performs runtime checks of the type - * by calling g_type_check_instance_cast(). - * This check has a certain overhead without being helpful. - * - * Example 1: - * static void foo (NMType *obj) - * { - * access_obj_without_check (obj); - * } - * foo ((NMType *) obj); - * // There is no runtime check and passing an invalid pointer - * // leads to a crash. - * - * Example 2: - * static void foo (NMType *obj) - * { - * access_obj_without_check (obj); - * } - * foo (NM_TYPE (obj)); - * // There is a runtime check which prints a g_warning(), but that doesn't - * // avoid the crash as NM_TYPE() cannot do anything then passing on the - * // invalid pointer. - * - * Example 3: - * static void foo (NMType *obj) - * { - * g_return_if_fail (NM_IS_TYPE (obj)); - * access_obj_without_check (obj); - * } - * foo ((NMType *) obj); - * // There is a runtime check which prints a g_critical() which also avoids - * // the crash. That is actually helpful to catch bugs and avoid crashes. - * - * Example 4: - * static void foo (NMType *obj) - * { - * g_return_if_fail (NM_IS_TYPE (obj)); - * access_obj_without_check (obj); - * } - * foo (NM_TYPE (obj)); - * // The runtime check is performed twice, with printing a g_warning() and - * // a g_critical() and avoiding the crash. - * - * Example 3 is how it should be done. Type checks in NM_TYPE() are pointless. - * Disable them for our production builds. - */ - #ifndef G_DISABLE_CAST_CHECKS - #define G_DISABLE_CAST_CHECKS - #endif -#endif - -/*****************************************************************************/ - -#include <stdlib.h> - -/*****************************************************************************/ - -#endif /* __NM_DEFAULT_STD_H__ */ diff --git a/shared/nm-std-aux/nm-networkmanager-compilation.h b/shared/nm-std-aux/nm-networkmanager-compilation.h deleted file mode 100644 index 025a158d..00000000 --- a/shared/nm-std-aux/nm-networkmanager-compilation.h +++ /dev/null @@ -1,54 +0,0 @@ -/* SPDX-License-Identifier: LGPL-2.1-or-later */ -/* - * Copyright (C) 2015 Red Hat, Inc. - */ - -#ifndef __NM_NETWORKMANAGER_COMPILATION_H__ -#define __NM_NETWORKMANAGER_COMPILATION_H__ - -#define NM_NETWORKMANAGER_COMPILATION_WITH_GLIB (1 << 0) -#define NM_NETWORKMANAGER_COMPILATION_WITH_GLIB_I18N_LIB (1 << 1) -#define NM_NETWORKMANAGER_COMPILATION_WITH_GLIB_I18N_PROG (1 << 2) -#define NM_NETWORKMANAGER_COMPILATION_WITH_LIBNM (1 << 3) -#define NM_NETWORKMANAGER_COMPILATION_WITH_LIBNM_PRIVATE (1 << 4) -#define NM_NETWORKMANAGER_COMPILATION_WITH_LIBNM_CORE (1 << 5) -#define NM_NETWORKMANAGER_COMPILATION_WITH_LIBNM_CORE_INTERNAL (1 << 6) -#define NM_NETWORKMANAGER_COMPILATION_WITH_LIBNM_CORE_PRIVATE (1 << 7) -#define NM_NETWORKMANAGER_COMPILATION_WITH_DAEMON (1 << 10) -#define NM_NETWORKMANAGER_COMPILATION_WITH_SYSTEMD (1 << 11) - -#define NM_NETWORKMANAGER_COMPILATION_LIBNM_CORE \ - (0 | NM_NETWORKMANAGER_COMPILATION_WITH_GLIB \ - | NM_NETWORKMANAGER_COMPILATION_WITH_GLIB_I18N_LIB \ - | NM_NETWORKMANAGER_COMPILATION_WITH_LIBNM_CORE \ - | NM_NETWORKMANAGER_COMPILATION_WITH_LIBNM_CORE_PRIVATE \ - | NM_NETWORKMANAGER_COMPILATION_WITH_LIBNM_CORE_INTERNAL) - -#define NM_NETWORKMANAGER_COMPILATION_LIBNM \ - (0 | NM_NETWORKMANAGER_COMPILATION_WITH_GLIB \ - | NM_NETWORKMANAGER_COMPILATION_WITH_GLIB_I18N_LIB | NM_NETWORKMANAGER_COMPILATION_WITH_LIBNM \ - | NM_NETWORKMANAGER_COMPILATION_WITH_LIBNM_PRIVATE \ - | NM_NETWORKMANAGER_COMPILATION_WITH_LIBNM_CORE \ - | NM_NETWORKMANAGER_COMPILATION_WITH_LIBNM_CORE_INTERNAL) - -#define NM_NETWORKMANAGER_COMPILATION_CLIENT \ - (0 | NM_NETWORKMANAGER_COMPILATION_WITH_GLIB \ - | NM_NETWORKMANAGER_COMPILATION_WITH_GLIB_I18N_PROG \ - | NM_NETWORKMANAGER_COMPILATION_WITH_LIBNM | NM_NETWORKMANAGER_COMPILATION_WITH_LIBNM_CORE) - -#define NM_NETWORKMANAGER_COMPILATION_DAEMON \ - (0 | NM_NETWORKMANAGER_COMPILATION_WITH_GLIB \ - | NM_NETWORKMANAGER_COMPILATION_WITH_GLIB_I18N_PROG \ - | NM_NETWORKMANAGER_COMPILATION_WITH_LIBNM_CORE \ - | NM_NETWORKMANAGER_COMPILATION_WITH_LIBNM_CORE_INTERNAL \ - | NM_NETWORKMANAGER_COMPILATION_WITH_DAEMON) - -#define NM_NETWORKMANAGER_COMPILATION_SYSTEMD_SHARED \ - (0 | NM_NETWORKMANAGER_COMPILATION_WITH_GLIB | NM_NETWORKMANAGER_COMPILATION_WITH_SYSTEMD) - -#define NM_NETWORKMANAGER_COMPILATION_SYSTEMD \ - (0 | NM_NETWORKMANAGER_COMPILATION_DAEMON | NM_NETWORKMANAGER_COMPILATION_SYSTEMD_SHARED) - -#define NM_NETWORKMANAGER_COMPILATION_GLIB (0 | NM_NETWORKMANAGER_COMPILATION_WITH_GLIB) - -#endif /* __NM_NETWORKMANAGER_COMPILATION_H__ */ diff --git a/shared/nm-std-aux/nm-std-aux.h b/shared/nm-std-aux/nm-std-aux.h deleted file mode 100644 index a619cebd..00000000 --- a/shared/nm-std-aux/nm-std-aux.h +++ /dev/null @@ -1,985 +0,0 @@ -/* SPDX-License-Identifier: LGPL-2.1-or-later */ - -#ifndef __NM_STD_AUX_H__ -#define __NM_STD_AUX_H__ - -#include <assert.h> -#include <string.h> -#include <stdbool.h> -#include <stdint.h> -#include <unistd.h> -#include <stdio.h> -#include <errno.h> - -/*****************************************************************************/ - -#define _nm_packed __attribute__((__packed__)) -#define _nm_unused __attribute__((__unused__)) -#define _nm_used __attribute__((__used__)) -#define _nm_pure __attribute__((__pure__)) -#define _nm_const __attribute__((__const__)) -#define _nm_printf(a, b) __attribute__((__format__(__printf__, a, b))) -#define _nm_align(s) __attribute__((__aligned__(s))) -#define _nm_section(s) __attribute__((__section__(s))) -#define _nm_alignof(type) __alignof(type) -#define _nm_alignas(type) _nm_align(_nm_alignof(type)) -#define nm_auto(fcn) __attribute__((__cleanup__(fcn))) - -/* This is required to make LTO working. - * - * See https://gitlab.freedesktop.org/NetworkManager/NetworkManager/merge_requests/76#note_112694 - * https://gcc.gnu.org/bugzilla/show_bug.cgi?id=48200#c28 - */ -#ifndef __clang__ - #define _nm_externally_visible __attribute__((__externally_visible__)) -#else - #define _nm_externally_visible -#endif - -#if __GNUC__ >= 7 - #define _nm_fallthrough __attribute__((__fallthrough__)) -#else - #define _nm_fallthrough -#endif - -/*****************************************************************************/ - -#ifdef __CHECKER__ - #define _nm_bitwise __attribute__((__bitwise__)) - #define _nm_force __attribute__((__force__)) -#else - #define _nm_bitwise - #define _nm_force -#endif - -typedef uint16_t _nm_bitwise nm_le16_t; -typedef uint16_t _nm_bitwise nm_be16_t; -typedef uint32_t _nm_bitwise nm_le32_t; -typedef uint32_t _nm_bitwise nm_be32_t; -typedef uint64_t _nm_bitwise nm_le64_t; -typedef uint64_t _nm_bitwise nm_be64_t; - -/*****************************************************************************/ - -#ifdef thread_local - #define _nm_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 _nm_thread_local _Thread_local -#else - #define _nm_thread_local __thread -#endif - -/*****************************************************************************/ - -#define _NM_DUMMY_STRUCT_FOR_TRAILING_SEMICOLON struct _nm_dummy_struct_for_trailing_semicolon - -/*****************************************************************************/ - -#define NM_PASTE_ARGS(identifier1, identifier2) identifier1##identifier2 -#define NM_PASTE(identifier1, identifier2) NM_PASTE_ARGS(identifier1, identifier2) - -/* Taken from systemd's UNIQ_T and UNIQ macros. */ - -#define NM_UNIQ_T(x, uniq) NM_PASTE(__unique_prefix_, NM_PASTE(x, uniq)) -#define NM_UNIQ __COUNTER__ - -/*****************************************************************************/ - -#define _NM_BOOLEAN_EXPR_IMPL(v, expr) \ - ({ \ - int NM_UNIQ_T(V, v); \ - \ - if (expr) \ - NM_UNIQ_T(V, v) = 1; \ - else \ - NM_UNIQ_T(V, v) = 0; \ - NM_UNIQ_T(V, v); \ - }) -#define NM_BOOLEAN_EXPR(expr) _NM_BOOLEAN_EXPR_IMPL(NM_UNIQ, expr) - -#if defined(__GNUC__) && (__GNUC__ > 2) && defined(__OPTIMIZE__) - #define NM_LIKELY(expr) (__builtin_expect(NM_BOOLEAN_EXPR(expr), 1)) - #define NM_UNLIKELY(expr) (__builtin_expect(NM_BOOLEAN_EXPR(expr), 0)) -#else - #define NM_LIKELY(expr) NM_BOOLEAN_EXPR(expr) - #define NM_UNLIKELY(expr) NM_BOOLEAN_EXPR(expr) -#endif - -/*****************************************************************************/ - -/* glib/C provides the following kind of assertions: - * - assert() -- disable with NDEBUG - * - g_return_if_fail() -- disable with G_DISABLE_CHECKS - * - g_assert() -- disable with G_DISABLE_ASSERT - * but they are all enabled by default and usually even production builds have - * these kind of assertions enabled. It also means, that disabling assertions - * is an untested configuration, and might have bugs. - * - * Add our own assertion macro nm_assert(), which is disabled by default and must - * be explicitly enabled. They are useful for more expensive checks or checks that - * depend less on runtime conditions (that is, are generally expected to be true). */ - -#ifndef NM_MORE_ASSERTS - #define NM_MORE_ASSERTS 0 -#endif - -#ifndef _nm_assert_call - #define _nm_assert_call(cond) assert(cond) - #define _nm_assert_call_not_reached() assert(0) -#endif - -#if NM_MORE_ASSERTS - #define nm_assert(cond) \ - do { \ - _nm_assert_call(cond); \ - } while (0) - #define nm_assert_se(cond) \ - do { \ - if (NM_LIKELY(cond)) { \ - ; \ - } else { \ - _nm_assert_call(0 && (cond)); \ - } \ - } while (0) - #define nm_assert_not_reached() \ - do { \ - _nm_assert_call_not_reached(); \ - } while (0) -#else - #define nm_assert(cond) \ - do { \ - if (0) { \ - if (cond) {} \ - } \ - } while (0) - #define nm_assert_se(cond) \ - do { \ - if (NM_LIKELY(cond)) { \ - ; \ - } \ - } while (0) - #define nm_assert_not_reached() \ - do { \ - ; \ - } while (0) -#endif - -#define nm_assert_unreachable_val(val) \ - ({ \ - nm_assert_not_reached(); \ - (val); \ - }) - -#define NM_STATIC_ASSERT(cond) static_assert(cond, "") -#define NM_STATIC_ASSERT_EXPR(cond) \ - ({ \ - NM_STATIC_ASSERT(cond); \ - 1; \ - }) - -/*****************************************************************************/ - -#define NM_N_ELEMENTS(arr) (sizeof(arr) / sizeof((arr)[0])) - -/*****************************************************************************/ - -static inline uint32_t -nm_add_clamped_u32(uint32_t a, uint32_t b) -{ - uint32_t c; - - /* returns a+b, or UINT32_MAX if the result would overflow. */ - - c = a + b; - if (c < a) - return UINT32_MAX; - return c; -} - -static inline unsigned -nm_mult_clamped_u(unsigned a, unsigned b) -{ - unsigned c; - - /* returns a*b, or UINT_MAX if the result would overflow. */ - - if (b == 0) - return 0; - - c = a * b; - - if (c / b != a) - return (unsigned) -1; - - return c; -} - -/* 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 NM_MIN(a, b) __NM_MIN(NM_UNIQ, a, NM_UNIQ, b) -#define __NM_MIN(aq, a, bq, b) \ - ({ \ - typeof(a) NM_UNIQ_T(A, aq) = (a); \ - typeof(b) NM_UNIQ_T(B, bq) = (b); \ - ((NM_UNIQ_T(A, aq) < NM_UNIQ_T(B, bq)) ? NM_UNIQ_T(A, aq) : NM_UNIQ_T(B, bq)); \ - }) - -#define NM_MAX(a, b) __NM_MAX(NM_UNIQ, a, NM_UNIQ, b) -#define __NM_MAX(aq, a, bq, b) \ - ({ \ - typeof(a) NM_UNIQ_T(A, aq) = (a); \ - typeof(b) NM_UNIQ_T(B, bq) = (b); \ - ((NM_UNIQ_T(A, aq) > NM_UNIQ_T(B, bq)) ? NM_UNIQ_T(A, aq) : NM_UNIQ_T(B, bq)); \ - }) - -#define NM_CLAMP(x, low, high) __NM_CLAMP(NM_UNIQ, x, NM_UNIQ, low, NM_UNIQ, high) -#define __NM_CLAMP(xq, x, lowq, low, highq, high) \ - ({ \ - typeof(x) NM_UNIQ_T(X, xq) = (x); \ - typeof(low) NM_UNIQ_T(LOW, lowq) = (low); \ - typeof(high) NM_UNIQ_T(HIGH, highq) = (high); \ - \ - ((NM_UNIQ_T(X, xq) > NM_UNIQ_T(HIGH, highq)) ? NM_UNIQ_T(HIGH, highq) \ - : (NM_UNIQ_T(X, xq) < NM_UNIQ_T(LOW, lowq)) ? NM_UNIQ_T(LOW, lowq) \ - : NM_UNIQ_T(X, xq)); \ - }) - -#define NM_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 NM_CONST_MAX(_A, _B) \ - (__builtin_choose_expr((__builtin_constant_p(_A) && __builtin_constant_p(_B) \ - && __builtin_types_compatible_p(typeof(_A), typeof(_B))), \ - ((_A) > (_B)) ? (_A) : (_B), \ - ((void) 0))) - -/* Determine whether @x is a power of two (@x being an integer type). - * Basically, this returns TRUE, if @x has exactly one bit set. - * For negative values and zero, this always returns FALSE. */ -#define nm_utils_is_power_of_two(x) \ - ({ \ - typeof(x) _x2 = (x); \ - const typeof(_x2) _X_0 = ((typeof(_x2)) 0); \ - const typeof(_x2) _X_1 = ((typeof(_x2)) 1); \ - \ - ((_x2 > _X_0) && ((_x2 & (_x2 - _X_1)) == _X_0)); \ - }) - -#define nm_utils_is_power_of_two_or_zero(x) \ - ({ \ - typeof(x) _x1 = (x); \ - \ - ((_x1 == 0) || nm_utils_is_power_of_two(_x1)); \ - }) - -/*****************************************************************************/ - -#define NM_SWAP(p_a, p_b) \ - do { \ - typeof(*(p_a)) *const _p_a = (p_a); \ - typeof(*(p_a)) *const _p_b = (p_b); \ - typeof(*(p_a)) _tmp; \ - \ - _tmp = *_p_a; \ - *_p_a = *_p_b; \ - *_p_b = _tmp; \ - } while (0) - -/*****************************************************************************/ - -/* macro to return strlen() of a compile time string. */ -#define NM_STRLEN(str) (sizeof("" str "") - 1u) - -/* returns the length of a NULL terminated array of pointers, - * like g_strv_length() does. The difference is: - * - it operates on arrays of pointers (of any kind, requiring no cast). - * - it accepts NULL to return zero. */ -#define NM_PTRARRAY_LEN(array) \ - ({ \ - typeof(*(array)) *const _array = (array); \ - size_t _n = 0; \ - \ - if (_array) { \ - _nm_unused const void *_type_check_is_pointer = _array[0]; \ - \ - while (_array[_n]) \ - _n++; \ - } \ - _n; \ - }) - -/*****************************************************************************/ - -static inline int -nm_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 int -nm_streq(const char *s1, const char *s2) -{ - return strcmp(s1, s2) == 0; -} - -static inline int -nm_streq0(const char *s1, const char *s2) -{ - return (s1 == s2) || (s1 && s2 && strcmp(s1, s2) == 0); -} - -#define NM_STR_HAS_PREFIX(str, prefix) \ - ({ \ - const char *const _str_has_prefix = (str); \ - \ - nm_assert(strlen(prefix) == NM_STRLEN(prefix)); \ - \ - _str_has_prefix && (strncmp(_str_has_prefix, "" prefix "", NM_STRLEN(prefix)) == 0); \ - }) - -#define NM_STR_HAS_SUFFIX(str, suffix) \ - ({ \ - const char *const _str_has_suffix = (str); \ - size_t _l; \ - \ - nm_assert(strlen(suffix) == NM_STRLEN(suffix)); \ - \ - (_str_has_suffix && ((_l = strlen(_str_has_suffix)) >= NM_STRLEN(suffix)) \ - && (memcmp(&_str_has_suffix[_l - NM_STRLEN(suffix)], "" suffix "", NM_STRLEN(suffix)) \ - == 0)); \ - }) - -/* whether @str starts with the string literal @prefix and is followed by - * some other text. It is like NM_STR_HAS_PREFIX() && !nm_streq() together. */ -#define NM_STR_HAS_PREFIX_WITH_MORE(str, prefix) \ - ({ \ - const char *const _str_has_prefix_with_more = (str); \ - \ - NM_STR_HAS_PREFIX(_str_has_prefix_with_more, "" prefix "") \ - &&_str_has_prefix_with_more[NM_STRLEN(prefix)] != '\0'; \ - }) - -#define NM_STR_HAS_SUFFIX_WITH_MORE(str, suffix) \ - ({ \ - const char *const _str_has_suffix = (str); \ - size_t _l; \ - \ - nm_assert(strlen(suffix) == NM_STRLEN(suffix)); \ - \ - (_str_has_suffix && ((_l = strlen(_str_has_suffix)) > NM_STRLEN(suffix)) \ - && (memcmp(&_str_has_suffix[_l - NM_STRLEN(suffix)], "" suffix "", NM_STRLEN(suffix)) \ - == 0)); \ - }) - -/*****************************************************************************/ - -#define _NM_IN_SET_EVAL_1(op, _x, y) (_x == (y)) -#define _NM_IN_SET_EVAL_2(op, _x, y, ...) (_x == (y)) op _NM_IN_SET_EVAL_1(op, _x, __VA_ARGS__) -#define _NM_IN_SET_EVAL_3(op, _x, y, ...) (_x == (y)) op _NM_IN_SET_EVAL_2(op, _x, __VA_ARGS__) -#define _NM_IN_SET_EVAL_4(op, _x, y, ...) (_x == (y)) op _NM_IN_SET_EVAL_3(op, _x, __VA_ARGS__) -#define _NM_IN_SET_EVAL_5(op, _x, y, ...) (_x == (y)) op _NM_IN_SET_EVAL_4(op, _x, __VA_ARGS__) -#define _NM_IN_SET_EVAL_6(op, _x, y, ...) (_x == (y)) op _NM_IN_SET_EVAL_5(op, _x, __VA_ARGS__) -#define _NM_IN_SET_EVAL_7(op, _x, y, ...) (_x == (y)) op _NM_IN_SET_EVAL_6(op, _x, __VA_ARGS__) -#define _NM_IN_SET_EVAL_8(op, _x, y, ...) (_x == (y)) op _NM_IN_SET_EVAL_7(op, _x, __VA_ARGS__) -#define _NM_IN_SET_EVAL_9(op, _x, y, ...) (_x == (y)) op _NM_IN_SET_EVAL_8(op, _x, __VA_ARGS__) -#define _NM_IN_SET_EVAL_10(op, _x, y, ...) (_x == (y)) op _NM_IN_SET_EVAL_9(op, _x, __VA_ARGS__) -#define _NM_IN_SET_EVAL_11(op, _x, y, ...) (_x == (y)) op _NM_IN_SET_EVAL_10(op, _x, __VA_ARGS__) -#define _NM_IN_SET_EVAL_12(op, _x, y, ...) (_x == (y)) op _NM_IN_SET_EVAL_11(op, _x, __VA_ARGS__) -#define _NM_IN_SET_EVAL_13(op, _x, y, ...) (_x == (y)) op _NM_IN_SET_EVAL_12(op, _x, __VA_ARGS__) -#define _NM_IN_SET_EVAL_14(op, _x, y, ...) (_x == (y)) op _NM_IN_SET_EVAL_13(op, _x, __VA_ARGS__) -#define _NM_IN_SET_EVAL_15(op, _x, y, ...) (_x == (y)) op _NM_IN_SET_EVAL_14(op, _x, __VA_ARGS__) -#define _NM_IN_SET_EVAL_16(op, _x, y, ...) (_x == (y)) op _NM_IN_SET_EVAL_15(op, _x, __VA_ARGS__) -#define _NM_IN_SET_EVAL_17(op, _x, y, ...) (_x == (y)) op _NM_IN_SET_EVAL_16(op, _x, __VA_ARGS__) -#define _NM_IN_SET_EVAL_18(op, _x, y, ...) (_x == (y)) op _NM_IN_SET_EVAL_17(op, _x, __VA_ARGS__) -#define _NM_IN_SET_EVAL_19(op, _x, y, ...) (_x == (y)) op _NM_IN_SET_EVAL_18(op, _x, __VA_ARGS__) -#define _NM_IN_SET_EVAL_20(op, _x, y, ...) (_x == (y)) op _NM_IN_SET_EVAL_19(op, _x, __VA_ARGS__) -#define _NM_IN_SET_EVAL_21(op, _x, y, ...) (_x == (y)) op _NM_IN_SET_EVAL_20(op, _x, __VA_ARGS__) -#define _NM_IN_SET_EVAL_22(op, _x, y, ...) (_x == (y)) op _NM_IN_SET_EVAL_21(op, _x, __VA_ARGS__) -#define _NM_IN_SET_EVAL_23(op, _x, y, ...) (_x == (y)) op _NM_IN_SET_EVAL_22(op, _x, __VA_ARGS__) -#define _NM_IN_SET_EVAL_24(op, _x, y, ...) (_x == (y)) op _NM_IN_SET_EVAL_23(op, _x, __VA_ARGS__) -#define _NM_IN_SET_EVAL_25(op, _x, y, ...) (_x == (y)) op _NM_IN_SET_EVAL_24(op, _x, __VA_ARGS__) -#define _NM_IN_SET_EVAL_26(op, _x, y, ...) (_x == (y)) op _NM_IN_SET_EVAL_25(op, _x, __VA_ARGS__) -#define _NM_IN_SET_EVAL_27(op, _x, y, ...) (_x == (y)) op _NM_IN_SET_EVAL_26(op, _x, __VA_ARGS__) -#define _NM_IN_SET_EVAL_28(op, _x, y, ...) (_x == (y)) op _NM_IN_SET_EVAL_27(op, _x, __VA_ARGS__) -#define _NM_IN_SET_EVAL_29(op, _x, y, ...) (_x == (y)) op _NM_IN_SET_EVAL_28(op, _x, __VA_ARGS__) -#define _NM_IN_SET_EVAL_30(op, _x, y, ...) (_x == (y)) op _NM_IN_SET_EVAL_29(op, _x, __VA_ARGS__) - -#define _NM_IN_SET_EVAL_N2(op, _x, n, ...) (_NM_IN_SET_EVAL_##n(op, _x, __VA_ARGS__)) -#define _NM_IN_SET_EVAL_N(op, type, x, n, ...) \ - ({ \ - type _x = (x); \ - \ - /* trigger a -Wenum-compare warning */ \ - nm_assert(true || _x == (x)); \ - \ - !!_NM_IN_SET_EVAL_N2(op, _x, n, __VA_ARGS__); \ - }) - -#define _NM_IN_SET(op, type, x, ...) \ - _NM_IN_SET_EVAL_N(op, type, x, NM_NARG(__VA_ARGS__), __VA_ARGS__) - -/* Beware that this does short-circuit evaluation (use "||" instead of "|") - * which has a possibly unexpected non-function-like behavior. - * Use NM_IN_SET_SE if you need all arguments to be evaluated. */ -#define NM_IN_SET(x, ...) _NM_IN_SET(||, typeof(x), x, __VA_ARGS__) - -/* "SE" stands for "side-effect". Contrary to NM_IN_SET(), this does not do - * short-circuit evaluation, which can make a difference if the arguments have - * side-effects. */ -#define NM_IN_SET_SE(x, ...) _NM_IN_SET(|, typeof(x), x, __VA_ARGS__) - -/* the *_TYPED forms allow to explicitly select the type of "x". This is useful - * if "x" doesn't support typeof (bitfields) or you want to gracefully convert - * a type using automatic type conversion rules (but not forcing the conversion - * with a cast). */ -#define NM_IN_SET_TYPED(type, x, ...) _NM_IN_SET(||, type, x, __VA_ARGS__) -#define NM_IN_SET_SE_TYPED(type, x, ...) _NM_IN_SET(|, type, x, __VA_ARGS__) - -/*****************************************************************************/ - -#define _NM_IN_SETOP_EVAL_1(op, op_eq, _x, y) (op_eq(_x, y)) -#define _NM_IN_SETOP_EVAL_2(op, op_eq, _x, y, ...) \ - (op_eq(_x, y)) op _NM_IN_SETOP_EVAL_1(op, op_eq, _x, __VA_ARGS__) -#define _NM_IN_SETOP_EVAL_3(op, op_eq, _x, y, ...) \ - (op_eq(_x, y)) op _NM_IN_SETOP_EVAL_2(op, op_eq, _x, __VA_ARGS__) -#define _NM_IN_SETOP_EVAL_4(op, op_eq, _x, y, ...) \ - (op_eq(_x, y)) op _NM_IN_SETOP_EVAL_3(op, op_eq, _x, __VA_ARGS__) -#define _NM_IN_SETOP_EVAL_5(op, op_eq, _x, y, ...) \ - (op_eq(_x, y)) op _NM_IN_SETOP_EVAL_4(op, op_eq, _x, __VA_ARGS__) -#define _NM_IN_SETOP_EVAL_6(op, op_eq, _x, y, ...) \ - (op_eq(_x, y)) op _NM_IN_SETOP_EVAL_5(op, op_eq, _x, __VA_ARGS__) -#define _NM_IN_SETOP_EVAL_7(op, op_eq, _x, y, ...) \ - (op_eq(_x, y)) op _NM_IN_SETOP_EVAL_6(op, op_eq, _x, __VA_ARGS__) -#define _NM_IN_SETOP_EVAL_8(op, op_eq, _x, y, ...) \ - (op_eq(_x, y)) op _NM_IN_SETOP_EVAL_7(op, op_eq, _x, __VA_ARGS__) -#define _NM_IN_SETOP_EVAL_9(op, op_eq, _x, y, ...) \ - (op_eq(_x, y)) op _NM_IN_SETOP_EVAL_8(op, op_eq, _x, __VA_ARGS__) -#define _NM_IN_SETOP_EVAL_10(op, op_eq, _x, y, ...) \ - (op_eq(_x, y)) op _NM_IN_SETOP_EVAL_9(op, op_eq, _x, __VA_ARGS__) -#define _NM_IN_SETOP_EVAL_11(op, op_eq, _x, y, ...) \ - (op_eq(_x, y)) op _NM_IN_SETOP_EVAL_10(op, op_eq, _x, __VA_ARGS__) -#define _NM_IN_SETOP_EVAL_12(op, op_eq, _x, y, ...) \ - (op_eq(_x, y)) op _NM_IN_SETOP_EVAL_11(op, op_eq, _x, __VA_ARGS__) -#define _NM_IN_SETOP_EVAL_13(op, op_eq, _x, y, ...) \ - (op_eq(_x, y)) op _NM_IN_SETOP_EVAL_12(op, op_eq, _x, __VA_ARGS__) -#define _NM_IN_SETOP_EVAL_14(op, op_eq, _x, y, ...) \ - (op_eq(_x, y)) op _NM_IN_SETOP_EVAL_13(op, op_eq, _x, __VA_ARGS__) -#define _NM_IN_SETOP_EVAL_15(op, op_eq, _x, y, ...) \ - (op_eq(_x, y)) op _NM_IN_SETOP_EVAL_14(op, op_eq, _x, __VA_ARGS__) -#define _NM_IN_SETOP_EVAL_16(op, op_eq, _x, y, ...) \ - (op_eq(_x, y)) op _NM_IN_SETOP_EVAL_15(op, op_eq, _x, __VA_ARGS__) -#define _NM_IN_SETOP_EVAL_17(op, op_eq, _x, y, ...) \ - (op_eq(_x, y)) op _NM_IN_SETOP_EVAL_16(op, op_eq, _x, __VA_ARGS__) -#define _NM_IN_SETOP_EVAL_18(op, op_eq, _x, y, ...) \ - (op_eq(_x, y)) op _NM_IN_SETOP_EVAL_17(op, op_eq, _x, __VA_ARGS__) -#define _NM_IN_SETOP_EVAL_19(op, op_eq, _x, y, ...) \ - (op_eq(_x, y)) op _NM_IN_SETOP_EVAL_18(op, op_eq, _x, __VA_ARGS__) -#define _NM_IN_SETOP_EVAL_20(op, op_eq, _x, y, ...) \ - (op_eq(_x, y)) op _NM_IN_SETOP_EVAL_19(op, op_eq, _x, __VA_ARGS__) -#define _NM_IN_SETOP_EVAL_21(op, op_eq, _x, y, ...) \ - (op_eq(_x, y)) op _NM_IN_SETOP_EVAL_20(op, op_eq, _x, __VA_ARGS__) -#define _NM_IN_SETOP_EVAL_22(op, op_eq, _x, y, ...) \ - (op_eq(_x, y)) op _NM_IN_SETOP_EVAL_21(op, op_eq, _x, __VA_ARGS__) -#define _NM_IN_SETOP_EVAL_23(op, op_eq, _x, y, ...) \ - (op_eq(_x, y)) op _NM_IN_SETOP_EVAL_22(op, op_eq, _x, __VA_ARGS__) -#define _NM_IN_SETOP_EVAL_24(op, op_eq, _x, y, ...) \ - (op_eq(_x, y)) op _NM_IN_SETOP_EVAL_23(op, op_eq, _x, __VA_ARGS__) -#define _NM_IN_SETOP_EVAL_25(op, op_eq, _x, y, ...) \ - (op_eq(_x, y)) op _NM_IN_SETOP_EVAL_24(op, op_eq, _x, __VA_ARGS__) -#define _NM_IN_SETOP_EVAL_26(op, op_eq, _x, y, ...) \ - (op_eq(_x, y)) op _NM_IN_SETOP_EVAL_25(op, op_eq, _x, __VA_ARGS__) -#define _NM_IN_SETOP_EVAL_27(op, op_eq, _x, y, ...) \ - (op_eq(_x, y)) op _NM_IN_SETOP_EVAL_26(op, op_eq, _x, __VA_ARGS__) -#define _NM_IN_SETOP_EVAL_28(op, op_eq, _x, y, ...) \ - (op_eq(_x, y)) op _NM_IN_SETOP_EVAL_27(op, op_eq, _x, __VA_ARGS__) -#define _NM_IN_SETOP_EVAL_29(op, op_eq, _x, y, ...) \ - (op_eq(_x, y)) op _NM_IN_SETOP_EVAL_28(op, op_eq, _x, __VA_ARGS__) -#define _NM_IN_SETOP_EVAL_30(op, op_eq, _x, y, ...) \ - (op_eq(_x, y)) op _NM_IN_SETOP_EVAL_29(op, op_eq, _x, __VA_ARGS__) - -/*****************************************************************************/ - -#define _NM_IN_STRSET_EVAL_N2(op, op_ed, _x, n, ...) \ - (_NM_IN_SETOP_EVAL_##n(op, op_ed, _x, __VA_ARGS__)) -#define _NM_IN_STRSET_EVAL_N(op, op_ed, x, n, ...) \ - ({ \ - const char *_x = (x); \ - (((_x == NULL) && _NM_IN_SET_EVAL_N2(op, ((const char *) NULL), n, __VA_ARGS__)) \ - || ((_x != NULL) && _NM_IN_STRSET_EVAL_N2(op, op_ed, _x, n, __VA_ARGS__))); \ - }) - -/*****************************************************************************/ - -static inline int -_NM_IN_STRSET_op_streq(const char *x, const char *s) -{ - return s && strcmp(x, s) == 0; -} - -/* Beware that this does short-circuit evaluation (use "||" instead of "|") - * which has a possibly unexpected non-function-like behavior. - * Use NM_IN_STRSET_SE if you need all arguments to be evaluated. */ -#define NM_IN_STRSET(x, ...) \ - _NM_IN_STRSET_EVAL_N(||, _NM_IN_STRSET_op_streq, x, NM_NARG(__VA_ARGS__), __VA_ARGS__) - -/* "SE" stands for "side-effect". Contrary to NM_IN_STRSET(), this does not do - * short-circuit evaluation, which can make a difference if the arguments have - * side-effects. */ -#define NM_IN_STRSET_SE(x, ...) \ - _NM_IN_STRSET_EVAL_N(|, _NM_IN_STRSET_op_streq, x, NM_NARG(__VA_ARGS__), __VA_ARGS__) - -/*****************************************************************************/ - -#define NM_STRCHAR_ALL(str, ch_iter, predicate) \ - ({ \ - int _val = true; \ - const char *_str = (str); \ - \ - if (_str) { \ - for (;;) { \ - const char ch_iter = _str[0]; \ - \ - if (ch_iter != '\0') { \ - if (predicate) { \ - _str++; \ - continue; \ - } \ - _val = false; \ - } \ - break; \ - } \ - } \ - _val; \ - }) - -#define NM_STRCHAR_ANY(str, ch_iter, predicate) \ - ({ \ - int _val = false; \ - const char *_str = (str); \ - \ - if (_str) { \ - for (;;) { \ - const char ch_iter = _str[0]; \ - \ - if (ch_iter != '\0') { \ - if (predicate) { \ - ; \ - } else { \ - _str++; \ - continue; \ - } \ - _val = true; \ - } \ - break; \ - } \ - } \ - _val; \ - }) - -/*****************************************************************************/ - -/** - * nm_close: - * - * Like close() but throws an assertion if the input fd is - * invalid. Closing an invalid fd is a programming error, so - * it's better to catch it early. - */ -static inline int -nm_close(int fd) -{ - int r; - - r = close(fd); - nm_assert(r != -1 || fd < 0 || errno != EBADF); - return r; -} - -/*****************************************************************************/ - -/* Note: @value is only evaluated when *out_val is present. - * Thus, - * NM_SET_OUT (out_str, g_strdup ("hallo")); - * does the right thing. - */ -#define NM_SET_OUT(out_val, value) \ - ({ \ - typeof(*(out_val)) *_out_val = (out_val); \ - \ - if (_out_val) { \ - *_out_val = (value); \ - } \ - \ - (!!_out_val); \ - }) - -/*****************************************************************************/ - -#define NM_AUTO_DEFINE_FCN_VOID(CastType, name, func) \ - static inline void name(void *v) \ - { \ - func(*((CastType *) v)); \ - } \ - _NM_DUMMY_STRUCT_FOR_TRAILING_SEMICOLON - -#define NM_AUTO_DEFINE_FCN_VOID0(CastType, name, func) \ - static inline void name(void *v) \ - { \ - if (*((CastType *) v)) \ - func(*((CastType *) v)); \ - } \ - _NM_DUMMY_STRUCT_FOR_TRAILING_SEMICOLON - -#define NM_AUTO_DEFINE_FCN(Type, name, func) \ - static inline void name(Type *v) \ - { \ - func(*v); \ - } \ - _NM_DUMMY_STRUCT_FOR_TRAILING_SEMICOLON - -#define NM_AUTO_DEFINE_FCN0(Type, name, func) \ - static inline void name(Type *v) \ - { \ - if (*v) \ - func(*v); \ - } \ - _NM_DUMMY_STRUCT_FOR_TRAILING_SEMICOLON - -/*****************************************************************************/ - -/** - * nm_auto_free: - * - * Call free() on a variable location when it goes out of scope. - * This is for pointers that are allocated with malloc() instead of - * g_malloc(). - * - * In practice, since glib 2.45, g_malloc()/g_free() always wraps malloc()/free(). - * See bgo#751592. In that case, it would be safe to free pointers allocated with - * malloc() with gs_free or g_free(). - * - * However, let's never mix them. To free malloc'ed memory, always use - * free() or nm_auto_free. - */ -NM_AUTO_DEFINE_FCN_VOID0(void *, _nm_auto_free_impl, free); -#define nm_auto_free nm_auto(_nm_auto_free_impl) - -static inline void -_nm_auto_protect_errno(const int *p_saved_errno) -{ - errno = *p_saved_errno; -} -#define NM_AUTO_PROTECT_ERRNO(errsv_saved) \ - nm_auto(_nm_auto_protect_errno) _nm_unused const int errsv_saved = (errno) - -/*****************************************************************************/ - -static inline void -_nm_auto_close(int *pfd) -{ - if (*pfd >= 0) { - int errsv = errno; - - (void) nm_close(*pfd); - errno = errsv; - } -} -#define nm_auto_close nm_auto(_nm_auto_close) - -static inline void -_nm_auto_fclose(FILE **pfd) -{ - if (*pfd) { - int errsv = errno; - - (void) fclose(*pfd); - errno = errsv; - } -} -#define nm_auto_fclose nm_auto(_nm_auto_fclose) - -/*****************************************************************************/ - -#define nm_clear_pointer(pp, destroy) \ - ({ \ - typeof(*(pp)) *_pp = (pp); \ - typeof(*_pp) _p; \ - int _changed = false; \ - \ - if (_pp && (_p = *_pp)) { \ - _nm_unused const void *_p_check_is_pointer = _p; \ - \ - *_pp = NULL; \ - \ - /* g_clear_pointer() assigns @destroy first to a local variable, so that - * you can call "g_clear_pointer (pp, (GDestroyNotify) destroy);" without - * gcc emitting a warning. We don't do that, hence, you cannot cast - * "destroy" first. - * - * On the upside: you are not supposed to cast fcn, because the pointer - * types are preserved. If you really need a cast, you should cast @pp. - * But that is hardly ever necessary. */ \ - (destroy)(_p); \ - \ - _changed = true; \ - } \ - _changed; \ - }) - -#define nm_clear_free(pp) nm_clear_pointer(pp, free) - -/*****************************************************************************/ - -static inline void * -_nm_steal_pointer(void *pp) -{ - void **ptr = (void **) pp; - void * ref; - - ref = *ptr; - *ptr = NULL; - return ref; -} - -#define nm_steal_pointer(pp) ((typeof(*(pp))) _nm_steal_pointer(pp)) - -/** - * nm_steal_int: - * @p_val: pointer to an int type. - * - * Returns: *p_val and sets *p_val to zero the same time. - * Accepts %NULL, in which case also numeric 0 will be returned. - */ -#define nm_steal_int(p_val) \ - ({ \ - typeof(p_val) const _p_val = (p_val); \ - typeof(*_p_val) _val = 0; \ - \ - if (_p_val && (_val = *_p_val)) { \ - *_p_val = 0; \ - } \ - _val; \ - }) - -static inline int -nm_steal_fd(int *p_fd) -{ - int fd; - - if (p_fd && ((fd = *p_fd) >= 0)) { - *p_fd = -1; - return fd; - } - return -1; -} - -/*****************************************************************************/ - -static inline uintptr_t -nm_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; -} - -/*****************************************************************************/ - -#define NM_CMP_RETURN(c) \ - do { \ - const int _cc = (c); \ - if (_cc) \ - return _cc < 0 ? -1 : 1; \ - } while (0) - -#define NM_CMP_RETURN_DIRECT(c) \ - do { \ - const int _cc = (c); \ - if (_cc) \ - return _cc; \ - } while (0) - -#define NM_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 NM_CMP_DIRECT(a, b) \ - do { \ - typeof(a) _a = (a); \ - typeof(b) _b = (b); \ - \ - if (_a != _b) \ - return (_a < _b) ? -1 : 1; \ - } while (0) - -#define NM_CMP_DIRECT_UNSAFE(a, b) \ - 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 NM_CMP_DIRECT_PTR(a, b) NM_CMP_DIRECT(nm_ptr_to_uintptr(a), nm_ptr_to_uintptr(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) \ - do { \ - const struct in6_addr *const _a = (a); \ - const struct in6_addr *const _b = (b); \ - NM_CMP_RETURN(memcmp(_a, _b, sizeof(struct in6_addr))); \ - } while (0) - -#define NM_CMP_FIELD(a, b, field) NM_CMP_DIRECT(((a)->field), ((b)->field)) - -#define NM_CMP_FIELD_UNSAFE(a, b, field) \ - do { \ - /* 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; \ - } while (0) - -#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) \ - do { \ - const char *_a = ((a)->field); \ - const char *_b = ((b)->field); \ - \ - if (_a != _b) { \ - NM_CMP_RETURN_DIRECT(nm_strcmp0(_a, _b)); \ - } \ - } while (0) - -#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) \ - do { \ - 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))); \ - } while (0) - -/*****************************************************************************/ - -#define NM_AF_UNSPEC 0 /* AF_UNSPEC */ -#define NM_AF_INET 2 /* AF_INET */ -#define NM_AF_INET6 10 /* AF_INET6 */ - -#define NM_AF_INET_SIZE 4 /* sizeof (in_addr_t) */ -#define NM_AF_INET6_SIZE 16 /* sizeof (stuct in6_addr) */ - -static inline char -nm_utils_addr_family_to_char(int addr_family) -{ - switch (addr_family) { - case NM_AF_UNSPEC: - return 'X'; - case NM_AF_INET: - return '4'; - case NM_AF_INET6: - return '6'; - } - nm_assert_not_reached(); - return '?'; -} - -static inline size_t -nm_utils_addr_family_to_size(int addr_family) -{ - switch (addr_family) { - case NM_AF_INET: - return NM_AF_INET_SIZE; - case NM_AF_INET6: - return NM_AF_INET6_SIZE; - } - nm_assert_not_reached(); - return 0; -} - -static inline int -nm_utils_addr_family_from_size(size_t len) -{ - switch (len) { - case NM_AF_INET_SIZE: - return NM_AF_INET; - case NM_AF_INET6_SIZE: - return NM_AF_INET6; - } - return NM_AF_UNSPEC; -} - -#define nm_assert_addr_family(addr_family) \ - nm_assert(NM_IN_SET((addr_family), NM_AF_INET, NM_AF_INET6)) - -#define NM_IS_IPv4(addr_family) \ - ({ \ - const int _addr_family = (addr_family); \ - \ - nm_assert_addr_family(_addr_family); \ - \ - (_addr_family == NM_AF_INET); \ - }) - -#endif /* __NM_STD_AUX_H__ */ diff --git a/shared/nm-std-aux/nm-std-utils.c b/shared/nm-std-aux/nm-std-utils.c deleted file mode 100644 index 18692b19..00000000 --- a/shared/nm-std-aux/nm-std-utils.c +++ /dev/null @@ -1,90 +0,0 @@ -/* SPDX-License-Identifier: LGPL-2.1-or-later */ - -#include "nm-default-std.h" - -#include "nm-std-utils.h" - -#include <stdint.h> -#include <assert.h> -#include <limits.h> - -/*****************************************************************************/ - -size_t -nm_utils_get_next_realloc_size(bool true_realloc, size_t requested) -{ - size_t n, x; - - /* https://doc.qt.io/qt-5/containers.html#growth-strategies */ - - if (requested <= 40) { - /* small allocations. Increase in small steps of 8 bytes. - * - * We get thus sizes of 8, 16, 32, 40. */ - if (requested <= 8) - return 8; - if (requested <= 16) - return 16; - if (requested <= 32) - return 32; - - /* The return values for < 104 are essentially hard-coded, and the choice here is - * made without very strong reasons. - * - * We want to stay 24 bytes below the power-of-two border 64. Hence, return 40 here. - * However, the next step then is already 104 (128 - 24). It's a larger gap than in - * the steps before. - * - * It's not clear whether some of the steps should be adjusted (or how exactly). */ - return 40; - } - - if (requested <= 0x2000u - 24u || NM_UNLIKELY(!true_realloc)) { - /* mid sized allocations. Return next power of two, minus 24 bytes extra space - * at the beginning. - * That means, we double the size as we grow. - * - * With !true_realloc, it means that the caller does not intend to call - * realloc() but instead clone the buffer. This is for example the case, when we - * want to nm_explicit_bzero() the old buffer. In that case we really want to grow - * the buffer exponentially every time and not increment in page sizes of 4K (below). - * - * We get thus sizes of 104, 232, 488, 1000, 2024, 4072, 8168... */ - - if (NM_UNLIKELY(requested > SIZE_MAX / 2u - 24u)) - goto out_huge; - - x = requested + 24u; - n = 128u; - while (n < x) { - n <<= 1; - nm_assert(n > 128u); - } - - nm_assert(n > 24u && n - 24u >= requested); - return n - 24u; - } - - if (NM_UNLIKELY(requested > SIZE_MAX - 0x1000u - 24u)) { - /* overflow happened. */ - goto out_huge; - } - - /* For large allocations (with !true_realloc) we allocate memory in chunks of - * 4K (- 24 bytes extra), assuming that the memory gets mmapped and thus - * realloc() is efficient by just reordering pages. */ - n = ((requested + (0x0FFFu + 24u)) & ~((size_t) 0x0FFFu)) - 24u; - nm_assert(n >= requested); - return n; - -out_huge: - if (sizeof(size_t) > 4u) { - /* on s390x (64 bit), gcc with LTO can complain that the size argument to - * malloc must not be larger than 9223372036854775807. - * - * Work around that by returning SSIZE_MAX. It should be plenty still! */ - assert(requested <= (size_t) SSIZE_MAX); - return (size_t) SSIZE_MAX; - } - return SIZE_MAX; -} diff --git a/shared/nm-std-aux/nm-std-utils.h b/shared/nm-std-aux/nm-std-utils.h deleted file mode 100644 index 9c851f1f..00000000 --- a/shared/nm-std-aux/nm-std-utils.h +++ /dev/null @@ -1,36 +0,0 @@ -/* SPDX-License-Identifier: LGPL-2.1-or-later */ - -#ifndef __NM_STD_UTILS_H__ -#define __NM_STD_UTILS_H__ - -#include <stdbool.h> - -#include "nm-std-aux.h" - -/*****************************************************************************/ - -/* 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_32 ((size_t) 32) -#define NM_UTILS_GET_NEXT_REALLOC_SIZE_40 ((size_t) 40) -#define NM_UTILS_GET_NEXT_REALLOC_SIZE_104 ((size_t) 104) -#define NM_UTILS_GET_NEXT_REALLOC_SIZE_1000 ((size_t) 1000) - -size_t nm_utils_get_next_realloc_size(bool true_realloc, size_t requested); - -#endif /* __NM_STD_UTILS_H__ */ diff --git a/shared/nm-std-aux/unaligned.h b/shared/nm-std-aux/unaligned.h deleted file mode 100644 index 4100be08..00000000 --- a/shared/nm-std-aux/unaligned.h +++ /dev/null @@ -1,99 +0,0 @@ -/* SPDX-License-Identifier: LGPL-2.1-or-later */ -#pragma once - -#include <endian.h> -#include <stdint.h> - -/* BE */ - -static inline uint16_t unaligned_read_be16(const void *_u) { - const struct __attribute__((__packed__, __may_alias__)) { uint16_t x; } *u = _u; - - return be16toh(u->x); -} - -static inline uint32_t unaligned_read_be32(const void *_u) { - const struct __attribute__((__packed__, __may_alias__)) { uint32_t x; } *u = _u; - - return be32toh(u->x); -} - -static inline uint64_t unaligned_read_be64(const void *_u) { - const struct __attribute__((__packed__, __may_alias__)) { uint64_t x; } *u = _u; - - return be64toh(u->x); -} - -static inline void unaligned_write_be16(void *_u, uint16_t a) { - struct __attribute__((__packed__, __may_alias__)) { uint16_t x; } *u = _u; - - u->x = be16toh(a); -} - -static inline void unaligned_write_be32(void *_u, uint32_t a) { - struct __attribute__((__packed__, __may_alias__)) { uint32_t x; } *u = _u; - - u->x = be32toh(a); -} - -static inline void unaligned_write_be64(void *_u, uint64_t a) { - struct __attribute__((__packed__, __may_alias__)) { uint64_t x; } *u = _u; - - u->x = be64toh(a); -} - -/* LE */ - -static inline uint16_t unaligned_read_le16(const void *_u) { - const struct __attribute__((__packed__, __may_alias__)) { uint16_t x; } *u = _u; - - return le16toh(u->x); -} - -static inline uint32_t unaligned_read_le32(const void *_u) { - const struct __attribute__((__packed__, __may_alias__)) { uint32_t x; } *u = _u; - - return le32toh(u->x); -} - -static inline uint64_t unaligned_read_le64(const void *_u) { - const struct __attribute__((__packed__, __may_alias__)) { uint64_t x; } *u = _u; - - return le64toh(u->x); -} - -static inline void unaligned_write_le16(void *_u, uint16_t a) { - struct __attribute__((__packed__, __may_alias__)) { uint16_t x; } *u = _u; - - u->x = le16toh(a); -} - -static inline void unaligned_write_le32(void *_u, uint32_t a) { - struct __attribute__((__packed__, __may_alias__)) { uint32_t x; } *u = _u; - - u->x = le32toh(a); -} - -static inline void unaligned_write_le64(void *_u, uint64_t a) { - struct __attribute__((__packed__, __may_alias__)) { uint64_t x; } *u = _u; - - u->x = le64toh(a); -} - -#if __BYTE_ORDER == __BIG_ENDIAN -#define unaligned_read_ne16 unaligned_read_be16 -#define unaligned_read_ne32 unaligned_read_be32 -#define unaligned_read_ne64 unaligned_read_be64 - -#define unaligned_write_ne16 unaligned_write_be16 -#define unaligned_write_ne32 unaligned_write_be32 -#define unaligned_write_ne64 unaligned_write_be64 -#else -#define unaligned_read_ne16 unaligned_read_le16 -#define unaligned_read_ne32 unaligned_read_le32 -#define unaligned_read_ne64 unaligned_read_le64 - -#define unaligned_write_ne16 unaligned_write_le16 -#define unaligned_write_ne32 unaligned_write_le32 -#define unaligned_write_ne64 unaligned_write_le64 -#endif diff --git a/shared/nm-test-libnm-utils.h b/shared/nm-test-libnm-utils.h deleted file mode 100644 index cac54076..00000000 --- a/shared/nm-test-libnm-utils.h +++ /dev/null @@ -1,90 +0,0 @@ -/* SPDX-License-Identifier: LGPL-2.1-or-later */ -/* - * Copyright (C) 2014 - 2015 Red Hat, Inc. - */ - -#ifndef __NM_TEST_LIBNM_UTILS_H__ -#define __NM_TEST_LIBNM_UTILS_H__ - -#include "NetworkManager.h" - -#include "nm-utils/nm-test-utils.h" - -typedef struct { - GDBusConnection *bus; - GDBusProxy * proxy; - GPid pid; - int keepalive_fd; -} NMTstcServiceInfo; - -NMTstcServiceInfo *nmtstc_service_init(void); -void nmtstc_service_cleanup(NMTstcServiceInfo *info); -NMTstcServiceInfo *nmtstc_service_available(NMTstcServiceInfo *info); - -static inline void -_nmtstc_auto_service_cleanup(NMTstcServiceInfo **info) -{ - nmtstc_service_cleanup(g_steal_pointer(info)); -} -#define nmtstc_auto_service_cleanup nm_auto(_nmtstc_auto_service_cleanup) - -#define NMTSTC_SERVICE_INFO_SETUP(sinfo) \ - NM_PRAGMA_WARNING_DISABLE("-Wunused-variable") \ - nmtstc_auto_service_cleanup NMTstcServiceInfo *sinfo = ({ \ - NMTstcServiceInfo *_sinfo; \ - \ - _sinfo = nmtstc_service_init(); \ - if (!nmtstc_service_available(_sinfo)) \ - return; \ - _sinfo; \ - }); \ - NM_PRAGMA_WARNING_REENABLE - -NMDevice *nmtstc_service_add_device(NMTstcServiceInfo *info, - NMClient * client, - const char * method, - const char * ifname); - -NMDevice *nmtstc_service_add_wired_device(NMTstcServiceInfo *sinfo, - NMClient * client, - const char * ifname, - const char * hwaddr, - const char ** subchannels); - -void nmtstc_service_add_connection(NMTstcServiceInfo *sinfo, - NMConnection * connection, - gboolean verify_connection, - char ** out_path); - -void nmtstc_service_add_connection_variant(NMTstcServiceInfo *sinfo, - GVariant * connection, - gboolean verify_connection, - char ** out_path); - -void nmtstc_service_update_connection(NMTstcServiceInfo *sinfo, - const char * path, - NMConnection * connection, - gboolean verify_connection); - -void nmtstc_service_update_connection_variant(NMTstcServiceInfo *sinfo, - const char * path, - GVariant * connection, - gboolean verify_connection); - -gpointer nmtstc_context_object_new_valist(GType gtype, - gboolean allow_iterate_main_context, - const char *first_property_name, - va_list var_args); - -gpointer nmtstc_context_object_new(GType gtype, - gboolean allow_iterate_main_context, - const char *first_property_name, - ...); - -static inline NMClient * -nmtstc_client_new(gboolean allow_iterate_main_context) -{ - return nmtstc_context_object_new(NM_TYPE_CLIENT, allow_iterate_main_context, NULL); -} - -#endif /* __NM_TEST_LIBNM_UTILS_H__ */ diff --git a/shared/nm-test-utils-impl.c b/shared/nm-test-utils-impl.c deleted file mode 100644 index f8f98d81..00000000 --- a/shared/nm-test-utils-impl.c +++ /dev/null @@ -1,644 +0,0 @@ -/* SPDX-License-Identifier: LGPL-2.1-or-later */ -/* - * Copyright (C) 2010 - 2015 Red Hat, Inc. - */ - -#include "libnm/nm-default-libnm.h" - -#include <sys/wait.h> - -#include "NetworkManager.h" -#include "nm-std-aux/nm-dbus-compat.h" - -#include "nm-test-libnm-utils.h" - -#define NMTSTC_NM_SERVICE NM_BUILD_SRCDIR "/tools/test-networkmanager-service.py" - -/*****************************************************************************/ - -static gboolean -name_exists(GDBusConnection *c, const char *name) -{ - GVariant *reply; - gboolean exists = FALSE; - - reply = g_dbus_connection_call_sync(c, - DBUS_SERVICE_DBUS, - DBUS_PATH_DBUS, - DBUS_INTERFACE_DBUS, - "GetNameOwner", - g_variant_new("(s)", name), - NULL, - G_DBUS_CALL_FLAGS_NO_AUTO_START, - -1, - NULL, - NULL); - if (reply != NULL) { - exists = TRUE; - g_variant_unref(reply); - } - - return exists; -} - -typedef struct { - GMainLoop * mainloop; - GDBusConnection *bus; - int exit_code; - bool exited : 1; - bool name_found : 1; -} ServiceInitWaitData; - -static gboolean -_service_init_wait_probe_name(gpointer user_data) -{ - ServiceInitWaitData *data = user_data; - - if (!name_exists(data->bus, "org.freedesktop.NetworkManager")) - return G_SOURCE_CONTINUE; - - data->name_found = TRUE; - g_main_loop_quit(data->mainloop); - return G_SOURCE_REMOVE; -} - -static void -_service_init_wait_child_wait(GPid pid, int status, gpointer user_data) -{ - ServiceInitWaitData *data = user_data; - - data->exited = TRUE; - data->exit_code = status; - g_main_loop_quit(data->mainloop); -} - -NMTstcServiceInfo * -nmtstc_service_available(NMTstcServiceInfo *info) -{ - gs_free char *m = NULL; - - if (info) - return info; - - /* This happens, when test-networkmanager-service.py exits with 77 status - * code. */ - m = g_strdup_printf("missing dependency for running NetworkManager stub service %s", - NMTSTC_NM_SERVICE); - g_test_skip(m); - return NULL; -} - -NMTstcServiceInfo * -nmtstc_service_init(void) -{ - NMTstcServiceInfo *info; - const char * args[] = {TEST_NM_PYTHON, NMTSTC_NM_SERVICE, NULL}; - GError * error = NULL; - - info = g_malloc0(sizeof(*info)); - - info->bus = g_bus_get_sync(G_BUS_TYPE_SESSION, NULL, &error); - g_assert_no_error(error); - - /* Spawn the test service. info->keepalive_fd will be a pipe to the service's - * stdin; if it closes, the service will exit immediately. We use this to - * make sure the service exits if the test program crashes. - */ - g_spawn_async_with_pipes(NULL, - (char **) args, - NULL, - G_SPAWN_SEARCH_PATH | G_SPAWN_DO_NOT_REAP_CHILD, - NULL, - NULL, - &info->pid, - &info->keepalive_fd, - NULL, - NULL, - &error); - g_assert_no_error(error); - - { - nm_auto_unref_gsource GSource *timeout_source = NULL; - nm_auto_unref_gsource GSource *child_source = NULL; - GMainContext * context = g_main_context_new(); - ServiceInitWaitData data = { - .bus = info->bus, - .mainloop = g_main_loop_new(context, FALSE), - }; - gboolean had_timeout; - - timeout_source = g_timeout_source_new(50); - g_source_set_callback(timeout_source, _service_init_wait_probe_name, &data, NULL); - g_source_attach(timeout_source, context); - - child_source = g_child_watch_source_new(info->pid); - g_source_set_callback(child_source, - G_SOURCE_FUNC(_service_init_wait_child_wait), - &data, - NULL); - g_source_attach(child_source, context); - - had_timeout = !nmtst_main_loop_run(data.mainloop, 30000); - - g_source_destroy(timeout_source); - g_source_destroy(child_source); - g_main_loop_unref(data.mainloop); - g_main_context_unref(context); - - if (had_timeout) - g_error("test service %s did not start in time", NMTSTC_NM_SERVICE); - if (!data.name_found) { - g_assert(data.exited); - info->pid = NM_PID_T_INVAL; - nmtstc_service_cleanup(info); - - if (WIFEXITED(data.exit_code) && WEXITSTATUS(data.exit_code) == 77) { - /* If the stub service exited with status 77 it means that it decided - * that it cannot conduct the tests and the test should be (gracefully) - * skip. The likely reason for that, is that libnm is not available - * via pygobject. */ - return NULL; - } - g_error("test service %s exited with error code %d", NMTSTC_NM_SERVICE, data.exit_code); - } - } - - /* Grab a proxy to our fake NM service to trigger tests */ - info->proxy = g_dbus_proxy_new_sync(info->bus, - G_DBUS_PROXY_FLAGS_DO_NOT_LOAD_PROPERTIES - | G_DBUS_PROXY_FLAGS_DO_NOT_CONNECT_SIGNALS - | G_DBUS_PROXY_FLAGS_DO_NOT_AUTO_START, - NULL, - NM_DBUS_SERVICE, - NM_DBUS_PATH, - "org.freedesktop.NetworkManager.LibnmGlibTest", - NULL, - &error); - g_assert_no_error(error); - - return info; -} - -void -nmtstc_service_cleanup(NMTstcServiceInfo *info) -{ - int ret; - gint64 t; - int status; - - if (!info) - return; - - nm_close(nm_steal_fd(&info->keepalive_fd)); - - g_clear_object(&info->proxy); - - if (info->pid != NM_PID_T_INVAL) { - kill(info->pid, SIGTERM); - - t = g_get_monotonic_time(); -again_wait: - ret = waitpid(info->pid, &status, WNOHANG); - if (ret == 0) { - if (t + 2000000 < g_get_monotonic_time()) { - kill(info->pid, SIGKILL); - g_error("child process %lld did not exit within timeout", (long long) info->pid); - } - g_usleep(G_USEC_PER_SEC / 50); - goto again_wait; - } - if (ret == -1 && errno == EINTR) - goto again_wait; - - g_assert(ret == info->pid); - } - - nmtst_main_context_iterate_until_assert_full( - NULL, - 1000, - 80, - (!name_exists(info->bus, "org.freedesktop.NetworkManager"))); - - g_clear_object(&info->bus); - - memset(info, 0, sizeof(*info)); - g_free(info); -} - -typedef struct { - GMainLoop * loop; - const char *ifname; - const char *path; - NMDevice * device; -} AddDeviceInfo; - -static void -device_added_cb(NMClient *client, NMDevice *device, gpointer user_data) -{ - AddDeviceInfo *info = user_data; - - g_assert(info); - g_assert(!info->device); - - g_assert(NM_IS_DEVICE(device)); - g_assert_cmpstr(nm_object_get_path(NM_OBJECT(device)), ==, info->path); - g_assert_cmpstr(nm_device_get_iface(device), ==, info->ifname); - - info->device = g_object_ref(device); - g_main_loop_quit(info->loop); -} - -static GVariant * -call_add_wired_device(GDBusProxy * proxy, - const char * ifname, - const char * hwaddr, - const char **subchannels, - GError ** error) -{ - const char *empty[] = {NULL}; - - if (!hwaddr) - hwaddr = "/"; - if (!subchannels) - subchannels = empty; - - return g_dbus_proxy_call_sync(proxy, - "AddWiredDevice", - g_variant_new("(ss^as)", ifname, hwaddr, subchannels), - G_DBUS_CALL_FLAGS_NO_AUTO_START, - 3000, - NULL, - error); -} - -static GVariant * -call_add_device(GDBusProxy *proxy, const char *method, const char *ifname, GError **error) -{ - return g_dbus_proxy_call_sync(proxy, - method, - g_variant_new("(s)", ifname), - G_DBUS_CALL_FLAGS_NO_AUTO_START, - 3000, - NULL, - error); -} - -static NMDevice * -add_device_common(NMTstcServiceInfo *sinfo, - NMClient * client, - const char * method, - const char * ifname, - const char * hwaddr, - const char ** subchannels) -{ - nm_auto_unref_gmainloop GMainLoop *loop = NULL; - gs_unref_variant GVariant *ret = NULL; - gs_free_error GError *error = NULL; - AddDeviceInfo info; - - g_assert(sinfo); - g_assert(NM_IS_CLIENT(client)); - - if (nm_streq0(method, "AddWiredDevice")) - ret = call_add_wired_device(sinfo->proxy, ifname, hwaddr, subchannels, &error); - else - ret = call_add_device(sinfo->proxy, method, ifname, &error); - - nmtst_assert_success(ret, error); - g_assert_cmpstr(g_variant_get_type_string(ret), ==, "(o)"); - - /* Wait for NMClient to find the device */ - - loop = g_main_loop_new(nm_client_get_main_context(client), FALSE); - - info = (AddDeviceInfo){ - .ifname = ifname, - .loop = loop, - }; - g_variant_get(ret, "(&o)", &info.path); - - g_signal_connect(client, NM_CLIENT_DEVICE_ADDED, G_CALLBACK(device_added_cb), &info); - - if (!nmtst_main_loop_run(loop, 5000)) - g_assert_not_reached(); - - g_signal_handlers_disconnect_by_func(client, device_added_cb, &info); - - g_assert(NM_IS_DEVICE(info.device)); - - g_assert(info.device - == nm_client_get_device_by_path(client, nm_object_get_path(NM_OBJECT(info.device)))); - g_object_unref(info.device); - return info.device; -} - -NMDevice * -nmtstc_service_add_device(NMTstcServiceInfo *sinfo, - NMClient * client, - const char * method, - const char * ifname) -{ - return add_device_common(sinfo, client, method, ifname, NULL, NULL); -} - -NMDevice * -nmtstc_service_add_wired_device(NMTstcServiceInfo *sinfo, - NMClient * client, - const char * ifname, - const char * hwaddr, - const char ** subchannels) -{ - return add_device_common(sinfo, client, "AddWiredDevice", ifname, hwaddr, subchannels); -} - -void -nmtstc_service_add_connection(NMTstcServiceInfo *sinfo, - NMConnection * connection, - gboolean verify_connection, - char ** out_path) -{ - nmtstc_service_add_connection_variant( - sinfo, - nm_connection_to_dbus(connection, NM_CONNECTION_SERIALIZE_ALL), - verify_connection, - out_path); -} - -void -nmtstc_service_add_connection_variant(NMTstcServiceInfo *sinfo, - GVariant * connection, - gboolean verify_connection, - char ** out_path) -{ - GVariant *result; - GError * error = NULL; - - g_assert(sinfo); - g_assert(G_IS_DBUS_PROXY(sinfo->proxy)); - g_assert(g_variant_is_of_type(connection, G_VARIANT_TYPE("a{sa{sv}}"))); - - result = g_dbus_proxy_call_sync(sinfo->proxy, - "AddConnection", - g_variant_new("(vb)", connection, verify_connection), - G_DBUS_CALL_FLAGS_NO_AUTO_START, - 3000, - NULL, - &error); - g_assert_no_error(error); - g_assert(g_variant_is_of_type(result, G_VARIANT_TYPE("(o)"))); - if (out_path) - g_variant_get(result, "(o)", out_path); - g_variant_unref(result); -} - -void -nmtstc_service_update_connection(NMTstcServiceInfo *sinfo, - const char * path, - NMConnection * connection, - gboolean verify_connection) -{ - if (!path) - path = nm_connection_get_path(connection); - g_assert(path); - - nmtstc_service_update_connection_variant( - sinfo, - path, - nm_connection_to_dbus(connection, NM_CONNECTION_SERIALIZE_ALL), - verify_connection); -} - -void -nmtstc_service_update_connection_variant(NMTstcServiceInfo *sinfo, - const char * path, - GVariant * connection, - gboolean verify_connection) -{ - GVariant *result; - GError * error = NULL; - - g_assert(sinfo); - g_assert(G_IS_DBUS_PROXY(sinfo->proxy)); - g_assert(g_variant_is_of_type(connection, G_VARIANT_TYPE("a{sa{sv}}"))); - g_assert(path && path[0] == '/'); - - result = g_dbus_proxy_call_sync(sinfo->proxy, - "UpdateConnection", - g_variant_new("(ovb)", path, connection, verify_connection), - G_DBUS_CALL_FLAGS_NO_AUTO_START, - 3000, - NULL, - &error); - g_assert_no_error(error); - g_assert(g_variant_is_of_type(result, G_VARIANT_TYPE("()"))); - g_variant_unref(result); -} - -/*****************************************************************************/ - -typedef struct { - GType gtype; - GMainLoop *loop; - GObject * obj; - bool call_nm_client_new_async : 1; -} NMTstcObjNewData; - -static void -_context_object_new_do_cb(GObject *source_object, GAsyncResult *res, gpointer user_data) -{ - NMTstcObjNewData *d = user_data; - gs_free_error GError *error = NULL; - - g_assert(!d->obj); - - if (d->call_nm_client_new_async) { - d->obj = G_OBJECT(nm_client_new_finish(res, nmtst_get_rand_bool() ? &error : NULL)); - } else { - d->obj = g_async_initable_new_finish(G_ASYNC_INITABLE(source_object), - res, - nmtst_get_rand_bool() ? &error : NULL); - } - - nmtst_assert_success(G_IS_OBJECT(d->obj), error); - g_assert(G_OBJECT_TYPE(d->obj) == d->gtype); - - g_main_loop_quit(d->loop); -} - -static GObject * -_context_object_new_do(GType gtype, - gboolean sync, - const char *first_property_name, - va_list var_args) -{ - gs_free_error GError *error = NULL; - GObject * obj; - - /* Create a GObject instance synchronously, and arbitrarily use either - * the sync or async constructor. - * - * Note that the sync and async construct differ in one important aspect: - * the async constructor iterates the current g_main_context_get_thread_default(), - * while the sync constructor does not! Aside from that, both should behave - * pretty much the same way. */ - - if (sync) { - nm_auto_destroy_and_unref_gsource GSource *source = NULL; - - if (nmtst_get_rand_bool()) { - /* the current main context must not be iterated! */ - source = g_idle_source_new(); - g_source_set_callback(source, nmtst_g_source_assert_not_called, NULL, NULL); - g_source_attach(source, g_main_context_get_thread_default()); - } - - if (gtype != NM_TYPE_CLIENT || first_property_name || nmtst_get_rand_bool()) { - gboolean success; - - if (first_property_name || nmtst_get_rand_bool()) - obj = g_object_new_valist(gtype, first_property_name, var_args); - else - obj = g_object_new(gtype, NULL); - - success = g_initable_init(G_INITABLE(obj), NULL, nmtst_get_rand_bool() ? &error : NULL); - nmtst_assert_success(success, error); - } else { - obj = G_OBJECT(nm_client_new(NULL, nmtst_get_rand_bool() ? &error : NULL)); - } - } else { - nm_auto_unref_gmainloop GMainLoop *loop = NULL; - NMTstcObjNewData d = { - .gtype = gtype, - .loop = NULL, - }; - gs_unref_object GObject *obj2 = NULL; - - loop = g_main_loop_new(g_main_context_get_thread_default(), FALSE); - d.loop = loop; - - if (gtype != NM_TYPE_CLIENT || first_property_name || nmtst_get_rand_bool()) { - if (first_property_name || nmtst_get_rand_bool()) - obj2 = g_object_new_valist(gtype, first_property_name, var_args); - else - obj2 = g_object_new(gtype, NULL); - - g_async_initable_init_async(G_ASYNC_INITABLE(obj2), - G_PRIORITY_DEFAULT, - NULL, - _context_object_new_do_cb, - &d); - } else { - d.call_nm_client_new_async = TRUE; - nm_client_new_async(NULL, _context_object_new_do_cb, &d); - } - g_main_loop_run(loop); - obj = d.obj; - g_assert(!obj2 || obj == obj2); - } - - nmtst_assert_success(G_IS_OBJECT(obj), error); - g_assert(G_OBJECT_TYPE(obj) == gtype); - return obj; -} - -typedef struct { - GType gtype; - const char *first_property_name; - va_list var_args; - GMainLoop * loop; - GObject * obj; - bool sync; -} NewSyncInsideDispatchedData; - -static gboolean -_context_object_new_inside_loop_do(gpointer user_data) -{ - NewSyncInsideDispatchedData *d = user_data; - - g_assert(d->loop); - g_assert(!d->obj); - - d->obj = - nmtstc_context_object_new_valist(d->gtype, d->sync, d->first_property_name, d->var_args); - g_main_loop_quit(d->loop); - return G_SOURCE_CONTINUE; -} - -static GObject * -_context_object_new_inside_loop(GType gtype, - gboolean sync, - const char *first_property_name, - va_list var_args) -{ - GMainContext * context = g_main_context_get_thread_default(); - nm_auto_unref_gmainloop GMainLoop *loop = g_main_loop_new(context, FALSE); - NewSyncInsideDispatchedData d = { - .gtype = gtype, - .first_property_name = first_property_name, - .sync = sync, - .loop = loop, - }; - nm_auto_destroy_and_unref_gsource GSource *source = NULL; - - va_copy(d.var_args, var_args); - - source = g_idle_source_new(); - g_source_set_callback(source, _context_object_new_inside_loop_do, &d, NULL); - g_source_attach(source, context); - - g_main_loop_run(loop); - - va_end(d.var_args); - - g_assert(G_IS_OBJECT(d.obj)); - g_assert(G_OBJECT_TYPE(d.obj) == gtype); - return d.obj; -} - -gpointer -nmtstc_context_object_new_valist(GType gtype, - gboolean allow_iterate_main_context, - const char *first_property_name, - va_list var_args) -{ - gboolean inside_loop; - gboolean sync; - - if (!allow_iterate_main_context) { - sync = TRUE; - inside_loop = FALSE; - } else { - /* The caller allows to iterate the main context. On that point, - * we can both use the synchronous and the asynchronous initialization, - * both should yield the same result. Choose one randomly. */ - sync = nmtst_get_rand_bool(); - inside_loop = ((nmtst_get_rand_uint32() % 3) == 0); - } - - if (inside_loop) { - /* Create the obj on an idle handler of the current context. - * In practice, it should make no difference, which this check - * tries to prove. */ - return _context_object_new_inside_loop(gtype, sync, first_property_name, var_args); - } - - return _context_object_new_do(gtype, sync, first_property_name, var_args); -} - -gpointer -nmtstc_context_object_new(GType gtype, - gboolean allow_iterate_main_context, - const char *first_property_name, - ...) -{ - GObject *obj; - va_list var_args; - - va_start(var_args, first_property_name); - obj = nmtstc_context_object_new_valist(gtype, - allow_iterate_main_context, - first_property_name, - var_args); - va_end(var_args); - return obj; -} diff --git a/shared/nm-udev-aux/nm-udev-utils.c b/shared/nm-udev-aux/nm-udev-utils.c deleted file mode 100644 index 0b941dff..00000000 --- a/shared/nm-udev-aux/nm-udev-utils.c +++ /dev/null @@ -1,265 +0,0 @@ -/* SPDX-License-Identifier: LGPL-2.1-or-later */ -/* - * Copyright (C) 2017 Red Hat, Inc. - */ - -#include "nm-glib-aux/nm-default-glib-i18n-lib.h" - -#include "nm-udev-utils.h" - -#include <libudev.h> - -struct _NMPUdevClient { - char ** subsystems; - GSource * watch_source; - struct udev * udev; - struct udev_monitor *monitor; - NMUdevClientEvent event_handler; - gpointer event_user_data; -}; - -/*****************************************************************************/ - -gboolean -nm_udev_utils_property_as_boolean(const char *uproperty) -{ - /* taken from g_udev_device_get_property_as_boolean() */ - - if (uproperty) { - if (strcmp(uproperty, "1") == 0 || g_ascii_strcasecmp(uproperty, "true") == 0) - return TRUE; - } - return FALSE; -} - -const char * -nm_udev_utils_property_decode(const char *uproperty, char **to_free) -{ - const char *p; - char * unescaped = NULL; - char * n = NULL; - - if (!uproperty) { - *to_free = NULL; - return NULL; - } - - p = uproperty; - while (*p) { - int a, b; - - if (p[0] == '\\' && p[1] == 'x' && (a = g_ascii_xdigit_value(p[2])) >= 0 - && (b = g_ascii_xdigit_value(p[3])) >= 0 && (a || b)) { - if (!n) { - gssize l = p - uproperty; - - unescaped = g_malloc(l + strlen(p) + 1 - 3); - memcpy(unescaped, uproperty, l); - n = &unescaped[l]; - } - *n++ = (a << 4) | b; - p += 4; - } else { - if (n) - *n++ = *p; - p++; - } - } - - if (!n) { - *to_free = NULL; - return uproperty; - } - - *n++ = '\0'; - return (*to_free = unescaped); -} - -char * -nm_udev_utils_property_decode_cp(const char *uproperty) -{ - char *cpy; - - uproperty = nm_udev_utils_property_decode(uproperty, &cpy); - return cpy ?: g_strdup(uproperty); -} - -/*****************************************************************************/ - -static void -_subsystem_split(const char * subsystem_full, - const char **out_subsystem, - const char **out_devtype, - char ** to_free) -{ - char *tmp, *s; - - nm_assert(subsystem_full); - nm_assert(out_subsystem); - nm_assert(out_devtype); - nm_assert(to_free); - - s = strstr(subsystem_full, "/"); - if (s) { - tmp = g_strdup(subsystem_full); - s = &tmp[s - subsystem_full]; - *s = '\0'; - *out_subsystem = tmp; - *out_devtype = &s[1]; - *to_free = tmp; - } else { - *out_subsystem = subsystem_full; - *out_devtype = NULL; - *to_free = NULL; - } -} - -static struct udev_enumerate * -nm_udev_utils_enumerate(struct udev *uclient, const char *const *subsystems) -{ - struct udev_enumerate *enumerate; - guint n; - - enumerate = udev_enumerate_new(uclient); - - if (subsystems) { - for (n = 0; subsystems[n]; n++) { - const char * subsystem; - const char * devtype; - gs_free char *to_free = NULL; - - _subsystem_split(subsystems[n], &subsystem, &devtype, &to_free); - - udev_enumerate_add_match_subsystem(enumerate, subsystem); - - if (devtype != NULL) - udev_enumerate_add_match_property(enumerate, "DEVTYPE", devtype); - } - } - - return enumerate; -} - -struct udev * -nm_udev_client_get_udev(NMUdevClient *self) -{ - g_return_val_if_fail(self, NULL); - - return self->udev; -} - -struct udev_enumerate * -nm_udev_client_enumerate_new(NMUdevClient *self) -{ - g_return_val_if_fail(self, NULL); - - return nm_udev_utils_enumerate(self->udev, (const char *const *) self->subsystems); -} - -/*****************************************************************************/ - -static gboolean -monitor_event(int fd, GIOCondition condition, gpointer user_data) -{ - NMUdevClient * self = user_data; - struct udev_device *udevice; - - if (!self->monitor) - goto out; - - udevice = udev_monitor_receive_device(self->monitor); - if (udevice == NULL) - goto out; - - self->event_handler(self, udevice, self->event_user_data); - udev_device_unref(udevice); - -out: - return TRUE; -} - -/** - * nm_udev_client_new: - * @subsystems: the subsystems - * @event_handler: callback for events - * @event_user_data: user-data for @event_handler - * - * Basically, it is g_udev_client_new(), and most notably - * g_udev_client_constructed(). - * - * Returns: a new NMUdevClient instance. - */ -NMUdevClient * -nm_udev_client_new(const char *const *subsystems, - NMUdevClientEvent event_handler, - gpointer event_user_data) -{ - NMUdevClient *self; - guint n; - - self = g_slice_new0(NMUdevClient); - - self->event_handler = event_handler; - self->event_user_data = event_user_data; - self->subsystems = subsystems && subsystems[0] ? g_strdupv((char **) subsystems) : NULL; - - self->udev = udev_new(); - if (!self->udev) - goto fail; - - /* connect to event source */ - if (self->event_handler) { - self->monitor = udev_monitor_new_from_netlink(self->udev, "udev"); - if (!self->monitor) - goto fail; - - if (self->subsystems) { - /* install subsystem filters to only wake up for certain events */ - for (n = 0; self->subsystems[n]; n++) { - gs_free char *to_free = NULL; - const char * subsystem; - const char * devtype; - - _subsystem_split(self->subsystems[n], &subsystem, &devtype, &to_free); - udev_monitor_filter_add_match_subsystem_devtype(self->monitor, subsystem, devtype); - } - - /* listen to events, and buffer them */ - udev_monitor_set_receive_buffer_size(self->monitor, 4 * 1024 * 1024); - udev_monitor_enable_receiving(self->monitor); - - self->watch_source = nm_g_unix_fd_source_new(udev_monitor_get_fd(self->monitor), - G_IO_IN, - G_PRIORITY_DEFAULT, - monitor_event, - self, - NULL); - g_source_attach(self->watch_source, g_main_context_get_thread_default()); - } - } - - return self; - -fail: - return nm_udev_client_destroy(self); -} - -NMUdevClient * -nm_udev_client_destroy(NMUdevClient *self) -{ - if (!self) - return NULL; - - nm_clear_g_source_inst(&self->watch_source); - - udev_monitor_unref(self->monitor); - self->monitor = NULL; - udev_unref(self->udev); - self->udev = NULL; - - g_strfreev(self->subsystems); - - g_slice_free(NMUdevClient, self); - - return NULL; -} diff --git a/shared/nm-udev-aux/nm-udev-utils.h b/shared/nm-udev-aux/nm-udev-utils.h deleted file mode 100644 index 191f9a89..00000000 --- a/shared/nm-udev-aux/nm-udev-utils.h +++ /dev/null @@ -1,33 +0,0 @@ -/* SPDX-License-Identifier: LGPL-2.1-or-later */ -/* - * Copyright (C) 2017 Red Hat, Inc. - */ - -#ifndef __NM_UDEV_UTILS_H__ -#define __NM_UDEV_UTILS_H__ - -struct udev; -struct udev_device; -struct udev_enumerate; - -gboolean nm_udev_utils_property_as_boolean(const char *uproperty); -const char *nm_udev_utils_property_decode(const char *uproperty, char **to_free); -char * nm_udev_utils_property_decode_cp(const char *uproperty); - -typedef struct _NMPUdevClient NMUdevClient; - -typedef void (*NMUdevClientEvent)(NMUdevClient * udev_client, - struct udev_device *udevice, - gpointer event_user_data); - -NMUdevClient *nm_udev_client_new(const char *const *subsystems, - NMUdevClientEvent event_handler, - gpointer event_user_data); - -NMUdevClient *nm_udev_client_destroy(NMUdevClient *self); - -struct udev *nm_udev_client_get_udev(NMUdevClient *self); - -struct udev_enumerate *nm_udev_client_enumerate_new(NMUdevClient *self); - -#endif /* __NM_UDEV_UTILS_H__ */ diff --git a/shared/nm-utils/nm-compat.c b/shared/nm-utils/nm-compat.c deleted file mode 100644 index dad48227..00000000 --- a/shared/nm-utils/nm-compat.c +++ /dev/null @@ -1,76 +0,0 @@ -/* SPDX-License-Identifier: LGPL-2.1-or-later */ -/* - * Copyright (C) 2017 Red Hat, Inc. - */ - -#include "libnm/nm-default-libnm.h" - -#include "nm-compat.h" - -/*****************************************************************************/ - -static void -_get_keys_cb(const char *key, const char *val, gpointer user_data) -{ - GPtrArray *a = user_data; - - g_ptr_array_add(a, g_strdup(key)); -} - -static const char ** -_get_keys(NMSettingVpn *setting, gboolean is_secrets, guint *out_length) -{ - guint len; - const char **keys = NULL; - GPtrArray * a; - - nm_assert(NM_IS_SETTING_VPN(setting)); - - if (is_secrets) - len = nm_setting_vpn_get_num_secrets(setting); - else - len = nm_setting_vpn_get_num_data_items(setting); - - a = g_ptr_array_sized_new(len + 1); - - if (is_secrets) - nm_setting_vpn_foreach_secret(setting, _get_keys_cb, a); - else - nm_setting_vpn_foreach_data_item(setting, _get_keys_cb, a); - - len = a->len; - if (len) { - g_ptr_array_sort(a, nm_strcmp_p); - g_ptr_array_add(a, NULL); - keys = g_malloc(a->len * sizeof(gpointer)); - memcpy(keys, a->pdata, a->len * sizeof(gpointer)); - - /* we need to cache the keys *somewhere*. */ - g_object_set_qdata_full(G_OBJECT(setting), - is_secrets - ? NM_CACHED_QUARK("libnm._nm_setting_vpn_get_secret_keys") - : NM_CACHED_QUARK("libnm._nm_setting_vpn_get_data_keys"), - g_ptr_array_free(a, FALSE), - (GDestroyNotify) g_strfreev); - } else - g_ptr_array_free(a, TRUE); - - NM_SET_OUT(out_length, len); - return keys; -} - -const char ** -_nm_setting_vpn_get_data_keys(NMSettingVpn *setting, guint *out_length) -{ - g_return_val_if_fail(NM_IS_SETTING_VPN(setting), NULL); - - return _get_keys(setting, FALSE, out_length); -} - -const char ** -_nm_setting_vpn_get_secret_keys(NMSettingVpn *setting, guint *out_length) -{ - g_return_val_if_fail(NM_IS_SETTING_VPN(setting), NULL); - - return _get_keys(setting, TRUE, out_length); -} diff --git a/shared/nm-utils/nm-compat.h b/shared/nm-utils/nm-compat.h deleted file mode 100644 index 559afd5d..00000000 --- a/shared/nm-utils/nm-compat.h +++ /dev/null @@ -1,35 +0,0 @@ -/* SPDX-License-Identifier: LGPL-2.1-or-later */ -/* - * Copyright (C) 2017 Red Hat, Inc. - */ - -#ifndef __NM_COMPAT_H__ -#define __NM_COMPAT_H__ - -#include "nm-setting-vpn.h" - -const char **_nm_setting_vpn_get_data_keys(NMSettingVpn *setting, guint *out_length); - -const char **_nm_setting_vpn_get_secret_keys(NMSettingVpn *setting, guint *out_length); - -#if NM_CHECK_VERSION(1, 11, 0) - #define nm_setting_vpn_get_data_keys(setting, out_length) \ - ({ \ - G_GNUC_BEGIN_IGNORE_DEPRECATIONS \ - nm_setting_vpn_get_data_keys(setting, out_length); \ - G_GNUC_END_IGNORE_DEPRECATIONS \ - }) - #define nm_setting_vpn_get_secret_keys(setting, out_length) \ - ({ \ - G_GNUC_BEGIN_IGNORE_DEPRECATIONS \ - nm_setting_vpn_get_secret_keys(setting, out_length); \ - G_GNUC_END_IGNORE_DEPRECATIONS \ - }) -#else - #define nm_setting_vpn_get_data_keys(setting, out_length) \ - _nm_setting_vpn_get_data_keys(setting, out_length) - #define nm_setting_vpn_get_secret_keys(setting, out_length) \ - _nm_setting_vpn_get_secret_keys(setting, out_length) -#endif - -#endif /* __NM_COMPAT_H__ */ diff --git a/shared/nm-utils/nm-test-utils.h b/shared/nm-utils/nm-test-utils.h deleted file mode 100644 index d51f972e..00000000 --- a/shared/nm-utils/nm-test-utils.h +++ /dev/null @@ -1,2824 +0,0 @@ -/* SPDX-License-Identifier: LGPL-2.1-or-later */ -/* - * Copyright (C) 2014 Red Hat, Inc. - */ - -#ifndef __NM_TEST_UTILS_H__ -#define __NM_TEST_UTILS_H__ - -/******************************************************************************* - * HOWTO run tests. - * - * Our tests (make check) include this header-only file nm-test-utils.h. - * - * You should always include this header *as last*. Reason is, that depending on - * previous includes, functionality will be enabled. - * - * Logging: - * In tests, nm-logging redirects to glib logging. By default, glib suppresses all debug - * messages unless you set G_MESSAGES_DEBUG. To enable debug logging, you can explicitly set - * G_MESSAGES_DEBUG. Otherwise, nm-test will set G_MESSAGES_DEBUG=all in debug mode (see below). - * For nm-logging, you can configure the log-level and domains via NMTST_DEBUG environment - * variable. - * - * Assert-logging: - * Some tests assert against logged messages (g_test_expect_message()). - * By specifying no-expect-message in NMTST_DEBUG, you can disable assert logging - * and g_test_assert_expected_messages() will not fail. - * - * NMTST_SEED_RAND environment variable: - * Tests that use random numbers from nmtst_get_rand() get seeded at each start. - * You can specify the seed by setting NMTST_SEED_RAND to a particular number or empty ("") - * for a random one. If NMTST_SEED_RAND is not set (default) a stable seed gets chosen. - * Tests will print the seed to stdout, so that you know which one was chosen or generated. - * - * - * NMTST_DEBUG environment variable: - * - * "debug", "no-debug": when at test is run in debug mode, it might behave differently, - * depending on the test. See nmtst_is_debug(). - * Known differences: - * - a test might leave the logging level unspecified. In this case, running in - * debug mode, will turn on DEBUG logging, otherwise WARN logging only. - * - if G_MESSAGES_DEBUG is unset, nm-test will set G_MESSAGES_DEBUG=all - * for tests that don't do assert-logging. - * Debug mode is determined as follows (highest priority first): - * - command line option --debug/--no-debug - * - NMTST_DEBUG=debug/no-debug - * - setting NMTST_DEBUG implies debugging turned on - * - g_test_verbose() - * - * "no-expect-message": for tests that would assert against log messages, disable - * those asserts. - * - * "log-level=LEVEL", "log-domains=DOMAIN": reset the log level and domain for tests. - * It only has an effect for nm-logging messages. - * This has no effect if the test asserts against logging (unless no-expect-message), - * otherwise, changing the logging would break tests. - * If you set the level to DEBUG or TRACE, it also sets G_MESSAGES_DEBUG=all (unless - * in assert-logging mode and unless G_MESSAGES_DEBUG is already defined). - * - * "TRACE", this is shorthand for "log-level=TRACE". - * - * "D", this is shorthand for "log-level=TRACE,no-expect-message". - * - * "sudo-cmd=PATH": when running root tests as normal user, the test will execute - * itself by invoking sudo at PATH. - * For example - * NMTST_DEBUG="sudo-cmd=$PWD/tools/test-sudo-wrapper.sh" make -C src/platform/tests/ check - * - * "slow|quick|thorough": enable/disable long-running tests. This sets nmtst_test_quick(). - * Whether long-running tests are enabled is determined as follows (highest priority first): - * - specifying the value in NMTST_DEBUG has highest priority - * - respect g_test_quick(), if the command line contains '-mslow', '-mquick', '-mthorough'. - * - use compile time default (CFLAGS=-DNMTST_TEST_QUICK=TRUE) - * - enable slow tests by default - * - * "p=PATH"|"s=PATH": passes the path to g_test_init() as "-p" and "-s", respectively. - * Unfortunately, these options conflict with "--tap" which our makefile passes to the - * tests, thus it's only useful outside of `make check`. - * - *******************************************************************************/ - -#if defined(NM_ASSERT_NO_MSG) && NM_ASSERT_NO_MSG - #undef g_return_if_fail_warning - #undef g_assertion_message_expr -#endif - -#include <arpa/inet.h> -#include <stdio.h> -#include <unistd.h> -#include <stdlib.h> -#include <string.h> -#include <errno.h> - -/*****************************************************************************/ - -#define NMTST_G_RETURN_MSG_S(expr) "*: assertion '" NM_ASSERT_G_RETURN_EXPR(expr) "' failed" -#define NMTST_G_RETURN_MSG(expr) NMTST_G_RETURN_MSG_S(#expr) - -/*****************************************************************************/ - -/* general purpose functions that have no dependency on other nmtst functions */ - -#define nmtst_assert_error(error, expect_error_domain, expect_error_code, expect_error_pattern) \ - G_STMT_START \ - { \ - GError * _error = (error); \ - GQuark _expect_error_domain = (expect_error_domain); \ - const char *_expect_error_pattern = (expect_error_pattern); \ - \ - if (_expect_error_domain) \ - g_assert_error(_error, _expect_error_domain, (expect_error_code)); \ - else \ - g_assert(_error); \ - g_assert(_error->message); \ - if (_expect_error_pattern \ - && !g_pattern_match_simple(_expect_error_pattern, _error->message)) { \ - g_error("%s:%d: error message does not have expected pattern '%s'. Instead it is " \ - "'%s' (%s, %d)", \ - __FILE__, \ - __LINE__, \ - _expect_error_pattern, \ - _error->message, \ - g_quark_to_string(_error->domain), \ - _error->code); \ - } \ - } \ - G_STMT_END - -#define NMTST_WAIT(max_wait_ms, wait) \ - ({ \ - gboolean _not_expired = TRUE; \ - const gint64 nmtst_wait_start_us = g_get_monotonic_time(); \ - const gint64 nmtst_wait_duration_us = (max_wait_ms) *1000L; \ - const gint64 nmtst_wait_end_us = nmtst_wait_start_us + nmtst_wait_duration_us; \ - gint64 _nmtst_wait_remaining_us = nmtst_wait_duration_us; \ - int _nmtst_wait_iteration = 0; \ - \ - while (TRUE) { \ - _nm_unused const gint64 nmtst_wait_remaining_us = _nmtst_wait_remaining_us; \ - _nm_unused int nmtst_wait_iteration = _nmtst_wait_iteration++; \ - \ - {wait}; \ - _nmtst_wait_remaining_us = (nmtst_wait_end_us - g_get_monotonic_time()); \ - if (_nmtst_wait_remaining_us <= 0) { \ - _not_expired = FALSE; \ - break; \ - } \ - } \ - _not_expired; \ - }) - -#define NMTST_WAIT_ASSERT(max_wait_ms, wait) \ - G_STMT_START \ - { \ - if (!(NMTST_WAIT(max_wait_ms, wait))) \ - g_assert_not_reached(); \ - } \ - G_STMT_END - -#define nmtst_assert_nonnull(command) \ - ({ \ - typeof(*(command)) *_ptr = (command); \ - \ - g_assert(_ptr && (TRUE || (command))); \ - _ptr; \ - }) - -#define nmtst_assert_success(success, error) \ - G_STMT_START \ - { \ - g_assert_no_error(error); \ - g_assert((success)); \ - } \ - G_STMT_END - -#define nmtst_assert_no_success(success, error) \ - G_STMT_START \ - { \ - g_assert(error); \ - g_assert(!(success)); \ - } \ - G_STMT_END - -/*****************************************************************************/ - -/* Our nm-error error numbers use negative values to signal failure. - * A non-negative value signals success. Hence, the correct way for checking - * is always (r < 0) vs. (r >= 0). Never (r == 0). - * - * For assertions in tests, we also want to assert that no positive values - * are returned. For a lot of functions, positive return values are unexpected - * and a bug. This macro evaluates @r to success or failure, while asserting - * that @r is not positive. */ -#define NMTST_NM_ERR_SUCCESS(r) \ - ({ \ - const int _r = (r); \ - \ - if (_r >= 0) \ - g_assert_cmpint(_r, ==, 0); \ - (_r >= 0); \ - }) - -/*****************************************************************************/ - -struct __nmtst_internal { - GRand * rand0; - guint32 rand_seed; - GRand * rand; - gboolean is_debug; - gboolean assert_logging; - gboolean no_expect_message; - gboolean test_quick; - gboolean test_tap_log; - char * sudo_cmd; - char ** orig_argv; -}; - -extern struct __nmtst_internal __nmtst_internal; - -#define NMTST_DEFINE() \ - struct __nmtst_internal __nmtst_internal = {0}; \ - \ - __attribute__((destructor)) static void _nmtst_exit(void) \ - { \ - __nmtst_internal.assert_logging = FALSE; \ - g_test_assert_expected_messages(); \ - nmtst_free(); \ - } - -static inline gboolean -nmtst_initialized(void) -{ - return !!__nmtst_internal.rand0; -} - -#define __NMTST_LOG(cmd, ...) \ - G_STMT_START \ - { \ - g_assert(nmtst_initialized()); \ - if (!__nmtst_internal.assert_logging || __nmtst_internal.no_expect_message) { \ - cmd(__VA_ARGS__); \ - } else { \ - printf(_NM_UTILS_MACRO_FIRST(__VA_ARGS__) "\n" _NM_UTILS_MACRO_REST(__VA_ARGS__)); \ - } \ - } \ - G_STMT_END - -/* split the string inplace at specific delimiters, allowing escaping with '\\'. - * Returns a zero terminated array of pointers into @str. - * - * The caller must g_free() the returned argv array. - **/ -static inline char ** -nmtst_str_split(char *str, const char *delimiters) -{ - const char *d; - GArray * result = g_array_sized_new(TRUE, FALSE, sizeof(char *), 3); - - g_assert(str); - g_assert(delimiters && !strchr(delimiters, '\\')); - - while (*str) { - gsize i = 0, j = 0; - - while (TRUE) { - char c = str[i]; - - if (c == '\0') { - str[j++] = 0; - break; - } else if (c == '\\') { - str[j++] = str[++i]; - if (!str[i]) - break; - } else { - for (d = delimiters; *d; d++) { - if (c == *d) { - str[j++] = 0; - i++; - goto BREAK_INNER_LOOPS; - } - } - str[j++] = c; - } - i++; - } - -BREAK_INNER_LOOPS: - g_array_append_val(result, str); - str = &str[i]; - } - - return (char **) g_array_free(result, FALSE); -} - -/* free instances allocated by nmtst (especially nmtst_init()) on shutdown - * to release memory. After nmtst_free(), the test is uninitialized again. */ -static inline void -nmtst_free(void) -{ - if (!nmtst_initialized()) - return; - - g_rand_free(__nmtst_internal.rand0); - if (__nmtst_internal.rand) - g_rand_free(__nmtst_internal.rand); - g_free(__nmtst_internal.sudo_cmd); - g_strfreev(__nmtst_internal.orig_argv); - - memset(&__nmtst_internal, 0, sizeof(__nmtst_internal)); -} - -static inline void -_nmtst_log_handler(const char * log_domain, - GLogLevelFlags log_level, - const char * message, - gpointer user_data) -{ - g_print("%s\n", message); -} - -static inline void -__nmtst_init(int * argc, - char *** argv, - gboolean assert_logging, - const char *log_level, - const char *log_domains, - gboolean * out_set_logging) -{ - const char * nmtst_debug; - gboolean is_debug = FALSE; - char * c_log_level = NULL, *c_log_domains = NULL; - char * sudo_cmd = NULL; - GArray * debug_messages = g_array_new(TRUE, FALSE, sizeof(char *)); - int i; - gboolean no_expect_message = FALSE; - gboolean _out_set_logging; - gboolean test_quick = FALSE; - gboolean test_quick_set = FALSE; - gboolean test_quick_argv = FALSE; - gs_unref_ptrarray GPtrArray *p_tests = NULL; - gs_unref_ptrarray GPtrArray *s_tests = NULL; - - if (!out_set_logging) - out_set_logging = &_out_set_logging; - *out_set_logging = FALSE; - - g_assert(!nmtst_initialized()); - - g_assert(!((!!argc) ^ (!!argv))); - g_assert(!argc || (g_strv_length(*argv) == *argc)); - g_assert(!assert_logging || (!log_level && !log_domains)); - -#ifdef __NETWORKMANAGER_UTILS_H__ - if (!nm_utils_get_testing_initialized()) - _nm_utils_set_testing(_NM_UTILS_TEST_GENERAL); -#endif - - if (argc) - __nmtst_internal.orig_argv = g_strdupv(*argv); - - __nmtst_internal.assert_logging = !!assert_logging; - - nm_g_type_init(); - - is_debug = g_test_verbose(); - - nmtst_debug = g_getenv("NMTST_DEBUG"); - if (nmtst_debug) { - char **d_argv, **i_argv, *nmtst_debug_copy; - - /* By setting then NMTST_DEBUG variable, @is_debug is set automatically. - * This can be reverted with no-debug (on command line or environment variable). */ - is_debug = TRUE; - - nmtst_debug_copy = g_strdup(nmtst_debug); - d_argv = nmtst_str_split(nmtst_debug_copy, ",; \t\r\n"); - - for (i_argv = d_argv; *i_argv; i_argv++) { - const char *debug = *i_argv; - - if (!g_ascii_strcasecmp(debug, "debug")) - is_debug = TRUE; - else if (!g_ascii_strcasecmp(debug, "no-debug")) { - /* when specifying the NMTST_DEBUG variable, we set is_debug to true. Use this flag to disable this - * (e.g. for only setting the log-level, but not is_debug). */ - is_debug = FALSE; - } else if (!g_ascii_strncasecmp(debug, "log-level=", strlen("log-level="))) { - g_free(c_log_level); - log_level = c_log_level = g_strdup(&debug[strlen("log-level=")]); - } else if (!g_ascii_strcasecmp(debug, "D")) { - /* shorthand for "log-level=TRACE,no-expect-message" */ - g_free(c_log_level); - log_level = c_log_level = g_strdup("TRACE"); - no_expect_message = TRUE; - } else if (!g_ascii_strcasecmp(debug, "TRACE")) { - g_free(c_log_level); - log_level = c_log_level = g_strdup("TRACE"); - } else if (!g_ascii_strncasecmp(debug, "log-domains=", strlen("log-domains="))) { - g_free(c_log_domains); - log_domains = c_log_domains = g_strdup(&debug[strlen("log-domains=")]); - } else if (!g_ascii_strncasecmp(debug, "sudo-cmd=", strlen("sudo-cmd="))) { - g_free(sudo_cmd); - sudo_cmd = g_strdup(&debug[strlen("sudo-cmd=")]); - } else if (!g_ascii_strcasecmp(debug, "no-expect-message")) { - no_expect_message = TRUE; - } else if (!g_ascii_strncasecmp(debug, "p=", strlen("p="))) { - if (!p_tests) - p_tests = g_ptr_array_new_with_free_func(g_free); - g_ptr_array_add(p_tests, g_strdup(&debug[strlen("p=")])); - } else if (!g_ascii_strncasecmp(debug, "s=", strlen("s="))) { - if (!s_tests) - s_tests = g_ptr_array_new_with_free_func(g_free); - g_ptr_array_add(s_tests, g_strdup(&debug[strlen("s=")])); - } else if (!g_ascii_strcasecmp(debug, "slow") - || !g_ascii_strcasecmp(debug, "thorough")) { - test_quick = FALSE; - test_quick_set = TRUE; - } else if (!g_ascii_strcasecmp(debug, "quick")) { - test_quick = TRUE; - test_quick_set = TRUE; - } else { - char *msg = - g_strdup_printf(">>> nmtst: ignore unrecognized NMTST_DEBUG option \"%s\"", - debug); - - g_array_append_val(debug_messages, msg); - } - } - - g_free(d_argv); - g_free(nmtst_debug_copy); - } - - if (__nmtst_internal.orig_argv) { - char **a = __nmtst_internal.orig_argv; - - for (; *a; a++) { - if (!g_ascii_strcasecmp(*a, "--debug")) - is_debug = TRUE; - else if (!g_ascii_strcasecmp(*a, "--no-debug")) - is_debug = FALSE; - else if (!strcmp(*a, "-m=slow") || !strcmp(*a, "-m=thorough") || !strcmp(*a, "-m=quick") - || (!strcmp(*a, "-m") && *(a + 1) - && (!strcmp(*(a + 1), "quick") || !strcmp(*(a + 1), "slow") - || !strcmp(*(a + 1), "thorough")))) - test_quick_argv = TRUE; - else if (strcmp(*a, "--tap") == 0) - __nmtst_internal.test_tap_log = TRUE; - } - } - - if (!argc || g_test_initialized()) { - if (p_tests || s_tests) { - char *msg = g_strdup_printf( - ">>> nmtst: ignore -p and -s options for test which calls g_test_init() itself"); - - g_array_append_val(debug_messages, msg); - } - } else { - /* We're intentionally assigning a value to static variables - * s_tests_x and p_tests_x without using it afterwards, just - * so that valgrind doesn't complain about the leak. */ - NM_PRAGMA_WARNING_DISABLE("-Wunused-but-set-variable") - - /* g_test_init() is a variadic function, so we cannot pass it - * (variadic) arguments. If you need to pass additional parameters, - * call nmtst_init() with argc==NULL and call g_test_init() yourself. */ - - /* g_test_init() sets g_log_set_always_fatal() for G_LOG_LEVEL_WARNING - * and G_LOG_LEVEL_CRITICAL. So, beware that the test will fail if you - * have any WARN or ERR log messages -- unless you g_test_expect_message(). */ - GPtrArray * arg_array = g_ptr_array_new(); - gs_free char **arg_array_c = NULL; - int arg_array_n, j; - static char ** s_tests_x, **p_tests_x; - - if (*argc) { - for (i = 0; i < *argc; i++) - g_ptr_array_add(arg_array, (*argv)[i]); - } else - g_ptr_array_add(arg_array, "./test"); - - if (test_quick_set && !test_quick_argv) - g_ptr_array_add(arg_array, "-m=quick"); - - if (!__nmtst_internal.test_tap_log) { - for (i = 0; p_tests && i < p_tests->len; i++) { - g_ptr_array_add(arg_array, "-p"); - g_ptr_array_add(arg_array, p_tests->pdata[i]); - } - for (i = 0; s_tests && i < s_tests->len; i++) { - g_ptr_array_add(arg_array, "-s"); - g_ptr_array_add(arg_array, s_tests->pdata[i]); - } - } else if (p_tests || s_tests) { - char *msg = g_strdup_printf(">>> nmtst: ignore -p and -s options for tap-tests"); - - g_array_append_val(debug_messages, msg); - } - - g_ptr_array_add(arg_array, NULL); - - arg_array_n = arg_array->len - 1; - arg_array_c = (char **) g_ptr_array_free(arg_array, FALSE); - - g_test_init(&arg_array_n, &arg_array_c, NULL); - - if (*argc > 1) { - /* collaps argc/argv by removing the arguments detected - * by g_test_init(). */ - for (i = 1, j = 1; i < *argc; i++) { - if ((*argv)[i] == arg_array_c[j]) - j++; - else - (*argv)[i] = NULL; - } - for (i = 1, j = 1; i < *argc; i++) { - if ((*argv)[i]) { - (*argv)[j++] = (*argv)[i]; - if (i >= j) - (*argv)[i] = NULL; - } - } - *argc = j; - } - - /* we must "leak" the test paths because they are not cloned by g_test_init(). */ - if (!__nmtst_internal.test_tap_log) { - if (p_tests) { - p_tests_x = (char **) g_ptr_array_free(p_tests, FALSE); - p_tests = NULL; - } - if (s_tests) { - s_tests_x = (char **) g_ptr_array_free(s_tests, FALSE); - s_tests = NULL; - } - } - - NM_PRAGMA_WARNING_REENABLE - } - - if (test_quick_set) - __nmtst_internal.test_quick = test_quick; - else if (test_quick_argv) - __nmtst_internal.test_quick = g_test_quick(); - else { -#ifdef NMTST_TEST_QUICK - __nmtst_internal.test_quick = NMTST_TEST_QUICK; -#else - __nmtst_internal.test_quick = FALSE; -#endif - } - - __nmtst_internal.is_debug = is_debug; - __nmtst_internal.rand0 = g_rand_new_with_seed(0); - __nmtst_internal.sudo_cmd = sudo_cmd; - __nmtst_internal.no_expect_message = no_expect_message; - - if (!log_level && log_domains) { - /* if the log level is not specified (but the domain is), we assume - * the caller wants to set it depending on is_debug */ - log_level = is_debug ? "DEBUG" : "WARN"; - } - - if (!__nmtst_internal.assert_logging) { - gboolean success = TRUE; -#ifdef _NMTST_INSIDE_CORE - success = nm_logging_setup(log_level, log_domains, NULL, NULL); - *out_set_logging = TRUE; -#endif - g_assert(success); -#if GLIB_CHECK_VERSION(2, 34, 0) - if (__nmtst_internal.no_expect_message) - g_log_set_always_fatal(G_LOG_FATAL_MASK); -#else - /* g_test_expect_message() is a NOP, so allow any messages */ - g_log_set_always_fatal(G_LOG_FATAL_MASK); -#endif - } else if (__nmtst_internal.no_expect_message) { - /* We have a test that would be assert_logging, but the user specified no_expect_message. - * This transforms g_test_expect_message() into a NOP, but we also have to relax - * g_log_set_always_fatal(), which was set by g_test_init(). */ - g_log_set_always_fatal(G_LOG_FATAL_MASK); -#ifdef _NMTST_INSIDE_CORE - if (c_log_domains || c_log_level) { - /* Normally, tests with assert_logging do not overwrite the logging level/domains because - * the logging statements are part of the assertions. But if the test is run with - * no-expect-message *and* the logging is set explicitly via environment variables, - * we still reset the logging. */ - gboolean success; - - success = nm_logging_setup(log_level, log_domains, NULL, NULL); - *out_set_logging = TRUE; - g_assert(success); - } -#endif - } else { -#if GLIB_CHECK_VERSION(2, 34, 0) - /* We were called not to set logging levels. This means, that the user - * expects to assert against (all) messages. - * Any uncaught message on >debug level is fatal. */ - g_log_set_always_fatal(G_LOG_LEVEL_MASK & ~G_LOG_LEVEL_DEBUG); -#else - /* g_test_expect_message() is a NOP, so allow any messages */ - g_log_set_always_fatal(G_LOG_FATAL_MASK); -#endif - } - - if ((!__nmtst_internal.assert_logging - || (__nmtst_internal.assert_logging && __nmtst_internal.no_expect_message)) - && (is_debug - || (c_log_level - && (!g_ascii_strcasecmp(c_log_level, "DEBUG") - || !g_ascii_strcasecmp(c_log_level, "TRACE")))) - && !g_getenv("G_MESSAGES_DEBUG")) { - /* if we are @is_debug or @log_level=="DEBUG" and - * G_MESSAGES_DEBUG is unset, we set G_MESSAGES_DEBUG=all. - * To disable this default behaviour, set G_MESSAGES_DEBUG='' */ - - /* Note that g_setenv is not thread safe, but you should anyway call - * nmtst_init() at the very start. */ - g_setenv("G_MESSAGES_DEBUG", "all", TRUE); - } - - /* "tc" is in /sbin, which might not be in $PATH of a regular user. Unconditionally - * add "/bin" and "/sbin" to $PATH for all tests. */ - { - static char *path_new; - const char * path_old; - - g_assert(!path_new); - - path_old = g_getenv("PATH"); - path_new = g_strjoin("", - path_old ?: "", - (nm_str_is_empty(path_old) ? "" : ":"), - "/bin:/sbin", - NULL); - g_setenv("PATH", path_new, TRUE); - } - - /* Delay messages until we setup logging. */ - for (i = 0; i < debug_messages->len; i++) - __NMTST_LOG(g_message, "%s", g_array_index(debug_messages, const char *, i)); - - g_strfreev((char **) g_array_free(debug_messages, FALSE)); - g_free(c_log_level); - g_free(c_log_domains); - -#ifdef __NETWORKMANAGER_UTILS_H__ - /* ensure that monotonic timestamp is called (because it initially logs a line) */ - nm_utils_get_monotonic_timestamp_sec(); -#endif - -#ifdef NM_UTILS_H - { - gs_free_error GError *error = NULL; - - if (!nm_utils_init(&error)) - g_assert_not_reached(); - g_assert_no_error(error); - } -#endif - - g_log_set_handler(G_LOG_DOMAIN, - G_LOG_LEVEL_MASK | G_LOG_FLAG_FATAL | G_LOG_FLAG_RECURSION, - _nmtst_log_handler, - NULL); -} - -#ifndef _NMTST_INSIDE_CORE -static inline void -nmtst_init(int *argc, char ***argv, gboolean assert_logging) -{ - __nmtst_init(argc, argv, assert_logging, NULL, NULL, NULL); -} -#endif - -static inline gboolean -nmtst_is_debug(void) -{ - g_assert(nmtst_initialized()); - return __nmtst_internal.is_debug; -} - -static inline gboolean -nmtst_test_quick(void) -{ - g_assert(nmtst_initialized()); - return __nmtst_internal.test_quick; -} - -#if GLIB_CHECK_VERSION(2, 34, 0) - #undef g_test_expect_message - #define g_test_expect_message(...) \ - G_STMT_START \ - { \ - g_assert(nmtst_initialized()); \ - if (__nmtst_internal.assert_logging && __nmtst_internal.no_expect_message) { \ - g_debug("nmtst: assert-logging: g_test_expect_message %s", \ - G_STRINGIFY((__VA_ARGS__))); \ - } else { \ - G_GNUC_BEGIN_IGNORE_DEPRECATIONS \ - g_test_expect_message(__VA_ARGS__); \ - G_GNUC_END_IGNORE_DEPRECATIONS \ - } \ - } \ - G_STMT_END - #undef g_test_assert_expected_messages_internal - #define g_test_assert_expected_messages_internal(domain, file, line, func) \ - G_STMT_START \ - { \ - const char *_domain = (domain); \ - const char *_file = (file); \ - const char *_func = (func); \ - int _line = (line); \ - \ - if (__nmtst_internal.assert_logging && __nmtst_internal.no_expect_message) \ - g_debug("nmtst: assert-logging: g_test_assert_expected_messages(%s, %s:%d, %s)", \ - _domain ?: "", \ - _file ?: "", \ - _line, \ - _func ?: ""); \ - \ - G_GNUC_BEGIN_IGNORE_DEPRECATIONS \ - g_test_assert_expected_messages_internal(_domain, _file, _line, _func); \ - G_GNUC_END_IGNORE_DEPRECATIONS \ - } \ - G_STMT_END -#endif - -#define NMTST_EXPECT(domain, level, msg) g_test_expect_message(domain, level, msg) - -#define NMTST_EXPECT_LIBNM(level, msg) NMTST_EXPECT("libnm", level, msg) - -#define NMTST_EXPECT_LIBNM_WARNING(msg) NMTST_EXPECT_LIBNM(G_LOG_LEVEL_WARNING, msg) -#define NMTST_EXPECT_LIBNM_CRITICAL(msg) NMTST_EXPECT_LIBNM(G_LOG_LEVEL_CRITICAL, msg) - -/*****************************************************************************/ - -typedef struct _NmtstTestData NmtstTestData; - -typedef void (*NmtstTestHandler)(const NmtstTestData *test_data); - -struct _NmtstTestData { - union { - const char *testpath; - char * _testpath; - }; - gsize n_args; - gpointer * args; - NmtstTestHandler _func_setup; - GTestDataFunc _func_test; - NmtstTestHandler _func_teardown; -}; - -static inline void -_nmtst_test_data_unpack(const NmtstTestData *test_data, gsize n_args, ...) -{ - gsize i; - va_list ap; - gpointer *p; - - g_assert(test_data); - g_assert_cmpint(n_args, ==, test_data->n_args); - - va_start(ap, n_args); - for (i = 0; i < n_args; i++) { - p = va_arg(ap, gpointer *); - - if (p) - *p = test_data->args[i]; - } - va_end(ap); -} -#define nmtst_test_data_unpack(test_data, ...) \ - _nmtst_test_data_unpack(test_data, NM_NARG(__VA_ARGS__), ##__VA_ARGS__) - -static inline void -_nmtst_test_data_free(gpointer data) -{ - NmtstTestData *test_data = data; - - g_assert(test_data); - - g_free(test_data->_testpath); - g_free(test_data); -} - -static inline void -_nmtst_test_run(gconstpointer data) -{ - const NmtstTestData *test_data = data; - - if (test_data->_func_setup) - test_data->_func_setup(test_data); - - test_data->_func_test(test_data); - - if (test_data->_func_teardown) - test_data->_func_teardown(test_data); -} - -static inline void -_nmtst_add_test_func_full(const char * testpath, - GTestDataFunc func_test, - NmtstTestHandler func_setup, - NmtstTestHandler func_teardown, - gsize n_args, - ...) -{ - gsize i; - NmtstTestData *data; - va_list ap; - - g_assert(testpath && testpath[0]); - g_assert(func_test); - - data = g_malloc0(sizeof(NmtstTestData) + (sizeof(gpointer) * (n_args + 1))); - - data->_testpath = g_strdup(testpath); - data->_func_test = func_test; - data->_func_setup = func_setup; - data->_func_teardown = func_teardown; - data->n_args = n_args; - data->args = (gpointer) &data[1]; - va_start(ap, n_args); - for (i = 0; i < n_args; i++) - data->args[i] = va_arg(ap, gpointer); - data->args[i] = NULL; - va_end(ap); - - g_test_add_data_func_full(testpath, data, _nmtst_test_run, _nmtst_test_data_free); -} -#define nmtst_add_test_func_full(testpath, func_test, func_setup, func_teardown, ...) \ - _nmtst_add_test_func_full(testpath, \ - func_test, \ - func_setup, \ - func_teardown, \ - NM_NARG(__VA_ARGS__), \ - ##__VA_ARGS__) -#define nmtst_add_test_func(testpath, func_test, ...) \ - nmtst_add_test_func_full(testpath, func_test, NULL, NULL, ##__VA_ARGS__) - -/*****************************************************************************/ - -static inline GRand * -nmtst_get_rand0(void) -{ - g_assert(nmtst_initialized()); - return __nmtst_internal.rand0; -} - -static inline GRand * -nmtst_get_rand(void) -{ - g_assert(nmtst_initialized()); - - if (G_UNLIKELY(!__nmtst_internal.rand)) { - guint32 seed; - const char *str = g_getenv("NMTST_SEED_RAND"); - - if (!str) { - /* No NMTST_SEED_RAND. Pick a stable one. */ - seed = 0; - __nmtst_internal.rand = g_rand_new_with_seed(seed); - } else if (str[0] == '\0') { - /* NMTST_SEED_RAND is set but empty. Pick a random one. */ - __nmtst_internal.rand = g_rand_new(); - - seed = g_rand_int(__nmtst_internal.rand); - g_rand_set_seed(__nmtst_internal.rand, seed); - } else { - /* NMTST_SEED_RAND is set. Use it as a seed. */ - char * s; - gint64 i; - - i = g_ascii_strtoll(str, &s, 0); - g_assert(s[0] == '\0' && i >= 0 && i < G_MAXUINT32); - - seed = i; - __nmtst_internal.rand = g_rand_new_with_seed(seed); - } - __nmtst_internal.rand_seed = seed; - - g_print("\nnmtst: initialize nmtst_get_rand() with NMTST_SEED_RAND=%u\n", seed); - } - return __nmtst_internal.rand; -} - -static inline guint32 -nmtst_get_rand_uint32(void) -{ - return g_rand_int(nmtst_get_rand()); -} - -static inline guint64 -nmtst_get_rand_uint64(void) -{ - GRand *rand = nmtst_get_rand(); - - return (((guint64) g_rand_int(rand))) | (((guint64) g_rand_int(rand)) << 32); -} - -static inline guint -nmtst_get_rand_uint(void) -{ - G_STATIC_ASSERT_EXPR((sizeof(guint) == sizeof(guint32) || (sizeof(guint) == sizeof(guint64)))); - if (sizeof(guint32) == sizeof(guint)) - return nmtst_get_rand_uint32(); - return nmtst_get_rand_uint64(); -} - -static inline gsize -nmtst_get_rand_size(void) -{ - G_STATIC_ASSERT_EXPR((sizeof(gsize) == sizeof(guint32) || (sizeof(gsize) == sizeof(guint64)))); - if (sizeof(gsize) == sizeof(guint32)) - return nmtst_get_rand_uint32(); - return nmtst_get_rand_uint64(); -} - -static inline gboolean -nmtst_get_rand_bool(void) -{ - return nmtst_get_rand_uint32() % 2; -} - -static inline gboolean -nmtst_get_rand_one_case_in(guint32 num) -{ - /* num=1 doesn't make much sense, because it will always return %TRUE. - * Still accept it, it might be that @num is calculated, so 1 might be - * a valid edge case. */ - g_assert(num > 0); - - return (nmtst_get_rand_uint32() % num) == 0; -} - -static inline gpointer -nmtst_rand_buf(GRand *rand, gpointer buffer, gsize buffer_length) -{ - guint32 v; - guint8 *b = buffer; - - if (!buffer_length) - return buffer; - - g_assert(buffer); - - if (!rand) - rand = nmtst_get_rand(); - - for (; buffer_length >= sizeof(guint32); - buffer_length -= sizeof(guint32), b += sizeof(guint32)) { - v = g_rand_int(rand); - memcpy(b, &v, sizeof(guint32)); - } - if (buffer_length > 0) { - v = g_rand_int(rand); - do { - *(b++) = v & 0xFF; - v >>= 8; - } while (--buffer_length > 0); - } - return buffer; -} - -#define _nmtst_rand_select(uniq, v0, ...) \ - ({ \ - typeof(v0) NM_UNIQ_T(UNIQ, uniq)[1 + NM_NARG(__VA_ARGS__)] = {(v0), __VA_ARGS__}; \ - \ - NM_UNIQ_T(UNIQ, uniq)[nmtst_get_rand_uint32() % G_N_ELEMENTS(NM_UNIQ_T(UNIQ, uniq))]; \ - }) - -#define nmtst_rand_select(...) _nmtst_rand_select(NM_UNIQ, __VA_ARGS__) - -static inline void * -nmtst_rand_perm(GRand *rand, void *dst, const void *src, gsize elmt_size, gsize n_elmt) -{ - gsize i, j; - char *p_, *pj; - char *bu; - - g_assert(dst); - g_assert(elmt_size > 0); - g_assert(n_elmt < G_MAXINT32); - - if (n_elmt == 0) - return dst; - - if (src && dst != src) - memcpy(dst, src, elmt_size * n_elmt); - - if (!rand) - rand = nmtst_get_rand(); - - bu = g_slice_alloc(elmt_size); - - p_ = dst; - for (i = n_elmt; i > 1; i--) { - j = g_rand_int_range(rand, 0, i); - - if (j != 0) { - pj = &p_[j * elmt_size]; - - /* swap */ - memcpy(bu, p_, elmt_size); - memcpy(p_, pj, elmt_size); - memcpy(pj, bu, elmt_size); - } - p_ += elmt_size; - } - - g_slice_free1(elmt_size, bu); - return dst; -} - -static inline const char ** -nmtst_rand_perm_strv(const char *const *strv) -{ - const char **res; - gsize n; - - if (!strv) - return NULL; - - /* this returns a (scrambled) SHALLOW copy of the strv array! */ - - n = NM_PTRARRAY_LEN(strv); - res = (const char **) (nm_utils_strv_dup(strv, n, FALSE) ?: g_new0(char *, 1)); - nmtst_rand_perm(NULL, res, res, sizeof(char *), n); - return res; -} - -static inline GSList * -nmtst_rand_perm_gslist(GRand *rand, GSList *list) -{ - GSList *result; - guint l; - - if (!rand) - rand = nmtst_get_rand(); - - /* no need for an efficient implementation :) */ - - result = 0; - for (l = g_slist_length(list); l > 0; l--) { - GSList *tmp; - - tmp = g_slist_nth(list, g_rand_int(rand) % l); - g_assert(tmp); - - list = g_slist_remove_link(list, tmp); - result = g_slist_concat(tmp, result); - } - g_assert(!list); - return result; -} - -static inline void -nmtst_stable_rand(guint64 seed, gpointer buf, gsize len) -{ - const guint64 C = 1442695040888963407llu; - const guint64 A = 6364136223846793005llu; - guint8 * b; - union { - guint8 a[sizeof(guint64)]; - guint64 n; - } n; - - /* We want a stable random generator that is in our control and does not - * depend on glibc/glib versions. - * Use a linear congruential generator (x[n+1] = (A * x[n] + C) % M) - * https://en.wikipedia.org/wiki/Linear_congruential_generator - * - * We choose (Knuth’s LCG MMIX) - * A = 6364136223846793005llu - * C = 1442695040888963407llu - * M = 2^64 - */ - - g_assert(len == 0 || buf); - - n.n = seed; - b = buf; - for (; len > 0; len--, b++) { - n.n = (A * n.n + C); - - /* let's combine the 64 bits randomness in one byte. By xor-ing, it's - * also independent of endianness. */ - b[0] = n.a[0] ^ n.a[1] ^ n.a[2] ^ n.a[3] ^ n.a[4] ^ n.a[5] ^ n.a[6] ^ n.a[7]; - } -} - -/*****************************************************************************/ - -/** - * nmtst_get_rand_word_length: - * @rand: (allow-none): #GRand instance or %NULL to use the singleton. - * - * Returns: a random integer >= 0, that most frequently is somewhere between - * 0 and 16, but (with decreasing) probability, it can be larger. This can - * be used when we generate random input for unit tests. - */ -static inline guint -nmtst_get_rand_word_length(GRand *rand) -{ - guint n; - - if (!rand) - rand = nmtst_get_rand(); - - n = 0; - while (TRUE) { - guint32 rnd = g_rand_int(rand); - guint probability; - - /* The following python code implements a random sample with this - * distribution: - * - * def random_histogram(n_tries, scale = None): - * def probability(n_tok): - * import math - * return max(2, math.floor(100 / (2*(n_tok+1)))) - * def n_tokens(): - * import random - * n_tok = 0 - * while True: - * if random.randint(0, 0xFFFFFFFF) % probability(n_tok) == 0: - * return n_tok - * n_tok += 1 - * hist = [] - * i = 0; - * while i < n_tries: - * n_tok = n_tokens() - * while n_tok >= len(hist): - * hist.append(0) - * hist[n_tok] = hist[n_tok] + 1 - * i += 1 - * if scale is not None: - * hist = list([round(x / n_tries * scale) for x in hist]) - * return hist - * - * For example, random_histogram(n_tries = 1000000, scale = 1000) may give - * - * IDX: [ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29] - * SEEN: [20, 39, 59, 73, 80, 91, 92, 90, 91, 73, 73, 54, 55, 36, 24, 16, 16, 8, 4, 2, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0] - * - * which give a sense of the probability with this individual results are returned. - */ - probability = NM_MAX(2u, (100u / (2u * (n + 1u)))); - if ((rnd % probability) == 0) - return n; - n++; - } -} - -/*****************************************************************************/ - -static inline gboolean -nmtst_g_source_assert_not_called(gpointer user_data) -{ - g_assert_not_reached(); - return G_SOURCE_CONTINUE; -} - -static inline gboolean -nmtst_g_source_nop(gpointer user_data) -{ - g_assert(!user_data); - return G_SOURCE_CONTINUE; -} - -static inline gboolean -nmtst_g_source_set_boolean_true(gpointer user_data) -{ - gboolean *ptr = user_data; - - g_assert(ptr); - g_assert(!*ptr); - *ptr = TRUE; - return G_SOURCE_CONTINUE; -} - -/*****************************************************************************/ - -static inline gboolean -_nmtst_main_loop_run_timeout(gpointer user_data) -{ - GMainLoop **p_loop = user_data; - - g_assert(p_loop && *p_loop); - g_main_loop_quit(g_steal_pointer(p_loop)); - return G_SOURCE_REMOVE; -} - -static inline gboolean -nmtst_main_loop_run(GMainLoop *loop, guint timeout_msec) -{ - nm_auto_unref_gsource GSource *source = NULL; - GMainLoop * loopx = loop; - - if (timeout_msec > 0) { - source = g_timeout_source_new(timeout_msec); - g_source_set_callback(source, _nmtst_main_loop_run_timeout, &loopx, NULL); - g_source_attach(source, g_main_loop_get_context(loop)); - } - - g_main_loop_run(loop); - - if (source) - g_source_destroy(source); - - /* if the timeout was reached, return FALSE. */ - return loopx != NULL; -} - -#define nmtst_main_loop_run_assert(loop, timeout_msec) \ - G_STMT_START \ - { \ - if (!nmtst_main_loop_run((loop), (timeout_msec))) \ - g_assert_not_reached(); \ - } \ - G_STMT_END - -static inline void -_nmtst_main_loop_quit_on_notify(GObject *object, GParamSpec *pspec, gpointer user_data) -{ - GMainLoop *loop = user_data; - - g_assert(G_IS_OBJECT(object)); - g_assert(loop); - - g_main_loop_quit(loop); -} -#define nmtst_main_loop_quit_on_notify ((GCallback) _nmtst_main_loop_quit_on_notify) - -#define nmtst_main_context_iterate_until_full(context, timeout_msec, poll_msec, condition) \ - ({ \ - nm_auto_destroy_and_unref_gsource GSource *_source_timeout = NULL; \ - nm_auto_destroy_and_unref_gsource GSource *_source_poll = NULL; \ - GMainContext * _context = (context); \ - gboolean _had_timeout = FALSE; \ - typeof(timeout_msec) _timeout_msec0 = (timeout_msec); \ - typeof(poll_msec) _poll_msec0 = (poll_msec); \ - gint64 _timeout_msec = _timeout_msec0; \ - guint _poll_msec = _poll_msec0; \ - \ - g_assert_cmpint(_timeout_msec0, ==, _timeout_msec); \ - g_assert_cmpint(_poll_msec0, ==, _poll_msec); \ - \ - _source_timeout = g_timeout_source_new(NM_CLAMP(_timeout_msec, 0, (gint64) G_MAXUINT)); \ - g_source_set_callback(_source_timeout, \ - nmtst_g_source_set_boolean_true, \ - &_had_timeout, \ - NULL); \ - g_source_attach(_source_timeout, _context); \ - \ - if (_poll_msec > 0) { \ - _source_poll = g_timeout_source_new(_poll_msec); \ - g_source_set_callback(_source_poll, nmtst_g_source_nop, NULL, NULL); \ - g_source_attach(_source_poll, _context); \ - } \ - \ - while (TRUE) { \ - if (condition) \ - break; \ - g_main_context_iteration(_context, TRUE); \ - if (_had_timeout) \ - break; \ - } \ - \ - !_had_timeout; \ - }) - -#define nmtst_main_context_iterate_until(context, timeout_msec, condition) \ - nmtst_main_context_iterate_until_full((context), (timeout_msec), 0, condition) - -#define nmtst_main_context_iterate_until_assert_full(context, timeout_msec, poll_msec, condition) \ - G_STMT_START \ - { \ - if (!nmtst_main_context_iterate_until_full((context), \ - (timeout_msec), \ - (poll_msec), \ - condition)) \ - g_assert(FALSE &&#condition); \ - } \ - G_STMT_END - -#define nmtst_main_context_iterate_until_assert(context, timeout_msec, condition) \ - nmtst_main_context_iterate_until_assert_full((context), (timeout_msec), 0, condition) - -/*****************************************************************************/ - -static inline void -nmtst_main_context_assert_no_dispatch(GMainContext *context, guint timeout_msec) -{ - nm_auto_destroy_and_unref_gsource GSource *source = NULL; - gboolean timeout_hit = FALSE; - - source = g_timeout_source_new(timeout_msec); - g_source_set_callback(source, nmtst_g_source_set_boolean_true, &timeout_hit, NULL); - g_source_attach(source, context); - - while (g_main_context_iteration(context, TRUE)) { - if (timeout_hit) - return; - g_assert_not_reached(); - } -} - -/*****************************************************************************/ - -typedef struct { - GMainLoop *_main_loop; - union { - GSList * _list; - const void *const is_waiting; - }; -} NMTstContextBusyWatcherData; - -static inline void -_nmtst_context_busy_watcher_add_cb(gpointer data, GObject *where_the_object_was) -{ - NMTstContextBusyWatcherData *watcher_data = data; - GSList * l; - - g_assert(watcher_data); - - l = g_slist_find(watcher_data->_list, where_the_object_was); - g_assert(l); - - watcher_data->_list = g_slist_delete_link(watcher_data->_list, l); - if (!watcher_data->_list) - g_main_loop_quit(watcher_data->_main_loop); -} - -static inline void -nmtst_context_busy_watcher_add(NMTstContextBusyWatcherData *watcher_data, GObject *object) -{ - g_assert(watcher_data); - g_assert(G_IS_OBJECT(object)); - - if (!watcher_data->_main_loop) { - watcher_data->_main_loop = g_main_loop_new(g_main_context_get_thread_default(), FALSE); - g_assert(!watcher_data->_list); - } else { - g_assert(g_main_loop_get_context(watcher_data->_main_loop) - == (g_main_context_get_thread_default() ?: g_main_context_default())); - } - - g_object_weak_ref(object, _nmtst_context_busy_watcher_add_cb, watcher_data); - watcher_data->_list = g_slist_prepend(watcher_data->_list, object); -} - -static inline void -nmtst_context_busy_watcher_wait(NMTstContextBusyWatcherData *watcher_data) -{ - g_assert(watcher_data); - - if (!watcher_data->_main_loop) { - g_assert(!watcher_data->_list); - return; - } - - if (watcher_data->_list) { - if (!nmtst_main_loop_run(watcher_data->_main_loop, 5000)) - g_error("timeout running mainloop waiting for GObject to destruct"); - } - - g_assert(!watcher_data->_list); - nm_clear_pointer(&watcher_data->_main_loop, g_main_loop_unref); -} - -/*****************************************************************************/ - -static inline const char * -nmtst_get_sudo_cmd(void) -{ - g_assert(nmtst_initialized()); - return __nmtst_internal.sudo_cmd; -} - -static inline void -nmtst_reexec_sudo(void) -{ - char * str; - char **argv; - int i; - int errsv; - - g_assert(nmtst_initialized()); - g_assert(__nmtst_internal.orig_argv); - - if (!__nmtst_internal.sudo_cmd) - return; - - str = g_strjoinv(" ", __nmtst_internal.orig_argv); - __NMTST_LOG(g_message, ">> exec %s %s", __nmtst_internal.sudo_cmd, str); - - argv = g_new0(char *, 1 + g_strv_length(__nmtst_internal.orig_argv) + 1); - argv[0] = __nmtst_internal.sudo_cmd; - for (i = 0; __nmtst_internal.orig_argv[i]; i++) - argv[i + 1] = __nmtst_internal.orig_argv[i]; - - execvp(__nmtst_internal.sudo_cmd, argv); - - errsv = errno; - g_error(">> exec %s failed: %d - %s", - __nmtst_internal.sudo_cmd, - errsv, - nm_strerror_native(errsv)); -} - -/*****************************************************************************/ - -static inline gsize -nmtst_find_all_indexes(gpointer *elements, - gsize n_elements, - gpointer *needles, - gsize n_needles, - gboolean (*equal_fcn)(gpointer element, gpointer needle, gpointer user_data), - gpointer user_data, - gssize * out_idx) -{ - gsize i, j, k; - gsize found = 0; - - for (i = 0; i < n_needles; i++) { - gssize idx = -1; - - for (j = 0; j < n_elements; j++) { - /* no duplicates */ - for (k = 0; k < i; k++) { - if (out_idx[k] == j) - goto next; - } - - if (equal_fcn(elements[j], needles[i], user_data)) { - idx = j; - break; - } -next:; - } - - out_idx[i] = idx; - if (idx >= 0) - found++; - } - - return found; -} - -/*****************************************************************************/ - -#define __define_nmtst_static(NUM, SIZE) \ - static inline const char *nmtst_static_##SIZE##_##NUM(const char *str) \ - { \ - gsize l; \ - static _nm_thread_local char buf[SIZE]; \ - \ - if (!str) \ - return NULL; \ - l = g_strlcpy(buf, str, sizeof(buf)); \ - g_assert(l < sizeof(buf)); \ - return buf; \ - } -__define_nmtst_static(01, 1024) __define_nmtst_static(02, 1024) __define_nmtst_static(03, 1024) -#undef __define_nmtst_static - -#if defined(__NM_UTILS_H__) || defined(NM_UTILS_H) - - #define NMTST_UUID_INIT(uuid) \ - gs_free char * _nmtst_hidden_##uuid = nm_utils_uuid_generate(); \ - const char *const uuid = _nmtst_hidden_##uuid - - static inline const char *nmtst_uuid_generate(void) -{ - static _nm_thread_local char u[37]; - gs_free char * m = NULL; - - m = nm_utils_uuid_generate(); - g_assert(m && strlen(m) == sizeof(u) - 1); - memcpy(u, m, sizeof(u)); - return u; -} - -#endif - -#define nmtst_assert_str_has_substr(str, substr) \ - G_STMT_START \ - { \ - const char *__str = (str); \ - const char *__substr = (substr); \ - \ - g_assert(__str); \ - g_assert(__substr); \ - if (strstr(__str, __substr) == NULL) \ - g_error("%s:%d: Expects \"%s\" but got \"%s\"", __FILE__, __LINE__, __substr, __str); \ - } \ - G_STMT_END - -static inline in_addr_t -nmtst_inet4_from_string(const char *str) -{ - in_addr_t addr; - int success; - - if (!str) - return 0; - - success = inet_pton(AF_INET, str, &addr); - - g_assert(success == 1); - - return addr; -} - -static inline const struct in6_addr * -nmtst_inet6_from_string(const char *str) -{ - static _nm_thread_local struct in6_addr addr; - int success; - - if (!str) - addr = in6addr_any; - else { - success = inet_pton(AF_INET6, str, &addr); - g_assert(success == 1); - } - - return &addr; -} - -static inline gconstpointer -nmtst_inet_from_string(int addr_family, const char *str) -{ - if (addr_family == AF_INET) { - static in_addr_t a; - - a = nmtst_inet4_from_string(str); - return &a; - } - if (addr_family == AF_INET6) - return nmtst_inet6_from_string(str); - - g_assert_not_reached(); - return NULL; -} - -static inline const char * -nmtst_inet_to_string(int addr_family, gconstpointer addr) -{ - static _nm_thread_local char buf[NM_CONST_MAX(INET6_ADDRSTRLEN, INET_ADDRSTRLEN)]; - - g_assert(NM_IN_SET(addr_family, AF_INET, AF_INET6)); - g_assert(addr); - - if (inet_ntop(addr_family, addr, buf, sizeof(buf)) != buf) - g_assert_not_reached(); - - return buf; -} - -static inline const char * -nmtst_inet4_to_string(in_addr_t addr) -{ - return nmtst_inet_to_string(AF_INET, &addr); -} - -static inline const char * -nmtst_inet6_to_string(const struct in6_addr *addr) -{ - return nmtst_inet_to_string(AF_INET6, addr); -} - -static inline void -_nmtst_assert_ip4_address(const char *file, int line, in_addr_t addr, const char *str_expected) -{ - if (nmtst_inet4_from_string(str_expected) != addr) { - char buf[100]; - - g_error("%s:%d: Unexpected IPv4 address: expected %s, got %s", - file, - line, - str_expected ?: "0.0.0.0", - inet_ntop(AF_INET, &addr, buf, sizeof(buf))); - } -} -#define nmtst_assert_ip4_address(addr, str_expected) \ - _nmtst_assert_ip4_address(__FILE__, __LINE__, addr, str_expected) - -static inline void -_nmtst_assert_ip6_address(const char * file, - int line, - const struct in6_addr *addr, - const char * str_expected) -{ - struct in6_addr any = in6addr_any; - - if (!addr) - addr = &any; - - if (memcmp(nmtst_inet6_from_string(str_expected), addr, sizeof(*addr)) != 0) { - char buf[100]; - - g_error("%s:%d: Unexpected IPv6 address: expected %s, got %s", - file, - line, - str_expected ?: "::", - inet_ntop(AF_INET6, addr, buf, sizeof(buf))); - } -} -#define nmtst_assert_ip6_address(addr, str_expected) \ - _nmtst_assert_ip6_address(__FILE__, __LINE__, addr, str_expected) - -#define nmtst_assert_ip_address(addr_family, addr, str_expected) \ - G_STMT_START \ - { \ - if (NM_IS_IPv4(addr_family)) \ - nmtst_assert_ip4_address(*((const in_addr_t *) (addr)), (str_expected)); \ - else \ - nmtst_assert_ip6_address((const struct in6_addr *) (addr), (str_expected)); \ - } \ - G_STMT_END - -#define nmtst_spawn_sync(working_directory, standard_out, standard_err, assert_exit_status, ...) \ - __nmtst_spawn_sync(working_directory, \ - standard_out, \ - standard_err, \ - assert_exit_status, \ - ##__VA_ARGS__, \ - NULL) -static inline int __nmtst_spawn_sync(const char *working_directory, - char ** standard_out, - char ** standard_err, - int assert_exit_status, - ...) G_GNUC_NULL_TERMINATED; -static inline int -__nmtst_spawn_sync(const char *working_directory, - char ** standard_out, - char ** standard_err, - int assert_exit_status, - ...) -{ - int exit_status = 0; - GError * error = NULL; - char * arg; - va_list va_args; - GPtrArray *argv = g_ptr_array_new(); - gboolean success; - - va_start(va_args, assert_exit_status); - while ((arg = va_arg(va_args, char *))) - g_ptr_array_add(argv, arg); - va_end(va_args); - - g_assert(argv->len >= 1); - g_ptr_array_add(argv, NULL); - - success = g_spawn_sync(working_directory, - (char **) argv->pdata, - NULL, - 0 /*G_SPAWN_DEFAULT*/, - NULL, - NULL, - standard_out, - standard_err, - &exit_status, - &error); - if (!success) - g_error("nmtst_spawn_sync(%s): %s", ((char **) argv->pdata)[0], error->message); - g_assert(!error); - - g_assert(!standard_out || *standard_out); - g_assert(!standard_err || *standard_err); - - if (assert_exit_status != -1) { - /* exit status is a guint8 on success. Set @assert_exit_status to -1 - * not to check for the exit status. */ - g_assert(WIFEXITED(exit_status)); - g_assert_cmpint(WEXITSTATUS(exit_status), ==, assert_exit_status); - } - - g_ptr_array_free(argv, TRUE); - return exit_status; -} - -/*****************************************************************************/ - -static inline char * -nmtst_file_resolve_relative_path(const char *rel, const char *cwd) -{ - gs_free char *cwd_free = NULL; - - g_assert(rel && *rel); - - if (g_path_is_absolute(rel)) - return g_strdup(rel); - - if (!cwd) - cwd = cwd_free = g_get_current_dir(); - return g_build_filename(cwd, rel, NULL); -} - -static inline char * -nmtst_file_get_contents(const char *filename) -{ - GError * error = NULL; - gboolean success; - char * contents = NULL; - gsize len; - - success = g_file_get_contents(filename, &contents, &len, &error); - nmtst_assert_success(success && contents, error); - g_assert_cmpint(strlen(contents), ==, len); - return contents; -} - -#define nmtst_file_set_contents_size(filename, content, size) \ - G_STMT_START \ - { \ - GError * _error = NULL; \ - gboolean _success; \ - const char *_content = (content); \ - gssize _size = (size); \ - \ - g_assert(_content); \ - \ - if (_size < 0) { \ - g_assert(_size == -1); \ - _size = strlen(_content); \ - } \ - \ - _success = g_file_set_contents((filename), _content, _size, &_error); \ - nmtst_assert_success(_success, _error); \ - } \ - G_STMT_END - -#define nmtst_file_set_contents(filename, content) \ - nmtst_file_set_contents_size(filename, content, -1) - -/*****************************************************************************/ - -static inline void -nmtst_file_unlink_if_exists(const char *name) -{ - int errsv; - - g_assert(name && name[0]); - - if (unlink(name) != 0) { - errsv = errno; - if (errsv != ENOENT) - g_error("nmtst_file_unlink_if_exists(%s): failed with %s", - name, - nm_strerror_native(errsv)); - } -} - -static inline void -nmtst_file_unlink(const char *name) -{ - int errsv; - - g_assert(name && name[0]); - - if (unlink(name) != 0) { - errsv = errno; - g_error("nmtst_file_unlink(%s): failed with %s", name, nm_strerror_native(errsv)); - } -} - -static inline void -_nmtst_auto_unlinkfile(char **p_name) -{ - if (*p_name) { - nmtst_file_unlink(*p_name); - nm_clear_g_free(p_name); - } -} - -#define nmtst_auto_unlinkfile nm_auto(_nmtst_auto_unlinkfile) - -/*****************************************************************************/ - -static inline void -_nmtst_assert_resolve_relative_path_equals(const char *f1, - const char *f2, - const char *file, - int line) -{ - gs_free char *p1 = NULL, *p2 = NULL; - - p1 = nmtst_file_resolve_relative_path(f1, NULL); - p2 = nmtst_file_resolve_relative_path(f2, NULL); - g_assert(p1 && *p1); - - /* Fixme: later we might need to coalesce repeated '/', "./", and "../". - * For now, it's good enough. */ - if (g_strcmp0(p1, p2) != 0) - g_error("%s:%d : filenames don't match \"%s\" vs. \"%s\" // \"%s\" - \"%s\"", - file, - line, - f1, - f2, - p1, - p2); -} -#define nmtst_assert_resolve_relative_path_equals(f1, f2) \ - _nmtst_assert_resolve_relative_path_equals(f1, f2, __FILE__, __LINE__); - -/*****************************************************************************/ - -#ifdef __NETWORKMANAGER_LOGGING_H__ -static inline gpointer -nmtst_logging_disable(gboolean always) -{ - gpointer p; - - g_assert(nmtst_initialized()); - if (!always && __nmtst_internal.no_expect_message) { - /* The caller does not want to @always suppress logging. Instead, - * the caller wants to suppress unexpected log messages that would - * fail assertions (since we possibly assert against all unexpected - * log messages). - * - * If the test is run with no-expect-message, then don't suppress - * the loggings, because they also wouldn't fail assertions. */ - return NULL; - } - - p = g_memdup(_nm_logging_enabled_state, sizeof(_nm_logging_enabled_state)); - memset(_nm_logging_enabled_state, 0, sizeof(_nm_logging_enabled_state)); - return p; -} - -static inline void -nmtst_logging_reenable(gpointer old_state) -{ - g_assert(nmtst_initialized()); - if (old_state) { - memcpy(_nm_logging_enabled_state, old_state, sizeof(_nm_logging_enabled_state)); - g_free(old_state); - } -} -#endif - -/*****************************************************************************/ - -#ifdef NM_SETTING_IP_CONFIG_H -static inline void -nmtst_setting_ip_config_add_address(NMSettingIPConfig *s_ip, const char *address, guint prefix) -{ - NMIPAddress *addr; - int family; - - g_assert(s_ip); - - if (nm_utils_ipaddr_is_valid(AF_INET, address)) - family = AF_INET; - else if (nm_utils_ipaddr_is_valid(AF_INET6, address)) - family = AF_INET6; - else - g_assert_not_reached(); - - addr = nm_ip_address_new(family, address, prefix, NULL); - g_assert(addr); - g_assert(nm_setting_ip_config_add_address(s_ip, addr)); - nm_ip_address_unref(addr); -} - -static inline void -nmtst_setting_ip_config_add_route(NMSettingIPConfig *s_ip, - const char * dest, - guint prefix, - const char * next_hop, - gint64 metric) -{ - NMIPRoute *route; - int family; - - g_assert(s_ip); - - if (nm_utils_ipaddr_is_valid(AF_INET, dest)) - family = AF_INET; - else if (nm_utils_ipaddr_is_valid(AF_INET6, dest)) - family = AF_INET6; - else - g_assert_not_reached(); - - route = nm_ip_route_new(family, dest, prefix, next_hop, metric, NULL); - g_assert(route); - g_assert(nm_setting_ip_config_add_route(s_ip, route)); - nm_ip_route_unref(route); -} - -static inline void -nmtst_assert_route_attribute_string(NMIPRoute *route, const char *name, const char *value) -{ - GVariant *variant; - - variant = nm_ip_route_get_attribute(route, name); - g_assert(variant); - g_assert(g_variant_is_of_type(variant, G_VARIANT_TYPE_STRING)); - g_assert_cmpstr(g_variant_get_string(variant, NULL), ==, value); -} - -static inline void -nmtst_assert_route_attribute_byte(NMIPRoute *route, const char *name, guchar value) -{ - GVariant *variant; - - variant = nm_ip_route_get_attribute(route, name); - g_assert(variant); - g_assert(g_variant_is_of_type(variant, G_VARIANT_TYPE_BYTE)); - g_assert_cmpint(g_variant_get_byte(variant), ==, value); -} - -static inline void -nmtst_assert_route_attribute_uint32(NMIPRoute *route, const char *name, guint32 value) -{ - GVariant *variant; - - variant = nm_ip_route_get_attribute(route, name); - g_assert(variant); - g_assert(g_variant_is_of_type(variant, G_VARIANT_TYPE_UINT32)); - g_assert_cmpint(g_variant_get_uint32(variant), ==, value); -} - -static inline void -nmtst_assert_route_attribute_boolean(NMIPRoute *route, const char *name, gboolean value) -{ - GVariant *variant; - - variant = nm_ip_route_get_attribute(route, name); - g_assert(variant); - g_assert(g_variant_is_of_type(variant, G_VARIANT_TYPE_BOOLEAN)); - g_assert_cmpint(g_variant_get_boolean(variant), ==, value); -} -#endif /* NM_SETTING_IP_CONFIG_H */ - -#if (defined(__NM_SIMPLE_CONNECTION_H__) && defined(__NM_SETTING_CONNECTION_H__)) \ - || (defined(NM_CONNECTION_H)) - -static inline NMConnection * -nmtst_clone_connection(NMConnection *connection) -{ - g_assert(NM_IS_CONNECTION(connection)); - - #if defined(__NM_SIMPLE_CONNECTION_H__) - return nm_simple_connection_new_clone(connection); - #else - return nm_connection_duplicate(connection); - #endif -} - -static inline NMConnection * -nmtst_create_minimal_connection(const char * id, - const char * uuid, - const char * type, - NMSettingConnection **out_s_con) -{ - NMConnection * con; - NMSetting * s_base = NULL; - NMSettingConnection *s_con; - gs_free char * uuid_free = NULL; - - g_assert(id); - - if (uuid) - g_assert(nm_utils_is_uuid(uuid)); - else - uuid = uuid_free = nm_utils_uuid_generate(); - - if (type) { - GType type_g; - - #if defined(__NM_SIMPLE_CONNECTION_H__) - type_g = nm_setting_lookup_type(type); - #else - type_g = nm_connection_lookup_setting_type(type); - #endif - - g_assert(type_g != G_TYPE_INVALID); - - s_base = g_object_new(type_g, NULL); - g_assert(NM_IS_SETTING(s_base)); - } - - #if defined(__NM_SIMPLE_CONNECTION_H__) - con = nm_simple_connection_new(); - #else - con = nm_connection_new(); - #endif - - g_assert(con); - - s_con = NM_SETTING_CONNECTION(nm_setting_connection_new()); - - g_assert(s_con); - - g_object_set(s_con, - NM_SETTING_CONNECTION_ID, - id, - NM_SETTING_CONNECTION_UUID, - uuid, - NM_SETTING_CONNECTION_TYPE, - type, - NULL); - nm_connection_add_setting(con, NM_SETTING(s_con)); - - if (s_base) - nm_connection_add_setting(con, s_base); - - if (out_s_con) - *out_s_con = s_con; - return con; -} - -static inline gboolean -_nmtst_connection_normalize_v(NMConnection *connection, va_list args) -{ - GError * error = NULL; - gboolean success; - gboolean was_modified = FALSE; - GHashTable *parameters = NULL; - const char *p_name; - - g_assert(NM_IS_CONNECTION(connection)); - - while ((p_name = va_arg(args, const char *))) { - if (!parameters) - parameters = g_hash_table_new(g_str_hash, g_str_equal); - g_hash_table_insert(parameters, (gpointer *) p_name, va_arg(args, gpointer)); - } - - success = nm_connection_normalize(connection, parameters, &was_modified, &error); - g_assert_no_error(error); - g_assert(success); - - if (parameters) - g_hash_table_destroy(parameters); - - return was_modified; -} - -static inline gboolean -_nmtst_connection_normalize(NMConnection *connection, ...) -{ - gboolean was_modified; - va_list args; - - va_start(args, connection); - was_modified = _nmtst_connection_normalize_v(connection, args); - va_end(args); - - return was_modified; -} - #define nmtst_connection_normalize(connection, ...) \ - _nmtst_connection_normalize(connection, ##__VA_ARGS__, NULL) - -static inline NMConnection * -_nmtst_connection_duplicate_and_normalize(NMConnection *connection, ...) -{ - va_list args; - - connection = nmtst_clone_connection(connection); - - va_start(args, connection); - _nmtst_connection_normalize_v(connection, args); - va_end(args); - - return connection; -} - #define nmtst_connection_duplicate_and_normalize(connection, ...) \ - _nmtst_connection_duplicate_and_normalize(connection, ##__VA_ARGS__, NULL) - -static inline void -nmtst_assert_connection_equals(NMConnection *a, - gboolean normalize_a, - NMConnection *b, - gboolean normalize_b) -{ - gboolean compare; - gs_unref_object NMConnection *a2 = NULL; - gs_unref_object NMConnection *b2 = NULL; - GHashTable * out_settings = NULL; - - g_assert(NM_IS_CONNECTION(a)); - g_assert(NM_IS_CONNECTION(b)); - - if (normalize_a) - a = a2 = nmtst_connection_duplicate_and_normalize(a); - if (normalize_b) - b = b2 = nmtst_connection_duplicate_and_normalize(b); - - compare = nm_connection_diff(a, b, NM_SETTING_COMPARE_FLAG_EXACT, &out_settings); - if (!compare || out_settings) { - const char * name, *pname; - GHashTable * setting; - GHashTableIter iter, iter2; - - __NMTST_LOG(g_message, ">>> ASSERTION nmtst_assert_connection_equals() fails"); - if (out_settings) { - g_hash_table_iter_init(&iter, out_settings); - while (g_hash_table_iter_next(&iter, (gpointer *) &name, (gpointer *) &setting)) { - __NMTST_LOG(g_message, ">>> differences in setting '%s':", name); - - g_hash_table_iter_init(&iter2, setting); - while (g_hash_table_iter_next(&iter2, (gpointer *) &pname, NULL)) - __NMTST_LOG(g_message, ">>> differences in setting '%s.%s'", name, pname); - } - } - - #ifdef __NM_KEYFILE_INTERNAL_H__ - { - nm_auto_unref_keyfile GKeyFile *kf_a = NULL, *kf_b = NULL; - gs_free char * str_a = NULL, *str_b = NULL; - - kf_a = nm_keyfile_write(a, NM_KEYFILE_HANDLER_FLAGS_NONE, NULL, NULL, NULL); - kf_b = nm_keyfile_write(b, NM_KEYFILE_HANDLER_FLAGS_NONE, NULL, NULL, NULL); - - if (kf_a) - str_a = g_key_file_to_data(kf_a, NULL, NULL); - if (kf_b) - str_b = g_key_file_to_data(kf_b, NULL, NULL); - - __NMTST_LOG(g_message, - ">>> Connection A as kf (*WARNING: keyfile representation might not show " - "the difference*):\n%s", - str_a); - __NMTST_LOG(g_message, - ">>> Connection B as kf (*WARNING: keyfile representation might not show " - "the difference*):\n%s", - str_b); - } - #endif - } - g_assert(compare); - g_assert(!out_settings); - - compare = nm_connection_compare(a, b, NM_SETTING_COMPARE_FLAG_EXACT); - g_assert(compare); -} - -static inline void -nmtst_assert_connection_verifies(NMConnection *con) -{ - /* assert that the connection does verify, it might be normaliziable or not */ - GError * error = NULL; - gboolean success; - - g_assert(NM_IS_CONNECTION(con)); - - success = nm_connection_verify(con, &error); - g_assert_no_error(error); - g_assert(success); -} - -static inline void -nmtst_assert_connection_verifies_without_normalization(NMConnection *con) -{ - /* assert that the connection verifies and does not need any normalization */ - GError * error = NULL; - gboolean success; - gboolean was_modified = FALSE; - gs_unref_object NMConnection *clone = NULL; - - clone = nmtst_clone_connection(con); - - nmtst_assert_connection_verifies(con); - - success = nm_connection_normalize(clone, NULL, &was_modified, &error); - g_assert_no_error(error); - g_assert(success); - nmtst_assert_connection_equals(con, FALSE, clone, FALSE); - g_assert(!was_modified); -} - -static inline void -nmtst_assert_connection_verifies_and_normalizable(NMConnection *con) -{ - /* assert that the connection does verify, but normalization still modifies it */ - GError * error = NULL; - gboolean success; - gboolean was_modified = FALSE; - gs_unref_object NMConnection *clone = NULL; - - clone = nmtst_clone_connection(con); - - nmtst_assert_connection_verifies(con); - - success = nm_connection_normalize(clone, NULL, &was_modified, &error); - g_assert_no_error(error); - g_assert(success); - g_assert(was_modified); - - /* again! */ - nmtst_assert_connection_verifies_without_normalization(clone); -} - -static inline void -nmtst_assert_connection_verifies_after_normalization(NMConnection *con, - GQuark expect_error_domain, - int expect_error_code) -{ - /* assert that the connection does not verify, but normalization does fix it */ - GError * error = NULL; - gboolean success; - gboolean was_modified = FALSE; - gs_unref_object NMConnection *clone = NULL; - - clone = nmtst_clone_connection(con); - - success = nm_connection_verify(con, &error); - nmtst_assert_error(error, expect_error_domain, expect_error_code, NULL); - g_assert(!success); - g_clear_error(&error); - - success = nm_connection_normalize(clone, NULL, &was_modified, &error); - g_assert_no_error(error); - g_assert(success); - g_assert(was_modified); - - /* again! */ - nmtst_assert_connection_verifies_without_normalization(clone); -} - -static inline void -nmtst_assert_connection_unnormalizable(NMConnection *con, - GQuark expect_error_domain, - int expect_error_code) -{ - /* assert that the connection does not verify, and it cannot be fixed by normalization */ - - GError * error = NULL; - gboolean success; - gboolean was_modified = FALSE; - gs_unref_object NMConnection *clone = NULL; - - clone = nmtst_clone_connection(con); - - success = nm_connection_verify(con, &error); - nmtst_assert_error(error, expect_error_domain, expect_error_code, NULL); - g_assert(!success); - g_clear_error(&error); - - success = nm_connection_normalize(clone, NULL, &was_modified, &error); - nmtst_assert_error(error, expect_error_domain, expect_error_code, NULL); - g_assert(!success); - g_assert(!was_modified); - nmtst_assert_connection_equals(con, FALSE, clone, FALSE); - g_clear_error(&error); -} - -static inline void -nmtst_assert_setting_verifies(NMSetting *setting) -{ - /* assert that the setting verifies without an error */ - - GError * error = NULL; - gboolean success; - - g_assert(NM_IS_SETTING(setting)); - - success = nm_setting_verify(setting, NULL, &error); - g_assert_no_error(error); - g_assert(success); -} - - #if defined(__NM_SIMPLE_CONNECTION_H__) && NM_CHECK_VERSION(1, 10, 0) \ - && (!defined(NM_VERSION_MAX_ALLOWED) || NM_VERSION_MAX_ALLOWED >= NM_VERSION_1_10) -static inline void -_nmtst_assert_connection_has_settings(NMConnection *connection, - gboolean has_at_least, - gboolean has_at_most, - ...) -{ - gs_unref_hashtable GHashTable *names = NULL; - gs_free NMSetting **settings = NULL; - va_list ap; - const char * name; - guint i, len; - gs_unref_ptrarray GPtrArray *names_arr = NULL; - - g_assert(NM_IS_CONNECTION(connection)); - - names = g_hash_table_new(g_str_hash, g_str_equal); - names_arr = g_ptr_array_new(); - - va_start(ap, has_at_most); - while ((name = va_arg(ap, const char *))) { - if (!nm_g_hash_table_add(names, (gpointer) name)) - g_assert_not_reached(); - g_ptr_array_add(names_arr, (gpointer) name); - } - va_end(ap); - - g_ptr_array_add(names_arr, NULL); - - settings = nm_connection_get_settings(connection, &len); - for (i = 0; i < len; i++) { - if (!g_hash_table_remove(names, nm_setting_get_name(settings[i])) && has_at_most) { - g_error( - "nmtst_assert_connection_has_settings(): has setting \"%s\" which is not expected", - nm_setting_get_name(settings[i])); - } - } - if (g_hash_table_size(names) > 0 && has_at_least) { - gs_free char * expected_str = g_strjoinv(" ", (char **) names_arr->pdata); - gs_free const char **settings_names = NULL; - gs_free char * has_str = NULL; - - settings_names = g_new0(const char *, len + 1); - for (i = 0; i < len; i++) - settings_names[i] = nm_setting_get_name(settings[i]); - has_str = g_strjoinv(" ", (char **) settings_names); - - g_error("nmtst_assert_connection_has_settings(): the setting lacks %u expected settings " - "(expected: [%s] vs. has: [%s])", - g_hash_table_size(names), - expected_str, - has_str); - } -} - #define nmtst_assert_connection_has_settings(connection, ...) \ - _nmtst_assert_connection_has_settings((connection), TRUE, TRUE, __VA_ARGS__, NULL) - #define nmtst_assert_connection_has_settings_at_least(connection, ...) \ - _nmtst_assert_connection_has_settings((connection), TRUE, FALSE, __VA_ARGS__, NULL) - #define nmtst_assert_connection_has_settings_at_most(connection, ...) \ - _nmtst_assert_connection_has_settings((connection), FALSE, TRUE, __VA_ARGS__, NULL) - #endif - -static inline void -nmtst_assert_setting_verify_fails(NMSetting *setting, - GQuark expect_error_domain, - int expect_error_code) -{ - /* assert that the setting verification fails */ - - GError * error = NULL; - gboolean success; - - g_assert(NM_IS_SETTING(setting)); - - success = nm_setting_verify(setting, NULL, &error); - nmtst_assert_error(error, expect_error_domain, expect_error_code, NULL); - g_assert(!success); - g_clear_error(&error); -} - -static inline void -nmtst_assert_setting_is_equal(gconstpointer /* const NMSetting * */ a, - gconstpointer /* const NMSetting * */ b, - NMSettingCompareFlags flags) -{ - gs_unref_hashtable GHashTable *hash = NULL; - guint32 r = nmtst_get_rand_uint32(); - - g_assert(NM_IS_SETTING(a)); - g_assert(NM_IS_SETTING(b)); - - if (NM_FLAGS_HAS(r, 0x4)) - NM_SWAP(&a, &b); - - g_assert(nm_setting_compare((NMSetting *) a, (NMSetting *) b, flags)); - - if (NM_FLAGS_HAS(r, 0x8)) - NM_SWAP(&a, &b); - - g_assert(nm_setting_diff((NMSetting *) a, (NMSetting *) b, flags, NM_FLAGS_HAS(r, 0x1), &hash)); - g_assert(!hash); -} -#endif - -#ifdef __NM_SETTING_PRIVATE_H__ -static inline NMSetting * -nmtst_assert_setting_dbus_new(GType gtype, GVariant *variant) -{ - NMSetting * setting; - gs_free_error GError *error = NULL; - - g_assert(g_type_is_a(gtype, NM_TYPE_SETTING)); - g_assert(gtype != NM_TYPE_SETTING); - g_assert(variant); - g_assert(g_variant_is_of_type(variant, NM_VARIANT_TYPE_SETTING)); - - setting = - _nm_setting_new_from_dbus(gtype, variant, NULL, NM_SETTING_PARSE_FLAGS_STRICT, &error); - nmtst_assert_success(setting, error); - return setting; -} - -static inline void -nmtst_assert_setting_dbus_roundtrip(gconstpointer /* const NMSetting * */ setting) -{ - gs_unref_object NMSetting *setting2 = NULL; - gs_unref_variant GVariant *variant = NULL; - - g_assert(NM_IS_SETTING(setting)); - - variant = _nm_setting_to_dbus((NMSetting *) setting, NULL, NM_CONNECTION_SERIALIZE_ALL, NULL); - setting2 = nmtst_assert_setting_dbus_new(G_OBJECT_TYPE(setting), variant); - nmtst_assert_setting_is_equal(setting, setting2, NM_SETTING_COMPARE_FLAG_EXACT); -} -#endif - -#ifdef __NM_UTILS_H__ -static inline void -nmtst_assert_hwaddr_equals(gconstpointer hwaddr1, - gssize hwaddr1_len, - const char * expected, - const char * file, - int line) -{ - guint8 buf2[NM_UTILS_HWADDR_LEN_MAX]; - gsize hwaddr2_len = 1; - const char *p; - gboolean success; - - g_assert(hwaddr1_len > 0 && hwaddr1_len <= NM_UTILS_HWADDR_LEN_MAX); - - g_assert(expected); - for (p = expected; *p; p++) { - if (*p == ':' || *p == '-') - hwaddr2_len++; - } - g_assert(hwaddr2_len <= NM_UTILS_HWADDR_LEN_MAX); - g_assert(nm_utils_hwaddr_aton(expected, buf2, hwaddr2_len)); - - /* Manually check the entire hardware address instead of using - * nm_utils_hwaddr_matches() because that function doesn't compare - * entire InfiniBand addresses for various (legitimate) reasons. - */ - success = (hwaddr1_len == hwaddr2_len); - if (success) - success = !memcmp(hwaddr1, buf2, hwaddr1_len); - if (!success) { - g_error("assert: %s:%d: hwaddr '%s' (%zd) expected, but got %s (%zd)", - file, - line, - expected, - hwaddr2_len, - nm_utils_hwaddr_ntoa(hwaddr1, hwaddr1_len), - hwaddr1_len); - } -} - #define nmtst_assert_hwaddr_equals(hwaddr1, hwaddr1_len, expected) \ - nmtst_assert_hwaddr_equals(hwaddr1, hwaddr1_len, expected, __FILE__, __LINE__) -#endif - -#if defined(__NM_SIMPLE_CONNECTION_H__) && defined(__NM_SETTING_CONNECTION_H__) \ - && defined(__NM_KEYFILE_INTERNAL_H__) - -static inline NMConnection * -nmtst_create_connection_from_keyfile(const char *keyfile_str, const char *full_filename) -{ - nm_auto_unref_keyfile GKeyFile *keyfile = NULL; - gs_free_error GError *error = NULL; - gboolean success; - NMConnection * con; - gs_free char * filename = g_path_get_basename(full_filename); - gs_free char * base_dir = g_path_get_dirname(full_filename); - - g_assert(keyfile_str); - g_assert(full_filename && full_filename[0] == '/'); - - keyfile = g_key_file_new(); - success = g_key_file_load_from_data(keyfile, - keyfile_str, - strlen(keyfile_str), - G_KEY_FILE_NONE, - &error); - nmtst_assert_success(success, error); - - con = nm_keyfile_read(keyfile, base_dir, NM_KEYFILE_HANDLER_FLAGS_NONE, NULL, NULL, &error); - nmtst_assert_success(NM_IS_CONNECTION(con), error); - - nm_keyfile_read_ensure_id(con, filename); - nm_keyfile_read_ensure_uuid(con, full_filename); - - nmtst_connection_normalize(con); - - return con; -} - -#endif - -#ifdef __NM_CONNECTION_H__ - -static inline GVariant * -_nmtst_variant_new_vardict(int dummy, ...) -{ - GVariantBuilder builder; - va_list ap; - const char * name; - GVariant * variant; - - g_variant_builder_init(&builder, G_VARIANT_TYPE_VARDICT); - - va_start(ap, dummy); - while ((name = va_arg(ap, const char *))) { - variant = va_arg(ap, GVariant *); - g_variant_builder_add(&builder, "{sv}", name, variant); - } - va_end(ap); - - return g_variant_builder_end(&builder); -} - #define nmtst_variant_new_vardict(...) _nmtst_variant_new_vardict(0, __VA_ARGS__, NULL) - - #define nmtst_assert_variant_is_of_type(variant, type) \ - G_STMT_START \ - { \ - GVariant *_variantx = (variant); \ - \ - g_assert(_variantx); \ - g_assert(g_variant_is_of_type(_variantx, (type))); \ - } \ - G_STMT_END - - #define nmtst_assert_variant_uint32(variant, val) \ - G_STMT_START \ - { \ - GVariant *_variant = (variant); \ - \ - nmtst_assert_variant_is_of_type(_variant, G_VARIANT_TYPE_UINT32); \ - g_assert_cmpint(g_variant_get_uint32(_variant), ==, (val)); \ - } \ - G_STMT_END - - #define nmtst_assert_variant_string(variant, str) \ - G_STMT_START \ - { \ - gsize _l; \ - GVariant * _variant = (variant); \ - const char *_str = (str); \ - \ - nmtst_assert_variant_is_of_type(_variant, G_VARIANT_TYPE_STRING); \ - g_assert(_str); \ - g_assert_cmpstr(g_variant_get_string(_variant, &_l), ==, _str); \ - g_assert_cmpint(_l, ==, strlen(_str)); \ - } \ - G_STMT_END - - #ifdef __NM_SHARED_UTILS_H__ - #define _nmtst_assert_variant_bytestring_cmp_str(_ptr, _ptr2, _len) \ - G_STMT_START \ - { \ - if (memcmp(_ptr2, _ptr, _len) != 0) { \ - gs_free char *_x1 = NULL; \ - gs_free char *_x2 = NULL; \ - const char * _xx1; \ - const char * _xx2; \ - \ - _xx1 = nm_utils_buf_utf8safe_escape(_ptr, \ - _len, \ - NM_UTILS_STR_UTF8_SAFE_FLAG_ESCAPE_CTRL, \ - &_x1); \ - _xx2 = nm_utils_buf_utf8safe_escape(_ptr2, \ - _len, \ - NM_UTILS_STR_UTF8_SAFE_FLAG_ESCAPE_CTRL, \ - &_x2); \ - g_assert_cmpstr(_xx1, ==, _xx2); \ - g_assert_not_reached(); \ - } \ - } \ - G_STMT_END - #else - #define _nmtst_assert_variant_bytestring_cmp_str(_ptr, _ptr2, _len) \ - G_STMT_START {} \ - G_STMT_END - #endif - - #define nmtst_assert_variant_bytestring(variant, ptr, len) \ - G_STMT_START \ - { \ - GVariant * _variant = (variant); \ - gconstpointer _ptr = (ptr); \ - gconstpointer _ptr2; \ - gsize _len = (len); \ - gsize _len2; \ - \ - nmtst_assert_variant_is_of_type(_variant, G_VARIANT_TYPE_BYTESTRING); \ - _ptr2 = g_variant_get_fixed_array(_variant, &_len2, 1); \ - g_assert_cmpint(_len2, ==, _len); \ - if (_len != 0 && _ptr) { \ - _nmtst_assert_variant_bytestring_cmp_str(_ptr, _ptr2, _len); \ - g_assert_cmpmem(_ptr2, _len2, _ptr, _len); \ - } \ - } \ - G_STMT_END - -typedef enum { - NMTST_VARIANT_EDITOR_CONNECTION, - NMTST_VARIANT_EDITOR_SETTING, - NMTST_VARIANT_EDITOR_PROPERTY -} NmtstVariantEditorPhase; - - #define NMTST_VARIANT_EDITOR(__connection_variant, __code) \ - G_STMT_START \ - { \ - GVariantIter __connection_iter, *__setting_iter; \ - GVariantBuilder __connection_builder, __setting_builder; \ - const char * __cur_setting_name, *__cur_property_name; \ - GVariant * __property_val; \ - NmtstVariantEditorPhase __phase; \ - \ - g_variant_builder_init(&__connection_builder, NM_VARIANT_TYPE_CONNECTION); \ - g_variant_iter_init(&__connection_iter, __connection_variant); \ - \ - __phase = NMTST_VARIANT_EDITOR_CONNECTION; \ - __cur_setting_name = NULL; \ - __cur_property_name = NULL; \ - __code; \ - while (g_variant_iter_next(&__connection_iter, \ - "{&sa{sv}}", \ - &__cur_setting_name, \ - &__setting_iter)) { \ - g_variant_builder_init(&__setting_builder, NM_VARIANT_TYPE_SETTING); \ - __phase = NMTST_VARIANT_EDITOR_SETTING; \ - __cur_property_name = NULL; \ - __code; \ - \ - while (__cur_setting_name \ - && g_variant_iter_next(__setting_iter, \ - "{&sv}", \ - &__cur_property_name, \ - &__property_val)) { \ - __phase = NMTST_VARIANT_EDITOR_PROPERTY; \ - __code; \ - \ - if (__cur_property_name) { \ - g_variant_builder_add(&__setting_builder, \ - "{sv}", \ - __cur_property_name, \ - __property_val); \ - } \ - g_variant_unref(__property_val); \ - } \ - \ - if (__cur_setting_name) \ - g_variant_builder_add(&__connection_builder, \ - "{sa{sv}}", \ - __cur_setting_name, \ - &__setting_builder); \ - else \ - g_variant_builder_clear(&__setting_builder); \ - g_variant_iter_free(__setting_iter); \ - } \ - \ - g_variant_unref(__connection_variant); \ - \ - __connection_variant = g_variant_builder_end(&__connection_builder); \ - } \ - G_STMT_END; - - #define NMTST_VARIANT_ADD_SETTING(__setting_name, __setting_variant) \ - G_STMT_START \ - { \ - if (__phase == NMTST_VARIANT_EDITOR_CONNECTION) \ - g_variant_builder_add(&__connection_builder, \ - "{s@a{sv}}", \ - __setting_name, \ - __setting_variant); \ - } \ - G_STMT_END - - #define NMTST_VARIANT_DROP_SETTING(__setting_name) \ - G_STMT_START \ - { \ - if (__phase == NMTST_VARIANT_EDITOR_SETTING && __cur_setting_name) { \ - if (!strcmp(__cur_setting_name, __setting_name)) \ - __cur_setting_name = NULL; \ - } \ - } \ - G_STMT_END - - #define NMTST_VARIANT_ADD_PROPERTY(__setting_name, __property_name, __format_string, __value) \ - G_STMT_START \ - { \ - if (__phase == NMTST_VARIANT_EDITOR_SETTING) { \ - if (!strcmp(__cur_setting_name, __setting_name)) { \ - g_variant_builder_add(&__setting_builder, \ - "{sv}", \ - __property_name, \ - g_variant_new(__format_string, __value)); \ - } \ - } \ - } \ - G_STMT_END - - #define NMTST_VARIANT_DROP_PROPERTY(__setting_name, __property_name) \ - G_STMT_START \ - { \ - if (__phase == NMTST_VARIANT_EDITOR_PROPERTY && __cur_property_name) { \ - if (!strcmp(__cur_setting_name, __setting_name) \ - && !strcmp(__cur_property_name, __property_name)) \ - __cur_property_name = NULL; \ - } \ - } \ - G_STMT_END - - #define NMTST_VARIANT_CHANGE_PROPERTY(__setting_name, \ - __property_name, \ - __format_string, \ - __value) \ - G_STMT_START \ - { \ - NMTST_VARIANT_DROP_PROPERTY(__setting_name, __property_name); \ - NMTST_VARIANT_ADD_PROPERTY(__setting_name, __property_name, __format_string, __value); \ - } \ - G_STMT_END - -#endif /* __NM_CONNECTION_H__ */ - -static inline GVariant * -nmtst_variant_from_string(const GVariantType *variant_type, const char *variant_str) -{ - GVariant *variant; - GError * error = NULL; - - g_assert(variant_type); - g_assert(variant_str); - - variant = g_variant_parse(variant_type, variant_str, NULL, NULL, &error); - nmtst_assert_success(variant, error); - return variant; -} - -/*****************************************************************************/ - -static inline void -nmtst_keyfile_assert_data(GKeyFile *kf, const char *data, gssize data_len) -{ - nm_auto_unref_keyfile GKeyFile *kf2 = NULL; - gs_free_error GError *error = NULL; - gs_free char * d1 = NULL; - gs_free char * d2 = NULL; - gboolean success; - gsize d1_len; - gsize d2_len; - - g_assert(kf); - g_assert(data || data_len == 0); - g_assert(data_len >= -1); - - d1 = g_key_file_to_data(kf, &d1_len, &error); - nmtst_assert_success(d1, error); - - if (data_len == -1) { - g_assert_cmpint(strlen(d1), ==, d1_len); - data_len = strlen(data); - g_assert_cmpstr(d1, ==, data); - } - - g_assert_cmpmem(d1, d1_len, data, (gsize) data_len); - - /* also check that we can re-generate the same keyfile from the data. */ - - kf2 = g_key_file_new(); - success = g_key_file_load_from_data(kf2, d1, d1_len, G_KEY_FILE_NONE, &error); - nmtst_assert_success(success, error); - - d2 = g_key_file_to_data(kf2, &d2_len, &error); - nmtst_assert_success(d2, error); - - g_assert_cmpmem(d2, d2_len, d1, d1_len); -} - -static inline gssize -nmtst_keyfile_get_num_keys(GKeyFile *keyfile, const char *group_name) -{ - gs_strfreev char **keys = NULL; - gs_free_error GError *error = NULL; - gsize l; - - g_assert(keyfile); - g_assert(group_name); - - if (!g_key_file_has_group(keyfile, group_name)) - return -1; - - keys = g_key_file_get_keys(keyfile, group_name, &l, &error); - - nmtst_assert_success(keys, error); - - g_assert_cmpint(NM_PTRARRAY_LEN(keys), ==, l); - - return l; -} - -/*****************************************************************************/ - -#if defined(NM_SETTING_IP_CONFIG_H) && defined(__NM_SHARED_UTILS_H__) - -static inline NMIPAddress * -nmtst_ip_address_new(int addr_family, const char *str) -{ - NMIPAddr addr; - int plen; - GError * error = NULL; - NMIPAddress *a; - - if (!nm_utils_parse_inaddr_prefix_bin(addr_family, str, &addr_family, &addr, &plen)) - g_assert_not_reached(); - - if (plen == -1) - plen = addr_family == AF_INET ? 32 : 128; - - a = nm_ip_address_new_binary(addr_family, &addr, plen, &error); - nmtst_assert_success(a, error); - return a; -} - -#endif - -/*****************************************************************************/ - -#endif /* __NM_TEST_UTILS_H__ */ diff --git a/shared/nm-utils/nm-vpn-editor-plugin-call.h b/shared/nm-utils/nm-vpn-editor-plugin-call.h deleted file mode 100644 index 5772b843..00000000 --- a/shared/nm-utils/nm-vpn-editor-plugin-call.h +++ /dev/null @@ -1,167 +0,0 @@ -/* SPDX-License-Identifier: LGPL-2.1-or-later */ -/* - * Copyright (C) 2016 Red Hat, Inc. - */ - -#ifndef __NM_VPN_EDITOR_PLUGIN_CALL_H__ -#define __NM_VPN_EDITOR_PLUGIN_CALL_H__ - -/* This header is an internal, header-only file that can be copied to - * other projects to call well-known service functions on VPN plugins. - * - * This uses the NMVpnEditorPluginVT and allows a user (nm-applet) - * to directly communicate with a VPN plugin using API that is newer - * then the current libnm version. That is, it allows to call to a VPN - * plugin bypassing libnm. */ - -#include <NetworkManager.h> - -/* we make use of other internal header files, you need those too. */ -#include "nm-glib-aux/nm-macros-internal.h" - -/*****************************************************************************/ - -/** - * NMVpnEditorPluginServiceFlags: - * @NM_VPN_EDITOR_PLUGIN_SERVICE_FLAGS_NONE: no flags - * @NM_VPN_EDITOR_PLUGIN_SERVICE_FLAGS_CAN_ADD: whether the plugin can - * add a new connection for the given service-type. - **/ -typedef enum { /*< skip >*/ - NM_VPN_EDITOR_PLUGIN_SERVICE_FLAGS_NONE = 0x00, - NM_VPN_EDITOR_PLUGIN_SERVICE_FLAGS_CAN_ADD = 0x01, -} NMVpnEditorPluginServiceFlags; - -struct _NMVpnEditorPluginVT { - gboolean (*fcn_get_service_info)(NMVpnEditorPlugin * plugin, - const char * service_type, - char ** out_short_name, - char ** out_pretty_name, - char ** out_description, - NMVpnEditorPluginServiceFlags *out_flags); - char **(*fcn_get_service_add_details)(NMVpnEditorPlugin *plugin, const char *service_name); - gboolean (*fcn_get_service_add_detail)(NMVpnEditorPlugin *plugin, - const char * service_type, - const char * add_detail, - char ** out_pretty_name, - char ** out_description, - char ** out_add_detail_key, - char ** out_add_detail_val, - guint * out_flags); -}; - -/***************************************************************************** - * Call - * - * The following wrap the calling of generic functions for a VPN plugin. - * They are used by callers (for example nm-connection-editor). - *****************************************************************************/ - -static inline gboolean -nm_vpn_editor_plugin_get_service_info(NMVpnEditorPlugin * plugin, - const char * service_type, - char ** out_short_name, - char ** out_pretty_name, - char ** out_description, - NMVpnEditorPluginServiceFlags *out_flags) -{ - NMVpnEditorPluginVT vt; - gs_free char * short_name_local = NULL; - gs_free char * pretty_name_local = NULL; - gs_free char * description_local = NULL; - guint flags_local = 0; - - g_return_val_if_fail(NM_IS_VPN_EDITOR_PLUGIN(plugin), FALSE); - g_return_val_if_fail(service_type, FALSE); - - nm_vpn_editor_plugin_get_vt(plugin, &vt, sizeof(vt)); - if (!vt.fcn_get_service_info - || !vt.fcn_get_service_info(plugin, - service_type, - out_short_name ? &short_name_local : NULL, - out_pretty_name ? &pretty_name_local : NULL, - out_description ? &description_local : NULL, - out_flags ? &flags_local : NULL)) - return FALSE; - NM_SET_OUT(out_short_name, g_steal_pointer(&short_name_local)); - NM_SET_OUT(out_pretty_name, g_steal_pointer(&pretty_name_local)); - NM_SET_OUT(out_description, g_steal_pointer(&description_local)); - NM_SET_OUT(out_flags, flags_local); - return TRUE; -} - -static inline char ** -nm_vpn_editor_plugin_get_service_add_details(NMVpnEditorPlugin *plugin, const char *service_name) -{ - NMVpnEditorPluginVT vt; - char ** details = NULL; - - g_return_val_if_fail(NM_IS_VPN_EDITOR_PLUGIN(plugin), NULL); - g_return_val_if_fail(service_name, NULL); - - nm_vpn_editor_plugin_get_vt(plugin, &vt, sizeof(vt)); - if (vt.fcn_get_service_add_details) - details = vt.fcn_get_service_add_details(plugin, service_name); - if (!details) - return g_new0(char *, 1); - return details; -} - -static inline gboolean -nm_vpn_editor_plugin_get_service_add_detail(NMVpnEditorPlugin *plugin, - const char * service_type, - const char * add_detail, - char ** out_pretty_name, - char ** out_description, - char ** out_add_detail_key, - char ** out_add_detail_val, - guint * out_flags) -{ - NMVpnEditorPluginVT vt; - gs_free char * pretty_name_local = NULL; - gs_free char * description_local = NULL; - gs_free char * add_detail_key_local = NULL; - gs_free char * add_detail_val_local = NULL; - guint flags_local = 0; - - g_return_val_if_fail(NM_IS_VPN_EDITOR_PLUGIN(plugin), FALSE); - g_return_val_if_fail(service_type, FALSE); - g_return_val_if_fail(add_detail, FALSE); - - nm_vpn_editor_plugin_get_vt(plugin, &vt, sizeof(vt)); - if (!vt.fcn_get_service_add_detail - || !vt.fcn_get_service_add_detail(plugin, - service_type, - add_detail, - out_pretty_name ? &pretty_name_local : NULL, - out_description ? &description_local : NULL, - out_add_detail_key ? &add_detail_key_local : NULL, - out_add_detail_val ? &add_detail_val_local : NULL, - out_flags ? &flags_local : NULL)) - return FALSE; - NM_SET_OUT(out_pretty_name, g_steal_pointer(&pretty_name_local)); - NM_SET_OUT(out_description, g_steal_pointer(&description_local)); - NM_SET_OUT(out_add_detail_key, g_steal_pointer(&add_detail_key_local)); - NM_SET_OUT(out_add_detail_val, g_steal_pointer(&add_detail_val_local)); - NM_SET_OUT(out_flags, flags_local); - return TRUE; -} - -/***************************************************************************** - * Implementation - * - * The following glue code can be used to implement calls in a VPN plugin. - *****************************************************************************/ - -#define NM_VPN_EDITOR_PLUGIN_VT_DEFINE(vt_name, get_vt, ...) \ - static const NMVpnEditorPluginVT vt_name = {__VA_ARGS__}; \ - static const NMVpnEditorPluginVT *get_vt(NMVpnEditorPlugin *plugin, gsize *out_vt_size) \ - { \ - nm_assert(NM_IS_VPN_EDITOR_PLUGIN(plugin)); \ - nm_assert(out_vt_size); \ - \ - *out_vt_size = sizeof(vt_name); \ - return &vt_name; \ - } - -#endif /* __NM_VPN_EDITOR_PLUGIN_CALL_H__ */ diff --git a/shared/nm-utils/nm-vpn-plugin-macros.h b/shared/nm-utils/nm-vpn-plugin-macros.h deleted file mode 100644 index 4ca22427..00000000 --- a/shared/nm-utils/nm-vpn-plugin-macros.h +++ /dev/null @@ -1,76 +0,0 @@ -/* SPDX-License-Identifier: LGPL-2.1-or-later */ -/* - * Copyright (C) 2016 Red Hat, Inc. - */ - -#ifndef __NM_VPN_PLUGIN_MACROS_H__ -#define __NM_VPN_PLUGIN_MACROS_H__ - -#include <syslog.h> - -static inline int -nm_utils_syslog_coerce_from_nm(int syslog_level) -{ - /* NetworkManager uses internally NMLogLevel levels. When spawning - * the VPN plugin, it maps those levels to syslog levels as follows: - * - * LOGL_INFO = LOG_NOTICE, - * LOGL_DEBUG = LOG_INFO, - * LOGL_TRACE = LOG_DEBUG, - * - * However, when actually printing to syslog, we don't want to print messages - * with LOGL_INFO level as LOG_NOTICE, because they are *not* to be highlighted. - * - * In other words: NetworkManager has 3 levels that should not require highlighting: - * LOGL_INFO, LOGL_DEBUG, LOGL_TRACE. syslog on the other hand has only LOG_INFO and LOG_DEBUG. - * - * So, coerce those values before printing to syslog. When you receive the syslog_level - * from NetworkManager, instead of calling - * syslog(syslog_level, ...) - * you should call - * syslog(nm_utils_syslog_coerce_from_nm(syslog_level), ...) - */ - switch (syslog_level) { - case LOG_INFO: - return LOG_DEBUG; - case LOG_NOTICE: - return LOG_INFO; - default: - return syslog_level; - } -} - -static inline const char * -nm_utils_syslog_to_str(int syslog_level) -{ - /* Maps the levels the same way as NetworkManager's nm-logging.c does */ - if (syslog_level >= LOG_DEBUG) - return "<trace>"; - if (syslog_level >= LOG_INFO) - return "<debug>"; - if (syslog_level >= LOG_NOTICE) - return "<info>"; - if (syslog_level >= LOG_WARNING) - return "<warn>"; - return "<error>"; -} - -/*****************************************************************************/ - -/* possibly missing defines from newer libnm API. */ - -#ifndef NM_VPN_PLUGIN_CONFIG_PROXY_PAC - #define NM_VPN_PLUGIN_CONFIG_PROXY_PAC "pac" -#endif - -#ifndef NM_VPN_PLUGIN_IP4_CONFIG_PRESERVE_ROUTES - #define NM_VPN_PLUGIN_IP4_CONFIG_PRESERVE_ROUTES "preserve-routes" -#endif - -#ifndef NM_VPN_PLUGIN_IP6_CONFIG_PRESERVE_ROUTES - #define NM_VPN_PLUGIN_IP6_CONFIG_PRESERVE_ROUTES "preserve-routes" -#endif - -/*****************************************************************************/ - -#endif /* __NM_VPN_PLUGIN_MACROS_H__ */ diff --git a/shared/nm-utils/nm-vpn-plugin-utils.c b/shared/nm-utils/nm-vpn-plugin-utils.c deleted file mode 100644 index 89285ecd..00000000 --- a/shared/nm-utils/nm-vpn-plugin-utils.c +++ /dev/null @@ -1,138 +0,0 @@ -/* SPDX-License-Identifier: LGPL-2.1-or-later */ -/* - * Copyright (C) 2016, 2018 Red Hat, Inc. - */ - -#include "libnm/nm-default-client.h" - -#include "nm-vpn-plugin-utils.h" - -#include <dlfcn.h> - -/*****************************************************************************/ - -NMVpnEditor * -nm_vpn_plugin_utils_load_editor(const char * module_name, - const char * factory_name, - NMVpnPluginUtilsEditorFactory editor_factory, - NMVpnEditorPlugin * editor_plugin, - NMConnection * connection, - gpointer user_data, - GError ** error) - -{ - static struct { - gpointer factory; - void * dl_module; - char * module_name; - char * factory_name; - } cached = {0}; - NMVpnEditor * editor; - gs_free char *module_path = NULL; - gs_free char *dirname = NULL; - Dl_info plugin_info; - - g_return_val_if_fail(module_name, NULL); - g_return_val_if_fail(factory_name && factory_name[0], NULL); - g_return_val_if_fail(editor_factory, NULL); - g_return_val_if_fail(NM_IS_VPN_EDITOR_PLUGIN(editor_plugin), NULL); - g_return_val_if_fail(NM_IS_CONNECTION(connection), NULL); - g_return_val_if_fail(!error || !*error, NULL); - - if (!g_path_is_absolute(module_name)) { - /* - * Load an editor from the same directory this plugin is in. - * Ideally, we'd get our .so name from the NMVpnEditorPlugin if it - * would just have a property with it... - */ - if (!dladdr(nm_vpn_plugin_utils_load_editor, &plugin_info)) { - /* Really a "can not happen" scenario. */ - g_set_error(error, - NM_VPN_PLUGIN_ERROR, - NM_VPN_PLUGIN_ERROR_FAILED, - _("unable to get editor plugin name: %s"), - dlerror()); - } - - dirname = g_path_get_dirname(plugin_info.dli_fname); - module_path = g_build_filename(dirname, module_name, NULL); - } else { - module_path = g_strdup(module_name); - } - - /* we really expect this function to be called with unchanging @module_name - * and @factory_name. And we only want to load the module once, hence it would - * be more complicated to accept changing @module_name/@factory_name arguments. - * - * The reason for only loading once is that due to glib types, we cannot create a - * certain type-name more then once, so loading the same module or another version - * of the same module will fail horribly as both try to create a GType with the same - * name. - * - * Only support loading once, any future calls will reuse the handle. To simplify - * that, we enforce that the @factory_name and @module_name is the same. */ - if (cached.factory) { - g_return_val_if_fail(cached.dl_module, NULL); - g_return_val_if_fail(cached.factory_name && nm_streq0(cached.factory_name, factory_name), - NULL); - g_return_val_if_fail(cached.module_name && nm_streq0(cached.module_name, module_name), - NULL); - } else { - gpointer factory; - void * dl_module; - - dl_module = dlopen(module_path, RTLD_LAZY | RTLD_LOCAL); - if (!dl_module) { - if (!g_file_test(module_path, G_FILE_TEST_EXISTS)) { - g_set_error(error, - G_FILE_ERROR, - G_FILE_ERROR_NOENT, - _("missing plugin file \"%s\""), - module_path); - return NULL; - } - g_set_error(error, - NM_VPN_PLUGIN_ERROR, - NM_VPN_PLUGIN_ERROR_FAILED, - _("cannot load editor plugin: %s"), - dlerror()); - return NULL; - } - - factory = dlsym(dl_module, factory_name); - if (!factory) { - g_set_error(error, - NM_VPN_PLUGIN_ERROR, - NM_VPN_PLUGIN_ERROR_FAILED, - _("cannot load factory %s from plugin: %s"), - factory_name, - dlerror()); - dlclose(dl_module); - return NULL; - } - - /* we cannot ever unload the module because it creates glib types, which - * cannot be unregistered. - * - * Thus we just leak the dl_module handle indefinitely. */ - cached.factory = factory; - cached.dl_module = dl_module; - cached.module_name = g_strdup(module_name); - cached.factory_name = g_strdup(factory_name); - } - - editor = editor_factory(cached.factory, editor_plugin, connection, user_data, error); - if (!editor) { - if (error && !*error) { - g_set_error_literal(error, - NM_VPN_PLUGIN_ERROR, - NM_VPN_PLUGIN_ERROR_FAILED, - _("unknown error creating editor instance")); - g_return_val_if_reached(NULL); - } - return NULL; - } - - g_return_val_if_fail(NM_IS_VPN_EDITOR(editor), NULL); - return editor; -} diff --git a/shared/nm-utils/nm-vpn-plugin-utils.h b/shared/nm-utils/nm-vpn-plugin-utils.h deleted file mode 100644 index 881a368a..00000000 --- a/shared/nm-utils/nm-vpn-plugin-utils.h +++ /dev/null @@ -1,25 +0,0 @@ -/* SPDX-License-Identifier: LGPL-2.1-or-later */ -/* - * Copyright (C) 2016 Red Hat, Inc. - */ - -#ifndef __NM_VPN_PLUGIN_UTILS_H__ -#define __NM_VPN_PLUGIN_UTILS_H__ - -#include <NetworkManager.h> - -typedef NMVpnEditor *(NMVpnPluginUtilsEditorFactory)(gpointer factory, - NMVpnEditorPlugin *editor_plugin, - NMConnection * connection, - gpointer user_data, - GError ** error); - -NMVpnEditor *nm_vpn_plugin_utils_load_editor(const char * module_name, - const char * factory_name, - NMVpnPluginUtilsEditorFactory editor_factory, - NMVpnEditorPlugin * editor_plugin, - NMConnection * connection, - gpointer user_data, - GError ** error); - -#endif /* __NM_VPN_PLUGIN_UTILS_H__ */ diff --git a/shared/systemd/nm-default-systemd-shared.h b/shared/systemd/nm-default-systemd-shared.h deleted file mode 100644 index bc0e6c4c..00000000 --- a/shared/systemd/nm-default-systemd-shared.h +++ /dev/null @@ -1,18 +0,0 @@ -/* SPDX-License-Identifier: LGPL-2.1-or-later */ -/* - * Copyright (C) 2015 Red Hat, Inc. - */ - -#ifndef __NM_DEFAULT_SYSTEMD_SHARED_H__ -#define __NM_DEFAULT_SYSTEMD_SHARED_H__ - -/*****************************************************************************/ - -#include "nm-glib-aux/nm-default-glib.h" - -#undef NETWORKMANAGER_COMPILATION -#define NETWORKMANAGER_COMPILATION NM_NETWORKMANAGER_COMPILATION_SYSTEMD_SHARED - -/*****************************************************************************/ - -#endif /* __NM_DEFAULT_SYSTEMD_SHARED_H__ */ diff --git a/shared/systemd/nm-logging-stub.c b/shared/systemd/nm-logging-stub.c deleted file mode 100644 index 8db90cd9..00000000 --- a/shared/systemd/nm-logging-stub.c +++ /dev/null @@ -1,36 +0,0 @@ -/* SPDX-License-Identifier: LGPL-2.1-or-later */ -/* - * Copyright (C) 2018 Red Hat, Inc. - */ - -#include "shared/systemd/nm-default-systemd-shared.h" - -#include "nm-glib-aux/nm-logging-fwd.h" - -/*****************************************************************************/ - -gboolean -_nm_log_enabled_impl(gboolean mt_require_locking, NMLogLevel level, NMLogDomain domain) -{ - return FALSE; -} - -void -_nm_log_impl(const char *file, - guint line, - const char *func, - gboolean mt_require_locking, - NMLogLevel level, - NMLogDomain domain, - int error, - const char *ifname, - const char *con_uuid, - const char *fmt, - ...) -{} - -void -_nm_utils_monotonic_timestamp_initialized(const struct timespec *tp, - gint64 offset_sec, - gboolean is_boottime) -{} diff --git a/shared/systemd/nm-sd-utils-shared.c b/shared/systemd/nm-sd-utils-shared.c deleted file mode 100644 index f0504aa9..00000000 --- a/shared/systemd/nm-sd-utils-shared.c +++ /dev/null @@ -1,192 +0,0 @@ -/* SPDX-License-Identifier: LGPL-2.1-or-later */ -/* - * Copyright (C) 2018 Red Hat, Inc. - */ - -#include "shared/systemd/nm-default-systemd-shared.h" - -#include "nm-sd-utils-shared.h" - -#include "nm-sd-adapt-shared.h" - -#include "dns-domain.h" -#include "hexdecoct.h" -#include "hostname-util.h" -#include "path-util.h" -#include "web-util.h" - -/*****************************************************************************/ - -const bool mempool_use_allowed = true; - -/*****************************************************************************/ - -gboolean -nm_sd_utils_path_equal(const char *a, const char *b) -{ - return path_equal(a, b); -} - -char * -nm_sd_utils_path_simplify(char *path, gboolean kill_dots) -{ - return path_simplify(path, kill_dots); -} - -const char * -nm_sd_utils_path_startswith(const char *path, const char *prefix) -{ - return path_startswith(path, prefix); -} - -/*****************************************************************************/ - -int -nm_sd_utils_unbase64char(char ch, gboolean accept_padding_equal) -{ - if (ch == '=' && accept_padding_equal) - return G_MAXINT; - return unbase64char(ch); -} - -/** - * nm_sd_utils_unbase64mem: - * @p: a valid base64 string. Whitespace is ignored, but invalid encodings - * will cause the function to fail. - * @l: the length of @p. @p is not treated as NUL terminated string but - * merely as a buffer of ascii characters. - * @secure: whether the temporary memory will be cleared to avoid leaving - * secrets in memory (see also nm_explicit_bzero()). - * @mem: (transfer full): the decoded buffer on success. - * @len: the length of @mem on success. - * - * glib provides g_base64_decode(), but that does not report any errors - * from invalid encodings. Expose systemd's implementation which does - * reject invalid inputs. - * - * Returns: a non-negative code on success. Invalid encoding let the - * function fail. - */ -int -nm_sd_utils_unbase64mem(const char *p, size_t l, gboolean secure, guint8 **mem, size_t *len) -{ - return unbase64mem_full(p, l, secure, (void **) mem, len); -} - -int -nm_sd_dns_name_to_wire_format(const char *domain, guint8 *buffer, size_t len, gboolean canonical) -{ - return dns_name_to_wire_format(domain, buffer, len, canonical); -} - -int -nm_sd_dns_name_is_valid(const char *s) -{ - return dns_name_is_valid(s); -} - -gboolean -nm_sd_hostname_is_valid(const char *s, bool allow_trailing_dot) -{ - return hostname_is_valid(s, - allow_trailing_dot ? VALID_HOSTNAME_TRAILING_DOT - : (ValidHostnameFlags) 0); -} - -char * -nm_sd_dns_name_normalize(const char *s) -{ - nm_auto_free char *n = NULL; - int r; - - r = dns_name_normalize(s, 0, &n); - if (r < 0) - return NULL; - - nm_assert(n); - - /* usually we try not to mix malloc/g_malloc and free/g_free. In practice, - * they are the same. So here we return a buffer allocated with malloc(), - * and the caller should free it with g_free(). */ - return g_steal_pointer(&n); -} - -/*****************************************************************************/ - -static gboolean -_http_url_is_valid(const char *url, gboolean only_https) -{ - if (!url || !url[0]) - return FALSE; - - if (!only_https && NM_STR_HAS_PREFIX(url, "http://")) - url += NM_STRLEN("http://"); - else if (NM_STR_HAS_PREFIX(url, "https://")) - url += NM_STRLEN("https://"); - else - return FALSE; - - if (!url[0]) - return FALSE; - - return !NM_STRCHAR_ANY(url, ch, (guchar) ch >= 128u); -} - -gboolean -nm_sd_http_url_is_valid_https(const char *url) -{ - /* We use this function to verify connection:mud-url property, it must thus - * not change behavior. - * - * Note that sd_dhcp_client_set_mud_url() and sd_dhcp6_client_set_request_mud_url() - * assert with http_url_is_valid() that the argument is valid. We thus must make - * sure to only pass URLs that are valid according to http_url_is_valid(). - * - * This is given, because our nm_sd_http_url_is_valid_https() is more strict - * than http_url_is_valid(). - * - * We only must make sure that this is also correct in the future, when we - * re-import systemd code. */ - nm_assert(_http_url_is_valid(url, FALSE) == http_url_is_valid(url)); - return _http_url_is_valid(url, TRUE); -} - -/*****************************************************************************/ - -int -nmtst_systemd_extract_first_word_all(const char *str, char ***out_strv) -{ - gs_unref_ptrarray GPtrArray *arr = NULL; - - /* we implement a str split function to parse `/proc/cmdline`. This - * code should behave like systemd, which uses extract_first_word() - * for that. - * - * As we want to unit-test our implementation to match systemd, - * expose this function for testing. */ - - g_assert(out_strv); - g_assert(!*out_strv); - - if (!str) - return 0; - - arr = g_ptr_array_new_with_free_func(g_free); - - for (;;) { - gs_free char *word = NULL; - int r; - - r = extract_first_word(&str, &word, NULL, EXTRACT_UNQUOTE | EXTRACT_RELAX); - if (r < 0) - return r; - if (r == 0) - break; - g_ptr_array_add(arr, g_steal_pointer(&word)); - } - - g_ptr_array_add(arr, NULL); - - *out_strv = (char **) g_ptr_array_free(g_steal_pointer(&arr), FALSE); - return 1; -} diff --git a/shared/systemd/nm-sd-utils-shared.h b/shared/systemd/nm-sd-utils-shared.h deleted file mode 100644 index 45089c07..00000000 --- a/shared/systemd/nm-sd-utils-shared.h +++ /dev/null @@ -1,41 +0,0 @@ -/* SPDX-License-Identifier: LGPL-2.1-or-later */ -/* - * Copyright (C) 2018 Red Hat, Inc. - */ - -#ifndef __NM_SD_UTILS_SHARED_H__ -#define __NM_SD_UTILS_SHARED_H__ - -/*****************************************************************************/ - -gboolean nm_sd_utils_path_equal(const char *a, const char *b); - -char *nm_sd_utils_path_simplify(char *path, gboolean kill_dots); - -const char *nm_sd_utils_path_startswith(const char *path, const char *prefix); - -/*****************************************************************************/ - -int nm_sd_utils_unbase64char(char ch, gboolean accept_padding_equal); - -int nm_sd_utils_unbase64mem(const char *p, size_t l, gboolean secure, guint8 **mem, size_t *len); - -/*****************************************************************************/ - -int -nm_sd_dns_name_to_wire_format(const char *domain, guint8 *buffer, size_t len, gboolean canonical); - -int nm_sd_dns_name_is_valid(const char *s); -gboolean nm_sd_hostname_is_valid(const char *s, bool allow_trailing_dot); - -char *nm_sd_dns_name_normalize(const char *s); - -/*****************************************************************************/ - -gboolean nm_sd_http_url_is_valid_https(const char *url); - -/*****************************************************************************/ - -int nmtst_systemd_extract_first_word_all(const char *str, char ***out_strv); - -#endif /* __NM_SD_UTILS_SHARED_H__ */ diff --git a/shared/systemd/sd-adapt-shared/architecture.h b/shared/systemd/sd-adapt-shared/architecture.h deleted file mode 100644 index 637892c2..00000000 --- a/shared/systemd/sd-adapt-shared/architecture.h +++ /dev/null @@ -1,3 +0,0 @@ -#pragma once - -/* dummy header */ diff --git a/shared/systemd/sd-adapt-shared/arphrd-list.h b/shared/systemd/sd-adapt-shared/arphrd-list.h deleted file mode 100644 index 637892c2..00000000 --- a/shared/systemd/sd-adapt-shared/arphrd-list.h +++ /dev/null @@ -1,3 +0,0 @@ -#pragma once - -/* dummy header */ diff --git a/shared/systemd/sd-adapt-shared/blockdev-util.h b/shared/systemd/sd-adapt-shared/blockdev-util.h deleted file mode 100644 index 637892c2..00000000 --- a/shared/systemd/sd-adapt-shared/blockdev-util.h +++ /dev/null @@ -1,3 +0,0 @@ -#pragma once - -/* dummy header */ diff --git a/shared/systemd/sd-adapt-shared/build.h b/shared/systemd/sd-adapt-shared/build.h deleted file mode 100644 index 637892c2..00000000 --- a/shared/systemd/sd-adapt-shared/build.h +++ /dev/null @@ -1,3 +0,0 @@ -#pragma once - -/* dummy header */ diff --git a/shared/systemd/sd-adapt-shared/copy.h b/shared/systemd/sd-adapt-shared/copy.h deleted file mode 100644 index 637892c2..00000000 --- a/shared/systemd/sd-adapt-shared/copy.h +++ /dev/null @@ -1,3 +0,0 @@ -#pragma once - -/* dummy header */ diff --git a/shared/systemd/sd-adapt-shared/def.h b/shared/systemd/sd-adapt-shared/def.h deleted file mode 100644 index 637892c2..00000000 --- a/shared/systemd/sd-adapt-shared/def.h +++ /dev/null @@ -1,3 +0,0 @@ -#pragma once - -/* dummy header */ diff --git a/shared/systemd/sd-adapt-shared/dhcp-server-internal.h b/shared/systemd/sd-adapt-shared/dhcp-server-internal.h deleted file mode 100644 index 637892c2..00000000 --- a/shared/systemd/sd-adapt-shared/dhcp-server-internal.h +++ /dev/null @@ -1,3 +0,0 @@ -#pragma once - -/* dummy header */ diff --git a/shared/systemd/sd-adapt-shared/dirent-util.h b/shared/systemd/sd-adapt-shared/dirent-util.h deleted file mode 100644 index 7132dfcc..00000000 --- a/shared/systemd/sd-adapt-shared/dirent-util.h +++ /dev/null @@ -1,5 +0,0 @@ -#pragma once - -/* dummy header */ - -#include "path-util.h" diff --git a/shared/systemd/sd-adapt-shared/errno-list.h b/shared/systemd/sd-adapt-shared/errno-list.h deleted file mode 100644 index 637892c2..00000000 --- a/shared/systemd/sd-adapt-shared/errno-list.h +++ /dev/null @@ -1,3 +0,0 @@ -#pragma once - -/* dummy header */ diff --git a/shared/systemd/sd-adapt-shared/glob-util.h b/shared/systemd/sd-adapt-shared/glob-util.h deleted file mode 100644 index 637892c2..00000000 --- a/shared/systemd/sd-adapt-shared/glob-util.h +++ /dev/null @@ -1,3 +0,0 @@ -#pragma once - -/* dummy header */ diff --git a/shared/systemd/sd-adapt-shared/gunicode.h b/shared/systemd/sd-adapt-shared/gunicode.h deleted file mode 100644 index 637892c2..00000000 --- a/shared/systemd/sd-adapt-shared/gunicode.h +++ /dev/null @@ -1,3 +0,0 @@ -#pragma once - -/* dummy header */ diff --git a/shared/systemd/sd-adapt-shared/idn-util.h b/shared/systemd/sd-adapt-shared/idn-util.h deleted file mode 100644 index 637892c2..00000000 --- a/shared/systemd/sd-adapt-shared/idn-util.h +++ /dev/null @@ -1,3 +0,0 @@ -#pragma once - -/* dummy header */ diff --git a/shared/systemd/sd-adapt-shared/ioprio.h b/shared/systemd/sd-adapt-shared/ioprio.h deleted file mode 100644 index 637892c2..00000000 --- a/shared/systemd/sd-adapt-shared/ioprio.h +++ /dev/null @@ -1,3 +0,0 @@ -#pragma once - -/* dummy header */ diff --git a/shared/systemd/sd-adapt-shared/locale-util.h b/shared/systemd/sd-adapt-shared/locale-util.h deleted file mode 100644 index 637892c2..00000000 --- a/shared/systemd/sd-adapt-shared/locale-util.h +++ /dev/null @@ -1,3 +0,0 @@ -#pragma once - -/* dummy header */ diff --git a/shared/systemd/sd-adapt-shared/memfd-util.h b/shared/systemd/sd-adapt-shared/memfd-util.h deleted file mode 100644 index 637892c2..00000000 --- a/shared/systemd/sd-adapt-shared/memfd-util.h +++ /dev/null @@ -1,3 +0,0 @@ -#pragma once - -/* dummy header */ diff --git a/shared/systemd/sd-adapt-shared/missing_fs.h b/shared/systemd/sd-adapt-shared/missing_fs.h deleted file mode 100644 index 637892c2..00000000 --- a/shared/systemd/sd-adapt-shared/missing_fs.h +++ /dev/null @@ -1,3 +0,0 @@ -#pragma once - -/* dummy header */ diff --git a/shared/systemd/sd-adapt-shared/missing_keyctl.h b/shared/systemd/sd-adapt-shared/missing_keyctl.h deleted file mode 100644 index 637892c2..00000000 --- a/shared/systemd/sd-adapt-shared/missing_keyctl.h +++ /dev/null @@ -1,3 +0,0 @@ -#pragma once - -/* dummy header */ diff --git a/shared/systemd/sd-adapt-shared/missing_magic.h b/shared/systemd/sd-adapt-shared/missing_magic.h deleted file mode 100644 index 637892c2..00000000 --- a/shared/systemd/sd-adapt-shared/missing_magic.h +++ /dev/null @@ -1,3 +0,0 @@ -#pragma once - -/* dummy header */ diff --git a/shared/systemd/sd-adapt-shared/missing_network.h b/shared/systemd/sd-adapt-shared/missing_network.h deleted file mode 100644 index 637892c2..00000000 --- a/shared/systemd/sd-adapt-shared/missing_network.h +++ /dev/null @@ -1,3 +0,0 @@ -#pragma once - -/* dummy header */ diff --git a/shared/systemd/sd-adapt-shared/missing_sched.h b/shared/systemd/sd-adapt-shared/missing_sched.h deleted file mode 100644 index 637892c2..00000000 --- a/shared/systemd/sd-adapt-shared/missing_sched.h +++ /dev/null @@ -1,3 +0,0 @@ -#pragma once - -/* dummy header */ diff --git a/shared/systemd/sd-adapt-shared/missing_timerfd.h b/shared/systemd/sd-adapt-shared/missing_timerfd.h deleted file mode 100644 index 637892c2..00000000 --- a/shared/systemd/sd-adapt-shared/missing_timerfd.h +++ /dev/null @@ -1,3 +0,0 @@ -#pragma once - -/* dummy header */ diff --git a/shared/systemd/sd-adapt-shared/mkdir.h b/shared/systemd/sd-adapt-shared/mkdir.h deleted file mode 100644 index 637892c2..00000000 --- a/shared/systemd/sd-adapt-shared/mkdir.h +++ /dev/null @@ -1,3 +0,0 @@ -#pragma once - -/* dummy header */ diff --git a/shared/systemd/sd-adapt-shared/namespace-util.h b/shared/systemd/sd-adapt-shared/namespace-util.h deleted file mode 100644 index 637892c2..00000000 --- a/shared/systemd/sd-adapt-shared/namespace-util.h +++ /dev/null @@ -1,3 +0,0 @@ -#pragma once - -/* dummy header */ diff --git a/shared/systemd/sd-adapt-shared/nm-sd-adapt-shared.h b/shared/systemd/sd-adapt-shared/nm-sd-adapt-shared.h deleted file mode 100644 index b094ce40..00000000 --- a/shared/systemd/sd-adapt-shared/nm-sd-adapt-shared.h +++ /dev/null @@ -1,221 +0,0 @@ -/* SPDX-License-Identifier: LGPL-2.1-or-later */ -/* - * Copyright (C) 2014 - 2018 Red Hat, Inc. - */ - -#ifndef __NM_SD_ADAPT_SHARED_H__ -#define __NM_SD_ADAPT_SHARED_H__ - -#include "shared/systemd/nm-default-systemd-shared.h" - -#include "nm-glib-aux/nm-logging-fwd.h" - -/*****************************************************************************/ - -/* strerror() is not thread-safe. Patch systemd-sources via a define. */ -#define strerror(errsv) nm_strerror_native(errsv) - -/*****************************************************************************/ - -/* systemd detects whether compiler supports "-Wstringop-truncation" to disable - * the warning at particular places. Since we anyway build with -Wno-pragma, - * we don't do that and just let systemd call - * - * _Pragma("GCC diagnostic ignored \"-Wstringop-truncation\"") - * - * regadless whether that would result in a -Wpragma warning. */ -#define HAVE_WSTRINGOP_TRUNCATION 1 - -/*****************************************************************************/ - -static inline int -_nm_log_get_max_level_realm(void) -{ - /* inline function, to avoid coverity warning about constant expression. */ - return 7 /* LOG_DEBUG */; -} -#define log_get_max_level_realm(realm) _nm_log_get_max_level_realm() - -#define log_internal_realm(level, error, file, line, func, format, ...) \ - ({ \ - const int _nm_e = (error); \ - const NMLogLevel _nm_l = nm_log_level_from_syslog(LOG_PRI(level)); \ - \ - if (_nm_log_enabled_impl(!(NM_THREAD_SAFE_ON_MAIN_THREAD), _nm_l, LOGD_SYSTEMD)) { \ - const char *_nm_location = strrchr(("" file), '/'); \ - \ - _nm_log_impl(_nm_location ? _nm_location + 1 : ("" file), \ - (line), \ - (func), \ - !(NM_THREAD_SAFE_ON_MAIN_THREAD), \ - _nm_l, \ - LOGD_SYSTEMD, \ - _nm_e, \ - NULL, \ - NULL, \ - ("%s" format), \ - "libsystemd: ", \ - ##__VA_ARGS__); \ - } \ - (_nm_e > 0 ? -_nm_e : _nm_e); \ - }) - -#define log_assert_failed(text, file, line, func) \ - G_STMT_START \ - { \ - log_internal(LOG_CRIT, \ - 0, \ - file, \ - line, \ - func, \ - "Assertion '%s' failed at %s:%u, function %s(). Aborting.", \ - text, \ - file, \ - line, \ - func); \ - g_assert_not_reached(); \ - } \ - G_STMT_END - -#define log_assert_failed_unreachable(text, file, line, func) \ - G_STMT_START \ - { \ - log_internal(LOG_CRIT, \ - 0, \ - file, \ - line, \ - func, \ - "Code should not be reached '%s' at %s:%u, function %s(). Aborting.", \ - text, \ - file, \ - line, \ - func); \ - g_assert_not_reached(); \ - } \ - G_STMT_END - -#define log_assert_failed_return(text, file, line, func) \ - ({ \ - log_internal(LOG_DEBUG, \ - 0, \ - file, \ - line, \ - func, \ - "Assertion '%s' failed at %s:%u, function %s(). Ignoring.", \ - text, \ - file, \ - line, \ - func); \ - g_return_if_fail_warning(G_LOG_DOMAIN, G_STRFUNC, text); \ - (void) 0; \ - }) - -/*****************************************************************************/ - -#ifndef VALGRIND - #define VALGRIND 0 -#endif - -#define ENABLE_DEBUG_HASHMAP 0 - -/***************************************************************************** - * The remainder of the header is only enabled when building the systemd code - * itself. - *****************************************************************************/ - -#if (NETWORKMANAGER_COMPILATION) & NM_NETWORKMANAGER_COMPILATION_WITH_SYSTEMD - - #include <sys/syscall.h> - #include <sys/ioctl.h> - #include <pthread.h> - - #define ENABLE_GSHADOW FALSE - - #define HAVE_SECCOMP 0 - -/*****************************************************************************/ - -/* systemd cannot be compiled with "-Wdeclaration-after-statement". In particular - * in combination with assert_cc(). */ -NM_PRAGMA_WARNING_DISABLE("-Wdeclaration-after-statement") - -/*****************************************************************************/ - -struct statx; - -/*****************************************************************************/ - -static inline pid_t -raw_getpid(void) -{ - #if defined(__alpha__) - return (pid_t) syscall(__NR_getxpid); - #else - return (pid_t) syscall(__NR_getpid); - #endif -} - -static inline pid_t -_nm_gettid(void) -{ - return (pid_t) syscall(SYS_gettid); -} - #define gettid() _nm_gettid() - - /* we build with C11 and thus <uchar.h> provides char32_t,char16_t. */ - #define HAVE_CHAR32_T 1 - #define HAVE_CHAR16_T 1 - - #if defined(HAVE_DECL_REALLOCARRAY) && HAVE_DECL_REALLOCARRAY == 1 - #define HAVE_REALLOCARRAY 1 - #else - #define HAVE_REALLOCARRAY 0 - #endif - - #if defined(HAVE_DECL_EXPLICIT_BZERO) && HAVE_DECL_EXPLICIT_BZERO == 1 - #define HAVE_EXPLICIT_BZERO 1 - #else - #define HAVE_EXPLICIT_BZERO 0 - #endif - - #if defined(HAVE_DECL_PIDFD_OPEN) && HAVE_DECL_PIDFD_OPEN == 1 - #define HAVE_PIDFD_OPEN 1 - #else - #define HAVE_PIDFD_OPEN 0 - #endif - - #if defined(HAVE_DECL_PIDFD_SEND_SIGNAL) && HAVE_DECL_PIDFD_SEND_SIGNAL == 1 - #define HAVE_PIDFD_SEND_SIGNAL 1 - #else - #define HAVE_PIDFD_SEND_SIGNAL 0 - #endif - - #if defined(HAVE_DECL_RT_SIGQUEUEINFO) && HAVE_DECL_RT_SIGQUEUEINFO == 1 - #define HAVE_RT_SIGQUEUEINFO 1 - #else - #define HAVE_RT_SIGQUEUEINFO 0 - #endif - - #ifndef __COMPAR_FN_T - #define __COMPAR_FN_T -typedef int (*__compar_fn_t)(const void *, const void *); -typedef __compar_fn_t comparison_fn_t; -typedef int (*__compar_d_fn_t)(const void *, const void *, void *); - #endif - - #ifndef __GLIBC__ -static inline int -__register_atfork(void (*prepare)(void), - void (*parent)(void), - void (*child)(void), - void *dso_handle) -{ - return pthread_atfork(prepare, parent, child); -} - #endif - -#endif /* (NETWORKMANAGER_COMPILATION) & NM_NETWORKMANAGER_COMPILATION_WITH_SYSTEMD */ - -/*****************************************************************************/ - -#endif /* __NM_SD_ADAPT_SHARED_H__ */ diff --git a/shared/systemd/sd-adapt-shared/nulstr-util.h b/shared/systemd/sd-adapt-shared/nulstr-util.h deleted file mode 100644 index 637892c2..00000000 --- a/shared/systemd/sd-adapt-shared/nulstr-util.h +++ /dev/null @@ -1,3 +0,0 @@ -#pragma once - -/* dummy header */ diff --git a/shared/systemd/sd-adapt-shared/raw-clone.h b/shared/systemd/sd-adapt-shared/raw-clone.h deleted file mode 100644 index 637892c2..00000000 --- a/shared/systemd/sd-adapt-shared/raw-clone.h +++ /dev/null @@ -1,3 +0,0 @@ -#pragma once - -/* dummy header */ diff --git a/shared/systemd/sd-adapt-shared/rlimit-util.h b/shared/systemd/sd-adapt-shared/rlimit-util.h deleted file mode 100644 index 637892c2..00000000 --- a/shared/systemd/sd-adapt-shared/rlimit-util.h +++ /dev/null @@ -1,3 +0,0 @@ -#pragma once - -/* dummy header */ diff --git a/shared/systemd/sd-adapt-shared/terminal-util.h b/shared/systemd/sd-adapt-shared/terminal-util.h deleted file mode 100644 index 637892c2..00000000 --- a/shared/systemd/sd-adapt-shared/terminal-util.h +++ /dev/null @@ -1,3 +0,0 @@ -#pragma once - -/* dummy header */ diff --git a/shared/systemd/sd-adapt-shared/unaligned.h b/shared/systemd/sd-adapt-shared/unaligned.h deleted file mode 100644 index ac1a6928..00000000 --- a/shared/systemd/sd-adapt-shared/unaligned.h +++ /dev/null @@ -1,3 +0,0 @@ -#pragma once - -#include "nm-std-aux/unaligned.h" diff --git a/shared/systemd/sd-adapt-shared/user-util.h b/shared/systemd/sd-adapt-shared/user-util.h deleted file mode 100644 index 637892c2..00000000 --- a/shared/systemd/sd-adapt-shared/user-util.h +++ /dev/null @@ -1,3 +0,0 @@ -#pragma once - -/* dummy header */ diff --git a/shared/systemd/sd-adapt-shared/virt.h b/shared/systemd/sd-adapt-shared/virt.h deleted file mode 100644 index 637892c2..00000000 --- a/shared/systemd/sd-adapt-shared/virt.h +++ /dev/null @@ -1,3 +0,0 @@ -#pragma once - -/* dummy header */ diff --git a/shared/systemd/src/basic/alloc-util.c b/shared/systemd/src/basic/alloc-util.c deleted file mode 100644 index 7f7eb433..00000000 --- a/shared/systemd/src/basic/alloc-util.c +++ /dev/null @@ -1,114 +0,0 @@ -/* SPDX-License-Identifier: LGPL-2.1-or-later */ - -#include "nm-sd-adapt-shared.h" - -#include <malloc.h> -#include <stdint.h> -#include <string.h> - -#include "alloc-util.h" -#include "macro.h" -#include "memory-util.h" - -void* memdup(const void *p, size_t l) { - void *ret; - - assert(l == 0 || p); - - ret = malloc(l ?: 1); - if (!ret) - return NULL; - - memcpy(ret, p, l); - return ret; -} - -void* memdup_suffix0(const void *p, size_t l) { - void *ret; - - assert(l == 0 || p); - - /* The same as memdup() but place a safety NUL byte after the allocated memory */ - - if (_unlikely_(l == SIZE_MAX)) /* prevent overflow */ - return NULL; - - ret = malloc(l + 1); - if (!ret) - return NULL; - - *((uint8_t*) mempcpy(ret, p, l)) = 0; - return ret; -} - -void* greedy_realloc(void **p, size_t *allocated, size_t need, size_t size) { - size_t a, newalloc; - void *q; - - assert(p); - assert(allocated); - - if (*allocated >= need) - return *p; - - if (_unlikely_(need > SIZE_MAX/2)) /* Overflow check */ - return NULL; - - newalloc = need * 2; - if (size_multiply_overflow(newalloc, size)) - return NULL; - - a = newalloc * size; - if (a < 64) /* Allocate at least 64 bytes */ - a = 64; - - q = realloc(*p, a); - if (!q) - return NULL; - - if (size > 0) { - size_t bn; - - /* Adjust for the 64 byte minimum */ - newalloc = a / size; - - bn = malloc_usable_size(q) / size; - if (bn > newalloc) { - void *qq; - - /* The actual size allocated is larger than what we asked for. Let's call realloc() again to - * take possession of the extra space. This should be cheap, since libc doesn't have to move - * the memory for this. */ - - qq = reallocarray(q, bn, size); - if (_likely_(qq)) { - *p = qq; - *allocated = bn; - return qq; - } - } - } - - *p = q; - *allocated = newalloc; - return q; -} - -void* greedy_realloc0(void **p, size_t *allocated, size_t need, size_t size) { - size_t prev; - uint8_t *q; - - assert(p); - assert(allocated); - - prev = *allocated; - - q = greedy_realloc(p, allocated, need, size); - if (!q) - return NULL; - - if (*allocated > prev) - memzero(q + prev * size, (*allocated - prev) * size); - - return q; -} diff --git a/shared/systemd/src/basic/alloc-util.h b/shared/systemd/src/basic/alloc-util.h deleted file mode 100644 index f3e192dd..00000000 --- a/shared/systemd/src/basic/alloc-util.h +++ /dev/null @@ -1,174 +0,0 @@ -/* SPDX-License-Identifier: LGPL-2.1-or-later */ -#pragma once - -#include <alloca.h> -#include <stddef.h> -#include <stdlib.h> -#include <string.h> - -#include "macro.h" - -#if HAS_FEATURE_MEMORY_SANITIZER -# include <sanitizer/msan_interface.h> -#endif - -typedef void (*free_func_t)(void *p); - -/* If for some reason more than 4M are allocated on the stack, let's abort immediately. It's better than - * proceeding and smashing the stack limits. Note that by default RLIMIT_STACK is 8M on Linux. */ -#define ALLOCA_MAX (4U*1024U*1024U) - -#define new(t, n) ((t*) malloc_multiply(sizeof(t), (n))) - -#define new0(t, n) ((t*) calloc((n) ?: 1, sizeof(t))) - -#define newa(t, n) \ - ({ \ - size_t _n_ = n; \ - assert(!size_multiply_overflow(sizeof(t), _n_)); \ - assert(sizeof(t)*_n_ <= ALLOCA_MAX); \ - (t*) alloca((sizeof(t)*_n_) ?: 1); \ - }) - -#define newa0(t, n) \ - ({ \ - size_t _n_ = n; \ - assert(!size_multiply_overflow(sizeof(t), _n_)); \ - assert(sizeof(t)*_n_ <= ALLOCA_MAX); \ - (t*) alloca0((sizeof(t)*_n_) ?: 1); \ - }) - -#define newdup(t, p, n) ((t*) memdup_multiply(p, sizeof(t), (n))) - -#define newdup_suffix0(t, p, n) ((t*) memdup_suffix0_multiply(p, sizeof(t), (n))) - -#define malloc0(n) (calloc(1, (n) ?: 1)) - -static inline void *mfree(void *memory) { - free(memory); - return NULL; -} - -#define free_and_replace(a, b) \ - ({ \ - free(a); \ - (a) = (b); \ - (b) = NULL; \ - 0; \ - }) - -void* memdup(const void *p, size_t l) _alloc_(2); -void* memdup_suffix0(const void *p, size_t l); /* We can't use _alloc_() here, since we return a buffer one byte larger than the specified size */ - -#define memdupa(p, l) \ - ({ \ - void *_q_; \ - size_t _l_ = l; \ - assert(_l_ <= ALLOCA_MAX); \ - _q_ = alloca(_l_ ?: 1); \ - memcpy(_q_, p, _l_); \ - }) - -#define memdupa_suffix0(p, l) \ - ({ \ - void *_q_; \ - size_t _l_ = l; \ - assert(_l_ <= ALLOCA_MAX); \ - _q_ = alloca(_l_ + 1); \ - ((uint8_t*) _q_)[_l_] = 0; \ - memcpy(_q_, p, _l_); \ - }) - -static inline void freep(void *p) { - free(*(void**) p); -} - -#define _cleanup_free_ _cleanup_(freep) - -static inline bool size_multiply_overflow(size_t size, size_t need) { - return _unlikely_(need != 0 && size > (SIZE_MAX / need)); -} - -_malloc_ _alloc_(1, 2) static inline void *malloc_multiply(size_t size, size_t need) { - if (size_multiply_overflow(size, need)) - return NULL; - - return malloc(size * need ?: 1); -} - -#if !HAVE_REALLOCARRAY -_alloc_(2, 3) static inline void *reallocarray(void *p, size_t need, size_t size) { - if (size_multiply_overflow(size, need)) - return NULL; - - return realloc(p, size * need ?: 1); -} -#endif - -_alloc_(2, 3) static inline void *memdup_multiply(const void *p, size_t size, size_t need) { - if (size_multiply_overflow(size, need)) - return NULL; - - return memdup(p, size * need); -} - -/* Note that we can't decorate this function with _alloc_() since the returned memory area is one byte larger - * than the product of its parameters. */ -static inline void *memdup_suffix0_multiply(const void *p, size_t size, size_t need) { - if (size_multiply_overflow(size, need)) - return NULL; - - return memdup_suffix0(p, size * need); -} - -void* greedy_realloc(void **p, size_t *allocated, size_t need, size_t size); -void* greedy_realloc0(void **p, size_t *allocated, size_t need, size_t size); - -#define GREEDY_REALLOC(array, allocated, need) \ - greedy_realloc((void**) &(array), &(allocated), (need), sizeof((array)[0])) - -#define GREEDY_REALLOC0(array, allocated, need) \ - greedy_realloc0((void**) &(array), &(allocated), (need), sizeof((array)[0])) - -#define alloca0(n) \ - ({ \ - char *_new_; \ - size_t _len_ = n; \ - assert(_len_ <= ALLOCA_MAX); \ - _new_ = alloca(_len_ ?: 1); \ - (void *) memset(_new_, 0, _len_); \ - }) - -/* It's not clear what alignment glibc/gcc alloca() guarantee, hence provide a guaranteed safe version */ -#define alloca_align(size, align) \ - ({ \ - void *_ptr_; \ - size_t _mask_ = (align) - 1; \ - size_t _size_ = size; \ - assert(_size_ <= ALLOCA_MAX); \ - _ptr_ = alloca((_size_ + _mask_) ?: 1); \ - (void*)(((uintptr_t)_ptr_ + _mask_) & ~_mask_); \ - }) - -#define alloca0_align(size, align) \ - ({ \ - void *_new_; \ - size_t _xsize_ = (size); \ - _new_ = alloca_align(_xsize_, (align)); \ - (void*)memset(_new_, 0, _xsize_); \ - }) - -/* Takes inspiration from Rust's Option::take() method: reads and returns a pointer, but at the same time - * resets it to NULL. See: https://doc.rust-lang.org/std/option/enum.Option.html#method.take */ -#define TAKE_PTR(ptr) \ - ({ \ - typeof(ptr) _ptr_ = (ptr); \ - (ptr) = NULL; \ - _ptr_; \ - }) - -#if HAS_FEATURE_MEMORY_SANITIZER -# define msan_unpoison(r, s) __msan_unpoison(r, s) -#else -# define msan_unpoison(r, s) -#endif diff --git a/shared/systemd/src/basic/async.h b/shared/systemd/src/basic/async.h deleted file mode 100644 index e0bbaa56..00000000 --- a/shared/systemd/src/basic/async.h +++ /dev/null @@ -1,13 +0,0 @@ -/* SPDX-License-Identifier: LGPL-2.1-or-later */ -#pragma once - -#include <sys/types.h> - -#include "macro.h" - -int asynchronous_job(void* (*func)(void *p), void *arg); - -int asynchronous_sync(pid_t *ret_pid); -int asynchronous_close(int fd); - -DEFINE_TRIVIAL_CLEANUP_FUNC(int, asynchronous_close); diff --git a/shared/systemd/src/basic/cgroup-util.h b/shared/systemd/src/basic/cgroup-util.h deleted file mode 100644 index bdc0d0d0..00000000 --- a/shared/systemd/src/basic/cgroup-util.h +++ /dev/null @@ -1,290 +0,0 @@ -/* SPDX-License-Identifier: LGPL-2.1-or-later */ -#pragma once - -#include <dirent.h> -#include <stdbool.h> -#include <stdint.h> -#include <stdio.h> -#include <sys/statfs.h> -#include <sys/types.h> - -#include "def.h" -#include "set.h" - -#define SYSTEMD_CGROUP_CONTROLLER_LEGACY "name=systemd" -#define SYSTEMD_CGROUP_CONTROLLER_HYBRID "name=unified" -#define SYSTEMD_CGROUP_CONTROLLER "_systemd" - -/* An enum of well known cgroup controllers */ -typedef enum CGroupController { - /* Original cgroup controllers */ - CGROUP_CONTROLLER_CPU, - CGROUP_CONTROLLER_CPUACCT, /* v1 only */ - CGROUP_CONTROLLER_CPUSET, /* v2 only */ - CGROUP_CONTROLLER_IO, /* v2 only */ - CGROUP_CONTROLLER_BLKIO, /* v1 only */ - CGROUP_CONTROLLER_MEMORY, - CGROUP_CONTROLLER_DEVICES, /* v1 only */ - CGROUP_CONTROLLER_PIDS, - - /* BPF-based pseudo-controllers, v2 only */ - CGROUP_CONTROLLER_BPF_FIREWALL, - CGROUP_CONTROLLER_BPF_DEVICES, - - _CGROUP_CONTROLLER_MAX, - _CGROUP_CONTROLLER_INVALID = -1, -} CGroupController; - -#define CGROUP_CONTROLLER_TO_MASK(c) (1U << (c)) - -/* A bit mask of well known cgroup controllers */ -typedef enum CGroupMask { - CGROUP_MASK_CPU = CGROUP_CONTROLLER_TO_MASK(CGROUP_CONTROLLER_CPU), - CGROUP_MASK_CPUACCT = CGROUP_CONTROLLER_TO_MASK(CGROUP_CONTROLLER_CPUACCT), - CGROUP_MASK_CPUSET = CGROUP_CONTROLLER_TO_MASK(CGROUP_CONTROLLER_CPUSET), - CGROUP_MASK_IO = CGROUP_CONTROLLER_TO_MASK(CGROUP_CONTROLLER_IO), - CGROUP_MASK_BLKIO = CGROUP_CONTROLLER_TO_MASK(CGROUP_CONTROLLER_BLKIO), - CGROUP_MASK_MEMORY = CGROUP_CONTROLLER_TO_MASK(CGROUP_CONTROLLER_MEMORY), - CGROUP_MASK_DEVICES = CGROUP_CONTROLLER_TO_MASK(CGROUP_CONTROLLER_DEVICES), - CGROUP_MASK_PIDS = CGROUP_CONTROLLER_TO_MASK(CGROUP_CONTROLLER_PIDS), - CGROUP_MASK_BPF_FIREWALL = CGROUP_CONTROLLER_TO_MASK(CGROUP_CONTROLLER_BPF_FIREWALL), - CGROUP_MASK_BPF_DEVICES = CGROUP_CONTROLLER_TO_MASK(CGROUP_CONTROLLER_BPF_DEVICES), - - /* All real cgroup v1 controllers */ - CGROUP_MASK_V1 = CGROUP_MASK_CPU|CGROUP_MASK_CPUACCT|CGROUP_MASK_BLKIO|CGROUP_MASK_MEMORY|CGROUP_MASK_DEVICES|CGROUP_MASK_PIDS, - - /* All real cgroup v2 controllers */ - CGROUP_MASK_V2 = CGROUP_MASK_CPU|CGROUP_MASK_CPUSET|CGROUP_MASK_IO|CGROUP_MASK_MEMORY|CGROUP_MASK_PIDS, - - /* All cgroup v2 BPF pseudo-controllers */ - CGROUP_MASK_BPF = CGROUP_MASK_BPF_FIREWALL|CGROUP_MASK_BPF_DEVICES, - - _CGROUP_MASK_ALL = CGROUP_CONTROLLER_TO_MASK(_CGROUP_CONTROLLER_MAX) - 1 -} CGroupMask; - -static inline CGroupMask CGROUP_MASK_EXTEND_JOINED(CGroupMask mask) { - /* We always mount "cpu" and "cpuacct" in the same hierarchy. Hence, when one bit is set also set the other */ - - if (mask & (CGROUP_MASK_CPU|CGROUP_MASK_CPUACCT)) - mask |= (CGROUP_MASK_CPU|CGROUP_MASK_CPUACCT); - - return mask; -} - -CGroupMask get_cpu_accounting_mask(void); -bool cpu_accounting_is_cheap(void); - -/* Special values for all weight knobs on unified hierarchy */ -#define CGROUP_WEIGHT_INVALID ((uint64_t) -1) -#define CGROUP_WEIGHT_MIN UINT64_C(1) -#define CGROUP_WEIGHT_MAX UINT64_C(10000) -#define CGROUP_WEIGHT_DEFAULT UINT64_C(100) - -#define CGROUP_LIMIT_MIN UINT64_C(0) -#define CGROUP_LIMIT_MAX ((uint64_t) -1) - -static inline bool CGROUP_WEIGHT_IS_OK(uint64_t x) { - return - x == CGROUP_WEIGHT_INVALID || - (x >= CGROUP_WEIGHT_MIN && x <= CGROUP_WEIGHT_MAX); -} - -/* IO limits on unified hierarchy */ -typedef enum CGroupIOLimitType { - CGROUP_IO_RBPS_MAX, - CGROUP_IO_WBPS_MAX, - CGROUP_IO_RIOPS_MAX, - CGROUP_IO_WIOPS_MAX, - - _CGROUP_IO_LIMIT_TYPE_MAX, - _CGROUP_IO_LIMIT_TYPE_INVALID = -1 -} CGroupIOLimitType; - -extern const uint64_t cgroup_io_limit_defaults[_CGROUP_IO_LIMIT_TYPE_MAX]; - -const char* cgroup_io_limit_type_to_string(CGroupIOLimitType t) _const_; -CGroupIOLimitType cgroup_io_limit_type_from_string(const char *s) _pure_; - -/* Special values for the cpu.shares attribute */ -#define CGROUP_CPU_SHARES_INVALID ((uint64_t) -1) -#define CGROUP_CPU_SHARES_MIN UINT64_C(2) -#define CGROUP_CPU_SHARES_MAX UINT64_C(262144) -#define CGROUP_CPU_SHARES_DEFAULT UINT64_C(1024) - -static inline bool CGROUP_CPU_SHARES_IS_OK(uint64_t x) { - return - x == CGROUP_CPU_SHARES_INVALID || - (x >= CGROUP_CPU_SHARES_MIN && x <= CGROUP_CPU_SHARES_MAX); -} - -/* Special values for the blkio.weight attribute */ -#define CGROUP_BLKIO_WEIGHT_INVALID ((uint64_t) -1) -#define CGROUP_BLKIO_WEIGHT_MIN UINT64_C(10) -#define CGROUP_BLKIO_WEIGHT_MAX UINT64_C(1000) -#define CGROUP_BLKIO_WEIGHT_DEFAULT UINT64_C(500) - -static inline bool CGROUP_BLKIO_WEIGHT_IS_OK(uint64_t x) { - return - x == CGROUP_BLKIO_WEIGHT_INVALID || - (x >= CGROUP_BLKIO_WEIGHT_MIN && x <= CGROUP_BLKIO_WEIGHT_MAX); -} - -typedef enum CGroupUnified { - CGROUP_UNIFIED_UNKNOWN = -1, - CGROUP_UNIFIED_NONE = 0, /* Both systemd and controllers on legacy */ - CGROUP_UNIFIED_SYSTEMD = 1, /* Only systemd on unified */ - CGROUP_UNIFIED_ALL = 2, /* Both systemd and controllers on unified */ -} CGroupUnified; - -/* - * General rules: - * - * We accept named hierarchies in the syntax "foo" and "name=foo". - * - * We expect that named hierarchies do not conflict in name with a - * kernel hierarchy, modulo the "name=" prefix. - * - * We always generate "normalized" controller names, i.e. without the - * "name=" prefix. - * - * We require absolute cgroup paths. When returning, we will always - * generate paths with multiple adjacent / removed. - */ - -int cg_enumerate_processes(const char *controller, const char *path, FILE **_f); -int cg_read_pid(FILE *f, pid_t *_pid); -int cg_read_event(const char *controller, const char *path, const char *event, - char **val); - -int cg_enumerate_subgroups(const char *controller, const char *path, DIR **_d); -int cg_read_subgroup(DIR *d, char **fn); - -typedef enum CGroupFlags { - CGROUP_SIGCONT = 1 << 0, - CGROUP_IGNORE_SELF = 1 << 1, - CGROUP_REMOVE = 1 << 2, -} CGroupFlags; - -typedef int (*cg_kill_log_func_t)(pid_t pid, int sig, void *userdata); - -int cg_kill(const char *controller, const char *path, int sig, CGroupFlags flags, Set *s, cg_kill_log_func_t kill_log, void *userdata); -int cg_kill_recursive(const char *controller, const char *path, int sig, CGroupFlags flags, Set *s, cg_kill_log_func_t kill_log, void *userdata); - -int cg_split_spec(const char *spec, char **ret_controller, char **ret_path); -int cg_mangle_path(const char *path, char **result); - -int cg_get_path(const char *controller, const char *path, const char *suffix, char **fs); -int cg_get_path_and_check(const char *controller, const char *path, const char *suffix, char **fs); - -int cg_pid_get_path(const char *controller, pid_t pid, char **path); - -int cg_rmdir(const char *controller, const char *path); - -typedef enum { - CG_KEY_MODE_GRACEFUL = 1 << 0, -} CGroupKeyMode; - -int cg_set_attribute(const char *controller, const char *path, const char *attribute, const char *value); -int cg_get_attribute(const char *controller, const char *path, const char *attribute, char **ret); -int cg_get_keyed_attribute_full(const char *controller, const char *path, const char *attribute, char **keys, char **values, CGroupKeyMode mode); - -static inline int cg_get_keyed_attribute( - const char *controller, - const char *path, - const char *attribute, - char **keys, - char **ret_values) { - return cg_get_keyed_attribute_full(controller, path, attribute, keys, ret_values, 0); -} - -static inline int cg_get_keyed_attribute_graceful( - const char *controller, - const char *path, - const char *attribute, - char **keys, - char **ret_values) { - return cg_get_keyed_attribute_full(controller, path, attribute, keys, ret_values, CG_KEY_MODE_GRACEFUL); -} - -int cg_get_attribute_as_uint64(const char *controller, const char *path, const char *attribute, uint64_t *ret); - -/* Does a parse_boolean() on the attribute contents and sets ret accordingly */ -int cg_get_attribute_as_bool(const char *controller, const char *path, const char *attribute, bool *ret); - -int cg_set_access(const char *controller, const char *path, uid_t uid, gid_t gid); - -int cg_set_xattr(const char *controller, const char *path, const char *name, const void *value, size_t size, int flags); -int cg_get_xattr(const char *controller, const char *path, const char *name, void *value, size_t size); -int cg_get_xattr_malloc(const char *controller, const char *path, const char *name, char **ret); -int cg_remove_xattr(const char *controller, const char *path, const char *name); - -int cg_install_release_agent(const char *controller, const char *agent); -int cg_uninstall_release_agent(const char *controller); - -int cg_is_empty(const char *controller, const char *path); -int cg_is_empty_recursive(const char *controller, const char *path); - -int cg_get_root_path(char **path); - -int cg_path_get_session(const char *path, char **session); -int cg_path_get_owner_uid(const char *path, uid_t *uid); -int cg_path_get_unit(const char *path, char **unit); -int cg_path_get_user_unit(const char *path, char **unit); -int cg_path_get_machine_name(const char *path, char **machine); -int cg_path_get_slice(const char *path, char **slice); -int cg_path_get_user_slice(const char *path, char **slice); - -int cg_shift_path(const char *cgroup, const char *cached_root, const char **shifted); -int cg_pid_get_path_shifted(pid_t pid, const char *cached_root, char **cgroup); - -int cg_pid_get_session(pid_t pid, char **session); -int cg_pid_get_owner_uid(pid_t pid, uid_t *uid); -int cg_pid_get_unit(pid_t pid, char **unit); -int cg_pid_get_user_unit(pid_t pid, char **unit); -int cg_pid_get_machine_name(pid_t pid, char **machine); -int cg_pid_get_slice(pid_t pid, char **slice); -int cg_pid_get_user_slice(pid_t pid, char **slice); - -int cg_path_decode_unit(const char *cgroup, char **unit); - -char *cg_escape(const char *p); -char *cg_unescape(const char *p) _pure_; - -bool cg_controller_is_valid(const char *p); - -int cg_slice_to_path(const char *unit, char **ret); - -typedef const char* (*cg_migrate_callback_t)(CGroupMask mask, void *userdata); - -int cg_mask_supported(CGroupMask *ret); -int cg_mask_from_string(const char *s, CGroupMask *ret); -int cg_mask_to_string(CGroupMask mask, char **ret); - -int cg_kernel_controllers(Set **controllers); - -bool cg_ns_supported(void); -bool cg_freezer_supported(void); - -int cg_all_unified(void); -int cg_hybrid_unified(void); -int cg_unified_controller(const char *controller); -int cg_unified_cached(bool flush); -static inline int cg_unified(void) { - return cg_unified_cached(true); -} - -const char* cgroup_controller_to_string(CGroupController c) _const_; -CGroupController cgroup_controller_from_string(const char *s) _pure_; - -bool is_cgroup_fs(const struct statfs *s); -bool fd_is_cgroup_fs(int fd); - -typedef enum ManagedOOMMode { - MANAGED_OOM_AUTO, - MANAGED_OOM_KILL, - _MANAGED_OOM_MODE_MAX, - _MANAGED_OOM_MODE_INVALID = -1, -} ManagedOOMMode; - -const char* managed_oom_mode_to_string(ManagedOOMMode m) _const_; -ManagedOOMMode managed_oom_mode_from_string(const char *s) _pure_; diff --git a/shared/systemd/src/basic/env-file.c b/shared/systemd/src/basic/env-file.c deleted file mode 100644 index 568b742f..00000000 --- a/shared/systemd/src/basic/env-file.c +++ /dev/null @@ -1,571 +0,0 @@ -/* SPDX-License-Identifier: LGPL-2.1-or-later */ - -#include "nm-sd-adapt-shared.h" - -#include "alloc-util.h" -#include "env-file.h" -#include "env-util.h" -#include "escape.h" -#include "fd-util.h" -#include "fileio.h" -#include "fs-util.h" -#include "string-util.h" -#include "strv.h" -#include "tmpfile-util.h" -#include "utf8.h" - -static int parse_env_file_internal( - FILE *f, - const char *fname, - int (*push) (const char *filename, unsigned line, - const char *key, char *value, void *userdata, int *n_pushed), - void *userdata, - int *n_pushed) { - - size_t key_alloc = 0, n_key = 0, value_alloc = 0, n_value = 0, last_value_whitespace = (size_t) -1, last_key_whitespace = (size_t) -1; - _cleanup_free_ char *contents = NULL, *key = NULL, *value = NULL; - unsigned line = 1; - char *p; - int r; - - enum { - PRE_KEY, - KEY, - PRE_VALUE, - VALUE, - VALUE_ESCAPE, - SINGLE_QUOTE_VALUE, - DOUBLE_QUOTE_VALUE, - DOUBLE_QUOTE_VALUE_ESCAPE, - COMMENT, - COMMENT_ESCAPE - } state = PRE_KEY; - - if (f) - r = read_full_stream(f, &contents, NULL); - else - r = read_full_file(fname, &contents, NULL); - if (r < 0) - return r; - - for (p = contents; *p; p++) { - char c = *p; - - switch (state) { - - case PRE_KEY: - if (strchr(COMMENTS, c)) - state = COMMENT; - else if (!strchr(WHITESPACE, c)) { - state = KEY; - last_key_whitespace = (size_t) -1; - - if (!GREEDY_REALLOC(key, key_alloc, n_key+2)) - return -ENOMEM; - - key[n_key++] = c; - } - break; - - case KEY: - if (strchr(NEWLINE, c)) { - state = PRE_KEY; - line++; - n_key = 0; - } else if (c == '=') { - state = PRE_VALUE; - last_value_whitespace = (size_t) -1; - } else { - if (!strchr(WHITESPACE, c)) - last_key_whitespace = (size_t) -1; - else if (last_key_whitespace == (size_t) -1) - last_key_whitespace = n_key; - - if (!GREEDY_REALLOC(key, key_alloc, n_key+2)) - return -ENOMEM; - - key[n_key++] = c; - } - - break; - - case PRE_VALUE: - if (strchr(NEWLINE, c)) { - state = PRE_KEY; - line++; - key[n_key] = 0; - - if (value) - value[n_value] = 0; - - /* strip trailing whitespace from key */ - if (last_key_whitespace != (size_t) -1) - key[last_key_whitespace] = 0; - - r = push(fname, line, key, value, userdata, n_pushed); - if (r < 0) - return r; - - n_key = 0; - value = NULL; - value_alloc = n_value = 0; - - } else if (c == '\'') - state = SINGLE_QUOTE_VALUE; - else if (c == '"') - state = DOUBLE_QUOTE_VALUE; - else if (c == '\\') - state = VALUE_ESCAPE; - else if (!strchr(WHITESPACE, c)) { - state = VALUE; - - if (!GREEDY_REALLOC(value, value_alloc, n_value+2)) - return -ENOMEM; - - value[n_value++] = c; - } - - break; - - case VALUE: - if (strchr(NEWLINE, c)) { - state = PRE_KEY; - line++; - - key[n_key] = 0; - - if (value) - value[n_value] = 0; - - /* Chomp off trailing whitespace from value */ - if (last_value_whitespace != (size_t) -1) - value[last_value_whitespace] = 0; - - /* strip trailing whitespace from key */ - if (last_key_whitespace != (size_t) -1) - key[last_key_whitespace] = 0; - - r = push(fname, line, key, value, userdata, n_pushed); - if (r < 0) - return r; - - n_key = 0; - value = NULL; - value_alloc = n_value = 0; - - } else if (c == '\\') { - state = VALUE_ESCAPE; - last_value_whitespace = (size_t) -1; - } else { - if (!strchr(WHITESPACE, c)) - last_value_whitespace = (size_t) -1; - else if (last_value_whitespace == (size_t) -1) - last_value_whitespace = n_value; - - if (!GREEDY_REALLOC(value, value_alloc, n_value+2)) - return -ENOMEM; - - value[n_value++] = c; - } - - break; - - case VALUE_ESCAPE: - state = VALUE; - - if (!strchr(NEWLINE, c)) { - /* Escaped newlines we eat up entirely */ - if (!GREEDY_REALLOC(value, value_alloc, n_value+2)) - return -ENOMEM; - - value[n_value++] = c; - } - break; - - case SINGLE_QUOTE_VALUE: - if (c == '\'') - state = PRE_VALUE; - else { - if (!GREEDY_REALLOC(value, value_alloc, n_value+2)) - return -ENOMEM; - - value[n_value++] = c; - } - - break; - - case DOUBLE_QUOTE_VALUE: - if (c == '"') - state = PRE_VALUE; - else if (c == '\\') - state = DOUBLE_QUOTE_VALUE_ESCAPE; - else { - if (!GREEDY_REALLOC(value, value_alloc, n_value+2)) - return -ENOMEM; - - value[n_value++] = c; - } - - break; - - case DOUBLE_QUOTE_VALUE_ESCAPE: - state = DOUBLE_QUOTE_VALUE; - - if (strchr(SHELL_NEED_ESCAPE, c)) { - /* If this is a char that needs escaping, just unescape it. */ - if (!GREEDY_REALLOC(value, value_alloc, n_value+2)) - return -ENOMEM; - value[n_value++] = c; - } else if (c != '\n') { - /* If other char than what needs escaping, keep the "\" in place, like the - * real shell does. */ - if (!GREEDY_REALLOC(value, value_alloc, n_value+3)) - return -ENOMEM; - value[n_value++] = '\\'; - value[n_value++] = c; - } - - /* Escaped newlines (aka "continuation lines") are eaten up entirely */ - break; - - case COMMENT: - if (c == '\\') - state = COMMENT_ESCAPE; - else if (strchr(NEWLINE, c)) { - state = PRE_KEY; - line++; - } - break; - - case COMMENT_ESCAPE: - state = COMMENT; - break; - } - } - - if (IN_SET(state, - PRE_VALUE, - VALUE, - VALUE_ESCAPE, - SINGLE_QUOTE_VALUE, - DOUBLE_QUOTE_VALUE, - DOUBLE_QUOTE_VALUE_ESCAPE)) { - - key[n_key] = 0; - - if (value) - value[n_value] = 0; - - if (state == VALUE) - if (last_value_whitespace != (size_t) -1) - value[last_value_whitespace] = 0; - - /* strip trailing whitespace from key */ - if (last_key_whitespace != (size_t) -1) - key[last_key_whitespace] = 0; - - r = push(fname, line, key, value, userdata, n_pushed); - if (r < 0) - return r; - - value = NULL; - } - - return 0; -} - -static int check_utf8ness_and_warn( - const char *filename, unsigned line, - const char *key, char *value) { - - if (!utf8_is_valid(key)) { - _cleanup_free_ char *p = NULL; - - p = utf8_escape_invalid(key); - return log_error_errno(SYNTHETIC_ERRNO(EINVAL), - "%s:%u: invalid UTF-8 in key '%s', ignoring.", - strna(filename), line, p); - } - - if (value && !utf8_is_valid(value)) { - _cleanup_free_ char *p = NULL; - - p = utf8_escape_invalid(value); - return log_error_errno(SYNTHETIC_ERRNO(EINVAL), - "%s:%u: invalid UTF-8 value for key %s: '%s', ignoring.", - strna(filename), line, key, p); - } - - return 0; -} - -static int parse_env_file_push( - const char *filename, unsigned line, - const char *key, char *value, - void *userdata, - int *n_pushed) { - - const char *k; - va_list aq, *ap = userdata; - int r; - - r = check_utf8ness_and_warn(filename, line, key, value); - if (r < 0) - return r; - - va_copy(aq, *ap); - - while ((k = va_arg(aq, const char *))) { - char **v; - - v = va_arg(aq, char **); - - if (streq(key, k)) { - va_end(aq); - free(*v); - *v = value; - - if (n_pushed) - (*n_pushed)++; - - return 1; - } - } - - va_end(aq); - free(value); - - return 0; -} - -int parse_env_filev( - FILE *f, - const char *fname, - va_list ap) { - - int r, n_pushed = 0; - va_list aq; - - va_copy(aq, ap); - r = parse_env_file_internal(f, fname, parse_env_file_push, &aq, &n_pushed); - va_end(aq); - if (r < 0) - return r; - - return n_pushed; -} - -int parse_env_file_sentinel( - FILE *f, - const char *fname, - ...) { - - va_list ap; - int r; - - va_start(ap, fname); - r = parse_env_filev(f, fname, ap); - va_end(ap); - - return r; -} - -#if 0 /* NM_IGNORED */ -static int load_env_file_push( - const char *filename, unsigned line, - const char *key, char *value, - void *userdata, - int *n_pushed) { - char ***m = userdata; - char *p; - int r; - - r = check_utf8ness_and_warn(filename, line, key, value); - if (r < 0) - return r; - - p = strjoin(key, "=", value); - if (!p) - return -ENOMEM; - - r = strv_env_replace(m, p); - if (r < 0) { - free(p); - return r; - } - - if (n_pushed) - (*n_pushed)++; - - free(value); - return 0; -} - -int load_env_file(FILE *f, const char *fname, char ***rl) { - char **m = NULL; - int r; - - r = parse_env_file_internal(f, fname, load_env_file_push, &m, NULL); - if (r < 0) { - strv_free(m); - return r; - } - - *rl = m; - return 0; -} - -static int load_env_file_push_pairs( - const char *filename, unsigned line, - const char *key, char *value, - void *userdata, - int *n_pushed) { - char ***m = userdata; - int r; - - r = check_utf8ness_and_warn(filename, line, key, value); - if (r < 0) - return r; - - r = strv_extend(m, key); - if (r < 0) - return -ENOMEM; - - if (!value) { - r = strv_extend(m, ""); - if (r < 0) - return -ENOMEM; - } else { - r = strv_push(m, value); - if (r < 0) - return r; - } - - if (n_pushed) - (*n_pushed)++; - - return 0; -} - -int load_env_file_pairs(FILE *f, const char *fname, char ***rl) { - char **m = NULL; - int r; - - r = parse_env_file_internal(f, fname, load_env_file_push_pairs, &m, NULL); - if (r < 0) { - strv_free(m); - return r; - } - - *rl = m; - return 0; -} - -static int merge_env_file_push( - const char *filename, unsigned line, - const char *key, char *value, - void *userdata, - int *n_pushed) { - - char ***env = userdata; - char *expanded_value; - - assert(env); - - if (!value) { - log_error("%s:%u: invalid syntax (around \"%s\"), ignoring.", strna(filename), line, key); - return 0; - } - - if (!env_name_is_valid(key)) { - log_error("%s:%u: invalid variable name \"%s\", ignoring.", strna(filename), line, key); - free(value); - return 0; - } - - expanded_value = replace_env(value, *env, - REPLACE_ENV_USE_ENVIRONMENT| - REPLACE_ENV_ALLOW_BRACELESS| - REPLACE_ENV_ALLOW_EXTENDED); - if (!expanded_value) - return -ENOMEM; - - free_and_replace(value, expanded_value); - - log_debug("%s:%u: setting %s=%s", filename, line, key, value); - - return load_env_file_push(filename, line, key, value, env, n_pushed); -} - -int merge_env_file( - char ***env, - FILE *f, - const char *fname) { - - /* NOTE: this function supports braceful and braceless variable expansions, - * plus "extended" substitutions, unlike other exported parsing functions. - */ - - return parse_env_file_internal(f, fname, merge_env_file_push, env, NULL); -} - -static void write_env_var(FILE *f, const char *v) { - const char *p; - - p = strchr(v, '='); - if (!p) { - /* Fallback */ - fputs_unlocked(v, f); - fputc_unlocked('\n', f); - return; - } - - p++; - fwrite_unlocked(v, 1, p-v, f); - - if (string_has_cc(p, NULL) || chars_intersect(p, WHITESPACE SHELL_NEED_QUOTES)) { - fputc_unlocked('"', f); - - for (; *p; p++) { - if (strchr(SHELL_NEED_ESCAPE, *p)) - fputc_unlocked('\\', f); - - fputc_unlocked(*p, f); - } - - fputc_unlocked('"', f); - } else - fputs_unlocked(p, f); - - fputc_unlocked('\n', f); -} - -int write_env_file(const char *fname, char **l) { - _cleanup_fclose_ FILE *f = NULL; - _cleanup_free_ char *p = NULL; - char **i; - int r; - - assert(fname); - - r = fopen_temporary(fname, &f, &p); - if (r < 0) - return r; - - (void) fchmod_umask(fileno(f), 0644); - - STRV_FOREACH(i, l) - write_env_var(f, *i); - - r = fflush_and_check(f); - if (r >= 0) { - if (rename(p, fname) >= 0) - return 0; - - r = -errno; - } - - (void) unlink(p); - return r; -} -#endif /* NM_IGNORED */ diff --git a/shared/systemd/src/basic/env-file.h b/shared/systemd/src/basic/env-file.h deleted file mode 100644 index de475885..00000000 --- a/shared/systemd/src/basic/env-file.h +++ /dev/null @@ -1,17 +0,0 @@ -/* SPDX-License-Identifier: LGPL-2.1-or-later */ -#pragma once - -#include <stdarg.h> -#include <stdio.h> - -#include "macro.h" - -int parse_env_filev(FILE *f, const char *fname, va_list ap); -int parse_env_file_sentinel(FILE *f, const char *fname, ...) _sentinel_; -#define parse_env_file(f, fname, ...) parse_env_file_sentinel(f, fname, __VA_ARGS__, NULL) -int load_env_file(FILE *f, const char *fname, char ***l); -int load_env_file_pairs(FILE *f, const char *fname, char ***l); - -int merge_env_file(char ***env, FILE *f, const char *fname); - -int write_env_file(const char *fname, char **l); diff --git a/shared/systemd/src/basic/env-util.c b/shared/systemd/src/basic/env-util.c deleted file mode 100644 index a311ee00..00000000 --- a/shared/systemd/src/basic/env-util.c +++ /dev/null @@ -1,767 +0,0 @@ -/* SPDX-License-Identifier: LGPL-2.1-or-later */ - -#include "nm-sd-adapt-shared.h" - -#include <errno.h> -#include <limits.h> -#include <stdarg.h> -#include <stdlib.h> -#include <unistd.h> - -#include "alloc-util.h" -#include "env-util.h" -#include "escape.h" -#include "extract-word.h" -#include "macro.h" -#include "parse-util.h" -#include "string-util.h" -#include "strv.h" -#include "utf8.h" - -#if 0 /* NM_IGNORED */ -/* We follow bash for the character set. Different shells have different rules. */ -#define VALID_BASH_ENV_NAME_CHARS \ - DIGITS LETTERS \ - "_" - -static bool env_name_is_valid_n(const char *e, size_t n) { - const char *p; - - if (!e) - return false; - - if (n <= 0) - return false; - - if (e[0] >= '0' && e[0] <= '9') - return false; - - /* POSIX says the overall size of the environment block cannot - * be > ARG_MAX, an individual assignment hence cannot be - * either. Discounting the equal sign and trailing NUL this - * hence leaves ARG_MAX-2 as longest possible variable - * name. */ - if (n > (size_t) sysconf(_SC_ARG_MAX) - 2) - return false; - - for (p = e; p < e + n; p++) - if (!strchr(VALID_BASH_ENV_NAME_CHARS, *p)) - return false; - - return true; -} - -bool env_name_is_valid(const char *e) { - return env_name_is_valid_n(e, strlen_ptr(e)); -} - -bool env_value_is_valid(const char *e) { - if (!e) - return false; - - if (!utf8_is_valid(e)) - return false; - - /* bash allows tabs and newlines in environment variables, and so - * should we */ - if (string_has_cc(e, "\t\n")) - return false; - - /* POSIX says the overall size of the environment block cannot - * be > ARG_MAX, an individual assignment hence cannot be - * either. Discounting the shortest possible variable name of - * length 1, the equal sign and trailing NUL this hence leaves - * ARG_MAX-3 as longest possible variable value. */ - if (strlen(e) > sc_arg_max() - 3) - return false; - - return true; -} - -bool env_assignment_is_valid(const char *e) { - const char *eq; - - eq = strchr(e, '='); - if (!eq) - return false; - - if (!env_name_is_valid_n(e, eq - e)) - return false; - - if (!env_value_is_valid(eq + 1)) - return false; - - /* POSIX says the overall size of the environment block cannot - * be > ARG_MAX, hence the individual variable assignments - * cannot be either, but let's leave room for one trailing NUL - * byte. */ - if (strlen(e) > sc_arg_max() - 1) - return false; - - return true; -} - -bool strv_env_is_valid(char **e) { - char **p, **q; - - STRV_FOREACH(p, e) { - size_t k; - - if (!env_assignment_is_valid(*p)) - return false; - - /* Check if there are duplicate assignments */ - k = strcspn(*p, "="); - STRV_FOREACH(q, p + 1) - if (strneq(*p, *q, k) && (*q)[k] == '=') - return false; - } - - return true; -} - -bool strv_env_name_is_valid(char **l) { - char **p; - - STRV_FOREACH(p, l) { - if (!env_name_is_valid(*p)) - return false; - - if (strv_contains(p + 1, *p)) - return false; - } - - return true; -} - -bool strv_env_name_or_assignment_is_valid(char **l) { - char **p; - - STRV_FOREACH(p, l) { - if (!env_assignment_is_valid(*p) && !env_name_is_valid(*p)) - return false; - - if (strv_contains(p + 1, *p)) - return false; - } - - return true; -} - -static int env_append(char **r, char ***k, char **a) { - assert(r); - assert(k); - assert(*k >= r); - - if (!a) - return 0; - - /* Expects the following arguments: 'r' shall point to the beginning of an strv we are going to append to, 'k' - * to a pointer pointing to the NULL entry at the end of the same array. 'a' shall point to another strv. - * - * This call adds every entry of 'a' to 'r', either overriding an existing matching entry, or appending to it. - * - * This call assumes 'r' has enough pre-allocated space to grow by all of 'a''s items. */ - - for (; *a; a++) { - char **j, *c; - size_t n; - - n = strcspn(*a, "="); - if ((*a)[n] == '=') - n++; - - for (j = r; j < *k; j++) - if (strneq(*j, *a, n)) - break; - - c = strdup(*a); - if (!c) - return -ENOMEM; - - if (j >= *k) { /* Append to the end? */ - (*k)[0] = c; - (*k)[1] = NULL; - (*k)++; - } else - free_and_replace(*j, c); /* Override existing item */ - } - - return 0; -} - -char **strv_env_merge(size_t n_lists, ...) { - _cleanup_strv_free_ char **ret = NULL; - size_t n = 0, i; - char **l, **k; - va_list ap; - - /* Merges an arbitrary number of environment sets */ - - va_start(ap, n_lists); - for (i = 0; i < n_lists; i++) { - l = va_arg(ap, char**); - n += strv_length(l); - } - va_end(ap); - - ret = new(char*, n+1); - if (!ret) - return NULL; - - *ret = NULL; - k = ret; - - va_start(ap, n_lists); - for (i = 0; i < n_lists; i++) { - l = va_arg(ap, char**); - if (env_append(ret, &k, l) < 0) { - va_end(ap); - return NULL; - } - } - va_end(ap); - - return TAKE_PTR(ret); -} - -static bool env_match(const char *t, const char *pattern) { - assert(t); - assert(pattern); - - /* pattern a matches string a - * a matches a= - * a matches a=b - * a= matches a= - * a=b matches a=b - * a= does not match a - * a=b does not match a= - * a=b does not match a - * a=b does not match a=c */ - - if (streq(t, pattern)) - return true; - - if (!strchr(pattern, '=')) { - size_t l = strlen(pattern); - - return strneq(t, pattern, l) && t[l] == '='; - } - - return false; -} - -static bool env_entry_has_name(const char *entry, const char *name) { - const char *t; - - assert(entry); - assert(name); - - t = startswith(entry, name); - if (!t) - return false; - - return *t == '='; -} - -char **strv_env_delete(char **x, size_t n_lists, ...) { - size_t n, i = 0; - char **k, **r; - va_list ap; - - /* Deletes every entry from x that is mentioned in the other - * string lists */ - - n = strv_length(x); - - r = new(char*, n+1); - if (!r) - return NULL; - - STRV_FOREACH(k, x) { - size_t v; - - va_start(ap, n_lists); - for (v = 0; v < n_lists; v++) { - char **l, **j; - - l = va_arg(ap, char**); - STRV_FOREACH(j, l) - if (env_match(*k, *j)) - goto skip; - } - va_end(ap); - - r[i] = strdup(*k); - if (!r[i]) { - strv_free(r); - return NULL; - } - - i++; - continue; - - skip: - va_end(ap); - } - - r[i] = NULL; - - assert(i <= n); - - return r; -} - -char **strv_env_unset(char **l, const char *p) { - - char **f, **t; - - if (!l) - return NULL; - - assert(p); - - /* Drops every occurrence of the env var setting p in the - * string list. Edits in-place. */ - - for (f = t = l; *f; f++) { - - if (env_match(*f, p)) { - free(*f); - continue; - } - - *(t++) = *f; - } - - *t = NULL; - return l; -} - -char **strv_env_unset_many(char **l, ...) { - char **f, **t; - - if (!l) - return NULL; - - /* Like strv_env_unset() but applies many at once. Edits in-place. */ - - for (f = t = l; *f; f++) { - bool found = false; - const char *p; - va_list ap; - - va_start(ap, l); - - while ((p = va_arg(ap, const char*))) { - if (env_match(*f, p)) { - found = true; - break; - } - } - - va_end(ap); - - if (found) { - free(*f); - continue; - } - - *(t++) = *f; - } - - *t = NULL; - return l; -} - -int strv_env_replace(char ***l, char *p) { - const char *t, *name; - char **f; - int r; - - assert(p); - - /* Replace first occurrence of the env var or add a new one in the string list. Drop other occurrences. Edits - * in-place. Does not copy p. p must be a valid key=value assignment. - */ - - t = strchr(p, '='); - if (!t) - return -EINVAL; - - name = strndupa(p, t - p); - - STRV_FOREACH(f, *l) - if (env_entry_has_name(*f, name)) { - free_and_replace(*f, p); - strv_env_unset(f + 1, *f); - return 0; - } - - /* We didn't find a match, we need to append p or create a new strv */ - r = strv_push(l, p); - if (r < 0) - return r; - - return 1; -} - -char **strv_env_set(char **x, const char *p) { - _cleanup_strv_free_ char **ret = NULL; - size_t n, m; - char **k; - - /* Overrides the env var setting of p, returns a new copy */ - - n = strv_length(x); - m = n + 2; - if (m < n) /* overflow? */ - return NULL; - - ret = new(char*, m); - if (!ret) - return NULL; - - *ret = NULL; - k = ret; - - if (env_append(ret, &k, x) < 0) - return NULL; - - if (env_append(ret, &k, STRV_MAKE(p)) < 0) - return NULL; - - return TAKE_PTR(ret); -} - -char *strv_env_get_n(char **l, const char *name, size_t k, unsigned flags) { - char **i; - - assert(name); - - if (k <= 0) - return NULL; - - STRV_FOREACH_BACKWARDS(i, l) - if (strneq(*i, name, k) && - (*i)[k] == '=') - return *i + k + 1; - - if (flags & REPLACE_ENV_USE_ENVIRONMENT) { - const char *t; - - t = strndupa(name, k); - return getenv(t); - }; - - return NULL; -} - -char *strv_env_get(char **l, const char *name) { - assert(name); - - return strv_env_get_n(l, name, strlen(name), 0); -} - -char **strv_env_clean_with_callback(char **e, void (*invalid_callback)(const char *p, void *userdata), void *userdata) { - char **p, **q; - int k = 0; - - STRV_FOREACH(p, e) { - size_t n; - bool duplicate = false; - - if (!env_assignment_is_valid(*p)) { - if (invalid_callback) - invalid_callback(*p, userdata); - free(*p); - continue; - } - - n = strcspn(*p, "="); - STRV_FOREACH(q, p + 1) - if (strneq(*p, *q, n) && (*q)[n] == '=') { - duplicate = true; - break; - } - - if (duplicate) { - free(*p); - continue; - } - - e[k++] = *p; - } - - if (e) - e[k] = NULL; - - return e; -} - -char *replace_env_n(const char *format, size_t n, char **env, unsigned flags) { - enum { - WORD, - CURLY, - VARIABLE, - VARIABLE_RAW, - TEST, - DEFAULT_VALUE, - ALTERNATE_VALUE, - } state = WORD; - - const char *e, *word = format, *test_value; - char *k; - _cleanup_free_ char *r = NULL; - size_t i, len; - int nest = 0; - - assert(format); - - for (e = format, i = 0; *e && i < n; e ++, i ++) - switch (state) { - - case WORD: - if (*e == '$') - state = CURLY; - break; - - case CURLY: - if (*e == '{') { - k = strnappend(r, word, e-word-1); - if (!k) - return NULL; - - free_and_replace(r, k); - - word = e-1; - state = VARIABLE; - nest++; - } else if (*e == '$') { - k = strnappend(r, word, e-word); - if (!k) - return NULL; - - free_and_replace(r, k); - - word = e+1; - state = WORD; - - } else if (flags & REPLACE_ENV_ALLOW_BRACELESS && strchr(VALID_BASH_ENV_NAME_CHARS, *e)) { - k = strnappend(r, word, e-word-1); - if (!k) - return NULL; - - free_and_replace(r, k); - - word = e-1; - state = VARIABLE_RAW; - - } else - state = WORD; - break; - - case VARIABLE: - if (*e == '}') { - const char *t; - - t = strv_env_get_n(env, word+2, e-word-2, flags); - - k = strjoin(r, t); - if (!k) - return NULL; - - free_and_replace(r, k); - - word = e+1; - state = WORD; - } else if (*e == ':') { - if (!(flags & REPLACE_ENV_ALLOW_EXTENDED)) - /* Treat this as unsupported syntax, i.e. do no replacement */ - state = WORD; - else { - len = e-word-2; - state = TEST; - } - } - break; - - case TEST: - if (*e == '-') - state = DEFAULT_VALUE; - else if (*e == '+') - state = ALTERNATE_VALUE; - else { - state = WORD; - break; - } - - test_value = e+1; - break; - - case DEFAULT_VALUE: /* fall through */ - case ALTERNATE_VALUE: - assert(flags & REPLACE_ENV_ALLOW_EXTENDED); - - if (*e == '{') { - nest++; - break; - } - - if (*e != '}') - break; - - nest--; - if (nest == 0) { - const char *t; - _cleanup_free_ char *v = NULL; - - t = strv_env_get_n(env, word+2, len, flags); - - if (t && state == ALTERNATE_VALUE) - t = v = replace_env_n(test_value, e-test_value, env, flags); - else if (!t && state == DEFAULT_VALUE) - t = v = replace_env_n(test_value, e-test_value, env, flags); - - k = strjoin(r, t); - if (!k) - return NULL; - - free_and_replace(r, k); - - word = e+1; - state = WORD; - } - break; - - case VARIABLE_RAW: - assert(flags & REPLACE_ENV_ALLOW_BRACELESS); - - if (!strchr(VALID_BASH_ENV_NAME_CHARS, *e)) { - const char *t; - - t = strv_env_get_n(env, word+1, e-word-1, flags); - - k = strjoin(r, t); - if (!k) - return NULL; - - free_and_replace(r, k); - - word = e--; - i--; - state = WORD; - } - break; - } - - if (state == VARIABLE_RAW) { - const char *t; - - assert(flags & REPLACE_ENV_ALLOW_BRACELESS); - - t = strv_env_get_n(env, word+1, e-word-1, flags); - return strjoin(r, t); - } else - return strnappend(r, word, e-word); -} - -char **replace_env_argv(char **argv, char **env) { - char **ret, **i; - size_t k = 0, l = 0; - - l = strv_length(argv); - - ret = new(char*, l+1); - if (!ret) - return NULL; - - STRV_FOREACH(i, argv) { - - /* If $FOO appears as single word, replace it by the split up variable */ - if ((*i)[0] == '$' && !IN_SET((*i)[1], '{', '$')) { - char *e; - char **w, **m = NULL; - size_t q; - - e = strv_env_get(env, *i+1); - if (e) { - int r; - - r = strv_split_full(&m, e, WHITESPACE, EXTRACT_RELAX|EXTRACT_UNQUOTE); - if (r < 0) { - ret[k] = NULL; - strv_free(ret); - return NULL; - } - } else - m = NULL; - - q = strv_length(m); - l = l + q - 1; - - w = reallocarray(ret, l + 1, sizeof(char *)); - if (!w) { - ret[k] = NULL; - strv_free(ret); - strv_free(m); - return NULL; - } - - ret = w; - if (m) { - memcpy(ret + k, m, q * sizeof(char*)); - free(m); - } - - k += q; - continue; - } - - /* If ${FOO} appears as part of a word, replace it by the variable as-is */ - ret[k] = replace_env(*i, env, 0); - if (!ret[k]) { - strv_free(ret); - return NULL; - } - k++; - } - - ret[k] = NULL; - return ret; -} -#endif /* NM_IGNORED */ - -int getenv_bool(const char *p) { - const char *e; - - e = getenv(p); - if (!e) - return -ENXIO; - - return parse_boolean(e); -} - -int getenv_bool_secure(const char *p) { - const char *e; - - e = secure_getenv(p); - if (!e) - return -ENXIO; - - return parse_boolean(e); -} - -#if 0 /* NM_IGNORED */ -int set_unset_env(const char *name, const char *value, bool overwrite) { - int r; - - if (value) - r = setenv(name, value, overwrite); - else - r = unsetenv(name); - if (r < 0) - return -errno; - return 0; -} -#endif /* NM_IGNORED */ diff --git a/shared/systemd/src/basic/env-util.h b/shared/systemd/src/basic/env-util.h deleted file mode 100644 index 6684b335..00000000 --- a/shared/systemd/src/basic/env-util.h +++ /dev/null @@ -1,57 +0,0 @@ -/* SPDX-License-Identifier: LGPL-2.1-or-later */ -#pragma once - -#include <stdbool.h> -#include <stddef.h> -#include <stdio.h> -#include <unistd.h> - -#include "macro.h" -#include "string.h" - -static inline size_t sc_arg_max(void) { - long l = sysconf(_SC_ARG_MAX); - assert(l > 0); - return (size_t) l; -} - -bool env_name_is_valid(const char *e); -bool env_value_is_valid(const char *e); -bool env_assignment_is_valid(const char *e); - -enum { - REPLACE_ENV_USE_ENVIRONMENT = 1 << 0, - REPLACE_ENV_ALLOW_BRACELESS = 1 << 1, - REPLACE_ENV_ALLOW_EXTENDED = 1 << 2, -}; - -char *replace_env_n(const char *format, size_t n, char **env, unsigned flags); -char **replace_env_argv(char **argv, char **env); - -static inline char *replace_env(const char *format, char **env, unsigned flags) { - return replace_env_n(format, strlen(format), env, flags); -} - -bool strv_env_is_valid(char **e); -#define strv_env_clean(l) strv_env_clean_with_callback(l, NULL, NULL) -char **strv_env_clean_with_callback(char **l, void (*invalid_callback)(const char *p, void *userdata), void *userdata); - -bool strv_env_name_is_valid(char **l); -bool strv_env_name_or_assignment_is_valid(char **l); - -char **strv_env_merge(size_t n_lists, ...); -char **strv_env_delete(char **x, size_t n_lists, ...); /* New copy */ - -char **strv_env_set(char **x, const char *p); /* New copy ... */ -char **strv_env_unset(char **l, const char *p); /* In place ... */ -char **strv_env_unset_many(char **l, ...) _sentinel_; -int strv_env_replace(char ***l, char *p); /* In place ... */ - -char *strv_env_get_n(char **l, const char *name, size_t k, unsigned flags) _pure_; -char *strv_env_get(char **x, const char *n) _pure_; - -int getenv_bool(const char *p); -int getenv_bool_secure(const char *p); - -/* Like setenv, but calls unsetenv if value == NULL. */ -int set_unset_env(const char *name, const char *value, bool overwrite); diff --git a/shared/systemd/src/basic/errno-util.h b/shared/systemd/src/basic/errno-util.h deleted file mode 100644 index 5609820b..00000000 --- a/shared/systemd/src/basic/errno-util.h +++ /dev/null @@ -1,119 +0,0 @@ -/* SPDX-License-Identifier: LGPL-2.1-or-later */ -#pragma once - -#include <stdlib.h> -#include <string.h> - -#include "macro.h" - -static inline void _reset_errno_(int *saved_errno) { - if (*saved_errno < 0) /* Invalidated by UNPROTECT_ERRNO? */ - return; - - errno = *saved_errno; -} - -#define PROTECT_ERRNO \ - _cleanup_(_reset_errno_) _unused_ int _saved_errno_ = errno - -#define UNPROTECT_ERRNO \ - do { \ - errno = _saved_errno_; \ - _saved_errno_ = -1; \ - } while (false) - -static inline int negative_errno(void) { - /* This helper should be used to shut up gcc if you know 'errno' is - * negative. Instead of "return -errno;", use "return negative_errno();" - * It will suppress bogus gcc warnings in case it assumes 'errno' might - * be 0 and thus the caller's error-handling might not be triggered. */ - assert_return(errno > 0, -EINVAL); - return -errno; -} - -static inline const char *strerror_safe(int error) { - /* 'safe' here does NOT mean thread safety. */ - return strerror(abs(error)); -} - -static inline int errno_or_else(int fallback) { - /* To be used when invoking library calls where errno handling is not defined clearly: we return - * errno if it is set, and the specified error otherwise. The idea is that the caller initializes - * errno to zero before doing an API call, and then uses this helper to retrieve a somewhat useful - * error code */ - if (errno > 0) - return -errno; - - return -abs(fallback); -} - -/* Hint #1: ENETUNREACH happens if we try to connect to "non-existing" special IP addresses, such as ::5. - * - * Hint #2: The kernel sends e.g., EHOSTUNREACH or ENONET to userspace in some ICMP error cases. See the - * icmp_err_convert[] in net/ipv4/icmp.c in the kernel sources. - * - * Hint #3: When asynchronous connect() on TCP fails because the host never acknowledges a single packet, - * kernel tells us that with ETIMEDOUT, see tcp(7). */ -static inline bool ERRNO_IS_DISCONNECT(int r) { - return IN_SET(abs(r), - ECONNABORTED, - ECONNREFUSED, - ECONNRESET, - EHOSTDOWN, - EHOSTUNREACH, - ENETDOWN, - ENETRESET, - ENETUNREACH, - ENONET, - ENOPROTOOPT, - ENOTCONN, - EPIPE, - EPROTO, - ESHUTDOWN, - ETIMEDOUT); -} - -/* Transient errors we might get on accept() that we should ignore. As per error handling comment in - * the accept(2) man page. */ -static inline bool ERRNO_IS_ACCEPT_AGAIN(int r) { - return ERRNO_IS_DISCONNECT(r) || - IN_SET(abs(r), - EAGAIN, - EINTR, - EOPNOTSUPP); -} - -/* Resource exhaustion, could be our fault or general system trouble */ -static inline bool ERRNO_IS_RESOURCE(int r) { - return IN_SET(abs(r), - EMFILE, - ENFILE, - ENOMEM); -} - -/* Seven different errors for "operation/system call/ioctl/socket feature not supported" */ -static inline bool ERRNO_IS_NOT_SUPPORTED(int r) { - return IN_SET(abs(r), - EOPNOTSUPP, - ENOTTY, - ENOSYS, - EAFNOSUPPORT, - EPFNOSUPPORT, - EPROTONOSUPPORT, - ESOCKTNOSUPPORT); -} - -/* Two different errors for access problems */ -static inline bool ERRNO_IS_PRIVILEGE(int r) { - return IN_SET(abs(r), - EACCES, - EPERM); -} - -/* Three difference errors for "not enough disk space" */ -static inline bool ERRNO_IS_DISK_SPACE(int r) { - return IN_SET(abs(r), - ENOSPC, - EDQUOT, - EFBIG); -} diff --git a/shared/systemd/src/basic/escape.c b/shared/systemd/src/basic/escape.c deleted file mode 100644 index 094b5c5f..00000000 --- a/shared/systemd/src/basic/escape.c +++ /dev/null @@ -1,553 +0,0 @@ -/* SPDX-License-Identifier: LGPL-2.1-or-later */ - -#include "nm-sd-adapt-shared.h" - -#include <errno.h> -#include <stdlib.h> -#include <string.h> - -#include "alloc-util.h" -#include "escape.h" -#include "hexdecoct.h" -#include "macro.h" -#include "utf8.h" - -int cescape_char(char c, char *buf) { - char *buf_old = buf; - - /* Needs space for 4 characters in the buffer */ - - switch (c) { - - case '\a': - *(buf++) = '\\'; - *(buf++) = 'a'; - break; - case '\b': - *(buf++) = '\\'; - *(buf++) = 'b'; - break; - case '\f': - *(buf++) = '\\'; - *(buf++) = 'f'; - break; - case '\n': - *(buf++) = '\\'; - *(buf++) = 'n'; - break; - case '\r': - *(buf++) = '\\'; - *(buf++) = 'r'; - break; - case '\t': - *(buf++) = '\\'; - *(buf++) = 't'; - break; - case '\v': - *(buf++) = '\\'; - *(buf++) = 'v'; - break; - case '\\': - *(buf++) = '\\'; - *(buf++) = '\\'; - break; - case '"': - *(buf++) = '\\'; - *(buf++) = '"'; - break; - case '\'': - *(buf++) = '\\'; - *(buf++) = '\''; - break; - - default: - /* For special chars we prefer octal over - * hexadecimal encoding, simply because glib's - * g_strescape() does the same */ - if ((c < ' ') || (c >= 127)) { - *(buf++) = '\\'; - *(buf++) = octchar((unsigned char) c >> 6); - *(buf++) = octchar((unsigned char) c >> 3); - *(buf++) = octchar((unsigned char) c); - } else - *(buf++) = c; - break; - } - - return buf - buf_old; -} - -char* cescape_length(const char *s, size_t n) { - const char *f; - char *r, *t; - - assert(s || n == 0); - - /* Does C style string escaping. May be reversed with - * cunescape(). */ - - r = new(char, n*4 + 1); - if (!r) - return NULL; - - for (f = s, t = r; f < s + n; f++) - t += cescape_char(*f, t); - - *t = 0; - - return r; -} - -char* cescape(const char *s) { - assert(s); - - return cescape_length(s, strlen(s)); -} - -int cunescape_one(const char *p, size_t length, char32_t *ret, bool *eight_bit, bool accept_nul) { - int r = 1; - - assert(p); - assert(ret); - - /* Unescapes C style. Returns the unescaped character in ret. - * Sets *eight_bit to true if the escaped sequence either fits in - * one byte in UTF-8 or is a non-unicode literal byte and should - * instead be copied directly. - */ - - if (length != (size_t) -1 && length < 1) - return -EINVAL; - - switch (p[0]) { - - case 'a': - *ret = '\a'; - break; - case 'b': - *ret = '\b'; - break; - case 'f': - *ret = '\f'; - break; - case 'n': - *ret = '\n'; - break; - case 'r': - *ret = '\r'; - break; - case 't': - *ret = '\t'; - break; - case 'v': - *ret = '\v'; - break; - case '\\': - *ret = '\\'; - break; - case '"': - *ret = '"'; - break; - case '\'': - *ret = '\''; - break; - - case 's': - /* This is an extension of the XDG syntax files */ - *ret = ' '; - break; - - case 'x': { - /* hexadecimal encoding */ - int a, b; - - if (length != (size_t) -1 && length < 3) - return -EINVAL; - - a = unhexchar(p[1]); - if (a < 0) - return -EINVAL; - - b = unhexchar(p[2]); - if (b < 0) - return -EINVAL; - - /* Don't allow NUL bytes */ - if (a == 0 && b == 0 && !accept_nul) - return -EINVAL; - - *ret = (a << 4U) | b; - *eight_bit = true; - r = 3; - break; - } - - case 'u': { - /* C++11 style 16bit unicode */ - - int a[4]; - size_t i; - uint32_t c; - - if (length != (size_t) -1 && length < 5) - return -EINVAL; - - for (i = 0; i < 4; i++) { - a[i] = unhexchar(p[1 + i]); - if (a[i] < 0) - return a[i]; - } - - c = ((uint32_t) a[0] << 12U) | ((uint32_t) a[1] << 8U) | ((uint32_t) a[2] << 4U) | (uint32_t) a[3]; - - /* Don't allow 0 chars */ - if (c == 0 && !accept_nul) - return -EINVAL; - - *ret = c; - r = 5; - break; - } - - case 'U': { - /* C++11 style 32bit unicode */ - - int a[8]; - size_t i; - char32_t c; - - if (length != (size_t) -1 && length < 9) - return -EINVAL; - - for (i = 0; i < 8; i++) { - a[i] = unhexchar(p[1 + i]); - if (a[i] < 0) - return a[i]; - } - - c = ((uint32_t) a[0] << 28U) | ((uint32_t) a[1] << 24U) | ((uint32_t) a[2] << 20U) | ((uint32_t) a[3] << 16U) | - ((uint32_t) a[4] << 12U) | ((uint32_t) a[5] << 8U) | ((uint32_t) a[6] << 4U) | (uint32_t) a[7]; - - /* Don't allow 0 chars */ - if (c == 0 && !accept_nul) - return -EINVAL; - - /* Don't allow invalid code points */ - if (!unichar_is_valid(c)) - return -EINVAL; - - *ret = c; - r = 9; - break; - } - - case '0': - case '1': - case '2': - case '3': - case '4': - case '5': - case '6': - case '7': { - /* octal encoding */ - int a, b, c; - char32_t m; - - if (length != (size_t) -1 && length < 3) - return -EINVAL; - - a = unoctchar(p[0]); - if (a < 0) - return -EINVAL; - - b = unoctchar(p[1]); - if (b < 0) - return -EINVAL; - - c = unoctchar(p[2]); - if (c < 0) - return -EINVAL; - - /* don't allow NUL bytes */ - if (a == 0 && b == 0 && c == 0 && !accept_nul) - return -EINVAL; - - /* Don't allow bytes above 255 */ - m = ((uint32_t) a << 6U) | ((uint32_t) b << 3U) | (uint32_t) c; - if (m > 255) - return -EINVAL; - - *ret = m; - *eight_bit = true; - r = 3; - break; - } - - default: - return -EINVAL; - } - - return r; -} - -#if 0 /* NM_IGNORED */ -int cunescape_length_with_prefix(const char *s, size_t length, const char *prefix, UnescapeFlags flags, char **ret) { - char *r, *t; - const char *f; - size_t pl; - - assert(s); - assert(ret); - - /* Undoes C style string escaping, and optionally prefixes it. */ - - pl = strlen_ptr(prefix); - - r = new(char, pl+length+1); - if (!r) - return -ENOMEM; - - if (prefix) - memcpy(r, prefix, pl); - - for (f = s, t = r + pl; f < s + length; f++) { - size_t remaining; - bool eight_bit = false; - char32_t u; - int k; - - remaining = s + length - f; - assert(remaining > 0); - - if (*f != '\\') { - /* A literal, copy verbatim */ - *(t++) = *f; - continue; - } - - if (remaining == 1) { - if (flags & UNESCAPE_RELAX) { - /* A trailing backslash, copy verbatim */ - *(t++) = *f; - continue; - } - - free(r); - return -EINVAL; - } - - k = cunescape_one(f + 1, remaining - 1, &u, &eight_bit, flags & UNESCAPE_ACCEPT_NUL); - if (k < 0) { - if (flags & UNESCAPE_RELAX) { - /* Invalid escape code, let's take it literal then */ - *(t++) = '\\'; - continue; - } - - free(r); - return k; - } - - f += k; - if (eight_bit) - /* One byte? Set directly as specified */ - *(t++) = u; - else - /* Otherwise encode as multi-byte UTF-8 */ - t += utf8_encode_unichar(t, u); - } - - *t = 0; - - *ret = r; - return t - r; -} - -char* xescape_full(const char *s, const char *bad, size_t console_width, bool eight_bits) { - char *ans, *t, *prev, *prev2; - const char *f; - - /* Escapes all chars in bad, in addition to \ and all special chars, in \xFF style escaping. May be - * reversed with cunescape(). If eight_bits is true, characters >= 127 are let through unchanged. - * This corresponds to non-ASCII printable characters in pre-unicode encodings. - * - * If console_width is reached, output is truncated and "..." is appended. */ - - if (console_width == 0) - return strdup(""); - - ans = new(char, MIN(strlen(s), console_width) * 4 + 1); - if (!ans) - return NULL; - - memset(ans, '_', MIN(strlen(s), console_width) * 4); - ans[MIN(strlen(s), console_width) * 4] = 0; - - for (f = s, t = prev = prev2 = ans; ; f++) { - char *tmp_t = t; - - if (!*f) { - *t = 0; - return ans; - } - - if ((unsigned char) *f < ' ' || (!eight_bits && (unsigned char) *f >= 127) || - *f == '\\' || strchr(bad, *f)) { - if ((size_t) (t - ans) + 4 > console_width) - break; - - *(t++) = '\\'; - *(t++) = 'x'; - *(t++) = hexchar(*f >> 4); - *(t++) = hexchar(*f); - } else { - if ((size_t) (t - ans) + 1 > console_width) - break; - - *(t++) = *f; - } - - /* We might need to go back two cycles to fit three dots, so remember two positions */ - prev2 = prev; - prev = tmp_t; - } - - /* We can just write where we want, since chars are one-byte */ - size_t c = MIN(console_width, 3u); /* If the console is too narrow, write fewer dots */ - size_t off; - if (console_width - c >= (size_t) (t - ans)) - off = (size_t) (t - ans); - else if (console_width - c >= (size_t) (prev - ans)) - off = (size_t) (prev - ans); - else if (console_width - c >= (size_t) (prev2 - ans)) - off = (size_t) (prev2 - ans); - else - off = console_width - c; - assert(off <= (size_t) (t - ans)); - - memcpy(ans + off, "...", c); - ans[off + c] = '\0'; - return ans; -} - -char* escape_non_printable_full(const char *str, size_t console_width, bool eight_bit) { - if (eight_bit) - return xescape_full(str, "", console_width, true); - else - return utf8_escape_non_printable_full(str, console_width); -} -#endif /* NM_IGNORED */ - -char* octescape(const char *s, size_t len) { - char *r, *t; - const char *f; - - /* Escapes all chars in bad, in addition to \ and " chars, - * in \nnn style escaping. */ - - r = new(char, len * 4 + 1); - if (!r) - return NULL; - - for (f = s, t = r; f < s + len; f++) { - - if (*f < ' ' || *f >= 127 || IN_SET(*f, '\\', '"')) { - *(t++) = '\\'; - *(t++) = '0' + (*f >> 6); - *(t++) = '0' + ((*f >> 3) & 8); - *(t++) = '0' + (*f & 8); - } else - *(t++) = *f; - } - - *t = 0; - - return r; - -} - -static char* strcpy_backslash_escaped(char *t, const char *s, const char *bad, bool escape_tab_nl) { - assert(bad); - - for (; *s; s++) { - if (escape_tab_nl && IN_SET(*s, '\n', '\t')) { - *(t++) = '\\'; - *(t++) = *s == '\n' ? 'n' : 't'; - continue; - } - - if (*s == '\\' || strchr(bad, *s)) - *(t++) = '\\'; - - *(t++) = *s; - } - - return t; -} - -char* shell_escape(const char *s, const char *bad) { - char *r, *t; - - r = new(char, strlen(s)*2+1); - if (!r) - return NULL; - - t = strcpy_backslash_escaped(r, s, bad, false); - *t = 0; - - return r; -} - -char* shell_maybe_quote(const char *s, EscapeStyle style) { - const char *p; - char *r, *t; - - assert(s); - - /* Encloses a string in quotes if necessary to make it OK as a shell - * string. Note that we treat benign UTF-8 characters as needing - * escaping too, but that should be OK. */ - - for (p = s; *p; p++) - if (*p <= ' ' || - *p >= 127 || - strchr(SHELL_NEED_QUOTES, *p)) - break; - - if (!*p) - return strdup(s); - - r = new(char, (style == ESCAPE_POSIX) + 1 + strlen(s)*2 + 1 + 1); - if (!r) - return NULL; - - t = r; - switch (style) { - case ESCAPE_BACKSLASH: - case ESCAPE_BACKSLASH_ONELINE: - *(t++) = '"'; - break; - case ESCAPE_POSIX: - *(t++) = '$'; - *(t++) = '\''; - break; - default: - assert_not_reached("Bad EscapeStyle"); - } - - t = mempcpy(t, s, p - s); - - if (IN_SET(style, ESCAPE_BACKSLASH, ESCAPE_BACKSLASH_ONELINE)) - t = strcpy_backslash_escaped(t, p, SHELL_NEED_ESCAPE, - style == ESCAPE_BACKSLASH_ONELINE); - else - t = strcpy_backslash_escaped(t, p, SHELL_NEED_ESCAPE_POSIX, true); - - if (IN_SET(style, ESCAPE_BACKSLASH, ESCAPE_BACKSLASH_ONELINE)) - *(t++) = '"'; - else - *(t++) = '\''; - *t = 0; - - return r; -} diff --git a/shared/systemd/src/basic/escape.h b/shared/systemd/src/basic/escape.h deleted file mode 100644 index 691b6d80..00000000 --- a/shared/systemd/src/basic/escape.h +++ /dev/null @@ -1,67 +0,0 @@ -/* SPDX-License-Identifier: LGPL-2.1-or-later */ -#pragma once - -#include <inttypes.h> -#include <stddef.h> -#include <stdint.h> -#include <sys/types.h> -#include <uchar.h> - -#include "string-util.h" -#include "missing_type.h" - -/* What characters are special in the shell? */ -/* must be escaped outside and inside double-quotes */ -#define SHELL_NEED_ESCAPE "\"\\`$" - -/* Those that can be escaped or double-quoted. - * - * Strictly speaking, ! does not need to be escaped, except in interactive - * mode, but let's be extra nice to the user and quote ! in case this - * output is ever used in interactive mode. */ -#define SHELL_NEED_QUOTES SHELL_NEED_ESCAPE GLOB_CHARS "'()<>|&;!" - -/* Note that we assume control characters would need to be escaped too in - * addition to the "special" characters listed here, if they appear in the - * string. Current users disallow control characters. Also '"' shall not - * be escaped. - */ -#define SHELL_NEED_ESCAPE_POSIX "\\\'" - -typedef enum UnescapeFlags { - UNESCAPE_RELAX = 1 << 0, - UNESCAPE_ACCEPT_NUL = 1 << 1, -} UnescapeFlags; - -typedef enum EscapeStyle { - ESCAPE_BACKSLASH = 1, /* Add shell quotes ("") so the shell will consider this a single - argument, possibly multiline. Tabs and newlines are not escaped. */ - ESCAPE_BACKSLASH_ONELINE = 2, /* Similar to ESCAPE_BACKSLASH, but always produces a single-line - string instead. Shell escape sequences are produced for tabs and - newlines. */ - ESCAPE_POSIX = 3, /* Similar to ESCAPE_BACKSLASH_ONELINE, but uses POSIX shell escape - * syntax (a string enclosed in $'') instead of plain quotes. */ -} EscapeStyle; - -char* cescape(const char *s); -char* cescape_length(const char *s, size_t n); -int cescape_char(char c, char *buf); - -int cunescape_length_with_prefix(const char *s, size_t length, const char *prefix, UnescapeFlags flags, char **ret); -static inline int cunescape_length(const char *s, size_t length, UnescapeFlags flags, char **ret) { - return cunescape_length_with_prefix(s, length, NULL, flags, ret); -} -static inline int cunescape(const char *s, UnescapeFlags flags, char **ret) { - return cunescape_length(s, strlen(s), flags, ret); -} -int cunescape_one(const char *p, size_t length, char32_t *ret, bool *eight_bit, bool accept_nul); - -char* xescape_full(const char *s, const char *bad, size_t console_width, bool eight_bits); -static inline char* xescape(const char *s, const char *bad) { - return xescape_full(s, bad, SIZE_MAX, false); -} -char* octescape(const char *s, size_t len); -char* escape_non_printable_full(const char *str, size_t console_width, bool eight_bit); - -char* shell_escape(const char *s, const char *bad); -char* shell_maybe_quote(const char *s, EscapeStyle style); diff --git a/shared/systemd/src/basic/ether-addr-util.c b/shared/systemd/src/basic/ether-addr-util.c deleted file mode 100644 index ae83eade..00000000 --- a/shared/systemd/src/basic/ether-addr-util.c +++ /dev/null @@ -1,128 +0,0 @@ -/* SPDX-License-Identifier: LGPL-2.1-or-later */ - -#include "nm-sd-adapt-shared.h" - -#include <errno.h> -#include <inttypes.h> -#include <net/ethernet.h> -#include <stdio.h> -#include <sys/types.h> - -#include "ether-addr-util.h" -#include "macro.h" -#include "string-util.h" - -char* hw_addr_to_string(const hw_addr_data *addr, char buffer[HW_ADDR_TO_STRING_MAX]) { - assert(addr); - assert(buffer); - assert(addr->length <= HW_ADDR_MAX_SIZE); - - for (size_t i = 0; i < addr->length; i++) { - sprintf(&buffer[3*i], "%02"PRIx8, addr->addr.bytes[i]); - if (i < addr->length - 1) - buffer[3*i + 2] = ':'; - } - - return buffer; -} - -char* ether_addr_to_string(const struct ether_addr *addr, char buffer[ETHER_ADDR_TO_STRING_MAX]) { - assert(addr); - assert(buffer); - - /* Like ether_ntoa() but uses %02x instead of %x to print - * ethernet addresses, which makes them look less funny. Also, - * doesn't use a static buffer. */ - - sprintf(buffer, "%02x:%02x:%02x:%02x:%02x:%02x", - addr->ether_addr_octet[0], - addr->ether_addr_octet[1], - addr->ether_addr_octet[2], - addr->ether_addr_octet[3], - addr->ether_addr_octet[4], - addr->ether_addr_octet[5]); - - return buffer; -} - -int ether_addr_compare(const struct ether_addr *a, const struct ether_addr *b) { - return memcmp(a, b, ETH_ALEN); -} - -static void ether_addr_hash_func(const struct ether_addr *p, struct siphash *state) { - siphash24_compress(p, sizeof(struct ether_addr), state); -} - -DEFINE_HASH_OPS(ether_addr_hash_ops, struct ether_addr, ether_addr_hash_func, ether_addr_compare); - -int ether_addr_from_string(const char *s, struct ether_addr *ret) { - size_t pos = 0, n, field; - char sep = '\0'; - const char *hex = HEXDIGITS, *hexoff; - size_t x; - bool touched; - -#define parse_fields(v) \ - for (field = 0; field < ELEMENTSOF(v); field++) { \ - touched = false; \ - for (n = 0; n < (2 * sizeof(v[0])); n++) { \ - if (s[pos] == '\0') \ - break; \ - hexoff = strchr(hex, s[pos]); \ - if (!hexoff) \ - break; \ - assert(hexoff >= hex); \ - x = hexoff - hex; \ - if (x >= 16) \ - x -= 6; /* A-F */ \ - assert(x < 16); \ - touched = true; \ - v[field] <<= 4; \ - v[field] += x; \ - pos++; \ - } \ - if (!touched) \ - return -EINVAL; \ - if (field < (ELEMENTSOF(v)-1)) { \ - if (s[pos] != sep) \ - return -EINVAL; \ - else \ - pos++; \ - } \ - } - - assert(s); - assert(ret); - - s += strspn(s, WHITESPACE); - sep = s[strspn(s, hex)]; - - if (sep == '.') { - uint16_t shorts[3] = { 0 }; - - parse_fields(shorts); - - if (s[pos] != '\0') - return -EINVAL; - - for (n = 0; n < ELEMENTSOF(shorts); n++) { - ret->ether_addr_octet[2*n] = ((shorts[n] & (uint16_t)0xff00) >> 8); - ret->ether_addr_octet[2*n + 1] = (shorts[n] & (uint16_t)0x00ff); - } - - } else if (IN_SET(sep, ':', '-')) { - struct ether_addr out = ETHER_ADDR_NULL; - - parse_fields(out.ether_addr_octet); - - if (s[pos] != '\0') - return -EINVAL; - - for (n = 0; n < ELEMENTSOF(out.ether_addr_octet); n++) - ret->ether_addr_octet[n] = out.ether_addr_octet[n]; - - } else - return -EINVAL; - - return 0; -} diff --git a/shared/systemd/src/basic/ether-addr-util.h b/shared/systemd/src/basic/ether-addr-util.h deleted file mode 100644 index 942ce556..00000000 --- a/shared/systemd/src/basic/ether-addr-util.h +++ /dev/null @@ -1,52 +0,0 @@ -/* SPDX-License-Identifier: LGPL-2.1-or-later */ -#pragma once - -#include <linux/if_infiniband.h> -#include <net/ethernet.h> -#include <stdbool.h> - -#include "hash-funcs.h" - -/* This is MAX_ADDR_LEN as defined in linux/netdevice.h, but net/if_arp.h - * defines a macro of the same name with a much lower size. */ -#define HW_ADDR_MAX_SIZE 32 - -union hw_addr_union { - struct ether_addr ether; - uint8_t infiniband[INFINIBAND_ALEN]; - uint8_t bytes[HW_ADDR_MAX_SIZE]; -}; - -typedef struct hw_addr_data { - union hw_addr_union addr; - size_t length; -} hw_addr_data; - -#define HW_ADDR_TO_STRING_MAX (3*HW_ADDR_MAX_SIZE) -char* hw_addr_to_string(const hw_addr_data *addr, char buffer[HW_ADDR_TO_STRING_MAX]); - -/* Use only as function argument, never stand-alone! */ -#define HW_ADDR_TO_STR(hw_addr) hw_addr_to_string((hw_addr), (char[HW_ADDR_TO_STRING_MAX]){}) - -#define HW_ADDR_NULL ((const hw_addr_data){}) - -#define ETHER_ADDR_FORMAT_STR "%02X%02X%02X%02X%02X%02X" -#define ETHER_ADDR_FORMAT_VAL(x) (x).ether_addr_octet[0], (x).ether_addr_octet[1], (x).ether_addr_octet[2], (x).ether_addr_octet[3], (x).ether_addr_octet[4], (x).ether_addr_octet[5] - -#define ETHER_ADDR_TO_STRING_MAX (3*6) -char* ether_addr_to_string(const struct ether_addr *addr, char buffer[ETHER_ADDR_TO_STRING_MAX]); - -int ether_addr_compare(const struct ether_addr *a, const struct ether_addr *b); -static inline bool ether_addr_equal(const struct ether_addr *a, const struct ether_addr *b) { - return ether_addr_compare(a, b) == 0; -} - -#define ETHER_ADDR_NULL ((const struct ether_addr){}) - -static inline bool ether_addr_is_null(const struct ether_addr *addr) { - return ether_addr_equal(addr, ÐER_ADDR_NULL); -} - -int ether_addr_from_string(const char *s, struct ether_addr *ret); - -extern const struct hash_ops ether_addr_hash_ops; diff --git a/shared/systemd/src/basic/extract-word.c b/shared/systemd/src/basic/extract-word.c deleted file mode 100644 index 1d86033f..00000000 --- a/shared/systemd/src/basic/extract-word.c +++ /dev/null @@ -1,294 +0,0 @@ -/* SPDX-License-Identifier: LGPL-2.1-or-later */ - -#include "nm-sd-adapt-shared.h" - -#include <errno.h> -#include <stdarg.h> -#include <stdbool.h> -#include <stddef.h> -#include <stdint.h> -#include <stdlib.h> -#include <syslog.h> - -#include "alloc-util.h" -#include "escape.h" -#include "extract-word.h" -#include "log.h" -#include "macro.h" -#include "string-util.h" -#include "strv.h" -#include "utf8.h" - -int extract_first_word(const char **p, char **ret, const char *separators, ExtractFlags flags) { - _cleanup_free_ char *s = NULL; - size_t allocated = 0, sz = 0; - char c; - int r; - - char quote = 0; /* 0 or ' or " */ - bool backslash = false; /* whether we've just seen a backslash */ - - assert(p); - assert(ret); - - /* Bail early if called after last value or with no input */ - if (!*p) - goto finish; - c = **p; - - if (!separators) - separators = WHITESPACE; - - /* Parses the first word of a string, and returns it in - * *ret. Removes all quotes in the process. When parsing fails - * (because of an uneven number of quotes or similar), leaves - * the pointer *p at the first invalid character. */ - - if (flags & EXTRACT_DONT_COALESCE_SEPARATORS) - if (!GREEDY_REALLOC(s, allocated, sz+1)) - return -ENOMEM; - - for (;; (*p)++, c = **p) { - if (c == 0) - goto finish_force_terminate; - else if (strchr(separators, c)) { - if (flags & EXTRACT_DONT_COALESCE_SEPARATORS) { - (*p)++; - goto finish_force_next; - } - } else { - /* We found a non-blank character, so we will always - * want to return a string (even if it is empty), - * allocate it here. */ - if (!GREEDY_REALLOC(s, allocated, sz+1)) - return -ENOMEM; - break; - } - } - - for (;; (*p)++, c = **p) { - if (backslash) { - if (!GREEDY_REALLOC(s, allocated, sz+7)) - return -ENOMEM; - - if (c == 0) { - if ((flags & EXTRACT_CUNESCAPE_RELAX) && - (!quote || flags & EXTRACT_RELAX)) { - /* If we find an unquoted trailing backslash and we're in - * EXTRACT_CUNESCAPE_RELAX mode, keep it verbatim in the - * output. - * - * Unbalanced quotes will only be allowed in EXTRACT_RELAX - * mode, EXTRACT_CUNESCAPE_RELAX mode does not allow them. - */ - s[sz++] = '\\'; - goto finish_force_terminate; - } - if (flags & EXTRACT_RELAX) - goto finish_force_terminate; - return -EINVAL; - } - - if (flags & (EXTRACT_CUNESCAPE|EXTRACT_UNESCAPE_SEPARATORS)) { - bool eight_bit = false; - char32_t u; - - if ((flags & EXTRACT_CUNESCAPE) && - (r = cunescape_one(*p, (size_t) -1, &u, &eight_bit, false)) >= 0) { - /* A valid escaped sequence */ - assert(r >= 1); - - (*p) += r - 1; - - if (eight_bit) - s[sz++] = u; - else - sz += utf8_encode_unichar(s + sz, u); - } else if ((flags & EXTRACT_UNESCAPE_SEPARATORS) && - strchr(separators, **p)) - /* An escaped separator char */ - s[sz++] = c; - else if (flags & EXTRACT_CUNESCAPE_RELAX) { - s[sz++] = '\\'; - s[sz++] = c; - } else - return -EINVAL; - } else - s[sz++] = c; - - backslash = false; - - } else if (quote) { /* inside either single or double quotes */ - for (;; (*p)++, c = **p) { - if (c == 0) { - if (flags & EXTRACT_RELAX) - goto finish_force_terminate; - return -EINVAL; - } else if (c == quote) { /* found the end quote */ - quote = 0; - break; - } else if (c == '\\' && !(flags & EXTRACT_RETAIN_ESCAPE)) { - backslash = true; - break; - } else { - if (!GREEDY_REALLOC(s, allocated, sz+2)) - return -ENOMEM; - - s[sz++] = c; - } - } - - } else { - for (;; (*p)++, c = **p) { - if (c == 0) - goto finish_force_terminate; - else if (IN_SET(c, '\'', '"') && (flags & EXTRACT_UNQUOTE)) { - quote = c; - break; - } else if (c == '\\' && !(flags & EXTRACT_RETAIN_ESCAPE)) { - backslash = true; - break; - } else if (strchr(separators, c)) { - if (flags & EXTRACT_DONT_COALESCE_SEPARATORS) { - (*p)++; - goto finish_force_next; - } - /* Skip additional coalesced separators. */ - for (;; (*p)++, c = **p) { - if (c == 0) - goto finish_force_terminate; - if (!strchr(separators, c)) - break; - } - goto finish; - - } else { - if (!GREEDY_REALLOC(s, allocated, sz+2)) - return -ENOMEM; - - s[sz++] = c; - } - } - } - } - -finish_force_terminate: - *p = NULL; -finish: - if (!s) { - *p = NULL; - *ret = NULL; - return 0; - } - -finish_force_next: - s[sz] = 0; - *ret = TAKE_PTR(s); - - return 1; -} - -#if 0 /* NM_IGNORED */ -int extract_first_word_and_warn( - const char **p, - char **ret, - const char *separators, - ExtractFlags flags, - const char *unit, - const char *filename, - unsigned line, - const char *rvalue) { - - /* Try to unquote it, if it fails, warn about it and try again - * but this time using EXTRACT_CUNESCAPE_RELAX to keep the - * backslashes verbatim in invalid escape sequences. */ - - const char *save; - int r; - - save = *p; - r = extract_first_word(p, ret, separators, flags); - if (r >= 0) - return r; - - if (r == -EINVAL && !(flags & EXTRACT_CUNESCAPE_RELAX)) { - - /* Retry it with EXTRACT_CUNESCAPE_RELAX. */ - *p = save; - r = extract_first_word(p, ret, separators, flags|EXTRACT_CUNESCAPE_RELAX); - if (r >= 0) { - /* It worked this time, hence it must have been an invalid escape sequence. */ - log_syntax(unit, LOG_WARNING, filename, line, EINVAL, "Ignoring unknown escape sequences: \"%s\"", *ret); - return r; - } - - /* If it's still EINVAL; then it must be unbalanced quoting, report this. */ - if (r == -EINVAL) - return log_syntax(unit, LOG_ERR, filename, line, r, "Unbalanced quoting, ignoring: \"%s\"", rvalue); - } - - /* Can be any error, report it */ - return log_syntax(unit, LOG_ERR, filename, line, r, "Unable to decode word \"%s\", ignoring: %m", rvalue); -} - -/* We pass ExtractFlags as unsigned int (to avoid undefined behaviour when passing - * an object that undergoes default argument promotion as an argument to va_start). - * Let's make sure that ExtractFlags fits into an unsigned int. */ -assert_cc(sizeof(enum ExtractFlags) <= sizeof(unsigned)); - -int extract_many_words(const char **p, const char *separators, unsigned flags, ...) { - va_list ap; - char **l; - int n = 0, i, c, r; - - /* Parses a number of words from a string, stripping any - * quotes if necessary. */ - - assert(p); - - /* Count how many words are expected */ - va_start(ap, flags); - for (;;) { - if (!va_arg(ap, char **)) - break; - n++; - } - va_end(ap); - - if (n <= 0) - return 0; - - /* Read all words into a temporary array */ - l = newa0(char*, n); - for (c = 0; c < n; c++) { - - r = extract_first_word(p, &l[c], separators, flags); - if (r < 0) { - int j; - - for (j = 0; j < c; j++) - free(l[j]); - - return r; - } - - if (r == 0) - break; - } - - /* If we managed to parse all words, return them in the passed - * in parameters */ - va_start(ap, flags); - for (i = 0; i < n; i++) { - char **v; - - v = va_arg(ap, char **); - assert(v); - - *v = l[i]; - } - va_end(ap); - - return c; -} -#endif /* NM_IGNORED */ diff --git a/shared/systemd/src/basic/extract-word.h b/shared/systemd/src/basic/extract-word.h deleted file mode 100644 index d1de32e5..00000000 --- a/shared/systemd/src/basic/extract-word.h +++ /dev/null @@ -1,18 +0,0 @@ -/* SPDX-License-Identifier: LGPL-2.1-or-later */ -#pragma once - -#include "macro.h" - -typedef enum ExtractFlags { - EXTRACT_RELAX = 1 << 0, - EXTRACT_CUNESCAPE = 1 << 1, - EXTRACT_CUNESCAPE_RELAX = 1 << 2, - EXTRACT_UNESCAPE_SEPARATORS = 1 << 3, - EXTRACT_UNQUOTE = 1 << 4, - EXTRACT_DONT_COALESCE_SEPARATORS = 1 << 5, - EXTRACT_RETAIN_ESCAPE = 1 << 6, -} ExtractFlags; - -int extract_first_word(const char **p, char **ret, const char *separators, ExtractFlags flags); -int extract_first_word_and_warn(const char **p, char **ret, const char *separators, ExtractFlags flags, const char *unit, const char *filename, unsigned line, const char *rvalue); -int extract_many_words(const char **p, const char *separators, unsigned flags, ...) _sentinel_; diff --git a/shared/systemd/src/basic/fd-util.c b/shared/systemd/src/basic/fd-util.c deleted file mode 100644 index e53cf18d..00000000 --- a/shared/systemd/src/basic/fd-util.c +++ /dev/null @@ -1,1071 +0,0 @@ -/* SPDX-License-Identifier: LGPL-2.1-or-later */ - -#include "nm-sd-adapt-shared.h" - -#include <errno.h> -#include <fcntl.h> -#include <sys/resource.h> -#include <sys/stat.h> -#include <unistd.h> - -#include "alloc-util.h" -#include "copy.h" -#include "dirent-util.h" -#include "fd-util.h" -#include "fileio.h" -#include "fs-util.h" -#include "io-util.h" -#include "macro.h" -#include "memfd-util.h" -#include "missing_fcntl.h" -#include "missing_syscall.h" -#include "parse-util.h" -#include "path-util.h" -#include "process-util.h" -#include "socket-util.h" -#include "sort-util.h" -#include "stat-util.h" -#include "stdio-util.h" -#include "tmpfile-util.h" -#include "util.h" - -/* The maximum number of iterations in the loop to close descriptors in the fallback case - * when /proc/self/fd/ is inaccessible. */ -#define MAX_FD_LOOP_LIMIT (1024*1024) - -int close_nointr(int fd) { - assert(fd >= 0); - - if (close(fd) >= 0) - return 0; - - /* - * Just ignore EINTR; a retry loop is the wrong thing to do on - * Linux. - * - * http://lkml.indiana.edu/hypermail/linux/kernel/0509.1/0877.html - * https://bugzilla.gnome.org/show_bug.cgi?id=682819 - * http://utcc.utoronto.ca/~cks/space/blog/unix/CloseEINTR - * https://sites.google.com/site/michaelsafyan/software-engineering/checkforeintrwheninvokingclosethinkagain - */ - if (errno == EINTR) - return 0; - - return -errno; -} - -int safe_close(int fd) { - - /* - * Like close_nointr() but cannot fail. Guarantees errno is - * unchanged. Is a NOP with negative fds passed, and returns - * -1, so that it can be used in this syntax: - * - * fd = safe_close(fd); - */ - - if (fd >= 0) { - PROTECT_ERRNO; - - /* The kernel might return pretty much any error code - * via close(), but the fd will be closed anyway. The - * only condition we want to check for here is whether - * the fd was invalid at all... */ - - assert_se(close_nointr(fd) != -EBADF); - } - - return -1; -} - -void safe_close_pair(int p[static 2]) { - assert(p); - - if (p[0] == p[1]) { - /* Special case pairs which use the same fd in both - * directions... */ - p[0] = p[1] = safe_close(p[0]); - return; - } - - p[0] = safe_close(p[0]); - p[1] = safe_close(p[1]); -} - -void close_many(const int fds[], size_t n_fd) { - size_t i; - - assert(fds || n_fd <= 0); - - for (i = 0; i < n_fd; i++) - safe_close(fds[i]); -} - -int fclose_nointr(FILE *f) { - assert(f); - - /* Same as close_nointr(), but for fclose() */ - - errno = 0; /* Extra safety: if the FILE* object is not encapsulating an fd, it might not set errno - * correctly. Let's hence initialize it to zero first, so that we aren't confused by any - * prior errno here */ - if (fclose(f) == 0) - return 0; - - if (errno == EINTR) - return 0; - - return errno_or_else(EIO); -} - -FILE* safe_fclose(FILE *f) { - - /* Same as safe_close(), but for fclose() */ - - if (f) { - PROTECT_ERRNO; - - assert_se(fclose_nointr(f) != -EBADF); - } - - return NULL; -} - -DIR* safe_closedir(DIR *d) { - - if (d) { - PROTECT_ERRNO; - - assert_se(closedir(d) >= 0 || errno != EBADF); - } - - return NULL; -} - -int fd_nonblock(int fd, bool nonblock) { - int flags, nflags; - - assert(fd >= 0); - - flags = fcntl(fd, F_GETFL, 0); - if (flags < 0) - return -errno; - - nflags = UPDATE_FLAG(flags, O_NONBLOCK, nonblock); - if (nflags == flags) - return 0; - - if (fcntl(fd, F_SETFL, nflags) < 0) - return -errno; - - return 0; -} - -int fd_cloexec(int fd, bool cloexec) { - int flags, nflags; - - assert(fd >= 0); - - flags = fcntl(fd, F_GETFD, 0); - if (flags < 0) - return -errno; - - nflags = UPDATE_FLAG(flags, FD_CLOEXEC, cloexec); - if (nflags == flags) - return 0; - - if (fcntl(fd, F_SETFD, nflags) < 0) - return -errno; - - return 0; -} - -#if 0 /* NM_IGNORED */ -_pure_ static bool fd_in_set(int fd, const int fdset[], size_t n_fdset) { - size_t i; - - assert(n_fdset == 0 || fdset); - - for (i = 0; i < n_fdset; i++) - if (fdset[i] == fd) - return true; - - return false; -} - -static int get_max_fd(void) { - struct rlimit rl; - rlim_t m; - - /* Return the highest possible fd, based RLIMIT_NOFILE, but enforcing FD_SETSIZE-1 as lower boundary - * and INT_MAX as upper boundary. */ - - if (getrlimit(RLIMIT_NOFILE, &rl) < 0) - return -errno; - - m = MAX(rl.rlim_cur, rl.rlim_max); - if (m < FD_SETSIZE) /* Let's always cover at least 1024 fds */ - return FD_SETSIZE-1; - - if (m == RLIM_INFINITY || m > INT_MAX) /* Saturate on overflow. After all fds are "int", hence can - * never be above INT_MAX */ - return INT_MAX; - - return (int) (m - 1); -} - -int close_all_fds(const int except[], size_t n_except) { - static bool have_close_range = true; /* Assume we live in the future */ - _cleanup_closedir_ DIR *d = NULL; - struct dirent *de; - int r = 0; - - assert(n_except == 0 || except); - - if (have_close_range) { - /* In the best case we have close_range() to close all fds between a start and an end fd, - * which we can use on the "inverted" exception array, i.e. all intervals between all - * adjacent pairs from the sorted exception array. This changes loop complexity from O(n) - * where n is number of open fds to O(m⋅log(m)) where m is the number of fds to keep - * open. Given that we assume n ≫ m that's preferable to us. */ - - if (n_except == 0) { - /* Close everything. Yay! */ - - if (close_range(3, -1, 0) >= 0) - return 1; - - if (!ERRNO_IS_NOT_SUPPORTED(errno) && !ERRNO_IS_PRIVILEGE(errno)) - return -errno; - - have_close_range = false; - } else { - _cleanup_free_ int *sorted_malloc = NULL; - size_t n_sorted; - int *sorted; - - assert(n_except < SIZE_MAX); - n_sorted = n_except + 1; - - if (n_sorted > 64) /* Use heap for large numbers of fds, stack otherwise */ - sorted = sorted_malloc = new(int, n_sorted); - else - sorted = newa(int, n_sorted); - - if (sorted) { - int c = 0; - - memcpy(sorted, except, n_except * sizeof(int)); - - /* Let's add fd 2 to the list of fds, to simplify the loop below, as this - * allows us to cover the head of the array the same way as the body */ - sorted[n_sorted-1] = 2; - - typesafe_qsort(sorted, n_sorted, cmp_int); - - for (size_t i = 0; i < n_sorted-1; i++) { - int start, end; - - start = MAX(sorted[i], 2); /* The first three fds shall always remain open */ - end = MAX(sorted[i+1], 2); - - assert(end >= start); - - if (end - start <= 1) - continue; - - /* Close everything between the start and end fds (both of which shall stay open) */ - if (close_range(start + 1, end - 1, 0) < 0) { - if (!ERRNO_IS_NOT_SUPPORTED(errno) && !ERRNO_IS_PRIVILEGE(errno)) - return -errno; - - have_close_range = false; - break; - } - - c += end - start - 1; - } - - if (have_close_range) { - /* The loop succeeded. Let's now close everything beyond the end */ - - if (sorted[n_sorted-1] >= INT_MAX) /* Dont let the addition below overflow */ - return c; - - if (close_range(sorted[n_sorted-1] + 1, -1, 0) >= 0) - return c + 1; - - if (!ERRNO_IS_NOT_SUPPORTED(errno) && !ERRNO_IS_PRIVILEGE(errno)) - return -errno; - - have_close_range = false; - } - } - } - - /* Fallback on OOM or if close_range() is not supported */ - } - - d = opendir("/proc/self/fd"); - if (!d) { - int fd, max_fd; - - /* When /proc isn't available (for example in chroots) the fallback is brute forcing through - * the fd table */ - - max_fd = get_max_fd(); - if (max_fd < 0) - return max_fd; - - /* Refuse to do the loop over more too many elements. It's better to fail immediately than to - * spin the CPU for a long time. */ - if (max_fd > MAX_FD_LOOP_LIMIT) - return log_debug_errno(SYNTHETIC_ERRNO(EPERM), - "/proc/self/fd is inaccessible. Refusing to loop over %d potential fds.", - max_fd); - - for (fd = 3; fd >= 0; fd = fd < max_fd ? fd + 1 : -1) { - int q; - - if (fd_in_set(fd, except, n_except)) - continue; - - q = close_nointr(fd); - if (q < 0 && q != -EBADF && r >= 0) - r = q; - } - - return r; - } - - FOREACH_DIRENT(de, d, return -errno) { - int fd = -1, q; - - if (safe_atoi(de->d_name, &fd) < 0) - /* Let's better ignore this, just in case */ - continue; - - if (fd < 3) - continue; - - if (fd == dirfd(d)) - continue; - - if (fd_in_set(fd, except, n_except)) - continue; - - q = close_nointr(fd); - if (q < 0 && q != -EBADF && r >= 0) /* Valgrind has its own FD and doesn't want to have it closed */ - r = q; - } - - return r; -} - -int same_fd(int a, int b) { - struct stat sta, stb; - pid_t pid; - int r, fa, fb; - - assert(a >= 0); - assert(b >= 0); - - /* Compares two file descriptors. Note that semantics are - * quite different depending on whether we have kcmp() or we - * don't. If we have kcmp() this will only return true for - * dup()ed file descriptors, but not otherwise. If we don't - * have kcmp() this will also return true for two fds of the same - * file, created by separate open() calls. Since we use this - * call mostly for filtering out duplicates in the fd store - * this difference hopefully doesn't matter too much. */ - - if (a == b) - return true; - - /* Try to use kcmp() if we have it. */ - pid = getpid_cached(); - r = kcmp(pid, pid, KCMP_FILE, a, b); - if (r == 0) - return true; - if (r > 0) - return false; - if (!IN_SET(errno, ENOSYS, EACCES, EPERM)) - return -errno; - - /* We don't have kcmp(), use fstat() instead. */ - if (fstat(a, &sta) < 0) - return -errno; - - if (fstat(b, &stb) < 0) - return -errno; - - if ((sta.st_mode & S_IFMT) != (stb.st_mode & S_IFMT)) - return false; - - /* We consider all device fds different, since two device fds - * might refer to quite different device contexts even though - * they share the same inode and backing dev_t. */ - - if (S_ISCHR(sta.st_mode) || S_ISBLK(sta.st_mode)) - return false; - - if (sta.st_dev != stb.st_dev || sta.st_ino != stb.st_ino) - return false; - - /* The fds refer to the same inode on disk, let's also check - * if they have the same fd flags. This is useful to - * distinguish the read and write side of a pipe created with - * pipe(). */ - fa = fcntl(a, F_GETFL); - if (fa < 0) - return -errno; - - fb = fcntl(b, F_GETFL); - if (fb < 0) - return -errno; - - return fa == fb; -} -#endif /* NM_IGNORED */ - -void cmsg_close_all(struct msghdr *mh) { - struct cmsghdr *cmsg; - - assert(mh); - - CMSG_FOREACH(cmsg, mh) - if (cmsg->cmsg_level == SOL_SOCKET && cmsg->cmsg_type == SCM_RIGHTS) - close_many((int*) CMSG_DATA(cmsg), (cmsg->cmsg_len - CMSG_LEN(0)) / sizeof(int)); -} - -bool fdname_is_valid(const char *s) { - const char *p; - - /* Validates a name for $LISTEN_FDNAMES. We basically allow - * everything ASCII that's not a control character. Also, as - * special exception the ":" character is not allowed, as we - * use that as field separator in $LISTEN_FDNAMES. - * - * Note that the empty string is explicitly allowed - * here. However, we limit the length of the names to 255 - * characters. */ - - if (!s) - return false; - - for (p = s; *p; p++) { - if (*p < ' ') - return false; - if (*p >= 127) - return false; - if (*p == ':') - return false; - } - - return p - s < 256; -} - -#if 0 /* NM_IGNORED */ -int fd_get_path(int fd, char **ret) { - char procfs_path[STRLEN("/proc/self/fd/") + DECIMAL_STR_MAX(int)]; - int r; - - xsprintf(procfs_path, "/proc/self/fd/%i", fd); - r = readlink_malloc(procfs_path, ret); - if (r == -ENOENT) { - /* ENOENT can mean two things: that the fd does not exist or that /proc is not mounted. Let's make - * things debuggable and distinguish the two. */ - - if (proc_mounted() == 0) - return -ENOSYS; /* /proc is not available or not set up properly, we're most likely in some chroot - * environment. */ - return -EBADF; /* The directory exists, hence it's the fd that doesn't. */ - } - - return r; -} - -int move_fd(int from, int to, int cloexec) { - int r; - - /* Move fd 'from' to 'to', make sure FD_CLOEXEC remains equal if requested, and release the old fd. If - * 'cloexec' is passed as -1, the original FD_CLOEXEC is inherited for the new fd. If it is 0, it is turned - * off, if it is > 0 it is turned on. */ - - if (from < 0) - return -EBADF; - if (to < 0) - return -EBADF; - - if (from == to) { - - if (cloexec >= 0) { - r = fd_cloexec(to, cloexec); - if (r < 0) - return r; - } - - return to; - } - - if (cloexec < 0) { - int fl; - - fl = fcntl(from, F_GETFD, 0); - if (fl < 0) - return -errno; - - cloexec = !!(fl & FD_CLOEXEC); - } - - r = dup3(from, to, cloexec ? O_CLOEXEC : 0); - if (r < 0) - return -errno; - - assert(r == to); - - safe_close(from); - - return to; -} - -int acquire_data_fd(const void *data, size_t size, unsigned flags) { - - _cleanup_close_pair_ int pipefds[2] = { -1, -1 }; - char pattern[] = "/dev/shm/data-fd-XXXXXX"; - _cleanup_close_ int fd = -1; - int isz = 0, r; - ssize_t n; - off_t f; - - assert(data || size == 0); - - /* Acquire a read-only file descriptor that when read from returns the specified data. This is much more - * complex than I wish it was. But here's why: - * - * a) First we try to use memfds. They are the best option, as we can seal them nicely to make them - * read-only. Unfortunately they require kernel 3.17, and – at the time of writing – we still support 3.14. - * - * b) Then, we try classic pipes. They are the second best options, as we can close the writing side, retaining - * a nicely read-only fd in the reading side. However, they are by default quite small, and unprivileged - * clients can only bump their size to a system-wide limit, which might be quite low. - * - * c) Then, we try an O_TMPFILE file in /dev/shm (that dir is the only suitable one known to exist from - * earliest boot on). To make it read-only we open the fd a second time with O_RDONLY via - * /proc/self/<fd>. Unfortunately O_TMPFILE is not available on older kernels on tmpfs. - * - * d) Finally, we try creating a regular file in /dev/shm, which we then delete. - * - * It sucks a bit that depending on the situation we return very different objects here, but that's Linux I - * figure. */ - - if (size == 0 && ((flags & ACQUIRE_NO_DEV_NULL) == 0)) { - /* As a special case, return /dev/null if we have been called for an empty data block */ - r = open("/dev/null", O_RDONLY|O_CLOEXEC|O_NOCTTY); - if (r < 0) - return -errno; - - return r; - } - - if ((flags & ACQUIRE_NO_MEMFD) == 0) { - fd = memfd_new("data-fd"); - if (fd < 0) - goto try_pipe; - - n = write(fd, data, size); - if (n < 0) - return -errno; - if ((size_t) n != size) - return -EIO; - - f = lseek(fd, 0, SEEK_SET); - if (f != 0) - return -errno; - - r = memfd_set_sealed(fd); - if (r < 0) - return r; - - return TAKE_FD(fd); - } - -try_pipe: - if ((flags & ACQUIRE_NO_PIPE) == 0) { - if (pipe2(pipefds, O_CLOEXEC|O_NONBLOCK) < 0) - return -errno; - - isz = fcntl(pipefds[1], F_GETPIPE_SZ, 0); - if (isz < 0) - return -errno; - - if ((size_t) isz < size) { - isz = (int) size; - if (isz < 0 || (size_t) isz != size) - return -E2BIG; - - /* Try to bump the pipe size */ - (void) fcntl(pipefds[1], F_SETPIPE_SZ, isz); - - /* See if that worked */ - isz = fcntl(pipefds[1], F_GETPIPE_SZ, 0); - if (isz < 0) - return -errno; - - if ((size_t) isz < size) - goto try_dev_shm; - } - - n = write(pipefds[1], data, size); - if (n < 0) - return -errno; - if ((size_t) n != size) - return -EIO; - - (void) fd_nonblock(pipefds[0], false); - - return TAKE_FD(pipefds[0]); - } - -try_dev_shm: - if ((flags & ACQUIRE_NO_TMPFILE) == 0) { - fd = open("/dev/shm", O_RDWR|O_TMPFILE|O_CLOEXEC, 0500); - if (fd < 0) - goto try_dev_shm_without_o_tmpfile; - - n = write(fd, data, size); - if (n < 0) - return -errno; - if ((size_t) n != size) - return -EIO; - - /* Let's reopen the thing, in order to get an O_RDONLY fd for the original O_RDWR one */ - return fd_reopen(fd, O_RDONLY|O_CLOEXEC); - } - -try_dev_shm_without_o_tmpfile: - if ((flags & ACQUIRE_NO_REGULAR) == 0) { - fd = mkostemp_safe(pattern); - if (fd < 0) - return fd; - - n = write(fd, data, size); - if (n < 0) { - r = -errno; - goto unlink_and_return; - } - if ((size_t) n != size) { - r = -EIO; - goto unlink_and_return; - } - - /* Let's reopen the thing, in order to get an O_RDONLY fd for the original O_RDWR one */ - r = open(pattern, O_RDONLY|O_CLOEXEC); - if (r < 0) - r = -errno; - - unlink_and_return: - (void) unlink(pattern); - return r; - } - - return -EOPNOTSUPP; -} - -/* When the data is smaller or equal to 64K, try to place the copy in a memfd/pipe */ -#define DATA_FD_MEMORY_LIMIT (64U*1024U) - -/* If memfd/pipe didn't work out, then let's use a file in /tmp up to a size of 1M. If it's large than that use /var/tmp instead. */ -#define DATA_FD_TMP_LIMIT (1024U*1024U) - -int fd_duplicate_data_fd(int fd) { - - _cleanup_close_ int copy_fd = -1, tmp_fd = -1; - _cleanup_free_ void *remains = NULL; - size_t remains_size = 0; - const char *td; - struct stat st; - int r; - - /* Creates a 'data' fd from the specified source fd, containing all the same data in a read-only fashion, but - * independent of it (i.e. the source fd can be closed and unmounted after this call succeeded). Tries to be - * somewhat smart about where to place the data. In the best case uses a memfd(). If memfd() are not supported - * uses a pipe instead. For larger data will use an unlinked file in /tmp, and for even larger data one in - * /var/tmp. */ - - if (fstat(fd, &st) < 0) - return -errno; - - /* For now, let's only accept regular files, sockets, pipes and char devices */ - if (S_ISDIR(st.st_mode)) - return -EISDIR; - if (S_ISLNK(st.st_mode)) - return -ELOOP; - if (!S_ISREG(st.st_mode) && !S_ISSOCK(st.st_mode) && !S_ISFIFO(st.st_mode) && !S_ISCHR(st.st_mode)) - return -EBADFD; - - /* If we have reason to believe the data is bounded in size, then let's use memfds or pipes as backing fd. Note - * that we use the reported regular file size only as a hint, given that there are plenty special files in - * /proc and /sys which report a zero file size but can be read from. */ - - if (!S_ISREG(st.st_mode) || st.st_size < DATA_FD_MEMORY_LIMIT) { - - /* Try a memfd first */ - copy_fd = memfd_new("data-fd"); - if (copy_fd >= 0) { - off_t f; - - r = copy_bytes(fd, copy_fd, DATA_FD_MEMORY_LIMIT, 0); - if (r < 0) - return r; - - f = lseek(copy_fd, 0, SEEK_SET); - if (f != 0) - return -errno; - - if (r == 0) { - /* Did it fit into the limit? If so, we are done. */ - r = memfd_set_sealed(copy_fd); - if (r < 0) - return r; - - return TAKE_FD(copy_fd); - } - - /* Hmm, pity, this didn't fit. Let's fall back to /tmp then, see below */ - - } else { - _cleanup_(close_pairp) int pipefds[2] = { -1, -1 }; - int isz; - - /* If memfds aren't available, use a pipe. Set O_NONBLOCK so that we will get EAGAIN rather - * then block indefinitely when we hit the pipe size limit */ - - if (pipe2(pipefds, O_CLOEXEC|O_NONBLOCK) < 0) - return -errno; - - isz = fcntl(pipefds[1], F_GETPIPE_SZ, 0); - if (isz < 0) - return -errno; - - /* Try to enlarge the pipe size if necessary */ - if ((size_t) isz < DATA_FD_MEMORY_LIMIT) { - - (void) fcntl(pipefds[1], F_SETPIPE_SZ, DATA_FD_MEMORY_LIMIT); - - isz = fcntl(pipefds[1], F_GETPIPE_SZ, 0); - if (isz < 0) - return -errno; - } - - if ((size_t) isz >= DATA_FD_MEMORY_LIMIT) { - - r = copy_bytes_full(fd, pipefds[1], DATA_FD_MEMORY_LIMIT, 0, &remains, &remains_size, NULL, NULL); - if (r < 0 && r != -EAGAIN) - return r; /* If we get EAGAIN it could be because of the source or because of - * the destination fd, we can't know, as sendfile() and friends won't - * tell us. Hence, treat this as reason to fall back, just to be - * sure. */ - if (r == 0) { - /* Everything fit in, yay! */ - (void) fd_nonblock(pipefds[0], false); - - return TAKE_FD(pipefds[0]); - } - - /* Things didn't fit in. But we read data into the pipe, let's remember that, so that - * when writing the new file we incorporate this first. */ - copy_fd = TAKE_FD(pipefds[0]); - } - } - } - - /* If we have reason to believe this will fit fine in /tmp, then use that as first fallback. */ - if ((!S_ISREG(st.st_mode) || st.st_size < DATA_FD_TMP_LIMIT) && - (DATA_FD_MEMORY_LIMIT + remains_size) < DATA_FD_TMP_LIMIT) { - off_t f; - - tmp_fd = open_tmpfile_unlinkable(NULL /* NULL as directory means /tmp */, O_RDWR|O_CLOEXEC); - if (tmp_fd < 0) - return tmp_fd; - - if (copy_fd >= 0) { - /* If we tried a memfd/pipe first and it ended up being too large, then copy this into the - * temporary file first. */ - - r = copy_bytes(copy_fd, tmp_fd, UINT64_MAX, 0); - if (r < 0) - return r; - - assert(r == 0); - } - - if (remains_size > 0) { - /* If there were remaining bytes (i.e. read into memory, but not written out yet) from the - * failed copy operation, let's flush them out next. */ - - r = loop_write(tmp_fd, remains, remains_size, false); - if (r < 0) - return r; - } - - r = copy_bytes(fd, tmp_fd, DATA_FD_TMP_LIMIT - DATA_FD_MEMORY_LIMIT - remains_size, COPY_REFLINK); - if (r < 0) - return r; - if (r == 0) - goto finish; /* Yay, it fit in */ - - /* It didn't fit in. Let's not forget to use what we already used */ - f = lseek(tmp_fd, 0, SEEK_SET); - if (f != 0) - return -errno; - - CLOSE_AND_REPLACE(copy_fd, tmp_fd); - - remains = mfree(remains); - remains_size = 0; - } - - /* As last fallback use /var/tmp */ - r = var_tmp_dir(&td); - if (r < 0) - return r; - - tmp_fd = open_tmpfile_unlinkable(td, O_RDWR|O_CLOEXEC); - if (tmp_fd < 0) - return tmp_fd; - - if (copy_fd >= 0) { - /* If we tried a memfd/pipe first, or a file in /tmp, and it ended up being too large, than copy this - * into the temporary file first. */ - r = copy_bytes(copy_fd, tmp_fd, UINT64_MAX, COPY_REFLINK); - if (r < 0) - return r; - - assert(r == 0); - } - - if (remains_size > 0) { - /* Then, copy in any read but not yet written bytes. */ - r = loop_write(tmp_fd, remains, remains_size, false); - if (r < 0) - return r; - } - - /* Copy in the rest */ - r = copy_bytes(fd, tmp_fd, UINT64_MAX, COPY_REFLINK); - if (r < 0) - return r; - - assert(r == 0); - -finish: - /* Now convert the O_RDWR file descriptor into an O_RDONLY one (and as side effect seek to the beginning of the - * file again */ - - return fd_reopen(tmp_fd, O_RDONLY|O_CLOEXEC); -} -#endif /* NM_IGNORED */ - -int fd_move_above_stdio(int fd) { - int flags, copy; - PROTECT_ERRNO; - - /* Moves the specified file descriptor if possible out of the range [0…2], i.e. the range of - * stdin/stdout/stderr. If it can't be moved outside of this range the original file descriptor is - * returned. This call is supposed to be used for long-lasting file descriptors we allocate in our code that - * might get loaded into foreign code, and where we want ensure our fds are unlikely used accidentally as - * stdin/stdout/stderr of unrelated code. - * - * Note that this doesn't fix any real bugs, it just makes it less likely that our code will be affected by - * buggy code from others that mindlessly invokes 'fprintf(stderr, …' or similar in places where stderr has - * been closed before. - * - * This function is written in a "best-effort" and "least-impact" style. This means whenever we encounter an - * error we simply return the original file descriptor, and we do not touch errno. */ - - if (fd < 0 || fd > 2) - return fd; - - flags = fcntl(fd, F_GETFD, 0); - if (flags < 0) - return fd; - - if (flags & FD_CLOEXEC) - copy = fcntl(fd, F_DUPFD_CLOEXEC, 3); - else - copy = fcntl(fd, F_DUPFD, 3); - if (copy < 0) - return fd; - - assert(copy > 2); - - (void) close(fd); - return copy; -} - -#if 0 /* NM_IGNORED */ -int rearrange_stdio(int original_input_fd, int original_output_fd, int original_error_fd) { - - int fd[3] = { /* Put together an array of fds we work on */ - original_input_fd, - original_output_fd, - original_error_fd - }; - - int r, i, - null_fd = -1, /* if we open /dev/null, we store the fd to it here */ - copy_fd[3] = { -1, -1, -1 }; /* This contains all fds we duplicate here temporarily, and hence need to close at the end */ - bool null_readable, null_writable; - - /* Sets up stdin, stdout, stderr with the three file descriptors passed in. If any of the descriptors is - * specified as -1 it will be connected with /dev/null instead. If any of the file descriptors is passed as - * itself (e.g. stdin as STDIN_FILENO) it is left unmodified, but the O_CLOEXEC bit is turned off should it be - * on. - * - * Note that if any of the passed file descriptors are > 2 they will be closed — both on success and on - * failure! Thus, callers should assume that when this function returns the input fds are invalidated. - * - * Note that when this function fails stdin/stdout/stderr might remain half set up! - * - * O_CLOEXEC is turned off for all three file descriptors (which is how it should be for - * stdin/stdout/stderr). */ - - null_readable = original_input_fd < 0; - null_writable = original_output_fd < 0 || original_error_fd < 0; - - /* First step, open /dev/null once, if we need it */ - if (null_readable || null_writable) { - - /* Let's open this with O_CLOEXEC first, and convert it to non-O_CLOEXEC when we move the fd to the final position. */ - null_fd = open("/dev/null", (null_readable && null_writable ? O_RDWR : - null_readable ? O_RDONLY : O_WRONLY) | O_CLOEXEC); - if (null_fd < 0) { - r = -errno; - goto finish; - } - - /* If this fd is in the 0…2 range, let's move it out of it */ - if (null_fd < 3) { - int copy; - - copy = fcntl(null_fd, F_DUPFD_CLOEXEC, 3); /* Duplicate this with O_CLOEXEC set */ - if (copy < 0) { - r = -errno; - goto finish; - } - - CLOSE_AND_REPLACE(null_fd, copy); - } - } - - /* Let's assemble fd[] with the fds to install in place of stdin/stdout/stderr */ - for (i = 0; i < 3; i++) { - - if (fd[i] < 0) - fd[i] = null_fd; /* A negative parameter means: connect this one to /dev/null */ - else if (fd[i] != i && fd[i] < 3) { - /* This fd is in the 0…2 territory, but not at its intended place, move it out of there, so that we can work there. */ - copy_fd[i] = fcntl(fd[i], F_DUPFD_CLOEXEC, 3); /* Duplicate this with O_CLOEXEC set */ - if (copy_fd[i] < 0) { - r = -errno; - goto finish; - } - - fd[i] = copy_fd[i]; - } - } - - /* At this point we now have the fds to use in fd[], and they are all above the stdio range, so that we - * have freedom to move them around. If the fds already were at the right places then the specific fds are - * -1. Let's now move them to the right places. This is the point of no return. */ - for (i = 0; i < 3; i++) { - - if (fd[i] == i) { - - /* fd is already in place, but let's make sure O_CLOEXEC is off */ - r = fd_cloexec(i, false); - if (r < 0) - goto finish; - - } else { - assert(fd[i] > 2); - - if (dup2(fd[i], i) < 0) { /* Turns off O_CLOEXEC on the new fd. */ - r = -errno; - goto finish; - } - } - } - - r = 0; - -finish: - /* Close the original fds, but only if they were outside of the stdio range. Also, properly check for the same - * fd passed in multiple times. */ - safe_close_above_stdio(original_input_fd); - if (original_output_fd != original_input_fd) - safe_close_above_stdio(original_output_fd); - if (original_error_fd != original_input_fd && original_error_fd != original_output_fd) - safe_close_above_stdio(original_error_fd); - - /* Close the copies we moved > 2 */ - for (i = 0; i < 3; i++) - safe_close(copy_fd[i]); - - /* Close our null fd, if it's > 2 */ - safe_close_above_stdio(null_fd); - - return r; -} - -int fd_reopen(int fd, int flags) { - char procfs_path[STRLEN("/proc/self/fd/") + DECIMAL_STR_MAX(int)]; - int new_fd; - - /* Reopens the specified fd with new flags. This is useful for convert an O_PATH fd into a regular one, or to - * turn O_RDWR fds into O_RDONLY fds. - * - * This doesn't work on sockets (since they cannot be open()ed, ever). - * - * This implicitly resets the file read index to 0. */ - - xsprintf(procfs_path, "/proc/self/fd/%i", fd); - new_fd = open(procfs_path, flags); - if (new_fd < 0) { - if (errno != ENOENT) - return -errno; - - if (proc_mounted() == 0) - return -ENOSYS; /* if we have no /proc/, the concept is not implementable */ - - return -ENOENT; - } - - return new_fd; -} - -int read_nr_open(void) { - _cleanup_free_ char *nr_open = NULL; - int r; - - /* Returns the kernel's current fd limit, either by reading it of /proc/sys if that works, or using the - * hard-coded default compiled-in value of current kernels (1M) if not. This call will never fail. */ - - r = read_one_line_file("/proc/sys/fs/nr_open", &nr_open); - if (r < 0) - log_debug_errno(r, "Failed to read /proc/sys/fs/nr_open, ignoring: %m"); - else { - int v; - - r = safe_atoi(nr_open, &v); - if (r < 0) - log_debug_errno(r, "Failed to parse /proc/sys/fs/nr_open value '%s', ignoring: %m", nr_open); - else - return v; - } - - /* If we fail, fall back to the hard-coded kernel limit of 1024 * 1024. */ - return 1024 * 1024; -} -#endif /* NM_IGNORED */ diff --git a/shared/systemd/src/basic/fd-util.h b/shared/systemd/src/basic/fd-util.h deleted file mode 100644 index 2162537b..00000000 --- a/shared/systemd/src/basic/fd-util.h +++ /dev/null @@ -1,108 +0,0 @@ -/* SPDX-License-Identifier: LGPL-2.1-or-later */ -#pragma once - -#include <dirent.h> -#include <stdbool.h> -#include <stdio.h> -#include <sys/socket.h> - -#include "macro.h" - -/* Make sure we can distinguish fd 0 and NULL */ -#define FD_TO_PTR(fd) INT_TO_PTR((fd)+1) -#define PTR_TO_FD(p) (PTR_TO_INT(p)-1) - -int close_nointr(int fd); -int safe_close(int fd); -void safe_close_pair(int p[static 2]); - -static inline int safe_close_above_stdio(int fd) { - if (fd < 3) /* Don't close stdin/stdout/stderr, but still invalidate the fd by returning -1 */ - return -1; - - return safe_close(fd); -} - -void close_many(const int fds[], size_t n_fd); - -int fclose_nointr(FILE *f); -FILE* safe_fclose(FILE *f); -DIR* safe_closedir(DIR *f); - -static inline void closep(int *fd) { - safe_close(*fd); -} - -static inline void close_pairp(int (*p)[2]) { - safe_close_pair(*p); -} - -static inline void fclosep(FILE **f) { - safe_fclose(*f); -} - -DEFINE_TRIVIAL_CLEANUP_FUNC(FILE*, pclose); -DEFINE_TRIVIAL_CLEANUP_FUNC(DIR*, closedir); - -#define _cleanup_close_ _cleanup_(closep) -#define _cleanup_fclose_ _cleanup_(fclosep) -#define _cleanup_pclose_ _cleanup_(pclosep) -#define _cleanup_closedir_ _cleanup_(closedirp) -#define _cleanup_close_pair_ _cleanup_(close_pairp) - -int fd_nonblock(int fd, bool nonblock); -int fd_cloexec(int fd, bool cloexec); - -int close_all_fds(const int except[], size_t n_except); - -int same_fd(int a, int b); - -void cmsg_close_all(struct msghdr *mh); - -bool fdname_is_valid(const char *s); - -int fd_get_path(int fd, char **ret); - -int move_fd(int from, int to, int cloexec); - -enum { - ACQUIRE_NO_DEV_NULL = 1 << 0, - ACQUIRE_NO_MEMFD = 1 << 1, - ACQUIRE_NO_PIPE = 1 << 2, - ACQUIRE_NO_TMPFILE = 1 << 3, - ACQUIRE_NO_REGULAR = 1 << 4, -}; - -int acquire_data_fd(const void *data, size_t size, unsigned flags); - -int fd_duplicate_data_fd(int fd); - -int fd_move_above_stdio(int fd); - -int rearrange_stdio(int original_input_fd, int original_output_fd, int original_error_fd); - -static inline int make_null_stdio(void) { - return rearrange_stdio(-1, -1, -1); -} - -/* Like TAKE_PTR() but for file descriptors, resetting them to -1 */ -#define TAKE_FD(fd) \ - ({ \ - int _fd_ = (fd); \ - (fd) = -1; \ - _fd_; \ - }) - -/* Like free_and_replace(), but for file descriptors */ -#define CLOSE_AND_REPLACE(a, b) \ - ({ \ - int *_fdp_ = &(a); \ - safe_close(*_fdp_); \ - *_fdp_ = TAKE_FD(b); \ - 0; \ - }) - - -int fd_reopen(int fd, int flags); - -int read_nr_open(void); diff --git a/shared/systemd/src/basic/fileio.c b/shared/systemd/src/basic/fileio.c deleted file mode 100644 index ea90614a..00000000 --- a/shared/systemd/src/basic/fileio.c +++ /dev/null @@ -1,1360 +0,0 @@ -/* SPDX-License-Identifier: LGPL-2.1-or-later */ - -#include "nm-sd-adapt-shared.h" - -#include <ctype.h> -#include <errno.h> -#include <fcntl.h> -#include <limits.h> -#include <stdarg.h> -#include <stdint.h> -#include <stdio_ext.h> -#include <stdlib.h> -#include <sys/stat.h> -#include <sys/types.h> -#include <unistd.h> - -#include "alloc-util.h" -#include "fd-util.h" -#include "fileio.h" -#include "fs-util.h" -#include "hexdecoct.h" -#include "log.h" -#include "macro.h" -#include "mkdir.h" -#include "parse-util.h" -#include "path-util.h" -#include "socket-util.h" -#include "stdio-util.h" -#include "string-util.h" -#include "tmpfile-util.h" - -#define READ_FULL_BYTES_MAX (4U*1024U*1024U) - -int fopen_unlocked(const char *path, const char *options, FILE **ret) { - assert(ret); - - FILE *f = fopen(path, options); - if (!f) - return -errno; - - (void) __fsetlocking(f, FSETLOCKING_BYCALLER); - - *ret = f; - return 0; -} - -int fdopen_unlocked(int fd, const char *options, FILE **ret) { - assert(ret); - - FILE *f = fdopen(fd, options); - if (!f) - return -errno; - - (void) __fsetlocking(f, FSETLOCKING_BYCALLER); - - *ret = f; - return 0; -} - -int take_fdopen_unlocked(int *fd, const char *options, FILE **ret) { - int r; - - assert(fd); - - r = fdopen_unlocked(*fd, options, ret); - if (r < 0) - return r; - - *fd = -1; - - return 0; -} - -FILE* take_fdopen(int *fd, const char *options) { - assert(fd); - - FILE *f = fdopen(*fd, options); - if (!f) - return NULL; - - *fd = -1; - - return f; -} - -DIR* take_fdopendir(int *dfd) { - assert(dfd); - - DIR *d = fdopendir(*dfd); - if (!d) - return NULL; - - *dfd = -1; - - return d; -} - -FILE* open_memstream_unlocked(char **ptr, size_t *sizeloc) { - FILE *f = open_memstream(ptr, sizeloc); - if (!f) - return NULL; - - (void) __fsetlocking(f, FSETLOCKING_BYCALLER); - - return f; -} - -FILE* fmemopen_unlocked(void *buf, size_t size, const char *mode) { - FILE *f = fmemopen(buf, size, mode); - if (!f) - return NULL; - - (void) __fsetlocking(f, FSETLOCKING_BYCALLER); - - return f; -} - -#if 0 /* NM_IGNORED */ -int write_string_stream_ts( - FILE *f, - const char *line, - WriteStringFileFlags flags, - const struct timespec *ts) { - - bool needs_nl; - int r, fd; - - assert(f); - assert(line); - - if (ferror(f)) - return -EIO; - - if (ts) { - /* If we shall set the timestamp we need the fd. But fmemopen() streams generally don't have - * an fd. Let's fail early in that case. */ - fd = fileno(f); - if (fd < 0) - return -EBADF; - } - - needs_nl = !(flags & WRITE_STRING_FILE_AVOID_NEWLINE) && !endswith(line, "\n"); - - if (needs_nl && (flags & WRITE_STRING_FILE_DISABLE_BUFFER)) { - /* If STDIO buffering was disabled, then let's append the newline character to the string itself, so - * that the write goes out in one go, instead of two */ - - line = strjoina(line, "\n"); - needs_nl = false; - } - - if (fputs(line, f) == EOF) - return -errno; - - if (needs_nl) - if (fputc('\n', f) == EOF) - return -errno; - - if (flags & WRITE_STRING_FILE_SYNC) - r = fflush_sync_and_check(f); - else - r = fflush_and_check(f); - if (r < 0) - return r; - - if (ts) { - const struct timespec twice[2] = {*ts, *ts}; - - if (futimens(fd, twice) < 0) - return -errno; - } - - return 0; -} - -static int write_string_file_atomic( - const char *fn, - const char *line, - WriteStringFileFlags flags, - const struct timespec *ts) { - - _cleanup_fclose_ FILE *f = NULL; - _cleanup_free_ char *p = NULL; - int r; - - assert(fn); - assert(line); - - /* Note that we'd really like to use O_TMPFILE here, but can't really, since we want replacement - * semantics here, and O_TMPFILE can't offer that. i.e. rename() replaces but linkat() doesn't. */ - - r = fopen_temporary(fn, &f, &p); - if (r < 0) - return r; - - r = write_string_stream_ts(f, line, flags, ts); - if (r < 0) - goto fail; - - r = fchmod_umask(fileno(f), FLAGS_SET(flags, WRITE_STRING_FILE_MODE_0600) ? 0600 : 0644); - if (r < 0) - goto fail; - - if (rename(p, fn) < 0) { - r = -errno; - goto fail; - } - - if (FLAGS_SET(flags, WRITE_STRING_FILE_SYNC)) { - /* Sync the rename, too */ - r = fsync_directory_of_file(fileno(f)); - if (r < 0) - return r; - } - - return 0; - -fail: - (void) unlink(p); - return r; -} - -int write_string_file_ts( - const char *fn, - const char *line, - WriteStringFileFlags flags, - const struct timespec *ts) { - - _cleanup_fclose_ FILE *f = NULL; - int q, r, fd; - - assert(fn); - assert(line); - - /* We don't know how to verify whether the file contents was already on-disk. */ - assert(!((flags & WRITE_STRING_FILE_VERIFY_ON_FAILURE) && (flags & WRITE_STRING_FILE_SYNC))); - - if (flags & WRITE_STRING_FILE_MKDIR_0755) { - r = mkdir_parents(fn, 0755); - if (r < 0) - return r; - } - - if (flags & WRITE_STRING_FILE_ATOMIC) { - assert(flags & WRITE_STRING_FILE_CREATE); - - r = write_string_file_atomic(fn, line, flags, ts); - if (r < 0) - goto fail; - - return r; - } else - assert(!ts); - - /* We manually build our own version of fopen(..., "we") that works without O_CREAT and with O_NOFOLLOW if needed. */ - fd = open(fn, O_WRONLY|O_CLOEXEC|O_NOCTTY | - (FLAGS_SET(flags, WRITE_STRING_FILE_NOFOLLOW) ? O_NOFOLLOW : 0) | - (FLAGS_SET(flags, WRITE_STRING_FILE_CREATE) ? O_CREAT : 0) | - (FLAGS_SET(flags, WRITE_STRING_FILE_TRUNCATE) ? O_TRUNC : 0), - (FLAGS_SET(flags, WRITE_STRING_FILE_MODE_0600) ? 0600 : 0666)); - if (fd < 0) { - r = -errno; - goto fail; - } - - r = fdopen_unlocked(fd, "w", &f); - if (r < 0) { - safe_close(fd); - goto fail; - } - - if (flags & WRITE_STRING_FILE_DISABLE_BUFFER) - setvbuf(f, NULL, _IONBF, 0); - - r = write_string_stream_ts(f, line, flags, ts); - if (r < 0) - goto fail; - - return 0; - -fail: - if (!(flags & WRITE_STRING_FILE_VERIFY_ON_FAILURE)) - return r; - - f = safe_fclose(f); - - /* OK, the operation failed, but let's see if the right - * contents in place already. If so, eat up the error. */ - - q = verify_file(fn, line, !(flags & WRITE_STRING_FILE_AVOID_NEWLINE)); - if (q <= 0) - return r; - - return 0; -} - -int write_string_filef( - const char *fn, - WriteStringFileFlags flags, - const char *format, ...) { - - _cleanup_free_ char *p = NULL; - va_list ap; - int r; - - va_start(ap, format); - r = vasprintf(&p, format, ap); - va_end(ap); - - if (r < 0) - return -ENOMEM; - - return write_string_file(fn, p, flags); -} - -int read_one_line_file(const char *fn, char **line) { - _cleanup_fclose_ FILE *f = NULL; - int r; - - assert(fn); - assert(line); - - r = fopen_unlocked(fn, "re", &f); - if (r < 0) - return r; - - return read_line(f, LONG_LINE_MAX, line); -} - -int verify_file(const char *fn, const char *blob, bool accept_extra_nl) { - _cleanup_fclose_ FILE *f = NULL; - _cleanup_free_ char *buf = NULL; - size_t l, k; - int r; - - assert(fn); - assert(blob); - - l = strlen(blob); - - if (accept_extra_nl && endswith(blob, "\n")) - accept_extra_nl = false; - - buf = malloc(l + accept_extra_nl + 1); - if (!buf) - return -ENOMEM; - - r = fopen_unlocked(fn, "re", &f); - if (r < 0) - return r; - - /* We try to read one byte more than we need, so that we know whether we hit eof */ - errno = 0; - k = fread(buf, 1, l + accept_extra_nl + 1, f); - if (ferror(f)) - return errno_or_else(EIO); - - if (k != l && k != l + accept_extra_nl) - return 0; - if (memcmp(buf, blob, l) != 0) - return 0; - if (k > l && buf[l] != '\n') - return 0; - - return 1; -} -#endif /* NM_IGNORED */ - -int read_full_virtual_file(const char *filename, char **ret_contents, size_t *ret_size) { - _cleanup_free_ char *buf = NULL; - _cleanup_close_ int fd = -1; - struct stat st; - size_t n, size; - int n_retries; - char *p; - - assert(ret_contents); - - /* Virtual filesystems such as sysfs or procfs use kernfs, and kernfs can work - * with two sorts of virtual files. One sort uses "seq_file", and the results of - * the first read are buffered for the second read. The other sort uses "raw" - * reads which always go direct to the device. In the latter case, the content of - * the virtual file must be retrieved with a single read otherwise a second read - * might get the new value instead of finding EOF immediately. That's the reason - * why the usage of fread(3) is prohibited in this case as it always performs a - * second call to read(2) looking for EOF. See issue 13585. */ - - fd = open(filename, O_RDONLY|O_CLOEXEC); - if (fd < 0) - return -errno; - - /* Start size for files in /proc which usually report a file size of 0. */ - size = LINE_MAX / 2; - - /* Limit the number of attempts to read the number of bytes returned by fstat(). */ - n_retries = 3; - - for (;;) { - if (n_retries <= 0) - return -EIO; - - if (fstat(fd, &st) < 0) - return -errno; - - if (!S_ISREG(st.st_mode)) - return -EBADF; - - /* Be prepared for files from /proc which generally report a file size of 0. */ - if (st.st_size > 0) { - size = st.st_size; - n_retries--; - } else - size = size * 2; - - if (size > READ_FULL_BYTES_MAX) - return -E2BIG; - - p = realloc(buf, size + 1); - if (!p) - return -ENOMEM; - buf = TAKE_PTR(p); - - for (;;) { - ssize_t k; - - /* Read one more byte so we can detect whether the content of the - * file has already changed or the guessed size for files from /proc - * wasn't large enough . */ - k = read(fd, buf, size + 1); - if (k >= 0) { - n = k; - break; - } - - if (errno != EINTR) - return -errno; - } - - /* Consider a short read as EOF */ - if (n <= size) - break; - - /* Hmm... either we read too few bytes from /proc or less likely the content - * of the file might have been changed (and is now bigger) while we were - * processing, let's try again either with a bigger guessed size or the new - * file size. */ - - if (lseek(fd, 0, SEEK_SET) < 0) - return -errno; - } - - if (n < size) { - p = realloc(buf, n + 1); - if (!p) - return -ENOMEM; - buf = TAKE_PTR(p); - } - - if (!ret_size) { - /* Safety check: if the caller doesn't want to know the size of what we - * just read it will rely on the trailing NUL byte. But if there's an - * embedded NUL byte, then we should refuse operation as otherwise - * there'd be ambiguity about what we just read. */ - - if (memchr(buf, 0, n)) - return -EBADMSG; - } else - *ret_size = n; - - buf[n] = 0; - *ret_contents = TAKE_PTR(buf); - - return 0; -} - -int read_full_stream_full( - FILE *f, - const char *filename, - uint64_t offset, - size_t size, - ReadFullFileFlags flags, - char **ret_contents, - size_t *ret_size) { - - _cleanup_free_ char *buf = NULL; - size_t n, n_next, l; - int fd, r; - - assert(f); - assert(ret_contents); - assert(!FLAGS_SET(flags, READ_FULL_FILE_UNBASE64 | READ_FULL_FILE_UNHEX)); - - if (offset != UINT64_MAX && offset > LONG_MAX) - return -ERANGE; - - n_next = size != SIZE_MAX ? size : LINE_MAX; /* Start size */ - - fd = fileno(f); - if (fd >= 0) { /* If the FILE* object is backed by an fd (as opposed to memory or such, see - * fmemopen()), let's optimize our buffering */ - struct stat st; - - if (fstat(fd, &st) < 0) - return -errno; - - if (S_ISREG(st.st_mode)) { - if (size == SIZE_MAX) { - uint64_t rsize = - LESS_BY((uint64_t) st.st_size, offset == UINT64_MAX ? 0 : offset); - - /* Safety check */ - if (rsize > READ_FULL_BYTES_MAX) - return -E2BIG; - - /* Start with the right file size. Note that we increase the size to read - * here by one, so that the first read attempt already makes us notice the - * EOF. If the reported size of the file is zero, we avoid this logic - * however, since quite likely it might be a virtual file in procfs that all - * report a zero file size. */ - if (st.st_size > 0) - n_next = rsize + 1; - } - - if (flags & READ_FULL_FILE_WARN_WORLD_READABLE) - (void) warn_file_is_world_accessible(filename, &st, NULL, 0); - } - } - - if (offset != UINT64_MAX && fseek(f, offset, SEEK_SET) < 0) - return -errno; - - n = l = 0; - for (;;) { - char *t; - size_t k; - - if (flags & READ_FULL_FILE_SECURE) { - t = malloc(n_next + 1); - if (!t) { - r = -ENOMEM; - goto finalize; - } - memcpy_safe(t, buf, n); - explicit_bzero_safe(buf, n); - buf = mfree(buf); - } else { - t = realloc(buf, n_next + 1); - if (!t) - return -ENOMEM; - } - - buf = t; - n = n_next; - - errno = 0; - k = fread(buf + l, 1, n - l, f); - - assert(k <= n - l); - l += k; - - if (ferror(f)) { - r = errno_or_else(EIO); - goto finalize; - } - if (feof(f)) - break; - - if (size != SIZE_MAX) { /* If we got asked to read some specific size, we already sized the buffer right, hence leave */ - assert(l == size); - break; - } - - assert(k > 0); /* we can't have read zero bytes because that would have been EOF */ - - /* Safety check */ - if (n >= READ_FULL_BYTES_MAX) { - r = -E2BIG; - goto finalize; - } - - n_next = MIN(n * 2, READ_FULL_BYTES_MAX); - } - - if (flags & (READ_FULL_FILE_UNBASE64 | READ_FULL_FILE_UNHEX)) { - _cleanup_free_ void *decoded = NULL; - size_t decoded_size; - - buf[l++] = 0; - if (flags & READ_FULL_FILE_UNBASE64) - r = unbase64mem_full(buf, l, flags & READ_FULL_FILE_SECURE, &decoded, &decoded_size); - else - r = unhexmem_full(buf, l, flags & READ_FULL_FILE_SECURE, &decoded, &decoded_size); - if (r < 0) - goto finalize; - - if (flags & READ_FULL_FILE_SECURE) - explicit_bzero_safe(buf, n); - free_and_replace(buf, decoded); - n = l = decoded_size; - } - - if (!ret_size) { - /* Safety check: if the caller doesn't want to know the size of what we just read it will rely on the - * trailing NUL byte. But if there's an embedded NUL byte, then we should refuse operation as otherwise - * there'd be ambiguity about what we just read. */ - - if (memchr(buf, 0, l)) { - r = -EBADMSG; - goto finalize; - } - } - - buf[l] = 0; - *ret_contents = TAKE_PTR(buf); - - if (ret_size) - *ret_size = l; - - return 0; - -finalize: - if (flags & READ_FULL_FILE_SECURE) - explicit_bzero_safe(buf, n); - - return r; -} - -int read_full_file_full( - int dir_fd, - const char *filename, - uint64_t offset, - size_t size, - ReadFullFileFlags flags, - const char *bind_name, - char **ret_contents, - size_t *ret_size) { - - _cleanup_fclose_ FILE *f = NULL; - int r; - - assert(filename); - assert(ret_contents); - - r = xfopenat(dir_fd, filename, "re", 0, &f); - if (r < 0) { - _cleanup_close_ int dfd = -1, sk = -1; - union sockaddr_union sa; - - /* ENXIO is what Linux returns if we open a node that is an AF_UNIX socket */ - if (r != -ENXIO) - return r; - - /* If this is enabled, let's try to connect to it */ - if (!FLAGS_SET(flags, READ_FULL_FILE_CONNECT_SOCKET)) - return -ENXIO; - - /* Seeking is not supported on AF_UNIX sockets */ - if (offset != UINT64_MAX) - return -ESPIPE; - - if (dir_fd == AT_FDCWD) - r = sockaddr_un_set_path(&sa.un, filename); - else { - char procfs_path[STRLEN("/proc/self/fd/") + DECIMAL_STR_MAX(int)]; - - /* If we shall operate relative to some directory, then let's use O_PATH first to - * open the socket inode, and then connect to it via /proc/self/fd/. We have to do - * this since there's not connectat() that takes a directory fd as first arg. */ - - dfd = openat(dir_fd, filename, O_PATH|O_CLOEXEC); - if (dfd < 0) - return -errno; - - xsprintf(procfs_path, "/proc/self/fd/%i", dfd); - r = sockaddr_un_set_path(&sa.un, procfs_path); - } - if (r < 0) - return r; - - sk = socket(AF_UNIX, SOCK_STREAM|SOCK_CLOEXEC, 0); - if (sk < 0) - return -errno; - - if (bind_name) { - /* If the caller specified a socket name to bind to, do so before connecting. This is - * useful to communicate some minor, short meta-information token from the client to - * the server. */ - union sockaddr_union bsa; - - r = sockaddr_un_set_path(&bsa.un, bind_name); - if (r < 0) - return r; - - if (bind(sk, &bsa.sa, r) < 0) - return r; - } - - if (connect(sk, &sa.sa, SOCKADDR_UN_LEN(sa.un)) < 0) - return errno == ENOTSOCK ? -ENXIO : -errno; /* propagate original error if this is - * not a socket after all */ - - if (shutdown(sk, SHUT_WR) < 0) - return -errno; - - f = fdopen(sk, "r"); - if (!f) - return -errno; - - TAKE_FD(sk); - } - - (void) __fsetlocking(f, FSETLOCKING_BYCALLER); - - return read_full_stream_full(f, filename, offset, size, flags, ret_contents, ret_size); -} - -#if 0 /* NM_IGNORED */ -int executable_is_script(const char *path, char **interpreter) { - _cleanup_free_ char *line = NULL; - size_t len; - char *ans; - int r; - - assert(path); - - r = read_one_line_file(path, &line); - if (r == -ENOBUFS) /* First line overly long? if so, then it's not a script */ - return 0; - if (r < 0) - return r; - - if (!startswith(line, "#!")) - return 0; - - ans = strstrip(line + 2); - len = strcspn(ans, " \t"); - - if (len == 0) - return 0; - - ans = strndup(ans, len); - if (!ans) - return -ENOMEM; - - *interpreter = ans; - return 1; -} -#endif /* NM_IGNORED */ - -/** - * Retrieve one field from a file like /proc/self/status. pattern - * should not include whitespace or the delimiter (':'). pattern matches only - * the beginning of a line. Whitespace before ':' is skipped. Whitespace and - * zeros after the ':' will be skipped. field must be freed afterwards. - * terminator specifies the terminating characters of the field value (not - * included in the value). - */ -int get_proc_field(const char *filename, const char *pattern, const char *terminator, char **field) { - _cleanup_free_ char *status = NULL; - char *t, *f; - size_t len; - int r; - - assert(terminator); - assert(filename); - assert(pattern); - assert(field); - - r = read_full_virtual_file(filename, &status, NULL); - if (r < 0) - return r; - - t = status; - - do { - bool pattern_ok; - - do { - t = strstr(t, pattern); - if (!t) - return -ENOENT; - - /* Check that pattern occurs in beginning of line. */ - pattern_ok = (t == status || t[-1] == '\n'); - - t += strlen(pattern); - - } while (!pattern_ok); - - t += strspn(t, " \t"); - if (!*t) - return -ENOENT; - - } while (*t != ':'); - - t++; - - if (*t) { - t += strspn(t, " \t"); - - /* Also skip zeros, because when this is used for - * capabilities, we don't want the zeros. This way the - * same capability set always maps to the same string, - * irrespective of the total capability set size. For - * other numbers it shouldn't matter. */ - t += strspn(t, "0"); - /* Back off one char if there's nothing but whitespace - and zeros */ - if (!*t || isspace(*t)) - t--; - } - - len = strcspn(t, terminator); - - f = strndup(t, len); - if (!f) - return -ENOMEM; - - *field = f; - return 0; -} - -DIR *xopendirat(int fd, const char *name, int flags) { - int nfd; - DIR *d; - - assert(!(flags & O_CREAT)); - - nfd = openat(fd, name, O_RDONLY|O_NONBLOCK|O_DIRECTORY|O_CLOEXEC|flags, 0); - if (nfd < 0) - return NULL; - - d = fdopendir(nfd); - if (!d) { - safe_close(nfd); - return NULL; - } - - return d; -} - -static int mode_to_flags(const char *mode) { - const char *p; - int flags; - - if ((p = startswith(mode, "r+"))) - flags = O_RDWR; - else if ((p = startswith(mode, "r"))) - flags = O_RDONLY; - else if ((p = startswith(mode, "w+"))) - flags = O_RDWR|O_CREAT|O_TRUNC; - else if ((p = startswith(mode, "w"))) - flags = O_WRONLY|O_CREAT|O_TRUNC; - else if ((p = startswith(mode, "a+"))) - flags = O_RDWR|O_CREAT|O_APPEND; - else if ((p = startswith(mode, "a"))) - flags = O_WRONLY|O_CREAT|O_APPEND; - else - return -EINVAL; - - for (; *p != 0; p++) { - - switch (*p) { - - case 'e': - flags |= O_CLOEXEC; - break; - - case 'x': - flags |= O_EXCL; - break; - - case 'm': - /* ignore this here, fdopen() might care later though */ - break; - - case 'c': /* not sure what to do about this one */ - default: - return -EINVAL; - } - } - - return flags; -} - -int xfopenat(int dir_fd, const char *path, const char *mode, int flags, FILE **ret) { - FILE *f; - - /* A combination of fopen() with openat() */ - - if (dir_fd == AT_FDCWD && flags == 0) { - f = fopen(path, mode); - if (!f) - return -errno; - } else { - int fd, mode_flags; - - mode_flags = mode_to_flags(mode); - if (mode_flags < 0) - return mode_flags; - - fd = openat(dir_fd, path, mode_flags | flags); - if (fd < 0) - return -errno; - - f = fdopen(fd, mode); - if (!f) { - safe_close(fd); - return -errno; - } - } - - *ret = f; - return 0; -} - -#if 0 /* NM_IGNORED */ -static int search_and_fopen_internal(const char *path, const char *mode, const char *root, char **search, FILE **_f) { - char **i; - - assert(path); - assert(mode); - assert(_f); - - if (!path_strv_resolve_uniq(search, root)) - return -ENOMEM; - - STRV_FOREACH(i, search) { - _cleanup_free_ char *p = NULL; - FILE *f; - - p = path_join(root, *i, path); - if (!p) - return -ENOMEM; - - f = fopen(p, mode); - if (f) { - *_f = f; - return 0; - } - - if (errno != ENOENT) - return -errno; - } - - return -ENOENT; -} - -int search_and_fopen(const char *path, const char *mode, const char *root, const char **search, FILE **_f) { - _cleanup_strv_free_ char **copy = NULL; - - assert(path); - assert(mode); - assert(_f); - - if (path_is_absolute(path)) { - FILE *f; - - f = fopen(path, mode); - if (f) { - *_f = f; - return 0; - } - - return -errno; - } - - copy = strv_copy((char**) search); - if (!copy) - return -ENOMEM; - - return search_and_fopen_internal(path, mode, root, copy, _f); -} - -int search_and_fopen_nulstr(const char *path, const char *mode, const char *root, const char *search, FILE **_f) { - _cleanup_strv_free_ char **s = NULL; - - if (path_is_absolute(path)) { - FILE *f; - - f = fopen(path, mode); - if (f) { - *_f = f; - return 0; - } - - return -errno; - } - - s = strv_split_nulstr(search); - if (!s) - return -ENOMEM; - - return search_and_fopen_internal(path, mode, root, s, _f); -} - -int chase_symlinks_and_fopen_unlocked( - const char *path, - const char *root, - unsigned chase_flags, - const char *open_flags, - FILE **ret_file, - char **ret_path) { - - _cleanup_close_ int fd = -1; - _cleanup_free_ char *final_path = NULL; - int mode_flags, r; - FILE *f; - - assert(path); - assert(open_flags); - assert(ret_file); - - mode_flags = mode_to_flags(open_flags); - if (mode_flags < 0) - return mode_flags; - - fd = chase_symlinks_and_open(path, root, chase_flags, mode_flags, ret_path ? &final_path : NULL); - if (fd < 0) - return fd; - - r = fdopen_unlocked(fd, open_flags, &f); - if (r < 0) - return r; - TAKE_FD(fd); - - *ret_file = f; - if (ret_path) - *ret_path = TAKE_PTR(final_path); - return 0; -} -#endif /* NM_IGNORED */ - -int fflush_and_check(FILE *f) { - assert(f); - - errno = 0; - fflush(f); - - if (ferror(f)) - return errno_or_else(EIO); - - return 0; -} - -#if 0 /* NM_IGNORED */ -int fflush_sync_and_check(FILE *f) { - int r, fd; - - assert(f); - - r = fflush_and_check(f); - if (r < 0) - return r; - - /* Not all file streams have an fd associated (think: fmemopen()), let's handle this gracefully and - * assume that in that case we need no explicit syncing */ - fd = fileno(f); - if (fd < 0) - return 0; - - if (fsync(fd) < 0) - return -errno; - - r = fsync_directory_of_file(fd); - if (r < 0) - return r; - - return 0; -} - -int write_timestamp_file_atomic(const char *fn, usec_t n) { - char ln[DECIMAL_STR_MAX(n)+2]; - - /* Creates a "timestamp" file, that contains nothing but a - * usec_t timestamp, formatted in ASCII. */ - - if (n <= 0 || n >= USEC_INFINITY) - return -ERANGE; - - xsprintf(ln, USEC_FMT "\n", n); - - return write_string_file(fn, ln, WRITE_STRING_FILE_CREATE|WRITE_STRING_FILE_ATOMIC); -} - -int read_timestamp_file(const char *fn, usec_t *ret) { - _cleanup_free_ char *ln = NULL; - uint64_t t; - int r; - - r = read_one_line_file(fn, &ln); - if (r < 0) - return r; - - r = safe_atou64(ln, &t); - if (r < 0) - return r; - - if (t <= 0 || t >= (uint64_t) USEC_INFINITY) - return -ERANGE; - - *ret = (usec_t) t; - return 0; -} -#endif /* NM_IGNORED */ - -int fputs_with_space(FILE *f, const char *s, const char *separator, bool *space) { - int r; - - assert(s); - - /* Outputs the specified string with fputs(), but optionally prefixes it with a separator. The *space parameter - * when specified shall initially point to a boolean variable initialized to false. It is set to true after the - * first invocation. This call is supposed to be use in loops, where a separator shall be inserted between each - * element, but not before the first one. */ - - if (!f) - f = stdout; - - if (space) { - if (!separator) - separator = " "; - - if (*space) { - r = fputs(separator, f); - if (r < 0) - return r; - } - - *space = true; - } - - return fputs(s, f); -} - -#if 0 /* NM_IGNORED */ -/* A bitmask of the EOL markers we know */ -typedef enum EndOfLineMarker { - EOL_NONE = 0, - EOL_ZERO = 1 << 0, /* \0 (aka NUL) */ - EOL_TEN = 1 << 1, /* \n (aka NL, aka LF) */ - EOL_THIRTEEN = 1 << 2, /* \r (aka CR) */ -} EndOfLineMarker; - -static EndOfLineMarker categorize_eol(char c, ReadLineFlags flags) { - - if (!IN_SET(flags, READ_LINE_ONLY_NUL)) { - if (c == '\n') - return EOL_TEN; - if (c == '\r') - return EOL_THIRTEEN; - } - - if (c == '\0') - return EOL_ZERO; - - return EOL_NONE; -} - -DEFINE_TRIVIAL_CLEANUP_FUNC(FILE*, funlockfile); - -int read_line_full(FILE *f, size_t limit, ReadLineFlags flags, char **ret) { - size_t n = 0, allocated = 0, count = 0; - _cleanup_free_ char *buffer = NULL; - int r; - - assert(f); - - /* Something like a bounded version of getline(). - * - * Considers EOF, \n, \r and \0 end of line delimiters (or combinations of these), and does not include these - * delimiters in the string returned. Specifically, recognizes the following combinations of markers as line - * endings: - * - * • \n (UNIX) - * • \r (old MacOS) - * • \0 (C strings) - * • \n\0 - * • \r\0 - * • \r\n (Windows) - * • \n\r - * • \r\n\0 - * • \n\r\0 - * - * Returns the number of bytes read from the files (i.e. including delimiters — this hence usually differs from - * the number of characters in the returned string). When EOF is hit, 0 is returned. - * - * The input parameter limit is the maximum numbers of characters in the returned string, i.e. excluding - * delimiters. If the limit is hit we fail and return -ENOBUFS. - * - * If a line shall be skipped ret may be initialized as NULL. */ - - if (ret) { - if (!GREEDY_REALLOC(buffer, allocated, 1)) - return -ENOMEM; - } - - { - _unused_ _cleanup_(funlockfilep) FILE *flocked = f; - EndOfLineMarker previous_eol = EOL_NONE; - flockfile(f); - - for (;;) { - EndOfLineMarker eol; - char c; - - if (n >= limit) - return -ENOBUFS; - - if (count >= INT_MAX) /* We couldn't return the counter anymore as "int", hence refuse this */ - return -ENOBUFS; - - r = safe_fgetc(f, &c); - if (r < 0) - return r; - if (r == 0) /* EOF is definitely EOL */ - break; - - eol = categorize_eol(c, flags); - - if (FLAGS_SET(previous_eol, EOL_ZERO) || - (eol == EOL_NONE && previous_eol != EOL_NONE) || - (eol != EOL_NONE && (previous_eol & eol) != 0)) { - /* Previous char was a NUL? This is not an EOL, but the previous char was? This type of - * EOL marker has been seen right before? In either of these three cases we are - * done. But first, let's put this character back in the queue. (Note that we have to - * cast this to (unsigned char) here as ungetc() expects a positive 'int', and if we - * are on an architecture where 'char' equals 'signed char' we need to ensure we don't - * pass a negative value here. That said, to complicate things further ungetc() is - * actually happy with most negative characters and implicitly casts them back to - * positive ones as needed, except for \xff (aka -1, aka EOF), which it refuses. What a - * godawful API!) */ - assert_se(ungetc((unsigned char) c, f) != EOF); - break; - } - - count++; - - if (eol != EOL_NONE) { - /* If we are on a tty, we can't shouldn't wait for more input, because that - * generally means waiting for the user, interactively. In the case of a TTY - * we expect only \n as the single EOL marker, so we are in the lucky - * position that there is no need to wait. We check this condition last, to - * avoid isatty() check if not necessary. */ - - if ((flags & (READ_LINE_IS_A_TTY|READ_LINE_NOT_A_TTY)) == 0) { - int fd; - - fd = fileno(f); - if (fd < 0) /* Maybe an fmemopen() stream? Handle this gracefully, - * and don't call isatty() on an invalid fd */ - flags |= READ_LINE_NOT_A_TTY; - else - flags |= isatty(fd) ? READ_LINE_IS_A_TTY : READ_LINE_NOT_A_TTY; - } - if (FLAGS_SET(flags, READ_LINE_IS_A_TTY)) - break; - } - - if (eol != EOL_NONE) { - previous_eol |= eol; - continue; - } - - if (ret) { - if (!GREEDY_REALLOC(buffer, allocated, n + 2)) - return -ENOMEM; - - buffer[n] = c; - } - - n++; - } - } - - if (ret) { - buffer[n] = 0; - - *ret = TAKE_PTR(buffer); - } - - return (int) count; -} - -int safe_fgetc(FILE *f, char *ret) { - int k; - - assert(f); - - /* A safer version of plain fgetc(): let's propagate the error that happened while reading as such, and - * separate the EOF condition from the byte read, to avoid those confusion signed/unsigned issues fgetc() - * has. */ - - errno = 0; - k = fgetc(f); - if (k == EOF) { - if (ferror(f)) - return errno_or_else(EIO); - - if (ret) - *ret = 0; - - return 0; - } - - if (ret) - *ret = k; - - return 1; -} -#endif /* NM_IGNORED */ - -int warn_file_is_world_accessible(const char *filename, struct stat *st, const char *unit, unsigned line) { - struct stat _st; - - if (!filename) - return 0; - - if (!st) { - if (stat(filename, &_st) < 0) - return -errno; - st = &_st; - } - - if ((st->st_mode & S_IRWXO) == 0) - return 0; - - if (unit) - log_syntax(unit, LOG_WARNING, filename, line, 0, - "%s has %04o mode that is too permissive, please adjust the ownership and access mode.", - filename, st->st_mode & 07777); - else - log_warning("%s has %04o mode that is too permissive, please adjust the ownership and access mode.", - filename, st->st_mode & 07777); - return 0; -} - -#if 0 /* NM_IGNORED */ -int sync_rights(int from, int to) { - struct stat st; - - if (fstat(from, &st) < 0) - return -errno; - - return fchmod_and_chown(to, st.st_mode & 07777, st.st_uid, st.st_gid); -} - -int rename_and_apply_smack_floor_label(const char *from, const char *to) { - int r = 0; - if (rename(from, to) < 0) - return -errno; - -#ifdef SMACK_RUN_LABEL - r = mac_smack_apply(to, SMACK_ATTR_ACCESS, SMACK_FLOOR_LABEL); - if (r < 0) - return r; -#endif - return r; -} -#endif /* NM_IGNORED */ diff --git a/shared/systemd/src/basic/fileio.h b/shared/systemd/src/basic/fileio.h deleted file mode 100644 index 5a028561..00000000 --- a/shared/systemd/src/basic/fileio.h +++ /dev/null @@ -1,127 +0,0 @@ -/* SPDX-License-Identifier: LGPL-2.1-or-later */ -#pragma once - -#include <dirent.h> -#include <stdbool.h> -#include <stddef.h> -#include <stdio.h> -#include <sys/stat.h> -#if 0 /* NM_IGNORED */ -#include <sys/fcntl.h> -#else /* NM_IGNORED */ -#include <fcntl.h> -#endif /* NM_IGNORED */ -#include <sys/types.h> - -#include "macro.h" -#include "time-util.h" - -#define LONG_LINE_MAX (1U*1024U*1024U) - -typedef enum { - WRITE_STRING_FILE_CREATE = 1 << 0, - WRITE_STRING_FILE_TRUNCATE = 1 << 1, - WRITE_STRING_FILE_ATOMIC = 1 << 2, - WRITE_STRING_FILE_AVOID_NEWLINE = 1 << 3, - WRITE_STRING_FILE_VERIFY_ON_FAILURE = 1 << 4, - WRITE_STRING_FILE_SYNC = 1 << 5, - WRITE_STRING_FILE_DISABLE_BUFFER = 1 << 6, - WRITE_STRING_FILE_NOFOLLOW = 1 << 7, - WRITE_STRING_FILE_MKDIR_0755 = 1 << 8, - WRITE_STRING_FILE_MODE_0600 = 1 << 9, - - /* And before you wonder, why write_string_file_atomic_label_ts() is a separate function instead of just one - more flag here: it's about linking: we don't want to pull -lselinux into all users of write_string_file() - and friends. */ - -} WriteStringFileFlags; - -typedef enum { - READ_FULL_FILE_SECURE = 1 << 0, /* erase any buffers we employ internally, after use */ - READ_FULL_FILE_UNBASE64 = 1 << 1, /* base64 decode what we read */ - READ_FULL_FILE_UNHEX = 1 << 2, /* hex decode what we read */ - READ_FULL_FILE_WARN_WORLD_READABLE = 1 << 3, /* if regular file, log at LOG_WARNING level if access mode above 0700 */ - READ_FULL_FILE_CONNECT_SOCKET = 1 << 4, /* if socket inode, connect to it and read off it */ -} ReadFullFileFlags; - -int fopen_unlocked(const char *path, const char *options, FILE **ret); -int fdopen_unlocked(int fd, const char *options, FILE **ret); -int take_fdopen_unlocked(int *fd, const char *options, FILE **ret); -FILE* take_fdopen(int *fd, const char *options); -DIR* take_fdopendir(int *dfd); -FILE* open_memstream_unlocked(char **ptr, size_t *sizeloc); -FILE* fmemopen_unlocked(void *buf, size_t size, const char *mode); - -int write_string_stream_ts(FILE *f, const char *line, WriteStringFileFlags flags, const struct timespec *ts); -static inline int write_string_stream(FILE *f, const char *line, WriteStringFileFlags flags) { - return write_string_stream_ts(f, line, flags, NULL); -} -int write_string_file_ts(const char *fn, const char *line, WriteStringFileFlags flags, const struct timespec *ts); -static inline int write_string_file(const char *fn, const char *line, WriteStringFileFlags flags) { - return write_string_file_ts(fn, line, flags, NULL); -} - -int write_string_filef(const char *fn, WriteStringFileFlags flags, const char *format, ...) _printf_(3, 4); - -int read_one_line_file(const char *filename, char **line); -int read_full_file_full(int dir_fd, const char *filename, uint64_t offset, size_t size, ReadFullFileFlags flags, const char *bind_name, char **ret_contents, size_t *ret_size); -static inline int read_full_file(const char *filename, char **ret_contents, size_t *ret_size) { - return read_full_file_full(AT_FDCWD, filename, UINT64_MAX, SIZE_MAX, 0, NULL, ret_contents, ret_size); -} -int read_full_virtual_file(const char *filename, char **ret_contents, size_t *ret_size); -int read_full_stream_full(FILE *f, const char *filename, uint64_t offset, size_t size, ReadFullFileFlags flags, char **ret_contents, size_t *ret_size); -static inline int read_full_stream(FILE *f, char **ret_contents, size_t *ret_size) { - return read_full_stream_full(f, NULL, UINT64_MAX, SIZE_MAX, 0, ret_contents, ret_size); -} - -int verify_file(const char *fn, const char *blob, bool accept_extra_nl); - -int executable_is_script(const char *path, char **interpreter); - -int get_proc_field(const char *filename, const char *pattern, const char *terminator, char **field); - -DIR *xopendirat(int dirfd, const char *name, int flags); -int xfopenat(int dir_fd, const char *path, const char *mode, int flags, FILE **ret); - -int search_and_fopen(const char *path, const char *mode, const char *root, const char **search, FILE **_f); -int search_and_fopen_nulstr(const char *path, const char *mode, const char *root, const char *search, FILE **_f); - -int chase_symlinks_and_fopen_unlocked( - const char *path, - const char *root, - unsigned chase_flags, - const char *open_flags, - FILE **ret_file, - char **ret_path); - -int fflush_and_check(FILE *f); -int fflush_sync_and_check(FILE *f); - -int write_timestamp_file_atomic(const char *fn, usec_t n); -int read_timestamp_file(const char *fn, usec_t *ret); - -int fputs_with_space(FILE *f, const char *s, const char *separator, bool *space); - -typedef enum ReadLineFlags { - READ_LINE_ONLY_NUL = 1 << 0, - READ_LINE_IS_A_TTY = 1 << 1, - READ_LINE_NOT_A_TTY = 1 << 2, -} ReadLineFlags; - -int read_line_full(FILE *f, size_t limit, ReadLineFlags flags, char **ret); - -static inline int read_line(FILE *f, size_t limit, char **ret) { - return read_line_full(f, limit, 0, ret); -} - -static inline int read_nul_string(FILE *f, size_t limit, char **ret) { - return read_line_full(f, limit, READ_LINE_ONLY_NUL, ret); -} - -int safe_fgetc(FILE *f, char *ret); - -int warn_file_is_world_accessible(const char *filename, struct stat *st, const char *unit, unsigned line); - -int sync_rights(int from, int to); - -int rename_and_apply_smack_floor_label(const char *temp_path, const char *dest_path); diff --git a/shared/systemd/src/basic/format-util.c b/shared/systemd/src/basic/format-util.c deleted file mode 100644 index 42224b6f..00000000 --- a/shared/systemd/src/basic/format-util.c +++ /dev/null @@ -1,83 +0,0 @@ -/* SPDX-License-Identifier: LGPL-2.1-or-later */ - -#include "nm-sd-adapt-shared.h" - -#include "format-util.h" -#include "memory-util.h" -#include "stdio-util.h" - -assert_cc(DECIMAL_STR_MAX(int) + 1 <= IF_NAMESIZE + 1); -char *format_ifname_full(int ifindex, char buf[static IF_NAMESIZE + 1], FormatIfnameFlag flag) { - /* Buffer is always cleared */ - memzero(buf, IF_NAMESIZE + 1); - if (if_indextoname(ifindex, buf)) - return buf; - - if (!FLAGS_SET(flag, FORMAT_IFNAME_IFINDEX)) - return NULL; - - if (FLAGS_SET(flag, FORMAT_IFNAME_IFINDEX_WITH_PERCENT)) - snprintf(buf, IF_NAMESIZE + 1, "%%%d", ifindex); - else - snprintf(buf, IF_NAMESIZE + 1, "%d", ifindex); - - return buf; -} - -char *format_bytes_full(char *buf, size_t l, uint64_t t, FormatBytesFlag flag) { - typedef struct { - const char *suffix; - uint64_t factor; - } suffix_table; - static const suffix_table table_iec[] = { - { "E", UINT64_C(1024)*UINT64_C(1024)*UINT64_C(1024)*UINT64_C(1024)*UINT64_C(1024)*UINT64_C(1024) }, - { "P", UINT64_C(1024)*UINT64_C(1024)*UINT64_C(1024)*UINT64_C(1024)*UINT64_C(1024) }, - { "T", UINT64_C(1024)*UINT64_C(1024)*UINT64_C(1024)*UINT64_C(1024) }, - { "G", UINT64_C(1024)*UINT64_C(1024)*UINT64_C(1024) }, - { "M", UINT64_C(1024)*UINT64_C(1024) }, - { "K", UINT64_C(1024) }, - }, table_si[] = { - { "E", UINT64_C(1000)*UINT64_C(1000)*UINT64_C(1000)*UINT64_C(1000)*UINT64_C(1000)*UINT64_C(1000) }, - { "P", UINT64_C(1000)*UINT64_C(1000)*UINT64_C(1000)*UINT64_C(1000)*UINT64_C(1000) }, - { "T", UINT64_C(1000)*UINT64_C(1000)*UINT64_C(1000)*UINT64_C(1000) }, - { "G", UINT64_C(1000)*UINT64_C(1000)*UINT64_C(1000) }, - { "M", UINT64_C(1000)*UINT64_C(1000) }, - { "K", UINT64_C(1000) }, - }; - const suffix_table *table; - size_t n, i; - - assert_cc(ELEMENTSOF(table_iec) == ELEMENTSOF(table_si)); - - if (t == (uint64_t) -1) - return NULL; - - table = flag & FORMAT_BYTES_USE_IEC ? table_iec : table_si; - n = ELEMENTSOF(table_iec); - - for (i = 0; i < n; i++) - if (t >= table[i].factor) { - if (flag & FORMAT_BYTES_BELOW_POINT) { - snprintf(buf, l, - "%" PRIu64 ".%" PRIu64 "%s", - t / table[i].factor, - i != n - 1 ? - (t / table[i + 1].factor * UINT64_C(10) / table[n - 1].factor) % UINT64_C(10): - (t * UINT64_C(10) / table[i].factor) % UINT64_C(10), - table[i].suffix); - } else - snprintf(buf, l, - "%" PRIu64 "%s", - t / table[i].factor, - table[i].suffix); - - goto finish; - } - - snprintf(buf, l, "%" PRIu64 "%s", t, flag & FORMAT_BYTES_TRAILING_B ? "B" : ""); - -finish: - buf[l-1] = 0; - return buf; - -} diff --git a/shared/systemd/src/basic/format-util.h b/shared/systemd/src/basic/format-util.h deleted file mode 100644 index b7e18768..00000000 --- a/shared/systemd/src/basic/format-util.h +++ /dev/null @@ -1,89 +0,0 @@ -/* SPDX-License-Identifier: LGPL-2.1-or-later */ -#pragma once - -#include <inttypes.h> -#include <net/if.h> -#include <stdbool.h> - -#include "cgroup-util.h" -#include "macro.h" - -assert_cc(sizeof(pid_t) == sizeof(int32_t)); -#define PID_PRI PRIi32 -#define PID_FMT "%" PID_PRI - -assert_cc(sizeof(uid_t) == sizeof(uint32_t)); -#define UID_FMT "%" PRIu32 - -assert_cc(sizeof(gid_t) == sizeof(uint32_t)); -#define GID_FMT "%" PRIu32 - -#if SIZEOF_TIME_T == 8 -# define PRI_TIME PRIi64 -#elif SIZEOF_TIME_T == 4 -# define PRI_TIME "li" -#else -# error Unknown time_t size -#endif - -#if defined __x86_64__ && defined __ILP32__ -# define PRI_TIMEX PRIi64 -#else -# define PRI_TIMEX "li" -#endif - -#if SIZEOF_RLIM_T == 8 -# define RLIM_FMT "%" PRIu64 -#elif SIZEOF_RLIM_T == 4 -# define RLIM_FMT "%" PRIu32 -#else -# error Unknown rlim_t size -#endif - -#if SIZEOF_DEV_T == 8 -# define DEV_FMT "%" PRIu64 -#elif SIZEOF_DEV_T == 4 -# define DEV_FMT "%" PRIu32 -#else -# error Unknown dev_t size -#endif - -#if SIZEOF_INO_T == 8 -# define INO_FMT "%" PRIu64 -#elif SIZEOF_INO_T == 4 -# define INO_FMT "%" PRIu32 -#else -# error Unknown ino_t size -#endif - -typedef enum { - FORMAT_IFNAME_IFINDEX = 1 << 0, - FORMAT_IFNAME_IFINDEX_WITH_PERCENT = (1 << 1) | FORMAT_IFNAME_IFINDEX, -} FormatIfnameFlag; - -char *format_ifname_full(int ifindex, char buf[static IF_NAMESIZE + 1], FormatIfnameFlag flag); -static inline char *format_ifname(int ifindex, char buf[static IF_NAMESIZE + 1]) { - return format_ifname_full(ifindex, buf, 0); -} - -typedef enum { - FORMAT_BYTES_USE_IEC = 1 << 0, - FORMAT_BYTES_BELOW_POINT = 1 << 1, - FORMAT_BYTES_TRAILING_B = 1 << 2, -} FormatBytesFlag; - -#define FORMAT_BYTES_MAX 16U - -char *format_bytes_full(char *buf, size_t l, uint64_t t, FormatBytesFlag flag); - -static inline char *format_bytes(char *buf, size_t l, uint64_t t) { - return format_bytes_full(buf, l, t, FORMAT_BYTES_USE_IEC | FORMAT_BYTES_BELOW_POINT | FORMAT_BYTES_TRAILING_B); -} - -static inline char *format_bytes_cgroup_protection(char *buf, size_t l, uint64_t t) { - if (t == CGROUP_LIMIT_MAX) { - (void) snprintf(buf, l, "%s", "infinity"); - return buf; - } - return format_bytes(buf, l, t); -} diff --git a/shared/systemd/src/basic/fs-util.c b/shared/systemd/src/basic/fs-util.c deleted file mode 100644 index 2ed9ee0e..00000000 --- a/shared/systemd/src/basic/fs-util.c +++ /dev/null @@ -1,1706 +0,0 @@ -/* SPDX-License-Identifier: LGPL-2.1-or-later */ - -#include "nm-sd-adapt-shared.h" - -#include <errno.h> -#include <stddef.h> -#include <stdlib.h> -#include <linux/falloc.h> -#include <linux/magic.h> -#include <unistd.h> - -#include "alloc-util.h" -#include "blockdev-util.h" -#include "dirent-util.h" -#include "fd-util.h" -#include "fileio.h" -#include "fs-util.h" -#include "locale-util.h" -#include "log.h" -#include "macro.h" -#include "missing_fcntl.h" -#include "missing_fs.h" -#include "missing_syscall.h" -#include "mkdir.h" -#include "parse-util.h" -#include "path-util.h" -#include "process-util.h" -#include "random-util.h" -#include "stat-util.h" -#include "stdio-util.h" -#include "string-util.h" -#include "strv.h" -#include "time-util.h" -#include "tmpfile-util.h" -#include "user-util.h" -#include "util.h" - -int unlink_noerrno(const char *path) { - PROTECT_ERRNO; - int r; - - r = unlink(path); - if (r < 0) - return -errno; - - return 0; -} - -#if 0 /* NM_IGNORED */ -int rmdir_parents(const char *path, const char *stop) { - size_t l; - int r = 0; - - assert(path); - assert(stop); - - l = strlen(path); - - /* Skip trailing slashes */ - while (l > 0 && path[l-1] == '/') - l--; - - while (l > 0) { - char *t; - - /* Skip last component */ - while (l > 0 && path[l-1] != '/') - l--; - - /* Skip trailing slashes */ - while (l > 0 && path[l-1] == '/') - l--; - - if (l <= 0) - break; - - t = strndup(path, l); - if (!t) - return -ENOMEM; - - if (path_startswith(stop, t)) { - free(t); - return 0; - } - - r = rmdir(t); - free(t); - - if (r < 0) - if (errno != ENOENT) - return -errno; - } - - return 0; -} - -int rename_noreplace(int olddirfd, const char *oldpath, int newdirfd, const char *newpath) { - int r; - - /* Try the ideal approach first */ - if (renameat2(olddirfd, oldpath, newdirfd, newpath, RENAME_NOREPLACE) >= 0) - return 0; - - /* renameat2() exists since Linux 3.15, btrfs and FAT added support for it later. If it is not implemented, - * fall back to a different method. */ - if (!IN_SET(errno, EINVAL, ENOSYS, ENOTTY)) - return -errno; - - /* Let's try to use linkat()+unlinkat() as fallback. This doesn't work on directories and on some file systems - * that do not support hard links (such as FAT, most prominently), but for files it's pretty close to what we - * want — though not atomic (i.e. for a short period both the new and the old filename will exist). */ - if (linkat(olddirfd, oldpath, newdirfd, newpath, 0) >= 0) { - - if (unlinkat(olddirfd, oldpath, 0) < 0) { - r = -errno; /* Backup errno before the following unlinkat() alters it */ - (void) unlinkat(newdirfd, newpath, 0); - return r; - } - - return 0; - } - - if (!IN_SET(errno, EINVAL, ENOSYS, ENOTTY, EPERM)) /* FAT returns EPERM on link()… */ - return -errno; - - /* OK, neither RENAME_NOREPLACE nor linkat()+unlinkat() worked. Let's then fall back to the racy TOCTOU - * vulnerable accessat(F_OK) check followed by classic, replacing renameat(), we have nothing better. */ - - if (faccessat(newdirfd, newpath, F_OK, AT_SYMLINK_NOFOLLOW) >= 0) - return -EEXIST; - if (errno != ENOENT) - return -errno; - - if (renameat(olddirfd, oldpath, newdirfd, newpath) < 0) - return -errno; - - return 0; -} -#endif /* NM_IGNORED */ - -int readlinkat_malloc(int fd, const char *p, char **ret) { - size_t l = FILENAME_MAX+1; - int r; - - assert(p); - assert(ret); - - for (;;) { - char *c; - ssize_t n; - - c = new(char, l); - if (!c) - return -ENOMEM; - - n = readlinkat(fd, p, c, l-1); - if (n < 0) { - r = -errno; - free(c); - return r; - } - - if ((size_t) n < l-1) { - c[n] = 0; - *ret = c; - return 0; - } - - free(c); - l *= 2; - } -} - -int readlink_malloc(const char *p, char **ret) { - return readlinkat_malloc(AT_FDCWD, p, ret); -} - -#if 0 /* NM_IGNORED */ -int readlink_value(const char *p, char **ret) { - _cleanup_free_ char *link = NULL; - char *value; - int r; - - r = readlink_malloc(p, &link); - if (r < 0) - return r; - - value = basename(link); - if (!value) - return -ENOENT; - - value = strdup(value); - if (!value) - return -ENOMEM; - - *ret = value; - - return 0; -} - -int readlink_and_make_absolute(const char *p, char **r) { - _cleanup_free_ char *target = NULL; - char *k; - int j; - - assert(p); - assert(r); - - j = readlink_malloc(p, &target); - if (j < 0) - return j; - - k = file_in_same_dir(p, target); - if (!k) - return -ENOMEM; - - *r = k; - return 0; -} - -int chmod_and_chown(const char *path, mode_t mode, uid_t uid, gid_t gid) { - _cleanup_close_ int fd = -1; - - assert(path); - - fd = open(path, O_PATH|O_CLOEXEC|O_NOFOLLOW); /* Let's acquire an O_PATH fd, as precaution to change - * mode/owner on the same file */ - if (fd < 0) - return -errno; - - return fchmod_and_chown(fd, mode, uid, gid); -} - -int fchmod_and_chown(int fd, mode_t mode, uid_t uid, gid_t gid) { - bool do_chown, do_chmod; - struct stat st; - int r; - - /* Change ownership and access mode of the specified fd. Tries to do so safely, ensuring that at no - * point in time the access mode is above the old access mode under the old ownership or the new - * access mode under the new ownership. Note: this call tries hard to leave the access mode - * unaffected if the uid/gid is changed, i.e. it undoes implicit suid/sgid dropping the kernel does - * on chown(). - * - * This call is happy with O_PATH fds. */ - - if (fstat(fd, &st) < 0) - return -errno; - - do_chown = - (uid != UID_INVALID && st.st_uid != uid) || - (gid != GID_INVALID && st.st_gid != gid); - - do_chmod = - !S_ISLNK(st.st_mode) && /* chmod is not defined on symlinks */ - ((mode != MODE_INVALID && ((st.st_mode ^ mode) & 07777) != 0) || - do_chown); /* If we change ownership, make sure we reset the mode afterwards, since chown() - * modifies the access mode too */ - - if (mode == MODE_INVALID) - mode = st.st_mode; /* If we only shall do a chown(), save original mode, since chown() might break it. */ - else if ((mode & S_IFMT) != 0 && ((mode ^ st.st_mode) & S_IFMT) != 0) - return -EINVAL; /* insist on the right file type if it was specified */ - - if (do_chown && do_chmod) { - mode_t minimal = st.st_mode & mode; /* the subset of the old and the new mask */ - - if (((minimal ^ st.st_mode) & 07777) != 0) { - r = fchmod_opath(fd, minimal & 07777); - if (r < 0) - return r; - } - } - - if (do_chown) - if (fchownat(fd, "", uid, gid, AT_EMPTY_PATH) < 0) - return -errno; - - if (do_chmod) { - r = fchmod_opath(fd, mode & 07777); - if (r < 0) - return r; - } - - return do_chown || do_chmod; -} - -int fchmod_umask(int fd, mode_t m) { - mode_t u; - int r; - - u = umask(0777); - r = fchmod(fd, m & (~u)) < 0 ? -errno : 0; - umask(u); - - return r; -} - -int fchmod_opath(int fd, mode_t m) { - char procfs_path[STRLEN("/proc/self/fd/") + DECIMAL_STR_MAX(int)]; - - /* This function operates also on fd that might have been opened with - * O_PATH. Indeed fchmodat() doesn't have the AT_EMPTY_PATH flag like - * fchownat() does. */ - - xsprintf(procfs_path, "/proc/self/fd/%i", fd); - if (chmod(procfs_path, m) < 0) { - if (errno != ENOENT) - return -errno; - - if (proc_mounted() == 0) - return -ENOSYS; /* if we have no /proc/, the concept is not implementable */ - - return -ENOENT; - } - - return 0; -} - -int futimens_opath(int fd, const struct timespec ts[2]) { - char procfs_path[STRLEN("/proc/self/fd/") + DECIMAL_STR_MAX(int)]; - - /* Similar to fchmod_path() but for futimens() */ - - xsprintf(procfs_path, "/proc/self/fd/%i", fd); - if (utimensat(AT_FDCWD, procfs_path, ts, 0) < 0) { - if (errno != ENOENT) - return -errno; - - if (proc_mounted() == 0) - return -ENOSYS; /* if we have no /proc/, the concept is not implementable */ - - return -ENOENT; - } - - return 0; -} - -int stat_warn_permissions(const char *path, const struct stat *st) { - assert(path); - assert(st); - - /* Don't complain if we are reading something that is not a file, for example /dev/null */ - if (!S_ISREG(st->st_mode)) - return 0; - - if (st->st_mode & 0111) - log_warning("Configuration file %s is marked executable. Please remove executable permission bits. Proceeding anyway.", path); - - if (st->st_mode & 0002) - log_warning("Configuration file %s is marked world-writable. Please remove world writability permission bits. Proceeding anyway.", path); - - if (getpid_cached() == 1 && (st->st_mode & 0044) != 0044) - log_warning("Configuration file %s is marked world-inaccessible. This has no effect as configuration data is accessible via APIs without restrictions. Proceeding anyway.", path); - - return 0; -} - -int fd_warn_permissions(const char *path, int fd) { - struct stat st; - - assert(path); - assert(fd >= 0); - - if (fstat(fd, &st) < 0) - return -errno; - - return stat_warn_permissions(path, &st); -} - -int touch_file(const char *path, bool parents, usec_t stamp, uid_t uid, gid_t gid, mode_t mode) { - char fdpath[STRLEN("/proc/self/fd/") + DECIMAL_STR_MAX(int)]; - _cleanup_close_ int fd = -1; - int r, ret = 0; - - assert(path); - - /* Note that touch_file() does not follow symlinks: if invoked on an existing symlink, then it is the symlink - * itself which is updated, not its target - * - * Returns the first error we encounter, but tries to apply as much as possible. */ - - if (parents) - (void) mkdir_parents(path, 0755); - - /* Initially, we try to open the node with O_PATH, so that we get a reference to the node. This is useful in - * case the path refers to an existing device or socket node, as we can open it successfully in all cases, and - * won't trigger any driver magic or so. */ - fd = open(path, O_PATH|O_CLOEXEC|O_NOFOLLOW); - if (fd < 0) { - if (errno != ENOENT) - return -errno; - - /* if the node doesn't exist yet, we create it, but with O_EXCL, so that we only create a regular file - * here, and nothing else */ - fd = open(path, O_WRONLY|O_CREAT|O_EXCL|O_CLOEXEC, IN_SET(mode, 0, MODE_INVALID) ? 0644 : mode); - if (fd < 0) - return -errno; - } - - /* Let's make a path from the fd, and operate on that. With this logic, we can adjust the access mode, - * ownership and time of the file node in all cases, even if the fd refers to an O_PATH object — which is - * something fchown(), fchmod(), futimensat() don't allow. */ - xsprintf(fdpath, "/proc/self/fd/%i", fd); - - ret = fchmod_and_chown(fd, mode, uid, gid); - - if (stamp != USEC_INFINITY) { - struct timespec ts[2]; - - timespec_store(&ts[0], stamp); - ts[1] = ts[0]; - r = utimensat(AT_FDCWD, fdpath, ts, 0); - } else - r = utimensat(AT_FDCWD, fdpath, NULL, 0); - if (r < 0 && ret >= 0) - return -errno; - - return ret; -} - -int touch(const char *path) { - return touch_file(path, false, USEC_INFINITY, UID_INVALID, GID_INVALID, MODE_INVALID); -} - -int symlink_idempotent(const char *from, const char *to, bool make_relative) { - _cleanup_free_ char *relpath = NULL; - int r; - - assert(from); - assert(to); - - if (make_relative) { - _cleanup_free_ char *parent = NULL; - - parent = dirname_malloc(to); - if (!parent) - return -ENOMEM; - - r = path_make_relative(parent, from, &relpath); - if (r < 0) - return r; - - from = relpath; - } - - if (symlink(from, to) < 0) { - _cleanup_free_ char *p = NULL; - - if (errno != EEXIST) - return -errno; - - r = readlink_malloc(to, &p); - if (r == -EINVAL) /* Not a symlink? In that case return the original error we encountered: -EEXIST */ - return -EEXIST; - if (r < 0) /* Any other error? In that case propagate it as is */ - return r; - - if (!streq(p, from)) /* Not the symlink we want it to be? In that case, propagate the original -EEXIST */ - return -EEXIST; - } - - return 0; -} - -int symlink_atomic(const char *from, const char *to) { - _cleanup_free_ char *t = NULL; - int r; - - assert(from); - assert(to); - - r = tempfn_random(to, NULL, &t); - if (r < 0) - return r; - - if (symlink(from, t) < 0) - return -errno; - - if (rename(t, to) < 0) { - unlink_noerrno(t); - return -errno; - } - - return 0; -} - -int mknod_atomic(const char *path, mode_t mode, dev_t dev) { - _cleanup_free_ char *t = NULL; - int r; - - assert(path); - - r = tempfn_random(path, NULL, &t); - if (r < 0) - return r; - - if (mknod(t, mode, dev) < 0) - return -errno; - - if (rename(t, path) < 0) { - unlink_noerrno(t); - return -errno; - } - - return 0; -} - -int mkfifo_atomic(const char *path, mode_t mode) { - _cleanup_free_ char *t = NULL; - int r; - - assert(path); - - r = tempfn_random(path, NULL, &t); - if (r < 0) - return r; - - if (mkfifo(t, mode) < 0) - return -errno; - - if (rename(t, path) < 0) { - unlink_noerrno(t); - return -errno; - } - - return 0; -} - -int mkfifoat_atomic(int dirfd, const char *path, mode_t mode) { - _cleanup_free_ char *t = NULL; - int r; - - assert(path); - - if (path_is_absolute(path)) - return mkfifo_atomic(path, mode); - - /* We're only interested in the (random) filename. */ - r = tempfn_random_child("", NULL, &t); - if (r < 0) - return r; - - if (mkfifoat(dirfd, t, mode) < 0) - return -errno; - - if (renameat(dirfd, t, dirfd, path) < 0) { - unlink_noerrno(t); - return -errno; - } - - return 0; -} - -int get_files_in_directory(const char *path, char ***list) { - _cleanup_closedir_ DIR *d = NULL; - struct dirent *de; - size_t bufsize = 0, n = 0; - _cleanup_strv_free_ char **l = NULL; - - assert(path); - - /* Returns all files in a directory in *list, and the number - * of files as return value. If list is NULL returns only the - * number. */ - - d = opendir(path); - if (!d) - return -errno; - - FOREACH_DIRENT_ALL(de, d, return -errno) { - dirent_ensure_type(d, de); - - if (!dirent_is_file(de)) - continue; - - if (list) { - /* one extra slot is needed for the terminating NULL */ - if (!GREEDY_REALLOC(l, bufsize, n + 2)) - return -ENOMEM; - - l[n] = strdup(de->d_name); - if (!l[n]) - return -ENOMEM; - - l[++n] = NULL; - } else - n++; - } - - if (list) - *list = TAKE_PTR(l); - - return n; -} -#endif /* NM_IGNORED */ - -static int getenv_tmp_dir(const char **ret_path) { - const char *n; - int r, ret = 0; - - assert(ret_path); - - /* We use the same order of environment variables python uses in tempfile.gettempdir(): - * https://docs.python.org/3/library/tempfile.html#tempfile.gettempdir */ - FOREACH_STRING(n, "TMPDIR", "TEMP", "TMP") { - const char *e; - - e = secure_getenv(n); - if (!e) - continue; - if (!path_is_absolute(e)) { - r = -ENOTDIR; - goto next; - } - if (!path_is_normalized(e)) { - r = -EPERM; - goto next; - } - - r = is_dir(e, true); - if (r < 0) - goto next; - if (r == 0) { - r = -ENOTDIR; - goto next; - } - - *ret_path = e; - return 1; - - next: - /* Remember first error, to make this more debuggable */ - if (ret >= 0) - ret = r; - } - - if (ret < 0) - return ret; - - *ret_path = NULL; - return ret; -} - -static int tmp_dir_internal(const char *def, const char **ret) { - const char *e; - int r, k; - - assert(def); - assert(ret); - - r = getenv_tmp_dir(&e); - if (r > 0) { - *ret = e; - return 0; - } - - k = is_dir(def, true); - if (k == 0) - k = -ENOTDIR; - if (k < 0) - return r < 0 ? r : k; - - *ret = def; - return 0; -} - -#if 0 /* NM_IGNORED */ -int var_tmp_dir(const char **ret) { - - /* Returns the location for "larger" temporary files, that is backed by physical storage if available, and thus - * even might survive a boot: /var/tmp. If $TMPDIR (or related environment variables) are set, its value is - * returned preferably however. Note that both this function and tmp_dir() below are affected by $TMPDIR, - * making it a variable that overrides all temporary file storage locations. */ - - return tmp_dir_internal("/var/tmp", ret); -} -#endif /* NM_IGNORED */ - -int tmp_dir(const char **ret) { - - /* Similar to var_tmp_dir() above, but returns the location for "smaller" temporary files, which is usually - * backed by an in-memory file system: /tmp. */ - - return tmp_dir_internal("/tmp", ret); -} - -#if 0 /* NM_IGNORED */ -int unlink_or_warn(const char *filename) { - if (unlink(filename) < 0 && errno != ENOENT) - /* If the file doesn't exist and the fs simply was read-only (in which - * case unlink() returns EROFS even if the file doesn't exist), don't - * complain */ - if (errno != EROFS || access(filename, F_OK) >= 0) - return log_error_errno(errno, "Failed to remove \"%s\": %m", filename); - - return 0; -} -#endif /* NM_IGNORED */ - -int inotify_add_watch_fd(int fd, int what, uint32_t mask) { - char path[STRLEN("/proc/self/fd/") + DECIMAL_STR_MAX(int) + 1]; - int wd; - - /* This is like inotify_add_watch(), except that the file to watch is not referenced by a path, but by an fd */ - xsprintf(path, "/proc/self/fd/%i", what); - - wd = inotify_add_watch(fd, path, mask); - if (wd < 0) - return -errno; - - return wd; -} - -#if 0 /* NM_IGNORED */ -int inotify_add_watch_and_warn(int fd, const char *pathname, uint32_t mask) { - int wd; - - wd = inotify_add_watch(fd, pathname, mask); - if (wd < 0) { - if (errno == ENOSPC) - return log_error_errno(errno, "Failed to add a watch for %s: inotify watch limit reached", pathname); - - return log_error_errno(errno, "Failed to add a watch for %s: %m", pathname); - } - - return wd; -} - -static bool unsafe_transition(const struct stat *a, const struct stat *b) { - /* Returns true if the transition from a to b is safe, i.e. that we never transition from unprivileged to - * privileged files or directories. Why bother? So that unprivileged code can't symlink to privileged files - * making us believe we read something safe even though it isn't safe in the specific context we open it in. */ - - if (a->st_uid == 0) /* Transitioning from privileged to unprivileged is always fine */ - return false; - - return a->st_uid != b->st_uid; /* Otherwise we need to stay within the same UID */ -} - -static int log_unsafe_transition(int a, int b, const char *path, unsigned flags) { - _cleanup_free_ char *n1 = NULL, *n2 = NULL; - - if (!FLAGS_SET(flags, CHASE_WARN)) - return -ENOLINK; - - (void) fd_get_path(a, &n1); - (void) fd_get_path(b, &n2); - - return log_warning_errno(SYNTHETIC_ERRNO(ENOLINK), - "Detected unsafe path transition %s %s %s during canonicalization of %s.", - strna(n1), special_glyph(SPECIAL_GLYPH_ARROW), strna(n2), path); -} - -static int log_autofs_mount_point(int fd, const char *path, unsigned flags) { - _cleanup_free_ char *n1 = NULL; - - if (!FLAGS_SET(flags, CHASE_WARN)) - return -EREMOTE; - - (void) fd_get_path(fd, &n1); - - return log_warning_errno(SYNTHETIC_ERRNO(EREMOTE), - "Detected autofs mount point %s during canonicalization of %s.", - strna(n1), path); -} - -int chase_symlinks(const char *path, const char *original_root, unsigned flags, char **ret_path, int *ret_fd) { - _cleanup_free_ char *buffer = NULL, *done = NULL, *root = NULL; - _cleanup_close_ int fd = -1; - unsigned max_follow = CHASE_SYMLINKS_MAX; /* how many symlinks to follow before giving up and returning ELOOP */ - struct stat previous_stat; - bool exists = true; - char *todo; - int r; - - assert(path); - - /* Either the file may be missing, or we return an fd to the final object, but both make no sense */ - if ((flags & CHASE_NONEXISTENT) && ret_fd) - return -EINVAL; - - if ((flags & CHASE_STEP) && ret_fd) - return -EINVAL; - - if (isempty(path)) - return -EINVAL; - - /* This is a lot like canonicalize_file_name(), but takes an additional "root" parameter, that allows following - * symlinks relative to a root directory, instead of the root of the host. - * - * Note that "root" primarily matters if we encounter an absolute symlink. It is also used when following - * relative symlinks to ensure they cannot be used to "escape" the root directory. The path parameter passed is - * assumed to be already prefixed by it, except if the CHASE_PREFIX_ROOT flag is set, in which case it is first - * prefixed accordingly. - * - * Algorithmically this operates on two path buffers: "done" are the components of the path we already - * processed and resolved symlinks, "." and ".." of. "todo" are the components of the path we still need to - * process. On each iteration, we move one component from "todo" to "done", processing it's special meaning - * each time. The "todo" path always starts with at least one slash, the "done" path always ends in no - * slash. We always keep an O_PATH fd to the component we are currently processing, thus keeping lookup races - * to a minimum. - * - * Suggested usage: whenever you want to canonicalize a path, use this function. Pass the absolute path you got - * as-is: fully qualified and relative to your host's root. Optionally, specify the root parameter to tell this - * function what to do when encountering a symlink with an absolute path as directory: prefix it by the - * specified path. - * - * There are five ways to invoke this function: - * - * 1. Without CHASE_STEP or ret_fd: in this case the path is resolved and the normalized path is - * returned in `ret_path`. The return value is < 0 on error. If CHASE_NONEXISTENT is also set, 0 - * is returned if the file doesn't exist, > 0 otherwise. If CHASE_NONEXISTENT is not set, >= 0 is - * returned if the destination was found, -ENOENT if it wasn't. - * - * 2. With ret_fd: in this case the destination is opened after chasing it as O_PATH and this file - * descriptor is returned as return value. This is useful to open files relative to some root - * directory. Note that the returned O_PATH file descriptors must be converted into a regular one (using - * fd_reopen() or such) before it can be used for reading/writing. ret_fd may not be combined with - * CHASE_NONEXISTENT. - * - * 3. With CHASE_STEP: in this case only a single step of the normalization is executed, i.e. only the first - * symlink or ".." component of the path is resolved, and the resulting path is returned. This is useful if - * a caller wants to trace the path through the file system verbosely. Returns < 0 on error, > 0 if the - * path is fully normalized, and == 0 for each normalization step. This may be combined with - * CHASE_NONEXISTENT, in which case 1 is returned when a component is not found. - * - * 4. With CHASE_SAFE: in this case the path must not contain unsafe transitions, i.e. transitions from - * unprivileged to privileged files or directories. In such cases the return value is -ENOLINK. If - * CHASE_WARN is also set, a warning describing the unsafe transition is emitted. - * - * 5. With CHASE_NO_AUTOFS: in this case if an autofs mount point is encountered, path normalization - * is aborted and -EREMOTE is returned. If CHASE_WARN is also set, a warning showing the path of - * the mount point is emitted. - */ - - /* A root directory of "/" or "" is identical to none */ - if (empty_or_root(original_root)) - original_root = NULL; - - if (!original_root && !ret_path && !(flags & (CHASE_NONEXISTENT|CHASE_NO_AUTOFS|CHASE_SAFE|CHASE_STEP)) && ret_fd) { - /* Shortcut the ret_fd case if the caller isn't interested in the actual path and has no root set - * and doesn't care about any of the other special features we provide either. */ - r = open(path, O_PATH|O_CLOEXEC|((flags & CHASE_NOFOLLOW) ? O_NOFOLLOW : 0)); - if (r < 0) - return -errno; - - *ret_fd = r; - return 0; - } - - if (original_root) { - r = path_make_absolute_cwd(original_root, &root); - if (r < 0) - return r; - - /* Simplify the root directory, so that it has no duplicate slashes and nothing at the - * end. While we won't resolve the root path we still simplify it. Note that dropping the - * trailing slash should not change behaviour, since when opening it we specify O_DIRECTORY - * anyway. Moreover at the end of this function after processing everything we'll always turn - * the empty string back to "/". */ - delete_trailing_chars(root, "/"); - path_simplify(root, true); - - if (flags & CHASE_PREFIX_ROOT) { - /* We don't support relative paths in combination with a root directory */ - if (!path_is_absolute(path)) - return -EINVAL; - - path = prefix_roota(root, path); - } - } - - r = path_make_absolute_cwd(path, &buffer); - if (r < 0) - return r; - - fd = open(root ?: "/", O_CLOEXEC|O_DIRECTORY|O_PATH); - if (fd < 0) - return -errno; - - if (flags & CHASE_SAFE) { - if (fstat(fd, &previous_stat) < 0) - return -errno; - } - - if (root) { - _cleanup_free_ char *absolute = NULL; - const char *e; - - /* If we are operating on a root directory, let's take the root directory as it is. */ - - e = path_startswith(buffer, root); - if (!e) - return log_full_errno(flags & CHASE_WARN ? LOG_WARNING : LOG_DEBUG, - SYNTHETIC_ERRNO(ECHRNG), - "Specified path '%s' is outside of specified root directory '%s', refusing to resolve.", - path, root); - - done = strdup(root); - if (!done) - return -ENOMEM; - - /* Make sure "todo" starts with a slash */ - absolute = strjoin("/", e); - if (!absolute) - return -ENOMEM; - - free_and_replace(buffer, absolute); - } - - todo = buffer; - for (;;) { - _cleanup_free_ char *first = NULL; - _cleanup_close_ int child = -1; - struct stat st; - size_t n, m; - - /* Determine length of first component in the path */ - n = strspn(todo, "/"); /* The slashes */ - - if (n > 1) { - /* If we are looking at more than a single slash then skip all but one, so that when - * we are done with everything we have a normalized path with only single slashes - * separating the path components. */ - todo += n - 1; - n = 1; - } - - m = n + strcspn(todo + n, "/"); /* The entire length of the component */ - - /* Extract the first component. */ - first = strndup(todo, m); - if (!first) - return -ENOMEM; - - todo += m; - - /* Empty? Then we reached the end. */ - if (isempty(first)) - break; - - /* Just a single slash? Then we reached the end. */ - if (path_equal(first, "/")) { - /* Preserve the trailing slash */ - - if (flags & CHASE_TRAIL_SLASH) - if (!strextend(&done, "/")) - return -ENOMEM; - - break; - } - - /* Just a dot? Then let's eat this up. */ - if (path_equal(first, "/.")) - continue; - - /* Two dots? Then chop off the last bit of what we already found out. */ - if (path_equal(first, "/..")) { - _cleanup_free_ char *parent = NULL; - _cleanup_close_ int fd_parent = -1; - - /* If we already are at the top, then going up will not change anything. This is in-line with - * how the kernel handles this. */ - if (empty_or_root(done)) - continue; - - parent = dirname_malloc(done); - if (!parent) - return -ENOMEM; - - /* Don't allow this to leave the root dir. */ - if (root && - path_startswith(done, root) && - !path_startswith(parent, root)) - continue; - - free_and_replace(done, parent); - - if (flags & CHASE_STEP) - goto chased_one; - - fd_parent = openat(fd, "..", O_CLOEXEC|O_NOFOLLOW|O_PATH); - if (fd_parent < 0) - return -errno; - - if (flags & CHASE_SAFE) { - if (fstat(fd_parent, &st) < 0) - return -errno; - - if (unsafe_transition(&previous_stat, &st)) - return log_unsafe_transition(fd, fd_parent, path, flags); - - previous_stat = st; - } - - safe_close(fd); - fd = TAKE_FD(fd_parent); - - continue; - } - - /* Otherwise let's see what this is. */ - child = openat(fd, first + n, O_CLOEXEC|O_NOFOLLOW|O_PATH); - if (child < 0) { - - if (errno == ENOENT && - (flags & CHASE_NONEXISTENT) && - (isempty(todo) || path_is_normalized(todo))) { - - /* If CHASE_NONEXISTENT is set, and the path does not exist, then that's OK, return - * what we got so far. But don't allow this if the remaining path contains "../ or "./" - * or something else weird. */ - - /* If done is "/", as first also contains slash at the head, then remove this redundant slash. */ - if (streq_ptr(done, "/")) - *done = '\0'; - - if (!strextend(&done, first, todo)) - return -ENOMEM; - - exists = false; - break; - } - - return -errno; - } - - if (fstat(child, &st) < 0) - return -errno; - if ((flags & CHASE_SAFE) && - unsafe_transition(&previous_stat, &st)) - return log_unsafe_transition(fd, child, path, flags); - - previous_stat = st; - - if ((flags & CHASE_NO_AUTOFS) && - fd_is_fs_type(child, AUTOFS_SUPER_MAGIC) > 0) - return log_autofs_mount_point(child, path, flags); - - if (S_ISLNK(st.st_mode) && !((flags & CHASE_NOFOLLOW) && isempty(todo))) { - char *joined; - _cleanup_free_ char *destination = NULL; - - /* This is a symlink, in this case read the destination. But let's make sure we don't follow - * symlinks without bounds. */ - if (--max_follow <= 0) - return -ELOOP; - - r = readlinkat_malloc(fd, first + n, &destination); - if (r < 0) - return r; - if (isempty(destination)) - return -EINVAL; - - if (path_is_absolute(destination)) { - - /* An absolute destination. Start the loop from the beginning, but use the root - * directory as base. */ - - safe_close(fd); - fd = open(root ?: "/", O_CLOEXEC|O_DIRECTORY|O_PATH); - if (fd < 0) - return -errno; - - if (flags & CHASE_SAFE) { - if (fstat(fd, &st) < 0) - return -errno; - - if (unsafe_transition(&previous_stat, &st)) - return log_unsafe_transition(child, fd, path, flags); - - previous_stat = st; - } - - free(done); - - /* Note that we do not revalidate the root, we take it as is. */ - if (isempty(root)) - done = NULL; - else { - done = strdup(root); - if (!done) - return -ENOMEM; - } - - /* Prefix what's left to do with what we just read, and start the loop again, but - * remain in the current directory. */ - joined = path_join(destination, todo); - } else - joined = path_join("/", destination, todo); - if (!joined) - return -ENOMEM; - - free(buffer); - todo = buffer = joined; - - if (flags & CHASE_STEP) - goto chased_one; - - continue; - } - - /* If this is not a symlink, then let's just add the name we read to what we already verified. */ - if (!done) - done = TAKE_PTR(first); - else { - /* If done is "/", as first also contains slash at the head, then remove this redundant slash. */ - if (streq(done, "/")) - *done = '\0'; - - if (!strextend(&done, first)) - return -ENOMEM; - } - - /* And iterate again, but go one directory further down. */ - safe_close(fd); - fd = TAKE_FD(child); - } - - if (!done) { - /* Special case, turn the empty string into "/", to indicate the root directory. */ - done = strdup("/"); - if (!done) - return -ENOMEM; - } - - if (ret_path) - *ret_path = TAKE_PTR(done); - - if (ret_fd) { - /* Return the O_PATH fd we currently are looking to the caller. It can translate it to a - * proper fd by opening /proc/self/fd/xyz. */ - - assert(fd >= 0); - *ret_fd = TAKE_FD(fd); - } - - if (flags & CHASE_STEP) - return 1; - - return exists; - -chased_one: - if (ret_path) { - char *c; - - c = strjoin(strempty(done), todo); - if (!c) - return -ENOMEM; - - *ret_path = c; - } - - return 0; -} - -int chase_symlinks_and_open( - const char *path, - const char *root, - unsigned chase_flags, - int open_flags, - char **ret_path) { - - _cleanup_close_ int path_fd = -1; - _cleanup_free_ char *p = NULL; - int r; - - if (chase_flags & CHASE_NONEXISTENT) - return -EINVAL; - - if (empty_or_root(root) && !ret_path && (chase_flags & (CHASE_NO_AUTOFS|CHASE_SAFE)) == 0) { - /* Shortcut this call if none of the special features of this call are requested */ - r = open(path, open_flags); - if (r < 0) - return -errno; - - return r; - } - - r = chase_symlinks(path, root, chase_flags, ret_path ? &p : NULL, &path_fd); - if (r < 0) - return r; - assert(path_fd >= 0); - - r = fd_reopen(path_fd, open_flags); - if (r < 0) - return r; - - if (ret_path) - *ret_path = TAKE_PTR(p); - - return r; -} - -int chase_symlinks_and_opendir( - const char *path, - const char *root, - unsigned chase_flags, - char **ret_path, - DIR **ret_dir) { - - char procfs_path[STRLEN("/proc/self/fd/") + DECIMAL_STR_MAX(int)]; - _cleanup_close_ int path_fd = -1; - _cleanup_free_ char *p = NULL; - DIR *d; - int r; - - if (!ret_dir) - return -EINVAL; - if (chase_flags & CHASE_NONEXISTENT) - return -EINVAL; - - if (empty_or_root(root) && !ret_path && (chase_flags & (CHASE_NO_AUTOFS|CHASE_SAFE)) == 0) { - /* Shortcut this call if none of the special features of this call are requested */ - d = opendir(path); - if (!d) - return -errno; - - *ret_dir = d; - return 0; - } - - r = chase_symlinks(path, root, chase_flags, ret_path ? &p : NULL, &path_fd); - if (r < 0) - return r; - assert(path_fd >= 0); - - xsprintf(procfs_path, "/proc/self/fd/%i", path_fd); - d = opendir(procfs_path); - if (!d) - return -errno; - - if (ret_path) - *ret_path = TAKE_PTR(p); - - *ret_dir = d; - return 0; -} - -int chase_symlinks_and_stat( - const char *path, - const char *root, - unsigned chase_flags, - char **ret_path, - struct stat *ret_stat, - int *ret_fd) { - - _cleanup_close_ int path_fd = -1; - _cleanup_free_ char *p = NULL; - int r; - - assert(path); - assert(ret_stat); - - if (chase_flags & CHASE_NONEXISTENT) - return -EINVAL; - - if (empty_or_root(root) && !ret_path && (chase_flags & (CHASE_NO_AUTOFS|CHASE_SAFE)) == 0) { - /* Shortcut this call if none of the special features of this call are requested */ - if (stat(path, ret_stat) < 0) - return -errno; - - return 1; - } - - r = chase_symlinks(path, root, chase_flags, ret_path ? &p : NULL, &path_fd); - if (r < 0) - return r; - assert(path_fd >= 0); - - if (fstat(path_fd, ret_stat) < 0) - return -errno; - - if (ret_path) - *ret_path = TAKE_PTR(p); - if (ret_fd) - *ret_fd = TAKE_FD(path_fd); - - return 1; -} - -int access_fd(int fd, int mode) { - char p[STRLEN("/proc/self/fd/") + DECIMAL_STR_MAX(fd) + 1]; - - /* Like access() but operates on an already open fd */ - - xsprintf(p, "/proc/self/fd/%i", fd); - if (access(p, mode) < 0) { - if (errno != ENOENT) - return -errno; - - /* ENOENT can mean two things: that the fd does not exist or that /proc is not mounted. Let's - * make things debuggable and distinguish the two. */ - - if (proc_mounted() == 0) - return -ENOSYS; /* /proc is not available or not set up properly, we're most likely in some chroot - * environment. */ - - return -EBADF; /* The directory exists, hence it's the fd that doesn't. */ - } - - return 0; -} - -void unlink_tempfilep(char (*p)[]) { - /* If the file is created with mkstemp(), it will (almost always) - * change the suffix. Treat this as a sign that the file was - * successfully created. We ignore both the rare case where the - * original suffix is used and unlink failures. */ - if (!endswith(*p, ".XXXXXX")) - (void) unlink_noerrno(*p); -} - -int unlinkat_deallocate(int fd, const char *name, UnlinkDeallocateFlags flags) { - _cleanup_close_ int truncate_fd = -1; - struct stat st; - off_t l, bs; - - assert((flags & ~(UNLINK_REMOVEDIR|UNLINK_ERASE)) == 0); - - /* Operates like unlinkat() but also deallocates the file contents if it is a regular file and there's no other - * link to it. This is useful to ensure that other processes that might have the file open for reading won't be - * able to keep the data pinned on disk forever. This call is particular useful whenever we execute clean-up - * jobs ("vacuuming"), where we want to make sure the data is really gone and the disk space released and - * returned to the free pool. - * - * Deallocation is preferably done by FALLOC_FL_PUNCH_HOLE|FALLOC_FL_KEEP_SIZE (👊) if supported, which means - * the file won't change size. That's a good thing since we shouldn't needlessly trigger SIGBUS in other - * programs that have mmap()ed the file. (The assumption here is that changing file contents to all zeroes - * underneath those programs is the better choice than simply triggering SIGBUS in them which truncation does.) - * However if hole punching is not implemented in the kernel or file system we'll fall back to normal file - * truncation (🔪), as our goal of deallocating the data space trumps our goal of being nice to readers (💐). - * - * Note that we attempt deallocation, but failure to succeed with that is not considered fatal, as long as the - * primary job – to delete the file – is accomplished. */ - - if (!FLAGS_SET(flags, UNLINK_REMOVEDIR)) { - truncate_fd = openat(fd, name, O_WRONLY|O_CLOEXEC|O_NOCTTY|O_NOFOLLOW|O_NONBLOCK); - if (truncate_fd < 0) { - - /* If this failed because the file doesn't exist propagate the error right-away. Also, - * AT_REMOVEDIR wasn't set, and we tried to open the file for writing, which means EISDIR is - * returned when this is a directory but we are not supposed to delete those, hence propagate - * the error right-away too. */ - if (IN_SET(errno, ENOENT, EISDIR)) - return -errno; - - if (errno != ELOOP) /* don't complain if this is a symlink */ - log_debug_errno(errno, "Failed to open file '%s' for deallocation, ignoring: %m", name); - } - } - - if (unlinkat(fd, name, FLAGS_SET(flags, UNLINK_REMOVEDIR) ? AT_REMOVEDIR : 0) < 0) - return -errno; - - if (truncate_fd < 0) /* Don't have a file handle, can't do more ☹️ */ - return 0; - - if (fstat(truncate_fd, &st) < 0) { - log_debug_errno(errno, "Failed to stat file '%s' for deallocation, ignoring: %m", name); - return 0; - } - - if (!S_ISREG(st.st_mode)) - return 0; - - if (FLAGS_SET(flags, UNLINK_ERASE) && st.st_size > 0 && st.st_nlink == 0) { - uint64_t left = st.st_size; - char buffer[64 * 1024]; - - /* If erasing is requested, let's overwrite the file with random data once before deleting - * it. This isn't going to give you shred(1) semantics, but hopefully should be good enough - * for stuff backed by tmpfs at least. - * - * Note that we only erase like this if the link count of the file is zero. If it is higher it - * is still linked by someone else and we'll leave it to them to remove it securely - * eventually! */ - - random_bytes(buffer, sizeof(buffer)); - - while (left > 0) { - ssize_t n; - - n = write(truncate_fd, buffer, MIN(sizeof(buffer), left)); - if (n < 0) { - log_debug_errno(errno, "Failed to erase data in file '%s', ignoring.", name); - break; - } - - assert(left >= (size_t) n); - left -= n; - } - - /* Let's refresh metadata */ - if (fstat(truncate_fd, &st) < 0) { - log_debug_errno(errno, "Failed to stat file '%s' for deallocation, ignoring: %m", name); - return 0; - } - } - - /* Don't dallocate if there's nothing to deallocate or if the file is linked elsewhere */ - if (st.st_blocks == 0 || st.st_nlink > 0) - return 0; - - /* If this is a regular file, it actually took up space on disk and there are no other links it's time to - * punch-hole/truncate this to release the disk space. */ - - bs = MAX(st.st_blksize, 512); - l = DIV_ROUND_UP(st.st_size, bs) * bs; /* Round up to next block size */ - - if (fallocate(truncate_fd, FALLOC_FL_PUNCH_HOLE|FALLOC_FL_KEEP_SIZE, 0, l) >= 0) - return 0; /* Successfully punched a hole! 😊 */ - - /* Fall back to truncation */ - if (ftruncate(truncate_fd, 0) < 0) { - log_debug_errno(errno, "Failed to truncate file to 0, ignoring: %m"); - return 0; - } - - return 0; -} - -#if 0 /* NM_IGNORED */ -int fsync_directory_of_file(int fd) { - _cleanup_free_ char *path = NULL; - _cleanup_close_ int dfd = -1; - int r; - - r = fd_verify_regular(fd); - if (r < 0) - return r; - - r = fd_get_path(fd, &path); - if (r < 0) { - log_debug_errno(r, "Failed to query /proc/self/fd/%d%s: %m", - fd, - r == -ENOSYS ? ", ignoring" : ""); - - if (r == -ENOSYS) - /* If /proc is not available, we're most likely running in some - * chroot environment, and syncing the directory is not very - * important in that case. Let's just silently do nothing. */ - return 0; - - return r; - } - - if (!path_is_absolute(path)) - return -EINVAL; - - dfd = open_parent(path, O_CLOEXEC, 0); - if (dfd < 0) - return dfd; - - if (fsync(dfd) < 0) - return -errno; - - return 0; -} -#endif /* NM_IGNORED */ - -int fsync_full(int fd) { - int r, q; - - /* Sync both the file and the directory */ - - r = fsync(fd) < 0 ? -errno : 0; - q = fsync_directory_of_file(fd); - - return r < 0 ? r : q; -} - -int fsync_path_at(int at_fd, const char *path) { - _cleanup_close_ int opened_fd = -1; - int fd; - - if (isempty(path)) { - if (at_fd == AT_FDCWD) { - opened_fd = open(".", O_RDONLY|O_DIRECTORY|O_CLOEXEC); - if (opened_fd < 0) - return -errno; - - fd = opened_fd; - } else - fd = at_fd; - } else { - - opened_fd = openat(at_fd, path, O_RDONLY|O_CLOEXEC); - if (opened_fd < 0) - return -errno; - - fd = opened_fd; - } - - if (fsync(fd) < 0) - return -errno; - - return 0; -} - -int syncfs_path(int atfd, const char *path) { - _cleanup_close_ int fd = -1; - - assert(path); - - fd = openat(atfd, path, O_CLOEXEC|O_RDONLY|O_NONBLOCK); - if (fd < 0) - return -errno; - - if (syncfs(fd) < 0) - return -errno; - - return 0; -} - -int open_parent(const char *path, int flags, mode_t mode) { - _cleanup_free_ char *parent = NULL; - int fd; - - if (isempty(path)) - return -EINVAL; - if (path_equal(path, "/")) /* requesting the parent of the root dir is fishy, let's prohibit that */ - return -EINVAL; - - parent = dirname_malloc(path); - if (!parent) - return -ENOMEM; - - /* Let's insist on O_DIRECTORY since the parent of a file or directory is a directory. Except if we open an - * O_TMPFILE file, because in that case we are actually create a regular file below the parent directory. */ - - if (FLAGS_SET(flags, O_PATH)) - flags |= O_DIRECTORY; - else if (!FLAGS_SET(flags, O_TMPFILE)) - flags |= O_DIRECTORY|O_RDONLY; - - fd = open(parent, flags, mode); - if (fd < 0) - return -errno; - - return fd; -} - -static int blockdev_is_encrypted(const char *sysfs_path, unsigned depth_left) { - _cleanup_free_ char *p = NULL, *uuids = NULL; - _cleanup_closedir_ DIR *d = NULL; - int r, found_encrypted = false; - - assert(sysfs_path); - - if (depth_left == 0) - return -EINVAL; - - p = path_join(sysfs_path, "dm/uuid"); - if (!p) - return -ENOMEM; - - r = read_one_line_file(p, &uuids); - if (r != -ENOENT) { - if (r < 0) - return r; - - /* The DM device's uuid attribute is prefixed with "CRYPT-" if this is a dm-crypt device. */ - if (startswith(uuids, "CRYPT-")) - return true; - } - - /* Not a dm-crypt device itself. But maybe it is on top of one? Follow the links in the "slaves/" - * subdir. */ - - p = mfree(p); - p = path_join(sysfs_path, "slaves"); - if (!p) - return -ENOMEM; - - d = opendir(p); - if (!d) { - if (errno == ENOENT) /* Doesn't have underlying devices */ - return false; - - return -errno; - } - - for (;;) { - _cleanup_free_ char *q = NULL; - struct dirent *de; - - errno = 0; - de = readdir_no_dot(d); - if (!de) { - if (errno != 0) - return -errno; - - break; /* No more underlying devices */ - } - - q = path_join(p, de->d_name); - if (!q) - return -ENOMEM; - - r = blockdev_is_encrypted(q, depth_left - 1); - if (r < 0) - return r; - if (r == 0) /* we found one that is not encrypted? then propagate that immediately */ - return false; - - found_encrypted = true; - } - - return found_encrypted; -} - -int path_is_encrypted(const char *path) { - char p[SYS_BLOCK_PATH_MAX(NULL)]; - dev_t devt; - int r; - - r = get_block_device(path, &devt); - if (r < 0) - return r; - if (r == 0) /* doesn't have a block device */ - return false; - - xsprintf_sys_block_path(p, NULL, devt); - - return blockdev_is_encrypted(p, 10 /* safety net: maximum recursion depth */); -} - -int conservative_rename( - int olddirfd, const char *oldpath, - int newdirfd, const char *newpath) { - - _cleanup_close_ int old_fd = -1, new_fd = -1; - struct stat old_stat, new_stat; - - /* Renames the old path to thew new path, much like renameat() — except if both are regular files and - * have the exact same contents and basic file attributes already. In that case remove the new file - * instead. This call is useful for reducing inotify wakeups on files that are updated but don't - * actually change. This function is written in a style that we rather rename too often than suppress - * too much. i.e. whenever we are in doubt we rather rename than fail. After all reducing inotify - * events is an optimization only, not more. */ - - old_fd = openat(olddirfd, oldpath, O_CLOEXEC|O_RDONLY|O_NOCTTY|O_NOFOLLOW); - if (old_fd < 0) - goto do_rename; - - new_fd = openat(newdirfd, newpath, O_CLOEXEC|O_RDONLY|O_NOCTTY|O_NOFOLLOW); - if (new_fd < 0) - goto do_rename; - - if (fstat(old_fd, &old_stat) < 0) - goto do_rename; - - if (!S_ISREG(old_stat.st_mode)) - goto do_rename; - - if (fstat(new_fd, &new_stat) < 0) - goto do_rename; - - if (new_stat.st_ino == old_stat.st_ino && - new_stat.st_dev == old_stat.st_dev) - goto is_same; - - if (old_stat.st_mode != new_stat.st_mode || - old_stat.st_size != new_stat.st_size || - old_stat.st_uid != new_stat.st_uid || - old_stat.st_gid != new_stat.st_gid) - goto do_rename; - - for (;;) { - char buf1[16*1024]; - char buf2[sizeof(buf1) + 1]; - ssize_t l1, l2; - - l1 = read(old_fd, buf1, sizeof(buf1)); - if (l1 < 0) - goto do_rename; - - l2 = read(new_fd, buf2, l1 + 1); - if (l1 != l2) - goto do_rename; - - if (l1 == 0) /* EOF on both! And everything's the same so far, yay! */ - break; - - if (memcmp(buf1, buf2, l1) != 0) - goto do_rename; - } - -is_same: - /* Everything matches? Then don't rename, instead remove the source file, and leave the existing - * destination in place */ - - if (unlinkat(olddirfd, oldpath, 0) < 0) - goto do_rename; - - return 0; - -do_rename: - if (renameat(olddirfd, oldpath, newdirfd, newpath) < 0) - return -errno; - - return 1; -} -#endif /* NM_IGNORED */ diff --git a/shared/systemd/src/basic/fs-util.h b/shared/systemd/src/basic/fs-util.h deleted file mode 100644 index 9a394735..00000000 --- a/shared/systemd/src/basic/fs-util.h +++ /dev/null @@ -1,136 +0,0 @@ -/* SPDX-License-Identifier: LGPL-2.1-or-later */ -#pragma once - -#include <dirent.h> -#include <fcntl.h> -#include <limits.h> -#include <stdbool.h> -#include <stdint.h> -#include <sys/inotify.h> -#include <sys/stat.h> -#include <sys/types.h> -#include <unistd.h> - -#include "errno-util.h" -#include "time-util.h" - -#define MODE_INVALID ((mode_t) -1) - -/* The following macros add 1 when converting things, since 0 is a valid mode, while the pointer - * NULL is special */ -#define PTR_TO_MODE(p) ((mode_t) ((uintptr_t) (p)-1)) -#define MODE_TO_PTR(u) ((void *) ((uintptr_t) (u)+1)) - -int unlink_noerrno(const char *path); - -int rmdir_parents(const char *path, const char *stop); - -int rename_noreplace(int olddirfd, const char *oldpath, int newdirfd, const char *newpath); - -int readlinkat_malloc(int fd, const char *p, char **ret); -int readlink_malloc(const char *p, char **r); -int readlink_value(const char *p, char **ret); -int readlink_and_make_absolute(const char *p, char **r); - -int chmod_and_chown(const char *path, mode_t mode, uid_t uid, gid_t gid); -int fchmod_and_chown(int fd, mode_t mode, uid_t uid, gid_t gid); - -int fchmod_umask(int fd, mode_t mode); -int fchmod_opath(int fd, mode_t m); - -int futimens_opath(int fd, const struct timespec ts[2]); - -int fd_warn_permissions(const char *path, int fd); -int stat_warn_permissions(const char *path, const struct stat *st); - -#define laccess(path, mode) faccessat(AT_FDCWD, (path), (mode), AT_SYMLINK_NOFOLLOW) - -int touch_file(const char *path, bool parents, usec_t stamp, uid_t uid, gid_t gid, mode_t mode); -int touch(const char *path); - -int symlink_idempotent(const char *from, const char *to, bool make_relative); - -int symlink_atomic(const char *from, const char *to); -int mknod_atomic(const char *path, mode_t mode, dev_t dev); -int mkfifo_atomic(const char *path, mode_t mode); -int mkfifoat_atomic(int dir_fd, const char *path, mode_t mode); - -int get_files_in_directory(const char *path, char ***list); - -int tmp_dir(const char **ret); -int var_tmp_dir(const char **ret); - -int unlink_or_warn(const char *filename); - -#define INOTIFY_EVENT_MAX (sizeof(struct inotify_event) + NAME_MAX + 1) - -#define FOREACH_INOTIFY_EVENT(e, buffer, sz) \ - for ((e) = &buffer.ev; \ - (uint8_t*) (e) < (uint8_t*) (buffer.raw) + (sz); \ - (e) = (struct inotify_event*) ((uint8_t*) (e) + sizeof(struct inotify_event) + (e)->len)) - -union inotify_event_buffer { - struct inotify_event ev; - uint8_t raw[INOTIFY_EVENT_MAX]; -}; - -int inotify_add_watch_fd(int fd, int what, uint32_t mask); -int inotify_add_watch_and_warn(int fd, const char *pathname, uint32_t mask); - -enum { - CHASE_PREFIX_ROOT = 1 << 0, /* The specified path will be prefixed by the specified root before beginning the iteration */ - CHASE_NONEXISTENT = 1 << 1, /* It's OK if the path doesn't actually exist. */ - CHASE_NO_AUTOFS = 1 << 2, /* Return -EREMOTE if autofs mount point found */ - CHASE_SAFE = 1 << 3, /* Return -EPERM if we ever traverse from unprivileged to privileged files or directories */ - CHASE_TRAIL_SLASH = 1 << 4, /* Any trailing slash will be preserved */ - CHASE_STEP = 1 << 5, /* Just execute a single step of the normalization */ - CHASE_NOFOLLOW = 1 << 6, /* Do not follow the path's right-most component. With ret_fd, when the path's - * right-most component refers to symlink, return O_PATH fd of the symlink. */ - CHASE_WARN = 1 << 7, /* Emit an appropriate warning when an error is encountered */ -}; - -/* How many iterations to execute before returning -ELOOP */ -#define CHASE_SYMLINKS_MAX 32 - -int chase_symlinks(const char *path_with_prefix, const char *root, unsigned flags, char **ret_path, int *ret_fd); - -int chase_symlinks_and_open(const char *path, const char *root, unsigned chase_flags, int open_flags, char **ret_path); -int chase_symlinks_and_opendir(const char *path, const char *root, unsigned chase_flags, char **ret_path, DIR **ret_dir); -int chase_symlinks_and_stat(const char *path, const char *root, unsigned chase_flags, char **ret_path, struct stat *ret_stat, int *ret_fd); - -/* Useful for usage with _cleanup_(), removes a directory and frees the pointer */ -static inline void rmdir_and_free(char *p) { - PROTECT_ERRNO; - (void) rmdir(p); - free(p); -} -DEFINE_TRIVIAL_CLEANUP_FUNC(char*, rmdir_and_free); - -static inline void unlink_and_free(char *p) { - (void) unlink_noerrno(p); - free(p); -} -DEFINE_TRIVIAL_CLEANUP_FUNC(char*, unlink_and_free); - -int access_fd(int fd, int mode); - -void unlink_tempfilep(char (*p)[]); - -typedef enum UnlinkDeallocateFlags { - UNLINK_REMOVEDIR = 1 << 0, - UNLINK_ERASE = 1 << 1, -} UnlinkDeallocateFlags; - -int unlinkat_deallocate(int fd, const char *name, UnlinkDeallocateFlags flags); - -int fsync_directory_of_file(int fd); -int fsync_full(int fd); -int fsync_path_at(int at_fd, const char *path); - -int syncfs_path(int atfd, const char *path); - -int open_parent(const char *path, int flags, mode_t mode); - -int path_is_encrypted(const char *path); - -int conservative_rename(int olddirfd, const char *oldpath, int newdirfd, const char *newpath); diff --git a/shared/systemd/src/basic/hash-funcs.c b/shared/systemd/src/basic/hash-funcs.c deleted file mode 100644 index 6f540b29..00000000 --- a/shared/systemd/src/basic/hash-funcs.c +++ /dev/null @@ -1,113 +0,0 @@ -/* SPDX-License-Identifier: LGPL-2.1-or-later */ - -#include "nm-sd-adapt-shared.h" - -#include <string.h> - -#include "hash-funcs.h" -#include "path-util.h" - -void string_hash_func(const char *p, struct siphash *state) { - siphash24_compress(p, strlen(p) + 1, state); -} - -DEFINE_HASH_OPS(string_hash_ops, char, string_hash_func, string_compare_func); -DEFINE_HASH_OPS_WITH_KEY_DESTRUCTOR(string_hash_ops_free, - char, string_hash_func, string_compare_func, free); -DEFINE_HASH_OPS_FULL(string_hash_ops_free_free, - char, string_hash_func, string_compare_func, free, - char, free); - -#if 0 /* NM_IGNORED */ -void path_hash_func(const char *q, struct siphash *state) { - size_t n; - - assert(q); - assert(state); - - /* Calculates a hash for a path in a way this duplicate inner slashes don't make a differences, and also - * whether there's a trailing slash or not. This fits well with the semantics of path_compare(), which does - * similar checks and also doesn't care for trailing slashes. Note that relative and absolute paths (i.e. those - * which begin in a slash or not) will hash differently though. */ - - n = strspn(q, "/"); - if (n > 0) { /* Eat up initial slashes, and add one "/" to the hash for all of them */ - siphash24_compress(q, 1, state); - q += n; - } - - for (;;) { - /* Determine length of next component */ - n = strcspn(q, "/"); - if (n == 0) /* Reached the end? */ - break; - - /* Add this component to the hash and skip over it */ - siphash24_compress(q, n, state); - q += n; - - /* How many slashes follow this component? */ - n = strspn(q, "/"); - if (q[n] == 0) /* Is this a trailing slash? If so, we are at the end, and don't care about the slashes anymore */ - break; - - /* We are not add the end yet. Hash exactly one slash for all of the ones we just encountered. */ - siphash24_compress(q, 1, state); - q += n; - } -} - -DEFINE_HASH_OPS(path_hash_ops, char, path_hash_func, path_compare); -DEFINE_HASH_OPS_WITH_KEY_DESTRUCTOR(path_hash_ops_free, - char, path_hash_func, path_compare, free); -#endif /* NM_IGNORED */ - -void trivial_hash_func(const void *p, struct siphash *state) { - siphash24_compress(&p, sizeof(p), state); -} - -int trivial_compare_func(const void *a, const void *b) { - return CMP(a, b); -} - -const struct hash_ops trivial_hash_ops = { - .hash = trivial_hash_func, - .compare = trivial_compare_func, -}; - -const struct hash_ops trivial_hash_ops_free = { - .hash = trivial_hash_func, - .compare = trivial_compare_func, - .free_key = free, -}; - -const struct hash_ops trivial_hash_ops_free_free = { - .hash = trivial_hash_func, - .compare = trivial_compare_func, - .free_key = free, - .free_value = free, -}; - -void uint64_hash_func(const uint64_t *p, struct siphash *state) { - siphash24_compress(p, sizeof(uint64_t), state); -} - -int uint64_compare_func(const uint64_t *a, const uint64_t *b) { - return CMP(*a, *b); -} - -DEFINE_HASH_OPS(uint64_hash_ops, uint64_t, uint64_hash_func, uint64_compare_func); - -#if 0 /* NM_IGNORED */ -#if SIZEOF_DEV_T != 8 -void devt_hash_func(const dev_t *p, struct siphash *state) { - siphash24_compress(p, sizeof(dev_t), state); -} - -int devt_compare_func(const dev_t *a, const dev_t *b) { - return CMP(*a, *b); -} - -DEFINE_HASH_OPS(devt_hash_ops, dev_t, devt_hash_func, devt_compare_func); -#endif -#endif /* NM_IGNORED */ diff --git a/shared/systemd/src/basic/hash-funcs.h b/shared/systemd/src/basic/hash-funcs.h deleted file mode 100644 index 5672df1d..00000000 --- a/shared/systemd/src/basic/hash-funcs.h +++ /dev/null @@ -1,110 +0,0 @@ -/* SPDX-License-Identifier: LGPL-2.1-or-later */ -#pragma once - -#include "alloc-util.h" -#include "macro.h" -#include "siphash24.h" - -typedef void (*hash_func_t)(const void *p, struct siphash *state); -typedef int (*compare_func_t)(const void *a, const void *b); - -struct hash_ops { - hash_func_t hash; - compare_func_t compare; - free_func_t free_key; - free_func_t free_value; -}; - -#define _DEFINE_HASH_OPS(uq, name, type, hash_func, compare_func, free_key_func, free_value_func, scope) \ - _unused_ static void (* UNIQ_T(static_hash_wrapper, uq))(const type *, struct siphash *) = hash_func; \ - _unused_ static int (* UNIQ_T(static_compare_wrapper, uq))(const type *, const type *) = compare_func; \ - scope const struct hash_ops name = { \ - .hash = (hash_func_t) hash_func, \ - .compare = (compare_func_t) compare_func, \ - .free_key = free_key_func, \ - .free_value = free_value_func, \ - } - -#define _DEFINE_FREE_FUNC(uq, type, wrapper_name, func) \ - /* Type-safe free function */ \ - static void UNIQ_T(wrapper_name, uq)(void *a) { \ - type *_a = a; \ - func(_a); \ - } - -#define _DEFINE_HASH_OPS_WITH_KEY_DESTRUCTOR(uq, name, type, hash_func, compare_func, free_func, scope) \ - _DEFINE_FREE_FUNC(uq, type, static_free_wrapper, free_func); \ - _DEFINE_HASH_OPS(uq, name, type, hash_func, compare_func, \ - UNIQ_T(static_free_wrapper, uq), NULL, scope) - -#define _DEFINE_HASH_OPS_WITH_VALUE_DESTRUCTOR(uq, name, type, hash_func, compare_func, type_value, free_func, scope) \ - _DEFINE_FREE_FUNC(uq, type_value, static_free_wrapper, free_func); \ - _DEFINE_HASH_OPS(uq, name, type, hash_func, compare_func, \ - NULL, UNIQ_T(static_free_wrapper, uq), scope) - -#define _DEFINE_HASH_OPS_FULL(uq, name, type, hash_func, compare_func, free_key_func, type_value, free_value_func, scope) \ - _DEFINE_FREE_FUNC(uq, type, static_free_key_wrapper, free_key_func); \ - _DEFINE_FREE_FUNC(uq, type_value, static_free_value_wrapper, free_value_func); \ - _DEFINE_HASH_OPS(uq, name, type, hash_func, compare_func, \ - UNIQ_T(static_free_key_wrapper, uq), \ - UNIQ_T(static_free_value_wrapper, uq), scope) - -#define DEFINE_HASH_OPS(name, type, hash_func, compare_func) \ - _DEFINE_HASH_OPS(UNIQ, name, type, hash_func, compare_func, NULL, NULL,) - -#define DEFINE_PRIVATE_HASH_OPS(name, type, hash_func, compare_func) \ - _DEFINE_HASH_OPS(UNIQ, name, type, hash_func, compare_func, NULL, NULL, static) - -#define DEFINE_HASH_OPS_WITH_KEY_DESTRUCTOR(name, type, hash_func, compare_func, free_func) \ - _DEFINE_HASH_OPS_WITH_KEY_DESTRUCTOR(UNIQ, name, type, hash_func, compare_func, free_func,) - -#define DEFINE_PRIVATE_HASH_OPS_WITH_KEY_DESTRUCTOR(name, type, hash_func, compare_func, free_func) \ - _DEFINE_HASH_OPS_WITH_KEY_DESTRUCTOR(UNIQ, name, type, hash_func, compare_func, free_func, static) - -#define DEFINE_HASH_OPS_WITH_VALUE_DESTRUCTOR(name, type, hash_func, compare_func, value_type, free_func) \ - _DEFINE_HASH_OPS_WITH_VALUE_DESTRUCTOR(UNIQ, name, type, hash_func, compare_func, value_type, free_func,) - -#define DEFINE_PRIVATE_HASH_OPS_WITH_VALUE_DESTRUCTOR(name, type, hash_func, compare_func, value_type, free_func) \ - _DEFINE_HASH_OPS_WITH_VALUE_DESTRUCTOR(UNIQ, name, type, hash_func, compare_func, value_type, free_func, static) - -#define DEFINE_HASH_OPS_FULL(name, type, hash_func, compare_func, free_key_func, value_type, free_value_func) \ - _DEFINE_HASH_OPS_FULL(UNIQ, name, type, hash_func, compare_func, free_key_func, value_type, free_value_func,) - -#define DEFINE_PRIVATE_HASH_OPS_FULL(name, type, hash_func, compare_func, free_key_func, value_type, free_value_func) \ - _DEFINE_HASH_OPS_FULL(UNIQ, name, type, hash_func, compare_func, free_key_func, value_type, free_value_func, static) - -void string_hash_func(const char *p, struct siphash *state); -#define string_compare_func strcmp -extern const struct hash_ops string_hash_ops; -extern const struct hash_ops string_hash_ops_free; -extern const struct hash_ops string_hash_ops_free_free; - -void path_hash_func(const char *p, struct siphash *state); -extern const struct hash_ops path_hash_ops; -extern const struct hash_ops path_hash_ops_free; - -/* This will compare the passed pointers directly, and will not dereference them. This is hence not useful for strings - * or suchlike. */ -void trivial_hash_func(const void *p, struct siphash *state); -int trivial_compare_func(const void *a, const void *b) _const_; -extern const struct hash_ops trivial_hash_ops; -extern const struct hash_ops trivial_hash_ops_free; -extern const struct hash_ops trivial_hash_ops_free_free; - -/* 32bit values we can always just embed in the pointer itself, but in order to support 32bit archs we need store 64bit - * values indirectly, since they don't fit in a pointer. */ -void uint64_hash_func(const uint64_t *p, struct siphash *state); -int uint64_compare_func(const uint64_t *a, const uint64_t *b) _pure_; -extern const struct hash_ops uint64_hash_ops; - -/* On some archs dev_t is 32bit, and on others 64bit. And sometimes it's 64bit on 32bit archs, and sometimes 32bit on - * 64bit archs. Yuck! */ -#if SIZEOF_DEV_T != 8 -void devt_hash_func(const dev_t *p, struct siphash *state) _pure_; -int devt_compare_func(const dev_t *a, const dev_t *b) _pure_; -extern const struct hash_ops devt_hash_ops; -#else -#define devt_hash_func uint64_hash_func -#define devt_compare_func uint64_compare_func -#define devt_hash_ops uint64_hash_ops -#endif diff --git a/shared/systemd/src/basic/hashmap.c b/shared/systemd/src/basic/hashmap.c deleted file mode 100644 index 0a5deabf..00000000 --- a/shared/systemd/src/basic/hashmap.c +++ /dev/null @@ -1,2032 +0,0 @@ -/* SPDX-License-Identifier: LGPL-2.1-or-later */ - -#include "nm-sd-adapt-shared.h" - -#include <errno.h> -#include <pthread.h> -#include <stdint.h> -#include <stdlib.h> - -#include "alloc-util.h" -#include "fileio.h" -#include "hashmap.h" -#include "macro.h" -#include "memory-util.h" -#include "mempool.h" -#include "missing_syscall.h" -#include "process-util.h" -#include "random-util.h" -#include "set.h" -#include "siphash24.h" -#include "string-util.h" -#include "strv.h" - -#if ENABLE_DEBUG_HASHMAP -#include "list.h" -#endif - -/* - * Implementation of hashmaps. - * Addressing: open - * - uses less RAM compared to closed addressing (chaining), because - * our entries are small (especially in Sets, which tend to contain - * the majority of entries in systemd). - * Collision resolution: Robin Hood - * - tends to equalize displacement of entries from their optimal buckets. - * Probe sequence: linear - * - though theoretically worse than random probing/uniform hashing/double - * hashing, it is good for cache locality. - * - * References: - * Celis, P. 1986. Robin Hood Hashing. - * Ph.D. Dissertation. University of Waterloo, Waterloo, Ont., Canada, Canada. - * https://cs.uwaterloo.ca/research/tr/1986/CS-86-14.pdf - * - The results are derived for random probing. Suggests deletion with - * tombstones and two mean-centered search methods. None of that works - * well for linear probing. - * - * Janson, S. 2005. Individual displacements for linear probing hashing with different insertion policies. - * ACM Trans. Algorithms 1, 2 (October 2005), 177-213. - * DOI=10.1145/1103963.1103964 http://doi.acm.org/10.1145/1103963.1103964 - * http://www.math.uu.se/~svante/papers/sj157.pdf - * - Applies to Robin Hood with linear probing. Contains remarks on - * the unsuitability of mean-centered search with linear probing. - * - * Viola, A. 2005. Exact distribution of individual displacements in linear probing hashing. - * ACM Trans. Algorithms 1, 2 (October 2005), 214-242. - * DOI=10.1145/1103963.1103965 http://doi.acm.org/10.1145/1103963.1103965 - * - Similar to Janson. Note that Viola writes about C_{m,n} (number of probes - * in a successful search), and Janson writes about displacement. C = d + 1. - * - * Goossaert, E. 2013. Robin Hood hashing: backward shift deletion. - * http://codecapsule.com/2013/11/17/robin-hood-hashing-backward-shift-deletion/ - * - Explanation of backward shift deletion with pictures. - * - * Khuong, P. 2013. The Other Robin Hood Hashing. - * http://www.pvk.ca/Blog/2013/11/26/the-other-robin-hood-hashing/ - * - Short summary of random vs. linear probing, and tombstones vs. backward shift. - */ - -/* - * XXX Ideas for improvement: - * For unordered hashmaps, randomize iteration order, similarly to Perl: - * http://blog.booking.com/hardening-perls-hash-function.html - */ - -/* INV_KEEP_FREE = 1 / (1 - max_load_factor) - * e.g. 1 / (1 - 0.8) = 5 ... keep one fifth of the buckets free. */ -#define INV_KEEP_FREE 5U - -/* Fields common to entries of all hashmap/set types */ -struct hashmap_base_entry { - const void *key; -}; - -/* Entry types for specific hashmap/set types - * hashmap_base_entry must be at the beginning of each entry struct. */ - -struct plain_hashmap_entry { - struct hashmap_base_entry b; - void *value; -}; - -struct ordered_hashmap_entry { - struct plain_hashmap_entry p; - unsigned iterate_next, iterate_previous; -}; - -struct set_entry { - struct hashmap_base_entry b; -}; - -/* In several functions it is advantageous to have the hash table extended - * virtually by a couple of additional buckets. We reserve special index values - * for these "swap" buckets. */ -#define _IDX_SWAP_BEGIN (UINT_MAX - 3) -#define IDX_PUT (_IDX_SWAP_BEGIN + 0) -#define IDX_TMP (_IDX_SWAP_BEGIN + 1) -#define _IDX_SWAP_END (_IDX_SWAP_BEGIN + 2) - -#define IDX_FIRST (UINT_MAX - 1) /* special index for freshly initialized iterators */ -#define IDX_NIL UINT_MAX /* special index value meaning "none" or "end" */ - -assert_cc(IDX_FIRST == _IDX_SWAP_END); -assert_cc(IDX_FIRST == _IDX_ITERATOR_FIRST); - -/* Storage space for the "swap" buckets. - * All entry types can fit into a ordered_hashmap_entry. */ -struct swap_entries { - struct ordered_hashmap_entry e[_IDX_SWAP_END - _IDX_SWAP_BEGIN]; -}; - -/* Distance from Initial Bucket */ -typedef uint8_t dib_raw_t; -#define DIB_RAW_OVERFLOW ((dib_raw_t)0xfdU) /* indicates DIB value is greater than representable */ -#define DIB_RAW_REHASH ((dib_raw_t)0xfeU) /* entry yet to be rehashed during in-place resize */ -#define DIB_RAW_FREE ((dib_raw_t)0xffU) /* a free bucket */ -#define DIB_RAW_INIT ((char)DIB_RAW_FREE) /* a byte to memset a DIB store with when initializing */ - -#define DIB_FREE UINT_MAX - -#if ENABLE_DEBUG_HASHMAP -struct hashmap_debug_info { - LIST_FIELDS(struct hashmap_debug_info, debug_list); - unsigned max_entries; /* high watermark of n_entries */ - - /* who allocated this hashmap */ - int line; - const char *file; - const char *func; - - /* fields to detect modification while iterating */ - unsigned put_count; /* counts puts into the hashmap */ - unsigned rem_count; /* counts removals from hashmap */ - unsigned last_rem_idx; /* remembers last removal index */ -}; - -/* Tracks all existing hashmaps. Get at it from gdb. See sd_dump_hashmaps.py */ -static LIST_HEAD(struct hashmap_debug_info, hashmap_debug_list); -static pthread_mutex_t hashmap_debug_list_mutex = PTHREAD_MUTEX_INITIALIZER; -#endif - -enum HashmapType { - HASHMAP_TYPE_PLAIN, - HASHMAP_TYPE_ORDERED, - HASHMAP_TYPE_SET, - _HASHMAP_TYPE_MAX -}; - -struct _packed_ indirect_storage { - void *storage; /* where buckets and DIBs are stored */ - uint8_t hash_key[HASH_KEY_SIZE]; /* hash key; changes during resize */ - - unsigned n_entries; /* number of stored entries */ - unsigned n_buckets; /* number of buckets */ - - unsigned idx_lowest_entry; /* Index below which all buckets are free. - Makes "while(hashmap_steal_first())" loops - O(n) instead of O(n^2) for unordered hashmaps. */ - uint8_t _pad[3]; /* padding for the whole HashmapBase */ - /* The bitfields in HashmapBase complete the alignment of the whole thing. */ -}; - -struct direct_storage { - /* This gives us 39 bytes on 64bit, or 35 bytes on 32bit. - * That's room for 4 set_entries + 4 DIB bytes + 3 unused bytes on 64bit, - * or 7 set_entries + 7 DIB bytes + 0 unused bytes on 32bit. */ - uint8_t storage[sizeof(struct indirect_storage)]; -}; - -#define DIRECT_BUCKETS(entry_t) \ - (sizeof(struct direct_storage) / (sizeof(entry_t) + sizeof(dib_raw_t))) - -/* We should be able to store at least one entry directly. */ -assert_cc(DIRECT_BUCKETS(struct ordered_hashmap_entry) >= 1); - -/* We have 3 bits for n_direct_entries. */ -assert_cc(DIRECT_BUCKETS(struct set_entry) < (1 << 3)); - -/* Hashmaps with directly stored entries all use this shared hash key. - * It's no big deal if the key is guessed, because there can be only - * a handful of directly stored entries in a hashmap. When a hashmap - * outgrows direct storage, it gets its own key for indirect storage. */ -static uint8_t shared_hash_key[HASH_KEY_SIZE]; - -/* Fields that all hashmap/set types must have */ -struct HashmapBase { - const struct hash_ops *hash_ops; /* hash and compare ops to use */ - - union _packed_ { - struct indirect_storage indirect; /* if has_indirect */ - struct direct_storage direct; /* if !has_indirect */ - }; - - enum HashmapType type:2; /* HASHMAP_TYPE_* */ - bool has_indirect:1; /* whether indirect storage is used */ - unsigned n_direct_entries:3; /* Number of entries in direct storage. - * Only valid if !has_indirect. */ - bool from_pool:1; /* whether was allocated from mempool */ - bool dirty:1; /* whether dirtied since last iterated_cache_get() */ - bool cached:1; /* whether this hashmap is being cached */ - -#if ENABLE_DEBUG_HASHMAP - struct hashmap_debug_info debug; -#endif -}; - -/* Specific hash types - * HashmapBase must be at the beginning of each hashmap struct. */ - -struct Hashmap { - struct HashmapBase b; -}; - -struct OrderedHashmap { - struct HashmapBase b; - unsigned iterate_list_head, iterate_list_tail; -}; - -struct Set { - struct HashmapBase b; -}; - -typedef struct CacheMem { - const void **ptr; - size_t n_populated, n_allocated; - bool active:1; -} CacheMem; - -struct IteratedCache { - HashmapBase *hashmap; - CacheMem keys, values; -}; - -DEFINE_MEMPOOL(hashmap_pool, Hashmap, 8); -DEFINE_MEMPOOL(ordered_hashmap_pool, OrderedHashmap, 8); -/* No need for a separate Set pool */ -assert_cc(sizeof(Hashmap) == sizeof(Set)); - -struct hashmap_type_info { - size_t head_size; - size_t entry_size; - struct mempool *mempool; - unsigned n_direct_buckets; -}; - -static _used_ const struct hashmap_type_info hashmap_type_info[_HASHMAP_TYPE_MAX] = { - [HASHMAP_TYPE_PLAIN] = { - .head_size = sizeof(Hashmap), - .entry_size = sizeof(struct plain_hashmap_entry), - .mempool = &hashmap_pool, - .n_direct_buckets = DIRECT_BUCKETS(struct plain_hashmap_entry), - }, - [HASHMAP_TYPE_ORDERED] = { - .head_size = sizeof(OrderedHashmap), - .entry_size = sizeof(struct ordered_hashmap_entry), - .mempool = &ordered_hashmap_pool, - .n_direct_buckets = DIRECT_BUCKETS(struct ordered_hashmap_entry), - }, - [HASHMAP_TYPE_SET] = { - .head_size = sizeof(Set), - .entry_size = sizeof(struct set_entry), - .mempool = &hashmap_pool, - .n_direct_buckets = DIRECT_BUCKETS(struct set_entry), - }, -}; - -#if VALGRIND -_destructor_ static void cleanup_pools(void) { - _cleanup_free_ char *t = NULL; - int r; - - /* Be nice to valgrind */ - - /* The pool is only allocated by the main thread, but the memory can - * be passed to other threads. Let's clean up if we are the main thread - * and no other threads are live. */ - /* We build our own is_main_thread() here, which doesn't use C11 - * TLS based caching of the result. That's because valgrind apparently - * doesn't like malloc() (which C11 TLS internally uses) to be called - * from a GCC destructors. */ - if (getpid() != gettid()) - return; - - r = get_proc_field("/proc/self/status", "Threads", WHITESPACE, &t); - if (r < 0 || !streq(t, "1")) - return; - - mempool_drop(&hashmap_pool); - mempool_drop(&ordered_hashmap_pool); -} -#endif - -static unsigned n_buckets(HashmapBase *h) { - return h->has_indirect ? h->indirect.n_buckets - : hashmap_type_info[h->type].n_direct_buckets; -} - -static unsigned n_entries(HashmapBase *h) { - return h->has_indirect ? h->indirect.n_entries - : h->n_direct_entries; -} - -static void n_entries_inc(HashmapBase *h) { - if (h->has_indirect) - h->indirect.n_entries++; - else - h->n_direct_entries++; -} - -static void n_entries_dec(HashmapBase *h) { - if (h->has_indirect) - h->indirect.n_entries--; - else - h->n_direct_entries--; -} - -static void* storage_ptr(HashmapBase *h) { - return h->has_indirect ? h->indirect.storage - : h->direct.storage; -} - -static uint8_t* hash_key(HashmapBase *h) { - return h->has_indirect ? h->indirect.hash_key - : shared_hash_key; -} - -static unsigned base_bucket_hash(HashmapBase *h, const void *p) { - struct siphash state; - uint64_t hash; - - siphash24_init(&state, hash_key(h)); - - h->hash_ops->hash(p, &state); - - hash = siphash24_finalize(&state); - - return (unsigned) (hash % n_buckets(h)); -} -#define bucket_hash(h, p) base_bucket_hash(HASHMAP_BASE(h), p) - -static void base_set_dirty(HashmapBase *h) { - h->dirty = true; -} -#define hashmap_set_dirty(h) base_set_dirty(HASHMAP_BASE(h)) - -static void get_hash_key(uint8_t hash_key[HASH_KEY_SIZE], bool reuse_is_ok) { - static uint8_t current[HASH_KEY_SIZE]; - static bool current_initialized = false; - - /* Returns a hash function key to use. In order to keep things - * fast we will not generate a new key each time we allocate a - * new hash table. Instead, we'll just reuse the most recently - * generated one, except if we never generated one or when we - * are rehashing an entire hash table because we reached a - * fill level */ - - if (!current_initialized || !reuse_is_ok) { - random_bytes(current, sizeof(current)); - current_initialized = true; - } - - memcpy(hash_key, current, sizeof(current)); -} - -static struct hashmap_base_entry* bucket_at(HashmapBase *h, unsigned idx) { - return (struct hashmap_base_entry*) - ((uint8_t*) storage_ptr(h) + idx * hashmap_type_info[h->type].entry_size); -} - -static struct plain_hashmap_entry* plain_bucket_at(Hashmap *h, unsigned idx) { - return (struct plain_hashmap_entry*) bucket_at(HASHMAP_BASE(h), idx); -} - -static struct ordered_hashmap_entry* ordered_bucket_at(OrderedHashmap *h, unsigned idx) { - return (struct ordered_hashmap_entry*) bucket_at(HASHMAP_BASE(h), idx); -} - -static struct set_entry *set_bucket_at(Set *h, unsigned idx) { - return (struct set_entry*) bucket_at(HASHMAP_BASE(h), idx); -} - -static struct ordered_hashmap_entry* bucket_at_swap(struct swap_entries *swap, unsigned idx) { - return &swap->e[idx - _IDX_SWAP_BEGIN]; -} - -/* Returns a pointer to the bucket at index idx. - * Understands real indexes and swap indexes, hence "_virtual". */ -static struct hashmap_base_entry* bucket_at_virtual(HashmapBase *h, struct swap_entries *swap, - unsigned idx) { - if (idx < _IDX_SWAP_BEGIN) - return bucket_at(h, idx); - - if (idx < _IDX_SWAP_END) - return &bucket_at_swap(swap, idx)->p.b; - - assert_not_reached("Invalid index"); -} - -static dib_raw_t* dib_raw_ptr(HashmapBase *h) { - return (dib_raw_t*) - ((uint8_t*) storage_ptr(h) + hashmap_type_info[h->type].entry_size * n_buckets(h)); -} - -static unsigned bucket_distance(HashmapBase *h, unsigned idx, unsigned from) { - return idx >= from ? idx - from - : n_buckets(h) + idx - from; -} - -static unsigned bucket_calculate_dib(HashmapBase *h, unsigned idx, dib_raw_t raw_dib) { - unsigned initial_bucket; - - if (raw_dib == DIB_RAW_FREE) - return DIB_FREE; - - if (_likely_(raw_dib < DIB_RAW_OVERFLOW)) - return raw_dib; - - /* - * Having an overflow DIB value is very unlikely. The hash function - * would have to be bad. For example, in a table of size 2^24 filled - * to load factor 0.9 the maximum observed DIB is only about 60. - * In theory (assuming I used Maxima correctly), for an infinite size - * hash table with load factor 0.8 the probability of a given entry - * having DIB > 40 is 1.9e-8. - * This returns the correct DIB value by recomputing the hash value in - * the unlikely case. XXX Hitting this case could be a hint to rehash. - */ - initial_bucket = bucket_hash(h, bucket_at(h, idx)->key); - return bucket_distance(h, idx, initial_bucket); -} - -static void bucket_set_dib(HashmapBase *h, unsigned idx, unsigned dib) { - dib_raw_ptr(h)[idx] = dib != DIB_FREE ? MIN(dib, DIB_RAW_OVERFLOW) : DIB_RAW_FREE; -} - -static unsigned skip_free_buckets(HashmapBase *h, unsigned idx) { - dib_raw_t *dibs; - - dibs = dib_raw_ptr(h); - - for ( ; idx < n_buckets(h); idx++) - if (dibs[idx] != DIB_RAW_FREE) - return idx; - - return IDX_NIL; -} - -static void bucket_mark_free(HashmapBase *h, unsigned idx) { - memzero(bucket_at(h, idx), hashmap_type_info[h->type].entry_size); - bucket_set_dib(h, idx, DIB_FREE); -} - -static void bucket_move_entry(HashmapBase *h, struct swap_entries *swap, - unsigned from, unsigned to) { - struct hashmap_base_entry *e_from, *e_to; - - assert(from != to); - - e_from = bucket_at_virtual(h, swap, from); - e_to = bucket_at_virtual(h, swap, to); - - memcpy(e_to, e_from, hashmap_type_info[h->type].entry_size); - - if (h->type == HASHMAP_TYPE_ORDERED) { - OrderedHashmap *lh = (OrderedHashmap*) h; - struct ordered_hashmap_entry *le, *le_to; - - le_to = (struct ordered_hashmap_entry*) e_to; - - if (le_to->iterate_next != IDX_NIL) { - le = (struct ordered_hashmap_entry*) - bucket_at_virtual(h, swap, le_to->iterate_next); - le->iterate_previous = to; - } - - if (le_to->iterate_previous != IDX_NIL) { - le = (struct ordered_hashmap_entry*) - bucket_at_virtual(h, swap, le_to->iterate_previous); - le->iterate_next = to; - } - - if (lh->iterate_list_head == from) - lh->iterate_list_head = to; - if (lh->iterate_list_tail == from) - lh->iterate_list_tail = to; - } -} - -static unsigned next_idx(HashmapBase *h, unsigned idx) { - return (idx + 1U) % n_buckets(h); -} - -static unsigned prev_idx(HashmapBase *h, unsigned idx) { - return (n_buckets(h) + idx - 1U) % n_buckets(h); -} - -static void* entry_value(HashmapBase *h, struct hashmap_base_entry *e) { - switch (h->type) { - - case HASHMAP_TYPE_PLAIN: - case HASHMAP_TYPE_ORDERED: - return ((struct plain_hashmap_entry*)e)->value; - - case HASHMAP_TYPE_SET: - return (void*) e->key; - - default: - assert_not_reached("Unknown hashmap type"); - } -} - -static void base_remove_entry(HashmapBase *h, unsigned idx) { - unsigned left, right, prev, dib; - dib_raw_t raw_dib, *dibs; - - dibs = dib_raw_ptr(h); - assert(dibs[idx] != DIB_RAW_FREE); - -#if ENABLE_DEBUG_HASHMAP - h->debug.rem_count++; - h->debug.last_rem_idx = idx; -#endif - - left = idx; - /* Find the stop bucket ("right"). It is either free or has DIB == 0. */ - for (right = next_idx(h, left); ; right = next_idx(h, right)) { - raw_dib = dibs[right]; - if (IN_SET(raw_dib, 0, DIB_RAW_FREE)) - break; - - /* The buckets are not supposed to be all occupied and with DIB > 0. - * That would mean we could make everyone better off by shifting them - * backward. This scenario is impossible. */ - assert(left != right); - } - - if (h->type == HASHMAP_TYPE_ORDERED) { - OrderedHashmap *lh = (OrderedHashmap*) h; - struct ordered_hashmap_entry *le = ordered_bucket_at(lh, idx); - - if (le->iterate_next != IDX_NIL) - ordered_bucket_at(lh, le->iterate_next)->iterate_previous = le->iterate_previous; - else - lh->iterate_list_tail = le->iterate_previous; - - if (le->iterate_previous != IDX_NIL) - ordered_bucket_at(lh, le->iterate_previous)->iterate_next = le->iterate_next; - else - lh->iterate_list_head = le->iterate_next; - } - - /* Now shift all buckets in the interval (left, right) one step backwards */ - for (prev = left, left = next_idx(h, left); left != right; - prev = left, left = next_idx(h, left)) { - dib = bucket_calculate_dib(h, left, dibs[left]); - assert(dib != 0); - bucket_move_entry(h, NULL, left, prev); - bucket_set_dib(h, prev, dib - 1); - } - - bucket_mark_free(h, prev); - n_entries_dec(h); - base_set_dirty(h); -} -#define remove_entry(h, idx) base_remove_entry(HASHMAP_BASE(h), idx) - -static unsigned hashmap_iterate_in_insertion_order(OrderedHashmap *h, Iterator *i) { - struct ordered_hashmap_entry *e; - unsigned idx; - - assert(h); - assert(i); - - if (i->idx == IDX_NIL) - goto at_end; - - if (i->idx == IDX_FIRST && h->iterate_list_head == IDX_NIL) - goto at_end; - - if (i->idx == IDX_FIRST) { - idx = h->iterate_list_head; - e = ordered_bucket_at(h, idx); - } else { - idx = i->idx; - e = ordered_bucket_at(h, idx); - /* - * We allow removing the current entry while iterating, but removal may cause - * a backward shift. The next entry may thus move one bucket to the left. - * To detect when it happens, we remember the key pointer of the entry we were - * going to iterate next. If it does not match, there was a backward shift. - */ - if (e->p.b.key != i->next_key) { - idx = prev_idx(HASHMAP_BASE(h), idx); - e = ordered_bucket_at(h, idx); - } - assert(e->p.b.key == i->next_key); - } - -#if ENABLE_DEBUG_HASHMAP - i->prev_idx = idx; -#endif - - if (e->iterate_next != IDX_NIL) { - struct ordered_hashmap_entry *n; - i->idx = e->iterate_next; - n = ordered_bucket_at(h, i->idx); - i->next_key = n->p.b.key; - } else - i->idx = IDX_NIL; - - return idx; - -at_end: - i->idx = IDX_NIL; - return IDX_NIL; -} - -static unsigned hashmap_iterate_in_internal_order(HashmapBase *h, Iterator *i) { - unsigned idx; - - assert(h); - assert(i); - - if (i->idx == IDX_NIL) - goto at_end; - - if (i->idx == IDX_FIRST) { - /* fast forward to the first occupied bucket */ - if (h->has_indirect) { - i->idx = skip_free_buckets(h, h->indirect.idx_lowest_entry); - h->indirect.idx_lowest_entry = i->idx; - } else - i->idx = skip_free_buckets(h, 0); - - if (i->idx == IDX_NIL) - goto at_end; - } else { - struct hashmap_base_entry *e; - - assert(i->idx > 0); - - e = bucket_at(h, i->idx); - /* - * We allow removing the current entry while iterating, but removal may cause - * a backward shift. The next entry may thus move one bucket to the left. - * To detect when it happens, we remember the key pointer of the entry we were - * going to iterate next. If it does not match, there was a backward shift. - */ - if (e->key != i->next_key) - e = bucket_at(h, --i->idx); - - assert(e->key == i->next_key); - } - - idx = i->idx; -#if ENABLE_DEBUG_HASHMAP - i->prev_idx = idx; -#endif - - i->idx = skip_free_buckets(h, i->idx + 1); - if (i->idx != IDX_NIL) - i->next_key = bucket_at(h, i->idx)->key; - else - i->idx = IDX_NIL; - - return idx; - -at_end: - i->idx = IDX_NIL; - return IDX_NIL; -} - -static unsigned hashmap_iterate_entry(HashmapBase *h, Iterator *i) { - if (!h) { - i->idx = IDX_NIL; - return IDX_NIL; - } - -#if ENABLE_DEBUG_HASHMAP - if (i->idx == IDX_FIRST) { - i->put_count = h->debug.put_count; - i->rem_count = h->debug.rem_count; - } else { - /* While iterating, must not add any new entries */ - assert(i->put_count == h->debug.put_count); - /* ... or remove entries other than the current one */ - assert(i->rem_count == h->debug.rem_count || - (i->rem_count == h->debug.rem_count - 1 && - i->prev_idx == h->debug.last_rem_idx)); - /* Reset our removals counter */ - i->rem_count = h->debug.rem_count; - } -#endif - - return h->type == HASHMAP_TYPE_ORDERED ? hashmap_iterate_in_insertion_order((OrderedHashmap*) h, i) - : hashmap_iterate_in_internal_order(h, i); -} - -bool _hashmap_iterate(HashmapBase *h, Iterator *i, void **value, const void **key) { - struct hashmap_base_entry *e; - void *data; - unsigned idx; - - idx = hashmap_iterate_entry(h, i); - if (idx == IDX_NIL) { - if (value) - *value = NULL; - if (key) - *key = NULL; - - return false; - } - - e = bucket_at(h, idx); - data = entry_value(h, e); - if (value) - *value = data; - if (key) - *key = e->key; - - return true; -} - -#define HASHMAP_FOREACH_IDX(idx, h, i) \ - for ((i) = ITERATOR_FIRST, (idx) = hashmap_iterate_entry((h), &(i)); \ - (idx != IDX_NIL); \ - (idx) = hashmap_iterate_entry((h), &(i))) - -IteratedCache* _hashmap_iterated_cache_new(HashmapBase *h) { - IteratedCache *cache; - - assert(h); - assert(!h->cached); - - if (h->cached) - return NULL; - - cache = new0(IteratedCache, 1); - if (!cache) - return NULL; - - cache->hashmap = h; - h->cached = true; - - return cache; -} - -static void reset_direct_storage(HashmapBase *h) { - const struct hashmap_type_info *hi = &hashmap_type_info[h->type]; - void *p; - - assert(!h->has_indirect); - - p = mempset(h->direct.storage, 0, hi->entry_size * hi->n_direct_buckets); - memset(p, DIB_RAW_INIT, sizeof(dib_raw_t) * hi->n_direct_buckets); -} - -static void shared_hash_key_initialize(void) { - random_bytes(shared_hash_key, sizeof(shared_hash_key)); -} - -static struct HashmapBase* hashmap_base_new(const struct hash_ops *hash_ops, enum HashmapType type HASHMAP_DEBUG_PARAMS) { - HashmapBase *h; - const struct hashmap_type_info *hi = &hashmap_type_info[type]; - bool up; - - up = mempool_enabled(); - - h = up ? mempool_alloc0_tile(hi->mempool) : malloc0(hi->head_size); - if (!h) - return NULL; - - h->type = type; - h->from_pool = up; - h->hash_ops = hash_ops ?: &trivial_hash_ops; - - if (type == HASHMAP_TYPE_ORDERED) { - OrderedHashmap *lh = (OrderedHashmap*)h; - lh->iterate_list_head = lh->iterate_list_tail = IDX_NIL; - } - - reset_direct_storage(h); - - static pthread_once_t once = PTHREAD_ONCE_INIT; - assert_se(pthread_once(&once, shared_hash_key_initialize) == 0); - -#if ENABLE_DEBUG_HASHMAP - h->debug.func = func; - h->debug.file = file; - h->debug.line = line; - assert_se(pthread_mutex_lock(&hashmap_debug_list_mutex) == 0); - LIST_PREPEND(debug_list, hashmap_debug_list, &h->debug); - assert_se(pthread_mutex_unlock(&hashmap_debug_list_mutex) == 0); -#endif - - return h; -} - -Hashmap *_hashmap_new(const struct hash_ops *hash_ops HASHMAP_DEBUG_PARAMS) { - return (Hashmap*) hashmap_base_new(hash_ops, HASHMAP_TYPE_PLAIN HASHMAP_DEBUG_PASS_ARGS); -} - -OrderedHashmap *_ordered_hashmap_new(const struct hash_ops *hash_ops HASHMAP_DEBUG_PARAMS) { - return (OrderedHashmap*) hashmap_base_new(hash_ops, HASHMAP_TYPE_ORDERED HASHMAP_DEBUG_PASS_ARGS); -} - -Set *_set_new(const struct hash_ops *hash_ops HASHMAP_DEBUG_PARAMS) { - return (Set*) hashmap_base_new(hash_ops, HASHMAP_TYPE_SET HASHMAP_DEBUG_PASS_ARGS); -} - -static int hashmap_base_ensure_allocated(HashmapBase **h, const struct hash_ops *hash_ops, - enum HashmapType type HASHMAP_DEBUG_PARAMS) { - HashmapBase *q; - - assert(h); - - if (*h) - return 0; - - q = hashmap_base_new(hash_ops, type HASHMAP_DEBUG_PASS_ARGS); - if (!q) - return -ENOMEM; - - *h = q; - return 1; -} - -int _hashmap_ensure_allocated(Hashmap **h, const struct hash_ops *hash_ops HASHMAP_DEBUG_PARAMS) { - return hashmap_base_ensure_allocated((HashmapBase**)h, hash_ops, HASHMAP_TYPE_PLAIN HASHMAP_DEBUG_PASS_ARGS); -} - -int _ordered_hashmap_ensure_allocated(OrderedHashmap **h, const struct hash_ops *hash_ops HASHMAP_DEBUG_PARAMS) { - return hashmap_base_ensure_allocated((HashmapBase**)h, hash_ops, HASHMAP_TYPE_ORDERED HASHMAP_DEBUG_PASS_ARGS); -} - -int _set_ensure_allocated(Set **s, const struct hash_ops *hash_ops HASHMAP_DEBUG_PARAMS) { - return hashmap_base_ensure_allocated((HashmapBase**)s, hash_ops, HASHMAP_TYPE_SET HASHMAP_DEBUG_PASS_ARGS); -} - -int _ordered_hashmap_ensure_put(OrderedHashmap **h, const struct hash_ops *hash_ops, const void *key, void *value HASHMAP_DEBUG_PARAMS) { - int r; - - r = _ordered_hashmap_ensure_allocated(h, hash_ops HASHMAP_DEBUG_PASS_ARGS); - if (r < 0) - return r; - - return ordered_hashmap_put(*h, key, value); -} - -static void hashmap_free_no_clear(HashmapBase *h) { - assert(!h->has_indirect); - assert(h->n_direct_entries == 0); - -#if ENABLE_DEBUG_HASHMAP - assert_se(pthread_mutex_lock(&hashmap_debug_list_mutex) == 0); - LIST_REMOVE(debug_list, hashmap_debug_list, &h->debug); - assert_se(pthread_mutex_unlock(&hashmap_debug_list_mutex) == 0); -#endif - - if (h->from_pool) { - /* Ensure that the object didn't get migrated between threads. */ - assert_se(is_main_thread()); - mempool_free_tile(hashmap_type_info[h->type].mempool, h); - } else - free(h); -} - -HashmapBase* _hashmap_free(HashmapBase *h, free_func_t default_free_key, free_func_t default_free_value) { - if (h) { - _hashmap_clear(h, default_free_key, default_free_value); - hashmap_free_no_clear(h); - } - - return NULL; -} - -void _hashmap_clear(HashmapBase *h, free_func_t default_free_key, free_func_t default_free_value) { - free_func_t free_key, free_value; - if (!h) - return; - - free_key = h->hash_ops->free_key ?: default_free_key; - free_value = h->hash_ops->free_value ?: default_free_value; - - if (free_key || free_value) { - - /* If destructor calls are defined, let's destroy things defensively: let's take the item out of the - * hash table, and only then call the destructor functions. If these destructors then try to unregister - * themselves from our hash table a second time, the entry is already gone. */ - - while (_hashmap_size(h) > 0) { - void *k = NULL; - void *v; - - v = _hashmap_first_key_and_value(h, true, &k); - - if (free_key) - free_key(k); - - if (free_value) - free_value(v); - } - } - - if (h->has_indirect) { - free(h->indirect.storage); - h->has_indirect = false; - } - - h->n_direct_entries = 0; - reset_direct_storage(h); - - if (h->type == HASHMAP_TYPE_ORDERED) { - OrderedHashmap *lh = (OrderedHashmap*) h; - lh->iterate_list_head = lh->iterate_list_tail = IDX_NIL; - } - - base_set_dirty(h); -} - -static int resize_buckets(HashmapBase *h, unsigned entries_add); - -/* - * Finds an empty bucket to put an entry into, starting the scan at 'idx'. - * Performs Robin Hood swaps as it goes. The entry to put must be placed - * by the caller into swap slot IDX_PUT. - * If used for in-place resizing, may leave a displaced entry in swap slot - * IDX_PUT. Caller must rehash it next. - * Returns: true if it left a displaced entry to rehash next in IDX_PUT, - * false otherwise. - */ -static bool hashmap_put_robin_hood(HashmapBase *h, unsigned idx, - struct swap_entries *swap) { - dib_raw_t raw_dib, *dibs; - unsigned dib, distance; - -#if ENABLE_DEBUG_HASHMAP - h->debug.put_count++; -#endif - - dibs = dib_raw_ptr(h); - - for (distance = 0; ; distance++) { - raw_dib = dibs[idx]; - if (IN_SET(raw_dib, DIB_RAW_FREE, DIB_RAW_REHASH)) { - if (raw_dib == DIB_RAW_REHASH) - bucket_move_entry(h, swap, idx, IDX_TMP); - - if (h->has_indirect && h->indirect.idx_lowest_entry > idx) - h->indirect.idx_lowest_entry = idx; - - bucket_set_dib(h, idx, distance); - bucket_move_entry(h, swap, IDX_PUT, idx); - if (raw_dib == DIB_RAW_REHASH) { - bucket_move_entry(h, swap, IDX_TMP, IDX_PUT); - return true; - } - - return false; - } - - dib = bucket_calculate_dib(h, idx, raw_dib); - - if (dib < distance) { - /* Found a wealthier entry. Go Robin Hood! */ - bucket_set_dib(h, idx, distance); - - /* swap the entries */ - bucket_move_entry(h, swap, idx, IDX_TMP); - bucket_move_entry(h, swap, IDX_PUT, idx); - bucket_move_entry(h, swap, IDX_TMP, IDX_PUT); - - distance = dib; - } - - idx = next_idx(h, idx); - } -} - -/* - * Puts an entry into a hashmap, boldly - no check whether key already exists. - * The caller must place the entry (only its key and value, not link indexes) - * in swap slot IDX_PUT. - * Caller must ensure: the key does not exist yet in the hashmap. - * that resize is not needed if !may_resize. - * Returns: 1 if entry was put successfully. - * -ENOMEM if may_resize==true and resize failed with -ENOMEM. - * Cannot return -ENOMEM if !may_resize. - */ -static int hashmap_base_put_boldly(HashmapBase *h, unsigned idx, - struct swap_entries *swap, bool may_resize) { - struct ordered_hashmap_entry *new_entry; - int r; - - assert(idx < n_buckets(h)); - - new_entry = bucket_at_swap(swap, IDX_PUT); - - if (may_resize) { - r = resize_buckets(h, 1); - if (r < 0) - return r; - if (r > 0) - idx = bucket_hash(h, new_entry->p.b.key); - } - assert(n_entries(h) < n_buckets(h)); - - if (h->type == HASHMAP_TYPE_ORDERED) { - OrderedHashmap *lh = (OrderedHashmap*) h; - - new_entry->iterate_next = IDX_NIL; - new_entry->iterate_previous = lh->iterate_list_tail; - - if (lh->iterate_list_tail != IDX_NIL) { - struct ordered_hashmap_entry *old_tail; - - old_tail = ordered_bucket_at(lh, lh->iterate_list_tail); - assert(old_tail->iterate_next == IDX_NIL); - old_tail->iterate_next = IDX_PUT; - } - - lh->iterate_list_tail = IDX_PUT; - if (lh->iterate_list_head == IDX_NIL) - lh->iterate_list_head = IDX_PUT; - } - - assert_se(hashmap_put_robin_hood(h, idx, swap) == false); - - n_entries_inc(h); -#if ENABLE_DEBUG_HASHMAP - h->debug.max_entries = MAX(h->debug.max_entries, n_entries(h)); -#endif - - base_set_dirty(h); - - return 1; -} -#define hashmap_put_boldly(h, idx, swap, may_resize) \ - hashmap_base_put_boldly(HASHMAP_BASE(h), idx, swap, may_resize) - -/* - * Returns 0 if resize is not needed. - * 1 if successfully resized. - * -ENOMEM on allocation failure. - */ -static int resize_buckets(HashmapBase *h, unsigned entries_add) { - struct swap_entries swap; - void *new_storage; - dib_raw_t *old_dibs, *new_dibs; - const struct hashmap_type_info *hi; - unsigned idx, optimal_idx; - unsigned old_n_buckets, new_n_buckets, n_rehashed, new_n_entries; - uint8_t new_shift; - bool rehash_next; - - assert(h); - - hi = &hashmap_type_info[h->type]; - new_n_entries = n_entries(h) + entries_add; - - /* overflow? */ - if (_unlikely_(new_n_entries < entries_add)) - return -ENOMEM; - - /* For direct storage we allow 100% load, because it's tiny. */ - if (!h->has_indirect && new_n_entries <= hi->n_direct_buckets) - return 0; - - /* - * Load factor = n/m = 1 - (1/INV_KEEP_FREE). - * From it follows: m = n + n/(INV_KEEP_FREE - 1) - */ - new_n_buckets = new_n_entries + new_n_entries / (INV_KEEP_FREE - 1); - /* overflow? */ - if (_unlikely_(new_n_buckets < new_n_entries)) - return -ENOMEM; - - if (_unlikely_(new_n_buckets > UINT_MAX / (hi->entry_size + sizeof(dib_raw_t)))) - return -ENOMEM; - - old_n_buckets = n_buckets(h); - - if (_likely_(new_n_buckets <= old_n_buckets)) - return 0; - - new_shift = log2u_round_up(MAX( - new_n_buckets * (hi->entry_size + sizeof(dib_raw_t)), - 2 * sizeof(struct direct_storage))); - - /* Realloc storage (buckets and DIB array). */ - new_storage = realloc(h->has_indirect ? h->indirect.storage : NULL, - 1U << new_shift); - if (!new_storage) - return -ENOMEM; - - /* Must upgrade direct to indirect storage. */ - if (!h->has_indirect) { - memcpy(new_storage, h->direct.storage, - old_n_buckets * (hi->entry_size + sizeof(dib_raw_t))); - h->indirect.n_entries = h->n_direct_entries; - h->indirect.idx_lowest_entry = 0; - h->n_direct_entries = 0; - } - - /* Get a new hash key. If we've just upgraded to indirect storage, - * allow reusing a previously generated key. It's still a different key - * from the shared one that we used for direct storage. */ - get_hash_key(h->indirect.hash_key, !h->has_indirect); - - h->has_indirect = true; - h->indirect.storage = new_storage; - h->indirect.n_buckets = (1U << new_shift) / - (hi->entry_size + sizeof(dib_raw_t)); - - old_dibs = (dib_raw_t*)((uint8_t*) new_storage + hi->entry_size * old_n_buckets); - new_dibs = dib_raw_ptr(h); - - /* - * Move the DIB array to the new place, replacing valid DIB values with - * DIB_RAW_REHASH to indicate all of the used buckets need rehashing. - * Note: Overlap is not possible, because we have at least doubled the - * number of buckets and dib_raw_t is smaller than any entry type. - */ - for (idx = 0; idx < old_n_buckets; idx++) { - assert(old_dibs[idx] != DIB_RAW_REHASH); - new_dibs[idx] = old_dibs[idx] == DIB_RAW_FREE ? DIB_RAW_FREE - : DIB_RAW_REHASH; - } - - /* Zero the area of newly added entries (including the old DIB area) */ - memzero(bucket_at(h, old_n_buckets), - (n_buckets(h) - old_n_buckets) * hi->entry_size); - - /* The upper half of the new DIB array needs initialization */ - memset(&new_dibs[old_n_buckets], DIB_RAW_INIT, - (n_buckets(h) - old_n_buckets) * sizeof(dib_raw_t)); - - /* Rehash entries that need it */ - n_rehashed = 0; - for (idx = 0; idx < old_n_buckets; idx++) { - if (new_dibs[idx] != DIB_RAW_REHASH) - continue; - - optimal_idx = bucket_hash(h, bucket_at(h, idx)->key); - - /* - * Not much to do if by luck the entry hashes to its current - * location. Just set its DIB. - */ - if (optimal_idx == idx) { - new_dibs[idx] = 0; - n_rehashed++; - continue; - } - - new_dibs[idx] = DIB_RAW_FREE; - bucket_move_entry(h, &swap, idx, IDX_PUT); - /* bucket_move_entry does not clear the source */ - memzero(bucket_at(h, idx), hi->entry_size); - - do { - /* - * Find the new bucket for the current entry. This may make - * another entry homeless and load it into IDX_PUT. - */ - rehash_next = hashmap_put_robin_hood(h, optimal_idx, &swap); - n_rehashed++; - - /* Did the current entry displace another one? */ - if (rehash_next) - optimal_idx = bucket_hash(h, bucket_at_swap(&swap, IDX_PUT)->p.b.key); - } while (rehash_next); - } - - assert(n_rehashed == n_entries(h)); - - return 1; -} - -/* - * Finds an entry with a matching key - * Returns: index of the found entry, or IDX_NIL if not found. - */ -static unsigned base_bucket_scan(HashmapBase *h, unsigned idx, const void *key) { - struct hashmap_base_entry *e; - unsigned dib, distance; - dib_raw_t *dibs = dib_raw_ptr(h); - - assert(idx < n_buckets(h)); - - for (distance = 0; ; distance++) { - if (dibs[idx] == DIB_RAW_FREE) - return IDX_NIL; - - dib = bucket_calculate_dib(h, idx, dibs[idx]); - - if (dib < distance) - return IDX_NIL; - if (dib == distance) { - e = bucket_at(h, idx); - if (h->hash_ops->compare(e->key, key) == 0) - return idx; - } - - idx = next_idx(h, idx); - } -} -#define bucket_scan(h, idx, key) base_bucket_scan(HASHMAP_BASE(h), idx, key) - -int hashmap_put(Hashmap *h, const void *key, void *value) { - struct swap_entries swap; - struct plain_hashmap_entry *e; - unsigned hash, idx; - - assert(h); - - hash = bucket_hash(h, key); - idx = bucket_scan(h, hash, key); - if (idx != IDX_NIL) { - e = plain_bucket_at(h, idx); - if (e->value == value) - return 0; - return -EEXIST; - } - - e = &bucket_at_swap(&swap, IDX_PUT)->p; - e->b.key = key; - e->value = value; - return hashmap_put_boldly(h, hash, &swap, true); -} - -int set_put(Set *s, const void *key) { - struct swap_entries swap; - struct hashmap_base_entry *e; - unsigned hash, idx; - - assert(s); - - hash = bucket_hash(s, key); - idx = bucket_scan(s, hash, key); - if (idx != IDX_NIL) - return 0; - - e = &bucket_at_swap(&swap, IDX_PUT)->p.b; - e->key = key; - return hashmap_put_boldly(s, hash, &swap, true); -} - -int _set_ensure_put(Set **s, const struct hash_ops *hash_ops, const void *key HASHMAP_DEBUG_PARAMS) { - int r; - - r = _set_ensure_allocated(s, hash_ops HASHMAP_DEBUG_PASS_ARGS); - if (r < 0) - return r; - - return set_put(*s, key); -} - -int _set_ensure_consume(Set **s, const struct hash_ops *hash_ops, void *key HASHMAP_DEBUG_PARAMS) { - int r; - - r = _set_ensure_put(s, hash_ops, key HASHMAP_DEBUG_PASS_ARGS); - if (r <= 0) { - if (hash_ops && hash_ops->free_key) - hash_ops->free_key(key); - else - free(key); - } - - return r; -} - -int hashmap_replace(Hashmap *h, const void *key, void *value) { - struct swap_entries swap; - struct plain_hashmap_entry *e; - unsigned hash, idx; - - assert(h); - - hash = bucket_hash(h, key); - idx = bucket_scan(h, hash, key); - if (idx != IDX_NIL) { - e = plain_bucket_at(h, idx); -#if ENABLE_DEBUG_HASHMAP - /* Although the key is equal, the key pointer may have changed, - * and this would break our assumption for iterating. So count - * this operation as incompatible with iteration. */ - if (e->b.key != key) { - h->b.debug.put_count++; - h->b.debug.rem_count++; - h->b.debug.last_rem_idx = idx; - } -#endif - e->b.key = key; - e->value = value; - hashmap_set_dirty(h); - - return 0; - } - - e = &bucket_at_swap(&swap, IDX_PUT)->p; - e->b.key = key; - e->value = value; - return hashmap_put_boldly(h, hash, &swap, true); -} - -int hashmap_update(Hashmap *h, const void *key, void *value) { - struct plain_hashmap_entry *e; - unsigned hash, idx; - - assert(h); - - hash = bucket_hash(h, key); - idx = bucket_scan(h, hash, key); - if (idx == IDX_NIL) - return -ENOENT; - - e = plain_bucket_at(h, idx); - e->value = value; - hashmap_set_dirty(h); - - return 0; -} - -void* _hashmap_get(HashmapBase *h, const void *key) { - struct hashmap_base_entry *e; - unsigned hash, idx; - - if (!h) - return NULL; - - hash = bucket_hash(h, key); - idx = bucket_scan(h, hash, key); - if (idx == IDX_NIL) - return NULL; - - e = bucket_at(h, idx); - return entry_value(h, e); -} - -void* hashmap_get2(Hashmap *h, const void *key, void **key2) { - struct plain_hashmap_entry *e; - unsigned hash, idx; - - if (!h) - return NULL; - - hash = bucket_hash(h, key); - idx = bucket_scan(h, hash, key); - if (idx == IDX_NIL) - return NULL; - - e = plain_bucket_at(h, idx); - if (key2) - *key2 = (void*) e->b.key; - - return e->value; -} - -bool _hashmap_contains(HashmapBase *h, const void *key) { - unsigned hash; - - if (!h) - return false; - - hash = bucket_hash(h, key); - return bucket_scan(h, hash, key) != IDX_NIL; -} - -void* _hashmap_remove(HashmapBase *h, const void *key) { - struct hashmap_base_entry *e; - unsigned hash, idx; - void *data; - - if (!h) - return NULL; - - hash = bucket_hash(h, key); - idx = bucket_scan(h, hash, key); - if (idx == IDX_NIL) - return NULL; - - e = bucket_at(h, idx); - data = entry_value(h, e); - remove_entry(h, idx); - - return data; -} - -void* hashmap_remove2(Hashmap *h, const void *key, void **rkey) { - struct plain_hashmap_entry *e; - unsigned hash, idx; - void *data; - - if (!h) { - if (rkey) - *rkey = NULL; - return NULL; - } - - hash = bucket_hash(h, key); - idx = bucket_scan(h, hash, key); - if (idx == IDX_NIL) { - if (rkey) - *rkey = NULL; - return NULL; - } - - e = plain_bucket_at(h, idx); - data = e->value; - if (rkey) - *rkey = (void*) e->b.key; - - remove_entry(h, idx); - - return data; -} - -int hashmap_remove_and_put(Hashmap *h, const void *old_key, const void *new_key, void *value) { - struct swap_entries swap; - struct plain_hashmap_entry *e; - unsigned old_hash, new_hash, idx; - - if (!h) - return -ENOENT; - - old_hash = bucket_hash(h, old_key); - idx = bucket_scan(h, old_hash, old_key); - if (idx == IDX_NIL) - return -ENOENT; - - new_hash = bucket_hash(h, new_key); - if (bucket_scan(h, new_hash, new_key) != IDX_NIL) - return -EEXIST; - - remove_entry(h, idx); - - e = &bucket_at_swap(&swap, IDX_PUT)->p; - e->b.key = new_key; - e->value = value; - assert_se(hashmap_put_boldly(h, new_hash, &swap, false) == 1); - - return 0; -} - -int set_remove_and_put(Set *s, const void *old_key, const void *new_key) { - struct swap_entries swap; - struct hashmap_base_entry *e; - unsigned old_hash, new_hash, idx; - - if (!s) - return -ENOENT; - - old_hash = bucket_hash(s, old_key); - idx = bucket_scan(s, old_hash, old_key); - if (idx == IDX_NIL) - return -ENOENT; - - new_hash = bucket_hash(s, new_key); - if (bucket_scan(s, new_hash, new_key) != IDX_NIL) - return -EEXIST; - - remove_entry(s, idx); - - e = &bucket_at_swap(&swap, IDX_PUT)->p.b; - e->key = new_key; - assert_se(hashmap_put_boldly(s, new_hash, &swap, false) == 1); - - return 0; -} - -int hashmap_remove_and_replace(Hashmap *h, const void *old_key, const void *new_key, void *value) { - struct swap_entries swap; - struct plain_hashmap_entry *e; - unsigned old_hash, new_hash, idx_old, idx_new; - - if (!h) - return -ENOENT; - - old_hash = bucket_hash(h, old_key); - idx_old = bucket_scan(h, old_hash, old_key); - if (idx_old == IDX_NIL) - return -ENOENT; - - old_key = bucket_at(HASHMAP_BASE(h), idx_old)->key; - - new_hash = bucket_hash(h, new_key); - idx_new = bucket_scan(h, new_hash, new_key); - if (idx_new != IDX_NIL) - if (idx_old != idx_new) { - remove_entry(h, idx_new); - /* Compensate for a possible backward shift. */ - if (old_key != bucket_at(HASHMAP_BASE(h), idx_old)->key) - idx_old = prev_idx(HASHMAP_BASE(h), idx_old); - assert(old_key == bucket_at(HASHMAP_BASE(h), idx_old)->key); - } - - remove_entry(h, idx_old); - - e = &bucket_at_swap(&swap, IDX_PUT)->p; - e->b.key = new_key; - e->value = value; - assert_se(hashmap_put_boldly(h, new_hash, &swap, false) == 1); - - return 0; -} - -void* _hashmap_remove_value(HashmapBase *h, const void *key, void *value) { - struct hashmap_base_entry *e; - unsigned hash, idx; - - if (!h) - return NULL; - - hash = bucket_hash(h, key); - idx = bucket_scan(h, hash, key); - if (idx == IDX_NIL) - return NULL; - - e = bucket_at(h, idx); - if (entry_value(h, e) != value) - return NULL; - - remove_entry(h, idx); - - return value; -} - -static unsigned find_first_entry(HashmapBase *h) { - Iterator i = ITERATOR_FIRST; - - if (!h || !n_entries(h)) - return IDX_NIL; - - return hashmap_iterate_entry(h, &i); -} - -void* _hashmap_first_key_and_value(HashmapBase *h, bool remove, void **ret_key) { - struct hashmap_base_entry *e; - void *key, *data; - unsigned idx; - - idx = find_first_entry(h); - if (idx == IDX_NIL) { - if (ret_key) - *ret_key = NULL; - return NULL; - } - - e = bucket_at(h, idx); - key = (void*) e->key; - data = entry_value(h, e); - - if (remove) - remove_entry(h, idx); - - if (ret_key) - *ret_key = key; - - return data; -} - -unsigned _hashmap_size(HashmapBase *h) { - if (!h) - return 0; - - return n_entries(h); -} - -unsigned _hashmap_buckets(HashmapBase *h) { - if (!h) - return 0; - - return n_buckets(h); -} - -int _hashmap_merge(Hashmap *h, Hashmap *other) { - Iterator i; - unsigned idx; - - assert(h); - - HASHMAP_FOREACH_IDX(idx, HASHMAP_BASE(other), i) { - struct plain_hashmap_entry *pe = plain_bucket_at(other, idx); - int r; - - r = hashmap_put(h, pe->b.key, pe->value); - if (r < 0 && r != -EEXIST) - return r; - } - - return 0; -} - -int set_merge(Set *s, Set *other) { - Iterator i; - unsigned idx; - - assert(s); - - HASHMAP_FOREACH_IDX(idx, HASHMAP_BASE(other), i) { - struct set_entry *se = set_bucket_at(other, idx); - int r; - - r = set_put(s, se->b.key); - if (r < 0) - return r; - } - - return 0; -} - -int _hashmap_reserve(HashmapBase *h, unsigned entries_add) { - int r; - - assert(h); - - r = resize_buckets(h, entries_add); - if (r < 0) - return r; - - return 0; -} - -/* - * The same as hashmap_merge(), but every new item from other is moved to h. - * Keys already in h are skipped and stay in other. - * Returns: 0 on success. - * -ENOMEM on alloc failure, in which case no move has been done. - */ -int _hashmap_move(HashmapBase *h, HashmapBase *other) { - struct swap_entries swap; - struct hashmap_base_entry *e, *n; - Iterator i; - unsigned idx; - int r; - - assert(h); - - if (!other) - return 0; - - assert(other->type == h->type); - - /* - * This reserves buckets for the worst case, where none of other's - * entries are yet present in h. This is preferable to risking - * an allocation failure in the middle of the moving and having to - * rollback or return a partial result. - */ - r = resize_buckets(h, n_entries(other)); - if (r < 0) - return r; - - HASHMAP_FOREACH_IDX(idx, other, i) { - unsigned h_hash; - - e = bucket_at(other, idx); - h_hash = bucket_hash(h, e->key); - if (bucket_scan(h, h_hash, e->key) != IDX_NIL) - continue; - - n = &bucket_at_swap(&swap, IDX_PUT)->p.b; - n->key = e->key; - if (h->type != HASHMAP_TYPE_SET) - ((struct plain_hashmap_entry*) n)->value = - ((struct plain_hashmap_entry*) e)->value; - assert_se(hashmap_put_boldly(h, h_hash, &swap, false) == 1); - - remove_entry(other, idx); - } - - return 0; -} - -int _hashmap_move_one(HashmapBase *h, HashmapBase *other, const void *key) { - struct swap_entries swap; - unsigned h_hash, other_hash, idx; - struct hashmap_base_entry *e, *n; - int r; - - assert(h); - - h_hash = bucket_hash(h, key); - if (bucket_scan(h, h_hash, key) != IDX_NIL) - return -EEXIST; - - if (!other) - return -ENOENT; - - assert(other->type == h->type); - - other_hash = bucket_hash(other, key); - idx = bucket_scan(other, other_hash, key); - if (idx == IDX_NIL) - return -ENOENT; - - e = bucket_at(other, idx); - - n = &bucket_at_swap(&swap, IDX_PUT)->p.b; - n->key = e->key; - if (h->type != HASHMAP_TYPE_SET) - ((struct plain_hashmap_entry*) n)->value = - ((struct plain_hashmap_entry*) e)->value; - r = hashmap_put_boldly(h, h_hash, &swap, true); - if (r < 0) - return r; - - remove_entry(other, idx); - return 0; -} - -HashmapBase* _hashmap_copy(HashmapBase *h HASHMAP_DEBUG_PARAMS) { - HashmapBase *copy; - int r; - - assert(h); - - copy = hashmap_base_new(h->hash_ops, h->type HASHMAP_DEBUG_PASS_ARGS); - if (!copy) - return NULL; - - switch (h->type) { - case HASHMAP_TYPE_PLAIN: - case HASHMAP_TYPE_ORDERED: - r = hashmap_merge((Hashmap*)copy, (Hashmap*)h); - break; - case HASHMAP_TYPE_SET: - r = set_merge((Set*)copy, (Set*)h); - break; - default: - assert_not_reached("Unknown hashmap type"); - } - - if (r < 0) - return _hashmap_free(copy, false, false); - - return copy; -} - -char** _hashmap_get_strv(HashmapBase *h) { - char **sv; - Iterator i; - unsigned idx, n; - - sv = new(char*, n_entries(h)+1); - if (!sv) - return NULL; - - n = 0; - HASHMAP_FOREACH_IDX(idx, h, i) - sv[n++] = entry_value(h, bucket_at(h, idx)); - sv[n] = NULL; - - return sv; -} - -void* ordered_hashmap_next(OrderedHashmap *h, const void *key) { - struct ordered_hashmap_entry *e; - unsigned hash, idx; - - if (!h) - return NULL; - - hash = bucket_hash(h, key); - idx = bucket_scan(h, hash, key); - if (idx == IDX_NIL) - return NULL; - - e = ordered_bucket_at(h, idx); - if (e->iterate_next == IDX_NIL) - return NULL; - return ordered_bucket_at(h, e->iterate_next)->p.value; -} - -int set_consume(Set *s, void *value) { - int r; - - assert(s); - assert(value); - - r = set_put(s, value); - if (r <= 0) - free(value); - - return r; -} - -#if 0 /* NM_IGNORED */ -int _hashmap_put_strdup_full(Hashmap **h, const struct hash_ops *hash_ops, const char *k, const char *v HASHMAP_DEBUG_PARAMS) { - int r; - - r = _hashmap_ensure_allocated(h, hash_ops HASHMAP_DEBUG_PASS_ARGS); - if (r < 0) - return r; - - _cleanup_free_ char *kdup = NULL, *vdup = NULL; - - kdup = strdup(k); - if (!kdup) - return -ENOMEM; - - if (v) { - vdup = strdup(v); - if (!vdup) - return -ENOMEM; - } - - r = hashmap_put(*h, kdup, vdup); - if (r < 0) { - if (r == -EEXIST && streq_ptr(v, hashmap_get(*h, kdup))) - return 0; - return r; - } - - /* 0 with non-null vdup would mean vdup is already in the hashmap, which cannot be */ - assert(vdup == NULL || r > 0); - if (r > 0) - kdup = vdup = NULL; - - return r; -} -#endif /* NM_IGNORED */ - -int _set_put_strdup_full(Set **s, const struct hash_ops *hash_ops, const char *p HASHMAP_DEBUG_PARAMS) { - char *c; - int r; - - assert(s); - assert(p); - - r = _set_ensure_allocated(s, hash_ops HASHMAP_DEBUG_PASS_ARGS); - if (r < 0) - return r; - - if (set_contains(*s, (char*) p)) - return 0; - - c = strdup(p); - if (!c) - return -ENOMEM; - - return set_consume(*s, c); -} - -int _set_put_strdupv_full(Set **s, const struct hash_ops *hash_ops, char **l HASHMAP_DEBUG_PARAMS) { - int n = 0, r; - char **i; - - assert(s); - - STRV_FOREACH(i, l) { - r = _set_put_strdup_full(s, hash_ops, *i HASHMAP_DEBUG_PASS_ARGS); - if (r < 0) - return r; - - n += r; - } - - return n; -} - -int set_put_strsplit(Set *s, const char *v, const char *separators, ExtractFlags flags) { - const char *p = v; - int r; - - assert(s); - assert(v); - - for (;;) { - char *word; - - r = extract_first_word(&p, &word, separators, flags); - if (r <= 0) - return r; - - r = set_consume(s, word); - if (r < 0) - return r; - } -} - -/* expand the cachemem if needed, return true if newly (re)activated. */ -static int cachemem_maintain(CacheMem *mem, unsigned size) { - assert(mem); - - if (!GREEDY_REALLOC(mem->ptr, mem->n_allocated, size)) { - if (size > 0) - return -ENOMEM; - } - - if (!mem->active) { - mem->active = true; - return true; - } - - return false; -} - -int iterated_cache_get(IteratedCache *cache, const void ***res_keys, const void ***res_values, unsigned *res_n_entries) { - bool sync_keys = false, sync_values = false; - unsigned size; - int r; - - assert(cache); - assert(cache->hashmap); - - size = n_entries(cache->hashmap); - - if (res_keys) { - r = cachemem_maintain(&cache->keys, size); - if (r < 0) - return r; - - sync_keys = r; - } else - cache->keys.active = false; - - if (res_values) { - r = cachemem_maintain(&cache->values, size); - if (r < 0) - return r; - - sync_values = r; - } else - cache->values.active = false; - - if (cache->hashmap->dirty) { - if (cache->keys.active) - sync_keys = true; - if (cache->values.active) - sync_values = true; - - cache->hashmap->dirty = false; - } - - if (sync_keys || sync_values) { - unsigned i, idx; - Iterator iter; - - i = 0; - HASHMAP_FOREACH_IDX(idx, cache->hashmap, iter) { - struct hashmap_base_entry *e; - - e = bucket_at(cache->hashmap, idx); - - if (sync_keys) - cache->keys.ptr[i] = e->key; - if (sync_values) - cache->values.ptr[i] = entry_value(cache->hashmap, e); - i++; - } - } - - if (res_keys) - *res_keys = cache->keys.ptr; - if (res_values) - *res_values = cache->values.ptr; - if (res_n_entries) - *res_n_entries = size; - - return 0; -} - -IteratedCache* iterated_cache_free(IteratedCache *cache) { - if (cache) { - free(cache->keys.ptr); - free(cache->values.ptr); - } - - return mfree(cache); -} - -int set_strjoin(Set *s, const char *separator, bool wrap_with_separator, char **ret) { - size_t separator_len, allocated = 0, len = 0; - _cleanup_free_ char *str = NULL; - const char *value; - bool first; - - assert(ret); - - if (set_isempty(s)) { - *ret = NULL; - return 0; - } - - separator_len = strlen_ptr(separator); - - if (separator_len == 0) - wrap_with_separator = false; - - first = !wrap_with_separator; - - SET_FOREACH(value, s) { - size_t l = strlen_ptr(value); - - if (l == 0) - continue; - - if (!GREEDY_REALLOC(str, allocated, len + l + (first ? 0 : separator_len) + (wrap_with_separator ? separator_len : 0) + 1)) - return -ENOMEM; - - if (separator_len > 0 && !first) { - memcpy(str + len, separator, separator_len); - len += separator_len; - } - - memcpy(str + len, value, l); - len += l; - first = false; - } - - if (wrap_with_separator) { - memcpy(str + len, separator, separator_len); - len += separator_len; - } - - str[len] = '\0'; - - *ret = TAKE_PTR(str); - return 0; -} diff --git a/shared/systemd/src/basic/hashmap.h b/shared/systemd/src/basic/hashmap.h deleted file mode 100644 index e9944837..00000000 --- a/shared/systemd/src/basic/hashmap.h +++ /dev/null @@ -1,449 +0,0 @@ -/* SPDX-License-Identifier: LGPL-2.1-or-later */ -#pragma once - -#include <limits.h> -#include <stdbool.h> -#include <stddef.h> - -#include "hash-funcs.h" -#include "macro.h" -#include "util.h" - -/* - * A hash table implementation. As a minor optimization a NULL hashmap object - * will be treated as empty hashmap for all read operations. That way it is not - * necessary to instantiate an object for each Hashmap use. - * - * If ENABLE_DEBUG_HASHMAP is defined (by configuring with -Ddebug-extra=hashmap), - * the implementation will: - * - store extra data for debugging and statistics (see tools/gdb-sd_dump_hashmaps.py) - * - perform extra checks for invalid use of iterators - */ - -#define HASH_KEY_SIZE 16 - -typedef void* (*hashmap_destroy_t)(void *p); - -/* The base type for all hashmap and set types. Many functions in the implementation take (HashmapBase*) - * parameters and are run-time polymorphic, though the API is not meant to be polymorphic (do not call - * underscore-prefixed functions directly). */ -typedef struct HashmapBase HashmapBase; - -/* Specific hashmap/set types */ -typedef struct Hashmap Hashmap; /* Maps keys to values */ -typedef struct OrderedHashmap OrderedHashmap; /* Like Hashmap, but also remembers entry insertion order */ -typedef struct Set Set; /* Stores just keys */ - -typedef struct IteratedCache IteratedCache; /* Caches the iterated order of one of the above */ - -/* Ideally the Iterator would be an opaque struct, but it is instantiated - * by hashmap users, so the definition has to be here. Do not use its fields - * directly. */ -typedef struct { - unsigned idx; /* index of an entry to be iterated next */ - const void *next_key; /* expected value of that entry's key pointer */ -#if ENABLE_DEBUG_HASHMAP - unsigned put_count; /* hashmap's put_count recorded at start of iteration */ - unsigned rem_count; /* hashmap's rem_count in previous iteration */ - unsigned prev_idx; /* idx in previous iteration */ -#endif -} Iterator; - -#define _IDX_ITERATOR_FIRST (UINT_MAX - 1) -#define ITERATOR_FIRST ((Iterator) { .idx = _IDX_ITERATOR_FIRST, .next_key = NULL }) - -/* Macros for type checking */ -#define PTR_COMPATIBLE_WITH_HASHMAP_BASE(h) \ - (__builtin_types_compatible_p(typeof(h), HashmapBase*) || \ - __builtin_types_compatible_p(typeof(h), Hashmap*) || \ - __builtin_types_compatible_p(typeof(h), OrderedHashmap*) || \ - __builtin_types_compatible_p(typeof(h), Set*)) - -#define PTR_COMPATIBLE_WITH_PLAIN_HASHMAP(h) \ - (__builtin_types_compatible_p(typeof(h), Hashmap*) || \ - __builtin_types_compatible_p(typeof(h), OrderedHashmap*)) \ - -#define HASHMAP_BASE(h) \ - __builtin_choose_expr(PTR_COMPATIBLE_WITH_HASHMAP_BASE(h), \ - (HashmapBase*)(h), \ - (void)0) - -#define PLAIN_HASHMAP(h) \ - __builtin_choose_expr(PTR_COMPATIBLE_WITH_PLAIN_HASHMAP(h), \ - (Hashmap*)(h), \ - (void)0) - -#if ENABLE_DEBUG_HASHMAP -# define HASHMAP_DEBUG_PARAMS , const char *func, const char *file, int line -# define HASHMAP_DEBUG_SRC_ARGS , __func__, PROJECT_FILE, __LINE__ -# define HASHMAP_DEBUG_PASS_ARGS , func, file, line -#else -# define HASHMAP_DEBUG_PARAMS -# define HASHMAP_DEBUG_SRC_ARGS -# define HASHMAP_DEBUG_PASS_ARGS -#endif - -Hashmap* _hashmap_new(const struct hash_ops *hash_ops HASHMAP_DEBUG_PARAMS); -OrderedHashmap* _ordered_hashmap_new(const struct hash_ops *hash_ops HASHMAP_DEBUG_PARAMS); -#define hashmap_new(ops) _hashmap_new(ops HASHMAP_DEBUG_SRC_ARGS) -#define ordered_hashmap_new(ops) _ordered_hashmap_new(ops HASHMAP_DEBUG_SRC_ARGS) - -#define hashmap_free_and_replace(a, b) \ - ({ \ - hashmap_free(a); \ - (a) = (b); \ - (b) = NULL; \ - 0; \ - }) - -HashmapBase* _hashmap_free(HashmapBase *h, free_func_t default_free_key, free_func_t default_free_value); -static inline Hashmap* hashmap_free(Hashmap *h) { - return (void*) _hashmap_free(HASHMAP_BASE(h), NULL, NULL); -} -static inline OrderedHashmap* ordered_hashmap_free(OrderedHashmap *h) { - return (void*) _hashmap_free(HASHMAP_BASE(h), NULL, NULL); -} - -static inline Hashmap* hashmap_free_free(Hashmap *h) { - return (void*) _hashmap_free(HASHMAP_BASE(h), NULL, free); -} -static inline OrderedHashmap* ordered_hashmap_free_free(OrderedHashmap *h) { - return (void*) _hashmap_free(HASHMAP_BASE(h), NULL, free); -} - -static inline Hashmap* hashmap_free_free_key(Hashmap *h) { - return (void*) _hashmap_free(HASHMAP_BASE(h), free, NULL); -} -static inline OrderedHashmap* ordered_hashmap_free_free_key(OrderedHashmap *h) { - return (void*) _hashmap_free(HASHMAP_BASE(h), free, NULL); -} - -static inline Hashmap* hashmap_free_free_free(Hashmap *h) { - return (void*) _hashmap_free(HASHMAP_BASE(h), free, free); -} -static inline OrderedHashmap* ordered_hashmap_free_free_free(OrderedHashmap *h) { - return (void*) _hashmap_free(HASHMAP_BASE(h), free, free); -} - -IteratedCache* iterated_cache_free(IteratedCache *cache); -int iterated_cache_get(IteratedCache *cache, const void ***res_keys, const void ***res_values, unsigned *res_n_entries); - -HashmapBase* _hashmap_copy(HashmapBase *h HASHMAP_DEBUG_PARAMS); -#define hashmap_copy(h) ((Hashmap*) _hashmap_copy(HASHMAP_BASE(h) HASHMAP_DEBUG_SRC_ARGS)) -#define ordered_hashmap_copy(h) ((OrderedHashmap*) _hashmap_copy(HASHMAP_BASE(h) HASHMAP_DEBUG_SRC_ARGS)) - -int _hashmap_ensure_allocated(Hashmap **h, const struct hash_ops *hash_ops HASHMAP_DEBUG_PARAMS); -int _ordered_hashmap_ensure_allocated(OrderedHashmap **h, const struct hash_ops *hash_ops HASHMAP_DEBUG_PARAMS); -#define hashmap_ensure_allocated(h, ops) _hashmap_ensure_allocated(h, ops HASHMAP_DEBUG_SRC_ARGS) -#define ordered_hashmap_ensure_allocated(h, ops) _ordered_hashmap_ensure_allocated(h, ops HASHMAP_DEBUG_SRC_ARGS) - -int _ordered_hashmap_ensure_put(OrderedHashmap **h, const struct hash_ops *hash_ops, const void *key, void *value HASHMAP_DEBUG_PARAMS); -#define ordered_hashmap_ensure_put(s, ops, key, value) _ordered_hashmap_ensure_put(s, ops, key, value HASHMAP_DEBUG_SRC_ARGS) - -IteratedCache* _hashmap_iterated_cache_new(HashmapBase *h); -static inline IteratedCache* hashmap_iterated_cache_new(Hashmap *h) { - return (IteratedCache*) _hashmap_iterated_cache_new(HASHMAP_BASE(h)); -} -static inline IteratedCache* ordered_hashmap_iterated_cache_new(OrderedHashmap *h) { - return (IteratedCache*) _hashmap_iterated_cache_new(HASHMAP_BASE(h)); -} - -int hashmap_put(Hashmap *h, const void *key, void *value); -static inline int ordered_hashmap_put(OrderedHashmap *h, const void *key, void *value) { - return hashmap_put(PLAIN_HASHMAP(h), key, value); -} - -int _hashmap_put_strdup_full(Hashmap **h, const struct hash_ops *hash_ops, const char *k, const char *v HASHMAP_DEBUG_PARAMS); -#define hashmap_put_strdup_full(h, hash_ops, k, v) _hashmap_put_strdup_full(h, hash_ops, k, v HASHMAP_DEBUG_SRC_ARGS) -#define hashmap_put_strdup(h, k, v) hashmap_put_strdup_full(h, &string_hash_ops_free_free, k, v) - -int hashmap_update(Hashmap *h, const void *key, void *value); -static inline int ordered_hashmap_update(OrderedHashmap *h, const void *key, void *value) { - return hashmap_update(PLAIN_HASHMAP(h), key, value); -} - -int hashmap_replace(Hashmap *h, const void *key, void *value); -static inline int ordered_hashmap_replace(OrderedHashmap *h, const void *key, void *value) { - return hashmap_replace(PLAIN_HASHMAP(h), key, value); -} - -void* _hashmap_get(HashmapBase *h, const void *key); -static inline void *hashmap_get(Hashmap *h, const void *key) { - return _hashmap_get(HASHMAP_BASE(h), key); -} -static inline void *ordered_hashmap_get(OrderedHashmap *h, const void *key) { - return _hashmap_get(HASHMAP_BASE(h), key); -} - -void* hashmap_get2(Hashmap *h, const void *key, void **rkey); -static inline void *ordered_hashmap_get2(OrderedHashmap *h, const void *key, void **rkey) { - return hashmap_get2(PLAIN_HASHMAP(h), key, rkey); -} - -bool _hashmap_contains(HashmapBase *h, const void *key); -static inline bool hashmap_contains(Hashmap *h, const void *key) { - return _hashmap_contains(HASHMAP_BASE(h), key); -} -static inline bool ordered_hashmap_contains(OrderedHashmap *h, const void *key) { - return _hashmap_contains(HASHMAP_BASE(h), key); -} - -void* _hashmap_remove(HashmapBase *h, const void *key); -static inline void *hashmap_remove(Hashmap *h, const void *key) { - return _hashmap_remove(HASHMAP_BASE(h), key); -} -static inline void *ordered_hashmap_remove(OrderedHashmap *h, const void *key) { - return _hashmap_remove(HASHMAP_BASE(h), key); -} - -void* hashmap_remove2(Hashmap *h, const void *key, void **rkey); -static inline void *ordered_hashmap_remove2(OrderedHashmap *h, const void *key, void **rkey) { - return hashmap_remove2(PLAIN_HASHMAP(h), key, rkey); -} - -void* _hashmap_remove_value(HashmapBase *h, const void *key, void *value); -static inline void *hashmap_remove_value(Hashmap *h, const void *key, void *value) { - return _hashmap_remove_value(HASHMAP_BASE(h), key, value); -} - -static inline void* ordered_hashmap_remove_value(OrderedHashmap *h, const void *key, void *value) { - return hashmap_remove_value(PLAIN_HASHMAP(h), key, value); -} - -int hashmap_remove_and_put(Hashmap *h, const void *old_key, const void *new_key, void *value); -static inline int ordered_hashmap_remove_and_put(OrderedHashmap *h, const void *old_key, const void *new_key, void *value) { - return hashmap_remove_and_put(PLAIN_HASHMAP(h), old_key, new_key, value); -} - -int hashmap_remove_and_replace(Hashmap *h, const void *old_key, const void *new_key, void *value); -static inline int ordered_hashmap_remove_and_replace(OrderedHashmap *h, const void *old_key, const void *new_key, void *value) { - return hashmap_remove_and_replace(PLAIN_HASHMAP(h), old_key, new_key, value); -} - -/* Since merging data from a OrderedHashmap into a Hashmap or vice-versa - * should just work, allow this by having looser type-checking here. */ -int _hashmap_merge(Hashmap *h, Hashmap *other); -#define hashmap_merge(h, other) _hashmap_merge(PLAIN_HASHMAP(h), PLAIN_HASHMAP(other)) -#define ordered_hashmap_merge(h, other) hashmap_merge(h, other) - -int _hashmap_reserve(HashmapBase *h, unsigned entries_add); -static inline int hashmap_reserve(Hashmap *h, unsigned entries_add) { - return _hashmap_reserve(HASHMAP_BASE(h), entries_add); -} -static inline int ordered_hashmap_reserve(OrderedHashmap *h, unsigned entries_add) { - return _hashmap_reserve(HASHMAP_BASE(h), entries_add); -} - -int _hashmap_move(HashmapBase *h, HashmapBase *other); -/* Unlike hashmap_merge, hashmap_move does not allow mixing the types. */ -static inline int hashmap_move(Hashmap *h, Hashmap *other) { - return _hashmap_move(HASHMAP_BASE(h), HASHMAP_BASE(other)); -} -static inline int ordered_hashmap_move(OrderedHashmap *h, OrderedHashmap *other) { - return _hashmap_move(HASHMAP_BASE(h), HASHMAP_BASE(other)); -} - -int _hashmap_move_one(HashmapBase *h, HashmapBase *other, const void *key); -static inline int hashmap_move_one(Hashmap *h, Hashmap *other, const void *key) { - return _hashmap_move_one(HASHMAP_BASE(h), HASHMAP_BASE(other), key); -} -static inline int ordered_hashmap_move_one(OrderedHashmap *h, OrderedHashmap *other, const void *key) { - return _hashmap_move_one(HASHMAP_BASE(h), HASHMAP_BASE(other), key); -} - -unsigned _hashmap_size(HashmapBase *h) _pure_; -static inline unsigned hashmap_size(Hashmap *h) { - return _hashmap_size(HASHMAP_BASE(h)); -} -static inline unsigned ordered_hashmap_size(OrderedHashmap *h) { - return _hashmap_size(HASHMAP_BASE(h)); -} - -static inline bool hashmap_isempty(Hashmap *h) { - return hashmap_size(h) == 0; -} -static inline bool ordered_hashmap_isempty(OrderedHashmap *h) { - return ordered_hashmap_size(h) == 0; -} - -unsigned _hashmap_buckets(HashmapBase *h) _pure_; -static inline unsigned hashmap_buckets(Hashmap *h) { - return _hashmap_buckets(HASHMAP_BASE(h)); -} -static inline unsigned ordered_hashmap_buckets(OrderedHashmap *h) { - return _hashmap_buckets(HASHMAP_BASE(h)); -} - -bool _hashmap_iterate(HashmapBase *h, Iterator *i, void **value, const void **key); -static inline bool hashmap_iterate(Hashmap *h, Iterator *i, void **value, const void **key) { - return _hashmap_iterate(HASHMAP_BASE(h), i, value, key); -} -static inline bool ordered_hashmap_iterate(OrderedHashmap *h, Iterator *i, void **value, const void **key) { - return _hashmap_iterate(HASHMAP_BASE(h), i, value, key); -} - -void _hashmap_clear(HashmapBase *h, free_func_t default_free_key, free_func_t default_free_value); -static inline void hashmap_clear(Hashmap *h) { - _hashmap_clear(HASHMAP_BASE(h), NULL, NULL); -} -static inline void ordered_hashmap_clear(OrderedHashmap *h) { - _hashmap_clear(HASHMAP_BASE(h), NULL, NULL); -} - -static inline void hashmap_clear_free(Hashmap *h) { - _hashmap_clear(HASHMAP_BASE(h), NULL, free); -} -static inline void ordered_hashmap_clear_free(OrderedHashmap *h) { - _hashmap_clear(HASHMAP_BASE(h), NULL, free); -} - -static inline void hashmap_clear_free_key(Hashmap *h) { - _hashmap_clear(HASHMAP_BASE(h), free, NULL); -} -static inline void ordered_hashmap_clear_free_key(OrderedHashmap *h) { - _hashmap_clear(HASHMAP_BASE(h), free, NULL); -} - -static inline void hashmap_clear_free_free(Hashmap *h) { - _hashmap_clear(HASHMAP_BASE(h), free, free); -} -static inline void ordered_hashmap_clear_free_free(OrderedHashmap *h) { - _hashmap_clear(HASHMAP_BASE(h), free, free); -} - -/* - * Note about all *_first*() functions - * - * For plain Hashmaps and Sets the order of entries is undefined. - * The functions find whatever entry is first in the implementation - * internal order. - * - * Only for OrderedHashmaps the order is well defined and finding - * the first entry is O(1). - */ - -void *_hashmap_first_key_and_value(HashmapBase *h, bool remove, void **ret_key); -static inline void *hashmap_steal_first_key_and_value(Hashmap *h, void **ret) { - return _hashmap_first_key_and_value(HASHMAP_BASE(h), true, ret); -} -static inline void *ordered_hashmap_steal_first_key_and_value(OrderedHashmap *h, void **ret) { - return _hashmap_first_key_and_value(HASHMAP_BASE(h), true, ret); -} -static inline void *hashmap_first_key_and_value(Hashmap *h, void **ret) { - return _hashmap_first_key_and_value(HASHMAP_BASE(h), false, ret); -} -static inline void *ordered_hashmap_first_key_and_value(OrderedHashmap *h, void **ret) { - return _hashmap_first_key_and_value(HASHMAP_BASE(h), false, ret); -} - -static inline void *hashmap_steal_first(Hashmap *h) { - return _hashmap_first_key_and_value(HASHMAP_BASE(h), true, NULL); -} -static inline void *ordered_hashmap_steal_first(OrderedHashmap *h) { - return _hashmap_first_key_and_value(HASHMAP_BASE(h), true, NULL); -} -static inline void *hashmap_first(Hashmap *h) { - return _hashmap_first_key_and_value(HASHMAP_BASE(h), false, NULL); -} -static inline void *ordered_hashmap_first(OrderedHashmap *h) { - return _hashmap_first_key_and_value(HASHMAP_BASE(h), false, NULL); -} - -static inline void *_hashmap_first_key(HashmapBase *h, bool remove) { - void *key = NULL; - - (void) _hashmap_first_key_and_value(HASHMAP_BASE(h), remove, &key); - return key; -} -static inline void *hashmap_steal_first_key(Hashmap *h) { - return _hashmap_first_key(HASHMAP_BASE(h), true); -} -static inline void *ordered_hashmap_steal_first_key(OrderedHashmap *h) { - return _hashmap_first_key(HASHMAP_BASE(h), true); -} -static inline void *hashmap_first_key(Hashmap *h) { - return _hashmap_first_key(HASHMAP_BASE(h), false); -} -static inline void *ordered_hashmap_first_key(OrderedHashmap *h) { - return _hashmap_first_key(HASHMAP_BASE(h), false); -} - -#define hashmap_clear_with_destructor(_s, _f) \ - ({ \ - void *_item; \ - while ((_item = hashmap_steal_first(_s))) \ - _f(_item); \ - }) -#define hashmap_free_with_destructor(_s, _f) \ - ({ \ - hashmap_clear_with_destructor(_s, _f); \ - hashmap_free(_s); \ - }) -#define ordered_hashmap_clear_with_destructor(_s, _f) \ - ({ \ - void *_item; \ - while ((_item = ordered_hashmap_steal_first(_s))) \ - _f(_item); \ - }) -#define ordered_hashmap_free_with_destructor(_s, _f) \ - ({ \ - ordered_hashmap_clear_with_destructor(_s, _f); \ - ordered_hashmap_free(_s); \ - }) - -/* no hashmap_next */ -void* ordered_hashmap_next(OrderedHashmap *h, const void *key); - -char** _hashmap_get_strv(HashmapBase *h); -static inline char** hashmap_get_strv(Hashmap *h) { - return _hashmap_get_strv(HASHMAP_BASE(h)); -} -static inline char** ordered_hashmap_get_strv(OrderedHashmap *h) { - return _hashmap_get_strv(HASHMAP_BASE(h)); -} - -/* - * Hashmaps are iterated in unpredictable order. - * OrderedHashmaps are an exception to this. They are iterated in the order - * the entries were inserted. - * It is safe to remove the current entry. - */ -#define _HASHMAP_FOREACH(e, h, i) \ - for (Iterator i = ITERATOR_FIRST; hashmap_iterate((h), &i, (void**)&(e), NULL); ) -#define HASHMAP_FOREACH(e, h) \ - _HASHMAP_FOREACH(e, h, UNIQ_T(i, UNIQ)) - -#define _ORDERED_HASHMAP_FOREACH(e, h, i) \ - for (Iterator i = ITERATOR_FIRST; ordered_hashmap_iterate((h), &i, (void**)&(e), NULL); ) -#define ORDERED_HASHMAP_FOREACH(e, h) \ - _ORDERED_HASHMAP_FOREACH(e, h, UNIQ_T(i, UNIQ)) - -#define _HASHMAP_FOREACH_KEY(e, k, h, i) \ - for (Iterator i = ITERATOR_FIRST; hashmap_iterate((h), &i, (void**)&(e), (const void**) &(k)); ) -#define HASHMAP_FOREACH_KEY(e, k, h) \ - _HASHMAP_FOREACH_KEY(e, k, h, UNIQ_T(i, UNIQ)) - -#define _ORDERED_HASHMAP_FOREACH_KEY(e, k, h, i) \ - for (Iterator i = ITERATOR_FIRST; ordered_hashmap_iterate((h), &i, (void**)&(e), (const void**) &(k)); ) -#define ORDERED_HASHMAP_FOREACH_KEY(e, k, h) \ - _ORDERED_HASHMAP_FOREACH_KEY(e, k, h, UNIQ_T(i, UNIQ)) - -DEFINE_TRIVIAL_CLEANUP_FUNC(Hashmap*, hashmap_free); -DEFINE_TRIVIAL_CLEANUP_FUNC(Hashmap*, hashmap_free_free); -DEFINE_TRIVIAL_CLEANUP_FUNC(Hashmap*, hashmap_free_free_key); -DEFINE_TRIVIAL_CLEANUP_FUNC(Hashmap*, hashmap_free_free_free); -DEFINE_TRIVIAL_CLEANUP_FUNC(OrderedHashmap*, ordered_hashmap_free); -DEFINE_TRIVIAL_CLEANUP_FUNC(OrderedHashmap*, ordered_hashmap_free_free); -DEFINE_TRIVIAL_CLEANUP_FUNC(OrderedHashmap*, ordered_hashmap_free_free_key); -DEFINE_TRIVIAL_CLEANUP_FUNC(OrderedHashmap*, ordered_hashmap_free_free_free); - -#define _cleanup_hashmap_free_ _cleanup_(hashmap_freep) -#define _cleanup_hashmap_free_free_ _cleanup_(hashmap_free_freep) -#define _cleanup_hashmap_free_free_free_ _cleanup_(hashmap_free_free_freep) -#define _cleanup_ordered_hashmap_free_ _cleanup_(ordered_hashmap_freep) -#define _cleanup_ordered_hashmap_free_free_ _cleanup_(ordered_hashmap_free_freep) -#define _cleanup_ordered_hashmap_free_free_free_ _cleanup_(ordered_hashmap_free_free_freep) - -DEFINE_TRIVIAL_CLEANUP_FUNC(IteratedCache*, iterated_cache_free); - -#define _cleanup_iterated_cache_free_ _cleanup_(iterated_cache_freep) diff --git a/shared/systemd/src/basic/hexdecoct.c b/shared/systemd/src/basic/hexdecoct.c deleted file mode 100644 index 78930b32..00000000 --- a/shared/systemd/src/basic/hexdecoct.c +++ /dev/null @@ -1,869 +0,0 @@ -/* SPDX-License-Identifier: LGPL-2.1-or-later */ - -#include "nm-sd-adapt-shared.h" - -#include <ctype.h> -#include <errno.h> -#include <stdint.h> -#include <stdlib.h> - -#include "alloc-util.h" -#include "hexdecoct.h" -#include "macro.h" -#include "memory-util.h" -#include "string-util.h" - -char octchar(int x) { - return '0' + (x & 7); -} - -int unoctchar(char c) { - - if (c >= '0' && c <= '7') - return c - '0'; - - return -EINVAL; -} - -char decchar(int x) { - return '0' + (x % 10); -} - -int undecchar(char c) { - - if (c >= '0' && c <= '9') - return c - '0'; - - return -EINVAL; -} - -char hexchar(int x) { - static const char table[16] = "0123456789abcdef"; - - return table[x & 15]; -} - -int unhexchar(char c) { - - if (c >= '0' && c <= '9') - return c - '0'; - - if (c >= 'a' && c <= 'f') - return c - 'a' + 10; - - if (c >= 'A' && c <= 'F') - return c - 'A' + 10; - - return -EINVAL; -} - -char *hexmem(const void *p, size_t l) { - const uint8_t *x; - char *r, *z; - - z = r = new(char, l * 2 + 1); - if (!r) - return NULL; - - for (x = p; x < (const uint8_t*) p + l; x++) { - *(z++) = hexchar(*x >> 4); - *(z++) = hexchar(*x & 15); - } - - *z = 0; - return r; -} - -static int unhex_next(const char **p, size_t *l) { - int r; - - assert(p); - assert(l); - - /* Find the next non-whitespace character, and decode it. We - * greedily skip all preceding and all following whitespace. */ - - for (;;) { - if (*l == 0) - return -EPIPE; - - if (!strchr(WHITESPACE, **p)) - break; - - /* Skip leading whitespace */ - (*p)++, (*l)--; - } - - r = unhexchar(**p); - if (r < 0) - return r; - - for (;;) { - (*p)++, (*l)--; - - if (*l == 0 || !strchr(WHITESPACE, **p)) - break; - - /* Skip following whitespace */ - } - - return r; -} - -int unhexmem_full(const char *p, size_t l, bool secure, void **ret, size_t *ret_len) { - _cleanup_free_ uint8_t *buf = NULL; - size_t buf_size; - const char *x; - uint8_t *z; - int r; - - assert(ret); - assert(ret_len); - assert(p || l == 0); - - if (l == (size_t) -1) - l = strlen(p); - - /* Note that the calculation of memory size is an upper boundary, as we ignore whitespace while decoding */ - buf_size = (l + 1) / 2 + 1; - buf = malloc(buf_size); - if (!buf) - return -ENOMEM; - - for (x = p, z = buf;;) { - int a, b; - - a = unhex_next(&x, &l); - if (a == -EPIPE) /* End of string */ - break; - if (a < 0) { - r = a; - goto on_failure; - } - - b = unhex_next(&x, &l); - if (b < 0) { - r = b; - goto on_failure; - } - - *(z++) = (uint8_t) a << 4 | (uint8_t) b; - } - - *z = 0; - - *ret_len = (size_t) (z - buf); - *ret = TAKE_PTR(buf); - - return 0; - -on_failure: - if (secure) - explicit_bzero_safe(buf, buf_size); - - return r; -} - -#if 0 /* NM_IGNORED */ -/* https://tools.ietf.org/html/rfc4648#section-6 - * Notice that base32hex differs from base32 in the alphabet it uses. - * The distinction is that the base32hex representation preserves the - * order of the underlying data when compared as bytestrings, this is - * useful when representing NSEC3 hashes, as one can then verify the - * order of hashes directly from their representation. */ -char base32hexchar(int x) { - static const char table[32] = "0123456789" - "ABCDEFGHIJKLMNOPQRSTUV"; - - return table[x & 31]; -} - -int unbase32hexchar(char c) { - unsigned offset; - - if (c >= '0' && c <= '9') - return c - '0'; - - offset = '9' - '0' + 1; - - if (c >= 'A' && c <= 'V') - return c - 'A' + offset; - - return -EINVAL; -} - -char *base32hexmem(const void *p, size_t l, bool padding) { - char *r, *z; - const uint8_t *x; - size_t len; - - assert(p || l == 0); - - if (padding) - /* five input bytes makes eight output bytes, padding is added so we must round up */ - len = 8 * (l + 4) / 5; - else { - /* same, but round down as there is no padding */ - len = 8 * l / 5; - - switch (l % 5) { - case 4: - len += 7; - break; - case 3: - len += 5; - break; - case 2: - len += 4; - break; - case 1: - len += 2; - break; - } - } - - z = r = malloc(len + 1); - if (!r) - return NULL; - - for (x = p; x < (const uint8_t*) p + (l / 5) * 5; x += 5) { - /* x[0] == XXXXXXXX; x[1] == YYYYYYYY; x[2] == ZZZZZZZZ - * x[3] == QQQQQQQQ; x[4] == WWWWWWWW */ - *(z++) = base32hexchar(x[0] >> 3); /* 000XXXXX */ - *(z++) = base32hexchar((x[0] & 7) << 2 | x[1] >> 6); /* 000XXXYY */ - *(z++) = base32hexchar((x[1] & 63) >> 1); /* 000YYYYY */ - *(z++) = base32hexchar((x[1] & 1) << 4 | x[2] >> 4); /* 000YZZZZ */ - *(z++) = base32hexchar((x[2] & 15) << 1 | x[3] >> 7); /* 000ZZZZQ */ - *(z++) = base32hexchar((x[3] & 127) >> 2); /* 000QQQQQ */ - *(z++) = base32hexchar((x[3] & 3) << 3 | x[4] >> 5); /* 000QQWWW */ - *(z++) = base32hexchar((x[4] & 31)); /* 000WWWWW */ - } - - switch (l % 5) { - case 4: - *(z++) = base32hexchar(x[0] >> 3); /* 000XXXXX */ - *(z++) = base32hexchar((x[0] & 7) << 2 | x[1] >> 6); /* 000XXXYY */ - *(z++) = base32hexchar((x[1] & 63) >> 1); /* 000YYYYY */ - *(z++) = base32hexchar((x[1] & 1) << 4 | x[2] >> 4); /* 000YZZZZ */ - *(z++) = base32hexchar((x[2] & 15) << 1 | x[3] >> 7); /* 000ZZZZQ */ - *(z++) = base32hexchar((x[3] & 127) >> 2); /* 000QQQQQ */ - *(z++) = base32hexchar((x[3] & 3) << 3); /* 000QQ000 */ - if (padding) - *(z++) = '='; - - break; - - case 3: - *(z++) = base32hexchar(x[0] >> 3); /* 000XXXXX */ - *(z++) = base32hexchar((x[0] & 7) << 2 | x[1] >> 6); /* 000XXXYY */ - *(z++) = base32hexchar((x[1] & 63) >> 1); /* 000YYYYY */ - *(z++) = base32hexchar((x[1] & 1) << 4 | x[2] >> 4); /* 000YZZZZ */ - *(z++) = base32hexchar((x[2] & 15) << 1); /* 000ZZZZ0 */ - if (padding) { - *(z++) = '='; - *(z++) = '='; - *(z++) = '='; - } - - break; - - case 2: - *(z++) = base32hexchar(x[0] >> 3); /* 000XXXXX */ - *(z++) = base32hexchar((x[0] & 7) << 2 | x[1] >> 6); /* 000XXXYY */ - *(z++) = base32hexchar((x[1] & 63) >> 1); /* 000YYYYY */ - *(z++) = base32hexchar((x[1] & 1) << 4); /* 000Y0000 */ - if (padding) { - *(z++) = '='; - *(z++) = '='; - *(z++) = '='; - *(z++) = '='; - } - - break; - - case 1: - *(z++) = base32hexchar(x[0] >> 3); /* 000XXXXX */ - *(z++) = base32hexchar((x[0] & 7) << 2); /* 000XXX00 */ - if (padding) { - *(z++) = '='; - *(z++) = '='; - *(z++) = '='; - *(z++) = '='; - *(z++) = '='; - *(z++) = '='; - } - - break; - } - - *z = 0; - return r; -} - -int unbase32hexmem(const char *p, size_t l, bool padding, void **mem, size_t *_len) { - _cleanup_free_ uint8_t *r = NULL; - int a, b, c, d, e, f, g, h; - uint8_t *z; - const char *x; - size_t len; - unsigned pad = 0; - - assert(p || l == 0); - assert(mem); - assert(_len); - - if (l == (size_t) -1) - l = strlen(p); - - /* padding ensures any base32hex input has input divisible by 8 */ - if (padding && l % 8 != 0) - return -EINVAL; - - if (padding) { - /* strip the padding */ - while (l > 0 && p[l - 1] == '=' && pad < 7) { - pad++; - l--; - } - } - - /* a group of eight input bytes needs five output bytes, in case of - * padding we need to add some extra bytes */ - len = (l / 8) * 5; - - switch (l % 8) { - case 7: - len += 4; - break; - case 5: - len += 3; - break; - case 4: - len += 2; - break; - case 2: - len += 1; - break; - case 0: - break; - default: - return -EINVAL; - } - - z = r = malloc(len + 1); - if (!r) - return -ENOMEM; - - for (x = p; x < p + (l / 8) * 8; x += 8) { - /* a == 000XXXXX; b == 000YYYYY; c == 000ZZZZZ; d == 000WWWWW - * e == 000SSSSS; f == 000QQQQQ; g == 000VVVVV; h == 000RRRRR */ - a = unbase32hexchar(x[0]); - if (a < 0) - return -EINVAL; - - b = unbase32hexchar(x[1]); - if (b < 0) - return -EINVAL; - - c = unbase32hexchar(x[2]); - if (c < 0) - return -EINVAL; - - d = unbase32hexchar(x[3]); - if (d < 0) - return -EINVAL; - - e = unbase32hexchar(x[4]); - if (e < 0) - return -EINVAL; - - f = unbase32hexchar(x[5]); - if (f < 0) - return -EINVAL; - - g = unbase32hexchar(x[6]); - if (g < 0) - return -EINVAL; - - h = unbase32hexchar(x[7]); - if (h < 0) - return -EINVAL; - - *(z++) = (uint8_t) a << 3 | (uint8_t) b >> 2; /* XXXXXYYY */ - *(z++) = (uint8_t) b << 6 | (uint8_t) c << 1 | (uint8_t) d >> 4; /* YYZZZZZW */ - *(z++) = (uint8_t) d << 4 | (uint8_t) e >> 1; /* WWWWSSSS */ - *(z++) = (uint8_t) e << 7 | (uint8_t) f << 2 | (uint8_t) g >> 3; /* SQQQQQVV */ - *(z++) = (uint8_t) g << 5 | (uint8_t) h; /* VVVRRRRR */ - } - - switch (l % 8) { - case 7: - a = unbase32hexchar(x[0]); - if (a < 0) - return -EINVAL; - - b = unbase32hexchar(x[1]); - if (b < 0) - return -EINVAL; - - c = unbase32hexchar(x[2]); - if (c < 0) - return -EINVAL; - - d = unbase32hexchar(x[3]); - if (d < 0) - return -EINVAL; - - e = unbase32hexchar(x[4]); - if (e < 0) - return -EINVAL; - - f = unbase32hexchar(x[5]); - if (f < 0) - return -EINVAL; - - g = unbase32hexchar(x[6]); - if (g < 0) - return -EINVAL; - - /* g == 000VV000 */ - if (g & 7) - return -EINVAL; - - *(z++) = (uint8_t) a << 3 | (uint8_t) b >> 2; /* XXXXXYYY */ - *(z++) = (uint8_t) b << 6 | (uint8_t) c << 1 | (uint8_t) d >> 4; /* YYZZZZZW */ - *(z++) = (uint8_t) d << 4 | (uint8_t) e >> 1; /* WWWWSSSS */ - *(z++) = (uint8_t) e << 7 | (uint8_t) f << 2 | (uint8_t) g >> 3; /* SQQQQQVV */ - - break; - case 5: - a = unbase32hexchar(x[0]); - if (a < 0) - return -EINVAL; - - b = unbase32hexchar(x[1]); - if (b < 0) - return -EINVAL; - - c = unbase32hexchar(x[2]); - if (c < 0) - return -EINVAL; - - d = unbase32hexchar(x[3]); - if (d < 0) - return -EINVAL; - - e = unbase32hexchar(x[4]); - if (e < 0) - return -EINVAL; - - /* e == 000SSSS0 */ - if (e & 1) - return -EINVAL; - - *(z++) = (uint8_t) a << 3 | (uint8_t) b >> 2; /* XXXXXYYY */ - *(z++) = (uint8_t) b << 6 | (uint8_t) c << 1 | (uint8_t) d >> 4; /* YYZZZZZW */ - *(z++) = (uint8_t) d << 4 | (uint8_t) e >> 1; /* WWWWSSSS */ - - break; - case 4: - a = unbase32hexchar(x[0]); - if (a < 0) - return -EINVAL; - - b = unbase32hexchar(x[1]); - if (b < 0) - return -EINVAL; - - c = unbase32hexchar(x[2]); - if (c < 0) - return -EINVAL; - - d = unbase32hexchar(x[3]); - if (d < 0) - return -EINVAL; - - /* d == 000W0000 */ - if (d & 15) - return -EINVAL; - - *(z++) = (uint8_t) a << 3 | (uint8_t) b >> 2; /* XXXXXYYY */ - *(z++) = (uint8_t) b << 6 | (uint8_t) c << 1 | (uint8_t) d >> 4; /* YYZZZZZW */ - - break; - case 2: - a = unbase32hexchar(x[0]); - if (a < 0) - return -EINVAL; - - b = unbase32hexchar(x[1]); - if (b < 0) - return -EINVAL; - - /* b == 000YYY00 */ - if (b & 3) - return -EINVAL; - - *(z++) = (uint8_t) a << 3 | (uint8_t) b >> 2; /* XXXXXYYY */ - - break; - case 0: - break; - default: - return -EINVAL; - } - - *z = 0; - - *mem = TAKE_PTR(r); - *_len = len; - - return 0; -} - -/* https://tools.ietf.org/html/rfc4648#section-4 */ -char base64char(int x) { - static const char table[64] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ" - "abcdefghijklmnopqrstuvwxyz" - "0123456789+/"; - return table[x & 63]; -} -#endif /* NM_IGNORED */ - -int unbase64char(char c) { - unsigned offset; - - if (c >= 'A' && c <= 'Z') - return c - 'A'; - - offset = 'Z' - 'A' + 1; - - if (c >= 'a' && c <= 'z') - return c - 'a' + offset; - - offset += 'z' - 'a' + 1; - - if (c >= '0' && c <= '9') - return c - '0' + offset; - - offset += '9' - '0' + 1; - - if (c == '+') - return offset; - - offset++; - - if (c == '/') - return offset; - - return -EINVAL; -} - -#if 0 /* NM_IGNORED */ -ssize_t base64mem(const void *p, size_t l, char **out) { - char *r, *z; - const uint8_t *x; - - assert(p || l == 0); - assert(out); - - /* three input bytes makes four output bytes, padding is added so we must round up */ - z = r = malloc(4 * (l + 2) / 3 + 1); - if (!r) - return -ENOMEM; - - for (x = p; x < (const uint8_t*) p + (l / 3) * 3; x += 3) { - /* x[0] == XXXXXXXX; x[1] == YYYYYYYY; x[2] == ZZZZZZZZ */ - *(z++) = base64char(x[0] >> 2); /* 00XXXXXX */ - *(z++) = base64char((x[0] & 3) << 4 | x[1] >> 4); /* 00XXYYYY */ - *(z++) = base64char((x[1] & 15) << 2 | x[2] >> 6); /* 00YYYYZZ */ - *(z++) = base64char(x[2] & 63); /* 00ZZZZZZ */ - } - - switch (l % 3) { - case 2: - *(z++) = base64char(x[0] >> 2); /* 00XXXXXX */ - *(z++) = base64char((x[0] & 3) << 4 | x[1] >> 4); /* 00XXYYYY */ - *(z++) = base64char((x[1] & 15) << 2); /* 00YYYY00 */ - *(z++) = '='; - - break; - case 1: - *(z++) = base64char(x[0] >> 2); /* 00XXXXXX */ - *(z++) = base64char((x[0] & 3) << 4); /* 00XX0000 */ - *(z++) = '='; - *(z++) = '='; - - break; - } - - *z = 0; - *out = r; - return z - r; -} - -static int base64_append_width( - char **prefix, int plen, - char sep, int indent, - const void *p, size_t l, - int width) { - - _cleanup_free_ char *x = NULL; - char *t, *s; - ssize_t len, avail, line, lines; - - len = base64mem(p, l, &x); - if (len <= 0) - return len; - - lines = DIV_ROUND_UP(len, width); - - if ((size_t) plen >= SSIZE_MAX - 1 - 1 || - lines > (SSIZE_MAX - plen - 1 - 1) / (indent + width + 1)) - return -ENOMEM; - - t = realloc(*prefix, (ssize_t) plen + 1 + 1 + (indent + width + 1) * lines); - if (!t) - return -ENOMEM; - - t[plen] = sep; - - for (line = 0, s = t + plen + 1, avail = len; line < lines; line++) { - int act = MIN(width, avail); - - if (line > 0 || sep == '\n') { - memset(s, ' ', indent); - s += indent; - } - - memcpy(s, x + width * line, act); - s += act; - *(s++) = line < lines - 1 ? '\n' : '\0'; - avail -= act; - } - assert(avail == 0); - - *prefix = t; - return 0; -} - -int base64_append( - char **prefix, int plen, - const void *p, size_t l, - int indent, int width) { - - if (plen > width / 2 || plen + indent > width) - /* leave indent on the left, keep last column free */ - return base64_append_width(prefix, plen, '\n', indent, p, l, width - indent - 1); - else - /* leave plen on the left, keep last column free */ - return base64_append_width(prefix, plen, ' ', plen + 1, p, l, width - plen - 1); -} -#endif /* NM_IGNORED */ - -static int unbase64_next(const char **p, size_t *l) { - int ret; - - assert(p); - assert(l); - - /* Find the next non-whitespace character, and decode it. If we find padding, we return it as INT_MAX. We - * greedily skip all preceding and all following whitespace. */ - - for (;;) { - if (*l == 0) - return -EPIPE; - - if (!strchr(WHITESPACE, **p)) - break; - - /* Skip leading whitespace */ - (*p)++, (*l)--; - } - - if (**p == '=') - ret = INT_MAX; /* return padding as INT_MAX */ - else { - ret = unbase64char(**p); - if (ret < 0) - return ret; - } - - for (;;) { - (*p)++, (*l)--; - - if (*l == 0) - break; - if (!strchr(WHITESPACE, **p)) - break; - - /* Skip following whitespace */ - } - - return ret; -} - -int unbase64mem_full(const char *p, size_t l, bool secure, void **ret, size_t *ret_size) { - _cleanup_free_ uint8_t *buf = NULL; - const char *x; - uint8_t *z; - size_t len; - int r; - - assert(p || l == 0); - assert(ret); - assert(ret_size); - - if (l == (size_t) -1) - l = strlen(p); - - /* A group of four input bytes needs three output bytes, in case of padding we need to add two or three extra - * bytes. Note that this calculation is an upper boundary, as we ignore whitespace while decoding */ - len = (l / 4) * 3 + (l % 4 != 0 ? (l % 4) - 1 : 0); - - buf = malloc(len + 1); - if (!buf) - return -ENOMEM; - - for (x = p, z = buf;;) { - int a, b, c, d; /* a == 00XXXXXX; b == 00YYYYYY; c == 00ZZZZZZ; d == 00WWWWWW */ - - a = unbase64_next(&x, &l); - if (a == -EPIPE) /* End of string */ - break; - if (a < 0) { - r = a; - goto on_failure; - } - if (a == INT_MAX) { /* Padding is not allowed at the beginning of a 4ch block */ - r = -EINVAL; - goto on_failure; - } - - b = unbase64_next(&x, &l); - if (b < 0) { - r = b; - goto on_failure; - } - if (b == INT_MAX) { /* Padding is not allowed at the second character of a 4ch block either */ - r = -EINVAL; - goto on_failure; - } - - c = unbase64_next(&x, &l); - if (c < 0) { - r = c; - goto on_failure; - } - - d = unbase64_next(&x, &l); - if (d < 0) { - r = d; - goto on_failure; - } - - if (c == INT_MAX) { /* Padding at the third character */ - - if (d != INT_MAX) { /* If the third character is padding, the fourth must be too */ - r = -EINVAL; - goto on_failure; - } - - /* b == 00YY0000 */ - if (b & 15) { - r = -EINVAL; - goto on_failure; - } - - if (l > 0) { /* Trailing rubbish? */ - r = -ENAMETOOLONG; - goto on_failure; - } - - *(z++) = (uint8_t) a << 2 | (uint8_t) (b >> 4); /* XXXXXXYY */ - break; - } - - if (d == INT_MAX) { - /* c == 00ZZZZ00 */ - if (c & 3) { - r = -EINVAL; - goto on_failure; - } - - if (l > 0) { /* Trailing rubbish? */ - r = -ENAMETOOLONG; - goto on_failure; - } - - *(z++) = (uint8_t) a << 2 | (uint8_t) b >> 4; /* XXXXXXYY */ - *(z++) = (uint8_t) b << 4 | (uint8_t) c >> 2; /* YYYYZZZZ */ - break; - } - - *(z++) = (uint8_t) a << 2 | (uint8_t) b >> 4; /* XXXXXXYY */ - *(z++) = (uint8_t) b << 4 | (uint8_t) c >> 2; /* YYYYZZZZ */ - *(z++) = (uint8_t) c << 6 | (uint8_t) d; /* ZZWWWWWW */ - } - - *z = 0; - - *ret_size = (size_t) (z - buf); - *ret = TAKE_PTR(buf); - - return 0; - -on_failure: - if (secure) - explicit_bzero_safe(buf, len); - - return r; -} - -#if 0 /* NM_IGNORED */ -void hexdump(FILE *f, const void *p, size_t s) { - const uint8_t *b = p; - unsigned n = 0; - - assert(b || s == 0); - - if (!f) - f = stdout; - - while (s > 0) { - size_t i; - - fprintf(f, "%04x ", n); - - for (i = 0; i < 16; i++) { - - if (i >= s) - fputs(" ", f); - else - fprintf(f, "%02x ", b[i]); - - if (i == 7) - fputc(' ', f); - } - - fputc(' ', f); - - for (i = 0; i < 16; i++) { - - if (i >= s) - fputc(' ', f); - else - fputc(isprint(b[i]) ? (char) b[i] : '.', f); - } - - fputc('\n', f); - - if (s < 16) - break; - - n += 16; - b += 16; - s -= 16; - } -} -#endif /* NM_IGNORED */ diff --git a/shared/systemd/src/basic/hexdecoct.h b/shared/systemd/src/basic/hexdecoct.h deleted file mode 100644 index 7e2a6892..00000000 --- a/shared/systemd/src/basic/hexdecoct.h +++ /dev/null @@ -1,44 +0,0 @@ -/* SPDX-License-Identifier: LGPL-2.1-or-later */ -#pragma once - -#include <stdbool.h> -#include <stddef.h> -#include <stdio.h> -#include <sys/types.h> - -#include "macro.h" - -char octchar(int x) _const_; -int unoctchar(char c) _const_; - -char decchar(int x) _const_; -int undecchar(char c) _const_; - -char hexchar(int x) _const_; -int unhexchar(char c) _const_; - -char *hexmem(const void *p, size_t l); -int unhexmem_full(const char *p, size_t l, bool secure, void **mem, size_t *len); -static inline int unhexmem(const char *p, size_t l, void **mem, size_t *len) { - return unhexmem_full(p, l, false, mem, len); -} - -char base32hexchar(int x) _const_; -int unbase32hexchar(char c) _const_; - -char base64char(int x) _const_; -int unbase64char(char c) _const_; - -char *base32hexmem(const void *p, size_t l, bool padding); -int unbase32hexmem(const char *p, size_t l, bool padding, void **mem, size_t *len); - -ssize_t base64mem(const void *p, size_t l, char **out); -int base64_append(char **prefix, int plen, - const void *p, size_t l, - int margin, int width); -int unbase64mem_full(const char *p, size_t l, bool secure, void **mem, size_t *len); -static inline int unbase64mem(const char *p, size_t l, void **mem, size_t *len) { - return unbase64mem_full(p, l, false, mem, len); -} - -void hexdump(FILE *f, const void *p, size_t s); diff --git a/shared/systemd/src/basic/hostname-util.c b/shared/systemd/src/basic/hostname-util.c deleted file mode 100644 index a3cdc62e..00000000 --- a/shared/systemd/src/basic/hostname-util.c +++ /dev/null @@ -1,196 +0,0 @@ -/* SPDX-License-Identifier: LGPL-2.1-or-later */ - -#include "nm-sd-adapt-shared.h" - -#include <errno.h> -#include <limits.h> -#include <stdio.h> -#include <sys/utsname.h> -#include <unistd.h> - -#include "alloc-util.h" -#include "hostname-util.h" -#include "string-util.h" -#include "strv.h" - -#if 0 /* NM_IGNORED */ -char* gethostname_malloc(void) { - struct utsname u; - const char *s; - - /* This call tries to return something useful, either the actual hostname - * or it makes something up. The only reason it might fail is OOM. - * It might even return "localhost" if that's set. */ - - assert_se(uname(&u) >= 0); - - s = u.nodename; - if (isempty(s) || streq(s, "(none)")) - s = FALLBACK_HOSTNAME; - - return strdup(s); -} - -char* gethostname_short_malloc(void) { - struct utsname u; - const char *s; - - /* Like above, but kills the FQDN part if present. */ - - assert_se(uname(&u) >= 0); - - s = u.nodename; - if (isempty(s) || streq(s, "(none)") || s[0] == '.') { - s = FALLBACK_HOSTNAME; - assert(s[0] != '.'); - } - - return strndup(s, strcspn(s, ".")); -} -#endif /* NM_IGNORED */ - -int gethostname_strict(char **ret) { - struct utsname u; - char *k; - - /* This call will rather fail than make up a name. It will not return "localhost" either. */ - - assert_se(uname(&u) >= 0); - - if (isempty(u.nodename)) - return -ENXIO; - - if (streq(u.nodename, "(none)")) - return -ENXIO; - - if (is_localhost(u.nodename)) - return -ENXIO; - - k = strdup(u.nodename); - if (!k) - return -ENOMEM; - - *ret = k; - return 0; -} - -bool valid_ldh_char(char c) { - /* "LDH" → "Letters, digits, hyphens", as per RFC 5890, Section 2.3.1 */ - - return - (c >= 'a' && c <= 'z') || - (c >= 'A' && c <= 'Z') || - (c >= '0' && c <= '9') || - c == '-'; -} - -bool hostname_is_valid(const char *s, ValidHostnameFlags flags) { - unsigned n_dots = 0; - const char *p; - bool dot, hyphen; - - /* Check if s looks like a valid hostname or FQDN. This does not do full DNS validation, but only - * checks if the name is composed of allowed characters and the length is not above the maximum - * allowed by Linux (c.f. dns_name_is_valid()). A trailing dot is allowed if - * VALID_HOSTNAME_TRAILING_DOT flag is set and at least two components are present in the name. Note - * that due to the restricted charset and length this call is substantially more conservative than - * dns_name_is_valid(). Doesn't accept empty hostnames, hostnames with leading dots, and hostnames - * with multiple dots in a sequence. Doesn't allow hyphens at the beginning or end of label. */ - - if (isempty(s)) - return false; - - if (streq(s, ".host")) /* Used by the container logic to denote the "root container" */ - return FLAGS_SET(flags, VALID_HOSTNAME_DOT_HOST); - - for (p = s, dot = hyphen = true; *p; p++) - if (*p == '.') { - if (dot || hyphen) - return false; - - dot = true; - hyphen = false; - n_dots++; - - } else if (*p == '-') { - if (dot) - return false; - - dot = false; - hyphen = true; - - } else { - if (!valid_ldh_char(*p)) - return false; - - dot = false; - hyphen = false; - } - - if (dot && (n_dots < 2 || !FLAGS_SET(flags, VALID_HOSTNAME_TRAILING_DOT))) - return false; - if (hyphen) - return false; - - if (p-s > HOST_NAME_MAX) /* Note that HOST_NAME_MAX is 64 on Linux, but DNS allows domain names up to - * 255 characters */ - return false; - - return true; -} - -char* hostname_cleanup(char *s) { - char *p, *d; - bool dot, hyphen; - - assert(s); - - for (p = s, d = s, dot = hyphen = true; *p && d - s < HOST_NAME_MAX; p++) - if (*p == '.') { - if (dot || hyphen) - continue; - - *(d++) = '.'; - dot = true; - hyphen = false; - - } else if (*p == '-') { - if (dot) - continue; - - *(d++) = '-'; - dot = false; - hyphen = true; - - } else if (valid_ldh_char(*p)) { - *(d++) = *p; - dot = false; - hyphen = false; - } - - if (d > s && IN_SET(d[-1], '-', '.')) - /* The dot can occur at most once, but we might have multiple - * hyphens, hence the loop */ - d--; - *d = 0; - - return s; -} - -bool is_localhost(const char *hostname) { - assert(hostname); - - /* This tries to identify local host and domain names - * described in RFC6761 plus the redhatism of localdomain */ - - return STRCASE_IN_SET( - hostname, - "localhost", - "localhost.", - "localhost.localdomain", - "localhost.localdomain.") || - endswith_no_case(hostname, ".localhost") || - endswith_no_case(hostname, ".localhost.") || - endswith_no_case(hostname, ".localhost.localdomain") || - endswith_no_case(hostname, ".localhost.localdomain."); -} diff --git a/shared/systemd/src/basic/hostname-util.h b/shared/systemd/src/basic/hostname-util.h deleted file mode 100644 index 6cff9c1d..00000000 --- a/shared/systemd/src/basic/hostname-util.h +++ /dev/null @@ -1,29 +0,0 @@ -/* SPDX-License-Identifier: LGPL-2.1-or-later */ -#pragma once - -#include <stdbool.h> -#include <stdio.h> - -#include "macro.h" -#include "strv.h" - -char* gethostname_malloc(void); -char* gethostname_short_malloc(void); -int gethostname_strict(char **ret); - -bool valid_ldh_char(char c) _const_; - -typedef enum ValidHostnameFlags { - VALID_HOSTNAME_TRAILING_DOT = 1 << 0, /* Accept trailing dot on multi-label names */ - VALID_HOSTNAME_DOT_HOST = 1 << 1, /* Accept ".host" as valid hostname */ -} ValidHostnameFlags; - -bool hostname_is_valid(const char *s, ValidHostnameFlags flags) _pure_; -char* hostname_cleanup(char *s); - -bool is_localhost(const char *hostname); - -static inline bool is_gateway_hostname(const char *hostname) { - /* This tries to identify the valid syntaxes for the our synthetic "gateway" host. */ - return STRCASE_IN_SET(hostname, "_gateway", "_gateway."); -} diff --git a/shared/systemd/src/basic/in-addr-util.c b/shared/systemd/src/basic/in-addr-util.c deleted file mode 100644 index c315dcbb..00000000 --- a/shared/systemd/src/basic/in-addr-util.c +++ /dev/null @@ -1,792 +0,0 @@ -/* SPDX-License-Identifier: LGPL-2.1-or-later */ - -#include "nm-sd-adapt-shared.h" - -#include <arpa/inet.h> -#include <endian.h> -#include <errno.h> -#include <net/if.h> -#include <stdint.h> -#include <stdio.h> -#include <stdlib.h> - -#include "alloc-util.h" -#include "errno-util.h" -#include "in-addr-util.h" -#include "macro.h" -#include "parse-util.h" -#include "random-util.h" -#include "string-util.h" -#include "strxcpyx.h" -#include "util.h" - -bool in4_addr_is_null(const struct in_addr *a) { - assert(a); - - return a->s_addr == 0; -} - -int in_addr_is_null(int family, const union in_addr_union *u) { - assert(u); - - if (family == AF_INET) - return in4_addr_is_null(&u->in); - - if (family == AF_INET6) - return IN6_IS_ADDR_UNSPECIFIED(&u->in6); - - return -EAFNOSUPPORT; -} - -bool in4_addr_is_link_local(const struct in_addr *a) { - assert(a); - - return (be32toh(a->s_addr) & UINT32_C(0xFFFF0000)) == (UINT32_C(169) << 24 | UINT32_C(254) << 16); -} - -int in_addr_is_link_local(int family, const union in_addr_union *u) { - assert(u); - - if (family == AF_INET) - return in4_addr_is_link_local(&u->in); - - if (family == AF_INET6) - return IN6_IS_ADDR_LINKLOCAL(&u->in6); - - return -EAFNOSUPPORT; -} - -bool in6_addr_is_link_local_all_nodes(const struct in6_addr *a) { - assert(a); - - /* ff02::1 */ - return be32toh(a->s6_addr32[0]) == UINT32_C(0xff020000) && - a->s6_addr32[1] == 0 && - a->s6_addr32[2] == 0 && - be32toh(a->s6_addr32[3]) == UINT32_C(0x00000001); -} - -int in_addr_is_multicast(int family, const union in_addr_union *u) { - assert(u); - - if (family == AF_INET) - return IN_MULTICAST(be32toh(u->in.s_addr)); - - if (family == AF_INET6) - return IN6_IS_ADDR_MULTICAST(&u->in6); - - return -EAFNOSUPPORT; -} - -bool in4_addr_is_local_multicast(const struct in_addr *a) { - assert(a); - - return (be32toh(a->s_addr) & UINT32_C(0xffffff00)) == UINT32_C(0xe0000000); -} - -bool in4_addr_is_localhost(const struct in_addr *a) { - assert(a); - - /* All of 127.x.x.x is localhost. */ - return (be32toh(a->s_addr) & UINT32_C(0xFF000000)) == UINT32_C(127) << 24; -} - -bool in4_addr_is_non_local(const struct in_addr *a) { - /* Whether the address is not null and not localhost. - * - * As such, it is suitable to configure as DNS/NTP server from DHCP. */ - return !in4_addr_is_null(a) && - !in4_addr_is_localhost(a); -} - -int in_addr_is_localhost(int family, const union in_addr_union *u) { - assert(u); - - if (family == AF_INET) - return in4_addr_is_localhost(&u->in); - - if (family == AF_INET6) - return IN6_IS_ADDR_LOOPBACK(&u->in6); - - return -EAFNOSUPPORT; -} - -bool in4_addr_equal(const struct in_addr *a, const struct in_addr *b) { - assert(a); - assert(b); - - return a->s_addr == b->s_addr; -} - -int in_addr_equal(int family, const union in_addr_union *a, const union in_addr_union *b) { - assert(a); - assert(b); - - if (family == AF_INET) - return in4_addr_equal(&a->in, &b->in); - - if (family == AF_INET6) - return IN6_ARE_ADDR_EQUAL(&a->in6, &b->in6); - - return -EAFNOSUPPORT; -} - -#if 0 /* NM_IGNORED */ -int in_addr_prefix_intersect( - int family, - const union in_addr_union *a, - unsigned aprefixlen, - const union in_addr_union *b, - unsigned bprefixlen) { - - unsigned m; - - assert(a); - assert(b); - - /* Checks whether there are any addresses that are in both - * networks */ - - m = MIN(aprefixlen, bprefixlen); - - if (family == AF_INET) { - uint32_t x, nm; - - x = be32toh(a->in.s_addr ^ b->in.s_addr); - nm = (m == 0) ? 0 : 0xFFFFFFFFUL << (32 - m); - - return (x & nm) == 0; - } - - if (family == AF_INET6) { - unsigned i; - - if (m > 128) - m = 128; - - for (i = 0; i < 16; i++) { - uint8_t x, nm; - - x = a->in6.s6_addr[i] ^ b->in6.s6_addr[i]; - - if (m < 8) - nm = 0xFF << (8 - m); - else - nm = 0xFF; - - if ((x & nm) != 0) - return 0; - - if (m > 8) - m -= 8; - else - m = 0; - } - - return 1; - } - - return -EAFNOSUPPORT; -} - -int in_addr_prefix_next(int family, union in_addr_union *u, unsigned prefixlen) { - assert(u); - - /* Increases the network part of an address by one. Returns - * positive if that succeeds, or -ERANGE if this overflows. */ - - return in_addr_prefix_nth(family, u, prefixlen, 1); -} - -/* - * Calculates the nth prefix of size prefixlen starting from the address denoted by u. - * - * On success 1 will be returned and the calculated prefix will be available in - * u. In the case nth == 0 the input will be left unchanged and 1 will be returned. - * In case the calculation cannot be performed (invalid prefix length, - * overflows would occur) -ERANGE is returned. If the address family given isn't - * supported -EAFNOSUPPORT will be returned. - * - * - * Examples: - * - in_addr_prefix_nth(AF_INET, 192.168.0.0, 24, 2), returns 1, writes 192.168.2.0 to u - * - in_addr_prefix_nth(AF_INET, 192.168.0.0, 24, 0), returns 1, no data written - * - in_addr_prefix_nth(AF_INET, 255.255.255.0, 24, 1), returns -ERANGE, no data written - * - in_addr_prefix_nth(AF_INET, 255.255.255.0, 0, 1), returns -ERANGE, no data written - * - in_addr_prefix_nth(AF_INET6, 2001:db8, 64, 0xff00) returns 1, writes 2001:0db8:0000:ff00:: to u - */ -int in_addr_prefix_nth(int family, union in_addr_union *u, unsigned prefixlen, uint64_t nth) { - assert(u); - - if (prefixlen <= 0) - return -ERANGE; - - if (nth == 0) - return 1; - - if (family == AF_INET) { - uint32_t c, n, t; - if (prefixlen > 32) - prefixlen = 32; - - c = be32toh(u->in.s_addr); - - t = nth << (32 - prefixlen); - - /* Check for wrap */ - if (c > UINT32_MAX - t) - return -ERANGE; - - n = c + t; - - n &= UINT32_C(0xFFFFFFFF) << (32 - prefixlen); - u->in.s_addr = htobe32(n); - return 1; - } - - if (family == AF_INET6) { - struct in6_addr result = {}; - uint8_t overflow = 0; - uint64_t delta; /* this assumes that we only ever have to up to 1<<64 subnets */ - unsigned start_byte = (prefixlen - 1) / 8; - - if (prefixlen > 128) - prefixlen = 128; - - /* First calculate what we have to add */ - delta = nth << ((128 - prefixlen) % 8); - - for (unsigned i = 16; i > 0; i--) { - unsigned j = i - 1; - unsigned d = 0; - - if (j <= start_byte) { - int16_t t; - - d = delta & 0xFF; - delta >>= 8; - - t = u->in6.s6_addr[j] + d + overflow; - overflow = t > UINT8_MAX ? t - UINT8_MAX : 0; - - result.s6_addr[j] = (uint8_t)t; - } else - result.s6_addr[j] = u->in6.s6_addr[j]; - } - - if (overflow || delta != 0) - return -ERANGE; - - u->in6 = result; - return 1; - } - - return -EAFNOSUPPORT; -} - -int in_addr_random_prefix( - int family, - union in_addr_union *u, - unsigned prefixlen_fixed_part, - unsigned prefixlen) { - - assert(u); - - /* Random network part of an address by one. */ - - if (prefixlen <= 0) - return 0; - - if (family == AF_INET) { - uint32_t c, n; - - if (prefixlen_fixed_part > 32) - prefixlen_fixed_part = 32; - if (prefixlen > 32) - prefixlen = 32; - if (prefixlen_fixed_part >= prefixlen) - return -EINVAL; - - c = be32toh(u->in.s_addr); - c &= ((UINT32_C(1) << prefixlen_fixed_part) - 1) << (32 - prefixlen_fixed_part); - - random_bytes(&n, sizeof(n)); - n &= ((UINT32_C(1) << (prefixlen - prefixlen_fixed_part)) - 1) << (32 - prefixlen); - - u->in.s_addr = htobe32(n | c); - return 1; - } - - if (family == AF_INET6) { - struct in6_addr n; - unsigned i, j; - - if (prefixlen_fixed_part > 128) - prefixlen_fixed_part = 128; - if (prefixlen > 128) - prefixlen = 128; - if (prefixlen_fixed_part >= prefixlen) - return -EINVAL; - - random_bytes(&n, sizeof(n)); - - for (i = 0; i < 16; i++) { - uint8_t mask_fixed_part = 0, mask = 0; - - if (i < (prefixlen_fixed_part + 7) / 8) { - if (i < prefixlen_fixed_part / 8) - mask_fixed_part = 0xffu; - else { - j = prefixlen_fixed_part % 8; - mask_fixed_part = ((UINT8_C(1) << (j + 1)) - 1) << (8 - j); - } - } - - if (i < (prefixlen + 7) / 8) { - if (i < prefixlen / 8) - mask = 0xffu ^ mask_fixed_part; - else { - j = prefixlen % 8; - mask = (((UINT8_C(1) << (j + 1)) - 1) << (8 - j)) ^ mask_fixed_part; - } - } - - u->in6.s6_addr[i] &= mask_fixed_part; - u->in6.s6_addr[i] |= n.s6_addr[i] & mask; - } - - return 1; - } - - return -EAFNOSUPPORT; -} -#endif /* NM_IGNORED */ - -int in_addr_to_string(int family, const union in_addr_union *u, char **ret) { - _cleanup_free_ char *x = NULL; - size_t l; - - assert(u); - assert(ret); - - if (family == AF_INET) - l = INET_ADDRSTRLEN; - else if (family == AF_INET6) - l = INET6_ADDRSTRLEN; - else - return -EAFNOSUPPORT; - - x = new(char, l); - if (!x) - return -ENOMEM; - - errno = 0; - if (!inet_ntop(family, u, x, l)) - return errno_or_else(EINVAL); - - *ret = TAKE_PTR(x); - return 0; -} - -#if 0 /* NM_IGNORED */ -int in_addr_prefix_to_string(int family, const union in_addr_union *u, unsigned prefixlen, char **ret) { - _cleanup_free_ char *x = NULL; - char *p; - size_t l; - - assert(u); - assert(ret); - - if (family == AF_INET) - l = INET_ADDRSTRLEN + 3; - else if (family == AF_INET6) - l = INET6_ADDRSTRLEN + 4; - else - return -EAFNOSUPPORT; - - if (prefixlen > FAMILY_ADDRESS_SIZE(family) * 8) - return -EINVAL; - - x = new(char, l); - if (!x) - return -ENOMEM; - - errno = 0; - if (!inet_ntop(family, u, x, l)) - return errno_or_else(EINVAL); - - p = x + strlen(x); - l -= strlen(x); - (void) strpcpyf(&p, l, "/%u", prefixlen); - - *ret = TAKE_PTR(x); - return 0; -} -#endif /* NM_IGNORED */ - -int in_addr_port_ifindex_name_to_string(int family, const union in_addr_union *u, uint16_t port, int ifindex, const char *server_name, char **ret) { - _cleanup_free_ char *ip_str = NULL, *x = NULL; - int r; - - assert(IN_SET(family, AF_INET, AF_INET6)); - assert(u); - assert(ret); - - /* Much like in_addr_to_string(), but optionally appends the zone interface index to the address, to properly - * handle IPv6 link-local addresses. */ - - r = in_addr_to_string(family, u, &ip_str); - if (r < 0) - return r; - - if (family == AF_INET6) { - r = in_addr_is_link_local(family, u); - if (r < 0) - return r; - if (r == 0) - ifindex = 0; - } else - ifindex = 0; /* For IPv4 address, ifindex is always ignored. */ - - if (port == 0 && ifindex == 0 && isempty(server_name)) { - *ret = TAKE_PTR(ip_str); - return 0; - } - - const char *separator = isempty(server_name) ? "" : "#"; - server_name = strempty(server_name); - - if (port > 0) { - if (family == AF_INET6) { - if (ifindex > 0) - r = asprintf(&x, "[%s]:%"PRIu16"%%%i%s%s", ip_str, port, ifindex, separator, server_name); - else - r = asprintf(&x, "[%s]:%"PRIu16"%s%s", ip_str, port, separator, server_name); - } else - r = asprintf(&x, "%s:%"PRIu16"%s%s", ip_str, port, separator, server_name); - } else { - if (ifindex > 0) - r = asprintf(&x, "%s%%%i%s%s", ip_str, ifindex, separator, server_name); - else { - x = strjoin(ip_str, separator, server_name); - r = x ? 0 : -ENOMEM; - } - } - if (r < 0) - return -ENOMEM; - - *ret = TAKE_PTR(x); - return 0; -} - -int in_addr_from_string(int family, const char *s, union in_addr_union *ret) { - union in_addr_union buffer; - assert(s); - - if (!IN_SET(family, AF_INET, AF_INET6)) - return -EAFNOSUPPORT; - - errno = 0; - if (inet_pton(family, s, ret ?: &buffer) <= 0) - return errno_or_else(EINVAL); - - return 0; -} - -int in_addr_from_string_auto(const char *s, int *ret_family, union in_addr_union *ret) { - int r; - - assert(s); - - r = in_addr_from_string(AF_INET, s, ret); - if (r >= 0) { - if (ret_family) - *ret_family = AF_INET; - return 0; - } - - r = in_addr_from_string(AF_INET6, s, ret); - if (r >= 0) { - if (ret_family) - *ret_family = AF_INET6; - return 0; - } - - return -EINVAL; -} - -unsigned char in4_addr_netmask_to_prefixlen(const struct in_addr *addr) { - assert(addr); - - return 32U - u32ctz(be32toh(addr->s_addr)); -} - -struct in_addr* in4_addr_prefixlen_to_netmask(struct in_addr *addr, unsigned char prefixlen) { - assert(addr); - assert(prefixlen <= 32); - - /* Shifting beyond 32 is not defined, handle this specially. */ - if (prefixlen == 0) - addr->s_addr = 0; - else - addr->s_addr = htobe32((0xffffffff << (32 - prefixlen)) & 0xffffffff); - - return addr; -} - -int in4_addr_default_prefixlen(const struct in_addr *addr, unsigned char *prefixlen) { - uint8_t msb_octet = *(uint8_t*) addr; - - /* addr may not be aligned, so make sure we only access it byte-wise */ - - assert(addr); - assert(prefixlen); - - if (msb_octet < 128) - /* class A, leading bits: 0 */ - *prefixlen = 8; - else if (msb_octet < 192) - /* class B, leading bits 10 */ - *prefixlen = 16; - else if (msb_octet < 224) - /* class C, leading bits 110 */ - *prefixlen = 24; - else - /* class D or E, no default prefixlen */ - return -ERANGE; - - return 0; -} - -int in4_addr_default_subnet_mask(const struct in_addr *addr, struct in_addr *mask) { - unsigned char prefixlen; - int r; - - assert(addr); - assert(mask); - - r = in4_addr_default_prefixlen(addr, &prefixlen); - if (r < 0) - return r; - - in4_addr_prefixlen_to_netmask(mask, prefixlen); - return 0; -} - -#if 0 /* NM_IGNORED */ -int in_addr_mask(int family, union in_addr_union *addr, unsigned char prefixlen) { - assert(addr); - - if (family == AF_INET) { - struct in_addr mask; - - if (!in4_addr_prefixlen_to_netmask(&mask, prefixlen)) - return -EINVAL; - - addr->in.s_addr &= mask.s_addr; - return 0; - } - - if (family == AF_INET6) { - unsigned i; - - for (i = 0; i < 16; i++) { - uint8_t mask; - - if (prefixlen >= 8) { - mask = 0xFF; - prefixlen -= 8; - } else { - mask = 0xFF << (8 - prefixlen); - prefixlen = 0; - } - - addr->in6.s6_addr[i] &= mask; - } - - return 0; - } - - return -EAFNOSUPPORT; -} - -int in_addr_prefix_covers(int family, - const union in_addr_union *prefix, - unsigned char prefixlen, - const union in_addr_union *address) { - - union in_addr_union masked_prefix, masked_address; - int r; - - assert(prefix); - assert(address); - - masked_prefix = *prefix; - r = in_addr_mask(family, &masked_prefix, prefixlen); - if (r < 0) - return r; - - masked_address = *address; - r = in_addr_mask(family, &masked_address, prefixlen); - if (r < 0) - return r; - - return in_addr_equal(family, &masked_prefix, &masked_address); -} - -int in_addr_parse_prefixlen(int family, const char *p, unsigned char *ret) { - uint8_t u; - int r; - - if (!IN_SET(family, AF_INET, AF_INET6)) - return -EAFNOSUPPORT; - - r = safe_atou8(p, &u); - if (r < 0) - return r; - - if (u > FAMILY_ADDRESS_SIZE(family) * 8) - return -ERANGE; - - *ret = u; - return 0; -} - -int in_addr_prefix_from_string( - const char *p, - int family, - union in_addr_union *ret_prefix, - unsigned char *ret_prefixlen) { - - _cleanup_free_ char *str = NULL; - union in_addr_union buffer; - const char *e, *l; - unsigned char k; - int r; - - assert(p); - - if (!IN_SET(family, AF_INET, AF_INET6)) - return -EAFNOSUPPORT; - - e = strchr(p, '/'); - if (e) { - str = strndup(p, e - p); - if (!str) - return -ENOMEM; - - l = str; - } else - l = p; - - r = in_addr_from_string(family, l, &buffer); - if (r < 0) - return r; - - if (e) { - r = in_addr_parse_prefixlen(family, e+1, &k); - if (r < 0) - return r; - } else - k = FAMILY_ADDRESS_SIZE(family) * 8; - - if (ret_prefix) - *ret_prefix = buffer; - if (ret_prefixlen) - *ret_prefixlen = k; - - return 0; -} - -int in_addr_prefix_from_string_auto_internal( - const char *p, - InAddrPrefixLenMode mode, - int *ret_family, - union in_addr_union *ret_prefix, - unsigned char *ret_prefixlen) { - - _cleanup_free_ char *str = NULL; - union in_addr_union buffer; - const char *e, *l; - unsigned char k; - int family, r; - - assert(p); - - e = strchr(p, '/'); - if (e) { - str = strndup(p, e - p); - if (!str) - return -ENOMEM; - - l = str; - } else - l = p; - - r = in_addr_from_string_auto(l, &family, &buffer); - if (r < 0) - return r; - - if (e) { - r = in_addr_parse_prefixlen(family, e+1, &k); - if (r < 0) - return r; - } else - switch (mode) { - case PREFIXLEN_FULL: - k = FAMILY_ADDRESS_SIZE(family) * 8; - break; - case PREFIXLEN_REFUSE: - return -ENOANO; /* To distinguish this error from others. */ - case PREFIXLEN_LEGACY: - if (family == AF_INET) { - r = in4_addr_default_prefixlen(&buffer.in, &k); - if (r < 0) - return r; - } else - k = 0; - break; - default: - assert_not_reached("Invalid prefixlen mode"); - } - - if (ret_family) - *ret_family = family; - if (ret_prefix) - *ret_prefix = buffer; - if (ret_prefixlen) - *ret_prefixlen = k; - - return 0; - -} - -static void in_addr_data_hash_func(const struct in_addr_data *a, struct siphash *state) { - siphash24_compress(&a->family, sizeof(a->family), state); - siphash24_compress(&a->address, FAMILY_ADDRESS_SIZE(a->family), state); -} - -static int in_addr_data_compare_func(const struct in_addr_data *x, const struct in_addr_data *y) { - int r; - - r = CMP(x->family, y->family); - if (r != 0) - return r; - - return memcmp(&x->address, &y->address, FAMILY_ADDRESS_SIZE(x->family)); -} - -DEFINE_HASH_OPS(in_addr_data_hash_ops, struct in_addr_data, in_addr_data_hash_func, in_addr_data_compare_func); - -void in6_addr_hash_func(const struct in6_addr *addr, struct siphash *state) { - assert(addr); - - siphash24_compress(addr, sizeof(*addr), state); -} - -int in6_addr_compare_func(const struct in6_addr *a, const struct in6_addr *b) { - return memcmp(a, b, sizeof(*a)); -} - -DEFINE_HASH_OPS(in6_addr_hash_ops, struct in6_addr, in6_addr_hash_func, in6_addr_compare_func); -#endif /* NM_IGNORED */ diff --git a/shared/systemd/src/basic/in-addr-util.h b/shared/systemd/src/basic/in-addr-util.h deleted file mode 100644 index 24308b70..00000000 --- a/shared/systemd/src/basic/in-addr-util.h +++ /dev/null @@ -1,89 +0,0 @@ -/* SPDX-License-Identifier: LGPL-2.1-or-later */ -#pragma once - -#include <netinet/in.h> -#include <stddef.h> -#include <sys/socket.h> - -#include "hash-funcs.h" -#include "macro.h" -#include "util.h" - -union in_addr_union { - struct in_addr in; - struct in6_addr in6; - uint8_t bytes[CONST_MAX(sizeof(struct in_addr), sizeof(struct in6_addr))]; -}; - -struct in_addr_data { - int family; - union in_addr_union address; -}; - -bool in4_addr_is_null(const struct in_addr *a); -int in_addr_is_null(int family, const union in_addr_union *u); - -int in_addr_is_multicast(int family, const union in_addr_union *u); - -bool in4_addr_is_link_local(const struct in_addr *a); -int in_addr_is_link_local(int family, const union in_addr_union *u); -bool in6_addr_is_link_local_all_nodes(const struct in6_addr *a); - -bool in4_addr_is_localhost(const struct in_addr *a); -int in_addr_is_localhost(int family, const union in_addr_union *u); - -bool in4_addr_is_local_multicast(const struct in_addr *a); -bool in4_addr_is_non_local(const struct in_addr *a); - -bool in4_addr_equal(const struct in_addr *a, const struct in_addr *b); -int in_addr_equal(int family, const union in_addr_union *a, const union in_addr_union *b); -int in_addr_prefix_intersect(int family, const union in_addr_union *a, unsigned aprefixlen, const union in_addr_union *b, unsigned bprefixlen); -int in_addr_prefix_next(int family, union in_addr_union *u, unsigned prefixlen); -int in_addr_prefix_nth(int family, union in_addr_union *u, unsigned prefixlen, uint64_t nth); -int in_addr_random_prefix(int family, union in_addr_union *u, unsigned prefixlen_fixed_part, unsigned prefixlen); -int in_addr_to_string(int family, const union in_addr_union *u, char **ret); -int in_addr_prefix_to_string(int family, const union in_addr_union *u, unsigned prefixlen, char **ret); -int in_addr_port_ifindex_name_to_string(int family, const union in_addr_union *u, uint16_t port, int ifindex, const char *server_name, char **ret); -static inline int in_addr_ifindex_to_string(int family, const union in_addr_union *u, int ifindex, char **ret) { - return in_addr_port_ifindex_name_to_string(family, u, 0, ifindex, NULL, ret); -} -static inline int in_addr_port_to_string(int family, const union in_addr_union *u, uint16_t port, char **ret) { - return in_addr_port_ifindex_name_to_string(family, u, port, 0, NULL, ret); -} -int in_addr_from_string(int family, const char *s, union in_addr_union *ret); -int in_addr_from_string_auto(const char *s, int *ret_family, union in_addr_union *ret); - -unsigned char in4_addr_netmask_to_prefixlen(const struct in_addr *addr); -struct in_addr* in4_addr_prefixlen_to_netmask(struct in_addr *addr, unsigned char prefixlen); -int in4_addr_default_prefixlen(const struct in_addr *addr, unsigned char *prefixlen); -int in4_addr_default_subnet_mask(const struct in_addr *addr, struct in_addr *mask); -int in_addr_mask(int family, union in_addr_union *addr, unsigned char prefixlen); -int in_addr_prefix_covers(int family, const union in_addr_union *prefix, unsigned char prefixlen, const union in_addr_union *address); -int in_addr_parse_prefixlen(int family, const char *p, unsigned char *ret); -int in_addr_prefix_from_string(const char *p, int family, union in_addr_union *ret_prefix, unsigned char *ret_prefixlen); - -typedef enum InAddrPrefixLenMode { - PREFIXLEN_FULL, /* Default to prefixlen of address size, 32 for IPv4 or 128 for IPv6, if not specified. */ - PREFIXLEN_REFUSE, /* Fail with -ENOANO if prefixlen is not specified. */ - PREFIXLEN_LEGACY, /* Default to legacy default prefixlen calculation from address if not specified. */ -} InAddrPrefixLenMode; - -int in_addr_prefix_from_string_auto_internal(const char *p, InAddrPrefixLenMode mode, int *ret_family, union in_addr_union *ret_prefix, unsigned char *ret_prefixlen); -static inline int in_addr_prefix_from_string_auto(const char *p, int *ret_family, union in_addr_union *ret_prefix, unsigned char *ret_prefixlen) { - return in_addr_prefix_from_string_auto_internal(p, PREFIXLEN_FULL, ret_family, ret_prefix, ret_prefixlen); -} - -static inline size_t FAMILY_ADDRESS_SIZE(int family) { - assert(IN_SET(family, AF_INET, AF_INET6)); - return family == AF_INET6 ? 16 : 4; -} - -/* Workaround for clang, explicitly specify the maximum-size element here. - * See also oss-fuzz#11344. */ -#define IN_ADDR_NULL ((union in_addr_union) { .in6 = {} }) - -void in6_addr_hash_func(const struct in6_addr *addr, struct siphash *state); -int in6_addr_compare_func(const struct in6_addr *a, const struct in6_addr *b); - -extern const struct hash_ops in_addr_data_hash_ops; -extern const struct hash_ops in6_addr_hash_ops; diff --git a/shared/systemd/src/basic/io-util.c b/shared/systemd/src/basic/io-util.c deleted file mode 100644 index f09c7fdd..00000000 --- a/shared/systemd/src/basic/io-util.c +++ /dev/null @@ -1,343 +0,0 @@ -/* SPDX-License-Identifier: LGPL-2.1-or-later */ - -#include "nm-sd-adapt-shared.h" - -#include <errno.h> -#include <limits.h> -#include <poll.h> -#include <stdio.h> -#include <unistd.h> - -#include "io-util.h" -#include "string-util.h" -#include "time-util.h" - -#if 0 /* NM_IGNORED */ -int flush_fd(int fd) { - int count = 0; - - /* Read from the specified file descriptor, until POLLIN is not set anymore, throwing away everything - * read. Note that some file descriptors (notable IP sockets) will trigger POLLIN even when no data can be read - * (due to IP packet checksum mismatches), hence this function is only safe to be non-blocking if the fd used - * was set to non-blocking too. */ - - for (;;) { - char buf[LINE_MAX]; - ssize_t l; - int r; - - r = fd_wait_for_event(fd, POLLIN, 0); - if (r < 0) { - if (r == -EINTR) - continue; - - return r; - } - if (r == 0) - return count; - - l = read(fd, buf, sizeof(buf)); - if (l < 0) { - if (errno == EINTR) - continue; - - if (errno == EAGAIN) - return count; - - return -errno; - } else if (l == 0) - return count; - - count += (int) l; - } -} -#endif /* NM_IGNORED */ - -ssize_t loop_read(int fd, void *buf, size_t nbytes, bool do_poll) { - uint8_t *p = buf; - ssize_t n = 0; - - assert(fd >= 0); - assert(buf); - - /* If called with nbytes == 0, let's call read() at least - * once, to validate the operation */ - - if (nbytes > (size_t) SSIZE_MAX) - return -EINVAL; - - do { - ssize_t k; - - k = read(fd, p, nbytes); - if (k < 0) { - if (errno == EINTR) - continue; - - if (errno == EAGAIN && do_poll) { - - /* We knowingly ignore any return value here, - * and expect that any error/EOF is reported - * via read() */ - - (void) fd_wait_for_event(fd, POLLIN, USEC_INFINITY); - continue; - } - - return n > 0 ? n : -errno; - } - - if (k == 0) - return n; - - assert((size_t) k <= nbytes); - - p += k; - nbytes -= k; - n += k; - } while (nbytes > 0); - - return n; -} - -int loop_read_exact(int fd, void *buf, size_t nbytes, bool do_poll) { - ssize_t n; - - n = loop_read(fd, buf, nbytes, do_poll); - if (n < 0) - return (int) n; - if ((size_t) n != nbytes) - return -EIO; - - return 0; -} - -#if 0 /* NM_IGNORED */ -int loop_write(int fd, const void *buf, size_t nbytes, bool do_poll) { - const uint8_t *p = buf; - - assert(fd >= 0); - assert(buf); - - if (_unlikely_(nbytes > (size_t) SSIZE_MAX)) - return -EINVAL; - - do { - ssize_t k; - - k = write(fd, p, nbytes); - if (k < 0) { - if (errno == EINTR) - continue; - - if (errno == EAGAIN && do_poll) { - /* We knowingly ignore any return value here, - * and expect that any error/EOF is reported - * via write() */ - - (void) fd_wait_for_event(fd, POLLOUT, USEC_INFINITY); - continue; - } - - return -errno; - } - - if (_unlikely_(nbytes > 0 && k == 0)) /* Can't really happen */ - return -EIO; - - assert((size_t) k <= nbytes); - - p += k; - nbytes -= k; - } while (nbytes > 0); - - return 0; -} - -int pipe_eof(int fd) { - int r; - - r = fd_wait_for_event(fd, POLLIN, 0); - if (r <= 0) - return r; - - return !!(r & POLLHUP); -} -#endif /* NM_IGNORED */ - -int fd_wait_for_event(int fd, int event, usec_t t) { - - struct pollfd pollfd = { - .fd = fd, - .events = event, - }; - - struct timespec ts; - int r; - - r = ppoll(&pollfd, 1, t == USEC_INFINITY ? NULL : timespec_store(&ts, t), NULL); - if (r < 0) - return -errno; - if (r == 0) - return 0; - - if (pollfd.revents & POLLNVAL) - return -EBADF; - - return pollfd.revents; -} - -#if 0 /* NM_IGNORED */ -static size_t nul_length(const uint8_t *p, size_t sz) { - size_t n = 0; - - while (sz > 0) { - if (*p != 0) - break; - - n++; - p++; - sz--; - } - - return n; -} - -ssize_t sparse_write(int fd, const void *p, size_t sz, size_t run_length) { - const uint8_t *q, *w, *e; - ssize_t l; - - q = w = p; - e = q + sz; - while (q < e) { - size_t n; - - n = nul_length(q, e - q); - - /* If there are more than the specified run length of - * NUL bytes, or if this is the beginning or the end - * of the buffer, then seek instead of write */ - if ((n > run_length) || - (n > 0 && q == p) || - (n > 0 && q + n >= e)) { - if (q > w) { - l = write(fd, w, q - w); - if (l < 0) - return -errno; - if (l != q -w) - return -EIO; - } - - if (lseek(fd, n, SEEK_CUR) == (off_t) -1) - return -errno; - - q += n; - w = q; - } else if (n > 0) - q += n; - else - q++; - } - - if (q > w) { - l = write(fd, w, q - w); - if (l < 0) - return -errno; - if (l != q - w) - return -EIO; - } - - return q - (const uint8_t*) p; -} - -char* set_iovec_string_field(struct iovec *iovec, size_t *n_iovec, const char *field, const char *value) { - char *x; - - x = strjoin(field, value); - if (x) - iovec[(*n_iovec)++] = IOVEC_MAKE_STRING(x); - return x; -} - -char* set_iovec_string_field_free(struct iovec *iovec, size_t *n_iovec, const char *field, char *value) { - char *x; - - x = set_iovec_string_field(iovec, n_iovec, field, value); - free(value); - return x; -} - -struct iovec_wrapper *iovw_new(void) { - return malloc0(sizeof(struct iovec_wrapper)); -} - -void iovw_free_contents(struct iovec_wrapper *iovw, bool free_vectors) { - if (free_vectors) - for (size_t i = 0; i < iovw->count; i++) - free(iovw->iovec[i].iov_base); - - iovw->iovec = mfree(iovw->iovec); - iovw->count = 0; - iovw->size_bytes = 0; -} - -struct iovec_wrapper *iovw_free_free(struct iovec_wrapper *iovw) { - iovw_free_contents(iovw, true); - - return mfree(iovw); -} - -struct iovec_wrapper *iovw_free(struct iovec_wrapper *iovw) { - iovw_free_contents(iovw, false); - - return mfree(iovw); -} - -int iovw_put(struct iovec_wrapper *iovw, void *data, size_t len) { - if (iovw->count >= IOV_MAX) - return -E2BIG; - - if (!GREEDY_REALLOC(iovw->iovec, iovw->size_bytes, iovw->count + 1)) - return -ENOMEM; - - iovw->iovec[iovw->count++] = IOVEC_MAKE(data, len); - return 0; -} - -int iovw_put_string_field(struct iovec_wrapper *iovw, const char *field, const char *value) { - _cleanup_free_ char *x = NULL; - int r; - - x = strjoin(field, value); - if (!x) - return -ENOMEM; - - r = iovw_put(iovw, x, strlen(x)); - if (r >= 0) - TAKE_PTR(x); - - return r; -} - -int iovw_put_string_field_free(struct iovec_wrapper *iovw, const char *field, char *value) { - _cleanup_free_ _unused_ char *free_ptr = value; - - return iovw_put_string_field(iovw, field, value); -} - -void iovw_rebase(struct iovec_wrapper *iovw, char *old, char *new) { - size_t i; - - for (i = 0; i < iovw->count; i++) - iovw->iovec[i].iov_base = (char *)iovw->iovec[i].iov_base - old + new; -} - -size_t iovw_size(struct iovec_wrapper *iovw) { - size_t n = 0, i; - - for (i = 0; i < iovw->count; i++) - n += iovw->iovec[i].iov_len; - - return n; -} -#endif /* NM_IGNORED */ diff --git a/shared/systemd/src/basic/io-util.h b/shared/systemd/src/basic/io-util.h deleted file mode 100644 index d817714b..00000000 --- a/shared/systemd/src/basic/io-util.h +++ /dev/null @@ -1,92 +0,0 @@ -/* SPDX-License-Identifier: LGPL-2.1-or-later */ -#pragma once - -#include <stdbool.h> -#include <stddef.h> -#include <stdint.h> -#include <sys/types.h> -#include <sys/uio.h> - -#include "macro.h" -#include "time-util.h" - -int flush_fd(int fd); - -ssize_t loop_read(int fd, void *buf, size_t nbytes, bool do_poll); -int loop_read_exact(int fd, void *buf, size_t nbytes, bool do_poll); -int loop_write(int fd, const void *buf, size_t nbytes, bool do_poll); - -int pipe_eof(int fd); - -int fd_wait_for_event(int fd, int event, usec_t timeout); - -ssize_t sparse_write(int fd, const void *p, size_t sz, size_t run_length); - -static inline size_t IOVEC_TOTAL_SIZE(const struct iovec *i, size_t n) { - size_t j, r = 0; - - for (j = 0; j < n; j++) - r += i[j].iov_len; - - return r; -} - -static inline size_t IOVEC_INCREMENT(struct iovec *i, size_t n, size_t k) { - size_t j; - - for (j = 0; j < n; j++) { - size_t sub; - - if (_unlikely_(k <= 0)) - break; - - sub = MIN(i[j].iov_len, k); - i[j].iov_len -= sub; - i[j].iov_base = (uint8_t*) i[j].iov_base + sub; - k -= sub; - } - - return k; -} - -static inline bool FILE_SIZE_VALID(uint64_t l) { - /* ftruncate() and friends take an unsigned file size, but actually cannot deal with file sizes larger than - * 2^63 since the kernel internally handles it as signed value. This call allows checking for this early. */ - - return (l >> 63) == 0; -} - -static inline bool FILE_SIZE_VALID_OR_INFINITY(uint64_t l) { - - /* Same as above, but allows one extra value: -1 as indication for infinity. */ - - if (l == (uint64_t) -1) - return true; - - return FILE_SIZE_VALID(l); - -} - -#define IOVEC_INIT(base, len) { .iov_base = (base), .iov_len = (len) } -#define IOVEC_MAKE(base, len) (struct iovec) IOVEC_INIT(base, len) -#define IOVEC_INIT_STRING(string) IOVEC_INIT((char*) string, strlen(string)) -#define IOVEC_MAKE_STRING(string) (struct iovec) IOVEC_INIT_STRING(string) - -char* set_iovec_string_field(struct iovec *iovec, size_t *n_iovec, const char *field, const char *value); -char* set_iovec_string_field_free(struct iovec *iovec, size_t *n_iovec, const char *field, char *value); - -struct iovec_wrapper { - struct iovec *iovec; - size_t count; - size_t size_bytes; -}; - -struct iovec_wrapper *iovw_new(void); -struct iovec_wrapper *iovw_free(struct iovec_wrapper *iovw); -struct iovec_wrapper *iovw_free_free(struct iovec_wrapper *iovw); -void iovw_free_contents(struct iovec_wrapper *iovw, bool free_vectors); -int iovw_put(struct iovec_wrapper *iovw, void *data, size_t len); -int iovw_put_string_field(struct iovec_wrapper *iovw, const char *field, const char *value); -int iovw_put_string_field_free(struct iovec_wrapper *iovw, const char *field, char *value); -void iovw_rebase(struct iovec_wrapper *iovw, char *old, char *new); -size_t iovw_size(struct iovec_wrapper *iovw); diff --git a/shared/systemd/src/basic/list.h b/shared/systemd/src/basic/list.h deleted file mode 100644 index 256b7187..00000000 --- a/shared/systemd/src/basic/list.h +++ /dev/null @@ -1,186 +0,0 @@ -/* SPDX-License-Identifier: LGPL-2.1-or-later */ -#pragma once - -#include "macro.h" - -/* The head of the linked list. Use this in the structure that shall - * contain the head of the linked list */ -#define LIST_HEAD(t,name) \ - t *name - -/* The pointers in the linked list's items. Use this in the item structure */ -#define LIST_FIELDS(t,name) \ - t *name##_next, *name##_prev - -/* Initialize the list's head */ -#define LIST_HEAD_INIT(head) \ - do { \ - (head) = NULL; \ - } while (false) - -/* Initialize a list item */ -#define LIST_INIT(name,item) \ - do { \ - typeof(*(item)) *_item = (item); \ - assert(_item); \ - _item->name##_prev = _item->name##_next = NULL; \ - } while (false) - -/* Prepend an item to the list */ -#define LIST_PREPEND(name,head,item) \ - do { \ - typeof(*(head)) **_head = &(head), *_item = (item); \ - assert(_item); \ - if ((_item->name##_next = *_head)) \ - _item->name##_next->name##_prev = _item; \ - _item->name##_prev = NULL; \ - *_head = _item; \ - } while (false) - -/* Append an item to the list */ -#define LIST_APPEND(name,head,item) \ - do { \ - typeof(*(head)) **_hhead = &(head), *_tail; \ - LIST_FIND_TAIL(name, *_hhead, _tail); \ - LIST_INSERT_AFTER(name, *_hhead, _tail, item); \ - } while (false) - -/* Remove an item from the list */ -#define LIST_REMOVE(name,head,item) \ - do { \ - typeof(*(head)) **_head = &(head), *_item = (item); \ - assert(_item); \ - if (_item->name##_next) \ - _item->name##_next->name##_prev = _item->name##_prev; \ - if (_item->name##_prev) \ - _item->name##_prev->name##_next = _item->name##_next; \ - else { \ - assert(*_head == _item); \ - *_head = _item->name##_next; \ - } \ - _item->name##_next = _item->name##_prev = NULL; \ - } while (false) - -/* Find the head of the list */ -#define LIST_FIND_HEAD(name,item,head) \ - do { \ - typeof(*(item)) *_item = (item); \ - if (!_item) \ - (head) = NULL; \ - else { \ - while (_item->name##_prev) \ - _item = _item->name##_prev; \ - (head) = _item; \ - } \ - } while (false) - -/* Find the tail of the list */ -#define LIST_FIND_TAIL(name,item,tail) \ - do { \ - typeof(*(item)) *_item = (item); \ - if (!_item) \ - (tail) = NULL; \ - else { \ - while (_item->name##_next) \ - _item = _item->name##_next; \ - (tail) = _item; \ - } \ - } while (false) - -/* Insert an item after another one (a = where, b = what) */ -#define LIST_INSERT_AFTER(name,head,a,b) \ - do { \ - typeof(*(head)) **_head = &(head), *_a = (a), *_b = (b); \ - assert(_b); \ - if (!_a) { \ - if ((_b->name##_next = *_head)) \ - _b->name##_next->name##_prev = _b; \ - _b->name##_prev = NULL; \ - *_head = _b; \ - } else { \ - if ((_b->name##_next = _a->name##_next)) \ - _b->name##_next->name##_prev = _b; \ - _b->name##_prev = _a; \ - _a->name##_next = _b; \ - } \ - } while (false) - -/* Insert an item before another one (a = where, b = what) */ -#define LIST_INSERT_BEFORE(name,head,a,b) \ - do { \ - typeof(*(head)) **_head = &(head), *_a = (a), *_b = (b); \ - assert(_b); \ - if (!_a) { \ - if (!*_head) { \ - _b->name##_next = NULL; \ - _b->name##_prev = NULL; \ - *_head = _b; \ - } else { \ - typeof(*(head)) *_tail = (head); \ - while (_tail->name##_next) \ - _tail = _tail->name##_next; \ - _b->name##_next = NULL; \ - _b->name##_prev = _tail; \ - _tail->name##_next = _b; \ - } \ - } else { \ - if ((_b->name##_prev = _a->name##_prev)) \ - _b->name##_prev->name##_next = _b; \ - else \ - *_head = _b; \ - _b->name##_next = _a; \ - _a->name##_prev = _b; \ - } \ - } while (false) - -#define LIST_JUST_US(name,item) \ - (!(item)->name##_prev && !(item)->name##_next) \ - -#define LIST_FOREACH(name,i,head) \ - for ((i) = (head); (i); (i) = (i)->name##_next) - -#define LIST_FOREACH_SAFE(name,i,n,head) \ - for ((i) = (head); (i) && (((n) = (i)->name##_next), 1); (i) = (n)) - -#define LIST_FOREACH_BEFORE(name,i,p) \ - for ((i) = (p)->name##_prev; (i); (i) = (i)->name##_prev) - -#define LIST_FOREACH_AFTER(name,i,p) \ - for ((i) = (p)->name##_next; (i); (i) = (i)->name##_next) - -/* Iterate through all the members of the list p is included in, but skip over p */ -#define LIST_FOREACH_OTHERS(name,i,p) \ - for (({ \ - (i) = (p); \ - while ((i) && (i)->name##_prev) \ - (i) = (i)->name##_prev; \ - if ((i) == (p)) \ - (i) = (p)->name##_next; \ - }); \ - (i); \ - (i) = (i)->name##_next == (p) ? (p)->name##_next : (i)->name##_next) - -/* Loop starting from p->next until p->prev. - p can be adjusted meanwhile. */ -#define LIST_LOOP_BUT_ONE(name,i,head,p) \ - for ((i) = (p)->name##_next ? (p)->name##_next : (head); \ - (i) != (p); \ - (i) = (i)->name##_next ? (i)->name##_next : (head)) - -#define LIST_IS_EMPTY(head) \ - (!(head)) - -/* Join two lists tail to head: a->b, c->d to a->b->c->d and de-initialise second list */ -#define LIST_JOIN(name,a,b) \ - do { \ - assert(b); \ - if (!(a)) \ - (a) = (b); \ - else { \ - typeof(*(a)) *_head = (b), *_tail; \ - LIST_FIND_TAIL(name, (a), _tail); \ - _tail->name##_next = _head; \ - _head->name##_prev = _tail; \ - } \ - (b) = NULL; \ - } while (false) diff --git a/shared/systemd/src/basic/log.h b/shared/systemd/src/basic/log.h deleted file mode 100644 index ee2e4839..00000000 --- a/shared/systemd/src/basic/log.h +++ /dev/null @@ -1,401 +0,0 @@ -/* SPDX-License-Identifier: LGPL-2.1-or-later */ -#pragma once - -#include <stdarg.h> -#include <stdbool.h> -#include <stdlib.h> -#include <syslog.h> - -#include "macro.h" - -/* Some structures we reference but don't want to pull in headers for */ -struct iovec; -struct signalfd_siginfo; - -typedef enum LogRealm { - LOG_REALM_SYSTEMD, - LOG_REALM_UDEV, - _LOG_REALM_MAX, -} LogRealm; - -#ifndef LOG_REALM -# define LOG_REALM LOG_REALM_SYSTEMD -#endif - -typedef enum LogTarget{ - LOG_TARGET_CONSOLE, - LOG_TARGET_CONSOLE_PREFIXED, - LOG_TARGET_KMSG, - LOG_TARGET_JOURNAL, - LOG_TARGET_JOURNAL_OR_KMSG, - LOG_TARGET_SYSLOG, - LOG_TARGET_SYSLOG_OR_KMSG, - LOG_TARGET_AUTO, /* console if stderr is not journal, JOURNAL_OR_KMSG otherwise */ - LOG_TARGET_NULL, - _LOG_TARGET_MAX, - _LOG_TARGET_INVALID = -1 -} LogTarget; - -/* Note to readers: << and >> have lower precedence than & and | */ -#define LOG_REALM_PLUS_LEVEL(realm, level) ((realm) << 10 | (level)) -#define LOG_REALM_REMOVE_LEVEL(realm_level) ((realm_level) >> 10) -#define SYNTHETIC_ERRNO(num) (1 << 30 | (num)) -#define IS_SYNTHETIC_ERRNO(val) ((val) >> 30 & 1) -#define ERRNO_VALUE(val) (abs(val) & 255) - -void log_set_target(LogTarget target); - -void log_set_max_level_realm(LogRealm realm, int level); - -#define log_set_max_level(level) \ - log_set_max_level_realm(LOG_REALM, (level)) - -static inline void log_set_max_level_all_realms(int level) { - for (LogRealm realm = 0; realm < _LOG_REALM_MAX; realm++) - log_set_max_level_realm(realm, level); -} - -void log_set_facility(int facility); - -int log_set_target_from_string(const char *e); -int log_set_max_level_from_string_realm(LogRealm realm, const char *e); -#define log_set_max_level_from_string(e) \ - log_set_max_level_from_string_realm(LOG_REALM, (e)) - -void log_show_color(bool b); -bool log_get_show_color(void) _pure_; -void log_show_location(bool b); -bool log_get_show_location(void) _pure_; -void log_show_time(bool b); -bool log_get_show_time(void) _pure_; -void log_show_tid(bool b); -bool log_get_show_tid(void) _pure_; - -int log_show_color_from_string(const char *e); -int log_show_location_from_string(const char *e); -int log_show_time_from_string(const char *e); -int log_show_tid_from_string(const char *e); - -LogTarget log_get_target(void) _pure_; -#if 0 /* NM_IGNORED */ -int log_get_max_level_realm(LogRealm realm) _pure_; -#endif /* NM_IGNORED */ -#define log_get_max_level() \ - log_get_max_level_realm(LOG_REALM) - -/* Functions below that open and close logs or configure logging based on the - * environment should not be called from library code — this is always a job - * for the application itself. - */ - -#if 0 /* NM_IGNORED */ -assert_cc(STRLEN(__FILE__) > STRLEN(RELATIVE_SOURCE_PATH) + 1); -#define PROJECT_FILE (&__FILE__[STRLEN(RELATIVE_SOURCE_PATH) + 1]) -#endif /* NM_IGNORED */ -#define PROJECT_FILE __FILE__ - -int log_open(void); -void log_close(void); -void log_forget_fds(void); - -void log_parse_environment_realm(LogRealm realm); -void log_parse_environment_cli_realm(LogRealm realm); -#define log_parse_environment() \ - log_parse_environment_realm(LOG_REALM) -#define log_parse_environment_cli() \ - log_parse_environment_cli_realm(LOG_REALM) - -#if 0 /* NM_IGNORED */ -int log_dispatch_internal( - int level, - int error, - const char *file, - int line, - const char *func, - const char *object_field, - const char *object, - const char *extra, - const char *extra_field, - char *buffer); - -int log_internal_realm( - int level, - int error, - const char *file, - int line, - const char *func, - const char *format, ...) _printf_(6,7); -#endif /* NM_IGNORED */ -#define log_internal(level, ...) \ - log_internal_realm(LOG_REALM_PLUS_LEVEL(LOG_REALM, (level)), __VA_ARGS__) - -#define log_object_internal(level, \ - error, \ - file, \ - line, \ - func, \ - object_field, \ - object, \ - extra_field, \ - extra, \ - format, \ - ...) \ - ({ \ - const char *const _object = (object); \ - \ - log_internal_realm((level), \ - (error), \ - file, \ - (line), \ - (func), \ - "%s%s" format, \ - _object ?: "", \ - _object ? ": " : "", \ - ##__VA_ARGS__); \ - }) - -#if 0 /* NM_IGNORED */ -int log_internalv_realm( - int level, - int error, - const char *file, - int line, - const char *func, - const char *format, - va_list ap) _printf_(6,0); -#define log_internalv(level, ...) \ - log_internalv_realm(LOG_REALM_PLUS_LEVEL(LOG_REALM, (level)), __VA_ARGS__) - -/* Realm is fixed to LOG_REALM_SYSTEMD for those */ -int log_object_internalv( - int level, - int error, - const char *file, - int line, - const char *func, - const char *object_field, - const char *object, - const char *extra_field, - const char *extra, - const char *format, - va_list ap) _printf_(10,0); - -int log_object_internal( - int level, - int error, - const char *file, - int line, - const char *func, - const char *object_field, - const char *object, - const char *extra_field, - const char *extra, - const char *format, ...) _printf_(10,11); - -int log_struct_internal( - int level, - int error, - const char *file, - int line, - const char *func, - const char *format, ...) _printf_(6,0) _sentinel_; - -int log_oom_internal( - int level, - const char *file, - int line, - const char *func); -#endif /* NM_IGNORED */ -#define log_oom_internal(realm, file, line, func) \ - log_internal_realm (LOG_REALM_PLUS_LEVEL (realm, LOG_ERR), \ - ENOMEM, file, line, func, "Out of memory.") - -#if 0 /* NM_IGNORED */ -int log_format_iovec( - struct iovec *iovec, - size_t iovec_len, - size_t *n, - bool newline_separator, - int error, - const char *format, - va_list ap) _printf_(6, 0); - -int log_struct_iovec_internal( - int level, - int error, - const char *file, - int line, - const char *func, - const struct iovec *input_iovec, - size_t n_input_iovec); - -/* This modifies the buffer passed! */ -int log_dump_internal( - int level, - int error, - const char *file, - int line, - const char *func, - char *buffer); - -/* Logging for various assertions */ -_noreturn_ void log_assert_failed_realm( - LogRealm realm, - const char *text, - const char *file, - int line, - const char *func); -#define log_assert_failed(text, ...) \ - log_assert_failed_realm(LOG_REALM, (text), __VA_ARGS__) - -_noreturn_ void log_assert_failed_unreachable_realm( - LogRealm realm, - const char *text, - const char *file, - int line, - const char *func); -#define log_assert_failed_unreachable(text, ...) \ - log_assert_failed_unreachable_realm(LOG_REALM, (text), __VA_ARGS__) - -void log_assert_failed_return_realm( - LogRealm realm, - const char *text, - const char *file, - int line, - const char *func); -#define log_assert_failed_return(text, ...) \ - log_assert_failed_return_realm(LOG_REALM, (text), __VA_ARGS__) - -#define log_dispatch(level, error, buffer) \ - log_dispatch_internal(level, error, PROJECT_FILE, __LINE__, __func__, NULL, NULL, NULL, NULL, buffer) -#endif /* NM_IGNORED */ - -/* Logging with level */ -#define log_full_errno_realm(realm, level, error, ...) \ - ({ \ - int _level = (level), _e = (error), _realm = (realm); \ - (log_get_max_level_realm(_realm) >= LOG_PRI(_level)) \ - ? log_internal_realm(LOG_REALM_PLUS_LEVEL(_realm, _level), _e, \ - PROJECT_FILE, __LINE__, __func__, __VA_ARGS__) \ - : -ERRNO_VALUE(_e); \ - }) - -#define log_full_errno(level, error, ...) \ - log_full_errno_realm(LOG_REALM, (level), (error), __VA_ARGS__) - -#define log_full(level, ...) (void) log_full_errno((level), 0, __VA_ARGS__) - -int log_emergency_level(void); - -/* Normal logging */ -#define log_debug(...) log_full_errno(LOG_DEBUG, 0, __VA_ARGS__) -#define log_info(...) log_full(LOG_INFO, __VA_ARGS__) -#define log_notice(...) log_full(LOG_NOTICE, __VA_ARGS__) -#define log_warning(...) log_full(LOG_WARNING, __VA_ARGS__) -#define log_error(...) log_full(LOG_ERR, __VA_ARGS__) -#define log_emergency(...) log_full(log_emergency_level(), __VA_ARGS__) - -/* Logging triggered by an errno-like error */ -#define log_debug_errno(error, ...) log_full_errno(LOG_DEBUG, error, __VA_ARGS__) -#define log_info_errno(error, ...) log_full_errno(LOG_INFO, error, __VA_ARGS__) -#define log_notice_errno(error, ...) log_full_errno(LOG_NOTICE, error, __VA_ARGS__) -#define log_warning_errno(error, ...) log_full_errno(LOG_WARNING, error, __VA_ARGS__) -#define log_error_errno(error, ...) log_full_errno(LOG_ERR, error, __VA_ARGS__) -#define log_emergency_errno(error, ...) log_full_errno(log_emergency_level(), error, __VA_ARGS__) - -#ifdef LOG_TRACE -# define log_trace(...) log_debug(__VA_ARGS__) -#else -# define log_trace(...) do {} while (0) -#endif - -/* Structured logging */ -#define log_struct_errno(level, error, ...) \ - log_struct_internal(LOG_REALM_PLUS_LEVEL(LOG_REALM, level), \ - error, PROJECT_FILE, __LINE__, __func__, __VA_ARGS__, NULL) -#define log_struct(level, ...) log_struct_errno(level, 0, __VA_ARGS__) - -#define log_struct_iovec_errno(level, error, iovec, n_iovec) \ - log_struct_iovec_internal(LOG_REALM_PLUS_LEVEL(LOG_REALM, level), \ - error, PROJECT_FILE, __LINE__, __func__, iovec, n_iovec) -#define log_struct_iovec(level, iovec, n_iovec) log_struct_iovec_errno(level, 0, iovec, n_iovec) - -/* This modifies the buffer passed! */ -#define log_dump(level, buffer) \ - log_dump_internal(LOG_REALM_PLUS_LEVEL(LOG_REALM, level), \ - 0, PROJECT_FILE, __LINE__, __func__, buffer) - -#define log_oom() log_oom_internal(LOG_REALM_PLUS_LEVEL(LOG_REALM, LOG_ERR), PROJECT_FILE, __LINE__, __func__) -#define log_oom_debug() log_oom_internal(LOG_REALM_PLUS_LEVEL(LOG_REALM, LOG_DEBUG), PROJECT_FILE, __LINE__, __func__) - -bool log_on_console(void) _pure_; - -const char *log_target_to_string(LogTarget target) _const_; -LogTarget log_target_from_string(const char *s) _pure_; - -/* Helper to prepare various field for structured logging */ -#define LOG_MESSAGE(fmt, ...) "MESSAGE=" fmt, ##__VA_ARGS__ - -void log_received_signal(int level, const struct signalfd_siginfo *si); - -/* If turned on, any requests for a log target involving "syslog" will be implicitly upgraded to the equivalent journal target */ -void log_set_upgrade_syslog_to_journal(bool b); - -/* If turned on, and log_open() is called, we'll not use STDERR_FILENO for logging ever, but rather open /dev/console */ -void log_set_always_reopen_console(bool b); - -/* If turned on, we'll open the log stream implicitly if needed on each individual log call. This is normally not - * desired as we want to reuse our logging streams. It is useful however */ -void log_set_open_when_needed(bool b); - -/* If turned on, then we'll never use IPC-based logging, i.e. never log to syslog or the journal. We'll only log to - * stderr, the console or kmsg */ -void log_set_prohibit_ipc(bool b); - -int log_dup_console(void); - -#if 0 /* NM_IGNORED */ -int log_syntax_internal( - const char *unit, - int level, - const char *config_file, - unsigned config_line, - int error, - const char *file, - int line, - const char *func, - const char *format, ...) _printf_(9, 10); -#endif /* NM_IGNORED */ -#define log_syntax_internal(unit, level, config_file, config_line, error, file, line, func, format, ...) \ - log_internal_realm((level), (error), file, (line), (func), "syntax[%s]: "format, (config_file), __VA_ARGS__) \ - -int log_syntax_invalid_utf8_internal( - const char *unit, - int level, - const char *config_file, - unsigned config_line, - const char *file, - int line, - const char *func, - const char *rvalue); - -#define log_syntax(unit, level, config_file, config_line, error, ...) \ - ({ \ - int _level = (level), _e = (error); \ - (log_get_max_level() >= LOG_PRI(_level)) \ - ? log_syntax_internal(unit, _level, config_file, config_line, _e, PROJECT_FILE, __LINE__, __func__, __VA_ARGS__) \ - : -ERRNO_VALUE(_e); \ - }) - -#define log_syntax_invalid_utf8(unit, level, config_file, config_line, rvalue) \ - ({ \ - int _level = (level); \ - (log_get_max_level() >= LOG_PRI(_level)) \ - ? log_syntax_invalid_utf8_internal(unit, _level, config_file, config_line, PROJECT_FILE, __LINE__, __func__, rvalue) \ - : -EINVAL; \ - }) - -#define DEBUG_LOGGING _unlikely_(log_get_max_level() >= LOG_DEBUG) - -void log_setup_service(void); -void log_setup_cli(void); diff --git a/shared/systemd/src/basic/macro.h b/shared/systemd/src/basic/macro.h deleted file mode 100644 index 34416b3d..00000000 --- a/shared/systemd/src/basic/macro.h +++ /dev/null @@ -1,666 +0,0 @@ -/* SPDX-License-Identifier: LGPL-2.1-or-later */ -#pragma once - -#include <assert.h> -#include <errno.h> -#include <inttypes.h> -#include <stdbool.h> -#include <sys/param.h> -#include <sys/sysmacros.h> -#include <sys/types.h> - -#define _printf_(a, b) __attribute__((__format__(printf, a, b))) -#ifdef __clang__ -# define _alloc_(...) -#else -# define _alloc_(...) __attribute__((__alloc_size__(__VA_ARGS__))) -#endif -#define _sentinel_ __attribute__((__sentinel__)) -#define _section_(x) __attribute__((__section__(x))) -#define _used_ __attribute__((__used__)) -#define _unused_ __attribute__((__unused__)) -#define _destructor_ __attribute__((__destructor__)) -#define _pure_ __attribute__((__pure__)) -#define _const_ __attribute__((__const__)) -#define _deprecated_ __attribute__((__deprecated__)) -#define _packed_ __attribute__((__packed__)) -#define _malloc_ __attribute__((__malloc__)) -#define _weak_ __attribute__((__weak__)) -#define _likely_(x) (__builtin_expect(!!(x), 1)) -#define _unlikely_(x) (__builtin_expect(!!(x), 0)) -#define _public_ __attribute__((__visibility__("default"))) -#define _hidden_ __attribute__((__visibility__("hidden"))) -#define _weakref_(x) __attribute__((__weakref__(#x))) -#define _align_(x) __attribute__((__aligned__(x))) -#define _alignas_(x) __attribute__((__aligned__(__alignof(x)))) -#define _alignptr_ __attribute__((__aligned__(sizeof(void*)))) -#define _cleanup_(x) __attribute__((__cleanup__(x))) -#if __GNUC__ >= 7 -#define _fallthrough_ __attribute__((__fallthrough__)) -#else -#define _fallthrough_ -#endif -/* Define C11 noreturn without <stdnoreturn.h> and even on older gcc - * compiler versions */ -#ifndef _noreturn_ -#if __STDC_VERSION__ >= 201112L -#define _noreturn_ _Noreturn -#else -#define _noreturn_ __attribute__((__noreturn__)) -#endif -#endif - -#if !defined(HAS_FEATURE_MEMORY_SANITIZER) -# if defined(__has_feature) -# if __has_feature(memory_sanitizer) -# define HAS_FEATURE_MEMORY_SANITIZER 1 -# endif -# endif -# if !defined(HAS_FEATURE_MEMORY_SANITIZER) -# define HAS_FEATURE_MEMORY_SANITIZER 0 -# endif -#endif - -#if !defined(HAS_FEATURE_ADDRESS_SANITIZER) -# ifdef __SANITIZE_ADDRESS__ -# define HAS_FEATURE_ADDRESS_SANITIZER 1 -# elif defined(__has_feature) -# if __has_feature(address_sanitizer) -# define HAS_FEATURE_ADDRESS_SANITIZER 1 -# endif -# endif -# if !defined(HAS_FEATURE_ADDRESS_SANITIZER) -# define HAS_FEATURE_ADDRESS_SANITIZER 0 -# endif -#endif - -/* Note: on GCC "no_sanitize_address" is a function attribute only, on llvm it may also be applied to global - * variables. We define a specific macro which knows this. Note that on GCC we don't need this decorator so much, since - * our primary usecase for this attribute is registration structures placed in named ELF sections which shall not be - * padded, but GCC doesn't pad those anyway if AddressSanitizer is enabled. */ -#if HAS_FEATURE_ADDRESS_SANITIZER && defined(__clang__) -#define _variable_no_sanitize_address_ __attribute__((__no_sanitize_address__)) -#else -#define _variable_no_sanitize_address_ -#endif - -/* Apparently there's no has_feature() call defined to check for ubsan, hence let's define this - * unconditionally on llvm */ -#if defined(__clang__) -#define _function_no_sanitize_float_cast_overflow_ __attribute__((no_sanitize("float-cast-overflow"))) -#else -#define _function_no_sanitize_float_cast_overflow_ -#endif - -#if (defined (__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6))) || defined (__clang__) -/* Temporarily disable some warnings */ -#define DISABLE_WARNING_DEPRECATED_DECLARATIONS \ - _Pragma("GCC diagnostic push"); \ - _Pragma("GCC diagnostic ignored \"-Wdeprecated-declarations\"") - -#define DISABLE_WARNING_FORMAT_NONLITERAL \ - _Pragma("GCC diagnostic push"); \ - _Pragma("GCC diagnostic ignored \"-Wformat-nonliteral\"") - -#define DISABLE_WARNING_MISSING_PROTOTYPES \ - _Pragma("GCC diagnostic push"); \ - _Pragma("GCC diagnostic ignored \"-Wmissing-prototypes\"") - -#define DISABLE_WARNING_NONNULL \ - _Pragma("GCC diagnostic push"); \ - _Pragma("GCC diagnostic ignored \"-Wnonnull\"") - -#define DISABLE_WARNING_SHADOW \ - _Pragma("GCC diagnostic push"); \ - _Pragma("GCC diagnostic ignored \"-Wshadow\"") - -#define DISABLE_WARNING_INCOMPATIBLE_POINTER_TYPES \ - _Pragma("GCC diagnostic push"); \ - _Pragma("GCC diagnostic ignored \"-Wincompatible-pointer-types\"") - -#if HAVE_WSTRINGOP_TRUNCATION -# define DISABLE_WARNING_STRINGOP_TRUNCATION \ - _Pragma("GCC diagnostic push"); \ - _Pragma("GCC diagnostic ignored \"-Wstringop-truncation\"") -#else -# define DISABLE_WARNING_STRINGOP_TRUNCATION \ - _Pragma("GCC diagnostic push") -#endif - -#define DISABLE_WARNING_FLOAT_EQUAL \ - _Pragma("GCC diagnostic push"); \ - _Pragma("GCC diagnostic ignored \"-Wfloat-equal\"") - -#define DISABLE_WARNING_TYPE_LIMITS \ - _Pragma("GCC diagnostic push"); \ - _Pragma("GCC diagnostic ignored \"-Wtype-limits\"") - -#define REENABLE_WARNING \ - _Pragma("GCC diagnostic pop") -#else -#define DISABLE_WARNING_DECLARATION_AFTER_STATEMENT -#define DISABLE_WARNING_FORMAT_NONLITERAL -#define DISABLE_WARNING_MISSING_PROTOTYPES -#define DISABLE_WARNING_NONNULL -#define DISABLE_WARNING_SHADOW -#define REENABLE_WARNING -#endif - -/* automake test harness */ -#define EXIT_TEST_SKIP 77 - -#define XSTRINGIFY(x) #x -#define STRINGIFY(x) XSTRINGIFY(x) - -#define XCONCATENATE(x, y) x ## y -#define CONCATENATE(x, y) XCONCATENATE(x, y) - -#define UNIQ_T(x, uniq) CONCATENATE(__unique_prefix_, CONCATENATE(x, uniq)) -#define UNIQ __COUNTER__ - -/* builtins */ -#if __SIZEOF_INT__ == 4 -#define BUILTIN_FFS_U32(x) __builtin_ffs(x); -#elif __SIZEOF_LONG__ == 4 -#define BUILTIN_FFS_U32(x) __builtin_ffsl(x); -#else -#error "neither int nor long are four bytes long?!?" -#endif - -/* Rounds up */ - -#define ALIGN4(l) (((l) + 3) & ~3) -#define ALIGN8(l) (((l) + 7) & ~7) - -#if __SIZEOF_POINTER__ == 8 -#define ALIGN(l) ALIGN8(l) -#elif __SIZEOF_POINTER__ == 4 -#define ALIGN(l) ALIGN4(l) -#else -#error "Wut? Pointers are neither 4 nor 8 bytes long?" -#endif - -#define ALIGN_PTR(p) ((void*) ALIGN((unsigned long) (p))) -#define ALIGN4_PTR(p) ((void*) ALIGN4((unsigned long) (p))) -#define ALIGN8_PTR(p) ((void*) ALIGN8((unsigned long) (p))) - -static inline size_t ALIGN_TO(size_t l, size_t ali) { - return ((l + ali - 1) & ~(ali - 1)); -} - -#define ALIGN_TO_PTR(p, ali) ((void*) ALIGN_TO((unsigned long) (p), (ali))) - -/* align to next higher power-of-2 (except for: 0 => 0, overflow => 0) */ -static inline unsigned long ALIGN_POWER2(unsigned long u) { - - /* Avoid subtraction overflow */ - if (u == 0) - return 0; - - /* clz(0) is undefined */ - if (u == 1) - return 1; - - /* left-shift overflow is undefined */ - if (__builtin_clzl(u - 1UL) < 1) - return 0; - - return 1UL << (sizeof(u) * 8 - __builtin_clzl(u - 1UL)); -} - -static inline size_t GREEDY_ALLOC_ROUND_UP(size_t l) { - size_t m; - - /* Round up allocation sizes a bit to some reasonable, likely larger value. This is supposed to be - * used for cases which are likely called in an allocation loop of some form, i.e. that repetitively - * grow stuff, for example strv_extend() and suchlike. - * - * Note the difference to GREEDY_REALLOC() here, as this helper operates on a single size value only, - * and rounds up to next multiple of 2, needing no further counter. - * - * Note the benefits of direct ALIGN_POWER2() usage: type-safety for size_t, sane handling for very - * small (i.e. <= 2) and safe handling for very large (i.e. > SSIZE_MAX) values. */ - - if (l <= 2) - return 2; /* Never allocate less than 2 of something. */ - - m = ALIGN_POWER2(l); - if (m == 0) /* overflow? */ - return l; - - return m; -} - -#ifndef __COVERITY__ -# define VOID_0 ((void)0) -#else -# define VOID_0 ((void*)0) -#endif - -#define ELEMENTSOF(x) \ - (__builtin_choose_expr( \ - !__builtin_types_compatible_p(typeof(x), typeof(&*(x))), \ - sizeof(x)/sizeof((x)[0]), \ - VOID_0)) - -/* - * STRLEN - return the length of a string literal, minus the trailing NUL byte. - * Contrary to strlen(), this is a constant expression. - * @x: a string literal. - */ -#define STRLEN(x) (sizeof(""x"") - 1) - -/* - * container_of - cast a member of a structure out to the containing structure - * @ptr: the pointer to the member. - * @type: the type of the container struct this is embedded in. - * @member: the name of the member within the struct. - */ -#define container_of(ptr, type, member) __container_of(UNIQ, (ptr), type, member) -#define __container_of(uniq, ptr, type, member) \ - ({ \ - const typeof( ((type*)0)->member ) *UNIQ_T(A, uniq) = (ptr); \ - (type*)( (char *)UNIQ_T(A, uniq) - offsetof(type, member) ); \ - }) - -#undef MAX -#define MAX(a, b) __MAX(UNIQ, (a), UNIQ, (b)) -#define __MAX(aq, a, bq, b) \ - ({ \ - const typeof(a) UNIQ_T(A, aq) = (a); \ - const typeof(b) UNIQ_T(B, bq) = (b); \ - UNIQ_T(A, aq) > UNIQ_T(B, bq) ? UNIQ_T(A, aq) : UNIQ_T(B, bq); \ - }) - -/* evaluates to (void) if _A or _B are not constant or of different types */ -#define 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)) - -/* takes two types and returns the size of the larger one */ -#define MAXSIZE(A, B) (sizeof(union _packed_ { typeof(A) a; typeof(B) b; })) - -#define MAX3(x, y, z) \ - ({ \ - const typeof(x) _c = MAX(x, y); \ - MAX(_c, z); \ - }) - -#define MAX4(x, y, z, a) \ - ({ \ - const typeof(x) _d = MAX3(x, y, z); \ - MAX(_d, a); \ - }) - -#undef MIN -#define MIN(a, b) __MIN(UNIQ, (a), UNIQ, (b)) -#define __MIN(aq, a, bq, b) \ - ({ \ - const typeof(a) UNIQ_T(A, aq) = (a); \ - const typeof(b) UNIQ_T(B, bq) = (b); \ - UNIQ_T(A, aq) < UNIQ_T(B, bq) ? UNIQ_T(A, aq) : UNIQ_T(B, bq); \ - }) - -/* evaluates to (void) if _A or _B are not constant or of different types */ -#define CONST_MIN(_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 MIN3(x, y, z) \ - ({ \ - const typeof(x) _c = MIN(x, y); \ - MIN(_c, z); \ - }) - -#define LESS_BY(a, b) __LESS_BY(UNIQ, (a), UNIQ, (b)) -#define __LESS_BY(aq, a, bq, b) \ - ({ \ - const typeof(a) UNIQ_T(A, aq) = (a); \ - const typeof(b) UNIQ_T(B, bq) = (b); \ - UNIQ_T(A, aq) > UNIQ_T(B, bq) ? UNIQ_T(A, aq) - UNIQ_T(B, bq) : 0; \ - }) - -#define CMP(a, b) __CMP(UNIQ, (a), UNIQ, (b)) -#define __CMP(aq, a, bq, b) \ - ({ \ - const typeof(a) UNIQ_T(A, aq) = (a); \ - const typeof(b) UNIQ_T(B, bq) = (b); \ - UNIQ_T(A, aq) < UNIQ_T(B, bq) ? -1 : \ - UNIQ_T(A, aq) > UNIQ_T(B, bq) ? 1 : 0; \ - }) - -#undef CLAMP -#define CLAMP(x, low, high) __CLAMP(UNIQ, (x), UNIQ, (low), UNIQ, (high)) -#define __CLAMP(xq, x, lowq, low, highq, high) \ - ({ \ - const typeof(x) UNIQ_T(X, xq) = (x); \ - const typeof(low) UNIQ_T(LOW, lowq) = (low); \ - const typeof(high) UNIQ_T(HIGH, highq) = (high); \ - UNIQ_T(X, xq) > UNIQ_T(HIGH, highq) ? \ - UNIQ_T(HIGH, highq) : \ - UNIQ_T(X, xq) < UNIQ_T(LOW, lowq) ? \ - UNIQ_T(LOW, lowq) : \ - UNIQ_T(X, xq); \ - }) - -/* [(x + y - 1) / y] suffers from an integer overflow, even though the - * computation should be possible in the given type. Therefore, we use - * [x / y + !!(x % y)]. Note that on "Real CPUs" a division returns both the - * quotient and the remainder, so both should be equally fast. */ -#define DIV_ROUND_UP(x, y) __DIV_ROUND_UP(UNIQ, (x), UNIQ, (y)) -#define __DIV_ROUND_UP(xq, x, yq, y) \ - ({ \ - const typeof(x) UNIQ_T(X, xq) = (x); \ - const typeof(y) UNIQ_T(Y, yq) = (y); \ - (UNIQ_T(X, xq) / UNIQ_T(Y, yq) + !!(UNIQ_T(X, xq) % UNIQ_T(Y, yq))); \ - }) - -#ifdef __COVERITY__ - -/* Use special definitions of assertion macros in order to prevent - * false positives of ASSERT_SIDE_EFFECT on Coverity static analyzer - * for uses of assert_se() and assert_return(). - * - * These definitions make expression go through a (trivial) function - * call to ensure they are not discarded. Also use ! or !! to ensure - * the boolean expressions are seen as such. - * - * This technique has been described and recommended in: - * https://community.synopsys.com/s/question/0D534000046Yuzb/suppressing-assertsideeffect-for-functions-that-allow-for-sideeffects - */ - -extern void __coverity_panic__(void); - -static inline void __coverity_check__(int condition) { - if (!condition) - __coverity_panic__(); -} - -static inline int __coverity_check_and_return__(int condition) { - return condition; -} - -#define assert_message_se(expr, message) __coverity_check__(!!(expr)) - -#define assert_log(expr, message) __coverity_check_and_return__(!!(expr)) - -#else /* ! __COVERITY__ */ - -#define assert_message_se(expr, message) \ - do { \ - if (_unlikely_(!(expr))) \ - log_assert_failed(message, PROJECT_FILE, __LINE__, __PRETTY_FUNCTION__); \ - } while (false) - -#define assert_log(expr, message) ((_likely_(expr)) \ - ? (true) \ - : (log_assert_failed_return(message, PROJECT_FILE, __LINE__, __PRETTY_FUNCTION__), false)) - -#endif /* __COVERITY__ */ - -#define assert_se(expr) assert_message_se(expr, #expr) - -/* We override the glibc assert() here. */ -#undef assert -#ifdef NDEBUG -#define assert(expr) do {} while (false) -#else -#define assert(expr) assert_message_se(expr, #expr) -#endif - -#define assert_not_reached(t) \ - log_assert_failed_unreachable(t, PROJECT_FILE, __LINE__, __PRETTY_FUNCTION__) - -#if defined(static_assert) -#define assert_cc(expr) \ - static_assert(expr, #expr) -#else -#define assert_cc(expr) \ - struct CONCATENATE(_assert_struct_, __COUNTER__) { \ - char x[(expr) ? 0 : -1]; \ - } -#endif - -#define assert_return(expr, r) \ - do { \ - if (!assert_log(expr, #expr)) \ - return (r); \ - } while (false) - -#define assert_return_errno(expr, r, err) \ - do { \ - if (!assert_log(expr, #expr)) { \ - errno = err; \ - return (r); \ - } \ - } while (false) - -#define return_with_errno(r, err) \ - do { \ - errno = abs(err); \ - return r; \ - } while (false) - -#define PTR_TO_INT(p) ((int) ((intptr_t) (p))) -#define INT_TO_PTR(u) ((void *) ((intptr_t) (u))) -#define PTR_TO_UINT(p) ((unsigned) ((uintptr_t) (p))) -#define UINT_TO_PTR(u) ((void *) ((uintptr_t) (u))) - -#define PTR_TO_LONG(p) ((long) ((intptr_t) (p))) -#define LONG_TO_PTR(u) ((void *) ((intptr_t) (u))) -#define PTR_TO_ULONG(p) ((unsigned long) ((uintptr_t) (p))) -#define ULONG_TO_PTR(u) ((void *) ((uintptr_t) (u))) - -#define PTR_TO_UINT8(p) ((uint8_t) ((uintptr_t) (p))) -#define UINT8_TO_PTR(u) ((void *) ((uintptr_t) (u))) - -#define PTR_TO_INT32(p) ((int32_t) ((intptr_t) (p))) -#define INT32_TO_PTR(u) ((void *) ((intptr_t) (u))) -#define PTR_TO_UINT32(p) ((uint32_t) ((uintptr_t) (p))) -#define UINT32_TO_PTR(u) ((void *) ((uintptr_t) (u))) - -#define PTR_TO_INT64(p) ((int64_t) ((intptr_t) (p))) -#define INT64_TO_PTR(u) ((void *) ((intptr_t) (u))) -#define PTR_TO_UINT64(p) ((uint64_t) ((uintptr_t) (p))) -#define UINT64_TO_PTR(u) ((void *) ((uintptr_t) (u))) - -#define PTR_TO_SIZE(p) ((size_t) ((uintptr_t) (p))) -#define SIZE_TO_PTR(u) ((void *) ((uintptr_t) (u))) - -#define CHAR_TO_STR(x) ((char[2]) { x, 0 }) - -#define char_array_0(x) x[sizeof(x)-1] = 0; - -#define sizeof_field(struct_type, member) sizeof(((struct_type *) 0)->member) - -/* Returns the number of chars needed to format variables of the - * specified type as a decimal string. Adds in extra space for a - * negative '-' prefix (hence works correctly on signed - * types). Includes space for the trailing NUL. */ -#define DECIMAL_STR_MAX(type) \ - (2+(sizeof(type) <= 1 ? 3 : \ - sizeof(type) <= 2 ? 5 : \ - sizeof(type) <= 4 ? 10 : \ - sizeof(type) <= 8 ? 20 : sizeof(int[-2*(sizeof(type) > 8)]))) - -#define DECIMAL_STR_WIDTH(x) \ - ({ \ - typeof(x) _x_ = (x); \ - unsigned ans = 1; \ - while ((_x_ /= 10) != 0) \ - ans++; \ - ans; \ - }) - -#define UPDATE_FLAG(orig, flag, b) \ - ((b) ? ((orig) | (flag)) : ((orig) & ~(flag))) -#define SET_FLAG(v, flag, b) \ - (v) = UPDATE_FLAG(v, flag, b) -#define FLAGS_SET(v, flags) \ - ((~(v) & (flags)) == 0) - -#define CASE_F(X) case X: -#define CASE_F_1(CASE, X) CASE_F(X) -#define CASE_F_2(CASE, X, ...) CASE(X) CASE_F_1(CASE, __VA_ARGS__) -#define CASE_F_3(CASE, X, ...) CASE(X) CASE_F_2(CASE, __VA_ARGS__) -#define CASE_F_4(CASE, X, ...) CASE(X) CASE_F_3(CASE, __VA_ARGS__) -#define CASE_F_5(CASE, X, ...) CASE(X) CASE_F_4(CASE, __VA_ARGS__) -#define CASE_F_6(CASE, X, ...) CASE(X) CASE_F_5(CASE, __VA_ARGS__) -#define CASE_F_7(CASE, X, ...) CASE(X) CASE_F_6(CASE, __VA_ARGS__) -#define CASE_F_8(CASE, X, ...) CASE(X) CASE_F_7(CASE, __VA_ARGS__) -#define CASE_F_9(CASE, X, ...) CASE(X) CASE_F_8(CASE, __VA_ARGS__) -#define CASE_F_10(CASE, X, ...) CASE(X) CASE_F_9(CASE, __VA_ARGS__) -#define CASE_F_11(CASE, X, ...) CASE(X) CASE_F_10(CASE, __VA_ARGS__) -#define CASE_F_12(CASE, X, ...) CASE(X) CASE_F_11(CASE, __VA_ARGS__) -#define CASE_F_13(CASE, X, ...) CASE(X) CASE_F_12(CASE, __VA_ARGS__) -#define CASE_F_14(CASE, X, ...) CASE(X) CASE_F_13(CASE, __VA_ARGS__) -#define CASE_F_15(CASE, X, ...) CASE(X) CASE_F_14(CASE, __VA_ARGS__) -#define CASE_F_16(CASE, X, ...) CASE(X) CASE_F_15(CASE, __VA_ARGS__) -#define CASE_F_17(CASE, X, ...) CASE(X) CASE_F_16(CASE, __VA_ARGS__) -#define CASE_F_18(CASE, X, ...) CASE(X) CASE_F_17(CASE, __VA_ARGS__) -#define CASE_F_19(CASE, X, ...) CASE(X) CASE_F_18(CASE, __VA_ARGS__) -#define CASE_F_20(CASE, X, ...) CASE(X) CASE_F_19(CASE, __VA_ARGS__) - -#define GET_CASE_F(_1,_2,_3,_4,_5,_6,_7,_8,_9,_10,_11,_12,_13,_14,_15,_16,_17,_18,_19,_20,NAME,...) NAME -#define FOR_EACH_MAKE_CASE(...) \ - GET_CASE_F(__VA_ARGS__,CASE_F_20,CASE_F_19,CASE_F_18,CASE_F_17,CASE_F_16,CASE_F_15,CASE_F_14,CASE_F_13,CASE_F_12,CASE_F_11, \ - CASE_F_10,CASE_F_9,CASE_F_8,CASE_F_7,CASE_F_6,CASE_F_5,CASE_F_4,CASE_F_3,CASE_F_2,CASE_F_1) \ - (CASE_F,__VA_ARGS__) - -#define IN_SET(x, ...) \ - ({ \ - bool _found = false; \ - /* If the build breaks in the line below, you need to extend the case macros. (We use "long double" as \ - * type for the array, in the hope that checkers such as ubsan don't complain that the initializers for \ - * the array are not representable by the base type. Ideally we'd use typeof(x) as base type, but that \ - * doesn't work, as we want to use this on bitfields and gcc refuses typeof() on bitfields.) */ \ - static const long double __assert_in_set[] _unused_ = { __VA_ARGS__ }; \ - assert_cc(ELEMENTSOF(__assert_in_set) <= 20); \ - switch(x) { \ - FOR_EACH_MAKE_CASE(__VA_ARGS__) \ - _found = true; \ - break; \ - default: \ - break; \ - } \ - _found; \ - }) - -#define SWAP_TWO(x, y) do { \ - typeof(x) _t = (x); \ - (x) = (y); \ - (y) = (_t); \ - } while (false) - -#define STRV_MAKE(...) ((char**) ((const char*[]) { __VA_ARGS__, NULL })) -#define STRV_MAKE_EMPTY ((char*[1]) { NULL }) - -/* Pointers range from NULL to POINTER_MAX */ -#define POINTER_MAX ((void*) UINTPTR_MAX) - -/* Iterates through a specified list of pointers. Accepts NULL pointers, but uses POINTER_MAX as internal marker for EOL. */ -#define FOREACH_POINTER(p, x, ...) \ - for (typeof(p) *_l = (typeof(p)[]) { ({ p = x; }), ##__VA_ARGS__, POINTER_MAX }; \ - p != (typeof(p)) POINTER_MAX; \ - p = *(++_l)) - -/* Define C11 thread_local attribute even on older gcc compiler - * version */ -#ifndef 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 - */ -#if __STDC_VERSION__ >= 201112L && !(defined(__STDC_NO_THREADS__) || (defined(__GNU_LIBRARY__) && __GLIBC__ == 2 && __GLIBC_MINOR__ < 16)) -#define thread_local _Thread_local -#else -#define thread_local __thread -#endif -#endif - -#define DEFINE_TRIVIAL_DESTRUCTOR(name, type, func) \ - static inline void name(type *p) { \ - func(p); \ - } - -#define DEFINE_TRIVIAL_CLEANUP_FUNC(type, func) \ - static inline void func##p(type *p) { \ - if (*p) \ - func(*p); \ - } - -#define _DEFINE_TRIVIAL_REF_FUNC(type, name, scope) \ - scope type *name##_ref(type *p) { \ - if (!p) \ - return NULL; \ - \ - assert(p->n_ref > 0); \ - p->n_ref++; \ - return p; \ - } - -#define _DEFINE_TRIVIAL_UNREF_FUNC(type, name, free_func, scope) \ - scope type *name##_unref(type *p) { \ - if (!p) \ - return NULL; \ - \ - assert(p->n_ref > 0); \ - p->n_ref--; \ - if (p->n_ref > 0) \ - return NULL; \ - \ - return free_func(p); \ - } - -#define DEFINE_TRIVIAL_REF_FUNC(type, name) \ - _DEFINE_TRIVIAL_REF_FUNC(type, name,) -#define DEFINE_PRIVATE_TRIVIAL_REF_FUNC(type, name) \ - _DEFINE_TRIVIAL_REF_FUNC(type, name, static) -#define DEFINE_PUBLIC_TRIVIAL_REF_FUNC(type, name) \ - _DEFINE_TRIVIAL_REF_FUNC(type, name, _public_) - -#define DEFINE_TRIVIAL_UNREF_FUNC(type, name, free_func) \ - _DEFINE_TRIVIAL_UNREF_FUNC(type, name, free_func,) -#define DEFINE_PRIVATE_TRIVIAL_UNREF_FUNC(type, name, free_func) \ - _DEFINE_TRIVIAL_UNREF_FUNC(type, name, free_func, static) -#define DEFINE_PUBLIC_TRIVIAL_UNREF_FUNC(type, name, free_func) \ - _DEFINE_TRIVIAL_UNREF_FUNC(type, name, free_func, _public_) - -#define DEFINE_TRIVIAL_REF_UNREF_FUNC(type, name, free_func) \ - DEFINE_TRIVIAL_REF_FUNC(type, name); \ - DEFINE_TRIVIAL_UNREF_FUNC(type, name, free_func); - -#define DEFINE_PRIVATE_TRIVIAL_REF_UNREF_FUNC(type, name, free_func) \ - DEFINE_PRIVATE_TRIVIAL_REF_FUNC(type, name); \ - DEFINE_PRIVATE_TRIVIAL_UNREF_FUNC(type, name, free_func); - -#define DEFINE_PUBLIC_TRIVIAL_REF_UNREF_FUNC(type, name, free_func) \ - DEFINE_PUBLIC_TRIVIAL_REF_FUNC(type, name); \ - DEFINE_PUBLIC_TRIVIAL_UNREF_FUNC(type, name, free_func); - -/* A macro to force copying of a variable from memory. This is useful whenever we want to read something from - * memory and want to make sure the compiler won't optimize away the destination variable for us. It's not - * supposed to be a full CPU memory barrier, i.e. CPU is still allowed to reorder the reads, but it is not - * allowed to remove our local copies of the variables. We want this to work for unaligned memory, hence - * memcpy() is great for our purposes. */ -#define READ_NOW(x) \ - ({ \ - typeof(x) _copy; \ - memcpy(&_copy, &(x), sizeof(_copy)); \ - asm volatile ("" : : : "memory"); \ - _copy; \ - }) - -static inline size_t size_add(size_t x, size_t y) { - return y >= SIZE_MAX - x ? SIZE_MAX : x + y; -} - -#include "log.h" diff --git a/shared/systemd/src/basic/memory-util.c b/shared/systemd/src/basic/memory-util.c deleted file mode 100644 index 7ee7c94e..00000000 --- a/shared/systemd/src/basic/memory-util.c +++ /dev/null @@ -1,61 +0,0 @@ -/* SPDX-License-Identifier: LGPL-2.1-or-later */ - -#include "nm-sd-adapt-shared.h" - -#include <unistd.h> - -#include "memory-util.h" - -size_t page_size(void) { - static thread_local size_t pgsz = 0; - long r; - - if (_likely_(pgsz > 0)) - return pgsz; - - r = sysconf(_SC_PAGESIZE); - assert(r > 0); - - pgsz = (size_t) r; - return pgsz; -} - -bool memeqzero(const void *data, size_t length) { - /* Does the buffer consist entirely of NULs? - * Copied from https://github.com/systemd/casync/, copied in turn from - * https://github.com/rustyrussell/ccan/blob/master/ccan/mem/mem.c#L92, - * which is licensed CC-0. - */ - - const uint8_t *p = data; - size_t i; - - /* Check first 16 bytes manually */ - for (i = 0; i < 16; i++, length--) { - if (length == 0) - return true; - if (p[i]) - return false; - } - - /* Now we know first 16 bytes are NUL, memcmp with self. */ - return memcmp(data, p + i, length) == 0; -} - -#if !HAVE_EXPLICIT_BZERO -/* - * The pointer to memset() is volatile so that compiler must de-reference the pointer and can't assume that - * it points to any function in particular (such as memset(), which it then might further "optimize"). This - * approach is inspired by openssl's crypto/mem_clr.c. - */ -typedef void *(*memset_t)(void *,int,size_t); - -static volatile memset_t memset_func = memset; - -void* explicit_bzero_safe(void *p, size_t l) { - if (l > 0) - memset_func(p, '\0', l); - - return p; -} -#endif diff --git a/shared/systemd/src/basic/memory-util.h b/shared/systemd/src/basic/memory-util.h deleted file mode 100644 index 179edd24..00000000 --- a/shared/systemd/src/basic/memory-util.h +++ /dev/null @@ -1,103 +0,0 @@ -/* SPDX-License-Identifier: LGPL-2.1-or-later */ -#pragma once - -#include <inttypes.h> -#include <malloc.h> -#include <stdbool.h> -#include <string.h> -#include <sys/types.h> - -#include "alloc-util.h" -#include "macro.h" - -size_t page_size(void) _pure_; -#define PAGE_ALIGN(l) ALIGN_TO((l), page_size()) -#define PAGE_ALIGN_DOWN(l) ((l) & ~(page_size() - 1)) -#define PAGE_OFFSET(l) ((l) & (page_size() - 1)) - -/* Normal memcpy requires src to be nonnull. We do nothing if n is 0. */ -static inline void memcpy_safe(void *dst, const void *src, size_t n) { - if (n == 0) - return; - assert(src); - memcpy(dst, src, n); -} - -/* Normal memcmp requires s1 and s2 to be nonnull. We do nothing if n is 0. */ -static inline int memcmp_safe(const void *s1, const void *s2, size_t n) { - if (n == 0) - return 0; - assert(s1); - assert(s2); - return memcmp(s1, s2, n); -} - -/* Compare s1 (length n1) with s2 (length n2) in lexicographic order. */ -static inline int memcmp_nn(const void *s1, size_t n1, const void *s2, size_t n2) { - return memcmp_safe(s1, s2, MIN(n1, n2)) - ?: CMP(n1, n2); -} - -#define memzero(x,l) \ - ({ \ - size_t _l_ = (l); \ - if (_l_ > 0) \ - memset(x, 0, _l_); \ - }) - -#define zero(x) (memzero(&(x), sizeof(x))) - -bool memeqzero(const void *data, size_t length); - -#define eqzero(x) memeqzero(x, sizeof(x)) - -static inline void *mempset(void *s, int c, size_t n) { - memset(s, c, n); - return (uint8_t*)s + n; -} - -/* Normal memmem() requires haystack to be nonnull, which is annoying for zero-length buffers */ -static inline void *memmem_safe(const void *haystack, size_t haystacklen, const void *needle, size_t needlelen) { - - if (needlelen <= 0) - return (void*) haystack; - - if (haystacklen < needlelen) - return NULL; - - assert(haystack); - assert(needle); - - return memmem(haystack, haystacklen, needle, needlelen); -} - -#if HAVE_EXPLICIT_BZERO -static inline void* explicit_bzero_safe(void *p, size_t l) { - if (l > 0) - explicit_bzero(p, l); - - return p; -} -#else -void *explicit_bzero_safe(void *p, size_t l); -#endif - -static inline void* erase_and_free(void *p) { - size_t l; - - if (!p) - return NULL; - - l = malloc_usable_size(p); - explicit_bzero_safe(p, l); - return mfree(p); -} - -static inline void erase_and_freep(void *p) { - erase_and_free(*(void**) p); -} - -/* Use with _cleanup_ to erase a single 'char' when leaving scope */ -static inline void erase_char(char *p) { - explicit_bzero_safe(p, sizeof(char)); -} diff --git a/shared/systemd/src/basic/mempool.c b/shared/systemd/src/basic/mempool.c deleted file mode 100644 index 46c44914..00000000 --- a/shared/systemd/src/basic/mempool.c +++ /dev/null @@ -1,102 +0,0 @@ -/* SPDX-License-Identifier: LGPL-2.1-or-later */ - -#include "nm-sd-adapt-shared.h" - -#include <stdint.h> -#include <stdlib.h> - -#include "env-util.h" -#include "macro.h" -#include "memory-util.h" -#include "mempool.h" -#include "process-util.h" -#include "util.h" - -struct pool { - struct pool *next; - size_t n_tiles; - size_t n_used; -}; - -void* mempool_alloc_tile(struct mempool *mp) { - size_t i; - - /* When a tile is released we add it to the list and simply - * place the next pointer at its offset 0. */ - - assert(mp->tile_size >= sizeof(void*)); - assert(mp->at_least > 0); - - if (mp->freelist) { - void *r; - - r = mp->freelist; - mp->freelist = * (void**) mp->freelist; - return r; - } - - if (_unlikely_(!mp->first_pool) || - _unlikely_(mp->first_pool->n_used >= mp->first_pool->n_tiles)) { - size_t size, n; - struct pool *p; - - n = mp->first_pool ? mp->first_pool->n_tiles : 0; - n = MAX(mp->at_least, n * 2); - size = PAGE_ALIGN(ALIGN(sizeof(struct pool)) + n*mp->tile_size); - n = (size - ALIGN(sizeof(struct pool))) / mp->tile_size; - - p = malloc(size); - if (!p) - return NULL; - - p->next = mp->first_pool; - p->n_tiles = n; - p->n_used = 0; - - mp->first_pool = p; - } - - i = mp->first_pool->n_used++; - - return ((uint8_t*) mp->first_pool) + ALIGN(sizeof(struct pool)) + i*mp->tile_size; -} - -void* mempool_alloc0_tile(struct mempool *mp) { - void *p; - - p = mempool_alloc_tile(mp); - if (p) - memzero(p, mp->tile_size); - return p; -} - -void mempool_free_tile(struct mempool *mp, void *p) { - * (void**) p = mp->freelist; - mp->freelist = p; -} - -bool mempool_enabled(void) { - static int b = -1; - - if (!is_main_thread()) - return false; - - if (!mempool_use_allowed) - b = false; - if (b < 0) - b = getenv_bool("SYSTEMD_MEMPOOL") != 0; - - return b; -} - -#if VALGRIND -void mempool_drop(struct mempool *mp) { - struct pool *p = mp->first_pool; - while (p) { - struct pool *n; - n = p->next; - free(p); - p = n; - } -} -#endif diff --git a/shared/systemd/src/basic/mempool.h b/shared/systemd/src/basic/mempool.h deleted file mode 100644 index 0fe2f278..00000000 --- a/shared/systemd/src/basic/mempool.h +++ /dev/null @@ -1,31 +0,0 @@ -/* SPDX-License-Identifier: LGPL-2.1-or-later */ -#pragma once - -#include <stdbool.h> -#include <stddef.h> - -struct pool; - -struct mempool { - struct pool *first_pool; - void *freelist; - size_t tile_size; - unsigned at_least; -}; - -void* mempool_alloc_tile(struct mempool *mp); -void* mempool_alloc0_tile(struct mempool *mp); -void mempool_free_tile(struct mempool *mp, void *p); - -#define DEFINE_MEMPOOL(pool_name, tile_type, alloc_at_least) \ -static struct mempool pool_name = { \ - .tile_size = sizeof(tile_type), \ - .at_least = alloc_at_least, \ -} - -extern const bool mempool_use_allowed; -bool mempool_enabled(void); - -#if VALGRIND -void mempool_drop(struct mempool *mp); -#endif diff --git a/shared/systemd/src/basic/missing_fcntl.h b/shared/systemd/src/basic/missing_fcntl.h deleted file mode 100644 index 00937d2a..00000000 --- a/shared/systemd/src/basic/missing_fcntl.h +++ /dev/null @@ -1,60 +0,0 @@ -/* SPDX-License-Identifier: LGPL-2.1-or-later */ -#pragma once - -#include <fcntl.h> - -#ifndef F_LINUX_SPECIFIC_BASE -#define F_LINUX_SPECIFIC_BASE 1024 -#endif - -#ifndef F_SETPIPE_SZ -#define F_SETPIPE_SZ (F_LINUX_SPECIFIC_BASE + 7) -#endif - -#ifndef F_GETPIPE_SZ -#define F_GETPIPE_SZ (F_LINUX_SPECIFIC_BASE + 8) -#endif - -#ifndef F_ADD_SEALS -#define F_ADD_SEALS (F_LINUX_SPECIFIC_BASE + 9) -#define F_GET_SEALS (F_LINUX_SPECIFIC_BASE + 10) - -#define F_SEAL_SEAL 0x0001 /* prevent further seals from being set */ -#define F_SEAL_SHRINK 0x0002 /* prevent file from shrinking */ -#define F_SEAL_GROW 0x0004 /* prevent file from growing */ -#define F_SEAL_WRITE 0x0008 /* prevent writes */ -#endif - -#ifndef F_OFD_GETLK -#define F_OFD_GETLK 36 -#define F_OFD_SETLK 37 -#define F_OFD_SETLKW 38 -#endif - -#ifndef MAX_HANDLE_SZ -#define MAX_HANDLE_SZ 128 -#endif - -/* The precise definition of __O_TMPFILE is arch specific; use the - * values defined by the kernel (note: some are hexa, some are octal, - * duplicated as-is from the kernel definitions): - * - alpha, parisc, sparc: each has a specific value; - * - others: they use the "generic" value. - */ - -#ifndef __O_TMPFILE -#if defined(__alpha__) -#define __O_TMPFILE 0100000000 -#elif defined(__parisc__) || defined(__hppa__) -#define __O_TMPFILE 0400000000 -#elif defined(__sparc__) || defined(__sparc64__) -#define __O_TMPFILE 0x2000000 -#else -#define __O_TMPFILE 020000000 -#endif -#endif - -/* a horrid kludge trying to make sure that this will fail on old kernels */ -#ifndef O_TMPFILE -#define O_TMPFILE (__O_TMPFILE | O_DIRECTORY) -#endif diff --git a/shared/systemd/src/basic/missing_random.h b/shared/systemd/src/basic/missing_random.h deleted file mode 100644 index 443b9136..00000000 --- a/shared/systemd/src/basic/missing_random.h +++ /dev/null @@ -1,20 +0,0 @@ -/* SPDX-License-Identifier: LGPL-2.1-or-later */ -#pragma once - -#if USE_SYS_RANDOM_H -# include <sys/random.h> -#else -# include <linux/random.h> -#endif - -#ifndef GRND_NONBLOCK -#define GRND_NONBLOCK 0x0001 -#endif - -#ifndef GRND_RANDOM -#define GRND_RANDOM 0x0002 -#endif - -#ifndef GRND_INSECURE -#define GRND_INSECURE 0x0004 -#endif diff --git a/shared/systemd/src/basic/missing_socket.h b/shared/systemd/src/basic/missing_socket.h deleted file mode 100644 index a4f6836f..00000000 --- a/shared/systemd/src/basic/missing_socket.h +++ /dev/null @@ -1,83 +0,0 @@ -/* SPDX-License-Identifier: LGPL-2.1-or-later */ -#pragma once - -#include <sys/socket.h> - -#if 0 /* NM_IGNORED */ -#if HAVE_LINUX_VM_SOCKETS_H -#include <linux/vm_sockets.h> -#else -#define VMADDR_CID_ANY -1U -struct sockaddr_vm { - unsigned short svm_family; - unsigned short svm_reserved1; - unsigned int svm_port; - unsigned int svm_cid; - unsigned char svm_zero[sizeof(struct sockaddr) - - sizeof(unsigned short) - - sizeof(unsigned short) - - sizeof(unsigned int) - - sizeof(unsigned int)]; -}; -#endif /* !HAVE_LINUX_VM_SOCKETS_H */ -#endif /* NM_IGNORED */ - -#ifndef AF_VSOCK -#define AF_VSOCK 40 -#endif - -#ifndef SO_REUSEPORT -#define SO_REUSEPORT 15 -#endif - -#ifndef SO_PEERGROUPS -#define SO_PEERGROUPS 59 -#endif - -#ifndef SO_BINDTOIFINDEX -#define SO_BINDTOIFINDEX 62 -#endif - -#ifndef SOL_NETLINK -#define SOL_NETLINK 270 -#endif - -#ifndef SOL_ALG -#define SOL_ALG 279 -#endif - -/* Not exposed yet. Defined in include/linux/socket.h. */ -#ifndef SOL_SCTP -#define SOL_SCTP 132 -#endif - -/* Not exposed yet. Defined in include/linux/socket.h */ -#ifndef SCM_SECURITY -#define SCM_SECURITY 0x03 -#endif - -/* netinet/in.h */ -#ifndef IP_FREEBIND -#define IP_FREEBIND 15 -#endif - -#ifndef IP_TRANSPARENT -#define IP_TRANSPARENT 19 -#endif - -#ifndef IPV6_FREEBIND -#define IPV6_FREEBIND 78 -#endif - -#ifndef IP_RECVFRAGSIZE -#define IP_RECVFRAGSIZE 25 -#endif - -#ifndef IPV6_RECVFRAGSIZE -#define IPV6_RECVFRAGSIZE 77 -#endif - -/* linux/sockios.h */ -#ifndef SIOCGSKNS -#define SIOCGSKNS 0x894C -#endif diff --git a/shared/systemd/src/basic/missing_stat.h b/shared/systemd/src/basic/missing_stat.h deleted file mode 100644 index 9c1df698..00000000 --- a/shared/systemd/src/basic/missing_stat.h +++ /dev/null @@ -1,137 +0,0 @@ -/* SPDX-License-Identifier: LGPL-2.1-or-later */ -#pragma once - -#include <linux/types.h> -#include <sys/stat.h> - -#if 0 /* NM_IGNORED */ -#if WANT_LINUX_STAT_H -#include <linux/stat.h> -#endif - -/* Thew newest definition we are aware of (fa2fcf4f1df1559a0a4ee0f46915b496cc2ebf60; 5.8) */ -#define STATX_DEFINITION { \ - __u32 stx_mask; \ - __u32 stx_blksize; \ - __u64 stx_attributes; \ - __u32 stx_nlink; \ - __u32 stx_uid; \ - __u32 stx_gid; \ - __u16 stx_mode; \ - __u16 __spare0[1]; \ - __u64 stx_ino; \ - __u64 stx_size; \ - __u64 stx_blocks; \ - __u64 stx_attributes_mask; \ - struct statx_timestamp stx_atime; \ - struct statx_timestamp stx_btime; \ - struct statx_timestamp stx_ctime; \ - struct statx_timestamp stx_mtime; \ - __u32 stx_rdev_major; \ - __u32 stx_rdev_minor; \ - __u32 stx_dev_major; \ - __u32 stx_dev_minor; \ - __u64 stx_mnt_id; \ - __u64 __spare2; \ - __u64 __spare3[12]; \ -} - -#if !HAVE_STRUCT_STATX -struct statx_timestamp { - __s64 tv_sec; - __u32 tv_nsec; - __s32 __reserved; -}; - -struct statx STATX_DEFINITION; -#endif - -/* Always define the newest version we are aware of as a distinct type, so that we can use it even if glibc - * defines an older definition */ -struct new_statx STATX_DEFINITION; -#endif /* NM_IGNORED */ - -/* a528d35e8bfcc521d7cb70aaf03e1bd296c8493f (4.11) */ -#ifndef AT_STATX_SYNC_AS_STAT -#define AT_STATX_SYNC_AS_STAT 0x0000 -#endif - -/* a528d35e8bfcc521d7cb70aaf03e1bd296c8493f (4.11) */ -#ifndef AT_STATX_FORCE_SYNC -#define AT_STATX_FORCE_SYNC 0x2000 -#endif - -/* a528d35e8bfcc521d7cb70aaf03e1bd296c8493f (4.11) */ -#ifndef AT_STATX_DONT_SYNC -#define AT_STATX_DONT_SYNC 0x4000 -#endif - -/* a528d35e8bfcc521d7cb70aaf03e1bd296c8493f (4.11) */ -#ifndef STATX_TYPE -#define STATX_TYPE 0x00000001U -#endif - -/* a528d35e8bfcc521d7cb70aaf03e1bd296c8493f (4.11) */ -#ifndef STATX_MODE -#define STATX_MODE 0x00000002U -#endif - -/* a528d35e8bfcc521d7cb70aaf03e1bd296c8493f (4.11) */ -#ifndef STATX_NLINK -#define STATX_NLINK 0x00000004U -#endif - -/* a528d35e8bfcc521d7cb70aaf03e1bd296c8493f (4.11) */ -#ifndef STATX_UID -#define STATX_UID 0x00000008U -#endif - -/* a528d35e8bfcc521d7cb70aaf03e1bd296c8493f (4.11) */ -#ifndef STATX_GID -#define STATX_GID 0x00000010U -#endif - -/* a528d35e8bfcc521d7cb70aaf03e1bd296c8493f (4.11) */ -#ifndef STATX_ATIME -#define STATX_ATIME 0x00000020U -#endif - -/* a528d35e8bfcc521d7cb70aaf03e1bd296c8493f (4.11) */ -#ifndef STATX_MTIME -#define STATX_MTIME 0x00000040U -#endif - -/* a528d35e8bfcc521d7cb70aaf03e1bd296c8493f (4.11) */ -#ifndef STATX_CTIME -#define STATX_CTIME 0x00000080U -#endif - -/* a528d35e8bfcc521d7cb70aaf03e1bd296c8493f (4.11) */ -#ifndef STATX_INO -#define STATX_INO 0x00000100U -#endif - -/* a528d35e8bfcc521d7cb70aaf03e1bd296c8493f (4.11) */ -#ifndef STATX_SIZE -#define STATX_SIZE 0x00000200U -#endif - -/* a528d35e8bfcc521d7cb70aaf03e1bd296c8493f (4.11) */ -#ifndef STATX_BLOCKS -#define STATX_BLOCKS 0x00000400U -#endif - -/* a528d35e8bfcc521d7cb70aaf03e1bd296c8493f (4.11) */ -#ifndef STATX_BTIME -#define STATX_BTIME 0x00000800U -#endif - -/* fa2fcf4f1df1559a0a4ee0f46915b496cc2ebf60 (5.8) */ -#ifndef STATX_MNT_ID -#define STATX_MNT_ID 0x00001000U -#endif - -/* 80340fe3605c0e78cfe496c3b3878be828cfdbfe (5.8) */ -#ifndef STATX_ATTR_MOUNT_ROOT -#define STATX_ATTR_MOUNT_ROOT 0x00002000 /* Root of a mount */ -#endif diff --git a/shared/systemd/src/basic/missing_syscall.h b/shared/systemd/src/basic/missing_syscall.h deleted file mode 100644 index 42d64753..00000000 --- a/shared/systemd/src/basic/missing_syscall.h +++ /dev/null @@ -1,889 +0,0 @@ -/* SPDX-License-Identifier: LGPL-2.1-or-later */ -#pragma once - -/* Missing glibc definitions to access certain kernel APIs */ - -#include <errno.h> -#include <fcntl.h> -#include <signal.h> -#include <sys/syscall.h> -#include <sys/types.h> -#include <sys/wait.h> -#include <unistd.h> - -#ifdef ARCH_MIPS -#include <asm/sgidefs.h> -#endif - -#if defined(__alpha__) -# define systemd_SC_arch_bias(x) (110 + (x)) -#elif defined(__ia64__) -# define systemd_SC_arch_bias(x) (1024 + (x)) -#elif defined(_MIPS_SIM) -# if _MIPS_SIM == _MIPS_SIM_ABI32 -# define systemd_SC_arch_bias(x) (4000 + (x)) -# elif _MIPS_SIM == _MIPS_SIM_NABI32 -# define systemd_SC_arch_bias(x) (6000 + (x)) -# elif _MIPS_SIM == _MIPS_SIM_ABI64 -# define systemd_SC_arch_bias(x) (5000 + (x)) -# else -# error "Unknown MIPS ABI" -# endif -#elif defined(__x86_64__) && defined(__ILP32__) -# define systemd_SC_arch_bias(x) ((x) | /* __X32_SYSCALL_BIT */ 0x40000000) -#else -# define systemd_SC_arch_bias(x) (x) -#endif - -#include "missing_keyctl.h" -#include "missing_stat.h" - -#if 0 /* NM_IGNORED */ - -/* linux/kcmp.h */ -#ifndef KCMP_FILE /* 3f4994cfc15f38a3159c6e3a4b3ab2e1481a6b02 (3.19) */ -#define KCMP_FILE 0 -#endif - -#if !HAVE_PIVOT_ROOT -static inline int missing_pivot_root(const char *new_root, const char *put_old) { - return syscall(__NR_pivot_root, new_root, put_old); -} - -# define pivot_root missing_pivot_root -#endif - -/* ======================================================================= */ - -#if defined(__aarch64__) -# define systemd_NR_memfd_create 279 -#elif defined(__alpha__) -# define systemd_NR_memfd_create 512 -#elif defined(__arc__) || defined(__tilegx__) -# define systemd_NR_memfd_create 279 -#elif defined(__arm__) -# define systemd_NR_memfd_create 385 -#elif defined(__i386__) -# define systemd_NR_memfd_create 356 -#elif defined(__ia64__) -# define systemd_NR_memfd_create systemd_SC_arch_bias(316) -#elif defined(__m68k__) -# define systemd_NR_memfd_create 353 -#elif defined(_MIPS_SIM) -# if _MIPS_SIM == _MIPS_SIM_ABI32 -# define systemd_NR_memfd_create systemd_SC_arch_bias(354) -# elif _MIPS_SIM == _MIPS_SIM_NABI32 -# define systemd_NR_memfd_create systemd_SC_arch_bias(318) -# elif _MIPS_SIM == _MIPS_SIM_ABI64 -# define systemd_NR_memfd_create systemd_SC_arch_bias(314) -# endif -#elif defined(__powerpc__) -# define systemd_NR_memfd_create 360 -#elif defined(__s390__) -# define systemd_NR_memfd_create 350 -#elif defined(__sparc__) -# define systemd_NR_memfd_create 348 -#elif defined(__x86_64__) -# define systemd_NR_memfd_create systemd_SC_arch_bias(319) -#else -# warning "memfd_create() syscall number is unknown for your architecture" -#endif - -/* may be (invalid) negative number due to libseccomp, see PR 13319 */ -#if defined __NR_memfd_create && __NR_memfd_create >= 0 -# if defined systemd_NR_memfd_create -assert_cc(__NR_memfd_create == systemd_NR_memfd_create); -# endif -#else -# if defined __NR_memfd_create -# undef __NR_memfd_create -# endif -# if defined systemd_NR_memfd_create -# define __NR_memfd_create systemd_NR_memfd_create -# endif -#endif - -#if !HAVE_MEMFD_CREATE -static inline int missing_memfd_create(const char *name, unsigned int flags) { -# ifdef __NR_memfd_create - return syscall(__NR_memfd_create, name, flags); -# else - errno = ENOSYS; - return -1; -# endif -} - -# define memfd_create missing_memfd_create -#endif - -/* ======================================================================= */ - -#if defined(__aarch64__) -# define systemd_NR_getrandom 278 -#elif defined(__alpha__) -# define systemd_NR_getrandom 511 -#elif defined(__arc__) || defined(__tilegx__) -# define systemd_NR_getrandom 278 -#elif defined(__arm__) -# define systemd_NR_getrandom 384 -#elif defined(__i386__) -# define systemd_NR_getrandom 355 -#elif defined(__ia64__) -# define systemd_NR_getrandom systemd_SC_arch_bias(318) -#elif defined(__m68k__) -# define systemd_NR_getrandom 352 -#elif defined(_MIPS_SIM) -# if _MIPS_SIM == _MIPS_SIM_ABI32 -# define systemd_NR_getrandom systemd_SC_arch_bias(353) -# elif _MIPS_SIM == _MIPS_SIM_NABI32 -# define systemd_NR_getrandom systemd_SC_arch_bias(317) -# elif _MIPS_SIM == _MIPS_SIM_ABI64 -# define systemd_NR_getrandom systemd_SC_arch_bias(313) -# endif -#elif defined(__powerpc__) -# define systemd_NR_getrandom 359 -#elif defined(__s390__) -# define systemd_NR_getrandom 349 -#elif defined(__sparc__) -# define systemd_NR_getrandom 347 -#elif defined(__x86_64__) -# define systemd_NR_getrandom systemd_SC_arch_bias(318) -#else -# warning "getrandom() syscall number is unknown for your architecture" -#endif - -/* may be (invalid) negative number due to libseccomp, see PR 13319 */ -#if defined __NR_getrandom && __NR_getrandom >= 0 -# if defined systemd_NR_getrandom -assert_cc(__NR_getrandom == systemd_NR_getrandom); -# endif -#else -# if defined __NR_getrandom -# undef __NR_getrandom -# endif -# if defined systemd_NR_getrandom -# define __NR_getrandom systemd_NR_getrandom -# endif -#endif - -#if !HAVE_GETRANDOM -static inline int missing_getrandom(void *buffer, size_t count, unsigned flags) { -# ifdef __NR_getrandom - return syscall(__NR_getrandom, buffer, count, flags); -# else - errno = ENOSYS; - return -1; -# endif -} - -# define getrandom missing_getrandom -#endif - -/* ======================================================================= */ - -/* The syscall has been defined since forever, but the glibc wrapper was missing. */ -#if !HAVE_GETTID -static inline pid_t missing_gettid(void) { -# if defined __NR_gettid && __NR_gettid >= 0 - return (pid_t) syscall(__NR_gettid); -# else -# error "__NR_gettid not defined" -# endif -} - -# define gettid missing_gettid -#endif - -/* ======================================================================= */ - -#if defined(__aarch64__) -# define systemd_NR_name_to_handle_at 264 -#elif defined(__alpha__) -# define systemd_NR_name_to_handle_at 497 -#elif defined(__arc__) || defined(__tilegx__) -# define systemd_NR_name_to_handle_at 264 -#elif defined(__arm__) -# define systemd_NR_name_to_handle_at 370 -#elif defined(__i386__) -# define systemd_NR_name_to_handle_at 341 -#elif defined(__ia64__) -# define systemd_NR_name_to_handle_at systemd_SC_arch_bias(302) -#elif defined(__m68k__) -# define systemd_NR_name_to_handle_at 340 -#elif defined(_MIPS_SIM) -# if _MIPS_SIM == _MIPS_SIM_ABI32 -# define systemd_NR_name_to_handle_at systemd_SC_arch_bias(339) -# elif _MIPS_SIM == _MIPS_SIM_NABI32 -# define systemd_NR_name_to_handle_at systemd_SC_arch_bias(303) -# elif _MIPS_SIM == _MIPS_SIM_ABI64 -# define systemd_NR_name_to_handle_at systemd_SC_arch_bias(298) -# endif -#elif defined(__powerpc__) -# define systemd_NR_name_to_handle_at 345 -#elif defined(__s390__) -# define systemd_NR_name_to_handle_at 335 -#elif defined(__sparc__) -# define systemd_NR_name_to_handle_at 332 -#elif defined(__x86_64__) -# define systemd_NR_name_to_handle_at systemd_SC_arch_bias(303) -#else -# warning "name_to_handle_at() syscall number is unknown for your architecture" -#endif - -/* may be (invalid) negative number due to libseccomp, see PR 13319 */ -#if defined __NR_name_to_handle_at && __NR_name_to_handle_at >= 0 -# if defined systemd_NR_name_to_handle_at -assert_cc(__NR_name_to_handle_at == systemd_NR_name_to_handle_at); -# endif -#else -# if defined __NR_name_to_handle_at -# undef __NR_name_to_handle_at -# endif -# if defined systemd_NR_name_to_handle_at -# define __NR_name_to_handle_at systemd_NR_name_to_handle_at -# endif -#endif - -#if !HAVE_NAME_TO_HANDLE_AT -struct file_handle { - unsigned int handle_bytes; - int handle_type; - unsigned char f_handle[0]; -}; - -static inline int missing_name_to_handle_at(int fd, const char *name, struct file_handle *handle, int *mnt_id, int flags) { -# ifdef __NR_name_to_handle_at - return syscall(__NR_name_to_handle_at, fd, name, handle, mnt_id, flags); -# else - errno = ENOSYS; - return -1; -# endif -} - -# define name_to_handle_at missing_name_to_handle_at -#endif - -/* ======================================================================= */ - -#if defined(__aarch64__) -# define systemd_NR_setns 268 -#elif defined(__alpha__) -# define systemd_NR_setns 501 -#elif defined(__arc__) || defined(__tilegx__) -# define systemd_NR_setns 268 -#elif defined(__arm__) -# define systemd_NR_setns 375 -#elif defined(__i386__) -# define systemd_NR_setns 346 -#elif defined(__ia64__) -# define systemd_NR_setns systemd_SC_arch_bias(306) -#elif defined(__m68k__) -# define systemd_NR_setns 344 -#elif defined(_MIPS_SIM) -# if _MIPS_SIM == _MIPS_SIM_ABI32 -# define systemd_NR_setns systemd_SC_arch_bias(344) -# elif _MIPS_SIM == _MIPS_SIM_NABI32 -# define systemd_NR_setns systemd_SC_arch_bias(308) -# elif _MIPS_SIM == _MIPS_SIM_ABI64 -# define systemd_NR_setns systemd_SC_arch_bias(303) -# endif -#elif defined(__powerpc__) -# define systemd_NR_setns 350 -#elif defined(__s390__) -# define systemd_NR_setns 339 -#elif defined(__sparc__) -# define systemd_NR_setns 337 -#elif defined(__x86_64__) -# define systemd_NR_setns systemd_SC_arch_bias(308) -#else -# warning "setns() syscall number is unknown for your architecture" -#endif - -/* may be (invalid) negative number due to libseccomp, see PR 13319 */ -#if defined __NR_setns && __NR_setns >= 0 -# if defined systemd_NR_setns -assert_cc(__NR_setns == systemd_NR_setns); -# endif -#else -# if defined __NR_setns -# undef __NR_setns -# endif -# if defined systemd_NR_setns -# define __NR_setns systemd_NR_setns -# endif -#endif - -#if !HAVE_SETNS -static inline int missing_setns(int fd, int nstype) { -# ifdef __NR_setns - return syscall(__NR_setns, fd, nstype); -# else - errno = ENOSYS; - return -1; -# endif -} - -# define setns missing_setns -#endif - -/* ======================================================================= */ - -static inline pid_t raw_getpid(void) { -#if defined(__alpha__) - return (pid_t) syscall(__NR_getxpid); -#else - return (pid_t) syscall(__NR_getpid); -#endif -} - -/* ======================================================================= */ - -#if defined(__aarch64__) -# define systemd_NR_renameat2 276 -#elif defined(__alpha__) -# define systemd_NR_renameat2 510 -#elif defined(__arc__) || defined(__tilegx__) -# define systemd_NR_renameat2 276 -#elif defined(__arm__) -# define systemd_NR_renameat2 382 -#elif defined(__i386__) -# define systemd_NR_renameat2 353 -#elif defined(__ia64__) -# define systemd_NR_renameat2 systemd_SC_arch_bias(314) -#elif defined(__m68k__) -# define systemd_NR_renameat2 351 -#elif defined(_MIPS_SIM) -# if _MIPS_SIM == _MIPS_SIM_ABI32 -# define systemd_NR_renameat2 systemd_SC_arch_bias(351) -# elif _MIPS_SIM == _MIPS_SIM_NABI32 -# define systemd_NR_renameat2 systemd_SC_arch_bias(315) -# elif _MIPS_SIM == _MIPS_SIM_ABI64 -# define systemd_NR_renameat2 systemd_SC_arch_bias(311) -# endif -#elif defined(__powerpc__) -# define systemd_NR_renameat2 357 -#elif defined(__s390__) -# define systemd_NR_renameat2 347 -#elif defined(__sparc__) -# define systemd_NR_renameat2 345 -#elif defined(__x86_64__) -# define systemd_NR_renameat2 systemd_SC_arch_bias(316) -#else -# warning "renameat2() syscall number is unknown for your architecture" -#endif - -/* may be (invalid) negative number due to libseccomp, see PR 13319 */ -#if defined __NR_renameat2 && __NR_renameat2 >= 0 -# if defined systemd_NR_renameat2 -assert_cc(__NR_renameat2 == systemd_NR_renameat2); -# endif -#else -# if defined __NR_renameat2 -# undef __NR_renameat2 -# endif -# if defined systemd_NR_renameat2 -# define __NR_renameat2 systemd_NR_renameat2 -# endif -#endif - -#if !HAVE_RENAMEAT2 -static inline int missing_renameat2(int oldfd, const char *oldname, int newfd, const char *newname, unsigned flags) { -# ifdef __NR_renameat2 - return syscall(__NR_renameat2, oldfd, oldname, newfd, newname, flags); -# else - errno = ENOSYS; - return -1; -# endif -} - -# define renameat2 missing_renameat2 -#endif - -/* ======================================================================= */ - -#if !HAVE_KCMP -static inline int missing_kcmp(pid_t pid1, pid_t pid2, int type, unsigned long idx1, unsigned long idx2) { -# if defined __NR_kcmp && __NR_kcmp >= 0 - return syscall(__NR_kcmp, pid1, pid2, type, idx1, idx2); -# else - errno = ENOSYS; - return -1; -# endif -} - -# define kcmp missing_kcmp -#endif - -/* ======================================================================= */ - -#if !HAVE_KEYCTL -static inline long missing_keyctl(int cmd, unsigned long arg2, unsigned long arg3, unsigned long arg4, unsigned long arg5) { -# if defined __NR_keyctl && __NR_keyctl >= 0 - return syscall(__NR_keyctl, cmd, arg2, arg3, arg4, arg5); -# else - errno = ENOSYS; - return -1; -# endif - -# define keyctl missing_keyctl -} - -static inline key_serial_t missing_add_key(const char *type, const char *description, const void *payload, size_t plen, key_serial_t ringid) { -# if defined __NR_add_key && __NR_add_key >= 0 - return syscall(__NR_add_key, type, description, payload, plen, ringid); -# else - errno = ENOSYS; - return -1; -# endif - -# define add_key missing_add_key -} - -static inline key_serial_t missing_request_key(const char *type, const char *description, const char * callout_info, key_serial_t destringid) { -# if defined __NR_request_key && __NR_request_key >= 0 - return syscall(__NR_request_key, type, description, callout_info, destringid); -# else - errno = ENOSYS; - return -1; -# endif - -# define request_key missing_request_key -} -#endif - -/* ======================================================================= */ - -#if defined(__aarch64__) -# define systemd_NR_copy_file_range 285 -#elif defined(__alpha__) -# define systemd_NR_copy_file_range 519 -#elif defined(__arc__) || defined(__tilegx__) -# define systemd_NR_copy_file_range 285 -#elif defined(__arm__) -# define systemd_NR_copy_file_range 391 -#elif defined(__i386__) -# define systemd_NR_copy_file_range 377 -#elif defined(__ia64__) -# define systemd_NR_copy_file_range systemd_SC_arch_bias(323) -#elif defined(__m68k__) -# define systemd_NR_copy_file_range 376 -#elif defined(_MIPS_SIM) -# if _MIPS_SIM == _MIPS_SIM_ABI32 -# define systemd_NR_copy_file_range systemd_SC_arch_bias(360) -# elif _MIPS_SIM == _MIPS_SIM_NABI32 -# define systemd_NR_copy_file_range systemd_SC_arch_bias(324) -# elif _MIPS_SIM == _MIPS_SIM_ABI64 -# define systemd_NR_copy_file_range systemd_SC_arch_bias(320) -# endif -#elif defined(__powerpc__) -# define systemd_NR_copy_file_range 379 -#elif defined(__s390__) -# define systemd_NR_copy_file_range 375 -#elif defined(__sparc__) -# define systemd_NR_copy_file_range 357 -#elif defined(__x86_64__) -# define systemd_NR_copy_file_range systemd_SC_arch_bias(326) -#else -# warning "copy_file_range() syscall number is unknown for your architecture" -#endif - -/* may be (invalid) negative number due to libseccomp, see PR 13319 */ -#if defined __NR_copy_file_range && __NR_copy_file_range >= 0 -# if defined systemd_NR_copy_file_range -assert_cc(__NR_copy_file_range == systemd_NR_copy_file_range); -# endif -#else -# if defined __NR_copy_file_range -# undef __NR_copy_file_range -# endif -# if defined systemd_NR_copy_file_range -# define __NR_copy_file_range systemd_NR_copy_file_range -# endif -#endif - -#if !HAVE_COPY_FILE_RANGE -static inline ssize_t missing_copy_file_range(int fd_in, loff_t *off_in, - int fd_out, loff_t *off_out, - size_t len, - unsigned int flags) { -# ifdef __NR_copy_file_range - return syscall(__NR_copy_file_range, fd_in, off_in, fd_out, off_out, len, flags); -# else - errno = ENOSYS; - return -1; -# endif -} - -# define copy_file_range missing_copy_file_range -#endif - -/* ======================================================================= */ - -#if defined(__aarch64__) -# define systemd_NR_bpf 280 -#elif defined(__alpha__) -# define systemd_NR_bpf 515 -#elif defined(__arc__) || defined(__tilegx__) -# define systemd_NR_bpf 280 -#elif defined(__arm__) -# define systemd_NR_bpf 386 -#elif defined(__i386__) -# define systemd_NR_bpf 357 -#elif defined(__ia64__) -# define systemd_NR_bpf systemd_SC_arch_bias(317) -#elif defined(__m68k__) -# define systemd_NR_bpf 354 -#elif defined(_MIPS_SIM) -# if _MIPS_SIM == _MIPS_SIM_ABI32 -# define systemd_NR_bpf systemd_SC_arch_bias(355) -# elif _MIPS_SIM == _MIPS_SIM_NABI32 -# define systemd_NR_bpf systemd_SC_arch_bias(319) -# elif _MIPS_SIM == _MIPS_SIM_ABI64 -# define systemd_NR_bpf systemd_SC_arch_bias(315) -# endif -#elif defined(__powerpc__) -# define systemd_NR_bpf 361 -#elif defined(__s390__) -# define systemd_NR_bpf 351 -#elif defined(__sparc__) -# define systemd_NR_bpf 349 -#elif defined(__x86_64__) -# define systemd_NR_bpf systemd_SC_arch_bias(321) -#else -# warning "bpf() syscall number is unknown for your architecture" -#endif - -/* may be (invalid) negative number due to libseccomp, see PR 13319 */ -#if defined __NR_bpf && __NR_bpf >= 0 -# if defined systemd_NR_bpf -assert_cc(__NR_bpf == systemd_NR_bpf); -# endif -#else -# if defined __NR_bpf -# undef __NR_bpf -# endif -# if defined systemd_NR_bpf -# define __NR_bpf systemd_NR_bpf -# endif -#endif - -#if !HAVE_BPF -union bpf_attr; - -static inline int missing_bpf(int cmd, union bpf_attr *attr, size_t size) { -#ifdef __NR_bpf - return (int) syscall(__NR_bpf, cmd, attr, size); -#else - errno = ENOSYS; - return -1; -#endif -} - -# define bpf missing_bpf -#endif - -/* ======================================================================= */ - -#ifndef __IGNORE_pkey_mprotect -# if defined(__aarch64__) -# define systemd_NR_pkey_mprotect 288 -# elif defined(__alpha__) -# define systemd_NR_pkey_mprotect 524 -# elif defined(__arc__) || defined(__tilegx__) -# define systemd_NR_pkey_mprotect 226 -# elif defined(__arm__) -# define systemd_NR_pkey_mprotect 394 -# elif defined(__i386__) -# define systemd_NR_pkey_mprotect 380 -# elif defined(__ia64__) -# define systemd_NR_pkey_mprotect systemd_SC_arch_bias(330) -# elif defined(__m68k__) -# define systemd_NR_pkey_mprotect 381 -# elif defined(_MIPS_SIM) -# if _MIPS_SIM == _MIPS_SIM_ABI32 -# define systemd_NR_pkey_mprotect systemd_SC_arch_bias(363) -# elif _MIPS_SIM == _MIPS_SIM_NABI32 -# define systemd_NR_pkey_mprotect systemd_SC_arch_bias(327) -# elif _MIPS_SIM == _MIPS_SIM_ABI64 -# define systemd_NR_pkey_mprotect systemd_SC_arch_bias(323) -# endif -# elif defined(__powerpc__) -# define systemd_NR_pkey_mprotect 386 -# elif defined(__s390__) -# define systemd_NR_pkey_mprotect 384 -# elif defined(__sparc__) -# define systemd_NR_pkey_mprotect 362 -# elif defined(__x86_64__) -# define systemd_NR_pkey_mprotect systemd_SC_arch_bias(329) -# else -# warning "pkey_mprotect() syscall number is unknown for your architecture" -# endif - -/* may be (invalid) negative number due to libseccomp, see PR 13319 */ -# if defined __NR_pkey_mprotect && __NR_pkey_mprotect >= 0 -# if defined systemd_NR_pkey_mprotect -assert_cc(__NR_pkey_mprotect == systemd_NR_pkey_mprotect); -# endif -# else -# if defined __NR_pkey_mprotect -# undef __NR_pkey_mprotect -# endif -# if defined systemd_NR_pkey_mprotect -# define __NR_pkey_mprotect systemd_NR_pkey_mprotect -# endif -# endif -#endif - -/* ======================================================================= */ - -#if defined(__aarch64__) -# define systemd_NR_statx 291 -#elif defined(__alpha__) -# define systemd_NR_statx 522 -#elif defined(__arc__) || defined(__tilegx__) -# define systemd_NR_statx 291 -#elif defined(__arm__) -# define systemd_NR_statx 397 -#elif defined(__i386__) -# define systemd_NR_statx 383 -#elif defined(__ia64__) -# define systemd_NR_statx systemd_SC_arch_bias(326) -#elif defined(__m68k__) -# define systemd_NR_statx 379 -#elif defined(_MIPS_SIM) -# if _MIPS_SIM == _MIPS_SIM_ABI32 -# define systemd_NR_statx systemd_SC_arch_bias(366) -# elif _MIPS_SIM == _MIPS_SIM_NABI32 -# define systemd_NR_statx systemd_SC_arch_bias(330) -# elif _MIPS_SIM == _MIPS_SIM_ABI64 -# define systemd_NR_statx systemd_SC_arch_bias(326) -# endif -#elif defined(__powerpc__) -# define systemd_NR_statx 383 -#elif defined(__s390__) -# define systemd_NR_statx 379 -#elif defined(__sparc__) -# define systemd_NR_statx 360 -#elif defined(__x86_64__) -# define systemd_NR_statx systemd_SC_arch_bias(332) -#else -# warning "statx() syscall number is unknown for your architecture" -#endif - -/* may be (invalid) negative number due to libseccomp, see PR 13319 */ -#if defined __NR_statx && __NR_statx >= 0 -# if defined systemd_NR_statx -assert_cc(__NR_statx == systemd_NR_statx); -# endif -#else -# if defined __NR_statx -# undef __NR_statx -# endif -# if defined systemd_NR_statx -# define __NR_statx systemd_NR_statx -# endif -#endif - -#if !HAVE_STATX -struct statx; - -static inline ssize_t missing_statx(int dfd, const char *filename, unsigned flags, unsigned int mask, struct statx *buffer) { -# ifdef __NR_statx - return syscall(__NR_statx, dfd, filename, flags, mask, buffer); -# else - errno = ENOSYS; - return -1; -# endif -} -#endif - -/* This typedef is supposed to be always defined. */ -typedef struct statx struct_statx; - -#if !HAVE_STATX -# define statx(dfd, filename, flags, mask, buffer) missing_statx(dfd, filename, flags, mask, buffer) -#endif - -/* ======================================================================= */ - -#if !HAVE_SET_MEMPOLICY -enum { - MPOL_DEFAULT, - MPOL_PREFERRED, - MPOL_BIND, - MPOL_INTERLEAVE, - MPOL_LOCAL, -}; - -static inline long missing_set_mempolicy(int mode, const unsigned long *nodemask, - unsigned long maxnode) { - long i; -# if defined __NR_set_mempolicy && __NR_set_mempolicy >= 0 - i = syscall(__NR_set_mempolicy, mode, nodemask, maxnode); -# else - errno = ENOSYS; - i = -1; -# endif - return i; -} - -# define set_mempolicy missing_set_mempolicy -#endif - -#if !HAVE_GET_MEMPOLICY -static inline long missing_get_mempolicy(int *mode, unsigned long *nodemask, - unsigned long maxnode, void *addr, - unsigned long flags) { - long i; -# if defined __NR_get_mempolicy && __NR_get_mempolicy >= 0 - i = syscall(__NR_get_mempolicy, mode, nodemask, maxnode, addr, flags); -# else - errno = ENOSYS; - i = -1; -# endif - return i; -} - -# define get_mempolicy missing_get_mempolicy -#endif - -#endif /* NM_IGNORED */ - -/* ======================================================================= */ - -/* should be always defined, see kernel 39036cd2727395c3369b1051005da74059a85317 */ -#define systemd_NR_pidfd_send_signal systemd_SC_arch_bias(424) - -/* may be (invalid) negative number due to libseccomp, see PR 13319 */ -#if defined __NR_pidfd_send_signal && __NR_pidfd_send_signal >= 0 -# if defined systemd_NR_pidfd_send_signal -assert_cc(__NR_pidfd_send_signal == systemd_NR_pidfd_send_signal); -# endif -#else -# if defined __NR_pidfd_send_signal -# undef __NR_pidfd_send_signal -# endif -# define __NR_pidfd_send_signal systemd_NR_pidfd_send_signal -#endif - -#if !HAVE_PIDFD_SEND_SIGNAL -static inline int missing_pidfd_send_signal(int fd, int sig, siginfo_t *info, unsigned flags) { -# ifdef __NR_pidfd_send_signal - return syscall(__NR_pidfd_send_signal, fd, sig, info, flags); -# else - errno = ENOSYS; - return -1; -# endif -} - -# define pidfd_send_signal missing_pidfd_send_signal -#endif - -/* should be always defined, see kernel 7615d9e1780e26e0178c93c55b73309a5dc093d7 */ -#define systemd_NR_pidfd_open systemd_SC_arch_bias(434) - -/* may be (invalid) negative number due to libseccomp, see PR 13319 */ -#if defined __NR_pidfd_open && __NR_pidfd_open >= 0 -# if defined systemd_NR_pidfd_open -assert_cc(__NR_pidfd_open == systemd_NR_pidfd_open); -# endif -#else -# if defined __NR_pidfd_open -# undef __NR_pidfd_open -# endif -# define __NR_pidfd_open systemd_NR_pidfd_open -#endif - -#if !HAVE_PIDFD_OPEN -static inline int missing_pidfd_open(pid_t pid, unsigned flags) { -# ifdef __NR_pidfd_open - return syscall(__NR_pidfd_open, pid, flags); -# else - errno = ENOSYS; - return -1; -# endif -} - -# define pidfd_open missing_pidfd_open -#endif - -/* ======================================================================= */ - -#if !HAVE_RT_SIGQUEUEINFO -static inline int missing_rt_sigqueueinfo(pid_t tgid, int sig, siginfo_t *info) { -# if defined __NR_rt_sigqueueinfo && __NR_rt_sigqueueinfo >= 0 - return syscall(__NR_rt_sigqueueinfo, tgid, sig, info); -# else -# error "__NR_rt_sigqueueinfo not defined" -# endif -} - -# define rt_sigqueueinfo missing_rt_sigqueueinfo -#endif - -/* ======================================================================= */ - -#if 0 /* NM_IGNORED */ -#if !HAVE_EXECVEAT -static inline int missing_execveat(int dirfd, const char *pathname, - char *const argv[], char *const envp[], - int flags) { -# if defined __NR_execveat && __NR_execveat >= 0 - return syscall(__NR_execveat, dirfd, pathname, argv, envp, flags); -# else - errno = ENOSYS; - return -1; -# endif -} - -# undef AT_EMPTY_PATH -# define AT_EMPTY_PATH 0x1000 -# define execveat missing_execveat -#endif - -/* ======================================================================= */ - -#define systemd_NR_close_range systemd_SC_arch_bias(436) - -/* may be (invalid) negative number due to libseccomp, see PR 13319 */ -#if defined __NR_close_range && __NR_close_range >= 0 -# if defined systemd_NR_close_range -assert_cc(__NR_close_range == systemd_NR_close_range); -# endif -#else -# if defined __NR_close_range -# undef __NR_close_range -# endif -# if defined systemd_NR_close_range -# define __NR_close_range systemd_NR_close_range -# endif -#endif - -#if !HAVE_CLOSE_RANGE -static inline int missing_close_range(int first_fd, int end_fd, unsigned flags) { -# ifdef __NR_close_range - /* Kernel-side the syscall expects fds as unsigned integers (just like close() actually), while - * userspace exclusively uses signed integers for fds. We don't know just yet how glibc is going to - * wrap this syscall, but let's assume it's going to be similar to what they do for close(), - * i.e. make the same unsigned → signed type change from the raw kernel syscall compared to the - * userspace wrapper. There's only one caveat for this: unlike for close() there's the special - * UINT_MAX fd value for the 'end_fd' argument. Let's safely map that to -1 here. And let's refuse - * any other negative values. */ - if ((first_fd < 0) || (end_fd < 0 && end_fd != -1)) { - errno = -EBADF; - return -1; - } - - return syscall(__NR_close_range, - (unsigned) first_fd, - end_fd == -1 ? UINT_MAX : (unsigned) end_fd, /* Of course, the compiler should figure out that this is the identity mapping IRL */ - flags); -# else - errno = ENOSYS; - return -1; -# endif -} - -# define close_range missing_close_range -#endif -#endif /* NM_IGNORED */ diff --git a/shared/systemd/src/basic/missing_type.h b/shared/systemd/src/basic/missing_type.h deleted file mode 100644 index f6233090..00000000 --- a/shared/systemd/src/basic/missing_type.h +++ /dev/null @@ -1,12 +0,0 @@ -/* SPDX-License-Identifier: LGPL-2.1-or-later */ -#pragma once - -#include <uchar.h> - -#if !HAVE_CHAR32_T -#define char32_t uint32_t -#endif - -#if !HAVE_CHAR16_T -#define char16_t uint16_t -#endif diff --git a/shared/systemd/src/basic/parse-util.c b/shared/systemd/src/basic/parse-util.c deleted file mode 100644 index d53bf620..00000000 --- a/shared/systemd/src/basic/parse-util.c +++ /dev/null @@ -1,912 +0,0 @@ -/* SPDX-License-Identifier: LGPL-2.1-or-later */ - -#include "nm-sd-adapt-shared.h" - -#include <errno.h> -#include <inttypes.h> -#include <linux/oom.h> -#include <net/if.h> -#include <stdio.h> -#include <stdlib.h> -#include <sys/socket.h> - -#include "alloc-util.h" -#include "errno-list.h" -#include "extract-word.h" -#include "locale-util.h" -#include "macro.h" -#include "missing_network.h" -#include "parse-util.h" -#include "process-util.h" -#if HAVE_SECCOMP -#include "seccomp-util.h" -#endif -#include "stat-util.h" -#include "string-util.h" -#include "strv.h" - -int parse_boolean(const char *v) { - if (!v) - return -EINVAL; - - if (STRCASE_IN_SET(v, - "1", - "yes", - "y", - "true", - "t", - "on")) - return 1; - - if (STRCASE_IN_SET(v, - "0", - "no", - "n", - "false", - "f", - "off")) - return 0; - - return -EINVAL; -} - -#if 0 /* NM_IGNORED */ -int parse_pid(const char *s, pid_t* ret_pid) { - unsigned long ul = 0; - pid_t pid; - int r; - - assert(s); - assert(ret_pid); - - r = safe_atolu(s, &ul); - if (r < 0) - return r; - - pid = (pid_t) ul; - - if ((unsigned long) pid != ul) - return -ERANGE; - - if (!pid_is_valid(pid)) - return -ERANGE; - - *ret_pid = pid; - return 0; -} - -int parse_mode(const char *s, mode_t *ret) { - unsigned m; - int r; - - assert(s); - - r = safe_atou_full(s, 8 | - SAFE_ATO_REFUSE_PLUS_MINUS, /* Leading '+' or even '-' char? that's just weird, - * refuse. User might have wanted to add mode flags or - * so, but this parser doesn't allow that, so let's - * better be safe. */ - &m); - if (r < 0) - return r; - if (m > 07777) - return -ERANGE; - - if (ret) - *ret = m; - return 0; -} - -int parse_ifindex(const char *s) { - int ifi, r; - - assert(s); - - r = safe_atoi(s, &ifi); - if (r < 0) - return r; - if (ifi <= 0) - return -EINVAL; - - return ifi; -} - -int parse_mtu(int family, const char *s, uint32_t *ret) { - uint64_t u; - size_t m; - int r; - - r = parse_size(s, 1024, &u); - if (r < 0) - return r; - - if (u > UINT32_MAX) - return -ERANGE; - - if (family == AF_INET6) - m = IPV6_MIN_MTU; /* This is 1280 */ - else - m = IPV4_MIN_MTU; /* For all other protocols, including 'unspecified' we assume the IPv4 minimal MTU */ - - if (u < m) - return -ERANGE; - - *ret = (uint32_t) u; - return 0; -} - -int parse_size(const char *t, uint64_t base, uint64_t *size) { - - /* Soo, sometimes we want to parse IEC binary suffixes, and - * sometimes SI decimal suffixes. This function can parse - * both. Which one is the right way depends on the - * context. Wikipedia suggests that SI is customary for - * hardware metrics and network speeds, while IEC is - * customary for most data sizes used by software and volatile - * (RAM) memory. Hence be careful which one you pick! - * - * In either case we use just K, M, G as suffix, and not Ki, - * Mi, Gi or so (as IEC would suggest). That's because that's - * frickin' ugly. But this means you really need to make sure - * to document which base you are parsing when you use this - * call. */ - - struct table { - const char *suffix; - unsigned long long factor; - }; - - static const struct table iec[] = { - { "E", 1024ULL*1024ULL*1024ULL*1024ULL*1024ULL*1024ULL }, - { "P", 1024ULL*1024ULL*1024ULL*1024ULL*1024ULL }, - { "T", 1024ULL*1024ULL*1024ULL*1024ULL }, - { "G", 1024ULL*1024ULL*1024ULL }, - { "M", 1024ULL*1024ULL }, - { "K", 1024ULL }, - { "B", 1ULL }, - { "", 1ULL }, - }; - - static const struct table si[] = { - { "E", 1000ULL*1000ULL*1000ULL*1000ULL*1000ULL*1000ULL }, - { "P", 1000ULL*1000ULL*1000ULL*1000ULL*1000ULL }, - { "T", 1000ULL*1000ULL*1000ULL*1000ULL }, - { "G", 1000ULL*1000ULL*1000ULL }, - { "M", 1000ULL*1000ULL }, - { "K", 1000ULL }, - { "B", 1ULL }, - { "", 1ULL }, - }; - - const struct table *table; - const char *p; - unsigned long long r = 0; - unsigned n_entries, start_pos = 0; - - assert(t); - assert(IN_SET(base, 1000, 1024)); - assert(size); - - if (base == 1000) { - table = si; - n_entries = ELEMENTSOF(si); - } else { - table = iec; - n_entries = ELEMENTSOF(iec); - } - - p = t; - do { - unsigned long long l, tmp; - double frac = 0; - char *e; - unsigned i; - - p += strspn(p, WHITESPACE); - - errno = 0; - l = strtoull(p, &e, 10); - if (errno > 0) - return -errno; - if (e == p) - return -EINVAL; - if (*p == '-') - return -ERANGE; - - if (*e == '.') { - e++; - - /* strtoull() itself would accept space/+/- */ - if (*e >= '0' && *e <= '9') { - unsigned long long l2; - char *e2; - - l2 = strtoull(e, &e2, 10); - if (errno > 0) - return -errno; - - /* Ignore failure. E.g. 10.M is valid */ - frac = l2; - for (; e < e2; e++) - frac /= 10; - } - } - - e += strspn(e, WHITESPACE); - - for (i = start_pos; i < n_entries; i++) - if (startswith(e, table[i].suffix)) - break; - - if (i >= n_entries) - return -EINVAL; - - if (l + (frac > 0) > ULLONG_MAX / table[i].factor) - return -ERANGE; - - tmp = l * table[i].factor + (unsigned long long) (frac * table[i].factor); - if (tmp > ULLONG_MAX - r) - return -ERANGE; - - r += tmp; - if ((unsigned long long) (uint64_t) r != r) - return -ERANGE; - - p = e + strlen(table[i].suffix); - - start_pos = i + 1; - - } while (*p); - - *size = r; - - return 0; -} - -int parse_range(const char *t, unsigned *lower, unsigned *upper) { - _cleanup_free_ char *word = NULL; - unsigned l, u; - int r; - - assert(lower); - assert(upper); - - /* Extract the lower bound. */ - r = extract_first_word(&t, &word, "-", EXTRACT_DONT_COALESCE_SEPARATORS); - if (r < 0) - return r; - if (r == 0) - return -EINVAL; - - r = safe_atou(word, &l); - if (r < 0) - return r; - - /* Check for the upper bound and extract it if needed */ - if (!t) - /* Single number with no dashes. */ - u = l; - else if (!*t) - /* Trailing dash is an error. */ - return -EINVAL; - else { - r = safe_atou(t, &u); - if (r < 0) - return r; - } - - *lower = l; - *upper = u; - return 0; -} - -int parse_errno(const char *t) { - int r, e; - - assert(t); - - r = errno_from_name(t); - if (r > 0) - return r; - - r = safe_atoi(t, &e); - if (r < 0) - return r; - - /* 0 is also allowed here */ - if (!errno_is_valid(e) && e != 0) - return -ERANGE; - - return e; -} - -#if HAVE_SECCOMP -int parse_syscall_and_errno(const char *in, char **name, int *error) { - _cleanup_free_ char *n = NULL; - char *p; - int e = -1; - - assert(in); - assert(name); - assert(error); - - /* - * This parse "syscall:errno" like "uname:EILSEQ", "@sync:255". - * If errno is omitted, then error is set to -1. - * Empty syscall name is not allowed. - * Here, we do not check that the syscall name is valid or not. - */ - - p = strchr(in, ':'); - if (p) { - e = seccomp_parse_errno_or_action(p + 1); - if (e < 0) - return e; - - n = strndup(in, p - in); - } else - n = strdup(in); - - if (!n) - return -ENOMEM; - - if (isempty(n)) - return -EINVAL; - - *error = e; - *name = TAKE_PTR(n); - - return 0; -} -#endif -#endif /* NM_IGNORED */ - -static const char *mangle_base(const char *s, unsigned *base) { - const char *k; - - assert(s); - assert(base); - - /* Base already explicitly specified, then don't do anything. */ - if (SAFE_ATO_MASK_FLAGS(*base) != 0) - return s; - - /* Support Python 3 style "0b" and 0x" prefixes, because they truly make sense, much more than C's "0" prefix for octal. */ - k = STARTSWITH_SET(s, "0b", "0B"); - if (k) { - *base = 2 | (*base & SAFE_ATO_ALL_FLAGS); - return k; - } - - k = STARTSWITH_SET(s, "0o", "0O"); - if (k) { - *base = 8 | (*base & SAFE_ATO_ALL_FLAGS); - return k; - } - - return s; -} - -int safe_atou_full(const char *s, unsigned base, unsigned *ret_u) { - char *x = NULL; - unsigned long l; - - assert(s); - assert(SAFE_ATO_MASK_FLAGS(base) <= 16); - - /* strtoul() is happy to parse negative values, and silently converts them to unsigned values without - * generating an error. We want a clean error, hence let's look for the "-" prefix on our own, and - * generate an error. But let's do so only after strtoul() validated that the string is clean - * otherwise, so that we return EINVAL preferably over ERANGE. */ - - if (FLAGS_SET(base, SAFE_ATO_REFUSE_LEADING_WHITESPACE) && - strchr(WHITESPACE, s[0])) - return -EINVAL; - - s += strspn(s, WHITESPACE); - - if (FLAGS_SET(base, SAFE_ATO_REFUSE_PLUS_MINUS) && - IN_SET(s[0], '+', '-')) - return -EINVAL; /* Note that we check the "-" prefix again a second time below, but return a - * different error. I.e. if the SAFE_ATO_REFUSE_PLUS_MINUS flag is set we - * blanket refuse +/- prefixed integers, while if it is missing we'll just - * return ERANGE, because the string actually parses correctly, but doesn't - * fit in the return type. */ - - if (FLAGS_SET(base, SAFE_ATO_REFUSE_LEADING_ZERO) && - s[0] == '0' && !streq(s, "0")) - return -EINVAL; /* This is particularly useful to avoid ambiguities between C's octal - * notation and assumed-to-be-decimal integers with a leading zero. */ - - s = mangle_base(s, &base); - - errno = 0; - l = strtoul(s, &x, SAFE_ATO_MASK_FLAGS(base) /* Let's mask off the flags bits so that only the actual - * base is left */); - if (errno > 0) - return -errno; - if (!x || x == s || *x != 0) - return -EINVAL; - if (l != 0 && s[0] == '-') - return -ERANGE; - if ((unsigned long) (unsigned) l != l) - return -ERANGE; - - if (ret_u) - *ret_u = (unsigned) l; - - return 0; -} - -int safe_atoi(const char *s, int *ret_i) { - unsigned base = 0; - char *x = NULL; - long l; - - assert(s); - - s += strspn(s, WHITESPACE); - s = mangle_base(s, &base); - - errno = 0; - l = strtol(s, &x, base); - if (errno > 0) - return -errno; - if (!x || x == s || *x != 0) - return -EINVAL; - if ((long) (int) l != l) - return -ERANGE; - - if (ret_i) - *ret_i = (int) l; - - return 0; -} - -int safe_atollu_full(const char *s, unsigned base, long long unsigned *ret_llu) { - char *x = NULL; - unsigned long long l; - - assert(s); - assert(SAFE_ATO_MASK_FLAGS(base) <= 16); - - if (FLAGS_SET(base, SAFE_ATO_REFUSE_LEADING_WHITESPACE) && - strchr(WHITESPACE, s[0])) - return -EINVAL; - - s += strspn(s, WHITESPACE); - - if (FLAGS_SET(base, SAFE_ATO_REFUSE_PLUS_MINUS) && - IN_SET(s[0], '+', '-')) - return -EINVAL; - - if (FLAGS_SET(base, SAFE_ATO_REFUSE_LEADING_ZERO) && - s[0] == '0' && s[1] != 0) - return -EINVAL; - - s = mangle_base(s, &base); - - errno = 0; - l = strtoull(s, &x, SAFE_ATO_MASK_FLAGS(base)); - if (errno > 0) - return -errno; - if (!x || x == s || *x != 0) - return -EINVAL; - if (l != 0 && s[0] == '-') - return -ERANGE; - - if (ret_llu) - *ret_llu = l; - - return 0; -} - -int safe_atolli(const char *s, long long int *ret_lli) { - unsigned base = 0; - char *x = NULL; - long long l; - - assert(s); - - s += strspn(s, WHITESPACE); - s = mangle_base(s, &base); - - errno = 0; - l = strtoll(s, &x, base); - if (errno > 0) - return -errno; - if (!x || x == s || *x != 0) - return -EINVAL; - - if (ret_lli) - *ret_lli = l; - - return 0; -} - -int safe_atou8(const char *s, uint8_t *ret) { - unsigned base = 0; - unsigned long l; - char *x = NULL; - - assert(s); - - s += strspn(s, WHITESPACE); - s = mangle_base(s, &base); - - errno = 0; - l = strtoul(s, &x, base); - if (errno > 0) - return -errno; - if (!x || x == s || *x != 0) - return -EINVAL; - if (l != 0 && s[0] == '-') - return -ERANGE; - if ((unsigned long) (uint8_t) l != l) - return -ERANGE; - - if (ret) - *ret = (uint8_t) l; - return 0; -} - -int safe_atou16_full(const char *s, unsigned base, uint16_t *ret) { - char *x = NULL; - unsigned long l; - - assert(s); - assert(SAFE_ATO_MASK_FLAGS(base) <= 16); - - if (FLAGS_SET(base, SAFE_ATO_REFUSE_LEADING_WHITESPACE) && - strchr(WHITESPACE, s[0])) - return -EINVAL; - - s += strspn(s, WHITESPACE); - - if (FLAGS_SET(base, SAFE_ATO_REFUSE_PLUS_MINUS) && - IN_SET(s[0], '+', '-')) - return -EINVAL; - - if (FLAGS_SET(base, SAFE_ATO_REFUSE_LEADING_ZERO) && - s[0] == '0' && s[1] != 0) - return -EINVAL; - - s = mangle_base(s, &base); - - errno = 0; - l = strtoul(s, &x, SAFE_ATO_MASK_FLAGS(base)); - if (errno > 0) - return -errno; - if (!x || x == s || *x != 0) - return -EINVAL; - if (l != 0 && s[0] == '-') - return -ERANGE; - if ((unsigned long) (uint16_t) l != l) - return -ERANGE; - - if (ret) - *ret = (uint16_t) l; - - return 0; -} - -int safe_atoi16(const char *s, int16_t *ret) { - unsigned base = 0; - char *x = NULL; - long l; - - assert(s); - - s += strspn(s, WHITESPACE); - s = mangle_base(s, &base); - - errno = 0; - l = strtol(s, &x, base); - if (errno > 0) - return -errno; - if (!x || x == s || *x != 0) - return -EINVAL; - if ((long) (int16_t) l != l) - return -ERANGE; - - if (ret) - *ret = (int16_t) l; - - return 0; -} - -#if 0 /* NM_IGNORED */ -int safe_atod(const char *s, double *ret_d) { - _cleanup_(freelocalep) locale_t loc = (locale_t) 0; - char *x = NULL; - double d = 0; - - assert(s); - - loc = newlocale(LC_NUMERIC_MASK, "C", (locale_t) 0); - if (loc == (locale_t) 0) - return -errno; - - errno = 0; - d = strtod_l(s, &x, loc); - if (errno > 0) - return -errno; - if (!x || x == s || *x != 0) - return -EINVAL; - - if (ret_d) - *ret_d = (double) d; - - return 0; -} - -int parse_fractional_part_u(const char **p, size_t digits, unsigned *res) { - size_t i; - unsigned val = 0; - const char *s; - - s = *p; - - /* accept any number of digits, strtoull is limited to 19 */ - for (i=0; i < digits; i++,s++) { - if (*s < '0' || *s > '9') { - if (i == 0) - return -EINVAL; - - /* too few digits, pad with 0 */ - for (; i < digits; i++) - val *= 10; - - break; - } - - val *= 10; - val += *s - '0'; - } - - /* maybe round up */ - if (*s >= '5' && *s <= '9') - val++; - - s += strspn(s, DIGITS); - - *p = s; - *res = val; - - return 0; -} - -int parse_percent_unbounded(const char *p) { - const char *pc, *n; - int r, v; - - pc = endswith(p, "%"); - if (!pc) - return -EINVAL; - - n = strndupa(p, pc - p); - r = safe_atoi(n, &v); - if (r < 0) - return r; - if (v < 0) - return -ERANGE; - - return v; -} - -int parse_percent(const char *p) { - int v; - - v = parse_percent_unbounded(p); - if (v > 100) - return -ERANGE; - - return v; -} - -int parse_permille_unbounded(const char *p) { - const char *pc, *pm, *dot, *n; - int r, q, v; - - pm = endswith(p, "‰"); - if (pm) { - n = strndupa(p, pm - p); - r = safe_atoi(n, &v); - if (r < 0) - return r; - if (v < 0) - return -ERANGE; - } else { - pc = endswith(p, "%"); - if (!pc) - return -EINVAL; - - dot = memchr(p, '.', pc - p); - if (dot) { - if (dot + 2 != pc) - return -EINVAL; - if (dot[1] < '0' || dot[1] > '9') - return -EINVAL; - q = dot[1] - '0'; - n = strndupa(p, dot - p); - } else { - q = 0; - n = strndupa(p, pc - p); - } - r = safe_atoi(n, &v); - if (r < 0) - return r; - if (v < 0) - return -ERANGE; - if (v > (INT_MAX - q) / 10) - return -ERANGE; - - v = v * 10 + q; - } - - return v; -} - -int parse_permille(const char *p) { - int v; - - v = parse_permille_unbounded(p); - if (v > 1000) - return -ERANGE; - - return v; -} - -int parse_nice(const char *p, int *ret) { - int n, r; - - r = safe_atoi(p, &n); - if (r < 0) - return r; - - if (!nice_is_valid(n)) - return -ERANGE; - - *ret = n; - return 0; -} - -int parse_ip_port(const char *s, uint16_t *ret) { - uint16_t l; - int r; - - r = safe_atou16(s, &l); - if (r < 0) - return r; - - if (l == 0) - return -EINVAL; - - *ret = (uint16_t) l; - - return 0; -} - -int parse_ip_port_range(const char *s, uint16_t *low, uint16_t *high) { - unsigned l, h; - int r; - - r = parse_range(s, &l, &h); - if (r < 0) - return r; - - if (l <= 0 || l > 65535 || h <= 0 || h > 65535) - return -EINVAL; - - if (h < l) - return -EINVAL; - - *low = l; - *high = h; - - return 0; -} - -int parse_ip_prefix_length(const char *s, int *ret) { - unsigned l; - int r; - - r = safe_atou(s, &l); - if (r < 0) - return r; - - if (l > 128) - return -ERANGE; - - *ret = (int) l; - - return 0; -} - -int parse_dev(const char *s, dev_t *ret) { - const char *major; - unsigned x, y; - size_t n; - int r; - - n = strspn(s, DIGITS); - if (n == 0) - return -EINVAL; - if (s[n] != ':') - return -EINVAL; - - major = strndupa(s, n); - r = safe_atou(major, &x); - if (r < 0) - return r; - - r = safe_atou(s + n + 1, &y); - if (r < 0) - return r; - - if (!DEVICE_MAJOR_VALID(x) || !DEVICE_MINOR_VALID(y)) - return -ERANGE; - - *ret = makedev(x, y); - return 0; -} - -int parse_oom_score_adjust(const char *s, int *ret) { - int r, v; - - assert(s); - assert(ret); - - r = safe_atoi(s, &v); - if (r < 0) - return r; - - if (v < OOM_SCORE_ADJ_MIN || v > OOM_SCORE_ADJ_MAX) - return -ERANGE; - - *ret = v; - return 0; -} - -int store_loadavg_fixed_point(unsigned long i, unsigned long f, loadavg_t *ret) { - assert(ret); - - if (i >= (~0UL << FSHIFT)) - return -ERANGE; - - i = i << FSHIFT; - f = DIV_ROUND_UP((f << FSHIFT), 100); - - if (f >= FIXED_1) - return -ERANGE; - - *ret = i | f; - return 0; -} - -int parse_loadavg_fixed_point(const char *s, loadavg_t *ret) { - const char *d, *f_str, *i_str; - unsigned long i, f; - int r; - - assert(s); - assert(ret); - - d = strchr(s, '.'); - if (!d) - return -EINVAL; - - i_str = strndupa(s, d - s); - f_str = d + 1; - - r = safe_atolu_full(i_str, 10, &i); - if (r < 0) - return r; - - r = safe_atolu_full(f_str, 10, &f); - if (r < 0) - return r; - - return store_loadavg_fixed_point(i, f, ret); -} -#endif /* NM_IGNORED */ diff --git a/shared/systemd/src/basic/parse-util.h b/shared/systemd/src/basic/parse-util.h deleted file mode 100644 index ba4e727e..00000000 --- a/shared/systemd/src/basic/parse-util.h +++ /dev/null @@ -1,153 +0,0 @@ -/* SPDX-License-Identifier: LGPL-2.1-or-later */ -#pragma once - -#include <inttypes.h> -#include <limits.h> -#if 0 /* NM_IGNORED */ -#include <linux/loadavg.h> -#endif /* NM_IGNORED */ -#include <stddef.h> -#include <stdint.h> -#include <sys/types.h> - -#include "macro.h" - -typedef unsigned long loadavg_t; - -int parse_boolean(const char *v) _pure_; -int parse_dev(const char *s, dev_t *ret); -int parse_pid(const char *s, pid_t* ret_pid); -int parse_mode(const char *s, mode_t *ret); -int parse_ifindex(const char *s); -int parse_mtu(int family, const char *s, uint32_t *ret); - -int parse_size(const char *t, uint64_t base, uint64_t *size); -int parse_range(const char *t, unsigned *lower, unsigned *upper); -int parse_errno(const char *t); -#if HAVE_SECCOMP -int parse_syscall_and_errno(const char *in, char **name, int *error); -#endif - -#define SAFE_ATO_REFUSE_PLUS_MINUS (1U << 30) -#define SAFE_ATO_REFUSE_LEADING_ZERO (1U << 29) -#define SAFE_ATO_REFUSE_LEADING_WHITESPACE (1U << 28) -#define SAFE_ATO_ALL_FLAGS (SAFE_ATO_REFUSE_PLUS_MINUS|SAFE_ATO_REFUSE_LEADING_ZERO|SAFE_ATO_REFUSE_LEADING_WHITESPACE) -#define SAFE_ATO_MASK_FLAGS(base) ((base) & ~SAFE_ATO_ALL_FLAGS) - -int safe_atou_full(const char *s, unsigned base, unsigned *ret_u); - -static inline int safe_atou(const char *s, unsigned *ret_u) { - return safe_atou_full(s, 0, ret_u); -} - -int safe_atoi(const char *s, int *ret_i); -int safe_atolli(const char *s, long long int *ret_i); - -int safe_atou8(const char *s, uint8_t *ret); - -int safe_atou16_full(const char *s, unsigned base, uint16_t *ret); - -static inline int safe_atou16(const char *s, uint16_t *ret) { - return safe_atou16_full(s, 0, ret); -} - -static inline int safe_atoux16(const char *s, uint16_t *ret) { - return safe_atou16_full(s, 16, ret); -} - -int safe_atoi16(const char *s, int16_t *ret); - -static inline int safe_atou32_full(const char *s, unsigned base, uint32_t *ret_u) { - assert_cc(sizeof(uint32_t) == sizeof(unsigned)); - return safe_atou_full(s, base, (unsigned*) ret_u); -} - -static inline int safe_atou32(const char *s, uint32_t *ret_u) { - return safe_atou32_full(s, 0, (unsigned*) ret_u); -} - -static inline int safe_atoi32(const char *s, int32_t *ret_i) { - assert_cc(sizeof(int32_t) == sizeof(int)); - return safe_atoi(s, (int*) ret_i); -} - -int safe_atollu_full(const char *s, unsigned base, long long unsigned *ret_llu); - -static inline int safe_atollu(const char *s, long long unsigned *ret_llu) { - return safe_atollu_full(s, 0, ret_llu); -} - -static inline int safe_atou64(const char *s, uint64_t *ret_u) { - assert_cc(sizeof(uint64_t) == sizeof(unsigned long long)); - return safe_atollu(s, (unsigned long long*) ret_u); -} - -static inline int safe_atoi64(const char *s, int64_t *ret_i) { - assert_cc(sizeof(int64_t) == sizeof(long long int)); - return safe_atolli(s, (long long int*) ret_i); -} - -static inline int safe_atoux64(const char *s, uint64_t *ret) { - assert_cc(sizeof(int64_t) == sizeof(long long unsigned)); - return safe_atollu_full(s, 16, (long long unsigned*) ret); -} - -#if LONG_MAX == INT_MAX -static inline int safe_atolu_full(const char *s, unsigned base, long unsigned *ret_u) { - assert_cc(sizeof(unsigned long) == sizeof(unsigned)); - return safe_atou_full(s, base, (unsigned*) ret_u); -} -static inline int safe_atoli(const char *s, long int *ret_u) { - assert_cc(sizeof(long int) == sizeof(int)); - return safe_atoi(s, (int*) ret_u); -} -#else -static inline int safe_atolu_full(const char *s, unsigned base, unsigned long *ret_u) { - assert_cc(sizeof(unsigned long) == sizeof(unsigned long long)); - return safe_atollu_full(s, base, (unsigned long long*) ret_u); -} -static inline int safe_atoli(const char *s, long int *ret_u) { - assert_cc(sizeof(long int) == sizeof(long long int)); - return safe_atolli(s, (long long int*) ret_u); -} -#endif - -static inline int safe_atolu(const char *s, unsigned long *ret_u) { - return safe_atolu_full(s, 0, ret_u); -} - -#if SIZE_MAX == UINT_MAX -static inline int safe_atozu(const char *s, size_t *ret_u) { - assert_cc(sizeof(size_t) == sizeof(unsigned)); - return safe_atou(s, (unsigned *) ret_u); -} -#else -static inline int safe_atozu(const char *s, size_t *ret_u) { - assert_cc(sizeof(size_t) == sizeof(long unsigned)); - return safe_atolu(s, ret_u); -} -#endif - -int safe_atod(const char *s, double *ret_d); - -int parse_fractional_part_u(const char **s, size_t digits, unsigned *res); - -int parse_percent_unbounded(const char *p); -int parse_percent(const char *p); - -int parse_permille_unbounded(const char *p); -int parse_permille(const char *p); - -int parse_nice(const char *p, int *ret); - -int parse_ip_port(const char *s, uint16_t *ret); -int parse_ip_port_range(const char *s, uint16_t *low, uint16_t *high); - -int parse_ip_prefix_length(const char *s, int *ret); - -int parse_oom_score_adjust(const char *s, int *ret); - -/* Given a Linux load average (e.g. decimal number 34.89 where 34 is passed as i and 89 is passed as f), convert it - * to a loadavg_t. */ -int store_loadavg_fixed_point(unsigned long i, unsigned long f, loadavg_t *ret); -int parse_loadavg_fixed_point(const char *s, loadavg_t *ret); diff --git a/shared/systemd/src/basic/path-util.c b/shared/systemd/src/basic/path-util.c deleted file mode 100644 index ea44c32b..00000000 --- a/shared/systemd/src/basic/path-util.c +++ /dev/null @@ -1,1188 +0,0 @@ -/* SPDX-License-Identifier: LGPL-2.1-or-later */ - -#include "nm-sd-adapt-shared.h" - -#include <errno.h> -#include <limits.h> -#include <stdio.h> -#include <stdlib.h> -#include <unistd.h> - -/* When we include libgen.h because we need dirname() we immediately - * undefine basename() since libgen.h defines it as a macro to the - * POSIX version which is really broken. We prefer GNU basename(). */ -#include <libgen.h> -#undef basename - -#include "alloc-util.h" -#include "extract-word.h" -#include "fd-util.h" -#include "fs-util.h" -#include "glob-util.h" -#include "log.h" -#include "macro.h" -#include "nulstr-util.h" -#include "parse-util.h" -#include "path-util.h" -#include "stat-util.h" -#include "string-util.h" -#include "strv.h" -#include "time-util.h" -#include "utf8.h" - -#if 0 /* NM_IGNORED */ -int path_split_and_make_absolute(const char *p, char ***ret) { - char **l; - int r; - - assert(p); - assert(ret); - - l = strv_split(p, ":"); - if (!l) - return -ENOMEM; - - r = path_strv_make_absolute_cwd(l); - if (r < 0) { - strv_free(l); - return r; - } - - *ret = l; - return r; -} - -char *path_make_absolute(const char *p, const char *prefix) { - assert(p); - - /* Makes every item in the list an absolute path by prepending - * the prefix, if specified and necessary */ - - if (path_is_absolute(p) || isempty(prefix)) - return strdup(p); - - return path_join(prefix, p); -} - -int safe_getcwd(char **ret) { - char *cwd; - - cwd = get_current_dir_name(); - if (!cwd) - return negative_errno(); - - /* Let's make sure the directory is really absolute, to protect us from the logic behind - * CVE-2018-1000001 */ - if (cwd[0] != '/') { - free(cwd); - return -ENOMEDIUM; - } - - *ret = cwd; - return 0; -} - -int path_make_absolute_cwd(const char *p, char **ret) { - char *c; - int r; - - assert(p); - assert(ret); - - /* Similar to path_make_absolute(), but prefixes with the - * current working directory. */ - - if (path_is_absolute(p)) - c = strdup(p); - else { - _cleanup_free_ char *cwd = NULL; - - r = safe_getcwd(&cwd); - if (r < 0) - return r; - - c = path_join(cwd, p); - } - if (!c) - return -ENOMEM; - - *ret = c; - return 0; -} - -int path_make_relative(const char *from_dir, const char *to_path, char **_r) { - char *f, *t, *r, *p; - unsigned n_parents = 0; - - assert(from_dir); - assert(to_path); - assert(_r); - - /* Strips the common part, and adds ".." elements as necessary. */ - - if (!path_is_absolute(from_dir) || !path_is_absolute(to_path)) - return -EINVAL; - - f = strdupa(from_dir); - t = strdupa(to_path); - - path_simplify(f, true); - path_simplify(t, true); - - /* Skip the common part. */ - for (;;) { - size_t a, b; - - f += *f == '/'; - t += *t == '/'; - - if (!*f) { - if (!*t) - /* from_dir equals to_path. */ - r = strdup("."); - else - /* from_dir is a parent directory of to_path. */ - r = strdup(t); - if (!r) - return -ENOMEM; - - *_r = r; - return 0; - } - - if (!*t) - break; - - a = strcspn(f, "/"); - b = strcspn(t, "/"); - - if (a != b || memcmp(f, t, a) != 0) - break; - - f += a; - t += b; - } - - /* If we're here, then "from_dir" has one or more elements that need to - * be replaced with "..". */ - - /* Count the number of necessary ".." elements. */ - for (; *f;) { - size_t w; - - w = strcspn(f, "/"); - - /* If this includes ".." we can't do a simple series of "..", refuse */ - if (w == 2 && f[0] == '.' && f[1] == '.') - return -EINVAL; - - /* Count number of elements */ - n_parents++; - - f += w; - f += *f == '/'; - } - - r = new(char, n_parents * 3 + strlen(t) + 1); - if (!r) - return -ENOMEM; - - for (p = r; n_parents > 0; n_parents--) - p = mempcpy(p, "../", 3); - - if (*t) - strcpy(p, t); - else - /* Remove trailing slash */ - *(--p) = 0; - - *_r = r; - return 0; -} - -char* path_startswith_strv(const char *p, char **set) { - char **s, *t; - - STRV_FOREACH(s, set) { - t = path_startswith(p, *s); - if (t) - return t; - } - - return NULL; -} - -int path_strv_make_absolute_cwd(char **l) { - char **s; - int r; - - /* Goes through every item in the string list and makes it - * absolute. This works in place and won't rollback any - * changes on failure. */ - - STRV_FOREACH(s, l) { - char *t; - - r = path_make_absolute_cwd(*s, &t); - if (r < 0) - return r; - - path_simplify(t, false); - free_and_replace(*s, t); - } - - return 0; -} - -char **path_strv_resolve(char **l, const char *root) { - char **s; - unsigned k = 0; - bool enomem = false; - int r; - - if (strv_isempty(l)) - return l; - - /* Goes through every item in the string list and canonicalize - * the path. This works in place and won't rollback any - * changes on failure. */ - - STRV_FOREACH(s, l) { - _cleanup_free_ char *orig = NULL; - char *t, *u; - - if (!path_is_absolute(*s)) { - free(*s); - continue; - } - - if (root) { - orig = *s; - t = path_join(root, orig); - if (!t) { - enomem = true; - continue; - } - } else - t = *s; - - r = chase_symlinks(t, root, 0, &u, NULL); - if (r == -ENOENT) { - if (root) { - u = TAKE_PTR(orig); - free(t); - } else - u = t; - } else if (r < 0) { - free(t); - - if (r == -ENOMEM) - enomem = true; - - continue; - } else if (root) { - char *x; - - free(t); - x = path_startswith(u, root); - if (x) { - /* restore the slash if it was lost */ - if (!startswith(x, "/")) - *(--x) = '/'; - - t = strdup(x); - free(u); - if (!t) { - enomem = true; - continue; - } - u = t; - } else { - /* canonicalized path goes outside of - * prefix, keep the original path instead */ - free_and_replace(u, orig); - } - } else - free(t); - - l[k++] = u; - } - - l[k] = NULL; - - if (enomem) - return NULL; - - return l; -} - -char **path_strv_resolve_uniq(char **l, const char *root) { - - if (strv_isempty(l)) - return l; - - if (!path_strv_resolve(l, root)) - return NULL; - - return strv_uniq(l); -} -#endif /* NM_IGNORED */ - -char *path_simplify(char *path, bool kill_dots) { - char *f, *t; - bool slash = false, ignore_slash = false, absolute; - - assert(path); - - /* Removes redundant inner and trailing slashes. Also removes unnecessary dots - * if kill_dots is true. Modifies the passed string in-place. - * - * ///foo//./bar/. becomes /foo/./bar/. (if kill_dots is false) - * ///foo//./bar/. becomes /foo/bar (if kill_dots is true) - * .//./foo//./bar/. becomes ././foo/./bar/. (if kill_dots is false) - * .//./foo//./bar/. becomes foo/bar (if kill_dots is true) - */ - - if (isempty(path)) - return path; - - absolute = path_is_absolute(path); - - f = path; - if (kill_dots && *f == '.' && IN_SET(f[1], 0, '/')) { - ignore_slash = true; - f++; - } - - for (t = path; *f; f++) { - - if (*f == '/') { - slash = true; - continue; - } - - if (slash) { - if (kill_dots && *f == '.' && IN_SET(f[1], 0, '/')) - continue; - - slash = false; - if (ignore_slash) - ignore_slash = false; - else - *(t++) = '/'; - } - - *(t++) = *f; - } - - /* Special rule, if we stripped everything, we either need a "/" (for the root directory) - * or "." for the current directory */ - if (t == path) { - if (absolute) - *(t++) = '/'; - else - *(t++) = '.'; - } - - *t = 0; - return path; -} - -#if 0 /* NM_IGNORED */ -int path_simplify_and_warn( - char *path, - unsigned flag, - const char *unit, - const char *filename, - unsigned line, - const char *lvalue) { - - bool fatal = flag & PATH_CHECK_FATAL; - - assert(!FLAGS_SET(flag, PATH_CHECK_ABSOLUTE | PATH_CHECK_RELATIVE)); - - if (!utf8_is_valid(path)) - return log_syntax_invalid_utf8(unit, LOG_ERR, filename, line, path); - - if (flag & (PATH_CHECK_ABSOLUTE | PATH_CHECK_RELATIVE)) { - bool absolute; - - absolute = path_is_absolute(path); - - if (!absolute && (flag & PATH_CHECK_ABSOLUTE)) - return log_syntax(unit, LOG_ERR, filename, line, SYNTHETIC_ERRNO(EINVAL), - "%s= path is not absolute%s: %s", - lvalue, fatal ? "" : ", ignoring", path); - - if (absolute && (flag & PATH_CHECK_RELATIVE)) - return log_syntax(unit, LOG_ERR, filename, line, SYNTHETIC_ERRNO(EINVAL), - "%s= path is absolute%s: %s", - lvalue, fatal ? "" : ", ignoring", path); - } - - path_simplify(path, true); - - if (!path_is_valid(path)) - return log_syntax(unit, LOG_ERR, filename, line, SYNTHETIC_ERRNO(EINVAL), - "%s= path has invalid length (%zu bytes)%s.", - lvalue, strlen(path), fatal ? "" : ", ignoring"); - - if (!path_is_normalized(path)) - return log_syntax(unit, LOG_ERR, filename, line, SYNTHETIC_ERRNO(EINVAL), - "%s= path is not normalized%s: %s", - lvalue, fatal ? "" : ", ignoring", path); - - return 0; -} -#endif /* NM_IGNORED */ - -char* path_startswith(const char *path, const char *prefix) { - assert(path); - assert(prefix); - - /* Returns a pointer to the start of the first component after the parts matched by - * the prefix, iff - * - both paths are absolute or both paths are relative, - * and - * - each component in prefix in turn matches a component in path at the same position. - * An empty string will be returned when the prefix and path are equivalent. - * - * Returns NULL otherwise. - */ - - if ((path[0] == '/') != (prefix[0] == '/')) - return NULL; - - for (;;) { - size_t a, b; - - path += strspn(path, "/"); - prefix += strspn(prefix, "/"); - - if (*prefix == 0) - return (char*) path; - - if (*path == 0) - return NULL; - - a = strcspn(path, "/"); - b = strcspn(prefix, "/"); - - if (a != b) - return NULL; - - if (memcmp(path, prefix, a) != 0) - return NULL; - - path += a; - prefix += b; - } -} - -int path_compare(const char *a, const char *b) { - int d; - - assert(a); - assert(b); - - /* A relative path and an absolute path must not compare as equal. - * Which one is sorted before the other does not really matter. - * Here a relative path is ordered before an absolute path. */ - d = (a[0] == '/') - (b[0] == '/'); - if (d != 0) - return d; - - for (;;) { - size_t j, k; - - a += strspn(a, "/"); - b += strspn(b, "/"); - - if (*a == 0 && *b == 0) - return 0; - - /* Order prefixes first: "/foo" before "/foo/bar" */ - if (*a == 0) - return -1; - if (*b == 0) - return 1; - - j = strcspn(a, "/"); - k = strcspn(b, "/"); - - /* Alphabetical sort: "/foo/aaa" before "/foo/b" */ - d = memcmp(a, b, MIN(j, k)); - if (d != 0) - return (d > 0) - (d < 0); /* sign of d */ - - /* Sort "/foo/a" before "/foo/aaa" */ - d = (j > k) - (j < k); /* sign of (j - k) */ - if (d != 0) - return d; - - a += j; - b += k; - } -} - -bool path_equal(const char *a, const char *b) { - return path_compare(a, b) == 0; -} - -#if 0 /* NM_IGNORED */ -bool path_equal_or_files_same(const char *a, const char *b, int flags) { - return path_equal(a, b) || files_same(a, b, flags) > 0; -} -#endif /* NM_IGNORED */ - -char* path_join_internal(const char *first, ...) { - char *joined, *q; - const char *p; - va_list ap; - bool slash; - size_t sz; - - /* Joins all listed strings until the sentinel and places a "/" between them unless the strings end/begin - * already with one so that it is unnecessary. Note that slashes which are already duplicate won't be - * removed. The string returned is hence always equal to or longer than the sum of the lengths of each - * individual string. - * - * Note: any listed empty string is simply skipped. This can be useful for concatenating strings of which some - * are optional. - * - * Examples: - * - * path_join("foo", "bar") → "foo/bar" - * path_join("foo/", "bar") → "foo/bar" - * path_join("", "foo", "", "bar", "") → "foo/bar" */ - - sz = strlen_ptr(first); - va_start(ap, first); - while ((p = va_arg(ap, char*)) != POINTER_MAX) - if (!isempty(p)) - sz += 1 + strlen(p); - va_end(ap); - - joined = new(char, sz + 1); - if (!joined) - return NULL; - - if (!isempty(first)) { - q = stpcpy(joined, first); - slash = endswith(first, "/"); - } else { - /* Skip empty items */ - joined[0] = 0; - q = joined; - slash = true; /* no need to generate a slash anymore */ - } - - va_start(ap, first); - while ((p = va_arg(ap, char*)) != POINTER_MAX) { - if (isempty(p)) - continue; - - if (!slash && p[0] != '/') - *(q++) = '/'; - - q = stpcpy(q, p); - slash = endswith(p, "/"); - } - va_end(ap); - - return joined; -} - -#if 0 /* NM_IGNORED */ -static int check_x_access(const char *path, int *ret_fd) { - if (ret_fd) { - _cleanup_close_ int fd = -1; - int r; - - /* We need to use O_PATH because there may be executables for which we have only exec - * permissions, but not read (usually suid executables). */ - fd = open(path, O_PATH|O_CLOEXEC); - if (fd < 0) - return -errno; - - r = access_fd(fd, X_OK); - if (r < 0) - return r; - - *ret_fd = TAKE_FD(fd); - } else { - /* Let's optimize things a bit by not opening the file if we don't need the fd. */ - if (access(path, X_OK) < 0) - return -errno; - } - - return 0; -} - -int find_executable_full(const char *name, bool use_path_envvar, char **ret_filename, int *ret_fd) { - int last_error, r; - const char *p = NULL; - - assert(name); - - if (is_path(name)) { - _cleanup_close_ int fd = -1; - - r = check_x_access(name, ret_fd ? &fd : NULL); - if (r < 0) - return r; - - if (ret_filename) { - r = path_make_absolute_cwd(name, ret_filename); - if (r < 0) - return r; - } - - if (ret_fd) - *ret_fd = TAKE_FD(fd); - - return 0; - } - - if (use_path_envvar) - /* Plain getenv, not secure_getenv, because we want to actually allow the user to pick the - * binary. */ - p = getenv("PATH"); - if (!p) - p = DEFAULT_PATH; - - last_error = -ENOENT; - - /* Resolve a single-component name to a full path */ - for (;;) { - _cleanup_free_ char *j = NULL, *element = NULL; - _cleanup_close_ int fd = -1; - - r = extract_first_word(&p, &element, ":", EXTRACT_RELAX|EXTRACT_DONT_COALESCE_SEPARATORS); - if (r < 0) - return r; - if (r == 0) - break; - - if (!path_is_absolute(element)) - continue; - - j = path_join(element, name); - if (!j) - return -ENOMEM; - - r = check_x_access(j, ret_fd ? &fd : NULL); - if (r >= 0) { - _cleanup_free_ char *with_dash; - - with_dash = strjoin(j, "/"); - if (!with_dash) - return -ENOMEM; - - /* If this passes, it must be a directory, and so should be skipped. */ - if (access(with_dash, X_OK) >= 0) - continue; - - /* We can't just `continue` inverting this case, since we need to update last_error. */ - if (errno == ENOTDIR) { - /* Found it! */ - if (ret_filename) - *ret_filename = path_simplify(TAKE_PTR(j), false); - if (ret_fd) - *ret_fd = TAKE_FD(fd); - - return 0; - } - } - - /* PATH entries which we don't have access to are ignored, as per tradition. */ - if (errno != EACCES) - last_error = -errno; - } - - return last_error; -} - -bool paths_check_timestamp(const char* const* paths, usec_t *timestamp, bool update) { - bool changed = false; - const char* const* i; - - assert(timestamp); - - if (!paths) - return false; - - STRV_FOREACH(i, paths) { - struct stat stats; - usec_t u; - - if (stat(*i, &stats) < 0) - continue; - - u = timespec_load(&stats.st_mtim); - - /* first check */ - if (*timestamp >= u) - continue; - - log_debug("timestamp of '%s' changed", *i); - - /* update timestamp */ - if (update) { - *timestamp = u; - changed = true; - } else - return true; - } - - return changed; -} - -static int executable_is_good(const char *executable) { - _cleanup_free_ char *p = NULL, *d = NULL; - int r; - - r = find_executable(executable, &p); - if (r == -ENOENT) - return 0; - if (r < 0) - return r; - - /* An fsck that is linked to /bin/true is a non-existent fsck */ - - r = readlink_malloc(p, &d); - if (r == -EINVAL) /* not a symlink */ - return 1; - if (r < 0) - return r; - - return !PATH_IN_SET(d, "true" - "/bin/true", - "/usr/bin/true", - "/dev/null"); -} - -int fsck_exists(const char *fstype) { - const char *checker; - - assert(fstype); - - if (streq(fstype, "auto")) - return -EINVAL; - - checker = strjoina("fsck.", fstype); - return executable_is_good(checker); -} - -int parse_path_argument_and_warn(const char *path, bool suppress_root, char **arg) { - char *p; - int r; - - /* - * This function is intended to be used in command line - * parsers, to handle paths that are passed in. It makes the - * path absolute, and reduces it to NULL if omitted or - * root (the latter optionally). - * - * NOTE THAT THIS WILL FREE THE PREVIOUS ARGUMENT POINTER ON - * SUCCESS! Hence, do not pass in uninitialized pointers. - */ - - if (isempty(path)) { - *arg = mfree(*arg); - return 0; - } - - r = path_make_absolute_cwd(path, &p); - if (r < 0) - return log_error_errno(r, "Failed to parse path \"%s\" and make it absolute: %m", path); - - path_simplify(p, false); - if (suppress_root && empty_or_root(p)) - p = mfree(p); - - free_and_replace(*arg, p); - - return 0; -} - -char* dirname_malloc(const char *path) { - char *d, *dir, *dir2; - - assert(path); - - d = strdup(path); - if (!d) - return NULL; - - dir = dirname(d); - assert(dir); - - if (dir == d) - return d; - - dir2 = strdup(dir); - free(d); - - return dir2; -} - -const char *last_path_component(const char *path) { - - /* Finds the last component of the path, preserving the optional trailing slash that signifies a directory. - * - * a/b/c → c - * a/b/c/ → c/ - * x → x - * x/ → x/ - * /y → y - * /y/ → y/ - * / → / - * // → / - * /foo/a → a - * /foo/a/ → a/ - * - * Also, the empty string is mapped to itself. - * - * This is different than basename(), which returns "" when a trailing slash is present. - */ - - unsigned l, k; - - if (!path) - return NULL; - - l = k = strlen(path); - if (l == 0) /* special case — an empty string */ - return path; - - while (k > 0 && path[k-1] == '/') - k--; - - if (k == 0) /* the root directory */ - return path + l - 1; - - while (k > 0 && path[k-1] != '/') - k--; - - return path + k; -} - -int path_extract_filename(const char *p, char **ret) { - _cleanup_free_ char *a = NULL; - const char *c, *e = NULL, *q; - - /* Extracts the filename part (i.e. right-most component) from a path, i.e. string that passes - * filename_is_valid(). A wrapper around last_path_component(), but eats up trailing slashes. */ - - if (!p) - return -EINVAL; - - c = last_path_component(p); - - for (q = c; *q != 0; q++) - if (*q != '/') - e = q + 1; - - if (!e) /* no valid character? */ - return -EINVAL; - - a = strndup(c, e - c); - if (!a) - return -ENOMEM; - - if (!filename_is_valid(a)) - return -EINVAL; - - *ret = TAKE_PTR(a); - - return 0; -} -#endif /* NM_IGNORED */ - -bool filename_is_valid(const char *p) { - const char *e; - - if (isempty(p)) - return false; - - if (dot_or_dot_dot(p)) - return false; - - e = strchrnul(p, '/'); - if (*e != 0) - return false; - - if (e - p > FILENAME_MAX) /* FILENAME_MAX is counted *without* the trailing NUL byte */ - return false; - - return true; -} - -bool path_is_valid(const char *p) { - - if (isempty(p)) - return false; - - if (strlen(p) >= PATH_MAX) /* PATH_MAX is counted *with* the trailing NUL byte */ - return false; - - return true; -} - -bool path_is_normalized(const char *p) { - - if (!path_is_valid(p)) - return false; - - if (dot_or_dot_dot(p)) - return false; - - if (startswith(p, "../") || endswith(p, "/..") || strstr(p, "/../")) - return false; - - if (startswith(p, "./") || endswith(p, "/.") || strstr(p, "/./")) - return false; - - if (strstr(p, "//")) - return false; - - return true; -} - -#if 0 /* NM_IGNORED */ -char *file_in_same_dir(const char *path, const char *filename) { - char *e, *ret; - size_t k; - - assert(path); - assert(filename); - - /* This removes the last component of path and appends - * filename, unless the latter is absolute anyway or the - * former isn't */ - - if (path_is_absolute(filename)) - return strdup(filename); - - e = strrchr(path, '/'); - if (!e) - return strdup(filename); - - k = strlen(filename); - ret = new(char, (e + 1 - path) + k + 1); - if (!ret) - return NULL; - - memcpy(mempcpy(ret, path, e + 1 - path), filename, k + 1); - return ret; -} - -bool hidden_or_backup_file(const char *filename) { - const char *p; - - assert(filename); - - if (filename[0] == '.' || - streq(filename, "lost+found") || - streq(filename, "aquota.user") || - streq(filename, "aquota.group") || - endswith(filename, "~")) - return true; - - p = strrchr(filename, '.'); - if (!p) - return false; - - /* Please, let's not add more entries to the list below. If external projects think it's a good idea to come up - * with always new suffixes and that everybody else should just adjust to that, then it really should be on - * them. Hence, in future, let's not add any more entries. Instead, let's ask those packages to instead adopt - * one of the generic suffixes/prefixes for hidden files or backups, possibly augmented with an additional - * string. Specifically: there's now: - * - * The generic suffixes "~" and ".bak" for backup files - * The generic prefix "." for hidden files - * - * Thus, if a new package manager "foopkg" wants its own set of ".foopkg-new", ".foopkg-old", ".foopkg-dist" - * or so registered, let's refuse that and ask them to use ".foopkg.new", ".foopkg.old" or ".foopkg~" instead. - */ - - return STR_IN_SET(p + 1, - "rpmnew", - "rpmsave", - "rpmorig", - "dpkg-old", - "dpkg-new", - "dpkg-tmp", - "dpkg-dist", - "dpkg-bak", - "dpkg-backup", - "dpkg-remove", - "ucf-new", - "ucf-old", - "ucf-dist", - "swp", - "bak", - "old", - "new"); -} - -bool is_device_path(const char *path) { - - /* Returns true on paths that likely refer to a device, either by path in sysfs or to something in /dev */ - - return PATH_STARTSWITH_SET(path, "/dev/", "/sys/"); -} - -bool valid_device_node_path(const char *path) { - - /* Some superficial checks whether the specified path is a valid device node path, all without looking at the - * actual device node. */ - - if (!PATH_STARTSWITH_SET(path, "/dev/", "/run/systemd/inaccessible/")) - return false; - - if (endswith(path, "/")) /* can't be a device node if it ends in a slash */ - return false; - - return path_is_normalized(path); -} - -bool valid_device_allow_pattern(const char *path) { - assert(path); - - /* Like valid_device_node_path(), but also allows full-subsystem expressions, like DeviceAllow= and DeviceDeny= - * accept it */ - - if (STARTSWITH_SET(path, "block-", "char-")) - return true; - - return valid_device_node_path(path); -} - -int systemd_installation_has_version(const char *root, unsigned minimal_version) { - const char *pattern; - int r; - - /* Try to guess if systemd installation is later than the specified version. This - * is hacky and likely to yield false negatives, particularly if the installation - * is non-standard. False positives should be relatively rare. - */ - - NULSTR_FOREACH(pattern, - /* /lib works for systems without usr-merge, and for systems with a sane - * usr-merge, where /lib is a symlink to /usr/lib. /usr/lib is necessary - * for Gentoo which does a merge without making /lib a symlink. - */ - "lib/systemd/libsystemd-shared-*.so\0" - "lib64/systemd/libsystemd-shared-*.so\0" - "usr/lib/systemd/libsystemd-shared-*.so\0" - "usr/lib64/systemd/libsystemd-shared-*.so\0") { - - _cleanup_strv_free_ char **names = NULL; - _cleanup_free_ char *path = NULL; - char *c, **name; - - path = path_join(root, pattern); - if (!path) - return -ENOMEM; - - r = glob_extend(&names, path, 0); - if (r == -ENOENT) - continue; - if (r < 0) - return r; - - assert_se(c = endswith(path, "*.so")); - *c = '\0'; /* truncate the glob part */ - - STRV_FOREACH(name, names) { - /* This is most likely to run only once, hence let's not optimize anything. */ - char *t, *t2; - unsigned version; - - t = startswith(*name, path); - if (!t) - continue; - - t2 = endswith(t, ".so"); - if (!t2) - continue; - - t2[0] = '\0'; /* truncate the suffix */ - - r = safe_atou(t, &version); - if (r < 0) { - log_debug_errno(r, "Found libsystemd shared at \"%s.so\", but failed to parse version: %m", *name); - continue; - } - - log_debug("Found libsystemd shared at \"%s.so\", version %u (%s).", - *name, version, - version >= minimal_version ? "OK" : "too old"); - if (version >= minimal_version) - return true; - } - } - - return false; -} -#endif /* NM_IGNORED */ - -bool dot_or_dot_dot(const char *path) { - if (!path) - return false; - if (path[0] != '.') - return false; - if (path[1] == 0) - return true; - if (path[1] != '.') - return false; - - return path[2] == 0; -} - -#if 0 /* NM_IGNORED */ -bool empty_or_root(const char *root) { - - /* For operations relative to some root directory, returns true if the specified root directory is redundant, - * i.e. either / or NULL or the empty string or any equivalent. */ - - if (!root) - return true; - - return root[strspn(root, "/")] == 0; -} - -bool path_strv_contains(char **l, const char *path) { - char **i; - - STRV_FOREACH(i, l) - if (path_equal(*i, path)) - return true; - - return false; -} - -bool prefixed_path_strv_contains(char **l, const char *path) { - char **i, *j; - - STRV_FOREACH(i, l) { - j = *i; - if (*j == '-') - j++; - if (*j == '+') - j++; - if (path_equal(j, path)) - return true; - } - - return false; -} - -bool credential_name_valid(const char *s) { - /* We want that credential names are both valid in filenames (since that's our primary way to pass - * them around) and as fdnames (which is how we might want to pass them around eventually) */ - return filename_is_valid(s) && fdname_is_valid(s); -} -#endif /* NM_IGNORED */ diff --git a/shared/systemd/src/basic/path-util.h b/shared/systemd/src/basic/path-util.h deleted file mode 100644 index 988f058b..00000000 --- a/shared/systemd/src/basic/path-util.h +++ /dev/null @@ -1,190 +0,0 @@ -/* SPDX-License-Identifier: LGPL-2.1-or-later */ -#pragma once - -#include <alloca.h> -#include <stdbool.h> -#include <stddef.h> - -#include "macro.h" -#include "string-util.h" -#include "strv.h" -#include "time-util.h" - -#if 0 /* NM_IGNORED */ -#define PATH_SPLIT_SBIN_BIN(x) x "sbin:" x "bin" -#define PATH_SPLIT_SBIN_BIN_NULSTR(x) x "sbin\0" x "bin\0" - -#define PATH_NORMAL_SBIN_BIN(x) x "bin" -#define PATH_NORMAL_SBIN_BIN_NULSTR(x) x "bin\0" - -#if HAVE_SPLIT_BIN -# define PATH_SBIN_BIN(x) PATH_SPLIT_SBIN_BIN(x) -# define PATH_SBIN_BIN_NULSTR(x) PATH_SPLIT_SBIN_BIN_NULSTR(x) -#else -# define PATH_SBIN_BIN(x) PATH_NORMAL_SBIN_BIN(x) -# define PATH_SBIN_BIN_NULSTR(x) PATH_NORMAL_SBIN_BIN_NULSTR(x) -#endif - -#define DEFAULT_PATH_NORMAL PATH_SBIN_BIN("/usr/local/") ":" PATH_SBIN_BIN("/usr/") -#define DEFAULT_PATH_NORMAL_NULSTR PATH_SBIN_BIN_NULSTR("/usr/local/") PATH_SBIN_BIN_NULSTR("/usr/") -#define DEFAULT_PATH_SPLIT_USR DEFAULT_PATH_NORMAL ":" PATH_SBIN_BIN("/") -#define DEFAULT_PATH_SPLIT_USR_NULSTR DEFAULT_PATH_NORMAL_NULSTR PATH_SBIN_BIN_NULSTR("/") -#define DEFAULT_PATH_COMPAT PATH_SPLIT_SBIN_BIN("/usr/local/") ":" PATH_SPLIT_SBIN_BIN("/usr/") ":" PATH_SPLIT_SBIN_BIN("/") - -#if HAVE_SPLIT_USR -# define DEFAULT_PATH DEFAULT_PATH_SPLIT_USR -# define DEFAULT_PATH_NULSTR DEFAULT_PATH_SPLIT_USR_NULSTR -#else -# define DEFAULT_PATH DEFAULT_PATH_NORMAL -# define DEFAULT_PATH_NULSTR DEFAULT_PATH_NORMAL_NULSTR -#endif -#endif /* NM_IGNORED */ - -#ifndef DEFAULT_USER_PATH -# define DEFAULT_USER_PATH DEFAULT_PATH -#endif - -static inline bool is_path(const char *p) { - assert(p); - return strchr(p, '/'); -} - -static inline bool path_is_absolute(const char *p) { - assert(p); - return p[0] == '/'; -} - -int path_split_and_make_absolute(const char *p, char ***ret); -char* path_make_absolute(const char *p, const char *prefix); -int safe_getcwd(char **ret); -int path_make_absolute_cwd(const char *p, char **ret); -int path_make_relative(const char *from_dir, const char *to_path, char **_r); -char* path_startswith(const char *path, const char *prefix) _pure_; -int path_compare(const char *a, const char *b) _pure_; -bool path_equal(const char *a, const char *b) _pure_; -bool path_equal_or_files_same(const char *a, const char *b, int flags); -char* path_join_internal(const char *first, ...); -#define path_join(x, ...) path_join_internal(x, __VA_ARGS__, POINTER_MAX) - -char* path_simplify(char *path, bool kill_dots); - -enum { - PATH_CHECK_FATAL = 1 << 0, /* If not set, then error message is appended with 'ignoring'. */ - PATH_CHECK_ABSOLUTE = 1 << 1, - PATH_CHECK_RELATIVE = 1 << 2, -}; - -int path_simplify_and_warn(char *path, unsigned flag, const char *unit, const char *filename, unsigned line, const char *lvalue); - -static inline bool path_equal_ptr(const char *a, const char *b) { - return !!a == !!b && (!a || path_equal(a, b)); -} - -/* Note: the search terminates on the first NULL item. */ -#define PATH_IN_SET(p, ...) path_strv_contains(STRV_MAKE(__VA_ARGS__), p) - -char* path_startswith_strv(const char *p, char **set); -#define PATH_STARTSWITH_SET(p, ...) path_startswith_strv(p, STRV_MAKE(__VA_ARGS__)) - -int path_strv_make_absolute_cwd(char **l); -char** path_strv_resolve(char **l, const char *root); -char** path_strv_resolve_uniq(char **l, const char *root); - -int find_executable_full(const char *name, bool use_path_envvar, char **ret_filename, int *ret_fd); -static inline int find_executable(const char *name, char **ret_filename) { - return find_executable_full(name, true, ret_filename, NULL); -} - -bool paths_check_timestamp(const char* const* paths, usec_t *paths_ts_usec, bool update); - -int fsck_exists(const char *fstype); - -/* Iterates through the path prefixes of the specified path, going up - * the tree, to root. Also returns "" (and not "/"!) for the root - * directory. Excludes the specified directory itself */ -#define PATH_FOREACH_PREFIX(prefix, path) \ - for (char *_slash = ({ \ - path_simplify(strcpy(prefix, path), false); \ - streq(prefix, "/") ? NULL : strrchr(prefix, '/'); \ - }); \ - _slash && ((*_slash = 0), true); \ - _slash = strrchr((prefix), '/')) - -/* Same as PATH_FOREACH_PREFIX but also includes the specified path itself */ -#define PATH_FOREACH_PREFIX_MORE(prefix, path) \ - for (char *_slash = ({ \ - path_simplify(strcpy(prefix, path), false); \ - if (streq(prefix, "/")) \ - prefix[0] = 0; \ - strrchr(prefix, 0); \ - }); \ - _slash && ((*_slash = 0), true); \ - _slash = strrchr((prefix), '/')) - -/* Similar to path_join(), but only works for two components, and only the first one may be NULL and returns - * an alloca() buffer, or possibly a const pointer into the path parameter. */ -#define prefix_roota(root, path) \ - ({ \ - const char* _path = (path), *_root = (root), *_ret; \ - char *_p, *_n; \ - size_t _l; \ - while (_path[0] == '/' && _path[1] == '/') \ - _path ++; \ - if (isempty(_root)) \ - _ret = _path; \ - else { \ - _l = strlen(_root) + 1 + strlen(_path) + 1; \ - _n = newa(char, _l); \ - _p = stpcpy(_n, _root); \ - while (_p > _n && _p[-1] == '/') \ - _p--; \ - if (_path[0] != '/') \ - *(_p++) = '/'; \ - strcpy(_p, _path); \ - _ret = _n; \ - } \ - _ret; \ - }) - -int parse_path_argument_and_warn(const char *path, bool suppress_root, char **arg); - -char* dirname_malloc(const char *path); -const char *last_path_component(const char *path); -int path_extract_filename(const char *p, char **ret); - -bool filename_is_valid(const char *p) _pure_; -bool path_is_valid(const char *p) _pure_; -bool path_is_normalized(const char *p) _pure_; - -char *file_in_same_dir(const char *path, const char *filename); - -bool hidden_or_backup_file(const char *filename) _pure_; - -bool is_device_path(const char *path); - -bool valid_device_node_path(const char *path); -bool valid_device_allow_pattern(const char *path); - -int systemd_installation_has_version(const char *root, unsigned minimal_version); - -bool dot_or_dot_dot(const char *path); - -static inline const char *skip_dev_prefix(const char *p) { - const char *e; - - /* Drop any /dev prefix if there is any */ - - e = path_startswith(p, "/dev/"); - - return e ?: p; -} - -bool empty_or_root(const char *root); -static inline const char *empty_to_root(const char *path) { - return isempty(path) ? "/" : path; -} - -bool path_strv_contains(char **l, const char *path); -bool prefixed_path_strv_contains(char **l, const char *path); - -bool credential_name_valid(const char *s); diff --git a/shared/systemd/src/basic/prioq.c b/shared/systemd/src/basic/prioq.c deleted file mode 100644 index 19a9bc57..00000000 --- a/shared/systemd/src/basic/prioq.c +++ /dev/null @@ -1,302 +0,0 @@ -/* SPDX-License-Identifier: LGPL-2.1-or-later */ - -/* - * Priority Queue - * The prioq object implements a priority queue. That is, it orders objects by - * their priority and allows O(1) access to the object with the highest - * priority. Insertion and removal are Θ(log n). Optionally, the caller can - * provide a pointer to an index which will be kept up-to-date by the prioq. - * - * The underlying algorithm used in this implementation is a Heap. - */ - -#include "nm-sd-adapt-shared.h" - -#include <errno.h> -#include <stdlib.h> - -#include "alloc-util.h" -#include "hashmap.h" -#include "prioq.h" - -struct prioq_item { - void *data; - unsigned *idx; -}; - -struct Prioq { - compare_func_t compare_func; - unsigned n_items, n_allocated; - - struct prioq_item *items; -}; - -Prioq *prioq_new(compare_func_t compare_func) { - Prioq *q; - - q = new(Prioq, 1); - if (!q) - return q; - - *q = (Prioq) { - .compare_func = compare_func, - }; - - return q; -} - -Prioq* prioq_free(Prioq *q) { - if (!q) - return NULL; - - free(q->items); - return mfree(q); -} - -int prioq_ensure_allocated(Prioq **q, compare_func_t compare_func) { - assert(q); - - if (*q) - return 0; - - *q = prioq_new(compare_func); - if (!*q) - return -ENOMEM; - - return 0; -} - -static void swap(Prioq *q, unsigned j, unsigned k) { - assert(q); - assert(j < q->n_items); - assert(k < q->n_items); - - assert(!q->items[j].idx || *(q->items[j].idx) == j); - assert(!q->items[k].idx || *(q->items[k].idx) == k); - - SWAP_TWO(q->items[j].data, q->items[k].data); - SWAP_TWO(q->items[j].idx, q->items[k].idx); - - if (q->items[j].idx) - *q->items[j].idx = j; - - if (q->items[k].idx) - *q->items[k].idx = k; -} - -static unsigned shuffle_up(Prioq *q, unsigned idx) { - assert(q); - assert(idx < q->n_items); - - while (idx > 0) { - unsigned k; - - k = (idx-1)/2; - - if (q->compare_func(q->items[k].data, q->items[idx].data) <= 0) - break; - - swap(q, idx, k); - idx = k; - } - - return idx; -} - -static unsigned shuffle_down(Prioq *q, unsigned idx) { - assert(q); - - for (;;) { - unsigned j, k, s; - - k = (idx+1)*2; /* right child */ - j = k-1; /* left child */ - - if (j >= q->n_items) - break; - - if (q->compare_func(q->items[j].data, q->items[idx].data) < 0) - - /* So our left child is smaller than we are, let's - * remember this fact */ - s = j; - else - s = idx; - - if (k < q->n_items && - q->compare_func(q->items[k].data, q->items[s].data) < 0) - - /* So our right child is smaller than we are, let's - * remember this fact */ - s = k; - - /* s now points to the smallest of the three items */ - - if (s == idx) - /* No swap necessary, we're done */ - break; - - swap(q, idx, s); - idx = s; - } - - return idx; -} - -int prioq_put(Prioq *q, void *data, unsigned *idx) { - struct prioq_item *i; - unsigned k; - - assert(q); - - if (q->n_items >= q->n_allocated) { - unsigned n; - struct prioq_item *j; - - n = MAX((q->n_items+1) * 2, 16u); - j = reallocarray(q->items, n, sizeof(struct prioq_item)); - if (!j) - return -ENOMEM; - - q->items = j; - q->n_allocated = n; - } - - k = q->n_items++; - i = q->items + k; - i->data = data; - i->idx = idx; - - if (idx) - *idx = k; - - shuffle_up(q, k); - - return 0; -} - -static void remove_item(Prioq *q, struct prioq_item *i) { - struct prioq_item *l; - - assert(q); - assert(i); - - l = q->items + q->n_items - 1; - - if (i == l) - /* Last entry, let's just remove it */ - q->n_items--; - else { - unsigned k; - - /* Not last entry, let's replace the last entry with - * this one, and reshuffle */ - - k = i - q->items; - - i->data = l->data; - i->idx = l->idx; - if (i->idx) - *i->idx = k; - q->n_items--; - - k = shuffle_down(q, k); - shuffle_up(q, k); - } -} - -_pure_ static struct prioq_item* find_item(Prioq *q, void *data, unsigned *idx) { - struct prioq_item *i; - - assert(q); - - if (q->n_items <= 0) - return NULL; - - if (idx) { - if (*idx == PRIOQ_IDX_NULL || - *idx >= q->n_items) - return NULL; - - i = q->items + *idx; - if (i->data != data) - return NULL; - - return i; - } else { - for (i = q->items; i < q->items + q->n_items; i++) - if (i->data == data) - return i; - return NULL; - } -} - -int prioq_remove(Prioq *q, void *data, unsigned *idx) { - struct prioq_item *i; - - if (!q) - return 0; - - i = find_item(q, data, idx); - if (!i) - return 0; - - remove_item(q, i); - return 1; -} - -int prioq_reshuffle(Prioq *q, void *data, unsigned *idx) { - struct prioq_item *i; - unsigned k; - - assert(q); - - i = find_item(q, data, idx); - if (!i) - return 0; - - k = i - q->items; - k = shuffle_down(q, k); - shuffle_up(q, k); - return 1; -} - -void *prioq_peek_by_index(Prioq *q, unsigned idx) { - if (!q) - return NULL; - - if (idx >= q->n_items) - return NULL; - - return q->items[idx].data; -} - -void *prioq_pop(Prioq *q) { - void *data; - - if (!q) - return NULL; - - if (q->n_items <= 0) - return NULL; - - data = q->items[0].data; - remove_item(q, q->items); - return data; -} - -unsigned prioq_size(Prioq *q) { - - if (!q) - return 0; - - return q->n_items; -} - -bool prioq_isempty(Prioq *q) { - - if (!q) - return true; - - return q->n_items <= 0; -} diff --git a/shared/systemd/src/basic/prioq.h b/shared/systemd/src/basic/prioq.h deleted file mode 100644 index 951576c0..00000000 --- a/shared/systemd/src/basic/prioq.h +++ /dev/null @@ -1,32 +0,0 @@ -/* SPDX-License-Identifier: LGPL-2.1-or-later */ -#pragma once - -#include <stdbool.h> - -#include "hashmap.h" -#include "macro.h" - -typedef struct Prioq Prioq; - -#define PRIOQ_IDX_NULL ((unsigned) -1) - -Prioq *prioq_new(compare_func_t compare); -Prioq *prioq_free(Prioq *q); -DEFINE_TRIVIAL_CLEANUP_FUNC(Prioq*, prioq_free); -int prioq_ensure_allocated(Prioq **q, compare_func_t compare_func); - -int prioq_put(Prioq *q, void *data, unsigned *idx); -int prioq_remove(Prioq *q, void *data, unsigned *idx); -int prioq_reshuffle(Prioq *q, void *data, unsigned *idx); - -void *prioq_peek_by_index(Prioq *q, unsigned idx) _pure_; -static inline void *prioq_peek(Prioq *q) { - return prioq_peek_by_index(q, 0); -} -void *prioq_pop(Prioq *q); - -#define PRIOQ_FOREACH_ITEM(q, p) \ - for (unsigned _i = 0; (p = prioq_peek_by_index(q, _i)); _i++) - -unsigned prioq_size(Prioq *q) _pure_; -bool prioq_isempty(Prioq *q) _pure_; diff --git a/shared/systemd/src/basic/process-util.c b/shared/systemd/src/basic/process-util.c deleted file mode 100644 index 0e25b020..00000000 --- a/shared/systemd/src/basic/process-util.c +++ /dev/null @@ -1,1662 +0,0 @@ -/* SPDX-License-Identifier: LGPL-2.1-or-later */ - -#include "nm-sd-adapt-shared.h" - -#include <ctype.h> -#include <errno.h> -#include <limits.h> -#include <linux/oom.h> -#include <stdbool.h> -#include <stdio.h> -#include <stdlib.h> -#include <sys/mman.h> -#include <sys/mount.h> -#include <sys/personality.h> -#include <sys/prctl.h> -#include <sys/types.h> -#include <sys/wait.h> -#include <syslog.h> -#include <unistd.h> -#if 0 /* NM_IGNORED */ -#if HAVE_VALGRIND_VALGRIND_H -#include <valgrind/valgrind.h> -#endif -#endif /* NM_IGNORED */ - -#include "alloc-util.h" -#include "architecture.h" -#include "env-util.h" -#include "errno-util.h" -#include "escape.h" -#include "fd-util.h" -#include "fileio.h" -#include "fs-util.h" -#include "ioprio.h" -#include "locale-util.h" -#include "log.h" -#include "macro.h" -#include "memory-util.h" -#include "missing_sched.h" -#include "missing_syscall.h" -#include "namespace-util.h" -#include "path-util.h" -#include "process-util.h" -#include "raw-clone.h" -#include "rlimit-util.h" -#include "signal-util.h" -#include "stat-util.h" -#include "stdio-util.h" -#include "string-table.h" -#include "string-util.h" -#include "terminal-util.h" -#include "user-util.h" -#include "utf8.h" - -#if 0 /* NM_IGNORED */ - -/* The kernel limits userspace processes to TASK_COMM_LEN (16 bytes), but allows higher values for its own - * workers, e.g. "kworker/u9:3-kcryptd/253:0". Let's pick a fixed smallish limit that will work for the kernel. - */ -#define COMM_MAX_LEN 128 - -static int get_process_state(pid_t pid) { - _cleanup_free_ char *line = NULL; - const char *p; - char state; - int r; - - assert(pid >= 0); - - /* Shortcut: if we are enquired about our own state, we are obviously running */ - if (pid == 0 || pid == getpid_cached()) - return (unsigned char) 'R'; - - p = procfs_file_alloca(pid, "stat"); - - r = read_one_line_file(p, &line); - if (r == -ENOENT) - return -ESRCH; - if (r < 0) - return r; - - p = strrchr(line, ')'); - if (!p) - return -EIO; - - p++; - - if (sscanf(p, " %c", &state) != 1) - return -EIO; - - return (unsigned char) state; -} - -int get_process_comm(pid_t pid, char **ret) { - _cleanup_free_ char *escaped = NULL, *comm = NULL; - int r; - - assert(ret); - assert(pid >= 0); - - if (pid == 0 || pid == getpid_cached()) { - comm = new0(char, TASK_COMM_LEN + 1); /* Must fit in 16 byte according to prctl(2) */ - if (!comm) - return -ENOMEM; - - if (prctl(PR_GET_NAME, comm) < 0) - return -errno; - } else { - const char *p; - - p = procfs_file_alloca(pid, "comm"); - - /* Note that process names of kernel threads can be much longer than TASK_COMM_LEN */ - r = read_one_line_file(p, &comm); - if (r == -ENOENT) - return -ESRCH; - if (r < 0) - return r; - } - - escaped = new(char, COMM_MAX_LEN); - if (!escaped) - return -ENOMEM; - - /* Escape unprintable characters, just in case, but don't grow the string beyond the underlying size */ - cellescape(escaped, COMM_MAX_LEN, comm); - - *ret = TAKE_PTR(escaped); - return 0; -} - -int get_process_cmdline(pid_t pid, size_t max_columns, ProcessCmdlineFlags flags, char **line) { - _cleanup_fclose_ FILE *f = NULL; - _cleanup_free_ char *t = NULL, *ans = NULL; - const char *p; - int r; - size_t k; - - /* This is supposed to be a safety guard against runaway command lines. */ - size_t max_length = sc_arg_max(); - - assert(line); - assert(pid >= 0); - - /* Retrieves a process' command line. Replaces non-utf8 bytes by replacement character (�). If - * max_columns is != -1 will return a string of the specified console width at most, abbreviated with - * an ellipsis. If PROCESS_CMDLINE_COMM_FALLBACK is specified in flags and the process has no command - * line set (the case for kernel threads), or has a command line that resolves to the empty string - * will return the "comm" name of the process instead. This will use at most _SC_ARG_MAX bytes of - * input data. - * - * Returns -ESRCH if the process doesn't exist, and -ENOENT if the process has no command line (and - * comm_fallback is false). Returns 0 and sets *line otherwise. */ - - p = procfs_file_alloca(pid, "cmdline"); - r = fopen_unlocked(p, "re", &f); - if (r == -ENOENT) - return -ESRCH; - if (r < 0) - return r; - - /* We assume that each four-byte character uses one or two columns. If we ever check for combining - * characters, this assumption will need to be adjusted. */ - if ((size_t) 4 * max_columns + 1 < max_columns) - max_length = MIN(max_length, (size_t) 4 * max_columns + 1); - - t = new(char, max_length); - if (!t) - return -ENOMEM; - - k = fread(t, 1, max_length, f); - if (k > 0) { - /* Arguments are separated by NULs. Let's replace those with spaces. */ - for (size_t i = 0; i < k - 1; i++) - if (t[i] == '\0') - t[i] = ' '; - - t[k] = '\0'; /* Normally, t[k] is already NUL, so this is just a guard in case of short read */ - } else { - /* We only treat getting nothing as an error. We *could* also get an error after reading some - * data, but we ignore that case, as such an error is rather unlikely and we prefer to get - * some data rather than none. */ - if (ferror(f)) - return -errno; - - if (!(flags & PROCESS_CMDLINE_COMM_FALLBACK)) - return -ENOENT; - - /* Kernel threads have no argv[] */ - _cleanup_free_ char *t2 = NULL; - - r = get_process_comm(pid, &t2); - if (r < 0) - return r; - - mfree(t); - t = strjoin("[", t2, "]"); - if (!t) - return -ENOMEM; - } - - delete_trailing_chars(t, WHITESPACE); - - bool eight_bit = (flags & PROCESS_CMDLINE_USE_LOCALE) && !is_locale_utf8(); - - ans = escape_non_printable_full(t, max_columns, eight_bit); - if (!ans) - return -ENOMEM; - - (void) str_realloc(&ans); - *line = TAKE_PTR(ans); - return 0; -} - -static int update_argv(const char name[], size_t l) { - static int can_do = -1; - - if (can_do == 0) - return 0; - can_do = false; /* We'll set it to true only if the whole process works */ - - /* Let's not bother with this if we don't have euid == 0. Strictly speaking we should check for the - * CAP_SYS_RESOURCE capability which is independent of the euid. In our own code the capability generally is - * present only for euid == 0, hence let's use this as quick bypass check, to avoid calling mmap() if - * PR_SET_MM_ARG_{START,END} fails with EPERM later on anyway. After all geteuid() is dead cheap to call, but - * mmap() is not. */ - if (geteuid() != 0) - return log_debug_errno(SYNTHETIC_ERRNO(EPERM), - "Skipping PR_SET_MM, as we don't have privileges."); - - static size_t mm_size = 0; - static char *mm = NULL; - int r; - - if (mm_size < l+1) { - size_t nn_size; - char *nn; - - nn_size = PAGE_ALIGN(l+1); - nn = mmap(NULL, nn_size, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0); - if (nn == MAP_FAILED) - return log_debug_errno(errno, "mmap() failed: %m"); - - strncpy(nn, name, nn_size); - - /* Now, let's tell the kernel about this new memory */ - if (prctl(PR_SET_MM, PR_SET_MM_ARG_START, (unsigned long) nn, 0, 0) < 0) { - if (ERRNO_IS_PRIVILEGE(errno)) - return log_debug_errno(errno, "PR_SET_MM_ARG_START failed: %m"); - - /* HACK: prctl() API is kind of dumb on this point. The existing end address may already be - * below the desired start address, in which case the kernel may have kicked this back due - * to a range-check failure (see linux/kernel/sys.c:validate_prctl_map() to see this in - * action). The proper solution would be to have a prctl() API that could set both start+end - * simultaneously, or at least let us query the existing address to anticipate this condition - * and respond accordingly. For now, we can only guess at the cause of this failure and try - * a workaround--which will briefly expand the arg space to something potentially huge before - * resizing it to what we want. */ - log_debug_errno(errno, "PR_SET_MM_ARG_START failed, attempting PR_SET_MM_ARG_END hack: %m"); - - if (prctl(PR_SET_MM, PR_SET_MM_ARG_END, (unsigned long) nn + l + 1, 0, 0) < 0) { - r = log_debug_errno(errno, "PR_SET_MM_ARG_END hack failed, proceeding without: %m"); - (void) munmap(nn, nn_size); - return r; - } - - if (prctl(PR_SET_MM, PR_SET_MM_ARG_START, (unsigned long) nn, 0, 0) < 0) - return log_debug_errno(errno, "PR_SET_MM_ARG_START still failed, proceeding without: %m"); - } else { - /* And update the end pointer to the new end, too. If this fails, we don't really know what - * to do, it's pretty unlikely that we can rollback, hence we'll just accept the failure, - * and continue. */ - if (prctl(PR_SET_MM, PR_SET_MM_ARG_END, (unsigned long) nn + l + 1, 0, 0) < 0) - log_debug_errno(errno, "PR_SET_MM_ARG_END failed, proceeding without: %m"); - } - - if (mm) - (void) munmap(mm, mm_size); - - mm = nn; - mm_size = nn_size; - } else { - strncpy(mm, name, mm_size); - - /* Update the end pointer, continuing regardless of any failure. */ - if (prctl(PR_SET_MM, PR_SET_MM_ARG_END, (unsigned long) mm + l + 1, 0, 0) < 0) - log_debug_errno(errno, "PR_SET_MM_ARG_END failed, proceeding without: %m"); - } - - can_do = true; - return 0; -} - -int rename_process(const char name[]) { - bool truncated = false; - - /* This is a like a poor man's setproctitle(). It changes the comm field, argv[0], and also the glibc's - * internally used name of the process. For the first one a limit of 16 chars applies; to the second one in - * many cases one of 10 (i.e. length of "/sbin/init") — however if we have CAP_SYS_RESOURCES it is unbounded; - * to the third one 7 (i.e. the length of "systemd". If you pass a longer string it will likely be - * truncated. - * - * Returns 0 if a name was set but truncated, > 0 if it was set but not truncated. */ - - if (isempty(name)) - return -EINVAL; /* let's not confuse users unnecessarily with an empty name */ - - if (!is_main_thread()) - return -EPERM; /* Let's not allow setting the process name from other threads than the main one, as we - * cache things without locking, and we make assumptions that PR_SET_NAME sets the - * process name that isn't correct on any other threads */ - - size_t l = strlen(name); - - /* First step, change the comm field. The main thread's comm is identical to the process comm. This means we - * can use PR_SET_NAME, which sets the thread name for the calling thread. */ - if (prctl(PR_SET_NAME, name) < 0) - log_debug_errno(errno, "PR_SET_NAME failed: %m"); - if (l >= TASK_COMM_LEN) /* Linux userspace process names can be 15 chars at max */ - truncated = true; - - /* Second step, change glibc's ID of the process name. */ - if (program_invocation_name) { - size_t k; - - k = strlen(program_invocation_name); - strncpy(program_invocation_name, name, k); - if (l > k) - truncated = true; - } - - /* Third step, completely replace the argv[] array the kernel maintains for us. This requires privileges, but - * has the advantage that the argv[] array is exactly what we want it to be, and not filled up with zeros at - * the end. This is the best option for changing /proc/self/cmdline. */ - (void) update_argv(name, l); - - /* Fourth step: in all cases we'll also update the original argv[], so that our own code gets it right too if - * it still looks here */ - if (saved_argc > 0) { - if (saved_argv[0]) { - size_t k; - - k = strlen(saved_argv[0]); - strncpy(saved_argv[0], name, k); - if (l > k) - truncated = true; - } - - for (int i = 1; i < saved_argc; i++) { - if (!saved_argv[i]) - break; - - memzero(saved_argv[i], strlen(saved_argv[i])); - } - } - - return !truncated; -} - -int is_kernel_thread(pid_t pid) { - _cleanup_free_ char *line = NULL; - unsigned long long flags; - size_t l, i; - const char *p; - char *q; - int r; - - if (IN_SET(pid, 0, 1) || pid == getpid_cached()) /* pid 1, and we ourselves certainly aren't a kernel thread */ - return 0; - if (!pid_is_valid(pid)) - return -EINVAL; - - p = procfs_file_alloca(pid, "stat"); - r = read_one_line_file(p, &line); - if (r == -ENOENT) - return -ESRCH; - if (r < 0) - return r; - - /* Skip past the comm field */ - q = strrchr(line, ')'); - if (!q) - return -EINVAL; - q++; - - /* Skip 6 fields to reach the flags field */ - for (i = 0; i < 6; i++) { - l = strspn(q, WHITESPACE); - if (l < 1) - return -EINVAL; - q += l; - - l = strcspn(q, WHITESPACE); - if (l < 1) - return -EINVAL; - q += l; - } - - /* Skip preceding whitespace */ - l = strspn(q, WHITESPACE); - if (l < 1) - return -EINVAL; - q += l; - - /* Truncate the rest */ - l = strcspn(q, WHITESPACE); - if (l < 1) - return -EINVAL; - q[l] = 0; - - r = safe_atollu(q, &flags); - if (r < 0) - return r; - - return !!(flags & PF_KTHREAD); -} - -int get_process_capeff(pid_t pid, char **capeff) { - const char *p; - int r; - - assert(capeff); - assert(pid >= 0); - - p = procfs_file_alloca(pid, "status"); - - r = get_proc_field(p, "CapEff", WHITESPACE, capeff); - if (r == -ENOENT) - return -ESRCH; - - return r; -} - -static int get_process_link_contents(const char *proc_file, char **name) { - int r; - - assert(proc_file); - assert(name); - - r = readlink_malloc(proc_file, name); - if (r == -ENOENT) - return -ESRCH; - if (r < 0) - return r; - - return 0; -} - -int get_process_exe(pid_t pid, char **name) { - const char *p; - char *d; - int r; - - assert(pid >= 0); - - p = procfs_file_alloca(pid, "exe"); - r = get_process_link_contents(p, name); - if (r < 0) - return r; - - d = endswith(*name, " (deleted)"); - if (d) - *d = '\0'; - - return 0; -} - -static int get_process_id(pid_t pid, const char *field, uid_t *uid) { - _cleanup_fclose_ FILE *f = NULL; - const char *p; - int r; - - assert(field); - assert(uid); - - if (pid < 0) - return -EINVAL; - - p = procfs_file_alloca(pid, "status"); - r = fopen_unlocked(p, "re", &f); - if (r == -ENOENT) - return -ESRCH; - if (r < 0) - return r; - - for (;;) { - _cleanup_free_ char *line = NULL; - char *l; - - r = read_line(f, LONG_LINE_MAX, &line); - if (r < 0) - return r; - if (r == 0) - break; - - l = strstrip(line); - - if (startswith(l, field)) { - l += strlen(field); - l += strspn(l, WHITESPACE); - - l[strcspn(l, WHITESPACE)] = 0; - - return parse_uid(l, uid); - } - } - - return -EIO; -} - -int get_process_uid(pid_t pid, uid_t *uid) { - - if (pid == 0 || pid == getpid_cached()) { - *uid = getuid(); - return 0; - } - - return get_process_id(pid, "Uid:", uid); -} - -int get_process_gid(pid_t pid, gid_t *gid) { - - if (pid == 0 || pid == getpid_cached()) { - *gid = getgid(); - return 0; - } - - assert_cc(sizeof(uid_t) == sizeof(gid_t)); - return get_process_id(pid, "Gid:", gid); -} - -int get_process_cwd(pid_t pid, char **cwd) { - const char *p; - - assert(pid >= 0); - - if (pid == 0 || pid == getpid_cached()) - return safe_getcwd(cwd); - - p = procfs_file_alloca(pid, "cwd"); - - return get_process_link_contents(p, cwd); -} - -int get_process_root(pid_t pid, char **root) { - const char *p; - - assert(pid >= 0); - - p = procfs_file_alloca(pid, "root"); - - return get_process_link_contents(p, root); -} - -#define ENVIRONMENT_BLOCK_MAX (5U*1024U*1024U) - -int get_process_environ(pid_t pid, char **env) { - _cleanup_fclose_ FILE *f = NULL; - _cleanup_free_ char *outcome = NULL; - size_t allocated = 0, sz = 0; - const char *p; - int r; - - assert(pid >= 0); - assert(env); - - p = procfs_file_alloca(pid, "environ"); - - r = fopen_unlocked(p, "re", &f); - if (r == -ENOENT) - return -ESRCH; - if (r < 0) - return r; - - for (;;) { - char c; - - if (sz >= ENVIRONMENT_BLOCK_MAX) - return -ENOBUFS; - - if (!GREEDY_REALLOC(outcome, allocated, sz + 5)) - return -ENOMEM; - - r = safe_fgetc(f, &c); - if (r < 0) - return r; - if (r == 0) - break; - - if (c == '\0') - outcome[sz++] = '\n'; - else - sz += cescape_char(c, outcome + sz); - } - - outcome[sz] = '\0'; - *env = TAKE_PTR(outcome); - - return 0; -} - -int get_process_ppid(pid_t pid, pid_t *_ppid) { - int r; - _cleanup_free_ char *line = NULL; - long unsigned ppid; - const char *p; - - assert(pid >= 0); - assert(_ppid); - - if (pid == 0 || pid == getpid_cached()) { - *_ppid = getppid(); - return 0; - } - - p = procfs_file_alloca(pid, "stat"); - r = read_one_line_file(p, &line); - if (r == -ENOENT) - return -ESRCH; - if (r < 0) - return r; - - /* Let's skip the pid and comm fields. The latter is enclosed - * in () but does not escape any () in its value, so let's - * skip over it manually */ - - p = strrchr(line, ')'); - if (!p) - return -EIO; - - p++; - - if (sscanf(p, " " - "%*c " /* state */ - "%lu ", /* ppid */ - &ppid) != 1) - return -EIO; - - if ((long unsigned) (pid_t) ppid != ppid) - return -ERANGE; - - *_ppid = (pid_t) ppid; - - return 0; -} - -int get_process_umask(pid_t pid, mode_t *umask) { - _cleanup_free_ char *m = NULL; - const char *p; - int r; - - assert(umask); - assert(pid >= 0); - - p = procfs_file_alloca(pid, "status"); - - r = get_proc_field(p, "Umask", WHITESPACE, &m); - if (r == -ENOENT) - return -ESRCH; - - return parse_mode(m, umask); -} - -int wait_for_terminate(pid_t pid, siginfo_t *status) { - siginfo_t dummy; - - assert(pid >= 1); - - if (!status) - status = &dummy; - - for (;;) { - zero(*status); - - if (waitid(P_PID, pid, status, WEXITED) < 0) { - - if (errno == EINTR) - continue; - - return negative_errno(); - } - - return 0; - } -} - -/* - * Return values: - * < 0 : wait_for_terminate() failed to get the state of the - * process, the process was terminated by a signal, or - * failed for an unknown reason. - * >=0 : The process terminated normally, and its exit code is - * returned. - * - * That is, success is indicated by a return value of zero, and an - * error is indicated by a non-zero value. - * - * A warning is emitted if the process terminates abnormally, - * and also if it returns non-zero unless check_exit_code is true. - */ -int wait_for_terminate_and_check(const char *name, pid_t pid, WaitFlags flags) { - _cleanup_free_ char *buffer = NULL; - siginfo_t status; - int r, prio; - - assert(pid > 1); - - if (!name) { - r = get_process_comm(pid, &buffer); - if (r < 0) - log_debug_errno(r, "Failed to acquire process name of " PID_FMT ", ignoring: %m", pid); - else - name = buffer; - } - - prio = flags & WAIT_LOG_ABNORMAL ? LOG_ERR : LOG_DEBUG; - - r = wait_for_terminate(pid, &status); - if (r < 0) - return log_full_errno(prio, r, "Failed to wait for %s: %m", strna(name)); - - if (status.si_code == CLD_EXITED) { - if (status.si_status != EXIT_SUCCESS) - log_full(flags & WAIT_LOG_NON_ZERO_EXIT_STATUS ? LOG_ERR : LOG_DEBUG, - "%s failed with exit status %i.", strna(name), status.si_status); - else - log_debug("%s succeeded.", name); - - return status.si_status; - - } else if (IN_SET(status.si_code, CLD_KILLED, CLD_DUMPED)) { - - log_full(prio, "%s terminated by signal %s.", strna(name), signal_to_string(status.si_status)); - return -EPROTO; - } - - log_full(prio, "%s failed due to unknown reason.", strna(name)); - return -EPROTO; -} - -/* - * Return values: - * - * < 0 : wait_for_terminate_with_timeout() failed to get the state of the process, the process timed out, the process - * was terminated by a signal, or failed for an unknown reason. - * - * >=0 : The process terminated normally with no failures. - * - * Success is indicated by a return value of zero, a timeout is indicated by ETIMEDOUT, and all other child failure - * states are indicated by error is indicated by a non-zero value. - * - * This call assumes SIGCHLD has been blocked already, in particular before the child to wait for has been forked off - * to remain entirely race-free. - */ -int wait_for_terminate_with_timeout(pid_t pid, usec_t timeout) { - sigset_t mask; - int r; - usec_t until; - - assert_se(sigemptyset(&mask) == 0); - assert_se(sigaddset(&mask, SIGCHLD) == 0); - - /* Drop into a sigtimewait-based timeout. Waiting for the - * pid to exit. */ - until = now(CLOCK_MONOTONIC) + timeout; - for (;;) { - usec_t n; - siginfo_t status = {}; - struct timespec ts; - - n = now(CLOCK_MONOTONIC); - if (n >= until) - break; - - r = sigtimedwait(&mask, NULL, timespec_store(&ts, until - n)) < 0 ? -errno : 0; - /* Assuming we woke due to the child exiting. */ - if (waitid(P_PID, pid, &status, WEXITED|WNOHANG) == 0) { - if (status.si_pid == pid) { - /* This is the correct child.*/ - if (status.si_code == CLD_EXITED) - return (status.si_status == 0) ? 0 : -EPROTO; - else - return -EPROTO; - } - } - /* Not the child, check for errors and proceed appropriately */ - if (r < 0) { - switch (r) { - case -EAGAIN: - /* Timed out, child is likely hung. */ - return -ETIMEDOUT; - case -EINTR: - /* Received a different signal and should retry */ - continue; - default: - /* Return any unexpected errors */ - return r; - } - } - } - - return -EPROTO; -} - -void sigkill_wait(pid_t pid) { - assert(pid > 1); - - if (kill(pid, SIGKILL) >= 0) - (void) wait_for_terminate(pid, NULL); -} - -void sigkill_waitp(pid_t *pid) { - PROTECT_ERRNO; - - if (!pid) - return; - if (*pid <= 1) - return; - - sigkill_wait(*pid); -} - -void sigterm_wait(pid_t pid) { - assert(pid > 1); - - if (kill_and_sigcont(pid, SIGTERM) >= 0) - (void) wait_for_terminate(pid, NULL); -} - -int kill_and_sigcont(pid_t pid, int sig) { - int r; - - r = kill(pid, sig) < 0 ? -errno : 0; - - /* If this worked, also send SIGCONT, unless we already just sent a SIGCONT, or SIGKILL was sent which isn't - * affected by a process being suspended anyway. */ - if (r >= 0 && !IN_SET(sig, SIGCONT, SIGKILL)) - (void) kill(pid, SIGCONT); - - return r; -} - -int getenv_for_pid(pid_t pid, const char *field, char **ret) { - _cleanup_fclose_ FILE *f = NULL; - char *value = NULL; - const char *path; - size_t l, sum = 0; - int r; - - assert(pid >= 0); - assert(field); - assert(ret); - - if (pid == 0 || pid == getpid_cached()) { - const char *e; - - e = getenv(field); - if (!e) { - *ret = NULL; - return 0; - } - - value = strdup(e); - if (!value) - return -ENOMEM; - - *ret = value; - return 1; - } - - if (!pid_is_valid(pid)) - return -EINVAL; - - path = procfs_file_alloca(pid, "environ"); - - r = fopen_unlocked(path, "re", &f); - if (r == -ENOENT) - return -ESRCH; - if (r < 0) - return r; - - l = strlen(field); - for (;;) { - _cleanup_free_ char *line = NULL; - - if (sum > ENVIRONMENT_BLOCK_MAX) /* Give up searching eventually */ - return -ENOBUFS; - - r = read_nul_string(f, LONG_LINE_MAX, &line); - if (r < 0) - return r; - if (r == 0) /* EOF */ - break; - - sum += r; - - if (strneq(line, field, l) && line[l] == '=') { - value = strdup(line + l + 1); - if (!value) - return -ENOMEM; - - *ret = value; - return 1; - } - } - - *ret = NULL; - return 0; -} - -int pid_is_my_child(pid_t pid) { - pid_t ppid; - int r; - - if (pid <= 1) - return false; - - r = get_process_ppid(pid, &ppid); - if (r < 0) - return r; - - return ppid == getpid_cached(); -} - -bool pid_is_unwaited(pid_t pid) { - /* Checks whether a PID is still valid at all, including a zombie */ - - if (pid < 0) - return false; - - if (pid <= 1) /* If we or PID 1 would be dead and have been waited for, this code would not be running */ - return true; - - if (pid == getpid_cached()) - return true; - - if (kill(pid, 0) >= 0) - return true; - - return errno != ESRCH; -} - -bool pid_is_alive(pid_t pid) { - int r; - - /* Checks whether a PID is still valid and not a zombie */ - - if (pid < 0) - return false; - - if (pid <= 1) /* If we or PID 1 would be a zombie, this code would not be running */ - return true; - - if (pid == getpid_cached()) - return true; - - r = get_process_state(pid); - if (IN_SET(r, -ESRCH, 'Z')) - return false; - - return true; -} - -int pid_from_same_root_fs(pid_t pid) { - const char *root; - - if (pid < 0) - return false; - - if (pid == 0 || pid == getpid_cached()) - return true; - - root = procfs_file_alloca(pid, "root"); - - return files_same(root, "/proc/1/root", 0); -} -#endif /* NM_IGNORED */ - -bool is_main_thread(void) { - static thread_local int cached = 0; - - if (_unlikely_(cached == 0)) - cached = getpid_cached() == gettid() ? 1 : -1; - - return cached > 0; -} - -#if 0 /* NM_IGNORED */ -_noreturn_ void freeze(void) { - - log_close(); - - /* Make sure nobody waits for us on a socket anymore */ - (void) close_all_fds(NULL, 0); - - sync(); - - /* Let's not freeze right away, but keep reaping zombies. */ - for (;;) { - int r; - siginfo_t si = {}; - - r = waitid(P_ALL, 0, &si, WEXITED); - if (r < 0 && errno != EINTR) - break; - } - - /* waitid() failed with an unexpected error, things are really borked. Freeze now! */ - for (;;) - pause(); -} - -bool oom_score_adjust_is_valid(int oa) { - return oa >= OOM_SCORE_ADJ_MIN && oa <= OOM_SCORE_ADJ_MAX; -} - -unsigned long personality_from_string(const char *p) { - int architecture; - - if (!p) - return PERSONALITY_INVALID; - - /* Parse a personality specifier. We use our own identifiers that indicate specific ABIs, rather than just - * hints regarding the register size, since we want to keep things open for multiple locally supported ABIs for - * the same register size. */ - - architecture = architecture_from_string(p); - if (architecture < 0) - return PERSONALITY_INVALID; - - if (architecture == native_architecture()) - return PER_LINUX; -#ifdef SECONDARY_ARCHITECTURE - if (architecture == SECONDARY_ARCHITECTURE) - return PER_LINUX32; -#endif - - return PERSONALITY_INVALID; -} - -const char* personality_to_string(unsigned long p) { - int architecture = _ARCHITECTURE_INVALID; - - if (p == PER_LINUX) - architecture = native_architecture(); -#ifdef SECONDARY_ARCHITECTURE - else if (p == PER_LINUX32) - architecture = SECONDARY_ARCHITECTURE; -#endif - - if (architecture < 0) - return NULL; - - return architecture_to_string(architecture); -} - -int safe_personality(unsigned long p) { - int ret; - - /* So here's the deal, personality() is weirdly defined by glibc. In some cases it returns a failure via errno, - * and in others as negative return value containing an errno-like value. Let's work around this: this is a - * wrapper that uses errno if it is set, and uses the return value otherwise. And then it sets both errno and - * the return value indicating the same issue, so that we are definitely on the safe side. - * - * See https://github.com/systemd/systemd/issues/6737 */ - - errno = 0; - ret = personality(p); - if (ret < 0) { - if (errno != 0) - return -errno; - - errno = -ret; - } - - return ret; -} - -int opinionated_personality(unsigned long *ret) { - int current; - - /* Returns the current personality, or PERSONALITY_INVALID if we can't determine it. This function is a bit - * opinionated though, and ignores all the finer-grained bits and exotic personalities, only distinguishing the - * two most relevant personalities: PER_LINUX and PER_LINUX32. */ - - current = safe_personality(PERSONALITY_INVALID); - if (current < 0) - return current; - - if (((unsigned long) current & 0xffff) == PER_LINUX32) - *ret = PER_LINUX32; - else - *ret = PER_LINUX; - - return 0; -} - -void valgrind_summary_hack(void) { -#if HAVE_VALGRIND_VALGRIND_H - if (getpid_cached() == 1 && RUNNING_ON_VALGRIND) { - pid_t pid; - pid = raw_clone(SIGCHLD); - if (pid < 0) - log_emergency_errno(errno, "Failed to fork off valgrind helper: %m"); - else if (pid == 0) - exit(EXIT_SUCCESS); - else { - log_info("Spawned valgrind helper as PID "PID_FMT".", pid); - (void) wait_for_terminate(pid, NULL); - } - } -#endif -} - -int pid_compare_func(const pid_t *a, const pid_t *b) { - /* Suitable for usage in qsort() */ - return CMP(*a, *b); -} - -int ioprio_parse_priority(const char *s, int *ret) { - int i, r; - - assert(s); - assert(ret); - - r = safe_atoi(s, &i); - if (r < 0) - return r; - - if (!ioprio_priority_is_valid(i)) - return -EINVAL; - - *ret = i; - return 0; -} -#endif /* NM_IGNORED */ - -/* The cached PID, possible values: - * - * == UNSET [0] → cache not initialized yet - * == BUSY [-1] → some thread is initializing it at the moment - * any other → the cached PID - */ - -#define CACHED_PID_UNSET ((pid_t) 0) -#define CACHED_PID_BUSY ((pid_t) -1) - -static pid_t cached_pid = CACHED_PID_UNSET; - -void reset_cached_pid(void) { - /* Invoked in the child after a fork(), i.e. at the first moment the PID changed */ - cached_pid = CACHED_PID_UNSET; -} - -/* We use glibc __register_atfork() + __dso_handle directly here, as they are not included in the glibc - * headers. __register_atfork() is mostly equivalent to pthread_atfork(), but doesn't require us to link against - * libpthread, as it is part of glibc anyway. */ -extern int __register_atfork(void (*prepare) (void), void (*parent) (void), void (*child) (void), void *dso_handle); -extern void* __dso_handle _weak_; - -pid_t getpid_cached(void) { - static bool installed = false; - pid_t current_value; - - /* getpid_cached() is much like getpid(), but caches the value in local memory, to avoid having to invoke a - * system call each time. This restores glibc behaviour from before 2.24, when getpid() was unconditionally - * cached. Starting with 2.24 getpid() started to become prohibitively expensive when used for detecting when - * objects were used across fork()s. With this caching the old behaviour is somewhat restored. - * - * https://bugzilla.redhat.com/show_bug.cgi?id=1443976 - * https://sourceware.org/git/gitweb.cgi?p=glibc.git;h=c579f48edba88380635ab98cb612030e3ed8691e - */ - - current_value = __sync_val_compare_and_swap(&cached_pid, CACHED_PID_UNSET, CACHED_PID_BUSY); - - switch (current_value) { - - case CACHED_PID_UNSET: { /* Not initialized yet, then do so now */ - pid_t new_pid; - - new_pid = raw_getpid(); - - if (!installed) { - /* __register_atfork() either returns 0 or -ENOMEM, in its glibc implementation. Since it's - * only half-documented (glibc doesn't document it but LSB does — though only superficially) - * we'll check for errors only in the most generic fashion possible. */ - - if (__register_atfork(NULL, NULL, reset_cached_pid, __dso_handle) != 0) { - /* OOM? Let's try again later */ - cached_pid = CACHED_PID_UNSET; - return new_pid; - } - - installed = true; - } - - cached_pid = new_pid; - return new_pid; - } - - case CACHED_PID_BUSY: /* Somebody else is currently initializing */ - return raw_getpid(); - - default: /* Properly initialized */ - return current_value; - } -} - -#if 0 /* NM_IGNORED */ -int must_be_root(void) { - - if (geteuid() == 0) - return 0; - - return log_error_errno(SYNTHETIC_ERRNO(EPERM), "Need to be root."); -} - -static void restore_sigsetp(sigset_t **ssp) { - if (*ssp) - (void) sigprocmask(SIG_SETMASK, *ssp, NULL); -} - -int safe_fork_full( - const char *name, - const int except_fds[], - size_t n_except_fds, - ForkFlags flags, - pid_t *ret_pid) { - - pid_t original_pid, pid; - sigset_t saved_ss, ss; - _cleanup_(restore_sigsetp) sigset_t *saved_ssp = NULL; - bool block_signals = false, block_all = false; - int prio, r; - - /* A wrapper around fork(), that does a couple of important initializations in addition to mere forking. Always - * returns the child's PID in *ret_pid. Returns == 0 in the child, and > 0 in the parent. */ - - prio = flags & FORK_LOG ? LOG_ERR : LOG_DEBUG; - - original_pid = getpid_cached(); - - if (flags & (FORK_RESET_SIGNALS|FORK_DEATHSIG)) { - /* We temporarily block all signals, so that the new child has them blocked initially. This way, we can - * be sure that SIGTERMs are not lost we might send to the child. */ - - assert_se(sigfillset(&ss) >= 0); - block_signals = block_all = true; - - } else if (flags & FORK_WAIT) { - /* Let's block SIGCHLD at least, so that we can safely watch for the child process */ - - assert_se(sigemptyset(&ss) >= 0); - assert_se(sigaddset(&ss, SIGCHLD) >= 0); - block_signals = true; - } - - if (block_signals) { - if (sigprocmask(SIG_SETMASK, &ss, &saved_ss) < 0) - return log_full_errno(prio, errno, "Failed to set signal mask: %m"); - saved_ssp = &saved_ss; - } - - if (flags & FORK_NEW_MOUNTNS) - pid = raw_clone(SIGCHLD|CLONE_NEWNS); - else - pid = fork(); - if (pid < 0) - return log_full_errno(prio, errno, "Failed to fork: %m"); - if (pid > 0) { - /* We are in the parent process */ - - log_debug("Successfully forked off '%s' as PID " PID_FMT ".", strna(name), pid); - - if (flags & FORK_WAIT) { - if (block_all) { - /* undo everything except SIGCHLD */ - ss = saved_ss; - assert_se(sigaddset(&ss, SIGCHLD) >= 0); - (void) sigprocmask(SIG_SETMASK, &ss, NULL); - } - - r = wait_for_terminate_and_check(name, pid, (flags & FORK_LOG ? WAIT_LOG : 0)); - if (r < 0) - return r; - if (r != EXIT_SUCCESS) /* exit status > 0 should be treated as failure, too */ - return -EPROTO; - } - - if (ret_pid) - *ret_pid = pid; - - return 1; - } - - /* We are in the child process */ - - /* Restore signal mask manually */ - saved_ssp = NULL; - - if (flags & FORK_REOPEN_LOG) { - /* Close the logs if requested, before we log anything. And make sure we reopen it if needed. */ - log_close(); - log_set_open_when_needed(true); - } - - if (name) { - r = rename_process(name); - if (r < 0) - log_full_errno(flags & FORK_LOG ? LOG_WARNING : LOG_DEBUG, - r, "Failed to rename process, ignoring: %m"); - } - - if (flags & (FORK_DEATHSIG|FORK_DEATHSIG_SIGINT)) - if (prctl(PR_SET_PDEATHSIG, (flags & FORK_DEATHSIG_SIGINT) ? SIGINT : SIGTERM) < 0) { - log_full_errno(prio, errno, "Failed to set death signal: %m"); - _exit(EXIT_FAILURE); - } - - if (flags & FORK_RESET_SIGNALS) { - r = reset_all_signal_handlers(); - if (r < 0) { - log_full_errno(prio, r, "Failed to reset signal handlers: %m"); - _exit(EXIT_FAILURE); - } - - /* This implicitly undoes the signal mask stuff we did before the fork()ing above */ - r = reset_signal_mask(); - if (r < 0) { - log_full_errno(prio, r, "Failed to reset signal mask: %m"); - _exit(EXIT_FAILURE); - } - } else if (block_signals) { /* undo what we did above */ - if (sigprocmask(SIG_SETMASK, &saved_ss, NULL) < 0) { - log_full_errno(prio, errno, "Failed to restore signal mask: %m"); - _exit(EXIT_FAILURE); - } - } - - if (flags & FORK_DEATHSIG) { - pid_t ppid; - /* Let's see if the parent PID is still the one we started from? If not, then the parent - * already died by the time we set PR_SET_PDEATHSIG, hence let's emulate the effect */ - - ppid = getppid(); - if (ppid == 0) - /* Parent is in a different PID namespace. */; - else if (ppid != original_pid) { - log_debug("Parent died early, raising SIGTERM."); - (void) raise(SIGTERM); - _exit(EXIT_FAILURE); - } - } - - if (FLAGS_SET(flags, FORK_NEW_MOUNTNS | FORK_MOUNTNS_SLAVE)) { - - /* Optionally, make sure we never propagate mounts to the host. */ - - if (mount(NULL, "/", NULL, MS_SLAVE | MS_REC, NULL) < 0) { - log_full_errno(prio, errno, "Failed to remount root directory as MS_SLAVE: %m"); - _exit(EXIT_FAILURE); - } - } - - if (flags & FORK_CLOSE_ALL_FDS) { - /* Close the logs here in case it got reopened above, as close_all_fds() would close them for us */ - log_close(); - - r = close_all_fds(except_fds, n_except_fds); - if (r < 0) { - log_full_errno(prio, r, "Failed to close all file descriptors: %m"); - _exit(EXIT_FAILURE); - } - } - - /* When we were asked to reopen the logs, do so again now */ - if (flags & FORK_REOPEN_LOG) { - log_open(); - log_set_open_when_needed(false); - } - - if (flags & FORK_NULL_STDIO) { - r = make_null_stdio(); - if (r < 0) { - log_full_errno(prio, r, "Failed to connect stdin/stdout to /dev/null: %m"); - _exit(EXIT_FAILURE); - } - - } else if (flags & FORK_STDOUT_TO_STDERR) { - if (dup2(STDERR_FILENO, STDOUT_FILENO) < 0) { - log_full_errno(prio, errno, "Failed to connect stdout to stderr: %m"); - _exit(EXIT_FAILURE); - } - } - - if (flags & FORK_RLIMIT_NOFILE_SAFE) { - r = rlimit_nofile_safe(); - if (r < 0) { - log_full_errno(prio, r, "Failed to lower RLIMIT_NOFILE's soft limit to 1K: %m"); - _exit(EXIT_FAILURE); - } - } - - if (ret_pid) - *ret_pid = getpid_cached(); - - return 0; -} - -int namespace_fork( - const char *outer_name, - const char *inner_name, - const int except_fds[], - size_t n_except_fds, - ForkFlags flags, - int pidns_fd, - int mntns_fd, - int netns_fd, - int userns_fd, - int root_fd, - pid_t *ret_pid) { - - int r; - - /* This is much like safe_fork(), but forks twice, and joins the specified namespaces in the middle - * process. This ensures that we are fully a member of the destination namespace, with pidns an all, so that - * /proc/self/fd works correctly. */ - - r = safe_fork_full(outer_name, except_fds, n_except_fds, (flags|FORK_DEATHSIG) & ~(FORK_REOPEN_LOG|FORK_NEW_MOUNTNS|FORK_MOUNTNS_SLAVE), ret_pid); - if (r < 0) - return r; - if (r == 0) { - pid_t pid; - - /* Child */ - - r = namespace_enter(pidns_fd, mntns_fd, netns_fd, userns_fd, root_fd); - if (r < 0) { - log_full_errno(FLAGS_SET(flags, FORK_LOG) ? LOG_ERR : LOG_DEBUG, r, "Failed to join namespace: %m"); - _exit(EXIT_FAILURE); - } - - /* We mask a few flags here that either make no sense for the grandchild, or that we don't have to do again */ - r = safe_fork_full(inner_name, except_fds, n_except_fds, flags & ~(FORK_WAIT|FORK_RESET_SIGNALS|FORK_CLOSE_ALL_FDS|FORK_NULL_STDIO), &pid); - if (r < 0) - _exit(EXIT_FAILURE); - if (r == 0) { - /* Child */ - if (ret_pid) - *ret_pid = pid; - return 0; - } - - r = wait_for_terminate_and_check(inner_name, pid, FLAGS_SET(flags, FORK_LOG) ? WAIT_LOG : 0); - if (r < 0) - _exit(EXIT_FAILURE); - - _exit(r); - } - - return 1; -} - -int fork_agent(const char *name, const int except[], size_t n_except, pid_t *ret_pid, const char *path, ...) { - bool stdout_is_tty, stderr_is_tty; - size_t n, i; - va_list ap; - char **l; - int r; - - assert(path); - - /* Spawns a temporary TTY agent, making sure it goes away when we go away */ - - r = safe_fork_full(name, except, n_except, FORK_RESET_SIGNALS|FORK_DEATHSIG|FORK_CLOSE_ALL_FDS, ret_pid); - if (r < 0) - return r; - if (r > 0) - return 0; - - /* In the child: */ - - stdout_is_tty = isatty(STDOUT_FILENO); - stderr_is_tty = isatty(STDERR_FILENO); - - if (!stdout_is_tty || !stderr_is_tty) { - int fd; - - /* Detach from stdout/stderr. and reopen - * /dev/tty for them. This is important to - * ensure that when systemctl is started via - * popen() or a similar call that expects to - * read EOF we actually do generate EOF and - * not delay this indefinitely by because we - * keep an unused copy of stdin around. */ - fd = open("/dev/tty", O_WRONLY); - if (fd < 0) { - log_error_errno(errno, "Failed to open /dev/tty: %m"); - _exit(EXIT_FAILURE); - } - - if (!stdout_is_tty && dup2(fd, STDOUT_FILENO) < 0) { - log_error_errno(errno, "Failed to dup2 /dev/tty: %m"); - _exit(EXIT_FAILURE); - } - - if (!stderr_is_tty && dup2(fd, STDERR_FILENO) < 0) { - log_error_errno(errno, "Failed to dup2 /dev/tty: %m"); - _exit(EXIT_FAILURE); - } - - safe_close_above_stdio(fd); - } - - (void) rlimit_nofile_safe(); - - /* Count arguments */ - va_start(ap, path); - for (n = 0; va_arg(ap, char*); n++) - ; - va_end(ap); - - /* Allocate strv */ - l = newa(char*, n + 1); - - /* Fill in arguments */ - va_start(ap, path); - for (i = 0; i <= n; i++) - l[i] = va_arg(ap, char*); - va_end(ap); - - execv(path, l); - _exit(EXIT_FAILURE); -} - -int set_oom_score_adjust(int value) { - char t[DECIMAL_STR_MAX(int)]; - - sprintf(t, "%i", value); - - return write_string_file("/proc/self/oom_score_adj", t, - WRITE_STRING_FILE_VERIFY_ON_FAILURE|WRITE_STRING_FILE_DISABLE_BUFFER); -} - -int pidfd_get_pid(int fd, pid_t *ret) { - char path[STRLEN("/proc/self/fdinfo/") + DECIMAL_STR_MAX(int)]; - _cleanup_free_ char *fdinfo = NULL; - char *p; - int r; - - if (fd < 0) - return -EBADF; - - xsprintf(path, "/proc/self/fdinfo/%i", fd); - - r = read_full_file(path, &fdinfo, NULL); - if (r == -ENOENT) /* if fdinfo doesn't exist we assume the process does not exist */ - return -ESRCH; - if (r < 0) - return r; - - p = startswith(fdinfo, "Pid:"); - if (!p) { - p = strstr(fdinfo, "\nPid:"); - if (!p) - return -ENOTTY; /* not a pidfd? */ - - p += 5; - } - - p += strspn(p, WHITESPACE); - p[strcspn(p, WHITESPACE)] = 0; - - return parse_pid(p, ret); -} - -static int rlimit_to_nice(rlim_t limit) { - if (limit <= 1) - return PRIO_MAX-1; /* i.e. 19 */ - - if (limit >= -PRIO_MIN + PRIO_MAX) - return PRIO_MIN; /* i.e. -20 */ - - return PRIO_MAX - (int) limit; -} - -int setpriority_closest(int priority) { - int current, limit, saved_errno; - struct rlimit highest; - - /* Try to set requested nice level */ - if (setpriority(PRIO_PROCESS, 0, priority) >= 0) - return 1; - - /* Permission failed */ - saved_errno = -errno; - if (!ERRNO_IS_PRIVILEGE(saved_errno)) - return saved_errno; - - errno = 0; - current = getpriority(PRIO_PROCESS, 0); - if (errno != 0) - return -errno; - - if (priority == current) - return 1; - - /* Hmm, we'd expect that raising the nice level from our status quo would always work. If it doesn't, - * then the whole setpriority() system call is blocked to us, hence let's propagate the error - * right-away */ - if (priority > current) - return saved_errno; - - if (getrlimit(RLIMIT_NICE, &highest) < 0) - return -errno; - - limit = rlimit_to_nice(highest.rlim_cur); - - /* We are already less nice than limit allows us */ - if (current < limit) { - log_debug("Cannot raise nice level, permissions and the resource limit do not allow it."); - return 0; - } - - /* Push to the allowed limit */ - if (setpriority(PRIO_PROCESS, 0, limit) < 0) - return -errno; - - log_debug("Cannot set requested nice level (%i), used next best (%i).", priority, limit); - return 0; -} - -static const char *const ioprio_class_table[] = { - [IOPRIO_CLASS_NONE] = "none", - [IOPRIO_CLASS_RT] = "realtime", - [IOPRIO_CLASS_BE] = "best-effort", - [IOPRIO_CLASS_IDLE] = "idle", -}; - -DEFINE_STRING_TABLE_LOOKUP_WITH_FALLBACK(ioprio_class, int, IOPRIO_N_CLASSES); - -static const char *const sigchld_code_table[] = { - [CLD_EXITED] = "exited", - [CLD_KILLED] = "killed", - [CLD_DUMPED] = "dumped", - [CLD_TRAPPED] = "trapped", - [CLD_STOPPED] = "stopped", - [CLD_CONTINUED] = "continued", -}; - -DEFINE_STRING_TABLE_LOOKUP(sigchld_code, int); - -static const char* const sched_policy_table[] = { - [SCHED_OTHER] = "other", - [SCHED_BATCH] = "batch", - [SCHED_IDLE] = "idle", - [SCHED_FIFO] = "fifo", - [SCHED_RR] = "rr", -}; - -DEFINE_STRING_TABLE_LOOKUP_WITH_FALLBACK(sched_policy, int, INT_MAX); -#endif /* NM_IGNORED */ diff --git a/shared/systemd/src/basic/process-util.h b/shared/systemd/src/basic/process-util.h deleted file mode 100644 index aab2c7a1..00000000 --- a/shared/systemd/src/basic/process-util.h +++ /dev/null @@ -1,203 +0,0 @@ -/* SPDX-License-Identifier: LGPL-2.1-or-later */ -#pragma once - -#include <errno.h> -#include <sched.h> -#include <signal.h> -#include <stdbool.h> -#include <stddef.h> -#include <stdio.h> -#include <string.h> -#include <sys/resource.h> -#include <sys/types.h> - -#include "alloc-util.h" -#include "format-util.h" -#include "ioprio.h" -#include "macro.h" -#include "time-util.h" - -#define procfs_file_alloca(pid, field) \ - ({ \ - pid_t _pid_ = (pid); \ - const char *_field_ = (field); \ - char *_r_; \ - if (_pid_ == 0) { \ - _r_ = newa(char, STRLEN("/proc/self/") + strlen(_field_) + 1); \ - strcpy(stpcpy(_r_, "/proc/self/"), _field_); \ - } else { \ - _r_ = newa(char, STRLEN("/proc/") + DECIMAL_STR_MAX(pid_t) + 1 + strlen(_field_) + 1); \ - sprintf(_r_, "/proc/" PID_FMT "/%s", _pid_, _field_); \ - } \ - (const char*) _r_; \ - }) - -typedef enum ProcessCmdlineFlags { - PROCESS_CMDLINE_COMM_FALLBACK = 1 << 0, - PROCESS_CMDLINE_USE_LOCALE = 1 << 1, -} ProcessCmdlineFlags; - -int get_process_comm(pid_t pid, char **name); -int get_process_cmdline(pid_t pid, size_t max_columns, ProcessCmdlineFlags flags, char **line); -int get_process_exe(pid_t pid, char **name); -int get_process_uid(pid_t pid, uid_t *uid); -int get_process_gid(pid_t pid, gid_t *gid); -int get_process_capeff(pid_t pid, char **capeff); -int get_process_cwd(pid_t pid, char **cwd); -int get_process_root(pid_t pid, char **root); -int get_process_environ(pid_t pid, char **environ); -int get_process_ppid(pid_t pid, pid_t *ppid); -int get_process_umask(pid_t pid, mode_t *umask); - -int wait_for_terminate(pid_t pid, siginfo_t *status); - -typedef enum WaitFlags { - WAIT_LOG_ABNORMAL = 1 << 0, - WAIT_LOG_NON_ZERO_EXIT_STATUS = 1 << 1, - - /* A shortcut for requesting the most complete logging */ - WAIT_LOG = WAIT_LOG_ABNORMAL|WAIT_LOG_NON_ZERO_EXIT_STATUS, -} WaitFlags; - -int wait_for_terminate_and_check(const char *name, pid_t pid, WaitFlags flags); -int wait_for_terminate_with_timeout(pid_t pid, usec_t timeout); - -void sigkill_wait(pid_t pid); -void sigkill_waitp(pid_t *pid); -void sigterm_wait(pid_t pid); - -int kill_and_sigcont(pid_t pid, int sig); - -int rename_process(const char name[]); -int is_kernel_thread(pid_t pid); - -int getenv_for_pid(pid_t pid, const char *field, char **_value); - -bool pid_is_alive(pid_t pid); -bool pid_is_unwaited(pid_t pid); -int pid_is_my_child(pid_t pid); -int pid_from_same_root_fs(pid_t pid); - -bool is_main_thread(void); - -_noreturn_ void freeze(void); - -bool oom_score_adjust_is_valid(int oa); - -#ifndef PERSONALITY_INVALID -/* personality(7) documents that 0xffffffffUL is used for querying the - * current personality, hence let's use that here as error - * indicator. */ -#define PERSONALITY_INVALID 0xffffffffLU -#endif - -unsigned long personality_from_string(const char *p); -const char *personality_to_string(unsigned long); - -int safe_personality(unsigned long p); -int opinionated_personality(unsigned long *ret); - -int ioprio_class_to_string_alloc(int i, char **s); -int ioprio_class_from_string(const char *s); - -const char *sigchld_code_to_string(int i) _const_; -int sigchld_code_from_string(const char *s) _pure_; - -int sched_policy_to_string_alloc(int i, char **s); -int sched_policy_from_string(const char *s); - -static inline pid_t PTR_TO_PID(const void *p) { - return (pid_t) ((uintptr_t) p); -} - -static inline void* PID_TO_PTR(pid_t pid) { - return (void*) ((uintptr_t) pid); -} - -void valgrind_summary_hack(void); - -int pid_compare_func(const pid_t *a, const pid_t *b); - -#if 0 /* NM_IGNORED */ -static inline bool nice_is_valid(int n) { - return n >= PRIO_MIN && n < PRIO_MAX; -} - -static inline bool sched_policy_is_valid(int i) { - return IN_SET(i, SCHED_OTHER, SCHED_BATCH, SCHED_IDLE, SCHED_FIFO, SCHED_RR); -} - -static inline bool sched_priority_is_valid(int i) { - return i >= 0 && i <= sched_get_priority_max(SCHED_RR); -} - -static inline bool ioprio_class_is_valid(int i) { - return IN_SET(i, IOPRIO_CLASS_NONE, IOPRIO_CLASS_RT, IOPRIO_CLASS_BE, IOPRIO_CLASS_IDLE); -} - -static inline bool ioprio_priority_is_valid(int i) { - return i >= 0 && i < IOPRIO_BE_NR; -} - -static inline bool pid_is_valid(pid_t p) { - return p > 0; -} -#endif /* NM_IGNORED */ - -int ioprio_parse_priority(const char *s, int *ret); - -pid_t getpid_cached(void); -void reset_cached_pid(void); - -int must_be_root(void); - -typedef enum ForkFlags { - FORK_RESET_SIGNALS = 1 << 0, /* Reset all signal handlers and signal mask */ - FORK_CLOSE_ALL_FDS = 1 << 1, /* Close all open file descriptors in the child, except for 0,1,2 */ - FORK_DEATHSIG = 1 << 2, /* Set PR_DEATHSIG in the child to SIGTERM */ - FORK_DEATHSIG_SIGINT = 1 << 3, /* Set PR_DEATHSIG in the child to SIGINT */ - FORK_NULL_STDIO = 1 << 4, /* Connect 0,1,2 to /dev/null */ - FORK_REOPEN_LOG = 1 << 5, /* Reopen log connection */ - FORK_LOG = 1 << 6, /* Log above LOG_DEBUG log level about failures */ - FORK_WAIT = 1 << 7, /* Wait until child exited */ - FORK_NEW_MOUNTNS = 1 << 8, /* Run child in its own mount namespace */ - FORK_MOUNTNS_SLAVE = 1 << 9, /* Make child's mount namespace MS_SLAVE */ - FORK_RLIMIT_NOFILE_SAFE = 1 << 10, /* Set RLIMIT_NOFILE soft limit to 1K for select() compat */ - FORK_STDOUT_TO_STDERR = 1 << 11, /* Make stdout a copy of stderr */ -} ForkFlags; - -int safe_fork_full(const char *name, const int except_fds[], size_t n_except_fds, ForkFlags flags, pid_t *ret_pid); - -static inline int safe_fork(const char *name, ForkFlags flags, pid_t *ret_pid) { - return safe_fork_full(name, NULL, 0, flags, ret_pid); -} - -int namespace_fork(const char *outer_name, const char *inner_name, const int except_fds[], size_t n_except_fds, ForkFlags flags, int pidns_fd, int mntns_fd, int netns_fd, int userns_fd, int root_fd, pid_t *ret_pid); - -int fork_agent(const char *name, const int except[], size_t n_except, pid_t *pid, const char *path, ...) _sentinel_; - -int set_oom_score_adjust(int value); - -/* The highest possibly (theoretic) pid_t value on this architecture. */ -#define PID_T_MAX ((pid_t) INT32_MAX) -/* The maximum number of concurrent processes Linux allows on this architecture, as well as the highest valid PID value - * the kernel will potentially assign. This reflects a value compiled into the kernel (PID_MAX_LIMIT), and sets the - * upper boundary on what may be written to the /proc/sys/kernel/pid_max sysctl (but do note that the sysctl is off by - * 1, since PID 0 can never exist and there can hence only be one process less than the limit would suggest). Since - * these values are documented in proc(5) we feel quite confident that they are stable enough for the near future at - * least to define them here too. */ -#define TASKS_MAX 4194303U - -assert_cc(TASKS_MAX <= (unsigned long) PID_T_MAX); - -/* Like TAKE_PTR() but for child PIDs, resetting them to 0 */ -#define TAKE_PID(pid) \ - ({ \ - pid_t _pid_ = (pid); \ - (pid) = 0; \ - _pid_; \ - }) - -int pidfd_get_pid(int fd, pid_t *ret); - -int setpriority_closest(int priority); diff --git a/shared/systemd/src/basic/random-util.c b/shared/systemd/src/basic/random-util.c deleted file mode 100644 index 4f67d9af..00000000 --- a/shared/systemd/src/basic/random-util.c +++ /dev/null @@ -1,507 +0,0 @@ -/* SPDX-License-Identifier: LGPL-2.1-or-later */ - -#include "nm-sd-adapt-shared.h" - -#if defined(__i386__) || defined(__x86_64__) -#include <cpuid.h> -#endif - -#include <elf.h> -#include <errno.h> -#include <fcntl.h> -#include <linux/random.h> -#include <pthread.h> -#include <stdbool.h> -#include <stdint.h> -#include <stdlib.h> -#include <string.h> -#include <sys/ioctl.h> -#include <sys/time.h> - -#if HAVE_SYS_AUXV_H -# include <sys/auxv.h> -#endif - -#include "alloc-util.h" -#include "env-util.h" -#include "errno-util.h" -#include "fd-util.h" -#include "fileio.h" -#include "io-util.h" -#include "missing_random.h" -#include "missing_syscall.h" -#include "parse-util.h" -#include "random-util.h" -#include "siphash24.h" -#include "time-util.h" - -static bool srand_called = false; - -int rdrand(unsigned long *ret) { - - /* So, you are a "security researcher", and you wonder why we bother with using raw RDRAND here, - * instead of sticking to /dev/urandom or getrandom()? - * - * Here's why: early boot. On Linux, during early boot the random pool that backs /dev/urandom and - * getrandom() is generally not initialized yet. It is very common that initialization of the random - * pool takes a longer time (up to many minutes), in particular on embedded devices that have no - * explicit hardware random generator, as well as in virtualized environments such as major cloud - * installations that do not provide virtio-rng or a similar mechanism. - * - * In such an environment using getrandom() synchronously means we'd block the entire system boot-up - * until the pool is initialized, i.e. *very* long. Using getrandom() asynchronously (GRND_NONBLOCK) - * would mean acquiring randomness during early boot would simply fail. Using /dev/urandom would mean - * generating many kmsg log messages about our use of it before the random pool is properly - * initialized. Neither of these outcomes is desirable. - * - * Thus, for very specific purposes we use RDRAND instead of either of these three options. RDRAND - * provides us quickly and relatively reliably with random values, without having to delay boot, - * without triggering warning messages in kmsg. - * - * Note that we use RDRAND only under very specific circumstances, when the requirements on the - * quality of the returned entropy permit it. Specifically, here are some cases where we *do* use - * RDRAND: - * - * • UUID generation: UUIDs are supposed to be universally unique but are not cryptographic - * key material. The quality and trust level of RDRAND should hence be OK: UUIDs should be - * generated in a way that is reliably unique, but they do not require ultimate trust into - * the entropy generator. systemd generates a number of UUIDs during early boot, including - * 'invocation IDs' for every unit spawned that identify the specific invocation of the - * service globally, and a number of others. Other alternatives for generating these UUIDs - * have been considered, but don't really work: for example, hashing uuids from a local - * system identifier combined with a counter falls flat because during early boot disk - * storage is not yet available (think: initrd) and thus a system-specific ID cannot be - * stored or retrieved yet. - * - * • Hash table seed generation: systemd uses many hash tables internally. Hash tables are - * generally assumed to have O(1) access complexity, but can deteriorate to prohibitive - * O(n) access complexity if an attacker manages to trigger a large number of hash - * collisions. Thus, systemd (as any software employing hash tables should) uses seeded - * hash functions for its hash tables, with a seed generated randomly. The hash tables - * systemd employs watch the fill level closely and reseed if necessary. This allows use of - * a low quality RNG initially, as long as it improves should a hash table be under attack: - * the attacker after all needs to trigger many collisions to exploit it for the purpose - * of DoS, but if doing so improves the seed the attack surface is reduced as the attack - * takes place. - * - * Some cases where we do NOT use RDRAND are: - * - * • Generation of cryptographic key material 🔑 - * - * • Generation of cryptographic salt values 🧂 - * - * This function returns: - * - * -EOPNOTSUPP → RDRAND is not available on this system 😔 - * -EAGAIN → The operation failed this time, but is likely to work if you try again a few - * times ♻ - * -EUCLEAN → We got some random value, but it looked strange, so we refused using it. - * This failure might or might not be temporary. 😕 - */ - -#if defined(__i386__) || defined(__x86_64__) - static int have_rdrand = -1; - unsigned long v; - uint8_t success; - - if (have_rdrand < 0) { - uint32_t eax, ebx, ecx, edx; - - /* Check if RDRAND is supported by the CPU */ - if (__get_cpuid(1, &eax, &ebx, &ecx, &edx) == 0) { - have_rdrand = false; - return -EOPNOTSUPP; - } - -/* Compat with old gcc where bit_RDRND didn't exist yet */ -#ifndef bit_RDRND -#define bit_RDRND (1U << 30) -#endif - - have_rdrand = !!(ecx & bit_RDRND); - - if (have_rdrand > 0) { - /* Allow disabling use of RDRAND with SYSTEMD_RDRAND=0 - If it is unset getenv_bool_secure will return a negative value. */ - if (getenv_bool_secure("SYSTEMD_RDRAND") == 0) { - have_rdrand = false; - return -EOPNOTSUPP; - } - } - } - - if (have_rdrand == 0) - return -EOPNOTSUPP; - - asm volatile("rdrand %0;" - "setc %1" - : "=r" (v), - "=qm" (success)); - msan_unpoison(&success, sizeof(success)); - if (!success) - return -EAGAIN; - - /* Apparently on some AMD CPUs RDRAND will sometimes (after a suspend/resume cycle?) report success - * via the carry flag but nonetheless return the same fixed value -1 in all cases. This appears to be - * a bad bug in the CPU or firmware. Let's deal with that and work-around this by explicitly checking - * for this special value (and also 0, just to be sure) and filtering it out. This is a work-around - * only however and something AMD really should fix properly. The Linux kernel should probably work - * around this issue by turning off RDRAND altogether on those CPUs. See: - * https://github.com/systemd/systemd/issues/11810 */ - if (v == 0 || v == ULONG_MAX) - return log_debug_errno(SYNTHETIC_ERRNO(EUCLEAN), - "RDRAND returned suspicious value %lx, assuming bad hardware RNG, not using value.", v); - - *ret = v; - return 0; -#else - return -EOPNOTSUPP; -#endif -} - -int genuine_random_bytes(void *p, size_t n, RandomFlags flags) { - static int have_syscall = -1; - _cleanup_close_ int fd = -1; - bool got_some = false; - int r; - - /* Gathers some high-quality randomness from the kernel (or potentially mid-quality randomness from - * the CPU if the RANDOM_ALLOW_RDRAND flag is set). This call won't block, unless the RANDOM_BLOCK - * flag is set. If RANDOM_MAY_FAIL is set, an error is returned if the random pool is not - * initialized. Otherwise it will always return some data from the kernel, regardless of whether the - * random pool is fully initialized or not. If RANDOM_EXTEND_WITH_PSEUDO is set, and some but not - * enough better quality randomness could be acquired, the rest is filled up with low quality - * randomness. - * - * Of course, when creating cryptographic key material you really shouldn't use RANDOM_ALLOW_DRDRAND - * or even RANDOM_EXTEND_WITH_PSEUDO. - * - * When generating UUIDs it's fine to use RANDOM_ALLOW_RDRAND but not OK to use - * RANDOM_EXTEND_WITH_PSEUDO. In fact RANDOM_EXTEND_WITH_PSEUDO is only really fine when invoked via - * an "all bets are off" wrapper, such as random_bytes(), see below. */ - - if (n == 0) - return 0; - - if (FLAGS_SET(flags, RANDOM_ALLOW_RDRAND)) - /* Try x86-64' RDRAND intrinsic if we have it. We only use it if high quality randomness is - * not required, as we don't trust it (who does?). Note that we only do a single iteration of - * RDRAND here, even though the Intel docs suggest calling this in a tight loop of 10 - * invocations or so. That's because we don't really care about the quality here. We - * generally prefer using RDRAND if the caller allows us to, since this way we won't upset - * the kernel's random subsystem by accessing it before the pool is initialized (after all it - * will kmsg log about every attempt to do so)..*/ - for (;;) { - unsigned long u; - size_t m; - - if (rdrand(&u) < 0) { - if (got_some && FLAGS_SET(flags, RANDOM_EXTEND_WITH_PSEUDO)) { - /* Fill in the remaining bytes using pseudo-random values */ - pseudo_random_bytes(p, n); - return 0; - } - - /* OK, this didn't work, let's go to getrandom() + /dev/urandom instead */ - break; - } - - m = MIN(sizeof(u), n); - memcpy(p, &u, m); - - p = (uint8_t*) p + m; - n -= m; - - if (n == 0) - return 0; /* Yay, success! */ - - got_some = true; - } - - /* Use the getrandom() syscall unless we know we don't have it. */ - if (have_syscall != 0 && !HAS_FEATURE_MEMORY_SANITIZER) { - - for (;;) { -#if !HAVE_GETRANDOM - /* NetworkManager Note: systemd calls the syscall directly in this case. Don't add that workaround. - * If you don't compile against a libc that provides getrandom(), you don't get it. */ - r = -1; - errno = ENOSYS; -#else - r = getrandom(p, n, - (FLAGS_SET(flags, RANDOM_BLOCK) ? 0 : GRND_NONBLOCK) | - (FLAGS_SET(flags, RANDOM_ALLOW_INSECURE) ? GRND_INSECURE : 0)); -#endif - if (r > 0) { - have_syscall = true; - - if ((size_t) r == n) - return 0; /* Yay, success! */ - - assert((size_t) r < n); - p = (uint8_t*) p + r; - n -= r; - - if (FLAGS_SET(flags, RANDOM_EXTEND_WITH_PSEUDO)) { - /* Fill in the remaining bytes using pseudo-random values */ - pseudo_random_bytes(p, n); - return 0; - } - - got_some = true; - - /* Hmm, we didn't get enough good data but the caller insists on good data? Then try again */ - if (FLAGS_SET(flags, RANDOM_BLOCK)) - continue; - - /* Fill in the rest with /dev/urandom */ - break; - - } else if (r == 0) { - have_syscall = true; - return -EIO; - - } else if (ERRNO_IS_NOT_SUPPORTED(errno)) { - /* We lack the syscall, continue with reading from /dev/urandom. */ - have_syscall = false; - break; - - } else if (errno == EAGAIN) { - /* The kernel has no entropy whatsoever. Let's remember to use the syscall - * the next time again though. - * - * If RANDOM_MAY_FAIL is set, return an error so that random_bytes() can - * produce some pseudo-random bytes instead. Otherwise, fall back to - * /dev/urandom, which we know is empty, but the kernel will produce some - * bytes for us on a best-effort basis. */ - have_syscall = true; - - if (got_some && FLAGS_SET(flags, RANDOM_EXTEND_WITH_PSEUDO)) { - /* Fill in the remaining bytes using pseudorandom values */ - pseudo_random_bytes(p, n); - return 0; - } - - if (FLAGS_SET(flags, RANDOM_MAY_FAIL)) - return -ENODATA; - - /* Use /dev/urandom instead */ - break; - - } else if (errno == EINVAL) { - - /* Most likely: unknown flag. We know that GRND_INSECURE might cause this, - * hence try without. */ - - if (FLAGS_SET(flags, RANDOM_ALLOW_INSECURE)) { - flags = flags &~ RANDOM_ALLOW_INSECURE; - continue; - } - - return -errno; - } else - return -errno; - } - } - - fd = open("/dev/urandom", O_RDONLY|O_CLOEXEC|O_NOCTTY); - if (fd < 0) - return errno == ENOENT ? -ENOSYS : -errno; - - return loop_read_exact(fd, p, n, true); -} - -static void clear_srand_initialization(void) { - srand_called = false; -} - -void initialize_srand(void) { - static bool pthread_atfork_registered = false; - unsigned x; -#if HAVE_SYS_AUXV_H - const void *auxv; -#endif - unsigned long k; - - if (srand_called) - return; - -#if HAVE_SYS_AUXV_H - /* The kernel provides us with 16 bytes of entropy in auxv, so let's try to make use of that to seed - * the pseudo-random generator. It's better than nothing... But let's first hash it to make it harder - * to recover the original value by watching any pseudo-random bits we generate. After all the - * AT_RANDOM data might be used by other stuff too (in particular: ASLR), and we probably shouldn't - * leak the seed for that. */ - - auxv = ULONG_TO_PTR(getauxval(AT_RANDOM)); - if (auxv) { - static const uint8_t auxval_hash_key[16] = { - 0x92, 0x6e, 0xfe, 0x1b, 0xcf, 0x00, 0x52, 0x9c, 0xcc, 0x42, 0xcf, 0xdc, 0x94, 0x1f, 0x81, 0x0f - }; - - x = (unsigned) siphash24(auxv, 16, auxval_hash_key); - } else -#endif - x = 0; - - x ^= (unsigned) now(CLOCK_REALTIME); - x ^= (unsigned) gettid(); - - if (rdrand(&k) >= 0) - x ^= (unsigned) k; - - srand(x); - srand_called = true; - - if (!pthread_atfork_registered) { - (void) pthread_atfork(NULL, NULL, clear_srand_initialization); - pthread_atfork_registered = true; - } -} - -/* INT_MAX gives us only 31 bits, so use 24 out of that. */ -#if RAND_MAX >= INT_MAX -assert_cc(RAND_MAX >= 16777215); -# define RAND_STEP 3 -#else -/* SHORT_INT_MAX or lower gives at most 15 bits, we just use 8 out of that. */ -assert_cc(RAND_MAX >= 255); -# define RAND_STEP 1 -#endif - -void pseudo_random_bytes(void *p, size_t n) { - uint8_t *q; - - /* This returns pseudo-random data using libc's rand() function. You probably never want to call this - * directly, because why would you use this if you can get better stuff cheaply? Use random_bytes() - * instead, see below: it will fall back to this function if there's nothing better to get, but only - * then. */ - - initialize_srand(); - - for (q = p; q < (uint8_t*) p + n; q += RAND_STEP) { - unsigned rr; - - rr = (unsigned) rand(); - -#if RAND_STEP >= 3 - if ((size_t) (q - (uint8_t*) p + 2) < n) - q[2] = rr >> 16; -#endif -#if RAND_STEP >= 2 - if ((size_t) (q - (uint8_t*) p + 1) < n) - q[1] = rr >> 8; -#endif - q[0] = rr; - } -} - -void random_bytes(void *p, size_t n) { - - /* This returns high quality randomness if we can get it cheaply. If we can't because for some reason - * it is not available we'll try some crappy fallbacks. - * - * What this function will do: - * - * • This function will preferably use the CPU's RDRAND operation, if it is available, in - * order to return "mid-quality" random values cheaply. - * - * • Use getrandom() with GRND_NONBLOCK, to return high-quality random values if they are - * cheaply available. - * - * • This function will return pseudo-random data, generated via libc rand() if nothing - * better is available. - * - * • This function will work fine in early boot - * - * • This function will always succeed - * - * What this function won't do: - * - * • This function will never fail: it will give you randomness no matter what. It might not - * be high quality, but it will return some, possibly generated via libc's rand() call. - * - * • This function will never block: if the only way to get good randomness is a blocking, - * synchronous getrandom() we'll instead provide you with pseudo-random data. - * - * This function is hence great for things like seeding hash tables, generating random numeric UNIX - * user IDs (that are checked for collisions before use) and such. - * - * This function is hence not useful for generating UUIDs or cryptographic key material. - */ - - if (genuine_random_bytes(p, n, RANDOM_EXTEND_WITH_PSEUDO|RANDOM_MAY_FAIL|RANDOM_ALLOW_RDRAND|RANDOM_ALLOW_INSECURE) >= 0) - return; - - /* If for some reason some user made /dev/urandom unavailable to us, or the kernel has no entropy, use a PRNG instead. */ - pseudo_random_bytes(p, n); -} - -#if 0 /* NM_IGNORED */ -size_t random_pool_size(void) { - _cleanup_free_ char *s = NULL; - int r; - - /* Read pool size, if possible */ - r = read_one_line_file("/proc/sys/kernel/random/poolsize", &s); - if (r < 0) - log_debug_errno(r, "Failed to read pool size from kernel: %m"); - else { - unsigned sz; - - r = safe_atou(s, &sz); - if (r < 0) - log_debug_errno(r, "Failed to parse pool size: %s", s); - else - /* poolsize is in bits on 2.6, but we want bytes */ - return CLAMP(sz / 8, RANDOM_POOL_SIZE_MIN, RANDOM_POOL_SIZE_MAX); - } - - /* Use the minimum as default, if we can't retrieve the correct value */ - return RANDOM_POOL_SIZE_MIN; -} - -int random_write_entropy(int fd, const void *seed, size_t size, bool credit) { - _cleanup_close_ int opened_fd = -1; - int r; - - assert(seed || size == 0); - - if (size == 0) - return 0; - - if (fd < 0) { - opened_fd = open("/dev/urandom", O_WRONLY|O_CLOEXEC|O_NOCTTY); - if (opened_fd < 0) - return -errno; - - fd = opened_fd; - } - - if (credit) { - _cleanup_free_ struct rand_pool_info *info = NULL; - - /* The kernel API only accepts "int" as entropy count (which is in bits), let's avoid any - * chance for confusion here. */ - if (size > INT_MAX / 8) - return -EOVERFLOW; - - info = malloc(offsetof(struct rand_pool_info, buf) + size); - if (!info) - return -ENOMEM; - - info->entropy_count = size * 8; - info->buf_size = size; - memcpy(info->buf, seed, size); - - if (ioctl(fd, RNDADDENTROPY, info) < 0) - return -errno; - } else { - r = loop_write(fd, seed, size, false); - if (r < 0) - return r; - } - - return 1; -} -#endif /* NM_IGNORED */ diff --git a/shared/systemd/src/basic/random-util.h b/shared/systemd/src/basic/random-util.h deleted file mode 100644 index f661fc09..00000000 --- a/shared/systemd/src/basic/random-util.h +++ /dev/null @@ -1,42 +0,0 @@ -/* SPDX-License-Identifier: LGPL-2.1-or-later */ -#pragma once - -#include <stdbool.h> -#include <stddef.h> -#include <stdint.h> - -typedef enum RandomFlags { - RANDOM_EXTEND_WITH_PSEUDO = 1 << 0, /* If we can't get enough genuine randomness, but some, fill up the rest with pseudo-randomness */ - RANDOM_BLOCK = 1 << 1, /* Rather block than return crap randomness (only if the kernel supports that) */ - RANDOM_MAY_FAIL = 1 << 2, /* If we can't get any randomness at all, return early with -ENODATA */ - RANDOM_ALLOW_RDRAND = 1 << 3, /* Allow usage of the CPU RNG */ - RANDOM_ALLOW_INSECURE = 1 << 4, /* Allow usage of GRND_INSECURE flag to kernel's getrandom() API */ -} RandomFlags; - -int genuine_random_bytes(void *p, size_t n, RandomFlags flags); /* returns "genuine" randomness, optionally filled up with pseudo random, if not enough is available */ -void pseudo_random_bytes(void *p, size_t n); /* returns only pseudo-randommess (but possibly seeded from something better) */ -void random_bytes(void *p, size_t n); /* returns genuine randomness if cheaply available, and pseudo randomness if not. */ - -void initialize_srand(void); - -static inline uint64_t random_u64(void) { - uint64_t u; - random_bytes(&u, sizeof(u)); - return u; -} - -static inline uint32_t random_u32(void) { - uint32_t u; - random_bytes(&u, sizeof(u)); - return u; -} - -int rdrand(unsigned long *ret); - -/* Some limits on the pool sizes when we deal with the kernel random pool */ -#define RANDOM_POOL_SIZE_MIN 512U -#define RANDOM_POOL_SIZE_MAX (10U*1024U*1024U) - -size_t random_pool_size(void); - -int random_write_entropy(int fd, const void *seed, size_t size, bool credit); diff --git a/shared/systemd/src/basic/ratelimit.c b/shared/systemd/src/basic/ratelimit.c deleted file mode 100644 index 12c8324d..00000000 --- a/shared/systemd/src/basic/ratelimit.c +++ /dev/null @@ -1,40 +0,0 @@ -/* SPDX-License-Identifier: LGPL-2.1-or-later */ - -#include "nm-sd-adapt-shared.h" - -#include <sys/time.h> - -#include "macro.h" -#include "ratelimit.h" - -/* Modelled after Linux' lib/ratelimit.c by Dave Young - * <hidave.darkstar@gmail.com>, which is licensed GPLv2. */ - -bool ratelimit_below(RateLimit *r) { - usec_t ts; - - assert(r); - - if (!ratelimit_configured(r)) - return true; - - ts = now(CLOCK_MONOTONIC); - - if (r->begin <= 0 || - ts - r->begin > r->interval) { - r->begin = ts; - - /* Reset counter */ - r->num = 0; - goto good; - } - - if (r->num < r->burst) - goto good; - - return false; - -good: - r->num++; - return true; -} diff --git a/shared/systemd/src/basic/ratelimit.h b/shared/systemd/src/basic/ratelimit.h deleted file mode 100644 index ee1d17c0..00000000 --- a/shared/systemd/src/basic/ratelimit.h +++ /dev/null @@ -1,24 +0,0 @@ -/* SPDX-License-Identifier: LGPL-2.1-or-later */ -#pragma once - -#include <stdbool.h> - -#include "time-util.h" -#include "util.h" - -typedef struct RateLimit { - usec_t interval; /* Keep those two fields first so they can be initialized easily: */ - unsigned burst; /* RateLimit rl = { INTERVAL, BURST }; */ - unsigned num; - usec_t begin; -} RateLimit; - -static inline void ratelimit_reset(RateLimit *rl) { - rl->num = rl->begin = 0; -} - -static inline bool ratelimit_configured(RateLimit *rl) { - return rl->interval > 0 && rl->burst > 0; -} - -bool ratelimit_below(RateLimit *r); diff --git a/shared/systemd/src/basic/set.h b/shared/systemd/src/basic/set.h deleted file mode 100644 index 57ff7130..00000000 --- a/shared/systemd/src/basic/set.h +++ /dev/null @@ -1,154 +0,0 @@ -/* SPDX-License-Identifier: LGPL-2.1-or-later */ -#pragma once - -#include "extract-word.h" -#include "hashmap.h" -#include "macro.h" - -#define set_free_and_replace(a, b) \ - ({ \ - set_free(a); \ - (a) = (b); \ - (b) = NULL; \ - 0; \ - }) - -Set* _set_new(const struct hash_ops *hash_ops HASHMAP_DEBUG_PARAMS); -#define set_new(ops) _set_new(ops HASHMAP_DEBUG_SRC_ARGS) - -static inline Set* set_free(Set *s) { - return (Set*) _hashmap_free(HASHMAP_BASE(s), NULL, NULL); -} - -static inline Set* set_free_free(Set *s) { - return (Set*) _hashmap_free(HASHMAP_BASE(s), free, NULL); -} - -/* no set_free_free_free */ - -#define set_copy(s) ((Set*) _hashmap_copy(HASHMAP_BASE(h) HASHMAP_DEBUG_SRC_ARGS)) - -int _set_ensure_allocated(Set **s, const struct hash_ops *hash_ops HASHMAP_DEBUG_PARAMS); -#define set_ensure_allocated(h, ops) _set_ensure_allocated(h, ops HASHMAP_DEBUG_SRC_ARGS) - -int set_put(Set *s, const void *key); -/* no set_update */ -/* no set_replace */ -static inline void *set_get(const Set *s, const void *key) { - return _hashmap_get(HASHMAP_BASE((Set *) s), key); -} -/* no set_get2 */ - -static inline bool set_contains(const Set *s, const void *key) { - return _hashmap_contains(HASHMAP_BASE((Set *) s), key); -} - -static inline void *set_remove(Set *s, const void *key) { - return _hashmap_remove(HASHMAP_BASE(s), key); -} - -/* no set_remove2 */ -/* no set_remove_value */ -int set_remove_and_put(Set *s, const void *old_key, const void *new_key); -/* no set_remove_and_replace */ -int set_merge(Set *s, Set *other); - -static inline int set_reserve(Set *h, unsigned entries_add) { - return _hashmap_reserve(HASHMAP_BASE(h), entries_add); -} - -static inline int set_move(Set *s, Set *other) { - return _hashmap_move(HASHMAP_BASE(s), HASHMAP_BASE(other)); -} - -static inline int set_move_one(Set *s, Set *other, const void *key) { - return _hashmap_move_one(HASHMAP_BASE(s), HASHMAP_BASE(other), key); -} - -static inline unsigned set_size(const Set *s) { - return _hashmap_size(HASHMAP_BASE((Set *) s)); -} - -static inline bool set_isempty(const Set *s) { - return set_size(s) == 0; -} - -static inline unsigned set_buckets(const Set *s) { - return _hashmap_buckets(HASHMAP_BASE((Set *) s)); -} - -static inline bool set_iterate(const Set *s, Iterator *i, void **value) { - return _hashmap_iterate(HASHMAP_BASE((Set*) s), i, value, NULL); -} - -static inline void set_clear(Set *s) { - _hashmap_clear(HASHMAP_BASE(s), NULL, NULL); -} - -static inline void set_clear_free(Set *s) { - _hashmap_clear(HASHMAP_BASE(s), free, NULL); -} - -/* no set_clear_free_free */ - -static inline void *set_steal_first(Set *s) { - return _hashmap_first_key_and_value(HASHMAP_BASE(s), true, NULL); -} - -#define set_clear_with_destructor(_s, _f) \ - ({ \ - void *_item; \ - while ((_item = set_steal_first(_s))) \ - _f(_item); \ - }) -#define set_free_with_destructor(_s, _f) \ - ({ \ - set_clear_with_destructor(_s, _f); \ - set_free(_s); \ - }) - -/* no set_steal_first_key */ -/* no set_first_key */ - -static inline void *set_first(const Set *s) { - return _hashmap_first_key_and_value(HASHMAP_BASE((Set *) s), false, NULL); -} - -/* no set_next */ - -static inline char **set_get_strv(Set *s) { - return _hashmap_get_strv(HASHMAP_BASE(s)); -} - -int _set_ensure_put(Set **s, const struct hash_ops *hash_ops, const void *key HASHMAP_DEBUG_PARAMS); -#define set_ensure_put(s, hash_ops, key) _set_ensure_put(s, hash_ops, key HASHMAP_DEBUG_SRC_ARGS) - -int _set_ensure_consume(Set **s, const struct hash_ops *hash_ops, void *key HASHMAP_DEBUG_PARAMS); -#define set_ensure_consume(s, hash_ops, key) _set_ensure_consume(s, hash_ops, key HASHMAP_DEBUG_SRC_ARGS) - -int set_consume(Set *s, void *value); - -int _set_put_strdup_full(Set **s, const struct hash_ops *hash_ops, const char *p HASHMAP_DEBUG_PARAMS); -#define set_put_strdup_full(s, hash_ops, p) _set_put_strdup_full(s, hash_ops, p HASHMAP_DEBUG_SRC_ARGS) -#define set_put_strdup(s, p) set_put_strdup_full(s, &string_hash_ops_free, p) -int _set_put_strdupv_full(Set **s, const struct hash_ops *hash_ops, char **l HASHMAP_DEBUG_PARAMS); -#define set_put_strdupv_full(s, hash_ops, l) _set_put_strdupv_full(s, hash_ops, l HASHMAP_DEBUG_SRC_ARGS) -#define set_put_strdupv(s, l) set_put_strdupv_full(s, &string_hash_ops_free, l) - -int set_put_strsplit(Set *s, const char *v, const char *separators, ExtractFlags flags); - -#define _SET_FOREACH(e, s, i) \ - for (Iterator i = ITERATOR_FIRST; set_iterate((s), &i, (void**)&(e)); ) -#define SET_FOREACH(e, s) \ - _SET_FOREACH(e, s, UNIQ_T(i, UNIQ)) - -#define SET_FOREACH_MOVE(e, d, s) \ - for (; ({ e = set_first(s); assert_se(!e || set_move_one(d, s, e) >= 0); e; }); ) - -DEFINE_TRIVIAL_CLEANUP_FUNC(Set*, set_free); -DEFINE_TRIVIAL_CLEANUP_FUNC(Set*, set_free_free); - -#define _cleanup_set_free_ _cleanup_(set_freep) -#define _cleanup_set_free_free_ _cleanup_(set_free_freep) - -int set_strjoin(Set *s, const char *separator, bool wrap_with_separator, char **ret); diff --git a/shared/systemd/src/basic/signal-util.c b/shared/systemd/src/basic/signal-util.c deleted file mode 100644 index 0c6f5818..00000000 --- a/shared/systemd/src/basic/signal-util.c +++ /dev/null @@ -1,299 +0,0 @@ -/* SPDX-License-Identifier: LGPL-2.1-or-later */ - -#include "nm-sd-adapt-shared.h" - -#include <errno.h> -#include <stdarg.h> - -#include "macro.h" -#include "parse-util.h" -#include "signal-util.h" -#include "stdio-util.h" -#include "string-table.h" -#include "string-util.h" - -#if 0 /* NM_IGNORED */ -int reset_all_signal_handlers(void) { - static const struct sigaction sa = { - .sa_handler = SIG_DFL, - .sa_flags = SA_RESTART, - }; - int sig, r = 0; - - for (sig = 1; sig < _NSIG; sig++) { - - /* These two cannot be caught... */ - if (IN_SET(sig, SIGKILL, SIGSTOP)) - continue; - - /* On Linux the first two RT signals are reserved by - * glibc, and sigaction() will return EINVAL for them. */ - if (sigaction(sig, &sa, NULL) < 0) - if (errno != EINVAL && r >= 0) - r = -errno; - } - - return r; -} - -int reset_signal_mask(void) { - sigset_t ss; - - if (sigemptyset(&ss) < 0) - return -errno; - - if (sigprocmask(SIG_SETMASK, &ss, NULL) < 0) - return -errno; - - return 0; -} - -static int sigaction_many_ap(const struct sigaction *sa, int sig, va_list ap) { - int r = 0; - - /* negative signal ends the list. 0 signal is skipped. */ - for (; sig >= 0; sig = va_arg(ap, int)) { - - if (sig == 0) - continue; - - if (sigaction(sig, sa, NULL) < 0) { - if (r >= 0) - r = -errno; - } - } - - return r; -} - -int sigaction_many(const struct sigaction *sa, ...) { - va_list ap; - int r; - - va_start(ap, sa); - r = sigaction_many_ap(sa, 0, ap); - va_end(ap); - - return r; -} - -int ignore_signals(int sig, ...) { - - static const struct sigaction sa = { - .sa_handler = SIG_IGN, - .sa_flags = SA_RESTART, - }; - - va_list ap; - int r; - - va_start(ap, sig); - r = sigaction_many_ap(&sa, sig, ap); - va_end(ap); - - return r; -} - -int default_signals(int sig, ...) { - - static const struct sigaction sa = { - .sa_handler = SIG_DFL, - .sa_flags = SA_RESTART, - }; - - va_list ap; - int r; - - va_start(ap, sig); - r = sigaction_many_ap(&sa, sig, ap); - va_end(ap); - - return r; -} - -static int sigset_add_many_ap(sigset_t *ss, va_list ap) { - int sig, r = 0; - - assert(ss); - - while ((sig = va_arg(ap, int)) >= 0) { - - if (sig == 0) - continue; - - if (sigaddset(ss, sig) < 0) { - if (r >= 0) - r = -errno; - } - } - - return r; -} - -int sigset_add_many(sigset_t *ss, ...) { - va_list ap; - int r; - - va_start(ap, ss); - r = sigset_add_many_ap(ss, ap); - va_end(ap); - - return r; -} - -int sigprocmask_many(int how, sigset_t *old, ...) { - va_list ap; - sigset_t ss; - int r; - - if (sigemptyset(&ss) < 0) - return -errno; - - va_start(ap, old); - r = sigset_add_many_ap(&ss, ap); - va_end(ap); - - if (r < 0) - return r; - - if (sigprocmask(how, &ss, old) < 0) - return -errno; - - return 0; -} - -static const char *const __signal_table[] = { - [SIGHUP] = "HUP", - [SIGINT] = "INT", - [SIGQUIT] = "QUIT", - [SIGILL] = "ILL", - [SIGTRAP] = "TRAP", - [SIGABRT] = "ABRT", - [SIGBUS] = "BUS", - [SIGFPE] = "FPE", - [SIGKILL] = "KILL", - [SIGUSR1] = "USR1", - [SIGSEGV] = "SEGV", - [SIGUSR2] = "USR2", - [SIGPIPE] = "PIPE", - [SIGALRM] = "ALRM", - [SIGTERM] = "TERM", -#ifdef SIGSTKFLT - [SIGSTKFLT] = "STKFLT", /* Linux on SPARC doesn't know SIGSTKFLT */ -#endif - [SIGCHLD] = "CHLD", - [SIGCONT] = "CONT", - [SIGSTOP] = "STOP", - [SIGTSTP] = "TSTP", - [SIGTTIN] = "TTIN", - [SIGTTOU] = "TTOU", - [SIGURG] = "URG", - [SIGXCPU] = "XCPU", - [SIGXFSZ] = "XFSZ", - [SIGVTALRM] = "VTALRM", - [SIGPROF] = "PROF", - [SIGWINCH] = "WINCH", - [SIGIO] = "IO", - [SIGPWR] = "PWR", - [SIGSYS] = "SYS" -}; - -DEFINE_PRIVATE_STRING_TABLE_LOOKUP(__signal, int); - -const char *signal_to_string(int signo) { - static thread_local char buf[STRLEN("RTMIN+") + DECIMAL_STR_MAX(int) + 1]; - const char *name; - - name = __signal_to_string(signo); - if (name) - return name; - - if (signo >= SIGRTMIN && signo <= SIGRTMAX) - xsprintf(buf, "RTMIN+%d", signo - SIGRTMIN); - else - xsprintf(buf, "%d", signo); - - return buf; -} - -int signal_from_string(const char *s) { - const char *p; - int signo, r; - - /* Check that the input is a signal number. */ - if (safe_atoi(s, &signo) >= 0) { - if (SIGNAL_VALID(signo)) - return signo; - else - return -ERANGE; - } - - /* Drop "SIG" prefix. */ - if (startswith(s, "SIG")) - s += 3; - - /* Check that the input is a signal name. */ - signo = __signal_from_string(s); - if (signo > 0) - return signo; - - /* Check that the input is RTMIN or - * RTMIN+n (0 <= n <= SIGRTMAX-SIGRTMIN). */ - p = startswith(s, "RTMIN"); - if (p) { - if (*p == '\0') - return SIGRTMIN; - if (*p != '+') - return -EINVAL; - - r = safe_atoi(p, &signo); - if (r < 0) - return r; - - if (signo < 0 || signo > SIGRTMAX - SIGRTMIN) - return -ERANGE; - - return signo + SIGRTMIN; - } - - /* Check that the input is RTMAX or - * RTMAX-n (0 <= n <= SIGRTMAX-SIGRTMIN). */ - p = startswith(s, "RTMAX"); - if (p) { - if (*p == '\0') - return SIGRTMAX; - if (*p != '-') - return -EINVAL; - - r = safe_atoi(p, &signo); - if (r < 0) - return r; - - if (signo > 0 || signo < SIGRTMIN - SIGRTMAX) - return -ERANGE; - - return signo + SIGRTMAX; - } - - return -EINVAL; -} - -void nop_signal_handler(int sig) { - /* nothing here */ -} -#endif /* NM_IGNORED */ - -int signal_is_blocked(int sig) { - sigset_t ss; - int r; - - r = pthread_sigmask(SIG_SETMASK, NULL, &ss); - if (r != 0) - return -r; - - r = sigismember(&ss, sig); - if (r < 0) - return -errno; - - return r; -} diff --git a/shared/systemd/src/basic/signal-util.h b/shared/systemd/src/basic/signal-util.h deleted file mode 100644 index bdd39d42..00000000 --- a/shared/systemd/src/basic/signal-util.h +++ /dev/null @@ -1,45 +0,0 @@ -/* SPDX-License-Identifier: LGPL-2.1-or-later */ -#pragma once - -#include <signal.h> - -#include "macro.h" - -int reset_all_signal_handlers(void); -int reset_signal_mask(void); - -int ignore_signals(int sig, ...); -int default_signals(int sig, ...); -int sigaction_many(const struct sigaction *sa, ...); - -int sigset_add_many(sigset_t *ss, ...); -int sigprocmask_many(int how, sigset_t *old, ...); - -const char *signal_to_string(int i) _const_; -int signal_from_string(const char *s) _pure_; - -void nop_signal_handler(int sig); - -static inline void block_signals_reset(sigset_t *ss) { - assert_se(sigprocmask(SIG_SETMASK, ss, NULL) >= 0); -} - -#define BLOCK_SIGNALS(...) \ - _cleanup_(block_signals_reset) _unused_ sigset_t _saved_sigset = ({ \ - sigset_t _t; \ - assert_se(sigprocmask_many(SIG_BLOCK, &_t, __VA_ARGS__, -1) >= 0); \ - _t; \ - }) - -static inline bool SIGNAL_VALID(int signo) { - return signo > 0 && signo < _NSIG; -} - -static inline const char* signal_to_string_with_check(int n) { - if (!SIGNAL_VALID(n)) - return NULL; - - return signal_to_string(n); -} - -int signal_is_blocked(int sig); diff --git a/shared/systemd/src/basic/siphash24.h b/shared/systemd/src/basic/siphash24.h deleted file mode 100644 index e46f3cc5..00000000 --- a/shared/systemd/src/basic/siphash24.h +++ /dev/null @@ -1,83 +0,0 @@ -/* SPDX-License-Identifier: CC0-1.0 */ - -#pragma once - -#include <inttypes.h> -#include <stddef.h> -#include <stdint.h> -#include <sys/types.h> - -#include "string-util.h" -#include "time-util.h" - -#if 0 /* NM_IGNORED */ -struct siphash { - uint64_t v0; - uint64_t v1; - uint64_t v2; - uint64_t v3; - uint64_t padding; - size_t inlen; -}; -#else /* NM_IGNORED */ -struct siphash { - CSipHash _csiphash; -}; - -static inline void -siphash24_init (struct siphash *state, const uint8_t k[16]) -{ - c_siphash_init ((CSipHash *) state, k); -} - -static inline void -siphash24_compress (const void *in, size_t inlen, struct siphash *state) -{ - c_siphash_append ((CSipHash *) state, in, inlen); -} - -static inline uint64_t -siphash24_finalize (struct siphash *state) -{ - return c_siphash_finalize ((CSipHash *) state); -} - -static inline uint64_t -siphash24 (const void *in, size_t inlen, const uint8_t k[16]) -{ - return c_siphash_hash (k, in, inlen); -} -#endif /* NM_IGNORED */ - -void siphash24_init(struct siphash *state, const uint8_t k[static 16]); -void siphash24_compress(const void *in, size_t inlen, struct siphash *state); -#define siphash24_compress_byte(byte, state) siphash24_compress((const uint8_t[]) { (byte) }, 1, (state)) - -static inline void siphash24_compress_boolean(bool in, struct siphash *state) { - uint8_t i = in; - - siphash24_compress(&i, sizeof i, state); -} - -static inline void siphash24_compress_usec_t(usec_t in, struct siphash *state) { - siphash24_compress(&in, sizeof in, state); -} - -static inline void siphash24_compress_safe(const void *in, size_t inlen, struct siphash *state) { - if (inlen == 0) - return; - - siphash24_compress(in, inlen, state); -} - -static inline void siphash24_compress_string(const char *in, struct siphash *state) { - siphash24_compress_safe(in, strlen_ptr(in), state); -} - -uint64_t siphash24_finalize(struct siphash *state); - -uint64_t siphash24(const void *in, size_t inlen, const uint8_t k[static 16]); - -static inline uint64_t siphash24_string(const char *s, const uint8_t k[static 16]) { - return siphash24(s, strlen(s) + 1, k); -} diff --git a/shared/systemd/src/basic/socket-util.c b/shared/systemd/src/basic/socket-util.c deleted file mode 100644 index e224091d..00000000 --- a/shared/systemd/src/basic/socket-util.c +++ /dev/null @@ -1,1360 +0,0 @@ -/* SPDX-License-Identifier: LGPL-2.1-or-later */ - -#include "nm-sd-adapt-shared.h" - -#include <arpa/inet.h> -#include <errno.h> -#include <limits.h> -#include <net/if.h> -#include <netdb.h> -#include <netinet/ip.h> -#include <poll.h> -#include <stddef.h> -#include <stdint.h> -#include <stdio.h> -#include <stdlib.h> -#include <sys/ioctl.h> -#include <unistd.h> -#if 0 /* NM_IGNORED */ -#include <linux/if.h> -#endif /* NM_IGNORED */ - -#include "alloc-util.h" -#include "errno-util.h" -#include "escape.h" -#include "fd-util.h" -#include "fileio.h" -#include "format-util.h" -#include "io-util.h" -#include "log.h" -#include "memory-util.h" -#include "parse-util.h" -#include "path-util.h" -#include "process-util.h" -#include "socket-util.h" -#include "string-table.h" -#include "string-util.h" -#include "strv.h" -#include "user-util.h" -#include "utf8.h" - -#if 0 /* NM_IGNORED */ -#if ENABLE_IDN -# define IDN_FLAGS NI_IDN -#else -# define IDN_FLAGS 0 -#endif - -static const char* const socket_address_type_table[] = { - [SOCK_STREAM] = "Stream", - [SOCK_DGRAM] = "Datagram", - [SOCK_RAW] = "Raw", - [SOCK_RDM] = "ReliableDatagram", - [SOCK_SEQPACKET] = "SequentialPacket", - [SOCK_DCCP] = "DatagramCongestionControl", -}; - -DEFINE_STRING_TABLE_LOOKUP(socket_address_type, int); - -int socket_address_verify(const SocketAddress *a, bool strict) { - assert(a); - - /* With 'strict' we enforce additional sanity constraints which are not set by the standard, - * but should only apply to sockets we create ourselves. */ - - switch (socket_address_family(a)) { - - case AF_INET: - if (a->size != sizeof(struct sockaddr_in)) - return -EINVAL; - - if (a->sockaddr.in.sin_port == 0) - return -EINVAL; - - if (!IN_SET(a->type, 0, SOCK_STREAM, SOCK_DGRAM)) - return -EINVAL; - - return 0; - - case AF_INET6: - if (a->size != sizeof(struct sockaddr_in6)) - return -EINVAL; - - if (a->sockaddr.in6.sin6_port == 0) - return -EINVAL; - - if (!IN_SET(a->type, 0, SOCK_STREAM, SOCK_DGRAM)) - return -EINVAL; - - return 0; - - case AF_UNIX: - if (a->size < offsetof(struct sockaddr_un, sun_path)) - return -EINVAL; - if (a->size > sizeof(struct sockaddr_un) + !strict) - /* If !strict, allow one extra byte, since getsockname() on Linux will append - * a NUL byte if we have path sockets that are above sun_path's full size. */ - return -EINVAL; - - if (a->size > offsetof(struct sockaddr_un, sun_path) && - a->sockaddr.un.sun_path[0] != 0 && - strict) { - /* Only validate file system sockets here, and only in strict mode */ - const char *e; - - e = memchr(a->sockaddr.un.sun_path, 0, sizeof(a->sockaddr.un.sun_path)); - if (e) { - /* If there's an embedded NUL byte, make sure the size of the socket address matches it */ - if (a->size != offsetof(struct sockaddr_un, sun_path) + (e - a->sockaddr.un.sun_path) + 1) - return -EINVAL; - } else { - /* If there's no embedded NUL byte, then the size needs to match the whole - * structure or the structure with one extra NUL byte suffixed. (Yeah, Linux is awful, - * and considers both equivalent: getsockname() even extends sockaddr_un beyond its - * size if the path is non NUL terminated.)*/ - if (!IN_SET(a->size, sizeof(a->sockaddr.un.sun_path), sizeof(a->sockaddr.un.sun_path)+1)) - return -EINVAL; - } - } - - if (!IN_SET(a->type, 0, SOCK_STREAM, SOCK_DGRAM, SOCK_SEQPACKET)) - return -EINVAL; - - return 0; - - case AF_NETLINK: - - if (a->size != sizeof(struct sockaddr_nl)) - return -EINVAL; - - if (!IN_SET(a->type, 0, SOCK_RAW, SOCK_DGRAM)) - return -EINVAL; - - return 0; - - case AF_VSOCK: - if (a->size != sizeof(struct sockaddr_vm)) - return -EINVAL; - - if (!IN_SET(a->type, 0, SOCK_STREAM, SOCK_DGRAM)) - return -EINVAL; - - return 0; - - default: - return -EAFNOSUPPORT; - } -} - -int socket_address_print(const SocketAddress *a, char **ret) { - int r; - - assert(a); - assert(ret); - - r = socket_address_verify(a, false); /* We do non-strict validation, because we want to be - * able to pretty-print any socket the kernel considers - * valid. We still need to do validation to know if we - * can meaningfully print the address. */ - if (r < 0) - return r; - - if (socket_address_family(a) == AF_NETLINK) { - _cleanup_free_ char *sfamily = NULL; - - r = netlink_family_to_string_alloc(a->protocol, &sfamily); - if (r < 0) - return r; - - r = asprintf(ret, "%s %u", sfamily, a->sockaddr.nl.nl_groups); - if (r < 0) - return -ENOMEM; - - return 0; - } - - return sockaddr_pretty(&a->sockaddr.sa, a->size, false, true, ret); -} - -bool socket_address_can_accept(const SocketAddress *a) { - assert(a); - - return - IN_SET(a->type, SOCK_STREAM, SOCK_SEQPACKET); -} - -bool socket_address_equal(const SocketAddress *a, const SocketAddress *b) { - assert(a); - assert(b); - - /* Invalid addresses are unequal to all */ - if (socket_address_verify(a, false) < 0 || - socket_address_verify(b, false) < 0) - return false; - - if (a->type != b->type) - return false; - - if (socket_address_family(a) != socket_address_family(b)) - return false; - - switch (socket_address_family(a)) { - - case AF_INET: - if (a->sockaddr.in.sin_addr.s_addr != b->sockaddr.in.sin_addr.s_addr) - return false; - - if (a->sockaddr.in.sin_port != b->sockaddr.in.sin_port) - return false; - - break; - - case AF_INET6: - if (memcmp(&a->sockaddr.in6.sin6_addr, &b->sockaddr.in6.sin6_addr, sizeof(a->sockaddr.in6.sin6_addr)) != 0) - return false; - - if (a->sockaddr.in6.sin6_port != b->sockaddr.in6.sin6_port) - return false; - - break; - - case AF_UNIX: - if (a->size <= offsetof(struct sockaddr_un, sun_path) || - b->size <= offsetof(struct sockaddr_un, sun_path)) - return false; - - if ((a->sockaddr.un.sun_path[0] == 0) != (b->sockaddr.un.sun_path[0] == 0)) - return false; - - if (a->sockaddr.un.sun_path[0]) { - if (!path_equal_or_files_same(a->sockaddr.un.sun_path, b->sockaddr.un.sun_path, 0)) - return false; - } else { - if (a->size != b->size) - return false; - - if (memcmp(a->sockaddr.un.sun_path, b->sockaddr.un.sun_path, a->size) != 0) - return false; - } - - break; - - case AF_NETLINK: - if (a->protocol != b->protocol) - return false; - - if (a->sockaddr.nl.nl_groups != b->sockaddr.nl.nl_groups) - return false; - - break; - - case AF_VSOCK: - if (a->sockaddr.vm.svm_cid != b->sockaddr.vm.svm_cid) - return false; - - if (a->sockaddr.vm.svm_port != b->sockaddr.vm.svm_port) - return false; - - break; - - default: - /* Cannot compare, so we assume the addresses are different */ - return false; - } - - return true; -} - -const char* socket_address_get_path(const SocketAddress *a) { - assert(a); - - if (socket_address_family(a) != AF_UNIX) - return NULL; - - if (a->sockaddr.un.sun_path[0] == 0) - return NULL; - - /* Note that this is only safe because we know that there's an extra NUL byte after the sockaddr_un - * structure. On Linux AF_UNIX file system socket addresses don't have to be NUL terminated if they take up the - * full sun_path space. */ - assert_cc(sizeof(union sockaddr_union) >= sizeof(struct sockaddr_un)+1); - return a->sockaddr.un.sun_path; -} - -bool socket_ipv6_is_supported(void) { - if (access("/proc/net/if_inet6", F_OK) != 0) - return false; - - return true; -} - -bool socket_address_matches_fd(const SocketAddress *a, int fd) { - SocketAddress b; - socklen_t solen; - - assert(a); - assert(fd >= 0); - - b.size = sizeof(b.sockaddr); - if (getsockname(fd, &b.sockaddr.sa, &b.size) < 0) - return false; - - if (b.sockaddr.sa.sa_family != a->sockaddr.sa.sa_family) - return false; - - solen = sizeof(b.type); - if (getsockopt(fd, SOL_SOCKET, SO_TYPE, &b.type, &solen) < 0) - return false; - - if (b.type != a->type) - return false; - - if (a->protocol != 0) { - solen = sizeof(b.protocol); - if (getsockopt(fd, SOL_SOCKET, SO_PROTOCOL, &b.protocol, &solen) < 0) - return false; - - if (b.protocol != a->protocol) - return false; - } - - return socket_address_equal(a, &b); -} - -int sockaddr_port(const struct sockaddr *_sa, unsigned *ret_port) { - const union sockaddr_union *sa = (const union sockaddr_union*) _sa; - - /* Note, this returns the port as 'unsigned' rather than 'uint16_t', as AF_VSOCK knows larger ports */ - - assert(sa); - - switch (sa->sa.sa_family) { - - case AF_INET: - *ret_port = be16toh(sa->in.sin_port); - return 0; - - case AF_INET6: - *ret_port = be16toh(sa->in6.sin6_port); - return 0; - - case AF_VSOCK: - *ret_port = sa->vm.svm_port; - return 0; - - default: - return -EAFNOSUPPORT; - } -} - -const union in_addr_union *sockaddr_in_addr(const struct sockaddr *_sa) { - const union sockaddr_union *sa = (const union sockaddr_union*) _sa; - - if (!sa) - return NULL; - - switch (sa->sa.sa_family) { - - case AF_INET: - return (const union in_addr_union*) &sa->in.sin_addr; - - case AF_INET6: - return (const union in_addr_union*) &sa->in6.sin6_addr; - - default: - return NULL; - } -} - -int sockaddr_pretty( - const struct sockaddr *_sa, - socklen_t salen, - bool translate_ipv6, - bool include_port, - char **ret) { - - union sockaddr_union *sa = (union sockaddr_union*) _sa; - char *p; - int r; - - assert(sa); - assert(salen >= sizeof(sa->sa.sa_family)); - - switch (sa->sa.sa_family) { - - case AF_INET: { - uint32_t a; - - a = be32toh(sa->in.sin_addr.s_addr); - - if (include_port) - r = asprintf(&p, - "%u.%u.%u.%u:%u", - a >> 24, (a >> 16) & 0xFF, (a >> 8) & 0xFF, a & 0xFF, - be16toh(sa->in.sin_port)); - else - r = asprintf(&p, - "%u.%u.%u.%u", - a >> 24, (a >> 16) & 0xFF, (a >> 8) & 0xFF, a & 0xFF); - if (r < 0) - return -ENOMEM; - break; - } - - case AF_INET6: { - static const unsigned char ipv4_prefix[] = { - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xFF, 0xFF - }; - - if (translate_ipv6 && - memcmp(&sa->in6.sin6_addr, ipv4_prefix, sizeof(ipv4_prefix)) == 0) { - const uint8_t *a = sa->in6.sin6_addr.s6_addr+12; - if (include_port) - r = asprintf(&p, - "%u.%u.%u.%u:%u", - a[0], a[1], a[2], a[3], - be16toh(sa->in6.sin6_port)); - else - r = asprintf(&p, - "%u.%u.%u.%u", - a[0], a[1], a[2], a[3]); - if (r < 0) - return -ENOMEM; - } else { - char a[INET6_ADDRSTRLEN], ifname[IF_NAMESIZE + 1]; - - inet_ntop(AF_INET6, &sa->in6.sin6_addr, a, sizeof(a)); - if (sa->in6.sin6_scope_id != 0) - format_ifname_full(sa->in6.sin6_scope_id, ifname, FORMAT_IFNAME_IFINDEX); - - if (include_port) { - r = asprintf(&p, - "[%s]:%u%s%s", - a, - be16toh(sa->in6.sin6_port), - sa->in6.sin6_scope_id != 0 ? "%" : "", - sa->in6.sin6_scope_id != 0 ? ifname : ""); - if (r < 0) - return -ENOMEM; - } else { - p = sa->in6.sin6_scope_id != 0 ? strjoin(a, "%", ifname) : strdup(a); - if (!p) - return -ENOMEM; - } - } - - break; - } - - case AF_UNIX: - if (salen <= offsetof(struct sockaddr_un, sun_path) || - (sa->un.sun_path[0] == 0 && salen == offsetof(struct sockaddr_un, sun_path) + 1)) - /* The name must have at least one character (and the leading NUL does not count) */ - p = strdup("<unnamed>"); - else { - /* Note that we calculate the path pointer here through the .un_buffer[] field, in order to - * outtrick bounds checking tools such as ubsan, which are too smart for their own good: on - * Linux the kernel may return sun_path[] data one byte longer than the declared size of the - * field. */ - char *path = (char*) sa->un_buffer + offsetof(struct sockaddr_un, sun_path); - size_t path_len = salen - offsetof(struct sockaddr_un, sun_path); - - if (path[0] == 0) { - /* Abstract socket. When parsing address information from, we - * explicitly reject overly long paths and paths with embedded NULs. - * But we might get such a socket from the outside. Let's return - * something meaningful and printable in this case. */ - - _cleanup_free_ char *e = NULL; - - e = cescape_length(path + 1, path_len - 1); - if (!e) - return -ENOMEM; - - p = strjoin("@", e); - } else { - if (path[path_len - 1] == '\0') - /* We expect a terminating NUL and don't print it */ - path_len --; - - p = cescape_length(path, path_len); - } - } - if (!p) - return -ENOMEM; - - break; - - case AF_VSOCK: - if (include_port) { - if (sa->vm.svm_cid == VMADDR_CID_ANY) - r = asprintf(&p, "vsock::%u", sa->vm.svm_port); - else - r = asprintf(&p, "vsock:%u:%u", sa->vm.svm_cid, sa->vm.svm_port); - } else - r = asprintf(&p, "vsock:%u", sa->vm.svm_cid); - if (r < 0) - return -ENOMEM; - break; - - default: - return -EOPNOTSUPP; - } - - *ret = p; - return 0; -} - -int getpeername_pretty(int fd, bool include_port, char **ret) { - union sockaddr_union sa; - socklen_t salen = sizeof(sa); - int r; - - assert(fd >= 0); - assert(ret); - - if (getpeername(fd, &sa.sa, &salen) < 0) - return -errno; - - if (sa.sa.sa_family == AF_UNIX) { - struct ucred ucred = {}; - - /* UNIX connection sockets are anonymous, so let's use - * PID/UID as pretty credentials instead */ - - r = getpeercred(fd, &ucred); - if (r < 0) - return r; - - if (asprintf(ret, "PID "PID_FMT"/UID "UID_FMT, ucred.pid, ucred.uid) < 0) - return -ENOMEM; - - return 0; - } - - /* For remote sockets we translate IPv6 addresses back to IPv4 - * if applicable, since that's nicer. */ - - return sockaddr_pretty(&sa.sa, salen, true, include_port, ret); -} - -int getsockname_pretty(int fd, char **ret) { - union sockaddr_union sa; - socklen_t salen = sizeof(sa); - - assert(fd >= 0); - assert(ret); - - if (getsockname(fd, &sa.sa, &salen) < 0) - return -errno; - - /* For local sockets we do not translate IPv6 addresses back - * to IPv6 if applicable, since this is usually used for - * listening sockets where the difference between IPv4 and - * IPv6 matters. */ - - return sockaddr_pretty(&sa.sa, salen, false, true, ret); -} - -int socknameinfo_pretty(union sockaddr_union *sa, socklen_t salen, char **_ret) { - int r; - char host[NI_MAXHOST], *ret; - - assert(_ret); - - r = getnameinfo(&sa->sa, salen, host, sizeof(host), NULL, 0, IDN_FLAGS); - if (r != 0) { - int saved_errno = errno; - - r = sockaddr_pretty(&sa->sa, salen, true, true, &ret); - if (r < 0) - return r; - - log_debug_errno(saved_errno, "getnameinfo(%s) failed: %m", ret); - } else { - ret = strdup(host); - if (!ret) - return -ENOMEM; - } - - *_ret = ret; - return 0; -} - -static const char* const netlink_family_table[] = { - [NETLINK_ROUTE] = "route", - [NETLINK_FIREWALL] = "firewall", - [NETLINK_INET_DIAG] = "inet-diag", - [NETLINK_NFLOG] = "nflog", - [NETLINK_XFRM] = "xfrm", - [NETLINK_SELINUX] = "selinux", - [NETLINK_ISCSI] = "iscsi", - [NETLINK_AUDIT] = "audit", - [NETLINK_FIB_LOOKUP] = "fib-lookup", - [NETLINK_CONNECTOR] = "connector", - [NETLINK_NETFILTER] = "netfilter", - [NETLINK_IP6_FW] = "ip6-fw", - [NETLINK_DNRTMSG] = "dnrtmsg", - [NETLINK_KOBJECT_UEVENT] = "kobject-uevent", - [NETLINK_GENERIC] = "generic", - [NETLINK_SCSITRANSPORT] = "scsitransport", - [NETLINK_ECRYPTFS] = "ecryptfs", - [NETLINK_RDMA] = "rdma", -}; - -DEFINE_STRING_TABLE_LOOKUP_WITH_FALLBACK(netlink_family, int, INT_MAX); - -static const char* const socket_address_bind_ipv6_only_table[_SOCKET_ADDRESS_BIND_IPV6_ONLY_MAX] = { - [SOCKET_ADDRESS_DEFAULT] = "default", - [SOCKET_ADDRESS_BOTH] = "both", - [SOCKET_ADDRESS_IPV6_ONLY] = "ipv6-only" -}; - -DEFINE_STRING_TABLE_LOOKUP(socket_address_bind_ipv6_only, SocketAddressBindIPv6Only); - -SocketAddressBindIPv6Only socket_address_bind_ipv6_only_or_bool_from_string(const char *n) { - int r; - - r = parse_boolean(n); - if (r > 0) - return SOCKET_ADDRESS_IPV6_ONLY; - if (r == 0) - return SOCKET_ADDRESS_BOTH; - - return socket_address_bind_ipv6_only_from_string(n); -} - -bool sockaddr_equal(const union sockaddr_union *a, const union sockaddr_union *b) { - assert(a); - assert(b); - - if (a->sa.sa_family != b->sa.sa_family) - return false; - - if (a->sa.sa_family == AF_INET) - return a->in.sin_addr.s_addr == b->in.sin_addr.s_addr; - - if (a->sa.sa_family == AF_INET6) - return memcmp(&a->in6.sin6_addr, &b->in6.sin6_addr, sizeof(a->in6.sin6_addr)) == 0; - - if (a->sa.sa_family == AF_VSOCK) - return a->vm.svm_cid == b->vm.svm_cid; - - return false; -} - -int fd_set_sndbuf(int fd, size_t n, bool increase) { - int r, value; - socklen_t l = sizeof(value); - - if (n > INT_MAX) - return -ERANGE; - - r = getsockopt(fd, SOL_SOCKET, SO_SNDBUF, &value, &l); - if (r >= 0 && l == sizeof(value) && increase ? (size_t) value >= n*2 : (size_t) value == n*2) - return 0; - - /* First, try to set the buffer size with SO_SNDBUF. */ - r = setsockopt_int(fd, SOL_SOCKET, SO_SNDBUF, n); - if (r < 0) - return r; - - /* SO_SNDBUF above may set to the kernel limit, instead of the requested size. - * So, we need to check the actual buffer size here. */ - l = sizeof(value); - r = getsockopt(fd, SOL_SOCKET, SO_SNDBUF, &value, &l); - if (r >= 0 && l == sizeof(value) && increase ? (size_t) value >= n*2 : (size_t) value == n*2) - return 1; - - /* If we have the privileges we will ignore the kernel limit. */ - r = setsockopt_int(fd, SOL_SOCKET, SO_SNDBUFFORCE, n); - if (r < 0) - return r; - - return 1; -} - -int fd_set_rcvbuf(int fd, size_t n, bool increase) { - int r, value; - socklen_t l = sizeof(value); - - if (n > INT_MAX) - return -ERANGE; - - r = getsockopt(fd, SOL_SOCKET, SO_RCVBUF, &value, &l); - if (r >= 0 && l == sizeof(value) && increase ? (size_t) value >= n*2 : (size_t) value == n*2) - return 0; - - /* First, try to set the buffer size with SO_RCVBUF. */ - r = setsockopt_int(fd, SOL_SOCKET, SO_RCVBUF, n); - if (r < 0) - return r; - - /* SO_RCVBUF above may set to the kernel limit, instead of the requested size. - * So, we need to check the actual buffer size here. */ - l = sizeof(value); - r = getsockopt(fd, SOL_SOCKET, SO_RCVBUF, &value, &l); - if (r >= 0 && l == sizeof(value) && increase ? (size_t) value >= n*2 : (size_t) value == n*2) - return 1; - - /* If we have the privileges we will ignore the kernel limit. */ - r = setsockopt_int(fd, SOL_SOCKET, SO_RCVBUFFORCE, n); - if (r < 0) - return r; - - return 1; -} - -static const char* const ip_tos_table[] = { - [IPTOS_LOWDELAY] = "low-delay", - [IPTOS_THROUGHPUT] = "throughput", - [IPTOS_RELIABILITY] = "reliability", - [IPTOS_LOWCOST] = "low-cost", -}; - -DEFINE_STRING_TABLE_LOOKUP_WITH_FALLBACK(ip_tos, int, 0xff); - -bool ifname_valid_full(const char *p, IfnameValidFlags flags) { - bool numeric = true; - - /* Checks whether a network interface name is valid. This is inspired by dev_valid_name() in the kernel sources - * but slightly stricter, as we only allow non-control, non-space ASCII characters in the interface name. We - * also don't permit names that only container numbers, to avoid confusion with numeric interface indexes. */ - - assert(!(flags & ~_IFNAME_VALID_ALL)); - - if (isempty(p)) - return false; - - if (flags & IFNAME_VALID_ALTERNATIVE) { - if (strlen(p) >= ALTIFNAMSIZ) - return false; - } else { - if (strlen(p) >= IFNAMSIZ) - return false; - } - - if (dot_or_dot_dot(p)) - return false; - - for (const char *t = p; *t; t++) { - if ((unsigned char) *t >= 127U) - return false; - - if ((unsigned char) *t <= 32U) - return false; - - if (IN_SET(*t, ':', '/')) - return false; - - numeric = numeric && (*t >= '0' && *t <= '9'); - } - - if (numeric) { - if (!(flags & IFNAME_VALID_NUMERIC)) - return false; - - /* Verify that the number is well-formatted and in range. */ - if (parse_ifindex(p) < 0) - return false; - } - - return true; -} - -bool address_label_valid(const char *p) { - - if (isempty(p)) - return false; - - if (strlen(p) >= IFNAMSIZ) - return false; - - while (*p) { - if ((uint8_t) *p >= 127U) - return false; - - if ((uint8_t) *p <= 31U) - return false; - p++; - } - - return true; -} - -int getpeercred(int fd, struct ucred *ucred) { - socklen_t n = sizeof(struct ucred); - struct ucred u; - int r; - - assert(fd >= 0); - assert(ucred); - - r = getsockopt(fd, SOL_SOCKET, SO_PEERCRED, &u, &n); - if (r < 0) - return -errno; - - if (n != sizeof(struct ucred)) - return -EIO; - - /* Check if the data is actually useful and not suppressed due to namespacing issues */ - if (!pid_is_valid(u.pid)) - return -ENODATA; - - /* Note that we don't check UID/GID here, as namespace translation works differently there: instead of - * receiving in "invalid" user/group we get the overflow UID/GID. */ - - *ucred = u; - return 0; -} - -int getpeersec(int fd, char **ret) { - _cleanup_free_ char *s = NULL; - socklen_t n = 64; - - assert(fd >= 0); - assert(ret); - - for (;;) { - s = new0(char, n+1); - if (!s) - return -ENOMEM; - - if (getsockopt(fd, SOL_SOCKET, SO_PEERSEC, s, &n) >= 0) - break; - - if (errno != ERANGE) - return -errno; - - s = mfree(s); - } - - if (isempty(s)) - return -EOPNOTSUPP; - - *ret = TAKE_PTR(s); - - return 0; -} - -int getpeergroups(int fd, gid_t **ret) { - socklen_t n = sizeof(gid_t) * 64; - _cleanup_free_ gid_t *d = NULL; - - assert(fd >= 0); - assert(ret); - - for (;;) { - d = malloc(n); - if (!d) - return -ENOMEM; - - if (getsockopt(fd, SOL_SOCKET, SO_PEERGROUPS, d, &n) >= 0) - break; - - if (errno != ERANGE) - return -errno; - - d = mfree(d); - } - - assert_se(n % sizeof(gid_t) == 0); - n /= sizeof(gid_t); - - if ((socklen_t) (int) n != n) - return -E2BIG; - - *ret = TAKE_PTR(d); - - return (int) n; -} - -ssize_t send_one_fd_iov_sa( - int transport_fd, - int fd, - struct iovec *iov, size_t iovlen, - const struct sockaddr *sa, socklen_t len, - int flags) { - - CMSG_BUFFER_TYPE(CMSG_SPACE(sizeof(int))) control = {}; - struct msghdr mh = { - .msg_name = (struct sockaddr*) sa, - .msg_namelen = len, - .msg_iov = iov, - .msg_iovlen = iovlen, - }; - ssize_t k; - - assert(transport_fd >= 0); - - /* - * We need either an FD or data to send. - * If there's nothing, return an error. - */ - if (fd < 0 && !iov) - return -EINVAL; - - if (fd >= 0) { - struct cmsghdr *cmsg; - - mh.msg_control = &control; - mh.msg_controllen = sizeof(control); - - cmsg = CMSG_FIRSTHDR(&mh); - cmsg->cmsg_level = SOL_SOCKET; - cmsg->cmsg_type = SCM_RIGHTS; - cmsg->cmsg_len = CMSG_LEN(sizeof(int)); - memcpy(CMSG_DATA(cmsg), &fd, sizeof(int)); - } - k = sendmsg(transport_fd, &mh, MSG_NOSIGNAL | flags); - if (k < 0) - return (ssize_t) -errno; - - return k; -} - -int send_one_fd_sa( - int transport_fd, - int fd, - const struct sockaddr *sa, socklen_t len, - int flags) { - - assert(fd >= 0); - - return (int) send_one_fd_iov_sa(transport_fd, fd, NULL, 0, sa, len, flags); -} - -ssize_t receive_one_fd_iov( - int transport_fd, - struct iovec *iov, size_t iovlen, - int flags, - int *ret_fd) { - - CMSG_BUFFER_TYPE(CMSG_SPACE(sizeof(int))) control; - struct msghdr mh = { - .msg_control = &control, - .msg_controllen = sizeof(control), - .msg_iov = iov, - .msg_iovlen = iovlen, - }; - struct cmsghdr *found; - ssize_t k; - - assert(transport_fd >= 0); - assert(ret_fd); - - /* - * Receive a single FD via @transport_fd. We don't care for - * the transport-type. We retrieve a single FD at most, so for - * packet-based transports, the caller must ensure to send - * only a single FD per packet. This is best used in - * combination with send_one_fd(). - */ - - k = recvmsg_safe(transport_fd, &mh, MSG_CMSG_CLOEXEC | flags); - if (k < 0) - return k; - - found = cmsg_find(&mh, SOL_SOCKET, SCM_RIGHTS, CMSG_LEN(sizeof(int))); - if (!found) { - cmsg_close_all(&mh); - - /* If didn't receive an FD or any data, return an error. */ - if (k == 0) - return -EIO; - } - - if (found) - *ret_fd = *(int*) CMSG_DATA(found); - else - *ret_fd = -1; - - return k; -} - -int receive_one_fd(int transport_fd, int flags) { - int fd; - ssize_t k; - - k = receive_one_fd_iov(transport_fd, NULL, 0, flags, &fd); - if (k == 0) - return fd; - - /* k must be negative, since receive_one_fd_iov() only returns - * a positive value if data was received through the iov. */ - assert(k < 0); - return (int) k; -} -#endif /* NM_IGNORED */ - -ssize_t next_datagram_size_fd(int fd) { - ssize_t l; - int k; - - /* This is a bit like FIONREAD/SIOCINQ, however a bit more powerful. The difference being: recv(MSG_PEEK) will - * actually cause the next datagram in the queue to be validated regarding checksums, which FIONREAD doesn't - * do. This difference is actually of major importance as we need to be sure that the size returned here - * actually matches what we will read with recvmsg() next, as otherwise we might end up allocating a buffer of - * the wrong size. */ - - l = recv(fd, NULL, 0, MSG_PEEK|MSG_TRUNC); - if (l < 0) { - if (IN_SET(errno, EOPNOTSUPP, EFAULT)) - goto fallback; - - return -errno; - } - if (l == 0) - goto fallback; - - return l; - -fallback: - k = 0; - - /* Some sockets (AF_PACKET) do not support null-sized recv() with MSG_TRUNC set, let's fall back to FIONREAD - * for them. Checksums don't matter for raw sockets anyway, hence this should be fine. */ - - if (ioctl(fd, FIONREAD, &k) < 0) - return -errno; - - return (ssize_t) k; -} - -#if 0 /* NM_IGNORED */ -/* Put a limit on how many times will attempt to call accept4(). We loop - * only on "transient" errors, but let's make sure we don't loop forever. */ -#define MAX_FLUSH_ITERATIONS 1024 - -int flush_accept(int fd) { - - int r, b; - socklen_t l = sizeof(b); - - /* Similar to flush_fd() but flushes all incoming connections by accepting and immediately closing - * them. */ - - if (getsockopt(fd, SOL_SOCKET, SO_ACCEPTCONN, &b, &l) < 0) - return -errno; - - assert(l == sizeof(b)); - if (!b) /* Let's check if this socket accepts connections before calling accept(). accept4() can - * return EOPNOTSUPP if the fd is not a listening socket, which we should treat as a fatal - * error, or in case the incoming TCP connection triggered a network issue, which we want to - * treat as a transient error. Thus, let's rule out the first reason for EOPNOTSUPP early, so - * we can loop safely on transient errors below. */ - return -ENOTTY; - - for (unsigned iteration = 0;; iteration++) { - int cfd; - - r = fd_wait_for_event(fd, POLLIN, 0); - if (r < 0) { - if (r == -EINTR) - continue; - - return r; - } - if (r == 0) - return 0; - - if (iteration >= MAX_FLUSH_ITERATIONS) - return log_debug_errno(SYNTHETIC_ERRNO(EBUSY), - "Failed to flush connections within " STRINGIFY(MAX_FLUSH_ITERATIONS) " iterations."); - - cfd = accept4(fd, NULL, NULL, SOCK_NONBLOCK|SOCK_CLOEXEC); - if (cfd < 0) { - if (errno == EAGAIN) - return 0; - - if (ERRNO_IS_ACCEPT_AGAIN(errno)) - continue; - - return -errno; - } - - safe_close(cfd); - } -} -#endif /* NM_IGNORED */ - -struct cmsghdr* cmsg_find(struct msghdr *mh, int level, int type, socklen_t length) { - struct cmsghdr *cmsg; - - assert(mh); - - CMSG_FOREACH(cmsg, mh) - if (cmsg->cmsg_level == level && - cmsg->cmsg_type == type && - (length == (socklen_t) -1 || length == cmsg->cmsg_len)) - return cmsg; - - return NULL; -} - -#if 0 /* NM_IGNORED */ -int socket_ioctl_fd(void) { - int fd; - - /* Create a socket to invoke the various network interface ioctl()s on. Traditionally only AF_INET was good for - * that. Since kernel 4.6 AF_NETLINK works for this too. We first try to use AF_INET hence, but if that's not - * available (for example, because it is made unavailable via SECCOMP or such), we'll fall back to the more - * generic AF_NETLINK. */ - - fd = socket(AF_INET, SOCK_DGRAM|SOCK_CLOEXEC, 0); - if (fd < 0) - fd = socket(AF_NETLINK, SOCK_RAW|SOCK_CLOEXEC, NETLINK_GENERIC); - if (fd < 0) - return -errno; - - return fd; -} - -int sockaddr_un_unlink(const struct sockaddr_un *sa) { - const char *p, * nul; - - assert(sa); - - if (sa->sun_family != AF_UNIX) - return -EPROTOTYPE; - - if (sa->sun_path[0] == 0) /* Nothing to do for abstract sockets */ - return 0; - - /* The path in .sun_path is not necessarily NUL terminated. Let's fix that. */ - nul = memchr(sa->sun_path, 0, sizeof(sa->sun_path)); - if (nul) - p = sa->sun_path; - else - p = memdupa_suffix0(sa->sun_path, sizeof(sa->sun_path)); - - if (unlink(p) < 0) - return -errno; - - return 1; -} -#endif /* NM_IGNORED */ - -int sockaddr_un_set_path(struct sockaddr_un *ret, const char *path) { - size_t l; - - assert(ret); - assert(path); - - /* Initialize ret->sun_path from the specified argument. This will interpret paths starting with '@' as - * abstract namespace sockets, and those starting with '/' as regular filesystem sockets. It won't accept - * anything else (i.e. no relative paths), to avoid ambiguities. Note that this function cannot be used to - * reference paths in the abstract namespace that include NUL bytes in the name. */ - - l = strlen(path); - if (l < 2) - return -EINVAL; - if (!IN_SET(path[0], '/', '@')) - return -EINVAL; - - /* Don't allow paths larger than the space in sockaddr_un. Note that we are a tiny bit more restrictive than - * the kernel is: we insist on NUL termination (both for abstract namespace and regular file system socket - * addresses!), which the kernel doesn't. We do this to reduce chance of incompatibility with other apps that - * do not expect non-NUL terminated file system path*/ - if (l+1 > sizeof(ret->sun_path)) - return -EINVAL; - - *ret = (struct sockaddr_un) { - .sun_family = AF_UNIX, - }; - - if (path[0] == '@') { - /* Abstract namespace socket */ - memcpy(ret->sun_path + 1, path + 1, l); /* copy *with* trailing NUL byte */ - return (int) (offsetof(struct sockaddr_un, sun_path) + l); /* 🔥 *don't* 🔥 include trailing NUL in size */ - - } else { - assert(path[0] == '/'); - - /* File system socket */ - memcpy(ret->sun_path, path, l + 1); /* copy *with* trailing NUL byte */ - return (int) (offsetof(struct sockaddr_un, sun_path) + l + 1); /* include trailing NUL in size */ - } -} - -int socket_bind_to_ifname(int fd, const char *ifname) { - assert(fd >= 0); - - /* Call with NULL to drop binding */ - - if (setsockopt(fd, SOL_SOCKET, SO_BINDTODEVICE, ifname, strlen_ptr(ifname)) < 0) - return -errno; - - return 0; -} - -int socket_bind_to_ifindex(int fd, int ifindex) { - char ifname[IF_NAMESIZE + 1]; - int r; - - assert(fd >= 0); - - if (ifindex <= 0) { - /* Drop binding */ - if (setsockopt(fd, SOL_SOCKET, SO_BINDTODEVICE, NULL, 0) < 0) - return -errno; - - return 0; - } - - r = setsockopt_int(fd, SOL_SOCKET, SO_BINDTOIFINDEX, ifindex); - if (r != -ENOPROTOOPT) - return r; - - /* Fall back to SO_BINDTODEVICE on kernels < 5.0 which didn't have SO_BINDTOIFINDEX */ - if (!format_ifname(ifindex, ifname)) - return -errno; - - return socket_bind_to_ifname(fd, ifname); -} - -ssize_t recvmsg_safe(int sockfd, struct msghdr *msg, int flags) { - ssize_t n; - - /* A wrapper around recvmsg() that checks for MSG_CTRUNC, and turns it into an error, in a reasonably - * safe way, closing any SCM_RIGHTS fds in the error path. - * - * Note that unlike our usual coding style this might modify *msg on failure. */ - - n = recvmsg(sockfd, msg, flags); - if (n < 0) - return -errno; - - if (FLAGS_SET(msg->msg_flags, MSG_CTRUNC)) { - cmsg_close_all(msg); - return -EXFULL; /* a recognizable error code */ - } - - return n; -} - -#if 0 /* NM_IGNORED */ -int socket_get_family(int fd, int *ret) { - int af; - socklen_t sl = sizeof(af); - - if (getsockopt(fd, SOL_SOCKET, SO_DOMAIN, &af, &sl) < 0) - return -errno; - - if (sl != sizeof(af)) - return -EINVAL; - - return af; -} - -int socket_set_recvpktinfo(int fd, int af, bool b) { - int r; - - if (af == AF_UNSPEC) { - r = socket_get_family(fd, &af); - if (r < 0) - return r; - } - - switch (af) { - - case AF_INET: - return setsockopt_int(fd, IPPROTO_IP, IP_PKTINFO, b); - - case AF_INET6: - return setsockopt_int(fd, IPPROTO_IPV6, IPV6_RECVPKTINFO, b); - - case AF_NETLINK: - return setsockopt_int(fd, SOL_NETLINK, NETLINK_PKTINFO, b); - - case AF_PACKET: - return setsockopt_int(fd, SOL_PACKET, PACKET_AUXDATA, b); - - default: - return -EAFNOSUPPORT; - } -} - -int socket_set_unicast_if(int fd, int af, int ifi) { - be32_t ifindex_be = htobe32(ifi); - int r; - - if (af == AF_UNSPEC) { - r = socket_get_family(fd, &af); - if (r < 0) - return r; - } - - switch (af) { - - case AF_INET: - if (setsockopt(fd, IPPROTO_IP, IP_UNICAST_IF, &ifindex_be, sizeof(ifindex_be)) < 0) - return -errno; - - return 0; - - case AF_INET6: - if (setsockopt(fd, IPPROTO_IPV6, IPV6_UNICAST_IF, &ifindex_be, sizeof(ifindex_be)) < 0) - return -errno; - - return 0; - - default: - return -EAFNOSUPPORT; - } -} - -int socket_set_option(int fd, int af, int opt_ipv4, int opt_ipv6, int val) { - int r; - - if (af == AF_UNSPEC) { - r = socket_get_family(fd, &af); - if (r < 0) - return r; - } - - switch (af) { - - case AF_INET: - return setsockopt_int(fd, IPPROTO_IP, opt_ipv4, val); - - case AF_INET6: - return setsockopt_int(fd, IPPROTO_IPV6, opt_ipv6, val); - - default: - return -EAFNOSUPPORT; - } -} - -int socket_get_mtu(int fd, int af, size_t *ret) { - int mtu, r; - - if (af == AF_UNSPEC) { - r = socket_get_family(fd, &af); - if (r < 0) - return r; - } - - switch (af) { - - case AF_INET: - r = getsockopt_int(fd, IPPROTO_IP, IP_MTU, &mtu); - break; - - case AF_INET6: - r = getsockopt_int(fd, IPPROTO_IPV6, IPV6_MTU, &mtu); - break; - - default: - return -EAFNOSUPPORT; - } - - if (r < 0) - return r; - if (mtu <= 0) - return -EINVAL; - - *ret = (size_t) mtu; - return 0; -} -#endif /* NM_IGNORED */ diff --git a/shared/systemd/src/basic/socket-util.h b/shared/systemd/src/basic/socket-util.h deleted file mode 100644 index 1de06947..00000000 --- a/shared/systemd/src/basic/socket-util.h +++ /dev/null @@ -1,307 +0,0 @@ -/* SPDX-License-Identifier: LGPL-2.1-or-later */ -#pragma once - -#include <inttypes.h> -#include <linux/netlink.h> -#include <linux/if_ether.h> -#include <linux/if_infiniband.h> -#include <linux/if_packet.h> -#include <netinet/in.h> -#include <stdbool.h> -#include <stddef.h> -#include <string.h> -#include <sys/socket.h> -#include <sys/types.h> -#include <sys/un.h> - -#include "macro.h" -#include "missing_network.h" -#include "missing_socket.h" -#include "sparse-endian.h" - -union sockaddr_union { - /* The minimal, abstract version */ - struct sockaddr sa; - - /* The libc provided version that allocates "enough room" for every protocol */ - struct sockaddr_storage storage; - - /* Protoctol-specific implementations */ - struct sockaddr_in in; - struct sockaddr_in6 in6; - struct sockaddr_un un; - struct sockaddr_nl nl; - struct sockaddr_ll ll; -#if 0 /* NM_IGNORED */ - struct sockaddr_vm vm; -#endif /* NM_IGNORED */ - - /* Ensure there is enough space to store Infiniband addresses */ - uint8_t ll_buffer[offsetof(struct sockaddr_ll, sll_addr) + CONST_MAX(ETH_ALEN, INFINIBAND_ALEN)]; - - /* Ensure there is enough space after the AF_UNIX sun_path for one more NUL byte, just to be sure that the path - * component is always followed by at least one NUL byte. */ - uint8_t un_buffer[sizeof(struct sockaddr_un) + 1]; -}; - -#define SUN_PATH_LEN (sizeof(((struct sockaddr_un){}).sun_path)) - -typedef struct SocketAddress { - union sockaddr_union sockaddr; - - /* We store the size here explicitly due to the weird - * sockaddr_un semantics for abstract sockets */ - socklen_t size; - - /* Socket type, i.e. SOCK_STREAM, SOCK_DGRAM, ... */ - int type; - - /* Socket protocol, IPPROTO_xxx, usually 0, except for netlink */ - int protocol; -} SocketAddress; - -typedef enum SocketAddressBindIPv6Only { - SOCKET_ADDRESS_DEFAULT, - SOCKET_ADDRESS_BOTH, - SOCKET_ADDRESS_IPV6_ONLY, - _SOCKET_ADDRESS_BIND_IPV6_ONLY_MAX, - _SOCKET_ADDRESS_BIND_IPV6_ONLY_INVALID = -1 -} SocketAddressBindIPv6Only; - -#define socket_address_family(a) ((a)->sockaddr.sa.sa_family) - -const char* socket_address_type_to_string(int t) _const_; -int socket_address_type_from_string(const char *s) _pure_; - -int sockaddr_un_unlink(const struct sockaddr_un *sa); - -static inline int socket_address_unlink(const SocketAddress *a) { - return socket_address_family(a) == AF_UNIX ? sockaddr_un_unlink(&a->sockaddr.un) : 0; -} - -bool socket_address_can_accept(const SocketAddress *a) _pure_; - -int socket_address_listen( - const SocketAddress *a, - int flags, - int backlog, - SocketAddressBindIPv6Only only, - const char *bind_to_device, - bool reuse_port, - bool free_bind, - bool transparent, - mode_t directory_mode, - mode_t socket_mode, - const char *label); - -int socket_address_verify(const SocketAddress *a, bool strict) _pure_; -int socket_address_print(const SocketAddress *a, char **p); -bool socket_address_matches_fd(const SocketAddress *a, int fd); - -bool socket_address_equal(const SocketAddress *a, const SocketAddress *b) _pure_; - -const char* socket_address_get_path(const SocketAddress *a); - -bool socket_ipv6_is_supported(void); - -int sockaddr_port(const struct sockaddr *_sa, unsigned *port); -const union in_addr_union *sockaddr_in_addr(const struct sockaddr *sa); - -int sockaddr_pretty(const struct sockaddr *_sa, socklen_t salen, bool translate_ipv6, bool include_port, char **ret); -int getpeername_pretty(int fd, bool include_port, char **ret); -int getsockname_pretty(int fd, char **ret); - -int socknameinfo_pretty(union sockaddr_union *sa, socklen_t salen, char **_ret); - -const char* socket_address_bind_ipv6_only_to_string(SocketAddressBindIPv6Only b) _const_; -SocketAddressBindIPv6Only socket_address_bind_ipv6_only_from_string(const char *s) _pure_; -SocketAddressBindIPv6Only socket_address_bind_ipv6_only_or_bool_from_string(const char *s); - -int netlink_family_to_string_alloc(int b, char **s); -int netlink_family_from_string(const char *s) _pure_; - -bool sockaddr_equal(const union sockaddr_union *a, const union sockaddr_union *b); - -int fd_set_sndbuf(int fd, size_t n, bool increase); -static inline int fd_inc_sndbuf(int fd, size_t n) { - return fd_set_sndbuf(fd, n, true); -} -int fd_set_rcvbuf(int fd, size_t n, bool increase); -static inline int fd_inc_rcvbuf(int fd, size_t n) { - return fd_set_rcvbuf(fd, n, true); -} - -int ip_tos_to_string_alloc(int i, char **s); -int ip_tos_from_string(const char *s); - -typedef enum { - IFNAME_VALID_ALTERNATIVE = 1 << 0, - IFNAME_VALID_NUMERIC = 1 << 1, - _IFNAME_VALID_ALL = IFNAME_VALID_ALTERNATIVE | IFNAME_VALID_NUMERIC, -} IfnameValidFlags; -bool ifname_valid_full(const char *p, IfnameValidFlags flags); -static inline bool ifname_valid(const char *p) { - return ifname_valid_full(p, 0); -} -bool address_label_valid(const char *p); - -int getpeercred(int fd, struct ucred *ucred); -int getpeersec(int fd, char **ret); -int getpeergroups(int fd, gid_t **ret); - -ssize_t send_one_fd_iov_sa( - int transport_fd, - int fd, - struct iovec *iov, size_t iovlen, - const struct sockaddr *sa, socklen_t len, - int flags); -int send_one_fd_sa(int transport_fd, - int fd, - const struct sockaddr *sa, socklen_t len, - int flags); -#define send_one_fd_iov(transport_fd, fd, iov, iovlen, flags) send_one_fd_iov_sa(transport_fd, fd, iov, iovlen, NULL, 0, flags) -#define send_one_fd(transport_fd, fd, flags) send_one_fd_iov_sa(transport_fd, fd, NULL, 0, NULL, 0, flags) -ssize_t receive_one_fd_iov(int transport_fd, struct iovec *iov, size_t iovlen, int flags, int *ret_fd); -int receive_one_fd(int transport_fd, int flags); - -ssize_t next_datagram_size_fd(int fd); - -int flush_accept(int fd); - -#define CMSG_FOREACH(cmsg, mh) \ - for ((cmsg) = CMSG_FIRSTHDR(mh); (cmsg); (cmsg) = CMSG_NXTHDR((mh), (cmsg))) - -struct cmsghdr* cmsg_find(struct msghdr *mh, int level, int type, socklen_t length); - -/* Type-safe, dereferencing version of cmsg_find() */ -#define CMSG_FIND_DATA(mh, level, type, ctype) \ - ({ \ - struct cmsghdr *_found; \ - _found = cmsg_find(mh, level, type, CMSG_LEN(sizeof(ctype))); \ - (ctype*) (_found ? CMSG_DATA(_found) : NULL); \ - }) - -/* Resolves to a type that can carry cmsghdr structures. Make sure things are properly aligned, i.e. the type - * itself is placed properly in memory and the size is also aligned to what's appropriate for "cmsghdr" - * structures. */ -#define CMSG_BUFFER_TYPE(size) \ - union { \ - struct cmsghdr cmsghdr; \ - uint8_t buf[size]; \ - uint8_t align_check[(size) >= CMSG_SPACE(0) && \ - (size) == CMSG_ALIGN(size) ? 1 : -1]; \ - } - -/* - * Certain hardware address types (e.g Infiniband) do not fit into sll_addr - * (8 bytes) and run over the structure. This macro returns the correct size that - * must be passed to kernel. - */ -#define SOCKADDR_LL_LEN(sa) \ - ({ \ - const struct sockaddr_ll *_sa = &(sa); \ - size_t _mac_len = sizeof(_sa->sll_addr); \ - assert(_sa->sll_family == AF_PACKET); \ - if (be16toh(_sa->sll_hatype) == ARPHRD_ETHER) \ - _mac_len = MAX(_mac_len, (size_t) ETH_ALEN); \ - if (be16toh(_sa->sll_hatype) == ARPHRD_INFINIBAND) \ - _mac_len = MAX(_mac_len, (size_t) INFINIBAND_ALEN); \ - offsetof(struct sockaddr_ll, sll_addr) + _mac_len; \ - }) - -/* Covers only file system and abstract AF_UNIX socket addresses, but not unnamed socket addresses. */ -#define SOCKADDR_UN_LEN(sa) \ - ({ \ - const struct sockaddr_un *_sa = &(sa); \ - assert(_sa->sun_family == AF_UNIX); \ - offsetof(struct sockaddr_un, sun_path) + \ - (_sa->sun_path[0] == 0 ? \ - 1 + strnlen(_sa->sun_path+1, sizeof(_sa->sun_path)-1) : \ - strnlen(_sa->sun_path, sizeof(_sa->sun_path))+1); \ - }) - -#define SOCKADDR_LEN(sa) \ - ({ \ - const union sockaddr_union *__sa = &(sa); \ - size_t _len; \ - switch(__sa->sa.sa_family) { \ - case AF_INET: \ - _len = sizeof(struct sockaddr_in); \ - break; \ - case AF_INET6: \ - _len = sizeof(struct sockaddr_in6); \ - break; \ - case AF_UNIX: \ - _len = SOCKADDR_UN_LEN(__sa->un); \ - break; \ - case AF_PACKET: \ - _len = SOCKADDR_LL_LEN(__sa->ll); \ - break; \ - case AF_NETLINK: \ - _len = sizeof(struct sockaddr_nl); \ - break; \ - case AF_VSOCK: \ - _len = sizeof(struct sockaddr_vm); \ - break; \ - default: \ - assert_not_reached("invalid socket family"); \ - } \ - _len; \ - }) - -int socket_ioctl_fd(void); - -int sockaddr_un_set_path(struct sockaddr_un *ret, const char *path); - -static inline int setsockopt_int(int fd, int level, int optname, int value) { - if (setsockopt(fd, level, optname, &value, sizeof(value)) < 0) - return -errno; - - return 0; -} - -static inline int getsockopt_int(int fd, int level, int optname, int *ret) { - int v; - socklen_t sl = sizeof(v); - - if (getsockopt(fd, level, optname, &v, &sl) < 0) - return -errno; - if (sl != sizeof(v)) - return -EIO; - - *ret = v; - return 0; -} - -int socket_bind_to_ifname(int fd, const char *ifname); -int socket_bind_to_ifindex(int fd, int ifindex); - -ssize_t recvmsg_safe(int sockfd, struct msghdr *msg, int flags); - -int socket_get_family(int fd, int *ret); -int socket_set_recvpktinfo(int fd, int af, bool b); -int socket_set_unicast_if(int fd, int af, int ifi); - -int socket_set_option(int fd, int af, int opt_ipv4, int opt_ipv6, int val); -#if 0 /* NM_IGNORED */ -static inline int socket_set_recverr(int fd, int af, bool b) { - return socket_set_option(fd, af, IP_RECVERR, IPV6_RECVERR, b); -} -static inline int socket_set_recvttl(int fd, int af, bool b) { - return socket_set_option(fd, af, IP_RECVTTL, IPV6_RECVHOPLIMIT, b); -} -static inline int socket_set_ttl(int fd, int af, int ttl) { - return socket_set_option(fd, af, IP_TTL, IPV6_UNICAST_HOPS, ttl); -} -static inline int socket_set_freebind(int fd, int af, bool b) { - return socket_set_option(fd, af, IP_FREEBIND, IPV6_FREEBIND, b); -} -static inline int socket_set_transparent(int fd, int af, bool b) { - return socket_set_option(fd, af, IP_TRANSPARENT, IPV6_TRANSPARENT, b); -} -static inline int socket_set_recvfragsize(int fd, int af, bool b) { - return socket_set_option(fd, af, IP_RECVFRAGSIZE, IPV6_RECVFRAGSIZE, b); -} -#endif /* NM_IGNORED */ - -int socket_get_mtu(int fd, int af, size_t *ret); diff --git a/shared/systemd/src/basic/sort-util.h b/shared/systemd/src/basic/sort-util.h deleted file mode 100644 index a8984fc1..00000000 --- a/shared/systemd/src/basic/sort-util.h +++ /dev/null @@ -1,74 +0,0 @@ -/* SPDX-License-Identifier: LGPL-2.1-or-later */ -#pragma once - -#include <stdlib.h> - -#include "macro.h" - -void *xbsearch_r(const void *key, const void *base, size_t nmemb, size_t size, - __compar_d_fn_t compar, void *arg); - -#define typesafe_bsearch_r(k, b, n, func, userdata) \ - ({ \ - const typeof(b[0]) *_k = k; \ - int (*_func_)(const typeof(b[0])*, const typeof(b[0])*, typeof(userdata)) = func; \ - xbsearch_r((const void*) _k, (b), (n), sizeof((b)[0]), (__compar_d_fn_t) _func_, userdata); \ - }) - -/** - * Normal bsearch requires base to be nonnull. Here were require - * that only if nmemb > 0. - */ -static inline void* bsearch_safe(const void *key, const void *base, - size_t nmemb, size_t size, __compar_fn_t compar) { - if (nmemb <= 0) - return NULL; - - assert(base); - return bsearch(key, base, nmemb, size, compar); -} - -#define typesafe_bsearch(k, b, n, func) \ - ({ \ - const typeof(b[0]) *_k = k; \ - int (*_func_)(const typeof(b[0])*, const typeof(b[0])*) = func; \ - bsearch_safe((const void*) _k, (b), (n), sizeof((b)[0]), (__compar_fn_t) _func_); \ - }) - -/** - * Normal qsort requires base to be nonnull. Here were require - * that only if nmemb > 0. - */ -static inline void _qsort_safe(void *base, size_t nmemb, size_t size, __compar_fn_t compar) { - if (nmemb <= 1) - return; - - assert(base); - qsort(base, nmemb, size, compar); -} - -/* A wrapper around the above, but that adds typesafety: the element size is automatically derived from the type and so - * is the prototype for the comparison function */ -#define typesafe_qsort(p, n, func) \ - ({ \ - int (*_func_)(const typeof(p[0])*, const typeof(p[0])*) = func; \ - _qsort_safe((p), (n), sizeof((p)[0]), (__compar_fn_t) _func_); \ - }) - -#if 0 /* NM_IGNORED */ -static inline void qsort_r_safe(void *base, size_t nmemb, size_t size, __compar_d_fn_t compar, void *userdata) { - if (nmemb <= 1) - return; - - assert(base); - qsort_r(base, nmemb, size, compar, userdata); -} - -#define typesafe_qsort_r(p, n, func, userdata) \ - ({ \ - int (*_func_)(const typeof(p[0])*, const typeof(p[0])*, typeof(userdata)) = func; \ - qsort_r_safe((p), (n), sizeof((p)[0]), (__compar_d_fn_t) _func_, userdata); \ - }) -#endif /* NM_IGNORED */ - -int cmp_int(const int *a, const int *b); diff --git a/shared/systemd/src/basic/sparse-endian.h b/shared/systemd/src/basic/sparse-endian.h deleted file mode 100644 index 9583dda9..00000000 --- a/shared/systemd/src/basic/sparse-endian.h +++ /dev/null @@ -1,90 +0,0 @@ -/* SPDX-License-Identifier: MIT - * - * Copyright (c) 2012 Josh Triplett <josh@joshtriplett.org> - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to - * deal in the Software without restriction, including without limitation the - * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or - * sell copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS - * IN THE SOFTWARE. - */ -#pragma once - -#include <byteswap.h> -#include <endian.h> -#include <stdint.h> - -#ifdef __CHECKER__ -#define __sd_bitwise __attribute__((__bitwise__)) -#define __sd_force __attribute__((__force__)) -#else -#define __sd_bitwise -#define __sd_force -#endif - -typedef uint16_t __sd_bitwise le16_t; -typedef uint16_t __sd_bitwise be16_t; -typedef uint32_t __sd_bitwise le32_t; -typedef uint32_t __sd_bitwise be32_t; -typedef uint64_t __sd_bitwise le64_t; -typedef uint64_t __sd_bitwise be64_t; - -#undef htobe16 -#undef htole16 -#undef be16toh -#undef le16toh -#undef htobe32 -#undef htole32 -#undef be32toh -#undef le32toh -#undef htobe64 -#undef htole64 -#undef be64toh -#undef le64toh - -#if __BYTE_ORDER == __LITTLE_ENDIAN -#define bswap_16_on_le(x) __bswap_16(x) -#define bswap_32_on_le(x) __bswap_32(x) -#define bswap_64_on_le(x) __bswap_64(x) -#define bswap_16_on_be(x) (x) -#define bswap_32_on_be(x) (x) -#define bswap_64_on_be(x) (x) -#elif __BYTE_ORDER == __BIG_ENDIAN -#define bswap_16_on_le(x) (x) -#define bswap_32_on_le(x) (x) -#define bswap_64_on_le(x) (x) -#define bswap_16_on_be(x) __bswap_16(x) -#define bswap_32_on_be(x) __bswap_32(x) -#define bswap_64_on_be(x) __bswap_64(x) -#endif - -static inline le16_t htole16(uint16_t value) { return (le16_t __sd_force) bswap_16_on_be(value); } -static inline le32_t htole32(uint32_t value) { return (le32_t __sd_force) bswap_32_on_be(value); } -static inline le64_t htole64(uint64_t value) { return (le64_t __sd_force) bswap_64_on_be(value); } - -static inline be16_t htobe16(uint16_t value) { return (be16_t __sd_force) bswap_16_on_le(value); } -static inline be32_t htobe32(uint32_t value) { return (be32_t __sd_force) bswap_32_on_le(value); } -static inline be64_t htobe64(uint64_t value) { return (be64_t __sd_force) bswap_64_on_le(value); } - -static inline uint16_t le16toh(le16_t value) { return bswap_16_on_be((uint16_t __sd_force)value); } -static inline uint32_t le32toh(le32_t value) { return bswap_32_on_be((uint32_t __sd_force)value); } -static inline uint64_t le64toh(le64_t value) { return bswap_64_on_be((uint64_t __sd_force)value); } - -static inline uint16_t be16toh(be16_t value) { return bswap_16_on_le((uint16_t __sd_force)value); } -static inline uint32_t be32toh(be32_t value) { return bswap_32_on_le((uint32_t __sd_force)value); } -static inline uint64_t be64toh(be64_t value) { return bswap_64_on_le((uint64_t __sd_force)value); } - -#undef __sd_bitwise -#undef __sd_force diff --git a/shared/systemd/src/basic/stat-util.c b/shared/systemd/src/basic/stat-util.c deleted file mode 100644 index a9880096..00000000 --- a/shared/systemd/src/basic/stat-util.c +++ /dev/null @@ -1,480 +0,0 @@ -/* SPDX-License-Identifier: LGPL-2.1-or-later */ - -#include "nm-sd-adapt-shared.h" - -#include <errno.h> -#include <fcntl.h> -#include <sched.h> -#include <sys/statvfs.h> -#include <sys/types.h> -#include <unistd.h> - -#include "alloc-util.h" -#include "dirent-util.h" -#include "fd-util.h" -#include "fileio.h" -#include "fs-util.h" -#include "macro.h" -#include "missing_fs.h" -#include "missing_magic.h" -#include "missing_syscall.h" -#include "parse-util.h" -#include "stat-util.h" -#include "string-util.h" - -#if 0 /* NM_IGNORED */ -int is_symlink(const char *path) { - struct stat info; - - assert(path); - - if (lstat(path, &info) < 0) - return -errno; - - return !!S_ISLNK(info.st_mode); -} -#endif /* NM_IGNORED */ - -int is_dir(const char* path, bool follow) { - struct stat st; - int r; - - assert(path); - - if (follow) - r = stat(path, &st); - else - r = lstat(path, &st); - if (r < 0) - return -errno; - - return !!S_ISDIR(st.st_mode); -} - -#if 0 /* NM_IGNORED */ -int is_dir_fd(int fd) { - struct stat st; - - if (fstat(fd, &st) < 0) - return -errno; - - return !!S_ISDIR(st.st_mode); -} - -int is_device_node(const char *path) { - struct stat info; - - assert(path); - - if (lstat(path, &info) < 0) - return -errno; - - return !!(S_ISBLK(info.st_mode) || S_ISCHR(info.st_mode)); -} - -int dir_is_empty_at(int dir_fd, const char *path) { - _cleanup_close_ int fd = -1; - _cleanup_closedir_ DIR *d = NULL; - struct dirent *de; - - if (path) - fd = openat(dir_fd, path, O_RDONLY|O_DIRECTORY|O_CLOEXEC); - else - fd = fcntl(fd, F_DUPFD_CLOEXEC, 3); - if (fd < 0) - return -errno; - - d = take_fdopendir(&fd); - if (!d) - return -errno; - - FOREACH_DIRENT(de, d, return -errno) - return 0; - - return 1; -} - -bool null_or_empty(struct stat *st) { - assert(st); - - if (S_ISREG(st->st_mode) && st->st_size <= 0) - return true; - - /* We don't want to hardcode the major/minor of /dev/null, hence we do a simpler "is this a character - * device node?" check. */ - - if (S_ISCHR(st->st_mode)) - return true; - - return false; -} - -int null_or_empty_path(const char *fn) { - struct stat st; - - assert(fn); - - /* If we have the path, let's do an easy text comparison first. */ - if (path_equal(fn, "/dev/null")) - return true; - - if (stat(fn, &st) < 0) - return -errno; - - return null_or_empty(&st); -} - -int null_or_empty_fd(int fd) { - struct stat st; - - assert(fd >= 0); - - if (fstat(fd, &st) < 0) - return -errno; - - return null_or_empty(&st); -} - -int path_is_read_only_fs(const char *path) { - struct statvfs st; - - assert(path); - - if (statvfs(path, &st) < 0) - return -errno; - - if (st.f_flag & ST_RDONLY) - return true; - - /* On NFS, statvfs() might not reflect whether we can actually - * write to the remote share. Let's try again with - * access(W_OK) which is more reliable, at least sometimes. */ - if (access(path, W_OK) < 0 && errno == EROFS) - return true; - - return false; -} - -int files_same(const char *filea, const char *fileb, int flags) { - struct stat a, b; - - assert(filea); - assert(fileb); - - if (fstatat(AT_FDCWD, filea, &a, flags) < 0) - return -errno; - - if (fstatat(AT_FDCWD, fileb, &b, flags) < 0) - return -errno; - - return a.st_dev == b.st_dev && - a.st_ino == b.st_ino; -} - -bool is_fs_type(const struct statfs *s, statfs_f_type_t magic_value) { - assert(s); - assert_cc(sizeof(statfs_f_type_t) >= sizeof(s->f_type)); - - return F_TYPE_EQUAL(s->f_type, magic_value); -} - -int fd_is_fs_type(int fd, statfs_f_type_t magic_value) { - struct statfs s; - - if (fstatfs(fd, &s) < 0) - return -errno; - - return is_fs_type(&s, magic_value); -} - -int path_is_fs_type(const char *path, statfs_f_type_t magic_value) { - struct statfs s; - - if (statfs(path, &s) < 0) - return -errno; - - return is_fs_type(&s, magic_value); -} - -bool is_temporary_fs(const struct statfs *s) { - return is_fs_type(s, TMPFS_MAGIC) || - is_fs_type(s, RAMFS_MAGIC); -} - -bool is_network_fs(const struct statfs *s) { - return is_fs_type(s, CIFS_MAGIC_NUMBER) || - is_fs_type(s, CODA_SUPER_MAGIC) || - is_fs_type(s, NCP_SUPER_MAGIC) || - is_fs_type(s, NFS_SUPER_MAGIC) || - is_fs_type(s, SMB_SUPER_MAGIC) || - is_fs_type(s, V9FS_MAGIC) || - is_fs_type(s, AFS_SUPER_MAGIC) || - is_fs_type(s, OCFS2_SUPER_MAGIC); -} - -int fd_is_temporary_fs(int fd) { - struct statfs s; - - if (fstatfs(fd, &s) < 0) - return -errno; - - return is_temporary_fs(&s); -} - -int fd_is_network_fs(int fd) { - struct statfs s; - - if (fstatfs(fd, &s) < 0) - return -errno; - - return is_network_fs(&s); -} - -int path_is_temporary_fs(const char *path) { - struct statfs s; - - if (statfs(path, &s) < 0) - return -errno; - - return is_temporary_fs(&s); -} -#endif /* NM_IGNORED */ - -int stat_verify_regular(const struct stat *st) { - assert(st); - - /* Checks whether the specified stat() structure refers to a regular file. If not returns an appropriate error - * code. */ - - if (S_ISDIR(st->st_mode)) - return -EISDIR; - - if (S_ISLNK(st->st_mode)) - return -ELOOP; - - if (!S_ISREG(st->st_mode)) - return -EBADFD; - - return 0; -} - -int fd_verify_regular(int fd) { - struct stat st; - - assert(fd >= 0); - - if (fstat(fd, &st) < 0) - return -errno; - - return stat_verify_regular(&st); -} - -#if 0 /* NM_IGNORED */ -int stat_verify_directory(const struct stat *st) { - assert(st); - - if (S_ISLNK(st->st_mode)) - return -ELOOP; - - if (!S_ISDIR(st->st_mode)) - return -ENOTDIR; - - return 0; -} - -int fd_verify_directory(int fd) { - struct stat st; - - assert(fd >= 0); - - if (fstat(fd, &st) < 0) - return -errno; - - return stat_verify_directory(&st); -} - -int device_path_make_major_minor(mode_t mode, dev_t devno, char **ret) { - const char *t; - - /* Generates the /dev/{char|block}/MAJOR:MINOR path for a dev_t */ - - if (S_ISCHR(mode)) - t = "char"; - else if (S_ISBLK(mode)) - t = "block"; - else - return -ENODEV; - - if (asprintf(ret, "/dev/%s/%u:%u", t, major(devno), minor(devno)) < 0) - return -ENOMEM; - - return 0; -} - -int device_path_make_canonical(mode_t mode, dev_t devno, char **ret) { - _cleanup_free_ char *p = NULL; - int r; - - /* Finds the canonical path for a device, i.e. resolves the /dev/{char|block}/MAJOR:MINOR path to the end. */ - - assert(ret); - - if (major(devno) == 0 && minor(devno) == 0) { - char *s; - - /* A special hack to make sure our 'inaccessible' device nodes work. They won't have symlinks in - * /dev/block/ and /dev/char/, hence we handle them specially here. */ - - if (S_ISCHR(mode)) - s = strdup("/run/systemd/inaccessible/chr"); - else if (S_ISBLK(mode)) - s = strdup("/run/systemd/inaccessible/blk"); - else - return -ENODEV; - - if (!s) - return -ENOMEM; - - *ret = s; - return 0; - } - - r = device_path_make_major_minor(mode, devno, &p); - if (r < 0) - return r; - - return chase_symlinks(p, NULL, 0, ret, NULL); -} - -int device_path_parse_major_minor(const char *path, mode_t *ret_mode, dev_t *ret_devno) { - mode_t mode; - dev_t devno; - int r; - - /* Tries to extract the major/minor directly from the device path if we can. Handles /dev/block/ and /dev/char/ - * paths, as well out synthetic inaccessible device nodes. Never goes to disk. Returns -ENODEV if the device - * path cannot be parsed like this. */ - - if (path_equal(path, "/run/systemd/inaccessible/chr")) { - mode = S_IFCHR; - devno = makedev(0, 0); - } else if (path_equal(path, "/run/systemd/inaccessible/blk")) { - mode = S_IFBLK; - devno = makedev(0, 0); - } else { - const char *w; - - w = path_startswith(path, "/dev/block/"); - if (w) - mode = S_IFBLK; - else { - w = path_startswith(path, "/dev/char/"); - if (!w) - return -ENODEV; - - mode = S_IFCHR; - } - - r = parse_dev(w, &devno); - if (r < 0) - return r; - } - - if (ret_mode) - *ret_mode = mode; - if (ret_devno) - *ret_devno = devno; - - return 0; -} - -int proc_mounted(void) { - int r; - - /* A quick check of procfs is properly mounted */ - - r = path_is_fs_type("/proc/", PROC_SUPER_MAGIC); - if (r == -ENOENT) /* not mounted at all */ - return false; - - return r; -} - -bool stat_inode_unmodified(const struct stat *a, const struct stat *b) { - - /* Returns if the specified stat structures reference the same, unmodified inode. This check tries to - * be reasonably careful when detecting changes: we check both inode and mtime, to cater for file - * systems where mtimes are fixed to 0 (think: ostree/nixos type installations). We also check file - * size, backing device, inode type and if this refers to a device not the major/minor. - * - * Note that we don't care if file attributes such as ownership or access mode change, this here is - * about contents of the file. The purpose here is to detect file contents changes, and nothing - * else. */ - - return a && b && - (a->st_mode & S_IFMT) != 0 && /* We use the check for .st_mode if the structure was ever initialized */ - ((a->st_mode ^ b->st_mode) & S_IFMT) == 0 && /* same inode type */ - a->st_mtim.tv_sec == b->st_mtim.tv_sec && - a->st_mtim.tv_nsec == b->st_mtim.tv_nsec && - (!S_ISREG(a->st_mode) || a->st_size == b->st_size) && /* if regular file, compare file size */ - a->st_dev == b->st_dev && - a->st_ino == b->st_ino && - (!(S_ISCHR(a->st_mode) || S_ISBLK(a->st_mode)) || a->st_rdev == b->st_rdev); /* if device node, also compare major/minor, because we can */ -} - -int statx_fallback(int dfd, const char *path, int flags, unsigned mask, struct statx *sx) { - static bool avoid_statx = false; - struct stat st; - - if (!avoid_statx) { - if (statx(dfd, path, flags, mask, sx) < 0) { - if (!ERRNO_IS_NOT_SUPPORTED(errno) && errno != EPERM) - return -errno; - - /* If statx() is not supported or if we see EPERM (which might indicate seccomp - * filtering or so), let's do a fallback. Not that on EACCES we'll not fall back, - * since that is likely an indication of fs access issues, which we should - * propagate */ - } else - return 0; - - avoid_statx = true; - } - - /* Only do fallback if fstatat() supports the flag too, or if it's one of the sync flags, which are - * OK to ignore */ - if ((flags & ~(AT_EMPTY_PATH|AT_NO_AUTOMOUNT|AT_SYMLINK_NOFOLLOW| - AT_STATX_SYNC_AS_STAT|AT_STATX_FORCE_SYNC|AT_STATX_DONT_SYNC)) != 0) - return -EOPNOTSUPP; - - if (fstatat(dfd, path, &st, flags & (AT_EMPTY_PATH|AT_NO_AUTOMOUNT|AT_SYMLINK_NOFOLLOW)) < 0) - return -errno; - - *sx = (struct statx) { - .stx_mask = STATX_TYPE|STATX_MODE| - STATX_NLINK|STATX_UID|STATX_GID| - STATX_ATIME|STATX_MTIME|STATX_CTIME| - STATX_INO|STATX_SIZE|STATX_BLOCKS, - .stx_blksize = st.st_blksize, - .stx_nlink = st.st_nlink, - .stx_uid = st.st_uid, - .stx_gid = st.st_gid, - .stx_mode = st.st_mode, - .stx_ino = st.st_ino, - .stx_size = st.st_size, - .stx_blocks = st.st_blocks, - .stx_rdev_major = major(st.st_rdev), - .stx_rdev_minor = minor(st.st_rdev), - .stx_dev_major = major(st.st_dev), - .stx_dev_minor = minor(st.st_dev), - .stx_atime.tv_sec = st.st_atim.tv_sec, - .stx_atime.tv_nsec = st.st_atim.tv_nsec, - .stx_mtime.tv_sec = st.st_mtim.tv_sec, - .stx_mtime.tv_nsec = st.st_mtim.tv_nsec, - .stx_ctime.tv_sec = st.st_ctim.tv_sec, - .stx_ctime.tv_nsec = st.st_ctim.tv_nsec, - }; - - return 0; -} -#endif /* NM_IGNORED */ diff --git a/shared/systemd/src/basic/stat-util.h b/shared/systemd/src/basic/stat-util.h deleted file mode 100644 index a566114f..00000000 --- a/shared/systemd/src/basic/stat-util.h +++ /dev/null @@ -1,115 +0,0 @@ -/* SPDX-License-Identifier: LGPL-2.1-or-later */ -#pragma once - -#include <fcntl.h> -#include <stdbool.h> -#include <stddef.h> -#include <sys/stat.h> -#include <sys/statfs.h> -#include <sys/types.h> -#include <sys/vfs.h> - -#include "macro.h" -#include "missing_stat.h" - -int is_symlink(const char *path); -int is_dir(const char *path, bool follow); -int is_dir_fd(int fd); -int is_device_node(const char *path); - -int dir_is_empty_at(int dir_fd, const char *path); -static inline int dir_is_empty(const char *path) { - return dir_is_empty_at(AT_FDCWD, path); -} - -static inline int dir_is_populated(const char *path) { - int r; - r = dir_is_empty(path); - if (r < 0) - return r; - return !r; -} - -bool null_or_empty(struct stat *st) _pure_; -int null_or_empty_path(const char *fn); -int null_or_empty_fd(int fd); - -int path_is_read_only_fs(const char *path); - -int files_same(const char *filea, const char *fileb, int flags); - -/* The .f_type field of struct statfs is really weird defined on - * different archs. Let's give its type a name. */ -typedef typeof(((struct statfs*)NULL)->f_type) statfs_f_type_t; - -bool is_fs_type(const struct statfs *s, statfs_f_type_t magic_value) _pure_; -int fd_is_fs_type(int fd, statfs_f_type_t magic_value); -int path_is_fs_type(const char *path, statfs_f_type_t magic_value); - -bool is_temporary_fs(const struct statfs *s) _pure_; -bool is_network_fs(const struct statfs *s) _pure_; - -int fd_is_temporary_fs(int fd); -int fd_is_network_fs(int fd); - -int path_is_temporary_fs(const char *path); - -/* Because statfs.t_type can be int on some architectures, we have to cast - * the const magic to the type, otherwise the compiler warns about - * signed/unsigned comparison, because the magic can be 32 bit unsigned. - */ -#define F_TYPE_EQUAL(a, b) (a == (typeof(a)) b) - -int stat_verify_regular(const struct stat *st); -int fd_verify_regular(int fd); - -int stat_verify_directory(const struct stat *st); -int fd_verify_directory(int fd); - -/* glibc and the Linux kernel have different ideas about the major/minor size. These calls will check whether the - * specified major is valid by the Linux kernel's standards, not by glibc's. Linux has 20bits of minor, and 12 bits of - * major space. See MINORBITS in linux/kdev_t.h in the kernel sources. (If you wonder why we define _y here, instead of - * comparing directly >= 0: it's to trick out -Wtype-limits, which would otherwise complain if the type is unsigned, as - * such a test would be pointless in such a case.) */ - -#define DEVICE_MAJOR_VALID(x) \ - ({ \ - typeof(x) _x = (x), _y = 0; \ - _x >= _y && _x < (UINT32_C(1) << 12); \ - \ - }) - -#define DEVICE_MINOR_VALID(x) \ - ({ \ - typeof(x) _x = (x), _y = 0; \ - _x >= _y && _x < (UINT32_C(1) << 20); \ - }) - -int device_path_make_major_minor(mode_t mode, dev_t devno, char **ret); -int device_path_make_canonical(mode_t mode, dev_t devno, char **ret); -int device_path_parse_major_minor(const char *path, mode_t *ret_mode, dev_t *ret_devno); - -int proc_mounted(void); - -bool stat_inode_unmodified(const struct stat *a, const struct stat *b); - -int statx_fallback(int dfd, const char *path, int flags, unsigned mask, struct statx *sx); - -#if HAS_FEATURE_MEMORY_SANITIZER -# warning "Explicitly initializing struct statx, to work around msan limitation. Please remove as soon as msan has been updated to not require this." -# define STRUCT_STATX_DEFINE(var) \ - struct statx var = {} -# define STRUCT_NEW_STATX_DEFINE(var) \ - union { \ - struct statx sx; \ - struct new_statx nsx; \ - } var = {} -#else -# define STRUCT_STATX_DEFINE(var) \ - struct statx var -# define STRUCT_NEW_STATX_DEFINE(var) \ - union { \ - struct statx sx; \ - struct new_statx nsx; \ - } var -#endif diff --git a/shared/systemd/src/basic/stdio-util.h b/shared/systemd/src/basic/stdio-util.h deleted file mode 100644 index d45d3c1a..00000000 --- a/shared/systemd/src/basic/stdio-util.h +++ /dev/null @@ -1,66 +0,0 @@ -/* SPDX-License-Identifier: LGPL-2.1-or-later */ -#pragma once - -#if 0 /* NM_IGNORED */ -#include <printf.h> -#endif /* NM_IGNORED */ -#include <stdarg.h> -#include <stdio.h> -#include <sys/types.h> - -#include "macro.h" -#include "memory-util.h" - -#define snprintf_ok(buf, len, fmt, ...) \ - ((size_t) snprintf(buf, len, fmt, __VA_ARGS__) < (len)) - -#define xsprintf(buf, fmt, ...) \ - assert_message_se(snprintf_ok(buf, ELEMENTSOF(buf), fmt, __VA_ARGS__), "xsprintf: " #buf "[] must be big enough") - -#define VA_FORMAT_ADVANCE(format, ap) \ -do { \ - int _argtypes[128]; \ - size_t _i, _k; \ - /* See https://github.com/google/sanitizers/issues/992 */ \ - if (HAS_FEATURE_MEMORY_SANITIZER) \ - zero(_argtypes); \ - _k = parse_printf_format((format), ELEMENTSOF(_argtypes), _argtypes); \ - assert(_k < ELEMENTSOF(_argtypes)); \ - for (_i = 0; _i < _k; _i++) { \ - if (_argtypes[_i] & PA_FLAG_PTR) { \ - (void) va_arg(ap, void*); \ - continue; \ - } \ - \ - switch (_argtypes[_i]) { \ - case PA_INT: \ - case PA_INT|PA_FLAG_SHORT: \ - case PA_CHAR: \ - (void) va_arg(ap, int); \ - break; \ - case PA_INT|PA_FLAG_LONG: \ - (void) va_arg(ap, long int); \ - break; \ - case PA_INT|PA_FLAG_LONG_LONG: \ - (void) va_arg(ap, long long int); \ - break; \ - case PA_WCHAR: \ - (void) va_arg(ap, wchar_t); \ - break; \ - case PA_WSTRING: \ - case PA_STRING: \ - case PA_POINTER: \ - (void) va_arg(ap, void*); \ - break; \ - case PA_FLOAT: \ - case PA_DOUBLE: \ - (void) va_arg(ap, double); \ - break; \ - case PA_DOUBLE|PA_FLAG_LONG_DOUBLE: \ - (void) va_arg(ap, long double); \ - break; \ - default: \ - assert_not_reached("Unknown format string argument."); \ - } \ - } \ -} while (false) diff --git a/shared/systemd/src/basic/string-table.c b/shared/systemd/src/basic/string-table.c deleted file mode 100644 index bd8047e6..00000000 --- a/shared/systemd/src/basic/string-table.c +++ /dev/null @@ -1,17 +0,0 @@ -/* SPDX-License-Identifier: LGPL-2.1-or-later */ - -#include "nm-sd-adapt-shared.h" - -#include "string-table.h" -#include "string-util.h" - -ssize_t string_table_lookup(const char * const *table, size_t len, const char *key) { - if (!key) - return -1; - - for (size_t i = 0; i < len; ++i) - if (streq_ptr(table[i], key)) - return (ssize_t) i; - - return -1; -} diff --git a/shared/systemd/src/basic/string-table.h b/shared/systemd/src/basic/string-table.h deleted file mode 100644 index ae4ea145..00000000 --- a/shared/systemd/src/basic/string-table.h +++ /dev/null @@ -1,112 +0,0 @@ -/* SPDX-License-Identifier: LGPL-2.1-or-later */ - -#pragma once - -#include <errno.h> -#include <stddef.h> -#include <stdio.h> -#include <sys/types.h> - -#include "macro.h" -#include "parse-util.h" -#include "string-util.h" - -ssize_t string_table_lookup(const char * const *table, size_t len, const char *key); - -/* For basic lookup tables with strictly enumerated entries */ -#define _DEFINE_STRING_TABLE_LOOKUP_TO_STRING(name,type,scope) \ - scope const char *name##_to_string(type i) { \ - if (i < 0 || i >= (type) ELEMENTSOF(name##_table)) \ - return NULL; \ - return name##_table[i]; \ - } - -#define _DEFINE_STRING_TABLE_LOOKUP_FROM_STRING(name,type,scope) \ - scope type name##_from_string(const char *s) { \ - return (type) string_table_lookup(name##_table, ELEMENTSOF(name##_table), s); \ - } - -#define _DEFINE_STRING_TABLE_LOOKUP_FROM_STRING_WITH_BOOLEAN(name,type,yes,scope) \ - scope type name##_from_string(const char *s) { \ - if (!s) \ - return -1; \ - int b = parse_boolean(s); \ - if (b == 0) \ - return (type) 0; \ - if (b > 0) \ - return yes; \ - return (type) string_table_lookup(name##_table, ELEMENTSOF(name##_table), s); \ - } - -#define _DEFINE_STRING_TABLE_LOOKUP_TO_STRING_FALLBACK(name,type,max,scope) \ - scope int name##_to_string_alloc(type i, char **str) { \ - char *s; \ - if (i < 0 || i > max) \ - return -ERANGE; \ - if (i < (type) ELEMENTSOF(name##_table) && name##_table[i]) { \ - s = strdup(name##_table[i]); \ - if (!s) \ - return -ENOMEM; \ - } else { \ - if (asprintf(&s, "%i", i) < 0) \ - return -ENOMEM; \ - } \ - *str = s; \ - return 0; \ - } - -#define _DEFINE_STRING_TABLE_LOOKUP_FROM_STRING_FALLBACK(name,type,max,scope) \ - scope type name##_from_string(const char *s) { \ - unsigned u = 0; \ - type i; \ - if (!s) \ - return (type) -1; \ - i = (type) string_table_lookup(name##_table, ELEMENTSOF(name##_table), s); \ - if (i >= 0) \ - return i; \ - if (safe_atou(s, &u) >= 0 && u <= max) \ - return (type) u; \ - return (type) -1; \ - } \ - -#define _DEFINE_STRING_TABLE_LOOKUP(name,type,scope) \ - _DEFINE_STRING_TABLE_LOOKUP_TO_STRING(name,type,scope) \ - _DEFINE_STRING_TABLE_LOOKUP_FROM_STRING(name,type,scope) - -#define _DEFINE_STRING_TABLE_LOOKUP_WITH_BOOLEAN(name,type,yes,scope) \ - _DEFINE_STRING_TABLE_LOOKUP_TO_STRING(name,type,scope) \ - _DEFINE_STRING_TABLE_LOOKUP_FROM_STRING_WITH_BOOLEAN(name,type,yes,scope) - -#define DEFINE_STRING_TABLE_LOOKUP(name,type) _DEFINE_STRING_TABLE_LOOKUP(name,type,) -#define DEFINE_STRING_TABLE_LOOKUP_TO_STRING(name,type) _DEFINE_STRING_TABLE_LOOKUP_TO_STRING(name,type,) -#define DEFINE_PRIVATE_STRING_TABLE_LOOKUP(name,type) _DEFINE_STRING_TABLE_LOOKUP(name,type,static) -#define DEFINE_PRIVATE_STRING_TABLE_LOOKUP_TO_STRING(name,type) _DEFINE_STRING_TABLE_LOOKUP_TO_STRING(name,type,static) -#define DEFINE_PRIVATE_STRING_TABLE_LOOKUP_FROM_STRING(name,type) _DEFINE_STRING_TABLE_LOOKUP_FROM_STRING(name,type,static) - -#define DEFINE_STRING_TABLE_LOOKUP_WITH_BOOLEAN(name,type,yes) _DEFINE_STRING_TABLE_LOOKUP_WITH_BOOLEAN(name,type,yes,) -#define DEFINE_PRIVATE_STRING_TABLE_LOOKUP_WITH_BOOLEAN(name,type,yes) _DEFINE_STRING_TABLE_LOOKUP_WITH_BOOLEAN(name,type,yes,static) - -/* For string conversions where numbers are also acceptable */ -#define DEFINE_STRING_TABLE_LOOKUP_WITH_FALLBACK(name,type,max) \ - _DEFINE_STRING_TABLE_LOOKUP_TO_STRING_FALLBACK(name,type,max,) \ - _DEFINE_STRING_TABLE_LOOKUP_FROM_STRING_FALLBACK(name,type,max,) - -#define DEFINE_PRIVATE_STRING_TABLE_LOOKUP_TO_STRING_FALLBACK(name,type,max) \ - _DEFINE_STRING_TABLE_LOOKUP_TO_STRING_FALLBACK(name,type,max,static) -#define DEFINE_PRIVATE_STRING_TABLE_LOOKUP_FROM_STRING_FALLBACK(name,type,max) \ - _DEFINE_STRING_TABLE_LOOKUP_FROM_STRING_FALLBACK(name,type,max,static) - -#define DUMP_STRING_TABLE(name,type,max) \ - do { \ - type _k; \ - flockfile(stdout); \ - for (_k = 0; _k < (max); _k++) { \ - const char *_t; \ - _t = name##_to_string(_k); \ - if (!_t) \ - continue; \ - fputs_unlocked(_t, stdout); \ - fputc_unlocked('\n', stdout); \ - } \ - funlockfile(stdout); \ - } while(false) diff --git a/shared/systemd/src/basic/string-util.c b/shared/systemd/src/basic/string-util.c deleted file mode 100644 index 744a3606..00000000 --- a/shared/systemd/src/basic/string-util.c +++ /dev/null @@ -1,1146 +0,0 @@ -/* SPDX-License-Identifier: LGPL-2.1-or-later */ - -#include "nm-sd-adapt-shared.h" - -#include <errno.h> -#include <stdarg.h> -#include <stdint.h> -#include <stdio.h> -#include <stdlib.h> - -#include "alloc-util.h" -#include "escape.h" -#include "extract-word.h" -#include "fileio.h" -#include "gunicode.h" -#include "locale-util.h" -#include "macro.h" -#include "memory-util.h" -#include "string-util.h" -#include "strv.h" -#include "terminal-util.h" -#include "utf8.h" -#include "util.h" - -int strcmp_ptr(const char *a, const char *b) { - /* Like strcmp(), but tries to make sense of NULL pointers */ - - if (a && b) - return strcmp(a, b); - return CMP(a, b); /* Direct comparison of pointers, one of which is NULL */ -} - -int strcasecmp_ptr(const char *a, const char *b) { - /* Like strcasecmp(), but tries to make sense of NULL pointers */ - - if (a && b) - return strcasecmp(a, b); - return CMP(a, b); /* Direct comparison of pointers, one of which is NULL */ -} - -char* endswith(const char *s, const char *postfix) { - size_t sl, pl; - - assert(s); - assert(postfix); - - sl = strlen(s); - pl = strlen(postfix); - - if (pl == 0) - return (char*) s + sl; - - if (sl < pl) - return NULL; - - if (memcmp(s + sl - pl, postfix, pl) != 0) - return NULL; - - return (char*) s + sl - pl; -} - -char* endswith_no_case(const char *s, const char *postfix) { - size_t sl, pl; - - assert(s); - assert(postfix); - - sl = strlen(s); - pl = strlen(postfix); - - if (pl == 0) - return (char*) s + sl; - - if (sl < pl) - return NULL; - - if (strcasecmp(s + sl - pl, postfix) != 0) - return NULL; - - return (char*) s + sl - pl; -} - -char* first_word(const char *s, const char *word) { - size_t sl, wl; - const char *p; - - assert(s); - assert(word); - - /* Checks if the string starts with the specified word, either - * followed by NUL or by whitespace. Returns a pointer to the - * NUL or the first character after the whitespace. */ - - sl = strlen(s); - wl = strlen(word); - - if (sl < wl) - return NULL; - - if (wl == 0) - return (char*) s; - - if (memcmp(s, word, wl) != 0) - return NULL; - - p = s + wl; - if (*p == 0) - return (char*) p; - - if (!strchr(WHITESPACE, *p)) - return NULL; - - p += strspn(p, WHITESPACE); - return (char*) p; -} - -char *strnappend(const char *s, const char *suffix, size_t b) { - size_t a; - char *r; - - if (!s && !suffix) - return strdup(""); - - if (!s) - return strndup(suffix, b); - - if (!suffix) - return strdup(s); - - assert(s); - assert(suffix); - - a = strlen(s); - if (b > ((size_t) -1) - a) - return NULL; - - r = new(char, a+b+1); - if (!r) - return NULL; - - memcpy(r, s, a); - memcpy(r+a, suffix, b); - r[a+b] = 0; - - return r; -} - -char *strjoin_real(const char *x, ...) { - va_list ap; - size_t l = 1; - char *r, *p; - - va_start(ap, x); - for (const char *t = x; t; t = va_arg(ap, const char *)) { - size_t n; - - n = strlen(t); - if (n > SIZE_MAX - l) { - va_end(ap); - return NULL; - } - l += n; - } - va_end(ap); - - p = r = new(char, l); - if (!r) - return NULL; - - va_start(ap, x); - for (const char *t = x; t; t = va_arg(ap, const char *)) - p = stpcpy(p, t); - va_end(ap); - - *p = 0; - - return r; -} - -#if 0 /* NM_IGNORED */ -char *strstrip(char *s) { - if (!s) - return NULL; - - /* Drops trailing whitespace. Modifies the string in place. Returns pointer to first non-space character */ - - return delete_trailing_chars(skip_leading_chars(s, WHITESPACE), WHITESPACE); -} - -char *delete_chars(char *s, const char *bad) { - char *f, *t; - - /* Drops all specified bad characters, regardless where in the string */ - - if (!s) - return NULL; - - if (!bad) - bad = WHITESPACE; - - for (f = s, t = s; *f; f++) { - if (strchr(bad, *f)) - continue; - - *(t++) = *f; - } - - *t = 0; - - return s; -} - -char *delete_trailing_chars(char *s, const char *bad) { - char *p, *c = s; - - /* Drops all specified bad characters, at the end of the string */ - - if (!s) - return NULL; - - if (!bad) - bad = WHITESPACE; - - for (p = s; *p; p++) - if (!strchr(bad, *p)) - c = p + 1; - - *c = 0; - - return s; -} -#endif /* NM_IGNORED */ - -char *truncate_nl(char *s) { - assert(s); - - s[strcspn(s, NEWLINE)] = 0; - return s; -} - -char ascii_tolower(char x) { - - if (x >= 'A' && x <= 'Z') - return x - 'A' + 'a'; - - return x; -} - -char ascii_toupper(char x) { - - if (x >= 'a' && x <= 'z') - return x - 'a' + 'A'; - - return x; -} - -char *ascii_strlower(char *t) { - char *p; - - assert(t); - - for (p = t; *p; p++) - *p = ascii_tolower(*p); - - return t; -} - -char *ascii_strupper(char *t) { - char *p; - - assert(t); - - for (p = t; *p; p++) - *p = ascii_toupper(*p); - - return t; -} - -char *ascii_strlower_n(char *t, size_t n) { - size_t i; - - if (n <= 0) - return t; - - for (i = 0; i < n; i++) - t[i] = ascii_tolower(t[i]); - - return t; -} - -int ascii_strcasecmp_n(const char *a, const char *b, size_t n) { - - for (; n > 0; a++, b++, n--) { - int x, y; - - x = (int) (uint8_t) ascii_tolower(*a); - y = (int) (uint8_t) ascii_tolower(*b); - - if (x != y) - return x - y; - } - - return 0; -} - -int ascii_strcasecmp_nn(const char *a, size_t n, const char *b, size_t m) { - int r; - - r = ascii_strcasecmp_n(a, b, MIN(n, m)); - if (r != 0) - return r; - - return CMP(n, m); -} - -bool chars_intersect(const char *a, const char *b) { - const char *p; - - /* Returns true if any of the chars in a are in b. */ - for (p = a; *p; p++) - if (strchr(b, *p)) - return true; - - return false; -} - -bool string_has_cc(const char *p, const char *ok) { - const char *t; - - assert(p); - - /* - * Check if a string contains control characters. If 'ok' is - * non-NULL it may be a string containing additional CCs to be - * considered OK. - */ - - for (t = p; *t; t++) { - if (ok && strchr(ok, *t)) - continue; - - if (*t > 0 && *t < ' ') - return true; - - if (*t == 127) - return true; - } - - return false; -} - -#if 0 /* NM_IGNORED */ -static int write_ellipsis(char *buf, bool unicode) { - if (unicode || is_locale_utf8()) { - buf[0] = 0xe2; /* tri-dot ellipsis: … */ - buf[1] = 0x80; - buf[2] = 0xa6; - } else { - buf[0] = '.'; - buf[1] = '.'; - buf[2] = '.'; - } - - return 3; -} - -static char *ascii_ellipsize_mem(const char *s, size_t old_length, size_t new_length, unsigned percent) { - size_t x, need_space, suffix_len; - char *t; - - assert(s); - assert(percent <= 100); - assert(new_length != (size_t) -1); - - if (old_length <= new_length) - return strndup(s, old_length); - - /* Special case short ellipsations */ - switch (new_length) { - - case 0: - return strdup(""); - - case 1: - if (is_locale_utf8()) - return strdup("…"); - else - return strdup("."); - - case 2: - if (!is_locale_utf8()) - return strdup(".."); - - break; - - default: - break; - } - - /* Calculate how much space the ellipsis will take up. If we are in UTF-8 mode we only need space for one - * character ("…"), otherwise for three characters ("..."). Note that in both cases we need 3 bytes of storage, - * either for the UTF-8 encoded character or for three ASCII characters. */ - need_space = is_locale_utf8() ? 1 : 3; - - t = new(char, new_length+3); - if (!t) - return NULL; - - assert(new_length >= need_space); - - x = ((new_length - need_space) * percent + 50) / 100; - assert(x <= new_length - need_space); - - memcpy(t, s, x); - write_ellipsis(t + x, false); - suffix_len = new_length - x - need_space; - memcpy(t + x + 3, s + old_length - suffix_len, suffix_len); - *(t + x + 3 + suffix_len) = '\0'; - - return t; -} - -char *ellipsize_mem(const char *s, size_t old_length, size_t new_length, unsigned percent) { - size_t x, k, len, len2; - const char *i, *j; - char *e; - int r; - - /* Note that 'old_length' refers to bytes in the string, while 'new_length' refers to character cells taken up - * on screen. This distinction doesn't matter for ASCII strings, but it does matter for non-ASCII UTF-8 - * strings. - * - * Ellipsation is done in a locale-dependent way: - * 1. If the string passed in is fully ASCII and the current locale is not UTF-8, three dots are used ("...") - * 2. Otherwise, a unicode ellipsis is used ("…") - * - * In other words: you'll get a unicode ellipsis as soon as either the string contains non-ASCII characters or - * the current locale is UTF-8. - */ - - assert(s); - assert(percent <= 100); - - if (new_length == (size_t) -1) - return strndup(s, old_length); - - if (new_length == 0) - return strdup(""); - - /* If no multibyte characters use ascii_ellipsize_mem for speed */ - if (ascii_is_valid_n(s, old_length)) - return ascii_ellipsize_mem(s, old_length, new_length, percent); - - x = ((new_length - 1) * percent) / 100; - assert(x <= new_length - 1); - - k = 0; - for (i = s; i < s + old_length; i = utf8_next_char(i)) { - char32_t c; - int w; - - r = utf8_encoded_to_unichar(i, &c); - if (r < 0) - return NULL; - - w = unichar_iswide(c) ? 2 : 1; - if (k + w <= x) - k += w; - else - break; - } - - for (j = s + old_length; j > i; ) { - char32_t c; - int w; - const char *jj; - - jj = utf8_prev_char(j); - r = utf8_encoded_to_unichar(jj, &c); - if (r < 0) - return NULL; - - w = unichar_iswide(c) ? 2 : 1; - if (k + w <= new_length) { - k += w; - j = jj; - } else - break; - } - assert(i <= j); - - /* we don't actually need to ellipsize */ - if (i == j) - return memdup_suffix0(s, old_length); - - /* make space for ellipsis, if possible */ - if (j < s + old_length) - j = utf8_next_char(j); - else if (i > s) - i = utf8_prev_char(i); - - len = i - s; - len2 = s + old_length - j; - e = new(char, len + 3 + len2 + 1); - if (!e) - return NULL; - - /* - printf("old_length=%zu new_length=%zu x=%zu len=%u len2=%u k=%u\n", - old_length, new_length, x, len, len2, k); - */ - - memcpy(e, s, len); - write_ellipsis(e + len, true); - memcpy(e + len + 3, j, len2); - *(e + len + 3 + len2) = '\0'; - - return e; -} - -char *cellescape(char *buf, size_t len, const char *s) { - /* Escape and ellipsize s into buffer buf of size len. Only non-control ASCII - * characters are copied as they are, everything else is escaped. The result - * is different then if escaping and ellipsization was performed in two - * separate steps, because each sequence is either stored in full or skipped. - * - * This function should be used for logging about strings which expected to - * be plain ASCII in a safe way. - * - * An ellipsis will be used if s is too long. It was always placed at the - * very end. - */ - - size_t i = 0, last_char_width[4] = {}, k = 0, j; - - assert(len > 0); /* at least a terminating NUL */ - - for (;;) { - char four[4]; - int w; - - if (*s == 0) /* terminating NUL detected? then we are done! */ - goto done; - - w = cescape_char(*s, four); - if (i + w + 1 > len) /* This character doesn't fit into the buffer anymore? In that case let's - * ellipsize at the previous location */ - break; - - /* OK, there was space, let's add this escaped character to the buffer */ - memcpy(buf + i, four, w); - i += w; - - /* And remember its width in the ring buffer */ - last_char_width[k] = w; - k = (k + 1) % 4; - - s++; - } - - /* Ellipsation is necessary. This means we might need to truncate the string again to make space for 4 - * characters ideally, but the buffer is shorter than that in the first place take what we can get */ - for (j = 0; j < ELEMENTSOF(last_char_width); j++) { - - if (i + 4 <= len) /* nice, we reached our space goal */ - break; - - k = k == 0 ? 3 : k - 1; - if (last_char_width[k] == 0) /* bummer, we reached the beginning of the strings */ - break; - - assert(i >= last_char_width[k]); - i -= last_char_width[k]; - } - - if (i + 4 <= len) /* yay, enough space */ - i += write_ellipsis(buf + i, false); - else if (i + 3 <= len) { /* only space for ".." */ - buf[i++] = '.'; - buf[i++] = '.'; - } else if (i + 2 <= len) /* only space for a single "." */ - buf[i++] = '.'; - else - assert(i + 1 <= len); - - done: - buf[i] = '\0'; - return buf; -} -#endif /* NM_IGNORED */ - -char* strshorten(char *s, size_t l) { - assert(s); - - if (strnlen(s, l+1) > l) - s[l] = 0; - - return s; -} - -char *strreplace(const char *text, const char *old_string, const char *new_string) { - size_t l, old_len, new_len, allocated = 0; - char *t, *ret = NULL; - const char *f; - - assert(old_string); - assert(new_string); - - if (!text) - return NULL; - - old_len = strlen(old_string); - new_len = strlen(new_string); - - l = strlen(text); - if (!GREEDY_REALLOC(ret, allocated, l+1)) - return NULL; - - f = text; - t = ret; - while (*f) { - size_t d, nl; - - if (!startswith(f, old_string)) { - *(t++) = *(f++); - continue; - } - - d = t - ret; - nl = l - old_len + new_len; - - if (!GREEDY_REALLOC(ret, allocated, nl + 1)) - return mfree(ret); - - l = nl; - t = ret + d; - - t = stpcpy(t, new_string); - f += old_len; - } - - *t = 0; - return ret; -} - -#if 0 /* NM_IGNORED */ -static void advance_offsets( - ssize_t diff, - size_t offsets[2], /* note: we can't use [static 2] here, since this may be NULL */ - size_t shift[static 2], - size_t size) { - - if (!offsets) - return; - - assert(shift); - - if ((size_t) diff < offsets[0]) - shift[0] += size; - if ((size_t) diff < offsets[1]) - shift[1] += size; -} - -char *strip_tab_ansi(char **ibuf, size_t *_isz, size_t highlight[2]) { - const char *begin = NULL; - enum { - STATE_OTHER, - STATE_ESCAPE, - STATE_CSI, - STATE_CSO, - } state = STATE_OTHER; - char *obuf = NULL; - size_t osz = 0, isz, shift[2] = {}, n_carriage_returns = 0; - FILE *f; - - assert(ibuf); - assert(*ibuf); - - /* This does three things: - * - * 1. Replaces TABs by 8 spaces - * 2. Strips ANSI color sequences (a subset of CSI), i.e. ESC '[' … 'm' sequences - * 3. Strips ANSI operating system sequences (CSO), i.e. ESC ']' … BEL sequences - * 4. Strip trailing \r characters (since they would "move the cursor", but have no - * other effect). - * - * Everything else will be left as it is. In particular other ANSI sequences are left as they are, as - * are any other special characters. Truncated ANSI sequences are left-as is too. This call is - * supposed to suppress the most basic formatting noise, but nothing else. - * - * Why care for CSO sequences? Well, to undo what terminal_urlify() and friends generate. */ - - isz = _isz ? *_isz : strlen(*ibuf); - - /* Note we turn off internal locking on f for performance reasons. It's safe to do so since we - * created f here and it doesn't leave our scope. */ - f = open_memstream_unlocked(&obuf, &osz); - if (!f) - return NULL; - - for (const char *i = *ibuf; i < *ibuf + isz + 1; i++) { - - switch (state) { - - case STATE_OTHER: - if (i >= *ibuf + isz) /* EOT */ - break; - - if (*i == '\r') { - n_carriage_returns++; - break; - } else if (*i == '\n') - /* Ignore carriage returns before new line */ - n_carriage_returns = 0; - for (; n_carriage_returns > 0; n_carriage_returns--) - fputc('\r', f); - - if (*i == '\x1B') - state = STATE_ESCAPE; - else if (*i == '\t') { - fputs(" ", f); - advance_offsets(i - *ibuf, highlight, shift, 7); - } else - fputc(*i, f); - - break; - - case STATE_ESCAPE: - assert(n_carriage_returns == 0); - - if (i >= *ibuf + isz) { /* EOT */ - fputc('\x1B', f); - advance_offsets(i - *ibuf, highlight, shift, 1); - break; - } else if (*i == '[') { /* ANSI CSI */ - state = STATE_CSI; - begin = i + 1; - } else if (*i == ']') { /* ANSI CSO */ - state = STATE_CSO; - begin = i + 1; - } else { - fputc('\x1B', f); - fputc(*i, f); - advance_offsets(i - *ibuf, highlight, shift, 1); - state = STATE_OTHER; - } - - break; - - case STATE_CSI: - assert(n_carriage_returns == 0); - - if (i >= *ibuf + isz || /* EOT … */ - !strchr("01234567890;m", *i)) { /* … or invalid chars in sequence */ - fputc('\x1B', f); - fputc('[', f); - advance_offsets(i - *ibuf, highlight, shift, 2); - state = STATE_OTHER; - i = begin-1; - } else if (*i == 'm') - state = STATE_OTHER; - - break; - - case STATE_CSO: - assert(n_carriage_returns == 0); - - if (i >= *ibuf + isz || /* EOT … */ - (*i != '\a' && (uint8_t) *i < 32U) || (uint8_t) *i > 126U) { /* … or invalid chars in sequence */ - fputc('\x1B', f); - fputc(']', f); - advance_offsets(i - *ibuf, highlight, shift, 2); - state = STATE_OTHER; - i = begin-1; - } else if (*i == '\a') - state = STATE_OTHER; - - break; - } - } - - if (fflush_and_check(f) < 0) { - fclose(f); - return mfree(obuf); - } - fclose(f); - - free_and_replace(*ibuf, obuf); - - if (_isz) - *_isz = osz; - - if (highlight) { - highlight[0] += shift[0]; - highlight[1] += shift[1]; - } - - return *ibuf; -} - -char *strextend_with_separator_internal(char **x, const char *separator, ...) { - size_t f, l, l_separator; - bool need_separator; - char *nr, *p; - va_list ap; - - assert(x); - - l = f = strlen_ptr(*x); - - need_separator = !isempty(*x); - l_separator = strlen_ptr(separator); - - va_start(ap, separator); - for (;;) { - const char *t; - size_t n; - - t = va_arg(ap, const char *); - if (!t) - break; - - n = strlen(t); - - if (need_separator) - n += l_separator; - - if (n >= SIZE_MAX - l) { - va_end(ap); - return NULL; - } - - l += n; - need_separator = true; - } - va_end(ap); - - need_separator = !isempty(*x); - - nr = realloc(*x, GREEDY_ALLOC_ROUND_UP(l+1)); - if (!nr) - return NULL; - - *x = nr; - p = nr + f; - - va_start(ap, separator); - for (;;) { - const char *t; - - t = va_arg(ap, const char *); - if (!t) - break; - - if (need_separator && separator) - p = stpcpy(p, separator); - - p = stpcpy(p, t); - - need_separator = true; - } - va_end(ap); - - assert(p == nr + l); - - *p = 0; - - return p; -} -#endif /* NM_IGNORED */ - -char *strrep(const char *s, unsigned n) { - size_t l; - char *r, *p; - unsigned i; - - assert(s); - - l = strlen(s); - p = r = malloc(l * n + 1); - if (!r) - return NULL; - - for (i = 0; i < n; i++) - p = stpcpy(p, s); - - *p = 0; - return r; -} - -int split_pair(const char *s, const char *sep, char **l, char **r) { - char *x, *a, *b; - - assert(s); - assert(sep); - assert(l); - assert(r); - - if (isempty(sep)) - return -EINVAL; - - x = strstr(s, sep); - if (!x) - return -EINVAL; - - a = strndup(s, x - s); - if (!a) - return -ENOMEM; - - b = strdup(x + strlen(sep)); - if (!b) { - free(a); - return -ENOMEM; - } - - *l = a; - *r = b; - - return 0; -} - -int free_and_strdup(char **p, const char *s) { - char *t; - - assert(p); - - /* Replaces a string pointer with a strdup()ed new string, - * possibly freeing the old one. */ - - if (streq_ptr(*p, s)) - return 0; - - if (s) { - t = strdup(s); - if (!t) - return -ENOMEM; - } else - t = NULL; - - free(*p); - *p = t; - - return 1; -} - -int free_and_strndup(char **p, const char *s, size_t l) { - char *t; - - assert(p); - assert(s || l == 0); - - /* Replaces a string pointer with a strndup()ed new string, - * freeing the old one. */ - - if (!*p && !s) - return 0; - - if (*p && s && strneq(*p, s, l) && (l > strlen(*p) || (*p)[l] == '\0')) - return 0; - - if (s) { - t = strndup(s, l); - if (!t) - return -ENOMEM; - } else - t = NULL; - - free_and_replace(*p, t); - return 1; -} - -bool string_is_safe(const char *p) { - const char *t; - - if (!p) - return false; - - /* Checks if the specified string contains no quotes or control characters */ - - for (t = p; *t; t++) { - if (*t > 0 && *t < ' ') /* no control characters */ - return false; - - if (strchr(QUOTES "\\\x7f", *t)) - return false; - } - - return true; -} - -#if 0 /* NM_IGNORED */ -char* string_erase(char *x) { - if (!x) - return NULL; - - /* A delicious drop of snake-oil! To be called on memory where we stored passphrases or so, after we - * used them. */ - explicit_bzero_safe(x, strlen(x)); - return x; -} - -int string_truncate_lines(const char *s, size_t n_lines, char **ret) { - const char *p = s, *e = s; - bool truncation_applied = false; - char *copy; - size_t n = 0; - - assert(s); - - /* Truncate after the specified number of lines. Returns > 0 if a truncation was applied or == 0 if - * there were fewer lines in the string anyway. Trailing newlines on input are ignored, and not - * generated either. */ - - for (;;) { - size_t k; - - k = strcspn(p, "\n"); - - if (p[k] == 0) { - if (k == 0) /* final empty line */ - break; - - if (n >= n_lines) /* above threshold */ - break; - - e = p + k; /* last line to include */ - break; - } - - assert(p[k] == '\n'); - - if (n >= n_lines) - break; - - if (k > 0) - e = p + k; - - p += k + 1; - n++; - } - - /* e points after the last character we want to keep */ - if (isempty(e)) - copy = strdup(s); - else { - if (!in_charset(e, "\n")) /* We only consider things truncated if we remove something that - * isn't a new-line or a series of them */ - truncation_applied = true; - - copy = strndup(s, e - s); - } - if (!copy) - return -ENOMEM; - - *ret = copy; - return truncation_applied; -} - -int string_extract_line(const char *s, size_t i, char **ret) { - const char *p = s; - size_t c = 0; - - /* Extract the i'nth line from the specified string. Returns > 0 if there are more lines after that, - * and == 0 if we are looking at the last line or already beyond the last line. As special - * optimization, if the first line is requested and the string only consists of one line we return - * NULL, indicating the input string should be used as is, and avoid a memory allocation for a very - * common case. */ - - for (;;) { - const char *q; - - q = strchr(p, '\n'); - if (i == c) { - /* The line we are looking for! */ - - if (q) { - char *m; - - m = strndup(p, q - p); - if (!m) - return -ENOMEM; - - *ret = m; - return !isempty(q + 1); /* more coming? */ - } else { - if (p == s) - *ret = NULL; /* Just use the input string */ - else { - char *m; - - m = strdup(p); - if (!m) - return -ENOMEM; - - *ret = m; - } - - return 0; /* The end */ - } - } - - if (!q) { - char *m; - - /* No more lines, return empty line */ - - m = strdup(""); - if (!m) - return -ENOMEM; - - *ret = m; - return 0; /* The end */ - } - - p = q + 1; - c++; - } -} - -int string_contains_word_strv(const char *string, const char *separators, char **words, const char **ret_word) { - /* In the default mode with no separators specified, we split on whitespace and - * don't coalesce separators. */ - const ExtractFlags flags = separators ? EXTRACT_DONT_COALESCE_SEPARATORS : 0; - - const char *found = NULL; - - for (const char *p = string;;) { - _cleanup_free_ char *w = NULL; - int r; - - r = extract_first_word(&p, &w, separators, flags); - if (r < 0) - return r; - if (r == 0) - break; - - found = strv_find(words, w); - if (found) - break; - } - - if (ret_word) - *ret_word = found; - return !!found; -} -#endif /* NM_IGNORED */ diff --git a/shared/systemd/src/basic/string-util.h b/shared/systemd/src/basic/string-util.h deleted file mode 100644 index 593cf04a..00000000 --- a/shared/systemd/src/basic/string-util.h +++ /dev/null @@ -1,280 +0,0 @@ -/* SPDX-License-Identifier: LGPL-2.1-or-later */ -#pragma once - -#include <stdbool.h> -#include <stddef.h> -#include <string.h> - -#include "alloc-util.h" -#include "macro.h" - -/* What is interpreted as whitespace? */ -#define WHITESPACE " \t\n\r" -#define NEWLINE "\n\r" -#define QUOTES "\"\'" -#define COMMENTS "#;" -#define GLOB_CHARS "*?[" -#define DIGITS "0123456789" -#define LOWERCASE_LETTERS "abcdefghijklmnopqrstuvwxyz" -#define UPPERCASE_LETTERS "ABCDEFGHIJKLMNOPQRSTUVWXYZ" -#define LETTERS LOWERCASE_LETTERS UPPERCASE_LETTERS -#define ALPHANUMERICAL LETTERS DIGITS -#define HEXDIGITS DIGITS "abcdefABCDEF" - -#define streq(a,b) (strcmp((a),(b)) == 0) -#define strneq(a, b, n) (strncmp((a), (b), (n)) == 0) -#define strcaseeq(a,b) (strcasecmp((a),(b)) == 0) -#define strncaseeq(a, b, n) (strncasecmp((a), (b), (n)) == 0) - -int strcmp_ptr(const char *a, const char *b) _pure_; -int strcasecmp_ptr(const char *a, const char *b) _pure_; - -static inline bool streq_ptr(const char *a, const char *b) { - return strcmp_ptr(a, b) == 0; -} - -static inline char* strstr_ptr(const char *haystack, const char *needle) { - if (!haystack || !needle) - return NULL; - return strstr(haystack, needle); -} - -static inline const char* strempty(const char *s) { - return s ?: ""; -} - -static inline const char* strnull(const char *s) { - return s ?: "(null)"; -} - -static inline const char *strna(const char *s) { - return s ?: "n/a"; -} - -static inline const char* yes_no(bool b) { - return b ? "yes" : "no"; -} - -static inline const char* true_false(bool b) { - return b ? "true" : "false"; -} - -static inline const char* plus_minus(bool b) { - return b ? "+" : "-"; -} - -static inline const char* one_zero(bool b) { - return b ? "1" : "0"; -} - -static inline const char* enable_disable(bool b) { - return b ? "enable" : "disable"; -} - -static inline bool isempty(const char *p) { - return !p || !p[0]; -} - -static inline const char *empty_to_null(const char *p) { - return isempty(p) ? NULL : p; -} - -static inline const char *empty_to_dash(const char *str) { - return isempty(str) ? "-" : str; -} - -static inline bool empty_or_dash(const char *str) { - return !str || - str[0] == 0 || - (str[0] == '-' && str[1] == 0); -} - -static inline const char *empty_or_dash_to_null(const char *p) { - return empty_or_dash(p) ? NULL : p; -} - -static inline char *startswith(const char *s, const char *prefix) { - size_t l; - - l = strlen(prefix); - if (strncmp(s, prefix, l) == 0) - return (char*) s + l; - - return NULL; -} - -static inline char *startswith_no_case(const char *s, const char *prefix) { - size_t l; - - l = strlen(prefix); - if (strncasecmp(s, prefix, l) == 0) - return (char*) s + l; - - return NULL; -} - -char *endswith(const char *s, const char *postfix) _pure_; -char *endswith_no_case(const char *s, const char *postfix) _pure_; - -char *first_word(const char *s, const char *word) _pure_; - -char *strnappend(const char *s, const char *suffix, size_t length); - -char *strjoin_real(const char *x, ...) _sentinel_; -#define strjoin(a, ...) strjoin_real((a), __VA_ARGS__, NULL) - -#define strjoina(a, ...) \ - ({ \ - const char *_appendees_[] = { a, __VA_ARGS__ }; \ - char *_d_, *_p_; \ - size_t _len_ = 0; \ - size_t _i_; \ - for (_i_ = 0; _i_ < ELEMENTSOF(_appendees_) && _appendees_[_i_]; _i_++) \ - _len_ += strlen(_appendees_[_i_]); \ - _p_ = _d_ = newa(char, _len_ + 1); \ - for (_i_ = 0; _i_ < ELEMENTSOF(_appendees_) && _appendees_[_i_]; _i_++) \ - _p_ = stpcpy(_p_, _appendees_[_i_]); \ - *_p_ = 0; \ - _d_; \ - }) - -char *strstrip(char *s); -char *delete_chars(char *s, const char *bad); -char *delete_trailing_chars(char *s, const char *bad); -char *truncate_nl(char *s); - -static inline char *skip_leading_chars(const char *s, const char *bad) { - if (!s) - return NULL; - - if (!bad) - bad = WHITESPACE; - - return (char*) s + strspn(s, bad); -} - -char ascii_tolower(char x); -char *ascii_strlower(char *s); -char *ascii_strlower_n(char *s, size_t n); - -char ascii_toupper(char x); -char *ascii_strupper(char *s); - -int ascii_strcasecmp_n(const char *a, const char *b, size_t n); -int ascii_strcasecmp_nn(const char *a, size_t n, const char *b, size_t m); - -bool chars_intersect(const char *a, const char *b) _pure_; - -static inline bool _pure_ in_charset(const char *s, const char* charset) { - assert(s); - assert(charset); - return s[strspn(s, charset)] == '\0'; -} - -bool string_has_cc(const char *p, const char *ok) _pure_; - -char *ellipsize_mem(const char *s, size_t old_length_bytes, size_t new_length_columns, unsigned percent); -static inline char *ellipsize(const char *s, size_t length, unsigned percent) { - return ellipsize_mem(s, strlen(s), length, percent); -} - -char *cellescape(char *buf, size_t len, const char *s); - -/* This limit is arbitrary, enough to give some idea what the string contains */ -#define CELLESCAPE_DEFAULT_LENGTH 64 - -char* strshorten(char *s, size_t l); - -char *strreplace(const char *text, const char *old_string, const char *new_string); - -char *strip_tab_ansi(char **ibuf, size_t *_isz, size_t highlight[2]); - -char *strextend_with_separator_internal(char **x, const char *separator, ...) _sentinel_; - -#define strextend_with_separator(x, separator, ...) strextend_with_separator_internal(x, separator, __VA_ARGS__, NULL) -#define strextend(x, ...) strextend_with_separator_internal(x, NULL, __VA_ARGS__, NULL) - -char *strrep(const char *s, unsigned n); - -int split_pair(const char *s, const char *sep, char **l, char **r); - -int free_and_strdup(char **p, const char *s); -static inline int free_and_strdup_warn(char **p, const char *s) { - if (free_and_strdup(p, s) < 0) - return log_oom(); - return 0; -} -int free_and_strndup(char **p, const char *s, size_t l); - -bool string_is_safe(const char *p) _pure_; - -static inline size_t strlen_ptr(const char *s) { - if (!s) - return 0; - - return strlen(s); -} - -DISABLE_WARNING_STRINGOP_TRUNCATION; -static inline void strncpy_exact(char *buf, const char *src, size_t buf_len) { - strncpy(buf, src, buf_len); -} -REENABLE_WARNING; - -/* Like startswith(), but operates on arbitrary memory blocks */ -static inline void *memory_startswith(const void *p, size_t sz, const char *token) { - assert(token); - - size_t n = strlen(token); - if (sz < n) - return NULL; - - assert(p); - - if (memcmp(p, token, n) != 0) - return NULL; - - return (uint8_t*) p + n; -} - -/* Like startswith_no_case(), but operates on arbitrary memory blocks. - * It works only for ASCII strings. - */ -static inline void *memory_startswith_no_case(const void *p, size_t sz, const char *token) { - assert(token); - - size_t n = strlen(token); - if (sz < n) - return NULL; - - assert(p); - - for (size_t i = 0; i < n; i++) - if (ascii_tolower(((char *)p)[i]) != ascii_tolower(token[i])) - return NULL; - - return (uint8_t*) p + n; -} - -static inline char* str_realloc(char **p) { - /* Reallocate *p to actual size */ - - if (!*p) - return NULL; - - char *t = realloc(*p, strlen(*p) + 1); - if (!t) - return NULL; - - return (*p = t); -} - -char* string_erase(char *x); - -int string_truncate_lines(const char *s, size_t n_lines, char **ret); -int string_extract_line(const char *s, size_t i, char **ret); - -int string_contains_word_strv(const char *string, const char *separators, char **words, const char **ret_word); -static inline int string_contains_word(const char *string, const char *separators, const char *word) { - return string_contains_word_strv(string, separators, STRV_MAKE(word), NULL); -} diff --git a/shared/systemd/src/basic/strv.c b/shared/systemd/src/basic/strv.c deleted file mode 100644 index 7d3e3fc7..00000000 --- a/shared/systemd/src/basic/strv.c +++ /dev/null @@ -1,1005 +0,0 @@ -/* SPDX-License-Identifier: LGPL-2.1-or-later */ - -#include "nm-sd-adapt-shared.h" - -#include <errno.h> -#include <fnmatch.h> -#include <stdarg.h> -#include <stdio.h> -#include <stdlib.h> - -#include "alloc-util.h" -#include "escape.h" -#include "extract-word.h" -#include "fileio.h" -#include "memory-util.h" -#include "nulstr-util.h" -#include "sort-util.h" -#include "string-util.h" -#include "strv.h" - -char *strv_find(char * const *l, const char *name) { - char * const *i; - - assert(name); - - STRV_FOREACH(i, l) - if (streq(*i, name)) - return *i; - - return NULL; -} - -char *strv_find_case(char * const *l, const char *name) { - char * const *i; - - assert(name); - - STRV_FOREACH(i, l) - if (strcaseeq(*i, name)) - return *i; - - return NULL; -} - -char *strv_find_prefix(char * const *l, const char *name) { - char * const *i; - - assert(name); - - STRV_FOREACH(i, l) - if (startswith(*i, name)) - return *i; - - return NULL; -} - -char *strv_find_startswith(char * const *l, const char *name) { - char * const *i, *e; - - assert(name); - - /* Like strv_find_prefix, but actually returns only the - * suffix, not the whole item */ - - STRV_FOREACH(i, l) { - e = startswith(*i, name); - if (e) - return e; - } - - return NULL; -} - -char **strv_free(char **l) { - char **k; - - if (!l) - return NULL; - - for (k = l; *k; k++) - free(*k); - - return mfree(l); -} - -char **strv_free_erase(char **l) { - char **i; - - STRV_FOREACH(i, l) - erase_and_freep(i); - - return mfree(l); -} - -char **strv_copy(char * const *l) { - char **r, **k; - - k = r = new(char*, strv_length(l) + 1); - if (!r) - return NULL; - - if (l) - for (; *l; k++, l++) { - *k = strdup(*l); - if (!*k) { - strv_free(r); - return NULL; - } - } - - *k = NULL; - return r; -} - -size_t strv_length(char * const *l) { - size_t n = 0; - - if (!l) - return 0; - - for (; *l; l++) - n++; - - return n; -} - -char **strv_new_ap(const char *x, va_list ap) { - _cleanup_strv_free_ char **a = NULL; - size_t n = 0, i = 0; - va_list aq; - - /* As a special trick we ignore all listed strings that equal - * STRV_IGNORE. This is supposed to be used with the - * STRV_IFNOTNULL() macro to include possibly NULL strings in - * the string list. */ - - va_copy(aq, ap); - for (const char *s = x; s; s = va_arg(aq, const char*)) { - if (s == STRV_IGNORE) - continue; - - n++; - } - va_end(aq); - - a = new(char*, n+1); - if (!a) - return NULL; - - for (const char *s = x; s; s = va_arg(ap, const char*)) { - if (s == STRV_IGNORE) - continue; - - a[i] = strdup(s); - if (!a[i]) - return NULL; - - i++; - } - - a[i] = NULL; - - return TAKE_PTR(a); -} - -char **strv_new_internal(const char *x, ...) { - char **r; - va_list ap; - - va_start(ap, x); - r = strv_new_ap(x, ap); - va_end(ap); - - return r; -} - -int strv_extend_strv(char ***a, char * const *b, bool filter_duplicates) { - char * const *s, **t; - size_t p, q, i = 0, j; - - assert(a); - - if (strv_isempty(b)) - return 0; - - p = strv_length(*a); - q = strv_length(b); - - if (p >= SIZE_MAX - q) - return -ENOMEM; - - t = reallocarray(*a, GREEDY_ALLOC_ROUND_UP(p + q + 1), sizeof(char *)); - if (!t) - return -ENOMEM; - - t[p] = NULL; - *a = t; - - STRV_FOREACH(s, b) { - - if (filter_duplicates && strv_contains(t, *s)) - continue; - - t[p+i] = strdup(*s); - if (!t[p+i]) - goto rollback; - - i++; - t[p+i] = NULL; - } - - assert(i <= q); - - return (int) i; - -rollback: - for (j = 0; j < i; j++) - free(t[p + j]); - - t[p] = NULL; - return -ENOMEM; -} - -#if 0 /* NM_IGNORED */ -int strv_extend_strv_concat(char ***a, char * const *b, const char *suffix) { - char * const *s; - int r; - - STRV_FOREACH(s, b) { - char *v; - - v = strjoin(*s, suffix); - if (!v) - return -ENOMEM; - - r = strv_push(a, v); - if (r < 0) { - free(v); - return r; - } - } - - return 0; -} -#endif /* NM_IGNORED */ - -char **strv_split_newlines(const char *s) { - char **l; - size_t n; - - assert(s); - - /* Special version of strv_split() that splits on newlines and - * suppresses an empty string at the end */ - - l = strv_split(s, NEWLINE); - if (!l) - return NULL; - - n = strv_length(l); - if (n <= 0) - return l; - - if (isempty(l[n - 1])) - l[n - 1] = mfree(l[n - 1]); - - return l; -} - -int strv_split_full(char ***t, const char *s, const char *separators, ExtractFlags flags) { - _cleanup_strv_free_ char **l = NULL; - size_t n = 0, allocated = 0; - int r; - - assert(t); - assert(s); - - for (;;) { - _cleanup_free_ char *word = NULL; - - r = extract_first_word(&s, &word, separators, flags); - if (r < 0) - return r; - if (r == 0) - break; - - if (!GREEDY_REALLOC(l, allocated, n + 2)) - return -ENOMEM; - - l[n++] = TAKE_PTR(word); - - l[n] = NULL; - } - - if (!l) { - l = new0(char*, 1); - if (!l) - return -ENOMEM; - } - - *t = TAKE_PTR(l); - - return (int) n; -} - -#if 0 /* NM_IGNORED */ -int strv_split_colon_pairs(char ***t, const char *s) { - _cleanup_strv_free_ char **l = NULL; - size_t n = 0, allocated = 0; - int r; - - assert(t); - assert(s); - - for (;;) { - _cleanup_free_ char *first = NULL, *second = NULL, *tuple = NULL, *second_or_empty = NULL; - - r = extract_first_word(&s, &tuple, NULL, EXTRACT_UNQUOTE|EXTRACT_RETAIN_ESCAPE); - if (r < 0) - return r; - if (r == 0) - break; - - const char *p = tuple; - r = extract_many_words(&p, ":", EXTRACT_CUNESCAPE|EXTRACT_UNESCAPE_SEPARATORS, - &first, &second, NULL); - if (r < 0) - return r; - if (r == 0) - continue; - /* Enforce that at most 2 colon-separated words are contained in each group */ - if (!isempty(p)) - return -EINVAL; - - second_or_empty = strdup(strempty(second)); - if (!second_or_empty) - return -ENOMEM; - - if (!GREEDY_REALLOC(l, allocated, n + 3)) - return -ENOMEM; - - l[n++] = TAKE_PTR(first); - l[n++] = TAKE_PTR(second_or_empty); - - l[n] = NULL; - } - - if (!l) { - l = new0(char*, 1); - if (!l) - return -ENOMEM; - } - - *t = TAKE_PTR(l); - - return (int) n; -} -#endif /* NM_IGNORED */ - -char *strv_join_full(char * const *l, const char *separator, const char *prefix, bool unescape_separators) { - char * const *s; - char *r, *e; - size_t n, k, m; - - if (!separator) - separator = " "; - - k = strlen(separator); - m = strlen_ptr(prefix); - - if (unescape_separators) /* If there separator is multi-char, we won't know how to escape it. */ - assert(k == 1); - - n = 0; - STRV_FOREACH(s, l) { - if (s != l) - n += k; - - bool needs_escaping = unescape_separators && strchr(*s, separator[0]); - - n += m + strlen(*s) * (1 + needs_escaping); - } - - r = new(char, n+1); - if (!r) - return NULL; - - e = r; - STRV_FOREACH(s, l) { - if (s != l) - e = stpcpy(e, separator); - - if (prefix) - e = stpcpy(e, prefix); - - bool needs_escaping = unescape_separators && strchr(*s, separator[0]); - - if (needs_escaping) - for (size_t i = 0; (*s)[i]; i++) { - if ((*s)[i] == separator[0]) - *(e++) = '\\'; - *(e++) = (*s)[i]; - } - else - e = stpcpy(e, *s); - } - - *e = 0; - - return r; -} - -int strv_push(char ***l, char *value) { - char **c; - size_t n; - - if (!value) - return 0; - - n = strv_length(*l); - - /* Check for overflow */ - if (n > SIZE_MAX-2) - return -ENOMEM; - - c = reallocarray(*l, GREEDY_ALLOC_ROUND_UP(n + 2), sizeof(char*)); - if (!c) - return -ENOMEM; - - c[n] = value; - c[n+1] = NULL; - - *l = c; - return 0; -} - -int strv_push_pair(char ***l, char *a, char *b) { - char **c; - size_t n; - - if (!a && !b) - return 0; - - n = strv_length(*l); - - /* Check for overflow */ - if (n > SIZE_MAX-3) - return -ENOMEM; - - /* increase and check for overflow */ - c = reallocarray(*l, GREEDY_ALLOC_ROUND_UP(n + !!a + !!b + 1), sizeof(char*)); - if (!c) - return -ENOMEM; - - if (a) - c[n++] = a; - if (b) - c[n++] = b; - c[n] = NULL; - - *l = c; - return 0; -} - -int strv_insert(char ***l, size_t position, char *value) { - char **c; - size_t n, m, i; - - if (!value) - return 0; - - n = strv_length(*l); - position = MIN(position, n); - - /* increase and check for overflow */ - m = n + 2; - if (m < n) - return -ENOMEM; - - c = new(char*, m); - if (!c) - return -ENOMEM; - - for (i = 0; i < position; i++) - c[i] = (*l)[i]; - c[position] = value; - for (i = position; i < n; i++) - c[i+1] = (*l)[i]; - - c[n+1] = NULL; - - free(*l); - *l = c; - - return 0; -} - -int strv_consume(char ***l, char *value) { - int r; - - r = strv_push(l, value); - if (r < 0) - free(value); - - return r; -} - -int strv_consume_pair(char ***l, char *a, char *b) { - int r; - - r = strv_push_pair(l, a, b); - if (r < 0) { - free(a); - free(b); - } - - return r; -} - -int strv_consume_prepend(char ***l, char *value) { - int r; - - r = strv_push_prepend(l, value); - if (r < 0) - free(value); - - return r; -} - -int strv_prepend(char ***l, const char *value) { - char *v; - - if (!value) - return 0; - - v = strdup(value); - if (!v) - return -ENOMEM; - - return strv_consume_prepend(l, v); -} - -int strv_extend(char ***l, const char *value) { - char *v; - - if (!value) - return 0; - - v = strdup(value); - if (!v) - return -ENOMEM; - - return strv_consume(l, v); -} - -int strv_extend_front(char ***l, const char *value) { - size_t n, m; - char *v, **c; - - assert(l); - - /* Like strv_extend(), but prepends rather than appends the new entry */ - - if (!value) - return 0; - - n = strv_length(*l); - - /* Increase and overflow check. */ - m = n + 2; - if (m < n) - return -ENOMEM; - - v = strdup(value); - if (!v) - return -ENOMEM; - - c = reallocarray(*l, m, sizeof(char*)); - if (!c) { - free(v); - return -ENOMEM; - } - - memmove(c+1, c, n * sizeof(char*)); - c[0] = v; - c[n+1] = NULL; - - *l = c; - return 0; -} - -char **strv_uniq(char **l) { - char **i; - - /* Drops duplicate entries. The first identical string will be - * kept, the others dropped */ - - STRV_FOREACH(i, l) - strv_remove(i+1, *i); - - return l; -} - -bool strv_is_uniq(char * const *l) { - char * const *i; - - STRV_FOREACH(i, l) - if (strv_find(i+1, *i)) - return false; - - return true; -} - -char **strv_remove(char **l, const char *s) { - char **f, **t; - - if (!l) - return NULL; - - assert(s); - - /* Drops every occurrence of s in the string list, edits - * in-place. */ - - for (f = t = l; *f; f++) - if (streq(*f, s)) - free(*f); - else - *(t++) = *f; - - *t = NULL; - return l; -} - -char **strv_parse_nulstr(const char *s, size_t l) { - /* l is the length of the input data, which will be split at NULs into - * elements of the resulting strv. Hence, the number of items in the resulting strv - * will be equal to one plus the number of NUL bytes in the l bytes starting at s, - * unless s[l-1] is NUL, in which case the final empty string is not stored in - * the resulting strv, and length is equal to the number of NUL bytes. - * - * Note that contrary to a normal nulstr which cannot contain empty strings, because - * the input data is terminated by any two consequent NUL bytes, this parser accepts - * empty strings in s. - */ - - const char *p; - size_t c = 0, i = 0; - char **v; - - assert(s || l <= 0); - - if (l <= 0) - return new0(char*, 1); - - for (p = s; p < s + l; p++) - if (*p == 0) - c++; - - if (s[l-1] != 0) - c++; - - v = new0(char*, c+1); - if (!v) - return NULL; - - p = s; - while (p < s + l) { - const char *e; - - e = memchr(p, 0, s + l - p); - - v[i] = strndup(p, e ? e - p : s + l - p); - if (!v[i]) { - strv_free(v); - return NULL; - } - - i++; - - if (!e) - break; - - p = e + 1; - } - - assert(i == c); - - return v; -} - -#if 0 /* NM_IGNORED */ -char **strv_split_nulstr(const char *s) { - const char *i; - char **r = NULL; - - NULSTR_FOREACH(i, s) - if (strv_extend(&r, i) < 0) { - strv_free(r); - return NULL; - } - - if (!r) - return strv_new(NULL); - - return r; -} -#endif /* NM_IGNORED */ - -int strv_make_nulstr(char * const *l, char **ret, size_t *ret_size) { - /* A valid nulstr with two NULs at the end will be created, but - * q will be the length without the two trailing NULs. Thus the output - * string is a valid nulstr and can be iterated over using NULSTR_FOREACH, - * and can also be parsed by strv_parse_nulstr as long as the length - * is provided separately. - */ - - size_t n_allocated = 0, n = 0; - _cleanup_free_ char *m = NULL; - char * const *i; - - assert(ret); - assert(ret_size); - - STRV_FOREACH(i, l) { - size_t z; - - z = strlen(*i); - - if (!GREEDY_REALLOC(m, n_allocated, n + z + 2)) - return -ENOMEM; - - memcpy(m + n, *i, z + 1); - n += z + 1; - } - - if (!m) { - m = new0(char, 1); - if (!m) - return -ENOMEM; - n = 1; - } else - /* make sure there is a second extra NUL at the end of resulting nulstr */ - m[n] = '\0'; - - assert(n > 0); - *ret = m; - *ret_size = n - 1; - - m = NULL; - - return 0; -} - -bool strv_overlap(char * const *a, char * const *b) { - char * const *i; - - STRV_FOREACH(i, a) - if (strv_contains(b, *i)) - return true; - - return false; -} - -#if 0 /* NM_IGNORED */ -static int str_compare(char * const *a, char * const *b) { - return strcmp(*a, *b); -} - -char **strv_sort(char **l) { - typesafe_qsort(l, strv_length(l), str_compare); - return l; -} -#endif /* NM_IGNORED */ - -int strv_compare(char * const *a, char * const *b) { - int r; - - if (strv_isempty(a)) { - if (strv_isempty(b)) - return 0; - else - return -1; - } - - if (strv_isempty(b)) - return 1; - - for ( ; *a || *b; ++a, ++b) { - r = strcmp_ptr(*a, *b); - if (r != 0) - return r; - } - - return 0; -} - -void strv_print(char * const *l) { - char * const *s; - - STRV_FOREACH(s, l) - puts(*s); -} - -int strv_extendf(char ***l, const char *format, ...) { - va_list ap; - char *x; - int r; - - va_start(ap, format); - r = vasprintf(&x, format, ap); - va_end(ap); - - if (r < 0) - return -ENOMEM; - - return strv_consume(l, x); -} - -char **strv_reverse(char **l) { - size_t n, i; - - n = strv_length(l); - if (n <= 1) - return l; - - for (i = 0; i < n / 2; i++) - SWAP_TWO(l[i], l[n-1-i]); - - return l; -} - -char **strv_shell_escape(char **l, const char *bad) { - char **s; - - /* Escapes every character in every string in l that is in bad, - * edits in-place, does not roll-back on error. */ - - STRV_FOREACH(s, l) { - char *v; - - v = shell_escape(*s, bad); - if (!v) - return NULL; - - free(*s); - *s = v; - } - - return l; -} - -bool strv_fnmatch_full(char* const* patterns, const char *s, int flags, size_t *matched_pos) { - for (size_t i = 0; patterns && patterns[i]; i++) - if (fnmatch(patterns[i], s, flags) == 0) { - if (matched_pos) - *matched_pos = i; - return true; - } - - return false; -} - -char ***strv_free_free(char ***l) { - char ***i; - - if (!l) - return NULL; - - for (i = l; *i; i++) - strv_free(*i); - - return mfree(l); -} - -char **strv_skip(char **l, size_t n) { - - while (n > 0) { - if (strv_isempty(l)) - return l; - - l++, n--; - } - - return l; -} - -int strv_extend_n(char ***l, const char *value, size_t n) { - size_t i, j, k; - char **nl; - - assert(l); - - if (!value) - return 0; - if (n == 0) - return 0; - - /* Adds the value n times to l */ - - k = strv_length(*l); - if (n >= SIZE_MAX - k) - return -ENOMEM; - - nl = reallocarray(*l, GREEDY_ALLOC_ROUND_UP(k + n + 1), sizeof(char *)); - if (!nl) - return -ENOMEM; - - *l = nl; - - for (i = k; i < k + n; i++) { - nl[i] = strdup(value); - if (!nl[i]) - goto rollback; - } - - nl[i] = NULL; - return 0; - -rollback: - for (j = k; j < i; j++) - free(nl[j]); - - nl[k] = NULL; - return -ENOMEM; -} - -int fputstrv(FILE *f, char * const *l, const char *separator, bool *space) { - bool b = false; - char * const *s; - int r; - - /* Like fputs(), but for strv, and with a less stupid argument order */ - - if (!space) - space = &b; - - STRV_FOREACH(s, l) { - r = fputs_with_space(f, *s, separator, space); - if (r < 0) - return r; - } - - return 0; -} - -static int string_strv_hashmap_put_internal(Hashmap *h, const char *key, const char *value) { - char **l; - int r; - - l = hashmap_get(h, key); - if (l) { - /* A list for this key already exists, let's append to it if it is not listed yet */ - if (strv_contains(l, value)) - return 0; - - r = strv_extend(&l, value); - if (r < 0) - return r; - - assert_se(hashmap_update(h, key, l) >= 0); - } else { - /* No list for this key exists yet, create one */ - _cleanup_strv_free_ char **l2 = NULL; - _cleanup_free_ char *t = NULL; - - t = strdup(key); - if (!t) - return -ENOMEM; - - r = strv_extend(&l2, value); - if (r < 0) - return r; - - r = hashmap_put(h, t, l2); - if (r < 0) - return r; - TAKE_PTR(t); - TAKE_PTR(l2); - } - - return 1; -} - -int _string_strv_hashmap_put(Hashmap **h, const char *key, const char *value HASHMAP_DEBUG_PARAMS) { - int r; - - r = _hashmap_ensure_allocated(h, &string_strv_hash_ops HASHMAP_DEBUG_PASS_ARGS); - if (r < 0) - return r; - - return string_strv_hashmap_put_internal(*h, key, value); -} - -int _string_strv_ordered_hashmap_put(OrderedHashmap **h, const char *key, const char *value HASHMAP_DEBUG_PARAMS) { - int r; - - r = _ordered_hashmap_ensure_allocated(h, &string_strv_hash_ops HASHMAP_DEBUG_PASS_ARGS); - if (r < 0) - return r; - - return string_strv_hashmap_put_internal(PLAIN_HASHMAP(*h), key, value); -} - -DEFINE_HASH_OPS_FULL(string_strv_hash_ops, char, string_hash_func, string_compare_func, free, char*, strv_free); diff --git a/shared/systemd/src/basic/strv.h b/shared/systemd/src/basic/strv.h deleted file mode 100644 index 6b3e8e7f..00000000 --- a/shared/systemd/src/basic/strv.h +++ /dev/null @@ -1,240 +0,0 @@ -/* SPDX-License-Identifier: LGPL-2.1-or-later */ -#pragma once - -#include <fnmatch.h> -#include <stdarg.h> -#include <stdbool.h> -#include <stddef.h> -#include <stdio.h> - -#include "alloc-util.h" -#include "extract-word.h" -#include "hashmap.h" -#include "macro.h" -#include "string-util.h" - -char *strv_find(char * const *l, const char *name) _pure_; -char *strv_find_case(char * const *l, const char *name) _pure_; -char *strv_find_prefix(char * const *l, const char *name) _pure_; -char *strv_find_startswith(char * const *l, const char *name) _pure_; - -#define strv_contains(l, s) (!!strv_find((l), (s))) -#define strv_contains_case(l, s) (!!strv_find_case((l), (s))) - -char **strv_free(char **l); -DEFINE_TRIVIAL_CLEANUP_FUNC(char**, strv_free); -#define _cleanup_strv_free_ _cleanup_(strv_freep) - -char **strv_free_erase(char **l); -DEFINE_TRIVIAL_CLEANUP_FUNC(char**, strv_free_erase); -#define _cleanup_strv_free_erase_ _cleanup_(strv_free_erasep) - -char **strv_copy(char * const *l); -size_t strv_length(char * const *l) _pure_; - -int strv_extend_strv(char ***a, char * const *b, bool filter_duplicates); -int strv_extend_strv_concat(char ***a, char * const *b, const char *suffix); -int strv_prepend(char ***l, const char *value); -int strv_extend(char ***l, const char *value); -int strv_extendf(char ***l, const char *format, ...) _printf_(2,0); -int strv_extend_front(char ***l, const char *value); -int strv_push(char ***l, char *value); -int strv_push_pair(char ***l, char *a, char *b); -int strv_insert(char ***l, size_t position, char *value); - -static inline int strv_push_prepend(char ***l, char *value) { - return strv_insert(l, 0, value); -} - -int strv_consume(char ***l, char *value); -int strv_consume_pair(char ***l, char *a, char *b); -int strv_consume_prepend(char ***l, char *value); - -char **strv_remove(char **l, const char *s); -char **strv_uniq(char **l); -bool strv_is_uniq(char * const *l); - -int strv_compare(char * const *a, char * const *b); -static inline bool strv_equal(char * const *a, char * const *b) { - return strv_compare(a, b) == 0; -} - -char **strv_new_internal(const char *x, ...) _sentinel_; -char **strv_new_ap(const char *x, va_list ap); -#define strv_new(...) strv_new_internal(__VA_ARGS__, NULL) - -#define STRV_IGNORE ((const char *) POINTER_MAX) - -static inline const char* STRV_IFNOTNULL(const char *x) { - return x ? x : STRV_IGNORE; -} - -static inline bool strv_isempty(char * const *l) { - return !l || !*l; -} - -char **strv_split_newlines(const char *s); - -int strv_split_full(char ***t, const char *s, const char *separators, ExtractFlags flags); -static inline char **strv_split(const char *s, const char *separators) { - char **ret; - int r; - - r = strv_split_full(&ret, s, separators, 0); - if (r < 0) - return NULL; - - return ret; -} - -/* Given a string containing white-space separated tuples of words themselves separated by ':', - * returns a vector of strings. If the second element in a tuple is missing, the corresponding - * string in the vector is an empty string. */ -int strv_split_colon_pairs(char ***t, const char *s); - -char *strv_join_full(char * const *l, const char *separator, const char *prefix, bool escape_separtor); -static inline char *strv_join(char * const *l, const char *separator) { - return strv_join_full(l, separator, NULL, false); -} - -char **strv_parse_nulstr(const char *s, size_t l); -char **strv_split_nulstr(const char *s); -int strv_make_nulstr(char * const *l, char **p, size_t *n); - -static inline int strv_from_nulstr(char ***a, const char *nulstr) { - char **t; - - t = strv_split_nulstr(nulstr); - if (!t) - return -ENOMEM; - *a = t; - return 0; -} - -bool strv_overlap(char * const *a, char * const *b) _pure_; - -#define STRV_FOREACH(s, l) \ - for ((s) = (l); (s) && *(s); (s)++) - -#define STRV_FOREACH_BACKWARDS(s, l) \ - for (s = ({ \ - typeof(l) _l = l; \ - _l ? _l + strv_length(_l) - 1U : NULL; \ - }); \ - (l) && ((s) >= (l)); \ - (s)--) - -#define STRV_FOREACH_PAIR(x, y, l) \ - for ((x) = (l), (y) = (x) ? (x+1) : NULL; (x) && *(x) && *(y); (x) += 2, (y) = (x + 1)) - -char **strv_sort(char **l); -void strv_print(char * const *l); - -#define strv_from_stdarg_alloca(first) \ - ({ \ - char **_l; \ - \ - if (!first) \ - _l = (char**) &first; \ - else { \ - size_t _n; \ - va_list _ap; \ - \ - _n = 1; \ - va_start(_ap, first); \ - while (va_arg(_ap, char*)) \ - _n++; \ - va_end(_ap); \ - \ - _l = newa(char*, _n+1); \ - _l[_n = 0] = (char*) first; \ - va_start(_ap, first); \ - for (;;) { \ - _l[++_n] = va_arg(_ap, char*); \ - if (!_l[_n]) \ - break; \ - } \ - va_end(_ap); \ - } \ - _l; \ - }) - -#define STR_IN_SET(x, ...) strv_contains(STRV_MAKE(__VA_ARGS__), x) -#define STRPTR_IN_SET(x, ...) \ - ({ \ - const char* _x = (x); \ - _x && strv_contains(STRV_MAKE(__VA_ARGS__), _x); \ - }) - -#define STRCASE_IN_SET(x, ...) strv_contains_case(STRV_MAKE(__VA_ARGS__), x) -#define STRCASEPTR_IN_SET(x, ...) \ - ({ \ - const char* _x = (x); \ - _x && strv_contains_case(STRV_MAKE(__VA_ARGS__), _x); \ - }) - -#define STARTSWITH_SET(p, ...) \ - ({ \ - const char *_p = (p); \ - char *_found = NULL, **_i; \ - STRV_FOREACH(_i, STRV_MAKE(__VA_ARGS__)) { \ - _found = startswith(_p, *_i); \ - if (_found) \ - break; \ - } \ - _found; \ - }) - -#define ENDSWITH_SET(p, ...) \ - ({ \ - const char *_p = (p); \ - char *_found = NULL, **_i; \ - STRV_FOREACH(_i, STRV_MAKE(__VA_ARGS__)) { \ - _found = endswith(_p, *_i); \ - if (_found) \ - break; \ - } \ - _found; \ - }) - -#define FOREACH_STRING(x, y, ...) \ - for (char **_l = STRV_MAKE(({ x = y; }), ##__VA_ARGS__); \ - x; \ - x = *(++_l)) - -char **strv_reverse(char **l); -char **strv_shell_escape(char **l, const char *bad); - -bool strv_fnmatch_full(char* const* patterns, const char *s, int flags, size_t *matched_pos); -static inline bool strv_fnmatch(char* const* patterns, const char *s) { - return strv_fnmatch_full(patterns, s, 0, NULL); -} - -static inline bool strv_fnmatch_or_empty(char* const* patterns, const char *s, int flags) { - assert(s); - return strv_isempty(patterns) || - strv_fnmatch_full(patterns, s, flags, NULL); -} - -char ***strv_free_free(char ***l); -DEFINE_TRIVIAL_CLEANUP_FUNC(char***, strv_free_free); - -char **strv_skip(char **l, size_t n); - -int strv_extend_n(char ***l, const char *value, size_t n); - -int fputstrv(FILE *f, char * const *l, const char *separator, bool *space); - -#define strv_free_and_replace(a, b) \ - ({ \ - strv_free(a); \ - (a) = (b); \ - (b) = NULL; \ - 0; \ - }) - -extern const struct hash_ops string_strv_hash_ops; -int _string_strv_hashmap_put(Hashmap **h, const char *key, const char *value HASHMAP_DEBUG_PARAMS); -int _string_strv_ordered_hashmap_put(OrderedHashmap **h, const char *key, const char *value HASHMAP_DEBUG_PARAMS); -#define string_strv_hashmap_put(h, k, v) _string_strv_hashmap_put(h, k, v HASHMAP_DEBUG_SRC_ARGS) -#define string_strv_ordered_hashmap_put(h, k, v) _string_strv_ordered_hashmap_put(h, k, v HASHMAP_DEBUG_SRC_ARGS) diff --git a/shared/systemd/src/basic/strxcpyx.c b/shared/systemd/src/basic/strxcpyx.c deleted file mode 100644 index 39aebb88..00000000 --- a/shared/systemd/src/basic/strxcpyx.c +++ /dev/null @@ -1,118 +0,0 @@ -/* SPDX-License-Identifier: LGPL-2.1-or-later */ - -/* - * Concatenates/copies strings. In any case, terminates in all cases - * with '\0' and moves the @dest pointer forward to the added '\0'. - * Returns the remaining size, and 0 if the string was truncated. - * - * Due to the intended usage, these helpers silently noop invocations - * having zero size. This is technically an exception to the above - * statement "terminates in all cases". It's unexpected for such calls to - * occur outside of a loop where this is the preferred behavior. - */ - -#include "nm-sd-adapt-shared.h" - -#include <stdarg.h> -#include <stdio.h> -#include <string.h> - -#include "strxcpyx.h" - -size_t strnpcpy(char **dest, size_t size, const char *src, size_t len) { - assert(dest); - assert(src); - - if (size == 0) - return 0; - - if (len >= size) { - if (size > 1) - *dest = mempcpy(*dest, src, size-1); - size = 0; - } else if (len > 0) { - *dest = mempcpy(*dest, src, len); - size -= len; - } - - *dest[0] = '\0'; - return size; -} - -size_t strpcpy(char **dest, size_t size, const char *src) { - assert(dest); - assert(src); - - return strnpcpy(dest, size, src, strlen(src)); -} - -size_t strpcpyf(char **dest, size_t size, const char *src, ...) { - va_list va; - int i; - - assert(dest); - assert(src); - - if (size == 0) - return 0; - - va_start(va, src); - i = vsnprintf(*dest, size, src, va); - if (i < (int)size) { - *dest += i; - size -= i; - } else - size = 0; - va_end(va); - return size; -} - -size_t strpcpyl(char **dest, size_t size, const char *src, ...) { - va_list va; - - assert(dest); - assert(src); - - va_start(va, src); - do { - size = strpcpy(dest, size, src); - src = va_arg(va, char *); - } while (src); - va_end(va); - return size; -} - -size_t strnscpy(char *dest, size_t size, const char *src, size_t len) { - char *s; - - assert(dest); - assert(src); - - s = dest; - return strnpcpy(&s, size, src, len); -} - -size_t strscpy(char *dest, size_t size, const char *src) { - assert(dest); - assert(src); - - return strnscpy(dest, size, src, strlen(src)); -} - -size_t strscpyl(char *dest, size_t size, const char *src, ...) { - va_list va; - char *s; - - assert(dest); - assert(src); - - va_start(va, src); - s = dest; - do { - size = strpcpy(&s, size, src); - src = va_arg(va, char *); - } while (src); - va_end(va); - - return size; -} diff --git a/shared/systemd/src/basic/strxcpyx.h b/shared/systemd/src/basic/strxcpyx.h deleted file mode 100644 index cdef492d..00000000 --- a/shared/systemd/src/basic/strxcpyx.h +++ /dev/null @@ -1,14 +0,0 @@ -/* SPDX-License-Identifier: LGPL-2.1-or-later */ -#pragma once - -#include <stddef.h> - -#include "macro.h" - -size_t strnpcpy(char **dest, size_t size, const char *src, size_t len); -size_t strpcpy(char **dest, size_t size, const char *src); -size_t strpcpyf(char **dest, size_t size, const char *src, ...) _printf_(3, 4); -size_t strpcpyl(char **dest, size_t size, const char *src, ...) _sentinel_; -size_t strnscpy(char *dest, size_t size, const char *src, size_t len); -size_t strscpy(char *dest, size_t size, const char *src); -size_t strscpyl(char *dest, size_t size, const char *src, ...) _sentinel_; diff --git a/shared/systemd/src/basic/time-util.c b/shared/systemd/src/basic/time-util.c deleted file mode 100644 index e5faa334..00000000 --- a/shared/systemd/src/basic/time-util.c +++ /dev/null @@ -1,1620 +0,0 @@ -/* SPDX-License-Identifier: LGPL-2.1-or-later */ - -#include "nm-sd-adapt-shared.h" - -#include <ctype.h> -#include <errno.h> -#include <limits.h> -#include <stdlib.h> -#include <sys/mman.h> -#include <sys/time.h> -#include <sys/timerfd.h> -#include <sys/timex.h> -#include <sys/types.h> -#include <unistd.h> - -#include "alloc-util.h" -#include "fd-util.h" -#include "fileio.h" -#include "fs-util.h" -#include "io-util.h" -#include "log.h" -#include "macro.h" -#include "missing_timerfd.h" -#include "parse-util.h" -#include "path-util.h" -#include "process-util.h" -#include "stat-util.h" -#include "string-table.h" -#include "string-util.h" -#include "strv.h" -#include "time-util.h" - -static clockid_t map_clock_id(clockid_t c) { - - /* Some more exotic archs (s390, ppc, …) lack the "ALARM" flavour of the clocks. Thus, clock_gettime() will - * fail for them. Since they are essentially the same as their non-ALARM pendants (their only difference is - * when timers are set on them), let's just map them accordingly. This way, we can get the correct time even on - * those archs. */ - - switch (c) { - - case CLOCK_BOOTTIME_ALARM: - return CLOCK_BOOTTIME; - - case CLOCK_REALTIME_ALARM: - return CLOCK_REALTIME; - - default: - return c; - } -} - -usec_t now(clockid_t clock_id) { - struct timespec ts; - - assert_se(clock_gettime(map_clock_id(clock_id), &ts) == 0); - - return timespec_load(&ts); -} - -nsec_t now_nsec(clockid_t clock_id) { - struct timespec ts; - - assert_se(clock_gettime(map_clock_id(clock_id), &ts) == 0); - - return timespec_load_nsec(&ts); -} - -dual_timestamp* dual_timestamp_get(dual_timestamp *ts) { - assert(ts); - - ts->realtime = now(CLOCK_REALTIME); - ts->monotonic = now(CLOCK_MONOTONIC); - - return ts; -} - -triple_timestamp* triple_timestamp_get(triple_timestamp *ts) { - assert(ts); - - ts->realtime = now(CLOCK_REALTIME); - ts->monotonic = now(CLOCK_MONOTONIC); - ts->boottime = clock_boottime_supported() ? now(CLOCK_BOOTTIME) : USEC_INFINITY; - - return ts; -} - -static usec_t map_clock_usec_internal(usec_t from, usec_t from_base, usec_t to_base) { - - /* Maps the time 'from' between two clocks, based on a common reference point where the first clock - * is at 'from_base' and the second clock at 'to_base'. Basically calculates: - * - * from - from_base + to_base - * - * But takes care of overflows/underflows and avoids signed operations. */ - - if (from >= from_base) { /* In the future */ - usec_t delta = from - from_base; - - if (to_base >= USEC_INFINITY - delta) /* overflow? */ - return USEC_INFINITY; - - return to_base + delta; - - } else { /* In the past */ - usec_t delta = from_base - from; - - if (to_base <= delta) /* underflow? */ - return 0; - - return to_base - delta; - } -} - -usec_t map_clock_usec(usec_t from, clockid_t from_clock, clockid_t to_clock) { - - /* Try to avoid any inaccuracy needlessly added in case we convert from effectively the same clock - * onto itself */ - if (map_clock_id(from_clock) == map_clock_id(to_clock)) - return from; - - /* Keep infinity as is */ - if (from == USEC_INFINITY) - return from; - - return map_clock_usec_internal(from, now(from_clock), now(to_clock)); -} - -dual_timestamp* dual_timestamp_from_realtime(dual_timestamp *ts, usec_t u) { - assert(ts); - - if (u == USEC_INFINITY || u == 0) { - ts->realtime = ts->monotonic = u; - return ts; - } - - ts->realtime = u; - ts->monotonic = map_clock_usec(u, CLOCK_REALTIME, CLOCK_MONOTONIC); - return ts; -} - -triple_timestamp* triple_timestamp_from_realtime(triple_timestamp *ts, usec_t u) { - usec_t nowr; - - assert(ts); - - if (u == USEC_INFINITY || u == 0) { - ts->realtime = ts->monotonic = ts->boottime = u; - return ts; - } - - nowr = now(CLOCK_REALTIME); - - ts->realtime = u; - ts->monotonic = map_clock_usec_internal(u, nowr, now(CLOCK_MONOTONIC)); - ts->boottime = clock_boottime_supported() ? - map_clock_usec_internal(u, nowr, now(CLOCK_BOOTTIME)) : - USEC_INFINITY; - - return ts; -} - -dual_timestamp* dual_timestamp_from_monotonic(dual_timestamp *ts, usec_t u) { - assert(ts); - - if (u == USEC_INFINITY) { - ts->realtime = ts->monotonic = USEC_INFINITY; - return ts; - } - - ts->monotonic = u; - ts->realtime = map_clock_usec(u, CLOCK_MONOTONIC, CLOCK_REALTIME); - return ts; -} - -dual_timestamp* dual_timestamp_from_boottime_or_monotonic(dual_timestamp *ts, usec_t u) { - clockid_t cid; - usec_t nowm; - - if (u == USEC_INFINITY) { - ts->realtime = ts->monotonic = USEC_INFINITY; - return ts; - } - - cid = clock_boottime_or_monotonic(); - nowm = now(cid); - - if (cid == CLOCK_MONOTONIC) - ts->monotonic = u; - else - ts->monotonic = map_clock_usec_internal(u, nowm, now(CLOCK_MONOTONIC)); - - ts->realtime = map_clock_usec_internal(u, nowm, now(CLOCK_REALTIME)); - return ts; -} - -usec_t triple_timestamp_by_clock(triple_timestamp *ts, clockid_t clock) { - - switch (clock) { - - case CLOCK_REALTIME: - case CLOCK_REALTIME_ALARM: - return ts->realtime; - - case CLOCK_MONOTONIC: - return ts->monotonic; - - case CLOCK_BOOTTIME: - case CLOCK_BOOTTIME_ALARM: - return ts->boottime; - - default: - return USEC_INFINITY; - } -} - -usec_t timespec_load(const struct timespec *ts) { - assert(ts); - - if (ts->tv_sec < 0 || ts->tv_nsec < 0) - return USEC_INFINITY; - - if ((usec_t) ts->tv_sec > (UINT64_MAX - (ts->tv_nsec / NSEC_PER_USEC)) / USEC_PER_SEC) - return USEC_INFINITY; - - return - (usec_t) ts->tv_sec * USEC_PER_SEC + - (usec_t) ts->tv_nsec / NSEC_PER_USEC; -} - -nsec_t timespec_load_nsec(const struct timespec *ts) { - assert(ts); - - if (ts->tv_sec < 0 || ts->tv_nsec < 0) - return NSEC_INFINITY; - - if ((nsec_t) ts->tv_sec >= (UINT64_MAX - ts->tv_nsec) / NSEC_PER_SEC) - return NSEC_INFINITY; - - return (nsec_t) ts->tv_sec * NSEC_PER_SEC + (nsec_t) ts->tv_nsec; -} - -struct timespec *timespec_store(struct timespec *ts, usec_t u) { - assert(ts); - - if (u == USEC_INFINITY || - u / USEC_PER_SEC >= TIME_T_MAX) { - ts->tv_sec = (time_t) -1; - ts->tv_nsec = -1L; - return ts; - } - - ts->tv_sec = (time_t) (u / USEC_PER_SEC); - ts->tv_nsec = (long) ((u % USEC_PER_SEC) * NSEC_PER_USEC); - - return ts; -} - -struct timespec *timespec_store_nsec(struct timespec *ts, nsec_t n) { - assert(ts); - - if (n == NSEC_INFINITY || - n / NSEC_PER_SEC >= TIME_T_MAX) { - ts->tv_sec = (time_t) -1; - ts->tv_nsec = -1L; - return ts; - } - - ts->tv_sec = (time_t) (n / NSEC_PER_SEC); - ts->tv_nsec = (long) (n % NSEC_PER_SEC); - - return ts; -} - -#if 0 /* NM_IGNORED */ -usec_t timeval_load(const struct timeval *tv) { - assert(tv); - - if (tv->tv_sec < 0 || tv->tv_usec < 0) - return USEC_INFINITY; - - if ((usec_t) tv->tv_sec > (UINT64_MAX - tv->tv_usec) / USEC_PER_SEC) - return USEC_INFINITY; - - return - (usec_t) tv->tv_sec * USEC_PER_SEC + - (usec_t) tv->tv_usec; -} - -struct timeval *timeval_store(struct timeval *tv, usec_t u) { - assert(tv); - - if (u == USEC_INFINITY || - u / USEC_PER_SEC > TIME_T_MAX) { - tv->tv_sec = (time_t) -1; - tv->tv_usec = (suseconds_t) -1; - } else { - tv->tv_sec = (time_t) (u / USEC_PER_SEC); - tv->tv_usec = (suseconds_t) (u % USEC_PER_SEC); - } - - return tv; -} - -char *format_timestamp_style( - char *buf, - size_t l, - usec_t t, - TimestampStyle style) { - - /* The weekdays in non-localized (English) form. We use this instead of the localized form, so that our - * generated timestamps may be parsed with parse_timestamp(), and always read the same. */ - static const char * const weekdays[] = { - [0] = "Sun", - [1] = "Mon", - [2] = "Tue", - [3] = "Wed", - [4] = "Thu", - [5] = "Fri", - [6] = "Sat", - }; - - struct tm tm; - time_t sec; - size_t n; - bool utc = false, us = false; - - assert(buf); - - switch (style) { - case TIMESTAMP_PRETTY: - break; - case TIMESTAMP_US: - us = true; - break; - case TIMESTAMP_UTC: - utc = true; - break; - case TIMESTAMP_US_UTC: - us = true; - utc = true; - break; - default: - return NULL; - } - - if (l < (size_t) (3 + /* week day */ - 1 + 10 + /* space and date */ - 1 + 8 + /* space and time */ - (us ? 1 + 6 : 0) + /* "." and microsecond part */ - 1 + 1 + /* space and shortest possible zone */ - 1)) - return NULL; /* Not enough space even for the shortest form. */ - if (t <= 0 || t == USEC_INFINITY) - return NULL; /* Timestamp is unset */ - - /* Let's not format times with years > 9999 */ - if (t > USEC_TIMESTAMP_FORMATTABLE_MAX) { - assert(l >= STRLEN("--- XXXX-XX-XX XX:XX:XX") + 1); - strcpy(buf, "--- XXXX-XX-XX XX:XX:XX"); - return buf; - } - - sec = (time_t) (t / USEC_PER_SEC); /* Round down */ - - if (!localtime_or_gmtime_r(&sec, &tm, utc)) - return NULL; - - /* Start with the week day */ - assert((size_t) tm.tm_wday < ELEMENTSOF(weekdays)); - memcpy(buf, weekdays[tm.tm_wday], 4); - - /* Add the main components */ - if (strftime(buf + 3, l - 3, " %Y-%m-%d %H:%M:%S", &tm) <= 0) - return NULL; /* Doesn't fit */ - - /* Append the microseconds part, if that's requested */ - if (us) { - n = strlen(buf); - if (n + 8 > l) - return NULL; /* Microseconds part doesn't fit. */ - - sprintf(buf + n, ".%06"PRI_USEC, t % USEC_PER_SEC); - } - - /* Append the timezone */ - n = strlen(buf); - if (utc) { - /* If this is UTC then let's explicitly use the "UTC" string here, because gmtime_r() normally uses the - * obsolete "GMT" instead. */ - if (n + 5 > l) - return NULL; /* "UTC" doesn't fit. */ - - strcpy(buf + n, " UTC"); - - } else if (!isempty(tm.tm_zone)) { - size_t tn; - - /* An explicit timezone is specified, let's use it, if it fits */ - tn = strlen(tm.tm_zone); - if (n + 1 + tn + 1 > l) { - /* The full time zone does not fit in. Yuck. */ - - if (n + 1 + _POSIX_TZNAME_MAX + 1 > l) - return NULL; /* Not even enough space for the POSIX minimum (of 6)? In that case, complain that it doesn't fit */ - - /* So the time zone doesn't fit in fully, but the caller passed enough space for the POSIX - * minimum time zone length. In this case suppress the timezone entirely, in order not to dump - * an overly long, hard to read string on the user. This should be safe, because the user will - * assume the local timezone anyway if none is shown. And so does parse_timestamp(). */ - } else { - buf[n++] = ' '; - strcpy(buf + n, tm.tm_zone); - } - } - - return buf; -} - -char *format_timestamp_relative(char *buf, size_t l, usec_t t) { - const char *s; - usec_t n, d; - - if (t <= 0 || t == USEC_INFINITY) - return NULL; - - n = now(CLOCK_REALTIME); - if (n > t) { - d = n - t; - s = "ago"; - } else { - d = t - n; - s = "left"; - } - - if (d >= USEC_PER_YEAR) - snprintf(buf, l, USEC_FMT " years " USEC_FMT " months %s", - d / USEC_PER_YEAR, - (d % USEC_PER_YEAR) / USEC_PER_MONTH, s); - else if (d >= USEC_PER_MONTH) - snprintf(buf, l, USEC_FMT " months " USEC_FMT " days %s", - d / USEC_PER_MONTH, - (d % USEC_PER_MONTH) / USEC_PER_DAY, s); - else if (d >= USEC_PER_WEEK) - snprintf(buf, l, USEC_FMT " weeks " USEC_FMT " days %s", - d / USEC_PER_WEEK, - (d % USEC_PER_WEEK) / USEC_PER_DAY, s); - else if (d >= 2*USEC_PER_DAY) - snprintf(buf, l, USEC_FMT " days %s", d / USEC_PER_DAY, s); - else if (d >= 25*USEC_PER_HOUR) - snprintf(buf, l, "1 day " USEC_FMT "h %s", - (d - USEC_PER_DAY) / USEC_PER_HOUR, s); - else if (d >= 6*USEC_PER_HOUR) - snprintf(buf, l, USEC_FMT "h %s", - d / USEC_PER_HOUR, s); - else if (d >= USEC_PER_HOUR) - snprintf(buf, l, USEC_FMT "h " USEC_FMT "min %s", - d / USEC_PER_HOUR, - (d % USEC_PER_HOUR) / USEC_PER_MINUTE, s); - else if (d >= 5*USEC_PER_MINUTE) - snprintf(buf, l, USEC_FMT "min %s", - d / USEC_PER_MINUTE, s); - else if (d >= USEC_PER_MINUTE) - snprintf(buf, l, USEC_FMT "min " USEC_FMT "s %s", - d / USEC_PER_MINUTE, - (d % USEC_PER_MINUTE) / USEC_PER_SEC, s); - else if (d >= USEC_PER_SEC) - snprintf(buf, l, USEC_FMT "s %s", - d / USEC_PER_SEC, s); - else if (d >= USEC_PER_MSEC) - snprintf(buf, l, USEC_FMT "ms %s", - d / USEC_PER_MSEC, s); - else if (d > 0) - snprintf(buf, l, USEC_FMT"us %s", - d, s); - else - snprintf(buf, l, "now"); - - buf[l-1] = 0; - return buf; -} -#endif /* NM_IGNORED */ - -char *format_timespan(char *buf, size_t l, usec_t t, usec_t accuracy) { - static const struct { - const char *suffix; - usec_t usec; - } table[] = { - { "y", USEC_PER_YEAR }, - { "month", USEC_PER_MONTH }, - { "w", USEC_PER_WEEK }, - { "d", USEC_PER_DAY }, - { "h", USEC_PER_HOUR }, - { "min", USEC_PER_MINUTE }, - { "s", USEC_PER_SEC }, - { "ms", USEC_PER_MSEC }, - { "us", 1 }, - }; - - size_t i; - char *p = buf; - bool something = false; - - assert(buf); - assert(l > 0); - - if (t == USEC_INFINITY) { - strncpy(p, "infinity", l-1); - p[l-1] = 0; - return p; - } - - if (t <= 0) { - strncpy(p, "0", l-1); - p[l-1] = 0; - return p; - } - - /* The result of this function can be parsed with parse_sec */ - - for (i = 0; i < ELEMENTSOF(table); i++) { - int k = 0; - size_t n; - bool done = false; - usec_t a, b; - - if (t <= 0) - break; - - if (t < accuracy && something) - break; - - if (t < table[i].usec) - continue; - - if (l <= 1) - break; - - a = t / table[i].usec; - b = t % table[i].usec; - - /* Let's see if we should shows this in dot notation */ - if (t < USEC_PER_MINUTE && b > 0) { - usec_t cc; - signed char j; - - j = 0; - for (cc = table[i].usec; cc > 1; cc /= 10) - j++; - - for (cc = accuracy; cc > 1; cc /= 10) { - b /= 10; - j--; - } - - if (j > 0) { - k = snprintf(p, l, - "%s"USEC_FMT".%0*"PRI_USEC"%s", - p > buf ? " " : "", - a, - j, - b, - table[i].suffix); - - t = 0; - done = true; - } - } - - /* No? Then let's show it normally */ - if (!done) { - k = snprintf(p, l, - "%s"USEC_FMT"%s", - p > buf ? " " : "", - a, - table[i].suffix); - - t = b; - } - - n = MIN((size_t) k, l); - - l -= n; - p += n; - - something = true; - } - - *p = 0; - - return buf; -} - -#if 0 /* NM_IGNORED */ -static int parse_timestamp_impl(const char *t, usec_t *usec, bool with_tz) { - static const struct { - const char *name; - const int nr; - } day_nr[] = { - { "Sunday", 0 }, - { "Sun", 0 }, - { "Monday", 1 }, - { "Mon", 1 }, - { "Tuesday", 2 }, - { "Tue", 2 }, - { "Wednesday", 3 }, - { "Wed", 3 }, - { "Thursday", 4 }, - { "Thu", 4 }, - { "Friday", 5 }, - { "Fri", 5 }, - { "Saturday", 6 }, - { "Sat", 6 }, - }; - - const char *k, *utc = NULL, *tzn = NULL; - struct tm tm, copy; - time_t x; - usec_t x_usec, plus = 0, minus = 0, ret; - int r, weekday = -1, dst = -1; - size_t i; - - /* Allowed syntaxes: - * - * 2012-09-22 16:34:22 - * 2012-09-22 16:34 (seconds will be set to 0) - * 2012-09-22 (time will be set to 00:00:00) - * 16:34:22 (date will be set to today) - * 16:34 (date will be set to today, seconds to 0) - * now - * yesterday (time is set to 00:00:00) - * today (time is set to 00:00:00) - * tomorrow (time is set to 00:00:00) - * +5min - * -5days - * @2147483647 (seconds since epoch) - */ - - assert(t); - - if (t[0] == '@' && !with_tz) - return parse_sec(t + 1, usec); - - ret = now(CLOCK_REALTIME); - - if (!with_tz) { - if (streq(t, "now")) - goto finish; - - else if (t[0] == '+') { - r = parse_sec(t+1, &plus); - if (r < 0) - return r; - - goto finish; - - } else if (t[0] == '-') { - r = parse_sec(t+1, &minus); - if (r < 0) - return r; - - goto finish; - - } else if ((k = endswith(t, " ago"))) { - t = strndupa(t, k - t); - - r = parse_sec(t, &minus); - if (r < 0) - return r; - - goto finish; - - } else if ((k = endswith(t, " left"))) { - t = strndupa(t, k - t); - - r = parse_sec(t, &plus); - if (r < 0) - return r; - - goto finish; - } - - /* See if the timestamp is suffixed with UTC */ - utc = endswith_no_case(t, " UTC"); - if (utc) - t = strndupa(t, utc - t); - else { - const char *e = NULL; - int j; - - tzset(); - - /* See if the timestamp is suffixed by either the DST or non-DST local timezone. Note that we only - * support the local timezones here, nothing else. Not because we wouldn't want to, but simply because - * there are no nice APIs available to cover this. By accepting the local time zone strings, we make - * sure that all timestamps written by format_timestamp() can be parsed correctly, even though we don't - * support arbitrary timezone specifications. */ - - for (j = 0; j <= 1; j++) { - - if (isempty(tzname[j])) - continue; - - e = endswith_no_case(t, tzname[j]); - if (!e) - continue; - if (e == t) - continue; - if (e[-1] != ' ') - continue; - - break; - } - - if (IN_SET(j, 0, 1)) { - /* Found one of the two timezones specified. */ - t = strndupa(t, e - t - 1); - dst = j; - tzn = tzname[j]; - } - } - } - - x = (time_t) (ret / USEC_PER_SEC); - x_usec = 0; - - if (!localtime_or_gmtime_r(&x, &tm, utc)) - return -EINVAL; - - tm.tm_isdst = dst; - if (!with_tz && tzn) - tm.tm_zone = tzn; - - if (streq(t, "today")) { - tm.tm_sec = tm.tm_min = tm.tm_hour = 0; - goto from_tm; - - } else if (streq(t, "yesterday")) { - tm.tm_mday--; - tm.tm_sec = tm.tm_min = tm.tm_hour = 0; - goto from_tm; - - } else if (streq(t, "tomorrow")) { - tm.tm_mday++; - tm.tm_sec = tm.tm_min = tm.tm_hour = 0; - goto from_tm; - } - - for (i = 0; i < ELEMENTSOF(day_nr); i++) { - size_t skip; - - if (!startswith_no_case(t, day_nr[i].name)) - continue; - - skip = strlen(day_nr[i].name); - if (t[skip] != ' ') - continue; - - weekday = day_nr[i].nr; - t += skip + 1; - break; - } - - copy = tm; - k = strptime(t, "%y-%m-%d %H:%M:%S", &tm); - if (k) { - if (*k == '.') - goto parse_usec; - else if (*k == 0) - goto from_tm; - } - - tm = copy; - k = strptime(t, "%Y-%m-%d %H:%M:%S", &tm); - if (k) { - if (*k == '.') - goto parse_usec; - else if (*k == 0) - goto from_tm; - } - - tm = copy; - k = strptime(t, "%y-%m-%d %H:%M", &tm); - if (k && *k == 0) { - tm.tm_sec = 0; - goto from_tm; - } - - tm = copy; - k = strptime(t, "%Y-%m-%d %H:%M", &tm); - if (k && *k == 0) { - tm.tm_sec = 0; - goto from_tm; - } - - tm = copy; - k = strptime(t, "%y-%m-%d", &tm); - if (k && *k == 0) { - tm.tm_sec = tm.tm_min = tm.tm_hour = 0; - goto from_tm; - } - - tm = copy; - k = strptime(t, "%Y-%m-%d", &tm); - if (k && *k == 0) { - tm.tm_sec = tm.tm_min = tm.tm_hour = 0; - goto from_tm; - } - - tm = copy; - k = strptime(t, "%H:%M:%S", &tm); - if (k) { - if (*k == '.') - goto parse_usec; - else if (*k == 0) - goto from_tm; - } - - tm = copy; - k = strptime(t, "%H:%M", &tm); - if (k && *k == 0) { - tm.tm_sec = 0; - goto from_tm; - } - - return -EINVAL; - -parse_usec: - { - unsigned add; - - k++; - r = parse_fractional_part_u(&k, 6, &add); - if (r < 0) - return -EINVAL; - - if (*k) - return -EINVAL; - - x_usec = add; - } - -from_tm: - if (weekday >= 0 && tm.tm_wday != weekday) - return -EINVAL; - - x = mktime_or_timegm(&tm, utc); - if (x < 0) - return -EINVAL; - - ret = (usec_t) x * USEC_PER_SEC + x_usec; - if (ret > USEC_TIMESTAMP_FORMATTABLE_MAX) - return -EINVAL; - -finish: - if (ret + plus < ret) /* overflow? */ - return -EINVAL; - ret += plus; - if (ret > USEC_TIMESTAMP_FORMATTABLE_MAX) - return -EINVAL; - - if (ret >= minus) - ret -= minus; - else - return -EINVAL; - - if (usec) - *usec = ret; - return 0; -} - -typedef struct ParseTimestampResult { - usec_t usec; - int return_value; -} ParseTimestampResult; - -int parse_timestamp(const char *t, usec_t *usec) { - char *last_space, *tz = NULL; - ParseTimestampResult *shared, tmp; - int r; - - last_space = strrchr(t, ' '); - if (last_space != NULL && timezone_is_valid(last_space + 1, LOG_DEBUG)) - tz = last_space + 1; - - if (!tz || endswith_no_case(t, " UTC")) - return parse_timestamp_impl(t, usec, false); - - shared = mmap(NULL, sizeof *shared, PROT_READ|PROT_WRITE, MAP_SHARED|MAP_ANONYMOUS, -1, 0); - if (shared == MAP_FAILED) - return negative_errno(); - - r = safe_fork("(sd-timestamp)", FORK_RESET_SIGNALS|FORK_CLOSE_ALL_FDS|FORK_DEATHSIG|FORK_WAIT, NULL); - if (r < 0) { - (void) munmap(shared, sizeof *shared); - return r; - } - if (r == 0) { - bool with_tz = true; - char *colon_tz; - - /* tzset(3) says $TZ should be prefixed with ":" if we reference timezone files */ - colon_tz = strjoina(":", tz); - - if (setenv("TZ", colon_tz, 1) != 0) { - shared->return_value = negative_errno(); - _exit(EXIT_FAILURE); - } - - tzset(); - - /* If there is a timezone that matches the tzname fields, leave the parsing to the implementation. - * Otherwise just cut it off. */ - with_tz = !STR_IN_SET(tz, tzname[0], tzname[1]); - - /* Cut off the timezone if we don't need it. */ - if (with_tz) - t = strndupa(t, last_space - t); - - shared->return_value = parse_timestamp_impl(t, &shared->usec, with_tz); - - _exit(EXIT_SUCCESS); - } - - tmp = *shared; - if (munmap(shared, sizeof *shared) != 0) - return negative_errno(); - - if (tmp.return_value == 0 && usec) - *usec = tmp.usec; - - return tmp.return_value; -} - -static const char* extract_multiplier(const char *p, usec_t *multiplier) { - static const struct { - const char *suffix; - usec_t usec; - } table[] = { - { "seconds", USEC_PER_SEC }, - { "second", USEC_PER_SEC }, - { "sec", USEC_PER_SEC }, - { "s", USEC_PER_SEC }, - { "minutes", USEC_PER_MINUTE }, - { "minute", USEC_PER_MINUTE }, - { "min", USEC_PER_MINUTE }, - { "months", USEC_PER_MONTH }, - { "month", USEC_PER_MONTH }, - { "M", USEC_PER_MONTH }, - { "msec", USEC_PER_MSEC }, - { "ms", USEC_PER_MSEC }, - { "m", USEC_PER_MINUTE }, - { "hours", USEC_PER_HOUR }, - { "hour", USEC_PER_HOUR }, - { "hr", USEC_PER_HOUR }, - { "h", USEC_PER_HOUR }, - { "days", USEC_PER_DAY }, - { "day", USEC_PER_DAY }, - { "d", USEC_PER_DAY }, - { "weeks", USEC_PER_WEEK }, - { "week", USEC_PER_WEEK }, - { "w", USEC_PER_WEEK }, - { "years", USEC_PER_YEAR }, - { "year", USEC_PER_YEAR }, - { "y", USEC_PER_YEAR }, - { "usec", 1ULL }, - { "us", 1ULL }, - { "µs", 1ULL }, - }; - size_t i; - - for (i = 0; i < ELEMENTSOF(table); i++) { - char *e; - - e = startswith(p, table[i].suffix); - if (e) { - *multiplier = table[i].usec; - return e; - } - } - - return p; -} - -int parse_time(const char *t, usec_t *usec, usec_t default_unit) { - const char *p, *s; - usec_t r = 0; - bool something = false; - - assert(t); - assert(default_unit > 0); - - p = t; - - p += strspn(p, WHITESPACE); - s = startswith(p, "infinity"); - if (s) { - s += strspn(s, WHITESPACE); - if (*s != 0) - return -EINVAL; - - if (usec) - *usec = USEC_INFINITY; - return 0; - } - - for (;;) { - usec_t multiplier = default_unit, k; - long long l; - char *e; - - p += strspn(p, WHITESPACE); - - if (*p == 0) { - if (!something) - return -EINVAL; - - break; - } - - if (*p == '-') /* Don't allow "-0" */ - return -ERANGE; - - errno = 0; - l = strtoll(p, &e, 10); - if (errno > 0) - return -errno; - if (l < 0) - return -ERANGE; - - if (*e == '.') { - p = e + 1; - p += strspn(p, DIGITS); - } else if (e == p) - return -EINVAL; - else - p = e; - - s = extract_multiplier(p + strspn(p, WHITESPACE), &multiplier); - if (s == p && *s != '\0') - /* Don't allow '12.34.56', but accept '12.34 .56' or '12.34s.56'*/ - return -EINVAL; - - p = s; - - if ((usec_t) l >= USEC_INFINITY / multiplier) - return -ERANGE; - - k = (usec_t) l * multiplier; - if (k >= USEC_INFINITY - r) - return -ERANGE; - - r += k; - - something = true; - - if (*e == '.') { - usec_t m = multiplier / 10; - const char *b; - - for (b = e + 1; *b >= '0' && *b <= '9'; b++, m /= 10) { - k = (usec_t) (*b - '0') * m; - if (k >= USEC_INFINITY - r) - return -ERANGE; - - r += k; - } - - /* Don't allow "0.-0", "3.+1", "3. 1", "3.sec" or "3.hoge"*/ - if (b == e + 1) - return -EINVAL; - } - } - - if (usec) - *usec = r; - return 0; -} - -int parse_sec(const char *t, usec_t *usec) { - return parse_time(t, usec, USEC_PER_SEC); -} - -int parse_sec_fix_0(const char *t, usec_t *ret) { - usec_t k; - int r; - - assert(t); - assert(ret); - - r = parse_sec(t, &k); - if (r < 0) - return r; - - *ret = k == 0 ? USEC_INFINITY : k; - return r; -} - -int parse_sec_def_infinity(const char *t, usec_t *ret) { - t += strspn(t, WHITESPACE); - if (isempty(t)) { - *ret = USEC_INFINITY; - return 0; - } - return parse_sec(t, ret); -} - -static const char* extract_nsec_multiplier(const char *p, nsec_t *multiplier) { - static const struct { - const char *suffix; - nsec_t nsec; - } table[] = { - { "seconds", NSEC_PER_SEC }, - { "second", NSEC_PER_SEC }, - { "sec", NSEC_PER_SEC }, - { "s", NSEC_PER_SEC }, - { "minutes", NSEC_PER_MINUTE }, - { "minute", NSEC_PER_MINUTE }, - { "min", NSEC_PER_MINUTE }, - { "months", NSEC_PER_MONTH }, - { "month", NSEC_PER_MONTH }, - { "M", NSEC_PER_MONTH }, - { "msec", NSEC_PER_MSEC }, - { "ms", NSEC_PER_MSEC }, - { "m", NSEC_PER_MINUTE }, - { "hours", NSEC_PER_HOUR }, - { "hour", NSEC_PER_HOUR }, - { "hr", NSEC_PER_HOUR }, - { "h", NSEC_PER_HOUR }, - { "days", NSEC_PER_DAY }, - { "day", NSEC_PER_DAY }, - { "d", NSEC_PER_DAY }, - { "weeks", NSEC_PER_WEEK }, - { "week", NSEC_PER_WEEK }, - { "w", NSEC_PER_WEEK }, - { "years", NSEC_PER_YEAR }, - { "year", NSEC_PER_YEAR }, - { "y", NSEC_PER_YEAR }, - { "usec", NSEC_PER_USEC }, - { "us", NSEC_PER_USEC }, - { "µs", NSEC_PER_USEC }, - { "nsec", 1ULL }, - { "ns", 1ULL }, - { "", 1ULL }, /* default is nsec */ - }; - size_t i; - - for (i = 0; i < ELEMENTSOF(table); i++) { - char *e; - - e = startswith(p, table[i].suffix); - if (e) { - *multiplier = table[i].nsec; - return e; - } - } - - return p; -} - -int parse_nsec(const char *t, nsec_t *nsec) { - const char *p, *s; - nsec_t r = 0; - bool something = false; - - assert(t); - assert(nsec); - - p = t; - - p += strspn(p, WHITESPACE); - s = startswith(p, "infinity"); - if (s) { - s += strspn(s, WHITESPACE); - if (*s != 0) - return -EINVAL; - - *nsec = NSEC_INFINITY; - return 0; - } - - for (;;) { - nsec_t multiplier = 1, k; - long long l; - char *e; - - p += strspn(p, WHITESPACE); - - if (*p == 0) { - if (!something) - return -EINVAL; - - break; - } - - if (*p == '-') /* Don't allow "-0" */ - return -ERANGE; - - errno = 0; - l = strtoll(p, &e, 10); - if (errno > 0) - return -errno; - if (l < 0) - return -ERANGE; - - if (*e == '.') { - p = e + 1; - p += strspn(p, DIGITS); - } else if (e == p) - return -EINVAL; - else - p = e; - - s = extract_nsec_multiplier(p + strspn(p, WHITESPACE), &multiplier); - if (s == p && *s != '\0') - /* Don't allow '12.34.56', but accept '12.34 .56' or '12.34s.56'*/ - return -EINVAL; - - p = s; - - if ((nsec_t) l >= NSEC_INFINITY / multiplier) - return -ERANGE; - - k = (nsec_t) l * multiplier; - if (k >= NSEC_INFINITY - r) - return -ERANGE; - - r += k; - - something = true; - - if (*e == '.') { - nsec_t m = multiplier / 10; - const char *b; - - for (b = e + 1; *b >= '0' && *b <= '9'; b++, m /= 10) { - k = (nsec_t) (*b - '0') * m; - if (k >= NSEC_INFINITY - r) - return -ERANGE; - - r += k; - } - - /* Don't allow "0.-0", "3.+1", "3. 1", "3.sec" or "3.hoge"*/ - if (b == e + 1) - return -EINVAL; - } - } - - *nsec = r; - - return 0; -} - -bool ntp_synced(void) { - struct timex txc = {}; - - if (adjtimex(&txc) < 0) - return false; - - /* Consider the system clock synchronized if the reported maximum error is smaller than the maximum - * value (16 seconds). Ignore the STA_UNSYNC flag as it may have been set to prevent the kernel from - * touching the RTC. */ - if (txc.maxerror >= 16000000) - return false; - - return true; -} - -int get_timezones(char ***ret) { - _cleanup_fclose_ FILE *f = NULL; - _cleanup_strv_free_ char **zones = NULL; - size_t n_zones = 0, n_allocated = 0; - int r; - - assert(ret); - - zones = strv_new("UTC"); - if (!zones) - return -ENOMEM; - - n_allocated = 2; - n_zones = 1; - - f = fopen("/usr/share/zoneinfo/zone1970.tab", "re"); - if (f) { - for (;;) { - _cleanup_free_ char *line = NULL; - char *p, *w; - size_t k; - - r = read_line(f, LONG_LINE_MAX, &line); - if (r < 0) - return r; - if (r == 0) - break; - - p = strstrip(line); - - if (isempty(p) || *p == '#') - continue; - - /* Skip over country code */ - p += strcspn(p, WHITESPACE); - p += strspn(p, WHITESPACE); - - /* Skip over coordinates */ - p += strcspn(p, WHITESPACE); - p += strspn(p, WHITESPACE); - - /* Found timezone name */ - k = strcspn(p, WHITESPACE); - if (k <= 0) - continue; - - w = strndup(p, k); - if (!w) - return -ENOMEM; - - if (!GREEDY_REALLOC(zones, n_allocated, n_zones + 2)) { - free(w); - return -ENOMEM; - } - - zones[n_zones++] = w; - zones[n_zones] = NULL; - } - - strv_sort(zones); - strv_uniq(zones); - - } else if (errno != ENOENT) - return -errno; - - *ret = TAKE_PTR(zones); - - return 0; -} -#endif /* NM_IGNORED */ - -bool timezone_is_valid(const char *name, int log_level) { - bool slash = false; - const char *p, *t; - _cleanup_close_ int fd = -1; - char buf[4]; - int r; - - if (isempty(name)) - return false; - - /* Always accept "UTC" as valid timezone, since it's the fallback, even if user has no timezones installed. */ - if (streq(name, "UTC")) - return true; - - if (name[0] == '/') - return false; - - for (p = name; *p; p++) { - if (!(*p >= '0' && *p <= '9') && - !(*p >= 'a' && *p <= 'z') && - !(*p >= 'A' && *p <= 'Z') && - !IN_SET(*p, '-', '_', '+', '/')) - return false; - - if (*p == '/') { - - if (slash) - return false; - - slash = true; - } else - slash = false; - } - - if (slash) - return false; - - if (p - name >= PATH_MAX) - return false; - - t = strjoina("/usr/share/zoneinfo/", name); - - fd = open(t, O_RDONLY|O_CLOEXEC); - if (fd < 0) { - log_full_errno(log_level, errno, "Failed to open timezone file '%s': %m", t); - return false; - } - - r = fd_verify_regular(fd); - if (r < 0) { - log_full_errno(log_level, r, "Timezone file '%s' is not a regular file: %m", t); - return false; - } - - r = loop_read_exact(fd, buf, 4, false); - if (r < 0) { - log_full_errno(log_level, r, "Failed to read from timezone file '%s': %m", t); - return false; - } - - /* Magic from tzfile(5) */ - if (memcmp(buf, "TZif", 4) != 0) { - log_full(log_level, "Timezone file '%s' has wrong magic bytes", t); - return false; - } - - return true; -} - -bool clock_boottime_supported(void) { - static int supported = -1; - - /* Note that this checks whether CLOCK_BOOTTIME is available in general as well as available for timerfds()! */ - - if (supported < 0) { - int fd; - - fd = timerfd_create(CLOCK_BOOTTIME, TFD_NONBLOCK|TFD_CLOEXEC); - if (fd < 0) - supported = false; - else { - safe_close(fd); - supported = true; - } - } - - return supported; -} - -clockid_t clock_boottime_or_monotonic(void) { - if (clock_boottime_supported()) - return CLOCK_BOOTTIME; - else - return CLOCK_MONOTONIC; -} - -bool clock_supported(clockid_t clock) { - struct timespec ts; - - switch (clock) { - - case CLOCK_MONOTONIC: - case CLOCK_REALTIME: - return true; - - case CLOCK_BOOTTIME: - return clock_boottime_supported(); - - case CLOCK_BOOTTIME_ALARM: - if (!clock_boottime_supported()) - return false; - - _fallthrough_; - default: - /* For everything else, check properly */ - return clock_gettime(clock, &ts) >= 0; - } -} - -#if 0 /* NM_IGNORED */ -int get_timezone(char **ret) { - _cleanup_free_ char *t = NULL; - const char *e; - char *z; - int r; - - r = readlink_malloc("/etc/localtime", &t); - if (r == -ENOENT) { - /* If the symlink does not exist, assume "UTC", like glibc does*/ - z = strdup("UTC"); - if (!z) - return -ENOMEM; - - *ret = z; - return 0; - } - if (r < 0) - return r; /* returns EINVAL if not a symlink */ - - e = PATH_STARTSWITH_SET(t, "/usr/share/zoneinfo/", "../usr/share/zoneinfo/"); - if (!e) - return -EINVAL; - - if (!timezone_is_valid(e, LOG_DEBUG)) - return -EINVAL; - - z = strdup(e); - if (!z) - return -ENOMEM; - - *ret = z; - return 0; -} - -time_t mktime_or_timegm(struct tm *tm, bool utc) { - return utc ? timegm(tm) : mktime(tm); -} - -struct tm *localtime_or_gmtime_r(const time_t *t, struct tm *tm, bool utc) { - return utc ? gmtime_r(t, tm) : localtime_r(t, tm); -} - -static uint32_t sysconf_clock_ticks_cached(void) { - static thread_local uint32_t hz = 0; - long r; - - if (hz == 0) { - r = sysconf(_SC_CLK_TCK); - - assert(r > 0); - hz = r; - } - - return hz; -} - -uint32_t usec_to_jiffies(usec_t u) { - uint32_t hz = sysconf_clock_ticks_cached(); - return DIV_ROUND_UP(u, USEC_PER_SEC / hz); -} - -usec_t jiffies_to_usec(uint32_t j) { - uint32_t hz = sysconf_clock_ticks_cached(); - return DIV_ROUND_UP(j * USEC_PER_SEC, hz); -} - -usec_t usec_shift_clock(usec_t x, clockid_t from, clockid_t to) { - usec_t a, b; - - if (x == USEC_INFINITY) - return USEC_INFINITY; - if (map_clock_id(from) == map_clock_id(to)) - return x; - - a = now(from); - b = now(to); - - if (x > a) - /* x lies in the future */ - return usec_add(b, usec_sub_unsigned(x, a)); - else - /* x lies in the past */ - return usec_sub_unsigned(b, usec_sub_unsigned(a, x)); -} - -bool in_utc_timezone(void) { - tzset(); - - return timezone == 0 && daylight == 0; -} - -int time_change_fd(void) { - - /* We only care for the cancellation event, hence we set the timeout to the latest possible value. */ - static const struct itimerspec its = { - .it_value.tv_sec = TIME_T_MAX, - }; - - _cleanup_close_ int fd; - - assert_cc(sizeof(time_t) == sizeof(TIME_T_MAX)); - - /* Uses TFD_TIMER_CANCEL_ON_SET to get notifications whenever CLOCK_REALTIME makes a jump relative to - * CLOCK_MONOTONIC. */ - - fd = timerfd_create(CLOCK_REALTIME, TFD_NONBLOCK|TFD_CLOEXEC); - if (fd < 0) - return -errno; - - if (timerfd_settime(fd, TFD_TIMER_ABSTIME|TFD_TIMER_CANCEL_ON_SET, &its, NULL) >= 0) - return TAKE_FD(fd); - - /* So apparently there are systems where time_t is 64bit, but the kernel actually doesn't support - * 64bit time_t. In that case configuring a timer to TIME_T_MAX will fail with EOPNOTSUPP or a - * similar error. If that's the case let's try with INT32_MAX instead, maybe that works. It's a bit - * of a black magic thing though, but what can we do? - * - * We don't want this code on x86-64, hence let's conditionalize this for systems with 64bit time_t - * but where "long" is shorter than 64bit, i.e. 32bit archs. - * - * See: https://github.com/systemd/systemd/issues/14362 */ - -#if SIZEOF_TIME_T == 8 && ULONG_MAX < UINT64_MAX - if (ERRNO_IS_NOT_SUPPORTED(errno) || errno == EOVERFLOW) { - static const struct itimerspec its32 = { - .it_value.tv_sec = INT32_MAX, - }; - - if (timerfd_settime(fd, TFD_TIMER_ABSTIME|TFD_TIMER_CANCEL_ON_SET, &its32, NULL) >= 0) - return TAKE_FD(fd); - } -#endif - - return -errno; -} - -static const char* const timestamp_style_table[_TIMESTAMP_STYLE_MAX] = { - [TIMESTAMP_PRETTY] = "pretty", - [TIMESTAMP_US] = "us", - [TIMESTAMP_UTC] = "utc", - [TIMESTAMP_US_UTC] = "us+utc", -}; - -/* Use the macro for enum → string to allow for aliases */ -_DEFINE_STRING_TABLE_LOOKUP_TO_STRING(timestamp_style, TimestampStyle,); - -/* For the string → enum mapping we use the generic implementation, but also support two aliases */ -TimestampStyle timestamp_style_from_string(const char *s) { - TimestampStyle t; - - t = (TimestampStyle) string_table_lookup(timestamp_style_table, ELEMENTSOF(timestamp_style_table), s); - if (t >= 0) - return t; - if (streq_ptr(s, "µs")) - return TIMESTAMP_US; - if (streq_ptr(s, "µs+utc")) - return TIMESTAMP_US_UTC; - return t; -} -#endif /* NM_IGNORED */ diff --git a/shared/systemd/src/basic/time-util.h b/shared/systemd/src/basic/time-util.h deleted file mode 100644 index 89ee8b4a..00000000 --- a/shared/systemd/src/basic/time-util.h +++ /dev/null @@ -1,201 +0,0 @@ -/* SPDX-License-Identifier: LGPL-2.1-or-later */ -#pragma once - -#include <inttypes.h> -#include <stdbool.h> -#include <stddef.h> -#include <stdint.h> -#include <stdio.h> -#include <time.h> - -typedef uint64_t usec_t; -typedef uint64_t nsec_t; - -#define PRI_NSEC PRIu64 -#define PRI_USEC PRIu64 -#define NSEC_FMT "%" PRI_NSEC -#define USEC_FMT "%" PRI_USEC - -#include "macro.h" - -typedef struct dual_timestamp { - usec_t realtime; - usec_t monotonic; -} dual_timestamp; - -typedef struct triple_timestamp { - usec_t realtime; - usec_t monotonic; - usec_t boottime; -} triple_timestamp; - -typedef enum TimestampStyle { - TIMESTAMP_PRETTY, - TIMESTAMP_US, - TIMESTAMP_UTC, - TIMESTAMP_US_UTC, - _TIMESTAMP_STYLE_MAX, - _TIMESTAMP_STYLE_INVALID = -1, -} TimestampStyle; - -#define USEC_INFINITY ((usec_t) UINT64_MAX) -#define NSEC_INFINITY ((nsec_t) UINT64_MAX) - -#define MSEC_PER_SEC 1000ULL -#define USEC_PER_SEC ((usec_t) 1000000ULL) -#define USEC_PER_MSEC ((usec_t) 1000ULL) -#define NSEC_PER_SEC ((nsec_t) 1000000000ULL) -#define NSEC_PER_MSEC ((nsec_t) 1000000ULL) -#define NSEC_PER_USEC ((nsec_t) 1000ULL) - -#define USEC_PER_MINUTE ((usec_t) (60ULL*USEC_PER_SEC)) -#define NSEC_PER_MINUTE ((nsec_t) (60ULL*NSEC_PER_SEC)) -#define USEC_PER_HOUR ((usec_t) (60ULL*USEC_PER_MINUTE)) -#define NSEC_PER_HOUR ((nsec_t) (60ULL*NSEC_PER_MINUTE)) -#define USEC_PER_DAY ((usec_t) (24ULL*USEC_PER_HOUR)) -#define NSEC_PER_DAY ((nsec_t) (24ULL*NSEC_PER_HOUR)) -#define USEC_PER_WEEK ((usec_t) (7ULL*USEC_PER_DAY)) -#define NSEC_PER_WEEK ((nsec_t) (7ULL*NSEC_PER_DAY)) -#define USEC_PER_MONTH ((usec_t) (2629800ULL*USEC_PER_SEC)) -#define NSEC_PER_MONTH ((nsec_t) (2629800ULL*NSEC_PER_SEC)) -#define USEC_PER_YEAR ((usec_t) (31557600ULL*USEC_PER_SEC)) -#define NSEC_PER_YEAR ((nsec_t) (31557600ULL*NSEC_PER_SEC)) - -/* We assume a maximum timezone length of 6. TZNAME_MAX is not defined on Linux, but glibc internally initializes this - * to 6. Let's rely on that. */ -#define FORMAT_TIMESTAMP_MAX (3U+1U+10U+1U+8U+1U+6U+1U+6U+1U) -#define FORMAT_TIMESTAMP_WIDTH 28U /* when outputting, assume this width */ -#define FORMAT_TIMESTAMP_RELATIVE_MAX 256U -#define FORMAT_TIMESPAN_MAX 64U - -#define TIME_T_MAX (time_t)((UINTMAX_C(1) << ((sizeof(time_t) << 3) - 1)) - 1) - -#define DUAL_TIMESTAMP_NULL ((struct dual_timestamp) {}) -#define TRIPLE_TIMESTAMP_NULL ((struct triple_timestamp) {}) - -usec_t now(clockid_t clock); -nsec_t now_nsec(clockid_t clock); - -usec_t map_clock_usec(usec_t from, clockid_t from_clock, clockid_t to_clock); - -dual_timestamp* dual_timestamp_get(dual_timestamp *ts); -dual_timestamp* dual_timestamp_from_realtime(dual_timestamp *ts, usec_t u); -dual_timestamp* dual_timestamp_from_monotonic(dual_timestamp *ts, usec_t u); -dual_timestamp* dual_timestamp_from_boottime_or_monotonic(dual_timestamp *ts, usec_t u); - -triple_timestamp* triple_timestamp_get(triple_timestamp *ts); -triple_timestamp* triple_timestamp_from_realtime(triple_timestamp *ts, usec_t u); - -#define DUAL_TIMESTAMP_HAS_CLOCK(clock) \ - IN_SET(clock, CLOCK_REALTIME, CLOCK_REALTIME_ALARM, CLOCK_MONOTONIC) - -#define TRIPLE_TIMESTAMP_HAS_CLOCK(clock) \ - IN_SET(clock, CLOCK_REALTIME, CLOCK_REALTIME_ALARM, CLOCK_MONOTONIC, CLOCK_BOOTTIME, CLOCK_BOOTTIME_ALARM) - -static inline bool timestamp_is_set(usec_t timestamp) { - return timestamp > 0 && timestamp != USEC_INFINITY; -} - -static inline bool dual_timestamp_is_set(const dual_timestamp *ts) { - return timestamp_is_set(ts->realtime) || - timestamp_is_set(ts->monotonic); -} - -static inline bool triple_timestamp_is_set(const triple_timestamp *ts) { - return timestamp_is_set(ts->realtime) || - timestamp_is_set(ts->monotonic) || - timestamp_is_set(ts->boottime); -} - -usec_t triple_timestamp_by_clock(triple_timestamp *ts, clockid_t clock); - -usec_t timespec_load(const struct timespec *ts) _pure_; -nsec_t timespec_load_nsec(const struct timespec *ts) _pure_; -struct timespec *timespec_store(struct timespec *ts, usec_t u); -struct timespec *timespec_store_nsec(struct timespec *ts, nsec_t n); - -usec_t timeval_load(const struct timeval *tv) _pure_; -struct timeval *timeval_store(struct timeval *tv, usec_t u); - -char *format_timestamp_style(char *buf, size_t l, usec_t t, TimestampStyle style); -char *format_timestamp_relative(char *buf, size_t l, usec_t t); -char *format_timespan(char *buf, size_t l, usec_t t, usec_t accuracy); - -static inline char *format_timestamp(char *buf, size_t l, usec_t t) { - return format_timestamp_style(buf, l, t, TIMESTAMP_PRETTY); -} - -int parse_timestamp(const char *t, usec_t *usec); - -int parse_sec(const char *t, usec_t *usec); -int parse_sec_fix_0(const char *t, usec_t *usec); -int parse_sec_def_infinity(const char *t, usec_t *usec); -int parse_time(const char *t, usec_t *usec, usec_t default_unit); -int parse_nsec(const char *t, nsec_t *nsec); - -bool ntp_synced(void); - -int get_timezones(char ***l); -bool timezone_is_valid(const char *name, int log_level); - -bool clock_boottime_supported(void); -bool clock_supported(clockid_t clock); -clockid_t clock_boottime_or_monotonic(void); - -usec_t usec_shift_clock(usec_t, clockid_t from, clockid_t to); - -int get_timezone(char **timezone); - -time_t mktime_or_timegm(struct tm *tm, bool utc); -struct tm *localtime_or_gmtime_r(const time_t *t, struct tm *tm, bool utc); - -uint32_t usec_to_jiffies(usec_t usec); -usec_t jiffies_to_usec(uint32_t jiffies); - -bool in_utc_timezone(void); - -static inline usec_t usec_add(usec_t a, usec_t b) { - usec_t c; - - /* Adds two time values, and makes sure USEC_INFINITY as input results as USEC_INFINITY in output, and doesn't - * overflow. */ - - c = a + b; - if (c < a || c < b) /* overflow check */ - return USEC_INFINITY; - - return c; -} - -static inline usec_t usec_sub_unsigned(usec_t timestamp, usec_t delta) { - - if (timestamp == USEC_INFINITY) /* Make sure infinity doesn't degrade */ - return USEC_INFINITY; - if (timestamp < delta) - return 0; - - return timestamp - delta; -} - -static inline usec_t usec_sub_signed(usec_t timestamp, int64_t delta) { - if (delta < 0) - return usec_add(timestamp, (usec_t) (-delta)); - else - return usec_sub_unsigned(timestamp, (usec_t) delta); -} - -#if SIZEOF_TIME_T == 8 -/* The last second we can format is 31. Dec 9999, 1s before midnight, because otherwise we'd enter 5 digit year - * territory. However, since we want to stay away from this in all timezones we take one day off. */ -#define USEC_TIMESTAMP_FORMATTABLE_MAX ((usec_t) 253402214399000000) -#elif SIZEOF_TIME_T == 4 -/* With a 32bit time_t we can't go beyond 2038... */ -#define USEC_TIMESTAMP_FORMATTABLE_MAX ((usec_t) 2147483647000000) -#else -#error "Yuck, time_t is neither 4 nor 8 bytes wide?" -#endif - -int time_change_fd(void); - -const char* timestamp_style_to_string(TimestampStyle t) _const_; -TimestampStyle timestamp_style_from_string(const char *s) _pure_; diff --git a/shared/systemd/src/basic/tmpfile-util.c b/shared/systemd/src/basic/tmpfile-util.c deleted file mode 100644 index bbd6a1ed..00000000 --- a/shared/systemd/src/basic/tmpfile-util.c +++ /dev/null @@ -1,344 +0,0 @@ -/* SPDX-License-Identifier: LGPL-2.1-or-later */ - -#include "nm-sd-adapt-shared.h" - -#include <sys/mman.h> - -#include "alloc-util.h" -#include "fd-util.h" -#include "fileio.h" -#include "fs-util.h" -#include "hexdecoct.h" -#include "macro.h" -#include "memfd-util.h" -#include "missing_fcntl.h" -#include "missing_syscall.h" -#include "path-util.h" -#include "process-util.h" -#include "random-util.h" -#include "stdio-util.h" -#include "string-util.h" -#include "tmpfile-util.h" -#include "umask-util.h" - -int fopen_temporary(const char *path, FILE **ret_f, char **ret_temp_path) { - _cleanup_fclose_ FILE *f = NULL; - _cleanup_free_ char *t = NULL; - _cleanup_close_ int fd = -1; - int r; - - if (path) { - r = tempfn_xxxxxx(path, NULL, &t); - if (r < 0) - return r; - } else { - const char *d; - - r = tmp_dir(&d); - if (r < 0) - return r; - - t = path_join(d, "XXXXXX"); - if (!t) - return -ENOMEM; - } - - fd = mkostemp_safe(t); - if (fd < 0) - return -errno; - - /* This assumes that returned FILE object is short-lived and used within the same single-threaded - * context and never shared externally, hence locking is not necessary. */ - - r = take_fdopen_unlocked(&fd, "w", &f); - if (r < 0) { - (void) unlink(t); - return r; - } - - if (ret_f) - *ret_f = TAKE_PTR(f); - - if (ret_temp_path) - *ret_temp_path = TAKE_PTR(t); - - return 0; -} - -/* This is much like mkostemp() but is subject to umask(). */ -int mkostemp_safe(char *pattern) { - int fd = -1; /* avoid false maybe-uninitialized warning */ - - assert(pattern); - - RUN_WITH_UMASK(0077) - fd = mkostemp(pattern, O_CLOEXEC); - if (fd < 0) - return -errno; - - return fd; -} - -#if 0 /* NM_IGNORED */ -int fmkostemp_safe(char *pattern, const char *mode, FILE **ret_f) { - _cleanup_close_ int fd = -1; - FILE *f; - - fd = mkostemp_safe(pattern); - if (fd < 0) - return fd; - - f = take_fdopen(&fd, mode); - if (!f) - return -errno; - - *ret_f = f; - return 0; -} -#endif /* NM_IGNORED */ - -int tempfn_xxxxxx(const char *p, const char *extra, char **ret) { - const char *fn; - char *t; - - assert(ret); - - if (isempty(p)) - return -EINVAL; - if (path_equal(p, "/")) - return -EINVAL; - - /* - * Turns this: - * /foo/bar/waldo - * - * Into this: - * /foo/bar/.#<extra>waldoXXXXXX - */ - - fn = basename(p); - if (!filename_is_valid(fn)) - return -EINVAL; - - extra = strempty(extra); - - t = new(char, strlen(p) + 2 + strlen(extra) + 6 + 1); - if (!t) - return -ENOMEM; - - strcpy(stpcpy(stpcpy(stpcpy(mempcpy(t, p, fn - p), ".#"), extra), fn), "XXXXXX"); - - *ret = path_simplify(t, false); - return 0; -} - -#if 0 /* NM_IGNORED */ -int tempfn_random(const char *p, const char *extra, char **ret) { - const char *fn; - char *t, *x; - uint64_t u; - unsigned i; - - assert(ret); - - if (isempty(p)) - return -EINVAL; - if (path_equal(p, "/")) - return -EINVAL; - - /* - * Turns this: - * /foo/bar/waldo - * - * Into this: - * /foo/bar/.#<extra>waldobaa2a261115984a9 - */ - - fn = basename(p); - if (!filename_is_valid(fn)) - return -EINVAL; - - extra = strempty(extra); - - t = new(char, strlen(p) + 2 + strlen(extra) + 16 + 1); - if (!t) - return -ENOMEM; - - x = stpcpy(stpcpy(stpcpy(mempcpy(t, p, fn - p), ".#"), extra), fn); - - u = random_u64(); - for (i = 0; i < 16; i++) { - *(x++) = hexchar(u & 0xF); - u >>= 4; - } - - *x = 0; - - *ret = path_simplify(t, false); - return 0; -} - -int tempfn_random_child(const char *p, const char *extra, char **ret) { - char *t, *x; - uint64_t u; - unsigned i; - int r; - - assert(ret); - - /* Turns this: - * /foo/bar/waldo - * Into this: - * /foo/bar/waldo/.#<extra>3c2b6219aa75d7d0 - */ - - if (!p) { - r = tmp_dir(&p); - if (r < 0) - return r; - } - - extra = strempty(extra); - - t = new(char, strlen(p) + 3 + strlen(extra) + 16 + 1); - if (!t) - return -ENOMEM; - - if (isempty(p)) - x = stpcpy(stpcpy(t, ".#"), extra); - else - x = stpcpy(stpcpy(stpcpy(t, p), "/.#"), extra); - - u = random_u64(); - for (i = 0; i < 16; i++) { - *(x++) = hexchar(u & 0xF); - u >>= 4; - } - - *x = 0; - - *ret = path_simplify(t, false); - return 0; -} - -int open_tmpfile_unlinkable(const char *directory, int flags) { - char *p; - int fd, r; - - if (!directory) { - r = tmp_dir(&directory); - if (r < 0) - return r; - } else if (isempty(directory)) - return -EINVAL; - - /* Returns an unlinked temporary file that cannot be linked into the file system anymore */ - - /* Try O_TMPFILE first, if it is supported */ - fd = open(directory, flags|O_TMPFILE|O_EXCL, S_IRUSR|S_IWUSR); - if (fd >= 0) - return fd; - - /* Fall back to unguessable name + unlinking */ - p = strjoina(directory, "/systemd-tmp-XXXXXX"); - - fd = mkostemp_safe(p); - if (fd < 0) - return fd; - - (void) unlink(p); - - return fd; -} - -int open_tmpfile_linkable(const char *target, int flags, char **ret_path) { - _cleanup_free_ char *tmp = NULL; - int r, fd; - - assert(target); - assert(ret_path); - - /* Don't allow O_EXCL, as that has a special meaning for O_TMPFILE */ - assert((flags & O_EXCL) == 0); - - /* Creates a temporary file, that shall be renamed to "target" later. If possible, this uses O_TMPFILE – in - * which case "ret_path" will be returned as NULL. If not possible the temporary path name used is returned in - * "ret_path". Use link_tmpfile() below to rename the result after writing the file in full. */ - - fd = open_parent(target, O_TMPFILE|flags, 0640); - if (fd >= 0) { - *ret_path = NULL; - return fd; - } - - log_debug_errno(fd, "Failed to use O_TMPFILE for %s: %m", target); - - r = tempfn_random(target, NULL, &tmp); - if (r < 0) - return r; - - fd = open(tmp, O_CREAT|O_EXCL|O_NOFOLLOW|O_NOCTTY|flags, 0640); - if (fd < 0) - return -errno; - - *ret_path = TAKE_PTR(tmp); - - return fd; -} - -int link_tmpfile(int fd, const char *path, const char *target) { - int r; - - assert(fd >= 0); - assert(target); - - /* Moves a temporary file created with open_tmpfile() above into its final place. if "path" is NULL an fd - * created with O_TMPFILE is assumed, and linkat() is used. Otherwise it is assumed O_TMPFILE is not supported - * on the directory, and renameat2() is used instead. - * - * Note that in both cases we will not replace existing files. This is because linkat() does not support this - * operation currently (renameat2() does), and there is no nice way to emulate this. */ - - if (path) { - r = rename_noreplace(AT_FDCWD, path, AT_FDCWD, target); - if (r < 0) - return r; - } else { - char proc_fd_path[STRLEN("/proc/self/fd/") + DECIMAL_STR_MAX(fd) + 1]; - - xsprintf(proc_fd_path, "/proc/self/fd/%i", fd); - - if (linkat(AT_FDCWD, proc_fd_path, AT_FDCWD, target, AT_SYMLINK_FOLLOW) < 0) - return -errno; - } - - return 0; -} - -int mkdtemp_malloc(const char *template, char **ret) { - _cleanup_free_ char *p = NULL; - int r; - - assert(ret); - - if (template) - p = strdup(template); - else { - const char *tmp; - - r = tmp_dir(&tmp); - if (r < 0) - return r; - - p = path_join(tmp, "XXXXXX"); - } - if (!p) - return -ENOMEM; - - if (!mkdtemp(p)) - return -errno; - - *ret = TAKE_PTR(p); - return 0; -} -#endif /* NM_IGNORED */ diff --git a/shared/systemd/src/basic/tmpfile-util.h b/shared/systemd/src/basic/tmpfile-util.h deleted file mode 100644 index 45255fc0..00000000 --- a/shared/systemd/src/basic/tmpfile-util.h +++ /dev/null @@ -1,19 +0,0 @@ -/* SPDX-License-Identifier: LGPL-2.1-or-later */ -#pragma once - -#include <stdio.h> - -int fopen_temporary(const char *path, FILE **_f, char **_temp_path); -int mkostemp_safe(char *pattern); -int fmkostemp_safe(char *pattern, const char *mode, FILE**_f); - -int tempfn_xxxxxx(const char *p, const char *extra, char **ret); -int tempfn_random(const char *p, const char *extra, char **ret); -int tempfn_random_child(const char *p, const char *extra, char **ret); - -int open_tmpfile_unlinkable(const char *directory, int flags); -int open_tmpfile_linkable(const char *target, int flags, char **ret_path); - -int link_tmpfile(int fd, const char *path, const char *target); - -int mkdtemp_malloc(const char *template, char **ret); diff --git a/shared/systemd/src/basic/umask-util.h b/shared/systemd/src/basic/umask-util.h deleted file mode 100644 index bd7c2bdb..00000000 --- a/shared/systemd/src/basic/umask-util.h +++ /dev/null @@ -1,26 +0,0 @@ -/* SPDX-License-Identifier: LGPL-2.1-or-later */ -#pragma once - -#include <stdbool.h> -#include <sys/stat.h> -#include <sys/types.h> - -#include "macro.h" - -static inline void umaskp(mode_t *u) { - umask(*u & 0777); -} - -#define _cleanup_umask_ _cleanup_(umaskp) - -/* We make use of the fact here that the umask() concept is using only the lower 9 bits of mode_t, although - * mode_t has space for the file type in the bits further up. We simply OR in the file type mask S_IFMT to - * distinguish the first and the second iteration of the RUN_WITH_UMASK() loop, so that we can run the first - * one, and exit on the second. */ - -assert_cc((S_IFMT & 0777) == 0); - -#define RUN_WITH_UMASK(mask) \ - for (_cleanup_umask_ mode_t _saved_umask_ = umask(mask) | S_IFMT; \ - FLAGS_SET(_saved_umask_, S_IFMT); \ - _saved_umask_ &= 0777) diff --git a/shared/systemd/src/basic/utf8.c b/shared/systemd/src/basic/utf8.c deleted file mode 100644 index a7679bfa..00000000 --- a/shared/systemd/src/basic/utf8.c +++ /dev/null @@ -1,595 +0,0 @@ -/* SPDX-License-Identifier: LGPL-2.1-or-later */ - -/* Parts of this file are based on the GLIB utf8 validation functions. The - * original license text follows. */ - -/* gutf8.c - Operations on UTF-8 strings. - * - * Copyright (C) 1999 Tom Tromey - * Copyright (C) 2000 Red Hat, Inc. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Library General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Library General Public License for more details. - * - * You should have received a copy of the GNU Library General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#include "nm-sd-adapt-shared.h" - -#include <errno.h> -#include <stdbool.h> -#include <stdlib.h> - -#include "alloc-util.h" -#include "gunicode.h" -#include "hexdecoct.h" -#include "macro.h" -#include "string-util.h" -#include "utf8.h" - -bool unichar_is_valid(char32_t ch) { - - if (ch >= 0x110000) /* End of unicode space */ - return false; - if ((ch & 0xFFFFF800) == 0xD800) /* Reserved area for UTF-16 */ - return false; - if ((ch >= 0xFDD0) && (ch <= 0xFDEF)) /* Reserved */ - return false; - if ((ch & 0xFFFE) == 0xFFFE) /* BOM (Byte Order Mark) */ - return false; - - return true; -} - -static bool unichar_is_control(char32_t ch) { - - /* - 0 to ' '-1 is the C0 range. - DEL=0x7F, and DEL+1 to 0x9F is C1 range. - '\t' is in C0 range, but more or less harmless and commonly used. - */ - - return (ch < ' ' && !IN_SET(ch, '\t', '\n')) || - (0x7F <= ch && ch <= 0x9F); -} - -/* count of characters used to encode one unicode char */ -static size_t utf8_encoded_expected_len(uint8_t c) { - if (c < 0x80) - return 1; - if ((c & 0xe0) == 0xc0) - return 2; - if ((c & 0xf0) == 0xe0) - return 3; - if ((c & 0xf8) == 0xf0) - return 4; - if ((c & 0xfc) == 0xf8) - return 5; - if ((c & 0xfe) == 0xfc) - return 6; - - return 0; -} - -/* decode one unicode char */ -int utf8_encoded_to_unichar(const char *str, char32_t *ret_unichar) { - char32_t unichar; - size_t len, i; - - assert(str); - - len = utf8_encoded_expected_len(str[0]); - - switch (len) { - case 1: - *ret_unichar = (char32_t)str[0]; - return 0; - case 2: - unichar = str[0] & 0x1f; - break; - case 3: - unichar = (char32_t)str[0] & 0x0f; - break; - case 4: - unichar = (char32_t)str[0] & 0x07; - break; - case 5: - unichar = (char32_t)str[0] & 0x03; - break; - case 6: - unichar = (char32_t)str[0] & 0x01; - break; - default: - return -EINVAL; - } - - for (i = 1; i < len; i++) { - if (((char32_t)str[i] & 0xc0) != 0x80) - return -EINVAL; - - unichar <<= 6; - unichar |= (char32_t)str[i] & 0x3f; - } - - *ret_unichar = unichar; - - return 0; -} - -bool utf8_is_printable_newline(const char* str, size_t length, bool allow_newline) { - const char *p; - - assert(str); - - for (p = str; length > 0;) { - int encoded_len, r; - char32_t val; - - encoded_len = utf8_encoded_valid_unichar(p, length); - if (encoded_len < 0) - return false; - assert(encoded_len > 0 && (size_t) encoded_len <= length); - - r = utf8_encoded_to_unichar(p, &val); - if (r < 0 || - unichar_is_control(val) || - (!allow_newline && val == '\n')) - return false; - - length -= encoded_len; - p += encoded_len; - } - - return true; -} - -char *utf8_is_valid_n(const char *str, size_t len_bytes) { - /* Check if the string is composed of valid utf8 characters. If length len_bytes is given, stop after - * len_bytes. Otherwise, stop at NUL. */ - - assert(str); - - for (const char *p = str; len_bytes != (size_t) -1 ? (size_t) (p - str) < len_bytes : *p != '\0'; ) { - int len; - - if (_unlikely_(*p == '\0') && len_bytes != (size_t) -1) - return NULL; /* embedded NUL */ - - len = utf8_encoded_valid_unichar(p, - len_bytes != (size_t) -1 ? len_bytes - (p - str) : (size_t) -1); - if (_unlikely_(len < 0)) - return NULL; /* invalid character */ - - p += len; - } - - return (char*) str; -} - -char *utf8_escape_invalid(const char *str) { - char *p, *s; - - assert(str); - - p = s = malloc(strlen(str) * 4 + 1); - if (!p) - return NULL; - - while (*str) { - int len; - - len = utf8_encoded_valid_unichar(str, (size_t) -1); - if (len > 0) { - s = mempcpy(s, str, len); - str += len; - } else { - s = stpcpy(s, UTF8_REPLACEMENT_CHARACTER); - str += 1; - } - } - - *s = '\0'; - (void) str_realloc(&p); - return p; -} - -#if 0 /* NM_IGNORED */ -static int utf8_char_console_width(const char *str) { - char32_t c; - int r; - - r = utf8_encoded_to_unichar(str, &c); - if (r < 0) - return r; - - /* TODO: we should detect combining characters */ - - return unichar_iswide(c) ? 2 : 1; -} - -char *utf8_escape_non_printable_full(const char *str, size_t console_width) { - char *p, *s, *prev_s; - size_t n = 0; /* estimated print width */ - - assert(str); - - if (console_width == 0) - return strdup(""); - - p = s = prev_s = malloc(strlen(str) * 4 + 1); - if (!p) - return NULL; - - for (;;) { - int len; - char *saved_s = s; - - if (!*str) /* done! */ - goto finish; - - len = utf8_encoded_valid_unichar(str, (size_t) -1); - if (len > 0) { - if (utf8_is_printable(str, len)) { - int w; - - w = utf8_char_console_width(str); - assert(w >= 0); - if (n + w > console_width) - goto truncation; - - s = mempcpy(s, str, len); - str += len; - n += w; - - } else { - for (; len > 0; len--) { - if (n + 4 > console_width) - goto truncation; - - *(s++) = '\\'; - *(s++) = 'x'; - *(s++) = hexchar((int) *str >> 4); - *(s++) = hexchar((int) *str); - - str += 1; - n += 4; - } - } - } else { - if (n + 1 > console_width) - goto truncation; - - s = mempcpy(s, UTF8_REPLACEMENT_CHARACTER, strlen(UTF8_REPLACEMENT_CHARACTER)); - str += 1; - n += 1; - } - - prev_s = saved_s; - } - - truncation: - /* Try to go back one if we don't have enough space for the ellipsis */ - if (n + 1 >= console_width) - s = prev_s; - - s = mempcpy(s, "…", strlen("…")); - - finish: - *s = '\0'; - (void) str_realloc(&p); - return p; -} -#endif /* NM_IGNORED */ - -char *ascii_is_valid(const char *str) { - const char *p; - - /* Check whether the string consists of valid ASCII bytes, - * i.e values between 0 and 127, inclusive. */ - - assert(str); - - for (p = str; *p; p++) - if ((unsigned char) *p >= 128) - return NULL; - - return (char*) str; -} - -#if 0 /* NM_IGNORED */ -char *ascii_is_valid_n(const char *str, size_t len) { - size_t i; - - /* Very similar to ascii_is_valid(), but checks exactly len - * bytes and rejects any NULs in that range. */ - - assert(str); - - for (i = 0; i < len; i++) - if ((unsigned char) str[i] >= 128 || str[i] == 0) - return NULL; - - return (char*) str; -} -#endif /* NM_IGNORED */ - -/** - * utf8_encode_unichar() - Encode single UCS-4 character as UTF-8 - * @out_utf8: output buffer of at least 4 bytes or NULL - * @g: UCS-4 character to encode - * - * This encodes a single UCS-4 character as UTF-8 and writes it into @out_utf8. - * The length of the character is returned. It is not zero-terminated! If the - * output buffer is NULL, only the length is returned. - * - * Returns: The length in bytes that the UTF-8 representation does or would - * occupy. - */ -size_t utf8_encode_unichar(char *out_utf8, char32_t g) { - - if (g < (1 << 7)) { - if (out_utf8) - out_utf8[0] = g & 0x7f; - return 1; - } else if (g < (1 << 11)) { - if (out_utf8) { - out_utf8[0] = 0xc0 | ((g >> 6) & 0x1f); - out_utf8[1] = 0x80 | (g & 0x3f); - } - return 2; - } else if (g < (1 << 16)) { - if (out_utf8) { - out_utf8[0] = 0xe0 | ((g >> 12) & 0x0f); - out_utf8[1] = 0x80 | ((g >> 6) & 0x3f); - out_utf8[2] = 0x80 | (g & 0x3f); - } - return 3; - } else if (g < (1 << 21)) { - if (out_utf8) { - out_utf8[0] = 0xf0 | ((g >> 18) & 0x07); - out_utf8[1] = 0x80 | ((g >> 12) & 0x3f); - out_utf8[2] = 0x80 | ((g >> 6) & 0x3f); - out_utf8[3] = 0x80 | (g & 0x3f); - } - return 4; - } - - return 0; -} - -#if 0 /* NM_IGNORED */ -char *utf16_to_utf8(const char16_t *s, size_t length /* bytes! */) { - const uint8_t *f; - char *r, *t; - - assert(s); - - /* Input length is in bytes, i.e. the shortest possible character takes 2 bytes. Each unicode character may - * take up to 4 bytes in UTF-8. Let's also account for a trailing NUL byte. */ - if (length * 2 < length) - return NULL; /* overflow */ - - r = new(char, length * 2 + 1); - if (!r) - return NULL; - - f = (const uint8_t*) s; - t = r; - - while (f + 1 < (const uint8_t*) s + length) { - char16_t w1, w2; - - /* see RFC 2781 section 2.2 */ - - w1 = f[1] << 8 | f[0]; - f += 2; - - if (!utf16_is_surrogate(w1)) { - t += utf8_encode_unichar(t, w1); - continue; - } - - if (utf16_is_trailing_surrogate(w1)) - continue; /* spurious trailing surrogate, ignore */ - - if (f + 1 >= (const uint8_t*) s + length) - break; - - w2 = f[1] << 8 | f[0]; - f += 2; - - if (!utf16_is_trailing_surrogate(w2)) { - f -= 2; - continue; /* surrogate missing its trailing surrogate, ignore */ - } - - t += utf8_encode_unichar(t, utf16_surrogate_pair_to_unichar(w1, w2)); - } - - *t = 0; - return r; -} - -size_t utf16_encode_unichar(char16_t *out, char32_t c) { - - /* Note that this encodes as little-endian. */ - - switch (c) { - - case 0 ... 0xd7ffU: - case 0xe000U ... 0xffffU: - out[0] = htole16(c); - return 1; - - case 0x10000U ... 0x10ffffU: - c -= 0x10000U; - out[0] = htole16((c >> 10) + 0xd800U); - out[1] = htole16((c & 0x3ffU) + 0xdc00U); - return 2; - - default: /* A surrogate (invalid) */ - return 0; - } -} - -char16_t *utf8_to_utf16(const char *s, size_t length) { - char16_t *n, *p; - size_t i; - int r; - - assert(s); - - n = new(char16_t, length + 1); - if (!n) - return NULL; - - p = n; - - for (i = 0; i < length;) { - char32_t unichar; - size_t e; - - e = utf8_encoded_expected_len(s[i]); - if (e <= 1) /* Invalid and single byte characters are copied as they are */ - goto copy; - - if (i + e > length) /* sequence longer than input buffer, then copy as-is */ - goto copy; - - r = utf8_encoded_to_unichar(s + i, &unichar); - if (r < 0) /* sequence invalid, then copy as-is */ - goto copy; - - p += utf16_encode_unichar(p, unichar); - i += e; - continue; - - copy: - *(p++) = htole16(s[i++]); - } - - *p = 0; - return n; -} - -size_t char16_strlen(const char16_t *s) { - size_t n = 0; - - assert(s); - - while (*s != 0) - n++, s++; - - return n; -} -#endif /* NM_IGNORED */ - -/* expected size used to encode one unicode char */ -static int utf8_unichar_to_encoded_len(char32_t unichar) { - - if (unichar < 0x80) - return 1; - if (unichar < 0x800) - return 2; - if (unichar < 0x10000) - return 3; - if (unichar < 0x200000) - return 4; - if (unichar < 0x4000000) - return 5; - - return 6; -} - -/* validate one encoded unicode char and return its length */ -int utf8_encoded_valid_unichar(const char *str, size_t length /* bytes */) { - char32_t unichar; - size_t len, i; - int r; - - assert(str); - assert(length > 0); - - /* We read until NUL, at most length bytes. (size_t) -1 may be used to disable the length check. */ - - len = utf8_encoded_expected_len(str[0]); - if (len == 0) - return -EINVAL; - - /* Do we have a truncated multi-byte character? */ - if (len > length) - return -EINVAL; - - /* ascii is valid */ - if (len == 1) - return 1; - - /* check if expected encoded chars are available */ - for (i = 0; i < len; i++) - if ((str[i] & 0x80) != 0x80) - return -EINVAL; - - r = utf8_encoded_to_unichar(str, &unichar); - if (r < 0) - return r; - - /* check if encoded length matches encoded value */ - if (utf8_unichar_to_encoded_len(unichar) != (int) len) - return -EINVAL; - - /* check if value has valid range */ - if (!unichar_is_valid(unichar)) - return -EINVAL; - - return (int) len; -} - -#if 0 /* NM_IGNORED */ -size_t utf8_n_codepoints(const char *str) { - size_t n = 0; - - /* Returns the number of UTF-8 codepoints in this string, or (size_t) -1 if the string is not valid UTF-8. */ - - while (*str != 0) { - int k; - - k = utf8_encoded_valid_unichar(str, (size_t) -1); - if (k < 0) - return (size_t) -1; - - str += k; - n++; - } - - return n; -} - -size_t utf8_console_width(const char *str) { - size_t n = 0; - - /* Returns the approximate width a string will take on screen when printed on a character cell - * terminal/console. */ - - while (*str) { - int w; - - w = utf8_char_console_width(str); - if (w < 0) - return (size_t) -1; - - n += w; - str = utf8_next_char(str); - } - - return n; -} -#endif /* NM_IGNORED */ diff --git a/shared/systemd/src/basic/utf8.h b/shared/systemd/src/basic/utf8.h deleted file mode 100644 index a6ea942c..00000000 --- a/shared/systemd/src/basic/utf8.h +++ /dev/null @@ -1,57 +0,0 @@ -/* SPDX-License-Identifier: LGPL-2.1-or-later */ -#pragma once - -#include <stdbool.h> -#include <stddef.h> -#include <stdint.h> -#include <uchar.h> - -#include "macro.h" -#include "missing_type.h" - -#define UTF8_REPLACEMENT_CHARACTER "\xef\xbf\xbd" -#define UTF8_BYTE_ORDER_MARK "\xef\xbb\xbf" - -bool unichar_is_valid(char32_t c); - -char *utf8_is_valid_n(const char *str, size_t len_bytes) _pure_; -static inline char *utf8_is_valid(const char *s) { - return utf8_is_valid_n(s, (size_t) -1); -} -char *ascii_is_valid(const char *s) _pure_; -char *ascii_is_valid_n(const char *str, size_t len); - -bool utf8_is_printable_newline(const char* str, size_t length, bool allow_newline) _pure_; -#define utf8_is_printable(str, length) utf8_is_printable_newline(str, length, true) - -char *utf8_escape_invalid(const char *s); -char *utf8_escape_non_printable_full(const char *str, size_t console_width); -static inline char *utf8_escape_non_printable(const char *str) { - return utf8_escape_non_printable_full(str, (size_t) -1); -} - -size_t utf8_encode_unichar(char *out_utf8, char32_t g); -size_t utf16_encode_unichar(char16_t *out, char32_t c); - -char *utf16_to_utf8(const char16_t *s, size_t length /* bytes! */); -char16_t *utf8_to_utf16(const char *s, size_t length); - -size_t char16_strlen(const char16_t *s); /* returns the number of 16bit words in the string (not bytes!) */ - -int utf8_encoded_valid_unichar(const char *str, size_t length); -int utf8_encoded_to_unichar(const char *str, char32_t *ret_unichar); - -static inline bool utf16_is_surrogate(char16_t c) { - return c >= 0xd800U && c <= 0xdfffU; -} - -static inline bool utf16_is_trailing_surrogate(char16_t c) { - return c >= 0xdc00U && c <= 0xdfffU; -} - -static inline char32_t utf16_surrogate_pair_to_unichar(char16_t lead, char16_t trail) { - return ((((char32_t) lead - 0xd800U) << 10) + ((char32_t) trail - 0xdc00U) + 0x10000U); -} - -size_t utf8_n_codepoints(const char *str); -size_t utf8_console_width(const char *str); diff --git a/shared/systemd/src/basic/util.c b/shared/systemd/src/basic/util.c deleted file mode 100644 index 10a5bcff..00000000 --- a/shared/systemd/src/basic/util.c +++ /dev/null @@ -1,277 +0,0 @@ -/* SPDX-License-Identifier: LGPL-2.1-or-later */ - -#include "nm-sd-adapt-shared.h" - -#include <errno.h> -#include <fcntl.h> -#include <sys/mman.h> - -#include "alloc-util.h" -#include "build.h" -#include "dirent-util.h" -#include "env-file.h" -#include "env-util.h" -#include "fd-util.h" -#include "fileio.h" -#include "hostname-util.h" -#include "log.h" -#include "macro.h" -#include "parse-util.h" -#include "stat-util.h" -#include "string-util.h" -#include "util.h" -#include "virt.h" - -#if 0 /* NM_IGNORED */ -int saved_argc = 0; -char **saved_argv = NULL; -static int saved_in_initrd = -1; - -bool kexec_loaded(void) { - _cleanup_free_ char *s = NULL; - - if (read_one_line_file("/sys/kernel/kexec_loaded", &s) < 0) - return false; - - return s[0] == '1'; -} - -int prot_from_flags(int flags) { - - switch (flags & O_ACCMODE) { - - case O_RDONLY: - return PROT_READ; - - case O_WRONLY: - return PROT_WRITE; - - case O_RDWR: - return PROT_READ|PROT_WRITE; - - default: - return -EINVAL; - } -} - -bool in_initrd(void) { - struct statfs s; - int r; - - if (saved_in_initrd >= 0) - return saved_in_initrd; - - /* We make two checks here: - * - * 1. the flag file /etc/initrd-release must exist - * 2. the root file system must be a memory file system - * - * The second check is extra paranoia, since misdetecting an - * initrd can have bad consequences due the initrd - * emptying when transititioning to the main systemd. - */ - - r = getenv_bool_secure("SYSTEMD_IN_INITRD"); - if (r < 0 && r != -ENXIO) - log_debug_errno(r, "Failed to parse $SYSTEMD_IN_INITRD, ignoring: %m"); - - if (r >= 0) - saved_in_initrd = r > 0; - else - saved_in_initrd = access("/etc/initrd-release", F_OK) >= 0 && - statfs("/", &s) >= 0 && - is_temporary_fs(&s); - - return saved_in_initrd; -} - -void in_initrd_force(bool value) { - saved_in_initrd = value; -} - -int on_ac_power(void) { - bool found_offline = false, found_online = false; - _cleanup_closedir_ DIR *d = NULL; - struct dirent *de; - - d = opendir("/sys/class/power_supply"); - if (!d) - return errno == ENOENT ? true : -errno; - - FOREACH_DIRENT(de, d, return -errno) { - _cleanup_close_ int fd = -1, device = -1; - char contents[6]; - ssize_t n; - - device = openat(dirfd(d), de->d_name, O_DIRECTORY|O_RDONLY|O_CLOEXEC|O_NOCTTY); - if (device < 0) { - if (IN_SET(errno, ENOENT, ENOTDIR)) - continue; - - return -errno; - } - - fd = openat(device, "type", O_RDONLY|O_CLOEXEC|O_NOCTTY); - if (fd < 0) { - if (errno == ENOENT) - continue; - - return -errno; - } - - n = read(fd, contents, sizeof(contents)); - if (n < 0) - return -errno; - - if (n != 6 || memcmp(contents, "Mains\n", 6)) - continue; - - safe_close(fd); - fd = openat(device, "online", O_RDONLY|O_CLOEXEC|O_NOCTTY); - if (fd < 0) { - if (errno == ENOENT) - continue; - - return -errno; - } - - n = read(fd, contents, sizeof(contents)); - if (n < 0) - return -errno; - - if (n != 2 || contents[1] != '\n') - return -EIO; - - if (contents[0] == '1') { - found_online = true; - break; - } else if (contents[0] == '0') - found_offline = true; - else - return -EIO; - } - - return found_online || !found_offline; -} - -int container_get_leader(const char *machine, pid_t *pid) { - _cleanup_free_ char *s = NULL, *class = NULL; - const char *p; - pid_t leader; - int r; - - assert(machine); - assert(pid); - - if (streq(machine, ".host")) { - *pid = 1; - return 0; - } - - if (!hostname_is_valid(machine, 0)) - return -EINVAL; - - p = strjoina("/run/systemd/machines/", machine); - r = parse_env_file(NULL, p, - "LEADER", &s, - "CLASS", &class); - if (r == -ENOENT) - return -EHOSTDOWN; - if (r < 0) - return r; - if (!s) - return -EIO; - - if (!streq_ptr(class, "container")) - return -EIO; - - r = parse_pid(s, &leader); - if (r < 0) - return r; - if (leader <= 1) - return -EIO; - - *pid = leader; - return 0; -} - -int version(void) { - printf("systemd " STRINGIFY(PROJECT_VERSION) " (" GIT_VERSION ")\n%s\n", - systemd_features); - return 0; -} - -/* This is a direct translation of str_verscmp from boot.c */ -static bool is_digit(int c) { - return c >= '0' && c <= '9'; -} - -static int c_order(int c) { - if (c == 0 || is_digit(c)) - return 0; - - if ((c >= 'a') && (c <= 'z')) - return c; - - return c + 0x10000; -} - -int str_verscmp(const char *s1, const char *s2) { - const char *os1, *os2; - - assert(s1); - assert(s2); - - os1 = s1; - os2 = s2; - - while (*s1 || *s2) { - int first; - - while ((*s1 && !is_digit(*s1)) || (*s2 && !is_digit(*s2))) { - int order; - - order = c_order(*s1) - c_order(*s2); - if (order != 0) - return order; - s1++; - s2++; - } - - while (*s1 == '0') - s1++; - while (*s2 == '0') - s2++; - - first = 0; - while (is_digit(*s1) && is_digit(*s2)) { - if (first == 0) - first = *s1 - *s2; - s1++; - s2++; - } - - if (is_digit(*s1)) - return 1; - if (is_digit(*s2)) - return -1; - - if (first != 0) - return first; - } - - return strcmp(os1, os2); -} - -/* Turn off core dumps but only if we're running outside of a container. */ -void disable_coredumps(void) { - int r; - - if (detect_container() > 0) - return; - - r = write_string_file("/proc/sys/kernel/core_pattern", "|/bin/false", WRITE_STRING_FILE_DISABLE_BUFFER); - if (r < 0) - log_debug_errno(r, "Failed to turn off coredumps, ignoring: %m"); -} -#endif /* NM_IGNORED */ diff --git a/shared/systemd/src/basic/util.h b/shared/systemd/src/basic/util.h deleted file mode 100644 index 942d773f..00000000 --- a/shared/systemd/src/basic/util.h +++ /dev/null @@ -1,68 +0,0 @@ -/* SPDX-License-Identifier: LGPL-2.1-or-later */ -#pragma once - -#include <stdint.h> - -#include "macro.h" - -extern int saved_argc; -extern char **saved_argv; - -static inline void save_argc_argv(int argc, char **argv) { - saved_argc = argc; - saved_argv = argv; -} - -bool kexec_loaded(void); - -int prot_from_flags(int flags) _const_; - -bool in_initrd(void); -void in_initrd_force(bool value); - -int on_ac_power(void); - -static inline unsigned u64log2(uint64_t n) { -#if __SIZEOF_LONG_LONG__ == 8 - return (n > 1) ? (unsigned) __builtin_clzll(n) ^ 63U : 0; -#else -#error "Wut?" -#endif -} - -static inline unsigned u32ctz(uint32_t n) { -#if __SIZEOF_INT__ == 4 - return n != 0 ? __builtin_ctz(n) : 32; -#else -#error "Wut?" -#endif -} - -static inline unsigned log2i(int x) { - assert(x > 0); - - return __SIZEOF_INT__ * 8 - __builtin_clz(x) - 1; -} - -static inline unsigned log2u(unsigned x) { - assert(x > 0); - - return sizeof(unsigned) * 8 - __builtin_clz(x) - 1; -} - -static inline unsigned log2u_round_up(unsigned x) { - assert(x > 0); - - if (x == 1) - return 0; - - return log2u(x - 1) + 1; -} - -int container_get_leader(const char *machine, pid_t *pid); - -int version(void); - -int str_verscmp(const char *s1, const char *s2); - -void disable_coredumps(void); diff --git a/shared/systemd/src/shared/dns-domain.c b/shared/systemd/src/shared/dns-domain.c deleted file mode 100644 index 95e4a93a..00000000 --- a/shared/systemd/src/shared/dns-domain.c +++ /dev/null @@ -1,1429 +0,0 @@ -/* SPDX-License-Identifier: LGPL-2.1-or-later */ - -#include "nm-sd-adapt-shared.h" - -#include <endian.h> -#include <netinet/in.h> -#include <stdio.h> -#include <sys/socket.h> - -#include "alloc-util.h" -#include "dns-domain.h" -#include "hashmap.h" -#include "hexdecoct.h" -#include "hostname-util.h" -#include "idn-util.h" -#include "in-addr-util.h" -#include "macro.h" -#include "parse-util.h" -#include "string-util.h" -#include "strv.h" -#include "utf8.h" - -int dns_label_unescape(const char **name, char *dest, size_t sz, DNSLabelFlags flags) { - const char *n; - char *d, last_char = 0; - int r = 0; - - assert(name); - assert(*name); - - n = *name; - d = dest; - - for (;;) { - if (IN_SET(*n, 0, '.')) { - if (FLAGS_SET(flags, DNS_LABEL_LDH) && last_char == '-') - /* Trailing dash */ - return -EINVAL; - - if (n[0] == '.' && (n[1] != 0 || !FLAGS_SET(flags, DNS_LABEL_LEAVE_TRAILING_DOT))) - n++; - - break; - } - - if (r >= DNS_LABEL_MAX) - return -EINVAL; - - if (sz <= 0) - return -ENOBUFS; - - if (*n == '\\') { - /* Escaped character */ - if (FLAGS_SET(flags, DNS_LABEL_NO_ESCAPES)) - return -EINVAL; - - n++; - - if (*n == 0) - /* Ending NUL */ - return -EINVAL; - - else if (IN_SET(*n, '\\', '.')) { - /* Escaped backslash or dot */ - - if (FLAGS_SET(flags, DNS_LABEL_LDH)) - return -EINVAL; - - last_char = *n; - if (d) - *(d++) = *n; - sz--; - r++; - n++; - - } else if (n[0] >= '0' && n[0] <= '9') { - unsigned k; - - /* Escaped literal ASCII character */ - - if (!(n[1] >= '0' && n[1] <= '9') || - !(n[2] >= '0' && n[2] <= '9')) - return -EINVAL; - - k = ((unsigned) (n[0] - '0') * 100) + - ((unsigned) (n[1] - '0') * 10) + - ((unsigned) (n[2] - '0')); - - /* Don't allow anything that doesn't - * fit in 8bit. Note that we do allow - * control characters, as some servers - * (e.g. cloudflare) are happy to - * generate labels with them - * inside. */ - if (k > 255) - return -EINVAL; - - if (FLAGS_SET(flags, DNS_LABEL_LDH) && - !valid_ldh_char((char) k)) - return -EINVAL; - - last_char = (char) k; - if (d) - *(d++) = (char) k; - sz--; - r++; - - n += 3; - } else - return -EINVAL; - - } else if ((uint8_t) *n >= (uint8_t) ' ' && *n != 127) { - - /* Normal character */ - - if (FLAGS_SET(flags, DNS_LABEL_LDH)) { - if (!valid_ldh_char(*n)) - return -EINVAL; - if (r == 0 && *n == '-') - /* Leading dash */ - return -EINVAL; - } - - last_char = *n; - if (d) - *(d++) = *n; - sz--; - r++; - n++; - } else - return -EINVAL; - } - - /* Empty label that is not at the end? */ - if (r == 0 && *n) - return -EINVAL; - - /* More than one trailing dot? */ - if (n[0] == '.' && !FLAGS_SET(flags, DNS_LABEL_LEAVE_TRAILING_DOT)) - return -EINVAL; - - if (sz >= 1 && d) - *d = 0; - - *name = n; - return r; -} - -#if 0 /* NM_IGNORED */ -/* @label_terminal: terminal character of a label, updated to point to the terminal character of - * the previous label (always skipping one dot) or to NULL if there are no more - * labels. */ -int dns_label_unescape_suffix(const char *name, const char **label_terminal, char *dest, size_t sz) { - const char *terminal; - int r; - - assert(name); - assert(label_terminal); - assert(dest); - - /* no more labels */ - if (!*label_terminal) { - if (sz >= 1) - *dest = 0; - - return 0; - } - - terminal = *label_terminal; - assert(IN_SET(*terminal, 0, '.')); - - /* Skip current terminal character (and accept domain names ending it ".") */ - if (*terminal == 0) - terminal--; - if (terminal >= name && *terminal == '.') - terminal--; - - /* Point name to the last label, and terminal to the preceding terminal symbol (or make it a NULL pointer) */ - for (;;) { - if (terminal < name) { - /* Reached the first label, so indicate that there are no more */ - terminal = NULL; - break; - } - - /* Find the start of the last label */ - if (*terminal == '.') { - const char *y; - unsigned slashes = 0; - - for (y = terminal - 1; y >= name && *y == '\\'; y--) - slashes++; - - if (slashes % 2 == 0) { - /* The '.' was not escaped */ - name = terminal + 1; - break; - } else { - terminal = y; - continue; - } - } - - terminal--; - } - - r = dns_label_unescape(&name, dest, sz, 0); - if (r < 0) - return r; - - *label_terminal = terminal; - - return r; -} -#endif /* NM_IGNORED */ - -int dns_label_escape(const char *p, size_t l, char *dest, size_t sz) { - char *q; - - /* DNS labels must be between 1 and 63 characters long. A - * zero-length label does not exist. See RFC 2182, Section - * 11. */ - - if (l <= 0 || l > DNS_LABEL_MAX) - return -EINVAL; - if (sz < 1) - return -ENOBUFS; - - assert(p); - assert(dest); - - q = dest; - while (l > 0) { - - if (IN_SET(*p, '.', '\\')) { - - /* Dot or backslash */ - - if (sz < 3) - return -ENOBUFS; - - *(q++) = '\\'; - *(q++) = *p; - - sz -= 2; - - } else if (IN_SET(*p, '_', '-') || - (*p >= '0' && *p <= '9') || - (*p >= 'a' && *p <= 'z') || - (*p >= 'A' && *p <= 'Z')) { - - /* Proper character */ - - if (sz < 2) - return -ENOBUFS; - - *(q++) = *p; - sz -= 1; - - } else { - - /* Everything else */ - - if (sz < 5) - return -ENOBUFS; - - *(q++) = '\\'; - *(q++) = '0' + (char) ((uint8_t) *p / 100); - *(q++) = '0' + (char) (((uint8_t) *p / 10) % 10); - *(q++) = '0' + (char) ((uint8_t) *p % 10); - - sz -= 4; - } - - p++; - l--; - } - - *q = 0; - return (int) (q - dest); -} - -#if 0 /* NM_IGNORED */ -int dns_label_escape_new(const char *p, size_t l, char **ret) { - _cleanup_free_ char *s = NULL; - int r; - - assert(p); - assert(ret); - - if (l <= 0 || l > DNS_LABEL_MAX) - return -EINVAL; - - s = new(char, DNS_LABEL_ESCAPED_MAX); - if (!s) - return -ENOMEM; - - r = dns_label_escape(p, l, s, DNS_LABEL_ESCAPED_MAX); - if (r < 0) - return r; - - *ret = TAKE_PTR(s); - - return r; -} - -#if HAVE_LIBIDN -int dns_label_apply_idna(const char *encoded, size_t encoded_size, char *decoded, size_t decoded_max) { - _cleanup_free_ uint32_t *input = NULL; - size_t input_size, l; - const char *p; - bool contains_8bit = false; - char buffer[DNS_LABEL_MAX+1]; - int r; - - assert(encoded); - assert(decoded); - - /* Converts an U-label into an A-label */ - - r = dlopen_idn(); - if (r < 0) - return r; - - if (encoded_size <= 0) - return -EINVAL; - - for (p = encoded; p < encoded + encoded_size; p++) - if ((uint8_t) *p > 127) - contains_8bit = true; - - if (!contains_8bit) { - if (encoded_size > DNS_LABEL_MAX) - return -EINVAL; - - return 0; - } - - input = sym_stringprep_utf8_to_ucs4(encoded, encoded_size, &input_size); - if (!input) - return -ENOMEM; - - if (sym_idna_to_ascii_4i(input, input_size, buffer, 0) != 0) - return -EINVAL; - - l = strlen(buffer); - - /* Verify that the result is not longer than one DNS label. */ - if (l <= 0 || l > DNS_LABEL_MAX) - return -EINVAL; - if (l > decoded_max) - return -ENOBUFS; - - memcpy(decoded, buffer, l); - - /* If there's room, append a trailing NUL byte, but only then */ - if (decoded_max > l) - decoded[l] = 0; - - return (int) l; -} - -int dns_label_undo_idna(const char *encoded, size_t encoded_size, char *decoded, size_t decoded_max) { - size_t input_size, output_size; - _cleanup_free_ uint32_t *input = NULL; - _cleanup_free_ char *result = NULL; - uint32_t *output = NULL; - size_t w; - int r; - - /* To be invoked after unescaping. Converts an A-label into an U-label. */ - - assert(encoded); - assert(decoded); - - r = dlopen_idn(); - if (r < 0) - return r; - - if (encoded_size <= 0 || encoded_size > DNS_LABEL_MAX) - return -EINVAL; - - if (!memory_startswith(encoded, encoded_size, IDNA_ACE_PREFIX)) - return 0; - - input = sym_stringprep_utf8_to_ucs4(encoded, encoded_size, &input_size); - if (!input) - return -ENOMEM; - - output_size = input_size; - output = newa(uint32_t, output_size); - - sym_idna_to_unicode_44i(input, input_size, output, &output_size, 0); - - result = sym_stringprep_ucs4_to_utf8(output, output_size, NULL, &w); - if (!result) - return -ENOMEM; - if (w <= 0) - return -EINVAL; - if (w > decoded_max) - return -ENOBUFS; - - memcpy(decoded, result, w); - - /* Append trailing NUL byte if there's space, but only then. */ - if (decoded_max > w) - decoded[w] = 0; - - return w; -} -#endif -#endif /* NM_IGNORED */ - -int dns_name_concat(const char *a, const char *b, DNSLabelFlags flags, char **_ret) { - _cleanup_free_ char *ret = NULL; - size_t n = 0, allocated = 0; - const char *p; - bool first = true; - int r; - - if (a) - p = a; - else if (b) - p = TAKE_PTR(b); - else - goto finish; - - for (;;) { - char label[DNS_LABEL_MAX]; - - r = dns_label_unescape(&p, label, sizeof label, flags); - if (r < 0) - return r; - if (r == 0) { - if (*p != 0) - return -EINVAL; - - if (b) { - /* Now continue with the second string, if there is one */ - p = TAKE_PTR(b); - continue; - } - - break; - } - - if (_ret) { - if (!GREEDY_REALLOC(ret, allocated, n + !first + DNS_LABEL_ESCAPED_MAX)) - return -ENOMEM; - - r = dns_label_escape(label, r, ret + n + !first, DNS_LABEL_ESCAPED_MAX); - if (r < 0) - return r; - - if (!first) - ret[n] = '.'; - } else { - char escaped[DNS_LABEL_ESCAPED_MAX]; - - r = dns_label_escape(label, r, escaped, sizeof(escaped)); - if (r < 0) - return r; - } - - if (!first) - n++; - else - first = false; - - n += r; - } - -finish: - if (n > DNS_HOSTNAME_MAX) - return -EINVAL; - - if (_ret) { - if (n == 0) { - /* Nothing appended? If so, generate at least a single dot, to indicate the DNS root domain */ - if (!GREEDY_REALLOC(ret, allocated, 2)) - return -ENOMEM; - - ret[n++] = '.'; - } else { - if (!GREEDY_REALLOC(ret, allocated, n + 1)) - return -ENOMEM; - } - - ret[n] = 0; - *_ret = TAKE_PTR(ret); - } - - return 0; -} - -#if 0 /* NM_IGNORED */ -void dns_name_hash_func(const char *p, struct siphash *state) { - int r; - - assert(p); - - for (;;) { - char label[DNS_LABEL_MAX+1]; - - r = dns_label_unescape(&p, label, sizeof label, 0); - if (r < 0) - break; - if (r == 0) - break; - - ascii_strlower_n(label, r); - siphash24_compress(label, r, state); - siphash24_compress_byte(0, state); /* make sure foobar and foo.bar result in different hashes */ - } - - /* enforce that all names are terminated by the empty label */ - string_hash_func("", state); -} - -int dns_name_compare_func(const char *a, const char *b) { - const char *x, *y; - int r, q; - - assert(a); - assert(b); - - x = a + strlen(a); - y = b + strlen(b); - - for (;;) { - char la[DNS_LABEL_MAX], lb[DNS_LABEL_MAX]; - - if (x == NULL && y == NULL) - return 0; - - r = dns_label_unescape_suffix(a, &x, la, sizeof(la)); - q = dns_label_unescape_suffix(b, &y, lb, sizeof(lb)); - if (r < 0 || q < 0) - return CMP(r, q); - - r = ascii_strcasecmp_nn(la, r, lb, q); - if (r != 0) - return r; - } -} - -DEFINE_HASH_OPS(dns_name_hash_ops, char, dns_name_hash_func, dns_name_compare_func); - -int dns_name_equal(const char *x, const char *y) { - int r, q; - - assert(x); - assert(y); - - for (;;) { - char la[DNS_LABEL_MAX], lb[DNS_LABEL_MAX]; - - r = dns_label_unescape(&x, la, sizeof la, 0); - if (r < 0) - return r; - - q = dns_label_unescape(&y, lb, sizeof lb, 0); - if (q < 0) - return q; - - if (r != q) - return false; - if (r == 0) - return true; - - if (ascii_strcasecmp_n(la, lb, r) != 0) - return false; - } -} - -int dns_name_endswith(const char *name, const char *suffix) { - const char *n, *s, *saved_n = NULL; - int r, q; - - assert(name); - assert(suffix); - - n = name; - s = suffix; - - for (;;) { - char ln[DNS_LABEL_MAX], ls[DNS_LABEL_MAX]; - - r = dns_label_unescape(&n, ln, sizeof ln, 0); - if (r < 0) - return r; - - if (!saved_n) - saved_n = n; - - q = dns_label_unescape(&s, ls, sizeof ls, 0); - if (q < 0) - return q; - - if (r == 0 && q == 0) - return true; - if (r == 0 && saved_n == n) - return false; - - if (r != q || ascii_strcasecmp_n(ln, ls, r) != 0) { - - /* Not the same, let's jump back, and try with the next label again */ - s = suffix; - n = TAKE_PTR(saved_n); - } - } -} - -int dns_name_startswith(const char *name, const char *prefix) { - const char *n, *p; - int r, q; - - assert(name); - assert(prefix); - - n = name; - p = prefix; - - for (;;) { - char ln[DNS_LABEL_MAX], lp[DNS_LABEL_MAX]; - - r = dns_label_unescape(&p, lp, sizeof lp, 0); - if (r < 0) - return r; - if (r == 0) - return true; - - q = dns_label_unescape(&n, ln, sizeof ln, 0); - if (q < 0) - return q; - - if (r != q) - return false; - if (ascii_strcasecmp_n(ln, lp, r) != 0) - return false; - } -} - -int dns_name_change_suffix(const char *name, const char *old_suffix, const char *new_suffix, char **ret) { - const char *n, *s, *saved_before = NULL, *saved_after = NULL, *prefix; - int r, q; - - assert(name); - assert(old_suffix); - assert(new_suffix); - assert(ret); - - n = name; - s = old_suffix; - - for (;;) { - char ln[DNS_LABEL_MAX], ls[DNS_LABEL_MAX]; - - if (!saved_before) - saved_before = n; - - r = dns_label_unescape(&n, ln, sizeof ln, 0); - if (r < 0) - return r; - - if (!saved_after) - saved_after = n; - - q = dns_label_unescape(&s, ls, sizeof ls, 0); - if (q < 0) - return q; - - if (r == 0 && q == 0) - break; - if (r == 0 && saved_after == n) { - *ret = NULL; /* doesn't match */ - return 0; - } - - if (r != q || ascii_strcasecmp_n(ln, ls, r) != 0) { - - /* Not the same, let's jump back, and try with the next label again */ - s = old_suffix; - n = TAKE_PTR(saved_after); - saved_before = NULL; - } - } - - /* Found it! Now generate the new name */ - prefix = strndupa(name, saved_before - name); - - r = dns_name_concat(prefix, new_suffix, 0, ret); - if (r < 0) - return r; - - return 1; -} - -int dns_name_between(const char *a, const char *b, const char *c) { - /* Determine if b is strictly greater than a and strictly smaller than c. - We consider the order of names to be circular, so that if a is - strictly greater than c, we consider b to be between them if it is - either greater than a or smaller than c. This is how the canonical - DNS name order used in NSEC records work. */ - - if (dns_name_compare_func(a, c) < 0) - /* - a and c are properly ordered: - a<---b--->c - */ - return dns_name_compare_func(a, b) < 0 && - dns_name_compare_func(b, c) < 0; - else - /* - a and c are equal or 'reversed': - <--b--c a-----> - or: - <-----c a--b--> - */ - return dns_name_compare_func(b, c) < 0 || - dns_name_compare_func(a, b) < 0; -} - -int dns_name_reverse(int family, const union in_addr_union *a, char **ret) { - const uint8_t *p; - int r; - - assert(a); - assert(ret); - - p = (const uint8_t*) a; - - if (family == AF_INET) - r = asprintf(ret, "%u.%u.%u.%u.in-addr.arpa", p[3], p[2], p[1], p[0]); - else if (family == AF_INET6) - r = asprintf(ret, "%c.%c.%c.%c.%c.%c.%c.%c.%c.%c.%c.%c.%c.%c.%c.%c.%c.%c.%c.%c.%c.%c.%c.%c.%c.%c.%c.%c.%c.%c.%c.%c.ip6.arpa", - hexchar(p[15] & 0xF), hexchar(p[15] >> 4), hexchar(p[14] & 0xF), hexchar(p[14] >> 4), - hexchar(p[13] & 0xF), hexchar(p[13] >> 4), hexchar(p[12] & 0xF), hexchar(p[12] >> 4), - hexchar(p[11] & 0xF), hexchar(p[11] >> 4), hexchar(p[10] & 0xF), hexchar(p[10] >> 4), - hexchar(p[ 9] & 0xF), hexchar(p[ 9] >> 4), hexchar(p[ 8] & 0xF), hexchar(p[ 8] >> 4), - hexchar(p[ 7] & 0xF), hexchar(p[ 7] >> 4), hexchar(p[ 6] & 0xF), hexchar(p[ 6] >> 4), - hexchar(p[ 5] & 0xF), hexchar(p[ 5] >> 4), hexchar(p[ 4] & 0xF), hexchar(p[ 4] >> 4), - hexchar(p[ 3] & 0xF), hexchar(p[ 3] >> 4), hexchar(p[ 2] & 0xF), hexchar(p[ 2] >> 4), - hexchar(p[ 1] & 0xF), hexchar(p[ 1] >> 4), hexchar(p[ 0] & 0xF), hexchar(p[ 0] >> 4)); - else - return -EAFNOSUPPORT; - if (r < 0) - return -ENOMEM; - - return 0; -} - -int dns_name_address(const char *p, int *ret_family, union in_addr_union *ret_address) { - int r; - - assert(p); - assert(ret_family); - assert(ret_address); - - r = dns_name_endswith(p, "in-addr.arpa"); - if (r < 0) - return r; - if (r > 0) { - uint8_t a[4]; - unsigned i; - - for (i = 0; i < ELEMENTSOF(a); i++) { - char label[DNS_LABEL_MAX+1]; - - r = dns_label_unescape(&p, label, sizeof label, 0); - if (r < 0) - return r; - if (r == 0) - return -EINVAL; - if (r > 3) - return -EINVAL; - - r = safe_atou8(label, &a[i]); - if (r < 0) - return r; - } - - r = dns_name_equal(p, "in-addr.arpa"); - if (r <= 0) - return r; - - *ret_family = AF_INET; - ret_address->in.s_addr = htobe32(((uint32_t) a[3] << 24) | - ((uint32_t) a[2] << 16) | - ((uint32_t) a[1] << 8) | - (uint32_t) a[0]); - - return 1; - } - - r = dns_name_endswith(p, "ip6.arpa"); - if (r < 0) - return r; - if (r > 0) { - struct in6_addr a; - unsigned i; - - for (i = 0; i < ELEMENTSOF(a.s6_addr); i++) { - char label[DNS_LABEL_MAX+1]; - int x, y; - - r = dns_label_unescape(&p, label, sizeof label, 0); - if (r <= 0) - return r; - if (r != 1) - return -EINVAL; - x = unhexchar(label[0]); - if (x < 0) - return -EINVAL; - - r = dns_label_unescape(&p, label, sizeof label, 0); - if (r <= 0) - return r; - if (r != 1) - return -EINVAL; - y = unhexchar(label[0]); - if (y < 0) - return -EINVAL; - - a.s6_addr[ELEMENTSOF(a.s6_addr) - i - 1] = (uint8_t) y << 4 | (uint8_t) x; - } - - r = dns_name_equal(p, "ip6.arpa"); - if (r <= 0) - return r; - - *ret_family = AF_INET6; - ret_address->in6 = a; - return 1; - } - - *ret_family = AF_UNSPEC; - *ret_address = IN_ADDR_NULL; - - return 0; -} -#endif /* NM_IGNORED */ - -bool dns_name_is_root(const char *name) { - - assert(name); - - /* There are exactly two ways to encode the root domain name: - * as empty string, or with a single dot. */ - - return STR_IN_SET(name, "", "."); -} - -bool dns_name_is_single_label(const char *name) { - int r; - - assert(name); - - r = dns_name_parent(&name); - if (r <= 0) - return false; - - return dns_name_is_root(name); -} - -/* Encode a domain name according to RFC 1035 Section 3.1, without compression */ -int dns_name_to_wire_format(const char *domain, uint8_t *buffer, size_t len, bool canonical) { - uint8_t *label_length, *out; - int r; - - assert(domain); - assert(buffer); - - out = buffer; - - do { - /* Reserve a byte for label length */ - if (len <= 0) - return -ENOBUFS; - len--; - label_length = out; - out++; - - /* Convert and copy a single label. Note that - * dns_label_unescape() returns 0 when it hits the end - * of the domain name, which we rely on here to encode - * the trailing NUL byte. */ - r = dns_label_unescape(&domain, (char *) out, len, 0); - if (r < 0) - return r; - - /* Optionally, output the name in DNSSEC canonical - * format, as described in RFC 4034, section 6.2. Or - * in other words: in lower-case. */ - if (canonical) - ascii_strlower_n((char*) out, (size_t) r); - - /* Fill label length, move forward */ - *label_length = r; - out += r; - len -= r; - - } while (r != 0); - - /* Verify the maximum size of the encoded name. The trailing - * dot + NUL byte account are included this time, hence - * compare against DNS_HOSTNAME_MAX + 2 (which is 255) this - * time. */ - if (out - buffer > DNS_HOSTNAME_MAX + 2) - return -EINVAL; - - return out - buffer; -} - -#if 0 /* NM_IGNORED */ -static bool srv_type_label_is_valid(const char *label, size_t n) { - size_t k; - - assert(label); - - if (n < 2) /* Label needs to be at least 2 chars long */ - return false; - - if (label[0] != '_') /* First label char needs to be underscore */ - return false; - - /* Second char must be a letter */ - if (!(label[1] >= 'A' && label[1] <= 'Z') && - !(label[1] >= 'a' && label[1] <= 'z')) - return false; - - /* Third and further chars must be alphanumeric or a hyphen */ - for (k = 2; k < n; k++) { - if (!(label[k] >= 'A' && label[k] <= 'Z') && - !(label[k] >= 'a' && label[k] <= 'z') && - !(label[k] >= '0' && label[k] <= '9') && - label[k] != '-') - return false; - } - - return true; -} - -bool dns_srv_type_is_valid(const char *name) { - unsigned c = 0; - int r; - - if (!name) - return false; - - for (;;) { - char label[DNS_LABEL_MAX]; - - /* This more or less implements RFC 6335, Section 5.1 */ - - r = dns_label_unescape(&name, label, sizeof label, 0); - if (r < 0) - return false; - if (r == 0) - break; - - if (c >= 2) - return false; - - if (!srv_type_label_is_valid(label, r)) - return false; - - c++; - } - - return c == 2; /* exactly two labels */ -} - -bool dnssd_srv_type_is_valid(const char *name) { - return dns_srv_type_is_valid(name) && - ((dns_name_endswith(name, "_tcp") > 0) || - (dns_name_endswith(name, "_udp") > 0)); /* Specific to DNS-SD. RFC 6763, Section 7 */ -} - -bool dns_service_name_is_valid(const char *name) { - size_t l; - - /* This more or less implements RFC 6763, Section 4.1.1 */ - - if (!name) - return false; - - if (!utf8_is_valid(name)) - return false; - - if (string_has_cc(name, NULL)) - return false; - - l = strlen(name); - if (l <= 0) - return false; - if (l > 63) - return false; - - return true; -} - -int dns_service_join(const char *name, const char *type, const char *domain, char **ret) { - char escaped[DNS_LABEL_ESCAPED_MAX]; - _cleanup_free_ char *n = NULL; - int r; - - assert(type); - assert(domain); - assert(ret); - - if (!dns_srv_type_is_valid(type)) - return -EINVAL; - - if (!name) - return dns_name_concat(type, domain, 0, ret); - - if (!dns_service_name_is_valid(name)) - return -EINVAL; - - r = dns_label_escape(name, strlen(name), escaped, sizeof(escaped)); - if (r < 0) - return r; - - r = dns_name_concat(type, domain, 0, &n); - if (r < 0) - return r; - - return dns_name_concat(escaped, n, 0, ret); -} - -static bool dns_service_name_label_is_valid(const char *label, size_t n) { - char *s; - - assert(label); - - if (memchr(label, 0, n)) - return false; - - s = strndupa(label, n); - return dns_service_name_is_valid(s); -} - -int dns_service_split(const char *joined, char **_name, char **_type, char **_domain) { - _cleanup_free_ char *name = NULL, *type = NULL, *domain = NULL; - const char *p = joined, *q = NULL, *d = NULL; - char a[DNS_LABEL_MAX], b[DNS_LABEL_MAX], c[DNS_LABEL_MAX]; - int an, bn, cn, r; - unsigned x = 0; - - assert(joined); - - /* Get first label from the full name */ - an = dns_label_unescape(&p, a, sizeof(a), 0); - if (an < 0) - return an; - - if (an > 0) { - x++; - - /* If there was a first label, try to get the second one */ - bn = dns_label_unescape(&p, b, sizeof(b), 0); - if (bn < 0) - return bn; - - if (bn > 0) { - x++; - - /* If there was a second label, try to get the third one */ - q = p; - cn = dns_label_unescape(&p, c, sizeof(c), 0); - if (cn < 0) - return cn; - - if (cn > 0) - x++; - } else - cn = 0; - } else - an = 0; - - if (x >= 2 && srv_type_label_is_valid(b, bn)) { - - if (x >= 3 && srv_type_label_is_valid(c, cn)) { - - if (dns_service_name_label_is_valid(a, an)) { - /* OK, got <name> . <type> . <type2> . <domain> */ - - name = strndup(a, an); - if (!name) - return -ENOMEM; - - type = strjoin(b, ".", c); - if (!type) - return -ENOMEM; - - d = p; - goto finish; - } - - } else if (srv_type_label_is_valid(a, an)) { - - /* OK, got <type> . <type2> . <domain> */ - - name = NULL; - - type = strjoin(a, ".", b); - if (!type) - return -ENOMEM; - - d = q; - goto finish; - } - } - - name = NULL; - type = NULL; - d = joined; - -finish: - r = dns_name_normalize(d, 0, &domain); - if (r < 0) - return r; - - if (_domain) - *_domain = TAKE_PTR(domain); - - if (_type) - *_type = TAKE_PTR(type); - - if (_name) - *_name = TAKE_PTR(name); - - return 0; -} - -static int dns_name_build_suffix_table(const char *name, const char *table[]) { - const char *p; - unsigned n = 0; - int r; - - assert(name); - assert(table); - - p = name; - for (;;) { - if (n > DNS_N_LABELS_MAX) - return -EINVAL; - - table[n] = p; - r = dns_name_parent(&p); - if (r < 0) - return r; - if (r == 0) - break; - - n++; - } - - return (int) n; -} - -int dns_name_suffix(const char *name, unsigned n_labels, const char **ret) { - const char* labels[DNS_N_LABELS_MAX+1]; - int n; - - assert(name); - assert(ret); - - n = dns_name_build_suffix_table(name, labels); - if (n < 0) - return n; - - if ((unsigned) n < n_labels) - return -EINVAL; - - *ret = labels[n - n_labels]; - return (int) (n - n_labels); -} - -int dns_name_skip(const char *a, unsigned n_labels, const char **ret) { - int r; - - assert(a); - assert(ret); - - for (; n_labels > 0; n_labels--) { - r = dns_name_parent(&a); - if (r < 0) - return r; - if (r == 0) { - *ret = ""; - return 0; - } - } - - *ret = a; - return 1; -} - -int dns_name_count_labels(const char *name) { - unsigned n = 0; - const char *p; - int r; - - assert(name); - - p = name; - for (;;) { - r = dns_name_parent(&p); - if (r < 0) - return r; - if (r == 0) - break; - - if (n >= DNS_N_LABELS_MAX) - return -EINVAL; - - n++; - } - - return (int) n; -} - -int dns_name_equal_skip(const char *a, unsigned n_labels, const char *b) { - int r; - - assert(a); - assert(b); - - r = dns_name_skip(a, n_labels, &a); - if (r <= 0) - return r; - - return dns_name_equal(a, b); -} - -int dns_name_common_suffix(const char *a, const char *b, const char **ret) { - const char *a_labels[DNS_N_LABELS_MAX+1], *b_labels[DNS_N_LABELS_MAX+1]; - int n = 0, m = 0, k = 0, r, q; - - assert(a); - assert(b); - assert(ret); - - /* Determines the common suffix of domain names a and b */ - - n = dns_name_build_suffix_table(a, a_labels); - if (n < 0) - return n; - - m = dns_name_build_suffix_table(b, b_labels); - if (m < 0) - return m; - - for (;;) { - char la[DNS_LABEL_MAX], lb[DNS_LABEL_MAX]; - const char *x, *y; - - if (k >= n || k >= m) { - *ret = a_labels[n - k]; - return 0; - } - - x = a_labels[n - 1 - k]; - r = dns_label_unescape(&x, la, sizeof la, 0); - if (r < 0) - return r; - - y = b_labels[m - 1 - k]; - q = dns_label_unescape(&y, lb, sizeof lb, 0); - if (q < 0) - return q; - - if (r != q || ascii_strcasecmp_n(la, lb, r) != 0) { - *ret = a_labels[n - k]; - return 0; - } - - k++; - } -} - -int dns_name_apply_idna(const char *name, char **ret) { - - /* Return negative on error, 0 if not implemented, positive on success. */ - -#if HAVE_LIBIDN2 || HAVE_LIBIDN2 - int r; - - r = dlopen_idn(); - if (r == -EOPNOTSUPP) { - *ret = NULL; - return 0; - } - if (r < 0) - return r; -#endif - -#if HAVE_LIBIDN2 - _cleanup_free_ char *t = NULL; - - assert(name); - assert(ret); - - /* First, try non-transitional mode (i.e. IDN2008 rules) */ - r = sym_idn2_lookup_u8((uint8_t*) name, (uint8_t**) &t, - IDN2_NFC_INPUT | IDN2_NONTRANSITIONAL); - if (r == IDN2_DISALLOWED) /* If that failed, because of disallowed characters, try transitional mode. - * (i.e. IDN2003 rules which supports some unicode chars IDN2008 doesn't allow). */ - r = sym_idn2_lookup_u8((uint8_t*) name, (uint8_t**) &t, - IDN2_NFC_INPUT | IDN2_TRANSITIONAL); - - log_debug("idn2_lookup_u8: %s → %s", name, t); - if (r == IDN2_OK) { - if (!startswith(name, "xn--")) { - _cleanup_free_ char *s = NULL; - - r = sym_idn2_to_unicode_8z8z(t, &s, 0); - if (r != IDN2_OK) { - log_debug("idn2_to_unicode_8z8z(\"%s\") failed: %d/%s", - t, r, sym_idn2_strerror(r)); - *ret = NULL; - return 0; - } - - if (!streq_ptr(name, s)) { - log_debug("idn2 roundtrip failed: \"%s\" → \"%s\" → \"%s\", ignoring.", - name, t, s); - *ret = NULL; - return 0; - } - } - - *ret = TAKE_PTR(t); - return 1; /* *ret has been written */ - } - - log_debug("idn2_lookup_u8(\"%s\") failed: %d/%s", name, r, sym_idn2_strerror(r)); - if (r == IDN2_2HYPHEN) - /* The name has two hyphens — forbidden by IDNA2008 in some cases */ - return 0; - if (IN_SET(r, IDN2_TOO_BIG_DOMAIN, IDN2_TOO_BIG_LABEL)) - return -ENOSPC; - - return -EINVAL; -#elif HAVE_LIBIDN - _cleanup_free_ char *buf = NULL; - size_t n = 0, allocated = 0; - bool first = true; - int r, q; - - assert(name); - assert(ret); - - for (;;) { - char label[DNS_LABEL_MAX]; - - r = dns_label_unescape(&name, label, sizeof label, 0); - if (r < 0) - return r; - if (r == 0) - break; - - q = dns_label_apply_idna(label, r, label, sizeof label); - if (q < 0) - return q; - if (q > 0) - r = q; - - if (!GREEDY_REALLOC(buf, allocated, n + !first + DNS_LABEL_ESCAPED_MAX)) - return -ENOMEM; - - r = dns_label_escape(label, r, buf + n + !first, DNS_LABEL_ESCAPED_MAX); - if (r < 0) - return r; - - if (first) - first = false; - else - buf[n++] = '.'; - - n += r; - } - - if (n > DNS_HOSTNAME_MAX) - return -EINVAL; - - if (!GREEDY_REALLOC(buf, allocated, n + 1)) - return -ENOMEM; - - buf[n] = 0; - *ret = TAKE_PTR(buf); - - return 1; -#else - *ret = NULL; - return 0; -#endif -} - -int dns_name_is_valid_or_address(const char *name) { - /* Returns > 0 if the specified name is either a valid IP address formatted as string or a valid DNS name */ - - if (isempty(name)) - return 0; - - if (in_addr_from_string_auto(name, NULL, NULL) >= 0) - return 1; - - return dns_name_is_valid(name); -} - -int dns_name_dot_suffixed(const char *name) { - const char *p = name; - int r; - - for (;;) { - if (streq(p, ".")) - return true; - - r = dns_label_unescape(&p, NULL, DNS_LABEL_MAX, DNS_LABEL_LEAVE_TRAILING_DOT); - if (r < 0) - return r; - if (r == 0) - return false; - } -} -#endif /* NM_IGNORED */ diff --git a/shared/systemd/src/shared/dns-domain.h b/shared/systemd/src/shared/dns-domain.h deleted file mode 100644 index 984f4840..00000000 --- a/shared/systemd/src/shared/dns-domain.h +++ /dev/null @@ -1,117 +0,0 @@ -/* SPDX-License-Identifier: LGPL-2.1-or-later */ -#pragma once - -#include <errno.h> -#include <stdbool.h> -#include <stddef.h> -#include <stdint.h> - -#include "hashmap.h" -#include "in-addr-util.h" - -/* Length of a single label, with all escaping removed, excluding any trailing dot or NUL byte */ -#define DNS_LABEL_MAX 63 - -/* Worst case length of a single label, with all escaping applied and room for a trailing NUL byte. */ -#define DNS_LABEL_ESCAPED_MAX (DNS_LABEL_MAX*4+1) - -/* Maximum length of a full hostname, consisting of a series of unescaped labels, and no trailing dot or NUL byte */ -#define DNS_HOSTNAME_MAX 253 - -/* Maximum length of a full hostname, on the wire, including the final NUL byte */ -#define DNS_WIRE_FORMAT_HOSTNAME_MAX 255 - -/* Maximum number of labels per valid hostname */ -#define DNS_N_LABELS_MAX 127 - -typedef enum DNSLabelFlags { - DNS_LABEL_LDH = 1 << 0, /* Follow the "LDH" rule — only letters, digits, and internal hyphens. */ - DNS_LABEL_NO_ESCAPES = 1 << 1, /* Do not treat backslashes specially */ - DNS_LABEL_LEAVE_TRAILING_DOT = 1 << 2, /* Leave trailing dot in place */ -} DNSLabelFlags; - -int dns_label_unescape(const char **name, char *dest, size_t sz, DNSLabelFlags flags); -int dns_label_unescape_suffix(const char *name, const char **label_end, char *dest, size_t sz); -int dns_label_escape(const char *p, size_t l, char *dest, size_t sz); -int dns_label_escape_new(const char *p, size_t l, char **ret); - -static inline int dns_name_parent(const char **name) { - return dns_label_unescape(name, NULL, DNS_LABEL_MAX, 0); -} - -#if 0 /* NM_IGNORED */ -#if HAVE_LIBIDN -int dns_label_apply_idna(const char *encoded, size_t encoded_size, char *decoded, size_t decoded_max); -int dns_label_undo_idna(const char *encoded, size_t encoded_size, char *decoded, size_t decoded_max); -#endif -#endif /* NM_IGNORED */ - -int dns_name_concat(const char *a, const char *b, DNSLabelFlags flags, char **ret); - -static inline int dns_name_normalize(const char *s, DNSLabelFlags flags, char **ret) { - /* dns_name_concat() normalizes as a side-effect */ - return dns_name_concat(s, NULL, flags, ret); -} - -static inline int dns_name_is_valid(const char *s) { - int r; - - /* dns_name_normalize() verifies as a side effect */ - r = dns_name_normalize(s, 0, NULL); - if (r == -EINVAL) - return 0; - if (r < 0) - return r; - return 1; -} - -static inline int dns_name_is_valid_ldh(const char *s) { - int r; - - r = dns_name_concat(s, NULL, DNS_LABEL_LDH|DNS_LABEL_NO_ESCAPES, NULL); - if (r == -EINVAL) - return 0; - if (r < 0) - return r; - return 1; -} - -void dns_name_hash_func(const char *s, struct siphash *state); -int dns_name_compare_func(const char *a, const char *b); -extern const struct hash_ops dns_name_hash_ops; - -int dns_name_between(const char *a, const char *b, const char *c); -int dns_name_equal(const char *x, const char *y); -int dns_name_endswith(const char *name, const char *suffix); -int dns_name_startswith(const char *name, const char *prefix); - -int dns_name_change_suffix(const char *name, const char *old_suffix, const char *new_suffix, char **ret); - -int dns_name_reverse(int family, const union in_addr_union *a, char **ret); -int dns_name_address(const char *p, int *family, union in_addr_union *a); - -bool dns_name_is_root(const char *name); -bool dns_name_is_single_label(const char *name); - -int dns_name_to_wire_format(const char *domain, uint8_t *buffer, size_t len, bool canonical); - -bool dns_srv_type_is_valid(const char *name); -bool dnssd_srv_type_is_valid(const char *name); -bool dns_service_name_is_valid(const char *name); - -int dns_service_join(const char *name, const char *type, const char *domain, char **ret); -int dns_service_split(const char *joined, char **name, char **type, char **domain); - -int dns_name_suffix(const char *name, unsigned n_labels, const char **ret); -int dns_name_count_labels(const char *name); - -int dns_name_skip(const char *a, unsigned n_labels, const char **ret); -int dns_name_equal_skip(const char *a, unsigned n_labels, const char *b); - -int dns_name_common_suffix(const char *a, const char *b, const char **ret); - -int dns_name_apply_idna(const char *name, char **ret); - -int dns_name_is_valid_or_address(const char *name); - -int dns_name_dot_suffixed(const char *name); diff --git a/shared/systemd/src/shared/log-link.h b/shared/systemd/src/shared/log-link.h deleted file mode 100644 index 3a4dcaa2..00000000 --- a/shared/systemd/src/shared/log-link.h +++ /dev/null @@ -1,45 +0,0 @@ -/* SPDX-License-Identifier: LGPL-2.1-or-later */ -#pragma once - -#include "log.h" - -#define log_interface_full_errno(ifname, level, error, ...) \ - ({ \ - const char *_ifname = (ifname); \ - _ifname ? log_object_internal(level, error, PROJECT_FILE, __LINE__, __func__, "INTERFACE=", _ifname, NULL, NULL, ##__VA_ARGS__) : \ - log_internal(level, error, PROJECT_FILE, __LINE__, __func__, ##__VA_ARGS__); \ - }) - -/* - * The following macros append INTERFACE= to the message. - * The macros require a struct named 'Link' which contains 'char *ifname': - * - * typedef struct Link { - * char *ifname; - * } Link; - * - * See, network/networkd-link.h for example. - */ - -#define log_link_full_errno(link, level, error, ...) \ - ({ \ - const Link *_l = (link); \ - log_interface_full_errno(_l ? _l->ifname : NULL, level, error, ##__VA_ARGS__); \ - }) - -#define log_link_full(link, level, ...) (void) log_link_full_errno(link, level, 0, __VA_ARGS__) - -#define log_link_debug(link, ...) log_link_full_errno(link, LOG_DEBUG, 0, __VA_ARGS__) -#define log_link_info(link, ...) log_link_full(link, LOG_INFO, __VA_ARGS__) -#define log_link_notice(link, ...) log_link_full(link, LOG_NOTICE, __VA_ARGS__) -#define log_link_warning(link, ...) log_link_full(link, LOG_WARNING, __VA_ARGS__) -#define log_link_error(link, ...) log_link_full(link, LOG_ERR, __VA_ARGS__) - -#define log_link_debug_errno(link, error, ...) log_link_full_errno(link, LOG_DEBUG, error, __VA_ARGS__) -#define log_link_info_errno(link, error, ...) log_link_full_errno(link, LOG_INFO, error, __VA_ARGS__) -#define log_link_notice_errno(link, error, ...) log_link_full_errno(link, LOG_NOTICE, error, __VA_ARGS__) -#define log_link_warning_errno(link, error, ...) log_link_full_errno(link, LOG_WARNING, error, __VA_ARGS__) -#define log_link_error_errno(link, error, ...) log_link_full_errno(link, LOG_ERR, error, __VA_ARGS__) - -#define LOG_LINK_MESSAGE(link, fmt, ...) "MESSAGE=%s: " fmt, (link)->ifname, ##__VA_ARGS__ -#define LOG_LINK_INTERFACE(link) "INTERFACE=%s", (link)->ifname diff --git a/shared/systemd/src/shared/web-util.c b/shared/systemd/src/shared/web-util.c deleted file mode 100644 index 35ba1a2e..00000000 --- a/shared/systemd/src/shared/web-util.c +++ /dev/null @@ -1,63 +0,0 @@ -/* SPDX-License-Identifier: LGPL-2.1-or-later */ - -#include "nm-sd-adapt-shared.h" - -#include <stdbool.h> - -#include "string-util.h" -#include "strv.h" -#include "utf8.h" -#include "web-util.h" - -#if 0 /* NM_IGNORED */ -bool http_etag_is_valid(const char *etag) { - if (isempty(etag)) - return false; - - if (!endswith(etag, "\"")) - return false; - - if (!STARTSWITH_SET(etag, "\"", "W/\"")) - return false; - - return true; -} -#endif /* NM_IGNORED */ - -/* NM: we use http_url_is_valid() for our own code, and it must not - * change behavior. If a re-import results in a merge-conflict, you must - * ensure that it does not change behavior, and possibly do something - * about that. */ -/**/ bool http_url_is_valid(const char *url) { -/**/ const char *p; -/**/ -/**/ if (isempty(url)) -/**/ return false; -/**/ -/**/ p = STARTSWITH_SET(url, "http://", "https://"); -/**/ if (!p) -/**/ return false; -/**/ -/**/ if (isempty(p)) -/**/ return false; -/**/ -/**/ return ascii_is_valid(p); -/**/ } - -#if 0 /* NM_IGNORED */ -bool documentation_url_is_valid(const char *url) { - const char *p; - - if (isempty(url)) - return false; - - if (http_url_is_valid(url)) - return true; - - p = STARTSWITH_SET(url, "file:/", "info:", "man:"); - if (isempty(p)) - return false; - - return ascii_is_valid(p); -} -#endif /* NM_IGNORED */ diff --git a/shared/systemd/src/shared/web-util.h b/shared/systemd/src/shared/web-util.h deleted file mode 100644 index ec54669f..00000000 --- a/shared/systemd/src/shared/web-util.h +++ /dev/null @@ -1,12 +0,0 @@ -/* SPDX-License-Identifier: LGPL-2.1-or-later */ -#pragma once - -#include <stdbool.h> - -#include "macro.h" - -bool http_url_is_valid(const char *url) _pure_; - -bool documentation_url_is_valid(const char *url) _pure_; - -bool http_etag_is_valid(const char *etag); |