/* Determine whether the current system is running under VirtualBox/KVM. Copyright (C) 2021-2024 Free Software Foundation, Inc. This file is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This file is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this program. If not, see . */ /* Written by Bruno Haible , 2024. */ #ifdef __linux__ # include # include # include #endif /* This function determines whether the current system is Linux and running under the VirtualBox emulator. */ _GL_ATTRIBUTE_MAYBE_UNUSED static bool is_running_under_virtualbox (void) { #ifdef __linux__ /* On distributions with systemd, this could be done through test `systemd-detect-virt --vm` = oracle More generally, it can be done through test "`cat /sys/class/dmi/id/product_name`" = VirtualBox This is what we do here. */ char buf[4096]; int fd = open ("/sys/class/dmi/id/product_name", O_RDONLY); if (fd >= 0) { int n = read (fd, buf, sizeof (buf)); close (fd); if (n == 10 + 1 && memcmp (buf, "VirtualBox\n", 10 + 1) == 0) return true; } #endif return false; } /* This function determines whether the current system is Linux and running under the VirtualBox emulator, with paravirtualization acceleration set to "Default" or "KVM". */ static bool is_running_under_virtualbox_kvm (void) { #ifdef __linux__ if (is_running_under_virtualbox ()) { /* As root, one can determine this paravirtualization mode through dmesg | grep -i kvm which produces output like this: [ 0.000000] Hypervisor detected: KVM [ 0.000000] kvm-clock: Using msrs 4b564d01 and 4b564d00 [ 0.000001] kvm-clock: using sched offset of 3736655524 cycles [ 0.000004] clocksource: kvm-clock: mask: 0xffffffffffffffff max_cycles: 0x1cd42e4dffb, max_idle_ns: 881590591483 ns [ 0.007355] Booting paravirtualized kernel on KVM [ 0.213538] clocksource: Switched to clocksource kvm-clock So, we test whether the file /sys/devices/system/clocksource/clocksource0/available_clocksource contains the word 'kvm-clock'. */ char buf[4096 + 1]; int fd = open ("/sys/devices/system/clocksource/clocksource0/available_clocksource", O_RDONLY); if (fd >= 0) { int n = read (fd, buf, sizeof (buf) - 1); close (fd); if (n > 0) { buf[n] = '\0'; char *saveptr; char *word; for (word = strtok_r (buf, " \n", &saveptr); word != NULL; word = strtok_r (NULL, " \n", &saveptr)) { if (strcmp (word, "kvm-clock") == 0) return true; } } } } #endif return false; } /* This function returns the number of CPUs in the current system, assuming it is Linux. */ static int num_cpus (void) { #ifdef __linux__ /* We could use sysconf (_SC_NPROCESSORS_CONF), which on glibc and musl libc is implemented through sched_getaffinity(). But there are some complications; see nproc.c. It's simpler to parse /proc/cpuinfo. More precisely, it's sufficient to count the number of blank lines in /proc/cpuinfo. */ char buf[4096]; int fd = open ("/proc/cpuinfo", O_RDONLY); if (fd >= 0) { unsigned int blank_lines = 0; bool last_char_was_newline = false; for (;;) { int n = read (fd, buf, sizeof (buf)); if (n <= 0) break; int i; for (i = 0; i < n; i++) { if (last_char_was_newline && buf[i] == '\n') blank_lines++; last_char_was_newline = (buf[i] == '\n'); } } close (fd); if (blank_lines > 0) return blank_lines; } #endif return 1; }