diff --git a/usr.sbin/bhyve/Makefile b/usr.sbin/bhyve/Makefile --- a/usr.sbin/bhyve/Makefile +++ b/usr.sbin/bhyve/Makefile @@ -31,6 +31,7 @@ ctl_util.c \ hda_codec.c \ iov.c \ + ipc.c \ mem.c \ mevent.c \ net_backend_netmap.c \ diff --git a/usr.sbin/bhyve/bhyverun.c b/usr.sbin/bhyve/bhyverun.c --- a/usr.sbin/bhyve/bhyverun.c +++ b/usr.sbin/bhyve/bhyverun.c @@ -84,6 +84,7 @@ #ifdef BHYVE_GDB #include "gdb.h" #endif +#include "ipc.h" #include "mem.h" #include "mevent.h" #include "pci_emul.h" @@ -1024,13 +1025,11 @@ */ setproctitle("%s", vmname); -#ifdef BHYVE_SNAPSHOT /* - * checkpointing thread for communication with bhyvectl + * Thread for handling bhyvectl commands. */ - if (init_checkpoint_thread(ctx) != 0) + if (init_ipc_thread(ctx) != 0) errx(EX_OSERR, "Failed to start checkpoint thread"); -#endif #ifndef WITHOUT_CAPSICUM caph_cache_catpages(); diff --git a/usr.sbin/bhyve/ipc.h b/usr.sbin/bhyve/ipc.h --- a/usr.sbin/bhyve/ipc.h +++ b/usr.sbin/bhyve/ipc.h @@ -33,18 +33,17 @@ #include #include +struct vmctx; + struct ipc_command { const char *name; - int (*handler)(struct vmctx *ctx, const nvlist_t *nvl); + nvlist_t *(*handler)(struct vmctx *ctx, const nvlist_t *nvl); }; -#define IPC_COMMAND(set, name, function) \ - static struct ipc_command name ## _ipc_command = \ - { #name, function }; \ - DATA_SET(set, name ## _ipc_command) - -#define IPC_COMMAND_FOREACH(pvar, set) SET_FOREACH(pvar, set) +#define IPC_COMMAND(name, function) \ + static struct ipc_command name##_ipc_command = { #name, function }; \ + DATA_SET(ipc_cmd_set, name##_ipc_command) -SET_DECLARE(ipc_cmd_set, struct ipc_command); +int init_ipc_thread(struct vmctx *ctx); #endif /* _IPC_H_ */ diff --git a/usr.sbin/bhyve/ipc.c b/usr.sbin/bhyve/ipc.c new file mode 100644 --- /dev/null +++ b/usr.sbin/bhyve/ipc.c @@ -0,0 +1,189 @@ +/* + * Copyright (c) 2016 Flavius Anton + * Copyright (c) 2016 Mihai Tiganus + * Copyright (c) 2016-2019 Mihai Carabas + * Copyright (c) 2017-2019 Darius Mihai + * Copyright (c) 2017-2019 Elena Mihailescu + * Copyright (c) 2018-2019 Sergiu Weisz + * Copyright (c) 2025 Bojan Novković + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#include "ipc.h" + +#ifndef WITHOUT_CAPSICUM +#include +#endif +#include +#include +#include +#include + +#ifndef WITHOUT_CAPSICUM +#include +#endif +#include +#include + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "bhyverun.h" +#include "debug.h" + +#define IPC_COMMAND_FOREACH(pvar, set) SET_FOREACH(pvar, set) + +SET_DECLARE(ipc_cmd_set, struct ipc_command); + +static struct ipc_thread_ctx { + struct vmctx *vmctx; + int sockfd; +} thr_ctx; + +static nvlist_t * +handle_message(struct vmctx *ctx, nvlist_t *nvl) +{ + const char *cmd; + struct ipc_command **ipc_cmd; + + cmd = nvlist_get_string(nvl, "cmd"); + IPC_COMMAND_FOREACH(ipc_cmd, ipc_cmd_set) + { + if (strcmp(cmd, (*ipc_cmd)->name) == 0) + return ((*ipc_cmd)->handler(ctx, nvl)); + } + + return (NULL); +} + +/* + * Listen for commands from bhyvectl. + */ +static void * +ipc_thread(void *param) +{ + int fd; + nvlist_t *nvl, *reply; + const char *cmdname; + struct ipc_thread_ctx *ctx; + + pthread_set_name_np(pthread_self(), "IPC thread"); + ctx = (struct ipc_thread_ctx *)param; + while ((fd = accept(ctx->sockfd, NULL, NULL)) != -1) { + nvl = nvlist_recv(fd, 0); + if (nvl == NULL) { + EPRINTLN("%s: nvlist_recv() failed: %s", __func__, + strerror(errno)); + close(fd); + continue; + } + + cmdname = nvlist_get_string(nvl, "cmd"); + if (cmdname == NULL) { + nvlist_add_string(nvl, "error", "missing command name"); + nvlist_send(fd, nvl); + nvlist_destroy(nvl); + close(fd); + continue; + } + reply = handle_message(ctx->vmctx, nvl); + if (reply == NULL) { + reply = nvlist_create(0); + nvlist_add_stringf(reply, "error", "command '%s' not found", + cmdname); + } + /* The handler should set the error message if need be. */ + nvlist_send(fd, reply); + + nvlist_destroy(nvl); + nvlist_destroy(reply); + close(fd); + } + + return (NULL); +} + +/* + * Create the listening socket for IPC with bhyvectl. + */ +int +init_ipc_thread(struct vmctx *ctx) +{ + struct sockaddr_un addr; + int socket_fd; + pthread_t ipc_pthread; + int err; +#ifndef WITHOUT_CAPSICUM + cap_rights_t rights; +#endif + + memset(&addr, 0, sizeof(addr)); + socket_fd = socket(PF_UNIX, SOCK_STREAM, 0); + if (socket_fd < 0) { + EPRINTLN("%s: Socket creation failed: %s", __func__, + strerror(errno)); + err = -1; + goto fail; + } + + addr.sun_family = AF_UNIX; + + snprintf(addr.sun_path, sizeof(addr.sun_path), "%s%s", BHYVE_RUN_DIR, + vm_get_name(ctx)); + addr.sun_len = SUN_LEN(&addr); + unlink(addr.sun_path); + + if (bind(socket_fd, (struct sockaddr *)&addr, addr.sun_len) != 0) { + EPRINTLN("Failed to bind socket \"%s\": %s\n", addr.sun_path, + strerror(errno)); + err = -1; + goto fail; + } + + if (listen(socket_fd, 10) < 0) { + EPRINTLN("ipc socket listen: %s\n", strerror(errno)); + err = errno; + goto fail; + } + +#ifndef WITHOUT_CAPSICUM + cap_rights_init(&rights, CAP_ACCEPT, CAP_READ, CAP_RECV, CAP_WRITE, + CAP_SEND, CAP_GETSOCKOPT); + + if (caph_rights_limit(socket_fd, &rights) == -1) + errx(EX_OSERR, "Unable to apply rights for sandbox"); +#endif + + memset(&thr_ctx, 0, sizeof(thr_ctx)); + thr_ctx.vmctx = ctx; + thr_ctx.sockfd = socket_fd; + + err = pthread_create(&ipc_pthread, NULL, ipc_thread, &thr_ctx); + if (err != 0) + goto fail; + + return (0); +fail: + if (socket_fd > 0) + close(socket_fd); + unlink(addr.sun_path); + + return (err); +} diff --git a/usr.sbin/bhyve/snapshot.h b/usr.sbin/bhyve/snapshot.h --- a/usr.sbin/bhyve/snapshot.h +++ b/usr.sbin/bhyve/snapshot.h @@ -40,7 +40,6 @@ #include #include -#define BHYVE_RUN_DIR "/var/run/bhyve/" #define MAX_SNAPSHOT_FILENAME PATH_MAX struct vmctx; diff --git a/usr.sbin/bhyve/snapshot.c b/usr.sbin/bhyve/snapshot.c --- a/usr.sbin/bhyve/snapshot.c +++ b/usr.sbin/bhyve/snapshot.c @@ -1331,56 +1331,13 @@ return (error); } -static int -handle_message(struct vmctx *ctx, nvlist_t *nvl) -{ - const char *cmd; - struct ipc_command **ipc_cmd; - - if (!nvlist_exists_string(nvl, "cmd")) - return (EINVAL); - - cmd = nvlist_get_string(nvl, "cmd"); - IPC_COMMAND_FOREACH(ipc_cmd, ipc_cmd_set) { - if (strcmp(cmd, (*ipc_cmd)->name) == 0) - return ((*ipc_cmd)->handler(ctx, nvl)); - } - - return (EOPNOTSUPP); -} - -/* - * Listen for commands from bhyvectl - */ -void * -checkpoint_thread(void *param) -{ - int fd; - struct checkpoint_thread_info *thread_info; - nvlist_t *nvl; - - pthread_set_name_np(pthread_self(), "checkpoint thread"); - thread_info = (struct checkpoint_thread_info *)param; - - while ((fd = accept(thread_info->socket_fd, NULL, NULL)) != -1) { - nvl = nvlist_recv(fd, 0); - if (nvl != NULL) - handle_message(thread_info->ctx, nvl); - else - EPRINTLN("nvlist_recv() failed: %s", strerror(errno)); - - close(fd); - nvlist_destroy(nvl); - } - - return (NULL); -} - -static int +static nvlist_t * vm_do_checkpoint(struct vmctx *ctx, const nvlist_t *nvl) { int error; + nvlist_t *reply; + reply = nvlist_create(0); if (!nvlist_exists_string(nvl, "filename") || !nvlist_exists_bool(nvl, "suspend") || !nvlist_exists_descriptor(nvl, "fddir")) @@ -1390,80 +1347,12 @@ nvlist_get_descriptor(nvl, "fddir"), nvlist_get_string(nvl, "filename"), nvlist_get_bool(nvl, "suspend")); + if (error != 0) + nvlist_add_string(reply, "error", strerror(error)); - return (error); -} -IPC_COMMAND(ipc_cmd_set, checkpoint, vm_do_checkpoint); - -/* - * Create the listening socket for IPC with bhyvectl - */ -int -init_checkpoint_thread(struct vmctx *ctx) -{ - struct checkpoint_thread_info *checkpoint_info = NULL; - struct sockaddr_un addr; - int socket_fd; - pthread_t checkpoint_pthread; - int err; -#ifndef WITHOUT_CAPSICUM - cap_rights_t rights; -#endif - - memset(&addr, 0, sizeof(addr)); - - socket_fd = socket(PF_UNIX, SOCK_STREAM, 0); - if (socket_fd < 0) { - EPRINTLN("Socket creation failed: %s", strerror(errno)); - err = -1; - goto fail; - } - - addr.sun_family = AF_UNIX; - - snprintf(addr.sun_path, sizeof(addr.sun_path), "%s%s", - BHYVE_RUN_DIR, vm_get_name(ctx)); - addr.sun_len = SUN_LEN(&addr); - unlink(addr.sun_path); - - if (bind(socket_fd, (struct sockaddr *)&addr, addr.sun_len) != 0) { - EPRINTLN("Failed to bind socket \"%s\": %s\n", - addr.sun_path, strerror(errno)); - err = -1; - goto fail; - } - - if (listen(socket_fd, 10) < 0) { - EPRINTLN("ipc socket listen: %s\n", strerror(errno)); - err = errno; - goto fail; - } - -#ifndef WITHOUT_CAPSICUM - cap_rights_init(&rights, CAP_ACCEPT, CAP_READ, CAP_RECV, CAP_WRITE, - CAP_SEND, CAP_GETSOCKOPT); - - if (caph_rights_limit(socket_fd, &rights) == -1) - errx(EX_OSERR, "Unable to apply rights for sandbox"); -#endif - checkpoint_info = calloc(1, sizeof(*checkpoint_info)); - checkpoint_info->ctx = ctx; - checkpoint_info->socket_fd = socket_fd; - - err = pthread_create(&checkpoint_pthread, NULL, checkpoint_thread, - checkpoint_info); - if (err != 0) - goto fail; - - return (0); -fail: - free(checkpoint_info); - if (socket_fd > 0) - close(socket_fd); - unlink(addr.sun_path); - - return (err); + return (reply); } +IPC_COMMAND(checkpoint, vm_do_checkpoint); void vm_snapshot_buf_err(const char *bufname, const enum vm_snapshot_op op)