• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

systemd / systemd / 15263807472

26 May 2025 08:53PM UTC coverage: 72.046% (-0.002%) from 72.048%
15263807472

push

github

yuwata
src/core/manager.c: log preset activity on first boot

This gives us a little more information about what units were enabled
or disabled on that first boot and will be useful for OS developers
tracking down the source of unit state.

An example with this enabled looks like:

```
NET: Registered PF_VSOCK protocol family
systemd[1]: Applying preset policy.
systemd[1]: Unit /etc/systemd/system/dnsmasq.service is masked, ignoring.
systemd[1]: Unit /etc/systemd/system/systemd-repart.service is masked, ignoring.
systemd[1]: Removed '/etc/systemd/system/sockets.target.wants/systemd-resolved-monitor.socket'.
systemd[1]: Removed '/etc/systemd/system/sockets.target.wants/systemd-resolved-varlink.socket'.
systemd[1]: Created symlink '/etc/systemd/system/multi-user.target.wants/var-mnt-workdir.mount' → '/etc/systemd/system/var-mnt-workdir.mount'.
systemd[1]: Created symlink '/etc/systemd/system/multi-user.target.wants/var-mnt-workdir\x2dtmp.mount' → '/etc/systemd/system/var-mnt-workdir\x2dtmp.mount'.
systemd[1]: Created symlink '/etc/systemd/system/afterburn-sshkeys.target.requires/afterburn-sshkeys@core.service' → '/usr/lib/systemd/system/afterburn-sshkeys@.service'.
systemd[1]: Created symlink '/etc/systemd/system/sockets.target.wants/systemd-resolved-varlink.socket' → '/usr/lib/systemd/system/systemd-resolved-varlink.socket'.
systemd[1]: Created symlink '/etc/systemd/system/sockets.target.wants/systemd-resolved-monitor.socket' → '/usr/lib/systemd/system/systemd-resolved-monitor.socket'.
systemd[1]: Populated /etc with preset unit settings.
```

Considering it only happens on first boot and not on every boot I think
the extra information is worth the extra verbosity in the logs just for
that boot.

5 of 6 new or added lines in 1 file covered. (83.33%)

5463 existing lines in 165 files now uncovered.

299151 of 415222 relevant lines covered (72.05%)

702386.45 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

63.32
/src/basic/virt.c
1
/* SPDX-License-Identifier: LGPL-2.1-or-later */
2

3
#if defined(__i386__) || defined(__x86_64__)
4
#include <cpuid.h>
5
#endif
6
#include <stdlib.h>
7
#include <threads.h>
8
#include <unistd.h>
9

10
#include "alloc-util.h"
11
#include "dirent-util.h"
12
#include "env-util.h"
13
#include "extract-word.h"
14
#include "fd-util.h"
15
#include "fileio.h"
16
#include "log.h"
17
#include "namespace-util.h"
18
#include "parse-util.h"
19
#include "pidref.h"
20
#include "process-util.h"
21
#include "string-table.h"
22
#include "string-util.h"
23
#include "strv.h"
24
#include "virt.h"
25

26
enum {
27
      SMBIOS_VM_BIT_SET,
28
      SMBIOS_VM_BIT_UNSET,
29
      SMBIOS_VM_BIT_UNKNOWN,
30
};
31

32
static Virtualization detect_vm_cpuid(void) {
477✔
33

34
        /* CPUID is an x86 specific interface. */
35
#if defined(__i386__) || defined(__x86_64__)
36

37
        static const struct {
477✔
38
                const char sig[13];
39
                Virtualization id;
40
        } vm_table[] = {
41
                { "XenVMMXenVMM", VIRTUALIZATION_XEN       },
42
                { "KVMKVMKVM",    VIRTUALIZATION_KVM       }, /* qemu with KVM */
43
                { "Linux KVM Hv", VIRTUALIZATION_KVM       }, /* qemu with KVM + HyperV Enlightenments */
44
                { "TCGTCGTCGTCG", VIRTUALIZATION_QEMU      }, /* qemu without KVM */
45
                /* http://kb.vmware.com/selfservice/microsites/search.do?language=en_US&cmd=displayKC&externalId=1009458 */
46
                { "VMwareVMware", VIRTUALIZATION_VMWARE    },
47
                /* https://docs.microsoft.com/en-us/virtualization/hyper-v-on-windows/reference/tlfs */
48
                { "Microsoft Hv", VIRTUALIZATION_MICROSOFT },
49
                /* https://wiki.freebsd.org/bhyve */
50
                { "bhyve bhyve ", VIRTUALIZATION_BHYVE     },
51
                { "QNXQVMBSQG",   VIRTUALIZATION_QNX       },
52
                /* https://projectacrn.org */
53
                { "ACRNACRNACRN", VIRTUALIZATION_ACRN      },
54
                /* https://www.lockheedmartin.com/en-us/products/Hardened-Security-for-Intel-Processors.html */
55
                { "SRESRESRESRE", VIRTUALIZATION_SRE       },
56
                { "Apple VZ",     VIRTUALIZATION_APPLE     },
57
        };
58

59
        uint32_t eax, ebx, ecx, edx;
477✔
60
        bool hypervisor;
477✔
61

62
        /* http://lwn.net/Articles/301888/ */
63

64
        /* First detect whether there is a hypervisor */
65
        if (__get_cpuid(1, &eax, &ebx, &ecx, &edx) == 0)
477✔
66
                return VIRTUALIZATION_NONE;
477✔
67

68
        hypervisor = ecx & 0x80000000U;
477✔
69

70
        if (hypervisor) {
477✔
71
                union {
477✔
72
                        uint32_t sig32[3];
73
                        char text[13];
74
                } sig = {};
477✔
75

76
                /* There is a hypervisor, see what it is */
77
                __cpuid(0x40000000U, eax, ebx, ecx, edx);
477✔
78

79
                sig.sig32[0] = ebx;
477✔
80
                sig.sig32[1] = ecx;
477✔
81
                sig.sig32[2] = edx;
477✔
82

83
                log_debug("Virtualization found, CPUID=%s", sig.text);
477✔
84

85
                FOREACH_ELEMENT(vm, vm_table)
970✔
86
                        if (memcmp_nn(sig.text, sizeof(sig.text),
493✔
87
                                      vm->sig, sizeof(vm->sig)) == 0)
970✔
88
                                return vm->id;
477✔
89

UNCOV
90
                log_debug("Unknown virtualization with CPUID=%s. Add to vm_table[]?", sig.text);
×
UNCOV
91
                return VIRTUALIZATION_VM_OTHER;
×
92
        }
93
#endif
94
        log_debug("No virtualization found in CPUID");
×
95

96
        return VIRTUALIZATION_NONE;
97
}
98

