summaryrefslogtreecommitdiffstats
path: root/src/basic
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-06-12 03:50:45 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-06-12 03:50:45 +0000
commitefeb864cb547a2cbf96dc0053a8bdb4d9190b364 (patch)
treec0b83368f18be983fcc763200c4c24d633244588 /src/basic
parentReleasing progress-linux version 255.5-1~progress7.99u1. (diff)
downloadsystemd-efeb864cb547a2cbf96dc0053a8bdb4d9190b364.tar.xz
systemd-efeb864cb547a2cbf96dc0053a8bdb4d9190b364.zip
Merging upstream version 256.
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to '')
-rw-r--r--src/basic/alloc-util.h8
-rw-r--r--src/basic/bitfield.h2
-rw-r--r--src/basic/build-path.c274
-rw-r--r--src/basic/build-path.h8
-rw-r--r--src/basic/build.c15
-rw-r--r--src/basic/capability-util.c47
-rw-r--r--src/basic/cgroup-util.c239
-rw-r--r--src/basic/cgroup-util.h33
-rw-r--r--src/basic/chase.c13
-rw-r--r--src/basic/compress.c423
-rw-r--r--src/basic/compress.h24
-rw-r--r--src/basic/conf-files.c99
-rw-r--r--src/basic/conf-files.h18
-rw-r--r--src/basic/constants.h11
-rw-r--r--src/basic/devnum-util.c11
-rw-r--r--src/basic/dlfcn-util.c (renamed from src/shared/dlfcn-util.c)0
-rw-r--r--src/basic/dlfcn-util.h82
-rw-r--r--src/basic/efivars.c24
-rw-r--r--src/basic/env-file.c2
-rw-r--r--src/basic/env-util.c78
-rw-r--r--src/basic/env-util.h15
-rw-r--r--src/basic/errno-util.h3
-rw-r--r--src/basic/escape.c33
-rw-r--r--src/basic/escape.h1
-rw-r--r--src/basic/ether-addr-util.c14
-rw-r--r--src/basic/ether-addr-util.h2
-rw-r--r--src/basic/extract-word.c35
-rw-r--r--src/basic/extract-word.h5
-rw-r--r--src/basic/fd-util.c200
-rw-r--r--src/basic/fd-util.h20
-rw-r--r--src/basic/fileio.c76
-rw-r--r--src/basic/fileio.h5
-rw-r--r--src/basic/filesystems-gperf.gperf1
-rw-r--r--src/basic/format-util.c21
-rw-r--r--src/basic/format-util.h8
-rw-r--r--src/basic/fs-util.c127
-rw-r--r--src/basic/fs-util.h15
-rw-r--r--src/basic/gcrypt-util.c115
-rw-r--r--src/basic/gcrypt-util.h50
-rw-r--r--src/basic/getopt-defs.h2
-rw-r--r--src/basic/glyph-util.c13
-rw-r--r--src/basic/glyph-util.h6
-rw-r--r--src/basic/hash-funcs.c8
-rw-r--r--src/basic/hashmap.c53
-rw-r--r--src/basic/hashmap.h8
-rw-r--r--src/basic/hexdecoct.c12
-rw-r--r--src/basic/hexdecoct.h12
-rw-r--r--src/basic/in-addr-util.c108
-rw-r--r--src/basic/in-addr-util.h20
-rw-r--r--src/basic/initrd-util.c2
-rw-r--r--src/basic/iovec-util.c6
-rw-r--r--src/basic/iovec-util.h69
-rw-r--r--src/basic/keyring-util.c (renamed from src/shared/keyring-util.c)31
-rw-r--r--src/basic/keyring-util.h (renamed from src/shared/keyring-util.h)1
-rw-r--r--src/basic/label.c4
-rw-r--r--src/basic/label.h1
-rw-r--r--src/basic/linux/btrfs.h20
-rw-r--r--src/basic/linux/btrfs_tree.h74
-rw-r--r--src/basic/linux/fou.h56
-rw-r--r--src/basic/linux/if_bridge.h32
-rw-r--r--src/basic/linux/if_link.h569
-rw-r--r--src/basic/linux/in.h2
-rw-r--r--src/basic/linux/in6.h2
-rw-r--r--src/basic/linux/magic.h106
-rw-r--r--src/basic/linux/netfilter.h76
-rw-r--r--src/basic/linux/netfilter/nf_tables.h37
-rw-r--r--src/basic/linux/netlink.h5
-rw-r--r--src/basic/linux/nexthop.h45
-rw-r--r--src/basic/linux/nl80211.h400
-rw-r--r--src/basic/linux/pkt_sched.h152
-rw-r--r--src/basic/linux/rtnetlink.h20
-rw-r--r--src/basic/linux/stddef.h13
-rw-r--r--src/basic/locale-util.c9
-rw-r--r--src/basic/lock-util.c9
-rw-r--r--src/basic/lock-util.h2
-rw-r--r--src/basic/log.c142
-rw-r--r--src/basic/log.h18
-rw-r--r--src/basic/macro.h30
-rw-r--r--src/basic/memory-util.c16
-rw-r--r--src/basic/memory-util.h3
-rw-r--r--src/basic/meson.build55
-rw-r--r--src/basic/missing_audit.h20
-rw-r--r--src/basic/missing_capability.h9
-rw-r--r--src/basic/missing_drm.h4
-rw-r--r--src/basic/missing_fs.h55
-rw-r--r--src/basic/missing_input.h33
-rw-r--r--src/basic/missing_ioprio.h34
-rw-r--r--src/basic/missing_keyctl.h102
-rw-r--r--src/basic/missing_loop.h15
-rw-r--r--src/basic/missing_magic.h196
-rw-r--r--src/basic/missing_mman.h18
-rw-r--r--src/basic/missing_mount.h6
-rw-r--r--src/basic/missing_prctl.h15
-rw-r--r--src/basic/missing_random.h14
-rw-r--r--src/basic/missing_resource.h6
-rw-r--r--src/basic/missing_sched.h27
-rw-r--r--src/basic/missing_socket.h25
-rw-r--r--src/basic/missing_timerfd.h6
-rw-r--r--src/basic/missing_type.h4
-rw-r--r--src/basic/missing_wait.h12
-rw-r--r--src/basic/mkdir.c50
-rw-r--r--src/basic/mkdir.h7
-rw-r--r--src/basic/mountpoint-util.c69
-rw-r--r--src/basic/mountpoint-util.h11
-rw-r--r--src/basic/namespace-util.c290
-rw-r--r--src/basic/namespace-util.h20
-rw-r--r--src/basic/nulstr-util.c47
-rw-r--r--src/basic/nulstr-util.h7
-rw-r--r--src/basic/ordered-set.c5
-rw-r--r--src/basic/os-util.c13
-rw-r--r--src/basic/parse-util.c9
-rw-r--r--src/basic/parse-util.h2
-rw-r--r--src/basic/path-lookup.c76
-rw-r--r--src/basic/path-lookup.h3
-rw-r--r--src/basic/path-util.c80
-rw-r--r--src/basic/path-util.h52
-rw-r--r--src/basic/pidref.c135
-rw-r--r--src/basic/pidref.h29
-rw-r--r--src/basic/proc-cmdline.c12
-rw-r--r--src/basic/process-util.c270
-rw-r--r--src/basic/process-util.h57
-rw-r--r--src/basic/recurse-dir.c13
-rw-r--r--src/basic/recurse-dir.h1
-rw-r--r--src/basic/rlimit-util.c116
-rw-r--r--src/basic/rlimit-util.h2
-rw-r--r--src/basic/sha256.c50
-rw-r--r--src/basic/sha256.h16
-rw-r--r--src/basic/signal-util.c88
-rw-r--r--src/basic/signal-util.h9
-rw-r--r--src/basic/siphash24.h9
-rw-r--r--src/basic/socket-util.c138
-rw-r--r--src/basic/socket-util.h13
-rw-r--r--src/basic/special.h7
-rw-r--r--src/basic/stat-util.c263
-rw-r--r--src/basic/stat-util.h62
-rw-r--r--src/basic/stdio-util.h4
-rw-r--r--src/basic/string-table.h6
-rw-r--r--src/basic/string-util.c129
-rw-r--r--src/basic/string-util.h36
-rw-r--r--src/basic/strv.c120
-rw-r--r--src/basic/strv.h31
-rw-r--r--src/basic/syscall-list.txt6
-rw-r--r--src/basic/syscalls-alpha.txt8
-rw-r--r--src/basic/syscalls-arc.txt6
-rw-r--r--src/basic/syscalls-arm.txt6
-rw-r--r--src/basic/syscalls-arm64.txt6
-rw-r--r--src/basic/syscalls-i386.txt6
-rw-r--r--src/basic/syscalls-loongarch64.txt6
-rw-r--r--src/basic/syscalls-m68k.txt6
-rw-r--r--src/basic/syscalls-mips64.txt6
-rw-r--r--src/basic/syscalls-mips64n32.txt6
-rw-r--r--src/basic/syscalls-mipso32.txt6
-rw-r--r--src/basic/syscalls-parisc.txt6
-rw-r--r--src/basic/syscalls-powerpc.txt6
-rw-r--r--src/basic/syscalls-powerpc64.txt6
-rw-r--r--src/basic/syscalls-riscv32.txt6
-rw-r--r--src/basic/syscalls-riscv64.txt6
-rw-r--r--src/basic/syscalls-s390.txt6
-rw-r--r--src/basic/syscalls-s390x.txt6
-rw-r--r--src/basic/syscalls-sparc.txt6
-rw-r--r--src/basic/syscalls-x86_64.txt6
-rw-r--r--src/basic/sysctl-util.c20
-rw-r--r--src/basic/sysctl-util.h7
-rw-r--r--src/basic/terminal-util.c352
-rw-r--r--src/basic/terminal-util.h27
-rw-r--r--src/basic/time-util.c48
-rw-r--r--src/basic/time-util.h11
-rw-r--r--src/basic/tmpfile-util.c52
-rw-r--r--src/basic/uid-classification.c (renamed from src/basic/uid-alloc-range.c)2
-rw-r--r--src/basic/uid-classification.h (renamed from src/basic/uid-alloc-range.h)0
-rw-r--r--src/basic/uid-range.c191
-rw-r--r--src/basic/uid-range.h72
-rw-r--r--src/basic/unit-def.c41
-rw-r--r--src/basic/unit-def.h5
-rw-r--r--src/basic/unit-name.c68
-rw-r--r--src/basic/unit-name.h11
-rw-r--r--src/basic/user-util.c481
-rw-r--r--src/basic/user-util.h15
-rw-r--r--src/basic/utf8.c22
-rw-r--r--src/basic/virt.c51
180 files changed, 6774 insertions, 2188 deletions
diff --git a/src/basic/alloc-util.h b/src/basic/alloc-util.h
index 136d2b3..c215c33 100644
--- a/src/basic/alloc-util.h
+++ b/src/basic/alloc-util.h
@@ -20,7 +20,7 @@ typedef void* (*mfree_func_t)(void *p);
* 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((n), sizeof(t)))
+#define new(t, n) ((t*) malloc_multiply(n, sizeof(t)))
#define new0(t, n) ((t*) calloc((n) ?: 1, sizeof(t)))
@@ -45,9 +45,9 @@ typedef void* (*mfree_func_t)(void *p);
(t*) alloca0((sizeof(t)*_n_)); \
})
-#define newdup(t, p, n) ((t*) memdup_multiply(p, (n), sizeof(t)))
+#define newdup(t, p, n) ((t*) memdup_multiply(p, n, sizeof(t)))
-#define newdup_suffix0(t, p, n) ((t*) memdup_suffix0_multiply(p, (n), sizeof(t)))
+#define newdup_suffix0(t, p, n) ((t*) memdup_suffix0_multiply(p, n, sizeof(t)))
#define malloc0(n) (calloc(1, (n) ?: 1))
@@ -237,7 +237,7 @@ 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. */
diff --git a/src/basic/bitfield.h b/src/basic/bitfield.h
index 25bc0eb..048e08d 100644
--- a/src/basic/bitfield.h
+++ b/src/basic/bitfield.h
@@ -27,7 +27,7 @@
({ \
typeof(type) UNIQ_T(_mask, uniq) = (type)0; \
int UNIQ_T(_i, uniq); \
- VA_ARGS_FOREACH(UNIQ_T(_i, uniq), ##__VA_ARGS__) \
+ FOREACH_ARGUMENT(UNIQ_T(_i, uniq), ##__VA_ARGS__) \
UNIQ_T(_mask, uniq) |= INDEX_TO_MASK(type, UNIQ_T(_i, uniq)); \
UNIQ_T(_mask, uniq); \
})
diff --git a/src/basic/build-path.c b/src/basic/build-path.c
new file mode 100644
index 0000000..b597265
--- /dev/null
+++ b/src/basic/build-path.c
@@ -0,0 +1,274 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+
+#include <elf.h>
+#include <link.h>
+#include <sys/auxv.h>
+
+#include "build-path.h"
+#include "errno-list.h"
+#include "errno-util.h"
+#include "macro.h"
+#include "path-util.h"
+#include "process-util.h"
+#include "unistd.h"
+
+static int get_runpath_from_dynamic(const ElfW(Dyn) *d, ElfW(Addr) bias, const char **ret) {
+ size_t runpath_index = SIZE_MAX, rpath_index = SIZE_MAX;
+ const char *strtab = NULL;
+
+ assert(d);
+
+ /* Iterates through the PT_DYNAMIC section to find the DT_RUNPATH/DT_RPATH entries */
+
+ for (; d->d_tag != DT_NULL; d++) {
+
+ switch (d->d_tag) {
+
+ case DT_RUNPATH:
+ runpath_index = (size_t) d->d_un.d_val;
+ break;
+
+ case DT_RPATH:
+ rpath_index = (size_t) d->d_un.d_val;
+ break;
+
+ case DT_STRTAB:
+ /* On MIPS and RISC-V DT_STRTAB records an offset, not a valid address, so it has to be adjusted
+ * using the bias calculated earlier. */
+ if (d->d_un.d_val != 0)
+ strtab = (const char *) ((uintptr_t) d->d_un.d_val
+#if defined(__mips__) || defined(__riscv)
+ + bias
+#endif
+ );
+ break;
+ }
+
+ /* runpath wins, hence if we have the table and runpath we can exit the loop early */
+ if (strtab && runpath_index != SIZE_MAX)
+ break;
+ }
+
+ if (!strtab)
+ return -ENOTRECOVERABLE;
+
+ /* According to ld.so runpath wins if both runpath and rpath are defined. */
+ if (runpath_index != SIZE_MAX) {
+ if (ret)
+ *ret = strtab + runpath_index;
+ return 1;
+ }
+
+ if (rpath_index != SIZE_MAX) {
+ if (ret)
+ *ret = strtab + rpath_index;
+ return 1;
+ }
+
+ if (ret)
+ *ret = NULL;
+
+ return 0;
+}
+
+static int get_runpath(const char **ret) {
+ unsigned long phdr, phent, phnum;
+
+ /* Finds the rpath/runpath in the program headers of the main executable we are running in */
+
+ phdr = getauxval(AT_PHDR); /* Start offset of phdr */
+ if (phdr == 0)
+ return -ENOTRECOVERABLE;
+
+ phnum = getauxval(AT_PHNUM); /* Number of entries in phdr */
+ if (phnum == 0)
+ return -ENOTRECOVERABLE;
+
+ phent = getauxval(AT_PHENT); /* Size of entries in phdr */
+ if (phent < sizeof(ElfW(Phdr))) /* Safety check, that our idea of the structure matches the file */
+ return -ENOTRECOVERABLE;
+
+ ElfW(Addr) bias = 0, dyn = 0;
+ bool found_bias = false, found_dyn = false;
+
+ /* Iterate through the Phdr structures to find the PT_PHDR and PT_DYNAMIC sections */
+ for (unsigned long i = 0; i < phnum; i++) {
+ const ElfW(Phdr) *p = (const ElfW(Phdr)*) (phdr + (i * phent));
+
+ switch (p->p_type) {
+
+ case PT_PHDR:
+ if (p->p_vaddr > phdr) /* safety overflow check */
+ return -ENOTRECOVERABLE;
+
+ bias = (ElfW(Addr)) phdr - p->p_vaddr;
+ found_bias = true;
+ break;
+
+ case PT_DYNAMIC:
+ dyn = p->p_vaddr;
+ found_dyn = true;
+ break;
+ }
+
+ if (found_bias && found_dyn)
+ break;
+ }
+
+ if (!found_dyn)
+ return -ENOTRECOVERABLE;
+
+ return get_runpath_from_dynamic((const ElfW(Dyn)*) (bias + dyn), bias, ret);
+}
+
+int get_build_exec_dir(char **ret) {
+ int r;
+
+ /* Returns the build execution directory if we are invoked in a build environment. Specifically, this
+ * checks if the main program binary has an rpath/runpath set (i.e. an explicit directory where to
+ * look for shared libraries) to $ORIGIN. If so we know that this is not a regular installed binary,
+ * but one which shall acquire its libraries from below a directory it is located in, i.e. a build
+ * directory or similar. In that case it typically makes sense to also search for our auxiliary
+ * executables we fork() off in a directory close to our main program binary, rather than in the
+ * system.
+ *
+ * This function is supposed to be used when looking for "callout" binaries that are closely related
+ * to the main program (i.e. speak a specific protocol between each other). And where it's generally
+ * a good idea to use the binary from the build tree (if there is one) instead of the system.
+ *
+ * Note that this does *not* actually return the rpath/runpath but the instead the directory the main
+ * executable was found in. This follows the logic that the result is supposed to be used for
+ * executable binaries (i.e. stuff in bindir), not for shared libraries (i.e. stuff in libdir), and
+ * hence the literal shared library path would just be wrong.
+ *
+ * TLDR: if we look for callouts in this dir first, running binaries from the meson build tree
+ * automatically uses the right callout.
+ *
+ * Returns:
+ * -ENOEXEC → We are not running in an rpath/runpath $ORIGIN environment
+ * -ENOENT → We don't know our own binary path
+ * -NOTRECOVERABLE → Dynamic binary information missing?
+ */
+
+ static int runpath_cached = -ERRNO_MAX-1;
+ if (runpath_cached == -ERRNO_MAX-1) {
+ const char *runpath = NULL;
+
+ runpath_cached = get_runpath(&runpath);
+
+ /* We only care if the runpath starts with $ORIGIN/ */
+ if (runpath_cached > 0 && !startswith(runpath, "$ORIGIN/"))
+ runpath_cached = 0;
+ }
+ if (runpath_cached < 0)
+ return runpath_cached;
+ if (runpath_cached == 0)
+ return -ENOEXEC;
+
+ _cleanup_free_ char *exe = NULL;
+ r = get_process_exe(0, &exe);
+ if (r < 0)
+ return runpath_cached = r;
+
+ return path_extract_directory(exe, ret);
+}
+
+static int find_build_dir_binary(const char *fn, char **ret) {
+ int r;
+
+ assert(fn);
+ assert(ret);
+
+ _cleanup_free_ char *build_dir = NULL;
+ r = get_build_exec_dir(&build_dir);
+ if (r < 0)
+ return r;
+
+ _cleanup_free_ char *np = path_join(build_dir, fn);
+ if (!np)
+ return -ENOMEM;
+
+ *ret = TAKE_PTR(np);
+ return 0;
+}
+
+static int find_environment_binary(const char *fn, const char **ret) {
+
+ /* If a path such as /usr/lib/systemd/systemd-foobar is specified, then this will check for an
+ * environment variable SYSTEMD_FOOBAR_PATH and return it if set. */
+
+ _cleanup_free_ char *s = strdup(fn);
+ if (!s)
+ return -ENOMEM;
+
+ ascii_strupper(s);
+ string_replace_char(s, '-', '_');
+
+ if (!strextend(&s, "_PATH"))
+ return -ENOMEM;
+
+ const char *e;
+ e = secure_getenv(s);
+ if (!e)
+ return -ENXIO;
+
+ *ret = e;
+ return 0;
+}
+
+int invoke_callout_binary(const char *path, char *const argv[]) {
+ int r;
+
+ assert(path);
+
+ /* Just like execv(), but tries to execute the specified binary in the build dir instead, if known */
+
+ _cleanup_free_ char *fn = NULL;
+ r = path_extract_filename(path, &fn);
+ if (r < 0)
+ return r;
+ if (r == O_DIRECTORY) /* Uh? */
+ return -EISDIR;
+
+ const char *e;
+ if (find_environment_binary(fn, &e) >= 0) {
+ /* If there's an explicit environment variable set for this binary, prefer it */
+ execv(e, argv);
+ return -errno; /* The environment variable counts, let's fail otherwise */
+ }
+
+ _cleanup_free_ char *np = NULL;
+ if (find_build_dir_binary(fn, &np) >= 0)
+ execv(np, argv);
+
+ execv(path, argv);
+ return -errno;
+}
+
+int pin_callout_binary(const char *path) {
+ int r;
+
+ assert(path);
+
+ /* Similar to invoke_callout_binary(), but pins (i.e. O_PATH opens) the binary instead of executing it. */
+
+ _cleanup_free_ char *fn = NULL;
+ r = path_extract_filename(path, &fn);
+ if (r < 0)
+ return r;
+ if (r == O_DIRECTORY) /* Uh? */
+ return -EISDIR;
+
+ const char *e;
+ if (find_environment_binary(fn, &e) >= 0)
+ return RET_NERRNO(open(e, O_CLOEXEC|O_PATH));
+
+ _cleanup_free_ char *np = NULL;
+ if (find_build_dir_binary(fn, &np) >= 0) {
+ r = RET_NERRNO(open(np, O_CLOEXEC|O_PATH));
+ if (r >= 0)
+ return r;
+ }
+
+ return RET_NERRNO(open(path, O_CLOEXEC|O_PATH));
+}
diff --git a/src/basic/build-path.h b/src/basic/build-path.h
new file mode 100644
index 0000000..6c38a4a
--- /dev/null
+++ b/src/basic/build-path.h
@@ -0,0 +1,8 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+#pragma once
+
+int get_build_exec_dir(char **ret);
+
+int invoke_callout_binary(const char *path, char *const argv[]);
+
+int pin_callout_binary(const char *path);
diff --git a/src/basic/build.c b/src/basic/build.c
index c587ada..3ab25f7 100644
--- a/src/basic/build.c
+++ b/src/basic/build.c
@@ -138,6 +138,12 @@ const char* const systemd_features =
" -LIBCRYPTSETUP"
#endif
+#if HAVE_LIBCRYPTSETUP_PLUGINS
+ " +LIBCRYPTSETUP_PLUGINS"
+#else
+ " -LIBCRYPTSETUP_PLUGINS"
+#endif
+
#if HAVE_LIBFDISK
" +LIBFDISK"
#else
@@ -232,7 +238,12 @@ const char* const systemd_features =
" -SYSVINIT"
#endif
- " default-hierarchy=" DEFAULT_HIERARCHY_NAME
+#if HAVE_LIBARCHIVE
+ " +LIBARCHIVE"
+#else
+ " -LIBARCHIVE"
+#endif
+
;
static char *systemd_features_with_color(void) {
@@ -276,7 +287,7 @@ int version(void) {
if (colors_enabled())
b = systemd_features_with_color();
- printf("%ssystemd " STRINGIFY(PROJECT_VERSION) "%s (" GIT_VERSION ")\n%s\n",
+ printf("%ssystemd " PROJECT_VERSION_FULL "%s (" GIT_VERSION ")\n%s\n",
ansi_highlight(), ansi_normal(),
b ?: systemd_features);
return 0;
diff --git a/src/basic/capability-util.c b/src/basic/capability-util.c
index c3cf455..e9b41fe 100644
--- a/src/basic/capability-util.c
+++ b/src/basic/capability-util.c
@@ -1,6 +1,7 @@
/* SPDX-License-Identifier: LGPL-2.1-or-later */
#include <errno.h>
+#include <stdatomic.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/prctl.h>
@@ -34,37 +35,38 @@ int have_effective_cap(int value) {
}
unsigned cap_last_cap(void) {
- static thread_local unsigned saved;
- static thread_local bool valid = false;
- _cleanup_free_ char *content = NULL;
- unsigned long p = 0;
- int r;
+ static atomic_int saved = INT_MAX;
+ int r, c;
- if (valid)
- return saved;
+ c = saved;
+ if (c != INT_MAX)
+ return c;
- /* available since linux-3.2 */
+ /* Available since linux-3.2 */
+ _cleanup_free_ char *content = NULL;
r = read_one_line_file("/proc/sys/kernel/cap_last_cap", &content);
- if (r >= 0) {
- r = safe_atolu(content, &p);
- if (r >= 0) {
-
- if (p > CAP_LIMIT) /* Safety for the future: if one day the kernel learns more than
+ if (r < 0)
+ log_debug_errno(r, "Failed to read /proc/sys/kernel/cap_last_cap, ignoring: %m");
+ else {
+ r = safe_atoi(content, &c);
+ if (r < 0)
+ log_debug_errno(r, "Failed to parse /proc/sys/kernel/cap_last_cap, ignoring: %m");
+ else {
+ if (c > CAP_LIMIT) /* Safety for the future: if one day the kernel learns more than
* 64 caps, then we are in trouble (since we, as much userspace
* and kernel space store capability masks in uint64_t types). We
* also want to use UINT64_MAX as marker for "unset". Hence let's
* hence protect ourselves against that and always cap at 62 for
* now. */
- p = CAP_LIMIT;
+ c = CAP_LIMIT;
- saved = p;
- valid = true;
- return p;
+ saved = c;
+ return c;
}
}
- /* fall back to syscall-probing for pre linux-3.2 */
- p = (unsigned long) MIN(CAP_LAST_CAP, CAP_LIMIT);
+ /* Fall back to syscall-probing for pre linux-3.2, or where /proc/ is not mounted */
+ unsigned long p = (unsigned long) MIN(CAP_LAST_CAP, CAP_LIMIT);
if (prctl(PR_CAPBSET_READ, p) < 0) {
@@ -81,10 +83,9 @@ unsigned cap_last_cap(void) {
break;
}
- saved = p;
- valid = true;
-
- return p;
+ c = (int) p;
+ saved = c;
+ return c;
}
int capability_update_inherited_set(cap_t caps, uint64_t set) {
diff --git a/src/basic/cgroup-util.c b/src/basic/cgroup-util.c
index 18b16ec..553ee60 100644
--- a/src/basic/cgroup-util.c
+++ b/src/basic/cgroup-util.c
@@ -22,6 +22,7 @@
#include "log.h"
#include "login-util.h"
#include "macro.h"
+#include "missing_fs.h"
#include "missing_magic.h"
#include "missing_threads.h"
#include "mkdir.h"
@@ -39,6 +40,38 @@
#include "user-util.h"
#include "xattr-util.h"
+int cg_path_open(const char *controller, const char *path) {
+ _cleanup_free_ char *fs = NULL;
+ int r;
+
+ r = cg_get_path(controller, path, /* item=*/ NULL, &fs);
+ if (r < 0)
+ return r;
+
+ return RET_NERRNO(open(fs, O_DIRECTORY|O_CLOEXEC));
+}
+
+int cg_cgroupid_open(int cgroupfs_fd, uint64_t id) {
+ _cleanup_close_ int fsfd = -EBADF;
+
+ if (cgroupfs_fd < 0) {
+ fsfd = open("/sys/fs/cgroup", O_CLOEXEC|O_DIRECTORY);
+ if (fsfd < 0)
+ return -errno;
+
+ cgroupfs_fd = fsfd;
+ }
+
+ cg_file_handle fh = CG_FILE_HANDLE_INIT;
+ CG_FILE_HANDLE_CGROUPID(fh) = id;
+
+ int fd = open_by_handle_at(cgroupfs_fd, &fh.file_handle, O_DIRECTORY|O_CLOEXEC);
+ if (fd < 0)
+ return -errno;
+
+ return fd;
+}
+
static int cg_enumerate_items(const char *controller, const char *path, FILE **ret, const char *item) {
_cleanup_free_ char *fs = NULL;
FILE *f;
@@ -62,7 +95,7 @@ int cg_enumerate_processes(const char *controller, const char *path, FILE **ret)
return cg_enumerate_items(controller, path, ret, "cgroup.procs");
}
-int cg_read_pid(FILE *f, pid_t *ret) {
+int cg_read_pid(FILE *f, pid_t *ret, CGroupFlags flags) {
unsigned long ul;
/* Note that the cgroup.procs might contain duplicates! See cgroups.txt for details. */
@@ -70,27 +103,33 @@ int cg_read_pid(FILE *f, pid_t *ret) {
assert(f);
assert(ret);
- errno = 0;
- if (fscanf(f, "%lu", &ul) != 1) {
+ for (;;) {
+ errno = 0;
+ if (fscanf(f, "%lu", &ul) != 1) {
- if (feof(f)) {
- *ret = 0;
- return 0;
+ if (feof(f)) {
+ *ret = 0;
+ return 0;
+ }
+
+ return errno_or_else(EIO);
}
- return errno_or_else(EIO);
- }
+ if (ul > PID_T_MAX)
+ return -EIO;
- if (ul <= 0)
- return -EIO;
- if (ul > PID_T_MAX)
- return -EIO;
+ /* In some circumstances (e.g. WSL), cgroups might contain unmappable PIDs from other
+ * contexts. These show up as zeros, and depending on the caller, can either be plain
+ * skipped over, or returned as-is. */
+ if (ul == 0 && !FLAGS_SET(flags, CGROUP_DONT_SKIP_UNMAPPED))
+ continue;
- *ret = (pid_t) ul;
- return 1;
+ *ret = (pid_t) ul;
+ return 1;
+ }
}
-int cg_read_pidref(FILE *f, PidRef *ret) {
+int cg_read_pidref(FILE *f, PidRef *ret, CGroupFlags flags) {
int r;
assert(f);
@@ -99,14 +138,22 @@ int cg_read_pidref(FILE *f, PidRef *ret) {
for (;;) {
pid_t pid;
- r = cg_read_pid(f, &pid);
+ r = cg_read_pid(f, &pid, flags);
if (r < 0)
- return r;
+ return log_debug_errno(r, "Failed to read pid from cgroup item: %m");
if (r == 0) {
*ret = PIDREF_NULL;
return 0;
}
+ if (pid == 0)
+ return -EREMOTE;
+
+ if (FLAGS_SET(flags, CGROUP_NO_PIDFD)) {
+ *ret = PIDREF_MAKE_FROM_PID(pid);
+ return 1;
+ }
+
r = pidref_set_pid(ret, pid);
if (r >= 0)
return 1;
@@ -135,7 +182,7 @@ int cg_read_event(
return r;
for (const char *p = content;;) {
- _cleanup_free_ char *line = NULL, *key = NULL, *val = NULL;
+ _cleanup_free_ char *line = NULL, *key = NULL;
const char *q;
r = extract_first_word(&p, &line, "\n", 0);
@@ -154,12 +201,7 @@ int cg_read_event(
if (!streq(key, event))
continue;
- val = strdup(q);
- if (!val)
- return -ENOMEM;
-
- *ret = TAKE_PTR(val);
- return 0;
+ return strdup_to(ret, q);
}
}
@@ -234,20 +276,13 @@ int cg_read_subgroup(DIR *d, char **ret) {
assert(ret);
FOREACH_DIRENT_ALL(de, d, return -errno) {
- char *b;
-
if (de->d_type != DT_DIR)
continue;
if (dot_or_dot_dot(de->d_name))
continue;
- b = strdup(de->d_name);
- if (!b)
- return -ENOMEM;
-
- *ret = b;
- return 1;
+ return strdup_to_full(ret, de->d_name);
}
*ret = NULL;
@@ -317,14 +352,14 @@ static int cg_kill_items(
if (r == -ENOENT)
break;
if (r < 0)
- return RET_GATHER(ret, r);
+ return RET_GATHER(ret, log_debug_errno(r, "Failed to enumerate cgroup items: %m"));
for (;;) {
_cleanup_(pidref_done) PidRef pidref = PIDREF_NULL;
- r = cg_read_pidref(f, &pidref);
+ r = cg_read_pidref(f, &pidref, flags);
if (r < 0)
- return RET_GATHER(ret, r);
+ return RET_GATHER(ret, log_debug_errno(r, "Failed to read pidref from cgroup '%s': %m", path));
if (r == 0)
break;
@@ -340,7 +375,7 @@ static int cg_kill_items(
/* If we haven't killed this process yet, kill it */
r = pidref_kill(&pidref, sig);
if (r < 0 && r != -ESRCH)
- RET_GATHER(ret, r);
+ RET_GATHER(ret, log_debug_errno(r, "Failed to kill process with pid " PID_FMT " from cgroup '%s': %m", pidref.pid, path));
if (r >= 0) {
if (flags & CGROUP_SIGCONT)
(void) pidref_kill(&pidref, SIGCONT);
@@ -379,6 +414,8 @@ int cg_kill(
int r, ret;
r = cg_kill_items(path, sig, flags, s, log_kill, userdata, "cgroup.procs");
+ if (r < 0)
+ log_debug_errno(r, "Failed to kill processes in cgroup '%s' item cgroup.procs: %m", path);
if (r < 0 || sig != SIGKILL)
return r;
@@ -393,9 +430,13 @@ int cg_kill(
if (r == 0)
return ret;
- r = cg_kill_items(path, sig, flags, s, log_kill, userdata, "cgroup.threads");
+ /* Opening pidfds for non thread group leaders only works from 6.9 onwards with PIDFD_THREAD. On
+ * older kernels or without PIDFD_THREAD pidfd_open() fails with EINVAL. Since we might read non
+ * thread group leader IDs from cgroup.threads, we set CGROUP_NO_PIDFD to avoid trying open pidfd's
+ * for them and instead use the regular pid. */
+ r = cg_kill_items(path, sig, flags|CGROUP_NO_PIDFD, s, log_kill, userdata, "cgroup.threads");
if (r < 0)
- return r;
+ return log_debug_errno(r, "Failed to kill processes in cgroup '%s' item cgroup.threads: %m", path);
return r > 0 || ret > 0;
}
@@ -418,7 +459,7 @@ int cg_kill_kernel_sigkill(const char *path) {
r = write_string_file(killfile, "1", WRITE_STRING_FILE_DISABLE_BUFFER);
if (r < 0)
- return r;
+ return log_debug_errno(r, "Failed to write to cgroup.kill for cgroup '%s': %m", path);
return 0;
}
@@ -455,7 +496,7 @@ int cg_kill_recursive(
r = cg_enumerate_subgroups(SYSTEMD_CGROUP_CONTROLLER, path, &d);
if (r < 0) {
if (r != -ENOENT)
- RET_GATHER(ret, r);
+ RET_GATHER(ret, log_debug_errno(r, "Failed to enumerate cgroup '%s' subgroups: %m", path));
return ret;
}
@@ -465,7 +506,7 @@ int cg_kill_recursive(
r = cg_read_subgroup(d, &fn);
if (r < 0) {
- RET_GATHER(ret, r);
+ RET_GATHER(ret, log_debug_errno(r, "Failed to read subgroup from cgroup '%s': %m", path));
break;
}
if (r == 0)
@@ -476,6 +517,8 @@ int cg_kill_recursive(
return -ENOMEM;
r = cg_kill_recursive(p, sig, flags, s, log_kill, userdata);
+ if (r < 0)
+ log_debug_errno(r, "Failed to recursively kill processes in cgroup '%s': %m", p);
if (r != 0 && ret >= 0)
ret = r;
}
@@ -484,7 +527,7 @@ int cg_kill_recursive(
if (FLAGS_SET(flags, CGROUP_REMOVE)) {
r = cg_rmdir(SYSTEMD_CGROUP_CONTROLLER, path);
if (!IN_SET(r, -ENOENT, -EBUSY))
- RET_GATHER(ret, r);
+ RET_GATHER(ret, log_debug_errno(r, "Failed to remove cgroup '%s': %m", path));
}
return ret;
@@ -917,7 +960,7 @@ int cg_is_empty(const char *controller, const char *path) {
if (r < 0)
return r;
- r = cg_read_pid(f, &pid);
+ r = cg_read_pid(f, &pid, CGROUP_DONT_SKIP_UNMAPPED);
if (r < 0)
return r;
@@ -1125,44 +1168,29 @@ int cg_pid_get_path_shifted(pid_t pid, const char *root, char **ret_cgroup) {
if (r < 0)
return r;
- if (c == raw)
+ if (c == raw) {
*ret_cgroup = TAKE_PTR(raw);
- else {
- char *n;
-
- n = strdup(c);
- if (!n)
- return -ENOMEM;
-
- *ret_cgroup = n;
+ return 0;
}
- return 0;
+ return strdup_to(ret_cgroup, c);
}
int cg_path_decode_unit(const char *cgroup, char **ret_unit) {
- char *c, *s;
- size_t n;
-
assert(cgroup);
assert(ret_unit);
- n = strcspn(cgroup, "/");
+ size_t n = strcspn(cgroup, "/");
if (n < 3)
return -ENXIO;
- c = strndupa_safe(cgroup, n);
+ char *c = strndupa_safe(cgroup, n);
c = cg_unescape(c);
if (!unit_name_is_valid(c, UNIT_NAME_PLAIN|UNIT_NAME_INSTANCE))
return -ENXIO;
- s = strdup(c);
- if (!s)
- return -ENOMEM;
-
- *ret_unit = s;
- return 0;
+ return strdup_to(ret_unit, c);
}
static bool valid_slice_name(const char *p, size_t n) {
@@ -1431,7 +1459,7 @@ int cg_pid_get_machine_name(pid_t pid, char **ret_machine) {
int cg_path_get_cgroupid(const char *path, uint64_t *ret) {
cg_file_handle fh = CG_FILE_HANDLE_INIT;
- int mnt_id = -1;
+ int mnt_id;
assert(path);
assert(ret);
@@ -1445,6 +1473,20 @@ int cg_path_get_cgroupid(const char *path, uint64_t *ret) {
return 0;
}
+int cg_fd_get_cgroupid(int fd, uint64_t *ret) {
+ cg_file_handle fh = CG_FILE_HANDLE_INIT;
+ int mnt_id = -1;
+
+ assert(fd >= 0);
+ assert(ret);
+
+ if (name_to_handle_at(fd, "", &fh.file_handle, &mnt_id, AT_EMPTY_PATH) < 0)
+ return -errno;
+
+ *ret = CG_FILE_HANDLE_CGROUPID(fh);
+ return 0;
+}
+
int cg_path_get_session(const char *path, char **ret_session) {
_cleanup_free_ char *unit = NULL;
char *start, *end;
@@ -1467,17 +1509,10 @@ int cg_path_get_session(const char *path, char **ret_session) {
if (!session_id_valid(start))
return -ENXIO;
- if (ret_session) {
- char *rr;
-
- rr = strdup(start);
- if (!rr)
- return -ENOMEM;
-
- *ret_session = rr;
- }
+ if (!ret_session)
+ return 0;
- return 0;
+ return strdup_to(ret_session, start);
}
int cg_pid_get_session(pid_t pid, char **ret_session) {
@@ -1534,34 +1569,26 @@ int cg_path_get_slice(const char *p, char **ret_slice) {
assert(p);
assert(ret_slice);
- /* Finds the right-most slice unit from the beginning, but
- * stops before we come to the first non-slice unit. */
+ /* Finds the right-most slice unit from the beginning, but stops before we come to
+ * the first non-slice unit. */
for (;;) {
- size_t n;
-
- p += strspn(p, "/");
-
- n = strcspn(p, "/");
- if (!valid_slice_name(p, n)) {
-
- if (!e) {
- char *s;
+ const char *s;
+ int n;
- s = strdup(SPECIAL_ROOT_SLICE);
- if (!s)
- return -ENOMEM;
+ n = path_find_first_component(&p, /* accept_dot_dot = */ false, &s);
+ if (n < 0)
+ return n;
+ if (!valid_slice_name(s, n))
+ break;
- *ret_slice = s;
- return 0;
- }
+ e = s;
+ }
- return cg_path_decode_unit(e, ret_slice);
- }
+ if (e)
+ return cg_path_decode_unit(e, ret_slice);
- e = p;
- p += n;
- }
+ return strdup_to(ret_slice, SPECIAL_ROOT_SLICE);
}
int cg_pid_get_slice(pid_t pid, char **ret_slice) {
@@ -1714,15 +1741,8 @@ int cg_slice_to_path(const char *unit, char **ret) {
assert(unit);
assert(ret);
- if (streq(unit, SPECIAL_ROOT_SLICE)) {
- char *x;
-
- x = strdup("");
- if (!x)
- return -ENOMEM;
- *ret = x;
- return 0;
- }
+ if (streq(unit, SPECIAL_ROOT_SLICE))
+ return strdup_to(ret, "");
if (!unit_name_is_valid(unit, UNIT_NAME_PLAIN))
return -EINVAL;
@@ -2141,15 +2161,14 @@ int cg_kernel_controllers(Set **ret) {
_cleanup_free_ char *controller = NULL;
int enabled = 0;
- errno = 0;
if (fscanf(f, "%ms %*i %*i %i", &controller, &enabled) != 2) {
+ if (ferror(f))
+ return -errno;
+
if (feof(f))
break;
- if (ferror(f))
- return errno_or_else(EIO);
-
return -EBADMSG;
}
diff --git a/src/basic/cgroup-util.h b/src/basic/cgroup-util.h
index d06eb6d..a887178 100644
--- a/src/basic/cgroup-util.h
+++ b/src/basic/cgroup-util.h
@@ -67,7 +67,7 @@ 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 prety much the v2 controllers only, as delegation on v1 is not safe, and bpf stuff isn't a real controller */
+ /* 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 */
@@ -180,20 +180,25 @@ typedef enum CGroupUnified {
* generate paths with multiple adjacent / removed.
*/
+int cg_path_open(const char *controller, const char *path);
+int cg_cgroupid_open(int fsfd, uint64_t id);
+
+typedef enum CGroupFlags {
+ CGROUP_SIGCONT = 1 << 0,
+ CGROUP_IGNORE_SELF = 1 << 1,
+ CGROUP_REMOVE = 1 << 2,
+ CGROUP_DONT_SKIP_UNMAPPED = 1 << 3,
+ CGROUP_NO_PIDFD = 1 << 4,
+} CGroupFlags;
+
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_pid(FILE *f, pid_t *ret, CGroupFlags flags);
+int cg_read_pidref(FILE *f, PidRef *ret, CGroupFlags flags);
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 **ret);
int cg_read_subgroup(DIR *d, char **ret);
-typedef enum CGroupFlags {
- CGROUP_SIGCONT = 1 << 0,
- CGROUP_IGNORE_SELF = 1 << 1,
- CGROUP_REMOVE = 1 << 2,
-} CGroupFlags;
-
typedef int (*cg_kill_log_func_t)(const PidRef *pid, int sig, void *userdata);
int cg_kill(const char *path, int sig, CGroupFlags flags, Set *s, cg_kill_log_func_t kill_log, void *userdata);
@@ -218,7 +223,7 @@ 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;
@@ -267,6 +272,7 @@ 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_fd_get_cgroupid(int fd, uint64_t *ret);
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);
@@ -352,5 +358,10 @@ typedef union {
uint8_t space[offsetof(struct file_handle, f_handle) + sizeof(uint64_t)];
} cg_file_handle;
-#define CG_FILE_HANDLE_INIT { .file_handle.handle_bytes = sizeof(uint64_t) }
+#define CG_FILE_HANDLE_INIT \
+ (cg_file_handle) { \
+ .file_handle.handle_bytes = sizeof(uint64_t), \
+ .file_handle.handle_type = FILEID_KERNFS, \
+ }
+
#define CG_FILE_HANDLE_CGROUPID(fh) (*(uint64_t*) (fh).file_handle.f_handle)
diff --git a/src/basic/chase.c b/src/basic/chase.c
index 9f5477e..4576e4b 100644
--- a/src/basic/chase.c
+++ b/src/basic/chase.c
@@ -641,8 +641,8 @@ int chase(const char *path, const char *root, ChaseFlags flags, char **ret_path,
* absolute, hence it is not necessary to prefix with the root. When "root" points to
* a non-root directory, the result path is always normalized and relative, hence
* we can simply call path_join() and not necessary to call path_simplify().
- * Note that the result of chaseat() may start with "." (more specifically, it may be
- * "." or "./"), and we need to drop "." in that case. */
+ * As a special case, chaseat() may return "." or "./", which are normalized too,
+ * but we need to drop "." before merging with root. */
if (empty_or_root(root))
assert(path_is_absolute(p));
@@ -651,7 +651,7 @@ int chase(const char *path, const char *root, ChaseFlags flags, char **ret_path,
assert(!path_is_absolute(p));
- q = path_join(root, p + (*p == '.'));
+ q = path_join(root, p + STR_IN_SET(p, ".", "./"));
if (!q)
return -ENOMEM;
@@ -741,12 +741,7 @@ int chase_extract_filename(const char *path, const char *root, char **ret) {
return r;
}
- char *fname = strdup(".");
- if (!fname)
- return -ENOMEM;
-
- *ret = fname;
- return 0;
+ return strdup_to(ret, ".");
}
int chase_and_open(const char *path, const char *root, ChaseFlags chase_flags, int open_flags, char **ret_path) {
diff --git a/src/basic/compress.c b/src/basic/compress.c
index ac0bfdf..33b27d3 100644
--- a/src/basic/compress.c
+++ b/src/basic/compress.c
@@ -12,11 +12,6 @@
#include <lzma.h>
#endif
-#if HAVE_LZ4
-#include <lz4.h>
-#include <lz4frame.h>
-#endif
-
#if HAVE_ZSTD
#include <zstd.h>
#include <zstd_errors.h>
@@ -34,16 +29,52 @@
#include "unaligned.h"
#if HAVE_LZ4
-DEFINE_TRIVIAL_CLEANUP_FUNC_FULL(LZ4F_compressionContext_t, LZ4F_freeCompressionContext, NULL);
-DEFINE_TRIVIAL_CLEANUP_FUNC_FULL(LZ4F_decompressionContext_t, LZ4F_freeDecompressionContext, NULL);
+static void *lz4_dl = NULL;
+
+static DLSYM_FUNCTION(LZ4F_compressBegin);
+static DLSYM_FUNCTION(LZ4F_compressBound);
+static DLSYM_FUNCTION(LZ4F_compressEnd);
+static DLSYM_FUNCTION(LZ4F_compressUpdate);
+static DLSYM_FUNCTION(LZ4F_createCompressionContext);
+static DLSYM_FUNCTION(LZ4F_createDecompressionContext);
+static DLSYM_FUNCTION(LZ4F_decompress);
+static DLSYM_FUNCTION(LZ4F_freeCompressionContext);
+static DLSYM_FUNCTION(LZ4F_freeDecompressionContext);
+static DLSYM_FUNCTION(LZ4F_isError);
+DLSYM_FUNCTION(LZ4_compress_default);
+DLSYM_FUNCTION(LZ4_decompress_safe);
+DLSYM_FUNCTION(LZ4_decompress_safe_partial);
+DLSYM_FUNCTION(LZ4_versionNumber);
+
+DEFINE_TRIVIAL_CLEANUP_FUNC_FULL(LZ4F_compressionContext_t, sym_LZ4F_freeCompressionContext, NULL);
+DEFINE_TRIVIAL_CLEANUP_FUNC_FULL(LZ4F_decompressionContext_t, sym_LZ4F_freeDecompressionContext, NULL);
#endif
#if HAVE_ZSTD
-DEFINE_TRIVIAL_CLEANUP_FUNC_FULL(ZSTD_CCtx*, ZSTD_freeCCtx, NULL);
-DEFINE_TRIVIAL_CLEANUP_FUNC_FULL(ZSTD_DCtx*, ZSTD_freeDCtx, NULL);
+static void *zstd_dl = NULL;
+
+static DLSYM_FUNCTION(ZSTD_CCtx_setParameter);
+static DLSYM_FUNCTION(ZSTD_compress);
+static DLSYM_FUNCTION(ZSTD_compressStream2);
+static DLSYM_FUNCTION(ZSTD_createCCtx);
+static DLSYM_FUNCTION(ZSTD_createDCtx);
+static DLSYM_FUNCTION(ZSTD_CStreamInSize);
+static DLSYM_FUNCTION(ZSTD_CStreamOutSize);
+static DLSYM_FUNCTION(ZSTD_decompressStream);
+static DLSYM_FUNCTION(ZSTD_DStreamInSize);
+static DLSYM_FUNCTION(ZSTD_DStreamOutSize);
+static DLSYM_FUNCTION(ZSTD_freeCCtx);
+static DLSYM_FUNCTION(ZSTD_freeDCtx);
+static DLSYM_FUNCTION(ZSTD_getErrorCode);
+static DLSYM_FUNCTION(ZSTD_getErrorName);
+static DLSYM_FUNCTION(ZSTD_getFrameContentSize);
+static DLSYM_FUNCTION(ZSTD_isError);
+
+DEFINE_TRIVIAL_CLEANUP_FUNC_FULL(ZSTD_CCtx*, sym_ZSTD_freeCCtx, NULL);
+DEFINE_TRIVIAL_CLEANUP_FUNC_FULL(ZSTD_DCtx*, sym_ZSTD_freeDCtx, NULL);
static int zstd_ret_to_errno(size_t ret) {
- switch (ZSTD_getErrorCode(ret)) {
+ switch (sym_ZSTD_getErrorCode(ret)) {
case ZSTD_error_dstSize_tooSmall:
return -ENOBUFS;
case ZSTD_error_memory_allocation:
@@ -54,6 +85,27 @@ static int zstd_ret_to_errno(size_t ret) {
}
#endif
+#if HAVE_XZ
+static void *lzma_dl = NULL;
+
+static DLSYM_FUNCTION(lzma_code);
+static DLSYM_FUNCTION(lzma_easy_encoder);
+static DLSYM_FUNCTION(lzma_end);
+static DLSYM_FUNCTION(lzma_stream_buffer_encode);
+static DLSYM_FUNCTION(lzma_stream_decoder);
+
+/* We can't just do _cleanup_(sym_lzma_end) because a compiler bug makes
+ * this fail with:
+ * ../src/basic/compress.c: In function ‘decompress_blob_xz’:
+ * ../src/basic/compress.c:304:9: error: cleanup argument not a function
+ * 304 | _cleanup_(sym_lzma_end) lzma_stream s = LZMA_STREAM_INIT;
+ * | ^~~~~~~~~
+ */
+static inline void lzma_end_wrapper(lzma_stream *ls) {
+ sym_lzma_end(ls);
+}
+#endif
+
#define ALIGN_8(l) ALIGN_TO(l, sizeof(size_t))
static const char* const compression_table[_COMPRESSION_MAX] = {
@@ -75,8 +127,33 @@ bool compression_supported(Compression c) {
return c >= 0 && c < _COMPRESSION_MAX && FLAGS_SET(supported, 1U << c);
}
+#if HAVE_XZ
+int dlopen_lzma(void) {
+ ELF_NOTE_DLOPEN("lzma",
+ "Support lzma compression in journal and coredump files",
+ COMPRESSION_PRIORITY_XZ,
+ "liblzma.so.5");
+
+ return dlopen_many_sym_or_warn(
+ &lzma_dl,
+ "liblzma.so.5", LOG_DEBUG,
+ DLSYM_ARG(lzma_code),
+ DLSYM_ARG(lzma_easy_encoder),
+ DLSYM_ARG(lzma_end),
+ DLSYM_ARG(lzma_stream_buffer_encode),
+ DLSYM_ARG(lzma_stream_decoder));
+}
+#endif
+
int compress_blob_xz(const void *src, uint64_t src_size,
void *dst, size_t dst_alloc_size, size_t *dst_size) {
+
+ assert(src);
+ assert(src_size > 0);
+ assert(dst);
+ assert(dst_alloc_size > 0);
+ assert(dst_size);
+
#if HAVE_XZ
static const lzma_options_lzma opt = {
1u << 20u, NULL, 0, LZMA_LC_DEFAULT, LZMA_LP_DEFAULT,
@@ -88,12 +165,11 @@ int compress_blob_xz(const void *src, uint64_t src_size,
};
lzma_ret ret;
size_t out_pos = 0;
+ int r;
- assert(src);
- assert(src_size > 0);
- assert(dst);
- assert(dst_alloc_size > 0);
- assert(dst_size);
+ r = dlopen_lzma();
+ if (r < 0)
+ return r;
/* Returns < 0 if we couldn't compress the data or the
* compressed result is longer than the original */
@@ -101,7 +177,7 @@ int compress_blob_xz(const void *src, uint64_t src_size,
if (src_size < 80)
return -ENOBUFS;
- ret = lzma_stream_buffer_encode((lzma_filter*) filters, LZMA_CHECK_NONE, NULL,
+ ret = sym_lzma_stream_buffer_encode((lzma_filter*) filters, LZMA_CHECK_NONE, NULL,
src, src_size, dst, &out_pos, dst_alloc_size);
if (ret != LZMA_OK)
return -ENOBUFS;
@@ -113,10 +189,35 @@ int compress_blob_xz(const void *src, uint64_t src_size,
#endif
}
+#if HAVE_LZ4
+int dlopen_lz4(void) {
+ ELF_NOTE_DLOPEN("lz4",
+ "Support lz4 compression in journal and coredump files",
+ COMPRESSION_PRIORITY_LZ4,
+ "liblz4.so.1");
+
+ return dlopen_many_sym_or_warn(
+ &lz4_dl,
+ "liblz4.so.1", LOG_DEBUG,
+ DLSYM_ARG(LZ4F_compressBegin),
+ DLSYM_ARG(LZ4F_compressBound),
+ DLSYM_ARG(LZ4F_compressEnd),
+ DLSYM_ARG(LZ4F_compressUpdate),
+ DLSYM_ARG(LZ4F_createCompressionContext),
+ DLSYM_ARG(LZ4F_createDecompressionContext),
+ DLSYM_ARG(LZ4F_decompress),
+ DLSYM_ARG(LZ4F_freeCompressionContext),
+ DLSYM_ARG(LZ4F_freeDecompressionContext),
+ DLSYM_ARG(LZ4F_isError),
+ DLSYM_ARG(LZ4_compress_default),
+ DLSYM_ARG(LZ4_decompress_safe),
+ DLSYM_ARG(LZ4_decompress_safe_partial),
+ DLSYM_ARG(LZ4_versionNumber));
+}
+#endif
+
int compress_blob_lz4(const void *src, uint64_t src_size,
void *dst, size_t dst_alloc_size, size_t *dst_size) {
-#if HAVE_LZ4
- int r;
assert(src);
assert(src_size > 0);
@@ -124,13 +225,19 @@ int compress_blob_lz4(const void *src, uint64_t src_size,
assert(dst_alloc_size > 0);
assert(dst_size);
+#if HAVE_LZ4
+ int r;
+
+ r = dlopen_lz4();
+ if (r < 0)
+ return r;
/* Returns < 0 if we couldn't compress the data or the
* compressed result is longer than the original */
if (src_size < 9)
return -ENOBUFS;
- r = LZ4_compress_default(src, (char*)dst + 8, src_size, (int) dst_alloc_size - 8);
+ r = sym_LZ4_compress_default(src, (char*)dst + 8, src_size, (int) dst_alloc_size - 8);
if (r <= 0)
return -ENOBUFS;
@@ -143,11 +250,38 @@ int compress_blob_lz4(const void *src, uint64_t src_size,
#endif
}
+#if HAVE_ZSTD
+int dlopen_zstd(void) {
+ ELF_NOTE_DLOPEN("zstd",
+ "Support zstd compression in journal and coredump files",
+ COMPRESSION_PRIORITY_ZSTD,
+ "libzstd.so.1");
+
+ return dlopen_many_sym_or_warn(
+ &zstd_dl,
+ "libzstd.so.1", LOG_DEBUG,
+ DLSYM_ARG(ZSTD_getErrorCode),
+ DLSYM_ARG(ZSTD_compress),
+ DLSYM_ARG(ZSTD_getFrameContentSize),
+ DLSYM_ARG(ZSTD_decompressStream),
+ DLSYM_ARG(ZSTD_getErrorName),
+ DLSYM_ARG(ZSTD_DStreamOutSize),
+ DLSYM_ARG(ZSTD_CStreamInSize),
+ DLSYM_ARG(ZSTD_CStreamOutSize),
+ DLSYM_ARG(ZSTD_CCtx_setParameter),
+ DLSYM_ARG(ZSTD_compressStream2),
+ DLSYM_ARG(ZSTD_DStreamInSize),
+ DLSYM_ARG(ZSTD_freeCCtx),
+ DLSYM_ARG(ZSTD_freeDCtx),
+ DLSYM_ARG(ZSTD_isError),
+ DLSYM_ARG(ZSTD_createDCtx),
+ DLSYM_ARG(ZSTD_createCCtx));
+}
+#endif
+
int compress_blob_zstd(
const void *src, uint64_t src_size,
void *dst, size_t dst_alloc_size, size_t *dst_size) {
-#if HAVE_ZSTD
- size_t k;
assert(src);
assert(src_size > 0);
@@ -155,8 +289,16 @@ int compress_blob_zstd(
assert(dst_alloc_size > 0);
assert(dst_size);
- k = ZSTD_compress(dst, dst_alloc_size, src, src_size, 0);
- if (ZSTD_isError(k))
+#if HAVE_ZSTD
+ size_t k;
+ int r;
+
+ r = dlopen_zstd();
+ if (r < 0)
+ return r;
+
+ k = sym_ZSTD_compress(dst, dst_alloc_size, src, src_size, 0);
+ if (sym_ZSTD_isError(k))
return zstd_ret_to_errno(k);
*dst_size = k;
@@ -173,17 +315,22 @@ int decompress_blob_xz(
size_t* dst_size,
size_t dst_max) {
-#if HAVE_XZ
- _cleanup_(lzma_end) lzma_stream s = LZMA_STREAM_INIT;
- lzma_ret ret;
- size_t space;
-
assert(src);
assert(src_size > 0);
assert(dst);
assert(dst_size);
- ret = lzma_stream_decoder(&s, UINT64_MAX, 0);
+#if HAVE_XZ
+ _cleanup_(lzma_end_wrapper) lzma_stream s = LZMA_STREAM_INIT;
+ lzma_ret ret;
+ size_t space;
+ int r;
+
+ r = dlopen_lzma();
+ if (r < 0)
+ return r;
+
+ ret = sym_lzma_stream_decoder(&s, UINT64_MAX, 0);
if (ret != LZMA_OK)
return -ENOMEM;
@@ -200,7 +347,7 @@ int decompress_blob_xz(
for (;;) {
size_t used;
- ret = lzma_code(&s, LZMA_FINISH);
+ ret = sym_lzma_code(&s, LZMA_FINISH);
if (ret == LZMA_STREAM_END)
break;
@@ -235,15 +382,19 @@ int decompress_blob_lz4(
size_t* dst_size,
size_t dst_max) {
-#if HAVE_LZ4
- char* out;
- int r, size; /* LZ4 uses int for size */
-
assert(src);
assert(src_size > 0);
assert(dst);
assert(dst_size);
+#if HAVE_LZ4
+ char* out;
+ int r, size; /* LZ4 uses int for size */
+
+ r = dlopen_lz4();
+ if (r < 0)
+ return r;
+
if (src_size <= 8)
return -EBADMSG;
@@ -254,7 +405,7 @@ int decompress_blob_lz4(
if (!out)
return -ENOMEM;
- r = LZ4_decompress_safe((char*)src + 8, out, src_size - 8, size);
+ r = sym_LZ4_decompress_safe((char*)src + 8, out, src_size - 8, size);
if (r < 0 || r != size)
return -EBADMSG;
@@ -272,15 +423,20 @@ int decompress_blob_zstd(
size_t *dst_size,
size_t dst_max) {
-#if HAVE_ZSTD
- uint64_t size;
-
assert(src);
assert(src_size > 0);
assert(dst);
assert(dst_size);
- size = ZSTD_getFrameContentSize(src, src_size);
+#if HAVE_ZSTD
+ uint64_t size;
+ int r;
+
+ r = dlopen_zstd();
+ if (r < 0)
+ return r;
+
+ size = sym_ZSTD_getFrameContentSize(src, src_size);
if (IN_SET(size, ZSTD_CONTENTSIZE_ERROR, ZSTD_CONTENTSIZE_UNKNOWN))
return -EBADMSG;
@@ -289,10 +445,10 @@ int decompress_blob_zstd(
if (size > SIZE_MAX)
return -E2BIG;
- if (!(greedy_realloc(dst, MAX(ZSTD_DStreamOutSize(), size), 1)))
+ if (!(greedy_realloc(dst, MAX(sym_ZSTD_DStreamOutSize(), size), 1)))
return -ENOMEM;
- _cleanup_(ZSTD_freeDCtxp) ZSTD_DCtx *dctx = ZSTD_createDCtx();
+ _cleanup_(sym_ZSTD_freeDCtxp) ZSTD_DCtx *dctx = sym_ZSTD_createDCtx();
if (!dctx)
return -ENOMEM;
@@ -305,9 +461,9 @@ int decompress_blob_zstd(
.size = MALLOC_SIZEOF_SAFE(*dst),
};
- size_t k = ZSTD_decompressStream(dctx, &output, &input);
- if (ZSTD_isError(k)) {
- log_debug("ZSTD decoder failed: %s", ZSTD_getErrorName(k));
+ size_t k = sym_ZSTD_decompressStream(dctx, &output, &input);
+ if (sym_ZSTD_isError(k)) {
+ log_debug("ZSTD decoder failed: %s", sym_ZSTD_getErrorName(k));
return zstd_ret_to_errno(k);
}
assert(output.pos >= size);
@@ -351,11 +507,6 @@ int decompress_startswith_xz(
size_t prefix_len,
uint8_t extra) {
-#if HAVE_XZ
- _cleanup_(lzma_end) lzma_stream s = LZMA_STREAM_INIT;
- size_t allocated;
- lzma_ret ret;
-
/* Checks whether the decompressed blob starts with the mentioned prefix. The byte extra needs to
* follow the prefix */
@@ -364,7 +515,17 @@ int decompress_startswith_xz(
assert(buffer);
assert(prefix);
- ret = lzma_stream_decoder(&s, UINT64_MAX, 0);
+#if HAVE_XZ
+ _cleanup_(lzma_end_wrapper) lzma_stream s = LZMA_STREAM_INIT;
+ size_t allocated;
+ lzma_ret ret;
+ int r;
+
+ r = dlopen_lzma();
+ if (r < 0)
+ return r;
+
+ ret = sym_lzma_stream_decoder(&s, UINT64_MAX, 0);
if (ret != LZMA_OK)
return -EBADMSG;
@@ -380,7 +541,7 @@ int decompress_startswith_xz(
s.avail_out = allocated;
for (;;) {
- ret = lzma_code(&s, LZMA_FINISH);
+ ret = sym_lzma_code(&s, LZMA_FINISH);
if (!IN_SET(ret, LZMA_OK, LZMA_STREAM_END))
return -EBADMSG;
@@ -414,18 +575,22 @@ int decompress_startswith_lz4(
size_t prefix_len,
uint8_t extra) {
-#if HAVE_LZ4
/* Checks whether the decompressed blob starts with the mentioned prefix. The byte extra needs to
* follow the prefix */
- size_t allocated;
- int r;
-
assert(src);
assert(src_size > 0);
assert(buffer);
assert(prefix);
+#if HAVE_LZ4
+ size_t allocated;
+ int r;
+
+ r = dlopen_lz4();
+ if (r < 0)
+ return r;
+
if (src_size <= 8)
return -EBADMSG;
@@ -433,7 +598,7 @@ int decompress_startswith_lz4(
return -ENOMEM;
allocated = MALLOC_SIZEOF_SAFE(*buffer);
- r = LZ4_decompress_safe_partial(
+ r = sym_LZ4_decompress_safe_partial(
(char*)src + 8,
*buffer,
src_size - 8,
@@ -447,7 +612,7 @@ int decompress_startswith_lz4(
if (r < 0 || (size_t) r < prefix_len + 1) {
size_t size;
- if (LZ4_versionNumber() >= 10803)
+ if (sym_LZ4_versionNumber() >= 10803)
/* We trust that the newer lz4 decompresses the number of bytes we
* requested if available in the compressed string. */
return 0;
@@ -482,24 +647,31 @@ int decompress_startswith_zstd(
const void *prefix,
size_t prefix_len,
uint8_t extra) {
-#if HAVE_ZSTD
+
assert(src);
assert(src_size > 0);
assert(buffer);
assert(prefix);
- uint64_t size = ZSTD_getFrameContentSize(src, src_size);
+#if HAVE_ZSTD
+ int r;
+
+ r = dlopen_zstd();
+ if (r < 0)
+ return r;
+
+ uint64_t size = sym_ZSTD_getFrameContentSize(src, src_size);
if (IN_SET(size, ZSTD_CONTENTSIZE_ERROR, ZSTD_CONTENTSIZE_UNKNOWN))
return -EBADMSG;
if (size < prefix_len + 1)
return 0; /* Decompressed text too short to match the prefix and extra */
- _cleanup_(ZSTD_freeDCtxp) ZSTD_DCtx *dctx = ZSTD_createDCtx();
+ _cleanup_(sym_ZSTD_freeDCtxp) ZSTD_DCtx *dctx = sym_ZSTD_createDCtx();
if (!dctx)
return -ENOMEM;
- if (!(greedy_realloc(buffer, MAX(ZSTD_DStreamOutSize(), prefix_len + 1), 1)))
+ if (!(greedy_realloc(buffer, MAX(sym_ZSTD_DStreamOutSize(), prefix_len + 1), 1)))
return -ENOMEM;
ZSTD_inBuffer input = {
@@ -512,9 +684,9 @@ int decompress_startswith_zstd(
};
size_t k;
- k = ZSTD_decompressStream(dctx, &output, &input);
- if (ZSTD_isError(k)) {
- log_debug("ZSTD decoder failed: %s", ZSTD_getErrorName(k));
+ k = sym_ZSTD_decompressStream(dctx, &output, &input);
+ if (sym_ZSTD_isError(k)) {
+ log_debug("ZSTD decoder failed: %s", sym_ZSTD_getErrorName(k));
return zstd_ret_to_errno(k);
}
assert(output.pos >= prefix_len + 1);
@@ -559,16 +731,21 @@ int decompress_startswith(
}
int compress_stream_xz(int fdf, int fdt, uint64_t max_bytes, uint64_t *ret_uncompressed_size) {
+ assert(fdf >= 0);
+ assert(fdt >= 0);
+
#if HAVE_XZ
- _cleanup_(lzma_end) lzma_stream s = LZMA_STREAM_INIT;
+ _cleanup_(lzma_end_wrapper) lzma_stream s = LZMA_STREAM_INIT;
lzma_ret ret;
uint8_t buf[BUFSIZ], out[BUFSIZ];
lzma_action action = LZMA_RUN;
+ int r;
- assert(fdf >= 0);
- assert(fdt >= 0);
+ r = dlopen_lzma();
+ if (r < 0)
+ return r;
- ret = lzma_easy_encoder(&s, LZMA_PRESET_DEFAULT, LZMA_CHECK_CRC64);
+ ret = sym_lzma_easy_encoder(&s, LZMA_PRESET_DEFAULT, LZMA_CHECK_CRC64);
if (ret != LZMA_OK)
return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
"Failed to initialize XZ encoder: code %u",
@@ -603,7 +780,7 @@ int compress_stream_xz(int fdf, int fdt, uint64_t max_bytes, uint64_t *ret_uncom
s.avail_out = sizeof(out);
}
- ret = lzma_code(&s, action);
+ ret = sym_lzma_code(&s, action);
if (!IN_SET(ret, LZMA_OK, LZMA_STREAM_END))
return log_error_errno(SYNTHETIC_ERRNO(EBADMSG),
"Compression failed: code %u",
@@ -641,7 +818,7 @@ int compress_stream_lz4(int fdf, int fdt, uint64_t max_bytes, uint64_t *ret_unco
#if HAVE_LZ4
LZ4F_errorCode_t c;
- _cleanup_(LZ4F_freeCompressionContextp) LZ4F_compressionContext_t ctx = NULL;
+ _cleanup_(sym_LZ4F_freeCompressionContextp) LZ4F_compressionContext_t ctx = NULL;
_cleanup_free_ void *in_buff = NULL;
_cleanup_free_ char *out_buff = NULL;
size_t out_allocsize, n, offset = 0, frame_size;
@@ -651,11 +828,15 @@ int compress_stream_lz4(int fdf, int fdt, uint64_t max_bytes, uint64_t *ret_unco
.frameInfo.blockSizeID = 5,
};
- c = LZ4F_createCompressionContext(&ctx, LZ4F_VERSION);
- if (LZ4F_isError(c))
+ r = dlopen_lz4();
+ if (r < 0)
+ return r;
+
+ c = sym_LZ4F_createCompressionContext(&ctx, LZ4F_VERSION);
+ if (sym_LZ4F_isError(c))
return -ENOMEM;
- frame_size = LZ4F_compressBound(LZ4_BUFSIZE, &preferences);
+ frame_size = sym_LZ4F_compressBound(LZ4_BUFSIZE, &preferences);
out_allocsize = frame_size + 64*1024; /* add some space for header and trailer */
out_buff = malloc(out_allocsize);
if (!out_buff)
@@ -665,8 +846,8 @@ int compress_stream_lz4(int fdf, int fdt, uint64_t max_bytes, uint64_t *ret_unco
if (!in_buff)
return -ENOMEM;
- n = offset = total_out = LZ4F_compressBegin(ctx, out_buff, out_allocsize, &preferences);
- if (LZ4F_isError(n))
+ n = offset = total_out = sym_LZ4F_compressBegin(ctx, out_buff, out_allocsize, &preferences);
+ if (sym_LZ4F_isError(n))
return -EINVAL;
log_debug("Buffer size is %zu bytes, header size %zu bytes.", out_allocsize, n);
@@ -679,9 +860,9 @@ int compress_stream_lz4(int fdf, int fdt, uint64_t max_bytes, uint64_t *ret_unco
return k;
if (k == 0)
break;
- n = LZ4F_compressUpdate(ctx, out_buff + offset, out_allocsize - offset,
+ n = sym_LZ4F_compressUpdate(ctx, out_buff + offset, out_allocsize - offset,
in_buff, k, NULL);
- if (LZ4F_isError(n))
+ if (sym_LZ4F_isError(n))
return -ENOTRECOVERABLE;
total_in += k;
@@ -700,8 +881,8 @@ int compress_stream_lz4(int fdf, int fdt, uint64_t max_bytes, uint64_t *ret_unco
}
}
- n = LZ4F_compressEnd(ctx, out_buff + offset, out_allocsize - offset, NULL);
- if (LZ4F_isError(n))
+ n = sym_LZ4F_compressEnd(ctx, out_buff + offset, out_allocsize - offset, NULL);
+ if (sym_LZ4F_isError(n))
return -ENOTRECOVERABLE;
offset += n;
@@ -724,18 +905,22 @@ int compress_stream_lz4(int fdf, int fdt, uint64_t max_bytes, uint64_t *ret_unco
}
int decompress_stream_xz(int fdf, int fdt, uint64_t max_bytes) {
+ assert(fdf >= 0);
+ assert(fdt >= 0);
#if HAVE_XZ
- _cleanup_(lzma_end) lzma_stream s = LZMA_STREAM_INIT;
+ _cleanup_(lzma_end_wrapper) lzma_stream s = LZMA_STREAM_INIT;
lzma_ret ret;
uint8_t buf[BUFSIZ], out[BUFSIZ];
lzma_action action = LZMA_RUN;
+ int r;
- assert(fdf >= 0);
- assert(fdt >= 0);
+ r = dlopen_lzma();
+ if (r < 0)
+ return r;
- ret = lzma_stream_decoder(&s, UINT64_MAX, 0);
+ ret = sym_lzma_stream_decoder(&s, UINT64_MAX, 0);
if (ret != LZMA_OK)
return log_debug_errno(SYNTHETIC_ERRNO(ENOMEM),
"Failed to initialize XZ decoder: code %u",
@@ -761,7 +946,7 @@ int decompress_stream_xz(int fdf, int fdt, uint64_t max_bytes) {
s.avail_out = sizeof(out);
}
- ret = lzma_code(&s, action);
+ ret = sym_lzma_code(&s, action);
if (!IN_SET(ret, LZMA_OK, LZMA_STREAM_END))
return log_debug_errno(SYNTHETIC_ERRNO(EBADMSG),
"Decompression failed: code %u",
@@ -801,15 +986,19 @@ int decompress_stream_xz(int fdf, int fdt, uint64_t max_bytes) {
int decompress_stream_lz4(int in, int out, uint64_t max_bytes) {
#if HAVE_LZ4
size_t c;
- _cleanup_(LZ4F_freeDecompressionContextp) LZ4F_decompressionContext_t ctx = NULL;
+ _cleanup_(sym_LZ4F_freeDecompressionContextp) LZ4F_decompressionContext_t ctx = NULL;
_cleanup_free_ char *buf = NULL;
char *src;
struct stat st;
- int r = 0;
+ int r;
size_t total_in = 0, total_out = 0;
- c = LZ4F_createDecompressionContext(&ctx, LZ4F_VERSION);
- if (LZ4F_isError(c))
+ r = dlopen_lz4();
+ if (r < 0)
+ return r;
+
+ c = sym_LZ4F_createDecompressionContext(&ctx, LZ4F_VERSION);
+ if (sym_LZ4F_isError(c))
return -ENOMEM;
if (fstat(in, &st) < 0)
@@ -830,8 +1019,8 @@ int decompress_stream_lz4(int in, int out, uint64_t max_bytes) {
size_t produced = LZ4_BUFSIZE;
size_t used = st.st_size - total_in;
- c = LZ4F_decompress(ctx, buf, &produced, src + total_in, &used, NULL);
- if (LZ4F_isError(c)) {
+ c = sym_LZ4F_decompress(ctx, buf, &produced, src + total_in, &used, NULL);
+ if (sym_LZ4F_isError(c)) {
r = -EBADMSG;
goto cleanup;
}
@@ -853,6 +1042,7 @@ int decompress_stream_lz4(int in, int out, uint64_t max_bytes) {
log_debug("LZ4 decompression finished (%zu -> %zu bytes, %.1f%%)",
total_in, total_out,
total_in > 0 ? (double) total_out / total_in * 100 : 0.0);
+ r = 0;
cleanup:
munmap(src, st.st_size);
return r;
@@ -863,28 +1053,33 @@ int decompress_stream_lz4(int in, int out, uint64_t max_bytes) {
}
int compress_stream_zstd(int fdf, int fdt, uint64_t max_bytes, uint64_t *ret_uncompressed_size) {
+ assert(fdf >= 0);
+ assert(fdt >= 0);
+
#if HAVE_ZSTD
- _cleanup_(ZSTD_freeCCtxp) ZSTD_CCtx *cctx = NULL;
+ _cleanup_(sym_ZSTD_freeCCtxp) ZSTD_CCtx *cctx = NULL;
_cleanup_free_ void *in_buff = NULL, *out_buff = NULL;
size_t in_allocsize, out_allocsize;
size_t z;
uint64_t left = max_bytes, in_bytes = 0;
+ int r;
- assert(fdf >= 0);
- assert(fdt >= 0);
+ r = dlopen_zstd();
+ if (r < 0)
+ return r;
/* Create the context and buffers */
- in_allocsize = ZSTD_CStreamInSize();
- out_allocsize = ZSTD_CStreamOutSize();
+ in_allocsize = sym_ZSTD_CStreamInSize();
+ out_allocsize = sym_ZSTD_CStreamOutSize();
in_buff = malloc(in_allocsize);
out_buff = malloc(out_allocsize);
- cctx = ZSTD_createCCtx();
+ cctx = sym_ZSTD_createCCtx();
if (!cctx || !out_buff || !in_buff)
return -ENOMEM;
- z = ZSTD_CCtx_setParameter(cctx, ZSTD_c_checksumFlag, 1);
- if (ZSTD_isError(z))
- log_debug("Failed to enable ZSTD checksum, ignoring: %s", ZSTD_getErrorName(z));
+ z = sym_ZSTD_CCtx_setParameter(cctx, ZSTD_c_checksumFlag, 1);
+ if (sym_ZSTD_isError(z))
+ log_debug("Failed to enable ZSTD checksum, ignoring: %s", sym_ZSTD_getErrorName(z));
/* This loop read from the input file, compresses that entire chunk,
* and writes all output produced to the output file.
@@ -919,12 +1114,12 @@ int compress_stream_zstd(int fdf, int fdt, uint64_t max_bytes, uint64_t *ret_unc
* output to the file so we can reuse the buffer next
* iteration.
*/
- remaining = ZSTD_compressStream2(
+ remaining = sym_ZSTD_compressStream2(
cctx, &output, &input,
is_last_chunk ? ZSTD_e_end : ZSTD_e_continue);
- if (ZSTD_isError(remaining)) {
- log_debug("ZSTD encoder failed: %s", ZSTD_getErrorName(remaining));
+ if (sym_ZSTD_isError(remaining)) {
+ log_debug("ZSTD encoder failed: %s", sym_ZSTD_getErrorName(remaining));
return zstd_ret_to_errno(remaining);
}
@@ -968,22 +1163,26 @@ int compress_stream_zstd(int fdf, int fdt, uint64_t max_bytes, uint64_t *ret_unc
}
int decompress_stream_zstd(int fdf, int fdt, uint64_t max_bytes) {
+ assert(fdf >= 0);
+ assert(fdt >= 0);
+
#if HAVE_ZSTD
- _cleanup_(ZSTD_freeDCtxp) ZSTD_DCtx *dctx = NULL;
+ _cleanup_(sym_ZSTD_freeDCtxp) ZSTD_DCtx *dctx = NULL;
_cleanup_free_ void *in_buff = NULL, *out_buff = NULL;
size_t in_allocsize, out_allocsize;
size_t last_result = 0;
uint64_t left = max_bytes, in_bytes = 0;
+ int r;
- assert(fdf >= 0);
- assert(fdt >= 0);
-
+ r = dlopen_zstd();
+ if (r < 0)
+ return r;
/* Create the context and buffers */
- in_allocsize = ZSTD_DStreamInSize();
- out_allocsize = ZSTD_DStreamOutSize();
+ in_allocsize = sym_ZSTD_DStreamInSize();
+ out_allocsize = sym_ZSTD_DStreamOutSize();
in_buff = malloc(in_allocsize);
out_buff = malloc(out_allocsize);
- dctx = ZSTD_createDCtx();
+ dctx = sym_ZSTD_createDCtx();
if (!dctx || !out_buff || !in_buff)
return -ENOMEM;
@@ -1032,8 +1231,8 @@ int decompress_stream_zstd(int fdf, int fdt, uint64_t max_bytes) {
* for instance if the last decompression call returned
* an error.
*/
- last_result = ZSTD_decompressStream(dctx, &output, &input);
- if (ZSTD_isError(last_result)) {
+ last_result = sym_ZSTD_decompressStream(dctx, &output, &input);
+ if (sym_ZSTD_isError(last_result)) {
has_error = true;
break;
}
@@ -1059,7 +1258,7 @@ int decompress_stream_zstd(int fdf, int fdt, uint64_t max_bytes) {
* on a frame, but we reached the end of the file! We assume
* this is an error, and the input was truncated.
*/
- log_debug("ZSTD decoder failed: %s", ZSTD_getErrorName(last_result));
+ log_debug("ZSTD decoder failed: %s", sym_ZSTD_getErrorName(last_result));
return zstd_ret_to_errno(last_result);
}
diff --git a/src/basic/compress.h b/src/basic/compress.h
index 1b5c645..d15c189 100644
--- a/src/basic/compress.h
+++ b/src/basic/compress.h
@@ -6,6 +6,13 @@
#include <stdint.h>
#include <unistd.h>
+#if HAVE_LZ4
+#include <lz4.h>
+#include <lz4frame.h>
+#endif
+
+#include "dlfcn-util.h"
+
typedef enum Compression {
COMPRESSION_NONE,
COMPRESSION_XZ,
@@ -63,6 +70,23 @@ int decompress_stream_xz(int fdf, int fdt, uint64_t max_size);
int decompress_stream_lz4(int fdf, int fdt, uint64_t max_size);
int decompress_stream_zstd(int fdf, int fdt, uint64_t max_size);
+#if HAVE_LZ4
+DLSYM_PROTOTYPE(LZ4_compress_default);
+DLSYM_PROTOTYPE(LZ4_decompress_safe);
+DLSYM_PROTOTYPE(LZ4_decompress_safe_partial);
+DLSYM_PROTOTYPE(LZ4_versionNumber);
+
+int dlopen_lz4(void);
+#endif
+
+#if HAVE_ZSTD
+int dlopen_zstd(void);
+#endif
+
+#if HAVE_XZ
+int dlopen_lzma(void);
+#endif
+
static inline int compress_blob(
Compression compression,
const void *src, uint64_t src_size,
diff --git a/src/basic/conf-files.c b/src/basic/conf-files.c
index a56f82f..7fdcc71 100644
--- a/src/basic/conf-files.c
+++ b/src/basic/conf-files.c
@@ -9,7 +9,10 @@
#include "conf-files.h"
#include "constants.h"
#include "dirent-util.h"
+#include "errno-util.h"
#include "fd-util.h"
+#include "fileio.h"
+#include "glyph-util.h"
#include "hashmap.h"
#include "log.h"
#include "macro.h"
@@ -366,9 +369,103 @@ int conf_files_list_dropins(
assert(dirs);
suffix = strjoina("/", dropin_dirname);
- r = strv_extend_strv_concat(&dropin_dirs, (char**) dirs, suffix);
+ r = strv_extend_strv_concat(&dropin_dirs, dirs, suffix);
if (r < 0)
return r;
return conf_files_list_strv(ret, ".conf", root, 0, (const char* const*) dropin_dirs);
}
+
+/**
+ * Open and read a config file.
+ *
+ * The <fn> argument may be:
+ * - '-', meaning stdin.
+ * - a file name without a path. In this case <config_dirs> are searched.
+ * - a path, either relative or absolute. In this case <fn> is opened directly.
+ *
+ * This method is only suitable for configuration files which have a flat layout without dropins.
+ */
+int conf_file_read(
+ const char *root,
+ const char **config_dirs,
+ const char *fn,
+ parse_line_t parse_line,
+ void *userdata,
+ bool ignore_enoent,
+ bool *invalid_config) {
+
+ _cleanup_fclose_ FILE *_f = NULL;
+ _cleanup_free_ char *_fn = NULL;
+ unsigned v = 0;
+ FILE *f;
+ int r = 0;
+
+ assert(fn);
+
+ if (streq(fn, "-")) {
+ f = stdin;
+ fn = "<stdin>";
+
+ log_debug("Reading config from stdin%s", special_glyph(SPECIAL_GLYPH_ELLIPSIS));
+
+ } else if (is_path(fn)) {
+ r = path_make_absolute_cwd(fn, &_fn);
+ if (r < 0)
+ return log_error_errno(r, "Failed to make path absolute: %m");
+ fn = _fn;
+
+ f = _f = fopen(fn, "re");
+ if (!_f)
+ r = -errno;
+ else
+ log_debug("Reading config file \"%s\"%s", fn, special_glyph(SPECIAL_GLYPH_ELLIPSIS));
+
+ } else {
+ r = search_and_fopen(fn, "re", root, config_dirs, &_f, &_fn);
+ if (r >= 0) {
+ f = _f;
+ fn = _fn;
+ log_debug("Reading config file \"%s\"%s", fn, special_glyph(SPECIAL_GLYPH_ELLIPSIS));
+ }
+ }
+
+ if (r == -ENOENT && ignore_enoent) {
+ log_debug_errno(r, "Failed to open \"%s\", ignoring: %m", fn);
+ return 0; /* No error, but nothing happened. */
+ }
+ if (r < 0)
+ return log_error_errno(r, "Failed to read '%s': %m", fn);
+
+ r = 1; /* We entered the part where we may modify state. */
+
+ for (;;) {
+ _cleanup_free_ char *line = NULL;
+ bool invalid_line = false;
+ int k;
+
+ k = read_stripped_line(f, LONG_LINE_MAX, &line);
+ if (k < 0)
+ return log_error_errno(k, "Failed to read '%s': %m", fn);
+ if (k == 0)
+ break;
+
+ v++;
+
+ if (IN_SET(line[0], 0, '#'))
+ continue;
+
+ k = parse_line(fn, v, line, invalid_config ? &invalid_line : NULL, userdata);
+ if (k < 0 && invalid_line)
+ /* Allow reporting with a special code if the caller requested this. */
+ *invalid_config = true;
+ else
+ /* The first error, if any, becomes our return value. */
+ RET_GATHER(r, k);
+ }
+
+ if (ferror(f))
+ RET_GATHER(r, log_error_errno(SYNTHETIC_ERRNO(EIO), "Failed to read from file %s.", fn));
+
+ return r;
+}
diff --git a/src/basic/conf-files.h b/src/basic/conf-files.h
index 566cc8f..cf89ee6 100644
--- a/src/basic/conf-files.h
+++ b/src/basic/conf-files.h
@@ -1,6 +1,8 @@
/* SPDX-License-Identifier: LGPL-2.1-or-later */
#pragma once
+#include <stdbool.h>
+
#include "macro.h"
enum {
@@ -29,3 +31,19 @@ int conf_files_list_dropins(
const char *dropin_dirname,
const char *root,
const char * const *dirs);
+
+typedef int parse_line_t(
+ const char *fname,
+ unsigned line,
+ const char *buffer,
+ bool *invalid_config,
+ void *userdata);
+
+int conf_file_read(
+ const char *root,
+ const char **config_dirs,
+ const char *fn,
+ parse_line_t parse_line,
+ void *userdata,
+ bool ignore_enoent,
+ bool *invalid_config);
diff --git a/src/basic/constants.h b/src/basic/constants.h
index 6bb5f3c..e70817c 100644
--- a/src/basic/constants.h
+++ b/src/basic/constants.h
@@ -42,9 +42,6 @@
#define DEFAULT_START_LIMIT_INTERVAL (10*USEC_PER_SEC)
#define DEFAULT_START_LIMIT_BURST 5
-/* Wait for 1.5 seconds at maximum for freeze operation */
-#define FREEZE_TIMEOUT (1500 * USEC_PER_MSEC)
-
/* The default time after which exit-on-idle services exit. This
* should be kept lower than the watchdog timeout, because otherwise
* the watchdog pings will keep the loop busy. */
@@ -67,18 +64,12 @@
"/usr/local/lib/" n "\0" \
"/usr/lib/" n "\0"
-#define CONF_PATHS_USR(n) \
+#define CONF_PATHS(n) \
"/etc/" n, \
"/run/" n, \
"/usr/local/lib/" n, \
"/usr/lib/" n
-#define CONF_PATHS(n) \
- CONF_PATHS_USR(n)
-
-#define CONF_PATHS_USR_STRV(n) \
- STRV_MAKE(CONF_PATHS_USR(n))
-
#define CONF_PATHS_STRV(n) \
STRV_MAKE(CONF_PATHS(n))
diff --git a/src/basic/devnum-util.c b/src/basic/devnum-util.c
index f82e13b..652740c 100644
--- a/src/basic/devnum-util.c
+++ b/src/basic/devnum-util.c
@@ -58,21 +58,18 @@ int device_path_make_major_minor(mode_t mode, dev_t devnum, char **ret) {
}
int device_path_make_inaccessible(mode_t mode, char **ret) {
- char *s;
+ const char *s;
assert(ret);
if (S_ISCHR(mode))
- s = strdup("/run/systemd/inaccessible/chr");
+ s = "/run/systemd/inaccessible/chr";
else if (S_ISBLK(mode))
- s = strdup("/run/systemd/inaccessible/blk");
+ s = "/run/systemd/inaccessible/blk";
else
return -ENODEV;
- if (!s)
- return -ENOMEM;
- *ret = s;
- return 0;
+ return strdup_to(ret, s);
}
int device_path_make_canonical(mode_t mode, dev_t devnum, char **ret) {
diff --git a/src/shared/dlfcn-util.c b/src/basic/dlfcn-util.c
index 8022f55..8022f55 100644
--- a/src/shared/dlfcn-util.c
+++ b/src/basic/dlfcn-util.c
diff --git a/src/basic/dlfcn-util.h b/src/basic/dlfcn-util.h
new file mode 100644
index 0000000..83ab320
--- /dev/null
+++ b/src/basic/dlfcn-util.h
@@ -0,0 +1,82 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+#pragma once
+
+#include <dlfcn.h>
+
+#include "macro.h"
+
+static inline void* safe_dlclose(void *dl) {
+ if (!dl)
+ return NULL;
+
+ assert_se(dlclose(dl) == 0);
+ return NULL;
+}
+
+static inline void dlclosep(void **dlp) {
+ safe_dlclose(*dlp);
+}
+
+int dlsym_many_or_warn_sentinel(void *dl, int log_level, ...) _sentinel_;
+int dlopen_many_sym_or_warn_sentinel(void **dlp, const char *filename, int log_level, ...) _sentinel_;
+
+#define dlsym_many_or_warn(dl, log_level, ...) \
+ dlsym_many_or_warn_sentinel(dl, log_level, __VA_ARGS__, NULL)
+#define dlopen_many_sym_or_warn(dlp, filename, log_level, ...) \
+ dlopen_many_sym_or_warn_sentinel(dlp, filename, log_level, __VA_ARGS__, NULL)
+
+#define DLSYM_PROTOTYPE(symbol) \
+ extern typeof(symbol)* sym_##symbol
+#define DLSYM_FUNCTION(symbol) \
+ typeof(symbol)* sym_##symbol = NULL
+
+/* Macro useful for putting together variable/symbol name pairs when calling dlsym_many_or_warn(). Assumes
+ * that each library symbol to resolve will be placed in a variable with the "sym_" prefix, i.e. a symbol
+ * "foobar" is loaded into a variable "sym_foobar". */
+#define DLSYM_ARG(arg) \
+ ({ assert_cc(__builtin_types_compatible_p(typeof(sym_##arg), typeof(&arg))); &sym_##arg; }), STRINGIFY(arg)
+
+/* libbpf is a bit confused about type-safety and API compatibility. Provide a macro that can tape over that mess. Sad. */
+#define DLSYM_ARG_FORCE(arg) \
+ &sym_##arg, STRINGIFY(arg)
+
+#define ELF_NOTE_DLOPEN_VENDOR "FDO"
+#define ELF_NOTE_DLOPEN_TYPE UINT32_C(0x407c0c0a)
+#define ELF_NOTE_DLOPEN_PRIORITY_REQUIRED "required"
+#define ELF_NOTE_DLOPEN_PRIORITY_RECOMMENDED "recommended"
+#define ELF_NOTE_DLOPEN_PRIORITY_SUGGESTED "suggested"
+
+/* Add an ".note.dlopen" ELF note to our binary that declares our weak dlopen() dependency. This
+ * information can be read from an ELF file via "readelf -p .note.dlopen" or an equivalent command. */
+#define _ELF_NOTE_DLOPEN(json, variable_name) \
+ __attribute__((used, section(".note.dlopen"))) _Alignas(sizeof(uint32_t)) static const struct { \
+ struct { \
+ uint32_t n_namesz, n_descsz, n_type; \
+ } nhdr; \
+ char name[sizeof(ELF_NOTE_DLOPEN_VENDOR)]; \
+ _Alignas(sizeof(uint32_t)) char dlopen_json[sizeof(json)]; \
+ } variable_name = { \
+ .nhdr = { \
+ .n_namesz = sizeof(ELF_NOTE_DLOPEN_VENDOR), \
+ .n_descsz = sizeof(json), \
+ .n_type = ELF_NOTE_DLOPEN_TYPE, \
+ }, \
+ .name = ELF_NOTE_DLOPEN_VENDOR, \
+ .dlopen_json = json, \
+ }
+
+#define _SONAME_ARRAY1(a) "[\""a"\"]"
+#define _SONAME_ARRAY2(a, b) "[\""a"\",\""b"\"]"
+#define _SONAME_ARRAY3(a, b, c) "[\""a"\",\""b"\",\""c"\"]"
+#define _SONAME_ARRAY4(a, b, c, d) "[\""a"\",\""b"\",\""c"\"",\""d"\"]"
+#define _SONAME_ARRAY5(a, b, c, d, e) "[\""a"\",\""b"\",\""c"\"",\""d"\",\""e"\"]"
+#define _SONAME_ARRAY_GET(_1,_2,_3,_4,_5,NAME,...) NAME
+#define _SONAME_ARRAY(...) _SONAME_ARRAY_GET(__VA_ARGS__, _SONAME_ARRAY5, _SONAME_ARRAY4, _SONAME_ARRAY3, _SONAME_ARRAY2, _SONAME_ARRAY1)(__VA_ARGS__)
+
+/* The 'priority' must be one of 'required', 'recommended' or 'suggested' as per specification, use the
+ * macro defined above to specify it.
+ * Multiple sonames can be passed and they will be automatically constructed into a json array (but note that
+ * due to preprocessor language limitations if more than the limit defined above is used, a new
+ * _SONAME_ARRAY<X+1> will need to be added). */
+#define ELF_NOTE_DLOPEN(feature, description, priority, ...) \
+ _ELF_NOTE_DLOPEN("[{\"feature\":\"" feature "\",\"description\":\"" description "\",\"priority\":\"" priority "\",\"soname\":" _SONAME_ARRAY(__VA_ARGS__) "}]", UNIQ_T(s, UNIQ))
diff --git a/src/basic/efivars.c b/src/basic/efivars.c
index 9011ae2..8470d08 100644
--- a/src/basic/efivars.c
+++ b/src/basic/efivars.c
@@ -177,12 +177,13 @@ static int efi_verify_variable(const char *variable, uint32_t attr, const void *
}
int efi_set_variable(const char *variable, const void *value, size_t size) {
+ static const uint32_t attr = EFI_VARIABLE_NON_VOLATILE|EFI_VARIABLE_BOOTSERVICE_ACCESS|EFI_VARIABLE_RUNTIME_ACCESS;
+
struct var {
uint32_t attr;
char buf[];
} _packed_ * _cleanup_free_ buf = NULL;
_cleanup_close_ int fd = -EBADF;
- uint32_t attr = EFI_VARIABLE_NON_VOLATILE|EFI_VARIABLE_BOOTSERVICE_ACCESS|EFI_VARIABLE_RUNTIME_ACCESS;
bool saved_flags_valid = false;
unsigned saved_flags;
int r;
@@ -190,14 +191,14 @@ int efi_set_variable(const char *variable, const void *value, size_t size) {
assert(variable);
assert(value || size == 0);
- const char *p = strjoina("/sys/firmware/efi/efivars/", variable);
-
/* size 0 means removal, empty variable would not be enough for that */
if (size > 0 && efi_verify_variable(variable, attr, value, size) > 0) {
log_debug("Variable '%s' is already in wanted state, skipping write.", variable);
return 0;
}
+ const char *p = strjoina("/sys/firmware/efi/efivars/", variable);
+
/* Newer efivarfs protects variables that are not in an allow list with FS_IMMUTABLE_FL by default,
* to protect them for accidental removal and modification. We are not changing these variables
* accidentally however, hence let's unset the bit first. */
@@ -238,10 +239,7 @@ int efi_set_variable(const char *variable, const void *value, size_t size) {
/* For some reason efivarfs doesn't update mtime automatically. Let's do it manually then. This is
* useful for processes that cache EFI variables to detect when changes occurred. */
- if (futimens(fd, (struct timespec[2]) {
- { .tv_nsec = UTIME_NOW },
- { .tv_nsec = UTIME_NOW }
- }) < 0)
+ if (futimens(fd, /* times = */ NULL) < 0)
log_debug_errno(errno, "Failed to update mtime/atime on %s, ignoring: %m", p);
r = 0;
@@ -398,16 +396,8 @@ int systemd_efi_options_variable(char **ret) {
/* For testing purposes it is sometimes useful to be able to override this */
e = secure_getenv("SYSTEMD_EFI_OPTIONS");
- if (e) {
- char *m;
-
- m = strdup(e);
- if (!m)
- return -ENOMEM;
-
- *ret = m;
- return 0;
- }
+ if (e)
+ return strdup_to(ret, e);
r = read_one_line_file(EFIVAR_CACHE_PATH(EFI_SYSTEMD_VARIABLE(SystemdOptions)), ret);
if (r == -ENOENT)
diff --git a/src/basic/env-file.c b/src/basic/env-file.c
index c2cbff4..2fff98f 100644
--- a/src/basic/env-file.c
+++ b/src/basic/env-file.c
@@ -125,7 +125,7 @@ static int parse_env_file_internal(
state = VALUE;
if (!GREEDY_REALLOC(value, n_value+2))
- return -ENOMEM;
+ return -ENOMEM;
value[n_value++] = c;
}
diff --git a/src/basic/env-util.c b/src/basic/env-util.c
index a97651d..9e74ba0 100644
--- a/src/basic/env-util.c
+++ b/src/basic/env-util.c
@@ -18,6 +18,7 @@
#include "stdio-util.h"
#include "string-util.h"
#include "strv.h"
+#include "syslog-util.h"
#include "utf8.h"
/* We follow bash for the character set. Different shells have different rules. */
@@ -244,9 +245,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;
@@ -309,19 +310,17 @@ char **strv_env_delete(char **x, size_t n_lists, ...) {
return TAKE_PTR(t);
}
-char **strv_env_unset(char **l, const char *p) {
- char **f, **t;
+char** strv_env_unset(char **l, const char *p) {
+ assert(p);
if (!l)
return NULL;
- assert(p);
-
/* Drops every occurrence of the env var setting p in the
* string list. Edits in-place. */
+ char **f, **t;
for (f = t = l; *f; f++) {
-
if (env_match(*f, p)) {
free(*f);
continue;
@@ -334,14 +333,13 @@ char **strv_env_unset(char **l, const char *p) {
return l;
}
-char **strv_env_unset_many(char **l, ...) {
- char **f, **t;
-
+char** strv_env_unset_many_internal(char **l, ...) {
if (!l)
return NULL;
/* Like strv_env_unset() but applies many at once. Edits in-place. */
+ char **f, **t;
for (f = t = l; *f; f++) {
bool found = false;
const char *p;
@@ -349,12 +347,11 @@ char **strv_env_unset_many(char **l, ...) {
va_start(ap, l);
- while ((p = va_arg(ap, const char*))) {
+ while ((p = va_arg(ap, const char*)))
if (env_match(*f, p)) {
found = true;
break;
}
- }
va_end(ap);
@@ -458,6 +455,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;
@@ -500,18 +526,17 @@ int _strv_env_assign_many(char ***l, ...) {
return 0;
}
-char *strv_env_get_n(char **l, const char *name, size_t k, ReplaceEnvFlags 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_ptr(name);
+ 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;
@@ -654,7 +679,7 @@ int replace_env_full(
pu = ret_unset_variables ? &unset_variables : NULL;
pb = ret_bad_variables ? &bad_variables : NULL;
- for (e = format, i = 0; *e && i < n; e ++, i ++)
+ for (e = format, i = 0; *e && i < n; e++, i++)
switch (state) {
case WORD:
@@ -938,7 +963,7 @@ int getenv_bool(const char *p) {
return parse_boolean(e);
}
-int getenv_bool_secure(const char *p) {
+int secure_getenv_bool(const char *p) {
const char *e;
e = secure_getenv(p);
@@ -948,7 +973,7 @@ int getenv_bool_secure(const char *p) {
return parse_boolean(e);
}
-int getenv_uint64_secure(const char *p, uint64_t *ret) {
+int secure_getenv_uint64(const char *p, uint64_t *ret) {
const char *e;
assert(p);
@@ -1002,6 +1027,17 @@ int setenv_systemd_exec_pid(bool update_only) {
return 1;
}
+int setenv_systemd_log_level(void) {
+ _cleanup_free_ char *val = NULL;
+ int r;
+
+ r = log_level_to_string_alloc(log_get_max_level(), &val);
+ if (r < 0)
+ return r;
+
+ return RET_NERRNO(setenv("SYSTEMD_LOG_LEVEL", val, /* overwrite= */ true));
+}
+
int getenv_path_list(const char *name, char ***ret_paths) {
_cleanup_strv_free_ char **l = NULL;
const char *e;
@@ -1104,9 +1140,7 @@ int setenvf(const char *name, bool overwrite, const char *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)
diff --git a/src/basic/env-util.h b/src/basic/env-util.h
index 34cf1f9..6610ca8 100644
--- a/src/basic/env-util.h
+++ b/src/basic/env-util.h
@@ -43,26 +43,28 @@ char** _strv_env_merge(char **first, ...);
#define strv_env_merge(first, ...) _strv_env_merge(first, __VA_ARGS__, POINTER_MAX)
char **strv_env_delete(char **x, size_t n_lists, ...); /* New copy */
-char **strv_env_unset(char **l, const char *p); /* In place ... */
-char **strv_env_unset_many(char **l, ...) _sentinel_;
+char** strv_env_unset(char **l, const char *p); /* In place ... */
+char** strv_env_unset_many_internal(char **l, ...) _sentinel_;
+#define strv_env_unset_many(l, ...) strv_env_unset_many_internal(l, __VA_ARGS__, NULL)
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, ReplaceEnvFlags flags) _pure_;
-static inline char *strv_env_get(char **x, const char *n) {
+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);
-int getenv_bool_secure(const char *p);
+int secure_getenv_bool(const char *p);
-int getenv_uint64_secure(const char *p, uint64_t *ret);
+int secure_getenv_uint64(const char *p, uint64_t *ret);
/* Like setenv, but calls unsetenv if value == NULL. */
int set_unset_env(const char *name, const char *value, bool overwrite);
@@ -71,6 +73,7 @@ int set_unset_env(const char *name, const char *value, bool overwrite);
int putenv_dup(const char *assignment, bool override);
int setenv_systemd_exec_pid(bool update_only);
+int setenv_systemd_log_level(void);
/* Parses and does sanity checks on an environment variable containing
* PATH-like colon-separated absolute paths */
diff --git a/src/basic/errno-util.h b/src/basic/errno-util.h
index 27804e6..48b76e4 100644
--- a/src/basic/errno-util.h
+++ b/src/basic/errno-util.h
@@ -167,7 +167,8 @@ static inline bool ERRNO_IS_NEG_NOT_SUPPORTED(intmax_t r) {
-EAFNOSUPPORT,
-EPFNOSUPPORT,
-EPROTONOSUPPORT,
- -ESOCKTNOSUPPORT);
+ -ESOCKTNOSUPPORT,
+ -ENOPROTOOPT);
}
_DEFINE_ABS_WRAPPER(NOT_SUPPORTED);
diff --git a/src/basic/escape.c b/src/basic/escape.c
index 75a1d68..2067be4 100644
--- a/src/basic/escape.c
+++ b/src/basic/escape.c
@@ -451,6 +451,12 @@ char* octescape(const char *s, size_t len) {
assert(s || len == 0);
+ if (len == SIZE_MAX)
+ len = strlen(s);
+
+ if (len > (SIZE_MAX - 1) / 4)
+ return NULL;
+
t = buf = new(char, len * 4 + 1);
if (!buf)
return NULL;
@@ -471,6 +477,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/basic/escape.h b/src/basic/escape.h
index 318da6f..65caf0d 100644
--- a/src/basic/escape.h
+++ b/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/basic/ether-addr-util.c b/src/basic/ether-addr-util.c
index 0a6a54f..4bf91f6 100644
--- a/src/basic/ether-addr-util.c
+++ b/src/basic/ether-addr-util.c
@@ -59,8 +59,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);
@@ -106,7 +106,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);
@@ -270,3 +270,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/basic/ether-addr-util.h b/src/basic/ether-addr-util.h
index 83ed77d..187e4ef 100644
--- a/src/basic/ether-addr-util.h
+++ b/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/basic/extract-word.c b/src/basic/extract-word.c
index 160f771..012cee6 100644
--- a/src/basic/extract-word.c
+++ b/src/basic/extract-word.c
@@ -244,52 +244,43 @@ int extract_first_word_and_warn(
* Let's make sure that ExtractFlags fits into an unsigned int. */
assert_cc(sizeof(enum ExtractFlags) <= sizeof(unsigned));
-int extract_many_words(const char **p, const char *separators, unsigned flags, ...) {
+int extract_many_words_internal(const char **p, const char *separators, unsigned flags, ...) {
va_list ap;
- char **l;
- int n = 0, i, c, r;
+ unsigned n = 0;
+ int r;
- /* Parses a number of words from a string, stripping any
- * quotes if necessary. */
+ /* Parses a number of words from a string, stripping any quotes if necessary. */
assert(p);
/* Count how many words are expected */
va_start(ap, flags);
- for (;;) {
- if (!va_arg(ap, char **))
- break;
+ while (va_arg(ap, char**))
n++;
- }
va_end(ap);
- if (n <= 0)
+ if (n == 0)
return 0;
/* Read all words into a temporary array */
- l = newa0(char*, n);
- for (c = 0; c < n; c++) {
+ char **l = newa0(char*, n);
+ unsigned c;
+ for (c = 0; c < n; c++) {
r = extract_first_word(p, &l[c], separators, flags);
if (r < 0) {
free_many_charp(l, c);
return r;
}
-
if (r == 0)
break;
}
- /* If we managed to parse all words, return them in the passed
- * in parameters */
+ /* If we managed to parse all words, return them in the passed in parameters */
va_start(ap, flags);
- for (i = 0; i < n; i++) {
- char **v;
-
- v = va_arg(ap, char **);
- assert(v);
-
- *v = l[i];
+ FOREACH_ARRAY(i, l, n) {
+ char **v = ASSERT_PTR(va_arg(ap, char**));
+ *v = *i;
}
va_end(ap);
diff --git a/src/basic/extract-word.h b/src/basic/extract-word.h
index c82ad76..da4f6ae 100644
--- a/src/basic/extract-word.h
+++ b/src/basic/extract-word.h
@@ -19,4 +19,7 @@ typedef enum ExtractFlags {
int extract_first_word(const char **p, char **ret, const char *separators, ExtractFlags flags);
int extract_first_word_and_warn(const char **p, char **ret, const char *separators, ExtractFlags flags, const char *unit, const char *filename, unsigned line, const char *rvalue);
-int extract_many_words(const char **p, const char *separators, unsigned flags, ...) _sentinel_;
+
+int extract_many_words_internal(const char **p, const char *separators, unsigned flags, ...) _sentinel_;
+#define extract_many_words(p, separators, flags, ...) \
+ extract_many_words_internal(p, separators, flags, ##__VA_ARGS__, NULL)
diff --git a/src/basic/fd-util.c b/src/basic/fd-util.c
index 542acca..da4ee63 100644
--- a/src/basic/fd-util.c
+++ b/src/basic/fd-util.c
@@ -167,7 +167,23 @@ int fd_nonblock(int fd, bool nonblock) {
if (nflags == flags)
return 0;
- return RET_NERRNO(fcntl(fd, F_SETFL, nflags));
+ if (fcntl(fd, F_SETFL, nflags) < 0)
+ return -errno;
+
+ return 1;
+}
+
+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) {
@@ -451,6 +467,53 @@ int close_all_fds(const int except[], size_t n_except) {
return r;
}
+int pack_fds(int fds[], size_t n_fds) {
+ if (n_fds <= 0)
+ return 0;
+
+ /* Shifts around the fds in the provided array such that they
+ * all end up packed next to each-other, in order, starting
+ * from SD_LISTEN_FDS_START. This must be called after close_all_fds();
+ * it is likely to freeze up otherwise. You should probably use safe_fork_full
+ * with FORK_CLOSE_ALL_FDS|FORK_PACK_FDS set, to ensure that this is done correctly.
+ * The fds array is modified in place with the new FD numbers. */
+
+ assert(fds);
+
+ for (int start = 0;;) {
+ int restart_from = -1;
+
+ for (int i = start; i < (int) n_fds; i++) {
+ int nfd;
+
+ /* Already at right index? */
+ if (fds[i] == i + 3)
+ continue;
+
+ nfd = fcntl(fds[i], F_DUPFD, i + 3);
+ if (nfd < 0)
+ return -errno;
+
+ safe_close(fds[i]);
+ fds[i] = nfd;
+
+ /* Hmm, the fd we wanted isn't free? Then
+ * let's remember that and try again from here */
+ if (nfd != i + 3 && restart_from < 0)
+ restart_from = i;
+ }
+
+ if (restart_from < 0)
+ break;
+
+ start = restart_from;
+ }
+
+ assert(fds[0] == 3);
+
+ return 0;
+}
+
int same_fd(int a, int b) {
struct stat sta, stb;
pid_t pid;
@@ -809,6 +872,46 @@ int fd_reopen(int fd, int flags) {
return new_fd;
}
+int fd_reopen_propagate_append_and_position(int fd, int flags) {
+ /* Invokes fd_reopen(fd, flags), but propagates O_APPEND if set on original fd, and also tries to
+ * keep current file position.
+ *
+ * You should use this if the original fd potentially is O_APPEND, otherwise we get rather
+ * "unexpected" behavior. Unless you intentionally want to overwrite pre-existing data, and have
+ * your output overwritten by the next user.
+ *
+ * Use case: "systemd-run --pty >> some-log".
+ *
+ * The "keep position" part is obviously nonsense for the O_APPEND case, but should reduce surprises
+ * if someone carefully pre-positioned the passed in original input or non-append output FDs. */
+
+ assert(fd >= 0);
+ assert(!(flags & (O_APPEND|O_DIRECTORY)));
+
+ int existing_flags = fcntl(fd, F_GETFL);
+ if (existing_flags < 0)
+ return -errno;
+
+ int new_fd = fd_reopen(fd, flags | (existing_flags & O_APPEND));
+ if (new_fd < 0)
+ return new_fd;
+
+ /* Try to adjust the offset, but ignore errors. */
+ off_t p = lseek(fd, 0, SEEK_CUR);
+ if (p > 0) {
+ off_t new_p = lseek(new_fd, p, SEEK_SET);
+ if (new_p < 0)
+ log_debug_errno(errno,
+ "Failed to propagate file position for re-opened fd %d, ignoring: %m",
+ fd);
+ else if (new_p != p)
+ log_debug("Failed to propagate file position for re-opened fd %d (%lld != %lld), ignoring.",
+ fd, (long long) new_p, (long long) p);
+ }
+
+ return new_fd;
+}
+
int fd_reopen_condition(
int fd,
int flags,
@@ -853,6 +956,38 @@ int fd_is_opath(int fd) {
return FLAGS_SET(r, O_PATH);
}
+int fd_verify_safe_flags_full(int fd, int extra_flags) {
+ int flags, unexpected_flags;
+
+ /* Check if an extrinsic fd is safe to work on (by a privileged service). This ensures that clients
+ * can't trick a privileged service into giving access to a file the client doesn't already have
+ * access to (especially via something like O_PATH).
+ *
+ * O_NOFOLLOW: For some reason the kernel will return this flag from fcntl(); it doesn't go away
+ * immediately after open(). It should have no effect whatsoever to an already-opened FD,
+ * and since we refuse O_PATH it should be safe.
+ *
+ * RAW_O_LARGEFILE: glibc secretly sets this and neglects to hide it from us if we call fcntl.
+ * See comment in missing_fcntl.h for more details about this.
+ *
+ * If 'extra_flags' is specified as non-zero the included flags are also allowed.
+ */
+
+ assert(fd >= 0);
+
+ flags = fcntl(fd, F_GETFL);
+ if (flags < 0)
+ return -errno;
+
+ unexpected_flags = flags & ~(O_ACCMODE|O_NOFOLLOW|RAW_O_LARGEFILE|extra_flags);
+ if (unexpected_flags != 0)
+ return log_debug_errno(SYNTHETIC_ERRNO(EREMOTEIO),
+ "Unexpected flags set for extrinsic fd: 0%o",
+ (unsigned) unexpected_flags);
+
+ return flags & (O_ACCMODE | extra_flags); /* return the flags variable, but remove the noise */
+}
+
int read_nr_open(void) {
_cleanup_free_ char *nr_open = NULL;
int r;
@@ -899,10 +1034,7 @@ 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);
@@ -914,60 +1046,74 @@ int path_is_root_at(int dir_fd, const char *path) {
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
+ /* 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_fallback(dir_fd, "", &mntid);
+ 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_fallback(dir_fd, "..", &mntid);
+ 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) {
diff --git a/src/basic/fd-util.h b/src/basic/fd-util.h
index d3e9192..af17481 100644
--- a/src/basic/fd-util.h
+++ b/src/basic/fd-util.h
@@ -8,6 +8,7 @@
#include <sys/socket.h>
#include "macro.h"
+#include "missing_fcntl.h"
#include "stdio-util.h"
/* maximum length of fdname */
@@ -52,6 +53,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);
@@ -62,6 +68,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);
@@ -70,6 +78,8 @@ int get_max_fd(void);
int close_all_fds(const int except[], size_t n_except);
int close_all_fds_without_malloc(const int except[], size_t n_except);
+int pack_fds(int fds[], size_t n);
+
int same_fd(int a, int b);
void cmsg_close_all(struct msghdr *mh);
@@ -101,8 +111,16 @@ static inline int make_null_stdio(void) {
})
int fd_reopen(int fd, int flags);
+int fd_reopen_propagate_append_and_position(int fd, int flags);
int fd_reopen_condition(int fd, int flags, int mask, int *ret_new_fd);
+
int fd_is_opath(int fd);
+
+int fd_verify_safe_flags_full(int fd, int extra_flags);
+static inline int fd_verify_safe_flags(int fd) {
+ return fd_verify_safe_flags_full(fd, 0);
+}
+
int read_nr_open(void);
int fd_get_diskseq(int fd, uint64_t *ret);
@@ -117,6 +135,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))
diff --git a/src/basic/fileio.c b/src/basic/fileio.c
index a050b61..5233781 100644
--- a/src/basic/fileio.c
+++ b/src/basic/fileio.c
@@ -28,10 +28,11 @@
#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)
+#define READ_FULL_BYTES_MAX (64U * U64_MB - UINT64_C(1))
/* 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
@@ -44,7 +45,7 @@
* exponentially in a loop. We use a size limit of 4M-2 because 4M-1 is the maximum buffer that /proc/sys/
* allows us to read() (larger reads will fail with ENOMEM), and we want to read one extra byte so that we
* can detect EOFs. */
-#define READ_VIRTUAL_BYTES_MAX (4U*1024U*1024U - 2U)
+#define READ_VIRTUAL_BYTES_MAX (4U * U64_MB - UINT64_C(2))
int fdopen_unlocked(int fd, const char *options, FILE **ret) {
assert(ret);
@@ -199,6 +200,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,
@@ -224,7 +238,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;
@@ -287,7 +301,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;
@@ -1313,33 +1327,31 @@ int read_timestamp_file(const char *fn, usec_t *ret) {
return 0;
}
-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;
}
/* A bitmask of the EOL markers we know */
@@ -1459,7 +1471,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;
@@ -1492,7 +1504,7 @@ int read_line_full(FILE *f, size_t limit, ReadLineFlags flags, char **ret) {
int read_stripped_line(FILE *f, size_t limit, char **ret) {
_cleanup_free_ char *s = NULL;
- int r;
+ int r, k;
assert(f);
@@ -1501,23 +1513,17 @@ int read_stripped_line(FILE *f, size_t limit, char **ret) {
return r;
if (ret) {
- const char *p;
-
- p = strstrip(s);
+ const char *p = strstrip(s);
if (p == s)
*ret = TAKE_PTR(s);
else {
- char *copy;
-
- copy = strdup(p);
- if (!copy)
- return -ENOMEM;
-
- *ret = copy;
+ k = strdup_to(ret, p);
+ if (k < 0)
+ return k;
}
}
- return r;
+ return r > 0; /* Return 1 if something was read. */
}
int safe_fgetc(FILE *f, char *ret) {
diff --git a/src/basic/fileio.h b/src/basic/fileio.h
index e0e0a45..03c3f3f 100644
--- a/src/basic/fileio.h
+++ b/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()
@@ -142,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,
diff --git a/src/basic/filesystems-gperf.gperf b/src/basic/filesystems-gperf.gperf
index 1cd66b5..c82fe98 100644
--- a/src/basic/filesystems-gperf.gperf
+++ b/src/basic/filesystems-gperf.gperf
@@ -28,6 +28,7 @@ afs, {AFS_FS_MAGIC, AFS_SUPER_MAGIC}
anon_inodefs, {ANON_INODE_FS_MAGIC}
autofs, {AUTOFS_SUPER_MAGIC}
balloon-kvm, {BALLOON_KVM_MAGIC}
+bcachefs, {BCACHEFS_SUPER_MAGIC}
bdev, {BDEVFS_MAGIC}
binder, {BINDERFS_SUPER_MAGIC}
binfmt_misc, {BINFMTFS_MAGIC}
diff --git a/src/basic/format-util.c b/src/basic/format-util.c
index 9450185..445fecc 100644
--- a/src/basic/format-util.c
+++ b/src/basic/format-util.c
@@ -25,7 +25,7 @@ int format_ifname_full(int ifindex, FormatIfnameFlag flag, char buf[static IF_NA
}
int format_ifname_full_alloc(int ifindex, FormatIfnameFlag flag, char **ret) {
- char buf[IF_NAMESIZE], *copy;
+ char buf[IF_NAMESIZE];
int r;
assert(ret);
@@ -34,12 +34,7 @@ int format_ifname_full_alloc(int ifindex, FormatIfnameFlag flag, char **ret) {
if (r < 0)
return r;
- copy = strdup(buf);
- if (!copy)
- return -ENOMEM;
-
- *ret = copy;
- return 0;
+ return strdup_to(ret, buf);
}
char *format_bytes_full(char *buf, size_t l, uint64_t t, FormatBytesFlag flag) {
@@ -75,15 +70,17 @@ char *format_bytes_full(char *buf, size_t l, uint64_t t, FormatBytesFlag flag) {
for (size_t i = 0; i < n; i++)
if (t >= table[i].factor) {
- if (flag & FORMAT_BYTES_BELOW_POINT) {
+ uint64_t remainder = i != n - 1 ?
+ (t / table[i + 1].factor * 10 / table[n - 1].factor) % 10 :
+ (t * 10 / table[i].factor) % 10;
+
+ if (FLAGS_SET(flag, FORMAT_BYTES_BELOW_POINT) && remainder > 0)
(void) snprintf(buf, l,
"%" PRIu64 ".%" PRIu64 "%s",
t / table[i].factor,
- i != n - 1 ?
- (t / table[i + 1].factor * UINT64_C(10) / table[n - 1].factor) % UINT64_C(10):
- (t * UINT64_C(10) / table[i].factor) % UINT64_C(10),
+ remainder,
table[i].suffix);
- } else
+ else
(void) snprintf(buf, l,
"%" PRIu64 "%s",
t / table[i].factor,
diff --git a/src/basic/format-util.h b/src/basic/format-util.h
index 8719df3..ba7cff6 100644
--- a/src/basic/format-util.h
+++ b/src/basic/format-util.h
@@ -18,6 +18,14 @@ assert_cc(sizeof(uid_t) == sizeof(uint32_t));
assert_cc(sizeof(gid_t) == sizeof(uint32_t));
#define GID_FMT "%" PRIu32
+/* Note: the lifetime of the compound literal is the immediately surrounding block,
+ * see C11 §6.5.2.5, and
+ * https://stackoverflow.com/questions/34880638/compound-literal-lifetime-and-if-blocks */
+#define FORMAT_UID(uid) \
+ snprintf_ok((char[DECIMAL_STR_MAX(uid_t)]){}, DECIMAL_STR_MAX(uid_t), UID_FMT, uid)
+#define FORMAT_GID(gid) \
+ snprintf_ok((char[DECIMAL_STR_MAX(gid_t)]){}, DECIMAL_STR_MAX(gid_t), GID_FMT, gid)
+
#if SIZEOF_TIME_T == 8
# define PRI_TIME PRIi64
#elif SIZEOF_TIME_T == 4
diff --git a/src/basic/fs-util.c b/src/basic/fs-util.c
index 5bc7d2f..64d3093 100644
--- a/src/basic/fs-util.c
+++ b/src/basic/fs-util.c
@@ -325,12 +325,22 @@ int fchmod_opath(int fd, mode_t m) {
int futimens_opath(int fd, const struct timespec ts[2]) {
/* Similar to fchmod_opath() but for futimens() */
- if (utimensat(AT_FDCWD, FORMAT_PROC_FD_PATH(fd), ts, 0) < 0) {
+ assert(fd >= 0);
+
+ if (utimensat(fd, "", ts, AT_EMPTY_PATH) >= 0)
+ return 0;
+ if (errno != EINVAL)
+ return -errno;
+
+ /* Support for AT_EMPTY_PATH is added rather late (kernel 5.8), so fall back to going through /proc/
+ * if unavailable. */
+
+ if (utimensat(AT_FDCWD, FORMAT_PROC_FD_PATH(fd), ts, /* flags = */ 0) < 0) {
if (errno != ENOENT)
return -errno;
if (proc_mounted() == 0)
- return -ENOSYS; /* if we have no /proc/, the concept is not implementable */
+ return -ENOSYS;
return -ENOENT;
}
@@ -405,17 +415,14 @@ int touch_file(const char *path, bool parents, usec_t stamp, uid_t uid, gid_t gi
ret = fchmod_and_chown(fd, mode, uid, gid);
if (stamp != USEC_INFINITY) {
- struct timespec ts[2];
+ struct timespec ts;
+ timespec_store(&ts, stamp);
- timespec_store(&ts[0], stamp);
- ts[1] = ts[0];
- r = futimens_opath(fd, ts);
+ r = futimens_opath(fd, (const struct timespec[2]) { ts, ts });
} else
- r = futimens_opath(fd, NULL);
- if (r < 0 && ret >= 0)
- return r;
+ r = futimens_opath(fd, /* ts = */ NULL);
- return ret;
+ return RET_GATHER(ret, r);
}
int symlink_idempotent(const char *from, const char *to, bool make_relative) {
@@ -1018,7 +1025,7 @@ int parse_cifs_service(
return 0;
}
-int open_mkdir_at(int dirfd, const char *path, int flags, mode_t mode) {
+int open_mkdir_at_full(int dirfd, const char *path, int flags, XOpenFlags xopen_flags, mode_t mode) {
_cleanup_close_ int fd = -EBADF, parent_fd = -EBADF;
_cleanup_free_ char *fname = NULL, *parent = NULL;
int r;
@@ -1054,7 +1061,7 @@ int open_mkdir_at(int dirfd, const char *path, int flags, mode_t mode) {
path = fname;
}
- fd = xopenat_full(dirfd, path, flags|O_CREAT|O_DIRECTORY|O_NOFOLLOW, /* xopen_flags = */ 0, mode);
+ fd = xopenat_full(dirfd, path, flags|O_CREAT|O_DIRECTORY|O_NOFOLLOW, xopen_flags, mode);
if (IN_SET(fd, -ELOOP, -ENOTDIR))
return -EEXIST;
if (fd < 0)
@@ -1236,3 +1243,99 @@ int xopenat_lock_full(
return TAKE_FD(fd);
}
+
+int link_fd(int fd, int newdirfd, const char *newpath) {
+ int r;
+
+ assert(fd >= 0);
+ assert(newdirfd >= 0 || newdirfd == AT_FDCWD);
+ assert(newpath);
+
+ /* Try linking via /proc/self/fd/ first. */
+ r = RET_NERRNO(linkat(AT_FDCWD, FORMAT_PROC_FD_PATH(fd), newdirfd, newpath, AT_SYMLINK_FOLLOW));
+ if (r != -ENOENT)
+ return r;
+
+ /* Fall back to symlinking via AT_EMPTY_PATH as fallback (this requires CAP_DAC_READ_SEARCH and a
+ * more recent kernel, but does not require /proc/ mounted) */
+ if (proc_mounted() != 0)
+ return r;
+
+ return RET_NERRNO(linkat(fd, "", newdirfd, newpath, AT_EMPTY_PATH));
+}
+
+int linkat_replace(int olddirfd, const char *oldpath, int newdirfd, const char *newpath) {
+ _cleanup_close_ int old_fd = -EBADF;
+ int r;
+
+ assert(olddirfd >= 0 || olddirfd == AT_FDCWD);
+ assert(newdirfd >= 0 || newdirfd == AT_FDCWD);
+ assert(!isempty(newpath)); /* source path is optional, but the target path is not */
+
+ /* Like linkat() but replaces the target if needed. Is a NOP if source and target already share the
+ * same inode. */
+
+ if (olddirfd == AT_FDCWD && isempty(oldpath)) /* Refuse operating on the cwd (which is a dir, and dirs can't be hardlinked) */
+ return -EISDIR;
+
+ if (path_implies_directory(oldpath)) /* Refuse these definite directories early */
+ return -EISDIR;
+
+ if (path_implies_directory(newpath))
+ return -EISDIR;
+
+ /* First, try to link this directly */
+ if (oldpath)
+ r = RET_NERRNO(linkat(olddirfd, oldpath, newdirfd, newpath, 0));
+ else
+ r = link_fd(olddirfd, newdirfd, newpath);
+ if (r >= 0)
+ return 0;
+ if (r != -EEXIST)
+ return r;
+
+ old_fd = xopenat(olddirfd, oldpath, O_PATH|O_CLOEXEC);
+ if (old_fd < 0)
+ return old_fd;
+
+ struct stat old_st;
+ if (fstat(old_fd, &old_st) < 0)
+ return -errno;
+
+ if (S_ISDIR(old_st.st_mode)) /* Don't bother if we are operating on a directory */
+ return -EISDIR;
+
+ struct stat new_st;
+ if (fstatat(newdirfd, newpath, &new_st, AT_SYMLINK_NOFOLLOW) < 0)
+ return -errno;
+
+ if (S_ISDIR(new_st.st_mode)) /* Refuse replacing directories */
+ return -EEXIST;
+
+ if (stat_inode_same(&old_st, &new_st)) /* Already the same inode? Then shortcut this */
+ return 0;
+
+ _cleanup_free_ char *tmp_path = NULL;
+ r = tempfn_random(newpath, /* extra= */ NULL, &tmp_path);
+ if (r < 0)
+ return r;
+
+ r = link_fd(old_fd, newdirfd, tmp_path);
+ if (r < 0) {
+ if (!ERRNO_IS_PRIVILEGE(r))
+ return r;
+
+ /* If that didn't work due to permissions then go via the path of the dentry */
+ r = RET_NERRNO(linkat(olddirfd, oldpath, newdirfd, tmp_path, 0));
+ if (r < 0)
+ return r;
+ }
+
+ r = RET_NERRNO(renameat(newdirfd, tmp_path, newdirfd, newpath));
+ if (r < 0) {
+ (void) unlinkat(newdirfd, tmp_path, /* flags= */ 0);
+ return r;
+ }
+
+ return 0;
+}
diff --git a/src/basic/fs-util.h b/src/basic/fs-util.h
index 6a1e2e7..3e2db95 100644
--- a/src/basic/fs-util.h
+++ b/src/basic/fs-util.h
@@ -128,15 +128,18 @@ int posix_fallocate_loop(int fd, uint64_t offset, uint64_t size);
int parse_cifs_service(const char *s, char **ret_host, char **ret_service, char **ret_path);
-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_SUBVOLUME = 1 << 1,
} XOpenFlags;
+int open_mkdir_at_full(int dirfd, const char *path, int flags, XOpenFlags xopen_flags, mode_t mode);
+static inline int open_mkdir_at(int dirfd, const char *path, int flags, mode_t mode) {
+ return open_mkdir_at_full(dirfd, path, flags, 0, mode);
+}
+
+int openat_report_new(int dirfd, const char *pathname, int flags, mode_t mode, bool *ret_newly_created);
+
int xopenat_full(int dir_fd, const char *path, int open_flags, XOpenFlags xopen_flags, mode_t mode);
static inline int xopenat(int dir_fd, const char *path, int open_flags) {
return xopenat_full(dir_fd, path, open_flags, 0, 0);
@@ -146,3 +149,7 @@ int xopenat_lock_full(int dir_fd, const char *path, int open_flags, XOpenFlags x
static inline int xopenat_lock(int dir_fd, const char *path, int open_flags, LockType locktype, int operation) {
return xopenat_lock_full(dir_fd, path, open_flags, 0, 0, locktype, operation);
}
+
+int link_fd(int fd, int newdirfd, const char *newpath);
+
+int linkat_replace(int olddirfd, const char *oldpath, int newdirfd, const char *newpath);
diff --git a/src/basic/gcrypt-util.c b/src/basic/gcrypt-util.c
index 41c9362..4d68d2c 100644
--- a/src/basic/gcrypt-util.c
+++ b/src/basic/gcrypt-util.c
@@ -5,41 +5,130 @@
#include "gcrypt-util.h"
#include "hexdecoct.h"
-void initialize_libgcrypt(bool secmem) {
- if (gcry_control(GCRYCTL_INITIALIZATION_FINISHED_P))
- return;
+static void *gcrypt_dl = NULL;
- gcry_control(GCRYCTL_SET_PREFERRED_RNG_TYPE, GCRY_RNG_TYPE_SYSTEM);
- assert_se(gcry_check_version("1.4.5"));
+static DLSYM_FUNCTION(gcry_control);
+static DLSYM_FUNCTION(gcry_check_version);
+DLSYM_FUNCTION(gcry_md_close);
+DLSYM_FUNCTION(gcry_md_copy);
+DLSYM_FUNCTION(gcry_md_ctl);
+DLSYM_FUNCTION(gcry_md_get_algo_dlen);
+DLSYM_FUNCTION(gcry_md_open);
+DLSYM_FUNCTION(gcry_md_read);
+DLSYM_FUNCTION(gcry_md_reset);
+DLSYM_FUNCTION(gcry_md_setkey);
+DLSYM_FUNCTION(gcry_md_write);
+DLSYM_FUNCTION(gcry_mpi_add);
+DLSYM_FUNCTION(gcry_mpi_add_ui);
+DLSYM_FUNCTION(gcry_mpi_cmp);
+DLSYM_FUNCTION(gcry_mpi_cmp_ui);
+DLSYM_FUNCTION(gcry_mpi_get_nbits);
+DLSYM_FUNCTION(gcry_mpi_invm);
+DLSYM_FUNCTION(gcry_mpi_mod);
+DLSYM_FUNCTION(gcry_mpi_mul);
+DLSYM_FUNCTION(gcry_mpi_mulm);
+DLSYM_FUNCTION(gcry_mpi_new);
+DLSYM_FUNCTION(gcry_mpi_powm);
+DLSYM_FUNCTION(gcry_mpi_print);
+DLSYM_FUNCTION(gcry_mpi_release);
+DLSYM_FUNCTION(gcry_mpi_scan);
+DLSYM_FUNCTION(gcry_mpi_set_ui);
+DLSYM_FUNCTION(gcry_mpi_sub);
+DLSYM_FUNCTION(gcry_mpi_subm);
+DLSYM_FUNCTION(gcry_mpi_sub_ui);
+DLSYM_FUNCTION(gcry_prime_check);
+DLSYM_FUNCTION(gcry_randomize);
+DLSYM_FUNCTION(gcry_strerror);
+
+static int dlopen_gcrypt(void) {
+ ELF_NOTE_DLOPEN("gcrypt",
+ "Support for journald forward-sealing",
+ ELF_NOTE_DLOPEN_PRIORITY_SUGGESTED,
+ "libgcrypt.so.20");
+
+ return dlopen_many_sym_or_warn(
+ &gcrypt_dl,
+ "libgcrypt.so.20", LOG_DEBUG,
+ DLSYM_ARG(gcry_control),
+ DLSYM_ARG(gcry_check_version),
+ DLSYM_ARG(gcry_md_close),
+ DLSYM_ARG(gcry_md_copy),
+ DLSYM_ARG(gcry_md_ctl),
+ DLSYM_ARG(gcry_md_get_algo_dlen),
+ DLSYM_ARG(gcry_md_open),
+ DLSYM_ARG(gcry_md_read),
+ DLSYM_ARG(gcry_md_reset),
+ DLSYM_ARG(gcry_md_setkey),
+ DLSYM_ARG(gcry_md_write),
+ DLSYM_ARG(gcry_mpi_add),
+ DLSYM_ARG(gcry_mpi_add_ui),
+ DLSYM_ARG(gcry_mpi_cmp),
+ DLSYM_ARG(gcry_mpi_cmp_ui),
+ DLSYM_ARG(gcry_mpi_get_nbits),
+ DLSYM_ARG(gcry_mpi_invm),
+ DLSYM_ARG(gcry_mpi_mod),
+ DLSYM_ARG(gcry_mpi_mul),
+ DLSYM_ARG(gcry_mpi_mulm),
+ DLSYM_ARG(gcry_mpi_new),
+ DLSYM_ARG(gcry_mpi_powm),
+ DLSYM_ARG(gcry_mpi_print),
+ DLSYM_ARG(gcry_mpi_release),
+ DLSYM_ARG(gcry_mpi_scan),
+ DLSYM_ARG(gcry_mpi_set_ui),
+ DLSYM_ARG(gcry_mpi_sub),
+ DLSYM_ARG(gcry_mpi_subm),
+ DLSYM_ARG(gcry_mpi_sub_ui),
+ DLSYM_ARG(gcry_prime_check),
+ DLSYM_ARG(gcry_randomize),
+ DLSYM_ARG(gcry_strerror));
+}
+
+int initialize_libgcrypt(bool secmem) {
+ int r;
+
+ r = dlopen_gcrypt();
+ if (r < 0)
+ return r;
+
+ if (sym_gcry_control(GCRYCTL_INITIALIZATION_FINISHED_P))
+ return 0;
+
+ sym_gcry_control(GCRYCTL_SET_PREFERRED_RNG_TYPE, GCRY_RNG_TYPE_SYSTEM);
+ assert_se(sym_gcry_check_version("1.4.5"));
/* Turn off "secmem". Clients which wish to make use of this
* feature should initialize the library manually */
if (!secmem)
- gcry_control(GCRYCTL_DISABLE_SECMEM);
+ sym_gcry_control(GCRYCTL_DISABLE_SECMEM);
- gcry_control(GCRYCTL_INITIALIZATION_FINISHED, 0);
+ sym_gcry_control(GCRYCTL_INITIALIZATION_FINISHED, 0);
+
+ return 0;
}
# if !PREFER_OPENSSL
int string_hashsum(const char *s, size_t len, int md_algorithm, char **out) {
- _cleanup_(gcry_md_closep) gcry_md_hd_t md = NULL;
+ _cleanup_(sym_gcry_md_closep) gcry_md_hd_t md = NULL;
gcry_error_t err;
size_t hash_size;
void *hash;
char *enc;
+ int r;
- initialize_libgcrypt(false);
+ r = initialize_libgcrypt(false);
+ if (r < 0)
+ return r;
- hash_size = gcry_md_get_algo_dlen(md_algorithm);
+ hash_size = sym_gcry_md_get_algo_dlen(md_algorithm);
assert(hash_size > 0);
- err = gcry_md_open(&md, md_algorithm, 0);
+ err = sym_gcry_md_open(&md, md_algorithm, 0);
if (gcry_err_code(err) != GPG_ERR_NO_ERROR || !md)
return -EIO;
- gcry_md_write(md, s, len);
+ sym_gcry_md_write(md, s, len);
- hash = gcry_md_read(md, 0);
+ hash = sym_gcry_md_read(md, 0);
if (!hash)
return -EIO;
diff --git a/src/basic/gcrypt-util.h b/src/basic/gcrypt-util.h
index 4c40cef..acb50e8 100644
--- a/src/basic/gcrypt-util.h
+++ b/src/basic/gcrypt-util.h
@@ -9,11 +9,59 @@
#if HAVE_GCRYPT
#include <gcrypt.h>
+#include "dlfcn-util.h"
#include "macro.h"
-void initialize_libgcrypt(bool secmem);
+DLSYM_PROTOTYPE(gcry_md_close);
+DLSYM_PROTOTYPE(gcry_md_copy);
+DLSYM_PROTOTYPE(gcry_md_ctl);
+DLSYM_PROTOTYPE(gcry_md_get_algo_dlen);
+DLSYM_PROTOTYPE(gcry_md_open);
+DLSYM_PROTOTYPE(gcry_md_read);
+DLSYM_PROTOTYPE(gcry_md_reset);
+DLSYM_PROTOTYPE(gcry_md_setkey);
+DLSYM_PROTOTYPE(gcry_md_write);
+DLSYM_PROTOTYPE(gcry_mpi_add);
+DLSYM_PROTOTYPE(gcry_mpi_add_ui);
+DLSYM_PROTOTYPE(gcry_mpi_cmp);
+DLSYM_PROTOTYPE(gcry_mpi_cmp_ui);
+DLSYM_PROTOTYPE(gcry_mpi_get_nbits);
+DLSYM_PROTOTYPE(gcry_mpi_invm);
+DLSYM_PROTOTYPE(gcry_mpi_mod);
+DLSYM_PROTOTYPE(gcry_mpi_mul);
+DLSYM_PROTOTYPE(gcry_mpi_mulm);
+DLSYM_PROTOTYPE(gcry_mpi_new);
+DLSYM_PROTOTYPE(gcry_mpi_powm);
+DLSYM_PROTOTYPE(gcry_mpi_print);
+DLSYM_PROTOTYPE(gcry_mpi_release);
+DLSYM_PROTOTYPE(gcry_mpi_scan);
+DLSYM_PROTOTYPE(gcry_mpi_set_ui);
+DLSYM_PROTOTYPE(gcry_mpi_sub);
+DLSYM_PROTOTYPE(gcry_mpi_subm);
+DLSYM_PROTOTYPE(gcry_mpi_sub_ui);
+DLSYM_PROTOTYPE(gcry_prime_check);
+DLSYM_PROTOTYPE(gcry_randomize);
+DLSYM_PROTOTYPE(gcry_strerror);
+int initialize_libgcrypt(bool secmem);
+
+static inline gcry_md_hd_t* sym_gcry_md_closep(gcry_md_hd_t *md) {
+ if (!md || !*md)
+ return NULL;
+ sym_gcry_md_close(*md);
+
+ return NULL;
+}
DEFINE_TRIVIAL_CLEANUP_FUNC_FULL(gcry_md_hd_t, gcry_md_close, NULL);
+
+/* Copied from gcry_md_putc from gcrypt.h due to the need to call the sym_ variant */
+#define sym_gcry_md_putc(h,c) \
+ do { \
+ gcry_md_hd_t h__ = (h); \
+ if ((h__)->bufpos == (h__)->bufsize) \
+ sym_gcry_md_write((h__), NULL, 0); \
+ (h__)->buf[(h__)->bufpos++] = (c) & 0xff; \
+ } while(false)
#endif
#if !PREFER_OPENSSL
diff --git a/src/basic/getopt-defs.h b/src/basic/getopt-defs.h
index 3efeb6d..9abef6f 100644
--- a/src/basic/getopt-defs.h
+++ b/src/basic/getopt-defs.h
@@ -26,6 +26,7 @@
ARG_CRASH_CHVT, \
ARG_CRASH_SHELL, \
ARG_CRASH_REBOOT, \
+ ARG_CRASH_ACTION, \
ARG_CONFIRM_SPAWN, \
ARG_SHOW_STATUS, \
ARG_DESERIALIZE, \
@@ -61,6 +62,7 @@
{ "crash-chvt", required_argument, NULL, ARG_CRASH_CHVT }, \
{ "crash-shell", optional_argument, NULL, ARG_CRASH_SHELL }, \
{ "crash-reboot", optional_argument, NULL, ARG_CRASH_REBOOT }, \
+ { "crash-action", required_argument, NULL, ARG_CRASH_ACTION }, \
{ "confirm-spawn", optional_argument, NULL, ARG_CONFIRM_SPAWN }, \
{ "show-status", optional_argument, NULL, ARG_SHOW_STATUS }, \
{ "deserialize", required_argument, NULL, ARG_DESERIALIZE }, \
diff --git a/src/basic/glyph-util.c b/src/basic/glyph-util.c
index 803bdd9..d37be32 100644
--- a/src/basic/glyph-util.c
+++ b/src/basic/glyph-util.c
@@ -41,6 +41,8 @@ const char *special_glyph_full(SpecialGlyph code, bool force_utf) {
[SPECIAL_GLYPH_TREE_SPACE] = " ",
[SPECIAL_GLYPH_TREE_TOP] = ",-",
[SPECIAL_GLYPH_VERTICAL_DOTTED] = ":",
+ [SPECIAL_GLYPH_HORIZONTAL_DOTTED] = "-",
+ [SPECIAL_GLYPH_HORIZONTAL_FAT] = "=",
[SPECIAL_GLYPH_TRIANGULAR_BULLET] = ">",
[SPECIAL_GLYPH_BLACK_CIRCLE] = "*",
[SPECIAL_GLYPH_WHITE_CIRCLE] = "*",
@@ -74,6 +76,10 @@ const char *special_glyph_full(SpecialGlyph code, bool force_utf) {
[SPECIAL_GLYPH_SPARKLES] = "*",
[SPECIAL_GLYPH_LOW_BATTERY] = "!",
[SPECIAL_GLYPH_WARNING_SIGN] = "!",
+ [SPECIAL_GLYPH_RED_CIRCLE] = "o",
+ [SPECIAL_GLYPH_YELLOW_CIRCLE] = "o",
+ [SPECIAL_GLYPH_BLUE_CIRCLE] = "o",
+ [SPECIAL_GLYPH_GREEN_CIRCLE] = "o",
},
/* UTF-8 */
@@ -87,6 +93,8 @@ const char *special_glyph_full(SpecialGlyph code, bool force_utf) {
/* Single glyphs in both cases */
[SPECIAL_GLYPH_VERTICAL_DOTTED] = u8"┆",
+ [SPECIAL_GLYPH_HORIZONTAL_DOTTED] = u8"┄",
+ [SPECIAL_GLYPH_HORIZONTAL_FAT] = u8"━",
[SPECIAL_GLYPH_TRIANGULAR_BULLET] = u8"‣",
[SPECIAL_GLYPH_BLACK_CIRCLE] = u8"●",
[SPECIAL_GLYPH_WHITE_CIRCLE] = u8"○",
@@ -136,6 +144,11 @@ const char *special_glyph_full(SpecialGlyph code, bool force_utf) {
[SPECIAL_GLYPH_WARNING_SIGN] = u8"⚠️",
[SPECIAL_GLYPH_COMPUTER_DISK] = u8"💽",
[SPECIAL_GLYPH_WORLD] = u8"🌍",
+
+ [SPECIAL_GLYPH_RED_CIRCLE] = u8"🔴",
+ [SPECIAL_GLYPH_YELLOW_CIRCLE] = u8"🟡",
+ [SPECIAL_GLYPH_BLUE_CIRCLE] = u8"🔵",
+ [SPECIAL_GLYPH_GREEN_CIRCLE] = u8"🟢",
},
};
diff --git a/src/basic/glyph-util.h b/src/basic/glyph-util.h
index a770997..db8dbbf 100644
--- a/src/basic/glyph-util.h
+++ b/src/basic/glyph-util.h
@@ -13,6 +13,8 @@ typedef enum SpecialGlyph {
SPECIAL_GLYPH_TREE_SPACE,
SPECIAL_GLYPH_TREE_TOP,
SPECIAL_GLYPH_VERTICAL_DOTTED,
+ SPECIAL_GLYPH_HORIZONTAL_DOTTED,
+ SPECIAL_GLYPH_HORIZONTAL_FAT,
SPECIAL_GLYPH_TRIANGULAR_BULLET,
SPECIAL_GLYPH_BLACK_CIRCLE,
SPECIAL_GLYPH_WHITE_CIRCLE,
@@ -49,6 +51,10 @@ typedef enum SpecialGlyph {
SPECIAL_GLYPH_WARNING_SIGN,
SPECIAL_GLYPH_COMPUTER_DISK,
SPECIAL_GLYPH_WORLD,
+ SPECIAL_GLYPH_RED_CIRCLE,
+ SPECIAL_GLYPH_YELLOW_CIRCLE,
+ SPECIAL_GLYPH_BLUE_CIRCLE,
+ SPECIAL_GLYPH_GREEN_CIRCLE,
_SPECIAL_GLYPH_MAX,
_SPECIAL_GLYPH_INVALID = -EINVAL,
} SpecialGlyph;
diff --git a/src/basic/hash-funcs.c b/src/basic/hash-funcs.c
index 5fac467..251ee4f 100644
--- a/src/basic/hash-funcs.c
+++ b/src/basic/hash-funcs.c
@@ -33,7 +33,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;
@@ -67,7 +67,7 @@ DEFINE_HASH_OPS_FULL(path_hash_ops_free_free,
void, free);
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) {
@@ -93,7 +93,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) {
@@ -104,7 +104,7 @@ DEFINE_HASH_OPS(uint64_hash_ops, uint64_t, uint64_hash_func, uint64_compare_func
#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/basic/hashmap.c b/src/basic/hashmap.c
index 894760c..a9fd762 100644
--- a/src/basic/hashmap.c
+++ b/src/basic/hashmap.c
@@ -2120,24 +2120,27 @@ static int hashmap_entry_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;
+static int _hashmap_dump_entries_sorted(
+ HashmapBase *h,
+ void ***ret,
+ size_t *ret_n) {
+ _cleanup_free_ void **entries = NULL;
Iterator iter;
unsigned idx;
size_t n = 0;
assert(ret);
+ assert(ret_n);
if (_hashmap_size(h) == 0) {
*ret = NULL;
- if (ret_n)
- *ret_n = 0;
+ *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);
+ entries = new(void*, _hashmap_size(h) + 1);
if (!entries)
return -ENOMEM;
@@ -2147,13 +2150,47 @@ int _hashmap_dump_sorted(HashmapBase *h, void ***ret, size_t *ret_n) {
assert(n == _hashmap_size(h));
entries[n] = NULL;
- typesafe_qsort_r(entries, n, hashmap_entry_compare, h->hash_ops->compare);
+ typesafe_qsort_r((struct hashmap_base_entry**) entries, n,
+ hashmap_entry_compare, h->hash_ops->compare);
+
+ *ret = TAKE_PTR(entries);
+ *ret_n = n;
+ return 0;
+}
+
+int _hashmap_dump_keys_sorted(HashmapBase *h, void ***ret, size_t *ret_n) {
+ _cleanup_free_ void **entries = NULL;
+ size_t n;
+ int r;
+
+ r = _hashmap_dump_entries_sorted(h, &entries, &n);
+ if (r < 0)
+ return r;
+
+ /* Reuse the array. */
+ FOREACH_ARRAY(e, entries, n)
+ *e = (void*) (*(struct hashmap_base_entry**) e)->key;
+
+ *ret = TAKE_PTR(entries);
+ if (ret_n)
+ *ret_n = n;
+ return 0;
+}
+
+int _hashmap_dump_sorted(HashmapBase *h, void ***ret, size_t *ret_n) {
+ _cleanup_free_ void **entries = NULL;
+ size_t n;
+ int r;
+
+ r = _hashmap_dump_entries_sorted(h, &entries, &n);
+ if (r < 0)
+ return r;
/* Reuse the array. */
FOREACH_ARRAY(e, entries, n)
- *e = entry_value(h, *e);
+ *e = entry_value(h, *(struct hashmap_base_entry**) e);
- *ret = (void**) TAKE_PTR(entries);
+ *ret = TAKE_PTR(entries);
if (ret_n)
*ret_n = n;
return 0;
diff --git a/src/basic/hashmap.h b/src/basic/hashmap.h
index d0ebdf5..49d9d11 100644
--- a/src/basic/hashmap.h
+++ b/src/basic/hashmap.h
@@ -409,6 +409,14 @@ static inline int set_dump_sorted(Set *h, void ***ret, size_t *ret_n) {
return _hashmap_dump_sorted(HASHMAP_BASE(h), ret, ret_n);
}
+int _hashmap_dump_keys_sorted(HashmapBase *h, void ***ret, size_t *ret_n);
+static inline int hashmap_dump_keys_sorted(Hashmap *h, void ***ret, size_t *ret_n) {
+ return _hashmap_dump_keys_sorted(HASHMAP_BASE(h), ret, ret_n);
+}
+static inline int ordered_hashmap_dump_keys_sorted(OrderedHashmap *h, void ***ret, size_t *ret_n) {
+ return _hashmap_dump_keys_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
diff --git a/src/basic/hexdecoct.c b/src/basic/hexdecoct.c
index ea683eb..4cb67d9 100644
--- a/src/basic/hexdecoct.c
+++ b/src/basic/hexdecoct.c
@@ -114,7 +114,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;
@@ -155,8 +155,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;
}
@@ -766,7 +766,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;
@@ -854,8 +854,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/basic/hexdecoct.h b/src/basic/hexdecoct.h
index 319b21a..0a10af3 100644
--- a/src/basic/hexdecoct.h
+++ b/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/basic/in-addr-util.c b/src/basic/in-addr-util.c
index ee4ea67..c077f0a 100644
--- a/src/basic/in-addr-util.c
+++ b/src/basic/in-addr-util.c
@@ -91,14 +91,26 @@ bool in6_addr_is_link_local_all_nodes(const struct in6_addr *a) {
be32toh(a->s6_addr32[3]) == UINT32_C(0x00000001);
}
+bool in4_addr_is_multicast(const struct in_addr *a) {
+ assert(a);
+
+ return IN_MULTICAST(be32toh(a->s_addr));
+}
+
+bool in6_addr_is_multicast(const struct in6_addr *a) {
+ assert(a);
+
+ return IN6_IS_ADDR_MULTICAST(a);
+}
+
int in_addr_is_multicast(int family, const union in_addr_union *u) {
assert(u);
if (family == AF_INET)
- return IN_MULTICAST(be32toh(u->in.s_addr));
+ return in4_addr_is_multicast(&u->in);
if (family == AF_INET6)
- return IN6_IS_ADDR_MULTICAST(&u->in6);
+ return in6_addr_is_multicast(&u->in6);
return -EAFNOSUPPORT;
}
@@ -182,58 +194,69 @@ int in_addr_equal(int family, const union in_addr_union *a, const union in_addr_
return -EAFNOSUPPORT;
}
-int in_addr_prefix_intersect(
- int family,
- const union in_addr_union *a,
+bool in4_addr_prefix_intersect(
+ const struct in_addr *a,
unsigned aprefixlen,
- const union in_addr_union *b,
+ const struct in_addr *b,
unsigned bprefixlen) {
- unsigned m;
-
assert(a);
assert(b);
- /* Checks whether there are any addresses that are in both networks */
+ unsigned m = MIN3(aprefixlen, bprefixlen, (unsigned) (sizeof(struct in_addr) * 8));
+ if (m == 0)
+ return true; /* Let's return earlier, to avoid shift by 32. */
- m = MIN(aprefixlen, bprefixlen);
+ uint32_t x = be32toh(a->s_addr ^ b->s_addr);
+ uint32_t n = 0xFFFFFFFFUL << (32 - m);
+ return (x & n) == 0;
+}
- if (family == AF_INET) {
- uint32_t x, nm;
+bool in6_addr_prefix_intersect(
+ const struct in6_addr *a,
+ unsigned aprefixlen,
+ const struct in6_addr *b,
+ unsigned bprefixlen) {
- x = be32toh(a->in.s_addr ^ b->in.s_addr);
- nm = m == 0 ? 0 : 0xFFFFFFFFUL << (32 - m);
+ assert(a);
+ assert(b);
- return (x & nm) == 0;
- }
+ unsigned m = MIN3(aprefixlen, bprefixlen, (unsigned) (sizeof(struct in6_addr) * 8));
+ if (m == 0)
+ return true;
- if (family == AF_INET6) {
- unsigned i;
+ for (size_t i = 0; i < sizeof(struct in6_addr); i++) {
+ uint8_t x = a->s6_addr[i] ^ b->s6_addr[i];
+ uint8_t n = m < 8 ? (0xFF << (8 - m)) : 0xFF;
+ if ((x & n) != 0)
+ return false;
- if (m > 128)
- m = 128;
+ if (m <= 8)
+ break;
- for (i = 0; i < 16; i++) {
- uint8_t x, nm;
+ m -= 8;
+ }
- x = a->in6.s6_addr[i] ^ b->in6.s6_addr[i];
+ return true;
+}
- if (m < 8)
- nm = 0xFF << (8 - m);
- else
- nm = 0xFF;
+int in_addr_prefix_intersect(
+ int family,
+ const union in_addr_union *a,
+ unsigned aprefixlen,
+ const union in_addr_union *b,
+ unsigned bprefixlen) {
- if ((x & nm) != 0)
- return 0;
+ assert(a);
+ assert(b);
- if (m > 8)
- m -= 8;
- else
- m = 0;
- }
+ /* Checks whether there are any addresses that are in both networks. */
- return 1;
- }
+ if (family == AF_INET)
+ return in4_addr_prefix_intersect(&a->in, aprefixlen, &b->in, bprefixlen);
+
+ if (family == AF_INET6)
+ return in6_addr_prefix_intersect(&a->in6, aprefixlen, &b->in6, bprefixlen);
return -EAFNOSUPPORT;
}
@@ -922,12 +945,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 +990,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/basic/in-addr-util.h b/src/basic/in-addr-util.h
index 12720ca..9cd0aca 100644
--- a/src/basic/in-addr-util.h
+++ b/src/basic/in-addr-util.h
@@ -40,6 +40,8 @@ static inline bool in_addr_data_is_set(const struct in_addr_data *a) {
return in_addr_data_is_null(a);
}
+bool in4_addr_is_multicast(const struct in_addr *a);
+bool in6_addr_is_multicast(const struct in6_addr *a);
int in_addr_is_multicast(int family, const union in_addr_union *u);
bool in4_addr_is_link_local(const struct in_addr *a);
@@ -59,7 +61,22 @@ bool in6_addr_is_ipv4_mapped_address(const struct in6_addr *a);
bool in4_addr_equal(const struct in_addr *a, const struct in_addr *b);
bool in6_addr_equal(const struct in6_addr *a, const struct in6_addr *b);
int in_addr_equal(int family, const union in_addr_union *a, const union in_addr_union *b);
-int in_addr_prefix_intersect(int family, const union in_addr_union *a, unsigned aprefixlen, const union in_addr_union *b, unsigned bprefixlen);
+bool in4_addr_prefix_intersect(
+ const struct in_addr *a,
+ unsigned aprefixlen,
+ const struct in_addr *b,
+ unsigned bprefixlen);
+bool in6_addr_prefix_intersect(
+ const struct in6_addr *a,
+ unsigned aprefixlen,
+ const struct in6_addr *b,
+ unsigned bprefixlen);
+int in_addr_prefix_intersect(
+ int family,
+ const union in_addr_union *a,
+ unsigned aprefixlen,
+ const union in_addr_union *b,
+ unsigned bprefixlen);
int in_addr_prefix_next(int family, union in_addr_union *u, unsigned prefixlen);
int in_addr_prefix_nth(int family, union in_addr_union *u, unsigned prefixlen, uint64_t nth);
int in_addr_random_prefix(int family, union in_addr_union *u, unsigned prefixlen_fixed_part, unsigned prefixlen);
@@ -185,6 +202,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);
diff --git a/src/basic/initrd-util.c b/src/basic/initrd-util.c
index 03ccfbe..d3aa933 100644
--- a/src/basic/initrd-util.c
+++ b/src/basic/initrd-util.c
@@ -21,7 +21,7 @@ bool in_initrd(void) {
* This can be overridden by setting SYSTEMD_IN_INITRD=0|1.
*/
- r = getenv_bool_secure("SYSTEMD_IN_INITRD");
+ r = secure_getenv_bool("SYSTEMD_IN_INITRD");
if (r < 0 && r != -ENXIO)
log_debug_errno(r, "Failed to parse $SYSTEMD_IN_INITRD, ignoring: %m");
diff --git a/src/basic/iovec-util.c b/src/basic/iovec-util.c
index 991889a..6456945 100644
--- a/src/basic/iovec-util.c
+++ b/src/basic/iovec-util.c
@@ -62,8 +62,10 @@ char* set_iovec_string_field_free(struct iovec *iovec, size_t *n_iovec, const ch
return x;
}
-void iovec_array_free(struct iovec *iovec, size_t n) {
- FOREACH_ARRAY(i, iovec, n)
+void iovec_array_free(struct iovec *iovec, size_t n_iovec) {
+ assert(iovec || n_iovec == 0);
+
+ FOREACH_ARRAY(i, iovec, n_iovec)
free(i->iov_base);
free(iovec);
diff --git a/src/basic/iovec-util.h b/src/basic/iovec-util.h
index 39feabd..8cfa571 100644
--- a/src/basic/iovec-util.h
+++ b/src/basic/iovec-util.h
@@ -8,16 +8,38 @@
#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);
-#define IOVEC_MAKE(base, len) (struct iovec) { .iov_base = (base), .iov_len = (len) }
-#define IOVEC_MAKE_STRING(string) \
- ({ \
- const char *_s = (string); \
- IOVEC_MAKE((char*) _s, strlen(_s)); \
- })
+/* 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 */
@@ -35,10 +57,43 @@ static inline void iovec_done_erase(struct iovec *iovec) {
}
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);
+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/shared/keyring-util.c b/src/basic/keyring-util.c
index fadd90e..c32bd50 100644
--- a/src/shared/keyring-util.c
+++ b/src/basic/keyring-util.c
@@ -33,3 +33,34 @@ int keyring_read(key_serial_t serial, void **ret, size_t *ret_size) {
bufsize = (size_t) n;
}
}
+
+int keyring_describe(key_serial_t serial, char **ret) {
+ _cleanup_free_ char *tuple = NULL;
+ size_t sz = 64;
+ int c = -1; /* Workaround for maybe-uninitialized false positive due to missing_syscall indirection */
+
+ assert(ret);
+
+ for (;;) {
+ tuple = new(char, sz);
+ if (!tuple)
+ return log_oom_debug();
+
+ c = keyctl(KEYCTL_DESCRIBE, serial, (unsigned long) tuple, c, 0);
+ if (c < 0)
+ return log_debug_errno(errno, "Failed to describe key id %d: %m", serial);
+
+ if ((size_t) c <= sz)
+ break;
+
+ sz = c;
+ free(tuple);
+ }
+
+ /* The kernel returns a final NUL in the string, verify that. */
+ assert(tuple[c-1] == 0);
+
+ *ret = TAKE_PTR(tuple);
+
+ return 0;
+}
diff --git a/src/shared/keyring-util.h b/src/basic/keyring-util.h
index c8c53f1..6e6e685 100644
--- a/src/shared/keyring-util.h
+++ b/src/basic/keyring-util.h
@@ -9,3 +9,4 @@
#define TAKE_KEY_SERIAL(key_serial) TAKE_GENERIC(key_serial, key_serial_t, -1)
int keyring_read(key_serial_t serial, void **ret, size_t *ret_size);
+int keyring_describe(key_serial_t serial, char **ret);
diff --git a/src/basic/label.c b/src/basic/label.c
index f134e77..8b084a7 100644
--- a/src/basic/label.c
+++ b/src/basic/label.c
@@ -28,3 +28,7 @@ int label_ops_post(int dir_fd, const char *path) {
return label_ops->post(dir_fd, path);
}
+
+void label_ops_reset(void) {
+ label_ops = NULL;
+}
diff --git a/src/basic/label.h b/src/basic/label.h
index 9644e43..a070bf2 100644
--- a/src/basic/label.h
+++ b/src/basic/label.h
@@ -12,3 +12,4 @@ int label_ops_set(const LabelOps *label_ops);
int label_ops_pre(int dir_fd, const char *path, mode_t mode);
int label_ops_post(int dir_fd, const char *path);
+void label_ops_reset(void);
diff --git a/src/basic/linux/btrfs.h b/src/basic/linux/btrfs.h
index 74ed908..73a295e 100644
--- a/src/basic/linux/btrfs.h
+++ b/src/basic/linux/btrfs.h
@@ -94,6 +94,7 @@ struct btrfs_qgroup_limit {
* struct btrfs_qgroup_inherit.flags
*/
#define BTRFS_QGROUP_INHERIT_SET_LIMITS (1ULL << 0)
+#define BTRFS_QGROUP_INHERIT_FLAGS_SUPP (BTRFS_QGROUP_INHERIT_SET_LIMITS)
struct btrfs_qgroup_inherit {
__u64 flags;
@@ -189,6 +190,7 @@ struct btrfs_scrub_progress {
};
#define BTRFS_SCRUB_READONLY 1
+#define BTRFS_SCRUB_SUPPORTED_FLAGS (BTRFS_SCRUB_READONLY)
struct btrfs_ioctl_scrub_args {
__u64 devid; /* in */
__u64 start; /* in */
@@ -247,7 +249,17 @@ struct btrfs_ioctl_dev_info_args {
__u8 uuid[BTRFS_UUID_SIZE]; /* in/out */
__u64 bytes_used; /* out */
__u64 total_bytes; /* out */
- __u64 unused[379]; /* pad to 4k */
+ /*
+ * Optional, out.
+ *
+ * Showing the fsid of the device, allowing user space to check if this
+ * device is a seeding one.
+ *
+ * Introduced in v6.3, thus user space still needs to check if kernel
+ * changed this value. Older kernel will not touch the values here.
+ */
+ __u8 fsid[BTRFS_UUID_SIZE];
+ __u64 unused[377]; /* pad to 4k */
__u8 path[BTRFS_DEVICE_PATH_NAME_MAX]; /* out */
};
@@ -324,6 +336,8 @@ struct btrfs_ioctl_fs_info_args {
#define BTRFS_FEATURE_INCOMPAT_RAID1C34 (1ULL << 11)
#define BTRFS_FEATURE_INCOMPAT_ZONED (1ULL << 12)
#define BTRFS_FEATURE_INCOMPAT_EXTENT_TREE_V2 (1ULL << 13)
+#define BTRFS_FEATURE_INCOMPAT_RAID_STRIPE_TREE (1ULL << 14)
+#define BTRFS_FEATURE_INCOMPAT_SIMPLE_QUOTA (1ULL << 16)
struct btrfs_ioctl_feature_flags {
__u64 compat_flags;
@@ -603,6 +617,9 @@ struct btrfs_ioctl_clone_range_args {
*/
#define BTRFS_DEFRAG_RANGE_COMPRESS 1
#define BTRFS_DEFRAG_RANGE_START_IO 2
+#define BTRFS_DEFRAG_RANGE_FLAGS_SUPP (BTRFS_DEFRAG_RANGE_COMPRESS | \
+ BTRFS_DEFRAG_RANGE_START_IO)
+
struct btrfs_ioctl_defrag_range_args {
/* start of the defrag operation */
__u64 start;
@@ -744,6 +761,7 @@ struct btrfs_ioctl_get_dev_stats {
#define BTRFS_QUOTA_CTL_ENABLE 1
#define BTRFS_QUOTA_CTL_DISABLE 2
#define BTRFS_QUOTA_CTL_RESCAN__NOTUSED 3
+#define BTRFS_QUOTA_CTL_ENABLE_SIMPLE_QUOTA 4
struct btrfs_ioctl_quota_ctl_args {
__u64 cmd;
__u64 status;
diff --git a/src/basic/linux/btrfs_tree.h b/src/basic/linux/btrfs_tree.h
index ab38d0f..d24e8e1 100644
--- a/src/basic/linux/btrfs_tree.h
+++ b/src/basic/linux/btrfs_tree.h
@@ -73,6 +73,9 @@
/* Holds the block group items for extent tree v2. */
#define BTRFS_BLOCK_GROUP_TREE_OBJECTID 11ULL
+/* Tracks RAID stripes in block groups. */
+#define BTRFS_RAID_STRIPE_TREE_OBJECTID 12ULL
+
/* device stats in the device tree */
#define BTRFS_DEV_STATS_OBJECTID 0ULL
@@ -216,11 +219,31 @@
*/
#define BTRFS_METADATA_ITEM_KEY 169
+/*
+ * Special inline ref key which stores the id of the subvolume which originally
+ * created the extent. This subvolume owns the extent permanently from the
+ * perspective of simple quotas. Needed to know which subvolume to free quota
+ * usage from when the extent is deleted.
+ *
+ * Stored as an inline ref rather to avoid wasting space on a separate item on
+ * top of the existing extent item. However, unlike the other inline refs,
+ * there is one one owner ref per extent rather than one per extent.
+ *
+ * Because of this, it goes at the front of the list of inline refs, and thus
+ * must have a lower type value than any other inline ref type (to satisfy the
+ * disk format rule that inline refs have non-decreasing type).
+ */
+#define BTRFS_EXTENT_OWNER_REF_KEY 172
+
#define BTRFS_TREE_BLOCK_REF_KEY 176
#define BTRFS_EXTENT_DATA_REF_KEY 178
-#define BTRFS_EXTENT_REF_V0_KEY 180
+/*
+ * Obsolete key. Defintion removed in 6.6, value may be reused in the future.
+ *
+ * #define BTRFS_EXTENT_REF_V0_KEY 180
+ */
#define BTRFS_SHARED_BLOCK_REF_KEY 182
@@ -257,6 +280,8 @@
#define BTRFS_DEV_ITEM_KEY 216
#define BTRFS_CHUNK_ITEM_KEY 228
+#define BTRFS_RAID_STRIPE_KEY 230
+
/*
* Records the overall state of the qgroups.
* There's only one instance of this key present,
@@ -715,6 +740,30 @@ struct btrfs_free_space_header {
__le64 num_bitmaps;
} __attribute__ ((__packed__));
+struct btrfs_raid_stride {
+ /* The id of device this raid extent lives on. */
+ __le64 devid;
+ /* The physical location on disk. */
+ __le64 physical;
+} __attribute__ ((__packed__));
+
+/* The stripe_extent::encoding, 1:1 mapping of enum btrfs_raid_types. */
+#define BTRFS_STRIPE_RAID0 1
+#define BTRFS_STRIPE_RAID1 2
+#define BTRFS_STRIPE_DUP 3
+#define BTRFS_STRIPE_RAID10 4
+#define BTRFS_STRIPE_RAID5 5
+#define BTRFS_STRIPE_RAID6 6
+#define BTRFS_STRIPE_RAID1C3 7
+#define BTRFS_STRIPE_RAID1C4 8
+
+struct btrfs_stripe_extent {
+ __u8 encoding;
+ __u8 reserved[7];
+ /* An array of raid strides this stripe is composed of. */
+ struct btrfs_raid_stride strides[];
+} __attribute__ ((__packed__));
+
#define BTRFS_HEADER_FLAG_WRITTEN (1ULL << 0)
#define BTRFS_HEADER_FLAG_RELOC (1ULL << 1)
@@ -783,6 +832,10 @@ struct btrfs_shared_data_ref {
__le32 count;
} __attribute__ ((__packed__));
+struct btrfs_extent_owner_ref {
+ __le64 root_id;
+} __attribute__ ((__packed__));
+
struct btrfs_extent_inline_ref {
__u8 type;
__le64 offset;
@@ -1200,9 +1253,17 @@ static inline __u16 btrfs_qgroup_level(__u64 qgroupid)
*/
#define BTRFS_QGROUP_STATUS_FLAG_INCONSISTENT (1ULL << 2)
+/*
+ * Whether or not this filesystem is using simple quotas. Not exactly the
+ * incompat bit, because we support using simple quotas, disabling it, then
+ * going back to full qgroup quotas.
+ */
+#define BTRFS_QGROUP_STATUS_FLAG_SIMPLE_MODE (1ULL << 3)
+
#define BTRFS_QGROUP_STATUS_FLAGS_MASK (BTRFS_QGROUP_STATUS_FLAG_ON | \
BTRFS_QGROUP_STATUS_FLAG_RESCAN | \
- BTRFS_QGROUP_STATUS_FLAG_INCONSISTENT)
+ BTRFS_QGROUP_STATUS_FLAG_INCONSISTENT | \
+ BTRFS_QGROUP_STATUS_FLAG_SIMPLE_MODE)
#define BTRFS_QGROUP_STATUS_VERSION 1
@@ -1224,6 +1285,15 @@ struct btrfs_qgroup_status_item {
* of the scan. It contains a logical address
*/
__le64 rescan;
+
+ /*
+ * The generation when quotas were last enabled. Used by simple quotas to
+ * avoid decrementing when freeing an extent that was written before
+ * enable.
+ *
+ * Set only if flags contain BTRFS_QGROUP_STATUS_FLAG_SIMPLE_MODE.
+ */
+ __le64 enable_gen;
} __attribute__ ((__packed__));
struct btrfs_qgroup_info_item {
diff --git a/src/basic/linux/fou.h b/src/basic/linux/fou.h
index 87c2c9f..b5cd3e7 100644
--- a/src/basic/linux/fou.h
+++ b/src/basic/linux/fou.h
@@ -1,32 +1,37 @@
-/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */
-/* fou.h - FOU Interface */
+/* SPDX-License-Identifier: ((GPL-2.0 WITH Linux-syscall-note) OR BSD-3-Clause) */
+/* Do not edit directly, auto-generated from: */
+/* Documentation/netlink/specs/fou.yaml */
+/* YNL-GEN uapi header */
#ifndef _UAPI_LINUX_FOU_H
#define _UAPI_LINUX_FOU_H
-/* NETLINK_GENERIC related info
- */
#define FOU_GENL_NAME "fou"
-#define FOU_GENL_VERSION 0x1
+#define FOU_GENL_VERSION 1
enum {
- FOU_ATTR_UNSPEC,
- FOU_ATTR_PORT, /* u16 */
- FOU_ATTR_AF, /* u8 */
- FOU_ATTR_IPPROTO, /* u8 */
- FOU_ATTR_TYPE, /* u8 */
- FOU_ATTR_REMCSUM_NOPARTIAL, /* flag */
- FOU_ATTR_LOCAL_V4, /* u32 */
- FOU_ATTR_LOCAL_V6, /* in6_addr */
- FOU_ATTR_PEER_V4, /* u32 */
- FOU_ATTR_PEER_V6, /* in6_addr */
- FOU_ATTR_PEER_PORT, /* u16 */
- FOU_ATTR_IFINDEX, /* s32 */
-
- __FOU_ATTR_MAX,
+ FOU_ENCAP_UNSPEC,
+ FOU_ENCAP_DIRECT,
+ FOU_ENCAP_GUE,
};
-#define FOU_ATTR_MAX (__FOU_ATTR_MAX - 1)
+enum {
+ FOU_ATTR_UNSPEC,
+ FOU_ATTR_PORT,
+ FOU_ATTR_AF,
+ FOU_ATTR_IPPROTO,
+ FOU_ATTR_TYPE,
+ FOU_ATTR_REMCSUM_NOPARTIAL,
+ FOU_ATTR_LOCAL_V4,
+ FOU_ATTR_LOCAL_V6,
+ FOU_ATTR_PEER_V4,
+ FOU_ATTR_PEER_V6,
+ FOU_ATTR_PEER_PORT,
+ FOU_ATTR_IFINDEX,
+
+ __FOU_ATTR_MAX
+};
+#define FOU_ATTR_MAX (__FOU_ATTR_MAX - 1)
enum {
FOU_CMD_UNSPEC,
@@ -34,15 +39,8 @@ enum {
FOU_CMD_DEL,
FOU_CMD_GET,
- __FOU_CMD_MAX,
+ __FOU_CMD_MAX
};
-
-enum {
- FOU_ENCAP_UNSPEC,
- FOU_ENCAP_DIRECT,
- FOU_ENCAP_GUE,
-};
-
-#define FOU_CMD_MAX (__FOU_CMD_MAX - 1)
+#define FOU_CMD_MAX (__FOU_CMD_MAX - 1)
#endif /* _UAPI_LINUX_FOU_H */
diff --git a/src/basic/linux/if_bridge.h b/src/basic/linux/if_bridge.h
index d9de241..a5b743a 100644
--- a/src/basic/linux/if_bridge.h
+++ b/src/basic/linux/if_bridge.h
@@ -523,6 +523,9 @@ enum {
BRIDGE_VLANDB_ENTRY_TUNNEL_INFO,
BRIDGE_VLANDB_ENTRY_STATS,
BRIDGE_VLANDB_ENTRY_MCAST_ROUTER,
+ BRIDGE_VLANDB_ENTRY_MCAST_N_GROUPS,
+ BRIDGE_VLANDB_ENTRY_MCAST_MAX_GROUPS,
+ BRIDGE_VLANDB_ENTRY_NEIGH_SUPPRESS,
__BRIDGE_VLANDB_ENTRY_MAX,
};
#define BRIDGE_VLANDB_ENTRY_MAX (__BRIDGE_VLANDB_ENTRY_MAX - 1)
@@ -631,6 +634,11 @@ enum {
MDBA_MDB_EATTR_GROUP_MODE,
MDBA_MDB_EATTR_SOURCE,
MDBA_MDB_EATTR_RTPROT,
+ MDBA_MDB_EATTR_DST,
+ MDBA_MDB_EATTR_DST_PORT,
+ MDBA_MDB_EATTR_VNI,
+ MDBA_MDB_EATTR_IFINDEX,
+ MDBA_MDB_EATTR_SRC_VNI,
__MDBA_MDB_EATTR_MAX
};
#define MDBA_MDB_EATTR_MAX (__MDBA_MDB_EATTR_MAX - 1)
@@ -715,6 +723,24 @@ enum {
};
#define MDBA_SET_ENTRY_MAX (__MDBA_SET_ENTRY_MAX - 1)
+/* [MDBA_GET_ENTRY] = {
+ * struct br_mdb_entry
+ * [MDBA_GET_ENTRY_ATTRS] = {
+ * [MDBE_ATTR_SOURCE]
+ * struct in_addr / struct in6_addr
+ * [MDBE_ATTR_SRC_VNI]
+ * u32
+ * }
+ * }
+ */
+enum {
+ MDBA_GET_ENTRY_UNSPEC,
+ MDBA_GET_ENTRY,
+ MDBA_GET_ENTRY_ATTRS,
+ __MDBA_GET_ENTRY_MAX,
+};
+#define MDBA_GET_ENTRY_MAX (__MDBA_GET_ENTRY_MAX - 1)
+
/* [MDBA_SET_ENTRY_ATTRS] = {
* [MDBE_ATTR_xxx]
* ...
@@ -726,6 +752,12 @@ enum {
MDBE_ATTR_SRC_LIST,
MDBE_ATTR_GROUP_MODE,
MDBE_ATTR_RTPROT,
+ MDBE_ATTR_DST,
+ MDBE_ATTR_DST_PORT,
+ MDBE_ATTR_VNI,
+ MDBE_ATTR_IFINDEX,
+ MDBE_ATTR_SRC_VNI,
+ MDBE_ATTR_STATE_MASK,
__MDBE_ATTR_MAX,
};
#define MDBE_ATTR_MAX (__MDBE_ATTR_MAX - 1)
diff --git a/src/basic/linux/if_link.h b/src/basic/linux/if_link.h
index 1021a7e..ffa637b 100644
--- a/src/basic/linux/if_link.h
+++ b/src/basic/linux/if_link.h
@@ -374,6 +374,9 @@ enum {
IFLA_DEVLINK_PORT,
+ IFLA_GSO_IPV4_MAX_SIZE,
+ IFLA_GRO_IPV4_MAX_SIZE,
+ IFLA_DPLL_PIN,
__IFLA_MAX
};
@@ -458,6 +461,286 @@ enum in6_addr_gen_mode {
/* Bridge section */
+/**
+ * DOC: Bridge enum definition
+ *
+ * Please *note* that the timer values in the following section are expected
+ * in clock_t format, which is seconds multiplied by USER_HZ (generally
+ * defined as 100).
+ *
+ * @IFLA_BR_FORWARD_DELAY
+ * The bridge forwarding delay is the time spent in LISTENING state
+ * (before moving to LEARNING) and in LEARNING state (before moving
+ * to FORWARDING). Only relevant if STP is enabled.
+ *
+ * The valid values are between (2 * USER_HZ) and (30 * USER_HZ).
+ * The default value is (15 * USER_HZ).
+ *
+ * @IFLA_BR_HELLO_TIME
+ * The time between hello packets sent by the bridge, when it is a root
+ * bridge or a designated bridge. Only relevant if STP is enabled.
+ *
+ * The valid values are between (1 * USER_HZ) and (10 * USER_HZ).
+ * The default value is (2 * USER_HZ).
+ *
+ * @IFLA_BR_MAX_AGE
+ * The hello packet timeout is the time until another bridge in the
+ * spanning tree is assumed to be dead, after reception of its last hello
+ * message. Only relevant if STP is enabled.
+ *
+ * The valid values are between (6 * USER_HZ) and (40 * USER_HZ).
+ * The default value is (20 * USER_HZ).
+ *
+ * @IFLA_BR_AGEING_TIME
+ * Configure the bridge's FDB entries aging time. It is the time a MAC
+ * address will be kept in the FDB after a packet has been received from
+ * that address. After this time has passed, entries are cleaned up.
+ * Allow values outside the 802.1 standard specification for special cases:
+ *
+ * * 0 - entry never ages (all permanent)
+ * * 1 - entry disappears (no persistence)
+ *
+ * The default value is (300 * USER_HZ).
+ *
+ * @IFLA_BR_STP_STATE
+ * Turn spanning tree protocol on (*IFLA_BR_STP_STATE* > 0) or off
+ * (*IFLA_BR_STP_STATE* == 0) for this bridge.
+ *
+ * The default value is 0 (disabled).
+ *
+ * @IFLA_BR_PRIORITY
+ * Set this bridge's spanning tree priority, used during STP root bridge
+ * election.
+ *
+ * The valid values are between 0 and 65535.
+ *
+ * @IFLA_BR_VLAN_FILTERING
+ * Turn VLAN filtering on (*IFLA_BR_VLAN_FILTERING* > 0) or off
+ * (*IFLA_BR_VLAN_FILTERING* == 0). When disabled, the bridge will not
+ * consider the VLAN tag when handling packets.
+ *
+ * The default value is 0 (disabled).
+ *
+ * @IFLA_BR_VLAN_PROTOCOL
+ * Set the protocol used for VLAN filtering.
+ *
+ * The valid values are 0x8100(802.1Q) or 0x88A8(802.1AD). The default value
+ * is 0x8100(802.1Q).
+ *
+ * @IFLA_BR_GROUP_FWD_MASK
+ * The group forwarding mask. This is the bitmask that is applied to
+ * decide whether to forward incoming frames destined to link-local
+ * addresses (of the form 01:80:C2:00:00:0X).
+ *
+ * The default value is 0, which means the bridge does not forward any
+ * link-local frames coming on this port.
+ *
+ * @IFLA_BR_ROOT_ID
+ * The bridge root id, read only.
+ *
+ * @IFLA_BR_BRIDGE_ID
+ * The bridge id, read only.
+ *
+ * @IFLA_BR_ROOT_PORT
+ * The bridge root port, read only.
+ *
+ * @IFLA_BR_ROOT_PATH_COST
+ * The bridge root path cost, read only.
+ *
+ * @IFLA_BR_TOPOLOGY_CHANGE
+ * The bridge topology change, read only.
+ *
+ * @IFLA_BR_TOPOLOGY_CHANGE_DETECTED
+ * The bridge topology change detected, read only.
+ *
+ * @IFLA_BR_HELLO_TIMER
+ * The bridge hello timer, read only.
+ *
+ * @IFLA_BR_TCN_TIMER
+ * The bridge tcn timer, read only.
+ *
+ * @IFLA_BR_TOPOLOGY_CHANGE_TIMER
+ * The bridge topology change timer, read only.
+ *
+ * @IFLA_BR_GC_TIMER
+ * The bridge gc timer, read only.
+ *
+ * @IFLA_BR_GROUP_ADDR
+ * Set the MAC address of the multicast group this bridge uses for STP.
+ * The address must be a link-local address in standard Ethernet MAC address
+ * format. It is an address of the form 01:80:C2:00:00:0X, with X in [0, 4..f].
+ *
+ * The default value is 0.
+ *
+ * @IFLA_BR_FDB_FLUSH
+ * Flush bridge's fdb dynamic entries.
+ *
+ * @IFLA_BR_MCAST_ROUTER
+ * Set bridge's multicast router if IGMP snooping is enabled.
+ * The valid values are:
+ *
+ * * 0 - disabled.
+ * * 1 - automatic (queried).
+ * * 2 - permanently enabled.
+ *
+ * The default value is 1.
+ *
+ * @IFLA_BR_MCAST_SNOOPING
+ * Turn multicast snooping on (*IFLA_BR_MCAST_SNOOPING* > 0) or off
+ * (*IFLA_BR_MCAST_SNOOPING* == 0).
+ *
+ * The default value is 1.
+ *
+ * @IFLA_BR_MCAST_QUERY_USE_IFADDR
+ * If enabled use the bridge's own IP address as source address for IGMP
+ * queries (*IFLA_BR_MCAST_QUERY_USE_IFADDR* > 0) or the default of 0.0.0.0
+ * (*IFLA_BR_MCAST_QUERY_USE_IFADDR* == 0).
+ *
+ * The default value is 0 (disabled).
+ *
+ * @IFLA_BR_MCAST_QUERIER
+ * Enable (*IFLA_BR_MULTICAST_QUERIER* > 0) or disable
+ * (*IFLA_BR_MULTICAST_QUERIER* == 0) IGMP querier, ie sending of multicast
+ * queries by the bridge.
+ *
+ * The default value is 0 (disabled).
+ *
+ * @IFLA_BR_MCAST_HASH_ELASTICITY
+ * Set multicast database hash elasticity, It is the maximum chain length in
+ * the multicast hash table. This attribute is *deprecated* and the value
+ * is always 16.
+ *
+ * @IFLA_BR_MCAST_HASH_MAX
+ * Set maximum size of the multicast hash table
+ *
+ * The default value is 4096, the value must be a power of 2.
+ *
+ * @IFLA_BR_MCAST_LAST_MEMBER_CNT
+ * The Last Member Query Count is the number of Group-Specific Queries
+ * sent before the router assumes there are no local members. The Last
+ * Member Query Count is also the number of Group-and-Source-Specific
+ * Queries sent before the router assumes there are no listeners for a
+ * particular source.
+ *
+ * The default value is 2.
+ *
+ * @IFLA_BR_MCAST_STARTUP_QUERY_CNT
+ * The Startup Query Count is the number of Queries sent out on startup,
+ * separated by the Startup Query Interval.
+ *
+ * The default value is 2.
+ *
+ * @IFLA_BR_MCAST_LAST_MEMBER_INTVL
+ * The Last Member Query Interval is the Max Response Time inserted into
+ * Group-Specific Queries sent in response to Leave Group messages, and
+ * is also the amount of time between Group-Specific Query messages.
+ *
+ * The default value is (1 * USER_HZ).
+ *
+ * @IFLA_BR_MCAST_MEMBERSHIP_INTVL
+ * The interval after which the bridge will leave a group, if no membership
+ * reports for this group are received.
+ *
+ * The default value is (260 * USER_HZ).
+ *
+ * @IFLA_BR_MCAST_QUERIER_INTVL
+ * The interval between queries sent by other routers. if no queries are
+ * seen after this delay has passed, the bridge will start to send its own
+ * queries (as if *IFLA_BR_MCAST_QUERIER_INTVL* was enabled).
+ *
+ * The default value is (255 * USER_HZ).
+ *
+ * @IFLA_BR_MCAST_QUERY_INTVL
+ * The Query Interval is the interval between General Queries sent by
+ * the Querier.
+ *
+ * The default value is (125 * USER_HZ). The minimum value is (1 * USER_HZ).
+ *
+ * @IFLA_BR_MCAST_QUERY_RESPONSE_INTVL
+ * The Max Response Time used to calculate the Max Resp Code inserted
+ * into the periodic General Queries.
+ *
+ * The default value is (10 * USER_HZ).
+ *
+ * @IFLA_BR_MCAST_STARTUP_QUERY_INTVL
+ * The interval between queries in the startup phase.
+ *
+ * The default value is (125 * USER_HZ) / 4. The minimum value is (1 * USER_HZ).
+ *
+ * @IFLA_BR_NF_CALL_IPTABLES
+ * Enable (*NF_CALL_IPTABLES* > 0) or disable (*NF_CALL_IPTABLES* == 0)
+ * iptables hooks on the bridge.
+ *
+ * The default value is 0 (disabled).
+ *
+ * @IFLA_BR_NF_CALL_IP6TABLES
+ * Enable (*NF_CALL_IP6TABLES* > 0) or disable (*NF_CALL_IP6TABLES* == 0)
+ * ip6tables hooks on the bridge.
+ *
+ * The default value is 0 (disabled).
+ *
+ * @IFLA_BR_NF_CALL_ARPTABLES
+ * Enable (*NF_CALL_ARPTABLES* > 0) or disable (*NF_CALL_ARPTABLES* == 0)
+ * arptables hooks on the bridge.
+ *
+ * The default value is 0 (disabled).
+ *
+ * @IFLA_BR_VLAN_DEFAULT_PVID
+ * VLAN ID applied to untagged and priority-tagged incoming packets.
+ *
+ * The default value is 1. Setting to the special value 0 makes all ports of
+ * this bridge not have a PVID by default, which means that they will
+ * not accept VLAN-untagged traffic.
+ *
+ * @IFLA_BR_PAD
+ * Bridge attribute padding type for netlink message.
+ *
+ * @IFLA_BR_VLAN_STATS_ENABLED
+ * Enable (*IFLA_BR_VLAN_STATS_ENABLED* == 1) or disable
+ * (*IFLA_BR_VLAN_STATS_ENABLED* == 0) per-VLAN stats accounting.
+ *
+ * The default value is 0 (disabled).
+ *
+ * @IFLA_BR_MCAST_STATS_ENABLED
+ * Enable (*IFLA_BR_MCAST_STATS_ENABLED* > 0) or disable
+ * (*IFLA_BR_MCAST_STATS_ENABLED* == 0) multicast (IGMP/MLD) stats
+ * accounting.
+ *
+ * The default value is 0 (disabled).
+ *
+ * @IFLA_BR_MCAST_IGMP_VERSION
+ * Set the IGMP version.
+ *
+ * The valid values are 2 and 3. The default value is 2.
+ *
+ * @IFLA_BR_MCAST_MLD_VERSION
+ * Set the MLD version.
+ *
+ * The valid values are 1 and 2. The default value is 1.
+ *
+ * @IFLA_BR_VLAN_STATS_PER_PORT
+ * Enable (*IFLA_BR_VLAN_STATS_PER_PORT* == 1) or disable
+ * (*IFLA_BR_VLAN_STATS_PER_PORT* == 0) per-VLAN per-port stats accounting.
+ * Can be changed only when there are no port VLANs configured.
+ *
+ * The default value is 0 (disabled).
+ *
+ * @IFLA_BR_MULTI_BOOLOPT
+ * The multi_boolopt is used to control new boolean options to avoid adding
+ * new netlink attributes. You can look at ``enum br_boolopt_id`` for those
+ * options.
+ *
+ * @IFLA_BR_MCAST_QUERIER_STATE
+ * Bridge mcast querier states, read only.
+ *
+ * @IFLA_BR_FDB_N_LEARNED
+ * The number of dynamically learned FDB entries for the current bridge,
+ * read only.
+ *
+ * @IFLA_BR_FDB_MAX_LEARNED
+ * Set the number of max dynamically learned FDB entries for the current
+ * bridge.
+ */
enum {
IFLA_BR_UNSPEC,
IFLA_BR_FORWARD_DELAY,
@@ -507,6 +790,8 @@ enum {
IFLA_BR_VLAN_STATS_PER_PORT,
IFLA_BR_MULTI_BOOLOPT,
IFLA_BR_MCAST_QUERIER_STATE,
+ IFLA_BR_FDB_N_LEARNED,
+ IFLA_BR_FDB_MAX_LEARNED,
__IFLA_BR_MAX,
};
@@ -517,11 +802,252 @@ struct ifla_bridge_id {
__u8 addr[6]; /* ETH_ALEN */
};
+/**
+ * DOC: Bridge mode enum definition
+ *
+ * @BRIDGE_MODE_HAIRPIN
+ * Controls whether traffic may be sent back out of the port on which it
+ * was received. This option is also called reflective relay mode, and is
+ * used to support basic VEPA (Virtual Ethernet Port Aggregator)
+ * capabilities. By default, this flag is turned off and the bridge will
+ * not forward traffic back out of the receiving port.
+ */
enum {
BRIDGE_MODE_UNSPEC,
BRIDGE_MODE_HAIRPIN,
};
+/**
+ * DOC: Bridge port enum definition
+ *
+ * @IFLA_BRPORT_STATE
+ * The operation state of the port. Here are the valid values.
+ *
+ * * 0 - port is in STP *DISABLED* state. Make this port completely
+ * inactive for STP. This is also called BPDU filter and could be used
+ * to disable STP on an untrusted port, like a leaf virtual device.
+ * The traffic forwarding is also stopped on this port.
+ * * 1 - port is in STP *LISTENING* state. Only valid if STP is enabled
+ * on the bridge. In this state the port listens for STP BPDUs and
+ * drops all other traffic frames.
+ * * 2 - port is in STP *LEARNING* state. Only valid if STP is enabled on
+ * the bridge. In this state the port will accept traffic only for the
+ * purpose of updating MAC address tables.
+ * * 3 - port is in STP *FORWARDING* state. Port is fully active.
+ * * 4 - port is in STP *BLOCKING* state. Only valid if STP is enabled on
+ * the bridge. This state is used during the STP election process.
+ * In this state, port will only process STP BPDUs.
+ *
+ * @IFLA_BRPORT_PRIORITY
+ * The STP port priority. The valid values are between 0 and 255.
+ *
+ * @IFLA_BRPORT_COST
+ * The STP path cost of the port. The valid values are between 1 and 65535.
+ *
+ * @IFLA_BRPORT_MODE
+ * Set the bridge port mode. See *BRIDGE_MODE_HAIRPIN* for more details.
+ *
+ * @IFLA_BRPORT_GUARD
+ * Controls whether STP BPDUs will be processed by the bridge port. By
+ * default, the flag is turned off to allow BPDU processing. Turning this
+ * flag on will disable the bridge port if a STP BPDU packet is received.
+ *
+ * If the bridge has Spanning Tree enabled, hostile devices on the network
+ * may send BPDU on a port and cause network failure. Setting *guard on*
+ * will detect and stop this by disabling the port. The port will be
+ * restarted if the link is brought down, or removed and reattached.
+ *
+ * @IFLA_BRPORT_PROTECT
+ * Controls whether a given port is allowed to become a root port or not.
+ * Only used when STP is enabled on the bridge. By default the flag is off.
+ *
+ * This feature is also called root port guard. If BPDU is received from a
+ * leaf (edge) port, it should not be elected as root port. This could
+ * be used if using STP on a bridge and the downstream bridges are not fully
+ * trusted; this prevents a hostile guest from rerouting traffic.
+ *
+ * @IFLA_BRPORT_FAST_LEAVE
+ * This flag allows the bridge to immediately stop multicast traffic
+ * forwarding on a port that receives an IGMP Leave message. It is only used
+ * when IGMP snooping is enabled on the bridge. By default the flag is off.
+ *
+ * @IFLA_BRPORT_LEARNING
+ * Controls whether a given port will learn *source* MAC addresses from
+ * received traffic or not. Also controls whether dynamic FDB entries
+ * (which can also be added by software) will be refreshed by incoming
+ * traffic. By default this flag is on.
+ *
+ * @IFLA_BRPORT_UNICAST_FLOOD
+ * Controls whether unicast traffic for which there is no FDB entry will
+ * be flooded towards this port. By default this flag is on.
+ *
+ * @IFLA_BRPORT_PROXYARP
+ * Enable proxy ARP on this port.
+ *
+ * @IFLA_BRPORT_LEARNING_SYNC
+ * Controls whether a given port will sync MAC addresses learned on device
+ * port to bridge FDB.
+ *
+ * @IFLA_BRPORT_PROXYARP_WIFI
+ * Enable proxy ARP on this port which meets extended requirements by
+ * IEEE 802.11 and Hotspot 2.0 specifications.
+ *
+ * @IFLA_BRPORT_ROOT_ID
+ *
+ * @IFLA_BRPORT_BRIDGE_ID
+ *
+ * @IFLA_BRPORT_DESIGNATED_PORT
+ *
+ * @IFLA_BRPORT_DESIGNATED_COST
+ *
+ * @IFLA_BRPORT_ID
+ *
+ * @IFLA_BRPORT_NO
+ *
+ * @IFLA_BRPORT_TOPOLOGY_CHANGE_ACK
+ *
+ * @IFLA_BRPORT_CONFIG_PENDING
+ *
+ * @IFLA_BRPORT_MESSAGE_AGE_TIMER
+ *
+ * @IFLA_BRPORT_FORWARD_DELAY_TIMER
+ *
+ * @IFLA_BRPORT_HOLD_TIMER
+ *
+ * @IFLA_BRPORT_FLUSH
+ * Flush bridge ports' fdb dynamic entries.
+ *
+ * @IFLA_BRPORT_MULTICAST_ROUTER
+ * Configure the port's multicast router presence. A port with
+ * a multicast router will receive all multicast traffic.
+ * The valid values are:
+ *
+ * * 0 disable multicast routers on this port
+ * * 1 let the system detect the presence of routers (default)
+ * * 2 permanently enable multicast traffic forwarding on this port
+ * * 3 enable multicast routers temporarily on this port, not depending
+ * on incoming queries.
+ *
+ * @IFLA_BRPORT_PAD
+ *
+ * @IFLA_BRPORT_MCAST_FLOOD
+ * Controls whether a given port will flood multicast traffic for which
+ * there is no MDB entry. By default this flag is on.
+ *
+ * @IFLA_BRPORT_MCAST_TO_UCAST
+ * Controls whether a given port will replicate packets using unicast
+ * instead of multicast. By default this flag is off.
+ *
+ * This is done by copying the packet per host and changing the multicast
+ * destination MAC to a unicast one accordingly.
+ *
+ * *mcast_to_unicast* works on top of the multicast snooping feature of the
+ * bridge. Which means unicast copies are only delivered to hosts which
+ * are interested in unicast and signaled this via IGMP/MLD reports previously.
+ *
+ * This feature is intended for interface types which have a more reliable
+ * and/or efficient way to deliver unicast packets than broadcast ones
+ * (e.g. WiFi).
+ *
+ * However, it should only be enabled on interfaces where no IGMPv2/MLDv1
+ * report suppression takes place. IGMP/MLD report suppression issue is
+ * usually overcome by the network daemon (supplicant) enabling AP isolation
+ * and by that separating all STAs.
+ *
+ * Delivery of STA-to-STA IP multicast is made possible again by enabling
+ * and utilizing the bridge hairpin mode, which considers the incoming port
+ * as a potential outgoing port, too (see *BRIDGE_MODE_HAIRPIN* option).
+ * Hairpin mode is performed after multicast snooping, therefore leading
+ * to only deliver reports to STAs running a multicast router.
+ *
+ * @IFLA_BRPORT_VLAN_TUNNEL
+ * Controls whether vlan to tunnel mapping is enabled on the port.
+ * By default this flag is off.
+ *
+ * @IFLA_BRPORT_BCAST_FLOOD
+ * Controls flooding of broadcast traffic on the given port. By default
+ * this flag is on.
+ *
+ * @IFLA_BRPORT_GROUP_FWD_MASK
+ * Set the group forward mask. This is a bitmask that is applied to
+ * decide whether to forward incoming frames destined to link-local
+ * addresses. The addresses of the form are 01:80:C2:00:00:0X (defaults
+ * to 0, which means the bridge does not forward any link-local frames
+ * coming on this port).
+ *
+ * @IFLA_BRPORT_NEIGH_SUPPRESS
+ * Controls whether neighbor discovery (arp and nd) proxy and suppression
+ * is enabled on the port. By default this flag is off.
+ *
+ * @IFLA_BRPORT_ISOLATED
+ * Controls whether a given port will be isolated, which means it will be
+ * able to communicate with non-isolated ports only. By default this
+ * flag is off.
+ *
+ * @IFLA_BRPORT_BACKUP_PORT
+ * Set a backup port. If the port loses carrier all traffic will be
+ * redirected to the configured backup port. Set the value to 0 to disable
+ * it.
+ *
+ * @IFLA_BRPORT_MRP_RING_OPEN
+ *
+ * @IFLA_BRPORT_MRP_IN_OPEN
+ *
+ * @IFLA_BRPORT_MCAST_EHT_HOSTS_LIMIT
+ * The number of per-port EHT hosts limit. The default value is 512.
+ * Setting to 0 is not allowed.
+ *
+ * @IFLA_BRPORT_MCAST_EHT_HOSTS_CNT
+ * The current number of tracked hosts, read only.
+ *
+ * @IFLA_BRPORT_LOCKED
+ * Controls whether a port will be locked, meaning that hosts behind the
+ * port will not be able to communicate through the port unless an FDB
+ * entry with the unit's MAC address is in the FDB. The common use case is
+ * that hosts are allowed access through authentication with the IEEE 802.1X
+ * protocol or based on whitelists. By default this flag is off.
+ *
+ * Please note that secure 802.1X deployments should always use the
+ * *BR_BOOLOPT_NO_LL_LEARN* flag, to not permit the bridge to populate its
+ * FDB based on link-local (EAPOL) traffic received on the port.
+ *
+ * @IFLA_BRPORT_MAB
+ * Controls whether a port will use MAC Authentication Bypass (MAB), a
+ * technique through which select MAC addresses may be allowed on a locked
+ * port, without using 802.1X authentication. Packets with an unknown source
+ * MAC address generates a "locked" FDB entry on the incoming bridge port.
+ * The common use case is for user space to react to these bridge FDB
+ * notifications and optionally replace the locked FDB entry with a normal
+ * one, allowing traffic to pass for whitelisted MAC addresses.
+ *
+ * Setting this flag also requires *IFLA_BRPORT_LOCKED* and
+ * *IFLA_BRPORT_LEARNING*. *IFLA_BRPORT_LOCKED* ensures that unauthorized
+ * data packets are dropped, and *IFLA_BRPORT_LEARNING* allows the dynamic
+ * FDB entries installed by user space (as replacements for the locked FDB
+ * entries) to be refreshed and/or aged out.
+ *
+ * @IFLA_BRPORT_MCAST_N_GROUPS
+ *
+ * @IFLA_BRPORT_MCAST_MAX_GROUPS
+ * Sets the maximum number of MDB entries that can be registered for a
+ * given port. Attempts to register more MDB entries at the port than this
+ * limit allows will be rejected, whether they are done through netlink
+ * (e.g. the bridge tool), or IGMP or MLD membership reports. Setting a
+ * limit of 0 disables the limit. The default value is 0.
+ *
+ * @IFLA_BRPORT_NEIGH_VLAN_SUPPRESS
+ * Controls whether neighbor discovery (arp and nd) proxy and suppression is
+ * enabled for a given port. By default this flag is off.
+ *
+ * Note that this option only takes effect when *IFLA_BRPORT_NEIGH_SUPPRESS*
+ * is enabled for a given port.
+ *
+ * @IFLA_BRPORT_BACKUP_NHID
+ * The FDB nexthop object ID to attach to packets being redirected to a
+ * backup port that has VLAN tunnel mapping enabled (via the
+ * *IFLA_BRPORT_VLAN_TUNNEL* option). Setting a value of 0 (default) has
+ * the effect of not attaching any ID.
+ */
enum {
IFLA_BRPORT_UNSPEC,
IFLA_BRPORT_STATE, /* Spanning tree state */
@@ -564,6 +1090,10 @@ enum {
IFLA_BRPORT_MCAST_EHT_HOSTS_CNT,
IFLA_BRPORT_LOCKED,
IFLA_BRPORT_MAB,
+ IFLA_BRPORT_MCAST_N_GROUPS,
+ IFLA_BRPORT_MCAST_MAX_GROUPS,
+ IFLA_BRPORT_NEIGH_VLAN_SUPPRESS,
+ IFLA_BRPORT_BACKUP_NHID,
__IFLA_BRPORT_MAX
};
#define IFLA_BRPORT_MAX (__IFLA_BRPORT_MAX - 1)
@@ -630,6 +1160,7 @@ enum {
IFLA_MACVLAN_MACADDR_COUNT,
IFLA_MACVLAN_BC_QUEUE_LEN,
IFLA_MACVLAN_BC_QUEUE_LEN_USED,
+ IFLA_MACVLAN_BC_CUTOFF,
__IFLA_MACVLAN_MAX,
};
@@ -748,6 +1279,30 @@ struct tunnel_msg {
__u32 ifindex;
};
+/* netkit section */
+enum netkit_action {
+ NETKIT_NEXT = -1,
+ NETKIT_PASS = 0,
+ NETKIT_DROP = 2,
+ NETKIT_REDIRECT = 7,
+};
+
+enum netkit_mode {
+ NETKIT_L2,
+ NETKIT_L3,
+};
+
+enum {
+ IFLA_NETKIT_UNSPEC,
+ IFLA_NETKIT_PEER_INFO,
+ IFLA_NETKIT_PRIMARY,
+ IFLA_NETKIT_POLICY,
+ IFLA_NETKIT_PEER_POLICY,
+ IFLA_NETKIT_MODE,
+ __IFLA_NETKIT_MAX,
+};
+#define IFLA_NETKIT_MAX (__IFLA_NETKIT_MAX - 1)
+
/* VXLAN section */
/* include statistics in the dump */
@@ -821,6 +1376,8 @@ enum {
IFLA_VXLAN_TTL_INHERIT,
IFLA_VXLAN_DF,
IFLA_VXLAN_VNIFILTER, /* only applicable with COLLECT_METADATA mode */
+ IFLA_VXLAN_LOCALBYPASS,
+ IFLA_VXLAN_LABEL_POLICY, /* IPv6 flow label policy; ifla_vxlan_label_policy */
__IFLA_VXLAN_MAX
};
#define IFLA_VXLAN_MAX (__IFLA_VXLAN_MAX - 1)
@@ -838,6 +1395,13 @@ enum ifla_vxlan_df {
VXLAN_DF_MAX = __VXLAN_DF_END - 1,
};
+enum ifla_vxlan_label_policy {
+ VXLAN_LABEL_FIXED = 0,
+ VXLAN_LABEL_INHERIT = 1,
+ __VXLAN_LABEL_END,
+ VXLAN_LABEL_MAX = __VXLAN_LABEL_END - 1,
+};
+
/* GENEVE section */
enum {
IFLA_GENEVE_UNSPEC,
@@ -941,6 +1505,7 @@ enum {
IFLA_BOND_AD_LACP_ACTIVE,
IFLA_BOND_MISSED_MAX,
IFLA_BOND_NS_IP6_TARGET,
+ IFLA_BOND_COUPLED_CONTROL,
__IFLA_BOND_MAX,
};
@@ -1383,7 +1948,9 @@ enum {
enum {
IFLA_DSA_UNSPEC,
- IFLA_DSA_MASTER,
+ IFLA_DSA_CONDUIT,
+ /* Deprecated, use IFLA_DSA_CONDUIT instead */
+ IFLA_DSA_MASTER = IFLA_DSA_CONDUIT,
__IFLA_DSA_MAX,
};
diff --git a/src/basic/linux/in.h b/src/basic/linux/in.h
index 07a4cb1..e682ab6 100644
--- a/src/basic/linux/in.h
+++ b/src/basic/linux/in.h
@@ -162,6 +162,8 @@ struct in_addr {
#define MCAST_MSFILTER 48
#define IP_MULTICAST_ALL 49
#define IP_UNICAST_IF 50
+#define IP_LOCAL_PORT_RANGE 51
+#define IP_PROTOCOL 52
#define MCAST_EXCLUDE 0
#define MCAST_INCLUDE 1
diff --git a/src/basic/linux/in6.h b/src/basic/linux/in6.h
index c4c53a9..ff8d21f 100644
--- a/src/basic/linux/in6.h
+++ b/src/basic/linux/in6.h
@@ -145,7 +145,7 @@ struct in6_flowlabel_req {
#define IPV6_TLV_PADN 1
#define IPV6_TLV_ROUTERALERT 5
#define IPV6_TLV_CALIPSO 7 /* RFC 5570 */
-#define IPV6_TLV_IOAM 49 /* TEMPORARY IANA allocation for IOAM */
+#define IPV6_TLV_IOAM 49 /* RFC 9486 */
#define IPV6_TLV_JUMBO 194
#define IPV6_TLV_HAO 201 /* home address option */
diff --git a/src/basic/linux/magic.h b/src/basic/linux/magic.h
new file mode 100644
index 0000000..1b40a96
--- /dev/null
+++ b/src/basic/linux/magic.h
@@ -0,0 +1,106 @@
+/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */
+#ifndef __LINUX_MAGIC_H__
+#define __LINUX_MAGIC_H__
+
+#define ADFS_SUPER_MAGIC 0xadf5
+#define AFFS_SUPER_MAGIC 0xadff
+#define AFS_SUPER_MAGIC 0x5346414F
+#define AUTOFS_SUPER_MAGIC 0x0187
+#define CEPH_SUPER_MAGIC 0x00c36400
+#define CODA_SUPER_MAGIC 0x73757245
+#define CRAMFS_MAGIC 0x28cd3d45 /* some random number */
+#define CRAMFS_MAGIC_WEND 0x453dcd28 /* magic number with the wrong endianess */
+#define DEBUGFS_MAGIC 0x64626720
+#define SECURITYFS_MAGIC 0x73636673
+#define SELINUX_MAGIC 0xf97cff8c
+#define SMACK_MAGIC 0x43415d53 /* "SMAC" */
+#define RAMFS_MAGIC 0x858458f6 /* some random number */
+#define TMPFS_MAGIC 0x01021994
+#define HUGETLBFS_MAGIC 0x958458f6 /* some random number */
+#define SQUASHFS_MAGIC 0x73717368
+#define ECRYPTFS_SUPER_MAGIC 0xf15f
+#define EFS_SUPER_MAGIC 0x414A53
+#define EROFS_SUPER_MAGIC_V1 0xE0F5E1E2
+#define EXT2_SUPER_MAGIC 0xEF53
+#define EXT3_SUPER_MAGIC 0xEF53
+#define XENFS_SUPER_MAGIC 0xabba1974
+#define EXT4_SUPER_MAGIC 0xEF53
+#define BTRFS_SUPER_MAGIC 0x9123683E
+#define NILFS_SUPER_MAGIC 0x3434
+#define F2FS_SUPER_MAGIC 0xF2F52010
+#define HPFS_SUPER_MAGIC 0xf995e849
+#define ISOFS_SUPER_MAGIC 0x9660
+#define JFFS2_SUPER_MAGIC 0x72b6
+#define XFS_SUPER_MAGIC 0x58465342 /* "XFSB" */
+#define PSTOREFS_MAGIC 0x6165676C
+#define EFIVARFS_MAGIC 0xde5e81e4
+#define HOSTFS_SUPER_MAGIC 0x00c0ffee
+#define OVERLAYFS_SUPER_MAGIC 0x794c7630
+#define FUSE_SUPER_MAGIC 0x65735546
+
+#define MINIX_SUPER_MAGIC 0x137F /* minix v1 fs, 14 char names */
+#define MINIX_SUPER_MAGIC2 0x138F /* minix v1 fs, 30 char names */
+#define MINIX2_SUPER_MAGIC 0x2468 /* minix v2 fs, 14 char names */
+#define MINIX2_SUPER_MAGIC2 0x2478 /* minix v2 fs, 30 char names */
+#define MINIX3_SUPER_MAGIC 0x4d5a /* minix v3 fs, 60 char names */
+
+#define MSDOS_SUPER_MAGIC 0x4d44 /* MD */
+#define EXFAT_SUPER_MAGIC 0x2011BAB0
+#define NCP_SUPER_MAGIC 0x564c /* Guess, what 0x564c is :-) */
+#define NFS_SUPER_MAGIC 0x6969
+#define OCFS2_SUPER_MAGIC 0x7461636f
+#define OPENPROM_SUPER_MAGIC 0x9fa1
+#define QNX4_SUPER_MAGIC 0x002f /* qnx4 fs detection */
+#define QNX6_SUPER_MAGIC 0x68191122 /* qnx6 fs detection */
+#define AFS_FS_MAGIC 0x6B414653
+
+
+#define REISERFS_SUPER_MAGIC 0x52654973 /* used by gcc */
+ /* used by file system utilities that
+ look at the superblock, etc. */
+#define REISERFS_SUPER_MAGIC_STRING "ReIsErFs"
+#define REISER2FS_SUPER_MAGIC_STRING "ReIsEr2Fs"
+#define REISER2FS_JR_SUPER_MAGIC_STRING "ReIsEr3Fs"
+
+#define SMB_SUPER_MAGIC 0x517B
+#define CIFS_SUPER_MAGIC 0xFF534D42 /* the first four bytes of SMB PDUs */
+#define SMB2_SUPER_MAGIC 0xFE534D42
+
+#define CGROUP_SUPER_MAGIC 0x27e0eb
+#define CGROUP2_SUPER_MAGIC 0x63677270
+
+#define RDTGROUP_SUPER_MAGIC 0x7655821
+
+#define STACK_END_MAGIC 0x57AC6E9D
+
+#define TRACEFS_MAGIC 0x74726163
+
+#define V9FS_MAGIC 0x01021997
+
+#define BDEVFS_MAGIC 0x62646576
+#define DAXFS_MAGIC 0x64646178
+#define BINFMTFS_MAGIC 0x42494e4d
+#define DEVPTS_SUPER_MAGIC 0x1cd1
+#define BINDERFS_SUPER_MAGIC 0x6c6f6f70
+#define FUTEXFS_SUPER_MAGIC 0xBAD1DEA
+#define PIPEFS_MAGIC 0x50495045
+#define PROC_SUPER_MAGIC 0x9fa0
+#define SOCKFS_MAGIC 0x534F434B
+#define SYSFS_MAGIC 0x62656572
+#define USBDEVICE_SUPER_MAGIC 0x9fa2
+#define MTD_INODE_FS_MAGIC 0x11307854
+#define ANON_INODE_FS_MAGIC 0x09041934
+#define BTRFS_TEST_MAGIC 0x73727279
+#define NSFS_MAGIC 0x6e736673
+#define BPF_FS_MAGIC 0xcafe4a11
+#define AAFS_MAGIC 0x5a3c69f0
+#define ZONEFS_MAGIC 0x5a4f4653
+
+/* Since UDF 2.01 is ISO 13346 based... */
+#define UDF_SUPER_MAGIC 0x15013346
+#define DMA_BUF_MAGIC 0x444d4142 /* "DMAB" */
+#define DEVMEM_MAGIC 0x454d444d /* "DMEM" */
+#define SECRETMEM_MAGIC 0x5345434d /* "SECM" */
+#define PID_FS_MAGIC 0x50494446 /* "PIDF" */
+
+#endif /* __LINUX_MAGIC_H__ */
diff --git a/src/basic/linux/netfilter.h b/src/basic/linux/netfilter.h
new file mode 100644
index 0000000..30c045b
--- /dev/null
+++ b/src/basic/linux/netfilter.h
@@ -0,0 +1,76 @@
+/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */
+#ifndef __LINUX_NETFILTER_H
+#define __LINUX_NETFILTER_H
+
+#include <linux/types.h>
+
+#include <linux/in.h>
+#include <linux/in6.h>
+
+/* Responses from hook functions. */
+#define NF_DROP 0
+#define NF_ACCEPT 1
+#define NF_STOLEN 2
+#define NF_QUEUE 3
+#define NF_REPEAT 4
+#define NF_STOP 5 /* Deprecated, for userspace nf_queue compatibility. */
+#define NF_MAX_VERDICT NF_STOP
+
+/* we overload the higher bits for encoding auxiliary data such as the queue
+ * number or errno values. Not nice, but better than additional function
+ * arguments. */
+#define NF_VERDICT_MASK 0x000000ff
+
+/* extra verdict flags have mask 0x0000ff00 */
+#define NF_VERDICT_FLAG_QUEUE_BYPASS 0x00008000
+
+/* queue number (NF_QUEUE) or errno (NF_DROP) */
+#define NF_VERDICT_QMASK 0xffff0000
+#define NF_VERDICT_QBITS 16
+
+#define NF_QUEUE_NR(x) ((((x) << 16) & NF_VERDICT_QMASK) | NF_QUEUE)
+
+#define NF_DROP_ERR(x) (((-x) << 16) | NF_DROP)
+
+/* only for userspace compatibility */
+
+/* NF_VERDICT_BITS should be 8 now, but userspace might break if this changes */
+#define NF_VERDICT_BITS 16
+
+enum nf_inet_hooks {
+ NF_INET_PRE_ROUTING,
+ NF_INET_LOCAL_IN,
+ NF_INET_FORWARD,
+ NF_INET_LOCAL_OUT,
+ NF_INET_POST_ROUTING,
+ NF_INET_NUMHOOKS,
+ NF_INET_INGRESS = NF_INET_NUMHOOKS,
+};
+
+enum nf_dev_hooks {
+ NF_NETDEV_INGRESS,
+ NF_NETDEV_EGRESS,
+ NF_NETDEV_NUMHOOKS
+};
+
+enum {
+ NFPROTO_UNSPEC = 0,
+ NFPROTO_INET = 1,
+ NFPROTO_IPV4 = 2,
+ NFPROTO_ARP = 3,
+ NFPROTO_NETDEV = 5,
+ NFPROTO_BRIDGE = 7,
+ NFPROTO_IPV6 = 10,
+ NFPROTO_DECNET = 12,
+ NFPROTO_NUMPROTO,
+};
+
+union nf_inet_addr {
+ __u32 all[4];
+ __be32 ip;
+ __be32 ip6[4];
+ struct in_addr in;
+ struct in6_addr in6;
+};
+
+#endif /* __LINUX_NETFILTER_H */
diff --git a/src/basic/linux/netfilter/nf_tables.h b/src/basic/linux/netfilter/nf_tables.h
index cfa844d..aa4094c 100644
--- a/src/basic/linux/netfilter/nf_tables.h
+++ b/src/basic/linux/netfilter/nf_tables.h
@@ -98,6 +98,14 @@ enum nft_verdicts {
* @NFT_MSG_GETFLOWTABLE: get flow table (enum nft_flowtable_attributes)
* @NFT_MSG_DELFLOWTABLE: delete flow table (enum nft_flowtable_attributes)
* @NFT_MSG_GETRULE_RESET: get rules and reset stateful expressions (enum nft_obj_attributes)
+ * @NFT_MSG_DESTROYTABLE: destroy a table (enum nft_table_attributes)
+ * @NFT_MSG_DESTROYCHAIN: destroy a chain (enum nft_chain_attributes)
+ * @NFT_MSG_DESTROYRULE: destroy a rule (enum nft_rule_attributes)
+ * @NFT_MSG_DESTROYSET: destroy a set (enum nft_set_attributes)
+ * @NFT_MSG_DESTROYSETELEM: destroy a set element (enum nft_set_elem_attributes)
+ * @NFT_MSG_DESTROYOBJ: destroy a stateful object (enum nft_object_attributes)
+ * @NFT_MSG_DESTROYFLOWTABLE: destroy flow table (enum nft_flowtable_attributes)
+ * @NFT_MSG_GETSETELEM_RESET: get set elements and reset attached stateful expressions (enum nft_set_elem_attributes)
*/
enum nf_tables_msg_types {
NFT_MSG_NEWTABLE,
@@ -126,6 +134,14 @@ enum nf_tables_msg_types {
NFT_MSG_GETFLOWTABLE,
NFT_MSG_DELFLOWTABLE,
NFT_MSG_GETRULE_RESET,
+ NFT_MSG_DESTROYTABLE,
+ NFT_MSG_DESTROYCHAIN,
+ NFT_MSG_DESTROYRULE,
+ NFT_MSG_DESTROYSET,
+ NFT_MSG_DESTROYSETELEM,
+ NFT_MSG_DESTROYOBJ,
+ NFT_MSG_DESTROYFLOWTABLE,
+ NFT_MSG_GETSETELEM_RESET,
NFT_MSG_MAX,
};
@@ -163,13 +179,17 @@ enum nft_hook_attributes {
* enum nft_table_flags - nf_tables table flags
*
* @NFT_TABLE_F_DORMANT: this table is not active
+ * @NFT_TABLE_F_OWNER: this table is owned by a process
+ * @NFT_TABLE_F_PERSIST: this table shall outlive its owner
*/
enum nft_table_flags {
NFT_TABLE_F_DORMANT = 0x1,
NFT_TABLE_F_OWNER = 0x2,
+ NFT_TABLE_F_PERSIST = 0x4,
};
#define NFT_TABLE_F_MASK (NFT_TABLE_F_DORMANT | \
- NFT_TABLE_F_OWNER)
+ NFT_TABLE_F_OWNER | \
+ NFT_TABLE_F_PERSIST)
/**
* enum nft_table_attributes - nf_tables table netlink attributes
@@ -247,6 +267,7 @@ enum nft_chain_attributes {
* @NFTA_RULE_USERDATA: user data (NLA_BINARY, NFT_USERDATA_MAXLEN)
* @NFTA_RULE_ID: uniquely identifies a rule in a transaction (NLA_U32)
* @NFTA_RULE_POSITION_ID: transaction unique identifier of the previous rule (NLA_U32)
+ * @NFTA_RULE_CHAIN_ID: add the rule to chain by ID, alternative to @NFTA_RULE_CHAIN (NLA_U32)
*/
enum nft_rule_attributes {
NFTA_RULE_UNSPEC,
@@ -268,9 +289,11 @@ enum nft_rule_attributes {
/**
* enum nft_rule_compat_flags - nf_tables rule compat flags
*
+ * @NFT_RULE_COMPAT_F_UNUSED: unused
* @NFT_RULE_COMPAT_F_INV: invert the check result
*/
enum nft_rule_compat_flags {
+ NFT_RULE_COMPAT_F_UNUSED = (1 << 0),
NFT_RULE_COMPAT_F_INV = (1 << 1),
NFT_RULE_COMPAT_F_MASK = NFT_RULE_COMPAT_F_INV,
};
@@ -671,7 +694,7 @@ enum nft_range_ops {
* enum nft_range_attributes - nf_tables range expression netlink attributes
*
* @NFTA_RANGE_SREG: source register of data to compare (NLA_U32: nft_registers)
- * @NFTA_RANGE_OP: cmp operation (NLA_U32: nft_cmp_ops)
+ * @NFTA_RANGE_OP: cmp operation (NLA_U32: nft_range_ops)
* @NFTA_RANGE_FROM_DATA: data range from (NLA_NESTED: nft_data_attributes)
* @NFTA_RANGE_TO_DATA: data range to (NLA_NESTED: nft_data_attributes)
*/
@@ -845,12 +868,14 @@ enum nft_exthdr_flags {
* @NFT_EXTHDR_OP_TCP: match against tcp options
* @NFT_EXTHDR_OP_IPV4: match against ipv4 options
* @NFT_EXTHDR_OP_SCTP: match against sctp chunks
+ * @NFT_EXTHDR_OP_DCCP: match against dccp otions
*/
enum nft_exthdr_op {
NFT_EXTHDR_OP_IPV6,
NFT_EXTHDR_OP_TCPOPT,
NFT_EXTHDR_OP_IPV4,
NFT_EXTHDR_OP_SCTP,
+ NFT_EXTHDR_OP_DCCP,
__NFT_EXTHDR_OP_MAX
};
#define NFT_EXTHDR_OP_MAX (__NFT_EXTHDR_OP_MAX - 1)
@@ -864,7 +889,7 @@ enum nft_exthdr_op {
* @NFTA_EXTHDR_LEN: extension header length (NLA_U32)
* @NFTA_EXTHDR_FLAGS: extension header flags (NLA_U32)
* @NFTA_EXTHDR_OP: option match type (NLA_U32)
- * @NFTA_EXTHDR_SREG: option match type (NLA_U32)
+ * @NFTA_EXTHDR_SREG: source register (NLA_U32: nft_registers)
*/
enum nft_exthdr_attributes {
NFTA_EXTHDR_UNSPEC,
@@ -917,6 +942,7 @@ enum nft_exthdr_attributes {
* @NFT_META_TIME_HOUR: hour of day (in seconds)
* @NFT_META_SDIF: slave device interface index
* @NFT_META_SDIFNAME: slave device interface name
+ * @NFT_META_BRI_BROUTE: packet br_netfilter_broute bit
*/
enum nft_meta_keys {
NFT_META_LEN,
@@ -955,6 +981,7 @@ enum nft_meta_keys {
NFT_META_TIME_HOUR,
NFT_META_SDIF,
NFT_META_SDIFNAME,
+ NFT_META_BRI_BROUTE,
__NFT_META_IIFTYPE,
};
@@ -1246,10 +1273,10 @@ enum nft_last_attributes {
/**
* enum nft_log_attributes - nf_tables log expression netlink attributes
*
- * @NFTA_LOG_GROUP: netlink group to send messages to (NLA_U32)
+ * @NFTA_LOG_GROUP: netlink group to send messages to (NLA_U16)
* @NFTA_LOG_PREFIX: prefix to prepend to log messages (NLA_STRING)
* @NFTA_LOG_SNAPLEN: length of payload to include in netlink message (NLA_U32)
- * @NFTA_LOG_QTHRESHOLD: queue threshold (NLA_U32)
+ * @NFTA_LOG_QTHRESHOLD: queue threshold (NLA_U16)
* @NFTA_LOG_LEVEL: log level (NLA_U32)
* @NFTA_LOG_FLAGS: logging flags (NLA_U32)
*/
diff --git a/src/basic/linux/netlink.h b/src/basic/linux/netlink.h
index e2ae82e..f87aaf2 100644
--- a/src/basic/linux/netlink.h
+++ b/src/basic/linux/netlink.h
@@ -298,6 +298,8 @@ struct nla_bitfield32 {
* entry has attributes again, the policy for those inner ones
* and the corresponding maxtype may be specified.
* @NL_ATTR_TYPE_BITFIELD32: &struct nla_bitfield32 attribute
+ * @NL_ATTR_TYPE_SINT: 32-bit or 64-bit signed attribute, aligned to 4B
+ * @NL_ATTR_TYPE_UINT: 32-bit or 64-bit unsigned attribute, aligned to 4B
*/
enum netlink_attribute_type {
NL_ATTR_TYPE_INVALID,
@@ -322,6 +324,9 @@ enum netlink_attribute_type {
NL_ATTR_TYPE_NESTED_ARRAY,
NL_ATTR_TYPE_BITFIELD32,
+
+ NL_ATTR_TYPE_SINT,
+ NL_ATTR_TYPE_UINT,
};
/**
diff --git a/src/basic/linux/nexthop.h b/src/basic/linux/nexthop.h
index d8ffa8c..dd8787f 100644
--- a/src/basic/linux/nexthop.h
+++ b/src/basic/linux/nexthop.h
@@ -30,6 +30,9 @@ enum {
#define NEXTHOP_GRP_TYPE_MAX (__NEXTHOP_GRP_TYPE_MAX - 1)
+#define NHA_OP_FLAG_DUMP_STATS BIT(0)
+#define NHA_OP_FLAG_DUMP_HW_STATS BIT(1)
+
enum {
NHA_UNSPEC,
NHA_ID, /* u32; id for nexthop. id == 0 means auto-assign */
@@ -60,6 +63,18 @@ enum {
/* nested; nexthop bucket attributes */
NHA_RES_BUCKET,
+ /* u32; operation-specific flags */
+ NHA_OP_FLAGS,
+
+ /* nested; nexthop group stats */
+ NHA_GROUP_STATS,
+
+ /* u32; nexthop hardware stats enable */
+ NHA_HW_STATS_ENABLE,
+
+ /* u32; read-only; whether any driver collects HW stats */
+ NHA_HW_STATS_USED,
+
__NHA_MAX,
};
@@ -101,4 +116,34 @@ enum {
#define NHA_RES_BUCKET_MAX (__NHA_RES_BUCKET_MAX - 1)
+enum {
+ NHA_GROUP_STATS_UNSPEC,
+
+ /* nested; nexthop group entry stats */
+ NHA_GROUP_STATS_ENTRY,
+
+ __NHA_GROUP_STATS_MAX,
+};
+
+#define NHA_GROUP_STATS_MAX (__NHA_GROUP_STATS_MAX - 1)
+
+enum {
+ NHA_GROUP_STATS_ENTRY_UNSPEC,
+
+ /* u32; nexthop id of the nexthop group entry */
+ NHA_GROUP_STATS_ENTRY_ID,
+
+ /* uint; number of packets forwarded via the nexthop group entry */
+ NHA_GROUP_STATS_ENTRY_PACKETS,
+
+ /* uint; number of packets forwarded via the nexthop group entry in
+ * hardware
+ */
+ NHA_GROUP_STATS_ENTRY_PACKETS_HW,
+
+ __NHA_GROUP_STATS_ENTRY_MAX,
+};
+
+#define NHA_GROUP_STATS_ENTRY_MAX (__NHA_GROUP_STATS_ENTRY_MAX - 1)
+
#endif
diff --git a/src/basic/linux/nl80211.h b/src/basic/linux/nl80211.h
index c14a91b..f23ecbd 100644
--- a/src/basic/linux/nl80211.h
+++ b/src/basic/linux/nl80211.h
@@ -11,7 +11,7 @@
* Copyright 2008 Jouni Malinen <jouni.malinen@atheros.com>
* Copyright 2008 Colin McCabe <colin@cozybit.com>
* Copyright 2015-2017 Intel Deutschland GmbH
- * Copyright (C) 2018-2022 Intel Corporation
+ * Copyright (C) 2018-2024 Intel Corporation
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
@@ -72,7 +72,7 @@
* For drivers supporting TDLS with external setup (WIPHY_FLAG_SUPPORTS_TDLS
* and WIPHY_FLAG_TDLS_EXTERNAL_SETUP), the station lifetime is as follows:
* - a setup station entry is added, not yet authorized, without any rate
- * or capability information, this just exists to avoid race conditions
+ * or capability information; this just exists to avoid race conditions
* - when the TDLS setup is done, a single NL80211_CMD_SET_STATION is valid
* to add rate and capability information to the station and at the same
* time mark it authorized.
@@ -87,7 +87,7 @@
* DOC: Frame transmission/registration support
*
* Frame transmission and registration support exists to allow userspace
- * management entities such as wpa_supplicant react to management frames
+ * management entities such as wpa_supplicant to react to management frames
* that are not being handled by the kernel. This includes, for example,
* certain classes of action frames that cannot be handled in the kernel
* for various reasons.
@@ -113,7 +113,7 @@
*
* Frame transmission allows userspace to send for example the required
* responses to action frames. It is subject to some sanity checking,
- * but many frames can be transmitted. When a frame was transmitted, its
+ * but many frames can be transmitted. When a frame is transmitted, its
* status is indicated to the sending socket.
*
* For more technical details, see the corresponding command descriptions
@@ -123,7 +123,7 @@
/**
* DOC: Virtual interface / concurrency capabilities
*
- * Some devices are able to operate with virtual MACs, they can have
+ * Some devices are able to operate with virtual MACs; they can have
* more than one virtual interface. The capability handling for this
* is a bit complex though, as there may be a number of restrictions
* on the types of concurrency that are supported.
@@ -135,7 +135,7 @@
* Once concurrency is desired, more attributes must be observed:
* To start with, since some interface types are purely managed in
* software, like the AP-VLAN type in mac80211 for example, there's
- * an additional list of these, they can be added at any time and
+ * an additional list of these; they can be added at any time and
* are only restricted by some semantic restrictions (e.g. AP-VLAN
* cannot be added without a corresponding AP interface). This list
* is exported in the %NL80211_ATTR_SOFTWARE_IFTYPES attribute.
@@ -164,17 +164,17 @@
* Packet coalesce feature helps to reduce number of received interrupts
* to host by buffering these packets in firmware/hardware for some
* predefined time. Received interrupt will be generated when one of the
- * following events occur.
+ * following events occurs.
* a) Expiration of hardware timer whose expiration time is set to maximum
* coalescing delay of matching coalesce rule.
- * b) Coalescing buffer in hardware reaches it's limit.
+ * b) Coalescing buffer in hardware reaches its limit.
* c) Packet doesn't match any of the configured coalesce rules.
*
* User needs to configure following parameters for creating a coalesce
* rule.
* a) Maximum coalescing delay
* b) List of packet patterns which needs to be matched
- * c) Condition for coalescence. pattern 'match' or 'no match'
+ * c) Condition for coalescence: pattern 'match' or 'no match'
* Multiple such rules can be created.
*/
@@ -213,7 +213,7 @@
/**
* DOC: FILS shared key authentication offload
*
- * FILS shared key authentication offload can be advertized by drivers by
+ * FILS shared key authentication offload can be advertised by drivers by
* setting @NL80211_EXT_FEATURE_FILS_SK_OFFLOAD flag. The drivers that support
* FILS shared key authentication offload should be able to construct the
* authentication and association frames for FILS shared key authentication and
@@ -239,7 +239,7 @@
* The PMKSA can be maintained in userspace persistently so that it can be used
* later after reboots or wifi turn off/on also.
*
- * %NL80211_ATTR_FILS_CACHE_ID is the cache identifier advertized by a FILS
+ * %NL80211_ATTR_FILS_CACHE_ID is the cache identifier advertised by a FILS
* capable AP supporting PMK caching. It specifies the scope within which the
* PMKSAs are cached in an ESS. %NL80211_CMD_SET_PMKSA and
* %NL80211_CMD_DEL_PMKSA are enhanced to allow support for PMKSA caching based
@@ -290,12 +290,12 @@
* If the configuration needs to be applied for specific peer then the MAC
* address of the peer needs to be passed in %NL80211_ATTR_MAC, otherwise the
* configuration will be applied for all the connected peers in the vif except
- * any peers that have peer specific configuration for the TID by default; if
- * the %NL80211_TID_CONFIG_ATTR_OVERRIDE flag is set, peer specific values
+ * any peers that have peer-specific configuration for the TID by default; if
+ * the %NL80211_TID_CONFIG_ATTR_OVERRIDE flag is set, peer-specific values
* will be overwritten.
*
- * All this configuration is valid only for STA's current connection
- * i.e. the configuration will be reset to default when the STA connects back
+ * All this configuration is valid only for STA's current connection,
+ * i.e., the configuration will be reset to default when the STA connects back
* after disconnection/roaming, and this configuration will be cleared when
* the interface goes down.
*/
@@ -326,7 +326,7 @@
/**
* DOC: Multi-Link Operation
*
- * In Multi-Link Operation, a connection between to MLDs utilizes multiple
+ * In Multi-Link Operation, a connection between two MLDs utilizes multiple
* links. To use this in nl80211, various commands and responses now need
* to or will include the new %NL80211_ATTR_MLO_LINKS attribute.
* Additionally, various commands that need to operate on a specific link
@@ -335,6 +335,15 @@
*/
/**
+ * DOC: OWE DH IE handling offload
+ *
+ * By setting @NL80211_EXT_FEATURE_OWE_OFFLOAD flag, drivers can indicate
+ * kernel/application space to avoid DH IE handling. When this flag is
+ * advertised, the driver/device will take care of DH IE inclusion and
+ * processing of peer DH IE to generate PMK.
+ */
+
+/**
* enum nl80211_commands - supported nl80211 commands
*
* @NL80211_CMD_UNSPEC: unspecified command to catch errors
@@ -424,11 +433,13 @@
* interface identified by %NL80211_ATTR_IFINDEX.
* @NL80211_CMD_DEL_STATION: Remove a station identified by %NL80211_ATTR_MAC
* or, if no MAC address given, all stations, on the interface identified
- * by %NL80211_ATTR_IFINDEX. %NL80211_ATTR_MGMT_SUBTYPE and
+ * by %NL80211_ATTR_IFINDEX. For MLD station, MLD address is used in
+ * %NL80211_ATTR_MAC. %NL80211_ATTR_MGMT_SUBTYPE and
* %NL80211_ATTR_REASON_CODE can optionally be used to specify which type
* of disconnection indication should be sent to the station
* (Deauthentication or Disassociation frame and reason code for that
- * frame).
+ * frame). %NL80211_ATTR_MLO_LINK_ID can be used optionally to remove
+ * stations connected and using at least that link as one of its links.
*
* @NL80211_CMD_GET_MPATH: Get mesh path attributes for mesh path to
* destination %NL80211_ATTR_MAC on the interface identified by
@@ -511,7 +522,7 @@
* %NL80211_ATTR_SCHED_SCAN_PLANS. If %NL80211_ATTR_SCHED_SCAN_PLANS is
* not specified and only %NL80211_ATTR_SCHED_SCAN_INTERVAL is specified,
* scheduled scan will run in an infinite loop with the specified interval.
- * These attributes are mutually exculsive,
+ * These attributes are mutually exclusive,
* i.e. NL80211_ATTR_SCHED_SCAN_INTERVAL must not be passed if
* NL80211_ATTR_SCHED_SCAN_PLANS is defined.
* If for some reason scheduled scan is aborted by the driver, all scan
@@ -542,7 +553,7 @@
* %NL80211_CMD_STOP_SCHED_SCAN command is received or when the interface
* is brought down while a scheduled scan was running.
*
- * @NL80211_CMD_GET_SURVEY: get survey resuls, e.g. channel occupation
+ * @NL80211_CMD_GET_SURVEY: get survey results, e.g. channel occupation
* or noise level
* @NL80211_CMD_NEW_SURVEY_RESULTS: survey data notification (as a reply to
* NL80211_CMD_GET_SURVEY and on the "scan" multicast group)
@@ -553,12 +564,13 @@
* using %NL80211_ATTR_SSID, %NL80211_ATTR_FILS_CACHE_ID,
* %NL80211_ATTR_PMKID, and %NL80211_ATTR_PMK in case of FILS
* authentication where %NL80211_ATTR_FILS_CACHE_ID is the identifier
- * advertized by a FILS capable AP identifying the scope of PMKSA in an
+ * advertised by a FILS capable AP identifying the scope of PMKSA in an
* ESS.
* @NL80211_CMD_DEL_PMKSA: Delete a PMKSA cache entry, using %NL80211_ATTR_MAC
* (for the BSSID) and %NL80211_ATTR_PMKID or using %NL80211_ATTR_SSID,
* %NL80211_ATTR_FILS_CACHE_ID, and %NL80211_ATTR_PMKID in case of FILS
- * authentication.
+ * authentication. Additionally in case of SAE offload and OWE offloads
+ * PMKSA entry can be deleted using %NL80211_ATTR_SSID.
* @NL80211_CMD_FLUSH_PMKSA: Flush all PMKSA cache entries.
*
* @NL80211_CMD_REG_CHANGE: indicates to userspace the regulatory domain
@@ -597,7 +609,7 @@
* BSSID in case of station mode). %NL80211_ATTR_SSID is used to specify
* the SSID (mainly for association, but is included in authentication
* request, too, to help BSS selection. %NL80211_ATTR_WIPHY_FREQ +
- * %NL80211_ATTR_WIPHY_FREQ_OFFSET is used to specify the frequence of the
+ * %NL80211_ATTR_WIPHY_FREQ_OFFSET is used to specify the frequency of the
* channel in MHz. %NL80211_ATTR_AUTH_TYPE is used to specify the
* authentication type. %NL80211_ATTR_IE is used to define IEs
* (VendorSpecificInfo, but also including RSN IE and FT IEs) to be added
@@ -806,7 +818,7 @@
* reached.
* @NL80211_CMD_SET_CHANNEL: Set the channel (using %NL80211_ATTR_WIPHY_FREQ
* and the attributes determining channel width) the given interface
- * (identifed by %NL80211_ATTR_IFINDEX) shall operate on.
+ * (identified by %NL80211_ATTR_IFINDEX) shall operate on.
* In case multiple channels are supported by the device, the mechanism
* with which it switches channels is implementation-defined.
* When a monitor interface is given, it can only switch channel while
@@ -878,7 +890,7 @@
* inform userspace of the new replay counter.
*
* @NL80211_CMD_PMKSA_CANDIDATE: This is used as an event to inform userspace
- * of PMKSA caching dandidates.
+ * of PMKSA caching candidates.
*
* @NL80211_CMD_TDLS_OPER: Perform a high-level TDLS command (e.g. link setup).
* In addition, this can be used as an event to request userspace to take
@@ -914,7 +926,7 @@
*
* @NL80211_CMD_PROBE_CLIENT: Probe an associated station on an AP interface
* by sending a null data frame to it and reporting when the frame is
- * acknowleged. This is used to allow timing out inactive clients. Uses
+ * acknowledged. This is used to allow timing out inactive clients. Uses
* %NL80211_ATTR_IFINDEX and %NL80211_ATTR_MAC. The command returns a
* direct reply with an %NL80211_ATTR_COOKIE that is later used to match
* up the event with the request. The event includes the same data and
@@ -1125,11 +1137,15 @@
* @NL80211_CMD_DEL_PMK: For offloaded 4-Way handshake, delete the previously
* configured PMK for the authenticator address identified by
* %NL80211_ATTR_MAC.
- * @NL80211_CMD_PORT_AUTHORIZED: An event that indicates an 802.1X FT roam was
- * completed successfully. Drivers that support 4 way handshake offload
- * should send this event after indicating 802.1X FT assocation with
- * %NL80211_CMD_ROAM. If the 4 way handshake failed %NL80211_CMD_DISCONNECT
- * should be indicated instead.
+ * @NL80211_CMD_PORT_AUTHORIZED: An event that indicates port is authorized and
+ * open for regular data traffic. For STA/P2P-client, this event is sent
+ * with AP MAC address and for AP/P2P-GO, the event carries the STA/P2P-
+ * client MAC address.
+ * Drivers that support 4 way handshake offload should send this event for
+ * STA/P2P-client after successful 4-way HS or after 802.1X FT following
+ * NL80211_CMD_CONNECT or NL80211_CMD_ROAM. Drivers using AP/P2P-GO 4-way
+ * handshake offload should send this event on successful completion of
+ * 4-way handshake with the peer (STA/P2P-client).
* @NL80211_CMD_CONTROL_PORT_FRAME: Control Port (e.g. PAE) frame TX request
* and RX notification. This command is used both as a request to transmit
* a control port frame and as a notification that a control port frame
@@ -1166,6 +1182,23 @@
* %NL80211_ATTR_STATUS_CODE attribute in %NL80211_CMD_EXTERNAL_AUTH
* command interface.
*
+ * Host driver sends MLD address of the AP with %NL80211_ATTR_MLD_ADDR in
+ * %NL80211_CMD_EXTERNAL_AUTH event to indicate user space to enable MLO
+ * during the authentication offload in STA mode while connecting to MLD
+ * APs. Host driver should check %NL80211_ATTR_MLO_SUPPORT flag capability
+ * in %NL80211_CMD_CONNECT to know whether the user space supports enabling
+ * MLO during the authentication offload or not.
+ * User space should enable MLO during the authentication only when it
+ * receives the AP MLD address in authentication offload request. User
+ * space shouldn't enable MLO when the authentication offload request
+ * doesn't indicate the AP MLD address even if the AP is MLO capable.
+ * User space should use %NL80211_ATTR_MLD_ADDR as peer's MLD address and
+ * interface address identified by %NL80211_ATTR_IFINDEX as self MLD
+ * address. User space and host driver to use MLD addresses in RA, TA and
+ * BSSID fields of the frames between them, and host driver translates the
+ * MLD addresses to/from link addresses based on the link chosen for the
+ * authentication.
+ *
* Host driver reports this status on an authentication failure to the
* user space through the connect result as the user space would have
* initiated the connection through the connect request.
@@ -1281,6 +1314,26 @@
* @NL80211_CMD_MODIFY_LINK_STA: Modify a link of an MLD station
* @NL80211_CMD_REMOVE_LINK_STA: Remove a link of an MLD station
*
+ * @NL80211_CMD_SET_HW_TIMESTAMP: Enable/disable HW timestamping of Timing
+ * measurement and Fine timing measurement frames. If %NL80211_ATTR_MAC
+ * is included, enable/disable HW timestamping only for frames to/from the
+ * specified MAC address. Otherwise enable/disable HW timestamping for
+ * all TM/FTM frames (including ones that were enabled with specific MAC
+ * address). If %NL80211_ATTR_HW_TIMESTAMP_ENABLED is not included, disable
+ * HW timestamping.
+ * The number of peers that HW timestamping can be enabled for concurrently
+ * is indicated by %NL80211_ATTR_MAX_HW_TIMESTAMP_PEERS.
+ *
+ * @NL80211_CMD_LINKS_REMOVED: Notify userspace about the removal of STA MLD
+ * setup links due to AP MLD removing the corresponding affiliated APs with
+ * Multi-Link reconfiguration. %NL80211_ATTR_MLO_LINKS is used to provide
+ * information about the removed STA MLD setup links.
+ *
+ * @NL80211_CMD_SET_TID_TO_LINK_MAPPING: Set the TID to Link Mapping for a
+ * non-AP MLD station. The %NL80211_ATTR_MLO_TTLM_DLINK and
+ * %NL80211_ATTR_MLO_TTLM_ULINK attributes are used to specify the
+ * TID to Link mapping for downlink/uplink traffic.
+ *
* @NL80211_CMD_MAX: highest used command number
* @__NL80211_CMD_AFTER_LAST: internal use
*/
@@ -1532,6 +1585,12 @@ enum nl80211_commands {
NL80211_CMD_MODIFY_LINK_STA,
NL80211_CMD_REMOVE_LINK_STA,
+ NL80211_CMD_SET_HW_TIMESTAMP,
+
+ NL80211_CMD_LINKS_REMOVED,
+
+ NL80211_CMD_SET_TID_TO_LINK_MAPPING,
+
/* add new commands above here */
/* used to define NL80211_CMD_MAX below */
@@ -1789,7 +1848,7 @@ enum nl80211_commands {
* using %CMD_CONTROL_PORT_FRAME. If control port routing over NL80211 is
* to be used then userspace must also use the %NL80211_ATTR_SOCKET_OWNER
* flag. When used with %NL80211_ATTR_CONTROL_PORT_NO_PREAUTH, pre-auth
- * frames are not forwared over the control port.
+ * frames are not forwarded over the control port.
*
* @NL80211_ATTR_TESTDATA: Testmode data blob, passed through to the driver.
* We recommend using nested, driver-specific attributes within this.
@@ -1926,10 +1985,10 @@ enum nl80211_commands {
* bit. Depending on which antennas are selected in the bitmap, 802.11n
* drivers can derive which chainmasks to use (if all antennas belonging to
* a particular chain are disabled this chain should be disabled) and if
- * a chain has diversity antennas wether diversity should be used or not.
+ * a chain has diversity antennas whether diversity should be used or not.
* HT capabilities (STBC, TX Beamforming, Antenna selection) can be
* derived from the available chains after applying the antenna mask.
- * Non-802.11n drivers can derive wether to use diversity or not.
+ * Non-802.11n drivers can derive whether to use diversity or not.
* Drivers may reject configurations or RX/TX mask combinations they cannot
* support by returning -EINVAL.
*
@@ -2499,7 +2558,7 @@ enum nl80211_commands {
* from successful FILS authentication and is used with
* %NL80211_CMD_CONNECT.
*
- * @NL80211_ATTR_FILS_CACHE_ID: A 2-octet identifier advertized by a FILS AP
+ * @NL80211_ATTR_FILS_CACHE_ID: A 2-octet identifier advertised by a FILS AP
* identifying the scope of PMKSAs. This is used with
* @NL80211_CMD_SET_PMKSA and @NL80211_CMD_DEL_PMKSA.
*
@@ -2653,11 +2712,13 @@ enum nl80211_commands {
*
* @NL80211_ATTR_FILS_DISCOVERY: Optional parameter to configure FILS
* discovery. It is a nested attribute, see
- * &enum nl80211_fils_discovery_attributes.
+ * &enum nl80211_fils_discovery_attributes. Userspace should pass an empty
+ * nested attribute to disable this feature and delete the templates.
*
* @NL80211_ATTR_UNSOL_BCAST_PROBE_RESP: Optional parameter to configure
* unsolicited broadcast probe response. It is a nested attribute, see
- * &enum nl80211_unsol_bcast_probe_resp_attributes.
+ * &enum nl80211_unsol_bcast_probe_resp_attributes. Userspace should pass an empty
+ * nested attribute to disable this feature and delete the templates.
*
* @NL80211_ATTR_S1G_CAPABILITY: S1G Capability information element (from
* association request when used with NL80211_CMD_NEW_STATION)
@@ -2751,6 +2812,50 @@ enum nl80211_commands {
* the incoming frame RX timestamp.
* @NL80211_ATTR_TD_BITMAP: Transition Disable bitmap, for subsequent
* (re)associations.
+ *
+ * @NL80211_ATTR_PUNCT_BITMAP: (u32) Preamble puncturing bitmap, lowest
+ * bit corresponds to the lowest 20 MHz channel. Each bit set to 1
+ * indicates that the sub-channel is punctured. Higher 16 bits are
+ * reserved.
+ *
+ * @NL80211_ATTR_MAX_HW_TIMESTAMP_PEERS: Maximum number of peers that HW
+ * timestamping can be enabled for concurrently (u16), a wiphy attribute.
+ * A value of 0xffff indicates setting for all peers (i.e. not specifying
+ * an address with %NL80211_CMD_SET_HW_TIMESTAMP) is supported.
+ * @NL80211_ATTR_HW_TIMESTAMP_ENABLED: Indicates whether HW timestamping should
+ * be enabled or not (flag attribute).
+ *
+ * @NL80211_ATTR_EMA_RNR_ELEMS: Optional nested attribute for
+ * reduced neighbor report (RNR) elements. This attribute can be used
+ * only when NL80211_MBSSID_CONFIG_ATTR_EMA is enabled.
+ * Userspace is responsible for splitting the RNR into multiple
+ * elements such that each element excludes the non-transmitting
+ * profiles already included in the MBSSID element
+ * (%NL80211_ATTR_MBSSID_ELEMS) at the same index. Each EMA beacon
+ * will be generated by adding MBSSID and RNR elements at the same
+ * index. If the userspace includes more RNR elements than number of
+ * MBSSID elements then these will be added in every EMA beacon.
+ *
+ * @NL80211_ATTR_MLO_LINK_DISABLED: Flag attribute indicating that the link is
+ * disabled.
+ *
+ * @NL80211_ATTR_BSS_DUMP_INCLUDE_USE_DATA: Include BSS usage data, i.e.
+ * include BSSes that can only be used in restricted scenarios and/or
+ * cannot be used at all.
+ *
+ * @NL80211_ATTR_MLO_TTLM_DLINK: Binary attribute specifying the downlink TID to
+ * link mapping. The length is 8 * sizeof(u16). For each TID the link
+ * mapping is as defined in section 9.4.2.314 (TID-To-Link Mapping element)
+ * in Draft P802.11be_D4.0.
+ * @NL80211_ATTR_MLO_TTLM_ULINK: Binary attribute specifying the uplink TID to
+ * link mapping. The length is 8 * sizeof(u16). For each TID the link
+ * mapping is as defined in section 9.4.2.314 (TID-To-Link Mapping element)
+ * in Draft P802.11be_D4.0.
+ *
+ * @NL80211_ATTR_ASSOC_SPP_AMSDU: flag attribute used with
+ * %NL80211_CMD_ASSOCIATE indicating the SPP A-MSDUs
+ * are used on this connection
+ *
* @NUM_NL80211_ATTR: total number of nl80211_attrs available
* @NL80211_ATTR_MAX: highest attribute number currently defined
* @__NL80211_ATTR_AFTER_LAST: internal use
@@ -3280,6 +3385,22 @@ enum nl80211_attrs {
NL80211_ATTR_RX_HW_TIMESTAMP,
NL80211_ATTR_TD_BITMAP,
+ NL80211_ATTR_PUNCT_BITMAP,
+
+ NL80211_ATTR_MAX_HW_TIMESTAMP_PEERS,
+ NL80211_ATTR_HW_TIMESTAMP_ENABLED,
+
+ NL80211_ATTR_EMA_RNR_ELEMS,
+
+ NL80211_ATTR_MLO_LINK_DISABLED,
+
+ NL80211_ATTR_BSS_DUMP_INCLUDE_USE_DATA,
+
+ NL80211_ATTR_MLO_TTLM_DLINK,
+ NL80211_ATTR_MLO_TTLM_ULINK,
+
+ NL80211_ATTR_ASSOC_SPP_AMSDU,
+
/* add attributes here, update the policy in nl80211.c */
__NL80211_ATTR_AFTER_LAST,
@@ -3420,6 +3541,7 @@ enum nl80211_iftype {
* @NL80211_STA_FLAG_ASSOCIATED: station is associated; used with drivers
* that support %NL80211_FEATURE_FULL_AP_CLIENT_STATE to transition a
* previously added station into associated state
+ * @NL80211_STA_FLAG_SPP_AMSDU: station supports SPP A-MSDUs
* @NL80211_STA_FLAG_MAX: highest station flag number currently defined
* @__NL80211_STA_FLAG_AFTER_LAST: internal use
*/
@@ -3432,6 +3554,7 @@ enum nl80211_sta_flags {
NL80211_STA_FLAG_AUTHENTICATED,
NL80211_STA_FLAG_TDLS_PEER,
NL80211_STA_FLAG_ASSOCIATED,
+ NL80211_STA_FLAG_SPP_AMSDU,
/* keep last */
__NL80211_STA_FLAG_AFTER_LAST,
@@ -3606,6 +3729,13 @@ enum nl80211_eht_ru_alloc {
* (u8, see &enum nl80211_eht_gi)
* @NL80211_RATE_INFO_EHT_RU_ALLOC: EHT RU allocation, if not present then
* non-OFDMA was used (u8, see &enum nl80211_eht_ru_alloc)
+ * @NL80211_RATE_INFO_S1G_MCS: S1G MCS index (u8, 0-10)
+ * @NL80211_RATE_INFO_S1G_NSS: S1G NSS value (u8, 1-4)
+ * @NL80211_RATE_INFO_1_MHZ_WIDTH: 1 MHz S1G rate
+ * @NL80211_RATE_INFO_2_MHZ_WIDTH: 2 MHz S1G rate
+ * @NL80211_RATE_INFO_4_MHZ_WIDTH: 4 MHz S1G rate
+ * @NL80211_RATE_INFO_8_MHZ_WIDTH: 8 MHz S1G rate
+ * @NL80211_RATE_INFO_16_MHZ_WIDTH: 16 MHz S1G rate
* @__NL80211_RATE_INFO_AFTER_LAST: internal use
*/
enum nl80211_rate_info {
@@ -3632,6 +3762,13 @@ enum nl80211_rate_info {
NL80211_RATE_INFO_EHT_NSS,
NL80211_RATE_INFO_EHT_GI,
NL80211_RATE_INFO_EHT_RU_ALLOC,
+ NL80211_RATE_INFO_S1G_MCS,
+ NL80211_RATE_INFO_S1G_NSS,
+ NL80211_RATE_INFO_1_MHZ_WIDTH,
+ NL80211_RATE_INFO_2_MHZ_WIDTH,
+ NL80211_RATE_INFO_4_MHZ_WIDTH,
+ NL80211_RATE_INFO_8_MHZ_WIDTH,
+ NL80211_RATE_INFO_16_MHZ_WIDTH,
/* keep last */
__NL80211_RATE_INFO_AFTER_LAST,
@@ -4000,6 +4137,10 @@ enum nl80211_band_iftype_attr {
* @NL80211_BAND_ATTR_EDMG_BW_CONFIG: Channel BW Configuration subfield encodes
* the allowed channel bandwidth configurations.
* Defined by IEEE P802.11ay/D4.0 section 9.4.2.251, Table 13.
+ * @NL80211_BAND_ATTR_S1G_MCS_NSS_SET: S1G capabilities, supported S1G-MCS and NSS
+ * set subfield, as in the S1G information IE, 5 bytes
+ * @NL80211_BAND_ATTR_S1G_CAPA: S1G capabilities information subfield as in the
+ * S1G information IE, 10 bytes
* @NL80211_BAND_ATTR_MAX: highest band attribute currently defined
* @__NL80211_BAND_ATTR_AFTER_LAST: internal use
*/
@@ -4020,6 +4161,9 @@ enum nl80211_band_attr {
NL80211_BAND_ATTR_EDMG_CHANNELS,
NL80211_BAND_ATTR_EDMG_BW_CONFIG,
+ NL80211_BAND_ATTR_S1G_MCS_NSS_SET,
+ NL80211_BAND_ATTR_S1G_CAPA,
+
/* keep last */
__NL80211_BAND_ATTR_AFTER_LAST,
NL80211_BAND_ATTR_MAX = __NL80211_BAND_ATTR_AFTER_LAST - 1
@@ -4065,7 +4209,7 @@ enum nl80211_wmm_rule {
* (100 * dBm).
* @NL80211_FREQUENCY_ATTR_DFS_STATE: current state for DFS
* (enum nl80211_dfs_state)
- * @NL80211_FREQUENCY_ATTR_DFS_TIME: time in miliseconds for how long
+ * @NL80211_FREQUENCY_ATTR_DFS_TIME: time in milliseconds for how long
* this channel is in this DFS state.
* @NL80211_FREQUENCY_ATTR_NO_HT40_MINUS: HT40- isn't possible with this
* channel as the control channel
@@ -4119,6 +4263,19 @@ enum nl80211_wmm_rule {
* as the primary or any of the secondary channels isn't possible
* @NL80211_FREQUENCY_ATTR_NO_EHT: EHT operation is not allowed on this channel
* in current regulatory domain.
+ * @NL80211_FREQUENCY_ATTR_PSD: Power spectral density (in dBm) that
+ * is allowed on this channel in current regulatory domain.
+ * @NL80211_FREQUENCY_ATTR_DFS_CONCURRENT: Operation on this channel is
+ * allowed for peer-to-peer or adhoc communication under the control
+ * of a DFS master which operates on the same channel (FCC-594280 D01
+ * Section B.3). Should be used together with %NL80211_RRF_DFS only.
+ * @NL80211_FREQUENCY_ATTR_NO_6GHZ_VLP_CLIENT: Client connection to VLP AP
+ * not allowed using this channel
+ * @NL80211_FREQUENCY_ATTR_NO_6GHZ_AFC_CLIENT: Client connection to AFC AP
+ * not allowed using this channel
+ * @NL80211_FREQUENCY_ATTR_CAN_MONITOR: This channel can be used in monitor
+ * mode despite other (regulatory) restrictions, even if the channel is
+ * otherwise completely disabled.
* @NL80211_FREQUENCY_ATTR_MAX: highest frequency attribute number
* currently defined
* @__NL80211_FREQUENCY_ATTR_AFTER_LAST: internal use
@@ -4157,6 +4314,11 @@ enum nl80211_frequency_attr {
NL80211_FREQUENCY_ATTR_16MHZ,
NL80211_FREQUENCY_ATTR_NO_320MHZ,
NL80211_FREQUENCY_ATTR_NO_EHT,
+ NL80211_FREQUENCY_ATTR_PSD,
+ NL80211_FREQUENCY_ATTR_DFS_CONCURRENT,
+ NL80211_FREQUENCY_ATTR_NO_6GHZ_VLP_CLIENT,
+ NL80211_FREQUENCY_ATTR_NO_6GHZ_AFC_CLIENT,
+ NL80211_FREQUENCY_ATTR_CAN_MONITOR,
/* keep last */
__NL80211_FREQUENCY_ATTR_AFTER_LAST,
@@ -4169,6 +4331,10 @@ enum nl80211_frequency_attr {
#define NL80211_FREQUENCY_ATTR_NO_IR NL80211_FREQUENCY_ATTR_NO_IR
#define NL80211_FREQUENCY_ATTR_GO_CONCURRENT \
NL80211_FREQUENCY_ATTR_IR_CONCURRENT
+#define NL80211_FREQUENCY_ATTR_NO_UHB_VLP_CLIENT \
+ NL80211_FREQUENCY_ATTR_NO_6GHZ_VLP_CLIENT
+#define NL80211_FREQUENCY_ATTR_NO_UHB_AFC_CLIENT \
+ NL80211_FREQUENCY_ATTR_NO_6GHZ_AFC_CLIENT
/**
* enum nl80211_bitrate_attr - bitrate attributes
@@ -4257,6 +4423,8 @@ enum nl80211_reg_type {
* a given frequency range. The value is in mBm (100 * dBm).
* @NL80211_ATTR_DFS_CAC_TIME: DFS CAC time in milliseconds.
* If not present or 0 default CAC time will be used.
+ * @NL80211_ATTR_POWER_RULE_PSD: power spectral density (in dBm).
+ * This could be negative.
* @NL80211_REG_RULE_ATTR_MAX: highest regulatory rule attribute number
* currently defined
* @__NL80211_REG_RULE_ATTR_AFTER_LAST: internal use
@@ -4274,6 +4442,8 @@ enum nl80211_reg_rule_attr {
NL80211_ATTR_DFS_CAC_TIME,
+ NL80211_ATTR_POWER_RULE_PSD,
+
/* keep last */
__NL80211_REG_RULE_ATTR_AFTER_LAST,
NL80211_REG_RULE_ATTR_MAX = __NL80211_REG_RULE_ATTR_AFTER_LAST - 1
@@ -4302,14 +4472,7 @@ enum nl80211_reg_rule_attr {
* value as specified by &struct nl80211_bss_select_rssi_adjust.
* @NL80211_SCHED_SCAN_MATCH_ATTR_BSSID: BSSID to be used for matching
* (this cannot be used together with SSID).
- * @NL80211_SCHED_SCAN_MATCH_PER_BAND_RSSI: Nested attribute that carries the
- * band specific minimum rssi thresholds for the bands defined in
- * enum nl80211_band. The minimum rssi threshold value(s32) specific to a
- * band shall be encapsulated in attribute with type value equals to one
- * of the NL80211_BAND_* defined in enum nl80211_band. For example, the
- * minimum rssi threshold value for 2.4GHZ band shall be encapsulated
- * within an attribute of type NL80211_BAND_2GHZ. And one or more of such
- * attributes will be nested within this attribute.
+ * @NL80211_SCHED_SCAN_MATCH_PER_BAND_RSSI: Obsolete
* @NL80211_SCHED_SCAN_MATCH_ATTR_MAX: highest scheduled scan filter
* attribute number currently defined
* @__NL80211_SCHED_SCAN_MATCH_ATTR_AFTER_LAST: internal use
@@ -4322,7 +4485,7 @@ enum nl80211_sched_scan_match_attr {
NL80211_SCHED_SCAN_MATCH_ATTR_RELATIVE_RSSI,
NL80211_SCHED_SCAN_MATCH_ATTR_RSSI_ADJUST,
NL80211_SCHED_SCAN_MATCH_ATTR_BSSID,
- NL80211_SCHED_SCAN_MATCH_PER_BAND_RSSI,
+ NL80211_SCHED_SCAN_MATCH_PER_BAND_RSSI, /* obsolete */
/* keep last */
__NL80211_SCHED_SCAN_MATCH_ATTR_AFTER_LAST,
@@ -4356,6 +4519,14 @@ enum nl80211_sched_scan_match_attr {
* @NL80211_RRF_NO_160MHZ: 160MHz operation not allowed
* @NL80211_RRF_NO_HE: HE operation not allowed
* @NL80211_RRF_NO_320MHZ: 320MHz operation not allowed
+ * @NL80211_RRF_NO_EHT: EHT operation not allowed
+ * @NL80211_RRF_PSD: Ruleset has power spectral density value
+ * @NL80211_RRF_DFS_CONCURRENT: Operation on this channel is allowed for
+ peer-to-peer or adhoc communication under the control of a DFS master
+ which operates on the same channel (FCC-594280 D01 Section B.3).
+ Should be used together with %NL80211_RRF_DFS only.
+ * @NL80211_RRF_NO_6GHZ_VLP_CLIENT: Client connection to VLP AP not allowed
+ * @NL80211_RRF_NO_6GHZ_AFC_CLIENT: Client connection to AFC AP not allowed
*/
enum nl80211_reg_rule_flags {
NL80211_RRF_NO_OFDM = 1<<0,
@@ -4375,6 +4546,11 @@ enum nl80211_reg_rule_flags {
NL80211_RRF_NO_160MHZ = 1<<16,
NL80211_RRF_NO_HE = 1<<17,
NL80211_RRF_NO_320MHZ = 1<<18,
+ NL80211_RRF_NO_EHT = 1<<19,
+ NL80211_RRF_PSD = 1<<20,
+ NL80211_RRF_DFS_CONCURRENT = 1<<21,
+ NL80211_RRF_NO_6GHZ_VLP_CLIENT = 1<<22,
+ NL80211_RRF_NO_6GHZ_AFC_CLIENT = 1<<23,
};
#define NL80211_RRF_PASSIVE_SCAN NL80211_RRF_NO_IR
@@ -4383,6 +4559,8 @@ enum nl80211_reg_rule_flags {
#define NL80211_RRF_NO_HT40 (NL80211_RRF_NO_HT40MINUS |\
NL80211_RRF_NO_HT40PLUS)
#define NL80211_RRF_GO_CONCURRENT NL80211_RRF_IR_CONCURRENT
+#define NL80211_RRF_NO_UHB_VLP_CLIENT NL80211_RRF_NO_6GHZ_VLP_CLIENT
+#define NL80211_RRF_NO_UHB_AFC_CLIENT NL80211_RRF_NO_6GHZ_AFC_CLIENT
/* For backport compatibility with older userspace */
#define NL80211_RRF_NO_IR_ALL (NL80211_RRF_NO_IR | __NL80211_RRF_NO_IBSS)
@@ -4912,6 +5090,36 @@ enum nl80211_bss_scan_width {
};
/**
+ * enum nl80211_bss_use_for - bitmap indicating possible BSS use
+ * @NL80211_BSS_USE_FOR_NORMAL: Use this BSS for normal "connection",
+ * including IBSS/MBSS depending on the type.
+ * @NL80211_BSS_USE_FOR_MLD_LINK: This BSS can be used as a link in an
+ * MLO connection. Note that for an MLO connection, all links including
+ * the assoc link must have this flag set, and the assoc link must
+ * additionally have %NL80211_BSS_USE_FOR_NORMAL set.
+ */
+enum nl80211_bss_use_for {
+ NL80211_BSS_USE_FOR_NORMAL = 1 << 0,
+ NL80211_BSS_USE_FOR_MLD_LINK = 1 << 1,
+};
+
+/**
+ * enum nl80211_bss_cannot_use_reasons - reason(s) connection to a
+ * BSS isn't possible
+ * @NL80211_BSS_CANNOT_USE_NSTR_NONPRIMARY: NSTR nonprimary links aren't
+ * supported by the device, and this BSS entry represents one.
+ * @NL80211_BSS_CANNOT_USE_6GHZ_PWR_MISMATCH: STA is not supporting
+ * the AP power type (SP, VLP, AP) that the AP uses.
+ */
+enum nl80211_bss_cannot_use_reasons {
+ NL80211_BSS_CANNOT_USE_NSTR_NONPRIMARY = 1 << 0,
+ NL80211_BSS_CANNOT_USE_6GHZ_PWR_MISMATCH = 1 << 1,
+};
+
+#define NL80211_BSS_CANNOT_USE_UHB_PWR_MISMATCH \
+ NL80211_BSS_CANNOT_USE_6GHZ_PWR_MISMATCH
+
+/**
* enum nl80211_bss - netlink attributes for a BSS
*
* @__NL80211_BSS_INVALID: invalid
@@ -4942,7 +5150,7 @@ enum nl80211_bss_scan_width {
* elements from a Beacon frame (bin); not present if no Beacon frame has
* yet been received
* @NL80211_BSS_CHAN_WIDTH: channel width of the control channel
- * (u32, enum nl80211_bss_scan_width)
+ * (u32, enum nl80211_bss_scan_width) - No longer used!
* @NL80211_BSS_BEACON_TSF: TSF of the last received beacon (u64)
* (not present if no beacon frame has been received yet)
* @NL80211_BSS_PRESP_DATA: the data in @NL80211_BSS_INFORMATION_ELEMENTS and
@@ -4963,6 +5171,14 @@ enum nl80211_bss_scan_width {
* @NL80211_BSS_FREQUENCY_OFFSET: frequency offset in KHz
* @NL80211_BSS_MLO_LINK_ID: MLO link ID of the BSS (u8).
* @NL80211_BSS_MLD_ADDR: MLD address of this BSS if connected to it.
+ * @NL80211_BSS_USE_FOR: u32 bitmap attribute indicating what the BSS can be
+ * used for, see &enum nl80211_bss_use_for.
+ * @NL80211_BSS_CANNOT_USE_REASONS: Indicates the reason that this BSS cannot
+ * be used for all or some of the possible uses by the device reporting it,
+ * even though its presence was detected.
+ * This is a u64 attribute containing a bitmap of values from
+ * &enum nl80211_cannot_use_reasons, note that the attribute may be missing
+ * if no reasons are specified.
* @__NL80211_BSS_AFTER_LAST: internal
* @NL80211_BSS_MAX: highest BSS attribute
*/
@@ -4990,6 +5206,8 @@ enum nl80211_bss {
NL80211_BSS_FREQUENCY_OFFSET,
NL80211_BSS_MLO_LINK_ID,
NL80211_BSS_MLD_ADDR,
+ NL80211_BSS_USE_FOR,
+ NL80211_BSS_CANNOT_USE_REASONS,
/* keep last */
__NL80211_BSS_AFTER_LAST,
@@ -5338,7 +5556,7 @@ enum nl80211_tx_rate_setting {
* (%NL80211_TID_CONFIG_ATTR_TIDS, %NL80211_TID_CONFIG_ATTR_OVERRIDE).
* @NL80211_TID_CONFIG_ATTR_PEER_SUPP: same as the previous per-vif one, but
* per peer instead.
- * @NL80211_TID_CONFIG_ATTR_OVERRIDE: flag attribue, if set indicates
+ * @NL80211_TID_CONFIG_ATTR_OVERRIDE: flag attribute, if set indicates
* that the new configuration overrides all previous peer
* configurations, otherwise previous peer specific configurations
* should be left untouched.
@@ -5539,6 +5757,8 @@ struct nl80211_pattern_support {
* %NL80211_ATTR_SCAN_FREQUENCIES contains more than one
* frequency, it means that the match occurred in more than one
* channel.
+ * @NL80211_WOWLAN_TRIG_UNPROTECTED_DEAUTH_DISASSOC: For wakeup reporting only.
+ * Wake up happened due to unprotected deauth or disassoc frame in MFP.
* @NUM_NL80211_WOWLAN_TRIG: number of wake on wireless triggers
* @MAX_NL80211_WOWLAN_TRIG: highest wowlan trigger attribute number
*
@@ -5566,6 +5786,7 @@ enum nl80211_wowlan_triggers {
NL80211_WOWLAN_TRIG_WAKEUP_TCP_NOMORETOKENS,
NL80211_WOWLAN_TRIG_NET_DETECT,
NL80211_WOWLAN_TRIG_NET_DETECT_RESULTS,
+ NL80211_WOWLAN_TRIG_UNPROTECTED_DEAUTH_DISASSOC,
/* keep last */
NUM_NL80211_WOWLAN_TRIG,
@@ -5721,7 +5942,7 @@ enum nl80211_attr_coalesce_rule {
/**
* enum nl80211_coalesce_condition - coalesce rule conditions
- * @NL80211_COALESCE_CONDITION_MATCH: coalaesce Rx packets when patterns
+ * @NL80211_COALESCE_CONDITION_MATCH: coalesce Rx packets when patterns
* in a rule are matched.
* @NL80211_COALESCE_CONDITION_NO_MATCH: coalesce Rx packets when patterns
* in a rule are not matched.
@@ -5820,7 +6041,7 @@ enum nl80211_if_combination_attrs {
* enum nl80211_plink_state - state of a mesh peer link finite state machine
*
* @NL80211_PLINK_LISTEN: initial state, considered the implicit
- * state of non existent mesh peer links
+ * state of non-existent mesh peer links
* @NL80211_PLINK_OPN_SNT: mesh plink open frame has been sent to
* this mesh peer
* @NL80211_PLINK_OPN_RCVD: mesh plink open frame has been received
@@ -5869,6 +6090,7 @@ enum plink_actions {
#define NL80211_KEK_LEN 16
#define NL80211_KCK_EXT_LEN 24
#define NL80211_KEK_EXT_LEN 32
+#define NL80211_KCK_EXT_LEN_32 32
#define NL80211_REPLAY_CTR_LEN 8
/**
@@ -6112,7 +6334,7 @@ enum nl80211_feature_flags {
* request to use RRM (see %NL80211_ATTR_USE_RRM) with
* %NL80211_CMD_ASSOCIATE and %NL80211_CMD_CONNECT requests, which will set
* the ASSOC_REQ_USE_RRM flag in the association request even if
- * NL80211_FEATURE_QUIET is not advertized.
+ * NL80211_FEATURE_QUIET is not advertised.
* @NL80211_EXT_FEATURE_MU_MIMO_AIR_SNIFFER: This device supports MU-MIMO air
* sniffer which means that it can be configured to hear packets from
* certain groups which can be configured by the
@@ -6124,13 +6346,15 @@ enum nl80211_feature_flags {
* the BSS that the interface that requested the scan is connected to
* (if available).
* @NL80211_EXT_FEATURE_BSS_PARENT_TSF: Per BSS, this driver reports the
- * time the last beacon/probe was received. The time is the TSF of the
- * BSS that the interface that requested the scan is connected to
- * (if available).
+ * time the last beacon/probe was received. For a non-MLO connection, the
+ * time is the TSF of the BSS that the interface that requested the scan is
+ * connected to (if available). For an MLO connection, the time is the TSF
+ * of the BSS corresponding with link ID specified in the scan request (if
+ * specified).
* @NL80211_EXT_FEATURE_SET_SCAN_DWELL: This driver supports configuration of
* channel dwell time.
* @NL80211_EXT_FEATURE_BEACON_RATE_LEGACY: Driver supports beacon rate
- * configuration (AP/mesh), supporting a legacy (non HT/VHT) rate.
+ * configuration (AP/mesh), supporting a legacy (non-HT/VHT) rate.
* @NL80211_EXT_FEATURE_BEACON_RATE_HT: Driver supports beacon rate
* configuration (AP/mesh) with HT rates.
* @NL80211_EXT_FEATURE_BEACON_RATE_VHT: Driver supports beacon rate
@@ -6204,8 +6428,7 @@ enum nl80211_feature_flags {
* @NL80211_EXT_FEATURE_AP_PMKSA_CACHING: Driver/device supports PMKSA caching
* (set/del PMKSA operations) in AP mode.
*
- * @NL80211_EXT_FEATURE_SCHED_SCAN_BAND_SPECIFIC_RSSI_THOLD: Driver supports
- * filtering of sched scan results using band specific RSSI thresholds.
+ * @NL80211_EXT_FEATURE_SCHED_SCAN_BAND_SPECIFIC_RSSI_THOLD: Obsolete
*
* @NL80211_EXT_FEATURE_STA_TX_PWR: This driver supports controlling tx power
* to a station.
@@ -6294,6 +6517,31 @@ enum nl80211_feature_flags {
* might apply, e.g. no scans in progress, no offchannel operations
* in progress, and no active connections.
*
+ * @NL80211_EXT_FEATURE_PUNCT: Driver supports preamble puncturing in AP mode.
+ *
+ * @NL80211_EXT_FEATURE_SECURE_NAN: Device supports NAN Pairing which enables
+ * authentication, data encryption and message integrity.
+ *
+ * @NL80211_EXT_FEATURE_AUTH_AND_DEAUTH_RANDOM_TA: Device supports randomized TA
+ * in authentication and deauthentication frames sent to unassociated peer
+ * using @NL80211_CMD_FRAME.
+ *
+ * @NL80211_EXT_FEATURE_OWE_OFFLOAD: Driver/Device wants to do OWE DH IE
+ * handling in station mode.
+ *
+ * @NL80211_EXT_FEATURE_OWE_OFFLOAD_AP: Driver/Device wants to do OWE DH IE
+ * handling in AP mode.
+ *
+ * @NL80211_EXT_FEATURE_DFS_CONCURRENT: The device supports peer-to-peer or
+ * ad hoc operation on DFS channels under the control of a concurrent
+ * DFS master on the same channel as described in FCC-594280 D01
+ * (Section B.3). This, for example, allows P2P GO and P2P clients to
+ * operate on DFS channels as long as there's a concurrent BSS connection.
+ *
+ * @NL80211_EXT_FEATURE_SPP_AMSDU_SUPPORT: The driver has support for SPP
+ * (signaling and payload protected) A-MSDUs and this shall be advertised
+ * in the RSNXE.
+ *
* @NUM_NL80211_EXT_FEATURES: number of extended features.
* @MAX_NL80211_EXT_FEATURES: highest extended feature index.
*/
@@ -6335,7 +6583,7 @@ enum nl80211_ext_feature_index {
NL80211_EXT_FEATURE_ENABLE_FTM_RESPONDER,
NL80211_EXT_FEATURE_AIRTIME_FAIRNESS,
NL80211_EXT_FEATURE_AP_PMKSA_CACHING,
- NL80211_EXT_FEATURE_SCHED_SCAN_BAND_SPECIFIC_RSSI_THOLD,
+ NL80211_EXT_FEATURE_SCHED_SCAN_BAND_SPECIFIC_RSSI_THOLD, /* obsolete */
NL80211_EXT_FEATURE_EXT_KEY_ID,
NL80211_EXT_FEATURE_STA_TX_PWR,
NL80211_EXT_FEATURE_SAE_OFFLOAD,
@@ -6362,6 +6610,13 @@ enum nl80211_ext_feature_index {
NL80211_EXT_FEATURE_FILS_CRYPTO_OFFLOAD,
NL80211_EXT_FEATURE_RADAR_BACKGROUND,
NL80211_EXT_FEATURE_POWERED_ADDR_CHANGE,
+ NL80211_EXT_FEATURE_PUNCT,
+ NL80211_EXT_FEATURE_SECURE_NAN,
+ NL80211_EXT_FEATURE_AUTH_AND_DEAUTH_RANDOM_TA,
+ NL80211_EXT_FEATURE_OWE_OFFLOAD,
+ NL80211_EXT_FEATURE_OWE_OFFLOAD_AP,
+ NL80211_EXT_FEATURE_DFS_CONCURRENT,
+ NL80211_EXT_FEATURE_SPP_AMSDU_SUPPORT,
/* add new features before the definition below */
NUM_NL80211_EXT_FEATURES,
@@ -6446,7 +6701,7 @@ enum nl80211_timeout_reason {
* request parameters IE in the probe request
* @NL80211_SCAN_FLAG_ACCEPT_BCAST_PROBE_RESP: accept broadcast probe responses
* @NL80211_SCAN_FLAG_OCE_PROBE_REQ_HIGH_TX_RATE: send probe request frames at
- * rate of at least 5.5M. In case non OCE AP is discovered in the channel,
+ * rate of at least 5.5M. In case non-OCE AP is discovered in the channel,
* only the first probe req in the channel will be sent in high rate.
* @NL80211_SCAN_FLAG_OCE_PROBE_REQ_DEFERRAL_SUPPRESSION: allow probe request
* tx deferral (dot11FILSProbeDelay shall be set to 15ms)
@@ -6476,8 +6731,16 @@ enum nl80211_timeout_reason {
* @NL80211_SCAN_FLAG_FREQ_KHZ: report scan results with
* %NL80211_ATTR_SCAN_FREQ_KHZ. This also means
* %NL80211_ATTR_SCAN_FREQUENCIES will not be included.
- * @NL80211_SCAN_FLAG_COLOCATED_6GHZ: scan for colocated APs reported by
- * 2.4/5 GHz APs
+ * @NL80211_SCAN_FLAG_COLOCATED_6GHZ: scan for collocated APs reported by
+ * 2.4/5 GHz APs. When the flag is set, the scan logic will use the
+ * information from the RNR element found in beacons/probe responses
+ * received on the 2.4/5 GHz channels to actively scan only the 6GHz
+ * channels on which APs are expected to be found. Note that when not set,
+ * the scan logic would scan all 6GHz channels, but since transmission of
+ * probe requests on non-PSC channels is limited, it is highly likely that
+ * these channels would passively be scanned. Also note that when the flag
+ * is set, in addition to the colocated APs, PSC channels would also be
+ * scanned if the user space has asked for it.
*/
enum nl80211_scan_flags {
NL80211_SCAN_FLAG_LOW_PRIORITY = 1<<0,
@@ -6806,7 +7069,7 @@ enum nl80211_nan_func_term_reason {
* The instance ID for the follow up Service Discovery Frame. This is u8.
* @NL80211_NAN_FUNC_FOLLOW_UP_REQ_ID: relevant if the function's type
* is follow up. This is a u8.
- * The requestor instance ID for the follow up Service Discovery Frame.
+ * The requester instance ID for the follow up Service Discovery Frame.
* @NL80211_NAN_FUNC_FOLLOW_UP_DEST: the MAC address of the recipient of the
* follow up Service Discovery Frame. This is a binary attribute.
* @NL80211_NAN_FUNC_CLOSE_RANGE: is this function limited for devices in a
@@ -7196,7 +7459,7 @@ enum nl80211_peer_measurement_attrs {
* @NL80211_PMSR_FTM_CAPA_ATTR_TRIGGER_BASED: flag attribute indicating if
* trigger based ranging measurement is supported
* @NL80211_PMSR_FTM_CAPA_ATTR_NON_TRIGGER_BASED: flag attribute indicating
- * if non trigger based ranging measurement is supported
+ * if non-trigger-based ranging measurement is supported
*
* @NUM_NL80211_PMSR_FTM_CAPA_ATTR: internal
* @NL80211_PMSR_FTM_CAPA_ATTR_MAX: highest attribute number
@@ -7250,7 +7513,7 @@ enum nl80211_peer_measurement_ftm_capa {
* if neither %NL80211_PMSR_FTM_REQ_ATTR_TRIGGER_BASED nor
* %NL80211_PMSR_FTM_REQ_ATTR_NON_TRIGGER_BASED is set, EDCA based
* ranging will be used.
- * @NL80211_PMSR_FTM_REQ_ATTR_NON_TRIGGER_BASED: request non trigger based
+ * @NL80211_PMSR_FTM_REQ_ATTR_NON_TRIGGER_BASED: request non-trigger-based
* ranging measurement (flag)
* This attribute and %NL80211_PMSR_FTM_REQ_ATTR_TRIGGER_BASED are
* mutually exclusive.
@@ -7328,7 +7591,7 @@ enum nl80211_peer_measurement_ftm_failure_reasons {
* @NL80211_PMSR_FTM_RESP_ATTR_NUM_FTMR_ATTEMPTS: number of FTM Request frames
* transmitted (u32, optional)
* @NL80211_PMSR_FTM_RESP_ATTR_NUM_FTMR_SUCCESSES: number of FTM Request frames
- * that were acknowleged (u32, optional)
+ * that were acknowledged (u32, optional)
* @NL80211_PMSR_FTM_RESP_ATTR_BUSY_RETRY_TIME: retry time received from the
* busy peer (u32, seconds)
* @NL80211_PMSR_FTM_RESP_ATTR_NUM_BURSTS_EXP: actual number of bursts exponent
@@ -7489,7 +7752,7 @@ enum nl80211_iftype_akm_attributes {
* @NL80211_FILS_DISCOVERY_ATTR_INT_MIN: Minimum packet interval (u32, TU).
* Allowed range: 0..10000 (TU = Time Unit)
* @NL80211_FILS_DISCOVERY_ATTR_INT_MAX: Maximum packet interval (u32, TU).
- * Allowed range: 0..10000 (TU = Time Unit)
+ * Allowed range: 0..10000 (TU = Time Unit). If set to 0, the feature is disabled.
* @NL80211_FILS_DISCOVERY_ATTR_TMPL: Template data for FILS discovery action
* frame including the headers.
*
@@ -7522,7 +7785,8 @@ enum nl80211_fils_discovery_attributes {
*
* @NL80211_UNSOL_BCAST_PROBE_RESP_ATTR_INT: Maximum packet interval (u32, TU).
* Allowed range: 0..20 (TU = Time Unit). IEEE P802.11ax/D6.0
- * 26.17.2.3.2 (AP behavior for fast passive scanning).
+ * 26.17.2.3.2 (AP behavior for fast passive scanning). If set to 0, the feature is
+ * disabled.
* @NL80211_UNSOL_BCAST_PROBE_RESP_ATTR_TMPL: Unsolicited broadcast probe response
* frame template (binary).
*
diff --git a/src/basic/linux/pkt_sched.h b/src/basic/linux/pkt_sched.h
index 000eec1..a3cd0c2 100644
--- a/src/basic/linux/pkt_sched.h
+++ b/src/basic/linux/pkt_sched.h
@@ -477,115 +477,6 @@ enum {
#define TCA_HFSC_MAX (__TCA_HFSC_MAX - 1)
-
-/* CBQ section */
-
-#define TC_CBQ_MAXPRIO 8
-#define TC_CBQ_MAXLEVEL 8
-#define TC_CBQ_DEF_EWMA 5
-
-struct tc_cbq_lssopt {
- unsigned char change;
- unsigned char flags;
-#define TCF_CBQ_LSS_BOUNDED 1
-#define TCF_CBQ_LSS_ISOLATED 2
- unsigned char ewma_log;
- unsigned char level;
-#define TCF_CBQ_LSS_FLAGS 1
-#define TCF_CBQ_LSS_EWMA 2
-#define TCF_CBQ_LSS_MAXIDLE 4
-#define TCF_CBQ_LSS_MINIDLE 8
-#define TCF_CBQ_LSS_OFFTIME 0x10
-#define TCF_CBQ_LSS_AVPKT 0x20
- __u32 maxidle;
- __u32 minidle;
- __u32 offtime;
- __u32 avpkt;
-};
-
-struct tc_cbq_wrropt {
- unsigned char flags;
- unsigned char priority;
- unsigned char cpriority;
- unsigned char __reserved;
- __u32 allot;
- __u32 weight;
-};
-
-struct tc_cbq_ovl {
- unsigned char strategy;
-#define TC_CBQ_OVL_CLASSIC 0
-#define TC_CBQ_OVL_DELAY 1
-#define TC_CBQ_OVL_LOWPRIO 2
-#define TC_CBQ_OVL_DROP 3
-#define TC_CBQ_OVL_RCLASSIC 4
- unsigned char priority2;
- __u16 pad;
- __u32 penalty;
-};
-
-struct tc_cbq_police {
- unsigned char police;
- unsigned char __res1;
- unsigned short __res2;
-};
-
-struct tc_cbq_fopt {
- __u32 split;
- __u32 defmap;
- __u32 defchange;
-};
-
-struct tc_cbq_xstats {
- __u32 borrows;
- __u32 overactions;
- __s32 avgidle;
- __s32 undertime;
-};
-
-enum {
- TCA_CBQ_UNSPEC,
- TCA_CBQ_LSSOPT,
- TCA_CBQ_WRROPT,
- TCA_CBQ_FOPT,
- TCA_CBQ_OVL_STRATEGY,
- TCA_CBQ_RATE,
- TCA_CBQ_RTAB,
- TCA_CBQ_POLICE,
- __TCA_CBQ_MAX,
-};
-
-#define TCA_CBQ_MAX (__TCA_CBQ_MAX - 1)
-
-/* dsmark section */
-
-enum {
- TCA_DSMARK_UNSPEC,
- TCA_DSMARK_INDICES,
- TCA_DSMARK_DEFAULT_INDEX,
- TCA_DSMARK_SET_TC_INDEX,
- TCA_DSMARK_MASK,
- TCA_DSMARK_VALUE,
- __TCA_DSMARK_MAX,
-};
-
-#define TCA_DSMARK_MAX (__TCA_DSMARK_MAX - 1)
-
-/* ATM section */
-
-enum {
- TCA_ATM_UNSPEC,
- TCA_ATM_FD, /* file/socket descriptor */
- TCA_ATM_PTR, /* pointer to descriptor - later */
- TCA_ATM_HDR, /* LL header */
- TCA_ATM_EXCESS, /* excess traffic class (0 for CLP) */
- TCA_ATM_ADDR, /* PVC address (for output only) */
- TCA_ATM_STATE, /* VC state (ATM_VS_*; for output only) */
- __TCA_ATM_MAX,
-};
-
-#define TCA_ATM_MAX (__TCA_ATM_MAX - 1)
-
/* Network emulator */
enum {
@@ -603,6 +494,7 @@ enum {
TCA_NETEM_JITTER64,
TCA_NETEM_SLOT,
TCA_NETEM_SLOT_DIST,
+ TCA_NETEM_PRNG_SEED,
__TCA_NETEM_MAX,
};
@@ -719,6 +611,11 @@ enum {
#define __TC_MQPRIO_SHAPER_MAX (__TC_MQPRIO_SHAPER_MAX - 1)
+enum {
+ TC_FP_EXPRESS = 1,
+ TC_FP_PREEMPTIBLE = 2,
+};
+
struct tc_mqprio_qopt {
__u8 num_tc;
__u8 prio_tc_map[TC_QOPT_BITMASK + 1];
@@ -733,11 +630,22 @@ struct tc_mqprio_qopt {
#define TC_MQPRIO_F_MAX_RATE 0x8
enum {
+ TCA_MQPRIO_TC_ENTRY_UNSPEC,
+ TCA_MQPRIO_TC_ENTRY_INDEX, /* u32 */
+ TCA_MQPRIO_TC_ENTRY_FP, /* u32 */
+
+ /* add new constants above here */
+ __TCA_MQPRIO_TC_ENTRY_CNT,
+ TCA_MQPRIO_TC_ENTRY_MAX = (__TCA_MQPRIO_TC_ENTRY_CNT - 1)
+};
+
+enum {
TCA_MQPRIO_UNSPEC,
TCA_MQPRIO_MODE,
TCA_MQPRIO_SHAPER,
TCA_MQPRIO_MIN_RATE64,
TCA_MQPRIO_MAX_RATE64,
+ TCA_MQPRIO_TC_ENTRY,
__TCA_MQPRIO_MAX,
};
@@ -924,15 +832,22 @@ enum {
TCA_FQ_HORIZON_DROP, /* drop packets beyond horizon, or cap their EDT */
+ TCA_FQ_PRIOMAP, /* prio2band */
+
+ TCA_FQ_WEIGHTS, /* Weights for each band */
+
__TCA_FQ_MAX
};
#define TCA_FQ_MAX (__TCA_FQ_MAX - 1)
+#define FQ_BANDS 3
+#define FQ_MIN_WEIGHT 16384
+
struct tc_fq_qd_stats {
__u64 gc_flows;
- __u64 highprio_packets;
- __u64 tcp_retrans;
+ __u64 highprio_packets; /* obsolete */
+ __u64 tcp_retrans; /* obsolete */
__u64 throttled;
__u64 flows_plimit;
__u64 pkts_too_long;
@@ -945,6 +860,10 @@ struct tc_fq_qd_stats {
__u64 ce_mark; /* packets above ce_threshold */
__u64 horizon_drops;
__u64 horizon_caps;
+ __u64 fastpath_packets;
+ __u64 band_drops[FQ_BANDS];
+ __u32 band_pkt_count[FQ_BANDS];
+ __u32 pad;
};
/* Heavy-Hitter Filter */
@@ -1236,6 +1155,7 @@ enum {
TCA_TAPRIO_TC_ENTRY_UNSPEC,
TCA_TAPRIO_TC_ENTRY_INDEX, /* u32 */
TCA_TAPRIO_TC_ENTRY_MAX_SDU, /* u32 */
+ TCA_TAPRIO_TC_ENTRY_FP, /* u32 */
/* add new constants above here */
__TCA_TAPRIO_TC_ENTRY_CNT,
@@ -1243,6 +1163,16 @@ enum {
};
enum {
+ TCA_TAPRIO_OFFLOAD_STATS_PAD = 1, /* u64 */
+ TCA_TAPRIO_OFFLOAD_STATS_WINDOW_DROPS, /* u64 */
+ TCA_TAPRIO_OFFLOAD_STATS_TX_OVERRUNS, /* u64 */
+
+ /* add new constants above here */
+ __TCA_TAPRIO_OFFLOAD_STATS_CNT,
+ TCA_TAPRIO_OFFLOAD_STATS_MAX = (__TCA_TAPRIO_OFFLOAD_STATS_CNT - 1)
+};
+
+enum {
TCA_TAPRIO_ATTR_UNSPEC,
TCA_TAPRIO_ATTR_PRIOMAP, /* struct tc_mqprio_qopt */
TCA_TAPRIO_ATTR_SCHED_ENTRY_LIST, /* nested of entry */
diff --git a/src/basic/linux/rtnetlink.h b/src/basic/linux/rtnetlink.h
index eb2747d..3b687d2 100644
--- a/src/basic/linux/rtnetlink.h
+++ b/src/basic/linux/rtnetlink.h
@@ -502,13 +502,17 @@ enum {
#define RTAX_MAX (__RTAX_MAX - 1)
-#define RTAX_FEATURE_ECN (1 << 0)
-#define RTAX_FEATURE_SACK (1 << 1)
-#define RTAX_FEATURE_TIMESTAMP (1 << 2)
-#define RTAX_FEATURE_ALLFRAG (1 << 3)
-
-#define RTAX_FEATURE_MASK (RTAX_FEATURE_ECN | RTAX_FEATURE_SACK | \
- RTAX_FEATURE_TIMESTAMP | RTAX_FEATURE_ALLFRAG)
+#define RTAX_FEATURE_ECN (1 << 0)
+#define RTAX_FEATURE_SACK (1 << 1) /* unused */
+#define RTAX_FEATURE_TIMESTAMP (1 << 2) /* unused */
+#define RTAX_FEATURE_ALLFRAG (1 << 3) /* unused */
+#define RTAX_FEATURE_TCP_USEC_TS (1 << 4)
+
+#define RTAX_FEATURE_MASK (RTAX_FEATURE_ECN | \
+ RTAX_FEATURE_SACK | \
+ RTAX_FEATURE_TIMESTAMP | \
+ RTAX_FEATURE_ALLFRAG | \
+ RTAX_FEATURE_TCP_USEC_TS)
struct rta_session {
__u8 proto;
@@ -635,6 +639,7 @@ enum {
TCA_INGRESS_BLOCK,
TCA_EGRESS_BLOCK,
TCA_DUMP_FLAGS,
+ TCA_EXT_WARN_MSG,
__TCA_MAX
};
@@ -788,6 +793,7 @@ enum {
TCA_ROOT_FLAGS,
TCA_ROOT_COUNT,
TCA_ROOT_TIME_DELTA, /* in msecs */
+ TCA_ROOT_EXT_WARN_MSG,
__TCA_ROOT_MAX,
#define TCA_ROOT_MAX (__TCA_ROOT_MAX - 1)
};
diff --git a/src/basic/linux/stddef.h b/src/basic/linux/stddef.h
index 1a73963..b888a83 100644
--- a/src/basic/linux/stddef.h
+++ b/src/basic/linux/stddef.h
@@ -26,8 +26,13 @@
union { \
struct { MEMBERS } ATTRS; \
struct TAG { MEMBERS } ATTRS NAME; \
- }
+ } ATTRS
+#ifdef __cplusplus
+/* sizeof(struct{}) is 1 in C++, not 0, can't use C version of the macro. */
+#define __DECLARE_FLEX_ARRAY(T, member) \
+ T member[0]
+#else
/**
* __DECLARE_FLEX_ARRAY() - Declare a flexible array usable in a union
*
@@ -44,3 +49,9 @@
TYPE NAME[]; \
}
#endif
+
+#ifndef __counted_by
+#define __counted_by(m)
+#endif
+
+#endif /* _UAPI_LINUX_STDDEF_H */
diff --git a/src/basic/locale-util.c b/src/basic/locale-util.c
index d3fef01..2356527 100644
--- a/src/basic/locale-util.c
+++ b/src/basic/locale-util.c
@@ -221,7 +221,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 */
@@ -260,7 +260,10 @@ bool locale_is_valid(const char *name) {
if (!filename_is_valid(name))
return false;
- if (!string_is_safe(name))
+ /* Locales look like: ll_CC.ENC@variant, where ll and CC are alphabetic, ENC is alphanumeric with
+ * dashes, and variant seems to be alphabetic.
+ * See: https://www.gnu.org/software/gettext/manual/html_node/Locale-Names.html */
+ if (!in_charset(name, ALPHANUMERICAL "_.-@"))
return false;
return true;
@@ -292,7 +295,7 @@ bool is_locale_utf8(void) {
if (cached_answer >= 0)
goto out;
- r = getenv_bool_secure("SYSTEMD_UTF8");
+ r = secure_getenv_bool("SYSTEMD_UTF8");
if (r >= 0) {
cached_answer = r;
goto out;
diff --git a/src/basic/lock-util.c b/src/basic/lock-util.c
index 7bffe85..aef395d 100644
--- a/src/basic/lock-util.c
+++ b/src/basic/lock-util.c
@@ -139,7 +139,14 @@ static int fcntl_lock(int fd, int operation, bool ofd) {
.l_len = 0,
}));
- if (r == -EACCES) /* Treat EACCESS/EAGAIN the same as per man page. */
+ /* If we are doing non-blocking operations, treat EACCES/EAGAIN the same as per man page. But if
+ * not, propagate EACCES back, as it will likely be due to an LSM denying the operation (for example
+ * LXC with AppArmor when running on kernel < 6.2), and in some cases we want to gracefully
+ * fallback (e.g.: PrivateNetwork=yes). As per documentation, it's only the non-blocking operation
+ * F_SETLK that might return EACCES on some platforms (although the Linux implementation doesn't
+ * seem to), as F_SETLKW and F_OFD_SETLKW block so this is not an issue, and F_OFD_SETLK is documented
+ * to only return EAGAIN if the lock is already held. */
+ if ((operation & LOCK_NB) && r == -EACCES)
r = -EAGAIN;
return r;
diff --git a/src/basic/lock-util.h b/src/basic/lock-util.h
index 91b332f..8fb4757 100644
--- a/src/basic/lock-util.h
+++ b/src/basic/lock-util.h
@@ -17,7 +17,7 @@ static inline int make_lock_file(const char *p, int operation, LockFile *ret) {
int make_lock_file_for(const char *p, int operation, LockFile *ret);
void release_lock_file(LockFile *f);
-#define LOCK_FILE_INIT { .dir_fd = -EBADF, .fd = -EBADF }
+#define LOCK_FILE_INIT (LockFile) { .dir_fd = -EBADF, .fd = -EBADF }
/* POSIX locks with the same interface as flock(). */
int posix_lock(int fd, int operation);
diff --git a/src/basic/log.c b/src/basic/log.c
index 7a44300..13ad19a 100644
--- a/src/basic/log.c
+++ b/src/basic/log.c
@@ -49,6 +49,12 @@ static void *log_syntax_callback_userdata = NULL;
static LogTarget log_target = LOG_TARGET_CONSOLE;
static int log_max_level = LOG_INFO;
+static int log_target_max_level[] = {
+ [LOG_TARGET_CONSOLE] = INT_MAX,
+ [LOG_TARGET_KMSG] = INT_MAX,
+ [LOG_TARGET_SYSLOG] = INT_MAX,
+ [LOG_TARGET_JOURNAL] = INT_MAX,
+};
static int log_facility = LOG_DAEMON;
static bool ratelimit_kmsg = true;
@@ -69,6 +75,7 @@ static bool upgrade_syslog_to_journal = false;
static bool always_reopen_console = false;
static bool open_when_needed = false;
static bool prohibit_ipc = false;
+static bool assert_return_is_critical = BUILD_MODE_DEVELOPER;
/* Akin to glibc's __abort_msg; which is private and we hence cannot
* use here. */
@@ -249,7 +256,7 @@ fail:
return r;
}
-static bool stderr_is_journal(void) {
+bool stderr_is_journal(void) {
_cleanup_free_ char *w = NULL;
const char *e;
uint64_t dev, ino;
@@ -389,7 +396,7 @@ void log_forget_fds(void) {
}
void log_set_max_level(int level) {
- assert(level == LOG_NULL || (level & LOG_PRIMASK) == level);
+ assert(level == LOG_NULL || LOG_PRI(level) == level);
log_max_level = level;
@@ -414,7 +421,7 @@ static bool check_console_fd_is_tty(void) {
return false;
if (console_fd_is_tty < 0)
- console_fd_is_tty = isatty(console_fd) > 0;
+ console_fd_is_tty = isatty_safe(console_fd);
return console_fd_is_tty;
}
@@ -443,6 +450,9 @@ static int write_to_console(
if (dumb < 0)
dumb = getenv_terminal_is_dumb();
+ if (LOG_PRI(level) > log_target_max_level[LOG_TARGET_CONSOLE])
+ return 0;
+
if (log_target == LOG_TARGET_CONSOLE_PREFIXED) {
xsprintf(prefix, "<%i>", level);
iovec[n++] = IOVEC_MAKE_STRING(prefix);
@@ -528,6 +538,9 @@ static int write_to_syslog(
if (syslog_fd < 0)
return 0;
+ if (LOG_PRI(level) > log_target_max_level[LOG_TARGET_SYSLOG])
+ return 0;
+
xsprintf(header_priority, "<%i>", level);
t = (time_t) (now(CLOCK_REALTIME) / USEC_PER_SEC);
@@ -597,6 +610,9 @@ static int write_to_kmsg(
if (kmsg_fd < 0)
return 0;
+ if (LOG_PRI(level) > log_target_max_level[LOG_TARGET_KMSG])
+ return 0;
+
if (ratelimit_kmsg && !ratelimit_below(&ratelimit)) {
if (ratelimit_num_dropped(&ratelimit) > 1)
return 0;
@@ -724,6 +740,9 @@ static int write_to_journal(
if (journal_fd < 0)
return 0;
+ if (LOG_PRI(level) > log_target_max_level[LOG_TARGET_JOURNAL])
+ return 0;
+
iovec_len = MIN(6 + _log_context_num_fields * 2, IOVEC_MAX);
iovec = newa(struct iovec, iovec_len);
@@ -769,7 +788,7 @@ int log_dispatch_internal(
return -ERRNO_VALUE(error);
/* Patch in LOG_DAEMON facility if necessary */
- if ((level & LOG_FACMASK) == 0)
+ if (LOG_FAC(level) == 0)
level |= log_facility;
if (open_when_needed)
@@ -987,9 +1006,13 @@ void log_assert_failed_return(
const char *file,
int line,
const char *func) {
+
+ if (assert_return_is_critical)
+ log_assert_failed(text, file, line, func);
+
PROTECT_ERRNO;
log_assert(LOG_DEBUG, text, file, line, func,
- "Assertion '%s' failed at %s:%u, function %s(). Ignoring.");
+ "Assertion '%s' failed at %s:%u, function %s(), ignoring.");
}
int log_oom_internal(int level, const char *file, int line, const char *func) {
@@ -1054,7 +1077,7 @@ int log_struct_internal(
log_target == LOG_TARGET_NULL)
return -ERRNO_VALUE(error);
- if ((level & LOG_FACMASK) == 0)
+ if (LOG_FAC(level) == 0)
level |= log_facility;
if (IN_SET(log_target,
@@ -1157,7 +1180,7 @@ int log_struct_iovec_internal(
log_target == LOG_TARGET_NULL)
return -ERRNO_VALUE(error);
- if ((level & LOG_FACMASK) == 0)
+ if (LOG_FAC(level) == 0)
level |= log_facility;
if (IN_SET(log_target, LOG_TARGET_AUTO,
@@ -1219,11 +1242,74 @@ int log_set_target_from_string(const char *e) {
int log_set_max_level_from_string(const char *e) {
int r;
- r = log_level_from_string(e);
+ for (;;) {
+ _cleanup_free_ char *word = NULL, *prefix = NULL;
+ LogTarget target;
+ const char *colon;
+
+ r = extract_first_word(&e, &word, ",", 0);
+ if (r < 0)
+ return r;
+ if (r == 0)
+ break;
+
+ colon = strchr(word, ':');
+ if (!colon) {
+ r = log_level_from_string(word);
+ if (r < 0)
+ return r;
+
+ log_set_max_level(r);
+ continue;
+ }
+
+ prefix = strndup(word, colon - word);
+ if (!prefix)
+ return -ENOMEM;
+
+ target = log_target_from_string(prefix);
+ if (target < 0)
+ return target;
+
+ if (target >= _LOG_TARGET_SINGLE_MAX)
+ return -EINVAL;
+
+ r = log_level_from_string(colon + 1);
+ if (r < 0)
+ return r;
+
+ log_target_max_level[target] = r;
+ }
+
+ return 0;
+}
+
+int log_max_levels_to_string(int level, char **ret) {
+ _cleanup_free_ char *s = NULL;
+ int r;
+
+ assert(ret);
+
+ r = log_level_to_string_alloc(level, &s);
if (r < 0)
return r;
- log_set_max_level(r);
+ for (LogTarget target = 0; target < _LOG_TARGET_SINGLE_MAX; target++) {
+ _cleanup_free_ char *l = NULL;
+
+ if (log_target_max_level[target] == INT_MAX)
+ continue;
+
+ r = log_level_to_string_alloc(log_target_max_level[target], &l);
+ if (r < 0)
+ return r;
+
+ r = strextendf_with_separator(&s, ",", "%s:%s", log_target_to_string(target), l);
+ if (r < 0)
+ return r;
+ }
+
+ *ret = TAKE_PTR(s);
return 0;
}
@@ -1238,6 +1324,14 @@ static int log_set_ratelimit_kmsg_from_string(const char *e) {
return 0;
}
+void log_set_assert_return_is_critical(bool b) {
+ assert_return_is_critical = b;
+}
+
+bool log_get_assert_return_is_critical(void) {
+ return assert_return_is_critical;
+}
+
static int parse_proc_cmdline_item(const char *key, const char *value, void *data) {
/*
@@ -1258,7 +1352,7 @@ static int parse_proc_cmdline_item(const char *key, const char *value, void *dat
return 0;
if (log_set_target_from_string(value) < 0)
- log_warning("Failed to parse log target '%s'. Ignoring.", value);
+ log_warning("Failed to parse log target '%s', ignoring.", value);
} else if (proc_cmdline_key_streq(key, "systemd.log_level")) {
@@ -1266,32 +1360,32 @@ static int parse_proc_cmdline_item(const char *key, const char *value, void *dat
return 0;
if (log_set_max_level_from_string(value) < 0)
- log_warning("Failed to parse log level '%s'. Ignoring.", value);
+ log_warning("Failed to parse log level setting '%s', ignoring.", value);
} else if (proc_cmdline_key_streq(key, "systemd.log_color")) {
if (log_show_color_from_string(value ?: "1") < 0)
- log_warning("Failed to parse log color setting '%s'. Ignoring.", value);
+ log_warning("Failed to parse log color setting '%s', ignoring.", value);
} else if (proc_cmdline_key_streq(key, "systemd.log_location")) {
if (log_show_location_from_string(value ?: "1") < 0)
- log_warning("Failed to parse log location setting '%s'. Ignoring.", value);
+ log_warning("Failed to parse log location setting '%s', ignoring.", value);
} else if (proc_cmdline_key_streq(key, "systemd.log_tid")) {
if (log_show_tid_from_string(value ?: "1") < 0)
- log_warning("Failed to parse log tid setting '%s'. Ignoring.", value);
+ log_warning("Failed to parse log tid setting '%s', ignoring.", value);
} else if (proc_cmdline_key_streq(key, "systemd.log_time")) {
if (log_show_time_from_string(value ?: "1") < 0)
- log_warning("Failed to parse log time setting '%s'. Ignoring.", value);
+ log_warning("Failed to parse log time setting '%s', ignoring.", value);
} else if (proc_cmdline_key_streq(key, "systemd.log_ratelimit_kmsg")) {
if (log_set_ratelimit_kmsg_from_string(value ?: "1") < 0)
- log_warning("Failed to parse log ratelimit kmsg boolean '%s'. Ignoring.", value);
+ log_warning("Failed to parse log ratelimit kmsg boolean '%s', ignoring.", value);
}
return 0;
@@ -1311,31 +1405,31 @@ void log_parse_environment_variables(void) {
e = getenv("SYSTEMD_LOG_TARGET");
if (e && log_set_target_from_string(e) < 0)
- log_warning("Failed to parse log target '%s'. Ignoring.", e);
+ log_warning("Failed to parse log target '%s', ignoring.", e);
e = getenv("SYSTEMD_LOG_LEVEL");
if (e && log_set_max_level_from_string(e) < 0)
- log_warning("Failed to parse log level '%s'. Ignoring.", e);
+ log_warning("Failed to parse log level '%s', ignoring.", e);
e = getenv("SYSTEMD_LOG_COLOR");
if (e && log_show_color_from_string(e) < 0)
- log_warning("Failed to parse log color '%s'. Ignoring.", e);
+ log_warning("Failed to parse log color '%s', ignoring.", e);
e = getenv("SYSTEMD_LOG_LOCATION");
if (e && log_show_location_from_string(e) < 0)
- log_warning("Failed to parse log location '%s'. Ignoring.", e);
+ log_warning("Failed to parse log location '%s', ignoring.", e);
e = getenv("SYSTEMD_LOG_TIME");
if (e && log_show_time_from_string(e) < 0)
- log_warning("Failed to parse log time '%s'. Ignoring.", e);
+ log_warning("Failed to parse log time '%s', ignoring.", e);
e = getenv("SYSTEMD_LOG_TID");
if (e && log_show_tid_from_string(e) < 0)
- log_warning("Failed to parse log tid '%s'. Ignoring.", e);
+ log_warning("Failed to parse log tid '%s', ignoring.", e);
e = getenv("SYSTEMD_LOG_RATELIMIT_KMSG");
if (e && log_set_ratelimit_kmsg_from_string(e) < 0)
- log_warning("Failed to parse log ratelimit kmsg boolean '%s'. Ignoring.", e);
+ log_warning("Failed to parse log ratelimit kmsg boolean '%s', ignoring.", e);
}
void log_parse_environment(void) {
@@ -1667,7 +1761,7 @@ bool log_context_enabled(void) {
if (saved_log_context_enabled >= 0)
return saved_log_context_enabled;
- r = getenv_bool_secure("SYSTEMD_ENABLE_LOG_CONTEXT");
+ r = secure_getenv_bool("SYSTEMD_ENABLE_LOG_CONTEXT");
if (r < 0 && r != -ENXIO)
log_debug_errno(r, "Failed to parse $SYSTEMD_ENABLE_LOG_CONTEXT, ignoring: %m");
diff --git a/src/basic/log.h b/src/basic/log.h
index 9008d47..726f035 100644
--- a/src/basic/log.h
+++ b/src/basic/log.h
@@ -18,20 +18,21 @@ struct signalfd_siginfo;
typedef enum LogTarget{
LOG_TARGET_CONSOLE,
- LOG_TARGET_CONSOLE_PREFIXED,
LOG_TARGET_KMSG,
LOG_TARGET_JOURNAL,
- LOG_TARGET_JOURNAL_OR_KMSG,
LOG_TARGET_SYSLOG,
+ LOG_TARGET_CONSOLE_PREFIXED,
+ LOG_TARGET_JOURNAL_OR_KMSG,
LOG_TARGET_SYSLOG_OR_KMSG,
LOG_TARGET_AUTO, /* console if stderr is not journal, JOURNAL_OR_KMSG otherwise */
LOG_TARGET_NULL,
- _LOG_TARGET_MAX,
+ _LOG_TARGET_SINGLE_MAX = LOG_TARGET_SYSLOG + 1,
+ _LOG_TARGET_MAX = LOG_TARGET_NULL + 1,
_LOG_TARGET_INVALID = -EINVAL,
} LogTarget;
/* This log level disables logging completely. It can only be passed to log_set_max_level() and cannot be
- * used a regular log level. */
+ * used as a regular log level. */
#define LOG_NULL (LOG_EMERG - 1)
/* Note to readers: << and >> have lower precedence (are evaluated earlier) than & and | */
@@ -59,6 +60,7 @@ void log_settle_target(void);
void log_set_max_level(int level);
int log_set_max_level_from_string(const char *e);
int log_get_max_level(void) _pure_;
+int log_max_levels_to_string(int level, char **ret);
void log_set_facility(int facility);
@@ -83,6 +85,7 @@ int log_show_tid_from_string(const char *e);
assert_cc(STRLEN(__FILE__) > STRLEN(RELATIVE_SOURCE_PATH) + 1);
#define PROJECT_FILE (&__FILE__[STRLEN(RELATIVE_SOURCE_PATH) + 1])
+bool stderr_is_journal(void);
int log_open(void);
void log_close(void);
void log_forget_fds(void);
@@ -331,6 +334,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);
int log_syntax_internal(
@@ -380,7 +386,7 @@ typedef struct LogRateLimit {
RateLimit ratelimit;
} LogRateLimit;
-#define log_ratelimit_internal(_level, _error, _ratelimit, _format, _file, _line, _func, ...) \
+#define log_ratelimit_internal(_level, _error, _ratelimit, _file, _line, _func, _format, ...) \
({ \
int _log_ratelimit_error = (_error); \
int _log_ratelimit_level = (_level); \
@@ -404,7 +410,7 @@ typedef struct LogRateLimit {
({ \
int _level = (level), _e = (error); \
_e = (log_get_max_level() >= LOG_PRI(_level)) \
- ? log_ratelimit_internal(_level, _e, _ratelimit, format, PROJECT_FILE, __LINE__, __func__, ##__VA_ARGS__) \
+ ? log_ratelimit_internal(_level, _e, _ratelimit, PROJECT_FILE, __LINE__, __func__, format, ##__VA_ARGS__) \
: -ERRNO_VALUE(_e); \
_e < 0 ? _e : -ESTRPIPE; \
})
diff --git a/src/basic/macro.h b/src/basic/macro.h
index d63aa81..19d5039 100644
--- a/src/basic/macro.h
+++ b/src/basic/macro.h
@@ -266,12 +266,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); \
@@ -281,6 +275,9 @@ 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 FOREACH_ELEMENT(i, array) \
+ FOREACH_ARRAY(i, array, ELEMENTSOF(array))
+
#define _DEFINE_TRIVIAL_REF_FUNC(type, name, scope) \
scope type *name##_ref(type *p) { \
if (!p) \
@@ -380,13 +377,26 @@ 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), UNIQ_T(_va_sentinel_, UNIQ), ##__VA_ARGS__)
-#define _VA_ARGS_FOREACH(entry, _entries_, _current_, _va_sentinel_, ...) \
+#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_++)
+#define DECIMAL_STR_FMT(x) _Generic((x), \
+ char: "%c", \
+ bool: "%d", \
+ unsigned char: "%d", \
+ short: "%hd", \
+ unsigned short: "%hu", \
+ int: "%d", \
+ unsigned: "%u", \
+ long: "%ld", \
+ unsigned long: "%lu", \
+ long long: "%lld", \
+ unsigned long long: "%llu")
+
#include "log.h"
diff --git a/src/basic/memory-util.c b/src/basic/memory-util.c
index fcedae2..ed6024f 100644
--- a/src/basic/memory-util.c
+++ b/src/basic/memory-util.c
@@ -39,3 +39,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/basic/memory-util.h b/src/basic/memory-util.h
index 1179513..294aed6 100644
--- a/src/basic/memory-util.h
+++ b/src/basic/memory-util.h
@@ -107,3 +107,6 @@ static inline void erase_and_freep(void *p) {
static inline void erase_char(char *p) {
explicit_bzero_safe(p, sizeof(char));
}
+
+/* Makes a copy of the buffer with reversed order of bytes */
+void *memdup_reverse(const void *mem, size_t size);
diff --git a/src/basic/meson.build b/src/basic/meson.build
index 111253e..9a21457 100644
--- a/src/basic/meson.build
+++ b/src/basic/meson.build
@@ -10,16 +10,19 @@ basic_sources = files(
'audit-util.c',
'btrfs.c',
'build.c',
+ 'build-path.c',
'bus-label.c',
'cap-list.c',
'capability-util.c',
'cgroup-util.c',
'chase.c',
'chattr-util.c',
+ 'compress.c',
'conf-files.c',
'confidential-virt.c',
'devnum-util.c',
'dirent-util.c',
+ 'dlfcn-util.c',
'efivars.c',
'env-file.c',
'env-util.c',
@@ -32,6 +35,7 @@ basic_sources = files(
'filesystems.c',
'format-util.c',
'fs-util.c',
+ 'gcrypt-util.c',
'glob-util.c',
'glyph-util.c',
'gunicode.c',
@@ -53,6 +57,7 @@ basic_sources = files(
'lock-util.c',
'log.c',
'login-util.c',
+ 'keyring-util.c',
'memfd-util.c',
'memory-util.c',
'mempool.c',
@@ -79,6 +84,7 @@ basic_sources = files(
'replace-var.c',
'rlimit-util.c',
'runtime-scope.c',
+ 'sha256.c',
'sigbus.c',
'signal-util.c',
'siphash24.c',
@@ -96,7 +102,7 @@ basic_sources = files(
'terminal-util.c',
'time-util.c',
'tmpfile-util.c',
- 'uid-alloc-range.c',
+ 'uid-classification.c',
'uid-range.c',
'unit-def.c',
'unit-file.c',
@@ -229,8 +235,10 @@ run_target(
############################################################
-filesystem_includes = ['linux/magic.h',
- 'linux/gfs2_ondisk.h']
+filesystem_includes = files(
+ 'linux/magic.h',
+ 'missing_magic.h',
+)
check_filesystems = find_program('check-filesystems.sh')
r = run_command([check_filesystems, cpp, files('filesystems-gperf.gperf')] + filesystem_includes, check: false)
@@ -272,45 +280,14 @@ libbasic = static_library(
fundamental_sources,
include_directories : basic_includes,
dependencies : [libcap,
+ libdl,
+ libgcrypt_cflags,
+ liblz4_cflags,
libm,
librt,
+ libxz_cflags,
+ libzstd_cflags,
threads,
userspace],
c_args : ['-fvisibility=default'],
build_by_default : false)
-
-############################################################
-
-basic_gcrypt_sources = files(
- 'gcrypt-util.c',
-)
-
-# A convenience library that is separate from libbasic to avoid
-# unnecessary linking to libgcrypt.
-libbasic_gcrypt = static_library(
- 'basic-gcrypt',
- basic_gcrypt_sources,
- include_directories : basic_includes,
- dependencies : [libgcrypt,
- userspace],
- c_args : ['-fvisibility=default'],
- build_by_default : false)
-
-############################################################
-
-basic_compress_sources = files(
- 'compress.c',
-)
-
-# A convenience library that is separate from libbasic to avoid unnecessary
-# linking to the compression libraries.
-libbasic_compress = static_library(
- 'basic-compress',
- basic_compress_sources,
- include_directories : basic_includes,
- dependencies : [liblz4,
- libxz,
- libzstd,
- userspace],
- c_args : ['-fvisibility=default'],
- build_by_default : false)
diff --git a/src/basic/missing_audit.h b/src/basic/missing_audit.h
index 62e3c29..3f72acf 100644
--- a/src/basic/missing_audit.h
+++ b/src/basic/missing_audit.h
@@ -4,21 +4,31 @@
#include <linux/audit.h>
#if HAVE_AUDIT
-#include <libaudit.h>
+# include <libaudit.h>
#endif
#ifndef AUDIT_SERVICE_START
-#define AUDIT_SERVICE_START 1130 /* Service (daemon) start */
+# define AUDIT_SERVICE_START 1130 /* Service (daemon) start */
+#else
+assert_cc(AUDIT_SERVICE_START == 1130);
#endif
#ifndef AUDIT_SERVICE_STOP
-#define AUDIT_SERVICE_STOP 1131 /* Service (daemon) stop */
+# define AUDIT_SERVICE_STOP 1131 /* Service (daemon) stop */
+#else
+assert_cc(AUDIT_SERVICE_STOP == 1131);
#endif
#ifndef MAX_AUDIT_MESSAGE_LENGTH
-#define MAX_AUDIT_MESSAGE_LENGTH 8970
+# define MAX_AUDIT_MESSAGE_LENGTH 8970
+#else
+assert_cc(MAX_AUDIT_MESSAGE_LENGTH == 8970);
#endif
+/* Note: we check for AUDIT_NLGRP_MAX because it's a define, but we actually
+ * need AUDIT_NLGRP_READLOG which is an enum. */
#ifndef AUDIT_NLGRP_MAX
-#define AUDIT_NLGRP_READLOG 1
+# define AUDIT_NLGRP_READLOG 1
+#else
+assert_cc(AUDIT_NLGRP_READLOG == 1);
#endif
diff --git a/src/basic/missing_capability.h b/src/basic/missing_capability.h
index 5adda55..c1c63a6 100644
--- a/src/basic/missing_capability.h
+++ b/src/basic/missing_capability.h
@@ -6,21 +6,29 @@
/* 3a101b8de0d39403b2c7e5c23fd0b005668acf48 (3.16) */
#ifndef CAP_AUDIT_READ
# define CAP_AUDIT_READ 37
+#else
+assert_cc(CAP_AUDIT_READ == 37);
#endif
/* 980737282232b752bb14dab96d77665c15889c36 (5.8) */
#ifndef CAP_PERFMON
# define CAP_PERFMON 38
+#else
+assert_cc(CAP_PERFMON == 38);
#endif
/* a17b53c4a4b55ec322c132b6670743612229ee9c (5.8) */
#ifndef CAP_BPF
# define CAP_BPF 39
+#else
+assert_cc(CAP_BPF == 39);
#endif
/* 124ea650d3072b005457faed69909221c2905a1f (5.9) */
#ifndef CAP_CHECKPOINT_RESTORE
# define CAP_CHECKPOINT_RESTORE 40
+#else
+assert_cc(CAP_CHECKPOINT_RESTORE == 40);
#endif
#define SYSTEMD_CAP_LAST_CAP CAP_CHECKPOINT_RESTORE
@@ -34,6 +42,7 @@
# undef CAP_LAST_CAP
# endif
#endif
+
#ifndef CAP_LAST_CAP
# define CAP_LAST_CAP SYSTEMD_CAP_LAST_CAP
#endif
diff --git a/src/basic/missing_drm.h b/src/basic/missing_drm.h
index 0dec591..e4ca56f 100644
--- a/src/basic/missing_drm.h
+++ b/src/basic/missing_drm.h
@@ -2,9 +2,9 @@
#pragma once
#ifndef DRM_IOCTL_SET_MASTER
-#define DRM_IOCTL_SET_MASTER _IO('d', 0x1e)
+# define DRM_IOCTL_SET_MASTER _IO('d', 0x1e)
#endif
#ifndef DRM_IOCTL_DROP_MASTER
-#define DRM_IOCTL_DROP_MASTER _IO('d', 0x1f)
+# define DRM_IOCTL_DROP_MASTER _IO('d', 0x1f)
#endif
diff --git a/src/basic/missing_fs.h b/src/basic/missing_fs.h
index 9b03bba..d97b190 100644
--- a/src/basic/missing_fs.h
+++ b/src/basic/missing_fs.h
@@ -3,6 +3,8 @@
#include <linux/types.h>
+#include "macro.h"
+
/* linux/fs.h */
#ifndef RENAME_NOREPLACE /* 0a7c3937a1f23f8cb5fc77ae01661e9968a51d0c (3.15) */
#define RENAME_NOREPLACE (1 << 0)
@@ -28,43 +30,63 @@ struct file_clone_range {
/* linux/fs.h or sys/mount.h */
#ifndef MS_MOVE
-#define MS_MOVE 8192
+# define MS_MOVE 8192
+#else
+assert_cc(MS_MOVE == 8192);
#endif
#ifndef MS_REC
-#define MS_REC 16384
+# define MS_REC 16384
+#else
+assert_cc(MS_REC == 16384);
#endif
#ifndef MS_PRIVATE
-#define MS_PRIVATE (1<<18)
+# define MS_PRIVATE (1<<18)
+#else
+assert_cc(MS_PRIVATE == (1<<18));
#endif
#ifndef MS_SLAVE
-#define MS_SLAVE (1<<19)
+# define MS_SLAVE (1<<19)
+#else
+assert_cc(MS_SLAVE == (1<<19));
#endif
#ifndef MS_SHARED
-#define MS_SHARED (1<<20)
+# define MS_SHARED (1<<20)
+#else
+assert_cc(MS_SHARED == (1<<20));
#endif
#ifndef MS_RELATIME
-#define MS_RELATIME (1<<21)
+# define MS_RELATIME (1<<21)
+#else
+assert_cc(MS_RELATIME == (1<<21));
#endif
#ifndef MS_KERNMOUNT
-#define MS_KERNMOUNT (1<<22)
+# define MS_KERNMOUNT (1<<22)
+#else
+assert_cc(MS_KERNMOUNT == (1<<22));
#endif
#ifndef MS_I_VERSION
-#define MS_I_VERSION (1<<23)
+# define MS_I_VERSION (1<<23)
+#else
+assert_cc(MS_I_VERSION == (1<<23));
#endif
#ifndef MS_STRICTATIME
-#define MS_STRICTATIME (1<<24)
+# define MS_STRICTATIME (1<<24)
+#else
+assert_cc(MS_STRICTATIME == (1 << 24));
#endif
#ifndef MS_LAZYTIME
-#define MS_LAZYTIME (1<<25)
+# define MS_LAZYTIME (1<<25)
+#else
+assert_cc(MS_LAZYTIME == (1<<25));
#endif
/* Not exposed yet. Defined at fs/ext4/ext4.h */
@@ -78,10 +100,19 @@ struct file_clone_range {
#endif
#ifndef FS_PROJINHERIT_FL
-#define FS_PROJINHERIT_FL 0x20000000
+# define FS_PROJINHERIT_FL 0x20000000
+#else
+assert_cc(FS_PROJINHERIT_FL == 0x20000000);
#endif
/* linux/fscrypt.h */
#ifndef FS_KEY_DESCRIPTOR_SIZE
-#define FS_KEY_DESCRIPTOR_SIZE 8
+# define FS_KEY_DESCRIPTOR_SIZE 8
+#else
+assert_cc(FS_KEY_DESCRIPTOR_SIZE == 8);
+#endif
+
+/* linux/exportfs.h */
+#ifndef FILEID_KERNFS
+#define FILEID_KERNFS 0xfe
#endif
diff --git a/src/basic/missing_input.h b/src/basic/missing_input.h
index 6cf16ff..ee61bf9 100644
--- a/src/basic/missing_input.h
+++ b/src/basic/missing_input.h
@@ -4,9 +4,11 @@
#include <linux/input.h>
#include <linux/types.h>
+#include "macro.h"
+
/* linux@c7dc65737c9a607d3e6f8478659876074ad129b8 (3.12) */
#ifndef EVIOCREVOKE
-#define EVIOCREVOKE _IOW('E', 0x91, int)
+# define EVIOCREVOKE _IOW('E', 0x91, int)
#endif
/* linux@06a16293f71927f756dcf37558a79c0b05a91641 (4.4) */
@@ -17,29 +19,40 @@ struct input_mask {
__u64 codes_ptr;
};
-#define EVIOCGMASK _IOR('E', 0x92, struct input_mask)
-#define EVIOCSMASK _IOW('E', 0x93, struct input_mask)
+# define EVIOCGMASK _IOR('E', 0x92, struct input_mask)
+# define EVIOCSMASK _IOW('E', 0x93, struct input_mask)
#endif
/* linux@7611392fe8ff95ecae528b01a815ae3d72ca6b95 (3.17) */
#ifndef INPUT_PROP_POINTING_STICK
-#define INPUT_PROP_POINTING_STICK 0x05
+# define INPUT_PROP_POINTING_STICK 0x05
+#else
+assert_cc(INPUT_PROP_POINTING_STICK == 0x05);
#endif
/* linux@500d4160abe9a2e88b12e319c13ae3ebd1e18108 (4.0) */
#ifndef INPUT_PROP_ACCELEROMETER
-#define INPUT_PROP_ACCELEROMETER 0x06
+# define INPUT_PROP_ACCELEROMETER 0x06
+#else
+assert_cc(INPUT_PROP_ACCELEROMETER == 0x06);
#endif
/* linux@d09bbfd2a8408a995419dff0d2ba906013cf4cc9 (3.11) */
#ifndef BTN_DPAD_UP
-#define BTN_DPAD_UP 0x220
-#define BTN_DPAD_DOWN 0x221
-#define BTN_DPAD_LEFT 0x222
-#define BTN_DPAD_RIGHT 0x223
+# define BTN_DPAD_UP 0x220
+# define BTN_DPAD_DOWN 0x221
+# define BTN_DPAD_LEFT 0x222
+# define BTN_DPAD_RIGHT 0x223
+#else
+assert_cc(BTN_DPAD_UP == 0x220);
+assert_cc(BTN_DPAD_DOWN == 0x221);
+assert_cc(BTN_DPAD_LEFT == 0x222);
+assert_cc(BTN_DPAD_RIGHT == 0x223);
#endif
/* linux@358f24704f2f016af7d504b357cdf32606091d07 (3.13) */
#ifndef KEY_ALS_TOGGLE
-#define KEY_ALS_TOGGLE 0x230
+# fine KEY_ALS_TOGGLE 0x230
+#else
+assert_cc(KEY_ALS_TOGGLE == 0x230);
#endif
diff --git a/src/basic/missing_ioprio.h b/src/basic/missing_ioprio.h
index 9cbd172..13ce792 100644
--- a/src/basic/missing_ioprio.h
+++ b/src/basic/missing_ioprio.h
@@ -1,49 +1,81 @@
/* SPDX-License-Identifier: LGPL-2.1-or-later */
#pragma once
-#include <sched.h>
+#if HAVE_LINUX_IOPRIO_H
+# include <linux/ioprio.h>
+#endif
+
+#include "macro.h"
/* Match values uses by the kernel internally, as no public header seems to exist. */
#ifndef IOPRIO_N_CLASSES
# define IOPRIO_N_CLASSES 8
+#else
+assert_cc(IOPRIO_N_CLASSES == 8);
#endif
#ifndef IOPRIO_BE_NR
# define IOPRIO_BE_NR 8
+#else
+assert_cc(IOPRIO_BE_NR == 8);
#endif
#ifndef IOPRIO_CLASS_NONE
# define IOPRIO_CLASS_NONE 0
+#else
+assert_cc(IOPRIO_CLASS_NONE == 0);
#endif
#ifndef IOPRIO_CLASS_RT
# define IOPRIO_CLASS_RT 1
+#else
+assert_cc(IOPRIO_CLASS_RT == 1);
#endif
#ifndef IOPRIO_CLASS_BE
# define IOPRIO_CLASS_BE 2
+#else
+assert_cc(IOPRIO_CLASS_BE == 2);
#endif
#ifndef IOPRIO_CLASS_IDLE
# define IOPRIO_CLASS_IDLE 3
+#else
+assert_cc(IOPRIO_CLASS_IDLE == 3);
#endif
#ifndef IOPRIO_WHO_PROCESS
# define IOPRIO_WHO_PROCESS 1
+#else
+assert_cc(IOPRIO_WHO_PROCESS == 1);
#endif
+
#ifndef IOPRIO_WHO_PGRP
# define IOPRIO_WHO_PGRP 2
+#else
+assert_cc(IOPRIO_WHO_PGRP == 2);
#endif
+
#ifndef IOPRIO_WHO_USER
# define IOPRIO_WHO_USER 3
+#else
+assert_cc(IOPRIO_WHO_USER == 3);
#endif
#ifndef IOPRIO_BITS
# define IOPRIO_BITS 16
+#else
+assert_cc(IOPRIO_BITS == 16);
#endif
+
#ifndef IOPRIO_N_CLASSES
# define IOPRIO_N_CLASSES 8
+#else
+assert_cc(IOPRIO_N_CLASSES == 8);
#endif
+
#ifndef IOPRIO_CLASS_SHIFT
# define IOPRIO_CLASS_SHIFT 13
+#else
+assert_cc(IOPRIO_CLASS_SHIFT == 13);
#endif
static inline int ioprio_prio_class(int value) {
diff --git a/src/basic/missing_keyctl.h b/src/basic/missing_keyctl.h
index 081003a..78795fa 100644
--- a/src/basic/missing_keyctl.h
+++ b/src/basic/missing_keyctl.h
@@ -4,40 +4,60 @@
#include <inttypes.h>
#include <linux/keyctl.h>
+#include "macro.h"
+
#ifndef KEYCTL_JOIN_SESSION_KEYRING
-#define KEYCTL_JOIN_SESSION_KEYRING 1
+# define KEYCTL_JOIN_SESSION_KEYRING 1
+#else
+assert_cc(KEYCTL_JOIN_SESSION_KEYRING == 1);
#endif
#ifndef KEYCTL_CHOWN
-#define KEYCTL_CHOWN 4
+# define KEYCTL_CHOWN 4
+#else
+assert_cc(KEYCTL_CHOWN == 4);
#endif
#ifndef KEYCTL_SETPERM
-#define KEYCTL_SETPERM 5
+# define KEYCTL_SETPERM 5
+#else
+assert_cc(KEYCTL_SETPERM == 5);
#endif
#ifndef KEYCTL_DESCRIBE
-#define KEYCTL_DESCRIBE 6
+# define KEYCTL_DESCRIBE 6
+#else
+assert_cc(KEYCTL_DESCRIBE == 6);
#endif
#ifndef KEYCTL_LINK
-#define KEYCTL_LINK 8
+# define KEYCTL_LINK 8
+#else
+assert_cc(KEYCTL_LINK == 8);
#endif
#ifndef KEYCTL_READ
-#define KEYCTL_READ 11
+# define KEYCTL_READ 11
+#else
+assert_cc(KEYCTL_READ == 11);
#endif
#ifndef KEYCTL_SET_TIMEOUT
-#define KEYCTL_SET_TIMEOUT 15
+# define KEYCTL_SET_TIMEOUT 15
+#else
+assert_cc(KEYCTL_SET_TIMEOUT == 15);
#endif
#ifndef KEY_SPEC_USER_KEYRING
-#define KEY_SPEC_USER_KEYRING -4
+# define KEY_SPEC_USER_KEYRING -4
+#else
+assert_cc(KEY_SPEC_USER_KEYRING == -4);
#endif
#ifndef KEY_SPEC_SESSION_KEYRING
-#define KEY_SPEC_SESSION_KEYRING -3
+# define KEY_SPEC_SESSION_KEYRING -3
+#else
+assert_cc(KEY_SPEC_SESSION_KEYRING == -3);
#endif
/* From linux/key.h */
@@ -45,35 +65,37 @@
typedef int32_t key_serial_t;
-#define KEY_POS_VIEW 0x01000000
-#define KEY_POS_READ 0x02000000
-#define KEY_POS_WRITE 0x04000000
-#define KEY_POS_SEARCH 0x08000000
-#define KEY_POS_LINK 0x10000000
-#define KEY_POS_SETATTR 0x20000000
-#define KEY_POS_ALL 0x3f000000
-
-#define KEY_USR_VIEW 0x00010000
-#define KEY_USR_READ 0x00020000
-#define KEY_USR_WRITE 0x00040000
-#define KEY_USR_SEARCH 0x00080000
-#define KEY_USR_LINK 0x00100000
-#define KEY_USR_SETATTR 0x00200000
-#define KEY_USR_ALL 0x003f0000
-
-#define KEY_GRP_VIEW 0x00000100
-#define KEY_GRP_READ 0x00000200
-#define KEY_GRP_WRITE 0x00000400
-#define KEY_GRP_SEARCH 0x00000800
-#define KEY_GRP_LINK 0x00001000
-#define KEY_GRP_SETATTR 0x00002000
-#define KEY_GRP_ALL 0x00003f00
-
-#define KEY_OTH_VIEW 0x00000001
-#define KEY_OTH_READ 0x00000002
-#define KEY_OTH_WRITE 0x00000004
-#define KEY_OTH_SEARCH 0x00000008
-#define KEY_OTH_LINK 0x00000010
-#define KEY_OTH_SETATTR 0x00000020
-#define KEY_OTH_ALL 0x0000003f
+# define KEY_POS_VIEW 0x01000000
+# define KEY_POS_READ 0x02000000
+# define KEY_POS_WRITE 0x04000000
+# define KEY_POS_SEARCH 0x08000000
+# define KEY_POS_LINK 0x10000000
+# define KEY_POS_SETATTR 0x20000000
+# define KEY_POS_ALL 0x3f000000
+
+# define KEY_USR_VIEW 0x00010000
+# define KEY_USR_READ 0x00020000
+# define KEY_USR_WRITE 0x00040000
+# define KEY_USR_SEARCH 0x00080000
+# define KEY_USR_LINK 0x00100000
+# define KEY_USR_SETATTR 0x00200000
+# define KEY_USR_ALL 0x003f0000
+
+# define KEY_GRP_VIEW 0x00000100
+# define KEY_GRP_READ 0x00000200
+# define KEY_GRP_WRITE 0x00000400
+# define KEY_GRP_SEARCH 0x00000800
+# define KEY_GRP_LINK 0x00001000
+# define KEY_GRP_SETATTR 0x00002000
+# define KEY_GRP_ALL 0x00003f00
+
+# define KEY_OTH_VIEW 0x00000001
+# define KEY_OTH_READ 0x00000002
+# define KEY_OTH_WRITE 0x00000004
+# define KEY_OTH_SEARCH 0x00000008
+# define KEY_OTH_LINK 0x00000010
+# define KEY_OTH_SETATTR 0x00000020
+# define KEY_OTH_ALL 0x0000003f
+#else
+assert_cc(KEY_OTH_ALL == 0x0000003f);
#endif
diff --git a/src/basic/missing_loop.h b/src/basic/missing_loop.h
index 7141544..b88501d 100644
--- a/src/basic/missing_loop.h
+++ b/src/basic/missing_loop.h
@@ -3,6 +3,8 @@
#include <linux/loop.h>
+#include "macro.h"
+
#ifndef LOOP_CONFIGURE
struct loop_config {
__u32 fd;
@@ -11,14 +13,19 @@ struct loop_config {
__u64 __reserved[8];
};
-#define LOOP_CONFIGURE 0x4C0A
+# define LOOP_CONFIGURE 0x4C0A
+#else
+assert_cc(LOOP_CONFIGURE == 0x4C0A);
#endif
#ifndef LO_FLAGS_DIRECT_IO
-#define LO_FLAGS_DIRECT_IO 16
-#define LOOP_SET_DIRECT_IO 0x4C08
+# define LO_FLAGS_DIRECT_IO 16
+# define LOOP_SET_DIRECT_IO 0x4C08
+#else
+assert_cc(LO_FLAGS_DIRECT_IO == 16);
+assert_cc(LOOP_SET_DIRECT_IO == 0x4C08);
#endif
#ifndef LOOP_SET_STATUS_SETTABLE_FLAGS
-#define LOOP_SET_STATUS_SETTABLE_FLAGS (LO_FLAGS_AUTOCLEAR | LO_FLAGS_PARTSCAN | LO_FLAGS_DIRECT_IO)
+# define LOOP_SET_STATUS_SETTABLE_FLAGS (LO_FLAGS_AUTOCLEAR | LO_FLAGS_PARTSCAN)
#endif
diff --git a/src/basic/missing_magic.h b/src/basic/missing_magic.h
index 82d71c8..4e930ac 100644
--- a/src/basic/missing_magic.h
+++ b/src/basic/missing_magic.h
@@ -3,197 +3,107 @@
#include <linux/magic.h>
-/* 62aa81d7c4c24b90fdb61da70ac0dbbc414f9939 (4.13) */
-#ifndef OCFS2_SUPER_MAGIC
-#define OCFS2_SUPER_MAGIC 0x7461636f
-#endif
-
-/* 67e9c74b8a873408c27ac9a8e4c1d1c8d72c93ff (4.5) */
-#ifndef CGROUP2_SUPER_MAGIC
-#define CGROUP2_SUPER_MAGIC 0x63677270
-#endif
-
-/* 4282d60689d4f21b40692029080440cc58e8a17d (4.1) */
-#ifndef TRACEFS_MAGIC
-#define TRACEFS_MAGIC 0x74726163
-#endif
-
-/* e149ed2b805fefdccf7ccdfc19eca22fdd4514ac (3.19) */
-#ifndef NSFS_MAGIC
-#define NSFS_MAGIC 0x6e736673
-#endif
-
-/* b2197755b2633e164a439682fb05a9b5ea48f706 (4.4) */
-#ifndef BPF_FS_MAGIC
-#define BPF_FS_MAGIC 0xcafe4a11
-#endif
-
/* Not exposed yet (4.20). Defined at ipc/mqueue.c */
#ifndef MQUEUE_MAGIC
-#define MQUEUE_MAGIC 0x19800202
-#endif
-
-/* Not exposed yet (as of Linux 5.4). Defined in fs/xfs/libxfs/xfs_format.h */
-#ifndef XFS_SB_MAGIC
-#define XFS_SB_MAGIC 0x58465342
-#endif
-
-/* dea2903719283c156b53741126228c4a1b40440f (5.17) */
-#ifndef CIFS_SUPER_MAGIC
-#define CIFS_SUPER_MAGIC 0xFF534D42
-#endif
-
-/* dea2903719283c156b53741126228c4a1b40440f (5.17) */
-#ifndef SMB2_SUPER_MAGIC
-#define SMB2_SUPER_MAGIC 0xFE534D42
-#endif
-
-/* 257f871993474e2bde6c497b54022c362cf398e1 (4.5) */
-#ifndef OVERLAYFS_SUPER_MAGIC
-#define OVERLAYFS_SUPER_MAGIC 0x794c7630
-#endif
-
-/* 2a28900be20640fcd1e548b1e3bad79e8221fcf9 (4.7) */
-#ifndef UDF_SUPER_MAGIC
-#define UDF_SUPER_MAGIC 0x15013346
+# define MQUEUE_MAGIC 0x19800202
+#else
+assert_cc(MQUEUE_MAGIC == 0x19800202);
#endif
-/* b1123ea6d3b3da25af5c8a9d843bd07ab63213f4 (4.8) */
+/* b1123ea6d3b3da25af5c8a9d843bd07ab63213f4 (4.8), dropped by 68f2736a858324c3ec852f6c2cddd9d1c777357d (v6.0) */
#ifndef BALLOON_KVM_MAGIC
-#define BALLOON_KVM_MAGIC 0x13661366
+# define BALLOON_KVM_MAGIC 0x13661366
+#else
+assert_cc(BALLOON_KVM_MAGIC == 0x13661366);
#endif
-/* 48b4800a1c6af2cdda344ea4e2c843dcc1f6afc9 (4.8) */
+/* 48b4800a1c6af2cdda344ea4e2c843dcc1f6afc9 (4.8), dropped by 68f2736a858324c3ec852f6c2cddd9d1c777357d (v6.0) */
#ifndef ZSMALLOC_MAGIC
-#define ZSMALLOC_MAGIC 0x58295829
-#endif
-
-/* 3bc52c45bac26bf7ed1dc8d287ad1aeaed1250b6 (4.9) */
-#ifndef DAXFS_MAGIC
-#define DAXFS_MAGIC 0x64646178
-#endif
-
-/* 5ff193fbde20df5d80fec367cea3e7856c057320 (4.10) */
-#ifndef RDTGROUP_SUPER_MAGIC
-#define RDTGROUP_SUPER_MAGIC 0x7655821
-#endif
-
-/* a481f4d917835cad86701fc0d1e620c74bb5cd5f (4.13) */
-#ifndef AAFS_MAGIC
-#define AAFS_MAGIC 0x5a3c69f0
+# define ZSMALLOC_MAGIC 0x58295829
+#else
+assert_cc(ZSMALLOC_MAGIC == 0x58295829);
#endif
-/* f044c8847bb61eff5e1e95b6f6bb950e7f4a73a4 (4.15) */
-#ifndef AFS_FS_MAGIC
-#define AFS_FS_MAGIC 0x6b414653
-#endif
-
-/* dddde68b8f06dd83486124b8d245e7bfb15c185d (4.20) */
-#ifndef XFS_SUPER_MAGIC
-#define XFS_SUPER_MAGIC 0x58465342
-#endif
-
-/* 3ad20fe393b31025bebfc2d76964561f65df48aa (5.0) */
-#ifndef BINDERFS_SUPER_MAGIC
-#define BINDERFS_SUPER_MAGIC 0x6c6f6f70
-#endif
-
-/* ed63bb1d1f8469586006a9ca63c42344401aa2ab (5.3) */
-#ifndef DMA_BUF_MAGIC
-#define DMA_BUF_MAGIC 0x444d4142
-#endif
-
-/* ea8157ab2ae5e914dd427e5cfab533b6da3819cd (5.3) */
+/* ea8157ab2ae5e914dd427e5cfab533b6da3819cd (5.3), dropped by 68f2736a858324c3ec852f6c2cddd9d1c777357d (v6.0) */
#ifndef Z3FOLD_MAGIC
-#define Z3FOLD_MAGIC 0x33
-#endif
-
-/* 47e4937a4a7ca4184fd282791dfee76c6799966a (5.4) */
-#ifndef EROFS_SUPER_MAGIC_V1
-#define EROFS_SUPER_MAGIC_V1 0xe0f5e1e2
+# define Z3FOLD_MAGIC 0x33
+#else
+assert_cc(Z3FOLD_MAGIC == 0x33);
#endif
-/* fe030c9b85e6783bc52fe86449c0a4b8aa16c753 (5.5) */
+/* fe030c9b85e6783bc52fe86449c0a4b8aa16c753 (5.5), dropped by 68f2736a858324c3ec852f6c2cddd9d1c777357d (v6.0) */
#ifndef PPC_CMM_MAGIC
-#define PPC_CMM_MAGIC 0xc7571590
-#endif
-
-/* 8dcc1a9d90c10fa4143e5c17821082e5e60e46a1 (5.6) */
-#ifndef ZONEFS_MAGIC
-#define ZONEFS_MAGIC 0x5a4f4653
-#endif
-
-/* 3234ac664a870e6ea69ae3a57d824cd7edbeacc5 (5.8) */
-#ifndef DEVMEM_MAGIC
-#define DEVMEM_MAGIC 0x454d444d
-#endif
-
-/* cb12fd8e0dabb9a1c8aef55a6a41e2c255fcdf4b (6.8) */
-#ifndef PID_FS_MAGIC
-#define PID_FS_MAGIC 0x50494446
+# define PPC_CMM_MAGIC 0xc7571590
+#else
+assert_cc(PPC_CMM_MAGIC == 0xc7571590);
#endif
/* Not in mainline but included in Ubuntu */
#ifndef SHIFTFS_MAGIC
-#define SHIFTFS_MAGIC 0x6a656a62
-#endif
-
-/* 1507f51255c9ff07d75909a84e7c0d7f3c4b2f49 (5.14) */
-#ifndef SECRETMEM_MAGIC
-#define SECRETMEM_MAGIC 0x5345434d
-#endif
-
-/* Not exposed yet. Defined at fs/fuse/inode.c */
-#ifndef FUSE_SUPER_MAGIC
-#define FUSE_SUPER_MAGIC 0x65735546
+# define SHIFTFS_MAGIC 0x6a656a62
+#else
+assert_cc(SHIFTFS_MAGIC == 0x6a656a62);
#endif
/* Not exposed yet. Defined at fs/fuse/control.c */
#ifndef FUSE_CTL_SUPER_MAGIC
-#define FUSE_CTL_SUPER_MAGIC 0x65735543
-#endif
-
-/* Not exposed yet. Defined at fs/ceph/super.h */
-#ifndef CEPH_SUPER_MAGIC
-#define CEPH_SUPER_MAGIC 0x00c36400
+# define FUSE_CTL_SUPER_MAGIC 0x65735543
+#else
+assert_cc(FUSE_CTL_SUPER_MAGIC == 0x65735543);
#endif
/* Not exposed yet. Defined at fs/orangefs/orangefs-kernel.h */
#ifndef ORANGEFS_DEVREQ_MAGIC
-#define ORANGEFS_DEVREQ_MAGIC 0x20030529
+# define ORANGEFS_DEVREQ_MAGIC 0x20030529
+#else
+assert_cc(ORANGEFS_DEVREQ_MAGIC == 0x20030529);
#endif
/* linux/gfs2_ondisk.h */
#ifndef GFS2_MAGIC
-#define GFS2_MAGIC 0x01161970
+# define GFS2_MAGIC 0x01161970
+#else
+assert_cc(GFS2_MAGIC == 0x01161970);
#endif
/* Not exposed yet. Defined at fs/configfs/mount.c */
#ifndef CONFIGFS_MAGIC
-#define CONFIGFS_MAGIC 0x62656570
+# define CONFIGFS_MAGIC 0x62656570
+#else
+assert_cc(CONFIGFS_MAGIC == 0x62656570);
#endif
/* Not exposed yet. Defined at fs/vboxsf/super.c */
#ifndef VBOXSF_SUPER_MAGIC
-#define VBOXSF_SUPER_MAGIC 0x786f4256
-#endif
-
-/* Not exposed yet. Defined at fs/exfat/exfat_fs.h */
-#ifndef EXFAT_SUPER_MAGIC
-#define EXFAT_SUPER_MAGIC 0x2011BAB0UL
+# define VBOXSF_SUPER_MAGIC 0x786f4256
+#else
+assert_cc(VBOXSF_SUPER_MAGIC == 0x786f4256);
#endif
/* Not exposed yet, internally actually called RPCAUTH_GSSMAGIC. Defined in net/sunrpc/rpc_pipe.c */
#ifndef RPC_PIPEFS_SUPER_MAGIC
-#define RPC_PIPEFS_SUPER_MAGIC 0x67596969
+# define RPC_PIPEFS_SUPER_MAGIC 0x67596969
+#else
+assert_cc(RPC_PIPEFS_SUPER_MAGIC == 0x67596969);
#endif
/* Not exposed yet, defined at fs/ntfs/ntfs.h */
#ifndef NTFS_SB_MAGIC
-#define NTFS_SB_MAGIC 0x5346544e
+# define NTFS_SB_MAGIC 0x5346544e
+#else
+assert_cc(NTFS_SB_MAGIC == 0x5346544e);
#endif
/* Not exposed yet, encoded literally in fs/ntfs3/super.c. */
#ifndef NTFS3_SUPER_MAGIC
-#define NTFS3_SUPER_MAGIC 0x7366746e
+# define NTFS3_SUPER_MAGIC 0x7366746e
+#else
+assert_cc(NTFS3_SUPER_MAGIC == 0x7366746e);
+#endif
+
+/* Added in Linux commit e2f48c48090dea172c0c571101041de64634dae5. Remove when next sync'd */
+#ifndef BCACHEFS_SUPER_MAGIC
+# define BCACHEFS_SUPER_MAGIC 0xca451a4e
+#else
+assert_cc(BCACHEFS_SUPER_MAGIC == 0xca451a4e)
#endif
diff --git a/src/basic/missing_mman.h b/src/basic/missing_mman.h
index f48c436..d6a8b4b 100644
--- a/src/basic/missing_mman.h
+++ b/src/basic/missing_mman.h
@@ -3,18 +3,28 @@
#include <sys/mman.h>
+#include "macro.h"
+
#ifndef MFD_ALLOW_SEALING
-#define MFD_ALLOW_SEALING 0x0002U
+# define MFD_ALLOW_SEALING 0x0002U
+#else
+assert_cc(MFD_ALLOW_SEALING == 0x0002U);
#endif
#ifndef MFD_CLOEXEC
-#define MFD_CLOEXEC 0x0001U
+# define MFD_CLOEXEC 0x0001U
+#else
+assert_cc(MFD_CLOEXEC == 0x0001U);
#endif
#ifndef MFD_NOEXEC_SEAL
-#define MFD_NOEXEC_SEAL 0x0008U
+# define MFD_NOEXEC_SEAL 0x0008U
+#else
+assert_cc(MFD_NOEXEC_SEAL == 0x0008U);
#endif
#ifndef MFD_EXEC
-#define MFD_EXEC 0x0010U
+# define MFD_EXEC 0x0010U
+#else
+assert_cc(MFD_EXEC == 0x0010U);
#endif
diff --git a/src/basic/missing_mount.h b/src/basic/missing_mount.h
index 69b0bcf..d6e16e5 100644
--- a/src/basic/missing_mount.h
+++ b/src/basic/missing_mount.h
@@ -3,7 +3,11 @@
#include <sys/mount.h>
+#include "macro.h"
+
/* dab741e0e02bd3c4f5e2e97be74b39df2523fc6e (5.10) */
#ifndef MS_NOSYMFOLLOW
-#define MS_NOSYMFOLLOW 256
+# define MS_NOSYMFOLLOW 256
+#else
+assert_cc(MS_NOSYMFOLLOW == 256);
#endif
diff --git a/src/basic/missing_prctl.h b/src/basic/missing_prctl.h
index 7d9e395..2c9f9f6 100644
--- a/src/basic/missing_prctl.h
+++ b/src/basic/missing_prctl.h
@@ -3,6 +3,8 @@
#include <linux/prctl.h>
+#include "macro.h"
+
/* 58319057b7847667f0c9585b9de0e8932b0fdb08 (4.3) */
#ifndef PR_CAP_AMBIENT
#define PR_CAP_AMBIENT 47
@@ -15,12 +17,19 @@
/* b507808ebce23561d4ff8c2aa1fb949fe402bc61 (6.3) */
#ifndef PR_SET_MDWE
-#define PR_SET_MDWE 65
+# define PR_SET_MDWE 65
+#else
+assert_cc(PR_SET_MDWE == 65);
#endif
+
#ifndef PR_MDWE_REFUSE_EXEC_GAIN
-#define PR_MDWE_REFUSE_EXEC_GAIN 1
+# define PR_MDWE_REFUSE_EXEC_GAIN 1
+#else
+assert_cc(PR_MDWE_REFUSE_EXEC_GAIN == 1);
#endif
#ifndef PR_SET_MEMORY_MERGE
-#define PR_SET_MEMORY_MERGE 67
+# define PR_SET_MEMORY_MERGE 67
+#else
+assert_cc(PR_SET_MEMORY_MERGE == 67);
#endif
diff --git a/src/basic/missing_random.h b/src/basic/missing_random.h
index 443b913..0f8a5be 100644
--- a/src/basic/missing_random.h
+++ b/src/basic/missing_random.h
@@ -1,6 +1,8 @@
/* SPDX-License-Identifier: LGPL-2.1-or-later */
#pragma once
+#include "macro.h"
+
#if USE_SYS_RANDOM_H
# include <sys/random.h>
#else
@@ -8,13 +10,19 @@
#endif
#ifndef GRND_NONBLOCK
-#define GRND_NONBLOCK 0x0001
+# define GRND_NONBLOCK 0x0001
+#else
+assert_cc(GRND_NONBLOCK == 0x0001);
#endif
#ifndef GRND_RANDOM
-#define GRND_RANDOM 0x0002
+# define GRND_RANDOM 0x0002
+#else
+assert_cc(GRND_RANDOM == 0x0002);
#endif
#ifndef GRND_INSECURE
-#define GRND_INSECURE 0x0004
+# define GRND_INSECURE 0x0004
+#else
+assert_cc(GRND_INSECURE == 0x0004);
#endif
diff --git a/src/basic/missing_resource.h b/src/basic/missing_resource.h
index 6e76765..1807673 100644
--- a/src/basic/missing_resource.h
+++ b/src/basic/missing_resource.h
@@ -3,8 +3,12 @@
#include <sys/resource.h>
+#include "macro.h"
+
#ifndef RLIMIT_RTTIME
-#define RLIMIT_RTTIME 15
+# define RLIMIT_RTTIME 15
+#else
+assert_cc(RLIMIT_RTTIME == 15);
#endif
/* If RLIMIT_RTTIME is not defined, then we cannot use RLIMIT_NLIMITS as is */
diff --git a/src/basic/missing_sched.h b/src/basic/missing_sched.h
index bcd5b77..b8109d3 100644
--- a/src/basic/missing_sched.h
+++ b/src/basic/missing_sched.h
@@ -3,24 +3,35 @@
#include <sched.h>
+#include "macro.h"
+
#ifndef CLONE_NEWCGROUP
-#define CLONE_NEWCGROUP 0x02000000
+# define CLONE_NEWCGROUP 0x02000000
+#else
+assert_cc(CLONE_NEWCGROUP == 0x02000000);
#endif
/* 769071ac9f20b6a447410c7eaa55d1a5233ef40c (5.8) */
#ifndef CLONE_NEWTIME
-#define CLONE_NEWTIME 0x00000080
+# define CLONE_NEWTIME 0x00000080
+#else
+assert_cc(CLONE_NEWTIME == 0x00000080);
#endif
/* Not exposed yet. Defined at include/linux/sched.h */
#ifndef PF_KTHREAD
-#define PF_KTHREAD 0x00200000
+# define PF_KTHREAD 0x00200000
+#else
+assert_cc(PF_KTHREAD == 0x00200000);
#endif
-/* The maximum thread/process name length including trailing NUL byte. This mimics the kernel definition of the same
- * name, which we need in userspace at various places but is not defined in userspace currently, neither under this
- * name nor any other. */
-/* Not exposed yet. Defined at include/linux/sched.h */
+/* The maximum thread/process name length including trailing NUL byte. This mimics the kernel definition of
+ * the same name, which we need in userspace at various places but is not defined in userspace currently,
+ * neither under this name nor any other.
+ *
+ * Not exposed yet. Defined at include/linux/sched.h */
#ifndef TASK_COMM_LEN
-#define TASK_COMM_LEN 16
+# define TASK_COMM_LEN 16
+#else
+assert_cc(TASK_COMM_LEN == 16);
#endif
diff --git a/src/basic/missing_socket.h b/src/basic/missing_socket.h
index 30ac297..47cc762 100644
--- a/src/basic/missing_socket.h
+++ b/src/basic/missing_socket.h
@@ -6,7 +6,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;
@@ -20,6 +19,26 @@ struct sockaddr_vm {
};
#endif /* !HAVE_LINUX_VM_SOCKETS_H */
+#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
@@ -32,6 +51,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/basic/missing_timerfd.h b/src/basic/missing_timerfd.h
index dba3043..a01a4ec 100644
--- a/src/basic/missing_timerfd.h
+++ b/src/basic/missing_timerfd.h
@@ -3,6 +3,10 @@
#include <sys/timerfd.h>
+#include "macro.h"
+
#ifndef TFD_TIMER_CANCEL_ON_SET
-#define TFD_TIMER_CANCEL_ON_SET (1 << 1)
+# define TFD_TIMER_CANCEL_ON_SET (1 << 1)
+#else
+assert_cc(TFD_TIMER_CANCEL_ON_SET == (1 << 1));
#endif
diff --git a/src/basic/missing_type.h b/src/basic/missing_type.h
index f623309..1d17705 100644
--- a/src/basic/missing_type.h
+++ b/src/basic/missing_type.h
@@ -4,9 +4,9 @@
#include <uchar.h>
#if !HAVE_CHAR32_T
-#define char32_t uint32_t
+# define char32_t uint32_t
#endif
#if !HAVE_CHAR16_T
-#define char16_t uint16_t
+# define char16_t uint16_t
#endif
diff --git a/src/basic/missing_wait.h b/src/basic/missing_wait.h
new file mode 100644
index 0000000..3965b5b
--- /dev/null
+++ b/src/basic/missing_wait.h
@@ -0,0 +1,12 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+#pragma once
+
+#include <sys/wait.h>
+
+#include "macro.h"
+
+#ifndef P_PIDFD
+# define P_PIDFD 3
+#else
+assert_cc(P_PIDFD == 3);
+#endif
diff --git a/src/basic/mkdir.c b/src/basic/mkdir.c
index c770e5e..f87de0a 100644
--- a/src/basic/mkdir.c
+++ b/src/basic/mkdir.c
@@ -70,17 +70,11 @@ int mkdirat_safe_internal(
path, st.st_mode & 0777, mode);
if ((uid != UID_INVALID && st.st_uid != uid) ||
- (gid != GID_INVALID && st.st_gid != gid)) {
- char u[DECIMAL_STR_MAX(uid_t)] = "-", g[DECIMAL_STR_MAX(gid_t)] = "-";
-
- if (uid != UID_INVALID)
- xsprintf(u, UID_FMT, uid);
- if (gid != UID_INVALID)
- xsprintf(g, GID_FMT, gid);
+ (gid != GID_INVALID && st.st_gid != gid))
return log_full_errno(flags & MKDIR_WARN_MODE ? LOG_WARNING : LOG_DEBUG, SYNTHETIC_ERRNO(EEXIST),
"Directory \"%s\" already exists, but is owned by "UID_FMT":"GID_FMT" (%s:%s was requested), refusing.",
- path, st.st_uid, st.st_gid, u, g);
- }
+ path, st.st_uid, st.st_gid, uid != UID_INVALID ? FORMAT_UID(uid) : "-",
+ gid != UID_INVALID ? FORMAT_GID(gid) : "-");
return 0;
}
@@ -118,7 +112,7 @@ int mkdirat_parents_internal(int dir_fd, const char *path, mode_t mode, uid_t ui
/* drop the last component */
path = strndupa_safe(path, e - path);
- r = is_dir_full(dir_fd, path, true);
+ r = is_dir_at(dir_fd, path, /* follow = */ true);
if (r > 0)
return 0;
if (r == 0)
@@ -210,11 +204,13 @@ int mkdir_p_safe(const char *prefix, const char *path, mode_t mode, uid_t uid, g
return mkdir_p_internal(prefix, path, mode, uid, gid, flags, mkdirat_errno_wrapper);
}
-int mkdir_p_root(const char *root, const char *p, uid_t uid, gid_t gid, mode_t m, char **subvolumes) {
+int mkdir_p_root_full(const char *root, const char *p, uid_t uid, gid_t gid, mode_t m, usec_t ts, char **subvolumes) {
_cleanup_free_ char *pp = NULL, *bn = NULL;
_cleanup_close_ int dfd = -EBADF;
int r;
+ assert(p);
+
r = path_extract_directory(p, &pp);
if (r == -EDESTADDRREQ) {
/* only fname is passed, no prefix to operate on */
@@ -228,11 +224,11 @@ int mkdir_p_root(const char *root, const char *p, uid_t uid, gid_t gid, mode_t m
return r;
else {
/* Extracting the parent dir worked, hence we aren't top-level? Recurse up first. */
- r = mkdir_p_root(root, pp, uid, gid, m, subvolumes);
+ r = mkdir_p_root_full(root, pp, uid, gid, m, ts, subvolumes);
if (r < 0)
return r;
- dfd = chase_and_open(pp, root, CHASE_PREFIX_ROOT, O_RDONLY|O_CLOEXEC|O_DIRECTORY, NULL);
+ dfd = chase_and_open(pp, root, CHASE_PREFIX_ROOT, O_CLOEXEC|O_DIRECTORY, NULL);
if (dfd < 0)
return dfd;
}
@@ -247,23 +243,31 @@ int mkdir_p_root(const char *root, const char *p, uid_t uid, gid_t gid, mode_t m
r = btrfs_subvol_make_fallback(dfd, bn, m);
else
r = RET_NERRNO(mkdirat(dfd, bn, m));
- if (r < 0) {
- if (r == -EEXIST)
- return 0;
-
+ if (r == -EEXIST)
+ return 0;
+ if (r < 0)
return r;
- }
- if (uid_is_valid(uid) || gid_is_valid(gid)) {
- _cleanup_close_ int nfd = -EBADF;
+ if (ts == USEC_INFINITY && !uid_is_valid(uid) && !gid_is_valid(gid))
+ return 1;
+
+ _cleanup_close_ int nfd = openat(dfd, bn, O_CLOEXEC|O_DIRECTORY|O_NOFOLLOW);
+ if (nfd < 0)
+ return -errno;
+
+ if (ts != USEC_INFINITY) {
+ struct timespec tspec;
+ timespec_store(&tspec, ts);
- nfd = openat(dfd, bn, O_RDONLY|O_CLOEXEC|O_DIRECTORY|O_NOFOLLOW);
- if (nfd < 0)
+ if (futimens(dfd, (const struct timespec[2]) { TIMESPEC_OMIT, tspec }) < 0)
return -errno;
- if (fchown(nfd, uid, gid) < 0)
+ if (futimens(nfd, (const struct timespec[2]) { tspec, tspec }) < 0)
return -errno;
}
+ if ((uid_is_valid(uid) || gid_is_valid(gid)) && fchown(nfd, uid, gid) < 0)
+ return -errno;
+
return 1;
}
diff --git a/src/basic/mkdir.h b/src/basic/mkdir.h
index e538748..471f45b 100644
--- a/src/basic/mkdir.h
+++ b/src/basic/mkdir.h
@@ -4,6 +4,8 @@
#include <fcntl.h>
#include <sys/types.h>
+#include "time-util.h"
+
typedef enum MkdirFlags {
MKDIR_FOLLOW_SYMLINK = 1 << 0,
MKDIR_IGNORE_EXISTING = 1 << 1, /* Quietly accept a preexisting directory (or file) */
@@ -23,7 +25,10 @@ static inline int mkdir_parents(const char *path, mode_t mode) {
int mkdir_parents_safe(const char *prefix, const char *path, mode_t mode, uid_t uid, gid_t gid, MkdirFlags flags);
int mkdir_p(const char *path, mode_t mode);
int mkdir_p_safe(const char *prefix, const char *path, mode_t mode, uid_t uid, gid_t gid, MkdirFlags flags);
-int mkdir_p_root(const char *root, const char *p, uid_t uid, gid_t gid, mode_t m, char **subvolumes);
+int mkdir_p_root_full(const char *root, const char *p, uid_t uid, gid_t gid, mode_t m, usec_t ts, char **subvolumes);
+static inline int mkdir_p_root(const char *root, const char *p, uid_t uid, gid_t gid, mode_t m) {
+ return mkdir_p_root_full(root, p, uid, gid, m, USEC_INFINITY, NULL);
+}
/* The following are used to implement the mkdir_xyz_label() calls, don't use otherwise. */
typedef int (*mkdirat_func_t)(int dir_fd, const char *pathname, mode_t mode);
diff --git a/src/basic/mountpoint-util.c b/src/basic/mountpoint-util.c
index bf67f7e..66fa35b 100644
--- a/src/basic/mountpoint-util.c
+++ b/src/basic/mountpoint-util.c
@@ -329,34 +329,33 @@ fallback_fstat:
}
/* flags can be AT_SYMLINK_FOLLOW or 0 */
-int path_is_mount_point(const char *t, const char *root, int flags) {
+int path_is_mount_point_full(const char *path, const char *root, int flags) {
_cleanup_free_ char *canonical = NULL;
_cleanup_close_ int fd = -EBADF;
int r;
- assert(t);
+ assert(path);
assert((flags & ~AT_SYMLINK_FOLLOW) == 0);
- if (path_equal(t, "/"))
+ if (path_equal(path, "/"))
return 1;
- /* we need to resolve symlinks manually, we can't just rely on
- * fd_is_mount_point() to do that for us; if we have a structure like
- * /bin -> /usr/bin/ and /usr is a mount point, then the parent that we
+ /* we need to resolve symlinks manually, we can't just rely on fd_is_mount_point() to do that for us;
+ * if we have a structure like /bin -> /usr/bin/ and /usr is a mount point, then the parent that we
* look at needs to be /usr, not /. */
- if (flags & AT_SYMLINK_FOLLOW) {
- r = chase(t, root, CHASE_TRAIL_SLASH, &canonical, NULL);
+ if (FLAGS_SET(flags, AT_SYMLINK_FOLLOW)) {
+ r = chase(path, root, CHASE_TRAIL_SLASH, &canonical, NULL);
if (r < 0)
return r;
- t = canonical;
+ path = canonical;
}
- fd = open_parent(t, O_PATH|O_CLOEXEC, 0);
+ fd = open_parent(path, O_PATH|O_CLOEXEC, 0);
if (fd < 0)
return fd;
- return fd_is_mount_point(fd, last_path_component(t), flags);
+ return fd_is_mount_point(fd, last_path_component(path), flags);
}
int path_get_mnt_id_at_fallback(int dir_fd, const char *path, int *ret) {
@@ -446,14 +445,15 @@ bool fstype_needs_quota(const char *fstype) {
}
bool fstype_is_api_vfs(const char *fstype) {
- const FilesystemSet *fs;
+ assert(fstype);
- FOREACH_POINTER(fs,
- filesystem_sets + FILESYSTEM_SET_BASIC_API,
- filesystem_sets + FILESYSTEM_SET_AUXILIARY_API,
- filesystem_sets + FILESYSTEM_SET_PRIVILEGED_API,
- filesystem_sets + FILESYSTEM_SET_TEMPORARY)
- if (nulstr_contains(fs->value, fstype))
+ const FilesystemSet *fs;
+ FOREACH_ARGUMENT(fs,
+ filesystem_sets + FILESYSTEM_SET_BASIC_API,
+ filesystem_sets + FILESYSTEM_SET_AUXILIARY_API,
+ filesystem_sets + FILESYSTEM_SET_PRIVILEGED_API,
+ filesystem_sets + FILESYSTEM_SET_TEMPORARY)
+ if (nulstr_contains(fs->value, fstype))
return true;
/* Filesystems not present in the internal database */
@@ -495,16 +495,34 @@ bool fstype_can_discard(const char *fstype) {
return mount_option_supported(fstype, "discard", NULL) > 0;
}
-bool fstype_can_norecovery(const char *fstype) {
+const char* fstype_norecovery_option(const char *fstype) {
+ int r;
+
assert(fstype);
/* Use a curated list as first check, to avoid calling fsopen() which might load kmods, which might
* not be allowed in our MAC context. */
- if (STR_IN_SET(fstype, "ext3", "ext4", "xfs", "btrfs"))
- return true;
+ if (STR_IN_SET(fstype, "ext3", "ext4", "xfs"))
+ return "norecovery";
+
+ /* btrfs dropped support for the "norecovery" option in 6.8
+ * (https://github.com/torvalds/linux/commit/a1912f712188291f9d7d434fba155461f1ebef66) and replaced
+ * it with rescue=nologreplay, so we check for the new name first and fall back to checking for the
+ * old name if the new name doesn't work. */
+ if (streq(fstype, "btrfs")) {
+ r = mount_option_supported(fstype, "rescue=nologreplay", NULL);
+ if (r == -EAGAIN) {
+ log_debug_errno(r, "Failed to check for btrfs 'rescue=nologreplay' option, assuming old kernel with 'norecovery': %m");
+ return "norecovery";
+ }
+ if (r < 0)
+ log_debug_errno(r, "Failed to check for btrfs 'rescue=nologreplay' option, assuming it is not supported: %m");
+ if (r > 0)
+ return "rescue=nologreplay";
+ }
/* On new kernels we can just ask the kernel */
- return mount_option_supported(fstype, "norecovery", NULL) > 0;
+ return mount_option_supported(fstype, "norecovery", NULL) > 0 ? "norecovery" : NULL;
}
bool fstype_can_umask(const char *fstype) {
@@ -784,3 +802,10 @@ int mount_option_supported(const char *fstype, const char *key, const char *valu
return true; /* works! */
}
+
+bool path_below_api_vfs(const char *p) {
+ assert(p);
+
+ /* API VFS are either directly mounted on any of these three paths, or below it. */
+ return PATH_STARTSWITH_SET(p, "/dev", "/sys", "/proc");
+}
diff --git a/src/basic/mountpoint-util.h b/src/basic/mountpoint-util.h
index 499403a..d7c6251 100644
--- a/src/basic/mountpoint-util.h
+++ b/src/basic/mountpoint-util.h
@@ -3,6 +3,7 @@
#include <fcntl.h>
#include <stdbool.h>
+#include <stddef.h>
#include <sys/types.h>
/* The limit used for /dev itself. 4MB should be enough since device nodes and symlinks don't
@@ -44,7 +45,10 @@ static inline int path_get_mnt_id(const char *path, int *ret) {
}
int fd_is_mount_point(int fd, const char *filename, int flags);
-int path_is_mount_point(const char *path, const char *root, int flags);
+int path_is_mount_point_full(const char *path, const char *root, int flags);
+static inline int path_is_mount_point(const char *path) {
+ return path_is_mount_point_full(path, NULL, 0);
+}
bool fstype_is_network(const char *fstype);
bool fstype_needs_quota(const char *fstype);
@@ -53,9 +57,10 @@ bool fstype_is_blockdev_backed(const char *fstype);
bool fstype_is_ro(const char *fsype);
bool fstype_can_discard(const char *fstype);
bool fstype_can_uid_gid(const char *fstype);
-bool fstype_can_norecovery(const char *fstype);
bool fstype_can_umask(const char *fstype);
+const char* fstype_norecovery_option(const char *fstype);
+
int dev_is_devtmpfs(void);
int mount_fd(const char *source, int target_fd, const char *filesystemtype, unsigned long mountflags, const void *data);
@@ -69,3 +74,5 @@ bool mount_new_api_supported(void);
unsigned long ms_nosymfollow_supported(void);
int mount_option_supported(const char *fstype, const char *key, const char *value);
+
+bool path_below_api_vfs(const char *p);
diff --git a/src/basic/namespace-util.c b/src/basic/namespace-util.c
index 2101f61..5b4e43f 100644
--- a/src/basic/namespace-util.c
+++ b/src/basic/namespace-util.c
@@ -11,6 +11,7 @@
#include "missing_magic.h"
#include "missing_sched.h"
#include "namespace-util.h"
+#include "parse-util.h"
#include "process-util.h"
#include "stat-util.h"
#include "stdio-util.h"
@@ -33,71 +34,86 @@ const struct namespace_info namespace_info[] = {
#define pid_namespace_path(pid, type) procfs_file_alloca(pid, namespace_info[type].proc_path)
-int namespace_open(pid_t pid, int *pidns_fd, int *mntns_fd, int *netns_fd, int *userns_fd, int *root_fd) {
- _cleanup_close_ int pidnsfd = -EBADF, mntnsfd = -EBADF, netnsfd = -EBADF, usernsfd = -EBADF;
- int rfd = -EBADF;
+static NamespaceType clone_flag_to_namespace_type(unsigned long clone_flag) {
+ for (NamespaceType t = 0; t < _NAMESPACE_TYPE_MAX; t++)
+ if (((namespace_info[t].clone_flag ^ clone_flag) & (CLONE_NEWCGROUP|CLONE_NEWIPC|CLONE_NEWNET|CLONE_NEWNS|CLONE_NEWPID|CLONE_NEWUSER|CLONE_NEWUTS|CLONE_NEWTIME)) == 0)
+ return t;
+
+ return _NAMESPACE_TYPE_INVALID;
+}
+
+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) {
+
+ _cleanup_close_ int pidns_fd = -EBADF, mntns_fd = -EBADF, netns_fd = -EBADF,
+ userns_fd = -EBADF, root_fd = -EBADF;
assert(pid >= 0);
- if (mntns_fd) {
- const char *mntns;
+ if (ret_pidns_fd) {
+ const char *pidns;
- mntns = pid_namespace_path(pid, NAMESPACE_MOUNT);
- mntnsfd = open(mntns, O_RDONLY|O_NOCTTY|O_CLOEXEC);
- if (mntnsfd < 0)
+ pidns = pid_namespace_path(pid, NAMESPACE_PID);
+ pidns_fd = open(pidns, O_RDONLY|O_NOCTTY|O_CLOEXEC);
+ if (pidns_fd < 0)
return -errno;
}
- if (pidns_fd) {
- const char *pidns;
+ if (ret_mntns_fd) {
+ const char *mntns;
- pidns = pid_namespace_path(pid, NAMESPACE_PID);
- pidnsfd = open(pidns, O_RDONLY|O_NOCTTY|O_CLOEXEC);
- if (pidnsfd < 0)
+ mntns = pid_namespace_path(pid, NAMESPACE_MOUNT);
+ mntns_fd = open(mntns, O_RDONLY|O_NOCTTY|O_CLOEXEC);
+ if (mntns_fd < 0)
return -errno;
}
- if (netns_fd) {
+ if (ret_netns_fd) {
const char *netns;
netns = pid_namespace_path(pid, NAMESPACE_NET);
- netnsfd = open(netns, O_RDONLY|O_NOCTTY|O_CLOEXEC);
- if (netnsfd < 0)
+ netns_fd = open(netns, O_RDONLY|O_NOCTTY|O_CLOEXEC);
+ if (netns_fd < 0)
return -errno;
}
- if (userns_fd) {
+ if (ret_userns_fd) {
const char *userns;
userns = pid_namespace_path(pid, NAMESPACE_USER);
- usernsfd = open(userns, O_RDONLY|O_NOCTTY|O_CLOEXEC);
- if (usernsfd < 0 && errno != ENOENT)
+ userns_fd = open(userns, O_RDONLY|O_NOCTTY|O_CLOEXEC);
+ if (userns_fd < 0 && errno != ENOENT)
return -errno;
}
- if (root_fd) {
+ if (ret_root_fd) {
const char *root;
root = procfs_file_alloca(pid, "root");
- rfd = open(root, O_RDONLY|O_NOCTTY|O_CLOEXEC|O_DIRECTORY);
- if (rfd < 0)
+ root_fd = open(root, O_RDONLY|O_NOCTTY|O_CLOEXEC|O_DIRECTORY);
+ if (root_fd < 0)
return -errno;
}
- if (pidns_fd)
- *pidns_fd = TAKE_FD(pidnsfd);
+ if (ret_pidns_fd)
+ *ret_pidns_fd = TAKE_FD(pidns_fd);
- if (mntns_fd)
- *mntns_fd = TAKE_FD(mntnsfd);
+ if (ret_mntns_fd)
+ *ret_mntns_fd = TAKE_FD(mntns_fd);
- if (netns_fd)
- *netns_fd = TAKE_FD(netnsfd);
+ if (ret_netns_fd)
+ *ret_netns_fd = TAKE_FD(netns_fd);
- if (userns_fd)
- *userns_fd = TAKE_FD(usernsfd);
+ if (ret_userns_fd)
+ *ret_userns_fd = TAKE_FD(userns_fd);
- if (root_fd)
- *root_fd = TAKE_FD(rfd);
+ if (ret_root_fd)
+ *ret_root_fd = TAKE_FD(root_fd);
return 0;
}
@@ -206,6 +222,88 @@ int detach_mount_namespace(void) {
return 0;
}
+int detach_mount_namespace_harder(uid_t target_uid, gid_t target_gid) {
+ int r;
+
+ /* Tried detach_mount_namespace() first. If that doesn't work due to permissions, opens up an
+ * unprivileged user namespace with a mapping of the originating UID/GID to the specified target
+ * UID/GID. Then, tries detach_mount_namespace() again.
+ *
+ * Or in other words: tries much harder to get a mount namespace, making use of unprivileged user
+ * namespaces if need be.
+ *
+ * Note that after this function completed:
+ *
+ * → if we had privs, afterwards uids/gids on files and processes are as before
+ *
+ * → if we had no privs, our own id and all our files will show up owned by target_uid/target_gid,
+ * and everything else owned by nobody.
+ *
+ * Yes, that's quite a difference. */
+
+ if (!uid_is_valid(target_uid))
+ return -EINVAL;
+ if (!gid_is_valid(target_gid))
+ return -EINVAL;
+
+ r = detach_mount_namespace();
+ if (r != -EPERM)
+ return r;
+
+ if (unshare(CLONE_NEWUSER) < 0)
+ return log_debug_errno(errno, "Failed to acquire user namespace: %m");
+
+ r = write_string_filef("/proc/self/uid_map", 0,
+ UID_FMT " " UID_FMT " 1\n", target_uid, getuid());
+ if (r < 0)
+ return log_debug_errno(r, "Failed to write uid map: %m");
+
+ r = write_string_file("/proc/self/setgroups", "deny", 0);
+ if (r < 0)
+ return log_debug_errno(r, "Failed to write setgroups file: %m");
+
+ r = write_string_filef("/proc/self/gid_map", 0,
+ GID_FMT " " GID_FMT " 1\n", target_gid, getgid());
+ if (r < 0)
+ return log_debug_errno(r, "Failed to write gid map: %m");
+
+ return detach_mount_namespace();
+}
+
+int detach_mount_namespace_userns(int userns_fd) {
+ int r;
+
+ assert(userns_fd >= 0);
+
+ if (setns(userns_fd, CLONE_NEWUSER) < 0)
+ return log_debug_errno(errno, "Failed to join user namespace: %m");
+
+ r = reset_uid_gid();
+ if (r < 0)
+ return log_debug_errno(r, "Failed to become root in user namespace: %m");
+
+ return detach_mount_namespace();
+}
+
+int userns_acquire_empty(void) {
+ _cleanup_(sigkill_waitp) pid_t pid = 0;
+ _cleanup_close_ int userns_fd = -EBADF;
+ int r;
+
+ r = safe_fork("(sd-mkuserns)", FORK_CLOSE_ALL_FDS|FORK_DEATHSIG_SIGKILL|FORK_NEW_USERNS, &pid);
+ if (r < 0)
+ return r;
+ if (r == 0)
+ /* Child. We do nothing here, just freeze until somebody kills us. */
+ freeze();
+
+ r = namespace_open(pid, NULL, NULL, NULL, &userns_fd, NULL);
+ if (r < 0)
+ return log_error_errno(r, "Failed to open userns fd: %m");
+
+ return TAKE_FD(userns_fd);
+}
+
int userns_acquire(const char *uid_map, const char *gid_map) {
char path[STRLEN("/proc//uid_map") + DECIMAL_STR_MAX(pid_t) + 1];
_cleanup_(sigkill_waitp) pid_t pid = 0;
@@ -221,7 +319,7 @@ int userns_acquire(const char *uid_map, const char *gid_map) {
r = safe_fork("(sd-mkuserns)", FORK_CLOSE_ALL_FDS|FORK_DEATHSIG_SIGKILL|FORK_NEW_USERNS, &pid);
if (r < 0)
- return r;
+ return log_debug_errno(r, "Failed to fork process (sd-mkuserns): %m");
if (r == 0)
/* Child. We do nothing here, just freeze until somebody kills us. */
freeze();
@@ -229,19 +327,50 @@ int userns_acquire(const char *uid_map, const char *gid_map) {
xsprintf(path, "/proc/" PID_FMT "/uid_map", pid);
r = write_string_file(path, uid_map, WRITE_STRING_FILE_DISABLE_BUFFER);
if (r < 0)
- return log_error_errno(r, "Failed to write UID map: %m");
+ return log_debug_errno(r, "Failed to write UID map: %m");
xsprintf(path, "/proc/" PID_FMT "/gid_map", pid);
r = write_string_file(path, gid_map, WRITE_STRING_FILE_DISABLE_BUFFER);
if (r < 0)
- return log_error_errno(r, "Failed to write GID map: %m");
-
- r = namespace_open(pid, NULL, NULL, NULL, &userns_fd, NULL);
+ return log_debug_errno(r, "Failed to write GID map: %m");
+
+ r = namespace_open(pid,
+ /* ret_pidns_fd = */ NULL,
+ /* ret_mntns_fd = */ NULL,
+ /* ret_netns_fd = */ NULL,
+ &userns_fd,
+ /* ret_root_fd = */ NULL);
if (r < 0)
- return log_error_errno(r, "Failed to open userns fd: %m");
+ return log_debug_errno(r, "Failed to open userns fd: %m");
return TAKE_FD(userns_fd);
+}
+int netns_acquire(void) {
+ _cleanup_(sigkill_waitp) pid_t pid = 0;
+ _cleanup_close_ int netns_fd = -EBADF;
+ int r;
+
+ /* Forks off a process in a new network namespace, acquires a network namespace fd, and then kills
+ * the process again. This way we have a netns fd that is not bound to any process. */
+
+ r = safe_fork("(sd-mknetns)", FORK_CLOSE_ALL_FDS|FORK_DEATHSIG_SIGKILL|FORK_NEW_NETNS, &pid);
+ if (r < 0)
+ return log_debug_errno(r, "Failed to fork process (sd-mknetns): %m");
+ if (r == 0)
+ /* Child. We do nothing here, just freeze until somebody kills us. */
+ freeze();
+
+ r = namespace_open(pid,
+ /* ret_pidns_fd = */ NULL,
+ /* ret_mntns_fd = */ NULL,
+ &netns_fd,
+ /* ret_userns_fd = */ NULL,
+ /* ret_root_fd = */ NULL);
+ if (r < 0)
+ return log_debug_errno(r, "Failed to open netns fd: %m");
+
+ return TAKE_FD(netns_fd);
}
int in_same_namespace(pid_t pid1, pid_t pid2, NamespaceType type) {
@@ -267,3 +396,88 @@ int in_same_namespace(pid_t pid1, pid_t pid2, NamespaceType type) {
return stat_inode_same(&ns_st1, &ns_st2);
}
+
+int parse_userns_uid_range(const char *s, uid_t *ret_uid_shift, uid_t *ret_uid_range) {
+ _cleanup_free_ char *buffer = NULL;
+ const char *range, *shift;
+ int r;
+ uid_t uid_shift, uid_range = 65536;
+
+ assert(s);
+
+ range = strchr(s, ':');
+ if (range) {
+ buffer = strndup(s, range - s);
+ if (!buffer)
+ return log_oom();
+ shift = buffer;
+
+ range++;
+ r = safe_atou32(range, &uid_range);
+ if (r < 0)
+ return log_error_errno(r, "Failed to parse UID range \"%s\": %m", range);
+ } else
+ shift = s;
+
+ r = parse_uid(shift, &uid_shift);
+ if (r < 0)
+ return log_error_errno(r, "Failed to parse UID \"%s\": %m", s);
+
+ if (!userns_shift_range_valid(uid_shift, uid_range))
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "UID range cannot be empty or go beyond " UID_FMT ".", UID_INVALID);
+
+ if (ret_uid_shift)
+ *ret_uid_shift = uid_shift;
+
+ if (ret_uid_range)
+ *ret_uid_range = uid_range;
+
+ return 0;
+}
+
+int namespace_open_by_type(NamespaceType type) {
+ const char *p;
+ int fd;
+
+ assert(type >= 0);
+ assert(type < _NAMESPACE_TYPE_MAX);
+
+ p = pid_namespace_path(0, type);
+
+ fd = RET_NERRNO(open(p, O_RDONLY|O_NOCTTY|O_CLOEXEC));
+ if (fd == -ENOENT && proc_mounted() == 0)
+ return -ENOSYS;
+
+ return fd;
+}
+
+int is_our_namespace(int fd, NamespaceType request_type) {
+ int clone_flag;
+
+ assert(fd >= 0);
+
+ clone_flag = ioctl(fd, NS_GET_NSTYPE);
+ if (clone_flag < 0)
+ return -errno;
+
+ NamespaceType found_type = clone_flag_to_namespace_type(clone_flag);
+ if (found_type < 0)
+ return -EBADF; /* Uh? Unknown namespace type? */
+
+ if (request_type >= 0 && request_type != found_type) /* It's a namespace, but not of the right type? */
+ return -EUCLEAN;
+
+ struct stat st_fd, st_ours;
+ if (fstat(fd, &st_fd) < 0)
+ return -errno;
+
+ const char *p = pid_namespace_path(0, found_type);
+ if (stat(p, &st_ours) < 0) {
+ if (errno == ENOENT)
+ return proc_mounted() == 0 ? -ENOSYS : -ENOENT;
+
+ return -errno;
+ }
+
+ return stat_inode_same(&st_ours, &st_fd);
+}
diff --git a/src/basic/namespace-util.h b/src/basic/namespace-util.h
index be5b228..545952a 100644
--- a/src/basic/namespace-util.h
+++ b/src/basic/namespace-util.h
@@ -22,12 +22,20 @@ extern const struct namespace_info {
unsigned int clone_flag;
} namespace_info[_NAMESPACE_TYPE_MAX + 1];
-int namespace_open(pid_t pid, int *pidns_fd, int *mntns_fd, int *netns_fd, int *userns_fd, int *root_fd);
+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);
+int detach_mount_namespace_harder(uid_t target_uid, gid_t target_gid);
+int detach_mount_namespace_userns(int userns_fd);
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
@@ -44,5 +52,15 @@ static inline bool userns_shift_range_valid(uid_t shift, uid_t range) {
return true;
}
+int userns_acquire_empty(void);
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);
+
+int parse_userns_uid_range(const char *s, uid_t *ret_uid_shift, uid_t *ret_uid_range);
+
+int namespace_open_by_type(NamespaceType type);
+
+int is_our_namespace(int fd, NamespaceType type);
diff --git a/src/basic/nulstr-util.c b/src/basic/nulstr-util.c
index 06fa219..7097a2c 100644
--- a/src/basic/nulstr-util.c
+++ b/src/basic/nulstr-util.c
@@ -4,7 +4,21 @@
#include "string-util.h"
#include "strv.h"
+const char* nulstr_get(const char *nulstr, const char *needle) {
+ if (!nulstr)
+ return NULL;
+
+ NULSTR_FOREACH(i, nulstr)
+ if (streq(i, needle))
+ return i;
+
+ return NULL;
+}
+
char** strv_parse_nulstr_full(const char *s, size_t l, bool drop_trailing_nuls) {
+ _cleanup_strv_free_ char **v = NULL;
+ size_t c = 0, i = 0;
+
/* l is the length of the input data, which will be split at NULs into elements of the resulting
* strv. Hence, the number of items in the resulting strv will be equal to one plus the number of NUL
* bytes in the l bytes starting at s, unless s[l-1] is NUL, in which case the final empty string is
@@ -13,9 +27,6 @@ char** strv_parse_nulstr_full(const char *s, size_t l, bool drop_trailing_nuls)
* Note that contrary to a normal nulstr which cannot contain empty strings, because the input data
* is terminated by any two consequent NUL bytes, this parser accepts empty strings in s. */
- _cleanup_strv_free_ char **v = NULL;
- size_t c = 0, i = 0;
-
assert(s || l <= 0);
if (drop_trailing_nuls)
@@ -36,7 +47,7 @@ char** strv_parse_nulstr_full(const char *s, size_t l, bool drop_trailing_nuls)
if (!v)
return NULL;
- for (const char *p = s; p < s + l; ) {
+ for (const char *p = s; p < s + l;) {
const char *e;
e = memchr(p, 0, s + l - p);
@@ -44,7 +55,6 @@ char** strv_parse_nulstr_full(const char *s, size_t l, bool drop_trailing_nuls)
v[i] = memdup_suffix0(p, e ? e - p : s + l - p);
if (!v[i])
return NULL;
-
i++;
if (!e)
@@ -74,6 +84,9 @@ char** strv_split_nulstr(const char *s) {
}
int strv_make_nulstr(char * const *l, char **ret, size_t *ret_size) {
+ _cleanup_free_ char *m = NULL;
+ size_t n = 0;
+
/* Builds a nulstr and returns it together with the size. An extra NUL byte will be appended (⚠️ but
* not included in the size! ⚠️). This is done so that the nulstr can be used both in
* strv_parse_nulstr() and in NULSTR_FOREACH()/strv_split_nulstr() contexts, i.e. with and without a
@@ -84,21 +97,18 @@ int strv_make_nulstr(char * const *l, char **ret, size_t *ret_size) {
* NUL bytes (which it will, if not empty). To ensure that this assumption *always* holds, we'll
* return a buffer with two NUL bytes in that case, but return a size of zero. */
- _cleanup_free_ char *m = NULL;
- size_t n = 0;
-
assert(ret);
STRV_FOREACH(i, l) {
size_t z;
- z = strlen(*i);
+ z = strlen(*i) + 1;
- if (!GREEDY_REALLOC(m, n + z + 2))
+ if (!GREEDY_REALLOC(m, n + z + 1)) /* One extra NUL at the end as marker */
return -ENOMEM;
- memcpy(m + n, *i, z + 1);
- n += z + 1;
+ memcpy(m + n, *i, z);
+ n += z;
}
if (!m) {
@@ -109,7 +119,7 @@ int strv_make_nulstr(char * const *l, char **ret, size_t *ret_size) {
n = 0;
} else
- /* Make sure there is a second extra NUL at the end of resulting nulstr (not counted in return size) */
+ /* Extra NUL is not counted in size returned */
m[n] = '\0';
*ret = TAKE_PTR(m);
@@ -132,14 +142,3 @@ int set_make_nulstr(Set *s, char **ret, size_t *ret_size) {
return strv_make_nulstr(strv, ret, ret_size);
}
-
-const char* nulstr_get(const char *nulstr, const char *needle) {
- if (!nulstr)
- return NULL;
-
- NULSTR_FOREACH(i, nulstr)
- if (streq(i, needle))
- return i;
-
- return NULL;
-}
diff --git a/src/basic/nulstr-util.h b/src/basic/nulstr-util.h
index d7bc5fd..d6f2f58 100644
--- a/src/basic/nulstr-util.h
+++ b/src/basic/nulstr-util.h
@@ -15,7 +15,6 @@
for (typeof(*(l)) *(i) = (l), *(j) = strchr((i), 0)+1; (i) && *(i); (i) = strchr((j), 0)+1, (j) = *(i) ? strchr((i), 0)+1 : (i))
const char* nulstr_get(const char *nulstr, const char *needle);
-
static inline bool nulstr_contains(const char *nulstr, const char *needle) {
return nulstr_get(nulstr, needle);
}
@@ -25,9 +24,6 @@ static inline char** strv_parse_nulstr(const char *s, size_t l) {
return strv_parse_nulstr_full(s, l, false);
}
char** strv_split_nulstr(const char *s);
-int strv_make_nulstr(char * const *l, char **p, size_t *n);
-int set_make_nulstr(Set *s, char **ret, size_t *ret_size);
-
static inline int strv_from_nulstr(char ***ret, const char *nulstr) {
char **t;
@@ -40,3 +36,6 @@ static inline int strv_from_nulstr(char ***ret, const char *nulstr) {
*ret = t;
return 0;
}
+
+int strv_make_nulstr(char * const *l, char **p, size_t *n);
+int set_make_nulstr(Set *s, char **ret, size_t *ret_size);
diff --git a/src/basic/ordered-set.c b/src/basic/ordered-set.c
index b4c2588..65cf3a0 100644
--- a/src/basic/ordered-set.c
+++ b/src/basic/ordered-set.c
@@ -91,13 +91,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/basic/os-util.c b/src/basic/os-util.c
index 985d89b..0d26d18 100644
--- a/src/basic/os-util.c
+++ b/src/basic/os-util.c
@@ -72,16 +72,11 @@ int path_extract_image_name(const char *path, char **ret) {
r = path_extract_filename(path, &fn);
if (r < 0)
return r;
-
if (r != O_DIRECTORY) {
- /* Chop off any image suffixes we recognize (unless we already know this must refer to some dir */
- FOREACH_STRING(suffix, ".sysext.raw", ".confext.raw", ".raw") {
- char *m = endswith(fn, suffix);
- if (m) {
- *m = 0;
- break;
- }
- }
+ /* Chop off any image suffixes we recognize (unless we already know this must refer to some dir) */
+ char *m = ENDSWITH_SET(fn, ".sysext.raw", ".confext.raw", ".raw");
+ if (m)
+ *m = 0;
}
/* Truncate the version/counting suffixes */
diff --git a/src/basic/parse-util.c b/src/basic/parse-util.c
index 0430e33..35d13eb 100644
--- a/src/basic/parse-util.c
+++ b/src/basic/parse-util.c
@@ -633,7 +633,7 @@ int parse_fractional_part_u(const char **p, size_t digits, unsigned *res) {
s = *p;
/* accept any number of digits, strtoull is limited to 19 */
- for (size_t i = 0; i < digits; i++,s++) {
+ for (size_t i = 0; i < digits; i++, s++) {
if (!ascii_isdigit(*s)) {
if (i == 0)
return -EINVAL;
@@ -691,7 +691,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;
@@ -699,7 +699,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)
diff --git a/src/basic/parse-util.h b/src/basic/parse-util.h
index 1845f0a..c12988e 100644
--- a/src/basic/parse-util.h
+++ b/src/basic/parse-util.h
@@ -139,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);
diff --git a/src/basic/path-lookup.c b/src/basic/path-lookup.c
index 4e3d59f..540256b 100644
--- a/src/basic/path-lookup.c
+++ b/src/basic/path-lookup.c
@@ -91,6 +91,37 @@ int xdg_user_data_dir(char **ret, const char *suffix) {
return 1;
}
+int runtime_directory(char **ret, RuntimeScope scope, const char *suffix) {
+ int r;
+
+ assert(ret);
+ assert(suffix);
+ assert(IN_SET(scope, RUNTIME_SCOPE_SYSTEM, RUNTIME_SCOPE_USER, RUNTIME_SCOPE_GLOBAL));
+
+ /* Accept $RUNTIME_DIRECTORY as authoritative
+ * If its missing apply the suffix to /run or $XDG_RUNTIME_DIR
+ * if we are in a user runtime scope.
+ *
+ * Return value indicates whether the suffix was applied or not */
+
+ const char *e = secure_getenv("RUNTIME_DIRECTORY");
+ if (e)
+ return strdup_to(ret, e);
+
+ if (scope == RUNTIME_SCOPE_USER) {
+ r = xdg_user_runtime_dir(ret, suffix);
+ if (r < 0)
+ return r;
+ } else {
+ char *d = path_join("/run", suffix);
+ if (!d)
+ return -ENOMEM;
+ *ret = d;
+ }
+
+ return true;
+}
+
static const char* const user_data_unit_paths[] = {
"/usr/local/lib/systemd/user",
"/usr/local/share/systemd/user",
@@ -167,22 +198,16 @@ static char** user_dirs(
return NULL;
/* Now merge everything we found. */
- if (strv_extend(&res, persistent_control) < 0)
+ if (strv_extend_many(
+ &res,
+ persistent_control,
+ runtime_control,
+ transient,
+ generator_early,
+ persistent_config) < 0)
return NULL;
- if (strv_extend(&res, runtime_control) < 0)
- return NULL;
-
- if (strv_extend(&res, transient) < 0)
- return NULL;
-
- if (strv_extend(&res, generator_early) < 0)
- return NULL;
-
- if (strv_extend(&res, persistent_config) < 0)
- return NULL;
-
- if (strv_extend_strv_concat(&res, config_dirs, "/systemd/user") < 0)
+ if (strv_extend_strv_concat(&res, (const char* const*) config_dirs, "/systemd/user") < 0)
return NULL;
/* global config has lower priority than the user config of the same type */
@@ -192,19 +217,15 @@ static char** user_dirs(
if (strv_extend_strv(&res, (char**) user_config_unit_paths, false) < 0)
return NULL;
- if (strv_extend(&res, runtime_config) < 0)
- return NULL;
-
- if (strv_extend(&res, global_runtime_config) < 0)
- return NULL;
-
- if (strv_extend(&res, generator) < 0)
- return NULL;
-
- if (strv_extend(&res, data_home) < 0)
+ if (strv_extend_many(
+ &res,
+ runtime_config,
+ global_runtime_config,
+ generator,
+ data_home) < 0)
return NULL;
- if (strv_extend_strv_concat(&res, data_dirs, "/systemd/user") < 0)
+ if (strv_extend_strv_concat(&res, (const char* const*) data_dirs, "/systemd/user") < 0)
return NULL;
if (strv_extend_strv(&res, (char**) user_data_unit_paths, false) < 0)
@@ -748,9 +769,8 @@ int lookup_paths_init_or_warn(LookupPaths *lp, RuntimeScope scope, LookupPathsFl
return r;
}
-void lookup_paths_free(LookupPaths *lp) {
- if (!lp)
- return;
+void lookup_paths_done(LookupPaths *lp) {
+ assert(lp);
lp->search_path = strv_free(lp->search_path);
diff --git a/src/basic/path-lookup.h b/src/basic/path-lookup.h
index 1601787..0db2c5a 100644
--- a/src/basic/path-lookup.h
+++ b/src/basic/path-lookup.h
@@ -59,12 +59,13 @@ int xdg_user_dirs(char ***ret_config_dirs, char ***ret_data_dirs);
int xdg_user_runtime_dir(char **ret, const char *suffix);
int xdg_user_config_dir(char **ret, const char *suffix);
int xdg_user_data_dir(char **ret, const char *suffix);
+int runtime_directory(char **ret, RuntimeScope scope, const char *suffix);
bool path_is_user_data_dir(const char *path);
bool path_is_user_config_dir(const char *path);
void lookup_paths_log(LookupPaths *p);
-void lookup_paths_free(LookupPaths *p);
+void lookup_paths_done(LookupPaths *p);
char **generator_binary_paths(RuntimeScope scope);
char **env_generator_binary_paths(RuntimeScope scope);
diff --git a/src/basic/path-util.c b/src/basic/path-util.c
index 6810bf6..068fb42 100644
--- a/src/basic/path-util.c
+++ b/src/basic/path-util.c
@@ -217,8 +217,10 @@ int path_make_relative_parent(const char *from_child, const char *to, char **ret
return path_make_relative(from, to, ret);
}
-char* path_startswith_strv(const char *p, char **set) {
- STRV_FOREACH(s, set) {
+char* path_startswith_strv(const char *p, char * const *strv) {
+ assert(p);
+
+ STRV_FOREACH(s, strv) {
char *t;
t = path_startswith(p, *s);
@@ -525,6 +527,18 @@ int path_compare_filename(const char *a, const char *b) {
return strcmp(fa, fb);
}
+int path_equal_or_inode_same_full(const char *a, const char *b, int flags) {
+ /* Returns true if paths are of the same entry, false if not, <0 on error. */
+
+ if (path_equal(a, b))
+ return 1;
+
+ if (!a || !b)
+ return 0;
+
+ return inode_same(a, b, flags);
+}
+
char* path_extend_internal(char **x, ...) {
size_t sz, old_sz;
char *q, *nx;
@@ -684,7 +698,7 @@ int find_executable_full(
* binary. */
p = getenv("PATH");
if (!p)
- p = DEFAULT_PATH;
+ p = default_PATH();
if (exec_search_path) {
STRV_FOREACH(element, exec_search_path) {
@@ -1094,7 +1108,6 @@ int path_extract_filename(const char *path, char **ret) {
}
int path_extract_directory(const char *path, char **ret) {
- _cleanup_free_ char *a = NULL;
const char *c, *next = NULL;
int r;
@@ -1118,14 +1131,10 @@ int path_extract_directory(const char *path, char **ret) {
if (*path != '/') /* filename only */
return -EDESTADDRREQ;
- a = strdup("/");
- if (!a)
- return -ENOMEM;
- *ret = TAKE_PTR(a);
- return 0;
+ return strdup_to(ret, "/");
}
- a = strndup(path, next - path);
+ _cleanup_free_ char *a = strndup(path, next - path);
if (!a)
return -ENOMEM;
@@ -1336,6 +1345,20 @@ bool dot_or_dot_dot(const char *path) {
return path[2] == 0;
}
+bool path_implies_directory(const char *path) {
+
+ /* Sometimes, if we look at a path we already know it must refer to a directory, because it is
+ * suffixed with a slash, or its last component is "." or ".." */
+
+ if (!path)
+ return false;
+
+ if (dot_or_dot_dot(path))
+ return true;
+
+ return ENDSWITH_SET(path, "/", "/.", "/..");
+}
+
bool empty_or_root(const char *path) {
/* For operations relative to some root directory, returns true if the specified root directory is
@@ -1347,7 +1370,9 @@ bool empty_or_root(const char *path) {
return path_equal(path, "/");
}
-bool path_strv_contains(char **l, const char *path) {
+bool path_strv_contains(char * const *l, const char *path) {
+ assert(path);
+
STRV_FOREACH(i, l)
if (path_equal(*i, path))
return true;
@@ -1355,7 +1380,9 @@ bool path_strv_contains(char **l, const char *path) {
return false;
}
-bool prefixed_path_strv_contains(char **l, const char *path) {
+bool prefixed_path_strv_contains(char * const *l, const char *path) {
+ assert(path);
+
STRV_FOREACH(i, l) {
const char *j = *i;
@@ -1363,6 +1390,7 @@ bool prefixed_path_strv_contains(char **l, const char *path) {
j++;
if (*j == '+')
j++;
+
if (path_equal(j, path))
return true;
}
@@ -1432,3 +1460,31 @@ int path_glob_can_match(const char *pattern, const char *prefix, char **ret) {
*ret = NULL;
return false;
}
+
+const char* default_PATH(void) {
+#if HAVE_SPLIT_BIN
+ static int split = -1;
+ int r;
+
+ /* Check whether /usr/sbin is not a symlink and return the appropriate $PATH.
+ * On error fall back to the safe value with both directories as configured… */
+
+ if (split < 0)
+ STRV_FOREACH_PAIR(bin, sbin, STRV_MAKE("/usr/bin", "/usr/sbin",
+ "/usr/local/bin", "/usr/local/sbin")) {
+ r = inode_same(*bin, *sbin, AT_NO_AUTOMOUNT);
+ if (r > 0 || r == -ENOENT)
+ continue;
+ if (r < 0)
+ log_debug_errno(r, "Failed to compare \"%s\" and \"%s\", using compat $PATH: %m",
+ *bin, *sbin);
+ split = true;
+ break;
+ }
+ if (split < 0)
+ split = false;
+ if (split)
+ return DEFAULT_PATH_WITH_SBIN;
+#endif
+ return DEFAULT_PATH_WITHOUT_SBIN;
+}
diff --git a/src/basic/path-util.h b/src/basic/path-util.h
index 6d943e9..792b8ff 100644
--- a/src/basic/path-util.h
+++ b/src/basic/path-util.h
@@ -11,27 +11,26 @@
#include "strv.h"
#include "time-util.h"
-#define PATH_SPLIT_SBIN_BIN(x) x "sbin:" x "bin"
-#define PATH_SPLIT_SBIN_BIN_NULSTR(x) x "sbin\0" x "bin\0"
+#define PATH_SPLIT_BIN(x) x "sbin:" x "bin"
+#define PATH_SPLIT_BIN_NULSTR(x) x "sbin\0" x "bin\0"
-#define PATH_NORMAL_SBIN_BIN(x) x "bin"
-#define PATH_NORMAL_SBIN_BIN_NULSTR(x) x "bin\0"
+#define PATH_MERGED_BIN(x) x "bin"
+#define PATH_MERGED_BIN_NULSTR(x) x "bin\0"
-#if HAVE_SPLIT_BIN
-# define PATH_SBIN_BIN(x) PATH_SPLIT_SBIN_BIN(x)
-# define PATH_SBIN_BIN_NULSTR(x) PATH_SPLIT_SBIN_BIN_NULSTR(x)
-#else
-# define PATH_SBIN_BIN(x) PATH_NORMAL_SBIN_BIN(x)
-# define PATH_SBIN_BIN_NULSTR(x) PATH_NORMAL_SBIN_BIN_NULSTR(x)
-#endif
+#define DEFAULT_PATH_WITH_SBIN PATH_SPLIT_BIN("/usr/local/") ":" PATH_SPLIT_BIN("/usr/")
+#define DEFAULT_PATH_WITHOUT_SBIN PATH_MERGED_BIN("/usr/local/") ":" PATH_MERGED_BIN("/usr/")
+
+#define DEFAULT_PATH_COMPAT PATH_SPLIT_BIN("/usr/local/") ":" PATH_SPLIT_BIN("/usr/") ":" PATH_SPLIT_BIN("/")
-#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("/")
+const char* default_PATH(void);
-#ifndef DEFAULT_USER_PATH
-# define DEFAULT_USER_PATH DEFAULT_PATH
+static inline const char* default_user_PATH(void) {
+#ifdef DEFAULT_USER_PATH
+ return DEFAULT_USER_PATH;
+#else
+ return default_PATH();
#endif
+}
static inline bool is_path(const char *p) {
if (!p) /* A NULL pointer is definitely not a path */
@@ -68,14 +67,19 @@ static inline bool path_equal_filename(const char *a, const char *b) {
return path_compare_filename(a, b) == 0;
}
+int path_equal_or_inode_same_full(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;
+ return path_equal_or_inode_same_full(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)
+static inline char* skip_leading_slash(const char *p) {
+ return skip_leading_chars(p, "/");
+}
+
typedef enum PathSimplifyFlags {
PATH_SIMPLIFY_KEEP_TRAILING_SLASH = 1 << 0,
} PathSimplifyFlags;
@@ -101,14 +105,10 @@ static inline int path_simplify_alloc(const char *path, char **ret) {
return 0;
}
-static inline bool path_equal_ptr(const char *a, const char *b) {
- return !!a == !!b && (!a || path_equal(a, b));
-}
-
/* Note: the search terminates on the first NULL item. */
#define PATH_IN_SET(p, ...) path_strv_contains(STRV_MAKE(__VA_ARGS__), p)
-char* path_startswith_strv(const char *p, char **set);
+char* path_startswith_strv(const char *p, char * const *strv);
#define PATH_STARTSWITH_SET(p, ...) path_startswith_strv(p, STRV_MAKE(__VA_ARGS__))
int path_strv_make_absolute_cwd(char **l);
@@ -156,7 +156,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 { \
@@ -201,6 +201,8 @@ bool valid_device_allow_pattern(const char *path);
bool dot_or_dot_dot(const char *path);
+bool path_implies_directory(const char *path);
+
static inline const char *skip_dev_prefix(const char *p) {
const char *e;
@@ -216,7 +218,7 @@ static inline const char* empty_to_root(const char *path) {
return isempty(path) ? "/" : path;
}
-bool path_strv_contains(char **l, const char *path);
-bool prefixed_path_strv_contains(char **l, const char *path);
+bool path_strv_contains(char * const *l, const char *path);
+bool prefixed_path_strv_contains(char * const *l, const char *path);
int path_glob_can_match(const char *pattern, const char *prefix, char **ret);
diff --git a/src/basic/pidref.c b/src/basic/pidref.c
index 69b5cad..69a0102 100644
--- a/src/basic/pidref.c
+++ b/src/basic/pidref.c
@@ -1,12 +1,44 @@
/* SPDX-License-Identifier: LGPL-2.1-or-later */
+#if HAVE_PIDFD_OPEN
+#include <sys/pidfd.h>
+#endif
+
#include "errno-util.h"
#include "fd-util.h"
#include "missing_syscall.h"
+#include "missing_wait.h"
#include "parse-util.h"
#include "pidref.h"
#include "process-util.h"
#include "signal-util.h"
+#include "stat-util.h"
+
+bool pidref_equal(const PidRef *a, const PidRef *b) {
+ int r;
+
+ if (pidref_is_set(a)) {
+ if (!pidref_is_set(b))
+ return false;
+
+ if (a->pid != b->pid)
+ return false;
+
+ if (a->fd < 0 || b->fd < 0)
+ return true;
+
+ /* pidfds live in their own pidfs and each process comes with a unique inode number since
+ * kernel 6.8. We can safely do this on older kernels too though, as previously anonymous
+ * inode was used and inode number was the same for all pidfds. */
+ r = fd_inode_same(a->fd, b->fd);
+ if (r < 0)
+ log_debug_errno(r, "Failed to check whether pidfds for pid " PID_FMT " are equal, assuming yes: %m",
+ a->pid);
+ return r != 0;
+ }
+
+ return !pidref_is_set(b);
+}
int pidref_set_pid(PidRef *pidref, pid_t pid) {
int fd;
@@ -22,7 +54,7 @@ int pidref_set_pid(PidRef *pidref, pid_t pid) {
if (fd < 0) {
/* Graceful fallback in case the kernel doesn't support pidfds or is out of fds */
if (!ERRNO_IS_NOT_SUPPORTED(errno) && !ERRNO_IS_PRIVILEGE(errno) && !ERRNO_IS_RESOURCE(errno))
- return -errno;
+ return log_debug_errno(errno, "Failed to open pidfd for pid " PID_FMT ": %m", pid);
fd = -EBADF;
}
@@ -106,6 +138,38 @@ int pidref_set_pidfd_consume(PidRef *pidref, int fd) {
return r;
}
+int pidref_set_parent(PidRef *ret) {
+ _cleanup_(pidref_done) PidRef parent = PIDREF_NULL;
+ pid_t ppid;
+ int r;
+
+ assert(ret);
+
+ /* Acquires a pidref to our parent process. Deals with the fact that parent processes might exit, and
+ * we get reparented to other processes, with our old parent's PID already being recycled. */
+
+ ppid = getppid();
+ for (;;) {
+ r = pidref_set_pid(&parent, ppid);
+ if (r < 0)
+ return r;
+
+ if (parent.fd < 0) /* If pidfds are not available, then we are done */
+ break;
+
+ pid_t now_ppid = getppid();
+ if (now_ppid == ppid) /* If our ppid is still the same, then we are done */
+ break;
+
+ /* Otherwise let's try again with the new ppid */
+ ppid = now_ppid;
+ pidref_done(&parent);
+ }
+
+ *ret = TAKE_PIDREF(parent);
+ return 0;
+}
+
void pidref_done(PidRef *pidref) {
assert(pidref);
@@ -123,11 +187,11 @@ PidRef *pidref_free(PidRef *pidref) {
return mfree(pidref);
}
-int pidref_dup(const PidRef *pidref, PidRef **ret) {
+int pidref_copy(const PidRef *pidref, PidRef *dest) {
_cleanup_close_ int dup_fd = -EBADF;
pid_t dup_pid = 0;
- assert(ret);
+ assert(dest);
/* Allocates a new PidRef on the heap, making it a copy of the specified pidref. This does not try to
* acquire a pidfd if we don't have one yet!
@@ -150,21 +214,34 @@ int pidref_dup(const PidRef *pidref, PidRef **ret) {
dup_pid = pidref->pid;
}
- PidRef *dup_pidref = new(PidRef, 1);
- if (!dup_pidref)
- return -ENOMEM;
-
- *dup_pidref = (PidRef) {
+ *dest = (PidRef) {
.fd = TAKE_FD(dup_fd),
.pid = dup_pid,
};
+ return 0;
+}
+
+int pidref_dup(const PidRef *pidref, PidRef **ret) {
+ _cleanup_(pidref_freep) PidRef *dup_pidref = NULL;
+ int r;
+
+ assert(ret);
+
+ dup_pidref = newdup(PidRef, &PIDREF_NULL, 1);
+ if (!dup_pidref)
+ return -ENOMEM;
+
+ r = pidref_copy(pidref, dup_pidref);
+ if (r < 0)
+ return r;
+
*ret = TAKE_PTR(dup_pidref);
return 0;
}
int pidref_new_from_pid(pid_t pid, PidRef **ret) {
- _cleanup_(pidref_freep) PidRef *n = 0;
+ _cleanup_(pidref_freep) PidRef *n = NULL;
int r;
assert(ret);
@@ -270,8 +347,46 @@ bool pidref_is_self(const PidRef *pidref) {
return pidref->pid == getpid_cached();
}
+int pidref_wait(const PidRef *pidref, siginfo_t *ret, int options) {
+ int r;
+
+ if (!pidref_is_set(pidref))
+ return -ESRCH;
+
+ if (pidref->pid == 1 || pidref->pid == getpid_cached())
+ return -ECHILD;
+
+ siginfo_t si = {};
+
+ if (pidref->fd >= 0) {
+ r = RET_NERRNO(waitid(P_PIDFD, pidref->fd, &si, options));
+ if (r >= 0) {
+ if (ret)
+ *ret = si;
+ return r;
+ }
+ if (r != -EINVAL) /* P_PIDFD was added in kernel 5.4 only */
+ return r;
+ }
+
+ r = RET_NERRNO(waitid(P_PID, pidref->pid, &si, options));
+ if (r >= 0 && ret)
+ *ret = si;
+ return r;
+}
+
+int pidref_wait_for_terminate(const PidRef *pidref, siginfo_t *ret) {
+ int r;
+
+ for (;;) {
+ r = pidref_wait(pidref, ret, WEXITED);
+ if (r != -EINTR)
+ return r;
+ }
+}
+
static void pidref_hash_func(const PidRef *pidref, struct siphash *state) {
- siphash24_compress(&pidref->pid, sizeof(pidref->pid), state);
+ siphash24_compress_typesafe(pidref->pid, state);
}
static int pidref_compare_func(const PidRef *a, const PidRef *b) {
diff --git a/src/basic/pidref.h b/src/basic/pidref.h
index dada069..9920ebb 100644
--- a/src/basic/pidref.h
+++ b/src/basic/pidref.h
@@ -19,17 +19,7 @@ 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);
-}
+bool pidref_equal(const PidRef *a, const PidRef *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.) */
@@ -38,7 +28,7 @@ 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);
}
@@ -49,13 +39,26 @@ 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 *pidfref, int sig, int value);
+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);
diff --git a/src/basic/proc-cmdline.c b/src/basic/proc-cmdline.c
index 522d8de..ce1ba3a 100644
--- a/src/basic/proc-cmdline.c
+++ b/src/basic/proc-cmdline.c
@@ -116,16 +116,8 @@ int proc_cmdline(char **ret) {
/* For testing purposes it is sometimes useful to be able to override what we consider /proc/cmdline to be */
e = secure_getenv("SYSTEMD_PROC_CMDLINE");
- if (e) {
- char *m;
-
- m = strdup(e);
- if (!m)
- return -ENOMEM;
-
- *ret = m;
- return 0;
- }
+ if (e)
+ return strdup_to(ret, e);
if (detect_container() > 0)
return pid_get_cmdline(1, SIZE_MAX, 0, ret);
diff --git a/src/basic/process-util.c b/src/basic/process-util.c
index 4492e7d..c9d968d 100644
--- a/src/basic/process-util.c
+++ b/src/basic/process-util.c
@@ -25,6 +25,7 @@
#include "alloc-util.h"
#include "architecture.h"
#include "argv-util.h"
+#include "cgroup-util.h"
#include "dirent-util.h"
#include "env-file.h"
#include "env-util.h"
@@ -510,7 +511,7 @@ static int get_process_link_contents(pid_t pid, const char *proc_file, char **re
p = procfs_file_alloca(pid, proc_file);
r = readlink_malloc(p, ret);
- return r == -ENOENT ? -ESRCH : r;
+ return (r == -ENOENT && proc_mounted() > 0) ? -ESRCH : r;
}
int get_process_exe(pid_t pid, char **ret) {
@@ -730,6 +731,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;
@@ -946,31 +1023,16 @@ int kill_and_sigcont(pid_t pid, int sig) {
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);
assert(field);
assert(ret);
- if (pid == 0 || pid == getpid_cached()) {
- const char *e;
-
- e = getenv(field);
- if (!e) {
- *ret = NULL;
- return 0;
- }
-
- value = strdup(e);
- if (!value)
- return -ENOMEM;
-
- *ret = value;
- return 1;
- }
+ if (pid == 0 || pid == getpid_cached())
+ return strdup_to_full(ret, getenv(field));
if (!pid_is_valid(pid))
return -EINVAL;
@@ -983,9 +1045,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;
@@ -998,14 +1060,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);
- if (!value)
- return -ENOMEM;
-
- *ret = value;
- return 1;
- }
+ match = startswith(line, field);
+ if (match && *match == '=')
+ return strdup_to_full(ret, match + 1);
}
*ret = NULL;
@@ -1112,8 +1169,10 @@ int pidref_is_alive(const PidRef *pidref) {
return -ESRCH;
result = pid_is_alive(pidref->pid);
- if (result < 0)
+ if (result < 0) {
+ assert(result != -ESRCH);
return result;
+ }
r = pidref_verify(pidref);
if (r == -ESRCH)
@@ -1224,7 +1283,7 @@ int opinionated_personality(unsigned long *ret) {
if (current < 0)
return current;
- if (((unsigned long) current & 0xffff) == PER_LINUX32)
+ if (((unsigned long) current & OPINIONATED_PERSONALITY_MASK) == PER_LINUX32)
*ret = PER_LINUX32;
else
*ret = PER_LINUX;
@@ -1389,7 +1448,7 @@ static int fork_flags_to_signal(ForkFlags flags) {
int safe_fork_full(
const char *name,
const int stdio_fds[3],
- const int except_fds[],
+ int except_fds[],
size_t n_except_fds,
ForkFlags flags,
pid_t *ret_pid) {
@@ -1462,10 +1521,11 @@ int safe_fork_full(
}
}
- if ((flags & (FORK_NEW_MOUNTNS|FORK_NEW_USERNS)) != 0)
+ 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)
@@ -1589,6 +1649,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) {
@@ -1614,6 +1677,19 @@ int safe_fork_full(
}
}
+ if (flags & FORK_PACK_FDS) {
+ /* FORK_CLOSE_ALL_FDS ensures that except_fds are the only FDs >= 3 that are
+ * open, this is including the log. This is required by pack_fds, which will
+ * get stuck in an infinite loop of any FDs other than except_fds are open. */
+ assert(FLAGS_SET(flags, FORK_CLOSE_ALL_FDS));
+
+ r = pack_fds(except_fds, n_except_fds);
+ if (r < 0) {
+ log_full_errno(prio, r, "Failed to pack file descriptors: %m");
+ _exit(EXIT_FAILURE);
+ }
+ }
+
if (flags & FORK_CLOEXEC_OFF) {
r = fd_cloexec_many(except_fds, n_except_fds, false);
if (r < 0) {
@@ -1650,10 +1726,34 @@ int safe_fork_full(
return 0;
}
+int pidref_safe_fork_full(
+ const char *name,
+ const int stdio_fds[3],
+ 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,
- const int except_fds[],
+ int except_fds[],
size_t n_except_fds,
ForkFlags flags,
int pidns_fd,
@@ -1927,47 +2027,115 @@ int make_reaper_process(bool b) {
return 0;
}
-int posix_spawn_wrapper(const char *path, char *const *argv, char *const *envp, pid_t *ret_pid) {
+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;
- pid_t pid;
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. */
+ * 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_pid);
+ 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 */
- /* Set all signals to SIG_DFL */
- r = posix_spawnattr_setflags(&attr, POSIX_SPAWN_SETSIGMASK|POSIX_SPAWN_SETSIGDEF);
+
+ /* 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)
- goto fail;
+ return -r;
r = posix_spawnattr_setsigmask(&attr, &mask);
if (r != 0)
- goto fail;
+ return -r;
- r = posix_spawn(&pid, path, NULL, &attr, argv, envp);
+#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)) {
+ /* clone3() could also return EOPNOTSUPP if the target cgroup is in threaded mode. */
+ if (cgroup && cg_is_threaded(cgroup) > 0)
+ return -EUCLEAN;
+
+ /* clone3() not available? */
+ } else if (!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)
- goto fail;
+ return -r;
+#endif
- *ret_pid = pid;
+ pid_t pid;
+ r = posix_spawn(&pid, path, NULL, &attr, argv, envp);
+ if (r != 0)
+ return -r;
- posix_spawnattr_destroy(&attr);
- return 0;
+ r = pidref_set_pid(ret_pidref, pid);
+ if (r < 0)
+ return r;
-fail:
- assert(r > 0);
- posix_spawnattr_destroy(&attr);
- 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) {
diff --git a/src/basic/process-util.h b/src/basic/process-util.h
index af6cba1..8308402 100644
--- a/src/basic/process-util.h
+++ b/src/basic/process-util.h
@@ -54,6 +54,8 @@ 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);
@@ -99,12 +101,17 @@ bool is_main_thread(void);
bool oom_score_adjust_is_valid(int oa);
#ifndef PERSONALITY_INVALID
-/* personality(7) documents that 0xffffffffUL is used for querying the
+/* personality(2) documents that 0xFFFFFFFFUL is used for querying the
* current personality, hence let's use that here as error
* indicator. */
-#define PERSONALITY_INVALID 0xffffffffLU
+#define PERSONALITY_INVALID 0xFFFFFFFFUL
#endif
+/* The personality() syscall returns a 32-bit value where the top three bytes are reserved for flags that
+ * emulate historical or architectural quirks, and only the least significant byte reflects the actual
+ * personality we're interested in. */
+#define OPINIONATED_PERSONALITY_MASK 0xFFUL
+
unsigned long personality_from_string(const char *p);
const char *personality_to_string(unsigned long);
@@ -152,11 +159,11 @@ 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 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(). 💣 */
+/* 💣 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 */
@@ -177,12 +184,14 @@ typedef enum ForkFlags {
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! 💣 */
+ FORK_PACK_FDS = 1 << 20, /* Rearrange the passed FDs to be FD 3,4,5,etc. Updates the array in place (combine with FORK_CLOSE_ALL_FDS!) */
} ForkFlags;
int safe_fork_full(
const char *name,
const int stdio_fds[3],
- const int except_fds[],
+ int except_fds[],
size_t n_except_fds,
ForkFlags flags,
pid_t *ret_pid);
@@ -191,7 +200,30 @@ 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 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 pidref_safe_fork_full(
+ const char *name,
+ const int stdio_fds[3],
+ 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,
+ 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);
int get_oom_score_adjust(int *ret);
@@ -223,7 +255,12 @@ 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, pid_t *ret_pid);
+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);
diff --git a/src/basic/recurse-dir.c b/src/basic/recurse-dir.c
index 5e98b7a..7767331 100644
--- a/src/basic/recurse-dir.c
+++ b/src/basic/recurse-dir.c
@@ -4,6 +4,7 @@
#include "dirent-util.h"
#include "fd-util.h"
#include "fileio.h"
+#include "fs-util.h"
#include "missing_syscall.h"
#include "mountpoint-util.h"
#include "recurse-dir.h"
@@ -132,6 +133,18 @@ int readdir_all(int dir_fd,
return 0;
}
+int readdir_all_at(int fd, const char *path, RecurseDirFlags flags, DirectoryEntries **ret) {
+ _cleanup_close_ int dir_fd = -EBADF;
+
+ assert(fd >= 0 || fd == AT_FDCWD);
+
+ dir_fd = xopenat(fd, path, O_DIRECTORY|O_CLOEXEC);
+ if (dir_fd < 0)
+ return dir_fd;
+
+ return readdir_all(dir_fd, flags, ret);
+}
+
int recurse_dir(
int dir_fd,
const char *path,
diff --git a/src/basic/recurse-dir.h b/src/basic/recurse-dir.h
index 9f6a7ad..aaeae95 100644
--- a/src/basic/recurse-dir.h
+++ b/src/basic/recurse-dir.h
@@ -76,6 +76,7 @@ typedef struct DirectoryEntries {
} DirectoryEntries;
int readdir_all(int dir_fd, RecurseDirFlags flags, DirectoryEntries **ret);
+int readdir_all_at(int fd, const char *path, RecurseDirFlags flags, DirectoryEntries **ret);
int recurse_dir(int dir_fd, const char *path, unsigned statx_mask, unsigned n_depth_max, RecurseDirFlags flags, recurse_dir_func_t func, void *userdata);
int recurse_dir_at(int atfd, const char *path, unsigned statx_mask, unsigned n_depth_max, RecurseDirFlags flags, recurse_dir_func_t func, void *userdata);
diff --git a/src/basic/rlimit-util.c b/src/basic/rlimit-util.c
index c1f0b2b..a9f7b87 100644
--- a/src/basic/rlimit-util.c
+++ b/src/basic/rlimit-util.c
@@ -6,11 +6,14 @@
#include "errno-util.h"
#include "extract-word.h"
#include "fd-util.h"
+#include "fileio.h"
#include "format-util.h"
#include "macro.h"
#include "missing_resource.h"
+#include "process-util.h"
#include "rlimit-util.h"
#include "string-table.h"
+#include "strv.h"
#include "time-util.h"
int setrlimit_closest(int resource, const struct rlimit *rlim) {
@@ -426,3 +429,116 @@ int rlimit_nofile_safe(void) {
return 1;
}
+
+int pid_getrlimit(pid_t pid, int resource, struct rlimit *ret) {
+
+ static const char * const prefix_table[_RLIMIT_MAX] = {
+ [RLIMIT_CPU] = "Max cpu time",
+ [RLIMIT_FSIZE] = "Max file size",
+ [RLIMIT_DATA] = "Max data size",
+ [RLIMIT_STACK] = "Max stack size",
+ [RLIMIT_CORE] = "Max core file size",
+ [RLIMIT_RSS] = "Max resident set",
+ [RLIMIT_NPROC] = "Max processes",
+ [RLIMIT_NOFILE] = "Max open files",
+ [RLIMIT_MEMLOCK] = "Max locked memory",
+ [RLIMIT_AS] = "Max address space",
+ [RLIMIT_LOCKS] = "Max file locks",
+ [RLIMIT_SIGPENDING] = "Max pending signals",
+ [RLIMIT_MSGQUEUE] = "Max msgqueue size",
+ [RLIMIT_NICE] = "Max nice priority",
+ [RLIMIT_RTPRIO] = "Max realtime priority",
+ [RLIMIT_RTTIME] = "Max realtime timeout",
+ };
+
+ int r;
+
+ assert(resource >= 0);
+ assert(resource < _RLIMIT_MAX);
+ assert(pid >= 0);
+ assert(ret);
+
+ if (pid == 0 || pid == getpid_cached())
+ return RET_NERRNO(getrlimit(resource, ret));
+
+ r = RET_NERRNO(prlimit(pid, resource, /* new_limit= */ NULL, ret));
+ if (!ERRNO_IS_NEG_PRIVILEGE(r))
+ return r;
+
+ /* We don't have access? Then try to go via /proc/$PID/limits. Weirdly that's world readable in
+ * contrast to querying the data via prlimit() */
+
+ const char *p = procfs_file_alloca(pid, "limits");
+ _cleanup_free_ char *limits = NULL;
+
+ r = read_full_virtual_file(p, &limits, NULL);
+ if (r < 0)
+ return -EPERM; /* propagate original permission error if we can't access the limits file */
+
+ _cleanup_strv_free_ char **l = NULL;
+ l = strv_split(limits, "\n");
+ if (!l)
+ return -ENOMEM;
+
+ STRV_FOREACH(i, strv_skip(l, 1)) {
+ _cleanup_free_ char *soft = NULL, *hard = NULL;
+ uint64_t sv, hv;
+ const char *e;
+
+ e = startswith(*i, prefix_table[resource]);
+ if (!e)
+ continue;
+
+ if (*e != ' ')
+ continue;
+
+ e += strspn(e, WHITESPACE);
+
+ size_t n;
+ n = strcspn(e, WHITESPACE);
+ if (n == 0)
+ continue;
+
+ soft = strndup(e, n);
+ if (!soft)
+ return -ENOMEM;
+
+ e += n;
+ if (*e != ' ')
+ continue;
+
+ e += strspn(e, WHITESPACE);
+ n = strcspn(e, WHITESPACE);
+ if (n == 0)
+ continue;
+
+ hard = strndup(e, n);
+ if (!hard)
+ return -ENOMEM;
+
+ if (streq(soft, "unlimited"))
+ sv = RLIM_INFINITY;
+ else {
+ r = safe_atou64(soft, &sv);
+ if (r < 0)
+ return r;
+ }
+
+ if (streq(hard, "unlimited"))
+ hv = RLIM_INFINITY;
+ else {
+ r = safe_atou64(hard, &hv);
+ if (r < 0)
+ return r;
+ }
+
+ *ret = (struct rlimit) {
+ .rlim_cur = sv,
+ .rlim_max = hv,
+ };
+
+ return 0;
+ }
+
+ return -ENOTRECOVERABLE;
+}
diff --git a/src/basic/rlimit-util.h b/src/basic/rlimit-util.h
index 202c3fd..afc1a1f 100644
--- a/src/basic/rlimit-util.h
+++ b/src/basic/rlimit-util.h
@@ -25,3 +25,5 @@ void rlimit_free_all(struct rlimit **rl);
int rlimit_nofile_bump(int limit);
int rlimit_nofile_safe(void);
+
+int pid_getrlimit(pid_t pid, int resource, struct rlimit *ret);
diff --git a/src/basic/sha256.c b/src/basic/sha256.c
new file mode 100644
index 0000000..f011695
--- /dev/null
+++ b/src/basic/sha256.c
@@ -0,0 +1,50 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+
+#include <unistd.h>
+
+#include "hexdecoct.h"
+#include "macro.h"
+#include "sha256.h"
+
+int sha256_fd(int fd, uint64_t max_size, uint8_t ret[static SHA256_DIGEST_SIZE]) {
+ struct sha256_ctx ctx;
+ uint64_t total_size = 0;
+
+ sha256_init_ctx(&ctx);
+
+ for (;;) {
+ uint8_t buffer[64 * 1024];
+ ssize_t n;
+
+ n = read(fd, buffer, sizeof(buffer));
+ if (n < 0)
+ return -errno;
+ if (n == 0)
+ break;
+
+ if (!INC_SAFE(&total_size, n) || total_size > max_size)
+ return -EFBIG;
+
+ sha256_process_bytes(buffer, n, &ctx);
+ }
+
+ sha256_finish_ctx(&ctx, ret);
+ return 0;
+}
+
+int parse_sha256(const char *s, uint8_t ret[static SHA256_DIGEST_SIZE]) {
+ _cleanup_free_ uint8_t *data = NULL;
+ size_t size = 0;
+ int r;
+
+ if (!sha256_is_valid(s))
+ return -EINVAL;
+
+ r = unhexmem_full(s, SHA256_DIGEST_SIZE * 2, false, (void**) &data, &size);
+ if (r < 0)
+ return r;
+ assert(size == SHA256_DIGEST_SIZE);
+
+ memcpy(ret, data, size);
+ return 0;
+}
diff --git a/src/basic/sha256.h b/src/basic/sha256.h
new file mode 100644
index 0000000..95bac1b
--- /dev/null
+++ b/src/basic/sha256.h
@@ -0,0 +1,16 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+
+#pragma once
+
+#include <stdint.h>
+
+#include "sha256-fundamental.h"
+#include "string-util.h"
+
+int sha256_fd(int fd, uint64_t max_size, uint8_t ret[static SHA256_DIGEST_SIZE]);
+
+int parse_sha256(const char *s, uint8_t res[static SHA256_DIGEST_SIZE]);
+
+static inline bool sha256_is_valid(const char *s) {
+ return s && in_charset(s, HEXDIGITS) && (strlen(s) == SHA256_DIGEST_SIZE * 2);
+}
diff --git a/src/basic/signal-util.c b/src/basic/signal-util.c
index 5d948462..27d094b 100644
--- a/src/basic/signal-util.c
+++ b/src/basic/signal-util.c
@@ -18,7 +18,7 @@ int reset_all_signal_handlers(void) {
.sa_handler = SIG_DFL,
.sa_flags = SA_RESTART,
};
- int r = 0;
+ int ret = 0, r;
for (int sig = 1; sig < _NSIG; sig++) {
@@ -26,14 +26,14 @@ int reset_all_signal_handlers(void) {
if (IN_SET(sig, SIGKILL, SIGSTOP))
continue;
- /* On Linux the first two RT signals are reserved by
- * glibc, and sigaction() will return EINVAL for them. */
- if (sigaction(sig, &sa, NULL) < 0)
- if (errno != EINVAL && r >= 0)
- r = -errno;
+ /* On Linux the first two RT signals are reserved by glibc, and sigaction() will return
+ * EINVAL for them. */
+ r = RET_NERRNO(sigaction(sig, &sa, NULL));
+ if (r != -EINVAL)
+ RET_GATHER(ret, r);
}
- return r;
+ return ret;
}
int reset_signal_mask(void) {
@@ -57,10 +57,7 @@ int sigaction_many_internal(const struct sigaction *sa, ...) {
if (sig == 0)
continue;
- if (sigaction(sig, sa, NULL) < 0) {
- if (r >= 0)
- r = -errno;
- }
+ RET_GATHER(r, RET_NERRNO(sigaction(sig, sa, NULL)));
}
va_end(ap);
@@ -87,7 +84,7 @@ static int sigset_add_many_ap(sigset_t *ss, va_list ap) {
return r;
}
-int sigset_add_many(sigset_t *ss, ...) {
+int sigset_add_many_internal(sigset_t *ss, ...) {
va_list ap;
int r;
@@ -98,7 +95,7 @@ int sigset_add_many(sigset_t *ss, ...) {
return r;
}
-int sigprocmask_many(int how, sigset_t *old, ...) {
+int sigprocmask_many_internal(int how, sigset_t *old, ...) {
va_list ap;
sigset_t ss;
int r;
@@ -113,46 +110,43 @@ int sigprocmask_many(int how, sigset_t *old, ...) {
if (r < 0)
return r;
- if (sigprocmask(how, &ss, old) < 0)
- return -errno;
-
- return 0;
+ return RET_NERRNO(sigprocmask(how, &ss, 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);
@@ -274,7 +268,7 @@ int pop_pending_signal_internal(int sig, ...) {
if (r < 0)
return r;
- r = sigtimedwait(&ss, NULL, &(struct timespec) { 0, 0 });
+ r = sigtimedwait(&ss, NULL, &(const struct timespec) {});
if (r < 0) {
if (errno == EAGAIN)
return 0;
diff --git a/src/basic/signal-util.h b/src/basic/signal-util.h
index ad2ba84..8826fbe 100644
--- a/src/basic/signal-util.h
+++ b/src/basic/signal-util.h
@@ -31,8 +31,11 @@ int sigaction_many_internal(const struct sigaction *sa, ...);
#define sigaction_many(sa, ...) \
sigaction_many_internal(sa, __VA_ARGS__, -1)
-int sigset_add_many(sigset_t *ss, ...);
-int sigprocmask_many(int how, sigset_t *old, ...);
+int sigset_add_many_internal(sigset_t *ss, ...);
+#define sigset_add_many(...) sigset_add_many_internal(__VA_ARGS__, -1)
+
+int sigprocmask_many_internal(int how, sigset_t *old, ...);
+#define sigprocmask_many(...) sigprocmask_many_internal(__VA_ARGS__, -1)
const char *signal_to_string(int i) _const_;
int signal_from_string(const char *s) _pure_;
@@ -46,7 +49,7 @@ static inline void block_signals_reset(sigset_t *ss) {
#define BLOCK_SIGNALS(...) \
_cleanup_(block_signals_reset) _unused_ sigset_t _saved_sigset = ({ \
sigset_t _t; \
- assert_se(sigprocmask_many(SIG_BLOCK, &_t, __VA_ARGS__, -1) >= 0); \
+ assert_se(sigprocmask_many(SIG_BLOCK, &_t, __VA_ARGS__) >= 0); \
_t; \
})
diff --git a/src/basic/siphash24.h b/src/basic/siphash24.h
index 0b3e845..2ef4a04 100644
--- a/src/basic/siphash24.h
+++ b/src/basic/siphash24.h
@@ -22,15 +22,16 @@ struct siphash {
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/basic/socket-util.c b/src/basic/socket-util.c
index beb64d8..6e304e8 100644
--- a/src/basic/socket-util.c
+++ b/src/basic/socket-util.c
@@ -1,9 +1,10 @@
/* SPDX-License-Identifier: LGPL-2.1-or-later */
+/* Make sure the net/if.h header is included before any linux/ one */
+#include <net/if.h>
#include <arpa/inet.h>
#include <errno.h>
#include <limits.h>
-#include <net/if.h>
#include <netdb.h>
#include <netinet/ip.h>
#include <poll.h>
@@ -453,6 +454,7 @@ int sockaddr_pretty(
assert(sa);
assert(salen >= sizeof(sa->sa.sa_family));
+ assert(ret);
switch (sa->sa.sa_family) {
@@ -547,7 +549,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,29 +630,27 @@ 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));
+ assert(ret);
- 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;
-
- r = sockaddr_pretty(&sa->sa, salen, true, true, &ret);
- if (r < 0)
- return r;
+ 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));
- log_debug_errno(saved_errno, "getnameinfo(%s) failed: %m", ret);
- } else {
- ret = strdup(host);
- if (!ret)
- return -ENOMEM;
+ return sockaddr_pretty(sa, salen, /* translate_ipv6= */ true, /* include_port= */ true, ret);
}
- *_ret = ret;
- return 0;
+ return strdup_to(ret, host);
}
static const char* const netlink_family_table[] = {
@@ -872,13 +872,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))
@@ -907,8 +905,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;
@@ -925,12 +925,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)
@@ -948,7 +952,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);
@@ -956,6 +960,21 @@ 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,
@@ -1093,14 +1112,10 @@ ssize_t receive_many_fds_iov(
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) {
+ if (!GREEDY_REALLOC_APPEND(fds_array, n_fds_array, CMSG_TYPED_DATA(cmsg, int), n)) {
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) {
@@ -1641,6 +1656,50 @@ int socket_address_parse_unix(SocketAddress *ret_address, const char *s) {
return 0;
}
+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;
@@ -1666,7 +1725,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;
@@ -1677,15 +1736,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,
@@ -1694,3 +1753,18 @@ 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;
+}
diff --git a/src/basic/socket-util.h b/src/basic/socket-util.h
index 9a11df8..c784125 100644
--- a/src/basic/socket-util.h
+++ b/src/basic/socket-util.h
@@ -113,7 +113,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_;
@@ -152,6 +152,7 @@ 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,
@@ -373,6 +374,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. */
@@ -385,3 +394,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/basic/special.h b/src/basic/special.h
index a625e75..166737a 100644
--- a/src/basic/special.h
+++ b/src/basic/special.h
@@ -47,6 +47,7 @@
#define SPECIAL_TIME_SYNC_TARGET "time-sync.target" /* LSB's $time */
#define SPECIAL_TIME_SET_TARGET "time-set.target"
#define SPECIAL_BASIC_TARGET "basic.target"
+#define SPECIAL_TPM2_TARGET "tpm2.target"
/* LSB compatibility */
#define SPECIAL_NETWORK_TARGET "network.target" /* LSB's $network */
@@ -83,8 +84,10 @@
#define SPECIAL_FSCK_SERVICE "systemd-fsck@.service"
#define SPECIAL_FSCK_ROOT_SERVICE "systemd-fsck-root.service"
#define SPECIAL_FSCK_USR_SERVICE "systemd-fsck-usr.service"
-#define SPECIAL_QUOTACHECK_SERVICE "systemd-quotacheck.service"
-#define SPECIAL_QUOTAON_SERVICE "quotaon.service"
+#define SPECIAL_QUOTACHECK_SERVICE "systemd-quotacheck@.service"
+#define SPECIAL_QUOTACHECK_ROOT_SERVICE "systemd-quotacheck-root.service"
+#define SPECIAL_QUOTAON_SERVICE "quotaon@.service"
+#define SPECIAL_QUOTAON_ROOT_SERVICE "quotaon-root.service"
#define SPECIAL_REMOUNT_FS_SERVICE "systemd-remount-fs.service"
#define SPECIAL_VOLATILE_ROOT_SERVICE "systemd-volatile-root.service"
#define SPECIAL_UDEVD_SERVICE "systemd-udevd.service"
diff --git a/src/basic/stat-util.c b/src/basic/stat-util.c
index 581370d..a833aa2 100644
--- a/src/basic/stat-util.c
+++ b/src/basic/stat-util.c
@@ -25,43 +25,130 @@
#include "stat-util.h"
#include "string-util.h"
-int is_symlink(const char *path) {
- struct stat info;
+static int verify_stat_at(
+ int fd,
+ const char *path,
+ bool follow,
+ int (*verify_func)(const struct stat *st),
+ bool verify) {
- assert(path);
+ struct stat st;
+ int r;
+
+ assert(fd >= 0 || fd == AT_FDCWD);
+ assert(!isempty(path) || !follow);
+ assert(verify_func);
- if (lstat(path, &info) < 0)
+ if (fstatat(fd, strempty(path), &st,
+ (isempty(path) ? AT_EMPTY_PATH : 0) | (follow ? 0 : AT_SYMLINK_NOFOLLOW)) < 0)
return -errno;
- return !!S_ISLNK(info.st_mode);
+ r = verify_func(&st);
+ return verify ? r : r >= 0;
}
-int is_dir_full(int atfd, const char* path, bool follow) {
- struct stat st;
- int r;
+int stat_verify_regular(const struct stat *st) {
+ assert(st);
- assert(atfd >= 0 || atfd == AT_FDCWD);
- assert(atfd >= 0 || path);
+ /* Checks whether the specified stat() structure refers to a regular file. If not returns an
+ * appropriate error code. */
- if (path)
- r = fstatat(atfd, path, &st, follow ? 0 : AT_SYMLINK_NOFOLLOW);
- else
- r = fstat(atfd, &st);
- if (r < 0)
- return -errno;
+ if (S_ISDIR(st->st_mode))
+ return -EISDIR;
+
+ if (S_ISLNK(st->st_mode))
+ return -ELOOP;
+
+ if (!S_ISREG(st->st_mode))
+ return -EBADFD;
- return !!S_ISDIR(st.st_mode);
+ return 0;
}
-int is_device_node(const char *path) {
- struct stat info;
+int verify_regular_at(int fd, const char *path, bool follow) {
+ return verify_stat_at(fd, path, follow, stat_verify_regular, true);
+}
- assert(path);
+int fd_verify_regular(int fd) {
+ assert(fd >= 0);
+ return verify_regular_at(fd, NULL, false);
+}
- if (lstat(path, &info) < 0)
- return -errno;
+int stat_verify_directory(const struct stat *st) {
+ assert(st);
+
+ if (S_ISLNK(st->st_mode))
+ return -ELOOP;
+
+ if (!S_ISDIR(st->st_mode))
+ return -ENOTDIR;
+
+ return 0;
+}
+
+int fd_verify_directory(int fd) {
+ assert(fd >= 0);
+ return verify_stat_at(fd, NULL, false, stat_verify_directory, true);
+}
- return !!(S_ISBLK(info.st_mode) || S_ISCHR(info.st_mode));
+int is_dir_at(int fd, const char *path, bool follow) {
+ return verify_stat_at(fd, path, follow, stat_verify_directory, false);
+}
+
+int is_dir(const char *path, bool follow) {
+ assert(!isempty(path));
+ return is_dir_at(AT_FDCWD, path, follow);
+}
+
+int stat_verify_symlink(const struct stat *st) {
+ assert(st);
+
+ if (S_ISDIR(st->st_mode))
+ return -EISDIR;
+
+ if (!S_ISLNK(st->st_mode))
+ return -ENOLINK;
+
+ return 0;
+}
+
+int is_symlink(const char *path) {
+ assert(!isempty(path));
+ return verify_stat_at(AT_FDCWD, path, false, stat_verify_symlink, false);
+}
+
+int stat_verify_linked(const struct stat *st) {
+ assert(st);
+
+ if (st->st_nlink <= 0)
+ return -EIDRM; /* recognizable error. */
+
+ return 0;
+}
+
+int fd_verify_linked(int fd) {
+ assert(fd >= 0);
+ return verify_stat_at(fd, NULL, false, stat_verify_linked, true);
+}
+
+int stat_verify_device_node(const struct stat *st) {
+ assert(st);
+
+ if (S_ISLNK(st->st_mode))
+ return -ELOOP;
+
+ if (S_ISDIR(st->st_mode))
+ return -EISDIR;
+
+ if (!S_ISBLK(st->st_mode) && !S_ISCHR(st->st_mode))
+ return -ENOTTY;
+
+ return 0;
+}
+
+int is_device_node(const char *path) {
+ assert(!isempty(path));
+ return verify_stat_at(AT_FDCWD, path, false, stat_verify_device_node, false);
}
int dir_is_empty_at(int dir_fd, const char *path, bool ignore_hidden_or_backup) {
@@ -142,7 +229,7 @@ int null_or_empty_path_with_root(const char *fn, const char *root) {
* When looking under root_dir, we can't expect /dev/ to be mounted,
* so let's see if the path is a (possibly dangling) symlink to /dev/null. */
- if (path_equal_ptr(path_startswith(fn, root ?: "/"), "dev/null"))
+ if (path_equal(path_startswith(fn, root ?: "/"), "dev/null"))
return true;
r = chase_and_stat(fn, root, CHASE_PREFIX_ROOT, NULL, &st);
@@ -152,7 +239,7 @@ int null_or_empty_path_with_root(const char *fn, const char *root) {
return null_or_empty(&st);
}
-static int fd_is_read_only_fs(int fd) {
+int fd_is_read_only_fs(int fd) {
struct statvfs st;
assert(fd >= 0);
@@ -187,14 +274,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);
@@ -262,90 +347,6 @@ int path_is_network_fs(const char *path) {
return is_network_fs(&s);
}
-int stat_verify_linked(const struct stat *st) {
- assert(st);
-
- if (st->st_nlink <= 0)
- return -EIDRM; /* recognizable error. */
-
- return 0;
-}
-
-int fd_verify_linked(int fd) {
- struct stat st;
-
- assert(fd >= 0);
-
- if (fstat(fd, &st) < 0)
- return -errno;
-
- return stat_verify_linked(&st);
-}
-
-int stat_verify_regular(const struct stat *st) {
- assert(st);
-
- /* Checks whether the specified stat() structure refers to a regular file. If not returns an
- * appropriate error code. */
-
- if (S_ISDIR(st->st_mode))
- return -EISDIR;
-
- if (S_ISLNK(st->st_mode))
- return -ELOOP;
-
- if (!S_ISREG(st->st_mode))
- return -EBADFD;
-
- return 0;
-}
-
-int fd_verify_regular(int fd) {
- struct stat st;
-
- assert(fd >= 0);
-
- if (fstat(fd, &st) < 0)
- return -errno;
-
- return stat_verify_regular(&st);
-}
-
-int verify_regular_at(int dir_fd, const char *path, bool follow) {
- struct stat st;
-
- assert(dir_fd >= 0 || dir_fd == AT_FDCWD);
- assert(path);
-
- if (fstatat(dir_fd, path, &st, (isempty(path) ? AT_EMPTY_PATH : 0) | (follow ? 0 : AT_SYMLINK_NOFOLLOW)) < 0)
- return -errno;
-
- return stat_verify_regular(&st);
-}
-
-int stat_verify_directory(const struct stat *st) {
- assert(st);
-
- if (S_ISLNK(st->st_mode))
- return -ELOOP;
-
- if (!S_ISDIR(st->st_mode))
- return -ENOTDIR;
-
- return 0;
-}
-
-int fd_verify_directory(int fd) {
- struct stat st;
-
- assert(fd >= 0);
-
- if (fstat(fd, &st) < 0)
- return -errno;
-
- return stat_verify_directory(&st);
-}
-
int proc_mounted(void) {
int r;
@@ -363,8 +364,7 @@ bool stat_inode_same(const struct stat *a, const struct stat *b) {
/* Returns if the specified stat structure references the same (though possibly modified) inode. Does
* a thorough check, comparing inode nr, backing device and if the inode is still of the same type. */
- return a && b &&
- (a->st_mode & S_IFMT) != 0 && /* We use the check for .st_mode if the structure was ever initialized */
+ return stat_is_set(a) && stat_is_set(b) &&
((a->st_mode ^ b->st_mode) & S_IFMT) == 0 && /* same inode type */
a->st_dev == b->st_dev &&
a->st_ino == b->st_ino;
@@ -392,9 +392,8 @@ bool statx_inode_same(const struct statx *a, const struct statx *b) {
/* Same as stat_inode_same() but for struct statx */
- return a && b &&
+ return statx_is_set(a) && statx_is_set(b) &&
FLAGS_SET(a->stx_mask, STATX_TYPE|STATX_INO) && FLAGS_SET(b->stx_mask, STATX_TYPE|STATX_INO) &&
- (a->stx_mode & S_IFMT) != 0 &&
((a->stx_mode ^ b->stx_mode) & S_IFMT) == 0 &&
a->stx_dev_major == b->stx_dev_major &&
a->stx_dev_minor == b->stx_dev_minor &&
@@ -402,7 +401,7 @@ bool statx_inode_same(const struct statx *a, const struct statx *b) {
}
bool statx_mount_same(const struct new_statx *a, const struct new_statx *b) {
- if (!a || !b)
+ if (!new_statx_is_set(a) || !new_statx_is_set(b))
return false;
/* if we have the mount ID, that's all we need */
@@ -498,8 +497,8 @@ int xstatfsat(int dir_fd, const char *path, struct statfs *ret) {
}
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) {
@@ -536,5 +535,29 @@ const char* inode_type_to_string(mode_t m) {
return "sock";
}
+ /* Note anonymous inodes in the kernel will have a zero type. Hence fstat() of an eventfd() will
+ * return an .st_mode where we'll return NULL here! */
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;
+}
diff --git a/src/basic/stat-util.h b/src/basic/stat-util.h
index 3501406..7556f8f 100644
--- a/src/basic/stat-util.h
+++ b/src/basic/stat-util.h
@@ -9,18 +9,28 @@
#include <sys/types.h>
#include <sys/vfs.h>
+#include "fs-util.h"
#include "macro.h"
#include "missing_stat.h"
#include "siphash24.h"
+#include "time-util.h"
+int stat_verify_regular(const struct stat *st);
+int verify_regular_at(int fd, const char *path, bool follow);
+int fd_verify_regular(int fd);
+
+int stat_verify_directory(const struct stat *st);
+int fd_verify_directory(int fd);
+int is_dir_at(int fd, const char *path, bool follow);
+int is_dir(const char *path, bool follow);
+
+int stat_verify_symlink(const struct stat *st);
int is_symlink(const char *path);
-int is_dir_full(int atfd, const char *fname, bool follow);
-static inline int is_dir(const char *path, bool follow) {
- return is_dir_full(AT_FDCWD, path, follow);
-}
-static inline int is_dir_fd(int fd) {
- return is_dir_full(fd, NULL, false);
-}
+
+int stat_verify_linked(const struct stat *st);
+int fd_verify_linked(int fd);
+
+int stat_verify_device_node(const struct stat *st);
int is_device_node(const char *path);
int dir_is_empty_at(int dir_fd, const char *path, bool ignore_hidden_or_backup);
@@ -35,13 +45,16 @@ static inline int null_or_empty_path(const char *fn) {
return null_or_empty_path_with_root(fn, NULL);
}
+int fd_is_read_only_fs(int fd);
int path_is_read_only_fs(const char *path);
int inode_same_at(int fda, const char *filea, int fdb, const char *fileb, int flags);
-
static inline int inode_same(const char *filea, const char *fileb, int flags) {
return inode_same_at(AT_FDCWD, filea, AT_FDCWD, fileb, flags);
}
+static inline int fd_inode_same(int fda, int fdb) {
+ return inode_same_at(fda, NULL, fdb, NULL, AT_EMPTY_PATH);
+}
/* The .f_type field of struct statfs is really weird defined on
* different archs. Let's give its type a name. */
@@ -71,16 +84,6 @@ int path_is_network_fs(const char *path);
*/
#define F_TYPE_EQUAL(a, b) (a == (typeof(a)) b)
-int stat_verify_linked(const struct stat *st);
-int fd_verify_linked(int fd);
-
-int stat_verify_regular(const struct stat *st);
-int fd_verify_regular(int fd);
-int verify_regular_at(int dir_fd, const char *path, bool follow);
-
-int stat_verify_directory(const struct stat *st);
-int fd_verify_directory(int fd);
-
int proc_mounted(void);
bool stat_inode_same(const struct stat *a, const struct stat *b);
@@ -112,8 +115,31 @@ 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);
+
+/* Macros that check whether the stat/statx structures have been initialized already. For "struct stat" we
+ * use a check for .st_dev being non-zero, since the kernel unconditionally fills that in, mapping the file
+ * to its originating superblock, regardless if the fs is block based or virtual (we also check for .st_mode
+ * being MODE_INVALID, since we use that as an invalid marker for separate mode_t fields). For "struct statx"
+ * we use the .stx_mask field, which must be non-zero if any of the fields have already been initialized. */
+static inline bool stat_is_set(const struct stat *st) {
+ return st && st->st_dev != 0 && st->st_mode != MODE_INVALID;
+}
+static inline bool statx_is_set(const struct statx *sx) {
+ return sx && sx->stx_mask != 0;
+}
+static inline bool new_statx_is_set(const struct new_statx *sx) {
+ return sx && sx->stx_mask != 0;
+}
diff --git a/src/basic/stdio-util.h b/src/basic/stdio-util.h
index 4e93ac9..0a2239d 100644
--- a/src/basic/stdio-util.h
+++ b/src/basic/stdio-util.h
@@ -9,14 +9,12 @@
#include "macro.h"
_printf_(3, 4)
-static inline char *snprintf_ok(char *buf, size_t len, const char *format, ...) {
+static inline char* snprintf_ok(char *buf, size_t len, const char *format, ...) {
va_list ap;
int r;
va_start(ap, format);
- DISABLE_WARNING_FORMAT_NONLITERAL;
r = vsnprintf(buf, len, format, ap);
- REENABLE_WARNING;
va_end(ap);
return r >= 0 && (size_t) r < len ? buf : NULL;
diff --git a/src/basic/string-table.h b/src/basic/string-table.h
index 3be70df..d1d90df 100644
--- a/src/basic/string-table.h
+++ b/src/basic/string-table.h
@@ -47,10 +47,8 @@ ssize_t string_table_lookup(const char * const *table, size_t len, const char *k
s = strdup(name##_table[i]); \
if (!s) \
return -ENOMEM; \
- } else { \
- if (asprintf(&s, "%i", i) < 0) \
- return -ENOMEM; \
- } \
+ } else if (asprintf(&s, "%i", i) < 0) \
+ return -ENOMEM; \
*str = s; \
return 0; \
}
diff --git a/src/basic/string-util.c b/src/basic/string-util.c
index 7329bfa..d0d33a4 100644
--- a/src/basic/string-util.c
+++ b/src/basic/string-util.c
@@ -11,6 +11,7 @@
#include "extract-word.h"
#include "fd-util.h"
#include "fileio.h"
+#include "glyph-util.h"
#include "gunicode.h"
#include "locale-util.h"
#include "macro.h"
@@ -282,16 +283,9 @@ bool string_has_cc(const char *p, const char *ok) {
}
static int write_ellipsis(char *buf, bool unicode) {
- if (unicode || is_locale_utf8()) {
- buf[0] = 0xe2; /* tri-dot ellipsis: … */
- buf[1] = 0x80;
- buf[2] = 0xa6;
- } else {
- buf[0] = '.';
- buf[1] = '.';
- buf[2] = '.';
- }
-
+ const char *s = special_glyph_full(SPECIAL_GLYPH_ELLIPSIS, unicode);
+ assert(strlen(s) == 3);
+ memcpy(buf, s, 3);
return 3;
}
@@ -398,8 +392,7 @@ static char *ascii_ellipsize_mem(const char *s, size_t old_length, size_t new_le
x = ((new_length - need_space) * percent + 50) / 100;
assert(x <= new_length - need_space);
- memcpy(t, s, x);
- write_ellipsis(t + x, false);
+ write_ellipsis(mempcpy(t, s, x), /* unicode = */ false);
suffix_len = new_length - x - need_space;
memcpy(t + x + 3, s + old_length - suffix_len, suffix_len);
*(t + x + 3 + suffix_len) = '\0';
@@ -520,13 +513,8 @@ char *ellipsize_mem(const char *s, size_t old_length, size_t new_length, unsigne
if (!e)
return NULL;
- /*
- 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_safe(e, s, len);
- write_ellipsis(e + len, true);
+ write_ellipsis(e + len, /* unicode = */ true);
char *dst = e + len + 3;
@@ -562,7 +550,9 @@ char *cellescape(char *buf, size_t len, const char *s) {
size_t i = 0, last_char_width[4] = {}, k = 0;
+ assert(buf);
assert(len > 0); /* at least a terminating NUL */
+ assert(s);
for (;;) {
char four[4];
@@ -603,7 +593,7 @@ char *cellescape(char *buf, size_t len, const char *s) {
}
if (i + 4 <= len) /* yay, enough space */
- i += write_ellipsis(buf + i, false);
+ i += write_ellipsis(buf + i, /* unicode = */ false);
else if (i + 3 <= len) { /* only space for ".." */
buf[i++] = '.';
buf[i++] = '.';
@@ -612,7 +602,7 @@ char *cellescape(char *buf, size_t len, const char *s) {
else
assert(i + 1 <= len);
- done:
+done:
buf[i] = '\0';
return buf;
}
@@ -620,6 +610,9 @@ char *cellescape(char *buf, size_t len, const char *s) {
char* strshorten(char *s, size_t l) {
assert(s);
+ if (l >= SIZE_MAX-1) /* Would not change anything */
+ return s;
+
if (strnlen(s, l+1) > l)
s[l] = 0;
@@ -993,7 +986,7 @@ int strextendf_with_separator(char **x, const char *separator, const char *forma
return 0;
oom:
- /* truncate the bytes added after the first vsnprintf() attempt again */
+ /* truncate the bytes added after memcpy_safe() again */
(*x)[m] = 0;
return -ENOMEM;
}
@@ -1123,6 +1116,24 @@ int free_and_strndup(char **p, const char *s, size_t l) {
return 1;
}
+int strdup_to_full(char **ret, const char *src) {
+ if (!src) {
+ if (ret)
+ *ret = NULL;
+
+ return 0;
+ } else {
+ if (ret) {
+ char *t = strdup(src);
+ if (!t)
+ return -ENOMEM;
+ *ret = t;
+ }
+
+ return 1;
+ }
+};
+
bool string_is_safe(const char *p) {
if (!p)
return false;
@@ -1232,54 +1243,31 @@ int string_extract_line(const char *s, size_t i, char **ret) {
return -ENOMEM;
*ret = m;
- return !isempty(q + 1); /* more coming? */
- } else {
- if (p == s)
- *ret = NULL; /* Just use the input string */
- else {
- char *m;
-
- m = strdup(p);
- if (!m)
- return -ENOMEM;
-
- *ret = m;
- }
-
- return 0; /* The end */
- }
+ return !isempty(q + 1); /* More coming? */
+ } else
+ /* Tell the caller to use the input string if equal */
+ return strdup_to(ret, p != s ? p : NULL);
}
- if (!q) {
- char *m;
-
+ if (!q)
/* No more lines, return empty line */
-
- m = strdup("");
- if (!m)
- return -ENOMEM;
-
- *ret = m;
- return 0; /* The end */
- }
+ return strdup_to(ret, "");
p = q + 1;
c++;
}
}
-int string_contains_word_strv(const char *string, const char *separators, char **words, const char **ret_word) {
- /* In the default mode with no separators specified, we split on whitespace and
- * don't coalesce separators. */
+int string_contains_word_strv(const char *string, const char *separators, char * const *words, const char **ret_word) {
+ /* In the default mode with no separators specified, we split on whitespace and coalesce separators. */
const ExtractFlags flags = separators ? EXTRACT_DONT_COALESCE_SEPARATORS : 0;
-
const char *found = NULL;
+ int r;
- for (const char *p = string;;) {
+ for (;;) {
_cleanup_free_ char *w = NULL;
- int r;
- r = extract_first_word(&p, &w, separators, flags);
+ r = extract_first_word(&string, &w, separators, flags);
if (r < 0)
return r;
if (r == 0)
@@ -1420,18 +1408,6 @@ char *find_line_startswith(const char *haystack, const char *needle) {
return p + strlen(needle);
}
-char *startswith_strv(const char *string, char **strv) {
- char *found = NULL;
-
- STRV_FOREACH(i, strv) {
- found = startswith(string, *i);
- if (found)
- break;
- }
-
- return found;
-}
-
bool version_is_valid(const char *s) {
if (isempty(s))
return false;
@@ -1519,3 +1495,22 @@ ssize_t strlevenshtein(const char *x, const char *y) {
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/basic/string-util.h b/src/basic/string-util.h
index b6d8be3..ff5efbc 100644
--- a/src/basic/string-util.h
+++ b/src/basic/string-util.h
@@ -133,7 +133,7 @@ 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) {
+static inline char* skip_leading_chars(const char *s, const char *bad) {
if (!s)
return NULL;
@@ -224,6 +224,12 @@ static inline int free_and_strdup_warn(char **p, const char *s) {
}
int free_and_strndup(char **p, const char *s, size_t l);
+int strdup_to_full(char **ret, const char *src);
+static inline int strdup_to(char **ret, const char *src) {
+ int r = strdup_to_full(ASSERT_PTR(ret), src);
+ return r < 0 ? r : 0; /* Suppress return value of 1. */
+}
+
bool string_is_safe(const char *p) _pure_;
DISABLE_WARNING_STRINGOP_TRUNCATION;
@@ -265,7 +271,7 @@ char* string_erase(char *x);
int string_truncate_lines(const char *s, size_t n_lines, char **ret);
int string_extract_line(const char *s, size_t i, char **ret);
-int string_contains_word_strv(const char *string, const char *separators, char **words, const char **ret_word);
+int string_contains_word_strv(const char *string, const char *separators, char * const *words, const char **ret_word);
static inline int string_contains_word(const char *string, const char *separators, const char *word) {
return string_contains_word_strv(string, separators, STRV_MAKE(word), NULL);
}
@@ -291,34 +297,10 @@ 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);
-
-#define STARTSWITH_SET(p, ...) \
- startswith_strv(p, STRV_MAKE(__VA_ARGS__))
-
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;
-}
+char *strrstr(const char *haystack, const char *needle);
diff --git a/src/basic/strv.c b/src/basic/strv.c
index 1065e1b..d081821 100644
--- a/src/basic/strv.c
+++ b/src/basic/strv.c
@@ -242,21 +242,19 @@ rollback:
return -ENOMEM;
}
-int strv_extend_strv_concat(char ***a, char * const *b, const char *suffix) {
+int strv_extend_strv_biconcat(char ***a, const char *prefix, const char* const *b, const char *suffix) {
int r;
STRV_FOREACH(s, b) {
char *v;
- v = strjoin(*s, suffix);
+ v = strjoin(strempty(prefix), *s, suffix);
if (!v)
return -ENOMEM;
- r = strv_push(a, v);
- if (r < 0) {
- free(v);
+ r = strv_consume(a, v);
+ if (r < 0)
return r;
- }
}
return 0;
@@ -358,7 +356,7 @@ int strv_split_colon_pairs(char ***t, const char *s) {
const char *p = tuple;
r = extract_many_words(&p, ":", EXTRACT_CUNESCAPE|EXTRACT_UNESCAPE_SEPARATORS,
- &first, &second, NULL);
+ &first, &second);
if (r < 0)
return r;
if (r == 0)
@@ -505,29 +503,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) {
@@ -588,39 +588,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;
}
@@ -722,6 +746,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;
@@ -848,13 +892,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/basic/strv.h b/src/basic/strv.h
index 03089d5..169737d 100644
--- a/src/basic/strv.h
+++ b/src/basic/strv.h
@@ -43,7 +43,10 @@ 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);
-int strv_extend_strv_concat(char ***a, char * const *b, const char *suffix);
+int strv_extend_strv_biconcat(char ***a, const char *prefix, const char* const *b, const char *suffix);
+static inline int strv_extend_strv_concat(char ***a, const char* const *b, const char *suffix) {
+ return strv_extend_strv_biconcat(a, NULL, b, suffix);
+}
int strv_prepend(char ***l, const char *value);
/* _with_size() are lower-level functions where the size can be provided externally,
@@ -55,8 +58,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) {
@@ -161,6 +166,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; \
@@ -204,18 +219,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/basic/syscall-list.txt b/src/basic/syscall-list.txt
index 1c335bb..4a7b7fb 100644
--- a/src/basic/syscall-list.txt
+++ b/src/basic/syscall-list.txt
@@ -188,12 +188,16 @@ lgetxattr
link
linkat
listen
+listmount
listxattr
llistxattr
lookup_dcookie
lremovexattr
lseek
lsetxattr
+lsm_get_self_attr
+lsm_list_modules
+lsm_set_self_attr
lstat
lstat64
madvise
@@ -229,6 +233,7 @@ mq_timedsend
mq_timedsend_time64
mq_unlink
mremap
+mseal
msgctl
msgget
msgrcv
@@ -449,6 +454,7 @@ stat
stat64
statfs
statfs64
+statmount
statx
stime
subpage_prot
diff --git a/src/basic/syscalls-alpha.txt b/src/basic/syscalls-alpha.txt
index d3ed3a4..da50c04 100644
--- a/src/basic/syscalls-alpha.txt
+++ b/src/basic/syscalls-alpha.txt
@@ -38,7 +38,7 @@ clock_nanosleep_time64
clock_settime 419
clock_settime64
clone 312
-clone3
+clone3 545
close 6
close_range 546
connect 98
@@ -188,12 +188,16 @@ lgetxattr 386
link 9
linkat 458
listen 106
+listmount 568
listxattr 388
llistxattr 389
lookup_dcookie 406
lremovexattr 392
lseek 19
lsetxattr 383
+lsm_get_self_attr 569
+lsm_list_modules 571
+lsm_set_self_attr 570
lstat 68
lstat64 426
madvise 75
@@ -229,6 +233,7 @@ mq_timedsend 434
mq_timedsend_time64
mq_unlink 433
mremap 341
+mseal 572
msgctl 200
msgget 201
msgrcv 202
@@ -449,6 +454,7 @@ stat 67
stat64 425
statfs 328
statfs64 528
+statmount 567
statx 522
stime
subpage_prot
diff --git a/src/basic/syscalls-arc.txt b/src/basic/syscalls-arc.txt
index 951ef56..cdb8a53 100644
--- a/src/basic/syscalls-arc.txt
+++ b/src/basic/syscalls-arc.txt
@@ -188,12 +188,16 @@ lgetxattr 9
link
linkat 37
listen 201
+listmount 458
listxattr 11
llistxattr 12
lookup_dcookie 18
lremovexattr 15
lseek
lsetxattr 6
+lsm_get_self_attr 459
+lsm_list_modules 461
+lsm_set_self_attr 460
lstat
lstat64
madvise 233
@@ -229,6 +233,7 @@ mq_timedsend 182
mq_timedsend_time64 418
mq_unlink 181
mremap 216
+mseal 462
msgctl 187
msgget 186
msgrcv 188
@@ -449,6 +454,7 @@ stat
stat64
statfs
statfs64 43
+statmount 457
statx 291
stime
subpage_prot
diff --git a/src/basic/syscalls-arm.txt b/src/basic/syscalls-arm.txt
index 1c0e66f..743dd87 100644
--- a/src/basic/syscalls-arm.txt
+++ b/src/basic/syscalls-arm.txt
@@ -188,12 +188,16 @@ lgetxattr 230
link 9
linkat 330
listen 284
+listmount 458
listxattr 232
llistxattr 233
lookup_dcookie 249
lremovexattr 236
lseek 19
lsetxattr 227
+lsm_get_self_attr 459
+lsm_list_modules 461
+lsm_set_self_attr 460
lstat 107
lstat64 196
madvise 220
@@ -229,6 +233,7 @@ mq_timedsend 276
mq_timedsend_time64 418
mq_unlink 275
mremap 163
+mseal 462
msgctl 304
msgget 303
msgrcv 302
@@ -449,6 +454,7 @@ stat 106
stat64 195
statfs 99
statfs64 266
+statmount 457
statx 397
stime
subpage_prot
diff --git a/src/basic/syscalls-arm64.txt b/src/basic/syscalls-arm64.txt
index b8602a1..e2fc548 100644
--- a/src/basic/syscalls-arm64.txt
+++ b/src/basic/syscalls-arm64.txt
@@ -188,12 +188,16 @@ lgetxattr 9
link
linkat 37
listen 201
+listmount 458
listxattr 11
llistxattr 12
lookup_dcookie 18
lremovexattr 15
lseek 62
lsetxattr 6
+lsm_get_self_attr 459
+lsm_list_modules 461
+lsm_set_self_attr 460
lstat
lstat64
madvise 233
@@ -229,6 +233,7 @@ mq_timedsend 182
mq_timedsend_time64
mq_unlink 181
mremap 216
+mseal 462
msgctl 187
msgget 186
msgrcv 188
@@ -449,6 +454,7 @@ stat
stat64
statfs 43
statfs64
+statmount 457
statx 291
stime
subpage_prot
diff --git a/src/basic/syscalls-i386.txt b/src/basic/syscalls-i386.txt
index 6d0c57f..3c571e8 100644
--- a/src/basic/syscalls-i386.txt
+++ b/src/basic/syscalls-i386.txt
@@ -188,12 +188,16 @@ lgetxattr 230
link 9
linkat 303
listen 363
+listmount 458
listxattr 232
llistxattr 233
lookup_dcookie 253
lremovexattr 236
lseek 19
lsetxattr 227
+lsm_get_self_attr 459
+lsm_list_modules 461
+lsm_set_self_attr 460
lstat 107
lstat64 196
madvise 219
@@ -229,6 +233,7 @@ mq_timedsend 279
mq_timedsend_time64 418
mq_unlink 278
mremap 163
+mseal 462
msgctl 402
msgget 399
msgrcv 401
@@ -449,6 +454,7 @@ stat 106
stat64 195
statfs 99
statfs64 268
+statmount 457
statx 383
stime 25
subpage_prot
diff --git a/src/basic/syscalls-loongarch64.txt b/src/basic/syscalls-loongarch64.txt
index 34a45cb..ba69d80 100644
--- a/src/basic/syscalls-loongarch64.txt
+++ b/src/basic/syscalls-loongarch64.txt
@@ -188,12 +188,16 @@ lgetxattr 9
link
linkat 37
listen 201
+listmount 458
listxattr 11
llistxattr 12
lookup_dcookie 18
lremovexattr 15
lseek 62
lsetxattr 6
+lsm_get_self_attr 459
+lsm_list_modules 461
+lsm_set_self_attr 460
lstat
lstat64
madvise 233
@@ -229,6 +233,7 @@ mq_timedsend 182
mq_timedsend_time64
mq_unlink 181
mremap 216
+mseal 462
msgctl 187
msgget 186
msgrcv 188
@@ -449,6 +454,7 @@ stat
stat64
statfs 43
statfs64
+statmount 457
statx 291
stime
subpage_prot
diff --git a/src/basic/syscalls-m68k.txt b/src/basic/syscalls-m68k.txt
index 712f272..032e354 100644
--- a/src/basic/syscalls-m68k.txt
+++ b/src/basic/syscalls-m68k.txt
@@ -188,12 +188,16 @@ lgetxattr 227
link 9
linkat 296
listen 360
+listmount 458
listxattr 229
llistxattr 230
lookup_dcookie 248
lremovexattr 233
lseek 19
lsetxattr 224
+lsm_get_self_attr 459
+lsm_list_modules 461
+lsm_set_self_attr 460
lstat 107
lstat64 196
madvise 238
@@ -229,6 +233,7 @@ mq_timedsend 273
mq_timedsend_time64 418
mq_unlink 272
mremap 163
+mseal 462
msgctl 402
msgget 399
msgrcv 401
@@ -449,6 +454,7 @@ stat 106
stat64 195
statfs 99
statfs64 263
+statmount 457
statx 379
stime 25
subpage_prot
diff --git a/src/basic/syscalls-mips64.txt b/src/basic/syscalls-mips64.txt
index 2d0984e..b470f8d 100644
--- a/src/basic/syscalls-mips64.txt
+++ b/src/basic/syscalls-mips64.txt
@@ -188,12 +188,16 @@ lgetxattr 5184
link 5084
linkat 5255
listen 5049
+listmount 5458
listxattr 5186
llistxattr 5187
lookup_dcookie 5206
lremovexattr 5190
lseek 5008
lsetxattr 5181
+lsm_get_self_attr 5459
+lsm_list_modules 5461
+lsm_set_self_attr 5460
lstat 5006
lstat64
madvise 5027
@@ -229,6 +233,7 @@ mq_timedsend 5232
mq_timedsend_time64
mq_unlink 5231
mremap 5024
+mseal 5462
msgctl 5069
msgget 5066
msgrcv 5068
@@ -449,6 +454,7 @@ stat 5004
stat64
statfs 5134
statfs64
+statmount 5457
statx 5326
stime
subpage_prot
diff --git a/src/basic/syscalls-mips64n32.txt b/src/basic/syscalls-mips64n32.txt
index 4475867..30ddfca 100644
--- a/src/basic/syscalls-mips64n32.txt
+++ b/src/basic/syscalls-mips64n32.txt
@@ -188,12 +188,16 @@ lgetxattr 6184
link 6084
linkat 6259
listen 6049
+listmount 6458
listxattr 6186
llistxattr 6187
lookup_dcookie 6206
lremovexattr 6190
lseek 6008
lsetxattr 6181
+lsm_get_self_attr 6459
+lsm_list_modules 6461
+lsm_set_self_attr 6460
lstat 6006
lstat64
madvise 6027
@@ -229,6 +233,7 @@ mq_timedsend 6236
mq_timedsend_time64 6418
mq_unlink 6235
mremap 6024
+mseal 6462
msgctl 6069
msgget 6066
msgrcv 6068
@@ -449,6 +454,7 @@ stat 6004
stat64
statfs 6134
statfs64 6217
+statmount 6457
statx 6330
stime
subpage_prot
diff --git a/src/basic/syscalls-mipso32.txt b/src/basic/syscalls-mipso32.txt
index 0254cb3..26cb3c1 100644
--- a/src/basic/syscalls-mipso32.txt
+++ b/src/basic/syscalls-mipso32.txt
@@ -188,12 +188,16 @@ lgetxattr 4228
link 4009
linkat 4296
listen 4174
+listmount 4458
listxattr 4230
llistxattr 4231
lookup_dcookie 4247
lremovexattr 4234
lseek 4019
lsetxattr 4225
+lsm_get_self_attr 4459
+lsm_list_modules 4461
+lsm_set_self_attr 4460
lstat 4107
lstat64 4214
madvise 4218
@@ -229,6 +233,7 @@ mq_timedsend 4273
mq_timedsend_time64 4418
mq_unlink 4272
mremap 4167
+mseal 4462
msgctl 4402
msgget 4399
msgrcv 4401
@@ -449,6 +454,7 @@ stat 4106
stat64 4213
statfs 4099
statfs64 4255
+statmount 4457
statx 4366
stime 4025
subpage_prot
diff --git a/src/basic/syscalls-parisc.txt b/src/basic/syscalls-parisc.txt
index 2bb1de5..99c7712 100644
--- a/src/basic/syscalls-parisc.txt
+++ b/src/basic/syscalls-parisc.txt
@@ -188,12 +188,16 @@ lgetxattr 242
link 9
linkat 283
listen 32
+listmount 458
listxattr 244
llistxattr 245
lookup_dcookie 223
lremovexattr 248
lseek 19
lsetxattr 239
+lsm_get_self_attr 459
+lsm_list_modules 461
+lsm_set_self_attr 460
lstat 84
lstat64 198
madvise 119
@@ -229,6 +233,7 @@ mq_timedsend 231
mq_timedsend_time64 418
mq_unlink 230
mremap 163
+mseal 462
msgctl 191
msgget 190
msgrcv 189
@@ -449,6 +454,7 @@ stat 18
stat64 101
statfs 99
statfs64 298
+statmount 457
statx 349
stime 25
subpage_prot
diff --git a/src/basic/syscalls-powerpc.txt b/src/basic/syscalls-powerpc.txt
index a8c1b1b..a1c6452 100644
--- a/src/basic/syscalls-powerpc.txt
+++ b/src/basic/syscalls-powerpc.txt
@@ -188,12 +188,16 @@ lgetxattr 213
link 9
linkat 294
listen 329
+listmount 458
listxattr 215
llistxattr 216
lookup_dcookie 235
lremovexattr 219
lseek 19
lsetxattr 210
+lsm_get_self_attr 459
+lsm_list_modules 461
+lsm_set_self_attr 460
lstat 107
lstat64 196
madvise 205
@@ -229,6 +233,7 @@ mq_timedsend 264
mq_timedsend_time64 418
mq_unlink 263
mremap 163
+mseal 462
msgctl 402
msgget 399
msgrcv 401
@@ -449,6 +454,7 @@ stat 106
stat64 195
statfs 99
statfs64 252
+statmount 457
statx 383
stime 25
subpage_prot 310
diff --git a/src/basic/syscalls-powerpc64.txt b/src/basic/syscalls-powerpc64.txt
index 824cc61..992c61d 100644
--- a/src/basic/syscalls-powerpc64.txt
+++ b/src/basic/syscalls-powerpc64.txt
@@ -188,12 +188,16 @@ lgetxattr 213
link 9
linkat 294
listen 329
+listmount 458
listxattr 215
llistxattr 216
lookup_dcookie 235
lremovexattr 219
lseek 19
lsetxattr 210
+lsm_get_self_attr 459
+lsm_list_modules 461
+lsm_set_self_attr 460
lstat 107
lstat64
madvise 205
@@ -229,6 +233,7 @@ mq_timedsend 264
mq_timedsend_time64
mq_unlink 263
mremap 163
+mseal 462
msgctl 402
msgget 399
msgrcv 401
@@ -449,6 +454,7 @@ stat 106
stat64
statfs 99
statfs64 252
+statmount 457
statx 383
stime 25
subpage_prot 310
diff --git a/src/basic/syscalls-riscv32.txt b/src/basic/syscalls-riscv32.txt
index 5011956..3af7cdb 100644
--- a/src/basic/syscalls-riscv32.txt
+++ b/src/basic/syscalls-riscv32.txt
@@ -188,12 +188,16 @@ lgetxattr 9
link
linkat 37
listen 201
+listmount 458
listxattr 11
llistxattr 12
lookup_dcookie 18
lremovexattr 15
lseek
lsetxattr 6
+lsm_get_self_attr 459
+lsm_list_modules 461
+lsm_set_self_attr 460
lstat
lstat64
madvise 233
@@ -229,6 +233,7 @@ mq_timedsend
mq_timedsend_time64 418
mq_unlink 181
mremap 216
+mseal 462
msgctl 187
msgget 186
msgrcv 188
@@ -449,6 +454,7 @@ stat
stat64
statfs
statfs64 43
+statmount 457
statx 291
stime
subpage_prot
diff --git a/src/basic/syscalls-riscv64.txt b/src/basic/syscalls-riscv64.txt
index ba00b90..7a1882a 100644
--- a/src/basic/syscalls-riscv64.txt
+++ b/src/basic/syscalls-riscv64.txt
@@ -188,12 +188,16 @@ lgetxattr 9
link
linkat 37
listen 201
+listmount 458
listxattr 11
llistxattr 12
lookup_dcookie 18
lremovexattr 15
lseek 62
lsetxattr 6
+lsm_get_self_attr 459
+lsm_list_modules 461
+lsm_set_self_attr 460
lstat
lstat64
madvise 233
@@ -229,6 +233,7 @@ mq_timedsend 182
mq_timedsend_time64
mq_unlink 181
mremap 216
+mseal 462
msgctl 187
msgget 186
msgrcv 188
@@ -449,6 +454,7 @@ stat
stat64
statfs 43
statfs64
+statmount 457
statx 291
stime
subpage_prot
diff --git a/src/basic/syscalls-s390.txt b/src/basic/syscalls-s390.txt
index c81f795..855d5c4 100644
--- a/src/basic/syscalls-s390.txt
+++ b/src/basic/syscalls-s390.txt
@@ -188,12 +188,16 @@ lgetxattr 228
link 9
linkat 296
listen 363
+listmount 458
listxattr 230
llistxattr 231
lookup_dcookie 110
lremovexattr 234
lseek 19
lsetxattr 225
+lsm_get_self_attr 459
+lsm_list_modules 461
+lsm_set_self_attr 460
lstat 107
lstat64 196
madvise 219
@@ -229,6 +233,7 @@ mq_timedsend 273
mq_timedsend_time64 418
mq_unlink 272
mremap 163
+mseal 462
msgctl 402
msgget 399
msgrcv 401
@@ -449,6 +454,7 @@ stat 106
stat64 195
statfs 99
statfs64 265
+statmount 457
statx 379
stime 25
subpage_prot
diff --git a/src/basic/syscalls-s390x.txt b/src/basic/syscalls-s390x.txt
index c999fd6..05dcbe9 100644
--- a/src/basic/syscalls-s390x.txt
+++ b/src/basic/syscalls-s390x.txt
@@ -188,12 +188,16 @@ lgetxattr 228
link 9
linkat 296
listen 363
+listmount 458
listxattr 230
llistxattr 231
lookup_dcookie 110
lremovexattr 234
lseek 19
lsetxattr 225
+lsm_get_self_attr 459
+lsm_list_modules 461
+lsm_set_self_attr 460
lstat 107
lstat64
madvise 219
@@ -229,6 +233,7 @@ mq_timedsend 273
mq_timedsend_time64
mq_unlink 272
mremap 163
+mseal 462
msgctl 402
msgget 399
msgrcv 401
@@ -449,6 +454,7 @@ stat 106
stat64
statfs 99
statfs64 265
+statmount 457
statx 379
stime
subpage_prot
diff --git a/src/basic/syscalls-sparc.txt b/src/basic/syscalls-sparc.txt
index e631d30..f4ff31e 100644
--- a/src/basic/syscalls-sparc.txt
+++ b/src/basic/syscalls-sparc.txt
@@ -188,12 +188,16 @@ lgetxattr 173
link 9
linkat 292
listen 354
+listmount 458
listxattr 178
llistxattr 179
lookup_dcookie 208
lremovexattr 182
lseek 19
lsetxattr 170
+lsm_get_self_attr 459
+lsm_list_modules 461
+lsm_set_self_attr 460
lstat 40
lstat64 132
madvise 75
@@ -229,6 +233,7 @@ mq_timedsend 275
mq_timedsend_time64 418
mq_unlink 274
mremap 250
+mseal 462
msgctl 402
msgget 399
msgrcv 401
@@ -449,6 +454,7 @@ stat 38
stat64 139
statfs 157
statfs64 234
+statmount 457
statx 360
stime 233
subpage_prot
diff --git a/src/basic/syscalls-x86_64.txt b/src/basic/syscalls-x86_64.txt
index 52d6176..f314ed0 100644
--- a/src/basic/syscalls-x86_64.txt
+++ b/src/basic/syscalls-x86_64.txt
@@ -188,12 +188,16 @@ lgetxattr 192
link 86
linkat 265
listen 50
+listmount 458
listxattr 194
llistxattr 195
lookup_dcookie 212
lremovexattr 198
lseek 8
lsetxattr 189
+lsm_get_self_attr 459
+lsm_list_modules 461
+lsm_set_self_attr 460
lstat 6
lstat64
madvise 28
@@ -229,6 +233,7 @@ mq_timedsend 242
mq_timedsend_time64
mq_unlink 241
mremap 25
+mseal 462
msgctl 71
msgget 68
msgrcv 70
@@ -449,6 +454,7 @@ stat 4
stat64
statfs 137
statfs64
+statmount 457
statx 332
stime
subpage_prot
diff --git a/src/basic/sysctl-util.c b/src/basic/sysctl-util.c
index b66a662..9a1933f 100644
--- a/src/basic/sysctl-util.c
+++ b/src/basic/sysctl-util.c
@@ -96,6 +96,26 @@ int sysctl_write_ip_property(int af, const char *ifname, const char *property, c
return sysctl_write(p, value);
}
+int sysctl_write_ip_neighbor_property(int af, const char *ifname, const char *property, const char *value) {
+ const char *p;
+
+ assert(property);
+ assert(value);
+ assert(ifname);
+
+ if (!IN_SET(af, AF_INET, AF_INET6))
+ return -EAFNOSUPPORT;
+
+ if (ifname) {
+ if (!ifname_valid_full(ifname, IFNAME_VALID_SPECIAL))
+ return -EINVAL;
+ p = strjoina("net/", af_to_ipv4_ipv6(af), "/neigh/", ifname, "/", property);
+ } else
+ p = strjoina("net/", af_to_ipv4_ipv6(af), "/neigh/default/", property);
+
+ return sysctl_write(p, value);
+}
+
int sysctl_read(const char *property, char **ret) {
char *p;
int r;
diff --git a/src/basic/sysctl-util.h b/src/basic/sysctl-util.h
index 3236419..7192e8c 100644
--- a/src/basic/sysctl-util.h
+++ b/src/basic/sysctl-util.h
@@ -19,6 +19,13 @@ static inline int sysctl_write_ip_property_boolean(int af, const char *ifname, c
return sysctl_write_ip_property(af, ifname, property, one_zero(value));
}
+int sysctl_write_ip_neighbor_property(int af, const char *ifname, const char *property, const char *value);
+static inline int sysctl_write_ip_neighbor_property_uint32(int af, const char *ifname, const char *property, uint32_t value) {
+ char buf[DECIMAL_STR_MAX(uint32_t)];
+ xsprintf(buf, "%u", value);
+ return sysctl_write_ip_neighbor_property(af, ifname, property, buf);
+}
+
#define DEFINE_SYSCTL_WRITE_IP_PROPERTY(name, type, format) \
static inline int sysctl_write_ip_property_##name(int af, const char *ifname, const char *property, type value) { \
char buf[DECIMAL_STR_MAX(type)]; \
diff --git a/src/basic/terminal-util.c b/src/basic/terminal-util.c
index 530ef9a..dda5920 100644
--- a/src/basic/terminal-util.c
+++ b/src/basic/terminal-util.c
@@ -27,6 +27,7 @@
#include "fd-util.h"
#include "fileio.h"
#include "fs-util.h"
+#include "hexdecoct.h"
#include "inotify-util.h"
#include "io-util.h"
#include "log.h"
@@ -53,6 +54,18 @@ static volatile int cached_on_dev_null = -1;
static volatile int cached_color_mode = _COLOR_INVALID;
static volatile int cached_underline_enabled = -1;
+bool isatty_safe(int fd) {
+ assert(fd >= 0);
+
+ if (isatty(fd))
+ return true;
+
+ /* Be resilient if we're working on stdio, since they're set up by parent process. */
+ assert(errno != EBADF || IN_SET(fd, STDIN_FILENO, STDOUT_FILENO, STDERR_FILENO));
+
+ return false;
+}
+
int chvt(int vt) {
_cleanup_close_ int fd = -EBADF;
@@ -239,7 +252,7 @@ int reset_terminal_fd(int fd, bool switch_to_text) {
assert(fd >= 0);
- if (isatty(fd) < 1)
+ if (!isatty_safe(fd))
return log_debug_errno(errno, "Asked to reset a terminal that actually isn't a terminal: %m");
/* We leave locked terminal attributes untouched, so that Plymouth may set whatever it wants to set,
@@ -293,6 +306,8 @@ int reset_terminal_fd(int fd, bool switch_to_text) {
termios.c_cc[VMIN] = 1;
r = RET_NERRNO(tcsetattr(fd, TCSANOW, &termios));
+ if (r < 0)
+ log_debug_errno(r, "Failed to set terminal parameters: %m");
finish:
/* Just in case, flush all crap out */
@@ -346,7 +361,7 @@ int open_terminal(const char *name, int mode) {
c++;
}
- if (isatty(fd) < 1)
+ if (!isatty_safe(fd))
return negative_errno();
return TAKE_FD(fd);
@@ -684,18 +699,10 @@ int vtnr_from_tty(const char *tty) {
tty = active;
}
- if (tty == active)
- *ret = TAKE_PTR(active);
- else {
- char *tmp;
-
- tmp = strdup(tty);
- if (!tmp)
- return -ENOMEM;
-
- *ret = tmp;
- }
+ if (tty != active)
+ return strdup_to(ret, tty);
+ *ret = TAKE_PTR(active);
return 0;
}
@@ -975,7 +982,7 @@ bool on_tty(void) {
}
int getttyname_malloc(int fd, char **ret) {
- char path[PATH_MAX], *c; /* PATH_MAX is counted *with* the trailing NUL byte */
+ char path[PATH_MAX]; /* PATH_MAX is counted *with* the trailing NUL byte */
int r;
assert(fd >= 0);
@@ -988,12 +995,7 @@ int getttyname_malloc(int fd, char **ret) {
if (r > 0)
return -r;
- c = strdup(skip_dev_prefix(path));
- if (!c)
- return -ENOMEM;
-
- *ret = c;
- return 0;
+ return strdup_to(ret, skip_dev_prefix(path));
}
int getttyname_harder(int fd, char **ret) {
@@ -1098,13 +1100,9 @@ int get_ctty(pid_t pid, dev_t *ret_devnr, char **ret) {
return -EINVAL;
if (ret) {
- _cleanup_free_ char *b = NULL;
-
- b = strdup(w);
- if (!b)
- return -ENOMEM;
-
- *ret = TAKE_PTR(b);
+ r = strdup_to(ret, w);
+ if (r < 0)
+ return r;
}
if (ret_devnr)
@@ -1198,7 +1196,7 @@ int openpt_allocate_in_namespace(pid_t pid, int flags, char **ret_slave) {
assert(pid > 0);
- r = namespace_open(pid, &pidnsfd, &mntnsfd, NULL, &usernsfd, &rootfd);
+ r = namespace_open(pid, &pidnsfd, &mntnsfd, /* ret_netns_fd = */ NULL, &usernsfd, &rootfd);
if (r < 0)
return r;
@@ -1249,7 +1247,7 @@ int open_terminal_in_namespace(pid_t pid, const char *name, int mode) {
pid_t child;
int r;
- r = namespace_open(pid, &pidnsfd, &mntnsfd, NULL, &usernsfd, &rootfd);
+ r = namespace_open(pid, &pidnsfd, &mntnsfd, /* ret_netns_fd = */ NULL, &usernsfd, &rootfd);
if (r < 0)
return r;
@@ -1446,38 +1444,33 @@ int vt_reset_keyboard(int fd) {
}
int vt_restore(int fd) {
+
static const struct vt_mode mode = {
.mode = VT_AUTO,
};
- int r, q = 0;
- if (isatty(fd) < 1)
+ int r, ret = 0;
+
+ assert(fd >= 0);
+
+ if (!isatty_safe(fd))
return log_debug_errno(errno, "Asked to restore the VT for an fd that does not refer to a terminal: %m");
if (ioctl(fd, KDSETMODE, KD_TEXT) < 0)
- q = log_debug_errno(errno, "Failed to set VT in text mode, ignoring: %m");
+ RET_GATHER(ret, log_debug_errno(errno, "Failed to set VT to text mode, ignoring: %m"));
r = vt_reset_keyboard(fd);
- if (r < 0) {
- log_debug_errno(r, "Failed to reset keyboard mode, ignoring: %m");
- if (q >= 0)
- q = r;
- }
+ if (r < 0)
+ RET_GATHER(ret, log_debug_errno(r, "Failed to reset keyboard mode, ignoring: %m"));
- if (ioctl(fd, VT_SETMODE, &mode) < 0) {
- log_debug_errno(errno, "Failed to set VT_AUTO mode, ignoring: %m");
- if (q >= 0)
- q = -errno;
- }
+ if (ioctl(fd, VT_SETMODE, &mode) < 0)
+ RET_GATHER(ret, log_debug_errno(errno, "Failed to set VT_AUTO mode, ignoring: %m"));
r = fchmod_and_chown(fd, TTY_MODE, 0, GID_INVALID);
- if (r < 0) {
- log_debug_errno(r, "Failed to chmod()/chown() VT, ignoring: %m");
- if (q >= 0)
- q = r;
- }
+ if (r < 0)
+ RET_GATHER(ret, log_debug_errno(r, "Failed to chmod()/chown() VT, ignoring: %m"));
- return q;
+ return ret;
}
int vt_release(int fd, bool restore) {
@@ -1487,7 +1480,7 @@ int vt_release(int fd, bool restore) {
* sent by the kernel and optionally reset the VT in text and auto
* VT-switching modes. */
- if (isatty(fd) < 1)
+ if (!isatty_safe(fd))
return log_debug_errno(errno, "Asked to release the VT for an fd that does not refer to a terminal: %m");
if (ioctl(fd, VT_RELDISP, 1) < 0)
@@ -1551,3 +1544,264 @@ int set_terminal_cursor_position(int fd, unsigned int row, unsigned int column)
return 0;
}
+
+int terminal_reset_ansi_seq(int fd) {
+ int r, k;
+
+ assert(fd >= 0);
+
+ if (getenv_terminal_is_dumb())
+ return 0;
+
+ r = fd_nonblock(fd, true);
+ if (r < 0)
+ return log_debug_errno(r, "Failed to set terminal to non-blocking mode: %m");
+
+ k = loop_write_full(fd,
+ "\033c" /* reset to initial state */
+ "\033[!p" /* soft terminal reset */
+ "\033]104\007" /* reset colors */
+ "\033[?7h", /* enable line-wrapping */
+ SIZE_MAX,
+ 50 * USEC_PER_MSEC);
+ if (k < 0)
+ log_debug_errno(k, "Failed to write to terminal: %m");
+
+ if (r > 0) {
+ r = fd_nonblock(fd, false);
+ if (r < 0)
+ log_debug_errno(r, "Failed to set terminal back to blocking mode: %m");
+ }
+
+ return k < 0 ? k : r;
+}
+
+void termios_disable_echo(struct termios *termios) {
+ assert(termios);
+
+ termios->c_lflag &= ~(ICANON|ECHO);
+ termios->c_cc[VMIN] = 1;
+ termios->c_cc[VTIME] = 0;
+}
+
+typedef enum BackgroundColorState {
+ BACKGROUND_TEXT,
+ BACKGROUND_ESCAPE,
+ BACKGROUND_BRACKET,
+ BACKGROUND_FIRST_ONE,
+ BACKGROUND_SECOND_ONE,
+ BACKGROUND_SEMICOLON,
+ BACKGROUND_R,
+ BACKGROUND_G,
+ BACKGROUND_B,
+ BACKGROUND_RED,
+ BACKGROUND_GREEN,
+ BACKGROUND_BLUE,
+ BACKGROUND_STRING_TERMINATOR,
+} BackgroundColorState;
+
+typedef struct BackgroundColorContext {
+ BackgroundColorState state;
+ uint32_t red, green, blue;
+ unsigned red_bits, green_bits, blue_bits;
+} BackgroundColorContext;
+
+static int scan_background_color_response(
+ BackgroundColorContext *context,
+ const char *buf,
+ size_t size) {
+
+ assert(context);
+ assert(buf || size == 0);
+
+ for (size_t i = 0; i < size; i++) {
+ char c = buf[i];
+
+ switch (context->state) {
+
+ case BACKGROUND_TEXT:
+ context->state = c == '\x1B' ? BACKGROUND_ESCAPE : BACKGROUND_TEXT;
+ break;
+
+ case BACKGROUND_ESCAPE:
+ context->state = c == ']' ? BACKGROUND_BRACKET : BACKGROUND_TEXT;
+ break;
+
+ case BACKGROUND_BRACKET:
+ context->state = c == '1' ? BACKGROUND_FIRST_ONE : BACKGROUND_TEXT;
+ break;
+
+ case BACKGROUND_FIRST_ONE:
+ context->state = c == '1' ? BACKGROUND_SECOND_ONE : BACKGROUND_TEXT;
+ break;
+
+ case BACKGROUND_SECOND_ONE:
+ context->state = c == ';' ? BACKGROUND_SEMICOLON : BACKGROUND_TEXT;
+ break;
+
+ case BACKGROUND_SEMICOLON:
+ context->state = c == 'r' ? BACKGROUND_R : BACKGROUND_TEXT;
+ break;
+
+ case BACKGROUND_R:
+ context->state = c == 'g' ? BACKGROUND_G : BACKGROUND_TEXT;
+ break;
+
+ case BACKGROUND_G:
+ context->state = c == 'b' ? BACKGROUND_B : BACKGROUND_TEXT;
+ break;
+
+ case BACKGROUND_B:
+ context->state = c == ':' ? BACKGROUND_RED : BACKGROUND_TEXT;
+ break;
+
+ case BACKGROUND_RED:
+ if (c == '/')
+ context->state = context->red_bits > 0 ? BACKGROUND_GREEN : BACKGROUND_TEXT;
+ else {
+ int d = unhexchar(c);
+ if (d < 0 || context->red_bits >= sizeof(context->red)*8)
+ context->state = BACKGROUND_TEXT;
+ else {
+ context->red = (context->red << 4) | d;
+ context->red_bits += 4;
+ }
+ }
+ break;
+
+ case BACKGROUND_GREEN:
+ if (c == '/')
+ context->state = context->green_bits > 0 ? BACKGROUND_BLUE : BACKGROUND_TEXT;
+ else {
+ int d = unhexchar(c);
+ if (d < 0 || context->green_bits >= sizeof(context->green)*8)
+ context->state = BACKGROUND_TEXT;
+ else {
+ context->green = (context->green << 4) | d;
+ context->green_bits += 4;
+ }
+ }
+ break;
+
+ case BACKGROUND_BLUE:
+ if (c == '\x07') {
+ if (context->blue_bits > 0)
+ return 1; /* success! */
+
+ context->state = BACKGROUND_TEXT;
+ } else if (c == '\x1b')
+ context->state = context->blue_bits > 0 ? BACKGROUND_STRING_TERMINATOR : BACKGROUND_TEXT;
+ else {
+ int d = unhexchar(c);
+ if (d < 0 || context->blue_bits >= sizeof(context->blue)*8)
+ context->state = BACKGROUND_TEXT;
+ else {
+ context->blue = (context->blue << 4) | d;
+ context->blue_bits += 4;
+ }
+ }
+ break;
+
+ case BACKGROUND_STRING_TERMINATOR:
+ if (c == '\\')
+ return 1; /* success! */
+
+ context->state = c == ']' ? BACKGROUND_ESCAPE : BACKGROUND_TEXT;
+ break;
+
+ }
+
+ /* Reset any colors we might have picked up */
+ if (IN_SET(context->state, BACKGROUND_TEXT, BACKGROUND_ESCAPE)) {
+ /* reset color */
+ context->red = context->green = context->blue = 0;
+ context->red_bits = context->green_bits = context->blue_bits = 0;
+ }
+ }
+
+ return 0; /* all good, but not enough data yet */
+}
+
+int get_default_background_color(double *ret_red, double *ret_green, double *ret_blue) {
+ int r;
+
+ assert(ret_red);
+ assert(ret_green);
+ assert(ret_blue);
+
+ if (!colors_enabled())
+ return -EOPNOTSUPP;
+
+ if (!isatty(STDIN_FILENO) || !isatty(STDOUT_FILENO))
+ return -EOPNOTSUPP;
+
+ if (streq_ptr(getenv("TERM"), "linux")) {
+ /* Linux console is black */
+ *ret_red = *ret_green = *ret_blue = 0.0;
+ return 0;
+ }
+
+ struct termios old_termios;
+ if (tcgetattr(STDIN_FILENO, &old_termios) < 0)
+ return -errno;
+
+ struct termios new_termios = old_termios;
+ termios_disable_echo(&new_termios);
+
+ if (tcsetattr(STDOUT_FILENO, TCSADRAIN, &new_termios) < 0)
+ return -errno;
+
+ r = loop_write(STDOUT_FILENO, "\x1B]11;?\x07", SIZE_MAX);
+ if (r < 0)
+ goto finish;
+
+ usec_t end = usec_add(now(CLOCK_MONOTONIC), 100 * USEC_PER_MSEC);
+ char buf[256];
+ size_t buf_full = 0;
+ BackgroundColorContext context = {};
+
+ for (;;) {
+ usec_t n = now(CLOCK_MONOTONIC);
+
+ if (n >= end) {
+ r = -EOPNOTSUPP;
+ goto finish;
+ }
+
+ r = fd_wait_for_event(STDIN_FILENO, POLLIN, usec_sub_unsigned(end, n));
+ if (r < 0)
+ goto finish;
+ if (r == 0) {
+ r = -EOPNOTSUPP;
+ goto finish;
+ }
+
+ ssize_t l;
+ l = read(STDIN_FILENO, buf, sizeof(buf) - buf_full);
+ if (l < 0) {
+ r = -errno;
+ goto finish;
+ }
+
+ buf_full += l;
+ assert(buf_full <= sizeof(buf));
+
+ r = scan_background_color_response(&context, buf, buf_full);
+ if (r < 0)
+ goto finish;
+ if (r > 0) {
+ assert(context.red_bits > 0);
+ *ret_red = (double) context.red / ((UINT64_C(1) << context.red_bits) - 1);
+ assert(context.green_bits > 0);
+ *ret_green = (double) context.green / ((UINT64_C(1) << context.green_bits) - 1);
+ assert(context.blue_bits > 0);
+ *ret_blue = (double) context.blue / ((UINT64_C(1) << context.blue_bits) - 1);
+ r = 0;
+ goto finish;
+ }
+ }
+
+finish:
+ (void) tcsetattr(STDOUT_FILENO, TCSADRAIN, &old_termios);
+ return r;
+}
diff --git a/src/basic/terminal-util.h b/src/basic/terminal-util.h
index b1d7aee..ecfe574 100644
--- a/src/basic/terminal-util.h
+++ b/src/basic/terminal-util.h
@@ -6,6 +6,7 @@
#include <stdio.h>
#include <syslog.h>
#include <sys/types.h>
+#include <termios.h>
#include "macro.h"
#include "time-util.h"
@@ -59,6 +60,8 @@
/* Other ANSI codes */
#define ANSI_UNDERLINE "\x1B[0;4m"
+#define ANSI_ADD_UNDERLINE "\x1B[4m"
+#define ANSI_ADD_UNDERLINE_GREY ANSI_ADD_UNDERLINE "\x1B[58;5;245m"
#define ANSI_HIGHLIGHT "\x1B[0;1;39m"
#define ANSI_HIGHLIGHT_UNDERLINE "\x1B[0;1;4m"
@@ -77,15 +80,25 @@
/* Erase characters until the end of the line */
#define ANSI_ERASE_TO_END_OF_LINE "\x1B[K"
+/* Erase characters until end of screen */
+#define ANSI_ERASE_TO_END_OF_SCREEN "\x1B[J"
+
/* Move cursor up one line */
#define ANSI_REVERSE_LINEFEED "\x1BM"
/* Set cursor to top left corner and clear screen */
#define ANSI_HOME_CLEAR "\x1B[H\x1B[2J"
+/* Push/pop a window title off the stack of window titles */
+#define ANSI_WINDOW_TITLE_PUSH "\x1b[22;2t"
+#define ANSI_WINDOW_TITLE_POP "\x1b[23;2t"
+
+bool isatty_safe(int fd);
+
int reset_terminal_fd(int fd, bool switch_to_text);
int reset_terminal(const char *name);
int set_terminal_cursor_position(int fd, unsigned int row, unsigned int column);
+int terminal_reset_ansi_seq(int fd);
int open_terminal(const char *name, int mode);
@@ -167,7 +180,6 @@ bool underline_enabled(void);
bool dev_console_colors_enabled(void);
static inline bool colors_enabled(void) {
-
/* Returns true if colors are considered supported on our stdout. */
return get_color_mode() != COLOR_OFF;
}
@@ -190,6 +202,15 @@ static inline const char *ansi_underline(void) {
return underline_enabled() ? ANSI_UNDERLINE : "";
}
+static inline const char *ansi_add_underline(void) {
+ return underline_enabled() ? ANSI_ADD_UNDERLINE : "";
+}
+
+static inline const char *ansi_add_underline_grey(void) {
+ return underline_enabled() ?
+ (colors_enabled() ? ANSI_ADD_UNDERLINE_GREY : ANSI_ADD_UNDERLINE) : "";
+}
+
#define DEFINE_ANSI_FUNC_UNDERLINE(name, NAME) \
static inline const char *ansi_##name(void) { \
return underline_enabled() ? ANSI_##NAME##_UNDERLINE : \
@@ -276,3 +297,7 @@ static inline const char* ansi_highlight_green_red(bool b) {
/* This assumes there is a 'tty' group */
#define TTY_MODE 0620
+
+void termios_disable_echo(struct termios *termios);
+
+int get_default_background_color(double *ret_red, double *ret_green, double *ret_blue);
diff --git a/src/basic/time-util.c b/src/basic/time-util.c
index f9014dc..b94f37c 100644
--- a/src/basic/time-util.c
+++ b/src/basic/time-util.c
@@ -83,7 +83,7 @@ triple_timestamp* triple_timestamp_now(triple_timestamp *ts) {
return ts;
}
-static usec_t map_clock_usec_internal(usec_t from, usec_t from_base, usec_t to_base) {
+usec_t map_clock_usec_raw(usec_t from, usec_t from_base, usec_t to_base) {
/* Maps the time 'from' between two clocks, based on a common reference point where the first clock
* is at 'from_base' and the second clock at 'to_base'. Basically calculates:
@@ -121,7 +121,7 @@ usec_t map_clock_usec(usec_t from, clockid_t from_clock, clockid_t to_clock) {
if (from == USEC_INFINITY)
return from;
- return map_clock_usec_internal(from, now(from_clock), now(to_clock));
+ return map_clock_usec_raw(from, now(from_clock), now(to_clock));
}
dual_timestamp* dual_timestamp_from_realtime(dual_timestamp *ts, usec_t u) {
@@ -150,8 +150,8 @@ triple_timestamp* triple_timestamp_from_realtime(triple_timestamp *ts, usec_t u)
nowr = now(CLOCK_REALTIME);
ts->realtime = u;
- ts->monotonic = map_clock_usec_internal(u, nowr, now(CLOCK_MONOTONIC));
- ts->boottime = map_clock_usec_internal(u, nowr, now(CLOCK_BOOTTIME));
+ ts->monotonic = map_clock_usec_raw(u, nowr, now(CLOCK_MONOTONIC));
+ ts->boottime = map_clock_usec_raw(u, nowr, now(CLOCK_BOOTTIME));
return ts;
}
@@ -169,8 +169,8 @@ triple_timestamp* triple_timestamp_from_boottime(triple_timestamp *ts, usec_t u)
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));
+ ts->monotonic = map_clock_usec_raw(u, nowb, now(CLOCK_MONOTONIC));
+ ts->realtime = map_clock_usec_raw(u, nowb, now(CLOCK_REALTIME));
return ts;
}
@@ -199,8 +199,8 @@ dual_timestamp* dual_timestamp_from_boottime(dual_timestamp *ts, usec_t u) {
}
nowm = now(CLOCK_BOOTTIME);
- ts->monotonic = map_clock_usec_internal(u, nowm, now(CLOCK_MONOTONIC));
- ts->realtime = map_clock_usec_internal(u, nowm, now(CLOCK_REALTIME));
+ ts->monotonic = map_clock_usec_raw(u, nowm, now(CLOCK_MONOTONIC));
+ ts->realtime = map_clock_usec_raw(u, nowm, now(CLOCK_REALTIME));
return ts;
}
@@ -1429,7 +1429,7 @@ static int get_timezones_from_zone1970_tab(char ***ret) {
/* Line format is:
* 'country codes' 'coordinates' 'timezone' 'comments' */
- r = extract_many_words(&p, NULL, 0, &cc, &co, &tz, NULL);
+ r = extract_many_words(&p, NULL, 0, &cc, &co, &tz);
if (r < 0)
continue;
@@ -1474,7 +1474,7 @@ static int get_timezones_from_tzdata_zi(char ***ret) {
* Link line format is:
* 'Link' 'target' 'alias'
* See 'man zic' for more detail. */
- r = extract_many_words(&p, NULL, 0, &type, &f1, &f2, NULL);
+ r = extract_many_words(&p, NULL, 0, &type, &f1, &f2);
if (r < 0)
continue;
@@ -1517,7 +1517,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);
@@ -1573,7 +1573,7 @@ int verify_timezone(const char *name, int log_level) {
r = fd_verify_regular(fd);
if (r < 0)
- return log_full_errno(log_level, r, "Timezone file '%s' is not a regular file: %m", t);
+ return log_full_errno(log_level, r, "Timezone file '%s' is not a regular file: %m", t);
r = loop_read_exact(fd, buf, 4, false);
if (r < 0)
@@ -1606,38 +1606,24 @@ bool clock_supported(clockid_t clock) {
int get_timezone(char **ret) {
_cleanup_free_ char *t = NULL;
- const char *e;
- char *z;
int r;
assert(ret);
r = readlink_malloc("/etc/localtime", &t);
- if (r == -ENOENT) {
+ if (r == -ENOENT)
/* If the symlink does not exist, assume "UTC", like glibc does */
- z = strdup("UTC");
- if (!z)
- return -ENOMEM;
-
- *ret = z;
- return 0;
- }
+ return strdup_to(ret, "UTC");
if (r < 0)
- return r; /* returns EINVAL if not a symlink */
+ return r; /* Return EINVAL if not a symlink */
- e = PATH_STARTSWITH_SET(t, "/usr/share/zoneinfo/", "../usr/share/zoneinfo/");
+ const char *e = PATH_STARTSWITH_SET(t, "/usr/share/zoneinfo/", "../usr/share/zoneinfo/");
if (!e)
return -EINVAL;
-
if (!timezone_is_valid(e, LOG_DEBUG))
return -EINVAL;
- z = strdup(e);
- if (!z)
- return -ENOMEM;
-
- *ret = z;
- return 0;
+ return strdup_to(ret, e);
}
time_t mktime_or_timegm(struct tm *tm, bool utc) {
diff --git a/src/basic/time-util.h b/src/basic/time-util.h
index ed4c1aa..f273770 100644
--- a/src/basic/time-util.h
+++ b/src/basic/time-util.h
@@ -71,12 +71,16 @@ typedef enum TimestampStyle {
#define TIME_T_MAX (time_t)((UINTMAX_C(1) << ((sizeof(time_t) << 3) - 1)) - 1)
-#define DUAL_TIMESTAMP_NULL ((struct dual_timestamp) {})
-#define TRIPLE_TIMESTAMP_NULL ((struct triple_timestamp) {})
+#define DUAL_TIMESTAMP_NULL ((dual_timestamp) {})
+#define DUAL_TIMESTAMP_INFINITY ((dual_timestamp) { USEC_INFINITY, USEC_INFINITY })
+#define TRIPLE_TIMESTAMP_NULL ((triple_timestamp) {})
+
+#define TIMESPEC_OMIT ((const struct timespec) { .tv_nsec = UTIME_OMIT })
usec_t now(clockid_t clock);
nsec_t now_nsec(clockid_t clock);
+usec_t map_clock_usec_raw(usec_t from, usec_t from_base, usec_t to_base);
usec_t map_clock_usec(usec_t from, clockid_t from_clock, clockid_t to_clock);
dual_timestamp* dual_timestamp_now(dual_timestamp *ts);
@@ -219,6 +223,9 @@ static inline int usleep_safe(usec_t usec) {
* ⚠️ 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;
}
diff --git a/src/basic/tmpfile-util.c b/src/basic/tmpfile-util.c
index e77ca94..3a3f7dc 100644
--- a/src/basic/tmpfile-util.c
+++ b/src/basic/tmpfile-util.c
@@ -330,28 +330,7 @@ int fopen_tmpfile_linkable(const char *target, int flags, char **ret_path, FILE
return 0;
}
-static int link_fd(int fd, int newdirfd, const char *newpath) {
- int r;
-
- assert(fd >= 0);
- assert(newdirfd >= 0 || newdirfd == AT_FDCWD);
- assert(newpath);
-
- /* Try symlinking via /proc/fd/ first. */
- r = RET_NERRNO(linkat(AT_FDCWD, FORMAT_PROC_FD_PATH(fd), newdirfd, newpath, AT_SYMLINK_FOLLOW));
- if (r != -ENOENT)
- return r;
-
- /* Fall back to symlinking via AT_EMPTY_PATH as fallback (this requires CAP_DAC_READ_SEARCH and a
- * more recent kernel, but does not require /proc/ mounted) */
- if (proc_mounted() != 0)
- return r;
-
- return RET_NERRNO(linkat(fd, "", newdirfd, newpath, AT_EMPTY_PATH));
-}
-
int link_tmpfile_at(int fd, int dir_fd, const char *path, const char *target, LinkTmpfileFlags flags) {
- _cleanup_free_ char *tmp = NULL;
int r;
assert(fd >= 0);
@@ -370,33 +349,14 @@ int link_tmpfile_at(int fd, int dir_fd, const char *path, const char *target, Li
r = RET_NERRNO(renameat(dir_fd, path, dir_fd, target));
else
r = rename_noreplace(dir_fd, path, dir_fd, target);
- if (r < 0)
- return r;
} else {
-
- r = link_fd(fd, dir_fd, target);
- if (r != -EEXIST || !FLAGS_SET(flags, LINK_TMPFILE_REPLACE))
- return r;
-
- /* So the target already exists and we were asked to replace it. That sucks a bit, since the kernel's
- * linkat() logic does not allow that. We work-around this by linking the file to a random name
- * first, and then renaming that to the final name. This reintroduces the race O_TMPFILE kinda is
- * trying to fix, but at least the vulnerability window (i.e. where the file is linked into the file
- * system under a temporary name) is very short. */
-
- r = tempfn_random(target, NULL, &tmp);
- if (r < 0)
- return r;
-
- if (link_fd(fd, dir_fd, tmp) < 0)
- return -EEXIST; /* propagate original error */
-
- r = RET_NERRNO(renameat(dir_fd, tmp, dir_fd, target));
- if (r < 0) {
- (void) unlinkat(dir_fd, tmp, 0);
- return r;
- }
+ if (FLAGS_SET(flags, LINK_TMPFILE_REPLACE))
+ r = linkat_replace(fd, /* oldpath= */ NULL, dir_fd, target);
+ else
+ r = link_fd(fd, dir_fd, target);
}
+ if (r < 0)
+ return r;
if (FLAGS_SET(flags, LINK_TMPFILE_SYNC)) {
r = fsync_full(fd);
diff --git a/src/basic/uid-alloc-range.c b/src/basic/uid-classification.c
index 669cb6d..e2d2ceb 100644
--- a/src/basic/uid-alloc-range.c
+++ b/src/basic/uid-classification.c
@@ -5,7 +5,7 @@
#include "fileio.h"
#include "missing_threads.h"
#include "string-util.h"
-#include "uid-alloc-range.h"
+#include "uid-classification.h"
#include "user-util.h"
static const UGIDAllocationRange default_ugid_allocation_range = {
diff --git a/src/basic/uid-alloc-range.h b/src/basic/uid-classification.h
index 5badde1..5badde1 100644
--- a/src/basic/uid-alloc-range.h
+++ b/src/basic/uid-classification.h
diff --git a/src/basic/uid-range.c b/src/basic/uid-range.c
index 8463599..a765881 100644
--- a/src/basic/uid-range.c
+++ b/src/basic/uid-range.c
@@ -10,12 +10,13 @@
#include "format-util.h"
#include "macro.h"
#include "path-util.h"
+#include "process-util.h"
#include "sort-util.h"
#include "stat-util.h"
#include "uid-range.h"
#include "user-util.h"
-UidRange *uid_range_free(UidRange *range) {
+UIDRange *uid_range_free(UIDRange *range) {
if (!range)
return NULL;
@@ -23,14 +24,14 @@ UidRange *uid_range_free(UidRange *range) {
return mfree(range);
}
-static bool uid_range_entry_intersect(const UidRangeEntry *a, const UidRangeEntry *b) {
+static bool uid_range_entry_intersect(const UIDRangeEntry *a, const UIDRangeEntry *b) {
assert(a);
assert(b);
return a->start <= b->start + b->nr && a->start + a->nr >= b->start;
}
-static int uid_range_entry_compare(const UidRangeEntry *a, const UidRangeEntry *b) {
+static int uid_range_entry_compare(const UIDRangeEntry *a, const UIDRangeEntry *b) {
int r;
assert(a);
@@ -43,7 +44,7 @@ static int uid_range_entry_compare(const UidRangeEntry *a, const UidRangeEntry *
return CMP(a->nr, b->nr);
}
-static void uid_range_coalesce(UidRange *range) {
+static void uid_range_coalesce(UIDRange *range) {
assert(range);
if (range->n_entries <= 0)
@@ -52,10 +53,10 @@ static void uid_range_coalesce(UidRange *range) {
typesafe_qsort(range->entries, range->n_entries, uid_range_entry_compare);
for (size_t i = 0; i < range->n_entries; i++) {
- UidRangeEntry *x = range->entries + i;
+ UIDRangeEntry *x = range->entries + i;
for (size_t j = i + 1; j < range->n_entries; j++) {
- UidRangeEntry *y = range->entries + j;
+ UIDRangeEntry *y = range->entries + j;
uid_t begin, end;
if (!uid_range_entry_intersect(x, y))
@@ -68,7 +69,7 @@ static void uid_range_coalesce(UidRange *range) {
x->nr = end - begin;
if (range->n_entries > j + 1)
- memmove(y, y + 1, sizeof(UidRangeEntry) * (range->n_entries - j - 1));
+ memmove(y, y + 1, sizeof(UIDRangeEntry) * (range->n_entries - j - 1));
range->n_entries--;
j--;
@@ -76,9 +77,9 @@ static void uid_range_coalesce(UidRange *range) {
}
}
-int uid_range_add_internal(UidRange **range, uid_t start, uid_t nr, bool coalesce) {
- _cleanup_(uid_range_freep) UidRange *range_new = NULL;
- UidRange *p;
+int uid_range_add_internal(UIDRange **range, uid_t start, uid_t nr, bool coalesce) {
+ _cleanup_(uid_range_freep) UIDRange *range_new = NULL;
+ UIDRange *p;
assert(range);
@@ -91,7 +92,7 @@ int uid_range_add_internal(UidRange **range, uid_t start, uid_t nr, bool coalesc
if (*range)
p = *range;
else {
- range_new = new0(UidRange, 1);
+ range_new = new0(UIDRange, 1);
if (!range_new)
return -ENOMEM;
@@ -101,7 +102,7 @@ int uid_range_add_internal(UidRange **range, uid_t start, uid_t nr, bool coalesc
if (!GREEDY_REALLOC(p->entries, p->n_entries + 1))
return -ENOMEM;
- p->entries[p->n_entries++] = (UidRangeEntry) {
+ p->entries[p->n_entries++] = (UIDRangeEntry) {
.start = start,
.nr = nr,
};
@@ -115,7 +116,7 @@ int uid_range_add_internal(UidRange **range, uid_t start, uid_t nr, bool coalesc
return 0;
}
-int uid_range_add_str(UidRange **range, const char *s) {
+int uid_range_add_str(UIDRange **range, const char *s) {
uid_t start, end;
int r;
@@ -129,7 +130,7 @@ int uid_range_add_str(UidRange **range, const char *s) {
return uid_range_add_internal(range, start, end - start + 1, /* coalesce = */ true);
}
-int uid_range_next_lower(const UidRange *range, uid_t *uid) {
+int uid_range_next_lower(const UIDRange *range, uid_t *uid) {
uid_t closest = UID_INVALID, candidate;
assert(range);
@@ -162,7 +163,7 @@ int uid_range_next_lower(const UidRange *range, uid_t *uid) {
return 1;
}
-bool uid_range_covers(const UidRange *range, uid_t start, uid_t nr) {
+bool uid_range_covers(const UIDRange *range, uid_t start, uid_t nr) {
if (nr == 0) /* empty range? always covered... */
return true;
@@ -172,16 +173,40 @@ bool uid_range_covers(const UidRange *range, uid_t start, uid_t nr) {
if (!range)
return false;
- for (size_t i = 0; i < range->n_entries; i++)
- if (start >= range->entries[i].start &&
- start + nr <= range->entries[i].start + range->entries[i].nr)
+ FOREACH_ARRAY(i, range->entries, range->n_entries)
+ if (start >= i->start &&
+ start + nr <= i->start + i->nr)
return true;
return false;
}
-int uid_range_load_userns(UidRange **ret, const char *path) {
- _cleanup_(uid_range_freep) UidRange *range = NULL;
+int uid_map_read_one(FILE *f, uid_t *ret_base, uid_t *ret_shift, uid_t *ret_range) {
+ uid_t uid_base, uid_shift, uid_range;
+ int r;
+
+ assert(f);
+ assert(ret_base);
+ assert(ret_shift);
+ assert(ret_range);
+
+ errno = 0;
+ r = fscanf(f, UID_FMT " " UID_FMT " " UID_FMT "\n", &uid_base, &uid_shift, &uid_range);
+ if (r == EOF)
+ return errno_or_else(ENOMSG);
+ assert(r >= 0);
+ if (r != 3)
+ return -EBADMSG;
+
+ *ret_base = uid_base;
+ *ret_shift = uid_shift;
+ *ret_range = uid_range;
+
+ return 0;
+}
+
+int uid_range_load_userns(const char *path, UIDRangeUsernsMode mode, UIDRange **ret) {
+ _cleanup_(uid_range_freep) UIDRange *range = NULL;
_cleanup_fclose_ FILE *f = NULL;
int r;
@@ -191,10 +216,12 @@ int uid_range_load_userns(UidRange **ret, const char *path) {
*
* To simplify things this will modify the passed array in case of later failure. */
+ assert(mode >= 0);
+ assert(mode < _UID_RANGE_USERNS_MODE_MAX);
assert(ret);
if (!path)
- path = "/proc/self/uid_map";
+ path = IN_SET(mode, UID_RANGE_USERNS_INSIDE, UID_RANGE_USERNS_OUTSIDE) ? "/proc/self/uid_map" : "/proc/self/gid_map";
f = fopen(path, "re");
if (!f) {
@@ -206,26 +233,24 @@ int uid_range_load_userns(UidRange **ret, const char *path) {
return r;
}
- range = new0(UidRange, 1);
+ range = new0(UIDRange, 1);
if (!range)
return -ENOMEM;
for (;;) {
uid_t uid_base, uid_shift, uid_range;
- int k;
-
- errno = 0;
- k = fscanf(f, UID_FMT " " UID_FMT " " UID_FMT "\n", &uid_base, &uid_shift, &uid_range);
- if (k == EOF) {
- if (ferror(f))
- return errno_or_else(EIO);
+ r = uid_map_read_one(f, &uid_base, &uid_shift, &uid_range);
+ if (r == -ENOMSG)
break;
- }
- if (k != 3)
- return -EBADMSG;
+ if (r < 0)
+ return r;
- r = uid_range_add_internal(&range, uid_base, uid_range, /* coalesce = */ false);
+ r = uid_range_add_internal(
+ &range,
+ IN_SET(mode, UID_RANGE_USERNS_INSIDE, GID_RANGE_USERNS_INSIDE) ? uid_base : uid_shift,
+ uid_range,
+ /* coalesce = */ false);
if (r < 0)
return r;
}
@@ -235,3 +260,103 @@ int uid_range_load_userns(UidRange **ret, const char *path) {
*ret = TAKE_PTR(range);
return 0;
}
+
+int uid_range_load_userns_by_fd(int userns_fd, UIDRangeUsernsMode mode, UIDRange **ret) {
+ _cleanup_(close_pairp) int pfd[2] = EBADF_PAIR;
+ _cleanup_(sigkill_waitp) pid_t pid = 0;
+ ssize_t n;
+ char x;
+ int r;
+
+ assert(userns_fd >= 0);
+ assert(mode >= 0);
+ assert(mode < _UID_RANGE_USERNS_MODE_MAX);
+ assert(ret);
+
+ if (pipe2(pfd, O_CLOEXEC) < 0)
+ return -errno;
+
+ r = safe_fork_full(
+ "(sd-mkuserns)",
+ /* stdio_fds= */ NULL,
+ (int[]) { pfd[1], userns_fd }, 2,
+ FORK_CLOSE_ALL_FDS|FORK_DEATHSIG_SIGKILL,
+ &pid);
+ if (r < 0)
+ return r;
+ if (r == 0) {
+ /* Child. */
+
+ if (setns(userns_fd, CLONE_NEWUSER) < 0) {
+ log_debug_errno(errno, "Failed to join userns: %m");
+ _exit(EXIT_FAILURE);
+ }
+
+ userns_fd = safe_close(userns_fd);
+
+ n = write(pfd[1], &(const char) { 'x' }, 1);
+ if (n < 0) {
+ log_debug_errno(errno, "Failed to write to fifo: %m");
+ _exit(EXIT_FAILURE);
+ }
+ assert(n == 1);
+
+ freeze();
+ }
+
+ pfd[1] = safe_close(pfd[1]);
+
+ n = read(pfd[0], &x, 1);
+ if (n < 0)
+ return -errno;
+ if (n == 0)
+ return -EPROTO;
+ assert(n == 1);
+ assert(x == 'x');
+
+ const char *p = procfs_file_alloca(
+ pid,
+ IN_SET(mode, UID_RANGE_USERNS_INSIDE, UID_RANGE_USERNS_OUTSIDE) ? "uid_map" : "gid_map");
+
+ return uid_range_load_userns(p, mode, ret);
+}
+
+bool uid_range_overlaps(const UIDRange *range, uid_t start, uid_t nr) {
+
+ if (!range)
+ return false;
+
+ /* Avoid overflow */
+ if (start > UINT32_MAX - nr)
+ nr = UINT32_MAX - start;
+
+ if (nr == 0)
+ return false;
+
+ FOREACH_ARRAY(entry, range->entries, range->n_entries)
+ if (start < entry->start + entry->nr &&
+ start + nr >= entry->start)
+ return true;
+
+ return false;
+}
+
+bool uid_range_equal(const UIDRange *a, const UIDRange *b) {
+ if (a == b)
+ return true;
+
+ if (!a || !b)
+ return false;
+
+ if (a->n_entries != b->n_entries)
+ return false;
+
+ for (size_t i = 0; i < a->n_entries; i++) {
+ if (a->entries[i].start != b->entries[i].start)
+ return false;
+ if (a->entries[i].nr != b->entries[i].nr)
+ return false;
+ }
+
+ return true;
+}
diff --git a/src/basic/uid-range.h b/src/basic/uid-range.h
index 461a511..1f687b2 100644
--- a/src/basic/uid-range.h
+++ b/src/basic/uid-range.h
@@ -6,29 +6,73 @@
#include "macro.h"
-typedef struct UidRangeEntry {
+typedef struct UIDRangeEntry {
uid_t start, nr;
-} UidRangeEntry;
+} UIDRangeEntry;
-typedef struct UidRange {
- UidRangeEntry *entries;
+typedef struct UIDRange {
+ UIDRangeEntry *entries;
size_t n_entries;
-} UidRange;
+} UIDRange;
-UidRange *uid_range_free(UidRange *range);
-DEFINE_TRIVIAL_CLEANUP_FUNC(UidRange*, uid_range_free);
+UIDRange *uid_range_free(UIDRange *range);
+DEFINE_TRIVIAL_CLEANUP_FUNC(UIDRange*, uid_range_free);
-int uid_range_add_internal(UidRange **range, uid_t start, uid_t nr, bool coalesce);
-static inline int uid_range_add(UidRange **range, uid_t start, uid_t nr) {
+int uid_range_add_internal(UIDRange **range, uid_t start, uid_t nr, bool coalesce);
+static inline int uid_range_add(UIDRange **range, uid_t start, uid_t nr) {
return uid_range_add_internal(range, start, nr, true);
}
-int uid_range_add_str(UidRange **range, const char *s);
+int uid_range_add_str(UIDRange **range, const char *s);
-int uid_range_next_lower(const UidRange *range, uid_t *uid);
+int uid_range_next_lower(const UIDRange *range, uid_t *uid);
-bool uid_range_covers(const UidRange *range, uid_t start, uid_t nr);
-static inline bool uid_range_contains(const UidRange *range, uid_t uid) {
+bool uid_range_covers(const UIDRange *range, uid_t start, uid_t nr);
+static inline bool uid_range_contains(const UIDRange *range, uid_t uid) {
return uid_range_covers(range, uid, 1);
}
-int uid_range_load_userns(UidRange **ret, const char *path);
+int uid_map_read_one(FILE *f, uid_t *ret_base, uid_t *ret_shift, uid_t *ret_range);
+
+static inline size_t uid_range_entries(const UIDRange *range) {
+ return range ? range->n_entries : 0;
+}
+
+static inline unsigned uid_range_size(const UIDRange *range) {
+ if (!range)
+ return 0;
+
+ unsigned n = 0;
+
+ FOREACH_ARRAY(e, range->entries, range->n_entries)
+ n += e->nr;
+
+ return n;
+}
+
+static inline bool uid_range_is_empty(const UIDRange *range) {
+
+ if (!range)
+ return true;
+
+ FOREACH_ARRAY(e, range->entries, range->n_entries)
+ if (e->nr > 0)
+ return false;
+
+ return true;
+}
+
+bool uid_range_equal(const UIDRange *a, const UIDRange *b);
+
+typedef enum UIDRangeUsernsMode {
+ UID_RANGE_USERNS_INSIDE,
+ UID_RANGE_USERNS_OUTSIDE,
+ GID_RANGE_USERNS_INSIDE,
+ GID_RANGE_USERNS_OUTSIDE,
+ _UID_RANGE_USERNS_MODE_MAX,
+ _UID_RANGE_USERNS_MODE_INVALID = -EINVAL,
+} UIDRangeUsernsMode;
+
+int uid_range_load_userns(const char *path, UIDRangeUsernsMode mode, UIDRange **ret);
+int uid_range_load_userns_by_fd(int userns_fd, UIDRangeUsernsMode mode, UIDRange **ret);
+
+bool uid_range_overlaps(const UIDRange *range, uid_t start, uid_t nr);
diff --git a/src/basic/unit-def.c b/src/basic/unit-def.c
index 908c0cd..d03363b 100644
--- a/src/basic/unit-def.c
+++ b/src/basic/unit-def.c
@@ -99,7 +99,7 @@ static const char* const unit_load_state_table[_UNIT_LOAD_STATE_MAX] = {
[UNIT_BAD_SETTING] = "bad-setting",
[UNIT_ERROR] = "error",
[UNIT_MERGED] = "merged",
- [UNIT_MASKED] = "masked"
+ [UNIT_MASKED] = "masked",
};
DEFINE_STRING_TABLE_LOOKUP(unit_load_state, UnitLoadState);
@@ -117,14 +117,33 @@ static const char* const unit_active_state_table[_UNIT_ACTIVE_STATE_MAX] = {
DEFINE_STRING_TABLE_LOOKUP(unit_active_state, UnitActiveState);
static const char* const freezer_state_table[_FREEZER_STATE_MAX] = {
- [FREEZER_RUNNING] = "running",
- [FREEZER_FREEZING] = "freezing",
- [FREEZER_FROZEN] = "frozen",
- [FREEZER_THAWING] = "thawing",
+ [FREEZER_RUNNING] = "running",
+ [FREEZER_FREEZING] = "freezing",
+ [FREEZER_FREEZING_BY_PARENT] = "freezing-by-parent",
+ [FREEZER_FROZEN] = "frozen",
+ [FREEZER_FROZEN_BY_PARENT] = "frozen-by-parent",
+ [FREEZER_THAWING] = "thawing",
};
DEFINE_STRING_TABLE_LOOKUP(freezer_state, FreezerState);
+/* Maps in-progress freezer states to the corresponding finished state */
+static const FreezerState freezer_state_finish_table[_FREEZER_STATE_MAX] = {
+ [FREEZER_FREEZING] = FREEZER_FROZEN,
+ [FREEZER_FREEZING_BY_PARENT] = FREEZER_FROZEN_BY_PARENT,
+ [FREEZER_THAWING] = FREEZER_RUNNING,
+
+ /* Finished states trivially map to themselves */
+ [FREEZER_RUNNING] = FREEZER_RUNNING,
+ [FREEZER_FROZEN] = FREEZER_FROZEN,
+ [FREEZER_FROZEN_BY_PARENT] = FREEZER_FROZEN_BY_PARENT,
+};
+
+FreezerState freezer_state_finish(FreezerState state) {
+ assert(state >= 0 && state < _FREEZER_STATE_MAX);
+ return freezer_state_finish_table[state];
+}
+
static const char* const unit_marker_table[_UNIT_MARKER_MAX] = {
[UNIT_MARKER_NEEDS_RELOAD] = "needs-reload",
[UNIT_MARKER_NEEDS_RESTART] = "needs-restart",
@@ -136,7 +155,7 @@ static const char* const automount_state_table[_AUTOMOUNT_STATE_MAX] = {
[AUTOMOUNT_DEAD] = "dead",
[AUTOMOUNT_WAITING] = "waiting",
[AUTOMOUNT_RUNNING] = "running",
- [AUTOMOUNT_FAILED] = "failed"
+ [AUTOMOUNT_FAILED] = "failed",
};
DEFINE_STRING_TABLE_LOOKUP(automount_state, AutomountState);
@@ -170,7 +189,7 @@ static const char* const path_state_table[_PATH_STATE_MAX] = {
[PATH_DEAD] = "dead",
[PATH_WAITING] = "waiting",
[PATH_RUNNING] = "running",
- [PATH_FAILED] = "failed"
+ [PATH_FAILED] = "failed",
};
DEFINE_STRING_TABLE_LOOKUP(path_state, PathState);
@@ -219,7 +238,7 @@ DEFINE_STRING_TABLE_LOOKUP(service_state, ServiceState);
static const char* const slice_state_table[_SLICE_STATE_MAX] = {
[SLICE_DEAD] = "dead",
- [SLICE_ACTIVE] = "active"
+ [SLICE_ACTIVE] = "active",
};
DEFINE_STRING_TABLE_LOOKUP(slice_state, SliceState);
@@ -259,7 +278,7 @@ DEFINE_STRING_TABLE_LOOKUP(swap_state, SwapState);
static const char* const target_state_table[_TARGET_STATE_MAX] = {
[TARGET_DEAD] = "dead",
- [TARGET_ACTIVE] = "active"
+ [TARGET_ACTIVE] = "active",
};
DEFINE_STRING_TABLE_LOOKUP(target_state, TargetState);
@@ -269,7 +288,7 @@ static const char* const timer_state_table[_TIMER_STATE_MAX] = {
[TIMER_WAITING] = "waiting",
[TIMER_RUNNING] = "running",
[TIMER_ELAPSED] = "elapsed",
- [TIMER_FAILED] = "failed"
+ [TIMER_FAILED] = "failed",
};
DEFINE_STRING_TABLE_LOOKUP(timer_state, TimerState);
@@ -314,7 +333,7 @@ static const char* const notify_access_table[_NOTIFY_ACCESS_MAX] = {
[NOTIFY_NONE] = "none",
[NOTIFY_MAIN] = "main",
[NOTIFY_EXEC] = "exec",
- [NOTIFY_ALL] = "all"
+ [NOTIFY_ALL] = "all",
};
DEFINE_STRING_TABLE_LOOKUP(notify_access, NotifyAccess);
diff --git a/src/basic/unit-def.h b/src/basic/unit-def.h
index 6627da5..8e73e28 100644
--- a/src/basic/unit-def.h
+++ b/src/basic/unit-def.h
@@ -53,8 +53,10 @@ typedef enum UnitActiveState {
typedef enum FreezerState {
FREEZER_RUNNING,
- FREEZER_FREEZING,
+ FREEZER_FREEZING, /* freezing due to user request */
FREEZER_FROZEN,
+ FREEZER_FREEZING_BY_PARENT, /* freezing as a result of parent slice freezing */
+ FREEZER_FROZEN_BY_PARENT,
FREEZER_THAWING,
_FREEZER_STATE_MAX,
_FREEZER_STATE_INVALID = -EINVAL,
@@ -297,6 +299,7 @@ UnitActiveState unit_active_state_from_string(const char *s) _pure_;
const char *freezer_state_to_string(FreezerState i) _const_;
FreezerState freezer_state_from_string(const char *s) _pure_;
+FreezerState freezer_state_finish(FreezerState i) _const_;
const char *unit_marker_to_string(UnitMarker m) _const_;
UnitMarker unit_marker_from_string(const char *s) _pure_;
diff --git a/src/basic/unit-name.c b/src/basic/unit-name.c
index 8bf28ba..4e2f77c 100644
--- a/src/basic/unit-name.c
+++ b/src/basic/unit-name.c
@@ -454,34 +454,45 @@ int unit_name_path_unescape(const char *f, char **ret) {
return 0;
}
-int unit_name_replace_instance(const char *f, const char *i, char **ret) {
+int unit_name_replace_instance_full(
+ const char *original,
+ const char *instance,
+ bool accept_glob,
+ char **ret) {
+
_cleanup_free_ char *s = NULL;
- const char *p, *e;
- size_t a, b;
+ const char *prefix, *suffix;
+ size_t pl;
- assert(f);
- assert(i);
+ assert(original);
+ assert(instance);
assert(ret);
- if (!unit_name_is_valid(f, UNIT_NAME_INSTANCE|UNIT_NAME_TEMPLATE))
+ if (!unit_name_is_valid(original, UNIT_NAME_INSTANCE|UNIT_NAME_TEMPLATE))
return -EINVAL;
- if (!unit_instance_is_valid(i))
+ if (!unit_instance_is_valid(instance) && !(accept_glob && in_charset(instance, VALID_CHARS_GLOB)))
return -EINVAL;
- assert_se(p = strchr(f, '@'));
- assert_se(e = strrchr(f, '.'));
+ prefix = ASSERT_PTR(strchr(original, '@'));
+ suffix = ASSERT_PTR(strrchr(original, '.'));
+ assert(prefix < suffix);
- a = p - f;
- b = strlen(i);
+ pl = prefix - original + 1; /* include '@' */
- s = new(char, a + 1 + b + strlen(e) + 1);
+ s = new(char, pl + strlen(instance) + strlen(suffix) + 1);
if (!s)
return -ENOMEM;
- strcpy(mempcpy(mempcpy(s, f, a + 1), i, b), e);
+#if HAS_FEATURE_MEMORY_SANITIZER
+ /* MSan doesn't like stpncpy... See also https://github.com/google/sanitizers/issues/926 */
+ memzero(s, pl + strlen(instance) + strlen(suffix) + 1);
+#endif
- /* Make sure the resulting name still is valid, i.e. didn't grow too large */
- if (!unit_name_is_valid(s, UNIT_NAME_INSTANCE))
+ strcpy(stpcpy(stpncpy(s, original, pl), instance), suffix);
+
+ /* Make sure the resulting name still is valid, i.e. didn't grow too large. Globs will be expanded
+ * by clients when used, so the check is pointless. */
+ if (!accept_glob && !unit_name_is_valid(s, UNIT_NAME_INSTANCE))
return -EINVAL;
*ret = TAKE_PTR(s);
@@ -782,19 +793,10 @@ int unit_name_mangle_with_suffix(
return 1;
good:
- s = strdup(name);
- if (!s)
- return -ENOMEM;
-
- *ret = TAKE_PTR(s);
- return 0;
+ return strdup_to(ret, name);
}
int slice_build_parent_slice(const char *slice, char **ret) {
- _cleanup_free_ char *s = NULL;
- char *dash;
- int r;
-
assert(slice);
assert(ret);
@@ -806,18 +808,16 @@ int slice_build_parent_slice(const char *slice, char **ret) {
return 0;
}
- s = strdup(slice);
+ _cleanup_free_ char *s = strdup(slice);
if (!s)
return -ENOMEM;
- dash = strrchr(s, '-');
- if (dash)
- strcpy(dash, ".slice");
- else {
- r = free_and_strdup(&s, SPECIAL_ROOT_SLICE);
- if (r < 0)
- return r;
- }
+ char *dash = strrchr(s, '-');
+ if (!dash)
+ return strdup_to_full(ret, SPECIAL_ROOT_SLICE);
+
+ /* We know that s ended with .slice before truncation, so we have enough space. */
+ strcpy(dash, ".slice");
*ret = TAKE_PTR(s);
return 1;
diff --git a/src/basic/unit-name.h b/src/basic/unit-name.h
index eaa701e..fa7295e 100644
--- a/src/basic/unit-name.h
+++ b/src/basic/unit-name.h
@@ -33,14 +33,21 @@ UnitType unit_name_to_type(const char *n) _pure_;
int unit_name_change_suffix(const char *n, const char *suffix, char **ret);
int unit_name_build(const char *prefix, const char *instance, const char *suffix, char **ret);
-int unit_name_build_from_type(const char *prefix, const char *instance, UnitType, char **ret);
+int unit_name_build_from_type(const char *prefix, const char *instance, UnitType type, char **ret);
char *unit_name_escape(const char *f);
int unit_name_unescape(const char *f, char **ret);
int unit_name_path_escape(const char *f, char **ret);
int unit_name_path_unescape(const char *f, char **ret);
-int unit_name_replace_instance(const char *f, const char *i, char **ret);
+int unit_name_replace_instance_full(
+ const char *original,
+ const char *instance,
+ bool accept_glob,
+ char **ret);
+static inline int unit_name_replace_instance(const char *original, const char *instance, char **ret) {
+ return unit_name_replace_instance_full(original, instance, false, ret);
+}
int unit_name_template(const char *f, char **ret);
diff --git a/src/basic/user-util.c b/src/basic/user-util.c
index 9e6926b..6bdf5bf 100644
--- a/src/basic/user-util.c
+++ b/src/basic/user-util.c
@@ -184,27 +184,28 @@ const char* default_root_shell(const char *root) {
static int synthesize_user_creds(
const char **username,
- uid_t *uid, gid_t *gid,
- const char **home,
- const char **shell,
+ uid_t *ret_uid, gid_t *ret_gid,
+ const char **ret_home,
+ const char **ret_shell,
UserCredsFlags flags) {
+ assert(username);
+ assert(*username);
+
/* We enforce some special rules for uid=0 and uid=65534: in order to avoid NSS lookups for root we hardcode
* their user record data. */
if (STR_IN_SET(*username, "root", "0")) {
*username = "root";
- if (uid)
- *uid = 0;
- if (gid)
- *gid = 0;
-
- if (home)
- *home = "/root";
-
- if (shell)
- *shell = default_root_shell(NULL);
+ if (ret_uid)
+ *ret_uid = 0;
+ if (ret_gid)
+ *ret_gid = 0;
+ if (ret_home)
+ *ret_home = "/root";
+ if (ret_shell)
+ *ret_shell = default_root_shell(NULL);
return 0;
}
@@ -213,16 +214,14 @@ static int synthesize_user_creds(
synthesize_nobody()) {
*username = NOBODY_USER_NAME;
- if (uid)
- *uid = UID_NOBODY;
- if (gid)
- *gid = GID_NOBODY;
-
- if (home)
- *home = FLAGS_SET(flags, USER_CREDS_CLEAN) ? NULL : "/";
-
- if (shell)
- *shell = FLAGS_SET(flags, USER_CREDS_CLEAN) ? NULL : NOLOGIN;
+ if (ret_uid)
+ *ret_uid = UID_NOBODY;
+ if (ret_gid)
+ *ret_gid = GID_NOBODY;
+ if (ret_home)
+ *ret_home = FLAGS_SET(flags, USER_CREDS_CLEAN) ? NULL : "/";
+ if (ret_shell)
+ *ret_shell = FLAGS_SET(flags, USER_CREDS_CLEAN) ? NULL : NOLOGIN;
return 0;
}
@@ -232,11 +231,12 @@ static int synthesize_user_creds(
int get_user_creds(
const char **username,
- uid_t *uid, gid_t *gid,
- const char **home,
- const char **shell,
+ uid_t *ret_uid, gid_t *ret_gid,
+ const char **ret_home,
+ const char **ret_shell,
UserCredsFlags flags) {
+ bool patch_username = false;
uid_t u = UID_INVALID;
struct passwd *p;
int r;
@@ -245,7 +245,7 @@ int get_user_creds(
assert(*username);
if (!FLAGS_SET(flags, USER_CREDS_PREFER_NSS) ||
- (!home && !shell)) {
+ (!ret_home && !ret_shell)) {
/* So here's the deal: normally, we'll try to synthesize all records we can synthesize, and override
* the user database with that. However, if the user specifies USER_CREDS_PREFER_NSS then the
@@ -256,7 +256,7 @@ int get_user_creds(
* of the relevant users, but changing the UID/GID mappings for them is something we explicitly don't
* support. */
- r = synthesize_user_creds(username, uid, gid, home, shell, flags);
+ r = synthesize_user_creds(username, ret_uid, ret_gid, ret_home, ret_shell, flags);
if (r >= 0)
return 0;
if (r != -ENOMEDIUM) /* not a username we can synthesize */
@@ -271,15 +271,15 @@ int get_user_creds(
* instead of the first occurrence in the database. However if the uid was configured by a numeric uid,
* then let's pick the real username from /etc/passwd. */
if (p)
- *username = p->pw_name;
- else if (FLAGS_SET(flags, USER_CREDS_ALLOW_MISSING) && !gid && !home && !shell) {
+ patch_username = true;
+ else if (FLAGS_SET(flags, USER_CREDS_ALLOW_MISSING) && !ret_gid && !ret_home && !ret_shell) {
/* If the specified user is a numeric UID and it isn't in the user database, and the caller
* passed USER_CREDS_ALLOW_MISSING and was only interested in the UID, then just return that
* and don't complain. */
- if (uid)
- *uid = u;
+ if (ret_uid)
+ *ret_uid = u;
return 0;
}
@@ -293,65 +293,57 @@ int get_user_creds(
r = IN_SET(errno, 0, ENOENT) ? -ESRCH : -errno;
/* If the user requested that we only synthesize as fallback, do so now */
- if (FLAGS_SET(flags, USER_CREDS_PREFER_NSS)) {
- if (synthesize_user_creds(username, uid, gid, home, shell, flags) >= 0)
+ if (FLAGS_SET(flags, USER_CREDS_PREFER_NSS))
+ if (synthesize_user_creds(username, ret_uid, ret_gid, ret_home, ret_shell, flags) >= 0)
return 0;
- }
return r;
}
- if (uid) {
- if (!uid_is_valid(p->pw_uid))
- return -EBADMSG;
+ if (ret_uid && !uid_is_valid(p->pw_uid))
+ return -EBADMSG;
- *uid = p->pw_uid;
- }
+ if (ret_gid && !gid_is_valid(p->pw_gid))
+ return -EBADMSG;
- if (gid) {
- if (!gid_is_valid(p->pw_gid))
- return -EBADMSG;
+ if (ret_uid)
+ *ret_uid = p->pw_uid;
- *gid = p->pw_gid;
- }
+ if (ret_gid)
+ *ret_gid = p->pw_gid;
- if (home) {
- if (FLAGS_SET(flags, USER_CREDS_CLEAN) &&
- (empty_or_root(p->pw_dir) ||
- !path_is_valid(p->pw_dir) ||
- !path_is_absolute(p->pw_dir)))
- *home = NULL; /* Note: we don't insist on normalized paths, since there are setups that have /./ in the path */
- else
- *home = p->pw_dir;
- }
+ if (ret_home)
+ /* Note: we don't insist on normalized paths, since there are setups that have /./ in the path */
+ *ret_home = (FLAGS_SET(flags, USER_CREDS_CLEAN) &&
+ (empty_or_root(p->pw_dir) ||
+ !path_is_valid(p->pw_dir) ||
+ !path_is_absolute(p->pw_dir))) ? NULL : p->pw_dir;
- if (shell) {
- if (FLAGS_SET(flags, USER_CREDS_CLEAN) &&
- (isempty(p->pw_shell) ||
- !path_is_valid(p->pw_shell) ||
- !path_is_absolute(p->pw_shell) ||
- is_nologin_shell(p->pw_shell)))
- *shell = NULL;
- else
- *shell = p->pw_shell;
- }
+ if (ret_shell)
+ *ret_shell = (FLAGS_SET(flags, USER_CREDS_CLEAN) &&
+ (isempty(p->pw_shell) ||
+ !path_is_valid(p->pw_shell) ||
+ !path_is_absolute(p->pw_shell) ||
+ is_nologin_shell(p->pw_shell))) ? NULL : p->pw_shell;
+
+ if (patch_username)
+ *username = p->pw_name;
return 0;
}
-int get_group_creds(const char **groupname, gid_t *gid, UserCredsFlags flags) {
- struct group *g;
- gid_t id;
+static int synthesize_group_creds(
+ const char **groupname,
+ gid_t *ret_gid) {
assert(groupname);
-
- /* We enforce some special rules for gid=0: in order to avoid NSS lookups for root we hardcode its data. */
+ assert(*groupname);
if (STR_IN_SET(*groupname, "root", "0")) {
*groupname = "root";
- if (gid)
- *gid = 0;
+ if (ret_gid)
+ *ret_gid = 0;
return 0;
}
@@ -360,21 +352,41 @@ int get_group_creds(const char **groupname, gid_t *gid, UserCredsFlags flags) {
synthesize_nobody()) {
*groupname = NOBODY_GROUP_NAME;
- if (gid)
- *gid = GID_NOBODY;
+ if (ret_gid)
+ *ret_gid = GID_NOBODY;
return 0;
}
+ return -ENOMEDIUM;
+}
+
+int get_group_creds(const char **groupname, gid_t *ret_gid, UserCredsFlags flags) {
+ bool patch_groupname = false;
+ struct group *g;
+ gid_t id;
+ int r;
+
+ assert(groupname);
+ assert(*groupname);
+
+ if (!FLAGS_SET(flags, USER_CREDS_PREFER_NSS)) {
+ r = synthesize_group_creds(groupname, ret_gid);
+ if (r >= 0)
+ return 0;
+ if (r != -ENOMEDIUM) /* not a groupname we can synthesize */
+ return r;
+ }
+
if (parse_gid(*groupname, &id) >= 0) {
errno = 0;
g = getgrgid(id);
if (g)
- *groupname = g->gr_name;
+ patch_groupname = true;
else if (FLAGS_SET(flags, USER_CREDS_ALLOW_MISSING)) {
- if (gid)
- *gid = id;
+ if (ret_gid)
+ *ret_gid = id;
return 0;
}
@@ -383,18 +395,28 @@ int get_group_creds(const char **groupname, gid_t *gid, UserCredsFlags flags) {
g = getgrnam(*groupname);
}
- if (!g)
+ if (!g) {
/* getgrnam() may fail with ENOENT if /etc/group is missing.
* For us that is equivalent to the name not being defined. */
- return IN_SET(errno, 0, ENOENT) ? -ESRCH : -errno;
+ r = IN_SET(errno, 0, ENOENT) ? -ESRCH : -errno;
+
+ if (FLAGS_SET(flags, USER_CREDS_PREFER_NSS))
+ if (synthesize_group_creds(groupname, ret_gid) >= 0)
+ return 0;
- if (gid) {
+ return r;
+ }
+
+ if (ret_gid) {
if (!gid_is_valid(g->gr_gid))
return -EBADMSG;
- *gid = g->gr_gid;
+ *ret_gid = g->gr_gid;
}
+ if (patch_groupname)
+ *groupname = g->gr_name;
+
return 0;
}
@@ -409,31 +431,11 @@ char* uid_to_name(uid_t uid) {
return strdup(NOBODY_USER_NAME);
if (uid_is_valid(uid)) {
- long bufsize;
-
- bufsize = sysconf(_SC_GETPW_R_SIZE_MAX);
- if (bufsize <= 0)
- bufsize = 4096;
+ _cleanup_free_ struct passwd *pw = NULL;
- for (;;) {
- struct passwd pwbuf, *pw = NULL;
- _cleanup_free_ char *buf = NULL;
-
- buf = malloc(bufsize);
- if (!buf)
- return NULL;
-
- r = getpwuid_r(uid, &pwbuf, buf, (size_t) bufsize, &pw);
- if (r == 0 && pw)
- return strdup(pw->pw_name);
- if (r != ERANGE)
- break;
-
- if (bufsize > LONG_MAX/2) /* overflow check */
- return NULL;
-
- bufsize *= 2;
- }
+ r = getpwuid_malloc(uid, &pw);
+ if (r >= 0)
+ return strdup(pw->pw_name);
}
if (asprintf(&ret, UID_FMT, uid) < 0)
@@ -452,31 +454,11 @@ char* gid_to_name(gid_t gid) {
return strdup(NOBODY_GROUP_NAME);
if (gid_is_valid(gid)) {
- long bufsize;
-
- bufsize = sysconf(_SC_GETGR_R_SIZE_MAX);
- if (bufsize <= 0)
- bufsize = 4096;
-
- for (;;) {
- struct group grbuf, *gr = NULL;
- _cleanup_free_ char *buf = NULL;
-
- buf = malloc(bufsize);
- if (!buf)
- return NULL;
-
- r = getgrgid_r(gid, &grbuf, buf, (size_t) bufsize, &gr);
- if (r == 0 && gr)
- return strdup(gr->gr_name);
- if (r != ERANGE)
- break;
-
- if (bufsize > LONG_MAX/2) /* overflow check */
- return NULL;
+ _cleanup_free_ struct group *gr = NULL;
- bufsize *= 2;
- }
+ r = getgrgid_malloc(gid, &gr);
+ if (r >= 0)
+ return strdup(gr->gr_name);
}
if (asprintf(&ret, GID_FMT, gid) < 0)
@@ -587,9 +569,10 @@ int getgroups_alloc(gid_t** gids) {
}
int get_home_dir(char **ret) {
- struct passwd *p;
+ _cleanup_free_ struct passwd *p = NULL;
const char *e;
uid_t u;
+ int r;
assert(ret);
@@ -604,19 +587,17 @@ int get_home_dir(char **ret) {
e = "/root";
goto found;
}
-
if (u == UID_NOBODY && synthesize_nobody()) {
e = "/";
goto found;
}
/* Check the database... */
- errno = 0;
- p = getpwuid(u);
- if (!p)
- return errno_or_else(ESRCH);
- e = p->pw_dir;
+ r = getpwuid_malloc(u, &p);
+ if (r < 0)
+ return r;
+ e = p->pw_dir;
if (!path_is_valid(e) || !path_is_absolute(e))
return -EINVAL;
@@ -625,9 +606,10 @@ int get_home_dir(char **ret) {
}
int get_shell(char **ret) {
- struct passwd *p;
+ _cleanup_free_ struct passwd *p = NULL;
const char *e;
uid_t u;
+ int r;
assert(ret);
@@ -648,12 +630,11 @@ int get_shell(char **ret) {
}
/* Check the database... */
- errno = 0;
- p = getpwuid(u);
- if (!p)
- return errno_or_else(ESRCH);
- e = p->pw_shell;
+ r = getpwuid_malloc(u, &p);
+ if (r < 0)
+ return r;
+ e = p->pw_shell;
if (!path_is_valid(e) || !path_is_absolute(e))
return -EINVAL;
@@ -661,17 +642,26 @@ int get_shell(char **ret) {
return path_simplify_alloc(e, 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) {
int r;
- r = maybe_setgroups(0, NULL);
+ assert(supplementary_gids || n_supplementary_gids == 0);
+
+ /* Sets all UIDs and all GIDs to the specified ones. Drops all auxiliary GIDs */
+
+ r = maybe_setgroups(n_supplementary_gids, supplementary_gids);
if (r < 0)
return r;
- if (setresgid(0, 0, 0) < 0)
- return -errno;
+ if (gid_is_valid(gid))
+ if (setresgid(gid, gid, gid) < 0)
+ return -errno;
+
+ if (uid_is_valid(uid))
+ if (setresuid(uid, uid, uid) < 0)
+ return -errno;
- return RET_NERRNO(setresuid(0, 0, 0));
+ return 0;
}
int take_etc_passwd_lock(const char *root) {
@@ -807,11 +797,11 @@ bool valid_user_group_name(const char *u, ValidUserFlags flags) {
sz = sysconf(_SC_LOGIN_NAME_MAX);
assert_se(sz > 0);
- if (l > (size_t) sz)
+ if (l > (size_t) sz) /* glibc: 256 */
return false;
- if (l > NAME_MAX) /* must fit in a filename */
+ if (l > NAME_MAX) /* must fit in a filename: 255 */
return false;
- if (l > UT_NAMESIZE - 1)
+ if (l > UT_NAMESIZE - 1) /* must fit in utmp: 31 */
return false;
}
@@ -987,8 +977,8 @@ int fgetpwent_sane(FILE *stream, struct passwd **pw) {
errno = 0;
struct passwd *p = fgetpwent(stream);
- if (!p && errno != ENOENT)
- return errno_or_else(EIO);
+ if (!p && !IN_SET(errno, 0, ENOENT))
+ return -errno;
*pw = p;
return !!p;
@@ -1000,8 +990,8 @@ int fgetspent_sane(FILE *stream, struct spwd **sp) {
errno = 0;
struct spwd *s = fgetspent(stream);
- if (!s && errno != ENOENT)
- return errno_or_else(EIO);
+ if (!s && !IN_SET(errno, 0, ENOENT))
+ return -errno;
*sp = s;
return !!s;
@@ -1013,8 +1003,8 @@ int fgetgrent_sane(FILE *stream, struct group **gr) {
errno = 0;
struct group *g = fgetgrent(stream);
- if (!g && errno != ENOENT)
- return errno_or_else(EIO);
+ if (!g && !IN_SET(errno, 0, ENOENT))
+ return -errno;
*gr = g;
return !!g;
@@ -1027,8 +1017,8 @@ int fgetsgent_sane(FILE *stream, struct sgrp **sg) {
errno = 0;
struct sgrp *s = fgetsgent(stream);
- if (!s && errno != ENOENT)
- return errno_or_else(EIO);
+ if (!s && !IN_SET(errno, 0, ENOENT))
+ return -errno;
*sg = s;
return !!s;
@@ -1058,3 +1048,180 @@ const char* get_home_root(void) {
return "/home";
}
+
+static size_t getpw_buffer_size(void) {
+ long bufsize = sysconf(_SC_GETPW_R_SIZE_MAX);
+ return bufsize <= 0 ? 4096U : (size_t) bufsize;
+}
+
+static bool errno_is_user_doesnt_exist(int error) {
+ /* See getpwnam(3) and getgrnam(3): those codes and others can be returned if the user or group are
+ * not found. */
+ return IN_SET(abs(error), ENOENT, ESRCH, EBADF, EPERM);
+}
+
+int getpwnam_malloc(const char *name, struct passwd **ret) {
+ size_t bufsize = getpw_buffer_size();
+ int r;
+
+ /* A wrapper around getpwnam_r() that allocates the necessary buffer on the heap. The caller must
+ * free() the returned structures! */
+
+ if (isempty(name))
+ return -EINVAL;
+
+ for (;;) {
+ _cleanup_free_ void *buf = NULL;
+
+ buf = malloc(ALIGN(sizeof(struct passwd)) + bufsize);
+ if (!buf)
+ return -ENOMEM;
+
+ struct passwd *pw = NULL;
+ r = getpwnam_r(name, buf, (char*) buf + ALIGN(sizeof(struct passwd)), (size_t) bufsize, &pw);
+ if (r == 0) {
+ if (pw) {
+ if (ret)
+ *ret = TAKE_PTR(buf);
+ return 0;
+ }
+
+ return -ESRCH;
+ }
+
+ assert(r > 0);
+
+ /* getpwnam() may fail with ENOENT if /etc/passwd is missing. For us that is equivalent to
+ * the name not being defined. */
+ if (errno_is_user_doesnt_exist(r))
+ return -ESRCH;
+ if (r != ERANGE)
+ return -r;
+
+ if (bufsize > SIZE_MAX/2 - ALIGN(sizeof(struct passwd)))
+ return -ENOMEM;
+ bufsize *= 2;
+ }
+}
+
+int getpwuid_malloc(uid_t uid, struct passwd **ret) {
+ size_t bufsize = getpw_buffer_size();
+ int r;
+
+ if (!uid_is_valid(uid))
+ return -EINVAL;
+
+ for (;;) {
+ _cleanup_free_ void *buf = NULL;
+
+ buf = malloc(ALIGN(sizeof(struct passwd)) + bufsize);
+ if (!buf)
+ return -ENOMEM;
+
+ struct passwd *pw = NULL;
+ r = getpwuid_r(uid, buf, (char*) buf + ALIGN(sizeof(struct passwd)), (size_t) bufsize, &pw);
+ if (r == 0) {
+ if (pw) {
+ if (ret)
+ *ret = TAKE_PTR(buf);
+ return 0;
+ }
+
+ return -ESRCH;
+ }
+
+ assert(r > 0);
+
+ if (errno_is_user_doesnt_exist(r))
+ return -ESRCH;
+ if (r != ERANGE)
+ return -r;
+
+ if (bufsize > SIZE_MAX/2 - ALIGN(sizeof(struct passwd)))
+ return -ENOMEM;
+ bufsize *= 2;
+ }
+}
+
+static size_t getgr_buffer_size(void) {
+ long bufsize = sysconf(_SC_GETGR_R_SIZE_MAX);
+ return bufsize <= 0 ? 4096U : (size_t) bufsize;
+}
+
+int getgrnam_malloc(const char *name, struct group **ret) {
+ size_t bufsize = getgr_buffer_size();
+ int r;
+
+ if (isempty(name))
+ return -EINVAL;
+
+ for (;;) {
+ _cleanup_free_ void *buf = NULL;
+
+ buf = malloc(ALIGN(sizeof(struct group)) + bufsize);
+ if (!buf)
+ return -ENOMEM;
+
+ struct group *gr = NULL;
+ r = getgrnam_r(name, buf, (char*) buf + ALIGN(sizeof(struct group)), (size_t) bufsize, &gr);
+ if (r == 0) {
+ if (gr) {
+ if (ret)
+ *ret = TAKE_PTR(buf);
+ return 0;
+ }
+
+ return -ESRCH;
+ }
+
+ assert(r > 0);
+
+ if (errno_is_user_doesnt_exist(r))
+ return -ESRCH;
+ if (r != ERANGE)
+ return -r;
+
+ if (bufsize > SIZE_MAX/2 - ALIGN(sizeof(struct group)))
+ return -ENOMEM;
+ bufsize *= 2;
+ }
+}
+
+int getgrgid_malloc(gid_t gid, struct group **ret) {
+ size_t bufsize = getgr_buffer_size();
+ int r;
+
+ if (!gid_is_valid(gid))
+ return -EINVAL;
+
+ for (;;) {
+ _cleanup_free_ void *buf = NULL;
+
+ buf = malloc(ALIGN(sizeof(struct group)) + bufsize);
+ if (!buf)
+ return -ENOMEM;
+
+ struct group *gr = NULL;
+ r = getgrgid_r(gid, buf, (char*) buf + ALIGN(sizeof(struct group)), (size_t) bufsize, &gr);
+ if (r == 0) {
+ if (gr) {
+ if (ret)
+ *ret = TAKE_PTR(buf);
+ return 0;
+ }
+
+ return -ESRCH;
+ }
+
+ assert(r > 0);
+
+ if (errno_is_user_doesnt_exist(r))
+ return -ESRCH;
+ if (r != ERANGE)
+ return -r;
+
+ if (bufsize > SIZE_MAX/2 - ALIGN(sizeof(struct group)))
+ return -ENOMEM;
+ bufsize *= 2;
+ }
+}
diff --git a/src/basic/user-util.h b/src/basic/user-util.h
index f394f62..9d07ef3 100644
--- a/src/basic/user-util.h
+++ b/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);
@@ -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/basic/utf8.c b/src/basic/utf8.c
index 36e1e0f..36f0dc9 100644
--- a/src/basic/utf8.c
+++ b/src/basic/utf8.c
@@ -1,26 +1,10 @@
-/* SPDX-License-Identifier: LGPL-2.1-or-later */
+/* SPDX-License-Identifier: LGPL-2.0-or-later */
-/* Parts of this file are based on the GLIB utf8 validation functions. The
- * original license text follows. */
-
-/* gutf8.c - Operations on UTF-8 strings.
+/* Parts of this file are based on the GLIB utf8 validation functions. The original copyright follows.
*
+ * gutf8.c - Operations on UTF-8 strings.
* Copyright (C) 1999 Tom Tromey
* Copyright (C) 2000 Red Hat, Inc.
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Library General Public
- * License as published by the Free Software Foundation; either
- * version 2 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Library General Public License for more details.
- *
- * You should have received a copy of the GNU Library General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include <errno.h>
diff --git a/src/basic/virt.c b/src/basic/virt.c
index 88357a9..0970350 100644
--- a/src/basic/virt.c
+++ b/src/basic/virt.c
@@ -21,6 +21,7 @@
#include "stat-util.h"
#include "string-table.h"
#include "string-util.h"
+#include "uid-range.h"
#include "virt.h"
enum {
@@ -446,7 +447,7 @@ static Virtualization detect_vm_zvm(void) {
/* Returns a short identifier for the various VM implementations */
Virtualization detect_vm(void) {
static thread_local Virtualization cached_found = _VIRTUALIZATION_INVALID;
- bool other = false;
+ bool other = false, hyperv = false;
int xen_dom0 = 0;
Virtualization v, dmi;
@@ -455,12 +456,12 @@ Virtualization detect_vm(void) {
/* We have to use the correct order here:
*
- * → First, try to detect Oracle Virtualbox, Amazon EC2 Nitro, Parallels, and Google Compute Engine, even if they use KVM,
- * as well as Xen even if it cloaks as Microsoft Hyper-V. Attempt to detect uml at this stage also
- * since it runs as a user-process nested inside other VMs. Also check for Xen now, because Xen PV
- * mode does not override CPUID when nested inside another hypervisor.
+ * → First, try to detect Oracle Virtualbox, Amazon EC2 Nitro, Parallels, and Google Compute Engine,
+ * even if they use KVM, as well as Xen, even if it cloaks as Microsoft Hyper-V. Attempt to detect
+ * UML at this stage too, since it runs as a user-process nested inside other VMs. Also check for
+ * Xen now, because Xen PV mode does not override CPUID when nested inside another hypervisor.
*
- * → Second, try to detect from CPUID, this will report KVM for whatever software is used even if
+ * → Second, try to detect from CPUID. This will report KVM for whatever software is used even if
* info in DMI is overwritten.
*
* → Third, try to detect from DMI. */
@@ -503,7 +504,12 @@ Virtualization detect_vm(void) {
v = detect_vm_cpuid();
if (v < 0)
return v;
- if (v == VIRTUALIZATION_VM_OTHER)
+ if (v == VIRTUALIZATION_MICROSOFT)
+ /* QEMU sets the CPUID string to hyperv's, in case it provides hyperv enlightenments. Let's
+ * hence not return Microsoft here but just use the other mechanisms first to make a better
+ * decision. */
+ hyperv = true;
+ else if (v == VIRTUALIZATION_VM_OTHER)
other = true;
else if (v != VIRTUALIZATION_NONE)
goto finish;
@@ -544,8 +550,15 @@ Virtualization detect_vm(void) {
return v;
finish:
- if (v == VIRTUALIZATION_NONE && other)
- v = VIRTUALIZATION_VM_OTHER;
+ /* None of the checks above gave us a clear answer, hence let's now use fallback logic: if hyperv
+ * enlightenments are available but the VMM wasn't recognized as anything yet, it's probably
+ * Microsoft. */
+ if (v == VIRTUALIZATION_NONE) {
+ if (hyperv)
+ v = VIRTUALIZATION_MICROSOFT;
+ else if (other)
+ v = VIRTUALIZATION_VM_OTHER;
+ }
cached_found = v;
log_debug("Found VM virtualization %s", virtualization_to_string(v));
@@ -818,7 +831,7 @@ Virtualization detect_virtualization(void) {
static int userns_has_mapping(const char *name) {
_cleanup_fclose_ FILE *f = NULL;
- uid_t a, b, c;
+ uid_t base, shift, range;
int r;
f = fopen(name, "re");
@@ -827,26 +840,22 @@ static int userns_has_mapping(const char *name) {
return errno == ENOENT ? false : -errno;
}
- errno = 0;
- r = fscanf(f, UID_FMT " " UID_FMT " " UID_FMT "\n", &a, &b, &c);
- if (r == EOF) {
- if (ferror(f))
- return log_debug_errno(errno_or_else(EIO), "Failed to read %s: %m", name);
-
- log_debug("%s is empty, we're in an uninitialized user namespace", name);
+ r = uid_map_read_one(f, &base, &shift, &range);
+ if (r == -ENOMSG) {
+ log_debug("%s is empty, we're in an uninitialized user namespace.", name);
return true;
}
- if (r != 3)
- return log_debug_errno(SYNTHETIC_ERRNO(EBADMSG), "Failed to parse %s: %m", name);
+ if (r < 0)
+ return log_debug_errno(r, "Failed to read %s: %m", name);
- if (a == 0 && b == 0 && c == UINT32_MAX) {
+ if (base == 0 && shift == 0 && range == UINT32_MAX) {
/* The kernel calls mappings_overlap() and does not allow overlaps */
log_debug("%s has a full 1:1 mapping", name);
return false;
}
/* Anything else implies that we are in a user namespace */
- log_debug("Mapping found in %s, we're in a user namespace", name);
+ log_debug("Mapping found in %s, we're in a user namespace.", name);
return true;
}