git: 1f3aeb3ea5a5 - main - bhyve: monitor: detect monitor exit

From: Roman Bogorodskiy <novel_at_FreeBSD.org>
Date: Sat, 15 Aug 2026 12:50:29 UTC
The branch main has been updated by novel:

URL: https://cgit.FreeBSD.org/src/commit/?id=1f3aeb3ea5a52e16e87439160dc19c53526600c3

commit 1f3aeb3ea5a52e16e87439160dc19c53526600c3
Author:     Roman Bogorodskiy <novel@FreeBSD.org>
AuthorDate: 2026-08-11 16:20:31 +0000
Commit:     Roman Bogorodskiy <novel@FreeBSD.org>
CommitDate: 2026-08-15 08:58:02 +0000

    bhyve: monitor: detect monitor exit
    
    Currently, sending SIGTERM to the bhyve process triggers ACPI poweroff
    for a VM. However, when running bhyve in monitor mode (-M), there are
    two processes: the monitor process and the actual VM process. Sending
    SIGTERM to the VM process works as before -- it powers off the VM.
    But sending SIGTERM to the monitor process just kills the monitor
    process, leaving the stale VM process running.
    
    Fix that by creating a pipe between these two processes. The child
    process uses the pipe to detect when the monitor goes away, and exits
    automatically.
    
    MFC after:              2 weeks
    Reviewed by:            markj
    Sponsored by:           The FreeBSD Foundation
    Differential Revision:  https://reviews.freebsd.org/D58788
---
 usr.sbin/bhyve/bhyverun.c | 41 +++++++++++++++++++++++++++++++++++++----
 1 file changed, 37 insertions(+), 4 deletions(-)

diff --git a/usr.sbin/bhyve/bhyverun.c b/usr.sbin/bhyve/bhyverun.c
index c089d1fbc8fa..339cf5071906 100644
--- a/usr.sbin/bhyve/bhyverun.c
+++ b/usr.sbin/bhyve/bhyverun.c
@@ -54,9 +54,7 @@
 #include <string.h>
 #include <err.h>
 #include <errno.h>
-#ifdef BHYVE_SNAPSHOT
 #include <fcntl.h>
-#endif
 #include <libgen.h>
 #include <libutil.h>
 #include <unistd.h>
@@ -127,6 +125,29 @@ static struct vcpu_info {
 
 static cpuset_t **vcpumap;
 
+static void
+monitor_pipe_handler(int fd __unused, enum ev_type type __unused,
+    void *param __unused)
+{
+
+	exit(BHYVE_EXIT_ERROR);
+}
+
+static void
+monitor_pipe_init(int fd)
+{
+#ifndef WITHOUT_CAPSICUM
+	cap_rights_t rights;
+
+	cap_rights_init(&rights, CAP_EVENT);
+	if (caph_rights_limit(fd, &rights) == -1)
+		err(BHYVE_EXIT_ERROR, "Unable to apply rights to monitor pipe");
+#endif
+
+	if (mevent_add(fd, EVF_READ, monitor_pipe_handler, NULL) == NULL)
+		errx(BHYVE_EXIT_ERROR, "Unable to register parent exit event");
+}
+
 /*
  * XXX This parser is known to have the following issues:
  * 1.  It accepts null key=value tokens ",," as setting "cpus" to an
@@ -922,17 +943,29 @@ main(int argc, char *argv[])
 	init_bootrom(ctx);
 
 	if (get_config_bool_default("monitor", false)) {
+		int monitor_pipe[2];
+		pid_t child;
+
 		while (1) {
-			pid_t child = fork();
+			if (pipe2(monitor_pipe, O_CLOEXEC) == -1)
+				err(BHYVE_EXIT_ERROR, "pipe2");
+
+			child = fork();
 			if (child == -1) {
 				EPRINTLN("Monitor mode fork failed: %s",
 				    strerror(errno));
 				exit(BHYVE_EXIT_ERROR);
 			}
-			if (child == 0)
+			if (child == 0) {
+				close(monitor_pipe[1]);
+				monitor_pipe_init(monitor_pipe[0]);
 				break;
+			}
+
+			close(monitor_pipe[0]);
 			while ((error = waitpid(child, &status, 0)) == -1 && errno == EINTR)
 			    ;
+			close(monitor_pipe[1]);
 			if (error == -1) {
 				EPRINTLN("Monitor mode wait failed: %s",
 				    strerror(errno));