summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/basic/virt.c70
-rw-r--r--src/basic/virt.h1
-rw-r--r--src/core/execute.c177
-rw-r--r--src/core/load-fragment.c7
-rw-r--r--src/detect-virt/detect-virt.c31
-rw-r--r--src/hwdb/hwdb.c4
-rw-r--r--src/network/networkd-network-gperf.gperf2
-rw-r--r--src/network/networkd-network.c50
-rw-r--r--src/network/networkd-network.h1
-rw-r--r--src/shared/condition.c19
-rw-r--r--src/systemctl/systemctl.c14
-rw-r--r--src/test/test-condition.c60
-rw-r--r--src/test/test-tables.c2
-rw-r--r--src/udev/udev-rules.c8
-rw-r--r--src/vconsole/90-vconsole.rules.in2
-rw-r--r--src/vconsole/vconsole-setup.c58
16 files changed, 379 insertions, 127 deletions
diff --git a/src/basic/virt.c b/src/basic/virt.c
index 41012d52a0..69b0f96183 100644
--- a/src/basic/virt.c
+++ b/src/basic/virt.c
@@ -485,6 +485,76 @@ int detect_virtualization(void) {
return r;
}
+static int userns_has_mapping(const char *name) {
+ _cleanup_fclose_ FILE *f = NULL;
+ _cleanup_free_ char *buf = NULL;
+ size_t n_allocated = 0;
+ ssize_t n;
+ uint32_t a, b, c;
+ int r;
+
+ f = fopen(name, "re");
+ if (!f) {
+ log_debug_errno(errno, "Failed to open %s: %m", name);
+ return errno == -ENOENT ? false : -errno;
+ }
+
+ n = getline(&buf, &n_allocated, f);
+ if (n < 0) {
+ if (feof(f)) {
+ log_debug("%s is empty, we're in an uninitialized user namespace", name);
+ return true;
+ }
+
+ return log_debug_errno(errno, "Failed to read %s: %m", name);
+ }
+
+ r = sscanf(buf, "%"PRIu32" %"PRIu32" %"PRIu32, &a, &b, &c);
+ if (r < 3)
+ return log_debug_errno(errno, "Failed to parse %s: %m", name);
+
+ if (a == 0 && b == 0 && c == UINT32_MAX) {
+ /* The kernel calls mappings_overlap() and does not allow overlaps */
+ log_debug("%s has a full 1:1 mapping", name);
+ return false;
+ }
+
+ /* Anything else implies that we are in a user namespace */
+ log_debug("Mapping found in %s, we're in a user namespace", name);
+ return true;
+}
+
+int running_in_userns(void) {
+ _cleanup_free_ char *line = NULL;
+ int r;
+
+ r = userns_has_mapping("/proc/self/uid_map");
+ if (r != 0)
+ return r;
+
+ r = userns_has_mapping("/proc/self/gid_map");
+ if (r != 0)
+ return r;
+
+ /* "setgroups" file was added in kernel v3.18-rc6-15-g9cc46516dd. It is also
+ * possible to compile a kernel without CONFIG_USER_NS, in which case "setgroups"
+ * also does not exist. We cannot distinguish those two cases, so assume that
+ * we're running on a stripped-down recent kernel, rather than on an old one,
+ * and if the file is not found, return false.
+ */
+ r = read_one_line_file("/proc/self/setgroups", &line);
+ if (r < 0) {
+ log_debug_errno(r, "/proc/self/setgroups: %m");
+ return r == -ENOENT ? false : r;
+ }
+
+ truncate_nl(line);
+ r = streq(line, "deny");
+ /* See user_namespaces(7) for a description of this "setgroups" contents. */
+ log_debug("/proc/self/setgroups contains \"%s\", %s user namespace", line, r ? "in" : "not in");
+ return r;
+}
+
int running_in_chroot(void) {
int ret;
diff --git a/src/basic/virt.h b/src/basic/virt.h
index bc5b3ae94d..7d15169112 100644
--- a/src/basic/virt.h
+++ b/src/basic/virt.h
@@ -67,6 +67,7 @@ int detect_vm(void);
int detect_container(void);
int detect_virtualization(void);
+int running_in_userns(void);
int running_in_chroot(void);
const char *virtualization_to_string(int v) _const_;
diff --git a/src/core/execute.c b/src/core/execute.c
index 7b42ac7bdc..ae9df41b99 100644
--- a/src/core/execute.c
+++ b/src/core/execute.c
@@ -1481,7 +1481,7 @@ finish:
return r;
}
-static int apply_protect_sysctl(Unit *u, const ExecContext *c) {
+static int apply_protect_sysctl(const Unit *u, const ExecContext *c) {
scmp_filter_ctx seccomp;
int r;
@@ -1512,7 +1512,7 @@ finish:
return r;
}
-static int apply_protect_kernel_modules(Unit *u, const ExecContext *c) {
+static int apply_protect_kernel_modules(const Unit *u, const ExecContext *c) {
assert(c);
/* Turn off module syscalls on ProtectKernelModules=yes */
@@ -1523,7 +1523,7 @@ static int apply_protect_kernel_modules(Unit *u, const ExecContext *c) {
return seccomp_load_filter_set(SCMP_ACT_ALLOW, syscall_filter_sets + SYSCALL_FILTER_SET_MODULE, SCMP_ACT_ERRNO(EPERM));
}
-static int apply_private_devices(Unit *u, const ExecContext *c) {
+static int apply_private_devices(const Unit *u, const ExecContext *c) {
assert(c);
/* If PrivateDevices= is set, also turn off iopl and all @raw-io syscalls. */
@@ -2014,6 +2014,92 @@ static int compile_read_write_paths(
return 0;
}
+static int apply_mount_namespace(Unit *u, const ExecContext *context,
+ const ExecParameters *params,
+ ExecRuntime *runtime) {
+ int r;
+ _cleanup_free_ char **rw = NULL;
+ char *tmp = NULL, *var = NULL;
+ const char *root_dir = NULL;
+ NameSpaceInfo ns_info = {
+ .private_dev = context->private_devices,
+ .protect_control_groups = context->protect_control_groups,
+ .protect_kernel_tunables = context->protect_kernel_tunables,
+ .protect_kernel_modules = context->protect_kernel_modules,
+ };
+
+ assert(context);
+
+ /* The runtime struct only contains the parent of the private /tmp,
+ * which is non-accessible to world users. Inside of it there's a /tmp
+ * that is sticky, and that's the one we want to use here. */
+
+ if (context->private_tmp && runtime) {
+ if (runtime->tmp_dir)
+ tmp = strjoina(runtime->tmp_dir, "/tmp");
+ if (runtime->var_tmp_dir)
+ var = strjoina(runtime->var_tmp_dir, "/tmp");
+ }
+
+ r = compile_read_write_paths(context, params, &rw);
+ if (r < 0)
+ return r;
+
+ if (params->flags & EXEC_APPLY_CHROOT)
+ root_dir = context->root_directory;
+
+ r = setup_namespace(root_dir, &ns_info, rw,
+ context->read_only_paths,
+ context->inaccessible_paths,
+ tmp,
+ var,
+ context->protect_home,
+ context->protect_system,
+ context->mount_flags);
+
+ /* If we couldn't set up the namespace this is probably due to a
+ * missing capability. In this case, silently proceeed. */
+ if (IN_SET(r, -EPERM, -EACCES)) {
+ log_open();
+ log_unit_debug_errno(u, r, "Failed to set up namespace, assuming containerized execution, ignoring: %m");
+ log_close();
+ r = 0;
+ }
+
+ return r;
+}
+
+static int apply_working_directory(const ExecContext *context,
+ const ExecParameters *params,
+ const char *home,
+ const bool needs_mount_ns) {
+ const char *d;
+ const char *wd;
+
+ assert(context);
+
+ if (context->working_directory_home)
+ wd = home;
+ else if (context->working_directory)
+ wd = context->working_directory;
+ else
+ wd = "/";
+
+ if (params->flags & EXEC_APPLY_CHROOT) {
+ if (!needs_mount_ns && context->root_directory)
+ if (chroot(context->root_directory) < 0)
+ return -errno;
+
+ d = wd;
+ } else
+ d = strjoina(strempty(context->root_directory), "/", strempty(wd));
+
+ if (chdir(d) < 0 && !context->working_directory_missing_ok)
+ return -errno;
+
+ return 0;
+}
+
static void append_socket_pair(int *array, unsigned *n, int pair[2]) {
assert(array);
assert(n);
@@ -2150,7 +2236,7 @@ static int exec_child(
_cleanup_free_ char *mac_selinux_context_net = NULL;
_cleanup_free_ gid_t *supplementary_gids = NULL;
const char *username = NULL, *groupname = NULL;
- const char *home = NULL, *shell = NULL, *wd;
+ const char *home = NULL, *shell = NULL;
dev_t journal_stream_dev = 0;
ino_t journal_stream_ino = 0;
bool needs_mount_namespace;
@@ -2477,65 +2563,19 @@ static int exec_child(
needs_mount_namespace = exec_needs_mount_namespace(context, params, runtime);
if (needs_mount_namespace) {
- _cleanup_free_ char **rw = NULL;
- char *tmp = NULL, *var = NULL;
- NameSpaceInfo ns_info = {
- .private_dev = context->private_devices,
- .protect_control_groups = context->protect_control_groups,
- .protect_kernel_tunables = context->protect_kernel_tunables,
- .protect_kernel_modules = context->protect_kernel_modules,
- };
-
- /* The runtime struct only contains the parent
- * of the private /tmp, which is
- * non-accessible to world users. Inside of it
- * there's a /tmp that is sticky, and that's
- * the one we want to use here. */
-
- if (context->private_tmp && runtime) {
- if (runtime->tmp_dir)
- tmp = strjoina(runtime->tmp_dir, "/tmp");
- if (runtime->var_tmp_dir)
- var = strjoina(runtime->var_tmp_dir, "/tmp");
- }
-
- r = compile_read_write_paths(context, params, &rw);
+ r = apply_mount_namespace(unit, context, params, runtime);
if (r < 0) {
*exit_status = EXIT_NAMESPACE;
return r;
}
-
- r = setup_namespace(
- (params->flags & EXEC_APPLY_CHROOT) ? context->root_directory : NULL,
- &ns_info,
- rw,
- context->read_only_paths,
- context->inaccessible_paths,
- tmp,
- var,
- context->protect_home,
- context->protect_system,
- context->mount_flags);
-
- /* If we couldn't set up the namespace this is
- * probably due to a missing capability. In this case,
- * silently proceeed. */
- if (r == -EPERM || r == -EACCES) {
- log_open();
- log_unit_debug_errno(unit, r, "Failed to set up namespace, assuming containerized execution, ignoring: %m");
- log_close();
- } else if (r < 0) {
- *exit_status = EXIT_NAMESPACE;
- return r;
- }
}
- if (context->working_directory_home)
- wd = home;
- else if (context->working_directory)
- wd = context->working_directory;
- else
- wd = "/";
+ /* Apply just after mount namespace setup */
+ r = apply_working_directory(context, params, home, needs_mount_namespace);
+ if (r < 0) {
+ *exit_status = EXIT_CHROOT;
+ return r;
+ }
/* Drop group as early as possbile */
if ((params->flags & EXEC_APPLY_PERMISSIONS) && !command->privileged) {
@@ -2546,29 +2586,6 @@ static int exec_child(
}
}
- if (params->flags & EXEC_APPLY_CHROOT) {
- if (!needs_mount_namespace && context->root_directory)
- if (chroot(context->root_directory) < 0) {
- *exit_status = EXIT_CHROOT;
- return -errno;
- }
-
- if (chdir(wd) < 0 &&
- !context->working_directory_missing_ok) {
- *exit_status = EXIT_CHDIR;
- return -errno;
- }
- } else {
- const char *d;
-
- d = strjoina(strempty(context->root_directory), "/", strempty(wd));
- if (chdir(d) < 0 &&
- !context->working_directory_missing_ok) {
- *exit_status = EXIT_CHDIR;
- return -errno;
- }
- }
-
#ifdef HAVE_SELINUX
if ((params->flags & EXEC_APPLY_PERMISSIONS) &&
mac_selinux_use() &&
diff --git a/src/core/load-fragment.c b/src/core/load-fragment.c
index 118b39c1cf..cbc826809e 100644
--- a/src/core/load-fragment.c
+++ b/src/core/load-fragment.c
@@ -2744,11 +2744,6 @@ int config_parse_syscall_filter(
return r;
}
- /* Turn on NNP, but only if it wasn't configured explicitly
- * before, and only if we are in user mode. */
- if (!c->no_new_privileges_set && MANAGER_IS_USER(u->manager))
- c->no_new_privileges = true;
-
return 0;
}
@@ -3837,7 +3832,7 @@ int config_parse_no_new_privileges(
return 0;
}
- c->no_new_privileges = !!k;
+ c->no_new_privileges = k;
c->no_new_privileges_set = true;
return 0;
diff --git a/src/detect-virt/detect-virt.c b/src/detect-virt/detect-virt.c
index 5d51589a31..4b8956f0ad 100644
--- a/src/detect-virt/detect-virt.c
+++ b/src/detect-virt/detect-virt.c
@@ -31,6 +31,7 @@ static enum {
ONLY_VM,
ONLY_CONTAINER,
ONLY_CHROOT,
+ ONLY_PRIVATE_USERS,
} arg_mode = ANY_VIRTUALIZATION;
static void help(void) {
@@ -41,6 +42,7 @@ static void help(void) {
" -c --container Only detect whether we are run in a container\n"
" -v --vm Only detect whether we are run in a VM\n"
" -r --chroot Detect whether we are run in a chroot() environment\n"
+ " --private-users Only detect whether we are running in a user namespace\n"
" -q --quiet Don't output anything, just set return value\n"
, program_invocation_short_name);
}
@@ -48,16 +50,18 @@ static void help(void) {
static int parse_argv(int argc, char *argv[]) {
enum {
- ARG_VERSION = 0x100
+ ARG_VERSION = 0x100,
+ ARG_PRIVATE_USERS,
};
static const struct option options[] = {
- { "help", no_argument, NULL, 'h' },
- { "version", no_argument, NULL, ARG_VERSION },
- { "container", no_argument, NULL, 'c' },
- { "vm", no_argument, NULL, 'v' },
- { "chroot", no_argument, NULL, 'r' },
- { "quiet", no_argument, NULL, 'q' },
+ { "help", no_argument, NULL, 'h' },
+ { "version", no_argument, NULL, ARG_VERSION },
+ { "container", no_argument, NULL, 'c' },
+ { "vm", no_argument, NULL, 'v' },
+ { "chroot", no_argument, NULL, 'r' },
+ { "private-users", no_argument, NULL, ARG_PRIVATE_USERS },
+ { "quiet", no_argument, NULL, 'q' },
{}
};
@@ -85,6 +89,10 @@ static int parse_argv(int argc, char *argv[]) {
arg_mode = ONLY_CONTAINER;
break;
+ case ARG_PRIVATE_USERS:
+ arg_mode = ONLY_PRIVATE_USERS;
+ break;
+
case 'v':
arg_mode = ONLY_VM;
break;
@@ -151,6 +159,15 @@ int main(int argc, char *argv[]) {
return r ? EXIT_SUCCESS : EXIT_FAILURE;
+ case ONLY_PRIVATE_USERS:
+ r = running_in_userns();
+ if (r < 0) {
+ log_error_errno(r, "Failed to check for user namespace: %m");
+ return EXIT_FAILURE;
+ }
+
+ return r ? EXIT_SUCCESS : EXIT_FAILURE;
+
case ANY_VIRTUALIZATION:
default:
r = detect_virtualization();
diff --git a/src/hwdb/hwdb.c b/src/hwdb/hwdb.c
index be4ef5f9e9..ab1feb435b 100644
--- a/src/hwdb/hwdb.c
+++ b/src/hwdb/hwdb.c
@@ -172,8 +172,8 @@ static int trie_node_add_value(struct trie *trie, struct trie_node *node,
if (v < 0)
return v;
fn = strbuf_add_string(trie->strings, filename, strlen(filename));
- if (v < 0)
- return v;
+ if (fn < 0)
+ return fn;
if (node->values_count) {
struct trie_value_entry search = {
diff --git a/src/network/networkd-network-gperf.gperf b/src/network/networkd-network-gperf.gperf
index 5587961b9f..bcf8186c33 100644
--- a/src/network/networkd-network-gperf.gperf
+++ b/src/network/networkd-network-gperf.gperf
@@ -49,7 +49,7 @@ Network.EmitLLDP, config_parse_lldp_emit,
Network.Address, config_parse_address, 0, 0
Network.Gateway, config_parse_gateway, 0, 0
Network.Domains, config_parse_domains, 0, 0
-Network.DNS, config_parse_strv, 0, offsetof(Network, dns)
+Network.DNS, config_parse_dns, 0, 0
Network.LLMNR, config_parse_resolve_support, 0, offsetof(Network, llmnr)
Network.MulticastDNS, config_parse_resolve_support, 0, offsetof(Network, mdns)
Network.DNSSEC, config_parse_dnssec_mode, 0, offsetof(Network, dnssec_mode)
diff --git a/src/network/networkd-network.c b/src/network/networkd-network.c
index 584cb96979..042232fcac 100644
--- a/src/network/networkd-network.c
+++ b/src/network/networkd-network.c
@@ -979,6 +979,56 @@ int config_parse_dhcp_server_ntp(
}
}
+int config_parse_dns(
+ const char *unit,
+ const char *filename,
+ unsigned line,
+ const char *section,
+ unsigned section_line,
+ const char *lvalue,
+ int ltype,
+ const char *rvalue,
+ void *data,
+ void *userdata) {
+
+ Network *n = userdata;
+ int r;
+
+ assert(filename);
+ assert(lvalue);
+ assert(rvalue);
+
+ for (;;) {
+ _cleanup_free_ char *w = NULL;
+ union in_addr_union a;
+ int family;
+
+ r = extract_first_word(&rvalue, &w, WHITESPACE, EXTRACT_QUOTES|EXTRACT_RETAIN_ESCAPE);
+ if (r == 0)
+ break;
+ if (r == -ENOMEM)
+ return log_oom();
+ if (r < 0) {
+ log_syntax(unit, LOG_ERR, filename, line, r, "Invalid syntax, ignoring: %s", rvalue);
+ break;
+ }
+
+ r = in_addr_from_string_auto(w, &family, &a);
+ if (r < 0) {
+ log_syntax(unit, LOG_ERR, filename, line, 0, "Failed to parse dns server address, ignoring: %s", w);
+ continue;
+ }
+
+ r = strv_consume(&n->dns, w);
+ if (r < 0)
+ return log_oom();
+
+ w = NULL;
+ }
+
+ return 0;
+}
+
int config_parse_dnssec_negative_trust_anchors(
const char *unit,
const char *filename,
diff --git a/src/network/networkd-network.h b/src/network/networkd-network.h
index ef4b499ab9..42fc82d392 100644
--- a/src/network/networkd-network.h
+++ b/src/network/networkd-network.h
@@ -220,6 +220,7 @@ int config_parse_netdev(const char *unit, const char *filename, unsigned line, c
int config_parse_domains(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
int config_parse_tunnel(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
int config_parse_dhcp(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
+int config_parse_dns(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
int config_parse_dhcp_client_identifier(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
int config_parse_ipv6token(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
int config_parse_ipv6_privacy_extensions(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
diff --git a/src/shared/condition.c b/src/shared/condition.c
index 69b4837e1f..8bd6a51a99 100644
--- a/src/shared/condition.c
+++ b/src/shared/condition.c
@@ -146,25 +146,24 @@ static int condition_test_virtualization(Condition *c) {
assert(c->parameter);
assert(c->type == CONDITION_VIRTUALIZATION);
+ if (streq(c->parameter, "private-users"))
+ return running_in_userns();
+
v = detect_virtualization();
if (v < 0)
return v;
/* First, compare with yes/no */
b = parse_boolean(c->parameter);
-
- if (v > 0 && b > 0)
- return true;
-
- if (v == 0 && b == 0)
- return true;
+ if (b >= 0)
+ return b == !!v;
/* Then, compare categorization */
- if (VIRTUALIZATION_IS_VM(v) && streq(c->parameter, "vm"))
- return true;
+ if (streq(c->parameter, "vm"))
+ return VIRTUALIZATION_IS_VM(v);
- if (VIRTUALIZATION_IS_CONTAINER(v) && streq(c->parameter, "container"))
- return true;
+ if (streq(c->parameter, "container"))
+ return VIRTUALIZATION_IS_CONTAINER(v);
/* Finally compare id */
return v != VIRTUALIZATION_NONE && streq(c->parameter, virtualization_to_string(v));
diff --git a/src/systemctl/systemctl.c b/src/systemctl/systemctl.c
index 35d5c11cc7..d311bbec1a 100644
--- a/src/systemctl/systemctl.c
+++ b/src/systemctl/systemctl.c
@@ -5272,6 +5272,20 @@ static int cat(int argc, char *argv[], void *userdata) {
else
puts("");
+ if (need_daemon_reload(bus, *name))
+ fprintf(stderr,
+ "%s# Warning: %s changed on disk, the version systemd has loaded is outdated.\n"
+ "%s# This output shows the current version of the unit's original fragment and drop-in files.\n"
+ "%s# If fragments or drop-ins were added or removed, they are not properly reflected in this output.\n"
+ "%s# Run 'systemctl%s daemon-reload' to reload units.%s\n",
+ ansi_highlight_red(),
+ *name,
+ ansi_highlight_red(),
+ ansi_highlight_red(),
+ ansi_highlight_red(),
+ arg_scope == UNIT_FILE_SYSTEM ? "" : " --user",
+ ansi_normal());
+
if (fragment_path) {
r = cat_file(fragment_path, false);
if (r < 0)
diff --git a/src/test/test-condition.c b/src/test/test-condition.c
index 6f7d71ef9a..dd985f5863 100644
--- a/src/test/test-condition.c
+++ b/src/test/test-condition.c
@@ -31,6 +31,8 @@
#include "macro.h"
#include "selinux-util.h"
#include "smack-util.h"
+#include "strv.h"
+#include "virt.h"
#include "util.h"
static void test_condition_test_path(void) {
@@ -265,7 +267,64 @@ static void test_condition_test_security(void) {
condition_free(condition);
}
+static void test_condition_test_virtualization(void) {
+ Condition *condition;
+ const char *virt;
+ int r;
+
+ condition = condition_new(CONDITION_VIRTUALIZATION, "garbage oifdsjfoidsjoj", false, false);
+ assert_se(condition);
+ r = condition_test(condition);
+ log_info("ConditionVirtualization=garbage → %i", r);
+ assert_se(r == 0);
+ condition_free(condition);
+
+ condition = condition_new(CONDITION_VIRTUALIZATION, "container", false, false);
+ assert_se(condition);
+ r = condition_test(condition);
+ log_info("ConditionVirtualization=container → %i", r);
+ assert_se(r == !!detect_container());
+ condition_free(condition);
+
+ condition = condition_new(CONDITION_VIRTUALIZATION, "vm", false, false);
+ assert_se(condition);
+ r = condition_test(condition);
+ log_info("ConditionVirtualization=vm → %i", r);
+ assert_se(r == (detect_vm() && !detect_container()));
+ condition_free(condition);
+
+ condition = condition_new(CONDITION_VIRTUALIZATION, "private-users", false, false);
+ assert_se(condition);
+ r = condition_test(condition);
+ log_info("ConditionVirtualization=private-users → %i", r);
+ assert_se(r == !!running_in_userns());
+ condition_free(condition);
+
+ NULSTR_FOREACH(virt,
+ "kvm\0"
+ "qemu\0"
+ "bochs\0"
+ "xen\0"
+ "uml\0"
+ "vmware\0"
+ "oracle\0"
+ "microsoft\0"
+ "zvm\0"
+ "parallels\0"
+ "bhyve\0"
+ "vm_other\0") {
+
+ condition = condition_new(CONDITION_VIRTUALIZATION, virt, false, false);
+ assert_se(condition);
+ r = condition_test(condition);
+ log_info("ConditionVirtualization=%s → %i", virt, r);
+ assert_se(r >= 0);
+ condition_free(condition);
+ }
+}
+
int main(int argc, char *argv[]) {
+ log_set_max_level(LOG_DEBUG);
log_parse_environment();
log_open();
@@ -276,6 +335,7 @@ int main(int argc, char *argv[]) {
test_condition_test_kernel_command_line();
test_condition_test_null();
test_condition_test_security();
+ test_condition_test_virtualization();
return 0;
}
diff --git a/src/test/test-tables.c b/src/test/test-tables.c
index 8d4622694e..294d219869 100644
--- a/src/test/test-tables.c
+++ b/src/test/test-tables.c
@@ -48,6 +48,7 @@
#include "unit-name.h"
#include "unit.h"
#include "util.h"
+#include "virt.h"
int main(int argc, char **argv) {
test_table(architecture, ARCHITECTURE);
@@ -114,6 +115,7 @@ int main(int argc, char **argv) {
test_table(unit_load_state, UNIT_LOAD_STATE);
test_table(unit_type, UNIT_TYPE);
test_table(locale_variable, VARIABLE_LC);
+ test_table(virtualization, VIRTUALIZATION);
test_table_sparse(object_compressed, OBJECT_COMPRESSED);
diff --git a/src/udev/udev-rules.c b/src/udev/udev-rules.c
index 7619c8371b..f6c416bf70 100644
--- a/src/udev/udev-rules.c
+++ b/src/udev/udev-rules.c
@@ -2218,10 +2218,16 @@ void udev_rules_apply_to_event(struct udev_rules *rules,
rule->rule.filename_line);
break;
case TK_A_SECLABEL: {
+ char label_str[UTIL_LINE_SIZE] = {};
const char *name, *label;
name = rules_str(rules, cur->key.attr_off);
- label = rules_str(rules, cur->key.value_off);
+ udev_event_apply_format(event, rules_str(rules, cur->key.value_off), label_str, sizeof(label_str));
+ if (label_str[0] != '\0')
+ label = label_str;
+ else
+ label = rules_str(rules, cur->key.value_off);
+
if (cur->key.op == OP_ASSIGN || cur->key.op == OP_ASSIGN_FINAL)
udev_list_cleanup(&event->seclabel_list);
udev_list_entry_add(&event->seclabel_list, name, label);
diff --git a/src/vconsole/90-vconsole.rules.in b/src/vconsole/90-vconsole.rules.in
index 35b9ad5151..84b4d575bd 100644
--- a/src/vconsole/90-vconsole.rules.in
+++ b/src/vconsole/90-vconsole.rules.in
@@ -7,4 +7,4 @@
# Each vtcon keeps its own state of fonts.
#
-ACTION=="add", SUBSYSTEM=="vtconsole", KERNEL=="vtcon*", RUN+="@rootlibexecdir@/systemd-vconsole-setup"
+ACTION=="add", SUBSYSTEM=="vtconsole", KERNEL=="vtcon*", ATTR{name}!="*dummy device", RUN+="@rootlibexecdir@/systemd-vconsole-setup"
diff --git a/src/vconsole/vconsole-setup.c b/src/vconsole/vconsole-setup.c
index ac4ceb1486..a0ab5990fc 100644
--- a/src/vconsole/vconsole-setup.c
+++ b/src/vconsole/vconsole-setup.c
@@ -208,9 +208,9 @@ static int font_load_and_wait(const char *vc, const char *font, const char *map,
*/
static void setup_remaining_vcs(int fd, bool utf8) {
struct console_font_op cfo = {
- .op = KD_FONT_OP_GET, .flags = 0,
- .width = 32, .height = 32,
- .charcount = 512,
+ .op = KD_FONT_OP_GET,
+ .width = UINT_MAX, .height = UINT_MAX,
+ .charcount = UINT_MAX,
};
struct vt_stat vcs = {};
struct unimapinit adv = {};
@@ -225,12 +225,6 @@ static void setup_remaining_vcs(int fd, bool utf8) {
return;
}
- fontbuf = malloc(cfo.width * cfo.height * cfo.charcount / 8);
- if (!fontbuf) {
- log_oom();
- return;
- }
-
/* get active, and 16 bit mask of used VT numbers */
r = ioctl(fd, VT_GETSTATE, &vcs);
if (r < 0) {
@@ -238,21 +232,47 @@ static void setup_remaining_vcs(int fd, bool utf8) {
return;
}
- /* get fonts from source console */
- cfo.data = fontbuf;
+ /* get metadata of the current font (width, height, count) */
r = ioctl(fd, KDFONTOP, &cfo);
if (r < 0)
- log_warning_errno(errno, "KD_FONT_OP_GET failed, fonts will not be copied: %m");
+ log_warning_errno(errno, "KD_FONT_OP_GET failed while trying to get the font metadata: %m");
else {
- unimapd.entries = unipairs;
- unimapd.entry_ct = USHRT_MAX;
- r = ioctl(fd, GIO_UNIMAP, &unimapd);
- if (r < 0)
- log_warning_errno(errno, "GIO_UNIMAP failed, fonts will not be copied: %m");
- else
- cfo.op = KD_FONT_OP_SET;
+ /* verify parameter sanity first */
+ if (cfo.width > 32 || cfo.height > 32 || cfo.charcount > 512)
+ log_warning("Invalid font metadata - width: %u (max 32), height: %u (max 32), count: %u (max 512)",
+ cfo.width, cfo.height, cfo.charcount);
+ else {
+ /*
+ * Console fonts supported by the kernel are limited in size to 32 x 32 and maximum 512
+ * characters. Thus with 1 bit per pixel it requires up to 65536 bytes. The height always
+ * requries 32 per glyph, regardless of the actual height - see the comment above #define
+ * max_font_size 65536 in drivers/tty/vt/vt.c for more details.
+ */
+ fontbuf = malloc((cfo.width + 7) / 8 * 32 * cfo.charcount);
+ if (!fontbuf) {
+ log_oom();
+ return;
+ }
+ /* get fonts from source console */
+ cfo.data = fontbuf;
+ r = ioctl(fd, KDFONTOP, &cfo);
+ if (r < 0)
+ log_warning_errno(errno, "KD_FONT_OP_GET failed while trying to read the font data: %m");
+ else {
+ unimapd.entries = unipairs;
+ unimapd.entry_ct = USHRT_MAX;
+ r = ioctl(fd, GIO_UNIMAP, &unimapd);
+ if (r < 0)
+ log_warning_errno(errno, "GIO_UNIMAP failed while trying to read unicode mappings: %m");
+ else
+ cfo.op = KD_FONT_OP_SET;
+ }
+ }
}
+ if (cfo.op != KD_FONT_OP_SET)
+ log_warning("Fonts will not be copied to remaining consoles");
+
for (i = 1; i <= 63; i++) {
char ttyname[strlen("/dev/tty") + DECIMAL_STR_MAX(int)];
_cleanup_close_ int fd_d = -1;