mirror of
https://github.com/systemd/systemd-stable.git
synced 2025-02-01 05:47:04 +03:00
Merge pull request #4442 from keszybz/detect-virt-userns
detect-virt: add --private-users switch to check if a userns is active; add Condition=private-users
This commit is contained in:
commit
492466c1b5
@ -50,7 +50,8 @@
|
||||
|
||||
<refsynopsisdiv>
|
||||
<cmdsynopsis>
|
||||
<command>systemd-detect-virt <arg choice="opt" rep="repeat">OPTIONS</arg></command>
|
||||
<command>systemd-detect-virt</command>
|
||||
<arg choice="opt" rep="repeat">OPTIONS</arg>
|
||||
</cmdsynopsis>
|
||||
</refsynopsisdiv>
|
||||
|
||||
@ -217,6 +218,16 @@
|
||||
environment or not.</para></listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry>
|
||||
<term><option>--private-users</option></term>
|
||||
|
||||
<listitem><para>Detect whether invoked in a user namespace. In this mode, no
|
||||
output is written, but the return value indicates whether the process was invoked
|
||||
inside of a user namespace or not. See
|
||||
<citerefentry project='man-pages'><refentrytitle>user_namespaces</refentrytitle><manvolnum>7</manvolnum></citerefentry>
|
||||
for more information.</para></listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry>
|
||||
<term><option>-q</option></term>
|
||||
<term><option>--quiet</option></term>
|
||||
@ -243,7 +254,8 @@
|
||||
<para>
|
||||
<citerefentry><refentrytitle>systemd</refentrytitle><manvolnum>1</manvolnum></citerefentry>,
|
||||
<citerefentry><refentrytitle>systemd-nspawn</refentrytitle><manvolnum>1</manvolnum></citerefentry>,
|
||||
<citerefentry><refentrytitle>chroot</refentrytitle><manvolnum>2</manvolnum></citerefentry>
|
||||
<citerefentry><refentrytitle>chroot</refentrytitle><manvolnum>2</manvolnum></citerefentry>,
|
||||
<citerefentry project='man-pages'><refentrytitle>namespaces</refentrytitle><manvolnum>7</manvolnum></citerefentry>
|
||||
</para>
|
||||
</refsect1>
|
||||
|
||||
|
@ -908,7 +908,8 @@
|
||||
<varname>systemd-nspawn</varname>,
|
||||
<varname>docker</varname>,
|
||||
<varname>rkt</varname> to test
|
||||
against a specific implementation. See
|
||||
against a specific implementation, or
|
||||
<varname>private-users</varname> to check whether we are running in a user namespace. See
|
||||
<citerefentry><refentrytitle>systemd-detect-virt</refentrytitle><manvolnum>1</manvolnum></citerefentry>
|
||||
for a full list of known virtualization technologies and their
|
||||
identifiers. If multiple virtualization technologies are
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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_;
|
||||
|
@ -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();
|
||||
|
@ -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));
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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);
|
||||
|
||||
|
@ -13,6 +13,7 @@ DefaultDependencies=no
|
||||
Before=sysinit.target
|
||||
ConditionPathExists=/sys/kernel/mm/hugepages
|
||||
ConditionCapability=CAP_SYS_ADMIN
|
||||
ConditionVirtualization=!private-users
|
||||
|
||||
[Mount]
|
||||
What=hugetlbfs
|
||||
|
Loading…
x
Reference in New Issue
Block a user