diff --git a/usr.sbin/bhyve/bhyve_config.5 b/usr.sbin/bhyve/bhyve_config.5 --- a/usr.sbin/bhyve/bhyve_config.5 +++ b/usr.sbin/bhyve/bhyve_config.5 @@ -232,7 +232,7 @@ .It Va rundir Ta String Ta Pa /var/run/bhyve/ Ta Path to the runtime directory for .Xr bhyve 8 -to store its service files, such as the checkpoint socket. +to store its service files, such as the IPC socket. Overriding this path is useful when running .Xr bhyve 8 as an unprivileged user. diff --git a/usr.sbin/bhyve/ipc.c b/usr.sbin/bhyve/ipc.c --- a/usr.sbin/bhyve/ipc.c +++ b/usr.sbin/bhyve/ipc.c @@ -56,6 +56,17 @@ int sockfd; } thr_ctx; +static nvlist_t * +ipc_get_vm_pid(struct vmctx *ctx __unused, const nvlist_t *nvl __unused) +{ + nvlist_t *reply; + + reply = nvlist_create(0); + nvlist_add_number(reply, "pid", getpid()); + return (reply); +} +IPC_COMMAND(get_vm_pid, ipc_get_vm_pid); + static nvlist_t * handle_message(struct vmctx *ctx, nvlist_t *nvl) { diff --git a/usr.sbin/bhyvectl/Makefile b/usr.sbin/bhyvectl/Makefile --- a/usr.sbin/bhyvectl/Makefile +++ b/usr.sbin/bhyvectl/Makefile @@ -10,12 +10,11 @@ MAN= bhyvectl.8 -LIBADD= vmmapi util +LIBADD= vmmapi util nv CFLAGS+= -I${.CURDIR} -I${SRCTOP}/sys/amd64/vmm .if ${MK_BHYVE_SNAPSHOT} != "no" -LIBADD+= nv CFLAGS+= -DBHYVE_SNAPSHOT # usr.sbin/bhyve/snapshot.h needs ucl header diff --git a/usr.sbin/bhyvectl/bhyvectl.8 b/usr.sbin/bhyvectl/bhyvectl.8 --- a/usr.sbin/bhyvectl/bhyvectl.8 +++ b/usr.sbin/bhyvectl/bhyvectl.8 @@ -25,7 +25,7 @@ .\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF .\" SUCH DAMAGE. .\" -.Dd July 8, 2026 +.Dd September 11, 2026 .Dt BHYVECTL 8 .Os .Sh NAME @@ -37,6 +37,7 @@ .Op Fl -create .Op Fl -destroy .Op Fl -get-stats +.Op Fl -get-vm-pid .Op Fl -inject-nmi .Op Fl -force-reset .Op Fl -force-poweroff @@ -61,6 +62,9 @@ Destroy the specified VM. .It Fl -get-stats Retrieve statistics for the specified VM. +.It Fl -get-vm-pid +Print the process ID of the bhyve process running the specified VM. +In monitor mode, this is the child process executing the VM. .It Fl -inject-nmi Inject a non-maskable interrupt (NMI) into the VM. .It Fl -force-reset @@ -79,7 +83,7 @@ The virtual machine will terminate after the snapshot has been saved. .It Fl -rundir= Ns Ar -Specify path to the directory containing the checkpoint socket. +Specify path to the directory containing the bhyve IPC socket. If not specified, .Pa /var/run/bhyve is used. diff --git a/usr.sbin/bhyvectl/bhyvectl.c b/usr.sbin/bhyvectl/bhyvectl.c --- a/usr.sbin/bhyvectl/bhyvectl.c +++ b/usr.sbin/bhyvectl/bhyvectl.c @@ -72,6 +72,7 @@ static uint64_t memsize; static int run; static int get_cpu_topology; +static int get_vm_pid; #ifdef BHYVE_SNAPSHOT static int vm_suspend_opt; #endif @@ -87,8 +88,8 @@ #ifdef BHYVE_SNAPSHOT SET_CHECKPOINT_FILE, SET_SUSPEND_FILE, - SET_RUNDIR, #endif + SET_RUNDIR, OPT_LAST, }; @@ -137,11 +138,12 @@ { "get-debug-cpus", NO_ARG, &get_debug_cpus, 1 }, { "get-suspended-cpus", NO_ARG, &get_suspended_cpus, 1 }, { "get-cpu-topology", NO_ARG, &get_cpu_topology, 1 }, + { "get-vm-pid", NO_ARG, &get_vm_pid, 1 }, #ifdef BHYVE_SNAPSHOT { "checkpoint", REQ_ARG, 0, SET_CHECKPOINT_FILE}, { "suspend", REQ_ARG, 0, SET_SUSPEND_FILE}, - { "rundir", REQ_ARG, 0, SET_RUNDIR}, #endif + { "rundir", REQ_ARG, 0, SET_RUNDIR}, }; return (bhyvectl_opts(common_opts, nitems(common_opts))); @@ -158,8 +160,8 @@ #ifdef BHYVE_SNAPSHOT [SET_CHECKPOINT_FILE] = "filename", [SET_SUSPEND_FILE] = "filename", - [SET_RUNDIR] = "path", #endif + [SET_RUNDIR] = "path", }; (void)fprintf(stderr, "Usage: %s --vm=\n", progname); for (const struct option *o = opts; o->name; o++) { @@ -255,12 +257,13 @@ } static int __unused -ipc_send_message(const char *vmname, nvlist_t *request, const char *rundir) +ipc_send_message(const char *vmname, nvlist_t *request, const char *rundir, + nvlist_t **replyp) { int err = 0, socket_fd, ret; struct sockaddr_un addr; const char* errmsg; - nvlist_t *reply; + nvlist_t *reply = NULL; socket_fd = socket(PF_UNIX, SOCK_STREAM, 0); if (socket_fd < 0) { @@ -292,22 +295,54 @@ request = NULL; if (reply == NULL) { perror("nvlist_xfer() failed"); + err = errno; goto done; } if (nvlist_exists_string(reply, "error")) { errmsg = nvlist_get_string(reply, "error"); - fprintf(stderr, "%s: IPC command failed: %s\n", __func__, errmsg); + fprintf(stderr, "%s: IPC command failed: %s\n", + __func__, errmsg); err = -1; + } else if (replyp != NULL) { + *replyp = reply; + reply = NULL; } done: if (request != NULL) nvlist_destroy(request); + if (reply != NULL) + nvlist_destroy(reply); if (socket_fd >= 0) close(socket_fd); return (err); } +static int +vm_pid_request(const char *vmname, const char *rundir, pid_t *pid) +{ + nvlist_t *nvl, *reply; + int error; + + nvl = nvlist_create(0); + nvlist_add_string(nvl, "cmd", "get_vm_pid"); + error = ipc_send_message(vmname, nvl, rundir, &reply); + if (error == -1) + error = EOPNOTSUPP; + else if (error == ENOENT || error == ECONNREFUSED) + error = ESRCH; + if (error == 0) { + if (nvlist_exists_number(reply, "pid")) + *pid = (pid_t)nvlist_get_number(reply, "pid"); + else + error = EPROTO; + nvlist_destroy(reply); + } + if (error != 0) + errno = error; + return (error); +} + #ifdef BHYVE_SNAPSHOT static int open_directory(const char *file) @@ -340,7 +375,7 @@ nvlist_add_bool(nvl, "suspend", suspend); nvlist_move_descriptor(nvl, "fddir", fd); - return (ipc_send_message(vmname, nvl, rundir)); + return (ipc_send_message(vmname, nvl, rundir, NULL)); } #endif @@ -402,11 +437,10 @@ vm_suspend_opt = (ch == SET_SUSPEND_FILE); break; +#endif case SET_RUNDIR: rundir = optarg; break; - -#endif default: usage(opts); } @@ -536,6 +570,14 @@ "maxcpus=%hu\n", sockets, cores, threads, maxcpus); } + if (!error && get_vm_pid) { + pid_t pid; + + error = vm_pid_request(vmname, rundir, &pid); + if (!error) + printf("vm pid:\t%d\n", pid); + } + if (!error && run) { struct vm_exit vmexit; cpuset_t cpuset;