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/systemd/src/basic | |
| parent | bfe522304da217296e2a61040f58e35ec5d6f3f2 (diff) | |
New upstream version 1.32.12 upstream/1.32.12
Diffstat (limited to 'shared/systemd/src/basic')
88 files changed, 0 insertions, 30290 deletions
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); |