The bhyveload(8) command does not have a native non-interactive mode.
It means that in case of errors, e.g. invalid boot media, it
just drops into a loader prompt and waits for user input. This behaviour
makes it tricky for users to understand what's going on.
To address that, run it with the timeout(1) tool which sends SIGTERM
after a certain timeout, and then optionally sends SIGKILL if the
command keeps hanging.
These timeout values could be configured in the bhyve.conf. Setting
timeout to 0 mean that bhyveload(8) will be executed directly, without
timeout(1).
Signed-off-by: Roman Bogorodskiy <bogorodskiy@gmail.com>
Reviewed-by: Michal Privoznik <mprivozn@redhat.com>
# Path to a directory with firmware files. By default it's pointing
# to the directory that sysutils/bhyve-firmware installs files into.
#firmware_dir = "/usr/local/share/uefi-firmware"
+
+# Set timeout for the bhyveload(8) command. This might be necessary
+# because in case of errors bhyveload(8) drops to an interactive
+# loader and hangs indefinitely. These timeout values are passed
+# to the timeout(1) command. Please refer to its manual page for more
+# details. When timeout is 0, bhyveload is executed directly.
+# Units are seconds.
+#bhyveload_timeout = 300
+#bhyveload_timeout_kill = 15
}
static virCommand *
-virBhyveProcessBuildBhyveloadCmd(virDomainDef *def, virDomainDiskDef *disk)
+virBhyveProcessBuildBhyveloadCmd(virDomainDef *def,
+ struct _bhyveConn *driver,
+ virDomainDiskDef *disk)
{
virCommand *cmd;
-
- cmd = virCommandNew("bhyveload");
+ g_autoptr(virBhyveDriverConfig) cfg = virBhyveDriverGetConfig(driver);
+
+ if (cfg->bhyveloadTimeout > 0) {
+ /* TODO: update bhyve_process.c to interpret timeout(1) exit
+ * codes 124-127 to produce more meaningful error messages */
+ cmd = virCommandNew("timeout");
+ virCommandAddArg(cmd, "--foreground");
+ virCommandAddArg(cmd, "--verbose");
+ if (cfg->bhyveloadTimeoutKill > 0) {
+ virCommandAddArg(cmd, "-k");
+ virCommandAddArgFormat(cmd, "%ds", cfg->bhyveloadTimeoutKill);
+ }
+ virCommandAddArgFormat(cmd, "%ds", cfg->bhyveloadTimeout);
+ virCommandAddArg(cmd, "bhyveload");
+ } else {
+ cmd = virCommandNew("bhyveload");
+ }
if (def->os.bootloaderArgs == NULL) {
VIR_DEBUG("bhyveload with default arguments");
if (disk == NULL)
return NULL;
- return virBhyveProcessBuildBhyveloadCmd(def, disk);
+ return virBhyveProcessBuildBhyveloadCmd(def, driver, disk);
} else if (strstr(def->os.bootloader, "grub-bhyve") != NULL) {
return virBhyveProcessBuildGrubbhyveCmd(def, driver, devmap_file,
devicesmap_out);
* bhyve_conf.c: bhyve config file
*
* Copyright (C) 2017 Roman Bogorodskiy
+ * Copyright (C) 2025 The FreeBSD Foundation
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
cfg->libDir = g_strdup_printf("%s/lib/libvirt/bhyve", LOCALSTATEDIR);
cfg->nvramDir = g_strdup_printf("%s/nvram", cfg->libDir);
+ cfg->bhyveloadTimeout = 300;
+ cfg->bhyveloadTimeoutKill = 15;
+
return cfg;
}
&cfg->firmwareDir) < 0)
return -1;
+ if (virConfGetValueInt(conf, "bhyveload_timeout",
+ &cfg->bhyveloadTimeout) < 0)
+ return -1;
+
+ if (virConfGetValueInt(conf, "bhyveload_timeout_kill",
+ &cfg->bhyveloadTimeoutKill) < 0)
+ return -1;
+
return 0;
}
char *firmwareDir;
char *libDir;
char *nvramDir;
+
+ int bhyveloadTimeout;
+ int bhyveloadTimeoutKill;
};
G_DEFINE_AUTOPTR_CLEANUP_FUNC(virBhyveDriverConfig, virObjectUnref);
let str_array_entry (kw:string) = [ key kw . value_sep . str_array_val ]
let log_entry = str_entry "firmware_dir"
+ let bhyveload_timeout = int_entry "bhyveload_timeout"
+ let bhyveload_timeout_kill = int_entry "bhyveload_timeout_kill"
(* Each entry in the config is one of the following three ... *)
- let entry = log_entry
+ let entry = log_entry | bhyveload_timeout | bhyveload_timeout_kill
let comment = [ label "#comment" . del /#[ \t]*/ "# " . store /([^ \t\n][^\n]*)?/ . del /\n/ "\n" ]
let empty = [ label "#empty" . eol ]
test Libvirtd_bhyve.lns get conf =
{ "firmware_dir" = "/usr/local/share/uefi-firmware" }
+{ "bhyveload_timeout" = "300" }
+{ "bhyveload_timeout_kill" = "15" }
--- /dev/null
+bhyve \
+-c 1 \
+-m 214 \
+-u \
+-H \
+-P \
+-s 0:0,hostbridge \
+-s 2:0,ahci-hd,/tmp/freebsd.img \
+-s 3:0,virtio-net,faketapdev,mac=52:54:00:b9:94:02 \
+bhyve
--- /dev/null
+timeout \
+--foreground \
+--verbose \
+-k 20s 300s bhyveload \
+-m 214 \
+-d /tmp/freebsd.img \
+bhyve
--- /dev/null
+<domain type='bhyve'>
+ <name>bhyve</name>
+ <uuid>df3be7e7-a104-11e3-aeb0-50e5492bd3dc</uuid>
+ <memory>219136</memory>
+ <vcpu>1</vcpu>
+ <os>
+ <type>hvm</type>
+ </os>
+ <devices>
+ <disk type='file'>
+ <driver name='file' type='raw'/>
+ <source file='/tmp/freebsd.img'/>
+ <target dev='hda' bus='sata'/>
+ <address type='drive' controller='0' bus='0' target='2' unit='0'/>
+ </disk>
+ <interface type='bridge'>
+ <mac address='52:54:00:b9:94:02'/>
+ <model type='virtio'/>
+ <source bridge="virbr0"/>
+ <address type='pci' domain='0x0000' bus='0x00' slot='0x03' function='0x0'/>
+ </interface>
+ </devices>
+</domain>
driver.config->firmwareDir = fakefirmwaredir;
driver.config->nvramDir = fakenvramdir;
+ driver.config->bhyveloadTimeout = 0;
+ driver.config->bhyveloadTimeoutKill = 0;
# define DO_TEST_FULL(name, flags) \
do { \
driver.bhyvecaps &= ~BHYVE_CAP_VNC_PASSWORD;
DO_TEST_FAILURE("vnc-password");
+ driver.config->bhyveloadTimeout = 300;
+ driver.config->bhyveloadTimeoutKill = 20;
+ DO_TEST("bhyveload-timeout");
+
virObjectUnref(driver.caps);
virObjectUnref(driver.xmlopt);
virPortAllocatorRangeFree(driver.remotePorts);