summaryrefslogtreecommitdiff
path: root/src/libnm-systemd-shared
diff options
context:
space:
mode:
Diffstat (limited to 'src/libnm-systemd-shared')
-rw-r--r--src/libnm-systemd-shared/meson.build2
-rw-r--r--src/libnm-systemd-shared/sd-adapt-shared/sd-messages.h (renamed from src/libnm-systemd-shared/sd-adapt-shared/netif-util.h)0
-rw-r--r--src/libnm-systemd-shared/src/basic/alloc-util.c27
-rw-r--r--src/libnm-systemd-shared/src/basic/alloc-util.h53
-rw-r--r--src/libnm-systemd-shared/src/basic/arphrd-util.h10
-rw-r--r--src/libnm-systemd-shared/src/basic/async.h13
-rw-r--r--src/libnm-systemd-shared/src/basic/btrfs.c100
-rw-r--r--src/libnm-systemd-shared/src/basic/btrfs.h9
-rw-r--r--src/libnm-systemd-shared/src/basic/cgroup-util.h101
-rw-r--r--src/libnm-systemd-shared/src/basic/constants.h23
-rw-r--r--src/libnm-systemd-shared/src/basic/env-file.c39
-rw-r--r--src/libnm-systemd-shared/src/basic/env-file.h7
-rw-r--r--src/libnm-systemd-shared/src/basic/env-util.c367
-rw-r--r--src/libnm-systemd-shared/src/basic/env-util.h26
-rw-r--r--src/libnm-systemd-shared/src/basic/errno-util.h142
-rw-r--r--src/libnm-systemd-shared/src/basic/escape.c31
-rw-r--r--src/libnm-systemd-shared/src/basic/escape.h1
-rw-r--r--src/libnm-systemd-shared/src/basic/ether-addr-util.c14
-rw-r--r--src/libnm-systemd-shared/src/basic/ether-addr-util.h2
-rw-r--r--src/libnm-systemd-shared/src/basic/extract-word.c6
-rw-r--r--src/libnm-systemd-shared/src/basic/fd-util.c183
-rw-r--r--src/libnm-systemd-shared/src/basic/fd-util.h31
-rw-r--r--src/libnm-systemd-shared/src/basic/fileio.c272
-rw-r--r--src/libnm-systemd-shared/src/basic/fileio.h15
-rw-r--r--src/libnm-systemd-shared/src/basic/fs-util.c37
-rw-r--r--src/libnm-systemd-shared/src/basic/fs-util.h3
-rw-r--r--src/libnm-systemd-shared/src/basic/glyph-util.c14
-rw-r--r--src/libnm-systemd-shared/src/basic/glyph-util.h22
-rw-r--r--src/libnm-systemd-shared/src/basic/hash-funcs.c8
-rw-r--r--src/libnm-systemd-shared/src/basic/hash-funcs.h6
-rw-r--r--src/libnm-systemd-shared/src/basic/hashmap.c58
-rw-r--r--src/libnm-systemd-shared/src/basic/hashmap.h21
-rw-r--r--src/libnm-systemd-shared/src/basic/hexdecoct.c16
-rw-r--r--src/libnm-systemd-shared/src/basic/hexdecoct.h12
-rw-r--r--src/libnm-systemd-shared/src/basic/in-addr-util.c38
-rw-r--r--src/libnm-systemd-shared/src/basic/in-addr-util.h26
-rw-r--r--src/libnm-systemd-shared/src/basic/inotify-util.c37
-rw-r--r--src/libnm-systemd-shared/src/basic/inotify-util.h26
-rw-r--r--src/libnm-systemd-shared/src/basic/io-util.c158
-rw-r--r--src/libnm-systemd-shared/src/basic/io-util.h76
-rw-r--r--src/libnm-systemd-shared/src/basic/iovec-util.h99
-rw-r--r--src/libnm-systemd-shared/src/basic/list.h12
-rw-r--r--src/libnm-systemd-shared/src/basic/locale-util.c20
-rw-r--r--src/libnm-systemd-shared/src/basic/locale-util.h3
-rw-r--r--src/libnm-systemd-shared/src/basic/lock-util.h3
-rw-r--r--src/libnm-systemd-shared/src/basic/log.h3
-rw-r--r--src/libnm-systemd-shared/src/basic/macro.h56
-rw-r--r--src/libnm-systemd-shared/src/basic/memory-util.c16
-rw-r--r--src/libnm-systemd-shared/src/basic/memory-util.h51
-rw-r--r--src/libnm-systemd-shared/src/basic/missing_socket.h25
-rw-r--r--src/libnm-systemd-shared/src/basic/missing_stat.h2
-rw-r--r--src/libnm-systemd-shared/src/basic/missing_syscall.h33
-rw-r--r--src/libnm-systemd-shared/src/basic/namespace-util.h55
-rw-r--r--src/libnm-systemd-shared/src/basic/ordered-set.c5
-rw-r--r--src/libnm-systemd-shared/src/basic/parse-util.c74
-rw-r--r--src/libnm-systemd-shared/src/basic/parse-util.h11
-rw-r--r--src/libnm-systemd-shared/src/basic/path-util.c94
-rw-r--r--src/libnm-systemd-shared/src/basic/path-util.h55
-rw-r--r--src/libnm-systemd-shared/src/basic/pidref.h78
-rw-r--r--src/libnm-systemd-shared/src/basic/prioq.c2
-rw-r--r--src/libnm-systemd-shared/src/basic/process-util.c674
-rw-r--r--src/libnm-systemd-shared/src/basic/process-util.h91
-rw-r--r--src/libnm-systemd-shared/src/basic/random-util.c6
-rw-r--r--src/libnm-systemd-shared/src/basic/ratelimit.h2
-rw-r--r--src/libnm-systemd-shared/src/basic/signal-util.c58
-rw-r--r--src/libnm-systemd-shared/src/basic/siphash24.h9
-rw-r--r--src/libnm-systemd-shared/src/basic/socket-util.c255
-rw-r--r--src/libnm-systemd-shared/src/basic/socket-util.h43
-rw-r--r--src/libnm-systemd-shared/src/basic/sort-util.h7
-rw-r--r--src/libnm-systemd-shared/src/basic/stat-util.c68
-rw-r--r--src/libnm-systemd-shared/src/basic/stat-util.h9
-rw-r--r--src/libnm-systemd-shared/src/basic/string-util.c321
-rw-r--r--src/libnm-systemd-shared/src/basic/string-util.h55
-rw-r--r--src/libnm-systemd-shared/src/basic/strv.c137
-rw-r--r--src/libnm-systemd-shared/src/basic/strv.h30
-rw-r--r--src/libnm-systemd-shared/src/basic/time-util.c120
-rw-r--r--src/libnm-systemd-shared/src/basic/time-util.h21
-rw-r--r--src/libnm-systemd-shared/src/basic/tmpfile-util.h1
-rw-r--r--src/libnm-systemd-shared/src/basic/umask-util.h4
-rw-r--r--src/libnm-systemd-shared/src/basic/user-util.h23
-rw-r--r--src/libnm-systemd-shared/src/basic/utf8.c33
-rw-r--r--src/libnm-systemd-shared/src/basic/utf8.h2
-rw-r--r--src/libnm-systemd-shared/src/fundamental/macro-fundamental.h126
-rw-r--r--src/libnm-systemd-shared/src/fundamental/memory-util-fundamental.h42
-rw-r--r--src/libnm-systemd-shared/src/fundamental/sha256.c11
-rw-r--r--src/libnm-systemd-shared/src/fundamental/string-util-fundamental.c16
-rw-r--r--src/libnm-systemd-shared/src/fundamental/string-util-fundamental.h8
-rw-r--r--src/libnm-systemd-shared/src/shared/dns-domain.c43
88 files changed, 3784 insertions, 1160 deletions
diff --git a/src/libnm-systemd-shared/meson.build b/src/libnm-systemd-shared/meson.build
index b32bd7f6e0..3d3e42709f 100644
--- a/src/libnm-systemd-shared/meson.build
+++ b/src/libnm-systemd-shared/meson.build
@@ -5,6 +5,7 @@ libnm_systemd_shared = static_library(
sources: files(
'nm-sd-utils-shared.c',
'src/basic/alloc-util.c',
+ 'src/basic/btrfs.c',
'src/basic/env-file.c',
'src/basic/env-util.c',
'src/basic/escape.c',
@@ -58,6 +59,7 @@ libnm_systemd_shared = static_library(
top_inc,
src_inc,
],
+ c_args: libnm_systemd_common_cflags,
dependencies: glib_dep,
)
diff --git a/src/libnm-systemd-shared/sd-adapt-shared/netif-util.h b/src/libnm-systemd-shared/sd-adapt-shared/sd-messages.h
index 637892c2d6..637892c2d6 100644
--- a/src/libnm-systemd-shared/sd-adapt-shared/netif-util.h
+++ b/src/libnm-systemd-shared/sd-adapt-shared/sd-messages.h
diff --git a/src/libnm-systemd-shared/src/basic/alloc-util.c b/src/libnm-systemd-shared/src/basic/alloc-util.c
index c07ab58942..243ff521a7 100644
--- a/src/libnm-systemd-shared/src/basic/alloc-util.c
+++ b/src/libnm-systemd-shared/src/basic/alloc-util.c
@@ -105,6 +105,33 @@ void* greedy_realloc0(
return q;
}
+void* greedy_realloc_append(
+ void **p,
+ size_t *n_p,
+ const void *from,
+ size_t n_from,
+ size_t size) {
+
+ uint8_t *q;
+
+ assert(p);
+ assert(n_p);
+ assert(from || n_from == 0);
+
+ if (n_from > SIZE_MAX - *n_p)
+ return NULL;
+
+ q = greedy_realloc(p, *n_p + n_from, size);
+ if (!q)
+ return NULL;
+
+ memcpy_safe(q + *n_p * size, from, n_from * size);
+
+ *n_p += n_from;
+
+ return q;
+}
+
void *expand_to_usable(void *ptr, size_t newsize _unused_) {
return ptr;
}
diff --git a/src/libnm-systemd-shared/src/basic/alloc-util.h b/src/libnm-systemd-shared/src/basic/alloc-util.h
index 9a62381df1..c215c33f4b 100644
--- a/src/libnm-systemd-shared/src/basic/alloc-util.h
+++ b/src/libnm-systemd-shared/src/basic/alloc-util.h
@@ -15,13 +15,12 @@
typedef void (*free_func_t)(void *p);
typedef void* (*mfree_func_t)(void *p);
-typedef void (*free_array_func_t)(void *p, size_t n);
/* 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 new(t, n) ((t*) malloc_multiply(n, sizeof(t)))
#define new0(t, n) ((t*) calloc((n) ?: 1, sizeof(t)))
@@ -46,9 +45,9 @@ typedef void (*free_array_func_t)(void *p, size_t n);
(t*) alloca0((sizeof(t)*_n_)); \
})
-#define newdup(t, p, n) ((t*) memdup_multiply(p, sizeof(t), (n)))
+#define newdup(t, p, n) ((t*) memdup_multiply(p, n, sizeof(t)))
-#define newdup_suffix0(t, p, n) ((t*) memdup_suffix0_multiply(p, sizeof(t), (n)))
+#define newdup_suffix0(t, p, n) ((t*) memdup_suffix0_multiply(p, n, sizeof(t)))
#define malloc0(n) (calloc(1, (n) ?: 1))
@@ -113,7 +112,7 @@ 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) {
+_malloc_ _alloc_(1, 2) static inline void *malloc_multiply(size_t need, size_t size) {
if (size_multiply_overflow(size, need))
return NULL;
@@ -129,7 +128,7 @@ _alloc_(2, 3) static inline void *reallocarray(void *p, size_t need, size_t size
}
#endif
-_alloc_(2, 3) static inline void *memdup_multiply(const void *p, size_t size, size_t need) {
+_alloc_(2, 3) static inline void *memdup_multiply(const void *p, size_t need, size_t size) {
if (size_multiply_overflow(size, need))
return NULL;
@@ -138,7 +137,7 @@ _alloc_(2, 3) static inline void *memdup_multiply(const void *p, size_t size, si
/* 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) {
+static inline void *memdup_suffix0_multiply(const void *p, size_t need, size_t size) {
if (size_multiply_overflow(size, need))
return NULL;
@@ -147,6 +146,7 @@ static inline void *memdup_suffix0_multiply(const void *p, size_t size, size_t n
void* greedy_realloc(void **p, size_t need, size_t size);
void* greedy_realloc0(void **p, size_t need, size_t size);
+void* greedy_realloc_append(void **p, size_t *n_p, const void *from, size_t n_from, size_t size);
#define GREEDY_REALLOC(array, need) \
greedy_realloc((void**) &(array), (need), sizeof((array)[0]))
@@ -154,6 +154,9 @@ void* greedy_realloc0(void **p, size_t need, size_t size);
#define GREEDY_REALLOC0(array, need) \
greedy_realloc0((void**) &(array), (need), sizeof((array)[0]))
+#define GREEDY_REALLOC_APPEND(array, n_array, from, n_from) \
+ greedy_realloc_append((void**) &(array), (size_t*) &(n_array), (from), (n_from), sizeof((array)[0]))
+
#define alloca0(n) \
({ \
char *_new_; \
@@ -224,7 +227,6 @@ static inline size_t malloc_sizeof_safe(void **xp) {
MALLOC_SIZEOF_SAFE(x)/sizeof((x)[0]), \
VOID_0))
-
/* These are like strdupa()/strndupa(), but honour ALLOCA_MAX */
#define strdupa_safe(s) \
({ \
@@ -235,7 +237,40 @@ static inline size_t malloc_sizeof_safe(void **xp) {
#define strndupa_safe(s, n) \
({ \
const char *_t = (s); \
- (char*) memdupa_suffix0(_t, strnlen(_t, (n))); \
+ (char*) memdupa_suffix0(_t, strnlen(_t, n)); \
})
+/* Free every element of the array. */
+static inline void free_many(void **p, size_t n) {
+ assert(p || n == 0);
+
+ FOREACH_ARRAY(i, p, n)
+ *i = mfree(*i);
+}
+
+/* Typesafe wrapper for char** rather than void**. Unfortunately C won't implicitly cast this. */
+static inline void free_many_charp(char **c, size_t n) {
+ free_many((void**) c, n);
+}
+
+_alloc_(2) static inline void *realloc0(void *p, size_t new_size) {
+ size_t old_size;
+ void *q;
+
+ /* Like realloc(), but initializes anything appended to zero */
+
+ old_size = MALLOC_SIZEOF_SAFE(p);
+
+ q = realloc(p, new_size);
+ if (!q)
+ return NULL;
+
+ new_size = MALLOC_SIZEOF_SAFE(q); /* Update with actually allocated space */
+
+ if (new_size > old_size)
+ memset((uint8_t*) q + old_size, 0, new_size - old_size);
+
+ return q;
+}
+
#include "memory-util.h"
diff --git a/src/libnm-systemd-shared/src/basic/arphrd-util.h b/src/libnm-systemd-shared/src/basic/arphrd-util.h
new file mode 100644
index 0000000000..33f5694abd
--- /dev/null
+++ b/src/libnm-systemd-shared/src/basic/arphrd-util.h
@@ -0,0 +1,10 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+#pragma once
+
+#include <inttypes.h>
+#include <stddef.h>
+
+const char *arphrd_to_name(int id);
+int arphrd_from_name(const char *name);
+
+size_t arphrd_to_hw_addr_len(uint16_t arphrd);
diff --git a/src/libnm-systemd-shared/src/basic/async.h b/src/libnm-systemd-shared/src/basic/async.h
deleted file mode 100644
index e0bbaa5658..0000000000
--- a/src/libnm-systemd-shared/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/src/libnm-systemd-shared/src/basic/btrfs.c b/src/libnm-systemd-shared/src/basic/btrfs.c
new file mode 100644
index 0000000000..3b23607849
--- /dev/null
+++ b/src/libnm-systemd-shared/src/basic/btrfs.c
@@ -0,0 +1,100 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+
+#include "nm-sd-adapt-shared.h"
+
+#include <linux/btrfs.h>
+#include <sys/ioctl.h>
+
+#include "btrfs.h"
+#include "fd-util.h"
+#include "fs-util.h"
+#include "path-util.h"
+
+int btrfs_validate_subvolume_name(const char *name) {
+
+ if (!filename_is_valid(name))
+ return -EINVAL;
+
+ if (strlen(name) > BTRFS_SUBVOL_NAME_MAX)
+ return -E2BIG;
+
+ return 0;
+}
+
+static int extract_subvolume_name(const char *path, char **ret) {
+ _cleanup_free_ char *fn = NULL;
+ int r;
+
+ assert(path);
+ assert(ret);
+
+ r = path_extract_filename(path, &fn);
+ if (r < 0)
+ return r;
+
+ r = btrfs_validate_subvolume_name(fn);
+ if (r < 0)
+ return r;
+
+ *ret = TAKE_PTR(fn);
+ return 0;
+}
+
+int btrfs_subvol_make(int dir_fd, const char *path) {
+ struct btrfs_ioctl_vol_args args = {};
+ _cleanup_free_ char *subvolume = NULL, *parent = NULL;
+ _cleanup_close_ int fd = -EBADF;
+ int r;
+
+ assert(dir_fd >= 0 || dir_fd == AT_FDCWD);
+ assert(!isempty(path));
+
+ r = extract_subvolume_name(path, &subvolume);
+ if (r < 0)
+ return r;
+
+ r = path_extract_directory(path, &parent);
+ if (r < 0) {
+ if (r != -EDESTADDRREQ) /* Propagate error, unless only a filename was specified, which is OK */
+ return r;
+
+ dir_fd = fd_reopen_condition(dir_fd, O_CLOEXEC, O_PATH, &fd); /* drop O_PATH if it is set */
+ if (dir_fd < 0)
+ return dir_fd;
+ } else {
+ fd = openat(dir_fd, parent, O_DIRECTORY|O_RDONLY|O_CLOEXEC, 0);
+ if (fd < 0)
+ return -errno;
+
+ dir_fd = fd;
+ }
+
+ strncpy(args.name, subvolume, sizeof(args.name)-1);
+
+ return RET_NERRNO(ioctl(dir_fd, BTRFS_IOC_SUBVOL_CREATE, &args));
+}
+
+int btrfs_subvol_make_fallback(int dir_fd, const char *path, mode_t mode) {
+ mode_t old, combined;
+ int r;
+
+ assert(path);
+
+ /* Let's work like mkdir(), i.e. take the specified mode, and mask it with the current umask. */
+ old = umask(~mode);
+ combined = old | ~mode;
+ if (combined != ~mode)
+ umask(combined);
+ r = btrfs_subvol_make(dir_fd, path);
+ umask(old);
+
+ if (r >= 0)
+ return 1; /* subvol worked */
+ if (!ERRNO_IS_NOT_SUPPORTED(r))
+ return r;
+
+ if (mkdirat(dir_fd, path, mode) < 0)
+ return -errno;
+
+ return 0; /* plain directory */
+}
diff --git a/src/libnm-systemd-shared/src/basic/btrfs.h b/src/libnm-systemd-shared/src/basic/btrfs.h
new file mode 100644
index 0000000000..38be9d2b3b
--- /dev/null
+++ b/src/libnm-systemd-shared/src/basic/btrfs.h
@@ -0,0 +1,9 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+
+#include <fcntl.h>
+
+int btrfs_validate_subvolume_name(const char *name);
+
+int btrfs_subvol_make(int dir_fd, const char *path);
+
+int btrfs_subvol_make_fallback(int dir_fd, const char *path, mode_t mode);
diff --git a/src/libnm-systemd-shared/src/basic/cgroup-util.h b/src/libnm-systemd-shared/src/basic/cgroup-util.h
index 9b30ae0396..244f3b657b 100644
--- a/src/libnm-systemd-shared/src/basic/cgroup-util.h
+++ b/src/libnm-systemd-shared/src/basic/cgroup-util.h
@@ -10,6 +10,7 @@
#include <sys/types.h>
#include "constants.h"
+#include "pidref.h"
#include "set.h"
#define SYSTEMD_CGROUP_CONTROLLER_LEGACY "name=systemd"
@@ -35,7 +36,7 @@ typedef enum CGroupController {
CGROUP_CONTROLLER_BPF_SOCKET_BIND,
CGROUP_CONTROLLER_BPF_RESTRICT_NETWORK_INTERFACES,
/* The BPF hook implementing RestrictFileSystems= is not defined here.
- * It's applied as late as possible in exec_child() so we don't block
+ * It's applied as late as possible in exec_invoke() so we don't block
* our own unit setup code. */
_CGROUP_CONTROLLER_MAX,
@@ -66,10 +67,13 @@ typedef enum CGroupMask {
/* All real cgroup v2 controllers */
CGROUP_MASK_V2 = CGROUP_MASK_CPU|CGROUP_MASK_CPUSET|CGROUP_MASK_IO|CGROUP_MASK_MEMORY|CGROUP_MASK_PIDS,
+ /* All controllers we want to delegate in case of Delegate=yes. Which are pretty much the v2 controllers only, as delegation on v1 is not safe, and bpf stuff isn't a real controller */
+ CGROUP_MASK_DELEGATE = CGROUP_MASK_V2,
+
/* All cgroup v2 BPF pseudo-controllers */
CGROUP_MASK_BPF = CGROUP_MASK_BPF_FIREWALL|CGROUP_MASK_BPF_DEVICES|CGROUP_MASK_BPF_FOREIGN|CGROUP_MASK_BPF_SOCKET_BIND|CGROUP_MASK_BPF_RESTRICT_NETWORK_INTERFACES,
- _CGROUP_MASK_ALL = CGROUP_CONTROLLER_TO_MASK(_CGROUP_CONTROLLER_MAX) - 1
+ _CGROUP_MASK_ALL = CGROUP_CONTROLLER_TO_MASK(_CGROUP_CONTROLLER_MAX) - 1,
} CGroupMask;
static inline CGroupMask CGROUP_MASK_EXTEND_JOINED(CGroupMask mask) {
@@ -176,13 +180,13 @@ typedef enum CGroupUnified {
* 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_processes(const char *controller, const char *path, FILE **ret);
+int cg_read_pid(FILE *f, pid_t *ret);
+int cg_read_pidref(FILE *f, PidRef *ret);
+int cg_read_event(const char *controller, const char *path, const char *event, char **ret);
-int cg_enumerate_subgroups(const char *controller, const char *path, DIR **_d);
-int cg_read_subgroup(DIR *d, char **fn);
+int cg_enumerate_subgroups(const char *controller, const char *path, DIR **ret);
+int cg_read_subgroup(DIR *d, char **ret);
typedef enum CGroupFlags {
CGROUP_SIGCONT = 1 << 0,
@@ -190,25 +194,31 @@ typedef enum CGroupFlags {
CGROUP_REMOVE = 1 << 2,
} CGroupFlags;
-typedef int (*cg_kill_log_func_t)(pid_t pid, int sig, void *userdata);
+typedef int (*cg_kill_log_func_t)(const PidRef *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_kernel_sigkill(const char *controller, const char *path);
-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_kill(const char *path, int sig, CGroupFlags flags, Set *s, cg_kill_log_func_t kill_log, void *userdata);
+int cg_kill_kernel_sigkill(const char *path);
+int cg_kill_recursive(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_mangle_path(const char *path, char **ret);
-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_get_path(const char *controller, const char *path, const char *suffix, char **ret);
+int cg_get_path_and_check(const char *controller, const char *path, const char *suffix, char **ret);
-int cg_pid_get_path(const char *controller, pid_t pid, char **path);
+int cg_pid_get_path(const char *controller, pid_t pid, char **ret);
+int cg_pidref_get_path(const char *controller, const PidRef *pidref, char **ret);
int cg_rmdir(const char *controller, const char *path);
-int cg_is_threaded(const char *controller, const char *path);
+int cg_is_threaded(const char *path);
+
+int cg_is_delegated(const char *path);
+int cg_is_delegated_fd(int fd);
+
+int cg_has_coredump_receive(const char *path);
-typedef enum {
+typedef enum {
CG_KEY_MODE_GRACEFUL = 1 << 0,
} CGroupKeyMode;
@@ -239,14 +249,14 @@ int cg_get_attribute_as_uint64(const char *controller, const char *path, const c
/* 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_get_owner(const char *controller, const char *path, uid_t *ret_uid);
+int cg_get_owner(const char *path, uid_t *ret_uid);
-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_set_xattr(const char *path, const char *name, const void *value, size_t size, int flags);
+int cg_get_xattr(const char *path, const char *name, void *value, size_t size);
+int cg_get_xattr_malloc(const char *path, const char *name, char **ret);
/* Returns negative on error, and 0 or 1 on success for the bool value */
-int cg_get_xattr_bool(const char *controller, const char *path, const char *name);
-int cg_remove_xattr(const char *controller, const char *path, const char *name);
+int cg_get_xattr_bool(const char *path, const char *name);
+int cg_remove_xattr(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);
@@ -257,27 +267,28 @@ int cg_is_empty_recursive(const char *controller, const char *path);
int cg_get_root_path(char **path);
int cg_path_get_cgroupid(const char *path, uint64_t *ret);
-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_unit_path(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);
+int cg_path_get_session(const char *path, char **ret_session);
+int cg_path_get_owner_uid(const char *path, uid_t *ret_uid);
+int cg_path_get_unit(const char *path, char **ret_unit);
+int cg_path_get_unit_path(const char *path, char **ret_unit);
+int cg_path_get_user_unit(const char *path, char **ret_unit);
+int cg_path_get_machine_name(const char *path, char **ret_machine);
+int cg_path_get_slice(const char *path, char **ret_slice);
+int cg_path_get_user_slice(const char *path, char **ret_slice);
+
+int cg_shift_path(const char *cgroup, const char *cached_root, const char **ret_shifted);
+int cg_pid_get_path_shifted(pid_t pid, const char *cached_root, char **ret_cgroup);
+
+int cg_pid_get_session(pid_t pid, char **ret_session);
+int cg_pid_get_owner_uid(pid_t pid, uid_t *ret_uid);
+int cg_pid_get_unit(pid_t pid, char **ret_unit);
+int cg_pidref_get_unit(const PidRef *pidref, char **ret);
+int cg_pid_get_user_unit(pid_t pid, char **ret_unit);
+int cg_pid_get_machine_name(pid_t pid, char **ret_machine);
+int cg_pid_get_slice(pid_t pid, char **ret_slice);
+int cg_pid_get_user_slice(pid_t pid, char **ret_slice);
+
+int cg_path_decode_unit(const char *cgroup, char **ret_unit);
bool cg_needs_escape(const char *p);
int cg_escape(const char *p, char **ret);
diff --git a/src/libnm-systemd-shared/src/basic/constants.h b/src/libnm-systemd-shared/src/basic/constants.h
index 3f96786da9..6bb5f3c281 100644
--- a/src/libnm-systemd-shared/src/basic/constants.h
+++ b/src/libnm-systemd-shared/src/basic/constants.h
@@ -59,22 +59,13 @@
#define NOTIFY_FD_MAX 768
#define NOTIFY_BUFFER_MAX PIPE_BUF
-#if HAVE_SPLIT_USR
-# define _CONF_PATHS_SPLIT_USR_NULSTR(n) "/lib/" n "\0"
-# define _CONF_PATHS_SPLIT_USR(n) , "/lib/" n
-#else
-# define _CONF_PATHS_SPLIT_USR_NULSTR(n)
-# define _CONF_PATHS_SPLIT_USR(n)
-#endif
-
/* Return a nulstr for a standard cascade of configuration paths, suitable to pass to
* conf_files_list_nulstr() to implement drop-in directories for extending configuration files. */
#define CONF_PATHS_NULSTR(n) \
"/etc/" n "\0" \
"/run/" n "\0" \
"/usr/local/lib/" n "\0" \
- "/usr/lib/" n "\0" \
- _CONF_PATHS_SPLIT_USR_NULSTR(n)
+ "/usr/lib/" n "\0"
#define CONF_PATHS_USR(n) \
"/etc/" n, \
@@ -83,8 +74,7 @@
"/usr/lib/" n
#define CONF_PATHS(n) \
- CONF_PATHS_USR(n) \
- _CONF_PATHS_SPLIT_USR(n)
+ CONF_PATHS_USR(n)
#define CONF_PATHS_USR_STRV(n) \
STRV_MAKE(CONF_PATHS_USR(n))
@@ -99,14 +89,9 @@
* in containers so that our children inherit that. */
#define DEFAULT_RLIMIT_MEMLOCK (1024ULL*1024ULL*8ULL)
-#define PLYMOUTH_SOCKET { \
- .un.sun_family = AF_UNIX, \
- .un.sun_path = "\0/org/freedesktop/plymouthd", \
- }
-
/* Path where PID1 listens for varlink subscriptions from systemd-oomd to notify of changes in ManagedOOM settings. */
-#define VARLINK_ADDR_PATH_MANAGED_OOM_SYSTEM "/run/systemd/io.system.ManagedOOM"
+#define VARLINK_ADDR_PATH_MANAGED_OOM_SYSTEM "/run/systemd/io.systemd.ManagedOOM"
/* Path where systemd-oomd listens for varlink connections from user managers to report changes in ManagedOOM settings. */
-#define VARLINK_ADDR_PATH_MANAGED_OOM_USER "/run/systemd/oom/io.system.ManagedOOM"
+#define VARLINK_ADDR_PATH_MANAGED_OOM_USER "/run/systemd/oom/io.systemd.ManagedOOM"
#define KERNEL_BASELINE_VERSION "4.15"
diff --git a/src/libnm-systemd-shared/src/basic/env-file.c b/src/libnm-systemd-shared/src/basic/env-file.c
index db270bedce..75b2febf7d 100644
--- a/src/libnm-systemd-shared/src/basic/env-file.c
+++ b/src/libnm-systemd-shared/src/basic/env-file.c
@@ -127,7 +127,7 @@ static int parse_env_file_internal(
state = VALUE;
if (!GREEDY_REALLOC(value, n_value+2))
- return -ENOMEM;
+ return -ENOMEM;
value[n_value++] = c;
}
@@ -245,7 +245,13 @@ static int parse_env_file_internal(
break;
case COMMENT_ESCAPE:
- state = COMMENT;
+ log_debug("The line which doesn't begin with \";\" or \"#\", but follows a comment" \
+ " line trailing with escape is now treated as a non comment line since v254.");
+ if (strchr(NEWLINE, c)) {
+ state = PRE_KEY;
+ line++;
+ } else
+ state = COMMENT;
break;
}
}
@@ -522,6 +528,7 @@ static int merge_env_file_push(
char ***env = ASSERT_PTR(userdata);
char *expanded_value;
+ int r;
assert(key);
@@ -536,12 +543,12 @@ static int merge_env_file_push(
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;
+ r = replace_env(value,
+ *env,
+ REPLACE_ENV_USE_ENVIRONMENT|REPLACE_ENV_ALLOW_BRACELESS|REPLACE_ENV_ALLOW_EXTENDED,
+ &expanded_value);
+ if (r < 0)
+ return log_error_errno(r, "%s:%u: Failed to expand variable '%s': %m", strna(filename), line, value);
free_and_replace(value, expanded_value);
@@ -599,7 +606,7 @@ static void write_env_var(FILE *f, const char *v) {
fputc_unlocked('\n', f);
}
-int write_env_file_at(int dir_fd, const char *fname, char **l) {
+int write_env_file(int dir_fd, const char *fname, char **headers, char **l) {
_cleanup_fclose_ FILE *f = NULL;
_cleanup_free_ char *p = NULL;
int r;
@@ -613,6 +620,12 @@ int write_env_file_at(int dir_fd, const char *fname, char **l) {
(void) fchmod_umask(fileno(f), 0644);
+ STRV_FOREACH(i, headers) {
+ assert(isempty(*i) || startswith(*i, "#"));
+ fputs_unlocked(*i, f);
+ fputc_unlocked('\n', f);
+ }
+
STRV_FOREACH(i, l)
write_env_var(f, *i);
@@ -627,4 +640,12 @@ int write_env_file_at(int dir_fd, const char *fname, char **l) {
(void) unlinkat(dir_fd, p, 0);
return r;
}
+
+int write_vconsole_conf(int dir_fd, const char *fname, char **l) {
+ char **headers = STRV_MAKE(
+ "# Written by systemd-localed(8) or systemd-firstboot(1), read by systemd-localed",
+ "# and systemd-vconsole-setup(8). Use localectl(1) to update this file.");
+
+ return write_env_file(dir_fd, fname, headers, l);
+}
#endif /* NM_IGNORED */
diff --git a/src/libnm-systemd-shared/src/basic/env-file.h b/src/libnm-systemd-shared/src/basic/env-file.h
index 2465eeddf4..37db30765b 100644
--- a/src/libnm-systemd-shared/src/basic/env-file.h
+++ b/src/libnm-systemd-shared/src/basic/env-file.h
@@ -19,7 +19,6 @@ int load_env_file_pairs_fd(int fd, const char *fname, char ***ret);
int merge_env_file(char ***env, FILE *f, const char *fname);
-int write_env_file_at(int dir_fd, const char *fname, char **l);
-static inline int write_env_file(const char *fname, char **l) {
- return write_env_file_at(AT_FDCWD, fname, l);
-}
+int write_env_file(int dir_fd, const char *fname, char **headers, char **l);
+
+int write_vconsole_conf(int dir_fd, const char *fname, char **l);
diff --git a/src/libnm-systemd-shared/src/basic/env-util.c b/src/libnm-systemd-shared/src/basic/env-util.c
index fa2753bccb..c12caa2e55 100644
--- a/src/libnm-systemd-shared/src/basic/env-util.c
+++ b/src/libnm-systemd-shared/src/basic/env-util.c
@@ -29,20 +29,21 @@
"_"
static bool env_name_is_valid_n(const char *e, size_t n) {
- if (!e)
- return false;
+
+ if (n == SIZE_MAX)
+ n = strlen_ptr(e);
if (n <= 0)
return false;
+ assert(e);
+
if (ascii_isdigit(e[0]))
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. */
+ /* 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;
@@ -246,9 +247,9 @@ static bool env_match(const char *t, const char *pattern) {
return true;
if (!strchr(pattern, '=')) {
- size_t l = strlen(pattern);
+ t = startswith(t, pattern);
- return strneq(t, pattern, l) && t[l] == '=';
+ return t && *t == '=';
}
return false;
@@ -460,6 +461,35 @@ int strv_env_assign(char ***l, const char *key, const char *value) {
return strv_env_replace_consume(l, p);
}
+int strv_env_assignf(char ***l, const char *key, const char *valuef, ...) {
+ int r;
+
+ assert(l);
+ assert(key);
+
+ if (!env_name_is_valid(key))
+ return -EINVAL;
+
+ if (!valuef) {
+ strv_env_unset(*l, key);
+ return 0;
+ }
+
+ _cleanup_free_ char *value = NULL;
+ va_list ap;
+ va_start(ap, valuef);
+ r = vasprintf(&value, valuef, ap);
+ va_end(ap);
+ if (r < 0)
+ return -ENOMEM;
+
+ char *p = strjoin(key, "=", value);
+ if (!p)
+ return -ENOMEM;
+
+ return strv_env_replace_consume(l, p);
+}
+
int _strv_env_assign_many(char ***l, ...) {
va_list ap;
int r;
@@ -502,32 +532,31 @@ int _strv_env_assign_many(char ***l, ...) {
return 0;
}
-char *strv_env_get_n(char **l, const char *name, size_t k, unsigned flags) {
+char* strv_env_get_n(char * const *l, const char *name, size_t k, ReplaceEnvFlags flags) {
assert(name);
+ if (k == SIZE_MAX)
+ k = strlen(name);
if (k <= 0)
return NULL;
STRV_FOREACH_BACKWARDS(i, l)
- if (strneq(*i, name, k) &&
- (*i)[k] == '=')
- return *i + k + 1;
+ if (strneq(*i, name, k) && (*i)[k] == '=')
+ return (char*) *i + k + 1;
if (flags & REPLACE_ENV_USE_ENVIRONMENT) {
const char *t;
+ /* Safety check that the name is not overly long, before we do a stack allocation */
+ if (k > (size_t) sysconf(_SC_ARG_MAX) - 2)
+ return NULL;
+
t = strndupa_safe(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);
-}
#endif /* NM_IGNORED */
char *strv_env_pairs_get(char **l, const char *name) {
@@ -578,7 +607,61 @@ char **strv_env_clean_with_callback(char **e, void (*invalid_callback)(const cha
return e;
}
-char *replace_env_n(const char *format, size_t n, char **env, unsigned flags) {
+static int strv_extend_with_length(char ***l, const char *s, size_t n) {
+ char *c;
+
+ c = strndup(s, n);
+ if (!c)
+ return -ENOMEM;
+
+ return strv_consume(l, c);
+}
+
+static int strv_env_get_n_validated(
+ char **env,
+ const char *name,
+ size_t l,
+ ReplaceEnvFlags flags,
+ char **ret, /* points into the env block! do not free! */
+ char ***unset_variables, /* updated in place */
+ char ***bad_variables) { /* ditto */
+
+ char *e;
+ int r;
+
+ assert(l == 0 || name);
+ assert(ret);
+
+ if (env_name_is_valid_n(name, l)) {
+ e = strv_env_get_n(env, name, l, flags);
+ if (!e && unset_variables) {
+ r = strv_extend_with_length(unset_variables, name, l);
+ if (r < 0)
+ return r;
+ }
+ } else {
+ e = NULL; /* Resolve invalid variable names the same way as unset ones */
+
+ if (bad_variables) {
+ r = strv_extend_with_length(bad_variables, name, l);
+ if (r < 0)
+ return r;
+ }
+ }
+
+ *ret = e;
+ return !!e;
+}
+
+int replace_env_full(
+ const char *format,
+ size_t n,
+ char **env,
+ ReplaceEnvFlags flags,
+ char **ret,
+ char ***ret_unset_variables,
+ char ***ret_bad_variables) {
+
enum {
WORD,
CURLY,
@@ -589,15 +672,22 @@ char *replace_env_n(const char *format, size_t n, char **env, unsigned flags) {
ALTERNATE_VALUE,
} state = WORD;
+ _cleanup_strv_free_ char **unset_variables = NULL, **bad_variables = NULL;
const char *e, *word = format, *test_value = NULL; /* test_value is initialized to appease gcc */
- char *k;
_cleanup_free_ char *s = NULL;
+ char ***pu, ***pb, *k;
size_t i, len = 0; /* len is initialized to appease gcc */
- int nest = 0;
+ int nest = 0, r;
assert(format);
- for (e = format, i = 0; *e && i < n; e ++, i ++)
+ if (n == SIZE_MAX)
+ n = strlen(format);
+
+ pu = ret_unset_variables ? &unset_variables : NULL;
+ pb = ret_bad_variables ? &bad_variables : NULL;
+
+ for (e = format, i = 0; *e && i < n; e++, i++)
switch (state) {
case WORD:
@@ -609,27 +699,28 @@ char *replace_env_n(const char *format, size_t n, char **env, unsigned flags) {
if (*e == '{') {
k = strnappend(s, word, e-word-1);
if (!k)
- return NULL;
+ return -ENOMEM;
free_and_replace(s, k);
word = e-1;
state = VARIABLE;
nest++;
+
} else if (*e == '$') {
k = strnappend(s, word, e-word);
if (!k)
- return NULL;
+ return -ENOMEM;
free_and_replace(s, k);
word = e+1;
state = WORD;
- } else if (flags & REPLACE_ENV_ALLOW_BRACELESS && strchr(VALID_BASH_ENV_NAME_CHARS, *e)) {
+ } else if (FLAGS_SET(flags, REPLACE_ENV_ALLOW_BRACELESS) && strchr(VALID_BASH_ENV_NAME_CHARS, *e)) {
k = strnappend(s, word, e-word-1);
if (!k)
- return NULL;
+ return -ENOMEM;
free_and_replace(s, k);
@@ -642,12 +733,14 @@ char *replace_env_n(const char *format, size_t n, char **env, unsigned flags) {
case VARIABLE:
if (*e == '}') {
- const char *t;
+ char *t;
- t = strv_env_get_n(env, word+2, e-word-2, flags);
+ r = strv_env_get_n_validated(env, word+2, e-word-2, flags, &t, pu, pb);
+ if (r < 0)
+ return r;
if (!strextend(&s, t))
- return NULL;
+ return -ENOMEM;
word = e+1;
state = WORD;
@@ -689,18 +782,37 @@ char *replace_env_n(const char *format, size_t n, char **env, unsigned flags) {
nest--;
if (nest == 0) {
- const char *t;
+ _cleanup_strv_free_ char **u = NULL, **b = NULL;
_cleanup_free_ char *v = NULL;
+ char *t = NULL;
+
+ r = strv_env_get_n_validated(env, word+2, len, flags, &t, pu, pb);
+ if (r < 0)
+ return r;
+
+ if (t && state == ALTERNATE_VALUE) {
+ r = replace_env_full(test_value, e-test_value, env, flags, &v, pu ? &u : NULL, pb ? &b : NULL);
+ if (r < 0)
+ return r;
+
+ t = v;
+ } else if (!t && state == DEFAULT_VALUE) {
+ r = replace_env_full(test_value, e-test_value, env, flags, &v, pu ? &u : NULL, pb ? &b : NULL);
+ if (r < 0)
+ return r;
- t = strv_env_get_n(env, word+2, len, flags);
+ t = v;
+ }
- 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);
+ r = strv_extend_strv(&unset_variables, u, /* filter_duplicates= */ true);
+ if (r < 0)
+ return r;
+ r = strv_extend_strv(&bad_variables, b, /* filter_duplicates= */ true);
+ if (r < 0)
+ return r;
if (!strextend(&s, t))
- return NULL;
+ return -ENOMEM;
word = e+1;
state = WORD;
@@ -711,12 +823,14 @@ char *replace_env_n(const char *format, size_t n, char **env, unsigned flags) {
assert(flags & REPLACE_ENV_ALLOW_BRACELESS);
if (!strchr(VALID_BASH_ENV_NAME_CHARS, *e)) {
- const char *t;
+ char *t = NULL;
- t = strv_env_get_n(env, word+1, e-word-1, flags);
+ r = strv_env_get_n_validated(env, word+1, e-word-1, flags, &t, &unset_variables, &bad_variables);
+ if (r < 0)
+ return r;
if (!strextend(&s, t))
- return NULL;
+ return -ENOMEM;
word = e--;
i--;
@@ -726,58 +840,83 @@ char *replace_env_n(const char *format, size_t n, char **env, unsigned flags) {
}
if (state == VARIABLE_RAW) {
- const char *t;
+ char *t;
assert(flags & REPLACE_ENV_ALLOW_BRACELESS);
- t = strv_env_get_n(env, word+1, e-word-1, flags);
- return strjoin(s, t);
- } else
- return strnappend(s, word, e-word);
+ r = strv_env_get_n_validated(env, word+1, e-word-1, flags, &t, &unset_variables, &bad_variables);
+ if (r < 0)
+ return r;
+
+ if (!strextend(&s, t))
+ return -ENOMEM;
+
+ } else if (!strextendn(&s, word, e-word))
+ return -ENOMEM;
+
+ if (ret_unset_variables)
+ *ret_unset_variables = TAKE_PTR(unset_variables);
+ if (ret_bad_variables)
+ *ret_bad_variables = TAKE_PTR(bad_variables);
+
+ if (ret)
+ *ret = TAKE_PTR(s);
+
+ return 0;
}
-char **replace_env_argv(char **argv, char **env) {
- _cleanup_strv_free_ char **ret = NULL;
+int replace_env_argv(
+ char **argv,
+ char **env,
+ char ***ret,
+ char ***ret_unset_variables,
+ char ***ret_bad_variables) {
+
+ _cleanup_strv_free_ char **n = NULL, **unset_variables = NULL, **bad_variables = NULL;
size_t k = 0, l = 0;
+ int r;
l = strv_length(argv);
- ret = new(char*, l+1);
- if (!ret)
- return NULL;
+ n = new(char*, l+1);
+ if (!n)
+ return -ENOMEM;
STRV_FOREACH(i, argv) {
+ const char *word = *i;
/* If $FOO appears as single word, replace it by the split up variable */
- if ((*i)[0] == '$' && !IN_SET((*i)[1], '{', '$')) {
- char *e;
- char **w;
+ if (word[0] == '$' && !IN_SET(word[1], '{', '$')) {
_cleanup_strv_free_ char **m = NULL;
+ const char *name = word + 1;
+ char *e, **w;
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;
- return NULL;
- }
- }
+ if (env_name_is_valid(name)) {
+ e = strv_env_get(env, name);
+ if (e)
+ r = strv_split_full(&m, e, WHITESPACE, EXTRACT_RELAX|EXTRACT_UNQUOTE);
+ else if (ret_unset_variables)
+ r = strv_extend(&unset_variables, name);
+ else
+ r = 0;
+ } else if (ret_bad_variables)
+ r = strv_extend(&bad_variables, name);
+ else
+ r = 0;
+ if (r < 0)
+ return r;
q = strv_length(m);
l = l + q - 1;
- w = reallocarray(ret, l + 1, sizeof(char *));
- if (!w) {
- ret[k] = NULL;
- return NULL;
- }
+ w = reallocarray(n, l + 1, sizeof(char*));
+ if (!w)
+ return -ENOMEM;
- ret = w;
+ n = w;
if (m) {
- memcpy(ret + k, m, q * sizeof(char*));
+ memcpy(n + k, m, (q + 1) * sizeof(char*));
m = mfree(m);
}
@@ -785,15 +924,41 @@ char **replace_env_argv(char **argv, char **env) {
continue;
}
+ _cleanup_strv_free_ char **u = NULL, **b = NULL;
+
/* 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])
- return NULL;
- k++;
+ r = replace_env_full(
+ word,
+ /* length= */ SIZE_MAX,
+ env,
+ /* flags= */ 0,
+ n + k,
+ ret_unset_variables ? &u : NULL,
+ ret_bad_variables ? &b : NULL);
+ if (r < 0)
+ return r;
+ n[++k] = NULL;
+
+ r = strv_extend_strv(&unset_variables, u, /* filter_duplicates= */ true);
+ if (r < 0)
+ return r;
+
+ r = strv_extend_strv(&bad_variables, b, /*filter_duplicates= */ true);
+ if (r < 0)
+ return r;
+ }
+
+ if (ret_unset_variables) {
+ strv_uniq(strv_sort(unset_variables));
+ *ret_unset_variables = TAKE_PTR(unset_variables);
+ }
+ if (ret_bad_variables) {
+ strv_uniq(strv_sort(bad_variables));
+ *ret_bad_variables = TAKE_PTR(bad_variables);
}
- ret[k] = NULL;
- return TAKE_PTR(ret);
+ *ret = TAKE_PTR(n);
+ return 0;
}
#endif /* NM_IGNORED */
@@ -853,8 +1018,8 @@ int putenv_dup(const char *assignment, bool override) {
}
int setenv_systemd_exec_pid(bool update_only) {
- char str[DECIMAL_STR_MAX(pid_t)];
const char *e;
+ int r;
/* Update $SYSTEMD_EXEC_PID=pid except when '*' is set for the variable. */
@@ -865,10 +1030,9 @@ int setenv_systemd_exec_pid(bool update_only) {
if (streq_ptr(e, "*"))
return 0;
- xsprintf(str, PID_FMT, getpid_cached());
-
- if (setenv("SYSTEMD_EXEC_PID", str, 1) < 0)
- return -errno;
+ r = setenvf("SYSTEMD_EXEC_PID", /* overwrite= */ 1, PID_FMT, getpid_cached());
+ if (r < 0)
+ return r;
return 1;
}
@@ -944,4 +1108,45 @@ int getenv_steal_erase(const char *name, char **ret) {
return 1;
}
+
+int set_full_environment(char **env) {
+ int r;
+
+ clearenv();
+
+ STRV_FOREACH(e, env) {
+ _cleanup_free_ char *k = NULL, *v = NULL;
+
+ r = split_pair(*e, "=", &k, &v);
+ if (r < 0)
+ return r;
+
+ if (setenv(k, v, /* overwrite= */ true) < 0)
+ return -errno;
+ }
+
+ return 0;
+}
+
+int setenvf(const char *name, bool overwrite, const char *valuef, ...) {
+ _cleanup_free_ char *value = NULL;
+ va_list ap;
+ int r;
+
+ assert(name);
+
+ if (!valuef)
+ return RET_NERRNO(unsetenv(name));
+
+ va_start(ap, valuef);
+ DISABLE_WARNING_FORMAT_NONLITERAL;
+ r = vasprintf(&value, valuef, ap);
+ REENABLE_WARNING;
+ va_end(ap);
+
+ if (r < 0)
+ return -ENOMEM;
+
+ return RET_NERRNO(setenv(name, value, overwrite));
+}
#endif /* NM_IGNORED */
diff --git a/src/libnm-systemd-shared/src/basic/env-util.h b/src/libnm-systemd-shared/src/basic/env-util.h
index b0ff5a11d1..ad127de39f 100644
--- a/src/libnm-systemd-shared/src/basic/env-util.h
+++ b/src/libnm-systemd-shared/src/basic/env-util.h
@@ -19,19 +19,19 @@ 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 {
+typedef enum ReplaceEnvFlags {
REPLACE_ENV_USE_ENVIRONMENT = 1 << 0,
REPLACE_ENV_ALLOW_BRACELESS = 1 << 1,
REPLACE_ENV_ALLOW_EXTENDED = 1 << 2,
-};
+} ReplaceEnvFlags;
-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);
+int replace_env_full(const char *format, size_t n, char **env, ReplaceEnvFlags flags, char **ret, char ***ret_unset_variables, char ***ret_bad_variables);
+static inline int replace_env(const char *format, char **env, ReplaceEnvFlags flags, char **ret) {
+ return replace_env_full(format, SIZE_MAX, env, flags, ret, NULL, NULL);
}
+int replace_env_argv(char **argv, char **env, char ***ret, char ***ret_unset_variables, char ***ret_bad_variables);
+
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);
@@ -49,11 +49,15 @@ int strv_env_replace_consume(char ***l, char *p); /* In place ... */
int strv_env_replace_strdup(char ***l, const char *assignment);
int strv_env_replace_strdup_passthrough(char ***l, const char *assignment);
int strv_env_assign(char ***l, const char *key, const char *value);
+int strv_env_assignf(char ***l, const char *key, const char *valuef, ...) _printf_(3, 4);
int _strv_env_assign_many(char ***l, ...) _sentinel_;
#define strv_env_assign_many(l, ...) _strv_env_assign_many(l, __VA_ARGS__, NULL)
-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_;
+char* strv_env_get_n(char * const *l, const char *name, size_t k, ReplaceEnvFlags flags);
+static inline char* strv_env_get(char * const *x, const char *n) {
+ return strv_env_get_n(x, n, SIZE_MAX, 0);
+}
+
char *strv_env_pairs_get(char **l, const char *name) _pure_;
int getenv_bool(const char *p);
@@ -74,3 +78,7 @@ int setenv_systemd_exec_pid(bool update_only);
int getenv_path_list(const char *name, char ***ret_paths);
int getenv_steal_erase(const char *name, char **ret);
+
+int set_full_environment(char **env);
+
+int setenvf(const char *name, bool overwrite, const char *valuef, ...) _printf_(3,4);
diff --git a/src/libnm-systemd-shared/src/basic/errno-util.h b/src/libnm-systemd-shared/src/basic/errno-util.h
index 091f99c590..27804e6382 100644
--- a/src/libnm-systemd-shared/src/basic/errno-util.h
+++ b/src/libnm-systemd-shared/src/basic/errno-util.h
@@ -1,6 +1,7 @@
/* SPDX-License-Identifier: LGPL-2.1-or-later */
#pragma once
+#include <inttypes.h>
#include <stdlib.h>
#include <string.h>
@@ -73,6 +74,16 @@ static inline int RET_NERRNO(int ret) {
return ret;
}
+/* Collect possible errors in <acc>, so that the first error can be returned.
+ * Returns (possibly updated) <acc>. */
+#define RET_GATHER(acc, err) \
+ ({ \
+ int *__a = &(acc), __e = (err); \
+ if (*__a >= 0 && __e < 0) \
+ *__a = __e; \
+ *__a; \
+ })
+
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
@@ -84,12 +95,23 @@ static inline int errno_or_else(int fallback) {
return -abs(fallback);
}
+/* abs(3) says: Trying to take the absolute value of the most negative integer is not defined. */
+#define _DEFINE_ABS_WRAPPER(name) \
+ static inline bool ERRNO_IS_##name(intmax_t r) { \
+ if (r == INTMAX_MIN) \
+ return false; \
+ return ERRNO_IS_NEG_##name(-imaxabs(r)); \
+ }
+
+assert_cc(INT_MAX <= INTMAX_MAX);
+
/* For send()/recv() or read()/write(). */
-static inline bool ERRNO_IS_TRANSIENT(int r) {
- return IN_SET(abs(r),
- EAGAIN,
- EINTR);
+static inline bool ERRNO_IS_NEG_TRANSIENT(intmax_t r) {
+ return IN_SET(r,
+ -EAGAIN,
+ -EINTR);
}
+_DEFINE_ABS_WRAPPER(TRANSIENT);
/* Hint #1: ENETUNREACH happens if we try to connect to "non-existing" special IP addresses, such as ::5.
*
@@ -98,79 +120,87 @@ static inline bool ERRNO_IS_TRANSIENT(int r) {
*
* 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);
+static inline bool ERRNO_IS_NEG_DISCONNECT(intmax_t r) {
+ return IN_SET(r,
+ -ECONNABORTED,
+ -ECONNREFUSED,
+ -ECONNRESET,
+ -EHOSTDOWN,
+ -EHOSTUNREACH,
+ -ENETDOWN,
+ -ENETRESET,
+ -ENETUNREACH,
+ -ENONET,
+ -ENOPROTOOPT,
+ -ENOTCONN,
+ -EPIPE,
+ -EPROTO,
+ -ESHUTDOWN,
+ -ETIMEDOUT);
}
+_DEFINE_ABS_WRAPPER(DISCONNECT);
/* 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) ||
- ERRNO_IS_TRANSIENT(r) ||
- abs(r) == EOPNOTSUPP;
+static inline bool ERRNO_IS_NEG_ACCEPT_AGAIN(intmax_t r) {
+ return ERRNO_IS_NEG_DISCONNECT(r) ||
+ ERRNO_IS_NEG_TRANSIENT(r) ||
+ r == -EOPNOTSUPP;
}
+_DEFINE_ABS_WRAPPER(ACCEPT_AGAIN);
/* 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);
+static inline bool ERRNO_IS_NEG_RESOURCE(intmax_t r) {
+ return IN_SET(r,
+ -EMFILE,
+ -ENFILE,
+ -ENOMEM);
}
+_DEFINE_ABS_WRAPPER(RESOURCE);
/* 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);
+static inline bool ERRNO_IS_NEG_NOT_SUPPORTED(intmax_t r) {
+ return IN_SET(r,
+ -EOPNOTSUPP,
+ -ENOTTY,
+ -ENOSYS,
+ -EAFNOSUPPORT,
+ -EPFNOSUPPORT,
+ -EPROTONOSUPPORT,
+ -ESOCKTNOSUPPORT);
}
+_DEFINE_ABS_WRAPPER(NOT_SUPPORTED);
/* Two different errors for access problems */
-static inline bool ERRNO_IS_PRIVILEGE(int r) {
- return IN_SET(abs(r),
- EACCES,
- EPERM);
+static inline bool ERRNO_IS_NEG_PRIVILEGE(intmax_t r) {
+ return IN_SET(r,
+ -EACCES,
+ -EPERM);
}
+_DEFINE_ABS_WRAPPER(PRIVILEGE);
/* Three different errors for "not enough disk space" */
-static inline bool ERRNO_IS_DISK_SPACE(int r) {
- return IN_SET(abs(r),
- ENOSPC,
- EDQUOT,
- EFBIG);
+static inline bool ERRNO_IS_NEG_DISK_SPACE(intmax_t r) {
+ return IN_SET(r,
+ -ENOSPC,
+ -EDQUOT,
+ -EFBIG);
}
+_DEFINE_ABS_WRAPPER(DISK_SPACE);
/* Three different errors for "this device does not quite exist" */
-static inline bool ERRNO_IS_DEVICE_ABSENT(int r) {
- return IN_SET(abs(r),
- ENODEV,
- ENXIO,
- ENOENT);
+static inline bool ERRNO_IS_NEG_DEVICE_ABSENT(intmax_t r) {
+ return IN_SET(r,
+ -ENODEV,
+ -ENXIO,
+ -ENOENT);
}
+_DEFINE_ABS_WRAPPER(DEVICE_ABSENT);
/* Quite often we want to handle cases where the backing FS doesn't support extended attributes at all and
* where it simply doesn't have the requested xattr the same way */
-static inline bool ERRNO_IS_XATTR_ABSENT(int r) {
- return abs(r) == ENODATA ||
- ERRNO_IS_NOT_SUPPORTED(r);
+static inline bool ERRNO_IS_NEG_XATTR_ABSENT(intmax_t r) {
+ return r == -ENODATA ||
+ ERRNO_IS_NEG_NOT_SUPPORTED(r);
}
+_DEFINE_ABS_WRAPPER(XATTR_ABSENT);
diff --git a/src/libnm-systemd-shared/src/basic/escape.c b/src/libnm-systemd-shared/src/basic/escape.c
index 6d2c1d4d66..29f8b9cd1b 100644
--- a/src/libnm-systemd-shared/src/basic/escape.c
+++ b/src/libnm-systemd-shared/src/basic/escape.c
@@ -184,7 +184,7 @@ int cunescape_one(const char *p, size_t length, char32_t *ret, bool *eight_bit,
}
case 'u': {
- /* C++11 style 16bit unicode */
+ /* C++11 style 16-bit unicode */
int a[4];
size_t i;
@@ -211,7 +211,7 @@ int cunescape_one(const char *p, size_t length, char32_t *ret, bool *eight_bit,
}
case 'U': {
- /* C++11 style 32bit unicode */
+ /* C++11 style 32-bit unicode */
int a[8];
size_t i;
@@ -474,6 +474,33 @@ char* octescape(const char *s, size_t len) {
return buf;
}
+char* decescape(const char *s, const char *bad, size_t len) {
+ char *buf, *t;
+
+ /* Escapes all chars in bad, in addition to \ and " chars, in \nnn decimal style escaping. */
+
+ assert(s || len == 0);
+
+ t = buf = new(char, len * 4 + 1);
+ if (!buf)
+ return NULL;
+
+ for (size_t i = 0; i < len; i++) {
+ uint8_t u = (uint8_t) s[i];
+
+ if (u < ' ' || u >= 127 || IN_SET(u, '\\', '"') || strchr(bad, u)) {
+ *(t++) = '\\';
+ *(t++) = '0' + (u / 100);
+ *(t++) = '0' + ((u / 10) % 10);
+ *(t++) = '0' + (u % 10);
+ } else
+ *(t++) = u;
+ }
+
+ *t = 0;
+ return buf;
+}
+
static char* strcpy_backslash_escaped(char *t, const char *s, const char *bad) {
assert(bad);
assert(t);
diff --git a/src/libnm-systemd-shared/src/basic/escape.h b/src/libnm-systemd-shared/src/basic/escape.h
index 318da6f220..65caf0dbcf 100644
--- a/src/libnm-systemd-shared/src/basic/escape.h
+++ b/src/libnm-systemd-shared/src/basic/escape.h
@@ -65,6 +65,7 @@ static inline char* xescape(const char *s, const char *bad) {
return xescape_full(s, bad, SIZE_MAX, 0);
}
char* octescape(const char *s, size_t len);
+char* decescape(const char *s, const char *bad, size_t len);
char* escape_non_printable_full(const char *str, size_t console_width, XEscapeFlags flags);
char* shell_escape(const char *s, const char *bad);
diff --git a/src/libnm-systemd-shared/src/basic/ether-addr-util.c b/src/libnm-systemd-shared/src/basic/ether-addr-util.c
index 7984ddf4ad..1eb2e70065 100644
--- a/src/libnm-systemd-shared/src/basic/ether-addr-util.c
+++ b/src/libnm-systemd-shared/src/basic/ether-addr-util.c
@@ -61,8 +61,8 @@ void hw_addr_hash_func(const struct hw_addr_data *p, struct siphash *state) {
assert(p);
assert(state);
- siphash24_compress(&p->length, sizeof(p->length), state);
- siphash24_compress(p->bytes, p->length, state);
+ siphash24_compress_typesafe(p->length, state);
+ siphash24_compress_safe(p->bytes, p->length, state);
}
DEFINE_HASH_OPS(hw_addr_hash_ops, struct hw_addr_data, hw_addr_hash_func, hw_addr_compare);
@@ -108,7 +108,7 @@ int ether_addr_compare(const struct ether_addr *a, const struct ether_addr *b) {
}
static void ether_addr_hash_func(const struct ether_addr *p, struct siphash *state) {
- siphash24_compress(p, sizeof(struct ether_addr), state);
+ siphash24_compress_typesafe(*p, state);
}
DEFINE_HASH_OPS(ether_addr_hash_ops, struct ether_addr, ether_addr_hash_func, ether_addr_compare);
@@ -272,3 +272,11 @@ int parse_ether_addr(const char *s, struct ether_addr *ret) {
*ret = a.ether;
return 0;
}
+
+void ether_addr_mark_random(struct ether_addr *addr) {
+ assert(addr);
+
+ /* see eth_random_addr in the kernel */
+ addr->ether_addr_octet[0] &= 0xfe; /* clear multicast bit */
+ addr->ether_addr_octet[0] |= 0x02; /* set local assignment bit (IEEE802) */
+}
diff --git a/src/libnm-systemd-shared/src/basic/ether-addr-util.h b/src/libnm-systemd-shared/src/basic/ether-addr-util.h
index 83ed77d634..187e4ef583 100644
--- a/src/libnm-systemd-shared/src/basic/ether-addr-util.h
+++ b/src/libnm-systemd-shared/src/basic/ether-addr-util.h
@@ -113,3 +113,5 @@ static inline bool ether_addr_is_global(const struct ether_addr *addr) {
extern const struct hash_ops ether_addr_hash_ops;
extern const struct hash_ops ether_addr_hash_ops_free;
+
+void ether_addr_mark_random(struct ether_addr *addr);
diff --git a/src/libnm-systemd-shared/src/basic/extract-word.c b/src/libnm-systemd-shared/src/basic/extract-word.c
index 6781fb5560..2910a4cb60 100644
--- a/src/libnm-systemd-shared/src/basic/extract-word.c
+++ b/src/libnm-systemd-shared/src/basic/extract-word.c
@@ -275,11 +275,7 @@ int extract_many_words(const char **p, const char *separators, unsigned flags, .
r = extract_first_word(p, &l[c], separators, flags);
if (r < 0) {
- int j;
-
- for (j = 0; j < c; j++)
- free(l[j]);
-
+ free_many_charp(l, c);
return r;
}
diff --git a/src/libnm-systemd-shared/src/basic/fd-util.c b/src/libnm-systemd-shared/src/basic/fd-util.c
index a0e2f4eb8c..afdc05f2ab 100644
--- a/src/libnm-systemd-shared/src/basic/fd-util.c
+++ b/src/libnm-systemd-shared/src/basic/fd-util.c
@@ -94,11 +94,25 @@ void safe_close_pair(int p[static 2]) {
p[1] = safe_close(p[1]);
}
-void close_many(const int fds[], size_t n_fd) {
- assert(fds || n_fd <= 0);
+void close_many(const int fds[], size_t n_fds) {
+ assert(fds || n_fds == 0);
- for (size_t i = 0; i < n_fd; i++)
- safe_close(fds[i]);
+ FOREACH_ARRAY(fd, fds, n_fds)
+ safe_close(*fd);
+}
+
+void close_many_unset(int fds[], size_t n_fds) {
+ assert(fds || n_fds == 0);
+
+ FOREACH_ARRAY(fd, fds, n_fds)
+ *fd = safe_close(*fd);
+}
+
+void close_many_and_free(int *fds, size_t n_fds) {
+ assert(fds || n_fds == 0);
+
+ close_many(fds, n_fds);
+ free(fds);
}
int fclose_nointr(FILE *f) {
@@ -158,6 +172,19 @@ int fd_nonblock(int fd, bool nonblock) {
return RET_NERRNO(fcntl(fd, F_SETFL, nflags));
}
+int stdio_disable_nonblock(void) {
+ int ret = 0;
+
+ /* stdin/stdout/stderr really should have O_NONBLOCK, which would confuse apps if left on, as
+ * write()s might unexpectedly fail with EAGAIN. */
+
+ RET_GATHER(ret, fd_nonblock(STDIN_FILENO, false));
+ RET_GATHER(ret, fd_nonblock(STDOUT_FILENO, false));
+ RET_GATHER(ret, fd_nonblock(STDERR_FILENO, false));
+
+ return ret;
+}
+
int fd_cloexec(int fd, bool cloexec) {
int flags, nflags;
@@ -176,32 +203,32 @@ int fd_cloexec(int fd, bool cloexec) {
#if 0 /* NM_IGNORED */
int fd_cloexec_many(const int fds[], size_t n_fds, bool cloexec) {
- int ret = 0, r;
+ int r = 0;
- assert(n_fds == 0 || fds);
+ assert(fds || n_fds == 0);
- for (size_t i = 0; i < n_fds; i++) {
- if (fds[i] < 0) /* Skip gracefully over already invalidated fds */
+ FOREACH_ARRAY(fd, fds, n_fds) {
+ if (*fd < 0) /* Skip gracefully over already invalidated fds */
continue;
- r = fd_cloexec(fds[i], cloexec);
- if (r < 0 && ret >= 0) /* Continue going, but return first error */
- ret = r;
- else
- ret = 1; /* report if we did anything */
+ RET_GATHER(r, fd_cloexec(*fd, cloexec));
+
+ if (r >= 0)
+ r = 1; /* report if we did anything */
}
- return ret;
+ return r;
}
-_pure_ static bool fd_in_set(int fd, const int fdset[], size_t n_fdset) {
- assert(n_fdset == 0 || fdset);
+static bool fd_in_set(int fd, const int fds[], size_t n_fds) {
+ assert(fd >= 0);
+ assert(fds || n_fds == 0);
- for (size_t i = 0; i < n_fdset; i++) {
- if (fdset[i] < 0)
+ FOREACH_ARRAY(i, fds, n_fds) {
+ if (*i < 0)
continue;
- if (fdset[i] == fd)
+ if (*i == fd)
return true;
}
@@ -232,7 +259,7 @@ int get_max_fd(void) {
static int close_all_fds_frugal(const int except[], size_t n_except) {
int max_fd, r = 0;
- assert(n_except == 0 || except);
+ assert(except || n_except == 0);
/* This is the inner fallback core of close_all_fds(). This never calls malloc() or opendir() or so
* and hence is safe to be called in signal handler context. Most users should call close_all_fds(),
@@ -247,8 +274,7 @@ static int close_all_fds_frugal(const int except[], size_t n_except) {
* spin the CPU for a long time. */
if (max_fd > MAX_FD_LOOP_LIMIT)
return log_debug_errno(SYNTHETIC_ERRNO(EPERM),
- "Refusing to loop over %d potential fds.",
- max_fd);
+ "Refusing to loop over %d potential fds.", max_fd);
for (int fd = 3; fd >= 0; fd = fd < max_fd ? fd + 1 : -EBADF) {
int q;
@@ -257,8 +283,8 @@ static int close_all_fds_frugal(const int except[], size_t n_except) {
continue;
q = close_nointr(fd);
- if (q < 0 && q != -EBADF && r >= 0)
- r = q;
+ if (q != -EBADF)
+ RET_GATHER(r, q);
}
return r;
@@ -589,7 +615,7 @@ int move_fd(int from, int to, int cloexec) {
if (fl < 0)
return -errno;
- cloexec = !!(fl & FD_CLOEXEC);
+ cloexec = FLAGS_SET(fl, FD_CLOEXEC);
}
r = dup3(from, to, cloexec ? O_CLOEXEC : 0);
@@ -647,7 +673,7 @@ int rearrange_stdio(int original_input_fd, int original_output_fd, int original_
original_output_fd,
original_error_fd },
null_fd = -EBADF, /* If we open /dev/null, we store the fd to it here */
- copy_fd[3] = { -EBADF, -EBADF, -EBADF }, /* This contains all fds we duplicate here
+ copy_fd[3] = EBADF_TRIPLET, /* This contains all fds we duplicate here
* temporarily, and hence need to close at the end. */
r;
bool null_readable, null_writable;
@@ -745,8 +771,7 @@ finish:
safe_close_above_stdio(original_error_fd);
/* Close the copies we moved > 2 */
- for (int i = 0; i < 3; i++)
- safe_close(copy_fd[i]);
+ close_many(copy_fd, 3);
/* Close our null fd, if it's > 2 */
safe_close_above_stdio(null_fd);
@@ -756,9 +781,10 @@ finish:
#endif /* NM_IGNORED */
int fd_reopen(int fd, int flags) {
- int new_fd, r;
+ int r;
assert(fd >= 0 || fd == AT_FDCWD);
+ assert(!FLAGS_SET(flags, O_CREAT));
/* 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.
@@ -782,19 +808,12 @@ int fd_reopen(int fd, int flags) {
* the same way as the non-O_DIRECTORY case. */
return -ELOOP;
- if (FLAGS_SET(flags, O_DIRECTORY) || fd == AT_FDCWD) {
+ if (FLAGS_SET(flags, O_DIRECTORY) || fd == AT_FDCWD)
/* If we shall reopen the fd as directory we can just go via "." and thus bypass the whole
* magic /proc/ directory, and make ourselves independent of that being mounted. */
- new_fd = openat(fd, ".", flags | O_DIRECTORY);
- if (new_fd < 0)
- return -errno;
-
- return new_fd;
- }
+ return RET_NERRNO(openat(fd, ".", flags | O_DIRECTORY));
- assert(fd >= 0);
-
- new_fd = open(FORMAT_PROC_FD_PATH(fd), flags);
+ int new_fd = open(FORMAT_PROC_FD_PATH(fd), flags);
if (new_fd < 0) {
if (errno != ENOENT)
return -errno;
@@ -811,7 +830,6 @@ int fd_reopen(int fd, int flags) {
return new_fd;
}
-#if 0 /* NM_IGNORED */
int fd_reopen_condition(
int fd,
int flags,
@@ -821,6 +839,7 @@ int fd_reopen_condition(
int r, new_fd;
assert(fd >= 0);
+ assert(!FLAGS_SET(flags, O_CREAT));
/* Invokes fd_reopen(fd, flags), but only if the existing F_GETFL flags don't match the specified
* flags (masked by the specified mask). This is useful for converting O_PATH fds into real fds if
@@ -843,6 +862,7 @@ int fd_reopen_condition(
return new_fd;
}
+#if 0 /* NM_IGNORED */
int fd_is_opath(int fd) {
int r;
@@ -901,75 +921,86 @@ int fd_get_diskseq(int fd, uint64_t *ret) {
}
int path_is_root_at(int dir_fd, const char *path) {
- STRUCT_NEW_STATX_DEFINE(st);
- STRUCT_NEW_STATX_DEFINE(pst);
- _cleanup_close_ int fd = -EBADF;
- int r;
+ _cleanup_close_ int fd = -EBADF, pfd = -EBADF;
assert(dir_fd >= 0 || dir_fd == AT_FDCWD);
if (!isempty(path)) {
- fd = openat(dir_fd, path, O_PATH|O_CLOEXEC);
+ fd = openat(dir_fd, path, O_PATH|O_DIRECTORY|O_CLOEXEC);
if (fd < 0)
- return -errno;
+ return errno == ENOTDIR ? false : -errno;
dir_fd = fd;
}
- r = statx_fallback(dir_fd, ".", 0, STATX_TYPE|STATX_INO|STATX_MNT_ID, &st.sx);
- if (r == -ENOTDIR)
- return false;
+ pfd = openat(dir_fd, "..", O_PATH|O_DIRECTORY|O_CLOEXEC);
+ if (pfd < 0)
+ return errno == ENOTDIR ? false : -errno;
+
+ /* Even if the parent directory has the same inode, the fd may not point to the root directory "/",
+ * and we also need to check that the mount ids are the same. Otherwise, a construct like the
+ * following could be used to trick us:
+ *
+ * $ mkdir /tmp/x /tmp/x/y
+ * $ mount --bind /tmp/x /tmp/x/y
+ */
+
+ return fds_are_same_mount(dir_fd, pfd);
+}
+
+int fds_are_same_mount(int fd1, int fd2) {
+ STRUCT_NEW_STATX_DEFINE(st1);
+ STRUCT_NEW_STATX_DEFINE(st2);
+ int r;
+
+ assert(fd1 >= 0);
+ assert(fd2 >= 0);
+
+ r = statx_fallback(fd1, "", AT_EMPTY_PATH, STATX_TYPE|STATX_INO|STATX_MNT_ID, &st1.sx);
if (r < 0)
return r;
- r = statx_fallback(dir_fd, "..", 0, STATX_TYPE|STATX_INO|STATX_MNT_ID, &pst.sx);
+ r = statx_fallback(fd2, "", AT_EMPTY_PATH, STATX_TYPE|STATX_INO|STATX_MNT_ID, &st2.sx);
if (r < 0)
return r;
/* First, compare inode. If these are different, the fd does not point to the root directory "/". */
- if (!statx_inode_same(&st.sx, &pst.sx))
+ if (!statx_inode_same(&st1.sx, &st2.sx))
return false;
- /* Even if the parent directory has the same inode, the fd may not point to the root directory "/",
- * and we also need to check that the mount ids are the same. Otherwise, a construct like the
- * following could be used to trick us:
- *
- * $ mkdir /tmp/x /tmp/x/y
- * $ mount --bind /tmp/x /tmp/x/y
- *
- * Note, statx() does not provide the mount ID and path_get_mnt_id_at() does not work when an old
- * kernel is used without /proc mounted. In that case, let's assume that we do not have such spurious
- * mount points in an early boot stage, and silently skip the following check. */
+ /* Note, statx() does not provide the mount ID and path_get_mnt_id_at() does not work when an old
+ * kernel is used. In that case, let's assume that we do not have such spurious mount points in an
+ * early boot stage, and silently skip the following check. */
- if (!FLAGS_SET(st.nsx.stx_mask, STATX_MNT_ID)) {
+ if (!FLAGS_SET(st1.nsx.stx_mask, STATX_MNT_ID)) {
int mntid;
- r = path_get_mnt_id_at(dir_fd, "", &mntid);
- if (r == -ENOSYS)
+ r = path_get_mnt_id_at_fallback(fd1, "", &mntid);
+ if (ERRNO_IS_NEG_NOT_SUPPORTED(r))
return true; /* skip the mount ID check */
if (r < 0)
return r;
assert(mntid >= 0);
- st.nsx.stx_mnt_id = mntid;
- st.nsx.stx_mask |= STATX_MNT_ID;
+ st1.nsx.stx_mnt_id = mntid;
+ st1.nsx.stx_mask |= STATX_MNT_ID;
}
- if (!FLAGS_SET(pst.nsx.stx_mask, STATX_MNT_ID)) {
+ if (!FLAGS_SET(st2.nsx.stx_mask, STATX_MNT_ID)) {
int mntid;
- r = path_get_mnt_id_at(dir_fd, "..", &mntid);
- if (r == -ENOSYS)
+ r = path_get_mnt_id_at_fallback(fd2, "", &mntid);
+ if (ERRNO_IS_NEG_NOT_SUPPORTED(r))
return true; /* skip the mount ID check */
if (r < 0)
return r;
assert(mntid >= 0);
- pst.nsx.stx_mnt_id = mntid;
- pst.nsx.stx_mask |= STATX_MNT_ID;
+ st2.nsx.stx_mnt_id = mntid;
+ st2.nsx.stx_mask |= STATX_MNT_ID;
}
- return statx_mount_same(&st.nsx, &pst.nsx);
+ return statx_mount_same(&st1.nsx, &st2.nsx);
}
const char *accmode_to_string(int flags) {
@@ -984,4 +1015,12 @@ const char *accmode_to_string(int flags) {
return NULL;
}
}
+
+char *format_proc_pid_fd_path(char buf[static PROC_PID_FD_PATH_MAX], pid_t pid, int fd) {
+ assert(buf);
+ assert(fd >= 0);
+ assert(pid >= 0);
+ assert_se(snprintf_ok(buf, PROC_PID_FD_PATH_MAX, "/proc/" PID_FMT "/fd/%i", pid == 0 ? getpid_cached() : pid, fd));
+ return buf;
+}
#endif /* NM_IGNORED */
diff --git a/src/libnm-systemd-shared/src/basic/fd-util.h b/src/libnm-systemd-shared/src/basic/fd-util.h
index c870a1b899..183266513a 100644
--- a/src/libnm-systemd-shared/src/basic/fd-util.h
+++ b/src/libnm-systemd-shared/src/basic/fd-util.h
@@ -16,7 +16,10 @@
/* 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)
-#define PIPE_EBADF { -EBADF, -EBADF }
+
+/* Useful helpers for initializing pipe(), socketpair() or stdio fd arrays */
+#define EBADF_PAIR { -EBADF, -EBADF }
+#define EBADF_TRIPLET { -EBADF, -EBADF, -EBADF }
int close_nointr(int fd);
int safe_close(int fd);
@@ -29,7 +32,9 @@ static inline int safe_close_above_stdio(int fd) {
return safe_close(fd);
}
-void close_many(const int fds[], size_t n_fd);
+void close_many(const int fds[], size_t n_fds);
+void close_many_unset(int fds[], size_t n_fds);
+void close_many_and_free(int *fds, size_t n_fds);
int fclose_nointr(FILE *f);
FILE* safe_fclose(FILE *f);
@@ -47,6 +52,11 @@ static inline void fclosep(FILE **f) {
safe_fclose(*f);
}
+static inline void* close_fd_ptr(void *p) {
+ safe_close(PTR_TO_FD(p));
+ return NULL;
+}
+
DEFINE_TRIVIAL_CLEANUP_FUNC_FULL(FILE*, pclose, NULL);
DEFINE_TRIVIAL_CLEANUP_FUNC_FULL(DIR*, closedir, NULL);
@@ -57,6 +67,8 @@ DEFINE_TRIVIAL_CLEANUP_FUNC_FULL(DIR*, closedir, NULL);
#define _cleanup_close_pair_ _cleanup_(close_pairp)
int fd_nonblock(int fd, bool nonblock);
+int stdio_disable_nonblock(void);
+
int fd_cloexec(int fd, bool cloexec);
int fd_cloexec_many(const int fds[], size_t n_fds, bool cloexec);
@@ -102,6 +114,9 @@ int read_nr_open(void);
int fd_get_diskseq(int fd, uint64_t *ret);
int path_is_root_at(int dir_fd, const char *path);
+static inline int path_is_root(const char *path) {
+ return path_is_root_at(AT_FDCWD, path);
+}
static inline int dir_fd_is_root(int dir_fd) {
return path_is_root_at(dir_fd, NULL);
}
@@ -109,6 +124,8 @@ static inline int dir_fd_is_root_or_cwd(int dir_fd) {
return dir_fd == AT_FDCWD ? true : path_is_root_at(dir_fd, NULL);
}
+int fds_are_same_mount(int fd1, int fd2);
+
/* The maximum length a buffer for a /proc/self/fd/<fd> path needs */
#define PROC_FD_PATH_MAX \
(STRLEN("/proc/self/fd/") + DECIMAL_STR_MAX(int))
@@ -123,6 +140,16 @@ static inline char *format_proc_fd_path(char buf[static PROC_FD_PATH_MAX], int f
#define FORMAT_PROC_FD_PATH(fd) \
format_proc_fd_path((char[PROC_FD_PATH_MAX]) {}, (fd))
+/* The maximum length a buffer for a /proc/<pid>/fd/<fd> path needs */
+#define PROC_PID_FD_PATH_MAX \
+ (STRLEN("/proc//fd/") + DECIMAL_STR_MAX(pid_t) + DECIMAL_STR_MAX(int))
+
+char *format_proc_pid_fd_path(char buf[static PROC_PID_FD_PATH_MAX], pid_t pid, int fd);
+
+/* Kinda the same as FORMAT_PROC_FD_PATH(), but goes by PID rather than "self" symlink */
+#define FORMAT_PROC_PID_FD_PATH(pid, fd) \
+ format_proc_pid_fd_path((char[PROC_PID_FD_PATH_MAX]) {}, (pid), (fd))
+
const char *accmode_to_string(int flags);
/* Like ASSERT_PTR, but for fds */
diff --git a/src/libnm-systemd-shared/src/basic/fileio.c b/src/libnm-systemd-shared/src/basic/fileio.c
index 908a030911..7ab29816fe 100644
--- a/src/libnm-systemd-shared/src/basic/fileio.c
+++ b/src/libnm-systemd-shared/src/basic/fileio.c
@@ -30,10 +30,13 @@
#include "stdio-util.h"
#include "string-util.h"
#include "sync-util.h"
+#include "terminal-util.h"
#include "tmpfile-util.h"
/* The maximum size of the file we'll read in one go in read_full_file() (64M). */
#define READ_FULL_BYTES_MAX (64U*1024U*1024U - 1U)
+/* Used when a size is specified for read_full_file() with READ_FULL_FILE_UNBASE64 or _UNHEX */
+#define READ_FULL_FILE_ENCODED_STRING_AMPLIFICATION_BOUNDARY 3
/* The maximum size of virtual files (i.e. procfs, sysfs, and other virtual "API" files) we'll read in one go
* in read_virtual_file(). Note that this limit is different (and much lower) than the READ_FULL_BYTES_MAX
@@ -200,6 +203,19 @@ int write_string_stream_ts(
return 0;
}
+static mode_t write_string_file_flags_to_mode(WriteStringFileFlags flags) {
+
+ /* We support three different modes, that are the ones that really make sense for text files like this:
+ *
+ * → 0600 (i.e. root-only)
+ * → 0444 (i.e. read-only)
+ * → 0644 (i.e. writable for root, readable for everyone else)
+ */
+
+ return FLAGS_SET(flags, WRITE_STRING_FILE_MODE_0600) ? 0600 :
+ FLAGS_SET(flags, WRITE_STRING_FILE_MODE_0444) ? 0444 : 0644;
+}
+
static int write_string_file_atomic_at(
int dir_fd,
const char *fn,
@@ -225,7 +241,7 @@ static int write_string_file_atomic_at(
if (r < 0)
goto fail;
- r = fchmod_umask(fileno(f), FLAGS_SET(flags, WRITE_STRING_FILE_MODE_0600) ? 0600 : 0644);
+ r = fchmod_umask(fileno(f), write_string_file_flags_to_mode(flags));
if (r < 0)
goto fail;
@@ -288,7 +304,7 @@ int write_string_file_ts_at(
(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_SUPPRESS_REDUNDANT_VIRTUAL) ? O_RDWR : O_WRONLY),
- (FLAGS_SET(flags, WRITE_STRING_FILE_MODE_0600) ? 0600 : 0666));
+ write_string_file_flags_to_mode(flags));
if (fd < 0) {
r = -errno;
goto fail;
@@ -576,7 +592,7 @@ int read_full_stream_full(
size_t *ret_size) {
_cleanup_free_ char *buf = NULL;
- size_t n, n_next = 0, l;
+ size_t n, n_next = 0, l, expected_decoded_size = size;
int fd, r;
assert(f);
@@ -587,6 +603,13 @@ int read_full_stream_full(
if (offset != UINT64_MAX && offset > LONG_MAX) /* fseek() can only deal with "long" offsets */
return -ERANGE;
+ if ((flags & (READ_FULL_FILE_UNBASE64 | READ_FULL_FILE_UNHEX)) != 0) {
+ if (size <= SIZE_MAX / READ_FULL_FILE_ENCODED_STRING_AMPLIFICATION_BOUNDARY)
+ size *= READ_FULL_FILE_ENCODED_STRING_AMPLIFICATION_BOUNDARY;
+ else
+ size = SIZE_MAX;
+ }
+
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 */
@@ -711,6 +734,11 @@ int read_full_stream_full(
explicit_bzero_safe(buf, n);
free_and_replace(buf, decoded);
n = l = decoded_size;
+
+ if (FLAGS_SET(flags, READ_FULL_FILE_FAIL_WHEN_LARGER) && l > expected_decoded_size) {
+ r = -E2BIG;
+ goto finalize;
+ }
}
if (!ret_size) {
@@ -1057,7 +1085,9 @@ int fdopen_independent(int fd, const char *mode, FILE **ret) {
if (mode_flags < 0)
return mode_flags;
- copy_fd = fd_reopen(fd, mode_flags);
+ /* Flags returned by fopen_mode_to_flags might contain O_CREAT, but it doesn't make sense for fd_reopen
+ * since we're working on an existing fd anyway. Let's drop it here to avoid triggering assertion. */
+ copy_fd = fd_reopen(fd, mode_flags & ~O_CREAT);
if (copy_fd < 0)
return copy_fd;
@@ -1069,123 +1099,171 @@ int fdopen_independent(int fd, const char *mode, FILE **ret) {
return 0;
}
-static int search_and_fopen_internal(
+static int search_and_open_internal(
const char *path,
- const char *mode,
+ int mode, /* if ret_fd is NULL this is an [FRWX]_OK mode for access(), otherwise an open mode for open() */
const char *root,
char **search,
- FILE **ret,
+ int *ret_fd,
char **ret_path) {
+ int r;
+
+ assert(!ret_fd || !FLAGS_SET(mode, O_CREAT)); /* We don't support O_CREAT for this */
assert(path);
- assert(mode);
- assert(ret);
+
+ if (path_is_absolute(path)) {
+ _cleanup_close_ int fd = -EBADF;
+
+ if (ret_fd)
+ /* We only specify 0777 here to appease static analyzers, it's never used since we
+ * don't support O_CREAT here */
+ r = fd = RET_NERRNO(open(path, mode, 0777));
+ else
+ r = RET_NERRNO(access(path, mode));
+ if (r < 0)
+ return r;
+
+ if (ret_path) {
+ r = path_simplify_alloc(path, ret_path);
+ if (r < 0)
+ return r;
+ }
+
+ if (ret_fd)
+ *ret_fd = TAKE_FD(fd);
+
+ return 0;
+ }
if (!path_strv_resolve_uniq(search, root))
return -ENOMEM;
STRV_FOREACH(i, search) {
+ _cleanup_close_ int fd = -EBADF;
_cleanup_free_ char *p = NULL;
- FILE *f;
p = path_join(root, *i, path);
if (!p)
return -ENOMEM;
- f = fopen(p, mode);
- if (f) {
+ if (ret_fd)
+ /* as above, 0777 is static analyzer appeasement */
+ r = fd = RET_NERRNO(open(p, mode, 0777));
+ else
+ r = RET_NERRNO(access(p, F_OK));
+ if (r >= 0) {
if (ret_path)
*ret_path = path_simplify(TAKE_PTR(p));
- *ret = f;
+ if (ret_fd)
+ *ret_fd = TAKE_FD(fd);
+
return 0;
}
-
- if (errno != ENOENT)
- return -errno;
+ if (r != -ENOENT)
+ return r;
}
return -ENOENT;
}
-int search_and_fopen(
- const char *filename,
- const char *mode,
+int search_and_open(
+ const char *path,
+ int mode,
const char *root,
- const char **search,
- FILE **ret,
+ char **search,
+ int *ret_fd,
char **ret_path) {
_cleanup_strv_free_ char **copy = NULL;
- assert(filename);
- assert(mode);
- assert(ret);
+ assert(path);
- if (path_is_absolute(filename)) {
- _cleanup_fclose_ FILE *f = NULL;
+ copy = strv_copy((char**) search);
+ if (!copy)
+ return -ENOMEM;
+
+ return search_and_open_internal(path, mode, root, copy, ret_fd, ret_path);
+}
+
+static int search_and_fopen_internal(
+ const char *path,
+ const char *mode,
+ const char *root,
+ char **search,
+ FILE **ret_file,
+ char **ret_path) {
+
+ _cleanup_free_ char *found_path = NULL;
+ _cleanup_close_ int fd = -EBADF;
+ int r;
+
+ assert(path);
+ assert(mode || !ret_file);
+
+ r = search_and_open(
+ path,
+ mode ? fopen_mode_to_flags(mode) : 0,
+ root,
+ search,
+ ret_file ? &fd : NULL,
+ ret_path ? &found_path : NULL);
+ if (r < 0)
+ return r;
- f = fopen(filename, mode);
+ if (ret_file) {
+ FILE *f = take_fdopen(&fd, mode);
if (!f)
return -errno;
- if (ret_path) {
- char *p;
+ *ret_file = f;
+ }
- p = strdup(filename);
- if (!p)
- return -ENOMEM;
+ if (ret_path)
+ *ret_path = TAKE_PTR(found_path);
- *ret_path = path_simplify(p);
- }
+ return 0;
+}
- *ret = TAKE_PTR(f);
- return 0;
- }
+int search_and_fopen(
+ const char *path,
+ const char *mode,
+ const char *root,
+ const char **search,
+ FILE **ret_file,
+ char **ret_path) {
+
+ _cleanup_strv_free_ char **copy = NULL;
+
+ assert(path);
+ assert(mode || !ret_file);
copy = strv_copy((char**) search);
if (!copy)
return -ENOMEM;
- return search_and_fopen_internal(filename, mode, root, copy, ret, ret_path);
+ return search_and_fopen_internal(path, mode, root, copy, ret_file, ret_path);
}
int search_and_fopen_nulstr(
- const char *filename,
+ const char *path,
const char *mode,
const char *root,
const char *search,
- FILE **ret,
+ FILE **ret_file,
char **ret_path) {
- _cleanup_strv_free_ char **s = NULL;
-
- if (path_is_absolute(filename)) {
- _cleanup_fclose_ FILE *f = NULL;
-
- f = fopen(filename, mode);
- if (!f)
- return -errno;
-
- if (ret_path) {
- char *p;
-
- p = strdup(filename);
- if (!p)
- return -ENOMEM;
-
- *ret_path = path_simplify(p);
- }
+ _cleanup_strv_free_ char **l = NULL;
- *ret = TAKE_PTR(f);
- return 0;
- }
+ assert(path);
+ assert(mode || !ret_file);
- s = strv_split_nulstr(search);
- if (!s)
+ l = strv_split_nulstr(search);
+ if (!l)
return -ENOMEM;
- return search_and_fopen_internal(filename, mode, root, s, ret, ret_path);
+ return search_and_fopen_internal(path, mode, root, l, ret_file, ret_path);
}
#endif /* NM_IGNORED */
@@ -1259,33 +1337,31 @@ int read_timestamp_file(const char *fn, usec_t *ret) {
}
#endif /* NM_IGNORED */
-int fputs_with_space(FILE *f, const char *s, const char *separator, bool *space) {
- int r;
-
+int fputs_with_separator(FILE *f, const char *s, const char *separator, bool *space) {
assert(s);
+ assert(space);
- /* 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. */
+ /* 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 (!separator)
+ separator = " ";
- if (*space) {
- r = fputs(separator, f);
- if (r < 0)
- return r;
- }
+ if (*space)
+ if (fputs(separator, f) < 0)
+ return -EIO;
- *space = true;
- }
+ *space = true;
+
+ if (fputs(s, f) < 0)
+ return -EIO;
- return fputs(s, f);
+ return 0;
}
#if 0 /* NM_IGNORED */
@@ -1406,7 +1482,7 @@ int read_line_full(FILE *f, size_t limit, ReadLineFlags flags, char **ret) {
* 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;
+ flags |= isatty_safe(fd) ? READ_LINE_IS_A_TTY : READ_LINE_NOT_A_TTY;
}
if (FLAGS_SET(flags, READ_LINE_IS_A_TTY))
break;
@@ -1437,6 +1513,36 @@ int read_line_full(FILE *f, size_t limit, ReadLineFlags flags, char **ret) {
return (int) count;
}
+int read_stripped_line(FILE *f, size_t limit, char **ret) {
+ _cleanup_free_ char *s = NULL;
+ int r;
+
+ assert(f);
+
+ r = read_line(f, limit, ret ? &s : NULL);
+ if (r < 0)
+ return r;
+
+ if (ret) {
+ const char *p;
+
+ p = strstrip(s);
+ if (p == s)
+ *ret = TAKE_PTR(s);
+ else {
+ char *copy;
+
+ copy = strdup(p);
+ if (!copy)
+ return -ENOMEM;
+
+ *ret = copy;
+ }
+ }
+
+ return r;
+}
+
int safe_fgetc(FILE *f, char *ret) {
int k;
diff --git a/src/libnm-systemd-shared/src/basic/fileio.h b/src/libnm-systemd-shared/src/basic/fileio.h
index 769bf394fd..03c3f3ff28 100644
--- a/src/libnm-systemd-shared/src/basic/fileio.h
+++ b/src/libnm-systemd-shared/src/basic/fileio.h
@@ -26,7 +26,8 @@ typedef enum {
WRITE_STRING_FILE_NOFOLLOW = 1 << 8,
WRITE_STRING_FILE_MKDIR_0755 = 1 << 9,
WRITE_STRING_FILE_MODE_0600 = 1 << 10,
- WRITE_STRING_FILE_SUPPRESS_REDUNDANT_VIRTUAL = 1 << 11,
+ WRITE_STRING_FILE_MODE_0444 = 1 << 11,
+ WRITE_STRING_FILE_SUPPRESS_REDUNDANT_VIRTUAL = 1 << 12,
/* 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()
@@ -129,8 +130,12 @@ static inline int fopen_unlocked(const char *path, const char *mode, FILE **ret)
int fdopen_independent(int fd, const char *mode, FILE **ret);
-int search_and_fopen(const char *path, const char *mode, const char *root, const char **search, FILE **ret, char **ret_path);
-int search_and_fopen_nulstr(const char *path, const char *mode, const char *root, const char *search, FILE **ret, char **ret_path);
+int search_and_open(const char *path, int mode, const char *root, char **search, int *ret_fd, char **ret_path);
+static inline int search_and_access(const char *path, int mode, const char *root, char**search, char **ret_path) {
+ return search_and_open(path, mode, root, search, NULL, ret_path);
+}
+int search_and_fopen(const char *path, const char *mode, const char *root, const char **search, FILE **ret_file, char **ret_path);
+int search_and_fopen_nulstr(const char *path, const char *mode, const char *root, const char *search, FILE **ret_file, char **ret_path);
int fflush_and_check(FILE *f);
int fflush_sync_and_check(FILE *f);
@@ -138,7 +143,7 @@ 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);
+int fputs_with_separator(FILE *f, const char *s, const char *separator, bool *space);
typedef enum ReadLineFlags {
READ_LINE_ONLY_NUL = 1 << 0,
@@ -162,6 +167,8 @@ 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 read_stripped_line(FILE *f, size_t limit, char **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);
diff --git a/src/libnm-systemd-shared/src/basic/fs-util.c b/src/libnm-systemd-shared/src/basic/fs-util.c
index 32fd849d34..8804c0c1a1 100644
--- a/src/libnm-systemd-shared/src/basic/fs-util.c
+++ b/src/libnm-systemd-shared/src/basic/fs-util.c
@@ -11,6 +11,7 @@
#include <unistd.h>
#include "alloc-util.h"
+#include "btrfs.h"
#include "dirent-util.h"
#include "fd-util.h"
#include "fileio.h"
@@ -292,8 +293,22 @@ int fchmod_umask(int fd, mode_t m) {
int fchmod_opath(int fd, mode_t m) {
/* 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. */
+ * O_PATH. The tool set we have is non-intuitive:
+ * - fchmod(2) only operates on open files (i. e., fds with an open file description);
+ * - fchmodat(2) does not have a flag arg like fchownat(2) does, so no way to pass AT_EMPTY_PATH;
+ * + it should not be confused with the libc fchmodat(3) interface, which adds 4th flag argument,
+ * but does not support AT_EMPTY_PATH (only supports AT_SYMLINK_NOFOLLOW);
+ * - fchmodat2(2) supports all the AT_* flags, but is still very recent.
+ *
+ * We try to use fchmodat2(), and, if it is not supported, resort
+ * to the /proc/self/fd dance. */
+
+ assert(fd >= 0);
+
+ if (fchmodat2(fd, "", m, AT_EMPTY_PATH) >= 0)
+ return 0;
+ if (!IN_SET(errno, ENOSYS, EPERM)) /* Some container managers block unknown syscalls with EPERM */
+ return -errno;
if (chmod(FORMAT_PROC_FD_PATH(fd), m) < 0) {
if (errno != ENOENT)
@@ -1108,6 +1123,16 @@ int xopenat(int dir_fd, const char *path, int open_flags, XOpenFlags xopen_flags
assert(dir_fd >= 0 || dir_fd == AT_FDCWD);
+ /* This is like openat(), but has a few tricks up its sleeves, extending behaviour:
+ *
+ * • O_DIRECTORY|O_CREAT is supported, which causes a directory to be created, and immediately
+ * opened. When used with the XO_SUBVOLUME flag this will even create a btrfs subvolume.
+ *
+ * • If O_CREAT is used with XO_LABEL, any created file will be immediately relabelled.
+ *
+ * • If the path is specified NULL or empty, behaves like fd_reopen().
+ */
+
if (isempty(path)) {
assert(!FLAGS_SET(open_flags, O_CREAT|O_EXCL));
return fd_reopen(dir_fd, open_flags & ~O_NOFOLLOW);
@@ -1120,7 +1145,10 @@ int xopenat(int dir_fd, const char *path, int open_flags, XOpenFlags xopen_flags
}
if (FLAGS_SET(open_flags, O_DIRECTORY|O_CREAT)) {
- r = RET_NERRNO(mkdirat(dir_fd, path, mode));
+ if (FLAGS_SET(xopen_flags, XO_SUBVOLUME))
+ r = btrfs_subvol_make_fallback(dir_fd, path, mode);
+ else
+ r = RET_NERRNO(mkdirat(dir_fd, path, mode));
if (r == -EEXIST) {
if (FLAGS_SET(open_flags, O_EXCL))
return -EEXIST;
@@ -1183,12 +1211,11 @@ int xopenat_lock(
int r;
assert(dir_fd >= 0 || dir_fd == AT_FDCWD);
- assert(path);
assert(IN_SET(operation & ~LOCK_NB, LOCK_EX, LOCK_SH));
/* POSIX/UNPOSIX locks don't work on directories (errno is set to -EBADF so let's return early with
* the same error here). */
- if (FLAGS_SET(open_flags, O_DIRECTORY) && locktype != LOCK_BSD)
+ if (FLAGS_SET(open_flags, O_DIRECTORY) && !IN_SET(locktype, LOCK_BSD, LOCK_NONE))
return -EBADF;
for (;;) {
diff --git a/src/libnm-systemd-shared/src/basic/fs-util.h b/src/libnm-systemd-shared/src/basic/fs-util.h
index a19836d138..1023ab73ca 100644
--- a/src/libnm-systemd-shared/src/basic/fs-util.h
+++ b/src/libnm-systemd-shared/src/basic/fs-util.h
@@ -133,7 +133,8 @@ int open_mkdir_at(int dirfd, const char *path, int flags, mode_t mode);
int openat_report_new(int dirfd, const char *pathname, int flags, mode_t mode, bool *ret_newly_created);
typedef enum XOpenFlags {
- XO_LABEL = 1 << 0,
+ XO_LABEL = 1 << 0,
+ XO_SUBVOLUME = 1 << 1,
} XOpenFlags;
int xopenat(int dir_fd, const char *path, int open_flags, XOpenFlags xopen_flags, mode_t mode);
diff --git a/src/libnm-systemd-shared/src/basic/glyph-util.c b/src/libnm-systemd-shared/src/basic/glyph-util.c
index 1ea8a645b2..1de60e105b 100644
--- a/src/libnm-systemd-shared/src/basic/glyph-util.c
+++ b/src/libnm-systemd-shared/src/basic/glyph-util.c
@@ -25,7 +25,7 @@ bool emoji_enabled(void) {
return cached_emoji_enabled;
}
-const char *special_glyph(SpecialGlyph code) {
+const char *special_glyph_full(SpecialGlyph code, bool force_utf) {
/* A list of a number of interesting unicode glyphs we can use to decorate our output. It's probably wise to be
* conservative here, and primarily stick to the glyphs defined in the eurlatgr font, so that display still
@@ -54,11 +54,12 @@ const char *special_glyph(SpecialGlyph code) {
[SPECIAL_GLYPH_CROSS_MARK] = "-",
[SPECIAL_GLYPH_LIGHT_SHADE] = "-",
[SPECIAL_GLYPH_DARK_SHADE] = "X",
+ [SPECIAL_GLYPH_FULL_BLOCK] = "#",
[SPECIAL_GLYPH_SIGMA] = "S",
- [SPECIAL_GLYPH_ARROW_LEFT] = "<-",
- [SPECIAL_GLYPH_ARROW_RIGHT] = "->",
[SPECIAL_GLYPH_ARROW_UP] = "^",
[SPECIAL_GLYPH_ARROW_DOWN] = "v",
+ [SPECIAL_GLYPH_ARROW_LEFT] = "<-",
+ [SPECIAL_GLYPH_ARROW_RIGHT] = "->",
[SPECIAL_GLYPH_ELLIPSIS] = "...",
[SPECIAL_GLYPH_EXTERNAL_LINK] = "[LNK]",
[SPECIAL_GLYPH_ECSTATIC_SMILEY] = ":-]",
@@ -73,6 +74,7 @@ const char *special_glyph(SpecialGlyph code) {
[SPECIAL_GLYPH_RECYCLING] = "~",
[SPECIAL_GLYPH_DOWNLOAD] = "\\",
[SPECIAL_GLYPH_SPARKLES] = "*",
+ [SPECIAL_GLYPH_LOW_BATTERY] = "!",
[SPECIAL_GLYPH_WARNING_SIGN] = "!",
},
@@ -98,6 +100,7 @@ const char *special_glyph(SpecialGlyph code) {
[SPECIAL_GLYPH_CROSS_MARK] = u8"✗", /* actually called: BALLOT X */
[SPECIAL_GLYPH_LIGHT_SHADE] = u8"░",
[SPECIAL_GLYPH_DARK_SHADE] = u8"▒",
+ [SPECIAL_GLYPH_FULL_BLOCK] = u8"█",
[SPECIAL_GLYPH_SIGMA] = u8"Σ",
[SPECIAL_GLYPH_ARROW_UP] = u8"↑", /* actually called: UPWARDS ARROW */
[SPECIAL_GLYPH_ARROW_DOWN] = u8"↓", /* actually called: DOWNWARDS ARROW */
@@ -131,7 +134,10 @@ const char *special_glyph(SpecialGlyph code) {
[SPECIAL_GLYPH_RECYCLING] = u8"♻️", /* actually called: UNIVERSAL RECYCLNG SYMBOL */
[SPECIAL_GLYPH_DOWNLOAD] = u8"⤵️", /* actually called: RIGHT ARROW CURVING DOWN */
[SPECIAL_GLYPH_SPARKLES] = u8"✨",
+ [SPECIAL_GLYPH_LOW_BATTERY] = u8"🪫",
[SPECIAL_GLYPH_WARNING_SIGN] = u8"⚠️",
+ [SPECIAL_GLYPH_COMPUTER_DISK] = u8"💽",
+ [SPECIAL_GLYPH_WORLD] = u8"🌍",
},
};
@@ -139,5 +145,5 @@ const char *special_glyph(SpecialGlyph code) {
return NULL;
assert(code < _SPECIAL_GLYPH_MAX);
- return draw_table[code >= _SPECIAL_GLYPH_FIRST_EMOJI ? emoji_enabled() : is_locale_utf8()][code];
+ return draw_table[force_utf || (code >= _SPECIAL_GLYPH_FIRST_EMOJI ? emoji_enabled() : is_locale_utf8())][code];
}
diff --git a/src/libnm-systemd-shared/src/basic/glyph-util.h b/src/libnm-systemd-shared/src/basic/glyph-util.h
index b64639622e..a7709976e1 100644
--- a/src/libnm-systemd-shared/src/basic/glyph-util.h
+++ b/src/libnm-systemd-shared/src/basic/glyph-util.h
@@ -22,14 +22,15 @@ typedef enum SpecialGlyph {
SPECIAL_GLYPH_MU,
SPECIAL_GLYPH_CHECK_MARK,
SPECIAL_GLYPH_CROSS_MARK,
- SPECIAL_GLYPH_ARROW_LEFT,
- SPECIAL_GLYPH_ARROW_RIGHT,
- SPECIAL_GLYPH_ARROW_UP,
- SPECIAL_GLYPH_ARROW_DOWN,
- SPECIAL_GLYPH_ELLIPSIS,
SPECIAL_GLYPH_LIGHT_SHADE,
SPECIAL_GLYPH_DARK_SHADE,
+ SPECIAL_GLYPH_FULL_BLOCK,
SPECIAL_GLYPH_SIGMA,
+ SPECIAL_GLYPH_ARROW_UP,
+ SPECIAL_GLYPH_ARROW_DOWN,
+ SPECIAL_GLYPH_ARROW_LEFT,
+ SPECIAL_GLYPH_ARROW_RIGHT,
+ SPECIAL_GLYPH_ELLIPSIS,
SPECIAL_GLYPH_EXTERNAL_LINK,
_SPECIAL_GLYPH_FIRST_EMOJI,
SPECIAL_GLYPH_ECSTATIC_SMILEY = _SPECIAL_GLYPH_FIRST_EMOJI,
@@ -44,15 +45,22 @@ typedef enum SpecialGlyph {
SPECIAL_GLYPH_RECYCLING,
SPECIAL_GLYPH_DOWNLOAD,
SPECIAL_GLYPH_SPARKLES,
+ SPECIAL_GLYPH_LOW_BATTERY,
SPECIAL_GLYPH_WARNING_SIGN,
+ SPECIAL_GLYPH_COMPUTER_DISK,
+ SPECIAL_GLYPH_WORLD,
_SPECIAL_GLYPH_MAX,
_SPECIAL_GLYPH_INVALID = -EINVAL,
} SpecialGlyph;
-const char *special_glyph(SpecialGlyph code) _const_;
-
bool emoji_enabled(void);
+const char *special_glyph_full(SpecialGlyph code, bool force_utf) _const_;
+
+static inline const char *special_glyph(SpecialGlyph code) {
+ return special_glyph_full(code, false);
+}
+
static inline const char *special_glyph_check_mark(bool b) {
return b ? special_glyph(SPECIAL_GLYPH_CHECK_MARK) : special_glyph(SPECIAL_GLYPH_CROSS_MARK);
}
diff --git a/src/libnm-systemd-shared/src/basic/hash-funcs.c b/src/libnm-systemd-shared/src/basic/hash-funcs.c
index eed30f0944..6dcc2f36d9 100644
--- a/src/libnm-systemd-shared/src/basic/hash-funcs.c
+++ b/src/libnm-systemd-shared/src/basic/hash-funcs.c
@@ -36,7 +36,7 @@ void path_hash_func(const char *q, struct siphash *state) {
/* if path is absolute, add one "/" to the hash. */
if (path_is_absolute(q))
- siphash24_compress("/", 1, state);
+ siphash24_compress_byte('/', state);
for (;;) {
const char *e;
@@ -71,7 +71,7 @@ DEFINE_HASH_OPS_FULL(path_hash_ops_free_free,
#endif /* NM_IGNORED */
void trivial_hash_func(const void *p, struct siphash *state) {
- siphash24_compress(&p, sizeof(p), state);
+ siphash24_compress_typesafe(p, state);
}
int trivial_compare_func(const void *a, const void *b) {
@@ -97,7 +97,7 @@ const struct hash_ops trivial_hash_ops_free_free = {
};
void uint64_hash_func(const uint64_t *p, struct siphash *state) {
- siphash24_compress(p, sizeof(uint64_t), state);
+ siphash24_compress_typesafe(*p, state);
}
int uint64_compare_func(const uint64_t *a, const uint64_t *b) {
@@ -109,7 +109,7 @@ 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);
+ siphash24_compress_typesafe(*p, state);
}
#endif
diff --git a/src/libnm-systemd-shared/src/basic/hash-funcs.h b/src/libnm-systemd-shared/src/basic/hash-funcs.h
index be64289252..3804e94d98 100644
--- a/src/libnm-systemd-shared/src/basic/hash-funcs.h
+++ b/src/libnm-systemd-shared/src/basic/hash-funcs.h
@@ -93,14 +93,14 @@ 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
+/* 32-bit values we can always just embed in the pointer itself, but in order to support 32-bit archs we need store 64-bit
* 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! */
+/* On some archs dev_t is 32-bit, and on others 64-bit. And sometimes it's 64-bit on 32-bit archs, and sometimes 32-bit on
+ * 64-bit archs. Yuck! */
#if SIZEOF_DEV_T != 8
void devt_hash_func(const dev_t *p, struct siphash *state);
#else
diff --git a/src/libnm-systemd-shared/src/basic/hashmap.c b/src/libnm-systemd-shared/src/basic/hashmap.c
index 356200cfcc..28d2efa0e2 100644
--- a/src/libnm-systemd-shared/src/basic/hashmap.c
+++ b/src/libnm-systemd-shared/src/basic/hashmap.c
@@ -23,6 +23,7 @@
#include "random-util.h"
#include "set.h"
#include "siphash24.h"
+#include "sort-util.h"
#include "string-util.h"
#include "strv.h"
@@ -176,9 +177,9 @@ struct _packed_ indirect_storage {
};
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. */
+ /* This gives us 39 bytes on 64-bit, or 35 bytes on 32-bit.
+ * That's room for 4 set_entries + 4 DIB bytes + 3 unused bytes on 64-bit,
+ * or 7 set_entries + 7 DIB bytes + 0 unused bytes on 32-bit. */
uint8_t storage[sizeof(struct indirect_storage)];
};
@@ -2112,3 +2113,54 @@ bool set_fnmatch(Set *include_patterns, Set *exclude_patterns, const char *needl
return set_fnmatch_one(include_patterns, needle);
}
+
+static int hashmap_entry_compare(
+ struct hashmap_base_entry * const *a,
+ struct hashmap_base_entry * const *b,
+ compare_func_t compare) {
+
+ assert(a && *a);
+ assert(b && *b);
+ assert(compare);
+
+ return compare((*a)->key, (*b)->key);
+}
+
+int _hashmap_dump_sorted(HashmapBase *h, void ***ret, size_t *ret_n) {
+ _cleanup_free_ struct hashmap_base_entry **entries = NULL;
+ Iterator iter;
+ unsigned idx;
+ size_t n = 0;
+
+ assert(ret);
+
+ if (_hashmap_size(h) == 0) {
+ *ret = NULL;
+ if (ret_n)
+ *ret_n = 0;
+ return 0;
+ }
+
+ /* We append one more element than needed so that the resulting array can be used as a strv. We
+ * don't count this entry in the returned size. */
+ entries = new(struct hashmap_base_entry*, _hashmap_size(h) + 1);
+ if (!entries)
+ return -ENOMEM;
+
+ HASHMAP_FOREACH_IDX(idx, h, iter)
+ entries[n++] = bucket_at(h, idx);
+
+ assert(n == _hashmap_size(h));
+ entries[n] = NULL;
+
+ typesafe_qsort_r(entries, n, hashmap_entry_compare, h->hash_ops->compare);
+
+ /* Reuse the array. */
+ FOREACH_ARRAY(e, entries, n)
+ *e = entry_value(h, *e);
+
+ *ret = (void**) TAKE_PTR(entries);
+ if (ret_n)
+ *ret_n = n;
+ return 0;
+}
diff --git a/src/libnm-systemd-shared/src/basic/hashmap.h b/src/libnm-systemd-shared/src/basic/hashmap.h
index 68d9b81cf2..233f1d7a1e 100644
--- a/src/libnm-systemd-shared/src/basic/hashmap.h
+++ b/src/libnm-systemd-shared/src/basic/hashmap.h
@@ -398,12 +398,28 @@ static inline char** ordered_hashmap_get_strv(OrderedHashmap *h) {
return _hashmap_get_strv(HASHMAP_BASE(h));
}
+int _hashmap_dump_sorted(HashmapBase *h, void ***ret, size_t *ret_n);
+static inline int hashmap_dump_sorted(Hashmap *h, void ***ret, size_t *ret_n) {
+ return _hashmap_dump_sorted(HASHMAP_BASE(h), ret, ret_n);
+}
+static inline int ordered_hashmap_dump_sorted(OrderedHashmap *h, void ***ret, size_t *ret_n) {
+ return _hashmap_dump_sorted(HASHMAP_BASE(h), ret, ret_n);
+}
+static inline int set_dump_sorted(Set *h, void ***ret, size_t *ret_n) {
+ return _hashmap_dump_sorted(HASHMAP_BASE(h), ret, ret_n);
+}
+
/*
* 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_BASE_FOREACH(e, h, i) \
+ for (Iterator i = ITERATOR_FIRST; _hashmap_iterate((h), &i, (void**)&(e), NULL); )
+#define HASHMAP_BASE_FOREACH(e, h) \
+ _HASHMAP_BASE_FOREACH(e, h, UNIQ_T(i, UNIQ))
+
#define _HASHMAP_FOREACH(e, h, i) \
for (Iterator i = ITERATOR_FIRST; hashmap_iterate((h), &i, (void**)&(e), NULL); )
#define HASHMAP_FOREACH(e, h) \
@@ -414,6 +430,11 @@ static inline char** ordered_hashmap_get_strv(OrderedHashmap *h) {
#define ORDERED_HASHMAP_FOREACH(e, h) \
_ORDERED_HASHMAP_FOREACH(e, h, UNIQ_T(i, UNIQ))
+#define _HASHMAP_BASE_FOREACH_KEY(e, k, h, i) \
+ for (Iterator i = ITERATOR_FIRST; _hashmap_iterate((h), &i, (void**)&(e), (const void**) &(k)); )
+#define HASHMAP_BASE_FOREACH_KEY(e, k, h) \
+ _HASHMAP_BASE_FOREACH_KEY(e, k, 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) \
diff --git a/src/libnm-systemd-shared/src/basic/hexdecoct.c b/src/libnm-systemd-shared/src/basic/hexdecoct.c
index d41d2ea079..41228520a5 100644
--- a/src/libnm-systemd-shared/src/basic/hexdecoct.c
+++ b/src/libnm-systemd-shared/src/basic/hexdecoct.c
@@ -116,7 +116,7 @@ int unhexmem_full(
const char *p,
size_t l,
bool secure,
- void **ret,
+ void **ret_data,
size_t *ret_len) {
_cleanup_free_ uint8_t *buf = NULL;
@@ -157,8 +157,8 @@ int unhexmem_full(
if (ret_len)
*ret_len = (size_t) (z - buf);
- if (ret)
- *ret = TAKE_PTR(buf);
+ if (ret_data)
+ *ret_data = TAKE_PTR(buf);
return 0;
}
@@ -557,12 +557,12 @@ int unbase64char(char c) {
offset += '9' - '0' + 1;
- if (c == '+')
+ if (IN_SET(c, '+', '-')) /* Support both the regular and the URL safe character set (see above) */
return offset;
offset++;
- if (c == '/')
+ if (IN_SET(c, '/', '_')) /* ditto */
return offset;
return -EINVAL;
@@ -772,7 +772,7 @@ int unbase64mem_full(
const char *p,
size_t l,
bool secure,
- void **ret,
+ void **ret_data,
size_t *ret_size) {
_cleanup_free_ uint8_t *buf = NULL;
@@ -860,8 +860,8 @@ int unbase64mem_full(
if (ret_size)
*ret_size = (size_t) (z - buf);
- if (ret)
- *ret = TAKE_PTR(buf);
+ if (ret_data)
+ *ret_data = TAKE_PTR(buf);
return 0;
}
diff --git a/src/libnm-systemd-shared/src/basic/hexdecoct.h b/src/libnm-systemd-shared/src/basic/hexdecoct.h
index 319b21a17c..0a10af3e16 100644
--- a/src/libnm-systemd-shared/src/basic/hexdecoct.h
+++ b/src/libnm-systemd-shared/src/basic/hexdecoct.h
@@ -18,9 +18,9 @@ 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);
+int unhexmem_full(const char *p, size_t l, bool secure, void **ret_data, size_t *ret_size);
+static inline int unhexmem(const char *p, void **ret_data, size_t *ret_size) {
+ return unhexmem_full(p, SIZE_MAX, false, ret_data, ret_size);
}
char base32hexchar(int x) _const_;
@@ -45,9 +45,9 @@ ssize_t base64_append(
size_t l,
size_t margin,
size_t 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);
+int unbase64mem_full(const char *p, size_t l, bool secure, void **ret_data, size_t *ret_size);
+static inline int unbase64mem(const char *p, void **ret_data, size_t *ret_size) {
+ return unbase64mem_full(p, SIZE_MAX, false, ret_data, ret_size);
}
void hexdump(FILE *f, const void *p, size_t s);
diff --git a/src/libnm-systemd-shared/src/basic/in-addr-util.c b/src/libnm-systemd-shared/src/basic/in-addr-util.c
index b863aec399..0c3923b0ef 100644
--- a/src/libnm-systemd-shared/src/basic/in-addr-util.c
+++ b/src/libnm-systemd-shared/src/basic/in-addr-util.c
@@ -736,10 +736,11 @@ int in_addr_mask(int family, union in_addr_union *addr, unsigned char prefixlen)
}
}
-int in4_addr_prefix_covers(
+int in4_addr_prefix_covers_full(
const struct in_addr *prefix,
unsigned char prefixlen,
- const struct in_addr *address) {
+ const struct in_addr *address,
+ unsigned char address_prefixlen) {
struct in_addr masked_prefix, masked_address;
int r;
@@ -747,6 +748,9 @@ int in4_addr_prefix_covers(
assert(prefix);
assert(address);
+ if (prefixlen > address_prefixlen)
+ return false;
+
masked_prefix = *prefix;
r = in4_addr_mask(&masked_prefix, prefixlen);
if (r < 0)
@@ -760,10 +764,11 @@ int in4_addr_prefix_covers(
return in4_addr_equal(&masked_prefix, &masked_address);
}
-int in6_addr_prefix_covers(
+int in6_addr_prefix_covers_full(
const struct in6_addr *prefix,
unsigned char prefixlen,
- const struct in6_addr *address) {
+ const struct in6_addr *address,
+ unsigned char address_prefixlen) {
struct in6_addr masked_prefix, masked_address;
int r;
@@ -771,6 +776,9 @@ int in6_addr_prefix_covers(
assert(prefix);
assert(address);
+ if (prefixlen > address_prefixlen)
+ return false;
+
masked_prefix = *prefix;
r = in6_addr_mask(&masked_prefix, prefixlen);
if (r < 0)
@@ -784,20 +792,21 @@ int in6_addr_prefix_covers(
return in6_addr_equal(&masked_prefix, &masked_address);
}
-int in_addr_prefix_covers(
+int in_addr_prefix_covers_full(
int family,
const union in_addr_union *prefix,
unsigned char prefixlen,
- const union in_addr_union *address) {
+ const union in_addr_union *address,
+ unsigned char address_prefixlen) {
assert(prefix);
assert(address);
switch (family) {
case AF_INET:
- return in4_addr_prefix_covers(&prefix->in, prefixlen, &address->in);
+ return in4_addr_prefix_covers_full(&prefix->in, prefixlen, &address->in, address_prefixlen);
case AF_INET6:
- return in6_addr_prefix_covers(&prefix->in6, prefixlen, &address->in6);
+ return in6_addr_prefix_covers_full(&prefix->in6, prefixlen, &address->in6, address_prefixlen);
default:
return -EAFNOSUPPORT;
}
@@ -922,12 +931,19 @@ int in_addr_prefix_from_string_auto_internal(
}
+void in_addr_hash_func(const union in_addr_union *u, int family, struct siphash *state) {
+ assert(u);
+ assert(state);
+
+ siphash24_compress(u->bytes, FAMILY_ADDRESS_SIZE(family), state);
+}
+
void in_addr_data_hash_func(const struct in_addr_data *a, struct siphash *state) {
assert(a);
assert(state);
- siphash24_compress(&a->family, sizeof(a->family), state);
- siphash24_compress(&a->address, FAMILY_ADDRESS_SIZE(a->family), state);
+ siphash24_compress_typesafe(a->family, state);
+ in_addr_hash_func(&a->address, a->family, state);
}
int in_addr_data_compare_func(const struct in_addr_data *x, const struct in_addr_data *y) {
@@ -960,7 +976,7 @@ void in6_addr_hash_func(const struct in6_addr *addr, struct siphash *state) {
assert(addr);
assert(state);
- siphash24_compress(addr, sizeof(*addr), state);
+ siphash24_compress_typesafe(*addr, state);
}
int in6_addr_compare_func(const struct in6_addr *a, const struct in6_addr *b) {
diff --git a/src/libnm-systemd-shared/src/basic/in-addr-util.h b/src/libnm-systemd-shared/src/basic/in-addr-util.h
index 200b9eb69d..9fae3cae45 100644
--- a/src/libnm-systemd-shared/src/basic/in-addr-util.h
+++ b/src/libnm-systemd-shared/src/basic/in-addr-util.h
@@ -144,9 +144,18 @@ int in4_addr_default_subnet_mask(const struct in_addr *addr, struct in_addr *mas
int in4_addr_mask(struct in_addr *addr, unsigned char prefixlen);
int in6_addr_mask(struct in6_addr *addr, unsigned char prefixlen);
int in_addr_mask(int family, union in_addr_union *addr, unsigned char prefixlen);
-int in4_addr_prefix_covers(const struct in_addr *prefix, unsigned char prefixlen, const struct in_addr *address);
-int in6_addr_prefix_covers(const struct in6_addr *prefix, unsigned char prefixlen, const struct in6_addr *address);
-int in_addr_prefix_covers(int family, const union in_addr_union *prefix, unsigned char prefixlen, const union in_addr_union *address);
+int in4_addr_prefix_covers_full(const struct in_addr *prefix, unsigned char prefixlen, const struct in_addr *address, unsigned char address_prefixlen);
+int in6_addr_prefix_covers_full(const struct in6_addr *prefix, unsigned char prefixlen, const struct in6_addr *address, unsigned char address_prefixlen);
+int in_addr_prefix_covers_full(int family, const union in_addr_union *prefix, unsigned char prefixlen, const union in_addr_union *address, unsigned char address_prefixlen);
+static inline int in4_addr_prefix_covers(const struct in_addr *prefix, unsigned char prefixlen, const struct in_addr *address) {
+ return in4_addr_prefix_covers_full(prefix, prefixlen, address, 32);
+}
+static inline int in6_addr_prefix_covers(const struct in6_addr *prefix, unsigned char prefixlen, const struct in6_addr *address) {
+ return in6_addr_prefix_covers_full(prefix, prefixlen, address, 128);
+}
+static inline int in_addr_prefix_covers(int family, const union in_addr_union *prefix, unsigned char prefixlen, const union in_addr_union *address) {
+ return in_addr_prefix_covers_full(family, prefix, prefixlen, address, family == AF_INET ? 32 : family == AF_INET6 ? 128 : 0);
+}
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);
@@ -176,6 +185,7 @@ static inline size_t FAMILY_ADDRESS_SIZE(int family) {
* See also oss-fuzz#11344. */
#define IN_ADDR_NULL ((union in_addr_union) { .in6 = {} })
+void in_addr_hash_func(const union in_addr_union *u, int family, struct siphash *state);
void in_addr_data_hash_func(const struct in_addr_data *a, struct siphash *state);
int in_addr_data_compare_func(const struct in_addr_data *x, const struct in_addr_data *y);
void in6_addr_hash_func(const struct in6_addr *addr, struct siphash *state);
@@ -186,6 +196,16 @@ extern const struct hash_ops in_addr_data_hash_ops_free;
extern const struct hash_ops in6_addr_hash_ops;
extern const struct hash_ops in6_addr_hash_ops_free;
+static inline void PTR_TO_IN4_ADDR(const void *p, struct in_addr *ret) {
+ assert(ret);
+ ret->s_addr = (uint32_t) ((uintptr_t) p);
+}
+
+static inline void* IN4_ADDR_TO_PTR(const struct in_addr *a) {
+ assert(a);
+ return (void*) ((uintptr_t) a->s_addr);
+}
+
#define IPV4_ADDRESS_FMT_STR "%u.%u.%u.%u"
#define IPV4_ADDRESS_FMT_VAL(address) \
be32toh((address).s_addr) >> 24, \
diff --git a/src/libnm-systemd-shared/src/basic/inotify-util.c b/src/libnm-systemd-shared/src/basic/inotify-util.c
index 59e03e620e..c748bf1bf0 100644
--- a/src/libnm-systemd-shared/src/basic/inotify-util.c
+++ b/src/libnm-systemd-shared/src/basic/inotify-util.c
@@ -6,6 +6,43 @@
#include "inotify-util.h"
#include "stat-util.h"
+bool inotify_event_next(
+ union inotify_event_buffer *buffer,
+ size_t size,
+ struct inotify_event **iterator,
+ int log_level) {
+
+ struct inotify_event *e;
+ size_t offset = 0;
+
+ assert(buffer);
+ assert(iterator);
+
+ if (*iterator) {
+ assert((uint8_t*) *iterator >= buffer->raw);
+ offset = (uint8_t*) *iterator - buffer->raw;
+ offset += offsetof(struct inotify_event, name) + (*iterator)->len;
+ }
+
+ if (size == offset)
+ return false; /* reached end of list */
+
+ if (size < offset ||
+ size - offset < offsetof(struct inotify_event, name)) {
+ log_full(log_level, "Received invalid inotify event, ignoring.");
+ return false;
+ }
+
+ e = CAST_ALIGN_PTR(struct inotify_event, buffer->raw + offset);
+ if (size - offset - offsetof(struct inotify_event, name) < e->len) {
+ log_full(log_level, "Received invalid inotify event, ignoring.");
+ return false;
+ }
+
+ *iterator = e;
+ return true;
+}
+
int inotify_add_watch_fd(int fd, int what, uint32_t mask) {
int wd, r;
diff --git a/src/libnm-systemd-shared/src/basic/inotify-util.h b/src/libnm-systemd-shared/src/basic/inotify-util.h
index 61951ff3e3..665fdacaa6 100644
--- a/src/libnm-systemd-shared/src/basic/inotify-util.h
+++ b/src/libnm-systemd-shared/src/basic/inotify-util.h
@@ -10,29 +10,27 @@
#define INOTIFY_EVENT_MAX (offsetof(struct inotify_event, name) + NAME_MAX + 1)
-#define _FOREACH_INOTIFY_EVENT(e, buffer, sz, log_level, start, end) \
- for (struct inotify_event \
- *start = &((buffer).ev), \
- *end = (struct inotify_event*) ((uint8_t*) start + (sz)), \
- *e = start; \
- (size_t) ((uint8_t*) end - (uint8_t*) e) >= sizeof(struct inotify_event) && \
- ((size_t) ((uint8_t*) end - (uint8_t*) e) >= sizeof(struct inotify_event) + e->len || \
- (log_full(log_level, "Received invalid inotify event, ignoring."), false)); \
- e = (struct inotify_event*) ((uint8_t*) e + sizeof(struct inotify_event) + e->len))
-
-#define _FOREACH_INOTIFY_EVENT_FULL(e, buffer, sz, log_level) \
- _FOREACH_INOTIFY_EVENT(e, buffer, sz, log_level, UNIQ_T(start, UNIQ), UNIQ_T(end, UNIQ))
+/* This evaluates arguments multiple times */
+#define FOREACH_INOTIFY_EVENT_FULL(e, buffer, sz, log_level) \
+ for (struct inotify_event *e = NULL; \
+ inotify_event_next(&buffer, sz, &e, log_level); )
#define FOREACH_INOTIFY_EVENT(e, buffer, sz) \
- _FOREACH_INOTIFY_EVENT_FULL(e, buffer, sz, LOG_DEBUG)
+ FOREACH_INOTIFY_EVENT_FULL(e, buffer, sz, LOG_DEBUG)
#define FOREACH_INOTIFY_EVENT_WARN(e, buffer, sz) \
- _FOREACH_INOTIFY_EVENT_FULL(e, buffer, sz, LOG_WARNING)
+ FOREACH_INOTIFY_EVENT_FULL(e, buffer, sz, LOG_WARNING)
union inotify_event_buffer {
struct inotify_event ev;
uint8_t raw[INOTIFY_EVENT_MAX];
};
+bool inotify_event_next(
+ union inotify_event_buffer *buffer,
+ size_t size,
+ struct inotify_event **iterator,
+ int log_level);
+
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);
diff --git a/src/libnm-systemd-shared/src/basic/io-util.c b/src/libnm-systemd-shared/src/basic/io-util.c
index 0c480091b2..abe61ed56c 100644
--- a/src/libnm-systemd-shared/src/basic/io-util.c
+++ b/src/libnm-systemd-shared/src/basic/io-util.c
@@ -7,7 +7,9 @@
#include <stdio.h>
#include <unistd.h>
+#include "errno-util.h"
#include "io-util.h"
+#include "iovec-util.h"
#include "string-util.h"
#include "time-util.h"
@@ -58,8 +60,7 @@ ssize_t loop_read(int fd, void *buf, size_t nbytes, bool do_poll) {
assert(fd >= 0);
- /* If called with nbytes == 0, let's call read() at least
- * once, to validate the operation */
+ /* If called with nbytes == 0, let's call read() at least once, to validate the operation */
if (nbytes > (size_t) SSIZE_MAX)
return -EINVAL;
@@ -111,13 +112,29 @@ int loop_read_exact(int fd, void *buf, size_t nbytes, bool do_poll) {
}
#if 0 /* NM_IGNORED */
-int loop_write(int fd, const void *buf, size_t nbytes, bool do_poll) {
- const uint8_t *p = ASSERT_PTR(buf);
+int loop_write_full(int fd, const void *buf, size_t nbytes, usec_t timeout) {
+ const uint8_t *p;
+ usec_t end;
+ int r;
assert(fd >= 0);
+ assert(buf || nbytes == 0);
+
+ if (nbytes == 0) {
+ static const dummy_t dummy[0];
+ assert_cc(sizeof(dummy) == 0);
+ p = (const void*) dummy; /* Some valid pointer, in case NULL was specified */
+ } else {
+ if (nbytes == SIZE_MAX)
+ nbytes = strlen(buf);
+ else if (_unlikely_(nbytes > (size_t) SSIZE_MAX))
+ return -EINVAL;
+
+ p = buf;
+ }
- if (_unlikely_(nbytes > (size_t) SSIZE_MAX))
- return -EINVAL;
+ /* When timeout is 0 or USEC_INFINITY this is not used. But we initialize it to a sensible value. */
+ end = timestamp_is_set(timeout) ? usec_add(now(CLOCK_MONOTONIC), timeout) : USEC_INFINITY;
do {
ssize_t k;
@@ -127,16 +144,31 @@ int loop_write(int fd, const void *buf, size_t nbytes, bool do_poll) {
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() */
+ if (errno != EAGAIN || timeout == 0)
+ return -errno;
- (void) fd_wait_for_event(fd, POLLOUT, USEC_INFINITY);
- continue;
+ usec_t wait_for;
+
+ if (timeout == USEC_INFINITY)
+ wait_for = USEC_INFINITY;
+ else {
+ usec_t t = now(CLOCK_MONOTONIC);
+ if (t >= end)
+ return -ETIME;
+
+ wait_for = usec_sub_unsigned(end, t);
}
- return -errno;
+ r = fd_wait_for_event(fd, POLLOUT, wait_for);
+ if (timeout == USEC_INFINITY || ERRNO_IS_NEG_TRANSIENT(r))
+ /* If timeout == USEC_INFINITY we knowingly ignore any return value
+ * here, and expect that any error/EOF is reported via write() */
+ continue;
+ if (r < 0)
+ return r;
+ if (r == 0)
+ return -ETIME;
+ continue;
}
if (_unlikely_(nbytes > 0 && k == 0)) /* Can't really happen */
@@ -260,7 +292,7 @@ ssize_t sparse_write(int fd, const void *p, size_t sz, size_t run_length) {
return -EIO;
}
- if (lseek(fd, n, SEEK_CUR) == (off_t) -1)
+ if (lseek(fd, n, SEEK_CUR) < 0)
return -errno;
q += n;
@@ -281,102 +313,4 @@ ssize_t sparse_write(int fd, const void *p, size_t sz, size_t run_length) {
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;
-}
-
-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->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) {
- for (size_t 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;
-
- for (size_t i = 0; i < iovw->count; i++)
- n += iovw->iovec[i].iov_len;
-
- return n;
-}
-
-void iovec_array_free(struct iovec *iov, size_t n) {
- if (!iov)
- return;
-
- for (size_t i = 0; i < n; i++)
- free(iov[i].iov_base);
-
- free(iov);
-}
#endif /* NM_IGNORED */
diff --git a/src/libnm-systemd-shared/src/basic/io-util.h b/src/libnm-systemd-shared/src/basic/io-util.h
index 3ad8267962..e027c1a878 100644
--- a/src/libnm-systemd-shared/src/basic/io-util.h
+++ b/src/libnm-systemd-shared/src/basic/io-util.h
@@ -6,7 +6,6 @@
#include <stddef.h>
#include <stdint.h>
#include <sys/types.h>
-#include <sys/uio.h>
#include "macro.h"
#include "time-util.h"
@@ -15,7 +14,11 @@ 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 loop_write_full(int fd, const void *buf, size_t nbytes, usec_t timeout);
+static inline int loop_write(int fd, const void *buf, size_t nbytes) {
+ return loop_write_full(fd, buf, nbytes, 0);
+}
int pipe_eof(int fd);
@@ -24,38 +27,6 @@ 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 r = 0;
-
- for (size_t j = 0; j < n; j++)
- r += i[j].iov_len;
-
- return r;
-}
-
-static inline bool IOVEC_INCREMENT(struct iovec *i, size_t n, size_t k) {
- /* Returns true if there is nothing else to send (bytes written cover all of the iovec),
- * false if there's still work to do. */
-
- for (size_t j = 0; j < n; j++) {
- size_t sub;
-
- if (i[j].iov_len == 0)
- continue;
- if (k == 0)
- return false;
-
- 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;
- }
-
- assert(k == 0); /* Anything else would mean that we wrote more bytes than available,
- * or the kernel reported writing more bytes than sent. */
- return true;
-}
-
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. */
@@ -73,40 +44,3 @@ static inline bool FILE_SIZE_VALID_OR_INFINITY(uint64_t l) {
return FILE_SIZE_VALID(l);
}
-
-#define IOVEC_NULL (struct iovec) {}
-#define IOVEC_MAKE(base, len) (struct iovec) { .iov_base = (base), .iov_len = (len) }
-#define IOVEC_MAKE_STRING(string) \
- ({ \
- char *_s = (char*) (string); \
- IOVEC_MAKE(_s, strlen(_s)); \
- })
-
-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;
-};
-
-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);
-static inline int iovw_consume(struct iovec_wrapper *iovw, void *data, size_t len) {
- /* Move data into iovw or free on error */
- int r = iovw_put(iovw, data, len);
- if (r < 0)
- free(data);
- return r;
-}
-
-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);
-
-void iovec_array_free(struct iovec *iov, size_t n);
diff --git a/src/libnm-systemd-shared/src/basic/iovec-util.h b/src/libnm-systemd-shared/src/basic/iovec-util.h
new file mode 100644
index 0000000000..8cfa5717dc
--- /dev/null
+++ b/src/libnm-systemd-shared/src/basic/iovec-util.h
@@ -0,0 +1,99 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+#pragma once
+
+#include <stdbool.h>
+#include <sys/types.h>
+#include <sys/uio.h>
+
+#include "alloc-util.h"
+#include "macro.h"
+
+/* An iovec pointing to a single NUL byte */
+#define IOVEC_NUL_BYTE (const struct iovec) { \
+ .iov_base = (void*) (const uint8_t[1]) { 0 }, \
+ .iov_len = 1, \
+ }
+
+size_t iovec_total_size(const struct iovec *iovec, size_t n);
+
+bool iovec_increment(struct iovec *iovec, size_t n, size_t k);
+
+/* This accepts both const and non-const pointers */
+#define IOVEC_MAKE(base, len) \
+ (struct iovec) { \
+ .iov_base = (void*) (base), \
+ .iov_len = (len), \
+ }
+
+static inline struct iovec* iovec_make_string(struct iovec *iovec, const char *s) {
+ assert(iovec);
+ /* We don't use strlen_ptr() here, because we don't want to include string-util.h for now */
+ *iovec = IOVEC_MAKE(s, s ? strlen(s) : 0);
+ return iovec;
+}
+
+#define IOVEC_MAKE_STRING(s) \
+ *iovec_make_string(&(struct iovec) {}, s)
+
+#define CONST_IOVEC_MAKE_STRING(s) \
+ (const struct iovec) { \
+ .iov_base = (char*) s, \
+ .iov_len = STRLEN(s), \
+ }
+
+static inline void iovec_done(struct iovec *iovec) {
+ /* A _cleanup_() helper that frees the iov_base in the iovec */
+ assert(iovec);
+
+ iovec->iov_base = mfree(iovec->iov_base);
+ iovec->iov_len = 0;
+}
+
+static inline void iovec_done_erase(struct iovec *iovec) {
+ assert(iovec);
+
+ iovec->iov_base = erase_and_free(iovec->iov_base);
+ iovec->iov_len = 0;
+}
+
+static inline bool iovec_is_set(const struct iovec *iovec) {
+ /* Checks if the iovec points to a non-empty chunk of memory */
+ return iovec && iovec->iov_len > 0 && iovec->iov_base;
+}
+
+static inline bool iovec_is_valid(const struct iovec *iovec) {
+ /* Checks if the iovec is either NULL, empty or points to a valid bit of memory */
+ return !iovec || (iovec->iov_base || iovec->iov_len == 0);
+}
+
+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);
+
+void iovec_array_free(struct iovec *iovec, size_t n_iovec);
+
+static inline int iovec_memcmp(const struct iovec *a, const struct iovec *b) {
+
+ if (a == b)
+ return 0;
+
+ return memcmp_nn(a ? a->iov_base : NULL,
+ a ? a->iov_len : 0,
+ b ? b->iov_base : NULL,
+ b ? b->iov_len : 0);
+}
+
+static inline struct iovec *iovec_memdup(const struct iovec *source, struct iovec *ret) {
+ assert(ret);
+
+ if (!iovec_is_set(source))
+ *ret = (struct iovec) {};
+ else {
+ void *p = memdup(source->iov_base, source->iov_len);
+ if (!p)
+ return NULL;
+
+ *ret = IOVEC_MAKE(p, source->iov_len);
+ }
+
+ return ret;
+}
diff --git a/src/libnm-systemd-shared/src/basic/list.h b/src/libnm-systemd-shared/src/basic/list.h
index e4e5dff3ea..10e69541d4 100644
--- a/src/libnm-systemd-shared/src/basic/list.h
+++ b/src/libnm-systemd-shared/src/basic/list.h
@@ -192,6 +192,18 @@
_p; \
})
+#define LIST_CLEAR(name, head, free_func) \
+ _LIST_CLEAR(name, head, free_func, UNIQ_T(elem, UNIQ))
+
+/* Clear the list, destroying each element with free_func */
+#define _LIST_CLEAR(name, head, free_func, elem) \
+ ({ \
+ typeof(head) elem; \
+ while ((elem = LIST_POP(name, head))) \
+ free_func(elem); \
+ head; \
+ })
+
/* Now include "macro.h", because we want our definition of assert() which the macros above use. We include
* it down here instead of up top, since macro.h pulls in log.h which in turn needs our own definitions. */
#include "macro.h"
diff --git a/src/libnm-systemd-shared/src/basic/locale-util.c b/src/libnm-systemd-shared/src/basic/locale-util.c
index 2f3701eba6..78564a74a0 100644
--- a/src/libnm-systemd-shared/src/basic/locale-util.c
+++ b/src/libnm-systemd-shared/src/basic/locale-util.c
@@ -19,6 +19,7 @@
#include "fileio.h"
#include "hashmap.h"
#include "locale-util.h"
+#include "missing_syscall.h"
#include "path-util.h"
#include "set.h"
#include "string-table.h"
@@ -223,7 +224,7 @@ int get_locales(char ***ret) {
locales = set_free(locales);
r = getenv_bool("SYSTEMD_LIST_NON_UTF8_LOCALES");
- if (r == -ENXIO || r == 0) {
+ if (IN_SET(r, -ENXIO, 0)) {
char **a, **b;
/* Filter out non-UTF-8 locales, because it's 2019, by default */
@@ -282,11 +283,6 @@ int locale_is_installed(const char *name) {
return true;
}
-
-void init_gettext(void) {
- setlocale(LC_ALL, "");
- textdomain(GETTEXT_PACKAGE);
-}
#endif /* NM_IGNORED */
bool is_locale_utf8(void) {
@@ -307,6 +303,12 @@ bool is_locale_utf8(void) {
} else if (r != -ENXIO)
log_debug_errno(r, "Failed to parse $SYSTEMD_UTF8, ignoring: %m");
+ /* This function may be called from libsystemd, and setlocale() is not thread safe. Assuming yes. */
+ if (gettid() != raw_getpid()) {
+ cached_answer = true;
+ goto out;
+ }
+
if (!setlocale(LC_ALL, "")) {
cached_answer = true;
goto out;
@@ -345,11 +347,7 @@ out:
#if 0 /* NM_IGNORED */
void locale_variables_free(char *l[_VARIABLE_LC_MAX]) {
- if (!l)
- return;
-
- for (LocaleVariable i = 0; i < _VARIABLE_LC_MAX; i++)
- l[i] = mfree(l[i]);
+ free_many_charp(l, _VARIABLE_LC_MAX);
}
void locale_variables_simplify(char *l[_VARIABLE_LC_MAX]) {
diff --git a/src/libnm-systemd-shared/src/basic/locale-util.h b/src/libnm-systemd-shared/src/basic/locale-util.h
index 8990cb6a75..81fe8d1084 100644
--- a/src/libnm-systemd-shared/src/basic/locale-util.h
+++ b/src/libnm-systemd-shared/src/basic/locale-util.h
@@ -33,9 +33,8 @@ int get_locales(char ***l);
bool locale_is_valid(const char *name);
int locale_is_installed(const char *name);
-#define _(String) gettext(String)
+#define _(String) dgettext(GETTEXT_PACKAGE, String)
#define N_(String) String
-void init_gettext(void);
bool is_locale_utf8(void);
diff --git a/src/libnm-systemd-shared/src/basic/lock-util.h b/src/libnm-systemd-shared/src/basic/lock-util.h
index e7744476bb..91b332f803 100644
--- a/src/libnm-systemd-shared/src/basic/lock-util.h
+++ b/src/libnm-systemd-shared/src/basic/lock-util.h
@@ -34,9 +34,12 @@ void unposix_unlockpp(int **fd);
_cleanup_(unposix_unlockpp) _unused_ int *CONCATENATE(_cleanup_unposix_unlock_, UNIQ) = &(fd)
typedef enum LockType {
+ LOCK_NONE, /* Don't lock the file descriptor. Useful if you need to conditionally lock a file. */
LOCK_BSD,
LOCK_POSIX,
LOCK_UNPOSIX,
} LockType;
int lock_generic(int fd, LockType type, int operation);
+
+int lock_generic_with_timeout(int fd, LockType type, int operation, usec_t timeout);
diff --git a/src/libnm-systemd-shared/src/basic/log.h b/src/libnm-systemd-shared/src/basic/log.h
index eb7b51cb82..cc59b0775c 100644
--- a/src/libnm-systemd-shared/src/basic/log.h
+++ b/src/libnm-systemd-shared/src/basic/log.h
@@ -467,6 +467,9 @@ void log_set_open_when_needed(bool b);
* stderr, the console or kmsg */
void log_set_prohibit_ipc(bool b);
+void log_set_assert_return_is_critical(bool b);
+bool log_get_assert_return_is_critical(void) _pure_;
+
int log_dup_console(void);
#if 0 /* NM_IGNORED */
diff --git a/src/libnm-systemd-shared/src/basic/macro.h b/src/libnm-systemd-shared/src/basic/macro.h
index ce7350cb0e..eec8cba673 100644
--- a/src/libnm-systemd-shared/src/basic/macro.h
+++ b/src/libnm-systemd-shared/src/basic/macro.h
@@ -14,7 +14,7 @@
/* 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
+ * our primary use case 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__))
@@ -84,7 +84,7 @@
#define REENABLE_WARNING
#endif
-/* automake test harness */
+/* test harness */
#define EXIT_TEST_SKIP 77
/* builtins */
@@ -96,6 +96,13 @@
#error "neither int nor long are four bytes long?!?"
#endif
+static inline uint64_t u64_multiply_safe(uint64_t a, uint64_t b) {
+ if (_unlikely_(a != 0 && b > (UINT64_MAX / a)))
+ return 0; /* overflow */
+
+ return a * b;
+}
+
/* align to next higher power-of-2 (except for: 0 => 0, overflow => 0) */
static inline unsigned long ALIGN_POWER2(unsigned long u) {
@@ -198,7 +205,7 @@ static inline int __coverity_check_and_return__(int condition) {
/* We override the glibc assert() here. */
#undef assert
#ifdef NDEBUG
-#define assert(expr) do {} while (false)
+#define assert(expr) ({ if (!(expr)) __builtin_unreachable(); })
#else
#define assert(expr) assert_message_se(expr, #expr)
#endif
@@ -304,12 +311,6 @@ static inline int __coverity_check_and_return__(int condition) {
/* 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 _FOREACH_ARRAY(i, array, num, m, end) \
for (typeof(array[0]) *i = (array), *end = ({ \
typeof(num) m = (num); \
@@ -319,31 +320,6 @@ static inline int __coverity_check_and_return__(int condition) {
#define FOREACH_ARRAY(i, array, num) \
_FOREACH_ARRAY(i, array, num, UNIQ_T(m, UNIQ), UNIQ_T(end, UNIQ))
-#define DEFINE_TRIVIAL_DESTRUCTOR(name, type, func) \
- static inline void name(type *p) { \
- func(p); \
- }
-
-/* When func() returns the void value (NULL, -1, …) of the appropriate type */
-#define DEFINE_TRIVIAL_CLEANUP_FUNC(type, func) \
- static inline void func##p(type *p) { \
- if (*p) \
- *p = func(*p); \
- }
-
-/* When func() doesn't return the appropriate type, set variable to empty afterwards.
- * The func() may be provided by a dynamically loaded shared library, hence add an assertion. */
-#define DEFINE_TRIVIAL_CLEANUP_FUNC_FULL(type, func, empty) \
- static inline void func##p(type *p) { \
- if (*p != (empty)) { \
- DISABLE_WARNING_ADDRESS; \
- assert(func); \
- REENABLE_WARNING; \
- func(*p); \
- *p = (empty); \
- } \
- }
-
#define _DEFINE_TRIVIAL_REF_FUNC(type, name, scope) \
scope type *name##_ref(type *p) { \
if (!p) \
@@ -443,13 +419,13 @@ assert_cc(sizeof(dummy_t) == 0);
_q && _q > (base) ? &_q[-1] : NULL; \
})
-/* Iterate through each variadic arg. All must be the same type as 'entry' or must be implicitly
+/* Iterate through each argument passed. All must be the same type as 'entry' or must be implicitly
* convertible. The iteration variable 'entry' must already be defined. */
-#define VA_ARGS_FOREACH(entry, ...) \
- _VA_ARGS_FOREACH(entry, UNIQ_T(_entries_, UNIQ), UNIQ_T(_current_, UNIQ), ##__VA_ARGS__)
-#define _VA_ARGS_FOREACH(entry, _entries_, _current_, ...) \
- for (typeof(entry) _entries_[] = { __VA_ARGS__ }, *_current_ = _entries_; \
- ((long)(_current_ - _entries_) < (long)ELEMENTSOF(_entries_)) && ({ entry = *_current_; true; }); \
+#define FOREACH_ARGUMENT(entry, ...) \
+ _FOREACH_ARGUMENT(entry, UNIQ_T(_entries_, UNIQ), UNIQ_T(_current_, UNIQ), UNIQ_T(_va_sentinel_, UNIQ), ##__VA_ARGS__)
+#define _FOREACH_ARGUMENT(entry, _entries_, _current_, _va_sentinel_, ...) \
+ for (typeof(entry) _va_sentinel_[1] = {}, _entries_[] = { __VA_ARGS__ __VA_OPT__(,) _va_sentinel_[0] }, *_current_ = _entries_; \
+ ((long)(_current_ - _entries_) < (long)(ELEMENTSOF(_entries_) - 1)) && ({ entry = *_current_; true; }); \
_current_++)
#include "log.h"
diff --git a/src/libnm-systemd-shared/src/basic/memory-util.c b/src/libnm-systemd-shared/src/basic/memory-util.c
index c1e0a742b1..789e96a9c3 100644
--- a/src/libnm-systemd-shared/src/basic/memory-util.c
+++ b/src/libnm-systemd-shared/src/basic/memory-util.c
@@ -41,3 +41,19 @@ bool memeqbyte(uint8_t byte, const void *data, size_t length) {
/* Now we know first 16 bytes match, memcmp() with self. */
return memcmp(data, p + 16, length) == 0;
}
+
+void *memdup_reverse(const void *mem, size_t size) {
+ assert(mem);
+ assert(size != 0);
+
+ void *p = malloc(size);
+ if (!p)
+ return NULL;
+
+ uint8_t *p_dst = p;
+ const uint8_t *p_src = mem;
+ for (size_t i = 0, k = size; i < size; i++, k--)
+ p_dst[i] = p_src[k-1];
+
+ return p;
+}
diff --git a/src/libnm-systemd-shared/src/basic/memory-util.h b/src/libnm-systemd-shared/src/basic/memory-util.h
index d26a0918e1..294aed67df 100644
--- a/src/libnm-systemd-shared/src/basic/memory-util.h
+++ b/src/libnm-systemd-shared/src/basic/memory-util.h
@@ -12,9 +12,12 @@
#include "memory-util-fundamental.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))
+#define PAGE_ALIGN(l) ALIGN_TO(l, page_size())
+#define PAGE_ALIGN_U64(l) ALIGN_TO_U64(l, page_size())
+#define PAGE_ALIGN_DOWN(l) ALIGN_DOWN(l, page_size())
+#define PAGE_ALIGN_DOWN_U64(l) ALIGN_DOWN_U64(l, page_size())
+#define PAGE_OFFSET(l) ALIGN_OFFSET(l, page_size())
+#define PAGE_OFFSET_U64(l) ALIGN_OFFSET_U64(l, page_size())
/* 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) {
@@ -47,13 +50,6 @@ static inline int memcmp_nn(const void *s1, size_t n1, const void *s2, size_t 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 memeqbyte(uint8_t byte, const void *data, size_t length);
@@ -112,36 +108,5 @@ static inline void erase_char(char *p) {
explicit_bzero_safe(p, sizeof(char));
}
-/* An automatic _cleanup_-like logic for destroy arrays (i.e. pointers + size) when leaving scope */
-typedef struct ArrayCleanup {
- void **parray;
- size_t *pn;
- free_array_func_t pfunc;
-} ArrayCleanup;
-
-static inline void array_cleanup(const ArrayCleanup *c) {
- assert(c);
-
- assert(!c->parray == !c->pn);
-
- if (!c->parray)
- return;
-
- if (*c->parray) {
- assert(c->pfunc);
- c->pfunc(*c->parray, *c->pn);
- *c->parray = NULL;
- }
-
- *c->pn = 0;
-}
-
-#define CLEANUP_ARRAY(array, n, func) \
- _cleanup_(array_cleanup) _unused_ const ArrayCleanup CONCATENATE(_cleanup_array_, UNIQ) = { \
- .parray = (void**) &(array), \
- .pn = &(n), \
- .pfunc = (free_array_func_t) ({ \
- void (*_f)(typeof(array[0]) *a, size_t b) = func; \
- _f; \
- }), \
- }
+/* Makes a copy of the buffer with reversed order of bytes */
+void *memdup_reverse(const void *mem, size_t size);
diff --git a/src/libnm-systemd-shared/src/basic/missing_socket.h b/src/libnm-systemd-shared/src/basic/missing_socket.h
index a4f6836fd4..ffda7cc684 100644
--- a/src/libnm-systemd-shared/src/basic/missing_socket.h
+++ b/src/libnm-systemd-shared/src/basic/missing_socket.h
@@ -7,7 +7,6 @@
#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;
@@ -22,6 +21,26 @@ struct sockaddr_vm {
#endif /* !HAVE_LINUX_VM_SOCKETS_H */
#endif /* NM_IGNORED */
+#ifndef VMADDR_CID_ANY
+#define VMADDR_CID_ANY -1U
+#endif
+
+#ifndef VMADDR_CID_HYPERVISOR
+#define VMADDR_CID_HYPERVISOR 0U
+#endif
+
+#ifndef VMADDR_CID_LOCAL
+#define VMADDR_CID_LOCAL 1U
+#endif
+
+#ifndef VMADDR_CID_HOST
+#define VMADDR_CID_HOST 2U
+#endif
+
+#ifndef VMADDR_PORT_ANY
+#define VMADDR_PORT_ANY -1U
+#endif
+
#ifndef AF_VSOCK
#define AF_VSOCK 40
#endif
@@ -34,6 +53,10 @@ struct sockaddr_vm {
#define SO_PEERGROUPS 59
#endif
+#ifndef SO_PEERPIDFD
+#define SO_PEERPIDFD 77
+#endif
+
#ifndef SO_BINDTOIFINDEX
#define SO_BINDTOIFINDEX 62
#endif
diff --git a/src/libnm-systemd-shared/src/basic/missing_stat.h b/src/libnm-systemd-shared/src/basic/missing_stat.h
index 8b39d4f44f..18a15ab00a 100644
--- a/src/libnm-systemd-shared/src/basic/missing_stat.h
+++ b/src/libnm-systemd-shared/src/basic/missing_stat.h
@@ -9,7 +9,7 @@
#include <linux/stat.h>
#endif
-/* Thew newest definition we are aware of (fa2fcf4f1df1559a0a4ee0f46915b496cc2ebf60; 5.8) */
+/* The newest definition we are aware of (fa2fcf4f1df1559a0a4ee0f46915b496cc2ebf60; 5.8) */
#define STATX_DEFINITION { \
__u32 stx_mask; \
__u32 stx_blksize; \
diff --git a/src/libnm-systemd-shared/src/basic/missing_syscall.h b/src/libnm-systemd-shared/src/basic/missing_syscall.h
index 610a7cef2e..a04633510d 100644
--- a/src/libnm-systemd-shared/src/basic/missing_syscall.h
+++ b/src/libnm-systemd-shared/src/basic/missing_syscall.h
@@ -34,6 +34,21 @@
/* ======================================================================= */
+#if !HAVE_FCHMODAT2
+static inline int missing_fchmodat2(int dirfd, const char *path, mode_t mode, int flags) {
+# ifdef __NR_fchmodat2
+ return syscall(__NR_fchmodat2, dirfd, path, mode, flags);
+# else
+ errno = ENOSYS;
+ return -1;
+# endif
+}
+
+# define fchmodat2 missing_fchmodat2
+#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);
@@ -546,6 +561,10 @@ static inline int missing_open_tree(
/* ======================================================================= */
+#ifndef MOVE_MOUNT_BENEATH
+#define MOVE_MOUNT_BENEATH 0x00000200
+#endif
+
#if !HAVE_MOVE_MOUNT
#ifndef MOVE_MOUNT_F_EMPTY_PATH
@@ -662,3 +681,17 @@ static inline ssize_t missing_getdents64(int fd, void *buffer, size_t length) {
# define getdents64 missing_getdents64
#endif
#endif /* NM_IGNORED */
+
+/* ======================================================================= */
+
+/* glibc does not provide clone() on ia64, only clone2(). Not only that, but it also doesn't provide a
+ * prototype, only the symbol in the shared library (it provides a prototype for clone(), but not the
+ * symbol in the shared library). */
+#if defined(__ia64__)
+int __clone2(int (*fn)(void *), void *stack_base, size_t stack_size, int flags, void *arg);
+#define HAVE_CLONE 0
+#else
+/* We know that everywhere else clone() is available, so we don't bother with a meson check (that takes time
+ * at build time) and just define it. Once the kernel drops ia64 support, we can drop this too. */
+#define HAVE_CLONE 1
+#endif
diff --git a/src/libnm-systemd-shared/src/basic/namespace-util.h b/src/libnm-systemd-shared/src/basic/namespace-util.h
new file mode 100644
index 0000000000..d1d015612f
--- /dev/null
+++ b/src/libnm-systemd-shared/src/basic/namespace-util.h
@@ -0,0 +1,55 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+#pragma once
+
+#include <sys/types.h>
+
+typedef enum NamespaceType {
+ NAMESPACE_CGROUP,
+ NAMESPACE_IPC,
+ NAMESPACE_NET,
+ NAMESPACE_MOUNT,
+ NAMESPACE_PID,
+ NAMESPACE_USER,
+ NAMESPACE_UTS,
+ NAMESPACE_TIME,
+ _NAMESPACE_TYPE_MAX,
+ _NAMESPACE_TYPE_INVALID = -EINVAL,
+} NamespaceType;
+
+extern const struct namespace_info {
+ const char *proc_name;
+ const char *proc_path;
+ unsigned int clone_flag;
+} namespace_info[_NAMESPACE_TYPE_MAX + 1];
+
+int namespace_open(
+ pid_t pid,
+ int *ret_pidns_fd,
+ int *ret_mntns_fd,
+ int *ret_netns_fd,
+ int *ret_userns_fd,
+ int *ret_root_fd);
+int namespace_enter(int pidns_fd, int mntns_fd, int netns_fd, int userns_fd, int root_fd);
+
+int fd_is_ns(int fd, unsigned long nsflag);
+
+int detach_mount_namespace(void);
+
+static inline bool userns_shift_range_valid(uid_t shift, uid_t range) {
+ /* Checks that the specified userns range makes sense, i.e. contains at least one UID, and the end
+ * doesn't overflow uid_t. */
+
+ assert_cc((uid_t) -1 > 0); /* verify that uid_t is unsigned */
+
+ if (range <= 0)
+ return false;
+
+ if (shift > (uid_t) -1 - range)
+ return false;
+
+ return true;
+}
+
+int userns_acquire(const char *uid_map, const char *gid_map);
+int netns_acquire(void);
+int in_same_namespace(pid_t pid1, pid_t pid2, NamespaceType type);
diff --git a/src/libnm-systemd-shared/src/basic/ordered-set.c b/src/libnm-systemd-shared/src/basic/ordered-set.c
index f402bb5b0c..ae50070f17 100644
--- a/src/libnm-systemd-shared/src/basic/ordered-set.c
+++ b/src/libnm-systemd-shared/src/basic/ordered-set.c
@@ -93,13 +93,16 @@ void ordered_set_print(FILE *f, const char *field, OrderedSet *s) {
bool space = false;
char *p;
+ assert(f);
+ assert(field);
+
if (ordered_set_isempty(s))
return;
fputs(field, f);
ORDERED_SET_FOREACH(p, s)
- fputs_with_space(f, p, NULL, &space);
+ fputs_with_separator(f, p, NULL, &space);
fputc('\n', f);
}
diff --git a/src/libnm-systemd-shared/src/basic/parse-util.c b/src/libnm-systemd-shared/src/basic/parse-util.c
index 2b22039c1c..34a5375f85 100644
--- a/src/libnm-systemd-shared/src/basic/parse-util.c
+++ b/src/libnm-systemd-shared/src/basic/parse-util.c
@@ -47,6 +47,24 @@ int parse_boolean(const char *v) {
}
#if 0 /* NM_IGNORED */
+int parse_tristate_full(const char *v, const char *third, int *ret) {
+ int r;
+
+ if (isempty(v) || streq_ptr(v, third)) { /* Empty string is always taken as the third/invalid/auto state */
+ if (ret)
+ *ret = -1;
+ } else {
+ r = parse_boolean(v);
+ if (r < 0)
+ return r;
+
+ if (ret)
+ *ret = r;
+ }
+
+ return 0;
+}
+
int parse_pid(const char *s, pid_t* ret_pid) {
unsigned long ul = 0;
pid_t pid;
@@ -110,8 +128,7 @@ int parse_ifindex(const char *s) {
#if 0 /* NM_IGNORED */
int parse_mtu(int family, const char *s, uint32_t *ret) {
- uint64_t u;
- size_t m;
+ uint64_t u, m;
int r;
r = parse_size(s, 1024, &u);
@@ -121,10 +138,16 @@ int parse_mtu(int family, const char *s, uint32_t *ret) {
if (u > UINT32_MAX)
return -ERANGE;
- if (family == AF_INET6)
+ switch (family) {
+ case AF_INET:
+ m = IPV4_MIN_MTU; /* This is 68 */
+ break;
+ case 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 */
+ break;
+ default:
+ m = 0;
+ }
if (u < m)
return -ERANGE;
@@ -431,6 +454,21 @@ int safe_atou_full(const char *s, unsigned base, unsigned *ret_u) {
return 0;
}
+int safe_atou_bounded(const char *s, unsigned min, unsigned max, unsigned *ret) {
+ unsigned v;
+ int r;
+
+ r = safe_atou(s, &v);
+ if (r < 0)
+ return r;
+
+ if (v < min || v > max)
+ return -ERANGE;
+
+ *ret = v;
+ return 0;
+}
+
int safe_atoi(const char *s, int *ret_i) {
unsigned base = 0;
char *x = NULL;
@@ -660,7 +698,7 @@ int parse_ip_port(const char *s, uint16_t *ret) {
return 0;
}
-int parse_ip_port_range(const char *s, uint16_t *low, uint16_t *high) {
+int parse_ip_port_range(const char *s, uint16_t *low, uint16_t *high, bool allow_zero) {
unsigned l, h;
int r;
@@ -668,7 +706,10 @@ int parse_ip_port_range(const char *s, uint16_t *low, uint16_t *high) {
if (r < 0)
return r;
- if (l <= 0 || l > 65535 || h <= 0 || h > 65535)
+ if (l > 65535 || h > 65535)
+ return -EINVAL;
+
+ if (!allow_zero && (l == 0 || h == 0))
return -EINVAL;
if (h < l)
@@ -754,4 +795,23 @@ int parse_loadavg_fixed_point(const char *s, loadavg_t *ret) {
return store_loadavg_fixed_point(i, f, ret);
}
+
+/* Limitations are described in https://www.netfilter.org/projects/nftables/manpage.html and
+ * https://bugzilla.netfilter.org/show_bug.cgi?id=1175 */
+bool nft_identifier_valid(const char *id) {
+ if (!id)
+ return false;
+
+ size_t len = strlen(id);
+ if (len == 0 || len > 31)
+ return false;
+
+ if (!ascii_isalpha(id[0]))
+ return false;
+
+ for (size_t i = 1; i < len; i++)
+ if (!ascii_isalpha(id[i]) && !ascii_isdigit(id[i]) && !IN_SET(id[i], '/', '\\', '_', '.'))
+ return false;
+ return true;
+}
#endif /* NM_IGNORED */
diff --git a/src/libnm-systemd-shared/src/basic/parse-util.h b/src/libnm-systemd-shared/src/basic/parse-util.h
index c480407c2a..c12988ef20 100644
--- a/src/libnm-systemd-shared/src/basic/parse-util.h
+++ b/src/libnm-systemd-shared/src/basic/parse-util.h
@@ -12,6 +12,10 @@
typedef unsigned long loadavg_t;
int parse_boolean(const char *v) _pure_;
+int parse_tristate_full(const char *v, const char *third, int *ret);
+static inline int parse_tristate(const char *v, int *ret) {
+ return parse_tristate_full(v, NULL, 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);
@@ -30,11 +34,12 @@ int parse_fd(const char *t);
#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_atou_bounded(const char *s, unsigned min, unsigned max, unsigned *ret);
+
int safe_atoi(const char *s, int *ret_i);
int safe_atolli(const char *s, long long int *ret_i);
@@ -134,7 +139,7 @@ int parse_fractional_part_u(const char **s, size_t digits, unsigned *res);
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_port_range(const char *s, uint16_t *low, uint16_t *high, bool allow_zero);
int parse_ip_prefix_length(const char *s, int *ret);
@@ -152,3 +157,5 @@ int parse_oom_score_adjust(const char *s, int *ret);
* 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);
+
+bool nft_identifier_valid(const char *id);
diff --git a/src/libnm-systemd-shared/src/basic/path-util.c b/src/libnm-systemd-shared/src/basic/path-util.c
index a2af9e0ce2..638e4860a6 100644
--- a/src/libnm-systemd-shared/src/basic/path-util.c
+++ b/src/libnm-systemd-shared/src/basic/path-util.c
@@ -43,7 +43,7 @@ int path_split_and_make_absolute(const char *p, char ***ret) {
return r;
}
-char *path_make_absolute(const char *p, const char *prefix) {
+char* path_make_absolute(const char *p, const char *prefix) {
assert(p);
/* Makes every item in the list an absolute path by prepending
@@ -135,11 +135,9 @@ int path_make_relative(const char *from, const char *to, char **ret) {
return -ENOMEM;
} else {
/* 'to' is inside of 'from'. */
- result = strdup(t);
- if (!result)
- return -ENOMEM;
-
- path_simplify(result);
+ r = path_simplify_alloc(t, &result);
+ if (r < 0)
+ return r;
if (!path_is_valid(result))
return -EINVAL;
@@ -255,7 +253,7 @@ int path_strv_make_absolute_cwd(char **l) {
return 0;
}
-char **path_strv_resolve(char **l, const char *root) {
+char** path_strv_resolve(char **l, const char *root) {
unsigned k = 0;
bool enomem = false;
int r;
@@ -336,7 +334,7 @@ char **path_strv_resolve(char **l, const char *root) {
return l;
}
-char **path_strv_resolve_uniq(char **l, const char *root) {
+char** path_strv_resolve_uniq(char **l, const char *root) {
if (strv_isempty(l))
return l;
@@ -348,9 +346,9 @@ char **path_strv_resolve_uniq(char **l, const char *root) {
}
#endif /* NM_IGNORED */
-char *path_simplify(char *path) {
- bool add_slash = false;
- char *f = ASSERT_PTR(path);
+char* path_simplify_full(char *path, PathSimplifyFlags flags) {
+ bool add_slash = false, keep_trailing_slash, absolute, beginning = true;
+ char *f = path;
int r;
/* Removes redundant inner and trailing slashes. Also removes unnecessary dots.
@@ -358,13 +356,17 @@ char *path_simplify(char *path) {
*
* ///foo//./bar/. becomes /foo/bar
* .//./foo//./bar/. becomes foo/bar
+ * /../foo/bar becomes /foo/bar
+ * /../foo/bar/.. becomes /foo/bar/..
*/
if (isempty(path))
return path;
- if (path_is_absolute(path))
- f++;
+ keep_trailing_slash = FLAGS_SET(flags, PATH_SIMPLIFY_KEEP_TRAILING_SLASH) && endswith(path, "/");
+
+ absolute = path_is_absolute(path);
+ f += absolute; /* Keep leading /, if present. */
for (const char *p = f;;) {
const char *e;
@@ -373,11 +375,17 @@ char *path_simplify(char *path) {
if (r == 0)
break;
+ if (r > 0 && absolute && beginning && path_startswith(e, ".."))
+ /* If we're at the beginning of an absolute path, we can safely skip ".." */
+ continue;
+
+ beginning = false;
+
if (add_slash)
*f++ = '/';
if (r < 0) {
- /* if path is invalid, then refuse to simplify remaining part. */
+ /* if path is invalid, then refuse to simplify the remaining part. */
memmove(f, p, strlen(p) + 1);
return path;
}
@@ -392,11 +400,14 @@ char *path_simplify(char *path) {
if (f == path)
*f++ = '.';
+ if (*(f-1) != '/' && keep_trailing_slash)
+ *f++ = '/';
+
*f = '\0';
return path;
}
-char *path_startswith_full(const char *path, const char *prefix, bool accept_dot_dot) {
+char* path_startswith_full(const char *path, const char *prefix, bool accept_dot_dot) {
assert(path);
assert(prefix);
@@ -489,10 +500,6 @@ int path_compare(const char *a, const char *b) {
}
}
-bool path_equal_or_inode_same(const char *a, const char *b, int flags) {
- return path_equal(a, b) || inode_same(a, b, flags) > 0;
-}
-
int path_compare_filename(const char *a, const char *b) {
_cleanup_free_ char *fa = NULL, *fb = NULL;
int r, j, k;
@@ -661,7 +668,14 @@ static int find_executable_impl(const char *name, const char *root, char **ret_f
return 0;
}
-int find_executable_full(const char *name, const char *root, char **exec_search_path, bool use_path_envvar, char **ret_filename, int *ret_fd) {
+int find_executable_full(
+ const char *name,
+ const char *root,
+ char **exec_search_path,
+ bool use_path_envvar,
+ char **ret_filename,
+ int *ret_fd) {
+
int last_error = -ENOENT, r = 0;
const char *p = NULL;
@@ -812,7 +826,7 @@ int fsck_exists_for_fstype(const char *fstype) {
}
#endif /* NM_IGNORED */
-static const char *skip_slash_or_dot(const char *p) {
+static const char* skip_slash_or_dot(const char *p) {
for (; !isempty(p); p++) {
if (*p == '/')
continue;
@@ -896,7 +910,7 @@ int path_find_first_component(const char **p, bool accept_dot_dot, const char **
return len;
}
-static const char *skip_slash_or_dot_backward(const char *path, const char *q) {
+static const char* skip_slash_or_dot_backward(const char *path, const char *q) {
assert(path);
assert(!q || q >= path);
@@ -1005,7 +1019,7 @@ int path_find_last_component(const char *path, bool accept_dot_dot, const char *
return len;
}
-const char *last_path_component(const char *path) {
+const char* last_path_component(const char *path) {
/* Finds the last component of the path, preserving the optional trailing slash that signifies a directory.
*
@@ -1126,17 +1140,19 @@ int path_extract_directory(const char *path, char **ret) {
if (!path_is_valid(a))
return -EINVAL;
- *ret = TAKE_PTR(a);
+ if (ret)
+ *ret = TAKE_PTR(a);
+
return 0;
}
-bool filename_is_valid(const char *p) {
+bool filename_part_is_valid(const char *p) {
const char *e;
- if (isempty(p))
- return false;
+ /* Checks f the specified string is OK to be *part* of a filename. This is different from
+ * filename_is_valid() as "." and ".." and "" are OK by this call, but not by filename_is_valid(). */
- if (dot_or_dot_dot(p)) /* Yes, in this context we consider "." and ".." invalid */
+ if (!p)
return false;
e = strchrnul(p, '/');
@@ -1149,6 +1165,17 @@ bool filename_is_valid(const char *p) {
return true;
}
+bool filename_is_valid(const char *p) {
+
+ if (isempty(p))
+ return false;
+
+ if (dot_or_dot_dot(p)) /* Yes, in this context we consider "." and ".." invalid */
+ return false;
+
+ return filename_part_is_valid(p);
+}
+
bool path_is_valid_full(const char *p, bool accept_dot_dot) {
if (isempty(p))
return false;
@@ -1265,9 +1292,16 @@ bool hidden_or_backup_file(const char *filename) {
bool is_device_path(const char *path) {
/* Returns true for paths that likely refer to a device, either by path in sysfs or to something in
- * /dev. */
+ * /dev. This accepts any path that starts with /dev/ or /sys/ and has something after that prefix.
+ * It does not actually resolve the path.
+ *
+ * Examples:
+ * /dev/sda, /dev/sda/foo, /sys/class, /dev/.., /sys/.., /./dev/foo → yes.
+ * /../dev/sda, /dev, /sys, /usr/path, /usr/../dev/sda → no.
+ */
- return PATH_STARTSWITH_SET(path, "/dev/", "/sys/");
+ const char *p = PATH_STARTSWITH_SET(ASSERT_PTR(path), "/dev/", "/sys/");
+ return !isempty(p);
}
bool valid_device_node_path(const char *path) {
diff --git a/src/libnm-systemd-shared/src/basic/path-util.h b/src/libnm-systemd-shared/src/basic/path-util.h
index fee6e8ee49..5bb51ff599 100644
--- a/src/libnm-systemd-shared/src/basic/path-util.h
+++ b/src/libnm-systemd-shared/src/basic/path-util.h
@@ -6,6 +6,7 @@
#include <stddef.h>
#include "macro.h"
+#include "stat-util.h"
#include "string-util.h"
#include "strv.h"
#include "time-util.h"
@@ -25,24 +26,14 @@
# 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 PATH_SBIN_BIN("/usr/local/") ":" PATH_SBIN_BIN("/usr/")
+#define DEFAULT_PATH_NULSTR PATH_SBIN_BIN_NULSTR("/usr/local/") PATH_SBIN_BIN_NULSTR("/usr/")
#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
+#endif /* NM_IGNORED */
static inline bool is_path(const char *p) {
if (!p) /* A NULL pointer is definitely not a path */
@@ -64,7 +55,7 @@ int safe_getcwd(char **ret);
int path_make_absolute_cwd(const char *p, char **ret);
int path_make_relative(const char *from, const char *to, char **ret);
int path_make_relative_parent(const char *from_child, const char *to, char **ret);
-char *path_startswith_full(const char *path, const char *prefix, bool accept_dot_dot) _pure_;
+char* path_startswith_full(const char *path, const char *prefix, bool accept_dot_dot) _pure_;
static inline char* path_startswith(const char *path, const char *prefix) {
return path_startswith_full(path, prefix, true);
}
@@ -79,13 +70,38 @@ static inline bool path_equal_filename(const char *a, const char *b) {
return path_compare_filename(a, b) == 0;
}
-bool path_equal_or_inode_same(const char *a, const char *b, int flags);
+static inline bool path_equal_or_inode_same(const char *a, const char *b, int flags) {
+ return path_equal(a, b) || inode_same(a, b, flags) > 0;
+}
char* path_extend_internal(char **x, ...);
#define path_extend(x, ...) path_extend_internal(x, __VA_ARGS__, POINTER_MAX)
#define path_join(...) path_extend_internal(NULL, __VA_ARGS__, POINTER_MAX)
-char* path_simplify(char *path);
+typedef enum PathSimplifyFlags {
+ PATH_SIMPLIFY_KEEP_TRAILING_SLASH = 1 << 0,
+} PathSimplifyFlags;
+
+char* path_simplify_full(char *path, PathSimplifyFlags flags);
+static inline char* path_simplify(char *path) {
+ return path_simplify_full(path, 0);
+}
+
+static inline int path_simplify_alloc(const char *path, char **ret) {
+ assert(ret);
+
+ if (!path) {
+ *ret = NULL;
+ return 0;
+ }
+
+ char *t = strdup(path);
+ if (!t)
+ return -ENOMEM;
+
+ *ret = path_simplify(t);
+ return 0;
+}
static inline bool path_equal_ptr(const char *a, const char *b) {
return !!a == !!b && (!a || path_equal(a, b));
@@ -142,7 +158,7 @@ int fsck_exists_for_fstype(const char *fstype);
char *_p, *_n; \
size_t _l; \
while (_path[0] == '/' && _path[1] == '/') \
- _path ++; \
+ _path++; \
if (isempty(_root)) \
_ret = _path; \
else { \
@@ -161,10 +177,11 @@ int fsck_exists_for_fstype(const char *fstype);
int path_find_first_component(const char **p, bool accept_dot_dot, const char **ret);
int path_find_last_component(const char *path, bool accept_dot_dot, const char **next, const char **ret);
-const char *last_path_component(const char *path);
+const char* last_path_component(const char *path);
int path_extract_filename(const char *path, char **ret);
int path_extract_directory(const char *path, char **ret);
+bool filename_part_is_valid(const char *p) _pure_;
bool filename_is_valid(const char *p) _pure_;
bool path_is_valid_full(const char *p, bool accept_dot_dot) _pure_;
static inline bool path_is_valid(const char *p) {
@@ -197,7 +214,7 @@ static inline const char *skip_dev_prefix(const char *p) {
}
bool empty_or_root(const char *path);
-static inline const char *empty_to_root(const char *path) {
+static inline const char* empty_to_root(const char *path) {
return isempty(path) ? "/" : path;
}
diff --git a/src/libnm-systemd-shared/src/basic/pidref.h b/src/libnm-systemd-shared/src/basic/pidref.h
new file mode 100644
index 0000000000..c440c8b0e0
--- /dev/null
+++ b/src/libnm-systemd-shared/src/basic/pidref.h
@@ -0,0 +1,78 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+#pragma once
+
+#include "macro.h"
+
+/* An embeddable structure carrying a reference to a process. Supposed to be used when tracking processes continuously. */
+typedef struct PidRef {
+ pid_t pid; /* always valid */
+ int fd; /* only valid if pidfd are available in the kernel, and we manage to get an fd */
+} PidRef;
+
+#define PIDREF_NULL (const PidRef) { .fd = -EBADF }
+
+/* Turns a pid_t into a PidRef structure on-the-fly *without* acquiring a pidfd for it. (As opposed to
+ * pidref_set_pid() which does so *with* acquiring one, see below) */
+#define PIDREF_MAKE_FROM_PID(x) (PidRef) { .pid = (x), .fd = -EBADF }
+
+static inline bool pidref_is_set(const PidRef *pidref) {
+ return pidref && pidref->pid > 0;
+}
+
+static inline bool pidref_equal(const PidRef *a, const PidRef *b) {
+
+ if (pidref_is_set(a)) {
+ if (!pidref_is_set(b))
+ return false;
+
+ return a->pid == b->pid;
+ }
+
+ return !pidref_is_set(b);
+}
+
+/* This turns a pid_t into a PidRef structure, and acquires a pidfd for it, if possible. (As opposed to
+ * PIDREF_MAKE_FROM_PID() above, which does not acquire a pidfd.) */
+int pidref_set_pid(PidRef *pidref, pid_t pid);
+int pidref_set_pidstr(PidRef *pidref, const char *pid);
+int pidref_set_pidfd(PidRef *pidref, int fd);
+int pidref_set_pidfd_take(PidRef *pidref, int fd); /* takes ownership of the passed pidfd on success*/
+int pidref_set_pidfd_consume(PidRef *pidref, int fd); /* takes ownership of the passed pidfd in both success and failure */
+int pidref_set_parent(PidRef *ret);
+static inline int pidref_set_self(PidRef *pidref) {
+ return pidref_set_pid(pidref, 0);
+}
+
+bool pidref_is_self(const PidRef *pidref);
+
+void pidref_done(PidRef *pidref);
+PidRef *pidref_free(PidRef *pidref);
+DEFINE_TRIVIAL_CLEANUP_FUNC(PidRef*, pidref_free);
+
+int pidref_copy(const PidRef *pidref, PidRef *dest);
+int pidref_dup(const PidRef *pidref, PidRef **ret);
+
+int pidref_new_from_pid(pid_t pid, PidRef **ret);
+
+int pidref_kill(const PidRef *pidref, int sig);
+int pidref_kill_and_sigcont(const PidRef *pidref, int sig);
+int pidref_sigqueue(const PidRef *pidref, int sig, int value);
+
+int pidref_wait(const PidRef *pidref, siginfo_t *siginfo, int options);
+int pidref_wait_for_terminate(const PidRef *pidref, siginfo_t *ret);
+
+static inline void pidref_done_sigkill_wait(PidRef *pidref) {
+ if (!pidref_is_set(pidref))
+ return;
+
+ (void) pidref_kill(pidref, SIGKILL);
+ (void) pidref_wait_for_terminate(pidref, NULL);
+ pidref_done(pidref);
+}
+
+int pidref_verify(const PidRef *pidref);
+
+#define TAKE_PIDREF(p) TAKE_GENERIC((p), PidRef, PIDREF_NULL)
+
+extern const struct hash_ops pidref_hash_ops;
+extern const struct hash_ops pidref_hash_ops_free; /* Has destructor call for pidref_free(), i.e. expects heap allocated PidRef as keys */
diff --git a/src/libnm-systemd-shared/src/basic/prioq.c b/src/libnm-systemd-shared/src/basic/prioq.c
index 0af84bd273..b05b08da4a 100644
--- a/src/libnm-systemd-shared/src/basic/prioq.c
+++ b/src/libnm-systemd-shared/src/basic/prioq.c
@@ -215,7 +215,7 @@ static void remove_item(Prioq *q, struct prioq_item *i) {
}
}
-_pure_ static struct prioq_item* find_item(Prioq *q, void *data, unsigned *idx) {
+static struct prioq_item* find_item(Prioq *q, void *data, unsigned *idx) {
struct prioq_item *i;
assert(q);
diff --git a/src/libnm-systemd-shared/src/basic/process-util.c b/src/libnm-systemd-shared/src/basic/process-util.c
index 8601e0da54..0f1894b3af 100644
--- a/src/libnm-systemd-shared/src/basic/process-util.c
+++ b/src/libnm-systemd-shared/src/basic/process-util.c
@@ -7,6 +7,7 @@
#include <limits.h>
#include <linux/oom.h>
#include <pthread.h>
+#include <spawn.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
@@ -26,6 +27,7 @@
#include "alloc-util.h"
#include "architecture.h"
#include "argv-util.h"
+#include "dirent-util.h"
#include "env-file.h"
#include "env-util.h"
#include "errno-util.h"
@@ -97,7 +99,7 @@ static int get_process_state(pid_t pid) {
return (unsigned char) state;
}
-int get_process_comm(pid_t pid, char **ret) {
+int pid_get_comm(pid_t pid, char **ret) {
_cleanup_free_ char *escaped = NULL, *comm = NULL;
int r;
@@ -135,15 +137,35 @@ int get_process_comm(pid_t pid, char **ret) {
return 0;
}
-static int get_process_cmdline_nulstr(
+int pidref_get_comm(const PidRef *pid, char **ret) {
+ _cleanup_free_ char *comm = NULL;
+ int r;
+
+ if (!pidref_is_set(pid))
+ return -ESRCH;
+
+ r = pid_get_comm(pid->pid, &comm);
+ if (r < 0)
+ return r;
+
+ r = pidref_verify(pid);
+ if (r < 0)
+ return r;
+
+ if (ret)
+ *ret = TAKE_PTR(comm);
+ return 0;
+}
+
+static int pid_get_cmdline_nulstr(
pid_t pid,
size_t max_size,
ProcessCmdlineFlags flags,
char **ret,
size_t *ret_size) {
+ _cleanup_free_ char *t = NULL;
const char *p;
- char *t;
size_t k;
int r;
@@ -167,18 +189,17 @@ static int get_process_cmdline_nulstr(
return r;
if (k == 0) {
- t = mfree(t);
-
if (!(flags & PROCESS_CMDLINE_COMM_FALLBACK))
return -ENOENT;
/* Kernel threads have no argv[] */
_cleanup_free_ char *comm = NULL;
- r = get_process_comm(pid, &comm);
+ r = pid_get_comm(pid, &comm);
if (r < 0)
return r;
+ free(t);
t = strjoin("[", comm, "]");
if (!t)
return -ENOMEM;
@@ -189,12 +210,15 @@ static int get_process_cmdline_nulstr(
t[max_size] = '\0';
}
- *ret = t;
- *ret_size = k;
+ if (ret)
+ *ret = TAKE_PTR(t);
+ if (ret_size)
+ *ret_size = k;
+
return r;
}
-int get_process_cmdline(pid_t pid, size_t max_columns, ProcessCmdlineFlags flags, char **ret) {
+int pid_get_cmdline(pid_t pid, size_t max_columns, ProcessCmdlineFlags flags, char **ret) {
_cleanup_free_ char *t = NULL;
size_t k;
char *ans;
@@ -202,7 +226,7 @@ int get_process_cmdline(pid_t pid, size_t max_columns, ProcessCmdlineFlags flags
assert(pid >= 0);
assert(ret);
- /* Retrieve and format a commandline. See above for discussion of retrieval options.
+ /* Retrieve and format a command line. See above for discussion of retrieval options.
*
* There are two main formatting modes:
*
@@ -216,7 +240,7 @@ int get_process_cmdline(pid_t pid, size_t max_columns, ProcessCmdlineFlags flags
* Returns -ESRCH if the process doesn't exist, and -ENOENT if the process has no command line (and
* PROCESS_CMDLINE_COMM_FALLBACK is not specified). Returns 0 and sets *line otherwise. */
- int full = get_process_cmdline_nulstr(pid, max_columns, flags, &t, &k);
+ int full = pid_get_cmdline_nulstr(pid, max_columns, flags, &t, &k);
if (full < 0)
return full;
@@ -259,7 +283,27 @@ int get_process_cmdline(pid_t pid, size_t max_columns, ProcessCmdlineFlags flags
return 0;
}
-int get_process_cmdline_strv(pid_t pid, ProcessCmdlineFlags flags, char ***ret) {
+int pidref_get_cmdline(const PidRef *pid, size_t max_columns, ProcessCmdlineFlags flags, char **ret) {
+ _cleanup_free_ char *s = NULL;
+ int r;
+
+ if (!pidref_is_set(pid))
+ return -ESRCH;
+
+ r = pid_get_cmdline(pid->pid, max_columns, flags, &s);
+ if (r < 0)
+ return r;
+
+ r = pidref_verify(pid);
+ if (r < 0)
+ return r;
+
+ if (ret)
+ *ret = TAKE_PTR(s);
+ return 0;
+}
+
+int pid_get_cmdline_strv(pid_t pid, ProcessCmdlineFlags flags, char ***ret) {
_cleanup_free_ char *t = NULL;
char **args;
size_t k;
@@ -269,7 +313,7 @@ int get_process_cmdline_strv(pid_t pid, ProcessCmdlineFlags flags, char ***ret)
assert((flags & ~PROCESS_CMDLINE_COMM_FALLBACK) == 0);
assert(ret);
- r = get_process_cmdline_nulstr(pid, SIZE_MAX, flags, &t, &k);
+ r = pid_get_cmdline_nulstr(pid, SIZE_MAX, flags, &t, &k);
if (r < 0)
return r;
@@ -281,6 +325,27 @@ int get_process_cmdline_strv(pid_t pid, ProcessCmdlineFlags flags, char ***ret)
return 0;
}
+int pidref_get_cmdline_strv(const PidRef *pid, ProcessCmdlineFlags flags, char ***ret) {
+ _cleanup_strv_free_ char **args = NULL;
+ int r;
+
+ if (!pidref_is_set(pid))
+ return -ESRCH;
+
+ r = pid_get_cmdline_strv(pid->pid, flags, &args);
+ if (r < 0)
+ return r;
+
+ r = pidref_verify(pid);
+ if (r < 0)
+ return r;
+
+ if (ret)
+ *ret = TAKE_PTR(args);
+
+ return 0;
+}
+
int container_get_leader(const char *machine, pid_t *pid) {
_cleanup_free_ char *s = NULL, *class = NULL;
const char *p;
@@ -322,7 +387,34 @@ int container_get_leader(const char *machine, pid_t *pid) {
return 0;
}
-int is_kernel_thread(pid_t pid) {
+int namespace_get_leader(pid_t pid, NamespaceType type, pid_t *ret) {
+ int r;
+
+ assert(ret);
+
+ for (;;) {
+ pid_t ppid;
+
+ r = get_process_ppid(pid, &ppid);
+ if (r < 0)
+ return r;
+
+ r = in_same_namespace(pid, ppid, type);
+ if (r < 0)
+ return r;
+ if (r == 0) {
+ /* If the parent and the child are not in the same
+ * namespace, then the child is the leader we are
+ * looking for. */
+ *ret = pid;
+ return 0;
+ }
+
+ pid = ppid;
+ }
+}
+
+int pid_is_kernel_thread(pid_t pid) {
_cleanup_free_ char *line = NULL;
unsigned long long flags;
size_t l, i;
@@ -380,6 +472,23 @@ int is_kernel_thread(pid_t pid) {
return !!(flags & PF_KTHREAD);
}
+int pidref_is_kernel_thread(const PidRef *pid) {
+ int result, r;
+
+ if (!pidref_is_set(pid))
+ return -ESRCH;
+
+ result = pid_is_kernel_thread(pid->pid);
+ if (result < 0)
+ return result;
+
+ r = pidref_verify(pid); /* Verify that the PID wasn't reused since */
+ if (r < 0)
+ return r;
+
+ return result;
+}
+
int get_process_capeff(pid_t pid, char **ret) {
const char *p;
int r;
@@ -449,16 +558,14 @@ static int get_process_id(pid_t pid, const char *field, uid_t *ret) {
_cleanup_free_ char *line = NULL;
char *l;
- r = read_line(f, LONG_LINE_MAX, &line);
+ r = read_stripped_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 = startswith(line, field);
+ if (l) {
l += strspn(l, WHITESPACE);
l[strcspn(l, WHITESPACE)] = 0;
@@ -470,7 +577,8 @@ static int get_process_id(pid_t pid, const char *field, uid_t *ret) {
return -EIO;
}
-int get_process_uid(pid_t pid, uid_t *ret) {
+int pid_get_uid(pid_t pid, uid_t *ret) {
+ assert(ret);
if (pid == 0 || pid == getpid_cached()) {
*ret = getuid();
@@ -480,6 +588,26 @@ int get_process_uid(pid_t pid, uid_t *ret) {
return get_process_id(pid, "Uid:", ret);
}
+int pidref_get_uid(const PidRef *pid, uid_t *ret) {
+ uid_t uid;
+ int r;
+
+ if (!pidref_is_set(pid))
+ return -ESRCH;
+
+ r = pid_get_uid(pid->pid, &uid);
+ if (r < 0)
+ return r;
+
+ r = pidref_verify(pid);
+ if (r < 0)
+ return r;
+
+ if (ret)
+ *ret = uid;
+ return 0;
+}
+
int get_process_gid(pid_t pid, gid_t *ret) {
if (pid == 0 || pid == getpid_cached()) {
@@ -606,6 +734,82 @@ int get_process_ppid(pid_t pid, pid_t *ret) {
return 0;
}
+int pid_get_start_time(pid_t pid, uint64_t *ret) {
+ _cleanup_free_ char *line = NULL;
+ const char *p;
+ int r;
+
+ assert(pid >= 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++;
+
+ unsigned long llu;
+
+ if (sscanf(p, " "
+ "%*c " /* state */
+ "%*u " /* ppid */
+ "%*u " /* pgrp */
+ "%*u " /* session */
+ "%*u " /* tty_nr */
+ "%*u " /* tpgid */
+ "%*u " /* flags */
+ "%*u " /* minflt */
+ "%*u " /* cminflt */
+ "%*u " /* majflt */
+ "%*u " /* cmajflt */
+ "%*u " /* utime */
+ "%*u " /* stime */
+ "%*u " /* cutime */
+ "%*u " /* cstime */
+ "%*i " /* priority */
+ "%*i " /* nice */
+ "%*u " /* num_threads */
+ "%*u " /* itrealvalue */
+ "%lu ", /* starttime */
+ &llu) != 1)
+ return -EIO;
+
+ if (ret)
+ *ret = llu;
+
+ return 0;
+}
+
+int pidref_get_start_time(const PidRef *pid, uint64_t *ret) {
+ uint64_t t;
+ int r;
+
+ if (!pidref_is_set(pid))
+ return -ESRCH;
+
+ r = pid_get_start_time(pid->pid, ret ? &t : NULL);
+ if (r < 0)
+ return r;
+
+ r = pidref_verify(pid);
+ if (r < 0)
+ return r;
+
+ if (ret)
+ *ret = t;
+
+ return 0;
+}
+
int get_process_umask(pid_t pid, mode_t *ret) {
_cleanup_free_ char *m = NULL;
const char *p;
@@ -670,7 +874,7 @@ int wait_for_terminate_and_check(const char *name, pid_t pid, WaitFlags flags) {
assert(pid > 1);
if (!name) {
- r = get_process_comm(pid, &buffer);
+ r = pid_get_comm(pid, &buffer);
if (r < 0)
log_debug_errno(r, "Failed to acquire process name of " PID_FMT ", ignoring: %m", pid);
else
@@ -824,7 +1028,7 @@ 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;
+ size_t sum = 0;
int r;
assert(pid >= 0);
@@ -859,9 +1063,9 @@ int getenv_for_pid(pid_t pid, const char *field, char **ret) {
if (r < 0)
return r;
- l = strlen(field);
for (;;) {
_cleanup_free_ char *line = NULL;
+ const char *match;
if (sum > ENVIRONMENT_BLOCK_MAX) /* Give up searching eventually */
return -ENOBUFS;
@@ -874,8 +1078,9 @@ int getenv_for_pid(pid_t pid, const char *field, char **ret) {
sum += r;
- if (strneq(line, field, l) && line[l] == '=') {
- value = strdup(line + l + 1);
+ match = startswith(line, field);
+ if (match && *match == '=') {
+ value = strdup(match + 1);
if (!value)
return -ENOMEM;
@@ -892,6 +1097,9 @@ int pid_is_my_child(pid_t pid) {
pid_t ppid;
int r;
+ if (pid < 0)
+ return -ESRCH;
+
if (pid <= 1)
return false;
@@ -902,11 +1110,28 @@ int pid_is_my_child(pid_t pid) {
return ppid == getpid_cached();
}
-bool pid_is_unwaited(pid_t pid) {
+int pidref_is_my_child(const PidRef *pid) {
+ int r, result;
+
+ if (!pidref_is_set(pid))
+ return -ESRCH;
+
+ result = pid_is_my_child(pid->pid);
+ if (result < 0)
+ return result;
+
+ r = pidref_verify(pid);
+ if (r < 0)
+ return r;
+
+ return result;
+}
+
+int pid_is_unwaited(pid_t pid) {
/* Checks whether a PID is still valid at all, including a zombie */
if (pid < 0)
- return false;
+ return -ESRCH;
if (pid <= 1) /* If we or PID 1 would be dead and have been waited for, this code would not be running */
return true;
@@ -920,13 +1145,31 @@ bool pid_is_unwaited(pid_t pid) {
return errno != ESRCH;
}
-bool pid_is_alive(pid_t pid) {
+int pidref_is_unwaited(const PidRef *pid) {
+ int r;
+
+ if (!pidref_is_set(pid))
+ return -ESRCH;
+
+ if (pid->pid == 1 || pidref_is_self(pid))
+ return true;
+
+ r = pidref_kill(pid, 0);
+ if (r == -ESRCH)
+ return false;
+ if (r < 0)
+ return r;
+
+ return true;
+}
+
+int pid_is_alive(pid_t pid) {
int r;
/* Checks whether a PID is still valid and not a zombie */
if (pid < 0)
- return false;
+ return -ESRCH;
if (pid <= 1) /* If we or PID 1 would be a zombie, this code would not be running */
return true;
@@ -935,10 +1178,33 @@ bool pid_is_alive(pid_t pid) {
return true;
r = get_process_state(pid);
- if (IN_SET(r, -ESRCH, 'Z'))
+ if (r == -ESRCH)
return false;
+ if (r < 0)
+ return r;
- return true;
+ return r != 'Z';
+}
+
+int pidref_is_alive(const PidRef *pidref) {
+ int r, result;
+
+ if (!pidref_is_set(pidref))
+ return -ESRCH;
+
+ result = pid_is_alive(pidref->pid);
+ if (result < 0) {
+ assert(result != -ESRCH);
+ return result;
+ }
+
+ r = pidref_verify(pidref);
+ if (r == -ESRCH)
+ return false;
+ if (r < 0)
+ return r;
+
+ return result;
}
int pid_from_same_root_fs(pid_t pid) {
@@ -1057,7 +1323,10 @@ void valgrind_summary_hack(void) {
pid_t pid;
pid = raw_clone(SIGCHLD);
if (pid < 0)
- log_emergency_errno(errno, "Failed to fork off valgrind helper: %m");
+ log_struct_errno(
+ LOG_EMERG, errno,
+ "MESSAGE_ID=" SD_MESSAGE_VALGRIND_HELPER_FORK_STR,
+ LOG_MESSAGE( "Failed to fork off valgrind helper: %m"));
else if (pid == 0)
exit(EXIT_SUCCESS);
else {
@@ -1104,7 +1373,7 @@ pid_t getpid_cached(void) {
* https://sourceware.org/git/gitweb.cgi?p=glibc.git;h=c579f48edba88380635ab98cb612030e3ed8691e
*/
- __atomic_compare_exchange_n(
+ (void) __atomic_compare_exchange_n(
&cached_pid,
&current_value,
CACHED_PID_BUSY,
@@ -1159,6 +1428,51 @@ static void restore_sigsetp(sigset_t **ssp) {
(void) sigprocmask(SIG_SETMASK, *ssp, NULL);
}
+pid_t clone_with_nested_stack(int (*fn)(void *), int flags, void *userdata) {
+ size_t ps;
+ pid_t pid;
+ void *mystack;
+
+ /* A wrapper around glibc's clone() call that automatically sets up a "nested" stack. Only supports
+ * invocations without CLONE_VM, so that we can continue to use the parent's stack mapping.
+ *
+ * Note: glibc's clone() wrapper does not synchronize malloc() locks. This means that if the parent
+ * is threaded these locks will be in an undefined state in the child, and hence memory allocations
+ * are likely going to run into deadlocks. Hence: if you use this function make sure your parent is
+ * strictly single-threaded or your child never calls malloc(). */
+
+ assert((flags & (CLONE_VM|CLONE_PARENT_SETTID|CLONE_CHILD_SETTID|
+ CLONE_CHILD_CLEARTID|CLONE_SETTLS)) == 0);
+
+ /* We allocate some space on the stack to use as the stack for the child (hence "nested"). Note that
+ * the net effect is that the child will have the start of its stack inside the stack of the parent,
+ * but since they are a CoW copy of each other that's fine. We allocate one page-aligned page. But
+ * since we don't want to deal with differences between systems where the stack grows backwards or
+ * forwards we'll allocate one more and place the stack address in the middle. Except that we also
+ * want it page aligned, hence we'll allocate one page more. Makes 3. */
+
+ ps = page_size();
+ mystack = alloca(ps*3);
+ mystack = (uint8_t*) mystack + ps; /* move pointer one page ahead since stacks usually grow backwards */
+ mystack = (void*) ALIGN_TO((uintptr_t) mystack, ps); /* align to page size (moving things further ahead) */
+
+#if HAVE_CLONE
+ pid = clone(fn, mystack, flags, userdata);
+#else
+ pid = __clone2(fn, mystack, ps, flags, userdata);
+#endif
+ if (pid < 0)
+ return -errno;
+
+ return pid;
+}
+
+static int fork_flags_to_signal(ForkFlags flags) {
+ return (flags & FORK_DEATHSIG_SIGTERM) ? SIGTERM :
+ (flags & FORK_DEATHSIG_SIGINT) ? SIGINT :
+ SIGKILL;
+}
+
int safe_fork_full(
const char *name,
const int stdio_fds[3],
@@ -1170,9 +1484,12 @@ int safe_fork_full(
pid_t original_pid, pid;
sigset_t saved_ss, ss;
_unused_ _cleanup_(restore_sigsetp) sigset_t *saved_ssp = NULL;
- bool block_signals = false, block_all = false;
+ bool block_signals = false, block_all = false, intermediary = false;
int prio, r;
+ assert(!FLAGS_SET(flags, FORK_DETACH) || !ret_pid);
+ assert(!FLAGS_SET(flags, FORK_DETACH|FORK_WAIT));
+
/* 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. */
@@ -1185,9 +1502,10 @@ int safe_fork_full(
fflush(stderr); /* This one shouldn't be necessary, stderr should be unbuffered anyway, but let's better be safe than sorry */
}
- 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. */
+ if (flags & (FORK_RESET_SIGNALS|FORK_DEATHSIG_SIGTERM|FORK_DEATHSIG_SIGINT)) {
+ /* 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. (Note that for
+ * FORK_DEATHSIG_SIGKILL we don't bother, since it cannot be blocked anyway.) */
assert_se(sigfillset(&ss) >= 0);
block_signals = block_all = true;
@@ -1206,17 +1524,47 @@ int safe_fork_full(
saved_ssp = &saved_ss;
}
- if ((flags & (FORK_NEW_MOUNTNS|FORK_NEW_USERNS)) != 0)
+ if (FLAGS_SET(flags, FORK_DETACH)) {
+ assert(!FLAGS_SET(flags, FORK_WAIT));
+ assert(!ret_pid);
+
+ /* Fork off intermediary child if needed */
+
+ r = is_reaper_process();
+ if (r < 0)
+ return log_full_errno(prio, r, "Failed to determine if we are a reaper process: %m");
+
+ if (!r) {
+ /* Not a reaper process, hence do a double fork() so we are reparented to one */
+
+ pid = fork();
+ if (pid < 0)
+ return log_full_errno(prio, errno, "Failed to fork off '%s': %m", strna(name));
+ if (pid > 0) {
+ log_debug("Successfully forked off intermediary '%s' as PID " PID_FMT ".", strna(name), pid);
+ return 1; /* return in the parent */
+ }
+
+ intermediary = true;
+ }
+ }
+
+ if ((flags & (FORK_NEW_MOUNTNS|FORK_NEW_USERNS|FORK_NEW_NETNS)) != 0)
pid = raw_clone(SIGCHLD|
(FLAGS_SET(flags, FORK_NEW_MOUNTNS) ? CLONE_NEWNS : 0) |
- (FLAGS_SET(flags, FORK_NEW_USERNS) ? CLONE_NEWUSER : 0));
+ (FLAGS_SET(flags, FORK_NEW_USERNS) ? CLONE_NEWUSER : 0) |
+ (FLAGS_SET(flags, FORK_NEW_NETNS) ? CLONE_NEWNET : 0));
else
pid = fork();
if (pid < 0)
return log_full_errno(prio, errno, "Failed to fork off '%s': %m", strna(name));
if (pid > 0) {
- /* We are in the parent process */
+ /* If we are in the intermediary process, exit now */
+ if (intermediary)
+ _exit(EXIT_SUCCESS);
+
+ /* We are in the parent process */
log_debug("Successfully forked off '%s' as PID " PID_FMT ".", strna(name), pid);
if (flags & FORK_WAIT) {
@@ -1259,8 +1607,8 @@ int safe_fork_full(
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) {
+ if (flags & (FORK_DEATHSIG_SIGTERM|FORK_DEATHSIG_SIGINT|FORK_DEATHSIG_SIGKILL))
+ if (prctl(PR_SET_PDEATHSIG, fork_flags_to_signal(flags)) < 0) {
log_full_errno(prio, errno, "Failed to set death signal: %m");
_exit(EXIT_FAILURE);
}
@@ -1285,7 +1633,7 @@ int safe_fork_full(
}
}
- if (flags & FORK_DEATHSIG) {
+ if (flags & (FORK_DEATHSIG_SIGTERM|FORK_DEATHSIG_SIGKILL|FORK_DEATHSIG_SIGINT)) {
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 */
@@ -1294,8 +1642,9 @@ int safe_fork_full(
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);
+ int sig = fork_flags_to_signal(flags);
+ log_debug("Parent died early, raising %s.", signal_to_string(sig));
+ (void) raise(sig);
_exit(EXIT_FAILURE);
}
}
@@ -1328,6 +1677,9 @@ int safe_fork_full(
log_full_errno(prio, r, "Failed to rearrange stdio fds: %m");
_exit(EXIT_FAILURE);
}
+
+ /* Turn off O_NONBLOCK on the fdio fds, in case it was left on */
+ stdio_disable_nonblock();
} else {
r = make_null_stdio();
if (r < 0) {
@@ -1389,6 +1741,30 @@ int safe_fork_full(
return 0;
}
+int pidref_safe_fork_full(
+ const char *name,
+ const int stdio_fds[3],
+ const int except_fds[],
+ size_t n_except_fds,
+ ForkFlags flags,
+ PidRef *ret_pid) {
+
+ pid_t pid;
+ int r, q;
+
+ assert(!FLAGS_SET(flags, FORK_WAIT));
+
+ r = safe_fork_full(name, stdio_fds, except_fds, n_except_fds, flags, &pid);
+ if (r < 0)
+ return r;
+
+ q = pidref_set_pid(ret_pid, pid);
+ if (q < 0) /* Let's not fail for this, no matter what, the process exists after all, and that's key */
+ *ret_pid = PIDREF_MAKE_FROM_PID(pid);
+
+ return r;
+}
+
int namespace_fork(
const char *outer_name,
const char *inner_name,
@@ -1411,7 +1787,7 @@ int namespace_fork(
r = safe_fork_full(outer_name,
NULL,
except_fds, n_except_fds,
- (flags|FORK_DEATHSIG) & ~(FORK_REOPEN_LOG|FORK_NEW_MOUNTNS|FORK_MOUNTNS_SLAVE), ret_pid);
+ (flags|FORK_DEATHSIG_SIGINT|FORK_DEATHSIG_SIGTERM|FORK_DEATHSIG_SIGKILL) & ~(FORK_REOPEN_LOG|FORK_NEW_MOUNTNS|FORK_MOUNTNS_SLAVE), ret_pid);
if (r < 0)
return r;
if (r == 0) {
@@ -1633,6 +2009,212 @@ int get_process_threads(pid_t pid) {
return n;
}
+int is_reaper_process(void) {
+ int b = 0;
+
+ /* Checks if we are running in a reaper process, i.e. if we are expected to deal with processes
+ * reparented to us. This simply checks if we are PID 1 or if PR_SET_CHILD_SUBREAPER was called. */
+
+ if (getpid_cached() == 1)
+ return true;
+
+ if (prctl(PR_GET_CHILD_SUBREAPER, (unsigned long) &b, 0UL, 0UL, 0UL) < 0)
+ return -errno;
+
+ return b != 0;
+}
+
+int make_reaper_process(bool b) {
+
+ if (getpid_cached() == 1) {
+
+ if (!b)
+ return -EINVAL;
+
+ return 0;
+ }
+
+ /* Some prctl()s insist that all 5 arguments are specified, others do not. Let's always specify all,
+ * to avoid any ambiguities */
+ if (prctl(PR_SET_CHILD_SUBREAPER, (unsigned long) b, 0UL, 0UL, 0UL) < 0)
+ return -errno;
+
+ return 0;
+}
+
+DEFINE_TRIVIAL_CLEANUP_FUNC_FULL(posix_spawnattr_t*, posix_spawnattr_destroy, NULL);
+
+int posix_spawn_wrapper(
+ const char *path,
+ char * const *argv,
+ char * const *envp,
+ const char *cgroup,
+ PidRef *ret_pidref) {
+
+ short flags = POSIX_SPAWN_SETSIGMASK|POSIX_SPAWN_SETSIGDEF;
+ posix_spawnattr_t attr;
+ sigset_t mask;
+ int r;
+
+ /* Forks and invokes 'path' with 'argv' and 'envp' using CLONE_VM and CLONE_VFORK, which means the
+ * caller will be blocked until the child either exits or exec's. The memory of the child will be
+ * fully shared with the memory of the parent, so that there are no copy-on-write or memory.max
+ * issues.
+ *
+ * Also, move the newly-created process into 'cgroup' through POSIX_SPAWN_SETCGROUP (clone3())
+ * if available. Note that CLONE_INTO_CGROUP is only supported on cgroup v2.
+ * returns 1: We're already in the right cgroup
+ * 0: 'cgroup' not specified or POSIX_SPAWN_SETCGROUP is not supported. The caller
+ * needs to call 'cg_attach' on their own */
+
+ assert(path);
+ assert(argv);
+ assert(ret_pidref);
+
+ assert_se(sigfillset(&mask) >= 0);
+
+ r = posix_spawnattr_init(&attr);
+ if (r != 0)
+ return -r; /* These functions return a positive errno on failure */
+
+ /* Initialization needs to succeed before we can set up a destructor. */
+ _unused_ _cleanup_(posix_spawnattr_destroyp) posix_spawnattr_t *attr_destructor = &attr;
+
+#if HAVE_PIDFD_SPAWN
+ _cleanup_close_ int cgroup_fd = -EBADF;
+
+ if (cgroup) {
+ _cleanup_free_ char *resolved_cgroup = NULL;
+
+ r = cg_get_path_and_check(
+ SYSTEMD_CGROUP_CONTROLLER,
+ cgroup,
+ /* suffix= */ NULL,
+ &resolved_cgroup);
+ if (r < 0)
+ return r;
+
+ cgroup_fd = open(resolved_cgroup, O_PATH|O_DIRECTORY|O_CLOEXEC);
+ if (cgroup_fd < 0)
+ return -errno;
+
+ r = posix_spawnattr_setcgroup_np(&attr, cgroup_fd);
+ if (r != 0)
+ return -r;
+
+ flags |= POSIX_SPAWN_SETCGROUP;
+ }
+#endif
+
+ r = posix_spawnattr_setflags(&attr, flags);
+ if (r != 0)
+ return -r;
+ r = posix_spawnattr_setsigmask(&attr, &mask);
+ if (r != 0)
+ return -r;
+
+#if HAVE_PIDFD_SPAWN
+ _cleanup_close_ int pidfd = -EBADF;
+
+ r = pidfd_spawn(&pidfd, path, NULL, &attr, argv, envp);
+ if (r == 0) {
+ r = pidref_set_pidfd_consume(ret_pidref, TAKE_FD(pidfd));
+ if (r < 0)
+ return r;
+
+ return FLAGS_SET(flags, POSIX_SPAWN_SETCGROUP);
+ }
+ if (!(ERRNO_IS_NOT_SUPPORTED(r) || ERRNO_IS_PRIVILEGE(r)))
+ return -r;
+
+ /* Compiled on a newer host, or seccomp&friends blocking clone3()? Fallback, but need to change the
+ * flags to remove the cgroup one, which is what redirects to clone3() */
+ flags &= ~POSIX_SPAWN_SETCGROUP;
+ r = posix_spawnattr_setflags(&attr, flags);
+ if (r != 0)
+ return -r;
+#endif
+
+ pid_t pid;
+ r = posix_spawn(&pid, path, NULL, &attr, argv, envp);
+ if (r != 0)
+ return -r;
+
+ r = pidref_set_pid(ret_pidref, pid);
+ if (r < 0)
+ return r;
+
+ return 0; /* We did not use CLONE_INTO_CGROUP so return 0, the caller will have to move the child */
+}
+
+int proc_dir_open(DIR **ret) {
+ DIR *d;
+
+ assert(ret);
+
+ d = opendir("/proc");
+ if (!d)
+ return -errno;
+
+ *ret = d;
+ return 0;
+}
+
+int proc_dir_read(DIR *d, pid_t *ret) {
+ assert(d);
+
+ for (;;) {
+ struct dirent *de;
+
+ errno = 0;
+ de = readdir_no_dot(d);
+ if (!de) {
+ if (errno != 0)
+ return -errno;
+
+ break;
+ }
+
+ if (!IN_SET(de->d_type, DT_DIR, DT_UNKNOWN))
+ continue;
+
+ if (parse_pid(de->d_name, ret) >= 0)
+ return 1;
+ }
+
+ if (ret)
+ *ret = 0;
+ return 0;
+}
+
+int proc_dir_read_pidref(DIR *d, PidRef *ret) {
+ int r;
+
+ assert(d);
+
+ for (;;) {
+ pid_t pid;
+
+ r = proc_dir_read(d, &pid);
+ if (r < 0)
+ return r;
+ if (r == 0)
+ break;
+
+ r = pidref_set_pid(ret, pid);
+ if (r == -ESRCH) /* gone by now? skip it */
+ continue;
+ if (r < 0)
+ return r;
+
+ return 1;
+ }
+
+ if (ret)
+ *ret = PIDREF_NULL;
+ return 0;
+}
+
static const char *const sigchld_code_table[] = {
[CLD_EXITED] = "exited",
[CLD_KILLED] = "killed",
diff --git a/src/libnm-systemd-shared/src/basic/process-util.h b/src/libnm-systemd-shared/src/basic/process-util.h
index 5cf5c7c6ec..a75c44cfad 100644
--- a/src/libnm-systemd-shared/src/basic/process-util.h
+++ b/src/libnm-systemd-shared/src/basic/process-util.h
@@ -14,6 +14,7 @@
#include "alloc-util.h"
#include "format-util.h"
#include "macro.h"
+#include "namespace-util.h"
#include "time-util.h"
#define procfs_file_alloca(pid, field) \
@@ -38,21 +39,29 @@ typedef enum ProcessCmdlineFlags {
PROCESS_CMDLINE_QUOTE_POSIX = 1 << 3,
} ProcessCmdlineFlags;
-int get_process_comm(pid_t pid, char **ret);
-int get_process_cmdline(pid_t pid, size_t max_columns, ProcessCmdlineFlags flags, char **ret);
-int get_process_cmdline_strv(pid_t pid, ProcessCmdlineFlags flags, char ***ret);
+int pid_get_comm(pid_t pid, char **ret);
+int pidref_get_comm(const PidRef *pid, char **ret);
+int pid_get_cmdline(pid_t pid, size_t max_columns, ProcessCmdlineFlags flags, char **ret);
+int pidref_get_cmdline(const PidRef *pid, size_t max_columns, ProcessCmdlineFlags flags, char **ret);
+int pid_get_cmdline_strv(pid_t pid, ProcessCmdlineFlags flags, char ***ret);
+int pidref_get_cmdline_strv(const PidRef *pid, ProcessCmdlineFlags flags, char ***ret);
int get_process_exe(pid_t pid, char **ret);
-int get_process_uid(pid_t pid, uid_t *ret);
+int pid_get_uid(pid_t pid, uid_t *ret);
+int pidref_get_uid(const PidRef *pid, uid_t *ret);
int get_process_gid(pid_t pid, gid_t *ret);
int get_process_capeff(pid_t pid, char **ret);
int get_process_cwd(pid_t pid, char **ret);
int get_process_root(pid_t pid, char **ret);
int get_process_environ(pid_t pid, char **ret);
int get_process_ppid(pid_t pid, pid_t *ret);
+int pid_get_start_time(pid_t pid, uint64_t *ret);
+int pidref_get_start_time(const PidRef* pid, uint64_t *ret);
int get_process_umask(pid_t pid, mode_t *ret);
int container_get_leader(const char *machine, pid_t *pid);
+int namespace_get_leader(pid_t pid, NamespaceType type, pid_t *ret);
+
int wait_for_terminate(pid_t pid, siginfo_t *status);
typedef enum WaitFlags {
@@ -74,13 +83,17 @@ void sigkill_nowaitp(pid_t *pid);
int kill_and_sigcont(pid_t pid, int sig);
-int is_kernel_thread(pid_t pid);
+int pid_is_kernel_thread(pid_t pid);
+int pidref_is_kernel_thread(const PidRef *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_alive(pid_t pid);
+int pidref_is_alive(const PidRef *pidref);
+int pid_is_unwaited(pid_t pid);
+int pidref_is_unwaited(const PidRef *pidref);
int pid_is_my_child(pid_t pid);
+int pidref_is_my_child(const PidRef *pidref);
int pid_from_same_root_fs(pid_t pid);
bool is_main_thread(void);
@@ -141,24 +154,34 @@ void reset_cached_pid(void);
int must_be_root(void);
+pid_t clone_with_nested_stack(int (*fn)(void *), int flags, void *userdata);
+
+/* 💣 Note that FORK_NEW_USERNS, FORK_NEW_MOUNTNS, or FORK_NEW_NETNS should not be called in threaded
+ * programs, because they cause us to use raw_clone() which does not synchronize the glibc malloc() locks,
+ * and thus will cause deadlocks if the parent uses threads and the child does memory allocations. Hence: if
+ * the parent is threaded these flags may not be used. These flags cannot be used if the parent uses threads
+ * or the child uses malloc(). 💣 */
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_SIGTERM = 1 << 2, /* Set PR_DEATHSIG in the child to SIGTERM */
FORK_DEATHSIG_SIGINT = 1 << 3, /* Set PR_DEATHSIG in the child to SIGINT */
- FORK_REARRANGE_STDIO = 1 << 4, /* Connect 0,1,2 to specified fds or /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_PRIVATE_TMP = 1 << 10, /* Mount new /tmp/ in the child (combine with FORK_NEW_MOUNTNS!) */
- FORK_RLIMIT_NOFILE_SAFE = 1 << 11, /* Set RLIMIT_NOFILE soft limit to 1K for select() compat */
- FORK_STDOUT_TO_STDERR = 1 << 12, /* Make stdout a copy of stderr */
- FORK_FLUSH_STDIO = 1 << 13, /* fflush() stdout (and stderr) before forking */
- FORK_NEW_USERNS = 1 << 14, /* Run child in its own user namespace */
- FORK_CLOEXEC_OFF = 1 << 15, /* In the child: turn off O_CLOEXEC on all fds in except_fds[] */
- FORK_KEEP_NOTIFY_SOCKET = 1 << 16, /* Unless this specified, $NOTIFY_SOCKET will be unset. */
+ FORK_DEATHSIG_SIGKILL = 1 << 4, /* Set PR_DEATHSIG in the child to SIGKILL */
+ FORK_REARRANGE_STDIO = 1 << 5, /* Connect 0,1,2 to specified fds or /dev/null */
+ FORK_REOPEN_LOG = 1 << 6, /* Reopen log connection */
+ FORK_LOG = 1 << 7, /* Log above LOG_DEBUG log level about failures */
+ FORK_WAIT = 1 << 8, /* Wait until child exited */
+ FORK_NEW_MOUNTNS = 1 << 9, /* Run child in its own mount namespace 💣 DO NOT USE IN THREADED PROGRAMS! 💣 */
+ FORK_MOUNTNS_SLAVE = 1 << 10, /* Make child's mount namespace MS_SLAVE */
+ FORK_PRIVATE_TMP = 1 << 11, /* Mount new /tmp/ in the child (combine with FORK_NEW_MOUNTNS!) */
+ FORK_RLIMIT_NOFILE_SAFE = 1 << 12, /* Set RLIMIT_NOFILE soft limit to 1K for select() compat */
+ FORK_STDOUT_TO_STDERR = 1 << 13, /* Make stdout a copy of stderr */
+ FORK_FLUSH_STDIO = 1 << 14, /* fflush() stdout (and stderr) before forking */
+ FORK_NEW_USERNS = 1 << 15, /* Run child in its own user namespace 💣 DO NOT USE IN THREADED PROGRAMS! 💣 */
+ FORK_CLOEXEC_OFF = 1 << 16, /* In the child: turn off O_CLOEXEC on all fds in except_fds[] */
+ FORK_KEEP_NOTIFY_SOCKET = 1 << 17, /* Unless this specified, $NOTIFY_SOCKET will be unset. */
+ FORK_DETACH = 1 << 18, /* Double fork if needed to ensure PID1/subreaper is parent */
+ FORK_NEW_NETNS = 1 << 19, /* Run child in its own network namespace 💣 DO NOT USE IN THREADED PROGRAMS! 💣 */
} ForkFlags;
int safe_fork_full(
@@ -173,6 +196,18 @@ static inline int safe_fork(const char *name, ForkFlags flags, pid_t *ret_pid) {
return safe_fork_full(name, NULL, NULL, 0, flags, ret_pid);
}
+int pidref_safe_fork_full(
+ const char *name,
+ const int stdio_fds[3],
+ const int except_fds[],
+ size_t n_except_fds,
+ ForkFlags flags,
+ PidRef *ret_pid);
+
+static inline int pidref_safe_fork(const char *name, ForkFlags flags, PidRef *ret_pid) {
+ return pidref_safe_fork_full(name, NULL, 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 set_oom_score_adjust(int value);
@@ -201,3 +236,17 @@ int setpriority_closest(int priority);
_noreturn_ void freeze(void);
int get_process_threads(pid_t pid);
+
+int is_reaper_process(void);
+int make_reaper_process(bool b);
+
+int posix_spawn_wrapper(
+ const char *path,
+ char * const *argv,
+ char * const *envp,
+ const char *cgroup,
+ PidRef *ret_pidref);
+
+int proc_dir_open(DIR **ret);
+int proc_dir_read(DIR *d, pid_t *ret);
+int proc_dir_read_pidref(DIR *d, PidRef *ret);
diff --git a/src/libnm-systemd-shared/src/basic/random-util.c b/src/libnm-systemd-shared/src/basic/random-util.c
index 934d5e2531..c7b9551646 100644
--- a/src/libnm-systemd-shared/src/basic/random-util.c
+++ b/src/libnm-systemd-shared/src/basic/random-util.c
@@ -6,7 +6,6 @@
#include <errno.h>
#include <fcntl.h>
#include <linux/random.h>
-#include <pthread.h>
#include <stdbool.h>
#include <stdint.h>
#include <stdlib.h>
@@ -28,6 +27,7 @@
#include "missing_syscall.h"
#include "missing_threads.h"
#include "parse-util.h"
+#include "process-util.h"
#include "random-util.h"
#include "sha256.h"
#include "time-util.h"
@@ -50,7 +50,7 @@ static void fallback_random_bytes(void *p, size_t n) {
.call_id = fallback_counter++,
.stamp_mono = now(CLOCK_MONOTONIC),
.stamp_real = now(CLOCK_REALTIME),
- .pid = getpid(),
+ .pid = getpid_cached(),
.tid = gettid(),
};
@@ -226,7 +226,7 @@ int random_write_entropy(int fd, const void *seed, size_t size, bool credit) {
if (ioctl(fd, RNDADDENTROPY, info) < 0)
return -errno;
} else {
- r = loop_write(fd, seed, size, false);
+ r = loop_write(fd, seed, size);
if (r < 0)
return r;
}
diff --git a/src/libnm-systemd-shared/src/basic/ratelimit.h b/src/libnm-systemd-shared/src/basic/ratelimit.h
index bb7160a895..492ea3b48d 100644
--- a/src/libnm-systemd-shared/src/basic/ratelimit.h
+++ b/src/libnm-systemd-shared/src/basic/ratelimit.h
@@ -12,6 +12,8 @@ typedef struct RateLimit {
usec_t begin;
} RateLimit;
+#define RATELIMIT_OFF (const RateLimit) { .interval = USEC_INFINITY, .burst = UINT_MAX }
+
static inline void ratelimit_reset(RateLimit *rl) {
rl->num = rl->begin = 0;
}
diff --git a/src/libnm-systemd-shared/src/basic/signal-util.c b/src/libnm-systemd-shared/src/basic/signal-util.c
index 270d397d50..4354edca7e 100644
--- a/src/libnm-systemd-shared/src/basic/signal-util.c
+++ b/src/libnm-systemd-shared/src/basic/signal-util.c
@@ -123,39 +123,39 @@ int sigprocmask_many(int how, sigset_t *old, ...) {
}
static const char *const static_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",
+ [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",
+ [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"
+ [SIGPROF] = "PROF",
+ [SIGWINCH] = "WINCH",
+ [SIGIO] = "IO",
+ [SIGPWR] = "PWR",
+ [SIGSYS] = "SYS"
};
DEFINE_PRIVATE_STRING_TABLE_LOOKUP(static_signal, int);
diff --git a/src/libnm-systemd-shared/src/basic/siphash24.h b/src/libnm-systemd-shared/src/basic/siphash24.h
index e46f3cc5be..f9867630af 100644
--- a/src/libnm-systemd-shared/src/basic/siphash24.h
+++ b/src/libnm-systemd-shared/src/basic/siphash24.h
@@ -52,15 +52,16 @@ siphash24 (const void *in, size_t inlen, const uint8_t k[16])
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))
+#define siphash24_compress_typesafe(in, state) \
+ siphash24_compress(&(in), sizeof(typeof(in)), (state))
static inline void siphash24_compress_boolean(bool in, struct siphash *state) {
- uint8_t i = in;
-
- siphash24_compress(&i, sizeof i, state);
+ siphash24_compress_byte(in, state);
}
static inline void siphash24_compress_usec_t(usec_t in, struct siphash *state) {
- siphash24_compress(&in, sizeof in, state);
+ uint64_t u = htole64(in);
+ siphash24_compress_typesafe(u, state);
}
static inline void siphash24_compress_safe(const void *in, size_t inlen, struct siphash *state) {
diff --git a/src/libnm-systemd-shared/src/basic/socket-util.c b/src/libnm-systemd-shared/src/basic/socket-util.c
index 9b411e07a2..df3e2c17c2 100644
--- a/src/libnm-systemd-shared/src/basic/socket-util.c
+++ b/src/libnm-systemd-shared/src/basic/socket-util.c
@@ -46,6 +46,11 @@
# define IDN_FLAGS 0
#endif
+/* From the kernel's include/net/scm.h */
+#ifndef SCM_MAX_FD
+# define SCM_MAX_FD 253
+#endif
+
static const char* const socket_address_type_table[] = {
[SOCK_STREAM] = "Stream",
[SOCK_DGRAM] = "Datagram",
@@ -547,7 +552,7 @@ int sockaddr_pretty(
} else {
if (path[path_len - 1] == '\0')
/* We expect a terminating NUL and don't print it */
- path_len --;
+ path_len--;
p = cescape_length(path, path_len);
}
@@ -628,28 +633,33 @@ int getsockname_pretty(int fd, char **ret) {
return sockaddr_pretty(&sa.sa, salen, false, true, ret);
}
-int socknameinfo_pretty(union sockaddr_union *sa, socklen_t salen, char **_ret) {
+int socknameinfo_pretty(const struct sockaddr *sa, socklen_t salen, char **ret) {
+ char host[NI_MAXHOST];
int r;
- char host[NI_MAXHOST], *ret;
- assert(_ret);
+ assert(sa);
+ assert(salen > sizeof(sa_family_t));
- r = getnameinfo(&sa->sa, salen, host, sizeof(host), NULL, 0, IDN_FLAGS);
+ r = getnameinfo(sa, salen, host, sizeof(host), /* service= */ NULL, /* service_len= */ 0, IDN_FLAGS);
if (r != 0) {
- int saved_errno = errno;
+ if (r == EAI_MEMORY)
+ return log_oom_debug();
+ if (r == EAI_SYSTEM)
+ log_debug_errno(errno, "getnameinfo() failed, ignoring: %m");
+ else
+ log_debug("getnameinfo() failed, ignoring: %s", gai_strerror(r));
- r = sockaddr_pretty(&sa->sa, salen, true, true, &ret);
- if (r < 0)
- return r;
+ return sockaddr_pretty(sa, salen, /* translate_ipv6= */ true, /* include_port= */ true, ret);
+ }
- log_debug_errno(saved_errno, "getnameinfo(%s) failed: %m", ret);
- } else {
- ret = strdup(host);
- if (!ret)
+ if (ret) {
+ char *copy = strdup(host);
+ if (!copy)
return -ENOMEM;
+
+ *ret = copy;
}
- *_ret = ret;
return 0;
}
@@ -874,13 +884,11 @@ bool address_label_valid(const char *p) {
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)
+ if (getsockopt(fd, SOL_SOCKET, SO_PEERCRED, &u, &n) < 0)
return -errno;
if (n != sizeof(struct ucred))
@@ -909,8 +917,10 @@ int getpeersec(int fd, char **ret) {
if (!s)
return -ENOMEM;
- if (getsockopt(fd, SOL_SOCKET, SO_PEERSEC, s, &n) >= 0)
+ if (getsockopt(fd, SOL_SOCKET, SO_PEERSEC, s, &n) >= 0) {
+ s[n] = 0;
break;
+ }
if (errno != ERANGE)
return -errno;
@@ -927,12 +937,16 @@ int getpeersec(int fd, char **ret) {
}
int getpeergroups(int fd, gid_t **ret) {
- socklen_t n = sizeof(gid_t) * 64;
+ socklen_t n = sizeof(gid_t) * 64U;
_cleanup_free_ gid_t *d = NULL;
assert(fd >= 0);
assert(ret);
+ long ngroups_max = sysconf(_SC_NGROUPS_MAX);
+ if (ngroups_max > 0)
+ n = MAX(n, sizeof(gid_t) * (socklen_t) ngroups_max);
+
for (;;) {
d = malloc(n);
if (!d)
@@ -950,7 +964,7 @@ int getpeergroups(int fd, gid_t **ret) {
assert_se(n % sizeof(gid_t) == 0);
n /= sizeof(gid_t);
- if ((socklen_t) (int) n != n)
+ if (n > INT_MAX)
return -E2BIG;
*ret = TAKE_PTR(d);
@@ -958,6 +972,68 @@ int getpeergroups(int fd, gid_t **ret) {
return (int) n;
}
+int getpeerpidfd(int fd) {
+ socklen_t n = sizeof(int);
+ int pidfd = -EBADF;
+
+ assert(fd >= 0);
+
+ if (getsockopt(fd, SOL_SOCKET, SO_PEERPIDFD, &pidfd, &n) < 0)
+ return -errno;
+
+ if (n != sizeof(int))
+ return -EIO;
+
+ return pidfd;
+}
+
+ssize_t send_many_fds_iov_sa(
+ int transport_fd,
+ int *fds_array, size_t n_fds_array,
+ const struct iovec *iov, size_t iovlen,
+ const struct sockaddr *sa, socklen_t len,
+ int flags) {
+
+ _cleanup_free_ struct cmsghdr *cmsg = NULL;
+ struct msghdr mh = {
+ .msg_name = (struct sockaddr*) sa,
+ .msg_namelen = len,
+ .msg_iov = (struct iovec *)iov,
+ .msg_iovlen = iovlen,
+ };
+ ssize_t k;
+
+ assert(transport_fd >= 0);
+ assert(fds_array || n_fds_array == 0);
+
+ /* The kernel will reject sending more than SCM_MAX_FD FDs at once */
+ if (n_fds_array > SCM_MAX_FD)
+ return -E2BIG;
+
+ /* We need either an FD array or data to send. If there's nothing, return an error. */
+ if (n_fds_array == 0 && !iov)
+ return -EINVAL;
+
+ if (n_fds_array > 0) {
+ mh.msg_controllen = CMSG_SPACE(sizeof(int) * n_fds_array);
+ mh.msg_control = cmsg = malloc(mh.msg_controllen);
+ if (!cmsg)
+ return -ENOMEM;
+
+ *cmsg = (struct cmsghdr) {
+ .cmsg_len = CMSG_LEN(sizeof(int) * n_fds_array),
+ .cmsg_level = SOL_SOCKET,
+ .cmsg_type = SCM_RIGHTS,
+ };
+ memcpy(CMSG_DATA(cmsg), fds_array, sizeof(int) * n_fds_array);
+ }
+ k = sendmsg(transport_fd, &mh, MSG_NOSIGNAL | flags);
+ if (k < 0)
+ return (ssize_t) -errno;
+
+ return k;
+}
+
ssize_t send_one_fd_iov_sa(
int transport_fd,
int fd,
@@ -1013,6 +1089,78 @@ int send_one_fd_sa(
return (int) send_one_fd_iov_sa(transport_fd, fd, NULL, 0, sa, len, flags);
}
+ssize_t receive_many_fds_iov(
+ int transport_fd,
+ struct iovec *iov, size_t iovlen,
+ int **ret_fds_array, size_t *ret_n_fds_array,
+ int flags) {
+
+ CMSG_BUFFER_TYPE(CMSG_SPACE(sizeof(int) * SCM_MAX_FD)) control;
+ struct msghdr mh = {
+ .msg_control = &control,
+ .msg_controllen = sizeof(control),
+ .msg_iov = iov,
+ .msg_iovlen = iovlen,
+ };
+ _cleanup_free_ int *fds_array = NULL;
+ size_t n_fds_array = 0;
+ struct cmsghdr *cmsg;
+ ssize_t k;
+
+ assert(transport_fd >= 0);
+ assert(ret_fds_array);
+ assert(ret_n_fds_array);
+
+ /*
+ * Receive many FDs via @transport_fd. We don't care for the transport-type. We retrieve all the FDs
+ * at once. This is best used in combination with send_many_fds().
+ */
+
+ k = recvmsg_safe(transport_fd, &mh, MSG_CMSG_CLOEXEC | flags);
+ if (k < 0)
+ return k;
+
+ CMSG_FOREACH(cmsg, &mh)
+ if (cmsg->cmsg_level == SOL_SOCKET && cmsg->cmsg_type == SCM_RIGHTS) {
+ size_t n = (cmsg->cmsg_len - CMSG_LEN(0)) / sizeof(int);
+
+ fds_array = GREEDY_REALLOC(fds_array, n_fds_array + n);
+ if (!fds_array) {
+ cmsg_close_all(&mh);
+ return -ENOMEM;
+ }
+
+ memcpy(fds_array + n_fds_array, CMSG_TYPED_DATA(cmsg, int), sizeof(int) * n);
+ n_fds_array += n;
+ }
+
+ if (n_fds_array == 0) {
+ cmsg_close_all(&mh);
+
+ /* If didn't receive an FD or any data, return an error. */
+ if (k == 0)
+ return -EIO;
+ }
+
+ *ret_fds_array = TAKE_PTR(fds_array);
+ *ret_n_fds_array = n_fds_array;
+
+ return k;
+}
+
+int receive_many_fds(int transport_fd, int **ret_fds_array, size_t *ret_n_fds_array, int flags) {
+ ssize_t k;
+
+ k = receive_many_fds_iov(transport_fd, NULL, 0, ret_fds_array, ret_n_fds_array, flags);
+ if (k == 0)
+ return 0;
+
+ /* k must be negative, since receive_many_fds_iov() only returns a positive value if data was received
+ * through the iov. */
+ assert(k < 0);
+ return (int) k;
+}
+
ssize_t receive_one_fd_iov(
int transport_fd,
struct iovec *iov, size_t iovlen,
@@ -1189,7 +1337,7 @@ void* cmsg_find_and_copy_data(struct msghdr *mh, int level, int type, void *buf,
assert(buf_len > 0);
/* This is similar to cmsg_find_data(), but copy the found data to buf. This should be typically used
- * when reading possibly unaligned data such as timestamp, as time_t is 64bit and size_t is 32bit on
+ * when reading possibly unaligned data such as timestamp, as time_t is 64-bit and size_t is 32-bit on
* RISCV32. See issue #27241. */
cmsg = cmsg_find(mh, level, type, CMSG_LEN(buf_len));
@@ -1532,6 +1680,50 @@ int socket_address_parse_unix(SocketAddress *ret_address, const char *s) {
}
#if 0 /* NM_IGNORED */
+int vsock_parse_port(const char *s, unsigned *ret) {
+ int r;
+
+ assert(ret);
+
+ if (!s)
+ return -EINVAL;
+
+ unsigned u;
+ r = safe_atou(s, &u);
+ if (r < 0)
+ return r;
+
+ /* Port 0 is apparently valid and not special in AF_VSOCK (unlike on IP). But VMADDR_PORT_ANY
+ * (UINT32_MAX) is. Hence refuse that. */
+
+ if (u == VMADDR_PORT_ANY)
+ return -EINVAL;
+
+ *ret = u;
+ return 0;
+}
+
+int vsock_parse_cid(const char *s, unsigned *ret) {
+ assert(ret);
+
+ if (!s)
+ return -EINVAL;
+
+ /* Parsed an AF_VSOCK "CID". This is a 32bit entity, and the usual type is "unsigned". We recognize
+ * the three special CIDs as strings, and otherwise parse the numeric CIDs. */
+
+ if (streq(s, "hypervisor"))
+ *ret = VMADDR_CID_HYPERVISOR;
+ else if (streq(s, "local"))
+ *ret = VMADDR_CID_LOCAL;
+ else if (streq(s, "host"))
+ *ret = VMADDR_CID_HOST;
+ else
+ return safe_atou(s, ret);
+
+ return 0;
+}
+
int socket_address_parse_vsock(SocketAddress *ret_address, const char *s) {
/* AF_VSOCK socket in vsock:cid:port notation */
_cleanup_free_ char *n = NULL;
@@ -1557,7 +1749,7 @@ int socket_address_parse_vsock(SocketAddress *ret_address, const char *s) {
if (!e)
return -EINVAL;
- r = safe_atou(e+1, &port);
+ r = vsock_parse_port(e+1, &port);
if (r < 0)
return r;
@@ -1568,15 +1760,15 @@ int socket_address_parse_vsock(SocketAddress *ret_address, const char *s) {
if (isempty(n))
cid = VMADDR_CID_ANY;
else {
- r = safe_atou(n, &cid);
+ r = vsock_parse_cid(n, &cid);
if (r < 0)
return r;
}
*ret_address = (SocketAddress) {
.sockaddr.vm = {
- .svm_cid = cid,
.svm_family = AF_VSOCK,
+ .svm_cid = cid,
.svm_port = port,
},
.type = type,
@@ -1585,4 +1777,19 @@ int socket_address_parse_vsock(SocketAddress *ret_address, const char *s) {
return 0;
}
+
+int vsock_get_local_cid(unsigned *ret) {
+ _cleanup_close_ int vsock_fd = -EBADF;
+
+ assert(ret);
+
+ vsock_fd = open("/dev/vsock", O_RDONLY|O_CLOEXEC);
+ if (vsock_fd < 0)
+ return log_debug_errno(errno, "Failed to open /dev/vsock: %m");
+
+ if (ioctl(vsock_fd, IOCTL_VM_SOCKETS_GET_LOCAL_CID, ret) < 0)
+ return log_debug_errno(errno, "Failed to query local AF_VSOCK CID: %m");
+
+ return 0;
+}
#endif /* NM_IGNORED */
diff --git a/src/libnm-systemd-shared/src/basic/socket-util.h b/src/libnm-systemd-shared/src/basic/socket-util.h
index 26f9636fa6..15c7d1c52f 100644
--- a/src/libnm-systemd-shared/src/basic/socket-util.h
+++ b/src/libnm-systemd-shared/src/basic/socket-util.h
@@ -115,7 +115,7 @@ int sockaddr_pretty(const struct sockaddr *_sa, socklen_t salen, bool translate_
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);
+int socknameinfo_pretty(const struct sockaddr *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_;
@@ -154,7 +154,30 @@ 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);
+int getpeerpidfd(int fd);
+ssize_t send_many_fds_iov_sa(
+ int transport_fd,
+ int *fds_array, size_t n_fds_array,
+ const struct iovec *iov, size_t iovlen,
+ const struct sockaddr *sa, socklen_t len,
+ int flags);
+static inline ssize_t send_many_fds_iov(
+ int transport_fd,
+ int *fds_array, size_t n_fds_array,
+ const struct iovec *iov, size_t iovlen,
+ int flags) {
+
+ return send_many_fds_iov_sa(transport_fd, fds_array, n_fds_array, iov, iovlen, NULL, 0, flags);
+}
+static inline int send_many_fds(
+ int transport_fd,
+ int *fds_array,
+ size_t n_fds_array,
+ int flags) {
+
+ return send_many_fds_iov_sa(transport_fd, fds_array, n_fds_array, NULL, 0, NULL, 0, flags);
+}
ssize_t send_one_fd_iov_sa(
int transport_fd,
int fd,
@@ -169,6 +192,8 @@ int send_one_fd_sa(int transport_fd,
#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 receive_many_fds_iov(int transport_fd, struct iovec *iov, size_t iovlen, int **ret_fds_array, size_t *ret_n_fds_array, int flags);
+int receive_many_fds(int transport_fd, int **ret_fds_array, size_t *ret_n_fds_array, int flags);
ssize_t next_datagram_size_fd(int fd);
@@ -181,7 +206,7 @@ int flush_accept(int fd);
* at compile time, that the requested type has a smaller or same alignment as 'struct cmsghdr', and one
* during runtime, that the actual pointer matches the alignment too. This is supposed to catch cases such as
* 'struct timeval' is embedded into 'struct cmsghdr' on architectures where the alignment of the former is 8
- * bytes (because of a 64bit time_t), but of the latter is 4 bytes (because size_t is 32bit), such as
+ * bytes (because of a 64-bit time_t), but of the latter is 4 bytes (because size_t is 32 bits), such as
* riscv32. */
#define CMSG_TYPED_DATA(cmsg, type) \
({ \
@@ -296,7 +321,7 @@ static inline int getsockopt_int(int fd, int level, int optname, int *ret) {
int socket_bind_to_ifname(int fd, const char *ifname);
int socket_bind_to_ifindex(int fd, int ifindex);
-/* Define a 64bit version of timeval/timespec in any case, even on 32bit userspace. */
+/* Define a 64-bit version of timeval/timespec in any case, even on 32-bit userspace. */
struct timeval_large {
uint64_t tvl_sec, tvl_usec;
};
@@ -304,7 +329,7 @@ struct timespec_large {
uint64_t tvl_sec, tvl_nsec;
};
-/* glibc duplicates timespec/timeval on certain 32bit archs, once in 32bit and once in 64bit.
+/* glibc duplicates timespec/timeval on certain 32-bit arches, once in 32-bit and once in 64-bit.
* See __convert_scm_timestamps() in glibc source code. Hence, we need additional buffer space for them
* to prevent from recvmsg_safe() returning -EXFULL. */
#define CMSG_SPACE_TIMEVAL \
@@ -353,6 +378,14 @@ int socket_get_mtu(int fd, int af, size_t *ret);
int connect_unix_path(int fd, int dir_fd, const char *path);
+static inline bool VSOCK_CID_IS_REGULAR(unsigned cid) {
+ /* 0, 1, 2, UINT32_MAX are special, refuse those */
+ return cid > 2 && cid < UINT32_MAX;
+}
+
+int vsock_parse_port(const char *s, unsigned *ret);
+int vsock_parse_cid(const char *s, unsigned *ret);
+
/* Parses AF_UNIX and AF_VSOCK addresses. AF_INET[6] require some netlink calls, so it cannot be in
* src/basic/ and is done from 'socket_local_address from src/shared/. Return -EPROTO in case of
* protocol mismatch. */
@@ -365,3 +398,5 @@ int socket_address_parse_vsock(SocketAddress *ret_address, const char *s);
* /proc/sys/net/core/somaxconn anyway, thus by setting this to unbounded we just make that sysctl file
* authoritative. */
#define SOMAXCONN_DELUXE INT_MAX
+
+int vsock_get_local_cid(unsigned *ret);
diff --git a/src/libnm-systemd-shared/src/basic/sort-util.h b/src/libnm-systemd-shared/src/basic/sort-util.h
index 52d611b820..9c818bd747 100644
--- a/src/libnm-systemd-shared/src/basic/sort-util.h
+++ b/src/libnm-systemd-shared/src/basic/sort-util.h
@@ -18,7 +18,7 @@ void *xbsearch_r(const void *key, const void *base, size_t nmemb, size_t size,
({ \
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]), (comparison_userdata_fn_t) _func_, userdata); \
+ (typeof((b)[0])*) xbsearch_r((const void*) _k, (b), (n), sizeof((b)[0]), (comparison_userdata_fn_t) _func_, userdata); \
})
/**
@@ -38,7 +38,7 @@ static inline void* bsearch_safe(const void *key, const void *base,
({ \
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]), (comparison_fn_t) _func_); \
+ (typeof((b)[0])*) bsearch_safe((const void*) _k, (b), (n), sizeof((b)[0]), (comparison_fn_t) _func_); \
})
/**
@@ -61,7 +61,6 @@ static inline void _qsort_safe(void *base, size_t nmemb, size_t size, comparison
_qsort_safe((p), (n), sizeof((p)[0]), (comparison_fn_t) _func_); \
})
-#if 0 /* NM_IGNORED */
static inline void qsort_r_safe(void *base, size_t nmemb, size_t size, comparison_userdata_fn_t compar, void *userdata) {
if (nmemb <= 1)
return;
@@ -75,6 +74,6 @@ static inline void qsort_r_safe(void *base, size_t nmemb, size_t size, compariso
int (*_func_)(const typeof((p)[0])*, const typeof((p)[0])*, typeof(userdata)) = func; \
qsort_r_safe((p), (n), sizeof((p)[0]), (comparison_userdata_fn_t) _func_, userdata); \
})
-#endif /* NM_IGNORED */
int cmp_int(const int *a, const int *b);
+int cmp_uint16(const uint16_t *a, const uint16_t *b);
diff --git a/src/libnm-systemd-shared/src/basic/stat-util.c b/src/libnm-systemd-shared/src/basic/stat-util.c
index a81ee468ff..b50544c9f3 100644
--- a/src/libnm-systemd-shared/src/basic/stat-util.c
+++ b/src/libnm-systemd-shared/src/basic/stat-util.c
@@ -193,14 +193,12 @@ int inode_same_at(int fda, const char *filea, int fdb, const char *fileb, int fl
struct stat a, b;
assert(fda >= 0 || fda == AT_FDCWD);
- assert(filea);
assert(fdb >= 0 || fdb == AT_FDCWD);
- assert(fileb);
- if (fstatat(fda, filea, &a, flags) < 0)
+ if (fstatat(fda, strempty(filea), &a, flags) < 0)
return log_debug_errno(errno, "Cannot stat %s: %m", filea);
- if (fstatat(fdb, fileb, &b, flags) < 0)
+ if (fstatat(fdb, strempty(fileb), &b, flags) < 0)
return log_debug_errno(errno, "Cannot stat %s: %m", fileb);
return stat_inode_same(&a, &b);
@@ -405,21 +403,35 @@ bool statx_mount_same(const struct new_statx *a, const struct new_statx *b) {
a->stx_dev_minor == b->stx_dev_minor;
}
+static bool is_statx_fatal_error(int err, int flags) {
+ assert(err < 0);
+
+ /* If statx() is not supported or if we see EPERM (which might indicate seccomp filtering or so),
+ * let's do a fallback. Note that on EACCES we'll not fall back, since that is likely an indication of
+ * fs access issues, which we should propagate. */
+ if (ERRNO_IS_NOT_SUPPORTED(err) || err == -EPERM)
+ return false;
+
+ /* When unsupported flags are specified, glibc's fallback function returns -EINVAL.
+ * See statx_generic() in glibc. */
+ if (err != -EINVAL)
+ return true;
+
+ if ((flags & ~(AT_EMPTY_PATH | AT_NO_AUTOMOUNT | AT_SYMLINK_NOFOLLOW | AT_STATX_SYNC_AS_STAT)) != 0)
+ return false; /* Unsupported flags are specified. Let's try to use our implementation. */
+
+ return true;
+}
+
int statx_fallback(int dfd, const char *path, int flags, unsigned mask, struct statx *sx) {
static bool avoid_statx = false;
struct stat st;
+ int r;
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;
+ r = RET_NERRNO(statx(dfd, path, flags, mask, sx));
+ if (r >= 0 || is_statx_fatal_error(r, flags))
+ return r;
avoid_statx = true;
}
@@ -477,8 +489,8 @@ int xstatfsat(int dir_fd, const char *path, struct statfs *ret) {
#if 0 /* NM_IGNORED */
void inode_hash_func(const struct stat *q, struct siphash *state) {
- siphash24_compress(&q->st_dev, sizeof(q->st_dev), state);
- siphash24_compress(&q->st_ino, sizeof(q->st_ino), state);
+ siphash24_compress_typesafe(q->st_dev, state);
+ siphash24_compress_typesafe(q->st_ino, state);
}
int inode_compare_func(const struct stat *a, const struct stat *b) {
@@ -503,6 +515,8 @@ const char* inode_type_to_string(mode_t m) {
return "reg";
case S_IFDIR:
return "dir";
+ case S_IFLNK:
+ return "lnk";
case S_IFCHR:
return "chr";
case S_IFBLK:
@@ -515,4 +529,26 @@ const char* inode_type_to_string(mode_t m) {
return NULL;
}
+
+mode_t inode_type_from_string(const char *s) {
+ if (!s)
+ return MODE_INVALID;
+
+ if (streq(s, "reg"))
+ return S_IFREG;
+ if (streq(s, "dir"))
+ return S_IFDIR;
+ if (streq(s, "lnk"))
+ return S_IFLNK;
+ if (streq(s, "chr"))
+ return S_IFCHR;
+ if (streq(s, "blk"))
+ return S_IFBLK;
+ if (streq(s, "fifo"))
+ return S_IFIFO;
+ if (streq(s, "sock"))
+ return S_IFSOCK;
+
+ return MODE_INVALID;
+}
#endif /* NM_IGNORED */
diff --git a/src/libnm-systemd-shared/src/basic/stat-util.h b/src/libnm-systemd-shared/src/basic/stat-util.h
index ae0aaf8f51..dc11a85f62 100644
--- a/src/libnm-systemd-shared/src/basic/stat-util.h
+++ b/src/libnm-systemd-shared/src/basic/stat-util.h
@@ -12,6 +12,7 @@
#include "macro.h"
#include "missing_stat.h"
#include "siphash24.h"
+#include "time-util.h"
int is_symlink(const char *path);
int is_dir_full(int atfd, const char *fname, bool follow);
@@ -109,8 +110,16 @@ int xstatfsat(int dir_fd, const char *path, struct statfs *ret);
} var
#endif
+static inline usec_t statx_timestamp_load(const struct statx_timestamp *ts) {
+ return timespec_load(&(const struct timespec) { .tv_sec = ts->tv_sec, .tv_nsec = ts->tv_nsec });
+}
+static inline nsec_t statx_timestamp_load_nsec(const struct statx_timestamp *ts) {
+ return timespec_load_nsec(&(const struct timespec) { .tv_sec = ts->tv_sec, .tv_nsec = ts->tv_nsec });
+}
+
void inode_hash_func(const struct stat *q, struct siphash *state);
int inode_compare_func(const struct stat *a, const struct stat *b);
extern const struct hash_ops inode_hash_ops;
const char* inode_type_to_string(mode_t m);
+mode_t inode_type_from_string(const char *s);
diff --git a/src/libnm-systemd-shared/src/basic/string-util.c b/src/libnm-systemd-shared/src/basic/string-util.c
index 1afa49bba0..539c0c76fe 100644
--- a/src/libnm-systemd-shared/src/basic/string-util.c
+++ b/src/libnm-systemd-shared/src/basic/string-util.c
@@ -18,6 +18,7 @@
#include "macro.h"
#include "memory-util.h"
#include "memstream-util.h"
+#include "path-util.h"
#include "string-util.h"
#include "strv.h"
#include "terminal-util.h"
@@ -174,10 +175,15 @@ char *delete_trailing_chars(char *s, const char *bad) {
}
#endif /* NM_IGNORED */
-char *truncate_nl(char *s) {
+char *truncate_nl_full(char *s, size_t *ret_len) {
+ size_t n;
+
assert(s);
- s[strcspn(s, NEWLINE)] = 0;
+ n = strcspn(s, NEWLINE);
+ s[n] = '\0';
+ if (ret_len)
+ *ret_len = n;
return s;
}
@@ -294,6 +300,62 @@ static int write_ellipsis(char *buf, bool unicode) {
return 3;
}
+static size_t ansi_sequence_length(const char *s, size_t len) {
+ assert(s);
+
+ if (len < 2)
+ return 0;
+
+ if (s[0] != 0x1B) /* ASCII 27, aka ESC, aka Ctrl-[ */
+ return 0; /* Not the start of a sequence */
+
+ if (s[1] == 0x5B) { /* [, start of CSI sequence */
+ size_t i = 2;
+
+ if (i == len)
+ return 0;
+
+ while (s[i] >= 0x30 && s[i] <= 0x3F) /* Parameter bytes */
+ if (++i == len)
+ return 0;
+ while (s[i] >= 0x20 && s[i] <= 0x2F) /* Intermediate bytes */
+ if (++i == len)
+ return 0;
+ if (s[i] >= 0x40 && s[i] <= 0x7E) /* Final byte */
+ return i + 1;
+ return 0; /* Bad sequence */
+
+ } else if (s[1] >= 0x40 && s[1] <= 0x5F) /* other non-CSI Fe sequence */
+ return 2;
+
+ return 0; /* Bad escape? */
+}
+
+static bool string_has_ansi_sequence(const char *s, size_t len) {
+ const char *t = s;
+
+ while ((t = memchr(s, 0x1B, len - (t - s))))
+ if (ansi_sequence_length(t, len - (t - s)) > 0)
+ return true;
+ return false;
+}
+
+static size_t previous_ansi_sequence(const char *s, size_t length, const char **ret_where) {
+ /* Locate the previous ANSI sequence and save its start in *ret_where and return length. */
+
+ for (size_t i = length - 2; i > 0; i--) { /* -2 because at least two bytes are needed */
+ size_t slen = ansi_sequence_length(s + (i - 1), length - (i - 1));
+ if (slen == 0)
+ continue;
+
+ *ret_where = s + (i - 1);
+ return slen;
+ }
+
+ *ret_where = NULL;
+ return 0;
+}
+
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;
@@ -353,7 +415,6 @@ static char *ascii_ellipsize_mem(const char *s, size_t old_length, size_t new_le
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
@@ -377,73 +438,116 @@ char *ellipsize_mem(const char *s, size_t old_length, size_t new_length, unsigne
if (new_length == 0)
return strdup("");
- /* If no multibyte characters use ascii_ellipsize_mem for speed */
- if (ascii_is_valid_n(s, old_length))
+ bool has_ansi_seq = string_has_ansi_sequence(s, old_length);
+
+ /* If no multibyte characters or ANSI sequences, use ascii_ellipsize_mem for speed */
+ if (!has_ansi_seq && ascii_is_valid_n(s, old_length))
return ascii_ellipsize_mem(s, old_length, new_length, percent);
- x = ((new_length - 1) * percent) / 100;
+ 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;
+ for (i = s; i < s + old_length; ) {
+ size_t slen = has_ansi_seq ? ansi_sequence_length(i, old_length - (i - s)) : 0;
+ if (slen > 0) {
+ i += slen;
+ continue; /* ANSI sequences don't take up any space in output */
+ }
+ char32_t c;
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
+ int w = unichar_iswide(c) ? 2 : 1;
+ if (k + w > x)
break;
+
+ k += w;
+ i += r;
}
- for (j = s + old_length; j > i; ) {
+ const char *ansi_start = s + old_length;
+ size_t ansi_len = 0;
+
+ for (const char *t = j = s + old_length; t > i && k < new_length; ) {
char32_t c;
int w;
- const char *jj;
+ const char *tt;
- jj = utf8_prev_char(j);
- r = utf8_encoded_to_unichar(jj, &c);
+ if (has_ansi_seq && ansi_start >= t)
+ /* Figure out the previous ANSI sequence, if any */
+ ansi_len = previous_ansi_sequence(s, t - s, &ansi_start);
+
+ /* If the sequence extends all the way to the current position, skip it. */
+ if (has_ansi_seq && ansi_len > 0 && ansi_start + ansi_len == t) {
+ t = ansi_start;
+ continue;
+ }
+
+ tt = utf8_prev_char(t);
+ r = utf8_encoded_to_unichar(tt, &c);
if (r < 0)
return NULL;
w = unichar_iswide(c) ? 2 : 1;
- if (k + w <= new_length) {
- k += w;
- j = jj;
- } else
+ if (k + w > new_length)
break;
+
+ k += w;
+ j = t = tt; /* j should always point to the first "real" character */
}
- assert(i <= j);
- /* we don't actually need to ellipsize */
- if (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);
+ if (k >= new_length) {
+ /* Make space for ellipsis, if required and possible. We know that the edge character is not
+ * part of an ANSI sequence (because then we'd skip it). If the last character we looked at
+ * was wide, we don't need to make space. */
+ 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 we have ANSI, allow the same length as the source string + ellipsis. It'd be too involved to
+ * figure out what exact space is needed. Strings with ANSI sequences are most likely to be fairly
+ * short anyway. */
+ size_t alloc_len = has_ansi_seq ? old_length + 3 + 1 : len + 3 + len2 + 1;
+
+ char *e = new(char, alloc_len);
if (!e)
return NULL;
/*
- printf("old_length=%zu new_length=%zu x=%zu len=%u len2=%u k=%u\n",
+ printf("old_length=%zu new_length=%zu x=%zu len=%zu len2=%zu k=%zu\n",
old_length, new_length, x, len, len2, k);
*/
- memcpy(e, s, len);
+ memcpy_safe(e, s, len);
write_ellipsis(e + len, true);
- memcpy(e + len + 3, j, len2);
- *(e + len + 3 + len2) = '\0';
+
+ char *dst = e + len + 3;
+
+ if (has_ansi_seq)
+ /* Copy over any ANSI sequences in full */
+ for (const char *p = s + len; p < j; ) {
+ size_t slen = ansi_sequence_length(p, j - p);
+ if (slen > 0) {
+ dst = mempcpy(dst, p, slen);
+ p += slen;
+ } else
+ p = utf8_next_char(p);
+ }
+
+ memcpy_safe(dst, j, len2);
+ dst[len2] = '\0';
return e;
}
@@ -528,14 +632,23 @@ char* strshorten(char *s, size_t l) {
}
int strgrowpad0(char **s, size_t l) {
+ size_t sz;
+
assert(s);
+ if (*s) {
+ sz = strlen(*s) + 1;
+ if (sz >= l) /* never shrink */
+ return 0;
+ } else
+ sz = 0;
+
char *q = realloc(*s, l);
if (!q)
return -ENOMEM;
+
*s = q;
- size_t sz = strlen(*s);
memzero(*s + sz, l - sz);
return 0;
}
@@ -893,6 +1006,33 @@ oom:
return -ENOMEM;
}
+char *strextendn(char **x, const char *s, size_t l) {
+ assert(x);
+ assert(s || l == 0);
+
+ if (l == SIZE_MAX)
+ l = strlen_ptr(s);
+ else if (l > 0)
+ l = strnlen(s, l); /* ignore trailing noise */
+
+ if (l > 0 || !*x) {
+ size_t q;
+ char *m;
+
+ q = strlen_ptr(*x);
+ m = realloc(*x, q + l + 1);
+ if (!m)
+ return NULL;
+
+ memcpy_safe(m + q, s, l);
+ m[q + l] = 0;
+
+ *x = m;
+ }
+
+ return *x;
+}
+
char *strrep(const char *s, unsigned n) {
char *r, *p;
size_t l;
@@ -1191,6 +1331,7 @@ char *string_replace_char(char *str, char old_char, char new_char) {
return str;
}
+#endif /* NM_IGNORED */
int make_cstring(const char *s, size_t n, MakeCStringMode mode, char **ret) {
char *b;
@@ -1235,6 +1376,7 @@ int make_cstring(const char *s, size_t n, MakeCStringMode mode, char **ret) {
return 0;
}
+#if 0 /* NM_IGNORED */
size_t strspn_from_end(const char *str, const char *accept) {
size_t n = 0;
@@ -1290,14 +1432,109 @@ char *find_line_startswith(const char *haystack, const char *needle) {
}
#endif /* NM_IGNORED */
-char *startswith_strv(const char *string, char **strv) {
- char *found = NULL;
+bool version_is_valid(const char *s) {
+ if (isempty(s))
+ return false;
- STRV_FOREACH(i, strv) {
- found = startswith(string, *i);
- if (found)
- break;
+ if (!filename_part_is_valid(s))
+ return false;
+
+ /* This is a superset of the characters used by semver. We additionally allow "," and "_". */
+ if (!in_charset(s, ALPHANUMERICAL ".,_-+"))
+ return false;
+
+ return true;
+}
+
+bool version_is_valid_versionspec(const char *s) {
+ if (!filename_part_is_valid(s))
+ return false;
+
+ if (!in_charset(s, ALPHANUMERICAL "-.~^"))
+ return false;
+
+ return true;
+}
+
+ssize_t strlevenshtein(const char *x, const char *y) {
+ _cleanup_free_ size_t *t0 = NULL, *t1 = NULL, *t2 = NULL;
+ size_t xl, yl;
+
+ /* This is inspired from the Linux kernel's Levenshtein implementation */
+
+ if (streq_ptr(x, y))
+ return 0;
+
+ xl = strlen_ptr(x);
+ if (xl > SSIZE_MAX)
+ return -E2BIG;
+
+ yl = strlen_ptr(y);
+ if (yl > SSIZE_MAX)
+ return -E2BIG;
+
+ if (isempty(x))
+ return yl;
+ if (isempty(y))
+ return xl;
+
+ t0 = new0(size_t, yl + 1);
+ if (!t0)
+ return -ENOMEM;
+ t1 = new0(size_t, yl + 1);
+ if (!t1)
+ return -ENOMEM;
+ t2 = new0(size_t, yl + 1);
+ if (!t2)
+ return -ENOMEM;
+
+ for (size_t i = 0; i <= yl; i++)
+ t1[i] = i;
+
+ for (size_t i = 0; i < xl; i++) {
+ t2[0] = i + 1;
+
+ for (size_t j = 0; j < yl; j++) {
+ /* Substitution */
+ t2[j+1] = t1[j] + (x[i] != y[j]);
+
+ /* Swap */
+ if (i > 0 && j > 0 && x[i-1] == y[j] && x[i] == y[j-1] && t2[j+1] > t0[j-1] + 1)
+ t2[j+1] = t0[j-1] + 1;
+
+ /* Deletion */
+ if (t2[j+1] > t1[j+1] + 1)
+ t2[j+1] = t1[j+1] + 1;
+
+ /* Insertion */
+ if (t2[j+1] > t2[j] + 1)
+ t2[j+1] = t2[j] + 1;
+ }
+
+ size_t *dummy = t0;
+ t0 = t1;
+ t1 = t2;
+ t2 = dummy;
}
- return found;
+ return t1[yl];
+}
+
+char *strrstr(const char *haystack, const char *needle) {
+ /* Like strstr() but returns the last rather than the first occurrence of "needle" in "haystack". */
+
+ if (!haystack || !needle)
+ return NULL;
+
+ /* Special case: for the empty string we return the very last possible occurrence, i.e. *after* the
+ * last char, not before. */
+ if (*needle == 0)
+ return strchr(haystack, 0);
+
+ for (const char *p = strstr(haystack, needle), *q; p; p = q) {
+ q = strstr(p + 1, needle);
+ if (!q)
+ return (char *) p;
+ }
+ return NULL;
}
diff --git a/src/libnm-systemd-shared/src/basic/string-util.h b/src/libnm-systemd-shared/src/basic/string-util.h
index 4430910e22..e162765aa7 100644
--- a/src/libnm-systemd-shared/src/basic/string-util.h
+++ b/src/libnm-systemd-shared/src/basic/string-util.h
@@ -22,6 +22,9 @@
#define ALPHANUMERICAL LETTERS DIGITS
#define HEXDIGITS DIGITS "abcdefABCDEF"
#define LOWERCASE_HEXDIGITS DIGITS "abcdef"
+#define URI_RESERVED ":/?#[]@!$&'()*+;=" /* [RFC3986] */
+#define URI_UNRESERVED ALPHANUMERICAL "-._~" /* [RFC3986] */
+#define URI_VALID URI_RESERVED URI_UNRESERVED /* [RFC3986] */
static inline char* strstr_ptr(const char *haystack, const char *needle) {
if (!haystack || !needle)
@@ -65,6 +68,10 @@ static inline const char* enable_disable(bool b) {
return b ? "enable" : "disable";
}
+static inline const char* enabled_disabled(bool b) {
+ return b ? "enabled" : "disabled";
+}
+
/* This macro's return pointer will have the "const" qualifier set or unset the same way as the input
* pointer. */
#define empty_to_null(p) \
@@ -121,7 +128,10 @@ char *strjoin_real(const char *x, ...) _sentinel_;
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);
+char *truncate_nl_full(char *s, size_t *ret_len);
+static inline char *truncate_nl(char *s) {
+ return truncate_nl_full(s, NULL);
+}
static inline char *skip_leading_chars(const char *s, const char *bad) {
if (!s)
@@ -183,11 +193,24 @@ char *strextend_with_separator_internal(char **x, const char *separator, ...) _s
#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 *strextendn(char **x, const char *s, size_t l);
+
int strextendf_with_separator(char **x, const char *separator, const char *format, ...) _printf_(3,4);
#define strextendf(x, ...) strextendf_with_separator(x, NULL, __VA_ARGS__)
char *strrep(const char *s, unsigned n);
+#define strrepa(s, n) \
+ ({ \
+ char *_d_, *_p_; \
+ size_t _len_ = strlen(s) * n; \
+ _p_ = _d_ = newa(char, _len_ + 1); \
+ for (unsigned _i_ = 0; _i_ < n; _i_++) \
+ _p_ = stpcpy(_p_, s); \
+ *_p_ = 0; \
+ _d_; \
+ })
+
int split_pair(const char *s, const char *sep, char **l, char **r);
int free_and_strdup(char **p, const char *s);
@@ -268,7 +291,31 @@ char *strdupcspn(const char *a, const char *reject);
char *find_line_startswith(const char *haystack, const char *needle);
-char *startswith_strv(const char *string, char **strv);
+bool version_is_valid(const char *s);
+
+bool version_is_valid_versionspec(const char *s);
+
+ssize_t strlevenshtein(const char *x, const char *y);
+
+static inline int strdup_or_null(const char *s, char **ret) {
+ char *c;
+
+ assert(ret);
+
+ /* This is a lot like strdup(), but is happy with NULL strings, and does not treat that as error, but
+ * copies the NULL value. */
+
+ if (!s) {
+ *ret = NULL;
+ return 0;
+ }
+
+ c = strdup(s);
+ if (!c)
+ return -ENOMEM;
+
+ *ret = c;
+ return 1;
+}
-#define STARTSWITH_SET(p, ...) \
- startswith_strv(p, STRV_MAKE(__VA_ARGS__))
+char *strrstr(const char *haystack, const char *needle);
diff --git a/src/libnm-systemd-shared/src/basic/strv.c b/src/libnm-systemd-shared/src/basic/strv.c
index 9ad5330739..7c6de915b2 100644
--- a/src/libnm-systemd-shared/src/basic/strv.c
+++ b/src/libnm-systemd-shared/src/basic/strv.c
@@ -90,6 +90,15 @@ char** strv_free_erase(char **l) {
return mfree(l);
}
+void strv_free_many(char ***strvs, size_t n) {
+ assert(strvs || n == 0);
+
+ FOREACH_ARRAY (i, strvs, n)
+ strv_free(*i);
+
+ free(strvs);
+}
+
char** strv_copy_n(char * const *l, size_t m) {
_cleanup_strv_free_ char **result = NULL;
char **k;
@@ -116,6 +125,22 @@ char** strv_copy_n(char * const *l, size_t m) {
return TAKE_PTR(result);
}
+int strv_copy_unless_empty(char * const *l, char ***ret) {
+ assert(ret);
+
+ if (strv_isempty(l)) {
+ *ret = NULL;
+ return 0;
+ }
+
+ char **copy = strv_copy(l);
+ if (!copy)
+ return -ENOMEM;
+
+ *ret = TAKE_PTR(copy);
+ return 1;
+}
+
size_t strv_length(char * const *l) {
size_t n = 0;
@@ -214,9 +239,7 @@ int strv_extend_strv(char ***a, char * const *b, bool filter_duplicates) {
return (int) i;
rollback:
- for (size_t j = 0; j < i; j++)
- free(t[p + j]);
-
+ free_many_charp(t + p, i);
t[p] = NULL;
return -ENOMEM;
}
@@ -488,29 +511,31 @@ int strv_insert(char ***l, size_t position, char *value) {
char **c;
size_t n, m;
+ assert(l);
+
if (!value)
return 0;
n = strv_length(*l);
position = MIN(position, n);
- /* increase and check for overflow */
- m = n + 2;
- if (m < n)
+ /* check for overflow and increase*/
+ if (n > SIZE_MAX - 2)
return -ENOMEM;
+ m = n + 2;
- c = new(char*, m);
+ c = reallocarray(*l, GREEDY_ALLOC_ROUND_UP(m), sizeof(char*));
if (!c)
return -ENOMEM;
- for (size_t i = 0; i < position; i++)
- c[i] = (*l)[i];
+ if (n > position)
+ memmove(c + position + 1, c + position, (n - position) * sizeof(char*));
+
c[position] = value;
- for (size_t i = position; i < n; i++)
- c[i+1] = (*l)[i];
- c[n+1] = NULL;
+ c[n + 1] = NULL;
- return free_and_replace(*l, c);
+ *l = c;
+ return 0;
}
int strv_consume_with_size(char ***l, size_t *n, char *value) {
@@ -571,39 +596,63 @@ int strv_extend_with_size(char ***l, size_t *n, const char *value) {
return strv_consume_with_size(l, n, v);
}
-int strv_extend_front(char ***l, const char *value) {
+int strv_extend_many_internal(char ***l, const char *value, ...) {
+ va_list ap;
size_t n, m;
- char *v, **c;
+ int r;
assert(l);
- /* Like strv_extend(), but prepends rather than appends the new entry */
+ m = n = strv_length(*l);
- if (!value)
- return 0;
+ r = 0;
+ va_start(ap, value);
+ for (const char *s = value; s != POINTER_MAX; s = va_arg(ap, const char*)) {
+ if (!s)
+ continue;
- n = strv_length(*l);
+ if (m > SIZE_MAX-1) { /* overflow */
+ r = -ENOMEM;
+ break;
+ }
+ m++;
+ }
+ va_end(ap);
- /* Increase and overflow check. */
- m = n + 2;
- if (m < n)
+ if (r < 0)
+ return r;
+ if (m > SIZE_MAX-1)
return -ENOMEM;
- v = strdup(value);
- if (!v)
+ char **c = reallocarray(*l, GREEDY_ALLOC_ROUND_UP(m+1), sizeof(char*));
+ if (!c)
return -ENOMEM;
+ *l = c;
- c = reallocarray(*l, m, sizeof(char*));
- if (!c) {
- free(v);
- return -ENOMEM;
+ r = 0;
+ size_t i = n;
+ va_start(ap, value);
+ for (const char *s = value; s != POINTER_MAX; s = va_arg(ap, const char*)) {
+ if (!s)
+ continue;
+
+ c[i] = strdup(s);
+ if (!c[i]) {
+ r = -ENOMEM;
+ break;
+ }
+ i++;
}
+ va_end(ap);
- memmove(c+1, c, n * sizeof(char*));
- c[0] = v;
- c[n+1] = NULL;
+ if (r < 0) {
+ /* rollback on error */
+ for (size_t j = n; j < i; j++)
+ c[j] = mfree(c[j]);
+ return r;
+ }
- *l = c;
+ c[i] = NULL;
return 0;
}
@@ -707,6 +756,26 @@ int strv_extendf(char ***l, const char *format, ...) {
return strv_consume(l, x);
}
+char* startswith_strv(const char *s, char * const *l) {
+ STRV_FOREACH(i, l) {
+ char *found = startswith(s, *i);
+ if (found)
+ return found;
+ }
+
+ return NULL;
+}
+
+char* endswith_strv(const char *s, char * const *l) {
+ STRV_FOREACH(i, l) {
+ char *found = endswith(s, *i);
+ if (found)
+ return found;
+ }
+
+ return NULL;
+}
+
char** strv_reverse(char **l) {
size_t n;
@@ -835,13 +904,15 @@ int fputstrv(FILE *f, char * const *l, const char *separator, bool *space) {
bool b = false;
int r;
+ assert(f);
+
/* 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);
+ r = fputs_with_separator(f, *s, separator, space);
if (r < 0)
return r;
}
diff --git a/src/libnm-systemd-shared/src/basic/strv.h b/src/libnm-systemd-shared/src/basic/strv.h
index 544d46a3f8..91337b9287 100644
--- a/src/libnm-systemd-shared/src/basic/strv.h
+++ b/src/libnm-systemd-shared/src/basic/strv.h
@@ -32,10 +32,14 @@ char** strv_free_erase(char **l);
DEFINE_TRIVIAL_CLEANUP_FUNC(char**, strv_free_erase);
#define _cleanup_strv_free_erase_ _cleanup_(strv_free_erasep)
+void strv_free_many(char ***strvs, size_t n);
+
char** strv_copy_n(char * const *l, size_t n);
static inline char** strv_copy(char * const *l) {
return strv_copy_n(l, SIZE_MAX);
}
+int strv_copy_unless_empty(char * const *l, char ***ret);
+
size_t strv_length(char * const *l) _pure_;
int strv_extend_strv(char ***a, char * const *b, bool filter_duplicates);
@@ -51,8 +55,10 @@ static inline int strv_extend(char ***l, const char *value) {
return strv_extend_with_size(l, NULL, value);
}
+int strv_extend_many_internal(char ***l, const char *value, ...);
+#define strv_extend_many(l, ...) strv_extend_many_internal(l, __VA_ARGS__, POINTER_MAX)
+
int strv_extendf(char ***l, const char *format, ...) _printf_(2,3);
-int strv_extend_front(char ***l, const char *value);
int strv_push_with_size(char ***l, size_t *n, char *value);
static inline int strv_push(char ***l, char *value) {
@@ -157,6 +163,16 @@ static inline void strv_print(char * const *l) {
strv_print_full(l, NULL);
}
+char* startswith_strv(const char *s, char * const *l);
+
+#define STARTSWITH_SET(p, ...) \
+ startswith_strv(p, STRV_MAKE(__VA_ARGS__))
+
+char* endswith_strv(const char *s, char * const *l);
+
+#define ENDSWITH_SET(p, ...) \
+ endswith_strv(p, STRV_MAKE(__VA_ARGS__))
+
#define strv_from_stdarg_alloca(first) \
({ \
char **_l; \
@@ -200,18 +216,6 @@ static inline void strv_print(char * const *l) {
_x && strv_contains_case(STRV_MAKE(__VA_ARGS__), _x); \
})
-#define ENDSWITH_SET(p, ...) \
- ({ \
- const char *_p = (p); \
- char *_found = NULL; \
- STRV_FOREACH(_i, STRV_MAKE(__VA_ARGS__)) { \
- _found = endswith(_p, *_i); \
- if (_found) \
- break; \
- } \
- _found; \
- })
-
#define _FOREACH_STRING(uniq, x, y, ...) \
for (const char *x, * const*UNIQ_T(l, uniq) = STRV_MAKE_CONST(({ x = y; }), ##__VA_ARGS__); \
x; \
diff --git a/src/libnm-systemd-shared/src/basic/time-util.c b/src/libnm-systemd-shared/src/basic/time-util.c
index 092912b2b0..2147156e55 100644
--- a/src/libnm-systemd-shared/src/basic/time-util.c
+++ b/src/libnm-systemd-shared/src/basic/time-util.c
@@ -66,7 +66,7 @@ nsec_t now_nsec(clockid_t clock_id) {
return timespec_load_nsec(&ts);
}
-dual_timestamp* dual_timestamp_get(dual_timestamp *ts) {
+dual_timestamp* dual_timestamp_now(dual_timestamp *ts) {
assert(ts);
ts->realtime = now(CLOCK_REALTIME);
@@ -75,7 +75,7 @@ dual_timestamp* dual_timestamp_get(dual_timestamp *ts) {
return ts;
}
-triple_timestamp* triple_timestamp_get(triple_timestamp *ts) {
+triple_timestamp* triple_timestamp_now(triple_timestamp *ts) {
assert(ts);
ts->realtime = now(CLOCK_REALTIME);
@@ -158,6 +158,25 @@ triple_timestamp* triple_timestamp_from_realtime(triple_timestamp *ts, usec_t u)
return ts;
}
+triple_timestamp* triple_timestamp_from_boottime(triple_timestamp *ts, usec_t u) {
+ usec_t nowb;
+
+ assert(ts);
+
+ if (u == USEC_INFINITY) {
+ ts->realtime = ts->monotonic = ts->boottime = u;
+ return ts;
+ }
+
+ nowb = now(CLOCK_BOOTTIME);
+
+ ts->boottime = u;
+ ts->monotonic = map_clock_usec_internal(u, nowb, now(CLOCK_MONOTONIC));
+ ts->realtime = map_clock_usec_internal(u, nowb, now(CLOCK_REALTIME));
+
+ return ts;
+}
+
dual_timestamp* dual_timestamp_from_monotonic(dual_timestamp *ts, usec_t u) {
assert(ts);
@@ -330,7 +349,7 @@ char *format_timestamp_style(
if (l < (size_t) (1 + 1 + 1))
return NULL; /* not enough space for even the shortest of forms */
- return snprintf_ok(buf, l, "@" USEC_FMT, t / USEC_PER_SEC); /* round down µs → s */
+ return snprintf_ok(buf, l, "@" USEC_FMT, t / USEC_PER_SEC); /* round down μs → s */
}
utc = IN_SET(style, TIMESTAMP_UTC, TIMESTAMP_US_UTC, TIMESTAMP_DATE);
@@ -618,7 +637,7 @@ char* format_timespan(char *buf, size_t l, usec_t t, usec_t accuracy) {
#if 0 /* NM_IGNORED */
static int parse_timestamp_impl(
const char *t,
- size_t tz_offset,
+ size_t max_len,
bool utc,
int isdst,
long gmtoff,
@@ -655,8 +674,12 @@ static int parse_timestamp_impl(
/* Allowed syntaxes:
*
- * 2012-09-22 16:34:22
+ * 2012-09-22 16:34:22.1[2[3[4[5[6]]]]]
+ * 2012-09-22 16:34:22 (µsec will be set to 0)
* 2012-09-22 16:34 (seconds will be set to 0)
+ * 2012-09-22T16:34:22.1[2[3[4[5[6]]]]]
+ * 2012-09-22T16:34:22 (µsec will be set to 0)
+ * 2012-09-22T16: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)
@@ -670,17 +693,26 @@ static int parse_timestamp_impl(
*
* Note, on DST change, 00:00:00 may not exist and in that case the time part may be shifted.
* E.g. "Sun 2023-03-13 America/Havana" is parsed as "Sun 2023-03-13 01:00:00 CDT".
+ *
+ * A simplified strptime-spelled RFC3339 ABNF looks like
+ * "%Y-%m-%d" "T" "%H" ":" "%M" ":" "%S" [".%N"] ("Z" / (("+" / "-") "%H:%M"))
+ * We additionally allow no seconds and inherited timezone
+ * for symmetry with our other syntaxes and improved interactive usability:
+ * "%Y-%m-%d" "T" "%H" ":" "%M" ":" ["%S" [".%N"]] ["Z" / (("+" / "-") "%H:%M")]
+ * RFC3339 defines time-secfrac to as "." 1*DIGIT, but we limit to 6 digits,
+ * since we're limited to 1µs resolution.
+ * We also accept "Sat 2012-09-22T16:34:22", RFC3339 warns against it.
*/
assert(t);
- if (tz_offset != SIZE_MAX) {
+ if (max_len != SIZE_MAX) {
/* If the input string contains timezone, then cut it here. */
- if (tz_offset <= 1) /* timezone must be after a space. */
+ if (max_len == 0) /* Can't be the only field */
return -EINVAL;
- t_alloc = strndup(t, tz_offset - 1);
+ t_alloc = strndup(t, max_len);
if (!t_alloc)
return -ENOMEM;
@@ -792,6 +824,7 @@ static int parse_timestamp_impl(
goto from_tm;
}
+ /* Our "canonical" RFC3339 syntax variant */
tm = copy;
k = strptime(t, "%Y-%m-%d %H:%M:%S", &tm);
if (k) {
@@ -801,6 +834,16 @@ static int parse_timestamp_impl(
goto from_tm;
}
+ /* RFC3339 syntax */
+ tm = copy;
+ k = strptime(t, "%Y-%m-%dT%H:%M:%S", &tm);
+ if (k) {
+ if (*k == '.')
+ goto parse_usec;
+ else if (*k == 0)
+ goto from_tm;
+ }
+
/* Support OUTPUT_SHORT and OUTPUT_SHORT_PRECISE formats */
tm = copy;
k = strptime(t, "%b %d %H:%M:%S", &tm);
@@ -818,6 +861,7 @@ static int parse_timestamp_impl(
goto from_tm;
}
+ /* Our "canonical" RFC3339 syntax variant without seconds */
tm = copy;
k = strptime(t, "%Y-%m-%d %H:%M", &tm);
if (k && *k == 0) {
@@ -825,6 +869,14 @@ static int parse_timestamp_impl(
goto from_tm;
}
+ /* RFC3339 syntax without seconds */
+ tm = copy;
+ k = strptime(t, "%Y-%m-%dT%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) {
@@ -927,13 +979,13 @@ static int parse_timestamp_maybe_with_tz(const char *t, size_t tz_offset, bool v
continue;
/* The specified timezone matches tzname[] of the local timezone. */
- return parse_timestamp_impl(t, tz_offset, /* utc = */ false, /* isdst = */ j, /* gmtoff = */ 0, ret);
+ return parse_timestamp_impl(t, tz_offset - 1, /* utc = */ false, /* isdst = */ j, /* gmtoff = */ 0, ret);
}
/* If we know that the last word is a valid timezone (e.g. Asia/Tokyo), then simply drop the timezone
* and parse the remaining string as a local time. If we know that the last word is not a timezone,
* then assume that it is a part of the time and try to parse the whole string as a local time. */
- return parse_timestamp_impl(t, valid_tz ? tz_offset : SIZE_MAX,
+ return parse_timestamp_impl(t, valid_tz ? tz_offset - 1 : SIZE_MAX,
/* utc = */ false, /* isdst = */ -1, /* gmtoff = */ 0, ret);
}
@@ -945,40 +997,50 @@ typedef struct ParseTimestampResult {
int parse_timestamp(const char *t, usec_t *ret) {
ParseTimestampResult *shared, tmp;
const char *k, *tz, *current_tz;
- size_t tz_offset;
+ size_t max_len, t_len;
struct tm tm;
int r;
assert(t);
+ t_len = strlen(t);
+ if (t_len > 2 && t[t_len - 1] == 'Z' && t[t_len - 2] != ' ') /* RFC3339-style welded UTC: "1985-04-12T23:20:50.52Z" */
+ return parse_timestamp_impl(t, t_len - 1, /* utc = */ true, /* isdst = */ -1, /* gmtoff = */ 0, ret);
+
+ if (t_len > 7 && IN_SET(t[t_len - 6], '+', '-') && t[t_len - 7] != ' ') { /* RFC3339-style welded offset: "1990-12-31T15:59:60-08:00" */
+ k = strptime(&t[t_len - 6], "%z", &tm);
+ if (k && *k == '\0')
+ return parse_timestamp_impl(t, t_len - 6, /* utc = */ true, /* isdst = */ -1, /* gmtoff = */ tm.tm_gmtoff, ret);
+ }
+
tz = strrchr(t, ' ');
if (!tz)
- return parse_timestamp_impl(t, /* tz_offset = */ SIZE_MAX, /* utc = */ false, /* isdst = */ -1, /* gmtoff = */ 0, ret);
+ return parse_timestamp_impl(t, /* max_len = */ SIZE_MAX, /* utc = */ false, /* isdst = */ -1, /* gmtoff = */ 0, ret);
+ max_len = tz - t;
tz++;
- tz_offset = tz - t;
/* Shortcut, parse the string as UTC. */
if (streq(tz, "UTC"))
- return parse_timestamp_impl(t, tz_offset, /* utc = */ true, /* isdst = */ -1, /* gmtoff = */ 0, ret);
+ return parse_timestamp_impl(t, max_len, /* utc = */ true, /* isdst = */ -1, /* gmtoff = */ 0, ret);
/* If the timezone is compatible with RFC-822/ISO 8601 (e.g. +06, or -03:00) then parse the string as
* UTC and shift the result. Note, this must be earlier than the timezone check with tzname[], as
* tzname[] may be in the same format. */
k = strptime(tz, "%z", &tm);
if (k && *k == '\0')
- return parse_timestamp_impl(t, tz_offset, /* utc = */ true, /* isdst = */ -1, /* gmtoff = */ tm.tm_gmtoff, ret);
+ return parse_timestamp_impl(t, max_len, /* utc = */ true, /* isdst = */ -1, /* gmtoff = */ tm.tm_gmtoff, ret);
/* If the last word is not a timezone file (e.g. Asia/Tokyo), then let's check if it matches
* tzname[] of the local timezone, e.g. JST or CEST. */
if (!timezone_is_valid(tz, LOG_DEBUG))
- return parse_timestamp_maybe_with_tz(t, tz_offset, /* valid_tz = */ false, ret);
+ return parse_timestamp_maybe_with_tz(t, tz - t, /* valid_tz = */ false, ret);
/* Shortcut. If the current $TZ is equivalent to the specified timezone, it is not necessary to fork
* the process. */
current_tz = getenv("TZ");
if (current_tz && *current_tz == ':' && streq(current_tz + 1, tz))
- return parse_timestamp_maybe_with_tz(t, tz_offset, /* valid_tz = */ true, ret);
+ return parse_timestamp_maybe_with_tz(t, tz - t, /* valid_tz = */ true, ret);
/* Otherwise, to avoid polluting the current environment variables, let's fork the process and set
* the specified timezone in the child process. */
@@ -987,7 +1049,7 @@ int parse_timestamp(const char *t, usec_t *ret) {
if (shared == MAP_FAILED)
return negative_errno();
- r = safe_fork("(sd-timestamp)", FORK_RESET_SIGNALS|FORK_CLOSE_ALL_FDS|FORK_DEATHSIG|FORK_WAIT, NULL);
+ r = safe_fork("(sd-timestamp)", FORK_RESET_SIGNALS|FORK_CLOSE_ALL_FDS|FORK_DEATHSIG_SIGKILL|FORK_WAIT, NULL);
if (r < 0) {
(void) munmap(shared, sizeof *shared);
return r;
@@ -1003,7 +1065,7 @@ int parse_timestamp(const char *t, usec_t *ret) {
_exit(EXIT_FAILURE);
}
- shared->return_value = parse_timestamp_maybe_with_tz(t, tz_offset, /* valid_tz = */ true, &shared->usec);
+ shared->return_value = parse_timestamp_maybe_with_tz(t, tz - t, /* valid_tz = */ true, &shared->usec);
_exit(EXIT_SUCCESS);
}
@@ -1051,7 +1113,8 @@ static const char* extract_multiplier(const char *p, usec_t *ret) {
{ "y", USEC_PER_YEAR },
{ "usec", 1ULL },
{ "us", 1ULL },
- { "µs", 1ULL },
+ { "μs", 1ULL }, /* U+03bc (aka GREEK SMALL LETTER MU) */
+ { "µs", 1ULL }, /* U+b5 (aka MICRO SIGN) */
};
assert(p);
@@ -1229,7 +1292,8 @@ static const char* extract_nsec_multiplier(const char *p, nsec_t *ret) {
{ "y", NSEC_PER_YEAR },
{ "usec", NSEC_PER_USEC },
{ "us", NSEC_PER_USEC },
- { "µs", NSEC_PER_USEC },
+ { "μs", NSEC_PER_USEC }, /* U+03bc (aka GREEK LETTER MU) */
+ { "µs", NSEC_PER_USEC }, /* U+b5 (aka MICRO SIGN) */
{ "nsec", 1ULL },
{ "ns", 1ULL },
{ "", 1ULL }, /* default is nsec */
@@ -1458,7 +1522,7 @@ int get_timezones(char ***ret) {
/* Always include UTC */
r = strv_extend(&zones, "UTC");
if (r < 0)
- return -ENOMEM;
+ return r;
strv_sort(zones);
strv_uniq(zones);
@@ -1666,13 +1730,13 @@ int time_change_fd(void) {
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
+ /* So apparently there are systems where time_t is 64-bit, but the kernel actually doesn't support
+ * 64-bit 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.
+ * We don't want this code on x86-64, hence let's conditionalize this for systems with 64-bit time_t
+ * but where "long" is shorter than 64-bit, i.e. 32-bit archs.
*
* See: https://github.com/systemd/systemd/issues/14362 */
@@ -1708,9 +1772,9 @@ TimestampStyle timestamp_style_from_string(const char *s) {
t = (TimestampStyle) string_table_lookup(timestamp_style_table, ELEMENTSOF(timestamp_style_table), s);
if (t >= 0)
return t;
- if (streq_ptr(s, "µs"))
+ if (STRPTR_IN_SET(s, "µs", "μs")) /* accept both µ symbols in unicode, i.e. micro symbol + Greek small letter mu. */
return TIMESTAMP_US;
- if (streq_ptr(s, "µs+utc"))
+ if (STRPTR_IN_SET(s, "µs+utc", "μs+utc"))
return TIMESTAMP_US_UTC;
return t;
}
diff --git a/src/libnm-systemd-shared/src/basic/time-util.h b/src/libnm-systemd-shared/src/basic/time-util.h
index b49137d5c3..29373477f4 100644
--- a/src/libnm-systemd-shared/src/basic/time-util.h
+++ b/src/libnm-systemd-shared/src/basic/time-util.h
@@ -79,13 +79,14 @@ 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_now(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(dual_timestamp *ts, usec_t u);
-triple_timestamp* triple_timestamp_get(triple_timestamp *ts);
+triple_timestamp* triple_timestamp_now(triple_timestamp *ts);
triple_timestamp* triple_timestamp_from_realtime(triple_timestamp *ts, usec_t u);
+triple_timestamp* triple_timestamp_from_boottime(triple_timestamp *ts, usec_t u);
#define DUAL_TIMESTAMP_HAS_CLOCK(clock) \
IN_SET(clock, CLOCK_REALTIME, CLOCK_REALTIME_ALARM, CLOCK_MONOTONIC)
@@ -211,10 +212,24 @@ static inline usec_t usec_sub_signed(usec_t timestamp, int64_t delta) {
return usec_sub_unsigned(timestamp, (usec_t) delta);
}
+static inline int usleep_safe(usec_t usec) {
+ /* usleep() takes useconds_t that is (typically?) uint32_t. Also, usleep() may only support the
+ * range [0, 1000000]. See usleep(3). Let's override usleep() with clock_nanosleep().
+ *
+ * ⚠️ Note we are not using plain nanosleep() here, since that operates on CLOCK_REALTIME, not
+ * CLOCK_MONOTONIC! */
+
+ if (usec == 0)
+ return 0;
+
+ // FIXME: use RET_NERRNO() macro here. Currently, this header cannot include errno-util.h.
+ return clock_nanosleep(CLOCK_MONOTONIC, 0, TIMESPEC_STORE(usec), NULL) < 0 ? -errno : 0;
+}
+
/* 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_64BIT ((usec_t) 253402214399000000) /* Thu 9999-12-30 23:59:59 UTC */
-/* With a 32bit time_t we can't go beyond 2038...
+/* With a 32-bit time_t we can't go beyond 2038...
* We parse timestamp with RFC-822/ISO 8601 (e.g. +06, or -03:00) as UTC, hence the upper bound must be off
* by USEC_PER_DAY. See parse_timestamp() for more details. */
#define USEC_TIMESTAMP_FORMATTABLE_MAX_32BIT (((usec_t) INT32_MAX) * USEC_PER_SEC - USEC_PER_DAY)
diff --git a/src/libnm-systemd-shared/src/basic/tmpfile-util.h b/src/libnm-systemd-shared/src/basic/tmpfile-util.h
index 50904ecac1..8c917c0680 100644
--- a/src/libnm-systemd-shared/src/basic/tmpfile-util.h
+++ b/src/libnm-systemd-shared/src/basic/tmpfile-util.h
@@ -29,7 +29,6 @@ static inline int open_tmpfile_linkable(const char *target, int flags, char **re
}
int fopen_tmpfile_linkable(const char *target, int flags, char **ret_path, FILE **ret_file);
-
typedef enum LinkTmpfileFlags {
LINK_TMPFILE_REPLACE = 1 << 0,
LINK_TMPFILE_SYNC = 1 << 1,
diff --git a/src/libnm-systemd-shared/src/basic/umask-util.h b/src/libnm-systemd-shared/src/basic/umask-util.h
index 6f0e1cc2b2..00417fa304 100644
--- a/src/libnm-systemd-shared/src/basic/umask-util.h
+++ b/src/libnm-systemd-shared/src/basic/umask-util.h
@@ -8,12 +8,12 @@
#include "macro.h"
static inline void umaskp(mode_t *u) {
- umask(*u & 0777);
+ umask(*u);
}
#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
+/* We make use of the fact here that the umask() syscall uses 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 WITH_UMASK() loop, so that we can run the first one,
* and exit on the second. */
diff --git a/src/libnm-systemd-shared/src/basic/user-util.h b/src/libnm-systemd-shared/src/basic/user-util.h
index 8b829a9ae2..9d07ef31d2 100644
--- a/src/libnm-systemd-shared/src/basic/user-util.h
+++ b/src/libnm-systemd-shared/src/basic/user-util.h
@@ -42,8 +42,8 @@ typedef enum UserCredsFlags {
USER_CREDS_CLEAN = 1 << 2, /* try to clean up shell and home fields with invalid data */
} UserCredsFlags;
-int get_user_creds(const char **username, uid_t *uid, gid_t *gid, const char **home, const char **shell, UserCredsFlags flags);
-int get_group_creds(const char **groupname, gid_t *gid, UserCredsFlags flags);
+int get_user_creds(const char **username, uid_t *ret_uid, gid_t *ret_gid, const char **ret_home, const char **ret_shell, UserCredsFlags flags);
+int get_group_creds(const char **groupname, gid_t *ret_gid, UserCredsFlags flags);
char* uid_to_name(uid_t uid);
char* gid_to_name(gid_t gid);
@@ -57,7 +57,10 @@ int getgroups_alloc(gid_t** gids);
int get_home_dir(char **ret);
int get_shell(char **ret);
-int reset_uid_gid(void);
+int fully_set_uid_gid(uid_t uid, gid_t gid, const gid_t supplementary_gids[], size_t n_supplementary_gids);
+static inline int reset_uid_gid(void) {
+ return fully_set_uid_gid(0, 0, NULL, 0);
+}
int take_etc_passwd_lock(const char *root);
@@ -69,13 +72,13 @@ int take_etc_passwd_lock(const char *root);
/* If REMOUNT_IDMAPPING_HOST_ROOT is set for remount_idmap() we'll include a mapping here that maps the host
* root user accessing the idmapped mount to the this user ID on the backing fs. This is the last valid UID in
- * the *signed* 32bit range. You might wonder why precisely use this specific UID for this purpose? Well, we
+ * the *signed* 32-bit range. You might wonder why precisely use this specific UID for this purpose? Well, we
* definitely cannot use the first 0…65536 UIDs for that, since in most cases that's precisely the file range
* we intend to map to some high UID range, and since UID mappings have to be bijective we thus cannot use
- * them at all. Furthermore the UID range beyond INT32_MAX (i.e. the range above the signed 32bit range) is
+ * them at all. Furthermore the UID range beyond INT32_MAX (i.e. the range above the signed 32-bit range) is
* icky, since many APIs cannot use it (example: setfsuid() returns the old UID as signed integer). Following
- * our usual logic of assigning a 16bit UID range to each container, so that the upper 16bit of a 32bit UID
- * value indicate kind of a "container ID" and the lower 16bit map directly to the intended user you can read
+ * our usual logic of assigning a 16-bit UID range to each container, so that the upper 16-bit of a 32-bit UID
+ * value indicate kind of a "container ID" and the lower 16-bit map directly to the intended user you can read
* this specific UID as the "nobody" user of the container with ID 0x7FFF, which is kinda nice. */
#define UID_MAPPED_ROOT ((uid_t) (INT32_MAX-1))
#define GID_MAPPED_ROOT ((gid_t) (INT32_MAX-1))
@@ -155,3 +158,9 @@ static inline bool hashed_password_is_locked_or_invalid(const char *password) {
* Also see https://github.com/systemd/systemd/pull/24680#pullrequestreview-1439464325.
*/
#define PASSWORD_UNPROVISIONED "!unprovisioned"
+
+int getpwuid_malloc(uid_t uid, struct passwd **ret);
+int getpwnam_malloc(const char *name, struct passwd **ret);
+
+int getgrnam_malloc(const char *name, struct group **ret);
+int getgrgid_malloc(gid_t gid, struct group **ret);
diff --git a/src/libnm-systemd-shared/src/basic/utf8.c b/src/libnm-systemd-shared/src/basic/utf8.c
index c8e39fe45e..cf24e82f09 100644
--- a/src/libnm-systemd-shared/src/basic/utf8.c
+++ b/src/libnm-systemd-shared/src/basic/utf8.c
@@ -92,7 +92,7 @@ int utf8_encoded_to_unichar(const char *str, char32_t *ret_unichar) {
switch (len) {
case 1:
*ret_unichar = (char32_t)str[0];
- return 0;
+ return 1;
case 2:
unichar = str[0] & 0x1f;
break;
@@ -121,15 +121,14 @@ int utf8_encoded_to_unichar(const char *str, char32_t *ret_unichar) {
}
*ret_unichar = unichar;
-
- return 0;
+ return len;
}
bool utf8_is_printable_newline(const char* str, size_t length, bool allow_newline) {
assert(str);
for (const char *p = str; length > 0;) {
- int encoded_len, r;
+ int encoded_len;
char32_t val;
encoded_len = utf8_encoded_valid_unichar(p, length);
@@ -137,8 +136,7 @@ bool utf8_is_printable_newline(const char* str, size_t length, bool allow_newlin
return false;
assert(encoded_len > 0 && (size_t) encoded_len <= length);
- r = utf8_encoded_to_unichar(p, &val);
- if (r < 0 ||
+ if (utf8_encoded_to_unichar(p, &val) < 0 ||
unichar_is_control(val) ||
(!allow_newline && val == '\n'))
return false;
@@ -398,11 +396,23 @@ char *utf16_to_utf8(const char16_t *s, size_t length /* bytes! */) {
const uint8_t *f;
char *r, *t;
+ if (length == 0)
+ return new0(char, 1);
+
assert(s);
+ if (length == SIZE_MAX) {
+ length = char16_strlen(s);
+
+ if (length > SIZE_MAX/2)
+ return NULL; /* overflow */
+
+ length *= 2;
+ }
+
/* 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)
+ if (length > (SIZE_MAX - 1) / 2)
return NULL; /* overflow */
r = new(char, length * 2 + 1);
@@ -472,8 +482,17 @@ char16_t *utf8_to_utf16(const char *s, size_t length) {
char16_t *n, *p;
int r;
+ if (length == 0)
+ return new0(char16_t, 1);
+
assert(s);
+ if (length == SIZE_MAX)
+ length = strlen(s);
+
+ if (length > SIZE_MAX - 1)
+ return NULL; /* overflow */
+
n = new(char16_t, length + 1);
if (!n)
return NULL;
diff --git a/src/libnm-systemd-shared/src/basic/utf8.h b/src/libnm-systemd-shared/src/basic/utf8.h
index 4a06dd62c5..962312c5fb 100644
--- a/src/libnm-systemd-shared/src/basic/utf8.h
+++ b/src/libnm-systemd-shared/src/basic/utf8.h
@@ -38,7 +38,7 @@ 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!) */
+size_t char16_strlen(const char16_t *s); /* returns the number of 16-bit 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);
diff --git a/src/libnm-systemd-shared/src/fundamental/macro-fundamental.h b/src/libnm-systemd-shared/src/fundamental/macro-fundamental.h
index 89b83e7d0b..c810261308 100644
--- a/src/libnm-systemd-shared/src/fundamental/macro-fundamental.h
+++ b/src/libnm-systemd-shared/src/fundamental/macro-fundamental.h
@@ -11,6 +11,42 @@
#include <stddef.h>
#include <stdint.h>
+/* 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\"")
+
+#define DISABLE_WARNING_TYPE_LIMITS \
+ _Pragma("GCC diagnostic push"); \
+ _Pragma("GCC diagnostic ignored \"-Wtype-limits\"")
+
+#define DISABLE_WARNING_ADDRESS \
+ _Pragma("GCC diagnostic push"); \
+ _Pragma("GCC diagnostic ignored \"-Waddress\"")
+
+#define REENABLE_WARNING \
+ _Pragma("GCC diagnostic pop")
+
#define _align_(x) __attribute__((__aligned__(x)))
#define _alignas_(x) __attribute__((__aligned__(alignof(x))))
#define _alignptr_ __attribute__((__aligned__(sizeof(void *))))
@@ -79,7 +115,7 @@
_noreturn_ void efi_assert(const char *expr, const char *file, unsigned line, const char *function);
#ifdef NDEBUG
- #define assert(expr)
+ #define assert(expr) ({ if (!(expr)) __builtin_unreachable(); })
#define assert_not_reached() __builtin_unreachable()
#else
#define assert(expr) ({ _likely_(expr) ? VOID_0 : efi_assert(#expr, __FILE__, __LINE__, __func__); })
@@ -129,6 +165,10 @@
__atomic_exchange_n(&(o), true, __ATOMIC_SEQ_CST); \
})
+#define U64_KB UINT64_C(1024)
+#define U64_MB (UINT64_C(1024) * U64_KB)
+#define U64_GB (UINT64_C(1024) * U64_MB)
+
#undef MAX
#define MAX(a, b) __MAX(UNIQ, (a), UNIQ, (b))
#define __MAX(aq, a, bq, b) \
@@ -355,7 +395,40 @@ static inline size_t ALIGN_TO(size_t l, size_t ali) {
if (l > SIZE_MAX - (ali - 1))
return SIZE_MAX; /* indicate overflow */
- return ((l + ali - 1) & ~(ali - 1));
+ return ((l + (ali - 1)) & ~(ali - 1));
+}
+
+static inline uint64_t ALIGN_TO_U64(uint64_t l, uint64_t ali) {
+ assert(ISPOWEROF2(ali));
+
+ if (l > UINT64_MAX - (ali - 1))
+ return UINT64_MAX; /* indicate overflow */
+
+ return ((l + (ali - 1)) & ~(ali - 1));
+}
+
+static inline size_t ALIGN_DOWN(size_t l, size_t ali) {
+ assert(ISPOWEROF2(ali));
+
+ return l & ~(ali - 1);
+}
+
+static inline uint64_t ALIGN_DOWN_U64(uint64_t l, uint64_t ali) {
+ assert(ISPOWEROF2(ali));
+
+ return l & ~(ali - 1);
+}
+
+static inline size_t ALIGN_OFFSET(size_t l, size_t ali) {
+ assert(ISPOWEROF2(ali));
+
+ return l & (ali - 1);
+}
+
+static inline uint64_t ALIGN_OFFSET_U64(uint64_t l, uint64_t ali) {
+ assert(ISPOWEROF2(ali));
+
+ return l & (ali - 1);
}
#define ALIGN2(l) ALIGN_TO(l, 2)
@@ -399,6 +472,42 @@ static inline size_t ALIGN_TO(size_t l, size_t ali) {
#define FLAGS_SET(v, flags) \
((~(v) & (flags)) == 0)
+/* A wrapper for 'func' to return void.
+ * Only useful when a void-returning function is required by some API. */
+#define DEFINE_TRIVIAL_DESTRUCTOR(name, type, func) \
+ static inline void name(type *p) { \
+ func(p); \
+ }
+
+/* When func() returns the void value (NULL, -1, …) of the appropriate type */
+#define DEFINE_TRIVIAL_CLEANUP_FUNC(type, func) \
+ static inline void func##p(type *p) { \
+ if (*p) \
+ *p = func(*p); \
+ }
+
+/* When func() doesn't return the appropriate type, set variable to empty afterwards.
+ * The func() may be provided by a dynamically loaded shared library, hence add an assertion. */
+#define DEFINE_TRIVIAL_CLEANUP_FUNC_FULL(type, func, empty) \
+ static inline void func##p(type *p) { \
+ if (*p != (empty)) { \
+ DISABLE_WARNING_ADDRESS; \
+ assert(func); \
+ REENABLE_WARNING; \
+ func(*p); \
+ *p = (empty); \
+ } \
+ }
+
+/* When func() doesn't return the appropriate type, and is also a macro, set variable to empty afterwards. */
+#define DEFINE_TRIVIAL_CLEANUP_FUNC_FULL_MACRO(type, func, empty) \
+ static inline void func##p(type *p) { \
+ if (*p != (empty)) { \
+ func(*p); \
+ *p = (empty); \
+ } \
+ }
+
/* Declare a flexible array usable in a union.
* This is essentially a work-around for a pointless constraint in C99
* and might go away in some future version of the standard.
@@ -410,3 +519,16 @@ static inline size_t ALIGN_TO(size_t l, size_t ali) {
dummy_t __empty__ ## name; \
type name[]; \
}
+
+/* Declares an ELF read-only string section that does not occupy memory at runtime. */
+#define DECLARE_NOALLOC_SECTION(name, text) \
+ asm(".pushsection " name ",\"S\"\n\t" \
+ ".ascii " STRINGIFY(text) "\n\t" \
+ ".zero 1\n\t" \
+ ".popsection\n")
+
+#ifdef SBAT_DISTRO
+ #define DECLARE_SBAT(text) DECLARE_NOALLOC_SECTION(".sbat", text)
+#else
+ #define DECLARE_SBAT(text)
+#endif
diff --git a/src/libnm-systemd-shared/src/fundamental/memory-util-fundamental.h b/src/libnm-systemd-shared/src/fundamental/memory-util-fundamental.h
index 78e2dbec59..6870f54f58 100644
--- a/src/libnm-systemd-shared/src/fundamental/memory-util-fundamental.h
+++ b/src/libnm-systemd-shared/src/fundamental/memory-util-fundamental.h
@@ -11,6 +11,12 @@
#include "macro-fundamental.h"
+#define memzero(x, l) \
+ ({ \
+ size_t _l_ = (l); \
+ _l_ > 0 ? memset((x), 0, _l_) : (x); \
+ })
+
#if !SD_BOOT && HAVE_EXPLICIT_BZERO
static inline void *explicit_bzero_safe(void *p, size_t l) {
if (p && l > 0)
@@ -64,3 +70,39 @@ static inline void erase_varp(struct VarEraser *e) {
.p = (ptr), \
.size = (sz), \
}
+
+typedef void (*free_array_func_t)(void *p, size_t n);
+
+/* An automatic _cleanup_-like logic for destroy arrays (i.e. pointers + size) when leaving scope */
+typedef struct ArrayCleanup {
+ void **parray;
+ size_t *pn;
+ free_array_func_t pfunc;
+} ArrayCleanup;
+
+static inline void array_cleanup(const ArrayCleanup *c) {
+ assert(c);
+
+ assert(!c->parray == !c->pn);
+
+ if (!c->parray)
+ return;
+
+ if (*c->parray) {
+ assert(c->pfunc);
+ c->pfunc(*c->parray, *c->pn);
+ *c->parray = NULL;
+ }
+
+ *c->pn = 0;
+}
+
+#define CLEANUP_ARRAY(array, n, func) \
+ _cleanup_(array_cleanup) _unused_ const ArrayCleanup CONCATENATE(_cleanup_array_, UNIQ) = { \
+ .parray = (void**) &(array), \
+ .pn = &(n), \
+ .pfunc = (free_array_func_t) ({ \
+ void (*_f)(typeof(array[0]) *a, size_t b) = func; \
+ _f; \
+ }), \
+ }
diff --git a/src/libnm-systemd-shared/src/fundamental/sha256.c b/src/libnm-systemd-shared/src/fundamental/sha256.c
index a4c6d627b7..84113aed6b 100644
--- a/src/libnm-systemd-shared/src/fundamental/sha256.c
+++ b/src/libnm-systemd-shared/src/fundamental/sha256.c
@@ -36,16 +36,9 @@
#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
# define SWAP(n) \
- (((n) << 24) | (((n) & 0xff00) << 8) | (((n) >> 8) & 0xff00) | ((n) >> 24))
+ __builtin_bswap32(n)
# define SWAP64(n) \
- (((n) << 56) \
- | (((n) & 0xff00) << 40) \
- | (((n) & 0xff0000) << 24) \
- | (((n) & 0xff000000) << 8) \
- | (((n) >> 8) & 0xff000000) \
- | (((n) >> 24) & 0xff0000) \
- | (((n) >> 40) & 0xff00) \
- | ((n) >> 56))
+ __builtin_bswap64(n)
#else
# define SWAP(n) (n)
# define SWAP64(n) (n)
diff --git a/src/libnm-systemd-shared/src/fundamental/string-util-fundamental.c b/src/libnm-systemd-shared/src/fundamental/string-util-fundamental.c
index 3a3e7f593a..da810cb749 100644
--- a/src/libnm-systemd-shared/src/fundamental/string-util-fundamental.c
+++ b/src/libnm-systemd-shared/src/fundamental/string-util-fundamental.c
@@ -35,14 +35,14 @@ sd_char *startswith_no_case(const sd_char *s, const sd_char *prefix) {
return (sd_char*) s + l;
}
-sd_char* endswith(const sd_char *s, const sd_char *postfix) {
+sd_char* endswith(const sd_char *s, const sd_char *suffix) {
size_t sl, pl;
assert(s);
- assert(postfix);
+ assert(suffix);
sl = strlen(s);
- pl = strlen(postfix);
+ pl = strlen(suffix);
if (pl == 0)
return (sd_char*) s + sl;
@@ -50,20 +50,20 @@ sd_char* endswith(const sd_char *s, const sd_char *postfix) {
if (sl < pl)
return NULL;
- if (strcmp(s + sl - pl, postfix) != 0)
+ if (!streq(s + sl - pl, suffix))
return NULL;
return (sd_char*) s + sl - pl;
}
-sd_char* endswith_no_case(const sd_char *s, const sd_char *postfix) {
+sd_char* endswith_no_case(const sd_char *s, const sd_char *suffix) {
size_t sl, pl;
assert(s);
- assert(postfix);
+ assert(suffix);
sl = strlen(s);
- pl = strlen(postfix);
+ pl = strlen(suffix);
if (pl == 0)
return (sd_char*) s + sl;
@@ -71,7 +71,7 @@ sd_char* endswith_no_case(const sd_char *s, const sd_char *postfix) {
if (sl < pl)
return NULL;
- if (strcasecmp(s + sl - pl, postfix) != 0)
+ if (!strcaseeq(s + sl - pl, suffix))
return NULL;
return (sd_char*) s + sl - pl;
diff --git a/src/libnm-systemd-shared/src/fundamental/string-util-fundamental.h b/src/libnm-systemd-shared/src/fundamental/string-util-fundamental.h
index 9019542b16..419f1cc3da 100644
--- a/src/libnm-systemd-shared/src/fundamental/string-util-fundamental.h
+++ b/src/libnm-systemd-shared/src/fundamental/string-util-fundamental.h
@@ -59,8 +59,8 @@ static inline size_t strlen_ptr(const sd_char *s) {
sd_char *startswith(const sd_char *s, const sd_char *prefix) _pure_;
sd_char *startswith_no_case(const sd_char *s, const sd_char *prefix) _pure_;
-sd_char *endswith(const sd_char *s, const sd_char *postfix) _pure_;
-sd_char *endswith_no_case(const sd_char *s, const sd_char *postfix) _pure_;
+sd_char *endswith(const sd_char *s, const sd_char *suffix) _pure_;
+sd_char *endswith_no_case(const sd_char *s, const sd_char *suffix) _pure_;
static inline bool isempty(const sd_char *a) {
return !a || a[0] == '\0';
@@ -74,6 +74,10 @@ static inline const sd_char *yes_no(bool b) {
return b ? STR_C("yes") : STR_C("no");
}
+static inline const sd_char *on_off(bool b) {
+ return b ? STR_C("on") : STR_C("off");
+}
+
static inline const sd_char* comparison_operator(int result) {
return result < 0 ? STR_C("<") : result > 0 ? STR_C(">") : STR_C("==");
}
diff --git a/src/libnm-systemd-shared/src/shared/dns-domain.c b/src/libnm-systemd-shared/src/shared/dns-domain.c
index 43f43197a9..a07eaa33c8 100644
--- a/src/libnm-systemd-shared/src/shared/dns-domain.c
+++ b/src/libnm-systemd-shared/src/shared/dns-domain.c
@@ -87,12 +87,9 @@ int dns_label_unescape(const char **name, char *dest, size_t sz, DNSLabelFlags f
((unsigned) (n[1] - '0') * 10) +
((unsigned) (n[2] - '0'));
- /* Don't allow anything that doesn't
- * fit in 8bit. Note that we do allow
- * control characters, as some servers
- * (e.g. cloudflare) are happy to
- * generate labels with them
- * inside. */
+ /* Don't allow anything that doesn't fit in 8 bits. Note that we do allow
+ * control characters, as some servers (e.g. cloudflare) are happy to
+ * generate labels with them inside. */
if (k > 255)
return -EINVAL;
@@ -213,7 +210,7 @@ int dns_label_escape(const char *p, size_t l, char *dest, size_t sz) {
char *q;
/* DNS labels must be between 1 and 63 characters long. A
- * zero-length label does not exist. See RFC 2182, Section
+ * zero-length label does not exist. See RFC 2181, Section
* 11. */
if (l <= 0 || l > DNS_LABEL_MAX)
@@ -302,14 +299,14 @@ int dns_label_escape_new(const char *p, size_t l, char **ret) {
int dns_label_apply_idna(const char *encoded, size_t encoded_size, char *decoded, size_t decoded_max) {
_cleanup_free_ uint32_t *input = NULL;
size_t input_size, l;
- bool contains_8bit = false;
+ bool contains_8_bit = false;
char buffer[DNS_LABEL_MAX+1];
int r;
assert(encoded);
assert(decoded);
- /* Converts an U-label into an A-label */
+ /* Converts a U-label into an A-label */
r = dlopen_idn();
if (r < 0)
@@ -320,9 +317,9 @@ int dns_label_apply_idna(const char *encoded, size_t encoded_size, char *decoded
for (const char *p = encoded; p < encoded + encoded_size; p++)
if ((uint8_t) *p > 127)
- contains_8bit = true;
+ contains_8_bit = true;
- if (!contains_8bit) {
+ if (!contains_8_bit) {
if (encoded_size > DNS_LABEL_MAX)
return -EINVAL;
@@ -361,7 +358,7 @@ int dns_label_undo_idna(const char *encoded, size_t encoded_size, char *decoded,
size_t w;
int r;
- /* To be invoked after unescaping. Converts an A-label into an U-label. */
+ /* To be invoked after unescaping. Converts an A-label into a U-label. */
assert(encoded);
assert(decoded);
@@ -419,7 +416,7 @@ int dns_name_concat(const char *a, const char *b, DNSLabelFlags flags, char **_r
goto finish;
for (;;) {
- char label[DNS_LABEL_MAX];
+ char label[DNS_LABEL_MAX+1];
r = dns_label_unescape(&p, label, sizeof label, flags);
if (r < 0)
@@ -517,7 +514,7 @@ int dns_name_compare_func(const char *a, const char *b) {
y = b + strlen(b);
for (;;) {
- char la[DNS_LABEL_MAX], lb[DNS_LABEL_MAX];
+ char la[DNS_LABEL_MAX+1], lb[DNS_LABEL_MAX+1];
if (x == NULL && y == NULL)
return 0;
@@ -553,7 +550,7 @@ int dns_name_equal(const char *x, const char *y) {
assert(y);
for (;;) {
- char la[DNS_LABEL_MAX], lb[DNS_LABEL_MAX];
+ char la[DNS_LABEL_MAX+1], lb[DNS_LABEL_MAX+1];
r = dns_label_unescape(&x, la, sizeof la, 0);
if (r < 0)
@@ -584,7 +581,7 @@ int dns_name_endswith(const char *name, const char *suffix) {
s = suffix;
for (;;) {
- char ln[DNS_LABEL_MAX], ls[DNS_LABEL_MAX];
+ char ln[DNS_LABEL_MAX+1], ls[DNS_LABEL_MAX+1];
r = dns_label_unescape(&n, ln, sizeof ln, 0);
if (r < 0)
@@ -622,7 +619,7 @@ int dns_name_startswith(const char *name, const char *prefix) {
p = prefix;
for (;;) {
- char ln[DNS_LABEL_MAX], lp[DNS_LABEL_MAX];
+ char ln[DNS_LABEL_MAX+1], lp[DNS_LABEL_MAX+1];
r = dns_label_unescape(&p, lp, sizeof lp, 0);
if (r < 0)
@@ -654,7 +651,7 @@ int dns_name_change_suffix(const char *name, const char *old_suffix, const char
s = old_suffix;
for (;;) {
- char ln[DNS_LABEL_MAX], ls[DNS_LABEL_MAX];
+ char ln[DNS_LABEL_MAX+1], ls[DNS_LABEL_MAX+1];
if (!saved_before)
saved_before = n;
@@ -941,7 +938,7 @@ bool dns_srv_type_is_valid(const char *name) {
return false;
for (;;) {
- char label[DNS_LABEL_MAX];
+ char label[DNS_LABEL_MAX+1];
/* This more or less implements RFC 6335, Section 5.1 */
@@ -1239,7 +1236,7 @@ int dns_name_common_suffix(const char *a, const char *b, const char **ret) {
return m;
for (;;) {
- char la[DNS_LABEL_MAX], lb[DNS_LABEL_MAX];
+ char la[DNS_LABEL_MAX+1], lb[DNS_LABEL_MAX+1];
const char *x, *y;
if (k >= n || k >= m) {
@@ -1340,7 +1337,7 @@ int dns_name_apply_idna(const char *name, char **ret) {
assert(ret);
for (;;) {
- char label[DNS_LABEL_MAX];
+ char label[DNS_LABEL_MAX+1];
r = dns_label_unescape(&name, label, sizeof label, 0);
if (r < 0)
@@ -1425,6 +1422,10 @@ bool dns_name_dont_resolve(const char *name) {
if (dns_name_endswith(name, "invalid") > 0)
return true;
+ /* Never respond to some of the domains listed in RFC9476 */
+ if (dns_name_endswith(name, "alt") > 0)
+ return true;
+
return false;
}
#endif /* NM_IGNORED */