99
static Virtualization detect_vm_device_tree(void) {
4✔
100
#if defined(__arm__) || defined(__aarch64__) || defined(__powerpc__) || defined(__powerpc64__) || defined(__riscv)
101
        _cleanup_free_ char *hvtype = NULL;
102
        int r;
103

104
        r = read_one_line_file("/proc/device-tree/hypervisor/compatible", &hvtype);
105
        if (r == -ENOENT) {
106
                if (access("/proc/device-tree/ibm,partition-name", F_OK) == 0 &&
107
                    access("/proc/device-tree/hmc-managed?", F_OK) == 0 &&
108
                    access("/proc/device-tree/chosen/qemu,graphic-width", F_OK) != 0)
109
                        return VIRTUALIZATION_POWERVM;
110

111
                _cleanup_closedir_ DIR *dir = opendir("/proc/device-tree");
112
                if (!dir) {
113
                        if (errno == ENOENT) {
114
                                log_debug_errno(errno, "/proc/device-tree/ does not exist");
115
                                return VIRTUALIZATION_NONE;
116
                        }
117
                        return log_debug_errno(errno, "Opening /proc/device-tree/ failed: %m");
118
                }
119

120
                FOREACH_DIRENT(de, dir, return log_debug_errno(errno, "Failed to enumerate /proc/device-tree/ contents: %m"))
121
                        if (strstr(de->d_name, "fw-cfg")) {
122
                                log_debug("Virtualization QEMU: \"fw-cfg\" present in /proc/device-tree/%s", de->d_name);
123
                                return VIRTUALIZATION_QEMU;
124
                        }
125

126
                _cleanup_free_ char *compat = NULL;
127
                r = read_one_line_file("/proc/device-tree/compatible", &compat);
128
                if (r < 0 && r != -ENOENT)
129
                        return log_debug_errno(r, "Failed to read /proc/device-tree/compatible: %m");
130
                if (r >= 0) {
131
                        if (streq(compat, "qemu,pseries")) {
132
                                log_debug("Virtualization %s found in /proc/device-tree/compatible", compat);
133
                                return VIRTUALIZATION_QEMU;
134
                        }
135
                        if (streq(compat, "linux,dummy-virt")) {
136
                                /* https://www.kernel.org/doc/Documentation/devicetree/bindings/arm/linux%2Cdummy-virt.yaml */
137
                                log_debug("Generic virtualization %s found in /proc/device-tree/compatible", compat);
138
                                return VIRTUALIZATION_VM_OTHER;
139
                        }
140
                }
141

142
                log_debug("No virtualization found in /proc/device-tree/*");
143
                return VIRTUALIZATION_NONE;
144
        } else if (r < 0)
145
                return r;
146

147
        log_debug("Virtualization %s found in /proc/device-tree/hypervisor/compatible", hvtype);
148
        if (streq(hvtype, "linux,kvm"))
149
                return VIRTUALIZATION_KVM;
150
        else if (strstr(hvtype, "xen"))
151
                return VIRTUALIZATION_XEN;
152
        else if (strstr(hvtype, "vmware"))
153
                return VIRTUALIZATION_VMWARE;
154
        else
155
                return VIRTUALIZATION_VM_OTHER;
156
#else
157
        log_debug("This platform does not support /proc/device-tree");
4✔
158
        return VIRTUALIZATION_NONE;
4✔
159
#endif
160
}
161

162
#if defined(__i386__) || defined(__x86_64__) || defined(__arm__) || defined(__aarch64__) || defined(__loongarch_lp64) || defined(__riscv)
163
static Virtualization detect_vm_dmi_vendor(void) {
477✔
164
        static const char* const dmi_vendors[] = {
477✔
165
                "/sys/class/dmi/id/product_name", /* Test this before sys_vendor to detect KVM over QEMU */
166
                "/sys/class/dmi/id/sys_vendor",
167
                "/sys/class/dmi/id/board_vendor",
168
                "/sys/class/dmi/id/bios_vendor",
169
                "/sys/class/dmi/id/product_version", /* For Hyper-V VMs test */
170
                NULL
171
        };
172

173
        static const struct {
477✔
174
                const char *vendor;
175
                Virtualization id;
176
        } dmi_vendor_table[] = {
177
                { "KVM",                   VIRTUALIZATION_KVM       },
178
                { "OpenStack",             VIRTUALIZATION_KVM       }, /* Detect OpenStack instance as KVM in non x86 architecture */
179
                { "KubeVirt",              VIRTUALIZATION_KVM       }, /* Detect KubeVirt instance as KVM in non x86 architecture */
180
                { "Amazon EC2",            VIRTUALIZATION_AMAZON    },
181
                { "QEMU",                  VIRTUALIZATION_QEMU      },
182
                { "VMware",                VIRTUALIZATION_VMWARE    }, /* https://kb.vmware.com/s/article/1009458 */
183
                { "VMW",                   VIRTUALIZATION_VMWARE    },
184
                { "innotek GmbH",          VIRTUALIZATION_ORACLE    },
185
                { "VirtualBox",            VIRTUALIZATION_ORACLE    },
186
                { "Oracle Corporation",    VIRTUALIZATION_ORACLE    }, /* Detect VirtualBox on some proprietary systems via the board_vendor */
187
                { "Xen",                   VIRTUALIZATION_XEN       },
188
                { "Bochs",                 VIRTUALIZATION_BOCHS     },
189
                { "Parallels",             VIRTUALIZATION_PARALLELS },
190
                /* https://wiki.freebsd.org/bhyve */
191
                { "BHYVE",                 VIRTUALIZATION_BHYVE     },
192
                { "Hyper-V",               VIRTUALIZATION_MICROSOFT },
193
                { "Apple Virtualization",  VIRTUALIZATION_APPLE     },
194
                { "Google Compute Engine", VIRTUALIZATION_GOOGLE    }, /* https://cloud.google.com/run/docs/container-contract#sandbox */
195
        };
196
        int r;
477✔
197

198
        STRV_FOREACH(vendor, dmi_vendors) {
970✔
199
                _cleanup_free_ char *s = NULL;
966✔
200

201
                r = read_one_line_file(*vendor, &s);
966✔
202
                if (r < 0) {
966✔
UNCOV
203
                        if (r == -ENOENT)
×
UNCOV
204
                                continue;
×
205

206
                        return r;
207
                }
208

209
                FOREACH_ELEMENT(dmi_vendor, dmi_vendor_table)
11,239✔
210
                        if (startswith(s, dmi_vendor->vendor)) {
10,746✔
211
                                log_debug("Virtualization %s found in DMI (%s)", s, *vendor);
473✔
212
                                return dmi_vendor->id;
473✔
213
                        }
214
        }
215
        log_debug("No virtualization found in DMI vendor table.");
4✔
216
        return VIRTUALIZATION_NONE;
217
}
218

