https://github.com/systemd/systemd/issues
OLDER DOCUMENTATION:
- http://0pointer.de/blog/projects/systemd.html
+ https://0pointer.de/blog/projects/systemd.html
https://www.freedesktop.org/wiki/Software/systemd
AUTHOR:
Required for signed Verity images support:
CONFIG_DM_VERITY_VERIFY_ROOTHASH_SIG
+ Required to verify signed Verity images using keys enrolled in the MoK
+ (Machine-Owner Key) keyring:
+ CONFIG_DM_VERITY_VERIFY_ROOTHASH_SIG_SECONDARY_KEYRING
+ CONFIG_IMA_ARCH_POLICY
+ CONFIG_INTEGRITY_MACHINE_KEYRING
Required for RestrictFileSystems= in service units:
CONFIG_BPF
Features:
+* sd-boot: add a new PE section .bls or so that carries a cpio with additional
+ boot loader entries (both type1 and type2). Then when initializing, find this
+ section, iterate through it and populate menu with it. cpio is simple enough
+ to make a parser for this reasonably robust. use same path structures as in
+ the ESP. Similar add one for signature key drop-ins.
+
+* add a new EFI tool "sd-fetch" or so. It looks in a PE section ".url" for an
+ URL, then downloads the file from it using UEFI HTTP APIs, and executes it.
+ Usecase: provide a minimal ESP with sd-boot and a couple of these sd-fetch
+ binaries in place of UKIs, and download them on-the-fly.
+
+* bootctl: warn if ESP is mounted world-readable (and in particular the seed)
+
* maybe: systemd-loop-generator that sets up loopback devices if requested via kernel
cmdline. usecase: include encrypted/verity root fs in UKI.
* `$SYSTEMD_MEMPOOL=0` — if set, the internal memory caching logic employed by
hash tables is turned off, and libc `malloc()` is used for all allocations.
+* `$SYSTEMD_UTF8=` — takes a boolean value, and overrides whether to generate
+ non-ASCII special glyphs at various places (i.e. "→" instead of
+ "->"). Usually this is deterined automatically, based on $LC_CTYPE, but in
+ scenarios where locale definitions are not installed it might make sense to
+ override this check explicitly.
+
* `$SYSTEMD_EMOJI=0` — if set, tools such as `systemd-analyze security` will
not output graphical smiley emojis, but ASCII alternatives instead. Note that
this only controls use of Unicode emoji glyphs, and has no effect on other
<title>Description</title>
<para><command>systemd-dissect</command> is a tool for introspecting and interacting with file system OS
- disk images, specifically Discoverable Disk Images (DDIs). It supports five different operations:</para>
+ disk images, specifically Discoverable Disk Images (DDIs). It supports four different operations:</para>
<orderedlist>
<listitem><para>Show general OS image information, including the image's
--add-section .initrd=initrd.cpio --change-section-vma .initrd=0x3000000 \
--add-section .splash=splash.bmp --change-section-vma .splash=0x100000 \
--add-section .dtb=devicetree.dtb --change-section-vma .dtb=0x40000 \
- --add-section .pcrsig=tpm2-pcr-signature.json --change-section-vma .splash=0x80000 \
- --add-section .pcrpkey=tpm2-pcr-public.pem --change-section-vma .splash=0x90000 \
+ --add-section .pcrsig=tpm2-pcr-signature.json --change-section-vma .pcrsig=0x80000 \
+ --add-section .pcrpkey=tpm2-pcr-public.pem --change-section-vma .pcrpkey=0x90000 \
/usr/lib/systemd/boot/efi/linuxx64.efi.stub \
foo.efi</programlisting>
install : true,
install_dir : rootlibexecdir)
-executable(
+public_programs += executable(
'systemd-ac-power',
'src/ac-power/ac-power.c',
include_directories : includes,
link_with : [libshared],
dependencies : [versiondep],
install_rpath : rootpkglibdir,
- install : true,
- install_dir : rootlibexecdir)
+ install : true)
public_programs += executable(
'systemd-detect-virt',
--enable MEMCG \
--enable MEMCG_SWAP \
--enable MEMCG_KMEM \
+ --enable IMA_ARCH_POLICY \
+ --enable DM_VERITY_VERIFY_ROOTHASH_SIG \
+ --enable DM_VERITY_VERIFY_ROOTHASH_SIG_SECONDARY_KEYRING \
+ --enable INTEGRITY_MACHINE_KEYRING \
--enable NETFILTER_ADVANCED \
--enable NF_CONNTRACK_MARK
#
# Finnish translation of systemd.
# Jan Kuparinen <copper_fin@hotmail.com>, 2021, 2022.
+# Ricky Tigg <ricky.tigg@gmail.com>, 2022.
msgid ""
msgstr ""
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2022-10-20 10:35+0200\n"
-"PO-Revision-Date: 2022-10-22 17:19+0000\n"
-"Last-Translator: Jan Kuparinen <copper_fin@hotmail.com>\n"
+"PO-Revision-Date: 2022-11-10 19:19+0000\n"
+"Last-Translator: Ricky Tigg <ricky.tigg@gmail.com>\n"
"Language-Team: Finnish <https://translate.fedoraproject.org/projects/systemd/"
"master/fi/>\n"
"Language: fi\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=2; plural=n != 1;\n"
-"X-Generator: Weblate 4.14.1\n"
+"X-Generator: Weblate 4.14.2\n"
#: src/core/org.freedesktop.systemd1.policy.in:22
msgid "Send passphrase back to system"
#: src/hostname/org.freedesktop.hostname1.policy:20
msgid "Set hostname"
-msgstr "Määritä isäntänimi"
+msgstr "Määritä konenimi"
#: src/hostname/org.freedesktop.hostname1.policy:21
msgid "Authentication is required to set the local hostname."
if (r < 0)
return log_error_errno(r, "Failed to format calendar specification '%s': %m", p);
- table = table_new("name", "value");
+ table = table_new_vertical();
if (!table)
return log_oom();
- table_set_header(table, false);
-
assert_se(cell = table_get_cell(table, 0, 0));
r = table_set_ellipsize_percent(table, cell, 100);
if (r < 0)
return r;
- r = table_set_align_percent(table, cell, 100);
- if (r < 0)
- return r;
-
assert_se(cell = table_get_cell(table, 0, 1));
r = table_set_ellipsize_percent(table, cell, 100);
if (r < 0)
if (!streq(t, p)) {
r = table_add_many(table,
- TABLE_STRING, "Original form:",
+ TABLE_FIELD, "Original form",
TABLE_STRING, p);
if (r < 0)
return table_log_add_error(r);
}
r = table_add_many(table,
- TABLE_STRING, "Normalized form:",
+ TABLE_FIELD, "Normalized form",
TABLE_STRING, t);
if (r < 0)
return table_log_add_error(r);
if (r == -ENOENT) {
if (i == 0) {
r = table_add_many(table,
- TABLE_STRING, "Next elapse:",
+ TABLE_FIELD, "Next elapse",
TABLE_STRING, "never",
TABLE_SET_COLOR, ansi_highlight_yellow());
if (r < 0)
if (i == 0) {
r = table_add_many(table,
- TABLE_STRING, "Next elapse:",
+ TABLE_FIELD, "Next elapse",
TABLE_TIMESTAMP, next,
TABLE_SET_COLOR, ansi_highlight_blue());
if (r < 0)
else
k = 0;
- r = table_add_cell_stringf(table, NULL, "Iter. #%u:", i+1);
+ r = table_add_cell_stringf_full(table, NULL, TABLE_FIELD, "Iteration #%u", i+1);
if (r < 0)
return table_log_add_error(r);
if (!in_utc_timezone()) {
r = table_add_many(table,
- TABLE_STRING, "(in UTC):",
+ TABLE_FIELD, "(in UTC)",
TABLE_TIMESTAMP_UTC, next);
if (r < 0)
return table_log_add_error(r);
}
r = table_add_many(table,
- TABLE_STRING, "From now:",
+ TABLE_FIELD, "From now",
TABLE_TIMESTAMP_RELATIVE, next);
if (r < 0)
return table_log_add_error(r);
print_separator();
if (path_is_absolute(*arg)) {
- const char *dir;
-
NULSTR_FOREACH(dir, CONF_PATHS_NULSTR("")) {
t = path_startswith(*arg, dir);
if (t)
}
static void filesystem_set_remove(Set *s, const FilesystemSet *set) {
- const char *filesystem;
-
NULSTR_FOREACH(filesystem, set->value) {
if (filesystem[0] == '@')
continue;
}
static void dump_filesystem_set(const FilesystemSet *set) {
- const char *filesystem;
int r;
if (!set)
if (strv_isempty(strv_skip(argv, 1))) {
_cleanup_set_free_ Set *kernel = NULL, *known = NULL;
- const char *fs;
int k;
NULSTR_FOREACH(fs, filesystem_sets[FILESYSTEM_SET_KNOWN].value)
if (r < 0)
return log_error_errno(r, "Parsing \"%s\" as ELF object failed: %m", abspath);
- t = table_new("", "");
+ t = table_new_vertical();
if (!t)
return log_oom();
r = table_add_many(
t,
- TABLE_STRING, "path:",
+ TABLE_FIELD, "path",
TABLE_STRING, abspath);
if (r < 0)
return table_log_add_error(r);
* metadata is parsed recursively in core files, so there might be
* multiple modules. */
if (STR_IN_SET(module_name, "elfType", "elfArchitecture")) {
- _cleanup_free_ char *suffixed = NULL;
-
- suffixed = strjoin(module_name, ":");
- if (!suffixed)
- return log_oom();
-
r = table_add_many(
t,
- TABLE_STRING, suffixed,
+ TABLE_FIELD, module_name,
TABLE_STRING, json_variant_string(module_json));
if (r < 0)
return table_log_add_error(r);
if (!streq(abspath, module_name)) {
r = table_add_many(
t,
- TABLE_STRING, "module name:",
+ TABLE_FIELD, "module name",
TABLE_STRING, module_name);
if (r < 0)
return table_log_add_error(r);
JSON_VARIANT_OBJECT_FOREACH(field_name, field, module_json)
if (json_variant_is_string(field)) {
- _cleanup_free_ char *suffixed = NULL;
-
- suffixed = strjoin(field_name, ":");
- if (!suffixed)
- return log_oom();
-
r = table_add_many(
t,
- TABLE_STRING, suffixed,
+ TABLE_FIELD, field_name,
TABLE_STRING, json_variant_string(field));
if (r < 0)
return table_log_add_error(r);
}
}
if (json_flags & JSON_FORMAT_OFF) {
- (void) table_set_header(t, true);
-
r = table_print(t, NULL);
if (r < 0)
return table_log_print_error(r);
}
static bool syscall_names_in_filter(Set *s, bool allow_list, const SyscallFilterSet *f, const char **ret_offending_syscall) {
- const char *syscall;
-
NULSTR_FOREACH(syscall, f->value) {
if (syscall[0] == '@') {
const SyscallFilterSet *g;
}
static void syscall_set_remove(Set *s, const SyscallFilterSet *set) {
- const char *syscall;
-
if (!set)
return;
}
static void dump_syscall_filter(const SyscallFilterSet *set) {
- const char *syscall;
-
printf("%s%s%s\n"
" # %s\n",
ansi_highlight(),
if (strv_isempty(strv_skip(argv, 1))) {
_cleanup_set_free_ Set *kernel = NULL, *known = NULL;
- const char *sys;
int k = 0; /* explicit initialization to appease gcc */
NULSTR_FOREACH(sys, syscall_filter_sets[SYSCALL_FILTER_SET_KNOWN].value)
return r;
}
- table = table_new("name", "value");
+ table = table_new_vertical();
if (!table)
return log_oom();
- table_set_header(table, false);
-
assert_se(cell = table_get_cell(table, 0, 0));
r = table_set_ellipsize_percent(table, cell, 100);
if (r < 0)
return r;
- r = table_set_align_percent(table, cell, 100);
- if (r < 0)
- return r;
-
assert_se(cell = table_get_cell(table, 0, 1));
r = table_set_ellipsize_percent(table, cell, 100);
if (r < 0)
return r;
r = table_add_many(table,
- TABLE_STRING, "Original:",
+ TABLE_FIELD, "Original",
TABLE_STRING, *input_timespan);
if (r < 0)
return table_log_add_error(r);
- r = table_add_cell_stringf(table, NULL, "%ss:", special_glyph(SPECIAL_GLYPH_MU));
+ r = table_add_cell_stringf_full(table, NULL, TABLE_FIELD, "%ss", special_glyph(SPECIAL_GLYPH_MU));
if (r < 0)
return table_log_add_error(r);
r = table_add_many(table,
TABLE_UINT64, output_usecs,
- TABLE_STRING, "Human:",
+ TABLE_FIELD, "Human",
TABLE_TIMESPAN, output_usecs,
TABLE_SET_COLOR, ansi_highlight());
if (r < 0)
return r;
}
- table = table_new("name", "value");
+ table = table_new_vertical();
if (!table)
return log_oom();
- table_set_header(table, false);
-
assert_se(cell = table_get_cell(table, 0, 0));
r = table_set_ellipsize_percent(table, cell, 100);
if (r < 0)
return r;
- r = table_set_align_percent(table, cell, 100);
- if (r < 0)
- return r;
-
assert_se(cell = table_get_cell(table, 0, 1));
r = table_set_ellipsize_percent(table, cell, 100);
if (r < 0)
return r;
r = table_add_many(table,
- TABLE_STRING, "Original form:",
+ TABLE_FIELD, "Original form",
TABLE_STRING, p,
- TABLE_STRING, "Normalized form:",
+ TABLE_FIELD, "Normalized form",
TABLE_TIMESTAMP, usec,
TABLE_SET_COLOR, ansi_highlight_blue());
if (r < 0)
if (!in_utc_timezone()) {
r = table_add_many(table,
- TABLE_STRING, "(in UTC):",
+ TABLE_FIELD, "(in UTC)",
TABLE_TIMESTAMP_UTC, usec);
if (r < 0)
return table_log_add_error(r);
}
- r = table_add_cell(table, NULL, TABLE_STRING, "UNIX seconds:");
+ r = table_add_cell(table, NULL, TABLE_FIELD, "UNIX seconds");
if (r < 0)
return table_log_add_error(r);
return r;
r = table_add_many(table,
- TABLE_STRING, "From now:",
+ TABLE_FIELD, "From now",
TABLE_TIMESTAMP_RELATIVE, usec);
if (r < 0)
return table_log_add_error(r);
strna(n1), path);
}
+static int log_prohibited_symlink(int fd, ChaseSymlinksFlags flags) {
+ _cleanup_free_ char *n1 = NULL;
+
+ assert(fd >= 0);
+
+ if (!FLAGS_SET(flags, CHASE_WARN))
+ return -EREMCHG;
+
+ (void) fd_get_path(fd, &n1);
+
+ return log_warning_errno(SYNTHETIC_ERRNO(EREMCHG),
+ "Detected symlink where not symlink is allowed at %s, refusing.",
+ strna(n1));
+}
+
int chase_symlinks_at(
int dir_fd,
const char *path,
if (S_ISLNK(st.st_mode) && !((flags & CHASE_NOFOLLOW) && isempty(todo))) {
_cleanup_free_ char *destination = NULL;
+ if (flags & CHASE_PROHIBIT_SYMLINKS)
+ return log_prohibited_symlink(child, flags);
+
/* This is a symlink, in this case read the destination. But let's make sure we
* don't follow symlinks without bounds. */
if (--max_follow <= 0)
SYNTHETIC_ERRNO(ECHRNG),
"Specified path '%s' is outside of specified root directory '%s', refusing to resolve.",
absolute, root);
- }
- if (root) {
fd = open(root, O_CLOEXEC|O_DIRECTORY|O_PATH);
if (fd < 0)
return -errno;
* Note: this may do an NSS lookup, hence this flag cannot be used in PID 1. */
CHASE_AT_RESOLVE_IN_ROOT = 1 << 8, /* Same as openat2()'s RESOLVE_IN_ROOT flag, symlinks are resolved
* relative to the given directory fd instead of root. */
+ CHASE_PROHIBIT_SYMLINKS = 1 << 9, /* Refuse all symlinks */
} ChaseSymlinksFlags;
bool unsafe_transition(const struct stat *a, const struct stat *b);
#include "hashmap.h"
#include "log.h"
#include "macro.h"
+#include "nulstr-util.h"
#include "path-util.h"
#include "set.h"
#include "sort-util.h"
#include "log.h"
#include "macro.h"
#include "mkdir.h"
+#include "nulstr-util.h"
#include "parse-util.h"
#include "path-util.h"
#include "socket-util.h"
}
bool fs_in_group(const struct statfs *s, FilesystemGroups fs_group) {
- const char *fs;
int r;
NULSTR_FOREACH(fs, filesystem_sets[fs_group].value) {
int chmod_and_chown_at(int dir_fd, const char *path, mode_t mode, uid_t uid, gid_t gid) {
_cleanup_close_ int fd = -1;
+ assert(dir_fd >= 0 || dir_fd == AT_FDCWD);
+
if (path) {
/* Let's acquire an O_PATH fd, as precaution to change mode/owner on the same file */
fd = openat(dir_fd, path, O_PATH|O_CLOEXEC|O_NOFOLLOW);
}
bool is_locale_utf8(void) {
- const char *set;
static int cached_answer = -1;
+ const char *set;
+ int r;
/* Note that we default to 'true' here, since today UTF8 is
* pretty much supported everywhere. */
if (cached_answer >= 0)
goto out;
+ r = getenv_bool_secure("SYSTEMD_UTF8");
+ if (r >= 0) {
+ cached_answer = r;
+ goto out;
+ } else if (r != -ENXIO)
+ log_debug_errno(r, "Failed to parse $SYSTEMD_UTF8, ignoring: %m");
+
if (!setlocale(LC_ALL, "")) {
cached_answer = true;
goto out;
#include "nulstr-util.h"
#include "string-util.h"
+#include "strv.h"
-const char* nulstr_get(const char *nulstr, const char *needle) {
- const char *i;
+char** strv_parse_nulstr(const char *s, size_t l) {
+ /* l is the length of the input data, which will be split at NULs into elements of the resulting
+ * strv. Hence, the number of items in the resulting strv will be equal to one plus the number of NUL
+ * bytes in the l bytes starting at s, unless s[l-1] is NUL, in which case the final empty string is
+ * not stored in the resulting strv, and length is equal to the number of NUL bytes.
+ *
+ * Note that contrary to a normal nulstr which cannot contain empty strings, because the input data
+ * is terminated by any two consequent NUL bytes, this parser accepts empty strings in s. */
+
+ _cleanup_strv_free_ char **v = NULL;
+ size_t c = 0, i = 0;
+
+ assert(s || l <= 0);
+
+ if (l <= 0)
+ return new0(char*, 1);
+
+ for (const char *p = s; p < s + l; p++)
+ if (*p == 0)
+ c++;
+
+ if (s[l-1] != 0)
+ c++;
+
+ v = new0(char*, c+1);
+ if (!v)
+ return NULL;
+
+ for (const char *p = s; p < s + l; ) {
+ const char *e;
+
+ e = memchr(p, 0, s + l - p);
+
+ v[i] = memdup_suffix0(p, e ? e - p : s + l - p);
+ if (!v[i])
+ return NULL;
+
+ i++;
+
+ if (!e)
+ break;
+
+ p = e + 1;
+ }
+
+ assert(i == c);
+
+ return TAKE_PTR(v);
+}
+
+char** strv_split_nulstr(const char *s) {
+ _cleanup_strv_free_ char **l = NULL;
+ /* This parses a nulstr, without specification of size, and stops at an empty string. This cannot
+ * parse nulstrs with embedded empty strings hence, as an empty string is an end marker. Use
+ * strv_parse_nulstr() above to parse a nulstr with embedded empty strings (which however requires a
+ * size to be specified) */
+
+ NULSTR_FOREACH(i, s)
+ if (strv_extend(&l, i) < 0)
+ return NULL;
+
+ return l ? TAKE_PTR(l) : strv_new(NULL);
+}
+
+int strv_make_nulstr(char * const *l, char **ret, size_t *ret_size) {
+ /* 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
+ * size parameter. In the former case we can include empty strings, in the latter case we cannot (as
+ * that is the end marker).
+ *
+ * When NULSTR_FOREACH()/strv_split_nulstr() is used it is often assumed that the nulstr ends in two
+ * 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);
+ assert(ret_size);
+
+ STRV_FOREACH(i, l) {
+ size_t z;
+
+ z = strlen(*i);
+
+ if (!GREEDY_REALLOC(m, n + z + 2))
+ return -ENOMEM;
+
+ memcpy(m + n, *i, z + 1);
+ n += z + 1;
+ }
+
+ if (!m) {
+ /* return a buffer with an extra NUL, so that the assumption that we always have two trailing NULs holds */
+ m = new0(char, 2);
+ if (!m)
+ return -ENOMEM;
+
+ n = 0;
+ } else
+ /* Make sure there is a second extra NUL at the end of resulting nulstr (not counted in return size) */
+ m[n] = '\0';
+
+ *ret = TAKE_PTR(m);
+ *ret_size = n;
+
+ return 0;
+}
+
+const char* nulstr_get(const char *nulstr, const char *needle) {
if (!nulstr)
return NULL;
/* SPDX-License-Identifier: LGPL-2.1-or-later */
#pragma once
+#include <errno.h>
+#include <macro.h>
#include <stdbool.h>
#include <string.h>
#define NULSTR_FOREACH(i, l) \
- for ((i) = (l); (i) && *(i); (i) = strchr((i), 0)+1)
+ for (typeof(*(l)) *(i) = (l); (i) && *(i); (i) = strchr((i), 0)+1)
#define NULSTR_FOREACH_PAIR(i, j, l) \
- for ((i) = (l), (j) = strchr((i), 0)+1; (i) && *(i); (i) = strchr((j), 0)+1, (j) = *(i) ? strchr((i), 0)+1 : (i))
+ 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);
}
+
+char** strv_parse_nulstr(const char *s, size_t l);
+char** strv_split_nulstr(const char *s);
+int strv_make_nulstr(char * const *l, char **p, size_t *n);
+
+static inline int strv_from_nulstr(char ***ret, const char *nulstr) {
+ char **t;
+
+ assert(ret);
+
+ t = strv_split_nulstr(nulstr);
+ if (!t)
+ return -ENOMEM;
+
+ *ret = t;
+ return 0;
+}
}
int find_portable_profile(const char *name, const char *unit, char **ret_path) {
- const char *p, *dot;
+ const char *dot;
assert(name);
assert(ret_path);
return path_make_relative(from, to, ret);
}
-int path_make_relative_cwd(const char *p, char **ret) {
- char *c;
- int r;
-
- assert(p);
- assert(ret);
-
- if (path_is_absolute(p)) {
- _cleanup_free_ char *cwd = NULL;
-
- r = safe_getcwd(&cwd);
- if (r < 0)
- return r;
-
- r = path_make_relative(cwd, p, &c);
- if (r < 0)
- return r;
- } else {
- c = strdup(p);
- if (!c)
- return -ENOMEM;
- }
-
- *ret = TAKE_PTR(c);
- return 0;
-}
-
char* path_startswith_strv(const char *p, char **set) {
STRV_FOREACH(s, set) {
char *t;
int path_make_absolute_cwd(const char *p, char **ret);
int path_make_relative(const char *from, const char *to, char **ret);
int path_make_relative_parent(const char *from_child, const char *to, char **ret);
-int path_make_relative_cwd(const char *from, char **ret);
char *path_startswith_full(const char *path, const char *prefix, bool accept_dot_dot) _pure_;
static inline char* path_startswith(const char *path, const char *prefix) {
return path_startswith_full(path, prefix, true);
#include "missing_sched.h"
#include "missing_syscall.h"
#include "namespace-util.h"
+#include "nulstr-util.h"
#include "parse-util.h"
#include "path-util.h"
#include "process-util.h"
#include "fileio.h"
#include "filesystems.h"
#include "fs-util.h"
+#include "hash-funcs.h"
#include "macro.h"
#include "missing_fs.h"
#include "missing_magic.h"
return 0;
}
+
+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);
+}
+
+int inode_compare_func(const struct stat *a, const struct stat *b) {
+ int r;
+
+ r = CMP(a->st_dev, b->st_dev);
+ if (r != 0)
+ return r;
+
+ return CMP(a->st_ino, b->st_ino);
+}
+
+DEFINE_HASH_OPS_WITH_KEY_DESTRUCTOR(inode_hash_ops, struct stat, inode_hash_func, inode_compare_func, free);
#include "macro.h"
#include "missing_stat.h"
+#include "siphash24.h"
int is_symlink(const char *path);
int is_dir_full(int atfd, const char *fname, bool follow);
struct new_statx nsx; \
} var
#endif
+
+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;
return l;
}
-char** strv_parse_nulstr(const char *s, size_t l) {
- /* l is the length of the input data, which will be split at NULs into
- * elements of the resulting strv. Hence, the number of items in the resulting strv
- * will be equal to one plus the number of NUL bytes in the l bytes starting at s,
- * unless s[l-1] is NUL, in which case the final empty string is not stored in
- * the resulting strv, and length is equal to the number of NUL bytes.
- *
- * Note that contrary to a normal nulstr which cannot contain empty strings, because
- * the input data is terminated by any two consequent NUL bytes, this parser accepts
- * empty strings in s.
- */
-
- size_t c = 0, i = 0;
- char **v;
-
- assert(s || l <= 0);
-
- if (l <= 0)
- return new0(char*, 1);
-
- for (const char *p = s; p < s + l; p++)
- if (*p == 0)
- c++;
-
- if (s[l-1] != 0)
- c++;
-
- v = new0(char*, c+1);
- if (!v)
- return NULL;
-
- for (const char *p = s; p < s + l; ) {
- const char *e;
-
- e = memchr(p, 0, s + l - p);
-
- v[i] = strndup(p, e ? e - p : s + l - p);
- if (!v[i]) {
- strv_free(v);
- return NULL;
- }
-
- i++;
-
- if (!e)
- break;
-
- p = e + 1;
- }
-
- assert(i == c);
-
- return v;
-}
-
-char** strv_split_nulstr(const char *s) {
- const char *i;
- char **r = NULL;
-
- NULSTR_FOREACH(i, s)
- if (strv_extend(&r, i) < 0) {
- strv_free(r);
- return NULL;
- }
-
- if (!r)
- return strv_new(NULL);
-
- return r;
-}
-
-int strv_make_nulstr(char * const *l, char **ret, size_t *ret_size) {
- /* A valid nulstr with two NULs at the end will be created, but
- * q will be the length without the two trailing NULs. Thus the output
- * string is a valid nulstr and can be iterated over using NULSTR_FOREACH,
- * and can also be parsed by strv_parse_nulstr as long as the length
- * is provided separately.
- */
-
- _cleanup_free_ char *m = NULL;
- size_t n = 0;
-
- assert(ret);
- assert(ret_size);
-
- STRV_FOREACH(i, l) {
- size_t z;
-
- z = strlen(*i);
-
- if (!GREEDY_REALLOC(m, n + z + 2))
- return -ENOMEM;
-
- memcpy(m + n, *i, z + 1);
- n += z + 1;
- }
-
- if (!m) {
- m = new0(char, 1);
- if (!m)
- return -ENOMEM;
- n = 1;
- } else
- /* make sure there is a second extra NUL at the end of resulting nulstr */
- m[n] = '\0';
-
- assert(n > 0);
- *ret = m;
- *ret_size = n - 1;
-
- m = NULL;
-
- return 0;
-}
-
bool strv_overlap(char * const *a, char * const *b) {
STRV_FOREACH(i, a)
if (strv_contains(b, *i))
return strv_join_full(l, separator, NULL, false);
}
-char** strv_parse_nulstr(const char *s, size_t l);
-char** strv_split_nulstr(const char *s);
-int strv_make_nulstr(char * const *l, char **p, size_t *n);
-
-static inline int strv_from_nulstr(char ***a, const char *nulstr) {
- char **t;
-
- t = strv_split_nulstr(nulstr);
- if (!t)
- return -ENOMEM;
- *a = t;
- return 0;
-}
-
bool strv_overlap(char * const *a, char * const *b) _pure_;
#define _STRV_FOREACH_BACKWARDS(s, l, h, i) \
_cleanup_close_ int fd = -1;
int r;
+ assert(dir_fd >= 0 || dir_fd == AT_FDCWD);
+
if (path) {
r = tempfn_random(path, NULL, &t);
if (r < 0)
assert(previous);
assert(is_first);
- r = chase_symlinks_and_opendir(path, esp_path, CHASE_PREFIX_ROOT, &p, &d);
+ r = chase_symlinks_and_opendir(path, esp_path, CHASE_PREFIX_ROOT|CHASE_PROHIBIT_SYMLINKS, &p, &d);
if (r == -ENOENT)
return 0;
if (r < 0)
r = efi_get_boot_option(id, &title, &partition, &path, &active);
if (r < 0)
- return log_error_errno(r, "Failed to read boot option %u: %m", id);
+ return log_debug_errno(r, "Failed to read boot option 0x%04X: %m", id);
/* print only configured entries with partition information */
if (!path || sd_id128_is_null(partition)) {
- log_debug("Ignoring boot entry %u without partition information.", id);
+ log_debug("Ignoring boot entry 0x%04X without partition information.", id);
return 0;
}
if (!p)
return log_oom();
- r = chase_symlinks(p, root, CHASE_PREFIX_ROOT, &source_path, NULL);
+ r = chase_symlinks(p, root, CHASE_PREFIX_ROOT|CHASE_PROHIBIT_SYMLINKS, &source_path, NULL);
/* If we had a root directory to try, we didn't find it and we are in auto mode, retry on the host */
if (r == -ENOENT && root && arg_install_source == ARG_INSTALL_SOURCE_AUTO)
- r = chase_symlinks(p, NULL, CHASE_PREFIX_ROOT, &source_path, NULL);
+ r = chase_symlinks(p, NULL, CHASE_PREFIX_ROOT|CHASE_PROHIBIT_SYMLINKS, &source_path, NULL);
if (r < 0)
return log_error_errno(r,
"Failed to resolve path %s%s%s: %m",
if (!q)
return log_oom();
- r = chase_symlinks(q, esp_path, CHASE_PREFIX_ROOT | CHASE_NONEXISTENT, &dest_path, NULL);
+ r = chase_symlinks(q, esp_path, CHASE_PREFIX_ROOT|CHASE_PROHIBIT_SYMLINKS|CHASE_NONEXISTENT, &dest_path, NULL);
if (r < 0)
return log_error_errno(r, "Failed to resolve path %s under directory %s: %m", q, esp_path);
v = strjoina("/EFI/BOOT/BOOT", e);
ascii_strupper(strrchr(v, '/') + 1);
- r = chase_symlinks(v, esp_path, CHASE_PREFIX_ROOT | CHASE_NONEXISTENT, &default_dest_path, NULL);
+ r = chase_symlinks(v, esp_path, CHASE_PREFIX_ROOT|CHASE_PROHIBIT_SYMLINKS|CHASE_NONEXISTENT, &default_dest_path, NULL);
if (r < 0)
return log_error_errno(r, "Failed to resolve path %s under directory %s: %m", v, esp_path);
_cleanup_free_ char *path = NULL;
int r;
- r = chase_symlinks_and_opendir(BOOTLIBDIR, root, CHASE_PREFIX_ROOT, &path, &d);
+ r = chase_symlinks_and_opendir(BOOTLIBDIR, root, CHASE_PREFIX_ROOT|CHASE_PROHIBIT_SYMLINKS, &path, &d);
/* If we had a root directory to try, we didn't find it and we are in auto mode, retry on the host */
if (r == -ENOENT && root && arg_install_source == ARG_INSTALL_SOURCE_AUTO)
- r = chase_symlinks_and_opendir(BOOTLIBDIR, NULL, CHASE_PREFIX_ROOT, &path, &d);
+ r = chase_symlinks_and_opendir(BOOTLIBDIR, NULL, CHASE_PREFIX_ROOT|CHASE_PROHIBIT_SYMLINKS, &path, &d);
if (r < 0)
return log_error_errno(r, "Failed to open boot loader directory %s%s: %m", strempty(root), BOOTLIBDIR);
return 0;
}
- r = chase_symlinks_and_access(path, esp_path, CHASE_PREFIX_ROOT, F_OK, NULL, NULL);
+ r = chase_symlinks_and_access(path, esp_path, CHASE_PREFIX_ROOT|CHASE_PROHIBIT_SYMLINKS, F_OK, NULL, NULL);
if (r == -ENOENT)
return 0;
if (r < 0)
_cleanup_free_ char *p = NULL;
int r, c = 0;
- r = chase_symlinks_and_opendir("/EFI/BOOT", esp_path, CHASE_PREFIX_ROOT, &p, &d);
+ r = chase_symlinks_and_opendir("/EFI/BOOT", esp_path, CHASE_PREFIX_ROOT|CHASE_PROHIBIT_SYMLINKS, &p, &d);
if (r == -ENOENT)
return 0;
if (r < 0)
err = device_path_to_str(loaded_image->FilePath, &loaded_image_path);
if (err != EFI_SUCCESS)
- return log_error_status_stall(err, L"Error getting loaded image path: %m");
+ return log_error_status_stall(err, L"Error getting loaded image path: %r", err);
export_variables(loaded_image, loaded_image_path, init_usec);
}
EFI_STATUS reconnect_all_drivers(void) {
- _cleanup_free_ EFI_HANDLE *handles = NULL;
- UINTN n_handles = 0;
- EFI_STATUS err;
+ _cleanup_free_ EFI_HANDLE *handles = NULL;
+ size_t n_handles = 0;
+ EFI_STATUS err;
- /* Reconnects all handles, so that any loaded drivers can take effect. */
+ /* Reconnects all handles, so that any loaded drivers can take effect. */
- err = BS->LocateHandleBuffer(AllHandles, NULL, NULL, &n_handles, &handles);
- if (err != EFI_SUCCESS)
- return log_error_status_stall(err, L"Failed to get list of handles: %r", err);
+ err = BS->LocateHandleBuffer(AllHandles, NULL, NULL, &n_handles, &handles);
+ if (err != EFI_SUCCESS)
+ return log_error_status_stall(err, L"Failed to get list of handles: %r", err);
- for (UINTN i = 0; i < n_handles; i++) {
- err = BS->ConnectController(handles[i], NULL, NULL, true);
- if (err == EFI_NOT_FOUND) /* No drivers for this handle */
- continue;
- if (err != EFI_SUCCESS)
- log_error_status_stall(err, L"Failed to reconnect handle %" PRIuN L", ignoring: %r", i, err);
- }
+ for (size_t i = 0; i < n_handles; i++)
+ /* Some firmware gives us some bogus handles (or they might become bad due to
+ * reconnecting everything). Security policy may also prevent us from doing so too.
+ * There is nothing we can realistically do on errors anyways, so just ignore them. */
+ (void) BS->ConnectController(handles[i], NULL, NULL, true);
- return EFI_SUCCESS;
+ return EFI_SUCCESS;
}
EFI_STATUS load_drivers(
"/run/systemd/inaccessible/blk\0" "rwm\0";
int r = 0, k;
- const char *node, *acc;
NULSTR_FOREACH_PAIR(node, acc, auto_devices) {
k = bpf_devices_allow_list_device(prog, path, node, acc);
if (r >= 0 && k < 0)
if (name[0] == '@') {
const FilesystemSet *set;
- const char *i;
set = filesystem_set_find(name);
if (!set) {
};
const char *prev = NULL;
- const char *i;
assert(f);
"/dev/tty\0";
char temporary_mount[] = "/tmp/namespace-dev-XXXXXX";
- const char *d, *dev = NULL, *devpts = NULL, *devshm = NULL, *devhugepages = NULL, *devmqueue = NULL, *devlog = NULL, *devptmx = NULL;
+ const char *dev = NULL, *devpts = NULL, *devshm = NULL, *devhugepages = NULL, *devmqueue = NULL, *devlog = NULL, *devptmx = NULL;
bool can_mknod = true;
int r;
"ID_MODEL\0";
_cleanup_(sd_device_unrefp) sd_device *device = NULL;
- const char *i, *name;
+ const char *name;
struct stat st;
assert(path);
static int should_skip_path(const char *prefix, const char *suffix) {
#if HAVE_SPLIT_USR
_cleanup_free_ char *target = NULL, *dirname = NULL;
- const char *p;
dirname = path_join(prefix, suffix);
if (!dirname)
}
static int process_suffix(const char *suffix, const char *onlyprefix) {
- const char *p;
char *f, *key;
OrderedHashmap *top, *bottom, *drops, *h;
int r = 0, k, n_found = 0;
}
static int process_suffixes(const char *onlyprefix) {
- const char *n;
int n_found = 0, r;
NULSTR_FOREACH(n, suffixes) {
}
static int process_suffix_chop(const char *arg) {
- const char *p;
-
assert(arg);
if (!path_is_absolute(arg))
pager_open(arg_pager_flags);
if (arg_json_format_flags & JSON_FORMAT_OFF)
- printf(" Name: %s\n", bn);
+ printf(" Name: %s%s%s\n", ansi_highlight(), bn, ansi_normal());
if (ioctl(d->fd, BLKGETSIZE64, &size) < 0)
log_debug_errno(errno, "Failed to query size of loopback device: %m");
else if (arg_json_format_flags & JSON_FORMAT_OFF) {
_cleanup_strv_free_ char **sysext_scopes = NULL;
+ if (!sd_id128_is_null(m->image_uuid))
+ printf("Image UUID: %s\n", SD_ID128_TO_UUID_STRING(m->image_uuid));
+
if (m->hostname)
printf(" Hostname: %s\n", m->hostname);
r = json_build(&v, JSON_BUILD_OBJECT(
JSON_BUILD_PAIR("name", JSON_BUILD_STRING(bn)),
+ JSON_BUILD_PAIR_CONDITION(!sd_id128_is_null(m->image_uuid), "imageUuid", JSON_BUILD_UUID(m->image_uuid)),
JSON_BUILD_PAIR("size", JSON_BUILD_INTEGER(size)),
JSON_BUILD_PAIR_CONDITION(m->hostname, "hostname", JSON_BUILD_STRING(m->hostname)),
JSON_BUILD_PAIR_CONDITION(!sd_id128_is_null(m->machine_id), "machineId", JSON_BUILD_ID128(m->machine_id)),
if (errno != ENOENT)
return log_error_errno(errno, "Failed to open destination '%s': %m", arg_target);
- r = copy_tree_at(source_fd, ".", dfd, bn, UID_INVALID, GID_INVALID, COPY_REFLINK|COPY_REPLACE|COPY_SIGINT|COPY_HARDLINKS);
+ r = copy_tree_at(source_fd, ".", dfd, bn, UID_INVALID, GID_INVALID, COPY_REFLINK|COPY_REPLACE|COPY_SIGINT|COPY_HARDLINKS, NULL);
} else
- r = copy_tree_at(source_fd, ".", target_fd, ".", UID_INVALID, GID_INVALID, COPY_REFLINK|COPY_REPLACE|COPY_SIGINT|COPY_HARDLINKS);
+ r = copy_tree_at(source_fd, ".", target_fd, ".", UID_INVALID, GID_INVALID, COPY_REFLINK|COPY_REPLACE|COPY_SIGINT|COPY_HARDLINKS, NULL);
if (r < 0)
return log_error_errno(r, "Failed to copy '%s' to '%s' in image '%s': %m", arg_source, arg_target, arg_image);
size_t *ret_volume_key_size) {
_cleanup_free_ char *xattr_buf = NULL;
- const char *xa;
int r;
assert(setup);
_cleanup_free_ char *xattr_buf = NULL;
size_t volume_key_size = 0;
uint32_t slot = 0;
- const char *xa;
int r;
assert(h);
assert(root_fd >= 0);
- r = copy_tree_at(AT_FDCWD, skel, root_fd, ".", UID_INVALID, GID_INVALID, COPY_MERGE|COPY_REPLACE);
+ r = copy_tree_at(AT_FDCWD, skel, root_fd, ".", UID_INVALID, GID_INVALID, COPY_MERGE|COPY_REPLACE, NULL);
if (r == -ENOENT) {
log_info("Skeleton directory %s missing, ignoring.", skel);
return 0;
assert(i);
- table = table_new("key", "value");
+ table = table_new_vertical();
if (!table)
return log_oom();
assert_se(cell = table_get_cell(table, 0, 0));
(void) table_set_ellipsize_percent(table, cell, 100);
- (void) table_set_align_percent(table, cell, 100);
-
- table_set_header(table, false);
table_set_ersatz_string(table, TABLE_ERSATZ_UNSET);
r = table_add_many(table,
- TABLE_STRING, "Static hostname:",
+ TABLE_FIELD, "Static hostname",
TABLE_STRING, i->static_hostname);
if (r < 0)
return table_log_add_error(r);
if (!isempty(i->pretty_hostname) &&
!streq_ptr(i->pretty_hostname, i->static_hostname)) {
r = table_add_many(table,
- TABLE_STRING, "Pretty hostname:",
+ TABLE_FIELD, "Pretty hostname",
TABLE_STRING, i->pretty_hostname);
if (r < 0)
return table_log_add_error(r);
if (!isempty(i->hostname) &&
!streq_ptr(i->hostname, i->static_hostname)) {
r = table_add_many(table,
- TABLE_STRING, "Transient hostname:",
+ TABLE_FIELD, "Transient hostname",
TABLE_STRING, i->hostname);
if (r < 0)
return table_log_add_error(r);
if (!isempty(i->icon_name)) {
r = table_add_many(table,
- TABLE_STRING, "Icon name:",
+ TABLE_FIELD, "Icon name",
TABLE_STRING, i->icon_name);
if (r < 0)
return table_log_add_error(r);
v = strjoina(i->chassis, " ", v);
r = table_add_many(table,
- TABLE_STRING, "Chassis:",
+ TABLE_FIELD, "Chassis",
TABLE_STRING, v ?: i->chassis);
if (r < 0)
return table_log_add_error(r);
if (!isempty(i->deployment)) {
r = table_add_many(table,
- TABLE_STRING, "Deployment:",
+ TABLE_FIELD, "Deployment",
TABLE_STRING, i->deployment);
if (r < 0)
return table_log_add_error(r);
if (!isempty(i->location)) {
r = table_add_many(table,
- TABLE_STRING, "Location:",
+ TABLE_FIELD, "Location",
TABLE_STRING, i->location);
if (r < 0)
return table_log_add_error(r);
r = sd_id128_get_machine(&mid);
if (r >= 0) {
r = table_add_many(table,
- TABLE_STRING, "Machine ID:",
+ TABLE_FIELD, "Machine ID",
TABLE_ID128, mid);
if (r < 0)
return table_log_add_error(r);
r = sd_id128_get_boot(&bid);
if (r >= 0) {
r = table_add_many(table,
- TABLE_STRING, "Boot ID:",
+ TABLE_FIELD, "Boot ID",
TABLE_ID128, bid);
if (r < 0)
return table_log_add_error(r);
if (!isempty(i->virtualization)) {
r = table_add_many(table,
- TABLE_STRING, "Virtualization:",
+ TABLE_FIELD, "Virtualization",
TABLE_STRING, i->virtualization);
if (r < 0)
return table_log_add_error(r);
if (!isempty(i->os_pretty_name)) {
r = table_add_many(table,
- TABLE_STRING, "Operating System:",
+ TABLE_FIELD, "Operating System",
TABLE_STRING, i->os_pretty_name,
TABLE_SET_URL, i->home_url);
if (r < 0)
if (!isempty(i->os_cpe_name)) {
r = table_add_many(table,
- TABLE_STRING, "CPE OS Name:",
+ TABLE_FIELD, "CPE OS Name",
TABLE_STRING, i->os_cpe_name);
if (r < 0)
return table_log_add_error(r);
v = strjoina(i->kernel_name, " ", i->kernel_release);
r = table_add_many(table,
- TABLE_STRING, "Kernel:",
+ TABLE_FIELD, "Kernel",
TABLE_STRING, v);
if (r < 0)
return table_log_add_error(r);
if (!isempty(i->architecture)) {
r = table_add_many(table,
- TABLE_STRING, "Architecture:",
+ TABLE_FIELD, "Architecture",
TABLE_STRING, i->architecture);
if (r < 0)
return table_log_add_error(r);
if (!isempty(i->hardware_vendor)) {
r = table_add_many(table,
- TABLE_STRING, "Hardware Vendor:",
+ TABLE_FIELD, "Hardware Vendor",
TABLE_STRING, i->hardware_vendor);
if (r < 0)
return table_log_add_error(r);
if (!isempty(i->hardware_model)) {
r = table_add_many(table,
- TABLE_STRING, "Hardware Model:",
+ TABLE_FIELD, "Hardware Model",
TABLE_STRING, i->hardware_model);
if (r < 0)
return table_log_add_error(r);
if (!isempty(i->firmware_version)) {
r = table_add_many(table,
- TABLE_STRING, "Firmware Version:",
+ TABLE_FIELD, "Firmware Version",
TABLE_STRING, i->firmware_version);
if (r < 0)
return table_log_add_error(r);
BTRFS_SNAPSHOT_FALLBACK_DIRECTORY|
BTRFS_SNAPSHOT_RECURSIVE);
else
- r = copy_tree(i->final_path, t, UID_INVALID, GID_INVALID, COPY_REFLINK|COPY_HARDLINKS);
+ r = copy_tree(i->final_path, t, UID_INVALID, GID_INVALID, COPY_REFLINK|COPY_HARDLINKS, NULL);
if (r < 0)
return log_error_errno(r, "Failed to create local image: %m");
int mhd_respondf(struct MHD_Connection *connection,
int error,
- unsigned code,
+ enum MHD_RequestTerminationCode code,
const char *format, ...) _printf_(4,5);
int mhd_respond(struct MHD_Connection *connection,
- unsigned code,
+ enum MHD_RequestTerminationCode code,
const char *message);
int mhd_respond_oom(struct MHD_Connection *connection);
Set **units) {
_cleanup_set_free_free_ Set *found = NULL;
- const char *field;
int r;
found = set_new(&string_hash_ops);
#include "fileio.h"
#include "format-util.h"
#include "hexdecoct.h"
+#include "nulstr-util.h"
#include "parse-util.h"
#include "process-util.h"
#include "string-util.h"
s);
if (r < 0)
- return r;
+ goto finish;
/* Make the slot of the match call floating now. We need the reference, but we don't
* want that this match pins the bus object, hence we first create it non-floating, but
return -ENOMEM;
size_t i = 0;
- char *p;
NULSTR_FOREACH(p, buf_nulstr)
buf_strv[i++] = p;
assert(i == num);
_cleanup_(sd_device_unrefp) sd_device *device = NULL, *from_nulstr = NULL;
_cleanup_free_ char *nulstr_copy = NULL;
- const char *devlink, *nulstr;
+ const char *nulstr;
size_t len;
assert_se(sd_device_new_from_syspath(&device, "/sys/class/net/lo") >= 0);
if (!hwdb->f)
return log_debug_errno(errno, "Failed to open %s: %m", path);
} else {
- NULSTR_FOREACH(path, hwdb_bin_paths) {
- log_debug("Trying to open \"%s\"...", path);
- hwdb->f = fopen(path, "re");
- if (hwdb->f)
+ NULSTR_FOREACH(p, hwdb_bin_paths) {
+ log_debug("Trying to open \"%s\"...", p);
+ hwdb->f = fopen(p, "re");
+ if (hwdb->f) {
+ path = p;
break;
+ }
if (errno != ENOENT)
- return log_debug_errno(errno, "Failed to open %s: %m", path);
+ return log_debug_errno(errno, "Failed to open %s: %m", p);
}
if (!hwdb->f)
static const char search_paths[] =
"/run/log/journal\0"
"/var/log/journal\0";
- const char *p;
assert(j);
#include "fd-util.h"
#include "fileio.h"
#include "fs-util.h"
+#include "nulstr-util.h"
#include "path-lookup.h"
#include "path-util.h"
#include "string-util.h"
return log_error_errno(r, "Failed to build locale settings from kernel command line: %m");
}
- table = table_new("key", "value");
+ table = table_new_vertical();
if (!table)
return log_oom();
assert_se(cell = table_get_cell(table, 0, 0));
(void) table_set_ellipsize_percent(table, cell, 100);
- (void) table_set_align_percent(table, cell, 100);
-
- table_set_header(table, false);
table_set_ersatz_string(table, TABLE_ERSATZ_UNSET);
}
r = table_add_many(table,
- TABLE_STRING, "System Locale:",
+ TABLE_FIELD, "System Locale",
TABLE_STRV, i->locale,
- TABLE_STRING, "VC Keymap:",
+ TABLE_FIELD, "VC Keymap",
TABLE_STRING, i->vconsole_keymap);
if (r < 0)
return table_log_add_error(r);
if (!isempty(i->vconsole_keymap_toggle)) {
r = table_add_many(table,
- TABLE_STRING, "VC Toggle Keymap:",
+ TABLE_FIELD, "VC Toggle Keymap",
TABLE_STRING, i->vconsole_keymap_toggle);
if (r < 0)
return table_log_add_error(r);
}
r = table_add_many(table,
- TABLE_STRING, "X11 Layout:",
+ TABLE_FIELD, "X11 Layout",
TABLE_STRING, i->x11_layout);
if (r < 0)
return table_log_add_error(r);
if (!isempty(i->x11_model)) {
r = table_add_many(table,
- TABLE_STRING, "X11 Model:",
+ TABLE_FIELD, "X11 Model",
TABLE_STRING, i->x11_model);
if (r < 0)
return table_log_add_error(r);
if (!isempty(i->x11_variant)) {
r = table_add_many(table,
- TABLE_STRING, "X11 Variant:",
+ TABLE_FIELD, "X11 Variant",
TABLE_STRING, i->x11_variant);
if (r < 0)
return table_log_add_error(r);
if (!isempty(i->x11_options)) {
r = table_add_many(table,
- TABLE_STRING, "X11 Options:",
+ TABLE_FIELD, "X11 Options",
TABLE_STRING, i->x11_options);
if (r < 0)
return table_log_add_error(r);
}
int find_converted_keymap(const char *x11_layout, const char *x11_variant, char **new_keymap) {
- const char *dir;
_cleanup_free_ char *n = NULL;
if (x11_variant)
* 0 or to the actual UID shift depending on the direction we copy. If no UID shift is set we'll copy
* the UID/GIDs as they are. */
if (copy_from)
- r = copy_tree_at(containerfd, container_basename, hostfd, host_basename, uid_shift == 0 ? UID_INVALID : 0, uid_shift == 0 ? GID_INVALID : 0, copy_flags);
+ r = copy_tree_at(containerfd, container_basename, hostfd, host_basename, uid_shift == 0 ? UID_INVALID : 0, uid_shift == 0 ? GID_INVALID : 0, copy_flags, NULL);
else
- r = copy_tree_at(hostfd, host_basename, containerfd, container_basename, uid_shift == 0 ? UID_INVALID : uid_shift, uid_shift == 0 ? GID_INVALID : uid_shift, copy_flags);
+ r = copy_tree_at(hostfd, host_basename, containerfd, container_basename, uid_shift == 0 ? UID_INVALID : uid_shift, uid_shift == 0 ? GID_INVALID : uid_shift, copy_flags, NULL);
hostfd = safe_close(hostfd);
containerfd = safe_close(containerfd);
static int call_get_os_release(sd_bus *bus, const char *method, const char *name, const char *query, ...) {
_cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
_cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
- const char *k, *v, *iter, **query_res = NULL;
+ const char *k, *v, **query_res = NULL;
size_t count = 0, awaited_args = 0;
va_list ap;
int r;
"tty\0"
"net/tun\0";
- const char *d;
int r = 0;
assert(dest);
#include "mkfs-util.h"
#include "mount-util.h"
#include "mountpoint-util.h"
+#include "nulstr-util.h"
#include "openssl-util.h"
#include "parse-argument.h"
#include "parse-helpers.h"
return 0;
}
-static int do_copy_files(Partition *p, const char *root) {
+static int do_copy_files(Partition *p, const char *root, const Set *denylist) {
int r;
assert(p);
sfd, ".",
pfd, fn,
UID_INVALID, GID_INVALID,
- COPY_REFLINK|COPY_MERGE|COPY_REPLACE|COPY_SIGINT|COPY_HARDLINKS|COPY_ALL_XATTRS);
+ COPY_REFLINK|COPY_MERGE|COPY_REPLACE|COPY_SIGINT|COPY_HARDLINKS|COPY_ALL_XATTRS,
+ denylist);
} else
r = copy_tree_at(
sfd, ".",
tfd, ".",
UID_INVALID, GID_INVALID,
- COPY_REFLINK|COPY_MERGE|COPY_REPLACE|COPY_SIGINT|COPY_HARDLINKS|COPY_ALL_XATTRS);
+ COPY_REFLINK|COPY_MERGE|COPY_REPLACE|COPY_SIGINT|COPY_HARDLINKS|COPY_ALL_XATTRS,
+ denylist);
if (r < 0)
return log_error_errno(r, "Failed to copy '%s' to '%s%s': %m", *source, strempty(arg_root), *target);
} else {
return 0;
}
-static int partition_populate_directory(Partition *p, char **ret_root, char **ret_tmp_root) {
+static int partition_populate_directory(Partition *p, const Set *denylist, char **ret_root, char **ret_tmp_root) {
_cleanup_(rm_rf_physical_and_freep) char *root = NULL;
int r;
* allocate a temporary directory, it's stored in "ret_tmp_root" to indicate it should be removed.
* Otherwise, we return the directory to use in "root" to indicate it should not be removed. */
- if (strv_length(p->copy_files) == 2 && strv_length(p->make_directories) == 0 && streq(p->copy_files[1], "/")) {
+ if (strv_length(p->copy_files) == 2 && strv_length(p->make_directories) == 0 &&
+ streq(p->copy_files[1], "/") && set_isempty(denylist)) {
_cleanup_free_ char *s = NULL;
r = chase_symlinks(p->copy_files[0], arg_root, CHASE_PREFIX_ROOT, &s, NULL);
if (r < 0)
return log_error_errno(r, "Failed to create temporary directory: %m");
- r = do_copy_files(p, root);
+ r = do_copy_files(p, root, denylist);
if (r < 0)
return r;
return 0;
}
-static int partition_populate_filesystem(Partition *p, const char *node) {
+static int partition_populate_filesystem(Partition *p, const char *node, const Set *denylist) {
int r;
assert(p);
if (mount_nofollow_verbose(LOG_ERR, node, fs, p->format, MS_NOATIME|MS_NODEV|MS_NOEXEC|MS_NOSUID, NULL) < 0)
_exit(EXIT_FAILURE);
- if (do_copy_files(p, fs) < 0)
+ if (do_copy_files(p, fs, denylist) < 0)
_exit(EXIT_FAILURE);
if (do_make_directories(p, fs) < 0)
return 0;
}
+static int make_copy_files_denylist(Context *context, Set **ret) {
+ _cleanup_set_free_ Set *denylist = NULL;
+ int r;
+
+ assert(context);
+ assert(ret);
+
+ LIST_FOREACH(partitions, p, context->partitions) {
+ const char *sources = gpt_partition_type_mountpoint_nulstr(p->type_uuid);
+ if (!sources)
+ continue;
+
+ NULSTR_FOREACH(s, sources) {
+ _cleanup_free_ char *d = NULL;
+ struct stat st;
+
+ r = chase_symlinks_and_stat(s, arg_root, CHASE_PREFIX_ROOT, NULL, &st, NULL);
+ if (r == -ENOENT)
+ continue;
+ if (r < 0)
+ return log_error_errno(r, "Failed to stat source file '%s%s': %m",
+ strempty(arg_root), s);
+
+ if (set_contains(denylist, &st))
+ continue;
+
+ d = memdup(&st, sizeof(st));
+ if (!d)
+ return log_oom();
+ if (set_ensure_put(&denylist, &inode_hash_ops, d) < 0)
+ return log_oom();
+
+ TAKE_PTR(d);
+ }
+ }
+
+ *ret = TAKE_PTR(denylist);
+ return 0;
+}
+
static int context_mkfs(Context *context) {
+ _cleanup_set_free_ Set *denylist = NULL;
int fd = -1, r;
assert(context);
/* Make a file system */
+ r = make_copy_files_denylist(context, &denylist);
+ if (r < 0)
+ return r;
+
LIST_FOREACH(partitions, p, context->partitions) {
_cleanup_(sym_crypt_freep) struct crypt_device *cd = NULL;
_cleanup_(loop_device_unrefp) LoopDevice *d = NULL;
* using read-only filesystems such as squashfs, we can't populate after creating the
* filesystem because it's read-only, so instead we create a temporary root to use as the
* source tree when generating the read-only filesystem. */
- r = partition_populate_directory(p, &root, &tmp_root);
+ r = partition_populate_directory(p, denylist, &root, &tmp_root);
if (r < 0)
return r;
return log_error_errno(errno, "Failed to unlock LUKS device: %m");
/* Now, we can populate all the other filesystems that aren't read-only. */
- r = partition_populate_filesystem(p, fsdev);
+ r = partition_populate_filesystem(p, fsdev, denylist);
if (r < 0) {
encrypted_dev_fd = safe_close(encrypted_dev_fd);
(void) deactivate_luks(cd, encrypted);
#include "pretty-print.h"
#include "process-util.h"
#include "resolvconf-compat.h"
+#include "resolve-util.h"
#include "resolvectl.h"
#include "resolved-def.h"
#include "resolved-dns-packet.h"
if (r < 0)
return bus_log_parse_error(r);
- table = table_new("key", "value");
+ table = table_new_vertical();
if (!table)
return log_oom();
- table_set_header(table, false);
-
r = table_add_many(table,
TABLE_STRING, "Transactions",
TABLE_SET_COLOR, ansi_highlight(),
+ TABLE_SET_ALIGN_PERCENT, 0,
TABLE_EMPTY,
- TABLE_STRING, "Current Transactions:",
+ TABLE_FIELD, "Current Transactions",
TABLE_SET_ALIGN_PERCENT, 100,
TABLE_UINT64, n_current_transactions,
- TABLE_STRING, "Total Transactions:",
+ TABLE_SET_ALIGN_PERCENT, 100,
+ TABLE_FIELD, "Total Transactions",
TABLE_UINT64, n_total_transactions,
TABLE_EMPTY, TABLE_EMPTY,
TABLE_STRING, "Cache",
TABLE_SET_COLOR, ansi_highlight(),
TABLE_SET_ALIGN_PERCENT, 0,
TABLE_EMPTY,
- TABLE_STRING, "Current Cache Size:",
+ TABLE_FIELD, "Current Cache Size",
TABLE_SET_ALIGN_PERCENT, 100,
TABLE_UINT64, cache_size,
- TABLE_STRING, "Cache Hits:",
+ TABLE_FIELD, "Cache Hits",
TABLE_UINT64, n_cache_hit,
- TABLE_STRING, "Cache Misses:",
+ TABLE_FIELD, "Cache Misses",
TABLE_UINT64, n_cache_miss,
TABLE_EMPTY, TABLE_EMPTY,
TABLE_STRING, "DNSSEC Verdicts",
TABLE_SET_COLOR, ansi_highlight(),
TABLE_SET_ALIGN_PERCENT, 0,
TABLE_EMPTY,
- TABLE_STRING, "Secure:",
+ TABLE_FIELD, "Secure",
TABLE_SET_ALIGN_PERCENT, 100,
TABLE_UINT64, n_dnssec_secure,
- TABLE_STRING, "Insecure:",
+ TABLE_FIELD, "Insecure",
TABLE_UINT64, n_dnssec_insecure,
- TABLE_STRING, "Bogus:",
+ TABLE_FIELD, "Bogus",
TABLE_UINT64, n_dnssec_bogus,
- TABLE_STRING, "Indeterminate:",
+ TABLE_FIELD, "Indeterminate:",
TABLE_UINT64, n_dnssec_indeterminate);
if (r < 0)
- table_log_add_error(r);
+ return table_log_add_error(r);
r = table_print(table, NULL);
if (r < 0)
strv_free(p->ntas);
}
-static int dump_list(Table *table, const char *prefix, char * const *l) {
+static int dump_list(Table *table, const char *field, char * const *l) {
int r;
if (strv_isempty(l))
return 0;
r = table_add_many(table,
- TABLE_STRING, prefix,
+ TABLE_FIELD, field,
TABLE_STRV_WRAPPED, l);
if (r < 0)
return table_log_add_error(r);
printf("%sLink %i (%s)%s\n",
ansi_highlight(), ifindex, name, ansi_normal());
- table = table_new("key", "value");
+ table = table_new_vertical();
if (!table)
return log_oom();
- table_set_header(table, false);
-
r = table_add_many(table,
- TABLE_STRING, "Current Scopes:",
- TABLE_SET_ALIGN_PERCENT, 100);
+ TABLE_FIELD, "Current Scopes",
+ TABLE_SET_MINIMUM_WIDTH, 19);
if (r < 0)
return table_log_add_error(r);
return log_oom();
r = table_add_many(table,
- TABLE_STRING, "Protocols:",
+ TABLE_FIELD, "Protocols",
TABLE_STRV_WRAPPED, pstatus);
if (r < 0)
return table_log_add_error(r);
if (link_info.current_dns) {
r = table_add_many(table,
- TABLE_STRING, "Current DNS Server:",
+ TABLE_FIELD, "Current DNS Server",
TABLE_STRING, link_info.current_dns_ex ?: link_info.current_dns);
if (r < 0)
return table_log_add_error(r);
}
- r = dump_list(table, "DNS Servers:", link_info.dns_ex ?: link_info.dns);
+ r = dump_list(table, "DNS Servers", link_info.dns_ex ?: link_info.dns);
if (r < 0)
return r;
- r = dump_list(table, "DNS Domain:", link_info.domains);
+ r = dump_list(table, "DNS Domain", link_info.domains);
if (r < 0)
return r;
printf("%sGlobal%s\n", ansi_highlight(), ansi_normal());
- table = table_new("key", "value");
+ table = table_new_vertical();
if (!table)
return log_oom();
- table_set_header(table, false);
-
_cleanup_strv_free_ char **pstatus = global_protocol_status(&global_info);
if (!pstatus)
return log_oom();
r = table_add_many(table,
- TABLE_STRING, "Protocols:",
- TABLE_SET_ALIGN_PERCENT, 100,
+ TABLE_FIELD, "Protocols",
+ TABLE_SET_MINIMUM_WIDTH, 19,
TABLE_STRV_WRAPPED, pstatus);
if (r < 0)
return table_log_add_error(r);
if (global_info.resolv_conf_mode) {
r = table_add_many(table,
- TABLE_STRING, "resolv.conf mode:",
+ TABLE_FIELD, "resolv.conf mode",
TABLE_STRING, global_info.resolv_conf_mode);
if (r < 0)
return table_log_add_error(r);
if (global_info.current_dns) {
r = table_add_many(table,
- TABLE_STRING, "Current DNS Server:",
+ TABLE_FIELD, "Current DNS Server",
TABLE_STRING, global_info.current_dns_ex ?: global_info.current_dns);
if (r < 0)
return table_log_add_error(r);
static int verb_llmnr(int argc, char **argv, void *userdata) {
_cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
+ _cleanup_free_ char *global_llmnr_support_str = NULL;
+ ResolveSupport global_llmnr_support, llmnr_support;
sd_bus *bus = ASSERT_PTR(userdata);
int r;
if (argc < 3)
return status_ifindex(bus, arg_ifindex, NULL, STATUS_LLMNR, NULL);
+ llmnr_support = resolve_support_from_string(argv[2]);
+ if (llmnr_support < 0)
+ return log_error_errno(llmnr_support, "Invalid LLMNR setting: %s", argv[2]);
+
+ r = bus_get_property_string(bus, bus_resolve_mgr, "LLMNR", &error, &global_llmnr_support_str);
+ if (r < 0)
+ return log_error_errno(r, "Failed to get the global LLMNR support state: %s", bus_error_message(&error, r));
+
+ global_llmnr_support = resolve_support_from_string(global_llmnr_support_str);
+ if (global_llmnr_support < 0)
+ return log_error_errno(global_llmnr_support, "Received invalid global LLMNR setting: %s", global_llmnr_support_str);
+
+ if (global_llmnr_support < llmnr_support)
+ log_warning("Setting LLMNR support level \"%s\" for \"%s\", but the global support level is \"%s\".",
+ argv[2], arg_ifname, global_llmnr_support_str);
+
r = bus_call_method(bus, bus_resolve_mgr, "SetLinkLLMNR", &error, NULL, "is", arg_ifindex, argv[2]);
if (r < 0 && sd_bus_error_has_name(&error, BUS_ERROR_LINK_BUSY)) {
sd_bus_error_free(&error);
static int verb_mdns(int argc, char **argv, void *userdata) {
_cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
+ _cleanup_free_ char *global_mdns_support_str = NULL;
+ ResolveSupport global_mdns_support, mdns_support;
sd_bus *bus = ASSERT_PTR(userdata);
int r;
if (argc < 3)
return status_ifindex(bus, arg_ifindex, NULL, STATUS_MDNS, NULL);
+ mdns_support = resolve_support_from_string(argv[2]);
+ if (mdns_support < 0)
+ return log_error_errno(mdns_support, "Invalid mDNS setting: %s", argv[2]);
+
+ r = bus_get_property_string(bus, bus_resolve_mgr, "MulticastDNS", &error, &global_mdns_support_str);
+ if (r < 0)
+ return log_error_errno(r, "Failed to get the global mDNS support state: %s", bus_error_message(&error, r));
+
+ global_mdns_support = resolve_support_from_string(global_mdns_support_str);
+ if (global_mdns_support < 0)
+ return log_error_errno(global_mdns_support, "Received invalid global mDNS setting: %s", global_mdns_support_str);
+
+ if (global_mdns_support < mdns_support)
+ log_warning("Setting mDNS support level \"%s\" for \"%s\", but the global support level is \"%s\".",
+ argv[2], arg_ifname, global_mdns_support_str);
+
r = bus_call_method(bus, bus_resolve_mgr, "SetLinkMulticastDNS", &error, NULL, "is", arg_ifindex, argv[2]);
if (r < 0 && sd_bus_error_has_name(&error, BUS_ERROR_LINK_BUSY)) {
sd_bus_error_free(&error);
struct DnsQuestion {
unsigned n_ref;
size_t n_keys, n_allocated;
- DnsQuestionItem items[0];
+ DnsQuestionItem items[];
};
DnsQuestion *dns_question_new(size_t n);
#define DNS_SERVER_FEATURE_LEVEL_IS_DNSSEC(x) ((x) >= DNS_SERVER_FEATURE_LEVEL_DO)
#define DNS_SERVER_FEATURE_LEVEL_IS_UDP(x) IN_SET(x, DNS_SERVER_FEATURE_LEVEL_UDP, DNS_SERVER_FEATURE_LEVEL_EDNS0, DNS_SERVER_FEATURE_LEVEL_DO)
-const char* dns_server_feature_level_to_string(int i) _const_;
-int dns_server_feature_level_from_string(const char *s) _pure_;
+const char* dns_server_feature_level_to_string(DnsServerFeatureLevel i) _const_;
+DnsServerFeatureLevel dns_server_feature_level_from_string(const char *s) _pure_;
struct DnsServer {
Manager *manager;
/* Defined by RFC 8375. The most official choice. */
"home.arpa\0";
- const char *name;
int r;
assert(d);
static BUS_DEFINE_PROPERTY_GET(property_get_dnssec_supported, "b", Link, link_dnssec_supported);
static BUS_DEFINE_PROPERTY_GET2(property_get_dnssec_mode, "s", Link, link_get_dnssec_mode, dnssec_mode_to_string);
+static BUS_DEFINE_PROPERTY_GET2(property_get_llmnr_support, "s", Link, link_get_llmnr_support, resolve_support_to_string);
+static BUS_DEFINE_PROPERTY_GET2(property_get_mdns_support, "s", Link, link_get_mdns_support, resolve_support_to_string);
static int property_get_dns_over_tls_mode(
sd_bus *bus,
SD_BUS_PROPERTY("CurrentDNSServerEx", "(iayqs)", property_get_current_dns_server_ex, offsetof(Link, current_dns_server), 0),
SD_BUS_PROPERTY("Domains", "a(sb)", property_get_domains, 0, 0),
SD_BUS_PROPERTY("DefaultRoute", "b", property_get_default_route, 0, 0),
- SD_BUS_PROPERTY("LLMNR", "s", bus_property_get_resolve_support, offsetof(Link, llmnr_support), 0),
- SD_BUS_PROPERTY("MulticastDNS", "s", bus_property_get_resolve_support, offsetof(Link, mdns_support), 0),
+ SD_BUS_PROPERTY("LLMNR", "s", property_get_llmnr_support, 0, 0),
+ SD_BUS_PROPERTY("MulticastDNS", "s", property_get_mdns_support, 0, 0),
SD_BUS_PROPERTY("DNSOverTLS", "s", property_get_dns_over_tls_mode, 0, 0),
SD_BUS_PROPERTY("DNSSEC", "s", property_get_dnssec_mode, 0, 0),
SD_BUS_PROPERTY("DNSSECNegativeTrustAnchors", "as", property_get_ntas, 0, 0),
l->unicast_scope = dns_scope_free(l->unicast_scope);
if (link_relevant(l, AF_INET, true) &&
- l->llmnr_support != RESOLVE_SUPPORT_NO &&
- l->manager->llmnr_support != RESOLVE_SUPPORT_NO) {
+ link_get_llmnr_support(l) != RESOLVE_SUPPORT_NO) {
if (!l->llmnr_ipv4_scope) {
r = dns_scope_new(l->manager, &l->llmnr_ipv4_scope, l, DNS_PROTOCOL_LLMNR, AF_INET);
if (r < 0)
l->llmnr_ipv4_scope = dns_scope_free(l->llmnr_ipv4_scope);
if (link_relevant(l, AF_INET6, true) &&
- l->llmnr_support != RESOLVE_SUPPORT_NO &&
- l->manager->llmnr_support != RESOLVE_SUPPORT_NO &&
- socket_ipv6_is_supported()) {
+ link_get_llmnr_support(l) != RESOLVE_SUPPORT_NO) {
if (!l->llmnr_ipv6_scope) {
r = dns_scope_new(l->manager, &l->llmnr_ipv6_scope, l, DNS_PROTOCOL_LLMNR, AF_INET6);
if (r < 0)
l->llmnr_ipv6_scope = dns_scope_free(l->llmnr_ipv6_scope);
if (link_relevant(l, AF_INET, true) &&
- l->mdns_support != RESOLVE_SUPPORT_NO &&
- l->manager->mdns_support != RESOLVE_SUPPORT_NO) {
+ link_get_mdns_support(l) != RESOLVE_SUPPORT_NO) {
if (!l->mdns_ipv4_scope) {
r = dns_scope_new(l->manager, &l->mdns_ipv4_scope, l, DNS_PROTOCOL_MDNS, AF_INET);
if (r < 0)
l->mdns_ipv4_scope = dns_scope_free(l->mdns_ipv4_scope);
if (link_relevant(l, AF_INET6, true) &&
- l->mdns_support != RESOLVE_SUPPORT_NO &&
- l->manager->mdns_support != RESOLVE_SUPPORT_NO) {
+ link_get_mdns_support(l) != RESOLVE_SUPPORT_NO) {
if (!l->mdns_ipv6_scope) {
r = dns_scope_new(l->manager, &l->mdns_ipv6_scope, l, DNS_PROTOCOL_MDNS, AF_INET6);
if (r < 0)
link_address_add_rrs(a, force_remove);
if (!force_remove &&
- l->mdns_support == RESOLVE_SUPPORT_YES &&
- l->manager->mdns_support == RESOLVE_SUPPORT_YES) {
+ link_get_mdns_support(l) == RESOLVE_SUPPORT_YES) {
if (l->mdns_ipv4_scope) {
r = dns_scope_add_dnssd_services(l->mdns_ipv4_scope);
if (r < 0)
return r;
- if (l->llmnr_support != RESOLVE_SUPPORT_NO) {
+ if (link_get_llmnr_support(l) != RESOLVE_SUPPORT_NO) {
r = manager_llmnr_start(l->manager);
if (r < 0)
return r;
}
- if (l->mdns_support != RESOLVE_SUPPORT_NO) {
+ if (link_get_mdns_support(l) != RESOLVE_SUPPORT_NO) {
r = manager_mdns_start(l->manager);
if (r < 0)
return r;
return true;
}
+ResolveSupport link_get_llmnr_support(Link *link) {
+ assert(link);
+ assert(link->manager);
+
+ /* This provides the effective LLMNR support level for the link, instead of the 'internal' per-link setting. */
+
+ return MIN(link->llmnr_support, link->manager->llmnr_support);
+}
+
+ResolveSupport link_get_mdns_support(Link *link) {
+ assert(link);
+ assert(link->manager);
+
+ /* This provides the effective mDNS support level for the link, instead of the 'internal' per-link setting. */
+
+ return MIN(link->mdns_support, link->manager->mdns_support);
+}
+
int link_address_new(Link *l, LinkAddress **ret, int family, const union in_addr_union *in_addr) {
LinkAddress *a;
if (!force_remove &&
link_address_relevant(a, true) &&
a->link->llmnr_ipv4_scope &&
- a->link->llmnr_support == RESOLVE_SUPPORT_YES &&
- a->link->manager->llmnr_support == RESOLVE_SUPPORT_YES) {
+ link_get_llmnr_support(a->link) == RESOLVE_SUPPORT_YES) {
if (!a->link->manager->llmnr_host_ipv4_key) {
a->link->manager->llmnr_host_ipv4_key = dns_resource_key_new(DNS_CLASS_IN, DNS_TYPE_A, a->link->manager->llmnr_hostname);
if (!force_remove &&
link_address_relevant(a, true) &&
a->link->mdns_ipv4_scope &&
- a->link->mdns_support == RESOLVE_SUPPORT_YES &&
- a->link->manager->mdns_support == RESOLVE_SUPPORT_YES) {
+ link_get_mdns_support(a->link) == RESOLVE_SUPPORT_YES) {
if (!a->link->manager->mdns_host_ipv4_key) {
a->link->manager->mdns_host_ipv4_key = dns_resource_key_new(DNS_CLASS_IN, DNS_TYPE_A, a->link->manager->mdns_hostname);
if (!a->link->manager->mdns_host_ipv4_key) {
if (!force_remove &&
link_address_relevant(a, true) &&
a->link->llmnr_ipv6_scope &&
- a->link->llmnr_support == RESOLVE_SUPPORT_YES &&
- a->link->manager->llmnr_support == RESOLVE_SUPPORT_YES) {
+ link_get_llmnr_support(a->link) == RESOLVE_SUPPORT_YES) {
if (!a->link->manager->llmnr_host_ipv6_key) {
a->link->manager->llmnr_host_ipv6_key = dns_resource_key_new(DNS_CLASS_IN, DNS_TYPE_AAAA, a->link->manager->llmnr_hostname);
if (!force_remove &&
link_address_relevant(a, true) &&
a->link->mdns_ipv6_scope &&
- a->link->mdns_support == RESOLVE_SUPPORT_YES &&
- a->link->manager->mdns_support == RESOLVE_SUPPORT_YES) {
+ link_get_mdns_support(a->link) == RESOLVE_SUPPORT_YES) {
if (!a->link->manager->mdns_host_ipv6_key) {
a->link->manager->mdns_host_ipv6_key = dns_resource_key_new(DNS_CLASS_IN, DNS_TYPE_AAAA, a->link->manager->mdns_hostname);
DnsOverTlsMode link_get_dns_over_tls_mode(Link *l);
+ResolveSupport link_get_llmnr_support(Link *link);
+ResolveSupport link_get_mdns_support(Link *link);
+
int link_save_user(Link *l);
int link_load_user(Link *l);
void link_remove_user(Link *l);
#include "memory-util.h"
#include "missing_syscall.h"
#include "mkdir-label.h"
+#include "nulstr-util.h"
#include "process-util.h"
#include "random-util.h"
#include "signal-util.h"
if (r < 0)
return r;
+ /* chop off the final NUL byte. We do this because we want to use the separator NUL bytes only if we
+ * have multiple passwords. */
+ n = LESS_BY(n, (size_t) 1);
+
serial = add_key("user", keyname, p, n, KEY_SPEC_USER_KEYRING);
if (serial == -1)
return -errno;
continue;
if (table[i].target) {
- const char *target = NULL, *s;
+ const char *target = NULL;
/* check if one of the targets exists */
NULSTR_FOREACH(s, table[i].target) {
#include "pretty-print.h"
#include "recurse-dir.h"
#include "sort-util.h"
+#include "stat-util.h"
#include "string-table.h"
#include "strv.h"
#include "terminal-util.h"
assert(config);
assert(path);
- r = chase_symlinks_and_fopen_unlocked(path, root, CHASE_PREFIX_ROOT, "re", &full, &f);
+ r = chase_symlinks_and_fopen_unlocked(path, root, CHASE_PREFIX_ROOT|CHASE_PROHIBIT_SYMLINKS, "re", &full, &f);
if (r == -ENOENT)
return 0;
if (r < 0)
return -strverscmp_improved(a->id, b->id);
}
-static 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);
-}
-
-static int inode_compare_func(const struct stat *a, const struct stat *b) {
- int r;
-
- r = CMP(a->st_dev, b->st_dev);
- if (r != 0)
- return r;
-
- return CMP(a->st_ino, b->st_ino);
-}
-
-DEFINE_HASH_OPS_WITH_KEY_DESTRUCTOR(inode_hash_ops, struct stat, inode_hash_func, inode_compare_func, free);
-
static int config_check_inode_relevant_and_unseen(BootConfig *config, int fd, const char *fname) {
_cleanup_free_ char *d = NULL;
struct stat st;
assert(root);
assert(dir);
- dir_fd = chase_symlinks_and_open(dir, root, CHASE_PREFIX_ROOT, O_DIRECTORY|O_CLOEXEC, &full);
+ dir_fd = chase_symlinks_and_open(dir, root, CHASE_PREFIX_ROOT|CHASE_PROHIBIT_SYMLINKS, O_DIRECTORY|O_CLOEXEC, &full);
if (dir_fd == -ENOENT)
return 0;
if (dir_fd < 0)
assert(config);
assert(dir);
- r = chase_symlinks_and_opendir(dir, root, CHASE_PREFIX_ROOT, &full, &d);
+ r = chase_symlinks_and_opendir(dir, root, CHASE_PREFIX_ROOT|CHASE_PROHIBIT_SYMLINKS, &full, &d);
if (r == -ENOENT)
return 0;
if (r < 0)
assert(p);
assert(ret_status);
- int status = chase_symlinks_and_access(p, root, CHASE_PREFIX_ROOT, F_OK, NULL, NULL);
+ int status = chase_symlinks_and_access(p, root, CHASE_PREFIX_ROOT|CHASE_PROHIBIT_SYMLINKS, F_OK, NULL, NULL);
printf("%13s%s ", strempty(field), field ? ":" : " ");
if (status < 0) {
if (FLAGS_SET(flags, OUTPUT_CGROUP_XATTRS) && fd >= 0) {
_cleanup_free_ char *nl = NULL;
- char *xa;
r = flistxattr_malloc(fd, &nl);
if (r < 0)
#include "fs-util.h"
#include "glob-util.h"
#include "hostname-util.h"
-#include "initrd-util.h"
#include "ima-util.h"
+#include "initrd-util.h"
#include "limits-util.h"
#include "list.h"
#include "macro.h"
#include "mountpoint-util.h"
+#include "nulstr-util.h"
#include "os-util.h"
#include "parse-util.h"
#include "path-util.h"
if (sections && !nulstr_contains(sections, n)) {
bool ignore;
- const char *t;
ignore = (flags & CONFIG_PARSE_RELAXED) || startswith(n, "X-");
uid_t override_uid,
gid_t override_gid,
CopyFlags copy_flags,
+ const Set *denylist,
HardlinkContext *hardlink_context,
const char *display_path,
copy_progress_path_t progress_path,
uid_t override_uid,
gid_t override_gid,
CopyFlags copy_flags,
+ const Set *denylist,
HardlinkContext *hardlink_context,
const char *display_path,
copy_progress_path_t progress_path,
return r;
}
+ if (set_contains(denylist, &buf)) {
+ log_debug("%s/%s is in the denylist, skipping", from, de->d_name);
+ continue;
+ }
+
if (S_ISDIR(buf.st_mode)) {
/*
* Don't descend into directories on other file systems, if this is requested. We do a simple
}
}
- q = fd_copy_tree_generic(dirfd(d), de->d_name, &buf, fdt, de->d_name, original_device, depth_left-1, override_uid, override_gid, copy_flags, hardlink_context, child_display_path, progress_path, progress_bytes, userdata);
+ q = fd_copy_tree_generic(dirfd(d), de->d_name, &buf, fdt, de->d_name, original_device,
+ depth_left-1, override_uid, override_gid, copy_flags, denylist,
+ hardlink_context, child_display_path, progress_path, progress_bytes,
+ userdata);
if (q == -EINTR) /* Propagate SIGINT/SIGTERM up instantly */
return q;
uid_t override_uid,
gid_t override_gid,
CopyFlags copy_flags,
+ const Set *denylist,
HardlinkContext *hardlink_context,
const char *display_path,
copy_progress_path_t progress_path,
int r;
if (S_ISDIR(st->st_mode))
- return fd_copy_directory(df, from, st, dt, to, original_device, depth_left-1, override_uid, override_gid, copy_flags, hardlink_context, display_path, progress_path, progress_bytes, userdata);
+ return fd_copy_directory(df, from, st, dt, to, original_device, depth_left-1, override_uid,
+ override_gid, copy_flags, denylist, hardlink_context, display_path,
+ progress_path, progress_bytes, userdata);
r = fd_copy_leaf(df, from, st, dt, to, override_uid, override_gid, copy_flags, hardlink_context, display_path, progress_bytes, userdata);
/* We just tried to copy a leaf node of the tree. If it failed because the node already exists *and* the COPY_REPLACE flag has been provided, we should unlink the node and re-copy. */
uid_t override_uid,
gid_t override_gid,
CopyFlags copy_flags,
+ const Set *denylist,
copy_progress_path_t progress_path,
copy_progress_bytes_t progress_bytes,
void *userdata) {
if (fstatat(fdf, from, &st, AT_SYMLINK_NOFOLLOW) < 0)
return -errno;
- r = fd_copy_tree_generic(fdf, from, &st, fdt, to, st.st_dev, COPY_DEPTH_MAX, override_uid, override_gid, copy_flags, NULL, NULL, progress_path, progress_bytes, userdata);
+ r = fd_copy_tree_generic(fdf, from, &st, fdt, to, st.st_dev, COPY_DEPTH_MAX, override_uid,
+ override_gid, copy_flags, denylist, NULL, NULL, progress_path,
+ progress_bytes, userdata);
if (r < 0)
return r;
COPY_DEPTH_MAX,
UID_INVALID, GID_INVALID,
copy_flags,
- NULL, NULL,
+ NULL, NULL, NULL,
progress_path,
progress_bytes,
userdata);
COPY_DEPTH_MAX,
UID_INVALID, GID_INVALID,
copy_flags,
- NULL, NULL,
+ NULL, NULL, NULL,
progress_path,
progress_bytes,
userdata);
int copy_xattr(int fdf, int fdt, CopyFlags copy_flags) {
_cleanup_free_ char *names = NULL;
int ret = 0, r;
- const char *p;
r = flistxattr_malloc(fdf, &names);
if (r < 0)
#include <sys/stat.h>
#include <sys/types.h>
+#include "set.h"
+
typedef enum CopyFlags {
COPY_REFLINK = 1 << 0, /* Try to reflink */
COPY_MERGE = 1 << 1, /* Merge existing trees with our new one to copy */
return copy_file_atomic_full(from, to, mode, chattr_flags, chattr_mask, copy_flags, NULL, NULL);
}
-int copy_tree_at_full(int fdf, const char *from, int fdt, const char *to, uid_t override_uid, gid_t override_gid, CopyFlags copy_flags, copy_progress_path_t progress_path, copy_progress_bytes_t progress_bytes, void *userdata);
-static inline int copy_tree_at(int fdf, const char *from, int fdt, const char *to, uid_t override_uid, gid_t override_gid, CopyFlags copy_flags) {
- return copy_tree_at_full(fdf, from, fdt, to, override_uid, override_gid, copy_flags, NULL, NULL, NULL);
+int copy_tree_at_full(int fdf, const char *from, int fdt, const char *to, uid_t override_uid, gid_t override_gid, CopyFlags copy_flags, const Set *denylist, copy_progress_path_t progress_path, copy_progress_bytes_t progress_bytes, void *userdata);
+static inline int copy_tree_at(int fdf, const char *from, int fdt, const char *to, uid_t override_uid, gid_t override_gid, CopyFlags copy_flags, const Set *denylist) {
+ return copy_tree_at_full(fdf, from, fdt, to, override_uid, override_gid, copy_flags, denylist, NULL, NULL, NULL);
}
-static inline int copy_tree(const char *from, const char *to, uid_t override_uid, gid_t override_gid, CopyFlags copy_flags) {
- return copy_tree_at_full(AT_FDCWD, from, AT_FDCWD, to, override_uid, override_gid, copy_flags, NULL, NULL, NULL);
+static inline int copy_tree(const char *from, const char *to, uid_t override_uid, gid_t override_gid, CopyFlags copy_flags, const Set *denylist) {
+ return copy_tree_at_full(AT_FDCWD, from, AT_FDCWD, to, override_uid, override_gid, copy_flags, denylist, NULL, NULL, NULL);
}
int copy_directory_fd_full(int dirfd, const char *to, CopyFlags copy_flags, copy_progress_path_t progress_path, copy_progress_bytes_t progress_bytes, void *userdata);
"/proc/self/fd/1\0" "/dev/stdout\0"
"/proc/self/fd/2\0" "/dev/stderr\0";
- const char *j, *k;
int r;
NULSTR_FOREACH_PAIR(j, k, symlinks) {
const char *root,
Image **ret) {
- const char *path;
int r;
assert(class >= 0);
const char *root,
Hashmap *h) {
- const char *path;
int r;
assert(class >= 0);
const char *root,
const char *image) {
- const char *path;
-
assert(image);
NULSTR_FOREACH(path, image_search_path[class]) {
_cleanup_(blkid_free_probep) blkid_probe b = NULL;
_cleanup_free_ char *generic_node = NULL;
sd_id128_t generic_uuid = SD_ID128_NULL;
- const char *pttype = NULL;
+ const char *pttype = NULL, *sptuuid = NULL;
blkid_partlist pl;
int r, generic_nr = -1, n_partitions;
if ((flags & DISSECT_IMAGE_GPT_ONLY) == 0) {
/* Look for file system superblocks, unless we only shall look for GPT partition tables */
blkid_probe_enable_superblocks(b, 1);
- blkid_probe_set_superblocks_flags(b, BLKID_SUBLKS_TYPE|BLKID_SUBLKS_USAGE);
+ blkid_probe_set_superblocks_flags(b, BLKID_SUBLKS_TYPE|BLKID_SUBLKS_USAGE|BLKID_SUBLKS_UUID);
}
blkid_probe_enable_partitions(b, 1);
(void) blkid_probe_lookup_value(b, "USAGE", &usage, NULL);
if (STRPTR_IN_SET(usage, "filesystem", "crypto")) {
_cleanup_free_ char *t = NULL, *n = NULL, *o = NULL;
- const char *fstype = NULL, *options = NULL;
+ const char *fstype = NULL, *options = NULL, *suuid = NULL;
_cleanup_close_ int mount_node_fd = -1;
+ sd_id128_t uuid = SD_ID128_NULL;
if (FLAGS_SET(flags, DISSECT_IMAGE_OPEN_PARTITION_DEVICES)) {
mount_node_fd = open_partition(devname, /* is_partition = */ false, m->loop);
/* OK, we have found a file system, that's our root partition then. */
(void) blkid_probe_lookup_value(b, "TYPE", &fstype, NULL);
+ (void) blkid_probe_lookup_value(b, "UUID", &suuid, NULL);
if (fstype) {
t = strdup(fstype);
return -ENOMEM;
}
+ if (suuid) {
+ /* blkid will return FAT's serial number as UUID, hence it is quite possible
+ * that parsing this will fail. We'll ignore the ID, since it's just too
+ * short to be useful as tru identifier. */
+ r = sd_id128_from_string(suuid, &uuid);
+ if (r < 0)
+ log_debug_errno(r, "Failed to parse file system UUID '%s', ignoring: %m", suuid);
+ }
+
n = strdup(devname);
if (!n)
return -ENOMEM;
m->verity_sig_ready = m->verity_ready &&
verity->root_hash_sig;
+ m->image_uuid = uuid;
+
options = mount_options_from_designator(mount_options, PARTITION_ROOT);
if (options) {
o = strdup(options);
return -EPROTONOSUPPORT;
}
+ (void) blkid_probe_lookup_value(b, "PTUUID", &sptuuid, NULL);
+ if (sptuuid) {
+ r = sd_id128_from_string(sptuuid, &m->image_uuid);
+ if (r < 0)
+ log_debug_errno(r, "Failed to parse partition table UUID '%s', ignoring: %m", sptuuid);
+ }
+
errno = 0;
pl = blkid_probe_get_partitions(b);
if (!pl)
continue;
assert_se((architecture = gpt_partition_type_uuid_to_arch(type_id)) >= 0);
- designator = PARTITION_ROOT_OF_ARCH(architecture);
+ designator = partition_root_of_arch(architecture);
rw = !(pflags & SD_GPT_FLAG_READ_ONLY);
growfs = FLAGS_SET(pflags, SD_GPT_FLAG_GROWFS);
continue;
assert_se((architecture = gpt_partition_type_uuid_to_arch(type_id)) >= 0);
- designator = PARTITION_VERITY_OF(PARTITION_ROOT_OF_ARCH(architecture));
+ designator = partition_verity_of(partition_root_of_arch(architecture));
fstype = "DM_verity_hash";
rw = false;
continue;
assert_se((architecture = gpt_partition_type_uuid_to_arch(type_id)) >= 0);
- designator = PARTITION_VERITY_SIG_OF(PARTITION_ROOT_OF_ARCH(architecture));
+ designator = partition_verity_sig_of(partition_root_of_arch(architecture));
fstype = "verity_hash_signature";
rw = false;
continue;
assert_se((architecture = gpt_partition_type_uuid_to_arch(type_id)) >= 0);
- designator = PARTITION_USR_OF_ARCH(architecture);
+ designator = partition_usr_of_arch(architecture);
rw = !(pflags & SD_GPT_FLAG_READ_ONLY);
growfs = FLAGS_SET(pflags, SD_GPT_FLAG_GROWFS);
continue;
assert_se((architecture = gpt_partition_type_uuid_to_arch(type_id)) >= 0);
- designator = PARTITION_VERITY_OF(PARTITION_USR_OF_ARCH(architecture));
+ designator = partition_verity_of(partition_usr_of_arch(architecture));
fstype = "DM_verity_hash";
rw = false;
continue;
assert_se((architecture = gpt_partition_type_uuid_to_arch(type_id)) >= 0);
- designator = PARTITION_VERITY_SIG_OF(PARTITION_USR_OF_ARCH(architecture));
+ designator = partition_verity_sig_of(partition_usr_of_arch(architecture));
fstype = "verity_hash_signature";
rw = false;
* let the newest version win. This permits a simple A/B versioning
* scheme in OS images. */
- if (!PARTITION_DESIGNATOR_VERSIONED(designator) ||
+ if (!partition_designator_is_versioned(designator) ||
strverscmp_improved(m->partitions[designator].label, label) >= 0)
continue;
log_debug("No root partition found of the native architecture, falling back to a root "
"partition of the secondary architecture.");
- m->partitions[PARTITION_ROOT] = m->partitions[PARTITION_ROOT_SECONDARY];
- zero(m->partitions[PARTITION_ROOT_SECONDARY]);
- m->partitions[PARTITION_ROOT_VERITY] = m->partitions[PARTITION_ROOT_SECONDARY_VERITY];
- zero(m->partitions[PARTITION_ROOT_SECONDARY_VERITY]);
- m->partitions[PARTITION_ROOT_VERITY_SIG] = m->partitions[PARTITION_ROOT_SECONDARY_VERITY_SIG];
- zero(m->partitions[PARTITION_ROOT_SECONDARY_VERITY_SIG]);
+ m->partitions[PARTITION_ROOT] = TAKE_PARTITION(m->partitions[PARTITION_ROOT_SECONDARY]);
+ m->partitions[PARTITION_ROOT_VERITY] = TAKE_PARTITION(m->partitions[PARTITION_ROOT_SECONDARY_VERITY]);
+ m->partitions[PARTITION_ROOT_VERITY_SIG] = TAKE_PARTITION(m->partitions[PARTITION_ROOT_SECONDARY_VERITY_SIG]);
- m->partitions[PARTITION_USR] = m->partitions[PARTITION_USR_SECONDARY];
- zero(m->partitions[PARTITION_USR_SECONDARY]);
- m->partitions[PARTITION_USR_VERITY] = m->partitions[PARTITION_USR_SECONDARY_VERITY];
- zero(m->partitions[PARTITION_USR_SECONDARY_VERITY]);
- m->partitions[PARTITION_USR_VERITY_SIG] = m->partitions[PARTITION_USR_SECONDARY_VERITY_SIG];
- zero(m->partitions[PARTITION_USR_SECONDARY_VERITY_SIG]);
+ m->partitions[PARTITION_USR] = TAKE_PARTITION(m->partitions[PARTITION_USR_SECONDARY]);
+ m->partitions[PARTITION_USR_VERITY] = TAKE_PARTITION(m->partitions[PARTITION_USR_SECONDARY_VERITY]);
+ m->partitions[PARTITION_USR_VERITY_SIG] = TAKE_PARTITION(m->partitions[PARTITION_USR_SECONDARY_VERITY_SIG]);
m->partitions[PARTITION_ROOT_OTHER].found = false;
m->partitions[PARTITION_ROOT_OTHER_VERITY].found = false;
"falling back to a root partition of a non-native architecture (%s).",
architecture_to_string(m->partitions[PARTITION_ROOT_OTHER].architecture));
- m->partitions[PARTITION_ROOT] = m->partitions[PARTITION_ROOT_OTHER];
- zero(m->partitions[PARTITION_ROOT_OTHER]);
- m->partitions[PARTITION_ROOT_VERITY] = m->partitions[PARTITION_ROOT_OTHER_VERITY];
- zero(m->partitions[PARTITION_ROOT_OTHER_VERITY]);
- m->partitions[PARTITION_ROOT_VERITY_SIG] = m->partitions[PARTITION_ROOT_OTHER_VERITY_SIG];
- zero(m->partitions[PARTITION_ROOT_OTHER_VERITY_SIG]);
-
- m->partitions[PARTITION_USR] = m->partitions[PARTITION_USR_OTHER];
- zero(m->partitions[PARTITION_USR_OTHER]);
- m->partitions[PARTITION_USR_VERITY] = m->partitions[PARTITION_USR_OTHER_VERITY];
- zero(m->partitions[PARTITION_USR_OTHER_VERITY]);
- m->partitions[PARTITION_USR_VERITY_SIG] = m->partitions[PARTITION_USR_OTHER_VERITY_SIG];
- zero(m->partitions[PARTITION_USR_OTHER_VERITY_SIG]);
+ m->partitions[PARTITION_ROOT] = TAKE_PARTITION(m->partitions[PARTITION_ROOT_OTHER]);
+ m->partitions[PARTITION_ROOT_VERITY] = TAKE_PARTITION(m->partitions[PARTITION_ROOT_OTHER_VERITY]);
+ m->partitions[PARTITION_ROOT_VERITY_SIG] = TAKE_PARTITION(m->partitions[PARTITION_ROOT_OTHER_VERITY_SIG]);
+
+ m->partitions[PARTITION_USR] = TAKE_PARTITION(m->partitions[PARTITION_USR_OTHER]);
+ m->partitions[PARTITION_USR_VERITY] = TAKE_PARTITION(m->partitions[PARTITION_USR_OTHER_VERITY]);
+ m->partitions[PARTITION_USR_VERITY_SIG] = TAKE_PARTITION(m->partitions[PARTITION_USR_OTHER_VERITY_SIG]);
}
/* Hmm, we found a signature partition but no Verity data? Something is off. */
"partition of the secondary architecture.");
/* Upgrade secondary arch to primary */
- m->partitions[PARTITION_USR] = m->partitions[PARTITION_USR_SECONDARY];
- zero(m->partitions[PARTITION_USR_SECONDARY]);
- m->partitions[PARTITION_USR_VERITY] = m->partitions[PARTITION_USR_SECONDARY_VERITY];
- zero(m->partitions[PARTITION_USR_SECONDARY_VERITY]);
- m->partitions[PARTITION_USR_VERITY_SIG] = m->partitions[PARTITION_USR_SECONDARY_VERITY_SIG];
- zero(m->partitions[PARTITION_USR_SECONDARY_VERITY_SIG]);
+ m->partitions[PARTITION_USR] = TAKE_PARTITION(m->partitions[PARTITION_USR_SECONDARY]);
+ m->partitions[PARTITION_USR_VERITY] = TAKE_PARTITION(m->partitions[PARTITION_USR_SECONDARY_VERITY]);
+ m->partitions[PARTITION_USR_VERITY_SIG] = TAKE_PARTITION(m->partitions[PARTITION_USR_SECONDARY_VERITY_SIG]);
m->partitions[PARTITION_USR_OTHER].found = false;
m->partitions[PARTITION_USR_OTHER_VERITY].found = false;
architecture_to_string(m->partitions[PARTITION_ROOT_OTHER].architecture));
/* Upgrade other arch to primary */
- m->partitions[PARTITION_USR] = m->partitions[PARTITION_USR_OTHER];
- zero(m->partitions[PARTITION_USR_OTHER]);
- m->partitions[PARTITION_USR_VERITY] = m->partitions[PARTITION_USR_OTHER_VERITY];
- zero(m->partitions[PARTITION_USR_OTHER_VERITY]);
- m->partitions[PARTITION_USR_VERITY_SIG] = m->partitions[PARTITION_USR_OTHER_VERITY_SIG];
- zero(m->partitions[PARTITION_USR_OTHER_VERITY_SIG]);
+ m->partitions[PARTITION_USR] = TAKE_PARTITION(m->partitions[PARTITION_USR_OTHER]);
+ m->partitions[PARTITION_USR_VERITY] = TAKE_PARTITION(m->partitions[PARTITION_USR_OTHER_VERITY]);
+ m->partitions[PARTITION_USR_VERITY_SIG] = TAKE_PARTITION(m->partitions[PARTITION_USR_OTHER_VERITY_SIG]);
}
/* Hmm, we found a signature partition but no Verity data? Something is off. */
if (r < 0)
return r;
- k = PARTITION_VERITY_OF(i);
+ k = partition_verity_of(i);
if (k >= 0) {
r = verity_partition(i, p, m->partitions + k, verity, flags | DISSECT_IMAGE_VERITY_SHARE, d);
if (r < 0)
if (r == 0)
return 0;
- d = PARTITION_VERITY_SIG_OF(verity->designator < 0 ? PARTITION_ROOT : verity->designator);
+ d = partition_verity_sig_of(verity->designator < 0 ? PARTITION_ROOT : verity->designator);
assert(d >= 0);
p = m->partitions + d;
for (unsigned k = 0; k < _META_MAX; k++) {
_cleanup_close_ int fd = -ENOENT;
- const char *p;
if (!paths[k])
continue;
if (image->single_file_system)
return partition_designator == PARTITION_ROOT && image->has_verity;
- return PARTITION_VERITY_OF(partition_designator) >= 0;
+ return partition_verity_of(partition_designator) >= 0;
}
bool dissected_image_verity_ready(const DissectedImage *image, PartitionDesignator partition_designator) {
if (image->single_file_system)
return partition_designator == PARTITION_ROOT;
- k = PARTITION_VERITY_OF(partition_designator);
+ k = partition_verity_of(partition_designator);
return k >= 0 && image->partitions[k].found;
}
if (image->single_file_system)
return partition_designator == PARTITION_ROOT;
- k = PARTITION_VERITY_SIG_OF(partition_designator);
+ k = partition_verity_sig_of(partition_designator);
return k >= 0 && image->partitions[k].found;
}
return 0;
}
-static const char *const partition_designator_table[] = {
- [PARTITION_ROOT] = "root",
- [PARTITION_ROOT_SECONDARY] = "root-secondary",
- [PARTITION_ROOT_OTHER] = "root-other",
- [PARTITION_USR] = "usr",
- [PARTITION_USR_SECONDARY] = "usr-secondary",
- [PARTITION_USR_OTHER] = "usr-other",
- [PARTITION_HOME] = "home",
- [PARTITION_SRV] = "srv",
- [PARTITION_ESP] = "esp",
- [PARTITION_XBOOTLDR] = "xbootldr",
- [PARTITION_SWAP] = "swap",
- [PARTITION_ROOT_VERITY] = "root-verity",
- [PARTITION_ROOT_SECONDARY_VERITY] = "root-secondary-verity",
- [PARTITION_ROOT_OTHER_VERITY] = "root-other-verity",
- [PARTITION_USR_VERITY] = "usr-verity",
- [PARTITION_USR_SECONDARY_VERITY] = "usr-secondary-verity",
- [PARTITION_USR_OTHER_VERITY] = "usr-other-verity",
- [PARTITION_ROOT_VERITY_SIG] = "root-verity-sig",
- [PARTITION_ROOT_SECONDARY_VERITY_SIG] = "root-secondary-verity-sig",
- [PARTITION_ROOT_OTHER_VERITY_SIG] = "root-other-verity-sig",
- [PARTITION_USR_VERITY_SIG] = "usr-verity-sig",
- [PARTITION_USR_SECONDARY_VERITY_SIG] = "usr-secondary-verity-sig",
- [PARTITION_USR_OTHER_VERITY_SIG] = "usr-other-verity-sig",
- [PARTITION_TMP] = "tmp",
- [PARTITION_VAR] = "var",
-};
-
static bool mount_options_relax_extension_release_checks(const MountOptions *options) {
if (!options)
return false;
return 0;
}
-
-DEFINE_STRING_TABLE_LOOKUP(partition_designator, PartitionDesignator);
#include "sd-id128.h"
#include "architecture.h"
+#include "gpt.h"
#include "list.h"
#include "loop-util.h"
#include "macro.h"
.architecture = _ARCHITECTURE_INVALID, \
.mount_node_fd = -1, \
})
-
-typedef enum PartitionDesignator {
- PARTITION_ROOT,
- PARTITION_ROOT_SECONDARY, /* Secondary architecture */
- PARTITION_ROOT_OTHER,
- PARTITION_USR,
- PARTITION_USR_SECONDARY,
- PARTITION_USR_OTHER,
- PARTITION_HOME,
- PARTITION_SRV,
- PARTITION_ESP,
- PARTITION_XBOOTLDR,
- PARTITION_SWAP,
- PARTITION_ROOT_VERITY, /* verity data for the PARTITION_ROOT partition */
- PARTITION_ROOT_SECONDARY_VERITY, /* verity data for the PARTITION_ROOT_SECONDARY partition */
- PARTITION_ROOT_OTHER_VERITY,
- PARTITION_USR_VERITY,
- PARTITION_USR_SECONDARY_VERITY,
- PARTITION_USR_OTHER_VERITY,
- PARTITION_ROOT_VERITY_SIG, /* PKCS#7 signature for root hash for the PARTITION_ROOT partition */
- PARTITION_ROOT_SECONDARY_VERITY_SIG, /* ditto for the PARTITION_ROOT_SECONDARY partition */
- PARTITION_ROOT_OTHER_VERITY_SIG,
- PARTITION_USR_VERITY_SIG,
- PARTITION_USR_SECONDARY_VERITY_SIG,
- PARTITION_USR_OTHER_VERITY_SIG,
- PARTITION_TMP,
- PARTITION_VAR,
- _PARTITION_DESIGNATOR_MAX,
- _PARTITION_DESIGNATOR_INVALID = -EINVAL,
-} PartitionDesignator;
-
-static inline bool PARTITION_DESIGNATOR_VERSIONED(PartitionDesignator d) {
- /* Returns true for all designators where we want to support a concept of "versioning", i.e. which
- * likely contain software binaries (or hashes thereof) that make sense to be versioned as a
- * whole. We use this check to automatically pick the newest version of these partitions, by version
- * comparing the partition labels. */
-
- return IN_SET(d,
- PARTITION_ROOT,
- PARTITION_ROOT_SECONDARY,
- PARTITION_ROOT_OTHER,
- PARTITION_USR,
- PARTITION_USR_SECONDARY,
- PARTITION_USR_OTHER,
- PARTITION_ROOT_VERITY,
- PARTITION_ROOT_SECONDARY_VERITY,
- PARTITION_ROOT_OTHER_VERITY,
- PARTITION_USR_VERITY,
- PARTITION_USR_SECONDARY_VERITY,
- PARTITION_USR_OTHER_VERITY,
- PARTITION_ROOT_VERITY_SIG,
- PARTITION_ROOT_SECONDARY_VERITY_SIG,
- PARTITION_ROOT_OTHER_VERITY_SIG,
- PARTITION_USR_VERITY_SIG,
- PARTITION_USR_SECONDARY_VERITY_SIG,
- PARTITION_USR_OTHER_VERITY_SIG);
-}
-
-static inline PartitionDesignator PARTITION_VERITY_OF(PartitionDesignator p) {
- switch (p) {
-
- case PARTITION_ROOT:
- return PARTITION_ROOT_VERITY;
-
- case PARTITION_ROOT_SECONDARY:
- return PARTITION_ROOT_SECONDARY_VERITY;
-
- case PARTITION_ROOT_OTHER:
- return PARTITION_ROOT_OTHER_VERITY;
-
- case PARTITION_USR:
- return PARTITION_USR_VERITY;
-
- case PARTITION_USR_SECONDARY:
- return PARTITION_USR_SECONDARY_VERITY;
-
- case PARTITION_USR_OTHER:
- return PARTITION_USR_OTHER_VERITY;
-
- default:
- return _PARTITION_DESIGNATOR_INVALID;
- }
-}
-
-static inline PartitionDesignator PARTITION_VERITY_SIG_OF(PartitionDesignator p) {
- switch (p) {
-
- case PARTITION_ROOT:
- return PARTITION_ROOT_VERITY_SIG;
-
- case PARTITION_ROOT_SECONDARY:
- return PARTITION_ROOT_SECONDARY_VERITY_SIG;
-
- case PARTITION_ROOT_OTHER:
- return PARTITION_ROOT_OTHER_VERITY_SIG;
-
- case PARTITION_USR:
- return PARTITION_USR_VERITY_SIG;
-
- case PARTITION_USR_SECONDARY:
- return PARTITION_USR_SECONDARY_VERITY_SIG;
-
- case PARTITION_USR_OTHER:
- return PARTITION_USR_OTHER_VERITY_SIG;
-
- default:
- return _PARTITION_DESIGNATOR_INVALID;
- }
-}
-
-static inline PartitionDesignator PARTITION_ROOT_OF_ARCH(Architecture arch) {
- switch (arch) {
-
- case native_architecture():
- return PARTITION_ROOT;
-
-#ifdef ARCHITECTURE_SECONDARY
- case ARCHITECTURE_SECONDARY:
- return PARTITION_ROOT_SECONDARY;
-#endif
-
- default:
- return PARTITION_ROOT_OTHER;
- }
-}
-
-static inline PartitionDesignator PARTITION_USR_OF_ARCH(Architecture arch) {
- switch (arch) {
-
- case native_architecture():
- return PARTITION_USR;
-
-#ifdef ARCHITECTURE_SECONDARY
- case ARCHITECTURE_SECONDARY:
- return PARTITION_USR_SECONDARY;
-#endif
-
- default:
- return PARTITION_USR_OTHER;
- }
-}
+#define TAKE_PARTITION(p) \
+ ({ \
+ DissectedPartition *_pp = &(p), _p = *_pp; \
+ *_pp = DISSECTED_PARTITION_NULL; \
+ _p; \
+ })
typedef enum DissectImageFlags {
DISSECT_IMAGE_DEVICE_READ_ONLY = 1 << 0, /* Make device read-only */
/* Meta information extracted from /etc/os-release and similar */
char *image_name;
+ sd_id128_t image_uuid;
char *hostname;
sd_id128_t machine_id;
char **machine_info;
int dissected_image_relinquish(DissectedImage *m);
-const char* partition_designator_to_string(PartitionDesignator d) _const_;
-PartitionDesignator partition_designator_from_string(const char *name) _pure_;
-
int verity_settings_load(VeritySettings *verity, const char *image, const char *root_hash_path, const char *root_hash_sig_path);
void verity_settings_done(VeritySettings *verity);
size_t n_cells;
bool header; /* Whether to show the header row? */
+ bool vertical; /* Whether to field names are on the left rather than the first line */
+
TableErsatz ersatz; /* What to show when we have an empty cell or an invalid value that cannot be rendered. */
size_t width; /* If == 0 format this as wide as necessary. If SIZE_MAX format this to console
for (const char *h = first_header; h; h = va_arg(ap, const char*)) {
TableCell *cell;
- r = table_add_cell(t, &cell, TABLE_STRING, h);
- if (r < 0) {
- va_end(ap);
- return NULL;
- }
-
- /* Make the table header uppercase */
- r = table_set_uppercase(t, cell, true);
+ r = table_add_cell(t, &cell, TABLE_HEADER, h);
if (r < 0) {
va_end(ap);
return NULL;
return TAKE_PTR(t);
}
+Table *table_new_vertical(void) {
+ _cleanup_(table_unrefp) Table *t = NULL;
+ TableCell *cell;
+
+ t = table_new_raw(2);
+ if (!t)
+ return NULL;
+
+ t->vertical = true;
+ t->header = false;
+
+ if (table_add_cell(t, &cell, TABLE_HEADER, "key") < 0)
+ return NULL;
+
+ if (table_set_align_percent(t, cell, 100) < 0)
+ return NULL;
+
+ if (table_add_cell(t, &cell, TABLE_HEADER, "value") < 0)
+ return NULL;
+
+ if (table_set_align_percent(t, cell, 0) < 0)
+ return NULL;
+
+ return TAKE_PTR(t);
+}
+
static TableData *table_data_free(TableData *d) {
assert(d);
case TABLE_STRING:
case TABLE_PATH:
+ case TABLE_FIELD:
+ case TABLE_HEADER:
return strlen(data) + 1;
case TABLE_STRV:
size_t maximum_width,
unsigned weight,
unsigned align_percent,
- unsigned ellipsize_percent) {
+ unsigned ellipsize_percent,
+ bool uppercase) {
size_t k, l;
assert(d);
if (d->ellipsize_percent != ellipsize_percent)
return false;
- /* If a color/url/uppercase flag is set, refuse to merge */
+ if (d->uppercase != uppercase)
+ return false;
+
+ /* If a color/url is set, refuse to merge */
if (d->color || d->rgap_color)
return false;
if (d->url)
return false;
- if (d->uppercase)
- return false;
k = table_data_size(type, data);
l = table_data_size(d->type, d->data);
size_t maximum_width,
unsigned weight,
unsigned align_percent,
- unsigned ellipsize_percent) {
+ unsigned ellipsize_percent,
+ bool uppercase) {
_cleanup_free_ TableData *d = NULL;
size_t data_size;
d->weight = weight;
d->align_percent = align_percent;
d->ellipsize_percent = ellipsize_percent;
+ d->uppercase = uppercase;
if (IN_SET(type, TABLE_STRV, TABLE_STRV_WRAPPED)) {
d->strv = strv_copy(data);
unsigned ellipsize_percent) {
_cleanup_(table_data_unrefp) TableData *d = NULL;
+ bool uppercase;
TableData *p;
assert(t);
assert(align_percent <= 100);
assert(ellipsize_percent <= 100);
+ uppercase = type == TABLE_HEADER;
+
/* Small optimization: Pretty often adjacent cells in two subsequent lines have the same data and
* formatting. Let's see if we can reuse the cell data and ref it once more. */
- if (p && table_data_matches(p, type, data, minimum_width, maximum_width, weight, align_percent, ellipsize_percent))
+ if (p && table_data_matches(p, type, data, minimum_width, maximum_width, weight, align_percent, ellipsize_percent, uppercase))
d = table_data_ref(p);
else {
- d = table_data_new(type, data, minimum_width, maximum_width, weight, align_percent, ellipsize_percent);
+ d = table_data_new(type, data, minimum_width, maximum_width, weight, align_percent, ellipsize_percent, uppercase);
if (!d)
return -ENOMEM;
}
return 0;
}
-int table_add_cell_stringf(Table *t, TableCell **ret_cell, const char *format, ...) {
+int table_add_cell_stringf_full(Table *t, TableCell **ret_cell, TableDataType dt, const char *format, ...) {
_cleanup_free_ char *buffer = NULL;
va_list ap;
int r;
+ assert(t);
+ assert(IN_SET(dt, TABLE_STRING, TABLE_PATH, TABLE_FIELD, TABLE_HEADER));
+
va_start(ap, format);
r = vasprintf(&buffer, format, ap);
va_end(ap);
if (r < 0)
return -ENOMEM;
- return table_add_cell(t, ret_cell, TABLE_STRING, buffer);
+ return table_add_cell(t, ret_cell, dt, buffer);
}
int table_fill_empty(Table *t, size_t until_column) {
od->maximum_width,
od->weight,
od->align_percent,
- od->ellipsize_percent);
+ od->ellipsize_percent,
+ od->uppercase);
if (!nd)
return -ENOMEM;
nd->color = od->color;
nd->rgap_color = od->rgap_color;
nd->url = TAKE_PTR(curl);
- nd->uppercase = od->uppercase;
table_data_unref(od);
t->data[i] = nd;
od->maximum_width,
od->weight,
od->align_percent,
- od->ellipsize_percent);
+ od->ellipsize_percent,
+ od->uppercase);
if (!nd)
return -ENOMEM;
nd->color = od->color;
nd->rgap_color = od->rgap_color;
nd->url = TAKE_PTR(curl);
- nd->uppercase = od->uppercase;
table_data_unref(od);
t->data[i] = nd;
case TABLE_STRING:
case TABLE_PATH:
+ case TABLE_FIELD:
+ case TABLE_HEADER:
data = va_arg(ap, const char *);
break;
switch (a->type) {
case TABLE_STRING:
+ case TABLE_FIELD:
+ case TABLE_HEADER:
return strcmp(a->string, b->string);
case TABLE_PATH:
case TABLE_STRING:
case TABLE_PATH:
+ case TABLE_FIELD:
+ case TABLE_HEADER:
if (d->uppercase && !avoid_uppercasing) {
- d->formatted = new(char, strlen(d->string) + 1);
+ d->formatted = new(char, strlen(d->string) + (d->type == TABLE_FIELD) + 1);
if (!d->formatted)
return NULL;
char *q = d->formatted;
- for (char *p = d->string; *p; p++, q++)
- *q = (char) toupper((unsigned char) *p);
+ for (char *p = d->string; *p; p++)
+ *(q++) = (char) toupper((unsigned char) *p);
+
+ if (d->type == TABLE_FIELD)
+ *(q++) = ':';
+
*q = 0;
+ return d->formatted;
+ } else if (d->type == TABLE_FIELD) {
+ d->formatted = strjoin(d->string, ":");
+ if (!d->formatted)
+ return NULL;
return d->formatted;
}
if (table_data_isempty(d))
return ansi_grey();
+ if (d->type == TABLE_FIELD)
+ return ansi_bright_blue();
+ if (d->type == TABLE_HEADER)
+ return ansi_underline();
+
return NULL;
}
if (d->rgap_color)
return d->rgap_color;
+ if (d->type == TABLE_HEADER)
+ return ansi_underline();
+
return NULL;
}
/* Drop trailing white spaces of last column when no cosmetics is set. */
if (j == display_columns - 1 &&
- (!colors_enabled() || (!table_data_color(d) && row != t->data)) &&
+ (!colors_enabled() || !table_data_color(d)) &&
(!urlify_enabled() || !d->url))
delete_trailing_chars(aligned, NULL);
field = buffer;
}
- if (colors_enabled()) {
- if (gap_color)
- fputs(gap_color, f);
- else if (row == t->data) /* underline header line fully, including the column separator */
- fputs(ansi_underline(), f);
- }
+ if (colors_enabled() && gap_color)
+ fputs(gap_color, f);
if (j > 0)
fputc(' ', f); /* column separator left of cell */
color = table_data_color(d);
/* Undo gap color */
- if (gap_color || (color && row == t->data))
+ if (gap_color)
fputs(ANSI_NORMAL, f);
if (color)
fputs(color, f);
- else if (gap_color && row == t->data) /* underline header line cell */
- fputs(ansi_underline(), f);
}
fputs(field, f);
- if (colors_enabled() && (color || row == t->data))
+ if (colors_enabled() && color)
fputs(ANSI_NORMAL, f);
gap_color = table_data_rgap_color(d);
case TABLE_STRING:
case TABLE_PATH:
+ case TABLE_FIELD:
+ case TABLE_HEADER:
return json_variant_new_string(ret, d->string);
case TABLE_STRV:
return json_variant_new_array_bytes(ret, &d->address, FAMILY_ADDRESS_SIZE(AF_INET6));
case TABLE_ID128:
- return json_variant_new_string(ret, SD_ID128_TO_STRING(d->id128));
+ return json_variant_new_id128(ret, d->id128);
case TABLE_UUID:
- return json_variant_new_string(ret, SD_ID128_TO_UUID_STRING(d->id128));
+ return json_variant_new_uuid(ret, d->id128);
case TABLE_UID:
if (!uid_is_valid(d->uid))
return c;
}
-static const char *table_get_json_field_name(Table *t, size_t column) {
+static int table_make_json_field_name(Table *t, TableData *d, char **ret) {
+ _cleanup_free_ char *mangled = NULL;
+ const char *n;
+
assert(t);
+ assert(d);
+ assert(ret);
- return column < t->n_json_fields ? t->json_fields[column] : NULL;
+ if (IN_SET(d->type, TABLE_HEADER, TABLE_FIELD))
+ n = d->string;
+ else {
+ n = table_data_format(t, d, /* avoid_uppercasing= */ true, SIZE_MAX, NULL);
+ if (!n)
+ return -ENOMEM;
+ }
+
+ mangled = string_to_json_field_name(n);
+ if (!mangled)
+ return -ENOMEM;
+
+ *ret = TAKE_PTR(mangled);
+ return 0;
}
-int table_to_json(Table *t, JsonVariant **ret) {
+static const char *table_get_json_field_name(Table *t, size_t idx) {
+ assert(t);
+
+ return idx < t->n_json_fields ? t->json_fields[idx] : NULL;
+}
+
+static int table_to_json_regular(Table *t, JsonVariant **ret) {
JsonVariant **rows = NULL, **elements = NULL;
_cleanup_free_ size_t *sorted = NULL;
size_t n_rows, display_columns;
int r;
assert(t);
+ assert(!t->vertical);
/* Ensure we have no incomplete rows */
+ assert(t->n_columns > 0);
assert(t->n_cells % t->n_columns == 0);
n_rows = t->n_cells / t->n_columns;
/* Use explicitly set JSON field name, if we have one. Otherwise mangle the column field value. */
n = table_get_json_field_name(t, c);
if (!n) {
- const char *formatted;
- TableData *d;
-
- assert_se(d = t->data[c]);
-
- /* Field names must be strings, hence format whatever we got here as a string first */
- formatted = table_data_format(t, d, true, SIZE_MAX, NULL);
- if (!formatted) {
- r = -ENOMEM;
+ r = table_make_json_field_name(t, ASSERT_PTR(t->data[c]), &mangled);
+ if (r < 0)
goto finish;
- }
- /* Arbitrary strings suck as field names, try to mangle them into something more suitable hence */
- mangled = string_to_json_field_name(formatted);
- if (!mangled) {
- r = -ENOMEM;
- goto finish;
- }
n = mangled;
}
return r;
}
+static int table_to_json_vertical(Table *t, JsonVariant **ret) {
+ JsonVariant **elements = NULL;
+ size_t n_elements = 0;
+ int r;
+
+ assert(t);
+ assert(t->vertical);
+
+ if (t->n_columns != 2)
+ return -EINVAL;
+
+ /* Ensure we have no incomplete rows */
+ assert(t->n_cells % t->n_columns == 0);
+
+ elements = new0(JsonVariant *, t->n_cells);
+ if (!elements) {
+ r = -ENOMEM;
+ goto finish;
+ }
+
+ for (size_t i = t->n_columns; i < t->n_cells; i++) {
+
+ if (i % t->n_columns == 0) {
+ _cleanup_free_ char *mangled = NULL;
+ const char *n;
+
+ n = table_get_json_field_name(t, i / t->n_columns - 1);
+ if (!n) {
+ r = table_make_json_field_name(t, ASSERT_PTR(t->data[i]), &mangled);
+ if (r < 0)
+ goto finish;
+
+ n = mangled;
+ }
+
+ r = json_variant_new_string(elements + n_elements, n);
+ } else
+ r = table_data_to_json(t->data[i], elements + n_elements);
+ if (r < 0)
+ goto finish;
+
+ n_elements++;
+ }
+
+ r = json_variant_new_object(ret, elements, n_elements);
+
+finish:
+ if (elements) {
+ json_variant_unref_many(elements, n_elements);
+ free(elements);
+ }
+
+ return r;
+}
+
+int table_to_json(Table *t, JsonVariant **ret) {
+ assert(t);
+
+ if (t->vertical)
+ return table_to_json_vertical(t, ret);
+
+ return table_to_json_regular(t, ret);
+}
+
int table_print_json(Table *t, FILE *f, JsonFormatFlags flags) {
_cleanup_(json_variant_unrefp) JsonVariant *v = NULL;
int r;
return 0;
}
-int table_set_json_field_name(Table *t, size_t column, const char *name) {
+int table_set_json_field_name(Table *t, size_t idx, const char *name) {
int r;
assert(t);
if (name) {
size_t m;
- m = MAX(column + 1, t->n_json_fields);
+ m = MAX(idx + 1, t->n_json_fields);
if (!GREEDY_REALLOC0(t->json_fields, m))
return -ENOMEM;
- r = free_and_strdup(t->json_fields + column, name);
+ r = free_and_strdup(t->json_fields + idx, name);
if (r < 0)
return r;
t->n_json_fields = m;
return r;
} else {
- if (column >= t->n_json_fields)
+ if (idx >= t->n_json_fields)
return 0;
- t->json_fields[column] = mfree(t->json_fields[column]);
+ t->json_fields[idx] = mfree(t->json_fields[idx]);
return 1;
}
}
typedef enum TableDataType {
TABLE_EMPTY,
TABLE_STRING,
+ TABLE_HEADER, /* in regular mode: the cells in the first row, that carry the column names */
+ TABLE_FIELD, /* in vertical mode: the cells in the first column, that carry the field names */
TABLE_STRV,
TABLE_STRV_WRAPPED,
TABLE_PATH,
Table *table_new_internal(const char *first_header, ...) _sentinel_;
#define table_new(...) table_new_internal(__VA_ARGS__, NULL)
Table *table_new_raw(size_t n_columns);
+Table *table_new_vertical(void);
Table *table_unref(Table *t);
DEFINE_TRIVIAL_CLEANUP_FUNC(Table*, table_unref);
static inline int table_add_cell(Table *t, TableCell **ret_cell, TableDataType type, const void *data) {
return table_add_cell_full(t, ret_cell, type, data, SIZE_MAX, SIZE_MAX, UINT_MAX, UINT_MAX, UINT_MAX);
}
-int table_add_cell_stringf(Table *t, TableCell **ret_cell, const char *format, ...) _printf_(3, 4);
+int table_add_cell_stringf_full(Table *t, TableCell **ret_cell, TableDataType type, const char *format, ...) _printf_(4, 5);
+#define table_add_cell_stringf(t, ret_cell, format, ...) table_add_cell_stringf_full(t, ret_cell, TABLE_STRING, format, __VA_ARGS__)
int table_fill_empty(Table *t, size_t until_column);
int table_print_with_pager(Table *t, JsonFormatFlags json_format_flags, PagerFlags pager_flags, bool show_header);
-int table_set_json_field_name(Table *t, size_t column, const char *name);
+int table_set_json_field_name(Table *t, size_t idx, const char *name);
#define table_log_add_error(r) \
log_error_errno(r, "Failed to add cells to table: %m")
char ***ret_values,
char **ret_filtered) {
- const char *name, *namefound = NULL, *x;
+ const char *namefound = NULL, *x;
_cleanup_strv_free_ char **stor = NULL, **values = NULL;
_cleanup_free_ char *value = NULL, **filtered = NULL;
int r;
if (!x)
continue;
/* Match name, but when ret_values, only when followed by assignment. */
- if (*x == '=' || (!ret_values && *x == '\0'))
+ if (*x == '=' || (!ret_values && *x == '\0')) {
+ /* Keep the last occurrence found */
+ namefound = name;
goto found;
+ }
}
*t = *s;
t++;
continue;
found:
- /* Keep the last occurrence found */
- namefound = name;
-
if (ret_value || ret_values) {
assert(IN_SET(*x, '=', '\0'));
/* SPDX-License-Identifier: LGPL-2.1-or-later */
#include "gpt.h"
+#include "string-table.h"
#include "string-util.h"
#include "utf8.h"
#pragma message "Please define GPT partition types for your architecture."
#endif
+bool partition_designator_is_versioned(PartitionDesignator d) {
+ /* Returns true for all designators where we want to support a concept of "versioning", i.e. which
+ * likely contain software binaries (or hashes thereof) that make sense to be versioned as a
+ * whole. We use this check to automatically pick the newest version of these partitions, by version
+ * comparing the partition labels. */
+
+ return IN_SET(d,
+ PARTITION_ROOT,
+ PARTITION_ROOT_SECONDARY,
+ PARTITION_ROOT_OTHER,
+ PARTITION_USR,
+ PARTITION_USR_SECONDARY,
+ PARTITION_USR_OTHER,
+ PARTITION_ROOT_VERITY,
+ PARTITION_ROOT_SECONDARY_VERITY,
+ PARTITION_ROOT_OTHER_VERITY,
+ PARTITION_USR_VERITY,
+ PARTITION_USR_SECONDARY_VERITY,
+ PARTITION_USR_OTHER_VERITY,
+ PARTITION_ROOT_VERITY_SIG,
+ PARTITION_ROOT_SECONDARY_VERITY_SIG,
+ PARTITION_ROOT_OTHER_VERITY_SIG,
+ PARTITION_USR_VERITY_SIG,
+ PARTITION_USR_SECONDARY_VERITY_SIG,
+ PARTITION_USR_OTHER_VERITY_SIG);
+}
+
+PartitionDesignator partition_verity_of(PartitionDesignator p) {
+ switch (p) {
+
+ case PARTITION_ROOT:
+ return PARTITION_ROOT_VERITY;
+
+ case PARTITION_ROOT_SECONDARY:
+ return PARTITION_ROOT_SECONDARY_VERITY;
+
+ case PARTITION_ROOT_OTHER:
+ return PARTITION_ROOT_OTHER_VERITY;
+
+ case PARTITION_USR:
+ return PARTITION_USR_VERITY;
+
+ case PARTITION_USR_SECONDARY:
+ return PARTITION_USR_SECONDARY_VERITY;
+
+ case PARTITION_USR_OTHER:
+ return PARTITION_USR_OTHER_VERITY;
+
+ default:
+ return _PARTITION_DESIGNATOR_INVALID;
+ }
+}
+
+PartitionDesignator partition_verity_sig_of(PartitionDesignator p) {
+ switch (p) {
+
+ case PARTITION_ROOT:
+ return PARTITION_ROOT_VERITY_SIG;
+
+ case PARTITION_ROOT_SECONDARY:
+ return PARTITION_ROOT_SECONDARY_VERITY_SIG;
+
+ case PARTITION_ROOT_OTHER:
+ return PARTITION_ROOT_OTHER_VERITY_SIG;
+
+ case PARTITION_USR:
+ return PARTITION_USR_VERITY_SIG;
+
+ case PARTITION_USR_SECONDARY:
+ return PARTITION_USR_SECONDARY_VERITY_SIG;
+
+ case PARTITION_USR_OTHER:
+ return PARTITION_USR_OTHER_VERITY_SIG;
+
+ default:
+ return _PARTITION_DESIGNATOR_INVALID;
+ }
+}
+
+PartitionDesignator partition_root_of_arch(Architecture arch) {
+ switch (arch) {
+
+ case native_architecture():
+ return PARTITION_ROOT;
+
+#ifdef ARCHITECTURE_SECONDARY
+ case ARCHITECTURE_SECONDARY:
+ return PARTITION_ROOT_SECONDARY;
+#endif
+
+ default:
+ return PARTITION_ROOT_OTHER;
+ }
+}
+
+PartitionDesignator partition_usr_of_arch(Architecture arch) {
+ switch (arch) {
+
+ case native_architecture():
+ return PARTITION_USR;
+
+#ifdef ARCHITECTURE_SECONDARY
+ case ARCHITECTURE_SECONDARY:
+ return PARTITION_USR_SECONDARY;
+#endif
+
+ default:
+ return PARTITION_USR_OTHER;
+ }
+}
+
+static const char *const partition_designator_table[] = {
+ [PARTITION_ROOT] = "root",
+ [PARTITION_ROOT_SECONDARY] = "root-secondary",
+ [PARTITION_ROOT_OTHER] = "root-other",
+ [PARTITION_USR] = "usr",
+ [PARTITION_USR_SECONDARY] = "usr-secondary",
+ [PARTITION_USR_OTHER] = "usr-other",
+ [PARTITION_HOME] = "home",
+ [PARTITION_SRV] = "srv",
+ [PARTITION_ESP] = "esp",
+ [PARTITION_XBOOTLDR] = "xbootldr",
+ [PARTITION_SWAP] = "swap",
+ [PARTITION_ROOT_VERITY] = "root-verity",
+ [PARTITION_ROOT_SECONDARY_VERITY] = "root-secondary-verity",
+ [PARTITION_ROOT_OTHER_VERITY] = "root-other-verity",
+ [PARTITION_USR_VERITY] = "usr-verity",
+ [PARTITION_USR_SECONDARY_VERITY] = "usr-secondary-verity",
+ [PARTITION_USR_OTHER_VERITY] = "usr-other-verity",
+ [PARTITION_ROOT_VERITY_SIG] = "root-verity-sig",
+ [PARTITION_ROOT_SECONDARY_VERITY_SIG] = "root-secondary-verity-sig",
+ [PARTITION_ROOT_OTHER_VERITY_SIG] = "root-other-verity-sig",
+ [PARTITION_USR_VERITY_SIG] = "usr-verity-sig",
+ [PARTITION_USR_SECONDARY_VERITY_SIG] = "usr-secondary-verity-sig",
+ [PARTITION_USR_OTHER_VERITY_SIG] = "usr-other-verity-sig",
+ [PARTITION_TMP] = "tmp",
+ [PARTITION_VAR] = "var",
+ [PARTITION_USER_HOME] = "user-home",
+ [PARTITION_LINUX_GENERIC] = "linux-generic",
+};
+
+DEFINE_STRING_TABLE_LOOKUP(partition_designator, PartitionDesignator);
+
+static const char *const partition_mountpoint_table[] = {
+ [PARTITION_ROOT] = "/\0",
+ [PARTITION_ROOT_SECONDARY] = "/\0",
+ [PARTITION_ROOT_OTHER] = "/\0",
+ [PARTITION_USR] = "/usr\0",
+ [PARTITION_USR_SECONDARY] = "/usr\0",
+ [PARTITION_USR_OTHER] = "/usr\0",
+ [PARTITION_HOME] = "/home\0",
+ [PARTITION_SRV] = "/srv\0",
+ [PARTITION_ESP] = "/efi\0/boot\0",
+ [PARTITION_XBOOTLDR] = "/boot\0",
+ [PARTITION_TMP] = "/var/tmp\0",
+ [PARTITION_VAR] = "/var\0",
+};
+
+DEFINE_PRIVATE_STRING_TABLE_LOOKUP_TO_STRING(partition_mountpoint, PartitionDesignator);
+
#define _GPT_ARCH_SEXTET(arch, name) \
- { SD_GPT_ROOT_##arch, "root-" name, ARCHITECTURE_##arch, .is_root = true }, \
- { SD_GPT_ROOT_##arch##_VERITY, "root-" name "-verity", ARCHITECTURE_##arch, .is_root_verity = true }, \
- { SD_GPT_ROOT_##arch##_VERITY_SIG, "root-" name "-verity-sig", ARCHITECTURE_##arch, .is_root_verity_sig = true }, \
- { SD_GPT_USR_##arch, "usr-" name, ARCHITECTURE_##arch, .is_usr = true }, \
- { SD_GPT_USR_##arch##_VERITY, "usr-" name "-verity", ARCHITECTURE_##arch, .is_usr_verity = true }, \
- { SD_GPT_USR_##arch##_VERITY_SIG, "usr-" name "-verity-sig", ARCHITECTURE_##arch, .is_usr_verity_sig = true }
+ { SD_GPT_ROOT_##arch, "root-" name, ARCHITECTURE_##arch, .designator = PARTITION_ROOT_OTHER }, \
+ { SD_GPT_ROOT_##arch##_VERITY, "root-" name "-verity", ARCHITECTURE_##arch, .designator = PARTITION_ROOT_OTHER_VERITY }, \
+ { SD_GPT_ROOT_##arch##_VERITY_SIG, "root-" name "-verity-sig", ARCHITECTURE_##arch, .designator = PARTITION_ROOT_OTHER_VERITY_SIG }, \
+ { SD_GPT_USR_##arch, "usr-" name, ARCHITECTURE_##arch, .designator = PARTITION_USR_OTHER }, \
+ { SD_GPT_USR_##arch##_VERITY, "usr-" name "-verity", ARCHITECTURE_##arch, .designator = PARTITION_USR_OTHER_VERITY }, \
+ { SD_GPT_USR_##arch##_VERITY_SIG, "usr-" name "-verity-sig", ARCHITECTURE_##arch, .designator = PARTITION_USR_OTHER_VERITY_SIG }
const GptPartitionType gpt_partition_type_table[] = {
_GPT_ARCH_SEXTET(ALPHA, "alpha"),
_GPT_ARCH_SEXTET(X86, "x86"),
_GPT_ARCH_SEXTET(X86_64, "x86-64"),
#ifdef SD_GPT_ROOT_NATIVE
- { SD_GPT_ROOT_NATIVE, "root", native_architecture(), .is_root = true },
- { SD_GPT_ROOT_NATIVE_VERITY, "root-verity", native_architecture(), .is_root_verity = true },
- { SD_GPT_ROOT_NATIVE_VERITY_SIG, "root-verity-sig", native_architecture(), .is_root_verity_sig = true },
- { SD_GPT_USR_NATIVE, "usr", native_architecture(), .is_usr = true },
- { SD_GPT_USR_NATIVE_VERITY, "usr-verity", native_architecture(), .is_usr_verity = true },
- { SD_GPT_USR_NATIVE_VERITY_SIG, "usr-verity-sig", native_architecture(), .is_usr_verity_sig = true },
+ { SD_GPT_ROOT_NATIVE, "root", native_architecture(), .designator = PARTITION_ROOT },
+ { SD_GPT_ROOT_NATIVE_VERITY, "root-verity", native_architecture(), .designator = PARTITION_ROOT_VERITY },
+ { SD_GPT_ROOT_NATIVE_VERITY_SIG, "root-verity-sig", native_architecture(), .designator = PARTITION_ROOT_VERITY_SIG },
+ { SD_GPT_USR_NATIVE, "usr", native_architecture(), .designator = PARTITION_USR },
+ { SD_GPT_USR_NATIVE_VERITY, "usr-verity", native_architecture(), .designator = PARTITION_USR_VERITY },
+ { SD_GPT_USR_NATIVE_VERITY_SIG, "usr-verity-sig", native_architecture(), .designator = PARTITION_USR_VERITY_SIG },
#endif
#ifdef SD_GPT_ROOT_SECONDARY
- _GPT_ARCH_SEXTET(SECONDARY, "secondary"),
+ { SD_GPT_ROOT_NATIVE, "root-secondary", native_architecture(), .designator = PARTITION_ROOT_SECONDARY },
+ { SD_GPT_ROOT_NATIVE_VERITY, "root-secondary-verity", native_architecture(), .designator = PARTITION_ROOT_SECONDARY_VERITY },
+ { SD_GPT_ROOT_NATIVE_VERITY_SIG, "root-secondary-verity-sig", native_architecture(), .designator = PARTITION_ROOT_SECONDARY_VERITY_SIG },
+ { SD_GPT_USR_NATIVE, "usr-secondary", native_architecture(), .designator = PARTITION_USR_SECONDARY },
+ { SD_GPT_USR_NATIVE_VERITY, "usr-secondary-verity", native_architecture(), .designator = PARTITION_USR_SECONDARY_VERITY },
+ { SD_GPT_USR_NATIVE_VERITY_SIG, "usr-secondary-verity-sig", native_architecture(), .designator = PARTITION_USR_SECONDARY_VERITY_SIG },
#endif
- { SD_GPT_ESP, "esp", _ARCHITECTURE_INVALID },
- { SD_GPT_XBOOTLDR, "xbootldr", _ARCHITECTURE_INVALID },
- { SD_GPT_SWAP, "swap", _ARCHITECTURE_INVALID },
- { SD_GPT_HOME, "home", _ARCHITECTURE_INVALID },
- { SD_GPT_SRV, "srv", _ARCHITECTURE_INVALID },
- { SD_GPT_VAR, "var", _ARCHITECTURE_INVALID },
- { SD_GPT_TMP, "tmp", _ARCHITECTURE_INVALID },
- { SD_GPT_USER_HOME, "user-home", _ARCHITECTURE_INVALID },
- { SD_GPT_LINUX_GENERIC, "linux-generic", _ARCHITECTURE_INVALID },
+ { SD_GPT_ESP, "esp", _ARCHITECTURE_INVALID, .designator = PARTITION_ESP },
+ { SD_GPT_XBOOTLDR, "xbootldr", _ARCHITECTURE_INVALID, .designator = PARTITION_XBOOTLDR },
+ { SD_GPT_SWAP, "swap", _ARCHITECTURE_INVALID, .designator = PARTITION_SWAP },
+ { SD_GPT_HOME, "home", _ARCHITECTURE_INVALID, .designator = PARTITION_HOME },
+ { SD_GPT_SRV, "srv", _ARCHITECTURE_INVALID, .designator = PARTITION_SRV },
+ { SD_GPT_VAR, "var", _ARCHITECTURE_INVALID, .designator = PARTITION_VAR },
+ { SD_GPT_TMP, "tmp", _ARCHITECTURE_INVALID, .designator = PARTITION_TMP },
+ { SD_GPT_USER_HOME, "user-home", _ARCHITECTURE_INVALID, .designator = PARTITION_USER_HOME },
+ { SD_GPT_LINUX_GENERIC, "linux-generic", _ARCHITECTURE_INVALID, .designator = PARTITION_LINUX_GENERIC },
{}
};
if (pt)
return *pt;
- return (GptPartitionType) { .uuid = id, .arch = _ARCHITECTURE_INVALID };
+ return (GptPartitionType) {
+ .uuid = id,
+ .arch = _ARCHITECTURE_INVALID,
+ .designator = _PARTITION_DESIGNATOR_INVALID,
+ };
}
bool gpt_partition_type_is_root(sd_id128_t id) {
- return gpt_partition_type_from_uuid(id).is_root;
+ return IN_SET(gpt_partition_type_from_uuid(id).designator,
+ PARTITION_ROOT,
+ PARTITION_ROOT_SECONDARY,
+ PARTITION_ROOT_OTHER);
}
bool gpt_partition_type_is_root_verity(sd_id128_t id) {
- return gpt_partition_type_from_uuid(id).is_root_verity;
+ return IN_SET(gpt_partition_type_from_uuid(id).designator,
+ PARTITION_ROOT_VERITY,
+ PARTITION_ROOT_SECONDARY_VERITY,
+ PARTITION_ROOT_OTHER_VERITY);
}
bool gpt_partition_type_is_root_verity_sig(sd_id128_t id) {
- return gpt_partition_type_from_uuid(id).is_root_verity_sig;
+ return IN_SET(gpt_partition_type_from_uuid(id).designator,
+ PARTITION_ROOT_VERITY_SIG,
+ PARTITION_ROOT_SECONDARY_VERITY_SIG,
+ PARTITION_ROOT_OTHER_VERITY_SIG);
}
bool gpt_partition_type_is_usr(sd_id128_t id) {
- return gpt_partition_type_from_uuid(id).is_usr;
+ return IN_SET(gpt_partition_type_from_uuid(id).designator,
+ PARTITION_USR,
+ PARTITION_USR_SECONDARY,
+ PARTITION_USR_OTHER);
}
bool gpt_partition_type_is_usr_verity(sd_id128_t id) {
- return gpt_partition_type_from_uuid(id).is_usr_verity;
+ return IN_SET(gpt_partition_type_from_uuid(id).designator,
+ PARTITION_USR_VERITY,
+ PARTITION_USR_SECONDARY_VERITY,
+ PARTITION_USR_OTHER_VERITY);
}
bool gpt_partition_type_is_usr_verity_sig(sd_id128_t id) {
- return gpt_partition_type_from_uuid(id).is_usr_verity_sig;
+ return IN_SET(gpt_partition_type_from_uuid(id).designator,
+ PARTITION_USR_VERITY_SIG,
+ PARTITION_USR_SECONDARY_VERITY_SIG,
+ PARTITION_USR_OTHER_VERITY_SIG);
+}
+
+const char *gpt_partition_type_mountpoint_nulstr(sd_id128_t id) {
+ PartitionDesignator d = gpt_partition_type_from_uuid(id).designator;
+ if (d < 0)
+ return NULL;
+
+ return partition_mountpoint_to_string(d);
}
bool gpt_partition_type_knows_read_only(sd_id128_t id) {
return gpt_partition_type_is_root(id) ||
gpt_partition_type_is_usr(id) ||
- sd_id128_in_set(id,
- SD_GPT_HOME,
- SD_GPT_SRV,
- SD_GPT_VAR,
- SD_GPT_TMP,
- SD_GPT_XBOOTLDR) ||
- gpt_partition_type_is_root_verity(id) || /* pretty much implied, but let's set the bit to make things really clear */
- gpt_partition_type_is_usr_verity(id); /* ditto */
+ /* pretty much implied, but let's set the bit to make things really clear */
+ gpt_partition_type_is_root_verity(id) ||
+ gpt_partition_type_is_usr_verity(id) ||
+ IN_SET(gpt_partition_type_from_uuid(id).designator,
+ PARTITION_HOME,
+ PARTITION_SRV,
+ PARTITION_VAR,
+ PARTITION_TMP,
+ PARTITION_XBOOTLDR);
}
bool gpt_partition_type_knows_growfs(sd_id128_t id) {
return gpt_partition_type_is_root(id) ||
gpt_partition_type_is_usr(id) ||
- sd_id128_in_set(id,
- SD_GPT_HOME,
- SD_GPT_SRV,
- SD_GPT_VAR,
- SD_GPT_TMP,
- SD_GPT_XBOOTLDR);
+ IN_SET(gpt_partition_type_from_uuid(id).designator,
+ PARTITION_HOME,
+ PARTITION_SRV,
+ PARTITION_VAR,
+ PARTITION_TMP,
+ PARTITION_XBOOTLDR);
}
bool gpt_partition_type_knows_no_auto(sd_id128_t id) {
gpt_partition_type_is_root_verity(id) ||
gpt_partition_type_is_usr(id) ||
gpt_partition_type_is_usr_verity(id) ||
- sd_id128_in_set(id,
- SD_GPT_HOME,
- SD_GPT_SRV,
- SD_GPT_VAR,
- SD_GPT_TMP,
- SD_GPT_XBOOTLDR,
- SD_GPT_SWAP);
+ IN_SET(gpt_partition_type_from_uuid(id).designator,
+ PARTITION_HOME,
+ PARTITION_SRV,
+ PARTITION_VAR,
+ PARTITION_TMP,
+ PARTITION_XBOOTLDR,
+ PARTITION_SWAP);
}
/* maximum length of gpt label */
#define GPT_LABEL_MAX 36
+typedef enum PartitionDesignator {
+ PARTITION_ROOT, /* Primary architecture */
+ PARTITION_ROOT_SECONDARY, /* Secondary architecture */
+ PARTITION_ROOT_OTHER, /* Any architecture not covered by the primary or secondary architecture. */
+ PARTITION_USR,
+ PARTITION_USR_SECONDARY,
+ PARTITION_USR_OTHER,
+ PARTITION_HOME,
+ PARTITION_SRV,
+ PARTITION_ESP,
+ PARTITION_XBOOTLDR,
+ PARTITION_SWAP,
+ PARTITION_ROOT_VERITY, /* verity data for the PARTITION_ROOT partition */
+ PARTITION_ROOT_SECONDARY_VERITY, /* verity data for the PARTITION_ROOT_SECONDARY partition */
+ PARTITION_ROOT_OTHER_VERITY,
+ PARTITION_USR_VERITY,
+ PARTITION_USR_SECONDARY_VERITY,
+ PARTITION_USR_OTHER_VERITY,
+ PARTITION_ROOT_VERITY_SIG, /* PKCS#7 signature for root hash for the PARTITION_ROOT partition */
+ PARTITION_ROOT_SECONDARY_VERITY_SIG, /* ditto for the PARTITION_ROOT_SECONDARY partition */
+ PARTITION_ROOT_OTHER_VERITY_SIG,
+ PARTITION_USR_VERITY_SIG,
+ PARTITION_USR_SECONDARY_VERITY_SIG,
+ PARTITION_USR_OTHER_VERITY_SIG,
+ PARTITION_TMP,
+ PARTITION_VAR,
+ PARTITION_USER_HOME,
+ PARTITION_LINUX_GENERIC,
+ _PARTITION_DESIGNATOR_MAX,
+ _PARTITION_DESIGNATOR_INVALID = -EINVAL,
+} PartitionDesignator;
+
+bool partition_designator_is_versioned(PartitionDesignator d);
+
+PartitionDesignator partition_verity_of(PartitionDesignator p);
+PartitionDesignator partition_verity_sig_of(PartitionDesignator p);
+PartitionDesignator partition_root_of_arch(Architecture arch);
+PartitionDesignator partition_usr_of_arch(Architecture arch);
+
+const char* partition_designator_to_string(PartitionDesignator d) _const_;
+PartitionDesignator partition_designator_from_string(const char *name) _pure_;
+
const char *gpt_partition_type_uuid_to_string(sd_id128_t id);
const char *gpt_partition_type_uuid_to_string_harder(
sd_id128_t id,
sd_id128_t uuid;
const char *name;
Architecture arch;
-
- bool is_root:1;
- bool is_root_verity:1;
- bool is_root_verity_sig:1;
- bool is_usr:1;
- bool is_usr_verity:1;
- bool is_usr_verity_sig:1;
+ PartitionDesignator designator;
} GptPartitionType;
extern const GptPartitionType gpt_partition_type_table[];
bool gpt_partition_type_is_usr_verity(sd_id128_t id);
bool gpt_partition_type_is_usr_verity_sig(sd_id128_t id);
+const char *gpt_partition_type_mountpoint_nulstr(sd_id128_t id);
+
bool gpt_partition_type_knows_read_only(sd_id128_t id);
bool gpt_partition_type_knows_growfs(sd_id128_t id);
bool gpt_partition_type_knows_no_auto(sd_id128_t id);
int hwdb_query(const char *modalias, const char *root) {
_cleanup_(sd_hwdb_unrefp) sd_hwdb *hwdb = NULL;
- const char *key, *value, *p;
+ const char *key, *value;
int r;
assert(modalias);
bool hwdb_should_reload(sd_hwdb *hwdb) {
bool found = false;
- const char* p;
struct stat st;
if (!hwdb)
return -ENOMEM;
for (;;) {
- const char *sfx;
bool changed = false;
NULSTR_FOREACH(sfx, suffixes) {
int unit_file_get_list(LookupScope scope, const char *root_dir, Hashmap *h, char **states, char **patterns);
Hashmap* unit_file_list_free(Hashmap *h);
-InstallChangeType install_changes_add(InstallChange **changes, size_t *n_changes, int type, const char *path, const char *source);
+InstallChangeType install_changes_add(InstallChange **changes, size_t *n_changes, InstallChangeType type, const char *path, const char *source);
void install_changes_free(InstallChange *changes, size_t n_changes);
void install_changes_dump(int r, const char *verb, const InstallChange *changes, size_t n_changes, bool quiet);
/* from_string conversion is unreliable because of the overlap between -EPERM and -1 for error. */
const char *install_change_type_to_string(InstallChangeType t) _const_;
-int install_change_type_from_string(const char *s) _pure_;
+InstallChangeType install_change_type_from_string(const char *s) _pure_;
const char *unit_file_preset_mode_to_string(UnitFilePresetMode m) _const_;
UnitFilePresetMode unit_file_preset_mode_from_string(const char *s) _pure_;
return json_variant_new_string(ret, SD_ID128_TO_STRING(id));
}
+int json_variant_new_uuid(JsonVariant **ret, sd_id128_t id) {
+ return json_variant_new_string(ret, SD_ID128_TO_UUID_STRING(id));
+}
+
static void json_variant_set(JsonVariant *a, JsonVariant *b) {
assert(a);
break;
}
- case _JSON_BUILD_ID128: {
+ case _JSON_BUILD_ID128:
+ case _JSON_BUILD_UUID: {
const sd_id128_t *id;
if (!IN_SET(current->expect, EXPECT_TOPLEVEL, EXPECT_OBJECT_VALUE, EXPECT_ARRAY_ELEMENT)) {
assert_se(id = va_arg(ap, sd_id128_t*));
if (current->n_suppress == 0) {
- r = json_variant_new_id128(&add, *id);
+ r = command == _JSON_BUILD_ID128 ?
+ json_variant_new_id128(&add, *id) :
+ json_variant_new_uuid(&add, *id);
if (r < 0)
goto finish;
}
int json_variant_new_object(JsonVariant **ret, JsonVariant **array, size_t n);
int json_variant_new_null(JsonVariant **ret);
int json_variant_new_id128(JsonVariant **ret, sd_id128_t id);
+int json_variant_new_uuid(JsonVariant **ret, sd_id128_t id);
static inline int json_variant_new_string(JsonVariant **ret, const char *s) {
return json_variant_new_stringn(ret, s, SIZE_MAX);
_JSON_BUILD_HEX,
_JSON_BUILD_OCTESCAPE,
_JSON_BUILD_ID128,
+ _JSON_BUILD_UUID,
_JSON_BUILD_BYTE_ARRAY,
_JSON_BUILD_HW_ADDR,
_JSON_BUILD_PAIR_UNSIGNED_NON_ZERO,
#define JSON_BUILD_HEX(p, n) _JSON_BUILD_HEX, (const void*) { p }, (size_t) { n }
#define JSON_BUILD_OCTESCAPE(p, n) _JSON_BUILD_OCTESCAPE, (const void*) { p }, (size_t) { n }
#define JSON_BUILD_ID128(id) _JSON_BUILD_ID128, (const sd_id128_t*) { &(id) }
+#define JSON_BUILD_UUID(id) _JSON_BUILD_UUID, (const sd_id128_t*) { &(id) }
#define JSON_BUILD_BYTE_ARRAY(v, n) _JSON_BUILD_BYTE_ARRAY, (const void*) { v }, (size_t) { n }
#define JSON_BUILD_CONST_STRING(s) _JSON_BUILD_VARIANT, JSON_VARIANT_STRING_CONST(s)
#define JSON_BUILD_IN4_ADDR(v) JSON_BUILD_BYTE_ARRAY((const struct in_addr*) { v }, sizeof(struct in_addr))
#define JSON_BUILD_PAIR_BASE64(name, p, n) JSON_BUILD_PAIR(name, JSON_BUILD_BASE64(p, n))
#define JSON_BUILD_PAIR_HEX(name, p, n) JSON_BUILD_PAIR(name, JSON_BUILD_HEX(p, n))
#define JSON_BUILD_PAIR_ID128(name, id) JSON_BUILD_PAIR(name, JSON_BUILD_ID128(id))
+#define JSON_BUILD_PAIR_UUID(name, id) JSON_BUILD_PAIR(name, JSON_BUILD_UUID(id))
#define JSON_BUILD_PAIR_BYTE_ARRAY(name, v, n) JSON_BUILD_PAIR(name, JSON_BUILD_BYTE_ARRAY(v, n))
#define JSON_BUILD_PAIR_IN4_ADDR(name, v) JSON_BUILD_PAIR(name, JSON_BUILD_IN4_ADDR(v))
#define JSON_BUILD_PAIR_IN6_ADDR(name, v) JSON_BUILD_PAIR(name, JSON_BUILD_IN6_ADDR(v))
if (!keymaps)
return -ENOMEM;
- const char *dir;
NULSTR_FOREACH(dir, KBD_KEYMAP_DIRS) {
r = recurse_dir_at(
AT_FDCWD,
if (!keymap_is_valid(name))
return -EINVAL;
- const char *dir;
NULSTR_FOREACH(dir, KBD_KEYMAP_DIRS) {
r = recurse_dir_at(
AT_FDCWD,
return 0;
}
+static int fido2_assert_set_basic_properties(
+ fido_assert_t *a,
+ const char *rp_id,
+ const void *cid,
+ size_t cid_size) {
+ int r;
+
+ assert(a);
+ assert(rp_id);
+ assert(cid);
+ assert(cid_size > 0);
+
+ r = sym_fido_assert_set_rp(a, rp_id);
+ if (r != FIDO_OK)
+ return log_error_errno(SYNTHETIC_ERRNO(EIO),
+ "Failed to set FIDO2 assertion ID: %s", sym_fido_strerr(r));
+
+ r = sym_fido_assert_set_clientdata_hash(a, (const unsigned char[32]) {}, 32);
+ if (r != FIDO_OK)
+ return log_error_errno(SYNTHETIC_ERRNO(EIO),
+ "Failed to set FIDO2 assertion client data hash: %s", sym_fido_strerr(r));
+
+ r = sym_fido_assert_allow_cred(a, cid, cid_size);
+ if (r != FIDO_OK)
+ return log_error_errno(SYNTHETIC_ERRNO(EIO),
+ "Failed to add FIDO2 assertion credential ID: %s", sym_fido_strerr(r));
+
+ return 0;
+}
+
+static int fido2_common_assert_error_handle(int r) {
+ switch (r) {
+ case FIDO_OK:
+ return 0;
+ case FIDO_ERR_NO_CREDENTIALS:
+ return log_error_errno(SYNTHETIC_ERRNO(EBADSLT),
+ "Wrong security token; needed credentials not present on token.");
+ case FIDO_ERR_PIN_REQUIRED:
+ return log_error_errno(SYNTHETIC_ERRNO(ENOANO),
+ "Security token requires PIN.");
+ case FIDO_ERR_PIN_AUTH_BLOCKED:
+ return log_error_errno(SYNTHETIC_ERRNO(EOWNERDEAD),
+ "PIN of security token is blocked, please remove/reinsert token.");
+#ifdef FIDO_ERR_UV_BLOCKED
+ case FIDO_ERR_UV_BLOCKED:
+ return log_error_errno(SYNTHETIC_ERRNO(EOWNERDEAD),
+ "Verification of security token is blocked, please remove/reinsert token.");
+#endif
+ case FIDO_ERR_PIN_INVALID:
+ return log_error_errno(SYNTHETIC_ERRNO(ENOLCK),
+ "PIN of security token incorrect.");
+ case FIDO_ERR_UP_REQUIRED:
+ return log_error_errno(SYNTHETIC_ERRNO(EMEDIUMTYPE),
+ "User presence required.");
+ case FIDO_ERR_ACTION_TIMEOUT:
+ return log_error_errno(SYNTHETIC_ERRNO(ENOSTR),
+ "Token action timeout. (User didn't interact with token quickly enough.)");
+ default:
+ return log_error_errno(SYNTHETIC_ERRNO(EIO),
+ "Failed to ask token for assertion: %s", sym_fido_strerr(r));
+ }
+}
+
+static int fido2_is_cred_in_specific_token(
+ const char *path,
+ const char *rp_id,
+ const void *cid,
+ size_t cid_size,
+ char **pins,
+ Fido2EnrollFlags flags) {
+
+ assert(path);
+ assert(rp_id);
+ assert(cid);
+ assert(cid_size);
+
+ _cleanup_(fido_dev_free_wrapper) fido_dev_t *d = NULL;
+ _cleanup_(fido_assert_free_wrapper) fido_assert_t *a = NULL;
+ bool has_up = false, has_uv = false;
+ int r;
+
+ d = sym_fido_dev_new();
+ if (!d)
+ return log_oom();
+
+ r = sym_fido_dev_open(d, path);
+ if (r != FIDO_OK)
+ return log_error_errno(SYNTHETIC_ERRNO(EIO),
+ "Failed to open FIDO2 device %s: %s", path, sym_fido_strerr(r));
+
+ r = verify_features(d, path, LOG_ERR, NULL, NULL, &has_up, &has_uv);
+ if (r < 0)
+ return r;
+
+ a = sym_fido_assert_new();
+ if (!a)
+ return log_oom();
+
+ r = fido2_assert_set_basic_properties(a, rp_id, cid, cid_size);
+ if (r < 0)
+ return r;
+
+ /* According to CTAP 2.1 specification, to do pre-flight we need to set up option to false
+ * with optionally pinUvAuthParam in assertion[1]. But for authenticator that doesn't support
+ * user presence, once up option is present, the authenticator may return CTAP2_ERR_UNSUPPORTED_OPTION[2].
+ * So we simplely omit the option in that case.
+ * Reference:
+ * 1: https://fidoalliance.org/specs/fido-v2.1-ps-20210615/fido-client-to-authenticator-protocol-v2.1-ps-20210615.html#pre-flight
+ * 2: https://fidoalliance.org/specs/fido-v2.0-ps-20190130/fido-client-to-authenticator-protocol-v2.0-ps-20190130.html#authenticatorGetAssertion (in step 5)
+ */
+ if (has_up)
+ r = sym_fido_assert_set_up(a, FIDO_OPT_FALSE);
+ else
+ r = sym_fido_assert_set_up(a, FIDO_OPT_OMIT);
+ if (r != FIDO_OK)
+ return log_error_errno(SYNTHETIC_ERRNO(EIO),
+ "Failed to set assertion user presence: %s", sym_fido_strerr(r));
+
+
+ /* According to CTAP 2.1 specification, if the credential is marked as userVerificationRequired,
+ * we may pass pinUvAuthParam parameter or set uv option to true in order to check whether the
+ * credential is in the token. Here we choose to use PIN (pinUvAuthParam) to achieve this.
+ * If we don't do that, the authenticator will remove the credential from the applicable
+ * credentials list, hence CTAP2_ERR_NO_CREDENTIALS error will be returned.
+ * Please see
+ * https://fidoalliance.org/specs/fido-v2.1-ps-20210615/fido-client-to-authenticator-protocol-v2.1-ps-20210615.html#sctn-getAssert-authnr-alg
+ * step 7.4 for detailed information.
+ */
+ if (FLAGS_SET(flags, FIDO2ENROLL_UV) && has_uv) {
+ if (strv_isempty(pins))
+ r = FIDO_ERR_PIN_REQUIRED;
+ else
+ STRV_FOREACH(i, pins) {
+ r = sym_fido_dev_get_assert(d, a, *i);
+ if (r != FIDO_ERR_PIN_INVALID)
+ break;
+ }
+
+ } else
+ r = sym_fido_dev_get_assert(d, a, NULL);
+
+ switch (r) {
+ case FIDO_OK:
+ return true;
+ case FIDO_ERR_NO_CREDENTIALS:
+ return false;
+ default:
+ return fido2_common_assert_error_handle(r);
+ }
+}
+
static int fido2_use_hmac_hash_specific_token(
const char *path,
const char *rp_id,
return log_error_errno(SYNTHETIC_ERRNO(EIO),
"Failed to set salt on FIDO2 assertion: %s", sym_fido_strerr(r));
- r = sym_fido_assert_set_rp(a, rp_id);
- if (r != FIDO_OK)
- return log_error_errno(SYNTHETIC_ERRNO(EIO),
- "Failed to set FIDO2 assertion ID: %s", sym_fido_strerr(r));
-
- r = sym_fido_assert_set_clientdata_hash(a, (const unsigned char[32]) {}, 32);
- if (r != FIDO_OK)
- return log_error_errno(SYNTHETIC_ERRNO(EIO),
- "Failed to set FIDO2 assertion client data hash: %s", sym_fido_strerr(r));
-
- r = sym_fido_assert_allow_cred(a, cid, cid_size);
- if (r != FIDO_OK)
- return log_error_errno(SYNTHETIC_ERRNO(EIO),
- "Failed to add FIDO2 assertion credential ID: %s", sym_fido_strerr(r));
+ r = fido2_assert_set_basic_properties(a, rp_id, cid, cid_size);
+ if (r < 0)
+ return r;
log_info("Asking FIDO2 token for authentication.");
required |= FIDO2ENROLL_PIN;
}
- switch (r) {
- case FIDO_OK:
- break;
- case FIDO_ERR_NO_CREDENTIALS:
- return log_error_errno(SYNTHETIC_ERRNO(EBADSLT),
- "Wrong security token; needed credentials not present on token.");
- case FIDO_ERR_PIN_REQUIRED:
- return log_error_errno(SYNTHETIC_ERRNO(ENOANO),
- "Security token requires PIN.");
- case FIDO_ERR_PIN_AUTH_BLOCKED:
- return log_error_errno(SYNTHETIC_ERRNO(EOWNERDEAD),
- "PIN of security token is blocked, please remove/reinsert token.");
-#ifdef FIDO_ERR_UV_BLOCKED
- case FIDO_ERR_UV_BLOCKED:
- return log_error_errno(SYNTHETIC_ERRNO(EOWNERDEAD),
- "Verification of security token is blocked, please remove/reinsert token.");
-#endif
- case FIDO_ERR_PIN_INVALID:
- return log_error_errno(SYNTHETIC_ERRNO(ENOLCK),
- "PIN of security token incorrect.");
- case FIDO_ERR_UP_REQUIRED:
- return log_error_errno(SYNTHETIC_ERRNO(EMEDIUMTYPE),
- "User presence required.");
- case FIDO_ERR_ACTION_TIMEOUT:
- return log_error_errno(SYNTHETIC_ERRNO(ENOSTR),
- "Token action timeout. (User didn't interact with token quickly enough.)");
- default:
- return log_error_errno(SYNTHETIC_ERRNO(EIO),
- "Failed to ask token for assertion: %s", sym_fido_strerr(r));
- }
+ r = fido2_common_assert_error_handle(r);
+ if (r < 0)
+ return r;
hmac = sym_fido_assert_hmac_secret_ptr(a, 0);
if (!hmac)
goto finish;
}
+ r = fido2_is_cred_in_specific_token(path, rp_id, cid, cid_size, pins, required);
+ /* We handle PIN related errors here since we have used PIN in the check.
+ * If PIN error happens, we can avoid pin retries decreased the second time. */
+ if (IN_SET(r, -ENOANO, /* → pin required */
+ -ENOLCK, /* → pin incorrect */
+ -EOWNERDEAD)) { /* → uv blocked */
+ /* If it's not the last device, try next one */
+ if (i < found - 1)
+ continue;
+
+ /* -EOWNERDEAD is returned when uv is blocked. Map it to EAGAIN so users can reinsert
+ * the token and try again. */
+ if (r == -EOWNERDEAD)
+ r = -EAGAIN;
+ goto finish;
+ } else if (r == -ENODEV) /* not a FIDO2 device or lacking HMAC-SECRET extension */
+ continue;
+ else if (r < 0)
+ log_error_errno(r, "Failed to determine whether the credential is in the token, trying anyway: %m");
+ else if (r == 0) {
+ log_debug("The credential is not in the token %s, skipping.", path);
+ continue;
+ }
+
r = fido2_use_hmac_hash_specific_token(path, rp_id, salt, salt_size, cid, cid_size, pins, required, ret_hmac, ret_hmac_size);
if (!IN_SET(r,
-EBADSLT, /* device doesn't understand our credential hash */
return log_error_errno(SYNTHETIC_ERRNO(EIO),
"Failed to set salt on FIDO2 assertion: %s", sym_fido_strerr(r));
- r = sym_fido_assert_set_rp(a, rp_id);
- if (r != FIDO_OK)
- return log_error_errno(SYNTHETIC_ERRNO(EIO),
- "Failed to set FIDO2 assertion ID: %s", sym_fido_strerr(r));
-
- r = sym_fido_assert_set_clientdata_hash(a, (const unsigned char[32]) {}, 32);
- if (r != FIDO_OK)
- return log_error_errno(SYNTHETIC_ERRNO(EIO),
- "Failed to set FIDO2 assertion client data hash: %s", sym_fido_strerr(r));
-
- r = sym_fido_assert_allow_cred(a, cid, cid_size);
- if (r != FIDO_OK)
- return log_error_errno(SYNTHETIC_ERRNO(EIO),
- "Failed to add FIDO2 assertion credential ID: %s", sym_fido_strerr(r));
+ r = fido2_assert_set_basic_properties(a, rp_id, cid, cid_size);
+ if (r < 0)
+ return r;
log_info("Generating secret key on FIDO2 security token.");
#include <errno.h>
#include "module-util.h"
+#include "proc-cmdline.h"
+#include "strv.h"
+
+static int denylist_modules(const char *p, char ***denylist) {
+ _cleanup_strv_free_ char **k = NULL;
+
+ assert(p);
+ assert(denylist);
+
+ k = strv_split(p, ",");
+ if (!k)
+ return -ENOMEM;
+
+ if (strv_extend_strv(denylist, k, true) < 0)
+ return -ENOMEM;
+
+ return 0;
+}
+
+static int parse_proc_cmdline_item(const char *key, const char *value, void *data) {
+ int r;
+
+ if (proc_cmdline_key_streq(key, "module_blacklist")) {
+
+ if (proc_cmdline_value_missing(key, value))
+ return 0;
+
+ r = denylist_modules(value, data);
+ if (r < 0)
+ return r;
+ }
+
+ return 0;
+}
int module_load_and_warn(struct kmod_ctx *ctx, const char *module, bool verbose) {
const int probe_flags = KMOD_PROBE_APPLY_BLACKLIST;
struct kmod_list *itr;
_cleanup_(kmod_module_unref_listp) struct kmod_list *modlist = NULL;
+ _cleanup_strv_free_ char **denylist = NULL;
+ bool denylist_parsed = false;
int r;
/* verbose==true means we should log at non-debug level if we
"Inserted module '%s'", kmod_module_get_name(mod));
else if (err == KMOD_PROBE_APPLY_BLACKLIST)
log_full(verbose ? LOG_INFO : LOG_DEBUG,
- "Module '%s' is deny-listed", kmod_module_get_name(mod));
+ "Module '%s' is deny-listed (by kmod)", kmod_module_get_name(mod));
else {
assert(err < 0);
+ if (err == -EPERM) {
+ if (!denylist_parsed) {
+ r = proc_cmdline_parse(parse_proc_cmdline_item, &denylist, 0);
+ if (r < 0)
+ log_full_errno(!verbose ? LOG_DEBUG : LOG_WARNING,
+ r,
+ "Failed to parse kernel command line, ignoring: %m");
+
+ denylist_parsed = true;
+ }
+ if (strv_contains(denylist, kmod_module_get_name(mod))) {
+ log_full(verbose ? LOG_INFO : LOG_DEBUG,
+ "Module '%s' is deny-listed (by kernel)", kmod_module_get_name(mod));
+ continue;
+ }
+ }
+
log_full_errno(!verbose ? LOG_DEBUG :
err == -ENODEV ? LOG_NOTICE :
err == -ENOENT ? LOG_WARNING :
typedef enum DnssecMode DnssecMode;
typedef enum DnsOverTlsMode DnsOverTlsMode;
+/* Do not change the order, see link_get_llmnr_support() or link_get_mdns_support(). */
enum ResolveSupport {
RESOLVE_SUPPORT_NO,
- RESOLVE_SUPPORT_YES,
RESOLVE_SUPPORT_RESOLVE,
+ RESOLVE_SUPPORT_YES,
_RESOLVE_SUPPORT_MAX,
_RESOLVE_SUPPORT_INVALID = -EINVAL,
};
#include "alloc-util.h"
#include "errno-util.h"
-#include "fd-util.h"
typedef enum RemoveFlags {
REMOVE_ONLY_DIRECTORIES = 1 << 0, /* Only remove empty directories, no files */
return mfree(p);
}
DEFINE_TRIVIAL_CLEANUP_FUNC(char*, rm_rf_subvolume_and_free);
-
-static inline int rm_rf_physical_and_close(int fd) {
- _cleanup_free_ char *p = NULL;
-
- if (fd < 0)
- return -1;
-
- if (fd_get_path(fd, &p) < 0)
- return safe_close(fd);
-
- safe_close(fd);
- (void) rm_rf(p, REMOVE_ROOT|REMOVE_PHYSICAL|REMOVE_MISSING_OK|REMOVE_CHMOD);
-
- return -1;
-}
-DEFINE_TRIVIAL_CLEANUP_FUNC(int, rm_rf_physical_and_close);
bool log_missing,
char ***added) {
- const char *sys;
int r;
/* Any syscalls that are handled are added to the *added strv. It needs to be initialized. */
if (name[0] == '@') {
const SyscallFilterSet *set;
- const char *i;
set = syscall_filter_set_find(name);
if (!set) {
}
int seccomp_filter_set_add(Hashmap *filter, bool add, const SyscallFilterSet *set) {
- const char *i;
int r;
assert(set);
SECCOMP_FOREACH_LOCAL_ARCH(arch) {
_cleanup_(seccomp_releasep) scmp_filter_ctx seccomp = NULL;
- const char *c;
r = seccomp_init_for_arch(&seccomp, arch, SCMP_ACT_ALLOW);
if (r < 0)
return found_sink || !found_source;
}
+static bool battery_is_discharging(sd_device *d) {
+ const char *val;
+ int r;
+
+ assert(d);
+
+ r = sd_device_get_sysattr_value(d, "scope", &val);
+ if (r < 0) {
+ if (r != -ENOENT)
+ log_device_debug_errno(d, r, "Failed to read 'scope' sysfs attribute, ignoring: %m");
+ } else if (streq(val, "Device")) {
+ log_device_debug(d, "The power supply is a device battery, ignoring device.");
+ return false;
+ }
+
+ r = device_get_sysattr_bool(d, "present");
+ if (r < 0)
+ log_device_debug_errno(d, r, "Failed to read 'present' sysfs attribute, assuming the battery is present: %m");
+ else if (r == 0) {
+ log_device_debug(d, "The battery is not present, ignoring the power supply.");
+ return false;
+ }
+
+ /* Possible values: "Unknown", "Charging", "Discharging", "Not charging", "Full" */
+ r = sd_device_get_sysattr_value(d, "status", &val);
+ if (r < 0) {
+ log_device_debug_errno(d, r, "Failed to read 'status' sysfs attribute, assuming the battery is discharging: %m");
+ return true;
+ }
+ if (!streq(val, "Discharging")) {
+ log_device_debug(d, "The battery status is '%s', assuming the battery is not used as a power source of this machine.", val);
+ return false;
+ }
+
+ return true;
+}
+
int on_ac_power(void) {
_cleanup_(sd_device_enumerator_unrefp) sd_device_enumerator *e = NULL;
- bool found_ac_online = false, found_battery = false;
+ bool found_ac_online = false, found_discharging_battery = false;
sd_device *d;
int r;
}
if (streq(val, "Battery")) {
- r = sd_device_get_sysattr_value(d, "scope", &val);
- if (r < 0) {
- if (r != -ENOENT)
- log_device_debug_errno(d, r, "Failed to read 'scope' sysfs attribute, ignoring: %m");
- } else if (streq(val, "Device")) {
- log_device_debug(d, "The power supply is a device battery, ignoring device.");
- continue;
+ if (battery_is_discharging(d)) {
+ found_discharging_battery = true;
+ log_device_debug(d, "The power supply is a battery and currently discharging.");
}
-
- found_battery = true;
- log_device_debug(d, "The power supply is battery.");
continue;
}
if (found_ac_online) {
log_debug("Found at least one online non-battery power supply, system is running on AC.");
return true;
- } else if (found_battery) {
- log_debug("Found battery and no online power sources, assuming system is running from battery.");
+ } else if (found_discharging_battery) {
+ log_debug("Found at least one discharging battery and no online power sources, assuming system is running from battery.");
return false;
} else {
- log_debug("No power supply reported online and no battery, assuming system is running on AC.");
+ log_debug("No power supply reported online and no discharging battery found, assuming system is running on AC.");
return true;
}
}
#include "env-util.h"
#include "fd-util.h"
#include "fuzz.h"
+#include "nulstr-util.h"
#include "selinux-util.h"
#include "static-destruct.h"
#include "stdio-util.h"
if (!arg_states && !arg_types) {
if (show_mode == SYSTEMCTL_SHOW_PROPERTIES)
- r = show_one(bus, "/org/freedesktop/systemd1", NULL, show_mode, &new_line, &ellipsized);
- else
- r = show_system_status(bus);
+ /* systemctl show --all → show properties of the manager */
+ return show_one(bus, "/org/freedesktop/systemd1", NULL, show_mode, &new_line, &ellipsized);
+
+ r = show_system_status(bus);
if (r < 0)
return r;
[files('test-strv.c')],
+ [files('test-nulstr-util.c')],
+
[files('test-path-util.c')],
[files('test-rm-rf.c')],
else
arg_start = getpid_cached();
- const char *i;
NULSTR_FOREACH(i, "zeros\0simple\0random\0") {
#if HAVE_XZ
test_compress_decompress("XZ", i, compress_blob_xz, decompress_blob_xz);
TEST(condition_test_virtualization) {
Condition *condition;
- const char *virt;
int r;
condition = condition_new(CONDITION_VIRTUALIZATION, "garbage oifdsjfoidsjoj", false, false);
/* The file exists- now overwrite original contents, and test the COPY_REPLACE flag. */
- assert_se(copy_tree(src, dst, UID_INVALID, GID_INVALID, COPY_REFLINK) == -EEXIST);
+ assert_se(copy_tree(src, dst, UID_INVALID, GID_INVALID, COPY_REFLINK, NULL) == -EEXIST);
assert_se(read_file_at_and_streq(AT_FDCWD, dst, "foo foo foo\n"));
- assert_se(copy_tree(src, dst, UID_INVALID, GID_INVALID, COPY_REFLINK|COPY_REPLACE) == 0);
+ assert_se(copy_tree(src, dst, UID_INVALID, GID_INVALID, COPY_REFLINK|COPY_REPLACE, NULL) == 0);
assert_se(read_file_at_and_streq(AT_FDCWD, dst, "bar bar\n"));
}
TEST(copy_tree_replace_dirs) {
- _cleanup_(rm_rf_physical_and_closep) int src = -1, dst = -1;
+ _cleanup_(rm_rf_physical_and_freep) char *srcp = NULL, *dstp = NULL;
+ _cleanup_close_ int src = -1, dst = -1;
/* Create the random source/destination directories */
- assert_se((src = mkdtemp_open(NULL, 0, NULL)) >= 0);
- assert_se((dst = mkdtemp_open(NULL, 0, NULL)) >= 0);
+ assert_se((src = mkdtemp_open(NULL, 0, &srcp)) >= 0);
+ assert_se((dst = mkdtemp_open(NULL, 0, &dstp)) >= 0);
/* Populate some data to differentiate the files. */
assert_se(write_string_file_at(src, "foo", "src file 1", WRITE_STRING_FILE_CREATE) >= 0);
assert_se(write_string_file_at(dst, "bar", "dest file 2", WRITE_STRING_FILE_CREATE) == 0);
/* Copying without COPY_REPLACE should fail because the destination file already exists. */
- assert_se(copy_tree_at(src, ".", dst, ".", UID_INVALID, GID_INVALID, COPY_REFLINK) == -EEXIST);
+ assert_se(copy_tree_at(src, ".", dst, ".", UID_INVALID, GID_INVALID, COPY_REFLINK, NULL) == -EEXIST);
assert_se(read_file_at_and_streq(src, "foo", "src file 1\n"));
assert_se(read_file_at_and_streq(src, "bar", "src file 2\n"));
assert_se(read_file_at_and_streq(dst, "foo", "dest file 1\n"));
assert_se(read_file_at_and_streq(dst, "bar", "dest file 2\n"));
- assert_se(copy_tree_at(src, ".", dst, ".", UID_INVALID, GID_INVALID, COPY_REFLINK|COPY_REPLACE|COPY_MERGE) == 0);
+ assert_se(copy_tree_at(src, ".", dst, ".", UID_INVALID, GID_INVALID, COPY_REFLINK|COPY_REPLACE|COPY_MERGE, NULL) == 0);
assert_se(read_file_at_and_streq(src, "foo", "src file 1\n"));
assert_se(read_file_at_and_streq(src, "bar", "src file 2\n"));
}
TEST(copy_tree) {
+ _cleanup_set_free_ Set *denylist = NULL;
+ _cleanup_free_ char *cp = NULL;
char original_dir[] = "/tmp/test-copy_tree/";
char copy_dir[] = "/tmp/test-copy_tree-copy/";
char **files = STRV_MAKE("file", "dir1/file", "dir1/dir2/file", "dir1/dir2/dir3/dir4/dir5/file");
"link2", "dir1/file");
char **hardlinks = STRV_MAKE("hlink", "file",
"hlink2", "dir1/file");
- const char *unixsockp;
+ const char *unixsockp, *ignorep;
struct stat st;
int xattr_worked = -1; /* xattr support is optional in temporary directories, hence use it if we can,
* but don't fail if we can't */
unixsockp = strjoina(original_dir, "unixsock");
assert_se(mknod(unixsockp, S_IFSOCK|0644, 0) >= 0);
- assert_se(copy_tree(original_dir, copy_dir, UID_INVALID, GID_INVALID, COPY_REFLINK|COPY_MERGE|COPY_HARDLINKS) == 0);
+ ignorep = strjoina(original_dir, "ignore/file");
+ assert_se(write_string_file(ignorep, "ignore", WRITE_STRING_FILE_CREATE|WRITE_STRING_FILE_MKDIR_0755) == 0);
+ assert_se(RET_NERRNO(stat(ignorep, &st)) >= 0);
+ assert_se(cp = memdup(&st, sizeof(st)));
+ assert_se(set_ensure_put(&denylist, &inode_hash_ops, cp) >= 0);
+ TAKE_PTR(cp);
+
+ assert_se(copy_tree(original_dir, copy_dir, UID_INVALID, GID_INVALID, COPY_REFLINK|COPY_MERGE|COPY_HARDLINKS, denylist) == 0);
STRV_FOREACH(p, files) {
_cleanup_free_ char *buf, *f, *c = NULL;
assert_se(stat(unixsockp, &st) >= 0);
assert_se(S_ISSOCK(st.st_mode));
- assert_se(copy_tree(original_dir, copy_dir, UID_INVALID, GID_INVALID, COPY_REFLINK) < 0);
- assert_se(copy_tree("/tmp/inexistent/foo/bar/fsdoi", copy_dir, UID_INVALID, GID_INVALID, COPY_REFLINK) < 0);
+ assert_se(copy_tree(original_dir, copy_dir, UID_INVALID, GID_INVALID, COPY_REFLINK, denylist) < 0);
+ assert_se(copy_tree("/tmp/inexistent/foo/bar/fsdoi", copy_dir, UID_INVALID, GID_INVALID, COPY_REFLINK, denylist) < 0);
+
+ ignorep = strjoina(copy_dir, "ignore/file");
+ assert_se(RET_NERRNO(access(ignorep, F_OK)) == -ENOENT);
(void) rm_rf(copy_dir, REMOVE_ROOT|REMOVE_PHYSICAL);
(void) rm_rf(original_dir, REMOVE_ROOT|REMOVE_PHYSICAL);
"5min 5min \n"));
}
+TEST(vertical) {
+ _cleanup_(table_unrefp) Table *t = NULL;
+ _cleanup_free_ char *formatted = NULL;
+
+ assert_se(t = table_new_vertical());
+
+ assert_se(table_add_many(t,
+ TABLE_FIELD, "pfft aa", TABLE_STRING, "foo",
+ TABLE_FIELD, "uuu o", TABLE_SIZE, UINT64_C(1024),
+ TABLE_FIELD, "lllllllllllo", TABLE_STRING, "jjjjjjjjjjjjjjjjj") >= 0);
+
+ assert_se(table_set_json_field_name(t, 1, "dimpfelmoser") >= 0);
+
+ assert_se(table_format(t, &formatted) >= 0);
+
+ assert_se(streq(formatted,
+ " pfft aa: foo\n"
+ " uuu o: 1.0K\n"
+ "lllllllllllo: jjjjjjjjjjjjjjjjj\n"));
+
+ _cleanup_(json_variant_unrefp) JsonVariant *a = NULL, *b = NULL;
+ assert_se(table_to_json(t, &a) >= 0);
+
+ assert_se(json_build(&b, JSON_BUILD_OBJECT(
+ JSON_BUILD_PAIR("pfft_aa", JSON_BUILD_STRING("foo")),
+ JSON_BUILD_PAIR("dimpfelmoser", JSON_BUILD_UNSIGNED(1024)),
+ JSON_BUILD_PAIR("lllllllllllo", JSON_BUILD_STRING("jjjjjjjjjjjjjjjjj")))) >= 0);
+
+ assert_se(json_variant_equal(a, b));
+}
+
static int intro(void) {
assert_se(setenv("SYSTEMD_COLORS", "0", 1) >= 0);
assert_se(setenv("COLUMNS", "40", 1) >= 0);
assert_se(path_equal(path_startswith(result, p), "usr"));
result = mfree(result);
+ /* Test CHASE_PROHIBIT_SYMLINKS */
+
+ assert_se(chase_symlinks("top/dot", temp, CHASE_PREFIX_ROOT|CHASE_PROHIBIT_SYMLINKS, NULL, NULL) == -EREMCHG);
+ assert_se(chase_symlinks("top/dot", temp, CHASE_PREFIX_ROOT|CHASE_PROHIBIT_SYMLINKS|CHASE_WARN, NULL, NULL) == -EREMCHG);
+ assert_se(chase_symlinks("top/dotdot", temp, CHASE_PREFIX_ROOT|CHASE_PROHIBIT_SYMLINKS, NULL, NULL) == -EREMCHG);
+ assert_se(chase_symlinks("top/dotdot", temp, CHASE_PREFIX_ROOT|CHASE_PROHIBIT_SYMLINKS|CHASE_WARN, NULL, NULL) == -EREMCHG);
+ assert_se(chase_symlinks("top/dot/dot", temp, CHASE_PREFIX_ROOT|CHASE_PROHIBIT_SYMLINKS, NULL, NULL) == -EREMCHG);
+ assert_se(chase_symlinks("top/dot/dot", temp, CHASE_PREFIX_ROOT|CHASE_PROHIBIT_SYMLINKS|CHASE_WARN, NULL, NULL) == -EREMCHG);
+
cleanup:
assert_se(rm_rf(temp, REMOVE_ROOT|REMOVE_PHYSICAL) >= 0);
}
m = hashmap_new(&string_hash_ops);
- NULSTR_FOREACH(key, key_table)
- hashmap_put(m, key, (void*) (const char*) "my dummy val");
+ NULSTR_FOREACH(k, key_table)
+ hashmap_put(m, k, (void*) (const char*) "my dummy val");
HASHMAP_FOREACH_KEY(s, key, m) {
assert_se(s);
--- /dev/null
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+
+#include "alloc-util.h"
+#include "nulstr-util.h"
+#include "strv.h"
+#include "tests.h"
+
+TEST(strv_split_nulstr) {
+ _cleanup_strv_free_ char **l = NULL;
+ const char nulstr[] = "str0\0str1\0str2\0str3\0";
+
+ l = strv_split_nulstr(nulstr);
+ assert_se(l);
+
+ assert_se(streq(l[0], "str0"));
+ assert_se(streq(l[1], "str1"));
+ assert_se(streq(l[2], "str2"));
+ assert_se(streq(l[3], "str3"));
+}
+
+TEST(strv_parse_nulstr) {
+ _cleanup_strv_free_ char **l = NULL;
+ const char nulstr[] = "hoge\0hoge2\0hoge3\0\0hoge5\0\0xxx";
+
+ l = strv_parse_nulstr(nulstr, sizeof(nulstr)-1);
+ assert_se(l);
+ puts("Parse nulstr:");
+ strv_print(l);
+
+ assert_se(streq(l[0], "hoge"));
+ assert_se(streq(l[1], "hoge2"));
+ assert_se(streq(l[2], "hoge3"));
+ assert_se(streq(l[3], ""));
+ assert_se(streq(l[4], "hoge5"));
+ assert_se(streq(l[5], ""));
+ assert_se(streq(l[6], "xxx"));
+ strv_free(l);
+
+ l = strv_parse_nulstr((const char[0]) {}, 0);
+ assert_se(l);
+ assert_se(strv_isempty(l));
+ strv_free(l);
+
+ l = strv_parse_nulstr((const char[1]) { 0 }, 1);
+ assert_se(l);
+ assert_se(strv_equal(l, STRV_MAKE("")));
+ strv_free(l);
+
+ l = strv_parse_nulstr((const char[1]) { 'x' }, 1);
+ assert_se(l);
+ assert_se(strv_equal(l, STRV_MAKE("x")));
+ strv_free(l);
+
+ l = strv_parse_nulstr((const char[2]) { 0, 0 }, 2);
+ assert_se(l);
+ assert_se(strv_equal(l, STRV_MAKE("", "")));
+ strv_free(l);
+
+ l = strv_parse_nulstr((const char[2]) { 'x', 0 }, 2);
+ assert_se(l);
+ assert_se(strv_equal(l, STRV_MAKE("x")));
+ strv_free(l);
+
+ l = strv_parse_nulstr((const char[3]) { 0, 0, 0 }, 3);
+ assert_se(l);
+ assert_se(strv_equal(l, STRV_MAKE("", "", "")));
+ strv_free(l);
+
+ l = strv_parse_nulstr((const char[3]) { 'x', 0, 0 }, 3);
+ assert_se(l);
+ assert_se(strv_equal(l, STRV_MAKE("x", "")));
+ strv_free(l);
+
+ l = strv_parse_nulstr((const char[3]) { 0, 'x', 0 }, 3);
+ assert_se(l);
+ assert_se(strv_equal(l, STRV_MAKE("", "x")));
+ strv_free(l);
+
+ l = strv_parse_nulstr((const char[3]) { 0, 0, 'x' }, 3);
+ assert_se(l);
+ assert_se(strv_equal(l, STRV_MAKE("", "", "x")));
+ strv_free(l);
+
+ l = strv_parse_nulstr((const char[3]) { 'x', 'x', 0 }, 3);
+ assert_se(l);
+ assert_se(strv_equal(l, STRV_MAKE("xx")));
+ strv_free(l);
+
+ l = strv_parse_nulstr((const char[3]) { 0, 'x', 'x' }, 3);
+ assert_se(l);
+ assert_se(strv_equal(l, STRV_MAKE("", "xx")));
+ strv_free(l);
+
+ l = strv_parse_nulstr((const char[3]) { 'x', 0, 'x' }, 3);
+ assert_se(l);
+ assert_se(strv_equal(l, STRV_MAKE("x", "x")));
+ strv_free(l);
+
+ l = strv_parse_nulstr((const char[3]) { 'x', 'x', 'x' }, 3);
+ assert_se(l);
+ assert_se(strv_equal(l, STRV_MAKE("xxx")));
+}
+
+static void test_strv_make_nulstr_one(char **l) {
+ _cleanup_free_ char *b = NULL, *c = NULL;
+ _cleanup_strv_free_ char **q = NULL;
+ size_t n, m;
+ unsigned i = 0;
+
+ log_info("/* %s */", __func__);
+
+ assert_se(strv_make_nulstr(l, &b, &n) >= 0);
+ assert_se(q = strv_parse_nulstr(b, n));
+ assert_se(strv_equal(l, q));
+
+ assert_se(strv_make_nulstr(q, &c, &m) >= 0);
+ assert_se(memcmp_nn(b, n, c, m) == 0);
+
+ NULSTR_FOREACH(s, b)
+ assert_se(streq(s, l[i++]));
+ assert_se(i == strv_length(l));
+}
+
+TEST(strv_make_nulstr) {
+ test_strv_make_nulstr_one(NULL);
+ test_strv_make_nulstr_one(STRV_MAKE(NULL));
+ test_strv_make_nulstr_one(STRV_MAKE("foo"));
+ test_strv_make_nulstr_one(STRV_MAKE("foo", "bar"));
+ test_strv_make_nulstr_one(STRV_MAKE("foo", "bar", "quuux"));
+}
+
+static void test_strv_make_nulstr_binary_one(char **l, const char *b, size_t n) {
+ _cleanup_strv_free_ char **z = NULL;
+ _cleanup_free_ char *a = NULL;
+ size_t m;
+
+ assert_se(strv_make_nulstr(l, &a, &m) >= 0);
+ assert_se(memcmp_nn(a, m, b, n) == 0);
+ assert_se(z = strv_parse_nulstr(a, m));
+ assert_se(strv_equal(l, z));
+}
+
+TEST(strv_make_nulstr_binary) {
+ test_strv_make_nulstr_binary_one(NULL, (const char[0]) {}, 0);
+ test_strv_make_nulstr_binary_one(STRV_MAKE(NULL), (const char[0]) {}, 0);
+ test_strv_make_nulstr_binary_one(STRV_MAKE(""), (const char[1]) { 0 }, 1);
+ test_strv_make_nulstr_binary_one(STRV_MAKE("", ""), (const char[2]) { 0, 0 }, 2);
+ test_strv_make_nulstr_binary_one(STRV_MAKE("x", ""), (const char[3]) { 'x', 0, 0 }, 3);
+ test_strv_make_nulstr_binary_one(STRV_MAKE("", "x"), (const char[3]) { 0, 'x', 0 }, 3);
+ test_strv_make_nulstr_binary_one(STRV_MAKE("", "", ""), (const char[3]) { 0, 0, 0 }, 3);
+ test_strv_make_nulstr_binary_one(STRV_MAKE("x", "", ""), (const char[4]) { 'x', 0, 0, 0 }, 4);
+ test_strv_make_nulstr_binary_one(STRV_MAKE("", "x", ""), (const char[4]) { 0, 'x', 0, 0 }, 4);
+ test_strv_make_nulstr_binary_one(STRV_MAKE("", "", "x"), (const char[4]) { 0, 0, 'x', 0 }, 4);
+ test_strv_make_nulstr_binary_one(STRV_MAKE("x", "x", ""), (const char[5]) { 'x', 0, 'x', 0, 0 }, 5);
+ test_strv_make_nulstr_binary_one(STRV_MAKE("", "x", "x"), (const char[5]) { 0, 'x', 0, 'x', 0 }, 5);
+ test_strv_make_nulstr_binary_one(STRV_MAKE("x", "", "x"), (const char[5]) { 'x', 0, 0, 'x', 0 }, 5);
+ test_strv_make_nulstr_binary_one(STRV_MAKE("x", "x", "x"), (const char[6]) { 'x', 0, 'x', 0, 'x', 0 }, 6);
+}
+
+DEFINE_TEST_MAIN(LOG_INFO);
TEST(sd_hwdb_new_from_path) {
_cleanup_(sd_hwdb_unrefp) sd_hwdb *hwdb = NULL;
- const char *hwdb_bin_path = NULL;
int r;
assert_se(sd_hwdb_new_from_path(NULL, &hwdb) == -EINVAL);
}
TEST(architecture_table) {
- const char *n, *n2;
+ const char *n2;
NULSTR_FOREACH(n,
"native\0"
assert_se(streq(syscall_filter_sets[SYSCALL_FILTER_SET_KNOWN].name, "@known"));
for (size_t i = 0; i < _SYSCALL_FILTER_SET_MAX; i++) {
- const char *k, *p = NULL;
+ const char *p = NULL;
/* Make sure each group has a description */
assert_se(!isempty(syscall_filter_sets[0].help));
#include <stdlib.h>
+#include "nulstr-util.h"
#include "strbuf.h"
#include "string-util.h"
#include "strv.h"
#include "alloc-util.h"
#include "escape.h"
-#include "nulstr-util.h"
#include "string-util.h"
#include "strv.h"
#include "tests.h"
assert_se(strv_equal(l, (char**) input_table_retain_escape));
}
-TEST(strv_split_nulstr) {
- _cleanup_strv_free_ char **l = NULL;
- const char nulstr[] = "str0\0str1\0str2\0str3\0";
-
- l = strv_split_nulstr (nulstr);
- assert_se(l);
-
- assert_se(streq(l[0], "str0"));
- assert_se(streq(l[1], "str1"));
- assert_se(streq(l[2], "str2"));
- assert_se(streq(l[3], "str3"));
-}
-
-TEST(strv_parse_nulstr) {
- _cleanup_strv_free_ char **l = NULL;
- const char nulstr[] = "hoge\0hoge2\0hoge3\0\0hoge5\0\0xxx";
-
- l = strv_parse_nulstr(nulstr, sizeof(nulstr)-1);
- assert_se(l);
- puts("Parse nulstr:");
- strv_print(l);
-
- assert_se(streq(l[0], "hoge"));
- assert_se(streq(l[1], "hoge2"));
- assert_se(streq(l[2], "hoge3"));
- assert_se(streq(l[3], ""));
- assert_se(streq(l[4], "hoge5"));
- assert_se(streq(l[5], ""));
- assert_se(streq(l[6], "xxx"));
-}
-
TEST(strv_overlap) {
const char * const input_table[] = {
"one",
assert_se(v[1] == NULL);
}
-static void test_strv_make_nulstr_one(char **l) {
- _cleanup_free_ char *b = NULL, *c = NULL;
- _cleanup_strv_free_ char **q = NULL;
- const char *s = NULL;
- size_t n, m;
- unsigned i = 0;
-
- log_info("/* %s */", __func__);
-
- assert_se(strv_make_nulstr(l, &b, &n) >= 0);
- assert_se(q = strv_parse_nulstr(b, n));
- assert_se(strv_equal(l, q));
-
- assert_se(strv_make_nulstr(q, &c, &m) >= 0);
- assert_se(m == n);
- assert_se(memcmp(b, c, m) == 0);
-
- NULSTR_FOREACH(s, b)
- assert_se(streq(s, l[i++]));
- assert_se(i == strv_length(l));
-}
-
-TEST(strv_make_nulstr) {
- test_strv_make_nulstr_one(NULL);
- test_strv_make_nulstr_one(STRV_MAKE(NULL));
- test_strv_make_nulstr_one(STRV_MAKE("foo"));
- test_strv_make_nulstr_one(STRV_MAKE("foo", "bar"));
- test_strv_make_nulstr_one(STRV_MAKE("foo", "bar", "quuux"));
-}
-
TEST(foreach_string) {
const char * const t[] = {
"foo",
assert(i);
- table = table_new("key", "value");
+ table = table_new_vertical();
if (!table)
return log_oom();
- table_set_header(table, false);
-
assert_se(cell = table_get_cell(table, 0, 0));
(void) table_set_ellipsize_percent(table, cell, 100);
- (void) table_set_align_percent(table, cell, 100);
assert_se(cell = table_get_cell(table, 0, 1));
(void) table_set_ellipsize_percent(table, cell, 100);
n = have_time ? strftime(a, sizeof a, "%a %Y-%m-%d %H:%M:%S %Z", localtime_r(&sec, &tm)) : 0;
r = table_add_many(table,
- TABLE_STRING, "Local time:",
+ TABLE_FIELD, "Local time",
TABLE_STRING, n > 0 ? a : "n/a");
if (r < 0)
return table_log_add_error(r);
n = have_time ? strftime(a, sizeof a, "%a %Y-%m-%d %H:%M:%S UTC", gmtime_r(&sec, &tm)) : 0;
r = table_add_many(table,
- TABLE_STRING, "Universal time:",
+ TABLE_FIELD, "Universal time",
TABLE_STRING, n > 0 ? a : "n/a");
if (r < 0)
return table_log_add_error(r);
} else
n = 0;
r = table_add_many(table,
- TABLE_STRING, "RTC time:",
+ TABLE_FIELD, "RTC time",
TABLE_STRING, n > 0 ? a : "n/a");
if (r < 0)
return table_log_add_error(r);
- r = table_add_cell(table, NULL, TABLE_STRING, "Time zone:");
+ r = table_add_cell(table, NULL, TABLE_FIELD, "Time zone");
if (r < 0)
return table_log_add_error(r);
tzset();
r = table_add_many(table,
- TABLE_STRING, "System clock synchronized:",
+ TABLE_FIELD, "System clock synchronized",
TABLE_BOOLEAN, i->ntp_synced,
- TABLE_STRING, "NTP service:",
+ TABLE_FIELD, "NTP service",
TABLE_STRING, i->ntp_capable ? (i->ntp_active ? "active" : "inactive") : "n/a",
- TABLE_STRING, "RTC in local TZ:",
+ TABLE_FIELD, "RTC in local TZ",
TABLE_BOOLEAN, i->rtc_local);
if (r < 0)
return table_log_add_error(r);
assert(i);
- table = table_new("key", "value");
+ table = table_new_vertical();
if (!table)
return log_oom();
- table_set_header(table, false);
-
assert_se(cell = table_get_cell(table, 0, 0));
(void) table_set_ellipsize_percent(table, cell, 100);
- (void) table_set_align_percent(table, cell, 100);
assert_se(cell = table_get_cell(table, 0, 1));
(void) table_set_ellipsize_percent(table, cell, 100);
* d = (T4 - T1) - (T3 - T2) t = ((T2 - T1) + (T3 - T4)) / 2"
*/
- r = table_add_cell(table, NULL, TABLE_STRING, "Server:");
+ r = table_add_cell(table, NULL, TABLE_FIELD, "Server");
if (r < 0)
return table_log_add_error(r);
if (r < 0)
return table_log_add_error(r);
- r = table_add_cell(table, NULL, TABLE_STRING, "Poll interval:");
+ r = table_add_cell(table, NULL, TABLE_FIELD, "Poll interval");
if (r < 0)
return table_log_add_error(r);
if (i->packet_count == 0) {
r = table_add_many(table,
- TABLE_STRING, "Packet count:",
+ TABLE_FIELD, "Packet count",
TABLE_STRING, "0");
if (r < 0)
return table_log_add_error(r);
root_distance = i->root_delay / 2 + i->root_dispersion;
r = table_add_many(table,
- TABLE_STRING, "Leap:",
+ TABLE_FIELD, "Leap",
TABLE_STRING, ntp_leap_to_string(i->leap),
- TABLE_STRING, "Version:",
+ TABLE_FIELD, "Version",
TABLE_UINT32, i->version,
- TABLE_STRING, "Stratum:",
+ TABLE_FIELD, "Stratum",
TABLE_UINT32, i->stratum,
- TABLE_STRING, "Reference:");
+ TABLE_FIELD, "Reference");
if (r < 0)
return table_log_add_error(r);
if (r < 0)
return table_log_add_error(r);
- r = table_add_cell(table, NULL, TABLE_STRING, "Precision:");
+ r = table_add_cell(table, NULL, TABLE_FIELD, "Precision");
if (r < 0)
return table_log_add_error(r);
if (r < 0)
return table_log_add_error(r);
- r = table_add_cell(table, NULL, TABLE_STRING, "Root distance:");
+ r = table_add_cell(table, NULL, TABLE_FIELD, "Root distance");
if (r < 0)
return table_log_add_error(r);
if (r < 0)
return table_log_add_error(r);
- r = table_add_cell(table, NULL, TABLE_STRING, "Offset:");
+ r = table_add_cell(table, NULL, TABLE_FIELD, "Offset");
if (r < 0)
return table_log_add_error(r);
return table_log_add_error(r);
r = table_add_many(table,
- TABLE_STRING, "Delay:",
+ TABLE_FIELD, "Delay",
TABLE_STRING, FORMAT_TIMESPAN(delay, 0),
- TABLE_STRING, "Jitter:",
+ TABLE_FIELD, "Jitter",
TABLE_STRING, FORMAT_TIMESPAN(i->jitter, 0),
- TABLE_STRING, "Packet count:",
+ TABLE_FIELD, "Packet count",
TABLE_UINT64, i->packet_count);
if (r < 0)
return table_log_add_error(r);
if (!i->spike) {
- r = table_add_cell(table, NULL, TABLE_STRING, "Frequency:");
+ r = table_add_cell(table, NULL, TABLE_FIELD, "Frequency");
if (r < 0)
return table_log_add_error(r);
#include "mkdir-label.h"
#include "mount-util.h"
#include "mountpoint-util.h"
+#include "nulstr-util.h"
#include "offline-passwd.h"
#include "pager.h"
#include "parse-argument.h"
return label_fix_full(fd, /* inode_path= */ NULL, /* label_path= */ path, 0);
}
-static int path_open_parent_safe(const char *path) {
+static int path_open_parent_safe(const char *path, bool allow_failure) {
_cleanup_free_ char *dn = NULL;
int r, fd;
if (!path_is_normalized(path))
- return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Failed to open parent of '%s': path not normalized.", path);
+ return log_full_errno(allow_failure ? LOG_INFO : LOG_ERR,
+ SYNTHETIC_ERRNO(EINVAL),
+ "Failed to open parent of '%s': path not normalized%s.",
+ path,
+ allow_failure ? ", ignoring" : "");
r = path_extract_directory(path, &dn);
if (r < 0)
- return log_error_errno(r, "Unable to determine parent directory of '%s': %m", path);
+ return log_full_errno(allow_failure ? LOG_INFO : LOG_ERR,
+ r,
+ "Unable to determine parent directory of '%s'%s: %m",
+ path,
+ allow_failure ? ", ignoring" : "");
- r = chase_symlinks(dn, arg_root, CHASE_SAFE|CHASE_WARN, NULL, &fd);
+ r = chase_symlinks(dn, arg_root, allow_failure ? CHASE_SAFE : CHASE_SAFE|CHASE_WARN, NULL, &fd);
if (r == -ENOLINK) /* Unsafe symlink: already covered by CHASE_WARN */
return r;
if (r < 0)
- return log_error_errno(r, "Failed to open path '%s': %m", dn);
+ return log_full_errno(allow_failure ? LOG_INFO : LOG_ERR,
+ r,
+ "Failed to open path '%s'%s: %m",
+ dn,
+ allow_failure ? ", ignoring" : "");
return fd;
}
/* Validate the path and keep the fd on the directory for opening the file so we're sure that it
* can't be changed behind our back. */
- dir_fd = path_open_parent_safe(path);
+ dir_fd = path_open_parent_safe(path, i->allow_failure);
if (dir_fd < 0)
return dir_fd;
/* Validate the path and keep the fd on the directory for opening the file so we're sure that it
* can't be changed behind our back. */
- dir_fd = path_open_parent_safe(path);
+ dir_fd = path_open_parent_safe(path, i->allow_failure);
if (dir_fd < 0)
return dir_fd;
/* Validate the path and keep the fd on the directory for opening the file so we're sure that it
* can't be changed behind our back. */
- dir_fd = path_open_parent_safe(path);
+ dir_fd = path_open_parent_safe(path, i->allow_failure);
if (dir_fd < 0)
return dir_fd;
/* Validate the path and use the returned directory fd for copying the target so we're sure that the
* path can't be changed behind our back. */
- dfd = path_open_parent_safe(i->path);
+ dfd = path_open_parent_safe(i->path, i->allow_failure);
if (dfd < 0)
return dfd;
dfd, bn,
i->uid_set ? i->uid : UID_INVALID,
i->gid_set ? i->gid : GID_INVALID,
- COPY_REFLINK | COPY_MERGE_EMPTY | COPY_MAC_CREATE | COPY_HARDLINKS);
+ COPY_REFLINK | COPY_MERGE_EMPTY | COPY_MAC_CREATE | COPY_HARDLINKS,
+ NULL);
fd = openat(dfd, bn, O_NOFOLLOW|O_CLOEXEC|O_PATH);
if (fd < 0) {
const char *path,
mode_t mode,
bool subvol,
+ bool allow_failure,
struct stat *ret_st,
CreationMode *ret_creation) {
if (r < 0)
return log_error_errno(r, "Failed to extract filename from path '%s': %m", path);
- pfd = path_open_parent_safe(path);
+ pfd = path_open_parent_safe(path, allow_failure);
if (pfd < 0)
return pfd;
/* Then look at the original error */
if (r < 0)
- return log_error_errno(r, "Failed to create directory or subvolume \"%s\": %m", path);
+ return log_full_errno(allow_failure ? LOG_INFO : LOG_ERR,
+ r,
+ "Failed to create directory or subvolume \"%s\"%s: %m",
+ path,
+ allow_failure ? ", ignoring" : "");
return log_error_errno(errno, "Failed to open directory/subvolume we just created '%s': %m", path);
}
assert(i);
assert(IN_SET(i->type, CREATE_DIRECTORY, TRUNCATE_DIRECTORY));
- fd = create_directory_or_subvolume(path, i->mode, /* subvol= */ false, &st, &creation);
+ fd = create_directory_or_subvolume(path, i->mode, /* subvol= */ false, i->allow_failure, &st, &creation);
if (fd == -EEXIST)
return 0;
if (fd < 0)
assert(i);
assert(IN_SET(i->type, CREATE_SUBVOLUME, CREATE_SUBVOLUME_NEW_QUOTA, CREATE_SUBVOLUME_INHERIT_QUOTA));
- fd = create_directory_or_subvolume(path, i->mode, /* subvol = */ true, &st, &creation);
+ fd = create_directory_or_subvolume(path, i->mode, /* subvol = */ true, i->allow_failure, &st, &creation);
if (fd == -EEXIST)
return 0;
if (fd < 0)
/* Validate the path and use the returned directory fd for copying the target so we're sure that the
* path can't be changed behind our back. */
- dfd = path_open_parent_safe(i->path);
+ dfd = path_open_parent_safe(i->path, i->allow_failure);
if (dfd < 0)
return dfd;
if (r == O_DIRECTORY)
return log_error_errno(SYNTHETIC_ERRNO(EISDIR), "Cannot open path '%s' for creating FIFO, is a directory.", i->path);
- pfd = path_open_parent_safe(i->path);
+ pfd = path_open_parent_safe(i->path, i->allow_failure);
if (pfd < 0)
return pfd;
if (r == O_DIRECTORY)
return log_error_errno(SYNTHETIC_ERRNO(EISDIR), "Cannot open path '%s' for creating FIFO, is a directory.", i->path);
- pfd = path_open_parent_safe(i->path);
+ pfd = path_open_parent_safe(i->path, i->allow_failure);
if (pfd < 0)
return pfd;
}
static bool token_match_string(UdevRuleToken *token, const char *str) {
- const char *i, *value;
+ const char *value;
bool match = false;
assert(token);
export SYSTEMD_LOG_LEVEL=debug
export PAGER=cat
+# Disable use of special glyphs such as →
+export SYSTEMD_UTF8=0
+
seed=750b6cd5c4ae4012a15e7be3c29e6a47
if ! systemd-detect-virt --quiet --container; then
output=$(systemd-repart --definitions="$defs" --empty=create --size=100M --json=pretty "$imgs/zzz")
- diff <(echo "$output") - <<EOF
+ diff -u <(echo "$output") - <<EOF
[
{
"type" : "swap",
"offset" : 1048576,
"old_size" : 0,
"raw_size" : 33554432,
- "size" : "→ 32.0M",
+ "size" : "-> 32.0M",
"old_padding" : 0,
"raw_padding" : 0,
- "padding" : "→ 0B",
+ "padding" : "-> 0B",
"activity" : "create",
"drop-in_files" : [
"$defs/root.conf.d/override1.conf",
output=$(systemd-repart --definitions="$defs/1" --definitions="$defs/2" --empty=create --size=100M --json=pretty "$imgs/zzz")
- diff <(echo "$output") - <<EOF
+ diff -u <(echo "$output") - <<EOF
[
{
"type" : "swap",
"offset" : 1048576,
"old_size" : 0,
"raw_size" : 33554432,
- "size" : "→ 32.0M",
+ "size" : "-> 32.0M",
"old_padding" : 0,
"raw_padding" : 0,
- "padding" : "→ 0B",
+ "padding" : "-> 0B",
"activity" : "create"
},
{
"offset" : 34603008,
"old_size" : 0,
"raw_size" : 33554432,
- "size" : "→ 32.0M",
+ "size" : "-> 32.0M",
"old_padding" : 0,
"raw_padding" : 0,
- "padding" : "→ 0B",
+ "padding" : "-> 0B",
"activity" : "create"
}
]
roothash=$(jq -r ".[] | select(.type == \"root-${architecture}-verity\") | .roothash" <<< "$output")
- # Check that we can dissect, mount and unmount a repart verity image.
+ # Check that we can dissect, mount and unmount a repart verity image. (and that the image UUID is deterministic)
systemd-dissect "$imgs/verity" --root-hash "$roothash"
+ systemd-dissect "$imgs/verity" --root-hash "$roothash" --json=short | grep -q '"imageUuid":"1d2ce291-7cce-4f7d-bc83-fdb49ad74ebd"'
systemd-dissect "$imgs/verity" --root-hash "$roothash" -M "$imgs/mnt"
systemd-dissect -U "$imgs/mnt"
}
+test_issue_24786() {
+ local defs imgs root output
+
+ if systemd-detect-virt --quiet --container; then
+ echo "Skipping verity test in container."
+ return
+ fi
+
+ defs="$(mktemp --directory "/tmp/test-repart.XXXXXXXXXX")"
+ imgs="$(mktemp --directory "/var/tmp/test-repart.XXXXXXXXXX")"
+ root="$(mktemp --directory "/var/tmp/test-repart.XXXXXXXXXX")"
+ # shellcheck disable=SC2064
+ trap "rm -rf '$defs' '$imgs' '$root'" RETURN
+
+ touch "$root/abc"
+ mkdir "$root/usr"
+ touch "$root/usr/def"
+
+ cat >"$defs/00-root.conf" <<EOF
+[Partition]
+Type=root-${architecture}
+CopyFiles=/
+EOF
+
+ cat >"$defs/10-usr.conf" <<EOF
+[Partition]
+Type=usr-${architecture}
+CopyFiles=/usr:/
+EOF
+
+ output=$(systemd-repart --definitions="$defs" \
+ --seed="$seed" \
+ --dry-run=no \
+ --empty=create \
+ --size=auto \
+ --json=pretty \
+ --root="$root" \
+ "$imgs/zzz")
+
+ loop=$(losetup -P --show -f "$imgs/zzz")
+ udevadm wait --timeout 60 --settle "${loop:?}"
+
+ mkdir "$imgs/mnt"
+ mount -t ext4 "${loop}p1" "$imgs/mnt"
+ assert_rc 0 ls "$imgs/mnt/abc"
+ assert_rc 2 ls "$imgs/mnt/usr"
+ mkdir "$imgs/mnt/usr"
+ mount -t ext4 "${loop}p2" "$imgs/mnt/usr"
+ assert_rc 0 ls "$imgs/mnt/usr/def"
+
+ umount -R "$imgs/mnt"
+ losetup -d "$loop"
+}
+
test_sector() {
local defs imgs output loop
local start size ratio
test_issue_24553
test_zero_uuid
test_verity
+test_issue_24786
# Valid block sizes on the Linux block layer are >= 512 and <= PAGE_SIZE, and
# must be powers of 2. Which leaves exactly four different ones to test on
echo nameserver 10.0.3.3 10.0.3.4 | "$RESOLVCONF" -a hoge.foo.dhcp
assert_in '10.0.3.1 10.0.3.2' "$(resolvectl dns hoge)"
assert_in '10.0.3.3 10.0.3.4' "$(resolvectl dns hoge.foo)"
+
+# Tests for mDNS and LLMNR settings
+mkdir -p /run/systemd/resolved.conf.d
+{
+ echo "[Resolve]"
+ echo "MulticastDNS=yes"
+ echo "LLMNR=yes"
+} >/run/systemd/resolved.conf.d/mdns-llmnr.conf
+systemctl restart systemd-resolved.service
+systemctl service-log-level systemd-resolved.service debug
+# make sure networkd is not running.
+systemctl stop systemd-networkd.service
+# defaults to yes (both the global and per-link settings are yes)
+assert_in 'yes' "$(resolvectl mdns hoge)"
+assert_in 'yes' "$(resolvectl llmnr hoge)"
+# set per-link setting
+resolvectl mdns hoge yes
+resolvectl llmnr hoge yes
+assert_in 'yes' "$(resolvectl mdns hoge)"
+assert_in 'yes' "$(resolvectl llmnr hoge)"
+resolvectl mdns hoge resolve
+resolvectl llmnr hoge resolve
+assert_in 'resolve' "$(resolvectl mdns hoge)"
+assert_in 'resolve' "$(resolvectl llmnr hoge)"
+resolvectl mdns hoge no
+resolvectl llmnr hoge no
+assert_in 'no' "$(resolvectl mdns hoge)"
+assert_in 'no' "$(resolvectl llmnr hoge)"
+# downgrade global setting to resolve
+{
+ echo "[Resolve]"
+ echo "MulticastDNS=resolve"
+ echo "LLMNR=resolve"
+} >/run/systemd/resolved.conf.d/mdns-llmnr.conf
+systemctl restart systemd-resolved.service
+systemctl service-log-level systemd-resolved.service debug
+# set per-link setting
+resolvectl mdns hoge yes
+resolvectl llmnr hoge yes
+assert_in 'resolve' "$(resolvectl mdns hoge)"
+assert_in 'resolve' "$(resolvectl llmnr hoge)"
+resolvectl mdns hoge resolve
+resolvectl llmnr hoge resolve
+assert_in 'resolve' "$(resolvectl mdns hoge)"
+assert_in 'resolve' "$(resolvectl llmnr hoge)"
+resolvectl mdns hoge no
+resolvectl llmnr hoge no
+assert_in 'no' "$(resolvectl mdns hoge)"
+assert_in 'no' "$(resolvectl llmnr hoge)"
+# downgrade global setting to no
+{
+ echo "[Resolve]"
+ echo "MulticastDNS=no"
+ echo "LLMNR=no"
+} >/run/systemd/resolved.conf.d/mdns-llmnr.conf
+systemctl restart systemd-resolved.service
+systemctl service-log-level systemd-resolved.service debug
+# set per-link setting
+resolvectl mdns hoge yes
+resolvectl llmnr hoge yes
+assert_in 'no' "$(resolvectl mdns hoge)"
+assert_in 'no' "$(resolvectl llmnr hoge)"
+resolvectl mdns hoge resolve
+resolvectl llmnr hoge resolve
+assert_in 'no' "$(resolvectl mdns hoge)"
+assert_in 'no' "$(resolvectl llmnr hoge)"
+resolvectl mdns hoge no
+resolvectl llmnr hoge no
+assert_in 'no' "$(resolvectl mdns hoge)"
+assert_in 'no' "$(resolvectl llmnr hoge)"
+
+# Cleanup
+rm -f /run/systemd/resolved.conf.d/mdns-llmnr.conf
ip link del hoge
ip link del hoge.foo
DNS=10.0.0.1
EOF
+mkdir -p /run/systemd/resolved.conf.d
{
+ echo "[Resolve]"
echo "FallbackDNS="
echo "DNSSEC=allow-downgrade"
echo "DNSOverTLS=opportunistic"
-} >>/etc/systemd/resolved.conf
+} >/run/systemd/resolved.conf.d/test.conf
ln -svf /run/systemd/resolve/stub-resolv.conf /etc/resolv.conf
# Override the default NTA list, which turns off DNSSEC validation for (among
# others) the test. domain