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