219
static int detect_vm_smbios(void) {
4✔
220
        /* The SMBIOS BIOS Characteristics Extension Byte 2 (Section 2.1.2.2 of
221
         * https://www.dmtf.org/sites/default/files/standards/documents/DSP0134_3.4.0.pdf), specifies that
222
         * the 4th bit being set indicates a VM. The BIOS Characteristics table is exposed via the kernel in
223
         * /sys/firmware/dmi/entries/0-0. Note that in the general case, this bit being unset should not
224
         * imply that the system is running on bare-metal.  For example, QEMU 3.1.0 (with or without KVM)
225
         * with SeaBIOS does not set this bit. */
226
        _cleanup_free_ char *s = NULL;
4✔
227
        size_t readsize;
4✔
228
        int r;
4✔
229

230
        r = read_full_virtual_file("/sys/firmware/dmi/entries/0-0/raw", &s, &readsize);
4✔
231
        if (r < 0) {
4✔
232
                log_debug_errno(r, "Unable to read /sys/firmware/dmi/entries/0-0/raw, "
4✔
233
                                "using the virtualization information found in DMI vendor table, ignoring: %m");
234
                return SMBIOS_VM_BIT_UNKNOWN;
4✔
235
        }
UNCOV
236
        if (readsize < 20 || s[1] < 20) {
×
237
                /* The spec indicates that byte 1 contains the size of the table, 0x12 + the number of
238
                 * extension bytes. The data we're interested in is in extension byte 2, which would be at
239
                 * 0x13. If we didn't read that much data, or if the BIOS indicates that we don't have that
240
                 * much data, we don't infer anything from the SMBIOS. */
UNCOV
241
                log_debug("Only read %zu bytes from /sys/firmware/dmi/entries/0-0/raw (expected 20). "
×
242
                          "Using the virtualization information found in DMI vendor table.", readsize);
UNCOV
243
                return SMBIOS_VM_BIT_UNKNOWN;
×
244
        }
245

UNCOV
246
        uint8_t byte = (uint8_t) s[19];
×
247
        if (byte & (1U<<4)) {
×
UNCOV
248
                log_debug("DMI BIOS Extension table indicates virtualization.");
×
UNCOV
249
                return SMBIOS_VM_BIT_SET;
×
250
        }
251
        log_debug("DMI BIOS Extension table does not indicate virtualization.");
×
252
        return SMBIOS_VM_BIT_UNSET;
253
}
254
#endif /* defined(__i386__) || defined(__x86_64__) || defined(__arm__) || defined(__aarch64__) || defined(__loongarch_lp64) */
255

256
static Virtualization detect_vm_dmi(void) {
477✔
257
#if defined(__i386__) || defined(__x86_64__) || defined(__arm__) || defined(__aarch64__) || defined(__loongarch_lp64)
258

259
        int r;
477✔
260
        r = detect_vm_dmi_vendor();
477✔
261

262
        /* The DMI vendor tables in /sys/class/dmi/id don't help us distinguish between Amazon EC2
263
         * virtual machines and bare-metal instances, so we need to look at SMBIOS. */
264
        if (r == VIRTUALIZATION_AMAZON) {
477✔
UNCOV
265
                switch (detect_vm_smbios()) {
×
266
                case SMBIOS_VM_BIT_SET:
267
                        return VIRTUALIZATION_AMAZON;
UNCOV
268
                case SMBIOS_VM_BIT_UNSET:
×
269
                        return VIRTUALIZATION_NONE;
×
UNCOV
270
                case SMBIOS_VM_BIT_UNKNOWN: {
×
271
                        /* The DMI information we are after is only accessible to the root user,
272
                         * so we fallback to using the product name which is less restricted
273
                         * to distinguish metal systems from virtualized instances */
274
                        _cleanup_free_ char *s = NULL;
×
UNCOV
275
                        const char *e;
×
276

UNCOV
277
                        r = read_full_virtual_file("/sys/class/dmi/id/product_name", &s, NULL);
×
278
                        /* In EC2, virtualized is much more common than metal, so if for some reason
279
                         * we fail to read the DMI data, assume we are virtualized. */
UNCOV
280
                        if (r < 0) {
×
281
                                log_debug_errno(r, "Can't read /sys/class/dmi/id/product_name,"
×
282
                                                " assuming virtualized: %m");
UNCOV
283
                                return VIRTUALIZATION_AMAZON;
×
284
                        }
285
                        e = strstrafter(truncate_nl(s), ".metal");
×
UNCOV
286
                        if (e && IN_SET(*e, 0, '-')) {
×
287
                                log_debug("DMI product name has '.metal', assuming no virtualization");
×
UNCOV
288
                                return VIRTUALIZATION_NONE;
×
289
                        } else
290
                                return VIRTUALIZATION_AMAZON;
291
                }
292
                default:
×
UNCOV
293
                        assert_not_reached();
×
294
              }
295
        }
296

297
        /* If we haven't identified a VM, but the firmware indicates that there is one, indicate as much. We
298
         * have no further information about what it is. */
299
        if (r == VIRTUALIZATION_NONE && detect_vm_smbios() == SMBIOS_VM_BIT_SET)
477✔
UNCOV
300
                return VIRTUALIZATION_VM_OTHER;
×
301
        return r;
302
#else
303
        return VIRTUALIZATION_NONE;
304
#endif
305
}
306

