{ "VMwareVMware", VIRTUALIZATION_VMWARE },
/* http://msdn.microsoft.com/en-us/library/ff542428.aspx */
{ "Microsoft Hv", VIRTUALIZATION_MICROSOFT },
+ /* https://wiki.freebsd.org/bhyve */
+ { "bhyve bhyve ", VIRTUALIZATION_BHYVE },
};
uint32_t eax, ecx;
{ "Xen", VIRTUALIZATION_XEN },
{ "Bochs", VIRTUALIZATION_BOCHS },
{ "Parallels", VIRTUALIZATION_PARALLELS },
+ /* https://wiki.freebsd.org/bhyve */
+ { "BHYVE", VIRTUALIZATION_BHYVE },
};
unsigned i;
int r;
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;
+}
#endif // 0
int running_in_chroot(void) {
- int ret;
+ _cleanup_free_ char *self_mnt = NULL, *pid1_mnt = NULL;
+ int r;
- ret = files_same("/proc/1/root", "/");
- if (ret < 0)
- return ret;
+ /* Try to detect whether we are running in a chroot() environment. Specifically, check whether we have a
+ * different root directory than PID 1, even though we live in the same mount namespace as it. */
+
+#if 0 /// elogind does not allow to ignore chroots, we are never init!
+ if (getenv_bool("SYSTEMD_IGNORE_CHROOT") > 0)
+ return 0;
+#endif // 0
+
+ r = files_same("/proc/1/root", "/");
+ if (r < 0)
+ return r;
+ if (r > 0)
+ return 0;
+
+ r = readlink_malloc("/proc/self/ns/mnt", &self_mnt);
+ if (r < 0)
+ return r;
+
+ r = readlink_malloc("/proc/1/ns/mnt", &pid1_mnt);
+ if (r < 0)
+ return r;
- return ret == 0;
+ return streq(self_mnt, pid1_mnt); /* Only if we live in the same namespace! */
}
static const char *const virtualization_table[_VIRTUALIZATION_MAX] = {
[VIRTUALIZATION_MICROSOFT] = "microsoft",
[VIRTUALIZATION_ZVM] = "zvm",
[VIRTUALIZATION_PARALLELS] = "parallels",
+ [VIRTUALIZATION_BHYVE] = "bhyve",
[VIRTUALIZATION_VM_OTHER] = "vm-other",
[VIRTUALIZATION_SYSTEMD_NSPAWN] = "systemd-nspawn",