1*04811d46SBojan Novković /*
2*04811d46SBojan Novković * Copyright (c) 2016 Flavius Anton
3*04811d46SBojan Novković * Copyright (c) 2016 Mihai Tiganus
4*04811d46SBojan Novković * Copyright (c) 2016-2019 Mihai Carabas
5*04811d46SBojan Novković * Copyright (c) 2017-2019 Darius Mihai
6*04811d46SBojan Novković * Copyright (c) 2017-2019 Elena Mihailescu
7*04811d46SBojan Novković * Copyright (c) 2018-2019 Sergiu Weisz
8*04811d46SBojan Novković * Copyright (c) 2025 Bojan Novković <bnovkov@FreeBSD.org>
9*04811d46SBojan Novković *
10*04811d46SBojan Novković * SPDX-License-Identifier: BSD-2-Clause
11*04811d46SBojan Novković */
12*04811d46SBojan Novković
13*04811d46SBojan Novković #include "ipc.h"
14*04811d46SBojan Novković
15*04811d46SBojan Novković #ifndef WITHOUT_CAPSICUM
16*04811d46SBojan Novković #include <sys/capsicum.h>
17*04811d46SBojan Novković #endif
18*04811d46SBojan Novković #include <sys/types.h>
19*04811d46SBojan Novković #include <sys/linker_set.h>
20*04811d46SBojan Novković #include <sys/socket.h>
21*04811d46SBojan Novković #include <sys/un.h>
22*04811d46SBojan Novković
23*04811d46SBojan Novković #ifndef WITHOUT_CAPSICUM
24*04811d46SBojan Novković #include <capsicum_helpers.h>
25*04811d46SBojan Novković #endif
26*04811d46SBojan Novković #include <sys/cpuset.h>
27*04811d46SBojan Novković #include <sys/ioctl.h>
28*04811d46SBojan Novković
29*04811d46SBojan Novković #include <machine/vmm.h>
30*04811d46SBojan Novković #include <machine/vmm_snapshot.h>
31*04811d46SBojan Novković
32*04811d46SBojan Novković #include <assert.h>
33*04811d46SBojan Novković #include <err.h>
34*04811d46SBojan Novković #include <errno.h>
35*04811d46SBojan Novković #include <fcntl.h>
36*04811d46SBojan Novković #include <libgen.h>
37*04811d46SBojan Novković #include <pthread.h>
38*04811d46SBojan Novković #include <pthread_np.h>
39*04811d46SBojan Novković #include <stdbool.h>
40*04811d46SBojan Novković #include <stdio.h>
41*04811d46SBojan Novković #include <stdlib.h>
42*04811d46SBojan Novković #include <string.h>
43*04811d46SBojan Novković #include <sysexits.h>
44*04811d46SBojan Novković #include <unistd.h>
45*04811d46SBojan Novković #include <vmmapi.h>
46*04811d46SBojan Novković
47*04811d46SBojan Novković #include "bhyverun.h"
48*04811d46SBojan Novković #include "debug.h"
49*04811d46SBojan Novković
50*04811d46SBojan Novković #define IPC_COMMAND_FOREACH(pvar, set) SET_FOREACH(pvar, set)
51*04811d46SBojan Novković
52*04811d46SBojan Novković SET_DECLARE(ipc_cmd_set, struct ipc_command);
53*04811d46SBojan Novković
54*04811d46SBojan Novković static struct ipc_thread_ctx {
55*04811d46SBojan Novković struct vmctx *vmctx;
56*04811d46SBojan Novković int sockfd;
57*04811d46SBojan Novković } thr_ctx;
58*04811d46SBojan Novković
59*04811d46SBojan Novković static nvlist_t *
handle_message(struct vmctx * ctx,nvlist_t * nvl)60*04811d46SBojan Novković handle_message(struct vmctx *ctx, nvlist_t *nvl)
61*04811d46SBojan Novković {
62*04811d46SBojan Novković const char *cmd;
63*04811d46SBojan Novković struct ipc_command **ipc_cmd;
64*04811d46SBojan Novković
65*04811d46SBojan Novković cmd = nvlist_get_string(nvl, "cmd");
66*04811d46SBojan Novković IPC_COMMAND_FOREACH(ipc_cmd, ipc_cmd_set)
67*04811d46SBojan Novković {
68*04811d46SBojan Novković if (strcmp(cmd, (*ipc_cmd)->name) == 0)
69*04811d46SBojan Novković return ((*ipc_cmd)->handler(ctx, nvl));
70*04811d46SBojan Novković }
71*04811d46SBojan Novković
72*04811d46SBojan Novković return (NULL);
73*04811d46SBojan Novković }
74*04811d46SBojan Novković
75*04811d46SBojan Novković /*
76*04811d46SBojan Novković * Listen for commands from bhyvectl.
77*04811d46SBojan Novković */
78*04811d46SBojan Novković static void *
ipc_thread(void * param)79*04811d46SBojan Novković ipc_thread(void *param)
80*04811d46SBojan Novković {
81*04811d46SBojan Novković int fd;
82*04811d46SBojan Novković nvlist_t *nvl, *reply;
83*04811d46SBojan Novković const char *cmdname;
84*04811d46SBojan Novković struct ipc_thread_ctx *ctx;
85*04811d46SBojan Novković
86*04811d46SBojan Novković pthread_set_name_np(pthread_self(), "IPC thread");
87*04811d46SBojan Novković ctx = (struct ipc_thread_ctx *)param;
88*04811d46SBojan Novković while ((fd = accept(ctx->sockfd, NULL, NULL)) != -1) {
89*04811d46SBojan Novković nvl = nvlist_recv(fd, 0);
90*04811d46SBojan Novković if (nvl == NULL) {
91*04811d46SBojan Novković EPRINTLN("%s: nvlist_recv() failed: %s", __func__,
92*04811d46SBojan Novković strerror(errno));
93*04811d46SBojan Novković close(fd);
94*04811d46SBojan Novković continue;
95*04811d46SBojan Novković }
96*04811d46SBojan Novković
97*04811d46SBojan Novković cmdname = nvlist_get_string(nvl, "cmd");
98*04811d46SBojan Novković if (cmdname == NULL) {
99*04811d46SBojan Novković nvlist_add_string(nvl, "error", "missing command name");
100*04811d46SBojan Novković nvlist_send(fd, nvl);
101*04811d46SBojan Novković nvlist_destroy(nvl);
102*04811d46SBojan Novković close(fd);
103*04811d46SBojan Novković continue;
104*04811d46SBojan Novković }
105*04811d46SBojan Novković reply = handle_message(ctx->vmctx, nvl);
106*04811d46SBojan Novković if (reply == NULL) {
107*04811d46SBojan Novković reply = nvlist_create(0);
108*04811d46SBojan Novković nvlist_add_stringf(reply, "error", "command '%s' not found",
109*04811d46SBojan Novković cmdname);
110*04811d46SBojan Novković }
111*04811d46SBojan Novković /* The handler should set the error message if need be. */
112*04811d46SBojan Novković nvlist_send(fd, reply);
113*04811d46SBojan Novković
114*04811d46SBojan Novković nvlist_destroy(nvl);
115*04811d46SBojan Novković nvlist_destroy(reply);
116*04811d46SBojan Novković close(fd);
117*04811d46SBojan Novković }
118*04811d46SBojan Novković
119*04811d46SBojan Novković return (NULL);
120*04811d46SBojan Novković }
121*04811d46SBojan Novković
122*04811d46SBojan Novković /*
123*04811d46SBojan Novković * Create the listening socket for IPC with bhyvectl.
124*04811d46SBojan Novković */
125*04811d46SBojan Novković int
init_ipc_thread(struct vmctx * ctx,const char * bhyve_run_dir)126*04811d46SBojan Novković init_ipc_thread(struct vmctx *ctx, const char *bhyve_run_dir)
127*04811d46SBojan Novković {
128*04811d46SBojan Novković struct sockaddr_un addr;
129*04811d46SBojan Novković pthread_t ipc_pthread;
130*04811d46SBojan Novković int socket_fd;
131*04811d46SBojan Novković int err, ret;
132*04811d46SBojan Novković #ifndef WITHOUT_CAPSICUM
133*04811d46SBojan Novković cap_rights_t rights;
134*04811d46SBojan Novković #endif
135*04811d46SBojan Novković
136*04811d46SBojan Novković memset(&addr, 0, sizeof(addr));
137*04811d46SBojan Novković
138*04811d46SBojan Novković socket_fd = socket(PF_UNIX, SOCK_STREAM, 0);
139*04811d46SBojan Novković if (socket_fd < 0) {
140*04811d46SBojan Novković EPRINTLN("Socket creation failed: %s", strerror(errno));
141*04811d46SBojan Novković err = -1;
142*04811d46SBojan Novković goto fail;
143*04811d46SBojan Novković }
144*04811d46SBojan Novković
145*04811d46SBojan Novković addr.sun_family = AF_UNIX;
146*04811d46SBojan Novković
147*04811d46SBojan Novković ret = snprintf(addr.sun_path, sizeof(addr.sun_path), "%s/%s",
148*04811d46SBojan Novković bhyve_run_dir, vm_get_name(ctx));
149*04811d46SBojan Novković if ((ret < 0) || ((size_t)ret >= sizeof(addr.sun_path))) {
150*04811d46SBojan Novković EPRINTLN("%s: error setting socket path (%d)", __func__, ret);
151*04811d46SBojan Novković err = -1;
152*04811d46SBojan Novković goto fail;
153*04811d46SBojan Novković }
154*04811d46SBojan Novković
155*04811d46SBojan Novković addr.sun_len = SUN_LEN(&addr);
156*04811d46SBojan Novković unlink(addr.sun_path);
157*04811d46SBojan Novković
158*04811d46SBojan Novković if (bind(socket_fd, (struct sockaddr *)&addr, addr.sun_len) != 0) {
159*04811d46SBojan Novković EPRINTLN("Failed to bind socket \"%s\": %s\n",
160*04811d46SBojan Novković addr.sun_path, strerror(errno));
161*04811d46SBojan Novković err = -1;
162*04811d46SBojan Novković goto fail;
163*04811d46SBojan Novković }
164*04811d46SBojan Novković
165*04811d46SBojan Novković if (listen(socket_fd, 10) < 0) {
166*04811d46SBojan Novković EPRINTLN("ipc socket listen: %s\n", strerror(errno));
167*04811d46SBojan Novković err = errno;
168*04811d46SBojan Novković goto fail;
169*04811d46SBojan Novković }
170*04811d46SBojan Novković
171*04811d46SBojan Novković #ifndef WITHOUT_CAPSICUM
172*04811d46SBojan Novković cap_rights_init(&rights, CAP_ACCEPT, CAP_READ, CAP_RECV, CAP_WRITE,
173*04811d46SBojan Novković CAP_SEND, CAP_GETSOCKOPT);
174*04811d46SBojan Novković
175*04811d46SBojan Novković if (caph_rights_limit(socket_fd, &rights) == -1)
176*04811d46SBojan Novković errx(EX_OSERR, "Unable to apply rights for sandbox");
177*04811d46SBojan Novković #endif
178*04811d46SBojan Novković memset(&thr_ctx, 0, sizeof(thr_ctx));
179*04811d46SBojan Novković thr_ctx.vmctx = ctx;
180*04811d46SBojan Novković thr_ctx.sockfd = socket_fd;
181*04811d46SBojan Novković
182*04811d46SBojan Novković err = pthread_create(&ipc_pthread, NULL, ipc_thread,
183*04811d46SBojan Novković &thr_ctx);
184*04811d46SBojan Novković if (err != 0)
185*04811d46SBojan Novković goto fail;
186*04811d46SBojan Novković
187*04811d46SBojan Novković return (0);
188*04811d46SBojan Novković fail:
189*04811d46SBojan Novković if (socket_fd > 0)
190*04811d46SBojan Novković close(socket_fd);
191*04811d46SBojan Novković unlink(addr.sun_path);
192*04811d46SBojan Novković
193*04811d46SBojan Novković return (err);
194*04811d46SBojan Novković }
195