xref: /freebsd/usr.sbin/bhyve/ipc.c (revision 04811d46d3f2176af245f6d5e7e06b422f5d6216)
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