1 /*
2 * QEMU monitor file descriptor passing
3 *
4 * Copyright (c) 2003-2004 Fabrice Bellard
5 *
6 * Permission is hereby granted, free of charge, to any person obtaining a copy
7 * of this software and associated documentation files (the "Software"), to deal
8 * in the Software without restriction, including without limitation the rights
9 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10 * copies of the Software, and to permit persons to whom the Software is
11 * furnished to do so, subject to the following conditions:
12 *
13 * The above copyright notice and this permission notice shall be included in
14 * all copies or substantial portions of the Software.
15 *
16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
19 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
22 * THE SOFTWARE.
23 */
24
25 #include "qemu/osdep.h"
26 #include "monitor-internal.h"
27 #include "qapi/error.h"
28 #include "qapi/qapi-commands-misc.h"
29 #include "qapi/qmp/qerror.h"
30 #include "qemu/ctype.h"
31 #include "qemu/cutils.h"
32 #include "sysemu/runstate.h"
33
34 /* file descriptors passed via SCM_RIGHTS */
35 typedef struct mon_fd_t mon_fd_t;
36 struct mon_fd_t {
37 char *name;
38 int fd;
39 QLIST_ENTRY(mon_fd_t) next;
40 };
41
42 /* file descriptor associated with a file descriptor set */
43 typedef struct MonFdsetFd MonFdsetFd;
44 struct MonFdsetFd {
45 int fd;
46 char *opaque;
47 QLIST_ENTRY(MonFdsetFd) next;
48 };
49
50 /* file descriptor set containing fds passed via SCM_RIGHTS */
51 typedef struct MonFdset MonFdset;
52 struct MonFdset {
53 int64_t id;
54 QLIST_HEAD(, MonFdsetFd) fds;
55 QLIST_HEAD(, MonFdsetFd) dup_fds;
56 QLIST_ENTRY(MonFdset) next;
57 };
58
59 /* Protects mon_fdsets */
60 static QemuMutex mon_fdsets_lock;
61 static QLIST_HEAD(, MonFdset) mon_fdsets;
62
monitor_add_fd(Monitor * mon,int fd,const char * fdname,Error ** errp)63 static bool monitor_add_fd(Monitor *mon, int fd, const char *fdname, Error **errp)
64 {
65 mon_fd_t *monfd;
66
67 if (qemu_isdigit(fdname[0])) {
68 close(fd);
69 error_setg(errp, QERR_INVALID_PARAMETER_VALUE, "fdname",
70 "a name not starting with a digit");
71 return false;
72 }
73
74 /* See close() call below. */
75 qemu_mutex_lock(&mon->mon_lock);
76 QLIST_FOREACH(monfd, &mon->fds, next) {
77 int tmp_fd;
78
79 if (strcmp(monfd->name, fdname) != 0) {
80 continue;
81 }
82
83 tmp_fd = monfd->fd;
84 monfd->fd = fd;
85 qemu_mutex_unlock(&mon->mon_lock);
86 /* Make sure close() is outside critical section */
87 close(tmp_fd);
88 return true;
89 }
90
91 monfd = g_new0(mon_fd_t, 1);
92 monfd->name = g_strdup(fdname);
93 monfd->fd = fd;
94
95 QLIST_INSERT_HEAD(&mon->fds, monfd, next);
96 qemu_mutex_unlock(&mon->mon_lock);
97 return true;
98 }
99
100 #ifdef CONFIG_POSIX
qmp_getfd(const char * fdname,Error ** errp)101 void qmp_getfd(const char *fdname, Error **errp)
102 {
103 Monitor *cur_mon = monitor_cur();
104 int fd;
105
106 fd = qemu_chr_fe_get_msgfd(&cur_mon->chr);
107 if (fd == -1) {
108 error_setg(errp, "No file descriptor supplied via SCM_RIGHTS");
109 return;
110 }
111
112 monitor_add_fd(cur_mon, fd, fdname, errp);
113 }
114 #endif
115
qmp_closefd(const char * fdname,Error ** errp)116 void qmp_closefd(const char *fdname, Error **errp)
117 {
118 Monitor *cur_mon = monitor_cur();
119 mon_fd_t *monfd;
120 int tmp_fd;
121
122 qemu_mutex_lock(&cur_mon->mon_lock);
123 QLIST_FOREACH(monfd, &cur_mon->fds, next) {
124 if (strcmp(monfd->name, fdname) != 0) {
125 continue;
126 }
127
128 QLIST_REMOVE(monfd, next);
129 tmp_fd = monfd->fd;
130 g_free(monfd->name);
131 g_free(monfd);
132 qemu_mutex_unlock(&cur_mon->mon_lock);
133 /* Make sure close() is outside critical section */
134 close(tmp_fd);
135 return;
136 }
137
138 qemu_mutex_unlock(&cur_mon->mon_lock);
139 error_setg(errp, "File descriptor named '%s' not found", fdname);
140 }
141
monitor_get_fd(Monitor * mon,const char * fdname,Error ** errp)142 int monitor_get_fd(Monitor *mon, const char *fdname, Error **errp)
143 {
144 mon_fd_t *monfd;
145
146 QEMU_LOCK_GUARD(&mon->mon_lock);
147 QLIST_FOREACH(monfd, &mon->fds, next) {
148 int fd;
149
150 if (strcmp(monfd->name, fdname) != 0) {
151 continue;
152 }
153
154 fd = monfd->fd;
155 assert(fd >= 0);
156
157 /* caller takes ownership of fd */
158 QLIST_REMOVE(monfd, next);
159 g_free(monfd->name);
160 g_free(monfd);
161
162 return fd;
163 }
164
165 error_setg(errp, "File descriptor named '%s' has not been found", fdname);
166 return -1;
167 }
168
monitor_fdset_free(MonFdset * mon_fdset)169 static void monitor_fdset_free(MonFdset *mon_fdset)
170 {
171 QLIST_REMOVE(mon_fdset, next);
172 g_free(mon_fdset);
173 }
174
monitor_fdset_free_if_empty(MonFdset * mon_fdset)175 static void monitor_fdset_free_if_empty(MonFdset *mon_fdset)
176 {
177 /*
178 * Only remove an empty fdset. The fds are owned by the user and
179 * should have been removed with qmp_remove_fd(). The dup_fds are
180 * owned by QEMU and should have been removed with qemu_close().
181 */
182 if (QLIST_EMPTY(&mon_fdset->fds) && QLIST_EMPTY(&mon_fdset->dup_fds)) {
183 monitor_fdset_free(mon_fdset);
184 }
185 }
186
monitor_fdset_fd_free(MonFdsetFd * mon_fdset_fd)187 static void monitor_fdset_fd_free(MonFdsetFd *mon_fdset_fd)
188 {
189 close(mon_fdset_fd->fd);
190 g_free(mon_fdset_fd->opaque);
191 QLIST_REMOVE(mon_fdset_fd, next);
192 g_free(mon_fdset_fd);
193 }
194
monitor_fdsets_cleanup(void)195 void monitor_fdsets_cleanup(void)
196 {
197 MonFdset *mon_fdset;
198 MonFdset *mon_fdset_next;
199
200 QEMU_LOCK_GUARD(&mon_fdsets_lock);
201 QLIST_FOREACH_SAFE(mon_fdset, &mon_fdsets, next, mon_fdset_next) {
202 monitor_fdset_free_if_empty(mon_fdset);
203 }
204 }
205
qmp_add_fd(bool has_fdset_id,int64_t fdset_id,const char * opaque,Error ** errp)206 AddfdInfo *qmp_add_fd(bool has_fdset_id, int64_t fdset_id,
207 const char *opaque, Error **errp)
208 {
209 int fd;
210 Monitor *mon = monitor_cur();
211 AddfdInfo *fdinfo;
212
213 fd = qemu_chr_fe_get_msgfd(&mon->chr);
214 if (fd == -1) {
215 error_setg(errp, "No file descriptor supplied via SCM_RIGHTS");
216 goto error;
217 }
218
219 fdinfo = monitor_fdset_add_fd(fd, has_fdset_id, fdset_id, opaque, errp);
220 if (fdinfo) {
221 return fdinfo;
222 }
223
224 error:
225 if (fd != -1) {
226 close(fd);
227 }
228 return NULL;
229 }
230
231 #ifdef WIN32
qmp_get_win32_socket(const char * infos,const char * fdname,Error ** errp)232 void qmp_get_win32_socket(const char *infos, const char *fdname, Error **errp)
233 {
234 g_autofree WSAPROTOCOL_INFOW *info = NULL;
235 gsize len;
236 SOCKET sk;
237 int fd;
238
239 info = (void *)g_base64_decode(infos, &len);
240 if (len != sizeof(*info)) {
241 error_setg(errp, "Invalid WSAPROTOCOL_INFOW value");
242 return;
243 }
244
245 sk = WSASocketW(FROM_PROTOCOL_INFO,
246 FROM_PROTOCOL_INFO,
247 FROM_PROTOCOL_INFO,
248 info, 0, 0);
249 if (sk == INVALID_SOCKET) {
250 error_setg_win32(errp, WSAGetLastError(), "Couldn't import socket");
251 return;
252 }
253
254 fd = _open_osfhandle(sk, _O_BINARY);
255 if (fd < 0) {
256 error_setg_errno(errp, errno, "Failed to associate a FD with the SOCKET");
257 closesocket(sk);
258 return;
259 }
260
261 monitor_add_fd(monitor_cur(), fd, fdname, errp);
262 }
263 #endif
264
265
qmp_remove_fd(int64_t fdset_id,bool has_fd,int64_t fd,Error ** errp)266 void qmp_remove_fd(int64_t fdset_id, bool has_fd, int64_t fd, Error **errp)
267 {
268 MonFdset *mon_fdset;
269 MonFdsetFd *mon_fdset_fd, *mon_fdset_fd_next;
270 char fd_str[60];
271
272 QEMU_LOCK_GUARD(&mon_fdsets_lock);
273 QLIST_FOREACH(mon_fdset, &mon_fdsets, next) {
274 if (mon_fdset->id != fdset_id) {
275 continue;
276 }
277 QLIST_FOREACH_SAFE(mon_fdset_fd, &mon_fdset->fds, next,
278 mon_fdset_fd_next) {
279 if (has_fd) {
280 if (mon_fdset_fd->fd != fd) {
281 continue;
282 }
283 monitor_fdset_fd_free(mon_fdset_fd);
284 break;
285 } else {
286 monitor_fdset_fd_free(mon_fdset_fd);
287 }
288 }
289 if (has_fd && !mon_fdset_fd) {
290 goto error;
291 }
292 monitor_fdset_free_if_empty(mon_fdset);
293 return;
294 }
295
296 error:
297 if (has_fd) {
298 snprintf(fd_str, sizeof(fd_str), "fdset-id:%" PRId64 ", fd:%" PRId64,
299 fdset_id, fd);
300 } else {
301 snprintf(fd_str, sizeof(fd_str), "fdset-id:%" PRId64, fdset_id);
302 }
303 error_setg(errp, "File descriptor named '%s' not found", fd_str);
304 }
305
qmp_query_fdsets(Error ** errp)306 FdsetInfoList *qmp_query_fdsets(Error **errp)
307 {
308 MonFdset *mon_fdset;
309 MonFdsetFd *mon_fdset_fd;
310 FdsetInfoList *fdset_list = NULL;
311
312 QEMU_LOCK_GUARD(&mon_fdsets_lock);
313 QLIST_FOREACH(mon_fdset, &mon_fdsets, next) {
314 FdsetInfo *fdset_info = g_malloc0(sizeof(*fdset_info));
315
316 fdset_info->fdset_id = mon_fdset->id;
317
318 QLIST_FOREACH(mon_fdset_fd, &mon_fdset->fds, next) {
319 FdsetFdInfo *fdsetfd_info;
320
321 fdsetfd_info = g_malloc0(sizeof(*fdsetfd_info));
322 fdsetfd_info->fd = mon_fdset_fd->fd;
323 fdsetfd_info->opaque = g_strdup(mon_fdset_fd->opaque);
324
325 QAPI_LIST_PREPEND(fdset_info->fds, fdsetfd_info);
326 }
327
328 QAPI_LIST_PREPEND(fdset_list, fdset_info);
329 }
330
331 return fdset_list;
332 }
333
monitor_fdset_add_fd(int fd,bool has_fdset_id,int64_t fdset_id,const char * opaque,Error ** errp)334 AddfdInfo *monitor_fdset_add_fd(int fd, bool has_fdset_id, int64_t fdset_id,
335 const char *opaque, Error **errp)
336 {
337 MonFdset *mon_fdset = NULL;
338 MonFdsetFd *mon_fdset_fd;
339 AddfdInfo *fdinfo;
340
341 QEMU_LOCK_GUARD(&mon_fdsets_lock);
342 if (has_fdset_id) {
343 QLIST_FOREACH(mon_fdset, &mon_fdsets, next) {
344 /* Break if match found or match impossible due to ordering by ID */
345 if (fdset_id <= mon_fdset->id) {
346 if (fdset_id < mon_fdset->id) {
347 mon_fdset = NULL;
348 }
349 break;
350 }
351 }
352 }
353
354 if (mon_fdset == NULL) {
355 int64_t fdset_id_prev = -1;
356 MonFdset *mon_fdset_cur = QLIST_FIRST(&mon_fdsets);
357
358 if (has_fdset_id) {
359 if (fdset_id < 0) {
360 error_setg(errp, QERR_INVALID_PARAMETER_VALUE, "fdset-id",
361 "a non-negative value");
362 return NULL;
363 }
364 /* Use specified fdset ID */
365 QLIST_FOREACH(mon_fdset, &mon_fdsets, next) {
366 mon_fdset_cur = mon_fdset;
367 if (fdset_id < mon_fdset_cur->id) {
368 break;
369 }
370 }
371 } else {
372 /* Use first available fdset ID */
373 QLIST_FOREACH(mon_fdset, &mon_fdsets, next) {
374 mon_fdset_cur = mon_fdset;
375 if (fdset_id_prev == mon_fdset_cur->id - 1) {
376 fdset_id_prev = mon_fdset_cur->id;
377 continue;
378 }
379 break;
380 }
381 }
382
383 mon_fdset = g_malloc0(sizeof(*mon_fdset));
384 if (has_fdset_id) {
385 mon_fdset->id = fdset_id;
386 } else {
387 mon_fdset->id = fdset_id_prev + 1;
388 }
389
390 /* The fdset list is ordered by fdset ID */
391 if (!mon_fdset_cur) {
392 QLIST_INSERT_HEAD(&mon_fdsets, mon_fdset, next);
393 } else if (mon_fdset->id < mon_fdset_cur->id) {
394 QLIST_INSERT_BEFORE(mon_fdset_cur, mon_fdset, next);
395 } else {
396 QLIST_INSERT_AFTER(mon_fdset_cur, mon_fdset, next);
397 }
398 }
399
400 mon_fdset_fd = g_malloc0(sizeof(*mon_fdset_fd));
401 mon_fdset_fd->fd = fd;
402 mon_fdset_fd->opaque = g_strdup(opaque);
403 QLIST_INSERT_HEAD(&mon_fdset->fds, mon_fdset_fd, next);
404
405 fdinfo = g_malloc0(sizeof(*fdinfo));
406 fdinfo->fdset_id = mon_fdset->id;
407 fdinfo->fd = mon_fdset_fd->fd;
408
409 return fdinfo;
410 }
411
monitor_fdset_dup_fd_add(int64_t fdset_id,int flags,Error ** errp)412 int monitor_fdset_dup_fd_add(int64_t fdset_id, int flags, Error **errp)
413 {
414 #ifdef _WIN32
415 error_setg(errp, "Platform does not support fd passing (fdset)");
416 return -ENOENT;
417 #else
418 MonFdset *mon_fdset;
419
420 QEMU_LOCK_GUARD(&mon_fdsets_lock);
421 QLIST_FOREACH(mon_fdset, &mon_fdsets, next) {
422 MonFdsetFd *mon_fdset_fd;
423 MonFdsetFd *mon_fdset_fd_dup;
424 int fd = -1;
425 int dup_fd;
426 int mon_fd_flags;
427 int mask = O_ACCMODE;
428
429 #ifdef O_DIRECT
430 mask |= O_DIRECT;
431 #endif
432
433 if (mon_fdset->id != fdset_id) {
434 continue;
435 }
436
437 QLIST_FOREACH(mon_fdset_fd, &mon_fdset->fds, next) {
438 mon_fd_flags = fcntl(mon_fdset_fd->fd, F_GETFL);
439 if (mon_fd_flags == -1) {
440 error_setg(errp, "Failed to read file status flags for fd=%d",
441 mon_fdset_fd->fd);
442 return -1;
443 }
444
445 if ((flags & mask) == (mon_fd_flags & mask)) {
446 fd = mon_fdset_fd->fd;
447 break;
448 }
449 }
450
451 if (fd == -1) {
452 errno = EACCES;
453 error_setg(errp,
454 "Failed to find file descriptor with matching flags=0x%x",
455 flags);
456 return -1;
457 }
458
459 dup_fd = qemu_dup_flags(fd, flags);
460 if (dup_fd == -1) {
461 error_setg(errp, "Failed to dup() given file descriptor fd=%d", fd);
462 return -1;
463 }
464
465 mon_fdset_fd_dup = g_malloc0(sizeof(*mon_fdset_fd_dup));
466 mon_fdset_fd_dup->fd = dup_fd;
467 QLIST_INSERT_HEAD(&mon_fdset->dup_fds, mon_fdset_fd_dup, next);
468 return dup_fd;
469 }
470
471 error_setg(errp, "Failed to find fdset /dev/fdset/%" PRId64, fdset_id);
472 errno = ENOENT;
473 return -1;
474 #endif
475 }
476
monitor_fdset_dup_fd_remove(int dup_fd)477 void monitor_fdset_dup_fd_remove(int dup_fd)
478 {
479 MonFdset *mon_fdset;
480 MonFdsetFd *mon_fdset_fd_dup;
481
482 QEMU_LOCK_GUARD(&mon_fdsets_lock);
483 QLIST_FOREACH(mon_fdset, &mon_fdsets, next) {
484 QLIST_FOREACH(mon_fdset_fd_dup, &mon_fdset->dup_fds, next) {
485 if (mon_fdset_fd_dup->fd == dup_fd) {
486 QLIST_REMOVE(mon_fdset_fd_dup, next);
487 g_free(mon_fdset_fd_dup);
488 monitor_fdset_free_if_empty(mon_fdset);
489 return;
490 }
491 }
492 }
493 }
494
monitor_fd_param(Monitor * mon,const char * fdname,Error ** errp)495 int monitor_fd_param(Monitor *mon, const char *fdname, Error **errp)
496 {
497 int fd;
498
499 if (!qemu_isdigit(fdname[0]) && mon) {
500 fd = monitor_get_fd(mon, fdname, errp);
501 } else {
502 fd = qemu_parse_fd(fdname);
503 if (fd < 0) {
504 error_setg(errp, "Invalid file descriptor number '%s'",
505 fdname);
506 }
507 }
508
509 return fd;
510 }
511
monitor_fds_init(void)512 static void __attribute__((__constructor__)) monitor_fds_init(void)
513 {
514 qemu_mutex_init(&mon_fdsets_lock);
515 }
516