]> git.proxmox.com Git - mirror_qemu.git/blame - monitor/fds.c
hw/usb/ohci: Include missing 'sysbus.h' header
[mirror_qemu.git] / monitor / fds.c
CommitLineData
7ef88b53
MA
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 */
35typedef struct mon_fd_t mon_fd_t;
36struct 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 */
43typedef struct MonFdsetFd MonFdsetFd;
44struct MonFdsetFd {
45 int fd;
46 bool removed;
47 char *opaque;
48 QLIST_ENTRY(MonFdsetFd) next;
49};
50
51/* file descriptor set containing fds passed via SCM_RIGHTS */
52typedef struct MonFdset MonFdset;
53struct MonFdset {
54 int64_t id;
55 QLIST_HEAD(, MonFdsetFd) fds;
56 QLIST_HEAD(, MonFdsetFd) dup_fds;
57 QLIST_ENTRY(MonFdset) next;
58};
59
60/* Protects mon_fdsets */
61static QemuMutex mon_fdsets_lock;
62static QLIST_HEAD(, MonFdset) mon_fdsets;
63
64void qmp_getfd(const char *fdname, Error **errp)
65{
66 Monitor *cur_mon = monitor_cur();
67 mon_fd_t *monfd;
68 int fd, tmp_fd;
69
70 fd = qemu_chr_fe_get_msgfd(&cur_mon->chr);
71 if (fd == -1) {
72 error_setg(errp, "No file descriptor supplied via SCM_RIGHTS");
73 return;
74 }
75
76 if (qemu_isdigit(fdname[0])) {
77 close(fd);
78 error_setg(errp, QERR_INVALID_PARAMETER_VALUE, "fdname",
79 "a name not starting with a digit");
80 return;
81 }
82
83 QEMU_LOCK_GUARD(&cur_mon->mon_lock);
84 QLIST_FOREACH(monfd, &cur_mon->fds, next) {
85 if (strcmp(monfd->name, fdname) != 0) {
86 continue;
87 }
88
89 tmp_fd = monfd->fd;
90 monfd->fd = fd;
91 /* Make sure close() is outside critical section */
92 close(tmp_fd);
93 return;
94 }
95
96 monfd = g_new0(mon_fd_t, 1);
97 monfd->name = g_strdup(fdname);
98 monfd->fd = fd;
99
100 QLIST_INSERT_HEAD(&cur_mon->fds, monfd, next);
101}
102
103void qmp_closefd(const char *fdname, Error **errp)
104{
105 Monitor *cur_mon = monitor_cur();
106 mon_fd_t *monfd;
107 int tmp_fd;
108
109 qemu_mutex_lock(&cur_mon->mon_lock);
110 QLIST_FOREACH(monfd, &cur_mon->fds, next) {
111 if (strcmp(monfd->name, fdname) != 0) {
112 continue;
113 }
114
115 QLIST_REMOVE(monfd, next);
116 tmp_fd = monfd->fd;
117 g_free(monfd->name);
118 g_free(monfd);
119 qemu_mutex_unlock(&cur_mon->mon_lock);
120 /* Make sure close() is outside critical section */
121 close(tmp_fd);
122 return;
123 }
124
125 qemu_mutex_unlock(&cur_mon->mon_lock);
126 error_setg(errp, "File descriptor named '%s' not found", fdname);
127}
128
129int monitor_get_fd(Monitor *mon, const char *fdname, Error **errp)
130{
131 mon_fd_t *monfd;
132
133 QEMU_LOCK_GUARD(&mon->mon_lock);
134 QLIST_FOREACH(monfd, &mon->fds, next) {
135 int fd;
136
137 if (strcmp(monfd->name, fdname) != 0) {
138 continue;
139 }
140
141 fd = monfd->fd;
142 assert(fd >= 0);
143
144 /* caller takes ownership of fd */
145 QLIST_REMOVE(monfd, next);
146 g_free(monfd->name);
147 g_free(monfd);
148
149 return fd;
150 }
151
152 error_setg(errp, "File descriptor named '%s' has not been found", fdname);
153 return -1;
154}
155
156static void monitor_fdset_cleanup(MonFdset *mon_fdset)
157{
158 MonFdsetFd *mon_fdset_fd;
159 MonFdsetFd *mon_fdset_fd_next;
160
161 QLIST_FOREACH_SAFE(mon_fdset_fd, &mon_fdset->fds, next, mon_fdset_fd_next) {
162 if ((mon_fdset_fd->removed ||
163 (QLIST_EMPTY(&mon_fdset->dup_fds) && mon_refcount == 0)) &&
164 runstate_is_running()) {
165 close(mon_fdset_fd->fd);
166 g_free(mon_fdset_fd->opaque);
167 QLIST_REMOVE(mon_fdset_fd, next);
168 g_free(mon_fdset_fd);
169 }
170 }
171
172 if (QLIST_EMPTY(&mon_fdset->fds) && QLIST_EMPTY(&mon_fdset->dup_fds)) {
173 QLIST_REMOVE(mon_fdset, next);
174 g_free(mon_fdset);
175 }
176}
177
178void monitor_fdsets_cleanup(void)
179{
180 MonFdset *mon_fdset;
181 MonFdset *mon_fdset_next;
182
183 QEMU_LOCK_GUARD(&mon_fdsets_lock);
184 QLIST_FOREACH_SAFE(mon_fdset, &mon_fdsets, next, mon_fdset_next) {
185 monitor_fdset_cleanup(mon_fdset);
186 }
187}
188
189AddfdInfo *qmp_add_fd(bool has_fdset_id, int64_t fdset_id,
190 const char *opaque, Error **errp)
191{
192 int fd;
193 Monitor *mon = monitor_cur();
194 AddfdInfo *fdinfo;
195
196 fd = qemu_chr_fe_get_msgfd(&mon->chr);
197 if (fd == -1) {
198 error_setg(errp, "No file descriptor supplied via SCM_RIGHTS");
199 goto error;
200 }
201
202 fdinfo = monitor_fdset_add_fd(fd, has_fdset_id, fdset_id, opaque, errp);
203 if (fdinfo) {
204 return fdinfo;
205 }
206
207error:
208 if (fd != -1) {
209 close(fd);
210 }
211 return NULL;
212}
213
214void qmp_remove_fd(int64_t fdset_id, bool has_fd, int64_t fd, Error **errp)
215{
216 MonFdset *mon_fdset;
217 MonFdsetFd *mon_fdset_fd;
218 char fd_str[60];
219
220 QEMU_LOCK_GUARD(&mon_fdsets_lock);
221 QLIST_FOREACH(mon_fdset, &mon_fdsets, next) {
222 if (mon_fdset->id != fdset_id) {
223 continue;
224 }
225 QLIST_FOREACH(mon_fdset_fd, &mon_fdset->fds, next) {
226 if (has_fd) {
227 if (mon_fdset_fd->fd != fd) {
228 continue;
229 }
230 mon_fdset_fd->removed = true;
231 break;
232 } else {
233 mon_fdset_fd->removed = true;
234 }
235 }
236 if (has_fd && !mon_fdset_fd) {
237 goto error;
238 }
239 monitor_fdset_cleanup(mon_fdset);
240 return;
241 }
242
243error:
244 if (has_fd) {
245 snprintf(fd_str, sizeof(fd_str), "fdset-id:%" PRId64 ", fd:%" PRId64,
246 fdset_id, fd);
247 } else {
248 snprintf(fd_str, sizeof(fd_str), "fdset-id:%" PRId64, fdset_id);
249 }
250 error_setg(errp, "File descriptor named '%s' not found", fd_str);
251}
252
253FdsetInfoList *qmp_query_fdsets(Error **errp)
254{
255 MonFdset *mon_fdset;
256 MonFdsetFd *mon_fdset_fd;
257 FdsetInfoList *fdset_list = NULL;
258
259 QEMU_LOCK_GUARD(&mon_fdsets_lock);
260 QLIST_FOREACH(mon_fdset, &mon_fdsets, next) {
261 FdsetInfo *fdset_info = g_malloc0(sizeof(*fdset_info));
262
263 fdset_info->fdset_id = mon_fdset->id;
264
265 QLIST_FOREACH(mon_fdset_fd, &mon_fdset->fds, next) {
266 FdsetFdInfo *fdsetfd_info;
267
268 fdsetfd_info = g_malloc0(sizeof(*fdsetfd_info));
269 fdsetfd_info->fd = mon_fdset_fd->fd;
270 fdsetfd_info->opaque = g_strdup(mon_fdset_fd->opaque);
271
272 QAPI_LIST_PREPEND(fdset_info->fds, fdsetfd_info);
273 }
274
275 QAPI_LIST_PREPEND(fdset_list, fdset_info);
276 }
277
278 return fdset_list;
279}
280
281AddfdInfo *monitor_fdset_add_fd(int fd, bool has_fdset_id, int64_t fdset_id,
282 const char *opaque, Error **errp)
283{
284 MonFdset *mon_fdset = NULL;
285 MonFdsetFd *mon_fdset_fd;
286 AddfdInfo *fdinfo;
287
288 QEMU_LOCK_GUARD(&mon_fdsets_lock);
289 if (has_fdset_id) {
290 QLIST_FOREACH(mon_fdset, &mon_fdsets, next) {
291 /* Break if match found or match impossible due to ordering by ID */
292 if (fdset_id <= mon_fdset->id) {
293 if (fdset_id < mon_fdset->id) {
294 mon_fdset = NULL;
295 }
296 break;
297 }
298 }
299 }
300
301 if (mon_fdset == NULL) {
302 int64_t fdset_id_prev = -1;
303 MonFdset *mon_fdset_cur = QLIST_FIRST(&mon_fdsets);
304
305 if (has_fdset_id) {
306 if (fdset_id < 0) {
307 error_setg(errp, QERR_INVALID_PARAMETER_VALUE, "fdset-id",
308 "a non-negative value");
309 return NULL;
310 }
311 /* Use specified fdset ID */
312 QLIST_FOREACH(mon_fdset, &mon_fdsets, next) {
313 mon_fdset_cur = mon_fdset;
314 if (fdset_id < mon_fdset_cur->id) {
315 break;
316 }
317 }
318 } else {
319 /* Use first available fdset ID */
320 QLIST_FOREACH(mon_fdset, &mon_fdsets, next) {
321 mon_fdset_cur = mon_fdset;
322 if (fdset_id_prev == mon_fdset_cur->id - 1) {
323 fdset_id_prev = mon_fdset_cur->id;
324 continue;
325 }
326 break;
327 }
328 }
329
330 mon_fdset = g_malloc0(sizeof(*mon_fdset));
331 if (has_fdset_id) {
332 mon_fdset->id = fdset_id;
333 } else {
334 mon_fdset->id = fdset_id_prev + 1;
335 }
336
337 /* The fdset list is ordered by fdset ID */
338 if (!mon_fdset_cur) {
339 QLIST_INSERT_HEAD(&mon_fdsets, mon_fdset, next);
340 } else if (mon_fdset->id < mon_fdset_cur->id) {
341 QLIST_INSERT_BEFORE(mon_fdset_cur, mon_fdset, next);
342 } else {
343 QLIST_INSERT_AFTER(mon_fdset_cur, mon_fdset, next);
344 }
345 }
346
347 mon_fdset_fd = g_malloc0(sizeof(*mon_fdset_fd));
348 mon_fdset_fd->fd = fd;
349 mon_fdset_fd->removed = false;
350 mon_fdset_fd->opaque = g_strdup(opaque);
351 QLIST_INSERT_HEAD(&mon_fdset->fds, mon_fdset_fd, next);
352
353 fdinfo = g_malloc0(sizeof(*fdinfo));
354 fdinfo->fdset_id = mon_fdset->id;
355 fdinfo->fd = mon_fdset_fd->fd;
356
357 return fdinfo;
358}
359
360int monitor_fdset_dup_fd_add(int64_t fdset_id, int flags)
361{
362#ifdef _WIN32
363 return -ENOENT;
364#else
365 MonFdset *mon_fdset;
366
367 QEMU_LOCK_GUARD(&mon_fdsets_lock);
368 QLIST_FOREACH(mon_fdset, &mon_fdsets, next) {
369 MonFdsetFd *mon_fdset_fd;
370 MonFdsetFd *mon_fdset_fd_dup;
371 int fd = -1;
372 int dup_fd;
373 int mon_fd_flags;
374
375 if (mon_fdset->id != fdset_id) {
376 continue;
377 }
378
379 QLIST_FOREACH(mon_fdset_fd, &mon_fdset->fds, next) {
380 mon_fd_flags = fcntl(mon_fdset_fd->fd, F_GETFL);
381 if (mon_fd_flags == -1) {
382 return -1;
383 }
384
385 if ((flags & O_ACCMODE) == (mon_fd_flags & O_ACCMODE)) {
386 fd = mon_fdset_fd->fd;
387 break;
388 }
389 }
390
391 if (fd == -1) {
392 errno = EACCES;
393 return -1;
394 }
395
396 dup_fd = qemu_dup_flags(fd, flags);
397 if (dup_fd == -1) {
398 return -1;
399 }
400
401 mon_fdset_fd_dup = g_malloc0(sizeof(*mon_fdset_fd_dup));
402 mon_fdset_fd_dup->fd = dup_fd;
403 QLIST_INSERT_HEAD(&mon_fdset->dup_fds, mon_fdset_fd_dup, next);
404 return dup_fd;
405 }
406
407 errno = ENOENT;
408 return -1;
409#endif
410}
411
412static int64_t monitor_fdset_dup_fd_find_remove(int dup_fd, bool remove)
413{
414 MonFdset *mon_fdset;
415 MonFdsetFd *mon_fdset_fd_dup;
416
417 QEMU_LOCK_GUARD(&mon_fdsets_lock);
418 QLIST_FOREACH(mon_fdset, &mon_fdsets, next) {
419 QLIST_FOREACH(mon_fdset_fd_dup, &mon_fdset->dup_fds, next) {
420 if (mon_fdset_fd_dup->fd == dup_fd) {
421 if (remove) {
422 QLIST_REMOVE(mon_fdset_fd_dup, next);
423 g_free(mon_fdset_fd_dup);
424 if (QLIST_EMPTY(&mon_fdset->dup_fds)) {
425 monitor_fdset_cleanup(mon_fdset);
426 }
427 return -1;
428 } else {
429 return mon_fdset->id;
430 }
431 }
432 }
433 }
434
435 return -1;
436}
437
438int64_t monitor_fdset_dup_fd_find(int dup_fd)
439{
440 return monitor_fdset_dup_fd_find_remove(dup_fd, false);
441}
442
443void monitor_fdset_dup_fd_remove(int dup_fd)
444{
445 monitor_fdset_dup_fd_find_remove(dup_fd, true);
446}
447
448int monitor_fd_param(Monitor *mon, const char *fdname, Error **errp)
449{
450 int fd;
451
452 if (!qemu_isdigit(fdname[0]) && mon) {
453 fd = monitor_get_fd(mon, fdname, errp);
454 } else {
455 fd = qemu_parse_fd(fdname);
456 if (fd < 0) {
457 error_setg(errp, "Invalid file descriptor number '%s'",
458 fdname);
459 }
460 }
461
462 return fd;
463}
464
465static void __attribute__((__constructor__)) monitor_fds_init(void)
466{
467 qemu_mutex_init(&mon_fdsets_lock);
468}