307
#define XENFEAT_dom0 11 /* xen/include/public/features.h */
308
#define PATH_FEATURES "/sys/hypervisor/properties/features"
309
/* Returns -errno, or 0 for domU, or 1 for dom0 */
UNCOV
310
static int detect_vm_xen_dom0(void) {
×
UNCOV
311
        _cleanup_free_ char *domcap = NULL;
×
UNCOV
312
        int r;
×
313

314
        r = read_one_line_file(PATH_FEATURES, &domcap);
×
315
        if (r < 0 && r != -ENOENT)
×
316
                return r;
UNCOV
317
        if (r >= 0) {
×
318
                unsigned long features;
×
319

320
                /* Here, we need to use sscanf() instead of safe_atoul()
321
                 * as the string lacks the leading "0x". */
322
                r = sscanf(domcap, "%lx", &features);
×
UNCOV
323
                if (r == 1) {
×
UNCOV
324
                        r = !!(features & (1U << XENFEAT_dom0));
×
UNCOV
325
                        log_debug("Virtualization XEN, found %s with value %08lx, "
×
326
                                  "XENFEAT_dom0 (indicating the 'hardware domain') is%s set.",
327
                                  PATH_FEATURES, features, r ? "" : " not");
328
                        return r;
×
329
                }
UNCOV
330
                log_debug("Virtualization XEN, found %s, unhandled content '%s'",
×
331
                          PATH_FEATURES, domcap);
332
        }
333

334
        r = read_one_line_file("/proc/xen/capabilities", &domcap);
×
UNCOV
335
        if (r == -ENOENT) {
×
UNCOV
336
                log_debug("Virtualization XEN because /proc/xen/capabilities does not exist");
×
UNCOV
337
                return 0;
×
338
        }
339
        if (r < 0)
×
340
                return r;
341

UNCOV
342
        for (const char *i = domcap;;) {
×
343
                _cleanup_free_ char *cap = NULL;
×
344

UNCOV
345
                r = extract_first_word(&i, &cap, ",", 0);
×
346
                if (r < 0)
×
347
                        return r;
UNCOV
348
                if (r == 0) {
×
349
                        log_debug("Virtualization XEN DomU found (/proc/xen/capabilities)");
×
350
                        return 0;
×
351
                }
352

353
                if (streq(cap, "control_d")) {
×
354
                        log_debug("Virtualization XEN Dom0 ignored (/proc/xen/capabilities)");
×
UNCOV
355
                        return 1;
×
356
                }
357
        }
358
}
359

360
static Virtualization detect_vm_xen(void) {
477✔
361
        /* The presence of /proc/xen indicates some form of a Xen domain
362
           The check for Dom0 is handled outside this function */
363
        if (access("/proc/xen", F_OK) < 0) {
477✔
364
                log_debug("Virtualization XEN not found, /proc/xen does not exist");
477✔
365
                return VIRTUALIZATION_NONE;
477✔
366
        }
UNCOV
367
        log_debug("Virtualization XEN found (/proc/xen exists)");
×
368
        return VIRTUALIZATION_XEN;
369
}
370

371
static Virtualization detect_vm_hypervisor(void) {
4✔
372
        _cleanup_free_ char *hvtype = NULL;
4✔
373
        int r;
4✔
374

375
        r = read_one_line_file("/sys/hypervisor/type", &hvtype);
4✔
376
        if (r == -ENOENT)
4✔
377
                return VIRTUALIZATION_NONE;
UNCOV
378
        if (r < 0)
×
379
                return r;
380

UNCOV
381
        log_debug("Virtualization %s found in /sys/hypervisor/type", hvtype);
×
382

UNCOV
383
        if (streq(hvtype, "xen"))
×
384
                return VIRTUALIZATION_XEN;
385
        else
UNCOV
386
                return VIRTUALIZATION_VM_OTHER;
×
387
}
388

389
static Virtualization detect_vm_uml(void) {
477✔
390
        _cleanup_fclose_ FILE *f = NULL;
477✔
391
        int r;
477✔
392

393
        /* Detect User-Mode Linux by reading /proc/cpuinfo */
394
        f = fopen("/proc/cpuinfo", "re");
477✔
395
        if (!f) {
477✔
UNCOV
396
                if (errno == ENOENT) {
×
UNCOV
397
                        log_debug("/proc/cpuinfo not found, assuming no UML virtualization.");
×
UNCOV
398
                        return VIRTUALIZATION_NONE;
×
399
                }
400
                return -errno;
×
401
        }
402

403
        for (;;) {
1,431✔
404
                _cleanup_free_ char *line = NULL;
477✔
405
                const char *t;
954✔
406

407
                r = read_line(f, LONG_LINE_MAX, &line);
954✔
408
                if (r < 0)
954✔
409
                        return r;
410
                if (r == 0)
954✔
411
                        break;
412

413
                t = startswith(line, "vendor_id\t: ");
954✔
414
                if (t) {
954✔
415
                        if (startswith(t, "User Mode Linux")) {
477✔
UNCOV
416
                                log_debug("UML virtualization found in /proc/cpuinfo");
×
UNCOV
417
                                return VIRTUALIZATION_UML;
×
418
                        }
419

420
                        break;
421
                }
422
        }
423

424
        log_debug("UML virtualization not found in /proc/cpuinfo.");
477✔
425
        return VIRTUALIZATION_NONE;
426
}
427

428
static Virtualization detect_vm_zvm(void) {
4✔
429

430
#if defined(__s390__)
431
        _cleanup_free_ char *t = NULL;
432
        int r;
433

434
        r = get_proc_field("/proc/sysinfo", "VM00 Control Program", &t);
435
        if (IN_SET(r, -ENOENT, -ENODATA))
436
                return VIRTUALIZATION_NONE;
437
        if (r < 0)
438
                return r;
439

440
        log_debug("Virtualization %s found in /proc/sysinfo", t);
441
        if (streq(t, "z/VM"))
442
                return VIRTUALIZATION_ZVM;
443
        else
444
                return VIRTUALIZATION_KVM;
445
#else
446
        log_debug("This platform does not support /proc/sysinfo");
4✔
447
        return VIRTUALIZATION_NONE;
4✔
448
#endif
449
}
450

