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