openat: don’t close (-1)
[gnulib.git] / tests / virtualbox.h
blob2cc970012fd7999f7ef328734599ef7d580dae58
1 /* Determine whether the current system is running under VirtualBox/KVM.
2 Copyright (C) 2021-2024 Free Software Foundation, Inc.
4 This file is free software: you can redistribute it and/or modify
5 it under the terms of the GNU Lesser General Public License as
6 published by the Free Software Foundation; either version 2.1 of the
7 License, or (at your option) any later version.
9 This file is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU Lesser General Public License for more details.
14 You should have received a copy of the GNU Lesser General Public License
15 along with this program. If not, see <https://www.gnu.org/licenses/>. */
17 /* Written by Bruno Haible <bruno@clisp.org>, 2024. */
19 #ifdef __linux__
20 # include <fcntl.h>
21 # include <string.h>
22 # include <unistd.h>
23 #endif
25 /* This function determines whether the current system is Linux and running
26 under the VirtualBox emulator. */
27 _GL_ATTRIBUTE_MAYBE_UNUSED static bool
28 is_running_under_virtualbox (void)
30 #ifdef __linux__
31 /* On distributions with systemd, this could be done through
32 test `systemd-detect-virt --vm` = oracle
33 More generally, it can be done through
34 test "`cat /sys/class/dmi/id/product_name`" = VirtualBox
35 This is what we do here. */
36 char buf[4096];
37 int fd = open ("/sys/class/dmi/id/product_name", O_RDONLY);
38 if (fd >= 0)
40 int n = read (fd, buf, sizeof (buf));
41 close (fd);
42 if (n == 10 + 1 && memcmp (buf, "VirtualBox\n", 10 + 1) == 0)
43 return true;
45 #endif
47 return false;
50 /* This function determines whether the current system is Linux and running
51 under the VirtualBox emulator, with paravirtualization acceleration set to
52 "Default" or "KVM". */
53 static bool
54 is_running_under_virtualbox_kvm (void)
56 #ifdef __linux__
57 if (is_running_under_virtualbox ())
59 /* As root, one can determine this paravirtualization mode through
60 dmesg | grep -i kvm
61 which produces output like this:
62 [ 0.000000] Hypervisor detected: KVM
63 [ 0.000000] kvm-clock: Using msrs 4b564d01 and 4b564d00
64 [ 0.000001] kvm-clock: using sched offset of 3736655524 cycles
65 [ 0.000004] clocksource: kvm-clock: mask: 0xffffffffffffffff max_cycles: 0x1cd42e4dffb, max_idle_ns: 881590591483 ns
66 [ 0.007355] Booting paravirtualized kernel on KVM
67 [ 0.213538] clocksource: Switched to clocksource kvm-clock
68 So, we test whether the file
69 /sys/devices/system/clocksource/clocksource0/available_clocksource
70 contains the word 'kvm-clock'. */
71 char buf[4096 + 1];
72 int fd = open ("/sys/devices/system/clocksource/clocksource0/available_clocksource", O_RDONLY);
73 if (fd >= 0)
75 int n = read (fd, buf, sizeof (buf) - 1);
76 close (fd);
77 if (n > 0)
79 buf[n] = '\0';
80 char *saveptr;
81 char *word;
82 for (word = strtok_r (buf, " \n", &saveptr);
83 word != NULL;
84 word = strtok_r (NULL, " \n", &saveptr))
86 if (strcmp (word, "kvm-clock") == 0)
87 return true;
92 #endif
94 return false;
97 /* This function returns the number of CPUs in the current system, assuming
98 it is Linux. */
99 static int
100 num_cpus (void)
102 #ifdef __linux__
103 /* We could use sysconf (_SC_NPROCESSORS_CONF), which on glibc and musl libc
104 is implemented through sched_getaffinity(). But there are some
105 complications; see nproc.c. It's simpler to parse /proc/cpuinfo.
106 More precisely, it's sufficient to count the number of blank lines in
107 /proc/cpuinfo. */
108 char buf[4096];
109 int fd = open ("/proc/cpuinfo", O_RDONLY);
110 if (fd >= 0)
112 unsigned int blank_lines = 0;
113 bool last_char_was_newline = false;
114 for (;;)
116 int n = read (fd, buf, sizeof (buf));
117 if (n <= 0)
118 break;
119 int i;
120 for (i = 0; i < n; i++)
122 if (last_char_was_newline && buf[i] == '\n')
123 blank_lines++;
124 last_char_was_newline = (buf[i] == '\n');
127 close (fd);
128 if (blank_lines > 0)
129 return blank_lines;
131 #endif
133 return 1;