451
/* Returns a short identifier for the various VM implementations */
452
Virtualization detect_vm(void) {
1,259✔
453
        static thread_local Virtualization cached_found = _VIRTUALIZATION_INVALID;
1,259✔
454
        bool other = false, hyperv = false;
1,259✔
455
        int xen_dom0 = 0;
1,259✔
456
        Virtualization v, dmi;
1,259✔
457

458
        if (cached_found >= 0)
1,259✔
459
                return cached_found;
460

461
        /* We have to use the correct order here:
462
         *
463
         * → First, try to detect Oracle Virtualbox, Amazon EC2 Nitro, Parallels, and Google Compute Engine,
464
         *   even if they use KVM, as well as Xen, even if it cloaks as Microsoft Hyper-V. Attempt to detect
465
         *   UML at this stage too, since it runs as a user-process nested inside other VMs. Also check for
466
         *   Xen now, because Xen PV mode does not override CPUID when nested inside another hypervisor.
467
         *
468
         * → Second, try to detect from CPUID. This will report KVM for whatever software is used even if
469
         *   info in DMI is overwritten.
470
         *
471
         * → Third, try to detect from DMI. */
472

473
        dmi = detect_vm_dmi();
477✔
474
        if (IN_SET(dmi,
477✔
475
                   VIRTUALIZATION_ORACLE,
476
                   VIRTUALIZATION_XEN,
477
                   VIRTUALIZATION_AMAZON,
478
                   VIRTUALIZATION_PARALLELS,
479
                   VIRTUALIZATION_GOOGLE)) {
UNCOV
480
                v = dmi;
×
UNCOV
481
                goto finish;
×
482
        }
483

484
        /* Detect UML */
485
        v = detect_vm_uml();
477✔
486
        if (v < 0)
477✔
487
                return v;
488
        if (v != VIRTUALIZATION_NONE)
477✔
UNCOV
489
                goto finish;
×
490

491
        /* Detect Xen */
492
        v = detect_vm_xen();
477✔
493
        if (v < 0)
477✔
494
                return v;
495
        if (v == VIRTUALIZATION_XEN) {
477✔
496
                 /* If we are Dom0, then we expect to not report as a VM. However, as we might be nested
497
                  * inside another hypervisor which can be detected via the CPUID check, wait to report this
498
                  * until after the CPUID check. */
UNCOV
499
                xen_dom0 = detect_vm_xen_dom0();
×
UNCOV
500
                if (xen_dom0 < 0)
×
501
                        return xen_dom0;
UNCOV
502
                if (xen_dom0 == 0)
×
503
                        goto finish;
×
504
        } else if (v != VIRTUALIZATION_NONE)
477✔
UNCOV
505
                assert_not_reached();
×
506

507
        /* Detect from CPUID */
508
        v = detect_vm_cpuid();
477✔
509
        if (v < 0)
477✔
510
                return v;
511
        if (v == VIRTUALIZATION_MICROSOFT)
477✔
512
                /* QEMU sets the CPUID string to hyperv's, in case it provides hyperv enlightenments. Let's
513
                 * hence not return Microsoft here but just use the other mechanisms first to make a better
514
                 * decision. */
515
                hyperv = true;
516
        else if (v == VIRTUALIZATION_VM_OTHER)
473✔
517
                other = true;
518
        else if (v != VIRTUALIZATION_NONE)
473✔
519
                goto finish;
473✔
520

521
        /* If we are in Dom0 and have not yet finished, finish with the result of detect_vm_cpuid */
522
        if (xen_dom0 > 0)
4✔
UNCOV
523
                goto finish;
×
524

525
        /* Now, let's get back to DMI */
526
        if (dmi < 0)
4✔
527
                return dmi;
528
        if (dmi == VIRTUALIZATION_VM_OTHER)
4✔
529
                other = true;
530
        else if (dmi != VIRTUALIZATION_NONE) {
4✔
UNCOV
531
                v = dmi;
×
UNCOV
532
                goto finish;
×
533
        }
534

535
        /* Check high-level hypervisor sysfs file */
536
        v = detect_vm_hypervisor();
4✔
537
        if (v < 0)
4✔
538
                return v;
539
        if (v == VIRTUALIZATION_VM_OTHER)
4✔
540
                other = true;
541
        else if (v != VIRTUALIZATION_NONE)
4✔
UNCOV
542
                goto finish;
×
543

544
        v = detect_vm_device_tree();
4✔
545
        if (v < 0)
4✔
546
                return v;
547
        if (v == VIRTUALIZATION_VM_OTHER)
4✔
548
                other = true;
549
        else if (v != VIRTUALIZATION_NONE)
4✔
UNCOV
550
                goto finish;
×
551

552
        v = detect_vm_zvm();
4✔
553
        if (v < 0)
4✔
554
                return v;
555

556
finish:
4✔
557
        /* None of the checks above gave us a clear answer, hence let's now use fallback logic: if hyperv
558
         * enlightenments are available but the VMM wasn't recognized as anything yet, it's probably
559
         * Microsoft. */
560
        if (v == VIRTUALIZATION_NONE) {
477✔
561
                if (hyperv)
4✔
562
                        v = VIRTUALIZATION_MICROSOFT;
UNCOV
563
                else if (other)
×
UNCOV
564
                        v = VIRTUALIZATION_VM_OTHER;
×
565
        }
566

567
        cached_found = v;
477✔
568
        log_debug("Found VM virtualization %s", virtualization_to_string(v));
477✔
569
        return v;
570
}
571

572
static const char *const container_table[_VIRTUALIZATION_MAX] = {
573
        [VIRTUALIZATION_LXC]            = "lxc",
574
        [VIRTUALIZATION_LXC_LIBVIRT]    = "lxc-libvirt",
575
        [VIRTUALIZATION_SYSTEMD_NSPAWN] = "systemd-nspawn",
576
        [VIRTUALIZATION_DOCKER]         = "docker",
577
        [VIRTUALIZATION_PODMAN]         = "podman",
578
        [VIRTUALIZATION_RKT]            = "rkt",
579
        [VIRTUALIZATION_WSL]            = "wsl",
580
        [VIRTUALIZATION_PROOT]          = "proot",
581
        [VIRTUALIZATION_POUCH]          = "pouch",
582
};
583

584
DEFINE_PRIVATE_STRING_TABLE_LOOKUP_FROM_STRING(container, int);
3,796✔
585

586
static int running_in_pidns(void) {
4,706✔
587
        int r;
4,706✔
588

589
        r = namespace_is_init(NAMESPACE_PID);
4,706✔
590
        if (r < 0)
4,706✔
591
                return log_debug_errno(r, "Failed to test if in root PID namespace, ignoring: %m");
12✔
592

593
        return !r;
4,694✔
594
}
595

596
static Virtualization detect_container_files(void) {
4,706✔
597
        static const struct {
4,706✔
598
                const char *file_path;
599
                Virtualization id;
600
        } container_file_table[] = {
601
                /* https://github.com/containers/podman/issues/6192 */
602
                /* https://github.com/containers/podman/issues/3586#issuecomment-661918679 */
603
                { "/run/.containerenv", VIRTUALIZATION_PODMAN },
604
                /* https://github.com/moby/moby/issues/18355 */
605
                /* Docker must be the last in this table, see below. */
606
                { "/.dockerenv",        VIRTUALIZATION_DOCKER },
607
        };
608

609
        FOREACH_ELEMENT(file, container_file_table) {
14,118✔
610
                if (access(file->file_path, F_OK) >= 0)
9,412✔
UNCOV
611
                        return file->id;
×
612

613
                if (errno != ENOENT)
9,412✔
614
                        log_debug_errno(errno,
9,412✔
615
                                        "Checking if %s exists failed, ignoring: %m",
616
                                        file->file_path);
617
        }
618

619
        return VIRTUALIZATION_NONE;
620
}
621

622
Virtualization detect_container(void) {
205,313✔
623
        static thread_local Virtualization cached_found = _VIRTUALIZATION_INVALID;
205,313✔
624
        _cleanup_free_ char *m = NULL, *o = NULL, *p = NULL;
205,313✔
625
        const char *e = NULL;
205,313✔
626
        Virtualization v;
205,313✔
627
        int r;
205,313✔
628

629
        if (cached_found >= 0)
205,313✔
630
                return cached_found;
631

632
        /* /proc/vz exists in container and outside of the container, /proc/bc only outside of the container. */
633
        if (access("/proc/vz", F_OK) < 0) {
8,502✔
634
                if (errno != ENOENT)
8,502✔
UNCOV
635
                        log_debug_errno(errno, "Failed to check if /proc/vz exists, ignoring: %m");
×
UNCOV
636
        } else if (access("/proc/bc", F_OK) < 0) {
×
UNCOV
637
                if (errno == ENOENT) {
×
UNCOV
638
                        v = VIRTUALIZATION_OPENVZ;
×
639
                        goto finish;
×
640
                }
641

642
                log_debug_errno(errno, "Failed to check if /proc/bc exists, ignoring: %m");
×
643
        }
644

645
        /* "Official" way of detecting WSL https://github.com/Microsoft/WSL/issues/423#issuecomment-221627364 */
646
        r = read_one_line_file("/proc/sys/kernel/osrelease", &o);
8,502✔
647
        if (r < 0)
8,502✔
648
                log_debug_errno(r, "Failed to read /proc/sys/kernel/osrelease, ignoring: %m");
12✔
649
        else if (strstr(o, "Microsoft") || strstr(o, "WSL")) {
8,490✔
UNCOV
650
                v = VIRTUALIZATION_WSL;
×
UNCOV
651
                goto finish;
×
652
        }
653

654
        /* proot doesn't use PID namespacing, so we can just check if we have a matching tracer for this
655
         * invocation without worrying about it being elsewhere.
656
         */
657
        r = get_proc_field("/proc/self/status", "TracerPid", &p);
8,502✔
658
        if (r < 0)
8,502✔
659
                log_debug_errno(r, "Failed to read our own trace PID, ignoring: %m");
12✔
660
        else if (!streq(p, "0")) {
8,490✔
UNCOV
661
                pid_t ptrace_pid;
×
662

UNCOV
663
                r = parse_pid(p, &ptrace_pid);
×
UNCOV
664
                if (r < 0)
×
665
                        log_debug_errno(r, "Failed to parse our own tracer PID, ignoring: %m");
×
666
                else {
667
                        _cleanup_free_ char *ptrace_comm = NULL;
×
668
                        const char *pf;
×
669

UNCOV
670
                        pf = procfs_file_alloca(ptrace_pid, "comm");
×
671
                        r = read_one_line_file(pf, &ptrace_comm);
×
672
                        if (r < 0)
×
UNCOV
673
                                log_debug_errno(r, "Failed to read %s, ignoring: %m", pf);
×
674
                        else if (startswith(ptrace_comm, "proot")) {
×
675
                                v = VIRTUALIZATION_PROOT;
×
676
                                goto finish;
×
677
                        }
678
                }
679
        }
680

681
        /* The container manager might have placed this in the /run/host/ hierarchy for us, which is best
682
         * because we can be consumed just like that, without special privileges. */
683
        r = read_one_line_file("/run/host/container-manager", &m);
8,502✔
684
        if (r > 0) {
8,502✔
685
                e = m;
3,796✔
686
                goto translate_name;
3,796✔
687
        }
688
        if (!IN_SET(r, -ENOENT, 0))
4,706✔
UNCOV
689
                return log_debug_errno(r, "Failed to read /run/host/container-manager: %m");
×
690

691
        if (getpid_cached() == 1) {
4,706✔
692
                /* If we are PID 1 we can just check our own environment variable, and that's authoritative.
693
                 * We distinguish three cases:
694
                 * - the variable is not defined → we jump to other checks
695
                 * - the variable is defined to an empty value → we are not in a container
696
                 * - anything else → some container, either one of the known ones or "container-other"
697
                 */
698
                e = getenv("container");
21✔
699
                if (!e)
21✔
700
                        goto check_files;
21✔
UNCOV
701
                if (isempty(e)) {
×
UNCOV
702
                        v = VIRTUALIZATION_NONE;
×
UNCOV
703
                        goto finish;
×
704
                }
705

706
                goto translate_name;
×
707
        }
708

709
        /* Otherwise, PID 1 might have dropped this information into a file in /run. This is better than accessing
710
         * /proc/1/environ, since we don't need CAP_SYS_PTRACE for that. */
711
        r = read_one_line_file("/run/systemd/container", &m);
4,685✔
712
        if (r > 0) {
4,685✔
UNCOV
713
                e = m;
×
UNCOV
714
                goto translate_name;
×
715
        }
716
        if (!IN_SET(r, -ENOENT, 0))
4,685✔
717
                return log_debug_errno(r, "Failed to read /run/systemd/container: %m");
×
718

719
        /* Fallback for cases where PID 1 was not systemd (for example, cases where init=/bin/sh is used. */
720
        r = getenv_for_pid(1, "container", &m);
4,685✔
721
        if (r > 0) {
4,685✔
UNCOV
722
                e = m;
×
UNCOV
723
                goto translate_name;
×
724
        }
725
        if (r < 0) /* This only works if we have CAP_SYS_PTRACE, hence let's better ignore failures here */
4,685✔
726
                log_debug_errno(r, "Failed to read $container of PID 1, ignoring: %m");
1,853✔
727

728
check_files:
2,832✔
729
        /* Check for existence of some well-known files. We only do this after checking
730
         * for other specific container managers, otherwise we risk mistaking another
731
         * container manager for Docker: the /.dockerenv file could inadvertently end up
732
         * in a file system image. */
733
        v = detect_container_files();
4,706✔
734
        if (v < 0)
4,706✔
735
                return v;
736
        if (v != VIRTUALIZATION_NONE)
4,706✔
UNCOV
737
                goto finish;
×
738

739
        /* Finally, the root pid namespace has an hardcoded inode number of 0xEFFFFFFC since kernel 3.8, so
740
         * if all else fails we can check the inode number of our pid namespace and compare it. */
741
        if (running_in_pidns() > 0) {
4,706✔
742
                log_debug("Running in a pid namespace, assuming unknown container manager.");
1✔
743
                v = VIRTUALIZATION_CONTAINER_OTHER;
1✔
744
                goto finish;
1✔
745
        }
746

747
        /* If none of that worked, give up, assume no container manager. */
748
        v = VIRTUALIZATION_NONE;
4,705✔
749
        goto finish;
4,705✔
750

751
translate_name:
3,796✔
752
        if (streq(e, "oci")) {
3,796✔
753
                /* Some images hardcode container=oci, but OCI is not a specific container manager.
754
                 * Try to detect one based on well-known files. */
UNCOV
755
                v = detect_container_files();
×
UNCOV
756
                if (v == VIRTUALIZATION_NONE)
×
UNCOV
757
                        v = VIRTUALIZATION_CONTAINER_OTHER;
×
UNCOV
758
                goto finish;
×
759
        }
760
        v = container_from_string(e);
3,796✔
761
        if (v < 0)
3,796✔
762
                v = VIRTUALIZATION_CONTAINER_OTHER;
×
763

764
finish:
3,796✔
765
        log_debug("Found container virtualization %s.", virtualization_to_string(v));
8,502✔
766
        cached_found = v;
8,502✔
767
        return v;
8,502✔
768
}
769

770
Virtualization detect_virtualization(void) {
1,574✔
771
        int v;
1,574✔
772

773
        v = detect_container();
1,574✔
774
        if (v != VIRTUALIZATION_NONE)
1,574✔
775
                return v;
776

777
        return detect_vm();
1,161✔
778
}
779

780
int running_in_userns(void) {
79✔
781
        int r;
79✔
782

783
        r = namespace_is_init(NAMESPACE_USER);
79✔
784
        if (r < 0)
79✔
UNCOV
785
                return log_debug_errno(r, "Failed to test if in root user namespace, ignoring: %m");
×
786

787
        return !r;
79✔
788
}
789

790
int running_in_chroot(void) {
13,639✔
791
        int r;
13,639✔
792

793
        /* If we're PID1, /proc may not be mounted (and most likely we're not in a chroot). But PID1 will
794
         * mount /proc, so all other programs can assume that if /proc is *not* available, we're in some
795
         * chroot. */
796

797
        r = getenv_bool("SYSTEMD_IN_CHROOT");
13,639✔
798
        if (r >= 0)
13,639✔
799
                return r > 0;
4✔
800
        if (r != -ENXIO)
13,635✔
UNCOV
801
                log_debug_errno(r, "Failed to parse $SYSTEMD_IN_CHROOT, ignoring: %m");
×
802

803
        /* Deprecated but kept for backwards compatibility. */
804
        if (getenv_bool("SYSTEMD_IGNORE_CHROOT") > 0)
13,635✔
805
                return 0;
806

807
        r = pidref_from_same_root_fs(&PIDREF_MAKE_FROM_PID(1), NULL);
13,635✔
808
        if (r == -ENOSYS) {
13,635✔
UNCOV
809
                if (getpid_cached() == 1)
×
810
                        return false; /* We will mount /proc, assuming we're not in a chroot. */
811

UNCOV
812
                log_debug("/proc/ is not mounted, assuming we're in a chroot.");
×
813
                return true;
×
814
        }
815
        if (r == -ESRCH) /* We must have a fake /proc/, we can't do the check properly. */
13,635✔
816
                return -ENOSYS;
817
        if (r < 0)
13,635✔
818
                return r;
819

820
        return r == 0;
13,517✔
821
}
822

823
#if defined(__i386__) || defined(__x86_64__)
824
struct cpuid_table_entry {
825
        uint32_t flag_bit;
826
        const char *name;
827
};
828

829
static const struct cpuid_table_entry leaf1_edx[] = {
830
        {  0, "fpu"     },
831
        {  1, "vme"     },
832
        {  2, "de"      },
833
        {  3, "pse"     },
834
        {  4, "tsc"     },
835
        {  5, "msr"     },
836
        {  6, "pae"     },
837
        {  7, "mce"     },
838
        {  8, "cx8"     },
839
        {  9, "apic"    },
840
        { 11, "sep"     },
841
        { 12, "mtrr"    },
842
        { 13, "pge"     },
843
        { 14, "mca"     },
844
        { 15, "cmov"    },
845
        { 16, "pat"     },
846
        { 17, "pse36"   },
847
        { 19, "clflush" },
848
        { 23, "mmx"     },
849
        { 24, "fxsr"    },
850
        { 25, "sse"     },
851
        { 26, "sse2"    },
852
        { 28, "ht"      },
853
};
854

855
static const struct cpuid_table_entry leaf1_ecx[] = {
856
        {  0, "pni"     },
857
        {  1, "pclmul"  },
858
        {  3, "monitor" },
859
        {  9, "ssse3"   },
860
        { 12, "fma3"    },
861
        { 13, "cx16"    },
862
        { 19, "sse4_1"  },
863
        { 20, "sse4_2"  },
864
        { 22, "movbe"   },
865
        { 23, "popcnt"  },
866
        { 25, "aes"     },
867
        { 26, "xsave"   },
868
        { 27, "osxsave" },
869
        { 28, "avx"     },
870
        { 29, "f16c"    },
871
        { 30, "rdrand"  },
872
};
873

874
static const struct cpuid_table_entry leaf7_ebx[] = {
875
        {  3, "bmi1"   },
876
        {  5, "avx2"   },
877
        {  8, "bmi2"   },
878
        { 18, "rdseed" },
879
        { 19, "adx"    },
880
        { 29, "sha_ni" },
881
};
882

883
static const struct cpuid_table_entry leaf81_edx[] = {
884
        { 11, "syscall" },
885
        { 27, "rdtscp"  },
886
        { 29, "lm"      },
887
};
888

889
static const struct cpuid_table_entry leaf81_ecx[] = {
890
        {  0, "lahf_lm" },
891
        {  5, "abm"     },
892
};
893

894
static const struct cpuid_table_entry leaf87_edx[] = {
895
        {  8, "constant_tsc" },
896
};
897

898
static bool given_flag_in_set(const char *flag, const struct cpuid_table_entry *set, size_t set_size, uint32_t val) {
14✔
899
        for (size_t i = 0; i < set_size; i++) {
132✔
900
                if ((UINT32_C(1) << set[i].flag_bit) & val &&
119✔
901
                                streq(flag, set[i].name))
116✔
902
                        return true;
903
        }
904
        return false;
905
}
906

907
static bool real_has_cpu_with_flag(const char *flag) {
3✔
908
        uint32_t eax, ebx, ecx, edx;
3✔
909

910
        if (__get_cpuid(1, &eax, &ebx, &ecx, &edx)) {
3✔
911
                if (given_flag_in_set(flag, leaf1_ecx, ELEMENTSOF(leaf1_ecx), ecx))
3✔
912
                        return true;
3✔
913

914
                if (given_flag_in_set(flag, leaf1_edx, ELEMENTSOF(leaf1_edx), edx))
3✔
915
                        return true;
916
        }
917

918
        if (__get_cpuid_count(7, 0, &eax, &ebx, &ecx, &edx)) {
2✔
919
                if (given_flag_in_set(flag, leaf7_ebx, ELEMENTSOF(leaf7_ebx), ebx))
2✔
920
                        return true;
921
        }
922

923
        if (__get_cpuid(0x80000001U, &eax, &ebx, &ecx, &edx)) {
2✔
924
                if (given_flag_in_set(flag, leaf81_ecx, ELEMENTSOF(leaf81_ecx), ecx))
2✔
925
                        return true;
926

927
                if (given_flag_in_set(flag, leaf81_edx, ELEMENTSOF(leaf81_edx), edx))
2✔
928
                        return true;
929
        }
930

931
        if (__get_cpuid(0x80000007U, &eax, &ebx, &ecx, &edx))
2✔
932
                if (given_flag_in_set(flag, leaf87_edx, ELEMENTSOF(leaf87_edx), edx))
2✔
UNCOV
933
                        return true;
×
934

935
        return false;
936
}
937
#endif
938

939
bool has_cpu_with_flag(const char *flag) {
3✔
940
        /* CPUID is an x86 specific interface. Assume on all others that no CPUs have those flags. */
941
#if defined(__i386__) || defined(__x86_64__)
942
        return real_has_cpu_with_flag(flag);
3✔
943
#else
944
        return false;
945
#endif
946
}
947

948
static const char *const virtualization_table[_VIRTUALIZATION_MAX] = {
949
        [VIRTUALIZATION_NONE]            = "none",
950
        [VIRTUALIZATION_KVM]             = "kvm",
951
        [VIRTUALIZATION_AMAZON]          = "amazon",
952
        [VIRTUALIZATION_QEMU]            = "qemu",
953
        [VIRTUALIZATION_BOCHS]           = "bochs",
954
        [VIRTUALIZATION_XEN]             = "xen",
955
        [VIRTUALIZATION_UML]             = "uml",
956
        [VIRTUALIZATION_VMWARE]          = "vmware",
957
        [VIRTUALIZATION_ORACLE]          = "oracle",
958
        [VIRTUALIZATION_MICROSOFT]       = "microsoft",
959
        [VIRTUALIZATION_ZVM]             = "zvm",
960
        [VIRTUALIZATION_PARALLELS]       = "parallels",
961
        [VIRTUALIZATION_BHYVE]           = "bhyve",
962
        [VIRTUALIZATION_QNX]             = "qnx",
963
        [VIRTUALIZATION_ACRN]            = "acrn",
964
        [VIRTUALIZATION_POWERVM]         = "powervm",
965
        [VIRTUALIZATION_APPLE]           = "apple",
966
        [VIRTUALIZATION_SRE]             = "sre",
967
        [VIRTUALIZATION_GOOGLE]          = "google",
968
        [VIRTUALIZATION_VM_OTHER]        = "vm-other",
969

970
        [VIRTUALIZATION_SYSTEMD_NSPAWN]  = "systemd-nspawn",
971
        [VIRTUALIZATION_LXC_LIBVIRT]     = "lxc-libvirt",
972
        [VIRTUALIZATION_LXC]             = "lxc",
973
        [VIRTUALIZATION_OPENVZ]          = "openvz",
974
        [VIRTUALIZATION_DOCKER]          = "docker",
975
        [VIRTUALIZATION_PODMAN]          = "podman",
976
        [VIRTUALIZATION_RKT]             = "rkt",
977
        [VIRTUALIZATION_WSL]             = "wsl",
978
        [VIRTUALIZATION_PROOT]           = "proot",
979
        [VIRTUALIZATION_POUCH]           = "pouch",
980
        [VIRTUALIZATION_CONTAINER_OTHER] = "container-other",
981
};
982

983
DEFINE_STRING_TABLE_LOOKUP(virtualization, Virtualization);
1,935✔
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc